summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:35:32 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:35:32 +0000
commit5ea77a75dd2d2158401331879f3c8f47940a732c (patch)
treed89dc06e9f4850a900f161e25f84e922c4f86cc8
parentInitial commit. (diff)
downloadopenldap-5ea77a75dd2d2158401331879f3c8f47940a732c.tar.xz
openldap-5ea77a75dd2d2158401331879f3c8f47940a732c.zip
Adding upstream version 2.5.13+dfsg.upstream/2.5.13+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--ANNOUNCEMENT168
-rw-r--r--CHANGES197
-rw-r--r--COPYRIGHT64
-rw-r--r--INSTALL109
-rw-r--r--LICENSE47
-rw-r--r--Makefile.in38
-rw-r--r--README95
-rw-r--r--aclocal.m4296
-rw-r--r--build/LICENSE-2.0.156
-rw-r--r--build/README13
-rwxr-xr-xbuild/config.guess1667
-rwxr-xr-xbuild/config.sub1793
-rw-r--r--build/dir.mk65
-rw-r--r--build/info.mk19
-rw-r--r--build/lib-shared.mk30
-rw-r--r--build/lib-static.mk23
-rw-r--r--build/lib.mk52
-rw-r--r--build/libtool.m48369
-rwxr-xr-xbuild/ltmain.sh11147
-rw-r--r--build/ltoptions.m4437
-rw-r--r--build/ltsugar.m4124
-rw-r--r--build/ltversion.m423
-rw-r--r--build/lt~obsolete.m499
-rw-r--r--build/man.mk59
-rwxr-xr-xbuild/missing215
-rwxr-xr-xbuild/mkdep223
-rwxr-xr-xbuild/mkdep.aix17
-rwxr-xr-xbuild/mkrelease96
-rwxr-xr-xbuild/mkvers.bat28
-rwxr-xr-xbuild/mkversion83
-rw-r--r--build/mod.mk92
-rw-r--r--build/openldap.m4810
-rw-r--r--build/rules.mk35
-rwxr-xr-xbuild/shtool1453
-rw-r--r--build/srv.mk59
-rw-r--r--build/top.mk261
-rw-r--r--build/version.h18
-rwxr-xr-xbuild/version.sh46
-rw-r--r--build/version.var23
-rw-r--r--clients/Makefile.in17
-rw-r--r--clients/tools/Makefile.in138
-rw-r--r--clients/tools/common.c2778
-rw-r--r--clients/tools/common.h140
-rw-r--r--clients/tools/ldapcompare.c366
-rw-r--r--clients/tools/ldapdelete.c443
-rw-r--r--clients/tools/ldapexop.c355
-rw-r--r--clients/tools/ldapmodify.c697
-rw-r--r--clients/tools/ldapmodrdn.c330
-rw-r--r--clients/tools/ldappasswd.c413
-rw-r--r--clients/tools/ldapsearch.c2423
-rw-r--r--clients/tools/ldapurl.c306
-rw-r--r--clients/tools/ldapvc.c506
-rw-r--r--clients/tools/ldapwhoami.c235
-rwxr-xr-xconfigure28163
-rw-r--r--configure.ac3432
-rw-r--r--contrib/ConfigOIDs8
-rw-r--r--contrib/README32
-rw-r--r--contrib/ldapc++/AUTHORS1
-rw-r--r--contrib/ldapc++/COPYRIGHT6
-rw-r--r--contrib/ldapc++/Makefile.am10
-rw-r--r--contrib/ldapc++/Makefile.in620
-rw-r--r--contrib/ldapc++/README37
-rw-r--r--contrib/ldapc++/TODO31
-rw-r--r--contrib/ldapc++/aclocal.m410228
-rwxr-xr-xcontrib/ldapc++/config.guess1667
-rwxr-xr-xcontrib/ldapc++/config.sub1793
-rwxr-xr-xcontrib/ldapc++/configure18656
-rw-r--r--contrib/ldapc++/configure.ac101
-rwxr-xr-xcontrib/ldapc++/depcomp530
-rw-r--r--contrib/ldapc++/doxygen.rc1313
-rw-r--r--contrib/ldapc++/examples/Makefile.am20
-rw-r--r--contrib/ldapc++/examples/Makefile.in506
-rw-r--r--contrib/ldapc++/examples/main.cpp134
-rw-r--r--contrib/ldapc++/examples/readSchema.cpp73
-rw-r--r--contrib/ldapc++/examples/startTls.cpp79
-rw-r--r--contrib/ldapc++/examples/urlTest.cpp41
-rwxr-xr-xcontrib/ldapc++/install-sh323
-rw-r--r--contrib/ldapc++/ltmain.sh11147
-rwxr-xr-xcontrib/ldapc++/missing215
-rw-r--r--contrib/ldapc++/src/LDAPAddRequest.cpp79
-rw-r--r--contrib/ldapc++/src/LDAPAddRequest.h30
-rw-r--r--contrib/ldapc++/src/LDAPAsynConnection.cpp366
-rw-r--r--contrib/ldapc++/src/LDAPAsynConnection.h338
-rw-r--r--contrib/ldapc++/src/LDAPAttrType.cpp148
-rw-r--r--contrib/ldapc++/src/LDAPAttrType.h101
-rw-r--r--contrib/ldapc++/src/LDAPAttribute.cpp199
-rw-r--r--contrib/ldapc++/src/LDAPAttribute.h181
-rw-r--r--contrib/ldapc++/src/LDAPAttributeList.cpp193
-rw-r--r--contrib/ldapc++/src/LDAPAttributeList.h121
-rw-r--r--contrib/ldapc++/src/LDAPBindRequest.cpp173
-rw-r--r--contrib/ldapc++/src/LDAPBindRequest.h61
-rw-r--r--contrib/ldapc++/src/LDAPCompareRequest.cpp79
-rw-r--r--contrib/ldapc++/src/LDAPCompareRequest.h31
-rw-r--r--contrib/ldapc++/src/LDAPConnection.cpp382
-rw-r--r--contrib/ldapc++/src/LDAPConnection.h241
-rw-r--r--contrib/ldapc++/src/LDAPConstraints.cpp178
-rw-r--r--contrib/ldapc++/src/LDAPConstraints.h98
-rw-r--r--contrib/ldapc++/src/LDAPControl.cpp94
-rw-r--r--contrib/ldapc++/src/LDAPControl.h87
-rw-r--r--contrib/ldapc++/src/LDAPControlSet.cpp84
-rw-r--r--contrib/ldapc++/src/LDAPControlSet.h89
-rw-r--r--contrib/ldapc++/src/LDAPDeleteRequest.cpp75
-rw-r--r--contrib/ldapc++/src/LDAPDeleteRequest.h26
-rw-r--r--contrib/ldapc++/src/LDAPEntry.cpp104
-rw-r--r--contrib/ldapc++/src/LDAPEntry.h116
-rw-r--r--contrib/ldapc++/src/LDAPEntryList.cpp40
-rw-r--r--contrib/ldapc++/src/LDAPEntryList.h70
-rw-r--r--contrib/ldapc++/src/LDAPException.cpp96
-rw-r--r--contrib/ldapc++/src/LDAPException.h107
-rw-r--r--contrib/ldapc++/src/LDAPExtRequest.cpp84
-rw-r--r--contrib/ldapc++/src/LDAPExtRequest.h28
-rw-r--r--contrib/ldapc++/src/LDAPExtResult.cpp49
-rw-r--r--contrib/ldapc++/src/LDAPExtResult.h50
-rw-r--r--contrib/ldapc++/src/LDAPMessage.cpp72
-rw-r--r--contrib/ldapc++/src/LDAPMessage.h127
-rw-r--r--contrib/ldapc++/src/LDAPMessageQueue.cpp171
-rw-r--r--contrib/ldapc++/src/LDAPMessageQueue.h72
-rw-r--r--contrib/ldapc++/src/LDAPModDNRequest.cpp88
-rw-r--r--contrib/ldapc++/src/LDAPModDNRequest.h33
-rw-r--r--contrib/ldapc++/src/LDAPModList.cpp48
-rw-r--r--contrib/ldapc++/src/LDAPModList.h59
-rw-r--r--contrib/ldapc++/src/LDAPModification.cpp48
-rw-r--r--contrib/ldapc++/src/LDAPModification.h30
-rw-r--r--contrib/ldapc++/src/LDAPModifyRequest.cpp81
-rw-r--r--contrib/ldapc++/src/LDAPModifyRequest.h30
-rw-r--r--contrib/ldapc++/src/LDAPObjClass.cpp130
-rw-r--r--contrib/ldapc++/src/LDAPObjClass.h104
-rw-r--r--contrib/ldapc++/src/LDAPRebind.cpp9
-rw-r--r--contrib/ldapc++/src/LDAPRebind.h27
-rw-r--r--contrib/ldapc++/src/LDAPRebindAuth.cpp40
-rw-r--r--contrib/ldapc++/src/LDAPRebindAuth.h55
-rw-r--r--contrib/ldapc++/src/LDAPReferenceList.cpp40
-rw-r--r--contrib/ldapc++/src/LDAPReferenceList.h74
-rw-r--r--contrib/ldapc++/src/LDAPRequest.cpp145
-rw-r--r--contrib/ldapc++/src/LDAPRequest.h89
-rw-r--r--contrib/ldapc++/src/LDAPResult.cpp96
-rw-r--r--contrib/ldapc++/src/LDAPResult.h162
-rw-r--r--contrib/ldapc++/src/LDAPSaslBindResult.cpp45
-rw-r--r--contrib/ldapc++/src/LDAPSaslBindResult.h43
-rw-r--r--contrib/ldapc++/src/LDAPSchema.cpp84
-rw-r--r--contrib/ldapc++/src/LDAPSchema.h73
-rw-r--r--contrib/ldapc++/src/LDAPSearchReference.cpp53
-rw-r--r--contrib/ldapc++/src/LDAPSearchReference.h46
-rw-r--r--contrib/ldapc++/src/LDAPSearchRequest.cpp135
-rw-r--r--contrib/ldapc++/src/LDAPSearchRequest.h43
-rw-r--r--contrib/ldapc++/src/LDAPSearchResult.cpp52
-rw-r--r--contrib/ldapc++/src/LDAPSearchResult.h45
-rw-r--r--contrib/ldapc++/src/LDAPSearchResults.cpp60
-rw-r--r--contrib/ldapc++/src/LDAPSearchResults.h56
-rw-r--r--contrib/ldapc++/src/LDAPUrl.cpp518
-rw-r--r--contrib/ldapc++/src/LDAPUrl.h207
-rw-r--r--contrib/ldapc++/src/LDAPUrlList.cpp57
-rw-r--r--contrib/ldapc++/src/LDAPUrlList.h78
-rw-r--r--contrib/ldapc++/src/LdifReader.cpp350
-rw-r--r--contrib/ldapc++/src/LdifReader.h57
-rw-r--r--contrib/ldapc++/src/LdifWriter.cpp116
-rw-r--r--contrib/ldapc++/src/LdifWriter.h31
-rw-r--r--contrib/ldapc++/src/Makefile.am103
-rw-r--r--contrib/ldapc++/src/Makefile.in717
-rw-r--r--contrib/ldapc++/src/SaslInteraction.cpp44
-rw-r--r--contrib/ldapc++/src/SaslInteraction.h29
-rw-r--r--contrib/ldapc++/src/SaslInteractionHandler.cpp101
-rw-r--r--contrib/ldapc++/src/SaslInteractionHandler.h27
-rw-r--r--contrib/ldapc++/src/StringList.cpp77
-rw-r--r--contrib/ldapc++/src/StringList.h88
-rw-r--r--contrib/ldapc++/src/TlsOptions.cpp163
-rw-r--r--contrib/ldapc++/src/TlsOptions.h162
-rw-r--r--contrib/ldapc++/src/ac/time.h28
-rw-r--r--contrib/ldapc++/src/config.h.in70
-rw-r--r--contrib/ldapc++/src/debug.h33
-rw-r--r--contrib/ldapc++/src/stamp-h.in1
-rwxr-xr-xcontrib/ldapc++/version.sh10
-rw-r--r--contrib/ldapc++/version.var13
-rw-r--r--contrib/ldaptcl/CHANGES30
-rw-r--r--contrib/ldaptcl/COPYRIGHT31
-rw-r--r--contrib/ldaptcl/Makefile.in196
-rw-r--r--contrib/ldaptcl/README67
-rwxr-xr-xcontrib/ldaptcl/configure4221
-rw-r--r--contrib/ldaptcl/configure.ac218
-rwxr-xr-xcontrib/ldaptcl/install-sh119
-rw-r--r--contrib/ldaptcl/ldap.n395
-rw-r--r--contrib/ldaptcl/ldaperr.tcl54
-rw-r--r--contrib/ldaptcl/man.macros236
-rw-r--r--contrib/ldaptcl/neoXldap.c1470
-rw-r--r--contrib/ldaptcl/pkgIndex.tcl.in1
-rw-r--r--contrib/ldaptcl/tclAppInit.c140
-rw-r--r--contrib/ldaptcl/tkAppInit.c119
-rw-r--r--contrib/slapd-modules/README64
-rw-r--r--contrib/slapd-modules/acl/Makefile50
-rw-r--r--contrib/slapd-modules/acl/README.gssacl32
-rw-r--r--contrib/slapd-modules/acl/README.posixgroup35
-rw-r--r--contrib/slapd-modules/acl/gssacl.c316
-rw-r--r--contrib/slapd-modules/acl/posixgroup.c329
-rw-r--r--contrib/slapd-modules/addpartial/Makefile46
-rw-r--r--contrib/slapd-modules/addpartial/README72
-rw-r--r--contrib/slapd-modules/addpartial/addpartial-overlay.c349
-rw-r--r--contrib/slapd-modules/adremap/Makefile68
-rw-r--r--contrib/slapd-modules/adremap/adremap.c652
-rw-r--r--contrib/slapd-modules/adremap/slapo-adremap.5104
-rw-r--r--contrib/slapd-modules/allop/Makefile58
-rw-r--r--contrib/slapd-modules/allop/README26
-rw-r--r--contrib/slapd-modules/allop/allop.c262
-rw-r--r--contrib/slapd-modules/allop/slapo-allop.563
-rw-r--r--contrib/slapd-modules/allowed/Makefile58
-rw-r--r--contrib/slapd-modules/allowed/README73
-rw-r--r--contrib/slapd-modules/allowed/allowed.c504
-rw-r--r--contrib/slapd-modules/authzid/Makefile58
-rw-r--r--contrib/slapd-modules/authzid/authzid.c390
-rw-r--r--contrib/slapd-modules/autogroup/Makefile46
-rw-r--r--contrib/slapd-modules/autogroup/README120
-rw-r--r--contrib/slapd-modules/autogroup/autogroup.c2236
-rw-r--r--contrib/slapd-modules/autogroup/slapo-autogroup.5116
-rw-r--r--contrib/slapd-modules/cloak/Makefile58
-rw-r--r--contrib/slapd-modules/cloak/cloak.c354
-rw-r--r--contrib/slapd-modules/cloak/slapo-cloak.582
-rw-r--r--contrib/slapd-modules/comp_match/Makefile69
-rw-r--r--contrib/slapd-modules/comp_match/README127
-rw-r--r--contrib/slapd-modules/comp_match/asn.h57
-rw-r--r--contrib/slapd-modules/comp_match/asn_to_syn_mr.c282
-rw-r--r--contrib/slapd-modules/comp_match/authorityKeyIdentifier.asn65
-rw-r--r--contrib/slapd-modules/comp_match/authorityKeyIdentifier.c2058
-rw-r--r--contrib/slapd-modules/comp_match/authorityKeyIdentifier.h327
-rw-r--r--contrib/slapd-modules/comp_match/certificate.asn1175
-rw-r--r--contrib/slapd-modules/comp_match/certificate.c3249
-rw-r--r--contrib/slapd-modules/comp_match/certificate.h379
-rw-r--r--contrib/slapd-modules/comp_match/componentlib.c2370
-rw-r--r--contrib/slapd-modules/comp_match/componentlib.h593
-rw-r--r--contrib/slapd-modules/comp_match/crl.c1294
-rw-r--r--contrib/slapd-modules/comp_match/crl.h359
-rw-r--r--contrib/slapd-modules/comp_match/init.c839
-rw-r--r--contrib/slapd-modules/datamorph/Makefile77
-rw-r--r--contrib/slapd-modules/datamorph/datamorph.c2091
-rw-r--r--contrib/slapd-modules/datamorph/slapo-datamorph.5338
-rw-r--r--contrib/slapd-modules/datamorph/tests/Rules.mk23
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/config.ldif108
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/datamorph.conf49
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test.ldif434
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test001-01-same-attr.ldif3
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test001-02-same-index.ldif4
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test001-02a-same-index.ldif4
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test001-03-invalid-attr.ldif3
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test002-config.ldif9
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test002-entry.ldif31
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test002-fail.ldif23
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test002-transformed-rdn.ldif5
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test003-config.ldif30
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test003-out.ldif125
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test005-01-fail.ldif5
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test005-02-fail.ldif5
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test005-03-fail.ldif5
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test005-03a-fail.ldif5
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test005-04-fail.ldif10
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test005-04a-fail.ldif6
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test005-changes.ldif30
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test005-out.ldif212
-rw-r--r--contrib/slapd-modules/datamorph/tests/data/test007-config.ldif30
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/run229
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/scripts/all102
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/scripts/common.sh152
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/scripts/test001-config248
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/scripts/test002-add-delete147
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/scripts/test003-search106
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/scripts/test004-compare62
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/scripts/test005-modify89
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/scripts/test006-modrdn52
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/scripts/test007-transformed-replication296
-rwxr-xr-xcontrib/slapd-modules/datamorph/tests/scripts/test008-ignored-replication299
-rw-r--r--contrib/slapd-modules/denyop/Makefile46
-rw-r--r--contrib/slapd-modules/denyop/denyop.c260
-rw-r--r--contrib/slapd-modules/dsaschema/Makefile46
-rw-r--r--contrib/slapd-modules/dsaschema/README23
-rw-r--r--contrib/slapd-modules/dsaschema/dsaschema.c369
-rw-r--r--contrib/slapd-modules/dupent/Makefile58
-rw-r--r--contrib/slapd-modules/dupent/dupent.c558
-rw-r--r--contrib/slapd-modules/emptyds/Makefile78
-rw-r--r--contrib/slapd-modules/emptyds/README66
-rw-r--r--contrib/slapd-modules/emptyds/emptyds.c325
-rw-r--r--contrib/slapd-modules/emptyds/slapo-emptyds.568
-rw-r--r--contrib/slapd-modules/emptyds/tests/Rules.mk23
-rw-r--r--contrib/slapd-modules/emptyds/tests/data/emptyds.conf54
-rw-r--r--contrib/slapd-modules/emptyds/tests/data/test001.ldif71
-rw-r--r--contrib/slapd-modules/emptyds/tests/data/test001.out54
-rwxr-xr-xcontrib/slapd-modules/emptyds/tests/run218
-rwxr-xr-xcontrib/slapd-modules/emptyds/tests/scripts/all92
-rwxr-xr-xcontrib/slapd-modules/emptyds/tests/scripts/test001-emptyds137
-rw-r--r--contrib/slapd-modules/kinit/Makefile46
-rw-r--r--contrib/slapd-modules/kinit/README36
-rw-r--r--contrib/slapd-modules/kinit/kinit.c295
-rw-r--r--contrib/slapd-modules/lastbind/Makefile68
-rw-r--r--contrib/slapd-modules/lastbind/lastbind.c320
-rw-r--r--contrib/slapd-modules/lastbind/slapo-lastbind.5108
-rw-r--r--contrib/slapd-modules/lastmod/Makefile58
-rw-r--r--contrib/slapd-modules/lastmod/lastmod.c963
-rw-r--r--contrib/slapd-modules/lastmod/slapo-lastmod.5185
-rw-r--r--contrib/slapd-modules/noopsrch/Makefile58
-rw-r--r--contrib/slapd-modules/noopsrch/noopsrch.c255
-rw-r--r--contrib/slapd-modules/nops/Makefile58
-rw-r--r--contrib/slapd-modules/nops/nops.c178
-rw-r--r--contrib/slapd-modules/nops/slapo-nops.532
-rw-r--r--contrib/slapd-modules/nssov/Makefile86
-rw-r--r--contrib/slapd-modules/nssov/README134
-rw-r--r--contrib/slapd-modules/nssov/alias.c116
-rw-r--r--contrib/slapd-modules/nssov/ether.c167
-rw-r--r--contrib/slapd-modules/nssov/group.c346
-rw-r--r--contrib/slapd-modules/nssov/host.c161
-rw-r--r--contrib/slapd-modules/nssov/ldapns.schema38
-rw-r--r--contrib/slapd-modules/nssov/netgroup.c199
-rw-r--r--contrib/slapd-modules/nssov/network.c161
-rw-r--r--contrib/slapd-modules/nssov/nss-pam-ldapd/README15
-rw-r--r--contrib/slapd-modules/nssov/nss-pam-ldapd/attrs.h91
-rw-r--r--contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd-prot.h391
-rw-r--r--contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd.h305
-rw-r--r--contrib/slapd-modules/nssov/nss-pam-ldapd/tio.c520
-rw-r--r--contrib/slapd-modules/nssov/nss-pam-ldapd/tio.h83
-rw-r--r--contrib/slapd-modules/nssov/nssov.c1045
-rw-r--r--contrib/slapd-modules/nssov/nssov.h348
-rw-r--r--contrib/slapd-modules/nssov/pam.c862
-rw-r--r--contrib/slapd-modules/nssov/passwd.c435
-rw-r--r--contrib/slapd-modules/nssov/protocol.c156
-rw-r--r--contrib/slapd-modules/nssov/rpc.c158
-rw-r--r--contrib/slapd-modules/nssov/service.c250
-rw-r--r--contrib/slapd-modules/nssov/shadow.c257
-rw-r--r--contrib/slapd-modules/nssov/slapo-nssov.5316
-rw-r--r--contrib/slapd-modules/passwd/Makefile70
-rw-r--r--contrib/slapd-modules/passwd/README69
-rw-r--r--contrib/slapd-modules/passwd/apr1-atol.pl29
-rw-r--r--contrib/slapd-modules/passwd/apr1-ltoa.pl31
-rw-r--r--contrib/slapd-modules/passwd/apr1.c236
-rw-r--r--contrib/slapd-modules/passwd/kerberos.c211
-rw-r--r--contrib/slapd-modules/passwd/netscape.c83
-rw-r--r--contrib/slapd-modules/passwd/pbkdf2/Makefile62
-rw-r--r--contrib/slapd-modules/passwd/pbkdf2/README99
-rw-r--r--contrib/slapd-modules/passwd/pbkdf2/pw-pbkdf2.c451
-rw-r--r--contrib/slapd-modules/passwd/pbkdf2/slapd-pw-pbkdf2.5112
-rw-r--r--contrib/slapd-modules/passwd/radius.c149
-rw-r--r--contrib/slapd-modules/passwd/sha2/Makefile59
-rw-r--r--contrib/slapd-modules/passwd/sha2/README144
-rw-r--r--contrib/slapd-modules/passwd/sha2/sha2.c1070
-rw-r--r--contrib/slapd-modules/passwd/sha2/sha2.h236
-rw-r--r--contrib/slapd-modules/passwd/sha2/slapd-pw-sha2.5118
-rw-r--r--contrib/slapd-modules/passwd/sha2/slapd-sha2.c508
-rw-r--r--contrib/slapd-modules/passwd/slapd-pw-radius.5110
-rw-r--r--contrib/slapd-modules/passwd/totp/Makefile58
-rw-r--r--contrib/slapd-modules/passwd/totp/README87
-rw-r--r--contrib/slapd-modules/passwd/totp/slapd-totp.c1000
-rw-r--r--contrib/slapd-modules/passwd/totp/slapo-totp.5109
-rw-r--r--contrib/slapd-modules/ppm/CHANGELOG.md38
-rw-r--r--contrib/slapd-modules/ppm/CONTRIBUTIONS.md5
-rw-r--r--contrib/slapd-modules/ppm/INSTALL.md51
-rw-r--r--contrib/slapd-modules/ppm/LICENSE50
-rw-r--r--contrib/slapd-modules/ppm/Makefile97
-rw-r--r--contrib/slapd-modules/ppm/README.md1
-rw-r--r--contrib/slapd-modules/ppm/ppm.c684
-rw-r--r--contrib/slapd-modules/ppm/ppm.example85
-rw-r--r--contrib/slapd-modules/ppm/ppm.h125
-rw-r--r--contrib/slapd-modules/ppm/ppm.md343
-rw-r--r--contrib/slapd-modules/ppm/ppm_test.c66
-rw-r--r--contrib/slapd-modules/ppm/slapm-ppm.5360
-rwxr-xr-xcontrib/slapd-modules/ppm/unit_tests.sh118
-rw-r--r--contrib/slapd-modules/proxyOld/Makefile58
-rw-r--r--contrib/slapd-modules/proxyOld/README31
-rw-r--r--contrib/slapd-modules/proxyOld/proxyOld.c128
-rwxr-xr-xcontrib/slapd-modules/rbac/Makefile63
-rw-r--r--contrib/slapd-modules/rbac/init.c324
-rw-r--r--contrib/slapd-modules/rbac/jts.c198
-rw-r--r--contrib/slapd-modules/rbac/ldap_rbac.h55
-rw-r--r--contrib/slapd-modules/rbac/rbac.c2169
-rw-r--r--contrib/slapd-modules/rbac/rbac.h402
-rw-r--r--contrib/slapd-modules/rbac/rbacacl.c37
-rw-r--r--contrib/slapd-modules/rbac/rbacaudit.c233
-rw-r--r--contrib/slapd-modules/rbac/rbacperm.c233
-rw-r--r--contrib/slapd-modules/rbac/rbacreq.c89
-rw-r--r--contrib/slapd-modules/rbac/rbacsess.c999
-rw-r--r--contrib/slapd-modules/rbac/rbacuser.c620
-rw-r--r--contrib/slapd-modules/rbac/slapo-rbac.5157
-rw-r--r--contrib/slapd-modules/rbac/util.c531
-rw-r--r--contrib/slapd-modules/samba4/Makefile68
-rw-r--r--contrib/slapd-modules/samba4/README72
-rw-r--r--contrib/slapd-modules/samba4/pguid.c460
-rw-r--r--contrib/slapd-modules/samba4/rdnval.c657
-rw-r--r--contrib/slapd-modules/samba4/vernum.c459
-rw-r--r--contrib/slapd-modules/smbk5pwd/Makefile77
-rw-r--r--contrib/slapd-modules/smbk5pwd/README94
-rw-r--r--contrib/slapd-modules/smbk5pwd/slapo-smbk5pwd.5177
-rw-r--r--contrib/slapd-modules/smbk5pwd/smbk5pwd.c1084
-rw-r--r--contrib/slapd-modules/trace/Makefile46
-rw-r--r--contrib/slapd-modules/trace/trace.c256
-rw-r--r--contrib/slapd-modules/usn/Makefile46
-rw-r--r--contrib/slapd-modules/usn/README44
-rw-r--r--contrib/slapd-modules/usn/usn.c330
-rw-r--r--contrib/slapd-modules/variant/Makefile77
-rw-r--r--contrib/slapd-modules/variant/slapo-variant.5472
-rw-r--r--contrib/slapd-modules/variant/tests/Rules.mk23
-rw-r--r--contrib/slapd-modules/variant/tests/data/additional-config.ldif23
-rw-r--r--contrib/slapd-modules/variant/tests/data/config.ldif89
-rw-r--r--contrib/slapd-modules/variant/tests/data/hidden.ldif4
-rw-r--r--contrib/slapd-modules/variant/tests/data/test001-01-same-dn.ldif4
-rw-r--r--contrib/slapd-modules/variant/tests/data/test001-01a-same-dn.ldif4
-rw-r--r--contrib/slapd-modules/variant/tests/data/test001-02-same-attribute.ldif6
-rw-r--r--contrib/slapd-modules/variant/tests/data/test001-03-different-types.ldif4
-rw-r--r--contrib/slapd-modules/variant/tests/data/test002-01-entry.ldif16
-rw-r--r--contrib/slapd-modules/variant/tests/data/test002-02-regex.ldif7
-rw-r--r--contrib/slapd-modules/variant/tests/data/test003-out.ldif124
-rw-r--r--contrib/slapd-modules/variant/tests/data/test005-changes.ldif35
-rw-r--r--contrib/slapd-modules/variant/tests/data/test005-modify-missing.ldif4
-rw-r--r--contrib/slapd-modules/variant/tests/data/test005-out.ldif206
-rw-r--r--contrib/slapd-modules/variant/tests/data/test005-variant-missing.ldif4
-rw-r--r--contrib/slapd-modules/variant/tests/data/test006-config.ldif61
-rw-r--r--contrib/slapd-modules/variant/tests/data/test006-out.ldif151
-rw-r--r--contrib/slapd-modules/variant/tests/data/test007-out.ldif6
-rw-r--r--contrib/slapd-modules/variant/tests/data/test010-out.ldif52
-rw-r--r--contrib/slapd-modules/variant/tests/data/test011-out.ldif10
-rw-r--r--contrib/slapd-modules/variant/tests/data/test012-data.ldif13
-rw-r--r--contrib/slapd-modules/variant/tests/data/test012-out.ldif9
-rw-r--r--contrib/slapd-modules/variant/tests/data/variant.conf17
-rwxr-xr-xcontrib/slapd-modules/variant/tests/run229
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/all102
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/common.sh115
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test001-config209
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test002-add-delete113
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test003-search113
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test004-compare63
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test005-modify120
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test006-acl323
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test007-subtypes67
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test008-variant-replication194
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test009-ignored-replication227
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test010-limits99
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test011-referral169
-rwxr-xr-xcontrib/slapd-modules/variant/tests/scripts/test012-crossdb90
-rw-r--r--contrib/slapd-modules/variant/variant.c1424
-rw-r--r--contrib/slapd-modules/vc/Makefile58
-rw-r--r--contrib/slapd-modules/vc/vc.c439
-rw-r--r--contrib/slapd-tools/README23
-rwxr-xr-xcontrib/slapd-tools/statslog171
-rwxr-xr-xcontrib/slapd-tools/wrap_slap_ops162
-rw-r--r--contrib/slapi-plugins/addrdnvalues/README29
-rw-r--r--contrib/slapi-plugins/addrdnvalues/addrdnvalues.c75
-rw-r--r--doc/Makefile.in16
-rw-r--r--doc/devel/OIDs119
-rw-r--r--doc/devel/README9
-rw-r--r--doc/devel/args64
-rw-r--r--doc/devel/lloadd/design.md282
-rw-r--r--doc/devel/template.c26
-rw-r--r--doc/devel/todo67
-rw-r--r--doc/devel/toolargs31
-rw-r--r--doc/devel/utfconv.txt291
-rw-r--r--doc/devel/variadic_debug/03-libldap_Debug.cocci70
-rw-r--r--doc/devel/variadic_debug/04-variadic.cocci165
-rw-r--r--doc/devel/variadic_debug/07-shortcut.cocci216
-rw-r--r--doc/devel/variadic_debug/09-merge.cocci147
-rw-r--r--doc/devel/variadic_debug/README39
-rw-r--r--doc/devel/variadic_debug/equivalence.iso12
-rw-r--r--doc/devel/variadic_debug/macros.h23
-rwxr-xr-xdoc/devel/variadic_debug/script.sh73
-rw-r--r--doc/guide/README16
-rw-r--r--doc/guide/admin/Makefile101
-rw-r--r--doc/guide/admin/README.spellcheck16
-rw-r--r--doc/guide/admin/abstract.sdf7
-rw-r--r--doc/guide/admin/access-control.sdf1342
-rw-r--r--doc/guide/admin/admin.sdf11
-rw-r--r--doc/guide/admin/allmail-en.pngbin0 -> 24419 bytes
-rw-r--r--doc/guide/admin/allusersgroup-en.pngbin0 -> 34127 bytes
-rw-r--r--doc/guide/admin/appendix-changes.sdf74
-rw-r--r--doc/guide/admin/appendix-common-errors.sdf650
-rw-r--r--doc/guide/admin/appendix-configs.sdf14
-rw-r--r--doc/guide/admin/appendix-contrib.sdf116
-rw-r--r--doc/guide/admin/appendix-deployments.sdf7
-rw-r--r--doc/guide/admin/appendix-ldap-result-codes.sdf269
-rw-r--r--doc/guide/admin/appendix-recommended-versions.sdf23
-rw-r--r--doc/guide/admin/appendix-upgrading.sdf74
-rw-r--r--doc/guide/admin/aspell.en.pws1675
-rw-r--r--doc/guide/admin/backends.sdf556
-rw-r--r--doc/guide/admin/booktitle.sdf34
-rw-r--r--doc/guide/admin/config.sdf70
-rw-r--r--doc/guide/admin/config_dit.pngbin0 -> 19735 bytes
-rw-r--r--doc/guide/admin/config_local.pngbin0 -> 4172 bytes
-rw-r--r--doc/guide/admin/config_ref.pngbin0 -> 7556 bytes
-rw-r--r--doc/guide/admin/config_repl.pngbin0 -> 11302 bytes
-rw-r--r--doc/guide/admin/dbtools.sdf382
-rw-r--r--doc/guide/admin/delta-syncrepl.pngbin0 -> 56557 bytes
-rw-r--r--doc/guide/admin/dual_dc.pngbin0 -> 48497 bytes
-rw-r--r--doc/guide/admin/glossary.sdf16
-rw-r--r--doc/guide/admin/guide.book3
-rw-r--r--doc/guide/admin/guide.html11454
-rw-r--r--doc/guide/admin/guide.sdf8
-rw-r--r--doc/guide/admin/index.sdf8
-rw-r--r--doc/guide/admin/install.sdf245
-rw-r--r--doc/guide/admin/intro.sdf465
-rw-r--r--doc/guide/admin/intro_dctree.pngbin0 -> 21788 bytes
-rw-r--r--doc/guide/admin/intro_tree.pngbin0 -> 24714 bytes
-rw-r--r--doc/guide/admin/ldap-sync-refreshandpersist.pngbin0 -> 71628 bytes
-rw-r--r--doc/guide/admin/ldap-sync-refreshonly.pngbin0 -> 67986 bytes
-rw-r--r--doc/guide/admin/limits.sdf266
-rw-r--r--doc/guide/admin/load-balancer-scenario.pngbin0 -> 16119 bytes
-rw-r--r--doc/guide/admin/loadbalancer.sdf169
-rw-r--r--doc/guide/admin/maintenance.sdf77
-rw-r--r--doc/guide/admin/master.sdf141
-rw-r--r--doc/guide/admin/monitoringslapd.sdf494
-rw-r--r--doc/guide/admin/n-way-multi-provider.pngbin0 -> 46634 bytes
-rw-r--r--doc/guide/admin/overlays.sdf1515
-rw-r--r--doc/guide/admin/preface.sdf85
-rw-r--r--doc/guide/admin/push-based-complete.pngbin0 -> 45005 bytes
-rw-r--r--doc/guide/admin/push-based-standalone.pngbin0 -> 56269 bytes
-rw-r--r--doc/guide/admin/quickstart.sdf300
-rw-r--r--doc/guide/admin/referrals.sdf146
-rw-r--r--doc/guide/admin/refint.pngbin0 -> 30103 bytes
-rw-r--r--doc/guide/admin/replication.sdf1148
-rw-r--r--doc/guide/admin/runningslapd.sdf160
-rw-r--r--doc/guide/admin/sasl.sdf741
-rw-r--r--doc/guide/admin/schema.sdf491
-rw-r--r--doc/guide/admin/security.sdf398
-rw-r--r--doc/guide/admin/set-following-references.pngbin0 -> 37722 bytes
-rw-r--r--doc/guide/admin/set-memberUid.pngbin0 -> 25857 bytes
-rw-r--r--doc/guide/admin/set-recursivegroup.pngbin0 -> 56165 bytes
-rw-r--r--doc/guide/admin/slapdconf2.sdf1264
-rw-r--r--doc/guide/admin/slapdconfig.sdf923
-rw-r--r--doc/guide/admin/title.sdf13
-rw-r--r--doc/guide/admin/tls.sdf256
-rw-r--r--doc/guide/admin/troubleshooting.sdf104
-rw-r--r--doc/guide/admin/tuning.sdf206
-rw-r--r--doc/guide/images/LDAPlogo.gifbin0 -> 1784 bytes
-rw-r--r--doc/guide/images/LDAPwww.gifbin0 -> 2572 bytes
-rw-r--r--doc/guide/images/src/README.fonts10
-rw-r--r--doc/guide/images/src/allmail-en.svg230
-rw-r--r--doc/guide/images/src/allusersgroup-en.svg193
-rw-r--r--doc/guide/images/src/config_dit.diabin0 -> 1594 bytes
-rw-r--r--doc/guide/images/src/config_local.diabin0 -> 1223 bytes
-rw-r--r--doc/guide/images/src/config_ref.diabin0 -> 1482 bytes
-rw-r--r--doc/guide/images/src/config_repl.diabin0 -> 1925 bytes
-rw-r--r--doc/guide/images/src/delta-syncrepl.diabin0 -> 2421 bytes
-rw-r--r--doc/guide/images/src/delta-syncrepl.svg4856
-rwxr-xr-xdoc/guide/images/src/dual_dc.svg6810
-rw-r--r--doc/guide/images/src/intro_dctree.diabin0 -> 2444 bytes
-rw-r--r--doc/guide/images/src/intro_tree.diabin0 -> 2437 bytes
-rw-r--r--doc/guide/images/src/ldap-sync-refreshandpersist.svg4853
-rw-r--r--doc/guide/images/src/ldap-sync-refreshonly.svg4814
-rw-r--r--doc/guide/images/src/mirrormode.diabin0 -> 2421 bytes
-rw-r--r--doc/guide/images/src/n-way-multi-provider.diabin0 -> 2421 bytes
-rw-r--r--doc/guide/images/src/n-way-multi-provider.svg5293
-rw-r--r--doc/guide/images/src/push-based-complete.svg4754
-rw-r--r--doc/guide/images/src/push-based-standalone.svg4844
-rw-r--r--doc/guide/images/src/refint.svg199
-rw-r--r--doc/guide/images/src/set-following-references.svg272
-rw-r--r--doc/guide/images/src/set-memberUid.svg272
-rw-r--r--doc/guide/images/src/set-recursivegroup.svg505
-rw-r--r--doc/guide/images/src/syncrepl-firewalls.diabin0 -> 2421 bytes
-rw-r--r--doc/guide/images/src/syncrepl-pull.diabin0 -> 2421 bytes
-rw-r--r--doc/guide/images/src/syncrepl-push.diabin0 -> 2421 bytes
-rw-r--r--doc/guide/images/src/syncrepl.diabin0 -> 2421 bytes
-rw-r--r--doc/guide/plain.sdf20
-rw-r--r--doc/guide/preamble.sdf312
-rw-r--r--doc/guide/release/autoconf-install.txt183
-rw-r--r--doc/guide/release/autoconf.sdf16
-rw-r--r--doc/guide/release/copyright-plain.sdf10
-rw-r--r--doc/guide/release/copyright.sdf89
-rw-r--r--doc/guide/release/install.sdf100
-rw-r--r--doc/guide/release/license-plain.sdf10
-rw-r--r--doc/guide/release/license.sdf15
-rw-r--r--doc/install/configure187
-rw-r--r--doc/man/Makefile.in16
-rw-r--r--doc/man/Project5
-rw-r--r--doc/man/man1/Makefile.in16
-rw-r--r--doc/man/man1/ldapcompare.1241
-rw-r--r--doc/man/man1/ldapdelete.1252
-rw-r--r--doc/man/man1/ldapexop.1242
-rw-r--r--doc/man/man1/ldapmodify.1390
-rw-r--r--doc/man/man1/ldapmodify.1.links1
-rw-r--r--doc/man/man1/ldapmodrdn.1268
-rw-r--r--doc/man/man1/ldappasswd.1231
-rw-r--r--doc/man/man1/ldapsearch.1495
-rw-r--r--doc/man/man1/ldapurl.1168
-rw-r--r--doc/man/man1/ldapvc.1213
-rw-r--r--doc/man/man1/ldapwhoami.1194
-rw-r--r--doc/man/man3/Deprecated7
-rw-r--r--doc/man/man3/Makefile.in16
-rw-r--r--doc/man/man3/lber-decode.3357
-rw-r--r--doc/man/man3/lber-decode.3.links13
-rw-r--r--doc/man/man3/lber-encode.3288
-rw-r--r--doc/man/man3/lber-encode.3.links11
-rw-r--r--doc/man/man3/lber-memory.349
-rw-r--r--doc/man/man3/lber-sockbuf.3199
-rw-r--r--doc/man/man3/lber-types.3188
-rw-r--r--doc/man/man3/lber-types.3.links11
-rw-r--r--doc/man/man3/ldap.3278
-rw-r--r--doc/man/man3/ldap_abandon.369
-rw-r--r--doc/man/man3/ldap_abandon.3.links1
-rw-r--r--doc/man/man3/ldap_add.381
-rw-r--r--doc/man/man3/ldap_add.3.links3
-rw-r--r--doc/man/man3/ldap_bind.3334
-rw-r--r--doc/man/man3/ldap_bind.3.links10
-rw-r--r--doc/man/man3/ldap_compare.379
-rw-r--r--doc/man/man3/ldap_compare.3.links3
-rw-r--r--doc/man/man3/ldap_controls.384
-rw-r--r--doc/man/man3/ldap_controls.3.links6
-rw-r--r--doc/man/man3/ldap_delete.389
-rw-r--r--doc/man/man3/ldap_delete.3.links3
-rw-r--r--doc/man/man3/ldap_dup.3125
-rw-r--r--doc/man/man3/ldap_dup.3.links1
-rw-r--r--doc/man/man3/ldap_error.3224
-rw-r--r--doc/man/man3/ldap_error.3.links5
-rw-r--r--doc/man/man3/ldap_extended_operation.375
-rw-r--r--doc/man/man3/ldap_extended_operation.3.links2
-rw-r--r--doc/man/man3/ldap_first_attribute.397
-rw-r--r--doc/man/man3/ldap_first_attribute.3.links2
-rw-r--r--doc/man/man3/ldap_first_entry.380
-rw-r--r--doc/man/man3/ldap_first_entry.3.links2
-rw-r--r--doc/man/man3/ldap_first_message.382
-rw-r--r--doc/man/man3/ldap_first_message.3.links2
-rw-r--r--doc/man/man3/ldap_first_reference.371
-rw-r--r--doc/man/man3/ldap_first_reference.3.links2
-rw-r--r--doc/man/man3/ldap_get_dn.3246
-rw-r--r--doc/man/man3/ldap_get_dn.3.links9
-rw-r--r--doc/man/man3/ldap_get_option.3932
-rw-r--r--doc/man/man3/ldap_get_option.3.links1
-rw-r--r--doc/man/man3/ldap_get_values.3102
-rw-r--r--doc/man/man3/ldap_get_values.3.links5
-rw-r--r--doc/man/man3/ldap_memory.350
-rw-r--r--doc/man/man3/ldap_memory.3.links6
-rw-r--r--doc/man/man3/ldap_modify.3134
-rw-r--r--doc/man/man3/ldap_modify.3.links4
-rw-r--r--doc/man/man3/ldap_modrdn.381
-rw-r--r--doc/man/man3/ldap_modrdn.3.links3
-rw-r--r--doc/man/man3/ldap_open.3236
-rw-r--r--doc/man/man3/ldap_open.3.links4
-rw-r--r--doc/man/man3/ldap_parse_reference.361
-rw-r--r--doc/man/man3/ldap_parse_result.3114
-rw-r--r--doc/man/man3/ldap_parse_result.3.links3
-rw-r--r--doc/man/man3/ldap_parse_sort_control.340
-rw-r--r--doc/man/man3/ldap_parse_vlv_control.349
-rw-r--r--doc/man/man3/ldap_rename.366
-rw-r--r--doc/man/man3/ldap_rename.3.links1
-rw-r--r--doc/man/man3/ldap_result.3136
-rw-r--r--doc/man/man3/ldap_result.3.links3
-rw-r--r--doc/man/man3/ldap_schema.3320
-rw-r--r--doc/man/man3/ldap_schema.3.links17
-rw-r--r--doc/man/man3/ldap_search.3144
-rw-r--r--doc/man/man3/ldap_search.3.links4
-rw-r--r--doc/man/man3/ldap_sort.321
-rw-r--r--doc/man/man3/ldap_sort.3.links3
-rw-r--r--doc/man/man3/ldap_sync.3326
-rw-r--r--doc/man/man3/ldap_tls.341
-rw-r--r--doc/man/man3/ldap_tls.3.links4
-rw-r--r--doc/man/man3/ldap_url.383
-rw-r--r--doc/man/man3/ldap_url.3.links3
-rw-r--r--doc/man/man5/Makefile.in16
-rw-r--r--doc/man/man5/ldap.conf.5529
-rw-r--r--doc/man/man5/ldif.5277
-rw-r--r--doc/man/man5/lloadd.conf.5848
-rw-r--r--doc/man/man5/slapd-asyncmeta.5532
-rw-r--r--doc/man/man5/slapd-config.52274
-rw-r--r--doc/man/man5/slapd-dnssrv.549
-rw-r--r--doc/man/man5/slapd-ldap.5700
-rw-r--r--doc/man/man5/slapd-ldif.554
-rw-r--r--doc/man/man5/slapd-mdb.5241
-rw-r--r--doc/man/man5/slapd-meta.51378
-rw-r--r--doc/man/man5/slapd-monitor.5126
-rw-r--r--doc/man/man5/slapd-ndb.5127
-rw-r--r--doc/man/man5/slapd-null.572
-rw-r--r--doc/man/man5/slapd-passwd.556
-rw-r--r--doc/man/man5/slapd-perl.5199
-rw-r--r--doc/man/man5/slapd-relay.5207
-rw-r--r--doc/man/man5/slapd-sock.5329
-rw-r--r--doc/man/man5/slapd-sock.5.links1
-rw-r--r--doc/man/man5/slapd-sql.5699
-rw-r--r--doc/man/man5/slapd-wt.597
-rw-r--r--doc/man/man5/slapd.access.51205
-rw-r--r--doc/man/man5/slapd.backends.5140
-rw-r--r--doc/man/man5/slapd.conf.52140
-rw-r--r--doc/man/man5/slapd.overlays.5204
-rw-r--r--doc/man/man5/slapd.plugin.5124
-rw-r--r--doc/man/man5/slapo-accesslog.5514
-rw-r--r--doc/man/man5/slapo-auditlog.598
-rw-r--r--doc/man/man5/slapo-autoca.5120
-rw-r--r--doc/man/man5/slapo-chain.5152
-rw-r--r--doc/man/man5/slapo-collect.552
-rw-r--r--doc/man/man5/slapo-constraint.5155
-rw-r--r--doc/man/man5/slapo-dds.5271
-rw-r--r--doc/man/man5/slapo-deref.580
-rw-r--r--doc/man/man5/slapo-dyngroup.558
-rw-r--r--doc/man/man5/slapo-dynlist.5275
-rw-r--r--doc/man/man5/slapo-homedir.5130
-rw-r--r--doc/man/man5/slapo-memberof.5145
-rw-r--r--doc/man/man5/slapo-otp.5138
-rw-r--r--doc/man/man5/slapo-pbind.561
-rw-r--r--doc/man/man5/slapo-pcache.5327
-rw-r--r--doc/man/man5/slapo-ppolicy.51060
-rw-r--r--doc/man/man5/slapo-refint.578
-rw-r--r--doc/man/man5/slapo-remoteauth.5160
-rw-r--r--doc/man/man5/slapo-retcode.5257
-rw-r--r--doc/man/man5/slapo-rwm.5669
-rw-r--r--doc/man/man5/slapo-sssvlv.557
-rw-r--r--doc/man/man5/slapo-syncprov.581
-rw-r--r--doc/man/man5/slapo-translucent.5133
-rw-r--r--doc/man/man5/slapo-unique.5187
-rw-r--r--doc/man/man5/slapo-valsort.597
-rw-r--r--doc/man/man5/slappw-argon2.5131
-rw-r--r--doc/man/man8/Makefile.in16
-rw-r--r--doc/man/man8/lloadd.8312
-rw-r--r--doc/man/man8/slapacl.8205
-rw-r--r--doc/man/man8/slapadd.8218
-rw-r--r--doc/man/man8/slapauth.8152
-rw-r--r--doc/man/man8/slapcat.8203
-rw-r--r--doc/man/man8/slapd.8377
-rw-r--r--doc/man/man8/slapdn.8108
-rw-r--r--doc/man/man8/slapindex.8178
-rw-r--r--doc/man/man8/slapmodify.8222
-rw-r--r--doc/man/man8/slappasswd.8203
-rw-r--r--doc/man/man8/slapschema.8193
-rw-r--r--doc/man/man8/slaptest.8117
-rw-r--r--include/Makefile.in85
-rw-r--r--include/ac/alloca.h43
-rw-r--r--include/ac/assert.h57
-rw-r--r--include/ac/bytes.h78
-rw-r--r--include/ac/crypt.h29
-rw-r--r--include/ac/ctype.h33
-rw-r--r--include/ac/dirent.h54
-rw-r--r--include/ac/errno.h32
-rw-r--r--include/ac/fdset.h42
-rw-r--r--include/ac/localize.h44
-rw-r--r--include/ac/param.h39
-rw-r--r--include/ac/regex.h39
-rw-r--r--include/ac/signal.h80
-rw-r--r--include/ac/socket.h266
-rw-r--r--include/ac/stdarg.h28
-rw-r--r--include/ac/stdlib.h48
-rw-r--r--include/ac/string.h118
-rw-r--r--include/ac/sysexits.h26
-rw-r--r--include/ac/syslog.h38
-rw-r--r--include/ac/termios.h50
-rw-r--r--include/ac/time.h39
-rw-r--r--include/ac/unistd.h72
-rw-r--r--include/ac/wait.h56
-rw-r--r--include/getopt-compat.h40
-rw-r--r--include/lber.h691
-rw-r--r--include/lber_pvt.h223
-rw-r--r--include/lber_types.hin62
-rw-r--r--include/ldap.h2817
-rw-r--r--include/ldap_avl.h165
-rw-r--r--include/ldap_cdefs.h248
-rw-r--r--include/ldap_config.hin73
-rw-r--r--include/ldap_defaults.h71
-rw-r--r--include/ldap_features.hin55
-rw-r--r--include/ldap_int_thread.h290
-rw-r--r--include/ldap_log.h211
-rw-r--r--include/ldap_pvt.h588
-rw-r--r--include/ldap_pvt_thread.h342
-rw-r--r--include/ldap_pvt_uc.h163
-rw-r--r--include/ldap_queue.h593
-rw-r--r--include/ldap_rq.h102
-rw-r--r--include/ldap_schema.h360
-rw-r--r--include/ldap_utf8.h106
-rw-r--r--include/ldif.h171
-rw-r--r--include/lutil.h375
-rw-r--r--include/lutil_hash.h78
-rw-r--r--include/lutil_ldap.h47
-rw-r--r--include/lutil_lockf.h34
-rw-r--r--include/lutil_md5.h64
-rw-r--r--include/lutil_meter.h70
-rw-r--r--include/lutil_sha1.h77
-rw-r--r--include/openldap.h39
-rw-r--r--include/portable.hin1195
-rw-r--r--include/rewrite.h298
-rw-r--r--include/slapi-plugin.h905
-rw-r--r--include/sysexits-compat.h115
-rw-r--r--libraries/Makefile.in30
-rw-r--r--libraries/liblber/Makefile.in56
-rw-r--r--libraries/liblber/assert.c40
-rw-r--r--libraries/liblber/bprint.c296
-rw-r--r--libraries/liblber/debug.c73
-rw-r--r--libraries/liblber/decode.c1026
-rw-r--r--libraries/liblber/dtest.c121
-rw-r--r--libraries/liblber/encode.c651
-rw-r--r--libraries/liblber/etest.c181
-rw-r--r--libraries/liblber/idtest.c87
-rw-r--r--libraries/liblber/io.c725
-rw-r--r--libraries/liblber/lber-int.h225
-rw-r--r--libraries/liblber/lber.pc.in12
-rw-r--r--libraries/liblber/liblber.vers.in17
-rw-r--r--libraries/liblber/memory.c825
-rw-r--r--libraries/liblber/nt_err.c96
-rw-r--r--libraries/liblber/options.c237
-rw-r--r--libraries/liblber/sockbuf.c988
-rw-r--r--libraries/liblber/stdio.c243
-rw-r--r--libraries/libldap/Makefile.in98
-rw-r--r--libraries/libldap/abandon.c458
-rw-r--r--libraries/libldap/account_usability.c128
-rw-r--r--libraries/libldap/add.c263
-rw-r--r--libraries/libldap/addentry.c72
-rw-r--r--libraries/libldap/apitest.c241
-rw-r--r--libraries/libldap/assertion.c100
-rw-r--r--libraries/libldap/avl.c671
-rw-r--r--libraries/libldap/bind.c117
-rw-r--r--libraries/libldap/cancel.c76
-rw-r--r--libraries/libldap/charray.c275
-rw-r--r--libraries/libldap/compare.c197
-rw-r--r--libraries/libldap/controls.c552
-rw-r--r--libraries/libldap/cyrus.c1335
-rw-r--r--libraries/libldap/dds.c156
-rw-r--r--libraries/libldap/delete.c174
-rw-r--r--libraries/libldap/deref.c289
-rw-r--r--libraries/libldap/dnssrv.c422
-rw-r--r--libraries/libldap/dntest.c296
-rw-r--r--libraries/libldap/error.c395
-rw-r--r--libraries/libldap/extended.c419
-rw-r--r--libraries/libldap/fetch.c146
-rw-r--r--libraries/libldap/filter.c1115
-rw-r--r--libraries/libldap/free.c107
-rw-r--r--libraries/libldap/ftest.c119
-rw-r--r--libraries/libldap/getattr.c157
-rw-r--r--libraries/libldap/getdn.c3334
-rw-r--r--libraries/libldap/getentry.c124
-rw-r--r--libraries/libldap/getvalues.c211
-rw-r--r--libraries/libldap/init.c782
-rw-r--r--libraries/libldap/lbase64.c108
-rw-r--r--libraries/libldap/ldap-int.h925
-rw-r--r--libraries/libldap/ldap-tls.h88
-rw-r--r--libraries/libldap/ldap.conf13
-rw-r--r--libraries/libldap/ldap.pc.in13
-rw-r--r--libraries/libldap/ldap_sync.c928
-rw-r--r--libraries/libldap/ldap_thr_debug.h197
-rw-r--r--libraries/libldap/ldif.c919
-rw-r--r--libraries/libldap/ldifutil.c724
-rw-r--r--libraries/libldap/libldap.vers.in16
-rw-r--r--libraries/libldap/messages.c68
-rw-r--r--libraries/libldap/modify.c233
-rw-r--r--libraries/libldap/modrdn.c273
-rw-r--r--libraries/libldap/msctrl.c280
-rw-r--r--libraries/libldap/open.c673
-rw-r--r--libraries/libldap/options.c1012
-rw-r--r--libraries/libldap/os-ip.c1265
-rw-r--r--libraries/libldap/os-local.c351
-rw-r--r--libraries/libldap/pagectrl.c271
-rw-r--r--libraries/libldap/passwd.c170
-rw-r--r--libraries/libldap/ppolicy.c257
-rw-r--r--libraries/libldap/print.c62
-rw-r--r--libraries/libldap/psearchctrl.c348
-rw-r--r--libraries/libldap/rdwr.c463
-rw-r--r--libraries/libldap/references.c147
-rw-r--r--libraries/libldap/request.c1714
-rw-r--r--libraries/libldap/result.c1401
-rw-r--r--libraries/libldap/rq.c225
-rw-r--r--libraries/libldap/sasl.c867
-rw-r--r--libraries/libldap/sbind.c115
-rw-r--r--libraries/libldap/schema.c3400
-rw-r--r--libraries/libldap/search.c545
-rw-r--r--libraries/libldap/sort.c183
-rw-r--r--libraries/libldap/sortctrl.c552
-rw-r--r--libraries/libldap/stctrl.c302
-rw-r--r--libraries/libldap/string.c177
-rw-r--r--libraries/libldap/t61.c692
-rw-r--r--libraries/libldap/tavl.c523
-rw-r--r--libraries/libldap/test.c807
-rw-r--r--libraries/libldap/testavl.c150
-rw-r--r--libraries/libldap/testtavl.c158
-rw-r--r--libraries/libldap/thr_debug.c1338
-rw-r--r--libraries/libldap/thr_nt.c252
-rw-r--r--libraries/libldap/thr_posix.c411
-rw-r--r--libraries/libldap/thr_pth.c238
-rw-r--r--libraries/libldap/thr_thr.c192
-rw-r--r--libraries/libldap/threads.c111
-rw-r--r--libraries/libldap/tls2.c1675
-rw-r--r--libraries/libldap/tls_g.c1258
-rw-r--r--libraries/libldap/tls_o.c1688
-rw-r--r--libraries/libldap/tpool.c1474
-rw-r--r--libraries/libldap/turn.c96
-rw-r--r--libraries/libldap/txn.c153
-rw-r--r--libraries/libldap/unbind.c319
-rw-r--r--libraries/libldap/url.c1650
-rw-r--r--libraries/libldap/urltest.c128
-rw-r--r--libraries/libldap/utf-8-conv.c485
-rw-r--r--libraries/libldap/utf-8.c562
-rw-r--r--libraries/libldap/util-int.c1026
-rw-r--r--libraries/libldap/vc.c367
-rw-r--r--libraries/libldap/vlvctrl.c361
-rw-r--r--libraries/libldap/whoami.c102
-rw-r--r--libraries/liblmdb/CHANGES266
-rw-r--r--libraries/liblmdb/COPYRIGHT20
-rw-r--r--libraries/liblmdb/Doxyfile1631
-rw-r--r--libraries/liblmdb/LICENSE47
-rw-r--r--libraries/liblmdb/Makefile117
-rw-r--r--libraries/liblmdb/intro.doc192
-rw-r--r--libraries/liblmdb/lmdb.h1608
-rw-r--r--libraries/liblmdb/mdb.c10320
-rw-r--r--libraries/liblmdb/mdb_copy.155
-rw-r--r--libraries/liblmdb/mdb_copy.c82
-rw-r--r--libraries/liblmdb/mdb_dump.175
-rw-r--r--libraries/liblmdb/mdb_dump.c319
-rw-r--r--libraries/liblmdb/mdb_load.184
-rw-r--r--libraries/liblmdb/mdb_load.c496
-rw-r--r--libraries/liblmdb/mdb_stat.164
-rw-r--r--libraries/liblmdb/mdb_stat.c263
-rw-r--r--libraries/liblmdb/midl.c359
-rw-r--r--libraries/liblmdb/midl.h186
-rw-r--r--libraries/liblmdb/mtest.c177
-rw-r--r--libraries/liblmdb/mtest2.c124
-rw-r--r--libraries/liblmdb/mtest3.c133
-rw-r--r--libraries/liblmdb/mtest4.c168
-rw-r--r--libraries/liblmdb/mtest5.c135
-rw-r--r--libraries/liblmdb/mtest6.c141
-rw-r--r--libraries/liblmdb/sample-bdb.txt73
-rw-r--r--libraries/liblmdb/sample-mdb.txt62
-rw-r--r--libraries/liblmdb/tooltag22
-rw-r--r--libraries/liblunicode/CompositionExclusions.txt176
-rw-r--r--libraries/liblunicode/Makefile.in54
-rw-r--r--libraries/liblunicode/UCD-Terms29
-rw-r--r--libraries/liblunicode/UnicodeData.txt13874
-rw-r--r--libraries/liblunicode/ucdata/MUTTUCData.txt303
-rw-r--r--libraries/liblunicode/ucdata/README313
-rw-r--r--libraries/liblunicode/ucdata/api.txt401
-rw-r--r--libraries/liblunicode/ucdata/bidiapi.txt84
-rw-r--r--libraries/liblunicode/ucdata/format.txt267
-rw-r--r--libraries/liblunicode/ucdata/ucdata.c1501
-rw-r--r--libraries/liblunicode/ucdata/ucdata.h364
-rw-r--r--libraries/liblunicode/ucdata/ucdata.man504
-rw-r--r--libraries/liblunicode/ucdata/ucgendat.c1960
-rw-r--r--libraries/liblunicode/ucdata/ucpgba.c750
-rw-r--r--libraries/liblunicode/ucdata/ucpgba.h167
-rw-r--r--libraries/liblunicode/ucdata/ucpgba.man97
-rw-r--r--libraries/liblunicode/ucdata/uctable.h14306
-rw-r--r--libraries/liblunicode/ucstr.c459
-rw-r--r--libraries/liblunicode/ure/README212
-rw-r--r--libraries/liblunicode/ure/ure.c2131
-rw-r--r--libraries/liblunicode/ure/ure.h154
-rw-r--r--libraries/liblunicode/ure/urestubs.c127
-rw-r--r--libraries/liblunicode/utbm/README121
-rw-r--r--libraries/liblunicode/utbm/utbm.c472
-rw-r--r--libraries/liblunicode/utbm/utbm.h114
-rw-r--r--libraries/liblunicode/utbm/utbmstub.c105
-rw-r--r--libraries/liblutil/Makefile.in53
-rw-r--r--libraries/liblutil/base64.c308
-rw-r--r--libraries/liblutil/detach.c144
-rw-r--r--libraries/liblutil/entropy.c170
-rw-r--r--libraries/liblutil/getopt.c136
-rw-r--r--libraries/liblutil/getpass.c130
-rw-r--r--libraries/liblutil/getpeereid.c220
-rw-r--r--libraries/liblutil/hash.c141
-rw-r--r--libraries/liblutil/lockf.c118
-rw-r--r--libraries/liblutil/md5.c332
-rw-r--r--libraries/liblutil/memcmp.c33
-rw-r--r--libraries/liblutil/meter.c386
-rw-r--r--libraries/liblutil/ntservice.c509
-rw-r--r--libraries/liblutil/passfile.c110
-rw-r--r--libraries/liblutil/passwd.c935
-rw-r--r--libraries/liblutil/ptest.c112
-rw-r--r--libraries/liblutil/sasl.c232
-rw-r--r--libraries/liblutil/sha1.c288
-rw-r--r--libraries/liblutil/signal.c41
-rw-r--r--libraries/liblutil/slapdmsg.binbin0 -> 116 bytes
-rw-r--r--libraries/liblutil/slapdmsg.h65
-rw-r--r--libraries/liblutil/slapdmsg.mc28
-rw-r--r--libraries/liblutil/slapdmsg.rc2
-rw-r--r--libraries/liblutil/sockpair.c78
-rw-r--r--libraries/liblutil/utils.c1071
-rw-r--r--libraries/liblutil/uuid.c460
-rw-r--r--libraries/librewrite/Copyright23
-rw-r--r--libraries/librewrite/Makefile.in37
-rw-r--r--libraries/librewrite/RATIONALE2
-rw-r--r--libraries/librewrite/config.c441
-rw-r--r--libraries/librewrite/context.c474
-rw-r--r--libraries/librewrite/info.c284
-rw-r--r--libraries/librewrite/ldapmap.c454
-rw-r--r--libraries/librewrite/map.c582
-rw-r--r--libraries/librewrite/params.c147
-rw-r--r--libraries/librewrite/parse.c124
-rw-r--r--libraries/librewrite/rewrite-int.h628
-rw-r--r--libraries/librewrite/rewrite-map.h32
-rw-r--r--libraries/librewrite/rewrite.c195
-rw-r--r--libraries/librewrite/rule.c510
-rw-r--r--libraries/librewrite/session.c427
-rw-r--r--libraries/librewrite/subst.c513
-rw-r--r--libraries/librewrite/var.c273
-rw-r--r--libraries/librewrite/xmap.c506
-rw-r--r--servers/Makefile.in17
-rw-r--r--servers/lloadd/Makefile.in48
-rw-r--r--servers/lloadd/Makefile_module.in45
-rw-r--r--servers/lloadd/Makefile_server.in90
-rw-r--r--servers/lloadd/backend.c736
-rw-r--r--servers/lloadd/bind.c992
-rw-r--r--servers/lloadd/client.c611
-rw-r--r--servers/lloadd/config.c3824
-rw-r--r--servers/lloadd/connection.c620
-rw-r--r--servers/lloadd/daemon.c1886
l---------servers/lloadd/design.md1
-rw-r--r--servers/lloadd/epoch.c339
-rw-r--r--servers/lloadd/epoch.h144
-rw-r--r--servers/lloadd/extended.c203
-rw-r--r--servers/lloadd/init.c211
-rw-r--r--servers/lloadd/libevent_support.c171
-rw-r--r--servers/lloadd/lload-config.h39
-rw-r--r--servers/lloadd/lload.h502
-rw-r--r--servers/lloadd/lloadd.service13
-rw-r--r--servers/lloadd/main.c949
-rw-r--r--servers/lloadd/module_init.c185
-rw-r--r--servers/lloadd/monitor.c1150
l---------servers/lloadd/nt_svc.c1
-rw-r--r--servers/lloadd/operation.c699
-rw-r--r--servers/lloadd/proto-lload.h225
-rw-r--r--servers/lloadd/upstream.c1103
-rw-r--r--servers/lloadd/value.c67
-rw-r--r--servers/slapd/Makefile.in467
-rw-r--r--servers/slapd/abandon.c141
-rw-r--r--servers/slapd/aci.c1834
-rw-r--r--servers/slapd/acl.c2687
-rw-r--r--servers/slapd/aclparse.c2815
-rw-r--r--servers/slapd/ad.c1313
-rw-r--r--servers/slapd/add.c693
-rw-r--r--servers/slapd/at.c1108
-rw-r--r--servers/slapd/attr.c722
-rw-r--r--servers/slapd/ava.c133
-rw-r--r--servers/slapd/back-asyncmeta/Makefile.in50
-rw-r--r--servers/slapd/back-asyncmeta/add.c363
-rw-r--r--servers/slapd/back-asyncmeta/back-asyncmeta.h782
-rw-r--r--servers/slapd/back-asyncmeta/bind.c1730
-rw-r--r--servers/slapd/back-asyncmeta/candidates.c239
-rw-r--r--servers/slapd/back-asyncmeta/compare.c304
-rw-r--r--servers/slapd/back-asyncmeta/config.c2443
-rw-r--r--servers/slapd/back-asyncmeta/conn.c1184
-rw-r--r--servers/slapd/back-asyncmeta/delete.c297
-rw-r--r--servers/slapd/back-asyncmeta/dncache.c228
-rw-r--r--servers/slapd/back-asyncmeta/init.c468
-rw-r--r--servers/slapd/back-asyncmeta/map.c214
-rw-r--r--servers/slapd/back-asyncmeta/message_queue.c236
-rw-r--r--servers/slapd/back-asyncmeta/meta_result.c1825
-rw-r--r--servers/slapd/back-asyncmeta/modify.c357
-rw-r--r--servers/slapd/back-asyncmeta/modrdn.c367
-rw-r--r--servers/slapd/back-asyncmeta/proto-asyncmeta.h53
-rw-r--r--servers/slapd/back-asyncmeta/search.c963
-rw-r--r--servers/slapd/back-dnssrv/Makefile.in46
-rw-r--r--servers/slapd/back-dnssrv/bind.c79
-rw-r--r--servers/slapd/back-dnssrv/compare.c46
-rw-r--r--servers/slapd/back-dnssrv/config.c54
-rw-r--r--servers/slapd/back-dnssrv/init.c115
-rw-r--r--servers/slapd/back-dnssrv/proto-dnssrv.h46
-rw-r--r--servers/slapd/back-dnssrv/referral.c129
-rw-r--r--servers/slapd/back-dnssrv/search.c239
-rw-r--r--servers/slapd/back-ldap/Makefile.in45
-rw-r--r--servers/slapd/back-ldap/TODO.proxy101
-rw-r--r--servers/slapd/back-ldap/add.c139
-rw-r--r--servers/slapd/back-ldap/back-ldap.h479
-rw-r--r--servers/slapd/back-ldap/bind.c3204
-rw-r--r--servers/slapd/back-ldap/chain.c2356
-rw-r--r--servers/slapd/back-ldap/compare.c88
-rw-r--r--servers/slapd/back-ldap/config.c2214
-rw-r--r--servers/slapd/back-ldap/delete.c85
-rw-r--r--servers/slapd/back-ldap/distproc.c998
-rw-r--r--servers/slapd/back-ldap/extended.c410
-rw-r--r--servers/slapd/back-ldap/init.c374
-rw-r--r--servers/slapd/back-ldap/modify.c136
-rw-r--r--servers/slapd/back-ldap/modrdn.c123
-rw-r--r--servers/slapd/back-ldap/monitor.c1074
-rw-r--r--servers/slapd/back-ldap/pbind.c173
-rw-r--r--servers/slapd/back-ldap/proto-ldap.h124
-rw-r--r--servers/slapd/back-ldap/search.c1042
-rw-r--r--servers/slapd/back-ldap/unbind.c78
-rw-r--r--servers/slapd/back-ldif/Makefile.in41
-rw-r--r--servers/slapd/back-ldif/ldif.c2054
-rw-r--r--servers/slapd/back-mdb/Makefile.in62
-rw-r--r--servers/slapd/back-mdb/add.c419
-rw-r--r--servers/slapd/back-mdb/attr.c824
-rw-r--r--servers/slapd/back-mdb/back-mdb.h207
-rw-r--r--servers/slapd/back-mdb/bind.c156
-rw-r--r--servers/slapd/back-mdb/compare.c142
-rw-r--r--servers/slapd/back-mdb/config.c828
-rw-r--r--servers/slapd/back-mdb/delete.c436
-rw-r--r--servers/slapd/back-mdb/dn2entry.c79
-rw-r--r--servers/slapd/back-mdb/dn2id.c981
-rw-r--r--servers/slapd/back-mdb/extended.c54
-rw-r--r--servers/slapd/back-mdb/filterindex.c1173
-rw-r--r--servers/slapd/back-mdb/id2entry.c1151
-rw-r--r--servers/slapd/back-mdb/idl.c1293
-rw-r--r--servers/slapd/back-mdb/idl.h118
-rw-r--r--servers/slapd/back-mdb/index.c577
-rw-r--r--servers/slapd/back-mdb/init.c508
-rw-r--r--servers/slapd/back-mdb/key.c72
-rw-r--r--servers/slapd/back-mdb/modify.c843
-rw-r--r--servers/slapd/back-mdb/modrdn.c624
-rw-r--r--servers/slapd/back-mdb/monitor.c807
-rw-r--r--servers/slapd/back-mdb/nextid.c53
-rw-r--r--servers/slapd/back-mdb/operational.c121
-rw-r--r--servers/slapd/back-mdb/proto-mdb.h411
-rw-r--r--servers/slapd/back-mdb/referral.c151
-rw-r--r--servers/slapd/back-mdb/search.c1543
-rw-r--r--servers/slapd/back-mdb/tools.c1712
-rw-r--r--servers/slapd/back-meta/Makefile.in45
-rw-r--r--servers/slapd/back-meta/add.c211
-rw-r--r--servers/slapd/back-meta/back-meta.h690
-rw-r--r--servers/slapd/back-meta/bind.c1758
-rw-r--r--servers/slapd/back-meta/candidates.c282
-rw-r--r--servers/slapd/back-meta/compare.c154
-rw-r--r--servers/slapd/back-meta/config.c3300
-rw-r--r--servers/slapd/back-meta/conn.c1893
-rw-r--r--servers/slapd/back-meta/delete.c103
-rw-r--r--servers/slapd/back-meta/dncache.c235
-rw-r--r--servers/slapd/back-meta/init.c473
-rw-r--r--servers/slapd/back-meta/map.c924
-rw-r--r--servers/slapd/back-meta/modify.c221
-rw-r--r--servers/slapd/back-meta/modrdn.c177
-rw-r--r--servers/slapd/back-meta/proto-meta.h54
-rw-r--r--servers/slapd/back-meta/search.c2431
-rw-r--r--servers/slapd/back-meta/suffixmassage.c110
-rw-r--r--servers/slapd/back-meta/unbind.c89
-rw-r--r--servers/slapd/back-monitor/Makefile.in49
-rw-r--r--servers/slapd/back-monitor/README243
-rw-r--r--servers/slapd/back-monitor/back-monitor.h327
-rw-r--r--servers/slapd/back-monitor/backend.c159
-rw-r--r--servers/slapd/back-monitor/bind.c48
-rw-r--r--servers/slapd/back-monitor/cache.c446
-rw-r--r--servers/slapd/back-monitor/compare.c76
-rw-r--r--servers/slapd/back-monitor/conn.c537
-rw-r--r--servers/slapd/back-monitor/database.c1027
-rw-r--r--servers/slapd/back-monitor/entry.c236
-rw-r--r--servers/slapd/back-monitor/init.c2573
-rw-r--r--servers/slapd/back-monitor/listener.c138
-rw-r--r--servers/slapd/back-monitor/log.c455
-rw-r--r--servers/slapd/back-monitor/modify.c90
-rw-r--r--servers/slapd/back-monitor/operation.c244
-rw-r--r--servers/slapd/back-monitor/operational.c72
-rw-r--r--servers/slapd/back-monitor/overlay.c140
-rw-r--r--servers/slapd/back-monitor/proto-back-monitor.h342
-rw-r--r--servers/slapd/back-monitor/rww.c232
-rw-r--r--servers/slapd/back-monitor/search.c271
-rw-r--r--servers/slapd/back-monitor/sent.c241
-rw-r--r--servers/slapd/back-monitor/thread.c351
-rw-r--r--servers/slapd/back-monitor/time.c247
-rw-r--r--servers/slapd/back-ndb/Makefile.in59
-rw-r--r--servers/slapd/back-ndb/TODO6
-rw-r--r--servers/slapd/back-ndb/add.cpp347
-rw-r--r--servers/slapd/back-ndb/attrsets.conf36
-rw-r--r--servers/slapd/back-ndb/back-ndb.h168
-rw-r--r--servers/slapd/back-ndb/bind.cpp165
-rw-r--r--servers/slapd/back-ndb/compare.cpp169
-rw-r--r--servers/slapd/back-ndb/config.cpp333
-rw-r--r--servers/slapd/back-ndb/delete.cpp322
-rw-r--r--servers/slapd/back-ndb/init.cpp449
-rw-r--r--servers/slapd/back-ndb/modify.cpp704
-rw-r--r--servers/slapd/back-ndb/modrdn.cpp558
-rw-r--r--servers/slapd/back-ndb/ndbio.cpp1677
-rw-r--r--servers/slapd/back-ndb/proto-ndb.h166
-rw-r--r--servers/slapd/back-ndb/search.cpp854
-rw-r--r--servers/slapd/back-ndb/tools.cpp544
-rw-r--r--servers/slapd/back-null/Makefile.in41
-rw-r--r--servers/slapd/back-null/README1
-rw-r--r--servers/slapd/back-null/null.c472
-rw-r--r--servers/slapd/back-passwd/Makefile.in41
-rw-r--r--servers/slapd/back-passwd/back-passwd.h31
-rw-r--r--servers/slapd/back-passwd/config.c73
-rw-r--r--servers/slapd/back-passwd/init.c122
-rw-r--r--servers/slapd/back-passwd/proto-passwd.h33
-rw-r--r--servers/slapd/back-passwd/search.c381
-rw-r--r--servers/slapd/back-perl/Makefile.in46
-rw-r--r--servers/slapd/back-perl/README24
-rw-r--r--servers/slapd/back-perl/SampleLDAP.pm171
-rw-r--r--servers/slapd/back-perl/add.c62
-rw-r--r--servers/slapd/back-perl/asperl_undefs.h38
-rw-r--r--servers/slapd/back-perl/bind.c80
-rw-r--r--servers/slapd/back-perl/close.c59
-rw-r--r--servers/slapd/back-perl/compare.c80
-rw-r--r--servers/slapd/back-perl/config.c256
-rw-r--r--servers/slapd/back-perl/delete.c59
-rw-r--r--servers/slapd/back-perl/init.c176
-rw-r--r--servers/slapd/back-perl/modify.c97
-rw-r--r--servers/slapd/back-perl/modrdn.c63
-rw-r--r--servers/slapd/back-perl/perl_back.h82
-rw-r--r--servers/slapd/back-perl/proto-perl.h43
-rw-r--r--servers/slapd/back-perl/search.c122
-rw-r--r--servers/slapd/back-relay/Makefile.in41
-rw-r--r--servers/slapd/back-relay/README83
-rw-r--r--servers/slapd/back-relay/back-relay.h49
-rw-r--r--servers/slapd/back-relay/init.c255
-rw-r--r--servers/slapd/back-relay/op.c333
-rw-r--r--servers/slapd/back-relay/proto-back-relay.h52
-rw-r--r--servers/slapd/back-sock/Makefile.in47
-rw-r--r--servers/slapd/back-sock/add.c69
-rw-r--r--servers/slapd/back-sock/back-sock.h61
-rw-r--r--servers/slapd/back-sock/bind.c80
-rw-r--r--servers/slapd/back-sock/compare.c88
-rw-r--r--servers/slapd/back-sock/config.c420
-rw-r--r--servers/slapd/back-sock/delete.c75
-rw-r--r--servers/slapd/back-sock/extended.c76
-rw-r--r--servers/slapd/back-sock/init.c97
-rw-r--r--servers/slapd/back-sock/modify.c117
-rw-r--r--servers/slapd/back-sock/modrdn.c81
-rw-r--r--servers/slapd/back-sock/opensock.c71
-rw-r--r--servers/slapd/back-sock/proto-sock.h49
-rw-r--r--servers/slapd/back-sock/result.c168
-rw-r--r--servers/slapd/back-sock/search.c74
-rw-r--r--servers/slapd/back-sock/searchexample.conf23
-rw-r--r--servers/slapd/back-sock/searchexample.pl90
-rw-r--r--servers/slapd/back-sock/unbind.c57
-rw-r--r--servers/slapd/back-sql/Makefile.in45
-rw-r--r--servers/slapd/back-sql/add.c1560
-rw-r--r--servers/slapd/back-sql/api.c211
-rw-r--r--servers/slapd/back-sql/back-sql.h631
-rw-r--r--servers/slapd/back-sql/bind.c115
-rw-r--r--servers/slapd/back-sql/compare.c194
-rw-r--r--servers/slapd/back-sql/config.c778
-rw-r--r--servers/slapd/back-sql/delete.c627
-rw-r--r--servers/slapd/back-sql/docs/bugs16
-rw-r--r--servers/slapd/back-sql/docs/concept1
-rw-r--r--servers/slapd/back-sql/docs/install86
-rw-r--r--servers/slapd/back-sql/docs/platforms8
-rw-r--r--servers/slapd/back-sql/docs/todo12
-rw-r--r--servers/slapd/back-sql/entry-id.c1092
-rw-r--r--servers/slapd/back-sql/init.c661
-rw-r--r--servers/slapd/back-sql/modify.c212
-rw-r--r--servers/slapd/back-sql/modrdn.c524
-rw-r--r--servers/slapd/back-sql/operational.c246
-rw-r--r--servers/slapd/back-sql/proto-sql.h313
-rw-r--r--servers/slapd/back-sql/rdbms_depend/README189
-rw-r--r--servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_create.sql59
-rw-r--r--servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_drop.sql5
-rw-r--r--servers/slapd/back-sql/rdbms_depend/ibmdb2/slapd.conf36
-rw-r--r--servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_create.sql75
-rw-r--r--servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_data.sql18
-rw-r--r--servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_drop.sql5
-rw-r--r--servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_metadata.sql123
-rw-r--r--servers/slapd/back-sql/rdbms_depend/mssql/backsql_create.sql100
-rw-r--r--servers/slapd/back-sql/rdbms_depend/mssql/backsql_drop.sql14
-rw-r--r--servers/slapd/back-sql/rdbms_depend/mssql/slapd.conf30
-rw-r--r--servers/slapd/back-sql/rdbms_depend/mssql/testdb_create.sql74
-rw-r--r--servers/slapd/back-sql/rdbms_depend/mssql/testdb_data.sql24
-rw-r--r--servers/slapd/back-sql/rdbms_depend/mssql/testdb_drop.sql39
-rw-r--r--servers/slapd/back-sql/rdbms_depend/mssql/testdb_metadata.sql198
-rw-r--r--servers/slapd/back-sql/rdbms_depend/mysql/backsql_create.sql58
-rw-r--r--servers/slapd/back-sql/rdbms_depend/mysql/backsql_drop.sql7
-rw-r--r--servers/slapd/back-sql/rdbms_depend/mysql/slapd.conf32
-rw-r--r--servers/slapd/back-sql/rdbms_depend/mysql/testdb_create.sql86
-rw-r--r--servers/slapd/back-sql/rdbms_depend/mysql/testdb_data.sql21
-rw-r--r--servers/slapd/back-sql/rdbms_depend/mysql/testdb_drop.sql5
-rw-r--r--servers/slapd/back-sql/rdbms_depend/mysql/testdb_metadata.sql125
-rw-r--r--servers/slapd/back-sql/rdbms_depend/oracle/backsql_create.sql90
-rw-r--r--servers/slapd/back-sql/rdbms_depend/oracle/backsql_drop.sql8
-rw-r--r--servers/slapd/back-sql/rdbms_depend/oracle/slapd.conf32
-rw-r--r--servers/slapd/back-sql/rdbms_depend/oracle/testdb_create.sql68
-rw-r--r--servers/slapd/back-sql/rdbms_depend/oracle/testdb_data.sql27
-rw-r--r--servers/slapd/back-sql/rdbms_depend/oracle/testdb_drop.sql25
-rw-r--r--servers/slapd/back-sql/rdbms_depend/oracle/testdb_metadata.sql252
-rw-r--r--servers/slapd/back-sql/rdbms_depend/pgsql/backsql_create.sql50
-rw-r--r--servers/slapd/back-sql/rdbms_depend/pgsql/backsql_drop.sql4
-rw-r--r--servers/slapd/back-sql/rdbms_depend/pgsql/slapd.conf35
-rw-r--r--servers/slapd/back-sql/rdbms_depend/pgsql/testdb_create.sql55
-rw-r--r--servers/slapd/back-sql/rdbms_depend/pgsql/testdb_data.sql21
-rw-r--r--servers/slapd/back-sql/rdbms_depend/pgsql/testdb_drop.sql13
-rw-r--r--servers/slapd/back-sql/rdbms_depend/pgsql/testdb_metadata.sql146
-rw-r--r--servers/slapd/back-sql/rdbms_depend/timesten/backsql_create.sql66
-rw-r--r--servers/slapd/back-sql/rdbms_depend/timesten/backsql_drop.sql9
-rwxr-xr-xservers/slapd/back-sql/rdbms_depend/timesten/create_schema.sh4
-rw-r--r--servers/slapd/back-sql/rdbms_depend/timesten/dnreverse/Makefile48
-rw-r--r--servers/slapd/back-sql/rdbms_depend/timesten/dnreverse/dnreverse.cpp387
-rw-r--r--servers/slapd/back-sql/rdbms_depend/timesten/slapd.conf31
-rw-r--r--servers/slapd/back-sql/rdbms_depend/timesten/testdb_create.sql36
-rw-r--r--servers/slapd/back-sql/rdbms_depend/timesten/testdb_data.sql16
-rw-r--r--servers/slapd/back-sql/rdbms_depend/timesten/testdb_drop.sql5
-rw-r--r--servers/slapd/back-sql/rdbms_depend/timesten/testdb_metadata.sql108
-rwxr-xr-xservers/slapd/back-sql/rdbms_depend/timesten/ttcreate_schema.sh4
-rw-r--r--servers/slapd/back-sql/rdbms_depend/timesten/tttestdb_create.sql42
-rw-r--r--servers/slapd/back-sql/rdbms_depend/timesten/tttestdb_data.sql20
-rw-r--r--servers/slapd/back-sql/rdbms_depend/timesten/tttestdb_drop.sql5
-rw-r--r--servers/slapd/back-sql/rdbms_depend/timesten/tttestdb_metadata.sql122
-rw-r--r--servers/slapd/back-sql/schema-map.c1012
-rw-r--r--servers/slapd/back-sql/search.c2874
-rw-r--r--servers/slapd/back-sql/sql-wrap.c529
-rw-r--r--servers/slapd/back-sql/util.c572
-rw-r--r--servers/slapd/back-wt/Makefile.in54
-rw-r--r--servers/slapd/back-wt/add.c373
-rw-r--r--servers/slapd/back-wt/attr.c425
-rw-r--r--servers/slapd/back-wt/back-wt.h120
-rw-r--r--servers/slapd/back-wt/bind.c150
-rw-r--r--servers/slapd/back-wt/cache.c231
-rw-r--r--servers/slapd/back-wt/compare.c154
-rw-r--r--servers/slapd/back-wt/config.c209
-rw-r--r--servers/slapd/back-wt/ctx.c117
-rw-r--r--servers/slapd/back-wt/delete.c419
-rw-r--r--servers/slapd/back-wt/dn2entry.c176
-rw-r--r--servers/slapd/back-wt/dn2id.c453
-rw-r--r--servers/slapd/back-wt/extended.c58
-rw-r--r--servers/slapd/back-wt/filterindex.c718
-rw-r--r--servers/slapd/back-wt/id2entry.c352
-rw-r--r--servers/slapd/back-wt/idl.c789
-rw-r--r--servers/slapd/back-wt/idl.h80
-rw-r--r--servers/slapd/back-wt/index.c423
-rw-r--r--servers/slapd/back-wt/init.c385
-rw-r--r--servers/slapd/back-wt/key.c162
-rw-r--r--servers/slapd/back-wt/modify.c714
-rw-r--r--servers/slapd/back-wt/modrdn.c552
-rw-r--r--servers/slapd/back-wt/nextid.c88
-rw-r--r--servers/slapd/back-wt/operational.c110
-rw-r--r--servers/slapd/back-wt/proto-wt.h268
-rw-r--r--servers/slapd/back-wt/search.c759
-rw-r--r--servers/slapd/back-wt/tools.c712
-rw-r--r--servers/slapd/backend.c2055
-rw-r--r--servers/slapd/backglue.c1552
-rw-r--r--servers/slapd/backover.c1484
-rw-r--r--servers/slapd/bconfig.c8150
-rw-r--r--servers/slapd/bind.c555
-rw-r--r--servers/slapd/cancel.c162
-rw-r--r--servers/slapd/ch_malloc.c142
-rw-r--r--servers/slapd/compare.c413
-rw-r--r--servers/slapd/component.c1393
-rw-r--r--servers/slapd/component.h76
-rw-r--r--servers/slapd/config.c2616
-rw-r--r--servers/slapd/connection.c2117
-rw-r--r--servers/slapd/controls.c2228
-rw-r--r--servers/slapd/cr.c501
-rw-r--r--servers/slapd/ctxcsn.c218
-rw-r--r--servers/slapd/daemon.c3610
-rw-r--r--servers/slapd/delete.c244
-rw-r--r--servers/slapd/dn.c1333
-rw-r--r--servers/slapd/entry.c1024
-rw-r--r--servers/slapd/extended.c462
-rw-r--r--servers/slapd/filter.c1450
-rw-r--r--servers/slapd/filterentry.c986
-rw-r--r--servers/slapd/frontend.c174
-rw-r--r--servers/slapd/globals.c38
-rw-r--r--servers/slapd/index.c91
-rw-r--r--servers/slapd/init.c330
-rw-r--r--servers/slapd/ldapsync.c525
-rw-r--r--servers/slapd/limits.c1355
-rw-r--r--servers/slapd/lock.c83
-rw-r--r--servers/slapd/main.c1193
-rw-r--r--servers/slapd/matchedValues.c348
-rw-r--r--servers/slapd/modify.c1099
-rw-r--r--servers/slapd/modrdn.c547
-rw-r--r--servers/slapd/mods.c487
-rw-r--r--servers/slapd/module.c367
-rw-r--r--servers/slapd/mr.c549
-rw-r--r--servers/slapd/mra.c231
-rw-r--r--servers/slapd/nt_svc.c110
-rw-r--r--servers/slapd/oc.c940
-rw-r--r--servers/slapd/oidm.c217
-rw-r--r--servers/slapd/operation.c252
-rw-r--r--servers/slapd/operational.c90
-rw-r--r--servers/slapd/overlays/Makefile.in172
-rw-r--r--servers/slapd/overlays/README5
-rw-r--r--servers/slapd/overlays/accesslog.c2774
-rw-r--r--servers/slapd/overlays/auditlog.c242
-rw-r--r--servers/slapd/overlays/autoca.c1117
-rw-r--r--servers/slapd/overlays/collect.c440
-rw-r--r--servers/slapd/overlays/constraint.c1249
-rw-r--r--servers/slapd/overlays/dds.c2056
-rw-r--r--servers/slapd/overlays/deref.c586
-rw-r--r--servers/slapd/overlays/dyngroup.c234
-rw-r--r--servers/slapd/overlays/dynlist.c2706
-rw-r--r--servers/slapd/overlays/homedir.c2074
-rw-r--r--servers/slapd/overlays/memberof.c2209
-rw-r--r--servers/slapd/overlays/otp.c974
-rw-r--r--servers/slapd/overlays/overlays.c44
-rw-r--r--servers/slapd/overlays/pcache.c5814
-rw-r--r--servers/slapd/overlays/ppolicy.c3413
-rw-r--r--servers/slapd/overlays/refint.c1097
-rw-r--r--servers/slapd/overlays/remoteauth.c996
-rw-r--r--servers/slapd/overlays/retcode.c1578
-rw-r--r--servers/slapd/overlays/rwm.c2723
-rw-r--r--servers/slapd/overlays/rwm.h183
-rw-r--r--servers/slapd/overlays/rwmconf.c413
-rw-r--r--servers/slapd/overlays/rwmdn.c215
-rw-r--r--servers/slapd/overlays/rwmmap.c1347
-rw-r--r--servers/slapd/overlays/seqmod.c207
-rw-r--r--servers/slapd/overlays/slapover.txt158
-rw-r--r--servers/slapd/overlays/sssvlv.c1439
-rw-r--r--servers/slapd/overlays/syncprov.c4368
-rw-r--r--servers/slapd/overlays/translucent.c1497
-rw-r--r--servers/slapd/overlays/unique.c1548
-rw-r--r--servers/slapd/overlays/valsort.c585
-rw-r--r--servers/slapd/passwd.c657
-rw-r--r--servers/slapd/phonetic.c459
-rw-r--r--servers/slapd/proto-slap.h2247
-rw-r--r--servers/slapd/proxyp.c226
-rw-r--r--servers/slapd/pwmods/Makefile.in59
-rw-r--r--servers/slapd/pwmods/README.argon2109
-rw-r--r--servers/slapd/pwmods/argon2.c240
-rw-r--r--servers/slapd/referral.c363
-rw-r--r--servers/slapd/result.c1916
-rw-r--r--servers/slapd/root_dse.c542
-rw-r--r--servers/slapd/sasl.c2050
-rw-r--r--servers/slapd/saslauthz.c2193
-rw-r--r--servers/slapd/schema.c167
-rw-r--r--servers/slapd/schema/README78
-rw-r--r--servers/slapd/schema/collective.ldif48
-rw-r--r--servers/slapd/schema/corba.ldif42
-rw-r--r--servers/slapd/schema/cosine.ldif200
-rw-r--r--servers/slapd/schema/dsee.ldif113
-rw-r--r--servers/slapd/schema/dsee.schema109
-rw-r--r--servers/slapd/schema/duaconf.ldif83
-rw-r--r--servers/slapd/schema/dyngroup.ldif74
-rw-r--r--servers/slapd/schema/dyngroup.schema98
-rw-r--r--servers/slapd/schema/inetorgperson.ldif69
-rw-r--r--servers/slapd/schema/java.ldif59
-rw-r--r--servers/slapd/schema/misc.ldif45
-rw-r--r--servers/slapd/schema/misc.schema75
-rw-r--r--servers/slapd/schema/msuser.ldif4299
-rw-r--r--servers/slapd/schema/msuser.schema4295
-rw-r--r--servers/slapd/schema/namedobject.ldif32
-rw-r--r--servers/slapd/schema/nis.ldif120
-rw-r--r--servers/slapd/schema/nis.schema237
-rw-r--r--servers/slapd/schema/openldap.ldif88
-rw-r--r--servers/slapd/schema/openldap.schema54
-rw-r--r--servers/slapd/schema/pmi.ldif123
-rw-r--r--servers/slapd/schema_check.c938
-rw-r--r--servers/slapd/schema_init.c6978
-rw-r--r--servers/slapd/schema_prep.c1638
-rw-r--r--servers/slapd/schemaparse.c400
-rw-r--r--servers/slapd/search.c439
-rw-r--r--servers/slapd/sets.c832
-rw-r--r--servers/slapd/sets.h75
-rw-r--r--servers/slapd/sl_malloc.c739
-rw-r--r--servers/slapd/slap-config.h240
-rw-r--r--servers/slapd/slap.h3377
-rw-r--r--servers/slapd/slapacl.c411
-rw-r--r--servers/slapd/slapadd.c528
-rw-r--r--servers/slapd/slapauth.c177
-rw-r--r--servers/slapd/slapcat.c175
-rw-r--r--servers/slapd/slapcommon.c1228
-rw-r--r--servers/slapd/slapcommon.h125
-rw-r--r--servers/slapd/slapd.conf79
-rw-r--r--servers/slapd/slapd.ldif101
-rw-r--r--servers/slapd/slapd.service15
-rw-r--r--servers/slapd/slapdn.c107
-rw-r--r--servers/slapd/slapi/Makefile.in51
-rw-r--r--servers/slapd/slapi/TODO16
-rw-r--r--servers/slapd/slapi/plugin.c833
-rw-r--r--servers/slapd/slapi/printmsg.c100
-rw-r--r--servers/slapd/slapi/proto-slapi.h93
-rw-r--r--servers/slapd/slapi/slapi.h204
-rw-r--r--servers/slapd/slapi/slapi_dn.c669
-rw-r--r--servers/slapd/slapi/slapi_ext.c349
-rw-r--r--servers/slapd/slapi/slapi_ops.c950
-rw-r--r--servers/slapd/slapi/slapi_overlay.c952
-rw-r--r--servers/slapd/slapi/slapi_pblock.c1426
-rw-r--r--servers/slapd/slapi/slapi_utils.c3473
-rw-r--r--servers/slapd/slapindex.c110
-rw-r--r--servers/slapd/slapmodify.c650
-rw-r--r--servers/slapd/slappasswd.c301
-rw-r--r--servers/slapd/slapschema.c165
-rw-r--r--servers/slapd/slaptest.c121
-rw-r--r--servers/slapd/starttls.c112
-rw-r--r--servers/slapd/str2filter.c84
-rw-r--r--servers/slapd/syncrepl.c7625
-rw-r--r--servers/slapd/syntax.c457
-rw-r--r--servers/slapd/syslog.c289
-rw-r--r--servers/slapd/txn.c363
-rw-r--r--servers/slapd/unbind.c61
-rw-r--r--servers/slapd/user.c171
-rw-r--r--servers/slapd/value.c798
-rw-r--r--servers/slapd/zn_malloc.c970
-rw-r--r--tests/Makefile.in94
-rw-r--r--tests/README23
-rw-r--r--tests/data/aci.out68
-rw-r--r--tests/data/acl.out.provider367
-rw-r--r--tests/data/certificate.out103
-rw-r--r--tests/data/certificate.tls240
-rw-r--r--tests/data/chain.out414
-rw-r--r--tests/data/chainmod.out393
-rw-r--r--tests/data/chainref.out4
-rw-r--r--tests/data/compsearch.out1558
-rw-r--r--tests/data/constraint/constraint.out31
-rw-r--r--tests/data/constraint/root.ldif25
-rw-r--r--tests/data/constraint/t_fail_01.ldif6
-rw-r--r--tests/data/constraint/t_fail_02.ldif8
-rw-r--r--tests/data/constraint/t_fail_03.ldif8
-rw-r--r--tests/data/constraint/t_fail_04.ldif10
-rw-r--r--tests/data/constraint/t_fail_05.ldif13
-rw-r--r--tests/data/constraint/t_fail_06.ldif7
-rw-r--r--tests/data/constraint/t_fail_07.ldif15
-rw-r--r--tests/data/constraint/t_fail_08.ldif12
-rw-r--r--tests/data/constraint/t_fail_09.ldif10
-rw-r--r--tests/data/constraint/t_fail_10.ldif4
-rw-r--r--tests/data/constraint/t_fail_11.ldif7
-rw-r--r--tests/data/constraint/t_fail_12.ldif10
-rw-r--r--tests/data/constraint/t_fail_13.ldif4
-rw-r--r--tests/data/constraint/t_fail_14.ldif4
-rw-r--r--tests/data/constraint/t_fail_15.ldif5
-rw-r--r--tests/data/constraint/t_fail_16.ldif4
-rw-r--r--tests/data/constraint/t_ok_01.ldif3
-rw-r--r--tests/data/constraint/t_ok_02.ldif5
-rw-r--r--tests/data/constraint/t_ok_03.ldif6
-rw-r--r--tests/data/constraint/t_ok_04.ldif3
-rw-r--r--tests/data/constraint/t_ok_05.ldif5
-rw-r--r--tests/data/constraint/t_ok_06.ldif7
-rw-r--r--tests/data/constraint/t_ok_07.ldif8
-rw-r--r--tests/data/constraint/t_ok_08.ldif12
-rw-r--r--tests/data/constraint/t_ok_09.ldif9
-rw-r--r--tests/data/constraint/t_ok_10.ldif24
-rw-r--r--tests/data/constraint/t_ok_11.ldif32
-rw-r--r--tests/data/constraint/t_ok_12.ldif11
-rw-r--r--tests/data/constraint/t_ok_13.ldif18
-rw-r--r--tests/data/constraint/t_ok_14.ldif8
-rw-r--r--tests/data/constraint/t_ok_15.ldif5
-rw-r--r--tests/data/constraint/user.ldif10
-rw-r--r--tests/data/dds.out70
-rw-r--r--tests/data/deref.out39
-rw-r--r--tests/data/ditcontentrules.conf18
-rw-r--r--tests/data/dn.out233
-rw-r--r--tests/data/do_add.118
-rw-r--r--tests/data/do_add.218
-rw-r--r--tests/data/do_add.318
-rw-r--r--tests/data/do_add.418
-rw-r--r--tests/data/do_bind.06
-rw-r--r--tests/data/do_modify.08
-rw-r--r--tests/data/do_modrdn.04
-rw-r--r--tests/data/do_read.05
-rw-r--r--tests/data/do_search.012
-rw-r--r--tests/data/dynlist.out864
-rw-r--r--tests/data/emptydn.out28
-rw-r--r--tests/data/emptydn.out.slapadd38
-rw-r--r--tests/data/gluesync.out410
-rw-r--r--tests/data/homedir/skel/.dotfile1
l---------tests/data/homedir/skel/directory/broken link1
l---------tests/data/homedir/skel/symlink1
-rw-r--r--tests/data/idassert.out64
-rw-r--r--tests/data/krb5.conf32
-rw-r--r--tests/data/lang-out.ldif35
-rw-r--r--tests/data/ldapglue.out51
-rw-r--r--tests/data/ldapglueanonymous.out6
-rw-r--r--tests/data/lloadd-anon.conf39
-rw-r--r--tests/data/lloadd-backend-issues.conf55
-rw-r--r--tests/data/lloadd-empty.conf25
-rw-r--r--tests/data/lloadd-sasl.conf48
-rw-r--r--tests/data/lloadd-tls.conf57
-rw-r--r--tests/data/lloadd.conf46
-rw-r--r--tests/data/lloadd/monitor.ldif278
-rw-r--r--tests/data/manage.out508
-rw-r--r--tests/data/memberof-refint.out125
-rw-r--r--tests/data/memberof.out341
-rw-r--r--tests/data/meta.out1450
-rw-r--r--tests/data/metaconcurrency.out431
-rw-r--r--tests/data/modify.out.provider396
-rw-r--r--tests/data/modrdn.out.provider.0411
-rw-r--r--tests/data/modrdn.out.provider.120
-rw-r--r--tests/data/modrdn.out.provider.219
-rw-r--r--tests/data/modrdn.out.provider.319
-rw-r--r--tests/data/monitor1.out31
-rw-r--r--tests/data/monitor2.out37
-rw-r--r--tests/data/monitor3.out15
-rw-r--r--tests/data/monitor4.out66
-rw-r--r--tests/data/ndb.conf23
-rw-r--r--tests/data/nis_sample.ldif8092
-rw-r--r--tests/data/otp/hotp.ldif61
-rw-r--r--tests/data/otp/test001-out.ldif5
-rw-r--r--tests/data/otp/totp.ldif64
-rw-r--r--tests/data/passwd.ldif37
-rw-r--r--tests/data/ppolicy.ldif69
-rw-r--r--tests/data/proxycache.out258
-rw-r--r--tests/data/referrals.ldif29
-rw-r--r--tests/data/referrals.out239
-rw-r--r--tests/data/regressions/README20
-rw-r--r--tests/data/regressions/its4184/README1
-rw-r--r--tests/data/regressions/its4184/adds.ldif83
-rwxr-xr-xtests/data/regressions/its4184/its418490
-rw-r--r--tests/data/regressions/its4184/mods.ldif15
-rw-r--r--tests/data/regressions/its4184/slapd.conf59
-rwxr-xr-xtests/data/regressions/its4326/its4326224
-rw-r--r--tests/data/regressions/its4326/slapd.conf43
-rwxr-xr-xtests/data/regressions/its4336/its4336139
-rw-r--r--tests/data/regressions/its4336/slapd.conf34
-rwxr-xr-xtests/data/regressions/its4448/its4448310
-rw-r--r--tests/data/regressions/its4448/slapd-meta.conf58
-rwxr-xr-xtests/data/regressions/its6794/its679484
-rw-r--r--tests/data/regressions/its6794/slapd-glue.conf64
-rwxr-xr-xtests/data/regressions/its7573/its7573121
-rwxr-xr-xtests/data/regressions/its8427/its8427314
-rwxr-xr-xtests/data/regressions/its8427/its8427-2395
-rw-r--r--tests/data/regressions/its8427/slapd.conf31
-rwxr-xr-xtests/data/regressions/its8444/its8444322
-rw-r--r--tests/data/regressions/its8444/slapd-provider1.ldif154
-rw-r--r--tests/data/regressions/its8444/slapd-provider2.ldif154
-rw-r--r--tests/data/regressions/its8444/slapd-provider3.ldif154
-rw-r--r--tests/data/regressions/its8444/slapd-provider4.ldif154
-rwxr-xr-xtests/data/regressions/its8521/its8521335
-rw-r--r--tests/data/regressions/its8521/slapd-consumer.ldif53
-rw-r--r--tests/data/regressions/its8521/slapd-provider.ldif53
-rwxr-xr-xtests/data/regressions/its8616/its8616259
-rw-r--r--tests/data/regressions/its8616/slapd-provider.ldif53
-rwxr-xr-xtests/data/regressions/its8663/its8663279
-rw-r--r--tests/data/regressions/its8663/slapd-provider.ldif53
-rw-r--r--tests/data/regressions/its8667/accounting.ldif63
-rw-r--r--tests/data/regressions/its8667/administrative.ldif63
-rwxr-xr-xtests/data/regressions/its8667/its8667116
-rw-r--r--tests/data/regressions/its8667/janitorial.ldif63
-rw-r--r--tests/data/regressions/its8667/root.ldif46
-rw-r--r--tests/data/regressions/its8667/slapd.ldif91
-rwxr-xr-xtests/data/regressions/its8721/its8721240
-rw-r--r--tests/data/regressions/its8721/slapd-backend.conf38
-rw-r--r--tests/data/regressions/its8721/slapd-proxy.conf41
-rwxr-xr-xtests/data/regressions/its8752/its8752513
-rw-r--r--tests/data/regressions/its8752/slapd.conf41
-rw-r--r--tests/data/regressions/its8752/slapd.conf.mpr142
-rw-r--r--tests/data/regressions/its8800/db.ldif9380
-rwxr-xr-xtests/data/regressions/its8800/its8800208
-rw-r--r--tests/data/regressions/its8800/slapd-provider1.ldif143
-rw-r--r--tests/data/regressions/its8800/slapd-provider2.ldif143
-rw-r--r--tests/data/regressions/its8800/slapd-provider3.ldif143
-rw-r--r--tests/data/regressions/its8800/slapd-provider4.ldif143
-rw-r--r--tests/data/regressions/its9051/db.ldif9339
-rwxr-xr-xtests/data/regressions/its9051/its9051185
-rw-r--r--tests/data/regressions/its9051/slapd-provider.ldif99
-rw-r--r--tests/data/regressions/its9282/config.ldif68
-rw-r--r--tests/data/regressions/its9282/exampledb.ldif29032
-rwxr-xr-xtests/data/regressions/its9282/its9282267
-rw-r--r--tests/data/regressions/its9282/noentry.flt0
-rwxr-xr-xtests/data/regressions/its9288/its9288186
-rw-r--r--tests/data/regressions/its9288/slapd-proxy.conf41
-rwxr-xr-xtests/data/regressions/its9338/its9338100
-rwxr-xr-xtests/data/regressions/its9400/its9400161
-rw-r--r--tests/data/regressions/its9400/slapd-proxy-idassert.conf52
-rw-r--r--tests/data/relay.out2585
-rw-r--r--tests/data/remoteauth/config.ldif21
-rw-r--r--tests/data/remoteauth/default_domain3
-rw-r--r--tests/data/remoteauth/remoteauth.conf21
-rw-r--r--tests/data/retcode.conf115
-rw-r--r--tests/data/rootdse.ldif2
-rw-r--r--tests/data/search.out.provider389
-rw-r--r--tests/data/search.out.xsearch682
-rw-r--r--tests/data/slapd-2db.conf50
-rw-r--r--tests/data/slapd-aci.conf56
-rw-r--r--tests/data/slapd-acl.conf144
-rw-r--r--tests/data/slapd-asyncmeta.conf85
-rw-r--r--tests/data/slapd-cache-provider-proxyauthz.conf45
-rw-r--r--tests/data/slapd-cache-provider.conf42
-rw-r--r--tests/data/slapd-chain1.conf61
-rw-r--r--tests/data/slapd-chain2.conf60
-rw-r--r--tests/data/slapd-component.conf46
-rw-r--r--tests/data/slapd-config-naked.conf12
-rw-r--r--tests/data/slapd-config-undo.conf19
-rw-r--r--tests/data/slapd-dds.conf85
-rw-r--r--tests/data/slapd-deltasync-consumer.conf74
-rw-r--r--tests/data/slapd-deltasync-provider.conf77
-rw-r--r--tests/data/slapd-deref.conf48
-rw-r--r--tests/data/slapd-dirsync1.conf65
-rw-r--r--tests/data/slapd-dn.conf44
-rw-r--r--tests/data/slapd-dnssrv.conf33
-rw-r--r--tests/data/slapd-dsee-consumer1.conf61
-rw-r--r--tests/data/slapd-dsee-consumer2.conf61
-rw-r--r--tests/data/slapd-dynamic.ldif8
-rw-r--r--tests/data/slapd-dynlist.conf54
-rw-r--r--tests/data/slapd-emptydn.conf77
-rw-r--r--tests/data/slapd-glue-ldap.conf79
-rw-r--r--tests/data/slapd-glue-syncrepl1.conf87
-rw-r--r--tests/data/slapd-glue-syncrepl2.conf90
-rw-r--r--tests/data/slapd-glue.conf64
-rw-r--r--tests/data/slapd-homedir.conf57
-rw-r--r--tests/data/slapd-idassert.conf125
-rw-r--r--tests/data/slapd-ldapglue.conf77
-rw-r--r--tests/data/slapd-ldapgluegroups.conf59
-rw-r--r--tests/data/slapd-ldapgluepeople.conf61
-rw-r--r--tests/data/slapd-limits.conf62
-rw-r--r--tests/data/slapd-lload.conf41
-rw-r--r--tests/data/slapd-meta-target1.conf62
-rw-r--r--tests/data/slapd-meta-target2.conf56
-rw-r--r--tests/data/slapd-meta.conf83
-rw-r--r--tests/data/slapd-nis-provider.conf53
-rw-r--r--tests/data/slapd-passwd.conf37
-rw-r--r--tests/data/slapd-ppolicy.conf58
-rw-r--r--tests/data/slapd-provider.conf43
-rw-r--r--tests/data/slapd-proxyauthz.conf73
-rw-r--r--tests/data/slapd-proxycache.conf63
-rw-r--r--tests/data/slapd-proxytimeout.conf71
-rw-r--r--tests/data/slapd-pw.conf56
-rw-r--r--tests/data/slapd-ref-consumer.conf45
-rw-r--r--tests/data/slapd-referrals.conf40
-rw-r--r--tests/data/slapd-refint.conf48
-rw-r--r--tests/data/slapd-relay.conf100
-rw-r--r--tests/data/slapd-repl-consumer-remote.conf78
-rw-r--r--tests/data/slapd-retcode.conf55
-rw-r--r--tests/data/slapd-schema.conf51
-rw-r--r--tests/data/slapd-sql-syncrepl-provider.conf78
-rw-r--r--tests/data/slapd-sql.conf74
-rw-r--r--tests/data/slapd-syncrepl-consumer-persist-ldap.conf74
-rw-r--r--tests/data/slapd-syncrepl-consumer-persist1.conf72
-rw-r--r--tests/data/slapd-syncrepl-consumer-persist2.conf44
-rw-r--r--tests/data/slapd-syncrepl-consumer-persist3.conf56
-rw-r--r--tests/data/slapd-syncrepl-consumer-refresh1.conf63
-rw-r--r--tests/data/slapd-syncrepl-consumer-refresh2.conf57
-rw-r--r--tests/data/slapd-syncrepl-multiproxy.conf103
-rw-r--r--tests/data/slapd-syncrepl-provider.conf48
-rw-r--r--tests/data/slapd-tls-sasl.conf62
-rw-r--r--tests/data/slapd-tls.conf58
-rw-r--r--tests/data/slapd-translucent-local.conf63
-rw-r--r--tests/data/slapd-translucent-remote.conf44
-rw-r--r--tests/data/slapd-unique.conf59
-rw-r--r--tests/data/slapd-valregex.conf70
-rw-r--r--tests/data/slapd-valsort.conf53
-rw-r--r--tests/data/slapd-whoami.conf62
-rw-r--r--tests/data/slapd.conf49
-rw-r--r--tests/data/slapd2.conf42
-rw-r--r--tests/data/sql-concurrency/do_add.19
-rw-r--r--tests/data/sql-concurrency/do_add.210
-rw-r--r--tests/data/sql-concurrency/do_add.310
-rw-r--r--tests/data/sql-concurrency/do_add.410
-rw-r--r--tests/data/sql-concurrency/do_bind.02
-rw-r--r--tests/data/sql-concurrency/do_modrdn.02
-rw-r--r--tests/data/sql-concurrency/do_read.04
-rw-r--r--tests/data/sql-concurrency/do_search.012
-rw-r--r--tests/data/sql-read.out623
-rw-r--r--tests/data/sql-write.out576
-rw-r--r--tests/data/subtree-rename.out97
-rw-r--r--tests/data/test-chain1.ldif374
-rw-r--r--tests/data/test-chain2.ldif88
-rw-r--r--tests/data/test-compmatch.ldif483
-rwxr-xr-xtests/data/test-deref.ldif43
-rw-r--r--tests/data/test-dirsync-cp.ldif12
-rw-r--r--tests/data/test-dirsync-nocp.ldif272
-rw-r--r--tests/data/test-dn.ldif362
-rw-r--r--tests/data/test-emptydn1.ldif14
-rw-r--r--tests/data/test-emptydn2.ldif39
-rw-r--r--tests/data/test-glued.ldif410
-rw-r--r--tests/data/test-idassert1.ldif73
-rw-r--r--tests/data/test-idassert2.ldif27
-rw-r--r--tests/data/test-lang.ldif12
-rw-r--r--tests/data/test-ldapglue.ldif5
-rw-r--r--tests/data/test-ldapgluegroups.ldif23
-rw-r--r--tests/data/test-ldapgluepeople.ldif28
-rw-r--r--tests/data/test-limits.ldif137
-rw-r--r--tests/data/test-meta.ldif25
-rw-r--r--tests/data/test-modify.ldif110
-rw-r--r--tests/data/test-ordered-cp.ldif16
-rw-r--r--tests/data/test-ordered-nocp.ldif402
-rw-r--r--tests/data/test-ordered.ldif411
-rwxr-xr-xtests/data/test-refint.ldif115
-rw-r--r--tests/data/test-reordered.ldif55
-rw-r--r--tests/data/test-translucent-add.ldif10
-rw-r--r--tests/data/test-translucent-config.ldif26
-rw-r--r--tests/data/test-translucent-data.ldif43
-rw-r--r--tests/data/test-translucent-merged.ldif41
-rwxr-xr-xtests/data/test-unique.ldif29
-rw-r--r--tests/data/test-unordered.ldif55
-rwxr-xr-xtests/data/test-valsort.ldif49
-rw-r--r--tests/data/test-whoami.ldif468
-rw-r--r--tests/data/test.ldif412
-rw-r--r--tests/data/test.schema69
-rw-r--r--tests/data/tls/ca/certs/testsuiteCA.crt121
-rw-r--r--tests/data/tls/ca/private/testsuiteCA.key52
-rw-r--r--tests/data/tls/certs/bjensen@mailgw.example.com.crt32
-rw-r--r--tests/data/tls/certs/localhost.crt32
-rw-r--r--tests/data/tls/conf/openssl.cnf129
-rwxr-xr-xtests/data/tls/create-crt.sh81
-rw-r--r--tests/data/tls/private/bjensen@mailgw.example.com.key52
-rw-r--r--tests/data/tls/private/localhost.key52
-rwxr-xr-xtests/data/valsort1.out45
-rwxr-xr-xtests/data/valsort2.out45
-rwxr-xr-xtests/data/valsort3.out61
-rw-r--r--tests/progs/Makefile.in66
-rw-r--r--tests/progs/ldif-filter.c256
-rw-r--r--tests/progs/slapd-addel.c302
-rw-r--r--tests/progs/slapd-auth.c335
-rw-r--r--tests/progs/slapd-bind.c551
-rw-r--r--tests/progs/slapd-common.c550
-rw-r--r--tests/progs/slapd-common.h92
-rw-r--r--tests/progs/slapd-modify.c225
-rw-r--r--tests/progs/slapd-modrdn.c229
-rw-r--r--tests/progs/slapd-mtread.c722
-rw-r--r--tests/progs/slapd-read.c445
-rw-r--r--tests/progs/slapd-search.c491
-rw-r--r--tests/progs/slapd-tester.c1143
-rw-r--r--tests/progs/slapd-watcher.c823
-rw-r--r--tests/run.in292
-rwxr-xr-xtests/scripts/all106
-rwxr-xr-xtests/scripts/conf.sh98
-rwxr-xr-xtests/scripts/confdirsync.sh18
-rwxr-xr-xtests/scripts/defines.sh451
-rw-r--r--tests/scripts/gdb.py85
-rwxr-xr-xtests/scripts/grandchild_wrapper.py72
-rwxr-xr-xtests/scripts/its-all52
-rwxr-xr-xtests/scripts/lloadd-all105
-rwxr-xr-xtests/scripts/lloadd/test000-rootdse118
-rwxr-xr-xtests/scripts/lloadd/test001-backend-issues218
-rwxr-xr-xtests/scripts/lloadd/test002-load174
-rwxr-xr-xtests/scripts/lloadd/test003-cnconfig433
-rwxr-xr-xtests/scripts/lloadd/test004-monitor345
-rwxr-xr-xtests/scripts/lloadd/test005-tls272
-rwxr-xr-xtests/scripts/lloadd/test006-sasl252
-rwxr-xr-xtests/scripts/monitor_data.sh42
-rwxr-xr-xtests/scripts/passwd-search133
-rwxr-xr-xtests/scripts/relay395
-rwxr-xr-xtests/scripts/setup_kdc.sh158
-rwxr-xr-xtests/scripts/sql-all70
-rwxr-xr-xtests/scripts/sql-test000-read568
-rwxr-xr-xtests/scripts/sql-test001-concurrency138
-rwxr-xr-xtests/scripts/sql-test900-write573
-rwxr-xr-xtests/scripts/sql-test901-syncrepl692
-rwxr-xr-xtests/scripts/start-server63
-rwxr-xr-xtests/scripts/start-server-nolog63
-rwxr-xr-xtests/scripts/start-server242
-rwxr-xr-xtests/scripts/start-server2-nolog42
-rwxr-xr-xtests/scripts/startup_nis_ldap_server.sh56
-rwxr-xr-xtests/scripts/test000-rootdse82
-rwxr-xr-xtests/scripts/test001-slapadd146
-rwxr-xr-xtests/scripts/test002-populate83
-rwxr-xr-xtests/scripts/test003-search155
-rwxr-xr-xtests/scripts/test004-modify122
-rwxr-xr-xtests/scripts/test005-modrdn300
-rwxr-xr-xtests/scripts/test006-acls667
-rwxr-xr-xtests/scripts/test007-slapmodify90
-rwxr-xr-xtests/scripts/test008-concurrency99
-rwxr-xr-xtests/scripts/test009-referral181
-rwxr-xr-xtests/scripts/test010-passwd189
-rwxr-xr-xtests/scripts/test011-glue-slapadd98
-rwxr-xr-xtests/scripts/test012-glue-populate83
-rwxr-xr-xtests/scripts/test013-language117
-rwxr-xr-xtests/scripts/test014-whoami468
-rwxr-xr-xtests/scripts/test015-xsearch272
-rwxr-xr-xtests/scripts/test016-subref197
-rwxr-xr-xtests/scripts/test017-syncreplication-refresh356
-rwxr-xr-xtests/scripts/test018-syncreplication-persist548
-rwxr-xr-xtests/scripts/test019-syncreplication-cascade487
-rwxr-xr-xtests/scripts/test020-proxycache643
-rwxr-xr-xtests/scripts/test021-certificate325
-rwxr-xr-xtests/scripts/test022-ppolicy778
-rwxr-xr-xtests/scripts/test023-refint276
-rwxr-xr-xtests/scripts/test024-unique845
-rwxr-xr-xtests/scripts/test025-limits1420
-rwxr-xr-xtests/scripts/test026-dn180
-rwxr-xr-xtests/scripts/test027-emptydn175
-rwxr-xr-xtests/scripts/test028-idassert273
-rwxr-xr-xtests/scripts/test029-ldapglue224
-rwxr-xr-xtests/scripts/test030-relay98
-rwxr-xr-xtests/scripts/test031-component-filter330
-rwxr-xr-xtests/scripts/test032-chain340
-rwxr-xr-xtests/scripts/test033-glue-syncrepl189
-rwxr-xr-xtests/scripts/test034-translucent807
-rwxr-xr-xtests/scripts/test035-meta739
-rwxr-xr-xtests/scripts/test036-meta-concurrency225
-rwxr-xr-xtests/scripts/test037-manage219
-rwxr-xr-xtests/scripts/test038-retcode112
-rwxr-xr-xtests/scripts/test039-glue-ldap-concurrency231
-rwxr-xr-xtests/scripts/test040-subtree-rename209
-rwxr-xr-xtests/scripts/test041-aci258
-rwxr-xr-xtests/scripts/test042-valsort229
-rwxr-xr-xtests/scripts/test043-delta-syncrepl552
-rwxr-xr-xtests/scripts/test044-dynlist1111
-rwxr-xr-xtests/scripts/test045-syncreplication-proxied867
-rwxr-xr-xtests/scripts/test046-dds575
-rwxr-xr-xtests/scripts/test047-ldap754
-rwxr-xr-xtests/scripts/test048-syncrepl-multiproxy596
-rwxr-xr-xtests/scripts/test049-sync-config406
-rwxr-xr-xtests/scripts/test050-syncrepl-multiprovider789
-rwxr-xr-xtests/scripts/test051-config-undo117
-rwxr-xr-xtests/scripts/test052-memberof464
-rwxr-xr-xtests/scripts/test053-syncprov-glue502
-rwxr-xr-xtests/scripts/test054-syncreplication-parallel-load377
-rwxr-xr-xtests/scripts/test055-valregex117
-rwxr-xr-xtests/scripts/test056-monitor162
-rwxr-xr-xtests/scripts/test057-memberof-refint280
-rwxr-xr-xtests/scripts/test058-syncrepl-asymmetric2471
-rwxr-xr-xtests/scripts/test059-consumer-config438
-rwxr-xr-xtests/scripts/test060-mt-hot299
-rwxr-xr-xtests/scripts/test061-syncreplication-initiation668
-rwxr-xr-xtests/scripts/test062-config-delete177
-rwxr-xr-xtests/scripts/test063-delta-multiprovider613
-rwxr-xr-xtests/scripts/test064-constraint215
-rwxr-xr-xtests/scripts/test065-proxyauthz255
-rwxr-xr-xtests/scripts/test066-autoca339
-rwxr-xr-xtests/scripts/test067-tls304
-rwxr-xr-xtests/scripts/test068-sasl-tls-external129
-rwxr-xr-xtests/scripts/test069-delta-multiprovider-starttls574
-rwxr-xr-xtests/scripts/test070-delta-multiprovider-ldaps571
-rwxr-xr-xtests/scripts/test071-dirsync370
-rwxr-xr-xtests/scripts/test072-dsee-sync331
-rwxr-xr-xtests/scripts/test073-asyncmeta620
-rwxr-xr-xtests/scripts/test074-asyncmeta-concurrency226
-rwxr-xr-xtests/scripts/test075-dsee-persist421
-rwxr-xr-xtests/scripts/test076-authid-rewrite640
-rwxr-xr-xtests/scripts/test077-sasl-gssapi255
-rwxr-xr-xtests/scripts/test078-persistent-sessionlog646
-rwxr-xr-xtests/scripts/test079-proxy-timeout374
-rwxr-xr-xtests/scripts/test080-hotp295
-rwxr-xr-xtests/scripts/test081-totp143
-rwxr-xr-xtests/scripts/test081-totp.py182
-rwxr-xr-xtests/scripts/test082-remoteauth417
-rwxr-xr-xtests/scripts/test083-argon2154
-rwxr-xr-xtests/scripts/test084-deref94
-rwxr-xr-xtests/scripts/test085-homedir139
-rwxr-xr-xtests/scripts/test086-delta-consumer-config581
1874 files changed, 836379 insertions, 0 deletions
diff --git a/ANNOUNCEMENT b/ANNOUNCEMENT
new file mode 100644
index 0000000..a3869c1
--- /dev/null
+++ b/ANNOUNCEMENT
@@ -0,0 +1,168 @@
+A N N O U N C E M E N T -- OpenLDAP 2.5
+
+ The OpenLDAP Project is pleased to announce the availability
+ of OpenLDAP Software 2.5, a suite of the Lightweight Directory
+ Access Protocol (v3) servers, clients, utilities, and
+ development tools.
+
+ This release contains the following major enhancements:
+
+ * Slapd(8) enhancements
+ - kqueue support for BSD based OSes
+ - cn=config delete support
+ - Don't use copy control support
+ - Threadpool queues
+ - non-blocking TLS support
+ - Configurable TCP read and write buffers
+ for listeners.
+ - LDAP Transaction support
+ - MS AD Lazy commit control
+ - MS AD replication support
+ - DSEE replication support
+ - Sun/Netscape draft persistent search support
+ - HAProxy proxy protocol v2 support
+ * New backends
+ - back-wt: Wiredtiger backend to slapd (Experimental)
+ - back-asyncmeta: Async version of back-meta
+ * Backend updates
+ - back-ldap: CANCHAINOPS
+ - back-meta META_CLIENT_PR
+ - back-monitor is always built as a part of slapd
+ * Retired backends
+ - back-bdb
+ - back-hdb
+ - back-shell
+ * Deprecated backends
+ - back-ndb
+ - back-sql
+ - back-perl
+ * New overlays
+ - autoca
+ - homedir
+ - otp
+ - remoteauth
+ * New password hashing module
+ - argon2
+ * Overlay updates
+ - pcache can access private DB with control
+ - pcache can remove a query from the cache
+ with exop
+ - back-monitor support for pcache
+ - ppolicy updated with password policy
+ draft 10 support
+ - dynlist can now generate (is)memberOf
+ dynamically
+ - dynlist do reverse lookups to find all
+ groups a user belongs to
+ - unique can now do db wide locking to avoid
+ race conditions
+ * New Library
+ - libldif provides an LDIF parsing API
+ * Library updates
+ - libldap_r has been merged with libldap
+ - libldap has TLS channel binding support
+ - libldap has TLS public key pinning support
+ - libldap has TLS SNI support
+ - libldap has GSSAPI channel binding support
+ * Clients and tools
+ - slapmodify for offline updates to cn=config
+ * Significant performance enhancements throughout
+ the client and server code base
+ * New contrib overlays
+ - adremap remaps attributes for PAM/NSS MS AD
+ support
+ - authzid implements RFC 3829 support
+ - datamorph stores enumerated values and fixed
+ size integers
+ - ppm adds additional password checking criteria
+ to the slapo-ppolicy overlay
+ - pw-radius allows bind operations to be
+ passed to the specified radius server(s)
+ - rbac intercepts, decodes and enforces specific
+ RBAC policies per the Apache Fortress RBAC
+ data formats
+ - totp provides one time password support
+ - usn adds MS AD usnCreated and usnChanged
+ operational attributes to entries
+ - variant allows attributes/values to be shared
+ between several entries
+ - vc provides the verify credentials
+ extended operation
+
+ This release includes the following major components:
+
+ * slapd - a stand-alone LDAP directory server
+ * lloadd - a stand-alone LDAP load balancing proxy server
+ * -lldap - a LDAP client library
+ * -llber - a lightweight BER/DER encoding/decoding library
+ * LDIF tools - data conversion tools for use with slapd
+ * LDAP tools - A collection of command line LDAP utilities
+ * Admin Guide, Manual Pages - associated documentation
+
+ In addition, there are some contributed components:
+
+ * LDAPC++ - a LDAP C++ SDK
+ * Various slapd modules and slapi plugins
+
+
+ACKNOWLEDGEMENTS
+
+ OpenLDAP Software is developed by the OpenLDAP Project. The
+ Project consists of a team of volunteers who use the
+ Internet to coordinate their activities. The Project is
+ an organized activity of the OpenLDAP Foundation.
+
+ OpenLDAP Software is derived from University of Michigan LDAP,
+ release 3.3.
+
+
+AVAILABILITY
+
+ This software is available under the OpenLDAP Public License,
+ an non-restrictive, "free", open-source license. Download
+ information is available at:
+
+ https://www.OpenLDAP.org/software/download/
+
+
+SUPPORT
+
+ OpenLDAP Software is user supported:
+
+ https://www.openldap.org/support/
+
+ The OpenLDAP Administrator's Guide, which includes quick
+ start instructions, is available at:
+
+ https://www.openldap.org/doc/admin/
+
+ In addition, there are also a number of discussion lists
+ related to OpenLDAP Software. A list of mailing lists is
+ available at:
+
+ https://www.OpenLDAP.org/lists/
+
+ To report bugs, please use project's Issue Tracking System:
+
+ https://bugs.openldap.org/
+
+ The OpenLDAP home page containing lots of interesting information
+ and online documentation is available at this URL:
+
+ https://www.OpenLDAP.org/
+
+
+SUPPORTED PLATFORMS
+
+ This release has been ported to many UNIX (and UNIX-like)
+ platforms including Darwin, FreeBSD, Linux, NetBSD, OpenBSD
+ and most commercial UNIX systems. The release has also been
+ ported (in part or in whole) to other platforms including
+ Apple MacOS X, IBM zOS, and Microsoft Windows NT/2000/etc.
+
+---
+OpenLDAP is a registered trademark of the OpenLDAP Foundation.
+
+Copyright 1999-2022 The OpenLDAP Foundation, Redwood City,
+California, USA. All Rights Reserved. Permission to copy and
+distribute verbatim copies of this document is granted.
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..736656d
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,197 @@
+OpenLDAP 2.5 Change Log
+
+OpenLDAP 2.5.13 Release (2022/07/14)
+ Fixed librewrite declaration of calloc (ITS#9841)
+ Fixed libldap memory leaks (ITS#9876)
+ Fixed slapd kqueue support (ITS#9847)
+ Fixed slapd delta-sync DN leak on ADD ops (ITS#9866)
+ Fixed slapd replication with back-glue (ITS#9868)
+ Fixed slapd-mdb to check for stale readers on MDB_READERS_FULL (ITS#7165)
+ Fixed slapo-accesslog onetime memory leak (ITS#9864)
+ Fixed slapo-ppolicy interaction with slapo-rwm (ITS#9871)
+ Fixed slapo-syncprov memory leaks (ITS#9867)
+ Fixed slapo-syncprov fallback in delta-sync mode (ITS#9823)
+ Fixed slapo-unique to not release NULL entry (ITS#8245)
+ Build Environment
+ Added slapd-watcher -c contextDN option (ITS#9865)
+ Fixed parallel builds (ITS#9840)
+ Fixed test020 to skip back-wt (ITS#9859)
+ Fixed slapd-watcher SID handling with single URI (ITS#9850)
+ Fixed test043 with workaround for ITS#9878
+ Contrib
+ Added slapo-emptyds contrib module (ITS#8882)
+ Fixed slapo-autogroup backwards compat (ITS#9020)
+ Documentation
+ Fixed ldap_get_option(3) to clarify ldap_get/set_option restrictions (ITS#9824)
+ Fixed slapd-ldap(5),slapd-meta(5) missing bold tag on authz parameter (ITS#9872)
+
+OpenLDAP 2.5.12 Release (2022/05/04)
+ Fixed libldap to drop connection when non-LDAP data is received (ITS#9803)
+ Fixed libldap to allow newlines at end of included file (ITS#9811)
+ Fixed slapd slaptest conversion of olcLastBind (ITS#9808)
+ Fixed slapd usage of thread local counters (ITS#9789)
+ Fixed slapd to clear runqueue task correctly (ITS#9785)
+ Fixed slapd idletimeout handling (ITS#9820)
+ Fixed slapd bconfig locking for cn=config replication (ITS#9584)
+ Fixed slapd syncrepl handling of new sessions (ITS#9584)
+ Fixed slapd to clear connections on bind (ITS#9799)
+ Fixed slapd to correctly advance connections index (ITS#9831)
+ Fixed slapd syncrepl ODSEE replication of unknown attr (ITS#9801)
+ Fixed slapd-asyncmeta memory leak in keepalive setting (ITS#9802)
+ Fixed slapd-ldap memory leak in keepalive setting (ITS#9802)
+ Fixed slapd-meta SEGV on config rewrite (ITS#9802)
+ Fixed slapd-meta ordering on config rewrite (ITS#9802)
+ Fixed slapd-meta memory leak in keepalive setting (ITS#9802)
+ Fixed slapd-monitor SEGV on shutdown (ITS#9809)
+ Fixed slapd-sql to properly escape filter value (ITS#9815)
+ Fixed slapo-dynlist dynamic group regression (ITS#9825)
+ Fixed slapo-pcache SEGV on shutdown (ITS#9809)
+ Fixed slapo-ppolicy operation handling to be consistent (ITS#9794)
+ Fixed slapo-translucent to correctly duplicate substring filters (ITS#9818)
+ Build Environment
+ Fix compilation with openssl exclusions (ITS#9791)
+ Fix warnings from make jobserver (ITS#9788)
+ Fix compiliation with certain versions of gcc (ITS#9790)
+ Documentation
+ Fixed slapd.conf(5)/slapd-config(5) syncrepl sizelimit/timelimit documentation (ITS#9804)
+
+OpenLDAP 2.5.11 Release (2022/01/20)
+ Fix broken build release variable
+
+OpenLDAP 2.5.10 Release (2022/01/20)
+ Fixed libldap to init client socket port (ITS#9743)
+ Fixed libldap with referrals (ITS#9781)
+ Fixed slapd to allow objectClass edits with no net change (ITS#9772)
+ Fixed slapd syncrepl recreation of deleted entries (ITS#9282)
+ Fixed slapd syncrepl replication with ODSEE (ITS#9707)
+ Fixed slapd syncrepl to reject REFRESH for precise resync (ITS#9742)
+ Fixed slapd syncrepl when X-ORDERED is specified (ITS#9761)
+ Fixed slapd syncrepl to better handle out of order delete ops (ITS#9751)
+ Fixed slapd syncrepl to correctly close connections when config is deleted (ITS#9776)
+ Fixed slapd-mdb to update indices correctly on replace ops (ITS#9753)
+ Fixed slapd-wt to set correct flags (ITS#9760)
+ Fixed slapo-accesslog to fix inconsistently normalized minCSN (ITS#9752)
+ Fixed slapo-autogroup to maintain values in insertion order (ITS#9766)
+ Fixed slapo-constraint to maintain values in insertion order (ITS#9770)
+ Fixed slapo-dyngroup to maintain values in insertion order (ITS#9762)
+ Fixed slapo-dynlist compare operation for static groups (ITS#9747)
+ Fixed slapo-dynlist static group filter with multiple members (ITS#9779)
+ slapo-refint to maintain values in insertion order (ITS#9763)
+ Fixed slapo-retcode to honor requested insert position (ITS#9759)
+ Fixed slapo-syncprov memory leak (ITS#8039)
+ Fixed slapo-syncprov to generate a more accurate accesslog query (ITS#9756)
+ Fixed slapo-translucent to warn on invalid config (ITS#9768)
+ Fixed slapo-unique to warn on invalid config (ITS#9767)
+ Fixed slapo-valsort to maintain values in insertion order (ITS#9764)
+ Documentation
+ Fixed slapo-accesslog(5) to clarify logoldattr usage (ITS#9749)
+
+OpenLDAP 2.5.9 Release (2021/10/25)
+ Fixed slapo-accesslog to initialize minCSN on import of 2.4 databases (ITS#9720)
+
+OpenLDAP 2.5.8 Release (2021/10/11)
+ Fixed libldap ldap_int_tls_connect: isdigit() requires unsigned char (ITS#9668)
+ Fixed libldap memory leak in ldap_get_option LDAP_OPT_X_TLS_PEERCERT (ITS#9696)
+ Fixed slapd to allow normalized values for namingContexts in cn=monitor (ITS#8341)
+ Fixed slapd to normalize the suffix in rootDSE (ITS#9664)
+ Fixed slapd slapadd to avoid destroying configDB prematurely (ITS#9678)
+ Fixed slapd to not spam logs with lastbind information (ITS#9156)
+ Fixed slapd slaptest migration to correctly set olcTSLVerifyClient (ITS#9711)
+ Fixed slapd-mdb multival delete handling (ITS#9712)
+ Fixed slapd-sql ldap_entry_objectclass table for mariadb/mysql (ITS#9679)
+ Fixed slapd-wt multiple issues (ITS#9463)
+ Fixed slapd-wt to close cache db correctly (ITS#9631)
+ Fixed slapo-ppolicy to restore OpenLDAP 2.4 compatibility (ITS#9671)
+ Fixed slapo-syncprov to free uuid list when finished replaying sessionlog (ITS#6467)
+ Build
+ Fixed libldap result.c compilation on musl systems (ITS#9648)
+ Fixed slapd duplicate definition of peerbv (ITS#9659)
+ Fixed test suite with memberof modular builds (ITS#9464)
+ Contrib
+ Added man page for ppm contrib module (ITS#9644)
+ Fix crash when pwdCheckModuleArg is not defined for ppm (ITS#9656)
+ Documentation
+ Fixed guide download link for heimdal (ITS#9669)
+ Fixed guide documentation for TLSECName (ITS#9687)
+ Fixed guide documentation missing tags (ITS#9693)
+ Fixed guide loadbalancer typo (ITS#9699)
+ Fixed guide synprov-nopresent redundant text (ITS#9689)
+ Fixed guide various typos and fix config alignment (ITS#9706)
+ Removed ppolicy.schema from servers/slapd/schema/README (ITS#9156)
+ Fixed slapd.conf(5)/slapd-config(5) to document default for database monitoring (ITS#9674)
+ Fixed slapd-meta(5)/slapd-asyncmeta(5) verbiage for try-propagate (ITS#9646)
+ Fixed slapo-syncprov(5) to note entryCSN indexing is highly recommended (ITS#9688)
+
+OpenLDAP 2.5.7 Release (2021/08/18)
+ Fixed lloadd client state tracking (ITS#9624)
+ Fixed slapd bconfig to canonicalize structuralObjectclass (ITS#9611)
+ Fixed slapd-ldif duplicate controls response (ITS#9497)
+ Fixed slapd-mdb multival crash when attribute is missing an equality matchingrule (ITS#9621)
+ Fixed slapd-mdb compatibility with OpenLDAP 2.4 MDB databases (ITS#8958)
+ Fixed slapd-mdb idlexp maximum size handling (ITS#9637)
+ Fixed slapd-monitor number of ops executing with asynchronous backends (ITS#9628)
+ Fixed slapd-sql to add support for ppolicy attributes (ITS#9629)
+ Fixed slapd-sql to close transactions after bind and search (ITS#9630)
+ Fixed slapo-accesslog to make reqMod optional (ITS#9569)
+ Fixed slapo-ppolicy logging when pwdChangedTime attribute is not present (ITS#9625)
+ Documentation
+ slapd-mdb(5) note max idlexp size is 30, not 31 (ITS#9637)
+ slapo-accesslog(5) note that reqMod is optional (ITS#9569)
+ Add ldapvc(1) man page (ITS#9549)
+ Add guide section on load balancer (ITS#9443)
+ Updated guide to document multiprovider as replacement for mirrormode (ITS#9200)
+ Updated guide to clarify slapd-mdb upgrade requirements (ITS#9200)
+ Updated guide to document removal of deprecated options from client tools (ITS#9200)
+
+OpenLDAP 2.5.6 Release (2021/07/27)
+ Fixed libldap buffer overflow (ITS#9578)
+ Fixed libldap missing mutex unlock on connection alloc failure (ITS#9590)
+ Fixed lloadd cn=config olcBkLloadClientMaxPending setting (ITS#8747)
+ Fixed slapd multiple config defaults (ITS#9363)
+ Fixed slapd ipv6 addresses to work with tcp wrappers (ITS#9603)
+ Fixed slapo-syncprov delete of nonexistent sessionlog (ITS#9608)
+ Build
+ Fixed library symbol versioning on Solaris (ITS#9591)
+ Fixed compile warning in libldap/tpool.c (ITS#9601)
+ Fixed compile warning in libldap/tls_o.c (ITS#9602)
+ Contrib
+ Fixed ppm module for sysconfdir (ITS#7832)
+ Documentation
+ Updated guide to document multival, idlexp, and maxentrysize (ITS#9613, ITS#9614)
+
+OpenLDAP 2.5.5 Release (2021/06/03)
+ Added libldap LDAP_OPT_TCP_USER_TIMEOUT support (ITS#9502)
+ Added lloadd tcp-user-timeout support (ITS#9502)
+ Added slapd-asyncmeta tcp-user-timeout support (ITS#9502)
+ Added slapd-ldap tcp-user-timeout support (ITS#9502)
+ Added slapd-meta tcp-user-timeout support (ITS#9502)
+ Fixed incorrect control OIDs for AuthZ Identity (ITS#9542)
+ Fixed libldap typo in util-int.c (ITS#9541)
+ Fixed libldap double free of LDAP_OPT_DEFBASE (ITS#9530)
+ Fixed libldap better TLS1.3 cipher suite handling (ITS#9521, ITS#9546)
+ Fixed lloadd multiple issues (ITS#8747)
+ Fixed slapd slap_op_time to avoid duplicates across restarts (ITS#9537)
+ Fixed slapd typo in daemon.c (ITS#9541)
+ Fixed slapd slapi compilation (ITS#9544)
+ Fixed slapd to handle empty DN in extended filters (ITS#9551)
+ Fixed slapd syncrepl searches with empty base (ITS#6467)
+ Fixed slapd syncrepl refresh on startup (ITS#9324, ITS#9534)
+ Fixed slapd abort due to typo (ITS#9561)
+ Fixed slapd-asyncmeta quarantine handling (ITS#8721)
+ Fixed slapd-asyncmeta to have a default operations timeout (ITS#9555)
+ Fixed slapd-ldap quarantine handling (ITS#8721)
+ Fixed slapd-mdb deletion of context entry (ITS#9531)
+ Fixed slapd-mdb off-by-one affecting search scope (ITS#9557)
+ Fixed slapd-meta quarantine handling (ITS#8721)
+ Fixed slapo-accesslog to record reqNewDN for modRDN ops (ITS#9552)
+ Fixed slapo-pcache locking during expiration (ITS#9529)
+ Build
+ Fixed slappw-argon2 module installation (ITS#9548)
+ Contrib
+ Update ldapc++/ldaptcl to use configure.ac (ITS#9554)
+ Documentation
+ ldap_first_attribute(3) - Document ldap_get_attribute_ber (ITS#8820)
+ ldap_modify(3) - Delete non-existent mod_next parameter (ITS#9559)
+
+OpenLDAP 2.5.4 Release (2021/04/29)
+ Initial release for "general use".
diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644
index 0000000..eecd9c6
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,64 @@
+Copyright 1998-2022 The OpenLDAP Foundation
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
+OpenLDAP is a registered trademark of the OpenLDAP Foundation.
+
+Individual files and/or contributed packages may be copyright by
+other parties and/or subject to additional restrictions.
+
+This work is derived from the University of Michigan LDAP v3.3
+distribution. Information concerning this software is available
+at <http://www.umich.edu/~dirsvcs/ldap/ldap.html>.
+
+This work also contains materials derived from public sources.
+
+Additional information about OpenLDAP can be obtained at
+<http://www.openldap.org/>.
+
+---
+
+Portions Copyright 1998-2013 Kurt D. Zeilenga.
+Portions Copyright 1998-2006 Net Boolean Incorporated.
+Portions Copyright 2001-2006 IBM Corporation.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+---
+
+Portions Copyright 1999-2008 Howard Y.H. Chu.
+Portions Copyright 1999-2008 Symas Corporation.
+Portions Copyright 1998-2003 Hallvard B. Furuseth.
+Portions Copyright 2007-2011 Gavin Henry.
+Portions Copyright 2007-2011 Suretec Systems Ltd.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that this notice is preserved.
+The names of the copyright holders may not be used to endorse or
+promote products derived from this software without their specific
+prior written permission. This software is provided ``as is''
+without express or implied warranty.
+
+---
+
+Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
+All rights reserved.
+
+Redistribution and use in source and binary forms are permitted
+provided that this notice is preserved and that due credit is given
+to the University of Michigan at Ann Arbor. The name of the
+University may not be used to endorse or promote products derived
+from this software without specific prior written permission. This
+software is provided ``as is'' without express or implied warranty.
+
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..9db9200
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,109 @@
+Making and Installing the OpenLDAP Distribution
+===============================================
+
+This file provides brief instructions on how to build and install
+OpenLDAP on UNIX (and UNIX-like) systems. More detailed information
+and instructions can be found in The OpenLDAP Administrator's Guide
+(available from http://www.openldap.org/doc/).
+
+It is recommended that you read, or at least skim through, ALL of the
+instructions in this file before attempting to build the software.
+
+Making and Installing the OpenLDAP Distribution
+-----------------------------------------------
+
+1. Unpack the distribution and change directory:
+
+ % tar xfz openldap-VERSION.tgz
+ % cd openldap-VERSION
+
+ (replacing VERSION with the appropriate version string). If you
+ are reading this file, you probably have already done this!
+
+2. Type:
+
+ % ./configure --help
+
+ to list available configuration options.
+
+ The configure script also looks for compiler/linker options on
+ the command line and in the environment. These include:
+
+ Variable Description Example
+ CC C compiler gcc
+ CFLAGS C flags -O -g
+ CPPFLAGS cpp flags -I/path/include -D__FOO__=42
+ LDFLAGS ld flags -L/usr/local/lib
+ LIBS libraries -llib
+ PATH command path /usr/local/bin:/usr/bin:/bin
+
+ See doc/install/configure for generic configure documentation.
+
+3. Configure the build system:
+
+ % ./configure [options] [var=value ...]
+
+ If all goes well, the configure script will automatically detect
+ the appropriate settings. If the configure script fails, you
+ should read the config.log file that it generated to see what it
+ was trying to do and exactly what failed. You may need to
+ specify additional options and/or variables besides those listed
+ above to obtain desired results, depending on your operating
+ system.
+
+4. Build dependencies:
+
+ % make depend
+
+5. Build the system:
+
+ % make
+
+ If all goes well, the system will build as configured. If not,
+ return to step 3 after reviewing the configuration settings.
+
+6. Test the standalone system:
+
+ This step requires the standalone LDAP server, slapd(8), with MDB
+ support.
+
+ % make test
+
+ If all goes well, the system has been built as configured. If
+ not, return to step 2 after reviewing your configuration
+ settings.
+
+7. Install the software. You may need to become the super-user
+ (e.g. root) to do this (depending on where you are installing
+ things):
+
+ % su root -c 'make install'
+
+8. That's it. Enjoy!
+
+See the OpenLDAP Administrator's Guide and the manual pages for the
+individual applications for configuration and use information. You may
+also want to edit the configuration files used by the various
+components. These configuration files are located in the OpenLDAP
+configuration directory (normally /usr/local/etc/openldap).
+
+ ldap.conf client defaults
+ slapd.conf Standalone LDAP daemon
+ lload.conf LDAP Load Balancer daemon
+ schema/*.schema Schema Definitions
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 1998-2022 The OpenLDAP Foundation.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
+OpenLDAP is a registered trademark of the OpenLDAP Foundation.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..05ad757
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,47 @@
+The OpenLDAP Public License
+ Version 2.8, 17 August 2003
+
+Redistribution and use of this software and associated documentation
+("Software"), with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Redistributions in source form must retain copyright statements
+ and notices,
+
+2. Redistributions in binary form must reproduce applicable copyright
+ statements and notices, this list of conditions, and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution, and
+
+3. Redistributions must contain a verbatim copy of this document.
+
+The OpenLDAP Foundation may revise this license from time to time.
+Each revision is distinguished by a version number. You may use
+this Software under terms of this license revision or under the
+terms of any subsequent revision of the license.
+
+THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS
+CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S)
+OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+The names of the authors and copyright holders must not be used in
+advertising or otherwise to promote the sale, use or other dealing
+in this Software without specific, written prior permission. Title
+to copyright in this Software shall at all times remain with copyright
+holders.
+
+OpenLDAP is a registered trademark of the OpenLDAP Foundation.
+
+Copyright 1999-2003 The OpenLDAP Foundation, Redwood City,
+California, USA. All Rights Reserved. Permission to copy and
+distribute verbatim copies of this document is granted.
diff --git a/Makefile.in b/Makefile.in
new file mode 100644
index 0000000..5c6bec0
--- /dev/null
+++ b/Makefile.in
@@ -0,0 +1,38 @@
+# Master Makefile for OpenLDAP
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SUBDIRS= include libraries clients servers tests doc
+CLEANDIRS=
+INSTALLDIRS=
+
+makefiles: FORCE
+ ./config.status
+
+# force a make all before make install
+# only done at the top-level
+install-common: all FORCE
+
+clean-local: FORCE
+ $(RM) config.cache config.log configure.lineno
+ $(RM) -r autom4te.cache
+
+veryclean-local: FORCE
+ $(RM) config.status libtool stamp-h stamp-h.in
+
+distclean: veryclean FORCE
+
+check: test
+test: FORCE
+ cd tests && $(MAKE) test
diff --git a/README b/README
new file mode 100644
index 0000000..5bd6b26
--- /dev/null
+++ b/README
@@ -0,0 +1,95 @@
+OpenLDAP 2.5 README
+ For a description of what this distribution contains, see the
+ ANNOUNCEMENT file in this directory. For a description of
+ changes from previous releases, see the CHANGES file in this
+ directory.
+
+ This is 2.5 release, it includes significant changes from prior
+ releases.
+
+REQUIRED SOFTWARE
+ Building OpenLDAP Software requires a number of software packages
+ to be preinstalled. Additional information regarding prerequisite
+ software can be found in the OpenLDAP Administrator's Guide.
+
+ Base system (libraries and tools):
+ Standard C compiler (required)
+ Cyrus SASL 2.1.27+ (recommended)
+ OpenSSL 1.1.1+ (recommended)
+ libevent 2.1.8+ (recommended)
+ libargon2 or libsodium (recommended)
+ Reentrant POSIX REGEX software (required)
+
+ SLAPD:
+ The ARGON2 password hashing module requires either libargon2
+ or libsodium
+ LLOADD:
+ The LLOADD daemon or integrated slapd module requires
+ libevent 2.1.8 or later.
+
+ CLIENTS/CONTRIB ware:
+ Depends on package. See per package README.
+
+
+MAKING AND INSTALLING THE DISTRIBUTION
+ Please see the INSTALL file for basic instructions. More
+ detailed instructions can be found in the OpenLDAP Administrator's
+ Guide (see DOCUMENTATION section).
+
+
+DOCUMENTATION
+ The OpenLDAP Administrator's Guide is available in the
+ guide.html file in the doc/guide/admin directory. The
+ guide and a number of other documents are available at
+ <http://www.openldap.org/doc/admin/guide.html>.
+
+ The distribution also includes manual pages for most programs
+ and library APIs. See ldap(3) for details.
+
+ The OpenLDAP website is available and contains the latest LDAP
+ news, releases announcements, pointers to other LDAP resources,
+ etc.. It is located at <http://www.OpenLDAP.org/>.
+
+ The OpenLDAP Software FAQ is available at
+ <http://www.openldap.org/faq/>.
+
+
+SUPPORT / FEEDBACK / PROBLEM REPORTS / DISCUSSIONS
+ OpenLDAP Software is user supported. If you have problems, please
+ review the OpenLDAP FAQ <http://www.openldap.org/faq/> and
+ archives of the OpenLDAP-software and OpenLDAP-bugs mailing lists
+ <http://www.openldap.org/lists/>. If you cannot find the answer,
+ please enquire on the OpenLDAP-software list.
+
+ Issues, such as bug reports, should be reported using our
+ Issue Tracking System <http://www.OpenLDAP.org/its/>. Do not
+ use this system for software enquiries. Please direct these
+ to an appropriate mailing list.
+
+
+CONTRIBUTING
+ See <http://www.openldap.org/devel/contributing.html> for
+ information regarding how to contribute code or documentation
+ to the OpenLDAP Project for inclusion in OpenLDAP Software.
+ While you are encouraged to coordinate and discuss the development
+ activities on the <openldap-devel@openldap.org> mailing list
+ prior to submission, it is noted that contributions must be
+ submitted using the Issue Tracking System
+ <http://www.openldap.org/its/> to be considered.
+
+---
+
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 1998-2022 The OpenLDAP Foundation.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
+OpenLDAP is a registered trademark of the OpenLDAP Foundation.
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644
index 0000000..daf32af
--- /dev/null
+++ b/aclocal.m4
@@ -0,0 +1,296 @@
+# generated automatically by aclocal 1.16.1 -*- Autoconf -*-
+
+# Copyright (C) 1996-2018 Free Software Foundation, Inc.
+
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
+dnl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
+dnl serial 11 (pkg-config-0.29.1)
+dnl
+dnl Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
+dnl Copyright © 2012-2015 Dan Nicholson <dbn.lists@gmail.com>
+dnl
+dnl This program is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; either version 2 of the License, or
+dnl (at your option) any later version.
+dnl
+dnl This program is distributed in the hope that it will be useful, but
+dnl WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+dnl General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with this program; if not, write to the Free Software
+dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+dnl 02111-1307, USA.
+dnl
+dnl As a special exception to the GNU General Public License, if you
+dnl distribute this file as part of a program that contains a
+dnl configuration script generated by Autoconf, you may include it under
+dnl the same distribution terms that you use for the rest of that
+dnl program.
+
+dnl PKG_PREREQ(MIN-VERSION)
+dnl -----------------------
+dnl Since: 0.29
+dnl
+dnl Verify that the version of the pkg-config macros are at least
+dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's
+dnl installed version of pkg-config, this checks the developer's version
+dnl of pkg.m4 when generating configure.
+dnl
+dnl To ensure that this macro is defined, also add:
+dnl m4_ifndef([PKG_PREREQ],
+dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])])
+dnl
+dnl See the "Since" comment for each macro you use to see what version
+dnl of the macros you require.
+m4_defun([PKG_PREREQ],
+[m4_define([PKG_MACROS_VERSION], [0.29.1])
+m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
+ [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
+])dnl PKG_PREREQ
+
+dnl PKG_PROG_PKG_CONFIG([MIN-VERSION])
+dnl ----------------------------------
+dnl Since: 0.16
+dnl
+dnl Search for the pkg-config tool and set the PKG_CONFIG variable to
+dnl first found in the path. Checks that the version of pkg-config found
+dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is
+dnl used since that's the first version where most current features of
+dnl pkg-config existed.
+AC_DEFUN([PKG_PROG_PKG_CONFIG],
+[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
+m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
+m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
+AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
+AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
+AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+ AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
+fi
+if test -n "$PKG_CONFIG"; then
+ _pkg_min_version=m4_default([$1], [0.9.0])
+ AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
+ if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ PKG_CONFIG=""
+ fi
+fi[]dnl
+])dnl PKG_PROG_PKG_CONFIG
+
+dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl -------------------------------------------------------------------
+dnl Since: 0.18
+dnl
+dnl Check to see whether a particular set of modules exists. Similar to
+dnl PKG_CHECK_MODULES(), but does not set variables or print errors.
+dnl
+dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+dnl only at the first occurence in configure.ac, so if the first place
+dnl it's called might be skipped (such as if it is within an "if", you
+dnl have to call PKG_CHECK_EXISTS manually
+AC_DEFUN([PKG_CHECK_EXISTS],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+if test -n "$PKG_CONFIG" && \
+ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
+ m4_default([$2], [:])
+m4_ifvaln([$3], [else
+ $3])dnl
+fi])
+
+dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
+dnl ---------------------------------------------
+dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting
+dnl pkg_failed based on the result.
+m4_define([_PKG_CONFIG],
+[if test -n "$$1"; then
+ pkg_cv_[]$1="$$1"
+ elif test -n "$PKG_CONFIG"; then
+ PKG_CHECK_EXISTS([$3],
+ [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes ],
+ [pkg_failed=yes])
+ else
+ pkg_failed=untried
+fi[]dnl
+])dnl _PKG_CONFIG
+
+dnl _PKG_SHORT_ERRORS_SUPPORTED
+dnl ---------------------------
+dnl Internal check to see if pkg-config supports short errors.
+AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi[]dnl
+])dnl _PKG_SHORT_ERRORS_SUPPORTED
+
+
+dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+dnl [ACTION-IF-NOT-FOUND])
+dnl --------------------------------------------------------------
+dnl Since: 0.4.0
+dnl
+dnl Note that if there is a possibility the first call to
+dnl PKG_CHECK_MODULES might not happen, you should be sure to include an
+dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
+AC_DEFUN([PKG_CHECK_MODULES],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
+AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
+
+pkg_failed=no
+AC_MSG_CHECKING([for $1])
+
+_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
+_PKG_CONFIG([$1][_LIBS], [libs], [$2])
+
+m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
+and $1[]_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.])
+
+if test $pkg_failed = yes; then
+ AC_MSG_RESULT([no])
+ _PKG_SHORT_ERRORS_SUPPORTED
+ if test $_pkg_short_errors_supported = yes; then
+ $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
+ else
+ $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
+
+ m4_default([$4], [AC_MSG_ERROR(
+[Package requirements ($2) were not met:
+
+$$1_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+_PKG_TEXT])[]dnl
+ ])
+elif test $pkg_failed = untried; then
+ AC_MSG_RESULT([no])
+ m4_default([$4], [AC_MSG_FAILURE(
+[The pkg-config script could not be found or is too old. Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+_PKG_TEXT
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.])[]dnl
+ ])
+else
+ $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
+ $1[]_LIBS=$pkg_cv_[]$1[]_LIBS
+ AC_MSG_RESULT([yes])
+ $3
+fi[]dnl
+])dnl PKG_CHECK_MODULES
+
+
+dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+dnl [ACTION-IF-NOT-FOUND])
+dnl ---------------------------------------------------------------------
+dnl Since: 0.29
+dnl
+dnl Checks for existence of MODULES and gathers its build flags with
+dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags
+dnl and VARIABLE-PREFIX_LIBS from --libs.
+dnl
+dnl Note that if there is a possibility the first call to
+dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to
+dnl include an explicit call to PKG_PROG_PKG_CONFIG in your
+dnl configure.ac.
+AC_DEFUN([PKG_CHECK_MODULES_STATIC],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+_save_PKG_CONFIG=$PKG_CONFIG
+PKG_CONFIG="$PKG_CONFIG --static"
+PKG_CHECK_MODULES($@)
+PKG_CONFIG=$_save_PKG_CONFIG[]dnl
+])dnl PKG_CHECK_MODULES_STATIC
+
+
+dnl PKG_INSTALLDIR([DIRECTORY])
+dnl -------------------------
+dnl Since: 0.27
+dnl
+dnl Substitutes the variable pkgconfigdir as the location where a module
+dnl should install pkg-config .pc files. By default the directory is
+dnl $libdir/pkgconfig, but the default can be changed by passing
+dnl DIRECTORY. The user can override through the --with-pkgconfigdir
+dnl parameter.
+AC_DEFUN([PKG_INSTALLDIR],
+[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
+m4_pushdef([pkg_description],
+ [pkg-config installation directory @<:@]pkg_default[@:>@])
+AC_ARG_WITH([pkgconfigdir],
+ [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],,
+ [with_pkgconfigdir=]pkg_default)
+AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
+m4_popdef([pkg_default])
+m4_popdef([pkg_description])
+])dnl PKG_INSTALLDIR
+
+
+dnl PKG_NOARCH_INSTALLDIR([DIRECTORY])
+dnl --------------------------------
+dnl Since: 0.27
+dnl
+dnl Substitutes the variable noarch_pkgconfigdir as the location where a
+dnl module should install arch-independent pkg-config .pc files. By
+dnl default the directory is $datadir/pkgconfig, but the default can be
+dnl changed by passing DIRECTORY. The user can override through the
+dnl --with-noarch-pkgconfigdir parameter.
+AC_DEFUN([PKG_NOARCH_INSTALLDIR],
+[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
+m4_pushdef([pkg_description],
+ [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@])
+AC_ARG_WITH([noarch-pkgconfigdir],
+ [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],,
+ [with_noarch_pkgconfigdir=]pkg_default)
+AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
+m4_popdef([pkg_default])
+m4_popdef([pkg_description])
+])dnl PKG_NOARCH_INSTALLDIR
+
+
+dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
+dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+dnl -------------------------------------------
+dnl Since: 0.28
+dnl
+dnl Retrieves the value of the pkg-config variable for the given module.
+AC_DEFUN([PKG_CHECK_VAR],
+[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
+AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
+
+_PKG_CONFIG([$1], [variable="][$3]["], [$2])
+AS_VAR_COPY([$1], [pkg_cv_][$1])
+
+AS_VAR_IF([$1], [""], [$5], [$4])dnl
+])dnl PKG_CHECK_VAR
+
+m4_include([build/libtool.m4])
+m4_include([build/ltoptions.m4])
+m4_include([build/ltsugar.m4])
+m4_include([build/ltversion.m4])
+m4_include([build/lt~obsolete.m4])
+m4_include([build/openldap.m4])
diff --git a/build/LICENSE-2.0.1 b/build/LICENSE-2.0.1
new file mode 100644
index 0000000..db93ec5
--- /dev/null
+++ b/build/LICENSE-2.0.1
@@ -0,0 +1,56 @@
+A number of files contained in OpenLDAP Software contain
+a statement:
+ USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT
+ TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF
+ WHICH IS AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR
+ IN THE FILE "LICENSE" IN THE TOP-LEVEL DIRECTORY OF THE
+ DISTRIBUTION.
+
+The following is a verbatim copy of version 2.0.1 of the OpenLDAP
+Public License referenced in the above statement.
+
+
+The OpenLDAP Public License
+
+ Version 2.0.1, 21 December 1999
+ Copyright 1999, The OpenLDAP Foundation, Redwood City, California, USA.
+ All Rights Reserved.
+
+Redistribution and use of this software and associated documentation
+("Software"), with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Redistributions of source code must retain copyright
+statements and notices. Redistributions must also contain a
+copy of this document.
+
+2. Redistributions in binary form must reproduce the
+above copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+3. The name "OpenLDAP" must not be used to endorse or promote
+products derived from this Software without prior written
+permission of the OpenLDAP Foundation. For written permission,
+please contact foundation@openldap.org.
+
+4. Products derived from this Software may not be called "OpenLDAP"
+nor may "OpenLDAP" appear in their names without prior written
+permission of the OpenLDAP Foundation. OpenLDAP is a trademark
+of the OpenLDAP Foundation.
+
+5. Due credit should be given to the OpenLDAP Project
+(http://www.openldap.org/).
+
+THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+THE OPENLDAP FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/build/README b/build/README
new file mode 100644
index 0000000..ac0ac58
--- /dev/null
+++ b/build/README
@@ -0,0 +1,13 @@
+The OpenLDAP build environment relies on non-standard versions of
+configuration tools:
+ Autoconf 2.13.1
+ Automake 1.4a
+ Libtool 1.4.3
+
+The autoconf/automake releases used are available at:
+ ftp://ftp.openldap.org/pub/tools/
+
+The libtool release used is available from:
+ ftp://ftp.gnu.org/
+
+but with ltmain.sh replaced with versions found in this directory.
diff --git a/build/config.guess b/build/config.guess
new file mode 100755
index 0000000..45001cf
--- /dev/null
+++ b/build/config.guess
@@ -0,0 +1,1667 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright 1992-2020 Free Software Foundation, Inc.
+
+timestamp='2020-01-01'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+#
+# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
+#
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
+#
+# Please send patches to <config-patches@gnu.org>.
+
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Options:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright 1992-2020 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+tmp=
+# shellcheck disable=SC2172
+trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15
+
+set_cc_for_build() {
+ # prevent multiple calls if $tmp is already set
+ test "$tmp" && return 0
+ : "${TMPDIR=/tmp}"
+ # shellcheck disable=SC2039
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; }
+ dummy=$tmp/dummy
+ case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in
+ ,,) echo "int x;" > "$dummy.c"
+ for driver in cc gcc c89 c99 ; do
+ if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$driver"
+ break
+ fi
+ done
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+ esac
+}
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if test -f /.attbin/uname ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+case "$UNAME_SYSTEM" in
+Linux|GNU|GNU/*)
+ # If the system lacks a compiler, then just pick glibc.
+ # We could probably try harder.
+ LIBC=gnu
+
+ set_cc_for_build
+ cat <<-EOF > "$dummy.c"
+ #include <features.h>
+ #if defined(__UCLIBC__)
+ LIBC=uclibc
+ #elif defined(__dietlibc__)
+ LIBC=dietlibc
+ #else
+ LIBC=gnu
+ #endif
+ EOF
+ eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`"
+
+ # If ldd exists, use it to detect musl libc.
+ if command -v ldd >/dev/null && \
+ ldd --version 2>&1 | grep -q ^musl
+ then
+ LIBC=musl
+ fi
+ ;;
+esac
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
+ "/sbin/$sysctl" 2>/dev/null || \
+ "/usr/sbin/$sysctl" 2>/dev/null || \
+ echo unknown)`
+ case "$UNAME_MACHINE_ARCH" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ sh5el) machine=sh5le-unknown ;;
+ earmv*)
+ arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
+ endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'`
+ machine="${arch}${endian}"-unknown
+ ;;
+ *) machine="$UNAME_MACHINE_ARCH"-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently (or will in the future) and ABI.
+ case "$UNAME_MACHINE_ARCH" in
+ earm*)
+ os=netbsdelf
+ ;;
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ELF__
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # Determine ABI tags.
+ case "$UNAME_MACHINE_ARCH" in
+ earm*)
+ expr='s/^earmv[0-9]/-eabi/;s/eb$//'
+ abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"`
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "$UNAME_VERSION" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "$machine-${os}${release}${abi-}"
+ exit ;;
+ *:Bitrig:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+ echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE"
+ exit ;;
+ *:OpenBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+ echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE"
+ exit ;;
+ *:LibertyBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
+ echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE"
+ exit ;;
+ *:MidnightBSD:*:*)
+ echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE"
+ exit ;;
+ *:ekkoBSD:*:*)
+ echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE"
+ exit ;;
+ *:SolidBSD:*:*)
+ echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE"
+ exit ;;
+ *:OS108:*:*)
+ echo "$UNAME_MACHINE"-unknown-os108_"$UNAME_RELEASE"
+ exit ;;
+ macppc:MirBSD:*:*)
+ echo powerpc-unknown-mirbsd"$UNAME_RELEASE"
+ exit ;;
+ *:MirBSD:*:*)
+ echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE"
+ exit ;;
+ *:Sortix:*:*)
+ echo "$UNAME_MACHINE"-unknown-sortix
+ exit ;;
+ *:Twizzler:*:*)
+ echo "$UNAME_MACHINE"-unknown-twizzler
+ exit ;;
+ *:Redox:*:*)
+ echo "$UNAME_MACHINE"-unknown-redox
+ exit ;;
+ mips:OSF1:*.*)
+ echo mips-dec-osf1
+ exit ;;
+ alpha:OSF1:*:*)
+ case $UNAME_RELEASE in
+ *4.0)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ ;;
+ *5.*)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ ;;
+ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE=alpha ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE=alpha ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE=alpha ;;
+ "EV5 (21164)")
+ UNAME_MACHINE=alphaev5 ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE=alphaev56 ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE=alphapca56 ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE=alphapca57 ;;
+ "EV6 (21264)")
+ UNAME_MACHINE=alphaev6 ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE=alphaev67 ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE=alphaev68 ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE=alphaev68 ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE=alphaev68 ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE=alphaev69 ;;
+ "EV7 (21364)")
+ UNAME_MACHINE=alphaev7 ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE=alphaev79 ;;
+ esac
+ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`"
+ # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+ exitcode=$?
+ trap '' 0
+ exit $exitcode ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo "$UNAME_MACHINE"-unknown-amigaos
+ exit ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo "$UNAME_MACHINE"-unknown-morphos
+ exit ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit ;;
+ *:z/VM:*:*)
+ echo s390-ibm-zvmoe
+ exit ;;
+ *:OS400:*:*)
+ echo powerpc-ibm-os400
+ exit ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix"$UNAME_RELEASE"
+ exit ;;
+ arm*:riscos:*:*|arm*:RISCOS:*:*)
+ echo arm-unknown-riscos
+ exit ;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit ;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit ;;
+ DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7; exit ;;
+ esac ;;
+ s390x:SunOS:*:*)
+ echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
+ exit ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+ exit ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
+ exit ;;
+ i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+ echo i386-pc-auroraux"$UNAME_RELEASE"
+ exit ;;
+ i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+ set_cc_for_build
+ SUN_ARCH=i386
+ # If there is a compiler, see if it is configured for 64-bit objects.
+ # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+ # This test works for both compilers.
+ if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
+ if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ SUN_ARCH=x86_64
+ fi
+ fi
+ echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+ exit ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+ exit ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`"
+ exit ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos"$UNAME_RELEASE"
+ exit ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos"$UNAME_RELEASE"
+ ;;
+ sun4)
+ echo sparc-sun-sunos"$UNAME_RELEASE"
+ ;;
+ esac
+ exit ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos"$UNAME_RELEASE"
+ exit ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint"$UNAME_RELEASE"
+ exit ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint"$UNAME_RELEASE"
+ exit ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint"$UNAME_RELEASE"
+ exit ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint"$UNAME_RELEASE"
+ exit ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint"$UNAME_RELEASE"
+ exit ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint"$UNAME_RELEASE"
+ exit ;;
+ m68k:machten:*:*)
+ echo m68k-apple-machten"$UNAME_RELEASE"
+ exit ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten"$UNAME_RELEASE"
+ exit ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix"$UNAME_RELEASE"
+ exit ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix"$UNAME_RELEASE"
+ exit ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix"$UNAME_RELEASE"
+ exit ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
+ dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+ SYSTEM_NAME=`"$dummy" "$dummyarg"` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo mips-mips-riscos"$UNAME_RELEASE"
+ exit ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ]
+ then
+ if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \
+ [ "$TARGET_BINARY_INTERFACE"x = x ]
+ then
+ echo m88k-dg-dgux"$UNAME_RELEASE"
+ else
+ echo m88k-dg-dguxbcs"$UNAME_RELEASE"
+ fi
+ else
+ echo i586-dg-dgux"$UNAME_RELEASE"
+ fi
+ exit ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`"
+ exit ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
+ fi
+ echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV"
+ exit ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"`
+ then
+ echo "$SYSTEM_NAME"
+ else
+ echo rs6000-ibm-aix3.2.5
+ fi
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit ;;
+ *:AIX:*:[4567])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/lslpp ] ; then
+ IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |
+ awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
+ else
+ IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
+ fi
+ echo "$IBM_ARCH"-ibm-aix"$IBM_REV"
+ exit ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit ;;
+ ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*)
+ echo romp-ibm-bsd4.4
+ exit ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to
+ exit ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
+ case "$UNAME_MACHINE" in
+ 9000/31?) HP_ARCH=m68000 ;;
+ 9000/[34]??) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "$sc_cpu_version" in
+ 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "$sc_kernel_bits" in
+ 32) HP_ARCH=hppa2.0n ;;
+ 64) HP_ARCH=hppa2.0w ;;
+ '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "$HP_ARCH" = "" ]; then
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ "$HP_ARCH" = hppa2.0w ]
+ then
+ set_cc_for_build
+
+ # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+ # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
+ # generating 64-bit code. GNU and HP use different nomenclature:
+ #
+ # $ CC_FOR_BUILD=cc ./config.guess
+ # => hppa2.0w-hp-hpux11.23
+ # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+ # => hppa64-hp-hpux11.23
+
+ if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) |
+ grep -q __LP64__
+ then
+ HP_ARCH=hppa2.0w
+ else
+ HP_ARCH=hppa64
+ fi
+ fi
+ echo "$HP_ARCH"-hp-hpux"$HPUX_REV"
+ exit ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux"$HPUX_REV"
+ exit ;;
+ 3050*:HI-UX:*:*)
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo unknown-hitachi-hiuxwe2
+ exit ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*)
+ echo hppa1.1-hp-bsd
+ exit ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*)
+ echo hppa1.1-hp-osf
+ exit ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo "$UNAME_MACHINE"-unknown-osf1mk
+ else
+ echo "$UNAME_MACHINE"-unknown-osf1
+ fi
+ exit ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ *:UNICOS/mp:*:*)
+ echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+ FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+ FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+ FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE"
+ exit ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi"$UNAME_RELEASE"
+ exit ;;
+ *:BSD/OS:*:*)
+ echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE"
+ exit ;;
+ arm:FreeBSD:*:*)
+ UNAME_PROCESSOR=`uname -p`
+ set_cc_for_build
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabi
+ else
+ echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabihf
+ fi
+ exit ;;
+ *:FreeBSD:*:*)
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ case "$UNAME_PROCESSOR" in
+ amd64)
+ UNAME_PROCESSOR=x86_64 ;;
+ i386)
+ UNAME_PROCESSOR=i586 ;;
+ esac
+ echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`"
+ exit ;;
+ i*:CYGWIN*:*)
+ echo "$UNAME_MACHINE"-pc-cygwin
+ exit ;;
+ *:MINGW64*:*)
+ echo "$UNAME_MACHINE"-pc-mingw64
+ exit ;;
+ *:MINGW*:*)
+ echo "$UNAME_MACHINE"-pc-mingw32
+ exit ;;
+ *:MSYS*:*)
+ echo "$UNAME_MACHINE"-pc-msys
+ exit ;;
+ i*:PW*:*)
+ echo "$UNAME_MACHINE"-pc-pw32
+ exit ;;
+ *:Interix*:*)
+ case "$UNAME_MACHINE" in
+ x86)
+ echo i586-pc-interix"$UNAME_RELEASE"
+ exit ;;
+ authenticamd | genuineintel | EM64T)
+ echo x86_64-unknown-interix"$UNAME_RELEASE"
+ exit ;;
+ IA64)
+ echo ia64-unknown-interix"$UNAME_RELEASE"
+ exit ;;
+ esac ;;
+ i*:UWIN*:*)
+ echo "$UNAME_MACHINE"-pc-uwin
+ exit ;;
+ amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+ echo x86_64-pc-cygwin
+ exit ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+ exit ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`"
+ exit ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC"
+ exit ;;
+ *:Minix:*:*)
+ echo "$UNAME_MACHINE"-unknown-minix
+ exit ;;
+ aarch64:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ aarch64_be:Linux:*:*)
+ UNAME_MACHINE=aarch64_be
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep -q ld.so.1
+ if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ arc:Linux:*:* | arceb:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ arm*:Linux:*:*)
+ set_cc_for_build
+ if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_EABI__
+ then
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ else
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi
+ else
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf
+ fi
+ fi
+ exit ;;
+ avr32*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ cris:Linux:*:*)
+ echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
+ exit ;;
+ crisv32:Linux:*:*)
+ echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
+ exit ;;
+ e2k:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ frv:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ hexagon:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ i*86:Linux:*:*)
+ echo "$UNAME_MACHINE"-pc-linux-"$LIBC"
+ exit ;;
+ ia64:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ k1om:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ m32r*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ m68*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ mips:Linux:*:* | mips64:Linux:*:*)
+ set_cc_for_build
+ IS_GLIBC=0
+ test x"${LIBC}" = xgnu && IS_GLIBC=1
+ sed 's/^ //' << EOF > "$dummy.c"
+ #undef CPU
+ #undef mips
+ #undef mipsel
+ #undef mips64
+ #undef mips64el
+ #if ${IS_GLIBC} && defined(_ABI64)
+ LIBCABI=gnuabi64
+ #else
+ #if ${IS_GLIBC} && defined(_ABIN32)
+ LIBCABI=gnuabin32
+ #else
+ LIBCABI=${LIBC}
+ #endif
+ #endif
+
+ #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+ CPU=mipsisa64r6
+ #else
+ #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+ CPU=mipsisa32r6
+ #else
+ #if defined(__mips64)
+ CPU=mips64
+ #else
+ CPU=mips
+ #endif
+ #endif
+ #endif
+
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ MIPS_ENDIAN=el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ MIPS_ENDIAN=
+ #else
+ MIPS_ENDIAN=
+ #endif
+ #endif
+EOF
+ eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'`"
+ test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; }
+ ;;
+ mips64el:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ openrisc*:Linux:*:*)
+ echo or1k-unknown-linux-"$LIBC"
+ exit ;;
+ or32:Linux:*:* | or1k*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ padre:Linux:*:*)
+ echo sparc-unknown-linux-"$LIBC"
+ exit ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-"$LIBC"
+ exit ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;;
+ PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;;
+ *) echo hppa-unknown-linux-"$LIBC" ;;
+ esac
+ exit ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-"$LIBC"
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-"$LIBC"
+ exit ;;
+ ppc64le:Linux:*:*)
+ echo powerpc64le-unknown-linux-"$LIBC"
+ exit ;;
+ ppcle:Linux:*:*)
+ echo powerpcle-unknown-linux-"$LIBC"
+ exit ;;
+ riscv32:Linux:*:* | riscv64:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo "$UNAME_MACHINE"-ibm-linux-"$LIBC"
+ exit ;;
+ sh64*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ sh*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ tile*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ vax:Linux:*:*)
+ echo "$UNAME_MACHINE"-dec-linux-"$LIBC"
+ exit ;;
+ x86_64:Linux:*:*)
+ echo "$UNAME_MACHINE"-pc-linux-"$LIBC"
+ exit ;;
+ xtensa*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION"
+ exit ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo "$UNAME_MACHINE"-pc-os2-emx
+ exit ;;
+ i*86:XTS-300:*:STOP)
+ echo "$UNAME_MACHINE"-unknown-stop
+ exit ;;
+ i*86:atheos:*:*)
+ echo "$UNAME_MACHINE"-unknown-atheos
+ exit ;;
+ i*86:syllable:*:*)
+ echo "$UNAME_MACHINE"-pc-syllable
+ exit ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+ echo i386-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ i*86:*DOS:*:*)
+ echo "$UNAME_MACHINE"-pc-msdosdjgpp
+ exit ;;
+ i*86:*:4.*:*)
+ UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL"
+ else
+ echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL"
+ fi
+ exit ;;
+ i*86:*:5:[678]*)
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}"
+ exit ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo "$UNAME_MACHINE"-pc-isc"$UNAME_REL"
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL"
+ else
+ echo "$UNAME_MACHINE"-pc-sysv32
+ fi
+ exit ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i586.
+ # Note: whatever this is, it MUST be the same as what config.sub
+ # prints for the "djgpp" host, or else GDB configure will decide that
+ # this is a cross-build.
+ echo i586-pc-msdosdjgpp
+ exit ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4
+ fi
+ exit ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit ;;
+ M68*:*:R3V[5678]*:*)
+ test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+ OS_REL='.3'
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+ echo powerpc-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv"$UNAME_RELEASE"
+ exit ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo "$UNAME_MACHINE"-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit ;;
+ i*86:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo "$UNAME_MACHINE"-stratus-vos
+ exit ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux"$UNAME_RELEASE"
+ exit ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv"$UNAME_RELEASE"
+ else
+ echo mips-unknown-sysv"$UNAME_RELEASE"
+ fi
+ exit ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit ;;
+ BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
+ echo i586-pc-haiku
+ exit ;;
+ x86_64:Haiku:*:*)
+ echo x86_64-unknown-haiku
+ exit ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-7:SUPER-UX:*:*)
+ echo sx7-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-8:SUPER-UX:*:*)
+ echo sx8-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-8R:SUPER-UX:*:*)
+ echo sx8r-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-ACE:SUPER-UX:*:*)
+ echo sxace-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody"$UNAME_RELEASE"
+ exit ;;
+ *:Rhapsody:*:*)
+ echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE"
+ exit ;;
+ *:Darwin:*:*)
+ UNAME_PROCESSOR=`uname -p`
+ case $UNAME_PROCESSOR in
+ unknown) UNAME_PROCESSOR=powerpc ;;
+ esac
+ if command -v xcode-select > /dev/null 2> /dev/null && \
+ ! xcode-select --print-path > /dev/null 2> /dev/null ; then
+ # Avoid executing cc if there is no toolchain installed as
+ # cc will be a stub that puts up a graphical alert
+ # prompting the user to install developer tools.
+ CC_FOR_BUILD=no_compiler_found
+ else
+ set_cc_for_build
+ fi
+ if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
+ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ case $UNAME_PROCESSOR in
+ i386) UNAME_PROCESSOR=x86_64 ;;
+ powerpc) UNAME_PROCESSOR=powerpc64 ;;
+ esac
+ fi
+ # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc
+ if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_PPC >/dev/null
+ then
+ UNAME_PROCESSOR=powerpc
+ fi
+ elif test "$UNAME_PROCESSOR" = i386 ; then
+ # uname -m returns i386 or x86_64
+ UNAME_PROCESSOR=$UNAME_MACHINE
+ fi
+ echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE"
+ exit ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = x86; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE"
+ exit ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit ;;
+ NEO-*:NONSTOP_KERNEL:*:*)
+ echo neo-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSE-*:NONSTOP_KERNEL:*:*)
+ echo nse-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSR-*:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSV-*:NONSTOP_KERNEL:*:*)
+ echo nsv-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSX-*:NONSTOP_KERNEL:*:*)
+ echo nsx-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit ;;
+ DS/*:UNIX_System_V:*:*)
+ echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE"
+ exit ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ # shellcheck disable=SC2154
+ if test "$cputype" = 386; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo "$UNAME_MACHINE"-unknown-plan9
+ exit ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux"$UNAME_RELEASE"
+ exit ;;
+ *:DragonFly:*:*)
+ echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`"
+ exit ;;
+ *:*VMS:*:*)
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ case "$UNAME_MACHINE" in
+ A*) echo alpha-dec-vms ; exit ;;
+ I*) echo ia64-dec-vms ; exit ;;
+ V*) echo vax-dec-vms ; exit ;;
+ esac ;;
+ *:XENIX:*:SysV)
+ echo i386-pc-xenix
+ exit ;;
+ i*86:skyos:*:*)
+ echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`"
+ exit ;;
+ i*86:rdos:*:*)
+ echo "$UNAME_MACHINE"-pc-rdos
+ exit ;;
+ i*86:AROS:*:*)
+ echo "$UNAME_MACHINE"-pc-aros
+ exit ;;
+ x86_64:VMkernel:*:*)
+ echo "$UNAME_MACHINE"-unknown-esx
+ exit ;;
+ amd64:Isilon\ OneFS:*:*)
+ echo x86_64-unknown-onefs
+ exit ;;
+ *:Unleashed:*:*)
+ echo "$UNAME_MACHINE"-unknown-unleashed"$UNAME_RELEASE"
+ exit ;;
+esac
+
+# No uname command or uname output not recognized.
+set_cc_for_build
+cat > "$dummy.c" <<EOF
+#ifdef _SEQUENT_
+#include <sys/types.h>
+#include <sys/utsname.h>
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#include <signal.h>
+#if defined(_SIZE_T_) || defined(SIGLOST)
+#include <sys/utsname.h>
+#endif
+#endif
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+#endif
+
+#if defined (vax)
+#if !defined (ultrix)
+#include <sys/param.h>
+#if defined (BSD)
+#if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+#else
+#if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#else
+ printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#endif
+#else
+ printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#else
+#if defined(_SIZE_T_) || defined(SIGLOST)
+ struct utsname un;
+ uname (&un);
+ printf ("vax-dec-ultrix%s\n", un.release); exit (0);
+#else
+ printf ("vax-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#if defined(_SIZE_T_) || defined(SIGLOST)
+ struct utsname *un;
+ uname (&un);
+ printf ("mips-dec-ultrix%s\n", un.release); exit (0);
+#else
+ printf ("mips-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; }
+
+echo "$0: unable to guess system type" >&2
+
+case "$UNAME_MACHINE:$UNAME_SYSTEM" in
+ mips:Linux | mips64:Linux)
+ # If we got here on MIPS GNU/Linux, output extra information.
+ cat >&2 <<EOF
+
+NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize
+the system type. Please install a C compiler and try again.
+EOF
+ ;;
+esac
+
+cat >&2 <<EOF
+
+This script (version $timestamp), has failed to recognize the
+operating system you are using. If your script is old, overwrite *all*
+copies of config.guess and config.sub with the latest versions from:
+
+ https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
+and
+ https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
+
+If $0 has already been updated, send the following data and any
+information you think might be pertinent to config-patches@gnu.org to
+provide the necessary information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = "$UNAME_MACHINE"
+UNAME_RELEASE = "$UNAME_RELEASE"
+UNAME_SYSTEM = "$UNAME_SYSTEM"
+UNAME_VERSION = "$UNAME_VERSION"
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/build/config.sub b/build/config.sub
new file mode 100755
index 0000000..f02d43a
--- /dev/null
+++ b/build/config.sub
@@ -0,0 +1,1793 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright 1992-2020 Free Software Foundation, Inc.
+
+timestamp='2020-01-01'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+
+
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
+
+Canonicalize a configuration name.
+
+Options:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright 1992-2020 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo "$1"
+ exit ;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Split fields of configuration type
+# shellcheck disable=SC2162
+IFS="-" read field1 field2 field3 field4 <<EOF
+$1
+EOF
+
+# Separate into logical components for further validation
+case $1 in
+ *-*-*-*-*)
+ echo Invalid configuration \`"$1"\': more than four components >&2
+ exit 1
+ ;;
+ *-*-*-*)
+ basic_machine=$field1-$field2
+ os=$field3-$field4
+ ;;
+ *-*-*)
+ # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two
+ # parts
+ maybe_os=$field2-$field3
+ case $maybe_os in
+ nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc \
+ | linux-newlib* | linux-musl* | linux-uclibc* | uclinux-uclibc* \
+ | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \
+ | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \
+ | storm-chaos* | os2-emx* | rtmk-nova*)
+ basic_machine=$field1
+ os=$maybe_os
+ ;;
+ android-linux)
+ basic_machine=$field1-unknown
+ os=linux-android
+ ;;
+ *)
+ basic_machine=$field1-$field2
+ os=$field3
+ ;;
+ esac
+ ;;
+ *-*)
+ # A lone config we happen to match not fitting any pattern
+ case $field1-$field2 in
+ decstation-3100)
+ basic_machine=mips-dec
+ os=
+ ;;
+ *-*)
+ # Second component is usually, but not always the OS
+ case $field2 in
+ # Prevent following clause from handling this valid os
+ sun*os*)
+ basic_machine=$field1
+ os=$field2
+ ;;
+ # Manufacturers
+ dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \
+ | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \
+ | unicom* | ibm* | next | hp | isi* | apollo | altos* \
+ | convergent* | ncr* | news | 32* | 3600* | 3100* \
+ | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \
+ | ultra | tti* | harris | dolphin | highlevel | gould \
+ | cbm | ns | masscomp | apple | axis | knuth | cray \
+ | microblaze* | sim | cisco \
+ | oki | wec | wrs | winbond)
+ basic_machine=$field1-$field2
+ os=
+ ;;
+ *)
+ basic_machine=$field1
+ os=$field2
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ *)
+ # Convert single-component short-hands not valid as part of
+ # multi-component configurations.
+ case $field1 in
+ 386bsd)
+ basic_machine=i386-pc
+ os=bsd
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=udi
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=scout
+ ;;
+ alliant)
+ basic_machine=fx80-alliant
+ os=
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ os=
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=bsd
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=sysv
+ ;;
+ amiga)
+ basic_machine=m68k-unknown
+ os=
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=bsd
+ ;;
+ aros)
+ basic_machine=i386-pc
+ os=aros
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=dynix
+ ;;
+ blackfin)
+ basic_machine=bfin-unknown
+ os=linux
+ ;;
+ cegcc)
+ basic_machine=arm-unknown
+ os=cegcc
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=bsd
+ ;;
+ cray)
+ basic_machine=j90-cray
+ os=unicos
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ os=
+ ;;
+ da30)
+ basic_machine=m68k-da30
+ os=
+ ;;
+ decstation | pmax | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ os=
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=sysv3
+ ;;
+ dicos)
+ basic_machine=i686-pc
+ os=dicos
+ ;;
+ djgpp)
+ basic_machine=i586-pc
+ os=msdosdjgpp
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=ebmon
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=ose
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=go32
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=sysv3
+ ;;
+ hp300 | hp300hpux)
+ basic_machine=m68k-hp
+ os=hpux
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=bsd
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=proelf
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=mach
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=sysv
+ ;;
+ m68knommu)
+ basic_machine=m68k-unknown
+ os=linux
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=sysv
+ ;;
+ mingw64)
+ basic_machine=x86_64-pc
+ os=mingw64
+ ;;
+ mingw32)
+ basic_machine=i686-pc
+ os=mingw32
+ ;;
+ mingw32ce)
+ basic_machine=arm-unknown
+ os=mingw32ce
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=coff
+ ;;
+ morphos)
+ basic_machine=powerpc-unknown
+ os=morphos
+ ;;
+ moxiebox)
+ basic_machine=moxie-unknown
+ os=moxiebox
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=msdos
+ ;;
+ msys)
+ basic_machine=i686-pc
+ os=msys
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=mvs
+ ;;
+ nacl)
+ basic_machine=le32-unknown
+ os=nacl
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-pc
+ os=netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=sysv
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=nonstopux
+ ;;
+ os400)
+ basic_machine=powerpc-ibm
+ os=os400
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=os68k
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=osf
+ ;;
+ parisc)
+ basic_machine=hppa-unknown
+ os=linux
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=pw32
+ ;;
+ rdos | rdos64)
+ basic_machine=x86_64-pc
+ os=rdos
+ ;;
+ rdos32)
+ basic_machine=i386-pc
+ os=rdos
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=coff
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=udi
+ ;;
+ sei)
+ basic_machine=mips-sei
+ os=seiux
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ os=
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=sysv2
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ os=
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ os=
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=sunos4
+ ;;
+ sun3)
+ basic_machine=m68k-sun
+ os=
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=sunos4
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ os=
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=solaris2
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ os=
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=dynix
+ ;;
+ t3e)
+ basic_machine=alphaev5-cray
+ os=unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
+ os=unicos
+ ;;
+ toad1)
+ basic_machine=pdp10-xkl
+ os=tops20
+ ;;
+ tpf)
+ basic_machine=s390x-ibm
+ os=tpf
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=vms
+ ;;
+ vsta)
+ basic_machine=i386-pc
+ os=vsta
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=vxworks
+ ;;
+ xbox)
+ basic_machine=i686-pc
+ os=mingw32
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ os=unicos
+ ;;
+ *)
+ basic_machine=$1
+ os=
+ ;;
+ esac
+ ;;
+esac
+
+# Decode 1-component or ad-hoc basic machines
+case $basic_machine in
+ # Here we handle the default manufacturer of certain CPU types. It is in
+ # some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ cpu=hppa1.1
+ vendor=winbond
+ ;;
+ op50n)
+ cpu=hppa1.1
+ vendor=oki
+ ;;
+ op60c)
+ cpu=hppa1.1
+ vendor=oki
+ ;;
+ ibm*)
+ cpu=i370
+ vendor=ibm
+ ;;
+ orion105)
+ cpu=clipper
+ vendor=highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ cpu=m68k
+ vendor=apple
+ ;;
+ pmac | pmac-mpw)
+ cpu=powerpc
+ vendor=apple
+ ;;
+
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ cpu=m68000
+ vendor=att
+ ;;
+ 3b*)
+ cpu=we32k
+ vendor=att
+ ;;
+ bluegene*)
+ cpu=powerpc
+ vendor=ibm
+ os=cnk
+ ;;
+ decsystem10* | dec10*)
+ cpu=pdp10
+ vendor=dec
+ os=tops10
+ ;;
+ decsystem20* | dec20*)
+ cpu=pdp10
+ vendor=dec
+ os=tops20
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ cpu=m68k
+ vendor=motorola
+ ;;
+ dpx2*)
+ cpu=m68k
+ vendor=bull
+ os=sysv3
+ ;;
+ encore | umax | mmax)
+ cpu=ns32k
+ vendor=encore
+ ;;
+ elxsi)
+ cpu=elxsi
+ vendor=elxsi
+ os=${os:-bsd}
+ ;;
+ fx2800)
+ cpu=i860
+ vendor=alliant
+ ;;
+ genix)
+ cpu=ns32k
+ vendor=ns
+ ;;
+ h3050r* | hiux*)
+ cpu=hppa1.1
+ vendor=hitachi
+ os=hiuxwe2
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ cpu=hppa1.0
+ vendor=hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ cpu=m68000
+ vendor=hp
+ ;;
+ hp9k3[2-9][0-9])
+ cpu=m68k
+ vendor=hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ cpu=hppa1.0
+ vendor=hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ cpu=hppa1.1
+ vendor=hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ cpu=hppa1.1
+ vendor=hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ cpu=hppa1.1
+ vendor=hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ cpu=hppa1.1
+ vendor=hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ cpu=hppa1.0
+ vendor=hp
+ ;;
+ i*86v32)
+ cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ vendor=pc
+ os=sysv32
+ ;;
+ i*86v4*)
+ cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ vendor=pc
+ os=sysv4
+ ;;
+ i*86v)
+ cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ vendor=pc
+ os=sysv
+ ;;
+ i*86sol2)
+ cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ vendor=pc
+ os=solaris2
+ ;;
+ j90 | j90-cray)
+ cpu=j90
+ vendor=cray
+ os=${os:-unicos}
+ ;;
+ iris | iris4d)
+ cpu=mips
+ vendor=sgi
+ case $os in
+ irix*)
+ ;;
+ *)
+ os=irix4
+ ;;
+ esac
+ ;;
+ miniframe)
+ cpu=m68000
+ vendor=convergent
+ ;;
+ *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ cpu=m68k
+ vendor=atari
+ os=mint
+ ;;
+ news-3600 | risc-news)
+ cpu=mips
+ vendor=sony
+ os=newsos
+ ;;
+ next | m*-next)
+ cpu=m68k
+ vendor=next
+ case $os in
+ openstep*)
+ ;;
+ nextstep*)
+ ;;
+ ns2*)
+ os=nextstep2
+ ;;
+ *)
+ os=nextstep3
+ ;;
+ esac
+ ;;
+ np1)
+ cpu=np1
+ vendor=gould
+ ;;
+ op50n-* | op60c-*)
+ cpu=hppa1.1
+ vendor=oki
+ os=proelf
+ ;;
+ pa-hitachi)
+ cpu=hppa1.1
+ vendor=hitachi
+ os=hiuxwe2
+ ;;
+ pbd)
+ cpu=sparc
+ vendor=tti
+ ;;
+ pbb)
+ cpu=m68k
+ vendor=tti
+ ;;
+ pc532)
+ cpu=ns32k
+ vendor=pc532
+ ;;
+ pn)
+ cpu=pn
+ vendor=gould
+ ;;
+ power)
+ cpu=power
+ vendor=ibm
+ ;;
+ ps2)
+ cpu=i386
+ vendor=ibm
+ ;;
+ rm[46]00)
+ cpu=mips
+ vendor=siemens
+ ;;
+ rtpc | rtpc-*)
+ cpu=romp
+ vendor=ibm
+ ;;
+ sde)
+ cpu=mipsisa32
+ vendor=sde
+ os=${os:-elf}
+ ;;
+ simso-wrs)
+ cpu=sparclite
+ vendor=wrs
+ os=vxworks
+ ;;
+ tower | tower-32)
+ cpu=m68k
+ vendor=ncr
+ ;;
+ vpp*|vx|vx-*)
+ cpu=f301
+ vendor=fujitsu
+ ;;
+ w65)
+ cpu=w65
+ vendor=wdc
+ ;;
+ w89k-*)
+ cpu=hppa1.1
+ vendor=winbond
+ os=proelf
+ ;;
+ none)
+ cpu=none
+ vendor=none
+ ;;
+ leon|leon[3-9])
+ cpu=sparc
+ vendor=$basic_machine
+ ;;
+ leon-*|leon[3-9]-*)
+ cpu=sparc
+ vendor=`echo "$basic_machine" | sed 's/-.*//'`
+ ;;
+
+ *-*)
+ # shellcheck disable=SC2162
+ IFS="-" read cpu vendor <<EOF
+$basic_machine
+EOF
+ ;;
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ cpu=$basic_machine
+ vendor=pc
+ ;;
+ # These rules are duplicated from below for sake of the special case above;
+ # i.e. things that normalized to x86 arches should also default to "pc"
+ pc98)
+ cpu=i386
+ vendor=pc
+ ;;
+ x64 | amd64)
+ cpu=x86_64
+ vendor=pc
+ ;;
+ # Recognize the basic CPU types without company name.
+ *)
+ cpu=$basic_machine
+ vendor=unknown
+ ;;
+esac
+
+unset -v basic_machine
+
+# Decode basic machines in the full and proper CPU-Company form.
+case $cpu-$vendor in
+ # Here we handle the default manufacturer of certain CPU types in canonical form. It is in
+ # some cases the only manufacturer, in others, it is the most popular.
+ craynv-unknown)
+ vendor=cray
+ os=${os:-unicosmp}
+ ;;
+ c90-unknown | c90-cray)
+ vendor=cray
+ os=${os:-unicos}
+ ;;
+ fx80-unknown)
+ vendor=alliant
+ ;;
+ romp-unknown)
+ vendor=ibm
+ ;;
+ mmix-unknown)
+ vendor=knuth
+ ;;
+ microblaze-unknown | microblazeel-unknown)
+ vendor=xilinx
+ ;;
+ rs6000-unknown)
+ vendor=ibm
+ ;;
+ vax-unknown)
+ vendor=dec
+ ;;
+ pdp11-unknown)
+ vendor=dec
+ ;;
+ we32k-unknown)
+ vendor=att
+ ;;
+ cydra-unknown)
+ vendor=cydrome
+ ;;
+ i370-ibm*)
+ vendor=ibm
+ ;;
+ orion-unknown)
+ vendor=highlevel
+ ;;
+ xps-unknown | xps100-unknown)
+ cpu=xps100
+ vendor=honeywell
+ ;;
+
+ # Here we normalize CPU types with a missing or matching vendor
+ dpx20-unknown | dpx20-bull)
+ cpu=rs6000
+ vendor=bull
+ os=${os:-bosx}
+ ;;
+
+ # Here we normalize CPU types irrespective of the vendor
+ amd64-*)
+ cpu=x86_64
+ ;;
+ blackfin-*)
+ cpu=bfin
+ os=linux
+ ;;
+ c54x-*)
+ cpu=tic54x
+ ;;
+ c55x-*)
+ cpu=tic55x
+ ;;
+ c6x-*)
+ cpu=tic6x
+ ;;
+ e500v[12]-*)
+ cpu=powerpc
+ os=$os"spe"
+ ;;
+ mips3*-*)
+ cpu=mips64
+ ;;
+ ms1-*)
+ cpu=mt
+ ;;
+ m68knommu-*)
+ cpu=m68k
+ os=linux
+ ;;
+ m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*)
+ cpu=s12z
+ ;;
+ openrisc-*)
+ cpu=or32
+ ;;
+ parisc-*)
+ cpu=hppa
+ os=linux
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ cpu=i586
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-* | athalon_*-*)
+ cpu=i686
+ ;;
+ pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+ cpu=i686
+ ;;
+ pentium4-*)
+ cpu=i786
+ ;;
+ pc98-*)
+ cpu=i386
+ ;;
+ ppc-* | ppcbe-*)
+ cpu=powerpc
+ ;;
+ ppcle-* | powerpclittle-*)
+ cpu=powerpcle
+ ;;
+ ppc64-*)
+ cpu=powerpc64
+ ;;
+ ppc64le-* | powerpc64little-*)
+ cpu=powerpc64le
+ ;;
+ sb1-*)
+ cpu=mipsisa64sb1
+ ;;
+ sb1el-*)
+ cpu=mipsisa64sb1el
+ ;;
+ sh5e[lb]-*)
+ cpu=`echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/'`
+ ;;
+ spur-*)
+ cpu=spur
+ ;;
+ strongarm-* | thumb-*)
+ cpu=arm
+ ;;
+ tx39-*)
+ cpu=mipstx39
+ ;;
+ tx39el-*)
+ cpu=mipstx39el
+ ;;
+ x64-*)
+ cpu=x86_64
+ ;;
+ xscale-* | xscalee[bl]-*)
+ cpu=`echo "$cpu" | sed 's/^xscale/arm/'`
+ ;;
+
+ # Recognize the canonical CPU Types that limit and/or modify the
+ # company names they are paired with.
+ cr16-*)
+ os=${os:-elf}
+ ;;
+ crisv32-* | etraxfs*-*)
+ cpu=crisv32
+ vendor=axis
+ ;;
+ cris-* | etrax*-*)
+ cpu=cris
+ vendor=axis
+ ;;
+ crx-*)
+ os=${os:-elf}
+ ;;
+ neo-tandem)
+ cpu=neo
+ vendor=tandem
+ ;;
+ nse-tandem)
+ cpu=nse
+ vendor=tandem
+ ;;
+ nsr-tandem)
+ cpu=nsr
+ vendor=tandem
+ ;;
+ nsv-tandem)
+ cpu=nsv
+ vendor=tandem
+ ;;
+ nsx-tandem)
+ cpu=nsx
+ vendor=tandem
+ ;;
+ s390-*)
+ cpu=s390
+ vendor=ibm
+ ;;
+ s390x-*)
+ cpu=s390x
+ vendor=ibm
+ ;;
+ tile*-*)
+ os=${os:-linux-gnu}
+ ;;
+
+ *)
+ # Recognize the canonical CPU types that are allowed with any
+ # company name.
+ case $cpu in
+ 1750a | 580 \
+ | a29k \
+ | aarch64 | aarch64_be \
+ | abacus \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \
+ | alphapca5[67] | alpha64pca5[67] \
+ | am33_2.0 \
+ | amdgcn \
+ | arc | arceb \
+ | arm | arm[lb]e | arme[lb] | armv* \
+ | avr | avr32 \
+ | asmjs \
+ | ba \
+ | be32 | be64 \
+ | bfin | bpf | bs2000 \
+ | c[123]* | c30 | [cjt]90 | c4x \
+ | c8051 | clipper | craynv | csky | cydra \
+ | d10v | d30v | dlx | dsp16xx \
+ | e2k | elxsi | epiphany \
+ | f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \
+ | h8300 | h8500 \
+ | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | hexagon \
+ | i370 | i*86 | i860 | i960 | ia16 | ia64 \
+ | ip2k | iq2000 \
+ | k1om \
+ | le32 | le64 \
+ | lm32 \
+ | m32c | m32r | m32rle \
+ | m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \
+ | m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \
+ | m88110 | m88k | maxq | mb | mcore | mep | metag \
+ | microblaze | microblazeel \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64eb | mips64el \
+ | mips64octeon | mips64octeonel \
+ | mips64orion | mips64orionel \
+ | mips64r5900 | mips64r5900el \
+ | mips64vr | mips64vrel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mips64vr5900 | mips64vr5900el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa32r6 | mipsisa32r6el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64r6 | mipsisa64r6el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipsr5900 | mipsr5900el \
+ | mipstx39 | mipstx39el \
+ | mmix \
+ | mn10200 | mn10300 \
+ | moxie \
+ | mt \
+ | msp430 \
+ | nds32 | nds32le | nds32be \
+ | nfp \
+ | nios | nios2 | nios2eb | nios2el \
+ | none | np1 | ns16k | ns32k | nvptx \
+ | open8 \
+ | or1k* \
+ | or32 \
+ | orion \
+ | picochip \
+ | pdp10 | pdp11 | pj | pjl | pn | power \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \
+ | pru \
+ | pyramid \
+ | riscv | riscv32 | riscv64 \
+ | rl78 | romp | rs6000 | rx \
+ | score \
+ | sh | shl \
+ | sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \
+ | sh[1234]e[lb] | sh[12345][lb]e | sh[23]ele | sh64 | sh64le \
+ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet \
+ | sparclite \
+ | sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \
+ | spu \
+ | tahoe \
+ | tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \
+ | tron \
+ | ubicom32 \
+ | v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \
+ | vax \
+ | visium \
+ | w65 \
+ | wasm32 | wasm64 \
+ | we32k \
+ | x86 | x86_64 | xc16x | xgate | xps100 \
+ | xstormy16 | xtensa* \
+ | ymp \
+ | z8k | z80)
+ ;;
+
+ *)
+ echo Invalid configuration \`"$1"\': machine \`"$cpu-$vendor"\' not recognized 1>&2
+ exit 1
+ ;;
+ esac
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $vendor in
+ digital*)
+ vendor=dec
+ ;;
+ commodore*)
+ vendor=cbm
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x$os != x ]
+then
+case $os in
+ # First match some system type aliases that might get confused
+ # with valid system types.
+ # solaris* is a basic system type, with this one exception.
+ auroraux)
+ os=auroraux
+ ;;
+ bluegene*)
+ os=cnk
+ ;;
+ solaris1 | solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ solaris)
+ os=solaris2
+ ;;
+ unixware*)
+ os=sysv4.2uw
+ ;;
+ gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # es1800 is here to avoid being matched by es* (a different OS)
+ es1800*)
+ os=ose
+ ;;
+ # Some version numbers need modification
+ chorusos*)
+ os=chorusos
+ ;;
+ isc)
+ os=isc2.2
+ ;;
+ sco6)
+ os=sco5v6
+ ;;
+ sco5)
+ os=sco3.2v5
+ ;;
+ sco4)
+ os=sco3.2v4
+ ;;
+ sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ ;;
+ sco3.2v[4-9]* | sco5v6*)
+ # Don't forget version if it is 3.2v4 or newer.
+ ;;
+ scout)
+ # Don't match below
+ ;;
+ sco*)
+ os=sco3.2v2
+ ;;
+ psos*)
+ os=psos
+ ;;
+ # Now accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST end in a * to match a version number.
+ # sysv* is not here because it comes later, after sysvr4.
+ gnu* | bsd* | mach* | minix* | genix* | ultrix* | irix* \
+ | *vms* | esix* | aix* | cnk* | sunos | sunos[34]*\
+ | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \
+ | sym* | kopensolaris* | plan9* \
+ | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \
+ | aos* | aros* | cloudabi* | sortix* | twizzler* \
+ | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \
+ | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \
+ | knetbsd* | mirbsd* | netbsd* \
+ | bitrig* | openbsd* | solidbsd* | libertybsd* | os108* \
+ | ekkobsd* | kfreebsd* | freebsd* | riscix* | lynxos* \
+ | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \
+ | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \
+ | udi* | eabi* | lites* | ieee* | go32* | aux* | hcos* \
+ | chorusrdb* | cegcc* | glidix* \
+ | cygwin* | msys* | pe* | moss* | proelf* | rtems* \
+ | midipix* | mingw32* | mingw64* | linux-gnu* | linux-android* \
+ | linux-newlib* | linux-musl* | linux-uclibc* \
+ | uxpv* | beos* | mpeix* | udk* | moxiebox* \
+ | interix* | uwin* | mks* | rhapsody* | darwin* \
+ | openstep* | oskit* | conix* | pw32* | nonstopux* \
+ | storm-chaos* | tops10* | tenex* | tops20* | its* \
+ | os2* | vos* | palmos* | uclinux* | nucleus* \
+ | morphos* | superux* | rtmk* | windiss* \
+ | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \
+ | skyos* | haiku* | rdos* | toppers* | drops* | es* \
+ | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
+ | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
+ | nsk* | powerunix)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ qnx*)
+ case $cpu in
+ x86 | i*86)
+ ;;
+ *)
+ os=nto-$os
+ ;;
+ esac
+ ;;
+ hiux*)
+ os=hiuxwe2
+ ;;
+ nto-qnx*)
+ ;;
+ nto*)
+ os=`echo $os | sed -e 's|nto|nto-qnx|'`
+ ;;
+ sim | xray | os68k* | v88r* \
+ | windows* | osx | abug | netware* | os9* \
+ | macos* | mpw* | magic* | mmixware* | mon960* | lnews*)
+ ;;
+ linux-dietlibc)
+ os=linux-dietlibc
+ ;;
+ linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ lynx*178)
+ os=lynxos178
+ ;;
+ lynx*5)
+ os=lynxos5
+ ;;
+ lynx*)
+ os=lynxos
+ ;;
+ mac*)
+ os=`echo "$os" | sed -e 's|mac|macos|'`
+ ;;
+ opened*)
+ os=openedition
+ ;;
+ os400*)
+ os=os400
+ ;;
+ sunos5*)
+ os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
+ ;;
+ sunos6*)
+ os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
+ ;;
+ wince*)
+ os=wince
+ ;;
+ utek*)
+ os=bsd
+ ;;
+ dynix*)
+ os=bsd
+ ;;
+ acis*)
+ os=aos
+ ;;
+ atheos*)
+ os=atheos
+ ;;
+ syllable*)
+ os=syllable
+ ;;
+ 386bsd)
+ os=bsd
+ ;;
+ ctix* | uts*)
+ os=sysv
+ ;;
+ nova*)
+ os=rtmk-nova
+ ;;
+ ns2)
+ os=nextstep2
+ ;;
+ # Preserve the version number of sinix5.
+ sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ sinix*)
+ os=sysv4
+ ;;
+ tpf*)
+ os=tpf
+ ;;
+ triton*)
+ os=sysv3
+ ;;
+ oss*)
+ os=sysv3
+ ;;
+ svr4*)
+ os=sysv4
+ ;;
+ svr3)
+ os=sysv3
+ ;;
+ sysvr4)
+ os=sysv4
+ ;;
+ # This must come after sysvr4.
+ sysv*)
+ ;;
+ ose*)
+ os=ose
+ ;;
+ *mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
+ os=mint
+ ;;
+ zvmoe)
+ os=zvmoe
+ ;;
+ dicos*)
+ os=dicos
+ ;;
+ pikeos*)
+ # Until real need of OS specific support for
+ # particular features comes up, bare metal
+ # configurations are quite functional.
+ case $cpu in
+ arm*)
+ os=eabi
+ ;;
+ *)
+ os=elf
+ ;;
+ esac
+ ;;
+ nacl*)
+ ;;
+ ios)
+ ;;
+ none)
+ ;;
+ *-eabi)
+ ;;
+ *)
+ echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $cpu-$vendor in
+ score-*)
+ os=elf
+ ;;
+ spu-*)
+ os=elf
+ ;;
+ *-acorn)
+ os=riscix1.2
+ ;;
+ arm*-rebel)
+ os=linux
+ ;;
+ arm*-semi)
+ os=aout
+ ;;
+ c4x-* | tic4x-*)
+ os=coff
+ ;;
+ c8051-*)
+ os=elf
+ ;;
+ clipper-intergraph)
+ os=clix
+ ;;
+ hexagon-*)
+ os=elf
+ ;;
+ tic54x-*)
+ os=coff
+ ;;
+ tic55x-*)
+ os=coff
+ ;;
+ tic6x-*)
+ os=coff
+ ;;
+ # This must come before the *-dec entry.
+ pdp10-*)
+ os=tops20
+ ;;
+ pdp11-*)
+ os=none
+ ;;
+ *-dec | vax-*)
+ os=ultrix4.2
+ ;;
+ m68*-apollo)
+ os=domain
+ ;;
+ i386-sun)
+ os=sunos4.0.2
+ ;;
+ m68000-sun)
+ os=sunos3
+ ;;
+ m68*-cisco)
+ os=aout
+ ;;
+ mep-*)
+ os=elf
+ ;;
+ mips*-cisco)
+ os=elf
+ ;;
+ mips*-*)
+ os=elf
+ ;;
+ or32-*)
+ os=coff
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=sysv3
+ ;;
+ sparc-* | *-sun)
+ os=sunos4.1.1
+ ;;
+ pru-*)
+ os=elf
+ ;;
+ *-be)
+ os=beos
+ ;;
+ *-ibm)
+ os=aix
+ ;;
+ *-knuth)
+ os=mmixware
+ ;;
+ *-wec)
+ os=proelf
+ ;;
+ *-winbond)
+ os=proelf
+ ;;
+ *-oki)
+ os=proelf
+ ;;
+ *-hp)
+ os=hpux
+ ;;
+ *-hitachi)
+ os=hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=sysv
+ ;;
+ *-cbm)
+ os=amigaos
+ ;;
+ *-dg)
+ os=dgux
+ ;;
+ *-dolphin)
+ os=sysv3
+ ;;
+ m68k-ccur)
+ os=rtu
+ ;;
+ m88k-omron*)
+ os=luna
+ ;;
+ *-next)
+ os=nextstep
+ ;;
+ *-sequent)
+ os=ptx
+ ;;
+ *-crds)
+ os=unos
+ ;;
+ *-ns)
+ os=genix
+ ;;
+ i370-*)
+ os=mvs
+ ;;
+ *-gould)
+ os=sysv
+ ;;
+ *-highlevel)
+ os=bsd
+ ;;
+ *-encore)
+ os=bsd
+ ;;
+ *-sgi)
+ os=irix
+ ;;
+ *-siemens)
+ os=sysv4
+ ;;
+ *-masscomp)
+ os=rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=uxpv
+ ;;
+ *-rom68k)
+ os=coff
+ ;;
+ *-*bug)
+ os=coff
+ ;;
+ *-apple)
+ os=macos
+ ;;
+ *-atari*)
+ os=mint
+ ;;
+ *-wrs)
+ os=vxworks
+ ;;
+ *)
+ os=none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+case $vendor in
+ unknown)
+ case $os in
+ riscix*)
+ vendor=acorn
+ ;;
+ sunos*)
+ vendor=sun
+ ;;
+ cnk*|-aix*)
+ vendor=ibm
+ ;;
+ beos*)
+ vendor=be
+ ;;
+ hpux*)
+ vendor=hp
+ ;;
+ mpeix*)
+ vendor=hp
+ ;;
+ hiux*)
+ vendor=hitachi
+ ;;
+ unos*)
+ vendor=crds
+ ;;
+ dgux*)
+ vendor=dg
+ ;;
+ luna*)
+ vendor=omron
+ ;;
+ genix*)
+ vendor=ns
+ ;;
+ clix*)
+ vendor=intergraph
+ ;;
+ mvs* | opened*)
+ vendor=ibm
+ ;;
+ os400*)
+ vendor=ibm
+ ;;
+ ptx*)
+ vendor=sequent
+ ;;
+ tpf*)
+ vendor=ibm
+ ;;
+ vxsim* | vxworks* | windiss*)
+ vendor=wrs
+ ;;
+ aux*)
+ vendor=apple
+ ;;
+ hms*)
+ vendor=hitachi
+ ;;
+ mpw* | macos*)
+ vendor=apple
+ ;;
+ *mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
+ vendor=atari
+ ;;
+ vos*)
+ vendor=stratus
+ ;;
+ esac
+ ;;
+esac
+
+echo "$cpu-$vendor-$os"
+exit
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/build/dir.mk b/build/dir.mk
new file mode 100644
index 0000000..e42406b
--- /dev/null
+++ b/build/dir.mk
@@ -0,0 +1,65 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##---------------------------------------------------------------------------
+#
+# Makes subdirectories
+#
+
+
+all-common: FORCE
+ @echo "Making all in `$(PWD)`"
+ @for i in $(SUBDIRS) $(ALLDIRS); do \
+ echo " Entering subdirectory $$i"; \
+ ( cd $$i && $(MAKE) all ); \
+ if test $$? != 0 ; then exit 1; fi ; \
+ echo " "; \
+ done
+
+install-common: FORCE
+ @echo "Making install in `$(PWD)`"
+ @for i in $(SUBDIRS) $(INSTALLDIRS); do \
+ echo " Entering subdirectory $$i"; \
+ ( cd $$i && $(MAKE) install ); \
+ if test $$? != 0 ; then exit 1; fi ; \
+ echo " "; \
+ done
+
+clean-common: FORCE
+ @echo "Making clean in `$(PWD)`"
+ @for i in $(SUBDIRS) $(CLEANDIRS); do \
+ echo " Entering subdirectory $$i"; \
+ ( cd $$i && $(MAKE) clean ); \
+ if test $$? != 0 ; then exit 1; fi ; \
+ echo " "; \
+ done
+
+veryclean-common: FORCE
+ @echo "Making veryclean in `$(PWD)`"
+ @for i in $(SUBDIRS) $(CLEANDIRS); do \
+ echo " Entering subdirectory $$i"; \
+ ( cd $$i && $(MAKE) veryclean ); \
+ if test $$? != 0 ; then exit 1; fi ; \
+ echo " "; \
+ done
+
+depend-common: FORCE
+ @echo "Making depend in `$(PWD)`"
+ @for i in $(SUBDIRS) $(DEPENDDIRS); do \
+ echo " Entering subdirectory $$i"; \
+ ( cd $$i && $(MAKE) depend ); \
+ if test $$? != 0 ; then exit 1; fi ; \
+ echo " "; \
+ done
+
+Makefile: $(top_srcdir)/build/dir.mk
diff --git a/build/info.mk b/build/info.mk
new file mode 100644
index 0000000..2782a2e
--- /dev/null
+++ b/build/info.mk
@@ -0,0 +1,19 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##---------------------------------------------------------------------------
+#
+# Makefile Template for Non-Source Directories
+#
+
+Makefile: $(top_srcdir)/build/info.mk
diff --git a/build/lib-shared.mk b/build/lib-shared.mk
new file mode 100644
index 0000000..8924876
--- /dev/null
+++ b/build/lib-shared.mk
@@ -0,0 +1,30 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##---------------------------------------------------------------------------
+#
+# Makefile Template for Shared Libraries
+#
+
+MKDEPFLAG = -l
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LTCOMPILE_LIB) $<
+
+$(LIBRARY): version.lo
+ $(LTLINK_LIB) -o $@ $(OBJS) version.lo $(LINK_LIBS)
+
+Makefile: $(top_srcdir)/build/lib-shared.mk
+
diff --git a/build/lib-static.mk b/build/lib-static.mk
new file mode 100644
index 0000000..76ca40b
--- /dev/null
+++ b/build/lib-static.mk
@@ -0,0 +1,23 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##---------------------------------------------------------------------------
+#
+# Makefile Template for Static Libraries
+#
+
+$(LIBRARY): version.o
+ $(AR) ru $@ $(OBJS) version.o
+ @$(RANLIB) $@
+
+Makefile: $(top_srcdir)/build/lib-static.mk
diff --git a/build/lib.mk b/build/lib.mk
new file mode 100644
index 0000000..442c0d3
--- /dev/null
+++ b/build/lib.mk
@@ -0,0 +1,52 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##---------------------------------------------------------------------------
+#
+# Makefile Template for Libraries
+#
+
+all-common: $(LIBRARY) $(PROGRAMS)
+
+version.c: Makefile
+ $(RM) $@
+ $(MKVERSION) $(LIBRARY) > $@
+
+version.o version.lo: version.c $(OBJS)
+
+install-common: FORCE
+
+lint: lint-local FORCE
+ $(LINT) $(DEFS) $(DEFINES) $(SRCS)
+
+lint5: lint5-local FORCE
+ $(5LINT) $(DEFS) $(DEFINES) $(SRCS)
+
+#
+# In the mingw/cygwin environment, the so and dll files must be
+# deleted separately, instead of using the {.so*,*.dll} construct
+# that was previously used. It just didn't work.
+#
+clean-common: FORCE
+ $(RM) $(LIBRARY) ../$(LIBRARY) $(XLIBRARY) \
+ $(PROGRAMS) $(XPROGRAMS) $(XSRCS) $(XXSRCS) \
+ *.o *.lo a.out *.exe *.pc core version.c .libs/*
+
+depend-common: FORCE
+ $(MKDEP) $(DEFS) $(DEFINES) $(SRCS) $(XXSRCS)
+
+lint-local: FORCE
+lint5-local: FORCE
+
+Makefile: $(top_srcdir)/build/lib.mk
+
diff --git a/build/libtool.m4 b/build/libtool.m4
new file mode 100644
index 0000000..a3bc337
--- /dev/null
+++ b/build/libtool.m4
@@ -0,0 +1,8369 @@
+# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*-
+#
+# Copyright (C) 1996-2001, 2003-2015 Free Software Foundation, Inc.
+# Written by Gordon Matzigkeit, 1996
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+m4_define([_LT_COPYING], [dnl
+# Copyright (C) 2014 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of of the License, or
+# (at your option) any later version.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program or library that is built
+# using GNU Libtool, you may include this file under the same
+# distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+])
+
+# serial 58 LT_INIT
+
+
+# LT_PREREQ(VERSION)
+# ------------------
+# Complain and exit if this libtool version is less that VERSION.
+m4_defun([LT_PREREQ],
+[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1,
+ [m4_default([$3],
+ [m4_fatal([Libtool version $1 or higher is required],
+ 63)])],
+ [$2])])
+
+
+# _LT_CHECK_BUILDDIR
+# ------------------
+# Complain if the absolute build directory name contains unusual characters
+m4_defun([_LT_CHECK_BUILDDIR],
+[case `pwd` in
+ *\ * | *\ *)
+ AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;;
+esac
+])
+
+
+# LT_INIT([OPTIONS])
+# ------------------
+AC_DEFUN([LT_INIT],
+[AC_PREREQ([2.62])dnl We use AC_PATH_PROGS_FEATURE_CHECK
+AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
+AC_BEFORE([$0], [LT_LANG])dnl
+AC_BEFORE([$0], [LT_OUTPUT])dnl
+AC_BEFORE([$0], [LTDL_INIT])dnl
+m4_require([_LT_CHECK_BUILDDIR])dnl
+
+dnl Autoconf doesn't catch unexpanded LT_ macros by default:
+m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl
+m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl
+dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4
+dnl unless we require an AC_DEFUNed macro:
+AC_REQUIRE([LTOPTIONS_VERSION])dnl
+AC_REQUIRE([LTSUGAR_VERSION])dnl
+AC_REQUIRE([LTVERSION_VERSION])dnl
+AC_REQUIRE([LTOBSOLETE_VERSION])dnl
+m4_require([_LT_PROG_LTMAIN])dnl
+
+_LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}])
+
+dnl Parse OPTIONS
+_LT_SET_OPTIONS([$0], [$1])
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS=$ltmain
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+AC_SUBST(LIBTOOL)dnl
+
+_LT_SETUP
+
+# Only expand once:
+m4_define([LT_INIT])
+])# LT_INIT
+
+# Old names:
+AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT])
+AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PROG_LIBTOOL], [])
+dnl AC_DEFUN([AM_PROG_LIBTOOL], [])
+
+
+# _LT_PREPARE_CC_BASENAME
+# -----------------------
+m4_defun([_LT_PREPARE_CC_BASENAME], [
+# Calculate cc_basename. Skip known compiler wrappers and cross-prefix.
+func_cc_basename ()
+{
+ for cc_temp in @S|@*""; do
+ case $cc_temp in
+ compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;;
+ distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+ done
+ func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+}
+])# _LT_PREPARE_CC_BASENAME
+
+
+# _LT_CC_BASENAME(CC)
+# -------------------
+# It would be clearer to call AC_REQUIREs from _LT_PREPARE_CC_BASENAME,
+# but that macro is also expanded into generated libtool script, which
+# arranges for $SED and $ECHO to be set by different means.
+m4_defun([_LT_CC_BASENAME],
+[m4_require([_LT_PREPARE_CC_BASENAME])dnl
+AC_REQUIRE([_LT_DECL_SED])dnl
+AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl
+func_cc_basename $1
+cc_basename=$func_cc_basename_result
+])
+
+
+# _LT_FILEUTILS_DEFAULTS
+# ----------------------
+# It is okay to use these file commands and assume they have been set
+# sensibly after 'm4_require([_LT_FILEUTILS_DEFAULTS])'.
+m4_defun([_LT_FILEUTILS_DEFAULTS],
+[: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+])# _LT_FILEUTILS_DEFAULTS
+
+
+# _LT_SETUP
+# ---------
+m4_defun([_LT_SETUP],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl
+AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl
+
+_LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl
+dnl
+_LT_DECL([], [host_alias], [0], [The host system])dnl
+_LT_DECL([], [host], [0])dnl
+_LT_DECL([], [host_os], [0])dnl
+dnl
+_LT_DECL([], [build_alias], [0], [The build system])dnl
+_LT_DECL([], [build], [0])dnl
+_LT_DECL([], [build_os], [0])dnl
+dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+dnl
+AC_REQUIRE([AC_PROG_LN_S])dnl
+test -z "$LN_S" && LN_S="ln -s"
+_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl
+dnl
+AC_REQUIRE([LT_CMD_MAX_LEN])dnl
+_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl
+_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl
+dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_CHECK_SHELL_FEATURES])dnl
+m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl
+m4_require([_LT_CMD_RELOAD])dnl
+m4_require([_LT_CHECK_MAGIC_METHOD])dnl
+m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl
+m4_require([_LT_CMD_OLD_ARCHIVE])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+m4_require([_LT_WITH_SYSROOT])dnl
+m4_require([_LT_CMD_TRUNCATE])dnl
+
+_LT_CONFIG_LIBTOOL_INIT([
+# See if we are running on zsh, and set the options that allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}"; then
+ setopt NO_GLOB_SUBST
+fi
+])
+if test -n "${ZSH_VERSION+set}"; then
+ setopt NO_GLOB_SUBST
+fi
+
+_LT_CHECK_OBJDIR
+
+m4_require([_LT_TAG_COMPILER])dnl
+
+case $host_os in
+aix3*)
+ # AIX sometimes has problems with the GCC collect2 program. For some
+ # reason, if we set the COLLECT_NAMES environment variable, the problems
+ # vanish in a puff of smoke.
+ if test set != "${COLLECT_NAMES+set}"; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+ fi
+ ;;
+esac
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a '.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+old_CC=$CC
+old_CFLAGS=$CFLAGS
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+_LT_CC_BASENAME([$compiler])
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+ if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+ _LT_PATH_MAGIC
+ fi
+ ;;
+esac
+
+# Use C for the default configuration in the libtool script
+LT_SUPPORTED_TAG([CC])
+_LT_LANG_C_CONFIG
+_LT_LANG_DEFAULT_CONFIG
+_LT_CONFIG_COMMANDS
+])# _LT_SETUP
+
+
+# _LT_PREPARE_SED_QUOTE_VARS
+# --------------------------
+# Define a few sed substitution that help us do robust quoting.
+m4_defun([_LT_PREPARE_SED_QUOTE_VARS],
+[# Backslashify metacharacters that are still active within
+# double-quoted strings.
+sed_quote_subst='s/\([["`$\\]]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\([["`\\]]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+])
+
+# _LT_PROG_LTMAIN
+# ---------------
+# Note that this code is called both from 'configure', and 'config.status'
+# now that we use AC_CONFIG_COMMANDS to generate libtool. Notably,
+# 'config.status' has no value for ac_aux_dir unless we are using Automake,
+# so we pass a copy along to make sure it has a sensible value anyway.
+m4_defun([_LT_PROG_LTMAIN],
+[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl
+_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir'])
+ltmain=$ac_aux_dir/ltmain.sh
+])# _LT_PROG_LTMAIN
+
+
+## ------------------------------------- ##
+## Accumulate code for creating libtool. ##
+## ------------------------------------- ##
+
+# So that we can recreate a full libtool script including additional
+# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS
+# in macros and then make a single call at the end using the 'libtool'
+# label.
+
+
+# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS])
+# ----------------------------------------
+# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL_INIT],
+[m4_ifval([$1],
+ [m4_append([_LT_OUTPUT_LIBTOOL_INIT],
+ [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_INIT])
+
+
+# _LT_CONFIG_LIBTOOL([COMMANDS])
+# ------------------------------
+# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL],
+[m4_ifval([$1],
+ [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS],
+ [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS])
+
+
+# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS])
+# -----------------------------------------------------
+m4_defun([_LT_CONFIG_SAVE_COMMANDS],
+[_LT_CONFIG_LIBTOOL([$1])
+_LT_CONFIG_LIBTOOL_INIT([$2])
+])
+
+
+# _LT_FORMAT_COMMENT([COMMENT])
+# -----------------------------
+# Add leading comment marks to the start of each line, and a trailing
+# full-stop to the whole comment if one is not present already.
+m4_define([_LT_FORMAT_COMMENT],
+[m4_ifval([$1], [
+m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])],
+ [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.])
+)])
+
+
+
+## ------------------------ ##
+## FIXME: Eliminate VARNAME ##
+## ------------------------ ##
+
+
+# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?])
+# -------------------------------------------------------------------
+# CONFIGNAME is the name given to the value in the libtool script.
+# VARNAME is the (base) name used in the configure script.
+# VALUE may be 0, 1 or 2 for a computed quote escaped value based on
+# VARNAME. Any other value will be used directly.
+m4_define([_LT_DECL],
+[lt_if_append_uniq([lt_decl_varnames], [$2], [, ],
+ [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name],
+ [m4_ifval([$1], [$1], [$2])])
+ lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3])
+ m4_ifval([$4],
+ [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])])
+ lt_dict_add_subkey([lt_decl_dict], [$2],
+ [tagged?], [m4_ifval([$5], [yes], [no])])])
+])
+
+
+# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION])
+# --------------------------------------------------------
+m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])])
+
+
+# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_tag_varnames],
+[_lt_decl_filter([tagged?], [yes], $@)])
+
+
+# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..])
+# ---------------------------------------------------------
+m4_define([_lt_decl_filter],
+[m4_case([$#],
+ [0], [m4_fatal([$0: too few arguments: $#])],
+ [1], [m4_fatal([$0: too few arguments: $#: $1])],
+ [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)],
+ [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)],
+ [lt_dict_filter([lt_decl_dict], $@)])[]dnl
+])
+
+
+# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...])
+# --------------------------------------------------
+m4_define([lt_decl_quote_varnames],
+[_lt_decl_filter([value], [1], $@)])
+
+
+# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_dquote_varnames],
+[_lt_decl_filter([value], [2], $@)])
+
+
+# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_varnames_tagged],
+[m4_assert([$# <= 2])dnl
+_$0(m4_quote(m4_default([$1], [[, ]])),
+ m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]),
+ m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))])
+m4_define([_lt_decl_varnames_tagged],
+[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])])
+
+
+# lt_decl_all_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_all_varnames],
+[_$0(m4_quote(m4_default([$1], [[, ]])),
+ m4_if([$2], [],
+ m4_quote(lt_decl_varnames),
+ m4_quote(m4_shift($@))))[]dnl
+])
+m4_define([_lt_decl_all_varnames],
+[lt_join($@, lt_decl_varnames_tagged([$1],
+ lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl
+])
+
+
+# _LT_CONFIG_STATUS_DECLARE([VARNAME])
+# ------------------------------------
+# Quote a variable value, and forward it to 'config.status' so that its
+# declaration there will have the same value as in 'configure'. VARNAME
+# must have a single quote delimited value for this to work.
+m4_define([_LT_CONFIG_STATUS_DECLARE],
+[$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`'])
+
+
+# _LT_CONFIG_STATUS_DECLARATIONS
+# ------------------------------
+# We delimit libtool config variables with single quotes, so when
+# we write them to config.status, we have to be sure to quote all
+# embedded single quotes properly. In configure, this macro expands
+# each variable declared with _LT_DECL (and _LT_TAGDECL) into:
+#
+# <var>='`$ECHO "$<var>" | $SED "$delay_single_quote_subst"`'
+m4_defun([_LT_CONFIG_STATUS_DECLARATIONS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames),
+ [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAGS
+# ----------------
+# Output comment and list of tags supported by the script
+m4_defun([_LT_LIBTOOL_TAGS],
+[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl
+available_tags='_LT_TAGS'dnl
+])
+
+
+# _LT_LIBTOOL_DECLARE(VARNAME, [TAG])
+# -----------------------------------
+# Extract the dictionary values for VARNAME (optionally with TAG) and
+# expand to a commented shell variable setting:
+#
+# # Some comment about what VAR is for.
+# visible_name=$lt_internal_name
+m4_define([_LT_LIBTOOL_DECLARE],
+[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1],
+ [description])))[]dnl
+m4_pushdef([_libtool_name],
+ m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl
+m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])),
+ [0], [_libtool_name=[$]$1],
+ [1], [_libtool_name=$lt_[]$1],
+ [2], [_libtool_name=$lt_[]$1],
+ [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl
+m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl
+])
+
+
+# _LT_LIBTOOL_CONFIG_VARS
+# -----------------------
+# Produce commented declarations of non-tagged libtool config variables
+# suitable for insertion in the LIBTOOL CONFIG section of the 'libtool'
+# script. Tagged libtool config variables (even for the LIBTOOL CONFIG
+# section) are produced by _LT_LIBTOOL_TAG_VARS.
+m4_defun([_LT_LIBTOOL_CONFIG_VARS],
+[m4_foreach([_lt_var],
+ m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)),
+ [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAG_VARS(TAG)
+# -------------------------
+m4_define([_LT_LIBTOOL_TAG_VARS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames),
+ [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])])
+
+
+# _LT_TAGVAR(VARNAME, [TAGNAME])
+# ------------------------------
+m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])])
+
+
+# _LT_CONFIG_COMMANDS
+# -------------------
+# Send accumulated output to $CONFIG_STATUS. Thanks to the lists of
+# variables for single and double quote escaping we saved from calls
+# to _LT_DECL, we can put quote escaped variables declarations
+# into 'config.status', and then the shell code to quote escape them in
+# for loops in 'config.status'. Finally, any additional code accumulated
+# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded.
+m4_defun([_LT_CONFIG_COMMANDS],
+[AC_PROVIDE_IFELSE([LT_OUTPUT],
+ dnl If the libtool generation code has been placed in $CONFIG_LT,
+ dnl instead of duplicating it all over again into config.status,
+ dnl then we will have config.status run $CONFIG_LT later, so it
+ dnl needs to know what name is stored there:
+ [AC_CONFIG_COMMANDS([libtool],
+ [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])],
+ dnl If the libtool generation code is destined for config.status,
+ dnl expand the accumulated commands and init code now:
+ [AC_CONFIG_COMMANDS([libtool],
+ [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])])
+])#_LT_CONFIG_COMMANDS
+
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT],
+[
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+_LT_CONFIG_STATUS_DECLARATIONS
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+\$[]1
+_LTECHO_EOF'
+}
+
+# Quote evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_quote_varnames); do
+ case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+ *[[\\\\\\\`\\"\\\$]]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Double-quote double-evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_dquote_varnames); do
+ case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+ *[[\\\\\\\`\\"\\\$]]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+_LT_OUTPUT_LIBTOOL_INIT
+])
+
+# _LT_GENERATED_FILE_INIT(FILE, [COMMENT])
+# ------------------------------------
+# Generate a child script FILE with all initialization necessary to
+# reuse the environment learned by the parent script, and make the
+# file executable. If COMMENT is supplied, it is inserted after the
+# '#!' sequence but before initialization text begins. After this
+# macro, additional text can be appended to FILE to form the body of
+# the child script. The macro ends with non-zero status if the
+# file could not be fully written (such as if the disk is full).
+m4_ifdef([AS_INIT_GENERATED],
+[m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])],
+[m4_defun([_LT_GENERATED_FILE_INIT],
+[m4_require([AS_PREPARE])]dnl
+[m4_pushdef([AS_MESSAGE_LOG_FD])]dnl
+[lt_write_fail=0
+cat >$1 <<_ASEOF || lt_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+$2
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$1 <<\_ASEOF || lt_write_fail=1
+AS_SHELL_SANITIZE
+_AS_PREPARE
+exec AS_MESSAGE_FD>&1
+_ASEOF
+test 0 = "$lt_write_fail" && chmod +x $1[]dnl
+m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT
+
+# LT_OUTPUT
+# ---------
+# This macro allows early generation of the libtool script (before
+# AC_OUTPUT is called), incase it is used in configure for compilation
+# tests.
+AC_DEFUN([LT_OUTPUT],
+[: ${CONFIG_LT=./config.lt}
+AC_MSG_NOTICE([creating $CONFIG_LT])
+_LT_GENERATED_FILE_INIT(["$CONFIG_LT"],
+[# Run this file to recreate a libtool stub with the current configuration.])
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+lt_cl_silent=false
+exec AS_MESSAGE_LOG_FD>>config.log
+{
+ echo
+ AS_BOX([Running $as_me.])
+} >&AS_MESSAGE_LOG_FD
+
+lt_cl_help="\
+'$as_me' creates a local libtool stub from the current configuration,
+for use in further configure time tests before the real libtool is
+generated.
+
+Usage: $[0] [[OPTIONS]]
+
+ -h, --help print this help, then exit
+ -V, --version print version number, then exit
+ -q, --quiet do not print progress messages
+ -d, --debug don't remove temporary files
+
+Report bugs to <bug-libtool@gnu.org>."
+
+lt_cl_version="\
+m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl
+m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION])
+configured by $[0], generated by m4_PACKAGE_STRING.
+
+Copyright (C) 2011 Free Software Foundation, Inc.
+This config.lt script is free software; the Free Software Foundation
+gives unlimited permision to copy, distribute and modify it."
+
+while test 0 != $[#]
+do
+ case $[1] in
+ --version | --v* | -V )
+ echo "$lt_cl_version"; exit 0 ;;
+ --help | --h* | -h )
+ echo "$lt_cl_help"; exit 0 ;;
+ --debug | --d* | -d )
+ debug=: ;;
+ --quiet | --q* | --silent | --s* | -q )
+ lt_cl_silent=: ;;
+
+ -*) AC_MSG_ERROR([unrecognized option: $[1]
+Try '$[0] --help' for more information.]) ;;
+
+ *) AC_MSG_ERROR([unrecognized argument: $[1]
+Try '$[0] --help' for more information.]) ;;
+ esac
+ shift
+done
+
+if $lt_cl_silent; then
+ exec AS_MESSAGE_FD>/dev/null
+fi
+_LTEOF
+
+cat >>"$CONFIG_LT" <<_LTEOF
+_LT_OUTPUT_LIBTOOL_COMMANDS_INIT
+_LTEOF
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+AC_MSG_NOTICE([creating $ofile])
+_LT_OUTPUT_LIBTOOL_COMMANDS
+AS_EXIT(0)
+_LTEOF
+chmod +x "$CONFIG_LT"
+
+# configure is writing to config.log, but config.lt does its own redirection,
+# appending to config.log, which fails on DOS, as config.log is still kept
+# open by configure. Here we exec the FD to /dev/null, effectively closing
+# config.log, so it can be properly (re)opened and appended to by config.lt.
+lt_cl_success=:
+test yes = "$silent" &&
+ lt_config_lt_args="$lt_config_lt_args --quiet"
+exec AS_MESSAGE_LOG_FD>/dev/null
+$SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false
+exec AS_MESSAGE_LOG_FD>>config.log
+$lt_cl_success || AS_EXIT(1)
+])# LT_OUTPUT
+
+
+# _LT_CONFIG(TAG)
+# ---------------
+# If TAG is the built-in tag, create an initial libtool script with a
+# default configuration from the untagged config vars. Otherwise add code
+# to config.status for appending the configuration named by TAG from the
+# matching tagged config vars.
+m4_defun([_LT_CONFIG],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_CONFIG_SAVE_COMMANDS([
+ m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl
+ m4_if(_LT_TAG, [C], [
+ # See if we are running on zsh, and set the options that allow our
+ # commands through without removal of \ escapes.
+ if test -n "${ZSH_VERSION+set}"; then
+ setopt NO_GLOB_SUBST
+ fi
+
+ cfgfile=${ofile}T
+ trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+ $RM "$cfgfile"
+
+ cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+# Generated automatically by $as_me ($PACKAGE) $VERSION
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+
+# Provide generalized library-building support services.
+# Written by Gordon Matzigkeit, 1996
+
+_LT_COPYING
+_LT_LIBTOOL_TAGS
+
+# Configured defaults for sys_lib_dlsearch_path munging.
+: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"}
+
+# ### BEGIN LIBTOOL CONFIG
+_LT_LIBTOOL_CONFIG_VARS
+_LT_LIBTOOL_TAG_VARS
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+ cat <<'_LT_EOF' >> "$cfgfile"
+
+# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE
+
+_LT_PREPARE_MUNGE_PATH_LIST
+_LT_PREPARE_CC_BASENAME
+
+# ### END FUNCTIONS SHARED WITH CONFIGURE
+
+_LT_EOF
+
+ case $host_os in
+ aix3*)
+ cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program. For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test set != "${COLLECT_NAMES+set}"; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+fi
+_LT_EOF
+ ;;
+ esac
+
+ _LT_PROG_LTMAIN
+
+ # We use sed instead of cat because bash on DJGPP gets confused if
+ # if finds mixed CR/LF and LF-only lines. Since sed operates in
+ # text mode, it properly converts lines to CR/LF. This bash problem
+ # is reportedly fixed, but why not run on old versions too?
+ sed '$q' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ mv -f "$cfgfile" "$ofile" ||
+ (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+ chmod +x "$ofile"
+],
+[cat <<_LT_EOF >> "$ofile"
+
+dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded
+dnl in a comment (ie after a #).
+# ### BEGIN LIBTOOL TAG CONFIG: $1
+_LT_LIBTOOL_TAG_VARS(_LT_TAG)
+# ### END LIBTOOL TAG CONFIG: $1
+_LT_EOF
+])dnl /m4_if
+],
+[m4_if([$1], [], [
+ PACKAGE='$PACKAGE'
+ VERSION='$VERSION'
+ RM='$RM'
+ ofile='$ofile'], [])
+])dnl /_LT_CONFIG_SAVE_COMMANDS
+])# _LT_CONFIG
+
+
+# LT_SUPPORTED_TAG(TAG)
+# ---------------------
+# Trace this macro to discover what tags are supported by the libtool
+# --tag option, using:
+# autoconf --trace 'LT_SUPPORTED_TAG:$1'
+AC_DEFUN([LT_SUPPORTED_TAG], [])
+
+
+# C support is built-in for now
+m4_define([_LT_LANG_C_enabled], [])
+m4_define([_LT_TAGS], [])
+
+
+# LT_LANG(LANG)
+# -------------
+# Enable libtool support for the given language if not already enabled.
+AC_DEFUN([LT_LANG],
+[AC_BEFORE([$0], [LT_OUTPUT])dnl
+m4_case([$1],
+ [C], [_LT_LANG(C)],
+ [C++], [_LT_LANG(CXX)],
+ [Go], [_LT_LANG(GO)],
+ [Java], [_LT_LANG(GCJ)],
+ [Fortran 77], [_LT_LANG(F77)],
+ [Fortran], [_LT_LANG(FC)],
+ [Windows Resource], [_LT_LANG(RC)],
+ [m4_ifdef([_LT_LANG_]$1[_CONFIG],
+ [_LT_LANG($1)],
+ [m4_fatal([$0: unsupported language: "$1"])])])dnl
+])# LT_LANG
+
+
+# _LT_LANG(LANGNAME)
+# ------------------
+m4_defun([_LT_LANG],
+[m4_ifdef([_LT_LANG_]$1[_enabled], [],
+ [LT_SUPPORTED_TAG([$1])dnl
+ m4_append([_LT_TAGS], [$1 ])dnl
+ m4_define([_LT_LANG_]$1[_enabled], [])dnl
+ _LT_LANG_$1_CONFIG($1)])dnl
+])# _LT_LANG
+
+
+m4_ifndef([AC_PROG_GO], [
+############################################################
+# NOTE: This macro has been submitted for inclusion into #
+# GNU Autoconf as AC_PROG_GO. When it is available in #
+# a released version of Autoconf we should remove this #
+# macro and use it instead. #
+############################################################
+m4_defun([AC_PROG_GO],
+[AC_LANG_PUSH(Go)dnl
+AC_ARG_VAR([GOC], [Go compiler command])dnl
+AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl
+_AC_ARG_VAR_LDFLAGS()dnl
+AC_CHECK_TOOL(GOC, gccgo)
+if test -z "$GOC"; then
+ if test -n "$ac_tool_prefix"; then
+ AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo])
+ fi
+fi
+if test -z "$GOC"; then
+ AC_CHECK_PROG(GOC, gccgo, gccgo, false)
+fi
+])#m4_defun
+])#m4_ifndef
+
+
+# _LT_LANG_DEFAULT_CONFIG
+# -----------------------
+m4_defun([_LT_LANG_DEFAULT_CONFIG],
+[AC_PROVIDE_IFELSE([AC_PROG_CXX],
+ [LT_LANG(CXX)],
+ [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_F77],
+ [LT_LANG(F77)],
+ [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_FC],
+ [LT_LANG(FC)],
+ [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])])
+
+dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal
+dnl pulling things in needlessly.
+AC_PROVIDE_IFELSE([AC_PROG_GCJ],
+ [LT_LANG(GCJ)],
+ [AC_PROVIDE_IFELSE([A][M_PROG_GCJ],
+ [LT_LANG(GCJ)],
+ [AC_PROVIDE_IFELSE([LT_PROG_GCJ],
+ [LT_LANG(GCJ)],
+ [m4_ifdef([AC_PROG_GCJ],
+ [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])])
+ m4_ifdef([A][M_PROG_GCJ],
+ [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])])
+ m4_ifdef([LT_PROG_GCJ],
+ [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])])
+
+AC_PROVIDE_IFELSE([AC_PROG_GO],
+ [LT_LANG(GO)],
+ [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])])
+
+AC_PROVIDE_IFELSE([LT_PROG_RC],
+ [LT_LANG(RC)],
+ [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])])
+])# _LT_LANG_DEFAULT_CONFIG
+
+# Obsolete macros:
+AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)])
+AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)])
+AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)])
+AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)])
+AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_CXX], [])
+dnl AC_DEFUN([AC_LIBTOOL_F77], [])
+dnl AC_DEFUN([AC_LIBTOOL_FC], [])
+dnl AC_DEFUN([AC_LIBTOOL_GCJ], [])
+dnl AC_DEFUN([AC_LIBTOOL_RC], [])
+
+
+# _LT_TAG_COMPILER
+# ----------------
+m4_defun([_LT_TAG_COMPILER],
+[AC_REQUIRE([AC_PROG_CC])dnl
+
+_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl
+_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl
+_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl
+_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+])# _LT_TAG_COMPILER
+
+
+# _LT_COMPILER_BOILERPLATE
+# ------------------------
+# Check for compiler boilerplate output or warnings with
+# the simple compiler test code.
+m4_defun([_LT_COMPILER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+])# _LT_COMPILER_BOILERPLATE
+
+
+# _LT_LINKER_BOILERPLATE
+# ----------------------
+# Check for linker boilerplate output or warnings with
+# the simple link test code.
+m4_defun([_LT_LINKER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+])# _LT_LINKER_BOILERPLATE
+
+# _LT_REQUIRED_DARWIN_CHECKS
+# -------------------------
+m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[
+ case $host_os in
+ rhapsody* | darwin*)
+ AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:])
+ AC_CHECK_TOOL([NMEDIT], [nmedit], [:])
+ AC_CHECK_TOOL([LIPO], [lipo], [:])
+ AC_CHECK_TOOL([OTOOL], [otool], [:])
+ AC_CHECK_TOOL([OTOOL64], [otool64], [:])
+ _LT_DECL([], [DSYMUTIL], [1],
+ [Tool to manipulate archived DWARF debug symbol files on Mac OS X])
+ _LT_DECL([], [NMEDIT], [1],
+ [Tool to change global to local symbols on Mac OS X])
+ _LT_DECL([], [LIPO], [1],
+ [Tool to manipulate fat objects and archives on Mac OS X])
+ _LT_DECL([], [OTOOL], [1],
+ [ldd/readelf like tool for Mach-O binaries on Mac OS X])
+ _LT_DECL([], [OTOOL64], [1],
+ [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4])
+
+ AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod],
+ [lt_cv_apple_cc_single_mod=no
+ if test -z "$LT_MULTI_MODULE"; then
+ # By default we will add the -single_module flag. You can override
+ # by either setting the environment variable LT_MULTI_MODULE
+ # non-empty at configure time, or by adding -multi_module to the
+ # link flags.
+ rm -rf libconftest.dylib*
+ echo "int foo(void){return 1;}" > conftest.c
+ echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD
+ $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+ _lt_result=$?
+ # If there is a non-empty error log, and "single_module"
+ # appears in it, assume the flag caused a linker warning
+ if test -s conftest.err && $GREP single_module conftest.err; then
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ # Otherwise, if the output was created with a 0 exit code from
+ # the compiler, it worked.
+ elif test -f libconftest.dylib && test 0 = "$_lt_result"; then
+ lt_cv_apple_cc_single_mod=yes
+ else
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ fi
+ rm -rf libconftest.dylib*
+ rm -f conftest.*
+ fi])
+
+ AC_CACHE_CHECK([for -exported_symbols_list linker flag],
+ [lt_cv_ld_exported_symbols_list],
+ [lt_cv_ld_exported_symbols_list=no
+ save_LDFLAGS=$LDFLAGS
+ echo "_main" > conftest.sym
+ LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+ [lt_cv_ld_exported_symbols_list=yes],
+ [lt_cv_ld_exported_symbols_list=no])
+ LDFLAGS=$save_LDFLAGS
+ ])
+
+ AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load],
+ [lt_cv_ld_force_load=no
+ cat > conftest.c << _LT_EOF
+int forced_loaded() { return 2;}
+_LT_EOF
+ echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD
+ $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD
+ echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD
+ $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD
+ echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD
+ $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD
+ cat > conftest.c << _LT_EOF
+int main() { return 0;}
+_LT_EOF
+ echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD
+ $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err
+ _lt_result=$?
+ if test -s conftest.err && $GREP force_load conftest.err; then
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then
+ lt_cv_ld_force_load=yes
+ else
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ fi
+ rm -f conftest.err libconftest.a conftest conftest.c
+ rm -rf conftest.dSYM
+ ])
+ case $host_os in
+ rhapsody* | darwin1.[[012]])
+ _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;;
+ darwin1.*)
+ _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
+ darwin*) # darwin 5.x on
+ # if running on 10.5 or later, the deployment target defaults
+ # to the OS version, if on x86, and 10.4, the deployment
+ # target defaults to 10.4. Don't you love it?
+ case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+ 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*)
+ _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
+ 10.[[012]][[,.]]*)
+ _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
+ 10.*)
+ _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
+ esac
+ ;;
+ esac
+ if test yes = "$lt_cv_apple_cc_single_mod"; then
+ _lt_dar_single_mod='$single_module'
+ fi
+ if test yes = "$lt_cv_ld_exported_symbols_list"; then
+ _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym'
+ else
+ _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib'
+ fi
+ if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then
+ _lt_dsymutil='~$DSYMUTIL $lib || :'
+ else
+ _lt_dsymutil=
+ fi
+ ;;
+ esac
+])
+
+
+# _LT_DARWIN_LINKER_FEATURES([TAG])
+# ---------------------------------
+# Checks for linker and compiler features on darwin
+m4_defun([_LT_DARWIN_LINKER_FEATURES],
+[
+ m4_require([_LT_REQUIRED_DARWIN_CHECKS])
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_automatic, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+ if test yes = "$lt_cv_ld_force_load"; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
+ m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes],
+ [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes])
+ else
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=''
+ fi
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(allow_undefined_flag, $1)=$_lt_dar_allow_undefined
+ case $cc_basename in
+ ifort*|nagfor*) _lt_dar_can_shared=yes ;;
+ *) _lt_dar_can_shared=$GCC ;;
+ esac
+ if test yes = "$_lt_dar_can_shared"; then
+ output_verbose_link_cmd=func_echo_all
+ _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil"
+ _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil"
+ _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil"
+ m4_if([$1], [CXX],
+[ if test yes != "$lt_cv_apple_cc_single_mod"; then
+ _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil"
+ fi
+],[])
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+])
+
+# _LT_SYS_MODULE_PATH_AIX([TAGNAME])
+# ----------------------------------
+# Links a minimal program and checks the executable
+# for the system default hardcoded library path. In most cases,
+# this is /usr/lib:/lib, but when the MPI compilers are used
+# the location of the communication and MPI libs are included too.
+# If we don't find anything, use the default library path according
+# to the aix ld manual.
+# Store the results from the different compilers for each TAGNAME.
+# Allow to override them for all tags through lt_cv_aix_libpath.
+m4_defun([_LT_SYS_MODULE_PATH_AIX],
+[m4_require([_LT_DECL_SED])dnl
+if test set = "${lt_cv_aix_libpath+set}"; then
+ aix_libpath=$lt_cv_aix_libpath
+else
+ AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])],
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM],[
+ lt_aix_libpath_sed='[
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\([^ ]*\) *$/\1/
+ p
+ }
+ }]'
+ _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ # Check for a 64-bit object if we didn't find anything.
+ if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then
+ _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ fi],[])
+ if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then
+ _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=/usr/lib:/lib
+ fi
+ ])
+ aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])
+fi
+])# _LT_SYS_MODULE_PATH_AIX
+
+
+# _LT_SHELL_INIT(ARG)
+# -------------------
+m4_define([_LT_SHELL_INIT],
+[m4_divert_text([M4SH-INIT], [$1
+])])# _LT_SHELL_INIT
+
+
+
+# _LT_PROG_ECHO_BACKSLASH
+# -----------------------
+# Find how we can fake an echo command that does not interpret backslash.
+# In particular, with Autoconf 2.60 or later we add some code to the start
+# of the generated configure script that will find a shell with a builtin
+# printf (that we can use as an echo command).
+m4_defun([_LT_PROG_ECHO_BACKSLASH],
+[ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
+
+AC_MSG_CHECKING([how to print strings])
+# Test print first, because it will be a builtin if present.
+if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \
+ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then
+ ECHO='print -r --'
+elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then
+ ECHO='printf %s\n'
+else
+ # Use this function as a fallback that always works.
+ func_fallback_echo ()
+ {
+ eval 'cat <<_LTECHO_EOF
+$[]1
+_LTECHO_EOF'
+ }
+ ECHO='func_fallback_echo'
+fi
+
+# func_echo_all arg...
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+ $ECHO "$*"
+}
+
+case $ECHO in
+ printf*) AC_MSG_RESULT([printf]) ;;
+ print*) AC_MSG_RESULT([print -r]) ;;
+ *) AC_MSG_RESULT([cat]) ;;
+esac
+
+m4_ifdef([_AS_DETECT_SUGGESTED],
+[_AS_DETECT_SUGGESTED([
+ test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || (
+ ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+ ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+ ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
+ PATH=/empty FPATH=/empty; export PATH FPATH
+ test "X`printf %s $ECHO`" = "X$ECHO" \
+ || test "X`print -r -- $ECHO`" = "X$ECHO" )])])
+
+_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts])
+_LT_DECL([], [ECHO], [1], [An echo program that protects backslashes])
+])# _LT_PROG_ECHO_BACKSLASH
+
+
+# _LT_WITH_SYSROOT
+# ----------------
+AC_DEFUN([_LT_WITH_SYSROOT],
+[AC_MSG_CHECKING([for sysroot])
+AC_ARG_WITH([sysroot],
+[AS_HELP_STRING([--with-sysroot@<:@=DIR@:>@],
+ [Search for dependent libraries within DIR (or the compiler's sysroot
+ if not specified).])],
+[], [with_sysroot=no])
+
+dnl lt_sysroot will always be passed unquoted. We quote it here
+dnl in case the user passed a directory name.
+lt_sysroot=
+case $with_sysroot in #(
+ yes)
+ if test yes = "$GCC"; then
+ lt_sysroot=`$CC --print-sysroot 2>/dev/null`
+ fi
+ ;; #(
+ /*)
+ lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"`
+ ;; #(
+ no|'')
+ ;; #(
+ *)
+ AC_MSG_RESULT([$with_sysroot])
+ AC_MSG_ERROR([The sysroot must be an absolute path.])
+ ;;
+esac
+
+ AC_MSG_RESULT([${lt_sysroot:-no}])
+_LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl
+[dependent libraries, and where our libraries should be installed.])])
+
+# _LT_ENABLE_LOCK
+# ---------------
+m4_defun([_LT_ENABLE_LOCK],
+[AC_ARG_ENABLE([libtool-lock],
+ [AS_HELP_STRING([--disable-libtool-lock],
+ [avoid locking (might break parallel builds)])])
+test no = "$enable_libtool_lock" || enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+ # Find out what ABI is being produced by ac_compile, and set mode
+ # options accordingly.
+ echo 'int i;' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *ELF-32*)
+ HPUX_IA64_MODE=32
+ ;;
+ *ELF-64*)
+ HPUX_IA64_MODE=64
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+*-*-irix6*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
+ echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ if test yes = "$lt_cv_prog_gnu_ld"; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -melf32bsmip"
+ ;;
+ *N32*)
+ LD="${LD-ld} -melf32bmipn32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -melf64bmip"
+ ;;
+ esac
+ else
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -32"
+ ;;
+ *N32*)
+ LD="${LD-ld} -n32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -64"
+ ;;
+ esac
+ fi
+ fi
+ rm -rf conftest*
+ ;;
+
+mips64*-*linux*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
+ echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ emul=elf
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ emul="${emul}32"
+ ;;
+ *64-bit*)
+ emul="${emul}64"
+ ;;
+ esac
+ case `/usr/bin/file conftest.$ac_objext` in
+ *MSB*)
+ emul="${emul}btsmip"
+ ;;
+ *LSB*)
+ emul="${emul}ltsmip"
+ ;;
+ esac
+ case `/usr/bin/file conftest.$ac_objext` in
+ *N32*)
+ emul="${emul}n32"
+ ;;
+ esac
+ LD="${LD-ld} -m $emul"
+ fi
+ rm -rf conftest*
+ ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly. Note that the listed cases only cover the
+ # situations where additional linker options are needed (such as when
+ # doing 32-bit compilation for a host where ld defaults to 64-bit, or
+ # vice versa); the common cases where no linker options are needed do
+ # not appear in the list.
+ echo 'int i;' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ case `/usr/bin/file conftest.o` in
+ *32-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_i386_fbsd"
+ ;;
+ x86_64-*linux*)
+ case `/usr/bin/file conftest.o` in
+ *x86-64*)
+ LD="${LD-ld} -m elf32_x86_64"
+ ;;
+ *)
+ LD="${LD-ld} -m elf_i386"
+ ;;
+ esac
+ ;;
+ powerpc64le-*linux*)
+ LD="${LD-ld} -m elf32lppclinux"
+ ;;
+ powerpc64-*linux*)
+ LD="${LD-ld} -m elf32ppclinux"
+ ;;
+ s390x-*linux*)
+ LD="${LD-ld} -m elf_s390"
+ ;;
+ sparc64-*linux*)
+ LD="${LD-ld} -m elf32_sparc"
+ ;;
+ esac
+ ;;
+ *64-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_x86_64_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ powerpcle-*linux*)
+ LD="${LD-ld} -m elf64lppc"
+ ;;
+ powerpc-*linux*)
+ LD="${LD-ld} -m elf64ppc"
+ ;;
+ s390*-*linux*|s390*-*tpf*)
+ LD="${LD-ld} -m elf64_s390"
+ ;;
+ sparc*-*linux*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+
+*-*-sco3.2v5*)
+ # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+ SAVE_CFLAGS=$CFLAGS
+ CFLAGS="$CFLAGS -belf"
+ AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf,
+ [AC_LANG_PUSH(C)
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no])
+ AC_LANG_POP])
+ if test yes != "$lt_cv_cc_needs_belf"; then
+ # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+ CFLAGS=$SAVE_CFLAGS
+ fi
+ ;;
+*-*solaris*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
+ echo 'int i;' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ case `/usr/bin/file conftest.o` in
+ *64-bit*)
+ case $lt_cv_prog_gnu_ld in
+ yes*)
+ case $host in
+ i?86-*-solaris*|x86_64-*-solaris*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ sparc*-*-solaris*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ # GNU ld 2.21 introduced _sol2 emulations. Use them if available.
+ if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then
+ LD=${LD-ld}_sol2
+ fi
+ ;;
+ *)
+ if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+ LD="${LD-ld} -64"
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+esac
+
+need_locks=$enable_libtool_lock
+])# _LT_ENABLE_LOCK
+
+
+# _LT_PROG_AR
+# -----------
+m4_defun([_LT_PROG_AR],
+[AC_CHECK_TOOLS(AR, [ar], false)
+: ${AR=ar}
+: ${AR_FLAGS=cru}
+_LT_DECL([], [AR], [1], [The archiver])
+_LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive])
+
+AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file],
+ [lt_cv_ar_at_file=no
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM],
+ [echo conftest.$ac_objext > conftest.lst
+ lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD'
+ AC_TRY_EVAL([lt_ar_try])
+ if test 0 -eq "$ac_status"; then
+ # Ensure the archiver fails upon bogus file names.
+ rm -f conftest.$ac_objext libconftest.a
+ AC_TRY_EVAL([lt_ar_try])
+ if test 0 -ne "$ac_status"; then
+ lt_cv_ar_at_file=@
+ fi
+ fi
+ rm -f conftest.* libconftest.a
+ ])
+ ])
+
+if test no = "$lt_cv_ar_at_file"; then
+ archiver_list_spec=
+else
+ archiver_list_spec=$lt_cv_ar_at_file
+fi
+_LT_DECL([], [archiver_list_spec], [1],
+ [How to feed a file listing to the archiver])
+])# _LT_PROG_AR
+
+
+# _LT_CMD_OLD_ARCHIVE
+# -------------------
+m4_defun([_LT_CMD_OLD_ARCHIVE],
+[_LT_PROG_AR
+
+AC_CHECK_TOOL(STRIP, strip, :)
+test -z "$STRIP" && STRIP=:
+_LT_DECL([], [STRIP], [1], [A symbol stripping program])
+
+AC_CHECK_TOOL(RANLIB, ranlib, :)
+test -z "$RANLIB" && RANLIB=:
+_LT_DECL([], [RANLIB], [1],
+ [Commands used to install an old-style archive])
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+ case $host_os in
+ bitrig* | openbsd*)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib"
+ ;;
+ *)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib"
+ ;;
+ esac
+ old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib"
+fi
+
+case $host_os in
+ darwin*)
+ lock_old_archive_extraction=yes ;;
+ *)
+ lock_old_archive_extraction=no ;;
+esac
+_LT_DECL([], [old_postinstall_cmds], [2])
+_LT_DECL([], [old_postuninstall_cmds], [2])
+_LT_TAGDECL([], [old_archive_cmds], [2],
+ [Commands used to build an old-style archive])
+_LT_DECL([], [lock_old_archive_extraction], [0],
+ [Whether to use a lock for old archive extraction])
+])# _LT_CMD_OLD_ARCHIVE
+
+
+# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------------------
+# Check whether the given compiler option works
+AC_DEFUN([_LT_COMPILER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+ [$2=no
+ m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4])
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="$3" ## exclude from sc_useless_quotes_in_assignment
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ $2=yes
+ fi
+ fi
+ $RM conftest*
+])
+
+if test yes = "[$]$2"; then
+ m4_if([$5], , :, [$5])
+else
+ m4_if([$6], , :, [$6])
+fi
+])# _LT_COMPILER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], [])
+
+
+# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+# [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------
+# Check whether the given linker option works
+AC_DEFUN([_LT_LINKER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+ [$2=no
+ save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS $3"
+ echo "$lt_simple_link_test_code" > conftest.$ac_ext
+ if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+ # The linker can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ # Append any errors to the config.log.
+ cat conftest.err 1>&AS_MESSAGE_LOG_FD
+ $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if diff conftest.exp conftest.er2 >/dev/null; then
+ $2=yes
+ fi
+ else
+ $2=yes
+ fi
+ fi
+ $RM -r conftest*
+ LDFLAGS=$save_LDFLAGS
+])
+
+if test yes = "[$]$2"; then
+ m4_if([$4], , :, [$4])
+else
+ m4_if([$5], , :, [$5])
+fi
+])# _LT_LINKER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], [])
+
+
+# LT_CMD_MAX_LEN
+#---------------
+AC_DEFUN([LT_CMD_MAX_LEN],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+# find the maximum length of command line arguments
+AC_MSG_CHECKING([the maximum length of command line arguments])
+AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
+ i=0
+ teststring=ABCD
+
+ case $build_os in
+ msdosdjgpp*)
+ # On DJGPP, this test can blow up pretty badly due to problems in libc
+ # (any single argument exceeding 2000 bytes causes a buffer overrun
+ # during glob expansion). Even if it were fixed, the result of this
+ # check would be larger than it should be.
+ lt_cv_sys_max_cmd_len=12288; # 12K is about right
+ ;;
+
+ gnu*)
+ # Under GNU Hurd, this test is not required because there is
+ # no limit to the length of command line arguments.
+ # Libtool will interpret -1 as no limit whatsoever
+ lt_cv_sys_max_cmd_len=-1;
+ ;;
+
+ cygwin* | mingw* | cegcc*)
+ # On Win9x/ME, this test blows up -- it succeeds, but takes
+ # about 5 minutes as the teststring grows exponentially.
+ # Worse, since 9x/ME are not pre-emptively multitasking,
+ # you end up with a "frozen" computer, even though with patience
+ # the test eventually succeeds (with a max line length of 256k).
+ # Instead, let's just punt: use the minimum linelength reported by
+ # all of the supported platforms: 8192 (on NT/2K/XP).
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ mint*)
+ # On MiNT this can take a long time and run out of memory.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ amigaos*)
+ # On AmigaOS with pdksh, this test takes hours, literally.
+ # So we just punt and use a minimum line length of 8192.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*)
+ # This has been around since 386BSD, at least. Likely further.
+ if test -x /sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+ elif test -x /usr/sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+ else
+ lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs
+ fi
+ # And add a safety zone
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ ;;
+
+ interix*)
+ # We know the value 262144 and hardcode it with a safety zone (like BSD)
+ lt_cv_sys_max_cmd_len=196608
+ ;;
+
+ os2*)
+ # The test takes a long time on OS/2.
+ lt_cv_sys_max_cmd_len=8192
+ ;;
+
+ osf*)
+ # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+ # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+ # nice to cause kernel panics so lets avoid the loop below.
+ # First set a reasonable default.
+ lt_cv_sys_max_cmd_len=16384
+ #
+ if test -x /sbin/sysconfig; then
+ case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+ *1*) lt_cv_sys_max_cmd_len=-1 ;;
+ esac
+ fi
+ ;;
+ sco3.2v5*)
+ lt_cv_sys_max_cmd_len=102400
+ ;;
+ sysv5* | sco5v6* | sysv4.2uw2*)
+ kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+ if test -n "$kargmax"; then
+ lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'`
+ else
+ lt_cv_sys_max_cmd_len=32768
+ fi
+ ;;
+ *)
+ lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+ if test -n "$lt_cv_sys_max_cmd_len" && \
+ test undefined != "$lt_cv_sys_max_cmd_len"; then
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ else
+ # Make teststring a little bigger before we do anything with it.
+ # a 1K string should be a reasonable start.
+ for i in 1 2 3 4 5 6 7 8; do
+ teststring=$teststring$teststring
+ done
+ SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+ # If test is not a shell built-in, we'll probably end up computing a
+ # maximum length that is only half of the actual maximum length, but
+ # we can't tell.
+ while { test X`env echo "$teststring$teststring" 2>/dev/null` \
+ = "X$teststring$teststring"; } >/dev/null 2>&1 &&
+ test 17 != "$i" # 1/2 MB should be enough
+ do
+ i=`expr $i + 1`
+ teststring=$teststring$teststring
+ done
+ # Only check the string length outside the loop.
+ lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+ teststring=
+ # Add a significant safety factor because C++ compilers can tack on
+ # massive amounts of additional arguments before passing them to the
+ # linker. It appears as though 1/2 is a usable value.
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+ fi
+ ;;
+ esac
+])
+if test -n "$lt_cv_sys_max_cmd_len"; then
+ AC_MSG_RESULT($lt_cv_sys_max_cmd_len)
+else
+ AC_MSG_RESULT(none)
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+_LT_DECL([], [max_cmd_len], [0],
+ [What is the maximum length of a command?])
+])# LT_CMD_MAX_LEN
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], [])
+
+
+# _LT_HEADER_DLFCN
+# ----------------
+m4_defun([_LT_HEADER_DLFCN],
+[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl
+])# _LT_HEADER_DLFCN
+
+
+# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE,
+# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING)
+# ----------------------------------------------------------------
+m4_defun([_LT_TRY_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test yes = "$cross_compiling"; then :
+ [$4]
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+[#line $LINENO "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+/* When -fvisibility=hidden is used, assume the code has been annotated
+ correspondingly for the symbols needed. */
+#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+int fnord () __attribute__((visibility("default")));
+#endif
+
+int fnord () { return 42; }
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else
+ {
+ if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ else puts (dlerror ());
+ }
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ return status;
+}]
+_LT_EOF
+ if AC_TRY_EVAL(ac_link) && test -s "conftest$ac_exeext" 2>/dev/null; then
+ (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) $1 ;;
+ x$lt_dlneed_uscore) $2 ;;
+ x$lt_dlunknown|x*) $3 ;;
+ esac
+ else :
+ # compilation failed
+ $3
+ fi
+fi
+rm -fr conftest*
+])# _LT_TRY_DLOPEN_SELF
+
+
+# LT_SYS_DLOPEN_SELF
+# ------------------
+AC_DEFUN([LT_SYS_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test yes != "$enable_dlopen"; then
+ enable_dlopen=unknown
+ enable_dlopen_self=unknown
+ enable_dlopen_self_static=unknown
+else
+ lt_cv_dlopen=no
+ lt_cv_dlopen_libs=
+
+ case $host_os in
+ beos*)
+ lt_cv_dlopen=load_add_on
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ;;
+
+ mingw* | pw32* | cegcc*)
+ lt_cv_dlopen=LoadLibrary
+ lt_cv_dlopen_libs=
+ ;;
+
+ cygwin*)
+ lt_cv_dlopen=dlopen
+ lt_cv_dlopen_libs=
+ ;;
+
+ darwin*)
+ # if libdl is installed we need to link against it
+ AC_CHECK_LIB([dl], [dlopen],
+ [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],[
+ lt_cv_dlopen=dyld
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ])
+ ;;
+
+ tpf*)
+ # Don't try to run any link tests for TPF. We know it's impossible
+ # because TPF is a cross-compiler, and we know how we open DSOs.
+ lt_cv_dlopen=dlopen
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=no
+ ;;
+
+ *)
+ AC_CHECK_FUNC([shl_load],
+ [lt_cv_dlopen=shl_load],
+ [AC_CHECK_LIB([dld], [shl_load],
+ [lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld],
+ [AC_CHECK_FUNC([dlopen],
+ [lt_cv_dlopen=dlopen],
+ [AC_CHECK_LIB([dl], [dlopen],
+ [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],
+ [AC_CHECK_LIB([svld], [dlopen],
+ [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld],
+ [AC_CHECK_LIB([dld], [dld_link],
+ [lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld])
+ ])
+ ])
+ ])
+ ])
+ ])
+ ;;
+ esac
+
+ if test no = "$lt_cv_dlopen"; then
+ enable_dlopen=no
+ else
+ enable_dlopen=yes
+ fi
+
+ case $lt_cv_dlopen in
+ dlopen)
+ save_CPPFLAGS=$CPPFLAGS
+ test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+ save_LDFLAGS=$LDFLAGS
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+ save_LIBS=$LIBS
+ LIBS="$lt_cv_dlopen_libs $LIBS"
+
+ AC_CACHE_CHECK([whether a program can dlopen itself],
+ lt_cv_dlopen_self, [dnl
+ _LT_TRY_DLOPEN_SELF(
+ lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes,
+ lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross)
+ ])
+
+ if test yes = "$lt_cv_dlopen_self"; then
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+ AC_CACHE_CHECK([whether a statically linked program can dlopen itself],
+ lt_cv_dlopen_self_static, [dnl
+ _LT_TRY_DLOPEN_SELF(
+ lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes,
+ lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross)
+ ])
+ fi
+
+ CPPFLAGS=$save_CPPFLAGS
+ LDFLAGS=$save_LDFLAGS
+ LIBS=$save_LIBS
+ ;;
+ esac
+
+ case $lt_cv_dlopen_self in
+ yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+ *) enable_dlopen_self=unknown ;;
+ esac
+
+ case $lt_cv_dlopen_self_static in
+ yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+ *) enable_dlopen_self_static=unknown ;;
+ esac
+fi
+_LT_DECL([dlopen_support], [enable_dlopen], [0],
+ [Whether dlopen is supported])
+_LT_DECL([dlopen_self], [enable_dlopen_self], [0],
+ [Whether dlopen of programs is supported])
+_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0],
+ [Whether dlopen of statically linked programs is supported])
+])# LT_SYS_DLOPEN_SELF
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], [])
+
+
+# _LT_COMPILER_C_O([TAGNAME])
+# ---------------------------
+# Check to see if options -c and -o are simultaneously supported by compiler.
+# This macro does not hard code the compiler like AC_PROG_CC_C_O.
+m4_defun([_LT_COMPILER_C_O],
+[m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext],
+ [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)],
+ [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&AS_MESSAGE_LOG_FD
+ echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+ fi
+ fi
+ chmod u+w . 2>&AS_MESSAGE_LOG_FD
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+])
+_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1],
+ [Does compiler simultaneously support -c and -o options?])
+])# _LT_COMPILER_C_O
+
+
+# _LT_COMPILER_FILE_LOCKS([TAGNAME])
+# ----------------------------------
+# Check to see if we can do hard links to lock some files if needed
+m4_defun([_LT_COMPILER_FILE_LOCKS],
+[m4_require([_LT_ENABLE_LOCK])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_COMPILER_C_O([$1])
+
+hard_links=nottested
+if test no = "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" && test no != "$need_locks"; then
+ # do not overwrite the value of need_locks provided by the user
+ AC_MSG_CHECKING([if we can lock with hard links])
+ hard_links=yes
+ $RM conftest*
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ touch conftest.a
+ ln conftest.a conftest.b 2>&5 || hard_links=no
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ AC_MSG_RESULT([$hard_links])
+ if test no = "$hard_links"; then
+ AC_MSG_WARN(['$CC' does not support '-c -o', so 'make -j' may be unsafe])
+ need_locks=warn
+ fi
+else
+ need_locks=no
+fi
+_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?])
+])# _LT_COMPILER_FILE_LOCKS
+
+
+# _LT_CHECK_OBJDIR
+# ----------------
+m4_defun([_LT_CHECK_OBJDIR],
+[AC_CACHE_CHECK([for objdir], [lt_cv_objdir],
+[rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+ lt_cv_objdir=.libs
+else
+ # MS-DOS does not allow filenames that begin with a dot.
+ lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null])
+objdir=$lt_cv_objdir
+_LT_DECL([], [objdir], [0],
+ [The name of the directory that contains temporary libtool files])dnl
+m4_pattern_allow([LT_OBJDIR])dnl
+AC_DEFINE_UNQUOTED([LT_OBJDIR], "$lt_cv_objdir/",
+ [Define to the sub-directory where libtool stores uninstalled libraries.])
+])# _LT_CHECK_OBJDIR
+
+
+# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME])
+# --------------------------------------
+# Check hardcoding attributes.
+m4_defun([_LT_LINKER_HARDCODE_LIBPATH],
+[AC_MSG_CHECKING([how to hardcode library paths into programs])
+_LT_TAGVAR(hardcode_action, $1)=
+if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" ||
+ test -n "$_LT_TAGVAR(runpath_var, $1)" ||
+ test yes = "$_LT_TAGVAR(hardcode_automatic, $1)"; then
+
+ # We can hardcode non-existent directories.
+ if test no != "$_LT_TAGVAR(hardcode_direct, $1)" &&
+ # If the only mechanism to avoid hardcoding is shlibpath_var, we
+ # have to relink, otherwise we might link with an installed library
+ # when we should be linking with a yet-to-be-installed one
+ ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" &&
+ test no != "$_LT_TAGVAR(hardcode_minus_L, $1)"; then
+ # Linking always hardcodes the temporary library directory.
+ _LT_TAGVAR(hardcode_action, $1)=relink
+ else
+ # We can link without hardcoding, and we can hardcode nonexisting dirs.
+ _LT_TAGVAR(hardcode_action, $1)=immediate
+ fi
+else
+ # We cannot hardcode anything, or else we can only hardcode existing
+ # directories.
+ _LT_TAGVAR(hardcode_action, $1)=unsupported
+fi
+AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)])
+
+if test relink = "$_LT_TAGVAR(hardcode_action, $1)" ||
+ test yes = "$_LT_TAGVAR(inherit_rpath, $1)"; then
+ # Fast installation is not supported
+ enable_fast_install=no
+elif test yes = "$shlibpath_overrides_runpath" ||
+ test no = "$enable_shared"; then
+ # Fast installation is not necessary
+ enable_fast_install=needless
+fi
+_LT_TAGDECL([], [hardcode_action], [0],
+ [How to hardcode a shared library path into an executable])
+])# _LT_LINKER_HARDCODE_LIBPATH
+
+
+# _LT_CMD_STRIPLIB
+# ----------------
+m4_defun([_LT_CMD_STRIPLIB],
+[m4_require([_LT_DECL_EGREP])
+striplib=
+old_striplib=
+AC_MSG_CHECKING([whether stripping libraries is possible])
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+ test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+ test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+ AC_MSG_RESULT([yes])
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+ case $host_os in
+ darwin*)
+ if test -n "$STRIP"; then
+ striplib="$STRIP -x"
+ old_striplib="$STRIP -S"
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ fi
+ ;;
+ *)
+ AC_MSG_RESULT([no])
+ ;;
+ esac
+fi
+_LT_DECL([], [old_striplib], [1], [Commands to strip libraries])
+_LT_DECL([], [striplib], [1])
+])# _LT_CMD_STRIPLIB
+
+
+# _LT_PREPARE_MUNGE_PATH_LIST
+# ---------------------------
+# Make sure func_munge_path_list() is defined correctly.
+m4_defun([_LT_PREPARE_MUNGE_PATH_LIST],
+[[# func_munge_path_list VARIABLE PATH
+# -----------------------------------
+# VARIABLE is name of variable containing _space_ separated list of
+# directories to be munged by the contents of PATH, which is string
+# having a format:
+# "DIR[:DIR]:"
+# string "DIR[ DIR]" will be prepended to VARIABLE
+# ":DIR[:DIR]"
+# string "DIR[ DIR]" will be appended to VARIABLE
+# "DIRP[:DIRP]::[DIRA:]DIRA"
+# string "DIRP[ DIRP]" will be prepended to VARIABLE and string
+# "DIRA[ DIRA]" will be appended to VARIABLE
+# "DIR[:DIR]"
+# VARIABLE will be replaced by "DIR[ DIR]"
+func_munge_path_list ()
+{
+ case x@S|@2 in
+ x)
+ ;;
+ *:)
+ eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'` \@S|@@S|@1\"
+ ;;
+ x:*)
+ eval @S|@1=\"\@S|@@S|@1 `$ECHO @S|@2 | $SED 's/:/ /g'`\"
+ ;;
+ *::*)
+ eval @S|@1=\"\@S|@@S|@1\ `$ECHO @S|@2 | $SED -e 's/.*:://' -e 's/:/ /g'`\"
+ eval @S|@1=\"`$ECHO @S|@2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \@S|@@S|@1\"
+ ;;
+ *)
+ eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'`\"
+ ;;
+ esac
+}
+]])# _LT_PREPARE_PATH_LIST
+
+
+# _LT_SYS_DYNAMIC_LINKER([TAG])
+# -----------------------------
+# PORTME Fill in your ld.so characteristics
+m4_defun([_LT_SYS_DYNAMIC_LINKER],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_OBJDUMP])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_CHECK_SHELL_FEATURES])dnl
+m4_require([_LT_PREPARE_MUNGE_PATH_LIST])dnl
+AC_MSG_CHECKING([dynamic linker characteristics])
+m4_if([$1],
+ [], [
+if test yes = "$GCC"; then
+ case $host_os in
+ darwin*) lt_awk_arg='/^libraries:/,/LR/' ;;
+ *) lt_awk_arg='/^libraries:/' ;;
+ esac
+ case $host_os in
+ mingw* | cegcc*) lt_sed_strip_eq='s|=\([[A-Za-z]]:\)|\1|g' ;;
+ *) lt_sed_strip_eq='s|=/|/|g' ;;
+ esac
+ lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq`
+ case $lt_search_path_spec in
+ *\;*)
+ # if the path contains ";" then we assume it to be the separator
+ # otherwise default to the standard path separator (i.e. ":") - it is
+ # assumed that no part of a normal pathname contains ";" but that should
+ # okay in the real world where ";" in dirpaths is itself problematic.
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'`
+ ;;
+ *)
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"`
+ ;;
+ esac
+ # Ok, now we have the path, separated by spaces, we can step through it
+ # and add multilib dir if necessary...
+ lt_tmp_lt_search_path_spec=
+ lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+ # ...but if some path component already ends with the multilib dir we assume
+ # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer).
+ case "$lt_multi_os_dir; $lt_search_path_spec " in
+ "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*)
+ lt_multi_os_dir=
+ ;;
+ esac
+ for lt_sys_path in $lt_search_path_spec; do
+ if test -d "$lt_sys_path$lt_multi_os_dir"; then
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir"
+ elif test -n "$lt_multi_os_dir"; then
+ test -d "$lt_sys_path" && \
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+ fi
+ done
+ lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk '
+BEGIN {RS = " "; FS = "/|\n";} {
+ lt_foo = "";
+ lt_count = 0;
+ for (lt_i = NF; lt_i > 0; lt_i--) {
+ if ($lt_i != "" && $lt_i != ".") {
+ if ($lt_i == "..") {
+ lt_count++;
+ } else {
+ if (lt_count == 0) {
+ lt_foo = "/" $lt_i lt_foo;
+ } else {
+ lt_count--;
+ }
+ }
+ }
+ }
+ if (lt_foo != "") { lt_freq[[lt_foo]]++; }
+ if (lt_freq[[lt_foo]] == 1) { print lt_foo; }
+}'`
+ # AWK program above erroneously prepends '/' to C:/dos/paths
+ # for these hosts.
+ case $host_os in
+ mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\
+ $SED 's|/\([[A-Za-z]]:\)|\1|g'` ;;
+ esac
+ sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP`
+else
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi])
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=.so
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+AC_ARG_VAR([LT_SYS_LIBRARY_PATH],
+[User-defined run-time library search path.])
+
+case $host_os in
+aix3*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname.a'
+ shlibpath_var=LIBPATH
+
+ # AIX 3 has no versioning support, so we append a major version to the name.
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+
+aix[[4-9]]*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ hardcode_into_libs=yes
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 supports IA64
+ library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ else
+ # With GCC up to 2.95.x, collect2 would create an import file
+ # for dependence libraries. The import file would start with
+ # the line '#! .'. This would cause the generated library to
+ # depend on '.', always an invalid library. This was fixed in
+ # development snapshots of GCC prior to 3.0.
+ case $host_os in
+ aix4 | aix4.[[01]] | aix4.[[01]].*)
+ if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+ echo ' yes '
+ echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then
+ :
+ else
+ can_build_shared=no
+ fi
+ ;;
+ esac
+ # Using Import Files as archive members, it is possible to support
+ # filename-based versioning of shared library archives on AIX. While
+ # this would work for both with and without runtime linking, it will
+ # prevent static linking of such archives. So we do filename-based
+ # shared library versioning with .so extension only, which is used
+ # when both runtime linking and shared linking is enabled.
+ # Unfortunately, runtime linking may impact performance, so we do
+ # not want this to be the default eventually. Also, we use the
+ # versioned .so libs for executables only if there is the -brtl
+ # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only.
+ # To allow for filename-based versioning support, we need to create
+ # libNAME.so.V as an archive file, containing:
+ # *) an Import File, referring to the versioned filename of the
+ # archive as well as the shared archive member, telling the
+ # bitwidth (32 or 64) of that shared object, and providing the
+ # list of exported symbols of that shared object, eventually
+ # decorated with the 'weak' keyword
+ # *) the shared object with the F_LOADONLY flag set, to really avoid
+ # it being seen by the linker.
+ # At run time we better use the real file rather than another symlink,
+ # but for link time we create the symlink libNAME.so -> libNAME.so.V
+
+ case $with_aix_soname,$aix_use_runtimelinking in
+ # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct
+ # soname into executable. Probably we can add versioning support to
+ # collect2, so additional links can be useful in future.
+ aix,yes) # traditional libtool
+ dynamic_linker='AIX unversionable lib.so'
+ # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+ # instead of lib<name>.a to let people know that these are not
+ # typical AIX shared libraries.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ ;;
+ aix,no) # traditional AIX only
+ dynamic_linker='AIX lib.a[(]lib.so.V[)]'
+ # We preserve .a as extension for shared libraries through AIX4.2
+ # and later when we are not doing run time linking.
+ library_names_spec='$libname$release.a $libname.a'
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+ svr4,*) # full svr4 only
+ dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)]"
+ library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
+ # We do not specify a path in Import Files, so LIBPATH fires.
+ shlibpath_overrides_runpath=yes
+ ;;
+ *,yes) # both, prefer svr4
+ dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)], lib.a[(]lib.so.V[)]"
+ library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
+ # unpreferred sharedlib libNAME.a needs extra handling
+ postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"'
+ postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"'
+ # We do not specify a path in Import Files, so LIBPATH fires.
+ shlibpath_overrides_runpath=yes
+ ;;
+ *,no) # both, prefer aix
+ dynamic_linker="AIX lib.a[(]lib.so.V[)], lib.so.V[(]$shared_archive_member_spec.o[)]"
+ library_names_spec='$libname$release.a $libname.a'
+ soname_spec='$libname$release$shared_ext$major'
+ # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling
+ postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)'
+ postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"'
+ ;;
+ esac
+ shlibpath_var=LIBPATH
+ fi
+ ;;
+
+amigaos*)
+ case $host_cpu in
+ powerpc)
+ # Since July 2007 AmigaOS4 officially supports .so libraries.
+ # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ ;;
+ m68k)
+ library_names_spec='$libname.ixlibrary $libname.a'
+ # Create ${libname}_ixlibrary.a entries in /sys/libs.
+ finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+ ;;
+ esac
+ ;;
+
+beos*)
+ library_names_spec='$libname$shared_ext'
+ dynamic_linker="$host_os ld.so"
+ shlibpath_var=LIBRARY_PATH
+ ;;
+
+bsdi[[45]]*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+ # the default ld.so.conf also contains /usr/contrib/lib and
+ # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+ # libtool to hard-code these into programs
+ ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+ version_type=windows
+ shrext_cmds=.dll
+ need_version=no
+ need_lib_prefix=no
+
+ case $GCC,$cc_basename in
+ yes,*)
+ # gcc
+ library_names_spec='$libname.dll.a'
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+
+ case $host_os in
+ cygwin*)
+ # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+ soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext'
+m4_if([$1], [],[
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"])
+ ;;
+ mingw* | cegcc*)
+ # MinGW DLLs use traditional 'lib' prefix
+ soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext'
+ ;;
+ pw32*)
+ # pw32 DLLs use 'pw' prefix rather than 'lib'
+ library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext'
+ ;;
+ esac
+ dynamic_linker='Win32 ld.exe'
+ ;;
+
+ *,cl*)
+ # Native MSVC
+ libname_spec='$name'
+ soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext'
+ library_names_spec='$libname.dll.lib'
+
+ case $build_os in
+ mingw*)
+ sys_lib_search_path_spec=
+ lt_save_ifs=$IFS
+ IFS=';'
+ for lt_path in $LIB
+ do
+ IFS=$lt_save_ifs
+ # Let DOS variable expansion print the short 8.3 style file name.
+ lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"`
+ sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path"
+ done
+ IFS=$lt_save_ifs
+ # Convert to MSYS style.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'`
+ ;;
+ cygwin*)
+ # Convert to unix form, then to dos form, then back to unix form
+ # but this time dos style (no spaces!) so that the unix form looks
+ # like /cygdrive/c/PROGRA~1:/cygdr...
+ sys_lib_search_path_spec=`cygpath --path --unix "$LIB"`
+ sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null`
+ sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ ;;
+ *)
+ sys_lib_search_path_spec=$LIB
+ if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then
+ # It is most probably a Windows format PATH.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ # FIXME: find the short name or the path components, as spaces are
+ # common. (e.g. "Program Files" -> "PROGRA~1")
+ ;;
+ esac
+
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+ dynamic_linker='Win32 link.exe'
+ ;;
+
+ *)
+ # Assume MSVC wrapper
+ library_names_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext $libname.lib'
+ dynamic_linker='Win32 ld.exe'
+ ;;
+ esac
+ # FIXME: first we should search . and the directory the executable is in
+ shlibpath_var=PATH
+ ;;
+
+darwin* | rhapsody*)
+ dynamic_linker="$host_os dyld"
+ version_type=darwin
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$major$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$major$shared_ext'
+ shlibpath_overrides_runpath=yes
+ shlibpath_var=DYLD_LIBRARY_PATH
+ shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+m4_if([$1], [],[
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"])
+ sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+ ;;
+
+dgux*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+freebsd* | dragonfly*)
+ # DragonFly does not have aout. When/if they implement a new
+ # versioning mechanism, adjust this.
+ if test -x /usr/bin/objformat; then
+ objformat=`/usr/bin/objformat`
+ else
+ case $host_os in
+ freebsd[[23]].*) objformat=aout ;;
+ *) objformat=elf ;;
+ esac
+ fi
+ version_type=freebsd-$objformat
+ case $version_type in
+ freebsd-elf*)
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ need_version=no
+ need_lib_prefix=no
+ ;;
+ freebsd-*)
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ need_version=yes
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_os in
+ freebsd2.*)
+ shlibpath_overrides_runpath=yes
+ ;;
+ freebsd3.[[01]]* | freebsdelf3.[[01]]*)
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \
+ freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1)
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+ *) # from 4.6 on, and DragonFly
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ esac
+ ;;
+
+haiku*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ dynamic_linker="$host_os runtime_loader"
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'
+ hardcode_into_libs=yes
+ ;;
+
+hpux9* | hpux10* | hpux11*)
+ # Give a soname corresponding to the major version so that dld.sl refuses to
+ # link against other versions.
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ case $host_cpu in
+ ia64*)
+ shrext_cmds='.so'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.so"
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ if test 32 = "$HPUX_IA64_MODE"; then
+ sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+ sys_lib_dlsearch_path_spec=/usr/lib/hpux32
+ else
+ sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+ sys_lib_dlsearch_path_spec=/usr/lib/hpux64
+ fi
+ ;;
+ hppa*64*)
+ shrext_cmds='.sl'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ *)
+ shrext_cmds='.sl'
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=SHLIB_PATH
+ shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+ esac
+ # HP-UX runs *really* slowly unless shared libraries are mode 555, ...
+ postinstall_cmds='chmod 555 $lib'
+ # or fails outright, so override atomically:
+ install_override_mode=555
+ ;;
+
+interix[[3-9]]*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $host_os in
+ nonstopux*) version_type=nonstopux ;;
+ *)
+ if test yes = "$lt_cv_prog_gnu_ld"; then
+ version_type=linux # correct to gnu/linux during the next big refactor
+ else
+ version_type=irix
+ fi ;;
+ esac
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext'
+ case $host_os in
+ irix5* | nonstopux*)
+ libsuff= shlibsuff=
+ ;;
+ *)
+ case $LD in # libtool.m4 will add one of these switches to LD
+ *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+ libsuff= shlibsuff= libmagic=32-bit;;
+ *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+ libsuff=32 shlibsuff=N32 libmagic=N32;;
+ *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+ libsuff=64 shlibsuff=64 libmagic=64-bit;;
+ *) libsuff= shlibsuff= libmagic=never-match;;
+ esac
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff"
+ sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff"
+ hardcode_into_libs=yes
+ ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+ dynamic_linker=no
+ ;;
+
+linux*android*)
+ version_type=none # Android doesn't support versioned libraries.
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext'
+ soname_spec='$libname$release$shared_ext'
+ finish_cmds=
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ dynamic_linker='Android linker'
+ # Don't embed -rpath directories since the linker doesn't support them.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+
+ # Some binutils ld are patched to set DT_RUNPATH
+ AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath],
+ [lt_cv_shlibpath_overrides_runpath=no
+ save_LDFLAGS=$LDFLAGS
+ save_libdir=$libdir
+ eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \
+ LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\""
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+ [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null],
+ [lt_cv_shlibpath_overrides_runpath=yes])])
+ LDFLAGS=$save_LDFLAGS
+ libdir=$save_libdir
+ ])
+ shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ # Ideally, we could use ldconfig to report *all* directores which are
+ # searched for libraries, however this is still not possible. Aside from not
+ # being certain /sbin/ldconfig is available, command
+ # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64,
+ # even though it is searched at run-time. Try to do the best guess by
+ # appending ld.so.conf contents (and includes) to the search path.
+ if test -f /etc/ld.so.conf; then
+ lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
+ sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+ fi
+
+ # We used to test for /lib/ld.so.1 and disable shared libraries on
+ # powerpc, because MkLinux only supported shared libraries with the
+ # GNU dynamic linker. Since this was broken with cross compilers,
+ # most powerpc-linux boxes support dynamic linking these days and
+ # people can always --disable-shared, the test was removed, and we
+ # assume the GNU/Linux dynamic linker is in use.
+ dynamic_linker='GNU/Linux ld.so'
+ ;;
+
+netbsd*)
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ dynamic_linker='NetBSD (a.out) ld.so'
+ else
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ dynamic_linker='NetBSD ld.elf_so'
+ fi
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+
+newsos6)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+*nto* | *qnx*)
+ version_type=qnx
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ dynamic_linker='ldqnx.so'
+ ;;
+
+openbsd* | bitrig*)
+ version_type=sunos
+ sys_lib_dlsearch_path_spec=/usr/lib
+ need_lib_prefix=no
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ need_version=no
+ else
+ need_version=yes
+ fi
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+os2*)
+ libname_spec='$name'
+ version_type=windows
+ shrext_cmds=.dll
+ need_version=no
+ need_lib_prefix=no
+ # OS/2 can only load a DLL with a base name of 8 characters or less.
+ soname_spec='`test -n "$os2dllname" && libname="$os2dllname";
+ v=$($ECHO $release$versuffix | tr -d .-);
+ n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _);
+ $ECHO $n$v`$shared_ext'
+ library_names_spec='${libname}_dll.$libext'
+ dynamic_linker='OS/2 ld.exe'
+ shlibpath_var=BEGINLIBPATH
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ ;;
+
+osf3* | osf4* | osf5*)
+ version_type=osf
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+
+rdos*)
+ dynamic_linker=no
+ ;;
+
+solaris*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ # ldd complains unless libraries are executable
+ postinstall_cmds='chmod +x $lib'
+ ;;
+
+sunos4*)
+ version_type=sunos
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ if test yes = "$with_gnu_ld"; then
+ need_lib_prefix=no
+ fi
+ need_version=yes
+ ;;
+
+sysv4 | sysv4.3*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_vendor in
+ sni)
+ shlibpath_overrides_runpath=no
+ need_lib_prefix=no
+ runpath_var=LD_RUN_PATH
+ ;;
+ siemens)
+ need_lib_prefix=no
+ ;;
+ motorola)
+ need_lib_prefix=no
+ need_version=no
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+ ;;
+ esac
+ ;;
+
+sysv4*MP*)
+ if test -d /usr/nec; then
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext'
+ soname_spec='$libname$shared_ext.$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ fi
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ version_type=sco
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ if test yes = "$with_gnu_ld"; then
+ sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+ else
+ sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+ case $host_os in
+ sco3.2v5*)
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+ ;;
+ esac
+ fi
+ sys_lib_dlsearch_path_spec='/usr/lib'
+ ;;
+
+tpf*)
+ # TPF is a cross-target only. Preferred cross-host = GNU/Linux.
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+uts4*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+*)
+ dynamic_linker=no
+ ;;
+esac
+AC_MSG_RESULT([$dynamic_linker])
+test no = "$dynamic_linker" && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test yes = "$GCC"; then
+ variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then
+ sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec
+fi
+
+if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then
+ sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec
+fi
+
+# remember unaugmented sys_lib_dlsearch_path content for libtool script decls...
+configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec
+
+# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code
+func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH"
+
+# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool
+configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH
+
+_LT_DECL([], [variables_saved_for_relink], [1],
+ [Variables whose values should be saved in libtool wrapper scripts and
+ restored at link time])
+_LT_DECL([], [need_lib_prefix], [0],
+ [Do we need the "lib" prefix for modules?])
+_LT_DECL([], [need_version], [0], [Do we need a version for libraries?])
+_LT_DECL([], [version_type], [0], [Library versioning type])
+_LT_DECL([], [runpath_var], [0], [Shared library runtime path variable])
+_LT_DECL([], [shlibpath_var], [0],[Shared library path variable])
+_LT_DECL([], [shlibpath_overrides_runpath], [0],
+ [Is shlibpath searched before the hard-coded library search path?])
+_LT_DECL([], [libname_spec], [1], [Format of library name prefix])
+_LT_DECL([], [library_names_spec], [1],
+ [[List of archive names. First name is the real one, the rest are links.
+ The last name is the one that the linker finds with -lNAME]])
+_LT_DECL([], [soname_spec], [1],
+ [[The coded name of the library, if different from the real name]])
+_LT_DECL([], [install_override_mode], [1],
+ [Permission mode override for installation of shared libraries])
+_LT_DECL([], [postinstall_cmds], [2],
+ [Command to use after installation of a shared archive])
+_LT_DECL([], [postuninstall_cmds], [2],
+ [Command to use after uninstallation of a shared archive])
+_LT_DECL([], [finish_cmds], [2],
+ [Commands used to finish a libtool library installation in a directory])
+_LT_DECL([], [finish_eval], [1],
+ [[As "finish_cmds", except a single script fragment to be evaled but
+ not shown]])
+_LT_DECL([], [hardcode_into_libs], [0],
+ [Whether we should hardcode library paths into libraries])
+_LT_DECL([], [sys_lib_search_path_spec], [2],
+ [Compile-time system search path for libraries])
+_LT_DECL([sys_lib_dlsearch_path_spec], [configure_time_dlsearch_path], [2],
+ [Detected run-time system search path for libraries])
+_LT_DECL([], [configure_time_lt_sys_library_path], [2],
+ [Explicit LT_SYS_LIBRARY_PATH set during ./configure time])
+])# _LT_SYS_DYNAMIC_LINKER
+
+
+# _LT_PATH_TOOL_PREFIX(TOOL)
+# --------------------------
+# find a file program that can recognize shared library
+AC_DEFUN([_LT_PATH_TOOL_PREFIX],
+[m4_require([_LT_DECL_EGREP])dnl
+AC_MSG_CHECKING([for $1])
+AC_CACHE_VAL(lt_cv_path_MAGIC_CMD,
+[case $MAGIC_CMD in
+[[\\/*] | ?:[\\/]*])
+ lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD=$MAGIC_CMD
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+dnl $ac_dummy forces splitting on constant user-supplied paths.
+dnl POSIX.2 word splitting is done only on the output of word expansions,
+dnl not every word. This closes a longstanding sh security hole.
+ ac_dummy="m4_if([$2], , $PATH, [$2])"
+ for ac_dir in $ac_dummy; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$1"; then
+ lt_cv_path_MAGIC_CMD=$ac_dir/"$1"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS=$lt_save_ifs
+ MAGIC_CMD=$lt_save_MAGIC_CMD
+ ;;
+esac])
+MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+if test -n "$MAGIC_CMD"; then
+ AC_MSG_RESULT($MAGIC_CMD)
+else
+ AC_MSG_RESULT(no)
+fi
+_LT_DECL([], [MAGIC_CMD], [0],
+ [Used to examine libraries when file_magic_cmd begins with "file"])dnl
+])# _LT_PATH_TOOL_PREFIX
+
+# Old name:
+AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], [])
+
+
+# _LT_PATH_MAGIC
+# --------------
+# find a file program that can recognize a shared library
+m4_defun([_LT_PATH_MAGIC],
+[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH)
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+ if test -n "$ac_tool_prefix"; then
+ _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH)
+ else
+ MAGIC_CMD=:
+ fi
+fi
+])# _LT_PATH_MAGIC
+
+
+# LT_PATH_LD
+# ----------
+# find the pathname to the GNU or non-GNU linker
+AC_DEFUN([LT_PATH_LD],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_PROG_ECHO_BACKSLASH])dnl
+
+AC_ARG_WITH([gnu-ld],
+ [AS_HELP_STRING([--with-gnu-ld],
+ [assume the C compiler uses GNU ld @<:@default=no@:>@])],
+ [test no = "$withval" || with_gnu_ld=yes],
+ [with_gnu_ld=no])dnl
+
+ac_prog=ld
+if test yes = "$GCC"; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ AC_MSG_CHECKING([for ld used by $CC])
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return, which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [[\\/]]* | ?:[[\\/]]*)
+ re_direlt='/[[^/]][[^/]]*/\.\./'
+ # Canonicalize the pathname of ld
+ ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+ while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD=$ac_prog
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test yes = "$with_gnu_ld"; then
+ AC_MSG_CHECKING([for GNU ld])
+else
+ AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(lt_cv_path_LD,
+[if test -z "$LD"; then
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ lt_cv_path_LD=$ac_dir/$ac_prog
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some variants of GNU ld only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+ *GNU* | *'with BFD'*)
+ test no != "$with_gnu_ld" && break
+ ;;
+ *)
+ test yes != "$with_gnu_ld" && break
+ ;;
+ esac
+ fi
+ done
+ IFS=$lt_save_ifs
+else
+ lt_cv_path_LD=$LD # Let the user override the test with a path.
+fi])
+LD=$lt_cv_path_LD
+if test -n "$LD"; then
+ AC_MSG_RESULT($LD)
+else
+ AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+_LT_PATH_LD_GNU
+AC_SUBST([LD])
+
+_LT_TAGDECL([], [LD], [1], [The linker used to build libraries])
+])# LT_PATH_LD
+
+# Old names:
+AU_ALIAS([AM_PROG_LD], [LT_PATH_LD])
+AU_ALIAS([AC_PROG_LD], [LT_PATH_LD])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_LD], [])
+dnl AC_DEFUN([AC_PROG_LD], [])
+
+
+# _LT_PATH_LD_GNU
+#- --------------
+m4_defun([_LT_PATH_LD_GNU],
+[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], lt_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ lt_cv_prog_gnu_ld=yes
+ ;;
+*)
+ lt_cv_prog_gnu_ld=no
+ ;;
+esac])
+with_gnu_ld=$lt_cv_prog_gnu_ld
+])# _LT_PATH_LD_GNU
+
+
+# _LT_CMD_RELOAD
+# --------------
+# find reload flag for linker
+# -- PORTME Some linkers may need a different reload flag.
+m4_defun([_LT_CMD_RELOAD],
+[AC_CACHE_CHECK([for $LD option to reload object files],
+ lt_cv_ld_reload_flag,
+ [lt_cv_ld_reload_flag='-r'])
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ if test yes != "$GCC"; then
+ reload_cmds=false
+ fi
+ ;;
+ darwin*)
+ if test yes = "$GCC"; then
+ reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs'
+ else
+ reload_cmds='$LD$reload_flag -o $output$reload_objs'
+ fi
+ ;;
+esac
+_LT_TAGDECL([], [reload_flag], [1], [How to create reloadable object files])dnl
+_LT_TAGDECL([], [reload_cmds], [2])dnl
+])# _LT_CMD_RELOAD
+
+
+# _LT_PATH_DD
+# -----------
+# find a working dd
+m4_defun([_LT_PATH_DD],
+[AC_CACHE_CHECK([for a working dd], [ac_cv_path_lt_DD],
+[printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+: ${lt_DD:=$DD}
+AC_PATH_PROGS_FEATURE_CHECK([lt_DD], [dd],
+[if "$ac_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
+ cmp -s conftest.i conftest.out \
+ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=:
+fi])
+rm -f conftest.i conftest2.i conftest.out])
+])# _LT_PATH_DD
+
+
+# _LT_CMD_TRUNCATE
+# ----------------
+# find command to truncate a binary pipe
+m4_defun([_LT_CMD_TRUNCATE],
+[m4_require([_LT_PATH_DD])
+AC_CACHE_CHECK([how to truncate binary pipes], [lt_cv_truncate_bin],
+[printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+lt_cv_truncate_bin=
+if "$ac_cv_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
+ cmp -s conftest.i conftest.out \
+ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1"
+fi
+rm -f conftest.i conftest2.i conftest.out
+test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"])
+_LT_DECL([lt_truncate_bin], [lt_cv_truncate_bin], [1],
+ [Command to truncate a binary pipe])
+])# _LT_CMD_TRUNCATE
+
+
+# _LT_CHECK_MAGIC_METHOD
+# ----------------------
+# how to check for library dependencies
+# -- PORTME fill in with the dynamic library characteristics
+m4_defun([_LT_CHECK_MAGIC_METHOD],
+[m4_require([_LT_DECL_EGREP])
+m4_require([_LT_DECL_OBJDUMP])
+AC_CACHE_CHECK([how to recognize dependent libraries],
+lt_cv_deplibs_check_method,
+[lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# 'unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# that responds to the $file_magic_cmd with a given extended regex.
+# If you have 'file' or equivalent on your system and you're not sure
+# whether 'pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[[4-9]]*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+beos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+bsdi[[45]]*)
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)'
+ lt_cv_file_magic_cmd='/usr/bin/file -L'
+ lt_cv_file_magic_test_file=/shlib/libc.so
+ ;;
+
+cygwin*)
+ # func_win32_libid is a shell function defined in ltmain.sh
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ ;;
+
+mingw* | pw32*)
+ # Base MSYS/MinGW do not provide the 'file' command needed by
+ # func_win32_libid shell function, so use a weaker test based on 'objdump',
+ # unless we find 'file', for example because we are cross-compiling.
+ if ( file / ) >/dev/null 2>&1; then
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ else
+ # Keep this pattern in sync with the one in func_win32_libid.
+ lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ fi
+ ;;
+
+cegcc*)
+ # use the weaker test based on 'objdump'. See mingw*.
+ lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ ;;
+
+darwin* | rhapsody*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+freebsd* | dragonfly*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ case $host_cpu in
+ i*86 )
+ # Not sure whether the presence of OpenBSD here was a mistake.
+ # Let's accept both of them until this is cleared up.
+ lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+ ;;
+ esac
+ else
+ lt_cv_deplibs_check_method=pass_all
+ fi
+ ;;
+
+haiku*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+hpux10.20* | hpux11*)
+ lt_cv_file_magic_cmd=/usr/bin/file
+ case $host_cpu in
+ ia64*)
+ lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64'
+ lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+ ;;
+ hppa*64*)
+ [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]']
+ lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+ ;;
+ *)
+ lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library'
+ lt_cv_file_magic_test_file=/usr/lib/libc.sl
+ ;;
+ esac
+ ;;
+
+interix[[3-9]]*)
+ # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$'
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $LD in
+ *-32|*"-32 ") libmagic=32-bit;;
+ *-n32|*"-n32 ") libmagic=N32;;
+ *-64|*"-64 ") libmagic=64-bit;;
+ *) libmagic=never-match;;
+ esac
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$'
+ fi
+ ;;
+
+newos6*)
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=/usr/lib/libnls.so
+ ;;
+
+*nto* | *qnx*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+openbsd* | bitrig*)
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+ fi
+ ;;
+
+osf3* | osf4* | osf5*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+rdos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+solaris*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv4 | sysv4.3*)
+ case $host_vendor in
+ motorola)
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]'
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+ ;;
+ ncr)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ sequent)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )'
+ ;;
+ sni)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib"
+ lt_cv_file_magic_test_file=/lib/libc.so
+ ;;
+ siemens)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ pc)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ esac
+ ;;
+
+tpf*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+os2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+esac
+])
+
+file_magic_glob=
+want_nocaseglob=no
+if test "$build" = "$host"; then
+ case $host_os in
+ mingw* | pw32*)
+ if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then
+ want_nocaseglob=yes
+ else
+ file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"`
+ fi
+ ;;
+ esac
+fi
+
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+_LT_DECL([], [deplibs_check_method], [1],
+ [Method to check whether dependent libraries are shared objects])
+_LT_DECL([], [file_magic_cmd], [1],
+ [Command to use when deplibs_check_method = "file_magic"])
+_LT_DECL([], [file_magic_glob], [1],
+ [How to find potential files when deplibs_check_method = "file_magic"])
+_LT_DECL([], [want_nocaseglob], [1],
+ [Find potential files using nocaseglob when deplibs_check_method = "file_magic"])
+])# _LT_CHECK_MAGIC_METHOD
+
+
+# LT_PATH_NM
+# ----------
+# find the pathname to a BSD- or MS-compatible name lister
+AC_DEFUN([LT_PATH_NM],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM,
+[if test -n "$NM"; then
+ # Let the user override the test.
+ lt_cv_path_NM=$NM
+else
+ lt_nm_to_check=${ac_tool_prefix}nm
+ if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+ lt_nm_to_check="$lt_nm_to_check nm"
+ fi
+ for lt_tmp_nm in $lt_nm_to_check; do
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ tmp_nm=$ac_dir/$lt_tmp_nm
+ if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then
+ # Check to see if the nm accepts a BSD-compat flag.
+ # Adding the 'sed 1q' prevents false positives on HP-UX, which says:
+ # nm: unknown option "B" ignored
+ # Tru64's nm complains that /dev/null is an invalid object file
+ # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty
+ case $build_os in
+ mingw*) lt_bad_file=conftest.nm/nofile ;;
+ *) lt_bad_file=/dev/null ;;
+ esac
+ case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in
+ *$lt_bad_file* | *'Invalid file or object type'*)
+ lt_cv_path_NM="$tmp_nm -B"
+ break 2
+ ;;
+ *)
+ case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+ */dev/null*)
+ lt_cv_path_NM="$tmp_nm -p"
+ break 2
+ ;;
+ *)
+ lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+ continue # so that we can try to find one that supports BSD flags
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ done
+ IFS=$lt_save_ifs
+ done
+ : ${lt_cv_path_NM=no}
+fi])
+if test no != "$lt_cv_path_NM"; then
+ NM=$lt_cv_path_NM
+else
+ # Didn't find any BSD compatible name lister, look for dumpbin.
+ if test -n "$DUMPBIN"; then :
+ # Let the user override the test.
+ else
+ AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :)
+ case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in
+ *COFF*)
+ DUMPBIN="$DUMPBIN -symbols -headers"
+ ;;
+ *)
+ DUMPBIN=:
+ ;;
+ esac
+ fi
+ AC_SUBST([DUMPBIN])
+ if test : != "$DUMPBIN"; then
+ NM=$DUMPBIN
+ fi
+fi
+test -z "$NM" && NM=nm
+AC_SUBST([NM])
+_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl
+
+AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface],
+ [lt_cv_nm_interface="BSD nm"
+ echo "int some_variable = 0;" > conftest.$ac_ext
+ (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$ac_compile" 2>conftest.err)
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD)
+ cat conftest.out >&AS_MESSAGE_LOG_FD
+ if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+ lt_cv_nm_interface="MS dumpbin"
+ fi
+ rm -f conftest*])
+])# LT_PATH_NM
+
+# Old names:
+AU_ALIAS([AM_PROG_NM], [LT_PATH_NM])
+AU_ALIAS([AC_PROG_NM], [LT_PATH_NM])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_NM], [])
+dnl AC_DEFUN([AC_PROG_NM], [])
+
+# _LT_CHECK_SHAREDLIB_FROM_LINKLIB
+# --------------------------------
+# how to determine the name of the shared library
+# associated with a specific link library.
+# -- PORTME fill in with the dynamic library characteristics
+m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB],
+[m4_require([_LT_DECL_EGREP])
+m4_require([_LT_DECL_OBJDUMP])
+m4_require([_LT_DECL_DLLTOOL])
+AC_CACHE_CHECK([how to associate runtime and link libraries],
+lt_cv_sharedlib_from_linklib_cmd,
+[lt_cv_sharedlib_from_linklib_cmd='unknown'
+
+case $host_os in
+cygwin* | mingw* | pw32* | cegcc*)
+ # two different shell functions defined in ltmain.sh;
+ # decide which one to use based on capabilities of $DLLTOOL
+ case `$DLLTOOL --help 2>&1` in
+ *--identify-strict*)
+ lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib
+ ;;
+ *)
+ lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback
+ ;;
+ esac
+ ;;
+*)
+ # fallback: assume linklib IS sharedlib
+ lt_cv_sharedlib_from_linklib_cmd=$ECHO
+ ;;
+esac
+])
+sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd
+test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO
+
+_LT_DECL([], [sharedlib_from_linklib_cmd], [1],
+ [Command to associate shared and link libraries])
+])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB
+
+
+# _LT_PATH_MANIFEST_TOOL
+# ----------------------
+# locate the manifest tool
+m4_defun([_LT_PATH_MANIFEST_TOOL],
+[AC_CHECK_TOOL(MANIFEST_TOOL, mt, :)
+test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt
+AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool],
+ [lt_cv_path_mainfest_tool=no
+ echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD
+ $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ if $GREP 'Manifest Tool' conftest.out > /dev/null; then
+ lt_cv_path_mainfest_tool=yes
+ fi
+ rm -f conftest*])
+if test yes != "$lt_cv_path_mainfest_tool"; then
+ MANIFEST_TOOL=:
+fi
+_LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl
+])# _LT_PATH_MANIFEST_TOOL
+
+
+# _LT_DLL_DEF_P([FILE])
+# ---------------------
+# True iff FILE is a Windows DLL '.def' file.
+# Keep in sync with func_dll_def_p in the libtool script
+AC_DEFUN([_LT_DLL_DEF_P],
+[dnl
+ test DEF = "`$SED -n dnl
+ -e '\''s/^[[ ]]*//'\'' dnl Strip leading whitespace
+ -e '\''/^\(;.*\)*$/d'\'' dnl Delete empty lines and comments
+ -e '\''s/^\(EXPORTS\|LIBRARY\)\([[ ]].*\)*$/DEF/p'\'' dnl
+ -e q dnl Only consider the first "real" line
+ $1`" dnl
+])# _LT_DLL_DEF_P
+
+
+# LT_LIB_M
+# --------
+# check for math library
+AC_DEFUN([LT_LIB_M],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+LIBM=
+case $host in
+*-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*)
+ # These system don't have libm, or don't need it
+ ;;
+*-ncr-sysv4.3*)
+ AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM=-lmw)
+ AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm")
+ ;;
+*)
+ AC_CHECK_LIB(m, cos, LIBM=-lm)
+ ;;
+esac
+AC_SUBST([LIBM])
+])# LT_LIB_M
+
+# Old name:
+AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_CHECK_LIBM], [])
+
+
+# _LT_COMPILER_NO_RTTI([TAGNAME])
+# -------------------------------
+m4_defun([_LT_COMPILER_NO_RTTI],
+[m4_require([_LT_TAG_COMPILER])dnl
+
+_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+
+if test yes = "$GCC"; then
+ case $cc_basename in
+ nvcc*)
+ _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;;
+ esac
+
+ _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions],
+ lt_cv_prog_compiler_rtti_exceptions,
+ [-fno-rtti -fno-exceptions], [],
+ [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"])
+fi
+_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1],
+ [Compiler flag to turn off builtin functions])
+])# _LT_COMPILER_NO_RTTI
+
+
+# _LT_CMD_GLOBAL_SYMBOLS
+# ----------------------
+m4_defun([_LT_CMD_GLOBAL_SYMBOLS],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+AC_MSG_CHECKING([command to parse $NM output from $compiler object])
+AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe],
+[
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix. What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[[BCDEGRST]]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+ symcode='[[BCDT]]'
+ ;;
+cygwin* | mingw* | pw32* | cegcc*)
+ symcode='[[ABCDGISTW]]'
+ ;;
+hpux*)
+ if test ia64 = "$host_cpu"; then
+ symcode='[[ABCDEGRST]]'
+ fi
+ ;;
+irix* | nonstopux*)
+ symcode='[[BCDEGRST]]'
+ ;;
+osf*)
+ symcode='[[BCDEGQRST]]'
+ ;;
+solaris*)
+ symcode='[[BDRT]]'
+ ;;
+sco3.2v5*)
+ symcode='[[DT]]'
+ ;;
+sysv4.2uw2*)
+ symcode='[[DT]]'
+ ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+ symcode='[[ABDT]]'
+ ;;
+sysv4)
+ symcode='[[DFNSTU]]'
+ ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+ symcode='[[ABCDGIRSTW]]' ;;
+esac
+
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Gets list of data symbols to import.
+ lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'"
+ # Adjust the below global symbol transforms to fixup imported variables.
+ lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'"
+ lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'"
+ lt_c_name_lib_hook="\
+ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\
+ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'"
+else
+ # Disable hooks by default.
+ lt_cv_sys_global_symbol_to_import=
+ lt_cdecl_hook=
+ lt_c_name_hook=
+ lt_c_name_lib_hook=
+fi
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n"\
+$lt_cdecl_hook\
+" -e 's/^T .* \(.*\)$/extern int \1();/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n"\
+$lt_c_name_hook\
+" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'"
+
+# Transform an extracted symbol line into symbol name with lib prefix and
+# symbol address.
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\
+$lt_c_name_lib_hook\
+" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+ opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+ ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+ # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+ symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+ # Write the raw and C identifiers.
+ if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Fake it for dumpbin and say T for any non-static function,
+ # D for any global variable and I for any imported variable.
+ # Also find C++ and __fastcall symbols from MSVC++,
+ # which start with @ or ?.
+ lt_cv_sys_global_symbol_pipe="$AWK ['"\
+" {last_section=section; section=\$ 3};"\
+" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\
+" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+" /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\
+" /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\
+" /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\
+" \$ 0!~/External *\|/{next};"\
+" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+" {if(hide[section]) next};"\
+" {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\
+" {split(\$ 0,a,/\||\r/); split(a[2],s)};"\
+" s[1]~/^[@?]/{print f,s[1],s[1]; next};"\
+" s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\
+" ' prfx=^$ac_symprfx]"
+ else
+ lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+ fi
+ lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'"
+
+ # Check to see that the pipe works correctly.
+ pipe_works=no
+
+ rm -f conftest*
+ cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+ if AC_TRY_EVAL(ac_compile); then
+ # Now try to grab the symbols.
+ nlist=conftest.nm
+ if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then
+ # Try sorting and uniquifying the output.
+ if sort "$nlist" | uniq > "$nlist"T; then
+ mv -f "$nlist"T "$nlist"
+ else
+ rm -f "$nlist"T
+ fi
+
+ # Make sure that we snagged all the symbols we need.
+ if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+ if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+ cat <<_LT_EOF > conftest.$ac_ext
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */
+#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE
+/* DATA imports from DLLs on WIN32 can't be const, because runtime
+ relocations are performed -- see ld's documentation on pseudo-relocs. */
+# define LT@&t@_DLSYM_CONST
+#elif defined __osf__
+/* This system does not cope well with relocations in const data. */
+# define LT@&t@_DLSYM_CONST
+#else
+# define LT@&t@_DLSYM_CONST const
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+ # Now generate the symbol file.
+ eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+ cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols. */
+LT@&t@_DLSYM_CONST struct {
+ const char *name;
+ void *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[[]] =
+{
+ { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+ $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+ cat <<\_LT_EOF >> conftest.$ac_ext
+ {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+ # Now try linking the two files.
+ mv conftest.$ac_objext conftstm.$ac_objext
+ lt_globsym_save_LIBS=$LIBS
+ lt_globsym_save_CFLAGS=$CFLAGS
+ LIBS=conftstm.$ac_objext
+ CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)"
+ if AC_TRY_EVAL(ac_link) && test -s conftest$ac_exeext; then
+ pipe_works=yes
+ fi
+ LIBS=$lt_globsym_save_LIBS
+ CFLAGS=$lt_globsym_save_CFLAGS
+ else
+ echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD
+ fi
+ else
+ echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD
+ fi
+ else
+ echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD
+ fi
+ else
+ echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD
+ cat conftest.$ac_ext >&5
+ fi
+ rm -rf conftest* conftst*
+
+ # Do not use the global_symbol_pipe unless it works.
+ if test yes = "$pipe_works"; then
+ break
+ else
+ lt_cv_sys_global_symbol_pipe=
+ fi
+done
+])
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+ lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+ AC_MSG_RESULT(failed)
+else
+ AC_MSG_RESULT(ok)
+fi
+
+# Response file support.
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ nm_file_list_spec='@'
+elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then
+ nm_file_list_spec='@'
+fi
+
+_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1],
+ [Take the output of nm and produce a listing of raw symbols and C names])
+_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1],
+ [Transform the output of nm in a proper C declaration])
+_LT_DECL([global_symbol_to_import], [lt_cv_sys_global_symbol_to_import], [1],
+ [Transform the output of nm into a list of symbols to manually relocate])
+_LT_DECL([global_symbol_to_c_name_address],
+ [lt_cv_sys_global_symbol_to_c_name_address], [1],
+ [Transform the output of nm in a C name address pair])
+_LT_DECL([global_symbol_to_c_name_address_lib_prefix],
+ [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1],
+ [Transform the output of nm in a C name address pair when lib prefix is needed])
+_LT_DECL([nm_interface], [lt_cv_nm_interface], [1],
+ [The name lister interface])
+_LT_DECL([], [nm_file_list_spec], [1],
+ [Specify filename containing input files for $NM])
+]) # _LT_CMD_GLOBAL_SYMBOLS
+
+
+# _LT_COMPILER_PIC([TAGNAME])
+# ---------------------------
+m4_defun([_LT_COMPILER_PIC],
+[m4_require([_LT_TAG_COMPILER])dnl
+_LT_TAGVAR(lt_prog_compiler_wl, $1)=
+_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+_LT_TAGVAR(lt_prog_compiler_static, $1)=
+
+m4_if([$1], [CXX], [
+ # C++ specific cases for pic, static, wl, etc.
+ if test yes = "$GXX"; then
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ fi
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the '-m68020' flag to GCC prevents building anything better,
+ # like '-m68040'.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+ mingw* | cygwin* | os2* | pw32* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ m4_if([$1], [GCJ], [],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ case $host_os in
+ os2*)
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static'
+ ;;
+ esac
+ ;;
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+ ;;
+ *djgpp*)
+ # DJGPP does not support shared libraries at all
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ ;;
+ haiku*)
+ # PIC is the default for Haiku.
+ # The "-static" flag exists, but is broken.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)=
+ ;;
+ interix[[3-9]]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+ fi
+ ;;
+ hpux*)
+ # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+ # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
+ # sets the default TLS model and affects inlining.
+ case $host_cpu in
+ hppa*64*)
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+ ;;
+ *qnx* | *nto*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+ else
+ case $host_os in
+ aix[[4-9]]*)
+ # All AIX code is PIC.
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ else
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+ chorus*)
+ case $cc_basename in
+ cxch68*)
+ # Green Hills C++ Compiler
+ # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a"
+ ;;
+ esac
+ ;;
+ mingw* | cygwin* | os2* | pw32* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ m4_if([$1], [GCJ], [],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ ;;
+ dgux*)
+ case $cc_basename in
+ ec++*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ ;;
+ ghcx*)
+ # Green Hills C++ Compiler
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ freebsd* | dragonfly*)
+ # FreeBSD uses GNU C++
+ ;;
+ hpux9* | hpux10* | hpux11*)
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive'
+ if test ia64 != "$host_cpu"; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+ fi
+ ;;
+ aCC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive'
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+ ;;
+ esac
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ interix*)
+ # This is c89, which is MS Visual C++ (no shared libs)
+ # Anyone wants to do a port?
+ ;;
+ irix5* | irix6* | nonstopux*)
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ # CC pic flag -KPIC is the default.
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ case $cc_basename in
+ KCC*)
+ # KAI C++ Compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ ecpc* )
+ # old Intel C++ for x86_64, which still supported -KPIC.
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ icpc* )
+ # Intel C++, used to be incompatible with GCC.
+ # ICC 10 doesn't accept -KPIC any more.
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ pgCC* | pgcpp*)
+ # Portland Group C++ compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ cxx*)
+ # Compaq C++
+ # Make sure the PIC flag is empty. It appears that all Alpha
+ # Linux and Compaq Tru64 Unix objects are PIC.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+ xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*)
+ # IBM XL 8.0, 9.0 on PPC and BlueGene
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C++ 5.9
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ lynxos*)
+ ;;
+ m88k*)
+ ;;
+ mvs*)
+ case $cc_basename in
+ cxx*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ netbsd*)
+ ;;
+ *qnx* | *nto*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+ osf3* | osf4* | osf5*)
+ case $cc_basename in
+ KCC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+ ;;
+ RCC*)
+ # Rational C++ 2.4.1
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ ;;
+ cxx*)
+ # Digital/Compaq C++
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # Make sure the PIC flag is empty. It appears that all Alpha
+ # Linux and Compaq Tru64 Unix objects are PIC.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ psos*)
+ ;;
+ solaris*)
+ case $cc_basename in
+ CC* | sunCC*)
+ # Sun C++ 4.2, 5.x and Centerline C++
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ ;;
+ gcx*)
+ # Green Hills C++ Compiler
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ sunos4*)
+ case $cc_basename in
+ CC*)
+ # Sun C++ 4.x
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ lcc*)
+ # Lucid
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ esac
+ ;;
+ tandem*)
+ case $cc_basename in
+ NCC*)
+ # NonStop-UX NCC 3.20
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ vxworks*)
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ ;;
+ esac
+ fi
+],
+[
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ fi
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the '-m68020' flag to GCC prevents building anything better,
+ # like '-m68040'.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ m4_if([$1], [GCJ], [],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ case $host_os in
+ os2*)
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static'
+ ;;
+ esac
+ ;;
+
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+ ;;
+
+ haiku*)
+ # PIC is the default for Haiku.
+ # The "-static" flag exists, but is broken.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)=
+ ;;
+
+ hpux*)
+ # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+ # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
+ # sets the default TLS model and affects inlining.
+ case $host_cpu in
+ hppa*64*)
+ # +Z the default
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+ ;;
+
+ interix[[3-9]]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+
+ msdosdjgpp*)
+ # Just because we use GCC doesn't mean we suddenly get shared libraries
+ # on systems that don't support them.
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ enable_shared=no
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+ fi
+ ;;
+
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+
+ case $cc_basename in
+ nvcc*) # Cuda Compiler Driver 2.2
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker '
+ if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)"
+ fi
+ ;;
+ esac
+ else
+ # PORTME Check for flag to pass linker flags through the system compiler.
+ case $host_os in
+ aix*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ else
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+ case $cc_basename in
+ nagfor*)
+ # NAG Fortran compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ esac
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ m4_if([$1], [GCJ], [],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ case $host_os in
+ os2*)
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static'
+ ;;
+ esac
+ ;;
+
+ hpux9* | hpux10* | hpux11*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+ # not for PA HP-UX.
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+ ;;
+ esac
+ # Is there a better lt_prog_compiler_static that works with the bundled CC?
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive'
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # PIC (with -KPIC) is the default.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+
+ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ case $cc_basename in
+ # old Intel for x86_64, which still supported -KPIC.
+ ecc*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ # icc used to be incompatible with GCC.
+ # ICC 10 doesn't accept -KPIC any more.
+ icc* | ifort*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ # Lahey Fortran 8.1.
+ lf95*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='--static'
+ ;;
+ nagfor*)
+ # NAG Fortran compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ tcc*)
+ # Fabrice Bellard et al's Tiny C Compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*)
+ # Portland Group compilers (*not* the Pentium gcc compiler,
+ # which looks to be a dead project)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ ccc*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # All Alpha code is PIC.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+ xl* | bgxl* | bgf* | mpixl*)
+ # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*)
+ # Sun Fortran 8.3 passes all unrecognized flags to the linker
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)=''
+ ;;
+ *Sun\ F* | *Sun*Fortran*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ ;;
+ *Sun\ C*)
+ # Sun C 5.9
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ ;;
+ *Intel*\ [[CF]]*Compiler*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ *Portland\ Group*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ newsos6)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+
+ osf3* | osf4* | osf5*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # All OSF/1 code is PIC.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+
+ rdos*)
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+
+ solaris*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ case $cc_basename in
+ f77* | f90* | f95* | sunf77* | sunf90* | sunf95*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';;
+ esac
+ ;;
+
+ sunos4*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ sysv4 | sysv4.2uw2* | sysv4.3*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ fi
+ ;;
+
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ unicos*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ ;;
+
+ uts4*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ *)
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ ;;
+ esac
+ fi
+])
+case $host_os in
+ # For platforms that do not support PIC, -DPIC is meaningless:
+ *djgpp*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])"
+ ;;
+esac
+
+AC_CACHE_CHECK([for $compiler option to produce PIC],
+ [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)],
+ [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)])
+_LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then
+ _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works],
+ [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)],
+ [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [],
+ [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in
+ "" | " "*) ;;
+ *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;;
+ esac],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no])
+fi
+_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1],
+ [Additional compiler flags for building library objects])
+
+_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1],
+ [How to pass a linker flag through the compiler])
+#
+# Check to make sure the static flag actually works.
+#
+wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\"
+_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works],
+ _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1),
+ $lt_tmp_static_flag,
+ [],
+ [_LT_TAGVAR(lt_prog_compiler_static, $1)=])
+_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1],
+ [Compiler flag to prevent dynamic linking])
+])# _LT_COMPILER_PIC
+
+
+# _LT_LINKER_SHLIBS([TAGNAME])
+# ----------------------------
+# See if the linker supports building shared libraries.
+m4_defun([_LT_LINKER_SHLIBS],
+[AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+m4_require([_LT_PATH_MANIFEST_TOOL])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+m4_if([$1], [CXX], [
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+ case $host_os in
+ aix[[4-9]]*)
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to GNU nm, but means don't demangle to AIX nm.
+ # Without the "-l" option, or with the "-B" option, AIX nm treats
+ # weak defined symbols like other global defined symbols, whereas
+ # GNU nm marks them as "W".
+ # While the 'weak' keyword is ignored in the Export File, we need
+ # it in the Import File for the 'aix-soname' feature, so we have
+ # to replace the "-B" option with "-P" for AIX nm.
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols'
+ else
+ _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols'
+ fi
+ ;;
+ pw32*)
+ _LT_TAGVAR(export_symbols_cmds, $1)=$ltdll_cmds
+ ;;
+ cygwin* | mingw* | cegcc*)
+ case $cc_basename in
+ cl*)
+ _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+ ;;
+ *)
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
+ _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname']
+ ;;
+ esac
+ ;;
+ *)
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ ;;
+ esac
+], [
+ runpath_var=
+ _LT_TAGVAR(allow_undefined_flag, $1)=
+ _LT_TAGVAR(always_export_symbols, $1)=no
+ _LT_TAGVAR(archive_cmds, $1)=
+ _LT_TAGVAR(archive_expsym_cmds, $1)=
+ _LT_TAGVAR(compiler_needs_object, $1)=no
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ _LT_TAGVAR(hardcode_automatic, $1)=no
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=
+ _LT_TAGVAR(hardcode_minus_L, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+ _LT_TAGVAR(inherit_rpath, $1)=no
+ _LT_TAGVAR(link_all_deplibs, $1)=unknown
+ _LT_TAGVAR(module_cmds, $1)=
+ _LT_TAGVAR(module_expsym_cmds, $1)=
+ _LT_TAGVAR(old_archive_from_new_cmds, $1)=
+ _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)=
+ _LT_TAGVAR(thread_safe_flag_spec, $1)=
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ # include_expsyms should be a list of space-separated symbols to be *always*
+ # included in the symbol list
+ _LT_TAGVAR(include_expsyms, $1)=
+ # exclude_expsyms can be an extended regexp of symbols to exclude
+ # it will be wrapped by ' (' and ')$', so one must not match beginning or
+ # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc',
+ # as well as any symbol that contains 'd'.
+ _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+ # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+ # platforms (ab)use it in PIC code, but their linkers get confused if
+ # the symbol is explicitly referenced. Since portable code cannot
+ # rely on this symbol name, it's probably fine to never include it in
+ # preloaded symbol tables.
+ # Exclude shared library initialization/finalization symbols.
+dnl Note also adjust exclude_expsyms for C++ above.
+ extract_expsyms_cmds=
+
+ case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ # FIXME: the MSVC++ port hasn't been tested in a loooong time
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ if test yes != "$GCC"; then
+ with_gnu_ld=no
+ fi
+ ;;
+ interix*)
+ # we just hope/assume this is gcc and not c89 (= MSVC++)
+ with_gnu_ld=yes
+ ;;
+ openbsd* | bitrig*)
+ with_gnu_ld=no
+ ;;
+ esac
+
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+
+ # On some targets, GNU ld is compatible enough with the native linker
+ # that we're better off using the native interface for both.
+ lt_use_gnu_ld_interface=no
+ if test yes = "$with_gnu_ld"; then
+ case $host_os in
+ aix*)
+ # The AIX port of GNU ld has always aspired to compatibility
+ # with the native linker. However, as the warning in the GNU ld
+ # block says, versions before 2.19.5* couldn't really create working
+ # shared libraries, regardless of the interface used.
+ case `$LD -v 2>&1` in
+ *\ \(GNU\ Binutils\)\ 2.19.5*) ;;
+ *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;;
+ *\ \(GNU\ Binutils\)\ [[3-9]]*) ;;
+ *)
+ lt_use_gnu_ld_interface=yes
+ ;;
+ esac
+ ;;
+ *)
+ lt_use_gnu_ld_interface=yes
+ ;;
+ esac
+ fi
+
+ if test yes = "$lt_use_gnu_ld_interface"; then
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ wlarc='$wl'
+
+ # Set some defaults for GNU ld with shared library support. These
+ # are reset later if shared libraries are not supported. Putting them
+ # here allows them to be overridden if necessary.
+ runpath_var=LD_RUN_PATH
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
+ else
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ fi
+ supports_anon_versioning=no
+ case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in
+ *GNU\ gold*) supports_anon_versioning=yes ;;
+ *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11
+ *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+ *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+ *\ 2.11.*) ;; # other 2.11 versions
+ *) supports_anon_versioning=yes ;;
+ esac
+
+ # See if GNU ld supports shared libraries.
+ case $host_os in
+ aix[[3-9]]*)
+ # On AIX/PPC, the GNU linker is very broken
+ if test ia64 != "$host_cpu"; then
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.19, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support. If you
+*** really care for shared libraries, you may want to install binutils
+*** 2.20 or above, or modify your PATH so that a non-GNU linker is found.
+*** You will then need to restart the configuration process.
+
+_LT_EOF
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)=''
+ ;;
+ m68k)
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ ;;
+ esac
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+ # as there is no search path for DLLs.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols'
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=no
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
+ _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname']
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file, use it as
+ # is; otherwise, prepend EXPORTS...
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ haiku*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+
+ os2*)
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ shrext_cmds=.dll
+ _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ prefix_cmds="$SED"~
+ if test EXPORTS = "`$SED 1q $export_symbols`"; then
+ prefix_cmds="$prefix_cmds -e 1d";
+ fi~
+ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ ;;
+
+ interix[[3-9]]*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+
+ gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
+ tmp_diet=no
+ if test linux-dietlibc = "$host_os"; then
+ case $cc_basename in
+ diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn)
+ esac
+ fi
+ if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+ && test no = "$tmp_diet"
+ then
+ tmp_addflag=' $pic_flag'
+ tmp_sharedflag='-shared'
+ case $cc_basename,$host_cpu in
+ pgcc*) # Portland Group C compiler
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ tmp_addflag=' $pic_flag'
+ ;;
+ pgf77* | pgf90* | pgf95* | pgfortran*)
+ # Portland Group f77 and f90 compilers
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ tmp_addflag=' $pic_flag -Mnomain' ;;
+ ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64
+ tmp_addflag=' -i_dynamic' ;;
+ efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64
+ tmp_addflag=' -i_dynamic -nofor_main' ;;
+ ifc* | ifort*) # Intel Fortran compiler
+ tmp_addflag=' -nofor_main' ;;
+ lf95*) # Lahey Fortran 8.1
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ tmp_sharedflag='--shared' ;;
+ nagfor*) # NAGFOR 5.3
+ tmp_sharedflag='-Wl,-shared' ;;
+ xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+ tmp_sharedflag='-qmkshrobj'
+ tmp_addflag= ;;
+ nvcc*) # Cuda Compiler Driver 2.2
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ _LT_TAGVAR(compiler_needs_object, $1)=yes
+ ;;
+ esac
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*) # Sun C 5.9
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ _LT_TAGVAR(compiler_needs_object, $1)=yes
+ tmp_sharedflag='-G' ;;
+ *Sun\ F*) # Sun Fortran 8.3
+ tmp_sharedflag='-G' ;;
+ esac
+ _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+
+ if test yes = "$supports_anon_versioning"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib'
+ fi
+
+ case $cc_basename in
+ tcc*)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='-rdynamic'
+ ;;
+ xlf* | bgf* | bgxlf* | mpixlf*)
+ # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib'
+ if test yes = "$supports_anon_versioning"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ esac
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+ wlarc=
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ fi
+ ;;
+
+ solaris*)
+ if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+ case `$LD -v 2>&1` in
+ *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*)
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot
+*** reliably create shared libraries on SCO systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ ;;
+ *)
+ # For security reasons, it is highly recommended that you always
+ # use absolute paths for naming shared libraries, and exclude the
+ # DT_RUNPATH tag from executables and libraries. But doing so
+ # requires that you compile everything twice, which is a pain.
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ sunos4*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ wlarc=
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ *)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+
+ if test no = "$_LT_TAGVAR(ld_shlibs, $1)"; then
+ runpath_var=
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ fi
+ else
+ # PORTME fill in a description of your system's linker (not GNU ld)
+ case $host_os in
+ aix3*)
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+ # Note: this linker hardcodes the directories in LIBPATH if there
+ # are no directories specified by -L.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then
+ # Neither direct hardcoding nor static linking is supported with a
+ # broken collect2.
+ _LT_TAGVAR(hardcode_direct, $1)=unsupported
+ fi
+ ;;
+
+ aix[[4-9]]*)
+ if test ia64 = "$host_cpu"; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=
+ else
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to GNU nm, but means don't demangle to AIX nm.
+ # Without the "-l" option, or with the "-B" option, AIX nm treats
+ # weak defined symbols like other global defined symbols, whereas
+ # GNU nm marks them as "W".
+ # While the 'weak' keyword is ignored in the Export File, we need
+ # it in the Import File for the 'aix-soname' feature, so we have
+ # to replace the "-B" option with "-P" for AIX nm.
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols'
+ else
+ _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols'
+ fi
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # have runtime linking enabled, and use it for executables.
+ # For shared libraries, we enable/disable runtime linking
+ # depending on the kind of the shared library created -
+ # when "with_aix_soname,aix_use_runtimelinking" is:
+ # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables
+ # "aix,yes" lib.so shared, rtl:yes, for executables
+ # lib.a static archive
+ # "both,no" lib.so.V(shr.o) shared, rtl:yes
+ # lib.a(lib.so.V) shared, rtl:no, for executables
+ # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a(lib.so.V) shared, rtl:no
+ # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a static archive
+ case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+ for ld_flag in $LDFLAGS; do
+ if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then
+ aix_use_runtimelinking=yes
+ break
+ fi
+ done
+ if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # With aix-soname=svr4, we create the lib.so.V shared archives only,
+ # so we don't have lib.a shared libs to link our executables.
+ # We have to force runtime linking in this case.
+ aix_use_runtimelinking=yes
+ LDFLAGS="$LDFLAGS -Wl,-brtl"
+ fi
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ _LT_TAGVAR(archive_cmds, $1)=''
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(file_list_spec, $1)='$wl-f,'
+ case $with_aix_soname,$aix_use_runtimelinking in
+ aix,*) ;; # traditional, no import file
+ svr4,* | *,yes) # use import file
+ # The Import File defines what to hardcode.
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=no
+ ;;
+ esac
+
+ if test yes = "$GCC"; then
+ case $host_os in aix4.[[012]]|aix4.[[012]].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`$CC -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ _LT_TAGVAR(hardcode_direct, $1)=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=
+ fi
+ ;;
+ esac
+ shared_flag='-shared'
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag="$shared_flag "'$wl-G'
+ fi
+ # Need to ensure runtime linking is disabled for the traditional
+ # shared library, or the linker may eventually find shared libraries
+ # /with/ Import File - we do not want to mix them.
+ shared_flag_aix='-shared'
+ shared_flag_svr4='-shared $wl-G'
+ else
+ # not using gcc
+ if test ia64 = "$host_cpu"; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag='$wl-G'
+ else
+ shared_flag='$wl-bM:SRE'
+ fi
+ shared_flag_aix='$wl-bM:SRE'
+ shared_flag_svr4='$wl-G'
+ fi
+ fi
+
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall'
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to export.
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ _LT_TAGVAR(allow_undefined_flag, $1)='-berok'
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ _LT_SYS_MODULE_PATH_AIX([$1])
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath"
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag
+ else
+ if test ia64 = "$host_cpu"; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib'
+ _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ _LT_SYS_MODULE_PATH_AIX([$1])
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok'
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok'
+ if test yes = "$with_gnu_ld"; then
+ # We only use this code for GNU lds that support --whole-archive.
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive'
+ else
+ # Exported symbols can be pulled into shared objects from archives
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d'
+ # -brtl affects multiple linker settings, -berok does not and is overridden later
+ compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`'
+ if test svr4 != "$with_aix_soname"; then
+ # This is similar to how AIX traditionally builds its shared libraries.
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname'
+ fi
+ if test aix != "$with_aix_soname"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp'
+ else
+ # used by -dlpreopen to get the symbols
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir'
+ fi
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d'
+ fi
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)=''
+ ;;
+ m68k)
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ ;;
+ esac
+ ;;
+
+ bsdi[[45]]*)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ case $cc_basename in
+ cl*)
+ # Native MSVC
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ _LT_TAGVAR(file_list_spec, $1)='@'
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=.dll
+ # FIXME: Setting linknames here is a bad hack.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames='
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then
+ cp "$export_symbols" "$output_objdir/$soname.def";
+ echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp";
+ else
+ $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp;
+ fi~
+ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+ linknames='
+ # The linker will not automatically build a static lib if we build a DLL.
+ # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols'
+ # Don't use ranlib
+ _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib'
+ _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~
+ lt_tool_outputfile="@TOOL_OUTPUT@"~
+ case $lt_outputfile in
+ *.exe|*.EXE) ;;
+ *)
+ lt_outputfile=$lt_outputfile.exe
+ lt_tool_outputfile=$lt_tool_outputfile.exe
+ ;;
+ esac~
+ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then
+ $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+ $RM "$lt_outputfile.manifest";
+ fi'
+ ;;
+ *)
+ # Assume MSVC wrapper
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=.dll
+ # FIXME: Setting linknames here is a bad hack.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames='
+ # The linker will automatically build a .lib file if we build a DLL.
+ _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+ # FIXME: Should let the user specify the lib program.
+ _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ ;;
+ esac
+ ;;
+
+ darwin* | rhapsody*)
+ _LT_DARWIN_LINKER_FEATURES($1)
+ ;;
+
+ dgux*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+ # support. Future versions do this automatically, but an explicit c++rt0.o
+ # does not break anything, and helps significantly (at the cost of a little
+ # extra space).
+ freebsd2.2*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+ freebsd2.*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+ freebsd* | dragonfly*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ hpux9*)
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ ;;
+
+ hpux10*)
+ if test yes,no = "$GCC,$with_gnu_ld"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ if test no = "$with_gnu_ld"; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ fi
+ ;;
+
+ hpux11*)
+ if test yes,no = "$GCC,$with_gnu_ld"; then
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ else
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ m4_if($1, [], [
+ # Older versions of the 11.00 compiler do not understand -b yet
+ # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does)
+ _LT_LINKER_OPTION([if $CC understands -b],
+ _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b],
+ [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'],
+ [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])],
+ [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'])
+ ;;
+ esac
+ fi
+ if test no = "$with_gnu_ld"; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+ *)
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ ;;
+ esac
+ fi
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ # Try to use the -exported_symbol ld option, if it does not
+ # work, assume that -exports_file does not work either and
+ # implicitly export all symbols.
+ # This should be the same for all languages, so no per-tag cache variable.
+ AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol],
+ [lt_cv_irix_exported_symbol],
+ [save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null"
+ AC_LINK_IFELSE(
+ [AC_LANG_SOURCE(
+ [AC_LANG_CASE([C], [[int foo (void) { return 0; }]],
+ [C++], [[int foo (void) { return 0; }]],
+ [Fortran 77], [[
+ subroutine foo
+ end]],
+ [Fortran], [[
+ subroutine foo
+ end]])])],
+ [lt_cv_irix_exported_symbol=yes],
+ [lt_cv_irix_exported_symbol=no])
+ LDFLAGS=$save_LDFLAGS])
+ if test yes = "$lt_cv_irix_exported_symbol"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib'
+ fi
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(inherit_rpath, $1)=yes
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+
+ linux*)
+ case $cc_basename in
+ tcc*)
+ # Fabrice Bellard et al's Tiny C Compiler
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ newsos6)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ *nto* | *qnx*)
+ ;;
+
+ openbsd* | bitrig*)
+ if test -f /usr/libexec/ld.so; then
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ fi
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ os2*)
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ shrext_cmds=.dll
+ _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ prefix_cmds="$SED"~
+ if test EXPORTS = "`$SED 1q $export_symbols`"; then
+ prefix_cmds="$prefix_cmds -e 1d";
+ fi~
+ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ ;;
+
+ osf3*)
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ else
+ _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ ;;
+
+ osf4* | osf5*) # as osf3* with the addition of -msym flag
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ else
+ _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp'
+
+ # Both c and cxx compiler support -rpath directly
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ ;;
+
+ solaris*)
+ _LT_TAGVAR(no_undefined_flag, $1)=' -z defs'
+ if test yes = "$GCC"; then
+ wlarc='$wl'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ else
+ case `$CC -V 2>&1` in
+ *"Compilers 5.0"*)
+ wlarc=''
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+ ;;
+ *)
+ wlarc='$wl'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ ;;
+ esac
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ case $host_os in
+ solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands '-z linker_flag'. GCC discards it without '$wl',
+ # but is careful enough not to reorder.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract'
+ else
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
+ fi
+ ;;
+ esac
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+
+ sunos4*)
+ if test sequent = "$host_vendor"; then
+ # Use $CC to link under sequent, because it throws in some extra .o
+ # files that make .init and .fini sections work.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ sysv4)
+ case $host_vendor in
+ sni)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true???
+ ;;
+ siemens)
+ ## LD is ld it makes a PLAMLIB
+ ## CC just makes a GrossModule.
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs'
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ ;;
+ motorola)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie
+ ;;
+ esac
+ runpath_var='LD_RUN_PATH'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ sysv4.3*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ runpath_var=LD_RUN_PATH
+ hardcode_runpath_var=yes
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ fi
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
+ _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ runpath_var='LD_RUN_PATH'
+
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We CANNOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text'
+ _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ uts4*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ *)
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+
+ if test sni = "$host_vendor"; then
+ case $host in
+ sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Blargedynsym'
+ ;;
+ esac
+ fi
+ fi
+])
+AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no
+
+_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld
+
+_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl
+_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl
+_LT_DECL([], [extract_expsyms_cmds], [2],
+ [The commands to extract the exported symbol list from a shared archive])
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in
+x|xyes)
+ # Assume -lc should be added
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+
+ if test yes,yes = "$GCC,$enable_shared"; then
+ case $_LT_TAGVAR(archive_cmds, $1) in
+ *'~'*)
+ # FIXME: we may have to deal with multi-command sequences.
+ ;;
+ '$CC '*)
+ # Test whether the compiler implicitly links with -lc since on some
+ # systems, -lgcc has to come before -lc. If gcc already passes -lc
+ # to ld, don't add -lc before -lgcc.
+ AC_CACHE_CHECK([whether -lc should be explicitly linked in],
+ [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1),
+ [$RM conftest*
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ if AC_TRY_EVAL(ac_compile) 2>conftest.err; then
+ soname=conftest
+ lib=conftest
+ libobjs=conftest.$ac_objext
+ deplibs=
+ wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1)
+ pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1)
+ compiler_flags=-v
+ linker_flags=-v
+ verstring=
+ output_objdir=.
+ libname=conftest
+ lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1)
+ _LT_TAGVAR(allow_undefined_flag, $1)=
+ if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1)
+ then
+ lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ else
+ lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+ fi
+ _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag
+ else
+ cat conftest.err 1>&5
+ fi
+ $RM conftest*
+ ])
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)
+ ;;
+ esac
+ fi
+ ;;
+esac
+
+_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0],
+ [Whether or not to add -lc for building shared libraries])
+_LT_TAGDECL([allow_libtool_libs_with_static_runtimes],
+ [enable_shared_with_static_runtimes], [0],
+ [Whether or not to disallow shared libs when runtime libs are static])
+_LT_TAGDECL([], [export_dynamic_flag_spec], [1],
+ [Compiler flag to allow reflexive dlopens])
+_LT_TAGDECL([], [whole_archive_flag_spec], [1],
+ [Compiler flag to generate shared objects directly from archives])
+_LT_TAGDECL([], [compiler_needs_object], [1],
+ [Whether the compiler copes with passing no objects directly])
+_LT_TAGDECL([], [old_archive_from_new_cmds], [2],
+ [Create an old-style archive from a shared archive])
+_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2],
+ [Create a temporary old-style archive to link instead of a shared archive])
+_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive])
+_LT_TAGDECL([], [archive_expsym_cmds], [2])
+_LT_TAGDECL([], [module_cmds], [2],
+ [Commands used to build a loadable module if different from building
+ a shared archive.])
+_LT_TAGDECL([], [module_expsym_cmds], [2])
+_LT_TAGDECL([], [with_gnu_ld], [1],
+ [Whether we are building with GNU ld or not])
+_LT_TAGDECL([], [allow_undefined_flag], [1],
+ [Flag that allows shared libraries with undefined symbols to be built])
+_LT_TAGDECL([], [no_undefined_flag], [1],
+ [Flag that enforces no undefined symbols])
+_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1],
+ [Flag to hardcode $libdir into a binary during linking.
+ This must work even if $libdir does not exist])
+_LT_TAGDECL([], [hardcode_libdir_separator], [1],
+ [Whether we need a single "-rpath" flag with a separated argument])
+_LT_TAGDECL([], [hardcode_direct], [0],
+ [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes
+ DIR into the resulting binary])
+_LT_TAGDECL([], [hardcode_direct_absolute], [0],
+ [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes
+ DIR into the resulting binary and the resulting library dependency is
+ "absolute", i.e impossible to change by setting $shlibpath_var if the
+ library is relocated])
+_LT_TAGDECL([], [hardcode_minus_L], [0],
+ [Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+ into the resulting binary])
+_LT_TAGDECL([], [hardcode_shlibpath_var], [0],
+ [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+ into the resulting binary])
+_LT_TAGDECL([], [hardcode_automatic], [0],
+ [Set to "yes" if building a shared library automatically hardcodes DIR
+ into the library and all subsequent libraries and executables linked
+ against it])
+_LT_TAGDECL([], [inherit_rpath], [0],
+ [Set to yes if linker adds runtime paths of dependent libraries
+ to runtime path list])
+_LT_TAGDECL([], [link_all_deplibs], [0],
+ [Whether libtool must link a program against all its dependency libraries])
+_LT_TAGDECL([], [always_export_symbols], [0],
+ [Set to "yes" if exported symbols are required])
+_LT_TAGDECL([], [export_symbols_cmds], [2],
+ [The commands to list exported symbols])
+_LT_TAGDECL([], [exclude_expsyms], [1],
+ [Symbols that should not be listed in the preloaded symbols])
+_LT_TAGDECL([], [include_expsyms], [1],
+ [Symbols that must always be exported])
+_LT_TAGDECL([], [prelink_cmds], [2],
+ [Commands necessary for linking programs (against libraries) with templates])
+_LT_TAGDECL([], [postlink_cmds], [2],
+ [Commands necessary for finishing linking programs])
+_LT_TAGDECL([], [file_list_spec], [1],
+ [Specify filename containing input files])
+dnl FIXME: Not yet implemented
+dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1],
+dnl [Compiler flag to generate thread safe objects])
+])# _LT_LINKER_SHLIBS
+
+
+# _LT_LANG_C_CONFIG([TAG])
+# ------------------------
+# Ensure that the configuration variables for a C compiler are suitably
+# defined. These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_C_CONFIG],
+[m4_require([_LT_DECL_EGREP])dnl
+lt_save_CC=$CC
+AC_LANG_PUSH(C)
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+_LT_TAG_COMPILER
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+## CAVEAT EMPTOR:
+## There is no encapsulation within the following macros, do not change
+## the running order or otherwise move them around unless you know exactly
+## what you are doing...
+if test -n "$compiler"; then
+ _LT_COMPILER_NO_RTTI($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+ LT_SYS_DLOPEN_SELF
+ _LT_CMD_STRIPLIB
+
+ # Report what library types will actually be built
+ AC_MSG_CHECKING([if libtool supports shared libraries])
+ AC_MSG_RESULT([$can_build_shared])
+
+ AC_MSG_CHECKING([whether to build shared libraries])
+ test no = "$can_build_shared" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test yes = "$enable_shared" && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+
+ aix[[4-9]]*)
+ if test ia64 != "$host_cpu"; then
+ case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in
+ yes,aix,yes) ;; # shared object as lib.so file only
+ yes,svr4,*) ;; # shared object as lib.so archive member only
+ yes,*) enable_static=no ;; # shared object in lib.a archive as well
+ esac
+ fi
+ ;;
+ esac
+ AC_MSG_RESULT([$enable_shared])
+
+ AC_MSG_CHECKING([whether to build static libraries])
+ # Make sure either enable_shared or enable_static is yes.
+ test yes = "$enable_shared" || enable_static=yes
+ AC_MSG_RESULT([$enable_static])
+
+ _LT_CONFIG($1)
+fi
+AC_LANG_POP
+CC=$lt_save_CC
+])# _LT_LANG_C_CONFIG
+
+
+# _LT_LANG_CXX_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a C++ compiler are suitably
+# defined. These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_CXX_CONFIG],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_PATH_MANIFEST_TOOL])dnl
+if test -n "$CXX" && ( test no != "$CXX" &&
+ ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) ||
+ (test g++ != "$CXX"))); then
+ AC_PROG_CXXCPP
+else
+ _lt_caught_CXX_error=yes
+fi
+
+AC_LANG_PUSH(C++)
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(compiler_needs_object, $1)=no
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for C++ test sources.
+ac_ext=cpp
+
+# Object file extension for compiled C++ test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the CXX compiler isn't working. Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test yes != "$_lt_caught_CXX_error"; then
+ # Code to be used in simple compile tests
+ lt_simple_compile_test_code="int some_variable = 0;"
+
+ # Code to be used in simple link tests
+ lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }'
+
+ # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+ _LT_TAG_COMPILER
+
+ # save warnings/boilerplate of simple test code
+ _LT_COMPILER_BOILERPLATE
+ _LT_LINKER_BOILERPLATE
+
+ # Allow CC to be a program name with arguments.
+ lt_save_CC=$CC
+ lt_save_CFLAGS=$CFLAGS
+ lt_save_LD=$LD
+ lt_save_GCC=$GCC
+ GCC=$GXX
+ lt_save_with_gnu_ld=$with_gnu_ld
+ lt_save_path_LD=$lt_cv_path_LD
+ if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then
+ lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx
+ else
+ $as_unset lt_cv_prog_gnu_ld
+ fi
+ if test -n "${lt_cv_path_LDCXX+set}"; then
+ lt_cv_path_LD=$lt_cv_path_LDCXX
+ else
+ $as_unset lt_cv_path_LD
+ fi
+ test -z "${LDCXX+set}" || LD=$LDCXX
+ CC=${CXX-"c++"}
+ CFLAGS=$CXXFLAGS
+ compiler=$CC
+ _LT_TAGVAR(compiler, $1)=$CC
+ _LT_CC_BASENAME([$compiler])
+
+ if test -n "$compiler"; then
+ # We don't want -fno-exception when compiling C++ code, so set the
+ # no_builtin_flag separately
+ if test yes = "$GXX"; then
+ _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
+ else
+ _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+ fi
+
+ if test yes = "$GXX"; then
+ # Set up default GNU C++ configuration
+
+ LT_PATH_LD
+
+ # Check if GNU C++ uses GNU ld as the underlying linker, since the
+ # archiving commands below assume that GNU ld is being used.
+ if test yes = "$with_gnu_ld"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to
+ # investigate it a little bit more. (MM)
+ wlarc='$wl'
+
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if eval "`$CC -print-prog-name=ld` --help 2>&1" |
+ $GREP 'no-whole-archive' > /dev/null; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
+ else
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ fi
+ else
+ with_gnu_ld=no
+ wlarc=
+
+ # A generic and very simple default shared library creation
+ # command for GNU C++ for the case where it uses the native
+ # linker, instead of GNU ld. If possible, this setting should
+ # overridden to take advantage of the native linker features on
+ # the platform it is being used on.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+ fi
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+
+ else
+ GXX=no
+ with_gnu_ld=no
+ wlarc=
+ fi
+
+ # PORTME: fill in a description of your system's C++ link characteristics
+ AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ case $host_os in
+ aix3*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ aix[[4-9]]*)
+ if test ia64 = "$host_cpu"; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=
+ else
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # have runtime linking enabled, and use it for executables.
+ # For shared libraries, we enable/disable runtime linking
+ # depending on the kind of the shared library created -
+ # when "with_aix_soname,aix_use_runtimelinking" is:
+ # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables
+ # "aix,yes" lib.so shared, rtl:yes, for executables
+ # lib.a static archive
+ # "both,no" lib.so.V(shr.o) shared, rtl:yes
+ # lib.a(lib.so.V) shared, rtl:no, for executables
+ # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a(lib.so.V) shared, rtl:no
+ # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a static archive
+ case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+ for ld_flag in $LDFLAGS; do
+ case $ld_flag in
+ *-brtl*)
+ aix_use_runtimelinking=yes
+ break
+ ;;
+ esac
+ done
+ if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # With aix-soname=svr4, we create the lib.so.V shared archives only,
+ # so we don't have lib.a shared libs to link our executables.
+ # We have to force runtime linking in this case.
+ aix_use_runtimelinking=yes
+ LDFLAGS="$LDFLAGS -Wl,-brtl"
+ fi
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ _LT_TAGVAR(archive_cmds, $1)=''
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(file_list_spec, $1)='$wl-f,'
+ case $with_aix_soname,$aix_use_runtimelinking in
+ aix,*) ;; # no import file
+ svr4,* | *,yes) # use import file
+ # The Import File defines what to hardcode.
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=no
+ ;;
+ esac
+
+ if test yes = "$GXX"; then
+ case $host_os in aix4.[[012]]|aix4.[[012]].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`$CC -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ _LT_TAGVAR(hardcode_direct, $1)=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=
+ fi
+ esac
+ shared_flag='-shared'
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag=$shared_flag' $wl-G'
+ fi
+ # Need to ensure runtime linking is disabled for the traditional
+ # shared library, or the linker may eventually find shared libraries
+ # /with/ Import File - we do not want to mix them.
+ shared_flag_aix='-shared'
+ shared_flag_svr4='-shared $wl-G'
+ else
+ # not using gcc
+ if test ia64 = "$host_cpu"; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag='$wl-G'
+ else
+ shared_flag='$wl-bM:SRE'
+ fi
+ shared_flag_aix='$wl-bM:SRE'
+ shared_flag_svr4='$wl-G'
+ fi
+ fi
+
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall'
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to
+ # export.
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ # The "-G" linker flag allows undefined symbols.
+ _LT_TAGVAR(no_undefined_flag, $1)='-bernotok'
+ # Determine the default libpath from the value encoded in an empty
+ # executable.
+ _LT_SYS_MODULE_PATH_AIX([$1])
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath"
+
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag
+ else
+ if test ia64 = "$host_cpu"; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib'
+ _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ _LT_SYS_MODULE_PATH_AIX([$1])
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok'
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok'
+ if test yes = "$with_gnu_ld"; then
+ # We only use this code for GNU lds that support --whole-archive.
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive'
+ else
+ # Exported symbols can be pulled into shared objects from archives
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d'
+ # -brtl affects multiple linker settings, -berok does not and is overridden later
+ compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`'
+ if test svr4 != "$with_aix_soname"; then
+ # This is similar to how AIX traditionally builds its shared
+ # libraries. Need -bnortl late, we may have -brtl in LDFLAGS.
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname'
+ fi
+ if test aix != "$with_aix_soname"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp'
+ else
+ # used by -dlpreopen to get the symbols
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir'
+ fi
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d'
+ fi
+ fi
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ chorus*)
+ case $cc_basename in
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ case $GXX,$cc_basename in
+ ,cl* | no,cl*)
+ # Native MSVC
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ _LT_TAGVAR(file_list_spec, $1)='@'
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=.dll
+ # FIXME: Setting linknames here is a bad hack.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames='
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then
+ cp "$export_symbols" "$output_objdir/$soname.def";
+ echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp";
+ else
+ $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp;
+ fi~
+ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+ linknames='
+ # The linker will not automatically build a static lib if we build a DLL.
+ # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ # Don't use ranlib
+ _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib'
+ _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~
+ lt_tool_outputfile="@TOOL_OUTPUT@"~
+ case $lt_outputfile in
+ *.exe|*.EXE) ;;
+ *)
+ lt_outputfile=$lt_outputfile.exe
+ lt_tool_outputfile=$lt_tool_outputfile.exe
+ ;;
+ esac~
+ func_to_tool_file "$lt_outputfile"~
+ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then
+ $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+ $RM "$lt_outputfile.manifest";
+ fi'
+ ;;
+ *)
+ # g++
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+ # as there is no search path for DLLs.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols'
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=no
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file, use it as
+ # is; otherwise, prepend EXPORTS...
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+ darwin* | rhapsody*)
+ _LT_DARWIN_LINKER_FEATURES($1)
+ ;;
+
+ os2*)
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ shrext_cmds=.dll
+ _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ prefix_cmds="$SED"~
+ if test EXPORTS = "`$SED 1q $export_symbols`"; then
+ prefix_cmds="$prefix_cmds -e 1d";
+ fi~
+ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ ;;
+
+ dgux*)
+ case $cc_basename in
+ ec++*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ ghcx*)
+ # Green Hills C++ Compiler
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ freebsd2.*)
+ # C++ shared libraries reported to be fairly broken before
+ # switch to ELF
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ freebsd-elf*)
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ ;;
+
+ freebsd* | dragonfly*)
+ # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF
+ # conventions
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ ;;
+
+ haiku*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+
+ hpux9*)
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+ # but as the default
+ # location of the library.
+
+ case $cc_basename in
+ CC*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ aCC*)
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+ ;;
+ *)
+ if test yes = "$GXX"; then
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+ else
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ hpux10*|hpux11*)
+ if test no = "$with_gnu_ld"; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ ;;
+ *)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ ;;
+ esac
+ fi
+ case $host_cpu in
+ hppa*64*|ia64*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+ *)
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+ # but as the default
+ # location of the library.
+ ;;
+ esac
+
+ case $cc_basename in
+ CC*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ aCC*)
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ esac
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+ ;;
+ *)
+ if test yes = "$GXX"; then
+ if test no = "$with_gnu_ld"; then
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ esac
+ fi
+ else
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ interix[[3-9]]*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+ irix5* | irix6*)
+ case $cc_basename in
+ CC*)
+ # SGI C++
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+
+ # Archives containing C++ object files must be created using
+ # "CC -ar", where "CC" is the IRIX C++ compiler. This is
+ # necessary to make sure instantiated templates are included
+ # in the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs'
+ ;;
+ *)
+ if test yes = "$GXX"; then
+ if test no = "$with_gnu_ld"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib'
+ fi
+ fi
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+ esac
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(inherit_rpath, $1)=yes
+ ;;
+
+ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ case $cc_basename in
+ KCC*)
+ # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+ # KCC will only create a shared library if the output file
+ # ends with ".so" (or ".sl" for HP-UX), so rename the library
+ # to its proper name (with version) after linking.
+ _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib'
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+
+ # Archives containing C++ object files must be created using
+ # "CC -Bstatic", where "CC" is the KAI C++ compiler.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs'
+ ;;
+ icpc* | ecpc* )
+ # Intel C++
+ with_gnu_ld=yes
+ # version 8.0 and above of icpc choke on multiply defined symbols
+ # if we add $predep_objects and $postdep_objects, however 7.1 and
+ # earlier do not add the objects themselves.
+ case `$CC -V 2>&1` in
+ *"Version 7."*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ *) # Version 8.0 or newer
+ tmp_idyn=
+ case $host_cpu in
+ ia64*) tmp_idyn=' -i_dynamic';;
+ esac
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ esac
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive'
+ ;;
+ pgCC* | pgcpp*)
+ # Portland Group C++ compiler
+ case `$CC -V` in
+ *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*)
+ _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+ _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+ $RANLIB $oldlib'
+ _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ *) # Version 6 and above use weak symbols
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ esac
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl--rpath $wl$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ ;;
+ cxx*)
+ # Compaq C++
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols'
+
+ runpath_var=LD_RUN_PATH
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed'
+ ;;
+ xl* | mpixl* | bgxl*)
+ # IBM XL 8.0 on PPC, with GNU ld
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ if test yes = "$supports_anon_versioning"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C++ 5.9
+ _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ _LT_TAGVAR(compiler_needs_object, $1)=yes
+
+ # Not sure whether something based on
+ # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1
+ # would be better.
+ output_verbose_link_cmd='func_echo_all'
+
+ # Archives containing C++ object files must be created using
+ # "CC -xar", where "CC" is the Sun C++ compiler. This is
+ # necessary to make sure instantiated templates are included
+ # in the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ lynxos*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ m88k*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ mvs*)
+ case $cc_basename in
+ cxx*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags'
+ wlarc=
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ fi
+ # Workaround some broken pre-1.5 toolchains
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"'
+ ;;
+
+ *nto* | *qnx*)
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ ;;
+
+ openbsd* | bitrig*)
+ if test -f /usr/libexec/ld.so; then
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
+ fi
+ output_verbose_link_cmd=func_echo_all
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ osf3* | osf4* | osf5*)
+ case $cc_basename in
+ KCC*)
+ # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+ # KCC will only create a shared library if the output file
+ # ends with ".so" (or ".sl" for HP-UX), so rename the library
+ # to its proper name (with version) after linking.
+ _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Archives containing C++ object files must be created using
+ # the KAI C++ compiler.
+ case $host in
+ osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;;
+ *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;;
+ esac
+ ;;
+ RCC*)
+ # Rational C++ 2.4.1
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ cxx*)
+ case $host in
+ osf3*)
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ ;;
+ *)
+ _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~
+ echo "-hidden">> $lib.exp~
+ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~
+ $RM $lib.exp'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+ ;;
+ esac
+
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+ ;;
+ *)
+ if test yes,no = "$GXX,$with_gnu_ld"; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*'
+ case $host in
+ osf3*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ ;;
+ esac
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+
+ else
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ psos*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ sunos4*)
+ case $cc_basename in
+ CC*)
+ # Sun C++ 4.x
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ lcc*)
+ # Lucid
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ solaris*)
+ case $cc_basename in
+ CC* | sunCC*)
+ # Sun C++ 4.2, 5.x and Centerline C++
+ _LT_TAGVAR(archive_cmds_need_lc,$1)=yes
+ _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ case $host_os in
+ solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands '-z linker_flag'.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
+ ;;
+ esac
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+
+ output_verbose_link_cmd='func_echo_all'
+
+ # Archives containing C++ object files must be created using
+ # "CC -xar", where "CC" is the Sun C++ compiler. This is
+ # necessary to make sure instantiated templates are included
+ # in the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+ ;;
+ gcx*)
+ # Green Hills C++ Compiler
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'
+
+ # The C++ compiler must be used to create the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs'
+ ;;
+ *)
+ # GNU C++ compiler with Solaris linker
+ if test yes,no = "$GXX,$with_gnu_ld"; then
+ _LT_TAGVAR(no_undefined_flag, $1)=' $wl-z ${wl}defs'
+ if $CC --version | $GREP -v '^2\.7' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+ else
+ # g++ 2.7 appears to require '-G' NOT '-shared' on this
+ # platform.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+ fi
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir'
+ case $host_os in
+ solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+ *)
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract'
+ ;;
+ esac
+ fi
+ ;;
+ esac
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
+ _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ runpath_var='LD_RUN_PATH'
+
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We CANNOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text'
+ _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~
+ '"$_LT_TAGVAR(old_archive_cmds, $1)"
+ _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~
+ '"$_LT_TAGVAR(reload_cmds, $1)"
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
+ tandem*)
+ case $cc_basename in
+ NCC*)
+ # NonStop-UX NCC 3.20
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ vxworks*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+
+ AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+ test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no
+
+ _LT_TAGVAR(GCC, $1)=$GXX
+ _LT_TAGVAR(LD, $1)=$LD
+
+ ## CAVEAT EMPTOR:
+ ## There is no encapsulation within the following macros, do not change
+ ## the running order or otherwise move them around unless you know exactly
+ ## what you are doing...
+ _LT_SYS_HIDDEN_LIBDEPS($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+ fi # test -n "$compiler"
+
+ CC=$lt_save_CC
+ CFLAGS=$lt_save_CFLAGS
+ LDCXX=$LD
+ LD=$lt_save_LD
+ GCC=$lt_save_GCC
+ with_gnu_ld=$lt_save_with_gnu_ld
+ lt_cv_path_LDCXX=$lt_cv_path_LD
+ lt_cv_path_LD=$lt_save_path_LD
+ lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld
+ lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld
+fi # test yes != "$_lt_caught_CXX_error"
+
+AC_LANG_POP
+])# _LT_LANG_CXX_CONFIG
+
+
+# _LT_FUNC_STRIPNAME_CNF
+# ----------------------
+# func_stripname_cnf prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+#
+# This function is identical to the (non-XSI) version of func_stripname,
+# except this one can be used by m4 code that may be executed by configure,
+# rather than the libtool script.
+m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl
+AC_REQUIRE([_LT_DECL_SED])
+AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])
+func_stripname_cnf ()
+{
+ case @S|@2 in
+ .*) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%\\\\@S|@2\$%%"`;;
+ *) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%@S|@2\$%%"`;;
+ esac
+} # func_stripname_cnf
+])# _LT_FUNC_STRIPNAME_CNF
+
+
+# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME])
+# ---------------------------------
+# Figure out "hidden" library dependencies from verbose
+# compiler output when linking a shared library.
+# Parse the compiler output and extract the necessary
+# objects, libraries and library flags.
+m4_defun([_LT_SYS_HIDDEN_LIBDEPS],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl
+# Dependencies to place before and after the object being linked:
+_LT_TAGVAR(predep_objects, $1)=
+_LT_TAGVAR(postdep_objects, $1)=
+_LT_TAGVAR(predeps, $1)=
+_LT_TAGVAR(postdeps, $1)=
+_LT_TAGVAR(compiler_lib_search_path, $1)=
+
+dnl we can't use the lt_simple_compile_test_code here,
+dnl because it contains code intended for an executable,
+dnl not a library. It's possible we should let each
+dnl tag define a new lt_????_link_test_code variable,
+dnl but it's only used here...
+m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF
+int a;
+void foo (void) { a = 0; }
+_LT_EOF
+], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF
+class Foo
+{
+public:
+ Foo (void) { a = 0; }
+private:
+ int a;
+};
+_LT_EOF
+], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF
+ subroutine foo
+ implicit none
+ integer*4 a
+ a=0
+ return
+ end
+_LT_EOF
+], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF
+ subroutine foo
+ implicit none
+ integer a
+ a=0
+ return
+ end
+_LT_EOF
+], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF
+public class foo {
+ private int a;
+ public void bar (void) {
+ a = 0;
+ }
+};
+_LT_EOF
+], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF
+package foo
+func foo() {
+}
+_LT_EOF
+])
+
+_lt_libdeps_save_CFLAGS=$CFLAGS
+case "$CC $CFLAGS " in #(
+*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;;
+*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;;
+*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;;
+esac
+
+dnl Parse the compiler output and extract the necessary
+dnl objects, libraries and library flags.
+if AC_TRY_EVAL(ac_compile); then
+ # Parse the compiler output and extract the necessary
+ # objects, libraries and library flags.
+
+ # Sentinel used to keep track of whether or not we are before
+ # the conftest object file.
+ pre_test_object_deps_done=no
+
+ for p in `eval "$output_verbose_link_cmd"`; do
+ case $prev$p in
+
+ -L* | -R* | -l*)
+ # Some compilers place space between "-{L,R}" and the path.
+ # Remove the space.
+ if test x-L = "$p" ||
+ test x-R = "$p"; then
+ prev=$p
+ continue
+ fi
+
+ # Expand the sysroot to ease extracting the directories later.
+ if test -z "$prev"; then
+ case $p in
+ -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;;
+ -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;;
+ -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;;
+ esac
+ fi
+ case $p in
+ =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;;
+ esac
+ if test no = "$pre_test_object_deps_done"; then
+ case $prev in
+ -L | -R)
+ # Internal compiler library paths should come after those
+ # provided the user. The postdeps already come after the
+ # user supplied libs so there is no need to process them.
+ if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then
+ _LT_TAGVAR(compiler_lib_search_path, $1)=$prev$p
+ else
+ _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} $prev$p"
+ fi
+ ;;
+ # The "-l" case would never come before the object being
+ # linked, so don't bother handling this case.
+ esac
+ else
+ if test -z "$_LT_TAGVAR(postdeps, $1)"; then
+ _LT_TAGVAR(postdeps, $1)=$prev$p
+ else
+ _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} $prev$p"
+ fi
+ fi
+ prev=
+ ;;
+
+ *.lto.$objext) ;; # Ignore GCC LTO objects
+ *.$objext)
+ # This assumes that the test object file only shows up
+ # once in the compiler output.
+ if test "$p" = "conftest.$objext"; then
+ pre_test_object_deps_done=yes
+ continue
+ fi
+
+ if test no = "$pre_test_object_deps_done"; then
+ if test -z "$_LT_TAGVAR(predep_objects, $1)"; then
+ _LT_TAGVAR(predep_objects, $1)=$p
+ else
+ _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p"
+ fi
+ else
+ if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then
+ _LT_TAGVAR(postdep_objects, $1)=$p
+ else
+ _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p"
+ fi
+ fi
+ ;;
+
+ *) ;; # Ignore the rest.
+
+ esac
+ done
+
+ # Clean up.
+ rm -f a.out a.exe
+else
+ echo "libtool.m4: error: problem compiling $1 test program"
+fi
+
+$RM -f confest.$objext
+CFLAGS=$_lt_libdeps_save_CFLAGS
+
+# PORTME: override above test on systems where it is broken
+m4_if([$1], [CXX],
+[case $host_os in
+interix[[3-9]]*)
+ # Interix 3.5 installs completely hosed .la files for C++, so rather than
+ # hack all around it, let's just trust "g++" to DTRT.
+ _LT_TAGVAR(predep_objects,$1)=
+ _LT_TAGVAR(postdep_objects,$1)=
+ _LT_TAGVAR(postdeps,$1)=
+ ;;
+esac
+])
+
+case " $_LT_TAGVAR(postdeps, $1) " in
+*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;;
+esac
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=
+if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | $SED -e 's! -L! !g' -e 's!^ !!'`
+fi
+_LT_TAGDECL([], [compiler_lib_search_dirs], [1],
+ [The directories searched by this compiler when creating a shared library])
+_LT_TAGDECL([], [predep_objects], [1],
+ [Dependencies to place before and after the objects being linked to
+ create a shared library])
+_LT_TAGDECL([], [postdep_objects], [1])
+_LT_TAGDECL([], [predeps], [1])
+_LT_TAGDECL([], [postdeps], [1])
+_LT_TAGDECL([], [compiler_lib_search_path], [1],
+ [The library search path used internally by the compiler when linking
+ a shared library])
+])# _LT_SYS_HIDDEN_LIBDEPS
+
+
+# _LT_LANG_F77_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a Fortran 77 compiler are
+# suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_F77_CONFIG],
+[AC_LANG_PUSH(Fortran 77)
+if test -z "$F77" || test no = "$F77"; then
+ _lt_disable_F77=yes
+fi
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for f77 test sources.
+ac_ext=f
+
+# Object file extension for compiled f77 test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the F77 compiler isn't working. Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test yes != "$_lt_disable_F77"; then
+ # Code to be used in simple compile tests
+ lt_simple_compile_test_code="\
+ subroutine t
+ return
+ end
+"
+
+ # Code to be used in simple link tests
+ lt_simple_link_test_code="\
+ program t
+ end
+"
+
+ # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+ _LT_TAG_COMPILER
+
+ # save warnings/boilerplate of simple test code
+ _LT_COMPILER_BOILERPLATE
+ _LT_LINKER_BOILERPLATE
+
+ # Allow CC to be a program name with arguments.
+ lt_save_CC=$CC
+ lt_save_GCC=$GCC
+ lt_save_CFLAGS=$CFLAGS
+ CC=${F77-"f77"}
+ CFLAGS=$FFLAGS
+ compiler=$CC
+ _LT_TAGVAR(compiler, $1)=$CC
+ _LT_CC_BASENAME([$compiler])
+ GCC=$G77
+ if test -n "$compiler"; then
+ AC_MSG_CHECKING([if libtool supports shared libraries])
+ AC_MSG_RESULT([$can_build_shared])
+
+ AC_MSG_CHECKING([whether to build shared libraries])
+ test no = "$can_build_shared" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test yes = "$enable_shared" && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+ aix[[4-9]]*)
+ if test ia64 != "$host_cpu"; then
+ case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in
+ yes,aix,yes) ;; # shared object as lib.so file only
+ yes,svr4,*) ;; # shared object as lib.so archive member only
+ yes,*) enable_static=no ;; # shared object in lib.a archive as well
+ esac
+ fi
+ ;;
+ esac
+ AC_MSG_RESULT([$enable_shared])
+
+ AC_MSG_CHECKING([whether to build static libraries])
+ # Make sure either enable_shared or enable_static is yes.
+ test yes = "$enable_shared" || enable_static=yes
+ AC_MSG_RESULT([$enable_static])
+
+ _LT_TAGVAR(GCC, $1)=$G77
+ _LT_TAGVAR(LD, $1)=$LD
+
+ ## CAVEAT EMPTOR:
+ ## There is no encapsulation within the following macros, do not change
+ ## the running order or otherwise move them around unless you know exactly
+ ## what you are doing...
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+ fi # test -n "$compiler"
+
+ GCC=$lt_save_GCC
+ CC=$lt_save_CC
+ CFLAGS=$lt_save_CFLAGS
+fi # test yes != "$_lt_disable_F77"
+
+AC_LANG_POP
+])# _LT_LANG_F77_CONFIG
+
+
+# _LT_LANG_FC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for a Fortran compiler are
+# suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_FC_CONFIG],
+[AC_LANG_PUSH(Fortran)
+
+if test -z "$FC" || test no = "$FC"; then
+ _lt_disable_FC=yes
+fi
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for fc test sources.
+ac_ext=${ac_fc_srcext-f}
+
+# Object file extension for compiled fc test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the FC compiler isn't working. Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test yes != "$_lt_disable_FC"; then
+ # Code to be used in simple compile tests
+ lt_simple_compile_test_code="\
+ subroutine t
+ return
+ end
+"
+
+ # Code to be used in simple link tests
+ lt_simple_link_test_code="\
+ program t
+ end
+"
+
+ # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+ _LT_TAG_COMPILER
+
+ # save warnings/boilerplate of simple test code
+ _LT_COMPILER_BOILERPLATE
+ _LT_LINKER_BOILERPLATE
+
+ # Allow CC to be a program name with arguments.
+ lt_save_CC=$CC
+ lt_save_GCC=$GCC
+ lt_save_CFLAGS=$CFLAGS
+ CC=${FC-"f95"}
+ CFLAGS=$FCFLAGS
+ compiler=$CC
+ GCC=$ac_cv_fc_compiler_gnu
+
+ _LT_TAGVAR(compiler, $1)=$CC
+ _LT_CC_BASENAME([$compiler])
+
+ if test -n "$compiler"; then
+ AC_MSG_CHECKING([if libtool supports shared libraries])
+ AC_MSG_RESULT([$can_build_shared])
+
+ AC_MSG_CHECKING([whether to build shared libraries])
+ test no = "$can_build_shared" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test yes = "$enable_shared" && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+ aix[[4-9]]*)
+ if test ia64 != "$host_cpu"; then
+ case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in
+ yes,aix,yes) ;; # shared object as lib.so file only
+ yes,svr4,*) ;; # shared object as lib.so archive member only
+ yes,*) enable_static=no ;; # shared object in lib.a archive as well
+ esac
+ fi
+ ;;
+ esac
+ AC_MSG_RESULT([$enable_shared])
+
+ AC_MSG_CHECKING([whether to build static libraries])
+ # Make sure either enable_shared or enable_static is yes.
+ test yes = "$enable_shared" || enable_static=yes
+ AC_MSG_RESULT([$enable_static])
+
+ _LT_TAGVAR(GCC, $1)=$ac_cv_fc_compiler_gnu
+ _LT_TAGVAR(LD, $1)=$LD
+
+ ## CAVEAT EMPTOR:
+ ## There is no encapsulation within the following macros, do not change
+ ## the running order or otherwise move them around unless you know exactly
+ ## what you are doing...
+ _LT_SYS_HIDDEN_LIBDEPS($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+ fi # test -n "$compiler"
+
+ GCC=$lt_save_GCC
+ CC=$lt_save_CC
+ CFLAGS=$lt_save_CFLAGS
+fi # test yes != "$_lt_disable_FC"
+
+AC_LANG_POP
+])# _LT_LANG_FC_CONFIG
+
+
+# _LT_LANG_GCJ_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for the GNU Java Compiler compiler
+# are suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_GCJ_CONFIG],
+[AC_REQUIRE([LT_PROG_GCJ])dnl
+AC_LANG_SAVE
+
+# Source file extension for Java test sources.
+ac_ext=java
+
+# Object file extension for compiled Java test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="class foo {}"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC=$CC
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=yes
+CC=${GCJ-"gcj"}
+CFLAGS=$GCJFLAGS
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_TAGVAR(LD, $1)=$LD
+_LT_CC_BASENAME([$compiler])
+
+# GCJ did not exist at the time GCC didn't implicitly link libc in.
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+
+## CAVEAT EMPTOR:
+## There is no encapsulation within the following macros, do not change
+## the running order or otherwise move them around unless you know exactly
+## what you are doing...
+if test -n "$compiler"; then
+ _LT_COMPILER_NO_RTTI($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+fi
+
+AC_LANG_RESTORE
+
+GCC=$lt_save_GCC
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_GCJ_CONFIG
+
+
+# _LT_LANG_GO_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for the GNU Go compiler
+# are suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_GO_CONFIG],
+[AC_REQUIRE([LT_PROG_GO])dnl
+AC_LANG_SAVE
+
+# Source file extension for Go test sources.
+ac_ext=go
+
+# Object file extension for compiled Go test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="package main; func main() { }"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='package main; func main() { }'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC=$CC
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=yes
+CC=${GOC-"gccgo"}
+CFLAGS=$GOFLAGS
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_TAGVAR(LD, $1)=$LD
+_LT_CC_BASENAME([$compiler])
+
+# Go did not exist at the time GCC didn't implicitly link libc in.
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+
+## CAVEAT EMPTOR:
+## There is no encapsulation within the following macros, do not change
+## the running order or otherwise move them around unless you know exactly
+## what you are doing...
+if test -n "$compiler"; then
+ _LT_COMPILER_NO_RTTI($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+fi
+
+AC_LANG_RESTORE
+
+GCC=$lt_save_GCC
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_GO_CONFIG
+
+
+# _LT_LANG_RC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for the Windows resource compiler
+# are suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_RC_CONFIG],
+[AC_REQUIRE([LT_PROG_RC])dnl
+AC_LANG_SAVE
+
+# Source file extension for RC test sources.
+ac_ext=rc
+
+# Object file extension for compiled RC test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }'
+
+# Code to be used in simple link tests
+lt_simple_link_test_code=$lt_simple_compile_test_code
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC=$CC
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=
+CC=${RC-"windres"}
+CFLAGS=
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_CC_BASENAME([$compiler])
+_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+
+if test -n "$compiler"; then
+ :
+ _LT_CONFIG($1)
+fi
+
+GCC=$lt_save_GCC
+AC_LANG_RESTORE
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_RC_CONFIG
+
+
+# LT_PROG_GCJ
+# -----------
+AC_DEFUN([LT_PROG_GCJ],
+[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ],
+ [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ],
+ [AC_CHECK_TOOL(GCJ, gcj,)
+ test set = "${GCJFLAGS+set}" || GCJFLAGS="-g -O2"
+ AC_SUBST(GCJFLAGS)])])[]dnl
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_GCJ], [])
+
+
+# LT_PROG_GO
+# ----------
+AC_DEFUN([LT_PROG_GO],
+[AC_CHECK_TOOL(GOC, gccgo,)
+])
+
+
+# LT_PROG_RC
+# ----------
+AC_DEFUN([LT_PROG_RC],
+[AC_CHECK_TOOL(RC, windres,)
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_RC], [])
+
+
+# _LT_DECL_EGREP
+# --------------
+# If we don't have a new enough Autoconf to choose the best grep
+# available, choose the one first in the user's PATH.
+m4_defun([_LT_DECL_EGREP],
+[AC_REQUIRE([AC_PROG_EGREP])dnl
+AC_REQUIRE([AC_PROG_FGREP])dnl
+test -z "$GREP" && GREP=grep
+_LT_DECL([], [GREP], [1], [A grep program that handles long lines])
+_LT_DECL([], [EGREP], [1], [An ERE matcher])
+_LT_DECL([], [FGREP], [1], [A literal string matcher])
+dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too
+AC_SUBST([GREP])
+])
+
+
+# _LT_DECL_OBJDUMP
+# --------------
+# If we don't have a new enough Autoconf to choose the best objdump
+# available, choose the one first in the user's PATH.
+m4_defun([_LT_DECL_OBJDUMP],
+[AC_CHECK_TOOL(OBJDUMP, objdump, false)
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper])
+AC_SUBST([OBJDUMP])
+])
+
+# _LT_DECL_DLLTOOL
+# ----------------
+# Ensure DLLTOOL variable is set.
+m4_defun([_LT_DECL_DLLTOOL],
+[AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+_LT_DECL([], [DLLTOOL], [1], [DLL creation program])
+AC_SUBST([DLLTOOL])
+])
+
+# _LT_DECL_SED
+# ------------
+# Check for a fully-functional sed program, that truncates
+# as few characters as possible. Prefer GNU sed if found.
+m4_defun([_LT_DECL_SED],
+[AC_PROG_SED
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+_LT_DECL([], [SED], [1], [A sed program that does not truncate output])
+_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"],
+ [Sed that helps us avoid accidentally triggering echo(1) options like -n])
+])# _LT_DECL_SED
+
+m4_ifndef([AC_PROG_SED], [
+############################################################
+# NOTE: This macro has been submitted for inclusion into #
+# GNU Autoconf as AC_PROG_SED. When it is available in #
+# a released version of Autoconf we should remove this #
+# macro and use it instead. #
+############################################################
+
+m4_defun([AC_PROG_SED],
+[AC_MSG_CHECKING([for a sed that does not truncate output])
+AC_CACHE_VAL(lt_cv_path_SED,
+[# Loop through the user's path and test for sed and gsed.
+# Then use that list of sed's as ones to test for truncation.
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for lt_ac_prog in sed gsed; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then
+ lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext"
+ fi
+ done
+ done
+done
+IFS=$as_save_IFS
+lt_ac_max=0
+lt_ac_count=0
+# Add /usr/xpg4/bin/sed as it is typically found on Solaris
+# along with /bin/sed that truncates output.
+for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do
+ test ! -f "$lt_ac_sed" && continue
+ cat /dev/null > conftest.in
+ lt_ac_count=0
+ echo $ECHO_N "0123456789$ECHO_C" >conftest.in
+ # Check for GNU sed and select it if it is found.
+ if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then
+ lt_cv_path_SED=$lt_ac_sed
+ break
+ fi
+ while true; do
+ cat conftest.in conftest.in >conftest.tmp
+ mv conftest.tmp conftest.in
+ cp conftest.in conftest.nl
+ echo >>conftest.nl
+ $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break
+ cmp -s conftest.out conftest.nl || break
+ # 10000 chars as input seems more than enough
+ test 10 -lt "$lt_ac_count" && break
+ lt_ac_count=`expr $lt_ac_count + 1`
+ if test "$lt_ac_count" -gt "$lt_ac_max"; then
+ lt_ac_max=$lt_ac_count
+ lt_cv_path_SED=$lt_ac_sed
+ fi
+ done
+done
+])
+SED=$lt_cv_path_SED
+AC_SUBST([SED])
+AC_MSG_RESULT([$SED])
+])#AC_PROG_SED
+])#m4_ifndef
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_SED], [])
+
+
+# _LT_CHECK_SHELL_FEATURES
+# ------------------------
+# Find out whether the shell is Bourne or XSI compatible,
+# or has some other useful features.
+m4_defun([_LT_CHECK_SHELL_FEATURES],
+[if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ lt_unset=unset
+else
+ lt_unset=false
+fi
+_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+ # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+ lt_SP2NL='tr \040 \012'
+ lt_NL2SP='tr \015\012 \040\040'
+ ;;
+ *) # EBCDIC based system
+ lt_SP2NL='tr \100 \n'
+ lt_NL2SP='tr \r\n \100\100'
+ ;;
+esac
+_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl
+_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl
+])# _LT_CHECK_SHELL_FEATURES
+
+
+# _LT_PATH_CONVERSION_FUNCTIONS
+# -----------------------------
+# Determine what file name conversion functions should be used by
+# func_to_host_file (and, implicitly, by func_to_host_path). These are needed
+# for certain cross-compile configurations and native mingw.
+m4_defun([_LT_PATH_CONVERSION_FUNCTIONS],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+AC_MSG_CHECKING([how to convert $build file names to $host format])
+AC_CACHE_VAL(lt_cv_to_host_file_cmd,
+[case $host in
+ *-*-mingw* )
+ case $build in
+ *-*-mingw* ) # actually msys
+ lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32
+ ;;
+ *-*-cygwin* )
+ lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32
+ ;;
+ * ) # otherwise, assume *nix
+ lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32
+ ;;
+ esac
+ ;;
+ *-*-cygwin* )
+ case $build in
+ *-*-mingw* ) # actually msys
+ lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin
+ ;;
+ *-*-cygwin* )
+ lt_cv_to_host_file_cmd=func_convert_file_noop
+ ;;
+ * ) # otherwise, assume *nix
+ lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin
+ ;;
+ esac
+ ;;
+ * ) # unhandled hosts (and "normal" native builds)
+ lt_cv_to_host_file_cmd=func_convert_file_noop
+ ;;
+esac
+])
+to_host_file_cmd=$lt_cv_to_host_file_cmd
+AC_MSG_RESULT([$lt_cv_to_host_file_cmd])
+_LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd],
+ [0], [convert $build file names to $host format])dnl
+
+AC_MSG_CHECKING([how to convert $build file names to toolchain format])
+AC_CACHE_VAL(lt_cv_to_tool_file_cmd,
+[#assume ordinary cross tools, or native build.
+lt_cv_to_tool_file_cmd=func_convert_file_noop
+case $host in
+ *-*-mingw* )
+ case $build in
+ *-*-mingw* ) # actually msys
+ lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32
+ ;;
+ esac
+ ;;
+esac
+])
+to_tool_file_cmd=$lt_cv_to_tool_file_cmd
+AC_MSG_RESULT([$lt_cv_to_tool_file_cmd])
+_LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd],
+ [0], [convert $build files to toolchain format])dnl
+])# _LT_PATH_CONVERSION_FUNCTIONS
diff --git a/build/ltmain.sh b/build/ltmain.sh
new file mode 100755
index 0000000..0f0a2da
--- /dev/null
+++ b/build/ltmain.sh
@@ -0,0 +1,11147 @@
+#! /bin/sh
+## DO NOT EDIT - This file generated from ./build-aux/ltmain.in
+## by inline-source v2014-01-03.01
+
+# libtool (GNU libtool) 2.4.6
+# Provide generalized library-building support services.
+# Written by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+
+# Copyright (C) 1996-2015 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+PROGRAM=libtool
+PACKAGE=libtool
+VERSION=2.4.6
+package_revision=2.4.6
+
+
+## ------ ##
+## Usage. ##
+## ------ ##
+
+# Run './libtool --help' for help with using this script from the
+# command line.
+
+
+## ------------------------------- ##
+## User overridable command paths. ##
+## ------------------------------- ##
+
+# After configure completes, it has a better idea of some of the
+# shell tools we need than the defaults used by the functions shared
+# with bootstrap, so set those here where they can still be over-
+# ridden by the user, but otherwise take precedence.
+
+: ${AUTOCONF="autoconf"}
+: ${AUTOMAKE="automake"}
+
+
+## -------------------------- ##
+## Source external libraries. ##
+## -------------------------- ##
+
+# Much of our low-level functionality needs to be sourced from external
+# libraries, which are installed to $pkgauxdir.
+
+# Set a version string for this script.
+scriptversion=2015-01-20.17; # UTC
+
+# General shell script boiler plate, and helper functions.
+# Written by Gary V. Vaughan, 2004
+
+# Copyright (C) 2004-2015 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# As a special exception to the GNU General Public License, if you distribute
+# this file as part of a program or library that is built using GNU Libtool,
+# you may include this file under the same distribution terms that you use
+# for the rest of that program.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNES FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Please report bugs or propose patches to gary@gnu.org.
+
+
+## ------ ##
+## Usage. ##
+## ------ ##
+
+# Evaluate this file near the top of your script to gain access to
+# the functions and variables defined here:
+#
+# . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh
+#
+# If you need to override any of the default environment variable
+# settings, do that before evaluating this file.
+
+
+## -------------------- ##
+## Shell normalisation. ##
+## -------------------- ##
+
+# Some shells need a little help to be as Bourne compatible as possible.
+# Before doing anything else, make sure all that help has been provided!
+
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac
+fi
+
+# NLS nuisances: We save the old values in case they are required later.
+_G_user_locale=
+_G_safe_locale=
+for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+do
+ eval "if test set = \"\${$_G_var+set}\"; then
+ save_$_G_var=\$$_G_var
+ $_G_var=C
+ export $_G_var
+ _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\"
+ _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\"
+ fi"
+done
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Make sure IFS has a sensible default
+sp=' '
+nl='
+'
+IFS="$sp $nl"
+
+# There are apparently some retarded systems that use ';' as a PATH separator!
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+
+## ------------------------- ##
+## Locate command utilities. ##
+## ------------------------- ##
+
+
+# func_executable_p FILE
+# ----------------------
+# Check that FILE is an executable regular file.
+func_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+}
+
+
+# func_path_progs PROGS_LIST CHECK_FUNC [PATH]
+# --------------------------------------------
+# Search for either a program that responds to --version with output
+# containing "GNU", or else returned by CHECK_FUNC otherwise, by
+# trying all the directories in PATH with each of the elements of
+# PROGS_LIST.
+#
+# CHECK_FUNC should accept the path to a candidate program, and
+# set $func_check_prog_result if it truncates its output less than
+# $_G_path_prog_max characters.
+func_path_progs ()
+{
+ _G_progs_list=$1
+ _G_check_func=$2
+ _G_PATH=${3-"$PATH"}
+
+ _G_path_prog_max=0
+ _G_path_prog_found=false
+ _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:}
+ for _G_dir in $_G_PATH; do
+ IFS=$_G_save_IFS
+ test -z "$_G_dir" && _G_dir=.
+ for _G_prog_name in $_G_progs_list; do
+ for _exeext in '' .EXE; do
+ _G_path_prog=$_G_dir/$_G_prog_name$_exeext
+ func_executable_p "$_G_path_prog" || continue
+ case `"$_G_path_prog" --version 2>&1` in
+ *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;;
+ *) $_G_check_func $_G_path_prog
+ func_path_progs_result=$func_check_prog_result
+ ;;
+ esac
+ $_G_path_prog_found && break 3
+ done
+ done
+ done
+ IFS=$_G_save_IFS
+ test -z "$func_path_progs_result" && {
+ echo "no acceptable sed could be found in \$PATH" >&2
+ exit 1
+ }
+}
+
+
+# We want to be able to use the functions in this file before configure
+# has figured out where the best binaries are kept, which means we have
+# to search for them ourselves - except when the results are already set
+# where we skip the searches.
+
+# Unless the user overrides by setting SED, search the path for either GNU
+# sed, or the sed that truncates its output the least.
+test -z "$SED" && {
+ _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+ for _G_i in 1 2 3 4 5 6 7; do
+ _G_sed_script=$_G_sed_script$nl$_G_sed_script
+ done
+ echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed
+ _G_sed_script=
+
+ func_check_prog_sed ()
+ {
+ _G_path_prog=$1
+
+ _G_count=0
+ printf 0123456789 >conftest.in
+ while :
+ do
+ cat conftest.in conftest.in >conftest.tmp
+ mv conftest.tmp conftest.in
+ cp conftest.in conftest.nl
+ echo '' >> conftest.nl
+ "$_G_path_prog" -f conftest.sed <conftest.nl >conftest.out 2>/dev/null || break
+ diff conftest.out conftest.nl >/dev/null 2>&1 || break
+ _G_count=`expr $_G_count + 1`
+ if test "$_G_count" -gt "$_G_path_prog_max"; then
+ # Best one so far, save it but keep looking for a better one
+ func_check_prog_result=$_G_path_prog
+ _G_path_prog_max=$_G_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test 10 -lt "$_G_count" && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out
+ }
+
+ func_path_progs "sed gsed" func_check_prog_sed $PATH:/usr/xpg4/bin
+ rm -f conftest.sed
+ SED=$func_path_progs_result
+}
+
+
+# Unless the user overrides by setting GREP, search the path for either GNU
+# grep, or the grep that truncates its output the least.
+test -z "$GREP" && {
+ func_check_prog_grep ()
+ {
+ _G_path_prog=$1
+
+ _G_count=0
+ _G_path_prog_max=0
+ printf 0123456789 >conftest.in
+ while :
+ do
+ cat conftest.in conftest.in >conftest.tmp
+ mv conftest.tmp conftest.in
+ cp conftest.in conftest.nl
+ echo 'GREP' >> conftest.nl
+ "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' <conftest.nl >conftest.out 2>/dev/null || break
+ diff conftest.out conftest.nl >/dev/null 2>&1 || break
+ _G_count=`expr $_G_count + 1`
+ if test "$_G_count" -gt "$_G_path_prog_max"; then
+ # Best one so far, save it but keep looking for a better one
+ func_check_prog_result=$_G_path_prog
+ _G_path_prog_max=$_G_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test 10 -lt "$_G_count" && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out
+ }
+
+ func_path_progs "grep ggrep" func_check_prog_grep $PATH:/usr/xpg4/bin
+ GREP=$func_path_progs_result
+}
+
+
+## ------------------------------- ##
+## User overridable command paths. ##
+## ------------------------------- ##
+
+# All uppercase variable names are used for environment variables. These
+# variables can be overridden by the user before calling a script that
+# uses them if a suitable command of that name is not already available
+# in the command search PATH.
+
+: ${CP="cp -f"}
+: ${ECHO="printf %s\n"}
+: ${EGREP="$GREP -E"}
+: ${FGREP="$GREP -F"}
+: ${LN_S="ln -s"}
+: ${MAKE="make"}
+: ${MKDIR="mkdir"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+: ${SHELL="${CONFIG_SHELL-/bin/sh}"}
+
+
+## -------------------- ##
+## Useful sed snippets. ##
+## -------------------- ##
+
+sed_dirname='s|/[^/]*$||'
+sed_basename='s|^.*/||'
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s|\([`"$\\]\)|\\\1|g'
+
+# Same as above, but do not quote variable references.
+sed_double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Sed substitution that turns a string into a regex matching for the
+# string literally.
+sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g'
+
+# Sed substitution that converts a w32 file name or path
+# that contains forward slashes, into one that contains
+# (escaped) backslashes. A very naive implementation.
+sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g'
+
+# Re-'\' parameter expansions in output of sed_double_quote_subst that
+# were '\'-ed in input to the same. If an odd number of '\' preceded a
+# '$' in input to sed_double_quote_subst, that '$' was protected from
+# expansion. Since each input '\' is now two '\'s, look for any number
+# of runs of four '\'s followed by two '\'s and then a '$'. '\' that '$'.
+_G_bs='\\'
+_G_bs2='\\\\'
+_G_bs4='\\\\\\\\'
+_G_dollar='\$'
+sed_double_backslash="\
+ s/$_G_bs4/&\\
+/g
+ s/^$_G_bs2$_G_dollar/$_G_bs&/
+ s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g
+ s/\n//g"
+
+
+## ----------------- ##
+## Global variables. ##
+## ----------------- ##
+
+# Except for the global variables explicitly listed below, the following
+# functions in the '^func_' namespace, and the '^require_' namespace
+# variables initialised in the 'Resource management' section, sourcing
+# this file will not pollute your global namespace with anything
+# else. There's no portable way to scope variables in Bourne shell
+# though, so actually running these functions will sometimes place
+# results into a variable named after the function, and often use
+# temporary variables in the '^_G_' namespace. If you are careful to
+# avoid using those namespaces casually in your sourcing script, things
+# should continue to work as you expect. And, of course, you can freely
+# overwrite any of the functions or variables defined here before
+# calling anything to customize them.
+
+EXIT_SUCCESS=0
+EXIT_FAILURE=1
+EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing.
+EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake.
+
+# Allow overriding, eg assuming that you follow the convention of
+# putting '$debug_cmd' at the start of all your functions, you can get
+# bash to show function call trace with:
+#
+# debug_cmd='eval echo "${FUNCNAME[0]} $*" >&2' bash your-script-name
+debug_cmd=${debug_cmd-":"}
+exit_cmd=:
+
+# By convention, finish your script with:
+#
+# exit $exit_status
+#
+# so that you can set exit_status to non-zero if you want to indicate
+# something went wrong during execution without actually bailing out at
+# the point of failure.
+exit_status=$EXIT_SUCCESS
+
+# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh
+# is ksh but when the shell is invoked as "sh" and the current value of
+# the _XPG environment variable is not equal to 1 (one), the special
+# positional parameter $0, within a function call, is the name of the
+# function.
+progpath=$0
+
+# The name of this program.
+progname=`$ECHO "$progpath" |$SED "$sed_basename"`
+
+# Make sure we have an absolute progpath for reexecution:
+case $progpath in
+ [\\/]*|[A-Za-z]:\\*) ;;
+ *[\\/]*)
+ progdir=`$ECHO "$progpath" |$SED "$sed_dirname"`
+ progdir=`cd "$progdir" && pwd`
+ progpath=$progdir/$progname
+ ;;
+ *)
+ _G_IFS=$IFS
+ IFS=${PATH_SEPARATOR-:}
+ for progdir in $PATH; do
+ IFS=$_G_IFS
+ test -x "$progdir/$progname" && break
+ done
+ IFS=$_G_IFS
+ test -n "$progdir" || progdir=`pwd`
+ progpath=$progdir/$progname
+ ;;
+esac
+
+
+## ----------------- ##
+## Standard options. ##
+## ----------------- ##
+
+# The following options affect the operation of the functions defined
+# below, and should be set appropriately depending on run-time para-
+# meters passed on the command line.
+
+opt_dry_run=false
+opt_quiet=false
+opt_verbose=false
+
+# Categories 'all' and 'none' are always available. Append any others
+# you will pass as the first argument to func_warning from your own
+# code.
+warning_categories=
+
+# By default, display warnings according to 'opt_warning_types'. Set
+# 'warning_func' to ':' to elide all warnings, or func_fatal_error to
+# treat the next displayed warning as a fatal error.
+warning_func=func_warn_and_continue
+
+# Set to 'all' to display all warnings, 'none' to suppress all
+# warnings, or a space delimited list of some subset of
+# 'warning_categories' to display only the listed warnings.
+opt_warning_types=all
+
+
+## -------------------- ##
+## Resource management. ##
+## -------------------- ##
+
+# This section contains definitions for functions that each ensure a
+# particular resource (a file, or a non-empty configuration variable for
+# example) is available, and if appropriate to extract default values
+# from pertinent package files. Call them using their associated
+# 'require_*' variable to ensure that they are executed, at most, once.
+#
+# It's entirely deliberate that calling these functions can set
+# variables that don't obey the namespace limitations obeyed by the rest
+# of this file, in order that that they be as useful as possible to
+# callers.
+
+
+# require_term_colors
+# -------------------
+# Allow display of bold text on terminals that support it.
+require_term_colors=func_require_term_colors
+func_require_term_colors ()
+{
+ $debug_cmd
+
+ test -t 1 && {
+ # COLORTERM and USE_ANSI_COLORS environment variables take
+ # precedence, because most terminfo databases neglect to describe
+ # whether color sequences are supported.
+ test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"}
+
+ if test 1 = "$USE_ANSI_COLORS"; then
+ # Standard ANSI escape sequences
+ tc_reset=''
+ tc_bold=''; tc_standout=''
+ tc_red=''; tc_green=''
+ tc_blue=''; tc_cyan=''
+ else
+ # Otherwise trust the terminfo database after all.
+ test -n "`tput sgr0 2>/dev/null`" && {
+ tc_reset=`tput sgr0`
+ test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold`
+ tc_standout=$tc_bold
+ test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso`
+ test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1`
+ test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2`
+ test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4`
+ test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5`
+ }
+ fi
+ }
+
+ require_term_colors=:
+}
+
+
+## ----------------- ##
+## Function library. ##
+## ----------------- ##
+
+# This section contains a variety of useful functions to call in your
+# scripts. Take note of the portable wrappers for features provided by
+# some modern shells, which will fall back to slower equivalents on
+# less featureful shells.
+
+
+# func_append VAR VALUE
+# ---------------------
+# Append VALUE onto the existing contents of VAR.
+
+ # We should try to minimise forks, especially on Windows where they are
+ # unreasonably slow, so skip the feature probes when bash or zsh are
+ # being used:
+ if test set = "${BASH_VERSION+set}${ZSH_VERSION+set}"; then
+ : ${_G_HAVE_ARITH_OP="yes"}
+ : ${_G_HAVE_XSI_OPS="yes"}
+ # The += operator was introduced in bash 3.1
+ case $BASH_VERSION in
+ [12].* | 3.0 | 3.0*) ;;
+ *)
+ : ${_G_HAVE_PLUSEQ_OP="yes"}
+ ;;
+ esac
+ fi
+
+ # _G_HAVE_PLUSEQ_OP
+ # Can be empty, in which case the shell is probed, "yes" if += is
+ # useable or anything else if it does not work.
+ test -z "$_G_HAVE_PLUSEQ_OP" \
+ && (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \
+ && _G_HAVE_PLUSEQ_OP=yes
+
+if test yes = "$_G_HAVE_PLUSEQ_OP"
+then
+ # This is an XSI compatible shell, allowing a faster implementation...
+ eval 'func_append ()
+ {
+ $debug_cmd
+
+ eval "$1+=\$2"
+ }'
+else
+ # ...otherwise fall back to using expr, which is often a shell builtin.
+ func_append ()
+ {
+ $debug_cmd
+
+ eval "$1=\$$1\$2"
+ }
+fi
+
+
+# func_append_quoted VAR VALUE
+# ----------------------------
+# Quote VALUE and append to the end of shell variable VAR, separated
+# by a space.
+if test yes = "$_G_HAVE_PLUSEQ_OP"; then
+ eval 'func_append_quoted ()
+ {
+ $debug_cmd
+
+ func_quote_for_eval "$2"
+ eval "$1+=\\ \$func_quote_for_eval_result"
+ }'
+else
+ func_append_quoted ()
+ {
+ $debug_cmd
+
+ func_quote_for_eval "$2"
+ eval "$1=\$$1\\ \$func_quote_for_eval_result"
+ }
+fi
+
+
+# func_append_uniq VAR VALUE
+# --------------------------
+# Append unique VALUE onto the existing contents of VAR, assuming
+# entries are delimited by the first character of VALUE. For example:
+#
+# func_append_uniq options " --another-option option-argument"
+#
+# will only append to $options if " --another-option option-argument "
+# is not already present somewhere in $options already (note spaces at
+# each end implied by leading space in second argument).
+func_append_uniq ()
+{
+ $debug_cmd
+
+ eval _G_current_value='`$ECHO $'$1'`'
+ _G_delim=`expr "$2" : '\(.\)'`
+
+ case $_G_delim$_G_current_value$_G_delim in
+ *"$2$_G_delim"*) ;;
+ *) func_append "$@" ;;
+ esac
+}
+
+
+# func_arith TERM...
+# ------------------
+# Set func_arith_result to the result of evaluating TERMs.
+ test -z "$_G_HAVE_ARITH_OP" \
+ && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \
+ && _G_HAVE_ARITH_OP=yes
+
+if test yes = "$_G_HAVE_ARITH_OP"; then
+ eval 'func_arith ()
+ {
+ $debug_cmd
+
+ func_arith_result=$(( $* ))
+ }'
+else
+ func_arith ()
+ {
+ $debug_cmd
+
+ func_arith_result=`expr "$@"`
+ }
+fi
+
+
+# func_basename FILE
+# ------------------
+# Set func_basename_result to FILE with everything up to and including
+# the last / stripped.
+if test yes = "$_G_HAVE_XSI_OPS"; then
+ # If this shell supports suffix pattern removal, then use it to avoid
+ # forking. Hide the definitions single quotes in case the shell chokes
+ # on unsupported syntax...
+ _b='func_basename_result=${1##*/}'
+ _d='case $1 in
+ */*) func_dirname_result=${1%/*}$2 ;;
+ * ) func_dirname_result=$3 ;;
+ esac'
+
+else
+ # ...otherwise fall back to using sed.
+ _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`'
+ _d='func_dirname_result=`$ECHO "$1" |$SED "$sed_dirname"`
+ if test "X$func_dirname_result" = "X$1"; then
+ func_dirname_result=$3
+ else
+ func_append func_dirname_result "$2"
+ fi'
+fi
+
+eval 'func_basename ()
+{
+ $debug_cmd
+
+ '"$_b"'
+}'
+
+
+# func_dirname FILE APPEND NONDIR_REPLACEMENT
+# -------------------------------------------
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+eval 'func_dirname ()
+{
+ $debug_cmd
+
+ '"$_d"'
+}'
+
+
+# func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT
+# --------------------------------------------------------
+# Perform func_basename and func_dirname in a single function
+# call:
+# dirname: Compute the dirname of FILE. If nonempty,
+# add APPEND to the result, otherwise set result
+# to NONDIR_REPLACEMENT.
+# value returned in "$func_dirname_result"
+# basename: Compute filename of FILE.
+# value retuned in "$func_basename_result"
+# For efficiency, we do not delegate to the functions above but instead
+# duplicate the functionality here.
+eval 'func_dirname_and_basename ()
+{
+ $debug_cmd
+
+ '"$_b"'
+ '"$_d"'
+}'
+
+
+# func_echo ARG...
+# ----------------
+# Echo program name prefixed message.
+func_echo ()
+{
+ $debug_cmd
+
+ _G_message=$*
+
+ func_echo_IFS=$IFS
+ IFS=$nl
+ for _G_line in $_G_message; do
+ IFS=$func_echo_IFS
+ $ECHO "$progname: $_G_line"
+ done
+ IFS=$func_echo_IFS
+}
+
+
+# func_echo_all ARG...
+# --------------------
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+ $ECHO "$*"
+}
+
+
+# func_echo_infix_1 INFIX ARG...
+# ------------------------------
+# Echo program name, followed by INFIX on the first line, with any
+# additional lines not showing INFIX.
+func_echo_infix_1 ()
+{
+ $debug_cmd
+
+ $require_term_colors
+
+ _G_infix=$1; shift
+ _G_indent=$_G_infix
+ _G_prefix="$progname: $_G_infix: "
+ _G_message=$*
+
+ # Strip color escape sequences before counting printable length
+ for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan"
+ do
+ test -n "$_G_tc" && {
+ _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"`
+ _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"`
+ }
+ done
+ _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`" " ## exclude from sc_prohibit_nested_quotes
+
+ func_echo_infix_1_IFS=$IFS
+ IFS=$nl
+ for _G_line in $_G_message; do
+ IFS=$func_echo_infix_1_IFS
+ $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2
+ _G_prefix=$_G_indent
+ done
+ IFS=$func_echo_infix_1_IFS
+}
+
+
+# func_error ARG...
+# -----------------
+# Echo program name prefixed message to standard error.
+func_error ()
+{
+ $debug_cmd
+
+ $require_term_colors
+
+ func_echo_infix_1 " $tc_standout${tc_red}error$tc_reset" "$*" >&2
+}
+
+
+# func_fatal_error ARG...
+# -----------------------
+# Echo program name prefixed message to standard error, and exit.
+func_fatal_error ()
+{
+ $debug_cmd
+
+ func_error "$*"
+ exit $EXIT_FAILURE
+}
+
+
+# func_grep EXPRESSION FILENAME
+# -----------------------------
+# Check whether EXPRESSION matches any line of FILENAME, without output.
+func_grep ()
+{
+ $debug_cmd
+
+ $GREP "$1" "$2" >/dev/null 2>&1
+}
+
+
+# func_len STRING
+# ---------------
+# Set func_len_result to the length of STRING. STRING may not
+# start with a hyphen.
+ test -z "$_G_HAVE_XSI_OPS" \
+ && (eval 'x=a/b/c;
+ test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \
+ && _G_HAVE_XSI_OPS=yes
+
+if test yes = "$_G_HAVE_XSI_OPS"; then
+ eval 'func_len ()
+ {
+ $debug_cmd
+
+ func_len_result=${#1}
+ }'
+else
+ func_len ()
+ {
+ $debug_cmd
+
+ func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len`
+ }
+fi
+
+
+# func_mkdir_p DIRECTORY-PATH
+# ---------------------------
+# Make sure the entire path to DIRECTORY-PATH is available.
+func_mkdir_p ()
+{
+ $debug_cmd
+
+ _G_directory_path=$1
+ _G_dir_list=
+
+ if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then
+
+ # Protect directory names starting with '-'
+ case $_G_directory_path in
+ -*) _G_directory_path=./$_G_directory_path ;;
+ esac
+
+ # While some portion of DIR does not yet exist...
+ while test ! -d "$_G_directory_path"; do
+ # ...make a list in topmost first order. Use a colon delimited
+ # list incase some portion of path contains whitespace.
+ _G_dir_list=$_G_directory_path:$_G_dir_list
+
+ # If the last portion added has no slash in it, the list is done
+ case $_G_directory_path in */*) ;; *) break ;; esac
+
+ # ...otherwise throw away the child directory and loop
+ _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"`
+ done
+ _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'`
+
+ func_mkdir_p_IFS=$IFS; IFS=:
+ for _G_dir in $_G_dir_list; do
+ IFS=$func_mkdir_p_IFS
+ # mkdir can fail with a 'File exist' error if two processes
+ # try to create one of the directories concurrently. Don't
+ # stop in that case!
+ $MKDIR "$_G_dir" 2>/dev/null || :
+ done
+ IFS=$func_mkdir_p_IFS
+
+ # Bail out if we (or some other process) failed to create a directory.
+ test -d "$_G_directory_path" || \
+ func_fatal_error "Failed to create '$1'"
+ fi
+}
+
+
+# func_mktempdir [BASENAME]
+# -------------------------
+# Make a temporary directory that won't clash with other running
+# libtool processes, and avoids race conditions if possible. If
+# given, BASENAME is the basename for that directory.
+func_mktempdir ()
+{
+ $debug_cmd
+
+ _G_template=${TMPDIR-/tmp}/${1-$progname}
+
+ if test : = "$opt_dry_run"; then
+ # Return a directory name, but don't create it in dry-run mode
+ _G_tmpdir=$_G_template-$$
+ else
+
+ # If mktemp works, use that first and foremost
+ _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null`
+
+ if test ! -d "$_G_tmpdir"; then
+ # Failing that, at least try and use $RANDOM to avoid a race
+ _G_tmpdir=$_G_template-${RANDOM-0}$$
+
+ func_mktempdir_umask=`umask`
+ umask 0077
+ $MKDIR "$_G_tmpdir"
+ umask $func_mktempdir_umask
+ fi
+
+ # If we're not in dry-run mode, bomb out on failure
+ test -d "$_G_tmpdir" || \
+ func_fatal_error "cannot create temporary directory '$_G_tmpdir'"
+ fi
+
+ $ECHO "$_G_tmpdir"
+}
+
+
+# func_normal_abspath PATH
+# ------------------------
+# Remove doubled-up and trailing slashes, "." path components,
+# and cancel out any ".." path components in PATH after making
+# it an absolute path.
+func_normal_abspath ()
+{
+ $debug_cmd
+
+ # These SED scripts presuppose an absolute path with a trailing slash.
+ _G_pathcar='s|^/\([^/]*\).*$|\1|'
+ _G_pathcdr='s|^/[^/]*||'
+ _G_removedotparts=':dotsl
+ s|/\./|/|g
+ t dotsl
+ s|/\.$|/|'
+ _G_collapseslashes='s|/\{1,\}|/|g'
+ _G_finalslash='s|/*$|/|'
+
+ # Start from root dir and reassemble the path.
+ func_normal_abspath_result=
+ func_normal_abspath_tpath=$1
+ func_normal_abspath_altnamespace=
+ case $func_normal_abspath_tpath in
+ "")
+ # Empty path, that just means $cwd.
+ func_stripname '' '/' "`pwd`"
+ func_normal_abspath_result=$func_stripname_result
+ return
+ ;;
+ # The next three entries are used to spot a run of precisely
+ # two leading slashes without using negated character classes;
+ # we take advantage of case's first-match behaviour.
+ ///*)
+ # Unusual form of absolute path, do nothing.
+ ;;
+ //*)
+ # Not necessarily an ordinary path; POSIX reserves leading '//'
+ # and for example Cygwin uses it to access remote file shares
+ # over CIFS/SMB, so we conserve a leading double slash if found.
+ func_normal_abspath_altnamespace=/
+ ;;
+ /*)
+ # Absolute path, do nothing.
+ ;;
+ *)
+ # Relative path, prepend $cwd.
+ func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath
+ ;;
+ esac
+
+ # Cancel out all the simple stuff to save iterations. We also want
+ # the path to end with a slash for ease of parsing, so make sure
+ # there is one (and only one) here.
+ func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
+ -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"`
+ while :; do
+ # Processed it all yet?
+ if test / = "$func_normal_abspath_tpath"; then
+ # If we ascended to the root using ".." the result may be empty now.
+ if test -z "$func_normal_abspath_result"; then
+ func_normal_abspath_result=/
+ fi
+ break
+ fi
+ func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \
+ -e "$_G_pathcar"`
+ func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
+ -e "$_G_pathcdr"`
+ # Figure out what to do with it
+ case $func_normal_abspath_tcomponent in
+ "")
+ # Trailing empty path component, ignore it.
+ ;;
+ ..)
+ # Parent dir; strip last assembled component from result.
+ func_dirname "$func_normal_abspath_result"
+ func_normal_abspath_result=$func_dirname_result
+ ;;
+ *)
+ # Actual path component, append it.
+ func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent"
+ ;;
+ esac
+ done
+ # Restore leading double-slash if one was found on entry.
+ func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result
+}
+
+
+# func_notquiet ARG...
+# --------------------
+# Echo program name prefixed message only when not in quiet mode.
+func_notquiet ()
+{
+ $debug_cmd
+
+ $opt_quiet || func_echo ${1+"$@"}
+
+ # A bug in bash halts the script if the last line of a function
+ # fails when set -e is in force, so we need another command to
+ # work around that:
+ :
+}
+
+
+# func_relative_path SRCDIR DSTDIR
+# --------------------------------
+# Set func_relative_path_result to the relative path from SRCDIR to DSTDIR.
+func_relative_path ()
+{
+ $debug_cmd
+
+ func_relative_path_result=
+ func_normal_abspath "$1"
+ func_relative_path_tlibdir=$func_normal_abspath_result
+ func_normal_abspath "$2"
+ func_relative_path_tbindir=$func_normal_abspath_result
+
+ # Ascend the tree starting from libdir
+ while :; do
+ # check if we have found a prefix of bindir
+ case $func_relative_path_tbindir in
+ $func_relative_path_tlibdir)
+ # found an exact match
+ func_relative_path_tcancelled=
+ break
+ ;;
+ $func_relative_path_tlibdir*)
+ # found a matching prefix
+ func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir"
+ func_relative_path_tcancelled=$func_stripname_result
+ if test -z "$func_relative_path_result"; then
+ func_relative_path_result=.
+ fi
+ break
+ ;;
+ *)
+ func_dirname $func_relative_path_tlibdir
+ func_relative_path_tlibdir=$func_dirname_result
+ if test -z "$func_relative_path_tlibdir"; then
+ # Have to descend all the way to the root!
+ func_relative_path_result=../$func_relative_path_result
+ func_relative_path_tcancelled=$func_relative_path_tbindir
+ break
+ fi
+ func_relative_path_result=../$func_relative_path_result
+ ;;
+ esac
+ done
+
+ # Now calculate path; take care to avoid doubling-up slashes.
+ func_stripname '' '/' "$func_relative_path_result"
+ func_relative_path_result=$func_stripname_result
+ func_stripname '/' '/' "$func_relative_path_tcancelled"
+ if test -n "$func_stripname_result"; then
+ func_append func_relative_path_result "/$func_stripname_result"
+ fi
+
+ # Normalisation. If bindir is libdir, return '.' else relative path.
+ if test -n "$func_relative_path_result"; then
+ func_stripname './' '' "$func_relative_path_result"
+ func_relative_path_result=$func_stripname_result
+ fi
+
+ test -n "$func_relative_path_result" || func_relative_path_result=.
+
+ :
+}
+
+
+# func_quote_for_eval ARG...
+# --------------------------
+# Aesthetically quote ARGs to be evaled later.
+# This function returns two values:
+# i) func_quote_for_eval_result
+# double-quoted, suitable for a subsequent eval
+# ii) func_quote_for_eval_unquoted_result
+# has all characters that are still active within double
+# quotes backslashified.
+func_quote_for_eval ()
+{
+ $debug_cmd
+
+ func_quote_for_eval_unquoted_result=
+ func_quote_for_eval_result=
+ while test 0 -lt $#; do
+ case $1 in
+ *[\\\`\"\$]*)
+ _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;;
+ *)
+ _G_unquoted_arg=$1 ;;
+ esac
+ if test -n "$func_quote_for_eval_unquoted_result"; then
+ func_append func_quote_for_eval_unquoted_result " $_G_unquoted_arg"
+ else
+ func_append func_quote_for_eval_unquoted_result "$_G_unquoted_arg"
+ fi
+
+ case $_G_unquoted_arg in
+ # Double-quote args containing shell metacharacters to delay
+ # word splitting, command substitution and variable expansion
+ # for a subsequent eval.
+ # Many Bourne shells cannot handle close brackets correctly
+ # in scan sets, so we specify it separately.
+ *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"")
+ _G_quoted_arg=\"$_G_unquoted_arg\"
+ ;;
+ *)
+ _G_quoted_arg=$_G_unquoted_arg
+ ;;
+ esac
+
+ if test -n "$func_quote_for_eval_result"; then
+ func_append func_quote_for_eval_result " $_G_quoted_arg"
+ else
+ func_append func_quote_for_eval_result "$_G_quoted_arg"
+ fi
+ shift
+ done
+}
+
+
+# func_quote_for_expand ARG
+# -------------------------
+# Aesthetically quote ARG to be evaled later; same as above,
+# but do not quote variable references.
+func_quote_for_expand ()
+{
+ $debug_cmd
+
+ case $1 in
+ *[\\\`\"]*)
+ _G_arg=`$ECHO "$1" | $SED \
+ -e "$sed_double_quote_subst" -e "$sed_double_backslash"` ;;
+ *)
+ _G_arg=$1 ;;
+ esac
+
+ case $_G_arg in
+ # Double-quote args containing shell metacharacters to delay
+ # word splitting and command substitution for a subsequent eval.
+ # Many Bourne shells cannot handle close brackets correctly
+ # in scan sets, so we specify it separately.
+ *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"")
+ _G_arg=\"$_G_arg\"
+ ;;
+ esac
+
+ func_quote_for_expand_result=$_G_arg
+}
+
+
+# func_stripname PREFIX SUFFIX NAME
+# ---------------------------------
+# strip PREFIX and SUFFIX from NAME, and store in func_stripname_result.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+if test yes = "$_G_HAVE_XSI_OPS"; then
+ eval 'func_stripname ()
+ {
+ $debug_cmd
+
+ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
+ # positional parameters, so assign one to ordinary variable first.
+ func_stripname_result=$3
+ func_stripname_result=${func_stripname_result#"$1"}
+ func_stripname_result=${func_stripname_result%"$2"}
+ }'
+else
+ func_stripname ()
+ {
+ $debug_cmd
+
+ case $2 in
+ .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;;
+ *) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;;
+ esac
+ }
+fi
+
+
+# func_show_eval CMD [FAIL_EXP]
+# -----------------------------
+# Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is
+# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it.
+func_show_eval ()
+{
+ $debug_cmd
+
+ _G_cmd=$1
+ _G_fail_exp=${2-':'}
+
+ func_quote_for_expand "$_G_cmd"
+ eval "func_notquiet $func_quote_for_expand_result"
+
+ $opt_dry_run || {
+ eval "$_G_cmd"
+ _G_status=$?
+ if test 0 -ne "$_G_status"; then
+ eval "(exit $_G_status); $_G_fail_exp"
+ fi
+ }
+}
+
+
+# func_show_eval_locale CMD [FAIL_EXP]
+# ------------------------------------
+# Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is
+# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it. Use the saved locale for evaluation.
+func_show_eval_locale ()
+{
+ $debug_cmd
+
+ _G_cmd=$1
+ _G_fail_exp=${2-':'}
+
+ $opt_quiet || {
+ func_quote_for_expand "$_G_cmd"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+
+ $opt_dry_run || {
+ eval "$_G_user_locale
+ $_G_cmd"
+ _G_status=$?
+ eval "$_G_safe_locale"
+ if test 0 -ne "$_G_status"; then
+ eval "(exit $_G_status); $_G_fail_exp"
+ fi
+ }
+}
+
+
+# func_tr_sh
+# ----------
+# Turn $1 into a string suitable for a shell variable name.
+# Result is stored in $func_tr_sh_result. All characters
+# not in the set a-zA-Z0-9_ are replaced with '_'. Further,
+# if $1 begins with a digit, a '_' is prepended as well.
+func_tr_sh ()
+{
+ $debug_cmd
+
+ case $1 in
+ [0-9]* | *[!a-zA-Z0-9_]*)
+ func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'`
+ ;;
+ * )
+ func_tr_sh_result=$1
+ ;;
+ esac
+}
+
+
+# func_verbose ARG...
+# -------------------
+# Echo program name prefixed message in verbose mode only.
+func_verbose ()
+{
+ $debug_cmd
+
+ $opt_verbose && func_echo "$*"
+
+ :
+}
+
+
+# func_warn_and_continue ARG...
+# -----------------------------
+# Echo program name prefixed warning message to standard error.
+func_warn_and_continue ()
+{
+ $debug_cmd
+
+ $require_term_colors
+
+ func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2
+}
+
+
+# func_warning CATEGORY ARG...
+# ----------------------------
+# Echo program name prefixed warning message to standard error. Warning
+# messages can be filtered according to CATEGORY, where this function
+# elides messages where CATEGORY is not listed in the global variable
+# 'opt_warning_types'.
+func_warning ()
+{
+ $debug_cmd
+
+ # CATEGORY must be in the warning_categories list!
+ case " $warning_categories " in
+ *" $1 "*) ;;
+ *) func_internal_error "invalid warning category '$1'" ;;
+ esac
+
+ _G_category=$1
+ shift
+
+ case " $opt_warning_types " in
+ *" $_G_category "*) $warning_func ${1+"$@"} ;;
+ esac
+}
+
+
+# func_sort_ver VER1 VER2
+# -----------------------
+# 'sort -V' is not generally available.
+# Note this deviates from the version comparison in automake
+# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a
+# but this should suffice as we won't be specifying old
+# version formats or redundant trailing .0 in bootstrap.conf.
+# If we did want full compatibility then we should probably
+# use m4_version_compare from autoconf.
+func_sort_ver ()
+{
+ $debug_cmd
+
+ printf '%s\n%s\n' "$1" "$2" \
+ | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n
+}
+
+# func_lt_ver PREV CURR
+# ---------------------
+# Return true if PREV and CURR are in the correct order according to
+# func_sort_ver, otherwise false. Use it like this:
+#
+# func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..."
+func_lt_ver ()
+{
+ $debug_cmd
+
+ test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q`
+}
+
+
+# Local variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC"
+# time-stamp-time-zone: "UTC"
+# End:
+#! /bin/sh
+
+# Set a version string for this script.
+scriptversion=2014-01-07.03; # UTC
+
+# A portable, pluggable option parser for Bourne shell.
+# Written by Gary V. Vaughan, 2010
+
+# Copyright (C) 2010-2015 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Please report bugs or propose patches to gary@gnu.org.
+
+
+## ------ ##
+## Usage. ##
+## ------ ##
+
+# This file is a library for parsing options in your shell scripts along
+# with assorted other useful supporting features that you can make use
+# of too.
+#
+# For the simplest scripts you might need only:
+#
+# #!/bin/sh
+# . relative/path/to/funclib.sh
+# . relative/path/to/options-parser
+# scriptversion=1.0
+# func_options ${1+"$@"}
+# eval set dummy "$func_options_result"; shift
+# ...rest of your script...
+#
+# In order for the '--version' option to work, you will need to have a
+# suitably formatted comment like the one at the top of this file
+# starting with '# Written by ' and ending with '# warranty; '.
+#
+# For '-h' and '--help' to work, you will also need a one line
+# description of your script's purpose in a comment directly above the
+# '# Written by ' line, like the one at the top of this file.
+#
+# The default options also support '--debug', which will turn on shell
+# execution tracing (see the comment above debug_cmd below for another
+# use), and '--verbose' and the func_verbose function to allow your script
+# to display verbose messages only when your user has specified
+# '--verbose'.
+#
+# After sourcing this file, you can plug processing for additional
+# options by amending the variables from the 'Configuration' section
+# below, and following the instructions in the 'Option parsing'
+# section further down.
+
+## -------------- ##
+## Configuration. ##
+## -------------- ##
+
+# You should override these variables in your script after sourcing this
+# file so that they reflect the customisations you have added to the
+# option parser.
+
+# The usage line for option parsing errors and the start of '-h' and
+# '--help' output messages. You can embed shell variables for delayed
+# expansion at the time the message is displayed, but you will need to
+# quote other shell meta-characters carefully to prevent them being
+# expanded when the contents are evaled.
+usage='$progpath [OPTION]...'
+
+# Short help message in response to '-h' and '--help'. Add to this or
+# override it after sourcing this library to reflect the full set of
+# options your script accepts.
+usage_message="\
+ --debug enable verbose shell tracing
+ -W, --warnings=CATEGORY
+ report the warnings falling in CATEGORY [all]
+ -v, --verbose verbosely report processing
+ --version print version information and exit
+ -h, --help print short or long help message and exit
+"
+
+# Additional text appended to 'usage_message' in response to '--help'.
+long_help_message="
+Warning categories include:
+ 'all' show all warnings
+ 'none' turn off all the warnings
+ 'error' warnings are treated as fatal errors"
+
+# Help message printed before fatal option parsing errors.
+fatal_help="Try '\$progname --help' for more information."
+
+
+
+## ------------------------- ##
+## Hook function management. ##
+## ------------------------- ##
+
+# This section contains functions for adding, removing, and running hooks
+# to the main code. A hook is just a named list of of function, that can
+# be run in order later on.
+
+# func_hookable FUNC_NAME
+# -----------------------
+# Declare that FUNC_NAME will run hooks added with
+# 'func_add_hook FUNC_NAME ...'.
+func_hookable ()
+{
+ $debug_cmd
+
+ func_append hookable_fns " $1"
+}
+
+
+# func_add_hook FUNC_NAME HOOK_FUNC
+# ---------------------------------
+# Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must
+# first have been declared "hookable" by a call to 'func_hookable'.
+func_add_hook ()
+{
+ $debug_cmd
+
+ case " $hookable_fns " in
+ *" $1 "*) ;;
+ *) func_fatal_error "'$1' does not accept hook functions." ;;
+ esac
+
+ eval func_append ${1}_hooks '" $2"'
+}
+
+
+# func_remove_hook FUNC_NAME HOOK_FUNC
+# ------------------------------------
+# Remove HOOK_FUNC from the list of functions called by FUNC_NAME.
+func_remove_hook ()
+{
+ $debug_cmd
+
+ eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`'
+}
+
+
+# func_run_hooks FUNC_NAME [ARG]...
+# ---------------------------------
+# Run all hook functions registered to FUNC_NAME.
+# It is assumed that the list of hook functions contains nothing more
+# than a whitespace-delimited list of legal shell function names, and
+# no effort is wasted trying to catch shell meta-characters or preserve
+# whitespace.
+func_run_hooks ()
+{
+ $debug_cmd
+
+ case " $hookable_fns " in
+ *" $1 "*) ;;
+ *) func_fatal_error "'$1' does not support hook funcions.n" ;;
+ esac
+
+ eval _G_hook_fns=\$$1_hooks; shift
+
+ for _G_hook in $_G_hook_fns; do
+ eval $_G_hook '"$@"'
+
+ # store returned options list back into positional
+ # parameters for next 'cmd' execution.
+ eval _G_hook_result=\$${_G_hook}_result
+ eval set dummy "$_G_hook_result"; shift
+ done
+
+ func_quote_for_eval ${1+"$@"}
+ func_run_hooks_result=$func_quote_for_eval_result
+}
+
+
+
+## --------------- ##
+## Option parsing. ##
+## --------------- ##
+
+# In order to add your own option parsing hooks, you must accept the
+# full positional parameter list in your hook function, remove any
+# options that you action, and then pass back the remaining unprocessed
+# options in '<hooked_function_name>_result', escaped suitably for
+# 'eval'. Like this:
+#
+# my_options_prep ()
+# {
+# $debug_cmd
+#
+# # Extend the existing usage message.
+# usage_message=$usage_message'
+# -s, --silent don'\''t print informational messages
+# '
+#
+# func_quote_for_eval ${1+"$@"}
+# my_options_prep_result=$func_quote_for_eval_result
+# }
+# func_add_hook func_options_prep my_options_prep
+#
+#
+# my_silent_option ()
+# {
+# $debug_cmd
+#
+# # Note that for efficiency, we parse as many options as we can
+# # recognise in a loop before passing the remainder back to the
+# # caller on the first unrecognised argument we encounter.
+# while test $# -gt 0; do
+# opt=$1; shift
+# case $opt in
+# --silent|-s) opt_silent=: ;;
+# # Separate non-argument short options:
+# -s*) func_split_short_opt "$_G_opt"
+# set dummy "$func_split_short_opt_name" \
+# "-$func_split_short_opt_arg" ${1+"$@"}
+# shift
+# ;;
+# *) set dummy "$_G_opt" "$*"; shift; break ;;
+# esac
+# done
+#
+# func_quote_for_eval ${1+"$@"}
+# my_silent_option_result=$func_quote_for_eval_result
+# }
+# func_add_hook func_parse_options my_silent_option
+#
+#
+# my_option_validation ()
+# {
+# $debug_cmd
+#
+# $opt_silent && $opt_verbose && func_fatal_help "\
+# '--silent' and '--verbose' options are mutually exclusive."
+#
+# func_quote_for_eval ${1+"$@"}
+# my_option_validation_result=$func_quote_for_eval_result
+# }
+# func_add_hook func_validate_options my_option_validation
+#
+# You'll alse need to manually amend $usage_message to reflect the extra
+# options you parse. It's preferable to append if you can, so that
+# multiple option parsing hooks can be added safely.
+
+
+# func_options [ARG]...
+# ---------------------
+# All the functions called inside func_options are hookable. See the
+# individual implementations for details.
+func_hookable func_options
+func_options ()
+{
+ $debug_cmd
+
+ func_options_prep ${1+"$@"}
+ eval func_parse_options \
+ ${func_options_prep_result+"$func_options_prep_result"}
+ eval func_validate_options \
+ ${func_parse_options_result+"$func_parse_options_result"}
+
+ eval func_run_hooks func_options \
+ ${func_validate_options_result+"$func_validate_options_result"}
+
+ # save modified positional parameters for caller
+ func_options_result=$func_run_hooks_result
+}
+
+
+# func_options_prep [ARG]...
+# --------------------------
+# All initialisations required before starting the option parse loop.
+# Note that when calling hook functions, we pass through the list of
+# positional parameters. If a hook function modifies that list, and
+# needs to propogate that back to rest of this script, then the complete
+# modified list must be put in 'func_run_hooks_result' before
+# returning.
+func_hookable func_options_prep
+func_options_prep ()
+{
+ $debug_cmd
+
+ # Option defaults:
+ opt_verbose=false
+ opt_warning_types=
+
+ func_run_hooks func_options_prep ${1+"$@"}
+
+ # save modified positional parameters for caller
+ func_options_prep_result=$func_run_hooks_result
+}
+
+
+# func_parse_options [ARG]...
+# ---------------------------
+# The main option parsing loop.
+func_hookable func_parse_options
+func_parse_options ()
+{
+ $debug_cmd
+
+ func_parse_options_result=
+
+ # this just eases exit handling
+ while test $# -gt 0; do
+ # Defer to hook functions for initial option parsing, so they
+ # get priority in the event of reusing an option name.
+ func_run_hooks func_parse_options ${1+"$@"}
+
+ # Adjust func_parse_options positional parameters to match
+ eval set dummy "$func_run_hooks_result"; shift
+
+ # Break out of the loop if we already parsed every option.
+ test $# -gt 0 || break
+
+ _G_opt=$1
+ shift
+ case $_G_opt in
+ --debug|-x) debug_cmd='set -x'
+ func_echo "enabling shell trace mode"
+ $debug_cmd
+ ;;
+
+ --no-warnings|--no-warning|--no-warn)
+ set dummy --warnings none ${1+"$@"}
+ shift
+ ;;
+
+ --warnings|--warning|-W)
+ test $# = 0 && func_missing_arg $_G_opt && break
+ case " $warning_categories $1" in
+ *" $1 "*)
+ # trailing space prevents matching last $1 above
+ func_append_uniq opt_warning_types " $1"
+ ;;
+ *all)
+ opt_warning_types=$warning_categories
+ ;;
+ *none)
+ opt_warning_types=none
+ warning_func=:
+ ;;
+ *error)
+ opt_warning_types=$warning_categories
+ warning_func=func_fatal_error
+ ;;
+ *)
+ func_fatal_error \
+ "unsupported warning category: '$1'"
+ ;;
+ esac
+ shift
+ ;;
+
+ --verbose|-v) opt_verbose=: ;;
+ --version) func_version ;;
+ -\?|-h) func_usage ;;
+ --help) func_help ;;
+
+ # Separate optargs to long options (plugins may need this):
+ --*=*) func_split_equals "$_G_opt"
+ set dummy "$func_split_equals_lhs" \
+ "$func_split_equals_rhs" ${1+"$@"}
+ shift
+ ;;
+
+ # Separate optargs to short options:
+ -W*)
+ func_split_short_opt "$_G_opt"
+ set dummy "$func_split_short_opt_name" \
+ "$func_split_short_opt_arg" ${1+"$@"}
+ shift
+ ;;
+
+ # Separate non-argument short options:
+ -\?*|-h*|-v*|-x*)
+ func_split_short_opt "$_G_opt"
+ set dummy "$func_split_short_opt_name" \
+ "-$func_split_short_opt_arg" ${1+"$@"}
+ shift
+ ;;
+
+ --) break ;;
+ -*) func_fatal_help "unrecognised option: '$_G_opt'" ;;
+ *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;;
+ esac
+ done
+
+ # save modified positional parameters for caller
+ func_quote_for_eval ${1+"$@"}
+ func_parse_options_result=$func_quote_for_eval_result
+}
+
+
+# func_validate_options [ARG]...
+# ------------------------------
+# Perform any sanity checks on option settings and/or unconsumed
+# arguments.
+func_hookable func_validate_options
+func_validate_options ()
+{
+ $debug_cmd
+
+ # Display all warnings if -W was not given.
+ test -n "$opt_warning_types" || opt_warning_types=" $warning_categories"
+
+ func_run_hooks func_validate_options ${1+"$@"}
+
+ # Bail if the options were screwed!
+ $exit_cmd $EXIT_FAILURE
+
+ # save modified positional parameters for caller
+ func_validate_options_result=$func_run_hooks_result
+}
+
+
+
+## ----------------- ##
+## Helper functions. ##
+## ----------------- ##
+
+# This section contains the helper functions used by the rest of the
+# hookable option parser framework in ascii-betical order.
+
+
+# func_fatal_help ARG...
+# ----------------------
+# Echo program name prefixed message to standard error, followed by
+# a help hint, and exit.
+func_fatal_help ()
+{
+ $debug_cmd
+
+ eval \$ECHO \""Usage: $usage"\"
+ eval \$ECHO \""$fatal_help"\"
+ func_error ${1+"$@"}
+ exit $EXIT_FAILURE
+}
+
+
+# func_help
+# ---------
+# Echo long help message to standard output and exit.
+func_help ()
+{
+ $debug_cmd
+
+ func_usage_message
+ $ECHO "$long_help_message"
+ exit 0
+}
+
+
+# func_missing_arg ARGNAME
+# ------------------------
+# Echo program name prefixed message to standard error and set global
+# exit_cmd.
+func_missing_arg ()
+{
+ $debug_cmd
+
+ func_error "Missing argument for '$1'."
+ exit_cmd=exit
+}
+
+
+# func_split_equals STRING
+# ------------------------
+# Set func_split_equals_lhs and func_split_equals_rhs shell variables after
+# splitting STRING at the '=' sign.
+test -z "$_G_HAVE_XSI_OPS" \
+ && (eval 'x=a/b/c;
+ test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \
+ && _G_HAVE_XSI_OPS=yes
+
+if test yes = "$_G_HAVE_XSI_OPS"
+then
+ # This is an XSI compatible shell, allowing a faster implementation...
+ eval 'func_split_equals ()
+ {
+ $debug_cmd
+
+ func_split_equals_lhs=${1%%=*}
+ func_split_equals_rhs=${1#*=}
+ test "x$func_split_equals_lhs" = "x$1" \
+ && func_split_equals_rhs=
+ }'
+else
+ # ...otherwise fall back to using expr, which is often a shell builtin.
+ func_split_equals ()
+ {
+ $debug_cmd
+
+ func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'`
+ func_split_equals_rhs=
+ test "x$func_split_equals_lhs" = "x$1" \
+ || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'`
+ }
+fi #func_split_equals
+
+
+# func_split_short_opt SHORTOPT
+# -----------------------------
+# Set func_split_short_opt_name and func_split_short_opt_arg shell
+# variables after splitting SHORTOPT after the 2nd character.
+if test yes = "$_G_HAVE_XSI_OPS"
+then
+ # This is an XSI compatible shell, allowing a faster implementation...
+ eval 'func_split_short_opt ()
+ {
+ $debug_cmd
+
+ func_split_short_opt_arg=${1#??}
+ func_split_short_opt_name=${1%"$func_split_short_opt_arg"}
+ }'
+else
+ # ...otherwise fall back to using expr, which is often a shell builtin.
+ func_split_short_opt ()
+ {
+ $debug_cmd
+
+ func_split_short_opt_name=`expr "x$1" : 'x-\(.\)'`
+ func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'`
+ }
+fi #func_split_short_opt
+
+
+# func_usage
+# ----------
+# Echo short help message to standard output and exit.
+func_usage ()
+{
+ $debug_cmd
+
+ func_usage_message
+ $ECHO "Run '$progname --help |${PAGER-more}' for full usage"
+ exit 0
+}
+
+
+# func_usage_message
+# ------------------
+# Echo short help message to standard output.
+func_usage_message ()
+{
+ $debug_cmd
+
+ eval \$ECHO \""Usage: $usage"\"
+ echo
+ $SED -n 's|^# ||
+ /^Written by/{
+ x;p;x
+ }
+ h
+ /^Written by/q' < "$progpath"
+ echo
+ eval \$ECHO \""$usage_message"\"
+}
+
+
+# func_version
+# ------------
+# Echo version message to standard output and exit.
+func_version ()
+{
+ $debug_cmd
+
+ printf '%s\n' "$progname $scriptversion"
+ $SED -n '
+ /(C)/!b go
+ :more
+ /\./!{
+ N
+ s|\n# | |
+ b more
+ }
+ :go
+ /^# Written by /,/# warranty; / {
+ s|^# ||
+ s|^# *$||
+ s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2|
+ p
+ }
+ /^# Written by / {
+ s|^# ||
+ p
+ }
+ /^warranty; /q' < "$progpath"
+
+ exit $?
+}
+
+
+# Local variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC"
+# time-stamp-time-zone: "UTC"
+# End:
+
+# Set a version string.
+scriptversion='(GNU libtool) 2.4.6'
+
+
+# func_echo ARG...
+# ----------------
+# Libtool also displays the current mode in messages, so override
+# funclib.sh func_echo with this custom definition.
+func_echo ()
+{
+ $debug_cmd
+
+ _G_message=$*
+
+ func_echo_IFS=$IFS
+ IFS=$nl
+ for _G_line in $_G_message; do
+ IFS=$func_echo_IFS
+ $ECHO "$progname${opt_mode+: $opt_mode}: $_G_line"
+ done
+ IFS=$func_echo_IFS
+}
+
+
+# func_warning ARG...
+# -------------------
+# Libtool warnings are not categorized, so override funclib.sh
+# func_warning with this simpler definition.
+func_warning ()
+{
+ $debug_cmd
+
+ $warning_func ${1+"$@"}
+}
+
+
+## ---------------- ##
+## Options parsing. ##
+## ---------------- ##
+
+# Hook in the functions to make sure our own options are parsed during
+# the option parsing loop.
+
+usage='$progpath [OPTION]... [MODE-ARG]...'
+
+# Short help message in response to '-h'.
+usage_message="Options:
+ --config show all configuration variables
+ --debug enable verbose shell tracing
+ -n, --dry-run display commands without modifying any files
+ --features display basic configuration information and exit
+ --mode=MODE use operation mode MODE
+ --no-warnings equivalent to '-Wnone'
+ --preserve-dup-deps don't remove duplicate dependency libraries
+ --quiet, --silent don't print informational messages
+ --tag=TAG use configuration variables from tag TAG
+ -v, --verbose print more informational messages than default
+ --version print version information
+ -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all]
+ -h, --help, --help-all print short, long, or detailed help message
+"
+
+# Additional text appended to 'usage_message' in response to '--help'.
+func_help ()
+{
+ $debug_cmd
+
+ func_usage_message
+ $ECHO "$long_help_message
+
+MODE must be one of the following:
+
+ clean remove files from the build directory
+ compile compile a source file into a libtool object
+ execute automatically set library path, then run a program
+ finish complete the installation of libtool libraries
+ install install libraries or executables
+ link create a library or an executable
+ uninstall remove libraries from an installed directory
+
+MODE-ARGS vary depending on the MODE. When passed as first option,
+'--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that.
+Try '$progname --help --mode=MODE' for a more detailed description of MODE.
+
+When reporting a bug, please describe a test case to reproduce it and
+include the following information:
+
+ host-triplet: $host
+ shell: $SHELL
+ compiler: $LTCC
+ compiler flags: $LTCFLAGS
+ linker: $LD (gnu? $with_gnu_ld)
+ version: $progname (GNU libtool) 2.4.6
+ automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q`
+ autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q`
+
+Report bugs to <bug-libtool@gnu.org>.
+GNU libtool home page: <http://www.gnu.org/software/libtool/>.
+General help using GNU software: <http://www.gnu.org/gethelp/>."
+ exit 0
+}
+
+
+# func_lo2o OBJECT-NAME
+# ---------------------
+# Transform OBJECT-NAME from a '.lo' suffix to the platform specific
+# object suffix.
+
+lo2o=s/\\.lo\$/.$objext/
+o2lo=s/\\.$objext\$/.lo/
+
+if test yes = "$_G_HAVE_XSI_OPS"; then
+ eval 'func_lo2o ()
+ {
+ case $1 in
+ *.lo) func_lo2o_result=${1%.lo}.$objext ;;
+ * ) func_lo2o_result=$1 ;;
+ esac
+ }'
+
+ # func_xform LIBOBJ-OR-SOURCE
+ # ---------------------------
+ # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise)
+ # suffix to a '.lo' libtool-object suffix.
+ eval 'func_xform ()
+ {
+ func_xform_result=${1%.*}.lo
+ }'
+else
+ # ...otherwise fall back to using sed.
+ func_lo2o ()
+ {
+ func_lo2o_result=`$ECHO "$1" | $SED "$lo2o"`
+ }
+
+ func_xform ()
+ {
+ func_xform_result=`$ECHO "$1" | $SED 's|\.[^.]*$|.lo|'`
+ }
+fi
+
+
+# func_fatal_configuration ARG...
+# -------------------------------
+# Echo program name prefixed message to standard error, followed by
+# a configuration failure hint, and exit.
+func_fatal_configuration ()
+{
+ func__fatal_error ${1+"$@"} \
+ "See the $PACKAGE documentation for more information." \
+ "Fatal configuration error."
+}
+
+
+# func_config
+# -----------
+# Display the configuration for all the tags in this script.
+func_config ()
+{
+ re_begincf='^# ### BEGIN LIBTOOL'
+ re_endcf='^# ### END LIBTOOL'
+
+ # Default configuration.
+ $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath"
+
+ # Now print the configurations for the tags.
+ for tagname in $taglist; do
+ $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath"
+ done
+
+ exit $?
+}
+
+
+# func_features
+# -------------
+# Display the features supported by this script.
+func_features ()
+{
+ echo "host: $host"
+ if test yes = "$build_libtool_libs"; then
+ echo "enable shared libraries"
+ else
+ echo "disable shared libraries"
+ fi
+ if test yes = "$build_old_libs"; then
+ echo "enable static libraries"
+ else
+ echo "disable static libraries"
+ fi
+
+ exit $?
+}
+
+
+# func_enable_tag TAGNAME
+# -----------------------
+# Verify that TAGNAME is valid, and either flag an error and exit, or
+# enable the TAGNAME tag. We also add TAGNAME to the global $taglist
+# variable here.
+func_enable_tag ()
+{
+ # Global variable:
+ tagname=$1
+
+ re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$"
+ re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$"
+ sed_extractcf=/$re_begincf/,/$re_endcf/p
+
+ # Validate tagname.
+ case $tagname in
+ *[!-_A-Za-z0-9,/]*)
+ func_fatal_error "invalid tag name: $tagname"
+ ;;
+ esac
+
+ # Don't test for the "default" C tag, as we know it's
+ # there but not specially marked.
+ case $tagname in
+ CC) ;;
+ *)
+ if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then
+ taglist="$taglist $tagname"
+
+ # Evaluate the configuration. Be careful to quote the path
+ # and the sed script, to avoid splitting on whitespace, but
+ # also don't use non-portable quotes within backquotes within
+ # quotes we have to do it in 2 steps:
+ extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"`
+ eval "$extractedcf"
+ else
+ func_error "ignoring unknown tag $tagname"
+ fi
+ ;;
+ esac
+}
+
+
+# func_check_version_match
+# ------------------------
+# Ensure that we are using m4 macros, and libtool script from the same
+# release of libtool.
+func_check_version_match ()
+{
+ if test "$package_revision" != "$macro_revision"; then
+ if test "$VERSION" != "$macro_version"; then
+ if test -z "$macro_version"; then
+ cat >&2 <<_LT_EOF
+$progname: Version mismatch error. This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from an older release.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+ else
+ cat >&2 <<_LT_EOF
+$progname: Version mismatch error. This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from $PACKAGE $macro_version.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+ fi
+ else
+ cat >&2 <<_LT_EOF
+$progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision,
+$progname: but the definition of this LT_INIT comes from revision $macro_revision.
+$progname: You should recreate aclocal.m4 with macros from revision $package_revision
+$progname: of $PACKAGE $VERSION and run autoconf again.
+_LT_EOF
+ fi
+
+ exit $EXIT_MISMATCH
+ fi
+}
+
+
+# libtool_options_prep [ARG]...
+# -----------------------------
+# Preparation for options parsed by libtool.
+libtool_options_prep ()
+{
+ $debug_mode
+
+ # Option defaults:
+ opt_config=false
+ opt_dlopen=
+ opt_dry_run=false
+ opt_help=false
+ opt_mode=
+ opt_preserve_dup_deps=false
+ opt_quiet=false
+
+ nonopt=
+ preserve_args=
+
+ # Shorthand for --mode=foo, only valid as the first argument
+ case $1 in
+ clean|clea|cle|cl)
+ shift; set dummy --mode clean ${1+"$@"}; shift
+ ;;
+ compile|compil|compi|comp|com|co|c)
+ shift; set dummy --mode compile ${1+"$@"}; shift
+ ;;
+ execute|execut|execu|exec|exe|ex|e)
+ shift; set dummy --mode execute ${1+"$@"}; shift
+ ;;
+ finish|finis|fini|fin|fi|f)
+ shift; set dummy --mode finish ${1+"$@"}; shift
+ ;;
+ install|instal|insta|inst|ins|in|i)
+ shift; set dummy --mode install ${1+"$@"}; shift
+ ;;
+ link|lin|li|l)
+ shift; set dummy --mode link ${1+"$@"}; shift
+ ;;
+ uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u)
+ shift; set dummy --mode uninstall ${1+"$@"}; shift
+ ;;
+ esac
+
+ # Pass back the list of options.
+ func_quote_for_eval ${1+"$@"}
+ libtool_options_prep_result=$func_quote_for_eval_result
+}
+func_add_hook func_options_prep libtool_options_prep
+
+
+# libtool_parse_options [ARG]...
+# ---------------------------------
+# Provide handling for libtool specific options.
+libtool_parse_options ()
+{
+ $debug_cmd
+
+ # Perform our own loop to consume as many options as possible in
+ # each iteration.
+ while test $# -gt 0; do
+ _G_opt=$1
+ shift
+ case $_G_opt in
+ --dry-run|--dryrun|-n)
+ opt_dry_run=:
+ ;;
+
+ --config) func_config ;;
+
+ --dlopen|-dlopen)
+ opt_dlopen="${opt_dlopen+$opt_dlopen
+}$1"
+ shift
+ ;;
+
+ --preserve-dup-deps)
+ opt_preserve_dup_deps=: ;;
+
+ --features) func_features ;;
+
+ --finish) set dummy --mode finish ${1+"$@"}; shift ;;
+
+ --help) opt_help=: ;;
+
+ --help-all) opt_help=': help-all' ;;
+
+ --mode) test $# = 0 && func_missing_arg $_G_opt && break
+ opt_mode=$1
+ case $1 in
+ # Valid mode arguments:
+ clean|compile|execute|finish|install|link|relink|uninstall) ;;
+
+ # Catch anything else as an error
+ *) func_error "invalid argument for $_G_opt"
+ exit_cmd=exit
+ break
+ ;;
+ esac
+ shift
+ ;;
+
+ --no-silent|--no-quiet)
+ opt_quiet=false
+ func_append preserve_args " $_G_opt"
+ ;;
+
+ --no-warnings|--no-warning|--no-warn)
+ opt_warning=false
+ func_append preserve_args " $_G_opt"
+ ;;
+
+ --no-verbose)
+ opt_verbose=false
+ func_append preserve_args " $_G_opt"
+ ;;
+
+ --silent|--quiet)
+ opt_quiet=:
+ opt_verbose=false
+ func_append preserve_args " $_G_opt"
+ ;;
+
+ --tag) test $# = 0 && func_missing_arg $_G_opt && break
+ opt_tag=$1
+ func_append preserve_args " $_G_opt $1"
+ func_enable_tag "$1"
+ shift
+ ;;
+
+ --verbose|-v) opt_quiet=false
+ opt_verbose=:
+ func_append preserve_args " $_G_opt"
+ ;;
+
+ # An option not handled by this hook function:
+ *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;;
+ esac
+ done
+
+
+ # save modified positional parameters for caller
+ func_quote_for_eval ${1+"$@"}
+ libtool_parse_options_result=$func_quote_for_eval_result
+}
+func_add_hook func_parse_options libtool_parse_options
+
+
+
+# libtool_validate_options [ARG]...
+# ---------------------------------
+# Perform any sanity checks on option settings and/or unconsumed
+# arguments.
+libtool_validate_options ()
+{
+ # save first non-option argument
+ if test 0 -lt $#; then
+ nonopt=$1
+ shift
+ fi
+
+ # preserve --debug
+ test : = "$debug_cmd" || func_append preserve_args " --debug"
+
+ case $host in
+ # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452
+ # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788
+ *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*)
+ # don't eliminate duplications in $postdeps and $predeps
+ opt_duplicate_compiler_generated_deps=:
+ ;;
+ *)
+ opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps
+ ;;
+ esac
+
+ $opt_help || {
+ # Sanity checks first:
+ func_check_version_match
+
+ test yes != "$build_libtool_libs" \
+ && test yes != "$build_old_libs" \
+ && func_fatal_configuration "not configured to build any kind of library"
+
+ # Darwin sucks
+ eval std_shrext=\"$shrext_cmds\"
+
+ # Only execute mode is allowed to have -dlopen flags.
+ if test -n "$opt_dlopen" && test execute != "$opt_mode"; then
+ func_error "unrecognized option '-dlopen'"
+ $ECHO "$help" 1>&2
+ exit $EXIT_FAILURE
+ fi
+
+ # Change the help message to a mode-specific one.
+ generic_help=$help
+ help="Try '$progname --help --mode=$opt_mode' for more information."
+ }
+
+ # Pass back the unparsed argument list
+ func_quote_for_eval ${1+"$@"}
+ libtool_validate_options_result=$func_quote_for_eval_result
+}
+func_add_hook func_validate_options libtool_validate_options
+
+
+# Process options as early as possible so that --help and --version
+# can return quickly.
+func_options ${1+"$@"}
+eval set dummy "$func_options_result"; shift
+
+
+
+## ----------- ##
+## Main. ##
+## ----------- ##
+
+magic='%%%MAGIC variable%%%'
+magic_exe='%%%MAGIC EXE variable%%%'
+
+# Global variables.
+extracted_archives=
+extracted_serial=0
+
+# If this variable is set in any of the actions, the command in it
+# will be execed at the end. This prevents here-documents from being
+# left over by shells.
+exec_cmd=
+
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+}
+
+# func_generated_by_libtool
+# True iff stdin has been generated by Libtool. This function is only
+# a basic sanity check; it will hardly flush out determined imposters.
+func_generated_by_libtool_p ()
+{
+ $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1
+}
+
+# func_lalib_p file
+# True iff FILE is a libtool '.la' library or '.lo' object file.
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_lalib_p ()
+{
+ test -f "$1" &&
+ $SED -e 4q "$1" 2>/dev/null | func_generated_by_libtool_p
+}
+
+# func_lalib_unsafe_p file
+# True iff FILE is a libtool '.la' library or '.lo' object file.
+# This function implements the same check as func_lalib_p without
+# resorting to external programs. To this end, it redirects stdin and
+# closes it afterwards, without saving the original file descriptor.
+# As a safety measure, use it only where a negative result would be
+# fatal anyway. Works if 'file' does not exist.
+func_lalib_unsafe_p ()
+{
+ lalib_p=no
+ if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then
+ for lalib_p_l in 1 2 3 4
+ do
+ read lalib_p_line
+ case $lalib_p_line in
+ \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;;
+ esac
+ done
+ exec 0<&5 5<&-
+ fi
+ test yes = "$lalib_p"
+}
+
+# func_ltwrapper_script_p file
+# True iff FILE is a libtool wrapper script
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_script_p ()
+{
+ test -f "$1" &&
+ $lt_truncate_bin < "$1" 2>/dev/null | func_generated_by_libtool_p
+}
+
+# func_ltwrapper_executable_p file
+# True iff FILE is a libtool wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_executable_p ()
+{
+ func_ltwrapper_exec_suffix=
+ case $1 in
+ *.exe) ;;
+ *) func_ltwrapper_exec_suffix=.exe ;;
+ esac
+ $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1
+}
+
+# func_ltwrapper_scriptname file
+# Assumes file is an ltwrapper_executable
+# uses $file to determine the appropriate filename for a
+# temporary ltwrapper_script.
+func_ltwrapper_scriptname ()
+{
+ func_dirname_and_basename "$1" "" "."
+ func_stripname '' '.exe' "$func_basename_result"
+ func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper
+}
+
+# func_ltwrapper_p file
+# True iff FILE is a libtool wrapper script or wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_p ()
+{
+ func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1"
+}
+
+
+# func_execute_cmds commands fail_cmd
+# Execute tilde-delimited COMMANDS.
+# If FAIL_CMD is given, eval that upon failure.
+# FAIL_CMD may read-access the current command in variable CMD!
+func_execute_cmds ()
+{
+ $debug_cmd
+
+ save_ifs=$IFS; IFS='~'
+ for cmd in $1; do
+ IFS=$sp$nl
+ eval cmd=\"$cmd\"
+ IFS=$save_ifs
+ func_show_eval "$cmd" "${2-:}"
+ done
+ IFS=$save_ifs
+}
+
+
+# func_source file
+# Source FILE, adding directory component if necessary.
+# Note that it is not necessary on cygwin/mingw to append a dot to
+# FILE even if both FILE and FILE.exe exist: automatic-append-.exe
+# behavior happens only for exec(3), not for open(2)! Also, sourcing
+# 'FILE.' does not work on cygwin managed mounts.
+func_source ()
+{
+ $debug_cmd
+
+ case $1 in
+ */* | *\\*) . "$1" ;;
+ *) . "./$1" ;;
+ esac
+}
+
+
+# func_resolve_sysroot PATH
+# Replace a leading = in PATH with a sysroot. Store the result into
+# func_resolve_sysroot_result
+func_resolve_sysroot ()
+{
+ func_resolve_sysroot_result=$1
+ case $func_resolve_sysroot_result in
+ =*)
+ func_stripname '=' '' "$func_resolve_sysroot_result"
+ func_resolve_sysroot_result=$lt_sysroot$func_stripname_result
+ ;;
+ esac
+}
+
+# func_replace_sysroot PATH
+# If PATH begins with the sysroot, replace it with = and
+# store the result into func_replace_sysroot_result.
+func_replace_sysroot ()
+{
+ case $lt_sysroot:$1 in
+ ?*:"$lt_sysroot"*)
+ func_stripname "$lt_sysroot" '' "$1"
+ func_replace_sysroot_result='='$func_stripname_result
+ ;;
+ *)
+ # Including no sysroot.
+ func_replace_sysroot_result=$1
+ ;;
+ esac
+}
+
+# func_infer_tag arg
+# Infer tagged configuration to use if any are available and
+# if one wasn't chosen via the "--tag" command line option.
+# Only attempt this if the compiler in the base compile
+# command doesn't match the default compiler.
+# arg is usually of the form 'gcc ...'
+func_infer_tag ()
+{
+ $debug_cmd
+
+ if test -n "$available_tags" && test -z "$tagname"; then
+ CC_quoted=
+ for arg in $CC; do
+ func_append_quoted CC_quoted "$arg"
+ done
+ CC_expanded=`func_echo_all $CC`
+ CC_quoted_expanded=`func_echo_all $CC_quoted`
+ case $@ in
+ # Blanks in the command may have been stripped by the calling shell,
+ # but not from the CC environment variable when configure was run.
+ " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \
+ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;;
+ # Blanks at the start of $base_compile will cause this to fail
+ # if we don't check for them as well.
+ *)
+ for z in $available_tags; do
+ if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then
+ # Evaluate the configuration.
+ eval "`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`"
+ CC_quoted=
+ for arg in $CC; do
+ # Double-quote args containing other shell metacharacters.
+ func_append_quoted CC_quoted "$arg"
+ done
+ CC_expanded=`func_echo_all $CC`
+ CC_quoted_expanded=`func_echo_all $CC_quoted`
+ case "$@ " in
+ " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \
+ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*)
+ # The compiler in the base compile command matches
+ # the one in the tagged configuration.
+ # Assume this is the tagged configuration we want.
+ tagname=$z
+ break
+ ;;
+ esac
+ fi
+ done
+ # If $tagname still isn't set, then no tagged configuration
+ # was found and let the user know that the "--tag" command
+ # line option must be used.
+ if test -z "$tagname"; then
+ func_echo "unable to infer tagged configuration"
+ func_fatal_error "specify a tag with '--tag'"
+# else
+# func_verbose "using $tagname tagged configuration"
+ fi
+ ;;
+ esac
+ fi
+}
+
+
+
+# func_write_libtool_object output_name pic_name nonpic_name
+# Create a libtool object file (analogous to a ".la" file),
+# but don't create it if we're doing a dry run.
+func_write_libtool_object ()
+{
+ write_libobj=$1
+ if test yes = "$build_libtool_libs"; then
+ write_lobj=\'$2\'
+ else
+ write_lobj=none
+ fi
+
+ if test yes = "$build_old_libs"; then
+ write_oldobj=\'$3\'
+ else
+ write_oldobj=none
+ fi
+
+ $opt_dry_run || {
+ cat >${write_libobj}T <<EOF
+# $write_libobj - a libtool object file
+# Generated by $PROGRAM (GNU $PACKAGE) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# Name of the PIC object.
+pic_object=$write_lobj
+
+# Name of the non-PIC object
+non_pic_object=$write_oldobj
+
+EOF
+ $MV "${write_libobj}T" "$write_libobj"
+ }
+}
+
+
+##################################################
+# FILE NAME AND PATH CONVERSION HELPER FUNCTIONS #
+##################################################
+
+# func_convert_core_file_wine_to_w32 ARG
+# Helper function used by file name conversion functions when $build is *nix,
+# and $host is mingw, cygwin, or some other w32 environment. Relies on a
+# correctly configured wine environment available, with the winepath program
+# in $build's $PATH.
+#
+# ARG is the $build file name to be converted to w32 format.
+# Result is available in $func_convert_core_file_wine_to_w32_result, and will
+# be empty on error (or when ARG is empty)
+func_convert_core_file_wine_to_w32 ()
+{
+ $debug_cmd
+
+ func_convert_core_file_wine_to_w32_result=$1
+ if test -n "$1"; then
+ # Unfortunately, winepath does not exit with a non-zero error code, so we
+ # are forced to check the contents of stdout. On the other hand, if the
+ # command is not found, the shell will set an exit code of 127 and print
+ # *an error message* to stdout. So we must check for both error code of
+ # zero AND non-empty stdout, which explains the odd construction:
+ func_convert_core_file_wine_to_w32_tmp=`winepath -w "$1" 2>/dev/null`
+ if test "$?" -eq 0 && test -n "$func_convert_core_file_wine_to_w32_tmp"; then
+ func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" |
+ $SED -e "$sed_naive_backslashify"`
+ else
+ func_convert_core_file_wine_to_w32_result=
+ fi
+ fi
+}
+# end: func_convert_core_file_wine_to_w32
+
+
+# func_convert_core_path_wine_to_w32 ARG
+# Helper function used by path conversion functions when $build is *nix, and
+# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly
+# configured wine environment available, with the winepath program in $build's
+# $PATH. Assumes ARG has no leading or trailing path separator characters.
+#
+# ARG is path to be converted from $build format to win32.
+# Result is available in $func_convert_core_path_wine_to_w32_result.
+# Unconvertible file (directory) names in ARG are skipped; if no directory names
+# are convertible, then the result may be empty.
+func_convert_core_path_wine_to_w32 ()
+{
+ $debug_cmd
+
+ # unfortunately, winepath doesn't convert paths, only file names
+ func_convert_core_path_wine_to_w32_result=
+ if test -n "$1"; then
+ oldIFS=$IFS
+ IFS=:
+ for func_convert_core_path_wine_to_w32_f in $1; do
+ IFS=$oldIFS
+ func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f"
+ if test -n "$func_convert_core_file_wine_to_w32_result"; then
+ if test -z "$func_convert_core_path_wine_to_w32_result"; then
+ func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result
+ else
+ func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result"
+ fi
+ fi
+ done
+ IFS=$oldIFS
+ fi
+}
+# end: func_convert_core_path_wine_to_w32
+
+
+# func_cygpath ARGS...
+# Wrapper around calling the cygpath program via LT_CYGPATH. This is used when
+# when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2)
+# $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or
+# (2), returns the Cygwin file name or path in func_cygpath_result (input
+# file name or path is assumed to be in w32 format, as previously converted
+# from $build's *nix or MSYS format). In case (3), returns the w32 file name
+# or path in func_cygpath_result (input file name or path is assumed to be in
+# Cygwin format). Returns an empty string on error.
+#
+# ARGS are passed to cygpath, with the last one being the file name or path to
+# be converted.
+#
+# Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH
+# environment variable; do not put it in $PATH.
+func_cygpath ()
+{
+ $debug_cmd
+
+ if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then
+ func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null`
+ if test "$?" -ne 0; then
+ # on failure, ensure result is empty
+ func_cygpath_result=
+ fi
+ else
+ func_cygpath_result=
+ func_error "LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'"
+ fi
+}
+#end: func_cygpath
+
+
+# func_convert_core_msys_to_w32 ARG
+# Convert file name or path ARG from MSYS format to w32 format. Return
+# result in func_convert_core_msys_to_w32_result.
+func_convert_core_msys_to_w32 ()
+{
+ $debug_cmd
+
+ # awkward: cmd appends spaces to result
+ func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null |
+ $SED -e 's/[ ]*$//' -e "$sed_naive_backslashify"`
+}
+#end: func_convert_core_msys_to_w32
+
+
+# func_convert_file_check ARG1 ARG2
+# Verify that ARG1 (a file name in $build format) was converted to $host
+# format in ARG2. Otherwise, emit an error message, but continue (resetting
+# func_to_host_file_result to ARG1).
+func_convert_file_check ()
+{
+ $debug_cmd
+
+ if test -z "$2" && test -n "$1"; then
+ func_error "Could not determine host file name corresponding to"
+ func_error " '$1'"
+ func_error "Continuing, but uninstalled executables may not work."
+ # Fallback:
+ func_to_host_file_result=$1
+ fi
+}
+# end func_convert_file_check
+
+
+# func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH
+# Verify that FROM_PATH (a path in $build format) was converted to $host
+# format in TO_PATH. Otherwise, emit an error message, but continue, resetting
+# func_to_host_file_result to a simplistic fallback value (see below).
+func_convert_path_check ()
+{
+ $debug_cmd
+
+ if test -z "$4" && test -n "$3"; then
+ func_error "Could not determine the host path corresponding to"
+ func_error " '$3'"
+ func_error "Continuing, but uninstalled executables may not work."
+ # Fallback. This is a deliberately simplistic "conversion" and
+ # should not be "improved". See libtool.info.
+ if test "x$1" != "x$2"; then
+ lt_replace_pathsep_chars="s|$1|$2|g"
+ func_to_host_path_result=`echo "$3" |
+ $SED -e "$lt_replace_pathsep_chars"`
+ else
+ func_to_host_path_result=$3
+ fi
+ fi
+}
+# end func_convert_path_check
+
+
+# func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG
+# Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT
+# and appending REPL if ORIG matches BACKPAT.
+func_convert_path_front_back_pathsep ()
+{
+ $debug_cmd
+
+ case $4 in
+ $1 ) func_to_host_path_result=$3$func_to_host_path_result
+ ;;
+ esac
+ case $4 in
+ $2 ) func_append func_to_host_path_result "$3"
+ ;;
+ esac
+}
+# end func_convert_path_front_back_pathsep
+
+
+##################################################
+# $build to $host FILE NAME CONVERSION FUNCTIONS #
+##################################################
+# invoked via '$to_host_file_cmd ARG'
+#
+# In each case, ARG is the path to be converted from $build to $host format.
+# Result will be available in $func_to_host_file_result.
+
+
+# func_to_host_file ARG
+# Converts the file name ARG from $build format to $host format. Return result
+# in func_to_host_file_result.
+func_to_host_file ()
+{
+ $debug_cmd
+
+ $to_host_file_cmd "$1"
+}
+# end func_to_host_file
+
+
+# func_to_tool_file ARG LAZY
+# converts the file name ARG from $build format to toolchain format. Return
+# result in func_to_tool_file_result. If the conversion in use is listed
+# in (the comma separated) LAZY, no conversion takes place.
+func_to_tool_file ()
+{
+ $debug_cmd
+
+ case ,$2, in
+ *,"$to_tool_file_cmd",*)
+ func_to_tool_file_result=$1
+ ;;
+ *)
+ $to_tool_file_cmd "$1"
+ func_to_tool_file_result=$func_to_host_file_result
+ ;;
+ esac
+}
+# end func_to_tool_file
+
+
+# func_convert_file_noop ARG
+# Copy ARG to func_to_host_file_result.
+func_convert_file_noop ()
+{
+ func_to_host_file_result=$1
+}
+# end func_convert_file_noop
+
+
+# func_convert_file_msys_to_w32 ARG
+# Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic
+# conversion to w32 is not available inside the cwrapper. Returns result in
+# func_to_host_file_result.
+func_convert_file_msys_to_w32 ()
+{
+ $debug_cmd
+
+ func_to_host_file_result=$1
+ if test -n "$1"; then
+ func_convert_core_msys_to_w32 "$1"
+ func_to_host_file_result=$func_convert_core_msys_to_w32_result
+ fi
+ func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_msys_to_w32
+
+
+# func_convert_file_cygwin_to_w32 ARG
+# Convert file name ARG from Cygwin to w32 format. Returns result in
+# func_to_host_file_result.
+func_convert_file_cygwin_to_w32 ()
+{
+ $debug_cmd
+
+ func_to_host_file_result=$1
+ if test -n "$1"; then
+ # because $build is cygwin, we call "the" cygpath in $PATH; no need to use
+ # LT_CYGPATH in this case.
+ func_to_host_file_result=`cygpath -m "$1"`
+ fi
+ func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_cygwin_to_w32
+
+
+# func_convert_file_nix_to_w32 ARG
+# Convert file name ARG from *nix to w32 format. Requires a wine environment
+# and a working winepath. Returns result in func_to_host_file_result.
+func_convert_file_nix_to_w32 ()
+{
+ $debug_cmd
+
+ func_to_host_file_result=$1
+ if test -n "$1"; then
+ func_convert_core_file_wine_to_w32 "$1"
+ func_to_host_file_result=$func_convert_core_file_wine_to_w32_result
+ fi
+ func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_nix_to_w32
+
+
+# func_convert_file_msys_to_cygwin ARG
+# Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set.
+# Returns result in func_to_host_file_result.
+func_convert_file_msys_to_cygwin ()
+{
+ $debug_cmd
+
+ func_to_host_file_result=$1
+ if test -n "$1"; then
+ func_convert_core_msys_to_w32 "$1"
+ func_cygpath -u "$func_convert_core_msys_to_w32_result"
+ func_to_host_file_result=$func_cygpath_result
+ fi
+ func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_msys_to_cygwin
+
+
+# func_convert_file_nix_to_cygwin ARG
+# Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed
+# in a wine environment, working winepath, and LT_CYGPATH set. Returns result
+# in func_to_host_file_result.
+func_convert_file_nix_to_cygwin ()
+{
+ $debug_cmd
+
+ func_to_host_file_result=$1
+ if test -n "$1"; then
+ # convert from *nix to w32, then use cygpath to convert from w32 to cygwin.
+ func_convert_core_file_wine_to_w32 "$1"
+ func_cygpath -u "$func_convert_core_file_wine_to_w32_result"
+ func_to_host_file_result=$func_cygpath_result
+ fi
+ func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_nix_to_cygwin
+
+
+#############################################
+# $build to $host PATH CONVERSION FUNCTIONS #
+#############################################
+# invoked via '$to_host_path_cmd ARG'
+#
+# In each case, ARG is the path to be converted from $build to $host format.
+# The result will be available in $func_to_host_path_result.
+#
+# Path separators are also converted from $build format to $host format. If
+# ARG begins or ends with a path separator character, it is preserved (but
+# converted to $host format) on output.
+#
+# All path conversion functions are named using the following convention:
+# file name conversion function : func_convert_file_X_to_Y ()
+# path conversion function : func_convert_path_X_to_Y ()
+# where, for any given $build/$host combination the 'X_to_Y' value is the
+# same. If conversion functions are added for new $build/$host combinations,
+# the two new functions must follow this pattern, or func_init_to_host_path_cmd
+# will break.
+
+
+# func_init_to_host_path_cmd
+# Ensures that function "pointer" variable $to_host_path_cmd is set to the
+# appropriate value, based on the value of $to_host_file_cmd.
+to_host_path_cmd=
+func_init_to_host_path_cmd ()
+{
+ $debug_cmd
+
+ if test -z "$to_host_path_cmd"; then
+ func_stripname 'func_convert_file_' '' "$to_host_file_cmd"
+ to_host_path_cmd=func_convert_path_$func_stripname_result
+ fi
+}
+
+
+# func_to_host_path ARG
+# Converts the path ARG from $build format to $host format. Return result
+# in func_to_host_path_result.
+func_to_host_path ()
+{
+ $debug_cmd
+
+ func_init_to_host_path_cmd
+ $to_host_path_cmd "$1"
+}
+# end func_to_host_path
+
+
+# func_convert_path_noop ARG
+# Copy ARG to func_to_host_path_result.
+func_convert_path_noop ()
+{
+ func_to_host_path_result=$1
+}
+# end func_convert_path_noop
+
+
+# func_convert_path_msys_to_w32 ARG
+# Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic
+# conversion to w32 is not available inside the cwrapper. Returns result in
+# func_to_host_path_result.
+func_convert_path_msys_to_w32 ()
+{
+ $debug_cmd
+
+ func_to_host_path_result=$1
+ if test -n "$1"; then
+ # Remove leading and trailing path separator characters from ARG. MSYS
+ # behavior is inconsistent here; cygpath turns them into '.;' and ';.';
+ # and winepath ignores them completely.
+ func_stripname : : "$1"
+ func_to_host_path_tmp1=$func_stripname_result
+ func_convert_core_msys_to_w32 "$func_to_host_path_tmp1"
+ func_to_host_path_result=$func_convert_core_msys_to_w32_result
+ func_convert_path_check : ";" \
+ "$func_to_host_path_tmp1" "$func_to_host_path_result"
+ func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+ fi
+}
+# end func_convert_path_msys_to_w32
+
+
+# func_convert_path_cygwin_to_w32 ARG
+# Convert path ARG from Cygwin to w32 format. Returns result in
+# func_to_host_file_result.
+func_convert_path_cygwin_to_w32 ()
+{
+ $debug_cmd
+
+ func_to_host_path_result=$1
+ if test -n "$1"; then
+ # See func_convert_path_msys_to_w32:
+ func_stripname : : "$1"
+ func_to_host_path_tmp1=$func_stripname_result
+ func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"`
+ func_convert_path_check : ";" \
+ "$func_to_host_path_tmp1" "$func_to_host_path_result"
+ func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+ fi
+}
+# end func_convert_path_cygwin_to_w32
+
+
+# func_convert_path_nix_to_w32 ARG
+# Convert path ARG from *nix to w32 format. Requires a wine environment and
+# a working winepath. Returns result in func_to_host_file_result.
+func_convert_path_nix_to_w32 ()
+{
+ $debug_cmd
+
+ func_to_host_path_result=$1
+ if test -n "$1"; then
+ # See func_convert_path_msys_to_w32:
+ func_stripname : : "$1"
+ func_to_host_path_tmp1=$func_stripname_result
+ func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1"
+ func_to_host_path_result=$func_convert_core_path_wine_to_w32_result
+ func_convert_path_check : ";" \
+ "$func_to_host_path_tmp1" "$func_to_host_path_result"
+ func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+ fi
+}
+# end func_convert_path_nix_to_w32
+
+
+# func_convert_path_msys_to_cygwin ARG
+# Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set.
+# Returns result in func_to_host_file_result.
+func_convert_path_msys_to_cygwin ()
+{
+ $debug_cmd
+
+ func_to_host_path_result=$1
+ if test -n "$1"; then
+ # See func_convert_path_msys_to_w32:
+ func_stripname : : "$1"
+ func_to_host_path_tmp1=$func_stripname_result
+ func_convert_core_msys_to_w32 "$func_to_host_path_tmp1"
+ func_cygpath -u -p "$func_convert_core_msys_to_w32_result"
+ func_to_host_path_result=$func_cygpath_result
+ func_convert_path_check : : \
+ "$func_to_host_path_tmp1" "$func_to_host_path_result"
+ func_convert_path_front_back_pathsep ":*" "*:" : "$1"
+ fi
+}
+# end func_convert_path_msys_to_cygwin
+
+
+# func_convert_path_nix_to_cygwin ARG
+# Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a
+# a wine environment, working winepath, and LT_CYGPATH set. Returns result in
+# func_to_host_file_result.
+func_convert_path_nix_to_cygwin ()
+{
+ $debug_cmd
+
+ func_to_host_path_result=$1
+ if test -n "$1"; then
+ # Remove leading and trailing path separator characters from
+ # ARG. msys behavior is inconsistent here, cygpath turns them
+ # into '.;' and ';.', and winepath ignores them completely.
+ func_stripname : : "$1"
+ func_to_host_path_tmp1=$func_stripname_result
+ func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1"
+ func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result"
+ func_to_host_path_result=$func_cygpath_result
+ func_convert_path_check : : \
+ "$func_to_host_path_tmp1" "$func_to_host_path_result"
+ func_convert_path_front_back_pathsep ":*" "*:" : "$1"
+ fi
+}
+# end func_convert_path_nix_to_cygwin
+
+
+# func_dll_def_p FILE
+# True iff FILE is a Windows DLL '.def' file.
+# Keep in sync with _LT_DLL_DEF_P in libtool.m4
+func_dll_def_p ()
+{
+ $debug_cmd
+
+ func_dll_def_p_tmp=`$SED -n \
+ -e 's/^[ ]*//' \
+ -e '/^\(;.*\)*$/d' \
+ -e 's/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p' \
+ -e q \
+ "$1"`
+ test DEF = "$func_dll_def_p_tmp"
+}
+
+
+# func_mode_compile arg...
+func_mode_compile ()
+{
+ $debug_cmd
+
+ # Get the compilation command and the source file.
+ base_compile=
+ srcfile=$nonopt # always keep a non-empty value in "srcfile"
+ suppress_opt=yes
+ suppress_output=
+ arg_mode=normal
+ libobj=
+ later=
+ pie_flag=
+
+ for arg
+ do
+ case $arg_mode in
+ arg )
+ # do not "continue". Instead, add this to base_compile
+ lastarg=$arg
+ arg_mode=normal
+ ;;
+
+ target )
+ libobj=$arg
+ arg_mode=normal
+ continue
+ ;;
+
+ normal )
+ # Accept any command-line options.
+ case $arg in
+ -o)
+ test -n "$libobj" && \
+ func_fatal_error "you cannot specify '-o' more than once"
+ arg_mode=target
+ continue
+ ;;
+
+ -pie | -fpie | -fPIE)
+ func_append pie_flag " $arg"
+ continue
+ ;;
+
+ -shared | -static | -prefer-pic | -prefer-non-pic)
+ func_append later " $arg"
+ continue
+ ;;
+
+ -no-suppress)
+ suppress_opt=no
+ continue
+ ;;
+
+ -Xcompiler)
+ arg_mode=arg # the next one goes into the "base_compile" arg list
+ continue # The current "srcfile" will either be retained or
+ ;; # replaced later. I would guess that would be a bug.
+
+ -Wc,*)
+ func_stripname '-Wc,' '' "$arg"
+ args=$func_stripname_result
+ lastarg=
+ save_ifs=$IFS; IFS=,
+ for arg in $args; do
+ IFS=$save_ifs
+ func_append_quoted lastarg "$arg"
+ done
+ IFS=$save_ifs
+ func_stripname ' ' '' "$lastarg"
+ lastarg=$func_stripname_result
+
+ # Add the arguments to base_compile.
+ func_append base_compile " $lastarg"
+ continue
+ ;;
+
+ *)
+ # Accept the current argument as the source file.
+ # The previous "srcfile" becomes the current argument.
+ #
+ lastarg=$srcfile
+ srcfile=$arg
+ ;;
+ esac # case $arg
+ ;;
+ esac # case $arg_mode
+
+ # Aesthetically quote the previous argument.
+ func_append_quoted base_compile "$lastarg"
+ done # for arg
+
+ case $arg_mode in
+ arg)
+ func_fatal_error "you must specify an argument for -Xcompile"
+ ;;
+ target)
+ func_fatal_error "you must specify a target with '-o'"
+ ;;
+ *)
+ # Get the name of the library object.
+ test -z "$libobj" && {
+ func_basename "$srcfile"
+ libobj=$func_basename_result
+ }
+ ;;
+ esac
+
+ # Recognize several different file suffixes.
+ # If the user specifies -o file.o, it is replaced with file.lo
+ case $libobj in
+ *.[cCFSifmso] | \
+ *.ada | *.adb | *.ads | *.asm | \
+ *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \
+ *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup)
+ func_xform "$libobj"
+ libobj=$func_xform_result
+ ;;
+ esac
+
+ case $libobj in
+ *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;;
+ *)
+ func_fatal_error "cannot determine name of library object from '$libobj'"
+ ;;
+ esac
+
+ func_infer_tag $base_compile
+
+ for arg in $later; do
+ case $arg in
+ -shared)
+ test yes = "$build_libtool_libs" \
+ || func_fatal_configuration "cannot build a shared library"
+ build_old_libs=no
+ continue
+ ;;
+
+ -static)
+ build_libtool_libs=no
+ build_old_libs=yes
+ continue
+ ;;
+
+ -prefer-pic)
+ pic_mode=yes
+ continue
+ ;;
+
+ -prefer-non-pic)
+ pic_mode=no
+ continue
+ ;;
+ esac
+ done
+
+ func_quote_for_eval "$libobj"
+ test "X$libobj" != "X$func_quote_for_eval_result" \
+ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \
+ && func_warning "libobj name '$libobj' may not contain shell special characters."
+ func_dirname_and_basename "$obj" "/" ""
+ objname=$func_basename_result
+ xdir=$func_dirname_result
+ lobj=$xdir$objdir/$objname
+
+ test -z "$base_compile" && \
+ func_fatal_help "you must specify a compilation command"
+
+ # Delete any leftover library objects.
+ if test yes = "$build_old_libs"; then
+ removelist="$obj $lobj $libobj ${libobj}T"
+ else
+ removelist="$lobj $libobj ${libobj}T"
+ fi
+
+ # On Cygwin there's no "real" PIC flag so we must build both object types
+ case $host_os in
+ cygwin* | mingw* | pw32* | os2* | cegcc*)
+ pic_mode=default
+ ;;
+ esac
+ if test no = "$pic_mode" && test pass_all != "$deplibs_check_method"; then
+ # non-PIC code in shared libraries is not supported
+ pic_mode=default
+ fi
+
+ # Calculate the filename of the output object if compiler does
+ # not support -o with -c
+ if test no = "$compiler_c_o"; then
+ output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.$objext
+ lockfile=$output_obj.lock
+ else
+ output_obj=
+ need_locks=no
+ lockfile=
+ fi
+
+ # Lock this critical section if it is needed
+ # We use this script file to make the link, it avoids creating a new file
+ if test yes = "$need_locks"; then
+ until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
+ func_echo "Waiting for $lockfile to be removed"
+ sleep 2
+ done
+ elif test warn = "$need_locks"; then
+ if test -f "$lockfile"; then
+ $ECHO "\
+*** ERROR, $lockfile exists and contains:
+`cat $lockfile 2>/dev/null`
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support '-c' and '-o' together. If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+ $opt_dry_run || $RM $removelist
+ exit $EXIT_FAILURE
+ fi
+ func_append removelist " $output_obj"
+ $ECHO "$srcfile" > "$lockfile"
+ fi
+
+ $opt_dry_run || $RM $removelist
+ func_append removelist " $lockfile"
+ trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15
+
+ func_to_tool_file "$srcfile" func_convert_file_msys_to_w32
+ srcfile=$func_to_tool_file_result
+ func_quote_for_eval "$srcfile"
+ qsrcfile=$func_quote_for_eval_result
+
+ # Only build a PIC object if we are building libtool libraries.
+ if test yes = "$build_libtool_libs"; then
+ # Without this assignment, base_compile gets emptied.
+ fbsd_hideous_sh_bug=$base_compile
+
+ if test no != "$pic_mode"; then
+ command="$base_compile $qsrcfile $pic_flag"
+ else
+ # Don't build PIC code
+ command="$base_compile $qsrcfile"
+ fi
+
+ func_mkdir_p "$xdir$objdir"
+
+ if test -z "$output_obj"; then
+ # Place PIC objects in $objdir
+ func_append command " -o $lobj"
+ fi
+
+ func_show_eval_locale "$command" \
+ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE'
+
+ if test warn = "$need_locks" &&
+ test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+ $ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support '-c' and '-o' together. If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+ $opt_dry_run || $RM $removelist
+ exit $EXIT_FAILURE
+ fi
+
+ # Just move the object if needed, then go on to compile the next one
+ if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then
+ func_show_eval '$MV "$output_obj" "$lobj"' \
+ 'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+ fi
+
+ # Allow error messages only from the first compilation.
+ if test yes = "$suppress_opt"; then
+ suppress_output=' >/dev/null 2>&1'
+ fi
+ fi
+
+ # Only build a position-dependent object if we build old libraries.
+ if test yes = "$build_old_libs"; then
+ if test yes != "$pic_mode"; then
+ # Don't build PIC code
+ command="$base_compile $qsrcfile$pie_flag"
+ else
+ command="$base_compile $qsrcfile $pic_flag"
+ fi
+ if test yes = "$compiler_c_o"; then
+ func_append command " -o $obj"
+ fi
+
+ # Suppress compiler output if we already did a PIC compilation.
+ func_append command "$suppress_output"
+ func_show_eval_locale "$command" \
+ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE'
+
+ if test warn = "$need_locks" &&
+ test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+ $ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support '-c' and '-o' together. If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+ $opt_dry_run || $RM $removelist
+ exit $EXIT_FAILURE
+ fi
+
+ # Just move the object if needed
+ if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then
+ func_show_eval '$MV "$output_obj" "$obj"' \
+ 'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+ fi
+ fi
+
+ $opt_dry_run || {
+ func_write_libtool_object "$libobj" "$objdir/$objname" "$objname"
+
+ # Unlock the critical section if it was locked
+ if test no != "$need_locks"; then
+ removelist=$lockfile
+ $RM "$lockfile"
+ fi
+ }
+
+ exit $EXIT_SUCCESS
+}
+
+$opt_help || {
+ test compile = "$opt_mode" && func_mode_compile ${1+"$@"}
+}
+
+func_mode_help ()
+{
+ # We need to display help for each of the modes.
+ case $opt_mode in
+ "")
+ # Generic help is extracted from the usage comments
+ # at the start of this file.
+ func_help
+ ;;
+
+ clean)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE...
+
+Remove files from the build directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed
+to RM.
+
+If FILE is a libtool library, object or program, all the files associated
+with it are deleted. Otherwise, only FILE itself is deleted using RM."
+ ;;
+
+ compile)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE
+
+Compile a source file into a libtool library object.
+
+This mode accepts the following additional options:
+
+ -o OUTPUT-FILE set the output file name to OUTPUT-FILE
+ -no-suppress do not suppress compiler output for multiple passes
+ -prefer-pic try to build PIC objects only
+ -prefer-non-pic try to build non-PIC objects only
+ -shared do not build a '.o' file suitable for static linking
+ -static only build a '.o' file suitable for static linking
+ -Wc,FLAG pass FLAG directly to the compiler
+
+COMPILE-COMMAND is a command to be used in creating a 'standard' object file
+from the given SOURCEFILE.
+
+The output file name is determined by removing the directory component from
+SOURCEFILE, then substituting the C source code suffix '.c' with the
+library object suffix, '.lo'."
+ ;;
+
+ execute)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]...
+
+Automatically set library path, then run a program.
+
+This mode accepts the following additional options:
+
+ -dlopen FILE add the directory containing FILE to the library path
+
+This mode sets the library path environment variable according to '-dlopen'
+flags.
+
+If any of the ARGS are libtool executable wrappers, then they are translated
+into their corresponding uninstalled binary, and any of their required library
+directories are added to the library path.
+
+Then, COMMAND is executed, with ARGS as arguments."
+ ;;
+
+ finish)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=finish [LIBDIR]...
+
+Complete the installation of libtool libraries.
+
+Each LIBDIR is a directory that contains libtool libraries.
+
+The commands that this mode executes may require superuser privileges. Use
+the '--dry-run' option if you just want to see what would be executed."
+ ;;
+
+ install)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND...
+
+Install executables or libraries.
+
+INSTALL-COMMAND is the installation command. The first component should be
+either the 'install' or 'cp' program.
+
+The following components of INSTALL-COMMAND are treated specially:
+
+ -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation
+
+The rest of the components are interpreted as arguments to that command (only
+BSD-compatible install options are recognized)."
+ ;;
+
+ link)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=link LINK-COMMAND...
+
+Link object files or libraries together to form another library, or to
+create an executable program.
+
+LINK-COMMAND is a command using the C compiler that you would use to create
+a program from several object files.
+
+The following components of LINK-COMMAND are treated specially:
+
+ -all-static do not do any dynamic linking at all
+ -avoid-version do not add a version suffix if possible
+ -bindir BINDIR specify path to binaries directory (for systems where
+ libraries must be found in the PATH setting at runtime)
+ -dlopen FILE '-dlpreopen' FILE if it cannot be dlopened at runtime
+ -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols
+ -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3)
+ -export-symbols SYMFILE
+ try to export only the symbols listed in SYMFILE
+ -export-symbols-regex REGEX
+ try to export only the symbols matching REGEX
+ -LLIBDIR search LIBDIR for required installed libraries
+ -lNAME OUTPUT-FILE requires the installed library libNAME
+ -module build a library that can dlopened
+ -no-fast-install disable the fast-install mode
+ -no-install link a not-installable executable
+ -no-undefined declare that a library does not refer to external symbols
+ -o OUTPUT-FILE create OUTPUT-FILE from the specified objects
+ -objectlist FILE use a list of object files found in FILE to specify objects
+ -os2dllname NAME force a short DLL name on OS/2 (no effect on other OSes)
+ -precious-files-regex REGEX
+ don't remove output files matching REGEX
+ -release RELEASE specify package release information
+ -rpath LIBDIR the created library will eventually be installed in LIBDIR
+ -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries
+ -shared only do dynamic linking of libtool libraries
+ -shrext SUFFIX override the standard shared library file extension
+ -static do not do any dynamic linking of uninstalled libtool libraries
+ -static-libtool-libs
+ do not do any dynamic linking of libtool libraries
+ -version-info CURRENT[:REVISION[:AGE]]
+ specify library version info [each variable defaults to 0]
+ -weak LIBNAME declare that the target provides the LIBNAME interface
+ -Wc,FLAG
+ -Xcompiler FLAG pass linker-specific FLAG directly to the compiler
+ -Wl,FLAG
+ -Xlinker FLAG pass linker-specific FLAG directly to the linker
+ -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC)
+
+All other options (arguments beginning with '-') are ignored.
+
+Every other argument is treated as a filename. Files ending in '.la' are
+treated as uninstalled libtool libraries, other files are standard or library
+object files.
+
+If the OUTPUT-FILE ends in '.la', then a libtool library is created,
+only library objects ('.lo' files) may be specified, and '-rpath' is
+required, except when creating a convenience library.
+
+If OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created
+using 'ar' and 'ranlib', or on Windows using 'lib'.
+
+If OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file
+is created, otherwise an executable program is created."
+ ;;
+
+ uninstall)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE...
+
+Remove libraries from an installation directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed
+to RM.
+
+If FILE is a libtool library, all the files associated with it are deleted.
+Otherwise, only FILE itself is deleted using RM."
+ ;;
+
+ *)
+ func_fatal_help "invalid operation mode '$opt_mode'"
+ ;;
+ esac
+
+ echo
+ $ECHO "Try '$progname --help' for more information about other modes."
+}
+
+# Now that we've collected a possible --mode arg, show help if necessary
+if $opt_help; then
+ if test : = "$opt_help"; then
+ func_mode_help
+ else
+ {
+ func_help noexit
+ for opt_mode in compile link execute install finish uninstall clean; do
+ func_mode_help
+ done
+ } | $SED -n '1p; 2,$s/^Usage:/ or: /p'
+ {
+ func_help noexit
+ for opt_mode in compile link execute install finish uninstall clean; do
+ echo
+ func_mode_help
+ done
+ } |
+ $SED '1d
+ /^When reporting/,/^Report/{
+ H
+ d
+ }
+ $x
+ /information about other modes/d
+ /more detailed .*MODE/d
+ s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/'
+ fi
+ exit $?
+fi
+
+
+# func_mode_execute arg...
+func_mode_execute ()
+{
+ $debug_cmd
+
+ # The first argument is the command name.
+ cmd=$nonopt
+ test -z "$cmd" && \
+ func_fatal_help "you must specify a COMMAND"
+
+ # Handle -dlopen flags immediately.
+ for file in $opt_dlopen; do
+ test -f "$file" \
+ || func_fatal_help "'$file' is not a file"
+
+ dir=
+ case $file in
+ *.la)
+ func_resolve_sysroot "$file"
+ file=$func_resolve_sysroot_result
+
+ # Check to see that this really is a libtool archive.
+ func_lalib_unsafe_p "$file" \
+ || func_fatal_help "'$lib' is not a valid libtool archive"
+
+ # Read the libtool library.
+ dlname=
+ library_names=
+ func_source "$file"
+
+ # Skip this library if it cannot be dlopened.
+ if test -z "$dlname"; then
+ # Warn if it was a shared library.
+ test -n "$library_names" && \
+ func_warning "'$file' was not linked with '-export-dynamic'"
+ continue
+ fi
+
+ func_dirname "$file" "" "."
+ dir=$func_dirname_result
+
+ if test -f "$dir/$objdir/$dlname"; then
+ func_append dir "/$objdir"
+ else
+ if test ! -f "$dir/$dlname"; then
+ func_fatal_error "cannot find '$dlname' in '$dir' or '$dir/$objdir'"
+ fi
+ fi
+ ;;
+
+ *.lo)
+ # Just add the directory containing the .lo file.
+ func_dirname "$file" "" "."
+ dir=$func_dirname_result
+ ;;
+
+ *)
+ func_warning "'-dlopen' is ignored for non-libtool libraries and objects"
+ continue
+ ;;
+ esac
+
+ # Get the absolute pathname.
+ absdir=`cd "$dir" && pwd`
+ test -n "$absdir" && dir=$absdir
+
+ # Now add the directory to shlibpath_var.
+ if eval "test -z \"\$$shlibpath_var\""; then
+ eval "$shlibpath_var=\"\$dir\""
+ else
+ eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\""
+ fi
+ done
+
+ # This variable tells wrapper scripts just to set shlibpath_var
+ # rather than running their programs.
+ libtool_execute_magic=$magic
+
+ # Check if any of the arguments is a wrapper script.
+ args=
+ for file
+ do
+ case $file in
+ -* | *.la | *.lo ) ;;
+ *)
+ # Do a test to see if this is really a libtool program.
+ if func_ltwrapper_script_p "$file"; then
+ func_source "$file"
+ # Transform arg to wrapped name.
+ file=$progdir/$program
+ elif func_ltwrapper_executable_p "$file"; then
+ func_ltwrapper_scriptname "$file"
+ func_source "$func_ltwrapper_scriptname_result"
+ # Transform arg to wrapped name.
+ file=$progdir/$program
+ fi
+ ;;
+ esac
+ # Quote arguments (to preserve shell metacharacters).
+ func_append_quoted args "$file"
+ done
+
+ if $opt_dry_run; then
+ # Display what would be done.
+ if test -n "$shlibpath_var"; then
+ eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\""
+ echo "export $shlibpath_var"
+ fi
+ $ECHO "$cmd$args"
+ exit $EXIT_SUCCESS
+ else
+ if test -n "$shlibpath_var"; then
+ # Export the shlibpath_var.
+ eval "export $shlibpath_var"
+ fi
+
+ # Restore saved environment variables
+ for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+ do
+ eval "if test \"\${save_$lt_var+set}\" = set; then
+ $lt_var=\$save_$lt_var; export $lt_var
+ else
+ $lt_unset $lt_var
+ fi"
+ done
+
+ # Now prepare to actually exec the command.
+ exec_cmd=\$cmd$args
+ fi
+}
+
+test execute = "$opt_mode" && func_mode_execute ${1+"$@"}
+
+
+# func_mode_finish arg...
+func_mode_finish ()
+{
+ $debug_cmd
+
+ libs=
+ libdirs=
+ admincmds=
+
+ for opt in "$nonopt" ${1+"$@"}
+ do
+ if test -d "$opt"; then
+ func_append libdirs " $opt"
+
+ elif test -f "$opt"; then
+ if func_lalib_unsafe_p "$opt"; then
+ func_append libs " $opt"
+ else
+ func_warning "'$opt' is not a valid libtool archive"
+ fi
+
+ else
+ func_fatal_error "invalid argument '$opt'"
+ fi
+ done
+
+ if test -n "$libs"; then
+ if test -n "$lt_sysroot"; then
+ sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"`
+ sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;"
+ else
+ sysroot_cmd=
+ fi
+
+ # Remove sysroot references
+ if $opt_dry_run; then
+ for lib in $libs; do
+ echo "removing references to $lt_sysroot and '=' prefixes from $lib"
+ done
+ else
+ tmpdir=`func_mktempdir`
+ for lib in $libs; do
+ $SED -e "$sysroot_cmd s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \
+ > $tmpdir/tmp-la
+ mv -f $tmpdir/tmp-la $lib
+ done
+ ${RM}r "$tmpdir"
+ fi
+ fi
+
+ if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+ for libdir in $libdirs; do
+ if test -n "$finish_cmds"; then
+ # Do each command in the finish commands.
+ func_execute_cmds "$finish_cmds" 'admincmds="$admincmds
+'"$cmd"'"'
+ fi
+ if test -n "$finish_eval"; then
+ # Do the single finish_eval.
+ eval cmds=\"$finish_eval\"
+ $opt_dry_run || eval "$cmds" || func_append admincmds "
+ $cmds"
+ fi
+ done
+ fi
+
+ # Exit here if they wanted silent mode.
+ $opt_quiet && exit $EXIT_SUCCESS
+
+ if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+ echo "----------------------------------------------------------------------"
+ echo "Libraries have been installed in:"
+ for libdir in $libdirs; do
+ $ECHO " $libdir"
+ done
+ echo
+ echo "If you ever happen to want to link against installed libraries"
+ echo "in a given directory, LIBDIR, you must either use libtool, and"
+ echo "specify the full pathname of the library, or use the '-LLIBDIR'"
+ echo "flag during linking and do at least one of the following:"
+ if test -n "$shlibpath_var"; then
+ echo " - add LIBDIR to the '$shlibpath_var' environment variable"
+ echo " during execution"
+ fi
+ if test -n "$runpath_var"; then
+ echo " - add LIBDIR to the '$runpath_var' environment variable"
+ echo " during linking"
+ fi
+ if test -n "$hardcode_libdir_flag_spec"; then
+ libdir=LIBDIR
+ eval flag=\"$hardcode_libdir_flag_spec\"
+
+ $ECHO " - use the '$flag' linker flag"
+ fi
+ if test -n "$admincmds"; then
+ $ECHO " - have your system administrator run these commands:$admincmds"
+ fi
+ if test -f /etc/ld.so.conf; then
+ echo " - have your system administrator add LIBDIR to '/etc/ld.so.conf'"
+ fi
+ echo
+
+ echo "See any operating system documentation about shared libraries for"
+ case $host in
+ solaris2.[6789]|solaris2.1[0-9])
+ echo "more information, such as the ld(1), crle(1) and ld.so(8) manual"
+ echo "pages."
+ ;;
+ *)
+ echo "more information, such as the ld(1) and ld.so(8) manual pages."
+ ;;
+ esac
+ echo "----------------------------------------------------------------------"
+ fi
+ exit $EXIT_SUCCESS
+}
+
+test finish = "$opt_mode" && func_mode_finish ${1+"$@"}
+
+
+# func_mode_install arg...
+func_mode_install ()
+{
+ $debug_cmd
+
+ # There may be an optional sh(1) argument at the beginning of
+ # install_prog (especially on Windows NT).
+ if test "$SHELL" = "$nonopt" || test /bin/sh = "$nonopt" ||
+ # Allow the use of GNU shtool's install command.
+ case $nonopt in *shtool*) :;; *) false;; esac
+ then
+ # Aesthetically quote it.
+ func_quote_for_eval "$nonopt"
+ install_prog="$func_quote_for_eval_result "
+ arg=$1
+ shift
+ else
+ install_prog=
+ arg=$nonopt
+ fi
+
+ # The real first argument should be the name of the installation program.
+ # Aesthetically quote it.
+ func_quote_for_eval "$arg"
+ func_append install_prog "$func_quote_for_eval_result"
+ install_shared_prog=$install_prog
+ case " $install_prog " in
+ *[\\\ /]cp\ *) install_cp=: ;;
+ *) install_cp=false ;;
+ esac
+
+ # We need to accept at least all the BSD install flags.
+ dest=
+ files=
+ opts=
+ prev=
+ install_type=
+ isdir=false
+ stripme=
+ no_mode=:
+ for arg
+ do
+ arg2=
+ if test -n "$dest"; then
+ func_append files " $dest"
+ dest=$arg
+ continue
+ fi
+
+ case $arg in
+ -d) isdir=: ;;
+ -f)
+ if $install_cp; then :; else
+ prev=$arg
+ fi
+ ;;
+ -g | -m | -o)
+ prev=$arg
+ ;;
+ -s)
+ stripme=" -s"
+ continue
+ ;;
+ -*)
+ ;;
+ *)
+ # If the previous option needed an argument, then skip it.
+ if test -n "$prev"; then
+ if test X-m = "X$prev" && test -n "$install_override_mode"; then
+ arg2=$install_override_mode
+ no_mode=false
+ fi
+ prev=
+ else
+ dest=$arg
+ continue
+ fi
+ ;;
+ esac
+
+ # Aesthetically quote the argument.
+ func_quote_for_eval "$arg"
+ func_append install_prog " $func_quote_for_eval_result"
+ if test -n "$arg2"; then
+ func_quote_for_eval "$arg2"
+ fi
+ func_append install_shared_prog " $func_quote_for_eval_result"
+ done
+
+ test -z "$install_prog" && \
+ func_fatal_help "you must specify an install program"
+
+ test -n "$prev" && \
+ func_fatal_help "the '$prev' option requires an argument"
+
+ if test -n "$install_override_mode" && $no_mode; then
+ if $install_cp; then :; else
+ func_quote_for_eval "$install_override_mode"
+ func_append install_shared_prog " -m $func_quote_for_eval_result"
+ fi
+ fi
+
+ if test -z "$files"; then
+ if test -z "$dest"; then
+ func_fatal_help "no file or destination specified"
+ else
+ func_fatal_help "you must specify a destination"
+ fi
+ fi
+
+ # Strip any trailing slash from the destination.
+ func_stripname '' '/' "$dest"
+ dest=$func_stripname_result
+
+ # Check to see that the destination is a directory.
+ test -d "$dest" && isdir=:
+ if $isdir; then
+ destdir=$dest
+ destname=
+ else
+ func_dirname_and_basename "$dest" "" "."
+ destdir=$func_dirname_result
+ destname=$func_basename_result
+
+ # Not a directory, so check to see that there is only one file specified.
+ set dummy $files; shift
+ test "$#" -gt 1 && \
+ func_fatal_help "'$dest' is not a directory"
+ fi
+ case $destdir in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ for file in $files; do
+ case $file in
+ *.lo) ;;
+ *)
+ func_fatal_help "'$destdir' must be an absolute directory name"
+ ;;
+ esac
+ done
+ ;;
+ esac
+
+ # This variable tells wrapper scripts just to set variables rather
+ # than running their programs.
+ libtool_install_magic=$magic
+
+ staticlibs=
+ future_libdirs=
+ current_libdirs=
+ for file in $files; do
+
+ # Do each installation.
+ case $file in
+ *.$libext)
+ # Do the static libraries later.
+ func_append staticlibs " $file"
+ ;;
+
+ *.la)
+ func_resolve_sysroot "$file"
+ file=$func_resolve_sysroot_result
+
+ # Check to see that this really is a libtool archive.
+ func_lalib_unsafe_p "$file" \
+ || func_fatal_help "'$file' is not a valid libtool archive"
+
+ library_names=
+ old_library=
+ relink_command=
+ func_source "$file"
+
+ # Add the libdir to current_libdirs if it is the destination.
+ if test "X$destdir" = "X$libdir"; then
+ case "$current_libdirs " in
+ *" $libdir "*) ;;
+ *) func_append current_libdirs " $libdir" ;;
+ esac
+ else
+ # Note the libdir as a future libdir.
+ case "$future_libdirs " in
+ *" $libdir "*) ;;
+ *) func_append future_libdirs " $libdir" ;;
+ esac
+ fi
+
+ func_dirname "$file" "/" ""
+ dir=$func_dirname_result
+ func_append dir "$objdir"
+
+ if test -n "$relink_command"; then
+ # Determine the prefix the user has applied to our future dir.
+ inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"`
+
+ # Don't allow the user to place us outside of our expected
+ # location b/c this prevents finding dependent libraries that
+ # are installed to the same prefix.
+ # At present, this check doesn't affect windows .dll's that
+ # are installed into $libdir/../bin (currently, that works fine)
+ # but it's something to keep an eye on.
+ test "$inst_prefix_dir" = "$destdir" && \
+ func_fatal_error "error: cannot install '$file' to a directory not ending in $libdir"
+
+ if test -n "$inst_prefix_dir"; then
+ # Stick the inst_prefix_dir data into the link command.
+ relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"`
+ else
+ relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"`
+ fi
+
+ func_warning "relinking '$file'"
+ func_show_eval "$relink_command" \
+ 'func_fatal_error "error: relink '\''$file'\'' with the above command before installing it"'
+ fi
+
+ # See the names of the shared library.
+ set dummy $library_names; shift
+ if test -n "$1"; then
+ realname=$1
+ shift
+
+ srcname=$realname
+ test -n "$relink_command" && srcname=${realname}T
+
+ # Install the shared library and build the symlinks.
+ func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \
+ 'exit $?'
+ tstripme=$stripme
+ case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ case $realname in
+ *.dll.a)
+ tstripme=
+ ;;
+ esac
+ ;;
+ os2*)
+ case $realname in
+ *_dll.a)
+ tstripme=
+ ;;
+ esac
+ ;;
+ esac
+ if test -n "$tstripme" && test -n "$striplib"; then
+ func_show_eval "$striplib $destdir/$realname" 'exit $?'
+ fi
+
+ if test "$#" -gt 0; then
+ # Delete the old symlinks, and create new ones.
+ # Try 'ln -sf' first, because the 'ln' binary might depend on
+ # the symlink we replace! Solaris /bin/ln does not understand -f,
+ # so we also need to try rm && ln -s.
+ for linkname
+ do
+ test "$linkname" != "$realname" \
+ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })"
+ done
+ fi
+
+ # Do each command in the postinstall commands.
+ lib=$destdir/$realname
+ func_execute_cmds "$postinstall_cmds" 'exit $?'
+ fi
+
+ # Install the pseudo-library for information purposes.
+ func_basename "$file"
+ name=$func_basename_result
+ instname=$dir/${name}i
+ func_show_eval "$install_prog $instname $destdir/$name" 'exit $?'
+
+ # Maybe install the static library, too.
+ test -n "$old_library" && func_append staticlibs " $dir/$old_library"
+ ;;
+
+ *.lo)
+ # Install (i.e. copy) a libtool object.
+
+ # Figure out destination file name, if it wasn't already specified.
+ if test -n "$destname"; then
+ destfile=$destdir/$destname
+ else
+ func_basename "$file"
+ destfile=$func_basename_result
+ destfile=$destdir/$destfile
+ fi
+
+ # Deduce the name of the destination old-style object file.
+ case $destfile in
+ *.lo)
+ func_lo2o "$destfile"
+ staticdest=$func_lo2o_result
+ ;;
+ *.$objext)
+ staticdest=$destfile
+ destfile=
+ ;;
+ *)
+ func_fatal_help "cannot copy a libtool object to '$destfile'"
+ ;;
+ esac
+
+ # Install the libtool object if requested.
+ test -n "$destfile" && \
+ func_show_eval "$install_prog $file $destfile" 'exit $?'
+
+ # Install the old object if enabled.
+ if test yes = "$build_old_libs"; then
+ # Deduce the name of the old-style object file.
+ func_lo2o "$file"
+ staticobj=$func_lo2o_result
+ func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?'
+ fi
+ exit $EXIT_SUCCESS
+ ;;
+
+ *)
+ # Figure out destination file name, if it wasn't already specified.
+ if test -n "$destname"; then
+ destfile=$destdir/$destname
+ else
+ func_basename "$file"
+ destfile=$func_basename_result
+ destfile=$destdir/$destfile
+ fi
+
+ # If the file is missing, and there is a .exe on the end, strip it
+ # because it is most likely a libtool script we actually want to
+ # install
+ stripped_ext=
+ case $file in
+ *.exe)
+ if test ! -f "$file"; then
+ func_stripname '' '.exe' "$file"
+ file=$func_stripname_result
+ stripped_ext=.exe
+ fi
+ ;;
+ esac
+
+ # Do a test to see if this is really a libtool program.
+ case $host in
+ *cygwin* | *mingw*)
+ if func_ltwrapper_executable_p "$file"; then
+ func_ltwrapper_scriptname "$file"
+ wrapper=$func_ltwrapper_scriptname_result
+ else
+ func_stripname '' '.exe' "$file"
+ wrapper=$func_stripname_result
+ fi
+ ;;
+ *)
+ wrapper=$file
+ ;;
+ esac
+ if func_ltwrapper_script_p "$wrapper"; then
+ notinst_deplibs=
+ relink_command=
+
+ func_source "$wrapper"
+
+ # Check the variables that should have been set.
+ test -z "$generated_by_libtool_version" && \
+ func_fatal_error "invalid libtool wrapper script '$wrapper'"
+
+ finalize=:
+ for lib in $notinst_deplibs; do
+ # Check to see that each library is installed.
+ libdir=
+ if test -f "$lib"; then
+ func_source "$lib"
+ fi
+ libfile=$libdir/`$ECHO "$lib" | $SED 's%^.*/%%g'`
+ if test -n "$libdir" && test ! -f "$libfile"; then
+ func_warning "'$lib' has not been installed in '$libdir'"
+ finalize=false
+ fi
+ done
+
+ relink_command=
+ func_source "$wrapper"
+
+ outputname=
+ if test no = "$fast_install" && test -n "$relink_command"; then
+ $opt_dry_run || {
+ if $finalize; then
+ tmpdir=`func_mktempdir`
+ func_basename "$file$stripped_ext"
+ file=$func_basename_result
+ outputname=$tmpdir/$file
+ # Replace the output file specification.
+ relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'`
+
+ $opt_quiet || {
+ func_quote_for_expand "$relink_command"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+ if eval "$relink_command"; then :
+ else
+ func_error "error: relink '$file' with the above command before installing it"
+ $opt_dry_run || ${RM}r "$tmpdir"
+ continue
+ fi
+ file=$outputname
+ else
+ func_warning "cannot relink '$file'"
+ fi
+ }
+ else
+ # Install the binary that we compiled earlier.
+ file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"`
+ fi
+ fi
+
+ # remove .exe since cygwin /usr/bin/install will append another
+ # one anyway
+ case $install_prog,$host in
+ */usr/bin/install*,*cygwin*)
+ case $file:$destfile in
+ *.exe:*.exe)
+ # this is ok
+ ;;
+ *.exe:*)
+ destfile=$destfile.exe
+ ;;
+ *:*.exe)
+ func_stripname '' '.exe' "$destfile"
+ destfile=$func_stripname_result
+ ;;
+ esac
+ ;;
+ esac
+ func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?'
+ $opt_dry_run || if test -n "$outputname"; then
+ ${RM}r "$tmpdir"
+ fi
+ ;;
+ esac
+ done
+
+ for file in $staticlibs; do
+ func_basename "$file"
+ name=$func_basename_result
+
+ # Set up the ranlib parameters.
+ oldlib=$destdir/$name
+ func_to_tool_file "$oldlib" func_convert_file_msys_to_w32
+ tool_oldlib=$func_to_tool_file_result
+
+ func_show_eval "$install_prog \$file \$oldlib" 'exit $?'
+
+ if test -n "$stripme" && test -n "$old_striplib"; then
+ func_show_eval "$old_striplib $tool_oldlib" 'exit $?'
+ fi
+
+ # Do each command in the postinstall commands.
+ func_execute_cmds "$old_postinstall_cmds" 'exit $?'
+ done
+
+ test -n "$future_libdirs" && \
+ func_warning "remember to run '$progname --finish$future_libdirs'"
+
+ if test -n "$current_libdirs"; then
+ # Maybe just do a dry run.
+ $opt_dry_run && current_libdirs=" -n$current_libdirs"
+ exec_cmd='$SHELL "$progpath" $preserve_args --finish$current_libdirs'
+ else
+ exit $EXIT_SUCCESS
+ fi
+}
+
+test install = "$opt_mode" && func_mode_install ${1+"$@"}
+
+
+# func_generate_dlsyms outputname originator pic_p
+# Extract symbols from dlprefiles and create ${outputname}S.o with
+# a dlpreopen symbol table.
+func_generate_dlsyms ()
+{
+ $debug_cmd
+
+ my_outputname=$1
+ my_originator=$2
+ my_pic_p=${3-false}
+ my_prefix=`$ECHO "$my_originator" | $SED 's%[^a-zA-Z0-9]%_%g'`
+ my_dlsyms=
+
+ if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then
+ if test -n "$NM" && test -n "$global_symbol_pipe"; then
+ my_dlsyms=${my_outputname}S.c
+ else
+ func_error "not configured to extract global symbols from dlpreopened files"
+ fi
+ fi
+
+ if test -n "$my_dlsyms"; then
+ case $my_dlsyms in
+ "") ;;
+ *.c)
+ # Discover the nlist of each of the dlfiles.
+ nlist=$output_objdir/$my_outputname.nm
+
+ func_show_eval "$RM $nlist ${nlist}S ${nlist}T"
+
+ # Parse the name list into a source file.
+ func_verbose "creating $output_objdir/$my_dlsyms"
+
+ $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\
+/* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */
+/* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */
+
+#ifdef __cplusplus
+extern \"C\" {
+#endif
+
+#if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4))
+#pragma GCC diagnostic ignored \"-Wstrict-prototypes\"
+#endif
+
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */
+#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE
+/* DATA imports from DLLs on WIN32 can't be const, because runtime
+ relocations are performed -- see ld's documentation on pseudo-relocs. */
+# define LT_DLSYM_CONST
+#elif defined __osf__
+/* This system does not cope well with relocations in const data. */
+# define LT_DLSYM_CONST
+#else
+# define LT_DLSYM_CONST const
+#endif
+
+#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0)
+
+/* External symbol declarations for the compiler. */\
+"
+
+ if test yes = "$dlself"; then
+ func_verbose "generating symbol list for '$output'"
+
+ $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist"
+
+ # Add our own program objects to the symbol list.
+ progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP`
+ for progfile in $progfiles; do
+ func_to_tool_file "$progfile" func_convert_file_msys_to_w32
+ func_verbose "extracting global C symbols from '$func_to_tool_file_result'"
+ $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'"
+ done
+
+ if test -n "$exclude_expsyms"; then
+ $opt_dry_run || {
+ eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T'
+ eval '$MV "$nlist"T "$nlist"'
+ }
+ fi
+
+ if test -n "$export_symbols_regex"; then
+ $opt_dry_run || {
+ eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T'
+ eval '$MV "$nlist"T "$nlist"'
+ }
+ fi
+
+ # Prepare the list of exported symbols
+ if test -z "$export_symbols"; then
+ export_symbols=$output_objdir/$outputname.exp
+ $opt_dry_run || {
+ $RM $export_symbols
+ eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"'
+ case $host in
+ *cygwin* | *mingw* | *cegcc* )
+ eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+ eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"'
+ ;;
+ esac
+ }
+ else
+ $opt_dry_run || {
+ eval "$SED -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"'
+ eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T'
+ eval '$MV "$nlist"T "$nlist"'
+ case $host in
+ *cygwin* | *mingw* | *cegcc* )
+ eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+ eval 'cat "$nlist" >> "$output_objdir/$outputname.def"'
+ ;;
+ esac
+ }
+ fi
+ fi
+
+ for dlprefile in $dlprefiles; do
+ func_verbose "extracting global C symbols from '$dlprefile'"
+ func_basename "$dlprefile"
+ name=$func_basename_result
+ case $host in
+ *cygwin* | *mingw* | *cegcc* )
+ # if an import library, we need to obtain dlname
+ if func_win32_import_lib_p "$dlprefile"; then
+ func_tr_sh "$dlprefile"
+ eval "curr_lafile=\$libfile_$func_tr_sh_result"
+ dlprefile_dlbasename=
+ if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then
+ # Use subshell, to avoid clobbering current variable values
+ dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"`
+ if test -n "$dlprefile_dlname"; then
+ func_basename "$dlprefile_dlname"
+ dlprefile_dlbasename=$func_basename_result
+ else
+ # no lafile. user explicitly requested -dlpreopen <import library>.
+ $sharedlib_from_linklib_cmd "$dlprefile"
+ dlprefile_dlbasename=$sharedlib_from_linklib_result
+ fi
+ fi
+ $opt_dry_run || {
+ if test -n "$dlprefile_dlbasename"; then
+ eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"'
+ else
+ func_warning "Could not compute DLL name from $name"
+ eval '$ECHO ": $name " >> "$nlist"'
+ fi
+ func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+ eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe |
+ $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'"
+ }
+ else # not an import lib
+ $opt_dry_run || {
+ eval '$ECHO ": $name " >> "$nlist"'
+ func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+ eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'"
+ }
+ fi
+ ;;
+ *)
+ $opt_dry_run || {
+ eval '$ECHO ": $name " >> "$nlist"'
+ func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+ eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'"
+ }
+ ;;
+ esac
+ done
+
+ $opt_dry_run || {
+ # Make sure we have at least an empty file.
+ test -f "$nlist" || : > "$nlist"
+
+ if test -n "$exclude_expsyms"; then
+ $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T
+ $MV "$nlist"T "$nlist"
+ fi
+
+ # Try sorting and uniquifying the output.
+ if $GREP -v "^: " < "$nlist" |
+ if sort -k 3 </dev/null >/dev/null 2>&1; then
+ sort -k 3
+ else
+ sort +2
+ fi |
+ uniq > "$nlist"S; then
+ :
+ else
+ $GREP -v "^: " < "$nlist" > "$nlist"S
+ fi
+
+ if test -f "$nlist"S; then
+ eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"'
+ else
+ echo '/* NONE */' >> "$output_objdir/$my_dlsyms"
+ fi
+
+ func_show_eval '$RM "${nlist}I"'
+ if test -n "$global_symbol_to_import"; then
+ eval "$global_symbol_to_import"' < "$nlist"S > "$nlist"I'
+ fi
+
+ echo >> "$output_objdir/$my_dlsyms" "\
+
+/* The mapping between symbol names and symbols. */
+typedef struct {
+ const char *name;
+ void *address;
+} lt_dlsymlist;
+extern LT_DLSYM_CONST lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[];\
+"
+
+ if test -s "$nlist"I; then
+ echo >> "$output_objdir/$my_dlsyms" "\
+static void lt_syminit(void)
+{
+ LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols;
+ for (; symbol->name; ++symbol)
+ {"
+ $SED 's/.*/ if (STREQ (symbol->name, \"&\")) symbol->address = (void *) \&&;/' < "$nlist"I >> "$output_objdir/$my_dlsyms"
+ echo >> "$output_objdir/$my_dlsyms" "\
+ }
+}"
+ fi
+ echo >> "$output_objdir/$my_dlsyms" "\
+LT_DLSYM_CONST lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[] =
+{ {\"$my_originator\", (void *) 0},"
+
+ if test -s "$nlist"I; then
+ echo >> "$output_objdir/$my_dlsyms" "\
+ {\"@INIT@\", (void *) &lt_syminit},"
+ fi
+
+ case $need_lib_prefix in
+ no)
+ eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms"
+ ;;
+ *)
+ eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms"
+ ;;
+ esac
+ echo >> "$output_objdir/$my_dlsyms" "\
+ {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt_${my_prefix}_LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif\
+"
+ } # !$opt_dry_run
+
+ pic_flag_for_symtable=
+ case "$compile_command " in
+ *" -static "*) ;;
+ *)
+ case $host in
+ # compiling the symbol table file with pic_flag works around
+ # a FreeBSD bug that causes programs to crash when -lm is
+ # linked before any other PIC object. But we must not use
+ # pic_flag when linking with -static. The problem exists in
+ # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1.
+ *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*)
+ pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;;
+ *-*-hpux*)
+ pic_flag_for_symtable=" $pic_flag" ;;
+ *)
+ $my_pic_p && pic_flag_for_symtable=" $pic_flag"
+ ;;
+ esac
+ ;;
+ esac
+ symtab_cflags=
+ for arg in $LTCFLAGS; do
+ case $arg in
+ -pie | -fpie | -fPIE) ;;
+ *) func_append symtab_cflags " $arg" ;;
+ esac
+ done
+
+ # Now compile the dynamic symbol file.
+ func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?'
+
+ # Clean up the generated files.
+ func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T" "${nlist}I"'
+
+ # Transform the symbol file into the correct name.
+ symfileobj=$output_objdir/${my_outputname}S.$objext
+ case $host in
+ *cygwin* | *mingw* | *cegcc* )
+ if test -f "$output_objdir/$my_outputname.def"; then
+ compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+ finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+ else
+ compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+ finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+ fi
+ ;;
+ *)
+ compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+ finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+ ;;
+ esac
+ ;;
+ *)
+ func_fatal_error "unknown suffix for '$my_dlsyms'"
+ ;;
+ esac
+ else
+ # We keep going just in case the user didn't refer to
+ # lt_preloaded_symbols. The linker will fail if global_symbol_pipe
+ # really was required.
+
+ # Nullify the symbol file.
+ compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"`
+ finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"`
+ fi
+}
+
+# func_cygming_gnu_implib_p ARG
+# This predicate returns with zero status (TRUE) if
+# ARG is a GNU/binutils-style import library. Returns
+# with nonzero status (FALSE) otherwise.
+func_cygming_gnu_implib_p ()
+{
+ $debug_cmd
+
+ func_to_tool_file "$1" func_convert_file_msys_to_w32
+ func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'`
+ test -n "$func_cygming_gnu_implib_tmp"
+}
+
+# func_cygming_ms_implib_p ARG
+# This predicate returns with zero status (TRUE) if
+# ARG is an MS-style import library. Returns
+# with nonzero status (FALSE) otherwise.
+func_cygming_ms_implib_p ()
+{
+ $debug_cmd
+
+ func_to_tool_file "$1" func_convert_file_msys_to_w32
+ func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'`
+ test -n "$func_cygming_ms_implib_tmp"
+}
+
+# func_win32_libid arg
+# return the library type of file 'arg'
+#
+# Need a lot of goo to handle *both* DLLs and import libs
+# Has to be a shell function in order to 'eat' the argument
+# that is supplied when $file_magic_command is called.
+# Despite the name, also deal with 64 bit binaries.
+func_win32_libid ()
+{
+ $debug_cmd
+
+ win32_libid_type=unknown
+ win32_fileres=`file -L $1 2>/dev/null`
+ case $win32_fileres in
+ *ar\ archive\ import\ library*) # definitely import
+ win32_libid_type="x86 archive import"
+ ;;
+ *ar\ archive*) # could be an import, or static
+ # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD.
+ if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null |
+ $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then
+ case $nm_interface in
+ "MS dumpbin")
+ if func_cygming_ms_implib_p "$1" ||
+ func_cygming_gnu_implib_p "$1"
+ then
+ win32_nmres=import
+ else
+ win32_nmres=
+ fi
+ ;;
+ *)
+ func_to_tool_file "$1" func_convert_file_msys_to_w32
+ win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" |
+ $SED -n -e '
+ 1,100{
+ / I /{
+ s|.*|import|
+ p
+ q
+ }
+ }'`
+ ;;
+ esac
+ case $win32_nmres in
+ import*) win32_libid_type="x86 archive import";;
+ *) win32_libid_type="x86 archive static";;
+ esac
+ fi
+ ;;
+ *DLL*)
+ win32_libid_type="x86 DLL"
+ ;;
+ *executable*) # but shell scripts are "executable" too...
+ case $win32_fileres in
+ *MS\ Windows\ PE\ Intel*)
+ win32_libid_type="x86 DLL"
+ ;;
+ esac
+ ;;
+ esac
+ $ECHO "$win32_libid_type"
+}
+
+# func_cygming_dll_for_implib ARG
+#
+# Platform-specific function to extract the
+# name of the DLL associated with the specified
+# import library ARG.
+# Invoked by eval'ing the libtool variable
+# $sharedlib_from_linklib_cmd
+# Result is available in the variable
+# $sharedlib_from_linklib_result
+func_cygming_dll_for_implib ()
+{
+ $debug_cmd
+
+ sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"`
+}
+
+# func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs
+#
+# The is the core of a fallback implementation of a
+# platform-specific function to extract the name of the
+# DLL associated with the specified import library LIBNAME.
+#
+# SECTION_NAME is either .idata$6 or .idata$7, depending
+# on the platform and compiler that created the implib.
+#
+# Echos the name of the DLL associated with the
+# specified import library.
+func_cygming_dll_for_implib_fallback_core ()
+{
+ $debug_cmd
+
+ match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"`
+ $OBJDUMP -s --section "$1" "$2" 2>/dev/null |
+ $SED '/^Contents of section '"$match_literal"':/{
+ # Place marker at beginning of archive member dllname section
+ s/.*/====MARK====/
+ p
+ d
+ }
+ # These lines can sometimes be longer than 43 characters, but
+ # are always uninteresting
+ /:[ ]*file format pe[i]\{,1\}-/d
+ /^In archive [^:]*:/d
+ # Ensure marker is printed
+ /^====MARK====/p
+ # Remove all lines with less than 43 characters
+ /^.\{43\}/!d
+ # From remaining lines, remove first 43 characters
+ s/^.\{43\}//' |
+ $SED -n '
+ # Join marker and all lines until next marker into a single line
+ /^====MARK====/ b para
+ H
+ $ b para
+ b
+ :para
+ x
+ s/\n//g
+ # Remove the marker
+ s/^====MARK====//
+ # Remove trailing dots and whitespace
+ s/[\. \t]*$//
+ # Print
+ /./p' |
+ # we now have a list, one entry per line, of the stringified
+ # contents of the appropriate section of all members of the
+ # archive that possess that section. Heuristic: eliminate
+ # all those that have a first or second character that is
+ # a '.' (that is, objdump's representation of an unprintable
+ # character.) This should work for all archives with less than
+ # 0x302f exports -- but will fail for DLLs whose name actually
+ # begins with a literal '.' or a single character followed by
+ # a '.'.
+ #
+ # Of those that remain, print the first one.
+ $SED -e '/^\./d;/^.\./d;q'
+}
+
+# func_cygming_dll_for_implib_fallback ARG
+# Platform-specific function to extract the
+# name of the DLL associated with the specified
+# import library ARG.
+#
+# This fallback implementation is for use when $DLLTOOL
+# does not support the --identify-strict option.
+# Invoked by eval'ing the libtool variable
+# $sharedlib_from_linklib_cmd
+# Result is available in the variable
+# $sharedlib_from_linklib_result
+func_cygming_dll_for_implib_fallback ()
+{
+ $debug_cmd
+
+ if func_cygming_gnu_implib_p "$1"; then
+ # binutils import library
+ sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"`
+ elif func_cygming_ms_implib_p "$1"; then
+ # ms-generated import library
+ sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"`
+ else
+ # unknown
+ sharedlib_from_linklib_result=
+ fi
+}
+
+
+# func_extract_an_archive dir oldlib
+func_extract_an_archive ()
+{
+ $debug_cmd
+
+ f_ex_an_ar_dir=$1; shift
+ f_ex_an_ar_oldlib=$1
+ if test yes = "$lock_old_archive_extraction"; then
+ lockfile=$f_ex_an_ar_oldlib.lock
+ until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
+ func_echo "Waiting for $lockfile to be removed"
+ sleep 2
+ done
+ fi
+ func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \
+ 'stat=$?; rm -f "$lockfile"; exit $stat'
+ if test yes = "$lock_old_archive_extraction"; then
+ $opt_dry_run || rm -f "$lockfile"
+ fi
+ if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then
+ :
+ else
+ func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib"
+ fi
+}
+
+
+# func_extract_archives gentop oldlib ...
+func_extract_archives ()
+{
+ $debug_cmd
+
+ my_gentop=$1; shift
+ my_oldlibs=${1+"$@"}
+ my_oldobjs=
+ my_xlib=
+ my_xabs=
+ my_xdir=
+
+ for my_xlib in $my_oldlibs; do
+ # Extract the objects.
+ case $my_xlib in
+ [\\/]* | [A-Za-z]:[\\/]*) my_xabs=$my_xlib ;;
+ *) my_xabs=`pwd`"/$my_xlib" ;;
+ esac
+ func_basename "$my_xlib"
+ my_xlib=$func_basename_result
+ my_xlib_u=$my_xlib
+ while :; do
+ case " $extracted_archives " in
+ *" $my_xlib_u "*)
+ func_arith $extracted_serial + 1
+ extracted_serial=$func_arith_result
+ my_xlib_u=lt$extracted_serial-$my_xlib ;;
+ *) break ;;
+ esac
+ done
+ extracted_archives="$extracted_archives $my_xlib_u"
+ my_xdir=$my_gentop/$my_xlib_u
+
+ func_mkdir_p "$my_xdir"
+
+ case $host in
+ *-darwin*)
+ func_verbose "Extracting $my_xabs"
+ # Do not bother doing anything if just a dry run
+ $opt_dry_run || {
+ darwin_orig_dir=`pwd`
+ cd $my_xdir || exit $?
+ darwin_archive=$my_xabs
+ darwin_curdir=`pwd`
+ func_basename "$darwin_archive"
+ darwin_base_archive=$func_basename_result
+ darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true`
+ if test -n "$darwin_arches"; then
+ darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'`
+ darwin_arch=
+ func_verbose "$darwin_base_archive has multiple architectures $darwin_arches"
+ for darwin_arch in $darwin_arches; do
+ func_mkdir_p "unfat-$$/$darwin_base_archive-$darwin_arch"
+ $LIPO -thin $darwin_arch -output "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" "$darwin_archive"
+ cd "unfat-$$/$darwin_base_archive-$darwin_arch"
+ func_extract_an_archive "`pwd`" "$darwin_base_archive"
+ cd "$darwin_curdir"
+ $RM "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive"
+ done # $darwin_arches
+ ## Okay now we've a bunch of thin objects, gotta fatten them up :)
+ darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$sed_basename" | sort -u`
+ darwin_file=
+ darwin_files=
+ for darwin_file in $darwin_filelist; do
+ darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP`
+ $LIPO -create -output "$darwin_file" $darwin_files
+ done # $darwin_filelist
+ $RM -rf unfat-$$
+ cd "$darwin_orig_dir"
+ else
+ cd $darwin_orig_dir
+ func_extract_an_archive "$my_xdir" "$my_xabs"
+ fi # $darwin_arches
+ } # !$opt_dry_run
+ ;;
+ *)
+ func_extract_an_archive "$my_xdir" "$my_xabs"
+ ;;
+ esac
+ my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP`
+ done
+
+ func_extract_archives_result=$my_oldobjs
+}
+
+
+# func_emit_wrapper [arg=no]
+#
+# Emit a libtool wrapper script on stdout.
+# Don't directly open a file because we may want to
+# incorporate the script contents within a cygwin/mingw
+# wrapper executable. Must ONLY be called from within
+# func_mode_link because it depends on a number of variables
+# set therein.
+#
+# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR
+# variable will take. If 'yes', then the emitted script
+# will assume that the directory where it is stored is
+# the $objdir directory. This is a cygwin/mingw-specific
+# behavior.
+func_emit_wrapper ()
+{
+ func_emit_wrapper_arg1=${1-no}
+
+ $ECHO "\
+#! $SHELL
+
+# $output - temporary wrapper script for $objdir/$outputname
+# Generated by $PROGRAM (GNU $PACKAGE) $VERSION
+#
+# The $output program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='$sed_quote_subst'
+
+# Be Bourne compatible
+if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '\${1+\"\$@\"}'='\"\$@\"'
+ setopt NO_GLOB_SUBST
+else
+ case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+relink_command=\"$relink_command\"
+
+# This environment variable determines our operation mode.
+if test \"\$libtool_install_magic\" = \"$magic\"; then
+ # install mode needs the following variables:
+ generated_by_libtool_version='$macro_version'
+ notinst_deplibs='$notinst_deplibs'
+else
+ # When we are sourced in execute mode, \$file and \$ECHO are already set.
+ if test \"\$libtool_execute_magic\" != \"$magic\"; then
+ file=\"\$0\""
+
+ qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"`
+ $ECHO "\
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+\$1
+_LTECHO_EOF'
+}
+ ECHO=\"$qECHO\"
+ fi
+
+# Very basic option parsing. These options are (a) specific to
+# the libtool wrapper, (b) are identical between the wrapper
+# /script/ and the wrapper /executable/ that is used only on
+# windows platforms, and (c) all begin with the string "--lt-"
+# (application programs are unlikely to have options that match
+# this pattern).
+#
+# There are only two supported options: --lt-debug and
+# --lt-dump-script. There is, deliberately, no --lt-help.
+#
+# The first argument to this parsing function should be the
+# script's $0 value, followed by "$@".
+lt_option_debug=
+func_parse_lt_options ()
+{
+ lt_script_arg0=\$0
+ shift
+ for lt_opt
+ do
+ case \"\$lt_opt\" in
+ --lt-debug) lt_option_debug=1 ;;
+ --lt-dump-script)
+ lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\`
+ test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=.
+ lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\`
+ cat \"\$lt_dump_D/\$lt_dump_F\"
+ exit 0
+ ;;
+ --lt-*)
+ \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2
+ exit 1
+ ;;
+ esac
+ done
+
+ # Print the debug banner immediately:
+ if test -n \"\$lt_option_debug\"; then
+ echo \"$outputname:$output:\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\" 1>&2
+ fi
+}
+
+# Used when --lt-debug. Prints its arguments to stdout
+# (redirection is the responsibility of the caller)
+func_lt_dump_args ()
+{
+ lt_dump_args_N=1;
+ for lt_arg
+ do
+ \$ECHO \"$outputname:$output:\$LINENO: newargv[\$lt_dump_args_N]: \$lt_arg\"
+ lt_dump_args_N=\`expr \$lt_dump_args_N + 1\`
+ done
+}
+
+# Core function for launching the target application
+func_exec_program_core ()
+{
+"
+ case $host in
+ # Backslashes separate directories on plain windows
+ *-*-mingw | *-*-os2* | *-cegcc*)
+ $ECHO "\
+ if test -n \"\$lt_option_debug\"; then
+ \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2
+ func_lt_dump_args \${1+\"\$@\"} 1>&2
+ fi
+ exec \"\$progdir\\\\\$program\" \${1+\"\$@\"}
+"
+ ;;
+
+ *)
+ $ECHO "\
+ if test -n \"\$lt_option_debug\"; then
+ \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir/\$program\" 1>&2
+ func_lt_dump_args \${1+\"\$@\"} 1>&2
+ fi
+ exec \"\$progdir/\$program\" \${1+\"\$@\"}
+"
+ ;;
+ esac
+ $ECHO "\
+ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2
+ exit 1
+}
+
+# A function to encapsulate launching the target application
+# Strips options in the --lt-* namespace from \$@ and
+# launches target application with the remaining arguments.
+func_exec_program ()
+{
+ case \" \$* \" in
+ *\\ --lt-*)
+ for lt_wr_arg
+ do
+ case \$lt_wr_arg in
+ --lt-*) ;;
+ *) set x \"\$@\" \"\$lt_wr_arg\"; shift;;
+ esac
+ shift
+ done ;;
+ esac
+ func_exec_program_core \${1+\"\$@\"}
+}
+
+ # Parse options
+ func_parse_lt_options \"\$0\" \${1+\"\$@\"}
+
+ # Find the directory that this script lives in.
+ thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\`
+ test \"x\$thisdir\" = \"x\$file\" && thisdir=.
+
+ # Follow symbolic links until we get to the real thisdir.
+ file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\`
+ while test -n \"\$file\"; do
+ destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\`
+
+ # If there was a directory component, then change thisdir.
+ if test \"x\$destdir\" != \"x\$file\"; then
+ case \"\$destdir\" in
+ [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;;
+ *) thisdir=\"\$thisdir/\$destdir\" ;;
+ esac
+ fi
+
+ file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\`
+ file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\`
+ done
+
+ # Usually 'no', except on cygwin/mingw when embedded into
+ # the cwrapper.
+ WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1
+ if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then
+ # special case for '.'
+ if test \"\$thisdir\" = \".\"; then
+ thisdir=\`pwd\`
+ fi
+ # remove .libs from thisdir
+ case \"\$thisdir\" in
+ *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;;
+ $objdir ) thisdir=. ;;
+ esac
+ fi
+
+ # Try to get the absolute directory name.
+ absdir=\`cd \"\$thisdir\" && pwd\`
+ test -n \"\$absdir\" && thisdir=\"\$absdir\"
+"
+
+ if test yes = "$fast_install"; then
+ $ECHO "\
+ program=lt-'$outputname'$exeext
+ progdir=\"\$thisdir/$objdir\"
+
+ if test ! -f \"\$progdir/\$program\" ||
+ { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | $SED 1q\`; \\
+ test \"X\$file\" != \"X\$progdir/\$program\"; }; then
+
+ file=\"\$\$-\$program\"
+
+ if test ! -d \"\$progdir\"; then
+ $MKDIR \"\$progdir\"
+ else
+ $RM \"\$progdir/\$file\"
+ fi"
+
+ $ECHO "\
+
+ # relink executable if necessary
+ if test -n \"\$relink_command\"; then
+ if relink_command_output=\`eval \$relink_command 2>&1\`; then :
+ else
+ \$ECHO \"\$relink_command_output\" >&2
+ $RM \"\$progdir/\$file\"
+ exit 1
+ fi
+ fi
+
+ $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null ||
+ { $RM \"\$progdir/\$program\";
+ $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; }
+ $RM \"\$progdir/\$file\"
+ fi"
+ else
+ $ECHO "\
+ program='$outputname'
+ progdir=\"\$thisdir/$objdir\"
+"
+ fi
+
+ $ECHO "\
+
+ if test -f \"\$progdir/\$program\"; then"
+
+ # fixup the dll searchpath if we need to.
+ #
+ # Fix the DLL searchpath if we need to. Do this before prepending
+ # to shlibpath, because on Windows, both are PATH and uninstalled
+ # libraries must come first.
+ if test -n "$dllsearchpath"; then
+ $ECHO "\
+ # Add the dll search path components to the executable PATH
+ PATH=$dllsearchpath:\$PATH
+"
+ fi
+
+ # Export our shlibpath_var if we have one.
+ if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+ $ECHO "\
+ # Add our own library path to $shlibpath_var
+ $shlibpath_var=\"$temp_rpath\$$shlibpath_var\"
+
+ # Some systems cannot cope with colon-terminated $shlibpath_var
+ # The second colon is a workaround for a bug in BeOS R4 sed
+ $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\`
+
+ export $shlibpath_var
+"
+ fi
+
+ $ECHO "\
+ if test \"\$libtool_execute_magic\" != \"$magic\"; then
+ # Run the actual program with our arguments.
+ func_exec_program \${1+\"\$@\"}
+ fi
+ else
+ # The program doesn't exist.
+ \$ECHO \"\$0: error: '\$progdir/\$program' does not exist\" 1>&2
+ \$ECHO \"This script is just a wrapper for \$program.\" 1>&2
+ \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2
+ exit 1
+ fi
+fi\
+"
+}
+
+
+# func_emit_cwrapperexe_src
+# emit the source code for a wrapper executable on stdout
+# Must ONLY be called from within func_mode_link because
+# it depends on a number of variable set therein.
+func_emit_cwrapperexe_src ()
+{
+ cat <<EOF
+
+/* $cwrappersource - temporary wrapper executable for $objdir/$outputname
+ Generated by $PROGRAM (GNU $PACKAGE) $VERSION
+
+ The $output program cannot be directly executed until all the libtool
+ libraries that it depends on are installed.
+
+ This wrapper executable should never be moved out of the build directory.
+ If it is, it will not operate correctly.
+*/
+EOF
+ cat <<"EOF"
+#ifdef _MSC_VER
+# define _CRT_SECURE_NO_DEPRECATE 1
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _MSC_VER
+# include <direct.h>
+# include <process.h>
+# include <io.h>
+#else
+# include <unistd.h>
+# include <stdint.h>
+# ifdef __CYGWIN__
+# include <io.h>
+# endif
+#endif
+#include <malloc.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0)
+
+/* declarations of non-ANSI functions */
+#if defined __MINGW32__
+# ifdef __STRICT_ANSI__
+int _putenv (const char *);
+# endif
+#elif defined __CYGWIN__
+# ifdef __STRICT_ANSI__
+char *realpath (const char *, char *);
+int putenv (char *);
+int setenv (const char *, const char *, int);
+# endif
+/* #elif defined other_platform || defined ... */
+#endif
+
+/* portability defines, excluding path handling macros */
+#if defined _MSC_VER
+# define setmode _setmode
+# define stat _stat
+# define chmod _chmod
+# define getcwd _getcwd
+# define putenv _putenv
+# define S_IXUSR _S_IEXEC
+#elif defined __MINGW32__
+# define setmode _setmode
+# define stat _stat
+# define chmod _chmod
+# define getcwd _getcwd
+# define putenv _putenv
+#elif defined __CYGWIN__
+# define HAVE_SETENV
+# define FOPEN_WB "wb"
+/* #elif defined other platforms ... */
+#endif
+
+#if defined PATH_MAX
+# define LT_PATHMAX PATH_MAX
+#elif defined MAXPATHLEN
+# define LT_PATHMAX MAXPATHLEN
+#else
+# define LT_PATHMAX 1024
+#endif
+
+#ifndef S_IXOTH
+# define S_IXOTH 0
+#endif
+#ifndef S_IXGRP
+# define S_IXGRP 0
+#endif
+
+/* path handling portability macros */
+#ifndef DIR_SEPARATOR
+# define DIR_SEPARATOR '/'
+# define PATH_SEPARATOR ':'
+#endif
+
+#if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \
+ defined __OS2__
+# define HAVE_DOS_BASED_FILE_SYSTEM
+# define FOPEN_WB "wb"
+# ifndef DIR_SEPARATOR_2
+# define DIR_SEPARATOR_2 '\\'
+# endif
+# ifndef PATH_SEPARATOR_2
+# define PATH_SEPARATOR_2 ';'
+# endif
+#endif
+
+#ifndef DIR_SEPARATOR_2
+# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
+#else /* DIR_SEPARATOR_2 */
+# define IS_DIR_SEPARATOR(ch) \
+ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
+#endif /* DIR_SEPARATOR_2 */
+
+#ifndef PATH_SEPARATOR_2
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR)
+#else /* PATH_SEPARATOR_2 */
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2)
+#endif /* PATH_SEPARATOR_2 */
+
+#ifndef FOPEN_WB
+# define FOPEN_WB "w"
+#endif
+#ifndef _O_BINARY
+# define _O_BINARY 0
+#endif
+
+#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type)))
+#define XFREE(stale) do { \
+ if (stale) { free (stale); stale = 0; } \
+} while (0)
+
+#if defined LT_DEBUGWRAPPER
+static int lt_debug = 1;
+#else
+static int lt_debug = 0;
+#endif
+
+const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */
+
+void *xmalloc (size_t num);
+char *xstrdup (const char *string);
+const char *base_name (const char *name);
+char *find_executable (const char *wrapper);
+char *chase_symlinks (const char *pathspec);
+int make_executable (const char *path);
+int check_executable (const char *path);
+char *strendzap (char *str, const char *pat);
+void lt_debugprintf (const char *file, int line, const char *fmt, ...);
+void lt_fatal (const char *file, int line, const char *message, ...);
+static const char *nonnull (const char *s);
+static const char *nonempty (const char *s);
+void lt_setenv (const char *name, const char *value);
+char *lt_extend_str (const char *orig_value, const char *add, int to_end);
+void lt_update_exe_path (const char *name, const char *value);
+void lt_update_lib_path (const char *name, const char *value);
+char **prepare_spawn (char **argv);
+void lt_dump_script (FILE *f);
+EOF
+
+ cat <<EOF
+#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 5)
+# define externally_visible volatile
+#else
+# define externally_visible __attribute__((externally_visible)) volatile
+#endif
+externally_visible const char * MAGIC_EXE = "$magic_exe";
+const char * LIB_PATH_VARNAME = "$shlibpath_var";
+EOF
+
+ if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+ func_to_host_path "$temp_rpath"
+ cat <<EOF
+const char * LIB_PATH_VALUE = "$func_to_host_path_result";
+EOF
+ else
+ cat <<"EOF"
+const char * LIB_PATH_VALUE = "";
+EOF
+ fi
+
+ if test -n "$dllsearchpath"; then
+ func_to_host_path "$dllsearchpath:"
+ cat <<EOF
+const char * EXE_PATH_VARNAME = "PATH";
+const char * EXE_PATH_VALUE = "$func_to_host_path_result";
+EOF
+ else
+ cat <<"EOF"
+const char * EXE_PATH_VARNAME = "";
+const char * EXE_PATH_VALUE = "";
+EOF
+ fi
+
+ if test yes = "$fast_install"; then
+ cat <<EOF
+const char * TARGET_PROGRAM_NAME = "lt-$outputname"; /* hopefully, no .exe */
+EOF
+ else
+ cat <<EOF
+const char * TARGET_PROGRAM_NAME = "$outputname"; /* hopefully, no .exe */
+EOF
+ fi
+
+
+ cat <<"EOF"
+
+#define LTWRAPPER_OPTION_PREFIX "--lt-"
+
+static const char *ltwrapper_option_prefix = LTWRAPPER_OPTION_PREFIX;
+static const char *dumpscript_opt = LTWRAPPER_OPTION_PREFIX "dump-script";
+static const char *debug_opt = LTWRAPPER_OPTION_PREFIX "debug";
+
+int
+main (int argc, char *argv[])
+{
+ char **newargz;
+ int newargc;
+ char *tmp_pathspec;
+ char *actual_cwrapper_path;
+ char *actual_cwrapper_name;
+ char *target_name;
+ char *lt_argv_zero;
+ int rval = 127;
+
+ int i;
+
+ program_name = (char *) xstrdup (base_name (argv[0]));
+ newargz = XMALLOC (char *, (size_t) argc + 1);
+
+ /* very simple arg parsing; don't want to rely on getopt
+ * also, copy all non cwrapper options to newargz, except
+ * argz[0], which is handled differently
+ */
+ newargc=0;
+ for (i = 1; i < argc; i++)
+ {
+ if (STREQ (argv[i], dumpscript_opt))
+ {
+EOF
+ case $host in
+ *mingw* | *cygwin* )
+ # make stdout use "unix" line endings
+ echo " setmode(1,_O_BINARY);"
+ ;;
+ esac
+
+ cat <<"EOF"
+ lt_dump_script (stdout);
+ return 0;
+ }
+ if (STREQ (argv[i], debug_opt))
+ {
+ lt_debug = 1;
+ continue;
+ }
+ if (STREQ (argv[i], ltwrapper_option_prefix))
+ {
+ /* however, if there is an option in the LTWRAPPER_OPTION_PREFIX
+ namespace, but it is not one of the ones we know about and
+ have already dealt with, above (inluding dump-script), then
+ report an error. Otherwise, targets might begin to believe
+ they are allowed to use options in the LTWRAPPER_OPTION_PREFIX
+ namespace. The first time any user complains about this, we'll
+ need to make LTWRAPPER_OPTION_PREFIX a configure-time option
+ or a configure.ac-settable value.
+ */
+ lt_fatal (__FILE__, __LINE__,
+ "unrecognized %s option: '%s'",
+ ltwrapper_option_prefix, argv[i]);
+ }
+ /* otherwise ... */
+ newargz[++newargc] = xstrdup (argv[i]);
+ }
+ newargz[++newargc] = NULL;
+
+EOF
+ cat <<EOF
+ /* The GNU banner must be the first non-error debug message */
+ lt_debugprintf (__FILE__, __LINE__, "libtool wrapper (GNU $PACKAGE) $VERSION\n");
+EOF
+ cat <<"EOF"
+ lt_debugprintf (__FILE__, __LINE__, "(main) argv[0]: %s\n", argv[0]);
+ lt_debugprintf (__FILE__, __LINE__, "(main) program_name: %s\n", program_name);
+
+ tmp_pathspec = find_executable (argv[0]);
+ if (tmp_pathspec == NULL)
+ lt_fatal (__FILE__, __LINE__, "couldn't find %s", argv[0]);
+ lt_debugprintf (__FILE__, __LINE__,
+ "(main) found exe (before symlink chase) at: %s\n",
+ tmp_pathspec);
+
+ actual_cwrapper_path = chase_symlinks (tmp_pathspec);
+ lt_debugprintf (__FILE__, __LINE__,
+ "(main) found exe (after symlink chase) at: %s\n",
+ actual_cwrapper_path);
+ XFREE (tmp_pathspec);
+
+ actual_cwrapper_name = xstrdup (base_name (actual_cwrapper_path));
+ strendzap (actual_cwrapper_path, actual_cwrapper_name);
+
+ /* wrapper name transforms */
+ strendzap (actual_cwrapper_name, ".exe");
+ tmp_pathspec = lt_extend_str (actual_cwrapper_name, ".exe", 1);
+ XFREE (actual_cwrapper_name);
+ actual_cwrapper_name = tmp_pathspec;
+ tmp_pathspec = 0;
+
+ /* target_name transforms -- use actual target program name; might have lt- prefix */
+ target_name = xstrdup (base_name (TARGET_PROGRAM_NAME));
+ strendzap (target_name, ".exe");
+ tmp_pathspec = lt_extend_str (target_name, ".exe", 1);
+ XFREE (target_name);
+ target_name = tmp_pathspec;
+ tmp_pathspec = 0;
+
+ lt_debugprintf (__FILE__, __LINE__,
+ "(main) libtool target name: %s\n",
+ target_name);
+EOF
+
+ cat <<EOF
+ newargz[0] =
+ XMALLOC (char, (strlen (actual_cwrapper_path) +
+ strlen ("$objdir") + 1 + strlen (actual_cwrapper_name) + 1));
+ strcpy (newargz[0], actual_cwrapper_path);
+ strcat (newargz[0], "$objdir");
+ strcat (newargz[0], "/");
+EOF
+
+ cat <<"EOF"
+ /* stop here, and copy so we don't have to do this twice */
+ tmp_pathspec = xstrdup (newargz[0]);
+
+ /* do NOT want the lt- prefix here, so use actual_cwrapper_name */
+ strcat (newargz[0], actual_cwrapper_name);
+
+ /* DO want the lt- prefix here if it exists, so use target_name */
+ lt_argv_zero = lt_extend_str (tmp_pathspec, target_name, 1);
+ XFREE (tmp_pathspec);
+ tmp_pathspec = NULL;
+EOF
+
+ case $host_os in
+ mingw*)
+ cat <<"EOF"
+ {
+ char* p;
+ while ((p = strchr (newargz[0], '\\')) != NULL)
+ {
+ *p = '/';
+ }
+ while ((p = strchr (lt_argv_zero, '\\')) != NULL)
+ {
+ *p = '/';
+ }
+ }
+EOF
+ ;;
+ esac
+
+ cat <<"EOF"
+ XFREE (target_name);
+ XFREE (actual_cwrapper_path);
+ XFREE (actual_cwrapper_name);
+
+ lt_setenv ("BIN_SH", "xpg4"); /* for Tru64 */
+ lt_setenv ("DUALCASE", "1"); /* for MSK sh */
+ /* Update the DLL searchpath. EXE_PATH_VALUE ($dllsearchpath) must
+ be prepended before (that is, appear after) LIB_PATH_VALUE ($temp_rpath)
+ because on Windows, both *_VARNAMEs are PATH but uninstalled
+ libraries must come first. */
+ lt_update_exe_path (EXE_PATH_VARNAME, EXE_PATH_VALUE);
+ lt_update_lib_path (LIB_PATH_VARNAME, LIB_PATH_VALUE);
+
+ lt_debugprintf (__FILE__, __LINE__, "(main) lt_argv_zero: %s\n",
+ nonnull (lt_argv_zero));
+ for (i = 0; i < newargc; i++)
+ {
+ lt_debugprintf (__FILE__, __LINE__, "(main) newargz[%d]: %s\n",
+ i, nonnull (newargz[i]));
+ }
+
+EOF
+
+ case $host_os in
+ mingw*)
+ cat <<"EOF"
+ /* execv doesn't actually work on mingw as expected on unix */
+ newargz = prepare_spawn (newargz);
+ rval = (int) _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz);
+ if (rval == -1)
+ {
+ /* failed to start process */
+ lt_debugprintf (__FILE__, __LINE__,
+ "(main) failed to launch target \"%s\": %s\n",
+ lt_argv_zero, nonnull (strerror (errno)));
+ return 127;
+ }
+ return rval;
+EOF
+ ;;
+ *)
+ cat <<"EOF"
+ execv (lt_argv_zero, newargz);
+ return rval; /* =127, but avoids unused variable warning */
+EOF
+ ;;
+ esac
+
+ cat <<"EOF"
+}
+
+void *
+xmalloc (size_t num)
+{
+ void *p = (void *) malloc (num);
+ if (!p)
+ lt_fatal (__FILE__, __LINE__, "memory exhausted");
+
+ return p;
+}
+
+char *
+xstrdup (const char *string)
+{
+ return string ? strcpy ((char *) xmalloc (strlen (string) + 1),
+ string) : NULL;
+}
+
+const char *
+base_name (const char *name)
+{
+ const char *base;
+
+#if defined HAVE_DOS_BASED_FILE_SYSTEM
+ /* Skip over the disk name in MSDOS pathnames. */
+ if (isalpha ((unsigned char) name[0]) && name[1] == ':')
+ name += 2;
+#endif
+
+ for (base = name; *name; name++)
+ if (IS_DIR_SEPARATOR (*name))
+ base = name + 1;
+ return base;
+}
+
+int
+check_executable (const char *path)
+{
+ struct stat st;
+
+ lt_debugprintf (__FILE__, __LINE__, "(check_executable): %s\n",
+ nonempty (path));
+ if ((!path) || (!*path))
+ return 0;
+
+ if ((stat (path, &st) >= 0)
+ && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+ return 1;
+ else
+ return 0;
+}
+
+int
+make_executable (const char *path)
+{
+ int rval = 0;
+ struct stat st;
+
+ lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n",
+ nonempty (path));
+ if ((!path) || (!*path))
+ return 0;
+
+ if (stat (path, &st) >= 0)
+ {
+ rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR);
+ }
+ return rval;
+}
+
+/* Searches for the full path of the wrapper. Returns
+ newly allocated full path name if found, NULL otherwise
+ Does not chase symlinks, even on platforms that support them.
+*/
+char *
+find_executable (const char *wrapper)
+{
+ int has_slash = 0;
+ const char *p;
+ const char *p_next;
+ /* static buffer for getcwd */
+ char tmp[LT_PATHMAX + 1];
+ size_t tmp_len;
+ char *concat_name;
+
+ lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n",
+ nonempty (wrapper));
+
+ if ((wrapper == NULL) || (*wrapper == '\0'))
+ return NULL;
+
+ /* Absolute path? */
+#if defined HAVE_DOS_BASED_FILE_SYSTEM
+ if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':')
+ {
+ concat_name = xstrdup (wrapper);
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ }
+ else
+ {
+#endif
+ if (IS_DIR_SEPARATOR (wrapper[0]))
+ {
+ concat_name = xstrdup (wrapper);
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ }
+#if defined HAVE_DOS_BASED_FILE_SYSTEM
+ }
+#endif
+
+ for (p = wrapper; *p; p++)
+ if (*p == '/')
+ {
+ has_slash = 1;
+ break;
+ }
+ if (!has_slash)
+ {
+ /* no slashes; search PATH */
+ const char *path = getenv ("PATH");
+ if (path != NULL)
+ {
+ for (p = path; *p; p = p_next)
+ {
+ const char *q;
+ size_t p_len;
+ for (q = p; *q; q++)
+ if (IS_PATH_SEPARATOR (*q))
+ break;
+ p_len = (size_t) (q - p);
+ p_next = (*q == '\0' ? q : q + 1);
+ if (p_len == 0)
+ {
+ /* empty path: current directory */
+ if (getcwd (tmp, LT_PATHMAX) == NULL)
+ lt_fatal (__FILE__, __LINE__, "getcwd failed: %s",
+ nonnull (strerror (errno)));
+ tmp_len = strlen (tmp);
+ concat_name =
+ XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+ memcpy (concat_name, tmp, tmp_len);
+ concat_name[tmp_len] = '/';
+ strcpy (concat_name + tmp_len + 1, wrapper);
+ }
+ else
+ {
+ concat_name =
+ XMALLOC (char, p_len + 1 + strlen (wrapper) + 1);
+ memcpy (concat_name, p, p_len);
+ concat_name[p_len] = '/';
+ strcpy (concat_name + p_len + 1, wrapper);
+ }
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ }
+ }
+ /* not found in PATH; assume curdir */
+ }
+ /* Relative path | not found in path: prepend cwd */
+ if (getcwd (tmp, LT_PATHMAX) == NULL)
+ lt_fatal (__FILE__, __LINE__, "getcwd failed: %s",
+ nonnull (strerror (errno)));
+ tmp_len = strlen (tmp);
+ concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+ memcpy (concat_name, tmp, tmp_len);
+ concat_name[tmp_len] = '/';
+ strcpy (concat_name + tmp_len + 1, wrapper);
+
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ return NULL;
+}
+
+char *
+chase_symlinks (const char *pathspec)
+{
+#ifndef S_ISLNK
+ return xstrdup (pathspec);
+#else
+ char buf[LT_PATHMAX];
+ struct stat s;
+ char *tmp_pathspec = xstrdup (pathspec);
+ char *p;
+ int has_symlinks = 0;
+ while (strlen (tmp_pathspec) && !has_symlinks)
+ {
+ lt_debugprintf (__FILE__, __LINE__,
+ "checking path component for symlinks: %s\n",
+ tmp_pathspec);
+ if (lstat (tmp_pathspec, &s) == 0)
+ {
+ if (S_ISLNK (s.st_mode) != 0)
+ {
+ has_symlinks = 1;
+ break;
+ }
+
+ /* search backwards for last DIR_SEPARATOR */
+ p = tmp_pathspec + strlen (tmp_pathspec) - 1;
+ while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+ p--;
+ if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+ {
+ /* no more DIR_SEPARATORS left */
+ break;
+ }
+ *p = '\0';
+ }
+ else
+ {
+ lt_fatal (__FILE__, __LINE__,
+ "error accessing file \"%s\": %s",
+ tmp_pathspec, nonnull (strerror (errno)));
+ }
+ }
+ XFREE (tmp_pathspec);
+
+ if (!has_symlinks)
+ {
+ return xstrdup (pathspec);
+ }
+
+ tmp_pathspec = realpath (pathspec, buf);
+ if (tmp_pathspec == 0)
+ {
+ lt_fatal (__FILE__, __LINE__,
+ "could not follow symlinks for %s", pathspec);
+ }
+ return xstrdup (tmp_pathspec);
+#endif
+}
+
+char *
+strendzap (char *str, const char *pat)
+{
+ size_t len, patlen;
+
+ assert (str != NULL);
+ assert (pat != NULL);
+
+ len = strlen (str);
+ patlen = strlen (pat);
+
+ if (patlen <= len)
+ {
+ str += len - patlen;
+ if (STREQ (str, pat))
+ *str = '\0';
+ }
+ return str;
+}
+
+void
+lt_debugprintf (const char *file, int line, const char *fmt, ...)
+{
+ va_list args;
+ if (lt_debug)
+ {
+ (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line);
+ va_start (args, fmt);
+ (void) vfprintf (stderr, fmt, args);
+ va_end (args);
+ }
+}
+
+static void
+lt_error_core (int exit_status, const char *file,
+ int line, const char *mode,
+ const char *message, va_list ap)
+{
+ fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode);
+ vfprintf (stderr, message, ap);
+ fprintf (stderr, ".\n");
+
+ if (exit_status >= 0)
+ exit (exit_status);
+}
+
+void
+lt_fatal (const char *file, int line, const char *message, ...)
+{
+ va_list ap;
+ va_start (ap, message);
+ lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap);
+ va_end (ap);
+}
+
+static const char *
+nonnull (const char *s)
+{
+ return s ? s : "(null)";
+}
+
+static const char *
+nonempty (const char *s)
+{
+ return (s && !*s) ? "(empty)" : nonnull (s);
+}
+
+void
+lt_setenv (const char *name, const char *value)
+{
+ lt_debugprintf (__FILE__, __LINE__,
+ "(lt_setenv) setting '%s' to '%s'\n",
+ nonnull (name), nonnull (value));
+ {
+#ifdef HAVE_SETENV
+ /* always make a copy, for consistency with !HAVE_SETENV */
+ char *str = xstrdup (value);
+ setenv (name, str, 1);
+#else
+ size_t len = strlen (name) + 1 + strlen (value) + 1;
+ char *str = XMALLOC (char, len);
+ sprintf (str, "%s=%s", name, value);
+ if (putenv (str) != EXIT_SUCCESS)
+ {
+ XFREE (str);
+ }
+#endif
+ }
+}
+
+char *
+lt_extend_str (const char *orig_value, const char *add, int to_end)
+{
+ char *new_value;
+ if (orig_value && *orig_value)
+ {
+ size_t orig_value_len = strlen (orig_value);
+ size_t add_len = strlen (add);
+ new_value = XMALLOC (char, add_len + orig_value_len + 1);
+ if (to_end)
+ {
+ strcpy (new_value, orig_value);
+ strcpy (new_value + orig_value_len, add);
+ }
+ else
+ {
+ strcpy (new_value, add);
+ strcpy (new_value + add_len, orig_value);
+ }
+ }
+ else
+ {
+ new_value = xstrdup (add);
+ }
+ return new_value;
+}
+
+void
+lt_update_exe_path (const char *name, const char *value)
+{
+ lt_debugprintf (__FILE__, __LINE__,
+ "(lt_update_exe_path) modifying '%s' by prepending '%s'\n",
+ nonnull (name), nonnull (value));
+
+ if (name && *name && value && *value)
+ {
+ char *new_value = lt_extend_str (getenv (name), value, 0);
+ /* some systems can't cope with a ':'-terminated path #' */
+ size_t len = strlen (new_value);
+ while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1]))
+ {
+ new_value[--len] = '\0';
+ }
+ lt_setenv (name, new_value);
+ XFREE (new_value);
+ }
+}
+
+void
+lt_update_lib_path (const char *name, const char *value)
+{
+ lt_debugprintf (__FILE__, __LINE__,
+ "(lt_update_lib_path) modifying '%s' by prepending '%s'\n",
+ nonnull (name), nonnull (value));
+
+ if (name && *name && value && *value)
+ {
+ char *new_value = lt_extend_str (getenv (name), value, 0);
+ lt_setenv (name, new_value);
+ XFREE (new_value);
+ }
+}
+
+EOF
+ case $host_os in
+ mingw*)
+ cat <<"EOF"
+
+/* Prepares an argument vector before calling spawn().
+ Note that spawn() does not by itself call the command interpreter
+ (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") :
+ ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&v);
+ v.dwPlatformId == VER_PLATFORM_WIN32_NT;
+ }) ? "cmd.exe" : "command.com").
+ Instead it simply concatenates the arguments, separated by ' ', and calls
+ CreateProcess(). We must quote the arguments since Win32 CreateProcess()
+ interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a
+ special way:
+ - Space and tab are interpreted as delimiters. They are not treated as
+ delimiters if they are surrounded by double quotes: "...".
+ - Unescaped double quotes are removed from the input. Their only effect is
+ that within double quotes, space and tab are treated like normal
+ characters.
+ - Backslashes not followed by double quotes are not special.
+ - But 2*n+1 backslashes followed by a double quote become
+ n backslashes followed by a double quote (n >= 0):
+ \" -> "
+ \\\" -> \"
+ \\\\\" -> \\"
+ */
+#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+char **
+prepare_spawn (char **argv)
+{
+ size_t argc;
+ char **new_argv;
+ size_t i;
+
+ /* Count number of arguments. */
+ for (argc = 0; argv[argc] != NULL; argc++)
+ ;
+
+ /* Allocate new argument vector. */
+ new_argv = XMALLOC (char *, argc + 1);
+
+ /* Put quoted arguments into the new argument vector. */
+ for (i = 0; i < argc; i++)
+ {
+ const char *string = argv[i];
+
+ if (string[0] == '\0')
+ new_argv[i] = xstrdup ("\"\"");
+ else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL)
+ {
+ int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL);
+ size_t length;
+ unsigned int backslashes;
+ const char *s;
+ char *quoted_string;
+ char *p;
+
+ length = 0;
+ backslashes = 0;
+ if (quote_around)
+ length++;
+ for (s = string; *s != '\0'; s++)
+ {
+ char c = *s;
+ if (c == '"')
+ length += backslashes + 1;
+ length++;
+ if (c == '\\')
+ backslashes++;
+ else
+ backslashes = 0;
+ }
+ if (quote_around)
+ length += backslashes + 1;
+
+ quoted_string = XMALLOC (char, length + 1);
+
+ p = quoted_string;
+ backslashes = 0;
+ if (quote_around)
+ *p++ = '"';
+ for (s = string; *s != '\0'; s++)
+ {
+ char c = *s;
+ if (c == '"')
+ {
+ unsigned int j;
+ for (j = backslashes + 1; j > 0; j--)
+ *p++ = '\\';
+ }
+ *p++ = c;
+ if (c == '\\')
+ backslashes++;
+ else
+ backslashes = 0;
+ }
+ if (quote_around)
+ {
+ unsigned int j;
+ for (j = backslashes; j > 0; j--)
+ *p++ = '\\';
+ *p++ = '"';
+ }
+ *p = '\0';
+
+ new_argv[i] = quoted_string;
+ }
+ else
+ new_argv[i] = (char *) string;
+ }
+ new_argv[argc] = NULL;
+
+ return new_argv;
+}
+EOF
+ ;;
+ esac
+
+ cat <<"EOF"
+void lt_dump_script (FILE* f)
+{
+EOF
+ func_emit_wrapper yes |
+ $SED -n -e '
+s/^\(.\{79\}\)\(..*\)/\1\
+\2/
+h
+s/\([\\"]\)/\\\1/g
+s/$/\\n/
+s/\([^\n]*\).*/ fputs ("\1", f);/p
+g
+D'
+ cat <<"EOF"
+}
+EOF
+}
+# end: func_emit_cwrapperexe_src
+
+# func_win32_import_lib_p ARG
+# True if ARG is an import lib, as indicated by $file_magic_cmd
+func_win32_import_lib_p ()
+{
+ $debug_cmd
+
+ case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in
+ *import*) : ;;
+ *) false ;;
+ esac
+}
+
+# func_suncc_cstd_abi
+# !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!!
+# Several compiler flags select an ABI that is incompatible with the
+# Cstd library. Avoid specifying it if any are in CXXFLAGS.
+func_suncc_cstd_abi ()
+{
+ $debug_cmd
+
+ case " $compile_command " in
+ *" -compat=g "*|*\ -std=c++[0-9][0-9]\ *|*" -library=stdcxx4 "*|*" -library=stlport4 "*)
+ suncc_use_cstd_abi=no
+ ;;
+ *)
+ suncc_use_cstd_abi=yes
+ ;;
+ esac
+}
+
+# func_mode_link arg...
+func_mode_link ()
+{
+ $debug_cmd
+
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ # It is impossible to link a dll without this setting, and
+ # we shouldn't force the makefile maintainer to figure out
+ # what system we are compiling for in order to pass an extra
+ # flag for every libtool invocation.
+ # allow_undefined=no
+
+ # FIXME: Unfortunately, there are problems with the above when trying
+ # to make a dll that has undefined symbols, in which case not
+ # even a static library is built. For now, we need to specify
+ # -no-undefined on the libtool link line when we can be certain
+ # that all symbols are satisfied, otherwise we get a static library.
+ allow_undefined=yes
+ ;;
+ *)
+ allow_undefined=yes
+ ;;
+ esac
+ libtool_args=$nonopt
+ base_compile="$nonopt $@"
+ compile_command=$nonopt
+ finalize_command=$nonopt
+
+ compile_rpath=
+ finalize_rpath=
+ compile_shlibpath=
+ finalize_shlibpath=
+ convenience=
+ old_convenience=
+ deplibs=
+ old_deplibs=
+ compiler_flags=
+ linker_flags=
+ dllsearchpath=
+ lib_search_path=`pwd`
+ inst_prefix_dir=
+ new_inherited_linker_flags=
+
+ avoid_version=no
+ bindir=
+ dlfiles=
+ dlprefiles=
+ dlself=no
+ export_dynamic=no
+ export_symbols=
+ export_symbols_regex=
+ generated=
+ libobjs=
+ ltlibs=
+ module=no
+ no_install=no
+ objs=
+ os2dllname=
+ non_pic_objects=
+ precious_files_regex=
+ prefer_static_libs=no
+ preload=false
+ prev=
+ prevarg=
+ release=
+ rpath=
+ xrpath=
+ perm_rpath=
+ temp_rpath=
+ thread_safe=no
+ vinfo=
+ vinfo_number=no
+ weak_libs=
+ single_module=$wl-single_module
+ func_infer_tag $base_compile
+
+ # We need to know -static, to get the right output filenames.
+ for arg
+ do
+ case $arg in
+ -shared)
+ test yes != "$build_libtool_libs" \
+ && func_fatal_configuration "cannot build a shared library"
+ build_old_libs=no
+ break
+ ;;
+ -all-static | -static | -static-libtool-libs)
+ case $arg in
+ -all-static)
+ if test yes = "$build_libtool_libs" && test -z "$link_static_flag"; then
+ func_warning "complete static linking is impossible in this configuration"
+ fi
+ if test -n "$link_static_flag"; then
+ dlopen_self=$dlopen_self_static
+ fi
+ prefer_static_libs=yes
+ ;;
+ -static)
+ if test -z "$pic_flag" && test -n "$link_static_flag"; then
+ dlopen_self=$dlopen_self_static
+ fi
+ prefer_static_libs=built
+ ;;
+ -static-libtool-libs)
+ if test -z "$pic_flag" && test -n "$link_static_flag"; then
+ dlopen_self=$dlopen_self_static
+ fi
+ prefer_static_libs=yes
+ ;;
+ esac
+ build_libtool_libs=no
+ build_old_libs=yes
+ break
+ ;;
+ esac
+ done
+
+ # See if our shared archives depend on static archives.
+ test -n "$old_archive_from_new_cmds" && build_old_libs=yes
+
+ # Go through the arguments, transforming them on the way.
+ while test "$#" -gt 0; do
+ arg=$1
+ shift
+ func_quote_for_eval "$arg"
+ qarg=$func_quote_for_eval_unquoted_result
+ func_append libtool_args " $func_quote_for_eval_result"
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$prev"; then
+ case $prev in
+ output)
+ func_append compile_command " @OUTPUT@"
+ func_append finalize_command " @OUTPUT@"
+ ;;
+ esac
+
+ case $prev in
+ bindir)
+ bindir=$arg
+ prev=
+ continue
+ ;;
+ dlfiles|dlprefiles)
+ $preload || {
+ # Add the symbol object into the linking commands.
+ func_append compile_command " @SYMFILE@"
+ func_append finalize_command " @SYMFILE@"
+ preload=:
+ }
+ case $arg in
+ *.la | *.lo) ;; # We handle these cases below.
+ force)
+ if test no = "$dlself"; then
+ dlself=needless
+ export_dynamic=yes
+ fi
+ prev=
+ continue
+ ;;
+ self)
+ if test dlprefiles = "$prev"; then
+ dlself=yes
+ elif test dlfiles = "$prev" && test yes != "$dlopen_self"; then
+ dlself=yes
+ else
+ dlself=needless
+ export_dynamic=yes
+ fi
+ prev=
+ continue
+ ;;
+ *)
+ if test dlfiles = "$prev"; then
+ func_append dlfiles " $arg"
+ else
+ func_append dlprefiles " $arg"
+ fi
+ prev=
+ continue
+ ;;
+ esac
+ ;;
+ expsyms)
+ export_symbols=$arg
+ test -f "$arg" \
+ || func_fatal_error "symbol file '$arg' does not exist"
+ prev=
+ continue
+ ;;
+ expsyms_regex)
+ export_symbols_regex=$arg
+ prev=
+ continue
+ ;;
+ framework)
+ case $host in
+ *-*-darwin*)
+ case "$deplibs " in
+ *" $qarg.ltframework "*) ;;
+ *) func_append deplibs " $qarg.ltframework" # this is fixed later
+ ;;
+ esac
+ ;;
+ esac
+ prev=
+ continue
+ ;;
+ inst_prefix)
+ inst_prefix_dir=$arg
+ prev=
+ continue
+ ;;
+ mllvm)
+ # Clang does not use LLVM to link, so we can simply discard any
+ # '-mllvm $arg' options when doing the link step.
+ prev=
+ continue
+ ;;
+ objectlist)
+ if test -f "$arg"; then
+ save_arg=$arg
+ moreargs=
+ for fil in `cat "$save_arg"`
+ do
+# func_append moreargs " $fil"
+ arg=$fil
+ # A libtool-controlled object.
+
+ # Check to see that this really is a libtool object.
+ if func_lalib_unsafe_p "$arg"; then
+ pic_object=
+ non_pic_object=
+
+ # Read the .lo file
+ func_source "$arg"
+
+ if test -z "$pic_object" ||
+ test -z "$non_pic_object" ||
+ test none = "$pic_object" &&
+ test none = "$non_pic_object"; then
+ func_fatal_error "cannot find name of object for '$arg'"
+ fi
+
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir=$func_dirname_result
+
+ if test none != "$pic_object"; then
+ # Prepend the subdirectory the object is found in.
+ pic_object=$xdir$pic_object
+
+ if test dlfiles = "$prev"; then
+ if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then
+ func_append dlfiles " $pic_object"
+ prev=
+ continue
+ else
+ # If libtool objects are unsupported, then we need to preload.
+ prev=dlprefiles
+ fi
+ fi
+
+ # CHECK ME: I think I busted this. -Ossama
+ if test dlprefiles = "$prev"; then
+ # Preload the old-style object.
+ func_append dlprefiles " $pic_object"
+ prev=
+ fi
+
+ # A PIC object.
+ func_append libobjs " $pic_object"
+ arg=$pic_object
+ fi
+
+ # Non-PIC object.
+ if test none != "$non_pic_object"; then
+ # Prepend the subdirectory the object is found in.
+ non_pic_object=$xdir$non_pic_object
+
+ # A standard non-PIC object
+ func_append non_pic_objects " $non_pic_object"
+ if test -z "$pic_object" || test none = "$pic_object"; then
+ arg=$non_pic_object
+ fi
+ else
+ # If the PIC object exists, use it instead.
+ # $xdir was prepended to $pic_object above.
+ non_pic_object=$pic_object
+ func_append non_pic_objects " $non_pic_object"
+ fi
+ else
+ # Only an error if not doing a dry-run.
+ if $opt_dry_run; then
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir=$func_dirname_result
+
+ func_lo2o "$arg"
+ pic_object=$xdir$objdir/$func_lo2o_result
+ non_pic_object=$xdir$func_lo2o_result
+ func_append libobjs " $pic_object"
+ func_append non_pic_objects " $non_pic_object"
+ else
+ func_fatal_error "'$arg' is not a valid libtool object"
+ fi
+ fi
+ done
+ else
+ func_fatal_error "link input file '$arg' does not exist"
+ fi
+ arg=$save_arg
+ prev=
+ continue
+ ;;
+ os2dllname)
+ os2dllname=$arg
+ prev=
+ continue
+ ;;
+ precious_regex)
+ precious_files_regex=$arg
+ prev=
+ continue
+ ;;
+ release)
+ release=-$arg
+ prev=
+ continue
+ ;;
+ rpath | xrpath)
+ # We need an absolute path.
+ case $arg in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ func_fatal_error "only absolute run-paths are allowed"
+ ;;
+ esac
+ if test rpath = "$prev"; then
+ case "$rpath " in
+ *" $arg "*) ;;
+ *) func_append rpath " $arg" ;;
+ esac
+ else
+ case "$xrpath " in
+ *" $arg "*) ;;
+ *) func_append xrpath " $arg" ;;
+ esac
+ fi
+ prev=
+ continue
+ ;;
+ shrext)
+ shrext_cmds=$arg
+ prev=
+ continue
+ ;;
+ weak)
+ func_append weak_libs " $arg"
+ prev=
+ continue
+ ;;
+ xcclinker)
+ func_append linker_flags " $qarg"
+ func_append compiler_flags " $qarg"
+ prev=
+ func_append compile_command " $qarg"
+ func_append finalize_command " $qarg"
+ continue
+ ;;
+ xcompiler)
+ func_append compiler_flags " $qarg"
+ prev=
+ func_append compile_command " $qarg"
+ func_append finalize_command " $qarg"
+ continue
+ ;;
+ xlinker)
+ func_append linker_flags " $qarg"
+ func_append compiler_flags " $wl$qarg"
+ prev=
+ func_append compile_command " $wl$qarg"
+ func_append finalize_command " $wl$qarg"
+ continue
+ ;;
+ *)
+ eval "$prev=\"\$arg\""
+ prev=
+ continue
+ ;;
+ esac
+ fi # test -n "$prev"
+
+ prevarg=$arg
+
+ case $arg in
+ -all-static)
+ if test -n "$link_static_flag"; then
+ # See comment for -static flag below, for more details.
+ func_append compile_command " $link_static_flag"
+ func_append finalize_command " $link_static_flag"
+ fi
+ continue
+ ;;
+
+ -allow-undefined)
+ # FIXME: remove this flag sometime in the future.
+ func_fatal_error "'-allow-undefined' must not be used because it is the default"
+ ;;
+
+ -avoid-version)
+ avoid_version=yes
+ continue
+ ;;
+
+ -bindir)
+ prev=bindir
+ continue
+ ;;
+
+ -dlopen)
+ prev=dlfiles
+ continue
+ ;;
+
+ -dlpreopen)
+ prev=dlprefiles
+ continue
+ ;;
+
+ -export-dynamic)
+ export_dynamic=yes
+ continue
+ ;;
+
+ -export-symbols | -export-symbols-regex)
+ if test -n "$export_symbols" || test -n "$export_symbols_regex"; then
+ func_fatal_error "more than one -exported-symbols argument is not allowed"
+ fi
+ if test X-export-symbols = "X$arg"; then
+ prev=expsyms
+ else
+ prev=expsyms_regex
+ fi
+ continue
+ ;;
+
+ -framework)
+ prev=framework
+ continue
+ ;;
+
+ -inst-prefix-dir)
+ prev=inst_prefix
+ continue
+ ;;
+
+ # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:*
+ # so, if we see these flags be careful not to treat them like -L
+ -L[A-Z][A-Z]*:*)
+ case $with_gcc/$host in
+ no/*-*-irix* | /*-*-irix*)
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ ;;
+ esac
+ continue
+ ;;
+
+ -L*)
+ func_stripname "-L" '' "$arg"
+ if test -z "$func_stripname_result"; then
+ if test "$#" -gt 0; then
+ func_fatal_error "require no space between '-L' and '$1'"
+ else
+ func_fatal_error "need path for '-L' option"
+ fi
+ fi
+ func_resolve_sysroot "$func_stripname_result"
+ dir=$func_resolve_sysroot_result
+ # We need an absolute path.
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ absdir=`cd "$dir" && pwd`
+ test -z "$absdir" && \
+ func_fatal_error "cannot determine absolute directory name of '$dir'"
+ dir=$absdir
+ ;;
+ esac
+ case "$deplibs " in
+ *" -L$dir "* | *" $arg "*)
+ # Will only happen for absolute or sysroot arguments
+ ;;
+ *)
+ # Preserve sysroot, but never include relative directories
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;;
+ *) func_append deplibs " -L$dir" ;;
+ esac
+ func_append lib_search_path " $dir"
+ ;;
+ esac
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'`
+ case :$dllsearchpath: in
+ *":$dir:"*) ;;
+ ::) dllsearchpath=$dir;;
+ *) func_append dllsearchpath ":$dir";;
+ esac
+ case :$dllsearchpath: in
+ *":$testbindir:"*) ;;
+ ::) dllsearchpath=$testbindir;;
+ *) func_append dllsearchpath ":$testbindir";;
+ esac
+ ;;
+ esac
+ continue
+ ;;
+
+ -l*)
+ if test X-lc = "X$arg" || test X-lm = "X$arg"; then
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*)
+ # These systems don't actually have a C or math library (as such)
+ continue
+ ;;
+ *-*-os2*)
+ # These systems don't actually have a C library (as such)
+ test X-lc = "X$arg" && continue
+ ;;
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*)
+ # Do not include libc due to us having libc/libc_r.
+ test X-lc = "X$arg" && continue
+ ;;
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # Rhapsody C and math libraries are in the System framework
+ func_append deplibs " System.ltframework"
+ continue
+ ;;
+ *-*-sco3.2v5* | *-*-sco5v6*)
+ # Causes problems with __ctype
+ test X-lc = "X$arg" && continue
+ ;;
+ *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+ # Compiler inserts libc in the correct place for threads to work
+ test X-lc = "X$arg" && continue
+ ;;
+ esac
+ elif test X-lc_r = "X$arg"; then
+ case $host in
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*)
+ # Do not include libc_r directly, use -pthread flag.
+ continue
+ ;;
+ esac
+ fi
+ func_append deplibs " $arg"
+ continue
+ ;;
+
+ -mllvm)
+ prev=mllvm
+ continue
+ ;;
+
+ -module)
+ module=yes
+ continue
+ ;;
+
+ # Tru64 UNIX uses -model [arg] to determine the layout of C++
+ # classes, name mangling, and exception handling.
+ # Darwin uses the -arch flag to determine output architecture.
+ -model|-arch|-isysroot|--sysroot)
+ func_append compiler_flags " $arg"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ prev=xcompiler
+ continue
+ ;;
+
+ -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \
+ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
+ func_append compiler_flags " $arg"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ case "$new_inherited_linker_flags " in
+ *" $arg "*) ;;
+ * ) func_append new_inherited_linker_flags " $arg" ;;
+ esac
+ continue
+ ;;
+
+ -multi_module)
+ single_module=$wl-multi_module
+ continue
+ ;;
+
+ -no-fast-install)
+ fast_install=no
+ continue
+ ;;
+
+ -no-install)
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*)
+ # The PATH hackery in wrapper scripts is required on Windows
+ # and Darwin in order for the loader to find any dlls it needs.
+ func_warning "'-no-install' is ignored for $host"
+ func_warning "assuming '-no-fast-install' instead"
+ fast_install=no
+ ;;
+ *) no_install=yes ;;
+ esac
+ continue
+ ;;
+
+ -no-undefined)
+ allow_undefined=no
+ continue
+ ;;
+
+ -objectlist)
+ prev=objectlist
+ continue
+ ;;
+
+ -os2dllname)
+ prev=os2dllname
+ continue
+ ;;
+
+ -o) prev=output ;;
+
+ -precious-files-regex)
+ prev=precious_regex
+ continue
+ ;;
+
+ -release)
+ prev=release
+ continue
+ ;;
+
+ -rpath)
+ prev=rpath
+ continue
+ ;;
+
+ -R)
+ prev=xrpath
+ continue
+ ;;
+
+ -R*)
+ func_stripname '-R' '' "$arg"
+ dir=$func_stripname_result
+ # We need an absolute path.
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ =*)
+ func_stripname '=' '' "$dir"
+ dir=$lt_sysroot$func_stripname_result
+ ;;
+ *)
+ func_fatal_error "only absolute run-paths are allowed"
+ ;;
+ esac
+ case "$xrpath " in
+ *" $dir "*) ;;
+ *) func_append xrpath " $dir" ;;
+ esac
+ continue
+ ;;
+
+ -shared)
+ # The effects of -shared are defined in a previous loop.
+ continue
+ ;;
+
+ -shrext)
+ prev=shrext
+ continue
+ ;;
+
+ -static | -static-libtool-libs)
+ # The effects of -static are defined in a previous loop.
+ # We used to do the same as -all-static on platforms that
+ # didn't have a PIC flag, but the assumption that the effects
+ # would be equivalent was wrong. It would break on at least
+ # Digital Unix and AIX.
+ continue
+ ;;
+
+ -thread-safe)
+ thread_safe=yes
+ continue
+ ;;
+
+ -version-info)
+ prev=vinfo
+ continue
+ ;;
+
+ -version-number)
+ prev=vinfo
+ vinfo_number=yes
+ continue
+ ;;
+
+ -weak)
+ prev=weak
+ continue
+ ;;
+
+ -Wc,*)
+ func_stripname '-Wc,' '' "$arg"
+ args=$func_stripname_result
+ arg=
+ save_ifs=$IFS; IFS=,
+ for flag in $args; do
+ IFS=$save_ifs
+ func_quote_for_eval "$flag"
+ func_append arg " $func_quote_for_eval_result"
+ func_append compiler_flags " $func_quote_for_eval_result"
+ done
+ IFS=$save_ifs
+ func_stripname ' ' '' "$arg"
+ arg=$func_stripname_result
+ ;;
+
+ -Wl,*)
+ func_stripname '-Wl,' '' "$arg"
+ args=$func_stripname_result
+ arg=
+ save_ifs=$IFS; IFS=,
+ for flag in $args; do
+ IFS=$save_ifs
+ func_quote_for_eval "$flag"
+ func_append arg " $wl$func_quote_for_eval_result"
+ func_append compiler_flags " $wl$func_quote_for_eval_result"
+ func_append linker_flags " $func_quote_for_eval_result"
+ done
+ IFS=$save_ifs
+ func_stripname ' ' '' "$arg"
+ arg=$func_stripname_result
+ ;;
+
+ -Xcompiler)
+ prev=xcompiler
+ continue
+ ;;
+
+ -Xlinker)
+ prev=xlinker
+ continue
+ ;;
+
+ -XCClinker)
+ prev=xcclinker
+ continue
+ ;;
+
+ # -msg_* for osf cc
+ -msg_*)
+ func_quote_for_eval "$arg"
+ arg=$func_quote_for_eval_result
+ ;;
+
+ # Flags to be passed through unchanged, with rationale:
+ # -64, -mips[0-9] enable 64-bit mode for the SGI compiler
+ # -r[0-9][0-9]* specify processor for the SGI compiler
+ # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler
+ # +DA*, +DD* enable 64-bit mode for the HP compiler
+ # -q* compiler args for the IBM compiler
+ # -m*, -t[45]*, -txscale* architecture-specific flags for GCC
+ # -F/path path to uninstalled frameworks, gcc on darwin
+ # -p, -pg, --coverage, -fprofile-* profiling flags for GCC
+ # -fstack-protector* stack protector flags for GCC
+ # @file GCC response files
+ # -tp=* Portland pgcc target processor selection
+ # --sysroot=* for sysroot support
+ # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization
+ # -stdlib=* select c++ std lib with clang
+ -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
+ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \
+ -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*)
+ func_quote_for_eval "$arg"
+ arg=$func_quote_for_eval_result
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ func_append compiler_flags " $arg"
+ continue
+ ;;
+
+ -Z*)
+ if test os2 = "`expr $host : '.*\(os2\)'`"; then
+ # OS/2 uses -Zxxx to specify OS/2-specific options
+ compiler_flags="$compiler_flags $arg"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ case $arg in
+ -Zlinker | -Zstack)
+ prev=xcompiler
+ ;;
+ esac
+ continue
+ else
+ # Otherwise treat like 'Some other compiler flag' below
+ func_quote_for_eval "$arg"
+ arg=$func_quote_for_eval_result
+ fi
+ ;;
+
+ # Some other compiler flag.
+ -* | +*)
+ func_quote_for_eval "$arg"
+ arg=$func_quote_for_eval_result
+ ;;
+
+ *.$objext)
+ # A standard object.
+ func_append objs " $arg"
+ ;;
+
+ *.lo)
+ # A libtool-controlled object.
+
+ # Check to see that this really is a libtool object.
+ if func_lalib_unsafe_p "$arg"; then
+ pic_object=
+ non_pic_object=
+
+ # Read the .lo file
+ func_source "$arg"
+
+ if test -z "$pic_object" ||
+ test -z "$non_pic_object" ||
+ test none = "$pic_object" &&
+ test none = "$non_pic_object"; then
+ func_fatal_error "cannot find name of object for '$arg'"
+ fi
+
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir=$func_dirname_result
+
+ test none = "$pic_object" || {
+ # Prepend the subdirectory the object is found in.
+ pic_object=$xdir$pic_object
+
+ if test dlfiles = "$prev"; then
+ if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then
+ func_append dlfiles " $pic_object"
+ prev=
+ continue
+ else
+ # If libtool objects are unsupported, then we need to preload.
+ prev=dlprefiles
+ fi
+ fi
+
+ # CHECK ME: I think I busted this. -Ossama
+ if test dlprefiles = "$prev"; then
+ # Preload the old-style object.
+ func_append dlprefiles " $pic_object"
+ prev=
+ fi
+
+ # A PIC object.
+ func_append libobjs " $pic_object"
+ arg=$pic_object
+ }
+
+ # Non-PIC object.
+ if test none != "$non_pic_object"; then
+ # Prepend the subdirectory the object is found in.
+ non_pic_object=$xdir$non_pic_object
+
+ # A standard non-PIC object
+ func_append non_pic_objects " $non_pic_object"
+ if test -z "$pic_object" || test none = "$pic_object"; then
+ arg=$non_pic_object
+ fi
+ else
+ # If the PIC object exists, use it instead.
+ # $xdir was prepended to $pic_object above.
+ non_pic_object=$pic_object
+ func_append non_pic_objects " $non_pic_object"
+ fi
+ else
+ # Only an error if not doing a dry-run.
+ if $opt_dry_run; then
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir=$func_dirname_result
+
+ func_lo2o "$arg"
+ pic_object=$xdir$objdir/$func_lo2o_result
+ non_pic_object=$xdir$func_lo2o_result
+ func_append libobjs " $pic_object"
+ func_append non_pic_objects " $non_pic_object"
+ else
+ func_fatal_error "'$arg' is not a valid libtool object"
+ fi
+ fi
+ ;;
+
+ *.$libext)
+ # An archive.
+ func_append deplibs " $arg"
+ func_append old_deplibs " $arg"
+ continue
+ ;;
+
+ *.la)
+ # A libtool-controlled library.
+
+ func_resolve_sysroot "$arg"
+ if test dlfiles = "$prev"; then
+ # This library was specified with -dlopen.
+ func_append dlfiles " $func_resolve_sysroot_result"
+ prev=
+ elif test dlprefiles = "$prev"; then
+ # The library was specified with -dlpreopen.
+ func_append dlprefiles " $func_resolve_sysroot_result"
+ prev=
+ else
+ func_append deplibs " $func_resolve_sysroot_result"
+ fi
+ continue
+ ;;
+
+ # Some other compiler argument.
+ *)
+ # Unknown arguments in both finalize_command and compile_command need
+ # to be aesthetically quoted because they are evaled later.
+ func_quote_for_eval "$arg"
+ arg=$func_quote_for_eval_result
+ ;;
+ esac # arg
+
+ # Now actually substitute the argument into the commands.
+ if test -n "$arg"; then
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ fi
+ done # argument parsing loop
+
+ test -n "$prev" && \
+ func_fatal_help "the '$prevarg' option requires an argument"
+
+ if test yes = "$export_dynamic" && test -n "$export_dynamic_flag_spec"; then
+ eval arg=\"$export_dynamic_flag_spec\"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ fi
+
+ oldlibs=
+ # calculate the name of the file, without its directory
+ func_basename "$output"
+ outputname=$func_basename_result
+ libobjs_save=$libobjs
+
+ if test -n "$shlibpath_var"; then
+ # get the directories listed in $shlibpath_var
+ eval shlib_search_path=\`\$ECHO \"\$$shlibpath_var\" \| \$SED \'s/:/ /g\'\`
+ else
+ shlib_search_path=
+ fi
+ eval sys_lib_search_path=\"$sys_lib_search_path_spec\"
+ eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\"
+
+ # Definition is injected by LT_CONFIG during libtool generation.
+ func_munge_path_list sys_lib_dlsearch_path "$LT_SYS_LIBRARY_PATH"
+
+ func_dirname "$output" "/" ""
+ output_objdir=$func_dirname_result$objdir
+ func_to_tool_file "$output_objdir/"
+ tool_output_objdir=$func_to_tool_file_result
+ # Create the object directory.
+ func_mkdir_p "$output_objdir"
+
+ # Determine the type of output
+ case $output in
+ "")
+ func_fatal_help "you must specify an output file"
+ ;;
+ *.$libext) linkmode=oldlib ;;
+ *.lo | *.$objext) linkmode=obj ;;
+ *.la) linkmode=lib ;;
+ *) linkmode=prog ;; # Anything else should be a program.
+ esac
+
+ specialdeplibs=
+
+ libs=
+ # Find all interdependent deplibs by searching for libraries
+ # that are linked more than once (e.g. -la -lb -la)
+ for deplib in $deplibs; do
+ if $opt_preserve_dup_deps; then
+ case "$libs " in
+ *" $deplib "*) func_append specialdeplibs " $deplib" ;;
+ esac
+ fi
+ func_append libs " $deplib"
+ done
+
+ if test lib = "$linkmode"; then
+ libs="$predeps $libs $compiler_lib_search_path $postdeps"
+
+ # Compute libraries that are listed more than once in $predeps
+ # $postdeps and mark them as special (i.e., whose duplicates are
+ # not to be eliminated).
+ pre_post_deps=
+ if $opt_duplicate_compiler_generated_deps; then
+ for pre_post_dep in $predeps $postdeps; do
+ case "$pre_post_deps " in
+ *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;;
+ esac
+ func_append pre_post_deps " $pre_post_dep"
+ done
+ fi
+ pre_post_deps=
+ fi
+
+ deplibs=
+ newdependency_libs=
+ newlib_search_path=
+ need_relink=no # whether we're linking any uninstalled libtool libraries
+ notinst_deplibs= # not-installed libtool libraries
+ notinst_path= # paths that contain not-installed libtool libraries
+
+ case $linkmode in
+ lib)
+ passes="conv dlpreopen link"
+ for file in $dlfiles $dlprefiles; do
+ case $file in
+ *.la) ;;
+ *)
+ func_fatal_help "libraries can '-dlopen' only libtool libraries: $file"
+ ;;
+ esac
+ done
+ ;;
+ prog)
+ compile_deplibs=
+ finalize_deplibs=
+ alldeplibs=false
+ newdlfiles=
+ newdlprefiles=
+ passes="conv scan dlopen dlpreopen link"
+ ;;
+ *) passes="conv"
+ ;;
+ esac
+
+ for pass in $passes; do
+ # The preopen pass in lib mode reverses $deplibs; put it back here
+ # so that -L comes before libs that need it for instance...
+ if test lib,link = "$linkmode,$pass"; then
+ ## FIXME: Find the place where the list is rebuilt in the wrong
+ ## order, and fix it there properly
+ tmp_deplibs=
+ for deplib in $deplibs; do
+ tmp_deplibs="$deplib $tmp_deplibs"
+ done
+ deplibs=$tmp_deplibs
+ fi
+
+ if test lib,link = "$linkmode,$pass" ||
+ test prog,scan = "$linkmode,$pass"; then
+ libs=$deplibs
+ deplibs=
+ fi
+ if test prog = "$linkmode"; then
+ case $pass in
+ dlopen) libs=$dlfiles ;;
+ dlpreopen) libs=$dlprefiles ;;
+ link) libs="$deplibs %DEPLIBS% $dependency_libs" ;;
+ esac
+ fi
+ if test lib,dlpreopen = "$linkmode,$pass"; then
+ # Collect and forward deplibs of preopened libtool libs
+ for lib in $dlprefiles; do
+ # Ignore non-libtool-libs
+ dependency_libs=
+ func_resolve_sysroot "$lib"
+ case $lib in
+ *.la) func_source "$func_resolve_sysroot_result" ;;
+ esac
+
+ # Collect preopened libtool deplibs, except any this library
+ # has declared as weak libs
+ for deplib in $dependency_libs; do
+ func_basename "$deplib"
+ deplib_base=$func_basename_result
+ case " $weak_libs " in
+ *" $deplib_base "*) ;;
+ *) func_append deplibs " $deplib" ;;
+ esac
+ done
+ done
+ libs=$dlprefiles
+ fi
+ if test dlopen = "$pass"; then
+ # Collect dlpreopened libraries
+ save_deplibs=$deplibs
+ deplibs=
+ fi
+
+ for deplib in $libs; do
+ lib=
+ found=false
+ case $deplib in
+ -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \
+ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
+ if test prog,link = "$linkmode,$pass"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ func_append compiler_flags " $deplib"
+ if test lib = "$linkmode"; then
+ case "$new_inherited_linker_flags " in
+ *" $deplib "*) ;;
+ * ) func_append new_inherited_linker_flags " $deplib" ;;
+ esac
+ fi
+ fi
+ continue
+ ;;
+ -l*)
+ if test lib != "$linkmode" && test prog != "$linkmode"; then
+ func_warning "'-l' is ignored for archives/objects"
+ continue
+ fi
+ func_stripname '-l' '' "$deplib"
+ name=$func_stripname_result
+ if test lib = "$linkmode"; then
+ searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path"
+ else
+ searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path"
+ fi
+ for searchdir in $searchdirs; do
+ for search_ext in .la $std_shrext .so .a; do
+ # Search the libtool library
+ lib=$searchdir/lib$name$search_ext
+ if test -f "$lib"; then
+ if test .la = "$search_ext"; then
+ found=:
+ else
+ found=false
+ fi
+ break 2
+ fi
+ done
+ done
+ if $found; then
+ # deplib is a libtool library
+ # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib,
+ # We need to do some special things here, and not later.
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+ case " $predeps $postdeps " in
+ *" $deplib "*)
+ if func_lalib_p "$lib"; then
+ library_names=
+ old_library=
+ func_source "$lib"
+ for l in $old_library $library_names; do
+ ll=$l
+ done
+ if test "X$ll" = "X$old_library"; then # only static version available
+ found=false
+ func_dirname "$lib" "" "."
+ ladir=$func_dirname_result
+ lib=$ladir/$old_library
+ if test prog,link = "$linkmode,$pass"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ deplibs="$deplib $deplibs"
+ test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs"
+ fi
+ continue
+ fi
+ fi
+ ;;
+ *) ;;
+ esac
+ fi
+ else
+ # deplib doesn't seem to be a libtool library
+ if test prog,link = "$linkmode,$pass"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ deplibs="$deplib $deplibs"
+ test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs"
+ fi
+ continue
+ fi
+ ;; # -l
+ *.ltframework)
+ if test prog,link = "$linkmode,$pass"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ deplibs="$deplib $deplibs"
+ if test lib = "$linkmode"; then
+ case "$new_inherited_linker_flags " in
+ *" $deplib "*) ;;
+ * ) func_append new_inherited_linker_flags " $deplib" ;;
+ esac
+ fi
+ fi
+ continue
+ ;;
+ -L*)
+ case $linkmode in
+ lib)
+ deplibs="$deplib $deplibs"
+ test conv = "$pass" && continue
+ newdependency_libs="$deplib $newdependency_libs"
+ func_stripname '-L' '' "$deplib"
+ func_resolve_sysroot "$func_stripname_result"
+ func_append newlib_search_path " $func_resolve_sysroot_result"
+ ;;
+ prog)
+ if test conv = "$pass"; then
+ deplibs="$deplib $deplibs"
+ continue
+ fi
+ if test scan = "$pass"; then
+ deplibs="$deplib $deplibs"
+ else
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ fi
+ func_stripname '-L' '' "$deplib"
+ func_resolve_sysroot "$func_stripname_result"
+ func_append newlib_search_path " $func_resolve_sysroot_result"
+ ;;
+ *)
+ func_warning "'-L' is ignored for archives/objects"
+ ;;
+ esac # linkmode
+ continue
+ ;; # -L
+ -R*)
+ if test link = "$pass"; then
+ func_stripname '-R' '' "$deplib"
+ func_resolve_sysroot "$func_stripname_result"
+ dir=$func_resolve_sysroot_result
+ # Make sure the xrpath contains only unique directories.
+ case "$xrpath " in
+ *" $dir "*) ;;
+ *) func_append xrpath " $dir" ;;
+ esac
+ fi
+ deplibs="$deplib $deplibs"
+ continue
+ ;;
+ *.la)
+ func_resolve_sysroot "$deplib"
+ lib=$func_resolve_sysroot_result
+ ;;
+ *.$libext)
+ if test conv = "$pass"; then
+ deplibs="$deplib $deplibs"
+ continue
+ fi
+ case $linkmode in
+ lib)
+ # Linking convenience modules into shared libraries is allowed,
+ # but linking other static libraries is non-portable.
+ case " $dlpreconveniencelibs " in
+ *" $deplib "*) ;;
+ *)
+ valid_a_lib=false
+ case $deplibs_check_method in
+ match_pattern*)
+ set dummy $deplibs_check_method; shift
+ match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+ if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \
+ | $EGREP "$match_pattern_regex" > /dev/null; then
+ valid_a_lib=:
+ fi
+ ;;
+ pass_all)
+ valid_a_lib=:
+ ;;
+ esac
+ if $valid_a_lib; then
+ echo
+ $ECHO "*** Warning: Linking the shared library $output against the"
+ $ECHO "*** static library $deplib is not portable!"
+ deplibs="$deplib $deplibs"
+ else
+ echo
+ $ECHO "*** Warning: Trying to link with static lib archive $deplib."
+ echo "*** I have the capability to make that library automatically link in when"
+ echo "*** you link to this library. But I can only do this if you have a"
+ echo "*** shared version of the library, which you do not appear to have"
+ echo "*** because the file extensions .$libext of this argument makes me believe"
+ echo "*** that it is just a static archive that I should not use here."
+ fi
+ ;;
+ esac
+ continue
+ ;;
+ prog)
+ if test link != "$pass"; then
+ deplibs="$deplib $deplibs"
+ else
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ fi
+ continue
+ ;;
+ esac # linkmode
+ ;; # *.$libext
+ *.lo | *.$objext)
+ if test conv = "$pass"; then
+ deplibs="$deplib $deplibs"
+ elif test prog = "$linkmode"; then
+ if test dlpreopen = "$pass" || test yes != "$dlopen_support" || test no = "$build_libtool_libs"; then
+ # If there is no dlopen support or we're linking statically,
+ # we need to preload.
+ func_append newdlprefiles " $deplib"
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ func_append newdlfiles " $deplib"
+ fi
+ fi
+ continue
+ ;;
+ %DEPLIBS%)
+ alldeplibs=:
+ continue
+ ;;
+ esac # case $deplib
+
+ $found || test -f "$lib" \
+ || func_fatal_error "cannot find the library '$lib' or unhandled argument '$deplib'"
+
+ # Check to see that this really is a libtool archive.
+ func_lalib_unsafe_p "$lib" \
+ || func_fatal_error "'$lib' is not a valid libtool archive"
+
+ func_dirname "$lib" "" "."
+ ladir=$func_dirname_result
+
+ dlname=
+ dlopen=
+ dlpreopen=
+ libdir=
+ library_names=
+ old_library=
+ inherited_linker_flags=
+ # If the library was installed with an old release of libtool,
+ # it will not redefine variables installed, or shouldnotlink
+ installed=yes
+ shouldnotlink=no
+ avoidtemprpath=
+
+
+ # Read the .la file
+ func_source "$lib"
+
+ # Convert "-framework foo" to "foo.ltframework"
+ if test -n "$inherited_linker_flags"; then
+ tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'`
+ for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do
+ case " $new_inherited_linker_flags " in
+ *" $tmp_inherited_linker_flag "*) ;;
+ *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";;
+ esac
+ done
+ fi
+ dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ if test lib,link = "$linkmode,$pass" ||
+ test prog,scan = "$linkmode,$pass" ||
+ { test prog != "$linkmode" && test lib != "$linkmode"; }; then
+ test -n "$dlopen" && func_append dlfiles " $dlopen"
+ test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen"
+ fi
+
+ if test conv = "$pass"; then
+ # Only check for convenience libraries
+ deplibs="$lib $deplibs"
+ if test -z "$libdir"; then
+ if test -z "$old_library"; then
+ func_fatal_error "cannot find name of link library for '$lib'"
+ fi
+ # It is a libtool convenience library, so add in its objects.
+ func_append convenience " $ladir/$objdir/$old_library"
+ func_append old_convenience " $ladir/$objdir/$old_library"
+ elif test prog != "$linkmode" && test lib != "$linkmode"; then
+ func_fatal_error "'$lib' is not a convenience library"
+ fi
+ tmp_libs=
+ for deplib in $dependency_libs; do
+ deplibs="$deplib $deplibs"
+ if $opt_preserve_dup_deps; then
+ case "$tmp_libs " in
+ *" $deplib "*) func_append specialdeplibs " $deplib" ;;
+ esac
+ fi
+ func_append tmp_libs " $deplib"
+ done
+ continue
+ fi # $pass = conv
+
+
+ # Get the name of the library we link against.
+ linklib=
+ if test -n "$old_library" &&
+ { test yes = "$prefer_static_libs" ||
+ test built,no = "$prefer_static_libs,$installed"; }; then
+ linklib=$old_library
+ else
+ for l in $old_library $library_names; do
+ linklib=$l
+ done
+ fi
+ if test -z "$linklib"; then
+ func_fatal_error "cannot find name of link library for '$lib'"
+ fi
+
+ # This library was specified with -dlopen.
+ if test dlopen = "$pass"; then
+ test -z "$libdir" \
+ && func_fatal_error "cannot -dlopen a convenience library: '$lib'"
+ if test -z "$dlname" ||
+ test yes != "$dlopen_support" ||
+ test no = "$build_libtool_libs"
+ then
+ # If there is no dlname, no dlopen support or we're linking
+ # statically, we need to preload. We also need to preload any
+ # dependent libraries so libltdl's deplib preloader doesn't
+ # bomb out in the load deplibs phase.
+ func_append dlprefiles " $lib $dependency_libs"
+ else
+ func_append newdlfiles " $lib"
+ fi
+ continue
+ fi # $pass = dlopen
+
+ # We need an absolute path.
+ case $ladir in
+ [\\/]* | [A-Za-z]:[\\/]*) abs_ladir=$ladir ;;
+ *)
+ abs_ladir=`cd "$ladir" && pwd`
+ if test -z "$abs_ladir"; then
+ func_warning "cannot determine absolute directory name of '$ladir'"
+ func_warning "passing it literally to the linker, although it might fail"
+ abs_ladir=$ladir
+ fi
+ ;;
+ esac
+ func_basename "$lib"
+ laname=$func_basename_result
+
+ # Find the relevant object directory and library name.
+ if test yes = "$installed"; then
+ if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+ func_warning "library '$lib' was moved."
+ dir=$ladir
+ absdir=$abs_ladir
+ libdir=$abs_ladir
+ else
+ dir=$lt_sysroot$libdir
+ absdir=$lt_sysroot$libdir
+ fi
+ test yes = "$hardcode_automatic" && avoidtemprpath=yes
+ else
+ if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+ dir=$ladir
+ absdir=$abs_ladir
+ # Remove this search path later
+ func_append notinst_path " $abs_ladir"
+ else
+ dir=$ladir/$objdir
+ absdir=$abs_ladir/$objdir
+ # Remove this search path later
+ func_append notinst_path " $abs_ladir"
+ fi
+ fi # $installed = yes
+ func_stripname 'lib' '.la' "$laname"
+ name=$func_stripname_result
+
+ # This library was specified with -dlpreopen.
+ if test dlpreopen = "$pass"; then
+ if test -z "$libdir" && test prog = "$linkmode"; then
+ func_fatal_error "only libraries may -dlpreopen a convenience library: '$lib'"
+ fi
+ case $host in
+ # special handling for platforms with PE-DLLs.
+ *cygwin* | *mingw* | *cegcc* )
+ # Linker will automatically link against shared library if both
+ # static and shared are present. Therefore, ensure we extract
+ # symbols from the import library if a shared library is present
+ # (otherwise, the dlopen module name will be incorrect). We do
+ # this by putting the import library name into $newdlprefiles.
+ # We recover the dlopen module name by 'saving' the la file
+ # name in a special purpose variable, and (later) extracting the
+ # dlname from the la file.
+ if test -n "$dlname"; then
+ func_tr_sh "$dir/$linklib"
+ eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname"
+ func_append newdlprefiles " $dir/$linklib"
+ else
+ func_append newdlprefiles " $dir/$old_library"
+ # Keep a list of preopened convenience libraries to check
+ # that they are being used correctly in the link pass.
+ test -z "$libdir" && \
+ func_append dlpreconveniencelibs " $dir/$old_library"
+ fi
+ ;;
+ * )
+ # Prefer using a static library (so that no silly _DYNAMIC symbols
+ # are required to link).
+ if test -n "$old_library"; then
+ func_append newdlprefiles " $dir/$old_library"
+ # Keep a list of preopened convenience libraries to check
+ # that they are being used correctly in the link pass.
+ test -z "$libdir" && \
+ func_append dlpreconveniencelibs " $dir/$old_library"
+ # Otherwise, use the dlname, so that lt_dlopen finds it.
+ elif test -n "$dlname"; then
+ func_append newdlprefiles " $dir/$dlname"
+ else
+ func_append newdlprefiles " $dir/$linklib"
+ fi
+ ;;
+ esac
+ fi # $pass = dlpreopen
+
+ if test -z "$libdir"; then
+ # Link the convenience library
+ if test lib = "$linkmode"; then
+ deplibs="$dir/$old_library $deplibs"
+ elif test prog,link = "$linkmode,$pass"; then
+ compile_deplibs="$dir/$old_library $compile_deplibs"
+ finalize_deplibs="$dir/$old_library $finalize_deplibs"
+ else
+ deplibs="$lib $deplibs" # used for prog,scan pass
+ fi
+ continue
+ fi
+
+
+ if test prog = "$linkmode" && test link != "$pass"; then
+ func_append newlib_search_path " $ladir"
+ deplibs="$lib $deplibs"
+
+ linkalldeplibs=false
+ if test no != "$link_all_deplibs" || test -z "$library_names" ||
+ test no = "$build_libtool_libs"; then
+ linkalldeplibs=:
+ fi
+
+ tmp_libs=
+ for deplib in $dependency_libs; do
+ case $deplib in
+ -L*) func_stripname '-L' '' "$deplib"
+ func_resolve_sysroot "$func_stripname_result"
+ func_append newlib_search_path " $func_resolve_sysroot_result"
+ ;;
+ esac
+ # Need to link against all dependency_libs?
+ if $linkalldeplibs; then
+ deplibs="$deplib $deplibs"
+ else
+ # Need to hardcode shared library paths
+ # or/and link against static libraries
+ newdependency_libs="$deplib $newdependency_libs"
+ fi
+ if $opt_preserve_dup_deps; then
+ case "$tmp_libs " in
+ *" $deplib "*) func_append specialdeplibs " $deplib" ;;
+ esac
+ fi
+ func_append tmp_libs " $deplib"
+ done # for deplib
+ continue
+ fi # $linkmode = prog...
+
+ if test prog,link = "$linkmode,$pass"; then
+ if test -n "$library_names" &&
+ { { test no = "$prefer_static_libs" ||
+ test built,yes = "$prefer_static_libs,$installed"; } ||
+ test -z "$old_library"; }; then
+ # We need to hardcode the library path
+ if test -n "$shlibpath_var" && test -z "$avoidtemprpath"; then
+ # Make sure the rpath contains only unique directories.
+ case $temp_rpath: in
+ *"$absdir:"*) ;;
+ *) func_append temp_rpath "$absdir:" ;;
+ esac
+ fi
+
+ # Hardcode the library path.
+ # Skip directories that are in the system default run-time
+ # search path.
+ case " $sys_lib_dlsearch_path " in
+ *" $absdir "*) ;;
+ *)
+ case "$compile_rpath " in
+ *" $absdir "*) ;;
+ *) func_append compile_rpath " $absdir" ;;
+ esac
+ ;;
+ esac
+ case " $sys_lib_dlsearch_path " in
+ *" $libdir "*) ;;
+ *)
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) func_append finalize_rpath " $libdir" ;;
+ esac
+ ;;
+ esac
+ fi # $linkmode,$pass = prog,link...
+
+ if $alldeplibs &&
+ { test pass_all = "$deplibs_check_method" ||
+ { test yes = "$build_libtool_libs" &&
+ test -n "$library_names"; }; }; then
+ # We only need to search for static libraries
+ continue
+ fi
+ fi
+
+ link_static=no # Whether the deplib will be linked statically
+ use_static_libs=$prefer_static_libs
+ if test built = "$use_static_libs" && test yes = "$installed"; then
+ use_static_libs=no
+ fi
+ if test -n "$library_names" &&
+ { test no = "$use_static_libs" || test -z "$old_library"; }; then
+ case $host in
+ *cygwin* | *mingw* | *cegcc* | *os2*)
+ # No point in relinking DLLs because paths are not encoded
+ func_append notinst_deplibs " $lib"
+ need_relink=no
+ ;;
+ *)
+ if test no = "$installed"; then
+ func_append notinst_deplibs " $lib"
+ need_relink=yes
+ fi
+ ;;
+ esac
+ # This is a shared library
+
+ # Warn about portability, can't link against -module's on some
+ # systems (darwin). Don't bleat about dlopened modules though!
+ dlopenmodule=
+ for dlpremoduletest in $dlprefiles; do
+ if test "X$dlpremoduletest" = "X$lib"; then
+ dlopenmodule=$dlpremoduletest
+ break
+ fi
+ done
+ if test -z "$dlopenmodule" && test yes = "$shouldnotlink" && test link = "$pass"; then
+ echo
+ if test prog = "$linkmode"; then
+ $ECHO "*** Warning: Linking the executable $output against the loadable module"
+ else
+ $ECHO "*** Warning: Linking the shared library $output against the loadable module"
+ fi
+ $ECHO "*** $linklib is not portable!"
+ fi
+ if test lib = "$linkmode" &&
+ test yes = "$hardcode_into_libs"; then
+ # Hardcode the library path.
+ # Skip directories that are in the system default run-time
+ # search path.
+ case " $sys_lib_dlsearch_path " in
+ *" $absdir "*) ;;
+ *)
+ case "$compile_rpath " in
+ *" $absdir "*) ;;
+ *) func_append compile_rpath " $absdir" ;;
+ esac
+ ;;
+ esac
+ case " $sys_lib_dlsearch_path " in
+ *" $libdir "*) ;;
+ *)
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) func_append finalize_rpath " $libdir" ;;
+ esac
+ ;;
+ esac
+ fi
+
+ if test -n "$old_archive_from_expsyms_cmds"; then
+ # figure out the soname
+ set dummy $library_names
+ shift
+ realname=$1
+ shift
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ # use dlname if we got it. it's perfectly good, no?
+ if test -n "$dlname"; then
+ soname=$dlname
+ elif test -n "$soname_spec"; then
+ # bleh windows
+ case $host in
+ *cygwin* | mingw* | *cegcc* | *os2*)
+ func_arith $current - $age
+ major=$func_arith_result
+ versuffix=-$major
+ ;;
+ esac
+ eval soname=\"$soname_spec\"
+ else
+ soname=$realname
+ fi
+
+ # Make a new name for the extract_expsyms_cmds to use
+ soroot=$soname
+ func_basename "$soroot"
+ soname=$func_basename_result
+ func_stripname 'lib' '.dll' "$soname"
+ newlib=libimp-$func_stripname_result.a
+
+ # If the library has no export list, then create one now
+ if test -f "$output_objdir/$soname-def"; then :
+ else
+ func_verbose "extracting exported symbol list from '$soname'"
+ func_execute_cmds "$extract_expsyms_cmds" 'exit $?'
+ fi
+
+ # Create $newlib
+ if test -f "$output_objdir/$newlib"; then :; else
+ func_verbose "generating import library for '$soname'"
+ func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?'
+ fi
+ # make sure the library variables are pointing to the new library
+ dir=$output_objdir
+ linklib=$newlib
+ fi # test -n "$old_archive_from_expsyms_cmds"
+
+ if test prog = "$linkmode" || test relink != "$opt_mode"; then
+ add_shlibpath=
+ add_dir=
+ add=
+ lib_linked=yes
+ case $hardcode_action in
+ immediate | unsupported)
+ if test no = "$hardcode_direct"; then
+ add=$dir/$linklib
+ case $host in
+ *-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;;
+ *-*-sysv4*uw2*) add_dir=-L$dir ;;
+ *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \
+ *-*-unixware7*) add_dir=-L$dir ;;
+ *-*-darwin* )
+ # if the lib is a (non-dlopened) module then we cannot
+ # link against it, someone is ignoring the earlier warnings
+ if /usr/bin/file -L $add 2> /dev/null |
+ $GREP ": [^:]* bundle" >/dev/null; then
+ if test "X$dlopenmodule" != "X$lib"; then
+ $ECHO "*** Warning: lib $linklib is a module, not a shared library"
+ if test -z "$old_library"; then
+ echo
+ echo "*** And there doesn't seem to be a static archive available"
+ echo "*** The link will probably fail, sorry"
+ else
+ add=$dir/$old_library
+ fi
+ elif test -n "$old_library"; then
+ add=$dir/$old_library
+ fi
+ fi
+ esac
+ elif test no = "$hardcode_minus_L"; then
+ case $host in
+ *-*-sunos*) add_shlibpath=$dir ;;
+ esac
+ add_dir=-L$dir
+ add=-l$name
+ elif test no = "$hardcode_shlibpath_var"; then
+ add_shlibpath=$dir
+ add=-l$name
+ else
+ lib_linked=no
+ fi
+ ;;
+ relink)
+ if test yes = "$hardcode_direct" &&
+ test no = "$hardcode_direct_absolute"; then
+ add=$dir/$linklib
+ elif test yes = "$hardcode_minus_L"; then
+ add_dir=-L$absdir
+ # Try looking first in the location we're being installed to.
+ if test -n "$inst_prefix_dir"; then
+ case $libdir in
+ [\\/]*)
+ func_append add_dir " -L$inst_prefix_dir$libdir"
+ ;;
+ esac
+ fi
+ add=-l$name
+ elif test yes = "$hardcode_shlibpath_var"; then
+ add_shlibpath=$dir
+ add=-l$name
+ else
+ lib_linked=no
+ fi
+ ;;
+ *) lib_linked=no ;;
+ esac
+
+ if test yes != "$lib_linked"; then
+ func_fatal_configuration "unsupported hardcode properties"
+ fi
+
+ if test -n "$add_shlibpath"; then
+ case :$compile_shlibpath: in
+ *":$add_shlibpath:"*) ;;
+ *) func_append compile_shlibpath "$add_shlibpath:" ;;
+ esac
+ fi
+ if test prog = "$linkmode"; then
+ test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs"
+ test -n "$add" && compile_deplibs="$add $compile_deplibs"
+ else
+ test -n "$add_dir" && deplibs="$add_dir $deplibs"
+ test -n "$add" && deplibs="$add $deplibs"
+ if test yes != "$hardcode_direct" &&
+ test yes != "$hardcode_minus_L" &&
+ test yes = "$hardcode_shlibpath_var"; then
+ case :$finalize_shlibpath: in
+ *":$libdir:"*) ;;
+ *) func_append finalize_shlibpath "$libdir:" ;;
+ esac
+ fi
+ fi
+ fi
+
+ if test prog = "$linkmode" || test relink = "$opt_mode"; then
+ add_shlibpath=
+ add_dir=
+ add=
+ # Finalize command for both is simple: just hardcode it.
+ if test yes = "$hardcode_direct" &&
+ test no = "$hardcode_direct_absolute"; then
+ add=$libdir/$linklib
+ elif test yes = "$hardcode_minus_L"; then
+ add_dir=-L$libdir
+ add=-l$name
+ elif test yes = "$hardcode_shlibpath_var"; then
+ case :$finalize_shlibpath: in
+ *":$libdir:"*) ;;
+ *) func_append finalize_shlibpath "$libdir:" ;;
+ esac
+ add=-l$name
+ elif test yes = "$hardcode_automatic"; then
+ if test -n "$inst_prefix_dir" &&
+ test -f "$inst_prefix_dir$libdir/$linklib"; then
+ add=$inst_prefix_dir$libdir/$linklib
+ else
+ add=$libdir/$linklib
+ fi
+ else
+ # We cannot seem to hardcode it, guess we'll fake it.
+ add_dir=-L$libdir
+ # Try looking first in the location we're being installed to.
+ if test -n "$inst_prefix_dir"; then
+ case $libdir in
+ [\\/]*)
+ func_append add_dir " -L$inst_prefix_dir$libdir"
+ ;;
+ esac
+ fi
+ add=-l$name
+ fi
+
+ if test prog = "$linkmode"; then
+ test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs"
+ test -n "$add" && finalize_deplibs="$add $finalize_deplibs"
+ else
+ test -n "$add_dir" && deplibs="$add_dir $deplibs"
+ test -n "$add" && deplibs="$add $deplibs"
+ fi
+ fi
+ elif test prog = "$linkmode"; then
+ # Here we assume that one of hardcode_direct or hardcode_minus_L
+ # is not unsupported. This is valid on all known static and
+ # shared platforms.
+ if test unsupported != "$hardcode_direct"; then
+ test -n "$old_library" && linklib=$old_library
+ compile_deplibs="$dir/$linklib $compile_deplibs"
+ finalize_deplibs="$dir/$linklib $finalize_deplibs"
+ else
+ compile_deplibs="-l$name -L$dir $compile_deplibs"
+ finalize_deplibs="-l$name -L$dir $finalize_deplibs"
+ fi
+ elif test yes = "$build_libtool_libs"; then
+ # Not a shared library
+ if test pass_all != "$deplibs_check_method"; then
+ # We're trying link a shared library against a static one
+ # but the system doesn't support it.
+
+ # Just print a warning and add the library to dependency_libs so
+ # that the program can be linked against the static library.
+ echo
+ $ECHO "*** Warning: This system cannot link to static lib archive $lib."
+ echo "*** I have the capability to make that library automatically link in when"
+ echo "*** you link to this library. But I can only do this if you have a"
+ echo "*** shared version of the library, which you do not appear to have."
+ if test yes = "$module"; then
+ echo "*** But as you try to build a module library, libtool will still create "
+ echo "*** a static module, that should work as long as the dlopening application"
+ echo "*** is linked with the -dlopen flag to resolve symbols at runtime."
+ if test -z "$global_symbol_pipe"; then
+ echo
+ echo "*** However, this would only work if libtool was able to extract symbol"
+ echo "*** lists from a program, using 'nm' or equivalent, but libtool could"
+ echo "*** not find such a program. So, this module is probably useless."
+ echo "*** 'nm' from GNU binutils and a full rebuild may help."
+ fi
+ if test no = "$build_old_libs"; then
+ build_libtool_libs=module
+ build_old_libs=yes
+ else
+ build_libtool_libs=no
+ fi
+ fi
+ else
+ deplibs="$dir/$old_library $deplibs"
+ link_static=yes
+ fi
+ fi # link shared/static library?
+
+ if test lib = "$linkmode"; then
+ if test -n "$dependency_libs" &&
+ { test yes != "$hardcode_into_libs" ||
+ test yes = "$build_old_libs" ||
+ test yes = "$link_static"; }; then
+ # Extract -R from dependency_libs
+ temp_deplibs=
+ for libdir in $dependency_libs; do
+ case $libdir in
+ -R*) func_stripname '-R' '' "$libdir"
+ temp_xrpath=$func_stripname_result
+ case " $xrpath " in
+ *" $temp_xrpath "*) ;;
+ *) func_append xrpath " $temp_xrpath";;
+ esac;;
+ *) func_append temp_deplibs " $libdir";;
+ esac
+ done
+ dependency_libs=$temp_deplibs
+ fi
+
+ func_append newlib_search_path " $absdir"
+ # Link against this library
+ test no = "$link_static" && newdependency_libs="$abs_ladir/$laname $newdependency_libs"
+ # ... and its dependency_libs
+ tmp_libs=
+ for deplib in $dependency_libs; do
+ newdependency_libs="$deplib $newdependency_libs"
+ case $deplib in
+ -L*) func_stripname '-L' '' "$deplib"
+ func_resolve_sysroot "$func_stripname_result";;
+ *) func_resolve_sysroot "$deplib" ;;
+ esac
+ if $opt_preserve_dup_deps; then
+ case "$tmp_libs " in
+ *" $func_resolve_sysroot_result "*)
+ func_append specialdeplibs " $func_resolve_sysroot_result" ;;
+ esac
+ fi
+ func_append tmp_libs " $func_resolve_sysroot_result"
+ done
+
+ if test no != "$link_all_deplibs"; then
+ # Add the search paths of all dependency libraries
+ for deplib in $dependency_libs; do
+ path=
+ case $deplib in
+ -L*) path=$deplib ;;
+ *.la)
+ func_resolve_sysroot "$deplib"
+ deplib=$func_resolve_sysroot_result
+ func_dirname "$deplib" "" "."
+ dir=$func_dirname_result
+ # We need an absolute path.
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]*) absdir=$dir ;;
+ *)
+ absdir=`cd "$dir" && pwd`
+ if test -z "$absdir"; then
+ func_warning "cannot determine absolute directory name of '$dir'"
+ absdir=$dir
+ fi
+ ;;
+ esac
+ if $GREP "^installed=no" $deplib > /dev/null; then
+ case $host in
+ *-*-darwin*)
+ depdepl=
+ eval deplibrary_names=`$SED -n -e 's/^library_names=\(.*\)$/\1/p' $deplib`
+ if test -n "$deplibrary_names"; then
+ for tmp in $deplibrary_names; do
+ depdepl=$tmp
+ done
+ if test -f "$absdir/$objdir/$depdepl"; then
+ depdepl=$absdir/$objdir/$depdepl
+ darwin_install_name=`$OTOOL -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
+ if test -z "$darwin_install_name"; then
+ darwin_install_name=`$OTOOL64 -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
+ fi
+ func_append compiler_flags " $wl-dylib_file $wl$darwin_install_name:$depdepl"
+ func_append linker_flags " -dylib_file $darwin_install_name:$depdepl"
+ path=
+ fi
+ fi
+ ;;
+ *)
+ path=-L$absdir/$objdir
+ ;;
+ esac
+ else
+ eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+ test -z "$libdir" && \
+ func_fatal_error "'$deplib' is not a valid libtool archive"
+ test "$absdir" != "$libdir" && \
+ func_warning "'$deplib' seems to be moved"
+
+ path=-L$absdir
+ fi
+ ;;
+ esac
+ case " $deplibs " in
+ *" $path "*) ;;
+ *) deplibs="$path $deplibs" ;;
+ esac
+ done
+ fi # link_all_deplibs != no
+ fi # linkmode = lib
+ done # for deplib in $libs
+ if test link = "$pass"; then
+ if test prog = "$linkmode"; then
+ compile_deplibs="$new_inherited_linker_flags $compile_deplibs"
+ finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs"
+ else
+ compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ fi
+ fi
+ dependency_libs=$newdependency_libs
+ if test dlpreopen = "$pass"; then
+ # Link the dlpreopened libraries before other libraries
+ for deplib in $save_deplibs; do
+ deplibs="$deplib $deplibs"
+ done
+ fi
+ if test dlopen != "$pass"; then
+ test conv = "$pass" || {
+ # Make sure lib_search_path contains only unique directories.
+ lib_search_path=
+ for dir in $newlib_search_path; do
+ case "$lib_search_path " in
+ *" $dir "*) ;;
+ *) func_append lib_search_path " $dir" ;;
+ esac
+ done
+ newlib_search_path=
+ }
+
+ if test prog,link = "$linkmode,$pass"; then
+ vars="compile_deplibs finalize_deplibs"
+ else
+ vars=deplibs
+ fi
+ for var in $vars dependency_libs; do
+ # Add libraries to $var in reverse order
+ eval tmp_libs=\"\$$var\"
+ new_libs=
+ for deplib in $tmp_libs; do
+ # FIXME: Pedantically, this is the right thing to do, so
+ # that some nasty dependency loop isn't accidentally
+ # broken:
+ #new_libs="$deplib $new_libs"
+ # Pragmatically, this seems to cause very few problems in
+ # practice:
+ case $deplib in
+ -L*) new_libs="$deplib $new_libs" ;;
+ -R*) ;;
+ *)
+ # And here is the reason: when a library appears more
+ # than once as an explicit dependence of a library, or
+ # is implicitly linked in more than once by the
+ # compiler, it is considered special, and multiple
+ # occurrences thereof are not removed. Compare this
+ # with having the same library being listed as a
+ # dependency of multiple other libraries: in this case,
+ # we know (pedantically, we assume) the library does not
+ # need to be listed more than once, so we keep only the
+ # last copy. This is not always right, but it is rare
+ # enough that we require users that really mean to play
+ # such unportable linking tricks to link the library
+ # using -Wl,-lname, so that libtool does not consider it
+ # for duplicate removal.
+ case " $specialdeplibs " in
+ *" $deplib "*) new_libs="$deplib $new_libs" ;;
+ *)
+ case " $new_libs " in
+ *" $deplib "*) ;;
+ *) new_libs="$deplib $new_libs" ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ done
+ tmp_libs=
+ for deplib in $new_libs; do
+ case $deplib in
+ -L*)
+ case " $tmp_libs " in
+ *" $deplib "*) ;;
+ *) func_append tmp_libs " $deplib" ;;
+ esac
+ ;;
+ *) func_append tmp_libs " $deplib" ;;
+ esac
+ done
+ eval $var=\"$tmp_libs\"
+ done # for var
+ fi
+
+ # Add Sun CC postdeps if required:
+ test CXX = "$tagname" && {
+ case $host_os in
+ linux*)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*) # Sun C++ 5.9
+ func_suncc_cstd_abi
+
+ if test no != "$suncc_use_cstd_abi"; then
+ func_append postdeps ' -library=Cstd -library=Crun'
+ fi
+ ;;
+ esac
+ ;;
+
+ solaris*)
+ func_cc_basename "$CC"
+ case $func_cc_basename_result in
+ CC* | sunCC*)
+ func_suncc_cstd_abi
+
+ if test no != "$suncc_use_cstd_abi"; then
+ func_append postdeps ' -library=Cstd -library=Crun'
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ }
+
+ # Last step: remove runtime libs from dependency_libs
+ # (they stay in deplibs)
+ tmp_libs=
+ for i in $dependency_libs; do
+ case " $predeps $postdeps $compiler_lib_search_path " in
+ *" $i "*)
+ i=
+ ;;
+ esac
+ if test -n "$i"; then
+ func_append tmp_libs " $i"
+ fi
+ done
+ dependency_libs=$tmp_libs
+ done # for pass
+ if test prog = "$linkmode"; then
+ dlfiles=$newdlfiles
+ fi
+ if test prog = "$linkmode" || test lib = "$linkmode"; then
+ dlprefiles=$newdlprefiles
+ fi
+
+ case $linkmode in
+ oldlib)
+ if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then
+ func_warning "'-dlopen' is ignored for archives"
+ fi
+
+ case " $deplibs" in
+ *\ -l* | *\ -L*)
+ func_warning "'-l' and '-L' are ignored for archives" ;;
+ esac
+
+ test -n "$rpath" && \
+ func_warning "'-rpath' is ignored for archives"
+
+ test -n "$xrpath" && \
+ func_warning "'-R' is ignored for archives"
+
+ test -n "$vinfo" && \
+ func_warning "'-version-info/-version-number' is ignored for archives"
+
+ test -n "$release" && \
+ func_warning "'-release' is ignored for archives"
+
+ test -n "$export_symbols$export_symbols_regex" && \
+ func_warning "'-export-symbols' is ignored for archives"
+
+ # Now set the variables for building old libraries.
+ build_libtool_libs=no
+ oldlibs=$output
+ func_append objs "$old_deplibs"
+ ;;
+
+ lib)
+ # Make sure we only generate libraries of the form 'libNAME.la'.
+ case $outputname in
+ lib*)
+ func_stripname 'lib' '.la' "$outputname"
+ name=$func_stripname_result
+ eval shared_ext=\"$shrext_cmds\"
+ eval libname=\"$libname_spec\"
+ ;;
+ *)
+ test no = "$module" \
+ && func_fatal_help "libtool library '$output' must begin with 'lib'"
+
+ if test no != "$need_lib_prefix"; then
+ # Add the "lib" prefix for modules if required
+ func_stripname '' '.la' "$outputname"
+ name=$func_stripname_result
+ eval shared_ext=\"$shrext_cmds\"
+ eval libname=\"$libname_spec\"
+ else
+ func_stripname '' '.la' "$outputname"
+ libname=$func_stripname_result
+ fi
+ ;;
+ esac
+
+ if test -n "$objs"; then
+ if test pass_all != "$deplibs_check_method"; then
+ func_fatal_error "cannot build libtool library '$output' from non-libtool objects on this host:$objs"
+ else
+ echo
+ $ECHO "*** Warning: Linking the shared library $output against the non-libtool"
+ $ECHO "*** objects $objs is not portable!"
+ func_append libobjs " $objs"
+ fi
+ fi
+
+ test no = "$dlself" \
+ || func_warning "'-dlopen self' is ignored for libtool libraries"
+
+ set dummy $rpath
+ shift
+ test 1 -lt "$#" \
+ && func_warning "ignoring multiple '-rpath's for a libtool library"
+
+ install_libdir=$1
+
+ oldlibs=
+ if test -z "$rpath"; then
+ if test yes = "$build_libtool_libs"; then
+ # Building a libtool convenience library.
+ # Some compilers have problems with a '.al' extension so
+ # convenience libraries should have the same extension an
+ # archive normally would.
+ oldlibs="$output_objdir/$libname.$libext $oldlibs"
+ build_libtool_libs=convenience
+ build_old_libs=yes
+ fi
+
+ test -n "$vinfo" && \
+ func_warning "'-version-info/-version-number' is ignored for convenience libraries"
+
+ test -n "$release" && \
+ func_warning "'-release' is ignored for convenience libraries"
+ else
+
+ # Parse the version information argument.
+ save_ifs=$IFS; IFS=:
+ set dummy $vinfo 0 0 0
+ shift
+ IFS=$save_ifs
+
+ test -n "$7" && \
+ func_fatal_help "too many parameters to '-version-info'"
+
+ # convert absolute version numbers to libtool ages
+ # this retains compatibility with .la files and attempts
+ # to make the code below a bit more comprehensible
+
+ case $vinfo_number in
+ yes)
+ number_major=$1
+ number_minor=$2
+ number_revision=$3
+ #
+ # There are really only two kinds -- those that
+ # use the current revision as the major version
+ # and those that subtract age and use age as
+ # a minor version. But, then there is irix
+ # that has an extra 1 added just for fun
+ #
+ case $version_type in
+ # correct linux to gnu/linux during the next big refactor
+ darwin|freebsd-elf|linux|osf|windows|none)
+ func_arith $number_major + $number_minor
+ current=$func_arith_result
+ age=$number_minor
+ revision=$number_revision
+ ;;
+ freebsd-aout|qnx|sunos)
+ current=$number_major
+ revision=$number_minor
+ age=0
+ ;;
+ irix|nonstopux)
+ func_arith $number_major + $number_minor
+ current=$func_arith_result
+ age=$number_minor
+ revision=$number_minor
+ lt_irix_increment=no
+ ;;
+ esac
+ ;;
+ no)
+ current=$1
+ revision=$2
+ age=$3
+ ;;
+ esac
+
+ # Check that each of the things are valid numbers.
+ case $current in
+ 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+ *)
+ func_error "CURRENT '$current' must be a nonnegative integer"
+ func_fatal_error "'$vinfo' is not valid version information"
+ ;;
+ esac
+
+ case $revision in
+ 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+ *)
+ func_error "REVISION '$revision' must be a nonnegative integer"
+ func_fatal_error "'$vinfo' is not valid version information"
+ ;;
+ esac
+
+ case $age in
+ 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+ *)
+ func_error "AGE '$age' must be a nonnegative integer"
+ func_fatal_error "'$vinfo' is not valid version information"
+ ;;
+ esac
+
+ if test "$age" -gt "$current"; then
+ func_error "AGE '$age' is greater than the current interface number '$current'"
+ func_fatal_error "'$vinfo' is not valid version information"
+ fi
+
+ # Calculate the version variables.
+ major=
+ versuffix=
+ verstring=
+ case $version_type in
+ none) ;;
+
+ darwin)
+ # Like Linux, but with the current version available in
+ # verstring for coding it into the library header
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix=$major.$age.$revision
+ # Darwin ld doesn't like 0 for these options...
+ func_arith $current + 1
+ minor_current=$func_arith_result
+ xlcverstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision"
+ verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
+ # On Darwin other compilers
+ case $CC in
+ nagfor*)
+ verstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision"
+ ;;
+ *)
+ verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
+ ;;
+ esac
+ ;;
+
+ freebsd-aout)
+ major=.$current
+ versuffix=.$current.$revision
+ ;;
+
+ freebsd-elf)
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix=$major.$age.$revision
+ ;;
+
+ irix | nonstopux)
+ if test no = "$lt_irix_increment"; then
+ func_arith $current - $age
+ else
+ func_arith $current - $age + 1
+ fi
+ major=$func_arith_result
+
+ case $version_type in
+ nonstopux) verstring_prefix=nonstopux ;;
+ *) verstring_prefix=sgi ;;
+ esac
+ verstring=$verstring_prefix$major.$revision
+
+ # Add in all the interfaces that we are compatible with.
+ loop=$revision
+ while test 0 -ne "$loop"; do
+ func_arith $revision - $loop
+ iface=$func_arith_result
+ func_arith $loop - 1
+ loop=$func_arith_result
+ verstring=$verstring_prefix$major.$iface:$verstring
+ done
+
+ # Before this point, $major must not contain '.'.
+ major=.$major
+ versuffix=$major.$revision
+ ;;
+
+ linux) # correct to gnu/linux during the next big refactor
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix=$major.$age.$revision
+ ;;
+
+ osf)
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix=.$current.$age.$revision
+ verstring=$current.$age.$revision
+
+ # Add in all the interfaces that we are compatible with.
+ loop=$age
+ while test 0 -ne "$loop"; do
+ func_arith $current - $loop
+ iface=$func_arith_result
+ func_arith $loop - 1
+ loop=$func_arith_result
+ verstring=$verstring:$iface.0
+ done
+
+ # Make executables depend on our current version.
+ func_append verstring ":$current.0"
+ ;;
+
+ qnx)
+ major=.$current
+ versuffix=.$current
+ ;;
+
+ sco)
+ major=.$current
+ versuffix=.$current
+ ;;
+
+ sunos)
+ major=.$current
+ versuffix=.$current.$revision
+ ;;
+
+ windows)
+ # Use '-' rather than '.', since we only want one
+ # extension on DOS 8.3 file systems.
+ func_arith $current - $age
+ major=$func_arith_result
+ versuffix=-$major
+ ;;
+
+ *)
+ func_fatal_configuration "unknown library version type '$version_type'"
+ ;;
+ esac
+
+ # Clear the version info if we defaulted, and they specified a release.
+ if test -z "$vinfo" && test -n "$release"; then
+ major=
+ case $version_type in
+ darwin)
+ # we can't check for "0.0" in archive_cmds due to quoting
+ # problems, so we reset it completely
+ verstring=
+ ;;
+ *)
+ verstring=0.0
+ ;;
+ esac
+ if test no = "$need_version"; then
+ versuffix=
+ else
+ versuffix=.0.0
+ fi
+ fi
+
+ # Remove version info from name if versioning should be avoided
+ if test yes,no = "$avoid_version,$need_version"; then
+ major=
+ versuffix=
+ verstring=
+ fi
+
+ # Check to see if the archive will have undefined symbols.
+ if test yes = "$allow_undefined"; then
+ if test unsupported = "$allow_undefined_flag"; then
+ if test yes = "$build_old_libs"; then
+ func_warning "undefined symbols not allowed in $host shared libraries; building static only"
+ build_libtool_libs=no
+ else
+ func_fatal_error "can't build $host shared library unless -no-undefined is specified"
+ fi
+ fi
+ else
+ # Don't allow undefined symbols.
+ allow_undefined_flag=$no_undefined_flag
+ fi
+
+ fi
+
+ func_generate_dlsyms "$libname" "$libname" :
+ func_append libobjs " $symfileobj"
+ test " " = "$libobjs" && libobjs=
+
+ if test relink != "$opt_mode"; then
+ # Remove our outputs, but don't remove object files since they
+ # may have been created when compiling PIC objects.
+ removelist=
+ tempremovelist=`$ECHO "$output_objdir/*"`
+ for p in $tempremovelist; do
+ case $p in
+ *.$objext | *.gcno)
+ ;;
+ $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/$libname$release.*)
+ if test -n "$precious_files_regex"; then
+ if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1
+ then
+ continue
+ fi
+ fi
+ func_append removelist " $p"
+ ;;
+ *) ;;
+ esac
+ done
+ test -n "$removelist" && \
+ func_show_eval "${RM}r \$removelist"
+ fi
+
+ # Now set the variables for building old libraries.
+ if test yes = "$build_old_libs" && test convenience != "$build_libtool_libs"; then
+ func_append oldlibs " $output_objdir/$libname.$libext"
+
+ # Transform .lo files to .o files.
+ oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; $lo2o" | $NL2SP`
+ fi
+
+ # Eliminate all temporary directories.
+ #for path in $notinst_path; do
+ # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"`
+ # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"`
+ # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"`
+ #done
+
+ if test -n "$xrpath"; then
+ # If the user specified any rpath flags, then add them.
+ temp_xrpath=
+ for libdir in $xrpath; do
+ func_replace_sysroot "$libdir"
+ func_append temp_xrpath " -R$func_replace_sysroot_result"
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) func_append finalize_rpath " $libdir" ;;
+ esac
+ done
+ if test yes != "$hardcode_into_libs" || test yes = "$build_old_libs"; then
+ dependency_libs="$temp_xrpath $dependency_libs"
+ fi
+ fi
+
+ # Make sure dlfiles contains only unique files that won't be dlpreopened
+ old_dlfiles=$dlfiles
+ dlfiles=
+ for lib in $old_dlfiles; do
+ case " $dlprefiles $dlfiles " in
+ *" $lib "*) ;;
+ *) func_append dlfiles " $lib" ;;
+ esac
+ done
+
+ # Make sure dlprefiles contains only unique files
+ old_dlprefiles=$dlprefiles
+ dlprefiles=
+ for lib in $old_dlprefiles; do
+ case "$dlprefiles " in
+ *" $lib "*) ;;
+ *) func_append dlprefiles " $lib" ;;
+ esac
+ done
+
+ if test yes = "$build_libtool_libs"; then
+ if test -n "$rpath"; then
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*)
+ # these systems don't actually have a c library (as such)!
+ ;;
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # Rhapsody C library is in the System framework
+ func_append deplibs " System.ltframework"
+ ;;
+ *-*-netbsd*)
+ # Don't link with libc until the a.out ld.so is fixed.
+ ;;
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+ # Do not include libc due to us having libc/libc_r.
+ ;;
+ *-*-sco3.2v5* | *-*-sco5v6*)
+ # Causes problems with __ctype
+ ;;
+ *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+ # Compiler inserts libc in the correct place for threads to work
+ ;;
+ *)
+ # Add libc to deplibs on all other systems if necessary.
+ if test yes = "$build_libtool_need_lc"; then
+ func_append deplibs " -lc"
+ fi
+ ;;
+ esac
+ fi
+
+ # Transform deplibs into only deplibs that can be linked in shared.
+ name_save=$name
+ libname_save=$libname
+ release_save=$release
+ versuffix_save=$versuffix
+ major_save=$major
+ # I'm not sure if I'm treating the release correctly. I think
+ # release should show up in the -l (ie -lgmp5) so we don't want to
+ # add it in twice. Is that correct?
+ release=
+ versuffix=
+ major=
+ newdeplibs=
+ droppeddeps=no
+ case $deplibs_check_method in
+ pass_all)
+ # Don't check for shared/static. Everything works.
+ # This might be a little naive. We might want to check
+ # whether the library exists or not. But this is on
+ # osf3 & osf4 and I'm not really sure... Just
+ # implementing what was already the behavior.
+ newdeplibs=$deplibs
+ ;;
+ test_compile)
+ # This code stresses the "libraries are programs" paradigm to its
+ # limits. Maybe even breaks it. We compile a program, linking it
+ # against the deplibs as a proxy for the library. Then we can check
+ # whether they linked in statically or dynamically with ldd.
+ $opt_dry_run || $RM conftest.c
+ cat > conftest.c <<EOF
+ int main() { return 0; }
+EOF
+ $opt_dry_run || $RM conftest
+ if $LTCC $LTCFLAGS -o conftest conftest.c $deplibs; then
+ ldd_output=`ldd conftest`
+ for i in $deplibs; do
+ case $i in
+ -l*)
+ func_stripname -l '' "$i"
+ name=$func_stripname_result
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+ case " $predeps $postdeps " in
+ *" $i "*)
+ func_append newdeplibs " $i"
+ i=
+ ;;
+ esac
+ fi
+ if test -n "$i"; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+ set dummy $deplib_matches; shift
+ deplib_match=$1
+ if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0; then
+ func_append newdeplibs " $i"
+ else
+ droppeddeps=yes
+ echo
+ $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+ echo "*** I have the capability to make that library automatically link in when"
+ echo "*** you link to this library. But I can only do this if you have a"
+ echo "*** shared version of the library, which I believe you do not have"
+ echo "*** because a test_compile did reveal that the linker did not use it for"
+ echo "*** its dynamic dependency list that programs get resolved with at runtime."
+ fi
+ fi
+ ;;
+ *)
+ func_append newdeplibs " $i"
+ ;;
+ esac
+ done
+ else
+ # Error occurred in the first compile. Let's try to salvage
+ # the situation: Compile a separate program for each library.
+ for i in $deplibs; do
+ case $i in
+ -l*)
+ func_stripname -l '' "$i"
+ name=$func_stripname_result
+ $opt_dry_run || $RM conftest
+ if $LTCC $LTCFLAGS -o conftest conftest.c $i; then
+ ldd_output=`ldd conftest`
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+ case " $predeps $postdeps " in
+ *" $i "*)
+ func_append newdeplibs " $i"
+ i=
+ ;;
+ esac
+ fi
+ if test -n "$i"; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+ set dummy $deplib_matches; shift
+ deplib_match=$1
+ if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0; then
+ func_append newdeplibs " $i"
+ else
+ droppeddeps=yes
+ echo
+ $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+ echo "*** I have the capability to make that library automatically link in when"
+ echo "*** you link to this library. But I can only do this if you have a"
+ echo "*** shared version of the library, which you do not appear to have"
+ echo "*** because a test_compile did reveal that the linker did not use this one"
+ echo "*** as a dynamic dependency that programs can get resolved with at runtime."
+ fi
+ fi
+ else
+ droppeddeps=yes
+ echo
+ $ECHO "*** Warning! Library $i is needed by this library but I was not able to"
+ echo "*** make it link in! You will probably need to install it or some"
+ echo "*** library that it depends on before this library will be fully"
+ echo "*** functional. Installing it before continuing would be even better."
+ fi
+ ;;
+ *)
+ func_append newdeplibs " $i"
+ ;;
+ esac
+ done
+ fi
+ ;;
+ file_magic*)
+ set dummy $deplibs_check_method; shift
+ file_magic_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+ for a_deplib in $deplibs; do
+ case $a_deplib in
+ -l*)
+ func_stripname -l '' "$a_deplib"
+ name=$func_stripname_result
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+ case " $predeps $postdeps " in
+ *" $a_deplib "*)
+ func_append newdeplibs " $a_deplib"
+ a_deplib=
+ ;;
+ esac
+ fi
+ if test -n "$a_deplib"; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ if test -n "$file_magic_glob"; then
+ libnameglob=`func_echo_all "$libname" | $SED -e $file_magic_glob`
+ else
+ libnameglob=$libname
+ fi
+ test yes = "$want_nocaseglob" && nocaseglob=`shopt -p nocaseglob`
+ for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+ if test yes = "$want_nocaseglob"; then
+ shopt -s nocaseglob
+ potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null`
+ $nocaseglob
+ else
+ potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null`
+ fi
+ for potent_lib in $potential_libs; do
+ # Follow soft links.
+ if ls -lLd "$potent_lib" 2>/dev/null |
+ $GREP " -> " >/dev/null; then
+ continue
+ fi
+ # The statement above tries to avoid entering an
+ # endless loop below, in case of cyclic links.
+ # We might still enter an endless loop, since a link
+ # loop can be closed while we follow links,
+ # but so what?
+ potlib=$potent_lib
+ while test -h "$potlib" 2>/dev/null; do
+ potliblink=`ls -ld $potlib | $SED 's/.* -> //'`
+ case $potliblink in
+ [\\/]* | [A-Za-z]:[\\/]*) potlib=$potliblink;;
+ *) potlib=`$ECHO "$potlib" | $SED 's|[^/]*$||'`"$potliblink";;
+ esac
+ done
+ if eval $file_magic_cmd \"\$potlib\" 2>/dev/null |
+ $SED -e 10q |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ func_append newdeplibs " $a_deplib"
+ a_deplib=
+ break 2
+ fi
+ done
+ done
+ fi
+ if test -n "$a_deplib"; then
+ droppeddeps=yes
+ echo
+ $ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+ echo "*** I have the capability to make that library automatically link in when"
+ echo "*** you link to this library. But I can only do this if you have a"
+ echo "*** shared version of the library, which you do not appear to have"
+ echo "*** because I did check the linker path looking for a file starting"
+ if test -z "$potlib"; then
+ $ECHO "*** with $libname but no candidates were found. (...for file magic test)"
+ else
+ $ECHO "*** with $libname and none of the candidates passed a file format test"
+ $ECHO "*** using a file magic. Last file checked: $potlib"
+ fi
+ fi
+ ;;
+ *)
+ # Add a -L argument.
+ func_append newdeplibs " $a_deplib"
+ ;;
+ esac
+ done # Gone through all deplibs.
+ ;;
+ match_pattern*)
+ set dummy $deplibs_check_method; shift
+ match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+ for a_deplib in $deplibs; do
+ case $a_deplib in
+ -l*)
+ func_stripname -l '' "$a_deplib"
+ name=$func_stripname_result
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+ case " $predeps $postdeps " in
+ *" $a_deplib "*)
+ func_append newdeplibs " $a_deplib"
+ a_deplib=
+ ;;
+ esac
+ fi
+ if test -n "$a_deplib"; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+ potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
+ for potent_lib in $potential_libs; do
+ potlib=$potent_lib # see symlink-check above in file_magic test
+ if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \
+ $EGREP "$match_pattern_regex" > /dev/null; then
+ func_append newdeplibs " $a_deplib"
+ a_deplib=
+ break 2
+ fi
+ done
+ done
+ fi
+ if test -n "$a_deplib"; then
+ droppeddeps=yes
+ echo
+ $ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+ echo "*** I have the capability to make that library automatically link in when"
+ echo "*** you link to this library. But I can only do this if you have a"
+ echo "*** shared version of the library, which you do not appear to have"
+ echo "*** because I did check the linker path looking for a file starting"
+ if test -z "$potlib"; then
+ $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)"
+ else
+ $ECHO "*** with $libname and none of the candidates passed a file format test"
+ $ECHO "*** using a regex pattern. Last file checked: $potlib"
+ fi
+ fi
+ ;;
+ *)
+ # Add a -L argument.
+ func_append newdeplibs " $a_deplib"
+ ;;
+ esac
+ done # Gone through all deplibs.
+ ;;
+ none | unknown | *)
+ newdeplibs=
+ tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'`
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+ for i in $predeps $postdeps; do
+ # can't use Xsed below, because $i might contain '/'
+ tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s|$i||"`
+ done
+ fi
+ case $tmp_deplibs in
+ *[!\ \ ]*)
+ echo
+ if test none = "$deplibs_check_method"; then
+ echo "*** Warning: inter-library dependencies are not supported in this platform."
+ else
+ echo "*** Warning: inter-library dependencies are not known to be supported."
+ fi
+ echo "*** All declared inter-library dependencies are being dropped."
+ droppeddeps=yes
+ ;;
+ esac
+ ;;
+ esac
+ versuffix=$versuffix_save
+ major=$major_save
+ release=$release_save
+ libname=$libname_save
+ name=$name_save
+
+ case $host in
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # On Rhapsody replace the C library with the System framework
+ newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'`
+ ;;
+ esac
+
+ if test yes = "$droppeddeps"; then
+ if test yes = "$module"; then
+ echo
+ echo "*** Warning: libtool could not satisfy all declared inter-library"
+ $ECHO "*** dependencies of module $libname. Therefore, libtool will create"
+ echo "*** a static module, that should work as long as the dlopening"
+ echo "*** application is linked with the -dlopen flag."
+ if test -z "$global_symbol_pipe"; then
+ echo
+ echo "*** However, this would only work if libtool was able to extract symbol"
+ echo "*** lists from a program, using 'nm' or equivalent, but libtool could"
+ echo "*** not find such a program. So, this module is probably useless."
+ echo "*** 'nm' from GNU binutils and a full rebuild may help."
+ fi
+ if test no = "$build_old_libs"; then
+ oldlibs=$output_objdir/$libname.$libext
+ build_libtool_libs=module
+ build_old_libs=yes
+ else
+ build_libtool_libs=no
+ fi
+ else
+ echo "*** The inter-library dependencies that have been dropped here will be"
+ echo "*** automatically added whenever a program is linked with this library"
+ echo "*** or is declared to -dlopen it."
+
+ if test no = "$allow_undefined"; then
+ echo
+ echo "*** Since this library must not contain undefined symbols,"
+ echo "*** because either the platform does not support them or"
+ echo "*** it was explicitly requested with -no-undefined,"
+ echo "*** libtool will only create a static version of it."
+ if test no = "$build_old_libs"; then
+ oldlibs=$output_objdir/$libname.$libext
+ build_libtool_libs=module
+ build_old_libs=yes
+ else
+ build_libtool_libs=no
+ fi
+ fi
+ fi
+ fi
+ # Done checking deplibs!
+ deplibs=$newdeplibs
+ fi
+ # Time to change all our "foo.ltframework" stuff back to "-framework foo"
+ case $host in
+ *-*-darwin*)
+ newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ ;;
+ esac
+
+ # move library search paths that coincide with paths to not yet
+ # installed libraries to the beginning of the library search list
+ new_libs=
+ for path in $notinst_path; do
+ case " $new_libs " in
+ *" -L$path/$objdir "*) ;;
+ *)
+ case " $deplibs " in
+ *" -L$path/$objdir "*)
+ func_append new_libs " -L$path/$objdir" ;;
+ esac
+ ;;
+ esac
+ done
+ for deplib in $deplibs; do
+ case $deplib in
+ -L*)
+ case " $new_libs " in
+ *" $deplib "*) ;;
+ *) func_append new_libs " $deplib" ;;
+ esac
+ ;;
+ *) func_append new_libs " $deplib" ;;
+ esac
+ done
+ deplibs=$new_libs
+
+ # All the library-specific variables (install_libdir is set above).
+ library_names=
+ old_library=
+ dlname=
+
+ # Test again, we may have decided not to build it any more
+ if test yes = "$build_libtool_libs"; then
+ # Remove $wl instances when linking with ld.
+ # FIXME: should test the right _cmds variable.
+ case $archive_cmds in
+ *\$LD\ *) wl= ;;
+ esac
+ if test yes = "$hardcode_into_libs"; then
+ # Hardcode the library paths
+ hardcode_libdirs=
+ dep_rpath=
+ rpath=$finalize_rpath
+ test relink = "$opt_mode" || rpath=$compile_rpath$rpath
+ for libdir in $rpath; do
+ if test -n "$hardcode_libdir_flag_spec"; then
+ if test -n "$hardcode_libdir_separator"; then
+ func_replace_sysroot "$libdir"
+ libdir=$func_replace_sysroot_result
+ if test -z "$hardcode_libdirs"; then
+ hardcode_libdirs=$libdir
+ else
+ # Just accumulate the unique libdirs.
+ case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+ *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+ ;;
+ *)
+ func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ func_append dep_rpath " $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$perm_rpath " in
+ *" $libdir "*) ;;
+ *) func_append perm_rpath " $libdir" ;;
+ esac
+ fi
+ done
+ # Substitute the hardcoded libdirs into the rpath.
+ if test -n "$hardcode_libdir_separator" &&
+ test -n "$hardcode_libdirs"; then
+ libdir=$hardcode_libdirs
+ eval "dep_rpath=\"$hardcode_libdir_flag_spec\""
+ fi
+ if test -n "$runpath_var" && test -n "$perm_rpath"; then
+ # We should set the runpath_var.
+ rpath=
+ for dir in $perm_rpath; do
+ func_append rpath "$dir:"
+ done
+ eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var"
+ fi
+ test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs"
+ fi
+
+ shlibpath=$finalize_shlibpath
+ test relink = "$opt_mode" || shlibpath=$compile_shlibpath$shlibpath
+ if test -n "$shlibpath"; then
+ eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var"
+ fi
+
+ # Get the real and link names of the library.
+ eval shared_ext=\"$shrext_cmds\"
+ eval library_names=\"$library_names_spec\"
+ set dummy $library_names
+ shift
+ realname=$1
+ shift
+
+ if test -n "$soname_spec"; then
+ eval soname=\"$soname_spec\"
+ else
+ soname=$realname
+ fi
+ if test -z "$dlname"; then
+ dlname=$soname
+ fi
+
+ lib=$output_objdir/$realname
+ linknames=
+ for link
+ do
+ func_append linknames " $link"
+ done
+
+ # Use standard objects if they are pic
+ test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP`
+ test "X$libobjs" = "X " && libobjs=
+
+ delfiles=
+ if test -n "$export_symbols" && test -n "$include_expsyms"; then
+ $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp"
+ export_symbols=$output_objdir/$libname.uexp
+ func_append delfiles " $export_symbols"
+ fi
+
+ orig_export_symbols=
+ case $host_os in
+ cygwin* | mingw* | cegcc*)
+ if test -n "$export_symbols" && test -z "$export_symbols_regex"; then
+ # exporting using user supplied symfile
+ func_dll_def_p "$export_symbols" || {
+ # and it's NOT already a .def file. Must figure out
+ # which of the given symbols are data symbols and tag
+ # them as such. So, trigger use of export_symbols_cmds.
+ # export_symbols gets reassigned inside the "prepare
+ # the list of exported symbols" if statement, so the
+ # include_expsyms logic still works.
+ orig_export_symbols=$export_symbols
+ export_symbols=
+ always_export_symbols=yes
+ }
+ fi
+ ;;
+ esac
+
+ # Prepare the list of exported symbols
+ if test -z "$export_symbols"; then
+ if test yes = "$always_export_symbols" || test -n "$export_symbols_regex"; then
+ func_verbose "generating symbol list for '$libname.la'"
+ export_symbols=$output_objdir/$libname.exp
+ $opt_dry_run || $RM $export_symbols
+ cmds=$export_symbols_cmds
+ save_ifs=$IFS; IFS='~'
+ for cmd1 in $cmds; do
+ IFS=$save_ifs
+ # Take the normal branch if the nm_file_list_spec branch
+ # doesn't work or if tool conversion is not needed.
+ case $nm_file_list_spec~$to_tool_file_cmd in
+ *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*)
+ try_normal_branch=yes
+ eval cmd=\"$cmd1\"
+ func_len " $cmd"
+ len=$func_len_result
+ ;;
+ *)
+ try_normal_branch=no
+ ;;
+ esac
+ if test yes = "$try_normal_branch" \
+ && { test "$len" -lt "$max_cmd_len" \
+ || test "$max_cmd_len" -le -1; }
+ then
+ func_show_eval "$cmd" 'exit $?'
+ skipped_export=false
+ elif test -n "$nm_file_list_spec"; then
+ func_basename "$output"
+ output_la=$func_basename_result
+ save_libobjs=$libobjs
+ save_output=$output
+ output=$output_objdir/$output_la.nm
+ func_to_tool_file "$output"
+ libobjs=$nm_file_list_spec$func_to_tool_file_result
+ func_append delfiles " $output"
+ func_verbose "creating $NM input file list: $output"
+ for obj in $save_libobjs; do
+ func_to_tool_file "$obj"
+ $ECHO "$func_to_tool_file_result"
+ done > "$output"
+ eval cmd=\"$cmd1\"
+ func_show_eval "$cmd" 'exit $?'
+ output=$save_output
+ libobjs=$save_libobjs
+ skipped_export=false
+ else
+ # The command line is too long to execute in one step.
+ func_verbose "using reloadable object file for export list..."
+ skipped_export=:
+ # Break out early, otherwise skipped_export may be
+ # set to false by a later but shorter cmd.
+ break
+ fi
+ done
+ IFS=$save_ifs
+ if test -n "$export_symbols_regex" && test : != "$skipped_export"; then
+ func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+ func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+ fi
+ fi
+ fi
+
+ if test -n "$export_symbols" && test -n "$include_expsyms"; then
+ tmp_export_symbols=$export_symbols
+ test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols
+ $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"'
+ fi
+
+ if test : != "$skipped_export" && test -n "$orig_export_symbols"; then
+ # The given exports_symbols file has to be filtered, so filter it.
+ func_verbose "filter symbol list for '$libname.la' to tag DATA exports"
+ # FIXME: $output_objdir/$libname.filter potentially contains lots of
+ # 's' commands, which not all seds can handle. GNU sed should be fine
+ # though. Also, the filter scales superlinearly with the number of
+ # global variables. join(1) would be nice here, but unfortunately
+ # isn't a blessed tool.
+ $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+ func_append delfiles " $export_symbols $output_objdir/$libname.filter"
+ export_symbols=$output_objdir/$libname.def
+ $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+ fi
+
+ tmp_deplibs=
+ for test_deplib in $deplibs; do
+ case " $convenience " in
+ *" $test_deplib "*) ;;
+ *)
+ func_append tmp_deplibs " $test_deplib"
+ ;;
+ esac
+ done
+ deplibs=$tmp_deplibs
+
+ if test -n "$convenience"; then
+ if test -n "$whole_archive_flag_spec" &&
+ test yes = "$compiler_needs_object" &&
+ test -z "$libobjs"; then
+ # extract the archives, so we have objects to list.
+ # TODO: could optimize this to just extract one archive.
+ whole_archive_flag_spec=
+ fi
+ if test -n "$whole_archive_flag_spec"; then
+ save_libobjs=$libobjs
+ eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+ test "X$libobjs" = "X " && libobjs=
+ else
+ gentop=$output_objdir/${outputname}x
+ func_append generated " $gentop"
+
+ func_extract_archives $gentop $convenience
+ func_append libobjs " $func_extract_archives_result"
+ test "X$libobjs" = "X " && libobjs=
+ fi
+ fi
+
+ if test yes = "$thread_safe" && test -n "$thread_safe_flag_spec"; then
+ eval flag=\"$thread_safe_flag_spec\"
+ func_append linker_flags " $flag"
+ fi
+
+ # Make a backup of the uninstalled library when relinking
+ if test relink = "$opt_mode"; then
+ $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $?
+ fi
+
+ # Do each of the archive commands.
+ if test yes = "$module" && test -n "$module_cmds"; then
+ if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+ eval test_cmds=\"$module_expsym_cmds\"
+ cmds=$module_expsym_cmds
+ else
+ eval test_cmds=\"$module_cmds\"
+ cmds=$module_cmds
+ fi
+ else
+ if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+ eval test_cmds=\"$archive_expsym_cmds\"
+ cmds=$archive_expsym_cmds
+ else
+ eval test_cmds=\"$archive_cmds\"
+ cmds=$archive_cmds
+ fi
+ fi
+
+ if test : != "$skipped_export" &&
+ func_len " $test_cmds" &&
+ len=$func_len_result &&
+ test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+ :
+ else
+ # The command line is too long to link in one step, link piecewise
+ # or, if using GNU ld and skipped_export is not :, use a linker
+ # script.
+
+ # Save the value of $output and $libobjs because we want to
+ # use them later. If we have whole_archive_flag_spec, we
+ # want to use save_libobjs as it was before
+ # whole_archive_flag_spec was expanded, because we can't
+ # assume the linker understands whole_archive_flag_spec.
+ # This may have to be revisited, in case too many
+ # convenience libraries get linked in and end up exceeding
+ # the spec.
+ if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then
+ save_libobjs=$libobjs
+ fi
+ save_output=$output
+ func_basename "$output"
+ output_la=$func_basename_result
+
+ # Clear the reloadable object creation command queue and
+ # initialize k to one.
+ test_cmds=
+ concat_cmds=
+ objlist=
+ last_robj=
+ k=1
+
+ if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then
+ output=$output_objdir/$output_la.lnkscript
+ func_verbose "creating GNU ld script: $output"
+ echo 'INPUT (' > $output
+ for obj in $save_libobjs
+ do
+ func_to_tool_file "$obj"
+ $ECHO "$func_to_tool_file_result" >> $output
+ done
+ echo ')' >> $output
+ func_append delfiles " $output"
+ func_to_tool_file "$output"
+ output=$func_to_tool_file_result
+ elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then
+ output=$output_objdir/$output_la.lnk
+ func_verbose "creating linker input file list: $output"
+ : > $output
+ set x $save_libobjs
+ shift
+ firstobj=
+ if test yes = "$compiler_needs_object"; then
+ firstobj="$1 "
+ shift
+ fi
+ for obj
+ do
+ func_to_tool_file "$obj"
+ $ECHO "$func_to_tool_file_result" >> $output
+ done
+ func_append delfiles " $output"
+ func_to_tool_file "$output"
+ output=$firstobj\"$file_list_spec$func_to_tool_file_result\"
+ else
+ if test -n "$save_libobjs"; then
+ func_verbose "creating reloadable object files..."
+ output=$output_objdir/$output_la-$k.$objext
+ eval test_cmds=\"$reload_cmds\"
+ func_len " $test_cmds"
+ len0=$func_len_result
+ len=$len0
+
+ # Loop over the list of objects to be linked.
+ for obj in $save_libobjs
+ do
+ func_len " $obj"
+ func_arith $len + $func_len_result
+ len=$func_arith_result
+ if test -z "$objlist" ||
+ test "$len" -lt "$max_cmd_len"; then
+ func_append objlist " $obj"
+ else
+ # The command $test_cmds is almost too long, add a
+ # command to the queue.
+ if test 1 -eq "$k"; then
+ # The first file doesn't have a previous command to add.
+ reload_objs=$objlist
+ eval concat_cmds=\"$reload_cmds\"
+ else
+ # All subsequent reloadable object files will link in
+ # the last one created.
+ reload_objs="$objlist $last_robj"
+ eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\"
+ fi
+ last_robj=$output_objdir/$output_la-$k.$objext
+ func_arith $k + 1
+ k=$func_arith_result
+ output=$output_objdir/$output_la-$k.$objext
+ objlist=" $obj"
+ func_len " $last_robj"
+ func_arith $len0 + $func_len_result
+ len=$func_arith_result
+ fi
+ done
+ # Handle the remaining objects by creating one last
+ # reloadable object file. All subsequent reloadable object
+ # files will link in the last one created.
+ test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+ reload_objs="$objlist $last_robj"
+ eval concat_cmds=\"\$concat_cmds$reload_cmds\"
+ if test -n "$last_robj"; then
+ eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\"
+ fi
+ func_append delfiles " $output"
+
+ else
+ output=
+ fi
+
+ ${skipped_export-false} && {
+ func_verbose "generating symbol list for '$libname.la'"
+ export_symbols=$output_objdir/$libname.exp
+ $opt_dry_run || $RM $export_symbols
+ libobjs=$output
+ # Append the command to create the export file.
+ test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+ eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\"
+ if test -n "$last_robj"; then
+ eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\"
+ fi
+ }
+
+ test -n "$save_libobjs" &&
+ func_verbose "creating a temporary reloadable object file: $output"
+
+ # Loop through the commands generated above and execute them.
+ save_ifs=$IFS; IFS='~'
+ for cmd in $concat_cmds; do
+ IFS=$save_ifs
+ $opt_quiet || {
+ func_quote_for_expand "$cmd"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+ $opt_dry_run || eval "$cmd" || {
+ lt_exit=$?
+
+ # Restore the uninstalled library and exit
+ if test relink = "$opt_mode"; then
+ ( cd "$output_objdir" && \
+ $RM "${realname}T" && \
+ $MV "${realname}U" "$realname" )
+ fi
+
+ exit $lt_exit
+ }
+ done
+ IFS=$save_ifs
+
+ if test -n "$export_symbols_regex" && ${skipped_export-false}; then
+ func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+ func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+ fi
+ fi
+
+ ${skipped_export-false} && {
+ if test -n "$export_symbols" && test -n "$include_expsyms"; then
+ tmp_export_symbols=$export_symbols
+ test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols
+ $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"'
+ fi
+
+ if test -n "$orig_export_symbols"; then
+ # The given exports_symbols file has to be filtered, so filter it.
+ func_verbose "filter symbol list for '$libname.la' to tag DATA exports"
+ # FIXME: $output_objdir/$libname.filter potentially contains lots of
+ # 's' commands, which not all seds can handle. GNU sed should be fine
+ # though. Also, the filter scales superlinearly with the number of
+ # global variables. join(1) would be nice here, but unfortunately
+ # isn't a blessed tool.
+ $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+ func_append delfiles " $export_symbols $output_objdir/$libname.filter"
+ export_symbols=$output_objdir/$libname.def
+ $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+ fi
+ }
+
+ libobjs=$output
+ # Restore the value of output.
+ output=$save_output
+
+ if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then
+ eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+ test "X$libobjs" = "X " && libobjs=
+ fi
+ # Expand the library linking commands again to reset the
+ # value of $libobjs for piecewise linking.
+
+ # Do each of the archive commands.
+ if test yes = "$module" && test -n "$module_cmds"; then
+ if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+ cmds=$module_expsym_cmds
+ else
+ cmds=$module_cmds
+ fi
+ else
+ if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+ cmds=$archive_expsym_cmds
+ else
+ cmds=$archive_cmds
+ fi
+ fi
+ fi
+
+ if test -n "$delfiles"; then
+ # Append the command to remove temporary files to $cmds.
+ eval cmds=\"\$cmds~\$RM $delfiles\"
+ fi
+
+ # Add any objects from preloaded convenience libraries
+ if test -n "$dlprefiles"; then
+ gentop=$output_objdir/${outputname}x
+ func_append generated " $gentop"
+
+ func_extract_archives $gentop $dlprefiles
+ func_append libobjs " $func_extract_archives_result"
+ test "X$libobjs" = "X " && libobjs=
+ fi
+
+ save_ifs=$IFS; IFS='~'
+ for cmd in $cmds; do
+ IFS=$sp$nl
+ eval cmd=\"$cmd\"
+ IFS=$save_ifs
+ $opt_quiet || {
+ func_quote_for_expand "$cmd"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+ $opt_dry_run || eval "$cmd" || {
+ lt_exit=$?
+
+ # Restore the uninstalled library and exit
+ if test relink = "$opt_mode"; then
+ ( cd "$output_objdir" && \
+ $RM "${realname}T" && \
+ $MV "${realname}U" "$realname" )
+ fi
+
+ exit $lt_exit
+ }
+ done
+ IFS=$save_ifs
+
+ # Restore the uninstalled library and exit
+ if test relink = "$opt_mode"; then
+ $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $?
+
+ if test -n "$convenience"; then
+ if test -z "$whole_archive_flag_spec"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+ fi
+
+ exit $EXIT_SUCCESS
+ fi
+
+ # Create links to the real library.
+ for linkname in $linknames; do
+ if test "$realname" != "$linkname"; then
+ func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?'
+ fi
+ done
+
+ # If -module or -export-dynamic was specified, set the dlname.
+ if test yes = "$module" || test yes = "$export_dynamic"; then
+ # On all known operating systems, these are identical.
+ dlname=$soname
+ fi
+ fi
+ ;;
+
+ obj)
+ if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then
+ func_warning "'-dlopen' is ignored for objects"
+ fi
+
+ case " $deplibs" in
+ *\ -l* | *\ -L*)
+ func_warning "'-l' and '-L' are ignored for objects" ;;
+ esac
+
+ test -n "$rpath" && \
+ func_warning "'-rpath' is ignored for objects"
+
+ test -n "$xrpath" && \
+ func_warning "'-R' is ignored for objects"
+
+ test -n "$vinfo" && \
+ func_warning "'-version-info' is ignored for objects"
+
+ test -n "$release" && \
+ func_warning "'-release' is ignored for objects"
+
+ case $output in
+ *.lo)
+ test -n "$objs$old_deplibs" && \
+ func_fatal_error "cannot build library object '$output' from non-libtool objects"
+
+ libobj=$output
+ func_lo2o "$libobj"
+ obj=$func_lo2o_result
+ ;;
+ *)
+ libobj=
+ obj=$output
+ ;;
+ esac
+
+ # Delete the old objects.
+ $opt_dry_run || $RM $obj $libobj
+
+ # Objects from convenience libraries. This assumes
+ # single-version convenience libraries. Whenever we create
+ # different ones for PIC/non-PIC, this we'll have to duplicate
+ # the extraction.
+ reload_conv_objs=
+ gentop=
+ # if reload_cmds runs $LD directly, get rid of -Wl from
+ # whole_archive_flag_spec and hope we can get by with turning comma
+ # into space.
+ case $reload_cmds in
+ *\$LD[\ \$]*) wl= ;;
+ esac
+ if test -n "$convenience"; then
+ if test -n "$whole_archive_flag_spec"; then
+ eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\"
+ test -n "$wl" || tmp_whole_archive_flags=`$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'`
+ reload_conv_objs=$reload_objs\ $tmp_whole_archive_flags
+ else
+ gentop=$output_objdir/${obj}x
+ func_append generated " $gentop"
+
+ func_extract_archives $gentop $convenience
+ reload_conv_objs="$reload_objs $func_extract_archives_result"
+ fi
+ fi
+
+ # If we're not building shared, we need to use non_pic_objs
+ test yes = "$build_libtool_libs" || libobjs=$non_pic_objects
+
+ # Create the old-style object.
+ reload_objs=$objs$old_deplibs' '`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; /\.lib$/d; $lo2o" | $NL2SP`' '$reload_conv_objs
+
+ output=$obj
+ func_execute_cmds "$reload_cmds" 'exit $?'
+
+ # Exit if we aren't doing a library object file.
+ if test -z "$libobj"; then
+ if test -n "$gentop"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+
+ exit $EXIT_SUCCESS
+ fi
+
+ test yes = "$build_libtool_libs" || {
+ if test -n "$gentop"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+
+ # Create an invalid libtool object if no PIC, so that we don't
+ # accidentally link it into a program.
+ # $show "echo timestamp > $libobj"
+ # $opt_dry_run || eval "echo timestamp > $libobj" || exit $?
+ exit $EXIT_SUCCESS
+ }
+
+ if test -n "$pic_flag" || test default != "$pic_mode"; then
+ # Only do commands if we really have different PIC objects.
+ reload_objs="$libobjs $reload_conv_objs"
+ output=$libobj
+ func_execute_cmds "$reload_cmds" 'exit $?'
+ fi
+
+ if test -n "$gentop"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+
+ exit $EXIT_SUCCESS
+ ;;
+
+ prog)
+ case $host in
+ *cygwin*) func_stripname '' '.exe' "$output"
+ output=$func_stripname_result.exe;;
+ esac
+ test -n "$vinfo" && \
+ func_warning "'-version-info' is ignored for programs"
+
+ test -n "$release" && \
+ func_warning "'-release' is ignored for programs"
+
+ $preload \
+ && test unknown,unknown,unknown = "$dlopen_support,$dlopen_self,$dlopen_self_static" \
+ && func_warning "'LT_INIT([dlopen])' not used. Assuming no dlopen support."
+
+ case $host in
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # On Rhapsody replace the C library is the System framework
+ compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'`
+ finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'`
+ ;;
+ esac
+
+ case $host in
+ *-*-darwin*)
+ # Don't allow lazy linking, it breaks C++ global constructors
+ # But is supposedly fixed on 10.4 or later (yay!).
+ if test CXX = "$tagname"; then
+ case ${MACOSX_DEPLOYMENT_TARGET-10.0} in
+ 10.[0123])
+ func_append compile_command " $wl-bind_at_load"
+ func_append finalize_command " $wl-bind_at_load"
+ ;;
+ esac
+ fi
+ # Time to change all our "foo.ltframework" stuff back to "-framework foo"
+ compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ ;;
+ esac
+
+
+ # move library search paths that coincide with paths to not yet
+ # installed libraries to the beginning of the library search list
+ new_libs=
+ for path in $notinst_path; do
+ case " $new_libs " in
+ *" -L$path/$objdir "*) ;;
+ *)
+ case " $compile_deplibs " in
+ *" -L$path/$objdir "*)
+ func_append new_libs " -L$path/$objdir" ;;
+ esac
+ ;;
+ esac
+ done
+ for deplib in $compile_deplibs; do
+ case $deplib in
+ -L*)
+ case " $new_libs " in
+ *" $deplib "*) ;;
+ *) func_append new_libs " $deplib" ;;
+ esac
+ ;;
+ *) func_append new_libs " $deplib" ;;
+ esac
+ done
+ compile_deplibs=$new_libs
+
+
+ func_append compile_command " $compile_deplibs"
+ func_append finalize_command " $finalize_deplibs"
+
+ if test -n "$rpath$xrpath"; then
+ # If the user specified any rpath flags, then add them.
+ for libdir in $rpath $xrpath; do
+ # This is the magic to use -rpath.
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) func_append finalize_rpath " $libdir" ;;
+ esac
+ done
+ fi
+
+ # Now hardcode the library paths
+ rpath=
+ hardcode_libdirs=
+ for libdir in $compile_rpath $finalize_rpath; do
+ if test -n "$hardcode_libdir_flag_spec"; then
+ if test -n "$hardcode_libdir_separator"; then
+ if test -z "$hardcode_libdirs"; then
+ hardcode_libdirs=$libdir
+ else
+ # Just accumulate the unique libdirs.
+ case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+ *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+ ;;
+ *)
+ func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ func_append rpath " $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$perm_rpath " in
+ *" $libdir "*) ;;
+ *) func_append perm_rpath " $libdir" ;;
+ esac
+ fi
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ testbindir=`$ECHO "$libdir" | $SED -e 's*/lib$*/bin*'`
+ case :$dllsearchpath: in
+ *":$libdir:"*) ;;
+ ::) dllsearchpath=$libdir;;
+ *) func_append dllsearchpath ":$libdir";;
+ esac
+ case :$dllsearchpath: in
+ *":$testbindir:"*) ;;
+ ::) dllsearchpath=$testbindir;;
+ *) func_append dllsearchpath ":$testbindir";;
+ esac
+ ;;
+ esac
+ done
+ # Substitute the hardcoded libdirs into the rpath.
+ if test -n "$hardcode_libdir_separator" &&
+ test -n "$hardcode_libdirs"; then
+ libdir=$hardcode_libdirs
+ eval rpath=\" $hardcode_libdir_flag_spec\"
+ fi
+ compile_rpath=$rpath
+
+ rpath=
+ hardcode_libdirs=
+ for libdir in $finalize_rpath; do
+ if test -n "$hardcode_libdir_flag_spec"; then
+ if test -n "$hardcode_libdir_separator"; then
+ if test -z "$hardcode_libdirs"; then
+ hardcode_libdirs=$libdir
+ else
+ # Just accumulate the unique libdirs.
+ case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+ *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+ ;;
+ *)
+ func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ func_append rpath " $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$finalize_perm_rpath " in
+ *" $libdir "*) ;;
+ *) func_append finalize_perm_rpath " $libdir" ;;
+ esac
+ fi
+ done
+ # Substitute the hardcoded libdirs into the rpath.
+ if test -n "$hardcode_libdir_separator" &&
+ test -n "$hardcode_libdirs"; then
+ libdir=$hardcode_libdirs
+ eval rpath=\" $hardcode_libdir_flag_spec\"
+ fi
+ finalize_rpath=$rpath
+
+ if test -n "$libobjs" && test yes = "$build_old_libs"; then
+ # Transform all the library objects into standard objects.
+ compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP`
+ finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP`
+ fi
+
+ func_generate_dlsyms "$outputname" "@PROGRAM@" false
+
+ # template prelinking step
+ if test -n "$prelink_cmds"; then
+ func_execute_cmds "$prelink_cmds" 'exit $?'
+ fi
+
+ wrappers_required=:
+ case $host in
+ *cegcc* | *mingw32ce*)
+ # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway.
+ wrappers_required=false
+ ;;
+ *cygwin* | *mingw* )
+ test yes = "$build_libtool_libs" || wrappers_required=false
+ ;;
+ *)
+ if test no = "$need_relink" || test yes != "$build_libtool_libs"; then
+ wrappers_required=false
+ fi
+ ;;
+ esac
+ $wrappers_required || {
+ # Replace the output file specification.
+ compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'`
+ link_command=$compile_command$compile_rpath
+
+ # We have no uninstalled library dependencies, so finalize right now.
+ exit_status=0
+ func_show_eval "$link_command" 'exit_status=$?'
+
+ if test -n "$postlink_cmds"; then
+ func_to_tool_file "$output"
+ postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+ func_execute_cmds "$postlink_cmds" 'exit $?'
+ fi
+
+ # Delete the generated files.
+ if test -f "$output_objdir/${outputname}S.$objext"; then
+ func_show_eval '$RM "$output_objdir/${outputname}S.$objext"'
+ fi
+
+ exit $exit_status
+ }
+
+ if test -n "$compile_shlibpath$finalize_shlibpath"; then
+ compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command"
+ fi
+ if test -n "$finalize_shlibpath"; then
+ finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command"
+ fi
+
+ compile_var=
+ finalize_var=
+ if test -n "$runpath_var"; then
+ if test -n "$perm_rpath"; then
+ # We should set the runpath_var.
+ rpath=
+ for dir in $perm_rpath; do
+ func_append rpath "$dir:"
+ done
+ compile_var="$runpath_var=\"$rpath\$$runpath_var\" "
+ fi
+ if test -n "$finalize_perm_rpath"; then
+ # We should set the runpath_var.
+ rpath=
+ for dir in $finalize_perm_rpath; do
+ func_append rpath "$dir:"
+ done
+ finalize_var="$runpath_var=\"$rpath\$$runpath_var\" "
+ fi
+ fi
+
+ if test yes = "$no_install"; then
+ # We don't need to create a wrapper script.
+ link_command=$compile_var$compile_command$compile_rpath
+ # Replace the output file specification.
+ link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'`
+ # Delete the old output file.
+ $opt_dry_run || $RM $output
+ # Link the executable and exit
+ func_show_eval "$link_command" 'exit $?'
+
+ if test -n "$postlink_cmds"; then
+ func_to_tool_file "$output"
+ postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+ func_execute_cmds "$postlink_cmds" 'exit $?'
+ fi
+
+ exit $EXIT_SUCCESS
+ fi
+
+ case $hardcode_action,$fast_install in
+ relink,*)
+ # Fast installation is not supported
+ link_command=$compile_var$compile_command$compile_rpath
+ relink_command=$finalize_var$finalize_command$finalize_rpath
+
+ func_warning "this platform does not like uninstalled shared libraries"
+ func_warning "'$output' will be relinked during installation"
+ ;;
+ *,yes)
+ link_command=$finalize_var$compile_command$finalize_rpath
+ relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'`
+ ;;
+ *,no)
+ link_command=$compile_var$compile_command$compile_rpath
+ relink_command=$finalize_var$finalize_command$finalize_rpath
+ ;;
+ *,needless)
+ link_command=$finalize_var$compile_command$finalize_rpath
+ relink_command=
+ ;;
+ esac
+
+ # Replace the output file specification.
+ link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'`
+
+ # Delete the old output files.
+ $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname
+
+ func_show_eval "$link_command" 'exit $?'
+
+ if test -n "$postlink_cmds"; then
+ func_to_tool_file "$output_objdir/$outputname"
+ postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+ func_execute_cmds "$postlink_cmds" 'exit $?'
+ fi
+
+ # Now create the wrapper script.
+ func_verbose "creating $output"
+
+ # Quote the relink command for shipping.
+ if test -n "$relink_command"; then
+ # Preserve any variables that may affect compiler behavior
+ for var in $variables_saved_for_relink; do
+ if eval test -z \"\${$var+set}\"; then
+ relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+ elif eval var_value=\$$var; test -z "$var_value"; then
+ relink_command="$var=; export $var; $relink_command"
+ else
+ func_quote_for_eval "$var_value"
+ relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+ fi
+ done
+ relink_command="(cd `pwd`; $relink_command)"
+ relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"`
+ fi
+
+ # Only actually do things if not in dry run mode.
+ $opt_dry_run || {
+ # win32 will think the script is a binary if it has
+ # a .exe suffix, so we strip it off here.
+ case $output in
+ *.exe) func_stripname '' '.exe' "$output"
+ output=$func_stripname_result ;;
+ esac
+ # test for cygwin because mv fails w/o .exe extensions
+ case $host in
+ *cygwin*)
+ exeext=.exe
+ func_stripname '' '.exe' "$outputname"
+ outputname=$func_stripname_result ;;
+ *) exeext= ;;
+ esac
+ case $host in
+ *cygwin* | *mingw* )
+ func_dirname_and_basename "$output" "" "."
+ output_name=$func_basename_result
+ output_path=$func_dirname_result
+ cwrappersource=$output_path/$objdir/lt-$output_name.c
+ cwrapper=$output_path/$output_name.exe
+ $RM $cwrappersource $cwrapper
+ trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15
+
+ func_emit_cwrapperexe_src > $cwrappersource
+
+ # The wrapper executable is built using the $host compiler,
+ # because it contains $host paths and files. If cross-
+ # compiling, it, like the target executable, must be
+ # executed on the $host or under an emulation environment.
+ $opt_dry_run || {
+ $LTCC $LTCFLAGS -o $cwrapper $cwrappersource
+ $STRIP $cwrapper
+ }
+
+ # Now, create the wrapper script for func_source use:
+ func_ltwrapper_scriptname $cwrapper
+ $RM $func_ltwrapper_scriptname_result
+ trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15
+ $opt_dry_run || {
+ # note: this script will not be executed, so do not chmod.
+ if test "x$build" = "x$host"; then
+ $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result
+ else
+ func_emit_wrapper no > $func_ltwrapper_scriptname_result
+ fi
+ }
+ ;;
+ * )
+ $RM $output
+ trap "$RM $output; exit $EXIT_FAILURE" 1 2 15
+
+ func_emit_wrapper no > $output
+ chmod +x $output
+ ;;
+ esac
+ }
+ exit $EXIT_SUCCESS
+ ;;
+ esac
+
+ # See if we need to build an old-fashioned archive.
+ for oldlib in $oldlibs; do
+
+ case $build_libtool_libs in
+ convenience)
+ oldobjs="$libobjs_save $symfileobj"
+ addlibs=$convenience
+ build_libtool_libs=no
+ ;;
+ module)
+ oldobjs=$libobjs_save
+ addlibs=$old_convenience
+ build_libtool_libs=no
+ ;;
+ *)
+ oldobjs="$old_deplibs $non_pic_objects"
+ $preload && test -f "$symfileobj" \
+ && func_append oldobjs " $symfileobj"
+ addlibs=$old_convenience
+ ;;
+ esac
+
+ if test -n "$addlibs"; then
+ gentop=$output_objdir/${outputname}x
+ func_append generated " $gentop"
+
+ func_extract_archives $gentop $addlibs
+ func_append oldobjs " $func_extract_archives_result"
+ fi
+
+ # Do each command in the archive commands.
+ if test -n "$old_archive_from_new_cmds" && test yes = "$build_libtool_libs"; then
+ cmds=$old_archive_from_new_cmds
+ else
+
+ # Add any objects from preloaded convenience libraries
+ if test -n "$dlprefiles"; then
+ gentop=$output_objdir/${outputname}x
+ func_append generated " $gentop"
+
+ func_extract_archives $gentop $dlprefiles
+ func_append oldobjs " $func_extract_archives_result"
+ fi
+
+ # POSIX demands no paths to be encoded in archives. We have
+ # to avoid creating archives with duplicate basenames if we
+ # might have to extract them afterwards, e.g., when creating a
+ # static archive out of a convenience library, or when linking
+ # the entirety of a libtool archive into another (currently
+ # not supported by libtool).
+ if (for obj in $oldobjs
+ do
+ func_basename "$obj"
+ $ECHO "$func_basename_result"
+ done | sort | sort -uc >/dev/null 2>&1); then
+ :
+ else
+ echo "copying selected object files to avoid basename conflicts..."
+ gentop=$output_objdir/${outputname}x
+ func_append generated " $gentop"
+ func_mkdir_p "$gentop"
+ save_oldobjs=$oldobjs
+ oldobjs=
+ counter=1
+ for obj in $save_oldobjs
+ do
+ func_basename "$obj"
+ objbase=$func_basename_result
+ case " $oldobjs " in
+ " ") oldobjs=$obj ;;
+ *[\ /]"$objbase "*)
+ while :; do
+ # Make sure we don't pick an alternate name that also
+ # overlaps.
+ newobj=lt$counter-$objbase
+ func_arith $counter + 1
+ counter=$func_arith_result
+ case " $oldobjs " in
+ *[\ /]"$newobj "*) ;;
+ *) if test ! -f "$gentop/$newobj"; then break; fi ;;
+ esac
+ done
+ func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj"
+ func_append oldobjs " $gentop/$newobj"
+ ;;
+ *) func_append oldobjs " $obj" ;;
+ esac
+ done
+ fi
+ func_to_tool_file "$oldlib" func_convert_file_msys_to_w32
+ tool_oldlib=$func_to_tool_file_result
+ eval cmds=\"$old_archive_cmds\"
+
+ func_len " $cmds"
+ len=$func_len_result
+ if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+ cmds=$old_archive_cmds
+ elif test -n "$archiver_list_spec"; then
+ func_verbose "using command file archive linking..."
+ for obj in $oldobjs
+ do
+ func_to_tool_file "$obj"
+ $ECHO "$func_to_tool_file_result"
+ done > $output_objdir/$libname.libcmd
+ func_to_tool_file "$output_objdir/$libname.libcmd"
+ oldobjs=" $archiver_list_spec$func_to_tool_file_result"
+ cmds=$old_archive_cmds
+ else
+ # the command line is too long to link in one step, link in parts
+ func_verbose "using piecewise archive linking..."
+ save_RANLIB=$RANLIB
+ RANLIB=:
+ objlist=
+ concat_cmds=
+ save_oldobjs=$oldobjs
+ oldobjs=
+ # Is there a better way of finding the last object in the list?
+ for obj in $save_oldobjs
+ do
+ last_oldobj=$obj
+ done
+ eval test_cmds=\"$old_archive_cmds\"
+ func_len " $test_cmds"
+ len0=$func_len_result
+ len=$len0
+ for obj in $save_oldobjs
+ do
+ func_len " $obj"
+ func_arith $len + $func_len_result
+ len=$func_arith_result
+ func_append objlist " $obj"
+ if test "$len" -lt "$max_cmd_len"; then
+ :
+ else
+ # the above command should be used before it gets too long
+ oldobjs=$objlist
+ if test "$obj" = "$last_oldobj"; then
+ RANLIB=$save_RANLIB
+ fi
+ test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+ eval concat_cmds=\"\$concat_cmds$old_archive_cmds\"
+ objlist=
+ len=$len0
+ fi
+ done
+ RANLIB=$save_RANLIB
+ oldobjs=$objlist
+ if test -z "$oldobjs"; then
+ eval cmds=\"\$concat_cmds\"
+ else
+ eval cmds=\"\$concat_cmds~\$old_archive_cmds\"
+ fi
+ fi
+ fi
+ func_execute_cmds "$cmds" 'exit $?'
+ done
+
+ test -n "$generated" && \
+ func_show_eval "${RM}r$generated"
+
+ # Now create the libtool archive.
+ case $output in
+ *.la)
+ old_library=
+ test yes = "$build_old_libs" && old_library=$libname.$libext
+ func_verbose "creating $output"
+
+ # Preserve any variables that may affect compiler behavior
+ for var in $variables_saved_for_relink; do
+ if eval test -z \"\${$var+set}\"; then
+ relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+ elif eval var_value=\$$var; test -z "$var_value"; then
+ relink_command="$var=; export $var; $relink_command"
+ else
+ func_quote_for_eval "$var_value"
+ relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+ fi
+ done
+ # Quote the link command for shipping.
+ relink_command="(cd `pwd`; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)"
+ relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"`
+ if test yes = "$hardcode_automatic"; then
+ relink_command=
+ fi
+
+ # Only create the output if not a dry run.
+ $opt_dry_run || {
+ for installed in no yes; do
+ if test yes = "$installed"; then
+ if test -z "$install_libdir"; then
+ break
+ fi
+ output=$output_objdir/${outputname}i
+ # Replace all uninstalled libtool libraries with the installed ones
+ newdependency_libs=
+ for deplib in $dependency_libs; do
+ case $deplib in
+ *.la)
+ func_basename "$deplib"
+ name=$func_basename_result
+ func_resolve_sysroot "$deplib"
+ eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result`
+ test -z "$libdir" && \
+ func_fatal_error "'$deplib' is not a valid libtool archive"
+ func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name"
+ ;;
+ -L*)
+ func_stripname -L '' "$deplib"
+ func_replace_sysroot "$func_stripname_result"
+ func_append newdependency_libs " -L$func_replace_sysroot_result"
+ ;;
+ -R*)
+ func_stripname -R '' "$deplib"
+ func_replace_sysroot "$func_stripname_result"
+ func_append newdependency_libs " -R$func_replace_sysroot_result"
+ ;;
+ *) func_append newdependency_libs " $deplib" ;;
+ esac
+ done
+ dependency_libs=$newdependency_libs
+ newdlfiles=
+
+ for lib in $dlfiles; do
+ case $lib in
+ *.la)
+ func_basename "$lib"
+ name=$func_basename_result
+ eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+ test -z "$libdir" && \
+ func_fatal_error "'$lib' is not a valid libtool archive"
+ func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name"
+ ;;
+ *) func_append newdlfiles " $lib" ;;
+ esac
+ done
+ dlfiles=$newdlfiles
+ newdlprefiles=
+ for lib in $dlprefiles; do
+ case $lib in
+ *.la)
+ # Only pass preopened files to the pseudo-archive (for
+ # eventual linking with the app. that links it) if we
+ # didn't already link the preopened objects directly into
+ # the library:
+ func_basename "$lib"
+ name=$func_basename_result
+ eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+ test -z "$libdir" && \
+ func_fatal_error "'$lib' is not a valid libtool archive"
+ func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name"
+ ;;
+ esac
+ done
+ dlprefiles=$newdlprefiles
+ else
+ newdlfiles=
+ for lib in $dlfiles; do
+ case $lib in
+ [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;;
+ *) abs=`pwd`"/$lib" ;;
+ esac
+ func_append newdlfiles " $abs"
+ done
+ dlfiles=$newdlfiles
+ newdlprefiles=
+ for lib in $dlprefiles; do
+ case $lib in
+ [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;;
+ *) abs=`pwd`"/$lib" ;;
+ esac
+ func_append newdlprefiles " $abs"
+ done
+ dlprefiles=$newdlprefiles
+ fi
+ $RM $output
+ # place dlname in correct position for cygwin
+ # In fact, it would be nice if we could use this code for all target
+ # systems that can't hard-code library paths into their executables
+ # and that have no shared library path variable independent of PATH,
+ # but it turns out we can't easily determine that from inspecting
+ # libtool variables, so we have to hard-code the OSs to which it
+ # applies here; at the moment, that means platforms that use the PE
+ # object format with DLL files. See the long comment at the top of
+ # tests/bindir.at for full details.
+ tdlname=$dlname
+ case $host,$output,$installed,$module,$dlname in
+ *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll)
+ # If a -bindir argument was supplied, place the dll there.
+ if test -n "$bindir"; then
+ func_relative_path "$install_libdir" "$bindir"
+ tdlname=$func_relative_path_result/$dlname
+ else
+ # Otherwise fall back on heuristic.
+ tdlname=../bin/$dlname
+ fi
+ ;;
+ esac
+ $ECHO > $output "\
+# $outputname - a libtool library file
+# Generated by $PROGRAM (GNU $PACKAGE) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# The name that we can dlopen(3).
+dlname='$tdlname'
+
+# Names of this library.
+library_names='$library_names'
+
+# The name of the static archive.
+old_library='$old_library'
+
+# Linker flags that cannot go in dependency_libs.
+inherited_linker_flags='$new_inherited_linker_flags'
+
+# Libraries that this one depends upon.
+dependency_libs='$dependency_libs'
+
+# Names of additional weak libraries provided by this library
+weak_library_names='$weak_libs'
+
+# Version information for $libname.
+current=$current
+age=$age
+revision=$revision
+
+# Is this an already installed library?
+installed=$installed
+
+# Should we warn about portability when linking against -modules?
+shouldnotlink=$module
+
+# Files to dlopen/dlpreopen
+dlopen='$dlfiles'
+dlpreopen='$dlprefiles'
+
+# Directory that this library needs to be installed in:
+libdir='$install_libdir'"
+ if test no,yes = "$installed,$need_relink"; then
+ $ECHO >> $output "\
+relink_command=\"$relink_command\""
+ fi
+ done
+ }
+
+ # Do a symbolic link so that the libtool archive can be found in
+ # LD_LIBRARY_PATH before the program is installed.
+ func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?'
+ ;;
+ esac
+ exit $EXIT_SUCCESS
+}
+
+if test link = "$opt_mode" || test relink = "$opt_mode"; then
+ func_mode_link ${1+"$@"}
+fi
+
+
+# func_mode_uninstall arg...
+func_mode_uninstall ()
+{
+ $debug_cmd
+
+ RM=$nonopt
+ files=
+ rmforce=false
+ exit_status=0
+
+ # This variable tells wrapper scripts just to set variables rather
+ # than running their programs.
+ libtool_install_magic=$magic
+
+ for arg
+ do
+ case $arg in
+ -f) func_append RM " $arg"; rmforce=: ;;
+ -*) func_append RM " $arg" ;;
+ *) func_append files " $arg" ;;
+ esac
+ done
+
+ test -z "$RM" && \
+ func_fatal_help "you must specify an RM program"
+
+ rmdirs=
+
+ for file in $files; do
+ func_dirname "$file" "" "."
+ dir=$func_dirname_result
+ if test . = "$dir"; then
+ odir=$objdir
+ else
+ odir=$dir/$objdir
+ fi
+ func_basename "$file"
+ name=$func_basename_result
+ test uninstall = "$opt_mode" && odir=$dir
+
+ # Remember odir for removal later, being careful to avoid duplicates
+ if test clean = "$opt_mode"; then
+ case " $rmdirs " in
+ *" $odir "*) ;;
+ *) func_append rmdirs " $odir" ;;
+ esac
+ fi
+
+ # Don't error if the file doesn't exist and rm -f was used.
+ if { test -L "$file"; } >/dev/null 2>&1 ||
+ { test -h "$file"; } >/dev/null 2>&1 ||
+ test -f "$file"; then
+ :
+ elif test -d "$file"; then
+ exit_status=1
+ continue
+ elif $rmforce; then
+ continue
+ fi
+
+ rmfiles=$file
+
+ case $name in
+ *.la)
+ # Possibly a libtool archive, so verify it.
+ if func_lalib_p "$file"; then
+ func_source $dir/$name
+
+ # Delete the libtool libraries and symlinks.
+ for n in $library_names; do
+ func_append rmfiles " $odir/$n"
+ done
+ test -n "$old_library" && func_append rmfiles " $odir/$old_library"
+
+ case $opt_mode in
+ clean)
+ case " $library_names " in
+ *" $dlname "*) ;;
+ *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;;
+ esac
+ test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i"
+ ;;
+ uninstall)
+ if test -n "$library_names"; then
+ # Do each command in the postuninstall commands.
+ func_execute_cmds "$postuninstall_cmds" '$rmforce || exit_status=1'
+ fi
+
+ if test -n "$old_library"; then
+ # Do each command in the old_postuninstall commands.
+ func_execute_cmds "$old_postuninstall_cmds" '$rmforce || exit_status=1'
+ fi
+ # FIXME: should reinstall the best remaining shared library.
+ ;;
+ esac
+ fi
+ ;;
+
+ *.lo)
+ # Possibly a libtool object, so verify it.
+ if func_lalib_p "$file"; then
+
+ # Read the .lo file
+ func_source $dir/$name
+
+ # Add PIC object to the list of files to remove.
+ if test -n "$pic_object" && test none != "$pic_object"; then
+ func_append rmfiles " $dir/$pic_object"
+ fi
+
+ # Add non-PIC object to the list of files to remove.
+ if test -n "$non_pic_object" && test none != "$non_pic_object"; then
+ func_append rmfiles " $dir/$non_pic_object"
+ fi
+ fi
+ ;;
+
+ *)
+ if test clean = "$opt_mode"; then
+ noexename=$name
+ case $file in
+ *.exe)
+ func_stripname '' '.exe' "$file"
+ file=$func_stripname_result
+ func_stripname '' '.exe' "$name"
+ noexename=$func_stripname_result
+ # $file with .exe has already been added to rmfiles,
+ # add $file without .exe
+ func_append rmfiles " $file"
+ ;;
+ esac
+ # Do a test to see if this is a libtool program.
+ if func_ltwrapper_p "$file"; then
+ if func_ltwrapper_executable_p "$file"; then
+ func_ltwrapper_scriptname "$file"
+ relink_command=
+ func_source $func_ltwrapper_scriptname_result
+ func_append rmfiles " $func_ltwrapper_scriptname_result"
+ else
+ relink_command=
+ func_source $dir/$noexename
+ fi
+
+ # note $name still contains .exe if it was in $file originally
+ # as does the version of $file that was added into $rmfiles
+ func_append rmfiles " $odir/$name $odir/${name}S.$objext"
+ if test yes = "$fast_install" && test -n "$relink_command"; then
+ func_append rmfiles " $odir/lt-$name"
+ fi
+ if test "X$noexename" != "X$name"; then
+ func_append rmfiles " $odir/lt-$noexename.c"
+ fi
+ fi
+ fi
+ ;;
+ esac
+ func_show_eval "$RM $rmfiles" 'exit_status=1'
+ done
+
+ # Try to remove the $objdir's in the directories where we deleted files
+ for dir in $rmdirs; do
+ if test -d "$dir"; then
+ func_show_eval "rmdir $dir >/dev/null 2>&1"
+ fi
+ done
+
+ exit $exit_status
+}
+
+if test uninstall = "$opt_mode" || test clean = "$opt_mode"; then
+ func_mode_uninstall ${1+"$@"}
+fi
+
+test -z "$opt_mode" && {
+ help=$generic_help
+ func_fatal_help "you must specify a MODE"
+}
+
+test -z "$exec_cmd" && \
+ func_fatal_help "invalid operation mode '$opt_mode'"
+
+if test -n "$exec_cmd"; then
+ eval exec "$exec_cmd"
+ exit $EXIT_FAILURE
+fi
+
+exit $exit_status
+
+
+# The TAGs below are defined such that we never get into a situation
+# where we disable both kinds of libraries. Given conflicting
+# choices, we go for a static library, that is the most portable,
+# since we can't tell whether shared libraries were disabled because
+# the user asked for that or because the platform doesn't support
+# them. This is particularly important on AIX, because we don't
+# support having both static and shared libraries enabled at the same
+# time on that platform, so we default to a shared-only configuration.
+# If a disable-shared tag is given, we'll fallback to a static-only
+# configuration. But we'll never go from static-only to shared-only.
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-shared
+build_libtool_libs=no
+build_old_libs=yes
+# ### END LIBTOOL TAG CONFIG: disable-shared
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-static
+build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac`
+# ### END LIBTOOL TAG CONFIG: disable-static
+
+# Local Variables:
+# mode:shell-script
+# sh-indentation:2
+# End:
diff --git a/build/ltoptions.m4 b/build/ltoptions.m4
new file mode 100644
index 0000000..94b0829
--- /dev/null
+++ b/build/ltoptions.m4
@@ -0,0 +1,437 @@
+# Helper functions for option handling. -*- Autoconf -*-
+#
+# Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software
+# Foundation, Inc.
+# Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 8 ltoptions.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
+
+
+# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
+# ------------------------------------------
+m4_define([_LT_MANGLE_OPTION],
+[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
+
+
+# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
+# ---------------------------------------
+# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
+# matching handler defined, dispatch to it. Other OPTION-NAMEs are
+# saved as a flag.
+m4_define([_LT_SET_OPTION],
+[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
+m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
+ _LT_MANGLE_DEFUN([$1], [$2]),
+ [m4_warning([Unknown $1 option '$2'])])[]dnl
+])
+
+
+# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
+# ------------------------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+m4_define([_LT_IF_OPTION],
+[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
+
+
+# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
+# -------------------------------------------------------
+# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
+# are set.
+m4_define([_LT_UNLESS_OPTIONS],
+[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+ [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
+ [m4_define([$0_found])])])[]dnl
+m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
+])[]dnl
+])
+
+
+# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
+# ----------------------------------------
+# OPTION-LIST is a space-separated list of Libtool options associated
+# with MACRO-NAME. If any OPTION has a matching handler declared with
+# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
+# the unknown option and exit.
+m4_defun([_LT_SET_OPTIONS],
+[# Set options
+m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+ [_LT_SET_OPTION([$1], _LT_Option)])
+
+m4_if([$1],[LT_INIT],[
+ dnl
+ dnl Simply set some default values (i.e off) if boolean options were not
+ dnl specified:
+ _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
+ ])
+ _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
+ ])
+ dnl
+ dnl If no reference was made to various pairs of opposing options, then
+ dnl we run the default mode handler for the pair. For example, if neither
+ dnl 'shared' nor 'disable-shared' was passed, we enable building of shared
+ dnl archives by default:
+ _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
+ _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
+ _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
+ _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
+ [_LT_ENABLE_FAST_INSTALL])
+ _LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4],
+ [_LT_WITH_AIX_SONAME([aix])])
+ ])
+])# _LT_SET_OPTIONS
+
+
+## --------------------------------- ##
+## Macros to handle LT_INIT options. ##
+## --------------------------------- ##
+
+# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
+# -----------------------------------------
+m4_define([_LT_MANGLE_DEFUN],
+[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
+
+
+# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
+# -----------------------------------------------
+m4_define([LT_OPTION_DEFINE],
+[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
+])# LT_OPTION_DEFINE
+
+
+# dlopen
+# ------
+LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
+])
+
+AU_DEFUN([AC_LIBTOOL_DLOPEN],
+[_LT_SET_OPTION([LT_INIT], [dlopen])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the 'dlopen' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
+
+
+# win32-dll
+# ---------
+# Declare package support for building win32 dll's.
+LT_OPTION_DEFINE([LT_INIT], [win32-dll],
+[enable_win32_dll=yes
+
+case $host in
+*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*)
+ AC_CHECK_TOOL(AS, as, false)
+ AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+ AC_CHECK_TOOL(OBJDUMP, objdump, false)
+ ;;
+esac
+
+test -z "$AS" && AS=as
+_LT_DECL([], [AS], [1], [Assembler program])dnl
+
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl
+])# win32-dll
+
+AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+_LT_SET_OPTION([LT_INIT], [win32-dll])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the 'win32-dll' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
+
+
+# _LT_ENABLE_SHARED([DEFAULT])
+# ----------------------------
+# implement the --enable-shared flag, and supports the 'shared' and
+# 'disable-shared' LT_INIT options.
+# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
+m4_define([_LT_ENABLE_SHARED],
+[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([shared],
+ [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
+ [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
+ [p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_shared=yes ;;
+ no) enable_shared=no ;;
+ *)
+ enable_shared=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for pkg in $enableval; do
+ IFS=$lt_save_ifs
+ if test "X$pkg" = "X$p"; then
+ enable_shared=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac],
+ [enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
+
+ _LT_DECL([build_libtool_libs], [enable_shared], [0],
+ [Whether or not to build shared libraries])
+])# _LT_ENABLE_SHARED
+
+LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
+])
+
+AC_DEFUN([AC_DISABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], [disable-shared])
+])
+
+AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
+AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_SHARED], [])
+dnl AC_DEFUN([AM_DISABLE_SHARED], [])
+
+
+
+# _LT_ENABLE_STATIC([DEFAULT])
+# ----------------------------
+# implement the --enable-static flag, and support the 'static' and
+# 'disable-static' LT_INIT options.
+# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
+m4_define([_LT_ENABLE_STATIC],
+[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([static],
+ [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
+ [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
+ [p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_static=yes ;;
+ no) enable_static=no ;;
+ *)
+ enable_static=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for pkg in $enableval; do
+ IFS=$lt_save_ifs
+ if test "X$pkg" = "X$p"; then
+ enable_static=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac],
+ [enable_static=]_LT_ENABLE_STATIC_DEFAULT)
+
+ _LT_DECL([build_old_libs], [enable_static], [0],
+ [Whether or not to build static libraries])
+])# _LT_ENABLE_STATIC
+
+LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
+])
+
+AC_DEFUN([AC_DISABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], [disable-static])
+])
+
+AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
+AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_STATIC], [])
+dnl AC_DEFUN([AM_DISABLE_STATIC], [])
+
+
+
+# _LT_ENABLE_FAST_INSTALL([DEFAULT])
+# ----------------------------------
+# implement the --enable-fast-install flag, and support the 'fast-install'
+# and 'disable-fast-install' LT_INIT options.
+# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
+m4_define([_LT_ENABLE_FAST_INSTALL],
+[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([fast-install],
+ [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
+ [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
+ [p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_fast_install=yes ;;
+ no) enable_fast_install=no ;;
+ *)
+ enable_fast_install=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for pkg in $enableval; do
+ IFS=$lt_save_ifs
+ if test "X$pkg" = "X$p"; then
+ enable_fast_install=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac],
+ [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
+
+_LT_DECL([fast_install], [enable_fast_install], [0],
+ [Whether or not to optimize for fast installation])dnl
+])# _LT_ENABLE_FAST_INSTALL
+
+LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
+
+# Old names:
+AU_DEFUN([AC_ENABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the 'fast-install' option into LT_INIT's first parameter.])
+])
+
+AU_DEFUN([AC_DISABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the 'disable-fast-install' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
+dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
+
+
+# _LT_WITH_AIX_SONAME([DEFAULT])
+# ----------------------------------
+# implement the --with-aix-soname flag, and support the `aix-soname=aix'
+# and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT
+# is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'.
+m4_define([_LT_WITH_AIX_SONAME],
+[m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl
+shared_archive_member_spec=
+case $host,$enable_shared in
+power*-*-aix[[5-9]]*,yes)
+ AC_MSG_CHECKING([which variant of shared library versioning to provide])
+ AC_ARG_WITH([aix-soname],
+ [AS_HELP_STRING([--with-aix-soname=aix|svr4|both],
+ [shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])],
+ [case $withval in
+ aix|svr4|both)
+ ;;
+ *)
+ AC_MSG_ERROR([Unknown argument to --with-aix-soname])
+ ;;
+ esac
+ lt_cv_with_aix_soname=$with_aix_soname],
+ [AC_CACHE_VAL([lt_cv_with_aix_soname],
+ [lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT)
+ with_aix_soname=$lt_cv_with_aix_soname])
+ AC_MSG_RESULT([$with_aix_soname])
+ if test aix != "$with_aix_soname"; then
+ # For the AIX way of multilib, we name the shared archive member
+ # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o',
+ # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File.
+ # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag,
+ # the AIX toolchain works better with OBJECT_MODE set (default 32).
+ if test 64 = "${OBJECT_MODE-32}"; then
+ shared_archive_member_spec=shr_64
+ else
+ shared_archive_member_spec=shr
+ fi
+ fi
+ ;;
+*)
+ with_aix_soname=aix
+ ;;
+esac
+
+_LT_DECL([], [shared_archive_member_spec], [0],
+ [Shared archive member basename, for filename based shared library versioning on AIX])dnl
+])# _LT_WITH_AIX_SONAME
+
+LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])])
+LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])])
+LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])])
+
+
+# _LT_WITH_PIC([MODE])
+# --------------------
+# implement the --with-pic flag, and support the 'pic-only' and 'no-pic'
+# LT_INIT options.
+# MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'.
+m4_define([_LT_WITH_PIC],
+[AC_ARG_WITH([pic],
+ [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@],
+ [try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
+ [lt_p=${PACKAGE-default}
+ case $withval in
+ yes|no) pic_mode=$withval ;;
+ *)
+ pic_mode=default
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for lt_pkg in $withval; do
+ IFS=$lt_save_ifs
+ if test "X$lt_pkg" = "X$lt_p"; then
+ pic_mode=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac],
+ [pic_mode=m4_default([$1], [default])])
+
+_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
+])# _LT_WITH_PIC
+
+LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
+
+# Old name:
+AU_DEFUN([AC_LIBTOOL_PICMODE],
+[_LT_SET_OPTION([LT_INIT], [pic-only])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the 'pic-only' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
+
+## ----------------- ##
+## LTDL_INIT Options ##
+## ----------------- ##
+
+m4_define([_LTDL_MODE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
+ [m4_define([_LTDL_MODE], [nonrecursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [recursive],
+ [m4_define([_LTDL_MODE], [recursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [subproject],
+ [m4_define([_LTDL_MODE], [subproject])])
+
+m4_define([_LTDL_TYPE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [installable],
+ [m4_define([_LTDL_TYPE], [installable])])
+LT_OPTION_DEFINE([LTDL_INIT], [convenience],
+ [m4_define([_LTDL_TYPE], [convenience])])
diff --git a/build/ltsugar.m4 b/build/ltsugar.m4
new file mode 100644
index 0000000..48bc934
--- /dev/null
+++ b/build/ltsugar.m4
@@ -0,0 +1,124 @@
+# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
+#
+# Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software
+# Foundation, Inc.
+# Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 6 ltsugar.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
+
+
+# lt_join(SEP, ARG1, [ARG2...])
+# -----------------------------
+# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
+# associated separator.
+# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
+# versions in m4sugar had bugs.
+m4_define([lt_join],
+[m4_if([$#], [1], [],
+ [$#], [2], [[$2]],
+ [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
+m4_define([_lt_join],
+[m4_if([$#$2], [2], [],
+ [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
+
+
+# lt_car(LIST)
+# lt_cdr(LIST)
+# ------------
+# Manipulate m4 lists.
+# These macros are necessary as long as will still need to support
+# Autoconf-2.59, which quotes differently.
+m4_define([lt_car], [[$1]])
+m4_define([lt_cdr],
+[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
+ [$#], 1, [],
+ [m4_dquote(m4_shift($@))])])
+m4_define([lt_unquote], $1)
+
+
+# lt_append(MACRO-NAME, STRING, [SEPARATOR])
+# ------------------------------------------
+# Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'.
+# Note that neither SEPARATOR nor STRING are expanded; they are appended
+# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
+# No SEPARATOR is output if MACRO-NAME was previously undefined (different
+# than defined and empty).
+#
+# This macro is needed until we can rely on Autoconf 2.62, since earlier
+# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
+m4_define([lt_append],
+[m4_define([$1],
+ m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
+
+
+
+# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
+# ----------------------------------------------------------
+# Produce a SEP delimited list of all paired combinations of elements of
+# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list
+# has the form PREFIXmINFIXSUFFIXn.
+# Needed until we can rely on m4_combine added in Autoconf 2.62.
+m4_define([lt_combine],
+[m4_if(m4_eval([$# > 3]), [1],
+ [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
+[[m4_foreach([_Lt_prefix], [$2],
+ [m4_foreach([_Lt_suffix],
+ ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
+ [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
+
+
+# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
+# -----------------------------------------------------------------------
+# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
+# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
+m4_define([lt_if_append_uniq],
+[m4_ifdef([$1],
+ [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
+ [lt_append([$1], [$2], [$3])$4],
+ [$5])],
+ [lt_append([$1], [$2], [$3])$4])])
+
+
+# lt_dict_add(DICT, KEY, VALUE)
+# -----------------------------
+m4_define([lt_dict_add],
+[m4_define([$1($2)], [$3])])
+
+
+# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
+# --------------------------------------------
+m4_define([lt_dict_add_subkey],
+[m4_define([$1($2:$3)], [$4])])
+
+
+# lt_dict_fetch(DICT, KEY, [SUBKEY])
+# ----------------------------------
+m4_define([lt_dict_fetch],
+[m4_ifval([$3],
+ m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
+ m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
+
+
+# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
+# -----------------------------------------------------------------
+m4_define([lt_if_dict_fetch],
+[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
+ [$5],
+ [$6])])
+
+
+# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
+# --------------------------------------------------------------
+m4_define([lt_dict_filter],
+[m4_if([$5], [], [],
+ [lt_join(m4_quote(m4_default([$4], [[, ]])),
+ lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
+ [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
+])
diff --git a/build/ltversion.m4 b/build/ltversion.m4
new file mode 100644
index 0000000..fa04b52
--- /dev/null
+++ b/build/ltversion.m4
@@ -0,0 +1,23 @@
+# ltversion.m4 -- version numbers -*- Autoconf -*-
+#
+# Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc.
+# Written by Scott James Remnant, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# @configure_input@
+
+# serial 4179 ltversion.m4
+# This file is part of GNU Libtool
+
+m4_define([LT_PACKAGE_VERSION], [2.4.6])
+m4_define([LT_PACKAGE_REVISION], [2.4.6])
+
+AC_DEFUN([LTVERSION_VERSION],
+[macro_version='2.4.6'
+macro_revision='2.4.6'
+_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
+_LT_DECL(, macro_revision, 0)
+])
diff --git a/build/lt~obsolete.m4 b/build/lt~obsolete.m4
new file mode 100644
index 0000000..c6b26f8
--- /dev/null
+++ b/build/lt~obsolete.m4
@@ -0,0 +1,99 @@
+# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*-
+#
+# Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software
+# Foundation, Inc.
+# Written by Scott James Remnant, 2004.
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 5 lt~obsolete.m4
+
+# These exist entirely to fool aclocal when bootstrapping libtool.
+#
+# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN),
+# which have later been changed to m4_define as they aren't part of the
+# exported API, or moved to Autoconf or Automake where they belong.
+#
+# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN
+# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
+# using a macro with the same name in our local m4/libtool.m4 it'll
+# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
+# and doesn't know about Autoconf macros at all.)
+#
+# So we provide this file, which has a silly filename so it's always
+# included after everything else. This provides aclocal with the
+# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
+# because those macros already exist, or will be overwritten later.
+# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
+#
+# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
+# Yes, that means every name once taken will need to remain here until
+# we give up compatibility with versions before 1.7, at which point
+# we need to keep only those names which we still refer to.
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
+
+m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
+m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])])
+m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
+m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])])
+m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
+m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])])
+m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])])
+m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
+m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])])
+m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])])
+m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])])
+m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
+m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
+m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
+m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
+m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
+m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
+m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
+m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])])
+m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])])
+m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
+m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
+m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
+m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
+m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])])
+m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])])
+m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])])
+m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
+m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
+m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])])
+m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])])
+m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
+m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
+m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])])
+m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
+m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])])
+m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])])
+m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])])
+m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
+m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
+m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
+m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
+m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
+m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
+m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])])
+m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
+m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])])
+m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])])
+m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])])
+m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])])
+m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])])
+m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])])
+m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])])
diff --git a/build/man.mk b/build/man.mk
new file mode 100644
index 0000000..0d2a023
--- /dev/null
+++ b/build/man.mk
@@ -0,0 +1,59 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##---------------------------------------------------------------------------
+#
+# Makefile Template for Manual Pages
+#
+
+MANDIR=$(mandir)/man$(MANSECT)
+TMP_SUFFIX=tmp
+
+all-common:
+ PAGES=`cd $(srcdir); echo *.$(MANSECT)`; \
+ for page in $$PAGES; do \
+ $(SED) -e "s%LDVERSION%$(VERSION)%" \
+ -e 's%ETCDIR%$(sysconfdir)%g' \
+ -e 's%LOCALSTATEDIR%$(localstatedir)%' \
+ -e 's%SYSCONFDIR%$(sysconfdir)%' \
+ -e 's%DATADIR%$(datadir)%' \
+ -e 's%SBINDIR%$(sbindir)%' \
+ -e 's%BINDIR%$(bindir)%' \
+ -e 's%LIBDIR%$(libdir)%' \
+ -e 's%LIBEXECDIR%$(libexecdir)%' \
+ -e 's%MODULEDIR%$(moduledir)%' \
+ -e 's%RELEASEDATE%$(RELEASEDATE)%' \
+ $(srcdir)/$$page \
+ | (cd $(srcdir); $(SOELIM) -) > $$page.$(TMP_SUFFIX); \
+ done
+
+install-common:
+ -$(MKDIR) $(DESTDIR)$(MANDIR)
+ PAGES=`cd $(srcdir); echo *.$(MANSECT)`; \
+ for page in $$PAGES; do \
+ echo "installing $$page in $(DESTDIR)$(MANDIR)"; \
+ $(RM) $(DESTDIR)$(MANDIR)/$$page; \
+ $(INSTALL) $(INSTALLFLAGS) -m 644 $$page.$(TMP_SUFFIX) $(DESTDIR)$(MANDIR)/$$page; \
+ if test -f "$(srcdir)/$$page.links" ; then \
+ for link in `$(CAT) $(srcdir)/$$page.links`; do \
+ echo "installing $$link in $(DESTDIR)$(MANDIR) as link to $$page"; \
+ $(RM) $(DESTDIR)$(MANDIR)/$$link ; \
+ $(LN_S) $(DESTDIR)$(MANDIR)/$$page $(DESTDIR)$(MANDIR)/$$link; \
+ done; \
+ fi; \
+ done
+
+clean-common: FORCE
+ $(RM) *.tmp all-common
+
+Makefile: $(top_srcdir)/build/man.mk
diff --git a/build/missing b/build/missing
new file mode 100755
index 0000000..f62bbae
--- /dev/null
+++ b/build/missing
@@ -0,0 +1,215 @@
+#! /bin/sh
+# Common wrapper for a few potentially missing GNU programs.
+
+scriptversion=2013-10-28.13; # UTC
+
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+if test $# -eq 0; then
+ echo 1>&2 "Try '$0 --help' for more information"
+ exit 1
+fi
+
+case $1 in
+
+ --is-lightweight)
+ # Used by our autoconf macros to check whether the available missing
+ # script is modern enough.
+ exit 0
+ ;;
+
+ --run)
+ # Back-compat with the calling convention used by older automake.
+ shift
+ ;;
+
+ -h|--h|--he|--hel|--help)
+ echo "\
+$0 [OPTION]... PROGRAM [ARGUMENT]...
+
+Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
+to PROGRAM being missing or too old.
+
+Options:
+ -h, --help display this help and exit
+ -v, --version output version information and exit
+
+Supported PROGRAM values:
+ aclocal autoconf autoheader autom4te automake makeinfo
+ bison yacc flex lex help2man
+
+Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
+'g' are ignored when checking the name.
+
+Send bug reports to <bug-automake@gnu.org>."
+ exit $?
+ ;;
+
+ -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
+ echo "missing $scriptversion (GNU Automake)"
+ exit $?
+ ;;
+
+ -*)
+ echo 1>&2 "$0: unknown '$1' option"
+ echo 1>&2 "Try '$0 --help' for more information"
+ exit 1
+ ;;
+
+esac
+
+# Run the given program, remember its exit status.
+"$@"; st=$?
+
+# If it succeeded, we are done.
+test $st -eq 0 && exit 0
+
+# Also exit now if we it failed (or wasn't found), and '--version' was
+# passed; such an option is passed most likely to detect whether the
+# program is present and works.
+case $2 in --version|--help) exit $st;; esac
+
+# Exit code 63 means version mismatch. This often happens when the user
+# tries to use an ancient version of a tool on a file that requires a
+# minimum version.
+if test $st -eq 63; then
+ msg="probably too old"
+elif test $st -eq 127; then
+ # Program was missing.
+ msg="missing on your system"
+else
+ # Program was found and executed, but failed. Give up.
+ exit $st
+fi
+
+perl_URL=http://www.perl.org/
+flex_URL=http://flex.sourceforge.net/
+gnu_software_URL=http://www.gnu.org/software
+
+program_details ()
+{
+ case $1 in
+ aclocal|automake)
+ echo "The '$1' program is part of the GNU Automake package:"
+ echo "<$gnu_software_URL/automake>"
+ echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
+ echo "<$gnu_software_URL/autoconf>"
+ echo "<$gnu_software_URL/m4/>"
+ echo "<$perl_URL>"
+ ;;
+ autoconf|autom4te|autoheader)
+ echo "The '$1' program is part of the GNU Autoconf package:"
+ echo "<$gnu_software_URL/autoconf/>"
+ echo "It also requires GNU m4 and Perl in order to run:"
+ echo "<$gnu_software_URL/m4/>"
+ echo "<$perl_URL>"
+ ;;
+ esac
+}
+
+give_advice ()
+{
+ # Normalize program name to check for.
+ normalized_program=`echo "$1" | sed '
+ s/^gnu-//; t
+ s/^gnu//; t
+ s/^g//; t'`
+
+ printf '%s\n' "'$1' is $msg."
+
+ configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
+ case $normalized_program in
+ autoconf*)
+ echo "You should only need it if you modified 'configure.ac',"
+ echo "or m4 files included by it."
+ program_details 'autoconf'
+ ;;
+ autoheader*)
+ echo "You should only need it if you modified 'acconfig.h' or"
+ echo "$configure_deps."
+ program_details 'autoheader'
+ ;;
+ automake*)
+ echo "You should only need it if you modified 'Makefile.am' or"
+ echo "$configure_deps."
+ program_details 'automake'
+ ;;
+ aclocal*)
+ echo "You should only need it if you modified 'acinclude.m4' or"
+ echo "$configure_deps."
+ program_details 'aclocal'
+ ;;
+ autom4te*)
+ echo "You might have modified some maintainer files that require"
+ echo "the 'autom4te' program to be rebuilt."
+ program_details 'autom4te'
+ ;;
+ bison*|yacc*)
+ echo "You should only need it if you modified a '.y' file."
+ echo "You may want to install the GNU Bison package:"
+ echo "<$gnu_software_URL/bison/>"
+ ;;
+ lex*|flex*)
+ echo "You should only need it if you modified a '.l' file."
+ echo "You may want to install the Fast Lexical Analyzer package:"
+ echo "<$flex_URL>"
+ ;;
+ help2man*)
+ echo "You should only need it if you modified a dependency" \
+ "of a man page."
+ echo "You may want to install the GNU Help2man package:"
+ echo "<$gnu_software_URL/help2man/>"
+ ;;
+ makeinfo*)
+ echo "You should only need it if you modified a '.texi' file, or"
+ echo "any other file indirectly affecting the aspect of the manual."
+ echo "You might want to install the Texinfo package:"
+ echo "<$gnu_software_URL/texinfo/>"
+ echo "The spurious makeinfo call might also be the consequence of"
+ echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
+ echo "want to install GNU make:"
+ echo "<$gnu_software_URL/make/>"
+ ;;
+ *)
+ echo "You might have modified some files without having the proper"
+ echo "tools for further handling them. Check the 'README' file, it"
+ echo "often tells you about the needed prerequisites for installing"
+ echo "this package. You may also peek at any GNU archive site, in"
+ echo "case some other package contains this missing '$1' program."
+ ;;
+ esac
+}
+
+give_advice "$1" | sed -e '1s/^/WARNING: /' \
+ -e '2,$s/^/ /' >&2
+
+# Propagate the correct exit status (expected to be 127 for a program
+# not found, 63 for a program that failed due to version mismatch).
+exit $st
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/build/mkdep b/build/mkdep
new file mode 100755
index 0000000..59a3764
--- /dev/null
+++ b/build/mkdep
@@ -0,0 +1,223 @@
+#! /bin/sh -
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+#
+## Portions Copyright (c) 1987 Regents of the University of California.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms are permitted
+## provided that the above copyright notice and this paragraph are
+## duplicated in all such forms and that any documentation,
+## advertising materials, and other materials related to such
+## distribution and use acknowledge that the software was developed
+## by the University of California, Berkeley. The name of the
+## University may not be used to endorse or promote products derived
+## from this software without specific prior written permission.
+## THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+## IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+## WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+#
+# @(#)mkdep.sh 5.12 (Berkeley) 6/30/88
+#
+# We now use whatever path is already set by the invoker
+#PATH=/bin:/usr/bin:/usr/ucb
+#export PATH
+
+set -e # exit immediately if any errors occur
+
+MAKE=Makefile # default makefile name is "Makefile"
+NOSLASH="no" # by default, / dependencies are included
+SRCDIR=""
+SED=cat
+
+: ${CC=cc} # use cc by default
+
+# We generally set these via the command line options
+: ${MKDEP_CC=$CC} # select default compiler to generate dependencies
+: ${MKDEP_CFLAGS="-M"} # cc -M usually produces dependencies
+
+while :
+ do case "$1" in
+ # the -s flag removes dependencies to files that begin with /
+ -s)
+ NOSLASH=yes;
+ shift ;;
+
+ # -f allows you to select a makefile name
+ -f)
+ MAKE=$2
+ shift; shift ;;
+
+ # -d allows you to select a VPATH directory
+ -d)
+ SRCDIR=$2
+ shift; shift ;;
+
+ # -c allows you to override the compiler used to generate dependencies
+ -c)
+ MKDEP_CC=$2
+ shift; shift ;;
+
+ # -m allows you to override the compiler flags used to generate
+ # dependencies.
+ -m)
+ MKDEP_CFLAGS=$2
+ shift; shift ;;
+
+ # the -p flag produces "program: program.c" style dependencies
+ # so .o's don't get produced
+ -p)
+ SED='sed -e s;\.o;;'
+ shift ;;
+
+ # the -l flag produces libtool compatible dependencies
+ -l)
+ SED='sed -e s;\.o:;.lo:;'
+ shift ;;
+
+# -*) shift ;;
+
+ *)
+ break ;;
+ esac
+done
+
+if test $# = 0 ; then
+ echo 'usage: mkdep [-p] [-s] [-c cc] [-m flags] [-f makefile] [-d srcdir] [cppflags] file ...'
+ exit 1
+fi
+
+if test ! -w $MAKE ; then
+ echo "mkdep: no writeable file \"$MAKE\""
+ exit 1
+fi
+
+TMP=${TMPDIR-/tmp}/mkdep$$
+
+trap 'rm -f $TMP.sed $TMP ; exit 1' 1 2 3 13 15
+
+cp $MAKE ${MAKE}.bak
+
+sed -e '/DO NOT DELETE THIS LINE/,$d' < $MAKE > $TMP
+
+cat << _EOF_ >> $TMP
+# DO NOT DELETE THIS LINE -- mkdep uses it.
+# DO NOT PUT ANYTHING AFTER THIS LINE, IT WILL GO AWAY.
+
+_EOF_
+
+# If your compiler doesn't have -M, you may be able to use -E instead.
+# The preprocessor must generate lines of the form
+# #.* [0-9]* "dependent file" .*
+# This script will parse out the "dependent file"s to generate the
+# dependency list.
+
+if test "x$SRCDIR" = "x" ; then
+ files=$*
+else
+ files=
+ for i in $* ; do
+ if test -f $i ; then
+ files="$files $i"
+ elif test -f $SRCDIR/$i ; then
+ files="$files $SRCDIR/$i"
+ else
+ files="$files $i"
+ fi
+ done
+
+ MKDEP_CFLAGS="$MKDEP_CFLAGS -I$SRCDIR"
+fi
+
+cat << _EOF_ >> $TMP
+
+#
+# files: $*
+# command: $MKDEP_CC $MKDEP_CFLAGS $files
+#
+
+_EOF_
+
+case $MKDEP_CFLAGS in
+# Using regular preprocessor output
+ -E*)
+FLAGS=""
+FILES=""
+for i in $files; do
+ case $i in
+ -*) FLAGS="$FLAGS $i" ;;
+ *) FILES="$FILES $i" ;;
+ esac
+done
+for i in $FILES; do
+ $MKDEP_CC $MKDEP_CFLAGS $FLAGS $i | grep '^#.*"' > $TMP.sed
+awk '
+BEGIN {
+ file = "'$i'"
+ n = split(file, parts, "/")
+ filenm = substr(parts[n], 0, length(parts[n])-1) "o"
+}
+{
+ dep = split($3, parts, "\"")
+ dep = parts[2]
+ if (dep ~ "^\./.*") dep = substr(dep, 3, length(dep)-2)
+ if (( noslash == "yes") && (dep ~ /^\// )) continue
+ if (deps[dep] == 0) printf "%s: %s\n", filenm, dep
+ deps[dep] = 1
+}' noslash="$NOSLASH" $TMP.sed >> $TMP
+done
+ ;;
+
+ *)
+# Using -M or some other specific dependency-generating option
+$MKDEP_CC $MKDEP_CFLAGS $files | \
+ sed -e 's; \./; ;g' -e 's/ :/:/' | \
+ $SED > $TMP.sed
+# do not pipe to awk. SGI awk wants a filename as argument.
+# (or '-', but I do not know if all other awks support that.)
+awk '
+$1 ~ /:$/ {
+ filenm=$1
+ dep=substr($0, length(filenm)+1)
+}
+$1 !~ /:$/ {
+ dep=$0
+}
+/.*/ {
+ if ( length(filenm) < 2 ) next
+ if ( filenm ~ /:.*:$/ ) next
+ split(dep, depends, " ")
+ for(d in depends) {
+ dfile = depends[d]
+ if ( length(dfile) < 2 ) continue
+ if ( dfile ~ /:/ ) continue
+ if (( noslash == "yes") && (dfile ~ /^\// )) continue
+ rec = filenm " " dfile
+ print rec
+ }
+}
+' noslash="$NOSLASH" $TMP.sed >> $TMP
+ ;;
+esac
+
+
+cat << _EOF_ >> $TMP
+
+# IF YOU PUT ANYTHING HERE IT WILL GO AWAY
+_EOF_
+
+# copy to preserve permissions
+cp $TMP $MAKE
+rm -f ${MAKE}.bak $TMP.sed $TMP
+exit 0
diff --git a/build/mkdep.aix b/build/mkdep.aix
new file mode 100755
index 0000000..5979279
--- /dev/null
+++ b/build/mkdep.aix
@@ -0,0 +1,17 @@
+#! /bin/sh
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+cc_r -ME $* > /dev/null
+cat *.u
+rm *.u
diff --git a/build/mkrelease b/build/mkrelease
new file mode 100755
index 0000000..79d37fe
--- /dev/null
+++ b/build/mkrelease
@@ -0,0 +1,96 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+#
+# Make a release
+#
+
+#
+# This script MUST NOT add files to the export nor modify
+# any file in the export, exceptions:
+# make guide.html
+#
+
+set -e # exit immediately if any errors occur
+
+if test $# != 3 ; then
+ echo 'usage: mkrelease REPO RELNAME TAG'
+ exit 1
+fi
+
+REPO=$1
+shift
+RELNAME=openldap-$1
+shift
+TAG=$1
+shift
+
+#Linux
+#SHA="sha1sum"
+#MD="md5sum"
+#BSD
+#SHA="sha1"
+#MD="md5"
+#OpenSSL
+#SHA="openssl sha1"
+SHA3="openssl sha3-512"
+#MD="openssl md5"
+
+if test -e $RELNAME ; then
+ echo "error: $RELNAME exists"
+ exit 1
+fi
+
+echo Release: $RELNAME
+echo Tag: $TAG
+
+git archive --format=tar --prefix="${RELNAME}/" --remote="${REPO}" "$TAG" | tar xvf -
+
+if test ! -d $RELNAME ; then
+ echo "error: $RELNAME doesn't exists"
+ exit 1
+fi
+
+if test -e $RELNAME/doc/guide/admin/guide.sdf ; then
+ echo "build guide..."
+ ( cd $RELNAME/doc/guide/admin ; make guide.html )
+else
+ echo "No guide"
+fi
+
+if test -e $RELNAME/libraries/liblunicode/ucdata/uctable.h ; then
+ echo "touching uctable.h..."
+ touch $RELNAME/libraries/liblunicode/ucdata/uctable.h
+fi
+
+if test ! -e $RELNAME/build/version.sh ; then
+ echo "No build version"
+ OL_STRING="something"
+else
+ eval `$RELNAME/build/version.sh`
+fi
+
+echo "Rolling up $OL_STRING ..."
+
+
+tar cf $RELNAME.tar $RELNAME
+gzip -9 -c $RELNAME.tar > $RELNAME.tgz
+#${MD} $RELNAME.tgz > $RELNAME.md5
+#${SHA} $RELNAME.tgz > $RELNAME.sha1
+${SHA3} $RELNAME.tgz > $RELNAME.sha3-512
+rm -f $RELNAME.tar
+
+ls -l $RELNAME.*
+
+echo "Made $OL_STRING as $RELNAME.tgz"
diff --git a/build/mkvers.bat b/build/mkvers.bat
new file mode 100755
index 0000000..0f83609
--- /dev/null
+++ b/build/mkvers.bat
@@ -0,0 +1,28 @@
+:: $OpenLDAP$
+:: This work is part of OpenLDAP Software <http://www.openldap.org/>.
+::
+:: Copyright 1998-2022 The OpenLDAP Foundation.
+:: All rights reserved.
+::
+:: Redistribution and use in source and binary forms, with or without
+:: modification, are permitted only as authorized by the OpenLDAP
+:: Public License.
+::
+:: A copy of this license is available in the file LICENSE in the
+:: top-level directory of the distribution or, alternatively, at
+:: <http://www.OpenLDAP.org/license.html>.
+
+::
+:: Create a version.c file from build/version.h
+::
+
+:: usage: mkvers.bat <path/version.h>, <version.c>, <appname>, <static>
+
+copy %1 %2
+(echo. ) >> %2
+(echo #include "portable.h") >> %2
+(echo. ) >> %2
+(echo %4 const char __Version[] =) >> %2
+(echo "@(#) $" OPENLDAP_PACKAGE ": %3 " OPENLDAP_VERSION) >> %2
+(echo " (" __DATE__ " " __TIME__ ") $\n") >> %2
+(echo "\t%USERNAME%@%COMPUTERNAME% %CD:\=/%\n";) >> %2
diff --git a/build/mkversion b/build/mkversion
new file mode 100755
index 0000000..19cbd80
--- /dev/null
+++ b/build/mkversion
@@ -0,0 +1,83 @@
+#! /bin/sh
+# Create a version.c file
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+PACKAGE=OpenLDAP
+VERSION=unknown
+SYMBOL=__Version
+static=static
+const=const
+while :
+ do case "$1" in
+ -p)
+ PACKAGE=$2
+ shift; shift ;;
+ -v)
+ VERSION=$2
+ shift; shift ;;
+
+ -c)
+ const=
+ shift ;;
+ -n)
+ SYMBOL=$2
+ shift; shift ;;
+ -s)
+ static=
+ shift ;;
+
+# -*) shift ;;
+ *)
+ break ;;
+ esac
+done
+
+if test $# != 1 ; then
+ echo 'usage: mkversion [-c] [-s] [-p package] [-v version] application'
+ exit 1
+fi
+
+APPLICATION=$1
+# Reproducible builds set SOURCE_DATE_EPOCH, want constant strings
+if [ -n "${SOURCE_DATE_EPOCH}" ]; then
+ WHOWHERE="openldap"
+else
+ WHOWHERE="$USER@$(uname -n):$(pwd)"
+fi
+
+cat << __EOF__
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+static const char copyright[] =
+"Copyright 1998-2022 The OpenLDAP Foundation. All rights reserved.\n"
+"COPYING RESTRICTIONS APPLY\n";
+
+$static $const char $SYMBOL[] =
+"@(#) \$$PACKAGE: $APPLICATION $VERSION (" __DATE__ " " __TIME__ ") \$\n"
+"\t$WHOWHERE\n";
+
+__EOF__
diff --git a/build/mod.mk b/build/mod.mk
new file mode 100644
index 0000000..2673047
--- /dev/null
+++ b/build/mod.mk
@@ -0,0 +1,92 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##---------------------------------------------------------------------------
+#
+# Makefile Template for Server Modules
+#
+
+LIBRARY = $(LIBBASE).la
+LIBSTAT = lib$(LIBBASE).a
+
+MKDEPFLAG = -l
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LTCOMPILE_MOD) $<
+
+all-no lint-no 5lint-no depend-no install-no: FORCE
+ @echo "run configure with $(BUILD_OPT) to make $(LIBBASE)"
+
+all-common: all-$(BUILD_MOD)
+
+version.c: Makefile
+ $(RM) $@
+ $(MKVERSION) $(LIBBASE) > $@
+
+version.lo: version.c $(OBJS)
+
+$(LIBRARY): version.lo
+ $(LTLINK_MOD) -module -o $@ $(OBJS) version.lo $(LINK_LIBS)
+
+$(LIBSTAT): version.lo
+ $(AR) ruv $@ `echo $(OBJS) | sed 's/\.lo/.o/g'` version.o
+ @$(RANLIB) $@
+
+clean-common: clean-lib FORCE
+veryclean-common: veryclean-lib FORCE
+
+
+lint-common: lint-$(BUILD_MOD)
+
+5lint-common: 5lint-$(BUILD_MOD)
+
+depend-common: depend-$(BUILD_MOD)
+
+install-common: install-$(BUILD_MOD)
+
+all-local-mod:
+all-mod: $(LIBRARY) all-local-mod FORCE
+
+all-local-lib:
+all-yes: $(LIBSTAT) all-local-lib FORCE
+
+install-mod: $(LIBRARY)
+ @-$(MKDIR) $(DESTDIR)$(moduledir)
+ $(LTINSTALL) $(INSTALLFLAGS) -m 755 $(LIBRARY) $(DESTDIR)$(moduledir)
+
+install-local-lib:
+install-yes: install-local-lib FORCE
+
+lint-local-lib:
+lint-yes lint-mod: lint-local-lib FORCE
+ $(LINT) $(DEFS) $(DEFINES) $(SRCS)
+
+5lint-local-lib:
+5lint-yes 5lint-mod: 5lint-local-lib FORCE
+ $(5LINT) $(DEFS) $(DEFINES) $(SRCS)
+
+clean-local-lib:
+clean-lib: clean-local-lib FORCE
+ $(RM) $(LIBRARY) $(LIBSTAT) version.c *.o *.lo a.out core .libs/*
+
+depend-local-lib:
+depend-yes depend-mod: depend-local-lib FORCE
+ $(MKDEP) $(DEFS) $(DEFINES) $(SRCS)
+
+veryclean-local-lib:
+veryclean-lib: clean-lib veryclean-local-lib
+
+Makefile: $(top_srcdir)/build/mod.mk
+
diff --git a/build/openldap.m4 b/build/openldap.m4
new file mode 100644
index 0000000..c7fa19e
--- /dev/null
+++ b/build/openldap.m4
@@ -0,0 +1,810 @@
+dnl OpenLDAP Autoconf Macros
+dnl $OpenLDAP$
+dnl This work is part of OpenLDAP Software <http://www.openldap.org/>.
+dnl
+dnl Copyright 1998-2022 The OpenLDAP Foundation.
+dnl All rights reserved.
+dnl
+dnl Redistribution and use in source and binary forms, with or without
+dnl modification, are permitted only as authorized by the OpenLDAP
+dnl Public License.
+dnl
+dnl A copy of this license is available in the file LICENSE in the
+dnl top-level directory of the distribution or, alternatively, at
+dnl <http://www.OpenLDAP.org/license.html>.
+dnl
+dnl --------------------------------------------------------------------
+dnl Restricted form of AC_ARG_ENABLE that limits user options
+dnl
+dnl $1 = option name
+dnl $2 = help-string
+dnl $3 = default value (auto). "--" means do not set it by default
+dnl $4 = allowed values (auto yes no)
+dnl $5 = overridden default
+AC_DEFUN([OL_ARG_ENABLE], [# OpenLDAP --enable-$1
+ pushdef([ol_DefVal],ifelse($3,,auto,$3))
+ AC_ARG_ENABLE($1,ifelse($4,,[$2],[$2] translit([$4],[ ],[|])) ifelse($3,--,,@<:@ol_DefVal@:>@),[
+ ol_arg=invalid
+ for ol_val in ifelse($4,,[auto yes no],[$4]) ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ AC_MSG_ERROR(bad value $enableval for --enable-$1)
+ fi
+ ol_enable_$1="$ol_arg"
+]ifelse($3,--,,[,
+[ ol_enable_$1=ifelse($5,,ol_DefVal,[${]$5[:-]ol_DefVal[}])]]))dnl
+dnl AC_MSG_RESULT([OpenLDAP -enable-$1 $ol_enable_$1])
+ popdef([ol_DefVal])
+# end --enable-$1
+])dnl
+dnl
+dnl --------------------------------------------------------------------
+dnl Restricted form of AC_ARG_WITH that limits user options
+dnl
+dnl $1 = option name
+dnl $2 = help-string
+dnl $3 = default value (no)
+dnl $4 = allowed values (yes or no)
+AC_DEFUN([OL_ARG_WITH], [# OpenLDAP --with-$1
+ AC_ARG_WITH($1,[$2 @<:@]ifelse($3,,yes,$3)@:>@,[
+ ol_arg=invalid
+ for ol_val in ifelse($4,,[yes no],[$4]) ; do
+ if test "$withval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ AC_MSG_ERROR(bad value $withval for --with-$1)
+ fi
+ ol_with_$1="$ol_arg"
+],
+[ ol_with_$1=ifelse($3,,"no","$3")])dnl
+dnl AC_MSG_RESULT([OpenLDAP --with-$1 $ol_with_$1])
+# end --with-$1
+])dnl
+dnl ====================================================================
+dnl Check for dependency generation flag
+AC_DEFUN([OL_MKDEPEND], [# test for make depend flag
+OL_MKDEP=
+OL_MKDEP_FLAGS=
+if test -z "${MKDEP}"; then
+ OL_MKDEP="${CC-cc}"
+ if test -z "${MKDEP_FLAGS}"; then
+ AC_CACHE_CHECK([for ${OL_MKDEP} depend flag], ol_cv_mkdep, [
+ ol_cv_mkdep=no
+ for flag in "-M" "-xM"; do
+ cat > conftest.c <<EOF
+ noCode;
+EOF
+ if AC_TRY_COMMAND($OL_MKDEP $flag conftest.c) \
+ | grep '^conftest\.'"${ac_objext}" >/dev/null 2>&1
+ then
+ if test ! -f conftest."${ac_object}" ; then
+ ol_cv_mkdep=$flag
+ OL_MKDEP_FLAGS="$flag"
+ break
+ fi
+ fi
+ done
+ rm -f conftest*
+ ])
+ test "$ol_cv_mkdep" = no && OL_MKDEP=":"
+ else
+ cc_cv_mkdep=yes
+ OL_MKDEP_FLAGS="${MKDEP_FLAGS}"
+ fi
+else
+ cc_cv_mkdep=yes
+ OL_MKDEP="${MKDEP}"
+ OL_MKDEP_FLAGS="${MKDEP_FLAGS}"
+fi
+AC_SUBST(OL_MKDEP)
+AC_SUBST(OL_MKDEP_FLAGS)
+])
+dnl
+dnl ====================================================================
+dnl Check if system uses EBCDIC instead of ASCII
+AC_DEFUN([OL_CPP_EBCDIC], [# test for EBCDIC
+AC_CACHE_CHECK([for EBCDIC],ol_cv_cpp_ebcdic,[
+ AC_PREPROC_IFELSE([AC_LANG_SOURCE([[
+#if !('M' == 0xd4)
+#include <__ASCII__/generate_error.h>
+#endif
+]])],[ol_cv_cpp_ebcdic=yes],[ol_cv_cpp_ebcdic=no])])
+if test $ol_cv_cpp_ebcdic = yes ; then
+ AC_DEFINE(HAVE_EBCDIC,1, [define if system uses EBCDIC instead of ASCII])
+fi
+])
+dnl
+dnl --------------------------------------------------------------------
+dnl Check for MSVC
+AC_DEFUN([OL_MSVC],
+[AC_REQUIRE_CPP()dnl
+AC_CACHE_CHECK([whether we are using MS Visual C++], ol_cv_msvc,
+[AC_PREPROC_IFELSE([AC_LANG_SOURCE([[
+#ifndef _MSC_VER
+#include <__FOO__/generate_error.h>
+#endif
+]])],[ol_cv_msvc=yes],[ol_cv_msvc=no])])])
+
+dnl --------------------------------------------------------------------
+dnl OpenLDAP version of STDC header check w/ EBCDIC support
+AC_DEFUN([OL_HEADER_STDC],
+[AC_REQUIRE_CPP()dnl
+AC_REQUIRE([OL_CPP_EBCDIC])dnl
+AC_CACHE_CHECK([for ANSI C header files], ol_cv_header_stdc,
+[AC_PREPROC_IFELSE([AC_LANG_SOURCE([[#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>]])],[ol_cv_header_stdc=yes],[ol_cv_header_stdc=no])
+
+if test $ol_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+AC_EGREP_HEADER(memchr, string.h, , ol_cv_header_stdc=no)
+fi
+
+if test $ol_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+AC_EGREP_HEADER(free, stdlib.h, , ol_cv_header_stdc=no)
+fi
+
+if test $ol_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <ctype.h>
+#ifndef HAVE_EBCDIC
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+]])],[],[ol_cv_header_stdc=no],[:])
+fi])
+if test $ol_cv_header_stdc = yes; then
+ AC_DEFINE(STDC_HEADERS)
+fi
+ac_cv_header_stdc=disable
+])
+dnl
+dnl ====================================================================
+dnl DNS resolver macros
+AC_DEFUN([OL_RESOLVER_TRY],
+[if test $ol_cv_lib_resolver = no ; then
+ AC_CACHE_CHECK([for resolver link (]ifelse($2,,default,$2)[)],[$1],
+[
+ ol_RESOLVER_LIB=ifelse($2,,,$2)
+ ol_LIBS=$LIBS
+ LIBS="$ol_RESOLVER_LIB $LIBS"
+
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <netinet/in.h>
+#ifdef HAVE_ARPA_NAMESER_H
+# include <arpa/nameser.h>
+#endif
+#ifdef HAVE_RESOLV_H
+# include <resolv.h>
+#endif
+]], [[{
+ int len, status;
+ char *request = NULL;
+ unsigned char reply[64*1024];
+ unsigned char host[64*1024];
+ unsigned char *p;
+
+#ifdef NS_HFIXEDSZ
+ /* Bind 8/9 interface */
+ len = res_query(request, ns_c_in, ns_t_srv, reply, sizeof(reply));
+#else
+ /* Bind 4 interface */
+# ifndef T_SRV
+# define T_SRV 33
+# endif
+ len = res_query(request, C_IN, T_SRV, reply, sizeof(reply));
+#endif
+ p = reply;
+#ifdef NS_HFIXEDSZ
+ /* Bind 8/9 interface */
+ p += NS_HFIXEDSZ;
+#elif defined(HFIXEDSZ)
+ /* Bind 4 interface w/ HFIXEDSZ */
+ p += HFIXEDSZ;
+#else
+ /* Bind 4 interface w/o HFIXEDSZ */
+ p += sizeof(HEADER);
+#endif
+ status = dn_expand( reply, reply+len, p, host, sizeof(host));
+}]])],[$1=yes],[$1=no])
+
+ LIBS="$ol_LIBS"
+])
+
+ if test $$1 = yes ; then
+ ol_cv_lib_resolver=ifelse($2,,yes,$2)
+ fi
+fi
+])
+dnl --------------------------------------------------------------------
+dnl Try to locate appropriate library
+AC_DEFUN([OL_RESOLVER_LINK],
+[ol_cv_lib_resolver=no
+OL_RESOLVER_TRY(ol_cv_resolver_none)
+OL_RESOLVER_TRY(ol_cv_resolver_resolv,[-lresolv])
+OL_RESOLVER_TRY(ol_cv_resolver_bind,[-lbind])
+])
+dnl
+dnl ====================================================================
+dnl Check POSIX Thread version
+dnl
+dnl defines ol_cv_pthread_version to 4, 5, 6, 7, 8, 10, depending on the
+dnl version of the POSIX.4a Draft that is implemented.
+dnl 10 == POSIX.4a Final == POSIX.1c-1996 for our purposes.
+dnl Existence of pthread.h should be tested separately.
+dnl
+dnl tests:
+dnl pthread_detach() was dropped in Draft 8, it is present
+dnl in every other version
+dnl PTHREAD_CREATE_UNDETACHED is only in Draft 7, it was called
+dnl PTHREAD_CREATE_JOINABLE after that
+dnl pthread_attr_create was renamed to pthread_attr_init in Draft 6.
+dnl Draft 6-10 has _init, Draft 4-5 has _create.
+dnl pthread_attr_default was dropped in Draft 6, only 4 and 5 have it
+dnl PTHREAD_MUTEX_INITIALIZER was introduced in Draft 5. It's not
+dnl interesting to us because we don't try to statically
+dnl initialize mutexes. 5-10 has it.
+dnl
+dnl Draft 9 and 10 are equivalent for our purposes.
+dnl
+AC_DEFUN([OL_POSIX_THREAD_VERSION],
+[AC_CACHE_CHECK([POSIX thread version],[ol_cv_pthread_version],[
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+# include <pthread.h>
+ ]], [[
+ int i = PTHREAD_CREATE_JOINABLE;
+ ]])],[
+ AC_EGREP_HEADER(pthread_detach,pthread.h,
+ ol_cv_pthread_version=10, ol_cv_pthread_version=8)],[
+ AC_EGREP_CPP(draft7,[
+# include <pthread.h>
+# ifdef PTHREAD_CREATE_UNDETACHED
+ draft7
+# endif
+ ], ol_cv_pthread_version=7, [
+ AC_EGREP_HEADER(pthread_attr_init,pthread.h,
+ ol_cv_pthread_version=6, [
+ AC_EGREP_CPP(draft5,[
+# include <pthread.h>
+#ifdef PTHREAD_MUTEX_INITIALIZER
+ draft5
+#endif
+ ], ol_cv_pthread_version=5, ol_cv_pthread_version=4) ]) ]) ])
+])
+])dnl
+dnl
+dnl --------------------------------------------------------------------
+AC_DEFUN([OL_PTHREAD_TEST_INCLUDES], [[
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+]])
+AC_DEFUN([OL_PTHREAD_TEST_FUNCTION],[[
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+]])
+
+AC_DEFUN([OL_PTHREAD_TEST_PROGRAM],
+[AC_LANG_SOURCE([OL_PTHREAD_TEST_INCLUDES
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+OL_PTHREAD_TEST_FUNCTION
+}
+])])
+dnl --------------------------------------------------------------------
+AC_DEFUN([OL_PTHREAD_TRY], [# Pthread try link: $1 ($2)
+if test "$ol_link_threads" = no ; then
+ # try $1
+ AC_CACHE_CHECK([for pthread link with $1], [$2], [
+ # save the flags
+ ol_LIBS="$LIBS"
+ LIBS="$1 $LIBS"
+
+ AC_RUN_IFELSE([OL_PTHREAD_TEST_PROGRAM],
+ [$2=yes],
+ [$2=no],
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM(OL_PTHREAD_TEST_INCLUDES,
+ OL_PTHREAD_TEST_FUNCTION)],
+ [$2=yes], [$2=no])])
+
+ # restore the LIBS
+ LIBS="$ol_LIBS"
+ ])
+
+ if test $$2 = yes ; then
+ ol_link_pthreads="$1"
+ ol_link_threads=posix
+ fi
+fi
+])
+dnl
+dnl ====================================================================
+dnl Check GNU Pth pthread Header
+dnl
+dnl defines ol_cv_header linux_threads to 'yes' or 'no'
+dnl 'no' implies pthreads.h is not LinuxThreads or pthreads.h
+dnl doesn't exist. Existence of pthread.h should separately
+dnl checked.
+dnl
+AC_DEFUN([OL_HEADER_GNU_PTH_PTHREAD_H], [
+ AC_CACHE_CHECK([for GNU Pth pthread.h],
+ [ol_cv_header_gnu_pth_pthread_h],
+ [AC_EGREP_CPP(__gnu_pth__,
+ [#include <pthread.h>
+#ifdef _POSIX_THREAD_IS_GNU_PTH
+ __gnu_pth__;
+#endif
+],
+ [ol_cv_header_gnu_pth_pthread_h=yes],
+ [ol_cv_header_gnu_pth_pthread_h=no])
+ ])
+])dnl
+dnl ====================================================================
+dnl Check for NT Threads
+AC_DEFUN([OL_NT_THREADS], [
+ AC_CHECK_FUNC(_beginthread)
+
+ if test $ac_cv_func__beginthread = yes ; then
+ AC_DEFINE(HAVE_NT_THREADS,1,[if you have NT Threads])
+ ol_cv_nt_threads=yes
+ fi
+])
+dnl ====================================================================
+dnl Check LinuxThreads Header
+dnl
+dnl defines ol_cv_header linux_threads to 'yes' or 'no'
+dnl 'no' implies pthreads.h is not LinuxThreads or pthreads.h
+dnl doesn't exist. Existence of pthread.h should separately
+dnl checked.
+dnl
+AC_DEFUN([OL_HEADER_LINUX_THREADS], [
+ AC_CACHE_CHECK([for LinuxThreads pthread.h],
+ [ol_cv_header_linux_threads],
+ [AC_EGREP_CPP(pthread_kill_other_threads_np,
+ [#include <pthread.h>],
+ [ol_cv_header_linux_threads=yes],
+ [ol_cv_header_linux_threads=no])
+ ])
+ if test $ol_cv_header_linux_threads = yes; then
+ AC_DEFINE(HAVE_LINUX_THREADS,1,[if you have LinuxThreads])
+ fi
+])dnl
+dnl --------------------------------------------------------------------
+dnl Check LinuxThreads Implementation
+dnl
+dnl defines ol_cv_sys_linux_threads to 'yes' or 'no'
+dnl 'no' implies pthreads implementation is not LinuxThreads.
+dnl
+AC_DEFUN([OL_SYS_LINUX_THREADS], [
+ AC_CHECK_FUNCS(pthread_kill_other_threads_np)
+ AC_CACHE_CHECK([for LinuxThreads implementation],
+ [ol_cv_sys_linux_threads],
+ [ol_cv_sys_linux_threads=$ac_cv_func_pthread_kill_other_threads_np])
+])dnl
+dnl
+dnl --------------------------------------------------------------------
+dnl Check LinuxThreads consistency
+AC_DEFUN([OL_LINUX_THREADS], [
+ AC_REQUIRE([OL_HEADER_LINUX_THREADS])
+ AC_REQUIRE([OL_SYS_LINUX_THREADS])
+ AC_CACHE_CHECK([for LinuxThreads consistency], [ol_cv_linux_threads], [
+ if test $ol_cv_header_linux_threads = yes &&
+ test $ol_cv_sys_linux_threads = yes; then
+ ol_cv_linux_threads=yes
+ elif test $ol_cv_header_linux_threads = no &&
+ test $ol_cv_sys_linux_threads = no; then
+ ol_cv_linux_threads=no
+ else
+ ol_cv_linux_threads=error
+ fi
+ ])
+])dnl
+dnl
+dnl ====================================================================
+dnl Check for POSIX Regex
+AC_DEFUN([OL_POSIX_REGEX], [
+AC_CACHE_CHECK([for compatible POSIX regex],ol_cv_c_posix_regex,[
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#include <sys/types.h>
+#include <regex.h>
+static char *pattern, *string;
+main()
+{
+ int rc;
+ regex_t re;
+
+ pattern = "^A";
+
+ if(regcomp(&re, pattern, 0)) {
+ return -1;
+ }
+
+ string = "ALL MATCH";
+
+ rc = regexec(&re, string, 0, (void*)0, 0);
+
+ regfree(&re);
+
+ return rc;
+}]])],[ol_cv_c_posix_regex=yes],[ol_cv_c_posix_regex=no],[ol_cv_c_posix_regex=cross])])
+])
+dnl
+dnl ====================================================================
+dnl Check if toupper() requires islower() to be called first
+AC_DEFUN([OL_C_UPPER_LOWER],
+[AC_CACHE_CHECK([if toupper() requires islower()],ol_cv_c_upper_lower,[
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#include <ctype.h>
+main()
+{
+ if ('C' == toupper('C'))
+ exit(0);
+ else
+ exit(1);
+}]])],[ol_cv_c_upper_lower=no],[ol_cv_c_upper_lower=yes],[ol_cv_c_upper_lower=safe])])
+if test $ol_cv_c_upper_lower != no ; then
+ AC_DEFINE(C_UPPER_LOWER,1, [define if toupper() requires islower()])
+fi
+])
+dnl
+dnl ====================================================================
+dnl Error string checks
+dnl
+dnl Check for declaration of sys_errlist in one of stdio.h and errno.h.
+dnl Declaration of sys_errlist on BSD4.4 interferes with our declaration.
+dnl Reported by Keith Bostic.
+AC_DEFUN([OL_SYS_ERRLIST],
+[AC_CACHE_CHECK([existence of sys_errlist],ol_cv_have_sys_errlist,[
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <errno.h>]], [[char *c = (char *) *sys_errlist]])],[ol_cv_have_sys_errlist=yes],[ol_cv_have_sys_errlist=no])])
+if test $ol_cv_have_sys_errlist = yes ; then
+ AC_DEFINE(HAVE_SYS_ERRLIST,1,
+ [define if you actually have sys_errlist in your libs])
+ AC_CACHE_CHECK([declaration of sys_errlist],ol_cv_dcl_sys_errlist,[
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <stdio.h>
+#include <sys/types.h>
+#include <errno.h>
+#ifdef _WIN32
+#include <stdlib.h>
+#endif ]], [[char *c = (char *) *sys_errlist]])],[ol_cv_dcl_sys_errlist=yes],
+ [ol_cv_dcl_sys_errlist=no])])
+#
+ # It's possible (for near-UNIX clones) that sys_errlist doesn't exist
+ if test $ol_cv_dcl_sys_errlist = no ; then
+ AC_DEFINE(DECL_SYS_ERRLIST,1,
+ [define if sys_errlist is not declared in stdio.h or errno.h])
+ fi
+fi
+])dnl
+dnl
+dnl ====================================================================
+dnl glibc supplies a non-standard strerror_r if _GNU_SOURCE is defined.
+dnl It's actually preferable to the POSIX version, if available.
+AC_DEFUN([OL_NONPOSIX_STRERROR_R],
+[AC_CACHE_CHECK([non-posix strerror_r],ol_cv_nonposix_strerror_r,[
+ AC_EGREP_CPP(strerror_r,[#include <string.h>],
+ ol_decl_strerror_r=yes, ol_decl_strerror_r=no)dnl
+
+ if test $ol_decl_strerror_r = yes ; then
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <string.h>]], [[ /* from autoconf 2.59 */
+ char buf[100];
+ char x = *strerror_r (0, buf, sizeof buf);
+ char *p = strerror_r (0, buf, sizeof buf);
+ ]])],[ol_cv_nonposix_strerror_r=yes],[ol_cv_nonposix_strerror_r=no])
+ else
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
+ main() {
+ char buf[100];
+ buf[0] = 0;
+ strerror_r( 1, buf, sizeof buf );
+ exit( buf[0] == 0 );
+ }
+ ]])],[ol_cv_nonposix_strerror_r=yes],[ol_cv_nonposix_strerror_r=no],[ol_cv_nonposix_strerror_r=no])
+ fi
+ ])
+if test $ol_cv_nonposix_strerror_r = yes ; then
+ AC_DEFINE(HAVE_NONPOSIX_STRERROR_R,1,
+ [define if strerror_r returns char* instead of int])
+fi
+])dnl
+dnl
+AC_DEFUN([OL_STRERROR],
+[AC_CHECK_FUNCS(strerror strerror_r)
+ol_cv_func_strerror_r=no
+if test "${ac_cv_func_strerror_r}" = yes ; then
+ OL_NONPOSIX_STRERROR_R
+elif test "${ac_cv_func_strerror}" = no ; then
+ OL_SYS_ERRLIST
+fi
+])dnl
+dnl ====================================================================
+dnl Early MIPS compilers (used in Ultrix 4.2) don't like
+dnl "int x; int *volatile a = &x; *a = 0;"
+dnl -- borrowed from PDKSH
+AC_DEFUN([OL_C_VOLATILE],
+ [AC_CACHE_CHECK(if compiler understands volatile, ol_cv_c_volatile,
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[int x, y, z;]], [[volatile int a; int * volatile b = x ? &y : &z;
+ /* Older MIPS compilers (eg., in Ultrix 4.2) don't like *b = 0 */
+ *b = 0;]])],[ol_cv_c_volatile=yes],[ol_cv_c_volatile=no])])
+ if test $ol_cv_c_volatile = yes; then
+ :
+ else
+ AC_DEFINE(volatile,,[define as empty if volatile is not supported])
+ fi
+ ])dnl
+dnl
+dnl ====================================================================
+dnl Look for fetch(3)
+AC_DEFUN([OL_LIB_FETCH],
+[ol_LIBS=$LIBS
+LIBS="-lfetch -lcom_err $LIBS"
+AC_CACHE_CHECK([fetch(3) library],ol_cv_lib_fetch,[
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#include <stdio.h>
+#include <fetch.h>]], [[struct url *u = fetchParseURL("file:///"); ]])],[ol_cv_lib_fetch=yes],[ol_cv_lib_fetch=no])])
+LIBS=$ol_LIBS
+if test $ol_cv_lib_fetch != no ; then
+ ol_link_fetch="-lfetch -lcom_err"
+ AC_DEFINE(HAVE_FETCH,1,
+ [define if you actually have FreeBSD fetch(3)])
+fi
+])dnl
+dnl
+dnl ====================================================================
+dnl Define inet_aton is available
+AC_DEFUN([OL_FUNC_INET_ATON],
+ [AC_CACHE_CHECK([for inet_aton()], ol_cv_func_inet_aton,
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+# ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+# endif
+# include <netinet/in.h>
+# ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+# endif
+#endif
+]], [[struct in_addr in;
+int rc = inet_aton( "255.255.255.255", &in );]])],[ol_cv_func_inet_aton=yes],[ol_cv_func_inet_aton=no])])
+ if test $ol_cv_func_inet_aton != no; then
+ AC_DEFINE(HAVE_INET_ATON, 1,
+ [define to you inet_aton(3) is available])
+ fi
+ ])dnl
+dnl
+dnl ====================================================================
+dnl check no of arguments for ctime_r
+AC_DEFUN([OL_FUNC_CTIME_R_NARGS],
+ [AC_CACHE_CHECK(number of arguments of ctime_r, ol_cv_func_ctime_r_nargs,
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <time.h>]], [[time_t ti; char *buffer; ctime_r(&ti,buffer,32);]])],[ol_cv_func_ctime_r_nargs3=yes],[ol_cv_func_ctime_r_nargs3=no])
+
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <time.h>]], [[time_t ti; char *buffer; ctime_r(&ti,buffer);]])],[ol_cv_func_ctime_r_nargs2=yes],[ol_cv_func_ctime_r_nargs2=no])
+
+ if test $ol_cv_func_ctime_r_nargs3 = yes &&
+ test $ol_cv_func_ctime_r_nargs2 = no ; then
+
+ ol_cv_func_ctime_r_nargs=3
+
+ elif test $ol_cv_func_ctime_r_nargs3 = no &&
+ test $ol_cv_func_ctime_r_nargs2 = yes ; then
+
+ ol_cv_func_ctime_r_nargs=2
+
+ else
+ ol_cv_func_ctime_r_nargs=0
+ fi
+ ])
+
+ if test $ol_cv_func_ctime_r_nargs -gt 1 ; then
+ AC_DEFINE_UNQUOTED(CTIME_R_NARGS, $ol_cv_func_ctime_r_nargs,
+ [set to the number of arguments ctime_r() expects])
+ fi
+])dnl
+dnl
+dnl --------------------------------------------------------------------
+dnl check return type of ctime_r()
+AC_DEFUN([OL_FUNC_CTIME_R_TYPE],
+ [AC_CACHE_CHECK(return type of ctime_r, ol_cv_func_ctime_r_type,
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <time.h>]], [[extern int (ctime_r)();]])],[ol_cv_func_ctime_r_type="int"],[ol_cv_func_ctime_r_type="charp"])
+ ])
+ if test $ol_cv_func_ctime_r_type = "int" ; then
+ AC_DEFINE(CTIME_R_RETURNS_INT,1, [define if ctime_r() returns int])
+ fi
+])dnl
+dnl ====================================================================
+dnl check no of arguments for gethostbyname_r
+AC_DEFUN([OL_FUNC_GETHOSTBYNAME_R_NARGS],
+ [AC_CACHE_CHECK(number of arguments of gethostbyname_r,
+ ol_cv_func_gethostbyname_r_nargs,
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#define BUFSIZE (sizeof(struct hostent)+10)]], [[struct hostent hent; char buffer[BUFSIZE];
+ int bufsize=BUFSIZE;int h_errno;
+ (void)gethostbyname_r("segovia.cs.purdue.edu", &hent,
+ buffer, bufsize, &h_errno);]])],[ol_cv_func_gethostbyname_r_nargs5=yes],[ol_cv_func_gethostbyname_r_nargs5=no])
+
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#define BUFSIZE (sizeof(struct hostent)+10)]], [[struct hostent hent;struct hostent *rhent;
+ char buffer[BUFSIZE];
+ int bufsize=BUFSIZE;int h_errno;
+ (void)gethostbyname_r("localhost", &hent, buffer, bufsize,
+ &rhent, &h_errno);]])],[ol_cv_func_gethostbyname_r_nargs6=yes],[ol_cv_func_gethostbyname_r_nargs6=no])
+
+ if test $ol_cv_func_gethostbyname_r_nargs5 = yes &&
+ test $ol_cv_func_gethostbyname_r_nargs6 = no ; then
+
+ ol_cv_func_gethostbyname_r_nargs=5
+
+ elif test $ol_cv_func_gethostbyname_r_nargs5 = no &&
+ test $ol_cv_func_gethostbyname_r_nargs6 = yes ; then
+
+ ol_cv_func_gethostbyname_r_nargs=6
+
+ else
+ ol_cv_func_gethostbyname_r_nargs=0
+ fi
+ ])
+ if test $ol_cv_func_gethostbyname_r_nargs -gt 1 ; then
+ AC_DEFINE_UNQUOTED(GETHOSTBYNAME_R_NARGS,
+ $ol_cv_func_gethostbyname_r_nargs,
+ [set to the number of arguments gethostbyname_r() expects])
+ fi
+])dnl
+dnl
+dnl check no of arguments for gethostbyaddr_r
+AC_DEFUN([OL_FUNC_GETHOSTBYADDR_R_NARGS],
+ [AC_CACHE_CHECK(number of arguments of gethostbyaddr_r,
+ [ol_cv_func_gethostbyaddr_r_nargs],
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#define BUFSIZE (sizeof(struct hostent)+10)]], [[struct hostent hent; char buffer[BUFSIZE];
+ struct in_addr add;
+ size_t alen=sizeof(struct in_addr);
+ int bufsize=BUFSIZE;int h_errno;
+ (void)gethostbyaddr_r( (void *)&(add.s_addr),
+ alen, AF_INET, &hent, buffer, bufsize, &h_errno);]])],[ol_cv_func_gethostbyaddr_r_nargs7=yes],[ol_cv_func_gethostbyaddr_r_nargs7=no])
+
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#define BUFSIZE (sizeof(struct hostent)+10)]], [[struct hostent hent;
+ struct hostent *rhent; char buffer[BUFSIZE];
+ struct in_addr add;
+ size_t alen=sizeof(struct in_addr);
+ int bufsize=BUFSIZE;int h_errno;
+ (void)gethostbyaddr_r( (void *)&(add.s_addr),
+ alen, AF_INET, &hent, buffer, bufsize,
+ &rhent, &h_errno);]])],[ol_cv_func_gethostbyaddr_r_nargs8=yes],[ol_cv_func_gethostbyaddr_r_nargs8=no])
+
+ if test $ol_cv_func_gethostbyaddr_r_nargs7 = yes &&
+ test $ol_cv_func_gethostbyaddr_r_nargs8 = no ; then
+
+ ol_cv_func_gethostbyaddr_r_nargs=7
+
+ elif test $ol_cv_func_gethostbyaddr_r_nargs7 = no &&
+ test $ol_cv_func_gethostbyaddr_r_nargs8 = yes ; then
+
+ ol_cv_func_gethostbyaddr_r_nargs=8
+
+ else
+ ol_cv_func_gethostbyaddr_r_nargs=0
+ fi
+ ])
+ if test $ol_cv_func_gethostbyaddr_r_nargs -gt 1 ; then
+ AC_DEFINE_UNQUOTED(GETHOSTBYADDR_R_NARGS,
+ $ol_cv_func_gethostbyaddr_r_nargs,
+ [set to the number of arguments gethostbyaddr_r() expects])
+ fi
+])dnl
+dnl
+dnl --------------------------------------------------------------------
+dnl Check for Cyrus SASL version compatibility
+AC_DEFUN([OL_SASL_COMPAT],
+[AC_CACHE_CHECK([Cyrus SASL library version], [ol_cv_sasl_compat],[
+ AC_EGREP_CPP(__sasl_compat,[
+#ifdef HAVE_SASL_SASL_H
+#include <sasl/sasl.h>
+#else
+#include <sasl.h>
+#endif
+
+/* Require 2.1.15+ */
+#if SASL_VERSION_MAJOR == 2 && SASL_VERSION_MINOR > 1
+ char *__sasl_compat = "2.2+ or better okay (we guess)";
+#elif SASL_VERSION_MAJOR == 2 && SASL_VERSION_MINOR == 1 \
+ && SASL_VERSION_STEP >=15
+ char *__sasl_compat = "2.1.15+ or better okay";
+#endif
+ ], [ol_cv_sasl_compat=yes], [ol_cv_sasl_compat=no])])
+])
diff --git a/build/rules.mk b/build/rules.mk
new file mode 100644
index 0000000..f7837e8
--- /dev/null
+++ b/build/rules.mk
@@ -0,0 +1,35 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##---------------------------------------------------------------------------
+#
+# Makefile Template for Programs
+#
+
+all-common: $(PROGRAMS) FORCE
+
+clean-common: FORCE
+ $(RM) $(PROGRAMS) $(XPROGRAMS) $(XSRCS) *.o *.lo a.out core *.core \
+ .libs/* *.exe
+
+depend-common: FORCE
+ $(MKDEP) $(DEFS) $(DEFINES) $(SRCS)
+
+lint: FORCE
+ $(LINT) $(DEFS) $(DEFINES) $(SRCS)
+
+lint5: FORCE
+ $(5LINT) $(DEFS) $(DEFINES) $(SRCS)
+
+Makefile: $(top_srcdir)/build/rules.mk
+
diff --git a/build/shtool b/build/shtool
new file mode 100755
index 0000000..16a6164
--- /dev/null
+++ b/build/shtool
@@ -0,0 +1,1453 @@
+#!/bin/sh
+##
+## GNU shtool -- The GNU Portable Shell Tool
+## Copyright (c) 1994-2008 Ralf S. Engelschall <rse@engelschall.com>
+##
+## See http://www.gnu.org/software/shtool/ for more information.
+## See ftp://ftp.gnu.org/gnu/shtool/ for latest version.
+##
+## Version: 2.0.8 (18-Jul-2008)
+## Contents: 6/19 available modules
+##
+
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+## General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+## USA, or contact Ralf S. Engelschall <rse@engelschall.com>.
+##
+## NOTICE: Given that you include this file verbatim into your own
+## source tree, you are justified in saying that it remains separate
+## from your package, and that this way you are simply just using GNU
+## shtool. So, in this situation, there is no requirement that your
+## package itself is licensed under the GNU General Public License in
+## order to take advantage of GNU shtool.
+##
+
+##
+## Usage: shtool [<options>] [<cmd-name> [<cmd-options>] [<cmd-args>]]
+##
+## Available commands:
+## echo Print string with optional construct expansion
+## move Move files with simultaneous substitution
+## install Install a program, script or datafile
+## mkdir Make one or more directories
+## mkln Make link with calculation of relative paths
+## subst Apply sed(1) substitution operations
+##
+## Not available commands (because module was not built-in):
+## mdate Pretty-print modification time of a file or dir
+## table Pretty-print a field-separated list as a table
+## prop Display progress with a running propeller
+## mkshadow Make a shadow tree through symbolic links
+## fixperm Fix file permissions inside a source tree
+## rotate Logfile rotation
+## tarball Roll distribution tarballs
+## platform Platform Identification Utility
+## arx Extended archive command
+## slo Separate linker options by library class
+## scpp Sharing C Pre-Processor
+## version Maintain a version information file
+## path Deal with program paths
+##
+
+# maximum Bourne-Shell compatibility
+if [ ".$ZSH_VERSION" != . ] && (emulate sh) >/dev/null 2>&1; then
+ # reconfigure zsh(1)
+ emulate sh
+ NULLCMD=:
+ alias -g '${1+"$@"}'='"$@"'
+elif [ ".$BASH_VERSION" != . ] && (set -o posix) >/dev/null 2>&1; then
+ # reconfigure bash(1)
+ set -o posix
+fi
+
+# maximum independence of NLS nuisances
+for var in \
+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+ LC_TELEPHONE LC_TIME
+do
+ if (set +x; test -z "`(eval $var=C; export $var) 2>&1`"); then
+ eval $var=C; export $var
+ else
+ unset $var
+ fi
+done
+
+# initial command line handling
+if [ $# -eq 0 ]; then
+ echo "$0:Error: invalid command line" 1>&2
+ echo "$0:Hint: run \`$0 -h' for usage" 1>&2
+ exit 1
+fi
+if [ ".$1" = ".-h" ] || [ ".$1" = ".--help" ]; then
+ echo "This is GNU shtool, version 2.0.8 (18-Jul-2008)"
+ echo 'Copyright (c) 1994-2008 Ralf S. Engelschall <rse@engelschall.com>'
+ echo 'Report bugs to <bug-shtool@gnu.org>'
+ echo ''
+ echo 'Usage: shtool [<options>] [<cmd-name> [<cmd-options>] [<cmd-args>]]'
+ echo ''
+ echo 'Available global <options>:'
+ echo ' -v, --version display shtool version information'
+ echo ' -h, --help display shtool usage help page (this one)'
+ echo ' -d, --debug display shell trace information'
+ echo ' -r, --recreate recreate this shtool script via shtoolize'
+ echo ''
+ echo 'Available <cmd-name> [<cmd-options>] [<cmd-args>]:'
+ echo ' echo [-n|--newline] [-e|--expand] [<string> ...]'
+ echo ' move [-v|--verbose] [-t|--trace] [-e|--expand] [-p|--preserve]'
+ echo ' <src-file> <dst-file>'
+ echo ' install [-v|--verbose] [-t|--trace] [-d|--mkdir] [-c|--copy]'
+ echo ' [-C|--compare-copy] [-s|--strip] [-m|--mode <mode>]'
+ echo ' [-o|--owner <owner>] [-g|--group <group>] [-e|--exec'
+ echo ' <sed-cmd>] <file> [<file> ...] <path>'
+ echo ' mkdir [-t|--trace] [-f|--force] [-p|--parents] [-m|--mode'
+ echo ' <mode>] [-o|--owner <owner>] [-g|--group <group>] <dir>'
+ echo ' [<dir> ...]'
+ echo ' mkln [-t|--trace] [-f|--force] [-s|--symbolic] <src-path>'
+ echo ' [<src-path> ...] <dst-path>'
+ echo ' subst [-v|--verbose] [-t|--trace] [-n|--nop] [-w|--warning]'
+ echo ' [-q|--quiet] [-s|--stealth] [-i|--interactive] [-b|--backup'
+ echo ' <ext>] [-e|--exec <cmd>] [-f|--file <cmd-file>] [<file>]'
+ echo ' [...]'
+ echo ''
+ echo 'Not available <cmd-name> (because module was not built-in):'
+ echo ' mdate [-n|--newline] [-z|--zero] [-s|--shorten] [-d|--digits]'
+ echo ' [-f|--field-sep <str>] [-o|--order <spec>] <path>'
+ echo ' table [-F|--field-sep <sep>] [-w|--width <width>] [-c|--columns'
+ echo ' <cols>] [-s|--strip <strip>] <str><sep><str>...'
+ echo ' prop [-p|--prefix <str>]'
+ echo ' mkshadow [-v|--verbose] [-t|--trace] [-a|--all] <src-dir> <dst-dir>'
+ echo ' fixperm [-v|--verbose] [-t|--trace] <path> [<path> ...]'
+ echo ' rotate [-v|--verbose] [-t|--trace] [-f|--force] [-n|--num-files'
+ echo ' <count>] [-s|--size <size>] [-c|--copy] [-r|--remove]'
+ echo ' [-a|--archive-dir <dir>] [-z|--compress [<tool>:]<level>]'
+ echo ' [-b|--background] [-d|--delay] [-p|--pad <len>] [-m|--mode'
+ echo ' <mode>] [-o|--owner <owner>] [-g|--group <group>] [-M|--migrate'
+ echo ' <cmd>] [-P|--prolog <cmd>] [-E|--epilog <cmd>] <file> [...]'
+ echo ' tarball [-t|--trace] [-v|--verbose] [-o|--output <tarball>]'
+ echo ' [-c|--compress <prog>] [-d|--directory <dir>] [-u|--user'
+ echo ' <user>] [-g|--group <group>] [-e|--exclude <pattern>]'
+ echo ' <path> [<path> ...]'
+ echo ' platform [-F|--format <format>] [-S|--sep <string>] [-C|--conc'
+ echo ' <string>] [-L|--lower] [-U|--upper] [-v|--verbose]'
+ echo ' [-c|--concise] [-n|--no-newline] [-t|--type <type>]'
+ echo ' [-V|--version] [-h|--help]'
+ echo ' arx [-t|--trace] [-C|--command <cmd>] <op> <archive> [<file>'
+ echo ' ...]'
+ echo ' slo [-p|--prefix <str>] -- -L<dir> -l<lib> [-L<dir> -l<lib>'
+ echo ' ...]'
+ echo ' scpp [-v|--verbose] [-p|--preserve] [-f|--filter <filter>]'
+ echo ' [-o|--output <ofile>] [-t|--template <tfile>] [-M|--mark'
+ echo ' <mark>] [-D|--define <dname>] [-C|--class <cname>]'
+ echo ' <file> [<file> ...]'
+ echo ' version [-l|--language <lang>] [-n|--name <name>] [-p|--prefix'
+ echo ' <prefix>] [-s|--set <version>] [-e|--edit] [-i|--increase'
+ echo ' <knob>] [-d|--display <type>] <file>'
+ echo ' path [-s|--suppress] [-r|--reverse] [-d|--dirname] [-b|--basename]'
+ echo ' [-m|--magic] [-p|--path <path>] <str> [<str> ...]'
+ echo ''
+ exit 0
+fi
+if [ ".$1" = ".-v" ] || [ ".$1" = ".--version" ]; then
+ echo "GNU shtool 2.0.8 (18-Jul-2008)"
+ exit 0
+fi
+if [ ".$1" = ".-r" ] || [ ".$1" = ".--recreate" ]; then
+ shtoolize -oshtool echo move install mkdir mkln subst
+ exit 0
+fi
+if [ ".$1" = ".-d" ] || [ ".$1" = ".--debug" ]; then
+ shift
+ set -x
+fi
+name=`echo "$0" | sed -e 's;.*/\([^/]*\)$;\1;' -e 's;-sh$;;' -e 's;\.sh$;;'`
+case "$name" in
+ echo|move|install|mkdir|mkln|subst )
+ # implicit tool command selection
+ tool="$name"
+ ;;
+ * )
+ # explicit tool command selection
+ tool="$1"
+ shift
+ ;;
+esac
+arg_spec=""
+opt_spec=""
+gen_tmpfile=no
+
+##
+## DISPATCH INTO SCRIPT PROLOG
+##
+
+case $tool in
+ echo )
+ str_tool="echo"
+ str_usage="[-n|--newline] [-e|--expand] [<string> ...]"
+ arg_spec="0+"
+ opt_spec="n.e."
+ opt_alias="n:newline,e:expand"
+ opt_n=no
+ opt_e=no
+ ;;
+ move )
+ str_tool="move"
+ str_usage="[-v|--verbose] [-t|--trace] [-e|--expand] [-p|--preserve] <src-file> <dst-file>"
+ arg_spec="2="
+ opt_spec="v.t.e.p."
+ opt_alias="v:verbose,t:trace,e:expand,p:preserve"
+ opt_v=no
+ opt_t=no
+ opt_e=no
+ opt_p=no
+ ;;
+ install )
+ str_tool="install"
+ str_usage="[-v|--verbose] [-t|--trace] [-d|--mkdir] [-c|--copy] [-C|--compare-copy] [-s|--strip] [-m|--mode <mode>] [-o|--owner <owner>] [-g|--group <group>] [-e|--exec <sed-cmd>] <file> [<file> ...] <path>"
+ arg_spec="1+"
+ opt_spec="v.t.d.c.C.s.m:o:g:e+"
+ opt_alias="v:verbose,t:trace,d:mkdir,c:copy,C:compare-copy,s:strip,m:mode,o:owner,g:group,e:exec"
+ opt_v=no
+ opt_t=no
+ opt_d=no
+ opt_c=no
+ opt_C=no
+ opt_s=no
+ opt_m="0755"
+ opt_o=""
+ opt_g=""
+ opt_e=""
+ ;;
+ mkdir )
+ str_tool="mkdir"
+ str_usage="[-t|--trace] [-f|--force] [-p|--parents] [-m|--mode <mode>] [-o|--owner <owner>] [-g|--group <group>] <dir> [<dir> ...]"
+ arg_spec="1+"
+ opt_spec="t.f.p.m:o:g:"
+ opt_alias="t:trace,f:force,p:parents,m:mode,o:owner,g:group"
+ opt_t=no
+ opt_f=no
+ opt_p=no
+ opt_m=""
+ opt_o=""
+ opt_g=""
+ ;;
+ mkln )
+ str_tool="mkln"
+ str_usage="[-t|--trace] [-f|--force] [-s|--symbolic] <src-path> [<src-path> ...] <dst-path>"
+ arg_spec="2+"
+ opt_spec="t.f.s."
+ opt_alias="t:trace,f:force,s:symbolic"
+ opt_t=no
+ opt_f=no
+ opt_s=no
+ ;;
+ subst )
+ str_tool="subst"
+ str_usage="[-v|--verbose] [-t|--trace] [-n|--nop] [-w|--warning] [-q|--quiet] [-s|--stealth] [-i|--interactive] [-b|--backup <ext>] [-e|--exec <cmd>] [-f|--file <cmd-file>] [<file>] [...]"
+ gen_tmpfile=yes
+ arg_spec="0+"
+ opt_spec="v.t.n.w.q.s.i.b:e+f:"
+ opt_alias="v:verbose,t:trace,n:nop,w:warning,q:quiet,s:stealth,i:interactive,b:backup,e:exec,f:file"
+ opt_v=no
+ opt_t=no
+ opt_n=no
+ opt_w=no
+ opt_q=no
+ opt_s=no
+ opt_i=no
+ opt_b=""
+ opt_e=""
+ opt_f=""
+ ;;
+ -* )
+ echo "$0:Error: unknown option \`$tool'" 2>&1
+ echo "$0:Hint: run \`$0 -h' for usage" 2>&1
+ exit 1
+ ;;
+ * )
+ echo "$0:Error: unknown command \`$tool'" 2>&1
+ echo "$0:Hint: run \`$0 -h' for usage" 2>&1
+ exit 1
+ ;;
+esac
+
+##
+## COMMON UTILITY CODE
+##
+
+# commonly used ASCII values
+ASC_TAB=" "
+ASC_NL="
+"
+
+# determine name of tool
+if [ ".$tool" != . ]; then
+ # used inside shtool script
+ toolcmd="$0 $tool"
+ toolcmdhelp="shtool $tool"
+ msgprefix="shtool:$tool"
+else
+ # used as standalone script
+ toolcmd="$0"
+ toolcmdhelp="sh $0"
+ msgprefix="$str_tool"
+fi
+
+# parse argument specification string
+eval `echo $arg_spec |\
+ sed -e 's/^\([0-9]*\)\([+=]\)/arg_NUMS=\1; arg_MODE=\2/'`
+
+# parse option specification string
+eval `echo h.$opt_spec |\
+ sed -e 's/\([a-zA-Z0-9]\)\([.:+]\)/opt_MODE_\1=\2;/g'`
+
+# parse option alias string
+eval `echo h:help,$opt_alias |\
+ sed -e 's/-/_/g' -e 's/\([a-zA-Z0-9]\):\([^,]*\),*/opt_ALIAS_\2=\1;/g'`
+
+# iterate over argument line
+opt_PREV=''
+while [ $# -gt 0 ]; do
+ # special option stops processing
+ if [ ".$1" = ".--" ]; then
+ shift
+ break
+ fi
+
+ # determine option and argument
+ opt_ARG_OK=no
+ if [ ".$opt_PREV" != . ]; then
+ # merge previous seen option with argument
+ opt_OPT="$opt_PREV"
+ opt_ARG="$1"
+ opt_ARG_OK=yes
+ opt_PREV=''
+ else
+ # split argument into option and argument
+ case "$1" in
+ --[a-zA-Z0-9]*=*)
+ eval `echo "x$1" |\
+ sed -e 's/^x--\([a-zA-Z0-9-]*\)=\(.*\)$/opt_OPT="\1";opt_ARG="\2"/'`
+ opt_STR=`echo $opt_OPT | sed -e 's/-/_/g'`
+ eval "opt_OPT=\${opt_ALIAS_${opt_STR}-${opt_OPT}}"
+ ;;
+ --[a-zA-Z0-9]*)
+ opt_OPT=`echo "x$1" | cut -c4-`
+ opt_STR=`echo $opt_OPT | sed -e 's/-/_/g'`
+ eval "opt_OPT=\${opt_ALIAS_${opt_STR}-${opt_OPT}}"
+ opt_ARG=''
+ ;;
+ -[a-zA-Z0-9]*)
+ eval `echo "x$1" |\
+ sed -e 's/^x-\([a-zA-Z0-9]\)/opt_OPT="\1";/' \
+ -e 's/";\(.*\)$/"; opt_ARG="\1"/'`
+ ;;
+ -[a-zA-Z0-9])
+ opt_OPT=`echo "x$1" | cut -c3-`
+ opt_ARG=''
+ ;;
+ *)
+ break
+ ;;
+ esac
+ fi
+
+ # eat up option
+ shift
+
+ # determine whether option needs an argument
+ eval "opt_MODE=\$opt_MODE_${opt_OPT}"
+ if [ ".$opt_ARG" = . ] && [ ".$opt_ARG_OK" != .yes ]; then
+ if [ ".$opt_MODE" = ".:" ] || [ ".$opt_MODE" = ".+" ]; then
+ opt_PREV="$opt_OPT"
+ continue
+ fi
+ fi
+
+ # process option
+ case $opt_MODE in
+ '.' )
+ # boolean option
+ eval "opt_${opt_OPT}=yes"
+ ;;
+ ':' )
+ # option with argument (multiple occurrences override)
+ eval "opt_${opt_OPT}=\"\$opt_ARG\""
+ ;;
+ '+' )
+ # option with argument (multiple occurrences append)
+ eval "opt_${opt_OPT}=\"\$opt_${opt_OPT}\${ASC_NL}\$opt_ARG\""
+ ;;
+ * )
+ echo "$msgprefix:Error: unknown option: \`$opt_OPT'" 1>&2
+ echo "$msgprefix:Hint: run \`$toolcmdhelp -h' or \`man shtool' for details" 1>&2
+ exit 1
+ ;;
+ esac
+done
+if [ ".$opt_PREV" != . ]; then
+ echo "$msgprefix:Error: missing argument to option \`$opt_PREV'" 1>&2
+ echo "$msgprefix:Hint: run \`$toolcmdhelp -h' or \`man shtool' for details" 1>&2
+ exit 1
+fi
+
+# process help option
+if [ ".$opt_h" = .yes ]; then
+ echo "Usage: $toolcmdhelp $str_usage"
+ exit 0
+fi
+
+# complain about incorrect number of arguments
+case $arg_MODE in
+ '=' )
+ if [ $# -ne $arg_NUMS ]; then
+ echo "$msgprefix:Error: invalid number of arguments (exactly $arg_NUMS expected)" 1>&2
+ echo "$msgprefix:Hint: run \`$toolcmd -h' or \`man shtool' for details" 1>&2
+ exit 1
+ fi
+ ;;
+ '+' )
+ if [ $# -lt $arg_NUMS ]; then
+ echo "$msgprefix:Error: invalid number of arguments (at least $arg_NUMS expected)" 1>&2
+ echo "$msgprefix:Hint: run \`$toolcmd -h' or \`man shtool' for details" 1>&2
+ exit 1
+ fi
+ ;;
+esac
+
+# establish a temporary file on request
+if [ ".$gen_tmpfile" = .yes ]; then
+ # create (explicitly) secure temporary directory
+ if [ ".$TMPDIR" != . ]; then
+ tmpdir="$TMPDIR"
+ elif [ ".$TEMPDIR" != . ]; then
+ tmpdir="$TEMPDIR"
+ else
+ tmpdir="/tmp"
+ fi
+ tmpdir="$tmpdir/.shtool.$$"
+ ( umask 077
+ rm -rf "$tmpdir" >/dev/null 2>&1 || true
+ mkdir "$tmpdir" >/dev/null 2>&1
+ if [ $? -ne 0 ]; then
+ echo "$msgprefix:Error: failed to create temporary directory \`$tmpdir'" 1>&2
+ exit 1
+ fi
+ )
+
+ # create (implicitly) secure temporary file
+ tmpfile="$tmpdir/shtool.tmp"
+ touch "$tmpfile"
+fi
+
+# utility function: map string to lower case
+util_lower () {
+ echo "$1" | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'
+}
+
+# utility function: map string to upper case
+util_upper () {
+ echo "$1" | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+}
+
+# cleanup procedure
+shtool_exit () {
+ rc="$1"
+ if [ ".$gen_tmpfile" = .yes ]; then
+ rm -rf "$tmpdir" >/dev/null 2>&1 || true
+ fi
+ exit $rc
+}
+
+##
+## DISPATCH INTO SCRIPT BODY
+##
+
+case $tool in
+
+echo )
+ ##
+ ## echo -- Print string with optional construct expansion
+ ## Copyright (c) 1998-2008 Ralf S. Engelschall <rse@engelschall.com>
+ ##
+
+ text="$*"
+
+ # check for broken escape sequence expansion
+ seo=''
+ bytes=`echo '\1' | wc -c | awk '{ printf("%s", $1); }'`
+ if [ ".$bytes" != .3 ]; then
+ bytes=`echo -E '\1' | wc -c | awk '{ printf("%s", $1); }'`
+ if [ ".$bytes" = .3 ]; then
+ seo='-E'
+ fi
+ fi
+
+ # check for existing -n option (to suppress newline)
+ minusn=''
+ bytes=`echo -n 123 2>/dev/null | wc -c | awk '{ printf("%s", $1); }'`
+ if [ ".$bytes" = .3 ]; then
+ minusn='-n'
+ fi
+
+ # determine terminal bold sequence
+ term_bold=''
+ term_norm=''
+ if [ ".$opt_e" = .yes ] && [ ".`echo $text | grep '%[Bb]'`" != . ]; then
+ case $TERM in
+ # for the most important terminal types we directly know the sequences
+ xterm|xterm*|vt220|vt220*)
+ term_bold=`awk 'BEGIN { printf("%c%c%c%c", 27, 91, 49, 109); }' </dev/null 2>/dev/null`
+ term_norm=`awk 'BEGIN { printf("%c%c%c", 27, 91, 109); }' </dev/null 2>/dev/null`
+ ;;
+ vt100|vt100*|cygwin)
+ term_bold=`awk 'BEGIN { printf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0); }' </dev/null 2>/dev/null`
+ term_norm=`awk 'BEGIN { printf("%c%c%c%c%c", 27, 91, 109, 0, 0); }' </dev/null 2>/dev/null`
+ ;;
+ # for all others, we try to use a possibly existing `tput' or `tcout' utility
+ * )
+ paths=`echo $PATH | sed -e 's/:/ /g'`
+ for tool in tput tcout; do
+ for dir in $paths; do
+ if [ -r "$dir/$tool" ]; then
+ for seq in bold md smso; do # 'smso' is last
+ bold="`$dir/$tool $seq 2>/dev/null`"
+ if [ ".$bold" != . ]; then
+ term_bold="$bold"
+ break
+ fi
+ done
+ if [ ".$term_bold" != . ]; then
+ for seq in sgr0 me rmso init reset; do # 'reset' is last
+ norm="`$dir/$tool $seq 2>/dev/null`"
+ if [ ".$norm" != . ]; then
+ term_norm="$norm"
+ break
+ fi
+ done
+ fi
+ break
+ fi
+ done
+ if [ ".$term_bold" != . ] && [ ".$term_norm" != . ]; then
+ break;
+ fi
+ done
+ ;;
+ esac
+ if [ ".$term_bold" = . ] || [ ".$term_norm" = . ]; then
+ echo "$msgprefix:Warning: unable to determine terminal sequence for bold mode" 1>&2
+ term_bold=''
+ term_norm=''
+ fi
+ fi
+
+ # determine user name
+ username=''
+ if [ ".$opt_e" = .yes ] && [ ".`echo $text | grep '%[uUgG]'`" != . ]; then
+ username="`(id -un) 2>/dev/null`"
+ if [ ".$username" = . ]; then
+ str="`(id) 2>/dev/null`"
+ if [ ".`echo $str | grep '^uid[ ]*=[ ]*[0-9]*('`" != . ]; then
+ username=`echo $str | sed -e 's/^uid[ ]*=[ ]*[0-9]*(//' -e 's/).*$//'`
+ fi
+ if [ ".$username" = . ]; then
+ username="$LOGNAME"
+ if [ ".$username" = . ]; then
+ username="$USER"
+ if [ ".$username" = . ]; then
+ username="`(whoami) 2>/dev/null |\
+ awk '{ printf("%s", $1); }'`"
+ if [ ".$username" = . ]; then
+ username="`(who am i) 2>/dev/null |\
+ awk '{ printf("%s", $1); }'`"
+ if [ ".$username" = . ]; then
+ username='unknown'
+ fi
+ fi
+ fi
+ fi
+ fi
+ fi
+ fi
+
+ # determine user id
+ userid=''
+ if [ ".$opt_e" = .yes ] && [ ".`echo $text | grep '%U'`" != . ]; then
+ userid="`(id -u) 2>/dev/null`"
+ if [ ".$userid" = . ]; then
+ userid="`(id -u ${username}) 2>/dev/null`"
+ if [ ".$userid" = . ]; then
+ str="`(id) 2>/dev/null`"
+ if [ ".`echo $str | grep '^uid[ ]*=[ ]*[0-9]*('`" != . ]; then
+ userid=`echo $str | sed -e 's/^uid[ ]*=[ ]*//' -e 's/(.*$//'`
+ fi
+ if [ ".$userid" = . ]; then
+ userid=`(getent passwd ${username}) 2>/dev/null | \
+ sed -e 's/[^:]*:[^:]*://' -e 's/:.*$//'`
+ if [ ".$userid" = . ]; then
+ userid=`grep "^${username}:" /etc/passwd 2>/dev/null | \
+ sed -e 's/[^:]*:[^:]*://' -e 's/:.*$//'`
+ if [ ".$userid" = . ]; then
+ userid=`(ypmatch "${username}" passwd; nismatch "${username}" passwd) 2>/dev/null | \
+ sed -e 'q' | sed -e 's/[^:]*:[^:]*://' -e 's/:.*$//'`
+ if [ ".$userid" = . ]; then
+ userid=`(nidump passwd . | grep "^${username}:") 2>/dev/null | \
+ sed -e 's/[^:]*:[^:]*://' -e 's/:.*$//'`
+ if [ ".$userid" = . ]; then
+ userid='?'
+ fi
+ fi
+ fi
+ fi
+ fi
+ fi
+ fi
+ fi
+
+ # determine (primary) group id
+ groupid=''
+ if [ ".$opt_e" = .yes ] && [ ".`echo $text | grep '%[gG]'`" != . ]; then
+ groupid="`(id -g ${username}) 2>/dev/null`"
+ if [ ".$groupid" = . ]; then
+ str="`(id) 2>/dev/null`"
+ if [ ".`echo $str | grep 'gid[ ]*=[ ]*[0-9]*('`" != . ]; then
+ groupid=`echo $str | sed -e 's/^.*gid[ ]*=[ ]*//' -e 's/(.*$//'`
+ fi
+ if [ ".$groupid" = . ]; then
+ groupid=`(getent passwd ${username}) 2>/dev/null | \
+ sed -e 's/[^:]*:[^:]*:[^:]*://' -e 's/:.*$//'`
+ if [ ".$groupid" = . ]; then
+ groupid=`grep "^${username}:" /etc/passwd 2>/dev/null | \
+ sed -e 's/[^:]*:[^:]*:[^:]*://' -e 's/:.*$//'`
+ if [ ".$groupid" = . ]; then
+ groupid=`(ypmatch "${username}" passwd; nismatch "${username}" passwd) 2>/dev/null | \
+ sed -e 'q' | sed -e 's/[^:]*:[^:]*:[^:]*://' -e 's/:.*$//'`
+ if [ ".$groupid" = . ]; then
+ groupid=`(nidump passwd . | grep "^${username}:") 2>/dev/null | \
+ sed -e 's/[^:]*:[^:]*:[^:]*://' -e 's/:.*$//'`
+ if [ ".$groupid" = . ]; then
+ groupid='?'
+ fi
+ fi
+ fi
+ fi
+ fi
+ fi
+ fi
+
+ # determine (primary) group name
+ groupname=''
+ if [ ".$opt_e" = .yes ] && [ ".`echo $text | grep '%g'`" != . ]; then
+ groupname="`(id -gn ${username}) 2>/dev/null`"
+ if [ ".$groupname" = . ]; then
+ str="`(id) 2>/dev/null`"
+ if [ ".`echo $str | grep 'gid[ ]*=[ ]*[0-9]*('`" != . ]; then
+ groupname=`echo $str | sed -e 's/^.*gid[ ]*=[ ]*[0-9]*(//' -e 's/).*$//'`
+ fi
+ if [ ".$groupname" = . ]; then
+ groupname=`(getent group) 2>/dev/null | \
+ grep "^[^:]*:[^:]*:${groupid}:" | \
+ sed -e 's/:.*$//'`
+ if [ ".$groupname" = . ]; then
+ groupname=`grep "^[^:]*:[^:]*:${groupid}:" /etc/group 2>/dev/null | \
+ sed -e 's/:.*$//'`
+ if [ ".$groupname" = . ]; then
+ groupname=`(ypcat group; niscat group) 2>/dev/null | \
+ sed -e 'q' | grep "^[^:]*:[^:]*:${groupid}:" | \
+ sed -e 's/:.*$//'`
+ if [ ".$groupname" = . ]; then
+ groupname=`(nidump group .) 2>/dev/null | \
+ grep "^[^:]*:[^:]*:${groupid}:" | \
+ sed -e 's/:.*$//'`
+ if [ ".$groupname" = . ]; then
+ groupname='?'
+ fi
+ fi
+ fi
+ fi
+ fi
+ fi
+ fi
+
+ # determine host and domain name
+ hostname=''
+ domainname=''
+ if [ ".$opt_e" = .yes ] && [ ".`echo $text | grep '%h'`" != . ]; then
+ hostname="`(uname -n) 2>/dev/null |\
+ awk '{ printf("%s", $1); }'`"
+ if [ ".$hostname" = . ]; then
+ hostname="`(hostname) 2>/dev/null |\
+ awk '{ printf("%s", $1); }'`"
+ if [ ".$hostname" = . ]; then
+ hostname='unknown'
+ fi
+ fi
+ case $hostname in
+ *.* )
+ domainname=".`echo $hostname | cut -d. -f2-`"
+ hostname="`echo $hostname | cut -d. -f1`"
+ ;;
+ esac
+ fi
+ if [ ".$opt_e" = .yes ] && [ ".`echo $text | grep '%d'`" != . ]; then
+ if [ ".$domainname" = . ]; then
+ if [ -f /etc/resolv.conf ]; then
+ domainname="`grep '^[ ]*domain' /etc/resolv.conf | sed -e 'q' |\
+ sed -e 's/.*domain//' \
+ -e 's/^[ ]*//' -e 's/^ *//' -e 's/^ *//' \
+ -e 's/^\.//' -e 's/^/./' |\
+ awk '{ printf("%s", $1); }'`"
+ if [ ".$domainname" = . ]; then
+ domainname="`grep '^[ ]*search' /etc/resolv.conf | sed -e 'q' |\
+ sed -e 's/.*search//' \
+ -e 's/^[ ]*//' -e 's/^ *//' -e 's/^ *//' \
+ -e 's/ .*//' -e 's/ .*//' \
+ -e 's/^\.//' -e 's/^/./' |\
+ awk '{ printf("%s", $1); }'`"
+ fi
+ fi
+ fi
+ fi
+
+ # determine current time
+ time_day=''
+ time_month=''
+ time_year=''
+ time_monthname=''
+ if [ ".$opt_e" = .yes ] && [ ".`echo $text | grep '%[DMYm]'`" != . ]; then
+ time_day=`date '+%d'`
+ time_month=`date '+%m'`
+ time_year=`date '+%Y' 2>/dev/null`
+ if [ ".$time_year" = . ]; then
+ time_year=`date '+%y'`
+ case $time_year in
+ [5-9][0-9]) time_year="19$time_year" ;;
+ [0-4][0-9]) time_year="20$time_year" ;;
+ esac
+ fi
+ case $time_month in
+ 1|01) time_monthname='Jan' ;;
+ 2|02) time_monthname='Feb' ;;
+ 3|03) time_monthname='Mar' ;;
+ 4|04) time_monthname='Apr' ;;
+ 5|05) time_monthname='May' ;;
+ 6|06) time_monthname='Jun' ;;
+ 7|07) time_monthname='Jul' ;;
+ 8|08) time_monthname='Aug' ;;
+ 9|09) time_monthname='Sep' ;;
+ 10) time_monthname='Oct' ;;
+ 11) time_monthname='Nov' ;;
+ 12) time_monthname='Dec' ;;
+ esac
+ fi
+
+ # expand special ``%x'' constructs
+ if [ ".$opt_e" = .yes ]; then
+ text=`echo $seo "$text" |\
+ sed -e "s/%B/${term_bold}/g" \
+ -e "s/%b/${term_norm}/g" \
+ -e "s/%u/${username}/g" \
+ -e "s/%U/${userid}/g" \
+ -e "s/%g/${groupname}/g" \
+ -e "s/%G/${groupid}/g" \
+ -e "s/%h/${hostname}/g" \
+ -e "s/%d/${domainname}/g" \
+ -e "s/%D/${time_day}/g" \
+ -e "s/%M/${time_month}/g" \
+ -e "s/%Y/${time_year}/g" \
+ -e "s/%m/${time_monthname}/g" 2>/dev/null`
+ fi
+
+ # create output
+ if [ .$opt_n = .no ]; then
+ echo $seo "$text"
+ else
+ # the harder part: echo -n is best, because
+ # awk may complain about some \xx sequences.
+ if [ ".$minusn" != . ]; then
+ echo $seo $minusn "$text"
+ else
+ echo dummy | awk '{ printf("%s", TEXT); }' TEXT="$text"
+ fi
+ fi
+
+ shtool_exit 0
+ ;;
+
+move )
+ ##
+ ## move -- Move files with simultaneous substitution
+ ## Copyright (c) 1999-2008 Ralf S. Engelschall <rse@engelschall.com>
+ ##
+
+ src="$1"
+ dst="$2"
+
+ # consistency checks
+ if [ ".$src" = . ] || [ ".$dst" = . ]; then
+ echo "$msgprefix:Error: Invalid arguments" 1>&2
+ shtool_exit 1
+ fi
+ if [ ".$src" = ".$dst" ]; then
+ echo "$msgprefix:Error: Source and destination files are the same" 1>&2
+ shtool_exit 1
+ fi
+ expsrc="$src"
+ if [ ".$opt_e" = .yes ]; then
+ expsrc="`echo $expsrc`"
+ fi
+ if [ ".$opt_e" = .yes ]; then
+ if [ ".`echo "$src" | sed -e 's;^.*\\*.*$;;'`" = ".$src" ]; then
+ echo "$msgprefix:Error: Source doesn't contain wildcard ('*'): $dst" 1>&2
+ shtool_exit 1
+ fi
+ if [ ".`echo "$dst" | sed -e 's;^.*%[1-9].*$;;'`" = ".$dst" ]; then
+ echo "$msgprefix:Error: Destination doesn't contain substitution ('%N'): $dst" 1>&2
+ shtool_exit 1
+ fi
+ if [ ".$expsrc" = ".$src" ]; then
+ echo "$msgprefix:Error: Sources not found or no asterisk : $src" 1>&2
+ shtool_exit 1
+ fi
+ else
+ if [ ! -r "$src" ]; then
+ echo "$msgprefix:Error: Source not found: $src" 1>&2
+ shtool_exit 1
+ fi
+ fi
+
+ # determine substitution patterns
+ if [ ".$opt_e" = .yes ]; then
+ srcpat=`echo "$src" | sed -e 's/\\./\\\\./g' -e 's/;/\\;/g' -e 's;\\*;\\\\(.*\\\\);g'`
+ dstpat=`echo "$dst" | sed -e 's;%\([1-9]\);\\\\\1;g'`
+ fi
+
+ # iterate over source(s)
+ for onesrc in $expsrc; do
+ if [ .$opt_e = .yes ]; then
+ onedst=`echo $onesrc | sed -e "s;$srcpat;$dstpat;"`
+ else
+ onedst="$dst"
+ fi
+ errorstatus=0
+ if [ ".$opt_v" = .yes ]; then
+ echo "$onesrc -> $onedst"
+ fi
+ if [ ".$opt_p" = .yes ]; then
+ if [ -r $onedst ]; then
+ if cmp -s $onesrc $onedst; then
+ if [ ".$opt_t" = .yes ]; then
+ echo "rm -f $onesrc" 1>&2
+ fi
+ rm -f $onesrc || errorstatus=$?
+ else
+ if [ ".$opt_t" = .yes ]; then
+ echo "mv -f $onesrc $onedst" 1>&2
+ fi
+ mv -f $onesrc $onedst || errorstatus=$?
+ fi
+ else
+ if [ ".$opt_t" = .yes ]; then
+ echo "mv -f $onesrc $onedst" 1>&2
+ fi
+ mv -f $onesrc $onedst || errorstatus=$?
+ fi
+ else
+ if [ ".$opt_t" = .yes ]; then
+ echo "mv -f $onesrc $onedst" 1>&2
+ fi
+ mv -f $onesrc $onedst || errorstatus=$?
+ fi
+ if [ $errorstatus -ne 0 ]; then
+ break;
+ fi
+ done
+
+ shtool_exit $errorstatus
+ ;;
+
+install )
+ ##
+ ## install -- Install a program, script or datafile
+ ## Copyright (c) 1997-2008 Ralf S. Engelschall <rse@engelschall.com>
+ ##
+
+ # special case: "shtool install -d <dir> [...]" internally
+ # maps to "shtool mkdir -f -p -m 755 <dir> [...]"
+ if [ "$opt_d" = yes ]; then
+ cmd="$0 mkdir -f -p -m 755"
+ if [ ".$opt_o" != . ]; then
+ cmd="$cmd -o '$opt_o'"
+ fi
+ if [ ".$opt_g" != . ]; then
+ cmd="$cmd -g '$opt_g'"
+ fi
+ if [ ".$opt_v" = .yes ]; then
+ cmd="$cmd -v"
+ fi
+ if [ ".$opt_t" = .yes ]; then
+ cmd="$cmd -t"
+ fi
+ for dir in "$@"; do
+ eval "$cmd $dir" || shtool_exit $?
+ done
+ shtool_exit 0
+ fi
+
+ # determine source(s) and destination
+ argc=$#
+ srcs=""
+ while [ $# -gt 1 ]; do
+ srcs="$srcs $1"
+ shift
+ done
+ dstpath="$1"
+
+ # type check for destination
+ dstisdir=0
+ if [ -d $dstpath ]; then
+ dstpath=`echo "$dstpath" | sed -e 's:/$::'`
+ dstisdir=1
+ fi
+
+ # consistency check for destination
+ if [ $argc -gt 2 ] && [ $dstisdir = 0 ]; then
+ echo "$msgprefix:Error: multiple sources require destination to be directory" 1>&2
+ shtool_exit 1
+ fi
+
+ # iterate over all source(s)
+ for src in $srcs; do
+ dst=$dstpath
+
+ # if destination is a directory, append the input filename
+ if [ $dstisdir = 1 ]; then
+ dstfile=`echo "$src" | sed -e 's;.*/\([^/]*\)$;\1;'`
+ dst="$dst/$dstfile"
+ fi
+
+ # check for correct arguments
+ if [ ".$src" = ".$dst" ]; then
+ echo "$msgprefix:Warning: source and destination are the same - skipped" 1>&2
+ continue
+ fi
+ if [ -d "$src" ]; then
+ echo "$msgprefix:Warning: source \`$src' is a directory - skipped" 1>&2
+ continue
+ fi
+
+ # make a temp file name in the destination directory
+ dsttmp=`echo $dst |\
+ sed -e 's;[^/]*$;;' -e 's;\(.\)/$;\1;' -e 's;^$;.;' \
+ -e "s;\$;/#INST@$$#;"`
+
+ # verbosity
+ if [ ".$opt_v" = .yes ]; then
+ echo "$src -> $dst" 1>&2
+ fi
+
+ # copy or move the file name to the temp name
+ # (because we might be not allowed to change the source)
+ if [ ".$opt_C" = .yes ]; then
+ opt_c=yes
+ fi
+ if [ ".$opt_c" = .yes ]; then
+ if [ ".$opt_t" = .yes ]; then
+ echo "cp $src $dsttmp" 1>&2
+ fi
+ cp "$src" "$dsttmp" || shtool_exit $?
+ else
+ if [ ".$opt_t" = .yes ]; then
+ echo "mv $src $dsttmp" 1>&2
+ fi
+ mv "$src" "$dsttmp" || shtool_exit $?
+ fi
+
+ # adjust the target file
+ if [ ".$opt_e" != . ]; then
+ sed='sed'
+ OIFS="$IFS"; IFS="$ASC_NL"; set -- $opt_e; IFS="$OIFS"
+ for e
+ do
+ sed="$sed -e '$e'"
+ done
+ cp "$dsttmp" "$dsttmp.old"
+ chmod u+w $dsttmp
+ eval "$sed <$dsttmp.old >$dsttmp" || shtool_exit $?
+ rm -f $dsttmp.old
+ fi
+ if [ ".$opt_s" = .yes ]; then
+ if [ ".$opt_t" = .yes ]; then
+ echo "strip $dsttmp" 1>&2
+ fi
+ ${STRIP:-strip} $dsttmp || shtool_exit $?
+ fi
+ if [ ".$opt_o" != . ]; then
+ if [ ".$opt_t" = .yes ]; then
+ echo "chown $opt_o $dsttmp" 1>&2
+ fi
+ chown $opt_o $dsttmp || shtool_exit $?
+ fi
+ if [ ".$opt_g" != . ]; then
+ if [ ".$opt_t" = .yes ]; then
+ echo "chgrp $opt_g $dsttmp" 1>&2
+ fi
+ chgrp $opt_g $dsttmp || shtool_exit $?
+ fi
+ if [ ".$opt_m" != ".-" ]; then
+ if [ ".$opt_t" = .yes ]; then
+ echo "chmod $opt_m $dsttmp" 1>&2
+ fi
+ chmod $opt_m $dsttmp || shtool_exit $?
+ fi
+
+ # determine whether to do a quick install
+ # (has to be done _after_ the strip was already done)
+ quick=no
+ if [ ".$opt_C" = .yes ]; then
+ if [ -r $dst ]; then
+ if cmp -s "$src" "$dst"; then
+ quick=yes
+ fi
+ fi
+ fi
+
+ # finally, install the file to the real destination
+ if [ $quick = yes ]; then
+ if [ ".$opt_t" = .yes ]; then
+ echo "rm -f $dsttmp" 1>&2
+ fi
+ rm -f $dsttmp
+ else
+ if [ ".$opt_t" = .yes ]; then
+ echo "rm -f $dst && mv $dsttmp $dst" 1>&2
+ fi
+ rm -f $dst && mv $dsttmp $dst
+ fi
+ done
+
+ shtool_exit 0
+ ;;
+
+mkdir )
+ ##
+ ## mkdir -- Make one or more directories
+ ## Copyright (c) 1996-2008 Ralf S. Engelschall <rse@engelschall.com>
+ ##
+
+ errstatus=0
+ for p in ${1+"$@"}; do
+ # if the directory already exists...
+ if [ -d "$p" ]; then
+ if [ ".$opt_f" = .no ] && [ ".$opt_p" = .no ]; then
+ echo "$msgprefix:Error: directory already exists: $p" 1>&2
+ errstatus=1
+ break
+ else
+ continue
+ fi
+ fi
+ # if the directory has to be created...
+ if [ ".$opt_p" = .no ]; then
+ if [ ".$opt_t" = .yes ]; then
+ echo "mkdir $p" 1>&2
+ fi
+ mkdir $p || errstatus=$?
+ if [ ".$opt_o" != . ]; then
+ if [ ".$opt_t" = .yes ]; then
+ echo "chown $opt_o $p" 1>&2
+ fi
+ chown $opt_o $p || errstatus=$?
+ fi
+ if [ ".$opt_g" != . ]; then
+ if [ ".$opt_t" = .yes ]; then
+ echo "chgrp $opt_g $p" 1>&2
+ fi
+ chgrp $opt_g $p || errstatus=$?
+ fi
+ if [ ".$opt_m" != . ]; then
+ if [ ".$opt_t" = .yes ]; then
+ echo "chmod $opt_m $p" 1>&2
+ fi
+ chmod $opt_m $p || errstatus=$?
+ fi
+ else
+ # the smart situation
+ set fnord `echo ":$p" |\
+ sed -e 's/^:\//%/' \
+ -e 's/^://' \
+ -e 's/\// /g' \
+ -e 's/^%/\//'`
+ shift
+ pathcomp=''
+ for d in ${1+"$@"}; do
+ pathcomp="$pathcomp$d"
+ case "$pathcomp" in
+ -* ) pathcomp="./$pathcomp" ;;
+ esac
+ if [ ! -d "$pathcomp" ]; then
+ if [ ".$opt_t" = .yes ]; then
+ echo "mkdir $pathcomp" 1>&2
+ fi
+ mkdir $pathcomp || errstatus=$?
+ if [ ".$opt_o" != . ]; then
+ if [ ".$opt_t" = .yes ]; then
+ echo "chown $opt_o $pathcomp" 1>&2
+ fi
+ chown $opt_o $pathcomp || errstatus=$?
+ fi
+ if [ ".$opt_g" != . ]; then
+ if [ ".$opt_t" = .yes ]; then
+ echo "chgrp $opt_g $pathcomp" 1>&2
+ fi
+ chgrp $opt_g $pathcomp || errstatus=$?
+ fi
+ if [ ".$opt_m" != . ]; then
+ if [ ".$opt_t" = .yes ]; then
+ echo "chmod $opt_m $pathcomp" 1>&2
+ fi
+ chmod $opt_m $pathcomp || errstatus=$?
+ fi
+ fi
+ pathcomp="$pathcomp/"
+ done
+ fi
+ done
+
+ shtool_exit $errstatus
+ ;;
+
+mkln )
+ ##
+ ## mkln -- Make link with calculation of relative paths
+ ## Copyright (c) 1998-2008 Ralf S. Engelschall <rse@engelschall.com>
+ ##
+
+ # determine source(s) and destination
+ args=$#
+ srcs=""
+ while [ $# -gt 1 ]; do
+ srcs="$srcs $1"
+ shift
+ done
+ dst="$1"
+ if [ ! -d $dst ]; then
+ if [ $args -gt 2 ]; then
+ echo "$msgprefix:Error: multiple sources not allowed when target isn't a directory" 1>&2
+ shtool_exit 1
+ fi
+ fi
+
+ # determine link options
+ lnopt=""
+ if [ ".$opt_f" = .yes ]; then
+ lnopt="$lnopt -f"
+ fi
+ if [ ".$opt_s" = .yes ]; then
+ lnopt="$lnopt -s"
+ fi
+
+ # iterate over sources
+ for src in $srcs; do
+ # determine if one of the paths is an absolute path,
+ # because then we _have_ to use an absolute symlink
+ oneisabs=0
+ srcisabs=0
+ dstisabs=0
+ case $src in
+ /* ) oneisabs=1; srcisabs=1 ;;
+ esac
+ case $dst in
+ /* ) oneisabs=1; dstisabs=1 ;;
+ esac
+
+ # split source and destination into dir and base name
+ if [ -d $src ]; then
+ srcdir=`echo $src | sed -e 's;/*$;;'`
+ srcbase=""
+ else
+ srcdir=`echo $src | sed -e 's;^[^/]*$;;' -e 's;^\(.*/\)[^/]*$;\1;' -e 's;\(.\)/$;\1;'`
+ srcbase=`echo $src | sed -e 's;.*/\([^/]*\)$;\1;'`
+ fi
+ if [ -d $dst ]; then
+ dstdir=`echo $dst | sed -e 's;/*$;;'`
+ dstbase=""
+ else
+ dstdir=`echo $dst | sed -e 's;^[^/]*$;;' -e 's;^\(.*/\)[^/]*$;\1;' -e 's;\(.\)/$;\1;'`
+ dstbase=`echo $dst | sed -e 's;.*/\([^/]*\)$;\1;'`
+ fi
+
+ # consistency check
+ if [ ".$dstdir" != . ]; then
+ if [ ! -d $dstdir ]; then
+ echo "$msgprefix:Error: destination directory not found: $dstdir" 1>&2
+ shtool_exit 1
+ fi
+ fi
+
+ # make sure the source is reachable from the destination
+ if [ $dstisabs = 1 ]; then
+ if [ $srcisabs = 0 ]; then
+ if [ ".$srcdir" = . ]; then
+ srcdir="`pwd | sed -e 's;/*$;;'`"
+ srcisabs=1
+ oneisabs=1
+ elif [ -d $srcdir ]; then
+ srcdir="`cd $srcdir; pwd | sed -e 's;/*$;;'`"
+ srcisabs=1
+ oneisabs=1
+ fi
+ fi
+ fi
+
+ # split away a common prefix
+ prefix=""
+ if [ ".$srcdir" = ".$dstdir" ] && [ ".$srcdir" != . ]; then
+ prefix="$srcdir/"
+ srcdir=""
+ dstdir=""
+ else
+ while [ ".$srcdir" != . ] && [ ".$dstdir" != . ]; do
+ presrc=`echo $srcdir | sed -e 's;^\([^/]*\)/.*;\1;'`
+ predst=`echo $dstdir | sed -e 's;^\([^/]*\)/.*;\1;'`
+ if [ ".$presrc" != ".$predst" ]; then
+ break
+ fi
+ prefix="$prefix$presrc/"
+ srcdir=`echo $srcdir | sed -e 's;^[^/]*/*;;'`
+ dstdir=`echo $dstdir | sed -e 's;^[^/]*/*;;'`
+ done
+ fi
+
+ # destination prefix is just the common prefix
+ dstpre="$prefix"
+
+ # determine source prefix which is the reverse directory
+ # step-up corresponding to the destination directory
+ srcpre=""
+ allow_relative_srcpre=no
+ if [ ".$prefix" != . ] && [ ".$prefix" != ./ ]; then
+ allow_relative_srcpre=yes
+ fi
+ if [ $oneisabs = 0 ]; then
+ allow_relative_srcpre=yes
+ fi
+ if [ ".$opt_s" != .yes ]; then
+ allow_relative_srcpre=no
+ fi
+ if [ ".$allow_relative_srcpre" = .yes ]; then
+ pl="$dstdir/"
+ OIFS="$IFS"; IFS='/'
+ for pe in $pl; do
+ [ ".$pe" = . ] && continue
+ [ ".$pe" = .. ] && continue
+ srcpre="../$srcpre"
+ done
+ IFS="$OIFS"
+ else
+ if [ $srcisabs = 1 ]; then
+ srcpre="$prefix"
+ fi
+ fi
+
+ # determine destination symlink name
+ if [ ".$dstbase" = . ]; then
+ if [ ".$srcbase" != . ]; then
+ dstbase="$srcbase"
+ else
+ dstbase=`echo "$prefix$srcdir" | sed -e 's;/*$;;' -e 's;.*/\([^/]*\)$;\1;'`
+ fi
+ fi
+
+ # now finalize source and destination directory paths
+ srcdir=`echo $srcdir | sed -e 's;\([^/]\)$;\1/;'`
+ dstdir=`echo $dstdir | sed -e 's;\([^/]\)$;\1/;'`
+
+ # run the final link command
+ if [ ".$opt_t" = .yes ]; then
+ echo "ln$lnopt $srcpre$srcdir$srcbase $dstpre$dstdir$dstbase"
+ fi
+ eval ln$lnopt $srcpre$srcdir$srcbase $dstpre$dstdir$dstbase
+ done
+
+ shtool_exit 0
+ ;;
+
+subst )
+ ##
+ ## subst -- Apply sed(1) substitution operations
+ ## Copyright (c) 2001-2008 Ralf S. Engelschall <rse@engelschall.com>
+ ##
+
+ # remember optional list of file(s)
+ files="$*"
+ files_num="$#"
+
+ # parameter consistency check
+ if [ $# -eq 0 ] && [ ".$opt_b" != . ]; then
+ echo "$msgprefix:Error: option -b cannot be applied to stdin" 1>&2
+ shtool_exit 1
+ fi
+ if [ $# -eq 0 ] && [ ".$opt_s" = .yes ]; then
+ echo "$msgprefix:Error: option -s cannot be applied to stdin" 1>&2
+ shtool_exit 1
+ fi
+
+ # build underlying sed(1) command
+ sedcmd='sed'
+ if [ ".$opt_e" != . ]; then
+ OIFS="$IFS"; IFS="$ASC_NL"; set -- $opt_e; IFS="$OIFS"
+ for e
+ do
+ sedcmd="$sedcmd -e '$e'"
+ done
+ elif [ ".$opt_f" != . ]; then
+ if [ ! -f $opt_f ]; then
+ echo "$msgprefix:Error: command file \`$opt_f' not found or not a regular file" 1>&2
+ shtool_exit 1
+ fi
+ sedcmd="$sedcmd -f '$opt_f'"
+ else
+ echo "$msgprefix:Error: either -e option(s) or -f option required" 1>&2
+ shtool_exit 1
+ fi
+
+ # determine extension for original file
+ orig=".orig"
+ if [ ".$opt_b" != . ]; then
+ orig="$opt_b"
+ fi
+
+ # apply sed(1) operation(s)
+ if [ ".$files" != . ]; then
+ # apply operation(s) to files
+ substdone=no
+ for file in $files; do
+ test ".$file" = . && continue
+ if [ ! -f $file ]; then
+ echo "$msgprefix:Warning: file \`$file' not found or not a regular file" 1>&2
+ continue
+ fi
+
+ # handle interactive mode
+ if [ ".$opt_i" = .yes ]; then
+ eval "$sedcmd <$file >$file.new"
+ skip=no
+ if cmp $file $file.new >/dev/null 2>&1; then
+ rm -f $file.new
+ skip=yes
+ else
+ (diff -U1 $file $file.new >$tmpfile) 2>/dev/null
+ if [ ".`cat $tmpfile`" = . ]; then
+ (diff -C1 $file $file.new >$tmpfile) 2>/dev/null
+ if [ ".`cat $tmpfile`" = . ]; then
+ echo "$msgprefix:Warning: unable to show difference for file \`$file'" 1>&2
+ cp /dev/null $tmpfile
+ fi
+ fi
+ rm -f $file.new
+ cat $tmpfile
+ echo dummy | awk '{ printf("%s", TEXT); }' TEXT=">>> Apply [Y/n]: "
+ read input
+ if [ ".$input" != .Y ] &&\
+ [ ".$input" != .y ] &&\
+ [ ".$input" != . ]; then
+ skip=yes
+ fi
+ fi
+ if [ ".$skip" = .yes ]; then
+ if [ ".$opt_v" = .yes ]; then
+ echo "file \`$file' -- skipped" 1>&2
+ fi
+ continue
+ fi
+ fi
+
+ # apply sed(1) operation(s)
+ if [ ".$opt_v" = .yes ]; then
+ echo "patching \`$file'" 1>&2
+ fi
+ if [ ".$opt_t" = .yes ]; then
+ echo "\$ cp -p $file $file$orig"
+ echo "\$ chmod u+w $file"
+ echo "\$ $sedcmd <$file$orig >$file"
+ fi
+ if [ ".$opt_n" = .no ]; then
+ cp -p $file $file$orig
+ chmod u+w $file >/dev/null 2>&1 || true
+ eval "$sedcmd <$file$orig >$file"
+ fi
+
+ # optionally fix timestamp
+ if [ ".$opt_s" = .yes ]; then
+ if [ ".$opt_t" = .yes ]; then
+ echo "\$ touch -r $file$orig $file"
+ fi
+ if [ ".$opt_n" = .no ]; then
+ touch -r $file$orig $file
+ fi
+ fi
+
+ # optionally check whether any content change actually occurred
+ if [ ".$opt_q" = .no ]; then
+ if cmp $file$orig $file >/dev/null 2>&1; then
+ if [ ".$opt_w" = .yes ]; then
+ echo "$msgprefix:Warning: substitution resulted in no content change on file \"$file\"" 1>&2
+ fi
+ else
+ substdone=yes
+ fi
+ fi
+
+ # optionally remove preserved original file
+ if [ ".$opt_b" = . ]; then
+ if [ ".$opt_t" = .yes ]; then
+ echo "\$ rm -f $file$orig"
+ fi
+ if [ ".$opt_n" = .no ]; then
+ rm -f $file$orig
+ fi
+ fi
+ done
+ if [ ".$opt_q" = .no ] && [ ".$opt_w" = .no ]; then
+ if [ ".$substdone" = .no ]; then
+ if [ ".$files_num" = .1 ]; then
+ echo "$msgprefix:Warning: substitution resulted in no content change on file \"$file\"" 1>&2
+ else
+ echo "$msgprefix:Warning: substitution resulted in no content change on any file" 1>&2
+ fi
+ fi
+ fi
+ else
+ # apply operation(s) to stdin/stdout
+ if [ ".$opt_v" = .yes ]; then
+ echo "patching <stdin>" 1>&2
+ fi
+ if [ ".$opt_t" = .yes ]; then
+ echo "\$ $sedcmd"
+ fi
+ if [ ".$opt_n" = .no ]; then
+ eval "$sedcmd"
+ fi
+ fi
+
+ shtool_exit 0
+ ;;
+
+esac
+
+shtool_exit 0
+
diff --git a/build/srv.mk b/build/srv.mk
new file mode 100644
index 0000000..e96865a
--- /dev/null
+++ b/build/srv.mk
@@ -0,0 +1,59 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##---------------------------------------------------------------------------
+#
+# Makefile Template for Servers
+#
+
+all-common: all-$(BUILD_SRV)
+all-no lint-no 5lint-no depend-no install-no:
+ @echo "run configure with $(BUILD_OPT) to make $(PROGRAMS)"
+
+clean-common: clean-srv FORCE
+veryclean-common: veryclean-srv FORCE
+
+lint-common: lint-$(BUILD_SRV)
+
+5lint-common: 5lint-$(BUILD_SRV)
+
+depend-common: depend-$(BUILD_SRV)
+
+install-common: install-$(BUILD_SRV)
+
+all-local-srv:
+all-yes: all-local-srv FORCE
+
+install-local-srv:
+install-yes: install-local-srv FORCE
+
+lint-local-srv:
+lint-yes: lint-local-srv FORCE
+ $(LINT) $(DEFS) $(DEFINES) $(SRCS)
+
+5lint-local-srv:
+5lint-yes: 5lint-local-srv FORCE
+ $(5LINT) $(DEFS) $(DEFINES) $(SRCS)
+
+clean-local-srv:
+clean-srv: clean-local-srv FORCE
+ $(RM) $(PROGRAMS) $(XPROGRAMS) $(XSRCS) *.o a.out core .libs/* *.exe
+
+depend-local-srv:
+depend-yes: depend-local-srv FORCE
+ $(MKDEP) $(DEFS) $(DEFINES) $(SRCS)
+
+veryclean-local-srv:
+veryclean-srv: clean-srv veryclean-local-srv
+
+Makefile: $(top_srcdir)/build/srv.mk
diff --git a/build/top.mk b/build/top.mk
new file mode 100644
index 0000000..9792c64
--- /dev/null
+++ b/build/top.mk
@@ -0,0 +1,261 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+##---------------------------------------------------------------------------
+#
+# Top-level Makefile template
+#
+
+PACKAGE= @PACKAGE@
+VERSION= @VERSION@
+RELEASEDATE= @OPENLDAP_RELEASE_DATE@
+
+@SET_MAKE@
+SHELL = /bin/sh
+
+top_builddir = @top_builddir@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+ldap_subdir = @ldap_subdir@
+
+bindir = @bindir@
+datarootdir = @datarootdir@
+datadir = @datadir@$(ldap_subdir)
+includedir = @includedir@
+infodir = @infodir@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+moduledir = @libexecdir@$(ldap_subdir)
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@$(ldap_subdir)
+schemadir = $(sysconfdir)/schema
+systemdsystemunitdir = @systemdsystemunitdir@
+
+PLAT = @PLAT@
+EXEEXT = @EXEEXT@
+OBJEXT = @OBJEXT@
+
+BUILD_LIBS_DYNAMIC = @BUILD_LIBS_DYNAMIC@
+
+SHTOOL = $(top_srcdir)/build/shtool
+
+INSTALL = $(SHTOOL) install -c
+INSTALL_PROGRAM = $(INSTALL)
+INSTALL_DATA = $(INSTALL) -m 644
+INSTALL_SCRIPT = $(INSTALL)
+
+STRIP_OPTS = -s
+
+LINT = lint
+5LINT = 5lint
+
+MKDEP = $(top_srcdir)/build/mkdep $(MKDEPFLAG) \
+ -d "$(srcdir)" -c "$(MKDEP_CC)" -m "$(MKDEP_CFLAGS)"
+MKDEP_CC = @OL_MKDEP@
+MKDEP_CFLAGS = @OL_MKDEP_FLAGS@
+
+MKVERSION = $(top_srcdir)/build/mkversion -v "$(VERSION)"
+
+LIBTOOL = @LIBTOOL@
+LIBRELEASE = @OPENLDAP_LIBRELEASE@
+LIBVERSION = @OPENLDAP_LIBVERSION@
+LTVERSION = -release $(LIBRELEASE) -version-info $(LIBVERSION)
+
+# libtool --only flag for libraries: platform specific
+NT_LTONLY_LIB = # --only-$(BUILD_LIBS_DYNAMIC)
+LTONLY_LIB = $(@PLAT@_LTONLY_LIB)
+
+# libtool --only flag for modules: depends on linkage of module
+# The BUILD_MOD macro is defined in each backend Makefile.in file
+LTONLY_yes = --tag=disable-shared
+LTONLY_mod = --tag=disable-static
+LTONLY_MOD = $(LTONLY_$(BUILD_MOD))
+
+# platform-specific libtool flags
+NT_LTFLAGS_LIB = -no-undefined -avoid-version -rpath $(libdir)
+NT_LTFLAGS_MOD = -no-undefined -avoid-version -rpath $(moduledir)
+UNIX_LTFLAGS_LIB = $(LTVERSION) -rpath $(libdir)
+UNIX_LTFLAGS_MOD = $(LTVERSION) -rpath $(moduledir)
+
+# libtool flags
+LTFLAGS = $(@PLAT@_LTFLAGS)
+LTFLAGS_LIB = $(@PLAT@_LTFLAGS_LIB)
+LTFLAGS_MOD = $(@PLAT@_LTFLAGS_MOD)
+
+# LIB_DEFS defined in liblber and libldap Makefile.in files.
+# MOD_DEFS defined in backend Makefile.in files.
+
+# platform-specific LINK_LIBS defined in various Makefile.in files.
+# LINK_LIBS referenced in library and module link commands.
+LINK_LIBS = $(MOD_LIBS) $(@PLAT@_LINK_LIBS)
+
+# compiler options for versioned library symbol support
+OL_VERSIONED_SYMBOLS = @OL_VERSIONED_SYMBOLS@
+
+LTSTATIC = @LTSTATIC@
+
+LTLINK = $(LIBTOOL) --mode=link \
+ $(CC) $(LTSTATIC) $(LT_CFLAGS) $(LDFLAGS) $(LTFLAGS)
+
+LTCOMPILE_LIB = $(LIBTOOL) $(LTONLY_LIB) --mode=compile \
+ $(CC) $(LT_CFLAGS) $(LT_CPPFLAGS) $(LIB_DEFS) -c
+
+LTLINK_LIB = $(LIBTOOL) $(LTONLY_LIB) --mode=link \
+ $(CC) $(LT_CFLAGS) $(LDFLAGS) $(LTFLAGS_LIB) $(SYMBOL_VERSION_FLAGS)
+
+LTCOMPILE_MOD = $(LIBTOOL) $(LTONLY_MOD) --mode=compile \
+ $(CC) $(LT_CFLAGS) $(LT_CPPFLAGS) $(MOD_DEFS) -c
+
+LTLINK_MOD = $(LIBTOOL) $(LTONLY_MOD) --mode=link \
+ $(CC) $(LT_CFLAGS) $(LDFLAGS) $(LTFLAGS_MOD)
+
+LTINSTALL = $(LIBTOOL) --mode=install $(INSTALL)
+LTFINISH = $(LIBTOOL) --mode=finish
+
+# Misc UNIX commands used in build environment
+AR = @AR@
+BASENAME = basename
+CAT = cat
+CHMOD = chmod
+DATE = date
+ECHO = $(SHTOOL) echo
+HOSTNAME = $(SHTOOL) echo -e "%h%d"
+LN = $(SHTOOL) mkln
+LN_H = $(LN)
+LN_S = $(LN) -s
+MAKEINFO = @MAKEINFO@
+MKDIR = $(SHTOOL) mkdir -p
+MV = $(SHTOOL) move
+PWD = pwd
+RANLIB = @RANLIB@
+RM = rm -f
+SED = sed
+SUBST = $(SHTOOL) subst
+
+# For manual pages
+# MANCOMPRESS=@MANCOMPRESS@
+# MANCOMPRESSSUFFIX=@MANCOMPRESSSUFFIX@
+MANCOMPRESS=$(CAT)
+MANCOMPRESSSUFFIX=
+
+SOELIM=soelim
+
+INCLUDEDIR= $(top_srcdir)/include
+LDAP_INCPATH= -I$(LDAP_INCDIR) -I$(INCLUDEDIR)
+LDAP_LIBDIR= $(top_builddir)/libraries
+
+CLIENT_LIBS = @CLIENT_LIBS@
+
+LUTIL_LIBS = @LUTIL_LIBS@
+LTHREAD_LIBS = @LTHREAD_LIBS@
+
+SLAPD_NDB_LIBS = @SLAPD_NDB_LIBS@
+WT_LIBS = @WT_LIBS@
+
+LEVENT_LIBS = @LEVENT_LIBS@
+
+LDAP_LIBLBER_LA = $(LDAP_LIBDIR)/liblber/liblber.la
+LDAP_LIBLDAP_LA = $(LDAP_LIBDIR)/libldap/libldap.la
+
+LDAP_LIBREWRITE_A = $(LDAP_LIBDIR)/librewrite/librewrite.a
+LDAP_LIBLUNICODE_A = $(LDAP_LIBDIR)/liblunicode/liblunicode.a
+LDAP_LIBLUTIL_A = $(LDAP_LIBDIR)/liblutil/liblutil.a
+
+LDAP_L = $(LDAP_LIBLUTIL_A) \
+ $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+SLAPD_L = $(LDAP_LIBLUNICODE_A) $(LDAP_LIBREWRITE_A) \
+ $(LDAP_LIBLUTIL_A) $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+LLOADD_L = $(LDAP_LIBLUTIL_A) $(LDAP_LIBLDAP_LA) \
+ $(LDAP_LIBLBER_LA)
+
+WRAP_LIBS = @WRAP_LIBS@
+# AutoConfig generated
+AC_CC = @CC@
+AC_CFLAGS = @CFLAGS@
+AC_DEFS = @CPPFLAGS@ # @DEFS@
+AC_LDFLAGS = @LDFLAGS@
+AC_LIBS = @LIBS@
+
+SASL_LIBS = @SASL_LIBS@
+TLS_LIBS = @TLS_LIBS@
+AUTH_LIBS = @AUTH_LIBS@
+ARGON2_LIBS = @ARGON2_LIBS@
+SECURITY_LIBS = $(SASL_LIBS) $(TLS_LIBS) $(AUTH_LIBS)
+SYSTEMD_LIBS = @SYSTEMD_LIBS@
+
+MODULES_CPPFLAGS = @SLAPD_MODULES_CPPFLAGS@
+MODULES_LDFLAGS = @SLAPD_MODULES_LDFLAGS@
+MODULES_LIBS = @MODULES_LIBS@
+SLAPD_PERL_LDFLAGS = @SLAPD_PERL_LDFLAGS@
+
+SLAPD_SQL_LDFLAGS = @SLAPD_SQL_LDFLAGS@
+SLAPD_SQL_INCLUDES = @SLAPD_SQL_INCLUDES@
+SLAPD_SQL_LIBS = @SLAPD_SQL_LIBS@
+
+SLAPD_LIBS = @SLAPD_LIBS@ @SLAPD_PERL_LDFLAGS@ @SLAPD_SQL_LDFLAGS@ @SLAPD_SQL_LIBS@ @SLAPD_SLP_LIBS@ @SLAPD_GMP_LIBS@ $(SYSTEMD_LIBS)
+LLOADD_LIBS = @BALANCER_LIBS@ $(LEVENT_LIBS)
+
+# Our Defaults
+CC = $(AC_CC)
+DEFS = $(LDAP_INCPATH) $(XINCPATH) $(XDEFS) $(AC_DEFS) $(DEFINES)
+CFLAGS = $(AC_CFLAGS) $(DEFS)
+LDFLAGS = $(LDAP_LIBPATH) $(AC_LDFLAGS) $(XLDFLAGS)
+LIBS = $(XLIBS) $(XXLIBS) $(AC_LIBS) $(XXXLIBS)
+
+LT_CFLAGS = $(AC_CFLAGS)
+LT_CPPFLAGS = $(DEFS)
+
+all: all-common all-local FORCE
+install: install-common install-local FORCE
+clean: clean-common clean-local FORCE
+veryclean: veryclean-common veryclean-local FORCE
+depend: depend-common depend-local FORCE
+
+# empty common rules
+all-common:
+install-common:
+clean-common:
+veryclean-common: clean-common FORCE
+depend-common:
+lint-common:
+lint5-common:
+
+# empty local rules
+all-local:
+install-local:
+clean-local:
+veryclean-local: clean-local FORCE
+depend-local:
+lint-local:
+lint5-local:
+
+veryclean: FORCE
+ $(RM) Makefile
+ $(RM) -r .libs
+
+Makefile: Makefile.in $(top_srcdir)/build/top.mk
+
+pathtest:
+ $(SHTOOL) --version
+
+# empty rule for forcing rules
+FORCE:
+
+##---------------------------------------------------------------------------
+
diff --git a/build/version.h b/build/version.h
new file mode 100644
index 0000000..63d37c9
--- /dev/null
+++ b/build/version.h
@@ -0,0 +1,18 @@
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+static const char copyright[] =
+"Copyright 1998-2022 The OpenLDAP Foundation. All rights reserved.\n"
+"COPYING RESTRICTIONS APPLY.\n";
+
diff --git a/build/version.sh b/build/version.sh
new file mode 100755
index 0000000..9049f07
--- /dev/null
+++ b/build/version.sh
@@ -0,0 +1,46 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+DIR=`dirname $0`
+. $DIR/version.var
+
+if test $ol_patch != X ; then
+ ol_version=${ol_major}.${ol_minor}.${ol_patch}
+ ol_api_lib_release=${ol_major}.${ol_minor}
+ ol_type=Release
+elif test $ol_minor != X ; then
+ ol_version=${ol_major}.${ol_minor}.${ol_patch}
+ ol_api_lib_release=${ol_major}.${ol_minor}.releng
+ ol_type=Engineering
+else
+ ol_version=${ol_major}.${ol_minor}
+ ol_api_lib_release=${ol_major}.devel
+ ol_type=Devel
+fi
+
+ol_string="${ol_package} ${ol_version}-${ol_type}"
+ol_api_lib_version="${ol_api_current}:${ol_api_revision}:${ol_api_age}"
+
+echo OL_PACKAGE=\"${ol_package}\"
+echo OL_MAJOR=$ol_major
+echo OL_MINOR=$ol_minor
+echo OL_PATCH=$ol_patch
+echo OL_API_INC=$ol_api_inc
+echo OL_API_LIB_RELEASE=$ol_api_lib_release
+echo OL_API_LIB_VERSION=$ol_api_lib_version
+echo OL_VERSION=$ol_version
+echo OL_TYPE=$ol_type
+echo OL_STRING=\"${ol_string}\"
+echo OL_RELEASE_DATE=\"${ol_release_date}\"
diff --git a/build/version.var b/build/version.var
new file mode 100644
index 0000000..64ac2df
--- /dev/null
+++ b/build/version.var
@@ -0,0 +1,23 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+ol_package=OpenLDAP
+ol_major=2
+ol_minor=5
+ol_patch=13
+ol_api_inc=20513
+ol_api_current=1
+ol_api_revision=8
+ol_api_age=1
+ol_release_date="2022/07/14"
diff --git a/clients/Makefile.in b/clients/Makefile.in
new file mode 100644
index 0000000..1423082
--- /dev/null
+++ b/clients/Makefile.in
@@ -0,0 +1,17 @@
+# Clients Makefile.in for OpenLDAP
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SUBDIRS = tools
+
diff --git a/clients/tools/Makefile.in b/clients/tools/Makefile.in
new file mode 100644
index 0000000..1fecb61
--- /dev/null
+++ b/clients/tools/Makefile.in
@@ -0,0 +1,138 @@
+# Makefile for LDAP tools
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SRCS = ldapsearch.c ldapmodify.c ldapdelete.c ldapmodrdn.c \
+ ldappasswd.c ldapwhoami.c ldapvc.c ldapcompare.c \
+ ldapexop.c ldapurl.c common.c
+OBJS = ldapsearch.o ldapmodify.o ldapdelete.o ldapmodrdn.o \
+ ldappasswd.o ldapwhoami.o ldapvc.o ldapcompare.o \
+ ldapexop.o ldapurl.o common.o
+
+LDAP_INCDIR= ../../include
+LDAP_LIBDIR= ../../libraries
+
+MKVOPTS = -s
+
+XLIBS = $(LDAP_L)
+XXLIBS = $(SECURITY_LIBS) $(LUTIL_LIBS) $(CLIENT_LIBS)
+
+XSRCS = ldsversion.c ldmversion.c lddversion.c ldrversion.c ldpversion.c \
+ ldwversion.c ldvversion.c ldcversion.c ldeversion.c lduversion.c
+
+PROGRAMS = ldapsearch ldapmodify ldapdelete ldapmodrdn \
+ ldappasswd ldapwhoami ldapvc ldapcompare ldapexop ldapurl
+
+
+ldapsearch: ldsversion.o
+ $(LTLINK) -o $@ ldapsearch.o common.o ldsversion.o $(LIBS)
+
+ldapmodify: ldmversion.o
+ $(LTLINK) -o $@ ldapmodify.o common.o ldmversion.o $(LIBS)
+
+ldapdelete: lddversion.o
+ $(LTLINK) -o $@ ldapdelete.o common.o lddversion.o $(LIBS)
+
+ldapmodrdn: ldrversion.o
+ $(LTLINK) -o $@ ldapmodrdn.o common.o ldrversion.o $(LIBS)
+
+ldappasswd: ldpversion.o
+ $(LTLINK) -o $@ ldappasswd.o common.o ldpversion.o $(LIBS)
+
+ldapwhoami: ldwversion.o
+ $(LTLINK) -o $@ ldapwhoami.o common.o ldwversion.o $(LIBS)
+
+ldapvc: ldvversion.o
+ $(LTLINK) -o $@ ldapvc.o common.o ldvversion.o $(LIBS)
+
+ldapcompare: ldcversion.o
+ $(LTLINK) -o $@ ldapcompare.o common.o ldcversion.o $(LIBS)
+
+ldapexop: ldeversion.o
+ $(LTLINK) -o $@ ldapexop.o common.o ldeversion.o $(LIBS)
+
+ldapurl: lduversion.o
+ $(LTLINK) -o $@ ldapurl.o lduversion.o $(LIBS)
+
+ldsversion.c: Makefile
+ @-$(RM) $@
+ $(MKVERSION) $(MKVOPTS) ldapsearch > $@
+
+ldsversion.o: ldapsearch.o common.o $(XLIBS)
+
+ldmversion.c: Makefile
+ @-$(RM) $@
+ $(MKVERSION) $(MKVOPTS) ldapmodify > $@
+
+ldmversion.o: ldapmodify.o common.o $(XLIBS)
+
+lddversion.c: Makefile
+ @-$(RM) $@
+ $(MKVERSION) $(MKVOPTS) ldapdelete > $@
+
+lddversion.o: ldapdelete.o common.o $(XLIBS)
+
+ldpversion.c: Makefile
+ @-$(RM) $@
+ $(MKVERSION) $(MKVOPTS) ldappasswd > $@
+
+ldpversion.o: ldappasswd.o common.o $(XLIBS)
+
+ldrversion.c: Makefile
+ @-$(RM) $@
+ $(MKVERSION) $(MKVOPTS) ldapmodrdn > $@
+
+ldrversion.o: ldapmodrdn.o common.o $(XLIBS)
+
+ldwversion.c: Makefile
+ @-$(RM) $@
+ $(MKVERSION) $(MKVOPTS) ldapwhoami > $@
+
+ldwversion.o: ldapwhoami.o common.o $(XLIBS)
+
+ldvversion.c: Makefile
+ @-$(RM) $@
+ $(MKVERSION) $(MKVOPTS) ldapvc > $@
+
+ldvversion.o: ldapvc.o common.o $(XLIBS)
+
+ldcversion.c: Makefile
+ @-$(RM) $@
+ $(MKVERSION) $(MKVOPTS) ldapcompare > $@
+
+ldcversion.o: ldapcompare.o common.o $(XLIBS)
+
+ldeversion.c: Makefile
+ @-$(RM) $@
+ $(MKVERSION) $(MKVOPTS) ldapexop > $@
+
+ldeversion.o: ldapexop.o common.o $(XLIBS)
+
+lduversion.c: Makefile
+ @-$(RM) $@
+ $(MKVERSION) $(MKVOPTS) ldapurl > $@
+
+lduversion.o: ldapurl.o $(XLIBS)
+
+install-local: FORCE
+ -$(MKDIR) $(DESTDIR)$(bindir)
+ @( \
+ for prg in $(PROGRAMS); do \
+ $(LTINSTALL) $(INSTALLFLAGS) $(STRIP_OPTS) -m 755 $$prg$(EXEEXT) \
+ $(DESTDIR)$(bindir); \
+ done \
+ )
+ $(RM) $(DESTDIR)$(bindir)/ldapadd$(EXEEXT)
+ $(LN_S) $(DESTDIR)$(bindir)/ldapmodify$(EXEEXT) $(DESTDIR)$(bindir)/ldapadd$(EXEEXT)
+
diff --git a/clients/tools/common.c b/clients/tools/common.c
new file mode 100644
index 0000000..b88f219
--- /dev/null
+++ b/clients/tools/common.c
@@ -0,0 +1,2778 @@
+/* common.c - common routines for the ldap client tools */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2003 Kurt D. Zeilenga.
+ * Portions Copyright 2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This file was initially created by Hallvard B. Furuseth based (in
+ * part) upon argument parsing code for individual tools located in
+ * this directory. Additional contributors include:
+ * Kurt D. Zeilenga (additional common argument and control support)
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/signal.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/unistd.h>
+#include <ac/errno.h>
+#include <ac/time.h>
+#include <ac/socket.h>
+
+#ifdef HAVE_CYRUS_SASL
+#ifdef HAVE_SASL_SASL_H
+#include <sasl/sasl.h>
+#else
+#include <sasl.h>
+#endif
+#endif
+
+#include <ldap.h>
+
+#include "ldif.h"
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "ldap_defaults.h"
+#include "ldap_pvt.h"
+#include "lber_pvt.h"
+
+#include "common.h"
+
+/* input-related vars */
+
+/* misc. parameters */
+tool_type_t tool_type;
+int contoper = 0;
+int debug = 0;
+char *infile = NULL;
+int dont = 0;
+int nocanon = 0;
+int referrals = 0;
+int verbose = 0;
+int ldif = 0;
+ber_len_t ldif_wrap = 0;
+char *prog = NULL;
+
+/* connection */
+char *ldapuri = NULL;
+int use_tls = 0;
+int protocol = -1;
+int version = 0;
+
+/* authc/authz */
+int authmethod = -1;
+char *binddn = NULL;
+int want_bindpw = 0;
+struct berval passwd = { 0, NULL };
+char *pw_file = NULL;
+#ifdef HAVE_CYRUS_SASL
+unsigned sasl_flags = LDAP_SASL_AUTOMATIC;
+char *sasl_realm = NULL;
+char *sasl_authc_id = NULL;
+char *sasl_authz_id = NULL;
+char *sasl_mech = NULL;
+char *sasl_secprops = NULL;
+#endif
+
+/* controls */
+int assertctl;
+char *assertion = NULL;
+struct berval assertionvalue = BER_BVNULL;
+char *authzid = NULL;
+int authzcrit = 1;
+/* support deprecated early version of proxyAuthz */
+#define LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ "2.16.840.1.113730.3.4.12"
+#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
+char *proxydn = NULL;
+#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
+int manageDIT = 0;
+int manageDSAit = 0;
+int noop = 0;
+int ppolicy = 0;
+int preread = 0;
+static char *preread_attrs = NULL;
+int postread = 0;
+static char *postread_attrs = NULL;
+ber_int_t pr_morePagedResults = 1;
+struct berval pr_cookie = { 0, NULL };
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+int chaining = 0;
+static int chainingResolve = -1;
+static int chainingContinuation = -1;
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+#ifdef LDAP_CONTROL_X_SESSION_TRACKING
+static int sessionTracking = 0;
+static char *sessionTrackingName;
+struct berval stValue;
+#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
+ber_int_t vlvPos;
+ber_int_t vlvCount;
+struct berval *vlvContext;
+static int bauthzid;
+
+LDAPControl *unknown_ctrls = NULL;
+int unknown_ctrls_num = 0;
+
+/* options */
+struct timeval nettimeout = { -1 , 0 };
+
+typedef int (*print_ctrl_fn)( LDAP *ld, LDAPControl *ctrl );
+
+static int print_preread( LDAP *ld, LDAPControl *ctrl );
+static int print_postread( LDAP *ld, LDAPControl *ctrl );
+static int print_paged_results( LDAP *ld, LDAPControl *ctrl );
+static int print_psearch( LDAP *ld, LDAPControl *ctrl );
+#ifdef LDAP_CONTROL_AUTHZID_RESPONSE
+static int print_authzid( LDAP *ld, LDAPControl *ctrl );
+#endif
+#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
+static int print_ppolicy( LDAP *ld, LDAPControl *ctrl );
+#endif
+static int print_sss( LDAP *ld, LDAPControl *ctrl );
+static int print_vlv( LDAP *ld, LDAPControl *ctrl );
+#ifdef LDAP_CONTROL_X_DEREF
+static int print_deref( LDAP *ld, LDAPControl *ctrl );
+#endif
+#ifdef LDAP_CONTROL_X_WHATFAILED
+static int print_whatfailed( LDAP *ld, LDAPControl *ctrl );
+#endif
+static int print_syncstate( LDAP *ld, LDAPControl *ctrl );
+static int print_syncdone( LDAP *ld, LDAPControl *ctrl );
+#ifdef LDAP_CONTROL_X_DIRSYNC
+static int print_dirsync( LDAP *ld, LDAPControl *ctrl );
+#endif
+#ifdef LDAP_CONTROL_X_ACCOUNT_USABILITY
+static int print_account_usability( LDAP *ld, LDAPControl *ctrl );
+#endif
+#ifdef LDAP_CONTROL_X_PASSWORD_EXPIRED
+static int print_netscape_pwexpired( LDAP *ld, LDAPControl *ctrl );
+static int print_netscape_pwexpiring( LDAP *ld, LDAPControl *ctrl );
+#endif
+
+static struct tool_ctrls_t {
+ const char *oid;
+ unsigned mask;
+ print_ctrl_fn func;
+} tool_ctrl_response[] = {
+ { LDAP_CONTROL_PRE_READ, TOOL_ALL, print_preread },
+ { LDAP_CONTROL_POST_READ, TOOL_ALL, print_postread },
+ { LDAP_CONTROL_PAGEDRESULTS, TOOL_SEARCH, print_paged_results },
+ { LDAP_CONTROL_PERSIST_ENTRY_CHANGE_NOTICE, TOOL_SEARCH, print_psearch },
+#ifdef LDAP_CONTROL_AUTHZID_RESPONSE
+ /* this is generally deprecated in favor of LDAP WhoAmI? operation, hence only supported as a VC inner control */
+ { LDAP_CONTROL_AUTHZID_RESPONSE, TOOL_VC, print_authzid },
+#endif
+#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
+ { LDAP_CONTROL_PASSWORDPOLICYRESPONSE, TOOL_ALL, print_ppolicy },
+#endif
+ { LDAP_CONTROL_SORTRESPONSE, TOOL_SEARCH, print_sss },
+ { LDAP_CONTROL_VLVRESPONSE, TOOL_SEARCH, print_vlv },
+#ifdef LDAP_CONTROL_X_DEREF
+ { LDAP_CONTROL_X_DEREF, TOOL_SEARCH, print_deref },
+#endif
+#ifdef LDAP_CONTROL_X_WHATFAILED
+ { LDAP_CONTROL_X_WHATFAILED, TOOL_ALL, print_whatfailed },
+#endif
+ { LDAP_CONTROL_SYNC_STATE, TOOL_SEARCH, print_syncstate },
+ { LDAP_CONTROL_SYNC_DONE, TOOL_SEARCH, print_syncdone },
+#ifdef LDAP_CONTROL_X_DIRSYNC
+ { LDAP_CONTROL_X_DIRSYNC, TOOL_SEARCH, print_dirsync },
+#endif
+#ifdef LDAP_CONTROL_X_ACCOUNT_USABILITY
+ { LDAP_CONTROL_X_ACCOUNT_USABILITY, TOOL_SEARCH, print_account_usability },
+#endif
+#ifdef LDAP_CONTROL_X_PASSWORD_EXPIRED
+ { LDAP_CONTROL_X_PASSWORD_EXPIRED, TOOL_ALL, print_netscape_pwexpired },
+ { LDAP_CONTROL_X_PASSWORD_EXPIRING, TOOL_ALL, print_netscape_pwexpiring },
+#endif
+ { NULL, 0, NULL }
+};
+
+/* "features" */
+enum { Intr_None = 0, Intr_Abandon, Intr_Cancel, Intr_Ignore };
+static volatile sig_atomic_t gotintr, abcan;
+
+int backlog;
+
+
+#ifdef LDAP_CONTROL_X_SESSION_TRACKING
+static int
+st_value( LDAP *ld, struct berval *value )
+{
+ char *ip = NULL, *name = NULL;
+ struct berval id = { 0 };
+ char namebuf[ MAXHOSTNAMELEN ];
+
+ if ( gethostname( namebuf, sizeof( namebuf ) ) == 0 ) {
+ struct hostent *h;
+ struct in_addr addr;
+
+ name = namebuf;
+
+ h = gethostbyname( name );
+ if ( h != NULL ) {
+ AC_MEMCPY( &addr, h->h_addr, sizeof( addr ) );
+ ip = inet_ntoa( addr );
+ }
+ }
+
+ if ( sessionTrackingName != NULL ) {
+ ber_str2bv( sessionTrackingName , 0, 0, &id );
+ } else
+#ifdef HAVE_CYRUS_SASL
+ if ( sasl_authz_id != NULL ) {
+ ber_str2bv( sasl_authz_id, 0, 0, &id );
+
+ } else if ( sasl_authc_id != NULL ) {
+ ber_str2bv( sasl_authc_id, 0, 0, &id );
+
+ } else
+#endif /* HAVE_CYRUS_SASL */
+ if ( binddn != NULL ) {
+ ber_str2bv( binddn, 0, 0, &id );
+ }
+
+ if ( ldap_create_session_tracking_value( ld,
+ ip, name, LDAP_CONTROL_X_SESSION_TRACKING_USERNAME,
+ &id, &stValue ) )
+ {
+ fprintf( stderr, _("Session tracking control encoding error!\n") );
+ return -1;
+ }
+
+ return 0;
+}
+#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
+
+RETSIGTYPE
+do_sig( int sig )
+{
+ gotintr = abcan;
+}
+
+void
+tool_init( tool_type_t type )
+{
+ tool_type = type;
+ ldap_pvt_setlocale(LC_MESSAGES, "");
+ ldap_pvt_bindtextdomain(OPENLDAP_PACKAGE, LDAP_LOCALEDIR);
+ ldap_pvt_textdomain(OPENLDAP_PACKAGE);
+}
+
+void
+tool_destroy( void )
+{
+ static int destroyed;
+ if ( destroyed++ )
+ return;
+
+#ifdef HAVE_CYRUS_SASL
+ sasl_done();
+#endif
+#ifdef HAVE_TLS
+ ldap_pvt_tls_destroy();
+#endif
+
+ if ( ldapuri != NULL ) {
+ ber_memfree( ldapuri );
+ ldapuri = NULL;
+ }
+
+ if ( pr_cookie.bv_val != NULL ) {
+ ber_memfree( pr_cookie.bv_val );
+ BER_BVZERO( &pr_cookie );
+ }
+
+ if ( passwd.bv_val != NULL ) {
+ ber_memfree( passwd.bv_val );
+ BER_BVZERO( &passwd );
+ }
+
+#ifdef LDAP_CONTROL_X_SESSION_TRACKING
+ if ( !BER_BVISNULL( &stValue ) ) {
+ ber_memfree( stValue.bv_val );
+ BER_BVZERO( &stValue );
+ }
+
+#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
+}
+
+void
+tool_common_usage( void )
+{
+ static const char *const descriptions[] = {
+N_(" -d level set LDAP debugging level to `level'\n"),
+N_(" -D binddn bind DN\n"),
+N_(" -e [!]<ext>[=<extparam>] general extensions (! indicates criticality)\n")
+N_(" [!]assert=<filter> (RFC 4528; a RFC 4515 Filter string)\n")
+N_(" [!]authzid=<authzid> (RFC 4370; \"dn:<dn>\" or \"u:<user>\")\n")
+N_(" [!]bauthzid (RFC 3829)\n")
+#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
+#if 0
+ /* non-advertized support for proxyDN */
+N_(" [!]proxydn=<dn> (a RFC 4514 DN string)\n")
+#endif
+#endif
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+N_(" [!]chaining[=<resolveBehavior>[/<continuationBehavior>]]\n")
+N_(" one of \"chainingPreferred\", \"chainingRequired\",\n")
+N_(" \"referralsPreferred\", \"referralsRequired\"\n")
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+N_(" [!]manageDSAit (RFC 3296)\n")
+N_(" [!]noop\n")
+#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
+N_(" ppolicy\n")
+#endif
+N_(" [!]postread[=<attrs>] (RFC 4527; comma-separated attr list)\n")
+N_(" [!]preread[=<attrs>] (RFC 4527; comma-separated attr list)\n")
+N_(" [!]relax\n")
+#ifdef LDAP_CONTROL_X_SESSION_TRACKING
+N_(" [!]sessiontracking[=<username>]\n")
+#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
+N_(" abandon, cancel, ignore (SIGINT sends abandon/cancel,\n"
+ " or ignores response; if critical, doesn't wait for SIGINT.\n"
+ " not really controls)\n")
+N_(" -H URI LDAP Uniform Resource Identifier(s)\n"),
+N_(" -I use SASL Interactive mode\n"),
+N_(" -n show what would be done but don't actually do it\n"),
+N_(" -N do not use reverse DNS to canonicalize SASL host name\n"),
+N_(" -O props SASL security properties\n"),
+N_(" -o <opt>[=<optparam>] any libldap ldap.conf options, plus\n"),
+N_(" ldif_wrap=<width> (in columns, or \"no\" for no wrapping)\n"),
+N_(" nettimeout=<timeout> (in seconds, or \"none\" or \"max\")\n"),
+N_(" -Q use SASL Quiet mode\n"),
+N_(" -R realm SASL realm\n"),
+N_(" -U authcid SASL authentication identity\n"),
+N_(" -v run in verbose mode (diagnostics to standard output)\n"),
+N_(" -V print version info (-VV only)\n"),
+N_(" -w passwd bind password (for simple authentication)\n"),
+N_(" -W prompt for bind password\n"),
+N_(" -x Simple authentication\n"),
+N_(" -X authzid SASL authorization identity (\"dn:<dn>\" or \"u:<user>\")\n"),
+N_(" -y file Read password from file\n"),
+N_(" -Y mech SASL mechanism\n"),
+N_(" -Z Start TLS request (-ZZ to require successful response)\n"),
+NULL
+ };
+ const char *const *cpp;
+
+ fputs( _("Common options:\n"), stderr );
+ for( cpp = descriptions; *cpp != NULL; cpp++ ) {
+ if( strchr( options, (*cpp)[3] ) || (*cpp)[3] == ' ' ) {
+ fputs( _(*cpp), stderr );
+ }
+ }
+
+ tool_destroy();
+}
+
+void tool_perror(
+ const char *func,
+ int err,
+ const char *extra,
+ const char *matched,
+ const char *info,
+ char **refs )
+{
+ fprintf( stderr, "%s: %s (%d)%s\n",
+ func, ldap_err2string( err ), err, extra ? extra : "" );
+
+ if ( matched && *matched ) {
+ fprintf( stderr, _("\tmatched DN: %s\n"), matched );
+ }
+
+ if ( info && *info ) {
+ fprintf( stderr, _("\tadditional info: %s\n"), info );
+ }
+
+ if ( refs && *refs ) {
+ int i;
+ fprintf( stderr, _("\treferrals:\n") );
+ for( i=0; refs[i]; i++ ) {
+ fprintf( stderr, "\t\t%s\n", refs[i] );
+ }
+ }
+}
+
+
+void
+tool_args( int argc, char **argv )
+{
+ int i;
+
+ while (( i = getopt( argc, argv, options )) != EOF ) {
+ int crit, ival;
+ char *control, *cvalue, *next;
+ switch( i ) {
+ case 'c': /* continuous operation mode */
+ contoper++;
+ break;
+ case 'C': /* referrals: obsolete */
+ referrals++;
+ break;
+ case 'd':
+ ival = strtol( optarg, &next, 10 );
+ if (next == NULL || next[0] != '\0') {
+ fprintf( stderr, "%s: unable to parse debug value \"%s\"\n", prog, optarg);
+ exit(EXIT_FAILURE);
+ }
+ debug |= ival;
+ break;
+ case 'D': /* bind DN */
+ if( binddn != NULL ) {
+ fprintf( stderr, "%s: -D previously specified\n", prog );
+ exit( EXIT_FAILURE );
+ }
+ binddn = optarg;
+ break;
+ case 'e': /* general extensions (controls and such) */
+ /* should be extended to support comma separated list of
+ * [!]key[=value] parameters, e.g. -e !foo,bar=567
+ */
+
+ crit = 0;
+ cvalue = NULL;
+ while ( optarg[0] == '!' ) {
+ crit++;
+ optarg++;
+ }
+
+ control = optarg;
+ if ( (cvalue = strchr( control, '=' )) != NULL ) {
+ *cvalue++ = '\0';
+ }
+
+ if ( strcasecmp( control, "assert" ) == 0 ) {
+ if( assertctl ) {
+ fprintf( stderr, "assert control previously specified\n");
+ exit( EXIT_FAILURE );
+ }
+ if( cvalue == NULL ) {
+ fprintf( stderr, "assert: control value expected\n" );
+ usage();
+ }
+
+ assertctl = 1 + crit;
+
+ assert( assertion == NULL );
+ assertion = cvalue;
+
+ } else if ( strcasecmp( control, "authzid" ) == 0 ) {
+ if( authzid != NULL ) {
+ fprintf( stderr, "authzid control previously specified\n");
+ exit( EXIT_FAILURE );
+ }
+#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
+ if( proxydn != NULL ) {
+ fprintf( stderr, "authzid control incompatible with proxydn\n");
+ exit( EXIT_FAILURE );
+ }
+#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
+ if( cvalue == NULL ) {
+ fprintf( stderr, "authzid: control value expected\n" );
+ usage();
+ }
+ if( !crit ) {
+ fprintf( stderr, "authzid: must be marked critical\n" );
+ usage();
+ } else if ( crit > 1 ) {
+ /* purposely flag proxied authorization
+ * as non-critical, to test DSA */
+ authzcrit = 0;
+ }
+
+ assert( authzid == NULL );
+ authzid = cvalue;
+
+#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
+ } else if ( strcasecmp( control, "proxydn" ) == 0 ) {
+ if( proxydn != NULL ) {
+ fprintf( stderr, "proxydn control previously specified\n");
+ exit( EXIT_FAILURE );
+ }
+ if( authzid != NULL ) {
+ fprintf( stderr, "proxydn control incompatible with authzid\n");
+ exit( EXIT_FAILURE );
+ }
+ if( cvalue == NULL ) {
+ fprintf( stderr, "proxydn: control value expected\n" );
+ usage();
+ }
+ if( !crit ) {
+ fprintf( stderr, "proxydn: must be marked critical\n" );
+ usage();
+ } else if ( crit > 1 ) {
+ /* purposely flag proxied authorization
+ * as non-critical, to test DSA */
+ authzcrit = 0;
+ }
+
+ assert( proxydn == NULL );
+ proxydn = cvalue;
+#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
+
+ } else if ( strcasecmp( control, "bauthzid" ) == 0 ) {
+ if( bauthzid ) {
+ fprintf( stderr, "bauthzid control previously specified\n");
+ exit( EXIT_FAILURE );
+ }
+ if( cvalue != NULL ) {
+ fprintf( stderr, "bauthzid: no control value expected\n" );
+ usage();
+ }
+ bauthzid = 1 + crit;
+
+ } else if ( ( strcasecmp( control, "relax" ) == 0 ) ||
+ ( strcasecmp( control, "manageDIT" ) == 0 ) )
+ {
+ if( manageDIT ) {
+ fprintf( stderr,
+ "relax control previously specified\n");
+ exit( EXIT_FAILURE );
+ }
+ if( cvalue != NULL ) {
+ fprintf( stderr,
+ "relax: no control value expected\n" );
+ usage();
+ }
+
+ manageDIT = 1 + crit;
+
+ } else if ( strcasecmp( control, "manageDSAit" ) == 0 ) {
+ if( manageDSAit ) {
+ fprintf( stderr,
+ "manageDSAit control previously specified\n");
+ exit( EXIT_FAILURE );
+ }
+ if( cvalue != NULL ) {
+ fprintf( stderr,
+ "manageDSAit: no control value expected\n" );
+ usage();
+ }
+
+ manageDSAit = 1 + crit;
+
+ } else if ( strcasecmp( control, "noop" ) == 0 ) {
+ if( noop ) {
+ fprintf( stderr, "noop control previously specified\n");
+ exit( EXIT_FAILURE );
+ }
+ if( cvalue != NULL ) {
+ fprintf( stderr, "noop: no control value expected\n" );
+ usage();
+ }
+
+ noop = 1 + crit;
+
+#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
+ } else if ( strcasecmp( control, "ppolicy" ) == 0 ) {
+ if( ppolicy ) {
+ fprintf( stderr, "ppolicy control previously specified\n");
+ exit( EXIT_FAILURE );
+ }
+ if( cvalue != NULL ) {
+ fprintf( stderr, "ppolicy: no control value expected\n" );
+ usage();
+ }
+ if( crit ) {
+ fprintf( stderr, "ppolicy: critical flag not allowed\n" );
+ usage();
+ }
+
+ ppolicy = 1;
+#endif
+
+ } else if ( strcasecmp( control, "preread" ) == 0 ) {
+ if( preread ) {
+ fprintf( stderr, "preread control previously specified\n");
+ exit( EXIT_FAILURE );
+ }
+
+ preread = 1 + crit;
+ preread_attrs = cvalue;
+
+ } else if ( strcasecmp( control, "postread" ) == 0 ) {
+ if( postread ) {
+ fprintf( stderr, "postread control previously specified\n");
+ exit( EXIT_FAILURE );
+ }
+
+ postread = 1 + crit;
+ postread_attrs = cvalue;
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ } else if ( strcasecmp( control, "chaining" ) == 0 ) {
+ if ( chaining ) {
+ fprintf( stderr, "chaining control previously specified\n");
+ exit( EXIT_FAILURE );
+ }
+
+ chaining = 1 + crit;
+
+ if ( cvalue != NULL ) {
+ char *continuation;
+
+ continuation = strchr( cvalue, '/' );
+ if ( continuation ) {
+ /* FIXME: this makes sense only in searches */
+ *continuation++ = '\0';
+ if ( strcasecmp( continuation, "chainingPreferred" ) == 0 ) {
+ chainingContinuation = LDAP_CHAINING_PREFERRED;
+ } else if ( strcasecmp( continuation, "chainingRequired" ) == 0 ) {
+ chainingContinuation = LDAP_CHAINING_REQUIRED;
+ } else if ( strcasecmp( continuation, "referralsPreferred" ) == 0 ) {
+ chainingContinuation = LDAP_REFERRALS_PREFERRED;
+ } else if ( strcasecmp( continuation, "referralsRequired" ) == 0 ) {
+ chainingContinuation = LDAP_REFERRALS_REQUIRED;
+ } else {
+ fprintf( stderr,
+ "chaining behavior control "
+ "continuation value \"%s\" invalid\n",
+ continuation );
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ if ( strcasecmp( cvalue, "chainingPreferred" ) == 0 ) {
+ chainingResolve = LDAP_CHAINING_PREFERRED;
+ } else if ( strcasecmp( cvalue, "chainingRequired" ) == 0 ) {
+ chainingResolve = LDAP_CHAINING_REQUIRED;
+ } else if ( strcasecmp( cvalue, "referralsPreferred" ) == 0 ) {
+ chainingResolve = LDAP_REFERRALS_PREFERRED;
+ } else if ( strcasecmp( cvalue, "referralsRequired" ) == 0 ) {
+ chainingResolve = LDAP_REFERRALS_REQUIRED;
+ } else {
+ fprintf( stderr,
+ "chaining behavior control "
+ "resolve value \"%s\" invalid\n",
+ cvalue);
+ exit( EXIT_FAILURE );
+ }
+ }
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+#ifdef LDAP_CONTROL_X_SESSION_TRACKING
+ } else if ( strcasecmp( control, "sessiontracking" ) == 0 ) {
+ if ( sessionTracking ) {
+ fprintf( stderr, "%s: session tracking can be only specified once\n", prog );
+ exit( EXIT_FAILURE );
+ }
+ sessionTracking = 1;
+ if ( crit ) {
+ fprintf( stderr, "sessiontracking: critical flag not allowed\n" );
+ usage();
+ }
+ if ( cvalue ) {
+ sessionTrackingName = cvalue;
+ }
+#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
+
+ /* this shouldn't go here, really; but it's a feature... */
+ } else if ( strcasecmp( control, "abandon" ) == 0 ) {
+ abcan = Intr_Abandon;
+ if ( crit ) {
+ gotintr = abcan;
+ }
+
+ } else if ( strcasecmp( control, "cancel" ) == 0 ) {
+ abcan = Intr_Cancel;
+ if ( crit ) {
+ gotintr = abcan;
+ }
+
+ } else if ( strcasecmp( control, "ignore" ) == 0 ) {
+ abcan = Intr_Ignore;
+ if ( crit ) {
+ gotintr = abcan;
+ }
+
+ } else if ( strcasecmp( control, "backlog" ) == 0 ) {
+ /* special search: accumulate lots of responses
+ * but don't read any, force slapd writer to wait.
+ * Then abandon the search and issue a new one.
+ */
+ backlog = 1;
+
+ } else if ( tool_is_oid( control ) ) {
+ LDAPControl *tmpctrls, ctrl;
+
+ if ( unknown_ctrls != NULL ) {
+ int i;
+ for ( i = 0; unknown_ctrls[ i ].ldctl_oid != NULL; i++ ) {
+ if ( strcmp( control, unknown_ctrls[ i ].ldctl_oid ) == 0 ) {
+ fprintf( stderr, "%s control previously specified\n", control );
+ exit( EXIT_FAILURE );
+ }
+ }
+ }
+
+ tmpctrls = (LDAPControl *)ber_memrealloc( unknown_ctrls,
+ (unknown_ctrls_num + 1)*sizeof( LDAPControl ) );
+ if ( tmpctrls == NULL ) {
+ fprintf( stderr, "%s: no memory?\n", prog );
+ exit( EXIT_FAILURE );
+ }
+ unknown_ctrls = tmpctrls;
+ ctrl.ldctl_oid = control;
+ ctrl.ldctl_value.bv_val = NULL;
+ ctrl.ldctl_value.bv_len = 0;
+ ctrl.ldctl_iscritical = crit;
+
+ if ( cvalue != NULL ) {
+ struct berval bv;
+ size_t len = strlen( cvalue );
+ int retcode;
+
+ bv.bv_len = LUTIL_BASE64_DECODE_LEN( len );
+ bv.bv_val = ber_memalloc( bv.bv_len + 1 );
+
+ retcode = lutil_b64_pton( cvalue,
+ (unsigned char *)bv.bv_val,
+ bv.bv_len );
+
+ if ( retcode == -1 || (unsigned) retcode > bv.bv_len ) {
+ fprintf( stderr, "Unable to parse value of general control %s\n",
+ control );
+ usage();
+ }
+
+ bv.bv_len = retcode;
+ ctrl.ldctl_value = bv;
+ }
+
+ /* don't free it */
+ control = NULL;
+ unknown_ctrls[ unknown_ctrls_num ] = ctrl;
+ unknown_ctrls_num++;
+
+ } else {
+ fprintf( stderr, "Invalid general control name: %s\n",
+ control );
+ usage();
+ }
+ break;
+ case 'f': /* read from file */
+ if( infile != NULL ) {
+ fprintf( stderr, "%s: -f previously specified\n", prog );
+ exit( EXIT_FAILURE );
+ }
+ infile = optarg;
+ break;
+ case 'H': /* ldap URI */
+ if( ldapuri != NULL ) {
+ fprintf( stderr, "%s: -H previously specified\n", prog );
+ exit( EXIT_FAILURE );
+ }
+ ldapuri = ber_strdup( optarg );
+ break;
+ case 'I':
+#ifdef HAVE_CYRUS_SASL
+ if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
+ fprintf( stderr, "%s: incompatible previous "
+ "authentication choice\n",
+ prog );
+ exit( EXIT_FAILURE );
+ }
+ authmethod = LDAP_AUTH_SASL;
+ sasl_flags = LDAP_SASL_INTERACTIVE;
+ break;
+#else
+ fprintf( stderr, "%s: was not compiled with SASL support\n",
+ prog );
+ exit( EXIT_FAILURE );
+#endif
+ case 'M':
+ /* enable Manage DSA IT */
+ manageDSAit++;
+ break;
+ case 'n': /* print operations, don't actually do them */
+ dont++;
+ break;
+ case 'N':
+ nocanon++;
+ break;
+ case 'o':
+ control = optarg;
+ if ( (cvalue = strchr( control, '=' )) != NULL ) {
+ *cvalue++ = '\0';
+ }
+ for ( next=control; *next; next++ ) {
+ if ( *next == '-' ) {
+ *next = '_';
+ }
+ }
+
+ if ( strcasecmp( control, "nettimeout" ) == 0 ) {
+ if( nettimeout.tv_sec != -1 ) {
+ fprintf( stderr, "nettimeout option previously specified\n");
+ exit( EXIT_FAILURE );
+ }
+ if( cvalue == NULL || cvalue[0] == '\0' ) {
+ fprintf( stderr, "nettimeout: option value expected\n" );
+ usage();
+ }
+ if ( strcasecmp( cvalue, "none" ) == 0 ) {
+ nettimeout.tv_sec = 0;
+ } else if ( strcasecmp( cvalue, "max" ) == 0 ) {
+ nettimeout.tv_sec = LDAP_MAXINT;
+ } else {
+ ival = strtol( cvalue, &next, 10 );
+ if ( next == NULL || next[0] != '\0' ) {
+ fprintf( stderr,
+ _("Unable to parse network timeout \"%s\"\n"), cvalue );
+ exit( EXIT_FAILURE );
+ }
+ nettimeout.tv_sec = ival;
+ }
+ if( nettimeout.tv_sec < 0 || nettimeout.tv_sec > LDAP_MAXINT ) {
+ fprintf( stderr, _("%s: invalid network timeout (%ld) specified\n"),
+ prog, (long)nettimeout.tv_sec );
+ exit( EXIT_FAILURE );
+ }
+
+ } else if ( strcasecmp( control, "ldif_wrap" ) == 0 ) {
+ if ( cvalue == 0 ) {
+ ldif_wrap = LDIF_LINE_WIDTH;
+
+ } else if ( strcasecmp( cvalue, "no" ) == 0 ) {
+ ldif_wrap = LDIF_LINE_WIDTH_MAX;
+
+ } else {
+ unsigned int u;
+ if ( lutil_atou( &u, cvalue ) ) {
+ fprintf( stderr,
+ _("Unable to parse ldif_wrap=\"%s\"\n"), cvalue );
+ exit( EXIT_FAILURE );
+ }
+ ldif_wrap = (ber_len_t)u;
+ }
+
+ } else if ( ldap_pvt_conf_option( control, cvalue, 1 ) ) {
+ fprintf( stderr, "Invalid general option name: %s\n",
+ control );
+ usage();
+ }
+ break;
+ case 'O':
+#ifdef HAVE_CYRUS_SASL
+ if( sasl_secprops != NULL ) {
+ fprintf( stderr, "%s: -O previously specified\n", prog );
+ exit( EXIT_FAILURE );
+ }
+ if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
+ fprintf( stderr, "%s: incompatible previous "
+ "authentication choice\n", prog );
+ exit( EXIT_FAILURE );
+ }
+ authmethod = LDAP_AUTH_SASL;
+ sasl_secprops = optarg;
+#else
+ fprintf( stderr, "%s: not compiled with SASL support\n", prog );
+ exit( EXIT_FAILURE );
+#endif
+ break;
+ case 'P':
+ ival = strtol( optarg, &next, 10 );
+ if ( next == NULL || next[0] != '\0' ) {
+ fprintf( stderr, "%s: unable to parse protocol version \"%s\"\n", prog, optarg );
+ exit( EXIT_FAILURE );
+ }
+ switch( ival ) {
+ case 2:
+ if( protocol == LDAP_VERSION3 ) {
+ fprintf( stderr, "%s: -P 2 incompatible with version %d\n",
+ prog, protocol );
+ exit( EXIT_FAILURE );
+ }
+ protocol = LDAP_VERSION2;
+ break;
+ case 3:
+ if( protocol == LDAP_VERSION2 ) {
+ fprintf( stderr, "%s: -P 2 incompatible with version %d\n",
+ prog, protocol );
+ exit( EXIT_FAILURE );
+ }
+ protocol = LDAP_VERSION3;
+ break;
+ default:
+ fprintf( stderr, "%s: protocol version should be 2 or 3\n",
+ prog );
+ usage();
+ }
+ break;
+ case 'Q':
+#ifdef HAVE_CYRUS_SASL
+ if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
+ fprintf( stderr, "%s: incompatible previous "
+ "authentication choice\n",
+ prog );
+ exit( EXIT_FAILURE );
+ }
+ authmethod = LDAP_AUTH_SASL;
+ sasl_flags = LDAP_SASL_QUIET;
+ break;
+#else
+ fprintf( stderr, "%s: not compiled with SASL support\n",
+ prog );
+ exit( EXIT_FAILURE );
+#endif
+ case 'R':
+#ifdef HAVE_CYRUS_SASL
+ if( sasl_realm != NULL ) {
+ fprintf( stderr, "%s: -R previously specified\n", prog );
+ exit( EXIT_FAILURE );
+ }
+ if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
+ fprintf( stderr, "%s: incompatible previous "
+ "authentication choice\n",
+ prog );
+ exit( EXIT_FAILURE );
+ }
+ authmethod = LDAP_AUTH_SASL;
+ sasl_realm = optarg;
+#else
+ fprintf( stderr, "%s: not compiled with SASL support\n",
+ prog );
+ exit( EXIT_FAILURE );
+#endif
+ break;
+ case 'U':
+#ifdef HAVE_CYRUS_SASL
+ if( sasl_authc_id != NULL ) {
+ fprintf( stderr, "%s: -U previously specified\n", prog );
+ exit( EXIT_FAILURE );
+ }
+ if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
+ fprintf( stderr, "%s: incompatible previous "
+ "authentication choice\n",
+ prog );
+ exit( EXIT_FAILURE );
+ }
+ authmethod = LDAP_AUTH_SASL;
+ sasl_authc_id = optarg;
+#else
+ fprintf( stderr, "%s: not compiled with SASL support\n",
+ prog );
+ exit( EXIT_FAILURE );
+#endif
+ break;
+ case 'v': /* verbose mode */
+ verbose++;
+ break;
+ case 'V': /* version */
+ version++;
+ break;
+ case 'w': /* password */
+ passwd.bv_val = ber_strdup( optarg );
+ {
+ char* p;
+
+ for( p = optarg; *p != '\0'; p++ ) {
+ *p = '\0';
+ }
+ }
+ passwd.bv_len = strlen( passwd.bv_val );
+ break;
+ case 'W':
+ want_bindpw++;
+ break;
+ case 'y':
+ pw_file = optarg;
+ break;
+ case 'Y':
+#ifdef HAVE_CYRUS_SASL
+ if( sasl_mech != NULL ) {
+ fprintf( stderr, "%s: -Y previously specified\n", prog );
+ exit( EXIT_FAILURE );
+ }
+ if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
+ fprintf( stderr,
+ "%s: incompatible with authentication choice\n", prog );
+ exit( EXIT_FAILURE );
+ }
+ authmethod = LDAP_AUTH_SASL;
+ sasl_mech = optarg;
+#else
+ fprintf( stderr, "%s: not compiled with SASL support\n", prog );
+ exit( EXIT_FAILURE );
+#endif
+ break;
+ case 'x':
+ if( authmethod != -1 && authmethod != LDAP_AUTH_SIMPLE ) {
+ fprintf( stderr, "%s: incompatible with previous "
+ "authentication choice\n", prog );
+ exit( EXIT_FAILURE );
+ }
+ authmethod = LDAP_AUTH_SIMPLE;
+ break;
+ case 'X':
+#ifdef HAVE_CYRUS_SASL
+ if( sasl_authz_id != NULL ) {
+ fprintf( stderr, "%s: -X previously specified\n", prog );
+ exit( EXIT_FAILURE );
+ }
+ if( authmethod != -1 && authmethod != LDAP_AUTH_SASL ) {
+ fprintf( stderr, "%s: -X incompatible with "
+ "authentication choice\n", prog );
+ exit( EXIT_FAILURE );
+ }
+ authmethod = LDAP_AUTH_SASL;
+ sasl_authz_id = optarg;
+#else
+ fprintf( stderr, "%s: not compiled with SASL support\n", prog );
+ exit( EXIT_FAILURE );
+#endif
+ break;
+ case 'Z':
+#ifdef HAVE_TLS
+ use_tls++;
+#else
+ fprintf( stderr, "%s: not compiled with TLS support\n", prog );
+ exit( EXIT_FAILURE );
+#endif
+ break;
+ default:
+ if( handle_private_option( i ) ) break;
+ fprintf( stderr, "%s: unrecognized option -%c\n",
+ prog, optopt );
+ usage();
+ }
+ }
+
+ {
+ /* prevent bad linking */
+ LDAPAPIInfo api;
+ api.ldapai_info_version = LDAP_API_INFO_VERSION;
+
+ if ( ldap_get_option(NULL, LDAP_OPT_API_INFO, &api)
+ != LDAP_OPT_SUCCESS )
+ {
+ fprintf( stderr, "%s: ldap_get_option(API_INFO) failed\n", prog );
+ exit( EXIT_FAILURE );
+ }
+
+ if (api.ldapai_info_version != LDAP_API_INFO_VERSION) {
+ fprintf( stderr, "LDAP APIInfo version mismatch: "
+ "library %d, header %d\n",
+ api.ldapai_info_version, LDAP_API_INFO_VERSION );
+ exit( EXIT_FAILURE );
+ }
+
+ if( api.ldapai_api_version != LDAP_API_VERSION ) {
+ fprintf( stderr, "LDAP API version mismatch: "
+ "library %d, header %d\n",
+ api.ldapai_api_version, LDAP_API_VERSION );
+ exit( EXIT_FAILURE );
+ }
+
+ if( strcmp(api.ldapai_vendor_name, LDAP_VENDOR_NAME ) != 0 ) {
+ fprintf( stderr, "LDAP vendor name mismatch: "
+ "library %s, header %s\n",
+ api.ldapai_vendor_name, LDAP_VENDOR_NAME );
+ exit( EXIT_FAILURE );
+ }
+
+ if( api.ldapai_vendor_version != LDAP_VENDOR_VERSION ) {
+ fprintf( stderr, "LDAP vendor version mismatch: "
+ "library %d, header %d\n",
+ api.ldapai_vendor_version, LDAP_VENDOR_VERSION );
+ exit( EXIT_FAILURE );
+ }
+
+ if (version) {
+ fprintf( stderr, "%s: %s\t(LDAP library: %s %d)\n",
+ prog, __Version,
+ LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION );
+ if (version > 1) exit( EXIT_SUCCESS );
+ }
+
+ ldap_memfree( api.ldapai_vendor_name );
+ ber_memvfree( (void **)api.ldapai_extensions );
+ }
+
+ if (protocol == -1)
+ protocol = LDAP_VERSION3;
+
+ if (authmethod == -1 && protocol > LDAP_VERSION2) {
+#ifdef HAVE_CYRUS_SASL
+ if ( binddn != NULL ) {
+ authmethod = LDAP_AUTH_SIMPLE;
+ } else {
+ authmethod = LDAP_AUTH_SASL;
+ }
+#else
+ authmethod = LDAP_AUTH_SIMPLE;
+#endif
+ }
+
+ if( protocol == LDAP_VERSION2 ) {
+ if( assertctl || authzid || manageDIT || manageDSAit ||
+#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
+ proxydn ||
+#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ chaining ||
+#endif
+#ifdef LDAP_CONTROL_X_SESSION_TRACKING
+ sessionTracking ||
+#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
+ noop || ppolicy || preread || postread )
+ {
+ fprintf( stderr, "%s: -e/-M incompatible with LDAPv2\n", prog );
+ exit( EXIT_FAILURE );
+ }
+#ifdef HAVE_TLS
+ if( use_tls ) {
+ fprintf( stderr, "%s: -Z incompatible with LDAPv2\n", prog );
+ exit( EXIT_FAILURE );
+ }
+#endif
+#ifdef HAVE_CYRUS_SASL
+ if( authmethod == LDAP_AUTH_SASL ) {
+ fprintf( stderr, "%s: -[IOQRUXY] incompatible with LDAPv2\n",
+ prog );
+ exit( EXIT_FAILURE );
+ }
+#endif
+ }
+
+ if ( ( pw_file || want_bindpw ) && !BER_BVISNULL( &passwd ) ) {
+ fprintf( stderr, "%s: -%c incompatible with -w\n",
+ prog, ( pw_file ? 'y' : 'W' ) );
+ exit( EXIT_FAILURE );
+ }
+}
+
+
+LDAP *
+tool_conn_setup( int dont, void (*private_setup)( LDAP * ) )
+{
+ LDAP *ld = NULL;
+
+ if ( debug ) {
+ if( ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &debug )
+ != LBER_OPT_SUCCESS )
+ {
+ fprintf( stderr,
+ "Could not set LBER_OPT_DEBUG_LEVEL %d\n", debug );
+ }
+ if( ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &debug )
+ != LDAP_OPT_SUCCESS )
+ {
+ fprintf( stderr,
+ "Could not set LDAP_OPT_DEBUG_LEVEL %d\n", debug );
+ }
+ }
+
+#ifdef SIGPIPE
+ (void) SIGNAL( SIGPIPE, SIG_IGN );
+#endif
+
+ if ( abcan ) {
+ SIGNAL( SIGINT, do_sig );
+ }
+
+ if ( !dont ) {
+ int rc;
+
+ if ( ldapuri != NULL ) {
+ LDAPURLDesc *ludlist, **ludp;
+ char **urls = NULL;
+ int nurls = 0;
+
+ rc = ldap_url_parselist( &ludlist, ldapuri );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ fprintf( stderr,
+ "Could not parse LDAP URI(s)=%s (%d)\n",
+ ldapuri, rc );
+ exit( EXIT_FAILURE );
+ }
+
+ for ( ludp = &ludlist; *ludp != NULL; ) {
+ LDAPURLDesc *lud = *ludp;
+ char **tmp;
+
+ if ( lud->lud_dn != NULL && lud->lud_dn[ 0 ] != '\0' &&
+ ( lud->lud_host == NULL || lud->lud_host[0] == '\0' ) )
+ {
+ /* if no host but a DN is provided,
+ * use DNS SRV to gather the host list
+ * and turn it into a list of URIs
+ * using the scheme provided */
+ char *domain = NULL,
+ *hostlist = NULL,
+ **hosts = NULL;
+ int i,
+ len_proto = strlen( lud->lud_scheme );
+
+ if ( ldap_dn2domain( lud->lud_dn, &domain )
+ || domain == NULL )
+ {
+ fprintf( stderr,
+ "DNS SRV: Could not turn "
+ "DN=\"%s\" into a domain\n",
+ lud->lud_dn );
+ goto dnssrv_free;
+ }
+
+ rc = ldap_domain2hostlist( domain, &hostlist );
+ if ( rc ) {
+ fprintf( stderr,
+ "DNS SRV: Could not turn "
+ "domain=%s into a hostlist\n",
+ domain );
+ goto dnssrv_free;
+ }
+
+ hosts = ldap_str2charray( hostlist, " " );
+ if ( hosts == NULL ) {
+ fprintf( stderr,
+ "DNS SRV: Could not parse "
+ "hostlist=\"%s\"\n",
+ hostlist );
+ goto dnssrv_free;
+ }
+
+ for ( i = 0; hosts[ i ] != NULL; i++ )
+ /* count'em */ ;
+
+ tmp = (char **)ber_memrealloc( urls, sizeof( char * ) * ( nurls + i + 1 ) );
+ if ( tmp == NULL ) {
+ fprintf( stderr,
+ "DNS SRV: out of memory?\n" );
+ goto dnssrv_free;
+ }
+ urls = tmp;
+ urls[ nurls ] = NULL;
+
+ for ( i = 0; hosts[ i ] != NULL; i++ ) {
+ size_t len = len_proto
+ + STRLENOF( "://" )
+ + strlen( hosts[ i ] )
+ + 1;
+
+ urls[ nurls + i + 1 ] = NULL;
+ urls[ nurls + i ] = (char *)malloc( sizeof( char ) * len );
+ if ( urls[ nurls + i ] == NULL ) {
+ fprintf( stderr,
+ "DNS SRV: out of memory?\n" );
+ goto dnssrv_free;
+ }
+
+ snprintf( urls[ nurls + i ], len, "%s://%s",
+ lud->lud_scheme, hosts[ i ] );
+ }
+ nurls += i;
+
+dnssrv_free:;
+ ber_memvfree( (void **)hosts );
+ ber_memfree( hostlist );
+ ber_memfree( domain );
+
+ } else {
+ tmp = (char **)ber_memrealloc( urls, sizeof( char * ) * ( nurls + 2 ) );
+ if ( tmp == NULL ) {
+ fprintf( stderr,
+ "DNS SRV: out of memory?\n" );
+ break;
+ }
+ urls = tmp;
+ urls[ nurls + 1 ] = NULL;
+
+ urls[ nurls ] = ldap_url_desc2str( lud );
+ if ( urls[ nurls ] == NULL ) {
+ fprintf( stderr,
+ "DNS SRV: out of memory?\n" );
+ break;
+ }
+ nurls++;
+ }
+
+ *ludp = lud->lud_next;
+
+ lud->lud_next = NULL;
+ ldap_free_urldesc( lud );
+ }
+
+ if ( ludlist != NULL ) {
+ ldap_free_urllist( ludlist );
+ exit( EXIT_FAILURE );
+
+ } else if ( urls == NULL ) {
+ exit( EXIT_FAILURE );
+ }
+
+ ldap_memfree( ldapuri );
+ ldapuri = ldap_charray2str( urls, " " );
+ ber_memvfree( (void **)urls );
+ }
+
+ if ( verbose ) {
+ fprintf( stderr, "ldap_initialize( %s )\n",
+ ldapuri != NULL ? ldapuri : "<DEFAULT>" );
+ }
+ rc = ldap_initialize( &ld, ldapuri );
+ if( rc != LDAP_SUCCESS ) {
+ fprintf( stderr,
+ "Could not create LDAP session handle for URI=%s (%d): %s\n",
+ ldapuri, rc, ldap_err2string(rc) );
+ exit( EXIT_FAILURE );
+ }
+
+ if( private_setup ) private_setup( ld );
+
+ /* referrals: obsolete */
+ if( ldap_set_option( ld, LDAP_OPT_REFERRALS,
+ referrals ? LDAP_OPT_ON : LDAP_OPT_OFF ) != LDAP_OPT_SUCCESS )
+ {
+ fprintf( stderr, "Could not set LDAP_OPT_REFERRALS %s\n",
+ referrals ? "on" : "off" );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+#ifdef HAVE_CYRUS_SASL
+ /* canon */
+ if( nocanon ) {
+ if( ldap_set_option( ld, LDAP_OPT_X_SASL_NOCANON,
+ LDAP_OPT_ON ) != LDAP_OPT_SUCCESS )
+ {
+ fprintf( stderr, "Could not set LDAP_OPT_X_SASL_NOCANON on\n" );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+ }
+#endif
+ if( ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &protocol )
+ != LDAP_OPT_SUCCESS )
+ {
+ fprintf( stderr, "Could not set LDAP_OPT_PROTOCOL_VERSION %d\n",
+ protocol );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if ( use_tls ) {
+ rc = ldap_start_tls_s( ld, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ char *msg=NULL;
+ ldap_get_option( ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*)&msg);
+ tool_perror( "ldap_start_tls", rc, NULL, NULL, msg, NULL );
+ ldap_memfree(msg);
+ if ( use_tls > 1 || rc < 0 ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+ }
+ }
+
+ if ( nettimeout.tv_sec > 0 ) {
+ if ( ldap_set_option( ld, LDAP_OPT_NETWORK_TIMEOUT, (void *) &nettimeout )
+ != LDAP_OPT_SUCCESS )
+ {
+ fprintf( stderr, "Could not set LDAP_OPT_NETWORK_TIMEOUT %ld\n",
+ (long)nettimeout.tv_sec );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+ }
+ }
+
+ return ld;
+}
+
+
+void
+tool_bind( LDAP *ld )
+{
+ LDAPControl **sctrlsp = NULL;
+ LDAPControl *sctrls[4];
+ LDAPControl sctrl[3];
+ int nsctrls = 0;
+
+ int rc, msgid;
+ LDAPMessage *result = NULL;
+
+ int err;
+ char *matched = NULL;
+ char *info = NULL;
+ char **refs = NULL;
+ LDAPControl **ctrls = NULL;
+ char msgbuf[256];
+
+ msgbuf[0] = 0;
+
+#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
+ if ( ppolicy ) {
+ LDAPControl c;
+ c.ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST;
+ c.ldctl_value.bv_val = NULL;
+ c.ldctl_value.bv_len = 0;
+ c.ldctl_iscritical = 0;
+ sctrl[nsctrls] = c;
+ sctrls[nsctrls] = &sctrl[nsctrls];
+ sctrls[++nsctrls] = NULL;
+ }
+#endif
+
+ if ( bauthzid ) {
+ LDAPControl c;
+
+ c.ldctl_oid = LDAP_CONTROL_AUTHZID_REQUEST;
+ c.ldctl_iscritical = bauthzid > 1;
+ BER_BVZERO( &c.ldctl_value );
+
+ sctrl[nsctrls] = c;
+ sctrls[nsctrls] = &sctrl[nsctrls];
+ sctrls[++nsctrls] = NULL;
+ }
+
+#ifdef LDAP_CONTROL_X_SESSION_TRACKING
+ if ( sessionTracking ) {
+ LDAPControl c;
+
+ if ( BER_BVISNULL( &stValue) && st_value( ld, &stValue ) ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ c.ldctl_oid = LDAP_CONTROL_X_SESSION_TRACKING;
+ c.ldctl_iscritical = 0;
+ c.ldctl_value = stValue;
+
+ sctrl[nsctrls] = c;
+ sctrls[nsctrls] = &sctrl[nsctrls];
+ sctrls[++nsctrls] = NULL;
+ }
+#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
+
+ if ( nsctrls ) {
+ sctrlsp = sctrls;
+ }
+
+ assert( nsctrls < (int) (sizeof(sctrls)/sizeof(sctrls[0])) );
+
+ if ( pw_file || want_bindpw ) {
+ assert( passwd.bv_val == NULL && passwd.bv_len == 0 );
+
+ if ( pw_file ) {
+ if ( lutil_get_filed_password( pw_file, &passwd ) ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ } else {
+ char *pw = getpassphrase( _("Enter LDAP Password: ") );
+ if ( pw ) {
+ passwd.bv_val = ber_strdup( pw );
+ passwd.bv_len = strlen( passwd.bv_val );
+ }
+ }
+ }
+
+ if ( authmethod == LDAP_AUTH_SASL ) {
+#ifdef HAVE_CYRUS_SASL
+ void *defaults;
+ const char *rmech = NULL;
+
+ if( sasl_secprops != NULL ) {
+ rc = ldap_set_option( ld, LDAP_OPT_X_SASL_SECPROPS,
+ (void *) sasl_secprops );
+
+ if( rc != LDAP_OPT_SUCCESS ) {
+ fprintf( stderr,
+ "Could not set LDAP_OPT_X_SASL_SECPROPS: %s\n",
+ sasl_secprops );
+ tool_exit( ld, LDAP_LOCAL_ERROR );
+ }
+ }
+
+ defaults = lutil_sasl_defaults( ld,
+ sasl_mech,
+ sasl_realm,
+ sasl_authc_id,
+ passwd.bv_val,
+ sasl_authz_id );
+
+ do {
+ rc = ldap_sasl_interactive_bind( ld, binddn, sasl_mech,
+ sctrlsp, NULL, sasl_flags, lutil_sasl_interact, defaults,
+ result, &rmech, &msgid );
+
+ if ( rc != LDAP_SASL_BIND_IN_PROGRESS )
+ break;
+
+ ldap_msgfree( result );
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1 || !result ) {
+ ldap_get_option( ld, LDAP_OPT_RESULT_CODE, (void*)&err );
+ ldap_get_option( ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*)&info );
+ tool_perror( "ldap_sasl_interactive_bind",
+ err, NULL, NULL, info, NULL );
+ ldap_memfree( info );
+ tool_exit( ld, err );
+ }
+ } while ( rc == LDAP_SASL_BIND_IN_PROGRESS );
+
+ lutil_sasl_freedefs( defaults );
+
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_get_option( ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*)&info );
+ tool_perror( "ldap_sasl_interactive_bind",
+ rc, NULL, NULL, info, NULL );
+ ldap_memfree( info );
+ tool_exit( ld, rc );
+ }
+#else
+ fprintf( stderr, "%s: not compiled with SASL support\n", prog );
+ tool_exit( ld, LDAP_NOT_SUPPORTED );
+#endif
+ } else {
+ /* simple bind */
+ rc = ldap_sasl_bind( ld, binddn, LDAP_SASL_SIMPLE, &passwd,
+ sctrlsp, NULL, &msgid );
+ if ( msgid == -1 ) {
+ tool_perror( "ldap_sasl_bind(SIMPLE)", rc,
+ NULL, NULL, NULL, NULL );
+ tool_exit( ld, rc );
+ }
+
+ rc = ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result );
+ if ( rc == -1 ) {
+ tool_perror( "ldap_result", -1, NULL, NULL, NULL, NULL );
+ tool_exit( ld, LDAP_LOCAL_ERROR );
+ }
+
+ if ( rc == 0 ) {
+ tool_perror( "ldap_result", LDAP_TIMEOUT, NULL, NULL, NULL, NULL );
+ tool_exit( ld, LDAP_LOCAL_ERROR );
+ }
+ }
+
+ if ( result ) {
+ rc = ldap_parse_result( ld, result, &err, &matched, &info, &refs,
+ &ctrls, 1 );
+ if ( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_bind parse result", rc, NULL, matched, info, refs );
+ tool_exit( ld, LDAP_LOCAL_ERROR );
+ }
+ }
+
+#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
+ if ( ctrls && ppolicy ) {
+ LDAPControl *ctrl;
+ int expire, grace, len = 0;
+ LDAPPasswordPolicyError pErr = -1;
+
+ ctrl = ldap_control_find( LDAP_CONTROL_PASSWORDPOLICYRESPONSE,
+ ctrls, NULL );
+
+ if ( ctrl && ldap_parse_passwordpolicy_control( ld, ctrl,
+ &expire, &grace, &pErr ) == LDAP_SUCCESS )
+ {
+ if ( pErr != PP_noError ){
+ msgbuf[0] = ';';
+ msgbuf[1] = ' ';
+ strcpy( msgbuf+2, ldap_passwordpolicy_err2txt( pErr ));
+ len = strlen( msgbuf );
+ }
+ if ( expire >= 0 ) {
+ sprintf( msgbuf+len,
+ " (Password expires in %d seconds)",
+ expire );
+ } else if ( grace >= 0 ) {
+ sprintf( msgbuf+len,
+ " (Password expired, %d grace logins remain)",
+ grace );
+ }
+ }
+ }
+#endif
+
+ if ( ctrls && bauthzid ) {
+ LDAPControl *ctrl;
+
+ ctrl = ldap_control_find( LDAP_CONTROL_AUTHZID_RESPONSE,
+ ctrls, NULL );
+ if ( ctrl ) {
+ LDAPControl *ctmp[2];
+ ctmp[0] = ctrl;
+ ctmp[1] = NULL;
+ tool_print_ctrls( ld, ctmp );
+ }
+ }
+
+#ifdef LDAP_CONTROL_X_PASSWORD_EXPIRED
+ if ( ctrls ) {
+ LDAPControl *ctrl;
+ ctrl = ldap_control_find( LDAP_CONTROL_X_PASSWORD_EXPIRED,
+ ctrls, NULL );
+ if ( !ctrl )
+ ctrl = ldap_control_find( LDAP_CONTROL_X_PASSWORD_EXPIRING,
+ ctrls, NULL );
+ if ( ctrl ) {
+ LDAPControl *ctmp[2];
+ ctmp[0] = ctrl;
+ ctmp[1] = NULL;
+ tool_print_ctrls( ld, ctmp );
+ }
+ }
+#endif
+
+ if ( ctrls ) {
+ ldap_controls_free( ctrls );
+ }
+
+ if ( err != LDAP_SUCCESS
+ || msgbuf[0]
+ || ( matched && matched[ 0 ] )
+ || ( info && info[ 0 ] )
+ || refs )
+ {
+ tool_perror( "ldap_bind", err, msgbuf, matched, info, refs );
+
+ if( matched ) ber_memfree( matched );
+ if( info ) ber_memfree( info );
+ if( refs ) ber_memvfree( (void **)refs );
+
+ if ( err != LDAP_SUCCESS ) tool_exit( ld, err );
+ }
+}
+
+void
+tool_unbind( LDAP *ld )
+{
+ int err = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, NULL );
+
+ if ( err != LDAP_OPT_SUCCESS ) {
+ fprintf( stderr, "Could not unset controls\n");
+ }
+
+ (void) ldap_unbind_ext( ld, NULL, NULL );
+}
+
+void
+tool_exit( LDAP *ld, int status )
+{
+ if ( ld != NULL ) {
+ tool_unbind( ld );
+ }
+ tool_destroy();
+ exit( status );
+}
+
+
+/* Set server controls. Add controls extra_c[0..count-1], if set. */
+void
+tool_server_controls( LDAP *ld, LDAPControl *extra_c, int count )
+{
+ int i = 0, j, crit = 0, err;
+ LDAPControl c[16], **ctrls;
+
+ if ( ! ( assertctl
+ || authzid
+#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
+ || proxydn
+#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
+ || manageDIT
+ || manageDSAit
+ || noop
+#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
+ || ppolicy
+#endif
+ || preread
+ || postread
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ || chaining
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+#ifdef LDAP_CONTROL_X_SESSION_TRACKING
+ || sessionTracking
+#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
+ || count
+ || unknown_ctrls_num ) )
+ {
+ return;
+ }
+
+ ctrls = (LDAPControl**) malloc(sizeof(c) + (count + unknown_ctrls_num + 1)*sizeof(LDAPControl*));
+ if ( ctrls == NULL ) {
+ fprintf( stderr, "No memory\n" );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if ( assertctl ) {
+ if ( BER_BVISNULL( &assertionvalue ) ) {
+ err = ldap_create_assertion_control_value( ld,
+ assertion, &assertionvalue );
+ if ( err ) {
+ fprintf( stderr,
+ "Unable to create assertion value "
+ "\"%s\" (%d)\n", assertion, err );
+ }
+ }
+
+ c[i].ldctl_oid = LDAP_CONTROL_ASSERT;
+ c[i].ldctl_value = assertionvalue;
+ c[i].ldctl_iscritical = assertctl > 1;
+ ctrls[i] = &c[i];
+ i++;
+ }
+
+ if ( authzid ) {
+ c[i].ldctl_value.bv_val = authzid;
+ c[i].ldctl_value.bv_len = strlen( authzid );
+ c[i].ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
+ c[i].ldctl_iscritical = authzcrit;
+ ctrls[i] = &c[i];
+ i++;
+ }
+
+#ifdef LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ
+ /* NOTE: doesn't need an extra count because it's incompatible
+ * with authzid */
+ if ( proxydn ) {
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+
+ if ( ber_printf( ber, "s", proxydn ) == -1 ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if ( ber_flatten2( ber, &c[i].ldctl_value, 0 ) == -1 ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ c[i].ldctl_oid = LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ;
+ c[i].ldctl_iscritical = authzcrit;
+ ctrls[i] = &c[i];
+ i++;
+ }
+#endif /* LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ */
+
+ if ( manageDIT ) {
+ c[i].ldctl_oid = LDAP_CONTROL_MANAGEDIT;
+ BER_BVZERO( &c[i].ldctl_value );
+ c[i].ldctl_iscritical = manageDIT > 1;
+ ctrls[i] = &c[i];
+ i++;
+ }
+
+ if ( manageDSAit ) {
+ c[i].ldctl_oid = LDAP_CONTROL_MANAGEDSAIT;
+ BER_BVZERO( &c[i].ldctl_value );
+ c[i].ldctl_iscritical = manageDSAit > 1;
+ ctrls[i] = &c[i];
+ i++;
+ }
+
+ if ( noop ) {
+ c[i].ldctl_oid = LDAP_CONTROL_NOOP;
+ BER_BVZERO( &c[i].ldctl_value );
+ c[i].ldctl_iscritical = noop > 1;
+ ctrls[i] = &c[i];
+ i++;
+ }
+
+#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
+ if ( ppolicy ) {
+ c[i].ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST;
+ BER_BVZERO( &c[i].ldctl_value );
+ c[i].ldctl_iscritical = 0;
+ ctrls[i] = &c[i];
+ i++;
+ }
+#endif
+
+ if ( preread ) {
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ char **attrs = NULL;
+
+ if( preread_attrs ) {
+ attrs = ldap_str2charray( preread_attrs, "," );
+ }
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+
+ if( ber_printf( ber, "{v}", attrs ) == -1 ) {
+ fprintf( stderr, "preread attrs encode failed.\n" );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ err = ber_flatten2( ber, &c[i].ldctl_value, 0 );
+ if( err < 0 ) {
+ fprintf( stderr, "preread flatten failed (%d)\n", err );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ c[i].ldctl_oid = LDAP_CONTROL_PRE_READ;
+ c[i].ldctl_iscritical = preread > 1;
+ ctrls[i] = &c[i];
+ i++;
+
+ if( attrs ) ldap_charray_free( attrs );
+ }
+
+ if ( postread ) {
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ char **attrs = NULL;
+
+ if( postread_attrs ) {
+ attrs = ldap_str2charray( postread_attrs, "," );
+ }
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+
+ if( ber_printf( ber, "{v}", attrs ) == -1 ) {
+ fprintf( stderr, "postread attrs encode failed.\n" );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ err = ber_flatten2( ber, &c[i].ldctl_value, 0 );
+ if( err < 0 ) {
+ fprintf( stderr, "postread flatten failed (%d)\n", err );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ c[i].ldctl_oid = LDAP_CONTROL_POST_READ;
+ c[i].ldctl_iscritical = postread > 1;
+ ctrls[i] = &c[i];
+ i++;
+
+ if( attrs ) ldap_charray_free( attrs );
+ }
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ if ( chaining ) {
+ if ( chainingResolve > -1 ) {
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+
+ err = ber_printf( ber, "{e" /* } */, chainingResolve );
+ if ( err == -1 ) {
+ ber_free( ber, 1 );
+ fprintf( stderr, _("Chaining behavior control encoding error!\n") );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if ( chainingContinuation > -1 ) {
+ err = ber_printf( ber, "e", chainingContinuation );
+ if ( err == -1 ) {
+ ber_free( ber, 1 );
+ fprintf( stderr, _("Chaining behavior control encoding error!\n") );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+ }
+
+ err = ber_printf( ber, /* { */ "N}" );
+ if ( err == -1 ) {
+ ber_free( ber, 1 );
+ fprintf( stderr, _("Chaining behavior control encoding error!\n") );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if ( ber_flatten2( ber, &c[i].ldctl_value, 0 ) == -1 ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ } else {
+ BER_BVZERO( &c[i].ldctl_value );
+ }
+
+ c[i].ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
+ c[i].ldctl_iscritical = chaining > 1;
+ ctrls[i] = &c[i];
+ i++;
+ }
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+#ifdef LDAP_CONTROL_X_SESSION_TRACKING
+ if ( sessionTracking ) {
+ if ( BER_BVISNULL( &stValue ) && st_value( ld, &stValue ) ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ c[i].ldctl_oid = LDAP_CONTROL_X_SESSION_TRACKING;
+ c[i].ldctl_iscritical = 0;
+ c[i].ldctl_value = stValue;
+
+ ctrls[i] = &c[i];
+ i++;
+ }
+#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
+
+ while ( count-- ) {
+ ctrls[i++] = extra_c++;
+ }
+ for ( count = 0; count < unknown_ctrls_num; count++ ) {
+ ctrls[i++] = &unknown_ctrls[count];
+ }
+ ctrls[i] = NULL;
+
+ err = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, ctrls );
+
+ if ( err != LDAP_OPT_SUCCESS ) {
+ for ( j = 0; j < i; j++ ) {
+ if ( ctrls[j]->ldctl_iscritical ) crit = 1;
+ }
+ fprintf( stderr, "Could not set %scontrols\n",
+ crit ? "critical " : "" );
+ }
+
+ free( ctrls );
+ if ( crit ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+}
+
+int
+tool_check_abandon( LDAP *ld, int msgid )
+{
+ int rc;
+ LDAPControl *sctrls[1] = { NULL };
+
+ switch ( gotintr ) {
+ case Intr_Cancel:
+ rc = ldap_cancel_s( ld, msgid, sctrls, NULL );
+ fprintf( stderr, "got interrupt, cancel got %d: %s\n",
+ rc, ldap_err2string( rc ) );
+ return -1;
+
+ case Intr_Abandon:
+ rc = ldap_abandon_ext( ld, msgid, sctrls, NULL );
+ fprintf( stderr, "got interrupt, abandon got %d: %s\n",
+ rc, ldap_err2string( rc ) );
+ return -1;
+
+ case Intr_Ignore:
+ /* just unbind, ignoring the request */
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+print_prepostread( LDAP *ld, LDAPControl *ctrl, struct berval *what)
+{
+ BerElement *ber;
+ struct berval bv;
+
+ tool_write_ldif( LDIF_PUT_COMMENT, "==> ",
+ what->bv_val, what->bv_len );
+ ber = ber_init( &ctrl->ldctl_value );
+ if ( ber == NULL ) {
+ /* error? */
+ return 1;
+
+ } else if ( ber_scanf( ber, "{m{" /*}}*/, &bv ) == LBER_ERROR ) {
+ /* error? */
+ return 1;
+
+ } else {
+ tool_write_ldif( LDIF_PUT_VALUE, "dn", bv.bv_val, bv.bv_len );
+
+ while ( ber_scanf( ber, "{m" /*}*/, &bv ) != LBER_ERROR ) {
+ int i;
+ BerVarray vals = NULL;
+ char *str = NULL;
+
+ if ( ber_scanf( ber, "[W]", &vals ) == LBER_ERROR ||
+ vals == NULL )
+ {
+ /* error? */
+ return 1;
+ }
+
+ if ( ldif ) {
+ char *ptr;
+
+ str = malloc( bv.bv_len + STRLENOF(": ") + 1 );
+
+ ptr = str;
+ ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
+ ptr = lutil_strcopy( ptr, ": " );
+ }
+
+ for ( i = 0; vals[ i ].bv_val != NULL; i++ ) {
+ tool_write_ldif(
+ ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
+ ldif ? str : bv.bv_val, vals[ i ].bv_val, vals[ i ].bv_len );
+ }
+
+ ber_bvarray_free( vals );
+ if ( str ) free( str );
+ }
+ }
+
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ tool_write_ldif( LDIF_PUT_COMMENT, "<== ",
+ what->bv_val, what->bv_len );
+
+ return 0;
+}
+
+static int
+print_preread( LDAP *ld, LDAPControl *ctrl )
+{
+ static struct berval what = BER_BVC( "preread" );
+
+ return print_prepostread( ld, ctrl, &what );
+}
+
+static int
+print_postread( LDAP *ld, LDAPControl *ctrl )
+{
+ static struct berval what = BER_BVC( "postread" );
+
+ return print_prepostread( ld, ctrl, &what );
+}
+
+static int
+print_paged_results( LDAP *ld, LDAPControl *ctrl )
+{
+ ber_int_t estimate;
+
+ /* note: pr_cookie is being malloced; it's freed
+ * the next time the control is sent, but the last
+ * time it's not; we don't care too much, because
+ * the last time an empty value is returned... */
+ if ( ldap_parse_pageresponse_control( ld, ctrl, &estimate, &pr_cookie )
+ != LDAP_SUCCESS )
+ {
+ /* error? */
+ return 1;
+
+ } else {
+ char buf[ BUFSIZ ], *ptr = buf;
+ int plen;
+
+ if ( estimate > 0 ) {
+ plen = sprintf( buf, "estimate=%d cookie=", estimate );
+ } else {
+ plen = sprintf( buf, "cookie=" );
+ }
+
+ if ( pr_cookie.bv_len > 0 ) {
+ struct berval bv;
+
+ bv.bv_len = LUTIL_BASE64_ENCODE_LEN(
+ pr_cookie.bv_len ) + 1;
+ ptr = ber_memalloc( bv.bv_len + 1 + plen );
+ bv.bv_val = ptr + plen;
+
+ strcpy( ptr, buf );
+
+ bv.bv_len = lutil_b64_ntop(
+ (unsigned char *) pr_cookie.bv_val,
+ pr_cookie.bv_len,
+ bv.bv_val, bv.bv_len );
+
+ pr_morePagedResults = 1;
+ plen += bv.bv_len;
+ }
+
+ tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
+ ldif ? "pagedresults: " : "pagedresults",
+ ptr, plen );
+
+ if ( ptr != buf )
+ ber_memfree( ptr );
+ }
+
+ return 0;
+}
+
+static int
+print_psearch( LDAP *ld, LDAPControl *ctrl )
+{
+ int rc;
+ int chgtype;
+ int chgpres;
+ long chgnum;
+ struct berval prevdn;
+
+ rc = ldap_parse_entrychange_control( ld, ctrl, &chgtype, &prevdn,
+ &chgpres, &chgnum );
+ if ( rc == LDAP_SUCCESS ) {
+ char buf[ BUFSIZ ];
+ char *ptr = buf;
+ int blen = sizeof(buf), len;
+
+ switch( chgtype ) {
+ case LDAP_CONTROL_PERSIST_ENTRY_CHANGE_ADD:
+ len = snprintf( ptr, blen, "add" );
+ ptr += len;
+ blen -= len;
+ break;
+ case LDAP_CONTROL_PERSIST_ENTRY_CHANGE_DELETE:
+ len = snprintf( ptr, blen, "delete" );
+ ptr += len;
+ blen -= len;
+ break;
+ case LDAP_CONTROL_PERSIST_ENTRY_CHANGE_MODIFY:
+ len = snprintf( ptr, blen, "modify" );
+ ptr += len;
+ blen -= len;
+ break;
+ case LDAP_CONTROL_PERSIST_ENTRY_CHANGE_RENAME:
+ len = snprintf( ptr, blen, "moddn" );
+ ptr += len;
+ blen -= len;
+ if ( prevdn.bv_val != NULL ) {
+ len = snprintf( ptr, blen, " prevdn %s", prevdn.bv_val );
+ ptr += len;
+ blen -= len;
+ }
+ break;
+ }
+ if ( chgpres ) {
+ len = snprintf( ptr, blen, " changeNumber %ld", chgnum) ;
+ ptr += len;
+ blen -= len;
+ }
+
+ tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
+ ldif ? "persistentSearch: " : "persistentSearch", buf, len );
+ }
+
+ return rc;
+}
+
+static int
+print_sss( LDAP *ld, LDAPControl *ctrl )
+{
+ int rc;
+ ber_int_t err;
+ char *attr;
+
+ rc = ldap_parse_sortresponse_control( ld, ctrl, &err, &attr );
+ if ( rc == LDAP_SUCCESS ) {
+ char buf[ BUFSIZ ];
+ rc = snprintf( buf, sizeof(buf), "(%d) %s%s%s",
+ err, ldap_err2string(err), attr ? " " : "", attr ? attr : "" );
+
+ tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
+ ldif ? "sortResult: " : "sortResult", buf, rc );
+ }
+
+ return rc;
+}
+
+static int
+print_vlv( LDAP *ld, LDAPControl *ctrl )
+{
+ int rc;
+ ber_int_t err;
+ struct berval bv;
+
+ rc = ldap_parse_vlvresponse_control( ld, ctrl, &vlvPos, &vlvCount,
+ &vlvContext, &err );
+ if ( rc == LDAP_SUCCESS ) {
+ char buf[ BUFSIZ ];
+
+ if ( vlvContext && vlvContext->bv_len > 0 ) {
+ bv.bv_len = LUTIL_BASE64_ENCODE_LEN(
+ vlvContext->bv_len ) + 1;
+ bv.bv_val = ber_memalloc( bv.bv_len + 1 );
+
+ bv.bv_len = lutil_b64_ntop(
+ (unsigned char *) vlvContext->bv_val,
+ vlvContext->bv_len,
+ bv.bv_val, bv.bv_len );
+ } else {
+ bv.bv_val = "";
+ bv.bv_len = 0;
+ }
+
+ rc = snprintf( buf, sizeof(buf), "pos=%d count=%d context=%s (%d) %s",
+ vlvPos, vlvCount, bv.bv_val,
+ err, ldap_err2string(err));
+
+ if ( bv.bv_len )
+ ber_memfree( bv.bv_val );
+
+ tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
+ ldif ? "vlvResult" : "vlvResult", buf, rc );
+ }
+
+ return rc;
+}
+
+#ifdef LDAP_CONTROL_X_DEREF
+static int
+print_deref( LDAP *ld, LDAPControl *ctrl )
+{
+ LDAPDerefRes *drhead = NULL, *dr;
+ int rc;
+
+ rc = ldap_parse_derefresponse_control( ld, ctrl, &drhead );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ for ( dr = drhead; dr != NULL; dr = dr->next ) {
+ LDAPDerefVal *dv;
+ ber_len_t len;
+ char *buf, *ptr;
+
+ len = strlen( dr->derefAttr ) + STRLENOF(": ");
+
+ for ( dv = dr->attrVals; dv != NULL; dv = dv->next ) {
+ if ( dv->vals != NULL ) {
+ int j;
+ ber_len_t tlen = strlen(dv->type);
+
+ for ( j = 0; dv->vals[ j ].bv_val != NULL; j++ ) {
+ len += STRLENOF("<:=>;") + tlen + 4*((dv->vals[ j ].bv_len - 1)/3 + 1);
+ }
+ }
+ }
+ len += dr->derefVal.bv_len + STRLENOF("\n");
+ buf = ldap_memalloc( len + 1 );
+ if ( buf == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto done;
+ }
+
+ ptr = buf;
+ ptr = lutil_strcopy( ptr, dr->derefAttr );
+ *ptr++ = ':';
+ *ptr++ = ' ';
+ for ( dv = dr->attrVals; dv != NULL; dv = dv->next ) {
+ if ( dv->vals != NULL ) {
+ int j;
+ for ( j = 0; dv->vals[ j ].bv_val != NULL; j++ ) {
+ int k = ldif_is_not_printable( dv->vals[ j ].bv_val, dv->vals[ j ].bv_len );
+
+ *ptr++ = '<';
+ ptr = lutil_strcopy( ptr, dv->type );
+ if ( k ) {
+ *ptr++ = ':';
+ }
+ *ptr++ = '=';
+ if ( k ) {
+ k = lutil_b64_ntop(
+ (unsigned char *) dv->vals[ j ].bv_val,
+ dv->vals[ j ].bv_len,
+ ptr, buf + len - ptr );
+ assert( k >= 0 );
+ ptr += k;
+
+ } else {
+ ptr = lutil_memcopy( ptr, dv->vals[ j ].bv_val, dv->vals[ j ].bv_len );
+ }
+ *ptr++ = '>';
+ *ptr++ = ';';
+ }
+ }
+ }
+ ptr = lutil_strncopy( ptr, dr->derefVal.bv_val, dr->derefVal.bv_len );
+ *ptr = '\0';
+ assert( ptr <= buf + len );
+
+ tool_write_ldif( LDIF_PUT_COMMENT, NULL, buf, ptr - buf);
+
+ ldap_memfree( buf );
+ }
+
+ rc = LDAP_SUCCESS;
+
+done:;
+ ldap_derefresponse_free( drhead );
+
+ return rc;
+}
+#endif
+
+#ifdef LDAP_CONTROL_X_WHATFAILED
+static int
+print_whatfailed( LDAP *ld, LDAPControl *ctrl )
+{
+ BerElement *ber;
+ ber_tag_t tag;
+ ber_len_t siz;
+ BerVarray bva = NULL;
+
+ /* Create a BerElement from the berval returned in the control. */
+ ber = ber_init( &ctrl->ldctl_value );
+
+ if ( ber == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+
+ siz = sizeof(struct berval);
+ tag = ber_scanf( ber, "[M]", &bva, &siz, 0 );
+ if ( tag != LBER_ERROR ) {
+ int i;
+
+ tool_write_ldif( LDIF_PUT_COMMENT, " what failed:", NULL, 0 );
+
+ for ( i = 0; bva[i].bv_val != NULL; i++ ) {
+ tool_write_ldif( LDIF_PUT_COMMENT, NULL, bva[i].bv_val, bva[i].bv_len );
+ }
+
+ ldap_memfree( bva );
+ }
+
+ ber_free( ber, 1 );
+
+
+ return 0;
+}
+#endif
+
+static int
+print_syncstate( LDAP *ld, LDAPControl *ctrl )
+{
+ struct berval syncUUID, syncCookie = BER_BVNULL;
+ char buf[LDAP_LUTIL_UUIDSTR_BUFSIZE], *uuidstr = "(UUID malformed)";
+ BerElement *ber;
+ ber_tag_t tag;
+ ber_int_t state;
+ int rc;
+
+ if ( ldif ) {
+ return 0;
+ }
+
+ /* Create a BerElement from the berval returned in the control. */
+ ber = ber_init( &ctrl->ldctl_value );
+
+ if ( ber == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+
+ if ( ber_scanf( ber, "{em", &state, &syncUUID ) == LBER_ERROR ) {
+ ber_free( ber, 1 );
+ return 1;
+ }
+
+ tag = ber_get_stringbv( ber, &syncCookie, 0 );
+
+ rc = lutil_uuidstr_from_normalized(
+ syncUUID.bv_val, syncUUID.bv_len,
+ buf, LDAP_LUTIL_UUIDSTR_BUFSIZE );
+
+ if ( rc > 0 && rc < LDAP_LUTIL_UUIDSTR_BUFSIZE ) {
+ uuidstr = buf;
+ }
+
+ switch ( state ) {
+ case LDAP_SYNC_PRESENT:
+ printf(_("# SyncState control, UUID %s present\n"), uuidstr);
+ break;
+ case LDAP_SYNC_ADD:
+ printf(_("# SyncState control, UUID %s added\n"), uuidstr);
+ break;
+ case LDAP_SYNC_MODIFY:
+ printf(_("# SyncState control, UUID %s modified\n"), uuidstr);
+ break;
+ case LDAP_SYNC_DELETE:
+ printf(_("# SyncState control, UUID %s deleted\n"), uuidstr);
+ break;
+ default:
+ ber_free( ber, 1 );
+ return 1;
+ }
+
+ if ( tag != LBER_ERROR ) {
+ if ( ldif_is_not_printable( syncCookie.bv_val, syncCookie.bv_len ) ) {
+ struct berval bv;
+
+ bv.bv_len = LUTIL_BASE64_ENCODE_LEN( syncCookie.bv_len ) + 1;
+ bv.bv_val = ber_memalloc( bv.bv_len + 1 );
+
+ bv.bv_len = lutil_b64_ntop(
+ (unsigned char *) syncCookie.bv_val, syncCookie.bv_len,
+ bv.bv_val, bv.bv_len );
+
+ printf(_("# cookie:: %s\n"), bv.bv_val );
+ ber_memfree( bv.bv_val );
+ } else {
+ printf(_("# cookie: %s\n"), syncCookie.bv_val );
+ }
+ }
+
+ ber_free( ber, 1 );
+ return 0;
+}
+
+static int
+print_syncdone( LDAP *ld, LDAPControl *ctrl )
+{
+ BerElement *ber;
+ struct berval cookie = BER_BVNULL;
+ ber_len_t len;
+ ber_int_t refreshDeletes = 0;
+
+ if ( ldif ) {
+ return 0;
+ }
+
+ /* Create a BerElement from the berval returned in the control. */
+ ber = ber_init( &ctrl->ldctl_value );
+
+ if ( ber == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+
+ ber_skip_tag( ber, &len );
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
+ ber_scanf( ber, "m", &cookie );
+ }
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES ) {
+ ber_scanf( ber, "b", &refreshDeletes );
+ }
+
+ printf(_("# SyncDone control refreshDeletes=%d\n"), refreshDeletes ? 1 : 0 );
+
+ if ( !BER_BVISNULL( &cookie ) ) {
+ if ( ldif_is_not_printable( cookie.bv_val, cookie.bv_len ) ) {
+ struct berval bv;
+
+ bv.bv_len = LUTIL_BASE64_ENCODE_LEN( cookie.bv_len ) + 1;
+ bv.bv_val = ber_memalloc( bv.bv_len + 1 );
+
+ bv.bv_len = lutil_b64_ntop(
+ (unsigned char *) cookie.bv_val, cookie.bv_len,
+ bv.bv_val, bv.bv_len );
+
+ printf(_("# cookie:: %s\n"), bv.bv_val );
+ ber_memfree( bv.bv_val );
+ } else {
+ printf(_("# cookie: %s\n"), cookie.bv_val );
+ }
+ }
+
+ ber_free( ber, 1 );
+ return 0;
+}
+
+#ifdef LDAP_CONTROL_X_DIRSYNC
+static int
+print_dirsync( LDAP *ld, LDAPControl *ctrl )
+{
+ int rc, continueFlag;
+ struct berval cookie;
+
+ rc = ldap_parse_dirsync_control( ld, ctrl,
+ &continueFlag, &cookie );
+ if ( rc == LDAP_SUCCESS ) {
+ printf(_("# DirSync control continueFlag=%d\n"), continueFlag );
+ if ( !BER_BVISNULL( &cookie )) {
+ if ( ldif_is_not_printable( cookie.bv_val, cookie.bv_len ) ) {
+ struct berval bv;
+
+ bv.bv_len = LUTIL_BASE64_ENCODE_LEN( cookie.bv_len ) + 1;
+ bv.bv_val = ber_memalloc( bv.bv_len + 1 );
+
+ bv.bv_len = lutil_b64_ntop(
+ (unsigned char *) cookie.bv_val, cookie.bv_len,
+ bv.bv_val, bv.bv_len );
+
+ printf(_("# cookie:: %s\n"), bv.bv_val );
+ ber_memfree( bv.bv_val );
+ } else {
+ printf(_("# cookie: %s\n"), cookie.bv_val );
+ }
+ }
+ }
+ return rc;
+}
+#endif
+
+#ifdef LDAP_CONTROL_AUTHZID_RESPONSE
+static int
+print_authzid( LDAP *ld, LDAPControl *ctrl )
+{
+ if ( ctrl->ldctl_value.bv_len ) {
+ tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
+ ldif ? "authzid: " : "authzid",
+ ctrl->ldctl_value.bv_val, ctrl->ldctl_value.bv_len );
+ } else {
+ tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
+ ldif ? "authzid: " : "authzid",
+ "anonymous", STRLENOF("anonymous") );
+ }
+
+ return 0;
+}
+#endif
+
+#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
+static int
+print_ppolicy( LDAP *ld, LDAPControl *ctrl )
+{
+ int expire = 0, grace = 0, rc;
+ LDAPPasswordPolicyError pperr;
+
+ rc = ldap_parse_passwordpolicy_control( ld, ctrl,
+ &expire, &grace, &pperr );
+ if ( rc == LDAP_SUCCESS ) {
+ char buf[ BUFSIZ ], *ptr = buf;
+
+ if ( expire != -1 ) {
+ ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
+ "expire=%d", expire );
+ }
+
+ if ( grace != -1 ) {
+ ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
+ "%sgrace=%d", ptr == buf ? "" : " ", grace );
+ }
+
+ if ( pperr != PP_noError ) {
+ ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
+ "%serror=%d (%s)", ptr == buf ? "" : " ",
+ pperr,
+ ldap_passwordpolicy_err2txt( pperr ) );
+ }
+
+ tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
+ ldif ? "ppolicy: " : "ppolicy", buf, ptr - buf );
+ }
+
+ return rc;
+}
+#endif
+
+#ifdef LDAP_CONTROL_X_PASSWORD_EXPIRED
+static int
+print_netscape_pwexpired( LDAP *ld, LDAPControl *ctrl )
+{
+ printf(_("# PasswordExpired control\n") );
+ return 0;
+}
+
+static int
+print_netscape_pwexpiring( LDAP *ld, LDAPControl *ctrl )
+{
+ long expiring = 0;
+ int rc;
+
+ rc = ldap_parse_password_expiring_control( ld, ctrl, &expiring );
+ if ( rc == LDAP_SUCCESS ) {
+ printf(_("# PasswordExpiring control seconds=%ld\n"), expiring );
+ }
+ return rc;
+}
+#endif
+
+#ifdef LDAP_CONTROL_X_ACCOUNT_USABILITY
+static int
+print_account_usability( LDAP *ld, LDAPControl *ctrl )
+{
+ LDAPAccountUsability usability;
+ ber_int_t available = 0;
+ int rc;
+
+ rc = ldap_parse_accountusability_control( ld, ctrl, &available, &usability );
+ if ( rc == LDAP_SUCCESS ) {
+ char buf[ BUFSIZ ], *ptr = buf;
+
+ ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
+ "%savailable", available ? "" : "not " );
+ if ( available ) {
+ if ( usability.seconds_remaining == -1 ) {
+ ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
+ " and does not expire" );
+ } else {
+ ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
+ " expire=%d", usability.seconds_remaining );
+ }
+ } else {
+ int added = 0;
+ ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
+ " (" /* ')' */ );
+
+ if ( usability.more_info.inactive ) {
+ ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
+ "inactive " );
+ added++;
+ }
+ if ( usability.more_info.reset ) {
+ ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
+ "reset " );
+ added++;
+ }
+ if ( usability.more_info.expired ) {
+ ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
+ "expired " );
+ added++;
+ }
+
+ if ( added ) {
+ ptr[-1] = ')';
+ *ptr++ = ' ';
+ } else {
+ *(--ptr) = '\0';
+ }
+
+ if ( usability.more_info.remaining_grace != -1 ) {
+ ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
+ "grace=%d ", usability.more_info.remaining_grace );
+ }
+
+ if ( usability.more_info.seconds_before_unlock != -1 ) {
+ ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ),
+ "seconds_before_unlock=%d ", usability.more_info.seconds_before_unlock );
+ }
+
+ *(--ptr) = '\0';
+ }
+
+ tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
+ ldif ? "accountUsability: " : "accountUsability", buf, ptr - buf );
+ }
+
+ return rc;
+}
+#endif
+
+void tool_print_ctrls(
+ LDAP *ld,
+ LDAPControl **ctrls )
+{
+ int i;
+ char *ptr;
+
+ for ( i = 0; ctrls[i] != NULL; i++ ) {
+ /* control: OID criticality base64value */
+ struct berval b64 = BER_BVNULL;
+ ber_len_t len;
+ char *str;
+ int j;
+
+ /* FIXME: there might be cases where a control has NULL OID;
+ * this makes little sense, especially when returned by the
+ * server, but libldap happily allows it */
+ if ( ctrls[i]->ldctl_oid == NULL ) {
+ continue;
+ }
+
+ len = ldif ? 2 : 0;
+ len += strlen( ctrls[i]->ldctl_oid );
+
+ /* add enough for space after OID and the critical value itself */
+ len += ctrls[i]->ldctl_iscritical
+ ? sizeof("true") : sizeof("false");
+
+ /* convert to base64 */
+ if ( !BER_BVISNULL( &ctrls[i]->ldctl_value ) ) {
+ b64.bv_len = LUTIL_BASE64_ENCODE_LEN(
+ ctrls[i]->ldctl_value.bv_len ) + 1;
+ b64.bv_val = ber_memalloc( b64.bv_len + 1 );
+
+ b64.bv_len = lutil_b64_ntop(
+ (unsigned char *) ctrls[i]->ldctl_value.bv_val,
+ ctrls[i]->ldctl_value.bv_len,
+ b64.bv_val, b64.bv_len );
+ }
+
+ if ( b64.bv_len ) {
+ len += 1 + b64.bv_len;
+ }
+
+ ptr = str = malloc( len + 1 );
+ if ( ldif ) {
+ ptr = lutil_strcopy( ptr, ": " );
+ }
+ ptr = lutil_strcopy( ptr, ctrls[i]->ldctl_oid );
+ ptr = lutil_strcopy( ptr, ctrls[i]->ldctl_iscritical
+ ? " true" : " false" );
+
+ if ( b64.bv_len ) {
+ ptr = lutil_strcopy( ptr, " " );
+ ptr = lutil_strcopy( ptr, b64.bv_val );
+ }
+
+ if ( ldif < 2 ) {
+ tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
+ "control", str, len );
+ }
+
+ free( str );
+ if ( b64.bv_len ) {
+ ber_memfree( b64.bv_val );
+ }
+
+ /* known controls */
+ for ( j = 0; tool_ctrl_response[j].oid != NULL; j++ ) {
+ if ( strcmp( tool_ctrl_response[j].oid, ctrls[i]->ldctl_oid ) == 0 ) {
+ if ( !(tool_ctrl_response[j].mask & tool_type )) {
+ /* this control should not appear
+ * with this tool; warning? */
+ }
+ break;
+ }
+ }
+
+ if ( tool_ctrl_response[j].oid != NULL && tool_ctrl_response[j].func ) {
+ (void)tool_ctrl_response[j].func( ld, ctrls[i] );
+ }
+ }
+}
+
+int
+tool_write_ldif( int type, char *name, char *value, ber_len_t vallen )
+{
+ char *ldif;
+
+ if (( ldif = ldif_put_wrap( type, name, value, vallen, ldif_wrap )) == NULL ) {
+ return( -1 );
+ }
+
+ fputs( ldif, stdout );
+ ber_memfree( ldif );
+
+ return( 0 );
+}
+
+int
+tool_is_oid( const char *s )
+{
+ int first = 1;
+
+ if ( !isdigit( (unsigned char) s[ 0 ] ) ) {
+ return 0;
+ }
+
+ for ( ; s[ 0 ]; s++ ) {
+ if ( s[ 0 ] == '.' ) {
+ if ( s[ 1 ] == '\0' ) {
+ return 0;
+ }
+ first = 1;
+ continue;
+ }
+
+ if ( !isdigit( (unsigned char) s[ 0 ] ) ) {
+ return 0;
+ }
+
+ if ( first == 1 && s[ 0 ] == '0' && s[ 1 ] != '.' ) {
+ return 0;
+ }
+ first = 0;
+ }
+
+ return 1;
+}
diff --git a/clients/tools/common.h b/clients/tools/common.h
new file mode 100644
index 0000000..c4377da
--- /dev/null
+++ b/clients/tools/common.h
@@ -0,0 +1,140 @@
+/* common.h - common definitions for the ldap client tools */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This file was initially created by Hallvard B. Furuseth based (in
+ * part) upon argument parsing code for individual tools located in
+ * this directory.
+ */
+
+#ifndef _CLIENT_TOOLS_COMMON_H_
+#define _CLIENT_TOOLS_COMMON_H_
+
+LDAP_BEGIN_DECL
+
+typedef enum tool_type_t {
+ TOOL_SEARCH = 0x01U,
+ TOOL_COMPARE = 0x02U,
+ TOOL_ADD = 0x04U,
+ TOOL_DELETE = 0x08U,
+ TOOL_MODIFY = 0x10U,
+ TOOL_MODRDN = 0x20U,
+
+ TOOL_EXOP = 0x40U,
+
+ TOOL_WHOAMI = TOOL_EXOP | 0x100U,
+ TOOL_PASSWD = TOOL_EXOP | 0x200U,
+ TOOL_VC = TOOL_EXOP | 0x400U,
+
+ TOOL_WRITE = (TOOL_ADD|TOOL_DELETE|TOOL_MODIFY|TOOL_MODRDN),
+ TOOL_READ = (TOOL_SEARCH|TOOL_COMPARE),
+
+ TOOL_ALL = 0xFFU
+} tool_type_t;
+
+
+/* input-related vars */
+
+/* misc. parameters */
+extern tool_type_t tool_type;
+extern int contoper;
+extern int debug;
+extern char *infile;
+extern int dont;
+extern int referrals;
+extern int verbose;
+extern int ldif;
+extern ber_len_t ldif_wrap;
+extern char *prog;
+
+/* connection */
+extern char *ldapuri;
+extern int use_tls;
+extern int protocol;
+extern int version;
+
+/* authc/authz */
+extern int authmethod;
+extern char *binddn;
+extern int want_bindpw;
+extern struct berval passwd;
+extern char *pw_file;
+#ifdef HAVE_CYRUS_SASL
+extern unsigned sasl_flags;
+extern char *sasl_realm;
+extern char *sasl_authc_id;
+extern char *sasl_authz_id;
+extern char *sasl_mech;
+extern char *sasl_secprops;
+#endif
+
+/* controls */
+extern char *assertion;
+extern char *authzid;
+extern int manageDIT;
+extern int manageDSAit;
+extern int noop;
+extern int ppolicy;
+extern int preread, postread;
+extern ber_int_t pr_morePagedResults;
+extern struct berval pr_cookie;
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+extern int chaining;
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+extern ber_int_t vlvPos;
+extern ber_int_t vlvCount;
+extern struct berval *vlvContext;
+
+/* features */
+extern int backlog;
+
+/* options */
+extern struct timeval nettimeout;
+
+/* Defined in common.c, set in main() */
+extern const char __Version[];
+
+/* Defined in main program */
+extern const char options[];
+
+void usage LDAP_P(( void )) LDAP_GCCATTR((noreturn));
+int handle_private_option LDAP_P(( int i ));
+
+/* Defined in common.c */
+void tool_init LDAP_P(( tool_type_t type ));
+void tool_common_usage LDAP_P(( void ));
+void tool_args LDAP_P(( int, char ** ));
+LDAP *tool_conn_setup LDAP_P(( int dont, void (*private_setup)( LDAP * ) ));
+void tool_bind LDAP_P(( LDAP * ));
+void tool_unbind LDAP_P(( LDAP * ));
+void tool_destroy LDAP_P(( void ));
+void tool_exit LDAP_P(( LDAP *ld, int status )) LDAP_GCCATTR((noreturn));
+void tool_server_controls LDAP_P(( LDAP *, LDAPControl *, int ));
+int tool_check_abandon LDAP_P(( LDAP *ld, int msgid ));
+void tool_perror LDAP_P((
+ const char *func,
+ int err,
+ const char *extra,
+ const char *matched,
+ const char *info,
+ char **refs ));
+void tool_print_ctrls LDAP_P(( LDAP *ld, LDAPControl **ctrls ));
+int tool_write_ldif LDAP_P(( int type, char *name, char *value, ber_len_t vallen ));
+int tool_is_oid LDAP_P(( const char *s ));
+
+
+LDAP_END_DECL
+
+#endif /* _CLIENT_TOOLS_COMMON_H_ */
diff --git a/clients/tools/ldapcompare.c b/clients/tools/ldapcompare.c
new file mode 100644
index 0000000..a83c8d4
--- /dev/null
+++ b/clients/tools/ldapcompare.c
@@ -0,0 +1,366 @@
+/* ldapcompare.c -- LDAP compare tool */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1998-2003 Kurt D. Zeilenga.
+ * Portions Copyright 1998-2001 Net Boolean Incorporated.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission. This
+ * software is provided ``as is'' without express or implied warranty.
+ */
+/* Portions Copyright 2002, F5 Networks, Inc, All rights reserved.
+ * This software is not subject to any license of F5 Networks.
+ * This is free software; you can redistribute and use it
+ * under the same terms as OpenLDAP itself.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by Jeff Costlow (F5 Networks)
+ * based, in part, on existing LDAP tools and adapted for inclusion
+ * into OpenLDAP Software by Kurt D. Zeilenga.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/time.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+
+#include <ldap.h>
+
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "ldap_defaults.h"
+
+#include "common.h"
+
+
+static int quiet = 0;
+
+
+void
+usage( void )
+{
+ fprintf( stderr, _("usage: %s [options] DN <attr:value|attr::b64value>\n"), prog);
+ fprintf( stderr, _("where:\n"));
+ fprintf( stderr, _(" DN\tDistinguished Name\n"));
+ fprintf( stderr, _(" attr\tassertion attribute\n"));
+ fprintf( stderr, _(" value\tassertion value\n"));
+ fprintf( stderr, _(" b64value\tbase64 encoding of assertion value\n"));
+
+ fprintf( stderr, _("Compare options:\n"));
+ fprintf( stderr, _(" -E [!]<ext>[=<extparam>] compare extensions (! indicates criticality)\n"));
+ fprintf( stderr, _(" !dontUseCopy (Don't Use Copy)\n"));
+ fprintf( stderr, _(" -M enable Manage DSA IT control (-MM to make critical)\n"));
+ fprintf( stderr, _(" -P version protocol version (default: 3)\n"));
+ fprintf( stderr, _(" -z Quiet mode,"
+ " don't print anything, use return values\n"));
+ tool_common_usage();
+ exit( EXIT_FAILURE );
+}
+
+static int docompare LDAP_P((
+ LDAP *ld,
+ char *dn,
+ char *attr,
+ struct berval *bvalue,
+ int quiet,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls));
+
+
+const char options[] = "z"
+ "Cd:D:e:h:H:IMnNO:o:p:P:QR:U:vVw:WxX:y:Y:Z";
+
+#ifdef LDAP_CONTROL_DONTUSECOPY
+int dontUseCopy = 0;
+#endif
+
+int
+handle_private_option( int i )
+{
+ char *control, *cvalue;
+ int crit;
+
+ switch ( i ) {
+ case 'E': /* compare extensions */
+ if( protocol == LDAP_VERSION2 ) {
+ fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"),
+ prog, protocol );
+ exit( EXIT_FAILURE );
+ }
+
+ /* should be extended to support comma separated list of
+ * [!]key[=value] parameters, e.g. -E !foo,bar=567
+ */
+
+ crit = 0;
+ cvalue = NULL;
+ if( optarg[0] == '!' ) {
+ crit = 1;
+ optarg++;
+ }
+
+ control = optarg;
+ if ( (cvalue = strchr( control, '=' )) != NULL ) {
+ *cvalue++ = '\0';
+ }
+
+#ifdef LDAP_CONTROL_DONTUSECOPY
+ if ( strcasecmp( control, "dontUseCopy" ) == 0 ) {
+ if( dontUseCopy ) {
+ fprintf( stderr,
+ _("dontUseCopy control previously specified\n"));
+ exit( EXIT_FAILURE );
+ }
+ if( cvalue != NULL ) {
+ fprintf( stderr,
+ _("dontUseCopy: no control value expected\n") );
+ usage();
+ }
+ if( !crit ) {
+ fprintf( stderr,
+ _("dontUseCopy: critical flag required\n") );
+ usage();
+ }
+
+ dontUseCopy = 1 + crit;
+ } else
+#endif
+ {
+ fprintf( stderr,
+ _("Invalid compare extension name: %s\n"), control );
+ usage();
+ }
+ break;
+
+ case 'z':
+ quiet = 1;
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+
+int
+main( int argc, char **argv )
+{
+ char *compdn = NULL, *attrs = NULL;
+ char *sep;
+ int rc;
+ LDAP *ld = NULL;
+ struct berval bvalue = { 0, NULL };
+ int i = 0;
+ LDAPControl c[1];
+
+
+ tool_init( TOOL_COMPARE );
+ prog = lutil_progname( "ldapcompare", argc, argv );
+
+ tool_args( argc, argv );
+
+ if ( argc - optind != 2 ) {
+ usage();
+ }
+
+ compdn = argv[optind++];
+ attrs = argv[optind++];
+
+ /* user passed in only 2 args, the last one better be in
+ * the form attr:value or attr::b64value
+ */
+ sep = strchr(attrs, ':');
+ if (!sep) {
+ usage();
+ }
+
+ *sep++='\0';
+ if ( *sep != ':' ) {
+ bvalue.bv_val = strdup( sep );
+ bvalue.bv_len = strlen( bvalue.bv_val );
+
+ } else {
+ /* it's base64 encoded. */
+ bvalue.bv_val = malloc( strlen( &sep[1] ));
+ bvalue.bv_len = lutil_b64_pton( &sep[1],
+ (unsigned char *) bvalue.bv_val, strlen( &sep[1] ));
+
+ if (bvalue.bv_len == (ber_len_t)-1) {
+ fprintf(stderr, _("base64 decode error\n"));
+ exit(-1);
+ }
+ }
+
+ ld = tool_conn_setup( 0, 0 );
+
+ tool_bind( ld );
+
+ if ( 0
+#ifdef LDAP_CONTROL_DONTUSECOPY
+ || dontUseCopy
+#endif
+ )
+ {
+#ifdef LDAP_CONTROL_DONTUSECOPY
+ if ( dontUseCopy ) {
+ c[i].ldctl_oid = LDAP_CONTROL_DONTUSECOPY;
+ c[i].ldctl_value.bv_val = NULL;
+ c[i].ldctl_value.bv_len = 0;
+ c[i].ldctl_iscritical = dontUseCopy > 1;
+ i++;
+ }
+#endif
+ }
+
+ tool_server_controls( ld, c, i );
+
+ if ( verbose ) {
+ fprintf( stderr, _("DN:%s, attr:%s, value:%s\n"),
+ compdn, attrs, sep );
+ }
+
+ rc = docompare( ld, compdn, attrs, &bvalue, quiet, NULL, NULL );
+
+ free( bvalue.bv_val );
+
+ tool_exit( ld, rc );
+}
+
+
+static int docompare(
+ LDAP *ld,
+ char *dn,
+ char *attr,
+ struct berval *bvalue,
+ int quiet,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ int rc, msgid, code;
+ LDAPMessage *res;
+ char *matcheddn;
+ char *text;
+ char **refs;
+ LDAPControl **ctrls = NULL;
+
+ if ( dont ) {
+ return LDAP_SUCCESS;
+ }
+
+ rc = ldap_compare_ext( ld, dn, attr, bvalue,
+ sctrls, cctrls, &msgid );
+ if ( rc == -1 ) {
+ return( rc );
+ }
+
+ for ( ; ; ) {
+ struct timeval tv;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+
+ if ( tool_check_abandon( ld, msgid ) ) {
+ return LDAP_CANCELLED;
+ }
+
+ rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res );
+ if ( rc < 0 ) {
+ tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL );
+ return rc;
+ }
+
+ if ( rc != 0 ) {
+ break;
+ }
+ }
+
+ rc = ldap_parse_result( ld, res, &code, &matcheddn, &text, &refs, &ctrls, 1 );
+
+ if( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s: ldap_parse_result: %s (%d)\n",
+ prog, ldap_err2string( rc ), rc );
+ return rc;
+ }
+
+ if ( !quiet && ( verbose || ( code != LDAP_SUCCESS && code != LDAP_COMPARE_TRUE && code != LDAP_COMPARE_FALSE )||
+ (matcheddn && *matcheddn) || (text && *text) || (refs && *refs) ) )
+ {
+ printf( _("Compare Result: %s (%d)\n"),
+ ldap_err2string( code ), code );
+
+ if( text && *text ) {
+ printf( _("Additional info: %s\n"), text );
+ }
+
+ if( matcheddn && *matcheddn ) {
+ printf( _("Matched DN: %s\n"), matcheddn );
+ }
+
+ if( refs ) {
+ int i;
+ for( i=0; refs[i]; i++ ) {
+ printf(_("Referral: %s\n"), refs[i] );
+ }
+ }
+ }
+
+ /* if we were told to be quiet, use the return value. */
+ if ( !quiet ) {
+ if ( code == LDAP_COMPARE_TRUE ) {
+ printf(_("TRUE\n"));
+ } else if ( code == LDAP_COMPARE_FALSE ) {
+ printf(_("FALSE\n"));
+ } else {
+ printf(_("UNDEFINED\n"));
+ }
+ }
+
+ if ( ctrls ) {
+ tool_print_ctrls( ld, ctrls );
+ ldap_controls_free( ctrls );
+ }
+
+ ber_memfree( text );
+ ber_memfree( matcheddn );
+ ber_memvfree( (void **) refs );
+
+ return( code );
+}
+
diff --git a/clients/tools/ldapdelete.c b/clients/tools/ldapdelete.c
new file mode 100644
index 0000000..dbc85b0
--- /dev/null
+++ b/clients/tools/ldapdelete.c
@@ -0,0 +1,443 @@
+/* ldapdelete.c - simple program to delete an entry using LDAP */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1998-2003 Kurt D. Zeilenga.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission. This
+ * software is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by the University of Michigan
+ * (as part of U-MICH LDAP). Additional significant contributors
+ * include:
+ * Kurt D. Zeilenga
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+#include <ac/socket.h>
+#include <ac/time.h>
+
+#include <ldap.h>
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "ldap_defaults.h"
+
+#include "common.h"
+
+
+static int prune = 0;
+static int sizelimit = -1;
+
+
+static int dodelete LDAP_P((
+ LDAP *ld,
+ const char *dn));
+
+static int deletechildren LDAP_P((
+ LDAP *ld,
+ const char *dn,
+ int subentries ));
+
+void
+usage( void )
+{
+ fprintf( stderr, _("Delete entries from an LDAP server\n\n"));
+ fprintf( stderr, _("usage: %s [options] [dn]...\n"), prog);
+ fprintf( stderr, _(" dn: list of DNs to delete. If not given, it will be read from stdin\n"));
+ fprintf( stderr, _(" or from the file specified with \"-f file\".\n"));
+ fprintf( stderr, _("Delete Options:\n"));
+ fprintf( stderr, _(" -c continuous operation mode (do not stop on errors)\n"));
+ fprintf( stderr, _(" -f file read operations from `file'\n"));
+ fprintf( stderr, _(" -M enable Manage DSA IT control (-MM to make critical)\n"));
+ fprintf( stderr, _(" -P version protocol version (default: 3)\n"));
+ fprintf( stderr, _(" -r delete recursively\n"));
+ tool_common_usage();
+ exit( EXIT_FAILURE );
+}
+
+
+const char options[] = "r"
+ "cd:D:e:f:h:H:IMnNO:o:p:P:QR:U:vVw:WxX:y:Y:z:Z";
+
+int
+handle_private_option( int i )
+{
+ int ival;
+ char *next;
+ switch ( i ) {
+#if 0
+ int crit;
+ char *control, *cvalue;
+ case 'E': /* delete extensions */
+ if( protocol == LDAP_VERSION2 ) {
+ fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"),
+ prog, protocol );
+ exit( EXIT_FAILURE );
+ }
+
+ /* should be extended to support comma separated list of
+ * [!]key[=value] parameters, e.g. -E !foo,bar=567
+ */
+
+ crit = 0;
+ cvalue = NULL;
+ if( optarg[0] == '!' ) {
+ crit = 1;
+ optarg++;
+ }
+
+ control = optarg;
+ if ( (cvalue = strchr( control, '=' )) != NULL ) {
+ *cvalue++ = '\0';
+ }
+ fprintf( stderr, _("Invalid delete extension name: %s\n"), control );
+ usage();
+#endif
+
+ case 'r':
+ prune = 1;
+ break;
+
+ case 'z': /* size limit */
+ if ( strcasecmp( optarg, "none" ) == 0 ) {
+ sizelimit = 0;
+
+ } else if ( strcasecmp( optarg, "max" ) == 0 ) {
+ sizelimit = LDAP_MAXINT;
+
+ } else {
+ ival = strtol( optarg, &next, 10 );
+ if ( next == NULL || next[0] != '\0' ) {
+ fprintf( stderr,
+ _("Unable to parse size limit \"%s\"\n"), optarg );
+ exit( EXIT_FAILURE );
+ }
+ sizelimit = ival;
+ }
+ if( sizelimit < 0 || sizelimit > LDAP_MAXINT ) {
+ fprintf( stderr, _("%s: invalid sizelimit (%d) specified\n"),
+ prog, sizelimit );
+ exit( EXIT_FAILURE );
+ }
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+
+static void
+private_conn_setup( LDAP *ld )
+{
+ /* this seems prudent for searches below */
+ int deref = LDAP_DEREF_NEVER;
+ ldap_set_option( ld, LDAP_OPT_DEREF, &deref );
+}
+
+
+int
+main( int argc, char **argv )
+{
+ char buf[ 4096 ];
+ FILE *fp = NULL;
+ LDAP *ld;
+ int rc, retval;
+
+ tool_init( TOOL_DELETE );
+ prog = lutil_progname( "ldapdelete", argc, argv );
+
+ tool_args( argc, argv );
+
+ if ( infile != NULL ) {
+ if (( fp = fopen( infile, "r" )) == NULL ) {
+ perror( optarg );
+ exit( EXIT_FAILURE );
+ }
+ } else {
+ if ( optind >= argc ) {
+ fp = stdin;
+ }
+ }
+
+ ld = tool_conn_setup( 0, &private_conn_setup );
+
+ tool_bind( ld );
+
+ tool_server_controls( ld, NULL, 0 );
+
+ retval = rc = 0;
+
+ if ( fp == NULL ) {
+ for ( ; optind < argc; ++optind ) {
+ rc = dodelete( ld, argv[ optind ] );
+
+ /* Stop on error and no -c option */
+ if( rc != 0 ) {
+ retval = rc;
+ if( contoper == 0 ) break;
+ }
+ }
+ } else {
+ while ((rc == 0 || contoper) && fgets(buf, sizeof(buf), fp) != NULL) {
+ buf[ strlen( buf ) - 1 ] = '\0'; /* remove trailing newline */
+
+ if ( *buf != '\0' ) {
+ rc = dodelete( ld, buf );
+ if ( rc != 0 )
+ retval = rc;
+ }
+ }
+ if ( fp != stdin )
+ fclose( fp );
+ }
+
+ tool_exit( ld, retval );
+}
+
+
+static int dodelete(
+ LDAP *ld,
+ const char *dn)
+{
+ int id;
+ int rc, code;
+ char *matcheddn = NULL, *text = NULL, **refs = NULL;
+ LDAPControl **ctrls = NULL;
+ LDAPMessage *res;
+ int subentries = 0;
+
+ if ( verbose ) {
+ printf( _("%sdeleting entry \"%s\"\n"),
+ (dont ? "!" : ""), dn );
+ }
+
+ if ( dont ) {
+ return LDAP_SUCCESS;
+ }
+
+ /* If prune is on, remove a whole subtree. Delete the children of the
+ * DN recursively, then the DN requested.
+ */
+ if ( prune ) {
+retry:;
+ deletechildren( ld, dn, subentries );
+ }
+
+ rc = ldap_delete_ext( ld, dn, NULL, NULL, &id );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s: ldap_delete_ext: %s (%d)\n",
+ prog, ldap_err2string( rc ), rc );
+ return rc;
+ }
+
+ for ( ; ; ) {
+ struct timeval tv;
+
+ if ( tool_check_abandon( ld, id ) ) {
+ return LDAP_CANCELLED;
+ }
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+
+ rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res );
+ if ( rc < 0 ) {
+ tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL );
+ return rc;
+ }
+
+ if ( rc != 0 ) {
+ break;
+ }
+ }
+
+ rc = ldap_parse_result( ld, res, &code, &matcheddn, &text, &refs, &ctrls, 1 );
+
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ break;
+
+ case LDAP_NOT_ALLOWED_ON_NONLEAF:
+ if ( prune && !subentries ) {
+ subentries = 1;
+ goto retry;
+ }
+ /* fallthru */
+
+ default:
+ fprintf( stderr, "%s: ldap_parse_result: %s (%d)\n",
+ prog, ldap_err2string( rc ), rc );
+ return rc;
+ }
+
+ if( code != LDAP_SUCCESS ) {
+ tool_perror( "ldap_delete", code, NULL, matcheddn, text, refs );
+ } else if ( verbose &&
+ ((matcheddn && *matcheddn) || (text && *text) || (refs && *refs) ))
+ {
+ printf( _("Delete Result: %s (%d)\n"),
+ ldap_err2string( code ), code );
+
+ if( text && *text ) {
+ printf( _("Additional info: %s\n"), text );
+ }
+
+ if( matcheddn && *matcheddn ) {
+ printf( _("Matched DN: %s\n"), matcheddn );
+ }
+
+ if( refs ) {
+ int i;
+ for( i=0; refs[i]; i++ ) {
+ printf(_("Referral: %s\n"), refs[i] );
+ }
+ }
+ }
+
+ if (ctrls) {
+ tool_print_ctrls( ld, ctrls );
+ ldap_controls_free( ctrls );
+ }
+
+ ber_memfree( text );
+ ber_memfree( matcheddn );
+ ber_memvfree( (void **) refs );
+
+ return code;
+}
+
+/*
+ * Delete all the children of an entry recursively until leaf nodes are reached.
+ */
+static int deletechildren(
+ LDAP *ld,
+ const char *base,
+ int subentries )
+{
+ LDAPMessage *res, *e;
+ int entries;
+ int rc = LDAP_SUCCESS, srch_rc;
+ static char *attrs[] = { LDAP_NO_ATTRS, NULL };
+ LDAPControl c, *ctrls[2], **ctrlsp = NULL;
+ BerElement *ber = NULL;
+
+ if ( verbose ) printf ( _("deleting children of: %s\n"), base );
+
+ if ( subentries ) {
+ /*
+ * Do a one level search at base for subentry children.
+ */
+
+ if ((ber = ber_alloc_t(LBER_USE_DER)) == NULL) {
+ return EXIT_FAILURE;
+ }
+ rc = ber_printf( ber, "b", 1 );
+ if ( rc == -1 ) {
+ ber_free( ber, 1 );
+ fprintf( stderr, _("Subentries control encoding error!\n"));
+ return EXIT_FAILURE;
+ }
+ if ( ber_flatten2( ber, &c.ldctl_value, 0 ) == -1 ) {
+ return EXIT_FAILURE;
+ }
+ c.ldctl_oid = LDAP_CONTROL_SUBENTRIES;
+ c.ldctl_iscritical = 1;
+ ctrls[0] = &c;
+ ctrls[1] = NULL;
+ ctrlsp = ctrls;
+ }
+
+ /*
+ * Do a one level search at base for children. For each, delete its children.
+ */
+more:;
+ srch_rc = ldap_search_ext_s( ld, base, LDAP_SCOPE_ONELEVEL, NULL, attrs, 1,
+ ctrlsp, NULL, NULL, sizelimit, &res );
+ switch ( srch_rc ) {
+ case LDAP_SUCCESS:
+ case LDAP_SIZELIMIT_EXCEEDED:
+ break;
+ default:
+ tool_perror( "ldap_search", srch_rc, NULL, NULL, NULL, NULL );
+ return( srch_rc );
+ }
+
+ entries = ldap_count_entries( ld, res );
+
+ if ( entries > 0 ) {
+ int i;
+
+ for (e = ldap_first_entry( ld, res ), i = 0; e != NULL;
+ e = ldap_next_entry( ld, e ), i++ )
+ {
+ char *dn = ldap_get_dn( ld, e );
+
+ if( dn == NULL ) {
+ ldap_get_option( ld, LDAP_OPT_RESULT_CODE, &rc );
+ tool_perror( "ldap_prune", rc, NULL, NULL, NULL, NULL );
+ ber_memfree( dn );
+ return rc;
+ }
+
+ rc = deletechildren( ld, dn, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_prune", rc, NULL, NULL, NULL, NULL );
+ ber_memfree( dn );
+ return rc;
+ }
+
+ if ( verbose ) {
+ printf( _("\tremoving %s\n"), dn );
+ }
+
+ rc = ldap_delete_ext_s( ld, dn, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_delete", rc, NULL, NULL, NULL, NULL );
+ ber_memfree( dn );
+ return rc;
+
+ }
+
+ if ( verbose ) {
+ printf( _("\t%s removed\n"), dn );
+ }
+
+ ber_memfree( dn );
+ }
+ }
+
+ ldap_msgfree( res );
+
+ if ( srch_rc == LDAP_SIZELIMIT_EXCEEDED ) {
+ goto more;
+ }
+
+ return rc;
+}
diff --git a/clients/tools/ldapexop.c b/clients/tools/ldapexop.c
new file mode 100644
index 0000000..bd6e029
--- /dev/null
+++ b/clients/tools/ldapexop.c
@@ -0,0 +1,355 @@
+/* ldapexop.c -- a tool for performing well-known extended operations */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software based, in part, on other client tools.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include <ldap.h>
+#include "ldif.h"
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "ldap_defaults.h"
+
+#include "common.h"
+
+void
+usage( void )
+{
+ fprintf( stderr, _("Issue LDAP extended operations\n\n"));
+ fprintf( stderr, _("usage: %s [options] <oid|oid:data|oid::b64data>\n"), prog);
+ fprintf( stderr, _(" %s [options] whoami\n"), prog);
+ fprintf( stderr, _(" %s [options] cancel <id>\n"), prog);
+ fprintf( stderr, _(" %s [options] refresh <DN> [<ttl>]\n"), prog);
+ tool_common_usage();
+ exit( EXIT_FAILURE );
+}
+
+
+const char options[] = ""
+ "d:D:e:h:H:InNO:o:p:QR:U:vVw:WxX:y:Y:Z";
+
+int
+handle_private_option( int i )
+{
+ switch ( i ) {
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+
+int
+main( int argc, char *argv[] )
+{
+ int rc;
+
+ LDAP *ld = NULL;
+
+ char *matcheddn = NULL, *text = NULL, **refs = NULL;
+ LDAPControl **ctrls = NULL;
+ int id, code;
+ LDAPMessage *res = NULL;
+
+ tool_init( TOOL_EXOP );
+ prog = lutil_progname( "ldapexop", argc, argv );
+
+ /* LDAPv3 only */
+ protocol = LDAP_VERSION3;
+
+ tool_args( argc, argv );
+
+ if ( argc - optind < 1 ) {
+ usage();
+ }
+
+ ld = tool_conn_setup( 0, 0 );
+
+ tool_bind( ld );
+
+ argv += optind;
+ argc -= optind;
+
+ if ( strcasecmp( argv[ 0 ], "whoami" ) == 0 ) {
+ tool_server_controls( ld, NULL, 0 );
+
+ rc = ldap_whoami( ld, NULL, NULL, &id );
+ if ( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_extended_operation", rc, NULL, NULL, NULL, NULL );
+ rc = EXIT_FAILURE;
+ goto skip;
+ }
+
+ } else if ( strcasecmp( argv[ 0 ], "cancel" ) == 0 ) {
+ int cancelid;
+
+ switch ( argc ) {
+ case 2:
+ if ( lutil_atoi( &cancelid, argv[ 1 ] ) != 0 || cancelid < 0 ) {
+ fprintf( stderr, "invalid cancelid=%s\n\n", argv[ 1 ] );
+ usage();
+ }
+ break;
+
+ default:
+ fprintf( stderr, "need cancelid\n\n" );
+ usage();
+ }
+
+ rc = ldap_cancel( ld, cancelid, NULL, NULL, &id );
+ if ( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_cancel", rc, NULL, NULL, NULL, NULL );
+ rc = EXIT_FAILURE;
+ goto skip;
+ }
+
+ } else if ( strcasecmp( argv[ 0 ], "passwd" ) == 0 ) {
+ fprintf( stderr, "use ldappasswd(1) instead.\n\n" );
+ usage();
+ /* TODO? */
+
+ } else if ( strcasecmp( argv[ 0 ], "refresh" ) == 0 ) {
+ int ttl = 3600;
+ struct berval dn;
+
+ switch ( argc ) {
+ case 3:
+ ttl = atoi( argv[ 2 ] );
+
+ case 2:
+ dn.bv_val = argv[ 1 ];
+ dn.bv_len = strlen( dn.bv_val );
+ break;
+
+ default:
+ fprintf( stderr, _("need DN [ttl]\n\n") );
+ usage();
+ }
+
+ tool_server_controls( ld, NULL, 0 );
+
+ rc = ldap_refresh( ld, &dn, ttl, NULL, NULL, &id );
+ if ( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_extended_operation", rc, NULL, NULL, NULL, NULL );
+ rc = EXIT_FAILURE;
+ goto skip;
+ }
+
+ } else {
+ char *p;
+
+ if ( argc != 1 ) {
+ usage();
+ }
+
+ p = strchr( argv[ 0 ], ':' );
+ if ( p == argv[ 0 ] ) {
+ usage();
+ }
+
+ if ( p != NULL )
+ *p++ = '\0';
+
+ if ( tool_is_oid( argv[ 0 ] ) ) {
+ struct berval reqdata;
+ struct berval type;
+ struct berval value;
+ int freeval;
+
+ if ( p != NULL ) {
+ p[ -1 ] = ':';
+ ldif_parse_line2( argv[ 0 ], &type, &value, &freeval );
+ p[ -1 ] = '\0';
+
+ if ( freeval ) {
+ reqdata = value;
+ } else {
+ ber_dupbv( &reqdata, &value );
+ }
+ }
+
+
+ tool_server_controls( ld, NULL, 0 );
+
+ rc = ldap_extended_operation( ld, argv[ 0 ], p ? &reqdata : NULL, NULL, NULL, &id );
+ if ( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_extended_operation", rc, NULL, NULL, NULL, NULL );
+ rc = EXIT_FAILURE;
+ goto skip;
+ }
+ } else {
+ fprintf( stderr, "unknown exop \"%s\"\n\n", argv[ 0 ] );
+ usage();
+ }
+ }
+
+ for ( ; ; ) {
+ struct timeval tv;
+
+ if ( tool_check_abandon( ld, id ) ) {
+ tool_exit( ld, LDAP_CANCELLED );
+ }
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+
+ rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res );
+ if ( rc < 0 ) {
+ tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL );
+ rc = EXIT_FAILURE;
+ goto skip;
+ }
+
+ if ( rc != 0 ) {
+ break;
+ }
+ }
+
+ rc = ldap_parse_result( ld, res,
+ &code, &matcheddn, &text, &refs, &ctrls, 0 );
+ if ( rc == LDAP_SUCCESS ) {
+ rc = code;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_parse_result", rc, NULL, matcheddn, text, refs );
+ rc = EXIT_FAILURE;
+ goto skip;
+ }
+
+ if ( strcasecmp( argv[ 0 ], "whoami" ) == 0 ) {
+ char *retoid = NULL;
+ struct berval *retdata = NULL;
+
+ rc = ldap_parse_extended_result( ld, res, &retoid, &retdata, 0 );
+
+ if ( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_parse_extended_result", rc, NULL, NULL, NULL, NULL );
+ rc = EXIT_FAILURE;
+ goto skip;
+ }
+
+ if ( retdata != NULL ) {
+ if ( retdata->bv_len == 0 ) {
+ printf(_("anonymous\n") );
+ } else {
+ printf("%s\n", retdata->bv_val );
+ }
+ }
+
+ ber_memfree( retoid );
+ ber_bvfree( retdata );
+
+ } else if ( strcasecmp( argv[ 0 ], "cancel" ) == 0 ) {
+ /* no extended response; returns specific errors */
+ assert( 0 );
+
+ } else if ( strcasecmp( argv[ 0 ], "passwd" ) == 0 ) {
+ /* TODO */
+
+ } else if ( strcasecmp( argv[ 0 ], "refresh" ) == 0 ) {
+ int newttl;
+
+ rc = ldap_parse_refresh( ld, res, &newttl );
+
+ if ( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_parse_refresh", rc, NULL, NULL, NULL, NULL );
+ rc = EXIT_FAILURE;
+ goto skip;
+ }
+
+ printf( "newttl=%d\n", newttl );
+
+ } else if ( tool_is_oid( argv[ 0 ] ) ) {
+ char *retoid = NULL;
+ struct berval *retdata = NULL;
+
+ if( ldif < 2 ) {
+ printf(_("# extended operation response\n"));
+ }
+
+ rc = ldap_parse_extended_result( ld, res, &retoid, &retdata, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_parse_extended_result", rc, NULL, NULL, NULL, NULL );
+ rc = EXIT_FAILURE;
+ goto skip;
+ }
+
+ if ( ldif < 2 && retoid != NULL ) {
+ tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
+ "oid", retoid, strlen(retoid) );
+ }
+
+ ber_memfree( retoid );
+
+ if( retdata != NULL ) {
+ if ( ldif < 2 ) {
+ tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_BINARY,
+ "data", retdata->bv_val, retdata->bv_len );
+ }
+
+ ber_bvfree( retdata );
+ }
+ }
+
+ if( verbose || code != LDAP_SUCCESS ||
+ ( matcheddn && *matcheddn ) || ( text && *text ) || refs ) {
+ printf( _("Result: %s (%d)\n"), ldap_err2string( code ), code );
+
+ if( text && *text ) {
+ printf( _("Additional info: %s\n"), text );
+ }
+
+ if( matcheddn && *matcheddn ) {
+ printf( _("Matched DN: %s\n"), matcheddn );
+ }
+
+ if( refs ) {
+ int i;
+ for( i=0; refs[i]; i++ ) {
+ printf(_("Referral: %s\n"), refs[i] );
+ }
+ }
+ }
+
+ if (ctrls) {
+ tool_print_ctrls( ld, ctrls );
+ ldap_controls_free( ctrls );
+ }
+
+ ber_memfree( text );
+ ber_memfree( matcheddn );
+ ber_memvfree( (void **) refs );
+
+skip:
+ /* disconnect from server */
+ if ( res )
+ ldap_msgfree( res );
+ tool_exit( ld, rc );
+}
diff --git a/clients/tools/ldapmodify.c b/clients/tools/ldapmodify.c
new file mode 100644
index 0000000..c94c11a
--- /dev/null
+++ b/clients/tools/ldapmodify.c
@@ -0,0 +1,697 @@
+/* ldapmodify.c - generic program to modify or add entries using LDAP */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2006 Howard Chu.
+ * Portions Copyright 1998-2003 Kurt D. Zeilenga.
+ * Portions Copyright 1998-2001 Net Boolean Incorporated.
+ * Portions Copyright 2001-2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission. This
+ * software is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by the University of Michigan
+ * (as part of U-MICH LDAP). Additional significant contributors
+ * include:
+ * Kurt D. Zeilenga
+ * Norbert Klasen
+ * Howard Chu
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+#include <ac/socket.h>
+#include <ac/time.h>
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <ldap.h>
+
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "ldif.h"
+#include "ldap_defaults.h"
+#include "ldap_pvt.h"
+#include "lber_pvt.h"
+
+#include "common.h"
+
+static int ldapadd;
+static char *rejfile = NULL;
+static LDAP *ld = NULL;
+
+static int process_ldif_rec LDAP_P(( char *rbuf, unsigned long lineno ));
+static int domodify LDAP_P((
+ const struct berval *dn,
+ LDAPMod **pmods,
+ LDAPControl **pctrls,
+ int newentry ));
+static int dodelete LDAP_P((
+ const struct berval *dn,
+ LDAPControl **pctrls ));
+static int dorename LDAP_P((
+ const struct berval *dn,
+ const struct berval *newrdn,
+ const struct berval *newsup,
+ int deleteoldrdn,
+ LDAPControl **pctrls ));
+static int process_response(
+ LDAP *ld,
+ int msgid,
+ int res,
+ const struct berval *dn );
+
+static int txn = 0;
+static int txnabort = 0;
+struct berval *txn_id = NULL;
+
+void
+usage( void )
+{
+ fprintf( stderr, _("Add or modify entries from an LDAP server\n\n"));
+ fprintf( stderr, _("usage: %s [options]\n"), prog);
+ fprintf( stderr, _(" The list of desired operations are read from stdin"
+ " or from the file\n"));
+ fprintf( stderr, _(" specified by \"-f file\".\n"));
+ fprintf( stderr, _("Add or modify options:\n"));
+ fprintf( stderr, _(" -a add values (%s)\n"),
+ (ldapadd ? _("default") : _("default is to replace")));
+ fprintf( stderr, _(" -c continuous operation mode (do not stop on errors)\n"));
+ fprintf( stderr, _(" -E [!]ext=extparam modify extensions"
+ " (! indicate s criticality)\n"));
+ fprintf( stderr, _(" -f file read operations from `file'\n"));
+ fprintf( stderr, _(" -M enable Manage DSA IT control (-MM to make critical)\n"));
+ fprintf( stderr, _(" -P version protocol version (default: 3)\n"));
+ fprintf( stderr,
+ _(" [!]txn=<commit|abort> (transaction)\n"));
+ fprintf( stderr, _(" -S file write skipped modifications to `file'\n"));
+
+ tool_common_usage();
+ exit( EXIT_FAILURE );
+}
+
+
+const char options[] = "aE:rS:"
+ "cd:D:e:f:h:H:IMnNO:o:p:P:QR:U:vVw:WxX:y:Y:Z";
+
+int
+handle_private_option( int i )
+{
+ char *control, *cvalue;
+ int crit;
+
+ switch ( i ) {
+ case 'E': /* modify extensions */
+ if( protocol == LDAP_VERSION2 ) {
+ fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"),
+ prog, protocol );
+ exit( EXIT_FAILURE );
+ }
+
+ /* should be extended to support comma separated list of
+ * [!]key[=value] parameters, e.g. -E !foo,bar=567
+ */
+
+ crit = 0;
+ cvalue = NULL;
+ if( optarg[0] == '!' ) {
+ crit = 1;
+ optarg++;
+ }
+
+ control = optarg;
+ if ( (cvalue = strchr( control, '=' )) != NULL ) {
+ *cvalue++ = '\0';
+ }
+
+ if( strcasecmp( control, "txn" ) == 0 ) {
+ /* Transaction */
+ if( txn ) {
+ fprintf( stderr,
+ _("txn control previously specified\n"));
+ exit( EXIT_FAILURE );
+ }
+ if( cvalue != NULL ) {
+ if( strcasecmp( cvalue, "abort" ) == 0 ) {
+ txnabort=1;
+ } else if( strcasecmp( cvalue, "commit" ) != 0 ) {
+ fprintf( stderr, _("Invalid value for txn control, %s\n"),
+ cvalue );
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ txn = 1 + crit;
+ } else
+ {
+ fprintf( stderr, _("Invalid modify extension name: %s\n"),
+ control );
+ usage();
+ }
+ break;
+
+ case 'a': /* add */
+ ldapadd = 1;
+ break;
+
+ case 'r': /* replace (obsolete) */
+ break;
+
+ case 'S': /* skipped modifications to file */
+ if( rejfile != NULL ) {
+ fprintf( stderr, _("%s: -S previously specified\n"), prog );
+ exit( EXIT_FAILURE );
+ }
+ rejfile = optarg;
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+
+int
+main( int argc, char **argv )
+{
+ char *rbuf = NULL, *rejbuf = NULL;
+ FILE *rejfp;
+ struct LDIFFP *ldiffp = NULL, ldifdummy = {0};
+ char *matched_msg, *error_msg;
+ int rc, retval, ldifrc;
+ int len;
+ int i = 0, lmax = 0;
+ unsigned long lineno, nextline = 0;
+ LDAPControl c[1];
+
+ prog = lutil_progname( "ldapmodify", argc, argv );
+
+ /* strncmp instead of strcmp since NT binaries carry .exe extension */
+ ldapadd = ( strncasecmp( prog, "ldapadd", sizeof("ldapadd")-1 ) == 0 );
+
+ tool_init( ldapadd ? TOOL_ADD : TOOL_MODIFY );
+
+ tool_args( argc, argv );
+
+ if ( argc != optind ) usage();
+
+ if ( rejfile != NULL ) {
+ if (( rejfp = fopen( rejfile, "w" )) == NULL ) {
+ perror( rejfile );
+ retval = EXIT_FAILURE;
+ goto fail;
+ }
+ } else {
+ rejfp = NULL;
+ }
+
+ if ( infile != NULL ) {
+ if (( ldiffp = ldif_open( infile, "r" )) == NULL ) {
+ perror( infile );
+ retval = EXIT_FAILURE;
+ goto fail;
+ }
+ } else {
+ ldifdummy.fp = stdin;
+ ldiffp = &ldifdummy;
+ }
+
+ if ( debug ) ldif_debug = debug;
+
+ ld = tool_conn_setup( dont, 0 );
+
+ if ( !dont ) {
+ tool_bind( ld );
+ }
+
+ if( txn ) {
+ /* start transaction */
+ rc = ldap_txn_start_s( ld, NULL, NULL, &txn_id );
+ if( rc != LDAP_SUCCESS || !txn_id ) {
+ tool_perror( "ldap_txn_start_s", rc, NULL, NULL, NULL, NULL );
+ if( txn > 1 ) {
+ retval = EXIT_FAILURE;
+ goto fail;
+ }
+ txn = 0;
+ }
+ }
+
+ if( txn ) {
+ c[i].ldctl_oid = LDAP_CONTROL_TXN_SPEC;
+ c[i].ldctl_value = *txn_id;
+ c[i].ldctl_iscritical = 1;
+ i++;
+ }
+
+ tool_server_controls( ld, c, i );
+
+ rc = 0;
+ retval = 0;
+ lineno = 1;
+ while (( rc == 0 || contoper ) && ( ldifrc = ldif_read_record( ldiffp, &nextline,
+ &rbuf, &lmax )) > 0 )
+ {
+ if ( rejfp ) {
+ len = strlen( rbuf );
+ if (( rejbuf = (char *)ber_memalloc( len+1 )) == NULL ) {
+ perror( "malloc" );
+ retval = EXIT_FAILURE;
+ goto fail;
+ }
+ memcpy( rejbuf, rbuf, len+1 );
+ }
+
+ rc = process_ldif_rec( rbuf, lineno );
+ lineno = nextline+1;
+
+ if ( rc ) retval = rc;
+ if ( rc && rejfp ) {
+ fprintf(rejfp, _("# Error: %s (%d)"), ldap_err2string(rc), rc);
+
+ matched_msg = NULL;
+ ldap_get_option(ld, LDAP_OPT_MATCHED_DN, &matched_msg);
+ if ( matched_msg != NULL ) {
+ if ( *matched_msg != '\0' ) {
+ fprintf( rejfp, _(", matched DN: %s"), matched_msg );
+ }
+ ldap_memfree( matched_msg );
+ }
+
+ error_msg = NULL;
+ ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, &error_msg);
+ if ( error_msg != NULL ) {
+ if ( *error_msg != '\0' ) {
+ fprintf( rejfp, _(", additional info: %s"), error_msg );
+ }
+ ldap_memfree( error_msg );
+ }
+ fprintf( rejfp, "\n%s\n", rejbuf );
+ }
+
+ if (rejfp) ber_memfree( rejbuf );
+ }
+ ber_memfree( rbuf );
+
+ if ( ldifrc < 0 )
+ retval = LDAP_OTHER;
+
+ if( retval == 0 && txn ) {
+ rc = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, NULL );
+ if ( rc != LDAP_OPT_SUCCESS ) {
+ fprintf( stderr, "Could not unset controls for ldap_txn_end\n");
+ }
+
+ /* create transaction */
+ rc = ldap_txn_end_s( ld, !txnabort, txn_id, NULL, NULL, NULL );
+ if( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_txn_end_s", rc, NULL, NULL, NULL, NULL );
+ retval = rc;
+ }
+ }
+
+fail:;
+ if ( rejfp != NULL ) {
+ fclose( rejfp );
+ }
+
+ if ( ldiffp != NULL && ldiffp != &ldifdummy ) {
+ ldif_close( ldiffp );
+ }
+
+ tool_exit( ld, retval );
+}
+
+
+static int
+process_ldif_rec( char *rbuf, unsigned long linenum )
+{
+ LDIFRecord lr;
+ int lrflags = ldapadd ? LDIF_DEFAULT_ADD : 0;
+ int rc;
+ struct berval rbuf_bv;
+
+#ifdef TEST_LDIF_API
+ if ( getenv( "LDIF_ENTRIES_ONLY" ) ) {
+ lrflags |= LDIF_ENTRIES_ONLY;
+ }
+ if ( getenv( "LDIF_NO_CONTROLS" ) ) {
+ lrflags |= LDIF_NO_CONTROLS;
+ }
+#endif /* TEST_LDIF_API */
+
+ rbuf_bv.bv_val = rbuf;
+ rbuf_bv.bv_len = 0; /* not used */
+ rc = ldap_parse_ldif_record( &rbuf_bv, linenum, &lr, prog, lrflags );
+
+ /* If default controls are set (as with -M option) and controls are
+ specified in the LDIF file, we must add the default controls to
+ the list of controls sent with the ldap operation.
+ */
+ if ( rc == 0 ) {
+ if (lr.lr_ctrls) {
+ LDAPControl **defctrls = NULL; /* Default server controls */
+ LDAPControl **newctrls = NULL;
+ ldap_get_option(ld, LDAP_OPT_SERVER_CONTROLS, &defctrls);
+ if (defctrls) {
+ int npc=0; /* Num of LDIF controls */
+ int ndefc=0; /* Num of default controls */
+ while (lr.lr_ctrls[npc]) npc++; /* Count LDIF controls */
+ while (defctrls[ndefc]) ndefc++; /* Count default controls */
+ newctrls = ber_memrealloc(lr.lr_ctrls,
+ (npc+ndefc+1)*sizeof(LDAPControl*));
+
+ if (newctrls == NULL) {
+ rc = LDAP_NO_MEMORY;
+ } else {
+ int i;
+ lr.lr_ctrls = newctrls;
+ for (i=npc; i<npc+ndefc; i++) {
+ lr.lr_ctrls[i] = ldap_control_dup(defctrls[i-npc]);
+ if (lr.lr_ctrls[i] == NULL) {
+ rc = LDAP_NO_MEMORY;
+ break;
+ }
+ }
+ lr.lr_ctrls[npc+ndefc] = NULL;
+ }
+ ldap_controls_free(defctrls); /* Must be freed by library */
+ }
+ }
+ }
+
+ if ( rc == 0 ) {
+ if ( LDAP_REQ_DELETE == lr.lr_op ) {
+ rc = dodelete( &lr.lr_dn, lr.lr_ctrls );
+ } else if ( LDAP_REQ_RENAME == lr.lr_op ) {
+ rc = dorename( &lr.lr_dn, &lr.lrop_newrdn, &lr.lrop_newsup, lr.lrop_delold, lr.lr_ctrls );
+ } else if ( ( LDAP_REQ_ADD == lr.lr_op ) || ( LDAP_REQ_MODIFY == lr.lr_op ) ) {
+ rc = domodify( &lr.lr_dn, lr.lrop_mods, lr.lr_ctrls, LDAP_REQ_ADD == lr.lr_op );
+ } else {
+ /* record skipped e.g. version: or comment or something we don't handle yet */
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ rc = 0;
+ }
+ }
+
+ ldap_ldif_record_done( &lr );
+
+ return( rc );
+}
+
+
+static int
+domodify(
+ const struct berval *dn,
+ LDAPMod **pmods,
+ LDAPControl **pctrls,
+ int newentry )
+{
+ int rc, i, j, k, notascii, op;
+ struct berval *bvp;
+
+ if ( ( dn == NULL ) || ( dn->bv_val == NULL ) ) {
+ fprintf( stderr, _("%s: no DN specified\n"), prog );
+ return( LDAP_PARAM_ERROR );
+ }
+
+ if ( pmods == NULL ) {
+ /* implement "touch" (empty sequence)
+ * modify operation (note that there
+ * is no symmetry with the UNIX command,
+ * since \"touch\" on a non-existent entry
+ * will fail)*/
+ printf( "warning: no attributes to %sadd (entry=\"%s\")\n",
+ newentry ? "" : "change or ", dn->bv_val );
+
+ } else {
+ for ( i = 0; pmods[ i ] != NULL; ++i ) {
+ op = pmods[ i ]->mod_op & ~LDAP_MOD_BVALUES;
+ if( op == LDAP_MOD_ADD && ( pmods[i]->mod_bvalues == NULL )) {
+ fprintf( stderr,
+ _("%s: attribute \"%s\" has no values (entry=\"%s\")\n"),
+ prog, pmods[i]->mod_type, dn->bv_val );
+ return LDAP_PARAM_ERROR;
+ }
+ }
+
+ if ( verbose ) {
+ for ( i = 0; pmods[ i ] != NULL; ++i ) {
+ op = pmods[ i ]->mod_op & ~LDAP_MOD_BVALUES;
+ printf( "%s %s:\n",
+ op == LDAP_MOD_REPLACE ? _("replace") :
+ op == LDAP_MOD_ADD ? _("add") :
+ op == LDAP_MOD_INCREMENT ? _("increment") :
+ op == LDAP_MOD_DELETE ? _("delete") :
+ _("unknown"),
+ pmods[ i ]->mod_type );
+
+ if ( pmods[ i ]->mod_bvalues != NULL ) {
+ for ( j = 0; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) {
+ bvp = pmods[ i ]->mod_bvalues[ j ];
+ notascii = 0;
+ for ( k = 0; (unsigned long) k < bvp->bv_len; ++k ) {
+ if ( !isascii( bvp->bv_val[ k ] )) {
+ notascii = 1;
+ break;
+ }
+ }
+ if ( notascii ) {
+ printf( _("\tNOT ASCII (%ld bytes)\n"), bvp->bv_len );
+ } else {
+ printf( "\t%s\n", bvp->bv_val );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( newentry ) {
+ printf( "%sadding new entry \"%s\"\n", dont ? "!" : "", dn->bv_val );
+ } else {
+ printf( "%smodifying entry \"%s\"\n", dont ? "!" : "", dn->bv_val );
+ }
+
+ if ( !dont ) {
+ int msgid;
+ if ( newentry ) {
+ rc = ldap_add_ext( ld, dn->bv_val, pmods, pctrls, NULL, &msgid );
+ } else {
+ rc = ldap_modify_ext( ld, dn->bv_val, pmods, pctrls, NULL, &msgid );
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ /* print error message about failed update including DN */
+ fprintf( stderr, _("%s: update failed: %s\n"), prog, dn->bv_val );
+ tool_perror( newentry ? "ldap_add" : "ldap_modify",
+ rc, NULL, NULL, NULL, NULL );
+ goto done;
+ }
+ rc = process_response( ld, msgid,
+ newentry ? LDAP_RES_ADD : LDAP_RES_MODIFY, dn );
+
+ if ( verbose && rc == LDAP_SUCCESS ) {
+ printf( _("modify complete\n") );
+ }
+
+ } else {
+ rc = LDAP_SUCCESS;
+ }
+
+done:
+ putchar( '\n' );
+ return rc;
+}
+
+
+static int
+dodelete(
+ const struct berval *dn,
+ LDAPControl **pctrls )
+{
+ int rc;
+ int msgid;
+
+ assert( dn != NULL );
+ assert( dn->bv_val != NULL );
+ printf( _("%sdeleting entry \"%s\"\n"), dont ? "!" : "", dn->bv_val );
+ if ( !dont ) {
+ rc = ldap_delete_ext( ld, dn->bv_val, pctrls, NULL, &msgid );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, _("%s: delete failed: %s\n"), prog, dn->bv_val );
+ tool_perror( "ldap_delete", rc, NULL, NULL, NULL, NULL );
+ goto done;
+ }
+ rc = process_response( ld, msgid, LDAP_RES_DELETE, dn );
+
+ if ( verbose && rc == LDAP_SUCCESS ) {
+ printf( _("delete complete\n") );
+ }
+ } else {
+ rc = LDAP_SUCCESS;
+ }
+
+done:
+ putchar( '\n' );
+ return( rc );
+}
+
+
+static int
+dorename(
+ const struct berval *dn,
+ const struct berval *newrdn,
+ const struct berval *newsup,
+ int deleteoldrdn,
+ LDAPControl **pctrls )
+{
+ int rc;
+ int msgid;
+
+ assert( dn != NULL );
+ assert( dn->bv_val != NULL );
+ assert( newrdn != NULL );
+ assert( newrdn->bv_val != NULL );
+ printf( _("%smodifying rdn of entry \"%s\"\n"), dont ? "!" : "", dn->bv_val );
+ if ( verbose ) {
+ printf( _("\tnew RDN: \"%s\" (%skeep existing values)\n"),
+ newrdn->bv_val, deleteoldrdn ? _("do not ") : "" );
+ }
+ if ( !dont ) {
+ rc = ldap_rename( ld, dn->bv_val, newrdn->bv_val,
+ ( newsup && newsup->bv_val ) ? newsup->bv_val : NULL,
+ deleteoldrdn, pctrls, NULL, &msgid );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, _("%s: rename failed: %s\n"), prog, dn->bv_val );
+ tool_perror( "ldap_rename", rc, NULL, NULL, NULL, NULL );
+ goto done;
+ }
+ rc = process_response( ld, msgid, LDAP_RES_RENAME, dn );
+
+ if ( verbose && rc == LDAP_SUCCESS ) {
+ printf( _("rename complete\n") );
+ }
+ } else {
+ rc = LDAP_SUCCESS;
+ }
+
+done:
+ putchar( '\n' );
+ return( rc );
+}
+
+static const char *
+res2str( int res ) {
+ switch ( res ) {
+ case LDAP_RES_ADD:
+ return "ldap_add";
+ case LDAP_RES_DELETE:
+ return "ldap_delete";
+ case LDAP_RES_MODIFY:
+ return "ldap_modify";
+ case LDAP_RES_MODRDN:
+ return "ldap_rename";
+ default:
+ assert( 0 );
+ }
+
+ return "ldap_unknown";
+}
+
+static int process_response(
+ LDAP *ld,
+ int msgid,
+ int op,
+ const struct berval *dn )
+{
+ LDAPMessage *res;
+ int rc = LDAP_OTHER, msgtype;
+ struct timeval tv = { 0, 0 };
+ int err;
+ char *text = NULL, *matched = NULL, **refs = NULL;
+ LDAPControl **ctrls = NULL;
+
+ assert( dn != NULL );
+ for ( ; ; ) {
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+
+ rc = ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res );
+ if ( tool_check_abandon( ld, msgid ) ) {
+ return LDAP_CANCELLED;
+ }
+
+ if ( rc == -1 ) {
+ ldap_get_option( ld, LDAP_OPT_RESULT_CODE, &rc );
+ tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL );
+ return rc;
+ }
+
+ if ( rc != 0 ) {
+ break;
+ }
+ }
+
+ msgtype = ldap_msgtype( res );
+
+ rc = ldap_parse_result( ld, res, &err, &matched, &text, &refs, &ctrls, 1 );
+ if ( rc == LDAP_SUCCESS ) rc = err;
+
+ if ( rc == LDAP_TXN_SPECIFY_OKAY ) {
+ rc = LDAP_SUCCESS;
+ } else if ( rc != LDAP_SUCCESS ) {
+ tool_perror( res2str( op ), rc, NULL, matched, text, refs );
+ } else if ( msgtype != op ) {
+ fprintf( stderr, "%s: msgtype: expected %d got %d\n",
+ res2str( op ), op, msgtype );
+ rc = LDAP_OTHER;
+ }
+
+ if ( text ) ldap_memfree( text );
+ if ( matched ) ldap_memfree( matched );
+ if ( refs ) ber_memvfree( (void **)refs );
+
+ if ( ctrls ) {
+ tool_print_ctrls( ld, ctrls );
+ ldap_controls_free( ctrls );
+ }
+
+ return rc;
+}
diff --git a/clients/tools/ldapmodrdn.c b/clients/tools/ldapmodrdn.c
new file mode 100644
index 0000000..d5cf43f
--- /dev/null
+++ b/clients/tools/ldapmodrdn.c
@@ -0,0 +1,330 @@
+/* ldapmodrdn.c - generic program to modify an entry's RDN using LDAP */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1998-2003 Kurt D. Zeilenga.
+ * Portions Copyright 1998-2001 Net Boolean Incorporated.
+ * Portions Copyright 2001-2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright 1999, Juan C. Gomez, All rights reserved.
+ * This software is not subject to any license of Silicon Graphics
+ * Inc. or Purdue University.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * without restriction or fee of any kind as long as this notice
+ * is preserved.
+ */
+/* Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission. This
+ * software is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by the University of Michigan
+ * (as part of U-MICH LDAP). Additional significant contributors
+ * include:
+ * Kurt D. Zeilenga
+ * Juan C Gomez
+ */
+
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+#include <ac/socket.h>
+#include <ac/time.h>
+
+#include <ldap.h>
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "ldap_defaults.h"
+
+#include "common.h"
+
+
+static char *newSuperior = NULL;
+static int remove_old_RDN = 0;
+
+
+static int domodrdn(
+ LDAP *ld,
+ char *dn,
+ char *rdn,
+ char *newSuperior,
+ int remove ); /* flag: remove old RDN */
+
+void
+usage( void )
+{
+ fprintf( stderr, _("Rename LDAP entries\n\n"));
+ fprintf( stderr, _("usage: %s [options] [dn rdn]\n"), prog);
+ fprintf( stderr, _(" dn rdn: If given, rdn will replace the RDN of the entry specified by DN\n"));
+ fprintf( stderr, _(" If not given, the list of modifications is read from stdin or\n"));
+ fprintf( stderr, _(" from the file specified by \"-f file\" (see man page).\n"));
+ fprintf( stderr, _("Rename options:\n"));
+ fprintf( stderr, _(" -c continuous operation mode (do not stop on errors)\n"));
+ fprintf( stderr, _(" -f file read operations from `file'\n"));
+ fprintf( stderr, _(" -M enable Manage DSA IT control (-MM to make critical)\n"));
+ fprintf( stderr, _(" -P version protocol version (default: 3)\n"));
+ fprintf( stderr, _(" -r remove old RDN\n"));
+ fprintf( stderr, _(" -s newsup new superior entry\n"));
+ tool_common_usage();
+ exit( EXIT_FAILURE );
+}
+
+
+const char options[] = "rs:"
+ "cd:D:e:f:h:H:IMnNO:o:p:P:QR:U:vVw:WxX:y:Y:Z";
+
+int
+handle_private_option( int i )
+{
+ switch ( i ) {
+#if 0
+ int crit;
+ char *control, *cvalue;
+ case 'E': /* modrdn extensions */
+ if( protocol == LDAP_VERSION2 ) {
+ fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"),
+ prog, version );
+ exit( EXIT_FAILURE );
+ }
+
+ /* should be extended to support comma separated list of
+ * [!]key[=value] parameters, e.g. -E !foo,bar=567
+ */
+
+ crit = 0;
+ cvalue = NULL;
+ if( optarg[0] == '!' ) {
+ crit = 1;
+ optarg++;
+ }
+
+ control = optarg;
+ if ( (cvalue = strchr( control, '=' )) != NULL ) {
+ *cvalue++ = '\0';
+ }
+ fprintf( stderr, _("Invalid modrdn extension name: %s\n"), control );
+ usage();
+#endif
+
+ case 'r': /* remove old RDN */
+ remove_old_RDN++;
+ break;
+
+ case 's': /* newSuperior */
+ if( protocol == LDAP_VERSION2 ) {
+ fprintf( stderr, _("%s: -X incompatible with LDAPv%d\n"),
+ prog, protocol );
+ exit( EXIT_FAILURE );
+ }
+ newSuperior = optarg;
+ protocol = LDAP_VERSION3;
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+
+int
+main(int argc, char **argv)
+{
+ char *entrydn = NULL, *rdn = NULL, buf[ 4096 ];
+ FILE *fp = NULL;
+ LDAP *ld = NULL;
+ int rc, retval, havedn;
+
+ tool_init( TOOL_MODRDN );
+ prog = lutil_progname( "ldapmodrdn", argc, argv );
+
+ tool_args( argc, argv );
+
+ havedn = 0;
+ if (argc - optind == 2) {
+ if (( rdn = strdup( argv[argc - 1] )) == NULL ) {
+ perror( "strdup" );
+ retval = EXIT_FAILURE;
+ goto fail;
+ }
+ if (( entrydn = strdup( argv[argc - 2] )) == NULL ) {
+ perror( "strdup" );
+ retval = EXIT_FAILURE;
+ goto fail;
+ }
+ ++havedn;
+ } else if ( argc - optind != 0 ) {
+ fprintf( stderr, _("%s: invalid number of arguments (%d), only two allowed\n"), prog, argc-optind );
+ usage();
+ }
+
+ if ( infile != NULL ) {
+ if (( fp = fopen( infile, "r" )) == NULL ) {
+ perror( infile );
+ retval = EXIT_FAILURE;
+ goto fail;
+ }
+ } else {
+ fp = stdin;
+ }
+
+ ld = tool_conn_setup( 0, 0 );
+
+ tool_bind( ld );
+
+ tool_server_controls( ld, NULL, 0 );
+
+ retval = rc = 0;
+ if (havedn)
+ retval = domodrdn( ld, entrydn, rdn, newSuperior, remove_old_RDN );
+ else while ((rc == 0 || contoper) && fgets(buf, sizeof(buf), fp) != NULL) {
+ if ( *buf != '\n' ) { /* blank lines optional, skip */
+ buf[ strlen( buf ) - 1 ] = '\0'; /* remove nl */
+
+ if ( havedn ) { /* have DN, get RDN */
+ if (( rdn = strdup( buf )) == NULL ) {
+ perror( "strdup" );
+ retval = EXIT_FAILURE;
+ goto fail;
+ }
+ rc = domodrdn(ld, entrydn, rdn, newSuperior, remove_old_RDN );
+ if ( rc != 0 )
+ retval = rc;
+ havedn = 0;
+ free( rdn ); rdn = NULL;
+ free( entrydn ); entrydn = NULL;
+ } else if ( !havedn ) { /* don't have DN yet */
+ if (( entrydn = strdup( buf )) == NULL ) {
+ retval = EXIT_FAILURE;
+ goto fail;
+ }
+ ++havedn;
+ }
+ }
+ }
+
+fail:
+ if ( fp && fp != stdin ) fclose( fp );
+ if ( entrydn ) free( entrydn );
+ if ( rdn ) free( rdn );
+ tool_exit( ld, retval );
+}
+
+static int domodrdn(
+ LDAP *ld,
+ char *dn,
+ char *rdn,
+ char *newSuperior,
+ int remove ) /* flag: remove old RDN */
+{
+ int rc, code, id;
+ char *matcheddn=NULL, *text=NULL, **refs=NULL;
+ LDAPControl **ctrls = NULL;
+ LDAPMessage *res;
+
+ if ( verbose ) {
+ printf( _("Renaming \"%s\"\n"), dn );
+ printf( _("\tnew rdn=\"%s\" (%s old rdn)\n"),
+ rdn, remove ? _("delete") : _("keep") );
+ if( newSuperior != NULL ) {
+ printf(_("\tnew parent=\"%s\"\n"), newSuperior);
+ }
+ }
+
+ if( dont ) return LDAP_SUCCESS;
+
+ rc = ldap_rename( ld, dn, rdn, newSuperior, remove,
+ NULL, NULL, &id );
+
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s: ldap_rename: %s (%d)\n",
+ prog, ldap_err2string( rc ), rc );
+ return rc;
+ }
+
+ for ( ; ; ) {
+ struct timeval tv = { 0, 0 };
+
+ if ( tool_check_abandon( ld, id ) ) {
+ return LDAP_CANCELLED;
+ }
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+
+ rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res );
+ if ( rc < 0 ) {
+ tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL );
+ return rc;
+ }
+
+ if ( rc != 0 ) {
+ break;
+ }
+ }
+
+ rc = ldap_parse_result( ld, res, &code, &matcheddn, &text, &refs, &ctrls, 1 );
+
+ if( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s: ldap_parse_result: %s (%d)\n",
+ prog, ldap_err2string( rc ), rc );
+ return rc;
+ }
+
+ if( verbose || code != LDAP_SUCCESS ||
+ (matcheddn && *matcheddn) || (text && *text) || (refs && *refs) )
+ {
+ printf( _("Rename Result: %s (%d)\n"),
+ ldap_err2string( code ), code );
+
+ if( text && *text ) {
+ printf( _("Additional info: %s\n"), text );
+ }
+
+ if( matcheddn && *matcheddn ) {
+ printf( _("Matched DN: %s\n"), matcheddn );
+ }
+
+ if( refs ) {
+ int i;
+ for( i=0; refs[i]; i++ ) {
+ printf(_("Referral: %s\n"), refs[i] );
+ }
+ }
+ }
+
+ if (ctrls) {
+ tool_print_ctrls( ld, ctrls );
+ ldap_controls_free( ctrls );
+ }
+
+ ber_memfree( text );
+ ber_memfree( matcheddn );
+ ber_memvfree( (void **) refs );
+
+ return code;
+}
diff --git a/clients/tools/ldappasswd.c b/clients/tools/ldappasswd.c
new file mode 100644
index 0000000..77ef305
--- /dev/null
+++ b/clients/tools/ldappasswd.c
@@ -0,0 +1,413 @@
+/* ldappasswd -- a tool for change LDAP passwords */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1998-2003 Kurt D. Zeilenga.
+ * Portions Copyright 1998-2001 Net Boolean Incorporated.
+ * Portions Copyright 2001-2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission. This
+ * software is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * The original ldappasswd(1) tool was developed by Dave Storey (F5
+ * Network), based on other OpenLDAP client tools (which are, of
+ * course, based on U-MICH LDAP). This version was rewritten
+ * by Kurt D. Zeilenga (based on other OpenLDAP client tools).
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include <ldap.h>
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "ldap_defaults.h"
+
+#include "common.h"
+
+
+static struct berval newpw = { 0, NULL };
+static struct berval oldpw = { 0, NULL };
+
+static int want_newpw = 0;
+static int want_oldpw = 0;
+
+static char *oldpwfile = NULL;
+static char *newpwfile = NULL;
+
+void
+usage( void )
+{
+ fprintf( stderr, _("Change password of an LDAP user\n\n"));
+ fprintf( stderr,_("usage: %s [options] [user]\n"), prog);
+ fprintf( stderr, _(" user: the authentication identity, commonly a DN\n"));
+ fprintf( stderr, _("Password change options:\n"));
+ fprintf( stderr, _(" -a secret old password\n"));
+ fprintf( stderr, _(" -A prompt for old password\n"));
+ fprintf( stderr, _(" -t file read file for old password\n"));
+ fprintf( stderr, _(" -s secret new password\n"));
+ fprintf( stderr, _(" -S prompt for new password\n"));
+ fprintf( stderr, _(" -T file read file for new password\n"));
+ tool_common_usage();
+ exit( EXIT_FAILURE );
+}
+
+
+const char options[] = "a:As:St:T:"
+ "d:D:e:h:H:InNO:o:p:QR:U:vVw:WxX:y:Y:Z";
+
+int
+handle_private_option( int i )
+{
+ switch ( i ) {
+#if 0
+ case 'E': /* passwd extensions */ {
+ int crit;
+ char *control, *cvalue;
+ if( protocol == LDAP_VERSION2 ) {
+ fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"),
+ prog, protocol );
+ exit( EXIT_FAILURE );
+ }
+
+ /* should be extended to support comma separated list of
+ * [!]key[=value] parameters, e.g. -E !foo,bar=567
+ */
+
+ crit = 0;
+ cvalue = NULL;
+ if( optarg[0] == '!' ) {
+ crit = 1;
+ optarg++;
+ }
+
+ control = optarg;
+ if ( (cvalue = strchr( control, '=' )) != NULL ) {
+ *cvalue++ = '\0';
+ }
+ fprintf( stderr, _("Invalid passwd extension name: %s\n"), control );
+ usage();
+ }
+#endif
+
+ case 'a': /* old password (secret) */
+ oldpw.bv_val = strdup( optarg );
+ {
+ char* p;
+ for( p = optarg; *p != '\0'; p++ ) {
+ *p = '\0';
+ }
+ }
+ oldpw.bv_len = strlen( oldpw.bv_val );
+ break;
+
+ case 'A': /* prompt for old password */
+ want_oldpw++;
+ break;
+
+ case 's': /* new password (secret) */
+ newpw.bv_val = strdup( optarg );
+ {
+ char* p;
+ for( p = optarg; *p != '\0'; p++ ) {
+ *p = '\0';
+ }
+ }
+ newpw.bv_len = strlen( newpw.bv_val );
+ break;
+
+ case 'S': /* prompt for user password */
+ want_newpw++;
+ break;
+
+ case 't':
+ oldpwfile = optarg;
+ break;
+
+ case 'T':
+ newpwfile = optarg;
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+
+int
+main( int argc, char *argv[] )
+{
+ int rc;
+ char *user = NULL;
+
+ LDAP *ld = NULL;
+ struct berval bv = {0, NULL};
+ BerElement *ber = NULL;
+
+ int id, code = LDAP_OTHER;
+ LDAPMessage *res;
+ char *matcheddn = NULL, *text = NULL, **refs = NULL;
+ char *retoid = NULL;
+ struct berval *retdata = NULL;
+ LDAPControl **ctrls = NULL;
+
+ tool_init( TOOL_PASSWD );
+ prog = lutil_progname( "ldappasswd", argc, argv );
+
+ /* LDAPv3 only */
+ protocol = LDAP_VERSION3;
+
+ tool_args( argc, argv );
+
+ if( argc - optind > 1 ) {
+ usage();
+ } else if ( argc - optind == 1 ) {
+ user = strdup( argv[optind] );
+ } else {
+ user = NULL;
+ }
+
+ if( oldpwfile ) {
+ rc = lutil_get_filed_password( oldpwfile, &oldpw );
+ if( rc ) {
+ rc = EXIT_FAILURE;
+ goto done;
+ }
+ }
+
+ if( want_oldpw && oldpw.bv_val == NULL ) {
+ /* prompt for old password */
+ char *ckoldpw;
+ oldpw.bv_val = strdup(getpassphrase(_("Old password: ")));
+ ckoldpw = getpassphrase(_("Re-enter old password: "));
+
+ if( oldpw.bv_val == NULL || ckoldpw == NULL ||
+ strcmp( oldpw.bv_val, ckoldpw ))
+ {
+ fprintf( stderr, _("passwords do not match\n") );
+ rc = EXIT_FAILURE;
+ goto done;
+ }
+
+ oldpw.bv_len = strlen( oldpw.bv_val );
+ }
+
+ if( newpwfile ) {
+ rc = lutil_get_filed_password( newpwfile, &newpw );
+ if( rc ) {
+ rc = EXIT_FAILURE;
+ goto done;
+ }
+ }
+
+ if( want_newpw && newpw.bv_val == NULL ) {
+ /* prompt for new password */
+ char *cknewpw;
+ newpw.bv_val = strdup(getpassphrase(_("New password: ")));
+ cknewpw = getpassphrase(_("Re-enter new password: "));
+
+ if( newpw.bv_val == NULL || cknewpw == NULL ||
+ strcmp( newpw.bv_val, cknewpw ))
+ {
+ fprintf( stderr, _("passwords do not match\n") );
+ rc = EXIT_FAILURE;
+ goto done;
+ }
+
+ newpw.bv_len = strlen( newpw.bv_val );
+ }
+
+ ld = tool_conn_setup( 0, 0 );
+
+ tool_bind( ld );
+
+ if( user != NULL || oldpw.bv_val != NULL || newpw.bv_val != NULL ) {
+ /* build the password modify request data */
+ ber = ber_alloc_t( LBER_USE_DER );
+
+ if( ber == NULL ) {
+ perror( "ber_alloc_t" );
+ rc = EXIT_FAILURE;
+ goto done;
+ }
+
+ ber_printf( ber, "{" /*}*/ );
+
+ if( user != NULL ) {
+ ber_printf( ber, "ts",
+ LDAP_TAG_EXOP_MODIFY_PASSWD_ID, user );
+ free(user);
+ }
+
+ if( oldpw.bv_val != NULL ) {
+ ber_printf( ber, "tO",
+ LDAP_TAG_EXOP_MODIFY_PASSWD_OLD, &oldpw );
+ free(oldpw.bv_val);
+ }
+
+ if( newpw.bv_val != NULL ) {
+ ber_printf( ber, "tO",
+ LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, &newpw );
+ free(newpw.bv_val);
+ }
+
+ ber_printf( ber, /*{*/ "N}" );
+
+ rc = ber_flatten2( ber, &bv, 0 );
+
+ if( rc < 0 ) {
+ perror( "ber_flatten2" );
+ rc = EXIT_FAILURE;
+ goto done;
+ }
+ }
+
+ if ( dont ) {
+ rc = LDAP_SUCCESS;
+ goto done;
+ }
+
+ tool_server_controls( ld, NULL, 0);
+
+ rc = ldap_extended_operation( ld,
+ LDAP_EXOP_MODIFY_PASSWD, bv.bv_val ? &bv : NULL,
+ NULL, NULL, &id );
+
+ ber_free( ber, 1 );
+
+ if( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_extended_operation", rc, NULL, NULL, NULL, NULL );
+ rc = EXIT_FAILURE;
+ goto done;
+ }
+
+ for ( ; ; ) {
+ struct timeval tv;
+
+ if ( tool_check_abandon( ld, id ) ) {
+ tool_exit( ld, LDAP_CANCELLED );
+ }
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+
+ rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res );
+ if ( rc < 0 ) {
+ tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL );
+ tool_exit( ld, rc );
+ }
+
+ if ( rc != 0 ) {
+ break;
+ }
+ }
+
+ rc = ldap_parse_result( ld, res,
+ &code, &matcheddn, &text, &refs, &ctrls, 0 );
+ if( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_parse_result", rc, NULL, NULL, NULL, NULL );
+ rc = EXIT_FAILURE;
+ goto done;
+ }
+
+ rc = ldap_parse_extended_result( ld, res, &retoid, &retdata, 1 );
+ if( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_parse_extended_result", rc, NULL, NULL, NULL, NULL );
+ rc = EXIT_FAILURE;
+ goto done;
+ }
+
+ if( retdata != NULL ) {
+ ber_tag_t tag;
+ char *s;
+ ber = ber_init( retdata );
+
+ if( ber == NULL ) {
+ perror( "ber_init" );
+ rc = EXIT_FAILURE;
+ goto done;
+ }
+
+ /* we should check the tag */
+ tag = ber_scanf( ber, "{a}", &s);
+
+ if( tag == LBER_ERROR ) {
+ perror( "ber_scanf" );
+ } else {
+ printf(_("New password: %s\n"), s);
+ ber_memfree( s );
+ }
+
+ ber_free( ber, 1 );
+
+ } else if ( code == LDAP_SUCCESS && newpw.bv_val == NULL ) {
+ tool_perror( "ldap_parse_extended_result", LDAP_DECODING_ERROR,
+ " new password expected", NULL, NULL, NULL );
+ }
+
+ if( verbose || code != LDAP_SUCCESS ||
+ ( matcheddn && *matcheddn ) || ( text && *text ) || refs || ctrls )
+ {
+ printf( _("Result: %s (%d)\n"), ldap_err2string( code ), code );
+
+ if( text && *text ) {
+ printf( _("Additional info: %s\n"), text );
+ }
+
+ if( matcheddn && *matcheddn ) {
+ printf( _("Matched DN: %s\n"), matcheddn );
+ }
+
+ if( refs ) {
+ int i;
+ for( i=0; refs[i]; i++ ) {
+ printf(_("Referral: %s\n"), refs[i] );
+ }
+ }
+
+ if( ctrls ) {
+ tool_print_ctrls( ld, ctrls );
+ ldap_controls_free( ctrls );
+ }
+ }
+
+ ber_memfree( text );
+ ber_memfree( matcheddn );
+ ber_memvfree( (void **) refs );
+ ber_memfree( retoid );
+ ber_bvfree( retdata );
+
+ rc = ( code == LDAP_SUCCESS ) ? EXIT_SUCCESS : EXIT_FAILURE;
+
+done:
+ /* disconnect from server */
+ tool_exit( ld, rc );
+}
diff --git a/clients/tools/ldapsearch.c b/clients/tools/ldapsearch.c
new file mode 100644
index 0000000..02b49bd
--- /dev/null
+++ b/clients/tools/ldapsearch.c
@@ -0,0 +1,2423 @@
+/* ldapsearch -- a tool for searching LDAP directories */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1998-2003 Kurt D. Zeilenga.
+ * Portions Copyright 1998-2001 Net Boolean Incorporated.
+ * Portions Copyright 2001-2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission. This
+ * software is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by the University of Michigan
+ * (as part of U-MICH LDAP). Additional significant contributors
+ * include:
+ * Jong Hyuk Choi
+ * Lynn Moss
+ * Mikhail Sahalaev
+ * Kurt D. Zeilenga
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+#include <ac/errno.h>
+#include <ac/time.h>
+
+#include <sys/stat.h>
+
+#include <ac/signal.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+
+#include <ldap.h>
+
+#include "ldif.h"
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "ldap_defaults.h"
+#include "ldap_pvt.h"
+
+#include "common.h"
+
+#if !LDAP_DEPRECATED
+/*
+ * NOTE: we use this deprecated function only because
+ * we want ldapsearch to provide some client-side sorting
+ * capability.
+ */
+/* from ldap.h */
+typedef int (LDAP_SORT_AD_CMP_PROC) LDAP_P(( /* deprecated */
+ LDAP_CONST char *left,
+ LDAP_CONST char *right ));
+
+LDAP_F( int ) /* deprecated */
+ldap_sort_entries LDAP_P(( LDAP *ld,
+ LDAPMessage **chain,
+ LDAP_CONST char *attr,
+ LDAP_SORT_AD_CMP_PROC *cmp ));
+#endif
+
+static int scope = LDAP_SCOPE_SUBTREE;
+static int deref = -1;
+static int attrsonly;
+static int timelimit = -1;
+static int sizelimit = -1;
+
+static char *control;
+
+static char *def_tmpdir;
+static char *def_urlpre;
+
+#if defined(__CYGWIN__) || defined(__MINGW32__)
+/* Turn off commandline globbing, otherwise you cannot search for
+ * attribute '*'
+ */
+int _CRT_glob = 0;
+#endif
+
+void
+usage( void )
+{
+ fprintf( stderr, _("usage: %s [options] [filter [attributes...]]\nwhere:\n"), prog);
+ fprintf( stderr, _(" filter\tRFC 4515 compliant LDAP search filter\n"));
+ fprintf( stderr, _(" attributes\twhitespace-separated list of attribute descriptions\n"));
+ fprintf( stderr, _(" which may include:\n"));
+ fprintf( stderr, _(" 1.1 no attributes\n"));
+ fprintf( stderr, _(" * all user attributes\n"));
+ fprintf( stderr, _(" + all operational attributes\n"));
+
+
+ fprintf( stderr, _("Search options:\n"));
+ fprintf( stderr, _(" -a deref one of never (default), always, search, or find\n"));
+ fprintf( stderr, _(" -A retrieve attribute names only (no values)\n"));
+ fprintf( stderr, _(" -b basedn base dn for search\n"));
+ fprintf( stderr, _(" -c continuous operation mode (do not stop on errors)\n"));
+ fprintf( stderr, _(" -E [!]<ext>[=<extparam>] search extensions (! indicates criticality)\n"));
+#ifdef LDAP_CONTROL_X_ACCOUNT_USABILITY
+ fprintf( stderr, _(" [!]accountUsability (NetScape Account usability)\n"));
+#endif
+ fprintf( stderr, _(" [!]domainScope (domain scope)\n"));
+ fprintf( stderr, _(" !dontUseCopy (Don't Use Copy)\n"));
+ fprintf( stderr, _(" [!]mv=<filter> (RFC 3876 matched values filter)\n"));
+ fprintf( stderr, _(" [!]pr=<size>[/prompt|noprompt] (RFC 2696 paged results/prompt)\n"));
+ fprintf( stderr, _(" [!]ps=<changetypes>/<changesonly>/<echg> (draft persistent search)\n"));
+ fprintf( stderr, _(" [!]sss=[-]<attr[:OID]>[/[-]<attr[:OID]>...]\n"));
+ fprintf( stderr, _(" (RFC 2891 server side sorting)\n"));
+ fprintf( stderr, _(" [!]subentries[=true|false] (RFC 3672 subentries)\n"));
+ fprintf( stderr, _(" [!]sync=ro[/<cookie>] (RFC 4533 LDAP Sync refreshOnly)\n"));
+ fprintf( stderr, _(" rp[/<cookie>][/<slimit>] (refreshAndPersist)\n"));
+ fprintf( stderr, _(" [!]vlv=<before>/<after>(/<offset>/<count>|:<value>)\n"));
+ fprintf( stderr, _(" (ldapv3-vlv-09 virtual list views)\n"));
+#ifdef LDAP_CONTROL_X_DEREF
+ fprintf( stderr, _(" [!]deref=derefAttr:attr[,...][;derefAttr:attr[,...][;...]]\n"));
+#endif
+#ifdef LDAP_CONTROL_X_DIRSYNC
+ fprintf( stderr, _(" !dirSync=<flags>/<maxAttrCount>[/<cookie>]\n"));
+ fprintf( stderr, _(" (MS AD DirSync)\n"));
+#endif
+#ifdef LDAP_CONTROL_X_EXTENDED_DN
+ fprintf( stderr, _(" [!]extendedDn=<flag> (MS AD Extended DN\n"));
+#endif
+#ifdef LDAP_CONTROL_X_SHOW_DELETED
+ fprintf( stderr, _(" [!]showDeleted (MS AD Show Deleted)\n"));
+#endif
+#ifdef LDAP_CONTROL_X_SERVER_NOTIFICATION
+ fprintf( stderr, _(" [!]serverNotif (MS AD Server Notification)\n"));
+#endif
+ fprintf( stderr, _(" [!]<oid>[=:<value>|::<b64value>] (generic control; no response handling)\n"));
+ fprintf( stderr, _(" -f file read operations from `file'\n"));
+ fprintf( stderr, _(" -F prefix URL prefix for files (default: %s)\n"), def_urlpre);
+ fprintf( stderr, _(" -l limit time limit (in seconds, or \"none\" or \"max\") for search\n"));
+ fprintf( stderr, _(" -L print responses in LDIFv1 format\n"));
+ fprintf( stderr, _(" -LL print responses in LDIF format without comments\n"));
+ fprintf( stderr, _(" -LLL print responses in LDIF format without comments\n"));
+ fprintf( stderr, _(" and version\n"));
+ fprintf( stderr, _(" -M enable Manage DSA IT control (-MM to make critical)\n"));
+ fprintf( stderr, _(" -P version protocol version (default: 3)\n"));
+ fprintf( stderr, _(" -s scope one of base, one, sub or children (search scope)\n"));
+ fprintf( stderr, _(" -S attr sort the results by attribute `attr'\n"));
+ fprintf( stderr, _(" -t write binary values to files in temporary directory\n"));
+ fprintf( stderr, _(" -tt write all values to files in temporary directory\n"));
+ fprintf( stderr, _(" -T path write files to directory specified by path (default: %s)\n"), def_tmpdir);
+ fprintf( stderr, _(" -u include User Friendly entry names in the output\n"));
+ fprintf( stderr, _(" -z limit size limit (in entries, or \"none\" or \"max\") for search\n"));
+ tool_common_usage();
+ exit( EXIT_FAILURE );
+}
+
+static void print_entry LDAP_P((
+ LDAP *ld,
+ LDAPMessage *entry,
+ int attrsonly));
+
+static void print_reference(
+ LDAP *ld,
+ LDAPMessage *reference );
+
+static void print_extended(
+ LDAP *ld,
+ LDAPMessage *extended );
+
+static void print_syncinfo(
+ BerValue *info );
+
+static void print_partial(
+ LDAP *ld,
+ LDAPMessage *partial );
+
+static int print_result(
+ LDAP *ld,
+ LDAPMessage *result,
+ int search );
+
+static int dosearch LDAP_P((
+ LDAP *ld,
+ char *base,
+ int scope,
+ char *filtpatt,
+ char *value,
+ char **attrs,
+ int attrsonly,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ struct timeval *timeout,
+ int sizelimit ));
+
+static char *tmpdir = NULL;
+static char *urlpre = NULL;
+static char *base = NULL;
+static char *sortattr = NULL;
+static int includeufn, vals2tmp = 0;
+
+static int subentries = 0, valuesReturnFilter = 0;
+static char *vrFilter = NULL;
+
+#ifdef LDAP_CONTROL_X_ACCOUNT_USABILITY
+static int accountUsability = 0;
+#endif
+
+#ifdef LDAP_CONTROL_DONTUSECOPY
+static int dontUseCopy = 0;
+#endif
+
+static int domainScope = 0;
+
+static int sss = 0;
+static LDAPSortKey **sss_keys = NULL;
+
+static int vlv = 0;
+static LDAPVLVInfo vlvInfo;
+static struct berval vlvValue;
+
+static int ldapsync = 0;
+static struct berval sync_cookie = { 0, NULL };
+static int sync_slimit = -1;
+
+static int psearch = 0;
+static int ps_chgtypes, ps_chgsonly, ps_echg_ctrls;
+
+/* cookie and morePagedResults moved to common.c */
+static int pagedResults = 0;
+static int pagePrompt = 1;
+static ber_int_t pageSize = 0;
+static ber_int_t entriesLeft = 0;
+static int npagedresponses;
+static int npagedentries;
+static int npagedreferences;
+static int npagedextended;
+static int npagedpartial;
+
+static LDAPControl *c = NULL;
+static int nctrls = 0;
+static int save_nctrls = 0;
+
+#ifdef LDAP_CONTROL_X_DEREF
+static int derefcrit;
+static LDAPDerefSpec *ds;
+static struct berval derefval;
+#endif
+
+#ifdef LDAP_CONTROL_X_DIRSYNC
+static int dirSync;
+static int dirSyncFlags;
+static int dirSyncMaxAttrCount;
+static struct berval dirSyncCookie;
+#endif
+
+#ifdef LDAP_CONTROL_X_EXTENDED_DN
+static int extendedDn;
+static int extendedDnFlag;
+#endif
+
+#ifdef LDAP_CONTROL_X_SHOW_DELETED
+static int showDeleted;
+#endif
+
+#ifdef LDAP_CONTROL_X_SERVER_NOTIFICATION
+static int serverNotif;
+#endif
+
+static int
+ctrl_add( void )
+{
+ LDAPControl *tmpc;
+
+ nctrls++;
+ tmpc = realloc( c, sizeof( LDAPControl ) * nctrls );
+ if ( tmpc == NULL ) {
+ nctrls--;
+ fprintf( stderr,
+ _("unable to make room for control; out of memory?\n"));
+ return -1;
+ }
+ c = tmpc;
+
+ return 0;
+}
+
+static void
+urlize(char *url)
+{
+ char *p;
+
+ if (*LDAP_DIRSEP != '/') {
+ for (p = url; *p; p++) {
+ if (*p == *LDAP_DIRSEP)
+ *p = '/';
+ }
+ }
+}
+
+static int
+parse_vlv(char *cvalue)
+{
+ char *keyp, *key2;
+ int num1, num2;
+
+ keyp = cvalue;
+ if ( sscanf( keyp, "%d/%d", &num1, &num2 ) != 2 ) {
+ fprintf( stderr,
+ _("VLV control value \"%s\" invalid\n"),
+ cvalue );
+ return -1;
+ }
+ vlvInfo.ldvlv_before_count = num1;
+ vlvInfo.ldvlv_after_count = num2;
+ keyp = strchr( keyp, '/' ) + 1;
+ key2 = strchr( keyp, '/' );
+ if ( key2 ) {
+ keyp = key2 + 1;
+ if ( sscanf( keyp, "%d/%d", &num1, &num2 ) != 2 ) {
+ fprintf( stderr,
+ _("VLV control value \"%s\" invalid\n"),
+ cvalue );
+ return -1;
+ }
+ vlvInfo.ldvlv_offset = num1;
+ vlvInfo.ldvlv_count = num2;
+ vlvInfo.ldvlv_attrvalue = NULL;
+ } else {
+ key2 = strchr( keyp, ':' );
+ if ( !key2 ) {
+ fprintf( stderr,
+ _("VLV control value \"%s\" invalid\n"),
+ cvalue );
+ return -1;
+ }
+ ber_str2bv( key2+1, 0, 0, &vlvValue );
+ vlvInfo.ldvlv_attrvalue = &vlvValue;
+ }
+ return 0;
+}
+
+const char options[] = "a:Ab:cE:F:l:Ls:S:tT:uz:"
+ "Cd:D:e:f:h:H:IMnNO:o:p:P:QR:U:vVw:WxX:y:Y:Z";
+
+int
+handle_private_option( int i )
+{
+ int crit, ival;
+ char *cvalue, *next;
+ switch ( i ) {
+ case 'a': /* set alias deref option */
+ if ( strcasecmp( optarg, "never" ) == 0 ) {
+ deref = LDAP_DEREF_NEVER;
+ } else if ( strncasecmp( optarg, "search", sizeof("search")-1 ) == 0 ) {
+ deref = LDAP_DEREF_SEARCHING;
+ } else if ( strncasecmp( optarg, "find", sizeof("find")-1 ) == 0 ) {
+ deref = LDAP_DEREF_FINDING;
+ } else if ( strcasecmp( optarg, "always" ) == 0 ) {
+ deref = LDAP_DEREF_ALWAYS;
+ } else {
+ fprintf( stderr,
+ _("alias deref should be never, search, find, or always\n") );
+ usage();
+ }
+ break;
+ case 'A': /* retrieve attribute names only -- no values */
+ ++attrsonly;
+ break;
+ case 'b': /* search base */
+ base = optarg;
+ break;
+ case 'E': /* search extensions */
+ if( protocol == LDAP_VERSION2 ) {
+ fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"),
+ prog, protocol );
+ exit( EXIT_FAILURE );
+ }
+
+ /* should be extended to support comma separated list of
+ * [!]key[=value] parameters, e.g. -E !foo,bar=567
+ */
+
+ crit = 0;
+ cvalue = NULL;
+ while ( optarg[0] == '!' ) {
+ crit++;
+ optarg++;
+ }
+
+ control = optarg;
+ if ( (cvalue = strchr( control, '=' )) != NULL ) {
+ *cvalue++ = '\0';
+ }
+
+ if ( strcasecmp( control, "mv" ) == 0 ) {
+ /* ValuesReturnFilter control */
+ if( valuesReturnFilter ) {
+ fprintf( stderr,
+ _("ValuesReturnFilter previously specified\n"));
+ exit( EXIT_FAILURE );
+ }
+ valuesReturnFilter= 1 + crit;
+
+ if ( cvalue == NULL ) {
+ fprintf( stderr,
+ _("missing filter in ValuesReturnFilter control\n"));
+ exit( EXIT_FAILURE );
+ }
+
+ vrFilter = cvalue;
+ protocol = LDAP_VERSION3;
+
+ } else if ( strcasecmp( control, "pr" ) == 0 ) {
+ int num, tmp;
+ /* PagedResults control */
+ if ( pagedResults != 0 ) {
+ fprintf( stderr,
+ _("PagedResultsControl previously specified\n") );
+ exit( EXIT_FAILURE );
+ }
+ if ( vlv != 0 ) {
+ fprintf( stderr,
+ _("PagedResultsControl incompatible with VLV\n") );
+ exit( EXIT_FAILURE );
+ }
+
+ if( cvalue != NULL ) {
+ char *promptp;
+
+ promptp = strchr( cvalue, '/' );
+ if ( promptp != NULL ) {
+ *promptp++ = '\0';
+ if ( strcasecmp( promptp, "prompt" ) == 0 ) {
+ pagePrompt = 1;
+ } else if ( strcasecmp( promptp, "noprompt" ) == 0) {
+ pagePrompt = 0;
+ } else {
+ fprintf( stderr,
+ _("Invalid value for PagedResultsControl,"
+ " %s/%s.\n"), cvalue, promptp );
+ exit( EXIT_FAILURE );
+ }
+ }
+ num = sscanf( cvalue, "%d", &tmp );
+ if ( num != 1 ) {
+ fprintf( stderr,
+ _("Invalid value for PagedResultsControl, %s.\n"),
+ cvalue );
+ exit( EXIT_FAILURE );
+ }
+ } else {
+ fprintf(stderr, _("Invalid value for PagedResultsControl.\n"));
+ exit( EXIT_FAILURE );
+ }
+ pageSize = (ber_int_t) tmp;
+ pagedResults = 1 + crit;
+
+ } else if ( strcasecmp( control, "ps" ) == 0 ) {
+ int num;
+ /* PersistentSearch control */
+ if ( psearch != 0 ) {
+ fprintf( stderr,
+ _("PersistentSearch previously specified\n") );
+ exit( EXIT_FAILURE );
+ }
+ if( cvalue != NULL ) {
+ num = sscanf( cvalue, "%i/%d/%d", &ps_chgtypes, &ps_chgsonly, &ps_echg_ctrls );
+ if ( num != 3 ) {
+ fprintf( stderr,
+ _("Invalid value for PersistentSearch, %s.\n"),
+ cvalue );
+ exit( EXIT_FAILURE );
+ }
+ } else {
+ fprintf(stderr, _("Invalid value for PersistentSearch.\n"));
+ exit( EXIT_FAILURE );
+ }
+ psearch = 1 + crit;
+
+#ifdef LDAP_CONTROL_DONTUSECOPY
+ } else if ( strcasecmp( control, "dontUseCopy" ) == 0 ) {
+ if( dontUseCopy ) {
+ fprintf( stderr,
+ _("dontUseCopy control previously specified\n"));
+ exit( EXIT_FAILURE );
+ }
+ if( cvalue != NULL ) {
+ fprintf( stderr,
+ _("dontUseCopy: no control value expected\n") );
+ usage();
+ }
+ if( !crit ) {
+ fprintf( stderr,
+ _("dontUseCopy: critical flag required\n") );
+ usage();
+ }
+
+ dontUseCopy = 1 + crit;
+#endif
+ } else if ( strcasecmp( control, "domainScope" ) == 0 ) {
+ if( domainScope ) {
+ fprintf( stderr,
+ _("domainScope control previously specified\n"));
+ exit( EXIT_FAILURE );
+ }
+ if( cvalue != NULL ) {
+ fprintf( stderr,
+ _("domainScope: no control value expected\n") );
+ usage();
+ }
+
+ domainScope = 1 + crit;
+
+ } else if ( strcasecmp( control, "sss" ) == 0 ) {
+ char *keyp;
+ if( sss ) {
+ fprintf( stderr,
+ _("server side sorting control previously specified\n"));
+ exit( EXIT_FAILURE );
+ }
+ if( cvalue == NULL ) {
+ fprintf( stderr,
+ _("missing specification of sss control\n") );
+ exit( EXIT_FAILURE );
+ }
+ keyp = cvalue;
+ while ( ( keyp = strchr(keyp, '/') ) != NULL ) {
+ *keyp++ = ' ';
+ }
+ if ( ldap_create_sort_keylist( &sss_keys, cvalue )) {
+ fprintf( stderr,
+ _("server side sorting control value \"%s\" invalid\n"),
+ cvalue );
+ exit( EXIT_FAILURE );
+ }
+
+ sss = 1 + crit;
+
+ } else if ( strcasecmp( control, "subentries" ) == 0 ) {
+ if( subentries ) {
+ fprintf( stderr,
+ _("subentries control previously specified\n"));
+ exit( EXIT_FAILURE );
+ }
+ if( cvalue == NULL || strcasecmp( cvalue, "true") == 0 ) {
+ subentries = 2;
+ } else if ( strcasecmp( cvalue, "false") == 0 ) {
+ subentries = 1;
+ } else {
+ fprintf( stderr,
+ _("subentries control value \"%s\" invalid\n"),
+ cvalue );
+ exit( EXIT_FAILURE );
+ }
+ if( crit ) subentries *= -1;
+
+ } else if ( strcasecmp( control, "sync" ) == 0 ) {
+ char *cookiep;
+ char *slimitp;
+ if ( ldapsync ) {
+ fprintf( stderr, _("sync control previously specified\n") );
+ exit( EXIT_FAILURE );
+ }
+ if ( cvalue == NULL ) {
+ fprintf( stderr, _("missing specification of sync control\n"));
+ exit( EXIT_FAILURE );
+ }
+ if ( strncasecmp( cvalue, "ro", 2 ) == 0 ) {
+ ldapsync = LDAP_SYNC_REFRESH_ONLY;
+ cookiep = strchr( cvalue, '/' );
+ if ( cookiep != NULL ) {
+ cookiep++;
+ if ( *cookiep != '\0' ) {
+ ber_str2bv( cookiep, 0, 0, &sync_cookie );
+ }
+ }
+ } else if ( strncasecmp( cvalue, "rp", 2 ) == 0 ) {
+ ldapsync = LDAP_SYNC_REFRESH_AND_PERSIST;
+ cookiep = strchr( cvalue, '/' );
+ if ( cookiep != NULL ) {
+ *cookiep++ = '\0';
+ cvalue = cookiep;
+ }
+ slimitp = strchr( cvalue, '/' );
+ if ( slimitp != NULL ) {
+ *slimitp++ = '\0';
+ }
+ if ( cookiep != NULL && *cookiep != '\0' )
+ ber_str2bv( cookiep, 0, 0, &sync_cookie );
+ if ( slimitp != NULL && *slimitp != '\0' ) {
+ ival = strtol( slimitp, &next, 10 );
+ if ( next == NULL || next[0] != '\0' ) {
+ fprintf( stderr, _("Unable to parse sync control value \"%s\"\n"), slimitp );
+ exit( EXIT_FAILURE );
+ }
+ sync_slimit = ival;
+ }
+ } else {
+ fprintf( stderr, _("sync control value \"%s\" invalid\n"),
+ cvalue );
+ exit( EXIT_FAILURE );
+ }
+ if ( crit ) ldapsync *= -1;
+
+ } else if ( strcasecmp( control, "vlv" ) == 0 ) {
+ if( vlv ) {
+ fprintf( stderr,
+ _("virtual list view control previously specified\n"));
+ exit( EXIT_FAILURE );
+ }
+ if ( pagedResults != 0 ) {
+ fprintf( stderr,
+ _("PagedResultsControl incompatible with VLV\n") );
+ exit( EXIT_FAILURE );
+ }
+ if( cvalue == NULL ) {
+ fprintf( stderr,
+ _("missing specification of vlv control\n") );
+ exit( EXIT_FAILURE );
+ }
+ if ( parse_vlv( cvalue ))
+ exit( EXIT_FAILURE );
+
+ vlv = 1 + crit;
+
+#ifdef LDAP_CONTROL_X_DEREF
+ } else if ( strcasecmp( control, "deref" ) == 0 ) {
+ int ispecs;
+ char **specs;
+
+ /* cvalue is something like
+ *
+ * derefAttr:attr[,attr[...]][;derefAttr:attr[,attr[...]]]"
+ */
+
+ specs = ldap_str2charray( cvalue, ";" );
+ if ( specs == NULL ) {
+ fprintf( stderr, _("deref specs \"%s\" invalid\n"),
+ cvalue );
+ exit( EXIT_FAILURE );
+ }
+ for ( ispecs = 0; specs[ ispecs ] != NULL; ispecs++ )
+ /* count'em */ ;
+
+ ds = ldap_memcalloc( ispecs + 1, sizeof( LDAPDerefSpec ) );
+ if ( ds == NULL ) {
+ perror( "malloc" );
+ exit( EXIT_FAILURE );
+ }
+
+ for ( ispecs = 0; specs[ ispecs ] != NULL; ispecs++ ) {
+ char *ptr;
+
+ ptr = strchr( specs[ ispecs ], ':' );
+ if ( ptr == NULL ) {
+ fprintf( stderr, _("deref specs \"%s\" invalid\n"),
+ cvalue );
+ exit( EXIT_FAILURE );
+ }
+
+ ds[ ispecs ].derefAttr = specs[ ispecs ];
+ *ptr++ = '\0';
+ ds[ ispecs ].attributes = ldap_str2charray( ptr, "," );
+ }
+
+ derefcrit = 1 + crit;
+
+ ldap_memfree( specs );
+#endif /* LDAP_CONTROL_X_DEREF */
+
+#ifdef LDAP_CONTROL_X_DIRSYNC
+ } else if ( strcasecmp( control, "dirSync" ) == 0 ) {
+ char *maxattrp;
+ char *cookiep;
+ int num, tmp;
+ if( dirSync ) {
+ fprintf( stderr,
+ _("dirSync control previously specified\n"));
+ exit( EXIT_FAILURE );
+ }
+ if ( cvalue == NULL ) {
+ fprintf( stderr, _("missing specification of dirSync control\n"));
+ exit( EXIT_FAILURE );
+ }
+ if( !crit ) {
+ fprintf( stderr,
+ _("dirSync: critical flag required\n") );
+ usage();
+ }
+ maxattrp = strchr( cvalue, '/' );
+ if ( maxattrp == NULL ) {
+ fprintf( stderr, _("dirSync control value \"%s\" invalid\n"),
+ cvalue );
+ exit( EXIT_FAILURE );
+ }
+ *maxattrp++ = '\0';
+ cookiep = strchr( maxattrp, '/' );
+ if ( cookiep != NULL ) {
+ if ( cookiep[1] != '\0' ) {
+ struct berval type;
+ int freeval;
+ char save1, save2;
+
+ /* dummy type "x"
+ * to use ldif_parse_line2() */
+ save1 = cookiep[ -1 ];
+ save2 = cookiep[ -2 ];
+ cookiep[ -2 ] = 'x';
+ cookiep[ -1 ] = ':';
+ cookiep[ 0 ] = ':';
+ ldif_parse_line2( &cookiep[ -2 ], &type,
+ &dirSyncCookie, &freeval );
+ cookiep[ -1 ] = save1;
+ cookiep[ -2 ] = save2;
+ }
+ *cookiep = '\0';
+ }
+ num = sscanf( cvalue, "%i", &tmp );
+ if ( num != 1 ) {
+ fprintf( stderr,
+ _("Invalid value for dirSync, %s.\n"),
+ cvalue );
+ exit( EXIT_FAILURE );
+ }
+ dirSyncFlags = tmp;
+
+ num = sscanf( maxattrp, "%d", &tmp );
+ if ( num != 1 ) {
+ fprintf( stderr,
+ _("Invalid value for dirSync, %s.\n"),
+ maxattrp );
+ exit( EXIT_FAILURE );
+ }
+ dirSyncMaxAttrCount = tmp;
+
+ dirSync = 1 + crit;
+#endif /* LDAP_CONTROL_X_DIRSYNC */
+
+#ifdef LDAP_CONTROL_X_EXTENDED_DN
+ } else if ( strcasecmp( control, "extendedDn" ) == 0 ) {
+ int num, tmp;
+ if( extendedDn ) {
+ fprintf( stderr,
+ _("extendedDn control previously specified\n"));
+ exit( EXIT_FAILURE );
+ }
+ if ( cvalue == NULL ) {
+ fprintf( stderr, _("missing specification of extendedDn control\n"));
+ exit( EXIT_FAILURE );
+ }
+ num = sscanf( cvalue, "%d", &tmp );
+ if ( num != 1 ) {
+ fprintf( stderr,
+ _("Invalid value for extendedDn, %s.\n"),
+ cvalue );
+ exit( EXIT_FAILURE );
+ }
+
+ extendedDnFlag = tmp;
+ extendedDn = 1 + crit;
+#endif /* LDAP_CONTROL_X_EXTENDED_DN */
+
+#ifdef LDAP_CONTROL_X_SHOW_DELETED
+ } else if ( strcasecmp( control, "showDeleted" ) == 0 ) {
+ if( showDeleted ) {
+ fprintf( stderr,
+ _("showDeleted control previously specified\n"));
+ exit( EXIT_FAILURE );
+ }
+ if ( cvalue != NULL ) {
+ fprintf( stderr,
+ _("showDeleted: no control value expected\n") );
+ usage();
+ }
+
+ showDeleted = 1 + crit;
+#endif /* LDAP_CONTROL_X_SHOW_DELETED */
+
+#ifdef LDAP_CONTROL_X_SERVER_NOTIFICATION
+ } else if ( strcasecmp( control, "serverNotif" ) == 0 ) {
+ if( serverNotif ) {
+ fprintf( stderr,
+ _("serverNotif control previously specified\n"));
+ exit( EXIT_FAILURE );
+ }
+ if ( cvalue != NULL ) {
+ fprintf( stderr,
+ _("serverNotif: no control value expected\n") );
+ usage();
+ }
+
+ serverNotif = 1 + crit;
+#endif /* LDAP_CONTROL_X_SERVER_NOTIFICATION */
+
+#ifdef LDAP_CONTROL_X_ACCOUNT_USABILITY
+ } else if ( strcasecmp( control, "accountUsability" ) == 0 ) {
+ if( accountUsability ) {
+ fprintf( stderr,
+ _("accountUsability control previously specified\n"));
+ exit( EXIT_FAILURE );
+ }
+ if( cvalue != NULL ) {
+ fprintf( stderr,
+ _("accountUsability: no control value expected\n") );
+ usage();
+ }
+
+ accountUsability = 1 + crit;
+#endif /* LDAP_CONTROL_X_ACCOUNT_USABILITY */
+
+ } else if ( tool_is_oid( control ) ) {
+ if ( c != NULL ) {
+ int i;
+ for ( i = 0; i < nctrls; i++ ) {
+ if ( strcmp( control, c[ i ].ldctl_oid ) == 0 ) {
+ fprintf( stderr, "%s control previously specified\n", control );
+ exit( EXIT_FAILURE );
+ }
+ }
+ }
+
+ if ( ctrl_add() ) {
+ exit( EXIT_FAILURE );
+ }
+
+ /* OID */
+ c[ nctrls - 1 ].ldctl_oid = control;
+
+ /* value */
+ if ( cvalue == NULL ) {
+ c[ nctrls - 1 ].ldctl_value.bv_val = NULL;
+ c[ nctrls - 1 ].ldctl_value.bv_len = 0;
+
+ } else if ( cvalue[ 0 ] == ':' ) {
+ struct berval type;
+ struct berval value;
+ int freeval;
+ char save_c;
+
+ cvalue++;
+
+ /* dummy type "x"
+ * to use ldif_parse_line2() */
+ save_c = cvalue[ -2 ];
+ cvalue[ -2 ] = 'x';
+ ldif_parse_line2( &cvalue[ -2 ], &type,
+ &value, &freeval );
+ cvalue[ -2 ] = save_c;
+
+ if ( freeval ) {
+ c[ nctrls - 1 ].ldctl_value = value;
+
+ } else {
+ ber_dupbv( &c[ nctrls - 1 ].ldctl_value, &value );
+ }
+
+ } else {
+ fprintf( stderr, "unable to parse %s control value\n", control );
+ exit( EXIT_FAILURE );
+
+ }
+
+ /* criticality */
+ c[ nctrls - 1 ].ldctl_iscritical = crit;
+
+ } else {
+ fprintf( stderr, _("Invalid search extension name: %s\n"),
+ control );
+ usage();
+ }
+ break;
+ case 'F': /* uri prefix */
+ if( urlpre ) free( urlpre );
+ urlpre = optarg;
+ break;
+ case 'l': /* time limit */
+ if ( strcasecmp( optarg, "none" ) == 0 ) {
+ timelimit = 0;
+
+ } else if ( strcasecmp( optarg, "max" ) == 0 ) {
+ timelimit = LDAP_MAXINT;
+
+ } else {
+ ival = strtol( optarg, &next, 10 );
+ if ( next == NULL || next[0] != '\0' ) {
+ fprintf( stderr,
+ _("Unable to parse time limit \"%s\"\n"), optarg );
+ exit( EXIT_FAILURE );
+ }
+ timelimit = ival;
+ }
+ if( timelimit < 0 || timelimit > LDAP_MAXINT ) {
+ fprintf( stderr, _("%s: invalid timelimit (%d) specified\n"),
+ prog, timelimit );
+ exit( EXIT_FAILURE );
+ }
+ break;
+ case 'L': /* print entries in LDIF format */
+ ++ldif;
+ break;
+ case 's': /* search scope */
+ if ( strncasecmp( optarg, "base", sizeof("base")-1 ) == 0 ) {
+ scope = LDAP_SCOPE_BASE;
+ } else if ( strncasecmp( optarg, "one", sizeof("one")-1 ) == 0 ) {
+ scope = LDAP_SCOPE_ONELEVEL;
+ } else if (( strcasecmp( optarg, "subordinate" ) == 0 )
+ || ( strcasecmp( optarg, "children" ) == 0 ))
+ {
+ scope = LDAP_SCOPE_SUBORDINATE;
+ } else if ( strncasecmp( optarg, "sub", sizeof("sub")-1 ) == 0 ) {
+ scope = LDAP_SCOPE_SUBTREE;
+ } else {
+ fprintf( stderr, _("scope should be base, one, or sub\n") );
+ usage();
+ }
+ break;
+ case 'S': /* sort attribute */
+ sortattr = optarg;
+ break;
+ case 't': /* write attribute values to TMPDIR files */
+ ++vals2tmp;
+ break;
+ case 'T': /* tmpdir */
+ if( tmpdir ) free( tmpdir );
+ tmpdir = optarg;
+ break;
+ case 'u': /* include UFN */
+ ++includeufn;
+ break;
+ case 'z': /* size limit */
+ if ( strcasecmp( optarg, "none" ) == 0 ) {
+ sizelimit = 0;
+
+ } else if ( strcasecmp( optarg, "max" ) == 0 ) {
+ sizelimit = LDAP_MAXINT;
+
+ } else {
+ ival = strtol( optarg, &next, 10 );
+ if ( next == NULL || next[0] != '\0' ) {
+ fprintf( stderr,
+ _("Unable to parse size limit \"%s\"\n"), optarg );
+ exit( EXIT_FAILURE );
+ }
+ sizelimit = ival;
+ }
+ if( sizelimit < 0 || sizelimit > LDAP_MAXINT ) {
+ fprintf( stderr, _("%s: invalid sizelimit (%d) specified\n"),
+ prog, sizelimit );
+ exit( EXIT_FAILURE );
+ }
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+
+static void
+private_conn_setup( LDAP *ld )
+{
+ if (deref != -1 &&
+ ldap_set_option( ld, LDAP_OPT_DEREF, (void *) &deref )
+ != LDAP_OPT_SUCCESS )
+ {
+ fprintf( stderr, _("Could not set LDAP_OPT_DEREF %d\n"), deref );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+}
+
+int
+main( int argc, char **argv )
+{
+ char *filtpattern, **attrs = NULL, line[BUFSIZ];
+ FILE *fp = NULL;
+ int rc, rc1, i, first;
+ LDAP *ld = NULL;
+ BerElement *seber = NULL, *vrber = NULL;
+
+ BerElement *syncber = NULL;
+ struct berval *syncbvalp = NULL;
+ int err;
+
+ tool_init( TOOL_SEARCH );
+
+ npagedresponses = npagedentries = npagedreferences =
+ npagedextended = npagedpartial = 0;
+
+ prog = lutil_progname( "ldapsearch", argc, argv );
+
+ if((def_tmpdir = getenv("TMPDIR")) == NULL &&
+ (def_tmpdir = getenv("TMP")) == NULL &&
+ (def_tmpdir = getenv("TEMP")) == NULL )
+ {
+ def_tmpdir = LDAP_TMPDIR;
+ }
+
+ if ( !*def_tmpdir )
+ def_tmpdir = LDAP_TMPDIR;
+
+ def_urlpre = malloc( sizeof("file:////") + strlen(def_tmpdir) );
+
+ if( def_urlpre == NULL ) {
+ perror( "malloc" );
+ return EXIT_FAILURE;
+ }
+
+ sprintf( def_urlpre, "file:///%s/",
+ def_tmpdir[0] == *LDAP_DIRSEP ? &def_tmpdir[1] : def_tmpdir );
+
+ urlize( def_urlpre );
+
+ tool_args( argc, argv );
+
+ if ( vlv && !sss ) {
+ fprintf( stderr,
+ _("VLV control requires server side sort control\n" ));
+ return EXIT_FAILURE;
+ }
+
+ if (( argc - optind < 1 ) ||
+ ( *argv[optind] != '(' /*')'*/ &&
+ ( strchr( argv[optind], '=' ) == NULL ) ) )
+ {
+ filtpattern = "(objectclass=*)";
+ } else {
+ filtpattern = argv[optind++];
+ }
+
+ if ( argv[optind] != NULL ) {
+ attrs = &argv[optind];
+ }
+
+ if ( infile != NULL ) {
+ int percent = 0;
+
+ if ( infile[0] == '-' && infile[1] == '\0' ) {
+ fp = stdin;
+ } else if (( fp = fopen( infile, "r" )) == NULL ) {
+ perror( infile );
+ return EXIT_FAILURE;
+ }
+
+ for( i=0 ; filtpattern[i] ; i++ ) {
+ if( filtpattern[i] == '%' ) {
+ if( percent ) {
+ fprintf( stderr, _("Bad filter pattern \"%s\"\n"),
+ filtpattern );
+ return EXIT_FAILURE;
+ }
+
+ percent++;
+
+ if( filtpattern[i+1] != 's' ) {
+ fprintf( stderr, _("Bad filter pattern \"%s\"\n"),
+ filtpattern );
+ return EXIT_FAILURE;
+ }
+ }
+ }
+ }
+
+ if ( tmpdir == NULL ) {
+ tmpdir = def_tmpdir;
+
+ if ( urlpre == NULL )
+ urlpre = def_urlpre;
+ }
+
+ if( urlpre == NULL ) {
+ urlpre = malloc( sizeof("file:////") + strlen(tmpdir) );
+
+ if( urlpre == NULL ) {
+ perror( "malloc" );
+ return EXIT_FAILURE;
+ }
+
+ sprintf( urlpre, "file:///%s/",
+ tmpdir[0] == *LDAP_DIRSEP ? &tmpdir[1] : tmpdir );
+
+ urlize( urlpre );
+ }
+
+ if ( debug )
+ ldif_debug = debug;
+
+ ld = tool_conn_setup( 0, &private_conn_setup );
+
+ tool_bind( ld );
+
+getNextPage:
+ /* fp may have been closed, need to reopen if code jumps
+ * back here to getNextPage.
+ */
+ if ( !fp && infile ) {
+ if (( fp = fopen( infile, "r" )) == NULL ) {
+ perror( infile );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+ }
+ save_nctrls = nctrls;
+ i = nctrls;
+ if ( nctrls > 0
+#ifdef LDAP_CONTROL_X_ACCOUNT_USABILITY
+ || accountUsability
+#endif
+#ifdef LDAP_CONTROL_DONTUSECOPY
+ || dontUseCopy
+#endif
+#ifdef LDAP_CONTROL_X_DEREF
+ || derefcrit
+#endif
+#ifdef LDAP_CONTROL_X_DIRSYNC
+ || dirSync
+#endif
+#ifdef LDAP_CONTROL_X_EXTENDED_DN
+ || extendedDn
+#endif
+#ifdef LDAP_CONTROL_X_SHOW_DELETED
+ || showDeleted
+#endif
+#ifdef LDAP_CONTROL_X_SERVER_NOTIFICATION
+ || serverNotif
+#endif
+ || domainScope
+ || pagedResults
+ || psearch
+ || ldapsync
+ || sss
+ || subentries
+ || valuesReturnFilter
+ || vlv )
+ {
+
+#ifdef LDAP_CONTROL_X_ACCOUNT_USABILITY
+ if ( accountUsability ) {
+ if ( ctrl_add() ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ c[i].ldctl_oid = LDAP_CONTROL_X_ACCOUNT_USABILITY;
+ c[i].ldctl_value.bv_val = NULL;
+ c[i].ldctl_value.bv_len = 0;
+ c[i].ldctl_iscritical = accountUsability == 2;
+ i++;
+ }
+#endif
+
+#ifdef LDAP_CONTROL_DONTUSECOPY
+ if ( dontUseCopy ) {
+ if ( ctrl_add() ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ c[i].ldctl_oid = LDAP_CONTROL_DONTUSECOPY;
+ c[i].ldctl_value.bv_val = NULL;
+ c[i].ldctl_value.bv_len = 0;
+ c[i].ldctl_iscritical = dontUseCopy == 2;
+ i++;
+ }
+#endif
+
+ if ( domainScope ) {
+ if ( ctrl_add() ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ c[i].ldctl_oid = LDAP_CONTROL_X_DOMAIN_SCOPE;
+ c[i].ldctl_value.bv_val = NULL;
+ c[i].ldctl_value.bv_len = 0;
+ c[i].ldctl_iscritical = domainScope > 1;
+ i++;
+ }
+
+ if ( subentries ) {
+ if ( ctrl_add() ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if (( seber = ber_alloc_t(LBER_USE_DER)) == NULL ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ err = ber_printf( seber, "b", abs(subentries) == 1 ? 0 : 1 );
+ if ( err == -1 ) {
+ ber_free( seber, 1 );
+ fprintf( stderr, _("Subentries control encoding error!\n") );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if ( ber_flatten2( seber, &c[i].ldctl_value, 0 ) == -1 ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ c[i].ldctl_oid = LDAP_CONTROL_SUBENTRIES;
+ c[i].ldctl_iscritical = subentries < 1;
+ i++;
+ }
+
+ if ( ldapsync ) {
+ if ( ctrl_add() ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if (( syncber = ber_alloc_t(LBER_USE_DER)) == NULL ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if ( sync_cookie.bv_len == 0 ) {
+ err = ber_printf( syncber, "{e}", abs(ldapsync) );
+ } else {
+ err = ber_printf( syncber, "{eO}", abs(ldapsync),
+ &sync_cookie );
+ }
+
+ if ( err == -1 ) {
+ ber_free( syncber, 1 );
+ fprintf( stderr, _("ldap sync control encoding error!\n") );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if ( ber_flatten( syncber, &syncbvalp ) == -1 ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ c[i].ldctl_oid = LDAP_CONTROL_SYNC;
+ c[i].ldctl_value = (*syncbvalp);
+ c[i].ldctl_iscritical = ldapsync < 0;
+ i++;
+ }
+
+ if ( valuesReturnFilter ) {
+ if ( ctrl_add() ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if (( vrber = ber_alloc_t(LBER_USE_DER)) == NULL ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if ( ( err = ldap_put_vrFilter( vrber, vrFilter ) ) == -1 ) {
+ ber_free( vrber, 1 );
+ fprintf( stderr, _("Bad ValuesReturnFilter: %s\n"), vrFilter );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if ( ber_flatten2( vrber, &c[i].ldctl_value, 0 ) == -1 ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ c[i].ldctl_oid = LDAP_CONTROL_VALUESRETURNFILTER;
+ c[i].ldctl_iscritical = valuesReturnFilter > 1;
+ i++;
+ }
+
+ if ( pagedResults ) {
+ if ( ctrl_add() ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if ( ldap_create_page_control_value( ld,
+ pageSize, &pr_cookie, &c[i].ldctl_value ) )
+ {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if ( pr_cookie.bv_val != NULL ) {
+ ber_memfree( pr_cookie.bv_val );
+ pr_cookie.bv_val = NULL;
+ pr_cookie.bv_len = 0;
+ }
+
+ c[i].ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
+ c[i].ldctl_iscritical = pagedResults > 1;
+ i++;
+ }
+
+ if ( psearch ) {
+ if ( ctrl_add() ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if ( ldap_create_persistentsearch_control_value( ld,
+ ps_chgtypes, ps_chgsonly, ps_echg_ctrls, &c[i].ldctl_value ) )
+ {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ c[i].ldctl_oid = LDAP_CONTROL_PERSIST_REQUEST;
+ c[i].ldctl_iscritical = psearch > 1;
+ i++;
+ }
+
+ if ( sss ) {
+ if ( ctrl_add() ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if ( ldap_create_sort_control_value( ld,
+ sss_keys, &c[i].ldctl_value ) )
+ {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ c[i].ldctl_oid = LDAP_CONTROL_SORTREQUEST;
+ c[i].ldctl_iscritical = sss > 1;
+ i++;
+ }
+
+ if ( vlv ) {
+ if ( ctrl_add() ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if ( ldap_create_vlv_control_value( ld,
+ &vlvInfo, &c[i].ldctl_value ) )
+ {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ c[i].ldctl_oid = LDAP_CONTROL_VLVREQUEST;
+ c[i].ldctl_iscritical = vlv > 1;
+ i++;
+ }
+#ifdef LDAP_CONTROL_X_DEREF
+ if ( derefcrit ) {
+ if ( derefval.bv_val == NULL ) {
+ int i;
+
+ assert( ds != NULL );
+
+ if ( ldap_create_deref_control_value( ld, ds, &derefval ) != LDAP_SUCCESS ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ for ( i = 0; ds[ i ].derefAttr != NULL; i++ ) {
+ ldap_memfree( ds[ i ].derefAttr );
+ ldap_charray_free( ds[ i ].attributes );
+ }
+ ldap_memfree( ds );
+ ds = NULL;
+ }
+
+ if ( ctrl_add() ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ c[ i ].ldctl_iscritical = derefcrit > 1;
+ c[ i ].ldctl_oid = LDAP_CONTROL_X_DEREF;
+ c[ i ].ldctl_value = derefval;
+ i++;
+ }
+#endif /* LDAP_CONTROL_X_DEREF */
+#ifdef LDAP_CONTROL_X_DIRSYNC
+ if ( dirSync ) {
+ if ( ctrl_add() ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if ( ldap_create_dirsync_value( ld,
+ dirSyncFlags, dirSyncMaxAttrCount, &dirSyncCookie,
+ &c[i].ldctl_value ) )
+ {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ c[i].ldctl_oid = LDAP_CONTROL_X_DIRSYNC;
+ c[i].ldctl_iscritical = dirSync > 1;
+ i++;
+ }
+#endif
+#ifdef LDAP_CONTROL_X_EXTENDED_DN
+ if ( extendedDn ) {
+ if ( ctrl_add() ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if ( ldap_create_extended_dn_value( ld,
+ extendedDnFlag, &c[i].ldctl_value ) )
+ {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ c[i].ldctl_oid = LDAP_CONTROL_X_EXTENDED_DN;
+ c[i].ldctl_iscritical = extendedDn > 1;
+ i++;
+ }
+#endif
+#ifdef LDAP_CONTROL_X_SHOW_DELETED
+ if ( showDeleted ) {
+ if ( ctrl_add() ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ c[i].ldctl_oid = LDAP_CONTROL_X_SHOW_DELETED;
+ c[i].ldctl_value.bv_val = NULL;
+ c[i].ldctl_value.bv_len = 0;
+ c[i].ldctl_iscritical = showDeleted > 1;
+ i++;
+ }
+#endif
+#ifdef LDAP_CONTROL_X_SERVER_NOTIFICATION
+ if ( serverNotif ) {
+ if ( ctrl_add() ) {
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ c[i].ldctl_oid = LDAP_CONTROL_X_SERVER_NOTIFICATION;
+ c[i].ldctl_value.bv_val = NULL;
+ c[i].ldctl_value.bv_len = 0;
+ c[i].ldctl_iscritical = serverNotif > 1;
+ i++;
+ }
+#endif
+ }
+
+ tool_server_controls( ld, c, i );
+
+ if ( seber ) ber_free( seber, 1 );
+ if ( vrber ) ber_free( vrber, 1 );
+
+ /* step back to the original number of controls, so that
+ * those set while parsing args are preserved */
+ nctrls = save_nctrls;
+
+ if ( verbose ) {
+ fprintf( stderr, _("filter%s: %s\nrequesting: "),
+ infile != NULL ? _(" pattern") : "",
+ filtpattern );
+
+ if ( attrs == NULL ) {
+ fprintf( stderr, _("All userApplication attributes") );
+ } else {
+ for ( i = 0; attrs[ i ] != NULL; ++i ) {
+ fprintf( stderr, "%s ", attrs[ i ] );
+ }
+ }
+ fprintf( stderr, "\n" );
+ }
+
+ if ( ldif == 0 ) {
+ printf( _("# extended LDIF\n") );
+ } else if ( ldif < 3 ) {
+ printf( _("version: %d\n\n"), 1 );
+ }
+
+ if (ldif < 2 ) {
+ char *realbase = base;
+
+ if ( realbase == NULL ) {
+ ldap_get_option( ld, LDAP_OPT_DEFBASE, (void **)(char *)&realbase );
+ }
+
+ printf( "#\n" );
+ printf(_("# LDAPv%d\n"), protocol);
+ printf(_("# base <%s>%s with scope %s\n"),
+ realbase ? realbase : "",
+ ( realbase == NULL || realbase != base ) ? " (default)" : "",
+ ((scope == LDAP_SCOPE_BASE) ? "baseObject"
+ : ((scope == LDAP_SCOPE_ONELEVEL) ? "oneLevel"
+ : ((scope == LDAP_SCOPE_SUBORDINATE) ? "children"
+ : "subtree" ))));
+ printf(_("# filter%s: %s\n"), infile != NULL ? _(" pattern") : "",
+ filtpattern);
+ printf(_("# requesting: "));
+
+ if ( attrs == NULL ) {
+ printf( _("ALL") );
+ } else {
+ for ( i = 0; attrs[ i ] != NULL; ++i ) {
+ printf( "%s ", attrs[ i ] );
+ }
+ }
+
+ if ( manageDSAit ) {
+ printf(_("\n# with manageDSAit %scontrol"),
+ manageDSAit > 1 ? _("critical ") : "" );
+ }
+ if ( noop ) {
+ printf(_("\n# with noop %scontrol"),
+ noop > 1 ? _("critical ") : "" );
+ }
+ if ( subentries ) {
+ printf(_("\n# with subentries %scontrol: %s"),
+ subentries < 0 ? _("critical ") : "",
+ abs(subentries) == 1 ? "false" : "true" );
+ }
+ if ( valuesReturnFilter ) {
+ printf(_("\n# with valuesReturnFilter %scontrol: %s"),
+ valuesReturnFilter > 1 ? _("critical ") : "", vrFilter );
+ }
+ if ( pagedResults ) {
+ printf(_("\n# with pagedResults %scontrol: size=%d"),
+ (pagedResults > 1) ? _("critical ") : "",
+ pageSize );
+ }
+ if ( sss ) {
+ printf(_("\n# with server side sorting %scontrol"),
+ sss > 1 ? _("critical ") : "" );
+ }
+ if ( vlv ) {
+ printf(_("\n# with virtual list view %scontrol: %d/%d"),
+ vlv > 1 ? _("critical ") : "",
+ vlvInfo.ldvlv_before_count, vlvInfo.ldvlv_after_count);
+ if ( vlvInfo.ldvlv_attrvalue )
+ printf(":%s", vlvInfo.ldvlv_attrvalue->bv_val );
+ else
+ printf("/%d/%d", vlvInfo.ldvlv_offset, vlvInfo.ldvlv_count );
+ }
+#ifdef LDAP_CONTROL_X_DEREF
+ if ( derefcrit ) {
+ printf(_("\n# with dereference %scontrol"),
+ derefcrit > 1 ? _("critical ") : "" );
+ }
+#endif
+
+ printf( _("\n#\n\n") );
+
+ if ( realbase && realbase != base ) {
+ ldap_memfree( realbase );
+ }
+ }
+
+ if ( infile == NULL ) {
+ rc = dosearch( ld, base, scope, NULL, filtpattern,
+ attrs, attrsonly, NULL, NULL, NULL, sizelimit );
+
+ } else {
+ rc = 0;
+ first = 1;
+ while ( fgets( line, sizeof( line ), fp ) != NULL ) {
+ line[ strlen( line ) - 1 ] = '\0';
+ if ( !first ) {
+ putchar( '\n' );
+ } else {
+ first = 0;
+ }
+ rc1 = dosearch( ld, base, scope, filtpattern, line,
+ attrs, attrsonly, NULL, NULL, NULL, sizelimit );
+
+ if ( rc1 != 0 ) {
+ rc = rc1;
+ if ( !contoper )
+ break;
+ }
+ }
+ if ( fp != stdin ) {
+ fclose( fp );
+ fp = NULL;
+ }
+ }
+
+ if (( rc == LDAP_SUCCESS ) && pageSize && pr_morePagedResults ) {
+ char buf[12];
+ int i, moreEntries, tmpSize;
+
+ /* Loop to get the next pages when
+ * enter is pressed on the terminal.
+ */
+ if ( pagePrompt != 0 ) {
+ if ( entriesLeft > 0 ) {
+ printf( _("Estimate entries: %d\n"), entriesLeft );
+ }
+ printf( _("Press [size] Enter for the next {%d|size} entries.\n"),
+ (int)pageSize );
+ i = 0;
+ moreEntries = getchar();
+ while ( moreEntries != EOF && moreEntries != '\n' ) {
+ if ( i < (int)sizeof(buf) - 1 ) {
+ buf[i] = moreEntries;
+ i++;
+ }
+ moreEntries = getchar();
+ }
+ buf[i] = '\0';
+
+ if ( i > 0 && isdigit( (unsigned char)buf[0] ) ) {
+ int num = sscanf( buf, "%d", &tmpSize );
+ if ( num != 1 ) {
+ fprintf( stderr,
+ _("Invalid value for PagedResultsControl, %s.\n"), buf);
+ tool_exit( ld, EXIT_FAILURE );
+
+ }
+ pageSize = (ber_int_t)tmpSize;
+ }
+ }
+
+ goto getNextPage;
+ }
+
+ if (( rc == LDAP_SUCCESS ) && vlv ) {
+ char buf[BUFSIZ];
+ int i, moreEntries;
+
+ /* Loop to get the next window when
+ * enter is pressed on the terminal.
+ */
+ printf( _("Press [before/after(/offset/count|:value)] Enter for the next window.\n"));
+ i = 0;
+ moreEntries = getchar();
+ while ( moreEntries != EOF && moreEntries != '\n' ) {
+ if ( i < (int)sizeof(buf) - 1 ) {
+ buf[i] = moreEntries;
+ i++;
+ }
+ moreEntries = getchar();
+ }
+ buf[i] = '\0';
+ if ( buf[0] ) {
+ i = parse_vlv( strdup( buf ));
+ if ( i )
+ tool_exit( ld, EXIT_FAILURE );
+ } else {
+ vlvInfo.ldvlv_attrvalue = NULL;
+ vlvInfo.ldvlv_count = vlvCount;
+ vlvInfo.ldvlv_offset += vlvInfo.ldvlv_after_count;
+ }
+
+ if ( vlvInfo.ldvlv_context )
+ ber_bvfree( vlvInfo.ldvlv_context );
+ vlvInfo.ldvlv_context = vlvContext;
+
+ goto getNextPage;
+ }
+
+ if ( sss_keys != NULL ) {
+ ldap_free_sort_keylist( sss_keys );
+ }
+ if ( derefval.bv_val != NULL ) {
+ ldap_memfree( derefval.bv_val );
+ }
+ if ( urlpre != NULL ) {
+ if ( def_urlpre != urlpre )
+ free( def_urlpre );
+ free( urlpre );
+ }
+
+ if ( c ) {
+ for ( ; save_nctrls-- > 0; ) {
+ ber_memfree( c[ save_nctrls ].ldctl_value.bv_val );
+ }
+ free( c );
+ c = NULL;
+ }
+
+ tool_exit( ld, rc );
+}
+
+
+static int dosearch(
+ LDAP *ld,
+ char *base,
+ int scope,
+ char *filtpatt,
+ char *value,
+ char **attrs,
+ int attrsonly,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ struct timeval *timeout,
+ int sizelimit )
+{
+ char *filter;
+ int rc, rc2 = LDAP_OTHER;
+ int nresponses;
+ int nentries;
+ int nreferences;
+ int nextended;
+ int npartial;
+ LDAPMessage *res, *msg;
+ ber_int_t msgid;
+ char *retoid = NULL;
+ struct berval *retdata = NULL;
+ int nresponses_psearch = -1;
+ int cancel_msgid = -1;
+ struct timeval tv, *tvp = NULL;
+ struct timeval tv_timelimit, *tv_timelimitp = NULL;
+
+ if( filtpatt != NULL ) {
+ size_t max_fsize = strlen( filtpatt ) + strlen( value ) + 1, outlen;
+ filter = malloc( max_fsize );
+ if( filter == NULL ) {
+ perror( "malloc" );
+ return EXIT_FAILURE;
+ }
+
+ outlen = snprintf( filter, max_fsize, filtpatt, value );
+ if( outlen >= max_fsize ) {
+ fprintf( stderr, "Bad filter pattern: \"%s\"\n", filtpatt );
+ free( filter );
+ return EXIT_FAILURE;
+ }
+
+ if ( verbose ) {
+ fprintf( stderr, _("filter: %s\n"), filter );
+ }
+
+ if( ldif < 2 ) {
+ printf( _("#\n# filter: %s\n#\n"), filter );
+ }
+
+ } else {
+ filter = value;
+ }
+
+ if ( dont ) {
+ if ( filtpatt != NULL ) {
+ free( filter );
+ }
+ return LDAP_SUCCESS;
+ }
+
+ if ( timelimit > 0 ) {
+ tv_timelimit.tv_sec = timelimit;
+ tv_timelimit.tv_usec = 0;
+ tv_timelimitp = &tv_timelimit;
+ }
+
+again:
+ rc = ldap_search_ext( ld, base, scope, filter, attrs, attrsonly,
+ sctrls, cctrls, tv_timelimitp, sizelimit, &msgid );
+
+ if ( filtpatt != NULL ) {
+ free( filter );
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_search_ext", rc, NULL, NULL, NULL, NULL );
+ return( rc );
+ }
+
+ nresponses = nentries = nreferences = nextended = npartial = 0;
+
+ res = NULL;
+
+ if ( timelimit > 0 ) {
+ /* disable timeout */
+ tv.tv_sec = -1;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ }
+
+ if ( backlog == 1 ) {
+ printf( _("\nWaiting for responses to accumulate, press Enter to continue: "));
+ fflush( stdout );
+ getchar();
+ printf( _("Abandoning msgid %d\n"), msgid );
+ ldap_abandon_ext( ld, msgid, NULL, NULL );
+ /* turn off syncrepl control */
+ ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, NULL );
+ backlog = 2;
+ scope = LDAP_SCOPE_BASE;
+ goto again;
+ } else if ( backlog == 2 ) {
+ tv.tv_sec = timelimit;
+ }
+
+ while ((rc = ldap_result( ld, LDAP_RES_ANY,
+ sortattr ? LDAP_MSG_ALL : LDAP_MSG_ONE,
+ tvp, &res )) > 0 )
+ {
+ if ( tool_check_abandon( ld, msgid ) ) {
+ return -1;
+ }
+
+ if( sortattr ) {
+ (void) ldap_sort_entries( ld, &res,
+ ( *sortattr == '\0' ) ? NULL : sortattr, strcasecmp );
+ }
+
+ for ( msg = ldap_first_message( ld, res );
+ msg != NULL;
+ msg = ldap_next_message( ld, msg ) )
+ {
+ if ( nresponses++ ) putchar('\n');
+ if ( nresponses_psearch >= 0 )
+ nresponses_psearch++;
+
+ switch( ldap_msgtype( msg ) ) {
+ case LDAP_RES_SEARCH_ENTRY:
+ nentries++;
+ print_entry( ld, msg, attrsonly );
+ break;
+
+ case LDAP_RES_SEARCH_REFERENCE:
+ nreferences++;
+ print_reference( ld, msg );
+ break;
+
+ case LDAP_RES_EXTENDED:
+ nextended++;
+ print_extended( ld, msg );
+
+ if ( ldap_msgid( msg ) == 0 ) {
+ /* unsolicited extended operation */
+ goto done;
+ }
+
+ if ( cancel_msgid != -1 &&
+ cancel_msgid == ldap_msgid( msg ) ) {
+ printf(_("Cancelled \n"));
+ printf(_("cancel_msgid = %d\n"), cancel_msgid);
+ goto done;
+ }
+ break;
+
+ case LDAP_RES_SEARCH_RESULT:
+ /* pagedResults stuff is dealt with
+ * in tool_print_ctrls(), called by
+ * print_results(). */
+ rc2 = print_result( ld, msg, 1 );
+ if ( ldapsync == LDAP_SYNC_REFRESH_AND_PERSIST ) {
+ break;
+ }
+
+ goto done;
+
+ case LDAP_RES_INTERMEDIATE:
+ npartial++;
+ ldap_parse_intermediate( ld, msg,
+ &retoid, &retdata, NULL, 0 );
+
+ nresponses_psearch = 0;
+
+ if ( strcmp( retoid, LDAP_SYNC_INFO ) == 0 ) {
+ if ( ldif < 1 ) {
+ print_syncinfo( retdata );
+ } else if ( ldif < 2 ) {
+ printf(_("# SyncInfo Received\n"));
+ }
+ ldap_memfree( retoid );
+ ber_bvfree( retdata );
+ break;
+ }
+
+ print_partial( ld, msg );
+ ldap_memfree( retoid );
+ ber_bvfree( retdata );
+ goto done;
+ }
+
+ if ( ldapsync && sync_slimit != -1 &&
+ nresponses_psearch >= sync_slimit ) {
+ BerElement *msgidber = NULL;
+ struct berval msgidval;
+ msgidber = ber_alloc_t(LBER_USE_DER);
+ ber_printf(msgidber, "{i}", msgid);
+ ber_flatten2( msgidber, &msgidval, 0 );
+ ldap_extended_operation(ld, LDAP_EXOP_CANCEL,
+ &msgidval, NULL, NULL, &cancel_msgid);
+ ber_free( msgidber, 1 );
+ nresponses_psearch = -1;
+ }
+ }
+
+ ldap_msgfree( res );
+ fflush( stdout );
+ }
+
+done:
+ if ( tvp == NULL && rc != LDAP_RES_SEARCH_RESULT ) {
+ ldap_get_option( ld, LDAP_OPT_RESULT_CODE, (void *)&rc2 );
+ }
+
+ ldap_msgfree( res );
+
+ if ( pagedResults ) {
+ npagedresponses += nresponses;
+ npagedentries += nentries;
+ npagedextended += nextended;
+ npagedpartial += npartial;
+ npagedreferences += nreferences;
+ if ( ( pr_morePagedResults == 0 ) && ( ldif < 2 ) ) {
+ printf( _("\n# numResponses: %d\n"), npagedresponses );
+ if( npagedentries ) {
+ printf( _("# numEntries: %d\n"), npagedentries );
+ }
+ if( npagedextended ) {
+ printf( _("# numExtended: %d\n"), npagedextended );
+ }
+ if( npagedpartial ) {
+ printf( _("# numPartial: %d\n"), npagedpartial );
+ }
+ if( npagedreferences ) {
+ printf( _("# numReferences: %d\n"), npagedreferences );
+ }
+ }
+ } else if ( ldif < 2 ) {
+ printf( _("\n# numResponses: %d\n"), nresponses );
+ if( nentries ) printf( _("# numEntries: %d\n"), nentries );
+ if( nextended ) printf( _("# numExtended: %d\n"), nextended );
+ if( npartial ) printf( _("# numPartial: %d\n"), npartial );
+ if( nreferences ) printf( _("# numReferences: %d\n"), nreferences );
+ }
+
+ if ( rc != LDAP_RES_SEARCH_RESULT ) {
+ tool_perror( "ldap_result", rc2, NULL, NULL, NULL, NULL );
+ }
+
+ return( rc2 );
+}
+
+/* This is the proposed new way of doing things.
+ * It is more efficient, but the API is non-standard.
+ */
+static void
+print_entry(
+ LDAP *ld,
+ LDAPMessage *entry,
+ int attrsonly)
+{
+ char *ufn = NULL;
+ char tmpfname[ 256 ];
+ char url[ 256 ];
+ int i, rc;
+ BerElement *ber = NULL;
+ struct berval bv, *bvals, **bvp = &bvals;
+ LDAPControl **ctrls = NULL;
+ FILE *tmpfp;
+
+ rc = ldap_get_dn_ber( ld, entry, &ber, &bv );
+
+ if ( ldif < 2 ) {
+ ufn = ldap_dn2ufn( bv.bv_val );
+ tool_write_ldif( LDIF_PUT_COMMENT, NULL, ufn, ufn ? strlen( ufn ) : 0 );
+ }
+ tool_write_ldif( LDIF_PUT_VALUE, "dn", bv.bv_val, bv.bv_len );
+
+ rc = ldap_get_entry_controls( ld, entry, &ctrls );
+ if( rc != LDAP_SUCCESS ) {
+ fprintf(stderr, _("print_entry: %d\n"), rc );
+ tool_perror( "ldap_get_entry_controls", rc, NULL, NULL, NULL, NULL );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if( ctrls ) {
+ tool_print_ctrls( ld, ctrls );
+ ldap_controls_free( ctrls );
+ }
+
+ if ( includeufn ) {
+ if( ufn == NULL ) {
+ ufn = ldap_dn2ufn( bv.bv_val );
+ }
+ tool_write_ldif( LDIF_PUT_VALUE, "ufn", ufn, ufn ? strlen( ufn ) : 0 );
+ }
+
+ if( ufn != NULL ) ldap_memfree( ufn );
+
+ if ( attrsonly ) bvp = NULL;
+
+ for ( rc = ldap_get_attribute_ber( ld, entry, ber, &bv, bvp );
+ rc == LDAP_SUCCESS;
+ rc = ldap_get_attribute_ber( ld, entry, ber, &bv, bvp ) )
+ {
+ if (bv.bv_val == NULL) break;
+
+ if ( attrsonly ) {
+ tool_write_ldif( LDIF_PUT_NOVALUE, bv.bv_val, NULL, 0 );
+
+ } else if ( bvals ) {
+ for ( i = 0; bvals[i].bv_val != NULL; i++ ) {
+ if ( vals2tmp > 1 || ( vals2tmp &&
+ ldif_is_not_printable( bvals[i].bv_val, bvals[i].bv_len )))
+ {
+ int tmpfd;
+ /* write value to file */
+ snprintf( tmpfname, sizeof tmpfname,
+ "%s" LDAP_DIRSEP "ldapsearch-%s-XXXXXX",
+ tmpdir, bv.bv_val );
+ tmpfp = NULL;
+
+ tmpfd = mkstemp( tmpfname );
+
+ if ( tmpfd < 0 ) {
+ perror( tmpfname );
+ continue;
+ }
+
+ if (( tmpfp = fdopen( tmpfd, "w")) == NULL ) {
+ perror( tmpfname );
+ continue;
+ }
+
+ if ( fwrite( bvals[ i ].bv_val,
+ bvals[ i ].bv_len, 1, tmpfp ) == 0 )
+ {
+ perror( tmpfname );
+ fclose( tmpfp );
+ continue;
+ }
+
+ fclose( tmpfp );
+
+ snprintf( url, sizeof url, "%s%s", urlpre,
+ &tmpfname[strlen(tmpdir) + sizeof(LDAP_DIRSEP) - 1] );
+
+ urlize( url );
+ tool_write_ldif( LDIF_PUT_URL, bv.bv_val, url, strlen( url ));
+
+ } else {
+ tool_write_ldif( LDIF_PUT_VALUE, bv.bv_val,
+ bvals[ i ].bv_val, bvals[ i ].bv_len );
+ }
+ }
+ ber_memfree( bvals );
+ }
+ }
+
+ if( ber != NULL ) {
+ ber_free( ber, 0 );
+ }
+}
+
+static void print_reference(
+ LDAP *ld,
+ LDAPMessage *reference )
+{
+ int rc;
+ char **refs = NULL;
+ LDAPControl **ctrls;
+
+ if( ldif < 2 ) {
+ printf(_("# search reference\n"));
+ }
+
+ rc = ldap_parse_reference( ld, reference, &refs, &ctrls, 0 );
+
+ if( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_parse_reference", rc, NULL, NULL, NULL, NULL );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if( refs ) {
+ int i;
+ for( i=0; refs[i] != NULL; i++ ) {
+ tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
+ "ref", refs[i], strlen(refs[i]) );
+ }
+ ber_memvfree( (void **) refs );
+ }
+
+ if( ctrls ) {
+ tool_print_ctrls( ld, ctrls );
+ ldap_controls_free( ctrls );
+ }
+}
+
+static void print_extended(
+ LDAP *ld,
+ LDAPMessage *extended )
+{
+ int rc;
+ char *retoid = NULL;
+ struct berval *retdata = NULL;
+
+ if( ldif < 2 ) {
+ printf(_("# extended result response\n"));
+ }
+
+ rc = ldap_parse_extended_result( ld, extended,
+ &retoid, &retdata, 0 );
+
+ if( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_parse_extended_result", rc, NULL, NULL, NULL, NULL );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if ( ldif < 2 ) {
+ tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
+ "extended", retoid, retoid ? strlen(retoid) : 0 );
+ }
+ ber_memfree( retoid );
+
+ if(retdata) {
+ if ( ldif < 2 ) {
+ tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_BINARY,
+ "data", retdata->bv_val, retdata->bv_len );
+ }
+ ber_bvfree( retdata );
+ }
+
+ print_result( ld, extended, 0 );
+}
+
+static void print_syncinfo(
+ BerValue *data )
+{
+ BerElement *syncinfo;
+ struct berval bv, cookie;
+ ber_tag_t tag;
+ ber_len_t len;
+
+ if ( (syncinfo = ber_alloc()) == NULL ) {
+ return;
+ }
+ ber_init2( syncinfo, data, 0 );
+
+ printf(_("# SyncInfo Received: "));
+ tag = ber_peek_tag( syncinfo, &len );
+ switch (tag) {
+ case LDAP_TAG_SYNC_NEW_COOKIE: {
+ printf(_("new cookie\n"));
+ ber_scanf( syncinfo, "m", &cookie );
+
+ if ( ldif_is_not_printable( cookie.bv_val, cookie.bv_len ) ) {
+ bv.bv_len = LUTIL_BASE64_ENCODE_LEN(
+ cookie.bv_len ) + 1;
+ bv.bv_val = ber_memalloc( bv.bv_len + 1 );
+
+ bv.bv_len = lutil_b64_ntop(
+ (unsigned char *) cookie.bv_val,
+ cookie.bv_len,
+ bv.bv_val, bv.bv_len );
+
+ printf(_("# cookie:: %s\n"), bv.bv_val );
+ ber_memfree( bv.bv_val );
+ } else {
+ printf(_("# cookie: %s\n"), cookie.bv_val );
+ }
+ } break;
+ case LDAP_TAG_SYNC_REFRESH_DELETE: {
+ ber_int_t done = 1;
+
+ printf(_("refresh delete\n"));
+ /* Skip sequence tag first */
+ ber_skip_tag( syncinfo, &len );
+
+ tag = ber_peek_tag( syncinfo, &len );
+ if ( tag == LDAP_TAG_SYNC_COOKIE ) {
+ ber_scanf( syncinfo, "m", &cookie );
+
+ if ( ldif_is_not_printable( cookie.bv_val, cookie.bv_len ) ) {
+ bv.bv_len = LUTIL_BASE64_ENCODE_LEN(
+ cookie.bv_len ) + 1;
+ bv.bv_val = ber_memalloc( bv.bv_len + 1 );
+
+ bv.bv_len = lutil_b64_ntop(
+ (unsigned char *) cookie.bv_val,
+ cookie.bv_len,
+ bv.bv_val, bv.bv_len );
+
+ printf(_("# cookie:: %s\n"), bv.bv_val );
+ ber_memfree( bv.bv_val );
+ } else {
+ printf(_("# cookie: %s\n"), cookie.bv_val );
+ }
+
+ tag = ber_peek_tag( syncinfo, &len );
+ }
+ if ( tag == LDAP_TAG_REFRESHDONE ) {
+ ber_get_boolean( syncinfo, &done );
+ }
+ if ( done )
+ printf(_("# refresh done, switching to persist stage\n"));
+ } break;
+ case LDAP_TAG_SYNC_REFRESH_PRESENT: {
+ ber_int_t done = 1;
+
+ printf(_("refresh present\n"));
+ /* Skip sequence tag first */
+ ber_skip_tag( syncinfo, &len );
+
+ tag = ber_peek_tag( syncinfo, &len );
+ if ( tag == LDAP_TAG_SYNC_COOKIE ) {
+ ber_scanf( syncinfo, "m", &cookie );
+
+ if ( ldif_is_not_printable( cookie.bv_val, cookie.bv_len ) ) {
+ bv.bv_len = LUTIL_BASE64_ENCODE_LEN(
+ cookie.bv_len ) + 1;
+ bv.bv_val = ber_memalloc( bv.bv_len + 1 );
+
+ bv.bv_len = lutil_b64_ntop(
+ (unsigned char *) cookie.bv_val,
+ cookie.bv_len,
+ bv.bv_val, bv.bv_len );
+
+ printf(_("# cookie:: %s\n"), bv.bv_val );
+ ber_memfree( bv.bv_val );
+ } else {
+ printf(_("# cookie: %s\n"), cookie.bv_val );
+ }
+
+ tag = ber_peek_tag( syncinfo, &len );
+ }
+ if ( tag == LDAP_TAG_REFRESHDONE ) {
+ ber_get_boolean( syncinfo, &done );
+ }
+ if ( done )
+ printf(_("# refresh done, switching to persist stage\n"));
+ } break;
+ case LDAP_TAG_SYNC_ID_SET: {
+ ber_int_t refreshDeletes = 0;
+ BerVarray uuids;
+
+ printf(_("ID Set\n"));
+ /* Skip sequence tag first */
+ ber_skip_tag( syncinfo, &len );
+
+ tag = ber_peek_tag( syncinfo, &len );
+ if ( tag == LDAP_TAG_SYNC_COOKIE ) {
+ ber_scanf( syncinfo, "m", &cookie );
+
+ if ( ldif_is_not_printable( cookie.bv_val, cookie.bv_len ) ) {
+ bv.bv_len = LUTIL_BASE64_ENCODE_LEN(
+ cookie.bv_len ) + 1;
+ bv.bv_val = ber_memalloc( bv.bv_len + 1 );
+
+ bv.bv_len = lutil_b64_ntop(
+ (unsigned char *) cookie.bv_val,
+ cookie.bv_len,
+ bv.bv_val, bv.bv_len );
+
+ printf(_("# cookie:: %s\n"), bv.bv_val );
+ ber_memfree( bv.bv_val );
+ } else {
+ printf(_("# cookie: %s\n"), cookie.bv_val );
+ }
+
+ tag = ber_peek_tag( syncinfo, &len );
+ }
+ if ( tag == LDAP_TAG_REFRESHDELETES ) {
+ ber_get_boolean( syncinfo, &refreshDeletes );
+ tag = ber_peek_tag( syncinfo, &len );
+ }
+ if ( refreshDeletes ) {
+ printf(_("# following UUIDs no longer match the search\n"));
+ }
+
+ printf(_("# syncUUIDs:\n"));
+ ber_scanf( syncinfo, "[W]", &uuids );
+ if ( uuids ) {
+ char buf[LDAP_LUTIL_UUIDSTR_BUFSIZE];
+ int i;
+
+ for ( i=0; !BER_BVISNULL( &uuids[i] ); i++ ) {
+ int rc = lutil_uuidstr_from_normalized(
+ uuids[i].bv_val, uuids[i].bv_len,
+ buf, LDAP_LUTIL_UUIDSTR_BUFSIZE );
+ if ( rc <= 0 || rc >= LDAP_LUTIL_UUIDSTR_BUFSIZE ) {
+ printf(_("#\t(UUID malformed)\n"));
+ } else {
+ printf(_("#\t%s\n"), buf);
+ }
+ }
+ ber_bvarray_free( uuids );
+ }
+ } break;
+ case LBER_DEFAULT:
+ printf(_("empty SyncInfoValue\n"));
+ default:
+ printf(_("SyncInfoValue unknown\n"));
+ break;
+ }
+ ber_free( syncinfo, 0 );
+}
+
+static void print_partial(
+ LDAP *ld,
+ LDAPMessage *partial )
+{
+ int rc;
+ char *retoid = NULL;
+ struct berval *retdata = NULL;
+ LDAPControl **ctrls = NULL;
+
+ if( ldif < 2 ) {
+ printf(_("# extended partial response\n"));
+ }
+
+ rc = ldap_parse_intermediate( ld, partial,
+ &retoid, &retdata, &ctrls, 0 );
+
+ if( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_parse_intermediate", rc, NULL, NULL, NULL, NULL );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+ if ( ldif < 2 ) {
+ tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_VALUE,
+ "partial", retoid, retoid ? strlen(retoid) : 0 );
+ }
+
+ ber_memfree( retoid );
+
+ if( retdata ) {
+ if ( ldif < 2 ) {
+ tool_write_ldif( ldif ? LDIF_PUT_COMMENT : LDIF_PUT_BINARY,
+ "data", retdata->bv_val, retdata->bv_len );
+ }
+
+ ber_bvfree( retdata );
+ }
+
+ if( ctrls ) {
+ tool_print_ctrls( ld, ctrls );
+ ldap_controls_free( ctrls );
+ }
+}
+
+static int print_result(
+ LDAP *ld,
+ LDAPMessage *result, int search )
+{
+ int rc;
+ int err;
+ char *matcheddn = NULL;
+ char *text = NULL;
+ char **refs = NULL;
+ LDAPControl **ctrls = NULL;
+
+ if( search ) {
+ if ( ldif < 2 ) {
+ printf(_("# search result\n"));
+ }
+ if ( ldif < 1 ) {
+ printf("%s: %d\n", _("search"), ldap_msgid(result) );
+ }
+ }
+
+ rc = ldap_parse_result( ld, result,
+ &err, &matcheddn, &text, &refs, &ctrls, 0 );
+
+ if( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_parse_result", rc, NULL, NULL, NULL, NULL );
+ tool_exit( ld, EXIT_FAILURE );
+ }
+
+
+ if( !ldif ) {
+ printf( _("result: %d %s\n"), err, ldap_err2string(err) );
+
+ } else if ( err != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s (%d)\n", ldap_err2string(err), err );
+ }
+
+ if( matcheddn ) {
+ if( *matcheddn ) {
+ if( !ldif ) {
+ tool_write_ldif( LDIF_PUT_VALUE,
+ "matchedDN", matcheddn, strlen(matcheddn) );
+ } else {
+ fprintf( stderr, _("Matched DN: %s\n"), matcheddn );
+ }
+ }
+
+ ber_memfree( matcheddn );
+ }
+
+ if( text ) {
+ if( *text ) {
+ if( !ldif ) {
+ if ( err == LDAP_PARTIAL_RESULTS ) {
+ char *line;
+
+ for ( line = text; line != NULL; ) {
+ char *next = strchr( line, '\n' );
+
+ tool_write_ldif( LDIF_PUT_TEXT,
+ "text", line,
+ next ? (size_t) (next - line) : strlen( line ));
+
+ line = next ? next + 1 : NULL;
+ }
+
+ } else {
+ tool_write_ldif( LDIF_PUT_TEXT, "text",
+ text, strlen(text) );
+ }
+ } else {
+ fprintf( stderr, _("Additional information: %s\n"), text );
+ }
+ }
+
+ ber_memfree( text );
+ }
+
+ if( refs ) {
+ int i;
+ for( i=0; refs[i] != NULL; i++ ) {
+ if( !ldif ) {
+ tool_write_ldif( LDIF_PUT_VALUE, "ref", refs[i], strlen(refs[i]) );
+ } else {
+ fprintf( stderr, _("Referral: %s\n"), refs[i] );
+ }
+ }
+
+ ber_memvfree( (void **) refs );
+ }
+
+ pr_morePagedResults = 0;
+
+ if( ctrls ) {
+ tool_print_ctrls( ld, ctrls );
+ ldap_controls_free( ctrls );
+ }
+
+ return err;
+}
diff --git a/clients/tools/ldapurl.c b/clients/tools/ldapurl.c
new file mode 100644
index 0000000..efc4896
--- /dev/null
+++ b/clients/tools/ldapurl.c
@@ -0,0 +1,306 @@
+/* ldapurl -- a tool for generating LDAP URLs */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 Pierangelo Masarati, SysNet
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission. This
+ * software is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by Pierangelo Masarati
+ * for inclusion in OpenLDAP software.
+ */
+
+#include "portable.h"
+
+#include <ac/stdlib.h>
+#include <stdio.h>
+#include <ac/unistd.h>
+#include <ac/socket.h>
+
+#include "ldap.h"
+#include "ldap_pvt.h"
+#include "lutil.h"
+
+static int
+usage(void)
+{
+ fprintf( stderr, _("usage: %s [options]\n\n"), "ldapurl" );
+ fprintf( stderr, _("generates RFC 4516 LDAP URL with extensions\n\n" ) );
+ fprintf( stderr, _("URL options:\n"));
+ fprintf( stderr, _(" -a attrs comma separated list of attributes\n" ) );
+ fprintf( stderr, _(" -b base (RFC 4514 LDAP DN)\n" ) );
+ fprintf( stderr, _(" -E ext (format: \"ext=value\"; multiple occurrences allowed)\n" ) );
+ fprintf( stderr, _(" -f filter (RFC 4515 LDAP filter)\n" ) );
+ fprintf( stderr, _(" -h host \n" ) );
+ fprintf( stderr, _(" -p port (default: 389 for ldap, 636 for ldaps)\n" ) );
+ fprintf( stderr, _(" -s scope (RFC 4511 searchScope and extensions)\n" ) );
+ fprintf( stderr, _(" -S scheme (RFC 4516 LDAP URL scheme and extensions)\n" ) );
+ exit( EXIT_FAILURE );
+}
+
+static int
+do_uri_create( LDAPURLDesc *lud )
+{
+ char *uri;
+
+ if ( lud->lud_scheme == NULL ) {
+ lud->lud_scheme = "ldap";
+ }
+
+ if ( lud->lud_port == -1 ) {
+ if ( strcasecmp( lud->lud_scheme, "ldap" ) == 0 ) {
+ lud->lud_port = LDAP_PORT;
+
+ } else if ( strcasecmp( lud->lud_scheme, "ldaps" ) == 0 ) {
+ lud->lud_port = LDAPS_PORT;
+
+ } else if ( strcasecmp( lud->lud_scheme, "ldapi" ) == 0 ) {
+ lud->lud_port = 0;
+
+ } else {
+ /* forgiving... */
+ lud->lud_port = 0;
+ }
+ }
+
+ if ( lud->lud_scope == -1 ) {
+ lud->lud_scope = LDAP_SCOPE_DEFAULT;
+ }
+
+ uri = ldap_url_desc2str( lud );
+
+ if ( lud->lud_attrs != NULL ) {
+ ldap_charray_free( lud->lud_attrs );
+ lud->lud_attrs = NULL;
+ }
+
+ if ( lud->lud_exts != NULL ) {
+ free( lud->lud_exts );
+ lud->lud_exts = NULL;
+ }
+
+ if ( uri == NULL ) {
+ fprintf( stderr, "unable to generate URI\n" );
+ exit( EXIT_FAILURE );
+ }
+
+ printf( "%s\n", uri );
+ free( uri );
+
+ return 0;
+}
+
+static int
+do_uri_explode( const char *uri )
+{
+ LDAPURLDesc *lud;
+ int rc;
+
+ rc = ldap_url_parse( uri, &lud );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ fprintf( stderr, "unable to parse URI \"%s\"\n", uri );
+ return 1;
+ }
+
+ if ( lud->lud_scheme != NULL && lud->lud_scheme[0] != '\0' ) {
+ printf( "scheme: %s\n", lud->lud_scheme );
+ }
+
+ if ( lud->lud_host != NULL && lud->lud_host[0] != '\0' ) {
+ printf( "host: %s\n", lud->lud_host );
+ }
+
+ if ( lud->lud_port != 0 ) {
+ printf( "port: %d\n", lud->lud_port );
+ }
+
+ if ( lud->lud_dn != NULL && lud->lud_dn[0] != '\0' ) {
+ printf( "dn: %s\n", lud->lud_dn );
+ }
+
+ if ( lud->lud_attrs != NULL ) {
+ int i;
+
+ for ( i = 0; lud->lud_attrs[i] != NULL; i++ ) {
+ printf( "selector: %s\n", lud->lud_attrs[i] );
+ }
+ }
+
+ if ( lud->lud_scope != LDAP_SCOPE_DEFAULT ) {
+ printf( "scope: %s\n", ldap_pvt_scope2str( lud->lud_scope ) );
+ }
+
+ if ( lud->lud_filter != NULL && lud->lud_filter[0] != '\0' ) {
+ printf( "filter: %s\n", lud->lud_filter );
+ }
+
+ if ( lud->lud_exts != NULL ) {
+ int i;
+
+ for ( i = 0; lud->lud_exts[i] != NULL; i++ ) {
+ printf( "extension: %s\n", lud->lud_exts[i] );
+ }
+ }
+ ldap_free_urldesc( lud );
+
+ return 0;
+}
+
+int
+main( int argc, char *argv[])
+{
+ LDAPURLDesc lud = { 0 };
+ char *uri = NULL;
+ int gotlud = 0;
+ int nexts = 0;
+
+ lud.lud_port = -1;
+ lud.lud_scope = -1;
+
+ while ( 1 ) {
+ int opt = getopt( argc, argv, "S:h:p:b:a:s:f:E:H:" );
+
+ if ( opt == EOF ) {
+ break;
+ }
+
+ if ( opt == 'H' ) {
+ if ( gotlud ) {
+ fprintf( stderr, "option -H incompatible with previous options\n" );
+ usage();
+ }
+
+ if ( uri != NULL ) {
+ fprintf( stderr, "URI already provided\n" );
+ usage();
+ }
+
+ uri = optarg;
+ continue;
+ }
+
+ switch ( opt ) {
+ case 'S':
+ case 'h':
+ case 'p':
+ case 'b':
+ case 'a':
+ case 's':
+ case 'f':
+ case 'E':
+ if ( uri != NULL ) {
+ fprintf( stderr, "option -%c incompatible with -H\n", opt );
+ usage();
+ }
+ gotlud++;
+ }
+
+ switch ( opt ) {
+ case 'S':
+ if ( lud.lud_scheme != NULL ) {
+ fprintf( stderr, "scheme already provided\n" );
+ usage();
+ }
+ lud.lud_scheme = optarg;
+ break;
+
+ case 'h':
+ if ( lud.lud_host != NULL ) {
+ fprintf( stderr, "host already provided\n" );
+ usage();
+ }
+ lud.lud_host = optarg;
+ break;
+
+ case 'p':
+ if ( lud.lud_port != -1 ) {
+ fprintf( stderr, "port already provided\n" );
+ usage();
+ }
+
+ if ( lutil_atoi( &lud.lud_port, optarg ) ) {
+ fprintf( stderr, "unable to parse port \"%s\"\n", optarg );
+ usage();
+ }
+ break;
+
+ case 'b':
+ if ( lud.lud_dn != NULL ) {
+ fprintf( stderr, "base already provided\n" );
+ usage();
+ }
+ lud.lud_dn = optarg;
+ break;
+
+ case 'a':
+ if ( lud.lud_attrs != NULL ) {
+ fprintf( stderr, "attrs already provided\n" );
+ usage();
+ }
+ lud.lud_attrs = ldap_str2charray( optarg, "," );
+ if ( lud.lud_attrs == NULL ) {
+ fprintf( stderr, "unable to parse attrs list \"%s\"\n", optarg );
+ usage();
+ }
+ break;
+
+ case 's':
+ if ( lud.lud_scope != -1 ) {
+ fprintf( stderr, "scope already provided\n" );
+ usage();
+ }
+
+ lud.lud_scope = ldap_pvt_str2scope( optarg );
+ if ( lud.lud_scope == -1 ) {
+ fprintf( stderr, "unable to parse scope \"%s\"\n", optarg );
+ usage();
+ }
+ break;
+
+ case 'f':
+ if ( lud.lud_filter != NULL ) {
+ fprintf( stderr, "filter already provided\n" );
+ usage();
+ }
+ lud.lud_filter = optarg;
+ break;
+
+ case 'E':
+ lud.lud_exts = (char **)realloc( lud.lud_exts,
+ sizeof( char * ) * ( nexts + 2 ) );
+ lud.lud_exts[ nexts++ ] = optarg;
+ lud.lud_exts[ nexts ] = NULL;
+ break;
+
+ default:
+ assert( opt != 'H' );
+ usage();
+ }
+ }
+
+ if ( uri != NULL ) {
+ return do_uri_explode( uri );
+
+ }
+
+ return do_uri_create( &lud );
+}
diff --git a/clients/tools/ldapvc.c b/clients/tools/ldapvc.c
new file mode 100644
index 0000000..8465618
--- /dev/null
+++ b/clients/tools/ldapvc.c
@@ -0,0 +1,506 @@
+/* ldapvc.c -- a tool for verifying credentials */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2010 Kurt D. Zeilenga.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission. This
+ * software is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by Kurt D. Zeilenga for inclusion
+ * in OpenLDAP Software based, in part, on other client tools.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include <ldap.h>
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "ldap_defaults.h"
+
+#include "common.h"
+
+static int req_authzid = 0;
+static int req_pp = 0;
+
+#if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL)
+#define LDAP_SASL_NONE (~0U)
+static unsigned vc_sasl = LDAP_SASL_NONE;
+static char *vc_sasl_realm = NULL;
+static char *vc_sasl_authcid = NULL;
+static char *vc_sasl_authzid = NULL;
+static char *vc_sasl_mech = NULL;
+static char *vc_sasl_secprops = NULL;
+#endif
+static char * dn = NULL;
+static struct berval cred = {0, NULL};
+
+void
+usage( void )
+{
+ fprintf( stderr, _("Issue LDAP Verify Credentials operation to verify a user's credentials\n\n"));
+ fprintf( stderr, _("usage: %s [options] [DN [cred]])\n"), prog);
+ fprintf( stderr, _("where:\n"));
+ fprintf( stderr, _(" DN\tDistinguished Name\n"));
+ fprintf( stderr, _(" cred\tCredentials (prompt if not present)\n"));
+ fprintf( stderr, _("options:\n"));
+ fprintf( stderr, _(" -a\tRequest AuthzId\n"));
+ fprintf( stderr, _(" -b\tRequest Password Policy Information\n"));
+ fprintf( stderr, _(" -E sasl=(a[utomatic]|i[nteractive]|q[uiet]>\tSASL mode (defaults to automatic if any other -E option provided, otherwise none))\n"));
+ fprintf( stderr, _(" -E mech=<mech>\tSASL mechanism (default "" e.g. Simple)\n"));
+ fprintf( stderr, _(" -E realm=<realm>\tSASL Realm (defaults to none)\n"));
+ fprintf( stderr, _(" -E authcid=<authcid>\tSASL Authentication Identity (defaults to USER)\n"));
+ fprintf( stderr, _(" -E authzid=<authzid>\tSASL Authorization Identity (defaults to none)\n"));
+ fprintf( stderr, _(" -E secprops=<secprops>\tSASL Security Properties (defaults to none)\n"));
+ tool_common_usage();
+ exit( EXIT_FAILURE );
+}
+
+
+const char options[] = "abE:"
+ "d:D:e:h:H:InNO:o:p:QR:U:vVw:WxX:y:Y:Z";
+
+int
+handle_private_option( int i )
+{
+ switch ( i ) {
+ char *control, *cvalue;
+ case 'E': /* vc extension */
+ if( protocol == LDAP_VERSION2 ) {
+ fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"),
+ prog, protocol );
+ exit( EXIT_FAILURE );
+ }
+
+ /* should be extended to support comma separated list of
+ * [!]key[=value] parameters, e.g. -E !foo,bar=567
+ */
+
+ cvalue = NULL;
+ if( optarg[0] == '!' ) {
+ optarg++;
+ }
+
+ control = optarg;
+ if ( (cvalue = strchr( control, '=' )) != NULL ) {
+ *cvalue++ = '\0';
+ }
+
+ if (strcasecmp(control, "sasl") == 0) {
+#if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL)
+ if (vc_sasl != LDAP_SASL_NONE) {
+ fprintf(stderr,
+ _("SASL option previously specified\n"));
+ exit(EXIT_FAILURE);
+ }
+ if (cvalue == NULL) {
+ fprintf(stderr,
+ _("missing mode in SASL option\n"));
+ exit(EXIT_FAILURE);
+ }
+
+ switch (*cvalue) {
+ case 'a':
+ case 'A':
+ vc_sasl = LDAP_SASL_AUTOMATIC;
+ break;
+ case 'i':
+ case 'I':
+ vc_sasl = LDAP_SASL_INTERACTIVE;
+ break;
+ case 'q':
+ case 'Q':
+ vc_sasl = LDAP_SASL_QUIET;
+ break;
+ default:
+ fprintf(stderr,
+ _("unknown mode %s in SASL option\n"), cvalue);
+ exit(EXIT_FAILURE);
+ }
+#else
+ fprintf(stderr,
+ _("%s: not compiled with SASL support\n"), prog);
+ exit(EXIT_FAILURE);
+#endif
+
+ } else if (strcasecmp(control, "mech") == 0) {
+#if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL)
+ if (vc_sasl_mech) {
+ fprintf(stderr,
+ _("SASL mech previously specified\n"));
+ exit(EXIT_FAILURE);
+ }
+ if (cvalue == NULL) {
+ fprintf(stderr,
+ _("missing mech in SASL option\n"));
+ exit(EXIT_FAILURE);
+ }
+
+ vc_sasl_mech = ber_strdup(cvalue);
+#else
+#endif
+
+ } else if (strcasecmp(control, "realm") == 0) {
+#if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL)
+ if (vc_sasl_realm) {
+ fprintf(stderr,
+ _("SASL realm previously specified\n"));
+ exit(EXIT_FAILURE);
+ }
+ if (cvalue == NULL) {
+ fprintf(stderr,
+ _("missing realm in SASL option\n"));
+ exit(EXIT_FAILURE);
+ }
+
+ vc_sasl_realm = ber_strdup(cvalue);
+#else
+ fprintf(stderr,
+ _("%s: not compiled with SASL support\n"), prog);
+ exit(EXIT_FAILURE);
+#endif
+
+ } else if (strcasecmp(control, "authcid") == 0) {
+#if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL)
+ if (vc_sasl_authcid) {
+ fprintf(stderr,
+ _("SASL authcid previously specified\n"));
+ exit(EXIT_FAILURE);
+ }
+ if (cvalue == NULL) {
+ fprintf(stderr,
+ _("missing authcid in SASL option\n"));
+ exit(EXIT_FAILURE);
+ }
+
+ vc_sasl_authcid = ber_strdup(cvalue);
+#else
+ fprintf(stderr,
+ _("%s: not compiled with SASL support\n"), prog);
+ exit(EXIT_FAILURE);
+#endif
+
+ } else if (strcasecmp(control, "authzid") == 0) {
+#if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL)
+ if (vc_sasl_authzid) {
+ fprintf(stderr,
+ _("SASL authzid previously specified\n"));
+ exit(EXIT_FAILURE);
+ }
+ if (cvalue == NULL) {
+ fprintf(stderr,
+ _("missing authzid in SASL option\n"));
+ exit(EXIT_FAILURE);
+ }
+
+ vc_sasl_authzid = ber_strdup(cvalue);
+#else
+ fprintf(stderr,
+ _("%s: not compiled with SASL support\n"), prog);
+ exit(EXIT_FAILURE);
+#endif
+
+ } else if (strcasecmp(control, "secprops") == 0) {
+#if defined(LDAP_API_FEATURES_VERIFY_CREDENTIALS_INTERACTIVE) && defined(HAVE_CYRUS_SASL)
+ if (vc_sasl_secprops) {
+ fprintf(stderr,
+ _("SASL secprops previously specified\n"));
+ exit(EXIT_FAILURE);
+ }
+ if (cvalue == NULL) {
+ fprintf(stderr,
+ _("missing secprops in SASL option\n"));
+ exit(EXIT_FAILURE);
+ }
+
+ vc_sasl_secprops = ber_strdup(cvalue);
+#else
+ fprintf(stderr,
+ _("%s: not compiled with SASL support\n"), prog);
+ exit(EXIT_FAILURE);
+#endif
+
+ } else {
+ fprintf( stderr, _("Invalid Verify Credentials extension name: %s\n"), control );
+ usage();
+ }
+ break;
+
+ case 'a': /* request authzid */
+ req_authzid++;
+ break;
+
+ case 'b': /* request authzid */
+ req_pp++;
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+
+int
+main( int argc, char *argv[] )
+{
+ int rc;
+ LDAP *ld = NULL;
+ char *matcheddn = NULL, *text = NULL, **refs = NULL;
+ int rcode;
+ char * diag = NULL;
+ struct berval *scookie = NULL;
+ struct berval *scred = NULL;
+ int id, code = 0;
+ LDAPMessage *res;
+ LDAPControl **ctrls = NULL;
+ LDAPControl **vcctrls = NULL;
+ int nvcctrls = 0;
+
+ tool_init( TOOL_VC );
+ prog = lutil_progname( "ldapvc", argc, argv );
+
+ /* LDAPv3 only */
+ protocol = LDAP_VERSION3;
+
+ tool_args( argc, argv );
+
+ if (argc - optind > 0) {
+ dn = argv[optind++];
+ }
+ if (argc - optind > 0) {
+ cred.bv_val = strdup(argv[optind++]);
+ cred.bv_len = strlen(cred.bv_val);
+ }
+ if (argc - optind > 0) {
+ usage();
+ }
+ if (dn
+#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS_INTERACTIVE
+ && !vc_sasl_mech
+#endif
+ && !cred.bv_val)
+ {
+ cred.bv_val = strdup(getpassphrase(_("User's password: ")));
+ cred.bv_len = strlen(cred.bv_val);
+ }
+
+#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS_INTERACTIVE
+ if (vc_sasl_mech && (vc_sasl == LDAP_SASL_NONE)) {
+ vc_sasl = LDAP_SASL_AUTOMATIC;
+ }
+#endif
+
+ ld = tool_conn_setup( 0, 0 );
+
+ tool_bind( ld );
+
+ if ( dont ) {
+ rc = LDAP_SUCCESS;
+ goto skip;
+ }
+
+ tool_server_controls( ld, NULL, 0 );
+
+ if (req_authzid) {
+ vcctrls = (LDAPControl **) malloc(3*sizeof(LDAPControl *));
+ vcctrls[nvcctrls] = (LDAPControl *) malloc(sizeof(LDAPControl));
+ vcctrls[nvcctrls]->ldctl_oid = ldap_strdup(LDAP_CONTROL_AUTHZID_REQUEST);
+ vcctrls[nvcctrls]->ldctl_iscritical = 0;
+ vcctrls[nvcctrls]->ldctl_value.bv_val = NULL;
+ vcctrls[nvcctrls]->ldctl_value.bv_len = 0;
+ vcctrls[++nvcctrls] = NULL;
+ }
+
+ if (req_pp) {
+ if (!vcctrls) vcctrls = (LDAPControl **) malloc(3*sizeof(LDAPControl *));
+ vcctrls[nvcctrls] = (LDAPControl *) malloc(sizeof(LDAPControl));
+ vcctrls[nvcctrls]->ldctl_oid = ldap_strdup(LDAP_CONTROL_PASSWORDPOLICYREQUEST);
+ vcctrls[nvcctrls]->ldctl_iscritical = 0;
+ vcctrls[nvcctrls]->ldctl_value.bv_val = NULL;
+ vcctrls[nvcctrls]->ldctl_value.bv_len = 0;
+ vcctrls[++nvcctrls] = NULL;
+ }
+
+#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS_INTERACTIVE
+#ifdef HAVE_CYRUS_SASL
+ if (vc_sasl_mech) {
+ int msgid;
+ void * defaults;
+ void * context = NULL;
+ const char *rmech = NULL;
+
+ defaults = lutil_sasl_defaults(ld,
+ vc_sasl_mech,
+ vc_sasl_realm,
+ vc_sasl_authcid,
+ cred.bv_val,
+ sasl_authz_id);
+
+ do {
+ rc = ldap_verify_credentials_interactive(ld, dn, vc_sasl_mech,
+ vcctrls, NULL, NULL,
+ vc_sasl, lutil_sasl_interact, defaults, context,
+ res, &rmech, &msgid);
+
+ if (rc != LDAP_SASL_BIND_IN_PROGRESS) break;
+
+ ldap_msgfree(res);
+
+ if (ldap_result(ld, msgid, LDAP_MSG_ALL, NULL, &res) == -1 || !res) {
+ ldap_get_option(ld, LDAP_OPT_RESULT_CODE, (void*) &rc);
+ ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*) &text);
+ tool_perror( "ldap_verify_credentials_interactive", rc, NULL, NULL, text, NULL);
+ ldap_memfree(text);
+ tool_exit(ld, rc);
+ }
+ } while (rc == LDAP_SASL_BIND_IN_PROGRESS);
+
+ lutil_sasl_freedefs(defaults);
+
+ if( rc != LDAP_SUCCESS ) {
+ ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*) &text);
+ tool_perror( "ldap_verify_credentials", rc, NULL, NULL, text, NULL );
+ rc = EXIT_FAILURE;
+ goto skip;
+ }
+
+ } else
+#endif
+#endif
+ {
+ rc = ldap_verify_credentials( ld,
+ NULL,
+ dn, NULL, cred.bv_val ? &cred: NULL, vcctrls,
+ NULL, NULL, &id );
+
+ if( rc != LDAP_SUCCESS ) {
+ ldap_get_option(ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*) &text);
+ tool_perror( "ldap_verify_credentials", rc, NULL, NULL, text, NULL );
+ rc = EXIT_FAILURE;
+ goto skip;
+ }
+
+ for ( ; ; ) {
+ struct timeval tv;
+
+ if ( tool_check_abandon( ld, id ) ) {
+ tool_exit( ld, LDAP_CANCELLED );
+ }
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+
+ rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res );
+ if ( rc < 0 ) {
+ tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL );
+ tool_exit( ld, rc );
+ }
+
+ if ( rc != 0 ) {
+ break;
+ }
+ }
+ }
+
+ ldap_controls_free(vcctrls);
+ vcctrls = NULL;
+
+ rc = ldap_parse_result( ld, res,
+ &code, &matcheddn, &text, &refs, &ctrls, 0 );
+
+ if (rc == LDAP_SUCCESS) rc = code;
+
+ if (rc != LDAP_SUCCESS) {
+ tool_perror( "ldap_parse_result", rc, NULL, matcheddn, text, refs );
+ rc = EXIT_FAILURE;
+ goto skip;
+ }
+
+ rc = ldap_parse_verify_credentials( ld, res, &rcode, &diag, &scookie, &scred, &vcctrls );
+ ldap_msgfree(res);
+
+ if (rc != LDAP_SUCCESS) {
+ tool_perror( "ldap_parse_verify_credentials", rc, NULL, NULL, NULL, NULL );
+ rc = EXIT_FAILURE;
+ goto skip;
+ }
+
+ if (rcode != LDAP_SUCCESS) {
+ printf(_("Failed: %s (%d)\n"), ldap_err2string(rcode), rcode);
+ }
+
+ if (diag && *diag) {
+ printf(_("Diagnostic: %s\n"), diag);
+ }
+
+ if (vcctrls) {
+ tool_print_ctrls( ld, vcctrls );
+ }
+
+skip:
+ if ( verbose || code != LDAP_SUCCESS ||
+ ( matcheddn && *matcheddn ) || ( text && *text ) || refs || ctrls )
+ {
+ printf( _("Result: %s (%d)\n"), ldap_err2string( code ), code );
+
+ if( text && *text ) {
+ printf( _("Additional info: %s\n"), text );
+ }
+
+ if( matcheddn && *matcheddn ) {
+ printf( _("Matched DN: %s\n"), matcheddn );
+ }
+
+ if( refs ) {
+ int i;
+ for( i=0; refs[i]; i++ ) {
+ printf(_("Referral: %s\n"), refs[i] );
+ }
+ }
+
+ if (ctrls) {
+ tool_print_ctrls( ld, ctrls );
+ ldap_controls_free( ctrls );
+ }
+ }
+
+ ber_memfree( text );
+ ber_memfree( matcheddn );
+ ber_memvfree( (void **) refs );
+ ber_bvfree( scookie );
+ ber_bvfree( scred );
+ ber_memfree( diag );
+ free( cred.bv_val );
+
+ /* disconnect from server */
+ tool_exit( ld, code == LDAP_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE );
+}
diff --git a/clients/tools/ldapwhoami.c b/clients/tools/ldapwhoami.c
new file mode 100644
index 0000000..45d32f5
--- /dev/null
+++ b/clients/tools/ldapwhoami.c
@@ -0,0 +1,235 @@
+/* ldapwhoami.c -- a tool for asking the directory "Who Am I?" */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1998-2003 Kurt D. Zeilenga.
+ * Portions Copyright 1998-2001 Net Boolean Incorporated.
+ * Portions Copyright 2001-2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission. This
+ * software is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by Kurt D. Zeilenga for inclusion
+ * in OpenLDAP Software based, in part, on other client tools.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include <ldap.h>
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "ldap_defaults.h"
+
+#include "common.h"
+
+
+void
+usage( void )
+{
+ fprintf( stderr, _("Issue LDAP Who am I? operation to request user's authzid\n\n"));
+ fprintf( stderr, _("usage: %s [options]\n"), prog);
+ tool_common_usage();
+ exit( EXIT_FAILURE );
+}
+
+
+const char options[] = ""
+ "d:D:e:h:H:InNO:o:p:QR:U:vVw:WxX:y:Y:Z";
+
+int
+handle_private_option( int i )
+{
+ switch ( i ) {
+#if 0
+ char *control, *cvalue;
+ int crit;
+ case 'E': /* whoami extension */
+ if( protocol == LDAP_VERSION2 ) {
+ fprintf( stderr, _("%s: -E incompatible with LDAPv%d\n"),
+ prog, protocol );
+ exit( EXIT_FAILURE );
+ }
+
+ /* should be extended to support comma separated list of
+ * [!]key[=value] parameters, e.g. -E !foo,bar=567
+ */
+
+ crit = 0;
+ cvalue = NULL;
+ if( optarg[0] == '!' ) {
+ crit = 1;
+ optarg++;
+ }
+
+ control = optarg;
+ if ( (cvalue = strchr( control, '=' )) != NULL ) {
+ *cvalue++ = '\0';
+ }
+
+ fprintf( stderr, _("Invalid whoami extension name: %s\n"), control );
+ usage();
+#endif
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+
+int
+main( int argc, char *argv[] )
+{
+ int rc;
+ LDAP *ld = NULL;
+ char *matcheddn = NULL, *text = NULL, **refs = NULL;
+ struct berval *authzid = NULL;
+ int id, code = 0;
+ LDAPMessage *res = NULL;
+ LDAPControl **ctrls = NULL;
+
+ tool_init( TOOL_WHOAMI );
+ prog = lutil_progname( "ldapwhoami", argc, argv );
+
+ /* LDAPv3 only */
+ protocol = LDAP_VERSION3;
+
+ tool_args( argc, argv );
+
+ if( argc - optind > 0 ) {
+ usage();
+ }
+
+ ld = tool_conn_setup( 0, 0 );
+
+ tool_bind( ld );
+
+ if ( dont ) {
+ rc = LDAP_SUCCESS;
+ goto skip;
+ }
+
+ tool_server_controls( ld, NULL, 0 );
+
+ rc = ldap_whoami( ld, NULL, NULL, &id );
+
+ if( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_whoami", rc, NULL, NULL, NULL, NULL );
+ rc = EXIT_FAILURE;
+ goto skip;
+ }
+
+ for ( ; ; ) {
+ struct timeval tv;
+
+ if ( tool_check_abandon( ld, id ) ) {
+ tool_exit( ld, LDAP_CANCELLED );
+ }
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+
+ rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res );
+ if ( rc < 0 ) {
+ tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL );
+ tool_exit( ld, rc );
+ }
+
+ if ( rc != 0 ) {
+ break;
+ }
+ }
+
+ rc = ldap_parse_result( ld, res,
+ &code, &matcheddn, &text, &refs, &ctrls, 0 );
+
+ if ( rc == LDAP_SUCCESS ) {
+ rc = code;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_parse_result", rc, NULL, matcheddn, text, refs );
+ rc = EXIT_FAILURE;
+ goto skip;
+ }
+
+ rc = ldap_parse_whoami( ld, res, &authzid );
+
+ if( rc != LDAP_SUCCESS ) {
+ tool_perror( "ldap_parse_whoami", rc, NULL, NULL, NULL, NULL );
+ rc = EXIT_FAILURE;
+ goto skip;
+ }
+
+ if( authzid != NULL ) {
+ if( authzid->bv_len == 0 ) {
+ printf(_("anonymous\n") );
+ } else {
+ printf("%s\n", authzid->bv_val );
+ }
+ }
+
+skip:
+ ldap_msgfree(res);
+ if ( verbose || code != LDAP_SUCCESS ||
+ ( matcheddn && *matcheddn ) || ( text && *text ) || refs || ctrls )
+ {
+ printf( _("Result: %s (%d)\n"), ldap_err2string( code ), code );
+
+ if( text && *text ) {
+ printf( _("Additional info: %s\n"), text );
+ }
+
+ if( matcheddn && *matcheddn ) {
+ printf( _("Matched DN: %s\n"), matcheddn );
+ }
+
+ if( refs ) {
+ int i;
+ for( i=0; refs[i]; i++ ) {
+ printf(_("Referral: %s\n"), refs[i] );
+ }
+ }
+
+ if (ctrls) {
+ tool_print_ctrls( ld, ctrls );
+ ldap_controls_free( ctrls );
+ }
+ }
+
+ ber_memfree( text );
+ ber_memfree( matcheddn );
+ ber_memvfree( (void **) refs );
+ ber_bvfree( authzid );
+
+ /* disconnect from server */
+ tool_exit( ld, code == LDAP_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE );
+}
diff --git a/configure b/configure
new file mode 100755
index 0000000..f0513cb
--- /dev/null
+++ b/configure
@@ -0,0 +1,28163 @@
+#! /bin/sh
+# From configure.ac Id: 15bca89511fc428731cf9ab71a9b46e37511be67 .
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.69.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation. All rights reserved.
+# Restrictions apply, see COPYRIGHT and LICENSE files.
+#
+#
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in #(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Use a proper internal environment variable to ensure we don't fall
+ # into an infinite loop, continuously re-executing ourselves.
+ if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+ _as_can_reexec=no; export _as_can_reexec;
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+ fi
+ # We don't want this to propagate to other subprocesses.
+ { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+ as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '\${1+\"\$@\"}'='\"\$@\"'
+ setopt NO_GLOB_SUBST
+else
+ case \`(set -o) 2>/dev/null\` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+"
+ as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+ exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
+ as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+ as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+ eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+ test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+
+ test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || (
+ ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+ ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO
+ ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO
+ PATH=/empty FPATH=/empty; export PATH FPATH
+ test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\
+ || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1"
+ if (eval "$as_required") 2>/dev/null; then :
+ as_have_required=yes
+else
+ as_have_required=no
+fi
+ if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ as_found=:
+ case $as_dir in #(
+ /*)
+ for as_base in sh bash ksh sh5; do
+ # Try only shells that exist, to save several forks.
+ as_shell=$as_dir/$as_base
+ if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ CONFIG_SHELL=$as_shell as_have_required=yes
+ if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ break 2
+fi
+fi
+ done;;
+ esac
+ as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+ CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+ if test "x$CONFIG_SHELL" != x; then :
+ export CONFIG_SHELL
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+ if test x$as_have_required = xno; then :
+ $as_echo "$0: This script requires a shell more modern than all"
+ $as_echo "$0: the shells that I found on your system."
+ if test x${ZSH_VERSION+set} = xset ; then
+ $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+ $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+ else
+ $as_echo "$0: Please tell bug-autoconf@gnu.org about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+ fi
+ exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+ as_lineno_1=$LINENO as_lineno_1a=$LINENO
+ as_lineno_2=$LINENO as_lineno_2a=$LINENO
+ eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+ test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+ # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+ # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+ # already done that, so ensure we don't try to do so again and fall
+ # in an infinite loop. This has already happened in practice.
+ _as_can_reexec=no; export _as_can_reexec
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -pR'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -pR'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -pR'
+ fi
+else
+ as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME=
+PACKAGE_TARNAME=
+PACKAGE_VERSION=
+PACKAGE_STRING=
+PACKAGE_BUGREPORT=
+PACKAGE_URL=
+
+ac_unique_file="OpenLDAP"
+ac_unique_file="build/version.sh"
+ac_default_prefix=/usr/local
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='LTLIBOBJS
+BALANCER_INCLUDE
+SLAPD_SQL_INCLUDES
+SLAPD_SQL_LIBS
+SLAPD_SQL_LDFLAGS
+SLAPD_GMP_LIBS
+SLAPD_SLP_LIBS
+SYSTEMD_LIBS
+ARGON2_LIBS
+AUTH_LIBS
+LIBSLAPI
+SLAPI_LIBS
+MODULES_LIBS
+WITH_TLS_TYPE
+TLS_LIBS
+SASL_LIBS
+MOD_PERL_LDFLAGS
+SLAPD_PERL_LDFLAGS
+PERL_CPPFLAGS
+SLAPD_DYNAMIC_PWMODS
+SLAPD_DYNAMIC_OVERLAYS
+SLAPD_STATIC_OVERLAYS
+SLAPD_DYNAMIC_BACKENDS
+SLAPD_STATIC_BACKENDS
+SLAPD_NO_STATIC
+SLAPD_MODULES_LDFLAGS
+SLAPD_MODULES_CPPFLAGS
+WRAP_LIBS
+LEVENT_LIBS
+LUTIL_LIBS
+LTHREAD_LIBS
+SLAPD_NDB_INCS
+SLAPD_NDB_LIBS
+BALANCER_LIBS
+SLAPD_LIBS
+CLIENT_LIBS
+LDAP_LIBS
+BUILD_PW_ARGON2
+BUILD_BALANCER
+BUILD_VALSORT
+BUILD_UNIQUE
+BUILD_TRANSLUCENT
+BUILD_SYNCPROV
+BUILD_SSSVLV
+BUILD_SEQMOD
+BUILD_RWM
+BUILD_RETCODE
+BUILD_REMOTEAUTH
+BUILD_REFINT
+BUILD_PROXYCACHE
+BUILD_PPOLICY
+BUILD_OTP
+BUILD_MEMBEROF
+BUILD_HOMEDIR
+BUILD_LASTMOD
+BUILD_DYNLIST
+BUILD_DYNGROUP
+BUILD_DEREF
+BUILD_DENYOP
+BUILD_DDS
+BUILD_CONSTRAINT
+BUILD_COLLECT
+BUILD_AUTOCA
+BUILD_AUDITLOG
+BUILD_ACCESSLOG
+BUILD_WT
+BUILD_SQL
+BUILD_SOCK
+BUILD_SHELL
+BUILD_PERL
+BUILD_RELAY
+BUILD_PASSWD
+BUILD_NULL
+BUILD_NDB
+BUILD_ASYNCMETA
+BUILD_META
+BUILD_MDB
+BUILD_LDAP
+BUILD_DNSSRV
+SLAPD_SLAPI_DEPEND
+BUILD_SLAPI
+BUILD_SLAPD
+OL_VERSIONED_SYMBOLS
+BUILD_LIBS_DYNAMIC
+BUILD_THREAD
+WITH_SYSTEMD
+WITH_ACI_ENABLED
+WITH_MODULES_ENABLED
+WITH_TLS
+WITH_SASL
+PLAT
+LIBSRCS
+LIBOBJS
+systemdsystemunitdir
+WT_LIBS
+WT_CFLAGS
+PKG_CONFIG_LIBDIR
+PKG_CONFIG_PATH
+PKG_CONFIG
+MYSQL
+LTSTATIC
+OL_MKDEP_FLAGS
+OL_MKDEP
+RC
+PERLBIN
+CPP
+LT_SYS_LIBRARY_PATH
+OTOOL64
+OTOOL
+LIPO
+NMEDIT
+DSYMUTIL
+MANIFEST_TOOL
+AWK
+RANLIB
+ac_ct_AR
+LN_S
+NM
+ac_ct_DUMPBIN
+DUMPBIN
+LD
+FGREP
+EGREP
+GREP
+SED
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+LIBTOOL
+SET_MAKE
+OBJDUMP
+DLLTOOL
+AS
+STRIP
+AR
+CC
+ldap_subdir
+top_builddir
+OPENLDAP_RELEASE_DATE
+OPENLDAP_LIBVERSION
+OPENLDAP_LIBRELEASE
+VERSION
+PACKAGE
+target_os
+target_vendor
+target_cpu
+target
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+runstatedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+with_subdir
+enable_debug
+enable_dynamic
+enable_syslog
+enable_ipv6
+enable_local
+with_cyrus_sasl
+with_systemd
+with_fetch
+with_threads
+with_tls
+with_yielding_select
+with_mp
+with_odbc
+enable_xxslapdoptions
+enable_slapd
+enable_dynacl
+enable_aci
+enable_cleartext
+enable_crypt
+enable_spasswd
+enable_modules
+enable_rlookups
+enable_slapi
+enable_slp
+enable_wrappers
+enable_xxslapbackends
+enable_backends
+enable_dnssrv
+enable_ldap
+enable_mdb
+enable_meta
+enable_asyncmeta
+enable_ndb
+enable_null
+enable_passwd
+enable_perl
+enable_relay
+enable_sock
+enable_sql
+enable_wt
+enable_xxslapoverlays
+enable_overlays
+enable_accesslog
+enable_auditlog
+enable_autoca
+enable_collect
+enable_constraint
+enable_dds
+enable_deref
+enable_dyngroup
+enable_dynlist
+enable_homedir
+enable_memberof
+enable_otp
+enable_ppolicy
+enable_proxycache
+enable_refint
+enable_remoteauth
+enable_retcode
+enable_rwm
+enable_seqmod
+enable_sssvlv
+enable_syncprov
+enable_translucent
+enable_unique
+enable_valsort
+enable_pwmodoptions
+enable_argon2
+with_argon2
+enable_balanceroptions
+enable_balancer
+enable_xxliboptions
+enable_static
+enable_shared
+enable_versioning
+with_pic
+enable_fast_install
+with_aix_soname
+with_gnu_ld
+with_sysroot
+enable_libtool_lock
+with_xxinstall
+'
+ ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+LT_SYS_LIBRARY_PATH
+CPP
+PKG_CONFIG
+PKG_CONFIG_PATH
+PKG_CONFIG_LIBDIR
+WT_CFLAGS
+WT_LIBS
+systemdsystemunitdir'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval $ac_prev=\$ac_option
+ ac_prev=
+ continue
+ fi
+
+ case $ac_option in
+ *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+ *=) ac_optarg= ;;
+ *) ac_optarg=yes ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_dashdash$ac_option in
+ --)
+ ac_dashdash=yes ;;
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=*)
+ datadir=$ac_optarg ;;
+
+ -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+ | --dataroo | --dataro | --datar)
+ ac_prev=datarootdir ;;
+ -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+ datarootdir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=no ;;
+
+ -docdir | --docdir | --docdi | --doc | --do)
+ ac_prev=docdir ;;
+ -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+ docdir=$ac_optarg ;;
+
+ -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+ ac_prev=dvidir ;;
+ -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+ dvidir=$ac_optarg ;;
+
+ -enable-* | --enable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=\$ac_optarg ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+ ac_prev=htmldir ;;
+ -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+ | --ht=*)
+ htmldir=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localedir | --localedir | --localedi | --localed | --locale)
+ ac_prev=localedir ;;
+ -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+ localedir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst | --locals)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+ ac_prev=pdfdir ;;
+ -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+ pdfdir=$ac_optarg ;;
+
+ -psdir | --psdir | --psdi | --psd | --ps)
+ ac_prev=psdir ;;
+ -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+ psdir=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -runstatedir | --runstatedir | --runstatedi | --runstated \
+ | --runstate | --runstat | --runsta | --runst | --runs \
+ | --run | --ru | --r)
+ ac_prev=runstatedir ;;
+ -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+ | --run=* | --ru=* | --r=*)
+ runstatedir=$ac_optarg ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=\$ac_optarg ;;
+
+ -without-* | --without-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=no ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ case $ac_envvar in #(
+ '' | [0-9]* | *[!_$as_cr_alnum]* )
+ as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+ esac
+ eval $ac_envvar=\$ac_optarg
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+ case $enable_option_checking in
+ no) ;;
+ fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+ *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+ esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
+ datadir sysconfdir sharedstatedir localstatedir includedir \
+ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+ libdir localedir mandir runstatedir
+do
+ eval ac_val=\$$ac_var
+ # Remove trailing slashes.
+ case $ac_val in
+ */ )
+ ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+ eval $ac_var=\$ac_val;;
+ esac
+ # Be sure to have absolute directory names.
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) continue;;
+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+ esac
+ as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+ as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+ as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then the parent directory.
+ ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_myself" : 'X\(//\)[^/]' \| \
+ X"$as_myself" : 'X\(//\)$' \| \
+ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r "$srcdir/$ac_unique_file"; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+ as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+ cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+ pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+ srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+ eval ac_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_env_${ac_var}_value=\$${ac_var}
+ eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking ...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
+ --infodir=DIR info documentation [DATAROOTDIR/info]
+ --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
+ --mandir=DIR man documentation [DATAROOTDIR/man]
+ --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE]
+ --htmldir=DIR html documentation [DOCDIR]
+ --dvidir=DIR dvi documentation [DOCDIR]
+ --pdfdir=DIR pdf documentation [DOCDIR]
+ --psdir=DIR ps documentation [DOCDIR]
+_ACEOF
+
+ cat <<\_ACEOF
+
+System types:
+ --build=BUILD configure for building on BUILD [guessed]
+ --host=HOST cross-compile to build programs to run on HOST [BUILD]
+ --target=TARGET configure for building compilers for TARGET [HOST]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-option-checking ignore unrecognized --enable/--with options
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --enable-debug enable debugging no|yes|traditional [yes]
+ --enable-dynamic enable linking built binaries with dynamic libs [auto]
+ --enable-syslog enable syslog support [auto]
+ --enable-ipv6 enable IPv6 support [auto]
+ --enable-local enable AF_LOCAL (AF_UNIX) socket support [auto]
+
+SLAPD (Standalone LDAP Daemon) Options:
+ --enable-slapd enable building slapd [yes]
+ --enable-dynacl enable run-time loadable ACL support (experimental) [no]
+ --enable-aci enable per-object ACIs (experimental) no|yes|mod [no]
+ --enable-cleartext enable cleartext passwords [yes]
+ --enable-crypt enable crypt(3) passwords [no]
+ --enable-spasswd enable (Cyrus) SASL password verification [no]
+ --enable-modules enable dynamic module support [no]
+ --enable-rlookups enable reverse lookups of client hostnames [no]
+ --enable-slapi enable SLAPI support (experimental) [no]
+ --enable-slp enable SLPv2 support [no]
+ --enable-wrappers enable tcp wrapper support [no]
+
+SLAPD Backend Options:
+ --enable-backends enable all available backends no|yes|mod
+ --enable-dnssrv enable dnssrv backend no|yes|mod [no]
+ --enable-ldap enable ldap backend no|yes|mod [no]
+ --enable-mdb enable mdb database backend no|yes|mod [yes]
+ --enable-meta enable metadirectory backend no|yes|mod [no]
+ --enable-asyncmeta enable asynchronous metadirectory backend no|yes|mod [no]
+ --enable-ndb enable MySQL NDB Cluster backend no|yes|mod [no]
+ --enable-null enable null backend no|yes|mod [no]
+ --enable-passwd enable passwd backend no|yes|mod [no]
+ --enable-perl enable perl backend no|yes|mod [no]
+ --enable-relay enable relay backend no|yes|mod [yes]
+ --enable-sock enable sock backend no|yes|mod [no]
+ --enable-sql enable sql backend no|yes|mod [no]
+ --enable-wt enable WiredTiger backend no|yes|mod [no]
+
+SLAPD Overlay Options:
+ --enable-overlays enable all available overlays no|yes|mod
+ --enable-accesslog In-Directory Access Logging overlay no|yes|mod [no]
+ --enable-auditlog Audit Logging overlay no|yes|mod [no]
+ --enable-autoca Automatic Certificate Authority overlay no|yes|mod [no]
+ --enable-collect Collect overlay no|yes|mod [no]
+ --enable-constraint Attribute Constraint overlay no|yes|mod [no]
+ --enable-dds Dynamic Directory Services overlay no|yes|mod [no]
+ --enable-deref Dereference overlay no|yes|mod [no]
+ --enable-dyngroup Dynamic Group overlay no|yes|mod [no]
+ --enable-dynlist Dynamic List overlay no|yes|mod [no]
+ --enable-homedir Home Directory Management overlay no|yes|mod [no]
+ --enable-memberof Reverse Group Membership overlay no|yes|mod [no]
+ --enable-otp OTP 2-factor authentication overlay no|yes|mod [no]
+ --enable-ppolicy Password Policy overlay no|yes|mod [no]
+ --enable-proxycache Proxy Cache overlay no|yes|mod [no]
+ --enable-refint Referential Integrity overlay no|yes|mod [no]
+ --enable-remoteauth Deferred Authentication overlay no|yes|mod [no]
+ --enable-retcode Return Code testing overlay no|yes|mod [no]
+ --enable-rwm Rewrite/Remap overlay no|yes|mod [no]
+ --enable-seqmod Sequential Modify overlay no|yes|mod [no]
+ --enable-sssvlv ServerSideSort/VLV overlay no|yes|mod [no]
+ --enable-syncprov Syncrepl Provider overlay no|yes|mod [yes]
+ --enable-translucent Translucent Proxy overlay no|yes|mod [no]
+ --enable-unique Attribute Uniqueness overlay no|yes|mod [no]
+ --enable-valsort Value Sorting overlay no|yes|mod [no]
+
+SLAPD Password Module Options:
+ --enable-argon2 Argon2 password hashing module no|yes [no]
+
+LLOADD (Load Balancer Daemon) Options:
+ --enable-balancer enable load balancer no|yes|mod [no]
+
+Library Generation & Linking Options
+ --enable-static[=PKGS] build static libraries [default=yes]
+ --enable-shared[=PKGS] build shared libraries [default=yes]
+ --enable-versioning Enable versioned symbols in shared library no|yes|auto [auto]
+ --enable-fast-install[=PKGS]
+ optimize for fast installation [default=yes]
+ --disable-libtool-lock avoid locking (might break parallel builds)
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-subdir=DIR change default subdirectory used for installs
+ --with-cyrus-sasl with Cyrus SASL support [auto]
+ --with-systemd with systemd service notification support [auto]
+ --with-fetch with fetch(3) URL support [auto]
+ --with-threads with threads library auto|nt|posix|pth|lwp|manual [auto]
+ --with-tls with TLS/SSL support auto|openssl|gnutls [auto]
+ --with-yielding-select with implicitly yielding select [auto]
+ --with-mp with multiple precision statistics
+ auto|longlong|long|bignum|gmp [auto]
+ --with-odbc with specific ODBC support
+ iodbc|unixodbc|odbc32|auto [auto]
+ --with-argon2 with argon2 support library auto|libsodium|libargon2 [auto]
+ --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use
+ both]
+ --with-aix-soname=aix|svr4|both
+ shared library versioning (aka "SONAME") variant to
+ provide on AIX, [default=aix].
+ --with-gnu-ld assume the C compiler uses GNU ld [default=no]
+ --with-sysroot[=DIR] Search for dependent libraries within DIR (or the
+ compiler's sysroot if not specified).
+
+See INSTALL file for further details.
+
+Some influential environment variables:
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ LIBS libraries to pass to the linker, e.g. -l<library>
+ CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+ you have headers in a nonstandard directory <include dir>
+ LT_SYS_LIBRARY_PATH
+ User-defined run-time library search path.
+ CPP C preprocessor
+ PKG_CONFIG path to pkg-config utility
+ PKG_CONFIG_PATH
+ directories to add to pkg-config's search path
+ PKG_CONFIG_LIBDIR
+ path overriding pkg-config's built-in search path
+ WT_CFLAGS C compiler flags for WT, overriding pkg-config
+ WT_LIBS linker flags for WT, overriding pkg-config
+ systemdsystemunitdir
+ value of systemdsystemunitdir for systemd, overriding pkg-config
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to the package provider.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d "$ac_dir" ||
+ { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+ continue
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+ cd "$ac_dir" || { ac_status=$?; continue; }
+ # Check for guested configure.
+ if test -f "$ac_srcdir/configure.gnu"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+ elif test -f "$ac_srcdir/configure"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure" --help=recursive
+ else
+ $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi || ac_status=$?
+ cd "$ac_pwd" || { ac_status=$?; break; }
+ done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+ cat <<\_ACEOF
+configure
+generated by GNU Autoconf 2.69
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+
+Copyright 1998-2022 The OpenLDAP Foundation. All rights reserved.
+Restrictions apply, see COPYRIGHT and LICENSE files.
+_ACEOF
+ exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext
+ if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_c_try_link LINENO
+# -----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext conftest$ac_exeext
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ test -x conftest$ac_exeext
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+ # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+ # interfere with the next link command; also delete a directory that is
+ # left behind by Apple's compiler. We do this before executing the actions.
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_link
+
+# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists and can be compiled using the include files in
+# INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$3=yes"
+else
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_compile
+
+# ac_fn_c_try_cpp LINENO
+# ----------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } > conftest.i && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_cpp
+
+# ac_fn_c_try_run LINENO
+# ----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
+# that executables *can* be run.
+ac_fn_c_try_run ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=$ac_status
+fi
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_run
+
+# ac_fn_c_check_func LINENO FUNC VAR
+# ----------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $2 (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$3=yes"
+else
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_func
+
+# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists, giving a warning if it cannot be compiled using
+# the include files in INCLUDES and setting the cache variable VAR
+# accordingly.
+ac_fn_c_check_header_mongrel ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if eval \${$3+:} false; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_header_compiler=yes
+else
+ ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <$2>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ ac_header_preproc=yes
+else
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
+ yes:no: )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+ ;;
+esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_mongrel
+
+# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
+# -------------------------------------------
+# Tests whether TYPE exists after having included INCLUDES, setting cache
+# variable VAR accordingly.
+ac_fn_c_check_type ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ eval "$3=no"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+if (sizeof ($2))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+if (sizeof (($2)))
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+ eval "$3=yes"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_type
+
+# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES
+# ----------------------------------------------------
+# Tries to find if the field MEMBER exists in type AGGR, after including
+# INCLUDES, setting cache variable VAR accordingly.
+ac_fn_c_check_member ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5
+$as_echo_n "checking for $2.$3... " >&6; }
+if eval \${$4+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$5
+int
+main ()
+{
+static $2 ac_aggr;
+if (ac_aggr.$3)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$4=yes"
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$5
+int
+main ()
+{
+static $2 ac_aggr;
+if (sizeof ac_aggr.$3)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$4=yes"
+else
+ eval "$4=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$4
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_member
+
+# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES
+# --------------------------------------------
+# Tries to find the compile-time value of EXPR in a program that includes
+# INCLUDES, setting VAR accordingly. Returns whether the value could be
+# computed
+ac_fn_c_compute_int ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if test "$cross_compiling" = yes; then
+ # Depending upon the size, compute the lo and hi bounds.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) >= 0)];
+test_array [0] = 0;
+return test_array [0];
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_lo=0 ac_mid=0
+ while :; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_hi=$ac_mid; break
+else
+ as_fn_arith $ac_mid + 1 && ac_lo=$as_val
+ if test $ac_lo -le $ac_mid; then
+ ac_lo= ac_hi=
+ break
+ fi
+ as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) < 0)];
+test_array [0] = 0;
+return test_array [0];
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_hi=-1 ac_mid=-1
+ while :; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) >= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_lo=$ac_mid; break
+else
+ as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val
+ if test $ac_mid -le $ac_hi; then
+ ac_lo= ac_hi=
+ break
+ fi
+ as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+else
+ ac_lo= ac_hi=
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+# Binary search between lo and hi bounds.
+while test "x$ac_lo" != "x$ac_hi"; do
+ as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+int
+main ()
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_hi=$ac_mid
+else
+ as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+case $ac_lo in #((
+?*) eval "$3=\$ac_lo"; ac_retval=0 ;;
+'') ac_retval=1 ;;
+esac
+ else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+static long int longval () { return $2; }
+static unsigned long int ulongval () { return $2; }
+#include <stdio.h>
+#include <stdlib.h>
+int
+main ()
+{
+
+ FILE *f = fopen ("conftest.val", "w");
+ if (! f)
+ return 1;
+ if (($2) < 0)
+ {
+ long int i = longval ();
+ if (i != ($2))
+ return 1;
+ fprintf (f, "%ld", i);
+ }
+ else
+ {
+ unsigned long int i = ulongval ();
+ if (i != ($2))
+ return 1;
+ fprintf (f, "%lu", i);
+ }
+ /* Do not output a trailing newline, as this causes \r\n confusion
+ on some platforms. */
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ echo >>conftest.val; read $3 <conftest.val; ac_retval=0
+else
+ ac_retval=1
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f conftest.val
+
+ fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_compute_int
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by $as_me, which was
+generated by GNU Autoconf 2.69. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ $as_echo "PATH: $as_dir"
+ done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *\'*)
+ ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+ 2)
+ as_fn_append ac_configure_args1 " '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ as_fn_append ac_configure_args " '$ac_arg'"
+ ;;
+ esac
+ done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+(
+ for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+ (set) 2>&1 |
+ case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ sed -n \
+ "s/'\''/'\''\\\\'\'''\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+ ;; #(
+ *)
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+)
+ echo
+
+ $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+ echo
+ cat confdefs.h
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ $as_echo "$as_me: caught signal $ac_signal"
+ $as_echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core core.conftest.* &&
+ rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+ # We do not want a PATH search for config.site.
+ case $CONFIG_SITE in #((
+ -*) ac_site_file1=./$CONFIG_SITE;;
+ */*) ac_site_file1=$CONFIG_SITE;;
+ *) ac_site_file1=./$CONFIG_SITE;;
+ esac
+elif test "x$prefix" != xNONE; then
+ ac_site_file1=$prefix/share/config.site
+ ac_site_file2=$prefix/etc/config.site
+else
+ ac_site_file1=$ac_default_prefix/share/config.site
+ ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+ test "x$ac_site_file" = xNONE && continue
+ if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file" \
+ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+done
+
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val=\$ac_cv_env_${ac_var}_value
+ eval ac_new_val=\$ac_env_${ac_var}_value
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ # differences in whitespace do not lead to failure.
+ ac_old_val_w=`echo x $ac_old_val`
+ ac_new_val_w=`echo x $ac_new_val`
+ if test "$ac_old_val_w" != "$ac_new_val_w"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ ac_cache_corrupted=:
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+ eval $ac_var=\$ac_old_val
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5
+$as_echo "$as_me: former value: \`$ac_old_val'" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5
+$as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ac_aux_dir=
+for ac_dir in build "$srcdir"/build; do
+ if test -f "$ac_dir/install-sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f "$ac_dir/install.sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f "$ac_dir/shtool"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ as_fn_error $? "cannot find install-sh, install.sh, or shtool in build \"$srcdir\"/build" "$LINENO" 5
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
+
+
+
+eval `$ac_aux_dir/version.sh`
+if test -z "$OL_STRING"; then
+ as_fn_error $? "could not determine version" "$LINENO" 5
+fi
+
+if test -f "$ac_aux_dir/shtool" && test ! -d $ac_aux_dir/shtool; then
+ ac_cv_shtool="$ac_aux_dir/shtool"
+else
+ as_fn_error $? "no shtool found in $ac_aux_dir" "$LINENO" 5
+fi
+
+SHTOOL="$ac_cv_shtool"
+
+TB="" TN=""
+if test -t 1; then
+ TB="`$SHTOOL echo -e '%B' 2>/dev/null`"
+ TN="`$SHTOOL echo -e '%b' 2>/dev/null`"
+fi
+
+OPENLDAP_REPO=""
+if test -d $ac_aux_dir/../.git; then
+ OPENLDAP_REPO="(from Git clone) "
+elif test -d $ac_aux_dir/CVS; then
+ OPENLDAP_REPO="(from CVS checkout) "
+fi
+
+echo "Configuring ${TB}${OL_STRING}${TN} ${OPENLDAP_REPO}..."
+
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+ as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+$as_echo_n "checking build system type... " >&6; }
+if ${ac_cv_build+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+ ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+ as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+$as_echo "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+$as_echo_n "checking host system type... " >&6; }
+if ${ac_cv_host+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "x$host_alias" = x; then
+ ac_cv_host=$ac_cv_build
+else
+ ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+$as_echo "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking target system type" >&5
+$as_echo_n "checking target system type... " >&6; }
+if ${ac_cv_target+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "x$target_alias" = x; then
+ ac_cv_target=$ac_cv_host
+else
+ ac_cv_target=`$SHELL "$ac_aux_dir/config.sub" $target_alias` ||
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $target_alias failed" "$LINENO" 5
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_target" >&5
+$as_echo "$ac_cv_target" >&6; }
+case $ac_cv_target in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical target" "$LINENO" 5;;
+esac
+target=$ac_cv_target
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_target
+shift
+target_cpu=$1
+target_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+target_os=$*
+IFS=$ac_save_IFS
+case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac
+
+
+# The aliases save the names the user supplied, while $host etc.
+# will get canonicalized.
+test -n "$target_alias" &&
+ test "$program_prefix$program_suffix$program_transform_name" = \
+ NONENONEs,x,x, &&
+ program_prefix=${target_alias}-
+
+PACKAGE=$OL_PACKAGE
+VERSION=$OL_VERSION
+
+cat >>confdefs.h <<_ACEOF
+#define OPENLDAP_PACKAGE "$PACKAGE"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define OPENLDAP_VERSION "$VERSION"
+_ACEOF
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define LDAP_VENDOR_VERSION $OL_API_INC
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define LDAP_VENDOR_VERSION_MAJOR $OL_MAJOR
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define LDAP_VENDOR_VERSION_MINOR $OL_MINOR
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define LDAP_VENDOR_VERSION_PATCH $OL_PATCH
+_ACEOF
+
+
+OPENLDAP_LIBRELEASE=$OL_API_LIB_RELEASE
+
+OPENLDAP_LIBVERSION=$OL_API_LIB_VERSION
+
+OPENLDAP_RELEASE_DATE="$OL_RELEASE_DATE"
+
+
+
+
+
+ac_config_headers="$ac_config_headers include/portable.h:include/portable.hin"
+
+ac_config_headers="$ac_config_headers include/ldap_features.h:include/ldap_features.hin"
+
+ac_config_headers="$ac_config_headers include/lber_types.h:include/lber_types.hin"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking configure arguments" >&5
+$as_echo_n "checking configure arguments... " >&6; }
+
+
+top_builddir=`pwd`
+
+ldap_subdir="/openldap"
+
+
+# Check whether --with-subdir was given.
+if test "${with_subdir+set}" = set; then :
+ withval=$with_subdir; case "$withval" in
+ no) ldap_subdir=""
+ ;;
+ yes)
+ ;;
+ /*|\\*)
+ ldap_subdir="$withval"
+ ;;
+ *)
+ ldap_subdir="/$withval"
+ ;;
+esac
+
+fi
+
+
+# OpenLDAP --enable-debug
+
+ # Check whether --enable-debug was given.
+if test "${enable_debug+set}" = set; then :
+ enableval=$enable_debug;
+ ol_arg=invalid
+ for ol_val in no yes traditional ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-debug" "$LINENO" 5
+ fi
+ ol_enable_debug="$ol_arg"
+
+else
+ ol_enable_debug=yes
+fi
+
+# end --enable-debug
+# OpenLDAP --enable-dynamic
+
+ # Check whether --enable-dynamic was given.
+if test "${enable_dynamic+set}" = set; then :
+ enableval=$enable_dynamic;
+ ol_arg=invalid
+ for ol_val in auto yes no ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-dynamic" "$LINENO" 5
+ fi
+ ol_enable_dynamic="$ol_arg"
+
+else
+ ol_enable_dynamic=auto
+fi
+
+# end --enable-dynamic
+# OpenLDAP --enable-syslog
+
+ # Check whether --enable-syslog was given.
+if test "${enable_syslog+set}" = set; then :
+ enableval=$enable_syslog;
+ ol_arg=invalid
+ for ol_val in auto yes no ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-syslog" "$LINENO" 5
+ fi
+ ol_enable_syslog="$ol_arg"
+
+else
+ ol_enable_syslog=auto
+fi
+
+# end --enable-syslog
+ol_enable_referrals=${ol_enable_referrals-no}
+# OpenLDAP --enable-ipv6
+
+ # Check whether --enable-ipv6 was given.
+if test "${enable_ipv6+set}" = set; then :
+ enableval=$enable_ipv6;
+ ol_arg=invalid
+ for ol_val in auto yes no ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-ipv6" "$LINENO" 5
+ fi
+ ol_enable_ipv6="$ol_arg"
+
+else
+ ol_enable_ipv6=auto
+fi
+
+# end --enable-ipv6
+# OpenLDAP --enable-local
+
+ # Check whether --enable-local was given.
+if test "${enable_local+set}" = set; then :
+ enableval=$enable_local;
+ ol_arg=invalid
+ for ol_val in auto yes no ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-local" "$LINENO" 5
+ fi
+ ol_enable_local="$ol_arg"
+
+else
+ ol_enable_local=auto
+fi
+
+# end --enable-local
+
+# OpenLDAP --with-cyrus_sasl
+
+# Check whether --with-cyrus_sasl was given.
+if test "${with_cyrus_sasl+set}" = set; then :
+ withval=$with_cyrus_sasl;
+ ol_arg=invalid
+ for ol_val in auto yes no ; do
+ if test "$withval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $withval for --with-cyrus_sasl" "$LINENO" 5
+ fi
+ ol_with_cyrus_sasl="$ol_arg"
+
+else
+ ol_with_cyrus_sasl="auto"
+fi
+# end --with-cyrus_sasl
+
+# OpenLDAP --with-systemd
+
+# Check whether --with-systemd was given.
+if test "${with_systemd+set}" = set; then :
+ withval=$with_systemd;
+ ol_arg=invalid
+ for ol_val in auto yes no ; do
+ if test "$withval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $withval for --with-systemd" "$LINENO" 5
+ fi
+ ol_with_systemd="$ol_arg"
+
+else
+ ol_with_systemd="auto"
+fi
+# end --with-systemd
+
+# OpenLDAP --with-fetch
+
+# Check whether --with-fetch was given.
+if test "${with_fetch+set}" = set; then :
+ withval=$with_fetch;
+ ol_arg=invalid
+ for ol_val in auto yes no ; do
+ if test "$withval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $withval for --with-fetch" "$LINENO" 5
+ fi
+ ol_with_fetch="$ol_arg"
+
+else
+ ol_with_fetch="auto"
+fi
+# end --with-fetch
+
+# OpenLDAP --with-threads
+
+# Check whether --with-threads was given.
+if test "${with_threads+set}" = set; then :
+ withval=$with_threads;
+ ol_arg=invalid
+ for ol_val in auto nt posix pth lwp yes no manual ; do
+ if test "$withval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $withval for --with-threads" "$LINENO" 5
+ fi
+ ol_with_threads="$ol_arg"
+
+else
+ ol_with_threads="auto"
+fi
+# end --with-threads
+
+# OpenLDAP --with-tls
+
+# Check whether --with-tls was given.
+if test "${with_tls+set}" = set; then :
+ withval=$with_tls;
+ ol_arg=invalid
+ for ol_val in auto openssl gnutls yes no ; do
+ if test "$withval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $withval for --with-tls" "$LINENO" 5
+ fi
+ ol_with_tls="$ol_arg"
+
+else
+ ol_with_tls="auto"
+fi
+# end --with-tls
+
+# OpenLDAP --with-yielding_select
+
+# Check whether --with-yielding_select was given.
+if test "${with_yielding_select+set}" = set; then :
+ withval=$with_yielding_select;
+ ol_arg=invalid
+ for ol_val in auto yes no manual ; do
+ if test "$withval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $withval for --with-yielding_select" "$LINENO" 5
+ fi
+ ol_with_yielding_select="$ol_arg"
+
+else
+ ol_with_yielding_select="auto"
+fi
+# end --with-yielding_select
+
+# OpenLDAP --with-mp
+
+# Check whether --with-mp was given.
+if test "${with_mp+set}" = set; then :
+ withval=$with_mp;
+ ol_arg=invalid
+ for ol_val in auto longlong long bignum gmp yes no ; do
+ if test "$withval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $withval for --with-mp" "$LINENO" 5
+ fi
+ ol_with_mp="$ol_arg"
+
+else
+ ol_with_mp="auto"
+fi
+# end --with-mp
+
+# OpenLDAP --with-odbc
+
+# Check whether --with-odbc was given.
+if test "${with_odbc+set}" = set; then :
+ withval=$with_odbc;
+ ol_arg=invalid
+ for ol_val in auto iodbc unixodbc odbc32 ; do
+ if test "$withval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $withval for --with-odbc" "$LINENO" 5
+ fi
+ ol_with_odbc="$ol_arg"
+
+else
+ ol_with_odbc="auto"
+fi
+# end --with-odbc
+
+
+
+SlapdOptions="dynacl \
+ aci \
+ cleartext \
+ crypt \
+ spasswd \
+ modules \
+ rlookups \
+ slapi \
+ slp \
+ wrappers"
+
+# Check whether --enable-xxslapdoptions was given.
+if test "${enable_xxslapdoptions+set}" = set; then :
+ enableval=$enable_xxslapdoptions;
+fi
+
+
+# OpenLDAP --enable-slapd
+
+ # Check whether --enable-slapd was given.
+if test "${enable_slapd+set}" = set; then :
+ enableval=$enable_slapd;
+ ol_arg=invalid
+ for ol_val in auto yes no ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-slapd" "$LINENO" 5
+ fi
+ ol_enable_slapd="$ol_arg"
+
+else
+ ol_enable_slapd=yes
+fi
+
+# end --enable-slapd
+# OpenLDAP --enable-dynacl
+
+ # Check whether --enable-dynacl was given.
+if test "${enable_dynacl+set}" = set; then :
+ enableval=$enable_dynacl;
+ ol_arg=invalid
+ for ol_val in auto yes no ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-dynacl" "$LINENO" 5
+ fi
+ ol_enable_dynacl="$ol_arg"
+
+else
+ ol_enable_dynacl=no
+fi
+
+# end --enable-dynacl
+# OpenLDAP --enable-aci
+
+ # Check whether --enable-aci was given.
+if test "${enable_aci+set}" = set; then :
+ enableval=$enable_aci;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-aci" "$LINENO" 5
+ fi
+ ol_enable_aci="$ol_arg"
+
+else
+ ol_enable_aci=no
+fi
+
+# end --enable-aci
+# OpenLDAP --enable-cleartext
+
+ # Check whether --enable-cleartext was given.
+if test "${enable_cleartext+set}" = set; then :
+ enableval=$enable_cleartext;
+ ol_arg=invalid
+ for ol_val in auto yes no ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-cleartext" "$LINENO" 5
+ fi
+ ol_enable_cleartext="$ol_arg"
+
+else
+ ol_enable_cleartext=yes
+fi
+
+# end --enable-cleartext
+# OpenLDAP --enable-crypt
+
+ # Check whether --enable-crypt was given.
+if test "${enable_crypt+set}" = set; then :
+ enableval=$enable_crypt;
+ ol_arg=invalid
+ for ol_val in auto yes no ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-crypt" "$LINENO" 5
+ fi
+ ol_enable_crypt="$ol_arg"
+
+else
+ ol_enable_crypt=no
+fi
+
+# end --enable-crypt
+# OpenLDAP --enable-spasswd
+
+ # Check whether --enable-spasswd was given.
+if test "${enable_spasswd+set}" = set; then :
+ enableval=$enable_spasswd;
+ ol_arg=invalid
+ for ol_val in auto yes no ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-spasswd" "$LINENO" 5
+ fi
+ ol_enable_spasswd="$ol_arg"
+
+else
+ ol_enable_spasswd=no
+fi
+
+# end --enable-spasswd
+# OpenLDAP --enable-modules
+
+ # Check whether --enable-modules was given.
+if test "${enable_modules+set}" = set; then :
+ enableval=$enable_modules;
+ ol_arg=invalid
+ for ol_val in auto yes no ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-modules" "$LINENO" 5
+ fi
+ ol_enable_modules="$ol_arg"
+
+else
+ ol_enable_modules=no
+fi
+
+# end --enable-modules
+# OpenLDAP --enable-rlookups
+
+ # Check whether --enable-rlookups was given.
+if test "${enable_rlookups+set}" = set; then :
+ enableval=$enable_rlookups;
+ ol_arg=invalid
+ for ol_val in auto yes no ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-rlookups" "$LINENO" 5
+ fi
+ ol_enable_rlookups="$ol_arg"
+
+else
+ ol_enable_rlookups=no
+fi
+
+# end --enable-rlookups
+# OpenLDAP --enable-slapi
+
+ # Check whether --enable-slapi was given.
+if test "${enable_slapi+set}" = set; then :
+ enableval=$enable_slapi;
+ ol_arg=invalid
+ for ol_val in auto yes no ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-slapi" "$LINENO" 5
+ fi
+ ol_enable_slapi="$ol_arg"
+
+else
+ ol_enable_slapi=no
+fi
+
+# end --enable-slapi
+# OpenLDAP --enable-slp
+
+ # Check whether --enable-slp was given.
+if test "${enable_slp+set}" = set; then :
+ enableval=$enable_slp;
+ ol_arg=invalid
+ for ol_val in auto yes no ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-slp" "$LINENO" 5
+ fi
+ ol_enable_slp="$ol_arg"
+
+else
+ ol_enable_slp=no
+fi
+
+# end --enable-slp
+# OpenLDAP --enable-wrappers
+
+ # Check whether --enable-wrappers was given.
+if test "${enable_wrappers+set}" = set; then :
+ enableval=$enable_wrappers;
+ ol_arg=invalid
+ for ol_val in auto yes no ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-wrappers" "$LINENO" 5
+ fi
+ ol_enable_wrappers="$ol_arg"
+
+else
+ ol_enable_wrappers=no
+fi
+
+# end --enable-wrappers
+
+Backends="dnssrv \
+ ldap \
+ mdb \
+ meta \
+ asyncmeta \
+ ndb \
+ null \
+ passwd \
+ perl \
+ relay \
+ sock \
+ sql \
+ wt"
+
+# Check whether --enable-xxslapbackends was given.
+if test "${enable_xxslapbackends+set}" = set; then :
+ enableval=$enable_xxslapbackends;
+fi
+
+
+# OpenLDAP --enable-backends
+
+ # Check whether --enable-backends was given.
+if test "${enable_backends+set}" = set; then :
+ enableval=$enable_backends;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-backends" "$LINENO" 5
+ fi
+ ol_enable_backends="$ol_arg"
+
+fi
+
+# end --enable-backends
+# OpenLDAP --enable-dnssrv
+
+ # Check whether --enable-dnssrv was given.
+if test "${enable_dnssrv+set}" = set; then :
+ enableval=$enable_dnssrv;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-dnssrv" "$LINENO" 5
+ fi
+ ol_enable_dnssrv="$ol_arg"
+
+else
+ ol_enable_dnssrv=${ol_enable_backends:-no}
+fi
+
+# end --enable-dnssrv
+# OpenLDAP --enable-ldap
+
+ # Check whether --enable-ldap was given.
+if test "${enable_ldap+set}" = set; then :
+ enableval=$enable_ldap;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-ldap" "$LINENO" 5
+ fi
+ ol_enable_ldap="$ol_arg"
+
+else
+ ol_enable_ldap=${ol_enable_backends:-no}
+fi
+
+# end --enable-ldap
+# OpenLDAP --enable-mdb
+
+ # Check whether --enable-mdb was given.
+if test "${enable_mdb+set}" = set; then :
+ enableval=$enable_mdb;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-mdb" "$LINENO" 5
+ fi
+ ol_enable_mdb="$ol_arg"
+
+else
+ ol_enable_mdb=${ol_enable_backends:-yes}
+fi
+
+# end --enable-mdb
+# OpenLDAP --enable-meta
+
+ # Check whether --enable-meta was given.
+if test "${enable_meta+set}" = set; then :
+ enableval=$enable_meta;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-meta" "$LINENO" 5
+ fi
+ ol_enable_meta="$ol_arg"
+
+else
+ ol_enable_meta=${ol_enable_backends:-no}
+fi
+
+# end --enable-meta
+# OpenLDAP --enable-asyncmeta
+
+ # Check whether --enable-asyncmeta was given.
+if test "${enable_asyncmeta+set}" = set; then :
+ enableval=$enable_asyncmeta;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-asyncmeta" "$LINENO" 5
+ fi
+ ol_enable_asyncmeta="$ol_arg"
+
+else
+ ol_enable_asyncmeta=${ol_enable_backends:-no}
+fi
+
+# end --enable-asyncmeta
+# OpenLDAP --enable-ndb
+
+ # Check whether --enable-ndb was given.
+if test "${enable_ndb+set}" = set; then :
+ enableval=$enable_ndb;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-ndb" "$LINENO" 5
+ fi
+ ol_enable_ndb="$ol_arg"
+
+else
+ ol_enable_ndb=no
+fi
+
+# end --enable-ndb
+# OpenLDAP --enable-null
+
+ # Check whether --enable-null was given.
+if test "${enable_null+set}" = set; then :
+ enableval=$enable_null;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-null" "$LINENO" 5
+ fi
+ ol_enable_null="$ol_arg"
+
+else
+ ol_enable_null=${ol_enable_backends:-no}
+fi
+
+# end --enable-null
+# OpenLDAP --enable-passwd
+
+ # Check whether --enable-passwd was given.
+if test "${enable_passwd+set}" = set; then :
+ enableval=$enable_passwd;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-passwd" "$LINENO" 5
+ fi
+ ol_enable_passwd="$ol_arg"
+
+else
+ ol_enable_passwd=${ol_enable_backends:-no}
+fi
+
+# end --enable-passwd
+# OpenLDAP --enable-perl
+
+ # Check whether --enable-perl was given.
+if test "${enable_perl+set}" = set; then :
+ enableval=$enable_perl;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-perl" "$LINENO" 5
+ fi
+ ol_enable_perl="$ol_arg"
+
+else
+ ol_enable_perl=no
+fi
+
+# end --enable-perl
+# OpenLDAP --enable-relay
+
+ # Check whether --enable-relay was given.
+if test "${enable_relay+set}" = set; then :
+ enableval=$enable_relay;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-relay" "$LINENO" 5
+ fi
+ ol_enable_relay="$ol_arg"
+
+else
+ ol_enable_relay=${ol_enable_backends:-yes}
+fi
+
+# end --enable-relay
+# OpenLDAP --enable-sock
+
+ # Check whether --enable-sock was given.
+if test "${enable_sock+set}" = set; then :
+ enableval=$enable_sock;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-sock" "$LINENO" 5
+ fi
+ ol_enable_sock="$ol_arg"
+
+else
+ ol_enable_sock=${ol_enable_backends:-no}
+fi
+
+# end --enable-sock
+# OpenLDAP --enable-sql
+
+ # Check whether --enable-sql was given.
+if test "${enable_sql+set}" = set; then :
+ enableval=$enable_sql;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-sql" "$LINENO" 5
+ fi
+ ol_enable_sql="$ol_arg"
+
+else
+ ol_enable_sql=no
+fi
+
+# end --enable-sql
+# OpenLDAP --enable-wt
+
+ # Check whether --enable-wt was given.
+if test "${enable_wt+set}" = set; then :
+ enableval=$enable_wt;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-wt" "$LINENO" 5
+ fi
+ ol_enable_wt="$ol_arg"
+
+else
+ ol_enable_wt=${ol_enable_backends:-no}
+fi
+
+# end --enable-wt
+
+Overlays="accesslog \
+ auditlog \
+ autoca \
+ collect \
+ constraint \
+ dds \
+ deref \
+ dyngroup \
+ dynlist \
+ homedir \
+ memberof \
+ otp \
+ ppolicy \
+ proxycache \
+ refint \
+ remoteauth \
+ retcode \
+ rwm \
+ seqmod \
+ sssvlv \
+ syncprov \
+ translucent \
+ unique \
+ valsort"
+
+Pwmods="argon2"
+
+# Check whether --enable-xxslapoverlays was given.
+if test "${enable_xxslapoverlays+set}" = set; then :
+ enableval=$enable_xxslapoverlays;
+fi
+
+
+# OpenLDAP --enable-overlays
+
+ # Check whether --enable-overlays was given.
+if test "${enable_overlays+set}" = set; then :
+ enableval=$enable_overlays;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-overlays" "$LINENO" 5
+ fi
+ ol_enable_overlays="$ol_arg"
+
+fi
+
+# end --enable-overlays
+# OpenLDAP --enable-accesslog
+
+ # Check whether --enable-accesslog was given.
+if test "${enable_accesslog+set}" = set; then :
+ enableval=$enable_accesslog;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-accesslog" "$LINENO" 5
+ fi
+ ol_enable_accesslog="$ol_arg"
+
+else
+ ol_enable_accesslog=${ol_enable_overlays:-no}
+fi
+
+# end --enable-accesslog
+
+# OpenLDAP --enable-auditlog
+
+ # Check whether --enable-auditlog was given.
+if test "${enable_auditlog+set}" = set; then :
+ enableval=$enable_auditlog;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-auditlog" "$LINENO" 5
+ fi
+ ol_enable_auditlog="$ol_arg"
+
+else
+ ol_enable_auditlog=${ol_enable_overlays:-no}
+fi
+
+# end --enable-auditlog
+
+# OpenLDAP --enable-autoca
+
+ # Check whether --enable-autoca was given.
+if test "${enable_autoca+set}" = set; then :
+ enableval=$enable_autoca;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-autoca" "$LINENO" 5
+ fi
+ ol_enable_autoca="$ol_arg"
+
+else
+ ol_enable_autoca=${ol_enable_overlays:-no}
+fi
+
+# end --enable-autoca
+
+# OpenLDAP --enable-collect
+
+ # Check whether --enable-collect was given.
+if test "${enable_collect+set}" = set; then :
+ enableval=$enable_collect;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-collect" "$LINENO" 5
+ fi
+ ol_enable_collect="$ol_arg"
+
+else
+ ol_enable_collect=${ol_enable_overlays:-no}
+fi
+
+# end --enable-collect
+
+# OpenLDAP --enable-constraint
+
+ # Check whether --enable-constraint was given.
+if test "${enable_constraint+set}" = set; then :
+ enableval=$enable_constraint;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-constraint" "$LINENO" 5
+ fi
+ ol_enable_constraint="$ol_arg"
+
+else
+ ol_enable_constraint=${ol_enable_overlays:-no}
+fi
+
+# end --enable-constraint
+
+# OpenLDAP --enable-dds
+
+ # Check whether --enable-dds was given.
+if test "${enable_dds+set}" = set; then :
+ enableval=$enable_dds;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-dds" "$LINENO" 5
+ fi
+ ol_enable_dds="$ol_arg"
+
+else
+ ol_enable_dds=${ol_enable_overlays:-no}
+fi
+
+# end --enable-dds
+
+# OpenLDAP --enable-deref
+
+ # Check whether --enable-deref was given.
+if test "${enable_deref+set}" = set; then :
+ enableval=$enable_deref;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-deref" "$LINENO" 5
+ fi
+ ol_enable_deref="$ol_arg"
+
+else
+ ol_enable_deref=${ol_enable_overlays:-no}
+fi
+
+# end --enable-deref
+
+# OpenLDAP --enable-dyngroup
+
+ # Check whether --enable-dyngroup was given.
+if test "${enable_dyngroup+set}" = set; then :
+ enableval=$enable_dyngroup;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-dyngroup" "$LINENO" 5
+ fi
+ ol_enable_dyngroup="$ol_arg"
+
+else
+ ol_enable_dyngroup=${ol_enable_overlays:-no}
+fi
+
+# end --enable-dyngroup
+
+# OpenLDAP --enable-dynlist
+
+ # Check whether --enable-dynlist was given.
+if test "${enable_dynlist+set}" = set; then :
+ enableval=$enable_dynlist;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-dynlist" "$LINENO" 5
+ fi
+ ol_enable_dynlist="$ol_arg"
+
+else
+ ol_enable_dynlist=${ol_enable_overlays:-no}
+fi
+
+# end --enable-dynlist
+
+# OpenLDAP --enable-homedir
+
+ # Check whether --enable-homedir was given.
+if test "${enable_homedir+set}" = set; then :
+ enableval=$enable_homedir;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-homedir" "$LINENO" 5
+ fi
+ ol_enable_homedir="$ol_arg"
+
+else
+ ol_enable_homedir=${ol_enable_overlays:-no}
+fi
+
+# end --enable-homedir
+
+# OpenLDAP --enable-memberof
+
+ # Check whether --enable-memberof was given.
+if test "${enable_memberof+set}" = set; then :
+ enableval=$enable_memberof;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-memberof" "$LINENO" 5
+ fi
+ ol_enable_memberof="$ol_arg"
+
+else
+ ol_enable_memberof=${ol_enable_overlays:-no}
+fi
+
+# end --enable-memberof
+
+# OpenLDAP --enable-otp
+
+ # Check whether --enable-otp was given.
+if test "${enable_otp+set}" = set; then :
+ enableval=$enable_otp;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-otp" "$LINENO" 5
+ fi
+ ol_enable_otp="$ol_arg"
+
+else
+ ol_enable_otp=${ol_enable_overlays:-no}
+fi
+
+# end --enable-otp
+
+# OpenLDAP --enable-ppolicy
+
+ # Check whether --enable-ppolicy was given.
+if test "${enable_ppolicy+set}" = set; then :
+ enableval=$enable_ppolicy;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-ppolicy" "$LINENO" 5
+ fi
+ ol_enable_ppolicy="$ol_arg"
+
+else
+ ol_enable_ppolicy=${ol_enable_overlays:-no}
+fi
+
+# end --enable-ppolicy
+
+# OpenLDAP --enable-proxycache
+
+ # Check whether --enable-proxycache was given.
+if test "${enable_proxycache+set}" = set; then :
+ enableval=$enable_proxycache;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-proxycache" "$LINENO" 5
+ fi
+ ol_enable_proxycache="$ol_arg"
+
+else
+ ol_enable_proxycache=${ol_enable_overlays:-no}
+fi
+
+# end --enable-proxycache
+
+# OpenLDAP --enable-refint
+
+ # Check whether --enable-refint was given.
+if test "${enable_refint+set}" = set; then :
+ enableval=$enable_refint;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-refint" "$LINENO" 5
+ fi
+ ol_enable_refint="$ol_arg"
+
+else
+ ol_enable_refint=${ol_enable_overlays:-no}
+fi
+
+# end --enable-refint
+
+# OpenLDAP --enable-remoteauth
+
+ # Check whether --enable-remoteauth was given.
+if test "${enable_remoteauth+set}" = set; then :
+ enableval=$enable_remoteauth;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-remoteauth" "$LINENO" 5
+ fi
+ ol_enable_remoteauth="$ol_arg"
+
+else
+ ol_enable_remoteauth=${ol_enable_overlays:-no}
+fi
+
+# end --enable-remoteauth
+
+# OpenLDAP --enable-retcode
+
+ # Check whether --enable-retcode was given.
+if test "${enable_retcode+set}" = set; then :
+ enableval=$enable_retcode;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-retcode" "$LINENO" 5
+ fi
+ ol_enable_retcode="$ol_arg"
+
+else
+ ol_enable_retcode=${ol_enable_overlays:-no}
+fi
+
+# end --enable-retcode
+
+# OpenLDAP --enable-rwm
+
+ # Check whether --enable-rwm was given.
+if test "${enable_rwm+set}" = set; then :
+ enableval=$enable_rwm;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-rwm" "$LINENO" 5
+ fi
+ ol_enable_rwm="$ol_arg"
+
+else
+ ol_enable_rwm=${ol_enable_overlays:-no}
+fi
+
+# end --enable-rwm
+
+# OpenLDAP --enable-seqmod
+
+ # Check whether --enable-seqmod was given.
+if test "${enable_seqmod+set}" = set; then :
+ enableval=$enable_seqmod;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-seqmod" "$LINENO" 5
+ fi
+ ol_enable_seqmod="$ol_arg"
+
+else
+ ol_enable_seqmod=${ol_enable_overlays:-no}
+fi
+
+# end --enable-seqmod
+
+# OpenLDAP --enable-sssvlv
+
+ # Check whether --enable-sssvlv was given.
+if test "${enable_sssvlv+set}" = set; then :
+ enableval=$enable_sssvlv;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-sssvlv" "$LINENO" 5
+ fi
+ ol_enable_sssvlv="$ol_arg"
+
+else
+ ol_enable_sssvlv=${ol_enable_overlays:-no}
+fi
+
+# end --enable-sssvlv
+
+# OpenLDAP --enable-syncprov
+
+ # Check whether --enable-syncprov was given.
+if test "${enable_syncprov+set}" = set; then :
+ enableval=$enable_syncprov;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-syncprov" "$LINENO" 5
+ fi
+ ol_enable_syncprov="$ol_arg"
+
+else
+ ol_enable_syncprov=${ol_enable_overlays:-yes}
+fi
+
+# end --enable-syncprov
+
+# OpenLDAP --enable-translucent
+
+ # Check whether --enable-translucent was given.
+if test "${enable_translucent+set}" = set; then :
+ enableval=$enable_translucent;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-translucent" "$LINENO" 5
+ fi
+ ol_enable_translucent="$ol_arg"
+
+else
+ ol_enable_translucent=${ol_enable_overlays:-no}
+fi
+
+# end --enable-translucent
+
+# OpenLDAP --enable-unique
+
+ # Check whether --enable-unique was given.
+if test "${enable_unique+set}" = set; then :
+ enableval=$enable_unique;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-unique" "$LINENO" 5
+ fi
+ ol_enable_unique="$ol_arg"
+
+else
+ ol_enable_unique=${ol_enable_overlays:-no}
+fi
+
+# end --enable-unique
+
+# OpenLDAP --enable-valsort
+
+ # Check whether --enable-valsort was given.
+if test "${enable_valsort+set}" = set; then :
+ enableval=$enable_valsort;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-valsort" "$LINENO" 5
+ fi
+ ol_enable_valsort="$ol_arg"
+
+else
+ ol_enable_valsort=${ol_enable_overlays:-no}
+fi
+
+# end --enable-valsort
+
+
+# Check whether --enable-pwmodoptions was given.
+if test "${enable_pwmodoptions+set}" = set; then :
+ enableval=$enable_pwmodoptions;
+fi
+
+# OpenLDAP --enable-argon2
+
+ # Check whether --enable-argon2 was given.
+if test "${enable_argon2+set}" = set; then :
+ enableval=$enable_argon2;
+ ol_arg=invalid
+ for ol_val in no yes ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-argon2" "$LINENO" 5
+ fi
+ ol_enable_argon2="$ol_arg"
+
+else
+ ol_enable_argon2=${ol_enable_pwmodules:-no}
+fi
+
+# end --enable-argon2
+
+# OpenLDAP --with-argon2
+
+# Check whether --with-argon2 was given.
+if test "${with_argon2+set}" = set; then :
+ withval=$with_argon2;
+ ol_arg=invalid
+ for ol_val in auto libsodium libargon2 yes no ; do
+ if test "$withval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $withval for --with-argon2" "$LINENO" 5
+ fi
+ ol_with_argon2="$ol_arg"
+
+else
+ ol_with_argon2="auto"
+fi
+# end --with-argon2
+
+
+# Check whether --enable-balanceroptions was given.
+if test "${enable_balanceroptions+set}" = set; then :
+ enableval=$enable_balanceroptions;
+fi
+
+# OpenLDAP --enable-balancer
+
+ # Check whether --enable-balancer was given.
+if test "${enable_balancer+set}" = set; then :
+ enableval=$enable_balancer;
+ ol_arg=invalid
+ for ol_val in no yes mod ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-balancer" "$LINENO" 5
+ fi
+ ol_enable_balancer="$ol_arg"
+
+else
+ ol_enable_balancer=no
+fi
+
+# end --enable-balancer
+
+
+# Check whether --enable-xxliboptions was given.
+if test "${enable_xxliboptions+set}" = set; then :
+ enableval=$enable_xxliboptions;
+fi
+
+# Check whether --enable-static was given.
+if test "${enable_static+set}" = set; then :
+ enableval=$enable_static; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_static=yes ;;
+ no) enable_static=no ;;
+ *)
+ enable_static=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for pkg in $enableval; do
+ IFS=$lt_save_ifs
+ if test "X$pkg" = "X$p"; then
+ enable_static=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac
+else
+ enable_static=yes
+fi
+
+
+
+
+
+
+
+
+
+# Check whether --enable-shared was given.
+if test "${enable_shared+set}" = set; then :
+ enableval=$enable_shared; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_shared=yes ;;
+ no) enable_shared=no ;;
+ *)
+ enable_shared=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for pkg in $enableval; do
+ IFS=$lt_save_ifs
+ if test "X$pkg" = "X$p"; then
+ enable_shared=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac
+else
+ enable_shared=yes
+fi
+
+
+
+
+
+
+
+
+
+# OpenLDAP --enable-versioning
+
+ # Check whether --enable-versioning was given.
+if test "${enable_versioning+set}" = set; then :
+ enableval=$enable_versioning;
+ ol_arg=invalid
+ for ol_val in no yes auto ; do
+ if test "$enableval" = "$ol_val" ; then
+ ol_arg="$ol_val"
+ fi
+ done
+ if test "$ol_arg" = "invalid" ; then
+ as_fn_error $? "bad value $enableval for --enable-versioning" "$LINENO" 5
+ fi
+ ol_enable_versioning="$ol_arg"
+
+else
+ ol_enable_versioning=auto
+fi
+
+# end --enable-versioning
+
+
+if test $ol_enable_slapd = no ; then
+
+ for i in $SlapdOptions; do
+ eval "ol_tmp=\$ol_enable_$i"
+ if test $ol_tmp = yes ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: slapd disabled, ignoring --enable-$i argument" >&5
+$as_echo "$as_me: WARNING: slapd disabled, ignoring --enable-$i argument" >&2;}
+ eval "ol_enable_$i=no"
+ fi
+ done
+
+ for i in $Backends $Overlays $Pwmods; do
+ eval "ol_tmp=\$ol_enable_$i"
+ if test $ol_tmp != no ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: slapd disabled, ignoring --enable-$i argument" >&5
+$as_echo "$as_me: WARNING: slapd disabled, ignoring --enable-$i argument" >&2;}
+ eval "ol_enable_$i=no"
+ fi
+ done
+
+ if test $ol_enable_balancer = mod ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: slapd disabled, ignoring --enable-balancer=mod argument" >&5
+$as_echo "$as_me: WARNING: slapd disabled, ignoring --enable-balancer=mod argument" >&2;}
+ ol_enable_balancer=no
+ fi
+else
+
+ if test $ol_enable_modules = no; then
+
+ for i in backends overlays balancer $Backends $Overlays; do
+ eval "ol_tmp=\$ol_enable_$i"
+ if test -n "$ol_tmp" && test "$ol_tmp" = mod ; then
+ as_fn_error $? "--enable-$i=mod requires --enable-modules" "$LINENO" 5
+ fi
+ done
+
+ for i in $Pwmods; do
+ eval "ol_tmp=\$ol_enable_$i"
+ if test -n "$ol_tmp" && test "$ol_tmp" = yes ; then
+ as_fn_error $? "--enable-$i=yes requires --enable-modules" "$LINENO" 5
+ fi
+ done
+
+ ol_any_backend=no
+ for i in $Backends; do
+ eval "ol_tmp=\$ol_enable_$i"
+ if test $ol_tmp = yes; then
+ ol_any_backend=yes
+ fi
+ done
+
+ if test $ol_any_backend = no; then
+ as_fn_error $? "slapd requires a backend" "$LINENO" 5
+ fi
+ fi
+fi
+
+if test $ol_enable_aci = yes ; then
+ if test $ol_enable_dynacl = no ; then
+ as_fn_error $? "--enable-aci requires --enable-dynacl" "$LINENO" 5
+ fi
+elif test $ol_enable_aci = mod ; then
+ as_fn_error $? "ACI build as dynamic module not supported (yet)" "$LINENO" 5
+fi
+
+if test $ol_enable_modules = yes ; then
+ if test $ol_enable_dynamic = no ; then
+ as_fn_error $? "--enable-modules requires --enable-dynamic" "$LINENO" 5
+ fi
+ ol_enable_dynamic=yes
+fi
+
+if test $ol_enable_balancer != no ; then
+ if test $ol_with_threads = no ; then
+ as_fn_error $? "Load balancer requires threads" "$LINENO" 5
+ fi
+fi
+
+if test $ol_enable_spasswd = yes ; then
+ if test $ol_with_cyrus_sasl = no ; then
+ as_fn_error $? "--enable-spasswd requires --with-cyrus-sasl" "$LINENO" 5
+ fi
+ ol_with_cyrus_sasl=yes
+fi
+
+if test $ol_enable_meta/$ol_enable_ldap = yes/no ; then
+ as_fn_error $? "--enable-meta requires --enable-ldap" "$LINENO" 5
+fi
+
+if test $ol_enable_asyncmeta/$ol_enable_ldap = yes/no ; then
+ as_fn_error $? "--enable-asyncmeta requires --enable-ldap" "$LINENO" 5
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5
+$as_echo "done" >&6; }
+
+LDAP_LIBS=
+SLAPD_NDB_LIBS=
+SLAPD_NDB_INCS=
+LTHREAD_LIBS=
+LEVENT_LIBS=
+LUTIL_LIBS=
+
+CLIENT_LIBS=
+
+SLAPD_LIBS=
+BALANCER_LIBS=
+BALANCER_INCLUDE=
+
+BUILD_SLAPD=no
+BUILD_BALANCER=no
+
+BUILD_THREAD=no
+
+BUILD_SLAPI=no
+SLAPD_SLAPI_DEPEND=
+
+BUILD_DNSSRV=no
+BUILD_LDAP=no
+BUILD_MDB=no
+BUILD_META=no
+BUILD_ASYNCMETA=no
+BUILD_NDB=no
+BUILD_NULL=no
+BUILD_PASSWD=no
+BUILD_PERL=no
+BUILD_RELAY=no
+BUILD_SHELL=no
+BUILD_SOCK=no
+BUILD_SQL=no
+BUILD_WT=no
+
+BUILD_ACCESSLOG=no
+BUILD_AUDITLOG=no
+BUILD_AUTOCA=no
+BUILD_CONSTRAINT=no
+BUILD_DDS=no
+BUILD_DENYOP=no
+BUILD_DEREF=no
+BUILD_DYNGROUP=no
+BUILD_DYNLIST=no
+BUILD_LASTMOD=no
+BUILD_HOMEDIR=no
+BUILD_MEMBEROF=no
+BUILD_OTP=no
+BUILD_PPOLICY=no
+BUILD_PROXYCACHE=no
+BUILD_REFINT=no
+BUILD_REMOTEAUTH=no
+BUILD_RETCODE=no
+BUILD_RWM=no
+BUILD_SEQMOD=no
+BUILD_SSSVLV=no
+BUILD_SYNCPROV=no
+BUILD_TRANSLUCENT=no
+BUILD_UNIQUE=no
+BUILD_VALSORT=no
+
+BUILD_PW_ARGON2=no
+
+SLAPD_STATIC_OVERLAYS=
+SLAPD_DYNAMIC_OVERLAYS=
+
+SLAPD_DYNAMIC_PWMODS=
+
+SLAPD_MODULES_LDFLAGS=
+SLAPD_MODULES_CPPFLAGS=
+
+SLAPD_STATIC_BACKENDS="back-ldif back-monitor"
+SLAPD_DYNAMIC_BACKENDS=
+
+SLAPD_PERL_LDFLAGS=
+MOD_PERL_LDFLAGS=
+PERL_CPPFLAGS=
+
+SLAPD_SQL_LDFLAGS=
+SLAPD_SQL_LIBS=
+SLAPD_SQL_INCLUDES=
+
+SASL_LIBS=
+TLS_LIBS=
+WITH_TLS_TYPE=no
+MODULES_LIBS=
+SLAPI_LIBS=
+LIBSLAPI=
+AUTH_LIBS=
+SYSTEMD_LIBS=
+
+SLAPD_SLP_LIBS=
+SLAPD_GMP_LIBS=
+
+
+
+$as_echo "#define HAVE_MKVERSION 1" >>confdefs.h
+
+
+
+
+ol_aix_threads=no
+case "$target" in
+*-*-aix*) if test -z "$CC" ; then
+ case "$ol_with_threads" in
+ auto | yes | posix) ol_aix_threads=yes ;;
+ esac
+ fi
+;;
+esac
+
+if test $ol_aix_threads = yes ; then
+ if test -z "${CC}" ; then
+ for ac_prog in cc_r xlc_r cc
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+done
+
+
+ if test "$CC" = cc ; then
+ if test $ol_with_threads != auto ; then
+ as_fn_error $? "--with-threads requires cc_r (or other suitable compiler) on AIX" "$LINENO" 5
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: disabling threads, no cc_r on AIX" >&5
+$as_echo "$as_me: WARNING: disabling threads, no cc_r on AIX" >&2;}
+ fi
+ ol_with_threads=no
+ fi
+ fi
+
+ case ${CC} in cc_r | xlc_r)
+ ol_with_threads=posix
+ ol_cv_pthread_create=yes
+ ;;
+ esac
+fi
+
+if test -z "${CC}"; then
+ for ac_prog in cc gcc
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+done
+test -n "$CC" || CC="missing"
+
+
+ if test "${CC}" = "missing" ; then
+ as_fn_error $? "Unable to locate cc(1) or suitable replacement. Check PATH or set CC." "$LINENO" 5
+ fi
+fi
+
+if test -z "${AR}"; then
+ for ac_prog in ar gar
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AR"; then
+ ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_AR="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$AR" && break
+done
+test -n "$AR" || AR="missing"
+
+
+ if test "${AR}" = "missing" ; then
+ as_fn_error $? "Unable to locate ar(1) or suitable replacement. Check PATH or set AR." "$LINENO" 5
+ fi
+fi
+
+if test -z "${STRIP}"; then
+ for ac_prog in strip
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$STRIP"; then
+ ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_STRIP="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$STRIP" && break
+done
+test -n "$STRIP" || STRIP="missing"
+
+
+ if test "${STRIP}" = "missing" ; then
+ as_fn_error $? "Unable to locate strip(1) or suitable replacement. Check PATH or set STRIP." "$LINENO" 5
+ fi
+fi
+
+
+enable_win32_dll=yes
+
+case $host in
+*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*)
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}as", so it can be a program name with args.
+set dummy ${ac_tool_prefix}as; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AS+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AS"; then
+ ac_cv_prog_AS="$AS" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_AS="${ac_tool_prefix}as"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AS=$ac_cv_prog_AS
+if test -n "$AS"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AS" >&5
+$as_echo "$AS" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_AS"; then
+ ac_ct_AS=$AS
+ # Extract the first word of "as", so it can be a program name with args.
+set dummy as; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_AS+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_AS"; then
+ ac_cv_prog_ac_ct_AS="$ac_ct_AS" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_AS="as"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AS=$ac_cv_prog_ac_ct_AS
+if test -n "$ac_ct_AS"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AS" >&5
+$as_echo "$ac_ct_AS" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_AS" = x; then
+ AS="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ AS=$ac_ct_AS
+ fi
+else
+ AS="$ac_cv_prog_AS"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dlltool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DLLTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$DLLTOOL"; then
+ ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+DLLTOOL=$ac_cv_prog_DLLTOOL
+if test -n "$DLLTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5
+$as_echo "$DLLTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DLLTOOL"; then
+ ac_ct_DLLTOOL=$DLLTOOL
+ # Extract the first word of "dlltool", so it can be a program name with args.
+set dummy dlltool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_DLLTOOL"; then
+ ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_DLLTOOL="dlltool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL
+if test -n "$ac_ct_DLLTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5
+$as_echo "$ac_ct_DLLTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_DLLTOOL" = x; then
+ DLLTOOL="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ DLLTOOL=$ac_ct_DLLTOOL
+ fi
+else
+ DLLTOOL="$ac_cv_prog_DLLTOOL"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args.
+set dummy ${ac_tool_prefix}objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OBJDUMP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OBJDUMP"; then
+ ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OBJDUMP=$ac_cv_prog_OBJDUMP
+if test -n "$OBJDUMP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5
+$as_echo "$OBJDUMP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OBJDUMP"; then
+ ac_ct_OBJDUMP=$OBJDUMP
+ # Extract the first word of "objdump", so it can be a program name with args.
+set dummy objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OBJDUMP"; then
+ ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_OBJDUMP="objdump"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP
+if test -n "$ac_ct_OBJDUMP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5
+$as_echo "$ac_ct_OBJDUMP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OBJDUMP" = x; then
+ OBJDUMP="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OBJDUMP=$ac_ct_OBJDUMP
+ fi
+else
+ OBJDUMP="$ac_cv_prog_OBJDUMP"
+fi
+
+ ;;
+esac
+
+test -z "$AS" && AS=as
+
+
+
+
+
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+
+
+
+
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+
+
+
+
+
+
+
+enable_dlopen=yes
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+ @echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+ *@@@%%%=?*=@@@%%%*)
+ eval ac_cv_prog_make_${ac_make}_set=yes;;
+ *)
+ eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ SET_MAKE=
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+case `pwd` in
+ *\ * | *\ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5
+$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;;
+esac
+
+
+
+macro_version='2.4.6'
+macro_revision='2.4.6'
+
+
+
+
+
+
+
+
+
+
+
+
+
+ltmain=$ac_aux_dir/ltmain.sh
+
+# Backslashify metacharacters that are still active within
+# double-quoted strings.
+sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+
+ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5
+$as_echo_n "checking how to print strings... " >&6; }
+# Test print first, because it will be a builtin if present.
+if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \
+ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then
+ ECHO='print -r --'
+elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then
+ ECHO='printf %s\n'
+else
+ # Use this function as a fallback that always works.
+ func_fallback_echo ()
+ {
+ eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+ }
+ ECHO='func_fallback_echo'
+fi
+
+# func_echo_all arg...
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+ $ECHO ""
+}
+
+case $ECHO in
+ printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5
+$as_echo "printf" >&6; } ;;
+ print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5
+$as_echo "print -r" >&6; } ;;
+ *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5
+$as_echo "cat" >&6; } ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+ { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ sed '10a\
+... rest of stderr output deleted ...
+ 10q' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ fi
+ rm -f conftest.er1 conftest.err
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+ esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link_default") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile. We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+ then :; else
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ fi
+ # We set ac_cv_exeext here because the later test for it is not
+ # safe: cross compilers may not add the suffix if given an `-o'
+ # argument, so we may need to know it at that point already.
+ # Even if this section looks crufty: it has the advantage of
+ # actually working.
+ break;;
+ * )
+ break;;
+ esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+ ac_file=''
+fi
+if test -z "$ac_file"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+ { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ if { ac_try='./conftest$ac_cv_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ for ac_file in conftest.o conftest.obj conftest.*; do
+ test -f "$ac_file" || continue;
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_compiler_gnu=yes
+else
+ ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+else
+ CFLAGS=""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5
+$as_echo_n "checking for a sed that does not truncate output... " >&6; }
+if ${ac_cv_path_SED+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+ for ac_i in 1 2 3 4 5 6 7; do
+ ac_script="$ac_script$as_nl$ac_script"
+ done
+ echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
+ { ac_script=; unset ac_script;}
+ if test -z "$SED"; then
+ ac_path_SED_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in sed gsed; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_SED" || continue
+# Check for GNU ac_path_SED and select it if it is found.
+ # Check for GNU $ac_path_SED
+case `"$ac_path_SED" --version 2>&1` in
+*GNU*)
+ ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo '' >> "conftest.nl"
+ "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_SED_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_SED="$ac_path_SED"
+ ac_path_SED_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_SED_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_SED"; then
+ as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5
+ fi
+else
+ ac_cv_path_SED=$SED
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5
+$as_echo "$ac_cv_path_SED" >&6; }
+ SED="$ac_cv_path_SED"
+ rm -f conftest.sed
+
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if ${ac_cv_path_GREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$GREP"; then
+ ac_path_GREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in grep ggrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_GREP" || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+ # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'GREP' >> "conftest.nl"
+ "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_GREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_GREP="$ac_path_GREP"
+ ac_path_GREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_GREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_GREP"; then
+ as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if ${ac_cv_path_EGREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+ then ac_cv_path_EGREP="$GREP -E"
+ else
+ if test -z "$EGREP"; then
+ ac_path_EGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in egrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_EGREP" || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+ # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'EGREP' >> "conftest.nl"
+ "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_EGREP="$ac_path_EGREP"
+ ac_path_EGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_EGREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_EGREP"; then
+ as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_EGREP=$EGREP
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5
+$as_echo_n "checking for fgrep... " >&6; }
+if ${ac_cv_path_FGREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1
+ then ac_cv_path_FGREP="$GREP -F"
+ else
+ if test -z "$FGREP"; then
+ ac_path_FGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in fgrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_FGREP" || continue
+# Check for GNU ac_path_FGREP and select it if it is found.
+ # Check for GNU $ac_path_FGREP
+case `"$ac_path_FGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'FGREP' >> "conftest.nl"
+ "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_FGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_FGREP="$ac_path_FGREP"
+ ac_path_FGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_FGREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_FGREP"; then
+ as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_FGREP=$FGREP
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5
+$as_echo "$ac_cv_path_FGREP" >&6; }
+ FGREP="$ac_cv_path_FGREP"
+
+
+test -z "$GREP" && GREP=grep
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then :
+ withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes
+else
+ with_gnu_ld=no
+fi
+
+ac_prog=ld
+if test yes = "$GCC"; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5
+$as_echo_n "checking for ld used by $CC... " >&6; }
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return, which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [\\/]* | ?:[\\/]*)
+ re_direlt='/[^/][^/]*/\.\./'
+ # Canonicalize the pathname of ld
+ ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+ while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD=$ac_prog
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test yes = "$with_gnu_ld"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5
+$as_echo_n "checking for GNU ld... " >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5
+$as_echo_n "checking for non-GNU ld... " >&6; }
+fi
+if ${lt_cv_path_LD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$LD"; then
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ lt_cv_path_LD=$ac_dir/$ac_prog
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some variants of GNU ld only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+ *GNU* | *'with BFD'*)
+ test no != "$with_gnu_ld" && break
+ ;;
+ *)
+ test yes != "$with_gnu_ld" && break
+ ;;
+ esac
+ fi
+ done
+ IFS=$lt_save_ifs
+else
+ lt_cv_path_LD=$LD # Let the user override the test with a path.
+fi
+fi
+
+LD=$lt_cv_path_LD
+if test -n "$LD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
+$as_echo "$LD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
+$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
+if ${lt_cv_prog_gnu_ld+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ lt_cv_prog_gnu_ld=yes
+ ;;
+*)
+ lt_cv_prog_gnu_ld=no
+ ;;
+esac
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5
+$as_echo "$lt_cv_prog_gnu_ld" >&6; }
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5
+$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; }
+if ${lt_cv_path_NM+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$NM"; then
+ # Let the user override the test.
+ lt_cv_path_NM=$NM
+else
+ lt_nm_to_check=${ac_tool_prefix}nm
+ if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+ lt_nm_to_check="$lt_nm_to_check nm"
+ fi
+ for lt_tmp_nm in $lt_nm_to_check; do
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ tmp_nm=$ac_dir/$lt_tmp_nm
+ if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then
+ # Check to see if the nm accepts a BSD-compat flag.
+ # Adding the 'sed 1q' prevents false positives on HP-UX, which says:
+ # nm: unknown option "B" ignored
+ # Tru64's nm complains that /dev/null is an invalid object file
+ # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty
+ case $build_os in
+ mingw*) lt_bad_file=conftest.nm/nofile ;;
+ *) lt_bad_file=/dev/null ;;
+ esac
+ case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in
+ *$lt_bad_file* | *'Invalid file or object type'*)
+ lt_cv_path_NM="$tmp_nm -B"
+ break 2
+ ;;
+ *)
+ case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+ */dev/null*)
+ lt_cv_path_NM="$tmp_nm -p"
+ break 2
+ ;;
+ *)
+ lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+ continue # so that we can try to find one that supports BSD flags
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ done
+ IFS=$lt_save_ifs
+ done
+ : ${lt_cv_path_NM=no}
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5
+$as_echo "$lt_cv_path_NM" >&6; }
+if test no != "$lt_cv_path_NM"; then
+ NM=$lt_cv_path_NM
+else
+ # Didn't find any BSD compatible name lister, look for dumpbin.
+ if test -n "$DUMPBIN"; then :
+ # Let the user override the test.
+ else
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in dumpbin "link -dump"
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DUMPBIN+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$DUMPBIN"; then
+ ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+DUMPBIN=$ac_cv_prog_DUMPBIN
+if test -n "$DUMPBIN"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5
+$as_echo "$DUMPBIN" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$DUMPBIN" && break
+ done
+fi
+if test -z "$DUMPBIN"; then
+ ac_ct_DUMPBIN=$DUMPBIN
+ for ac_prog in dumpbin "link -dump"
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_DUMPBIN"; then
+ ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_DUMPBIN="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN
+if test -n "$ac_ct_DUMPBIN"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5
+$as_echo "$ac_ct_DUMPBIN" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_DUMPBIN" && break
+done
+
+ if test "x$ac_ct_DUMPBIN" = x; then
+ DUMPBIN=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ DUMPBIN=$ac_ct_DUMPBIN
+ fi
+fi
+
+ case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in
+ *COFF*)
+ DUMPBIN="$DUMPBIN -symbols -headers"
+ ;;
+ *)
+ DUMPBIN=:
+ ;;
+ esac
+ fi
+
+ if test : != "$DUMPBIN"; then
+ NM=$DUMPBIN
+ fi
+fi
+test -z "$NM" && NM=nm
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5
+$as_echo_n "checking the name lister ($NM) interface... " >&6; }
+if ${lt_cv_nm_interface+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_nm_interface="BSD nm"
+ echo "int some_variable = 0;" > conftest.$ac_ext
+ (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5)
+ (eval "$ac_compile" 2>conftest.err)
+ cat conftest.err >&5
+ (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+ (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+ cat conftest.err >&5
+ (eval echo "\"\$as_me:$LINENO: output\"" >&5)
+ cat conftest.out >&5
+ if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+ lt_cv_nm_interface="MS dumpbin"
+ fi
+ rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5
+$as_echo "$lt_cv_nm_interface" >&6; }
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
+$as_echo_n "checking whether ln -s works... " >&6; }
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
+$as_echo "no, using $LN_S" >&6; }
+fi
+
+# find the maximum length of command line arguments
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5
+$as_echo_n "checking the maximum length of command line arguments... " >&6; }
+if ${lt_cv_sys_max_cmd_len+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ i=0
+ teststring=ABCD
+
+ case $build_os in
+ msdosdjgpp*)
+ # On DJGPP, this test can blow up pretty badly due to problems in libc
+ # (any single argument exceeding 2000 bytes causes a buffer overrun
+ # during glob expansion). Even if it were fixed, the result of this
+ # check would be larger than it should be.
+ lt_cv_sys_max_cmd_len=12288; # 12K is about right
+ ;;
+
+ gnu*)
+ # Under GNU Hurd, this test is not required because there is
+ # no limit to the length of command line arguments.
+ # Libtool will interpret -1 as no limit whatsoever
+ lt_cv_sys_max_cmd_len=-1;
+ ;;
+
+ cygwin* | mingw* | cegcc*)
+ # On Win9x/ME, this test blows up -- it succeeds, but takes
+ # about 5 minutes as the teststring grows exponentially.
+ # Worse, since 9x/ME are not pre-emptively multitasking,
+ # you end up with a "frozen" computer, even though with patience
+ # the test eventually succeeds (with a max line length of 256k).
+ # Instead, let's just punt: use the minimum linelength reported by
+ # all of the supported platforms: 8192 (on NT/2K/XP).
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ mint*)
+ # On MiNT this can take a long time and run out of memory.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ amigaos*)
+ # On AmigaOS with pdksh, this test takes hours, literally.
+ # So we just punt and use a minimum line length of 8192.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*)
+ # This has been around since 386BSD, at least. Likely further.
+ if test -x /sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+ elif test -x /usr/sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+ else
+ lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs
+ fi
+ # And add a safety zone
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ ;;
+
+ interix*)
+ # We know the value 262144 and hardcode it with a safety zone (like BSD)
+ lt_cv_sys_max_cmd_len=196608
+ ;;
+
+ os2*)
+ # The test takes a long time on OS/2.
+ lt_cv_sys_max_cmd_len=8192
+ ;;
+
+ osf*)
+ # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+ # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+ # nice to cause kernel panics so lets avoid the loop below.
+ # First set a reasonable default.
+ lt_cv_sys_max_cmd_len=16384
+ #
+ if test -x /sbin/sysconfig; then
+ case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+ *1*) lt_cv_sys_max_cmd_len=-1 ;;
+ esac
+ fi
+ ;;
+ sco3.2v5*)
+ lt_cv_sys_max_cmd_len=102400
+ ;;
+ sysv5* | sco5v6* | sysv4.2uw2*)
+ kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+ if test -n "$kargmax"; then
+ lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'`
+ else
+ lt_cv_sys_max_cmd_len=32768
+ fi
+ ;;
+ *)
+ lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+ if test -n "$lt_cv_sys_max_cmd_len" && \
+ test undefined != "$lt_cv_sys_max_cmd_len"; then
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ else
+ # Make teststring a little bigger before we do anything with it.
+ # a 1K string should be a reasonable start.
+ for i in 1 2 3 4 5 6 7 8; do
+ teststring=$teststring$teststring
+ done
+ SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+ # If test is not a shell built-in, we'll probably end up computing a
+ # maximum length that is only half of the actual maximum length, but
+ # we can't tell.
+ while { test X`env echo "$teststring$teststring" 2>/dev/null` \
+ = "X$teststring$teststring"; } >/dev/null 2>&1 &&
+ test 17 != "$i" # 1/2 MB should be enough
+ do
+ i=`expr $i + 1`
+ teststring=$teststring$teststring
+ done
+ # Only check the string length outside the loop.
+ lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+ teststring=
+ # Add a significant safety factor because C++ compilers can tack on
+ # massive amounts of additional arguments before passing them to the
+ # linker. It appears as though 1/2 is a usable value.
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+ fi
+ ;;
+ esac
+
+fi
+
+if test -n "$lt_cv_sys_max_cmd_len"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5
+$as_echo "$lt_cv_sys_max_cmd_len" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5
+$as_echo "none" >&6; }
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+
+
+
+
+
+
+: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ lt_unset=unset
+else
+ lt_unset=false
+fi
+
+
+
+
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+ # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+ lt_SP2NL='tr \040 \012'
+ lt_NL2SP='tr \015\012 \040\040'
+ ;;
+ *) # EBCDIC based system
+ lt_SP2NL='tr \100 \n'
+ lt_NL2SP='tr \r\n \100\100'
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5
+$as_echo_n "checking how to convert $build file names to $host format... " >&6; }
+if ${lt_cv_to_host_file_cmd+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $host in
+ *-*-mingw* )
+ case $build in
+ *-*-mingw* ) # actually msys
+ lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32
+ ;;
+ *-*-cygwin* )
+ lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32
+ ;;
+ * ) # otherwise, assume *nix
+ lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32
+ ;;
+ esac
+ ;;
+ *-*-cygwin* )
+ case $build in
+ *-*-mingw* ) # actually msys
+ lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin
+ ;;
+ *-*-cygwin* )
+ lt_cv_to_host_file_cmd=func_convert_file_noop
+ ;;
+ * ) # otherwise, assume *nix
+ lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin
+ ;;
+ esac
+ ;;
+ * ) # unhandled hosts (and "normal" native builds)
+ lt_cv_to_host_file_cmd=func_convert_file_noop
+ ;;
+esac
+
+fi
+
+to_host_file_cmd=$lt_cv_to_host_file_cmd
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5
+$as_echo "$lt_cv_to_host_file_cmd" >&6; }
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5
+$as_echo_n "checking how to convert $build file names to toolchain format... " >&6; }
+if ${lt_cv_to_tool_file_cmd+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ #assume ordinary cross tools, or native build.
+lt_cv_to_tool_file_cmd=func_convert_file_noop
+case $host in
+ *-*-mingw* )
+ case $build in
+ *-*-mingw* ) # actually msys
+ lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32
+ ;;
+ esac
+ ;;
+esac
+
+fi
+
+to_tool_file_cmd=$lt_cv_to_tool_file_cmd
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5
+$as_echo "$lt_cv_to_tool_file_cmd" >&6; }
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5
+$as_echo_n "checking for $LD option to reload object files... " >&6; }
+if ${lt_cv_ld_reload_flag+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ld_reload_flag='-r'
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5
+$as_echo "$lt_cv_ld_reload_flag" >&6; }
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ if test yes != "$GCC"; then
+ reload_cmds=false
+ fi
+ ;;
+ darwin*)
+ if test yes = "$GCC"; then
+ reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs'
+ else
+ reload_cmds='$LD$reload_flag -o $output$reload_objs'
+ fi
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args.
+set dummy ${ac_tool_prefix}objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OBJDUMP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OBJDUMP"; then
+ ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OBJDUMP=$ac_cv_prog_OBJDUMP
+if test -n "$OBJDUMP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5
+$as_echo "$OBJDUMP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OBJDUMP"; then
+ ac_ct_OBJDUMP=$OBJDUMP
+ # Extract the first word of "objdump", so it can be a program name with args.
+set dummy objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OBJDUMP"; then
+ ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_OBJDUMP="objdump"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP
+if test -n "$ac_ct_OBJDUMP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5
+$as_echo "$ac_ct_OBJDUMP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OBJDUMP" = x; then
+ OBJDUMP="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OBJDUMP=$ac_ct_OBJDUMP
+ fi
+else
+ OBJDUMP="$ac_cv_prog_OBJDUMP"
+fi
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5
+$as_echo_n "checking how to recognize dependent libraries... " >&6; }
+if ${lt_cv_deplibs_check_method+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# 'unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# that responds to the $file_magic_cmd with a given extended regex.
+# If you have 'file' or equivalent on your system and you're not sure
+# whether 'pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[4-9]*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+beos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+bsdi[45]*)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)'
+ lt_cv_file_magic_cmd='/usr/bin/file -L'
+ lt_cv_file_magic_test_file=/shlib/libc.so
+ ;;
+
+cygwin*)
+ # func_win32_libid is a shell function defined in ltmain.sh
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ ;;
+
+mingw* | pw32*)
+ # Base MSYS/MinGW do not provide the 'file' command needed by
+ # func_win32_libid shell function, so use a weaker test based on 'objdump',
+ # unless we find 'file', for example because we are cross-compiling.
+ if ( file / ) >/dev/null 2>&1; then
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ else
+ # Keep this pattern in sync with the one in func_win32_libid.
+ lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ fi
+ ;;
+
+cegcc*)
+ # use the weaker test based on 'objdump'. See mingw*.
+ lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ ;;
+
+darwin* | rhapsody*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+freebsd* | dragonfly*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ case $host_cpu in
+ i*86 )
+ # Not sure whether the presence of OpenBSD here was a mistake.
+ # Let's accept both of them until this is cleared up.
+ lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+ ;;
+ esac
+ else
+ lt_cv_deplibs_check_method=pass_all
+ fi
+ ;;
+
+haiku*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+hpux10.20* | hpux11*)
+ lt_cv_file_magic_cmd=/usr/bin/file
+ case $host_cpu in
+ ia64*)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64'
+ lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+ ;;
+ hppa*64*)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'
+ lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+ ;;
+ *)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library'
+ lt_cv_file_magic_test_file=/usr/lib/libc.sl
+ ;;
+ esac
+ ;;
+
+interix[3-9]*)
+ # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$'
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $LD in
+ *-32|*"-32 ") libmagic=32-bit;;
+ *-n32|*"-n32 ") libmagic=N32;;
+ *-64|*"-64 ") libmagic=64-bit;;
+ *) libmagic=never-match;;
+ esac
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$'
+ fi
+ ;;
+
+newos6*)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=/usr/lib/libnls.so
+ ;;
+
+*nto* | *qnx*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+openbsd* | bitrig*)
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+ fi
+ ;;
+
+osf3* | osf4* | osf5*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+rdos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+solaris*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv4 | sysv4.3*)
+ case $host_vendor in
+ motorola)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]'
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+ ;;
+ ncr)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ sequent)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'
+ ;;
+ sni)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib"
+ lt_cv_file_magic_test_file=/lib/libc.so
+ ;;
+ siemens)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ pc)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ esac
+ ;;
+
+tpf*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+os2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+esac
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5
+$as_echo "$lt_cv_deplibs_check_method" >&6; }
+
+file_magic_glob=
+want_nocaseglob=no
+if test "$build" = "$host"; then
+ case $host_os in
+ mingw* | pw32*)
+ if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then
+ want_nocaseglob=yes
+ else
+ file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"`
+ fi
+ ;;
+ esac
+fi
+
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dlltool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DLLTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$DLLTOOL"; then
+ ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+DLLTOOL=$ac_cv_prog_DLLTOOL
+if test -n "$DLLTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5
+$as_echo "$DLLTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DLLTOOL"; then
+ ac_ct_DLLTOOL=$DLLTOOL
+ # Extract the first word of "dlltool", so it can be a program name with args.
+set dummy dlltool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_DLLTOOL"; then
+ ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_DLLTOOL="dlltool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL
+if test -n "$ac_ct_DLLTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5
+$as_echo "$ac_ct_DLLTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_DLLTOOL" = x; then
+ DLLTOOL="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ DLLTOOL=$ac_ct_DLLTOOL
+ fi
+else
+ DLLTOOL="$ac_cv_prog_DLLTOOL"
+fi
+
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5
+$as_echo_n "checking how to associate runtime and link libraries... " >&6; }
+if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_sharedlib_from_linklib_cmd='unknown'
+
+case $host_os in
+cygwin* | mingw* | pw32* | cegcc*)
+ # two different shell functions defined in ltmain.sh;
+ # decide which one to use based on capabilities of $DLLTOOL
+ case `$DLLTOOL --help 2>&1` in
+ *--identify-strict*)
+ lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib
+ ;;
+ *)
+ lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback
+ ;;
+ esac
+ ;;
+*)
+ # fallback: assume linklib IS sharedlib
+ lt_cv_sharedlib_from_linklib_cmd=$ECHO
+ ;;
+esac
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5
+$as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; }
+sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd
+test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ for ac_prog in ar
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AR"; then
+ ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_AR="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$AR" && break
+ done
+fi
+if test -z "$AR"; then
+ ac_ct_AR=$AR
+ for ac_prog in ar
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_AR"; then
+ ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_AR="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
+$as_echo "$ac_ct_AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_AR" && break
+done
+
+ if test "x$ac_ct_AR" = x; then
+ AR="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ AR=$ac_ct_AR
+ fi
+fi
+
+: ${AR=ar}
+: ${AR_FLAGS=cru}
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5
+$as_echo_n "checking for archiver @FILE support... " >&6; }
+if ${lt_cv_ar_at_file+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ar_at_file=no
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ echo conftest.$ac_objext > conftest.lst
+ lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5'
+ { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5
+ (eval $lt_ar_try) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ if test 0 -eq "$ac_status"; then
+ # Ensure the archiver fails upon bogus file names.
+ rm -f conftest.$ac_objext libconftest.a
+ { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5
+ (eval $lt_ar_try) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ if test 0 -ne "$ac_status"; then
+ lt_cv_ar_at_file=@
+ fi
+ fi
+ rm -f conftest.* libconftest.a
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5
+$as_echo "$lt_cv_ar_at_file" >&6; }
+
+if test no = "$lt_cv_ar_at_file"; then
+ archiver_list_spec=
+else
+ archiver_list_spec=$lt_cv_ar_at_file
+fi
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$STRIP"; then
+ ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+ ac_ct_STRIP=$STRIP
+ # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_STRIP"; then
+ ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_STRIP="strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_STRIP" = x; then
+ STRIP=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ STRIP=$ac_ct_STRIP
+ fi
+else
+ STRIP="$ac_cv_prog_STRIP"
+fi
+
+test -z "$STRIP" && STRIP=:
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_RANLIB" = x; then
+ RANLIB=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ RANLIB=$ac_ct_RANLIB
+ fi
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+test -z "$RANLIB" && RANLIB=:
+
+
+
+
+
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+ case $host_os in
+ bitrig* | openbsd*)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib"
+ ;;
+ *)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib"
+ ;;
+ esac
+ old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib"
+fi
+
+case $host_os in
+ darwin*)
+ lock_old_archive_extraction=yes ;;
+ *)
+ lock_old_archive_extraction=no ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+for ac_prog in gawk mawk nawk awk
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AWK+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_AWK="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
+$as_echo "$AWK" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$AWK" && break
+done
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5
+$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; }
+if ${lt_cv_sys_global_symbol_pipe+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix. What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[BCDEGRST]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([_A-Za-z][_A-Za-z0-9]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+ symcode='[BCDT]'
+ ;;
+cygwin* | mingw* | pw32* | cegcc*)
+ symcode='[ABCDGISTW]'
+ ;;
+hpux*)
+ if test ia64 = "$host_cpu"; then
+ symcode='[ABCDEGRST]'
+ fi
+ ;;
+irix* | nonstopux*)
+ symcode='[BCDEGRST]'
+ ;;
+osf*)
+ symcode='[BCDEGQRST]'
+ ;;
+solaris*)
+ symcode='[BDRT]'
+ ;;
+sco3.2v5*)
+ symcode='[DT]'
+ ;;
+sysv4.2uw2*)
+ symcode='[DT]'
+ ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+ symcode='[ABDT]'
+ ;;
+sysv4)
+ symcode='[DFNSTU]'
+ ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+ symcode='[ABCDGIRSTW]' ;;
+esac
+
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Gets list of data symbols to import.
+ lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'"
+ # Adjust the below global symbol transforms to fixup imported variables.
+ lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'"
+ lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'"
+ lt_c_name_lib_hook="\
+ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\
+ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'"
+else
+ # Disable hooks by default.
+ lt_cv_sys_global_symbol_to_import=
+ lt_cdecl_hook=
+ lt_c_name_hook=
+ lt_c_name_lib_hook=
+fi
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n"\
+$lt_cdecl_hook\
+" -e 's/^T .* \(.*\)$/extern int \1();/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n"\
+$lt_c_name_hook\
+" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'"
+
+# Transform an extracted symbol line into symbol name with lib prefix and
+# symbol address.
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\
+$lt_c_name_lib_hook\
+" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+ opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+ ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+ # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+ symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+ # Write the raw and C identifiers.
+ if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Fake it for dumpbin and say T for any non-static function,
+ # D for any global variable and I for any imported variable.
+ # Also find C++ and __fastcall symbols from MSVC++,
+ # which start with @ or ?.
+ lt_cv_sys_global_symbol_pipe="$AWK '"\
+" {last_section=section; section=\$ 3};"\
+" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\
+" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+" /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\
+" /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\
+" /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\
+" \$ 0!~/External *\|/{next};"\
+" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+" {if(hide[section]) next};"\
+" {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\
+" {split(\$ 0,a,/\||\r/); split(a[2],s)};"\
+" s[1]~/^[@?]/{print f,s[1],s[1]; next};"\
+" s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\
+" ' prfx=^$ac_symprfx"
+ else
+ lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+ fi
+ lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'"
+
+ # Check to see that the pipe works correctly.
+ pipe_works=no
+
+ rm -f conftest*
+ cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ # Now try to grab the symbols.
+ nlist=conftest.nm
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5
+ (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s "$nlist"; then
+ # Try sorting and uniquifying the output.
+ if sort "$nlist" | uniq > "$nlist"T; then
+ mv -f "$nlist"T "$nlist"
+ else
+ rm -f "$nlist"T
+ fi
+
+ # Make sure that we snagged all the symbols we need.
+ if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+ if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+ cat <<_LT_EOF > conftest.$ac_ext
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */
+#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE
+/* DATA imports from DLLs on WIN32 can't be const, because runtime
+ relocations are performed -- see ld's documentation on pseudo-relocs. */
+# define LT_DLSYM_CONST
+#elif defined __osf__
+/* This system does not cope well with relocations in const data. */
+# define LT_DLSYM_CONST
+#else
+# define LT_DLSYM_CONST const
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+ # Now generate the symbol file.
+ eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+ cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols. */
+LT_DLSYM_CONST struct {
+ const char *name;
+ void *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[] =
+{
+ { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+ $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+ cat <<\_LT_EOF >> conftest.$ac_ext
+ {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+ # Now try linking the two files.
+ mv conftest.$ac_objext conftstm.$ac_objext
+ lt_globsym_save_LIBS=$LIBS
+ lt_globsym_save_CFLAGS=$CFLAGS
+ LIBS=conftstm.$ac_objext
+ CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag"
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s conftest$ac_exeext; then
+ pipe_works=yes
+ fi
+ LIBS=$lt_globsym_save_LIBS
+ CFLAGS=$lt_globsym_save_CFLAGS
+ else
+ echo "cannot find nm_test_func in $nlist" >&5
+ fi
+ else
+ echo "cannot find nm_test_var in $nlist" >&5
+ fi
+ else
+ echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5
+ fi
+ else
+ echo "$progname: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ fi
+ rm -rf conftest* conftst*
+
+ # Do not use the global_symbol_pipe unless it works.
+ if test yes = "$pipe_works"; then
+ break
+ else
+ lt_cv_sys_global_symbol_pipe=
+ fi
+done
+
+fi
+
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+ lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
+$as_echo "failed" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
+$as_echo "ok" >&6; }
+fi
+
+# Response file support.
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ nm_file_list_spec='@'
+elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then
+ nm_file_list_spec='@'
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5
+$as_echo_n "checking for sysroot... " >&6; }
+
+# Check whether --with-sysroot was given.
+if test "${with_sysroot+set}" = set; then :
+ withval=$with_sysroot;
+else
+ with_sysroot=no
+fi
+
+
+lt_sysroot=
+case $with_sysroot in #(
+ yes)
+ if test yes = "$GCC"; then
+ lt_sysroot=`$CC --print-sysroot 2>/dev/null`
+ fi
+ ;; #(
+ /*)
+ lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"`
+ ;; #(
+ no|'')
+ ;; #(
+ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_sysroot" >&5
+$as_echo "$with_sysroot" >&6; }
+ as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5
+ ;;
+esac
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5
+$as_echo "${lt_sysroot:-no}" >&6; }
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a working dd" >&5
+$as_echo_n "checking for a working dd... " >&6; }
+if ${ac_cv_path_lt_DD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+: ${lt_DD:=$DD}
+if test -z "$lt_DD"; then
+ ac_path_lt_DD_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in dd; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_lt_DD="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_lt_DD" || continue
+if "$ac_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
+ cmp -s conftest.i conftest.out \
+ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=:
+fi
+ $ac_path_lt_DD_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_lt_DD"; then
+ :
+ fi
+else
+ ac_cv_path_lt_DD=$lt_DD
+fi
+
+rm -f conftest.i conftest2.i conftest.out
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_lt_DD" >&5
+$as_echo "$ac_cv_path_lt_DD" >&6; }
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to truncate binary pipes" >&5
+$as_echo_n "checking how to truncate binary pipes... " >&6; }
+if ${lt_cv_truncate_bin+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+lt_cv_truncate_bin=
+if "$ac_cv_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
+ cmp -s conftest.i conftest.out \
+ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1"
+fi
+rm -f conftest.i conftest2.i conftest.out
+test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_truncate_bin" >&5
+$as_echo "$lt_cv_truncate_bin" >&6; }
+
+
+
+
+
+
+
+# Calculate cc_basename. Skip known compiler wrappers and cross-prefix.
+func_cc_basename ()
+{
+ for cc_temp in $*""; do
+ case $cc_temp in
+ compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+ distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+ done
+ func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+}
+
+# Check whether --enable-libtool-lock was given.
+if test "${enable_libtool_lock+set}" = set; then :
+ enableval=$enable_libtool_lock;
+fi
+
+test no = "$enable_libtool_lock" || enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+ # Find out what ABI is being produced by ac_compile, and set mode
+ # options accordingly.
+ echo 'int i;' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *ELF-32*)
+ HPUX_IA64_MODE=32
+ ;;
+ *ELF-64*)
+ HPUX_IA64_MODE=64
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+*-*-irix6*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
+ echo '#line '$LINENO' "configure"' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ if test yes = "$lt_cv_prog_gnu_ld"; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -melf32bsmip"
+ ;;
+ *N32*)
+ LD="${LD-ld} -melf32bmipn32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -melf64bmip"
+ ;;
+ esac
+ else
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -32"
+ ;;
+ *N32*)
+ LD="${LD-ld} -n32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -64"
+ ;;
+ esac
+ fi
+ fi
+ rm -rf conftest*
+ ;;
+
+mips64*-*linux*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
+ echo '#line '$LINENO' "configure"' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ emul=elf
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ emul="${emul}32"
+ ;;
+ *64-bit*)
+ emul="${emul}64"
+ ;;
+ esac
+ case `/usr/bin/file conftest.$ac_objext` in
+ *MSB*)
+ emul="${emul}btsmip"
+ ;;
+ *LSB*)
+ emul="${emul}ltsmip"
+ ;;
+ esac
+ case `/usr/bin/file conftest.$ac_objext` in
+ *N32*)
+ emul="${emul}n32"
+ ;;
+ esac
+ LD="${LD-ld} -m $emul"
+ fi
+ rm -rf conftest*
+ ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly. Note that the listed cases only cover the
+ # situations where additional linker options are needed (such as when
+ # doing 32-bit compilation for a host where ld defaults to 64-bit, or
+ # vice versa); the common cases where no linker options are needed do
+ # not appear in the list.
+ echo 'int i;' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ case `/usr/bin/file conftest.o` in
+ *32-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_i386_fbsd"
+ ;;
+ x86_64-*linux*)
+ case `/usr/bin/file conftest.o` in
+ *x86-64*)
+ LD="${LD-ld} -m elf32_x86_64"
+ ;;
+ *)
+ LD="${LD-ld} -m elf_i386"
+ ;;
+ esac
+ ;;
+ powerpc64le-*linux*)
+ LD="${LD-ld} -m elf32lppclinux"
+ ;;
+ powerpc64-*linux*)
+ LD="${LD-ld} -m elf32ppclinux"
+ ;;
+ s390x-*linux*)
+ LD="${LD-ld} -m elf_s390"
+ ;;
+ sparc64-*linux*)
+ LD="${LD-ld} -m elf32_sparc"
+ ;;
+ esac
+ ;;
+ *64-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_x86_64_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ powerpcle-*linux*)
+ LD="${LD-ld} -m elf64lppc"
+ ;;
+ powerpc-*linux*)
+ LD="${LD-ld} -m elf64ppc"
+ ;;
+ s390*-*linux*|s390*-*tpf*)
+ LD="${LD-ld} -m elf64_s390"
+ ;;
+ sparc*-*linux*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+
+*-*-sco3.2v5*)
+ # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+ SAVE_CFLAGS=$CFLAGS
+ CFLAGS="$CFLAGS -belf"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5
+$as_echo_n "checking whether the C compiler needs -belf... " >&6; }
+if ${lt_cv_cc_needs_belf+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ lt_cv_cc_needs_belf=yes
+else
+ lt_cv_cc_needs_belf=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5
+$as_echo "$lt_cv_cc_needs_belf" >&6; }
+ if test yes != "$lt_cv_cc_needs_belf"; then
+ # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+ CFLAGS=$SAVE_CFLAGS
+ fi
+ ;;
+*-*solaris*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
+ echo 'int i;' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ case `/usr/bin/file conftest.o` in
+ *64-bit*)
+ case $lt_cv_prog_gnu_ld in
+ yes*)
+ case $host in
+ i?86-*-solaris*|x86_64-*-solaris*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ sparc*-*-solaris*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ # GNU ld 2.21 introduced _sol2 emulations. Use them if available.
+ if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then
+ LD=${LD-ld}_sol2
+ fi
+ ;;
+ *)
+ if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+ LD="${LD-ld} -64"
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+esac
+
+need_locks=$enable_libtool_lock
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args.
+set dummy ${ac_tool_prefix}mt; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_MANIFEST_TOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$MANIFEST_TOOL"; then
+ ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL
+if test -n "$MANIFEST_TOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5
+$as_echo "$MANIFEST_TOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_MANIFEST_TOOL"; then
+ ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL
+ # Extract the first word of "mt", so it can be a program name with args.
+set dummy mt; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_MANIFEST_TOOL"; then
+ ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_MANIFEST_TOOL="mt"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL
+if test -n "$ac_ct_MANIFEST_TOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5
+$as_echo "$ac_ct_MANIFEST_TOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_MANIFEST_TOOL" = x; then
+ MANIFEST_TOOL=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL
+ fi
+else
+ MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL"
+fi
+
+test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5
+$as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; }
+if ${lt_cv_path_mainfest_tool+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_path_mainfest_tool=no
+ echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5
+ $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out
+ cat conftest.err >&5
+ if $GREP 'Manifest Tool' conftest.out > /dev/null; then
+ lt_cv_path_mainfest_tool=yes
+ fi
+ rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5
+$as_echo "$lt_cv_path_mainfest_tool" >&6; }
+if test yes != "$lt_cv_path_mainfest_tool"; then
+ MANIFEST_TOOL=:
+fi
+
+
+
+
+
+
+ case $host_os in
+ rhapsody* | darwin*)
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dsymutil; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DSYMUTIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$DSYMUTIL"; then
+ ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+DSYMUTIL=$ac_cv_prog_DSYMUTIL
+if test -n "$DSYMUTIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5
+$as_echo "$DSYMUTIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DSYMUTIL"; then
+ ac_ct_DSYMUTIL=$DSYMUTIL
+ # Extract the first word of "dsymutil", so it can be a program name with args.
+set dummy dsymutil; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_DSYMUTIL"; then
+ ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_DSYMUTIL="dsymutil"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL
+if test -n "$ac_ct_DSYMUTIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5
+$as_echo "$ac_ct_DSYMUTIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_DSYMUTIL" = x; then
+ DSYMUTIL=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ DSYMUTIL=$ac_ct_DSYMUTIL
+ fi
+else
+ DSYMUTIL="$ac_cv_prog_DSYMUTIL"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args.
+set dummy ${ac_tool_prefix}nmedit; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_NMEDIT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$NMEDIT"; then
+ ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+NMEDIT=$ac_cv_prog_NMEDIT
+if test -n "$NMEDIT"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5
+$as_echo "$NMEDIT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_NMEDIT"; then
+ ac_ct_NMEDIT=$NMEDIT
+ # Extract the first word of "nmedit", so it can be a program name with args.
+set dummy nmedit; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_NMEDIT"; then
+ ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_NMEDIT="nmedit"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT
+if test -n "$ac_ct_NMEDIT"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5
+$as_echo "$ac_ct_NMEDIT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_NMEDIT" = x; then
+ NMEDIT=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ NMEDIT=$ac_ct_NMEDIT
+ fi
+else
+ NMEDIT="$ac_cv_prog_NMEDIT"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args.
+set dummy ${ac_tool_prefix}lipo; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_LIPO+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$LIPO"; then
+ ac_cv_prog_LIPO="$LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_LIPO="${ac_tool_prefix}lipo"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+LIPO=$ac_cv_prog_LIPO
+if test -n "$LIPO"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5
+$as_echo "$LIPO" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_LIPO"; then
+ ac_ct_LIPO=$LIPO
+ # Extract the first word of "lipo", so it can be a program name with args.
+set dummy lipo; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_LIPO+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_LIPO"; then
+ ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_LIPO="lipo"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO
+if test -n "$ac_ct_LIPO"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5
+$as_echo "$ac_ct_LIPO" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_LIPO" = x; then
+ LIPO=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ LIPO=$ac_ct_LIPO
+ fi
+else
+ LIPO="$ac_cv_prog_LIPO"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OTOOL"; then
+ ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_OTOOL="${ac_tool_prefix}otool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL=$ac_cv_prog_OTOOL
+if test -n "$OTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5
+$as_echo "$OTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL"; then
+ ac_ct_OTOOL=$OTOOL
+ # Extract the first word of "otool", so it can be a program name with args.
+set dummy otool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OTOOL"; then
+ ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_OTOOL="otool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL
+if test -n "$ac_ct_OTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5
+$as_echo "$ac_ct_OTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OTOOL" = x; then
+ OTOOL=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OTOOL=$ac_ct_OTOOL
+ fi
+else
+ OTOOL="$ac_cv_prog_OTOOL"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool64; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OTOOL64+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OTOOL64"; then
+ ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL64=$ac_cv_prog_OTOOL64
+if test -n "$OTOOL64"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5
+$as_echo "$OTOOL64" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL64"; then
+ ac_ct_OTOOL64=$OTOOL64
+ # Extract the first word of "otool64", so it can be a program name with args.
+set dummy otool64; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OTOOL64"; then
+ ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_OTOOL64="otool64"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64
+if test -n "$ac_ct_OTOOL64"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5
+$as_echo "$ac_ct_OTOOL64" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OTOOL64" = x; then
+ OTOOL64=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OTOOL64=$ac_ct_OTOOL64
+ fi
+else
+ OTOOL64="$ac_cv_prog_OTOOL64"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5
+$as_echo_n "checking for -single_module linker flag... " >&6; }
+if ${lt_cv_apple_cc_single_mod+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_apple_cc_single_mod=no
+ if test -z "$LT_MULTI_MODULE"; then
+ # By default we will add the -single_module flag. You can override
+ # by either setting the environment variable LT_MULTI_MODULE
+ # non-empty at configure time, or by adding -multi_module to the
+ # link flags.
+ rm -rf libconftest.dylib*
+ echo "int foo(void){return 1;}" > conftest.c
+ echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&5
+ $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+ _lt_result=$?
+ # If there is a non-empty error log, and "single_module"
+ # appears in it, assume the flag caused a linker warning
+ if test -s conftest.err && $GREP single_module conftest.err; then
+ cat conftest.err >&5
+ # Otherwise, if the output was created with a 0 exit code from
+ # the compiler, it worked.
+ elif test -f libconftest.dylib && test 0 = "$_lt_result"; then
+ lt_cv_apple_cc_single_mod=yes
+ else
+ cat conftest.err >&5
+ fi
+ rm -rf libconftest.dylib*
+ rm -f conftest.*
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5
+$as_echo "$lt_cv_apple_cc_single_mod" >&6; }
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5
+$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; }
+if ${lt_cv_ld_exported_symbols_list+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ld_exported_symbols_list=no
+ save_LDFLAGS=$LDFLAGS
+ echo "_main" > conftest.sym
+ LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ lt_cv_ld_exported_symbols_list=yes
+else
+ lt_cv_ld_exported_symbols_list=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS=$save_LDFLAGS
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5
+$as_echo "$lt_cv_ld_exported_symbols_list" >&6; }
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5
+$as_echo_n "checking for -force_load linker flag... " >&6; }
+if ${lt_cv_ld_force_load+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ld_force_load=no
+ cat > conftest.c << _LT_EOF
+int forced_loaded() { return 2;}
+_LT_EOF
+ echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5
+ $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5
+ echo "$AR cru libconftest.a conftest.o" >&5
+ $AR cru libconftest.a conftest.o 2>&5
+ echo "$RANLIB libconftest.a" >&5
+ $RANLIB libconftest.a 2>&5
+ cat > conftest.c << _LT_EOF
+int main() { return 0;}
+_LT_EOF
+ echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5
+ $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err
+ _lt_result=$?
+ if test -s conftest.err && $GREP force_load conftest.err; then
+ cat conftest.err >&5
+ elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then
+ lt_cv_ld_force_load=yes
+ else
+ cat conftest.err >&5
+ fi
+ rm -f conftest.err libconftest.a conftest conftest.c
+ rm -rf conftest.dSYM
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5
+$as_echo "$lt_cv_ld_force_load" >&6; }
+ case $host_os in
+ rhapsody* | darwin1.[012])
+ _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;;
+ darwin1.*)
+ _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
+ darwin*) # darwin 5.x on
+ # if running on 10.5 or later, the deployment target defaults
+ # to the OS version, if on x86, and 10.4, the deployment
+ # target defaults to 10.4. Don't you love it?
+ case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+ 10.0,*86*-darwin8*|10.0,*-darwin[91]*)
+ _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
+ 10.[012][,.]*)
+ _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
+ 10.*)
+ _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
+ esac
+ ;;
+ esac
+ if test yes = "$lt_cv_apple_cc_single_mod"; then
+ _lt_dar_single_mod='$single_module'
+ fi
+ if test yes = "$lt_cv_ld_exported_symbols_list"; then
+ _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym'
+ else
+ _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib'
+ fi
+ if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then
+ _lt_dsymutil='~$DSYMUTIL $lib || :'
+ else
+ _lt_dsymutil=
+ fi
+ ;;
+ esac
+
+# func_munge_path_list VARIABLE PATH
+# -----------------------------------
+# VARIABLE is name of variable containing _space_ separated list of
+# directories to be munged by the contents of PATH, which is string
+# having a format:
+# "DIR[:DIR]:"
+# string "DIR[ DIR]" will be prepended to VARIABLE
+# ":DIR[:DIR]"
+# string "DIR[ DIR]" will be appended to VARIABLE
+# "DIRP[:DIRP]::[DIRA:]DIRA"
+# string "DIRP[ DIRP]" will be prepended to VARIABLE and string
+# "DIRA[ DIRA]" will be appended to VARIABLE
+# "DIR[:DIR]"
+# VARIABLE will be replaced by "DIR[ DIR]"
+func_munge_path_list ()
+{
+ case x$2 in
+ x)
+ ;;
+ *:)
+ eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\"
+ ;;
+ x:*)
+ eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\"
+ ;;
+ *::*)
+ eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\"
+ eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\"
+ ;;
+ *)
+ eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\"
+ ;;
+ esac
+}
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if ${ac_cv_prog_CPP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_stdc=yes
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then :
+ :
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+ inttypes.h stdint.h unistd.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in dlfcn.h
+do :
+ ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default
+"
+if test "x$ac_cv_header_dlfcn_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_DLFCN_H 1
+_ACEOF
+
+fi
+
+done
+
+
+
+
+
+# Set options
+
+
+
+
+
+
+
+
+# Check whether --with-pic was given.
+if test "${with_pic+set}" = set; then :
+ withval=$with_pic; lt_p=${PACKAGE-default}
+ case $withval in
+ yes|no) pic_mode=$withval ;;
+ *)
+ pic_mode=default
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for lt_pkg in $withval; do
+ IFS=$lt_save_ifs
+ if test "X$lt_pkg" = "X$lt_p"; then
+ pic_mode=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac
+else
+ pic_mode=default
+fi
+
+
+
+
+
+
+
+
+ # Check whether --enable-fast-install was given.
+if test "${enable_fast_install+set}" = set; then :
+ enableval=$enable_fast_install; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_fast_install=yes ;;
+ no) enable_fast_install=no ;;
+ *)
+ enable_fast_install=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for pkg in $enableval; do
+ IFS=$lt_save_ifs
+ if test "X$pkg" = "X$p"; then
+ enable_fast_install=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac
+else
+ enable_fast_install=yes
+fi
+
+
+
+
+
+
+
+
+ shared_archive_member_spec=
+case $host,$enable_shared in
+power*-*-aix[5-9]*,yes)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking which variant of shared library versioning to provide" >&5
+$as_echo_n "checking which variant of shared library versioning to provide... " >&6; }
+
+# Check whether --with-aix-soname was given.
+if test "${with_aix_soname+set}" = set; then :
+ withval=$with_aix_soname; case $withval in
+ aix|svr4|both)
+ ;;
+ *)
+ as_fn_error $? "Unknown argument to --with-aix-soname" "$LINENO" 5
+ ;;
+ esac
+ lt_cv_with_aix_soname=$with_aix_soname
+else
+ if ${lt_cv_with_aix_soname+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_with_aix_soname=aix
+fi
+
+ with_aix_soname=$lt_cv_with_aix_soname
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_aix_soname" >&5
+$as_echo "$with_aix_soname" >&6; }
+ if test aix != "$with_aix_soname"; then
+ # For the AIX way of multilib, we name the shared archive member
+ # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o',
+ # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File.
+ # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag,
+ # the AIX toolchain works better with OBJECT_MODE set (default 32).
+ if test 64 = "${OBJECT_MODE-32}"; then
+ shared_archive_member_spec=shr_64
+ else
+ shared_archive_member_spec=shr
+ fi
+ fi
+ ;;
+*)
+ with_aix_soname=aix
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS=$ltmain
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+test -z "$LN_S" && LN_S="ln -s"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "${ZSH_VERSION+set}"; then
+ setopt NO_GLOB_SUBST
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5
+$as_echo_n "checking for objdir... " >&6; }
+if ${lt_cv_objdir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+ lt_cv_objdir=.libs
+else
+ # MS-DOS does not allow filenames that begin with a dot.
+ lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5
+$as_echo "$lt_cv_objdir" >&6; }
+objdir=$lt_cv_objdir
+
+
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define LT_OBJDIR "$lt_cv_objdir/"
+_ACEOF
+
+
+
+
+case $host_os in
+aix3*)
+ # AIX sometimes has problems with the GCC collect2 program. For some
+ # reason, if we set the COLLECT_NAMES environment variable, the problems
+ # vanish in a puff of smoke.
+ if test set != "${COLLECT_NAMES+set}"; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+ fi
+ ;;
+esac
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a '.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+old_CC=$CC
+old_CFLAGS=$CFLAGS
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+func_cc_basename $compiler
+cc_basename=$func_cc_basename_result
+
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+ if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5
+$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; }
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $MAGIC_CMD in
+[\\/*] | ?:[\\/]*)
+ lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD=$MAGIC_CMD
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+ ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+ for ac_dir in $ac_dummy; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/${ac_tool_prefix}file"; then
+ lt_cv_path_MAGIC_CMD=$ac_dir/"${ac_tool_prefix}file"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS=$lt_save_ifs
+ MAGIC_CMD=$lt_save_MAGIC_CMD
+ ;;
+esac
+fi
+
+MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+if test -n "$MAGIC_CMD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+
+
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+ if test -n "$ac_tool_prefix"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5
+$as_echo_n "checking for file... " >&6; }
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $MAGIC_CMD in
+[\\/*] | ?:[\\/]*)
+ lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD=$MAGIC_CMD
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+ ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+ for ac_dir in $ac_dummy; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/file"; then
+ lt_cv_path_MAGIC_CMD=$ac_dir/"file"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS=$lt_save_ifs
+ MAGIC_CMD=$lt_save_MAGIC_CMD
+ ;;
+esac
+fi
+
+MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+if test -n "$MAGIC_CMD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ else
+ MAGIC_CMD=:
+ fi
+fi
+
+ fi
+ ;;
+esac
+
+# Use C for the default configuration in the libtool script
+
+lt_save_CC=$CC
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+objext=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+
+
+## CAVEAT EMPTOR:
+## There is no encapsulation within the following macros, do not change
+## the running order or otherwise move them around unless you know exactly
+## what you are doing...
+if test -n "$compiler"; then
+
+lt_prog_compiler_no_builtin_flag=
+
+if test yes = "$GCC"; then
+ case $cc_basename in
+ nvcc*)
+ lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;;
+ *)
+ lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;;
+ esac
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
+$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; }
+if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_rtti_exceptions=no
+ ac_outfile=conftest.$ac_objext
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="-fno-rtti -fno-exceptions" ## exclude from sc_useless_quotes_in_assignment
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_rtti_exceptions=yes
+ fi
+ fi
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5
+$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; }
+
+if test yes = "$lt_cv_prog_compiler_rtti_exceptions"; then
+ lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions"
+else
+ :
+fi
+
+fi
+
+
+
+
+
+
+ lt_prog_compiler_wl=
+lt_prog_compiler_pic=
+lt_prog_compiler_static=
+
+
+ if test yes = "$GCC"; then
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_static='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 now supports IA64 processor
+ lt_prog_compiler_static='-Bstatic'
+ fi
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the '-m68020' flag to GCC prevents building anything better,
+ # like '-m68040'.
+ lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ lt_prog_compiler_pic='-DDLL_EXPORT'
+ case $host_os in
+ os2*)
+ lt_prog_compiler_static='$wl-static'
+ ;;
+ esac
+ ;;
+
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ lt_prog_compiler_pic='-fno-common'
+ ;;
+
+ haiku*)
+ # PIC is the default for Haiku.
+ # The "-static" flag exists, but is broken.
+ lt_prog_compiler_static=
+ ;;
+
+ hpux*)
+ # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+ # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
+ # sets the default TLS model and affects inlining.
+ case $host_cpu in
+ hppa*64*)
+ # +Z the default
+ ;;
+ *)
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ esac
+ ;;
+
+ interix[3-9]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+
+ msdosdjgpp*)
+ # Just because we use GCC doesn't mean we suddenly get shared libraries
+ # on systems that don't support them.
+ lt_prog_compiler_can_build_shared=no
+ enable_shared=no
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ lt_prog_compiler_pic='-fPIC -shared'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ lt_prog_compiler_pic=-Kconform_pic
+ fi
+ ;;
+
+ *)
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ esac
+
+ case $cc_basename in
+ nvcc*) # Cuda Compiler Driver 2.2
+ lt_prog_compiler_wl='-Xlinker '
+ if test -n "$lt_prog_compiler_pic"; then
+ lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic"
+ fi
+ ;;
+ esac
+ else
+ # PORTME Check for flag to pass linker flags through the system compiler.
+ case $host_os in
+ aix*)
+ lt_prog_compiler_wl='-Wl,'
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 now supports IA64 processor
+ lt_prog_compiler_static='-Bstatic'
+ else
+ lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ lt_prog_compiler_pic='-fno-common'
+ case $cc_basename in
+ nagfor*)
+ # NAG Fortran compiler
+ lt_prog_compiler_wl='-Wl,-Wl,,'
+ lt_prog_compiler_pic='-PIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+ esac
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ lt_prog_compiler_pic='-DDLL_EXPORT'
+ case $host_os in
+ os2*)
+ lt_prog_compiler_static='$wl-static'
+ ;;
+ esac
+ ;;
+
+ hpux9* | hpux10* | hpux11*)
+ lt_prog_compiler_wl='-Wl,'
+ # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+ # not for PA HP-UX.
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ lt_prog_compiler_pic='+Z'
+ ;;
+ esac
+ # Is there a better lt_prog_compiler_static that works with the bundled CC?
+ lt_prog_compiler_static='$wl-a ${wl}archive'
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ lt_prog_compiler_wl='-Wl,'
+ # PIC (with -KPIC) is the default.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ case $cc_basename in
+ # old Intel for x86_64, which still supported -KPIC.
+ ecc*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ # icc used to be incompatible with GCC.
+ # ICC 10 doesn't accept -KPIC any more.
+ icc* | ifort*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ # Lahey Fortran 8.1.
+ lf95*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='--shared'
+ lt_prog_compiler_static='--static'
+ ;;
+ nagfor*)
+ # NAG Fortran compiler
+ lt_prog_compiler_wl='-Wl,-Wl,,'
+ lt_prog_compiler_pic='-PIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+ tcc*)
+ # Fabrice Bellard et al's Tiny C Compiler
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*)
+ # Portland Group compilers (*not* the Pentium gcc compiler,
+ # which looks to be a dead project)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fpic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+ ccc*)
+ lt_prog_compiler_wl='-Wl,'
+ # All Alpha code is PIC.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+ xl* | bgxl* | bgf* | mpixl*)
+ # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-qpic'
+ lt_prog_compiler_static='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*)
+ # Sun Fortran 8.3 passes all unrecognized flags to the linker
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl=''
+ ;;
+ *Sun\ F* | *Sun*Fortran*)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl='-Qoption ld '
+ ;;
+ *Sun\ C*)
+ # Sun C 5.9
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl='-Wl,'
+ ;;
+ *Intel*\ [CF]*Compiler*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ *Portland\ Group*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fpic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ newsos6)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ lt_prog_compiler_pic='-fPIC -shared'
+ ;;
+
+ osf3* | osf4* | osf5*)
+ lt_prog_compiler_wl='-Wl,'
+ # All OSF/1 code is PIC.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ rdos*)
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ solaris*)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ case $cc_basename in
+ f77* | f90* | f95* | sunf77* | sunf90* | sunf95*)
+ lt_prog_compiler_wl='-Qoption ld ';;
+ *)
+ lt_prog_compiler_wl='-Wl,';;
+ esac
+ ;;
+
+ sunos4*)
+ lt_prog_compiler_wl='-Qoption ld '
+ lt_prog_compiler_pic='-PIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ sysv4 | sysv4.2uw2* | sysv4.3*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ lt_prog_compiler_pic='-Kconform_pic'
+ lt_prog_compiler_static='-Bstatic'
+ fi
+ ;;
+
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ unicos*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_can_build_shared=no
+ ;;
+
+ uts4*)
+ lt_prog_compiler_pic='-pic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ *)
+ lt_prog_compiler_can_build_shared=no
+ ;;
+ esac
+ fi
+
+case $host_os in
+ # For platforms that do not support PIC, -DPIC is meaningless:
+ *djgpp*)
+ lt_prog_compiler_pic=
+ ;;
+ *)
+ lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC"
+ ;;
+esac
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5
+$as_echo_n "checking for $compiler option to produce PIC... " >&6; }
+if ${lt_cv_prog_compiler_pic+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_pic=$lt_prog_compiler_pic
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5
+$as_echo "$lt_cv_prog_compiler_pic" >&6; }
+lt_prog_compiler_pic=$lt_cv_prog_compiler_pic
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$lt_prog_compiler_pic"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5
+$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; }
+if ${lt_cv_prog_compiler_pic_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_pic_works=no
+ ac_outfile=conftest.$ac_objext
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="$lt_prog_compiler_pic -DPIC" ## exclude from sc_useless_quotes_in_assignment
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_pic_works=yes
+ fi
+ fi
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5
+$as_echo "$lt_cv_prog_compiler_pic_works" >&6; }
+
+if test yes = "$lt_cv_prog_compiler_pic_works"; then
+ case $lt_prog_compiler_pic in
+ "" | " "*) ;;
+ *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;;
+ esac
+else
+ lt_prog_compiler_pic=
+ lt_prog_compiler_can_build_shared=no
+fi
+
+fi
+
+
+
+
+
+
+
+
+
+
+
+#
+# Check to make sure the static flag actually works.
+#
+wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5
+$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; }
+if ${lt_cv_prog_compiler_static_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_static_works=no
+ save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS $lt_tmp_static_flag"
+ echo "$lt_simple_link_test_code" > conftest.$ac_ext
+ if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+ # The linker can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ # Append any errors to the config.log.
+ cat conftest.err 1>&5
+ $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_static_works=yes
+ fi
+ else
+ lt_cv_prog_compiler_static_works=yes
+ fi
+ fi
+ $RM -r conftest*
+ LDFLAGS=$save_LDFLAGS
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5
+$as_echo "$lt_cv_prog_compiler_static_works" >&6; }
+
+if test yes = "$lt_cv_prog_compiler_static_works"; then
+ :
+else
+ lt_prog_compiler_static=
+fi
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_c_o=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_c_o=yes
+ fi
+ fi
+ chmod u+w . 2>&5
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_c_o=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_c_o=yes
+ fi
+ fi
+ chmod u+w . 2>&5
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+hard_links=nottested
+if test no = "$lt_cv_prog_compiler_c_o" && test no != "$need_locks"; then
+ # do not overwrite the value of need_locks provided by the user
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5
+$as_echo_n "checking if we can lock with hard links... " >&6; }
+ hard_links=yes
+ $RM conftest*
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ touch conftest.a
+ ln conftest.a conftest.b 2>&5 || hard_links=no
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5
+$as_echo "$hard_links" >&6; }
+ if test no = "$hard_links"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5
+$as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;}
+ need_locks=warn
+ fi
+else
+ need_locks=no
+fi
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; }
+
+ runpath_var=
+ allow_undefined_flag=
+ always_export_symbols=no
+ archive_cmds=
+ archive_expsym_cmds=
+ compiler_needs_object=no
+ enable_shared_with_static_runtimes=no
+ export_dynamic_flag_spec=
+ export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ hardcode_automatic=no
+ hardcode_direct=no
+ hardcode_direct_absolute=no
+ hardcode_libdir_flag_spec=
+ hardcode_libdir_separator=
+ hardcode_minus_L=no
+ hardcode_shlibpath_var=unsupported
+ inherit_rpath=no
+ link_all_deplibs=unknown
+ module_cmds=
+ module_expsym_cmds=
+ old_archive_from_new_cmds=
+ old_archive_from_expsyms_cmds=
+ thread_safe_flag_spec=
+ whole_archive_flag_spec=
+ # include_expsyms should be a list of space-separated symbols to be *always*
+ # included in the symbol list
+ include_expsyms=
+ # exclude_expsyms can be an extended regexp of symbols to exclude
+ # it will be wrapped by ' (' and ')$', so one must not match beginning or
+ # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc',
+ # as well as any symbol that contains 'd'.
+ exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
+ # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+ # platforms (ab)use it in PIC code, but their linkers get confused if
+ # the symbol is explicitly referenced. Since portable code cannot
+ # rely on this symbol name, it's probably fine to never include it in
+ # preloaded symbol tables.
+ # Exclude shared library initialization/finalization symbols.
+ extract_expsyms_cmds=
+
+ case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ # FIXME: the MSVC++ port hasn't been tested in a loooong time
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ if test yes != "$GCC"; then
+ with_gnu_ld=no
+ fi
+ ;;
+ interix*)
+ # we just hope/assume this is gcc and not c89 (= MSVC++)
+ with_gnu_ld=yes
+ ;;
+ openbsd* | bitrig*)
+ with_gnu_ld=no
+ ;;
+ esac
+
+ ld_shlibs=yes
+
+ # On some targets, GNU ld is compatible enough with the native linker
+ # that we're better off using the native interface for both.
+ lt_use_gnu_ld_interface=no
+ if test yes = "$with_gnu_ld"; then
+ case $host_os in
+ aix*)
+ # The AIX port of GNU ld has always aspired to compatibility
+ # with the native linker. However, as the warning in the GNU ld
+ # block says, versions before 2.19.5* couldn't really create working
+ # shared libraries, regardless of the interface used.
+ case `$LD -v 2>&1` in
+ *\ \(GNU\ Binutils\)\ 2.19.5*) ;;
+ *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;;
+ *\ \(GNU\ Binutils\)\ [3-9]*) ;;
+ *)
+ lt_use_gnu_ld_interface=yes
+ ;;
+ esac
+ ;;
+ *)
+ lt_use_gnu_ld_interface=yes
+ ;;
+ esac
+ fi
+
+ if test yes = "$lt_use_gnu_ld_interface"; then
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ wlarc='$wl'
+
+ # Set some defaults for GNU ld with shared library support. These
+ # are reset later if shared libraries are not supported. Putting them
+ # here allows them to be overridden if necessary.
+ runpath_var=LD_RUN_PATH
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ export_dynamic_flag_spec='$wl--export-dynamic'
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+ whole_archive_flag_spec=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
+ else
+ whole_archive_flag_spec=
+ fi
+ supports_anon_versioning=no
+ case `$LD -v | $SED -e 's/(^)\+)\s\+//' 2>&1` in
+ *GNU\ gold*) supports_anon_versioning=yes ;;
+ *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11
+ *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+ *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+ *\ 2.11.*) ;; # other 2.11 versions
+ *) supports_anon_versioning=yes ;;
+ esac
+
+ # See if GNU ld supports shared libraries.
+ case $host_os in
+ aix[3-9]*)
+ # On AIX/PPC, the GNU linker is very broken
+ if test ia64 != "$host_cpu"; then
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.19, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support. If you
+*** really care for shared libraries, you may want to install binutils
+*** 2.20 or above, or modify your PATH so that a non-GNU linker is found.
+*** You will then need to restart the configuration process.
+
+_LT_EOF
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds=''
+ ;;
+ m68k)
+ archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ ;;
+ esac
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ allow_undefined_flag=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless,
+ # as there is no search path for DLLs.
+ hardcode_libdir_flag_spec='-L$libdir'
+ export_dynamic_flag_spec='$wl--export-all-symbols'
+ allow_undefined_flag=unsupported
+ always_export_symbols=no
+ enable_shared_with_static_runtimes=yes
+ export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols'
+ exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file, use it as
+ # is; otherwise, prepend EXPORTS...
+ archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ haiku*)
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ link_all_deplibs=yes
+ ;;
+
+ os2*)
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ allow_undefined_flag=unsupported
+ shrext_cmds=.dll
+ archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ prefix_cmds="$SED"~
+ if test EXPORTS = "`$SED 1q $export_symbols`"; then
+ prefix_cmds="$prefix_cmds -e 1d";
+ fi~
+ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+ enable_shared_with_static_runtimes=yes
+ ;;
+
+ interix[3-9]*)
+ hardcode_direct=no
+ hardcode_shlibpath_var=no
+ hardcode_libdir_flag_spec='$wl-rpath,$libdir'
+ export_dynamic_flag_spec='$wl-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ archive_expsym_cmds='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+
+ gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
+ tmp_diet=no
+ if test linux-dietlibc = "$host_os"; then
+ case $cc_basename in
+ diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn)
+ esac
+ fi
+ if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+ && test no = "$tmp_diet"
+ then
+ tmp_addflag=' $pic_flag'
+ tmp_sharedflag='-shared'
+ case $cc_basename,$host_cpu in
+ pgcc*) # Portland Group C compiler
+ whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ tmp_addflag=' $pic_flag'
+ ;;
+ pgf77* | pgf90* | pgf95* | pgfortran*)
+ # Portland Group f77 and f90 compilers
+ whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ tmp_addflag=' $pic_flag -Mnomain' ;;
+ ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64
+ tmp_addflag=' -i_dynamic' ;;
+ efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64
+ tmp_addflag=' -i_dynamic -nofor_main' ;;
+ ifc* | ifort*) # Intel Fortran compiler
+ tmp_addflag=' -nofor_main' ;;
+ lf95*) # Lahey Fortran 8.1
+ whole_archive_flag_spec=
+ tmp_sharedflag='--shared' ;;
+ nagfor*) # NAGFOR 5.3
+ tmp_sharedflag='-Wl,-shared' ;;
+ xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+ tmp_sharedflag='-qmkshrobj'
+ tmp_addflag= ;;
+ nvcc*) # Cuda Compiler Driver 2.2
+ whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ compiler_needs_object=yes
+ ;;
+ esac
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*) # Sun C 5.9
+ whole_archive_flag_spec='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ compiler_needs_object=yes
+ tmp_sharedflag='-G' ;;
+ *Sun\ F*) # Sun Fortran 8.3
+ tmp_sharedflag='-G' ;;
+ esac
+ archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+
+ if test yes = "$supports_anon_versioning"; then
+ archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib'
+ fi
+
+ case $cc_basename in
+ tcc*)
+ export_dynamic_flag_spec='-rdynamic'
+ ;;
+ xlf* | bgf* | bgxlf* | mpixlf*)
+ # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+ whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib'
+ if test yes = "$supports_anon_versioning"; then
+ archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ esac
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+ wlarc=
+ else
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ fi
+ ;;
+
+ solaris*)
+ if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+ case `$LD -v 2>&1` in
+ *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*)
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot
+*** reliably create shared libraries on SCO systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ ;;
+ *)
+ # For security reasons, it is highly recommended that you always
+ # use absolute paths for naming shared libraries, and exclude the
+ # DT_RUNPATH tag from executables and libraries. But doing so
+ # requires that you compile everything twice, which is a pain.
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ esac
+ ;;
+
+ sunos4*)
+ archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ wlarc=
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ *)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ esac
+
+ if test no = "$ld_shlibs"; then
+ runpath_var=
+ hardcode_libdir_flag_spec=
+ export_dynamic_flag_spec=
+ whole_archive_flag_spec=
+ fi
+ else
+ # PORTME fill in a description of your system's linker (not GNU ld)
+ case $host_os in
+ aix3*)
+ allow_undefined_flag=unsupported
+ always_export_symbols=yes
+ archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+ # Note: this linker hardcodes the directories in LIBPATH if there
+ # are no directories specified by -L.
+ hardcode_minus_L=yes
+ if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then
+ # Neither direct hardcoding nor static linking is supported with a
+ # broken collect2.
+ hardcode_direct=unsupported
+ fi
+ ;;
+
+ aix[4-9]*)
+ if test ia64 = "$host_cpu"; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=
+ else
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to GNU nm, but means don't demangle to AIX nm.
+ # Without the "-l" option, or with the "-B" option, AIX nm treats
+ # weak defined symbols like other global defined symbols, whereas
+ # GNU nm marks them as "W".
+ # While the 'weak' keyword is ignored in the Export File, we need
+ # it in the Import File for the 'aix-soname' feature, so we have
+ # to replace the "-B" option with "-P" for AIX nm.
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols'
+ else
+ export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols'
+ fi
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # have runtime linking enabled, and use it for executables.
+ # For shared libraries, we enable/disable runtime linking
+ # depending on the kind of the shared library created -
+ # when "with_aix_soname,aix_use_runtimelinking" is:
+ # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables
+ # "aix,yes" lib.so shared, rtl:yes, for executables
+ # lib.a static archive
+ # "both,no" lib.so.V(shr.o) shared, rtl:yes
+ # lib.a(lib.so.V) shared, rtl:no, for executables
+ # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a(lib.so.V) shared, rtl:no
+ # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a static archive
+ case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
+ for ld_flag in $LDFLAGS; do
+ if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then
+ aix_use_runtimelinking=yes
+ break
+ fi
+ done
+ if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # With aix-soname=svr4, we create the lib.so.V shared archives only,
+ # so we don't have lib.a shared libs to link our executables.
+ # We have to force runtime linking in this case.
+ aix_use_runtimelinking=yes
+ LDFLAGS="$LDFLAGS -Wl,-brtl"
+ fi
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ archive_cmds=''
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ hardcode_libdir_separator=':'
+ link_all_deplibs=yes
+ file_list_spec='$wl-f,'
+ case $with_aix_soname,$aix_use_runtimelinking in
+ aix,*) ;; # traditional, no import file
+ svr4,* | *,yes) # use import file
+ # The Import File defines what to hardcode.
+ hardcode_direct=no
+ hardcode_direct_absolute=no
+ ;;
+ esac
+
+ if test yes = "$GCC"; then
+ case $host_os in aix4.[012]|aix4.[012].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`$CC -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ hardcode_direct=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ hardcode_minus_L=yes
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_libdir_separator=
+ fi
+ ;;
+ esac
+ shared_flag='-shared'
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag="$shared_flag "'$wl-G'
+ fi
+ # Need to ensure runtime linking is disabled for the traditional
+ # shared library, or the linker may eventually find shared libraries
+ # /with/ Import File - we do not want to mix them.
+ shared_flag_aix='-shared'
+ shared_flag_svr4='-shared $wl-G'
+ else
+ # not using gcc
+ if test ia64 = "$host_cpu"; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag='$wl-G'
+ else
+ shared_flag='$wl-bM:SRE'
+ fi
+ shared_flag_aix='$wl-bM:SRE'
+ shared_flag_svr4='$wl-G'
+ fi
+ fi
+
+ export_dynamic_flag_spec='$wl-bexpall'
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to export.
+ always_export_symbols=yes
+ if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ allow_undefined_flag='-berok'
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ if test set = "${lt_cv_aix_libpath+set}"; then
+ aix_libpath=$lt_cv_aix_libpath
+else
+ if ${lt_cv_aix_libpath_+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+ lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\([^ ]*\) *$/\1/
+ p
+ }
+ }'
+ lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ # Check for a 64-bit object if we didn't find anything.
+ if test -z "$lt_cv_aix_libpath_"; then
+ lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ if test -z "$lt_cv_aix_libpath_"; then
+ lt_cv_aix_libpath_=/usr/lib:/lib
+ fi
+
+fi
+
+ aix_libpath=$lt_cv_aix_libpath_
+fi
+
+ hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath"
+ archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag
+ else
+ if test ia64 = "$host_cpu"; then
+ hardcode_libdir_flag_spec='$wl-R $libdir:/usr/lib:/lib'
+ allow_undefined_flag="-z nodefs"
+ archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ if test set = "${lt_cv_aix_libpath+set}"; then
+ aix_libpath=$lt_cv_aix_libpath
+else
+ if ${lt_cv_aix_libpath_+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+ lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\([^ ]*\) *$/\1/
+ p
+ }
+ }'
+ lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ # Check for a 64-bit object if we didn't find anything.
+ if test -z "$lt_cv_aix_libpath_"; then
+ lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ if test -z "$lt_cv_aix_libpath_"; then
+ lt_cv_aix_libpath_=/usr/lib:/lib
+ fi
+
+fi
+
+ aix_libpath=$lt_cv_aix_libpath_
+fi
+
+ hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ no_undefined_flag=' $wl-bernotok'
+ allow_undefined_flag=' $wl-berok'
+ if test yes = "$with_gnu_ld"; then
+ # We only use this code for GNU lds that support --whole-archive.
+ whole_archive_flag_spec='$wl--whole-archive$convenience $wl--no-whole-archive'
+ else
+ # Exported symbols can be pulled into shared objects from archives
+ whole_archive_flag_spec='$convenience'
+ fi
+ archive_cmds_need_lc=yes
+ archive_expsym_cmds='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d'
+ # -brtl affects multiple linker settings, -berok does not and is overridden later
+ compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`'
+ if test svr4 != "$with_aix_soname"; then
+ # This is similar to how AIX traditionally builds its shared libraries.
+ archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname'
+ fi
+ if test aix != "$with_aix_soname"; then
+ archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp'
+ else
+ # used by -dlpreopen to get the symbols
+ archive_expsym_cmds="$archive_expsym_cmds"'~$MV $output_objdir/$realname.d/$soname $output_objdir'
+ fi
+ archive_expsym_cmds="$archive_expsym_cmds"'~$RM -r $output_objdir/$realname.d'
+ fi
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds=''
+ ;;
+ m68k)
+ archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ ;;
+ esac
+ ;;
+
+ bsdi[45]*)
+ export_dynamic_flag_spec=-rdynamic
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ case $cc_basename in
+ cl*)
+ # Native MSVC
+ hardcode_libdir_flag_spec=' '
+ allow_undefined_flag=unsupported
+ always_export_symbols=yes
+ file_list_spec='@'
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=.dll
+ # FIXME: Setting linknames here is a bad hack.
+ archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames='
+ archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then
+ cp "$export_symbols" "$output_objdir/$soname.def";
+ echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp";
+ else
+ $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp;
+ fi~
+ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+ linknames='
+ # The linker will not automatically build a static lib if we build a DLL.
+ # _LT_TAGVAR(old_archive_from_new_cmds, )='true'
+ enable_shared_with_static_runtimes=yes
+ exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+ export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols'
+ # Don't use ranlib
+ old_postinstall_cmds='chmod 644 $oldlib'
+ postlink_cmds='lt_outputfile="@OUTPUT@"~
+ lt_tool_outputfile="@TOOL_OUTPUT@"~
+ case $lt_outputfile in
+ *.exe|*.EXE) ;;
+ *)
+ lt_outputfile=$lt_outputfile.exe
+ lt_tool_outputfile=$lt_tool_outputfile.exe
+ ;;
+ esac~
+ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then
+ $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+ $RM "$lt_outputfile.manifest";
+ fi'
+ ;;
+ *)
+ # Assume MSVC wrapper
+ hardcode_libdir_flag_spec=' '
+ allow_undefined_flag=unsupported
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=.dll
+ # FIXME: Setting linknames here is a bad hack.
+ archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames='
+ # The linker will automatically build a .lib file if we build a DLL.
+ old_archive_from_new_cmds='true'
+ # FIXME: Should let the user specify the lib program.
+ old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs'
+ enable_shared_with_static_runtimes=yes
+ ;;
+ esac
+ ;;
+
+ darwin* | rhapsody*)
+
+
+ archive_cmds_need_lc=no
+ hardcode_direct=no
+ hardcode_automatic=yes
+ hardcode_shlibpath_var=unsupported
+ if test yes = "$lt_cv_ld_force_load"; then
+ whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
+
+ else
+ whole_archive_flag_spec=''
+ fi
+ link_all_deplibs=yes
+ allow_undefined_flag=$_lt_dar_allow_undefined
+ case $cc_basename in
+ ifort*|nagfor*) _lt_dar_can_shared=yes ;;
+ *) _lt_dar_can_shared=$GCC ;;
+ esac
+ if test yes = "$_lt_dar_can_shared"; then
+ output_verbose_link_cmd=func_echo_all
+ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil"
+ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil"
+ archive_expsym_cmds="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil"
+ module_expsym_cmds="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil"
+
+ else
+ ld_shlibs=no
+ fi
+
+ ;;
+
+ dgux*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_shlibpath_var=no
+ ;;
+
+ # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+ # support. Future versions do this automatically, but an explicit c++rt0.o
+ # does not break anything, and helps significantly (at the cost of a little
+ # extra space).
+ freebsd2.2*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+ freebsd2.*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+ freebsd* | dragonfly*)
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ hpux9*)
+ if test yes = "$GCC"; then
+ archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+ else
+ archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+ fi
+ hardcode_libdir_flag_spec='$wl+b $wl$libdir'
+ hardcode_libdir_separator=:
+ hardcode_direct=yes
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ export_dynamic_flag_spec='$wl-E'
+ ;;
+
+ hpux10*)
+ if test yes,no = "$GCC,$with_gnu_ld"; then
+ archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ if test no = "$with_gnu_ld"; then
+ hardcode_libdir_flag_spec='$wl+b $wl$libdir'
+ hardcode_libdir_separator=:
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ export_dynamic_flag_spec='$wl-E'
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ fi
+ ;;
+
+ hpux11*)
+ if test yes,no = "$GCC,$with_gnu_ld"; then
+ case $host_cpu in
+ hppa*64*)
+ archive_cmds='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ else
+ case $host_cpu in
+ hppa*64*)
+ archive_cmds='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ archive_cmds='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+
+ # Older versions of the 11.00 compiler do not understand -b yet
+ # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5
+$as_echo_n "checking if $CC understands -b... " >&6; }
+if ${lt_cv_prog_compiler__b+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler__b=no
+ save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS -b"
+ echo "$lt_simple_link_test_code" > conftest.$ac_ext
+ if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+ # The linker can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ # Append any errors to the config.log.
+ cat conftest.err 1>&5
+ $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler__b=yes
+ fi
+ else
+ lt_cv_prog_compiler__b=yes
+ fi
+ fi
+ $RM -r conftest*
+ LDFLAGS=$save_LDFLAGS
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5
+$as_echo "$lt_cv_prog_compiler__b" >&6; }
+
+if test yes = "$lt_cv_prog_compiler__b"; then
+ archive_cmds='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+else
+ archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+fi
+
+ ;;
+ esac
+ fi
+ if test no = "$with_gnu_ld"; then
+ hardcode_libdir_flag_spec='$wl+b $wl$libdir'
+ hardcode_libdir_separator=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ hardcode_direct=no
+ hardcode_shlibpath_var=no
+ ;;
+ *)
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ export_dynamic_flag_spec='$wl-E'
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ ;;
+ esac
+ fi
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ if test yes = "$GCC"; then
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ # Try to use the -exported_symbol ld option, if it does not
+ # work, assume that -exports_file does not work either and
+ # implicitly export all symbols.
+ # This should be the same for all languages, so no per-tag cache variable.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5
+$as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; }
+if ${lt_cv_irix_exported_symbol+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+int foo (void) { return 0; }
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ lt_cv_irix_exported_symbol=yes
+else
+ lt_cv_irix_exported_symbol=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS=$save_LDFLAGS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5
+$as_echo "$lt_cv_irix_exported_symbol" >&6; }
+ if test yes = "$lt_cv_irix_exported_symbol"; then
+ archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib'
+ fi
+ else
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ hardcode_libdir_separator=:
+ inherit_rpath=yes
+ link_all_deplibs=yes
+ ;;
+
+ linux*)
+ case $cc_basename in
+ tcc*)
+ # Fabrice Bellard et al's Tiny C Compiler
+ ld_shlibs=yes
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
+ else
+ archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF
+ fi
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ newsos6)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ hardcode_libdir_separator=:
+ hardcode_shlibpath_var=no
+ ;;
+
+ *nto* | *qnx*)
+ ;;
+
+ openbsd* | bitrig*)
+ if test -f /usr/libexec/ld.so; then
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ hardcode_direct_absolute=yes
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols'
+ hardcode_libdir_flag_spec='$wl-rpath,$libdir'
+ export_dynamic_flag_spec='$wl-E'
+ else
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ hardcode_libdir_flag_spec='$wl-rpath,$libdir'
+ fi
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ os2*)
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ allow_undefined_flag=unsupported
+ shrext_cmds=.dll
+ archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ prefix_cmds="$SED"~
+ if test EXPORTS = "`$SED 1q $export_symbols`"; then
+ prefix_cmds="$prefix_cmds -e 1d";
+ fi~
+ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+ enable_shared_with_static_runtimes=yes
+ ;;
+
+ osf3*)
+ if test yes = "$GCC"; then
+ allow_undefined_flag=' $wl-expect_unresolved $wl\*'
+ archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ else
+ allow_undefined_flag=' -expect_unresolved \*'
+ archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ hardcode_libdir_separator=:
+ ;;
+
+ osf4* | osf5*) # as osf3* with the addition of -msym flag
+ if test yes = "$GCC"; then
+ allow_undefined_flag=' $wl-expect_unresolved $wl\*'
+ archive_cmds='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ else
+ allow_undefined_flag=' -expect_unresolved \*'
+ archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp'
+
+ # Both c and cxx compiler support -rpath directly
+ hardcode_libdir_flag_spec='-rpath $libdir'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_separator=:
+ ;;
+
+ solaris*)
+ no_undefined_flag=' -z defs'
+ if test yes = "$GCC"; then
+ wlarc='$wl'
+ archive_cmds='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ else
+ case `$CC -V 2>&1` in
+ *"Compilers 5.0"*)
+ wlarc=''
+ archive_cmds='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+ ;;
+ *)
+ wlarc='$wl'
+ archive_cmds='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ ;;
+ esac
+ fi
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_shlibpath_var=no
+ case $host_os in
+ solaris2.[0-5] | solaris2.[0-5].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands '-z linker_flag'. GCC discards it without '$wl',
+ # but is careful enough not to reorder.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ if test yes = "$GCC"; then
+ whole_archive_flag_spec='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract'
+ else
+ whole_archive_flag_spec='-z allextract$convenience -z defaultextract'
+ fi
+ ;;
+ esac
+ link_all_deplibs=yes
+ ;;
+
+ sunos4*)
+ if test sequent = "$host_vendor"; then
+ # Use $CC to link under sequent, because it throws in some extra .o
+ # files that make .init and .fini sections work.
+ archive_cmds='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ sysv4)
+ case $host_vendor in
+ sni)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes # is this really true???
+ ;;
+ siemens)
+ ## LD is ld it makes a PLAMLIB
+ ## CC just makes a GrossModule.
+ archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+ reload_cmds='$CC -r -o $output$reload_objs'
+ hardcode_direct=no
+ ;;
+ motorola)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=no #Motorola manual says yes, but my tests say they lie
+ ;;
+ esac
+ runpath_var='LD_RUN_PATH'
+ hardcode_shlibpath_var=no
+ ;;
+
+ sysv4.3*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_shlibpath_var=no
+ export_dynamic_flag_spec='-Bexport'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_shlibpath_var=no
+ runpath_var=LD_RUN_PATH
+ hardcode_runpath_var=yes
+ ld_shlibs=yes
+ fi
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
+ no_undefined_flag='$wl-z,text'
+ archive_cmds_need_lc=no
+ hardcode_shlibpath_var=no
+ runpath_var='LD_RUN_PATH'
+
+ if test yes = "$GCC"; then
+ archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We CANNOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ no_undefined_flag='$wl-z,text'
+ allow_undefined_flag='$wl-z,nodefs'
+ archive_cmds_need_lc=no
+ hardcode_shlibpath_var=no
+ hardcode_libdir_flag_spec='$wl-R,$libdir'
+ hardcode_libdir_separator=':'
+ link_all_deplibs=yes
+ export_dynamic_flag_spec='$wl-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ if test yes = "$GCC"; then
+ archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ uts4*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_shlibpath_var=no
+ ;;
+
+ *)
+ ld_shlibs=no
+ ;;
+ esac
+
+ if test sni = "$host_vendor"; then
+ case $host in
+ sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+ export_dynamic_flag_spec='$wl-Blargedynsym'
+ ;;
+ esac
+ fi
+ fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5
+$as_echo "$ld_shlibs" >&6; }
+test no = "$ld_shlibs" && can_build_shared=no
+
+with_gnu_ld=$with_gnu_ld
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$archive_cmds_need_lc" in
+x|xyes)
+ # Assume -lc should be added
+ archive_cmds_need_lc=yes
+
+ if test yes,yes = "$GCC,$enable_shared"; then
+ case $archive_cmds in
+ *'~'*)
+ # FIXME: we may have to deal with multi-command sequences.
+ ;;
+ '$CC '*)
+ # Test whether the compiler implicitly links with -lc since on some
+ # systems, -lgcc has to come before -lc. If gcc already passes -lc
+ # to ld, don't add -lc before -lgcc.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5
+$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; }
+if ${lt_cv_archive_cmds_need_lc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ $RM conftest*
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } 2>conftest.err; then
+ soname=conftest
+ lib=conftest
+ libobjs=conftest.$ac_objext
+ deplibs=
+ wl=$lt_prog_compiler_wl
+ pic_flag=$lt_prog_compiler_pic
+ compiler_flags=-v
+ linker_flags=-v
+ verstring=
+ output_objdir=.
+ libname=conftest
+ lt_save_allow_undefined_flag=$allow_undefined_flag
+ allow_undefined_flag=
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5
+ (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ then
+ lt_cv_archive_cmds_need_lc=no
+ else
+ lt_cv_archive_cmds_need_lc=yes
+ fi
+ allow_undefined_flag=$lt_save_allow_undefined_flag
+ else
+ cat conftest.err 1>&5
+ fi
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5
+$as_echo "$lt_cv_archive_cmds_need_lc" >&6; }
+ archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc
+ ;;
+ esac
+ fi
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5
+$as_echo_n "checking dynamic linker characteristics... " >&6; }
+
+if test yes = "$GCC"; then
+ case $host_os in
+ darwin*) lt_awk_arg='/^libraries:/,/LR/' ;;
+ *) lt_awk_arg='/^libraries:/' ;;
+ esac
+ case $host_os in
+ mingw* | cegcc*) lt_sed_strip_eq='s|=\([A-Za-z]:\)|\1|g' ;;
+ *) lt_sed_strip_eq='s|=/|/|g' ;;
+ esac
+ lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq`
+ case $lt_search_path_spec in
+ *\;*)
+ # if the path contains ";" then we assume it to be the separator
+ # otherwise default to the standard path separator (i.e. ":") - it is
+ # assumed that no part of a normal pathname contains ";" but that should
+ # okay in the real world where ";" in dirpaths is itself problematic.
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'`
+ ;;
+ *)
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"`
+ ;;
+ esac
+ # Ok, now we have the path, separated by spaces, we can step through it
+ # and add multilib dir if necessary...
+ lt_tmp_lt_search_path_spec=
+ lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+ # ...but if some path component already ends with the multilib dir we assume
+ # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer).
+ case "$lt_multi_os_dir; $lt_search_path_spec " in
+ "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*)
+ lt_multi_os_dir=
+ ;;
+ esac
+ for lt_sys_path in $lt_search_path_spec; do
+ if test -d "$lt_sys_path$lt_multi_os_dir"; then
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir"
+ elif test -n "$lt_multi_os_dir"; then
+ test -d "$lt_sys_path" && \
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+ fi
+ done
+ lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk '
+BEGIN {RS = " "; FS = "/|\n";} {
+ lt_foo = "";
+ lt_count = 0;
+ for (lt_i = NF; lt_i > 0; lt_i--) {
+ if ($lt_i != "" && $lt_i != ".") {
+ if ($lt_i == "..") {
+ lt_count++;
+ } else {
+ if (lt_count == 0) {
+ lt_foo = "/" $lt_i lt_foo;
+ } else {
+ lt_count--;
+ }
+ }
+ }
+ }
+ if (lt_foo != "") { lt_freq[lt_foo]++; }
+ if (lt_freq[lt_foo] == 1) { print lt_foo; }
+}'`
+ # AWK program above erroneously prepends '/' to C:/dos/paths
+ # for these hosts.
+ case $host_os in
+ mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\
+ $SED 's|/\([A-Za-z]:\)|\1|g'` ;;
+ esac
+ sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP`
+else
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=.so
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+
+
+case $host_os in
+aix3*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname.a'
+ shlibpath_var=LIBPATH
+
+ # AIX 3 has no versioning support, so we append a major version to the name.
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+
+aix[4-9]*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ hardcode_into_libs=yes
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 supports IA64
+ library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ else
+ # With GCC up to 2.95.x, collect2 would create an import file
+ # for dependence libraries. The import file would start with
+ # the line '#! .'. This would cause the generated library to
+ # depend on '.', always an invalid library. This was fixed in
+ # development snapshots of GCC prior to 3.0.
+ case $host_os in
+ aix4 | aix4.[01] | aix4.[01].*)
+ if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+ echo ' yes '
+ echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then
+ :
+ else
+ can_build_shared=no
+ fi
+ ;;
+ esac
+ # Using Import Files as archive members, it is possible to support
+ # filename-based versioning of shared library archives on AIX. While
+ # this would work for both with and without runtime linking, it will
+ # prevent static linking of such archives. So we do filename-based
+ # shared library versioning with .so extension only, which is used
+ # when both runtime linking and shared linking is enabled.
+ # Unfortunately, runtime linking may impact performance, so we do
+ # not want this to be the default eventually. Also, we use the
+ # versioned .so libs for executables only if there is the -brtl
+ # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only.
+ # To allow for filename-based versioning support, we need to create
+ # libNAME.so.V as an archive file, containing:
+ # *) an Import File, referring to the versioned filename of the
+ # archive as well as the shared archive member, telling the
+ # bitwidth (32 or 64) of that shared object, and providing the
+ # list of exported symbols of that shared object, eventually
+ # decorated with the 'weak' keyword
+ # *) the shared object with the F_LOADONLY flag set, to really avoid
+ # it being seen by the linker.
+ # At run time we better use the real file rather than another symlink,
+ # but for link time we create the symlink libNAME.so -> libNAME.so.V
+
+ case $with_aix_soname,$aix_use_runtimelinking in
+ # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct
+ # soname into executable. Probably we can add versioning support to
+ # collect2, so additional links can be useful in future.
+ aix,yes) # traditional libtool
+ dynamic_linker='AIX unversionable lib.so'
+ # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+ # instead of lib<name>.a to let people know that these are not
+ # typical AIX shared libraries.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ ;;
+ aix,no) # traditional AIX only
+ dynamic_linker='AIX lib.a(lib.so.V)'
+ # We preserve .a as extension for shared libraries through AIX4.2
+ # and later when we are not doing run time linking.
+ library_names_spec='$libname$release.a $libname.a'
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+ svr4,*) # full svr4 only
+ dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)"
+ library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
+ # We do not specify a path in Import Files, so LIBPATH fires.
+ shlibpath_overrides_runpath=yes
+ ;;
+ *,yes) # both, prefer svr4
+ dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)"
+ library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
+ # unpreferred sharedlib libNAME.a needs extra handling
+ postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"'
+ postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"'
+ # We do not specify a path in Import Files, so LIBPATH fires.
+ shlibpath_overrides_runpath=yes
+ ;;
+ *,no) # both, prefer aix
+ dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)"
+ library_names_spec='$libname$release.a $libname.a'
+ soname_spec='$libname$release$shared_ext$major'
+ # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling
+ postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)'
+ postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"'
+ ;;
+ esac
+ shlibpath_var=LIBPATH
+ fi
+ ;;
+
+amigaos*)
+ case $host_cpu in
+ powerpc)
+ # Since July 2007 AmigaOS4 officially supports .so libraries.
+ # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ ;;
+ m68k)
+ library_names_spec='$libname.ixlibrary $libname.a'
+ # Create ${libname}_ixlibrary.a entries in /sys/libs.
+ finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+ ;;
+ esac
+ ;;
+
+beos*)
+ library_names_spec='$libname$shared_ext'
+ dynamic_linker="$host_os ld.so"
+ shlibpath_var=LIBRARY_PATH
+ ;;
+
+bsdi[45]*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+ # the default ld.so.conf also contains /usr/contrib/lib and
+ # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+ # libtool to hard-code these into programs
+ ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+ version_type=windows
+ shrext_cmds=.dll
+ need_version=no
+ need_lib_prefix=no
+
+ case $GCC,$cc_basename in
+ yes,*)
+ # gcc
+ library_names_spec='$libname.dll.a'
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+
+ case $host_os in
+ cygwin*)
+ # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+ soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"
+ ;;
+ mingw* | cegcc*)
+ # MinGW DLLs use traditional 'lib' prefix
+ soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+ ;;
+ pw32*)
+ # pw32 DLLs use 'pw' prefix rather than 'lib'
+ library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+ ;;
+ esac
+ dynamic_linker='Win32 ld.exe'
+ ;;
+
+ *,cl*)
+ # Native MSVC
+ libname_spec='$name'
+ soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+ library_names_spec='$libname.dll.lib'
+
+ case $build_os in
+ mingw*)
+ sys_lib_search_path_spec=
+ lt_save_ifs=$IFS
+ IFS=';'
+ for lt_path in $LIB
+ do
+ IFS=$lt_save_ifs
+ # Let DOS variable expansion print the short 8.3 style file name.
+ lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"`
+ sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path"
+ done
+ IFS=$lt_save_ifs
+ # Convert to MSYS style.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'`
+ ;;
+ cygwin*)
+ # Convert to unix form, then to dos form, then back to unix form
+ # but this time dos style (no spaces!) so that the unix form looks
+ # like /cygdrive/c/PROGRA~1:/cygdr...
+ sys_lib_search_path_spec=`cygpath --path --unix "$LIB"`
+ sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null`
+ sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ ;;
+ *)
+ sys_lib_search_path_spec=$LIB
+ if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then
+ # It is most probably a Windows format PATH.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ # FIXME: find the short name or the path components, as spaces are
+ # common. (e.g. "Program Files" -> "PROGRA~1")
+ ;;
+ esac
+
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+ dynamic_linker='Win32 link.exe'
+ ;;
+
+ *)
+ # Assume MSVC wrapper
+ library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib'
+ dynamic_linker='Win32 ld.exe'
+ ;;
+ esac
+ # FIXME: first we should search . and the directory the executable is in
+ shlibpath_var=PATH
+ ;;
+
+darwin* | rhapsody*)
+ dynamic_linker="$host_os dyld"
+ version_type=darwin
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$major$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$major$shared_ext'
+ shlibpath_overrides_runpath=yes
+ shlibpath_var=DYLD_LIBRARY_PATH
+ shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"
+ sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+ ;;
+
+dgux*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+freebsd* | dragonfly*)
+ # DragonFly does not have aout. When/if they implement a new
+ # versioning mechanism, adjust this.
+ if test -x /usr/bin/objformat; then
+ objformat=`/usr/bin/objformat`
+ else
+ case $host_os in
+ freebsd[23].*) objformat=aout ;;
+ *) objformat=elf ;;
+ esac
+ fi
+ version_type=freebsd-$objformat
+ case $version_type in
+ freebsd-elf*)
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ need_version=no
+ need_lib_prefix=no
+ ;;
+ freebsd-*)
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ need_version=yes
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_os in
+ freebsd2.*)
+ shlibpath_overrides_runpath=yes
+ ;;
+ freebsd3.[01]* | freebsdelf3.[01]*)
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ freebsd3.[2-9]* | freebsdelf3.[2-9]* | \
+ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1)
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+ *) # from 4.6 on, and DragonFly
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ esac
+ ;;
+
+haiku*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ dynamic_linker="$host_os runtime_loader"
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'
+ hardcode_into_libs=yes
+ ;;
+
+hpux9* | hpux10* | hpux11*)
+ # Give a soname corresponding to the major version so that dld.sl refuses to
+ # link against other versions.
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ case $host_cpu in
+ ia64*)
+ shrext_cmds='.so'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.so"
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ if test 32 = "$HPUX_IA64_MODE"; then
+ sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+ sys_lib_dlsearch_path_spec=/usr/lib/hpux32
+ else
+ sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+ sys_lib_dlsearch_path_spec=/usr/lib/hpux64
+ fi
+ ;;
+ hppa*64*)
+ shrext_cmds='.sl'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ *)
+ shrext_cmds='.sl'
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=SHLIB_PATH
+ shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+ esac
+ # HP-UX runs *really* slowly unless shared libraries are mode 555, ...
+ postinstall_cmds='chmod 555 $lib'
+ # or fails outright, so override atomically:
+ install_override_mode=555
+ ;;
+
+interix[3-9]*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $host_os in
+ nonstopux*) version_type=nonstopux ;;
+ *)
+ if test yes = "$lt_cv_prog_gnu_ld"; then
+ version_type=linux # correct to gnu/linux during the next big refactor
+ else
+ version_type=irix
+ fi ;;
+ esac
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext'
+ case $host_os in
+ irix5* | nonstopux*)
+ libsuff= shlibsuff=
+ ;;
+ *)
+ case $LD in # libtool.m4 will add one of these switches to LD
+ *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+ libsuff= shlibsuff= libmagic=32-bit;;
+ *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+ libsuff=32 shlibsuff=N32 libmagic=N32;;
+ *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+ libsuff=64 shlibsuff=64 libmagic=64-bit;;
+ *) libsuff= shlibsuff= libmagic=never-match;;
+ esac
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff"
+ sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff"
+ hardcode_into_libs=yes
+ ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+ dynamic_linker=no
+ ;;
+
+linux*android*)
+ version_type=none # Android doesn't support versioned libraries.
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext'
+ soname_spec='$libname$release$shared_ext'
+ finish_cmds=
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ dynamic_linker='Android linker'
+ # Don't embed -rpath directories since the linker doesn't support them.
+ hardcode_libdir_flag_spec='-L$libdir'
+ ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+
+ # Some binutils ld are patched to set DT_RUNPATH
+ if ${lt_cv_shlibpath_overrides_runpath+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_shlibpath_overrides_runpath=no
+ save_LDFLAGS=$LDFLAGS
+ save_libdir=$libdir
+ eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \
+ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then :
+ lt_cv_shlibpath_overrides_runpath=yes
+fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS=$save_LDFLAGS
+ libdir=$save_libdir
+
+fi
+
+ shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ # Ideally, we could use ldconfig to report *all* directores which are
+ # searched for libraries, however this is still not possible. Aside from not
+ # being certain /sbin/ldconfig is available, command
+ # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64,
+ # even though it is searched at run-time. Try to do the best guess by
+ # appending ld.so.conf contents (and includes) to the search path.
+ if test -f /etc/ld.so.conf; then
+ lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
+ sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+ fi
+
+ # We used to test for /lib/ld.so.1 and disable shared libraries on
+ # powerpc, because MkLinux only supported shared libraries with the
+ # GNU dynamic linker. Since this was broken with cross compilers,
+ # most powerpc-linux boxes support dynamic linking these days and
+ # people can always --disable-shared, the test was removed, and we
+ # assume the GNU/Linux dynamic linker is in use.
+ dynamic_linker='GNU/Linux ld.so'
+ ;;
+
+netbsd*)
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ dynamic_linker='NetBSD (a.out) ld.so'
+ else
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ dynamic_linker='NetBSD ld.elf_so'
+ fi
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+
+newsos6)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+*nto* | *qnx*)
+ version_type=qnx
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ dynamic_linker='ldqnx.so'
+ ;;
+
+openbsd* | bitrig*)
+ version_type=sunos
+ sys_lib_dlsearch_path_spec=/usr/lib
+ need_lib_prefix=no
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ need_version=no
+ else
+ need_version=yes
+ fi
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+os2*)
+ libname_spec='$name'
+ version_type=windows
+ shrext_cmds=.dll
+ need_version=no
+ need_lib_prefix=no
+ # OS/2 can only load a DLL with a base name of 8 characters or less.
+ soname_spec='`test -n "$os2dllname" && libname="$os2dllname";
+ v=$($ECHO $release$versuffix | tr -d .-);
+ n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _);
+ $ECHO $n$v`$shared_ext'
+ library_names_spec='${libname}_dll.$libext'
+ dynamic_linker='OS/2 ld.exe'
+ shlibpath_var=BEGINLIBPATH
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ ;;
+
+osf3* | osf4* | osf5*)
+ version_type=osf
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+
+rdos*)
+ dynamic_linker=no
+ ;;
+
+solaris*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ # ldd complains unless libraries are executable
+ postinstall_cmds='chmod +x $lib'
+ ;;
+
+sunos4*)
+ version_type=sunos
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ if test yes = "$with_gnu_ld"; then
+ need_lib_prefix=no
+ fi
+ need_version=yes
+ ;;
+
+sysv4 | sysv4.3*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_vendor in
+ sni)
+ shlibpath_overrides_runpath=no
+ need_lib_prefix=no
+ runpath_var=LD_RUN_PATH
+ ;;
+ siemens)
+ need_lib_prefix=no
+ ;;
+ motorola)
+ need_lib_prefix=no
+ need_version=no
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+ ;;
+ esac
+ ;;
+
+sysv4*MP*)
+ if test -d /usr/nec; then
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext'
+ soname_spec='$libname$shared_ext.$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ fi
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ version_type=sco
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ if test yes = "$with_gnu_ld"; then
+ sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+ else
+ sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+ case $host_os in
+ sco3.2v5*)
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+ ;;
+ esac
+ fi
+ sys_lib_dlsearch_path_spec='/usr/lib'
+ ;;
+
+tpf*)
+ # TPF is a cross-target only. Preferred cross-host = GNU/Linux.
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+uts4*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+*)
+ dynamic_linker=no
+ ;;
+esac
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5
+$as_echo "$dynamic_linker" >&6; }
+test no = "$dynamic_linker" && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test yes = "$GCC"; then
+ variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then
+ sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec
+fi
+
+if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then
+ sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec
+fi
+
+# remember unaugmented sys_lib_dlsearch_path content for libtool script decls...
+configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec
+
+# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code
+func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH"
+
+# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool
+configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5
+$as_echo_n "checking how to hardcode library paths into programs... " >&6; }
+hardcode_action=
+if test -n "$hardcode_libdir_flag_spec" ||
+ test -n "$runpath_var" ||
+ test yes = "$hardcode_automatic"; then
+
+ # We can hardcode non-existent directories.
+ if test no != "$hardcode_direct" &&
+ # If the only mechanism to avoid hardcoding is shlibpath_var, we
+ # have to relink, otherwise we might link with an installed library
+ # when we should be linking with a yet-to-be-installed one
+ ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, )" &&
+ test no != "$hardcode_minus_L"; then
+ # Linking always hardcodes the temporary library directory.
+ hardcode_action=relink
+ else
+ # We can link without hardcoding, and we can hardcode nonexisting dirs.
+ hardcode_action=immediate
+ fi
+else
+ # We cannot hardcode anything, or else we can only hardcode existing
+ # directories.
+ hardcode_action=unsupported
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5
+$as_echo "$hardcode_action" >&6; }
+
+if test relink = "$hardcode_action" ||
+ test yes = "$inherit_rpath"; then
+ # Fast installation is not supported
+ enable_fast_install=no
+elif test yes = "$shlibpath_overrides_runpath" ||
+ test no = "$enable_shared"; then
+ # Fast installation is not necessary
+ enable_fast_install=needless
+fi
+
+
+
+
+
+
+ if test yes != "$enable_dlopen"; then
+ enable_dlopen=unknown
+ enable_dlopen_self=unknown
+ enable_dlopen_self_static=unknown
+else
+ lt_cv_dlopen=no
+ lt_cv_dlopen_libs=
+
+ case $host_os in
+ beos*)
+ lt_cv_dlopen=load_add_on
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ;;
+
+ mingw* | pw32* | cegcc*)
+ lt_cv_dlopen=LoadLibrary
+ lt_cv_dlopen_libs=
+ ;;
+
+ cygwin*)
+ lt_cv_dlopen=dlopen
+ lt_cv_dlopen_libs=
+ ;;
+
+ darwin*)
+ # if libdl is installed we need to link against it
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dl_dlopen=yes
+else
+ ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+ lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl
+else
+
+ lt_cv_dlopen=dyld
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+
+fi
+
+ ;;
+
+ tpf*)
+ # Don't try to run any link tests for TPF. We know it's impossible
+ # because TPF is a cross-compiler, and we know how we open DSOs.
+ lt_cv_dlopen=dlopen
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=no
+ ;;
+
+ *)
+ ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load"
+if test "x$ac_cv_func_shl_load" = xyes; then :
+ lt_cv_dlopen=shl_load
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5
+$as_echo_n "checking for shl_load in -ldld... " >&6; }
+if ${ac_cv_lib_dld_shl_load+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char shl_load ();
+int
+main ()
+{
+return shl_load ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dld_shl_load=yes
+else
+ ac_cv_lib_dld_shl_load=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5
+$as_echo "$ac_cv_lib_dld_shl_load" >&6; }
+if test "x$ac_cv_lib_dld_shl_load" = xyes; then :
+ lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld
+else
+ ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen"
+if test "x$ac_cv_func_dlopen" = xyes; then :
+ lt_cv_dlopen=dlopen
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dl_dlopen=yes
+else
+ ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+ lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5
+$as_echo_n "checking for dlopen in -lsvld... " >&6; }
+if ${ac_cv_lib_svld_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsvld $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_svld_dlopen=yes
+else
+ ac_cv_lib_svld_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5
+$as_echo "$ac_cv_lib_svld_dlopen" >&6; }
+if test "x$ac_cv_lib_svld_dlopen" = xyes; then :
+ lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5
+$as_echo_n "checking for dld_link in -ldld... " >&6; }
+if ${ac_cv_lib_dld_dld_link+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dld_link ();
+int
+main ()
+{
+return dld_link ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dld_dld_link=yes
+else
+ ac_cv_lib_dld_dld_link=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5
+$as_echo "$ac_cv_lib_dld_dld_link" >&6; }
+if test "x$ac_cv_lib_dld_dld_link" = xyes; then :
+ lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+ ;;
+ esac
+
+ if test no = "$lt_cv_dlopen"; then
+ enable_dlopen=no
+ else
+ enable_dlopen=yes
+ fi
+
+ case $lt_cv_dlopen in
+ dlopen)
+ save_CPPFLAGS=$CPPFLAGS
+ test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+ save_LDFLAGS=$LDFLAGS
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+ save_LIBS=$LIBS
+ LIBS="$lt_cv_dlopen_libs $LIBS"
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5
+$as_echo_n "checking whether a program can dlopen itself... " >&6; }
+if ${lt_cv_dlopen_self+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test yes = "$cross_compiling"; then :
+ lt_cv_dlopen_self=cross
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+#line $LINENO "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+/* When -fvisibility=hidden is used, assume the code has been annotated
+ correspondingly for the symbols needed. */
+#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+int fnord () __attribute__((visibility("default")));
+#endif
+
+int fnord () { return 42; }
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else
+ {
+ if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ else puts (dlerror ());
+ }
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ return status;
+}
+_LT_EOF
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then
+ (./conftest; exit; ) >&5 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;;
+ x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;;
+ x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;;
+ esac
+ else :
+ # compilation failed
+ lt_cv_dlopen_self=no
+ fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5
+$as_echo "$lt_cv_dlopen_self" >&6; }
+
+ if test yes = "$lt_cv_dlopen_self"; then
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5
+$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; }
+if ${lt_cv_dlopen_self_static+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test yes = "$cross_compiling"; then :
+ lt_cv_dlopen_self_static=cross
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+#line $LINENO "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+/* When -fvisibility=hidden is used, assume the code has been annotated
+ correspondingly for the symbols needed. */
+#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+int fnord () __attribute__((visibility("default")));
+#endif
+
+int fnord () { return 42; }
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else
+ {
+ if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ else puts (dlerror ());
+ }
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ return status;
+}
+_LT_EOF
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then
+ (./conftest; exit; ) >&5 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;;
+ x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;;
+ x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;;
+ esac
+ else :
+ # compilation failed
+ lt_cv_dlopen_self_static=no
+ fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5
+$as_echo "$lt_cv_dlopen_self_static" >&6; }
+ fi
+
+ CPPFLAGS=$save_CPPFLAGS
+ LDFLAGS=$save_LDFLAGS
+ LIBS=$save_LIBS
+ ;;
+ esac
+
+ case $lt_cv_dlopen_self in
+ yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+ *) enable_dlopen_self=unknown ;;
+ esac
+
+ case $lt_cv_dlopen_self_static in
+ yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+ *) enable_dlopen_self_static=unknown ;;
+ esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+striplib=
+old_striplib=
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5
+$as_echo_n "checking whether stripping libraries is possible... " >&6; }
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+ test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+ test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+ case $host_os in
+ darwin*)
+ if test -n "$STRIP"; then
+ striplib="$STRIP -x"
+ old_striplib="$STRIP -S"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ fi
+ ;;
+ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ ;;
+ esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+ # Report what library types will actually be built
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5
+$as_echo_n "checking if libtool supports shared libraries... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5
+$as_echo "$can_build_shared" >&6; }
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5
+$as_echo_n "checking whether to build shared libraries... " >&6; }
+ test no = "$can_build_shared" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test yes = "$enable_shared" && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+
+ aix[4-9]*)
+ if test ia64 != "$host_cpu"; then
+ case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in
+ yes,aix,yes) ;; # shared object as lib.so file only
+ yes,svr4,*) ;; # shared object as lib.so archive member only
+ yes,*) enable_static=no ;; # shared object in lib.a archive as well
+ esac
+ fi
+ ;;
+ esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5
+$as_echo "$enable_shared" >&6; }
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5
+$as_echo_n "checking whether to build static libraries... " >&6; }
+ # Make sure either enable_shared or enable_static is yes.
+ test yes = "$enable_shared" || enable_static=yes
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5
+$as_echo "$enable_static" >&6; }
+
+
+
+
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+CC=$lt_save_CC
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ac_config_commands="$ac_config_commands libtool"
+
+
+
+
+# Only expand once:
+
+
+
+ol_link_perl=no
+if test $ol_enable_perl != no ; then
+ # Extract the first word of "perl", so it can be a program name with args.
+set dummy perl; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PERLBIN+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $PERLBIN in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PERLBIN="$PERLBIN" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_PERLBIN="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ test -z "$ac_cv_path_PERLBIN" && ac_cv_path_PERLBIN="/usr/bin/perl"
+ ;;
+esac
+fi
+PERLBIN=$ac_cv_path_PERLBIN
+if test -n "$PERLBIN"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PERLBIN" >&5
+$as_echo "$PERLBIN" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+ if test "no$PERLBIN" = "no" ; then
+ if test $ol_enable_perl = yes ; then
+ as_fn_error $? "could not locate perl" "$LINENO" 5
+ fi
+
+ else
+ PERL_CPPFLAGS="`$PERLBIN -MExtUtils::Embed -e ccopts`"
+ PERL_LDFLAGS="`$PERLBIN -MExtUtils::Embed -e ldopts|sed -e 's/ -lc / /' -e 's/ -lc$//'`"
+
+ if test x"$ol_enable_perl" = "xyes" ; then
+ SLAPD_PERL_LDFLAGS="$PERL_LDFLAGS"
+ else
+ MOD_PERL_LDFLAGS="$PERL_LDFLAGS"
+ fi
+ ol_link_perl=yes
+ fi
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if ${ac_cv_prog_CPP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using MS Visual C++" >&5
+$as_echo_n "checking whether we are using MS Visual C++... " >&6; }
+if ${ol_cv_msvc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#ifndef _MSC_VER
+#include <__FOO__/generate_error.h>
+#endif
+
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ ol_cv_msvc=yes
+else
+ ol_cv_msvc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_msvc" >&5
+$as_echo "$ol_cv_msvc" >&6; }
+
+case $host_os in
+ *mingw32* ) ac_cv_mingw32=yes ;;
+ *cygwin* ) ac_cv_cygwin=yes ;;
+ *interix* ) ac_cv_interix=yes ;;
+esac
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}windres", so it can be a program name with args.
+set dummy ${ac_tool_prefix}windres; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_RC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$RC"; then
+ ac_cv_prog_RC="$RC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_RC="${ac_tool_prefix}windres"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+RC=$ac_cv_prog_RC
+if test -n "$RC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RC" >&5
+$as_echo "$RC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RC"; then
+ ac_ct_RC=$RC
+ # Extract the first word of "windres", so it can be a program name with args.
+set dummy windres; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_RC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_RC"; then
+ ac_cv_prog_ac_ct_RC="$ac_ct_RC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_RC="windres"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RC=$ac_cv_prog_ac_ct_RC
+if test -n "$ac_ct_RC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RC" >&5
+$as_echo "$ac_ct_RC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_RC" = x; then
+ RC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ RC=$ac_ct_RC
+ fi
+else
+ RC="$ac_cv_prog_RC"
+fi
+
+
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define EXEEXT "${EXEEXT}"
+_ACEOF
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for be_app in -lbe" >&5
+$as_echo_n "checking for be_app in -lbe... " >&6; }
+if ${ac_cv_lib_be_be_app+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lbe -lroot -lnet $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char be_app ();
+int
+main ()
+{
+return be_app ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_be_be_app=yes
+else
+ ac_cv_lib_be_be_app=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_be_be_app" >&5
+$as_echo "$ac_cv_lib_be_be_app" >&6; }
+if test "x$ac_cv_lib_be_be_app" = xyes; then :
+ LIBS="$LIBS -lbe -lroot -lnet"
+else
+ :
+fi
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+ { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ sed '10a\
+... rest of stderr output deleted ...
+ 10q' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ fi
+ rm -f conftest.er1 conftest.err
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_compiler_gnu=yes
+else
+ ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+else
+ CFLAGS=""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+if test "X${ac_cv_prog_cc_stdc}" = "Xno" ; then
+ as_fn_error $? "OpenLDAP requires compiler to support STDC constructs." "$LINENO" 5
+fi
+
+# test for make depend flag
+OL_MKDEP=
+OL_MKDEP_FLAGS=
+if test -z "${MKDEP}"; then
+ OL_MKDEP="${CC-cc}"
+ if test -z "${MKDEP_FLAGS}"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${OL_MKDEP} depend flag" >&5
+$as_echo_n "checking for ${OL_MKDEP} depend flag... " >&6; }
+if ${ol_cv_mkdep+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ ol_cv_mkdep=no
+ for flag in "-M" "-xM"; do
+ cat > conftest.c <<EOF
+ noCode;
+EOF
+ if { ac_try='$OL_MKDEP $flag conftest.c'
+ { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+ (eval $ac_try) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; } \
+ | grep '^conftest\.'"${ac_objext}" >/dev/null 2>&1
+ then
+ if test ! -f conftest."${ac_object}" ; then
+ ol_cv_mkdep=$flag
+ OL_MKDEP_FLAGS="$flag"
+ break
+ fi
+ fi
+ done
+ rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_mkdep" >&5
+$as_echo "$ol_cv_mkdep" >&6; }
+ test "$ol_cv_mkdep" = no && OL_MKDEP=":"
+ else
+ cc_cv_mkdep=yes
+ OL_MKDEP_FLAGS="${MKDEP_FLAGS}"
+ fi
+else
+ cc_cv_mkdep=yes
+ OL_MKDEP="${MKDEP}"
+ OL_MKDEP_FLAGS="${MKDEP_FLAGS}"
+fi
+
+
+
+if test "${ol_cv_mkdep}" = no ; then
+ # this will soon become an error
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: do not know how to generate dependencies" >&5
+$as_echo "$as_me: WARNING: do not know how to generate dependencies" >&2;}
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for afopen in -ls" >&5
+$as_echo_n "checking for afopen in -ls... " >&6; }
+if ${ac_cv_lib_s_afopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ls $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char afopen ();
+int
+main ()
+{
+return afopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_s_afopen=yes
+else
+ ac_cv_lib_s_afopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_s_afopen" >&5
+$as_echo "$ac_cv_lib_s_afopen" >&6; }
+if test "x$ac_cv_lib_s_afopen" = xyes; then :
+
+ AUTH_LIBS=-ls
+
+$as_echo "#define HAVE_AIX_SECURITY 1" >>confdefs.h
+
+
+fi
+
+
+case "$target" in
+*-ibm-openedition)
+ ac_cv_func_getopt=no
+
+$as_echo "#define BOTH_STRINGS_H 1" >>confdefs.h
+
+ ;;
+esac
+
+ol_link_modules=no
+WITH_MODULES_ENABLED=no
+if test $ol_enable_modules != no ; then
+ for ac_header in ltdl.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "ltdl.h" "ac_cv_header_ltdl_h" "$ac_includes_default"
+if test "x$ac_cv_header_ltdl_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LTDL_H 1
+_ACEOF
+
+fi
+
+done
+
+
+ if test $ac_cv_header_ltdl_h = no ; then
+ as_fn_error $? "could not locate libtool ltdl.h" "$LINENO" 5
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lt_dlinit in -lltdl" >&5
+$as_echo_n "checking for lt_dlinit in -lltdl... " >&6; }
+if ${ac_cv_lib_ltdl_lt_dlinit+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lltdl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char lt_dlinit ();
+int
+main ()
+{
+return lt_dlinit ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_ltdl_lt_dlinit=yes
+else
+ ac_cv_lib_ltdl_lt_dlinit=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ltdl_lt_dlinit" >&5
+$as_echo "$ac_cv_lib_ltdl_lt_dlinit" >&6; }
+if test "x$ac_cv_lib_ltdl_lt_dlinit" = xyes; then :
+
+ MODULES_LIBS=-lltdl
+
+$as_echo "#define HAVE_LIBLTDL 1" >>confdefs.h
+
+
+fi
+
+
+ if test "$ac_cv_lib_ltdl_lt_dlinit" = no ; then
+ as_fn_error $? "could not locate libtool -lltdl" "$LINENO" 5
+ fi
+ ol_link_modules=yes
+ WITH_MODULES_ENABLED=yes
+fi
+
+# test for EBCDIC
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for EBCDIC" >&5
+$as_echo_n "checking for EBCDIC... " >&6; }
+if ${ol_cv_cpp_ebcdic+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#if !('M' == 0xd4)
+#include <__ASCII__/generate_error.h>
+#endif
+
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ ol_cv_cpp_ebcdic=yes
+else
+ ol_cv_cpp_ebcdic=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_cpp_ebcdic" >&5
+$as_echo "$ol_cv_cpp_ebcdic" >&6; }
+if test $ol_cv_cpp_ebcdic = yes ; then
+
+$as_echo "#define HAVE_EBCDIC 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ol_cv_header_stdc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ ol_cv_header_stdc=yes
+else
+ ol_cv_header_stdc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+if test $ol_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+ ol_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ol_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then :
+
+else
+ ol_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ol_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+if test "$cross_compiling" = yes; then :
+ :
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ctype.h>
+#ifndef HAVE_EBCDIC
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+ ol_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_header_stdc" >&5
+$as_echo "$ol_cv_header_stdc" >&6; }
+if test $ol_cv_header_stdc = yes; then
+ $as_echo "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+ac_cv_header_stdc=disable
+
+
+if test $ol_cv_header_stdc != yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: could not locate Standard C compliant headers" >&5
+$as_echo "$as_me: WARNING: could not locate Standard C compliant headers" >&2;}
+fi
+
+ac_header_dirent=no
+for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
+ as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5
+$as_echo_n "checking for $ac_hdr that defines DIR... " >&6; }
+if eval \${$as_ac_Header+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <$ac_hdr>
+
+int
+main ()
+{
+if ((DIR *) 0)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$as_ac_Header=yes"
+else
+ eval "$as_ac_Header=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$as_ac_Header
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_hdr" | $as_tr_cpp` 1
+_ACEOF
+
+ac_header_dirent=$ac_hdr; break
+fi
+
+done
+# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix.
+if test $ac_header_dirent = dirent.h; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
+$as_echo_n "checking for library containing opendir... " >&6; }
+if ${ac_cv_search_opendir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char opendir ();
+int
+main ()
+{
+return opendir ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' dir; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_opendir=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_opendir+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_opendir+:} false; then :
+
+else
+ ac_cv_search_opendir=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5
+$as_echo "$ac_cv_search_opendir" >&6; }
+ac_res=$ac_cv_search_opendir
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
+$as_echo_n "checking for library containing opendir... " >&6; }
+if ${ac_cv_search_opendir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char opendir ();
+int
+main ()
+{
+return opendir ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' x; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_opendir=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_opendir+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_opendir+:} false; then :
+
+else
+ ac_cv_search_opendir=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5
+$as_echo "$ac_cv_search_opendir" >&6; }
+ac_res=$ac_cv_search_opendir
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5
+$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; }
+if ${ac_cv_header_sys_wait_h+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+int
+main ()
+{
+ int s;
+ wait (&s);
+ s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_sys_wait_h=yes
+else
+ ac_cv_header_sys_wait_h=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_wait_h" >&5
+$as_echo "$ac_cv_header_sys_wait_h" >&6; }
+if test $ac_cv_header_sys_wait_h = yes; then
+
+$as_echo "#define HAVE_SYS_WAIT_H 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether termios.h defines TIOCGWINSZ" >&5
+$as_echo_n "checking whether termios.h defines TIOCGWINSZ... " >&6; }
+if ${ac_cv_sys_tiocgwinsz_in_termios_h+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <termios.h>
+#ifdef TIOCGWINSZ
+ yes
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "yes" >/dev/null 2>&1; then :
+ ac_cv_sys_tiocgwinsz_in_termios_h=yes
+else
+ ac_cv_sys_tiocgwinsz_in_termios_h=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_tiocgwinsz_in_termios_h" >&5
+$as_echo "$ac_cv_sys_tiocgwinsz_in_termios_h" >&6; }
+
+if test $ac_cv_sys_tiocgwinsz_in_termios_h != yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether sys/ioctl.h defines TIOCGWINSZ" >&5
+$as_echo_n "checking whether sys/ioctl.h defines TIOCGWINSZ... " >&6; }
+if ${ac_cv_sys_tiocgwinsz_in_sys_ioctl_h+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#ifdef TIOCGWINSZ
+ yes
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "yes" >/dev/null 2>&1; then :
+ ac_cv_sys_tiocgwinsz_in_sys_ioctl_h=yes
+else
+ ac_cv_sys_tiocgwinsz_in_sys_ioctl_h=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_tiocgwinsz_in_sys_ioctl_h" >&5
+$as_echo "$ac_cv_sys_tiocgwinsz_in_sys_ioctl_h" >&6; }
+
+ if test $ac_cv_sys_tiocgwinsz_in_sys_ioctl_h = yes; then
+
+$as_echo "#define GWINSZ_IN_SYS_IOCTL 1" >>confdefs.h
+
+ fi
+fi
+
+
+for ac_header in \
+ arpa/inet.h \
+ arpa/nameser.h \
+ assert.h \
+ bits/types.h \
+ conio.h \
+ crypt.h \
+ direct.h \
+ errno.h \
+ fcntl.h \
+ filio.h \
+ getopt.h \
+ grp.h \
+ io.h \
+ libutil.h \
+ limits.h \
+ locale.h \
+ malloc.h \
+ memory.h \
+ psap.h \
+ pwd.h \
+ process.h \
+ sgtty.h \
+ shadow.h \
+ stddef.h \
+ string.h \
+ strings.h \
+ sysexits.h \
+ sys/file.h \
+ sys/filio.h \
+ sys/fstyp.h \
+ sys/errno.h \
+ sys/ioctl.h \
+ sys/param.h \
+ sys/privgrp.h \
+ sys/resource.h \
+ sys/select.h \
+ sys/socket.h \
+ sys/stat.h \
+ sys/syslog.h \
+ sys/time.h \
+ sys/types.h \
+ sys/uio.h \
+ sys/vmount.h \
+ syslog.h \
+ termios.h \
+ unistd.h \
+ utime.h \
+
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+if test "$ac_cv_mingw32" = yes \
+ -o "$ac_cv_interix" = yes \
+ -o "$ol_cv_msvc" = yes
+then
+ for ac_header in winsock.h winsock2.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+fi
+
+for ac_header in resolv.h
+do :
+ ac_fn_c_check_header_compile "$LINENO" "resolv.h" "ac_cv_header_resolv_h" "$ac_includes_default
+#include <netinet/in.h>
+
+"
+if test "x$ac_cv_header_resolv_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_RESOLV_H 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in netinet/tcp.h
+do :
+ ac_fn_c_check_header_compile "$LINENO" "netinet/tcp.h" "ac_cv_header_netinet_tcp_h" "$ac_includes_default
+#include <netinet/in.h>
+
+"
+if test "x$ac_cv_header_netinet_tcp_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_NETINET_TCP_H 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in sys/ucred.h
+do :
+ ac_fn_c_check_header_compile "$LINENO" "sys/ucred.h" "ac_cv_header_sys_ucred_h" "$ac_includes_default
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+"
+if test "x$ac_cv_header_sys_ucred_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_SYS_UCRED_H 1
+_ACEOF
+
+fi
+
+done
+
+
+
+for ac_func in sigaction sigset
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+for ac_func in fmemopen
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+if test $ac_cv_func_sigaction = no && test $ac_cv_func_sigaction = no ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sigset in -lV3" >&5
+$as_echo_n "checking for sigset in -lV3... " >&6; }
+if ${ac_cv_lib_V3_sigset+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lV3 $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char sigset ();
+int
+main ()
+{
+return sigset ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_V3_sigset=yes
+else
+ ac_cv_lib_V3_sigset=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_V3_sigset" >&5
+$as_echo "$ac_cv_lib_V3_sigset" >&6; }
+if test "x$ac_cv_lib_V3_sigset" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBV3 1
+_ACEOF
+
+ LIBS="-lV3 $LIBS"
+
+fi
+
+fi
+
+if test $ol_cv_msvc = yes ; then
+ ol_cv_winsock=yes
+fi
+
+if test "$ac_cv_header_winsock_h" = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for winsock" >&5
+$as_echo_n "checking for winsock... " >&6; }
+if ${ol_cv_winsock+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ save_LIBS="$LIBS"
+ for curlib in none ws2_32 wsock32; do
+ if test $curlib != none ; then
+ LIBS="$save_LIBS -l$curlib"
+ fi
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <winsock.h>
+
+int
+main ()
+{
+
+ socket(0,0,0);
+ select(0,NULL,NULL,NULL,NULL);
+ closesocket(0);
+ gethostname(NULL,0);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_winsock=$curlib
+else
+ ol_cv_winsock=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
+ test "$ol_cv_winsock" != no && break
+ done
+ LIBS="$save_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_winsock" >&5
+$as_echo "$ol_cv_winsock" >&6; }
+
+ if test $ol_cv_winsock != no ; then
+
+$as_echo "#define HAVE_WINSOCK 1" >>confdefs.h
+
+ ac_cv_func_socket=yes
+ ac_cv_func_select=yes
+ ac_cv_func_closesocket=yes
+ ac_cv_func_gethostname=yes
+
+ if test $ol_cv_winsock != none -a $ol_cv_winsock != yes ; then
+ LIBS="$LIBS -l$ol_cv_winsock"
+ fi
+
+ if test $ol_cv_winsock = ws2_32 -o $ol_cv_winsock = yes ; then
+
+$as_echo "#define HAVE_WINSOCK2 1" >>confdefs.h
+
+ fi
+ fi
+fi
+
+
+ac_fn_c_check_func "$LINENO" "socket" "ac_cv_func_socket"
+if test "x$ac_cv_func_socket" = xyes; then :
+ :
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lsocket" >&5
+$as_echo_n "checking for main in -lsocket... " >&6; }
+if ${ac_cv_lib_socket_main+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsocket $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+
+int
+main ()
+{
+return main ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_socket_main=yes
+else
+ ac_cv_lib_socket_main=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_main" >&5
+$as_echo "$ac_cv_lib_socket_main" >&6; }
+if test "x$ac_cv_lib_socket_main" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBSOCKET 1
+_ACEOF
+
+ LIBS="-lsocket $LIBS"
+
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for socket in -lnet" >&5
+$as_echo_n "checking for socket in -lnet... " >&6; }
+if ${ac_cv_lib_net_socket+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lnet $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char socket ();
+int
+main ()
+{
+return socket ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_net_socket=yes
+else
+ ac_cv_lib_net_socket=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_net_socket" >&5
+$as_echo "$ac_cv_lib_net_socket" >&6; }
+if test "x$ac_cv_lib_net_socket" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBNET 1
+_ACEOF
+
+ LIBS="-lnet $LIBS"
+
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lnsl_s" >&5
+$as_echo_n "checking for main in -lnsl_s... " >&6; }
+if ${ac_cv_lib_nsl_s_main+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lnsl_s $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+
+int
+main ()
+{
+return main ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_nsl_s_main=yes
+else
+ ac_cv_lib_nsl_s_main=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_s_main" >&5
+$as_echo "$ac_cv_lib_nsl_s_main" >&6; }
+if test "x$ac_cv_lib_nsl_s_main" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBNSL_S 1
+_ACEOF
+
+ LIBS="-lnsl_s $LIBS"
+
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lnsl" >&5
+$as_echo_n "checking for main in -lnsl... " >&6; }
+if ${ac_cv_lib_nsl_main+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lnsl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+
+int
+main ()
+{
+return main ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_nsl_main=yes
+else
+ ac_cv_lib_nsl_main=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_main" >&5
+$as_echo "$ac_cv_lib_nsl_main" >&6; }
+if test "x$ac_cv_lib_nsl_main" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBNSL 1
+_ACEOF
+
+ LIBS="-lnsl $LIBS"
+
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for socket in -linet" >&5
+$as_echo_n "checking for socket in -linet... " >&6; }
+if ${ac_cv_lib_inet_socket+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-linet $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char socket ();
+int
+main ()
+{
+return socket ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_inet_socket=yes
+else
+ ac_cv_lib_inet_socket=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_inet_socket" >&5
+$as_echo "$ac_cv_lib_inet_socket" >&6; }
+if test "x$ac_cv_lib_inet_socket" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBINET 1
+_ACEOF
+
+ LIBS="-linet $LIBS"
+
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lgen" >&5
+$as_echo_n "checking for main in -lgen... " >&6; }
+if ${ac_cv_lib_gen_main+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lgen $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+
+int
+main ()
+{
+return main ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_gen_main=yes
+else
+ ac_cv_lib_gen_main=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gen_main" >&5
+$as_echo "$ac_cv_lib_gen_main" >&6; }
+if test "x$ac_cv_lib_gen_main" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBGEN 1
+_ACEOF
+
+ LIBS="-lgen $LIBS"
+
+fi
+
+
+fi
+
+
+ac_fn_c_check_func "$LINENO" "select" "ac_cv_func_select"
+if test "x$ac_cv_func_select" = xyes; then :
+ :
+else
+ as_fn_error $? "select() required." "$LINENO" 5
+fi
+
+
+if test "${ac_cv_header_winsock_h}" != yes; then
+ for ac_header in sys/select.h sys/socket.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking types of arguments for select" >&5
+$as_echo_n "checking types of arguments for select... " >&6; }
+if ${ac_cv_func_select_args+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ for ac_arg234 in 'fd_set *' 'int *' 'void *'; do
+ for ac_arg1 in 'int' 'size_t' 'unsigned long int' 'unsigned int'; do
+ for ac_arg5 in 'struct timeval *' 'const struct timeval *'; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+
+int
+main ()
+{
+extern int select ($ac_arg1,
+ $ac_arg234, $ac_arg234, $ac_arg234,
+ $ac_arg5);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_func_select_args="$ac_arg1,$ac_arg234,$ac_arg5"; break 3
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done
+ done
+done
+# Provide a safe default value.
+: "${ac_cv_func_select_args=int,int *,struct timeval *}"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_select_args" >&5
+$as_echo "$ac_cv_func_select_args" >&6; }
+ac_save_IFS=$IFS; IFS=','
+set dummy `echo "$ac_cv_func_select_args" | sed 's/\*/\*/g'`
+IFS=$ac_save_IFS
+shift
+
+cat >>confdefs.h <<_ACEOF
+#define SELECT_TYPE_ARG1 $1
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define SELECT_TYPE_ARG234 ($2)
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define SELECT_TYPE_ARG5 ($3)
+_ACEOF
+
+rm -f conftest*
+
+fi
+
+
+for ac_func in poll
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+if test $ac_cv_func_poll = yes; then
+for ac_header in poll.h sys/poll.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+fi
+
+for ac_header in sys/epoll.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+if test "${ac_cv_header_sys_epoll_h}" = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for epoll system call" >&5
+$as_echo_n "checking for epoll system call... " >&6; }
+ if test "$cross_compiling" = yes; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+int main(int argc, char **argv)
+{
+ int epfd = epoll_create(256);
+ exit (epfd == -1 ? 1 : 0);
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+$as_echo "#define HAVE_EPOLL 1" >>confdefs.h
+
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+
+for ac_header in sys/event.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+if test "${ac_cv_header_sys_event_h}" = yes; then
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for kqueue system call" >&5
+$as_echo_n "checking for kqueue system call... " >&6; }
+if test "$cross_compiling" = yes; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+#ifdef HAVE_SYS_EVENT_H
+#include <sys/event.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+int main(int argc, char **argv)
+{
+ int kqfd = kqueue();
+ exit (kqfd == -1 ? 1 : 0);
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+$as_echo "#define HAVE_KQUEUE 1" >>confdefs.h
+
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+
+for ac_header in sys/devpoll.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+if test "${ac_cv_header_sys_devpoll_h}" = yes \
+ -a "${ac_cv_header_poll_h}" = yes ; \
+then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for /dev/poll" >&5
+$as_echo_n "checking for /dev/poll... " >&6; }
+ if test "$cross_compiling" = yes; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+int main(int argc, char **argv)
+{
+ int devpollfd = open("/dev/poll", /* O_RDWR */ 2);
+ exit (devpollfd == -1 ? 1 : 0);
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+$as_echo "#define HAVE_DEVPOLL 1" >>confdefs.h
+
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+
+for ac_func in strerror strerror_r
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+ol_cv_func_strerror_r=no
+if test "${ac_cv_func_strerror_r}" = yes ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking non-posix strerror_r" >&5
+$as_echo_n "checking non-posix strerror_r... " >&6; }
+if ${ol_cv_nonposix_strerror_r+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <string.h>
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "strerror_r" >/dev/null 2>&1; then :
+ ol_decl_strerror_r=yes
+else
+ ol_decl_strerror_r=no
+fi
+rm -f conftest*
+
+ if test $ol_decl_strerror_r = yes ; then
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <string.h>
+int
+main ()
+{
+ /* from autoconf 2.59 */
+ char buf[100];
+ char x = *strerror_r (0, buf, sizeof buf);
+ char *p = strerror_r (0, buf, sizeof buf);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ol_cv_nonposix_strerror_r=yes
+else
+ ol_cv_nonposix_strerror_r=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ else
+ if test "$cross_compiling" = yes; then :
+ ol_cv_nonposix_strerror_r=no
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+ main() {
+ char buf[100];
+ buf[0] = 0;
+ strerror_r( 1, buf, sizeof buf );
+ exit( buf[0] == 0 );
+ }
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ol_cv_nonposix_strerror_r=yes
+else
+ ol_cv_nonposix_strerror_r=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+ fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_nonposix_strerror_r" >&5
+$as_echo "$ol_cv_nonposix_strerror_r" >&6; }
+if test $ol_cv_nonposix_strerror_r = yes ; then
+
+$as_echo "#define HAVE_NONPOSIX_STRERROR_R 1" >>confdefs.h
+
+fi
+
+elif test "${ac_cv_func_strerror}" = no ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking existence of sys_errlist" >&5
+$as_echo_n "checking existence of sys_errlist... " >&6; }
+if ${ol_cv_have_sys_errlist+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <errno.h>
+int
+main ()
+{
+char *c = (char *) *sys_errlist
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_have_sys_errlist=yes
+else
+ ol_cv_have_sys_errlist=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_have_sys_errlist" >&5
+$as_echo "$ol_cv_have_sys_errlist" >&6; }
+if test $ol_cv_have_sys_errlist = yes ; then
+
+$as_echo "#define HAVE_SYS_ERRLIST 1" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking declaration of sys_errlist" >&5
+$as_echo_n "checking declaration of sys_errlist... " >&6; }
+if ${ol_cv_dcl_sys_errlist+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <errno.h>
+#ifdef _WIN32
+#include <stdlib.h>
+#endif
+int
+main ()
+{
+char *c = (char *) *sys_errlist
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ol_cv_dcl_sys_errlist=yes
+else
+ ol_cv_dcl_sys_errlist=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_dcl_sys_errlist" >&5
+$as_echo "$ol_cv_dcl_sys_errlist" >&6; }
+#
+ # It's possible (for near-UNIX clones) that sys_errlist doesn't exist
+ if test $ol_cv_dcl_sys_errlist = no ; then
+
+$as_echo "#define DECL_SYS_ERRLIST 1" >>confdefs.h
+
+ fi
+fi
+
+fi
+
+
+for ac_header in regex.h
+do :
+ ac_fn_c_check_header_compile "$LINENO" "regex.h" "ac_cv_header_regex_h" "$ac_includes_default
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+"
+if test "x$ac_cv_header_regex_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_REGEX_H 1
+_ACEOF
+
+fi
+
+done
+
+
+if test "$ac_cv_header_regex_h" != yes ; then
+ as_fn_error $? "POSIX regex.h required." "$LINENO" 5
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing regfree" >&5
+$as_echo_n "checking for library containing regfree... " >&6; }
+if ${ac_cv_search_regfree+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char regfree ();
+int
+main ()
+{
+return regfree ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' regex gnuregex; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_regfree=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_regfree+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_regfree+:} false; then :
+
+else
+ ac_cv_search_regfree=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_regfree" >&5
+$as_echo "$ac_cv_search_regfree" >&6; }
+ac_res=$ac_cv_search_regfree
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+ :
+else
+ as_fn_error $? "POSIX regex required." "$LINENO" 5
+fi
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compatible POSIX regex" >&5
+$as_echo_n "checking for compatible POSIX regex... " >&6; }
+if ${ol_cv_c_posix_regex+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ if test "$cross_compiling" = yes; then :
+ ol_cv_c_posix_regex=cross
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <sys/types.h>
+#include <regex.h>
+static char *pattern, *string;
+main()
+{
+ int rc;
+ regex_t re;
+
+ pattern = "^A";
+
+ if(regcomp(&re, pattern, 0)) {
+ return -1;
+ }
+
+ string = "ALL MATCH";
+
+ rc = regexec(&re, string, 0, (void*)0, 0);
+
+ regfree(&re);
+
+ return rc;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ol_cv_c_posix_regex=yes
+else
+ ol_cv_c_posix_regex=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_c_posix_regex" >&5
+$as_echo "$ol_cv_c_posix_regex" >&6; }
+
+if test "$ol_cv_c_posix_regex" = no ; then
+ as_fn_error $? "broken POSIX regex!" "$LINENO" 5
+fi
+
+
+have_uuid=no
+for ac_header in sys/uuid.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "sys/uuid.h" "ac_cv_header_sys_uuid_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_uuid_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_SYS_UUID_H 1
+_ACEOF
+
+fi
+
+done
+
+if test $ac_cv_header_sys_uuid_h = yes ; then
+ save_LIBS="$LIBS"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing uuid_to_str" >&5
+$as_echo_n "checking for library containing uuid_to_str... " >&6; }
+if ${ac_cv_search_uuid_to_str+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char uuid_to_str ();
+int
+main ()
+{
+return uuid_to_str ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' uuid; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_uuid_to_str=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_uuid_to_str+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_uuid_to_str+:} false; then :
+
+else
+ ac_cv_search_uuid_to_str=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_uuid_to_str" >&5
+$as_echo "$ac_cv_search_uuid_to_str" >&6; }
+ac_res=$ac_cv_search_uuid_to_str
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+ have_uuid=yes
+else
+ :
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing uuid_create" >&5
+$as_echo_n "checking for library containing uuid_create... " >&6; }
+if ${ac_cv_search_uuid_create+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char uuid_create ();
+int
+main ()
+{
+return uuid_create ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' uuid; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_uuid_create=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_uuid_create+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_uuid_create+:} false; then :
+
+else
+ ac_cv_search_uuid_create=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_uuid_create" >&5
+$as_echo "$ac_cv_search_uuid_create" >&6; }
+ac_res=$ac_cv_search_uuid_create
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+ :
+else
+ have_uuid=no
+fi
+
+ LIBS="$save_LIBS"
+
+ if test $have_uuid = yes ; then
+
+$as_echo "#define HAVE_UUID_TO_STR 1" >>confdefs.h
+
+
+ test "$ac_cv_search_uuid_to_str" = "none required" || \
+ LUTIL_LIBS="$LUTIL_LIBS $ac_cv_search_uuid_to_str"
+ fi
+fi
+
+if test $have_uuid = no ; then
+ for ac_header in uuid/uuid.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "uuid/uuid.h" "ac_cv_header_uuid_uuid_h" "$ac_includes_default"
+if test "x$ac_cv_header_uuid_uuid_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_UUID_UUID_H 1
+_ACEOF
+
+fi
+
+done
+
+ if test $ac_cv_header_uuid_uuid_h = yes ; then
+ save_LIBS="$LIBS"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing uuid_generate" >&5
+$as_echo_n "checking for library containing uuid_generate... " >&6; }
+if ${ac_cv_search_uuid_generate+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char uuid_generate ();
+int
+main ()
+{
+return uuid_generate ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' uuid; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_uuid_generate=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_uuid_generate+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_uuid_generate+:} false; then :
+
+else
+ ac_cv_search_uuid_generate=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_uuid_generate" >&5
+$as_echo "$ac_cv_search_uuid_generate" >&6; }
+ac_res=$ac_cv_search_uuid_generate
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+ have_uuid=yes
+else
+ :
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing uuid_unparse_lower" >&5
+$as_echo_n "checking for library containing uuid_unparse_lower... " >&6; }
+if ${ac_cv_search_uuid_unparse_lower+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char uuid_unparse_lower ();
+int
+main ()
+{
+return uuid_unparse_lower ();
+ ;
+ return 0;
+}
+_ACEOF
+for ac_lib in '' uuid; do
+ if test -z "$ac_lib"; then
+ ac_res="none required"
+ else
+ ac_res=-l$ac_lib
+ LIBS="-l$ac_lib $ac_func_search_save_LIBS"
+ fi
+ if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_search_uuid_unparse_lower=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext
+ if ${ac_cv_search_uuid_unparse_lower+:} false; then :
+ break
+fi
+done
+if ${ac_cv_search_uuid_unparse_lower+:} false; then :
+
+else
+ ac_cv_search_uuid_unparse_lower=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_uuid_unparse_lower" >&5
+$as_echo "$ac_cv_search_uuid_unparse_lower" >&6; }
+ac_res=$ac_cv_search_uuid_unparse_lower
+if test "$ac_res" != no; then :
+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+ :
+else
+ have_uuid=no
+fi
+
+ LIBS="$save_LIBS"
+
+ if test $have_uuid = yes ; then
+
+$as_echo "#define HAVE_UUID_GENERATE 1" >>confdefs.h
+
+
+ test "$ac_cv_search_uuid_generate" = "none required" || \
+ LUTIL_LIBS="$LUTIL_LIBS $ac_cv_search_uuid_generate"
+ fi
+ fi
+fi
+
+if test $have_uuid = no ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking to see if -lrpcrt4 is needed for win32 UUID support" >&5
+$as_echo_n "checking to see if -lrpcrt4 is needed for win32 UUID support... " >&6; }
+ save_LIBS="$LIBS"
+ LIBS="$LIBS -lrpcrt4"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+ int __stdcall UuidCreate(void *);
+ int __stdcall UuidToStringA(void *,void **);
+
+int
+main ()
+{
+
+ UuidCreate(0);
+ UuidToStringA(0,0);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ need_rpcrt=yes
+else
+ need_rpcrt=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ if test $need_rpcrt = yes; then
+ SLAPD_LIBS="$SLAPD_LIBS -lrpcrt4"
+ CLIENT_LIBS="$CLIENT_LIBS -lrpcrt4"
+ fi
+ LIBS="$save_LIBS"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $need_rpcrt" >&5
+$as_echo "$need_rpcrt" >&6; }
+fi
+
+ol_cv_lib_resolver=no
+if test $ol_cv_lib_resolver = no ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for resolver link (default)" >&5
+$as_echo_n "checking for resolver link (default)... " >&6; }
+if ${ol_cv_resolver_none+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ ol_RESOLVER_LIB=
+ ol_LIBS=$LIBS
+ LIBS="$ol_RESOLVER_LIB $LIBS"
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <netinet/in.h>
+#ifdef HAVE_ARPA_NAMESER_H
+# include <arpa/nameser.h>
+#endif
+#ifdef HAVE_RESOLV_H
+# include <resolv.h>
+#endif
+
+int
+main ()
+{
+{
+ int len, status;
+ char *request = NULL;
+ unsigned char reply[64*1024];
+ unsigned char host[64*1024];
+ unsigned char *p;
+
+#ifdef NS_HFIXEDSZ
+ /* Bind 8/9 interface */
+ len = res_query(request, ns_c_in, ns_t_srv, reply, sizeof(reply));
+#else
+ /* Bind 4 interface */
+# ifndef T_SRV
+# define T_SRV 33
+# endif
+ len = res_query(request, C_IN, T_SRV, reply, sizeof(reply));
+#endif
+ p = reply;
+#ifdef NS_HFIXEDSZ
+ /* Bind 8/9 interface */
+ p += NS_HFIXEDSZ;
+#elif defined(HFIXEDSZ)
+ /* Bind 4 interface w/ HFIXEDSZ */
+ p += HFIXEDSZ;
+#else
+ /* Bind 4 interface w/o HFIXEDSZ */
+ p += sizeof(HEADER);
+#endif
+ status = dn_expand( reply, reply+len, p, host, sizeof(host));
+}
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_resolver_none=yes
+else
+ ol_cv_resolver_none=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
+ LIBS="$ol_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_resolver_none" >&5
+$as_echo "$ol_cv_resolver_none" >&6; }
+
+ if test $ol_cv_resolver_none = yes ; then
+ ol_cv_lib_resolver=yes
+ fi
+fi
+
+if test $ol_cv_lib_resolver = no ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for resolver link (-lresolv)" >&5
+$as_echo_n "checking for resolver link (-lresolv)... " >&6; }
+if ${ol_cv_resolver_resolv+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ ol_RESOLVER_LIB=-lresolv
+ ol_LIBS=$LIBS
+ LIBS="$ol_RESOLVER_LIB $LIBS"
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <netinet/in.h>
+#ifdef HAVE_ARPA_NAMESER_H
+# include <arpa/nameser.h>
+#endif
+#ifdef HAVE_RESOLV_H
+# include <resolv.h>
+#endif
+
+int
+main ()
+{
+{
+ int len, status;
+ char *request = NULL;
+ unsigned char reply[64*1024];
+ unsigned char host[64*1024];
+ unsigned char *p;
+
+#ifdef NS_HFIXEDSZ
+ /* Bind 8/9 interface */
+ len = res_query(request, ns_c_in, ns_t_srv, reply, sizeof(reply));
+#else
+ /* Bind 4 interface */
+# ifndef T_SRV
+# define T_SRV 33
+# endif
+ len = res_query(request, C_IN, T_SRV, reply, sizeof(reply));
+#endif
+ p = reply;
+#ifdef NS_HFIXEDSZ
+ /* Bind 8/9 interface */
+ p += NS_HFIXEDSZ;
+#elif defined(HFIXEDSZ)
+ /* Bind 4 interface w/ HFIXEDSZ */
+ p += HFIXEDSZ;
+#else
+ /* Bind 4 interface w/o HFIXEDSZ */
+ p += sizeof(HEADER);
+#endif
+ status = dn_expand( reply, reply+len, p, host, sizeof(host));
+}
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_resolver_resolv=yes
+else
+ ol_cv_resolver_resolv=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
+ LIBS="$ol_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_resolver_resolv" >&5
+$as_echo "$ol_cv_resolver_resolv" >&6; }
+
+ if test $ol_cv_resolver_resolv = yes ; then
+ ol_cv_lib_resolver=-lresolv
+ fi
+fi
+
+if test $ol_cv_lib_resolver = no ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for resolver link (-lbind)" >&5
+$as_echo_n "checking for resolver link (-lbind)... " >&6; }
+if ${ol_cv_resolver_bind+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ ol_RESOLVER_LIB=-lbind
+ ol_LIBS=$LIBS
+ LIBS="$ol_RESOLVER_LIB $LIBS"
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <netinet/in.h>
+#ifdef HAVE_ARPA_NAMESER_H
+# include <arpa/nameser.h>
+#endif
+#ifdef HAVE_RESOLV_H
+# include <resolv.h>
+#endif
+
+int
+main ()
+{
+{
+ int len, status;
+ char *request = NULL;
+ unsigned char reply[64*1024];
+ unsigned char host[64*1024];
+ unsigned char *p;
+
+#ifdef NS_HFIXEDSZ
+ /* Bind 8/9 interface */
+ len = res_query(request, ns_c_in, ns_t_srv, reply, sizeof(reply));
+#else
+ /* Bind 4 interface */
+# ifndef T_SRV
+# define T_SRV 33
+# endif
+ len = res_query(request, C_IN, T_SRV, reply, sizeof(reply));
+#endif
+ p = reply;
+#ifdef NS_HFIXEDSZ
+ /* Bind 8/9 interface */
+ p += NS_HFIXEDSZ;
+#elif defined(HFIXEDSZ)
+ /* Bind 4 interface w/ HFIXEDSZ */
+ p += HFIXEDSZ;
+#else
+ /* Bind 4 interface w/o HFIXEDSZ */
+ p += sizeof(HEADER);
+#endif
+ status = dn_expand( reply, reply+len, p, host, sizeof(host));
+}
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_resolver_bind=yes
+else
+ ol_cv_resolver_bind=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
+ LIBS="$ol_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_resolver_bind" >&5
+$as_echo "$ol_cv_resolver_bind" >&6; }
+
+ if test $ol_cv_resolver_bind = yes ; then
+ ol_cv_lib_resolver=-lbind
+ fi
+fi
+
+
+
+ol_link_dnssrv=no
+if test "$ol_cv_lib_resolver" != no ; then
+
+$as_echo "#define HAVE_RES_QUERY 1" >>confdefs.h
+
+
+ if test "$ol_enable_dnssrv" != no ; then
+ ol_link_dnssrv=yes
+ fi
+
+ if test "$ol_cv_lib_resolver" != yes ; then
+ LIBS="$ol_cv_lib_resolver $LIBS"
+ fi
+fi
+
+if test "$ol_enable_dnssrv" = yes || test "$ol_enable_dnssrv" = mod ; then
+ if test "$ol_link_dnssrv" = no ; then
+ as_fn_error $? "DNSSRV requires res_query()" "$LINENO" 5
+ fi
+else
+ ol_enable_dnssrv=no
+fi
+
+for ac_func in hstrerror
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+for ac_func in getaddrinfo getnameinfo gai_strerror inet_ntop
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+ol_link_ipv6=no
+if test $ac_cv_func_getaddrinfo = no || test $ac_cv_func_inet_ntop = no ; then
+ if test $ol_enable_ipv6 = yes ; then
+ as_fn_error $? "IPv6 support requires getaddrinfo() and inet_ntop()" "$LINENO" 5
+ fi
+elif test $ol_enable_ipv6 != no ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking INET6_ADDRSTRLEN" >&5
+$as_echo_n "checking INET6_ADDRSTRLEN... " >&6; }
+if ${ol_cv_inet6_addrstrlen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+# include <netinet/in.h>
+# ifdef INET6_ADDRSTRLEN
+ __has_inet6_addrstrlen__;
+# endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "__has_inet6_addrstrlen__" >/dev/null 2>&1; then :
+ ol_cv_inet6_addrstrlen=yes
+else
+ ol_cv_inet6_addrstrlen=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_inet6_addrstrlen" >&5
+$as_echo "$ol_cv_inet6_addrstrlen" >&6; }
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking struct sockaddr_storage" >&5
+$as_echo_n "checking struct sockaddr_storage... " >&6; }
+if ${ol_cv_struct_sockaddr_storage+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+int
+main ()
+{
+
+ struct sockaddr_storage ss;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ol_cv_struct_sockaddr_storage=yes
+else
+ ol_cv_struct_sockaddr_storage=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_struct_sockaddr_storage" >&5
+$as_echo "$ol_cv_struct_sockaddr_storage" >&6; }
+
+ if test $ol_cv_inet6_addrstrlen = yes &&
+ test $ol_cv_struct_sockaddr_storage = yes ; then
+ ol_link_ipv6=yes
+ elif test $ol_enable_ipv6 = yes &&
+ test $ol_cv_inet6_addrstrlen = no ; then
+ as_fn_error $? "IPv6 support requires INET6_ADDRSTRLEN" "$LINENO" 5
+ elif test $ol_enable_ipv6 = yes &&
+ test $ol_cv_struct_sockaddr_storage = no ; then
+ as_fn_error $? "IPv6 support requires struct sockaddr_storage" "$LINENO" 5
+ fi
+fi
+
+if test $ol_enable_local != no ; then
+ for ac_header in sys/un.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+ if test $ol_enable_local = auto ; then
+ ol_enable_local=$ac_cv_header_sys_un_h
+ elif test $ac_cv_header_sys_un_h = no ; then
+ as_fn_error $? "AF_LOCAL domain support requires sys/un.h" "$LINENO" 5
+ fi
+fi
+
+
+if test $ol_with_tls = yes ; then
+ ol_with_tls=auto
+fi
+
+ol_link_tls=no
+if test $ol_with_tls = openssl || test $ol_with_tls = auto ; then
+ for ac_header in openssl/ssl.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "openssl/ssl.h" "ac_cv_header_openssl_ssl_h" "$ac_includes_default"
+if test "x$ac_cv_header_openssl_ssl_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_OPENSSL_SSL_H 1
+_ACEOF
+
+fi
+
+done
+
+
+ if test $ac_cv_header_openssl_ssl_h = yes ; then
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <openssl/opensslv.h>
+#if OPENSSL_VERSION_NUMBER < 0x1010100fL
+#error "OpenSSL is too old"
+#endif
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "OpenSSL 1.1.1 or newer required
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_export_keying_material_early in -lssl" >&5
+$as_echo_n "checking for SSL_export_keying_material_early in -lssl... " >&6; }
+if ${ac_cv_lib_ssl_SSL_export_keying_material_early+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lssl -lcrypto $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char SSL_export_keying_material_early ();
+int
+main ()
+{
+return SSL_export_keying_material_early ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_ssl_SSL_export_keying_material_early=yes
+else
+ ac_cv_lib_ssl_SSL_export_keying_material_early=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_SSL_export_keying_material_early" >&5
+$as_echo "$ac_cv_lib_ssl_SSL_export_keying_material_early" >&6; }
+if test "x$ac_cv_lib_ssl_SSL_export_keying_material_early" = xyes; then :
+ have_openssl=yes
+else
+ have_openssl=no
+fi
+
+
+ if test $have_openssl = yes ; then
+ ol_with_tls=openssl
+ ol_link_tls=yes
+ WITH_TLS_TYPE=openssl
+
+
+$as_echo "#define HAVE_OPENSSL 1" >>confdefs.h
+
+
+ TLS_LIBS="-lssl -lcrypto"
+ fi
+ fi
+fi
+
+if test $ol_link_tls = no ; then
+ if test $ol_with_tls = gnutls || test $ol_with_tls = auto ; then
+ for ac_header in gnutls/gnutls.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "gnutls/gnutls.h" "ac_cv_header_gnutls_gnutls_h" "$ac_includes_default"
+if test "x$ac_cv_header_gnutls_gnutls_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_GNUTLS_GNUTLS_H 1
+_ACEOF
+
+fi
+
+done
+
+
+ if test $ac_cv_header_gnutls_gnutls_h = yes ; then
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <gnutls/gnutls.h>
+#if GNUTLS_VERSION_NUMBER < 0x030306
+#error "GnuTLS is too old"
+#endif
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "GnuTLS 3.3.6 or newer required
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gnutls_init in -lgnutls" >&5
+$as_echo_n "checking for gnutls_init in -lgnutls... " >&6; }
+if ${ac_cv_lib_gnutls_gnutls_init+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lgnutls $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char gnutls_init ();
+int
+main ()
+{
+return gnutls_init ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_gnutls_gnutls_init=yes
+else
+ ac_cv_lib_gnutls_gnutls_init=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gnutls_gnutls_init" >&5
+$as_echo "$ac_cv_lib_gnutls_gnutls_init" >&6; }
+if test "x$ac_cv_lib_gnutls_gnutls_init" = xyes; then :
+ have_gnutls=yes
+else
+ have_gnutls=no
+fi
+
+
+ if test $have_gnutls = yes ; then
+ ol_with_tls=gnutls
+ ol_link_tls=yes
+ WITH_TLS_TYPE=gnutls
+
+ TLS_LIBS="-lgnutls"
+
+
+$as_echo "#define HAVE_GNUTLS 1" >>confdefs.h
+
+ fi
+ fi
+ fi
+fi
+
+WITH_TLS=no
+if test $ol_link_tls = yes ; then
+
+$as_echo "#define HAVE_TLS 1" >>confdefs.h
+
+ WITH_TLS=yes
+elif test $ol_with_tls = auto ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Could not locate TLS/SSL package" >&5
+$as_echo "$as_me: WARNING: Could not locate TLS/SSL package" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: TLS data protection not supported!" >&5
+$as_echo "$as_me: WARNING: TLS data protection not supported!" >&2;}
+elif test $ol_with_tls != no ; then
+ as_fn_error $? "Could not locate TLS/SSL package" "$LINENO" 5
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: TLS data protection not supported!" >&5
+$as_echo "$as_me: WARNING: TLS data protection not supported!" >&2;}
+fi
+
+
+ol_link_threads=no
+
+case $ol_with_threads in auto | yes | nt)
+
+
+ ac_fn_c_check_func "$LINENO" "_beginthread" "ac_cv_func__beginthread"
+if test "x$ac_cv_func__beginthread" = xyes; then :
+
+fi
+
+
+ if test $ac_cv_func__beginthread = yes ; then
+
+$as_echo "#define HAVE_NT_THREADS 1" >>confdefs.h
+
+ ol_cv_nt_threads=yes
+ fi
+
+
+ if test "$ol_cv_nt_threads" = yes ; then
+ ol_link_threads=nt
+ ol_with_threads=found
+ ol_with_yielding_select=yes
+
+
+$as_echo "#define HAVE_NT_SERVICE_MANAGER 1" >>confdefs.h
+
+
+$as_echo "#define HAVE_NT_EVENT_LOG 1" >>confdefs.h
+
+ fi
+
+ if test $ol_with_threads = nt ; then
+ as_fn_error $? "could not locate NT Threads" "$LINENO" 5
+ fi
+ ;;
+esac
+
+case $ol_with_threads in auto | yes | posix)
+
+ for ac_header in pthread.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "pthread.h" "ac_cv_header_pthread_h" "$ac_includes_default"
+if test "x$ac_cv_header_pthread_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_PTHREAD_H 1
+_ACEOF
+
+fi
+
+done
+
+
+ if test $ac_cv_header_pthread_h = yes ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking POSIX thread version" >&5
+$as_echo_n "checking POSIX thread version... " >&6; }
+if ${ol_cv_pthread_version+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+# include <pthread.h>
+
+int
+main ()
+{
+
+ int i = PTHREAD_CREATE_JOINABLE;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <pthread.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "pthread_detach" >/dev/null 2>&1; then :
+ ol_cv_pthread_version=10
+else
+ ol_cv_pthread_version=8
+fi
+rm -f conftest*
+
+else
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+# include <pthread.h>
+# ifdef PTHREAD_CREATE_UNDETACHED
+ draft7
+# endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "draft7" >/dev/null 2>&1; then :
+ ol_cv_pthread_version=7
+else
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <pthread.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "pthread_attr_init" >/dev/null 2>&1; then :
+ ol_cv_pthread_version=6
+else
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+# include <pthread.h>
+#ifdef PTHREAD_MUTEX_INITIALIZER
+ draft5
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "draft5" >/dev/null 2>&1; then :
+ ol_cv_pthread_version=5
+else
+ ol_cv_pthread_version=4
+fi
+rm -f conftest*
+
+fi
+rm -f conftest*
+
+fi
+rm -f conftest*
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_pthread_version" >&5
+$as_echo "$ol_cv_pthread_version" >&6; }
+
+
+ if test $ol_cv_pthread_version != 0 ; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_PTHREADS $ol_cv_pthread_version
+_ACEOF
+
+ else
+ as_fn_error $? "unknown pthread version" "$LINENO" 5
+ fi
+
+ # consider threads found
+ ol_with_threads=found
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LinuxThreads pthread.h" >&5
+$as_echo_n "checking for LinuxThreads pthread.h... " >&6; }
+if ${ol_cv_header_linux_threads+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <pthread.h>
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "pthread_kill_other_threads_np" >/dev/null 2>&1; then :
+ ol_cv_header_linux_threads=yes
+else
+ ol_cv_header_linux_threads=no
+fi
+rm -f conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_header_linux_threads" >&5
+$as_echo "$ol_cv_header_linux_threads" >&6; }
+ if test $ol_cv_header_linux_threads = yes; then
+
+$as_echo "#define HAVE_LINUX_THREADS 1" >>confdefs.h
+
+ fi
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU Pth pthread.h" >&5
+$as_echo_n "checking for GNU Pth pthread.h... " >&6; }
+if ${ol_cv_header_gnu_pth_pthread_h+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <pthread.h>
+#ifdef _POSIX_THREAD_IS_GNU_PTH
+ __gnu_pth__;
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "__gnu_pth__" >/dev/null 2>&1; then :
+ ol_cv_header_gnu_pth_pthread_h=yes
+else
+ ol_cv_header_gnu_pth_pthread_h=no
+fi
+rm -f conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_header_gnu_pth_pthread_h" >&5
+$as_echo "$ol_cv_header_gnu_pth_pthread_h" >&6; }
+
+
+ if test $ol_cv_header_gnu_pth_pthread_h = no ; then
+ for ac_header in sched.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "sched.h" "ac_cv_header_sched_h" "$ac_includes_default"
+if test "x$ac_cv_header_sched_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_SCHED_H 1
+_ACEOF
+
+fi
+
+done
+
+ fi
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in default libraries" >&5
+$as_echo_n "checking for pthread_create in default libraries... " >&6; }
+if ${ol_cv_pthread_create+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ if test "$cross_compiling" = yes; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+int
+main ()
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_pthread_create=yes
+else
+ ol_cv_pthread_create=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ol_cv_pthread_create=yes
+else
+ ol_cv_pthread_create=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_pthread_create" >&5
+$as_echo "$ol_cv_pthread_create" >&6; }
+
+ if test $ol_cv_pthread_create != no ; then
+ ol_link_threads=posix
+ ol_link_pthreads=""
+ fi
+
+ # Pthread try link: -kthread (ol_cv_pthread_kthread)
+if test "$ol_link_threads" = no ; then
+ # try -kthread
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread link with -kthread" >&5
+$as_echo_n "checking for pthread link with -kthread... " >&6; }
+if ${ol_cv_pthread_kthread+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ # save the flags
+ ol_LIBS="$LIBS"
+ LIBS="-kthread $LIBS"
+
+ if test "$cross_compiling" = yes; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+int
+main ()
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_pthread_kthread=yes
+else
+ ol_cv_pthread_kthread=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ol_cv_pthread_kthread=yes
+else
+ ol_cv_pthread_kthread=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+ # restore the LIBS
+ LIBS="$ol_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_pthread_kthread" >&5
+$as_echo "$ol_cv_pthread_kthread" >&6; }
+
+ if test $ol_cv_pthread_kthread = yes ; then
+ ol_link_pthreads="-kthread"
+ ol_link_threads=posix
+ fi
+fi
+
+ # Pthread try link: -pthread (ol_cv_pthread_pthread)
+if test "$ol_link_threads" = no ; then
+ # try -pthread
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread link with -pthread" >&5
+$as_echo_n "checking for pthread link with -pthread... " >&6; }
+if ${ol_cv_pthread_pthread+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ # save the flags
+ ol_LIBS="$LIBS"
+ LIBS="-pthread $LIBS"
+
+ if test "$cross_compiling" = yes; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+int
+main ()
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_pthread_pthread=yes
+else
+ ol_cv_pthread_pthread=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ol_cv_pthread_pthread=yes
+else
+ ol_cv_pthread_pthread=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+ # restore the LIBS
+ LIBS="$ol_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_pthread_pthread" >&5
+$as_echo "$ol_cv_pthread_pthread" >&6; }
+
+ if test $ol_cv_pthread_pthread = yes ; then
+ ol_link_pthreads="-pthread"
+ ol_link_threads=posix
+ fi
+fi
+
+ # Pthread try link: -pthreads (ol_cv_pthread_pthreads)
+if test "$ol_link_threads" = no ; then
+ # try -pthreads
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread link with -pthreads" >&5
+$as_echo_n "checking for pthread link with -pthreads... " >&6; }
+if ${ol_cv_pthread_pthreads+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ # save the flags
+ ol_LIBS="$LIBS"
+ LIBS="-pthreads $LIBS"
+
+ if test "$cross_compiling" = yes; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+int
+main ()
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_pthread_pthreads=yes
+else
+ ol_cv_pthread_pthreads=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ol_cv_pthread_pthreads=yes
+else
+ ol_cv_pthread_pthreads=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+ # restore the LIBS
+ LIBS="$ol_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_pthread_pthreads" >&5
+$as_echo "$ol_cv_pthread_pthreads" >&6; }
+
+ if test $ol_cv_pthread_pthreads = yes ; then
+ ol_link_pthreads="-pthreads"
+ ol_link_threads=posix
+ fi
+fi
+
+ # Pthread try link: -mthreads (ol_cv_pthread_mthreads)
+if test "$ol_link_threads" = no ; then
+ # try -mthreads
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread link with -mthreads" >&5
+$as_echo_n "checking for pthread link with -mthreads... " >&6; }
+if ${ol_cv_pthread_mthreads+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ # save the flags
+ ol_LIBS="$LIBS"
+ LIBS="-mthreads $LIBS"
+
+ if test "$cross_compiling" = yes; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+int
+main ()
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_pthread_mthreads=yes
+else
+ ol_cv_pthread_mthreads=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ol_cv_pthread_mthreads=yes
+else
+ ol_cv_pthread_mthreads=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+ # restore the LIBS
+ LIBS="$ol_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_pthread_mthreads" >&5
+$as_echo "$ol_cv_pthread_mthreads" >&6; }
+
+ if test $ol_cv_pthread_mthreads = yes ; then
+ ol_link_pthreads="-mthreads"
+ ol_link_threads=posix
+ fi
+fi
+
+ # Pthread try link: -thread (ol_cv_pthread_thread)
+if test "$ol_link_threads" = no ; then
+ # try -thread
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread link with -thread" >&5
+$as_echo_n "checking for pthread link with -thread... " >&6; }
+if ${ol_cv_pthread_thread+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ # save the flags
+ ol_LIBS="$LIBS"
+ LIBS="-thread $LIBS"
+
+ if test "$cross_compiling" = yes; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+int
+main ()
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_pthread_thread=yes
+else
+ ol_cv_pthread_thread=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ol_cv_pthread_thread=yes
+else
+ ol_cv_pthread_thread=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+ # restore the LIBS
+ LIBS="$ol_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_pthread_thread" >&5
+$as_echo "$ol_cv_pthread_thread" >&6; }
+
+ if test $ol_cv_pthread_thread = yes ; then
+ ol_link_pthreads="-thread"
+ ol_link_threads=posix
+ fi
+fi
+
+
+ # Pthread try link: -lpthread -lmach -lexc -lc_r (ol_cv_pthread_lpthread_lmach_lexc_lc_r)
+if test "$ol_link_threads" = no ; then
+ # try -lpthread -lmach -lexc -lc_r
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread link with -lpthread -lmach -lexc -lc_r" >&5
+$as_echo_n "checking for pthread link with -lpthread -lmach -lexc -lc_r... " >&6; }
+if ${ol_cv_pthread_lpthread_lmach_lexc_lc_r+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ # save the flags
+ ol_LIBS="$LIBS"
+ LIBS="-lpthread -lmach -lexc -lc_r $LIBS"
+
+ if test "$cross_compiling" = yes; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+int
+main ()
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_pthread_lpthread_lmach_lexc_lc_r=yes
+else
+ ol_cv_pthread_lpthread_lmach_lexc_lc_r=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ol_cv_pthread_lpthread_lmach_lexc_lc_r=yes
+else
+ ol_cv_pthread_lpthread_lmach_lexc_lc_r=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+ # restore the LIBS
+ LIBS="$ol_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_pthread_lpthread_lmach_lexc_lc_r" >&5
+$as_echo "$ol_cv_pthread_lpthread_lmach_lexc_lc_r" >&6; }
+
+ if test $ol_cv_pthread_lpthread_lmach_lexc_lc_r = yes ; then
+ ol_link_pthreads="-lpthread -lmach -lexc -lc_r"
+ ol_link_threads=posix
+ fi
+fi
+
+ # Pthread try link: -lpthread -lmach -lexc (ol_cv_pthread_lpthread_lmach_lexc)
+if test "$ol_link_threads" = no ; then
+ # try -lpthread -lmach -lexc
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread link with -lpthread -lmach -lexc" >&5
+$as_echo_n "checking for pthread link with -lpthread -lmach -lexc... " >&6; }
+if ${ol_cv_pthread_lpthread_lmach_lexc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ # save the flags
+ ol_LIBS="$LIBS"
+ LIBS="-lpthread -lmach -lexc $LIBS"
+
+ if test "$cross_compiling" = yes; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+int
+main ()
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_pthread_lpthread_lmach_lexc=yes
+else
+ ol_cv_pthread_lpthread_lmach_lexc=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ol_cv_pthread_lpthread_lmach_lexc=yes
+else
+ ol_cv_pthread_lpthread_lmach_lexc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+ # restore the LIBS
+ LIBS="$ol_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_pthread_lpthread_lmach_lexc" >&5
+$as_echo "$ol_cv_pthread_lpthread_lmach_lexc" >&6; }
+
+ if test $ol_cv_pthread_lpthread_lmach_lexc = yes ; then
+ ol_link_pthreads="-lpthread -lmach -lexc"
+ ol_link_threads=posix
+ fi
+fi
+
+
+ # Pthread try link: -lpthread -Wl,-woff,85 (ol_cv_pthread_lib_lpthread_woff)
+if test "$ol_link_threads" = no ; then
+ # try -lpthread -Wl,-woff,85
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread link with -lpthread -Wl,-woff,85" >&5
+$as_echo_n "checking for pthread link with -lpthread -Wl,-woff,85... " >&6; }
+if ${ol_cv_pthread_lib_lpthread_woff+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ # save the flags
+ ol_LIBS="$LIBS"
+ LIBS="-lpthread -Wl,-woff,85 $LIBS"
+
+ if test "$cross_compiling" = yes; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+int
+main ()
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_pthread_lib_lpthread_woff=yes
+else
+ ol_cv_pthread_lib_lpthread_woff=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ol_cv_pthread_lib_lpthread_woff=yes
+else
+ ol_cv_pthread_lib_lpthread_woff=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+ # restore the LIBS
+ LIBS="$ol_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_pthread_lib_lpthread_woff" >&5
+$as_echo "$ol_cv_pthread_lib_lpthread_woff" >&6; }
+
+ if test $ol_cv_pthread_lib_lpthread_woff = yes ; then
+ ol_link_pthreads="-lpthread -Wl,-woff,85"
+ ol_link_threads=posix
+ fi
+fi
+
+
+ # Pthread try link: -lpthread (ol_cv_pthread_lpthread)
+if test "$ol_link_threads" = no ; then
+ # try -lpthread
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread link with -lpthread" >&5
+$as_echo_n "checking for pthread link with -lpthread... " >&6; }
+if ${ol_cv_pthread_lpthread+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ # save the flags
+ ol_LIBS="$LIBS"
+ LIBS="-lpthread $LIBS"
+
+ if test "$cross_compiling" = yes; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+int
+main ()
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_pthread_lpthread=yes
+else
+ ol_cv_pthread_lpthread=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ol_cv_pthread_lpthread=yes
+else
+ ol_cv_pthread_lpthread=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+ # restore the LIBS
+ LIBS="$ol_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_pthread_lpthread" >&5
+$as_echo "$ol_cv_pthread_lpthread" >&6; }
+
+ if test $ol_cv_pthread_lpthread = yes ; then
+ ol_link_pthreads="-lpthread"
+ ol_link_threads=posix
+ fi
+fi
+
+ # Pthread try link: -lc_r (ol_cv_pthread_lc_r)
+if test "$ol_link_threads" = no ; then
+ # try -lc_r
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread link with -lc_r" >&5
+$as_echo_n "checking for pthread link with -lc_r... " >&6; }
+if ${ol_cv_pthread_lc_r+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ # save the flags
+ ol_LIBS="$LIBS"
+ LIBS="-lc_r $LIBS"
+
+ if test "$cross_compiling" = yes; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+int
+main ()
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_pthread_lc_r=yes
+else
+ ol_cv_pthread_lc_r=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ol_cv_pthread_lc_r=yes
+else
+ ol_cv_pthread_lc_r=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+ # restore the LIBS
+ LIBS="$ol_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_pthread_lc_r" >&5
+$as_echo "$ol_cv_pthread_lc_r" >&6; }
+
+ if test $ol_cv_pthread_lc_r = yes ; then
+ ol_link_pthreads="-lc_r"
+ ol_link_threads=posix
+ fi
+fi
+
+
+ # Pthread try link: -threads (ol_cv_pthread_threads)
+if test "$ol_link_threads" = no ; then
+ # try -threads
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread link with -threads" >&5
+$as_echo_n "checking for pthread link with -threads... " >&6; }
+if ${ol_cv_pthread_threads+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ # save the flags
+ ol_LIBS="$LIBS"
+ LIBS="-threads $LIBS"
+
+ if test "$cross_compiling" = yes; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+int
+main ()
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_pthread_threads=yes
+else
+ ol_cv_pthread_threads=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ol_cv_pthread_threads=yes
+else
+ ol_cv_pthread_threads=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+ # restore the LIBS
+ LIBS="$ol_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_pthread_threads" >&5
+$as_echo "$ol_cv_pthread_threads" >&6; }
+
+ if test $ol_cv_pthread_threads = yes ; then
+ ol_link_pthreads="-threads"
+ ol_link_threads=posix
+ fi
+fi
+
+
+ # Pthread try link: -lpthreads -lmach -lexc -lc_r (ol_cv_pthread_lpthreads_lmach_lexc_lc_r)
+if test "$ol_link_threads" = no ; then
+ # try -lpthreads -lmach -lexc -lc_r
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread link with -lpthreads -lmach -lexc -lc_r" >&5
+$as_echo_n "checking for pthread link with -lpthreads -lmach -lexc -lc_r... " >&6; }
+if ${ol_cv_pthread_lpthreads_lmach_lexc_lc_r+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ # save the flags
+ ol_LIBS="$LIBS"
+ LIBS="-lpthreads -lmach -lexc -lc_r $LIBS"
+
+ if test "$cross_compiling" = yes; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+int
+main ()
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_pthread_lpthreads_lmach_lexc_lc_r=yes
+else
+ ol_cv_pthread_lpthreads_lmach_lexc_lc_r=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ol_cv_pthread_lpthreads_lmach_lexc_lc_r=yes
+else
+ ol_cv_pthread_lpthreads_lmach_lexc_lc_r=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+ # restore the LIBS
+ LIBS="$ol_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_pthread_lpthreads_lmach_lexc_lc_r" >&5
+$as_echo "$ol_cv_pthread_lpthreads_lmach_lexc_lc_r" >&6; }
+
+ if test $ol_cv_pthread_lpthreads_lmach_lexc_lc_r = yes ; then
+ ol_link_pthreads="-lpthreads -lmach -lexc -lc_r"
+ ol_link_threads=posix
+ fi
+fi
+
+ # Pthread try link: -lpthreads -lmach -lexc (ol_cv_pthread_lpthreads_lmach_lexc)
+if test "$ol_link_threads" = no ; then
+ # try -lpthreads -lmach -lexc
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread link with -lpthreads -lmach -lexc" >&5
+$as_echo_n "checking for pthread link with -lpthreads -lmach -lexc... " >&6; }
+if ${ol_cv_pthread_lpthreads_lmach_lexc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ # save the flags
+ ol_LIBS="$LIBS"
+ LIBS="-lpthreads -lmach -lexc $LIBS"
+
+ if test "$cross_compiling" = yes; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+int
+main ()
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_pthread_lpthreads_lmach_lexc=yes
+else
+ ol_cv_pthread_lpthreads_lmach_lexc=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ol_cv_pthread_lpthreads_lmach_lexc=yes
+else
+ ol_cv_pthread_lpthreads_lmach_lexc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+ # restore the LIBS
+ LIBS="$ol_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_pthread_lpthreads_lmach_lexc" >&5
+$as_echo "$ol_cv_pthread_lpthreads_lmach_lexc" >&6; }
+
+ if test $ol_cv_pthread_lpthreads_lmach_lexc = yes ; then
+ ol_link_pthreads="-lpthreads -lmach -lexc"
+ ol_link_threads=posix
+ fi
+fi
+
+ # Pthread try link: -lpthreads -lexc (ol_cv_pthread_lpthreads_lexc)
+if test "$ol_link_threads" = no ; then
+ # try -lpthreads -lexc
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread link with -lpthreads -lexc" >&5
+$as_echo_n "checking for pthread link with -lpthreads -lexc... " >&6; }
+if ${ol_cv_pthread_lpthreads_lexc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ # save the flags
+ ol_LIBS="$LIBS"
+ LIBS="-lpthreads -lexc $LIBS"
+
+ if test "$cross_compiling" = yes; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+int
+main ()
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_pthread_lpthreads_lexc=yes
+else
+ ol_cv_pthread_lpthreads_lexc=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ol_cv_pthread_lpthreads_lexc=yes
+else
+ ol_cv_pthread_lpthreads_lexc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+ # restore the LIBS
+ LIBS="$ol_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_pthread_lpthreads_lexc" >&5
+$as_echo "$ol_cv_pthread_lpthreads_lexc" >&6; }
+
+ if test $ol_cv_pthread_lpthreads_lexc = yes ; then
+ ol_link_pthreads="-lpthreads -lexc"
+ ol_link_threads=posix
+ fi
+fi
+
+
+ # Pthread try link: -lpthreads (ol_cv_pthread_lib_lpthreads)
+if test "$ol_link_threads" = no ; then
+ # try -lpthreads
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread link with -lpthreads" >&5
+$as_echo_n "checking for pthread link with -lpthreads... " >&6; }
+if ${ol_cv_pthread_lib_lpthreads+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ # save the flags
+ ol_LIBS="$LIBS"
+ LIBS="-lpthreads $LIBS"
+
+ if test "$cross_compiling" = yes; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+int
+main ()
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_pthread_lib_lpthreads=yes
+else
+ ol_cv_pthread_lib_lpthreads=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ol_cv_pthread_lib_lpthreads=yes
+else
+ ol_cv_pthread_lib_lpthreads=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+
+ # restore the LIBS
+ LIBS="$ol_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_pthread_lib_lpthreads" >&5
+$as_echo "$ol_cv_pthread_lib_lpthreads" >&6; }
+
+ if test $ol_cv_pthread_lib_lpthreads = yes ; then
+ ol_link_pthreads="-lpthreads"
+ ol_link_threads=posix
+ fi
+fi
+
+
+ if test $ol_link_threads != no ; then
+ LTHREAD_LIBS="$LTHREAD_LIBS $ol_link_pthreads"
+
+ save_CPPFLAGS="$CPPFLAGS"
+ save_LIBS="$LIBS"
+ LIBS="$LTHREAD_LIBS $LIBS"
+
+ for ac_func in sched_yield pthread_yield thr_yield
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+ if test $ac_cv_func_sched_yield = no &&
+ test $ac_cv_func_pthread_yield = no &&
+ test $ac_cv_func_thr_yield = no ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sched_yield in -lrt" >&5
+$as_echo_n "checking for sched_yield in -lrt... " >&6; }
+if ${ac_cv_lib_rt_sched_yield+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lrt $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char sched_yield ();
+int
+main ()
+{
+return sched_yield ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_rt_sched_yield=yes
+else
+ ac_cv_lib_rt_sched_yield=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_sched_yield" >&5
+$as_echo "$ac_cv_lib_rt_sched_yield" >&6; }
+if test "x$ac_cv_lib_rt_sched_yield" = xyes; then :
+ LTHREAD_LIBS="$LTHREAD_LIBS -lrt"
+
+$as_echo "#define HAVE_SCHED_YIELD 1" >>confdefs.h
+
+ ac_cv_func_sched_yield=yes
+else
+ ac_cv_func_sched_yield=no
+fi
+
+ fi
+ if test $ac_cv_func_sched_yield = no &&
+ test $ac_cv_func_pthread_yield = no &&
+ test "$ac_cv_func_thr_yield" = no ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: could not locate sched_yield() or pthread_yield()" >&5
+$as_echo "$as_me: WARNING: could not locate sched_yield() or pthread_yield()" >&2;}
+ fi
+
+ for ac_func in pthread_kill
+do :
+ ac_fn_c_check_func "$LINENO" "pthread_kill" "ac_cv_func_pthread_kill"
+if test "x$ac_cv_func_pthread_kill" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_PTHREAD_KILL 1
+_ACEOF
+
+fi
+done
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_rwlock_destroy with <pthread.h>" >&5
+$as_echo_n "checking for pthread_rwlock_destroy with <pthread.h>... " >&6; }
+if ${ol_cv_func_pthread_rwlock_destroy+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <pthread.h>
+pthread_rwlock_t rwlock;
+
+int
+main ()
+{
+pthread_rwlock_destroy(&rwlock);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_func_pthread_rwlock_destroy=yes
+else
+ ol_cv_func_pthread_rwlock_destroy=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_func_pthread_rwlock_destroy" >&5
+$as_echo "$ol_cv_func_pthread_rwlock_destroy" >&6; }
+ if test $ol_cv_func_pthread_rwlock_destroy = yes ; then
+
+$as_echo "#define HAVE_PTHREAD_RWLOCK_DESTROY 1" >>confdefs.h
+
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_detach with <pthread.h>" >&5
+$as_echo_n "checking for pthread_detach with <pthread.h>... " >&6; }
+if ${ol_cv_func_pthread_detach+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <pthread.h>
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+int
+main ()
+{
+pthread_detach(NULL);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_func_pthread_detach=yes
+else
+ ol_cv_func_pthread_detach=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_func_pthread_detach" >&5
+$as_echo "$ol_cv_func_pthread_detach" >&6; }
+
+ if test $ol_cv_func_pthread_detach = no ; then
+ as_fn_error $? "could not locate pthread_detach()" "$LINENO" 5
+ fi
+
+
+$as_echo "#define HAVE_PTHREAD_DETACH 1" >>confdefs.h
+
+
+ for ac_func in \
+ pthread_setconcurrency \
+ pthread_getconcurrency \
+ thr_setconcurrency \
+ thr_getconcurrency \
+
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+
+ for ac_func in pthread_kill_other_threads_np
+do :
+ ac_fn_c_check_func "$LINENO" "pthread_kill_other_threads_np" "ac_cv_func_pthread_kill_other_threads_np"
+if test "x$ac_cv_func_pthread_kill_other_threads_np" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_PTHREAD_KILL_OTHER_THREADS_NP 1
+_ACEOF
+
+fi
+done
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LinuxThreads implementation" >&5
+$as_echo_n "checking for LinuxThreads implementation... " >&6; }
+if ${ol_cv_sys_linux_threads+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ol_cv_sys_linux_threads=$ac_cv_func_pthread_kill_other_threads_np
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_sys_linux_threads" >&5
+$as_echo "$ol_cv_sys_linux_threads" >&6; }
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LinuxThreads consistency" >&5
+$as_echo_n "checking for LinuxThreads consistency... " >&6; }
+if ${ol_cv_linux_threads+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ if test $ol_cv_header_linux_threads = yes &&
+ test $ol_cv_sys_linux_threads = yes; then
+ ol_cv_linux_threads=yes
+ elif test $ol_cv_header_linux_threads = no &&
+ test $ol_cv_sys_linux_threads = no; then
+ ol_cv_linux_threads=no
+ else
+ ol_cv_linux_threads=error
+ fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_linux_threads" >&5
+$as_echo "$ol_cv_linux_threads" >&6; }
+
+
+ if test $ol_cv_linux_threads = error; then
+ as_fn_error $? "LinuxThreads header/library mismatch" "$LINENO" 5;
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if pthread_create() works" >&5
+$as_echo_n "checking if pthread_create() works... " >&6; }
+if ${ol_cv_pthread_create_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ if test "$cross_compiling" = yes; then :
+ ol_cv_pthread_create_works=yes
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* pthread test headers */
+#include <pthread.h>
+#if HAVE_PTHREADS < 7
+#include <errno.h>
+#endif
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void *task(p)
+ void *p;
+{
+ return (void *) (p == NULL);
+}
+
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ /* pthread test function */
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+ pthread_t t;
+ int status;
+ int detach = PTHREAD_CREATE_DETACHED;
+
+#if HAVE_PTHREADS > 4
+ /* Final pthreads */
+ pthread_attr_t attr;
+
+ status = pthread_attr_init(&attr);
+ if( status ) return status;
+
+#if HAVE_PTHREADS < 7
+ status = pthread_attr_setdetachstate(&attr, &detach);
+ if( status < 0 ) status = errno;
+#else
+ status = pthread_attr_setdetachstate(&attr, detach);
+#endif
+ if( status ) return status;
+ status = pthread_create( &t, &attr, task, NULL );
+#if HAVE_PTHREADS < 7
+ if( status < 0 ) status = errno;
+#endif
+ if( status ) return status;
+#else
+ /* Draft 4 pthreads */
+ status = pthread_create( &t, pthread_attr_default, task, NULL );
+ if( status ) return errno;
+
+ /* give thread a chance to complete */
+ /* it should remain joinable and hence detachable */
+ sleep( 1 );
+
+ status = pthread_detach( &t );
+ if( status ) return errno;
+#endif
+
+#ifdef HAVE_LINUX_THREADS
+ pthread_kill_other_threads_np();
+#endif
+
+ return 0;
+
+}
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ol_cv_pthread_create_works=yes
+else
+ ol_cv_pthread_create_works=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_pthread_create_works" >&5
+$as_echo "$ol_cv_pthread_create_works" >&6; }
+
+ if test $ol_cv_pthread_create_works = no ; then
+ as_fn_error $? "pthread_create is not usable, check environment settings" "$LINENO" 5
+ fi
+
+ ol_replace_broken_yield=no
+
+ if test $ol_replace_broken_yield = yes ; then
+
+$as_echo "#define REPLACE_BROKEN_YIELD 1" >>confdefs.h
+
+ fi
+
+ if test $ol_with_yielding_select = auto ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if select yields when using pthreads" >&5
+$as_echo_n "checking if select yields when using pthreads... " >&6; }
+if ${ol_cv_pthread_select_yields+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ if test "$cross_compiling" = yes; then :
+ ol_cv_pthread_select_yields=cross
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <pthread.h>
+#ifndef NULL
+#define NULL (void*) 0
+#endif
+
+static int fildes[2];
+
+static void *task(p)
+ void *p;
+{
+ int i;
+ struct timeval tv;
+
+ fd_set rfds;
+
+ tv.tv_sec=10;
+ tv.tv_usec=0;
+
+ FD_ZERO(&rfds);
+ FD_SET(fildes[0], &rfds);
+
+ /* we're not interested in any fds */
+ i = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
+
+ if(i < 0) {
+ perror("select");
+ exit(10);
+ }
+
+ exit(0); /* if we exit here, the select blocked the whole process */
+}
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+ pthread_t t;
+
+ /* create a pipe to select */
+ if(pipe(&fildes[0])) {
+ perror("select");
+ exit(1);
+ }
+
+#ifdef HAVE_PTHREAD_SETCONCURRENCY
+ (void) pthread_setconcurrency(2);
+#else
+#ifdef HAVE_THR_SETCONCURRENCY
+ /* Set Solaris LWP concurrency to 2 */
+ thr_setconcurrency(2);
+#endif
+#endif
+
+#if HAVE_PTHREADS < 6
+ pthread_create(&t, pthread_attr_default, task, NULL);
+#else
+ pthread_create(&t, NULL, task, NULL);
+#endif
+
+ /* make sure task runs first */
+#ifdef HAVE_THR_YIELD
+ thr_yield();
+#elif defined( HAVE_SCHED_YIELD )
+ sched_yield();
+#elif defined( HAVE_PTHREAD_YIELD )
+ pthread_yield();
+#endif
+
+ exit(2);
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ol_cv_pthread_select_yields=no
+else
+ ol_cv_pthread_select_yields=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_pthread_select_yields" >&5
+$as_echo "$ol_cv_pthread_select_yields" >&6; }
+
+ if test $ol_cv_pthread_select_yields = cross ; then
+ as_fn_error $? "crossing compiling: use --with-yielding_select=yes|no|manual" "$LINENO" 5
+ fi
+
+ if test $ol_cv_pthread_select_yields = yes ; then
+ ol_with_yielding_select=yes
+ fi
+ fi
+
+ CPPFLAGS="$save_CPPFLAGS"
+ LIBS="$save_LIBS"
+ else
+ as_fn_error $? "could not locate usable POSIX Threads" "$LINENO" 5
+ fi
+ fi
+
+ if test $ol_with_threads = posix ; then
+ as_fn_error $? "could not locate POSIX Threads" "$LINENO" 5
+ fi
+ ;;
+esac
+
+case $ol_with_threads in auto | yes | pth)
+
+ for ac_header in pth.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "pth.h" "ac_cv_header_pth_h" "$ac_includes_default"
+if test "x$ac_cv_header_pth_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_PTH_H 1
+_ACEOF
+
+fi
+
+done
+
+
+ if test $ac_cv_header_pth_h = yes ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pth_version in -lpth" >&5
+$as_echo_n "checking for pth_version in -lpth... " >&6; }
+if ${ac_cv_lib_pth_pth_version+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpth $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pth_version ();
+int
+main ()
+{
+return pth_version ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_pth_pth_version=yes
+else
+ ac_cv_lib_pth_pth_version=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pth_pth_version" >&5
+$as_echo "$ac_cv_lib_pth_pth_version" >&6; }
+if test "x$ac_cv_lib_pth_pth_version" = xyes; then :
+ have_pth=yes
+else
+ have_pth=no
+fi
+
+
+ if test $have_pth = yes ; then
+
+$as_echo "#define HAVE_GNU_PTH 1" >>confdefs.h
+
+ LTHREAD_LIBS="$LTHREAD_LIBS -lpth"
+ ol_link_threads=pth
+ ol_with_threads=found
+
+ if test $ol_with_yielding_select = auto ; then
+ ol_with_yielding_select=yes
+ fi
+ fi
+ fi
+ ;;
+esac
+
+case $ol_with_threads in auto | yes | lwp)
+
+ for ac_header in thread.h synch.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+ if test $ac_cv_header_thread_h = yes &&
+ test $ac_cv_header_synch_h = yes ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for thr_create in -lthread" >&5
+$as_echo_n "checking for thr_create in -lthread... " >&6; }
+if ${ac_cv_lib_thread_thr_create+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lthread $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char thr_create ();
+int
+main ()
+{
+return thr_create ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_thread_thr_create=yes
+else
+ ac_cv_lib_thread_thr_create=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_thread_thr_create" >&5
+$as_echo "$ac_cv_lib_thread_thr_create" >&6; }
+if test "x$ac_cv_lib_thread_thr_create" = xyes; then :
+ have_thr=yes
+else
+ have_thr=no
+fi
+
+
+ if test $have_thr = yes ; then
+
+$as_echo "#define HAVE_THR 1" >>confdefs.h
+
+ LTHREAD_LIBS="$LTHREAD_LIBS -lthread"
+ ol_link_threads=thr
+
+ if test $ol_with_yielding_select = auto ; then
+ ol_with_yielding_select=yes
+ fi
+
+ for ac_func in \
+ thr_setconcurrency \
+ thr_getconcurrency \
+
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+ fi
+ fi
+ ;;
+esac
+
+if test $ol_with_yielding_select = yes ; then
+
+$as_echo "#define HAVE_YIELDING_SELECT 1" >>confdefs.h
+
+fi
+
+if test $ol_with_threads = manual ; then
+ ol_link_threads=yes
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: thread defines and link options must be set manually" >&5
+$as_echo "$as_me: WARNING: thread defines and link options must be set manually" >&2;}
+
+ for ac_header in pthread.h sched.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+ for ac_func in sched_yield pthread_yield
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LinuxThreads pthread.h" >&5
+$as_echo_n "checking for LinuxThreads pthread.h... " >&6; }
+if ${ol_cv_header_linux_threads+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <pthread.h>
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "pthread_kill_other_threads_np" >/dev/null 2>&1; then :
+ ol_cv_header_linux_threads=yes
+else
+ ol_cv_header_linux_threads=no
+fi
+rm -f conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_header_linux_threads" >&5
+$as_echo "$ol_cv_header_linux_threads" >&6; }
+ if test $ol_cv_header_linux_threads = yes; then
+
+$as_echo "#define HAVE_LINUX_THREADS 1" >>confdefs.h
+
+ fi
+
+
+ for ac_header in thread.h synch.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+fi
+
+if test $ol_link_threads != no && test $ol_link_threads != nt ; then
+ $as_echo "#define REENTRANT 1" >>confdefs.h
+
+ $as_echo "#define _REENTRANT 1" >>confdefs.h
+
+ $as_echo "#define THREAD_SAFE 1" >>confdefs.h
+
+ $as_echo "#define _THREAD_SAFE 1" >>confdefs.h
+
+ $as_echo "#define THREADSAFE 1" >>confdefs.h
+
+ $as_echo "#define _THREADSAFE 1" >>confdefs.h
+
+ $as_echo "#define _SGI_MP_SOURCE 1" >>confdefs.h
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for thread specific errno" >&5
+$as_echo_n "checking for thread specific errno... " >&6; }
+if ${ol_cv_errno_thread_specific+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <errno.h>
+int
+main ()
+{
+errno = 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_errno_thread_specific=yes
+else
+ ol_cv_errno_thread_specific=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_errno_thread_specific" >&5
+$as_echo "$ol_cv_errno_thread_specific" >&6; }
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for thread specific h_errno" >&5
+$as_echo_n "checking for thread specific h_errno... " >&6; }
+if ${ol_cv_h_errno_thread_specific+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <netdb.h>
+int
+main ()
+{
+h_errno = 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_h_errno_thread_specific=yes
+else
+ ol_cv_h_errno_thread_specific=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_h_errno_thread_specific" >&5
+$as_echo "$ol_cv_h_errno_thread_specific" >&6; }
+
+ if test $ol_cv_errno_thread_specific != yes ||
+ test $ol_cv_h_errno_thread_specific != yes ; then
+ LIBS="$LTHREAD_LIBS $LIBS"
+ LTHREAD_LIBS=""
+ fi
+
+fi
+
+if test $ol_link_threads = no ; then
+ if test $ol_enable_slapd != no; then
+ as_fn_error $? "slapd requires thread support" "$LINENO" 5
+ fi
+
+ if test $ol_with_threads = yes ; then
+ as_fn_error $? "no suitable thread support" "$LINENO" 5
+ fi
+
+ if test $ol_with_threads = auto ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: no suitable thread support, disabling threads" >&5
+$as_echo "$as_me: WARNING: no suitable thread support, disabling threads" >&2;}
+ ol_with_threads=no
+ fi
+
+
+$as_echo "#define NO_THREADS 1" >>confdefs.h
+
+ LTHREAD_LIBS=""
+ BUILD_THREAD=no
+else
+ BUILD_THREAD=yes
+fi
+
+if test $ol_link_threads != no ; then
+
+$as_echo "#define LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE 1" >>confdefs.h
+
+
+
+$as_echo "#define LDAP_API_FEATURE_X_OPENLDAP_REENTRANT 1" >>confdefs.h
+
+fi
+
+for ac_func in \
+ ctime_r \
+ gmtime_r localtime_r \
+ gethostbyname_r gethostbyaddr_r \
+
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+if test "$ac_cv_func_ctime_r" = no ; then
+ ol_cv_func_ctime_r_nargs=0
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking number of arguments of ctime_r" >&5
+$as_echo_n "checking number of arguments of ctime_r... " >&6; }
+if ${ol_cv_func_ctime_r_nargs+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <time.h>
+int
+main ()
+{
+time_t ti; char *buffer; ctime_r(&ti,buffer,32);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ol_cv_func_ctime_r_nargs3=yes
+else
+ ol_cv_func_ctime_r_nargs3=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <time.h>
+int
+main ()
+{
+time_t ti; char *buffer; ctime_r(&ti,buffer);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ol_cv_func_ctime_r_nargs2=yes
+else
+ ol_cv_func_ctime_r_nargs2=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+ if test $ol_cv_func_ctime_r_nargs3 = yes &&
+ test $ol_cv_func_ctime_r_nargs2 = no ; then
+
+ ol_cv_func_ctime_r_nargs=3
+
+ elif test $ol_cv_func_ctime_r_nargs3 = no &&
+ test $ol_cv_func_ctime_r_nargs2 = yes ; then
+
+ ol_cv_func_ctime_r_nargs=2
+
+ else
+ ol_cv_func_ctime_r_nargs=0
+ fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_func_ctime_r_nargs" >&5
+$as_echo "$ol_cv_func_ctime_r_nargs" >&6; }
+
+ if test $ol_cv_func_ctime_r_nargs -gt 1 ; then
+
+cat >>confdefs.h <<_ACEOF
+#define CTIME_R_NARGS $ol_cv_func_ctime_r_nargs
+_ACEOF
+
+ fi
+
+fi
+
+if test "$ac_cv_func_gethostbyname_r" = yes ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking number of arguments of gethostbyname_r" >&5
+$as_echo_n "checking number of arguments of gethostbyname_r... " >&6; }
+if ${ol_cv_func_gethostbyname_r_nargs+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#define BUFSIZE (sizeof(struct hostent)+10)
+int
+main ()
+{
+struct hostent hent; char buffer[BUFSIZE];
+ int bufsize=BUFSIZE;int h_errno;
+ (void)gethostbyname_r("segovia.cs.purdue.edu", &hent,
+ buffer, bufsize, &h_errno);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ol_cv_func_gethostbyname_r_nargs5=yes
+else
+ ol_cv_func_gethostbyname_r_nargs5=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#define BUFSIZE (sizeof(struct hostent)+10)
+int
+main ()
+{
+struct hostent hent;struct hostent *rhent;
+ char buffer[BUFSIZE];
+ int bufsize=BUFSIZE;int h_errno;
+ (void)gethostbyname_r("localhost", &hent, buffer, bufsize,
+ &rhent, &h_errno);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ol_cv_func_gethostbyname_r_nargs6=yes
+else
+ ol_cv_func_gethostbyname_r_nargs6=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+ if test $ol_cv_func_gethostbyname_r_nargs5 = yes &&
+ test $ol_cv_func_gethostbyname_r_nargs6 = no ; then
+
+ ol_cv_func_gethostbyname_r_nargs=5
+
+ elif test $ol_cv_func_gethostbyname_r_nargs5 = no &&
+ test $ol_cv_func_gethostbyname_r_nargs6 = yes ; then
+
+ ol_cv_func_gethostbyname_r_nargs=6
+
+ else
+ ol_cv_func_gethostbyname_r_nargs=0
+ fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_func_gethostbyname_r_nargs" >&5
+$as_echo "$ol_cv_func_gethostbyname_r_nargs" >&6; }
+ if test $ol_cv_func_gethostbyname_r_nargs -gt 1 ; then
+
+cat >>confdefs.h <<_ACEOF
+#define GETHOSTBYNAME_R_NARGS $ol_cv_func_gethostbyname_r_nargs
+_ACEOF
+
+ fi
+
+else
+ ol_cv_func_gethostbyname_r_nargs=0
+fi
+
+if test "$ac_cv_func_gethostbyaddr_r" = yes ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking number of arguments of gethostbyaddr_r" >&5
+$as_echo_n "checking number of arguments of gethostbyaddr_r... " >&6; }
+if ${ol_cv_func_gethostbyaddr_r_nargs+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#define BUFSIZE (sizeof(struct hostent)+10)
+int
+main ()
+{
+struct hostent hent; char buffer[BUFSIZE];
+ struct in_addr add;
+ size_t alen=sizeof(struct in_addr);
+ int bufsize=BUFSIZE;int h_errno;
+ (void)gethostbyaddr_r( (void *)&(add.s_addr),
+ alen, AF_INET, &hent, buffer, bufsize, &h_errno);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ol_cv_func_gethostbyaddr_r_nargs7=yes
+else
+ ol_cv_func_gethostbyaddr_r_nargs7=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#define BUFSIZE (sizeof(struct hostent)+10)
+int
+main ()
+{
+struct hostent hent;
+ struct hostent *rhent; char buffer[BUFSIZE];
+ struct in_addr add;
+ size_t alen=sizeof(struct in_addr);
+ int bufsize=BUFSIZE;int h_errno;
+ (void)gethostbyaddr_r( (void *)&(add.s_addr),
+ alen, AF_INET, &hent, buffer, bufsize,
+ &rhent, &h_errno);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ol_cv_func_gethostbyaddr_r_nargs8=yes
+else
+ ol_cv_func_gethostbyaddr_r_nargs8=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+ if test $ol_cv_func_gethostbyaddr_r_nargs7 = yes &&
+ test $ol_cv_func_gethostbyaddr_r_nargs8 = no ; then
+
+ ol_cv_func_gethostbyaddr_r_nargs=7
+
+ elif test $ol_cv_func_gethostbyaddr_r_nargs7 = no &&
+ test $ol_cv_func_gethostbyaddr_r_nargs8 = yes ; then
+
+ ol_cv_func_gethostbyaddr_r_nargs=8
+
+ else
+ ol_cv_func_gethostbyaddr_r_nargs=0
+ fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_func_gethostbyaddr_r_nargs" >&5
+$as_echo "$ol_cv_func_gethostbyaddr_r_nargs" >&6; }
+ if test $ol_cv_func_gethostbyaddr_r_nargs -gt 1 ; then
+
+cat >>confdefs.h <<_ACEOF
+#define GETHOSTBYADDR_R_NARGS $ol_cv_func_gethostbyaddr_r_nargs
+_ACEOF
+
+ fi
+
+else
+ ol_cv_func_gethostbyaddr_r_nargs=0
+fi
+
+
+if test $ol_enable_dynamic = yes && test $enable_shared = yes ; then
+ BUILD_LIBS_DYNAMIC=shared
+
+$as_echo "#define LDAP_LIBS_DYNAMIC 1" >>confdefs.h
+
+ LTSTATIC=""
+else
+ BUILD_LIBS_DYNAMIC=static
+ LTSTATIC="-static"
+fi
+
+if test $ol_enable_wrappers != no ; then
+ for ac_header in tcpd.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "tcpd.h" "ac_cv_header_tcpd_h" "$ac_includes_default"
+if test "x$ac_cv_header_tcpd_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_TCPD_H 1
+_ACEOF
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for TCP wrappers library" >&5
+$as_echo_n "checking for TCP wrappers library... " >&6; }
+ save_LIBS="$LIBS"
+ LIBS="$LIBS -lwrap"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <tcpd.h>
+int allow_severity = 0;
+int deny_severity = 0;
+
+struct request_info *req;
+
+int
+main ()
+{
+
+hosts_access(req)
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: -lwrap" >&5
+$as_echo "-lwrap" >&6; }
+ have_wrappers=yes
+ LIBS="$save_LIBS"
+else
+
+ LIBS="$LIBS -lnsl"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <tcpd.h>
+int allow_severity = 0;
+int deny_severity = 0;
+
+struct request_info *req;
+
+int
+main ()
+{
+
+hosts_access(req)
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: -lwrap -lnsl" >&5
+$as_echo "-lwrap -lnsl" >&6; }
+ have_wrappers=yes
+ LIBS="$save_LIBS -lnsl"
+else
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ have_wrappers=no
+ LIBS=$save_LIBS
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+else
+ have_wrappers=no
+fi
+
+done
+
+
+ if test $have_wrappers = yes ; then
+
+$as_echo "#define HAVE_TCPD 1" >>confdefs.h
+
+ WRAP_LIBS="-lwrap"
+ elif test $ol_enable_wrappers = yes ; then
+ as_fn_error $? "could not find TCP wrappers, select appropriate options or disable" "$LINENO" 5
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: could not find TCP wrappers, support disabled" >&5
+$as_echo "$as_me: WARNING: could not find TCP wrappers, support disabled" >&2;}
+ WRAP_LIBS=""
+ fi
+fi
+
+if test $ol_enable_syslog != no ; then
+ ac_fn_c_check_func "$LINENO" "openlog" "ac_cv_func_openlog"
+if test "x$ac_cv_func_openlog" = xyes; then :
+
+fi
+
+ if test $ac_cv_func_openlog = no && test $ol_enable_syslog = yes; then
+ as_fn_error select appropriate options or disable "could not find syslog" "$LINENO" 5
+ fi
+ ol_enable_syslog=$ac_cv_func_openlog
+fi
+
+ol_link_sql=no
+if test $ol_enable_sql != no ; then
+ for ac_header in sql.h sqlext.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+else
+
+ as_fn_error $? "could not locate SQL headers" "$LINENO" 5
+
+fi
+
+done
+
+
+ sql_LIBS="$LIBS"
+ LIBS="$LTHREAD_LIBS $LIBS"
+
+ if test $ol_with_odbc = auto ; then
+ ol_with_odbc="iodbc unixodbc odbc32"
+ fi
+
+ for odbc in $ol_with_odbc ; do
+ if test $ol_link_sql = no ; then
+ case $odbc in
+ iodbc)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SQLDriverConnect in -liodbc" >&5
+$as_echo_n "checking for SQLDriverConnect in -liodbc... " >&6; }
+if ${ac_cv_lib_iodbc_SQLDriverConnect+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-liodbc $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char SQLDriverConnect ();
+int
+main ()
+{
+return SQLDriverConnect ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_iodbc_SQLDriverConnect=yes
+else
+ ac_cv_lib_iodbc_SQLDriverConnect=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_iodbc_SQLDriverConnect" >&5
+$as_echo "$ac_cv_lib_iodbc_SQLDriverConnect" >&6; }
+if test "x$ac_cv_lib_iodbc_SQLDriverConnect" = xyes; then :
+ have_iodbc=yes
+else
+ have_iodbc=no
+fi
+
+ if test $have_iodbc = yes ; then
+ ol_link_sql="-liodbc"
+ fi
+ ;;
+
+ unixodbc)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SQLDriverConnect in -lodbc" >&5
+$as_echo_n "checking for SQLDriverConnect in -lodbc... " >&6; }
+if ${ac_cv_lib_odbc_SQLDriverConnect+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lodbc $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char SQLDriverConnect ();
+int
+main ()
+{
+return SQLDriverConnect ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_odbc_SQLDriverConnect=yes
+else
+ ac_cv_lib_odbc_SQLDriverConnect=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_odbc_SQLDriverConnect" >&5
+$as_echo "$ac_cv_lib_odbc_SQLDriverConnect" >&6; }
+if test "x$ac_cv_lib_odbc_SQLDriverConnect" = xyes; then :
+ have_odbc=yes
+else
+ have_odbc=no
+fi
+
+ if test $have_odbc = yes ; then
+ ol_link_sql="-lodbc"
+ fi
+ ;;
+
+ odbc32)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SQLDriverConnect in -lodbc32" >&5
+$as_echo_n "checking for SQLDriverConnect in -lodbc32... " >&6; }
+if ${ac_cv_lib_odbc32_SQLDriverConnect+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lodbc32 $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char SQLDriverConnect ();
+int
+main ()
+{
+return SQLDriverConnect ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_odbc32_SQLDriverConnect=yes
+else
+ ac_cv_lib_odbc32_SQLDriverConnect=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_odbc32_SQLDriverConnect" >&5
+$as_echo "$ac_cv_lib_odbc32_SQLDriverConnect" >&6; }
+if test "x$ac_cv_lib_odbc32_SQLDriverConnect" = xyes; then :
+ have_odbc32=yes
+else
+ have_odbc32=no
+fi
+
+
+ if test $have_odbc32 = no ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SQLDriverConnect in -lodbc32 with windows.h" >&5
+$as_echo_n "checking for SQLDriverConnect in -lodbc32 with windows.h... " >&6; }
+ save_LIBS="$LIBS"
+ LIBS="$LIBS -lodbc32"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <windows.h>
+ #include <sqlext.h>
+
+int
+main ()
+{
+
+ SQLDriverConnect(NULL,NULL,NULL,0,NULL,0,NULL,0);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ have_odbc32=yes
+else
+ have_odbc32=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LIBS="$save_LIBS"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_odbc32" >&5
+$as_echo "$have_odbc32" >&6; }
+ fi
+
+ if test $have_odbc32 = yes ; then
+ ol_link_sql="-lodbc32"
+ fi
+ ;;
+
+ *)
+ as_fn_error $? "unknown ODBC library" "$LINENO" 5
+ ;;
+ esac
+ fi
+ done
+
+ LIBS="$sql_LIBS"
+
+ if test $ol_link_sql != no ; then
+ SLAPD_SQL_LIBS="$ol_link_sql"
+
+ elif test $ol_enable_sql != auto ; then
+ as_fn_error $? "could not locate suitable ODBC library" "$LINENO" 5
+ fi
+fi
+
+ol_link_ndb=no
+if test $ol_enable_ndb != no ; then
+ # Extract the first word of "mysql_config", so it can be a program name with args.
+set dummy mysql_config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_MYSQL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$MYSQL"; then
+ ac_cv_prog_MYSQL="$MYSQL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_MYSQL="yes"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+MYSQL=$ac_cv_prog_MYSQL
+if test -n "$MYSQL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MYSQL" >&5
+$as_echo "$MYSQL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ if test "$MYSQL" != yes ; then
+ as_fn_error $? "could not locate mysql_config" "$LINENO" 5
+ fi
+
+ SQL_INC=`mysql_config --include`
+ SLAPD_NDB_INCS="$SQL_INC $SQL_INC/storage/ndb $SQL_INC/storage/ndb/ndbapi"
+
+ save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$SLAPD_NDB_INCS"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for NdbApi.hpp" >&5
+$as_echo_n "checking for NdbApi.hpp... " >&6; }
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <NdbApi.hpp>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ as_fn_error $? "could not locate NdbApi headers" "$LINENO" 5
+
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+ CPPFLAGS="$save_CPPFLAGS"
+
+ SQL_LIB=`mysql_config --libs_r`
+ SLAPD_NDB_LIBS="$SQL_LIB -lndbclient -lstdc++"
+
+ save_LDFLAGS="$LDFLAGS"
+ save_LIBS="$LIBS"
+ LDFLAGS="$SQL_LIB"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ndb_init in -lndbclient" >&5
+$as_echo_n "checking for ndb_init in -lndbclient... " >&6; }
+if ${ac_cv_lib_ndbclient_ndb_init+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lndbclient -lstdc++ $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ndb_init ();
+int
+main ()
+{
+return ndb_init ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_ndbclient_ndb_init=yes
+else
+ ac_cv_lib_ndbclient_ndb_init=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ndbclient_ndb_init" >&5
+$as_echo "$ac_cv_lib_ndbclient_ndb_init" >&6; }
+if test "x$ac_cv_lib_ndbclient_ndb_init" = xyes; then :
+ : ok
+else
+
+ as_fn_error $? "could not locate ndbclient library" "$LINENO" 5
+
+fi
+
+ LIBS="$save_LIBS"
+ LDFLAGS="$save_LDFLAGS"
+
+ if test "$ol_enable_ndb" = yes ; then
+ SLAPD_LIBS="$SLAPD_LIBS \$(SLAPD_NDB_LIBS)"
+ fi
+fi
+
+ol_link_wt=no
+if test $ol_enable_wt != no ; then
+
+
+
+
+
+
+
+if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
+set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_PKG_CONFIG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $PKG_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+PKG_CONFIG=$ac_cv_path_PKG_CONFIG
+if test -n "$PKG_CONFIG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5
+$as_echo "$PKG_CONFIG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_path_PKG_CONFIG"; then
+ ac_pt_PKG_CONFIG=$PKG_CONFIG
+ # Extract the first word of "pkg-config", so it can be a program name with args.
+set dummy pkg-config; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $ac_pt_PKG_CONFIG in
+ [\\/]* | ?:[\\/]*)
+ ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path.
+ ;;
+ *)
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+fi
+ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG
+if test -n "$ac_pt_PKG_CONFIG"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5
+$as_echo "$ac_pt_PKG_CONFIG" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_pt_PKG_CONFIG" = x; then
+ PKG_CONFIG=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ PKG_CONFIG=$ac_pt_PKG_CONFIG
+ fi
+else
+ PKG_CONFIG="$ac_cv_path_PKG_CONFIG"
+fi
+
+fi
+if test -n "$PKG_CONFIG"; then
+ _pkg_min_version=0.9.0
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5
+$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; }
+ if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ PKG_CONFIG=""
+ fi
+fi
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for WT" >&5
+$as_echo_n "checking for WT... " >&6; }
+
+if test -n "$WT_CFLAGS"; then
+ pkg_cv_WT_CFLAGS="$WT_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"wiredtiger\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "wiredtiger") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_WT_CFLAGS=`$PKG_CONFIG --cflags "wiredtiger" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+if test -n "$WT_LIBS"; then
+ pkg_cv_WT_LIBS="$WT_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"wiredtiger\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "wiredtiger") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_WT_LIBS=`$PKG_CONFIG --libs "wiredtiger" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+ _pkg_short_errors_supported=yes
+else
+ _pkg_short_errors_supported=no
+fi
+ if test $_pkg_short_errors_supported = yes; then
+ WT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "wiredtiger" 2>&1`
+ else
+ WT_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "wiredtiger" 2>&1`
+ fi
+ # Put the nasty error message in config.log where it belongs
+ echo "$WT_PKG_ERRORS" >&5
+
+ as_fn_error $? "Package requirements (wiredtiger) were not met:
+
+$WT_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables WT_CFLAGS
+and WT_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details." "$LINENO" 5
+elif test $pkg_failed = untried; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables WT_CFLAGS
+and WT_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ WT_CFLAGS=$pkg_cv_WT_CFLAGS
+ WT_LIBS=$pkg_cv_WT_LIBS
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+fi
+ if test $ol_enable_wt = yes ; then
+ SLAPD_LIBS="$SLAPD_LIBS \$(WT_LIBS)"
+ fi
+ ol_link_wt=yes
+fi
+
+WITH_SASL=no
+ol_link_sasl=no
+ol_link_spasswd=no
+if test $ol_with_cyrus_sasl != no ; then
+ for ac_header in sasl/sasl.h sasl.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+ if test $ac_cv_header_sasl_sasl_h = yes ||
+ test $ac_cv_header_sasl_h = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sasl_client_init in -lsasl2" >&5
+$as_echo_n "checking for sasl_client_init in -lsasl2... " >&6; }
+if ${ac_cv_lib_sasl2_sasl_client_init+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsasl2 $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char sasl_client_init ();
+int
+main ()
+{
+return sasl_client_init ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_sasl2_sasl_client_init=yes
+else
+ ac_cv_lib_sasl2_sasl_client_init=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sasl2_sasl_client_init" >&5
+$as_echo "$ac_cv_lib_sasl2_sasl_client_init" >&6; }
+if test "x$ac_cv_lib_sasl2_sasl_client_init" = xyes; then :
+ ol_link_sasl="-lsasl2"
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sasl_client_init in -lsasl" >&5
+$as_echo_n "checking for sasl_client_init in -lsasl... " >&6; }
+if ${ac_cv_lib_sasl_sasl_client_init+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsasl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char sasl_client_init ();
+int
+main ()
+{
+return sasl_client_init ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_sasl_sasl_client_init=yes
+else
+ ac_cv_lib_sasl_sasl_client_init=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sasl_sasl_client_init" >&5
+$as_echo "$ac_cv_lib_sasl_sasl_client_init" >&6; }
+if test "x$ac_cv_lib_sasl_sasl_client_init" = xyes; then :
+ ol_link_sasl="-lsasl"
+fi
+
+fi
+
+ fi
+
+ if test $ol_link_sasl = no ; then
+ if test $ol_with_cyrus_sasl != auto ; then
+ as_fn_error $? "Could not locate Cyrus SASL" "$LINENO" 5
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Could not locate Cyrus SASL" >&5
+$as_echo "$as_me: WARNING: Could not locate Cyrus SASL" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: SASL authentication not supported!" >&5
+$as_echo "$as_me: WARNING: SASL authentication not supported!" >&2;}
+ if test $ol_link_tls = no ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Strong authentication not supported!" >&5
+$as_echo "$as_me: WARNING: Strong authentication not supported!" >&2;}
+ fi
+ fi
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking Cyrus SASL library version" >&5
+$as_echo_n "checking Cyrus SASL library version... " >&6; }
+if ${ol_cv_sasl_compat+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#ifdef HAVE_SASL_SASL_H
+#include <sasl/sasl.h>
+#else
+#include <sasl.h>
+#endif
+
+/* Require 2.1.15+ */
+#if SASL_VERSION_MAJOR == 2 && SASL_VERSION_MINOR > 1
+ char *__sasl_compat = "2.2+ or better okay (we guess)";
+#elif SASL_VERSION_MAJOR == 2 && SASL_VERSION_MINOR == 1 \
+ && SASL_VERSION_STEP >=15
+ char *__sasl_compat = "2.1.15+ or better okay";
+#endif
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "__sasl_compat" >/dev/null 2>&1; then :
+ ol_cv_sasl_compat=yes
+else
+ ol_cv_sasl_compat=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_sasl_compat" >&5
+$as_echo "$ol_cv_sasl_compat" >&6; }
+
+ if test $ol_cv_sasl_compat = no ; then
+ ol_link_sasl=no
+ as_fn_error $? "Cyrus SASL library located but is incompatible" "$LINENO" 5
+ fi
+
+
+$as_echo "#define HAVE_CYRUS_SASL 1" >>confdefs.h
+
+ SASL_LIBS="$ol_link_sasl"
+ if test $ol_enable_spasswd != no ; then
+ ol_link_spasswd=yes
+ fi
+
+ ac_save_LIBS="$LIBS"
+ LIBS="$LIBS $ol_link_sasl"
+ ac_fn_c_check_func "$LINENO" "sasl_version" "ac_cv_func_sasl_version"
+if test "x$ac_cv_func_sasl_version" = xyes; then :
+
+$as_echo "#define HAVE_SASL_VERSION 1" >>confdefs.h
+
+fi
+
+ LIBS="$ac_save_LIBS"
+
+ WITH_SASL=yes
+ fi
+
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: SASL authentication not supported!" >&5
+$as_echo "$as_me: WARNING: SASL authentication not supported!" >&2;}
+ if test $ol_link_tls = no ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Strong authentication not supported!" >&5
+$as_echo "$as_me: WARNING: Strong authentication not supported!" >&2;}
+ fi
+fi
+
+WITH_SYSTEMD=no
+systemdsystemunitdir=
+ol_link_systemd=no
+if test $ol_enable_slapd == no && test $ol_enable_balancer != yes ; then
+ if test $ol_with_systemd != no ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: servers disabled, ignoring --with-systemd=$ol_with_systemd argument" >&5
+$as_echo "$as_me: WARNING: servers disabled, ignoring --with-systemd=$ol_with_systemd argument" >&2;}
+ ol_with_systemd=no
+ fi
+fi
+if test $ol_with_systemd != no ; then
+ for ac_header in systemd/sd-daemon.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "systemd/sd-daemon.h" "ac_cv_header_systemd_sd_daemon_h" "$ac_includes_default"
+if test "x$ac_cv_header_systemd_sd_daemon_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_SYSTEMD_SD_DAEMON_H 1
+_ACEOF
+
+fi
+
+done
+
+
+ if test $ac_cv_header_systemd_sd_daemon_h = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sd_notify in -lsystemd" >&5
+$as_echo_n "checking for sd_notify in -lsystemd... " >&6; }
+if ${ac_cv_lib_systemd_sd_notify+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsystemd $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char sd_notify ();
+int
+main ()
+{
+return sd_notify ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_systemd_sd_notify=yes
+else
+ ac_cv_lib_systemd_sd_notify=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_systemd_sd_notify" >&5
+$as_echo "$ac_cv_lib_systemd_sd_notify" >&6; }
+if test "x$ac_cv_lib_systemd_sd_notify" = xyes; then :
+ ol_link_systemd="-lsystemd"
+fi
+
+ fi
+
+ if test $ol_link_systemd = no ; then
+ if test $ol_with_systemd != auto ; then
+ as_fn_error $? "Could not locate systemd" "$LINENO" 5
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Could not locate systemd" >&5
+$as_echo "$as_me: WARNING: Could not locate systemd" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: systemd service notification not supported!" >&5
+$as_echo "$as_me: WARNING: systemd service notification not supported!" >&2;}
+ fi
+ else
+
+$as_echo "#define HAVE_SYSTEMD 1" >>confdefs.h
+
+ SYSTEMD_LIBS="$ol_link_systemd"
+ WITH_SYSTEMD=yes
+
+
+if test -n "$systemdsystemunitdir"; then
+ pkg_cv_systemdsystemunitdir="$systemdsystemunitdir"
+ elif test -n "$PKG_CONFIG"; then
+ if test -n "$PKG_CONFIG" && \
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"systemd\""; } >&5
+ ($PKG_CONFIG --exists --print-errors "systemd") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ pkg_cv_systemdsystemunitdir=`$PKG_CONFIG --variable="systemdsystemunitdir" "systemd" 2>/dev/null`
+ test "x$?" != "x0" && pkg_failed=yes
+else
+ pkg_failed=yes
+fi
+ else
+ pkg_failed=untried
+fi
+systemdsystemunitdir=$pkg_cv_systemdsystemunitdir
+
+if test "x$systemdsystemunitdir" = x""; then :
+
+fi
+ if test -z "$systemdsystemunitdir"; then
+ if test -d /usr/lib/systemd/system; then
+ systemdsystemunitdir=/usr/lib/systemd/system
+ else
+ systemdsystemunitdir=/lib/systemd/system
+ fi
+ fi
+ fi
+fi
+
+
+if test $cross_compiling != yes && test "$ac_cv_mingw32" != yes ; then
+ dev=no
+ if test -r /dev/urandom ; then
+ dev="/dev/urandom";
+ elif test -r /idev/urandom ; then
+ dev="/idev/urandom";
+ elif test -r /dev/srandom ; then
+ dev="/dev/srandom";
+ elif test -r /dev/random ; then
+ dev="/dev/random";
+ elif test -r /idev/random ; then
+ dev="/idev/random";
+ fi
+
+ if test $dev != no ; then
+
+cat >>confdefs.h <<_ACEOF
+#define URANDOM_DEVICE "$dev"
+_ACEOF
+
+ fi
+fi
+
+ol_link_fetch=no
+if test $ol_with_fetch != no ; then
+ ol_LIBS=$LIBS
+LIBS="-lfetch -lcom_err $LIBS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking fetch(3) library" >&5
+$as_echo_n "checking fetch(3) library... " >&6; }
+if ${ol_cv_lib_fetch+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#include <stdio.h>
+#include <fetch.h>
+int
+main ()
+{
+struct url *u = fetchParseURL("file:///");
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_lib_fetch=yes
+else
+ ol_cv_lib_fetch=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_lib_fetch" >&5
+$as_echo "$ol_cv_lib_fetch" >&6; }
+LIBS=$ol_LIBS
+if test $ol_cv_lib_fetch != no ; then
+ ol_link_fetch="-lfetch -lcom_err"
+
+$as_echo "#define HAVE_FETCH 1" >>confdefs.h
+
+fi
+
+
+ if test $ol_cv_lib_fetch != no ; then
+ LIBS="$LIBS $ol_link_fetch"
+ ol_link_fetch=freebsd
+
+ elif test $ol_with_fetch != auto ; then
+ as_fn_error $? "no suitable API for --with-fetch=$ol_with_fetch" "$LINENO" 5
+ fi
+fi
+
+if test $ol_enable_crypt != no ; then
+ save_LIBS="$LIBS"
+ LIBS="$TLS_LIBS $LIBS"
+
+ ac_fn_c_check_func "$LINENO" "crypt" "ac_cv_func_crypt"
+if test "x$ac_cv_func_crypt" = xyes; then :
+ have_crypt=yes
+else
+
+ LIBS="$save_LIBS"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for crypt in -lcrypt" >&5
+$as_echo_n "checking for crypt in -lcrypt... " >&6; }
+if ${ac_cv_lib_crypt_crypt+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lcrypt $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char crypt ();
+int
+main ()
+{
+return crypt ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_crypt_crypt=yes
+else
+ ac_cv_lib_crypt_crypt=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypt_crypt" >&5
+$as_echo "$ac_cv_lib_crypt_crypt" >&6; }
+if test "x$ac_cv_lib_crypt_crypt" = xyes; then :
+ LUTIL_LIBS="$LUTIL_LIBS -lcrypt"
+ have_crypt=yes
+else
+ have_crypt=no
+fi
+
+fi
+
+
+ LIBS="$TLS_LIBS $LIBS"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for crypt_r in -lcrypt" >&5
+$as_echo_n "checking for crypt_r in -lcrypt... " >&6; }
+if ${ac_cv_lib_crypt_crypt_r+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lcrypt $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char crypt_r ();
+int
+main ()
+{
+return crypt_r ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_crypt_crypt_r=yes
+else
+ ac_cv_lib_crypt_crypt_r=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypt_crypt_r" >&5
+$as_echo "$ac_cv_lib_crypt_crypt_r" >&6; }
+if test "x$ac_cv_lib_crypt_crypt_r" = xyes; then :
+ have_crypt_r=yes
+else
+ have_crypt_r=no
+fi
+
+
+ LIBS="$save_LIBS"
+
+ if test $have_crypt = yes ; then
+
+$as_echo "#define HAVE_CRYPT 1" >>confdefs.h
+
+ if test $have_crypt_r = yes ; then
+
+$as_echo "#define HAVE_CRYPT_R 1" >>confdefs.h
+
+ fi
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: could not find crypt" >&5
+$as_echo "$as_me: WARNING: could not find crypt" >&2;}
+ if test $ol_enable_crypt = yes ; then
+ as_fn_error $? "could not find crypt, select appropriate options or disable" "$LINENO" 5
+ fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: disabling crypt support" >&5
+$as_echo "$as_me: WARNING: disabling crypt support" >&2;}
+ ol_enable_crypt=no
+ fi
+fi
+
+if test $ol_enable_slp != no ; then
+ for ac_header in slp.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+ if test $ac_cv_header_slp_h = yes ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SLPOpen in -lslp" >&5
+$as_echo_n "checking for SLPOpen in -lslp... " >&6; }
+if ${ac_cv_lib_slp_SLPOpen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lslp $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char SLPOpen ();
+int
+main ()
+{
+return SLPOpen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_slp_SLPOpen=yes
+else
+ ac_cv_lib_slp_SLPOpen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_slp_SLPOpen" >&5
+$as_echo "$ac_cv_lib_slp_SLPOpen" >&6; }
+if test "x$ac_cv_lib_slp_SLPOpen" = xyes; then :
+ have_slp=yes
+else
+ have_slp=no
+fi
+
+ if test $have_slp = yes ; then
+
+$as_echo "#define HAVE_SLP 1" >>confdefs.h
+
+ SLAPD_SLP_LIBS=-lslp
+ fi
+
+ elif test $ol_enable_slp = yes ; then
+ as_fn_error $? "SLP not found" "$LINENO" 5
+ fi
+fi
+
+if test $ol_enable_balancer != no ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for evdns_base_new in -levent_extra" >&5
+$as_echo_n "checking for evdns_base_new in -levent_extra... " >&6; }
+if ${ac_cv_lib_event_extra_evdns_base_new+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-levent_extra $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char evdns_base_new ();
+int
+main ()
+{
+return evdns_base_new ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_event_extra_evdns_base_new=yes
+else
+ ac_cv_lib_event_extra_evdns_base_new=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_event_extra_evdns_base_new" >&5
+$as_echo "$ac_cv_lib_event_extra_evdns_base_new" >&6; }
+if test "x$ac_cv_lib_event_extra_evdns_base_new" = xyes; then :
+ have_libevent=yes
+ LEVENT_LIBS="$LEVENT_LIBS -levent_core -levent_extra"
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for evdns_base_new in -levent" >&5
+$as_echo_n "checking for evdns_base_new in -levent... " >&6; }
+if ${ac_cv_lib_event_evdns_base_new+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-levent $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char evdns_base_new ();
+int
+main ()
+{
+return evdns_base_new ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_event_evdns_base_new=yes
+else
+ ac_cv_lib_event_evdns_base_new=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_event_evdns_base_new" >&5
+$as_echo "$ac_cv_lib_event_evdns_base_new" >&6; }
+if test "x$ac_cv_lib_event_evdns_base_new" = xyes; then :
+ have_libevent=yes
+ LEVENT_LIBS="$LEVENT_LIBS -levent"
+else
+ have_libevent=no
+fi
+
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libevent_global_shutdown in -levent" >&5
+$as_echo_n "checking for libevent_global_shutdown in -levent... " >&6; }
+if ${ac_cv_lib_event_libevent_global_shutdown+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-levent $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char libevent_global_shutdown ();
+int
+main ()
+{
+return libevent_global_shutdown ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_event_libevent_global_shutdown=yes
+else
+ ac_cv_lib_event_libevent_global_shutdown=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_event_libevent_global_shutdown" >&5
+$as_echo "$ac_cv_lib_event_libevent_global_shutdown" >&6; }
+if test "x$ac_cv_lib_event_libevent_global_shutdown" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBEVENT 1
+_ACEOF
+
+ LIBS="-levent $LIBS"
+
+else
+ have_libevent=no
+fi
+
+
+ if test $have_libevent = yes ; then
+
+$as_echo "#define HAVE_LIBEVENT 1" >>confdefs.h
+
+ else
+ as_fn_error $? "You need libevent 2.1 or later with DNS support to build the load balancer" "$LINENO" 5
+ fi
+fi
+
+
+ac_fn_c_check_type "$LINENO" "mode_t" "ac_cv_type_mode_t" "$ac_includes_default"
+if test "x$ac_cv_type_mode_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+#define mode_t int
+_ACEOF
+
+fi
+
+ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default"
+if test "x$ac_cv_type_off_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+#define off_t long
+_ACEOF
+
+fi
+
+ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default"
+if test "x$ac_cv_type_pid_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+#define pid_t int
+_ACEOF
+
+fi
+
+ac_fn_c_check_type "$LINENO" "ssize_t" "ac_cv_type_ssize_t" "$ac_includes_default"
+if test "x$ac_cv_type_ssize_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+#define ssize_t signed int
+_ACEOF
+
+fi
+
+ac_fn_c_check_type "$LINENO" "caddr_t" "ac_cv_type_caddr_t" "$ac_includes_default"
+if test "x$ac_cv_type_caddr_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+#define caddr_t char *
+_ACEOF
+
+fi
+
+ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
+if test "x$ac_cv_type_size_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+#define size_t unsigned
+_ACEOF
+
+fi
+
+
+ac_fn_c_check_type "$LINENO" "long long" "ac_cv_type_long_long" "$ac_includes_default"
+if test "x$ac_cv_type_long_long" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LONG_LONG 1
+_ACEOF
+
+
+fi
+
+ac_fn_c_check_type "$LINENO" "ptrdiff_t" "ac_cv_type_ptrdiff_t" "$ac_includes_default"
+if test "x$ac_cv_type_ptrdiff_t" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_PTRDIFF_T 1
+_ACEOF
+
+
+fi
+
+
+
+ac_fn_c_check_type "$LINENO" "socklen_t" "ac_cv_type_socklen_t" "$ac_includes_default
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_WINSOCK2
+#include <ws2tcpip.h>
+#endif
+"
+if test "x$ac_cv_type_socklen_t" = xyes; then :
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the type of arg 3 to accept()" >&5
+$as_echo_n "checking the type of arg 3 to accept()... " >&6; }
+if ${ol_cv_type_ber_socklen_t+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ set socklen_t int unsigned "unsigned long" long size_t
+ test "$ac_cv_type_socklen_t" = yes || shift
+ ol_cv_type_ber_socklen_t=$1 guessing="guessing "
+ for lentype in "$@" ; do for addrtype in "struct sockaddr" void ; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+extern int accept(int s, $addrtype *ap, $lentype *lp);
+
+int
+main ()
+{
+
+accept(0, (struct sockaddr *) 0, ($lentype *) 0);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ol_cv_type_ber_socklen_t=$lentype guessing= ; break 2
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ done ; done
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $guessing$ol_cv_type_ber_socklen_t *" >&5
+$as_echo "$guessing$ol_cv_type_ber_socklen_t *" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+#define ber_socklen_t $ol_cv_type_ber_socklen_t
+_ACEOF
+
+
+if test "$ac_cv_type_socklen_t" != yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define socklen_t $ol_cv_type_ber_socklen_t
+_ACEOF
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of signal handlers" >&5
+$as_echo_n "checking return type of signal handlers... " >&6; }
+if ${ac_cv_type_signal+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <signal.h>
+
+int
+main ()
+{
+return *(signal (0, 0)) (0) == 1;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_type_signal=int
+else
+ ac_cv_type_signal=void
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_signal" >&5
+$as_echo "$ac_cv_type_signal" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+#define RETSIGTYPE $ac_cv_type_signal
+_ACEOF
+
+
+
+ac_fn_c_check_type "$LINENO" "sig_atomic_t" "ac_cv_type_sig_atomic_t" "$ac_includes_default
+#include <signal.h>
+
+"
+if test "x$ac_cv_type_sig_atomic_t" = xyes; then :
+
+else
+
+cat >>confdefs.h <<_ACEOF
+#define sig_atomic_t int
+_ACEOF
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5
+$as_echo_n "checking for uid_t in sys/types.h... " >&6; }
+if ${ac_cv_type_uid_t+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "uid_t" >/dev/null 2>&1; then :
+ ac_cv_type_uid_t=yes
+else
+ ac_cv_type_uid_t=no
+fi
+rm -f conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uid_t" >&5
+$as_echo "$ac_cv_type_uid_t" >&6; }
+if test $ac_cv_type_uid_t = no; then
+
+$as_echo "#define uid_t int" >>confdefs.h
+
+
+$as_echo "#define gid_t int" >>confdefs.h
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5
+$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; }
+if ${ac_cv_header_time+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+
+int
+main ()
+{
+if ((struct tm *) 0)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_time=yes
+else
+ ac_cv_header_time=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5
+$as_echo "$ac_cv_header_time" >&6; }
+if test $ac_cv_header_time = yes; then
+
+$as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5
+$as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; }
+if ${ac_cv_struct_tm+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <time.h>
+
+int
+main ()
+{
+struct tm tm;
+ int *p = &tm.tm_sec;
+ return !p;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_struct_tm=time.h
+else
+ ac_cv_struct_tm=sys/time.h
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_tm" >&5
+$as_echo "$ac_cv_struct_tm" >&6; }
+if test $ac_cv_struct_tm = sys/time.h; then
+
+$as_echo "#define TM_IN_SYS_TIME 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_member "$LINENO" "struct stat" "st_blksize" "ac_cv_member_struct_stat_st_blksize" "$ac_includes_default"
+if test "x$ac_cv_member_struct_stat_st_blksize" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_STAT_ST_BLKSIZE 1
+_ACEOF
+
+
+fi
+
+ac_fn_c_check_member "$LINENO" "struct passwd" "pw_gecos" "ac_cv_member_struct_passwd_pw_gecos" "$ac_includes_default
+#include <pwd.h>
+"
+if test "x$ac_cv_member_struct_passwd_pw_gecos" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_PASSWD_PW_GECOS 1
+_ACEOF
+
+
+fi
+
+ac_fn_c_check_member "$LINENO" "struct passwd" "pw_passwd" "ac_cv_member_struct_passwd_pw_passwd" "$ac_includes_default
+#include <pwd.h>
+"
+if test "x$ac_cv_member_struct_passwd_pw_passwd" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_PASSWD_PW_PASSWD 1
+_ACEOF
+
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if toupper() requires islower()" >&5
+$as_echo_n "checking if toupper() requires islower()... " >&6; }
+if ${ol_cv_c_upper_lower+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ if test "$cross_compiling" = yes; then :
+ ol_cv_c_upper_lower=safe
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <ctype.h>
+main()
+{
+ if ('C' == toupper('C'))
+ exit(0);
+ else
+ exit(1);
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ol_cv_c_upper_lower=no
+else
+ ol_cv_c_upper_lower=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_c_upper_lower" >&5
+$as_echo "$ol_cv_c_upper_lower" >&6; }
+if test $ol_cv_c_upper_lower != no ; then
+
+$as_echo "#define C_UPPER_LOWER 1" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5
+$as_echo_n "checking for an ANSI C-conforming const... " >&6; }
+if ${ac_cv_c_const+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+#ifndef __cplusplus
+ /* Ultrix mips cc rejects this sort of thing. */
+ typedef int charset[2];
+ const charset cs = { 0, 0 };
+ /* SunOS 4.1.1 cc rejects this. */
+ char const *const *pcpcc;
+ char **ppc;
+ /* NEC SVR4.0.2 mips cc rejects this. */
+ struct point {int x, y;};
+ static struct point const zero = {0,0};
+ /* AIX XL C 1.02.0.0 rejects this.
+ It does not let you subtract one const X* pointer from another in
+ an arm of an if-expression whose if-part is not a constant
+ expression */
+ const char *g = "string";
+ pcpcc = &g + (g ? g-g : 0);
+ /* HPUX 7.0 cc rejects these. */
+ ++pcpcc;
+ ppc = (char**) pcpcc;
+ pcpcc = (char const *const *) ppc;
+ { /* SCO 3.2v4 cc rejects this sort of thing. */
+ char tx;
+ char *t = &tx;
+ char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+ *t++ = 0;
+ if (s) return 0;
+ }
+ { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */
+ int x[] = {25, 17};
+ const int *foo = &x[0];
+ ++foo;
+ }
+ { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+ typedef const int *iptr;
+ iptr p = 0;
+ ++p;
+ }
+ { /* AIX XL C 1.02.0.0 rejects this sort of thing, saying
+ "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+ struct s { int j; const int *ap[3]; } bx;
+ struct s *b = &bx; b->j = 5;
+ }
+ { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+ const int foo = 10;
+ if (!foo) return 0;
+ }
+ return !cs[0] && !zero.x;
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_c_const=yes
+else
+ ac_cv_c_const=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5
+$as_echo "$ac_cv_c_const" >&6; }
+if test $ac_cv_c_const = no; then
+
+$as_echo "#define const /**/" >>confdefs.h
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if compiler understands volatile" >&5
+$as_echo_n "checking if compiler understands volatile... " >&6; }
+if ${ol_cv_c_volatile+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+int x, y, z;
+int
+main ()
+{
+volatile int a; int * volatile b = x ? &y : &z;
+ /* Older MIPS compilers (eg., in Ultrix 4.2) don't like *b = 0 */
+ *b = 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ol_cv_c_volatile=yes
+else
+ ol_cv_c_volatile=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_c_volatile" >&5
+$as_echo "$ol_cv_c_volatile" >&6; }
+ if test $ol_cv_c_volatile = yes; then
+ :
+ else
+
+$as_echo "#define volatile /**/" >>confdefs.h
+
+ fi
+
+
+if test $cross_compiling = yes ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Crossing compiling... all bets are off!" >&5
+$as_echo "$as_me: WARNING: Crossing compiling... all bets are off!" >&2;}
+
+$as_echo "#define CROSS_COMPILING 1" >>confdefs.h
+
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5
+$as_echo_n "checking whether byte ordering is bigendian... " >&6; }
+if ${ac_cv_c_bigendian+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_c_bigendian=unknown
+ # See if we're dealing with a universal compiler.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifndef __APPLE_CC__
+ not a universal capable compiler
+ #endif
+ typedef int dummy;
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+ # Check for potential -arch flags. It is not universal unless
+ # there are at least two -arch flags with different values.
+ ac_arch=
+ ac_prev=
+ for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do
+ if test -n "$ac_prev"; then
+ case $ac_word in
+ i?86 | x86_64 | ppc | ppc64)
+ if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then
+ ac_arch=$ac_word
+ else
+ ac_cv_c_bigendian=universal
+ break
+ fi
+ ;;
+ esac
+ ac_prev=
+ elif test "x$ac_word" = "x-arch"; then
+ ac_prev=arch
+ fi
+ done
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ if test $ac_cv_c_bigendian = unknown; then
+ # See if sys/param.h defines the BYTE_ORDER macro.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+ #include <sys/param.h>
+
+int
+main ()
+{
+#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \
+ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \
+ && LITTLE_ENDIAN)
+ bogus endian macros
+ #endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ # It does; now see whether it defined to BIG_ENDIAN or not.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+ #include <sys/param.h>
+
+int
+main ()
+{
+#if BYTE_ORDER != BIG_ENDIAN
+ not big endian
+ #endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_c_bigendian=yes
+else
+ ac_cv_c_bigendian=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ fi
+ if test $ac_cv_c_bigendian = unknown; then
+ # See if <limits.h> defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris).
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <limits.h>
+
+int
+main ()
+{
+#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN)
+ bogus endian macros
+ #endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ # It does; now see whether it defined to _BIG_ENDIAN or not.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <limits.h>
+
+int
+main ()
+{
+#ifndef _BIG_ENDIAN
+ not big endian
+ #endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_c_bigendian=yes
+else
+ ac_cv_c_bigendian=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ fi
+ if test $ac_cv_c_bigendian = unknown; then
+ # Compile a test program.
+ if test "$cross_compiling" = yes; then :
+ # Try to guess by grepping values from an object file.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+short int ascii_mm[] =
+ { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
+ short int ascii_ii[] =
+ { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
+ int use_ascii (int i) {
+ return ascii_mm[i] + ascii_ii[i];
+ }
+ short int ebcdic_ii[] =
+ { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
+ short int ebcdic_mm[] =
+ { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
+ int use_ebcdic (int i) {
+ return ebcdic_mm[i] + ebcdic_ii[i];
+ }
+ extern int foo;
+
+int
+main ()
+{
+return use_ascii (foo) == use_ebcdic (foo);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then
+ ac_cv_c_bigendian=yes
+ fi
+ if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then
+ if test "$ac_cv_c_bigendian" = unknown; then
+ ac_cv_c_bigendian=no
+ else
+ # finding both strings is unlikely to happen, but who knows?
+ ac_cv_c_bigendian=unknown
+ fi
+ fi
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+
+ /* Are we little or big endian? From Harbison&Steele. */
+ union
+ {
+ long int l;
+ char c[sizeof (long int)];
+ } u;
+ u.l = 1;
+ return u.c[sizeof (long int) - 1] == 1;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ac_cv_c_bigendian=no
+else
+ ac_cv_c_bigendian=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5
+$as_echo "$ac_cv_c_bigendian" >&6; }
+ case $ac_cv_c_bigendian in #(
+ yes)
+ $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h
+;; #(
+ no)
+ ;; #(
+ universal)
+
+$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h
+
+ ;; #(
+ *)
+ as_fn_error $? "unknown endianness
+ presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;;
+ esac
+
+fi
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of short" >&5
+$as_echo_n "checking size of short... " >&6; }
+if ${ac_cv_sizeof_short+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (short))" "ac_cv_sizeof_short" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_short" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (short)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_short=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_short" >&5
+$as_echo "$ac_cv_sizeof_short" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_SHORT $ac_cv_sizeof_short
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of int" >&5
+$as_echo_n "checking size of int... " >&6; }
+if ${ac_cv_sizeof_int+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (int))" "ac_cv_sizeof_int" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_int" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (int)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_int=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int" >&5
+$as_echo "$ac_cv_sizeof_int" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_INT $ac_cv_sizeof_int
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long" >&5
+$as_echo_n "checking size of long... " >&6; }
+if ${ac_cv_sizeof_long+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long))" "ac_cv_sizeof_long" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_long" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (long)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_long=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long" >&5
+$as_echo "$ac_cv_sizeof_long" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_LONG $ac_cv_sizeof_long
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long long" >&5
+$as_echo_n "checking size of long long... " >&6; }
+if ${ac_cv_sizeof_long_long+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long long))" "ac_cv_sizeof_long_long" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_long_long" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (long long)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_long_long=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long_long" >&5
+$as_echo "$ac_cv_sizeof_long_long" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_LONG_LONG $ac_cv_sizeof_long_long
+_ACEOF
+
+
+# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of wchar_t" >&5
+$as_echo_n "checking size of wchar_t... " >&6; }
+if ${ac_cv_sizeof_wchar_t+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (wchar_t))" "ac_cv_sizeof_wchar_t" "$ac_includes_default"; then :
+
+else
+ if test "$ac_cv_type_wchar_t" = yes; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (wchar_t)
+See \`config.log' for more details" "$LINENO" 5; }
+ else
+ ac_cv_sizeof_wchar_t=0
+ fi
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_wchar_t" >&5
+$as_echo "$ac_cv_sizeof_wchar_t" >&6; }
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define SIZEOF_WCHAR_T $ac_cv_sizeof_wchar_t
+_ACEOF
+
+
+
+if test "$ac_cv_sizeof_int" -lt 4 ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: OpenLDAP requires 'int' to be 32 bits or greater." >&5
+$as_echo "$as_me: WARNING: OpenLDAP requires 'int' to be 32 bits or greater." >&2;}
+
+
+$as_echo "#define LBER_INT_T long" >>confdefs.h
+
+else
+
+$as_echo "#define LBER_INT_T int" >>confdefs.h
+
+fi
+
+
+$as_echo "#define LBER_LEN_T long" >>confdefs.h
+
+
+$as_echo "#define LBER_SOCKET_T int" >>confdefs.h
+
+
+$as_echo "#define LBER_TAG_T long" >>confdefs.h
+
+
+if test $ol_with_mp = longlong || test $ol_with_mp = auto ; then
+ if test $ac_cv_sizeof_long_long -gt 4 ; then
+ ol_with_mp=longlong
+
+$as_echo "#define USE_MP_LONG_LONG 1" >>confdefs.h
+
+ elif test $ol_with_mp = longlong ; then
+ as_fn_error $? "long long unusable for multiple precision" "$LINENO" 5
+ fi
+fi
+if test $ol_with_mp = long || test $ol_with_mp = auto ; then
+ if test $ac_cv_sizeof_long -gt 4 ; then
+ ol_with_mp=long
+
+$as_echo "#define USE_MP_LONG 1" >>confdefs.h
+
+ elif test $ol_with_mp = long ; then
+ as_fn_error $? "long unusable for multiple precision" "$LINENO" 5
+ fi
+fi
+if test $ol_with_mp = bignum || test $ol_with_mp = auto ; then
+ for ac_header in openssl/bn.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "openssl/bn.h" "ac_cv_header_openssl_bn_h" "$ac_includes_default"
+if test "x$ac_cv_header_openssl_bn_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_OPENSSL_BN_H 1
+_ACEOF
+
+fi
+
+done
+
+ for ac_header in openssl/crypto.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "openssl/crypto.h" "ac_cv_header_openssl_crypto_h" "$ac_includes_default"
+if test "x$ac_cv_header_openssl_crypto_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_OPENSSL_CRYPTO_H 1
+_ACEOF
+
+fi
+
+done
+
+ if test "$ac_cv_header_openssl_bn_h" = "yes" &&
+ test "$ac_cv_header_openssl_crypto_h" = "yes" &&
+ test "$ol_with_tls" = "found" ; then
+ ol_with_mp=bignum
+
+$as_echo "#define USE_MP_BIGNUM 1" >>confdefs.h
+
+ elif test $ol_with_mp = bignum ; then
+ as_fn_error $? "bignum not available" "$LINENO" 5
+ fi
+fi
+if test $ol_with_mp = gmp || test $ol_with_mp = auto ; then
+ for ac_header in gmp.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "gmp.h" "ac_cv_header_gmp_h" "$ac_includes_default"
+if test "x$ac_cv_header_gmp_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_GMP_H 1
+_ACEOF
+
+fi
+
+done
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __gmpz_add_ui in -lgmp" >&5
+$as_echo_n "checking for __gmpz_add_ui in -lgmp... " >&6; }
+if ${ac_cv_lib_gmp___gmpz_add_ui+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lgmp $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char __gmpz_add_ui ();
+int
+main ()
+{
+return __gmpz_add_ui ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_gmp___gmpz_add_ui=yes
+else
+ ac_cv_lib_gmp___gmpz_add_ui=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gmp___gmpz_add_ui" >&5
+$as_echo "$ac_cv_lib_gmp___gmpz_add_ui" >&6; }
+if test "x$ac_cv_lib_gmp___gmpz_add_ui" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBGMP 1
+_ACEOF
+
+ LIBS="-lgmp $LIBS"
+
+fi
+
+ if test $ac_cv_header_gmp_h = yes && test $ac_cv_lib_gmp___gmpz_add_ui = yes ; then
+
+$as_echo "#define USE_MP_GMP 1" >>confdefs.h
+
+ ol_with_mp=gmp
+ elif test $ol_with_mp = gmp ; then
+ as_fn_error $? "gmp not available" "$LINENO" 5
+ fi
+fi
+if test $ol_with_mp = auto ; then
+ ol_with_mp=no
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working memcmp" >&5
+$as_echo_n "checking for working memcmp... " >&6; }
+if ${ac_cv_func_memcmp_working+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "$cross_compiling" = yes; then :
+ ac_cv_func_memcmp_working=no
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+int
+main ()
+{
+
+ /* Some versions of memcmp are not 8-bit clean. */
+ char c0 = '\100', c1 = '\200', c2 = '\201';
+ if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0)
+ return 1;
+
+ /* The Next x86 OpenStep bug shows up only when comparing 16 bytes
+ or more and with at least one buffer not starting on a 4-byte boundary.
+ William Lewis provided this test program. */
+ {
+ char foo[21];
+ char bar[21];
+ int i;
+ for (i = 0; i < 4; i++)
+ {
+ char *a = foo + i;
+ char *b = bar + i;
+ strcpy (a, "--------01111111");
+ strcpy (b, "--------10000000");
+ if (memcmp (a, b, 16) >= 0)
+ return 1;
+ }
+ return 0;
+ }
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+ ac_cv_func_memcmp_working=yes
+else
+ ac_cv_func_memcmp_working=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_memcmp_working" >&5
+$as_echo "$ac_cv_func_memcmp_working" >&6; }
+test $ac_cv_func_memcmp_working = no && case " $LIBOBJS " in
+ *" memcmp.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS memcmp.$ac_objext"
+ ;;
+esac
+
+
+
+if test $ac_cv_func_memcmp_working = no ; then
+
+$as_echo "#define NEED_MEMCMP_REPLACEMENT 1" >>confdefs.h
+
+fi
+
+for ac_func in strftime
+do :
+ ac_fn_c_check_func "$LINENO" "strftime" "ac_cv_func_strftime"
+if test "x$ac_cv_func_strftime" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_STRFTIME 1
+_ACEOF
+
+else
+ # strftime is in -lintl on SCO UNIX.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for strftime in -lintl" >&5
+$as_echo_n "checking for strftime in -lintl... " >&6; }
+if ${ac_cv_lib_intl_strftime+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lintl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char strftime ();
+int
+main ()
+{
+return strftime ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_intl_strftime=yes
+else
+ ac_cv_lib_intl_strftime=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_intl_strftime" >&5
+$as_echo "$ac_cv_lib_intl_strftime" >&6; }
+if test "x$ac_cv_lib_intl_strftime" = xyes; then :
+ $as_echo "#define HAVE_STRFTIME 1" >>confdefs.h
+
+LIBS="-lintl $LIBS"
+fi
+
+fi
+done
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inet_aton()" >&5
+$as_echo_n "checking for inet_aton()... " >&6; }
+if ${ol_cv_func_inet_aton+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+# ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+# endif
+# include <netinet/in.h>
+# ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+# endif
+#endif
+
+int
+main ()
+{
+struct in_addr in;
+int rc = inet_aton( "255.255.255.255", &in );
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ol_cv_func_inet_aton=yes
+else
+ ol_cv_func_inet_aton=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ol_cv_func_inet_aton" >&5
+$as_echo "$ol_cv_func_inet_aton" >&6; }
+ if test $ol_cv_func_inet_aton != no; then
+
+$as_echo "#define HAVE_INET_ATON 1" >>confdefs.h
+
+ fi
+
+
+ac_fn_c_check_func "$LINENO" "_spawnlp" "ac_cv_func__spawnlp"
+if test "x$ac_cv_func__spawnlp" = xyes; then :
+
+$as_echo "#define HAVE_SPAWNLP 1" >>confdefs.h
+
+fi
+
+
+ac_fn_c_check_func "$LINENO" "_snprintf" "ac_cv_func__snprintf"
+if test "x$ac_cv_func__snprintf" = xyes; then :
+ ac_cv_func_snprintf=yes
+
+$as_echo "#define snprintf _snprintf" >>confdefs.h
+
+
+fi
+
+
+for ac_func in vsnprintf _vsnprintf
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+if test $ac_cv_func_vsnprintf = no -a $ac_cv_func__vsnprintf = yes ; then
+ ac_cv_func_vsnprintf=yes
+
+$as_echo "#define vsnprintf _vsnprintf" >>confdefs.h
+
+fi
+
+for ac_func in vprintf
+do :
+ ac_fn_c_check_func "$LINENO" "vprintf" "ac_cv_func_vprintf"
+if test "x$ac_cv_func_vprintf" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_VPRINTF 1
+_ACEOF
+
+ac_fn_c_check_func "$LINENO" "_doprnt" "ac_cv_func__doprnt"
+if test "x$ac_cv_func__doprnt" = xyes; then :
+
+$as_echo "#define HAVE_DOPRNT 1" >>confdefs.h
+
+fi
+
+fi
+done
+
+
+
+if test $ac_cv_func_vprintf = yes ; then
+ for ac_func in snprintf vsnprintf
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+fi
+
+for ac_func in \
+ bcopy \
+ clock_gettime \
+ closesocket \
+ chroot \
+ endgrent \
+ endpwent \
+ fcntl \
+ flock \
+ fstat \
+ getdtablesize \
+ geteuid \
+ getgrgid \
+ gethostname \
+ getpassphrase \
+ getpwuid \
+ getpwnam \
+ getspnam \
+ gettimeofday \
+ initgroups \
+ inet_ntoa_b \
+ ioctl \
+ lockf \
+ memcpy \
+ memmove \
+ memrchr \
+ mkstemp \
+ mktemp \
+ pipe \
+ read \
+ recv \
+ recvfrom \
+ setpwfile \
+ setgid \
+ setegid \
+ setsid \
+ setuid \
+ seteuid \
+ signal \
+ strdup \
+ strpbrk \
+ strrchr \
+ strsep \
+ strstr \
+ strtol \
+ strtoul \
+ strtoq \
+ strtouq \
+ strtoll \
+ strtoull \
+ strspn \
+ sysconf \
+ waitpid \
+ wait4 \
+ write \
+ send \
+ sendmsg \
+ sendto \
+
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+ac_fn_c_check_func "$LINENO" "getopt" "ac_cv_func_getopt"
+if test "x$ac_cv_func_getopt" = xyes; then :
+ $as_echo "#define HAVE_GETOPT 1" >>confdefs.h
+
+else
+ case " $LIBOBJS " in
+ *" getopt.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS getopt.$ac_objext"
+ ;;
+esac
+
+fi
+
+ac_fn_c_check_func "$LINENO" "getpeereid" "ac_cv_func_getpeereid"
+if test "x$ac_cv_func_getpeereid" = xyes; then :
+ $as_echo "#define HAVE_GETPEEREID 1" >>confdefs.h
+
+else
+ case " $LIBOBJS " in
+ *" getpeereid.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS getpeereid.$ac_objext"
+ ;;
+esac
+
+fi
+
+
+
+if test "$ac_cv_func_getopt" != yes; then
+ LIBSRCS="$LIBSRCS getopt.c"
+fi
+
+if test "$ac_cv_func_getpeereid" != yes; then
+ for ac_func in getpeerucred
+do :
+ as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+ if test "$ac_cv_func_getpeerucred" != yes ; then
+ ac_fn_c_check_member "$LINENO" "struct msghdr" "msg_accrightslen" "ac_cv_member_struct_msghdr_msg_accrightslen" "$ac_includes_default
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+"
+if test "x$ac_cv_member_struct_msghdr_msg_accrightslen" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTSLEN 1
+_ACEOF
+
+
+fi
+
+ if test "$ac_cv_member_struct_msghdr_msg_accrightslen" != yes; then
+ ac_fn_c_check_member "$LINENO" "struct msghdr" "msg_control" "ac_cv_member_struct_msghdr_msg_control" "$ac_includes_default
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+"
+if test "x$ac_cv_member_struct_msghdr_msg_control" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_MSGHDR_MSG_CONTROL 1
+_ACEOF
+
+
+fi
+
+ fi
+ ac_fn_c_check_member "$LINENO" "struct stat" "st_fstype" "ac_cv_member_struct_stat_st_fstype" "$ac_includes_default"
+if test "x$ac_cv_member_struct_stat_st_fstype" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_STAT_ST_FSTYPE 1
+_ACEOF
+
+
+fi
+ac_fn_c_check_member "$LINENO" "struct stat" "st_vfstype" "ac_cv_member_struct_stat_st_vfstype" "$ac_includes_default"
+if test "x$ac_cv_member_struct_stat_st_vfstype" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_STRUCT_STAT_ST_VFSTYPE 1
+_ACEOF
+
+
+fi
+
+ if test "$ac_cv_member_struct_stat_st_fstype" = yes; then
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+struct stat st; char *ptr=st.st_fstype;
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+$as_echo "#define HAVE_STRUCT_STAT_ST_FSTYPE_CHAR 1" >>confdefs.h
+
+else
+
+$as_echo "#define HAVE_STRUCT_STAT_ST_FSTYPE_INT 1" >>confdefs.h
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ fi
+ fi
+ LIBSRCS="$LIBSRCS getpeereid.c"
+fi
+
+if test "$ac_cv_func_snprintf" != yes ||
+ test "$ac_cv_func_vsnprintf" != yes; then
+ if test "$ac_cv_func_snprintf" != yes; then
+
+$as_echo "#define snprintf ber_pvt_snprintf" >>confdefs.h
+
+ fi
+ if test "$ac_cv_func_vsnprintf" != yes; then
+
+$as_echo "#define vsnprintf ber_pvt_vsnprintf" >>confdefs.h
+
+ fi
+fi
+
+
+if test "$ol_enable_slapi" != no ; then
+ for ac_header in ltdl.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "ltdl.h" "ac_cv_header_ltdl_h" "$ac_includes_default"
+if test "x$ac_cv_header_ltdl_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LTDL_H 1
+_ACEOF
+
+fi
+
+done
+
+
+ if test $ac_cv_header_ltdl_h != yes ; then
+ as_fn_error $? "could not locate <ltdl.h>" "$LINENO" 5
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lt_dlinit in -lltdl" >&5
+$as_echo_n "checking for lt_dlinit in -lltdl... " >&6; }
+if ${ac_cv_lib_ltdl_lt_dlinit+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lltdl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char lt_dlinit ();
+int
+main ()
+{
+return lt_dlinit ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_ltdl_lt_dlinit=yes
+else
+ ac_cv_lib_ltdl_lt_dlinit=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ltdl_lt_dlinit" >&5
+$as_echo "$ac_cv_lib_ltdl_lt_dlinit" >&6; }
+if test "x$ac_cv_lib_ltdl_lt_dlinit" = xyes; then :
+
+ SLAPI_LIBS=-lltdl
+ LIBSLAPI=slapi/libslapi.la
+
+$as_echo "#define HAVE_LIBLTDL 1" >>confdefs.h
+
+
+else
+ as_fn_error $? "could not locate libtool -lltdl" "$LINENO" 5
+fi
+
+
+
+$as_echo "#define LDAP_SLAPI 1" >>confdefs.h
+
+fi
+
+if test "$ol_enable_debug" != no ; then
+ if test "$ol_enable_debug" = traditional; then
+
+$as_echo "#define OLD_DEBUG 1" >>confdefs.h
+
+ fi
+
+$as_echo "#define LDAP_DEBUG 1" >>confdefs.h
+
+fi
+if test "$ol_enable_syslog" != no ; then
+
+$as_echo "#define LDAP_SYSLOG 1" >>confdefs.h
+
+fi
+if test "$ol_enable_referrals" != no ; then
+
+$as_echo "#define LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS LDAP_VENDOR_VERSION" >>confdefs.h
+
+fi
+if test "$ol_enable_local" != no; then
+
+$as_echo "#define LDAP_PF_LOCAL 1" >>confdefs.h
+
+fi
+if test "$ol_link_ipv6" != no; then
+
+$as_echo "#define LDAP_PF_INET6 1" >>confdefs.h
+
+fi
+if test "$ol_enable_cleartext" != no ; then
+
+$as_echo "#define SLAPD_CLEARTEXT 1" >>confdefs.h
+
+fi
+if test "$ol_enable_crypt" != no ; then
+
+$as_echo "#define SLAPD_CRYPT 1" >>confdefs.h
+
+fi
+if test "$ol_link_spasswd" != no ; then
+
+$as_echo "#define SLAPD_SPASSWD 1" >>confdefs.h
+
+fi
+if test "$ol_enable_rlookups" != no ; then
+
+$as_echo "#define SLAPD_RLOOKUPS 1" >>confdefs.h
+
+fi
+if test "$ol_enable_aci" != no ; then
+ if test "$ol_enable_aci" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ as_fn_error $? "ACI build as dynamic module not supported (yet)" "$LINENO" 5
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+ WITH_ACI_ENABLED=$ol_enable_aci
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_ACI_ENABLED $MFLAG
+_ACEOF
+
+else
+ WITH_ACI_ENABLED=no
+fi
+if test "$ol_enable_dynacl" != no ; then
+
+$as_echo "#define SLAP_DYNACL 1" >>confdefs.h
+
+fi
+
+if test "$ol_link_modules" != no ; then
+
+$as_echo "#define SLAPD_MODULES 1" >>confdefs.h
+
+ BUILD_SLAPD=yes
+ SLAPD_MODULES_LDFLAGS="-dlopen self"
+fi
+
+
+$as_echo "#define SLAPD_MOD_STATIC 1" >>confdefs.h
+
+
+$as_echo "#define SLAPD_MOD_DYNAMIC 2" >>confdefs.h
+
+
+if test "$ol_enable_dnssrv" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_DNSSRV=$ol_enable_dnssrv
+ if test "$ol_enable_dnssrv" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-dnssrv"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-dnssrv"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_DNSSRV $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_ldap" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_LDAP=$ol_enable_ldap
+ if test "$ol_enable_ldap" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-ldap"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-ldap"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_LDAP $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_mdb" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_MDB=$ol_enable_mdb
+ if test "$ol_enable_mdb" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-mdb"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-mdb"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_MDB $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_meta" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_META=$ol_enable_meta
+ if test "$ol_enable_meta" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-meta"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-meta"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_META $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_asyncmeta" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_ASYNCMETA=$ol_enable_asyncmeta
+ if test "$ol_enable_asyncmeta" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-asyncmeta"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-asyncmeta"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_ASYNCMETA $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_ndb" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_NDB=$ol_enable_ndb
+ if test "$ol_enable_ndb" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-ndb"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-ndb"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_NDB $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_null" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_NULL=$ol_enable_null
+ if test "$ol_enable_null" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-null"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-null"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_NULL $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_passwd" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_PASSWD=$ol_enable_passwd
+ if test "$ol_enable_passwd" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-passwd"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-passwd"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_PASSWD $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_link_perl" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_PERL=$ol_enable_perl
+ if test "$ol_enable_perl" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-perl"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-perl"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_PERL $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_relay" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_RELAY=$ol_enable_relay
+ if test "$ol_enable_relay" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-relay"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-relay"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_RELAY $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_sock" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_SOCK=$ol_enable_sock
+ if test "$ol_enable_sock" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-sock"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-sock"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_SOCK $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_link_sql" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_SQL=$ol_enable_sql
+ if test "$ol_enable_sql" = mod; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-sql"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-sql"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_SQL $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_link_wt" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_WT=$ol_enable_wt
+ if test "$ol_enable_wt" = mod; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-wt"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-wt"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_WT $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_accesslog" != no ; then
+ BUILD_ACCESSLOG=$ol_enable_accesslog
+ if test "$ol_enable_accesslog" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS accesslog.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS accesslog.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_ACCESSLOG $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_auditlog" != no ; then
+ BUILD_AUDITLOG=$ol_enable_auditlog
+ if test "$ol_enable_auditlog" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS auditlog.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS auditlog.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_AUDITLOG $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_autoca" != no ; then
+ if test $ol_with_tls != openssl ; then
+ as_fn_error $? "--enable-autoca=$ol_enable_autoca requires --with-tls=openssl" "$LINENO" 5
+ fi
+
+ BUILD_AUTOCA=$ol_enable_autoca
+ if test "$ol_enable_autoca" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS autoca.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS autoca.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_AUTOCA $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_collect" != no ; then
+ BUILD_COLLECT=$ol_enable_collect
+ if test "$ol_enable_collect" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS collect.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS collect.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_COLLECT $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_constraint" != no ; then
+ BUILD_CONSTRAINT=$ol_enable_constraint
+ if test "$ol_enable_constraint" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS constraint.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS constraint.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_CONSTRAINT $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_dds" != no ; then
+ BUILD_DDS=$ol_enable_dds
+ if test "$ol_enable_dds" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS dds.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS dds.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_DDS $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_deref" != no ; then
+ BUILD_DEREF=$ol_enable_deref
+ if test "$ol_enable_deref" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS deref.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS deref.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_DEREF $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_dyngroup" != no ; then
+ BUILD_DYNGROUP=$ol_enable_dyngroup
+ if test "$ol_enable_dyngroup" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS dyngroup.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS dyngroup.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_DYNGROUP $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_dynlist" != no ; then
+ BUILD_DYNLIST=$ol_enable_dynlist
+ if test "$ol_enable_dynlist" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS dynlist.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS dynlist.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_DYNLIST $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_homedir" != no ; then
+ BUILD_HOMEDIR=$ol_enable_homedir
+ if test "$ol_enable_homedir" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS homedir.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS homedir.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_HOMEDIR $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_memberof" != no ; then
+ BUILD_MEMBEROF=$ol_enable_memberof
+ if test "$ol_enable_memberof" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS memberof.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS memberof.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_MEMBEROF $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_otp" != no ; then
+ if test $ol_with_tls = no ; then
+ as_fn_error $? "--enable-otp=$ol_enable_otp requires --with-tls" "$LINENO" 5
+ fi
+
+ BUILD_OTP=$ol_enable_otp
+ if test "$ol_enable_otp" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS otp.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS otp.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_OTP $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_ppolicy" != no ; then
+ BUILD_PPOLICY=$ol_enable_ppolicy
+ if test "$ol_enable_ppolicy" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS ppolicy.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS ppolicy.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_PPOLICY $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_proxycache" != no ; then
+ BUILD_PROXYCACHE=$ol_enable_proxycache
+ if test "$ol_enable_proxycache" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS pcache.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS pcache.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_PROXYCACHE $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_refint" != no ; then
+ BUILD_REFINT=$ol_enable_refint
+ if test "$ol_enable_refint" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS refint.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS refint.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_REFINT $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_remoteauth" != no ; then
+ BUILD_REMOTEAUTH=$ol_enable_remoteauth
+ if test "$ol_enable_remoteauth" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS remoteauth.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS remoteauth.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_REMOTEAUTH $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_retcode" != no ; then
+ BUILD_RETCODE=$ol_enable_retcode
+ if test "$ol_enable_retcode" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS retcode.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS retcode.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_RETCODE $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_rwm" != no ; then
+ BUILD_RWM=$ol_enable_rwm
+ if test "$ol_enable_rwm" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS rwm.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS rwm_x.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_RWM $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_seqmod" != no ; then
+ BUILD_SEQMOD=$ol_enable_seqmod
+ if test "$ol_enable_seqmod" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS seqmod.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS seqmod.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_SEQMOD $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_sssvlv" != no ; then
+ BUILD_SSSVLV=$ol_enable_sssvlv
+ if test "$ol_enable_sssvlv" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS sssvlv.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS sssvlv.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_SSSVLV $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_syncprov" != no ; then
+ BUILD_SYNCPROV=$ol_enable_syncprov
+ if test "$ol_enable_syncprov" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS syncprov.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS syncprov.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_SYNCPROV $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_translucent" != no ; then
+ BUILD_TRANSLUCENT=$ol_enable_translucent
+ if test "$ol_enable_translucent" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS translucent.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS translucent.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_TRANSLUCENT $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_unique" != no ; then
+ BUILD_UNIQUE=$ol_enable_unique
+ if test "$ol_enable_unique" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS unique.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS unique.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_UNIQUE $MFLAG
+_ACEOF
+
+fi
+
+if test "$ol_enable_valsort" != no ; then
+ BUILD_VALSORT=$ol_enable_valsort
+ if test "$ol_enable_valsort" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS valsort.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS valsort.o"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_OVER_VALSORT $MFLAG
+_ACEOF
+
+fi
+
+ol_link_argon2=no
+if test "$ol_enable_argon2" = "yes" ; then
+ if test $ol_with_argon2 = libargon2 || test $ol_with_argon2 = auto; then
+ for ac_header in argon2.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "argon2.h" "ac_cv_header_argon2_h" "$ac_includes_default"
+if test "x$ac_cv_header_argon2_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_ARGON2_H 1
+_ACEOF
+
+fi
+
+done
+
+ if test $ac_cv_header_argon2_h = yes ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for argon2i_hash_encoded in -largon2" >&5
+$as_echo_n "checking for argon2i_hash_encoded in -largon2... " >&6; }
+if ${ac_cv_lib_argon2_argon2i_hash_encoded+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-largon2 -largon2 $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char argon2i_hash_encoded ();
+int
+main ()
+{
+return argon2i_hash_encoded ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_argon2_argon2i_hash_encoded=yes
+else
+ ac_cv_lib_argon2_argon2i_hash_encoded=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_argon2_argon2i_hash_encoded" >&5
+$as_echo "$ac_cv_lib_argon2_argon2i_hash_encoded" >&6; }
+if test "x$ac_cv_lib_argon2_argon2i_hash_encoded" = xyes; then :
+ have_argon2=yes
+else
+ have_argon2=no
+fi
+
+ fi
+ if test "$have_argon2" = "yes" ; then
+ ol_with_argon2=libargon2
+ ol_link_argon2=yes
+
+$as_echo "#define HAVE_LIBARGON2 1" >>confdefs.h
+
+ ARGON2_LIBS="-largon2"
+ fi
+ fi
+ if test $ol_with_argon2 = libsodium || test $ol_with_argon2 = auto; then
+ for ac_header in sodium.h
+do :
+ ac_fn_c_check_header_mongrel "$LINENO" "sodium.h" "ac_cv_header_sodium_h" "$ac_includes_default"
+if test "x$ac_cv_header_sodium_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_SODIUM_H 1
+_ACEOF
+
+fi
+
+done
+
+ if test $ac_cv_header_sodium_h = yes ; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for crypto_pwhash_str_alg in -lsodium" >&5
+$as_echo_n "checking for crypto_pwhash_str_alg in -lsodium... " >&6; }
+if ${ac_cv_lib_sodium_crypto_pwhash_str_alg+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsodium -lsodium $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char crypto_pwhash_str_alg ();
+int
+main ()
+{
+return crypto_pwhash_str_alg ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_sodium_crypto_pwhash_str_alg=yes
+else
+ ac_cv_lib_sodium_crypto_pwhash_str_alg=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_sodium_crypto_pwhash_str_alg" >&5
+$as_echo "$ac_cv_lib_sodium_crypto_pwhash_str_alg" >&6; }
+if test "x$ac_cv_lib_sodium_crypto_pwhash_str_alg" = xyes; then :
+ have_argon2=yes
+else
+ have_argon2=no
+fi
+
+ fi
+ if test "$have_argon2" = "yes" ; then
+ ol_with_argon2=libsodium
+ ol_link_argon2=yes
+
+$as_echo "#define HAVE_LIBSODIUM 1" >>confdefs.h
+
+ ARGON2_LIBS="-lsodium"
+ fi
+ fi
+
+ if test "$ol_link_argon2" = no ; then
+ as_fn_error $? "--enable_argon2=$ol_enable_argon2 requires --with-argon2" "$LINENO" 5
+ fi
+
+ BUILD_PW_ARGON2=$ol_enable_argon2
+ if test "$ol_enable_argon2" = "yes" ; then
+ SLAPD_DYNAMIC_PWMODS="$SLAPD_DYNAMIC_PWDMODS argon2.la"
+ fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_PWMOD_PW_ARGON2 $SLAPD_MOD_DYNAMIC
+_ACEOF
+
+fi
+
+if test "$ol_enable_balancer" != no \
+ -a "$ol_with_threads" != no \
+ -a "$have_libevent" = yes ; then
+ if test "$ol_enable_balancer" = mod; then
+ BALANCER_INCLUDE=Makefile.module
+ BUILD_BALANCER=mod
+ else
+ BALANCER_INCLUDE=Makefile.server
+ BUILD_BALANCER=yes
+ fi
+fi
+
+if test "$ol_enable_slapi" != no ; then
+
+$as_echo "#define ENABLE_SLAPI 1" >>confdefs.h
+
+ BUILD_SLAPI=yes
+ SLAPD_SLAPI_DEPEND=libslapi.a
+fi
+
+OL_VERSIONED_SYMBOLS=""
+if test $ol_enable_versioning != no; then
+ LDVS=`$LD --help < /dev/null 2>/dev/null | grep gnu-version-script`
+ if test -z "$LDVS"; then
+ LDVS=`$LD --help < /dev/null 2>/dev/null | grep version-script`
+ if test -z "$LDVS"; then
+ if test $ol_enable_versioning = "yes" ; then
+ as_fn_error $? "Library symbol versioning requested but not supported" "$LINENO" 5
+ fi
+ else
+ OL_VERSIONED_SYMBOLS="-Wl,--version-script="
+ fi
+ else
+ OL_VERSIONED_SYMBOLS="-z gnu-version-script="
+ fi
+fi
+
+
+
+if test "$ac_cv_mingw32" = yes -o $ol_cv_msvc = yes ; then
+ PLAT=NT
+ SLAPD_MODULES_LDFLAGS=
+else
+ PLAT=UNIX
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-xxinstall was given.
+if test "${with_xxinstall+set}" = set; then :
+ withval=$with_xxinstall;
+fi
+
+
+
+ac_config_files="$ac_config_files Makefile:build/top.mk:Makefile.in:build/dir.mk doc/Makefile:build/top.mk:doc/Makefile.in:build/dir.mk doc/man/Makefile:build/top.mk:doc/man/Makefile.in:build/dir.mk doc/man/man1/Makefile:build/top.mk:doc/man/man1/Makefile.in:build/man.mk doc/man/man3/Makefile:build/top.mk:doc/man/man3/Makefile.in:build/man.mk doc/man/man5/Makefile:build/top.mk:doc/man/man5/Makefile.in:build/man.mk doc/man/man8/Makefile:build/top.mk:doc/man/man8/Makefile.in:build/man.mk clients/Makefile:build/top.mk:clients/Makefile.in:build/dir.mk clients/tools/Makefile:build/top.mk:clients/tools/Makefile.in:build/rules.mk include/Makefile:build/top.mk:include/Makefile.in libraries/Makefile:build/top.mk:libraries/Makefile.in:build/dir.mk libraries/liblber/Makefile:build/top.mk:libraries/liblber/Makefile.in:build/lib.mk:build/lib-shared.mk libraries/liblber/lber.pc libraries/liblber/liblber.vers libraries/libldap/Makefile:build/top.mk:libraries/libldap/Makefile.in:build/lib.mk:build/lib-shared.mk libraries/libldap/ldap.pc libraries/libldap/libldap.vers libraries/liblunicode/Makefile:build/top.mk:libraries/liblunicode/Makefile.in:build/lib.mk:build/lib-static.mk libraries/liblutil/Makefile:build/top.mk:libraries/liblutil/Makefile.in:build/lib.mk:build/lib-static.mk libraries/librewrite/Makefile:build/top.mk:libraries/librewrite/Makefile.in:build/lib.mk:build/lib-static.mk servers/Makefile:build/top.mk:servers/Makefile.in:build/dir.mk servers/slapd/Makefile:build/top.mk:servers/slapd/Makefile.in:build/srv.mk servers/slapd/back-dnssrv/Makefile:build/top.mk:servers/slapd/back-dnssrv/Makefile.in:build/mod.mk servers/slapd/back-ldap/Makefile:build/top.mk:servers/slapd/back-ldap/Makefile.in:build/mod.mk servers/slapd/back-ldif/Makefile:build/top.mk:servers/slapd/back-ldif/Makefile.in:build/mod.mk servers/slapd/back-mdb/Makefile:build/top.mk:servers/slapd/back-mdb/Makefile.in:build/mod.mk servers/slapd/back-meta/Makefile:build/top.mk:servers/slapd/back-meta/Makefile.in:build/mod.mk servers/slapd/back-asyncmeta/Makefile:build/top.mk:servers/slapd/back-asyncmeta/Makefile.in:build/mod.mk servers/slapd/back-monitor/Makefile:build/top.mk:servers/slapd/back-monitor/Makefile.in:build/mod.mk servers/slapd/back-ndb/Makefile:build/top.mk:servers/slapd/back-ndb/Makefile.in:build/mod.mk servers/slapd/back-null/Makefile:build/top.mk:servers/slapd/back-null/Makefile.in:build/mod.mk servers/slapd/back-passwd/Makefile:build/top.mk:servers/slapd/back-passwd/Makefile.in:build/mod.mk servers/slapd/back-perl/Makefile:build/top.mk:servers/slapd/back-perl/Makefile.in:build/mod.mk servers/slapd/back-relay/Makefile:build/top.mk:servers/slapd/back-relay/Makefile.in:build/mod.mk servers/slapd/back-sock/Makefile:build/top.mk:servers/slapd/back-sock/Makefile.in:build/mod.mk servers/slapd/back-sql/Makefile:build/top.mk:servers/slapd/back-sql/Makefile.in:build/mod.mk servers/slapd/back-wt/Makefile:build/top.mk:servers/slapd/back-wt/Makefile.in:build/mod.mk servers/slapd/slapi/Makefile:build/top.mk:servers/slapd/slapi/Makefile.in:build/lib.mk:build/lib-shared.mk servers/slapd/overlays/Makefile:build/top.mk:servers/slapd/overlays/Makefile.in:build/lib.mk servers/slapd/pwmods/Makefile:build/top.mk:servers/slapd/pwmods/Makefile.in:build/lib.mk servers/lloadd/Makefile:build/top.mk:servers/lloadd/Makefile.in servers/lloadd/Makefile.server:servers/lloadd/Makefile_server.in:build/srv.mk servers/lloadd/Makefile.module:servers/lloadd/Makefile_module.in:build/mod.mk tests/Makefile:build/top.mk:tests/Makefile.in:build/dir.mk tests/run tests/progs/Makefile:build/top.mk:tests/progs/Makefile.in:build/rules.mk"
+
+
+ac_config_commands="$ac_config_commands default"
+
+
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+ ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+ # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
+ # will be set to the directory where LIBOBJS objects are built.
+ as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+ as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in #(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -pR'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -pR'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -pR'
+ fi
+else
+ as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by $as_me, which was
+generated by GNU Autoconf 2.69. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+config_commands="$ac_config_commands"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration. Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number and configuration settings, then exit
+ --config print configuration, then exit
+ -q, --quiet, --silent
+ do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+ --header=FILE[:TEMPLATE]
+ instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to the package provider."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+config.status
+configured by $0, generated by GNU Autoconf 2.69,
+ with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+AWK='$AWK'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=?*)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ --*=)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=
+ ac_shift=:
+ ;;
+ *)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+ $as_echo "$ac_cs_version"; exit ;;
+ --config | --confi | --conf | --con | --co | --c )
+ $as_echo "$ac_cs_config"; exit ;;
+ --debug | --debu | --deb | --de | --d | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ '') as_fn_error $? "missing file argument" ;;
+ esac
+ as_fn_append CONFIG_FILES " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --he | --h)
+ # Conflict between --help and --header
+ as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+ --help | --hel | -h )
+ $as_echo "$ac_cs_usage"; exit ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+ *) as_fn_append ac_config_targets " $1"
+ ac_need_defaults=false ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+ set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+ shift
+ \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+ CONFIG_SHELL='$SHELL'
+ export CONFIG_SHELL
+ exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+ $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#
+# INIT-COMMANDS
+#
+
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`'
+enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`'
+AS='`$ECHO "$AS" | $SED "$delay_single_quote_subst"`'
+DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`'
+OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`'
+macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`'
+macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`'
+pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`'
+enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`'
+shared_archive_member_spec='`$ECHO "$shared_archive_member_spec" | $SED "$delay_single_quote_subst"`'
+SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`'
+ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`'
+PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`'
+host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`'
+host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`'
+host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`'
+build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`'
+build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`'
+build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`'
+SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`'
+Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`'
+GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`'
+EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`'
+FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`'
+LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`'
+NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`'
+LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`'
+max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`'
+ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`'
+exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`'
+lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`'
+lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`'
+lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`'
+lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`'
+lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`'
+reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`'
+reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`'
+deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`'
+file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`'
+file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`'
+want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`'
+sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`'
+AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`'
+AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`'
+archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`'
+STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`'
+RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`'
+old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`'
+old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`'
+old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`'
+lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`'
+CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`'
+CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`'
+compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`'
+GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_import='`$ECHO "$lt_cv_sys_global_symbol_to_import" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`'
+lt_cv_nm_interface='`$ECHO "$lt_cv_nm_interface" | $SED "$delay_single_quote_subst"`'
+nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`'
+lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`'
+lt_cv_truncate_bin='`$ECHO "$lt_cv_truncate_bin" | $SED "$delay_single_quote_subst"`'
+objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`'
+MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`'
+lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`'
+need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`'
+MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`'
+DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`'
+NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`'
+LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`'
+OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`'
+OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`'
+libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`'
+shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`'
+extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`'
+archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`'
+enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`'
+export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`'
+whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`'
+compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`'
+old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`'
+old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`'
+archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`'
+archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`'
+module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`'
+module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`'
+with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`'
+allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`'
+no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`'
+hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`'
+hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`'
+hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`'
+hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`'
+hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`'
+hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`'
+inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`'
+link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`'
+always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`'
+export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`'
+exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`'
+include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`'
+prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`'
+postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`'
+file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`'
+variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`'
+need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`'
+need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`'
+version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`'
+runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`'
+shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`'
+shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`'
+libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`'
+library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`'
+soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`'
+install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`'
+postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`'
+postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`'
+finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`'
+finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`'
+hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`'
+sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`'
+configure_time_dlsearch_path='`$ECHO "$configure_time_dlsearch_path" | $SED "$delay_single_quote_subst"`'
+configure_time_lt_sys_library_path='`$ECHO "$configure_time_lt_sys_library_path" | $SED "$delay_single_quote_subst"`'
+hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`'
+enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`'
+enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`'
+enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`'
+old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`'
+striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`'
+
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+\$1
+_LTECHO_EOF'
+}
+
+# Quote evaled strings.
+for var in AS \
+DLLTOOL \
+OBJDUMP \
+SHELL \
+ECHO \
+PATH_SEPARATOR \
+SED \
+GREP \
+EGREP \
+FGREP \
+LD \
+NM \
+LN_S \
+lt_SP2NL \
+lt_NL2SP \
+reload_flag \
+deplibs_check_method \
+file_magic_cmd \
+file_magic_glob \
+want_nocaseglob \
+sharedlib_from_linklib_cmd \
+AR \
+AR_FLAGS \
+archiver_list_spec \
+STRIP \
+RANLIB \
+CC \
+CFLAGS \
+compiler \
+lt_cv_sys_global_symbol_pipe \
+lt_cv_sys_global_symbol_to_cdecl \
+lt_cv_sys_global_symbol_to_import \
+lt_cv_sys_global_symbol_to_c_name_address \
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \
+lt_cv_nm_interface \
+nm_file_list_spec \
+lt_cv_truncate_bin \
+lt_prog_compiler_no_builtin_flag \
+lt_prog_compiler_pic \
+lt_prog_compiler_wl \
+lt_prog_compiler_static \
+lt_cv_prog_compiler_c_o \
+need_locks \
+MANIFEST_TOOL \
+DSYMUTIL \
+NMEDIT \
+LIPO \
+OTOOL \
+OTOOL64 \
+shrext_cmds \
+export_dynamic_flag_spec \
+whole_archive_flag_spec \
+compiler_needs_object \
+with_gnu_ld \
+allow_undefined_flag \
+no_undefined_flag \
+hardcode_libdir_flag_spec \
+hardcode_libdir_separator \
+exclude_expsyms \
+include_expsyms \
+file_list_spec \
+variables_saved_for_relink \
+libname_spec \
+library_names_spec \
+soname_spec \
+install_override_mode \
+finish_eval \
+old_striplib \
+striplib; do
+ case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+ *[\\\\\\\`\\"\\\$]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Double-quote double-evaled strings.
+for var in reload_cmds \
+old_postinstall_cmds \
+old_postuninstall_cmds \
+old_archive_cmds \
+extract_expsyms_cmds \
+old_archive_from_new_cmds \
+old_archive_from_expsyms_cmds \
+archive_cmds \
+archive_expsym_cmds \
+module_cmds \
+module_expsym_cmds \
+export_symbols_cmds \
+prelink_cmds \
+postlink_cmds \
+postinstall_cmds \
+postuninstall_cmds \
+finish_cmds \
+sys_lib_search_path_spec \
+configure_time_dlsearch_path \
+configure_time_lt_sys_library_path; do
+ case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+ *[\\\\\\\`\\"\\\$]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+ac_aux_dir='$ac_aux_dir'
+
+# See if we are running on zsh, and set the options that allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}"; then
+ setopt NO_GLOB_SUBST
+fi
+
+
+ PACKAGE='$PACKAGE'
+ VERSION='$VERSION'
+ RM='$RM'
+ ofile='$ofile'
+
+
+
+
+STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS"
+STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS"
+
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+ case $ac_config_target in
+ "include/portable.h") CONFIG_HEADERS="$CONFIG_HEADERS include/portable.h:include/portable.hin" ;;
+ "include/ldap_features.h") CONFIG_HEADERS="$CONFIG_HEADERS include/ldap_features.h:include/ldap_features.hin" ;;
+ "include/lber_types.h") CONFIG_HEADERS="$CONFIG_HEADERS include/lber_types.h:include/lber_types.hin" ;;
+ "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
+ "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile:build/top.mk:Makefile.in:build/dir.mk" ;;
+ "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile:build/top.mk:doc/Makefile.in:build/dir.mk" ;;
+ "doc/man/Makefile") CONFIG_FILES="$CONFIG_FILES doc/man/Makefile:build/top.mk:doc/man/Makefile.in:build/dir.mk" ;;
+ "doc/man/man1/Makefile") CONFIG_FILES="$CONFIG_FILES doc/man/man1/Makefile:build/top.mk:doc/man/man1/Makefile.in:build/man.mk" ;;
+ "doc/man/man3/Makefile") CONFIG_FILES="$CONFIG_FILES doc/man/man3/Makefile:build/top.mk:doc/man/man3/Makefile.in:build/man.mk" ;;
+ "doc/man/man5/Makefile") CONFIG_FILES="$CONFIG_FILES doc/man/man5/Makefile:build/top.mk:doc/man/man5/Makefile.in:build/man.mk" ;;
+ "doc/man/man8/Makefile") CONFIG_FILES="$CONFIG_FILES doc/man/man8/Makefile:build/top.mk:doc/man/man8/Makefile.in:build/man.mk" ;;
+ "clients/Makefile") CONFIG_FILES="$CONFIG_FILES clients/Makefile:build/top.mk:clients/Makefile.in:build/dir.mk" ;;
+ "clients/tools/Makefile") CONFIG_FILES="$CONFIG_FILES clients/tools/Makefile:build/top.mk:clients/tools/Makefile.in:build/rules.mk" ;;
+ "include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile:build/top.mk:include/Makefile.in" ;;
+ "libraries/Makefile") CONFIG_FILES="$CONFIG_FILES libraries/Makefile:build/top.mk:libraries/Makefile.in:build/dir.mk" ;;
+ "libraries/liblber/Makefile") CONFIG_FILES="$CONFIG_FILES libraries/liblber/Makefile:build/top.mk:libraries/liblber/Makefile.in:build/lib.mk:build/lib-shared.mk" ;;
+ "libraries/liblber/lber.pc") CONFIG_FILES="$CONFIG_FILES libraries/liblber/lber.pc" ;;
+ "libraries/liblber/liblber.vers") CONFIG_FILES="$CONFIG_FILES libraries/liblber/liblber.vers" ;;
+ "libraries/libldap/Makefile") CONFIG_FILES="$CONFIG_FILES libraries/libldap/Makefile:build/top.mk:libraries/libldap/Makefile.in:build/lib.mk:build/lib-shared.mk" ;;
+ "libraries/libldap/ldap.pc") CONFIG_FILES="$CONFIG_FILES libraries/libldap/ldap.pc" ;;
+ "libraries/libldap/libldap.vers") CONFIG_FILES="$CONFIG_FILES libraries/libldap/libldap.vers" ;;
+ "libraries/liblunicode/Makefile") CONFIG_FILES="$CONFIG_FILES libraries/liblunicode/Makefile:build/top.mk:libraries/liblunicode/Makefile.in:build/lib.mk:build/lib-static.mk" ;;
+ "libraries/liblutil/Makefile") CONFIG_FILES="$CONFIG_FILES libraries/liblutil/Makefile:build/top.mk:libraries/liblutil/Makefile.in:build/lib.mk:build/lib-static.mk" ;;
+ "libraries/librewrite/Makefile") CONFIG_FILES="$CONFIG_FILES libraries/librewrite/Makefile:build/top.mk:libraries/librewrite/Makefile.in:build/lib.mk:build/lib-static.mk" ;;
+ "servers/Makefile") CONFIG_FILES="$CONFIG_FILES servers/Makefile:build/top.mk:servers/Makefile.in:build/dir.mk" ;;
+ "servers/slapd/Makefile") CONFIG_FILES="$CONFIG_FILES servers/slapd/Makefile:build/top.mk:servers/slapd/Makefile.in:build/srv.mk" ;;
+ "servers/slapd/back-dnssrv/Makefile") CONFIG_FILES="$CONFIG_FILES servers/slapd/back-dnssrv/Makefile:build/top.mk:servers/slapd/back-dnssrv/Makefile.in:build/mod.mk" ;;
+ "servers/slapd/back-ldap/Makefile") CONFIG_FILES="$CONFIG_FILES servers/slapd/back-ldap/Makefile:build/top.mk:servers/slapd/back-ldap/Makefile.in:build/mod.mk" ;;
+ "servers/slapd/back-ldif/Makefile") CONFIG_FILES="$CONFIG_FILES servers/slapd/back-ldif/Makefile:build/top.mk:servers/slapd/back-ldif/Makefile.in:build/mod.mk" ;;
+ "servers/slapd/back-mdb/Makefile") CONFIG_FILES="$CONFIG_FILES servers/slapd/back-mdb/Makefile:build/top.mk:servers/slapd/back-mdb/Makefile.in:build/mod.mk" ;;
+ "servers/slapd/back-meta/Makefile") CONFIG_FILES="$CONFIG_FILES servers/slapd/back-meta/Makefile:build/top.mk:servers/slapd/back-meta/Makefile.in:build/mod.mk" ;;
+ "servers/slapd/back-asyncmeta/Makefile") CONFIG_FILES="$CONFIG_FILES servers/slapd/back-asyncmeta/Makefile:build/top.mk:servers/slapd/back-asyncmeta/Makefile.in:build/mod.mk" ;;
+ "servers/slapd/back-monitor/Makefile") CONFIG_FILES="$CONFIG_FILES servers/slapd/back-monitor/Makefile:build/top.mk:servers/slapd/back-monitor/Makefile.in:build/mod.mk" ;;
+ "servers/slapd/back-ndb/Makefile") CONFIG_FILES="$CONFIG_FILES servers/slapd/back-ndb/Makefile:build/top.mk:servers/slapd/back-ndb/Makefile.in:build/mod.mk" ;;
+ "servers/slapd/back-null/Makefile") CONFIG_FILES="$CONFIG_FILES servers/slapd/back-null/Makefile:build/top.mk:servers/slapd/back-null/Makefile.in:build/mod.mk" ;;
+ "servers/slapd/back-passwd/Makefile") CONFIG_FILES="$CONFIG_FILES servers/slapd/back-passwd/Makefile:build/top.mk:servers/slapd/back-passwd/Makefile.in:build/mod.mk" ;;
+ "servers/slapd/back-perl/Makefile") CONFIG_FILES="$CONFIG_FILES servers/slapd/back-perl/Makefile:build/top.mk:servers/slapd/back-perl/Makefile.in:build/mod.mk" ;;
+ "servers/slapd/back-relay/Makefile") CONFIG_FILES="$CONFIG_FILES servers/slapd/back-relay/Makefile:build/top.mk:servers/slapd/back-relay/Makefile.in:build/mod.mk" ;;
+ "servers/slapd/back-sock/Makefile") CONFIG_FILES="$CONFIG_FILES servers/slapd/back-sock/Makefile:build/top.mk:servers/slapd/back-sock/Makefile.in:build/mod.mk" ;;
+ "servers/slapd/back-sql/Makefile") CONFIG_FILES="$CONFIG_FILES servers/slapd/back-sql/Makefile:build/top.mk:servers/slapd/back-sql/Makefile.in:build/mod.mk" ;;
+ "servers/slapd/back-wt/Makefile") CONFIG_FILES="$CONFIG_FILES servers/slapd/back-wt/Makefile:build/top.mk:servers/slapd/back-wt/Makefile.in:build/mod.mk" ;;
+ "servers/slapd/slapi/Makefile") CONFIG_FILES="$CONFIG_FILES servers/slapd/slapi/Makefile:build/top.mk:servers/slapd/slapi/Makefile.in:build/lib.mk:build/lib-shared.mk" ;;
+ "servers/slapd/overlays/Makefile") CONFIG_FILES="$CONFIG_FILES servers/slapd/overlays/Makefile:build/top.mk:servers/slapd/overlays/Makefile.in:build/lib.mk" ;;
+ "servers/slapd/pwmods/Makefile") CONFIG_FILES="$CONFIG_FILES servers/slapd/pwmods/Makefile:build/top.mk:servers/slapd/pwmods/Makefile.in:build/lib.mk" ;;
+ "servers/lloadd/Makefile") CONFIG_FILES="$CONFIG_FILES servers/lloadd/Makefile:build/top.mk:servers/lloadd/Makefile.in" ;;
+ "servers/lloadd/Makefile.server") CONFIG_FILES="$CONFIG_FILES servers/lloadd/Makefile.server:servers/lloadd/Makefile_server.in:build/srv.mk" ;;
+ "servers/lloadd/Makefile.module") CONFIG_FILES="$CONFIG_FILES servers/lloadd/Makefile.module:servers/lloadd/Makefile_module.in:build/mod.mk" ;;
+ "tests/Makefile") CONFIG_FILES="$CONFIG_FILES tests/Makefile:build/top.mk:tests/Makefile.in:build/dir.mk" ;;
+ "tests/run") CONFIG_FILES="$CONFIG_FILES tests/run" ;;
+ "tests/progs/Makefile") CONFIG_FILES="$CONFIG_FILES tests/progs/Makefile:build/top.mk:tests/progs/Makefile.in:build/rules.mk" ;;
+ "default") CONFIG_COMMANDS="$CONFIG_COMMANDS default" ;;
+
+ *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+ esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+ test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+ test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+ tmp= ac_tmp=
+ trap 'exit_status=$?
+ : "${ac_tmp:=$tmp}"
+ { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+ trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+ test -d "$tmp"
+} ||
+{
+ tmp=./conf$$-$RANDOM
+ (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+ eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+ ac_cs_awk_cr='\\r'
+else
+ ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+ echo "cat >conf$$subs.awk <<_ACEOF" &&
+ echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+ echo "_ACEOF"
+} >conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ . ./conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+ ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+ if test $ac_delim_n = $ac_delim_num; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+ N
+ s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+ for (key in S) S_is_set[key] = 1
+ FS = ""
+
+}
+{
+ line = $ 0
+ nfields = split(line, field, "@")
+ substed = 0
+ len = length(field[1])
+ for (i = 2; i < nfields; i++) {
+ key = field[i]
+ keylen = length(key)
+ if (S_is_set[key]) {
+ value = S[key]
+ line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+ len += length(value) + length(field[++i])
+ substed = 1
+ } else
+ len += 1 + keylen
+ }
+
+ print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+ sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+ cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{
+h
+s///
+s/^/:/
+s/[ ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[ ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[ ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+ ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+ if test -z "$ac_tt"; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any. Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[ ]*#[ ]*define[ ][ ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ for (key in D) D_is_set[key] = 1
+ FS = ""
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+ line = \$ 0
+ split(line, arg, " ")
+ if (arg[1] == "#") {
+ defundef = arg[2]
+ mac1 = arg[3]
+ } else {
+ defundef = substr(arg[1], 2)
+ mac1 = arg[2]
+ }
+ split(mac1, mac2, "(") #)
+ macro = mac2[1]
+ prefix = substr(line, 1, index(line, defundef) - 1)
+ if (D_is_set[macro]) {
+ # Preserve the white space surrounding the "#".
+ print prefix "define", macro P[macro] D[macro]
+ next
+ } else {
+ # Replace #undef with comments. This is necessary, for example,
+ # in the case of _POSIX_SOURCE, which is predefined and required
+ # on some systems where configure will not decide to define it.
+ if (defundef == "undef") {
+ print "/*", prefix defundef, macro, "*/"
+ next
+ }
+ }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS"
+shift
+for ac_tag
+do
+ case $ac_tag in
+ :[FHLC]) ac_mode=$ac_tag; continue;;
+ esac
+ case $ac_mode$ac_tag in
+ :[FHL]*:*);;
+ :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+ :[FH]-) ac_tag=-:-;;
+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+ esac
+ ac_save_IFS=$IFS
+ IFS=:
+ set x $ac_tag
+ IFS=$ac_save_IFS
+ shift
+ ac_file=$1
+ shift
+
+ case $ac_mode in
+ :L) ac_source=$1;;
+ :[FH])
+ ac_file_inputs=
+ for ac_f
+ do
+ case $ac_f in
+ -) ac_f="$ac_tmp/stdin";;
+ *) # Look for the file first in the build tree, then in the source tree
+ # (if the path is not absolute). The absolute path cannot be DOS-style,
+ # because $ac_f cannot contain `:'.
+ test -f "$ac_f" ||
+ case $ac_f in
+ [\\/$]*) false;;
+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+ esac ||
+ as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+ esac
+ case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+ as_fn_append ac_file_inputs " '$ac_f'"
+ done
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ configure_input='Generated from '`
+ $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+ `' by configure.'
+ if test x"$ac_file" != x-; then
+ configure_input="$ac_file. $configure_input"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+ fi
+ # Neutralize special characters interpreted by sed in replacement strings.
+ case $configure_input in #(
+ *\&* | *\|* | *\\* )
+ ac_sed_conf_input=`$as_echo "$configure_input" |
+ sed 's/[\\\\&|]/\\\\&/g'`;; #(
+ *) ac_sed_conf_input=$configure_input;;
+ esac
+
+ case $ac_tag in
+ *:-:* | *:-) cat >"$ac_tmp/stdin" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+ esac
+ ;;
+ esac
+
+ ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ as_dir="$ac_dir"; as_fn_mkdir_p
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+ case $ac_mode in
+ :F)
+ #
+ # CONFIG_FILE
+ #
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+ p
+ q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ ac_datarootdir_hack='
+ s&@datadir@&$datadir&g
+ s&@docdir@&$docdir&g
+ s&@infodir@&$infodir&g
+ s&@localedir@&$localedir&g
+ s&@mandir@&$mandir&g
+ s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \
+ "$ac_tmp/out"`; test -z "$ac_out"; } &&
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&2;}
+
+ rm -f "$ac_tmp/stdin"
+ case $ac_file in
+ -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+ *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+ esac \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+ :H)
+ #
+ # CONFIG_HEADER
+ #
+ if test x"$ac_file" != x-; then
+ {
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+ } >"$ac_tmp/config.h" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+ else
+ rm -f "$ac_file"
+ mv "$ac_tmp/config.h" "$ac_file" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ fi
+ else
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+ || as_fn_error $? "could not create -" "$LINENO" 5
+ fi
+ ;;
+
+ :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
+$as_echo "$as_me: executing $ac_file commands" >&6;}
+ ;;
+ esac
+
+
+ case $ac_file$ac_mode in
+ "libtool":C)
+
+ # See if we are running on zsh, and set the options that allow our
+ # commands through without removal of \ escapes.
+ if test -n "${ZSH_VERSION+set}"; then
+ setopt NO_GLOB_SUBST
+ fi
+
+ cfgfile=${ofile}T
+ trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+ $RM "$cfgfile"
+
+ cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+# Generated automatically by $as_me ($PACKAGE) $VERSION
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+
+# Provide generalized library-building support services.
+# Written by Gordon Matzigkeit, 1996
+
+# Copyright (C) 2014 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of of the License, or
+# (at your option) any later version.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program or library that is built
+# using GNU Libtool, you may include this file under the same
+# distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# The names of the tagged configurations supported by this script.
+available_tags=''
+
+# Configured defaults for sys_lib_dlsearch_path munging.
+: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"}
+
+# ### BEGIN LIBTOOL CONFIG
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# Assembler program.
+AS=$lt_AS
+
+# DLL creation program.
+DLLTOOL=$lt_DLLTOOL
+
+# Object dumper program.
+OBJDUMP=$lt_OBJDUMP
+
+# Which release of libtool.m4 was used?
+macro_version=$macro_version
+macro_revision=$macro_revision
+
+# What type of objects to build.
+pic_mode=$pic_mode
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# Shared archive member basename,for filename based shared library versioning on AIX.
+shared_archive_member_spec=$shared_archive_member_spec
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# An echo program that protects backslashes.
+ECHO=$lt_ECHO
+
+# The PATH separator for the build system.
+PATH_SEPARATOR=$lt_PATH_SEPARATOR
+
+# The host system.
+host_alias=$host_alias
+host=$host
+host_os=$host_os
+
+# The build system.
+build_alias=$build_alias
+build=$build
+build_os=$build_os
+
+# A sed program that does not truncate output.
+SED=$lt_SED
+
+# Sed that helps us avoid accidentally triggering echo(1) options like -n.
+Xsed="\$SED -e 1s/^X//"
+
+# A grep program that handles long lines.
+GREP=$lt_GREP
+
+# An ERE matcher.
+EGREP=$lt_EGREP
+
+# A literal string matcher.
+FGREP=$lt_FGREP
+
+# A BSD- or MS-compatible name lister.
+NM=$lt_NM
+
+# Whether we need soft or hard links.
+LN_S=$lt_LN_S
+
+# What is the maximum length of a command?
+max_cmd_len=$max_cmd_len
+
+# Object file suffix (normally "o").
+objext=$ac_objext
+
+# Executable file suffix (normally "").
+exeext=$exeext
+
+# whether the shell understands "unset".
+lt_unset=$lt_unset
+
+# turn spaces into newlines.
+SP2NL=$lt_lt_SP2NL
+
+# turn newlines into spaces.
+NL2SP=$lt_lt_NL2SP
+
+# convert \$build file names to \$host format.
+to_host_file_cmd=$lt_cv_to_host_file_cmd
+
+# convert \$build files to toolchain format.
+to_tool_file_cmd=$lt_cv_to_tool_file_cmd
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method = "file_magic".
+file_magic_cmd=$lt_file_magic_cmd
+
+# How to find potential files when deplibs_check_method = "file_magic".
+file_magic_glob=$lt_file_magic_glob
+
+# Find potential files using nocaseglob when deplibs_check_method = "file_magic".
+want_nocaseglob=$lt_want_nocaseglob
+
+# Command to associate shared and link libraries.
+sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd
+
+# The archiver.
+AR=$lt_AR
+
+# Flags to create an archive.
+AR_FLAGS=$lt_AR_FLAGS
+
+# How to feed a file listing to the archiver.
+archiver_list_spec=$lt_archiver_list_spec
+
+# A symbol stripping program.
+STRIP=$lt_STRIP
+
+# Commands used to install an old-style archive.
+RANLIB=$lt_RANLIB
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# Whether to use a lock for old archive extraction.
+lock_old_archive_extraction=$lock_old_archive_extraction
+
+# A C compiler.
+LTCC=$lt_CC
+
+# LTCC compiler flags.
+LTCFLAGS=$lt_CFLAGS
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration.
+global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+
+# Transform the output of nm into a list of symbols to manually relocate.
+global_symbol_to_import=$lt_lt_cv_sys_global_symbol_to_import
+
+# Transform the output of nm in a C name address pair.
+global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
+
+# Transform the output of nm in a C name address pair when lib prefix is needed.
+global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix
+
+# The name lister interface.
+nm_interface=$lt_lt_cv_nm_interface
+
+# Specify filename containing input files for \$NM.
+nm_file_list_spec=$lt_nm_file_list_spec
+
+# The root where to search for dependent libraries,and where our libraries should be installed.
+lt_sysroot=$lt_sysroot
+
+# Command to truncate a binary pipe.
+lt_truncate_bin=$lt_lt_cv_truncate_bin
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# Used to examine libraries when file_magic_cmd begins with "file".
+MAGIC_CMD=$MAGIC_CMD
+
+# Must we lock files when doing compilation?
+need_locks=$lt_need_locks
+
+# Manifest tool.
+MANIFEST_TOOL=$lt_MANIFEST_TOOL
+
+# Tool to manipulate archived DWARF debug symbol files on Mac OS X.
+DSYMUTIL=$lt_DSYMUTIL
+
+# Tool to change global to local symbols on Mac OS X.
+NMEDIT=$lt_NMEDIT
+
+# Tool to manipulate fat objects and archives on Mac OS X.
+LIPO=$lt_LIPO
+
+# ldd/readelf like tool for Mach-O binaries on Mac OS X.
+OTOOL=$lt_OTOOL
+
+# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4.
+OTOOL64=$lt_OTOOL64
+
+# Old archive suffix (normally "a").
+libext=$libext
+
+# Shared library suffix (normally ".so").
+shrext_cmds=$lt_shrext_cmds
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at link time.
+variables_saved_for_relink=$lt_variables_saved_for_relink
+
+# Do we need the "lib" prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Library versioning type.
+version_type=$version_type
+
+# Shared library runtime path variable.
+runpath_var=$runpath_var
+
+# Shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names. First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Permission mode override for installation of shared libraries.
+install_override_mode=$lt_install_override_mode
+
+# Command to use after installation of a shared archive.
+postinstall_cmds=$lt_postinstall_cmds
+
+# Command to use after uninstallation of a shared archive.
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# As "finish_cmds", except a single script fragment to be evaled but
+# not shown.
+finish_eval=$lt_finish_eval
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Compile-time system search path for libraries.
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Detected run-time system search path for libraries.
+sys_lib_dlsearch_path_spec=$lt_configure_time_dlsearch_path
+
+# Explicit LT_SYS_LIBRARY_PATH set during ./configure time.
+configure_time_lt_sys_library_path=$lt_configure_time_lt_sys_library_path
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+
+# The linker used to build libraries.
+LD=$lt_LD
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# Commands used to build an old-style archive.
+old_archive_cmds=$lt_old_archive_cmds
+
+# A language specific compiler.
+CC=$lt_compiler
+
+# Is the compiler the GNU compiler?
+with_gcc=$GCC
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc
+
+# Whether or not to disallow shared libs when runtime libs are static.
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec
+
+# Whether the compiler copes with passing no objects directly.
+compiler_needs_object=$lt_compiler_needs_object
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
+
+# Commands used to build a shared archive.
+archive_cmds=$lt_archive_cmds
+archive_expsym_cmds=$lt_archive_expsym_cmds
+
+# Commands used to build a loadable module if different from building
+# a shared archive.
+module_cmds=$lt_module_cmds
+module_expsym_cmds=$lt_module_expsym_cmds
+
+# Whether we are building with GNU ld or not.
+with_gnu_ld=$lt_with_gnu_ld
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag
+
+# Flag that enforces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
+
+# Whether we need a single "-rpath" flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator
+
+# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes
+# DIR into the resulting binary.
+hardcode_direct=$hardcode_direct
+
+# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes
+# DIR into the resulting binary and the resulting library dependency is
+# "absolute",i.e impossible to change by setting \$shlibpath_var if the
+# library is relocated.
+hardcode_direct_absolute=$hardcode_direct_absolute
+
+# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+# into the resulting binary.
+hardcode_minus_L=$hardcode_minus_L
+
+# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+# into the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var
+
+# Set to "yes" if building a shared library automatically hardcodes DIR
+# into the library and all subsequent libraries and executables linked
+# against it.
+hardcode_automatic=$hardcode_automatic
+
+# Set to yes if linker adds runtime paths of dependent libraries
+# to runtime path list.
+inherit_rpath=$inherit_rpath
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs
+
+# Set to "yes" if exported symbols are required.
+always_export_symbols=$always_export_symbols
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms
+
+# Commands necessary for linking programs (against libraries) with templates.
+prelink_cmds=$lt_prelink_cmds
+
+# Commands necessary for finishing linking programs.
+postlink_cmds=$lt_postlink_cmds
+
+# Specify filename containing input files.
+file_list_spec=$lt_file_list_spec
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action
+
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+ cat <<'_LT_EOF' >> "$cfgfile"
+
+# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE
+
+# func_munge_path_list VARIABLE PATH
+# -----------------------------------
+# VARIABLE is name of variable containing _space_ separated list of
+# directories to be munged by the contents of PATH, which is string
+# having a format:
+# "DIR[:DIR]:"
+# string "DIR[ DIR]" will be prepended to VARIABLE
+# ":DIR[:DIR]"
+# string "DIR[ DIR]" will be appended to VARIABLE
+# "DIRP[:DIRP]::[DIRA:]DIRA"
+# string "DIRP[ DIRP]" will be prepended to VARIABLE and string
+# "DIRA[ DIRA]" will be appended to VARIABLE
+# "DIR[:DIR]"
+# VARIABLE will be replaced by "DIR[ DIR]"
+func_munge_path_list ()
+{
+ case x$2 in
+ x)
+ ;;
+ *:)
+ eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\"
+ ;;
+ x:*)
+ eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\"
+ ;;
+ *::*)
+ eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\"
+ eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\"
+ ;;
+ *)
+ eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\"
+ ;;
+ esac
+}
+
+
+# Calculate cc_basename. Skip known compiler wrappers and cross-prefix.
+func_cc_basename ()
+{
+ for cc_temp in $*""; do
+ case $cc_temp in
+ compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+ distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+ done
+ func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+}
+
+
+# ### END FUNCTIONS SHARED WITH CONFIGURE
+
+_LT_EOF
+
+ case $host_os in
+ aix3*)
+ cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program. For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test set != "${COLLECT_NAMES+set}"; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+fi
+_LT_EOF
+ ;;
+ esac
+
+
+ltmain=$ac_aux_dir/ltmain.sh
+
+
+ # We use sed instead of cat because bash on DJGPP gets confused if
+ # if finds mixed CR/LF and LF-only lines. Since sed operates in
+ # text mode, it properly converts lines to CR/LF. This bash problem
+ # is reportedly fixed, but why not run on old versions too?
+ sed '$q' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ mv -f "$cfgfile" "$ofile" ||
+ (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+ chmod +x "$ofile"
+
+ ;;
+ "default":C)
+chmod +x tests/run
+date > stamp-h
+BACKENDSC="servers/slapd/backends.c"
+echo "Making $BACKENDSC"
+rm -f $BACKENDSC
+cat > $BACKENDSC << ENDX
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* This file is automatically generated by configure; please do not edit. */
+
+#include "portable.h"
+#include "slap.h"
+
+ENDX
+if test "${STATIC_BACKENDS}"; then
+ for b in config ${STATIC_BACKENDS}; do
+ bb=`echo "${b}" | sed -e 's/back-//'`
+ cat >> $BACKENDSC << ENDX
+extern BI_init ${bb}_back_initialize;
+ENDX
+ done
+
+ cat >> $BACKENDSC << ENDX
+
+BackendInfo slap_binfo[] = {
+ENDX
+
+ for b in config ${STATIC_BACKENDS}; do
+ bb=`echo "${b}" | sed -e 's/back-//'`
+ echo " Add ${bb} ..."
+ cat >> $BACKENDSC << ENDX
+ { "${bb}", ${bb}_back_initialize },
+ENDX
+ done
+
+ cat >> $BACKENDSC << ENDX
+ { NULL, NULL },
+};
+
+/* end of generated file */
+ENDX
+fi
+OVERLAYSC="servers/slapd/overlays/statover.c"
+echo "Making $OVERLAYSC"
+rm -f $OVERLAYSC
+cat > $OVERLAYSC << ENDX
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* This file is automatically generated by configure; please do not edit. */
+
+#include "portable.h"
+#include "slap.h"
+
+ENDX
+if test "${STATIC_OVERLAYS}"; then
+ for o in ${STATIC_OVERLAYS}; do
+ oo=`echo "${o}" | sed -e 's/.o$//' -e 's/_x$//'`
+ cat >> $OVERLAYSC << ENDX
+extern OV_init ${oo}_initialize;
+ENDX
+ done
+fi
+
+cat >> $OVERLAYSC << ENDX
+
+OverlayInit slap_oinfo[] = {
+ENDX
+
+if test "${STATIC_OVERLAYS}"; then
+ for o in ${STATIC_OVERLAYS}; do
+ oo=`echo "${o}" | sed -e 's/.o$//' -e 's/_x$//'`
+ echo " Add ${oo} ..."
+ cat >> $OVERLAYSC << ENDX
+ { "${oo}", ${oo}_initialize },
+ENDX
+ done
+fi
+
+ cat >> $OVERLAYSC << ENDX
+ { NULL, NULL },
+};
+
+/* end of generated file */
+ENDX
+
+if test "${ol_cv_mkdep}" = no; then
+ echo '(Do not "make depend"; we do not know how to build dependencies)'
+else
+ echo 'Please run "make depend" to build dependencies'
+fi
+ ;;
+
+ esac
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+ as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..626d024
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,3432 @@
+dnl $OpenLDAP$
+dnl This work is part of OpenLDAP Software <http://www.openldap.org/>.
+dnl
+dnl Copyright 1998-2022 The OpenLDAP Foundation.
+dnl All rights reserved.
+dnl
+dnl Redistribution and use in source and binary forms, with or without
+dnl modification, are permitted only as authorized by the OpenLDAP
+dnl Public License.
+dnl
+dnl A copy of this license is available in the file LICENSE in the
+dnl top-level directory of the distribution or, alternatively, at
+dnl <http://www.OpenLDAP.org/license.html>.
+dnl
+dnl ----------------------------------------------------------------
+dnl Disable config.cache!
+define([AC_CACHE_LOAD], )dnl
+define([AC_CACHE_SAVE], )dnl
+dnl ----------------------------------------------------------------
+dnl Disable libtool 1.5 support for languages we don't use
+define([AC_LIBTOOL_LANG_CXX_CONFIG], [:])dnl
+define([AC_LIBTOOL_LANG_F77_CONFIG], [:])dnl
+define([AC_LIBTOOL_LANG_GCJ_CONFIG], [:])dnl
+dnl ================================================================
+dnl Configure.in for OpenLDAP
+AC_COPYRIGHT([[Copyright 1998-2022 The OpenLDAP Foundation. All rights reserved.
+Restrictions apply, see COPYRIGHT and LICENSE files.]])
+AC_REVISION([$Id: 15bca89511fc428731cf9ab71a9b46e37511be67 $])
+AC_INIT([OpenLDAP],,[https://bugs.openldap.org],,[https://www.openldap.org])
+AC_CONFIG_SRCDIR(build/version.sh)dnl
+dnl ----------------------------------------------------------------
+dnl OpenLDAP Autoconf Macros
+builtin(include, build/openldap.m4)dnl
+dnl ================================================================
+
+m4_ifndef([PKG_PREREQ],
+ [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])])
+
+AC_CONFIG_AUX_DIR(build)dnl
+AC_CONFIG_MACRO_DIRS([build])
+
+eval `$ac_aux_dir/version.sh`
+if test -z "$OL_STRING"; then
+ AC_MSG_ERROR([could not determine version])
+fi
+
+if test -f "$ac_aux_dir/shtool" && test ! -d $ac_aux_dir/shtool; then
+ ac_cv_shtool="$ac_aux_dir/shtool"
+else
+ AC_MSG_ERROR([no shtool found in $ac_aux_dir])
+fi
+
+SHTOOL="$ac_cv_shtool"
+dnl AC_SUBST(SHTOOL)dnl
+
+TB="" TN=""
+if test -t 1; then
+ TB="`$SHTOOL echo -e '%B' 2>/dev/null`"
+ TN="`$SHTOOL echo -e '%b' 2>/dev/null`"
+fi
+
+OPENLDAP_REPO=""
+if test -d $ac_aux_dir/../.git; then
+ OPENLDAP_REPO="(from Git clone) "
+elif test -d $ac_aux_dir/CVS; then
+ OPENLDAP_REPO="(from CVS checkout) "
+fi
+
+echo "Configuring ${TB}${OL_STRING}${TN} ${OPENLDAP_REPO}..."
+
+dnl Determine host platform
+dnl we try not to use this for much
+AC_CANONICAL_TARGET([])
+
+AC_SUBST(PACKAGE,$OL_PACKAGE)dnl
+AC_SUBST(VERSION,$OL_VERSION)dnl
+AC_DEFINE_UNQUOTED(OPENLDAP_PACKAGE,"$PACKAGE",Package)
+AC_DEFINE_UNQUOTED(OPENLDAP_VERSION,"$VERSION",Version)
+
+AC_DEFINE_UNQUOTED(LDAP_VENDOR_VERSION,$OL_API_INC,Version)
+AC_DEFINE_UNQUOTED(LDAP_VENDOR_VERSION_MAJOR,$OL_MAJOR,Major)
+AC_DEFINE_UNQUOTED(LDAP_VENDOR_VERSION_MINOR,$OL_MINOR,Minor)
+AC_DEFINE_UNQUOTED(LDAP_VENDOR_VERSION_PATCH,$OL_PATCH,Patch)
+
+OPENLDAP_LIBRELEASE=$OL_API_LIB_RELEASE
+AC_SUBST(OPENLDAP_LIBRELEASE)dnl
+
+OPENLDAP_LIBVERSION=$OL_API_LIB_VERSION
+AC_SUBST(OPENLDAP_LIBVERSION)dnl
+
+OPENLDAP_RELEASE_DATE="$OL_RELEASE_DATE"
+AC_SUBST(OPENLDAP_RELEASE_DATE)dnl
+
+AC_PREREQ(2.69)dnl Required Autoconf version
+
+AH_TOP([
+/* begin of portable.h.pre */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _LDAP_PORTABLE_H
+#define _LDAP_PORTABLE_H
+
+/* define this if needed to get reentrant functions */
+#ifndef REENTRANT
+#undef REENTRANT
+#endif
+#ifndef _REENTRANT
+#undef _REENTRANT
+#endif
+
+/* define this if needed to get threadsafe functions */
+#ifndef THREADSAFE
+#undef THREADSAFE
+#endif
+#ifndef _THREADSAFE
+#undef _THREADSAFE
+#endif
+#ifndef THREAD_SAFE
+#undef THREAD_SAFE
+#endif
+#ifndef _THREAD_SAFE
+#undef _THREAD_SAFE
+#endif
+
+#ifndef _SGI_MP_SOURCE
+#undef _SGI_MP_SOURCE
+#endif
+
+/* end of portable.h.pre */
+])
+AH_BOTTOM([
+/* begin of portable.h.post */
+
+#ifdef _WIN32
+ /* don't suck in all of the win32 api */
+# define WIN32_LEAN_AND_MEAN 1
+#endif
+
+#ifndef LDAP_NEEDS_PROTOTYPES
+/* force LDAP_P to always include prototypes */
+#define LDAP_NEEDS_PROTOTYPES 1
+#endif
+
+#ifndef LDAP_REL_ENG
+#if (LDAP_VENDOR_VERSION == 000000) && !defined(LDAP_DEVEL)
+#define LDAP_DEVEL
+#endif
+#if defined(LDAP_DEVEL) && !defined(LDAP_TEST)
+#define LDAP_TEST
+#endif
+#endif
+
+#ifdef HAVE_STDDEF_H
+# include <stddef.h>
+#endif
+
+#ifdef HAVE_EBCDIC
+/* ASCII/EBCDIC converting replacements for stdio funcs
+ * vsnprintf and snprintf are used too, but they are already
+ * checked by the configure script
+ */
+#define fputs ber_pvt_fputs
+#define fgets ber_pvt_fgets
+#define printf ber_pvt_printf
+#define fprintf ber_pvt_fprintf
+#define vfprintf ber_pvt_vfprintf
+#define vsprintf ber_pvt_vsprintf
+#endif
+
+#include "ac/fdset.h"
+
+#include "ldap_cdefs.h"
+#include "ldap_features.h"
+
+#include "ac/assert.h"
+#include "ac/localize.h"
+
+#endif /* _LDAP_PORTABLE_H */
+/* end of portable.h.post */
+])
+
+AC_CONFIG_HEADERS([include/portable.h:include/portable.hin])
+AC_CONFIG_HEADERS([include/ldap_features.h:include/ldap_features.hin])
+AC_CONFIG_HEADERS([include/lber_types.h:include/lber_types.hin])
+
+dnl ================================================================
+dnl Start Args
+AC_MSG_CHECKING(configure arguments)
+AC_PREFIX_DEFAULT(/usr/local)
+
+top_builddir=`pwd`
+AC_SUBST(top_builddir)dnl
+
+dnl ----------------------------------------------------------------
+dnl --with-subdir
+ldap_subdir="/openldap"
+
+AC_ARG_WITH(subdir,
+[ --with-subdir=DIR change default subdirectory used for installs],
+[case "$withval" in
+ no) ldap_subdir=""
+ ;;
+ yes)
+ ;;
+ /*|\\*)
+ ldap_subdir="$withval"
+ ;;
+ *)
+ ldap_subdir="/$withval"
+ ;;
+esac
+])dnl
+
+AC_SUBST(ldap_subdir)dnl
+
+dnl ----------------------------------------------------------------
+dnl General "enable" options
+dnl set default to traditional to enable the original debug style
+OL_ARG_ENABLE(debug, [AS_HELP_STRING([--enable-debug], [enable debugging])], yes, [no yes traditional])dnl
+OL_ARG_ENABLE(dynamic, [AS_HELP_STRING([--enable-dynamic], [enable linking built binaries with dynamic libs])], auto)dnl
+OL_ARG_ENABLE(syslog, [AS_HELP_STRING([--enable-syslog], [enable syslog support])], auto)dnl
+dnl OL_ARG_ENABLE(referrals,[AS_HELP_STRING([--enable-referrals], [enable LDAPv2+ Referrals (experimental)])], no)dnl
+ol_enable_referrals=${ol_enable_referrals-no}
+OL_ARG_ENABLE(ipv6, [AS_HELP_STRING([--enable-ipv6], [enable IPv6 support])], auto)dnl
+OL_ARG_ENABLE(local, [AS_HELP_STRING([--enable-local], [enable AF_LOCAL (AF_UNIX) socket support])], auto)dnl
+
+dnl ----------------------------------------------------------------
+dnl General "with" options
+OL_ARG_WITH(cyrus_sasl, [AS_HELP_STRING([--with-cyrus-sasl], [with Cyrus SASL support])],
+ auto, [auto yes no] )
+OL_ARG_WITH(systemd, [AS_HELP_STRING([--with-systemd], [with systemd service notification support])],
+ auto, [auto yes no] )
+OL_ARG_WITH(fetch, [AS_HELP_STRING([--with-fetch], [with fetch(3) URL support])],
+ auto, [auto yes no] )
+OL_ARG_WITH(threads,
+ [AS_HELP_STRING([--with-threads], [with threads library auto|nt|posix|pth|lwp|manual])],
+ auto, [auto nt posix pth lwp yes no manual] )
+OL_ARG_WITH(tls,
+ [AS_HELP_STRING([--with-tls], [with TLS/SSL support auto|openssl|gnutls])],
+ auto, [auto openssl gnutls yes no] )
+OL_ARG_WITH(yielding_select,
+ [AS_HELP_STRING([--with-yielding-select], [with implicitly yielding select])],
+ auto, [auto yes no manual] )
+OL_ARG_WITH(mp,
+ [AS_HELP_STRING([--with-mp], [with multiple precision statistics auto|longlong|long|bignum|gmp])],
+ auto, [auto longlong long bignum gmp yes no])
+OL_ARG_WITH(odbc,
+ [AS_HELP_STRING([--with-odbc], [with specific ODBC support iodbc|unixodbc|odbc32|auto])],
+ auto, [auto iodbc unixodbc odbc32] )
+
+dnl ----------------------------------------------------------------
+dnl Server options
+dnl ----------------------------------------------------------------
+
+dnl ----------------------------------------------------------------
+dnl SLAPD OPTIONS
+SlapdOptions="dynacl \
+ aci \
+ cleartext \
+ crypt \
+ spasswd \
+ modules \
+ rlookups \
+ slapi \
+ slp \
+ wrappers"
+
+AC_ARG_ENABLE(xxslapdoptions,[
+SLAPD (Standalone LDAP Daemon) Options:])
+
+OL_ARG_ENABLE(slapd, [AS_HELP_STRING([--enable-slapd], [enable building slapd])], yes)dnl
+OL_ARG_ENABLE(dynacl, [AS_HELP_STRING([--enable-dynacl], [enable run-time loadable ACL support (experimental)])], no)dnl
+OL_ARG_ENABLE(aci, [AS_HELP_STRING([--enable-aci], [enable per-object ACIs (experimental)])], no, [no yes mod])dnl
+OL_ARG_ENABLE(cleartext, [AS_HELP_STRING([--enable-cleartext], [enable cleartext passwords])], yes)dnl
+OL_ARG_ENABLE(crypt, [AS_HELP_STRING([--enable-crypt], [enable crypt(3) passwords])], no)dnl
+OL_ARG_ENABLE(spasswd, [AS_HELP_STRING([--enable-spasswd], [enable (Cyrus) SASL password verification])], no)dnl
+OL_ARG_ENABLE(modules, [AS_HELP_STRING([--enable-modules], [enable dynamic module support])], no)dnl
+OL_ARG_ENABLE(rlookups, [AS_HELP_STRING([--enable-rlookups], [enable reverse lookups of client hostnames])], no)dnl
+OL_ARG_ENABLE(slapi, [AS_HELP_STRING([--enable-slapi], [enable SLAPI support (experimental)])], no)dnl
+OL_ARG_ENABLE(slp, [AS_HELP_STRING([--enable-slp], [enable SLPv2 support])], no)dnl
+OL_ARG_ENABLE(wrappers, [AS_HELP_STRING([--enable-wrappers], [enable tcp wrapper support])], no)dnl
+
+dnl ----------------------------------------------------------------
+dnl SLAPD Backend Options
+Backends="dnssrv \
+ ldap \
+ mdb \
+ meta \
+ asyncmeta \
+ ndb \
+ null \
+ passwd \
+ perl \
+ relay \
+ sock \
+ sql \
+ wt"
+
+AC_ARG_ENABLE(xxslapbackends,[
+SLAPD Backend Options:])
+
+OL_ARG_ENABLE(backends, [AS_HELP_STRING([--enable-backends], [enable all available backends])],
+ --, [no yes mod])dnl
+OL_ARG_ENABLE(dnssrv, [AS_HELP_STRING([--enable-dnssrv], [enable dnssrv backend])],
+ no, [no yes mod], ol_enable_backends)dnl
+OL_ARG_ENABLE(ldap, [AS_HELP_STRING([--enable-ldap], [enable ldap backend])],
+ no, [no yes mod], ol_enable_backends)dnl
+OL_ARG_ENABLE(mdb, [AS_HELP_STRING([--enable-mdb], [enable mdb database backend])],
+ yes, [no yes mod], ol_enable_backends)dnl
+OL_ARG_ENABLE(meta, [AS_HELP_STRING([--enable-meta], [enable metadirectory backend])],
+ no, [no yes mod], ol_enable_backends)dnl
+OL_ARG_ENABLE(asyncmeta, [AS_HELP_STRING([--enable-asyncmeta], [enable asynchronous metadirectory backend])],
+ no, [no yes mod], ol_enable_backends)dnl
+OL_ARG_ENABLE(ndb, [AS_HELP_STRING([--enable-ndb], [enable MySQL NDB Cluster backend])],
+ no, [no yes mod])dnl
+OL_ARG_ENABLE(null, [AS_HELP_STRING([--enable-null], [enable null backend])],
+ no, [no yes mod], ol_enable_backends)dnl
+OL_ARG_ENABLE(passwd, [AS_HELP_STRING([--enable-passwd], [enable passwd backend])],
+ no, [no yes mod], ol_enable_backends)dnl
+OL_ARG_ENABLE(perl, [AS_HELP_STRING([--enable-perl], [enable perl backend])],
+ no, [no yes mod])dnl
+OL_ARG_ENABLE(relay, [AS_HELP_STRING([--enable-relay], [enable relay backend])],
+ yes, [no yes mod], ol_enable_backends)dnl
+OL_ARG_ENABLE(sock, [AS_HELP_STRING([--enable-sock], [enable sock backend])],
+ no, [no yes mod], ol_enable_backends)dnl
+OL_ARG_ENABLE(sql, [AS_HELP_STRING([--enable-sql], [enable sql backend])],
+ no, [no yes mod])dnl
+OL_ARG_ENABLE(wt, [AS_HELP_STRING([--enable-wt], [enable WiredTiger backend])],
+ no, [no yes mod], ol_enable_backends)dnl
+
+dnl ----------------------------------------------------------------
+dnl SLAPD Overlay Options
+Overlays="accesslog \
+ auditlog \
+ autoca \
+ collect \
+ constraint \
+ dds \
+ deref \
+ dyngroup \
+ dynlist \
+ homedir \
+ memberof \
+ otp \
+ ppolicy \
+ proxycache \
+ refint \
+ remoteauth \
+ retcode \
+ rwm \
+ seqmod \
+ sssvlv \
+ syncprov \
+ translucent \
+ unique \
+ valsort"
+
+Pwmods="argon2"
+
+AC_ARG_ENABLE(xxslapoverlays,[
+SLAPD Overlay Options:])
+
+OL_ARG_ENABLE(overlays, [AS_HELP_STRING([--enable-overlays], [enable all available overlays])],
+ --, [no yes mod])dnl
+OL_ARG_ENABLE(accesslog, [AS_HELP_STRING([--enable-accesslog], [In-Directory Access Logging overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(auditlog, [AS_HELP_STRING([--enable-auditlog], [Audit Logging overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(autoca, [AS_HELP_STRING([--enable-autoca], [Automatic Certificate Authority overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(collect, [AS_HELP_STRING([--enable-collect], [Collect overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(constraint, [AS_HELP_STRING([--enable-constraint], [Attribute Constraint overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(dds, [AS_HELP_STRING([--enable-dds], [Dynamic Directory Services overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(deref, [AS_HELP_STRING([--enable-deref], [Dereference overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(dyngroup, [AS_HELP_STRING([--enable-dyngroup], [Dynamic Group overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(dynlist, [AS_HELP_STRING([--enable-dynlist], [Dynamic List overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(homedir, [AS_HELP_STRING([--enable-homedir], [Home Directory Management overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(memberof, [AS_HELP_STRING([--enable-memberof], [Reverse Group Membership overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(otp, [AS_HELP_STRING([--enable-otp], [OTP 2-factor authentication overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(ppolicy, [AS_HELP_STRING([--enable-ppolicy], [Password Policy overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(proxycache, [AS_HELP_STRING([--enable-proxycache], [Proxy Cache overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(refint, [AS_HELP_STRING([--enable-refint], [Referential Integrity overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(remoteauth, [AS_HELP_STRING([--enable-remoteauth], [Deferred Authentication overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(retcode, [AS_HELP_STRING([--enable-retcode], [Return Code testing overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(rwm, [AS_HELP_STRING([--enable-rwm], [Rewrite/Remap overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(seqmod, [AS_HELP_STRING([--enable-seqmod], [Sequential Modify overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(sssvlv, [AS_HELP_STRING([--enable-sssvlv], [ServerSideSort/VLV overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(syncprov, [AS_HELP_STRING([--enable-syncprov], [Syncrepl Provider overlay])],
+ yes, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(translucent, [AS_HELP_STRING([--enable-translucent], [Translucent Proxy overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(unique, [AS_HELP_STRING([--enable-unique], [Attribute Uniqueness overlay])],
+ no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(valsort, [AS_HELP_STRING([--enable-valsort], [Value Sorting overlay])],
+ no, [no yes mod], ol_enable_overlays)
+
+dnl ----------------------------------------------------------------
+dnl PASSWORD MODULE OPTIONS
+AC_ARG_ENABLE(pwmodoptions,[
+SLAPD Password Module Options:])
+OL_ARG_ENABLE(argon2, [AS_HELP_STRING([--enable-argon2], [Argon2 password hashing module])],
+ no, [no yes], ol_enable_pwmodules)
+OL_ARG_WITH(argon2,
+ [AS_HELP_STRING([--with-argon2], [with argon2 support library auto|libsodium|libargon2])],
+ auto, [auto libsodium libargon2 yes no] )
+
+dnl ----------------------------------------------------------------
+dnl BALANCER OPTIONS
+AC_ARG_ENABLE(balanceroptions,[
+LLOADD (Load Balancer Daemon) Options:])
+OL_ARG_ENABLE(balancer, [AS_HELP_STRING([--enable-balancer], [enable load balancer])],
+ no, [no yes mod])
+
+dnl ----------------------------------------------------------------
+AC_ARG_ENABLE(xxliboptions,[
+Library Generation & Linking Options])
+AC_ENABLE_STATIC
+AC_ENABLE_SHARED
+OL_ARG_ENABLE(versioning, [AS_HELP_STRING([--enable-versioning], [Enable versioned symbols in shared library])],
+ auto, [no yes auto])
+dnl ----------------------------------------------------------------
+dnl Validate options
+dnl ----------------------------------------------------------------
+
+if test $ol_enable_slapd = no ; then
+ dnl SLAPD was specifically disabled
+ dnl Disable all of its options
+
+ for i in $SlapdOptions; do
+ eval "ol_tmp=\$ol_enable_$i"
+ if test $ol_tmp = yes ; then
+ AC_MSG_WARN([slapd disabled, ignoring --enable-$i argument])
+ eval "ol_enable_$i=no"
+ fi
+ done
+
+ for i in $Backends $Overlays $Pwmods; do
+ eval "ol_tmp=\$ol_enable_$i"
+ if test $ol_tmp != no ; then
+ AC_MSG_WARN([slapd disabled, ignoring --enable-$i argument])
+ eval "ol_enable_$i=no"
+ fi
+ done
+
+ if test $ol_enable_balancer = mod ; then
+ AC_MSG_WARN([slapd disabled, ignoring --enable-balancer=mod argument])
+ ol_enable_balancer=no
+ fi
+else
+ dnl If slapd enabled and loadable module support disabled
+ dnl then require at least one built-in backend
+
+ if test $ol_enable_modules = no; then
+
+ for i in backends overlays balancer $Backends $Overlays; do
+ eval "ol_tmp=\$ol_enable_$i"
+ if test -n "$ol_tmp" && test "$ol_tmp" = mod ; then
+ AC_MSG_ERROR([--enable-$i=mod requires --enable-modules])
+ fi
+ done
+
+ for i in $Pwmods; do
+ eval "ol_tmp=\$ol_enable_$i"
+ if test -n "$ol_tmp" && test "$ol_tmp" = yes ; then
+ AC_MSG_ERROR([--enable-$i=yes requires --enable-modules])
+ fi
+ done
+
+ ol_any_backend=no
+ for i in $Backends; do
+ eval "ol_tmp=\$ol_enable_$i"
+ if test $ol_tmp = yes; then
+ ol_any_backend=yes
+ fi
+ done
+
+ if test $ol_any_backend = no; then
+ AC_MSG_ERROR([slapd requires a backend])
+ fi
+ fi
+fi
+
+if test $ol_enable_aci = yes ; then
+ if test $ol_enable_dynacl = no ; then
+ AC_MSG_ERROR([--enable-aci requires --enable-dynacl])
+ fi
+elif test $ol_enable_aci = mod ; then
+ AC_MSG_ERROR([ACI build as dynamic module not supported (yet)])
+fi
+
+if test $ol_enable_modules = yes ; then
+ if test $ol_enable_dynamic = no ; then
+ AC_MSG_ERROR([--enable-modules requires --enable-dynamic])
+ fi
+ ol_enable_dynamic=yes
+fi
+
+if test $ol_enable_balancer != no ; then
+ dnl Load Balancer was specifically enabled
+ if test $ol_with_threads = no ; then
+ AC_MSG_ERROR([Load balancer requires threads])
+ fi
+fi
+
+if test $ol_enable_spasswd = yes ; then
+ if test $ol_with_cyrus_sasl = no ; then
+ AC_MSG_ERROR([--enable-spasswd requires --with-cyrus-sasl])
+ fi
+ ol_with_cyrus_sasl=yes
+fi
+
+if test $ol_enable_meta/$ol_enable_ldap = yes/no ; then
+ AC_MSG_ERROR([--enable-meta requires --enable-ldap])
+fi
+
+if test $ol_enable_asyncmeta/$ol_enable_ldap = yes/no ; then
+ AC_MSG_ERROR([--enable-asyncmeta requires --enable-ldap])
+fi
+
+AC_MSG_RESULT(done)
+
+dnl ----------------------------------------------------------------
+dnl Initialize vars
+LDAP_LIBS=
+SLAPD_NDB_LIBS=
+SLAPD_NDB_INCS=
+LTHREAD_LIBS=
+LEVENT_LIBS=
+LUTIL_LIBS=
+
+CLIENT_LIBS=
+
+SLAPD_LIBS=
+BALANCER_LIBS=
+BALANCER_INCLUDE=
+
+BUILD_SLAPD=no
+BUILD_BALANCER=no
+
+BUILD_THREAD=no
+
+BUILD_SLAPI=no
+SLAPD_SLAPI_DEPEND=
+
+BUILD_DNSSRV=no
+BUILD_LDAP=no
+BUILD_MDB=no
+BUILD_META=no
+BUILD_ASYNCMETA=no
+BUILD_NDB=no
+BUILD_NULL=no
+BUILD_PASSWD=no
+BUILD_PERL=no
+BUILD_RELAY=no
+BUILD_SHELL=no
+BUILD_SOCK=no
+BUILD_SQL=no
+BUILD_WT=no
+
+BUILD_ACCESSLOG=no
+BUILD_AUDITLOG=no
+BUILD_AUTOCA=no
+BUILD_CONSTRAINT=no
+BUILD_DDS=no
+BUILD_DENYOP=no
+BUILD_DEREF=no
+BUILD_DYNGROUP=no
+BUILD_DYNLIST=no
+BUILD_LASTMOD=no
+BUILD_HOMEDIR=no
+BUILD_MEMBEROF=no
+BUILD_OTP=no
+BUILD_PPOLICY=no
+BUILD_PROXYCACHE=no
+BUILD_REFINT=no
+BUILD_REMOTEAUTH=no
+BUILD_RETCODE=no
+BUILD_RWM=no
+BUILD_SEQMOD=no
+BUILD_SSSVLV=no
+BUILD_SYNCPROV=no
+BUILD_TRANSLUCENT=no
+BUILD_UNIQUE=no
+BUILD_VALSORT=no
+
+BUILD_PW_ARGON2=no
+
+SLAPD_STATIC_OVERLAYS=
+SLAPD_DYNAMIC_OVERLAYS=
+
+SLAPD_DYNAMIC_PWMODS=
+
+SLAPD_MODULES_LDFLAGS=
+SLAPD_MODULES_CPPFLAGS=
+
+SLAPD_STATIC_BACKENDS="back-ldif back-monitor"
+SLAPD_DYNAMIC_BACKENDS=
+
+SLAPD_PERL_LDFLAGS=
+MOD_PERL_LDFLAGS=
+PERL_CPPFLAGS=
+
+SLAPD_SQL_LDFLAGS=
+SLAPD_SQL_LIBS=
+SLAPD_SQL_INCLUDES=
+
+SASL_LIBS=
+TLS_LIBS=
+WITH_TLS_TYPE=no
+MODULES_LIBS=
+SLAPI_LIBS=
+LIBSLAPI=
+AUTH_LIBS=
+SYSTEMD_LIBS=
+
+SLAPD_SLP_LIBS=
+SLAPD_GMP_LIBS=
+
+dnl ================================================================
+dnl Checks for programs
+
+AC_DEFINE(HAVE_MKVERSION, 1, [define this if you have mkversion])
+
+dnl ----------------------------------------------------------------
+dnl
+dnl Determine which C translator to use
+dnl
+
+dnl AIX Thread requires we use cc_r or xlc_r.
+dnl But only do this IF AIX and CC is not set
+dnl and threads are auto|yes|posix.
+dnl
+dnl If we find cc_r|xlc_r, force pthreads and assume
+dnl pthread_create is in $LIBS (ie: don't bring in
+dnl any additional thread libraries)
+dnl If we do not find cc_r|xlc_r, disable threads
+
+ol_aix_threads=no
+case "$target" in
+*-*-aix*) dnl all AIX is not a good idea.
+ if test -z "$CC" ; then
+ case "$ol_with_threads" in
+ auto | yes | posix) ol_aix_threads=yes ;;
+ esac
+ fi
+;;
+esac
+
+if test $ol_aix_threads = yes ; then
+ if test -z "${CC}" ; then
+ AC_CHECK_PROGS(CC,cc_r xlc_r cc)
+
+ if test "$CC" = cc ; then
+ dnl no CC! don't allow --with-threads
+ if test $ol_with_threads != auto ; then
+ AC_MSG_ERROR([--with-threads requires cc_r (or other suitable compiler) on AIX])
+ else
+ AC_MSG_WARN([disabling threads, no cc_r on AIX])
+ fi
+ ol_with_threads=no
+ fi
+ fi
+
+ case ${CC} in cc_r | xlc_r)
+ ol_with_threads=posix
+ ol_cv_pthread_create=yes
+ ;;
+ esac
+fi
+
+if test -z "${CC}"; then
+ AC_CHECK_PROGS(CC,cc gcc,missing)
+
+ if test "${CC}" = "missing" ; then
+ AC_MSG_ERROR([Unable to locate cc(1) or suitable replacement. Check PATH or set CC.])
+ fi
+fi
+
+if test -z "${AR}"; then
+ AC_CHECK_PROGS(AR,ar gar,missing)
+
+ if test "${AR}" = "missing" ; then
+ AC_MSG_ERROR([Unable to locate ar(1) or suitable replacement. Check PATH or set AR.])
+ fi
+fi
+
+if test -z "${STRIP}"; then
+ AC_CHECK_PROGS(STRIP,strip,missing)
+
+ if test "${STRIP}" = "missing" ; then
+ AC_MSG_ERROR([Unable to locate strip(1) or suitable replacement. Check PATH or set STRIP.])
+ fi
+fi
+
+
+AC_LIBTOOL_WIN32_DLL
+AC_LIBTOOL_DLOPEN
+AC_PROG_MAKE_SET
+AC_PROG_LIBTOOL
+
+dnl ----------------------------------------------------------------
+dnl Perl
+ol_link_perl=no
+if test $ol_enable_perl != no ; then
+ AC_PATH_PROG(PERLBIN, perl, /usr/bin/perl)
+
+ if test "no$PERLBIN" = "no" ; then
+ if test $ol_enable_perl = yes ; then
+ AC_MSG_ERROR([could not locate perl])
+ fi
+
+ else
+ PERL_CPPFLAGS="`$PERLBIN -MExtUtils::Embed -e ccopts`"
+ PERL_LDFLAGS="`$PERLBIN -MExtUtils::Embed -e ldopts|sed -e 's/ -lc / /' -e 's/ -lc$//'`"
+
+ if test x"$ol_enable_perl" = "xyes" ; then
+ SLAPD_PERL_LDFLAGS="$PERL_LDFLAGS"
+ else
+ MOD_PERL_LDFLAGS="$PERL_LDFLAGS"
+ fi
+ dnl should check perl version
+ ol_link_perl=yes
+ fi
+fi
+
+AC_PROG_CPP
+OL_MSVC
+
+dnl ----------------------------------------------------------------
+dnl Checks for Windows NT
+case $host_os in
+ *mingw32* ) ac_cv_mingw32=yes ;;
+ *cygwin* ) ac_cv_cygwin=yes ;;
+ *interix* ) ac_cv_interix=yes ;;
+esac
+
+AC_CHECK_TOOL(RC, windres, )
+
+dnl ----------------------------------------------------------------
+dnl Checks for file extensions
+AC_EXEEXT
+AC_OBJEXT
+AC_DEFINE_UNQUOTED(EXEEXT, "${EXEEXT}", [defined to be the EXE extension])
+
+dnl ----------------------------------------------------------------
+dnl BeOS requires -lbe -lroot -lnet
+AC_CHECK_LIB(be, be_app, [LIBS="$LIBS -lbe -lroot -lnet"], :, [-lroot -lnet])
+
+dnl ----------------------------------------------------------------
+dnl OpenLDAP requires STDC features
+AC_PROG_CC
+if test "X${ac_cv_prog_cc_stdc}" = "Xno" ; then
+ AC_MSG_ERROR([OpenLDAP requires compiler to support STDC constructs.])
+fi
+
+dnl ----------------------------------------------------------------
+dnl Check cc depend flags
+OL_MKDEPEND
+if test "${ol_cv_mkdep}" = no ; then
+ # this will soon become an error
+ AC_MSG_WARN([do not know how to generate dependencies])
+fi
+
+dnl ----------------------------------------------------------------
+dnl Check for AIX security library
+AC_CHECK_LIB(s, afopen, [
+ AUTH_LIBS=-ls
+ AC_DEFINE(HAVE_AIX_SECURITY,1,[define if you have AIX security lib])
+])
+
+dnl ----------------------------------------------------------------
+dnl Check for IBM OS/390
+case "$target" in
+*-ibm-openedition)
+ ac_cv_func_getopt=no
+ AC_DEFINE(BOTH_STRINGS_H,1,[define to use both <string.h> and <strings.h>])
+ ;;
+esac
+
+dnl ----------------------------------------------------------------
+dnl Check for module support
+ol_link_modules=no
+WITH_MODULES_ENABLED=no
+if test $ol_enable_modules != no ; then
+ AC_CHECK_HEADERS(ltdl.h)
+
+ if test $ac_cv_header_ltdl_h = no ; then
+ AC_MSG_ERROR([could not locate libtool ltdl.h])
+ fi
+
+ AC_CHECK_LIB(ltdl, lt_dlinit, [
+ MODULES_LIBS=-lltdl
+ AC_DEFINE(HAVE_LIBLTDL,1,[define if you have libtool -ltdl])
+ ])
+
+ if test "$ac_cv_lib_ltdl_lt_dlinit" = no ; then
+ AC_MSG_ERROR([could not locate libtool -lltdl])
+ fi
+ ol_link_modules=yes
+ WITH_MODULES_ENABLED=yes
+fi
+
+dnl ----------------------------------------------------------------
+dnl Checks for header files.
+OL_HEADER_STDC
+
+if test $ol_cv_header_stdc != yes; then
+ AC_MSG_WARN([could not locate Standard C compliant headers])
+fi
+
+AC_HEADER_DIRENT
+AC_HEADER_SYS_WAIT
+AC_HEADER_TIOCGWINSZ
+
+AC_CHECK_HEADERS( \
+ arpa/inet.h \
+ arpa/nameser.h \
+ assert.h \
+ bits/types.h \
+ conio.h \
+ crypt.h \
+ direct.h \
+ errno.h \
+ fcntl.h \
+ filio.h \
+ getopt.h \
+ grp.h \
+ io.h \
+ libutil.h \
+ limits.h \
+ locale.h \
+ malloc.h \
+ memory.h \
+ psap.h \
+ pwd.h \
+ process.h \
+ sgtty.h \
+ shadow.h \
+ stddef.h \
+ string.h \
+ strings.h \
+ sysexits.h \
+ sys/file.h \
+ sys/filio.h \
+ sys/fstyp.h \
+ sys/errno.h \
+ sys/ioctl.h \
+ sys/param.h \
+ sys/privgrp.h \
+ sys/resource.h \
+ sys/select.h \
+ sys/socket.h \
+ sys/stat.h \
+ sys/syslog.h \
+ sys/time.h \
+ sys/types.h \
+ sys/uio.h \
+ sys/vmount.h \
+ syslog.h \
+ termios.h \
+ unistd.h \
+ utime.h \
+)
+
+dnl Only check Winsock on MinGW
+if test "$ac_cv_mingw32" = yes \
+ -o "$ac_cv_interix" = yes \
+ -o "$ol_cv_msvc" = yes
+then
+ AC_CHECK_HEADERS( winsock.h winsock2.h )
+fi
+
+AC_CHECK_HEADERS( resolv.h, [], [],
+[$ac_includes_default
+#include <netinet/in.h>
+])
+
+AC_CHECK_HEADERS( netinet/tcp.h, [], [],
+[$ac_includes_default
+#include <netinet/in.h>
+])
+
+AC_CHECK_HEADERS( sys/ucred.h, [], [],
+[$ac_includes_default
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+])
+
+dnl ----------------------------------------------------------------
+dnl Checks for libraries
+
+AC_CHECK_FUNCS( sigaction sigset )
+AC_CHECK_FUNCS( fmemopen )
+
+dnl HP-UX requires -lV3
+dnl this is not needed on newer versions of HP-UX
+if test $ac_cv_func_sigaction = no && test $ac_cv_func_sigaction = no ; then
+ AC_CHECK_LIB(V3, sigset)
+fi
+
+if test $ol_cv_msvc = yes ; then
+ ol_cv_winsock=yes
+fi
+
+dnl The following is INTENTIONALLY scripted out because shell does not
+dnl support variable names with the '@' character, which is what
+dnl autoconf would try to generate if one merely used AC_SEARCH_LIBS
+if test "$ac_cv_header_winsock_h" = yes; then
+ AC_CACHE_CHECK([for winsock], [ol_cv_winsock],[
+ save_LIBS="$LIBS"
+ for curlib in none ws2_32 wsock32; do
+ if test $curlib != none ; then
+ LIBS="$save_LIBS -l$curlib"
+ fi
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <winsock.h>
+ ]], [[
+ socket(0,0,0);
+ select(0,NULL,NULL,NULL,NULL);
+ closesocket(0);
+ gethostname(NULL,0);
+ ]])],[ol_cv_winsock=$curlib],[ol_cv_winsock=no])
+
+ test "$ol_cv_winsock" != no && break
+ done
+ LIBS="$save_LIBS"
+ ])
+
+ if test $ol_cv_winsock != no ; then
+ AC_DEFINE(HAVE_WINSOCK, 1, [define if you have winsock])
+ ac_cv_func_socket=yes
+ ac_cv_func_select=yes
+ ac_cv_func_closesocket=yes
+ ac_cv_func_gethostname=yes
+
+ if test $ol_cv_winsock != none -a $ol_cv_winsock != yes ; then
+ LIBS="$LIBS -l$ol_cv_winsock"
+ fi
+
+ if test $ol_cv_winsock = ws2_32 -o $ol_cv_winsock = yes ; then
+ AC_DEFINE(HAVE_WINSOCK2, 1, [define if you have winsock2])
+ fi
+ fi
+fi
+
+dnl Find socket()
+dnl Likely combinations:
+dnl -lsocket [ -lnsl_s | -lnsl ]
+dnl -linet
+
+AC_CHECK_FUNC(socket, :, [
+dnl hopefully we won't include too many libraries
+ AC_CHECK_LIB(socket, main)
+ AC_CHECK_LIB(net, socket)
+ AC_CHECK_LIB(nsl_s, main)
+ AC_CHECK_LIB(nsl, main)
+ AC_CHECK_LIB(inet, socket)
+ AC_CHECK_LIB(gen, main)
+])
+
+dnl require select
+AC_CHECK_FUNC(select, :, AC_MSG_ERROR([select() required.]))
+
+if test "${ac_cv_header_winsock_h}" != yes; then
+ dnl Select arg types
+ dnl (if this detection becomes permanent, it and the select() detection
+ dnl should be done before the yielding select test)
+ AC_FUNC_SELECT_ARGTYPES
+fi
+
+dnl check to see if system call automatically restart
+dnl AC_SYS_RESTARTABLE_SYSCALLS
+
+dnl ----------------------------------------------------------------
+AC_CHECK_FUNCS( poll )
+if test $ac_cv_func_poll = yes; then
+AC_CHECK_HEADERS( poll.h sys/poll.h )
+fi
+
+dnl ----------------------------------------------------------------
+AC_CHECK_HEADERS( sys/epoll.h )
+if test "${ac_cv_header_sys_epoll_h}" = yes; then
+ AC_MSG_CHECKING(for epoll system call)
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[int main(int argc, char **argv)
+{
+ int epfd = epoll_create(256);
+ exit (epfd == -1 ? 1 : 0);
+}]])],[AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_EPOLL,1, [define if your system supports epoll])],[AC_MSG_RESULT(no)],[AC_MSG_RESULT(no)])
+fi
+
+dnl ----------------------------------------------------------------
+AC_CHECK_HEADERS( sys/event.h )
+if test "${ac_cv_header_sys_event_h}" = yes; then
+AC_MSG_CHECKING(for kqueue system call)
+AC_RUN_IFELSE([AC_LANG_SOURCE([[$ac_includes_default
+#ifdef HAVE_SYS_EVENT_H
+#include <sys/event.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+int main(int argc, char **argv)
+{
+ int kqfd = kqueue();
+ exit (kqfd == -1 ? 1 : 0);
+}]])],[AC_MSG_RESULT(yes)
+AC_DEFINE(HAVE_KQUEUE,1, [define if your system supports kqueue])],[AC_MSG_RESULT(no)],[AC_MSG_RESULT(no)])
+fi
+
+dnl ----------------------------------------------------------------
+AC_CHECK_HEADERS( sys/devpoll.h )
+dnl "/dev/poll" needs <sys/poll.h> as well...
+if test "${ac_cv_header_sys_devpoll_h}" = yes \
+ -a "${ac_cv_header_poll_h}" = yes ; \
+then
+ AC_MSG_CHECKING(for /dev/poll)
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[int main(int argc, char **argv)
+{
+ int devpollfd = open("/dev/poll", /* O_RDWR */ 2);
+ exit (devpollfd == -1 ? 1 : 0);
+}]])],[AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_DEVPOLL,1, [define if your system supports /dev/poll])],[AC_MSG_RESULT(no)],[AC_MSG_RESULT(no)])
+fi
+
+dnl ----------------------------------------------------------------
+OL_STRERROR
+
+dnl ----------------------------------------------------------------
+dnl require POSIX regex
+AC_CHECK_HEADERS( regex.h, [], [],
+[$ac_includes_default
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+])
+
+if test "$ac_cv_header_regex_h" != yes ; then
+ AC_MSG_ERROR([POSIX regex.h required.])
+fi
+AC_SEARCH_LIBS(regfree, [regex gnuregex],
+ :, AC_MSG_ERROR([POSIX regex required.]))
+
+OL_POSIX_REGEX
+if test "$ol_cv_c_posix_regex" = no ; then
+ AC_MSG_ERROR([broken POSIX regex!])
+fi
+
+dnl ----------------------------------------------------------------
+dnl UUID Support
+
+have_uuid=no
+AC_CHECK_HEADERS(sys/uuid.h)
+dnl The HAVE_UUID_TO_STR code path also needs uuid_create
+if test $ac_cv_header_sys_uuid_h = yes ; then
+ save_LIBS="$LIBS"
+ AC_SEARCH_LIBS([uuid_to_str], [uuid], [have_uuid=yes], :)
+ AC_SEARCH_LIBS([uuid_create], [uuid], :, [have_uuid=no])
+ LIBS="$save_LIBS"
+
+ if test $have_uuid = yes ; then
+ AC_DEFINE(HAVE_UUID_TO_STR,1,
+ [define if you have uuid_to_str()])
+
+ test "$ac_cv_search_uuid_to_str" = "none required" || \
+ LUTIL_LIBS="$LUTIL_LIBS $ac_cv_search_uuid_to_str"
+ fi
+fi
+
+dnl Look for uuid_generate
+dnl The HAVE_UUID_GENERATE code path also needs uuid_unparse_lower
+if test $have_uuid = no ; then
+ AC_CHECK_HEADERS(uuid/uuid.h)
+ if test $ac_cv_header_uuid_uuid_h = yes ; then
+ save_LIBS="$LIBS"
+ AC_SEARCH_LIBS([uuid_generate], [uuid], [have_uuid=yes], :)
+ AC_SEARCH_LIBS([uuid_unparse_lower], [uuid], :, [have_uuid=no])
+ LIBS="$save_LIBS"
+
+ if test $have_uuid = yes ; then
+ AC_DEFINE(HAVE_UUID_GENERATE,1,
+ [define if you have uuid_generate()])
+
+ test "$ac_cv_search_uuid_generate" = "none required" || \
+ LUTIL_LIBS="$LUTIL_LIBS $ac_cv_search_uuid_generate"
+ fi
+ fi
+fi
+
+dnl For windows, check for the need of RPCRT for UUID function support
+if test $have_uuid = no ; then
+ AC_MSG_CHECKING(to see if -lrpcrt4 is needed for win32 UUID support)
+ save_LIBS="$LIBS"
+ LIBS="$LIBS -lrpcrt4"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+ int __stdcall UuidCreate(void *);
+ int __stdcall UuidToStringA(void *,void **);
+ ]], [[
+ UuidCreate(0);
+ UuidToStringA(0,0);
+ ]])],[need_rpcrt=yes],[need_rpcrt=no])
+ if test $need_rpcrt = yes; then
+ SLAPD_LIBS="$SLAPD_LIBS -lrpcrt4"
+ CLIENT_LIBS="$CLIENT_LIBS -lrpcrt4"
+ fi
+ LIBS="$save_LIBS"
+ AC_MSG_RESULT($need_rpcrt)
+fi
+
+dnl ----------------------------------------------------------------
+dnl Check for resolver routines
+OL_RESOLVER_LINK
+
+ol_link_dnssrv=no
+if test "$ol_cv_lib_resolver" != no ; then
+ AC_DEFINE(HAVE_RES_QUERY,1,
+ [define if you have res_query()])
+
+ if test "$ol_enable_dnssrv" != no ; then
+ ol_link_dnssrv=yes
+ fi
+
+ if test "$ol_cv_lib_resolver" != yes ; then
+ LIBS="$ol_cv_lib_resolver $LIBS"
+ fi
+fi
+
+if test "$ol_enable_dnssrv" = yes || test "$ol_enable_dnssrv" = mod ; then
+ if test "$ol_link_dnssrv" = no ; then
+ AC_MSG_ERROR([DNSSRV requires res_query()])
+ fi
+else
+ ol_enable_dnssrv=no
+fi
+
+AC_CHECK_FUNCS( hstrerror )
+
+dnl ----------------------------------------------------------------
+dnl PF_INET6 support requires getaddrinfo and INET6_ADDRSTRLEN
+dnl PF_LOCAL may use getaddrinfo in available
+AC_CHECK_FUNCS( getaddrinfo getnameinfo gai_strerror inet_ntop )
+
+ol_link_ipv6=no
+if test $ac_cv_func_getaddrinfo = no || test $ac_cv_func_inet_ntop = no ; then
+ if test $ol_enable_ipv6 = yes ; then
+ AC_MSG_ERROR([IPv6 support requires getaddrinfo() and inet_ntop()])
+ fi
+elif test $ol_enable_ipv6 != no ; then
+ AC_CACHE_CHECK([INET6_ADDRSTRLEN],[ol_cv_inet6_addrstrlen],[
+ AC_EGREP_CPP(__has_inet6_addrstrlen__,[
+# include <netinet/in.h>
+# ifdef INET6_ADDRSTRLEN
+ __has_inet6_addrstrlen__;
+# endif
+ ], [ol_cv_inet6_addrstrlen=yes], [ol_cv_inet6_addrstrlen=no])])
+
+
+ AC_CACHE_CHECK([struct sockaddr_storage],ol_cv_struct_sockaddr_storage,[
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sys/types.h>
+#include <sys/socket.h>
+]], [[
+ struct sockaddr_storage ss;
+]])],[ol_cv_struct_sockaddr_storage=yes],[ol_cv_struct_sockaddr_storage=no])])
+
+ if test $ol_cv_inet6_addrstrlen = yes &&
+ test $ol_cv_struct_sockaddr_storage = yes ; then
+ ol_link_ipv6=yes
+ elif test $ol_enable_ipv6 = yes &&
+ test $ol_cv_inet6_addrstrlen = no ; then
+ AC_MSG_ERROR([IPv6 support requires INET6_ADDRSTRLEN])
+ elif test $ol_enable_ipv6 = yes &&
+ test $ol_cv_struct_sockaddr_storage = no ; then
+ AC_MSG_ERROR([IPv6 support requires struct sockaddr_storage])
+ fi
+fi
+
+if test $ol_enable_local != no ; then
+ AC_CHECK_HEADERS( sys/un.h )
+
+ if test $ol_enable_local = auto ; then
+ ol_enable_local=$ac_cv_header_sys_un_h
+ elif test $ac_cv_header_sys_un_h = no ; then
+ AC_MSG_ERROR([AF_LOCAL domain support requires sys/un.h])
+ fi
+fi
+
+dnl ----------------------------------------------------------------
+dnl TLS/SSL
+
+if test $ol_with_tls = yes ; then
+ ol_with_tls=auto
+fi
+
+ol_link_tls=no
+if test $ol_with_tls = openssl || test $ol_with_tls = auto ; then
+ AC_CHECK_HEADERS(openssl/ssl.h)
+
+ if test $ac_cv_header_openssl_ssl_h = yes ; then
+ AC_PREPROC_IFELSE([AC_LANG_SOURCE(
+ [[#include <openssl/opensslv.h>]
+[#if OPENSSL_VERSION_NUMBER < 0x1010100fL]
+[#error "OpenSSL is too old"]
+[#endif]])],
+ , [AC_MSG_FAILURE([OpenSSL 1.1.1 or newer required])])
+
+ AC_CHECK_LIB(ssl, SSL_export_keying_material_early,
+ [have_openssl=yes], [have_openssl=no],
+ [-lcrypto])
+
+ if test $have_openssl = yes ; then
+ ol_with_tls=openssl
+ ol_link_tls=yes
+ WITH_TLS_TYPE=openssl
+
+ AC_DEFINE(HAVE_OPENSSL, 1,
+ [define if you have OpenSSL])
+
+ TLS_LIBS="-lssl -lcrypto"
+ fi
+ fi
+fi
+
+if test $ol_link_tls = no ; then
+ if test $ol_with_tls = gnutls || test $ol_with_tls = auto ; then
+ AC_CHECK_HEADERS(gnutls/gnutls.h)
+
+ if test $ac_cv_header_gnutls_gnutls_h = yes ; then
+ AC_PREPROC_IFELSE([AC_LANG_SOURCE(
+ [[#include <gnutls/gnutls.h>]
+[#if GNUTLS_VERSION_NUMBER < 0x030306]
+[#error "GnuTLS is too old"]
+[#endif]])],
+ , [AC_MSG_FAILURE([GnuTLS 3.3.6 or newer required])])
+
+ AC_CHECK_LIB(gnutls, gnutls_init,
+ [have_gnutls=yes], [have_gnutls=no])
+
+ if test $have_gnutls = yes ; then
+ ol_with_tls=gnutls
+ ol_link_tls=yes
+ WITH_TLS_TYPE=gnutls
+
+ TLS_LIBS="-lgnutls"
+
+ AC_DEFINE(HAVE_GNUTLS, 1,
+ [define if you have GNUtls])
+ fi
+ fi
+ fi
+fi
+
+WITH_TLS=no
+if test $ol_link_tls = yes ; then
+ AC_DEFINE(HAVE_TLS, 1, [define if you have TLS])
+ WITH_TLS=yes
+elif test $ol_with_tls = auto ; then
+ AC_MSG_WARN([Could not locate TLS/SSL package])
+ AC_MSG_WARN([TLS data protection not supported!])
+elif test $ol_with_tls != no ; then
+ AC_MSG_ERROR([Could not locate TLS/SSL package])
+else
+ AC_MSG_WARN([TLS data protection not supported!])
+fi
+
+
+dnl ----------------------------------------------------------------
+dnl Threads?
+ol_link_threads=no
+
+case $ol_with_threads in auto | yes | nt)
+
+ OL_NT_THREADS
+
+ if test "$ol_cv_nt_threads" = yes ; then
+ ol_link_threads=nt
+ ol_with_threads=found
+ ol_with_yielding_select=yes
+
+ AC_DEFINE(HAVE_NT_SERVICE_MANAGER,1,[if you have NT Service Manager])
+ AC_DEFINE(HAVE_NT_EVENT_LOG,1,[if you have NT Event Log])
+ fi
+
+ if test $ol_with_threads = nt ; then
+ AC_MSG_ERROR([could not locate NT Threads])
+ fi
+ ;;
+esac
+
+case $ol_with_threads in auto | yes | posix)
+
+ AC_CHECK_HEADERS(pthread.h)
+
+ if test $ac_cv_header_pthread_h = yes ; then
+ OL_POSIX_THREAD_VERSION
+
+ if test $ol_cv_pthread_version != 0 ; then
+ AC_DEFINE_UNQUOTED(HAVE_PTHREADS,$ol_cv_pthread_version,
+ [define to pthreads API spec revision])
+ else
+ AC_MSG_ERROR([unknown pthread version])
+ fi
+
+ # consider threads found
+ ol_with_threads=found
+
+ OL_HEADER_LINUX_THREADS
+ OL_HEADER_GNU_PTH_PTHREAD_H
+
+ if test $ol_cv_header_gnu_pth_pthread_h = no ; then
+ AC_CHECK_HEADERS(sched.h)
+ fi
+
+ dnl Now the hard part, how to link?
+ dnl
+ dnl currently supported checks:
+ dnl
+ dnl Check for no flags
+ dnl pthread_create() in $LIBS
+ dnl
+ dnl Check special pthread (final) flags
+ dnl [skipped] pthread_create() with -mt (Solaris) [disabled]
+ dnl pthread_create() with -kthread (FreeBSD)
+ dnl pthread_create() with -pthread (FreeBSD/Digital Unix)
+ dnl pthread_create() with -pthreads (?)
+ dnl pthread_create() with -mthreads (AIX)
+ dnl pthread_create() with -thread (?)
+ dnl
+ dnl Check pthread (final) libraries
+ dnl pthread_mutex_unlock() in -lpthread -lmach -lexc -lc_r (OSF/1)
+ dnl pthread_mutex_lock() in -lpthread -lmach -lexc (OSF/1)
+ dnl [skipped] pthread_mutex_trylock() in -lpthread -lexc (OSF/1)
+ dnl pthread_join() -Wl,-woff,85 -lpthread (IRIX)
+ dnl pthread_create() in -lpthread (many)
+ dnl pthread_create() in -lc_r (FreeBSD)
+ dnl
+ dnl Check pthread (draft4) flags (depreciated)
+ dnl pthread_create() with -threads (OSF/1)
+ dnl
+ dnl Check pthread (draft4) libraries (depreciated)
+ dnl pthread_mutex_unlock() in -lpthreads -lmach -lexc -lc_r (OSF/1)
+ dnl pthread_mutex_lock() in -lpthreads -lmach -lexc (OSF/1)
+ dnl pthread_mutex_trylock() in -lpthreads -lexc (OSF/1)
+ dnl pthread_create() in -lpthreads (many)
+ dnl
+
+ dnl pthread_create in $LIBS
+ AC_CACHE_CHECK([for pthread_create in default libraries],
+ ol_cv_pthread_create,[
+ AC_RUN_IFELSE([OL_PTHREAD_TEST_PROGRAM],
+ [ol_cv_pthread_create=yes],
+ [ol_cv_pthread_create=no],
+ [AC_TRY_LINK(OL_PTHREAD_TEST_INCLUDES,OL_PTHREAD_TEST_FUNCTION,
+ [ol_cv_pthread_create=yes],
+ [ol_cv_pthread_create=no])])])
+
+ if test $ol_cv_pthread_create != no ; then
+ ol_link_threads=posix
+ ol_link_pthreads=""
+ fi
+
+dnl OL_PTHREAD_TRY([-mt], [ol_cv_pthread_mt])
+ OL_PTHREAD_TRY([-kthread], [ol_cv_pthread_kthread])
+ OL_PTHREAD_TRY([-pthread], [ol_cv_pthread_pthread])
+ OL_PTHREAD_TRY([-pthreads], [ol_cv_pthread_pthreads])
+ OL_PTHREAD_TRY([-mthreads], [ol_cv_pthread_mthreads])
+ OL_PTHREAD_TRY([-thread], [ol_cv_pthread_thread])
+
+ OL_PTHREAD_TRY([-lpthread -lmach -lexc -lc_r],
+ [ol_cv_pthread_lpthread_lmach_lexc_lc_r])
+ OL_PTHREAD_TRY([-lpthread -lmach -lexc],
+ [ol_cv_pthread_lpthread_lmach_lexc])
+dnl OL_PTHREAD_TRY([-lpthread -lexc],
+dnl [ol_cv_pthread_lpthread_lexc])
+
+ OL_PTHREAD_TRY([-lpthread -Wl,-woff,85],
+ [ol_cv_pthread_lib_lpthread_woff])
+
+ OL_PTHREAD_TRY([-lpthread], [ol_cv_pthread_lpthread])
+ OL_PTHREAD_TRY([-lc_r], [ol_cv_pthread_lc_r])
+
+ OL_PTHREAD_TRY([-threads], [ol_cv_pthread_threads])
+
+ OL_PTHREAD_TRY([-lpthreads -lmach -lexc -lc_r],
+ [ol_cv_pthread_lpthreads_lmach_lexc_lc_r])
+ OL_PTHREAD_TRY([-lpthreads -lmach -lexc],
+ [ol_cv_pthread_lpthreads_lmach_lexc])
+ OL_PTHREAD_TRY([-lpthreads -lexc],
+ [ol_cv_pthread_lpthreads_lexc])
+
+ OL_PTHREAD_TRY([-lpthreads],[ol_cv_pthread_lib_lpthreads])
+
+ if test $ol_link_threads != no ; then
+ LTHREAD_LIBS="$LTHREAD_LIBS $ol_link_pthreads"
+
+ dnl save flags
+ save_CPPFLAGS="$CPPFLAGS"
+ save_LIBS="$LIBS"
+ LIBS="$LTHREAD_LIBS $LIBS"
+
+ dnl All POSIX Thread (final) implementations should have
+ dnl sched_yield instead of pthread yield.
+ dnl check for both, and thr_yield for Solaris
+ AC_CHECK_FUNCS(sched_yield pthread_yield thr_yield)
+
+ if test $ac_cv_func_sched_yield = no &&
+ test $ac_cv_func_pthread_yield = no &&
+ test $ac_cv_func_thr_yield = no ; then
+ dnl Digital UNIX has sched_yield() in -lrt
+ AC_CHECK_LIB(rt, sched_yield,
+ [LTHREAD_LIBS="$LTHREAD_LIBS -lrt"
+ AC_DEFINE(HAVE_SCHED_YIELD,1,
+ [Define if you have the sched_yield function.])
+ ac_cv_func_sched_yield=yes],
+ [ac_cv_func_sched_yield=no])
+ fi
+ if test $ac_cv_func_sched_yield = no &&
+ test $ac_cv_func_pthread_yield = no &&
+ test "$ac_cv_func_thr_yield" = no ; then
+ AC_MSG_WARN([could not locate sched_yield() or pthread_yield()])
+ fi
+
+ dnl Check functions for compatibility
+ AC_CHECK_FUNCS(pthread_kill)
+
+ dnl Check for pthread_rwlock_destroy with <pthread.h>
+ dnl as pthread_rwlock_t may not be defined.
+ AC_CACHE_CHECK([for pthread_rwlock_destroy with <pthread.h>],
+ [ol_cv_func_pthread_rwlock_destroy], [
+ dnl save the flags
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#include <pthread.h>
+pthread_rwlock_t rwlock;
+]], [[pthread_rwlock_destroy(&rwlock);]])],[ol_cv_func_pthread_rwlock_destroy=yes],[ol_cv_func_pthread_rwlock_destroy=no])
+ ])
+ if test $ol_cv_func_pthread_rwlock_destroy = yes ; then
+ AC_DEFINE(HAVE_PTHREAD_RWLOCK_DESTROY,1,
+ [define if you have pthread_rwlock_destroy function])
+ fi
+
+ dnl Check for pthread_detach with <pthread.h> inclusion
+ dnl as it's symbol may have been mangled.
+ AC_CACHE_CHECK([for pthread_detach with <pthread.h>],
+ [ol_cv_func_pthread_detach], [
+ dnl save the flags
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#include <pthread.h>
+#ifndef NULL
+#define NULL (void*)0
+#endif
+]], [[pthread_detach(NULL);]])],[ol_cv_func_pthread_detach=yes],[ol_cv_func_pthread_detach=no])
+ ])
+
+ if test $ol_cv_func_pthread_detach = no ; then
+ AC_MSG_ERROR([could not locate pthread_detach()])
+ fi
+
+ AC_DEFINE(HAVE_PTHREAD_DETACH,1,
+ [define if you have pthread_detach function])
+
+ dnl Check for setconcurrency functions
+ AC_CHECK_FUNCS( \
+ pthread_setconcurrency \
+ pthread_getconcurrency \
+ thr_setconcurrency \
+ thr_getconcurrency \
+ )
+
+ OL_SYS_LINUX_THREADS
+ OL_LINUX_THREADS
+
+ if test $ol_cv_linux_threads = error; then
+ AC_MSG_ERROR([LinuxThreads header/library mismatch]);
+ fi
+
+ AC_CACHE_CHECK([AC_LANG_SOURCE([if pthread_create() works])],
+ ol_cv_pthread_create_works,[
+ AC_RUN_IFELSE([OL_PTHREAD_TEST_PROGRAM],
+ [ol_cv_pthread_create_works=yes],
+ [ol_cv_pthread_create_works=no],
+ [dnl assume yes
+ ol_cv_pthread_create_works=yes])])
+
+ if test $ol_cv_pthread_create_works = no ; then
+ AC_MSG_ERROR([pthread_create is not usable, check environment settings])
+ fi
+
+ ol_replace_broken_yield=no
+dnl case "$target" in
+dnl *-*-linux*)
+dnl AC_CHECK_FUNCS(nanosleep)
+dnl ol_replace_broken_yield=yes
+dnl ;;
+dnl esac
+
+ if test $ol_replace_broken_yield = yes ; then
+ AC_DEFINE([REPLACE_BROKEN_YIELD],1,
+ [define if sched_yield yields the entire process])
+ fi
+
+ dnl Check if select causes an yield
+ if test $ol_with_yielding_select = auto ; then
+ AC_CACHE_CHECK([if select yields when using pthreads],
+ ol_cv_pthread_select_yields,[
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <pthread.h>
+#ifndef NULL
+#define NULL (void*) 0
+#endif
+
+static int fildes[2];
+
+static void *task(p)
+ void *p;
+{
+ int i;
+ struct timeval tv;
+
+ fd_set rfds;
+
+ tv.tv_sec=10;
+ tv.tv_usec=0;
+
+ FD_ZERO(&rfds);
+ FD_SET(fildes[0], &rfds);
+
+ /* we're not interested in any fds */
+ i = select(FD_SETSIZE, &rfds, NULL, NULL, &tv);
+
+ if(i < 0) {
+ perror("select");
+ exit(10);
+ }
+
+ exit(0); /* if we exit here, the select blocked the whole process */
+}
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+ pthread_t t;
+
+ /* create a pipe to select */
+ if(pipe(&fildes[0])) {
+ perror("select");
+ exit(1);
+ }
+
+#ifdef HAVE_PTHREAD_SETCONCURRENCY
+ (void) pthread_setconcurrency(2);
+#else
+#ifdef HAVE_THR_SETCONCURRENCY
+ /* Set Solaris LWP concurrency to 2 */
+ thr_setconcurrency(2);
+#endif
+#endif
+
+#if HAVE_PTHREADS < 6
+ pthread_create(&t, pthread_attr_default, task, NULL);
+#else
+ pthread_create(&t, NULL, task, NULL);
+#endif
+
+ /* make sure task runs first */
+#ifdef HAVE_THR_YIELD
+ thr_yield();
+#elif defined( HAVE_SCHED_YIELD )
+ sched_yield();
+#elif defined( HAVE_PTHREAD_YIELD )
+ pthread_yield();
+#endif
+
+ exit(2);
+}]])],[ol_cv_pthread_select_yields=no],[ol_cv_pthread_select_yields=yes],[ol_cv_pthread_select_yields=cross])])
+
+ if test $ol_cv_pthread_select_yields = cross ; then
+ AC_MSG_ERROR([crossing compiling: use --with-yielding_select=yes|no|manual])
+ fi
+
+ if test $ol_cv_pthread_select_yields = yes ; then
+ ol_with_yielding_select=yes
+ fi
+ fi
+
+ dnl restore flags
+ CPPFLAGS="$save_CPPFLAGS"
+ LIBS="$save_LIBS"
+ else
+ AC_MSG_ERROR([could not locate usable POSIX Threads])
+ fi
+ fi
+
+ if test $ol_with_threads = posix ; then
+ AC_MSG_ERROR([could not locate POSIX Threads])
+ fi
+ ;;
+esac
+
+case $ol_with_threads in auto | yes | pth)
+
+ AC_CHECK_HEADERS(pth.h)
+
+ if test $ac_cv_header_pth_h = yes ; then
+ AC_CHECK_LIB(pth, pth_version, [have_pth=yes], [have_pth=no])
+
+ if test $have_pth = yes ; then
+ AC_DEFINE(HAVE_GNU_PTH,1,[if you have GNU Pth])
+ LTHREAD_LIBS="$LTHREAD_LIBS -lpth"
+ ol_link_threads=pth
+ ol_with_threads=found
+
+ if test $ol_with_yielding_select = auto ; then
+ ol_with_yielding_select=yes
+ fi
+ fi
+ fi
+ ;;
+esac
+
+case $ol_with_threads in auto | yes | lwp)
+
+ dnl check for SunOS5 LWP
+ AC_CHECK_HEADERS(thread.h synch.h)
+ if test $ac_cv_header_thread_h = yes &&
+ test $ac_cv_header_synch_h = yes ; then
+ AC_CHECK_LIB(thread, thr_create, [have_thr=yes], [have_thr=no])
+
+ if test $have_thr = yes ; then
+ AC_DEFINE(HAVE_THR,1,
+ [if you have Solaris LWP (thr) package])
+ LTHREAD_LIBS="$LTHREAD_LIBS -lthread"
+ ol_link_threads=thr
+
+ if test $ol_with_yielding_select = auto ; then
+ ol_with_yielding_select=yes
+ fi
+
+ dnl Check for setconcurrency functions
+ AC_CHECK_FUNCS( \
+ thr_setconcurrency \
+ thr_getconcurrency \
+ )
+ fi
+ fi
+ ;;
+esac
+
+if test $ol_with_yielding_select = yes ; then
+ AC_DEFINE(HAVE_YIELDING_SELECT,1,
+ [define if select implicitly yields])
+fi
+
+if test $ol_with_threads = manual ; then
+ dnl User thinks he can manually configure threads.
+ ol_link_threads=yes
+
+ AC_MSG_WARN([thread defines and link options must be set manually])
+
+ AC_CHECK_HEADERS(pthread.h sched.h)
+ AC_CHECK_FUNCS(sched_yield pthread_yield)
+ OL_HEADER_LINUX_THREADS
+
+ AC_CHECK_HEADERS(thread.h synch.h)
+fi
+
+if test $ol_link_threads != no && test $ol_link_threads != nt ; then
+ dnl needed to get reentrant/threadsafe versions
+ dnl
+ AC_DEFINE(REENTRANT,1)
+ AC_DEFINE(_REENTRANT,1)
+ AC_DEFINE(THREAD_SAFE,1)
+ AC_DEFINE(_THREAD_SAFE,1)
+ AC_DEFINE(THREADSAFE,1)
+ AC_DEFINE(_THREADSAFE,1)
+ AC_DEFINE(_SGI_MP_SOURCE,1)
+
+ dnl The errno declaration may dependent upon _REENTRANT.
+ dnl If it does, we must link with thread support.
+ AC_CACHE_CHECK([for thread specific errno],
+ [ol_cv_errno_thread_specific], [
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <errno.h>]], [[errno = 0;]])],[ol_cv_errno_thread_specific=yes],[ol_cv_errno_thread_specific=no])
+ ])
+
+ dnl The h_errno declaration may dependent upon _REENTRANT.
+ dnl If it does, we must link with thread support.
+ AC_CACHE_CHECK([for thread specific h_errno],
+ [ol_cv_h_errno_thread_specific], [
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <netdb.h>]], [[h_errno = 0;]])],[ol_cv_h_errno_thread_specific=yes],[ol_cv_h_errno_thread_specific=no])
+ ])
+
+ if test $ol_cv_errno_thread_specific != yes ||
+ test $ol_cv_h_errno_thread_specific != yes ; then
+ LIBS="$LTHREAD_LIBS $LIBS"
+ LTHREAD_LIBS=""
+ fi
+
+dnl When in thread environment, use
+dnl #if defined( HAVE_REENTRANT_FUNCTIONS ) || defined( HAVE_FUNC_R )
+dnl func_r(...);
+dnl #else
+dnl /* lock */
+dnl func(...);
+dnl /* unlock */
+dnl #endif
+dnl
+dnl HAVE_REENTRANT_FUNCTIONS is derived from:
+dnl _POSIX_REENTRANT_FUNCTIONS
+dnl _POSIX_THREAD_SAFE_FUNCTIONS
+dnl _POSIX_THREADSAFE_FUNCTIONS
+dnl
+dnl and is currently defined in <ldap_pvt_thread.h>
+dnl
+dnl libldap/*.c should only include <ldap_pvt_thread.h> iff
+dnl LDAP_R_COMPILE is defined. ie:
+dnl #ifdef LDAP_R_COMPILE
+dnl # include <ldap_pvt_thread.h>
+dnl #endif
+dnl
+dnl LDAP_R_COMPILE is defined by libldap/ldap-int.h
+dnl specifically for compiling the threadsafe version of
+dnl the ldap library.
+dnl
+dnl dnl check for reentrant/threadsafe functions
+dnl dnl
+dnl dnl note: these should only be used when linking
+dnl dnl with $LTHREAD_LIBS
+dnl dnl
+dnl save_CPPFLAGS="$CPPFLAGS"
+dnl save_LIBS="$LIBS"
+dnl LIBS="$LTHREAD_LIBS $LIBS"
+dnl AC_CHECK_FUNCS( \
+dnl gmtime_r \
+dnl gethostbyaddr_r gethostbyname_r \
+dnl feof_unlocked unlocked_feof \
+dnl putc_unlocked unlocked_putc \
+dnl flockfile ftrylockfile \
+dnl )
+dnl CPPFLAGS="$save_CPPFLAGS"
+dnl LIBS="$save_LIBS"
+fi
+
+if test $ol_link_threads = no ; then
+ if test $ol_enable_slapd != no; then
+ AC_MSG_ERROR([slapd requires thread support])
+ fi
+
+ if test $ol_with_threads = yes ; then
+ AC_MSG_ERROR([no suitable thread support])
+ fi
+
+ if test $ol_with_threads = auto ; then
+ AC_MSG_WARN([no suitable thread support, disabling threads])
+ ol_with_threads=no
+ fi
+
+ AC_DEFINE(NO_THREADS,1,
+ [define if you have (or want) no threads])
+ LTHREAD_LIBS=""
+ BUILD_THREAD=no
+else
+ BUILD_THREAD=yes
+fi
+
+if test $ol_link_threads != no ; then
+ AC_DEFINE(LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE,1,
+ [define to 1 if library is thread safe])
+
+ dnl This could be enabled without threads if all of the
+ dnl reentrant functions are available. Needs testing.
+ AC_DEFINE(LDAP_API_FEATURE_X_OPENLDAP_REENTRANT,1,
+ [define to 1 if library is reentrant])
+fi
+
+dnl ----------------------------------------------------------------
+dnl Tests for reentrant functions necessary for reentrant build
+AC_CHECK_FUNCS( \
+ ctime_r \
+ gmtime_r localtime_r \
+ gethostbyname_r gethostbyaddr_r \
+)
+
+if test "$ac_cv_func_ctime_r" = no ; then
+ ol_cv_func_ctime_r_nargs=0
+else
+ OL_FUNC_CTIME_R_NARGS
+dnl OL_FUNC_CTIME_R_TYPE
+fi
+
+if test "$ac_cv_func_gethostbyname_r" = yes ; then
+ OL_FUNC_GETHOSTBYNAME_R_NARGS
+else
+ ol_cv_func_gethostbyname_r_nargs=0
+fi
+
+if test "$ac_cv_func_gethostbyaddr_r" = yes ; then
+ OL_FUNC_GETHOSTBYADDR_R_NARGS
+else
+ ol_cv_func_gethostbyaddr_r_nargs=0
+fi
+
+dnl ----------------------------------------------------------------
+
+if test $ol_enable_dynamic = yes && test $enable_shared = yes ; then
+ BUILD_LIBS_DYNAMIC=shared
+ AC_DEFINE(LDAP_LIBS_DYNAMIC, 1, [define if LDAP libs are dynamic])
+ LTSTATIC=""
+else
+ BUILD_LIBS_DYNAMIC=static
+ LTSTATIC="-static"
+fi
+AC_SUBST(LTSTATIC)dnl
+
+dnl ----------------------------------------------------------------
+if test $ol_enable_wrappers != no ; then
+ AC_CHECK_HEADERS(tcpd.h,[
+ AC_MSG_CHECKING([for TCP wrappers library])
+ save_LIBS="$LIBS"
+ LIBS="$LIBS -lwrap"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+#include <tcpd.h>
+int allow_severity = 0;
+int deny_severity = 0;
+
+struct request_info *req;
+ ]], [[
+hosts_access(req)
+ ]])],[AC_MSG_RESULT([-lwrap])
+ have_wrappers=yes
+ LIBS="$save_LIBS"],[
+ dnl try with -lnsl
+ LIBS="$LIBS -lnsl"
+ AC_TRY_LINK([
+#include <tcpd.h>
+int allow_severity = 0;
+int deny_severity = 0;
+
+struct request_info *req;
+ ],[
+hosts_access(req)
+ ],[AC_MSG_RESULT([-lwrap -lnsl])
+ have_wrappers=yes
+ LIBS="$save_LIBS -lnsl"],[
+ AC_MSG_RESULT(no)
+ have_wrappers=no
+ LIBS=$save_LIBS])])],[have_wrappers=no])
+
+ if test $have_wrappers = yes ; then
+ AC_DEFINE(HAVE_TCPD,1, [define if you have -lwrap])
+ WRAP_LIBS="-lwrap"
+ elif test $ol_enable_wrappers = yes ; then
+ AC_MSG_ERROR([could not find TCP wrappers, select appropriate options or disable])
+ else
+ AC_MSG_WARN([could not find TCP wrappers, support disabled])
+ WRAP_LIBS=""
+ fi
+fi
+
+dnl ----------------------------------------------------------------
+if test $ol_enable_syslog != no ; then
+ AC_CHECK_FUNC(openlog)
+ if test $ac_cv_func_openlog = no && test $ol_enable_syslog = yes; then
+ AC_MSG_ERROR(could not find syslog, select appropriate options or disable)
+ fi
+ ol_enable_syslog=$ac_cv_func_openlog
+fi
+
+dnl ----------------------------------------------------------------
+dnl SQL
+ol_link_sql=no
+if test $ol_enable_sql != no ; then
+ AC_CHECK_HEADERS(sql.h sqlext.h,[],[
+ AC_MSG_ERROR([could not locate SQL headers])
+ ])
+
+ sql_LIBS="$LIBS"
+ LIBS="$LTHREAD_LIBS $LIBS"
+
+ if test $ol_with_odbc = auto ; then
+ ol_with_odbc="iodbc unixodbc odbc32"
+ fi
+
+ for odbc in $ol_with_odbc ; do
+ if test $ol_link_sql = no ; then
+ case $odbc in
+ iodbc)
+ AC_CHECK_LIB(iodbc, SQLDriverConnect, [have_iodbc=yes], [have_iodbc=no])
+ if test $have_iodbc = yes ; then
+ ol_link_sql="-liodbc"
+ fi
+ ;;
+
+ unixodbc)
+ AC_CHECK_LIB(odbc, SQLDriverConnect, [have_odbc=yes], [have_odbc=no])
+ if test $have_odbc = yes ; then
+ ol_link_sql="-lodbc"
+ fi
+ ;;
+
+ odbc32)
+ AC_CHECK_LIB(odbc32, SQLDriverConnect, [have_odbc32=yes], [have_odbc32=no])
+
+ dnl The windows API uses __stdcall which cannot be detected by AC_CHECK_LIB
+ if test $have_odbc32 = no ; then
+ AC_MSG_CHECKING([for SQLDriverConnect in -lodbc32 with windows.h])
+ save_LIBS="$LIBS"
+ LIBS="$LIBS -lodbc32"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <windows.h>
+ #include <sqlext.h>
+ ]], [[
+ SQLDriverConnect(NULL,NULL,NULL,0,NULL,0,NULL,0);
+ ]])],[have_odbc32=yes], [have_odbc32=no])
+ LIBS="$save_LIBS"
+ AC_MSG_RESULT($have_odbc32)
+ fi
+
+ if test $have_odbc32 = yes ; then
+ ol_link_sql="-lodbc32"
+ fi
+ ;;
+
+ *)
+ AC_MSG_ERROR([unknown ODBC library])
+ ;;
+ esac
+ fi
+ done
+
+ LIBS="$sql_LIBS"
+
+ if test $ol_link_sql != no ; then
+ SLAPD_SQL_LIBS="$ol_link_sql"
+
+ elif test $ol_enable_sql != auto ; then
+ AC_MSG_ERROR([could not locate suitable ODBC library])
+ fi
+fi
+
+dnl ----------------------------------------------------------------
+dnl MySQL NDBapi
+dnl Note: uses C++, but we don't want to add C++ test overhead to
+dnl the rest of the libtool machinery.
+ol_link_ndb=no
+if test $ol_enable_ndb != no ; then
+ AC_CHECK_PROG(MYSQL,mysql_config,yes)
+ if test "$MYSQL" != yes ; then
+ AC_MSG_ERROR([could not locate mysql_config])
+ fi
+
+ SQL_INC=`mysql_config --include`
+ SLAPD_NDB_INCS="$SQL_INC $SQL_INC/storage/ndb $SQL_INC/storage/ndb/ndbapi"
+
+ save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$SLAPD_NDB_INCS"
+ AC_MSG_CHECKING(for NdbApi.hpp)
+ AC_PREPROC_IFELSE(
+ [AC_LANG_SOURCE([[#include <NdbApi.hpp>]])],
+ AC_MSG_RESULT(yes),
+ AC_MSG_ERROR([could not locate NdbApi headers])
+ )
+ CPPFLAGS="$save_CPPFLAGS"
+
+ SQL_LIB=`mysql_config --libs_r`
+ SLAPD_NDB_LIBS="$SQL_LIB -lndbclient -lstdc++"
+
+ save_LDFLAGS="$LDFLAGS"
+ save_LIBS="$LIBS"
+ LDFLAGS="$SQL_LIB"
+ AC_CHECK_LIB(ndbclient,ndb_init,[: ok],[
+ AC_MSG_ERROR([could not locate ndbclient library])
+ ],[-lstdc++])
+ LIBS="$save_LIBS"
+ LDFLAGS="$save_LDFLAGS"
+
+ if test "$ol_enable_ndb" = yes ; then
+ SLAPD_LIBS="$SLAPD_LIBS \$(SLAPD_NDB_LIBS)"
+ fi
+fi
+
+dnl ----------------------------------------------------------------
+dnl WiredTiger
+ol_link_wt=no
+if test $ol_enable_wt != no ; then
+ PKG_CHECK_MODULES(WT, wiredtiger)
+ if test $ol_enable_wt = yes ; then
+ SLAPD_LIBS="$SLAPD_LIBS \$(WT_LIBS)"
+ fi
+ ol_link_wt=yes
+fi
+
+dnl ----------------------------------------------------------------
+dnl
+dnl Check for Cyrus SASL
+dnl
+WITH_SASL=no
+ol_link_sasl=no
+ol_link_spasswd=no
+if test $ol_with_cyrus_sasl != no ; then
+ AC_CHECK_HEADERS(sasl/sasl.h sasl.h)
+
+ if test $ac_cv_header_sasl_sasl_h = yes ||
+ test $ac_cv_header_sasl_h = yes; then
+ AC_CHECK_LIB(sasl2, sasl_client_init,
+ [ol_link_sasl="-lsasl2"],
+ [AC_CHECK_LIB(sasl, sasl_client_init,
+ [ol_link_sasl="-lsasl"])])
+ fi
+
+ if test $ol_link_sasl = no ; then
+ if test $ol_with_cyrus_sasl != auto ; then
+ AC_MSG_ERROR([Could not locate Cyrus SASL])
+ else
+ AC_MSG_WARN([Could not locate Cyrus SASL])
+ AC_MSG_WARN([SASL authentication not supported!])
+ if test $ol_link_tls = no ; then
+ AC_MSG_WARN([Strong authentication not supported!])
+ fi
+ fi
+ else
+ OL_SASL_COMPAT
+ if test $ol_cv_sasl_compat = no ; then
+ ol_link_sasl=no
+ AC_MSG_ERROR([Cyrus SASL library located but is incompatible])
+ fi
+
+ AC_DEFINE(HAVE_CYRUS_SASL,1,[define if you have Cyrus SASL])
+ SASL_LIBS="$ol_link_sasl"
+ if test $ol_enable_spasswd != no ; then
+ ol_link_spasswd=yes
+ fi
+
+ ac_save_LIBS="$LIBS"
+ LIBS="$LIBS $ol_link_sasl"
+ AC_CHECK_FUNC(sasl_version, [AC_DEFINE(HAVE_SASL_VERSION,1,
+ [define if your SASL library has sasl_version()])])
+ LIBS="$ac_save_LIBS"
+
+ WITH_SASL=yes
+ fi
+
+else
+ AC_MSG_WARN([SASL authentication not supported!])
+ if test $ol_link_tls = no ; then
+ AC_MSG_WARN([Strong authentication not supported!])
+ fi
+fi
+
+dnl ----------------------------------------------------------------
+dnl
+dnl Check for systemd (only if we have a server)
+dnl
+WITH_SYSTEMD=no
+systemdsystemunitdir=
+ol_link_systemd=no
+if test $ol_enable_slapd == no && test $ol_enable_balancer != yes ; then
+ if test $ol_with_systemd != no ; then
+ AC_MSG_WARN([servers disabled, ignoring --with-systemd=$ol_with_systemd argument])
+ ol_with_systemd=no
+ fi
+fi
+if test $ol_with_systemd != no ; then
+ AC_CHECK_HEADERS(systemd/sd-daemon.h)
+
+ if test $ac_cv_header_systemd_sd_daemon_h = yes; then
+ AC_CHECK_LIB(systemd, sd_notify,
+ [ol_link_systemd="-lsystemd"])
+ fi
+
+ if test $ol_link_systemd = no ; then
+ if test $ol_with_systemd != auto ; then
+ AC_MSG_ERROR([Could not locate systemd])
+ else
+ AC_MSG_WARN([Could not locate systemd])
+ AC_MSG_WARN([systemd service notification not supported!])
+ fi
+ else
+ AC_DEFINE(HAVE_SYSTEMD,1,[define if you have systemd])
+ SYSTEMD_LIBS="$ol_link_systemd"
+ WITH_SYSTEMD=yes
+
+ PKG_CHECK_VAR(systemdsystemunitdir, systemd, systemdsystemunitdir)
+ if test -z "$systemdsystemunitdir"; then
+ if test -d /usr/lib/systemd/system; then
+ systemdsystemunitdir=/usr/lib/systemd/system
+ else
+ systemdsystemunitdir=/lib/systemd/system
+ fi
+ fi
+ fi
+fi
+AC_SUBST(systemdsystemunitdir)
+
+dnl ----------------------------------------------------------------
+dnl Check for entropy sources
+if test $cross_compiling != yes && test "$ac_cv_mingw32" != yes ; then
+ dev=no
+ if test -r /dev/urandom ; then
+ dev="/dev/urandom";
+ elif test -r /idev/urandom ; then
+ dev="/idev/urandom";
+ elif test -r /dev/srandom ; then
+ dev="/dev/srandom";
+ elif test -r /dev/random ; then
+ dev="/dev/random";
+ elif test -r /idev/random ; then
+ dev="/idev/random";
+ fi
+
+ if test $dev != no ; then
+ AC_DEFINE_UNQUOTED(URANDOM_DEVICE,"$dev",[set to urandom device])
+ fi
+fi
+
+dnl ----------------------------------------------------------------
+dnl
+dnl Check for fetch URL support
+dnl should be extended to support other fetch URL APIs
+dnl
+ol_link_fetch=no
+if test $ol_with_fetch != no ; then
+ OL_LIB_FETCH
+
+ if test $ol_cv_lib_fetch != no ; then
+ LIBS="$LIBS $ol_link_fetch"
+ ol_link_fetch=freebsd
+
+ elif test $ol_with_fetch != auto ; then
+ AC_MSG_ERROR(no suitable API for --with-fetch=$ol_with_fetch)
+ fi
+fi
+
+dnl ----------------------------------------------------------------
+dnl FreeBSD (and others) have crypt(3) in -lcrypt
+if test $ol_enable_crypt != no ; then
+ save_LIBS="$LIBS"
+ LIBS="$TLS_LIBS $LIBS"
+
+ AC_CHECK_FUNC(crypt, [have_crypt=yes], [
+ LIBS="$save_LIBS"
+ AC_CHECK_LIB(crypt, crypt, [LUTIL_LIBS="$LUTIL_LIBS -lcrypt"
+ have_crypt=yes], [have_crypt=no])])
+
+ LIBS="$TLS_LIBS $LIBS"
+ AC_CHECK_LIB(crypt, crypt_r, [have_crypt_r=yes], [have_crypt_r=no])
+
+ LIBS="$save_LIBS"
+
+ if test $have_crypt = yes ; then
+ AC_DEFINE(HAVE_CRYPT,1, [define if crypt(3) is available])
+ if test $have_crypt_r = yes ; then
+ AC_DEFINE(HAVE_CRYPT_R, 1, [define if crypt_r() is also available])
+ fi
+ else
+ AC_MSG_WARN([could not find crypt])
+ if test $ol_enable_crypt = yes ; then
+ AC_MSG_ERROR([could not find crypt, select appropriate options or disable])
+ fi
+
+ AC_MSG_WARN([disabling crypt support])
+ ol_enable_crypt=no
+ fi
+fi
+
+dnl ----------------------------------------------------------------
+if test $ol_enable_slp != no ; then
+ AC_CHECK_HEADERS( slp.h )
+
+ if test $ac_cv_header_slp_h = yes ; then
+ AC_CHECK_LIB(slp, SLPOpen, [have_slp=yes], [have_slp=no])
+ if test $have_slp = yes ; then
+ AC_DEFINE(HAVE_SLP, 1, [define if you have -lslp])
+ SLAPD_SLP_LIBS=-lslp
+ fi
+
+ elif test $ol_enable_slp = yes ; then
+ AC_MSG_ERROR([SLP not found])
+ fi
+fi
+
+dnl ----------------------------------------------------------------
+dnl Libevent
+if test $ol_enable_balancer != no ; then
+ AC_CHECK_LIB(event_extra, evdns_base_new,
+ [have_libevent=yes
+ LEVENT_LIBS="$LEVENT_LIBS -levent_core -levent_extra"],
+ [AC_CHECK_LIB(event, evdns_base_new,
+ [have_libevent=yes
+ LEVENT_LIBS="$LEVENT_LIBS -levent"],
+ [have_libevent=no])])
+ AC_CHECK_LIB(event, libevent_global_shutdown, [], [have_libevent=no])
+
+ if test $have_libevent = yes ; then
+ AC_DEFINE(HAVE_LIBEVENT, 1, [define if you have -levent])
+ else
+ AC_MSG_ERROR([You need libevent 2.1 or later with DNS support to build the load balancer])
+ fi
+fi
+
+dnl ----------------------------------------------------------------
+dnl Checks for typedefs, structures, and compiler characteristics.
+
+AC_CHECK_TYPE(mode_t, int)
+AC_CHECK_TYPE(off_t, long)
+AC_CHECK_TYPE(pid_t, int)
+AC_CHECK_TYPE(ssize_t, [signed int])
+AC_CHECK_TYPE(caddr_t, [char *])
+AC_CHECK_TYPE(size_t, unsigned)
+
+AC_CHECK_TYPES([long long])
+AC_CHECK_TYPES([ptrdiff_t])
+
+
+AC_CHECK_TYPE([socklen_t],,, [$ac_includes_default
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_WINSOCK2
+#include <ws2tcpip.h>
+#endif])
+
+dnl socklen_t-like type in accept(), default socklen_t or int:
+dnl - The OS might define socklen_t without using it. POSIX moved from
+dnl int to size_t to socklen_t, hoping to stay at a 32-bit type, and
+dnl HP-UX now has selectors for what to use.
+dnl - On Solaris 2.8 the prototype has void *len, but the default is OK.
+AC_MSG_CHECKING([the type of arg 3 to accept()])
+AC_CACHE_VAL(ol_cv_type_ber_socklen_t, [
+ set socklen_t int unsigned "unsigned long" long size_t
+ test "$ac_cv_type_socklen_t" = yes || shift
+ ol_cv_type_ber_socklen_t=$1 guessing="guessing "
+ for lentype in "$@" ; do for addrtype in "struct sockaddr" void ; do
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([$ac_includes_default
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+extern int accept(int s, $addrtype *ap, $lentype *lp);
+], [
+accept(0, (struct sockaddr *) 0, ($lentype *) 0);
+])], [ol_cv_type_ber_socklen_t=$lentype guessing= ; break 2])
+ done ; done])
+AC_MSG_RESULT([$guessing$ol_cv_type_ber_socklen_t *])
+AC_DEFINE_UNQUOTED(ber_socklen_t, $ol_cv_type_ber_socklen_t,
+ [Define to the type of arg 3 for `accept'.])
+
+dnl Modules should use ber_socklen_t, not socklen_t. Define socklen_t
+dnl for the time being anyway, for backwards compatibility.
+if test "$ac_cv_type_socklen_t" != yes; then
+ AC_DEFINE_UNQUOTED([socklen_t], [$ol_cv_type_ber_socklen_t],
+ [Define like ber_socklen_t if <sys/socket.h> does not define.])
+fi
+
+
+AC_TYPE_SIGNAL
+
+AC_CHECK_TYPE([sig_atomic_t],,
+ [AC_DEFINE_UNQUOTED([sig_atomic_t], [int],
+ [Define to `int' if <signal.h> does not define.])],
+ [$ac_includes_default
+#include <signal.h>
+ ])
+
+AC_TYPE_UID_T
+
+AC_HEADER_TIME
+AC_STRUCT_TM
+AC_CHECK_MEMBERS([struct stat.st_blksize])
+AC_CHECK_MEMBERS([struct passwd.pw_gecos],,,[$ac_includes_default
+#include <pwd.h>])
+AC_CHECK_MEMBERS([struct passwd.pw_passwd],,,[$ac_includes_default
+#include <pwd.h>])
+
+OL_C_UPPER_LOWER
+AC_C_CONST
+OL_C_VOLATILE
+
+if test $cross_compiling = yes ; then
+ AC_MSG_WARN([Crossing compiling... all bets are off!])
+ AC_DEFINE(CROSS_COMPILING, 1, [define if cross compiling])
+else
+ AC_C_BIGENDIAN
+fi
+
+AC_CHECK_SIZEOF(short)
+AC_CHECK_SIZEOF(int)
+AC_CHECK_SIZEOF(long)
+AC_CHECK_SIZEOF(long long)
+AC_CHECK_SIZEOF(wchar_t)
+
+if test "$ac_cv_sizeof_int" -lt 4 ; then
+ AC_MSG_WARN([OpenLDAP requires 'int' to be 32 bits or greater.])
+
+ AC_DEFINE(LBER_INT_T,long,[define to 32-bit or greater integer type])
+else
+ AC_DEFINE(LBER_INT_T,int,[define to 32-bit or greater integer type])
+fi
+
+AC_DEFINE(LBER_LEN_T,long,[define to large integer type])
+AC_DEFINE(LBER_SOCKET_T,int,[define to socket descriptor type])
+AC_DEFINE(LBER_TAG_T,long,[define to large integer type])
+
+dnl ----------------------------------------------------------------
+dnl Check for multiple precision support
+if test $ol_with_mp = longlong || test $ol_with_mp = auto ; then
+ if test $ac_cv_sizeof_long_long -gt 4 ; then
+ ol_with_mp=longlong
+ AC_DEFINE(USE_MP_LONG_LONG,1,[define to use 'long long' for MP])
+ elif test $ol_with_mp = longlong ; then
+ AC_MSG_ERROR([long long unusable for multiple precision])
+ fi
+fi
+if test $ol_with_mp = long || test $ol_with_mp = auto ; then
+ if test $ac_cv_sizeof_long -gt 4 ; then
+ ol_with_mp=long
+ AC_DEFINE(USE_MP_LONG,1,[define to use 'long' for MP])
+ elif test $ol_with_mp = long ; then
+ AC_MSG_ERROR([long unusable for multiple precision])
+ fi
+fi
+if test $ol_with_mp = bignum || test $ol_with_mp = auto ; then
+ AC_CHECK_HEADERS(openssl/bn.h)
+ AC_CHECK_HEADERS(openssl/crypto.h)
+ if test "$ac_cv_header_openssl_bn_h" = "yes" &&
+ test "$ac_cv_header_openssl_crypto_h" = "yes" &&
+ test "$ol_with_tls" = "found" ; then
+ ol_with_mp=bignum
+ AC_DEFINE(USE_MP_BIGNUM,1,[define to use OpenSSL BIGNUM for MP])
+ elif test $ol_with_mp = bignum ; then
+ AC_MSG_ERROR([bignum not available])
+ fi
+fi
+if test $ol_with_mp = gmp || test $ol_with_mp = auto ; then
+ AC_CHECK_HEADERS(gmp.h)
+ AC_CHECK_LIB(gmp, __gmpz_add_ui)
+ if test $ac_cv_header_gmp_h = yes && test $ac_cv_lib_gmp___gmpz_add_ui = yes ; then
+ AC_DEFINE(USE_MP_GMP,1,[define to use GMP for MP])
+ ol_with_mp=gmp
+ elif test $ol_with_mp = gmp ; then
+ AC_MSG_ERROR([gmp not available])
+ fi
+fi
+if test $ol_with_mp = auto ; then
+ ol_with_mp=no
+fi
+
+dnl ----------------------------------------------------------------
+dnl Checks for library functions.
+AC_FUNC_MEMCMP
+
+if test $ac_cv_func_memcmp_working = no ; then
+ AC_DEFINE(NEED_MEMCMP_REPLACEMENT,1,
+ [define if memcmp is not 8-bit clean or is otherwise broken])
+fi
+
+AC_FUNC_STRFTIME
+
+OL_FUNC_INET_ATON
+
+dnl Check for NT specific routines
+AC_CHECK_FUNC(_spawnlp, AC_DEFINE(HAVE_SPAWNLP,1,[if you have spawnlp()]))
+
+AC_CHECK_FUNC(_snprintf, [ac_cv_func_snprintf=yes
+ AC_DEFINE(snprintf, _snprintf, [define to snprintf routine])
+])
+
+AC_CHECK_FUNCS(vsnprintf _vsnprintf)
+
+if test $ac_cv_func_vsnprintf = no -a $ac_cv_func__vsnprintf = yes ; then
+ ac_cv_func_vsnprintf=yes
+ AC_DEFINE(vsnprintf, _vsnprintf, [define to vsnprintf routine])
+fi
+
+AC_FUNC_VPRINTF
+
+if test $ac_cv_func_vprintf = yes ; then
+ dnl check for vsnprintf
+ AC_CHECK_FUNCS(snprintf vsnprintf)
+fi
+
+AC_CHECK_FUNCS( \
+ bcopy \
+ clock_gettime \
+ closesocket \
+ chroot \
+ endgrent \
+ endpwent \
+ fcntl \
+ flock \
+ fstat \
+ getdtablesize \
+ geteuid \
+ getgrgid \
+ gethostname \
+ getpassphrase \
+ getpwuid \
+ getpwnam \
+ getspnam \
+ gettimeofday \
+ initgroups \
+ inet_ntoa_b \
+ ioctl \
+ lockf \
+ memcpy \
+ memmove \
+ memrchr \
+ mkstemp \
+ mktemp \
+ pipe \
+ read \
+ recv \
+ recvfrom \
+ setpwfile \
+ setgid \
+ setegid \
+ setsid \
+ setuid \
+ seteuid \
+ signal \
+ strdup \
+ strpbrk \
+ strrchr \
+ strsep \
+ strstr \
+ strtol \
+ strtoul \
+ strtoq \
+ strtouq \
+ strtoll \
+ strtoull \
+ strspn \
+ sysconf \
+ waitpid \
+ wait4 \
+ write \
+ send \
+ sendmsg \
+ sendto \
+)
+
+dnl We actually may need to replace more than this.
+AC_REPLACE_FUNCS(getopt getpeereid)
+
+if test "$ac_cv_func_getopt" != yes; then
+ LIBSRCS="$LIBSRCS getopt.c"
+fi
+
+if test "$ac_cv_func_getpeereid" != yes; then
+ AC_CHECK_FUNCS( getpeerucred )
+ if test "$ac_cv_func_getpeerucred" != yes ; then
+ AC_CHECK_MEMBERS([struct msghdr.msg_accrightslen],,,
+ [$ac_includes_default
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif])
+ if test "$ac_cv_member_struct_msghdr_msg_accrightslen" != yes; then
+ AC_CHECK_MEMBERS([struct msghdr.msg_control],,,
+ [$ac_includes_default
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif])
+ fi
+ AC_CHECK_MEMBERS([struct stat.st_fstype, struct stat.st_vfstype])
+ if test "$ac_cv_member_struct_stat_st_fstype" = yes; then
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([struct stat st; char *ptr=st.st_fstype;])],
+ AC_DEFINE([HAVE_STRUCT_STAT_ST_FSTYPE_CHAR],1,[define to 1 if st_fstype is char *]),
+ AC_DEFINE([HAVE_STRUCT_STAT_ST_FSTYPE_INT],1,[define to 1 if st_fstype is int]))
+ fi
+ fi
+ LIBSRCS="$LIBSRCS getpeereid.c"
+fi
+
+if test "$ac_cv_func_snprintf" != yes ||
+ test "$ac_cv_func_vsnprintf" != yes; then
+ if test "$ac_cv_func_snprintf" != yes; then
+ AC_DEFINE(snprintf, ber_pvt_snprintf, [define to snprintf routine])
+ fi
+ if test "$ac_cv_func_vsnprintf" != yes; then
+ AC_DEFINE(vsnprintf, ber_pvt_vsnprintf, [define to snprintf routine])
+ fi
+fi
+
+dnl ----------------------------------------------------------------
+dnl Sort out defines
+
+if test "$ol_enable_slapi" != no ; then
+ dnl This check is done also if --enable-modules is used;
+ dnl it is duplicated here, 'cause it'd be cached anyway
+ AC_CHECK_HEADERS(ltdl.h)
+
+ if test $ac_cv_header_ltdl_h != yes ; then
+ AC_MSG_ERROR([could not locate <ltdl.h>])
+ fi
+ AC_CHECK_LIB(ltdl, lt_dlinit, [
+ SLAPI_LIBS=-lltdl
+ LIBSLAPI=slapi/libslapi.la
+ AC_DEFINE(HAVE_LIBLTDL,1,[define if you have libtool -ltdl])
+ ],[AC_MSG_ERROR([could not locate libtool -lltdl])])
+
+ AC_DEFINE(LDAP_SLAPI,1, [define this to add SLAPI code])
+fi
+
+if test "$ol_enable_debug" != no ; then
+ if test "$ol_enable_debug" = traditional; then
+ AC_DEFINE(OLD_DEBUG,1,
+ [define to use the original debug style])
+ fi
+ AC_DEFINE(LDAP_DEBUG,1,
+ [define this to add debugging code])
+fi
+if test "$ol_enable_syslog" != no ; then
+ AC_DEFINE(LDAP_SYSLOG,1,
+ [define this to add syslog code])
+fi
+if test "$ol_enable_referrals" != no ; then
+ AC_DEFINE(LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS,LDAP_VENDOR_VERSION,
+ [define to LDAP VENDOR VERSION])
+fi
+if test "$ol_enable_local" != no; then
+ AC_DEFINE(LDAP_PF_LOCAL,1,[define to support PF_LOCAL])
+fi
+if test "$ol_link_ipv6" != no; then
+ AC_DEFINE(LDAP_PF_INET6,1,[define to support PF_INET6])
+fi
+if test "$ol_enable_cleartext" != no ; then
+ AC_DEFINE(SLAPD_CLEARTEXT,1,[define to support cleartext passwords])
+fi
+if test "$ol_enable_crypt" != no ; then
+ AC_DEFINE(SLAPD_CRYPT,1,[define to support crypt(3) passwords])
+fi
+if test "$ol_link_spasswd" != no ; then
+ AC_DEFINE(SLAPD_SPASSWD,1,[define to support SASL passwords])
+fi
+if test "$ol_enable_rlookups" != no ; then
+ AC_DEFINE(SLAPD_RLOOKUPS,1,[define to support reverse lookups])
+fi
+if test "$ol_enable_aci" != no ; then
+ if test "$ol_enable_aci" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ dnl remove this after moving servers/slapd/aci.c in contrib/slapd-modules/acl
+ AC_MSG_ERROR([ACI build as dynamic module not supported (yet)])
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+ WITH_ACI_ENABLED=$ol_enable_aci
+ AC_DEFINE_UNQUOTED(SLAPD_ACI_ENABLED,$MFLAG,[define to support per-object ACIs])
+else
+ WITH_ACI_ENABLED=no
+fi
+if test "$ol_enable_dynacl" != no ; then
+ AC_DEFINE(SLAP_DYNACL,1,[define to support run-time loadable ACL])
+fi
+
+if test "$ol_link_modules" != no ; then
+ AC_DEFINE(SLAPD_MODULES,1,[define to support modules])
+ BUILD_SLAPD=yes
+ SLAPD_MODULES_LDFLAGS="-dlopen self"
+fi
+
+AC_DEFINE(SLAPD_MOD_STATIC,1,[statically linked module])
+AC_DEFINE(SLAPD_MOD_DYNAMIC,2,[dynamically linked module])
+
+if test "$ol_enable_dnssrv" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_DNSSRV=$ol_enable_dnssrv
+ if test "$ol_enable_dnssrv" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-dnssrv"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-dnssrv"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_DNSSRV,$MFLAG,[define to support DNS SRV backend])
+fi
+
+if test "$ol_enable_ldap" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_LDAP=$ol_enable_ldap
+ if test "$ol_enable_ldap" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-ldap"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-ldap"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_LDAP,$MFLAG,[define to support LDAP backend])
+fi
+
+if test "$ol_enable_mdb" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_MDB=$ol_enable_mdb
+ if test "$ol_enable_mdb" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-mdb"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-mdb"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_MDB,$MFLAG,[define to support MDB backend])
+fi
+
+if test "$ol_enable_meta" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_META=$ol_enable_meta
+ if test "$ol_enable_meta" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-meta"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-meta"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_META,$MFLAG,[define to support LDAP Metadirectory backend])
+fi
+
+if test "$ol_enable_asyncmeta" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_ASYNCMETA=$ol_enable_asyncmeta
+ if test "$ol_enable_asyncmeta" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-asyncmeta"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-asyncmeta"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_ASYNCMETA,$MFLAG,[define to support LDAP Async Metadirectory backend])
+fi
+
+if test "$ol_enable_ndb" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_NDB=$ol_enable_ndb
+ if test "$ol_enable_ndb" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-ndb"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-ndb"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_NDB,$MFLAG,[define to support NDB backend])
+fi
+
+if test "$ol_enable_null" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_NULL=$ol_enable_null
+ if test "$ol_enable_null" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-null"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-null"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_NULL,$MFLAG,[define to support NULL backend])
+fi
+
+if test "$ol_enable_passwd" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_PASSWD=$ol_enable_passwd
+ if test "$ol_enable_passwd" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-passwd"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-passwd"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_PASSWD,$MFLAG,[define to support PASSWD backend])
+fi
+
+if test "$ol_link_perl" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_PERL=$ol_enable_perl
+ if test "$ol_enable_perl" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-perl"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-perl"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_PERL,$MFLAG,[define to support PERL backend])
+fi
+
+if test "$ol_enable_relay" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_RELAY=$ol_enable_relay
+ if test "$ol_enable_relay" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-relay"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-relay"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_RELAY,$MFLAG,[define to support relay backend])
+fi
+
+if test "$ol_enable_sock" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_SOCK=$ol_enable_sock
+ if test "$ol_enable_sock" = mod ; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-sock"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-sock"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_SOCK,$MFLAG,[define to support SOCK backend])
+fi
+
+if test "$ol_link_sql" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_SQL=$ol_enable_sql
+ if test "$ol_enable_sql" = mod; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-sql"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-sql"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_SQL,$MFLAG,[define to support SQL backend])
+fi
+
+if test "$ol_link_wt" != no ; then
+ BUILD_SLAPD=yes
+ BUILD_WT=$ol_enable_wt
+ if test "$ol_enable_wt" = mod; then
+ SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-wt"
+ MFLAG=SLAPD_MOD_DYNAMIC
+ else
+ SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-wt"
+ MFLAG=SLAPD_MOD_STATIC
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_WT,$MFLAG,[define to support WiredTiger backend])
+fi
+
+if test "$ol_enable_accesslog" != no ; then
+ BUILD_ACCESSLOG=$ol_enable_accesslog
+ if test "$ol_enable_accesslog" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS accesslog.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS accesslog.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_ACCESSLOG,$MFLAG,[define for In-Directory Access Logging overlay])
+fi
+
+if test "$ol_enable_auditlog" != no ; then
+ BUILD_AUDITLOG=$ol_enable_auditlog
+ if test "$ol_enable_auditlog" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS auditlog.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS auditlog.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_AUDITLOG,$MFLAG,[define for Audit Logging overlay])
+fi
+
+if test "$ol_enable_autoca" != no ; then
+ if test $ol_with_tls != openssl ; then
+ AC_MSG_ERROR([--enable-autoca=$ol_enable_autoca requires --with-tls=openssl])
+ fi
+
+ BUILD_AUTOCA=$ol_enable_autoca
+ if test "$ol_enable_autoca" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS autoca.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS autoca.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_AUTOCA,$MFLAG,[define for Automatic Certificate Authority overlay])
+fi
+
+if test "$ol_enable_collect" != no ; then
+ BUILD_COLLECT=$ol_enable_collect
+ if test "$ol_enable_collect" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS collect.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS collect.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_COLLECT,$MFLAG,[define for Collect overlay])
+fi
+
+if test "$ol_enable_constraint" != no ; then
+ BUILD_CONSTRAINT=$ol_enable_constraint
+ if test "$ol_enable_constraint" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS constraint.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS constraint.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_CONSTRAINT,$MFLAG,[define for Attribute Constraint overlay])
+fi
+
+if test "$ol_enable_dds" != no ; then
+ BUILD_DDS=$ol_enable_dds
+ if test "$ol_enable_dds" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS dds.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS dds.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_DDS,$MFLAG,[define for Dynamic Directory Services overlay])
+fi
+
+if test "$ol_enable_deref" != no ; then
+ BUILD_DEREF=$ol_enable_deref
+ if test "$ol_enable_deref" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS deref.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS deref.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_DEREF,$MFLAG,[define for Dynamic Directory Services overlay])
+fi
+
+if test "$ol_enable_dyngroup" != no ; then
+ BUILD_DYNGROUP=$ol_enable_dyngroup
+ if test "$ol_enable_dyngroup" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS dyngroup.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS dyngroup.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_DYNGROUP,$MFLAG,[define for Dynamic Group overlay])
+fi
+
+if test "$ol_enable_dynlist" != no ; then
+ BUILD_DYNLIST=$ol_enable_dynlist
+ if test "$ol_enable_dynlist" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS dynlist.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS dynlist.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_DYNLIST,$MFLAG,[define for Dynamic List overlay])
+fi
+
+if test "$ol_enable_homedir" != no ; then
+ BUILD_HOMEDIR=$ol_enable_homedir
+ if test "$ol_enable_homedir" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS homedir.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS homedir.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_HOMEDIR,$MFLAG,[define for Home Directory Management overlay])
+fi
+
+if test "$ol_enable_memberof" != no ; then
+ BUILD_MEMBEROF=$ol_enable_memberof
+ if test "$ol_enable_memberof" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS memberof.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS memberof.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_MEMBEROF,$MFLAG,[define for Reverse Group Membership overlay])
+fi
+
+if test "$ol_enable_otp" != no ; then
+ if test $ol_with_tls = no ; then
+ AC_MSG_ERROR([--enable-otp=$ol_enable_otp requires --with-tls])
+ fi
+
+ BUILD_OTP=$ol_enable_otp
+ if test "$ol_enable_otp" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS otp.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS otp.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_OTP,$MFLAG,[define for OTP 2-factor Authentication overlay])
+fi
+
+if test "$ol_enable_ppolicy" != no ; then
+ BUILD_PPOLICY=$ol_enable_ppolicy
+ if test "$ol_enable_ppolicy" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS ppolicy.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS ppolicy.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_PPOLICY,$MFLAG,[define for Password Policy overlay])
+fi
+
+if test "$ol_enable_proxycache" != no ; then
+ BUILD_PROXYCACHE=$ol_enable_proxycache
+ if test "$ol_enable_proxycache" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS pcache.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS pcache.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_PROXYCACHE,$MFLAG,[define for Proxy Cache overlay])
+fi
+
+if test "$ol_enable_refint" != no ; then
+ BUILD_REFINT=$ol_enable_refint
+ if test "$ol_enable_refint" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS refint.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS refint.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_REFINT,$MFLAG,[define for Referential Integrity overlay])
+fi
+
+if test "$ol_enable_remoteauth" != no ; then
+ BUILD_REMOTEAUTH=$ol_enable_remoteauth
+ if test "$ol_enable_remoteauth" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS remoteauth.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS remoteauth.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_REMOTEAUTH,$MFLAG,[define for Deferred Authentication overlay])
+fi
+
+if test "$ol_enable_retcode" != no ; then
+ BUILD_RETCODE=$ol_enable_retcode
+ if test "$ol_enable_retcode" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS retcode.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS retcode.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_RETCODE,$MFLAG,[define for Return Code overlay])
+fi
+
+if test "$ol_enable_rwm" != no ; then
+ BUILD_RWM=$ol_enable_rwm
+ if test "$ol_enable_rwm" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS rwm.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS rwm_x.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_RWM,$MFLAG,[define for Rewrite/Remap overlay])
+fi
+
+if test "$ol_enable_seqmod" != no ; then
+ BUILD_SEQMOD=$ol_enable_seqmod
+ if test "$ol_enable_seqmod" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS seqmod.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS seqmod.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_SEQMOD,$MFLAG,[define for Sequential Modify overlay])
+fi
+
+if test "$ol_enable_sssvlv" != no ; then
+ BUILD_SSSVLV=$ol_enable_sssvlv
+ if test "$ol_enable_sssvlv" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS sssvlv.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS sssvlv.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_SSSVLV,$MFLAG,[define for ServerSideSort/VLV overlay])
+fi
+
+if test "$ol_enable_syncprov" != no ; then
+ BUILD_SYNCPROV=$ol_enable_syncprov
+ if test "$ol_enable_syncprov" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS syncprov.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS syncprov.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_SYNCPROV,$MFLAG,[define for Syncrepl Provider overlay])
+fi
+
+if test "$ol_enable_translucent" != no ; then
+ BUILD_TRANSLUCENT=$ol_enable_translucent
+ if test "$ol_enable_translucent" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS translucent.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS translucent.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_TRANSLUCENT,$MFLAG,[define for Translucent Proxy overlay])
+fi
+
+if test "$ol_enable_unique" != no ; then
+ BUILD_UNIQUE=$ol_enable_unique
+ if test "$ol_enable_unique" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS unique.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS unique.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_UNIQUE,$MFLAG,[define for Attribute Uniqueness overlay])
+fi
+
+if test "$ol_enable_valsort" != no ; then
+ BUILD_VALSORT=$ol_enable_valsort
+ if test "$ol_enable_valsort" = mod ; then
+ MFLAG=SLAPD_MOD_DYNAMIC
+ SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS valsort.la"
+ else
+ MFLAG=SLAPD_MOD_STATIC
+ SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS valsort.o"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_OVER_VALSORT,$MFLAG,[define for Value Sorting overlay])
+fi
+
+ol_link_argon2=no
+if test "$ol_enable_argon2" = "yes" ; then
+ if test $ol_with_argon2 = libargon2 || test $ol_with_argon2 = auto; then
+ AC_CHECK_HEADERS(argon2.h)
+ if test $ac_cv_header_argon2_h = yes ; then
+ AC_CHECK_LIB(argon2, argon2i_hash_encoded,
+ [have_argon2=yes], [have_argon2=no],
+ [-largon2])
+ fi
+ if test "$have_argon2" = "yes" ; then
+ ol_with_argon2=libargon2
+ ol_link_argon2=yes
+ AC_DEFINE(HAVE_LIBARGON2, 1,
+ [define if you have libargon2])
+ ARGON2_LIBS="-largon2"
+ fi
+ fi
+ if test $ol_with_argon2 = libsodium || test $ol_with_argon2 = auto; then
+ AC_CHECK_HEADERS(sodium.h)
+ if test $ac_cv_header_sodium_h = yes ; then
+ AC_CHECK_LIB(sodium, crypto_pwhash_str_alg,
+ [have_argon2=yes], [have_argon2=no],
+ [-lsodium])
+ fi
+ if test "$have_argon2" = "yes" ; then
+ ol_with_argon2=libsodium
+ ol_link_argon2=yes
+ AC_DEFINE(HAVE_LIBSODIUM, 1,
+ [define if you have libsodium])
+ ARGON2_LIBS="-lsodium"
+ fi
+ fi
+
+ if test "$ol_link_argon2" = no ; then
+ AC_MSG_ERROR([--enable_argon2=$ol_enable_argon2 requires --with-argon2])
+ fi
+
+ BUILD_PW_ARGON2=$ol_enable_argon2
+ if test "$ol_enable_argon2" = "yes" ; then
+ SLAPD_DYNAMIC_PWMODS="$SLAPD_DYNAMIC_PWDMODS argon2.la"
+ fi
+ AC_DEFINE_UNQUOTED(SLAPD_PWMOD_PW_ARGON2,$SLAPD_MOD_DYNAMIC,[define for Argon2 Password hashing module])
+fi
+
+if test "$ol_enable_balancer" != no \
+ -a "$ol_with_threads" != no \
+ -a "$have_libevent" = yes ; then
+ if test "$ol_enable_balancer" = mod; then
+ BALANCER_INCLUDE=Makefile.module
+ BUILD_BALANCER=mod
+ else
+ BALANCER_INCLUDE=Makefile.server
+ BUILD_BALANCER=yes
+ fi
+fi
+
+if test "$ol_enable_slapi" != no ; then
+ AC_DEFINE(ENABLE_SLAPI,1,[define to enable slapi library])
+ BUILD_SLAPI=yes
+ SLAPD_SLAPI_DEPEND=libslapi.a
+fi
+
+OL_VERSIONED_SYMBOLS=""
+if test $ol_enable_versioning != no; then
+ LDVS=`$LD --help < /dev/null 2>/dev/null | grep gnu-version-script`
+ if test -z "$LDVS"; then
+ LDVS=`$LD --help < /dev/null 2>/dev/null | grep version-script`
+ if test -z "$LDVS"; then
+ if test $ol_enable_versioning = "yes" ; then
+ AC_MSG_ERROR([Library symbol versioning requested but not supported])
+ fi
+ else
+ OL_VERSIONED_SYMBOLS="-Wl,--version-script="
+ fi
+ else
+ OL_VERSIONED_SYMBOLS="-z gnu-version-script="
+ fi
+fi
+
+dnl ----------------------------------------------------------------
+
+dnl
+dnl For Windows build, we don't want to include -dlopen flags.
+dnl They hurt more than they help.
+dnl
+
+if test "$ac_cv_mingw32" = yes -o $ol_cv_msvc = yes ; then
+ PLAT=NT
+ SLAPD_MODULES_LDFLAGS=
+else
+ PLAT=UNIX
+fi
+
+AC_SUBST(LIBSRCS)
+AC_SUBST(PLAT)
+AC_SUBST(WITH_SASL)
+AC_SUBST(WITH_TLS)
+AC_SUBST(WITH_MODULES_ENABLED)
+AC_SUBST(WITH_ACI_ENABLED)
+AC_SUBST(WITH_SYSTEMD)
+AC_SUBST(BUILD_THREAD)
+AC_SUBST(BUILD_LIBS_DYNAMIC)
+AC_SUBST(OL_VERSIONED_SYMBOLS)
+
+AC_SUBST(BUILD_SLAPD)
+dnl slapi
+ AC_SUBST(BUILD_SLAPI)
+ AC_SUBST(SLAPD_SLAPI_DEPEND)
+dnl backends
+ AC_SUBST(BUILD_DNSSRV)
+ AC_SUBST(BUILD_LDAP)
+ AC_SUBST(BUILD_MDB)
+ AC_SUBST(BUILD_META)
+ AC_SUBST(BUILD_ASYNCMETA)
+ AC_SUBST(BUILD_NDB)
+ AC_SUBST(BUILD_NULL)
+ AC_SUBST(BUILD_PASSWD)
+ AC_SUBST(BUILD_RELAY)
+ AC_SUBST(BUILD_PERL)
+ AC_SUBST(BUILD_SHELL)
+ AC_SUBST(BUILD_SOCK)
+ AC_SUBST(BUILD_SQL)
+ AC_SUBST(BUILD_WT)
+dnl overlays
+ AC_SUBST(BUILD_ACCESSLOG)
+ AC_SUBST(BUILD_AUDITLOG)
+ AC_SUBST(BUILD_AUTOCA)
+ AC_SUBST(BUILD_COLLECT)
+ AC_SUBST(BUILD_CONSTRAINT)
+ AC_SUBST(BUILD_DDS)
+ AC_SUBST(BUILD_DENYOP)
+ AC_SUBST(BUILD_DEREF)
+ AC_SUBST(BUILD_DYNGROUP)
+ AC_SUBST(BUILD_DYNLIST)
+ AC_SUBST(BUILD_LASTMOD)
+ AC_SUBST(BUILD_HOMEDIR)
+ AC_SUBST(BUILD_MEMBEROF)
+ AC_SUBST(BUILD_OTP)
+ AC_SUBST(BUILD_PPOLICY)
+ AC_SUBST(BUILD_PROXYCACHE)
+ AC_SUBST(BUILD_REFINT)
+ AC_SUBST(BUILD_REMOTEAUTH)
+ AC_SUBST(BUILD_RETCODE)
+ AC_SUBST(BUILD_RWM)
+ AC_SUBST(BUILD_SEQMOD)
+ AC_SUBST(BUILD_SSSVLV)
+ AC_SUBST(BUILD_SYNCPROV)
+ AC_SUBST(BUILD_TRANSLUCENT)
+ AC_SUBST(BUILD_UNIQUE)
+ AC_SUBST(BUILD_VALSORT)
+ AC_SUBST(BUILD_BALANCER)
+dnl pwmods
+ AC_SUBST(BUILD_PW_ARGON2)
+
+AC_SUBST(LDAP_LIBS)
+AC_SUBST(CLIENT_LIBS)
+AC_SUBST(SLAPD_LIBS)
+AC_SUBST(BALANCER_LIBS)
+AC_SUBST(SLAPD_NDB_LIBS)
+AC_SUBST(SLAPD_NDB_INCS)
+AC_SUBST(LTHREAD_LIBS)
+AC_SUBST(LUTIL_LIBS)
+AC_SUBST(LEVENT_LIBS)
+AC_SUBST(WRAP_LIBS)
+
+AC_SUBST(SLAPD_MODULES_CPPFLAGS)
+AC_SUBST(SLAPD_MODULES_LDFLAGS)
+
+AC_SUBST(SLAPD_NO_STATIC)
+AC_SUBST(SLAPD_STATIC_BACKENDS)
+AC_SUBST(SLAPD_DYNAMIC_BACKENDS)
+AC_SUBST(SLAPD_STATIC_OVERLAYS)
+AC_SUBST(SLAPD_DYNAMIC_OVERLAYS)
+AC_SUBST(SLAPD_DYNAMIC_PWMODS)
+
+AC_SUBST(PERL_CPPFLAGS)
+AC_SUBST(SLAPD_PERL_LDFLAGS)
+AC_SUBST(MOD_PERL_LDFLAGS)
+
+AC_SUBST(SASL_LIBS)
+AC_SUBST(TLS_LIBS)
+AC_SUBST(WITH_TLS_TYPE)
+AC_SUBST(MODULES_LIBS)
+AC_SUBST(SLAPI_LIBS)
+AC_SUBST(LIBSLAPI)
+AC_SUBST(AUTH_LIBS)
+AC_SUBST(ARGON2_LIBS)
+AC_SUBST(SYSTEMD_LIBS)
+
+AC_SUBST(SLAPD_SLP_LIBS)
+AC_SUBST(SLAPD_GMP_LIBS)
+
+AC_SUBST(SLAPD_SQL_LDFLAGS)
+AC_SUBST(SLAPD_SQL_LIBS)
+AC_SUBST(SLAPD_SQL_INCLUDES)
+
+AC_SUBST(WT_CFLAGS)
+AC_SUBST(WT_LIBS)
+
+AC_SUBST(BALANCER_INCLUDE)
+
+dnl ----------------------------------------------------------------
+dnl final help output
+AC_ARG_WITH(xxinstall,[
+See INSTALL file for further details.])
+
+dnl ----------------------------------------------------------------
+dnl final output
+dnl
+
+AC_CONFIG_FILES([Makefile:build/top.mk:Makefile.in:build/dir.mk]
+[doc/Makefile:build/top.mk:doc/Makefile.in:build/dir.mk]
+[doc/man/Makefile:build/top.mk:doc/man/Makefile.in:build/dir.mk]
+[doc/man/man1/Makefile:build/top.mk:doc/man/man1/Makefile.in:build/man.mk]
+[doc/man/man3/Makefile:build/top.mk:doc/man/man3/Makefile.in:build/man.mk]
+[doc/man/man5/Makefile:build/top.mk:doc/man/man5/Makefile.in:build/man.mk]
+[doc/man/man8/Makefile:build/top.mk:doc/man/man8/Makefile.in:build/man.mk]
+[clients/Makefile:build/top.mk:clients/Makefile.in:build/dir.mk]
+[clients/tools/Makefile:build/top.mk:clients/tools/Makefile.in:build/rules.mk]
+[include/Makefile:build/top.mk:include/Makefile.in]
+[libraries/Makefile:build/top.mk:libraries/Makefile.in:build/dir.mk]
+[libraries/liblber/Makefile:build/top.mk:libraries/liblber/Makefile.in:build/lib.mk:build/lib-shared.mk]
+[libraries/liblber/lber.pc]
+[libraries/liblber/liblber.vers]
+[libraries/libldap/Makefile:build/top.mk:libraries/libldap/Makefile.in:build/lib.mk:build/lib-shared.mk]
+[libraries/libldap/ldap.pc]
+[libraries/libldap/libldap.vers]
+[libraries/liblunicode/Makefile:build/top.mk:libraries/liblunicode/Makefile.in:build/lib.mk:build/lib-static.mk]
+[libraries/liblutil/Makefile:build/top.mk:libraries/liblutil/Makefile.in:build/lib.mk:build/lib-static.mk]
+[libraries/librewrite/Makefile:build/top.mk:libraries/librewrite/Makefile.in:build/lib.mk:build/lib-static.mk]
+[servers/Makefile:build/top.mk:servers/Makefile.in:build/dir.mk]
+[servers/slapd/Makefile:build/top.mk:servers/slapd/Makefile.in:build/srv.mk]
+[servers/slapd/back-dnssrv/Makefile:build/top.mk:servers/slapd/back-dnssrv/Makefile.in:build/mod.mk]
+[servers/slapd/back-ldap/Makefile:build/top.mk:servers/slapd/back-ldap/Makefile.in:build/mod.mk]
+[servers/slapd/back-ldif/Makefile:build/top.mk:servers/slapd/back-ldif/Makefile.in:build/mod.mk]
+[servers/slapd/back-mdb/Makefile:build/top.mk:servers/slapd/back-mdb/Makefile.in:build/mod.mk]
+[servers/slapd/back-meta/Makefile:build/top.mk:servers/slapd/back-meta/Makefile.in:build/mod.mk]
+[servers/slapd/back-asyncmeta/Makefile:build/top.mk:servers/slapd/back-asyncmeta/Makefile.in:build/mod.mk]
+[servers/slapd/back-monitor/Makefile:build/top.mk:servers/slapd/back-monitor/Makefile.in:build/mod.mk]
+[servers/slapd/back-ndb/Makefile:build/top.mk:servers/slapd/back-ndb/Makefile.in:build/mod.mk]
+[servers/slapd/back-null/Makefile:build/top.mk:servers/slapd/back-null/Makefile.in:build/mod.mk]
+[servers/slapd/back-passwd/Makefile:build/top.mk:servers/slapd/back-passwd/Makefile.in:build/mod.mk]
+[servers/slapd/back-perl/Makefile:build/top.mk:servers/slapd/back-perl/Makefile.in:build/mod.mk]
+[servers/slapd/back-relay/Makefile:build/top.mk:servers/slapd/back-relay/Makefile.in:build/mod.mk]
+[servers/slapd/back-sock/Makefile:build/top.mk:servers/slapd/back-sock/Makefile.in:build/mod.mk]
+[servers/slapd/back-sql/Makefile:build/top.mk:servers/slapd/back-sql/Makefile.in:build/mod.mk]
+[servers/slapd/back-wt/Makefile:build/top.mk:servers/slapd/back-wt/Makefile.in:build/mod.mk]
+[servers/slapd/slapi/Makefile:build/top.mk:servers/slapd/slapi/Makefile.in:build/lib.mk:build/lib-shared.mk]
+[servers/slapd/overlays/Makefile:build/top.mk:servers/slapd/overlays/Makefile.in:build/lib.mk]
+[servers/slapd/pwmods/Makefile:build/top.mk:servers/slapd/pwmods/Makefile.in:build/lib.mk]
+[servers/lloadd/Makefile:build/top.mk:servers/lloadd/Makefile.in]
+[servers/lloadd/Makefile.server:servers/lloadd/Makefile_server.in:build/srv.mk]
+[servers/lloadd/Makefile.module:servers/lloadd/Makefile_module.in:build/mod.mk]
+[tests/Makefile:build/top.mk:tests/Makefile.in:build/dir.mk]
+[tests/run]
+[tests/progs/Makefile:build/top.mk:tests/progs/Makefile.in:build/rules.mk])
+
+AC_CONFIG_COMMANDS([default],[[
+chmod +x tests/run
+date > stamp-h
+BACKENDSC="servers/slapd/backends.c"
+echo "Making $BACKENDSC"
+rm -f $BACKENDSC
+cat > $BACKENDSC << ENDX
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* This file is automatically generated by configure; please do not edit. */
+
+#include "portable.h"
+#include "slap.h"
+
+ENDX
+if test "${STATIC_BACKENDS}"; then
+ for b in config ${STATIC_BACKENDS}; do
+ bb=`echo "${b}" | sed -e 's/back-//'`
+ cat >> $BACKENDSC << ENDX
+extern BI_init ${bb}_back_initialize;
+ENDX
+ done
+
+ cat >> $BACKENDSC << ENDX
+
+BackendInfo slap_binfo[] = {
+ENDX
+
+ for b in config ${STATIC_BACKENDS}; do
+ bb=`echo "${b}" | sed -e 's/back-//'`
+ echo " Add ${bb} ..."
+ cat >> $BACKENDSC << ENDX
+ { "${bb}", ${bb}_back_initialize },
+ENDX
+ done
+
+ cat >> $BACKENDSC << ENDX
+ { NULL, NULL },
+};
+
+/* end of generated file */
+ENDX
+fi
+OVERLAYSC="servers/slapd/overlays/statover.c"
+echo "Making $OVERLAYSC"
+rm -f $OVERLAYSC
+cat > $OVERLAYSC << ENDX
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* This file is automatically generated by configure; please do not edit. */
+
+#include "portable.h"
+#include "slap.h"
+
+ENDX
+if test "${STATIC_OVERLAYS}"; then
+ for o in ${STATIC_OVERLAYS}; do
+ oo=`echo "${o}" | sed -e 's/.o$//' -e 's/_x$//'`
+ cat >> $OVERLAYSC << ENDX
+extern OV_init ${oo}_initialize;
+ENDX
+ done
+fi
+
+cat >> $OVERLAYSC << ENDX
+
+OverlayInit slap_oinfo[] = {
+ENDX
+
+if test "${STATIC_OVERLAYS}"; then
+ for o in ${STATIC_OVERLAYS}; do
+ oo=`echo "${o}" | sed -e 's/.o$//' -e 's/_x$//'`
+ echo " Add ${oo} ..."
+ cat >> $OVERLAYSC << ENDX
+ { "${oo}", ${oo}_initialize },
+ENDX
+ done
+fi
+
+ cat >> $OVERLAYSC << ENDX
+ { NULL, NULL },
+};
+
+/* end of generated file */
+ENDX
+
+if test "${ol_cv_mkdep}" = no; then
+ echo '(Do not "make depend"; we do not know how to build dependencies)'
+else
+ echo 'Please run "make depend" to build dependencies'
+fi
+]],[[
+STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS"
+STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS"
+]])
+AC_OUTPUT
diff --git a/contrib/ConfigOIDs b/contrib/ConfigOIDs
new file mode 100644
index 0000000..6dd4a9a
--- /dev/null
+++ b/contrib/ConfigOIDs
@@ -0,0 +1,8 @@
+List of OpenLDAP Configuration OIDs allocated to contrib modules
+
+OLcfgCt{Oc|At}:1 smbk5pwd
+OLcfgCt{Oc|At}:2 autogroup
+OLcfgCt{Oc|At}:3 nssov
+OLcfgCt{Oc|At}:4 cloak
+OLcfgCt{Oc|At}:5 lastbind
+OLcfgCt{Oc|At}:6 adremap
diff --git a/contrib/README b/contrib/README
new file mode 100644
index 0000000..7c3f203
--- /dev/null
+++ b/contrib/README
@@ -0,0 +1,32 @@
+OpenLDAP Contributed Software README
+
+OpenLDAP Project provides a number of freely-distributable LDAP
+software packages. While distributed as part of OpenLDAP Software,
+they are not necessarily supported by the OpenLDAP Project. Some
+packages may be out of date. Each package in this directory has its
+own use and may have different redistribution restrictions than typical
+for OpenLDAP Software.
+
+Current contributions:
+ ldapc++
+ LDAP C++ API
+ Contributed by SuSE Gmbh.
+
+ ldaptcl
+ LDAP TCL API
+ Contributed by NeoSoft
+
+ slapd-modules
+ Native-API modules
+
+ slapd-tools
+ Tools to use with slapd
+
+ slapi-plugins
+ SLAPI plugins
+
+
+OpenLDAP Contributing Guidelines are available at:
+ <http://www.openldap.org/devel/contributing.html>.
+
+$OpenLDAP$
diff --git a/contrib/ldapc++/AUTHORS b/contrib/ldapc++/AUTHORS
new file mode 100644
index 0000000..aee9ec8
--- /dev/null
+++ b/contrib/ldapc++/AUTHORS
@@ -0,0 +1 @@
+Ralf Haferkamp <rhafer@suse.de>
diff --git a/contrib/ldapc++/COPYRIGHT b/contrib/ldapc++/COPYRIGHT
new file mode 100644
index 0000000..94dea5c
--- /dev/null
+++ b/contrib/ldapc++/COPYRIGHT
@@ -0,0 +1,6 @@
+Copyright 1998-2022 The OpenLDAP Foundation. All rights reserved.
+
+COPYING RESTRICTIONS APPLY.
+
+See COPYRIGHT and LICENSE files in the top-level directory of this
+distribution (i.e., ../../COPYRIGHT and ../../LICENSE, respectively).
diff --git a/contrib/ldapc++/Makefile.am b/contrib/ldapc++/Makefile.am
new file mode 100644
index 0000000..679d84d
--- /dev/null
+++ b/contrib/ldapc++/Makefile.am
@@ -0,0 +1,10 @@
+# $OpenLDAP$
+
+##
+# Copyright 2000-2022 The OpenLDAP Foundation. All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+##
+
+EXTRA_DIST=COPYRIGHT doxygen.rc LICENSE version.var version.sh
+SUBDIRS = src examples
+
diff --git a/contrib/ldapc++/Makefile.in b/contrib/ldapc++/Makefile.in
new file mode 100644
index 0000000..7e4edcb
--- /dev/null
+++ b/contrib/ldapc++/Makefile.in
@@ -0,0 +1,620 @@
+# Makefile.in generated by automake 1.10.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# $OpenLDAP$
+
+# Copyright 2000-2022 The OpenLDAP Foundation. All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = .
+DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in $(top_srcdir)/configure AUTHORS TODO \
+ config.guess config.sub depcomp install-sh ltmain.sh missing
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
+ configure.lineno config.status.lineno
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/src/config.h
+CONFIG_CLEAN_FILES =
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+ html-recursive info-recursive install-data-recursive \
+ install-dvi-recursive install-exec-recursive \
+ install-html-recursive install-info-recursive \
+ install-pdf-recursive install-ps-recursive install-recursive \
+ installcheck-recursive installdirs-recursive pdf-recursive \
+ ps-recursive uninstall-recursive
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+distdir = $(PACKAGE)-$(VERSION)
+top_distdir = $(distdir)
+am__remove_distdir = \
+ { test ! -d $(distdir) \
+ || { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \
+ && rm -fr $(distdir); }; }
+DIST_ARCHIVES = $(distdir).tar.gz
+GZIP_ENV = --best
+distuninstallcheck_listfiles = find . -type f -print
+distcleancheck_listfiles = find . -type f -print
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NMEDIT = @NMEDIT@
+OBJEXT = @OBJEXT@
+OPENLDAP_CPP_API_VERSION = @OPENLDAP_CPP_API_VERSION@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+EXTRA_DIST = COPYRIGHT doxygen.rc LICENSE version.var version.sh
+SUBDIRS = src examples
+all: all-recursive
+
+.SUFFIXES:
+am--refresh:
+ @:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign '; \
+ cd $(srcdir) && $(AUTOMAKE) --foreign \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ echo ' $(SHELL) ./config.status'; \
+ $(SHELL) ./config.status;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ $(SHELL) ./config.status --recheck
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(srcdir) && $(AUTOCONF)
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+distclean-libtool:
+ -rm -f libtool
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+# (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+$(RECURSIVE_CLEAN_TARGETS):
+ @failcom='exit 1'; \
+ for f in x $$MAKEFLAGS; do \
+ case $$f in \
+ *=* | --[!k]*);; \
+ *k*) failcom='fail=yes';; \
+ esac; \
+ done; \
+ dot_seen=no; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ rev=''; for subdir in $$list; do \
+ if test "$$subdir" = "."; then :; else \
+ rev="$$subdir $$rev"; \
+ fi; \
+ done; \
+ rev="$$rev ."; \
+ target=`echo $@ | sed s/-recursive//`; \
+ for subdir in $$rev; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done && test -z "$$fail"
+tags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+ done
+ctags-recursive:
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ $(am__remove_distdir)
+ test -d $(distdir) || mkdir $(distdir)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+ list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ distdir=`$(am__cd) $(distdir) && pwd`; \
+ top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
+ (cd $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$top_distdir" \
+ distdir="$$distdir/$$subdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+ -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \
+ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
+ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
+ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
+ || chmod -R a+r $(distdir)
+dist-gzip: distdir
+ tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+ $(am__remove_distdir)
+
+dist-bzip2: distdir
+ tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2
+ $(am__remove_distdir)
+
+dist-lzma: distdir
+ tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma
+ $(am__remove_distdir)
+
+dist-tarZ: distdir
+ tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
+ $(am__remove_distdir)
+
+dist-shar: distdir
+ shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
+ $(am__remove_distdir)
+
+dist-zip: distdir
+ -rm -f $(distdir).zip
+ zip -rq $(distdir).zip $(distdir)
+ $(am__remove_distdir)
+
+dist dist-all: distdir
+ tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+ $(am__remove_distdir)
+
+# This target untars the dist file and tries a VPATH configuration. Then
+# it guarantees that the distribution is self-contained by making another
+# tarfile.
+distcheck: dist
+ case '$(DIST_ARCHIVES)' in \
+ *.tar.gz*) \
+ GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\
+ *.tar.bz2*) \
+ bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\
+ *.tar.lzma*) \
+ unlzma -c $(distdir).tar.lzma | $(am__untar) ;;\
+ *.tar.Z*) \
+ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
+ *.shar.gz*) \
+ GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\
+ *.zip*) \
+ unzip $(distdir).zip ;;\
+ esac
+ chmod -R a-w $(distdir); chmod a+w $(distdir)
+ mkdir $(distdir)/_build
+ mkdir $(distdir)/_inst
+ chmod a-w $(distdir)
+ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
+ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
+ && cd $(distdir)/_build \
+ && ../configure --srcdir=.. --prefix="$$dc_install_base" \
+ $(DISTCHECK_CONFIGURE_FLAGS) \
+ && $(MAKE) $(AM_MAKEFLAGS) \
+ && $(MAKE) $(AM_MAKEFLAGS) dvi \
+ && $(MAKE) $(AM_MAKEFLAGS) check \
+ && $(MAKE) $(AM_MAKEFLAGS) install \
+ && $(MAKE) $(AM_MAKEFLAGS) installcheck \
+ && $(MAKE) $(AM_MAKEFLAGS) uninstall \
+ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
+ distuninstallcheck \
+ && chmod -R a-w "$$dc_install_base" \
+ && ({ \
+ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
+ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
+ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
+ } || { rm -rf "$$dc_destdir"; exit 1; }) \
+ && rm -rf "$$dc_destdir" \
+ && $(MAKE) $(AM_MAKEFLAGS) dist \
+ && rm -rf $(DIST_ARCHIVES) \
+ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck
+ $(am__remove_distdir)
+ @(echo "$(distdir) archives ready for distribution: "; \
+ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
+ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
+distuninstallcheck:
+ @cd $(distuninstallcheck_dir) \
+ && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \
+ || { echo "ERROR: files left after uninstall:" ; \
+ if test -n "$(DESTDIR)"; then \
+ echo " (check DESTDIR support)"; \
+ fi ; \
+ $(distuninstallcheck_listfiles) ; \
+ exit 1; } >&2
+distcleancheck: distclean
+ @if test '$(srcdir)' = . ; then \
+ echo "ERROR: distcleancheck can only run from a VPATH build" ; \
+ exit 1 ; \
+ fi
+ @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
+ || { echo "ERROR: files left in build directory after distclean:" ; \
+ $(distcleancheck_listfiles) ; \
+ exit 1; } >&2
+check-am: all-am
+check: check-recursive
+all-am: Makefile
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f $(am__CONFIG_DISTCLEAN_FILES)
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-libtool \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-info: install-info-recursive
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-ps: install-ps-recursive
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f $(am__CONFIG_DISTCLEAN_FILES)
+ -rm -rf $(top_srcdir)/autom4te.cache
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) install-am \
+ install-strip
+
+.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
+ all all-am am--refresh check check-am clean clean-generic \
+ clean-libtool ctags ctags-recursive dist dist-all dist-bzip2 \
+ dist-gzip dist-lzma dist-shar dist-tarZ dist-zip distcheck \
+ distclean distclean-generic distclean-libtool distclean-tags \
+ distcleancheck distdir distuninstallcheck dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs installdirs-am maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \
+ uninstall uninstall-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/contrib/ldapc++/README b/contrib/ldapc++/README
new file mode 100644
index 0000000..33a9135
--- /dev/null
+++ b/contrib/ldapc++/README
@@ -0,0 +1,37 @@
+This is an unstable development release of a LDAPv3 C++ Class Library.
+It was created as the diploma thesis (final project) of my computer
+science studies.
+It is based upon the OpenLDAP C-API and so it needs the C-library and
+Headerfiles installed.
+
+Installation:
+=============
+Just run the "configure" script with the appropriate options. Especially
+these two options can be imported, if you didn't install the OpenLDAP-
+libraries in the default place:
+
+--with-libldap=<path to libldap> : To tell configure where the OpenLDAP
+ C-libraries are located.
+--with-ldap-includes=<path to ldap include files> : To tell configure
+ where the OpenLDAP include files are located.
+--enable-debug to enable compilation with debugging symbols and stderr
+ output
+(run "configure --help" to see all possible command line options)
+
+If configure finishes without problems. You can simply call "make" to
+build the library and "make install" to install it.
+
+Documentation:
+==============
+Docs are very incomplete. You can either look in the source files for
+the documentation comment of generate documentation using "doxygen"
+or any other javadoc compatible documentation generator.
+
+Bugreports and other feedback:
+==============================
+If you find bugs please feel free to send me a detailed report. All
+other kinds of feedback are welcomed as well.
+
+
+ Ralf Haferkamp <rhafer@suse.de>
+
diff --git a/contrib/ldapc++/TODO b/contrib/ldapc++/TODO
new file mode 100644
index 0000000..643d573
--- /dev/null
+++ b/contrib/ldapc++/TODO
@@ -0,0 +1,31 @@
+OpenLDAP C++ LDAP API ToDo items:
+=================================
+
+This is a list of projects that need getting done for the C++ API.
+They are not listed in any specific order. Contribute to projects
+based upon your personal priorities.
+
+If you would like to work on any of these projects, please coordinate
+by posting to OpenLDAP-devel mailing list:
+ http://www.OpenLDAP.org/lists
+
+If you have a project you'd like added to the list, talk it up on
+Developer's list or just do it.
+
+Please read:
+ http://www.OpenLDAP.org/devel/programming.html
+ http://www.OpenLDAP.org/devel/contributing.html
+
+
+- Add SASL Authentication
+- Add methods to the Data Classes (LDAPAttribute, LDAPEntry) for higher
+ usability. (e.g. LDAPAttributeList::getAttribute(name), ... )
+- implement some Controls/Extended Operations
+- LDIF im/export library
+- Rework the logging and debugging facilities
+- write some more documentation about the design and structure of the
+ library.
+- example applications
+
+$ID$
+
diff --git a/contrib/ldapc++/aclocal.m4 b/contrib/ldapc++/aclocal.m4
new file mode 100644
index 0000000..8a9f08b
--- /dev/null
+++ b/contrib/ldapc++/aclocal.m4
@@ -0,0 +1,10228 @@
+# generated automatically by aclocal 1.15.1 -*- Autoconf -*-
+
+# Copyright (C) 1996-2017 Free Software Foundation, Inc.
+
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
+m4_ifndef([AC_AUTOCONF_VERSION],
+ [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],,
+[m4_warning([this file was generated for autoconf 2.69.
+You have another version of autoconf. It may work, but is not guaranteed to.
+If you have problems, you may need to regenerate the build system entirely.
+To do so, use the procedure documented by the package, typically 'autoreconf'.])])
+
+# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*-
+#
+# Copyright (C) 1996-2001, 2003-2015 Free Software Foundation, Inc.
+# Written by Gordon Matzigkeit, 1996
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+m4_define([_LT_COPYING], [dnl
+# Copyright (C) 2014 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of of the License, or
+# (at your option) any later version.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program or library that is built
+# using GNU Libtool, you may include this file under the same
+# distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+])
+
+# serial 58 LT_INIT
+
+
+# LT_PREREQ(VERSION)
+# ------------------
+# Complain and exit if this libtool version is less that VERSION.
+m4_defun([LT_PREREQ],
+[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1,
+ [m4_default([$3],
+ [m4_fatal([Libtool version $1 or higher is required],
+ 63)])],
+ [$2])])
+
+
+# _LT_CHECK_BUILDDIR
+# ------------------
+# Complain if the absolute build directory name contains unusual characters
+m4_defun([_LT_CHECK_BUILDDIR],
+[case `pwd` in
+ *\ * | *\ *)
+ AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;;
+esac
+])
+
+
+# LT_INIT([OPTIONS])
+# ------------------
+AC_DEFUN([LT_INIT],
+[AC_PREREQ([2.62])dnl We use AC_PATH_PROGS_FEATURE_CHECK
+AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
+AC_BEFORE([$0], [LT_LANG])dnl
+AC_BEFORE([$0], [LT_OUTPUT])dnl
+AC_BEFORE([$0], [LTDL_INIT])dnl
+m4_require([_LT_CHECK_BUILDDIR])dnl
+
+dnl Autoconf doesn't catch unexpanded LT_ macros by default:
+m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl
+m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl
+dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4
+dnl unless we require an AC_DEFUNed macro:
+AC_REQUIRE([LTOPTIONS_VERSION])dnl
+AC_REQUIRE([LTSUGAR_VERSION])dnl
+AC_REQUIRE([LTVERSION_VERSION])dnl
+AC_REQUIRE([LTOBSOLETE_VERSION])dnl
+m4_require([_LT_PROG_LTMAIN])dnl
+
+_LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}])
+
+dnl Parse OPTIONS
+_LT_SET_OPTIONS([$0], [$1])
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS=$ltmain
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+AC_SUBST(LIBTOOL)dnl
+
+_LT_SETUP
+
+# Only expand once:
+m4_define([LT_INIT])
+])# LT_INIT
+
+# Old names:
+AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT])
+AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PROG_LIBTOOL], [])
+dnl AC_DEFUN([AM_PROG_LIBTOOL], [])
+
+
+# _LT_PREPARE_CC_BASENAME
+# -----------------------
+m4_defun([_LT_PREPARE_CC_BASENAME], [
+# Calculate cc_basename. Skip known compiler wrappers and cross-prefix.
+func_cc_basename ()
+{
+ for cc_temp in @S|@*""; do
+ case $cc_temp in
+ compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;;
+ distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+ done
+ func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+}
+])# _LT_PREPARE_CC_BASENAME
+
+
+# _LT_CC_BASENAME(CC)
+# -------------------
+# It would be clearer to call AC_REQUIREs from _LT_PREPARE_CC_BASENAME,
+# but that macro is also expanded into generated libtool script, which
+# arranges for $SED and $ECHO to be set by different means.
+m4_defun([_LT_CC_BASENAME],
+[m4_require([_LT_PREPARE_CC_BASENAME])dnl
+AC_REQUIRE([_LT_DECL_SED])dnl
+AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl
+func_cc_basename $1
+cc_basename=$func_cc_basename_result
+])
+
+
+# _LT_FILEUTILS_DEFAULTS
+# ----------------------
+# It is okay to use these file commands and assume they have been set
+# sensibly after 'm4_require([_LT_FILEUTILS_DEFAULTS])'.
+m4_defun([_LT_FILEUTILS_DEFAULTS],
+[: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+])# _LT_FILEUTILS_DEFAULTS
+
+
+# _LT_SETUP
+# ---------
+m4_defun([_LT_SETUP],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl
+AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl
+
+_LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl
+dnl
+_LT_DECL([], [host_alias], [0], [The host system])dnl
+_LT_DECL([], [host], [0])dnl
+_LT_DECL([], [host_os], [0])dnl
+dnl
+_LT_DECL([], [build_alias], [0], [The build system])dnl
+_LT_DECL([], [build], [0])dnl
+_LT_DECL([], [build_os], [0])dnl
+dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+dnl
+AC_REQUIRE([AC_PROG_LN_S])dnl
+test -z "$LN_S" && LN_S="ln -s"
+_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl
+dnl
+AC_REQUIRE([LT_CMD_MAX_LEN])dnl
+_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl
+_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl
+dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_CHECK_SHELL_FEATURES])dnl
+m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl
+m4_require([_LT_CMD_RELOAD])dnl
+m4_require([_LT_CHECK_MAGIC_METHOD])dnl
+m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl
+m4_require([_LT_CMD_OLD_ARCHIVE])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+m4_require([_LT_WITH_SYSROOT])dnl
+m4_require([_LT_CMD_TRUNCATE])dnl
+
+_LT_CONFIG_LIBTOOL_INIT([
+# See if we are running on zsh, and set the options that allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}"; then
+ setopt NO_GLOB_SUBST
+fi
+])
+if test -n "${ZSH_VERSION+set}"; then
+ setopt NO_GLOB_SUBST
+fi
+
+_LT_CHECK_OBJDIR
+
+m4_require([_LT_TAG_COMPILER])dnl
+
+case $host_os in
+aix3*)
+ # AIX sometimes has problems with the GCC collect2 program. For some
+ # reason, if we set the COLLECT_NAMES environment variable, the problems
+ # vanish in a puff of smoke.
+ if test set != "${COLLECT_NAMES+set}"; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+ fi
+ ;;
+esac
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a '.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+old_CC=$CC
+old_CFLAGS=$CFLAGS
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+_LT_CC_BASENAME([$compiler])
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+ if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+ _LT_PATH_MAGIC
+ fi
+ ;;
+esac
+
+# Use C for the default configuration in the libtool script
+LT_SUPPORTED_TAG([CC])
+_LT_LANG_C_CONFIG
+_LT_LANG_DEFAULT_CONFIG
+_LT_CONFIG_COMMANDS
+])# _LT_SETUP
+
+
+# _LT_PREPARE_SED_QUOTE_VARS
+# --------------------------
+# Define a few sed substitution that help us do robust quoting.
+m4_defun([_LT_PREPARE_SED_QUOTE_VARS],
+[# Backslashify metacharacters that are still active within
+# double-quoted strings.
+sed_quote_subst='s/\([["`$\\]]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\([["`\\]]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+])
+
+# _LT_PROG_LTMAIN
+# ---------------
+# Note that this code is called both from 'configure', and 'config.status'
+# now that we use AC_CONFIG_COMMANDS to generate libtool. Notably,
+# 'config.status' has no value for ac_aux_dir unless we are using Automake,
+# so we pass a copy along to make sure it has a sensible value anyway.
+m4_defun([_LT_PROG_LTMAIN],
+[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl
+_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir'])
+ltmain=$ac_aux_dir/ltmain.sh
+])# _LT_PROG_LTMAIN
+
+
+
+# So that we can recreate a full libtool script including additional
+# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS
+# in macros and then make a single call at the end using the 'libtool'
+# label.
+
+
+# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS])
+# ----------------------------------------
+# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL_INIT],
+[m4_ifval([$1],
+ [m4_append([_LT_OUTPUT_LIBTOOL_INIT],
+ [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_INIT])
+
+
+# _LT_CONFIG_LIBTOOL([COMMANDS])
+# ------------------------------
+# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later.
+m4_define([_LT_CONFIG_LIBTOOL],
+[m4_ifval([$1],
+ [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS],
+ [$1
+])])])
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS])
+
+
+# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS])
+# -----------------------------------------------------
+m4_defun([_LT_CONFIG_SAVE_COMMANDS],
+[_LT_CONFIG_LIBTOOL([$1])
+_LT_CONFIG_LIBTOOL_INIT([$2])
+])
+
+
+# _LT_FORMAT_COMMENT([COMMENT])
+# -----------------------------
+# Add leading comment marks to the start of each line, and a trailing
+# full-stop to the whole comment if one is not present already.
+m4_define([_LT_FORMAT_COMMENT],
+[m4_ifval([$1], [
+m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])],
+ [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.])
+)])
+
+
+
+
+
+# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?])
+# -------------------------------------------------------------------
+# CONFIGNAME is the name given to the value in the libtool script.
+# VARNAME is the (base) name used in the configure script.
+# VALUE may be 0, 1 or 2 for a computed quote escaped value based on
+# VARNAME. Any other value will be used directly.
+m4_define([_LT_DECL],
+[lt_if_append_uniq([lt_decl_varnames], [$2], [, ],
+ [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name],
+ [m4_ifval([$1], [$1], [$2])])
+ lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3])
+ m4_ifval([$4],
+ [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])])
+ lt_dict_add_subkey([lt_decl_dict], [$2],
+ [tagged?], [m4_ifval([$5], [yes], [no])])])
+])
+
+
+# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION])
+# --------------------------------------------------------
+m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])])
+
+
+# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_tag_varnames],
+[_lt_decl_filter([tagged?], [yes], $@)])
+
+
+# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..])
+# ---------------------------------------------------------
+m4_define([_lt_decl_filter],
+[m4_case([$#],
+ [0], [m4_fatal([$0: too few arguments: $#])],
+ [1], [m4_fatal([$0: too few arguments: $#: $1])],
+ [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)],
+ [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)],
+ [lt_dict_filter([lt_decl_dict], $@)])[]dnl
+])
+
+
+# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...])
+# --------------------------------------------------
+m4_define([lt_decl_quote_varnames],
+[_lt_decl_filter([value], [1], $@)])
+
+
+# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_dquote_varnames],
+[_lt_decl_filter([value], [2], $@)])
+
+
+# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...])
+# ---------------------------------------------------
+m4_define([lt_decl_varnames_tagged],
+[m4_assert([$# <= 2])dnl
+_$0(m4_quote(m4_default([$1], [[, ]])),
+ m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]),
+ m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))])
+m4_define([_lt_decl_varnames_tagged],
+[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])])
+
+
+# lt_decl_all_varnames([SEPARATOR], [VARNAME1...])
+# ------------------------------------------------
+m4_define([lt_decl_all_varnames],
+[_$0(m4_quote(m4_default([$1], [[, ]])),
+ m4_if([$2], [],
+ m4_quote(lt_decl_varnames),
+ m4_quote(m4_shift($@))))[]dnl
+])
+m4_define([_lt_decl_all_varnames],
+[lt_join($@, lt_decl_varnames_tagged([$1],
+ lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl
+])
+
+
+# _LT_CONFIG_STATUS_DECLARE([VARNAME])
+# ------------------------------------
+# Quote a variable value, and forward it to 'config.status' so that its
+# declaration there will have the same value as in 'configure'. VARNAME
+# must have a single quote delimited value for this to work.
+m4_define([_LT_CONFIG_STATUS_DECLARE],
+[$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`'])
+
+
+# _LT_CONFIG_STATUS_DECLARATIONS
+# ------------------------------
+# We delimit libtool config variables with single quotes, so when
+# we write them to config.status, we have to be sure to quote all
+# embedded single quotes properly. In configure, this macro expands
+# each variable declared with _LT_DECL (and _LT_TAGDECL) into:
+#
+# <var>='`$ECHO "$<var>" | $SED "$delay_single_quote_subst"`'
+m4_defun([_LT_CONFIG_STATUS_DECLARATIONS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames),
+ [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAGS
+# ----------------
+# Output comment and list of tags supported by the script
+m4_defun([_LT_LIBTOOL_TAGS],
+[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl
+available_tags='_LT_TAGS'dnl
+])
+
+
+# _LT_LIBTOOL_DECLARE(VARNAME, [TAG])
+# -----------------------------------
+# Extract the dictionary values for VARNAME (optionally with TAG) and
+# expand to a commented shell variable setting:
+#
+# # Some comment about what VAR is for.
+# visible_name=$lt_internal_name
+m4_define([_LT_LIBTOOL_DECLARE],
+[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1],
+ [description])))[]dnl
+m4_pushdef([_libtool_name],
+ m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl
+m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])),
+ [0], [_libtool_name=[$]$1],
+ [1], [_libtool_name=$lt_[]$1],
+ [2], [_libtool_name=$lt_[]$1],
+ [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl
+m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl
+])
+
+
+# _LT_LIBTOOL_CONFIG_VARS
+# -----------------------
+# Produce commented declarations of non-tagged libtool config variables
+# suitable for insertion in the LIBTOOL CONFIG section of the 'libtool'
+# script. Tagged libtool config variables (even for the LIBTOOL CONFIG
+# section) are produced by _LT_LIBTOOL_TAG_VARS.
+m4_defun([_LT_LIBTOOL_CONFIG_VARS],
+[m4_foreach([_lt_var],
+ m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)),
+ [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])])
+
+
+# _LT_LIBTOOL_TAG_VARS(TAG)
+# -------------------------
+m4_define([_LT_LIBTOOL_TAG_VARS],
+[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames),
+ [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])])
+
+
+# _LT_TAGVAR(VARNAME, [TAGNAME])
+# ------------------------------
+m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])])
+
+
+# _LT_CONFIG_COMMANDS
+# -------------------
+# Send accumulated output to $CONFIG_STATUS. Thanks to the lists of
+# variables for single and double quote escaping we saved from calls
+# to _LT_DECL, we can put quote escaped variables declarations
+# into 'config.status', and then the shell code to quote escape them in
+# for loops in 'config.status'. Finally, any additional code accumulated
+# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded.
+m4_defun([_LT_CONFIG_COMMANDS],
+[AC_PROVIDE_IFELSE([LT_OUTPUT],
+ dnl If the libtool generation code has been placed in $CONFIG_LT,
+ dnl instead of duplicating it all over again into config.status,
+ dnl then we will have config.status run $CONFIG_LT later, so it
+ dnl needs to know what name is stored there:
+ [AC_CONFIG_COMMANDS([libtool],
+ [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])],
+ dnl If the libtool generation code is destined for config.status,
+ dnl expand the accumulated commands and init code now:
+ [AC_CONFIG_COMMANDS([libtool],
+ [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])])
+])#_LT_CONFIG_COMMANDS
+
+
+# Initialize.
+m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT],
+[
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+_LT_CONFIG_STATUS_DECLARATIONS
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+\$[]1
+_LTECHO_EOF'
+}
+
+# Quote evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_quote_varnames); do
+ case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+ *[[\\\\\\\`\\"\\\$]]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Double-quote double-evaled strings.
+for var in lt_decl_all_varnames([[ \
+]], lt_decl_dquote_varnames); do
+ case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+ *[[\\\\\\\`\\"\\\$]]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+_LT_OUTPUT_LIBTOOL_INIT
+])
+
+# _LT_GENERATED_FILE_INIT(FILE, [COMMENT])
+# ------------------------------------
+# Generate a child script FILE with all initialization necessary to
+# reuse the environment learned by the parent script, and make the
+# file executable. If COMMENT is supplied, it is inserted after the
+# '#!' sequence but before initialization text begins. After this
+# macro, additional text can be appended to FILE to form the body of
+# the child script. The macro ends with non-zero status if the
+# file could not be fully written (such as if the disk is full).
+m4_ifdef([AS_INIT_GENERATED],
+[m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])],
+[m4_defun([_LT_GENERATED_FILE_INIT],
+[m4_require([AS_PREPARE])]dnl
+[m4_pushdef([AS_MESSAGE_LOG_FD])]dnl
+[lt_write_fail=0
+cat >$1 <<_ASEOF || lt_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+$2
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$1 <<\_ASEOF || lt_write_fail=1
+AS_SHELL_SANITIZE
+_AS_PREPARE
+exec AS_MESSAGE_FD>&1
+_ASEOF
+test 0 = "$lt_write_fail" && chmod +x $1[]dnl
+m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT
+
+# LT_OUTPUT
+# ---------
+# This macro allows early generation of the libtool script (before
+# AC_OUTPUT is called), incase it is used in configure for compilation
+# tests.
+AC_DEFUN([LT_OUTPUT],
+[: ${CONFIG_LT=./config.lt}
+AC_MSG_NOTICE([creating $CONFIG_LT])
+_LT_GENERATED_FILE_INIT(["$CONFIG_LT"],
+[# Run this file to recreate a libtool stub with the current configuration.])
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+lt_cl_silent=false
+exec AS_MESSAGE_LOG_FD>>config.log
+{
+ echo
+ AS_BOX([Running $as_me.])
+} >&AS_MESSAGE_LOG_FD
+
+lt_cl_help="\
+'$as_me' creates a local libtool stub from the current configuration,
+for use in further configure time tests before the real libtool is
+generated.
+
+Usage: $[0] [[OPTIONS]]
+
+ -h, --help print this help, then exit
+ -V, --version print version number, then exit
+ -q, --quiet do not print progress messages
+ -d, --debug don't remove temporary files
+
+Report bugs to <bug-libtool@gnu.org>."
+
+lt_cl_version="\
+m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl
+m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION])
+configured by $[0], generated by m4_PACKAGE_STRING.
+
+Copyright (C) 2011 Free Software Foundation, Inc.
+This config.lt script is free software; the Free Software Foundation
+gives unlimited permision to copy, distribute and modify it."
+
+while test 0 != $[#]
+do
+ case $[1] in
+ --version | --v* | -V )
+ echo "$lt_cl_version"; exit 0 ;;
+ --help | --h* | -h )
+ echo "$lt_cl_help"; exit 0 ;;
+ --debug | --d* | -d )
+ debug=: ;;
+ --quiet | --q* | --silent | --s* | -q )
+ lt_cl_silent=: ;;
+
+ -*) AC_MSG_ERROR([unrecognized option: $[1]
+Try '$[0] --help' for more information.]) ;;
+
+ *) AC_MSG_ERROR([unrecognized argument: $[1]
+Try '$[0] --help' for more information.]) ;;
+ esac
+ shift
+done
+
+if $lt_cl_silent; then
+ exec AS_MESSAGE_FD>/dev/null
+fi
+_LTEOF
+
+cat >>"$CONFIG_LT" <<_LTEOF
+_LT_OUTPUT_LIBTOOL_COMMANDS_INIT
+_LTEOF
+
+cat >>"$CONFIG_LT" <<\_LTEOF
+AC_MSG_NOTICE([creating $ofile])
+_LT_OUTPUT_LIBTOOL_COMMANDS
+AS_EXIT(0)
+_LTEOF
+chmod +x "$CONFIG_LT"
+
+# configure is writing to config.log, but config.lt does its own redirection,
+# appending to config.log, which fails on DOS, as config.log is still kept
+# open by configure. Here we exec the FD to /dev/null, effectively closing
+# config.log, so it can be properly (re)opened and appended to by config.lt.
+lt_cl_success=:
+test yes = "$silent" &&
+ lt_config_lt_args="$lt_config_lt_args --quiet"
+exec AS_MESSAGE_LOG_FD>/dev/null
+$SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false
+exec AS_MESSAGE_LOG_FD>>config.log
+$lt_cl_success || AS_EXIT(1)
+])# LT_OUTPUT
+
+
+# _LT_CONFIG(TAG)
+# ---------------
+# If TAG is the built-in tag, create an initial libtool script with a
+# default configuration from the untagged config vars. Otherwise add code
+# to config.status for appending the configuration named by TAG from the
+# matching tagged config vars.
+m4_defun([_LT_CONFIG],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_CONFIG_SAVE_COMMANDS([
+ m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl
+ m4_if(_LT_TAG, [C], [
+ # See if we are running on zsh, and set the options that allow our
+ # commands through without removal of \ escapes.
+ if test -n "${ZSH_VERSION+set}"; then
+ setopt NO_GLOB_SUBST
+ fi
+
+ cfgfile=${ofile}T
+ trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+ $RM "$cfgfile"
+
+ cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+# Generated automatically by $as_me ($PACKAGE) $VERSION
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+
+# Provide generalized library-building support services.
+# Written by Gordon Matzigkeit, 1996
+
+_LT_COPYING
+_LT_LIBTOOL_TAGS
+
+# Configured defaults for sys_lib_dlsearch_path munging.
+: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"}
+
+# ### BEGIN LIBTOOL CONFIG
+_LT_LIBTOOL_CONFIG_VARS
+_LT_LIBTOOL_TAG_VARS
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+ cat <<'_LT_EOF' >> "$cfgfile"
+
+# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE
+
+_LT_PREPARE_MUNGE_PATH_LIST
+_LT_PREPARE_CC_BASENAME
+
+# ### END FUNCTIONS SHARED WITH CONFIGURE
+
+_LT_EOF
+
+ case $host_os in
+ aix3*)
+ cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program. For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test set != "${COLLECT_NAMES+set}"; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+fi
+_LT_EOF
+ ;;
+ esac
+
+ _LT_PROG_LTMAIN
+
+ # We use sed instead of cat because bash on DJGPP gets confused if
+ # if finds mixed CR/LF and LF-only lines. Since sed operates in
+ # text mode, it properly converts lines to CR/LF. This bash problem
+ # is reportedly fixed, but why not run on old versions too?
+ sed '$q' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ mv -f "$cfgfile" "$ofile" ||
+ (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+ chmod +x "$ofile"
+],
+[cat <<_LT_EOF >> "$ofile"
+
+dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded
+dnl in a comment (ie after a #).
+# ### BEGIN LIBTOOL TAG CONFIG: $1
+_LT_LIBTOOL_TAG_VARS(_LT_TAG)
+# ### END LIBTOOL TAG CONFIG: $1
+_LT_EOF
+])dnl /m4_if
+],
+[m4_if([$1], [], [
+ PACKAGE='$PACKAGE'
+ VERSION='$VERSION'
+ RM='$RM'
+ ofile='$ofile'], [])
+])dnl /_LT_CONFIG_SAVE_COMMANDS
+])# _LT_CONFIG
+
+
+# LT_SUPPORTED_TAG(TAG)
+# ---------------------
+# Trace this macro to discover what tags are supported by the libtool
+# --tag option, using:
+# autoconf --trace 'LT_SUPPORTED_TAG:$1'
+AC_DEFUN([LT_SUPPORTED_TAG], [])
+
+
+# C support is built-in for now
+m4_define([_LT_LANG_C_enabled], [])
+m4_define([_LT_TAGS], [])
+
+
+# LT_LANG(LANG)
+# -------------
+# Enable libtool support for the given language if not already enabled.
+AC_DEFUN([LT_LANG],
+[AC_BEFORE([$0], [LT_OUTPUT])dnl
+m4_case([$1],
+ [C], [_LT_LANG(C)],
+ [C++], [_LT_LANG(CXX)],
+ [Go], [_LT_LANG(GO)],
+ [Java], [_LT_LANG(GCJ)],
+ [Fortran 77], [_LT_LANG(F77)],
+ [Fortran], [_LT_LANG(FC)],
+ [Windows Resource], [_LT_LANG(RC)],
+ [m4_ifdef([_LT_LANG_]$1[_CONFIG],
+ [_LT_LANG($1)],
+ [m4_fatal([$0: unsupported language: "$1"])])])dnl
+])# LT_LANG
+
+
+# _LT_LANG(LANGNAME)
+# ------------------
+m4_defun([_LT_LANG],
+[m4_ifdef([_LT_LANG_]$1[_enabled], [],
+ [LT_SUPPORTED_TAG([$1])dnl
+ m4_append([_LT_TAGS], [$1 ])dnl
+ m4_define([_LT_LANG_]$1[_enabled], [])dnl
+ _LT_LANG_$1_CONFIG($1)])dnl
+])# _LT_LANG
+
+
+m4_ifndef([AC_PROG_GO], [
+# NOTE: This macro has been submitted for inclusion into #
+# GNU Autoconf as AC_PROG_GO. When it is available in #
+# a released version of Autoconf we should remove this #
+# macro and use it instead. #
+m4_defun([AC_PROG_GO],
+[AC_LANG_PUSH(Go)dnl
+AC_ARG_VAR([GOC], [Go compiler command])dnl
+AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl
+_AC_ARG_VAR_LDFLAGS()dnl
+AC_CHECK_TOOL(GOC, gccgo)
+if test -z "$GOC"; then
+ if test -n "$ac_tool_prefix"; then
+ AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo])
+ fi
+fi
+if test -z "$GOC"; then
+ AC_CHECK_PROG(GOC, gccgo, gccgo, false)
+fi
+])#m4_defun
+])#m4_ifndef
+
+
+# _LT_LANG_DEFAULT_CONFIG
+# -----------------------
+m4_defun([_LT_LANG_DEFAULT_CONFIG],
+[AC_PROVIDE_IFELSE([AC_PROG_CXX],
+ [LT_LANG(CXX)],
+ [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_F77],
+ [LT_LANG(F77)],
+ [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])])
+
+AC_PROVIDE_IFELSE([AC_PROG_FC],
+ [LT_LANG(FC)],
+ [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])])
+
+dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal
+dnl pulling things in needlessly.
+AC_PROVIDE_IFELSE([AC_PROG_GCJ],
+ [LT_LANG(GCJ)],
+ [AC_PROVIDE_IFELSE([A][M_PROG_GCJ],
+ [LT_LANG(GCJ)],
+ [AC_PROVIDE_IFELSE([LT_PROG_GCJ],
+ [LT_LANG(GCJ)],
+ [m4_ifdef([AC_PROG_GCJ],
+ [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])])
+ m4_ifdef([A][M_PROG_GCJ],
+ [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])])
+ m4_ifdef([LT_PROG_GCJ],
+ [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])])
+
+AC_PROVIDE_IFELSE([AC_PROG_GO],
+ [LT_LANG(GO)],
+ [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])])
+
+AC_PROVIDE_IFELSE([LT_PROG_RC],
+ [LT_LANG(RC)],
+ [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])])
+])# _LT_LANG_DEFAULT_CONFIG
+
+# Obsolete macros:
+AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)])
+AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)])
+AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)])
+AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)])
+AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_CXX], [])
+dnl AC_DEFUN([AC_LIBTOOL_F77], [])
+dnl AC_DEFUN([AC_LIBTOOL_FC], [])
+dnl AC_DEFUN([AC_LIBTOOL_GCJ], [])
+dnl AC_DEFUN([AC_LIBTOOL_RC], [])
+
+
+# _LT_TAG_COMPILER
+# ----------------
+m4_defun([_LT_TAG_COMPILER],
+[AC_REQUIRE([AC_PROG_CC])dnl
+
+_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl
+_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl
+_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl
+_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+])# _LT_TAG_COMPILER
+
+
+# _LT_COMPILER_BOILERPLATE
+# ------------------------
+# Check for compiler boilerplate output or warnings with
+# the simple compiler test code.
+m4_defun([_LT_COMPILER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+])# _LT_COMPILER_BOILERPLATE
+
+
+# _LT_LINKER_BOILERPLATE
+# ----------------------
+# Check for linker boilerplate output or warnings with
+# the simple link test code.
+m4_defun([_LT_LINKER_BOILERPLATE],
+[m4_require([_LT_DECL_SED])dnl
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+])# _LT_LINKER_BOILERPLATE
+
+# _LT_REQUIRED_DARWIN_CHECKS
+# -------------------------
+m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[
+ case $host_os in
+ rhapsody* | darwin*)
+ AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:])
+ AC_CHECK_TOOL([NMEDIT], [nmedit], [:])
+ AC_CHECK_TOOL([LIPO], [lipo], [:])
+ AC_CHECK_TOOL([OTOOL], [otool], [:])
+ AC_CHECK_TOOL([OTOOL64], [otool64], [:])
+ _LT_DECL([], [DSYMUTIL], [1],
+ [Tool to manipulate archived DWARF debug symbol files on Mac OS X])
+ _LT_DECL([], [NMEDIT], [1],
+ [Tool to change global to local symbols on Mac OS X])
+ _LT_DECL([], [LIPO], [1],
+ [Tool to manipulate fat objects and archives on Mac OS X])
+ _LT_DECL([], [OTOOL], [1],
+ [ldd/readelf like tool for Mach-O binaries on Mac OS X])
+ _LT_DECL([], [OTOOL64], [1],
+ [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4])
+
+ AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod],
+ [lt_cv_apple_cc_single_mod=no
+ if test -z "$LT_MULTI_MODULE"; then
+ # By default we will add the -single_module flag. You can override
+ # by either setting the environment variable LT_MULTI_MODULE
+ # non-empty at configure time, or by adding -multi_module to the
+ # link flags.
+ rm -rf libconftest.dylib*
+ echo "int foo(void){return 1;}" > conftest.c
+ echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD
+ $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+ _lt_result=$?
+ # If there is a non-empty error log, and "single_module"
+ # appears in it, assume the flag caused a linker warning
+ if test -s conftest.err && $GREP single_module conftest.err; then
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ # Otherwise, if the output was created with a 0 exit code from
+ # the compiler, it worked.
+ elif test -f libconftest.dylib && test 0 = "$_lt_result"; then
+ lt_cv_apple_cc_single_mod=yes
+ else
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ fi
+ rm -rf libconftest.dylib*
+ rm -f conftest.*
+ fi])
+
+ AC_CACHE_CHECK([for -exported_symbols_list linker flag],
+ [lt_cv_ld_exported_symbols_list],
+ [lt_cv_ld_exported_symbols_list=no
+ save_LDFLAGS=$LDFLAGS
+ echo "_main" > conftest.sym
+ LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+ [lt_cv_ld_exported_symbols_list=yes],
+ [lt_cv_ld_exported_symbols_list=no])
+ LDFLAGS=$save_LDFLAGS
+ ])
+
+ AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load],
+ [lt_cv_ld_force_load=no
+ cat > conftest.c << _LT_EOF
+int forced_loaded() { return 2;}
+_LT_EOF
+ echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD
+ $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD
+ echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD
+ $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD
+ echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD
+ $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD
+ cat > conftest.c << _LT_EOF
+int main() { return 0;}
+_LT_EOF
+ echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD
+ $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err
+ _lt_result=$?
+ if test -s conftest.err && $GREP force_load conftest.err; then
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then
+ lt_cv_ld_force_load=yes
+ else
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ fi
+ rm -f conftest.err libconftest.a conftest conftest.c
+ rm -rf conftest.dSYM
+ ])
+ case $host_os in
+ rhapsody* | darwin1.[[012]])
+ _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;;
+ darwin1.*)
+ _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
+ darwin*) # darwin 5.x on
+ # if running on 10.5 or later, the deployment target defaults
+ # to the OS version, if on x86, and 10.4, the deployment
+ # target defaults to 10.4. Don't you love it?
+ case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+ 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*)
+ _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
+ 10.[[012]][[,.]]*)
+ _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
+ 10.*)
+ _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
+ esac
+ ;;
+ esac
+ if test yes = "$lt_cv_apple_cc_single_mod"; then
+ _lt_dar_single_mod='$single_module'
+ fi
+ if test yes = "$lt_cv_ld_exported_symbols_list"; then
+ _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym'
+ else
+ _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib'
+ fi
+ if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then
+ _lt_dsymutil='~$DSYMUTIL $lib || :'
+ else
+ _lt_dsymutil=
+ fi
+ ;;
+ esac
+])
+
+
+# _LT_DARWIN_LINKER_FEATURES([TAG])
+# ---------------------------------
+# Checks for linker and compiler features on darwin
+m4_defun([_LT_DARWIN_LINKER_FEATURES],
+[
+ m4_require([_LT_REQUIRED_DARWIN_CHECKS])
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_automatic, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+ if test yes = "$lt_cv_ld_force_load"; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
+ m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes],
+ [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes])
+ else
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=''
+ fi
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(allow_undefined_flag, $1)=$_lt_dar_allow_undefined
+ case $cc_basename in
+ ifort*|nagfor*) _lt_dar_can_shared=yes ;;
+ *) _lt_dar_can_shared=$GCC ;;
+ esac
+ if test yes = "$_lt_dar_can_shared"; then
+ output_verbose_link_cmd=func_echo_all
+ _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil"
+ _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil"
+ _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil"
+ m4_if([$1], [CXX],
+[ if test yes != "$lt_cv_apple_cc_single_mod"; then
+ _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil"
+ fi
+],[])
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+])
+
+# _LT_SYS_MODULE_PATH_AIX([TAGNAME])
+# ----------------------------------
+# Links a minimal program and checks the executable
+# for the system default hardcoded library path. In most cases,
+# this is /usr/lib:/lib, but when the MPI compilers are used
+# the location of the communication and MPI libs are included too.
+# If we don't find anything, use the default library path according
+# to the aix ld manual.
+# Store the results from the different compilers for each TAGNAME.
+# Allow to override them for all tags through lt_cv_aix_libpath.
+m4_defun([_LT_SYS_MODULE_PATH_AIX],
+[m4_require([_LT_DECL_SED])dnl
+if test set = "${lt_cv_aix_libpath+set}"; then
+ aix_libpath=$lt_cv_aix_libpath
+else
+ AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])],
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM],[
+ lt_aix_libpath_sed='[
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\([^ ]*\) *$/\1/
+ p
+ }
+ }]'
+ _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ # Check for a 64-bit object if we didn't find anything.
+ if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then
+ _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ fi],[])
+ if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then
+ _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=/usr/lib:/lib
+ fi
+ ])
+ aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])
+fi
+])# _LT_SYS_MODULE_PATH_AIX
+
+
+# _LT_SHELL_INIT(ARG)
+# -------------------
+m4_define([_LT_SHELL_INIT],
+[m4_divert_text([M4SH-INIT], [$1
+])])# _LT_SHELL_INIT
+
+
+
+# _LT_PROG_ECHO_BACKSLASH
+# -----------------------
+# Find how we can fake an echo command that does not interpret backslash.
+# In particular, with Autoconf 2.60 or later we add some code to the start
+# of the generated configure script that will find a shell with a builtin
+# printf (that we can use as an echo command).
+m4_defun([_LT_PROG_ECHO_BACKSLASH],
+[ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
+
+AC_MSG_CHECKING([how to print strings])
+# Test print first, because it will be a builtin if present.
+if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \
+ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then
+ ECHO='print -r --'
+elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then
+ ECHO='printf %s\n'
+else
+ # Use this function as a fallback that always works.
+ func_fallback_echo ()
+ {
+ eval 'cat <<_LTECHO_EOF
+$[]1
+_LTECHO_EOF'
+ }
+ ECHO='func_fallback_echo'
+fi
+
+# func_echo_all arg...
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+ $ECHO "$*"
+}
+
+case $ECHO in
+ printf*) AC_MSG_RESULT([printf]) ;;
+ print*) AC_MSG_RESULT([print -r]) ;;
+ *) AC_MSG_RESULT([cat]) ;;
+esac
+
+m4_ifdef([_AS_DETECT_SUGGESTED],
+[_AS_DETECT_SUGGESTED([
+ test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || (
+ ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+ ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+ ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
+ PATH=/empty FPATH=/empty; export PATH FPATH
+ test "X`printf %s $ECHO`" = "X$ECHO" \
+ || test "X`print -r -- $ECHO`" = "X$ECHO" )])])
+
+_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts])
+_LT_DECL([], [ECHO], [1], [An echo program that protects backslashes])
+])# _LT_PROG_ECHO_BACKSLASH
+
+
+# _LT_WITH_SYSROOT
+# ----------------
+AC_DEFUN([_LT_WITH_SYSROOT],
+[AC_MSG_CHECKING([for sysroot])
+AC_ARG_WITH([sysroot],
+[AS_HELP_STRING([--with-sysroot@<:@=DIR@:>@],
+ [Search for dependent libraries within DIR (or the compiler's sysroot
+ if not specified).])],
+[], [with_sysroot=no])
+
+dnl lt_sysroot will always be passed unquoted. We quote it here
+dnl in case the user passed a directory name.
+lt_sysroot=
+case $with_sysroot in #(
+ yes)
+ if test yes = "$GCC"; then
+ lt_sysroot=`$CC --print-sysroot 2>/dev/null`
+ fi
+ ;; #(
+ /*)
+ lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"`
+ ;; #(
+ no|'')
+ ;; #(
+ *)
+ AC_MSG_RESULT([$with_sysroot])
+ AC_MSG_ERROR([The sysroot must be an absolute path.])
+ ;;
+esac
+
+ AC_MSG_RESULT([${lt_sysroot:-no}])
+_LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl
+[dependent libraries, and where our libraries should be installed.])])
+
+# _LT_ENABLE_LOCK
+# ---------------
+m4_defun([_LT_ENABLE_LOCK],
+[AC_ARG_ENABLE([libtool-lock],
+ [AS_HELP_STRING([--disable-libtool-lock],
+ [avoid locking (might break parallel builds)])])
+test no = "$enable_libtool_lock" || enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+ # Find out what ABI is being produced by ac_compile, and set mode
+ # options accordingly.
+ echo 'int i;' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *ELF-32*)
+ HPUX_IA64_MODE=32
+ ;;
+ *ELF-64*)
+ HPUX_IA64_MODE=64
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+*-*-irix6*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
+ echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ if test yes = "$lt_cv_prog_gnu_ld"; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -melf32bsmip"
+ ;;
+ *N32*)
+ LD="${LD-ld} -melf32bmipn32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -melf64bmip"
+ ;;
+ esac
+ else
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -32"
+ ;;
+ *N32*)
+ LD="${LD-ld} -n32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -64"
+ ;;
+ esac
+ fi
+ fi
+ rm -rf conftest*
+ ;;
+
+mips64*-*linux*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
+ echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ emul=elf
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ emul="${emul}32"
+ ;;
+ *64-bit*)
+ emul="${emul}64"
+ ;;
+ esac
+ case `/usr/bin/file conftest.$ac_objext` in
+ *MSB*)
+ emul="${emul}btsmip"
+ ;;
+ *LSB*)
+ emul="${emul}ltsmip"
+ ;;
+ esac
+ case `/usr/bin/file conftest.$ac_objext` in
+ *N32*)
+ emul="${emul}n32"
+ ;;
+ esac
+ LD="${LD-ld} -m $emul"
+ fi
+ rm -rf conftest*
+ ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly. Note that the listed cases only cover the
+ # situations where additional linker options are needed (such as when
+ # doing 32-bit compilation for a host where ld defaults to 64-bit, or
+ # vice versa); the common cases where no linker options are needed do
+ # not appear in the list.
+ echo 'int i;' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ case `/usr/bin/file conftest.o` in
+ *32-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_i386_fbsd"
+ ;;
+ x86_64-*linux*)
+ case `/usr/bin/file conftest.o` in
+ *x86-64*)
+ LD="${LD-ld} -m elf32_x86_64"
+ ;;
+ *)
+ LD="${LD-ld} -m elf_i386"
+ ;;
+ esac
+ ;;
+ powerpc64le-*linux*)
+ LD="${LD-ld} -m elf32lppclinux"
+ ;;
+ powerpc64-*linux*)
+ LD="${LD-ld} -m elf32ppclinux"
+ ;;
+ s390x-*linux*)
+ LD="${LD-ld} -m elf_s390"
+ ;;
+ sparc64-*linux*)
+ LD="${LD-ld} -m elf32_sparc"
+ ;;
+ esac
+ ;;
+ *64-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_x86_64_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ powerpcle-*linux*)
+ LD="${LD-ld} -m elf64lppc"
+ ;;
+ powerpc-*linux*)
+ LD="${LD-ld} -m elf64ppc"
+ ;;
+ s390*-*linux*|s390*-*tpf*)
+ LD="${LD-ld} -m elf64_s390"
+ ;;
+ sparc*-*linux*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+
+*-*-sco3.2v5*)
+ # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+ SAVE_CFLAGS=$CFLAGS
+ CFLAGS="$CFLAGS -belf"
+ AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf,
+ [AC_LANG_PUSH(C)
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no])
+ AC_LANG_POP])
+ if test yes != "$lt_cv_cc_needs_belf"; then
+ # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+ CFLAGS=$SAVE_CFLAGS
+ fi
+ ;;
+*-*solaris*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
+ echo 'int i;' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ case `/usr/bin/file conftest.o` in
+ *64-bit*)
+ case $lt_cv_prog_gnu_ld in
+ yes*)
+ case $host in
+ i?86-*-solaris*|x86_64-*-solaris*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ sparc*-*-solaris*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ # GNU ld 2.21 introduced _sol2 emulations. Use them if available.
+ if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then
+ LD=${LD-ld}_sol2
+ fi
+ ;;
+ *)
+ if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+ LD="${LD-ld} -64"
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+esac
+
+need_locks=$enable_libtool_lock
+])# _LT_ENABLE_LOCK
+
+
+# _LT_PROG_AR
+# -----------
+m4_defun([_LT_PROG_AR],
+[AC_CHECK_TOOLS(AR, [ar], false)
+: ${AR=ar}
+: ${AR_FLAGS=cru}
+_LT_DECL([], [AR], [1], [The archiver])
+_LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive])
+
+AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file],
+ [lt_cv_ar_at_file=no
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM],
+ [echo conftest.$ac_objext > conftest.lst
+ lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD'
+ AC_TRY_EVAL([lt_ar_try])
+ if test 0 -eq "$ac_status"; then
+ # Ensure the archiver fails upon bogus file names.
+ rm -f conftest.$ac_objext libconftest.a
+ AC_TRY_EVAL([lt_ar_try])
+ if test 0 -ne "$ac_status"; then
+ lt_cv_ar_at_file=@
+ fi
+ fi
+ rm -f conftest.* libconftest.a
+ ])
+ ])
+
+if test no = "$lt_cv_ar_at_file"; then
+ archiver_list_spec=
+else
+ archiver_list_spec=$lt_cv_ar_at_file
+fi
+_LT_DECL([], [archiver_list_spec], [1],
+ [How to feed a file listing to the archiver])
+])# _LT_PROG_AR
+
+
+# _LT_CMD_OLD_ARCHIVE
+# -------------------
+m4_defun([_LT_CMD_OLD_ARCHIVE],
+[_LT_PROG_AR
+
+AC_CHECK_TOOL(STRIP, strip, :)
+test -z "$STRIP" && STRIP=:
+_LT_DECL([], [STRIP], [1], [A symbol stripping program])
+
+AC_CHECK_TOOL(RANLIB, ranlib, :)
+test -z "$RANLIB" && RANLIB=:
+_LT_DECL([], [RANLIB], [1],
+ [Commands used to install an old-style archive])
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+ case $host_os in
+ bitrig* | openbsd*)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib"
+ ;;
+ *)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib"
+ ;;
+ esac
+ old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib"
+fi
+
+case $host_os in
+ darwin*)
+ lock_old_archive_extraction=yes ;;
+ *)
+ lock_old_archive_extraction=no ;;
+esac
+_LT_DECL([], [old_postinstall_cmds], [2])
+_LT_DECL([], [old_postuninstall_cmds], [2])
+_LT_TAGDECL([], [old_archive_cmds], [2],
+ [Commands used to build an old-style archive])
+_LT_DECL([], [lock_old_archive_extraction], [0],
+ [Whether to use a lock for old archive extraction])
+])# _LT_CMD_OLD_ARCHIVE
+
+
+# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------------------
+# Check whether the given compiler option works
+AC_DEFUN([_LT_COMPILER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+ [$2=no
+ m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4])
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="$3" ## exclude from sc_useless_quotes_in_assignment
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ $2=yes
+ fi
+ fi
+ $RM conftest*
+])
+
+if test yes = "[$]$2"; then
+ m4_if([$5], , :, [$5])
+else
+ m4_if([$6], , :, [$6])
+fi
+])# _LT_COMPILER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], [])
+
+
+# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS,
+# [ACTION-SUCCESS], [ACTION-FAILURE])
+# ----------------------------------------------------
+# Check whether the given linker option works
+AC_DEFUN([_LT_LINKER_OPTION],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_SED])dnl
+AC_CACHE_CHECK([$1], [$2],
+ [$2=no
+ save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS $3"
+ echo "$lt_simple_link_test_code" > conftest.$ac_ext
+ if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+ # The linker can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ # Append any errors to the config.log.
+ cat conftest.err 1>&AS_MESSAGE_LOG_FD
+ $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if diff conftest.exp conftest.er2 >/dev/null; then
+ $2=yes
+ fi
+ else
+ $2=yes
+ fi
+ fi
+ $RM -r conftest*
+ LDFLAGS=$save_LDFLAGS
+])
+
+if test yes = "[$]$2"; then
+ m4_if([$4], , :, [$4])
+else
+ m4_if([$5], , :, [$5])
+fi
+])# _LT_LINKER_OPTION
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], [])
+
+
+# LT_CMD_MAX_LEN
+#---------------
+AC_DEFUN([LT_CMD_MAX_LEN],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+# find the maximum length of command line arguments
+AC_MSG_CHECKING([the maximum length of command line arguments])
+AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
+ i=0
+ teststring=ABCD
+
+ case $build_os in
+ msdosdjgpp*)
+ # On DJGPP, this test can blow up pretty badly due to problems in libc
+ # (any single argument exceeding 2000 bytes causes a buffer overrun
+ # during glob expansion). Even if it were fixed, the result of this
+ # check would be larger than it should be.
+ lt_cv_sys_max_cmd_len=12288; # 12K is about right
+ ;;
+
+ gnu*)
+ # Under GNU Hurd, this test is not required because there is
+ # no limit to the length of command line arguments.
+ # Libtool will interpret -1 as no limit whatsoever
+ lt_cv_sys_max_cmd_len=-1;
+ ;;
+
+ cygwin* | mingw* | cegcc*)
+ # On Win9x/ME, this test blows up -- it succeeds, but takes
+ # about 5 minutes as the teststring grows exponentially.
+ # Worse, since 9x/ME are not pre-emptively multitasking,
+ # you end up with a "frozen" computer, even though with patience
+ # the test eventually succeeds (with a max line length of 256k).
+ # Instead, let's just punt: use the minimum linelength reported by
+ # all of the supported platforms: 8192 (on NT/2K/XP).
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ mint*)
+ # On MiNT this can take a long time and run out of memory.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ amigaos*)
+ # On AmigaOS with pdksh, this test takes hours, literally.
+ # So we just punt and use a minimum line length of 8192.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*)
+ # This has been around since 386BSD, at least. Likely further.
+ if test -x /sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+ elif test -x /usr/sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+ else
+ lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs
+ fi
+ # And add a safety zone
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ ;;
+
+ interix*)
+ # We know the value 262144 and hardcode it with a safety zone (like BSD)
+ lt_cv_sys_max_cmd_len=196608
+ ;;
+
+ os2*)
+ # The test takes a long time on OS/2.
+ lt_cv_sys_max_cmd_len=8192
+ ;;
+
+ osf*)
+ # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+ # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+ # nice to cause kernel panics so lets avoid the loop below.
+ # First set a reasonable default.
+ lt_cv_sys_max_cmd_len=16384
+ #
+ if test -x /sbin/sysconfig; then
+ case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+ *1*) lt_cv_sys_max_cmd_len=-1 ;;
+ esac
+ fi
+ ;;
+ sco3.2v5*)
+ lt_cv_sys_max_cmd_len=102400
+ ;;
+ sysv5* | sco5v6* | sysv4.2uw2*)
+ kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+ if test -n "$kargmax"; then
+ lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'`
+ else
+ lt_cv_sys_max_cmd_len=32768
+ fi
+ ;;
+ *)
+ lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+ if test -n "$lt_cv_sys_max_cmd_len" && \
+ test undefined != "$lt_cv_sys_max_cmd_len"; then
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ else
+ # Make teststring a little bigger before we do anything with it.
+ # a 1K string should be a reasonable start.
+ for i in 1 2 3 4 5 6 7 8; do
+ teststring=$teststring$teststring
+ done
+ SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+ # If test is not a shell built-in, we'll probably end up computing a
+ # maximum length that is only half of the actual maximum length, but
+ # we can't tell.
+ while { test X`env echo "$teststring$teststring" 2>/dev/null` \
+ = "X$teststring$teststring"; } >/dev/null 2>&1 &&
+ test 17 != "$i" # 1/2 MB should be enough
+ do
+ i=`expr $i + 1`
+ teststring=$teststring$teststring
+ done
+ # Only check the string length outside the loop.
+ lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+ teststring=
+ # Add a significant safety factor because C++ compilers can tack on
+ # massive amounts of additional arguments before passing them to the
+ # linker. It appears as though 1/2 is a usable value.
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+ fi
+ ;;
+ esac
+])
+if test -n "$lt_cv_sys_max_cmd_len"; then
+ AC_MSG_RESULT($lt_cv_sys_max_cmd_len)
+else
+ AC_MSG_RESULT(none)
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+_LT_DECL([], [max_cmd_len], [0],
+ [What is the maximum length of a command?])
+])# LT_CMD_MAX_LEN
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], [])
+
+
+# _LT_HEADER_DLFCN
+# ----------------
+m4_defun([_LT_HEADER_DLFCN],
+[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl
+])# _LT_HEADER_DLFCN
+
+
+# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE,
+# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING)
+# ----------------------------------------------------------------
+m4_defun([_LT_TRY_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test yes = "$cross_compiling"; then :
+ [$4]
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+[#line $LINENO "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+/* When -fvisibility=hidden is used, assume the code has been annotated
+ correspondingly for the symbols needed. */
+#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+int fnord () __attribute__((visibility("default")));
+#endif
+
+int fnord () { return 42; }
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else
+ {
+ if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ else puts (dlerror ());
+ }
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ return status;
+}]
+_LT_EOF
+ if AC_TRY_EVAL(ac_link) && test -s "conftest$ac_exeext" 2>/dev/null; then
+ (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) $1 ;;
+ x$lt_dlneed_uscore) $2 ;;
+ x$lt_dlunknown|x*) $3 ;;
+ esac
+ else :
+ # compilation failed
+ $3
+ fi
+fi
+rm -fr conftest*
+])# _LT_TRY_DLOPEN_SELF
+
+
+# LT_SYS_DLOPEN_SELF
+# ------------------
+AC_DEFUN([LT_SYS_DLOPEN_SELF],
+[m4_require([_LT_HEADER_DLFCN])dnl
+if test yes != "$enable_dlopen"; then
+ enable_dlopen=unknown
+ enable_dlopen_self=unknown
+ enable_dlopen_self_static=unknown
+else
+ lt_cv_dlopen=no
+ lt_cv_dlopen_libs=
+
+ case $host_os in
+ beos*)
+ lt_cv_dlopen=load_add_on
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ;;
+
+ mingw* | pw32* | cegcc*)
+ lt_cv_dlopen=LoadLibrary
+ lt_cv_dlopen_libs=
+ ;;
+
+ cygwin*)
+ lt_cv_dlopen=dlopen
+ lt_cv_dlopen_libs=
+ ;;
+
+ darwin*)
+ # if libdl is installed we need to link against it
+ AC_CHECK_LIB([dl], [dlopen],
+ [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],[
+ lt_cv_dlopen=dyld
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ])
+ ;;
+
+ tpf*)
+ # Don't try to run any link tests for TPF. We know it's impossible
+ # because TPF is a cross-compiler, and we know how we open DSOs.
+ lt_cv_dlopen=dlopen
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=no
+ ;;
+
+ *)
+ AC_CHECK_FUNC([shl_load],
+ [lt_cv_dlopen=shl_load],
+ [AC_CHECK_LIB([dld], [shl_load],
+ [lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld],
+ [AC_CHECK_FUNC([dlopen],
+ [lt_cv_dlopen=dlopen],
+ [AC_CHECK_LIB([dl], [dlopen],
+ [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],
+ [AC_CHECK_LIB([svld], [dlopen],
+ [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld],
+ [AC_CHECK_LIB([dld], [dld_link],
+ [lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld])
+ ])
+ ])
+ ])
+ ])
+ ])
+ ;;
+ esac
+
+ if test no = "$lt_cv_dlopen"; then
+ enable_dlopen=no
+ else
+ enable_dlopen=yes
+ fi
+
+ case $lt_cv_dlopen in
+ dlopen)
+ save_CPPFLAGS=$CPPFLAGS
+ test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+ save_LDFLAGS=$LDFLAGS
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+ save_LIBS=$LIBS
+ LIBS="$lt_cv_dlopen_libs $LIBS"
+
+ AC_CACHE_CHECK([whether a program can dlopen itself],
+ lt_cv_dlopen_self, [dnl
+ _LT_TRY_DLOPEN_SELF(
+ lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes,
+ lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross)
+ ])
+
+ if test yes = "$lt_cv_dlopen_self"; then
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+ AC_CACHE_CHECK([whether a statically linked program can dlopen itself],
+ lt_cv_dlopen_self_static, [dnl
+ _LT_TRY_DLOPEN_SELF(
+ lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes,
+ lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross)
+ ])
+ fi
+
+ CPPFLAGS=$save_CPPFLAGS
+ LDFLAGS=$save_LDFLAGS
+ LIBS=$save_LIBS
+ ;;
+ esac
+
+ case $lt_cv_dlopen_self in
+ yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+ *) enable_dlopen_self=unknown ;;
+ esac
+
+ case $lt_cv_dlopen_self_static in
+ yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+ *) enable_dlopen_self_static=unknown ;;
+ esac
+fi
+_LT_DECL([dlopen_support], [enable_dlopen], [0],
+ [Whether dlopen is supported])
+_LT_DECL([dlopen_self], [enable_dlopen_self], [0],
+ [Whether dlopen of programs is supported])
+_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0],
+ [Whether dlopen of statically linked programs is supported])
+])# LT_SYS_DLOPEN_SELF
+
+# Old name:
+AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], [])
+
+
+# _LT_COMPILER_C_O([TAGNAME])
+# ---------------------------
+# Check to see if options -c and -o are simultaneously supported by compiler.
+# This macro does not hard code the compiler like AC_PROG_CC_C_O.
+m4_defun([_LT_COMPILER_C_O],
+[m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext],
+ [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)],
+ [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&AS_MESSAGE_LOG_FD
+ echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+ fi
+ fi
+ chmod u+w . 2>&AS_MESSAGE_LOG_FD
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+])
+_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1],
+ [Does compiler simultaneously support -c and -o options?])
+])# _LT_COMPILER_C_O
+
+
+# _LT_COMPILER_FILE_LOCKS([TAGNAME])
+# ----------------------------------
+# Check to see if we can do hard links to lock some files if needed
+m4_defun([_LT_COMPILER_FILE_LOCKS],
+[m4_require([_LT_ENABLE_LOCK])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+_LT_COMPILER_C_O([$1])
+
+hard_links=nottested
+if test no = "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" && test no != "$need_locks"; then
+ # do not overwrite the value of need_locks provided by the user
+ AC_MSG_CHECKING([if we can lock with hard links])
+ hard_links=yes
+ $RM conftest*
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ touch conftest.a
+ ln conftest.a conftest.b 2>&5 || hard_links=no
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ AC_MSG_RESULT([$hard_links])
+ if test no = "$hard_links"; then
+ AC_MSG_WARN(['$CC' does not support '-c -o', so 'make -j' may be unsafe])
+ need_locks=warn
+ fi
+else
+ need_locks=no
+fi
+_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?])
+])# _LT_COMPILER_FILE_LOCKS
+
+
+# _LT_CHECK_OBJDIR
+# ----------------
+m4_defun([_LT_CHECK_OBJDIR],
+[AC_CACHE_CHECK([for objdir], [lt_cv_objdir],
+[rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+ lt_cv_objdir=.libs
+else
+ # MS-DOS does not allow filenames that begin with a dot.
+ lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null])
+objdir=$lt_cv_objdir
+_LT_DECL([], [objdir], [0],
+ [The name of the directory that contains temporary libtool files])dnl
+m4_pattern_allow([LT_OBJDIR])dnl
+AC_DEFINE_UNQUOTED([LT_OBJDIR], "$lt_cv_objdir/",
+ [Define to the sub-directory where libtool stores uninstalled libraries.])
+])# _LT_CHECK_OBJDIR
+
+
+# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME])
+# --------------------------------------
+# Check hardcoding attributes.
+m4_defun([_LT_LINKER_HARDCODE_LIBPATH],
+[AC_MSG_CHECKING([how to hardcode library paths into programs])
+_LT_TAGVAR(hardcode_action, $1)=
+if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" ||
+ test -n "$_LT_TAGVAR(runpath_var, $1)" ||
+ test yes = "$_LT_TAGVAR(hardcode_automatic, $1)"; then
+
+ # We can hardcode non-existent directories.
+ if test no != "$_LT_TAGVAR(hardcode_direct, $1)" &&
+ # If the only mechanism to avoid hardcoding is shlibpath_var, we
+ # have to relink, otherwise we might link with an installed library
+ # when we should be linking with a yet-to-be-installed one
+ ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" &&
+ test no != "$_LT_TAGVAR(hardcode_minus_L, $1)"; then
+ # Linking always hardcodes the temporary library directory.
+ _LT_TAGVAR(hardcode_action, $1)=relink
+ else
+ # We can link without hardcoding, and we can hardcode nonexisting dirs.
+ _LT_TAGVAR(hardcode_action, $1)=immediate
+ fi
+else
+ # We cannot hardcode anything, or else we can only hardcode existing
+ # directories.
+ _LT_TAGVAR(hardcode_action, $1)=unsupported
+fi
+AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)])
+
+if test relink = "$_LT_TAGVAR(hardcode_action, $1)" ||
+ test yes = "$_LT_TAGVAR(inherit_rpath, $1)"; then
+ # Fast installation is not supported
+ enable_fast_install=no
+elif test yes = "$shlibpath_overrides_runpath" ||
+ test no = "$enable_shared"; then
+ # Fast installation is not necessary
+ enable_fast_install=needless
+fi
+_LT_TAGDECL([], [hardcode_action], [0],
+ [How to hardcode a shared library path into an executable])
+])# _LT_LINKER_HARDCODE_LIBPATH
+
+
+# _LT_CMD_STRIPLIB
+# ----------------
+m4_defun([_LT_CMD_STRIPLIB],
+[m4_require([_LT_DECL_EGREP])
+striplib=
+old_striplib=
+AC_MSG_CHECKING([whether stripping libraries is possible])
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+ test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+ test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+ AC_MSG_RESULT([yes])
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+ case $host_os in
+ darwin*)
+ if test -n "$STRIP"; then
+ striplib="$STRIP -x"
+ old_striplib="$STRIP -S"
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ fi
+ ;;
+ *)
+ AC_MSG_RESULT([no])
+ ;;
+ esac
+fi
+_LT_DECL([], [old_striplib], [1], [Commands to strip libraries])
+_LT_DECL([], [striplib], [1])
+])# _LT_CMD_STRIPLIB
+
+
+# _LT_PREPARE_MUNGE_PATH_LIST
+# ---------------------------
+# Make sure func_munge_path_list() is defined correctly.
+m4_defun([_LT_PREPARE_MUNGE_PATH_LIST],
+[[# func_munge_path_list VARIABLE PATH
+# -----------------------------------
+# VARIABLE is name of variable containing _space_ separated list of
+# directories to be munged by the contents of PATH, which is string
+# having a format:
+# "DIR[:DIR]:"
+# string "DIR[ DIR]" will be prepended to VARIABLE
+# ":DIR[:DIR]"
+# string "DIR[ DIR]" will be appended to VARIABLE
+# "DIRP[:DIRP]::[DIRA:]DIRA"
+# string "DIRP[ DIRP]" will be prepended to VARIABLE and string
+# "DIRA[ DIRA]" will be appended to VARIABLE
+# "DIR[:DIR]"
+# VARIABLE will be replaced by "DIR[ DIR]"
+func_munge_path_list ()
+{
+ case x@S|@2 in
+ x)
+ ;;
+ *:)
+ eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'` \@S|@@S|@1\"
+ ;;
+ x:*)
+ eval @S|@1=\"\@S|@@S|@1 `$ECHO @S|@2 | $SED 's/:/ /g'`\"
+ ;;
+ *::*)
+ eval @S|@1=\"\@S|@@S|@1\ `$ECHO @S|@2 | $SED -e 's/.*:://' -e 's/:/ /g'`\"
+ eval @S|@1=\"`$ECHO @S|@2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \@S|@@S|@1\"
+ ;;
+ *)
+ eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'`\"
+ ;;
+ esac
+}
+]])# _LT_PREPARE_PATH_LIST
+
+
+# _LT_SYS_DYNAMIC_LINKER([TAG])
+# -----------------------------
+# PORTME Fill in your ld.so characteristics
+m4_defun([_LT_SYS_DYNAMIC_LINKER],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_OBJDUMP])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_CHECK_SHELL_FEATURES])dnl
+m4_require([_LT_PREPARE_MUNGE_PATH_LIST])dnl
+AC_MSG_CHECKING([dynamic linker characteristics])
+m4_if([$1],
+ [], [
+if test yes = "$GCC"; then
+ case $host_os in
+ darwin*) lt_awk_arg='/^libraries:/,/LR/' ;;
+ *) lt_awk_arg='/^libraries:/' ;;
+ esac
+ case $host_os in
+ mingw* | cegcc*) lt_sed_strip_eq='s|=\([[A-Za-z]]:\)|\1|g' ;;
+ *) lt_sed_strip_eq='s|=/|/|g' ;;
+ esac
+ lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq`
+ case $lt_search_path_spec in
+ *\;*)
+ # if the path contains ";" then we assume it to be the separator
+ # otherwise default to the standard path separator (i.e. ":") - it is
+ # assumed that no part of a normal pathname contains ";" but that should
+ # okay in the real world where ";" in dirpaths is itself problematic.
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'`
+ ;;
+ *)
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"`
+ ;;
+ esac
+ # Ok, now we have the path, separated by spaces, we can step through it
+ # and add multilib dir if necessary...
+ lt_tmp_lt_search_path_spec=
+ lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+ # ...but if some path component already ends with the multilib dir we assume
+ # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer).
+ case "$lt_multi_os_dir; $lt_search_path_spec " in
+ "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*)
+ lt_multi_os_dir=
+ ;;
+ esac
+ for lt_sys_path in $lt_search_path_spec; do
+ if test -d "$lt_sys_path$lt_multi_os_dir"; then
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir"
+ elif test -n "$lt_multi_os_dir"; then
+ test -d "$lt_sys_path" && \
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+ fi
+ done
+ lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk '
+BEGIN {RS = " "; FS = "/|\n";} {
+ lt_foo = "";
+ lt_count = 0;
+ for (lt_i = NF; lt_i > 0; lt_i--) {
+ if ($lt_i != "" && $lt_i != ".") {
+ if ($lt_i == "..") {
+ lt_count++;
+ } else {
+ if (lt_count == 0) {
+ lt_foo = "/" $lt_i lt_foo;
+ } else {
+ lt_count--;
+ }
+ }
+ }
+ }
+ if (lt_foo != "") { lt_freq[[lt_foo]]++; }
+ if (lt_freq[[lt_foo]] == 1) { print lt_foo; }
+}'`
+ # AWK program above erroneously prepends '/' to C:/dos/paths
+ # for these hosts.
+ case $host_os in
+ mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\
+ $SED 's|/\([[A-Za-z]]:\)|\1|g'` ;;
+ esac
+ sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP`
+else
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi])
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=.so
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+AC_ARG_VAR([LT_SYS_LIBRARY_PATH],
+[User-defined run-time library search path.])
+
+case $host_os in
+aix3*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname.a'
+ shlibpath_var=LIBPATH
+
+ # AIX 3 has no versioning support, so we append a major version to the name.
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+
+aix[[4-9]]*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ hardcode_into_libs=yes
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 supports IA64
+ library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ else
+ # With GCC up to 2.95.x, collect2 would create an import file
+ # for dependence libraries. The import file would start with
+ # the line '#! .'. This would cause the generated library to
+ # depend on '.', always an invalid library. This was fixed in
+ # development snapshots of GCC prior to 3.0.
+ case $host_os in
+ aix4 | aix4.[[01]] | aix4.[[01]].*)
+ if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+ echo ' yes '
+ echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then
+ :
+ else
+ can_build_shared=no
+ fi
+ ;;
+ esac
+ # Using Import Files as archive members, it is possible to support
+ # filename-based versioning of shared library archives on AIX. While
+ # this would work for both with and without runtime linking, it will
+ # prevent static linking of such archives. So we do filename-based
+ # shared library versioning with .so extension only, which is used
+ # when both runtime linking and shared linking is enabled.
+ # Unfortunately, runtime linking may impact performance, so we do
+ # not want this to be the default eventually. Also, we use the
+ # versioned .so libs for executables only if there is the -brtl
+ # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only.
+ # To allow for filename-based versioning support, we need to create
+ # libNAME.so.V as an archive file, containing:
+ # *) an Import File, referring to the versioned filename of the
+ # archive as well as the shared archive member, telling the
+ # bitwidth (32 or 64) of that shared object, and providing the
+ # list of exported symbols of that shared object, eventually
+ # decorated with the 'weak' keyword
+ # *) the shared object with the F_LOADONLY flag set, to really avoid
+ # it being seen by the linker.
+ # At run time we better use the real file rather than another symlink,
+ # but for link time we create the symlink libNAME.so -> libNAME.so.V
+
+ case $with_aix_soname,$aix_use_runtimelinking in
+ # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct
+ # soname into executable. Probably we can add versioning support to
+ # collect2, so additional links can be useful in future.
+ aix,yes) # traditional libtool
+ dynamic_linker='AIX unversionable lib.so'
+ # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+ # instead of lib<name>.a to let people know that these are not
+ # typical AIX shared libraries.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ ;;
+ aix,no) # traditional AIX only
+ dynamic_linker='AIX lib.a[(]lib.so.V[)]'
+ # We preserve .a as extension for shared libraries through AIX4.2
+ # and later when we are not doing run time linking.
+ library_names_spec='$libname$release.a $libname.a'
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+ svr4,*) # full svr4 only
+ dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)]"
+ library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
+ # We do not specify a path in Import Files, so LIBPATH fires.
+ shlibpath_overrides_runpath=yes
+ ;;
+ *,yes) # both, prefer svr4
+ dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)], lib.a[(]lib.so.V[)]"
+ library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
+ # unpreferred sharedlib libNAME.a needs extra handling
+ postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"'
+ postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"'
+ # We do not specify a path in Import Files, so LIBPATH fires.
+ shlibpath_overrides_runpath=yes
+ ;;
+ *,no) # both, prefer aix
+ dynamic_linker="AIX lib.a[(]lib.so.V[)], lib.so.V[(]$shared_archive_member_spec.o[)]"
+ library_names_spec='$libname$release.a $libname.a'
+ soname_spec='$libname$release$shared_ext$major'
+ # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling
+ postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)'
+ postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"'
+ ;;
+ esac
+ shlibpath_var=LIBPATH
+ fi
+ ;;
+
+amigaos*)
+ case $host_cpu in
+ powerpc)
+ # Since July 2007 AmigaOS4 officially supports .so libraries.
+ # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ ;;
+ m68k)
+ library_names_spec='$libname.ixlibrary $libname.a'
+ # Create ${libname}_ixlibrary.a entries in /sys/libs.
+ finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+ ;;
+ esac
+ ;;
+
+beos*)
+ library_names_spec='$libname$shared_ext'
+ dynamic_linker="$host_os ld.so"
+ shlibpath_var=LIBRARY_PATH
+ ;;
+
+bsdi[[45]]*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+ # the default ld.so.conf also contains /usr/contrib/lib and
+ # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+ # libtool to hard-code these into programs
+ ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+ version_type=windows
+ shrext_cmds=.dll
+ need_version=no
+ need_lib_prefix=no
+
+ case $GCC,$cc_basename in
+ yes,*)
+ # gcc
+ library_names_spec='$libname.dll.a'
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+
+ case $host_os in
+ cygwin*)
+ # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+ soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext'
+m4_if([$1], [],[
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"])
+ ;;
+ mingw* | cegcc*)
+ # MinGW DLLs use traditional 'lib' prefix
+ soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext'
+ ;;
+ pw32*)
+ # pw32 DLLs use 'pw' prefix rather than 'lib'
+ library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext'
+ ;;
+ esac
+ dynamic_linker='Win32 ld.exe'
+ ;;
+
+ *,cl*)
+ # Native MSVC
+ libname_spec='$name'
+ soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext'
+ library_names_spec='$libname.dll.lib'
+
+ case $build_os in
+ mingw*)
+ sys_lib_search_path_spec=
+ lt_save_ifs=$IFS
+ IFS=';'
+ for lt_path in $LIB
+ do
+ IFS=$lt_save_ifs
+ # Let DOS variable expansion print the short 8.3 style file name.
+ lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"`
+ sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path"
+ done
+ IFS=$lt_save_ifs
+ # Convert to MSYS style.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'`
+ ;;
+ cygwin*)
+ # Convert to unix form, then to dos form, then back to unix form
+ # but this time dos style (no spaces!) so that the unix form looks
+ # like /cygdrive/c/PROGRA~1:/cygdr...
+ sys_lib_search_path_spec=`cygpath --path --unix "$LIB"`
+ sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null`
+ sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ ;;
+ *)
+ sys_lib_search_path_spec=$LIB
+ if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then
+ # It is most probably a Windows format PATH.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ # FIXME: find the short name or the path components, as spaces are
+ # common. (e.g. "Program Files" -> "PROGRA~1")
+ ;;
+ esac
+
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+ dynamic_linker='Win32 link.exe'
+ ;;
+
+ *)
+ # Assume MSVC wrapper
+ library_names_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext $libname.lib'
+ dynamic_linker='Win32 ld.exe'
+ ;;
+ esac
+ # FIXME: first we should search . and the directory the executable is in
+ shlibpath_var=PATH
+ ;;
+
+darwin* | rhapsody*)
+ dynamic_linker="$host_os dyld"
+ version_type=darwin
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$major$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$major$shared_ext'
+ shlibpath_overrides_runpath=yes
+ shlibpath_var=DYLD_LIBRARY_PATH
+ shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+m4_if([$1], [],[
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"])
+ sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+ ;;
+
+dgux*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+freebsd* | dragonfly*)
+ # DragonFly does not have aout. When/if they implement a new
+ # versioning mechanism, adjust this.
+ if test -x /usr/bin/objformat; then
+ objformat=`/usr/bin/objformat`
+ else
+ case $host_os in
+ freebsd[[23]].*) objformat=aout ;;
+ *) objformat=elf ;;
+ esac
+ fi
+ version_type=freebsd-$objformat
+ case $version_type in
+ freebsd-elf*)
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ need_version=no
+ need_lib_prefix=no
+ ;;
+ freebsd-*)
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ need_version=yes
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_os in
+ freebsd2.*)
+ shlibpath_overrides_runpath=yes
+ ;;
+ freebsd3.[[01]]* | freebsdelf3.[[01]]*)
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \
+ freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1)
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+ *) # from 4.6 on, and DragonFly
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ esac
+ ;;
+
+haiku*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ dynamic_linker="$host_os runtime_loader"
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'
+ hardcode_into_libs=yes
+ ;;
+
+hpux9* | hpux10* | hpux11*)
+ # Give a soname corresponding to the major version so that dld.sl refuses to
+ # link against other versions.
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ case $host_cpu in
+ ia64*)
+ shrext_cmds='.so'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.so"
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ if test 32 = "$HPUX_IA64_MODE"; then
+ sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+ sys_lib_dlsearch_path_spec=/usr/lib/hpux32
+ else
+ sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+ sys_lib_dlsearch_path_spec=/usr/lib/hpux64
+ fi
+ ;;
+ hppa*64*)
+ shrext_cmds='.sl'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ *)
+ shrext_cmds='.sl'
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=SHLIB_PATH
+ shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+ esac
+ # HP-UX runs *really* slowly unless shared libraries are mode 555, ...
+ postinstall_cmds='chmod 555 $lib'
+ # or fails outright, so override atomically:
+ install_override_mode=555
+ ;;
+
+interix[[3-9]]*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $host_os in
+ nonstopux*) version_type=nonstopux ;;
+ *)
+ if test yes = "$lt_cv_prog_gnu_ld"; then
+ version_type=linux # correct to gnu/linux during the next big refactor
+ else
+ version_type=irix
+ fi ;;
+ esac
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext'
+ case $host_os in
+ irix5* | nonstopux*)
+ libsuff= shlibsuff=
+ ;;
+ *)
+ case $LD in # libtool.m4 will add one of these switches to LD
+ *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+ libsuff= shlibsuff= libmagic=32-bit;;
+ *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+ libsuff=32 shlibsuff=N32 libmagic=N32;;
+ *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+ libsuff=64 shlibsuff=64 libmagic=64-bit;;
+ *) libsuff= shlibsuff= libmagic=never-match;;
+ esac
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff"
+ sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff"
+ hardcode_into_libs=yes
+ ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+ dynamic_linker=no
+ ;;
+
+linux*android*)
+ version_type=none # Android doesn't support versioned libraries.
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext'
+ soname_spec='$libname$release$shared_ext'
+ finish_cmds=
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ dynamic_linker='Android linker'
+ # Don't embed -rpath directories since the linker doesn't support them.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+
+ # Some binutils ld are patched to set DT_RUNPATH
+ AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath],
+ [lt_cv_shlibpath_overrides_runpath=no
+ save_LDFLAGS=$LDFLAGS
+ save_libdir=$libdir
+ eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \
+ LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\""
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
+ [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null],
+ [lt_cv_shlibpath_overrides_runpath=yes])])
+ LDFLAGS=$save_LDFLAGS
+ libdir=$save_libdir
+ ])
+ shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ # Ideally, we could use ldconfig to report *all* directores which are
+ # searched for libraries, however this is still not possible. Aside from not
+ # being certain /sbin/ldconfig is available, command
+ # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64,
+ # even though it is searched at run-time. Try to do the best guess by
+ # appending ld.so.conf contents (and includes) to the search path.
+ if test -f /etc/ld.so.conf; then
+ lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
+ sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+ fi
+
+ # We used to test for /lib/ld.so.1 and disable shared libraries on
+ # powerpc, because MkLinux only supported shared libraries with the
+ # GNU dynamic linker. Since this was broken with cross compilers,
+ # most powerpc-linux boxes support dynamic linking these days and
+ # people can always --disable-shared, the test was removed, and we
+ # assume the GNU/Linux dynamic linker is in use.
+ dynamic_linker='GNU/Linux ld.so'
+ ;;
+
+netbsdelf*-gnu)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ dynamic_linker='NetBSD ld.elf_so'
+ ;;
+
+netbsd*)
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ dynamic_linker='NetBSD (a.out) ld.so'
+ else
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ dynamic_linker='NetBSD ld.elf_so'
+ fi
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+
+newsos6)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+*nto* | *qnx*)
+ version_type=qnx
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ dynamic_linker='ldqnx.so'
+ ;;
+
+openbsd* | bitrig*)
+ version_type=sunos
+ sys_lib_dlsearch_path_spec=/usr/lib
+ need_lib_prefix=no
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ need_version=no
+ else
+ need_version=yes
+ fi
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+os2*)
+ libname_spec='$name'
+ version_type=windows
+ shrext_cmds=.dll
+ need_version=no
+ need_lib_prefix=no
+ # OS/2 can only load a DLL with a base name of 8 characters or less.
+ soname_spec='`test -n "$os2dllname" && libname="$os2dllname";
+ v=$($ECHO $release$versuffix | tr -d .-);
+ n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _);
+ $ECHO $n$v`$shared_ext'
+ library_names_spec='${libname}_dll.$libext'
+ dynamic_linker='OS/2 ld.exe'
+ shlibpath_var=BEGINLIBPATH
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ ;;
+
+osf3* | osf4* | osf5*)
+ version_type=osf
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+
+rdos*)
+ dynamic_linker=no
+ ;;
+
+solaris*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ # ldd complains unless libraries are executable
+ postinstall_cmds='chmod +x $lib'
+ ;;
+
+sunos4*)
+ version_type=sunos
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ if test yes = "$with_gnu_ld"; then
+ need_lib_prefix=no
+ fi
+ need_version=yes
+ ;;
+
+sysv4 | sysv4.3*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_vendor in
+ sni)
+ shlibpath_overrides_runpath=no
+ need_lib_prefix=no
+ runpath_var=LD_RUN_PATH
+ ;;
+ siemens)
+ need_lib_prefix=no
+ ;;
+ motorola)
+ need_lib_prefix=no
+ need_version=no
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+ ;;
+ esac
+ ;;
+
+sysv4*MP*)
+ if test -d /usr/nec; then
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext'
+ soname_spec='$libname$shared_ext.$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ fi
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ version_type=sco
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ if test yes = "$with_gnu_ld"; then
+ sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+ else
+ sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+ case $host_os in
+ sco3.2v5*)
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+ ;;
+ esac
+ fi
+ sys_lib_dlsearch_path_spec='/usr/lib'
+ ;;
+
+tpf*)
+ # TPF is a cross-target only. Preferred cross-host = GNU/Linux.
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+uts4*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+*)
+ dynamic_linker=no
+ ;;
+esac
+AC_MSG_RESULT([$dynamic_linker])
+test no = "$dynamic_linker" && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test yes = "$GCC"; then
+ variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then
+ sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec
+fi
+
+if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then
+ sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec
+fi
+
+# remember unaugmented sys_lib_dlsearch_path content for libtool script decls...
+configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec
+
+# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code
+func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH"
+
+# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool
+configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH
+
+_LT_DECL([], [variables_saved_for_relink], [1],
+ [Variables whose values should be saved in libtool wrapper scripts and
+ restored at link time])
+_LT_DECL([], [need_lib_prefix], [0],
+ [Do we need the "lib" prefix for modules?])
+_LT_DECL([], [need_version], [0], [Do we need a version for libraries?])
+_LT_DECL([], [version_type], [0], [Library versioning type])
+_LT_DECL([], [runpath_var], [0], [Shared library runtime path variable])
+_LT_DECL([], [shlibpath_var], [0],[Shared library path variable])
+_LT_DECL([], [shlibpath_overrides_runpath], [0],
+ [Is shlibpath searched before the hard-coded library search path?])
+_LT_DECL([], [libname_spec], [1], [Format of library name prefix])
+_LT_DECL([], [library_names_spec], [1],
+ [[List of archive names. First name is the real one, the rest are links.
+ The last name is the one that the linker finds with -lNAME]])
+_LT_DECL([], [soname_spec], [1],
+ [[The coded name of the library, if different from the real name]])
+_LT_DECL([], [install_override_mode], [1],
+ [Permission mode override for installation of shared libraries])
+_LT_DECL([], [postinstall_cmds], [2],
+ [Command to use after installation of a shared archive])
+_LT_DECL([], [postuninstall_cmds], [2],
+ [Command to use after uninstallation of a shared archive])
+_LT_DECL([], [finish_cmds], [2],
+ [Commands used to finish a libtool library installation in a directory])
+_LT_DECL([], [finish_eval], [1],
+ [[As "finish_cmds", except a single script fragment to be evaled but
+ not shown]])
+_LT_DECL([], [hardcode_into_libs], [0],
+ [Whether we should hardcode library paths into libraries])
+_LT_DECL([], [sys_lib_search_path_spec], [2],
+ [Compile-time system search path for libraries])
+_LT_DECL([sys_lib_dlsearch_path_spec], [configure_time_dlsearch_path], [2],
+ [Detected run-time system search path for libraries])
+_LT_DECL([], [configure_time_lt_sys_library_path], [2],
+ [Explicit LT_SYS_LIBRARY_PATH set during ./configure time])
+])# _LT_SYS_DYNAMIC_LINKER
+
+
+# _LT_PATH_TOOL_PREFIX(TOOL)
+# --------------------------
+# find a file program that can recognize shared library
+AC_DEFUN([_LT_PATH_TOOL_PREFIX],
+[m4_require([_LT_DECL_EGREP])dnl
+AC_MSG_CHECKING([for $1])
+AC_CACHE_VAL(lt_cv_path_MAGIC_CMD,
+[case $MAGIC_CMD in
+[[\\/*] | ?:[\\/]*])
+ lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD=$MAGIC_CMD
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+dnl $ac_dummy forces splitting on constant user-supplied paths.
+dnl POSIX.2 word splitting is done only on the output of word expansions,
+dnl not every word. This closes a longstanding sh security hole.
+ ac_dummy="m4_if([$2], , $PATH, [$2])"
+ for ac_dir in $ac_dummy; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$1"; then
+ lt_cv_path_MAGIC_CMD=$ac_dir/"$1"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS=$lt_save_ifs
+ MAGIC_CMD=$lt_save_MAGIC_CMD
+ ;;
+esac])
+MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+if test -n "$MAGIC_CMD"; then
+ AC_MSG_RESULT($MAGIC_CMD)
+else
+ AC_MSG_RESULT(no)
+fi
+_LT_DECL([], [MAGIC_CMD], [0],
+ [Used to examine libraries when file_magic_cmd begins with "file"])dnl
+])# _LT_PATH_TOOL_PREFIX
+
+# Old name:
+AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], [])
+
+
+# _LT_PATH_MAGIC
+# --------------
+# find a file program that can recognize a shared library
+m4_defun([_LT_PATH_MAGIC],
+[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH)
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+ if test -n "$ac_tool_prefix"; then
+ _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH)
+ else
+ MAGIC_CMD=:
+ fi
+fi
+])# _LT_PATH_MAGIC
+
+
+# LT_PATH_LD
+# ----------
+# find the pathname to the GNU or non-GNU linker
+AC_DEFUN([LT_PATH_LD],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_PROG_ECHO_BACKSLASH])dnl
+
+AC_ARG_WITH([gnu-ld],
+ [AS_HELP_STRING([--with-gnu-ld],
+ [assume the C compiler uses GNU ld @<:@default=no@:>@])],
+ [test no = "$withval" || with_gnu_ld=yes],
+ [with_gnu_ld=no])dnl
+
+ac_prog=ld
+if test yes = "$GCC"; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ AC_MSG_CHECKING([for ld used by $CC])
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return, which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [[\\/]]* | ?:[[\\/]]*)
+ re_direlt='/[[^/]][[^/]]*/\.\./'
+ # Canonicalize the pathname of ld
+ ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+ while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD=$ac_prog
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test yes = "$with_gnu_ld"; then
+ AC_MSG_CHECKING([for GNU ld])
+else
+ AC_MSG_CHECKING([for non-GNU ld])
+fi
+AC_CACHE_VAL(lt_cv_path_LD,
+[if test -z "$LD"; then
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ lt_cv_path_LD=$ac_dir/$ac_prog
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some variants of GNU ld only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+ *GNU* | *'with BFD'*)
+ test no != "$with_gnu_ld" && break
+ ;;
+ *)
+ test yes != "$with_gnu_ld" && break
+ ;;
+ esac
+ fi
+ done
+ IFS=$lt_save_ifs
+else
+ lt_cv_path_LD=$LD # Let the user override the test with a path.
+fi])
+LD=$lt_cv_path_LD
+if test -n "$LD"; then
+ AC_MSG_RESULT($LD)
+else
+ AC_MSG_RESULT(no)
+fi
+test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH])
+_LT_PATH_LD_GNU
+AC_SUBST([LD])
+
+_LT_TAGDECL([], [LD], [1], [The linker used to build libraries])
+])# LT_PATH_LD
+
+# Old names:
+AU_ALIAS([AM_PROG_LD], [LT_PATH_LD])
+AU_ALIAS([AC_PROG_LD], [LT_PATH_LD])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_LD], [])
+dnl AC_DEFUN([AC_PROG_LD], [])
+
+
+# _LT_PATH_LD_GNU
+#- --------------
+m4_defun([_LT_PATH_LD_GNU],
+[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], lt_cv_prog_gnu_ld,
+[# I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ lt_cv_prog_gnu_ld=yes
+ ;;
+*)
+ lt_cv_prog_gnu_ld=no
+ ;;
+esac])
+with_gnu_ld=$lt_cv_prog_gnu_ld
+])# _LT_PATH_LD_GNU
+
+
+# _LT_CMD_RELOAD
+# --------------
+# find reload flag for linker
+# -- PORTME Some linkers may need a different reload flag.
+m4_defun([_LT_CMD_RELOAD],
+[AC_CACHE_CHECK([for $LD option to reload object files],
+ lt_cv_ld_reload_flag,
+ [lt_cv_ld_reload_flag='-r'])
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ if test yes != "$GCC"; then
+ reload_cmds=false
+ fi
+ ;;
+ darwin*)
+ if test yes = "$GCC"; then
+ reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs'
+ else
+ reload_cmds='$LD$reload_flag -o $output$reload_objs'
+ fi
+ ;;
+esac
+_LT_TAGDECL([], [reload_flag], [1], [How to create reloadable object files])dnl
+_LT_TAGDECL([], [reload_cmds], [2])dnl
+])# _LT_CMD_RELOAD
+
+
+# _LT_PATH_DD
+# -----------
+# find a working dd
+m4_defun([_LT_PATH_DD],
+[AC_CACHE_CHECK([for a working dd], [ac_cv_path_lt_DD],
+[printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+: ${lt_DD:=$DD}
+AC_PATH_PROGS_FEATURE_CHECK([lt_DD], [dd],
+[if "$ac_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
+ cmp -s conftest.i conftest.out \
+ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=:
+fi])
+rm -f conftest.i conftest2.i conftest.out])
+])# _LT_PATH_DD
+
+
+# _LT_CMD_TRUNCATE
+# ----------------
+# find command to truncate a binary pipe
+m4_defun([_LT_CMD_TRUNCATE],
+[m4_require([_LT_PATH_DD])
+AC_CACHE_CHECK([how to truncate binary pipes], [lt_cv_truncate_bin],
+[printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+lt_cv_truncate_bin=
+if "$ac_cv_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
+ cmp -s conftest.i conftest.out \
+ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1"
+fi
+rm -f conftest.i conftest2.i conftest.out
+test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"])
+_LT_DECL([lt_truncate_bin], [lt_cv_truncate_bin], [1],
+ [Command to truncate a binary pipe])
+])# _LT_CMD_TRUNCATE
+
+
+# _LT_CHECK_MAGIC_METHOD
+# ----------------------
+# how to check for library dependencies
+# -- PORTME fill in with the dynamic library characteristics
+m4_defun([_LT_CHECK_MAGIC_METHOD],
+[m4_require([_LT_DECL_EGREP])
+m4_require([_LT_DECL_OBJDUMP])
+AC_CACHE_CHECK([how to recognize dependent libraries],
+lt_cv_deplibs_check_method,
+[lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# 'unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# that responds to the $file_magic_cmd with a given extended regex.
+# If you have 'file' or equivalent on your system and you're not sure
+# whether 'pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[[4-9]]*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+beos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+bsdi[[45]]*)
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)'
+ lt_cv_file_magic_cmd='/usr/bin/file -L'
+ lt_cv_file_magic_test_file=/shlib/libc.so
+ ;;
+
+cygwin*)
+ # func_win32_libid is a shell function defined in ltmain.sh
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ ;;
+
+mingw* | pw32*)
+ # Base MSYS/MinGW do not provide the 'file' command needed by
+ # func_win32_libid shell function, so use a weaker test based on 'objdump',
+ # unless we find 'file', for example because we are cross-compiling.
+ if ( file / ) >/dev/null 2>&1; then
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ else
+ # Keep this pattern in sync with the one in func_win32_libid.
+ lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ fi
+ ;;
+
+cegcc*)
+ # use the weaker test based on 'objdump'. See mingw*.
+ lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ ;;
+
+darwin* | rhapsody*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+freebsd* | dragonfly*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ case $host_cpu in
+ i*86 )
+ # Not sure whether the presence of OpenBSD here was a mistake.
+ # Let's accept both of them until this is cleared up.
+ lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+ ;;
+ esac
+ else
+ lt_cv_deplibs_check_method=pass_all
+ fi
+ ;;
+
+haiku*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+hpux10.20* | hpux11*)
+ lt_cv_file_magic_cmd=/usr/bin/file
+ case $host_cpu in
+ ia64*)
+ lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64'
+ lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+ ;;
+ hppa*64*)
+ [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]']
+ lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+ ;;
+ *)
+ lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library'
+ lt_cv_file_magic_test_file=/usr/lib/libc.sl
+ ;;
+ esac
+ ;;
+
+interix[[3-9]]*)
+ # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$'
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $LD in
+ *-32|*"-32 ") libmagic=32-bit;;
+ *-n32|*"-n32 ") libmagic=N32;;
+ *-64|*"-64 ") libmagic=64-bit;;
+ *) libmagic=never-match;;
+ esac
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+netbsd* | netbsdelf*-gnu)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$'
+ fi
+ ;;
+
+newos6*)
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=/usr/lib/libnls.so
+ ;;
+
+*nto* | *qnx*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+openbsd* | bitrig*)
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
+ fi
+ ;;
+
+osf3* | osf4* | osf5*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+rdos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+solaris*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv4 | sysv4.3*)
+ case $host_vendor in
+ motorola)
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]'
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+ ;;
+ ncr)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ sequent)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )'
+ ;;
+ sni)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib"
+ lt_cv_file_magic_test_file=/lib/libc.so
+ ;;
+ siemens)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ pc)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ esac
+ ;;
+
+tpf*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+os2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+esac
+])
+
+file_magic_glob=
+want_nocaseglob=no
+if test "$build" = "$host"; then
+ case $host_os in
+ mingw* | pw32*)
+ if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then
+ want_nocaseglob=yes
+ else
+ file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"`
+ fi
+ ;;
+ esac
+fi
+
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+_LT_DECL([], [deplibs_check_method], [1],
+ [Method to check whether dependent libraries are shared objects])
+_LT_DECL([], [file_magic_cmd], [1],
+ [Command to use when deplibs_check_method = "file_magic"])
+_LT_DECL([], [file_magic_glob], [1],
+ [How to find potential files when deplibs_check_method = "file_magic"])
+_LT_DECL([], [want_nocaseglob], [1],
+ [Find potential files using nocaseglob when deplibs_check_method = "file_magic"])
+])# _LT_CHECK_MAGIC_METHOD
+
+
+# LT_PATH_NM
+# ----------
+# find the pathname to a BSD- or MS-compatible name lister
+AC_DEFUN([LT_PATH_NM],
+[AC_REQUIRE([AC_PROG_CC])dnl
+AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM,
+[if test -n "$NM"; then
+ # Let the user override the test.
+ lt_cv_path_NM=$NM
+else
+ lt_nm_to_check=${ac_tool_prefix}nm
+ if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+ lt_nm_to_check="$lt_nm_to_check nm"
+ fi
+ for lt_tmp_nm in $lt_nm_to_check; do
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ tmp_nm=$ac_dir/$lt_tmp_nm
+ if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then
+ # Check to see if the nm accepts a BSD-compat flag.
+ # Adding the 'sed 1q' prevents false positives on HP-UX, which says:
+ # nm: unknown option "B" ignored
+ # Tru64's nm complains that /dev/null is an invalid object file
+ # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty
+ case $build_os in
+ mingw*) lt_bad_file=conftest.nm/nofile ;;
+ *) lt_bad_file=/dev/null ;;
+ esac
+ case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in
+ *$lt_bad_file* | *'Invalid file or object type'*)
+ lt_cv_path_NM="$tmp_nm -B"
+ break 2
+ ;;
+ *)
+ case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+ */dev/null*)
+ lt_cv_path_NM="$tmp_nm -p"
+ break 2
+ ;;
+ *)
+ lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+ continue # so that we can try to find one that supports BSD flags
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ done
+ IFS=$lt_save_ifs
+ done
+ : ${lt_cv_path_NM=no}
+fi])
+if test no != "$lt_cv_path_NM"; then
+ NM=$lt_cv_path_NM
+else
+ # Didn't find any BSD compatible name lister, look for dumpbin.
+ if test -n "$DUMPBIN"; then :
+ # Let the user override the test.
+ else
+ AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :)
+ case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in
+ *COFF*)
+ DUMPBIN="$DUMPBIN -symbols -headers"
+ ;;
+ *)
+ DUMPBIN=:
+ ;;
+ esac
+ fi
+ AC_SUBST([DUMPBIN])
+ if test : != "$DUMPBIN"; then
+ NM=$DUMPBIN
+ fi
+fi
+test -z "$NM" && NM=nm
+AC_SUBST([NM])
+_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl
+
+AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface],
+ [lt_cv_nm_interface="BSD nm"
+ echo "int some_variable = 0;" > conftest.$ac_ext
+ (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$ac_compile" 2>conftest.err)
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD)
+ (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD)
+ cat conftest.out >&AS_MESSAGE_LOG_FD
+ if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+ lt_cv_nm_interface="MS dumpbin"
+ fi
+ rm -f conftest*])
+])# LT_PATH_NM
+
+# Old names:
+AU_ALIAS([AM_PROG_NM], [LT_PATH_NM])
+AU_ALIAS([AC_PROG_NM], [LT_PATH_NM])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_PROG_NM], [])
+dnl AC_DEFUN([AC_PROG_NM], [])
+
+# _LT_CHECK_SHAREDLIB_FROM_LINKLIB
+# --------------------------------
+# how to determine the name of the shared library
+# associated with a specific link library.
+# -- PORTME fill in with the dynamic library characteristics
+m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB],
+[m4_require([_LT_DECL_EGREP])
+m4_require([_LT_DECL_OBJDUMP])
+m4_require([_LT_DECL_DLLTOOL])
+AC_CACHE_CHECK([how to associate runtime and link libraries],
+lt_cv_sharedlib_from_linklib_cmd,
+[lt_cv_sharedlib_from_linklib_cmd='unknown'
+
+case $host_os in
+cygwin* | mingw* | pw32* | cegcc*)
+ # two different shell functions defined in ltmain.sh;
+ # decide which one to use based on capabilities of $DLLTOOL
+ case `$DLLTOOL --help 2>&1` in
+ *--identify-strict*)
+ lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib
+ ;;
+ *)
+ lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback
+ ;;
+ esac
+ ;;
+*)
+ # fallback: assume linklib IS sharedlib
+ lt_cv_sharedlib_from_linklib_cmd=$ECHO
+ ;;
+esac
+])
+sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd
+test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO
+
+_LT_DECL([], [sharedlib_from_linklib_cmd], [1],
+ [Command to associate shared and link libraries])
+])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB
+
+
+# _LT_PATH_MANIFEST_TOOL
+# ----------------------
+# locate the manifest tool
+m4_defun([_LT_PATH_MANIFEST_TOOL],
+[AC_CHECK_TOOL(MANIFEST_TOOL, mt, :)
+test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt
+AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool],
+ [lt_cv_path_mainfest_tool=no
+ echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD
+ $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ if $GREP 'Manifest Tool' conftest.out > /dev/null; then
+ lt_cv_path_mainfest_tool=yes
+ fi
+ rm -f conftest*])
+if test yes != "$lt_cv_path_mainfest_tool"; then
+ MANIFEST_TOOL=:
+fi
+_LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl
+])# _LT_PATH_MANIFEST_TOOL
+
+
+# _LT_DLL_DEF_P([FILE])
+# ---------------------
+# True iff FILE is a Windows DLL '.def' file.
+# Keep in sync with func_dll_def_p in the libtool script
+AC_DEFUN([_LT_DLL_DEF_P],
+[dnl
+ test DEF = "`$SED -n dnl
+ -e '\''s/^[[ ]]*//'\'' dnl Strip leading whitespace
+ -e '\''/^\(;.*\)*$/d'\'' dnl Delete empty lines and comments
+ -e '\''s/^\(EXPORTS\|LIBRARY\)\([[ ]].*\)*$/DEF/p'\'' dnl
+ -e q dnl Only consider the first "real" line
+ $1`" dnl
+])# _LT_DLL_DEF_P
+
+
+# LT_LIB_M
+# --------
+# check for math library
+AC_DEFUN([LT_LIB_M],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+LIBM=
+case $host in
+*-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*)
+ # These system don't have libm, or don't need it
+ ;;
+*-ncr-sysv4.3*)
+ AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM=-lmw)
+ AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm")
+ ;;
+*)
+ AC_CHECK_LIB(m, cos, LIBM=-lm)
+ ;;
+esac
+AC_SUBST([LIBM])
+])# LT_LIB_M
+
+# Old name:
+AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_CHECK_LIBM], [])
+
+
+# _LT_COMPILER_NO_RTTI([TAGNAME])
+# -------------------------------
+m4_defun([_LT_COMPILER_NO_RTTI],
+[m4_require([_LT_TAG_COMPILER])dnl
+
+_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+
+if test yes = "$GCC"; then
+ case $cc_basename in
+ nvcc*)
+ _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;;
+ esac
+
+ _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions],
+ lt_cv_prog_compiler_rtti_exceptions,
+ [-fno-rtti -fno-exceptions], [],
+ [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"])
+fi
+_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1],
+ [Compiler flag to turn off builtin functions])
+])# _LT_COMPILER_NO_RTTI
+
+
+# _LT_CMD_GLOBAL_SYMBOLS
+# ----------------------
+m4_defun([_LT_CMD_GLOBAL_SYMBOLS],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_PROG_CC])dnl
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+AC_REQUIRE([LT_PATH_LD])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+AC_MSG_CHECKING([command to parse $NM output from $compiler object])
+AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe],
+[
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix. What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[[BCDEGRST]]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+ symcode='[[BCDT]]'
+ ;;
+cygwin* | mingw* | pw32* | cegcc*)
+ symcode='[[ABCDGISTW]]'
+ ;;
+hpux*)
+ if test ia64 = "$host_cpu"; then
+ symcode='[[ABCDEGRST]]'
+ fi
+ ;;
+irix* | nonstopux*)
+ symcode='[[BCDEGRST]]'
+ ;;
+osf*)
+ symcode='[[BCDEGQRST]]'
+ ;;
+solaris*)
+ symcode='[[BDRT]]'
+ ;;
+sco3.2v5*)
+ symcode='[[DT]]'
+ ;;
+sysv4.2uw2*)
+ symcode='[[DT]]'
+ ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+ symcode='[[ABDT]]'
+ ;;
+sysv4)
+ symcode='[[DFNSTU]]'
+ ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+ symcode='[[ABCDGIRSTW]]' ;;
+esac
+
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Gets list of data symbols to import.
+ lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'"
+ # Adjust the below global symbol transforms to fixup imported variables.
+ lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'"
+ lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'"
+ lt_c_name_lib_hook="\
+ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\
+ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'"
+else
+ # Disable hooks by default.
+ lt_cv_sys_global_symbol_to_import=
+ lt_cdecl_hook=
+ lt_c_name_hook=
+ lt_c_name_lib_hook=
+fi
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n"\
+$lt_cdecl_hook\
+" -e 's/^T .* \(.*\)$/extern int \1();/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n"\
+$lt_c_name_hook\
+" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'"
+
+# Transform an extracted symbol line into symbol name with lib prefix and
+# symbol address.
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\
+$lt_c_name_lib_hook\
+" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+ opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+ ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+ # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+ symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+ # Write the raw and C identifiers.
+ if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Fake it for dumpbin and say T for any non-static function,
+ # D for any global variable and I for any imported variable.
+ # Also find C++ and __fastcall symbols from MSVC++,
+ # which start with @ or ?.
+ lt_cv_sys_global_symbol_pipe="$AWK ['"\
+" {last_section=section; section=\$ 3};"\
+" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\
+" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+" /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\
+" /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\
+" /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\
+" \$ 0!~/External *\|/{next};"\
+" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+" {if(hide[section]) next};"\
+" {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\
+" {split(\$ 0,a,/\||\r/); split(a[2],s)};"\
+" s[1]~/^[@?]/{print f,s[1],s[1]; next};"\
+" s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\
+" ' prfx=^$ac_symprfx]"
+ else
+ lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+ fi
+ lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'"
+
+ # Check to see that the pipe works correctly.
+ pipe_works=no
+
+ rm -f conftest*
+ cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+ if AC_TRY_EVAL(ac_compile); then
+ # Now try to grab the symbols.
+ nlist=conftest.nm
+ if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then
+ # Try sorting and uniquifying the output.
+ if sort "$nlist" | uniq > "$nlist"T; then
+ mv -f "$nlist"T "$nlist"
+ else
+ rm -f "$nlist"T
+ fi
+
+ # Make sure that we snagged all the symbols we need.
+ if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+ if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+ cat <<_LT_EOF > conftest.$ac_ext
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */
+#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE
+/* DATA imports from DLLs on WIN32 can't be const, because runtime
+ relocations are performed -- see ld's documentation on pseudo-relocs. */
+# define LT@&t@_DLSYM_CONST
+#elif defined __osf__
+/* This system does not cope well with relocations in const data. */
+# define LT@&t@_DLSYM_CONST
+#else
+# define LT@&t@_DLSYM_CONST const
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+ # Now generate the symbol file.
+ eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+ cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols. */
+LT@&t@_DLSYM_CONST struct {
+ const char *name;
+ void *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[[]] =
+{
+ { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+ $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+ cat <<\_LT_EOF >> conftest.$ac_ext
+ {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+ # Now try linking the two files.
+ mv conftest.$ac_objext conftstm.$ac_objext
+ lt_globsym_save_LIBS=$LIBS
+ lt_globsym_save_CFLAGS=$CFLAGS
+ LIBS=conftstm.$ac_objext
+ CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)"
+ if AC_TRY_EVAL(ac_link) && test -s conftest$ac_exeext; then
+ pipe_works=yes
+ fi
+ LIBS=$lt_globsym_save_LIBS
+ CFLAGS=$lt_globsym_save_CFLAGS
+ else
+ echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD
+ fi
+ else
+ echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD
+ fi
+ else
+ echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD
+ fi
+ else
+ echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD
+ cat conftest.$ac_ext >&5
+ fi
+ rm -rf conftest* conftst*
+
+ # Do not use the global_symbol_pipe unless it works.
+ if test yes = "$pipe_works"; then
+ break
+ else
+ lt_cv_sys_global_symbol_pipe=
+ fi
+done
+])
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+ lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+ AC_MSG_RESULT(failed)
+else
+ AC_MSG_RESULT(ok)
+fi
+
+# Response file support.
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ nm_file_list_spec='@'
+elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then
+ nm_file_list_spec='@'
+fi
+
+_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1],
+ [Take the output of nm and produce a listing of raw symbols and C names])
+_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1],
+ [Transform the output of nm in a proper C declaration])
+_LT_DECL([global_symbol_to_import], [lt_cv_sys_global_symbol_to_import], [1],
+ [Transform the output of nm into a list of symbols to manually relocate])
+_LT_DECL([global_symbol_to_c_name_address],
+ [lt_cv_sys_global_symbol_to_c_name_address], [1],
+ [Transform the output of nm in a C name address pair])
+_LT_DECL([global_symbol_to_c_name_address_lib_prefix],
+ [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1],
+ [Transform the output of nm in a C name address pair when lib prefix is needed])
+_LT_DECL([nm_interface], [lt_cv_nm_interface], [1],
+ [The name lister interface])
+_LT_DECL([], [nm_file_list_spec], [1],
+ [Specify filename containing input files for $NM])
+]) # _LT_CMD_GLOBAL_SYMBOLS
+
+
+# _LT_COMPILER_PIC([TAGNAME])
+# ---------------------------
+m4_defun([_LT_COMPILER_PIC],
+[m4_require([_LT_TAG_COMPILER])dnl
+_LT_TAGVAR(lt_prog_compiler_wl, $1)=
+_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+_LT_TAGVAR(lt_prog_compiler_static, $1)=
+
+m4_if([$1], [CXX], [
+ # C++ specific cases for pic, static, wl, etc.
+ if test yes = "$GXX"; then
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ fi
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the '-m68020' flag to GCC prevents building anything better,
+ # like '-m68040'.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+ mingw* | cygwin* | os2* | pw32* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ m4_if([$1], [GCJ], [],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ case $host_os in
+ os2*)
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static'
+ ;;
+ esac
+ ;;
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+ ;;
+ *djgpp*)
+ # DJGPP does not support shared libraries at all
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ ;;
+ haiku*)
+ # PIC is the default for Haiku.
+ # The "-static" flag exists, but is broken.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)=
+ ;;
+ interix[[3-9]]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+ fi
+ ;;
+ hpux*)
+ # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+ # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
+ # sets the default TLS model and affects inlining.
+ case $host_cpu in
+ hppa*64*)
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+ ;;
+ *qnx* | *nto*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+ else
+ case $host_os in
+ aix[[4-9]]*)
+ # All AIX code is PIC.
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ else
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+ chorus*)
+ case $cc_basename in
+ cxch68*)
+ # Green Hills C++ Compiler
+ # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a"
+ ;;
+ esac
+ ;;
+ mingw* | cygwin* | os2* | pw32* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ m4_if([$1], [GCJ], [],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ ;;
+ dgux*)
+ case $cc_basename in
+ ec++*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ ;;
+ ghcx*)
+ # Green Hills C++ Compiler
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ freebsd* | dragonfly*)
+ # FreeBSD uses GNU C++
+ ;;
+ hpux9* | hpux10* | hpux11*)
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive'
+ if test ia64 != "$host_cpu"; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+ fi
+ ;;
+ aCC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive'
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+ ;;
+ esac
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ interix*)
+ # This is c89, which is MS Visual C++ (no shared libs)
+ # Anyone wants to do a port?
+ ;;
+ irix5* | irix6* | nonstopux*)
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ # CC pic flag -KPIC is the default.
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ case $cc_basename in
+ KCC*)
+ # KAI C++ Compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ ecpc* )
+ # old Intel C++ for x86_64, which still supported -KPIC.
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ icpc* )
+ # Intel C++, used to be incompatible with GCC.
+ # ICC 10 doesn't accept -KPIC any more.
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ pgCC* | pgcpp*)
+ # Portland Group C++ compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ cxx*)
+ # Compaq C++
+ # Make sure the PIC flag is empty. It appears that all Alpha
+ # Linux and Compaq Tru64 Unix objects are PIC.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+ xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*)
+ # IBM XL 8.0, 9.0 on PPC and BlueGene
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C++ 5.9
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ lynxos*)
+ ;;
+ m88k*)
+ ;;
+ mvs*)
+ case $cc_basename in
+ cxx*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ netbsd* | netbsdelf*-gnu)
+ ;;
+ *qnx* | *nto*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+ osf3* | osf4* | osf5*)
+ case $cc_basename in
+ KCC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,'
+ ;;
+ RCC*)
+ # Rational C++ 2.4.1
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ ;;
+ cxx*)
+ # Digital/Compaq C++
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # Make sure the PIC flag is empty. It appears that all Alpha
+ # Linux and Compaq Tru64 Unix objects are PIC.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ psos*)
+ ;;
+ solaris*)
+ case $cc_basename in
+ CC* | sunCC*)
+ # Sun C++ 4.2, 5.x and Centerline C++
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ ;;
+ gcx*)
+ # Green Hills C++ Compiler
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ sunos4*)
+ case $cc_basename in
+ CC*)
+ # Sun C++ 4.x
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ lcc*)
+ # Lucid
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ esac
+ ;;
+ tandem*)
+ case $cc_basename in
+ NCC*)
+ # NonStop-UX NCC 3.20
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ vxworks*)
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ ;;
+ esac
+ fi
+],
+[
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ fi
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the '-m68020' flag to GCC prevents building anything better,
+ # like '-m68040'.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ m4_if([$1], [GCJ], [],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ case $host_os in
+ os2*)
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static'
+ ;;
+ esac
+ ;;
+
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+ ;;
+
+ haiku*)
+ # PIC is the default for Haiku.
+ # The "-static" flag exists, but is broken.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)=
+ ;;
+
+ hpux*)
+ # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+ # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
+ # sets the default TLS model and affects inlining.
+ case $host_cpu in
+ hppa*64*)
+ # +Z the default
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+ ;;
+
+ interix[[3-9]]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+
+ msdosdjgpp*)
+ # Just because we use GCC doesn't mean we suddenly get shared libraries
+ # on systems that don't support them.
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ enable_shared=no
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic
+ fi
+ ;;
+
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ ;;
+ esac
+
+ case $cc_basename in
+ nvcc*) # Cuda Compiler Driver 2.2
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker '
+ if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)"
+ fi
+ ;;
+ esac
+ else
+ # PORTME Check for flag to pass linker flags through the system compiler.
+ case $host_os in
+ aix*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 now supports IA64 processor
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ else
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+ case $cc_basename in
+ nagfor*)
+ # NAG Fortran compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ esac
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ m4_if([$1], [GCJ], [],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ case $host_os in
+ os2*)
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static'
+ ;;
+ esac
+ ;;
+
+ hpux9* | hpux10* | hpux11*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+ # not for PA HP-UX.
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
+ ;;
+ esac
+ # Is there a better lt_prog_compiler_static that works with the bundled CC?
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive'
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # PIC (with -KPIC) is the default.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+
+ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ case $cc_basename in
+ # old Intel for x86_64, which still supported -KPIC.
+ ecc*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ # icc used to be incompatible with GCC.
+ # ICC 10 doesn't accept -KPIC any more.
+ icc* | ifort*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ # Lahey Fortran 8.1.
+ lf95*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='--static'
+ ;;
+ nagfor*)
+ # NAG Fortran compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ tcc*)
+ # Fabrice Bellard et al's Tiny C Compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*)
+ # Portland Group compilers (*not* the Pentium gcc compiler,
+ # which looks to be a dead project)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ ccc*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # All Alpha code is PIC.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+ xl* | bgxl* | bgf* | mpixl*)
+ # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*)
+ # Sun Fortran 8.3 passes all unrecognized flags to the linker
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)=''
+ ;;
+ *Sun\ F* | *Sun*Fortran*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ ;;
+ *Sun\ C*)
+ # Sun C 5.9
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ ;;
+ *Intel*\ [[CF]]*Compiler*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ *Portland\ Group*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ newsos6)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared'
+ ;;
+
+ osf3* | osf4* | osf5*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ # All OSF/1 code is PIC.
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+
+ rdos*)
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
+ ;;
+
+ solaris*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ case $cc_basename in
+ f77* | f90* | f95* | sunf77* | sunf90* | sunf95*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';;
+ esac
+ ;;
+
+ sunos4*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ sysv4 | sysv4.2uw2* | sysv4.3*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ fi
+ ;;
+
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ unicos*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ ;;
+
+ uts4*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+
+ *)
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no
+ ;;
+ esac
+ fi
+])
+case $host_os in
+ # For platforms that do not support PIC, -DPIC is meaningless:
+ *djgpp*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ ;;
+ *)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])"
+ ;;
+esac
+
+AC_CACHE_CHECK([for $compiler option to produce PIC],
+ [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)],
+ [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)])
+_LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then
+ _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works],
+ [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)],
+ [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [],
+ [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in
+ "" | " "*) ;;
+ *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;;
+ esac],
+ [_LT_TAGVAR(lt_prog_compiler_pic, $1)=
+ _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no])
+fi
+_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1],
+ [Additional compiler flags for building library objects])
+
+_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1],
+ [How to pass a linker flag through the compiler])
+#
+# Check to make sure the static flag actually works.
+#
+wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\"
+_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works],
+ _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1),
+ $lt_tmp_static_flag,
+ [],
+ [_LT_TAGVAR(lt_prog_compiler_static, $1)=])
+_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1],
+ [Compiler flag to prevent dynamic linking])
+])# _LT_COMPILER_PIC
+
+
+# _LT_LINKER_SHLIBS([TAGNAME])
+# ----------------------------
+# See if the linker supports building shared libraries.
+m4_defun([_LT_LINKER_SHLIBS],
+[AC_REQUIRE([LT_PATH_LD])dnl
+AC_REQUIRE([LT_PATH_NM])dnl
+m4_require([_LT_PATH_MANIFEST_TOOL])dnl
+m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_DECL_SED])dnl
+m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
+m4_require([_LT_TAG_COMPILER])dnl
+AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+m4_if([$1], [CXX], [
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+ case $host_os in
+ aix[[4-9]]*)
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to GNU nm, but means don't demangle to AIX nm.
+ # Without the "-l" option, or with the "-B" option, AIX nm treats
+ # weak defined symbols like other global defined symbols, whereas
+ # GNU nm marks them as "W".
+ # While the 'weak' keyword is ignored in the Export File, we need
+ # it in the Import File for the 'aix-soname' feature, so we have
+ # to replace the "-B" option with "-P" for AIX nm.
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols'
+ else
+ _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols'
+ fi
+ ;;
+ pw32*)
+ _LT_TAGVAR(export_symbols_cmds, $1)=$ltdll_cmds
+ ;;
+ cygwin* | mingw* | cegcc*)
+ case $cc_basename in
+ cl*)
+ _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+ ;;
+ *)
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
+ _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname']
+ ;;
+ esac
+ ;;
+ linux* | k*bsd*-gnu | gnu*)
+ _LT_TAGVAR(link_all_deplibs, $1)=no
+ ;;
+ *)
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ ;;
+ esac
+], [
+ runpath_var=
+ _LT_TAGVAR(allow_undefined_flag, $1)=
+ _LT_TAGVAR(always_export_symbols, $1)=no
+ _LT_TAGVAR(archive_cmds, $1)=
+ _LT_TAGVAR(archive_expsym_cmds, $1)=
+ _LT_TAGVAR(compiler_needs_object, $1)=no
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ _LT_TAGVAR(hardcode_automatic, $1)=no
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=
+ _LT_TAGVAR(hardcode_minus_L, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+ _LT_TAGVAR(inherit_rpath, $1)=no
+ _LT_TAGVAR(link_all_deplibs, $1)=unknown
+ _LT_TAGVAR(module_cmds, $1)=
+ _LT_TAGVAR(module_expsym_cmds, $1)=
+ _LT_TAGVAR(old_archive_from_new_cmds, $1)=
+ _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)=
+ _LT_TAGVAR(thread_safe_flag_spec, $1)=
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ # include_expsyms should be a list of space-separated symbols to be *always*
+ # included in the symbol list
+ _LT_TAGVAR(include_expsyms, $1)=
+ # exclude_expsyms can be an extended regexp of symbols to exclude
+ # it will be wrapped by ' (' and ')$', so one must not match beginning or
+ # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc',
+ # as well as any symbol that contains 'd'.
+ _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
+ # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+ # platforms (ab)use it in PIC code, but their linkers get confused if
+ # the symbol is explicitly referenced. Since portable code cannot
+ # rely on this symbol name, it's probably fine to never include it in
+ # preloaded symbol tables.
+ # Exclude shared library initialization/finalization symbols.
+dnl Note also adjust exclude_expsyms for C++ above.
+ extract_expsyms_cmds=
+
+ case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ # FIXME: the MSVC++ port hasn't been tested in a loooong time
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ if test yes != "$GCC"; then
+ with_gnu_ld=no
+ fi
+ ;;
+ interix*)
+ # we just hope/assume this is gcc and not c89 (= MSVC++)
+ with_gnu_ld=yes
+ ;;
+ openbsd* | bitrig*)
+ with_gnu_ld=no
+ ;;
+ linux* | k*bsd*-gnu | gnu*)
+ _LT_TAGVAR(link_all_deplibs, $1)=no
+ ;;
+ esac
+
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+
+ # On some targets, GNU ld is compatible enough with the native linker
+ # that we're better off using the native interface for both.
+ lt_use_gnu_ld_interface=no
+ if test yes = "$with_gnu_ld"; then
+ case $host_os in
+ aix*)
+ # The AIX port of GNU ld has always aspired to compatibility
+ # with the native linker. However, as the warning in the GNU ld
+ # block says, versions before 2.19.5* couldn't really create working
+ # shared libraries, regardless of the interface used.
+ case `$LD -v 2>&1` in
+ *\ \(GNU\ Binutils\)\ 2.19.5*) ;;
+ *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;;
+ *\ \(GNU\ Binutils\)\ [[3-9]]*) ;;
+ *)
+ lt_use_gnu_ld_interface=yes
+ ;;
+ esac
+ ;;
+ *)
+ lt_use_gnu_ld_interface=yes
+ ;;
+ esac
+ fi
+
+ if test yes = "$lt_use_gnu_ld_interface"; then
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ wlarc='$wl'
+
+ # Set some defaults for GNU ld with shared library support. These
+ # are reset later if shared libraries are not supported. Putting them
+ # here allows them to be overridden if necessary.
+ runpath_var=LD_RUN_PATH
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
+ else
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ fi
+ supports_anon_versioning=no
+ case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in
+ *GNU\ gold*) supports_anon_versioning=yes ;;
+ *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11
+ *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+ *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+ *\ 2.11.*) ;; # other 2.11 versions
+ *) supports_anon_versioning=yes ;;
+ esac
+
+ # See if GNU ld supports shared libraries.
+ case $host_os in
+ aix[[3-9]]*)
+ # On AIX/PPC, the GNU linker is very broken
+ if test ia64 != "$host_cpu"; then
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.19, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support. If you
+*** really care for shared libraries, you may want to install binutils
+*** 2.20 or above, or modify your PATH so that a non-GNU linker is found.
+*** You will then need to restart the configuration process.
+
+_LT_EOF
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)=''
+ ;;
+ m68k)
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ ;;
+ esac
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+ # as there is no search path for DLLs.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols'
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=no
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
+ _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname']
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file, use it as
+ # is; otherwise, prepend EXPORTS...
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ haiku*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+
+ os2*)
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ shrext_cmds=.dll
+ _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ prefix_cmds="$SED"~
+ if test EXPORTS = "`$SED 1q $export_symbols`"; then
+ prefix_cmds="$prefix_cmds -e 1d";
+ fi~
+ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ ;;
+
+ interix[[3-9]]*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+
+ gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
+ tmp_diet=no
+ if test linux-dietlibc = "$host_os"; then
+ case $cc_basename in
+ diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn)
+ esac
+ fi
+ if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+ && test no = "$tmp_diet"
+ then
+ tmp_addflag=' $pic_flag'
+ tmp_sharedflag='-shared'
+ case $cc_basename,$host_cpu in
+ pgcc*) # Portland Group C compiler
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ tmp_addflag=' $pic_flag'
+ ;;
+ pgf77* | pgf90* | pgf95* | pgfortran*)
+ # Portland Group f77 and f90 compilers
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ tmp_addflag=' $pic_flag -Mnomain' ;;
+ ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64
+ tmp_addflag=' -i_dynamic' ;;
+ efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64
+ tmp_addflag=' -i_dynamic -nofor_main' ;;
+ ifc* | ifort*) # Intel Fortran compiler
+ tmp_addflag=' -nofor_main' ;;
+ lf95*) # Lahey Fortran 8.1
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ tmp_sharedflag='--shared' ;;
+ nagfor*) # NAGFOR 5.3
+ tmp_sharedflag='-Wl,-shared' ;;
+ xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+ tmp_sharedflag='-qmkshrobj'
+ tmp_addflag= ;;
+ nvcc*) # Cuda Compiler Driver 2.2
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ _LT_TAGVAR(compiler_needs_object, $1)=yes
+ ;;
+ esac
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*) # Sun C 5.9
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ _LT_TAGVAR(compiler_needs_object, $1)=yes
+ tmp_sharedflag='-G' ;;
+ *Sun\ F*) # Sun Fortran 8.3
+ tmp_sharedflag='-G' ;;
+ esac
+ _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+
+ if test yes = "$supports_anon_versioning"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib'
+ fi
+
+ case $cc_basename in
+ tcc*)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='-rdynamic'
+ ;;
+ xlf* | bgf* | bgxlf* | mpixlf*)
+ # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib'
+ if test yes = "$supports_anon_versioning"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ esac
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ netbsd* | netbsdelf*-gnu)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+ wlarc=
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ fi
+ ;;
+
+ solaris*)
+ if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+ case `$LD -v 2>&1` in
+ *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*)
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot
+*** reliably create shared libraries on SCO systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ ;;
+ *)
+ # For security reasons, it is highly recommended that you always
+ # use absolute paths for naming shared libraries, and exclude the
+ # DT_RUNPATH tag from executables and libraries. But doing so
+ # requires that you compile everything twice, which is a pain.
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ sunos4*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ wlarc=
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ *)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+
+ if test no = "$_LT_TAGVAR(ld_shlibs, $1)"; then
+ runpath_var=
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)=
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ fi
+ else
+ # PORTME fill in a description of your system's linker (not GNU ld)
+ case $host_os in
+ aix3*)
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+ # Note: this linker hardcodes the directories in LIBPATH if there
+ # are no directories specified by -L.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then
+ # Neither direct hardcoding nor static linking is supported with a
+ # broken collect2.
+ _LT_TAGVAR(hardcode_direct, $1)=unsupported
+ fi
+ ;;
+
+ aix[[4-9]]*)
+ if test ia64 = "$host_cpu"; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=
+ else
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to GNU nm, but means don't demangle to AIX nm.
+ # Without the "-l" option, or with the "-B" option, AIX nm treats
+ # weak defined symbols like other global defined symbols, whereas
+ # GNU nm marks them as "W".
+ # While the 'weak' keyword is ignored in the Export File, we need
+ # it in the Import File for the 'aix-soname' feature, so we have
+ # to replace the "-B" option with "-P" for AIX nm.
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols'
+ else
+ _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols'
+ fi
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # have runtime linking enabled, and use it for executables.
+ # For shared libraries, we enable/disable runtime linking
+ # depending on the kind of the shared library created -
+ # when "with_aix_soname,aix_use_runtimelinking" is:
+ # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables
+ # "aix,yes" lib.so shared, rtl:yes, for executables
+ # lib.a static archive
+ # "both,no" lib.so.V(shr.o) shared, rtl:yes
+ # lib.a(lib.so.V) shared, rtl:no, for executables
+ # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a(lib.so.V) shared, rtl:no
+ # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a static archive
+ case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+ for ld_flag in $LDFLAGS; do
+ if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then
+ aix_use_runtimelinking=yes
+ break
+ fi
+ done
+ if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # With aix-soname=svr4, we create the lib.so.V shared archives only,
+ # so we don't have lib.a shared libs to link our executables.
+ # We have to force runtime linking in this case.
+ aix_use_runtimelinking=yes
+ LDFLAGS="$LDFLAGS -Wl,-brtl"
+ fi
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ _LT_TAGVAR(archive_cmds, $1)=''
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(file_list_spec, $1)='$wl-f,'
+ case $with_aix_soname,$aix_use_runtimelinking in
+ aix,*) ;; # traditional, no import file
+ svr4,* | *,yes) # use import file
+ # The Import File defines what to hardcode.
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=no
+ ;;
+ esac
+
+ if test yes = "$GCC"; then
+ case $host_os in aix4.[[012]]|aix4.[[012]].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`$CC -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ _LT_TAGVAR(hardcode_direct, $1)=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=
+ fi
+ ;;
+ esac
+ shared_flag='-shared'
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag="$shared_flag "'$wl-G'
+ fi
+ # Need to ensure runtime linking is disabled for the traditional
+ # shared library, or the linker may eventually find shared libraries
+ # /with/ Import File - we do not want to mix them.
+ shared_flag_aix='-shared'
+ shared_flag_svr4='-shared $wl-G'
+ else
+ # not using gcc
+ if test ia64 = "$host_cpu"; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag='$wl-G'
+ else
+ shared_flag='$wl-bM:SRE'
+ fi
+ shared_flag_aix='$wl-bM:SRE'
+ shared_flag_svr4='$wl-G'
+ fi
+ fi
+
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall'
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to export.
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ _LT_TAGVAR(allow_undefined_flag, $1)='-berok'
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ _LT_SYS_MODULE_PATH_AIX([$1])
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath"
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag
+ else
+ if test ia64 = "$host_cpu"; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib'
+ _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ _LT_SYS_MODULE_PATH_AIX([$1])
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok'
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok'
+ if test yes = "$with_gnu_ld"; then
+ # We only use this code for GNU lds that support --whole-archive.
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive'
+ else
+ # Exported symbols can be pulled into shared objects from archives
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d'
+ # -brtl affects multiple linker settings, -berok does not and is overridden later
+ compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`'
+ if test svr4 != "$with_aix_soname"; then
+ # This is similar to how AIX traditionally builds its shared libraries.
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname'
+ fi
+ if test aix != "$with_aix_soname"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp'
+ else
+ # used by -dlpreopen to get the symbols
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir'
+ fi
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d'
+ fi
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)=''
+ ;;
+ m68k)
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ ;;
+ esac
+ ;;
+
+ bsdi[[45]]*)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ case $cc_basename in
+ cl*)
+ # Native MSVC
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ _LT_TAGVAR(file_list_spec, $1)='@'
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=.dll
+ # FIXME: Setting linknames here is a bad hack.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames='
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then
+ cp "$export_symbols" "$output_objdir/$soname.def";
+ echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp";
+ else
+ $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp;
+ fi~
+ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+ linknames='
+ # The linker will not automatically build a static lib if we build a DLL.
+ # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols'
+ # Don't use ranlib
+ _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib'
+ _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~
+ lt_tool_outputfile="@TOOL_OUTPUT@"~
+ case $lt_outputfile in
+ *.exe|*.EXE) ;;
+ *)
+ lt_outputfile=$lt_outputfile.exe
+ lt_tool_outputfile=$lt_tool_outputfile.exe
+ ;;
+ esac~
+ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then
+ $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+ $RM "$lt_outputfile.manifest";
+ fi'
+ ;;
+ *)
+ # Assume MSVC wrapper
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=.dll
+ # FIXME: Setting linknames here is a bad hack.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames='
+ # The linker will automatically build a .lib file if we build a DLL.
+ _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+ # FIXME: Should let the user specify the lib program.
+ _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ ;;
+ esac
+ ;;
+
+ darwin* | rhapsody*)
+ _LT_DARWIN_LINKER_FEATURES($1)
+ ;;
+
+ dgux*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+ # support. Future versions do this automatically, but an explicit c++rt0.o
+ # does not break anything, and helps significantly (at the cost of a little
+ # extra space).
+ freebsd2.2*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+ freebsd2.*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+ freebsd* | dragonfly*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ hpux9*)
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ ;;
+
+ hpux10*)
+ if test yes,no = "$GCC,$with_gnu_ld"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ if test no = "$with_gnu_ld"; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ fi
+ ;;
+
+ hpux11*)
+ if test yes,no = "$GCC,$with_gnu_ld"; then
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ else
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ m4_if($1, [], [
+ # Older versions of the 11.00 compiler do not understand -b yet
+ # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does)
+ _LT_LINKER_OPTION([if $CC understands -b],
+ _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b],
+ [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'],
+ [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])],
+ [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'])
+ ;;
+ esac
+ fi
+ if test no = "$with_gnu_ld"; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+ *)
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ ;;
+ esac
+ fi
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ # Try to use the -exported_symbol ld option, if it does not
+ # work, assume that -exports_file does not work either and
+ # implicitly export all symbols.
+ # This should be the same for all languages, so no per-tag cache variable.
+ AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol],
+ [lt_cv_irix_exported_symbol],
+ [save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null"
+ AC_LINK_IFELSE(
+ [AC_LANG_SOURCE(
+ [AC_LANG_CASE([C], [[int foo (void) { return 0; }]],
+ [C++], [[int foo (void) { return 0; }]],
+ [Fortran 77], [[
+ subroutine foo
+ end]],
+ [Fortran], [[
+ subroutine foo
+ end]])])],
+ [lt_cv_irix_exported_symbol=yes],
+ [lt_cv_irix_exported_symbol=no])
+ LDFLAGS=$save_LDFLAGS])
+ if test yes = "$lt_cv_irix_exported_symbol"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib'
+ fi
+ _LT_TAGVAR(link_all_deplibs, $1)=no
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(inherit_rpath, $1)=yes
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+
+ linux*)
+ case $cc_basename in
+ tcc*)
+ # Fabrice Bellard et al's Tiny C Compiler
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
+ netbsd* | netbsdelf*-gnu)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ newsos6)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ *nto* | *qnx*)
+ ;;
+
+ openbsd* | bitrig*)
+ if test -f /usr/libexec/ld.so; then
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ fi
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ os2*)
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ shrext_cmds=.dll
+ _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ prefix_cmds="$SED"~
+ if test EXPORTS = "`$SED 1q $export_symbols`"; then
+ prefix_cmds="$prefix_cmds -e 1d";
+ fi~
+ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ ;;
+
+ osf3*)
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ else
+ _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ ;;
+
+ osf4* | osf5*) # as osf3* with the addition of -msym flag
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ else
+ _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp'
+
+ # Both c and cxx compiler support -rpath directly
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)='no'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ ;;
+
+ solaris*)
+ _LT_TAGVAR(no_undefined_flag, $1)=' -z defs'
+ if test yes = "$GCC"; then
+ wlarc='$wl'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ else
+ case `$CC -V 2>&1` in
+ *"Compilers 5.0"*)
+ wlarc=''
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+ ;;
+ *)
+ wlarc='$wl'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ ;;
+ esac
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ case $host_os in
+ solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands '-z linker_flag'. GCC discards it without '$wl',
+ # but is careful enough not to reorder.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract'
+ else
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
+ fi
+ ;;
+ esac
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+
+ sunos4*)
+ if test sequent = "$host_vendor"; then
+ # Use $CC to link under sequent, because it throws in some extra .o
+ # files that make .init and .fini sections work.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ sysv4)
+ case $host_vendor in
+ sni)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true???
+ ;;
+ siemens)
+ ## LD is ld it makes a PLAMLIB
+ ## CC just makes a GrossModule.
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs'
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ ;;
+ motorola)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie
+ ;;
+ esac
+ runpath_var='LD_RUN_PATH'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ sysv4.3*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ runpath_var=LD_RUN_PATH
+ hardcode_runpath_var=yes
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ fi
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
+ _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ runpath_var='LD_RUN_PATH'
+
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We CANNOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text'
+ _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ uts4*)
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+
+ *)
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+
+ if test sni = "$host_vendor"; then
+ case $host in
+ sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Blargedynsym'
+ ;;
+ esac
+ fi
+ fi
+])
+AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no
+
+_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld
+
+_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl
+_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl
+_LT_DECL([], [extract_expsyms_cmds], [2],
+ [The commands to extract the exported symbol list from a shared archive])
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in
+x|xyes)
+ # Assume -lc should be added
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+
+ if test yes,yes = "$GCC,$enable_shared"; then
+ case $_LT_TAGVAR(archive_cmds, $1) in
+ *'~'*)
+ # FIXME: we may have to deal with multi-command sequences.
+ ;;
+ '$CC '*)
+ # Test whether the compiler implicitly links with -lc since on some
+ # systems, -lgcc has to come before -lc. If gcc already passes -lc
+ # to ld, don't add -lc before -lgcc.
+ AC_CACHE_CHECK([whether -lc should be explicitly linked in],
+ [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1),
+ [$RM conftest*
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ if AC_TRY_EVAL(ac_compile) 2>conftest.err; then
+ soname=conftest
+ lib=conftest
+ libobjs=conftest.$ac_objext
+ deplibs=
+ wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1)
+ pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1)
+ compiler_flags=-v
+ linker_flags=-v
+ verstring=
+ output_objdir=.
+ libname=conftest
+ lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1)
+ _LT_TAGVAR(allow_undefined_flag, $1)=
+ if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1)
+ then
+ lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ else
+ lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+ fi
+ _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag
+ else
+ cat conftest.err 1>&5
+ fi
+ $RM conftest*
+ ])
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)
+ ;;
+ esac
+ fi
+ ;;
+esac
+
+_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0],
+ [Whether or not to add -lc for building shared libraries])
+_LT_TAGDECL([allow_libtool_libs_with_static_runtimes],
+ [enable_shared_with_static_runtimes], [0],
+ [Whether or not to disallow shared libs when runtime libs are static])
+_LT_TAGDECL([], [export_dynamic_flag_spec], [1],
+ [Compiler flag to allow reflexive dlopens])
+_LT_TAGDECL([], [whole_archive_flag_spec], [1],
+ [Compiler flag to generate shared objects directly from archives])
+_LT_TAGDECL([], [compiler_needs_object], [1],
+ [Whether the compiler copes with passing no objects directly])
+_LT_TAGDECL([], [old_archive_from_new_cmds], [2],
+ [Create an old-style archive from a shared archive])
+_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2],
+ [Create a temporary old-style archive to link instead of a shared archive])
+_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive])
+_LT_TAGDECL([], [archive_expsym_cmds], [2])
+_LT_TAGDECL([], [module_cmds], [2],
+ [Commands used to build a loadable module if different from building
+ a shared archive.])
+_LT_TAGDECL([], [module_expsym_cmds], [2])
+_LT_TAGDECL([], [with_gnu_ld], [1],
+ [Whether we are building with GNU ld or not])
+_LT_TAGDECL([], [allow_undefined_flag], [1],
+ [Flag that allows shared libraries with undefined symbols to be built])
+_LT_TAGDECL([], [no_undefined_flag], [1],
+ [Flag that enforces no undefined symbols])
+_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1],
+ [Flag to hardcode $libdir into a binary during linking.
+ This must work even if $libdir does not exist])
+_LT_TAGDECL([], [hardcode_libdir_separator], [1],
+ [Whether we need a single "-rpath" flag with a separated argument])
+_LT_TAGDECL([], [hardcode_direct], [0],
+ [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes
+ DIR into the resulting binary])
+_LT_TAGDECL([], [hardcode_direct_absolute], [0],
+ [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes
+ DIR into the resulting binary and the resulting library dependency is
+ "absolute", i.e impossible to change by setting $shlibpath_var if the
+ library is relocated])
+_LT_TAGDECL([], [hardcode_minus_L], [0],
+ [Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+ into the resulting binary])
+_LT_TAGDECL([], [hardcode_shlibpath_var], [0],
+ [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+ into the resulting binary])
+_LT_TAGDECL([], [hardcode_automatic], [0],
+ [Set to "yes" if building a shared library automatically hardcodes DIR
+ into the library and all subsequent libraries and executables linked
+ against it])
+_LT_TAGDECL([], [inherit_rpath], [0],
+ [Set to yes if linker adds runtime paths of dependent libraries
+ to runtime path list])
+_LT_TAGDECL([], [link_all_deplibs], [0],
+ [Whether libtool must link a program against all its dependency libraries])
+_LT_TAGDECL([], [always_export_symbols], [0],
+ [Set to "yes" if exported symbols are required])
+_LT_TAGDECL([], [export_symbols_cmds], [2],
+ [The commands to list exported symbols])
+_LT_TAGDECL([], [exclude_expsyms], [1],
+ [Symbols that should not be listed in the preloaded symbols])
+_LT_TAGDECL([], [include_expsyms], [1],
+ [Symbols that must always be exported])
+_LT_TAGDECL([], [prelink_cmds], [2],
+ [Commands necessary for linking programs (against libraries) with templates])
+_LT_TAGDECL([], [postlink_cmds], [2],
+ [Commands necessary for finishing linking programs])
+_LT_TAGDECL([], [file_list_spec], [1],
+ [Specify filename containing input files])
+dnl FIXME: Not yet implemented
+dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1],
+dnl [Compiler flag to generate thread safe objects])
+])# _LT_LINKER_SHLIBS
+
+
+# _LT_LANG_C_CONFIG([TAG])
+# ------------------------
+# Ensure that the configuration variables for a C compiler are suitably
+# defined. These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_C_CONFIG],
+[m4_require([_LT_DECL_EGREP])dnl
+lt_save_CC=$CC
+AC_LANG_PUSH(C)
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+_LT_TAG_COMPILER
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+if test -n "$compiler"; then
+ _LT_COMPILER_NO_RTTI($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+ LT_SYS_DLOPEN_SELF
+ _LT_CMD_STRIPLIB
+
+ # Report what library types will actually be built
+ AC_MSG_CHECKING([if libtool supports shared libraries])
+ AC_MSG_RESULT([$can_build_shared])
+
+ AC_MSG_CHECKING([whether to build shared libraries])
+ test no = "$can_build_shared" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test yes = "$enable_shared" && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+
+ aix[[4-9]]*)
+ if test ia64 != "$host_cpu"; then
+ case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in
+ yes,aix,yes) ;; # shared object as lib.so file only
+ yes,svr4,*) ;; # shared object as lib.so archive member only
+ yes,*) enable_static=no ;; # shared object in lib.a archive as well
+ esac
+ fi
+ ;;
+ esac
+ AC_MSG_RESULT([$enable_shared])
+
+ AC_MSG_CHECKING([whether to build static libraries])
+ # Make sure either enable_shared or enable_static is yes.
+ test yes = "$enable_shared" || enable_static=yes
+ AC_MSG_RESULT([$enable_static])
+
+ _LT_CONFIG($1)
+fi
+AC_LANG_POP
+CC=$lt_save_CC
+])# _LT_LANG_C_CONFIG
+
+
+# _LT_LANG_CXX_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a C++ compiler are suitably
+# defined. These variables are subsequently used by _LT_CONFIG to write
+# the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_CXX_CONFIG],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+m4_require([_LT_DECL_EGREP])dnl
+m4_require([_LT_PATH_MANIFEST_TOOL])dnl
+if test -n "$CXX" && ( test no != "$CXX" &&
+ ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) ||
+ (test g++ != "$CXX"))); then
+ AC_PROG_CXXCPP
+else
+ _lt_caught_CXX_error=yes
+fi
+
+AC_LANG_PUSH(C++)
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(compiler_needs_object, $1)=no
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for C++ test sources.
+ac_ext=cpp
+
+# Object file extension for compiled C++ test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the CXX compiler isn't working. Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test yes != "$_lt_caught_CXX_error"; then
+ # Code to be used in simple compile tests
+ lt_simple_compile_test_code="int some_variable = 0;"
+
+ # Code to be used in simple link tests
+ lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }'
+
+ # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+ _LT_TAG_COMPILER
+
+ # save warnings/boilerplate of simple test code
+ _LT_COMPILER_BOILERPLATE
+ _LT_LINKER_BOILERPLATE
+
+ # Allow CC to be a program name with arguments.
+ lt_save_CC=$CC
+ lt_save_CFLAGS=$CFLAGS
+ lt_save_LD=$LD
+ lt_save_GCC=$GCC
+ GCC=$GXX
+ lt_save_with_gnu_ld=$with_gnu_ld
+ lt_save_path_LD=$lt_cv_path_LD
+ if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then
+ lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx
+ else
+ $as_unset lt_cv_prog_gnu_ld
+ fi
+ if test -n "${lt_cv_path_LDCXX+set}"; then
+ lt_cv_path_LD=$lt_cv_path_LDCXX
+ else
+ $as_unset lt_cv_path_LD
+ fi
+ test -z "${LDCXX+set}" || LD=$LDCXX
+ CC=${CXX-"c++"}
+ CFLAGS=$CXXFLAGS
+ compiler=$CC
+ _LT_TAGVAR(compiler, $1)=$CC
+ _LT_CC_BASENAME([$compiler])
+
+ if test -n "$compiler"; then
+ # We don't want -fno-exception when compiling C++ code, so set the
+ # no_builtin_flag separately
+ if test yes = "$GXX"; then
+ _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
+ else
+ _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
+ fi
+
+ if test yes = "$GXX"; then
+ # Set up default GNU C++ configuration
+
+ LT_PATH_LD
+
+ # Check if GNU C++ uses GNU ld as the underlying linker, since the
+ # archiving commands below assume that GNU ld is being used.
+ if test yes = "$with_gnu_ld"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to
+ # investigate it a little bit more. (MM)
+ wlarc='$wl'
+
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if eval "`$CC -print-prog-name=ld` --help 2>&1" |
+ $GREP 'no-whole-archive' > /dev/null; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
+ else
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=
+ fi
+ else
+ with_gnu_ld=no
+ wlarc=
+
+ # A generic and very simple default shared library creation
+ # command for GNU C++ for the case where it uses the native
+ # linker, instead of GNU ld. If possible, this setting should
+ # overridden to take advantage of the native linker features on
+ # the platform it is being used on.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+ fi
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+
+ else
+ GXX=no
+ with_gnu_ld=no
+ wlarc=
+ fi
+
+ # PORTME: fill in a description of your system's C++ link characteristics
+ AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries])
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ case $host_os in
+ aix3*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ aix[[4-9]]*)
+ if test ia64 = "$host_cpu"; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=
+ else
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # have runtime linking enabled, and use it for executables.
+ # For shared libraries, we enable/disable runtime linking
+ # depending on the kind of the shared library created -
+ # when "with_aix_soname,aix_use_runtimelinking" is:
+ # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables
+ # "aix,yes" lib.so shared, rtl:yes, for executables
+ # lib.a static archive
+ # "both,no" lib.so.V(shr.o) shared, rtl:yes
+ # lib.a(lib.so.V) shared, rtl:no, for executables
+ # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a(lib.so.V) shared, rtl:no
+ # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a static archive
+ case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
+ for ld_flag in $LDFLAGS; do
+ case $ld_flag in
+ *-brtl*)
+ aix_use_runtimelinking=yes
+ break
+ ;;
+ esac
+ done
+ if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # With aix-soname=svr4, we create the lib.so.V shared archives only,
+ # so we don't have lib.a shared libs to link our executables.
+ # We have to force runtime linking in this case.
+ aix_use_runtimelinking=yes
+ LDFLAGS="$LDFLAGS -Wl,-brtl"
+ fi
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ _LT_TAGVAR(archive_cmds, $1)=''
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(file_list_spec, $1)='$wl-f,'
+ case $with_aix_soname,$aix_use_runtimelinking in
+ aix,*) ;; # no import file
+ svr4,* | *,yes) # use import file
+ # The Import File defines what to hardcode.
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=no
+ ;;
+ esac
+
+ if test yes = "$GXX"; then
+ case $host_os in aix4.[[012]]|aix4.[[012]].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`$CC -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ _LT_TAGVAR(hardcode_direct, $1)=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=
+ fi
+ esac
+ shared_flag='-shared'
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag=$shared_flag' $wl-G'
+ fi
+ # Need to ensure runtime linking is disabled for the traditional
+ # shared library, or the linker may eventually find shared libraries
+ # /with/ Import File - we do not want to mix them.
+ shared_flag_aix='-shared'
+ shared_flag_svr4='-shared $wl-G'
+ else
+ # not using gcc
+ if test ia64 = "$host_cpu"; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag='$wl-G'
+ else
+ shared_flag='$wl-bM:SRE'
+ fi
+ shared_flag_aix='$wl-bM:SRE'
+ shared_flag_svr4='$wl-G'
+ fi
+ fi
+
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall'
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to
+ # export.
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ # The "-G" linker flag allows undefined symbols.
+ _LT_TAGVAR(no_undefined_flag, $1)='-bernotok'
+ # Determine the default libpath from the value encoded in an empty
+ # executable.
+ _LT_SYS_MODULE_PATH_AIX([$1])
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath"
+
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag
+ else
+ if test ia64 = "$host_cpu"; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib'
+ _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ _LT_SYS_MODULE_PATH_AIX([$1])
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok'
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok'
+ if test yes = "$with_gnu_ld"; then
+ # We only use this code for GNU lds that support --whole-archive.
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive'
+ else
+ # Exported symbols can be pulled into shared objects from archives
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
+ fi
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=yes
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d'
+ # -brtl affects multiple linker settings, -berok does not and is overridden later
+ compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`'
+ if test svr4 != "$with_aix_soname"; then
+ # This is similar to how AIX traditionally builds its shared
+ # libraries. Need -bnortl late, we may have -brtl in LDFLAGS.
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname'
+ fi
+ if test aix != "$with_aix_soname"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp'
+ else
+ # used by -dlpreopen to get the symbols
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir'
+ fi
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d'
+ fi
+ fi
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ chorus*)
+ case $cc_basename in
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ case $GXX,$cc_basename in
+ ,cl* | no,cl*)
+ # Native MSVC
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' '
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=yes
+ _LT_TAGVAR(file_list_spec, $1)='@'
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=.dll
+ # FIXME: Setting linknames here is a bad hack.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames='
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then
+ cp "$export_symbols" "$output_objdir/$soname.def";
+ echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp";
+ else
+ $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp;
+ fi~
+ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+ linknames='
+ # The linker will not automatically build a static lib if we build a DLL.
+ # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ # Don't use ranlib
+ _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib'
+ _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~
+ lt_tool_outputfile="@TOOL_OUTPUT@"~
+ case $lt_outputfile in
+ *.exe|*.EXE) ;;
+ *)
+ lt_outputfile=$lt_outputfile.exe
+ lt_tool_outputfile=$lt_tool_outputfile.exe
+ ;;
+ esac~
+ func_to_tool_file "$lt_outputfile"~
+ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then
+ $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+ $RM "$lt_outputfile.manifest";
+ fi'
+ ;;
+ *)
+ # g++
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
+ # as there is no search path for DLLs.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols'
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ _LT_TAGVAR(always_export_symbols, $1)=no
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file, use it as
+ # is; otherwise, prepend EXPORTS...
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+ darwin* | rhapsody*)
+ _LT_DARWIN_LINKER_FEATURES($1)
+ ;;
+
+ os2*)
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ shrext_cmds=.dll
+ _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ prefix_cmds="$SED"~
+ if test EXPORTS = "`$SED 1q $export_symbols`"; then
+ prefix_cmds="$prefix_cmds -e 1d";
+ fi~
+ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ ;;
+
+ dgux*)
+ case $cc_basename in
+ ec++*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ ghcx*)
+ # Green Hills C++ Compiler
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ freebsd2.*)
+ # C++ shared libraries reported to be fairly broken before
+ # switch to ELF
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ freebsd-elf*)
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ ;;
+
+ freebsd* | dragonfly*)
+ # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF
+ # conventions
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ ;;
+
+ haiku*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+
+ hpux9*)
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+ # but as the default
+ # location of the library.
+
+ case $cc_basename in
+ CC*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ aCC*)
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+ ;;
+ *)
+ if test yes = "$GXX"; then
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+ else
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ hpux10*|hpux11*)
+ if test no = "$with_gnu_ld"; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ ;;
+ *)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ ;;
+ esac
+ fi
+ case $host_cpu in
+ hppa*64*|ia64*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ ;;
+ *)
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
+ # but as the default
+ # location of the library.
+ ;;
+ esac
+
+ case $cc_basename in
+ CC*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ aCC*)
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ esac
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+ ;;
+ *)
+ if test yes = "$GXX"; then
+ if test no = "$with_gnu_ld"; then
+ case $host_cpu in
+ hppa*64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ ia64*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ esac
+ fi
+ else
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ interix[[3-9]]*)
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+ irix5* | irix6*)
+ case $cc_basename in
+ CC*)
+ # SGI C++
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+
+ # Archives containing C++ object files must be created using
+ # "CC -ar", where "CC" is the IRIX C++ compiler. This is
+ # necessary to make sure instantiated templates are included
+ # in the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs'
+ ;;
+ *)
+ if test yes = "$GXX"; then
+ if test no = "$with_gnu_ld"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ else
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib'
+ fi
+ fi
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ ;;
+ esac
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+ _LT_TAGVAR(inherit_rpath, $1)=yes
+ ;;
+
+ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ case $cc_basename in
+ KCC*)
+ # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+ # KCC will only create a shared library if the output file
+ # ends with ".so" (or ".sl" for HP-UX), so rename the library
+ # to its proper name (with version) after linking.
+ _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib'
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+
+ # Archives containing C++ object files must be created using
+ # "CC -Bstatic", where "CC" is the KAI C++ compiler.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs'
+ ;;
+ icpc* | ecpc* )
+ # Intel C++
+ with_gnu_ld=yes
+ # version 8.0 and above of icpc choke on multiply defined symbols
+ # if we add $predep_objects and $postdep_objects, however 7.1 and
+ # earlier do not add the objects themselves.
+ case `$CC -V 2>&1` in
+ *"Version 7."*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ *) # Version 8.0 or newer
+ tmp_idyn=
+ case $host_cpu in
+ ia64*) tmp_idyn=' -i_dynamic';;
+ esac
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ esac
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive'
+ ;;
+ pgCC* | pgcpp*)
+ # Portland Group C++ compiler
+ case `$CC -V` in
+ *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*)
+ _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+ _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+ $RANLIB $oldlib'
+ _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ *) # Version 6 and above use weak symbols
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ esac
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl--rpath $wl$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ ;;
+ cxx*)
+ # Compaq C++
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols'
+
+ runpath_var=LD_RUN_PATH
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed'
+ ;;
+ xl* | mpixl* | bgxl*)
+ # IBM XL 8.0 on PPC, with GNU ld
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ if test yes = "$supports_anon_versioning"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C++ 5.9
+ _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ _LT_TAGVAR(compiler_needs_object, $1)=yes
+
+ # Not sure whether something based on
+ # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1
+ # would be better.
+ output_verbose_link_cmd='func_echo_all'
+
+ # Archives containing C++ object files must be created using
+ # "CC -xar", where "CC" is the Sun C++ compiler. This is
+ # necessary to make sure instantiated templates are included
+ # in the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ lynxos*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ m88k*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ mvs*)
+ case $cc_basename in
+ cxx*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags'
+ wlarc=
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ fi
+ # Workaround some broken pre-1.5 toolchains
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"'
+ ;;
+
+ *nto* | *qnx*)
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ ;;
+
+ openbsd* | bitrig*)
+ if test -f /usr/libexec/ld.so; then
+ _LT_TAGVAR(hardcode_direct, $1)=yes
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=yes
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
+ fi
+ output_verbose_link_cmd=func_echo_all
+ else
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+
+ osf3* | osf4* | osf5*)
+ case $cc_basename in
+ KCC*)
+ # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+ # KCC will only create a shared library if the output file
+ # ends with ".so" (or ".sl" for HP-UX), so rename the library
+ # to its proper name (with version) after linking.
+ _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Archives containing C++ object files must be created using
+ # the KAI C++ compiler.
+ case $host in
+ osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;;
+ *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;;
+ esac
+ ;;
+ RCC*)
+ # Rational C++ 2.4.1
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ cxx*)
+ case $host in
+ osf3*)
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ ;;
+ *)
+ _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~
+ echo "-hidden">> $lib.exp~
+ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~
+ $RM $lib.exp'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
+ ;;
+ esac
+
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+ ;;
+ *)
+ if test yes,no = "$GXX,$with_gnu_ld"; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*'
+ case $host in
+ osf3*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ ;;
+ esac
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=:
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+
+ else
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ fi
+ ;;
+ esac
+ ;;
+
+ psos*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ sunos4*)
+ case $cc_basename in
+ CC*)
+ # Sun C++ 4.x
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ lcc*)
+ # Lucid
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ solaris*)
+ case $cc_basename in
+ CC* | sunCC*)
+ # Sun C++ 4.2, 5.x and Centerline C++
+ _LT_TAGVAR(archive_cmds_need_lc,$1)=yes
+ _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ case $host_os in
+ solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands '-z linker_flag'.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
+ ;;
+ esac
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+
+ output_verbose_link_cmd='func_echo_all'
+
+ # Archives containing C++ object files must be created using
+ # "CC -xar", where "CC" is the Sun C++ compiler. This is
+ # necessary to make sure instantiated templates are included
+ # in the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs'
+ ;;
+ gcx*)
+ # Green Hills C++ Compiler
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'
+
+ # The C++ compiler must be used to create the archive.
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs'
+ ;;
+ *)
+ # GNU C++ compiler with Solaris linker
+ if test yes,no = "$GXX,$with_gnu_ld"; then
+ _LT_TAGVAR(no_undefined_flag, $1)=' $wl-z ${wl}defs'
+ if $CC --version | $GREP -v '^2\.7' > /dev/null; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+ else
+ # g++ 2.7 appears to require '-G' NOT '-shared' on this
+ # platform.
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+ fi
+
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir'
+ case $host_os in
+ solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
+ *)
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract'
+ ;;
+ esac
+ fi
+ ;;
+ esac
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
+ _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ runpath_var='LD_RUN_PATH'
+
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We CANNOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text'
+ _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs'
+ _LT_TAGVAR(archive_cmds_need_lc, $1)=no
+ _LT_TAGVAR(hardcode_shlibpath_var, $1)=no
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir'
+ _LT_TAGVAR(hardcode_libdir_separator, $1)=':'
+ _LT_TAGVAR(link_all_deplibs, $1)=yes
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ case $cc_basename in
+ CC*)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~
+ '"$_LT_TAGVAR(old_archive_cmds, $1)"
+ _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~
+ '"$_LT_TAGVAR(reload_cmds, $1)"
+ ;;
+ *)
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
+ tandem*)
+ case $cc_basename in
+ NCC*)
+ # NonStop-UX NCC 3.20
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+ ;;
+
+ vxworks*)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+
+ *)
+ # FIXME: insert proper C++ library support
+ _LT_TAGVAR(ld_shlibs, $1)=no
+ ;;
+ esac
+
+ AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
+ test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no
+
+ _LT_TAGVAR(GCC, $1)=$GXX
+ _LT_TAGVAR(LD, $1)=$LD
+
+ ## CAVEAT EMPTOR:
+ ## There is no encapsulation within the following macros, do not change
+ ## the running order or otherwise move them around unless you know exactly
+ ## what you are doing...
+ _LT_SYS_HIDDEN_LIBDEPS($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+ fi # test -n "$compiler"
+
+ CC=$lt_save_CC
+ CFLAGS=$lt_save_CFLAGS
+ LDCXX=$LD
+ LD=$lt_save_LD
+ GCC=$lt_save_GCC
+ with_gnu_ld=$lt_save_with_gnu_ld
+ lt_cv_path_LDCXX=$lt_cv_path_LD
+ lt_cv_path_LD=$lt_save_path_LD
+ lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld
+ lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld
+fi # test yes != "$_lt_caught_CXX_error"
+
+AC_LANG_POP
+])# _LT_LANG_CXX_CONFIG
+
+
+# _LT_FUNC_STRIPNAME_CNF
+# ----------------------
+# func_stripname_cnf prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+#
+# This function is identical to the (non-XSI) version of func_stripname,
+# except this one can be used by m4 code that may be executed by configure,
+# rather than the libtool script.
+m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl
+AC_REQUIRE([_LT_DECL_SED])
+AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])
+func_stripname_cnf ()
+{
+ case @S|@2 in
+ .*) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%\\\\@S|@2\$%%"`;;
+ *) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%@S|@2\$%%"`;;
+ esac
+} # func_stripname_cnf
+])# _LT_FUNC_STRIPNAME_CNF
+
+
+# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME])
+# ---------------------------------
+# Figure out "hidden" library dependencies from verbose
+# compiler output when linking a shared library.
+# Parse the compiler output and extract the necessary
+# objects, libraries and library flags.
+m4_defun([_LT_SYS_HIDDEN_LIBDEPS],
+[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
+AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl
+# Dependencies to place before and after the object being linked:
+_LT_TAGVAR(predep_objects, $1)=
+_LT_TAGVAR(postdep_objects, $1)=
+_LT_TAGVAR(predeps, $1)=
+_LT_TAGVAR(postdeps, $1)=
+_LT_TAGVAR(compiler_lib_search_path, $1)=
+
+dnl we can't use the lt_simple_compile_test_code here,
+dnl because it contains code intended for an executable,
+dnl not a library. It's possible we should let each
+dnl tag define a new lt_????_link_test_code variable,
+dnl but it's only used here...
+m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF
+int a;
+void foo (void) { a = 0; }
+_LT_EOF
+], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF
+class Foo
+{
+public:
+ Foo (void) { a = 0; }
+private:
+ int a;
+};
+_LT_EOF
+], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF
+ subroutine foo
+ implicit none
+ integer*4 a
+ a=0
+ return
+ end
+_LT_EOF
+], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF
+ subroutine foo
+ implicit none
+ integer a
+ a=0
+ return
+ end
+_LT_EOF
+], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF
+public class foo {
+ private int a;
+ public void bar (void) {
+ a = 0;
+ }
+};
+_LT_EOF
+], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF
+package foo
+func foo() {
+}
+_LT_EOF
+])
+
+_lt_libdeps_save_CFLAGS=$CFLAGS
+case "$CC $CFLAGS " in #(
+*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;;
+*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;;
+*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;;
+esac
+
+dnl Parse the compiler output and extract the necessary
+dnl objects, libraries and library flags.
+if AC_TRY_EVAL(ac_compile); then
+ # Parse the compiler output and extract the necessary
+ # objects, libraries and library flags.
+
+ # Sentinel used to keep track of whether or not we are before
+ # the conftest object file.
+ pre_test_object_deps_done=no
+
+ for p in `eval "$output_verbose_link_cmd"`; do
+ case $prev$p in
+
+ -L* | -R* | -l*)
+ # Some compilers place space between "-{L,R}" and the path.
+ # Remove the space.
+ if test x-L = "$p" ||
+ test x-R = "$p"; then
+ prev=$p
+ continue
+ fi
+
+ # Expand the sysroot to ease extracting the directories later.
+ if test -z "$prev"; then
+ case $p in
+ -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;;
+ -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;;
+ -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;;
+ esac
+ fi
+ case $p in
+ =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;;
+ esac
+ if test no = "$pre_test_object_deps_done"; then
+ case $prev in
+ -L | -R)
+ # Internal compiler library paths should come after those
+ # provided the user. The postdeps already come after the
+ # user supplied libs so there is no need to process them.
+ if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then
+ _LT_TAGVAR(compiler_lib_search_path, $1)=$prev$p
+ else
+ _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} $prev$p"
+ fi
+ ;;
+ # The "-l" case would never come before the object being
+ # linked, so don't bother handling this case.
+ esac
+ else
+ if test -z "$_LT_TAGVAR(postdeps, $1)"; then
+ _LT_TAGVAR(postdeps, $1)=$prev$p
+ else
+ _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} $prev$p"
+ fi
+ fi
+ prev=
+ ;;
+
+ *.lto.$objext) ;; # Ignore GCC LTO objects
+ *.$objext)
+ # This assumes that the test object file only shows up
+ # once in the compiler output.
+ if test "$p" = "conftest.$objext"; then
+ pre_test_object_deps_done=yes
+ continue
+ fi
+
+ if test no = "$pre_test_object_deps_done"; then
+ if test -z "$_LT_TAGVAR(predep_objects, $1)"; then
+ _LT_TAGVAR(predep_objects, $1)=$p
+ else
+ _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p"
+ fi
+ else
+ if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then
+ _LT_TAGVAR(postdep_objects, $1)=$p
+ else
+ _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p"
+ fi
+ fi
+ ;;
+
+ *) ;; # Ignore the rest.
+
+ esac
+ done
+
+ # Clean up.
+ rm -f a.out a.exe
+else
+ echo "libtool.m4: error: problem compiling $1 test program"
+fi
+
+$RM -f confest.$objext
+CFLAGS=$_lt_libdeps_save_CFLAGS
+
+# PORTME: override above test on systems where it is broken
+m4_if([$1], [CXX],
+[case $host_os in
+interix[[3-9]]*)
+ # Interix 3.5 installs completely hosed .la files for C++, so rather than
+ # hack all around it, let's just trust "g++" to DTRT.
+ _LT_TAGVAR(predep_objects,$1)=
+ _LT_TAGVAR(postdep_objects,$1)=
+ _LT_TAGVAR(postdeps,$1)=
+ ;;
+esac
+])
+
+case " $_LT_TAGVAR(postdeps, $1) " in
+*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;;
+esac
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=
+if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | $SED -e 's! -L! !g' -e 's!^ !!'`
+fi
+_LT_TAGDECL([], [compiler_lib_search_dirs], [1],
+ [The directories searched by this compiler when creating a shared library])
+_LT_TAGDECL([], [predep_objects], [1],
+ [Dependencies to place before and after the objects being linked to
+ create a shared library])
+_LT_TAGDECL([], [postdep_objects], [1])
+_LT_TAGDECL([], [predeps], [1])
+_LT_TAGDECL([], [postdeps], [1])
+_LT_TAGDECL([], [compiler_lib_search_path], [1],
+ [The library search path used internally by the compiler when linking
+ a shared library])
+])# _LT_SYS_HIDDEN_LIBDEPS
+
+
+# _LT_LANG_F77_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for a Fortran 77 compiler are
+# suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_F77_CONFIG],
+[AC_LANG_PUSH(Fortran 77)
+if test -z "$F77" || test no = "$F77"; then
+ _lt_disable_F77=yes
+fi
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for f77 test sources.
+ac_ext=f
+
+# Object file extension for compiled f77 test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the F77 compiler isn't working. Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test yes != "$_lt_disable_F77"; then
+ # Code to be used in simple compile tests
+ lt_simple_compile_test_code="\
+ subroutine t
+ return
+ end
+"
+
+ # Code to be used in simple link tests
+ lt_simple_link_test_code="\
+ program t
+ end
+"
+
+ # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+ _LT_TAG_COMPILER
+
+ # save warnings/boilerplate of simple test code
+ _LT_COMPILER_BOILERPLATE
+ _LT_LINKER_BOILERPLATE
+
+ # Allow CC to be a program name with arguments.
+ lt_save_CC=$CC
+ lt_save_GCC=$GCC
+ lt_save_CFLAGS=$CFLAGS
+ CC=${F77-"f77"}
+ CFLAGS=$FFLAGS
+ compiler=$CC
+ _LT_TAGVAR(compiler, $1)=$CC
+ _LT_CC_BASENAME([$compiler])
+ GCC=$G77
+ if test -n "$compiler"; then
+ AC_MSG_CHECKING([if libtool supports shared libraries])
+ AC_MSG_RESULT([$can_build_shared])
+
+ AC_MSG_CHECKING([whether to build shared libraries])
+ test no = "$can_build_shared" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test yes = "$enable_shared" && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+ aix[[4-9]]*)
+ if test ia64 != "$host_cpu"; then
+ case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in
+ yes,aix,yes) ;; # shared object as lib.so file only
+ yes,svr4,*) ;; # shared object as lib.so archive member only
+ yes,*) enable_static=no ;; # shared object in lib.a archive as well
+ esac
+ fi
+ ;;
+ esac
+ AC_MSG_RESULT([$enable_shared])
+
+ AC_MSG_CHECKING([whether to build static libraries])
+ # Make sure either enable_shared or enable_static is yes.
+ test yes = "$enable_shared" || enable_static=yes
+ AC_MSG_RESULT([$enable_static])
+
+ _LT_TAGVAR(GCC, $1)=$G77
+ _LT_TAGVAR(LD, $1)=$LD
+
+ ## CAVEAT EMPTOR:
+ ## There is no encapsulation within the following macros, do not change
+ ## the running order or otherwise move them around unless you know exactly
+ ## what you are doing...
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+ fi # test -n "$compiler"
+
+ GCC=$lt_save_GCC
+ CC=$lt_save_CC
+ CFLAGS=$lt_save_CFLAGS
+fi # test yes != "$_lt_disable_F77"
+
+AC_LANG_POP
+])# _LT_LANG_F77_CONFIG
+
+
+# _LT_LANG_FC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for a Fortran compiler are
+# suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_FC_CONFIG],
+[AC_LANG_PUSH(Fortran)
+
+if test -z "$FC" || test no = "$FC"; then
+ _lt_disable_FC=yes
+fi
+
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+_LT_TAGVAR(allow_undefined_flag, $1)=
+_LT_TAGVAR(always_export_symbols, $1)=no
+_LT_TAGVAR(archive_expsym_cmds, $1)=
+_LT_TAGVAR(export_dynamic_flag_spec, $1)=
+_LT_TAGVAR(hardcode_direct, $1)=no
+_LT_TAGVAR(hardcode_direct_absolute, $1)=no
+_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
+_LT_TAGVAR(hardcode_libdir_separator, $1)=
+_LT_TAGVAR(hardcode_minus_L, $1)=no
+_LT_TAGVAR(hardcode_automatic, $1)=no
+_LT_TAGVAR(inherit_rpath, $1)=no
+_LT_TAGVAR(module_cmds, $1)=
+_LT_TAGVAR(module_expsym_cmds, $1)=
+_LT_TAGVAR(link_all_deplibs, $1)=unknown
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+_LT_TAGVAR(no_undefined_flag, $1)=
+_LT_TAGVAR(whole_archive_flag_spec, $1)=
+_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no
+
+# Source file extension for fc test sources.
+ac_ext=${ac_fc_srcext-f}
+
+# Object file extension for compiled fc test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# No sense in running all these tests if we already determined that
+# the FC compiler isn't working. Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test yes != "$_lt_disable_FC"; then
+ # Code to be used in simple compile tests
+ lt_simple_compile_test_code="\
+ subroutine t
+ return
+ end
+"
+
+ # Code to be used in simple link tests
+ lt_simple_link_test_code="\
+ program t
+ end
+"
+
+ # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+ _LT_TAG_COMPILER
+
+ # save warnings/boilerplate of simple test code
+ _LT_COMPILER_BOILERPLATE
+ _LT_LINKER_BOILERPLATE
+
+ # Allow CC to be a program name with arguments.
+ lt_save_CC=$CC
+ lt_save_GCC=$GCC
+ lt_save_CFLAGS=$CFLAGS
+ CC=${FC-"f95"}
+ CFLAGS=$FCFLAGS
+ compiler=$CC
+ GCC=$ac_cv_fc_compiler_gnu
+
+ _LT_TAGVAR(compiler, $1)=$CC
+ _LT_CC_BASENAME([$compiler])
+
+ if test -n "$compiler"; then
+ AC_MSG_CHECKING([if libtool supports shared libraries])
+ AC_MSG_RESULT([$can_build_shared])
+
+ AC_MSG_CHECKING([whether to build shared libraries])
+ test no = "$can_build_shared" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test yes = "$enable_shared" && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+ aix[[4-9]]*)
+ if test ia64 != "$host_cpu"; then
+ case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in
+ yes,aix,yes) ;; # shared object as lib.so file only
+ yes,svr4,*) ;; # shared object as lib.so archive member only
+ yes,*) enable_static=no ;; # shared object in lib.a archive as well
+ esac
+ fi
+ ;;
+ esac
+ AC_MSG_RESULT([$enable_shared])
+
+ AC_MSG_CHECKING([whether to build static libraries])
+ # Make sure either enable_shared or enable_static is yes.
+ test yes = "$enable_shared" || enable_static=yes
+ AC_MSG_RESULT([$enable_static])
+
+ _LT_TAGVAR(GCC, $1)=$ac_cv_fc_compiler_gnu
+ _LT_TAGVAR(LD, $1)=$LD
+
+ ## CAVEAT EMPTOR:
+ ## There is no encapsulation within the following macros, do not change
+ ## the running order or otherwise move them around unless you know exactly
+ ## what you are doing...
+ _LT_SYS_HIDDEN_LIBDEPS($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_SYS_DYNAMIC_LINKER($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+ fi # test -n "$compiler"
+
+ GCC=$lt_save_GCC
+ CC=$lt_save_CC
+ CFLAGS=$lt_save_CFLAGS
+fi # test yes != "$_lt_disable_FC"
+
+AC_LANG_POP
+])# _LT_LANG_FC_CONFIG
+
+
+# _LT_LANG_GCJ_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for the GNU Java Compiler compiler
+# are suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_GCJ_CONFIG],
+[AC_REQUIRE([LT_PROG_GCJ])dnl
+AC_LANG_SAVE
+
+# Source file extension for Java test sources.
+ac_ext=java
+
+# Object file extension for compiled Java test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="class foo {}"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC=$CC
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=yes
+CC=${GCJ-"gcj"}
+CFLAGS=$GCJFLAGS
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_TAGVAR(LD, $1)=$LD
+_LT_CC_BASENAME([$compiler])
+
+# GCJ did not exist at the time GCC didn't implicitly link libc in.
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+
+if test -n "$compiler"; then
+ _LT_COMPILER_NO_RTTI($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+fi
+
+AC_LANG_RESTORE
+
+GCC=$lt_save_GCC
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_GCJ_CONFIG
+
+
+# _LT_LANG_GO_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for the GNU Go compiler
+# are suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_GO_CONFIG],
+[AC_REQUIRE([LT_PROG_GO])dnl
+AC_LANG_SAVE
+
+# Source file extension for Go test sources.
+ac_ext=go
+
+# Object file extension for compiled Go test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="package main; func main() { }"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='package main; func main() { }'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC=$CC
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=yes
+CC=${GOC-"gccgo"}
+CFLAGS=$GOFLAGS
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_TAGVAR(LD, $1)=$LD
+_LT_CC_BASENAME([$compiler])
+
+# Go did not exist at the time GCC didn't implicitly link libc in.
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+
+if test -n "$compiler"; then
+ _LT_COMPILER_NO_RTTI($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+fi
+
+AC_LANG_RESTORE
+
+GCC=$lt_save_GCC
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_GO_CONFIG
+
+
+# _LT_LANG_RC_CONFIG([TAG])
+# -------------------------
+# Ensure that the configuration variables for the Windows resource compiler
+# are suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_RC_CONFIG],
+[AC_REQUIRE([LT_PROG_RC])dnl
+AC_LANG_SAVE
+
+# Source file extension for RC test sources.
+ac_ext=rc
+
+# Object file extension for compiled RC test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }'
+
+# Code to be used in simple link tests
+lt_simple_link_test_code=$lt_simple_compile_test_code
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC=$CC
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=
+CC=${RC-"windres"}
+CFLAGS=
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_CC_BASENAME([$compiler])
+_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes
+
+if test -n "$compiler"; then
+ :
+ _LT_CONFIG($1)
+fi
+
+GCC=$lt_save_GCC
+AC_LANG_RESTORE
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_RC_CONFIG
+
+
+# LT_PROG_GCJ
+# -----------
+AC_DEFUN([LT_PROG_GCJ],
+[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ],
+ [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ],
+ [AC_CHECK_TOOL(GCJ, gcj,)
+ test set = "${GCJFLAGS+set}" || GCJFLAGS="-g -O2"
+ AC_SUBST(GCJFLAGS)])])[]dnl
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_GCJ], [])
+
+
+# LT_PROG_GO
+# ----------
+AC_DEFUN([LT_PROG_GO],
+[AC_CHECK_TOOL(GOC, gccgo,)
+])
+
+
+# LT_PROG_RC
+# ----------
+AC_DEFUN([LT_PROG_RC],
+[AC_CHECK_TOOL(RC, windres,)
+])
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_RC], [])
+
+
+# _LT_DECL_EGREP
+# --------------
+# If we don't have a new enough Autoconf to choose the best grep
+# available, choose the one first in the user's PATH.
+m4_defun([_LT_DECL_EGREP],
+[AC_REQUIRE([AC_PROG_EGREP])dnl
+AC_REQUIRE([AC_PROG_FGREP])dnl
+test -z "$GREP" && GREP=grep
+_LT_DECL([], [GREP], [1], [A grep program that handles long lines])
+_LT_DECL([], [EGREP], [1], [An ERE matcher])
+_LT_DECL([], [FGREP], [1], [A literal string matcher])
+dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too
+AC_SUBST([GREP])
+])
+
+
+# _LT_DECL_OBJDUMP
+# --------------
+# If we don't have a new enough Autoconf to choose the best objdump
+# available, choose the one first in the user's PATH.
+m4_defun([_LT_DECL_OBJDUMP],
+[AC_CHECK_TOOL(OBJDUMP, objdump, false)
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper])
+AC_SUBST([OBJDUMP])
+])
+
+# _LT_DECL_DLLTOOL
+# ----------------
+# Ensure DLLTOOL variable is set.
+m4_defun([_LT_DECL_DLLTOOL],
+[AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+_LT_DECL([], [DLLTOOL], [1], [DLL creation program])
+AC_SUBST([DLLTOOL])
+])
+
+# _LT_DECL_SED
+# ------------
+# Check for a fully-functional sed program, that truncates
+# as few characters as possible. Prefer GNU sed if found.
+m4_defun([_LT_DECL_SED],
+[AC_PROG_SED
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+_LT_DECL([], [SED], [1], [A sed program that does not truncate output])
+_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"],
+ [Sed that helps us avoid accidentally triggering echo(1) options like -n])
+])# _LT_DECL_SED
+
+m4_ifndef([AC_PROG_SED], [
+# NOTE: This macro has been submitted for inclusion into #
+# GNU Autoconf as AC_PROG_SED. When it is available in #
+# a released version of Autoconf we should remove this #
+# macro and use it instead. #
+
+m4_defun([AC_PROG_SED],
+[AC_MSG_CHECKING([for a sed that does not truncate output])
+AC_CACHE_VAL(lt_cv_path_SED,
+[# Loop through the user's path and test for sed and gsed.
+# Then use that list of sed's as ones to test for truncation.
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for lt_ac_prog in sed gsed; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then
+ lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext"
+ fi
+ done
+ done
+done
+IFS=$as_save_IFS
+lt_ac_max=0
+lt_ac_count=0
+# Add /usr/xpg4/bin/sed as it is typically found on Solaris
+# along with /bin/sed that truncates output.
+for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do
+ test ! -f "$lt_ac_sed" && continue
+ cat /dev/null > conftest.in
+ lt_ac_count=0
+ echo $ECHO_N "0123456789$ECHO_C" >conftest.in
+ # Check for GNU sed and select it if it is found.
+ if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then
+ lt_cv_path_SED=$lt_ac_sed
+ break
+ fi
+ while true; do
+ cat conftest.in conftest.in >conftest.tmp
+ mv conftest.tmp conftest.in
+ cp conftest.in conftest.nl
+ echo >>conftest.nl
+ $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break
+ cmp -s conftest.out conftest.nl || break
+ # 10000 chars as input seems more than enough
+ test 10 -lt "$lt_ac_count" && break
+ lt_ac_count=`expr $lt_ac_count + 1`
+ if test "$lt_ac_count" -gt "$lt_ac_max"; then
+ lt_ac_max=$lt_ac_count
+ lt_cv_path_SED=$lt_ac_sed
+ fi
+ done
+done
+])
+SED=$lt_cv_path_SED
+AC_SUBST([SED])
+AC_MSG_RESULT([$SED])
+])#AC_PROG_SED
+])#m4_ifndef
+
+# Old name:
+AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED])
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([LT_AC_PROG_SED], [])
+
+
+# _LT_CHECK_SHELL_FEATURES
+# ------------------------
+# Find out whether the shell is Bourne or XSI compatible,
+# or has some other useful features.
+m4_defun([_LT_CHECK_SHELL_FEATURES],
+[if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ lt_unset=unset
+else
+ lt_unset=false
+fi
+_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+ # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+ lt_SP2NL='tr \040 \012'
+ lt_NL2SP='tr \015\012 \040\040'
+ ;;
+ *) # EBCDIC based system
+ lt_SP2NL='tr \100 \n'
+ lt_NL2SP='tr \r\n \100\100'
+ ;;
+esac
+_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl
+_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl
+])# _LT_CHECK_SHELL_FEATURES
+
+
+# _LT_PATH_CONVERSION_FUNCTIONS
+# -----------------------------
+# Determine what file name conversion functions should be used by
+# func_to_host_file (and, implicitly, by func_to_host_path). These are needed
+# for certain cross-compile configurations and native mingw.
+m4_defun([_LT_PATH_CONVERSION_FUNCTIONS],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+AC_REQUIRE([AC_CANONICAL_BUILD])dnl
+AC_MSG_CHECKING([how to convert $build file names to $host format])
+AC_CACHE_VAL(lt_cv_to_host_file_cmd,
+[case $host in
+ *-*-mingw* )
+ case $build in
+ *-*-mingw* ) # actually msys
+ lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32
+ ;;
+ *-*-cygwin* )
+ lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32
+ ;;
+ * ) # otherwise, assume *nix
+ lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32
+ ;;
+ esac
+ ;;
+ *-*-cygwin* )
+ case $build in
+ *-*-mingw* ) # actually msys
+ lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin
+ ;;
+ *-*-cygwin* )
+ lt_cv_to_host_file_cmd=func_convert_file_noop
+ ;;
+ * ) # otherwise, assume *nix
+ lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin
+ ;;
+ esac
+ ;;
+ * ) # unhandled hosts (and "normal" native builds)
+ lt_cv_to_host_file_cmd=func_convert_file_noop
+ ;;
+esac
+])
+to_host_file_cmd=$lt_cv_to_host_file_cmd
+AC_MSG_RESULT([$lt_cv_to_host_file_cmd])
+_LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd],
+ [0], [convert $build file names to $host format])dnl
+
+AC_MSG_CHECKING([how to convert $build file names to toolchain format])
+AC_CACHE_VAL(lt_cv_to_tool_file_cmd,
+[#assume ordinary cross tools, or native build.
+lt_cv_to_tool_file_cmd=func_convert_file_noop
+case $host in
+ *-*-mingw* )
+ case $build in
+ *-*-mingw* ) # actually msys
+ lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32
+ ;;
+ esac
+ ;;
+esac
+])
+to_tool_file_cmd=$lt_cv_to_tool_file_cmd
+AC_MSG_RESULT([$lt_cv_to_tool_file_cmd])
+_LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd],
+ [0], [convert $build files to toolchain format])dnl
+])# _LT_PATH_CONVERSION_FUNCTIONS
+
+# Helper functions for option handling. -*- Autoconf -*-
+#
+# Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software
+# Foundation, Inc.
+# Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 8 ltoptions.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
+
+
+# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
+# ------------------------------------------
+m4_define([_LT_MANGLE_OPTION],
+[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
+
+
+# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
+# ---------------------------------------
+# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
+# matching handler defined, dispatch to it. Other OPTION-NAMEs are
+# saved as a flag.
+m4_define([_LT_SET_OPTION],
+[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
+m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
+ _LT_MANGLE_DEFUN([$1], [$2]),
+ [m4_warning([Unknown $1 option '$2'])])[]dnl
+])
+
+
+# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
+# ------------------------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+m4_define([_LT_IF_OPTION],
+[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
+
+
+# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
+# -------------------------------------------------------
+# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
+# are set.
+m4_define([_LT_UNLESS_OPTIONS],
+[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+ [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
+ [m4_define([$0_found])])])[]dnl
+m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
+])[]dnl
+])
+
+
+# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
+# ----------------------------------------
+# OPTION-LIST is a space-separated list of Libtool options associated
+# with MACRO-NAME. If any OPTION has a matching handler declared with
+# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
+# the unknown option and exit.
+m4_defun([_LT_SET_OPTIONS],
+[# Set options
+m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
+ [_LT_SET_OPTION([$1], _LT_Option)])
+
+m4_if([$1],[LT_INIT],[
+ dnl
+ dnl Simply set some default values (i.e off) if boolean options were not
+ dnl specified:
+ _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
+ ])
+ _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
+ ])
+ dnl
+ dnl If no reference was made to various pairs of opposing options, then
+ dnl we run the default mode handler for the pair. For example, if neither
+ dnl 'shared' nor 'disable-shared' was passed, we enable building of shared
+ dnl archives by default:
+ _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
+ _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
+ _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
+ _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
+ [_LT_ENABLE_FAST_INSTALL])
+ _LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4],
+ [_LT_WITH_AIX_SONAME([aix])])
+ ])
+])# _LT_SET_OPTIONS
+
+
+
+# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
+# -----------------------------------------
+m4_define([_LT_MANGLE_DEFUN],
+[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
+
+
+# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
+# -----------------------------------------------
+m4_define([LT_OPTION_DEFINE],
+[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
+])# LT_OPTION_DEFINE
+
+
+# dlopen
+# ------
+LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
+])
+
+AU_DEFUN([AC_LIBTOOL_DLOPEN],
+[_LT_SET_OPTION([LT_INIT], [dlopen])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the 'dlopen' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
+
+
+# win32-dll
+# ---------
+# Declare package support for building win32 dll's.
+LT_OPTION_DEFINE([LT_INIT], [win32-dll],
+[enable_win32_dll=yes
+
+case $host in
+*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*)
+ AC_CHECK_TOOL(AS, as, false)
+ AC_CHECK_TOOL(DLLTOOL, dlltool, false)
+ AC_CHECK_TOOL(OBJDUMP, objdump, false)
+ ;;
+esac
+
+test -z "$AS" && AS=as
+_LT_DECL([], [AS], [1], [Assembler program])dnl
+
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+_LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+_LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl
+])# win32-dll
+
+AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
+[AC_REQUIRE([AC_CANONICAL_HOST])dnl
+_LT_SET_OPTION([LT_INIT], [win32-dll])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the 'win32-dll' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
+
+
+# _LT_ENABLE_SHARED([DEFAULT])
+# ----------------------------
+# implement the --enable-shared flag, and supports the 'shared' and
+# 'disable-shared' LT_INIT options.
+# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
+m4_define([_LT_ENABLE_SHARED],
+[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([shared],
+ [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
+ [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
+ [p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_shared=yes ;;
+ no) enable_shared=no ;;
+ *)
+ enable_shared=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for pkg in $enableval; do
+ IFS=$lt_save_ifs
+ if test "X$pkg" = "X$p"; then
+ enable_shared=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac],
+ [enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
+
+ _LT_DECL([build_libtool_libs], [enable_shared], [0],
+ [Whether or not to build shared libraries])
+])# _LT_ENABLE_SHARED
+
+LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
+])
+
+AC_DEFUN([AC_DISABLE_SHARED],
+[_LT_SET_OPTION([LT_INIT], [disable-shared])
+])
+
+AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
+AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_SHARED], [])
+dnl AC_DEFUN([AM_DISABLE_SHARED], [])
+
+
+
+# _LT_ENABLE_STATIC([DEFAULT])
+# ----------------------------
+# implement the --enable-static flag, and support the 'static' and
+# 'disable-static' LT_INIT options.
+# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
+m4_define([_LT_ENABLE_STATIC],
+[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([static],
+ [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
+ [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
+ [p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_static=yes ;;
+ no) enable_static=no ;;
+ *)
+ enable_static=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for pkg in $enableval; do
+ IFS=$lt_save_ifs
+ if test "X$pkg" = "X$p"; then
+ enable_static=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac],
+ [enable_static=]_LT_ENABLE_STATIC_DEFAULT)
+
+ _LT_DECL([build_old_libs], [enable_static], [0],
+ [Whether or not to build static libraries])
+])# _LT_ENABLE_STATIC
+
+LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
+
+# Old names:
+AC_DEFUN([AC_ENABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
+])
+
+AC_DEFUN([AC_DISABLE_STATIC],
+[_LT_SET_OPTION([LT_INIT], [disable-static])
+])
+
+AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
+AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AM_ENABLE_STATIC], [])
+dnl AC_DEFUN([AM_DISABLE_STATIC], [])
+
+
+
+# _LT_ENABLE_FAST_INSTALL([DEFAULT])
+# ----------------------------------
+# implement the --enable-fast-install flag, and support the 'fast-install'
+# and 'disable-fast-install' LT_INIT options.
+# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
+m4_define([_LT_ENABLE_FAST_INSTALL],
+[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
+AC_ARG_ENABLE([fast-install],
+ [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
+ [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
+ [p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_fast_install=yes ;;
+ no) enable_fast_install=no ;;
+ *)
+ enable_fast_install=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for pkg in $enableval; do
+ IFS=$lt_save_ifs
+ if test "X$pkg" = "X$p"; then
+ enable_fast_install=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac],
+ [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
+
+_LT_DECL([fast_install], [enable_fast_install], [0],
+ [Whether or not to optimize for fast installation])dnl
+])# _LT_ENABLE_FAST_INSTALL
+
+LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
+LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
+
+# Old names:
+AU_DEFUN([AC_ENABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the 'fast-install' option into LT_INIT's first parameter.])
+])
+
+AU_DEFUN([AC_DISABLE_FAST_INSTALL],
+[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you put
+the 'disable-fast-install' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
+dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
+
+
+# _LT_WITH_AIX_SONAME([DEFAULT])
+# ----------------------------------
+# implement the --with-aix-soname flag, and support the `aix-soname=aix'
+# and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT
+# is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'.
+m4_define([_LT_WITH_AIX_SONAME],
+[m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl
+shared_archive_member_spec=
+case $host,$enable_shared in
+power*-*-aix[[5-9]]*,yes)
+ AC_MSG_CHECKING([which variant of shared library versioning to provide])
+ AC_ARG_WITH([aix-soname],
+ [AS_HELP_STRING([--with-aix-soname=aix|svr4|both],
+ [shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])],
+ [case $withval in
+ aix|svr4|both)
+ ;;
+ *)
+ AC_MSG_ERROR([Unknown argument to --with-aix-soname])
+ ;;
+ esac
+ lt_cv_with_aix_soname=$with_aix_soname],
+ [AC_CACHE_VAL([lt_cv_with_aix_soname],
+ [lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT)
+ with_aix_soname=$lt_cv_with_aix_soname])
+ AC_MSG_RESULT([$with_aix_soname])
+ if test aix != "$with_aix_soname"; then
+ # For the AIX way of multilib, we name the shared archive member
+ # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o',
+ # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File.
+ # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag,
+ # the AIX toolchain works better with OBJECT_MODE set (default 32).
+ if test 64 = "${OBJECT_MODE-32}"; then
+ shared_archive_member_spec=shr_64
+ else
+ shared_archive_member_spec=shr
+ fi
+ fi
+ ;;
+*)
+ with_aix_soname=aix
+ ;;
+esac
+
+_LT_DECL([], [shared_archive_member_spec], [0],
+ [Shared archive member basename, for filename based shared library versioning on AIX])dnl
+])# _LT_WITH_AIX_SONAME
+
+LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])])
+LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])])
+LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])])
+
+
+# _LT_WITH_PIC([MODE])
+# --------------------
+# implement the --with-pic flag, and support the 'pic-only' and 'no-pic'
+# LT_INIT options.
+# MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'.
+m4_define([_LT_WITH_PIC],
+[AC_ARG_WITH([pic],
+ [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@],
+ [try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
+ [lt_p=${PACKAGE-default}
+ case $withval in
+ yes|no) pic_mode=$withval ;;
+ *)
+ pic_mode=default
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for lt_pkg in $withval; do
+ IFS=$lt_save_ifs
+ if test "X$lt_pkg" = "X$lt_p"; then
+ pic_mode=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac],
+ [pic_mode=m4_default([$1], [default])])
+
+_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
+])# _LT_WITH_PIC
+
+LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
+LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
+
+# Old name:
+AU_DEFUN([AC_LIBTOOL_PICMODE],
+[_LT_SET_OPTION([LT_INIT], [pic-only])
+AC_DIAGNOSE([obsolete],
+[$0: Remove this warning and the call to _LT_SET_OPTION when you
+put the 'pic-only' option into LT_INIT's first parameter.])
+])
+
+dnl aclocal-1.4 backwards compatibility:
+dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
+
+
+m4_define([_LTDL_MODE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
+ [m4_define([_LTDL_MODE], [nonrecursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [recursive],
+ [m4_define([_LTDL_MODE], [recursive])])
+LT_OPTION_DEFINE([LTDL_INIT], [subproject],
+ [m4_define([_LTDL_MODE], [subproject])])
+
+m4_define([_LTDL_TYPE], [])
+LT_OPTION_DEFINE([LTDL_INIT], [installable],
+ [m4_define([_LTDL_TYPE], [installable])])
+LT_OPTION_DEFINE([LTDL_INIT], [convenience],
+ [m4_define([_LTDL_TYPE], [convenience])])
+
+# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
+#
+# Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software
+# Foundation, Inc.
+# Written by Gary V. Vaughan, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 6 ltsugar.m4
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
+
+
+# lt_join(SEP, ARG1, [ARG2...])
+# -----------------------------
+# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
+# associated separator.
+# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
+# versions in m4sugar had bugs.
+m4_define([lt_join],
+[m4_if([$#], [1], [],
+ [$#], [2], [[$2]],
+ [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
+m4_define([_lt_join],
+[m4_if([$#$2], [2], [],
+ [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
+
+
+# lt_car(LIST)
+# lt_cdr(LIST)
+# ------------
+# Manipulate m4 lists.
+# These macros are necessary as long as will still need to support
+# Autoconf-2.59, which quotes differently.
+m4_define([lt_car], [[$1]])
+m4_define([lt_cdr],
+[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
+ [$#], 1, [],
+ [m4_dquote(m4_shift($@))])])
+m4_define([lt_unquote], $1)
+
+
+# lt_append(MACRO-NAME, STRING, [SEPARATOR])
+# ------------------------------------------
+# Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'.
+# Note that neither SEPARATOR nor STRING are expanded; they are appended
+# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
+# No SEPARATOR is output if MACRO-NAME was previously undefined (different
+# than defined and empty).
+#
+# This macro is needed until we can rely on Autoconf 2.62, since earlier
+# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
+m4_define([lt_append],
+[m4_define([$1],
+ m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
+
+
+
+# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
+# ----------------------------------------------------------
+# Produce a SEP delimited list of all paired combinations of elements of
+# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list
+# has the form PREFIXmINFIXSUFFIXn.
+# Needed until we can rely on m4_combine added in Autoconf 2.62.
+m4_define([lt_combine],
+[m4_if(m4_eval([$# > 3]), [1],
+ [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
+[[m4_foreach([_Lt_prefix], [$2],
+ [m4_foreach([_Lt_suffix],
+ ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
+ [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
+
+
+# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
+# -----------------------------------------------------------------------
+# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
+# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
+m4_define([lt_if_append_uniq],
+[m4_ifdef([$1],
+ [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
+ [lt_append([$1], [$2], [$3])$4],
+ [$5])],
+ [lt_append([$1], [$2], [$3])$4])])
+
+
+# lt_dict_add(DICT, KEY, VALUE)
+# -----------------------------
+m4_define([lt_dict_add],
+[m4_define([$1($2)], [$3])])
+
+
+# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
+# --------------------------------------------
+m4_define([lt_dict_add_subkey],
+[m4_define([$1($2:$3)], [$4])])
+
+
+# lt_dict_fetch(DICT, KEY, [SUBKEY])
+# ----------------------------------
+m4_define([lt_dict_fetch],
+[m4_ifval([$3],
+ m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
+ m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
+
+
+# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
+# -----------------------------------------------------------------
+m4_define([lt_if_dict_fetch],
+[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
+ [$5],
+ [$6])])
+
+
+# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
+# --------------------------------------------------------------
+m4_define([lt_dict_filter],
+[m4_if([$5], [], [],
+ [lt_join(m4_quote(m4_default([$4], [[, ]])),
+ lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
+ [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
+])
+
+# ltversion.m4 -- version numbers -*- Autoconf -*-
+#
+# Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc.
+# Written by Scott James Remnant, 2004
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# @configure_input@
+
+# serial 4179 ltversion.m4
+# This file is part of GNU Libtool
+
+m4_define([LT_PACKAGE_VERSION], [2.4.6])
+m4_define([LT_PACKAGE_REVISION], [2.4.6])
+
+AC_DEFUN([LTVERSION_VERSION],
+[macro_version='2.4.6'
+macro_revision='2.4.6'
+_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
+_LT_DECL(, macro_revision, 0)
+])
+
+# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*-
+#
+# Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software
+# Foundation, Inc.
+# Written by Scott James Remnant, 2004.
+#
+# This file is free software; the Free Software Foundation gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+
+# serial 5 lt~obsolete.m4
+
+# These exist entirely to fool aclocal when bootstrapping libtool.
+#
+# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN),
+# which have later been changed to m4_define as they aren't part of the
+# exported API, or moved to Autoconf or Automake where they belong.
+#
+# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN
+# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
+# using a macro with the same name in our local m4/libtool.m4 it'll
+# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
+# and doesn't know about Autoconf macros at all.)
+#
+# So we provide this file, which has a silly filename so it's always
+# included after everything else. This provides aclocal with the
+# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
+# because those macros already exist, or will be overwritten later.
+# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
+#
+# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
+# Yes, that means every name once taken will need to remain here until
+# we give up compatibility with versions before 1.7, at which point
+# we need to keep only those names which we still refer to.
+
+# This is to help aclocal find these macros, as it can't see m4_define.
+AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
+
+m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
+m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])])
+m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
+m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])])
+m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
+m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])])
+m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])])
+m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
+m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])])
+m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])])
+m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])])
+m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
+m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
+m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
+m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
+m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
+m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
+m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
+m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])])
+m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])])
+m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
+m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
+m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
+m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
+m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
+m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
+m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])])
+m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])])
+m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])])
+m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
+m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
+m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])])
+m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])])
+m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
+m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
+m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])])
+m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
+m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])])
+m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])])
+m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])])
+m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
+m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
+m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
+m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
+m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
+m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
+m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
+m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])])
+m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
+m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])])
+m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])])
+m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])])
+m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])])
+m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])])
+m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])])
+m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])])
+
+# Copyright (C) 2002-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_AUTOMAKE_VERSION(VERSION)
+# ----------------------------
+# Automake X.Y traces this macro to ensure aclocal.m4 has been
+# generated from the m4 files accompanying Automake X.Y.
+# (This private macro should not be called outside this file.)
+AC_DEFUN([AM_AUTOMAKE_VERSION],
+[am__api_version='1.15'
+dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
+dnl require some minimum version. Point them to the right macro.
+m4_if([$1], [1.15.1], [],
+ [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
+])
+
+# _AM_AUTOCONF_VERSION(VERSION)
+# -----------------------------
+# aclocal traces this macro to find the Autoconf version.
+# This is a private macro too. Using m4_define simplifies
+# the logic in aclocal, which can simply ignore this definition.
+m4_define([_AM_AUTOCONF_VERSION], [])
+
+# AM_SET_CURRENT_AUTOMAKE_VERSION
+# -------------------------------
+# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
+# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
+AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
+[AM_AUTOMAKE_VERSION([1.15.1])dnl
+m4_ifndef([AC_AUTOCONF_VERSION],
+ [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
+_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
+
+# AM_AUX_DIR_EXPAND -*- Autoconf -*-
+
+# Copyright (C) 2001-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
+# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to
+# '$srcdir', '$srcdir/..', or '$srcdir/../..'.
+#
+# Of course, Automake must honor this variable whenever it calls a
+# tool from the auxiliary directory. The problem is that $srcdir (and
+# therefore $ac_aux_dir as well) can be either absolute or relative,
+# depending on how configure is run. This is pretty annoying, since
+# it makes $ac_aux_dir quite unusable in subdirectories: in the top
+# source directory, any form will work fine, but in subdirectories a
+# relative path needs to be adjusted first.
+#
+# $ac_aux_dir/missing
+# fails when called from a subdirectory if $ac_aux_dir is relative
+# $top_srcdir/$ac_aux_dir/missing
+# fails if $ac_aux_dir is absolute,
+# fails when called from a subdirectory in a VPATH build with
+# a relative $ac_aux_dir
+#
+# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
+# are both prefixed by $srcdir. In an in-source build this is usually
+# harmless because $srcdir is '.', but things will broke when you
+# start a VPATH build or use an absolute $srcdir.
+#
+# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
+# iff we strip the leading $srcdir from $ac_aux_dir. That would be:
+# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
+# and then we would define $MISSING as
+# MISSING="\${SHELL} $am_aux_dir/missing"
+# This will work as long as MISSING is not called from configure, because
+# unfortunately $(top_srcdir) has no meaning in configure.
+# However there are other variables, like CC, which are often used in
+# configure, and could therefore not use this "fixed" $ac_aux_dir.
+#
+# Another solution, used here, is to always expand $ac_aux_dir to an
+# absolute PATH. The drawback is that using absolute paths prevent a
+# configured tree to be moved without reconfiguration.
+
+AC_DEFUN([AM_AUX_DIR_EXPAND],
+[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
+# Expand $ac_aux_dir to an absolute path.
+am_aux_dir=`cd "$ac_aux_dir" && pwd`
+])
+
+# AM_CONDITIONAL -*- Autoconf -*-
+
+# Copyright (C) 1997-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_CONDITIONAL(NAME, SHELL-CONDITION)
+# -------------------------------------
+# Define a conditional.
+AC_DEFUN([AM_CONDITIONAL],
+[AC_PREREQ([2.52])dnl
+ m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
+ [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
+AC_SUBST([$1_TRUE])dnl
+AC_SUBST([$1_FALSE])dnl
+_AM_SUBST_NOTMAKE([$1_TRUE])dnl
+_AM_SUBST_NOTMAKE([$1_FALSE])dnl
+m4_define([_AM_COND_VALUE_$1], [$2])dnl
+if $2; then
+ $1_TRUE=
+ $1_FALSE='#'
+else
+ $1_TRUE='#'
+ $1_FALSE=
+fi
+AC_CONFIG_COMMANDS_PRE(
+[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
+ AC_MSG_ERROR([[conditional "$1" was never defined.
+Usually this means the macro was only invoked conditionally.]])
+fi])])
+
+# Copyright (C) 1999-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+
+# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be
+# written in clear, in which case automake, when reading aclocal.m4,
+# will think it sees a *use*, and therefore will trigger all it's
+# C support machinery. Also note that it means that autoscan, seeing
+# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
+
+
+# _AM_DEPENDENCIES(NAME)
+# ----------------------
+# See how the compiler implements dependency checking.
+# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC".
+# We try a few techniques and use that to set a single cache variable.
+#
+# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
+# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
+# dependency, and given that the user is not expected to run this macro,
+# just rely on AC_PROG_CC.
+AC_DEFUN([_AM_DEPENDENCIES],
+[AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
+AC_REQUIRE([AM_MAKE_INCLUDE])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+
+m4_if([$1], [CC], [depcc="$CC" am_compiler_list=],
+ [$1], [CXX], [depcc="$CXX" am_compiler_list=],
+ [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
+ [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'],
+ [$1], [UPC], [depcc="$UPC" am_compiler_list=],
+ [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
+ [depcc="$$1" am_compiler_list=])
+
+AC_CACHE_CHECK([dependency style of $depcc],
+ [am_cv_$1_dependencies_compiler_type],
+[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named 'D' -- because '-MD' means "put the output
+ # in D".
+ rm -rf conftest.dir
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_$1_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
+ fi
+ am__universal=false
+ m4_case([$1], [CC],
+ [case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac],
+ [CXX],
+ [case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac])
+
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
+ # Solaris 10 /bin/sh.
+ echo '/* dummy */' > sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ # We check with '-c' and '-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle '-M -o', and we need to detect this. Also, some Intel
+ # versions had trouble with output in subdirs.
+ am__obj=sub/conftest.${OBJEXT-o}
+ am__minus_obj="-o $am__obj"
+ case $depmode in
+ gcc)
+ # This depmode causes a compiler race in universal mode.
+ test "$am__universal" = false || continue
+ ;;
+ nosideeffect)
+ # After this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested.
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ msvc7 | msvc7msys | msvisualcpp | msvcmsys)
+ # This compiler won't grok '-c -o', but also, the minuso test has
+ # not run yet. These depmodes are late enough in the game, and
+ # so weak that their functioning should not be impacted.
+ am__obj=conftest.${OBJEXT-o}
+ am__minus_obj=
+ ;;
+ none) break ;;
+ esac
+ if depmode=$depmode \
+ source=sub/conftest.c object=$am__obj \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_$1_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_$1_dependencies_compiler_type=none
+fi
+])
+AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
+AM_CONDITIONAL([am__fastdep$1], [
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
+])
+
+
+# AM_SET_DEPDIR
+# -------------
+# Choose a directory name for dependency files.
+# This macro is AC_REQUIREd in _AM_DEPENDENCIES.
+AC_DEFUN([AM_SET_DEPDIR],
+[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
+])
+
+
+# AM_DEP_TRACK
+# ------------
+AC_DEFUN([AM_DEP_TRACK],
+[AC_ARG_ENABLE([dependency-tracking], [dnl
+AS_HELP_STRING(
+ [--enable-dependency-tracking],
+ [do not reject slow dependency extractors])
+AS_HELP_STRING(
+ [--disable-dependency-tracking],
+ [speeds up one-time build])])
+if test "x$enable_dependency_tracking" != xno; then
+ am_depcomp="$ac_aux_dir/depcomp"
+ AMDEPBACKSLASH='\'
+ am__nodep='_no'
+fi
+AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+AC_SUBST([AMDEPBACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
+AC_SUBST([am__nodep])dnl
+_AM_SUBST_NOTMAKE([am__nodep])dnl
+])
+
+# Generate code to set up dependency tracking. -*- Autoconf -*-
+
+# Copyright (C) 1999-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+
+# _AM_OUTPUT_DEPENDENCY_COMMANDS
+# ------------------------------
+AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
+[{
+ # Older Autoconf quotes --file arguments for eval, but not when files
+ # are listed without --file. Let's play safe and only enable the eval
+ # if we detect the quoting.
+ case $CONFIG_FILES in
+ *\'*) eval set x "$CONFIG_FILES" ;;
+ *) set x $CONFIG_FILES ;;
+ esac
+ shift
+ for mf
+ do
+ # Strip MF so we end up with the name of the file.
+ mf=`echo "$mf" | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile or not.
+ # We used to match only the files named 'Makefile.in', but
+ # some people rename them; so instead we look at the file content.
+ # Grep'ing the first line is not enough: some people post-process
+ # each Makefile.in and add a new line on top of each file to say so.
+ # Grep'ing the whole file is not good either: AIX grep has a line
+ # limit of 2048, but all sed's we know have understand at least 4000.
+ if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+ dirpart=`AS_DIRNAME("$mf")`
+ else
+ continue
+ fi
+ # Extract the definition of DEPDIR, am__include, and am__quote
+ # from the Makefile without running 'make'.
+ DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+ test -z "$DEPDIR" && continue
+ am__include=`sed -n 's/^am__include = //p' < "$mf"`
+ test -z "$am__include" && continue
+ am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+ # Find all dependency output files, they are included files with
+ # $(DEPDIR) in their names. We invoke sed twice because it is the
+ # simplest approach to changing $(DEPDIR) to its actual value in the
+ # expansion.
+ for file in `sed -n "
+ s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
+ # Make sure the directory exists.
+ test -f "$dirpart/$file" && continue
+ fdir=`AS_DIRNAME(["$file"])`
+ AS_MKDIR_P([$dirpart/$fdir])
+ # echo "creating $dirpart/$file"
+ echo '# dummy' > "$dirpart/$file"
+ done
+ done
+}
+])# _AM_OUTPUT_DEPENDENCY_COMMANDS
+
+
+# AM_OUTPUT_DEPENDENCY_COMMANDS
+# -----------------------------
+# This macro should only be invoked once -- use via AC_REQUIRE.
+#
+# This code is only required when automatic dependency tracking
+# is enabled. FIXME. This creates each '.P' file that we will
+# need in order to bootstrap the dependency handling code.
+AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
+[AC_CONFIG_COMMANDS([depfiles],
+ [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
+ [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
+])
+
+# Do all the work for Automake. -*- Autoconf -*-
+
+# Copyright (C) 1996-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This macro actually does too much. Some checks are only needed if
+# your package does certain things. But this isn't really a big deal.
+
+dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O.
+m4_define([AC_PROG_CC],
+m4_defn([AC_PROG_CC])
+[_AM_PROG_CC_C_O
+])
+
+# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
+# AM_INIT_AUTOMAKE([OPTIONS])
+# -----------------------------------------------
+# The call with PACKAGE and VERSION arguments is the old style
+# call (pre autoconf-2.50), which is being phased out. PACKAGE
+# and VERSION should now be passed to AC_INIT and removed from
+# the call to AM_INIT_AUTOMAKE.
+# We support both call styles for the transition. After
+# the next Automake release, Autoconf can make the AC_INIT
+# arguments mandatory, and then we can depend on a new Autoconf
+# release and drop the old call support.
+AC_DEFUN([AM_INIT_AUTOMAKE],
+[AC_PREREQ([2.65])dnl
+dnl Autoconf wants to disallow AM_ names. We explicitly allow
+dnl the ones we care about.
+m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
+AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
+AC_REQUIRE([AC_PROG_INSTALL])dnl
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+ # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+ # is not polluted with repeated "-I."
+ AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
+ # test to see if srcdir already configured
+ if test -f $srcdir/config.status; then
+ AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
+ fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+ if (cygpath --version) >/dev/null 2>/dev/null; then
+ CYGPATH_W='cygpath -w'
+ else
+ CYGPATH_W=echo
+ fi
+fi
+AC_SUBST([CYGPATH_W])
+
+# Define the identity of the package.
+dnl Distinguish between old-style and new-style calls.
+m4_ifval([$2],
+[AC_DIAGNOSE([obsolete],
+ [$0: two- and three-arguments forms are deprecated.])
+m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
+ AC_SUBST([PACKAGE], [$1])dnl
+ AC_SUBST([VERSION], [$2])],
+[_AM_SET_OPTIONS([$1])dnl
+dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
+m4_if(
+ m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]),
+ [ok:ok],,
+ [m4_fatal([AC_INIT should be called with package and version arguments])])dnl
+ AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
+ AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
+
+_AM_IF_OPTION([no-define],,
+[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package])
+ AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl
+
+# Some tools Automake needs.
+AC_REQUIRE([AM_SANITY_CHECK])dnl
+AC_REQUIRE([AC_ARG_PROGRAM])dnl
+AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}])
+AM_MISSING_PROG([AUTOCONF], [autoconf])
+AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}])
+AM_MISSING_PROG([AUTOHEADER], [autoheader])
+AM_MISSING_PROG([MAKEINFO], [makeinfo])
+AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
+AC_REQUIRE([AC_PROG_MKDIR_P])dnl
+# For better backward compatibility. To be removed once Automake 1.9.x
+# dies out for good. For more background, see:
+# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
+# We need awk for the "check" target (and possibly the TAP driver). The
+# system "awk" is bad on some platforms.
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
+ [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
+ [_AM_PROG_TAR([v7])])])
+_AM_IF_OPTION([no-dependencies],,
+[AC_PROVIDE_IFELSE([AC_PROG_CC],
+ [_AM_DEPENDENCIES([CC])],
+ [m4_define([AC_PROG_CC],
+ m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_CXX],
+ [_AM_DEPENDENCIES([CXX])],
+ [m4_define([AC_PROG_CXX],
+ m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJC],
+ [_AM_DEPENDENCIES([OBJC])],
+ [m4_define([AC_PROG_OBJC],
+ m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJCXX],
+ [_AM_DEPENDENCIES([OBJCXX])],
+ [m4_define([AC_PROG_OBJCXX],
+ m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl
+])
+AC_REQUIRE([AM_SILENT_RULES])dnl
+dnl The testsuite driver may need to know about EXEEXT, so add the
+dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This
+dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below.
+AC_CONFIG_COMMANDS_PRE(dnl
+[m4_provide_if([_AM_COMPILER_EXEEXT],
+ [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
+
+# POSIX will say in a future version that running "rm -f" with no argument
+# is OK; and we want to be able to make that assumption in our Makefile
+# recipes. So use an aggressive probe to check that the usage we want is
+# actually supported "in the wild" to an acceptable degree.
+# See automake bug#10828.
+# To make any issue more visible, cause the running configure to be aborted
+# by default if the 'rm' program in use doesn't match our expectations; the
+# user can still override this though.
+if rm -f && rm -fr && rm -rf; then : OK; else
+ cat >&2 <<'END'
+Oops!
+
+Your 'rm' program seems unable to run without file operands specified
+on the command line, even when the '-f' option is present. This is contrary
+to the behaviour of most rm programs out there, and not conforming with
+the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>
+
+Please tell bug-automake@gnu.org about your system, including the value
+of your $PATH and any error possibly output before this message. This
+can help us improve future automake versions.
+
+END
+ if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
+ echo 'Configuration will proceed anyway, since you have set the' >&2
+ echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
+ echo >&2
+ else
+ cat >&2 <<'END'
+Aborting the configuration process, to ensure you take notice of the issue.
+
+You can download and install GNU coreutils to get an 'rm' implementation
+that behaves properly: <http://www.gnu.org/software/coreutils/>.
+
+If you want to complete the configuration process using your problematic
+'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
+to "yes", and re-run configure.
+
+END
+ AC_MSG_ERROR([Your 'rm' program is bad, sorry.])
+ fi
+fi
+dnl The trailing newline in this macro's definition is deliberate, for
+dnl backward compatibility and to allow trailing 'dnl'-style comments
+dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841.
+])
+
+dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not
+dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
+dnl mangled by Autoconf and run in a shell conditional statement.
+m4_define([_AC_COMPILER_EXEEXT],
+m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
+
+# When config.status generates a header, we must update the stamp-h file.
+# This file resides in the same directory as the config header
+# that is generated. The stamp files are numbered to have different names.
+
+# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
+# loop where config.status creates the headers, so we can generate
+# our stamp files there.
+AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
+[# Compute $1's index in $config_headers.
+_am_arg=$1
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+ case $_am_header in
+ $_am_arg | $_am_arg:* )
+ break ;;
+ * )
+ _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+ esac
+done
+echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
+
+# Copyright (C) 2001-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_SH
+# ------------------
+# Define $install_sh.
+AC_DEFUN([AM_PROG_INSTALL_SH],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+if test x"${install_sh+set}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+ *)
+ install_sh="\${SHELL} $am_aux_dir/install-sh"
+ esac
+fi
+AC_SUBST([install_sh])])
+
+# Copyright (C) 2003-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# Check whether the underlying file-system supports filenames
+# with a leading dot. For instance MS-DOS doesn't.
+AC_DEFUN([AM_SET_LEADING_DOT],
+[rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+ am__leading_dot=.
+else
+ am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+AC_SUBST([am__leading_dot])])
+
+# Check to see how 'make' treats includes. -*- Autoconf -*-
+
+# Copyright (C) 2001-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_MAKE_INCLUDE()
+# -----------------
+# Check to see how make treats includes.
+AC_DEFUN([AM_MAKE_INCLUDE],
+[am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+ @echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+AC_MSG_CHECKING([for style of include used by $am_make])
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from 'make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+ am__include=include
+ am__quote=
+ _am_result=GNU
+ ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+ echo '.include "confinc"' > confmf
+ case `$am_make -s -f confmf 2> /dev/null` in #(
+ *the\ am__doit\ target*)
+ am__include=.include
+ am__quote="\""
+ _am_result=BSD
+ ;;
+ esac
+fi
+AC_SUBST([am__include])
+AC_SUBST([am__quote])
+AC_MSG_RESULT([$_am_result])
+rm -f confinc confmf
+])
+
+# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
+
+# Copyright (C) 1997-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_MISSING_PROG(NAME, PROGRAM)
+# ------------------------------
+AC_DEFUN([AM_MISSING_PROG],
+[AC_REQUIRE([AM_MISSING_HAS_RUN])
+$1=${$1-"${am_missing_run}$2"}
+AC_SUBST($1)])
+
+# AM_MISSING_HAS_RUN
+# ------------------
+# Define MISSING if not defined so far and test if it is modern enough.
+# If it is, set am_missing_run to use it, otherwise, to nothing.
+AC_DEFUN([AM_MISSING_HAS_RUN],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([missing])dnl
+if test x"${MISSING+set}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+ *)
+ MISSING="\${SHELL} $am_aux_dir/missing" ;;
+ esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --is-lightweight"; then
+ am_missing_run="$MISSING "
+else
+ am_missing_run=
+ AC_MSG_WARN(['missing' script is too old or missing])
+fi
+])
+
+# -*- Autoconf -*-
+# Obsolete and "removed" macros, that must however still report explicit
+# error messages when used, to smooth transition.
+#
+# Copyright (C) 1996-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([AM_CONFIG_HEADER],
+[AC_DIAGNOSE([obsolete],
+['$0': this macro is obsolete.
+You should use the 'AC][_CONFIG_HEADERS' macro instead.])dnl
+AC_CONFIG_HEADERS($@)])
+
+AC_DEFUN([AM_PROG_CC_STDC],
+[AC_PROG_CC
+am_cv_prog_cc_stdc=$ac_cv_prog_cc_stdc
+AC_DIAGNOSE([obsolete],
+['$0': this macro is obsolete.
+You should simply use the 'AC][_PROG_CC' macro instead.
+Also, your code should no longer depend upon 'am_cv_prog_cc_stdc',
+but upon 'ac_cv_prog_cc_stdc'.])])
+
+AC_DEFUN([AM_C_PROTOTYPES],
+ [AC_FATAL([automatic de-ANSI-fication support has been removed])])
+AU_DEFUN([fp_C_PROTOTYPES], [AM_C_PROTOTYPES])
+
+# Helper functions for option handling. -*- Autoconf -*-
+
+# Copyright (C) 2001-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# _AM_MANGLE_OPTION(NAME)
+# -----------------------
+AC_DEFUN([_AM_MANGLE_OPTION],
+[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
+
+# _AM_SET_OPTION(NAME)
+# --------------------
+# Set option NAME. Presently that only means defining a flag for this option.
+AC_DEFUN([_AM_SET_OPTION],
+[m4_define(_AM_MANGLE_OPTION([$1]), [1])])
+
+# _AM_SET_OPTIONS(OPTIONS)
+# ------------------------
+# OPTIONS is a space-separated list of Automake options.
+AC_DEFUN([_AM_SET_OPTIONS],
+[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
+
+# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
+# -------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+AC_DEFUN([_AM_IF_OPTION],
+[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
+
+# Copyright (C) 1999-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# _AM_PROG_CC_C_O
+# ---------------
+# Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC
+# to automatically call this.
+AC_DEFUN([_AM_PROG_CC_C_O],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([compile])dnl
+AC_LANG_PUSH([C])dnl
+AC_CACHE_CHECK(
+ [whether $CC understands -c and -o together],
+ [am_cv_prog_cc_c_o],
+ [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])])
+ # Make sure it works both with $CC and with simple cc.
+ # Following AC_PROG_CC_C_O, we do the test twice because some
+ # compilers refuse to overwrite an existing .o file with -o,
+ # though they will create one.
+ am_cv_prog_cc_c_o=yes
+ for am_i in 1 2; do
+ if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \
+ && test -f conftest2.$ac_objext; then
+ : OK
+ else
+ am_cv_prog_cc_c_o=no
+ break
+ fi
+ done
+ rm -f core conftest*
+ unset am_i])
+if test "$am_cv_prog_cc_c_o" != yes; then
+ # Losing compiler, so override with the script.
+ # FIXME: It is wrong to rewrite CC.
+ # But if we don't then we get into trouble of one sort or another.
+ # A longer-term fix would be to have automake use am__CC in this case,
+ # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+ CC="$am_aux_dir/compile $CC"
+fi
+AC_LANG_POP([C])])
+
+# For backward compatibility.
+AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
+
+# Copyright (C) 2001-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_RUN_LOG(COMMAND)
+# -------------------
+# Run COMMAND, save the exit status in ac_status, and log it.
+# (This has been adapted from Autoconf's _AC_RUN_LOG macro.)
+AC_DEFUN([AM_RUN_LOG],
+[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
+ ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+ (exit $ac_status); }])
+
+# Check to make sure that the build environment is sane. -*- Autoconf -*-
+
+# Copyright (C) 1996-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_SANITY_CHECK
+# ---------------
+AC_DEFUN([AM_SANITY_CHECK],
+[AC_MSG_CHECKING([whether build environment is sane])
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name. Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+ *[[\\\"\#\$\&\'\`$am_lf]]*)
+ AC_MSG_ERROR([unsafe absolute working directory name]);;
+esac
+case $srcdir in
+ *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*)
+ AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);;
+esac
+
+# Do 'set' in a subshell so we don't clobber the current shell's
+# arguments. Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+ am_has_slept=no
+ for am_try in 1 2; do
+ echo "timestamp, slept: $am_has_slept" > conftest.file
+ set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+ if test "$[*]" = "X"; then
+ # -L didn't work.
+ set X `ls -t "$srcdir/configure" conftest.file`
+ fi
+ if test "$[*]" != "X $srcdir/configure conftest.file" \
+ && test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+ # If neither matched, then we have a broken ls. This can happen
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+ AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
+ alias in your environment])
+ fi
+ if test "$[2]" = conftest.file || test $am_try -eq 2; then
+ break
+ fi
+ # Just in case.
+ sleep 1
+ am_has_slept=yes
+ done
+ test "$[2]" = conftest.file
+ )
+then
+ # Ok.
+ :
+else
+ AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+AC_MSG_RESULT([yes])
+# If we didn't sleep, we still need to ensure time stamps of config.status and
+# generated files are strictly newer.
+am_sleep_pid=
+if grep 'slept: no' conftest.file >/dev/null 2>&1; then
+ ( sleep 1 ) &
+ am_sleep_pid=$!
+fi
+AC_CONFIG_COMMANDS_PRE(
+ [AC_MSG_CHECKING([that generated files are newer than configure])
+ if test -n "$am_sleep_pid"; then
+ # Hide warnings about reused PIDs.
+ wait $am_sleep_pid 2>/dev/null
+ fi
+ AC_MSG_RESULT([done])])
+rm -f conftest.file
+])
+
+# Copyright (C) 2009-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_SILENT_RULES([DEFAULT])
+# --------------------------
+# Enable less verbose build rules; with the default set to DEFAULT
+# ("yes" being less verbose, "no" or empty being verbose).
+AC_DEFUN([AM_SILENT_RULES],
+[AC_ARG_ENABLE([silent-rules], [dnl
+AS_HELP_STRING(
+ [--enable-silent-rules],
+ [less verbose build output (undo: "make V=1")])
+AS_HELP_STRING(
+ [--disable-silent-rules],
+ [verbose build output (undo: "make V=0")])dnl
+])
+case $enable_silent_rules in @%:@ (((
+ yes) AM_DEFAULT_VERBOSITY=0;;
+ no) AM_DEFAULT_VERBOSITY=1;;
+ *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);;
+esac
+dnl
+dnl A few 'make' implementations (e.g., NonStop OS and NextStep)
+dnl do not support nested variable expansions.
+dnl See automake bug#9928 and bug#10237.
+am_make=${MAKE-make}
+AC_CACHE_CHECK([whether $am_make supports nested variables],
+ [am_cv_make_support_nested_variables],
+ [if AS_ECHO([['TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+ @$(TRUE)
+.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then
+ am_cv_make_support_nested_variables=yes
+else
+ am_cv_make_support_nested_variables=no
+fi])
+if test $am_cv_make_support_nested_variables = yes; then
+ dnl Using '$V' instead of '$(V)' breaks IRIX make.
+ AM_V='$(V)'
+ AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+ AM_V=$AM_DEFAULT_VERBOSITY
+ AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AC_SUBST([AM_V])dnl
+AM_SUBST_NOTMAKE([AM_V])dnl
+AC_SUBST([AM_DEFAULT_V])dnl
+AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl
+AC_SUBST([AM_DEFAULT_VERBOSITY])dnl
+AM_BACKSLASH='\'
+AC_SUBST([AM_BACKSLASH])dnl
+_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
+])
+
+# Copyright (C) 2001-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# AM_PROG_INSTALL_STRIP
+# ---------------------
+# One issue with vendor 'install' (even GNU) is that you can't
+# specify the program used to strip binaries. This is especially
+# annoying in cross-compiling environments, where the build's strip
+# is unlikely to handle the host's binaries.
+# Fortunately install-sh will honor a STRIPPROG variable, so we
+# always use install-sh in "make install-strip", and initialize
+# STRIPPROG with the value of the STRIP variable (set by the user).
+AC_DEFUN([AM_PROG_INSTALL_STRIP],
+[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+# Installed binaries are usually stripped using 'strip' when the user
+# run "make install-strip". However 'strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the 'STRIP' environment variable to overrule this program.
+dnl Don't test for $cross_compiling = yes, because it might be 'maybe'.
+if test "$cross_compiling" != no; then
+ AC_CHECK_TOOL([STRIP], [strip], :)
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+AC_SUBST([INSTALL_STRIP_PROGRAM])])
+
+# Copyright (C) 2006-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# _AM_SUBST_NOTMAKE(VARIABLE)
+# ---------------------------
+# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
+# This macro is traced by Automake.
+AC_DEFUN([_AM_SUBST_NOTMAKE])
+
+# AM_SUBST_NOTMAKE(VARIABLE)
+# --------------------------
+# Public sister of _AM_SUBST_NOTMAKE.
+AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
+
+# Check how to create a tarball. -*- Autoconf -*-
+
+# Copyright (C) 2004-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# _AM_PROG_TAR(FORMAT)
+# --------------------
+# Check how to create a tarball in format FORMAT.
+# FORMAT should be one of 'v7', 'ustar', or 'pax'.
+#
+# Substitute a variable $(am__tar) that is a command
+# writing to stdout a FORMAT-tarball containing the directory
+# $tardir.
+# tardir=directory && $(am__tar) > result.tar
+#
+# Substitute a variable $(am__untar) that extract such
+# a tarball read from stdin.
+# $(am__untar) < result.tar
+#
+AC_DEFUN([_AM_PROG_TAR],
+[# Always define AMTAR for backward compatibility. Yes, it's still used
+# in the wild :-( We should find a proper way to deprecate it ...
+AC_SUBST([AMTAR], ['$${TAR-tar}'])
+
+# We'll loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
+
+m4_if([$1], [v7],
+ [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'],
+
+ [m4_case([$1],
+ [ustar],
+ [# The POSIX 1988 'ustar' format is defined with fixed-size fields.
+ # There is notably a 21 bits limit for the UID and the GID. In fact,
+ # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343
+ # and bug#13588).
+ am_max_uid=2097151 # 2^21 - 1
+ am_max_gid=$am_max_uid
+ # The $UID and $GID variables are not portable, so we need to resort
+ # to the POSIX-mandated id(1) utility. Errors in the 'id' calls
+ # below are definitely unexpected, so allow the users to see them
+ # (that is, avoid stderr redirection).
+ am_uid=`id -u || echo unknown`
+ am_gid=`id -g || echo unknown`
+ AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format])
+ if test $am_uid -le $am_max_uid; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ _am_tools=none
+ fi
+ AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format])
+ if test $am_gid -le $am_max_gid; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ _am_tools=none
+ fi],
+
+ [pax],
+ [],
+
+ [m4_fatal([Unknown tar format])])
+
+ AC_MSG_CHECKING([how to create a $1 tar archive])
+
+ # Go ahead even if we have the value already cached. We do so because we
+ # need to set the values for the 'am__tar' and 'am__untar' variables.
+ _am_tools=${am_cv_prog_tar_$1-$_am_tools}
+
+ for _am_tool in $_am_tools; do
+ case $_am_tool in
+ gnutar)
+ for _am_tar in tar gnutar gtar; do
+ AM_RUN_LOG([$_am_tar --version]) && break
+ done
+ am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
+ am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
+ am__untar="$_am_tar -xf -"
+ ;;
+ plaintar)
+ # Must skip GNU tar: if it does not support --format= it doesn't create
+ # ustar tarball either.
+ (tar --version) >/dev/null 2>&1 && continue
+ am__tar='tar chf - "$$tardir"'
+ am__tar_='tar chf - "$tardir"'
+ am__untar='tar xf -'
+ ;;
+ pax)
+ am__tar='pax -L -x $1 -w "$$tardir"'
+ am__tar_='pax -L -x $1 -w "$tardir"'
+ am__untar='pax -r'
+ ;;
+ cpio)
+ am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
+ am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
+ am__untar='cpio -i -H $1 -d'
+ ;;
+ none)
+ am__tar=false
+ am__tar_=false
+ am__untar=false
+ ;;
+ esac
+
+ # If the value was cached, stop now. We just wanted to have am__tar
+ # and am__untar set.
+ test -n "${am_cv_prog_tar_$1}" && break
+
+ # tar/untar a dummy directory, and stop if the command works.
+ rm -rf conftest.dir
+ mkdir conftest.dir
+ echo GrepMe > conftest.dir/file
+ AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+ rm -rf conftest.dir
+ if test -s conftest.tar; then
+ AM_RUN_LOG([$am__untar <conftest.tar])
+ AM_RUN_LOG([cat conftest.dir/file])
+ grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
+ fi
+ done
+ rm -rf conftest.dir
+
+ AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
+ AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+
+AC_SUBST([am__tar])
+AC_SUBST([am__untar])
+]) # _AM_PROG_TAR
+
diff --git a/contrib/ldapc++/config.guess b/contrib/ldapc++/config.guess
new file mode 100755
index 0000000..45001cf
--- /dev/null
+++ b/contrib/ldapc++/config.guess
@@ -0,0 +1,1667 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright 1992-2020 Free Software Foundation, Inc.
+
+timestamp='2020-01-01'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+#
+# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
+#
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
+#
+# Please send patches to <config-patches@gnu.org>.
+
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Options:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright 1992-2020 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+tmp=
+# shellcheck disable=SC2172
+trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15
+
+set_cc_for_build() {
+ # prevent multiple calls if $tmp is already set
+ test "$tmp" && return 0
+ : "${TMPDIR=/tmp}"
+ # shellcheck disable=SC2039
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; }
+ dummy=$tmp/dummy
+ case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in
+ ,,) echo "int x;" > "$dummy.c"
+ for driver in cc gcc c89 c99 ; do
+ if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$driver"
+ break
+ fi
+ done
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+ esac
+}
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if test -f /.attbin/uname ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+case "$UNAME_SYSTEM" in
+Linux|GNU|GNU/*)
+ # If the system lacks a compiler, then just pick glibc.
+ # We could probably try harder.
+ LIBC=gnu
+
+ set_cc_for_build
+ cat <<-EOF > "$dummy.c"
+ #include <features.h>
+ #if defined(__UCLIBC__)
+ LIBC=uclibc
+ #elif defined(__dietlibc__)
+ LIBC=dietlibc
+ #else
+ LIBC=gnu
+ #endif
+ EOF
+ eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`"
+
+ # If ldd exists, use it to detect musl libc.
+ if command -v ldd >/dev/null && \
+ ldd --version 2>&1 | grep -q ^musl
+ then
+ LIBC=musl
+ fi
+ ;;
+esac
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
+ "/sbin/$sysctl" 2>/dev/null || \
+ "/usr/sbin/$sysctl" 2>/dev/null || \
+ echo unknown)`
+ case "$UNAME_MACHINE_ARCH" in
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ sh5el) machine=sh5le-unknown ;;
+ earmv*)
+ arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
+ endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'`
+ machine="${arch}${endian}"-unknown
+ ;;
+ *) machine="$UNAME_MACHINE_ARCH"-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently (or will in the future) and ABI.
+ case "$UNAME_MACHINE_ARCH" in
+ earm*)
+ os=netbsdelf
+ ;;
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ELF__
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # Determine ABI tags.
+ case "$UNAME_MACHINE_ARCH" in
+ earm*)
+ expr='s/^earmv[0-9]/-eabi/;s/eb$//'
+ abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"`
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "$UNAME_VERSION" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "$machine-${os}${release}${abi-}"
+ exit ;;
+ *:Bitrig:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+ echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE"
+ exit ;;
+ *:OpenBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+ echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE"
+ exit ;;
+ *:LibertyBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
+ echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE"
+ exit ;;
+ *:MidnightBSD:*:*)
+ echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE"
+ exit ;;
+ *:ekkoBSD:*:*)
+ echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE"
+ exit ;;
+ *:SolidBSD:*:*)
+ echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE"
+ exit ;;
+ *:OS108:*:*)
+ echo "$UNAME_MACHINE"-unknown-os108_"$UNAME_RELEASE"
+ exit ;;
+ macppc:MirBSD:*:*)
+ echo powerpc-unknown-mirbsd"$UNAME_RELEASE"
+ exit ;;
+ *:MirBSD:*:*)
+ echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE"
+ exit ;;
+ *:Sortix:*:*)
+ echo "$UNAME_MACHINE"-unknown-sortix
+ exit ;;
+ *:Twizzler:*:*)
+ echo "$UNAME_MACHINE"-unknown-twizzler
+ exit ;;
+ *:Redox:*:*)
+ echo "$UNAME_MACHINE"-unknown-redox
+ exit ;;
+ mips:OSF1:*.*)
+ echo mips-dec-osf1
+ exit ;;
+ alpha:OSF1:*:*)
+ case $UNAME_RELEASE in
+ *4.0)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ ;;
+ *5.*)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ ;;
+ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE=alpha ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE=alpha ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE=alpha ;;
+ "EV5 (21164)")
+ UNAME_MACHINE=alphaev5 ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE=alphaev56 ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE=alphapca56 ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE=alphapca57 ;;
+ "EV6 (21264)")
+ UNAME_MACHINE=alphaev6 ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE=alphaev67 ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE=alphaev68 ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE=alphaev68 ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE=alphaev68 ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE=alphaev69 ;;
+ "EV7 (21364)")
+ UNAME_MACHINE=alphaev7 ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE=alphaev79 ;;
+ esac
+ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo "$UNAME_MACHINE"-dec-osf"`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`"
+ # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+ exitcode=$?
+ trap '' 0
+ exit $exitcode ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo "$UNAME_MACHINE"-unknown-amigaos
+ exit ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo "$UNAME_MACHINE"-unknown-morphos
+ exit ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit ;;
+ *:z/VM:*:*)
+ echo s390-ibm-zvmoe
+ exit ;;
+ *:OS400:*:*)
+ echo powerpc-ibm-os400
+ exit ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix"$UNAME_RELEASE"
+ exit ;;
+ arm*:riscos:*:*|arm*:RISCOS:*:*)
+ echo arm-unknown-riscos
+ exit ;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit ;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "`(/bin/universe) 2>/dev/null`" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit ;;
+ DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) echo sparc-icl-nx7; exit ;;
+ esac ;;
+ s390x:SunOS:*:*)
+ echo "$UNAME_MACHINE"-ibm-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
+ exit ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+ exit ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2"`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`"
+ exit ;;
+ i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+ echo i386-pc-auroraux"$UNAME_RELEASE"
+ exit ;;
+ i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+ set_cc_for_build
+ SUN_ARCH=i386
+ # If there is a compiler, see if it is configured for 64-bit objects.
+ # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+ # This test works for both compilers.
+ if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
+ if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ SUN_ARCH=x86_64
+ fi
+ fi
+ echo "$SUN_ARCH"-pc-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+ exit ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+ exit ;;
+ sun4*:SunOS:*:*)
+ case "`/usr/bin/arch -k`" in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos"`echo "$UNAME_RELEASE"|sed -e 's/-/_/'`"
+ exit ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos"$UNAME_RELEASE"
+ exit ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3
+ case "`/bin/arch`" in
+ sun3)
+ echo m68k-sun-sunos"$UNAME_RELEASE"
+ ;;
+ sun4)
+ echo sparc-sun-sunos"$UNAME_RELEASE"
+ ;;
+ esac
+ exit ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos"$UNAME_RELEASE"
+ exit ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint"$UNAME_RELEASE"
+ exit ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint"$UNAME_RELEASE"
+ exit ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint"$UNAME_RELEASE"
+ exit ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint"$UNAME_RELEASE"
+ exit ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint"$UNAME_RELEASE"
+ exit ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint"$UNAME_RELEASE"
+ exit ;;
+ m68k:machten:*:*)
+ echo m68k-apple-machten"$UNAME_RELEASE"
+ exit ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten"$UNAME_RELEASE"
+ exit ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix"$UNAME_RELEASE"
+ exit ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix"$UNAME_RELEASE"
+ exit ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix"$UNAME_RELEASE"
+ exit ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
+ dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+ SYSTEM_NAME=`"$dummy" "$dummyarg"` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo mips-mips-riscos"$UNAME_RELEASE"
+ exit ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if [ "$UNAME_PROCESSOR" = mc88100 ] || [ "$UNAME_PROCESSOR" = mc88110 ]
+ then
+ if [ "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx ] || \
+ [ "$TARGET_BINARY_INTERFACE"x = x ]
+ then
+ echo m88k-dg-dgux"$UNAME_RELEASE"
+ else
+ echo m88k-dg-dguxbcs"$UNAME_RELEASE"
+ fi
+ else
+ echo i586-dg-dgux"$UNAME_RELEASE"
+ fi
+ exit ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix"`echo "$UNAME_RELEASE"|sed -e 's/-/_/g'`"
+ exit ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit ;;
+ ia64:AIX:*:*)
+ if [ -x /usr/bin/oslevel ] ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
+ fi
+ echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV"
+ exit ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"`
+ then
+ echo "$SYSTEM_NAME"
+ else
+ echo rs6000-ibm-aix3.2.5
+ fi
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit ;;
+ *:AIX:*:[4567])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if [ -x /usr/bin/lslpp ] ; then
+ IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |
+ awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
+ else
+ IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
+ fi
+ echo "$IBM_ARCH"-ibm-aix"$IBM_REV"
+ exit ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit ;;
+ ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*)
+ echo romp-ibm-bsd4.4
+ exit ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to
+ exit ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
+ case "$UNAME_MACHINE" in
+ 9000/31?) HP_ARCH=m68000 ;;
+ 9000/[34]??) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if [ -x /usr/bin/getconf ]; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "$sc_cpu_version" in
+ 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "$sc_kernel_bits" in
+ 32) HP_ARCH=hppa2.0n ;;
+ 64) HP_ARCH=hppa2.0w ;;
+ '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if [ "$HP_ARCH" = "" ]; then
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if [ "$HP_ARCH" = hppa2.0w ]
+ then
+ set_cc_for_build
+
+ # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+ # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
+ # generating 64-bit code. GNU and HP use different nomenclature:
+ #
+ # $ CC_FOR_BUILD=cc ./config.guess
+ # => hppa2.0w-hp-hpux11.23
+ # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+ # => hppa64-hp-hpux11.23
+
+ if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) |
+ grep -q __LP64__
+ then
+ HP_ARCH=hppa2.0w
+ else
+ HP_ARCH=hppa64
+ fi
+ fi
+ echo "$HP_ARCH"-hp-hpux"$HPUX_REV"
+ exit ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//'`
+ echo ia64-hp-hpux"$HPUX_REV"
+ exit ;;
+ 3050*:HI-UX:*:*)
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo unknown-hitachi-hiuxwe2
+ exit ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*)
+ echo hppa1.1-hp-bsd
+ exit ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*)
+ echo hppa1.1-hp-osf
+ exit ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit ;;
+ i*86:OSF1:*:*)
+ if [ -x /usr/sbin/sysversion ] ; then
+ echo "$UNAME_MACHINE"-unknown-osf1mk
+ else
+ echo "$UNAME_MACHINE"-unknown-osf1
+ fi
+ exit ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ *:UNICOS/mp:*:*)
+ echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+ FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+ FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+ FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE"
+ exit ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi"$UNAME_RELEASE"
+ exit ;;
+ *:BSD/OS:*:*)
+ echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE"
+ exit ;;
+ arm:FreeBSD:*:*)
+ UNAME_PROCESSOR=`uname -p`
+ set_cc_for_build
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabi
+ else
+ echo "${UNAME_PROCESSOR}"-unknown-freebsd"`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`"-gnueabihf
+ fi
+ exit ;;
+ *:FreeBSD:*:*)
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ case "$UNAME_PROCESSOR" in
+ amd64)
+ UNAME_PROCESSOR=x86_64 ;;
+ i386)
+ UNAME_PROCESSOR=i586 ;;
+ esac
+ echo "$UNAME_PROCESSOR"-unknown-freebsd"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`"
+ exit ;;
+ i*:CYGWIN*:*)
+ echo "$UNAME_MACHINE"-pc-cygwin
+ exit ;;
+ *:MINGW64*:*)
+ echo "$UNAME_MACHINE"-pc-mingw64
+ exit ;;
+ *:MINGW*:*)
+ echo "$UNAME_MACHINE"-pc-mingw32
+ exit ;;
+ *:MSYS*:*)
+ echo "$UNAME_MACHINE"-pc-msys
+ exit ;;
+ i*:PW*:*)
+ echo "$UNAME_MACHINE"-pc-pw32
+ exit ;;
+ *:Interix*:*)
+ case "$UNAME_MACHINE" in
+ x86)
+ echo i586-pc-interix"$UNAME_RELEASE"
+ exit ;;
+ authenticamd | genuineintel | EM64T)
+ echo x86_64-unknown-interix"$UNAME_RELEASE"
+ exit ;;
+ IA64)
+ echo ia64-unknown-interix"$UNAME_RELEASE"
+ exit ;;
+ esac ;;
+ i*:UWIN*:*)
+ echo "$UNAME_MACHINE"-pc-uwin
+ exit ;;
+ amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+ echo x86_64-pc-cygwin
+ exit ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2"`echo "$UNAME_RELEASE"|sed -e 's/[^.]*//'`"
+ exit ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo "`echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,'`-unknown-$LIBC`echo "$UNAME_RELEASE"|sed -e 's,/.*$,,'`"
+ exit ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo "$UNAME_MACHINE-unknown-`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`-$LIBC"
+ exit ;;
+ *:Minix:*:*)
+ echo "$UNAME_MACHINE"-unknown-minix
+ exit ;;
+ aarch64:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ aarch64_be:Linux:*:*)
+ UNAME_MACHINE=aarch64_be
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep -q ld.so.1
+ if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ arc:Linux:*:* | arceb:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ arm*:Linux:*:*)
+ set_cc_for_build
+ if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_EABI__
+ then
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ else
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi
+ else
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf
+ fi
+ fi
+ exit ;;
+ avr32*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ cris:Linux:*:*)
+ echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
+ exit ;;
+ crisv32:Linux:*:*)
+ echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
+ exit ;;
+ e2k:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ frv:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ hexagon:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ i*86:Linux:*:*)
+ echo "$UNAME_MACHINE"-pc-linux-"$LIBC"
+ exit ;;
+ ia64:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ k1om:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ m32r*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ m68*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ mips:Linux:*:* | mips64:Linux:*:*)
+ set_cc_for_build
+ IS_GLIBC=0
+ test x"${LIBC}" = xgnu && IS_GLIBC=1
+ sed 's/^ //' << EOF > "$dummy.c"
+ #undef CPU
+ #undef mips
+ #undef mipsel
+ #undef mips64
+ #undef mips64el
+ #if ${IS_GLIBC} && defined(_ABI64)
+ LIBCABI=gnuabi64
+ #else
+ #if ${IS_GLIBC} && defined(_ABIN32)
+ LIBCABI=gnuabin32
+ #else
+ LIBCABI=${LIBC}
+ #endif
+ #endif
+
+ #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+ CPU=mipsisa64r6
+ #else
+ #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+ CPU=mipsisa32r6
+ #else
+ #if defined(__mips64)
+ CPU=mips64
+ #else
+ CPU=mips
+ #endif
+ #endif
+ #endif
+
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ MIPS_ENDIAN=el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ MIPS_ENDIAN=
+ #else
+ MIPS_ENDIAN=
+ #endif
+ #endif
+EOF
+ eval "`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'`"
+ test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; }
+ ;;
+ mips64el:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ openrisc*:Linux:*:*)
+ echo or1k-unknown-linux-"$LIBC"
+ exit ;;
+ or32:Linux:*:* | or1k*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ padre:Linux:*:*)
+ echo sparc-unknown-linux-"$LIBC"
+ exit ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-"$LIBC"
+ exit ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;;
+ PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;;
+ *) echo hppa-unknown-linux-"$LIBC" ;;
+ esac
+ exit ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-"$LIBC"
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-"$LIBC"
+ exit ;;
+ ppc64le:Linux:*:*)
+ echo powerpc64le-unknown-linux-"$LIBC"
+ exit ;;
+ ppcle:Linux:*:*)
+ echo powerpcle-unknown-linux-"$LIBC"
+ exit ;;
+ riscv32:Linux:*:* | riscv64:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo "$UNAME_MACHINE"-ibm-linux-"$LIBC"
+ exit ;;
+ sh64*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ sh*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ tile*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ vax:Linux:*:*)
+ echo "$UNAME_MACHINE"-dec-linux-"$LIBC"
+ exit ;;
+ x86_64:Linux:*:*)
+ echo "$UNAME_MACHINE"-pc-linux-"$LIBC"
+ exit ;;
+ xtensa*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION"
+ exit ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo "$UNAME_MACHINE"-pc-os2-emx
+ exit ;;
+ i*86:XTS-300:*:STOP)
+ echo "$UNAME_MACHINE"-unknown-stop
+ exit ;;
+ i*86:atheos:*:*)
+ echo "$UNAME_MACHINE"-unknown-atheos
+ exit ;;
+ i*86:syllable:*:*)
+ echo "$UNAME_MACHINE"-pc-syllable
+ exit ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+ echo i386-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ i*86:*DOS:*:*)
+ echo "$UNAME_MACHINE"-pc-msdosdjgpp
+ exit ;;
+ i*86:*:4.*:*)
+ UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL"
+ else
+ echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL"
+ fi
+ exit ;;
+ i*86:*:5:[678]*)
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}"
+ exit ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ echo "$UNAME_MACHINE"-pc-isc"$UNAME_REL"
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL"
+ else
+ echo "$UNAME_MACHINE"-pc-sysv32
+ fi
+ exit ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i586.
+ # Note: whatever this is, it MUST be the same as what config.sub
+ # prints for the "djgpp" host, or else GDB configure will decide that
+ # this is a cross-build.
+ echo i586-pc-msdosdjgpp
+ exit ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4
+ fi
+ exit ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit ;;
+ M68*:*:R3V[5678]*:*)
+ test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+ OS_REL='.3'
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+ echo powerpc-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv"$UNAME_RELEASE"
+ exit ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ echo "$UNAME_MACHINE"-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit ;;
+ i*86:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo "$UNAME_MACHINE"-stratus-vos
+ exit ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux"$UNAME_RELEASE"
+ exit ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if [ -d /usr/nec ]; then
+ echo mips-nec-sysv"$UNAME_RELEASE"
+ else
+ echo mips-unknown-sysv"$UNAME_RELEASE"
+ fi
+ exit ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit ;;
+ BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
+ echo i586-pc-haiku
+ exit ;;
+ x86_64:Haiku:*:*)
+ echo x86_64-unknown-haiku
+ exit ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-7:SUPER-UX:*:*)
+ echo sx7-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-8:SUPER-UX:*:*)
+ echo sx8-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-8R:SUPER-UX:*:*)
+ echo sx8r-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-ACE:SUPER-UX:*:*)
+ echo sxace-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody"$UNAME_RELEASE"
+ exit ;;
+ *:Rhapsody:*:*)
+ echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE"
+ exit ;;
+ *:Darwin:*:*)
+ UNAME_PROCESSOR=`uname -p`
+ case $UNAME_PROCESSOR in
+ unknown) UNAME_PROCESSOR=powerpc ;;
+ esac
+ if command -v xcode-select > /dev/null 2> /dev/null && \
+ ! xcode-select --print-path > /dev/null 2> /dev/null ; then
+ # Avoid executing cc if there is no toolchain installed as
+ # cc will be a stub that puts up a graphical alert
+ # prompting the user to install developer tools.
+ CC_FOR_BUILD=no_compiler_found
+ else
+ set_cc_for_build
+ fi
+ if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
+ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ case $UNAME_PROCESSOR in
+ i386) UNAME_PROCESSOR=x86_64 ;;
+ powerpc) UNAME_PROCESSOR=powerpc64 ;;
+ esac
+ fi
+ # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc
+ if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_PPC >/dev/null
+ then
+ UNAME_PROCESSOR=powerpc
+ fi
+ elif test "$UNAME_PROCESSOR" = i386 ; then
+ # uname -m returns i386 or x86_64
+ UNAME_PROCESSOR=$UNAME_MACHINE
+ fi
+ echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE"
+ exit ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = x86; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE"
+ exit ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit ;;
+ NEO-*:NONSTOP_KERNEL:*:*)
+ echo neo-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSE-*:NONSTOP_KERNEL:*:*)
+ echo nse-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSR-*:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSV-*:NONSTOP_KERNEL:*:*)
+ echo nsv-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSX-*:NONSTOP_KERNEL:*:*)
+ echo nsx-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit ;;
+ DS/*:UNIX_System_V:*:*)
+ echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE"
+ exit ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ # shellcheck disable=SC2154
+ if test "$cputype" = 386; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo "$UNAME_MACHINE"-unknown-plan9
+ exit ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux"$UNAME_RELEASE"
+ exit ;;
+ *:DragonFly:*:*)
+ echo "$UNAME_MACHINE"-unknown-dragonfly"`echo "$UNAME_RELEASE"|sed -e 's/[-(].*//'`"
+ exit ;;
+ *:*VMS:*:*)
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ case "$UNAME_MACHINE" in
+ A*) echo alpha-dec-vms ; exit ;;
+ I*) echo ia64-dec-vms ; exit ;;
+ V*) echo vax-dec-vms ; exit ;;
+ esac ;;
+ *:XENIX:*:SysV)
+ echo i386-pc-xenix
+ exit ;;
+ i*86:skyos:*:*)
+ echo "$UNAME_MACHINE"-pc-skyos"`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`"
+ exit ;;
+ i*86:rdos:*:*)
+ echo "$UNAME_MACHINE"-pc-rdos
+ exit ;;
+ i*86:AROS:*:*)
+ echo "$UNAME_MACHINE"-pc-aros
+ exit ;;
+ x86_64:VMkernel:*:*)
+ echo "$UNAME_MACHINE"-unknown-esx
+ exit ;;
+ amd64:Isilon\ OneFS:*:*)
+ echo x86_64-unknown-onefs
+ exit ;;
+ *:Unleashed:*:*)
+ echo "$UNAME_MACHINE"-unknown-unleashed"$UNAME_RELEASE"
+ exit ;;
+esac
+
+# No uname command or uname output not recognized.
+set_cc_for_build
+cat > "$dummy.c" <<EOF
+#ifdef _SEQUENT_
+#include <sys/types.h>
+#include <sys/utsname.h>
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#include <signal.h>
+#if defined(_SIZE_T_) || defined(SIGLOST)
+#include <sys/utsname.h>
+#endif
+#endif
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+#endif
+
+#if defined (vax)
+#if !defined (ultrix)
+#include <sys/param.h>
+#if defined (BSD)
+#if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+#else
+#if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#else
+ printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#endif
+#else
+ printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#else
+#if defined(_SIZE_T_) || defined(SIGLOST)
+ struct utsname un;
+ uname (&un);
+ printf ("vax-dec-ultrix%s\n", un.release); exit (0);
+#else
+ printf ("vax-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#if defined(_SIZE_T_) || defined(SIGLOST)
+ struct utsname *un;
+ uname (&un);
+ printf ("mips-dec-ultrix%s\n", un.release); exit (0);
+#else
+ printf ("mips-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`$dummy` &&
+ { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; }
+
+echo "$0: unable to guess system type" >&2
+
+case "$UNAME_MACHINE:$UNAME_SYSTEM" in
+ mips:Linux | mips64:Linux)
+ # If we got here on MIPS GNU/Linux, output extra information.
+ cat >&2 <<EOF
+
+NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize
+the system type. Please install a C compiler and try again.
+EOF
+ ;;
+esac
+
+cat >&2 <<EOF
+
+This script (version $timestamp), has failed to recognize the
+operating system you are using. If your script is old, overwrite *all*
+copies of config.guess and config.sub with the latest versions from:
+
+ https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
+and
+ https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
+
+If $0 has already been updated, send the following data and any
+information you think might be pertinent to config-patches@gnu.org to
+provide the necessary information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = "$UNAME_MACHINE"
+UNAME_RELEASE = "$UNAME_RELEASE"
+UNAME_SYSTEM = "$UNAME_SYSTEM"
+UNAME_VERSION = "$UNAME_VERSION"
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/contrib/ldapc++/config.sub b/contrib/ldapc++/config.sub
new file mode 100755
index 0000000..f02d43a
--- /dev/null
+++ b/contrib/ldapc++/config.sub
@@ -0,0 +1,1793 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright 1992-2020 Free Software Foundation, Inc.
+
+timestamp='2020-01-01'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+
+
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
+
+Canonicalize a configuration name.
+
+Options:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright 1992-2020 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo "$1"
+ exit ;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Split fields of configuration type
+# shellcheck disable=SC2162
+IFS="-" read field1 field2 field3 field4 <<EOF
+$1
+EOF
+
+# Separate into logical components for further validation
+case $1 in
+ *-*-*-*-*)
+ echo Invalid configuration \`"$1"\': more than four components >&2
+ exit 1
+ ;;
+ *-*-*-*)
+ basic_machine=$field1-$field2
+ os=$field3-$field4
+ ;;
+ *-*-*)
+ # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two
+ # parts
+ maybe_os=$field2-$field3
+ case $maybe_os in
+ nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc \
+ | linux-newlib* | linux-musl* | linux-uclibc* | uclinux-uclibc* \
+ | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \
+ | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \
+ | storm-chaos* | os2-emx* | rtmk-nova*)
+ basic_machine=$field1
+ os=$maybe_os
+ ;;
+ android-linux)
+ basic_machine=$field1-unknown
+ os=linux-android
+ ;;
+ *)
+ basic_machine=$field1-$field2
+ os=$field3
+ ;;
+ esac
+ ;;
+ *-*)
+ # A lone config we happen to match not fitting any pattern
+ case $field1-$field2 in
+ decstation-3100)
+ basic_machine=mips-dec
+ os=
+ ;;
+ *-*)
+ # Second component is usually, but not always the OS
+ case $field2 in
+ # Prevent following clause from handling this valid os
+ sun*os*)
+ basic_machine=$field1
+ os=$field2
+ ;;
+ # Manufacturers
+ dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \
+ | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \
+ | unicom* | ibm* | next | hp | isi* | apollo | altos* \
+ | convergent* | ncr* | news | 32* | 3600* | 3100* \
+ | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \
+ | ultra | tti* | harris | dolphin | highlevel | gould \
+ | cbm | ns | masscomp | apple | axis | knuth | cray \
+ | microblaze* | sim | cisco \
+ | oki | wec | wrs | winbond)
+ basic_machine=$field1-$field2
+ os=
+ ;;
+ *)
+ basic_machine=$field1
+ os=$field2
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ *)
+ # Convert single-component short-hands not valid as part of
+ # multi-component configurations.
+ case $field1 in
+ 386bsd)
+ basic_machine=i386-pc
+ os=bsd
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ os=udi
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ os=scout
+ ;;
+ alliant)
+ basic_machine=fx80-alliant
+ os=
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ os=
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ os=bsd
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ os=sysv
+ ;;
+ amiga)
+ basic_machine=m68k-unknown
+ os=
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ os=amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ os=sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ os=sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ os=bsd
+ ;;
+ aros)
+ basic_machine=i386-pc
+ os=aros
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ os=aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ os=dynix
+ ;;
+ blackfin)
+ basic_machine=bfin-unknown
+ os=linux
+ ;;
+ cegcc)
+ basic_machine=arm-unknown
+ os=cegcc
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ os=bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ os=bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ os=bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ os=bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ os=bsd
+ ;;
+ cray)
+ basic_machine=j90-cray
+ os=unicos
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ os=
+ ;;
+ da30)
+ basic_machine=m68k-da30
+ os=
+ ;;
+ decstation | pmax | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ os=
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ os=sysv3
+ ;;
+ dicos)
+ basic_machine=i686-pc
+ os=dicos
+ ;;
+ djgpp)
+ basic_machine=i586-pc
+ os=msdosdjgpp
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ os=ebmon
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ os=ose
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ os=sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ os=go32
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ os=hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ os=xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ os=hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ os=sysv3
+ ;;
+ hp300 | hp300hpux)
+ basic_machine=m68k-hp
+ os=hpux
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ os=bsd
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ os=osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ os=proelf
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ os=mach
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ os=sysv
+ ;;
+ m68knommu)
+ basic_machine=m68k-unknown
+ os=linux
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ os=sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ os=sysv
+ ;;
+ mingw64)
+ basic_machine=x86_64-pc
+ os=mingw64
+ ;;
+ mingw32)
+ basic_machine=i686-pc
+ os=mingw32
+ ;;
+ mingw32ce)
+ basic_machine=arm-unknown
+ os=mingw32ce
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ os=coff
+ ;;
+ morphos)
+ basic_machine=powerpc-unknown
+ os=morphos
+ ;;
+ moxiebox)
+ basic_machine=moxie-unknown
+ os=moxiebox
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ os=msdos
+ ;;
+ msys)
+ basic_machine=i686-pc
+ os=msys
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ os=mvs
+ ;;
+ nacl)
+ basic_machine=le32-unknown
+ os=nacl
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ os=sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-pc
+ os=netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ os=linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ os=newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ os=newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ os=sysv
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ os=cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ os=cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ os=nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ os=mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ os=nonstopux
+ ;;
+ os400)
+ basic_machine=powerpc-ibm
+ os=os400
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ os=ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ os=os68k
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ os=osf
+ ;;
+ parisc)
+ basic_machine=hppa-unknown
+ os=linux
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ os=pw32
+ ;;
+ rdos | rdos64)
+ basic_machine=x86_64-pc
+ os=rdos
+ ;;
+ rdos32)
+ basic_machine=i386-pc
+ os=rdos
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ os=coff
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ os=udi
+ ;;
+ sei)
+ basic_machine=mips-sei
+ os=seiux
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ os=
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ os=sysv2
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ os=
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ os=sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ os=
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ os=sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ os=sunos4
+ ;;
+ sun3)
+ basic_machine=m68k-sun
+ os=
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ os=sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ os=sunos4
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ os=
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ os=sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ os=sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ os=solaris2
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ os=
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ os=unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ os=dynix
+ ;;
+ t3e)
+ basic_machine=alphaev5-cray
+ os=unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
+ os=unicos
+ ;;
+ toad1)
+ basic_machine=pdp10-xkl
+ os=tops20
+ ;;
+ tpf)
+ basic_machine=s390x-ibm
+ os=tpf
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ os=udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ os=sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ os=none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ os=sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ os=vms
+ ;;
+ vsta)
+ basic_machine=i386-pc
+ os=vsta
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ os=vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ os=vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ os=vxworks
+ ;;
+ xbox)
+ basic_machine=i686-pc
+ os=mingw32
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ os=unicos
+ ;;
+ *)
+ basic_machine=$1
+ os=
+ ;;
+ esac
+ ;;
+esac
+
+# Decode 1-component or ad-hoc basic machines
+case $basic_machine in
+ # Here we handle the default manufacturer of certain CPU types. It is in
+ # some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ cpu=hppa1.1
+ vendor=winbond
+ ;;
+ op50n)
+ cpu=hppa1.1
+ vendor=oki
+ ;;
+ op60c)
+ cpu=hppa1.1
+ vendor=oki
+ ;;
+ ibm*)
+ cpu=i370
+ vendor=ibm
+ ;;
+ orion105)
+ cpu=clipper
+ vendor=highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ cpu=m68k
+ vendor=apple
+ ;;
+ pmac | pmac-mpw)
+ cpu=powerpc
+ vendor=apple
+ ;;
+
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ cpu=m68000
+ vendor=att
+ ;;
+ 3b*)
+ cpu=we32k
+ vendor=att
+ ;;
+ bluegene*)
+ cpu=powerpc
+ vendor=ibm
+ os=cnk
+ ;;
+ decsystem10* | dec10*)
+ cpu=pdp10
+ vendor=dec
+ os=tops10
+ ;;
+ decsystem20* | dec20*)
+ cpu=pdp10
+ vendor=dec
+ os=tops20
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ cpu=m68k
+ vendor=motorola
+ ;;
+ dpx2*)
+ cpu=m68k
+ vendor=bull
+ os=sysv3
+ ;;
+ encore | umax | mmax)
+ cpu=ns32k
+ vendor=encore
+ ;;
+ elxsi)
+ cpu=elxsi
+ vendor=elxsi
+ os=${os:-bsd}
+ ;;
+ fx2800)
+ cpu=i860
+ vendor=alliant
+ ;;
+ genix)
+ cpu=ns32k
+ vendor=ns
+ ;;
+ h3050r* | hiux*)
+ cpu=hppa1.1
+ vendor=hitachi
+ os=hiuxwe2
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ cpu=hppa1.0
+ vendor=hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ cpu=m68000
+ vendor=hp
+ ;;
+ hp9k3[2-9][0-9])
+ cpu=m68k
+ vendor=hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ cpu=hppa1.0
+ vendor=hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ cpu=hppa1.1
+ vendor=hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ cpu=hppa1.1
+ vendor=hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ cpu=hppa1.1
+ vendor=hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ cpu=hppa1.1
+ vendor=hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ cpu=hppa1.0
+ vendor=hp
+ ;;
+ i*86v32)
+ cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ vendor=pc
+ os=sysv32
+ ;;
+ i*86v4*)
+ cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ vendor=pc
+ os=sysv4
+ ;;
+ i*86v)
+ cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ vendor=pc
+ os=sysv
+ ;;
+ i*86sol2)
+ cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ vendor=pc
+ os=solaris2
+ ;;
+ j90 | j90-cray)
+ cpu=j90
+ vendor=cray
+ os=${os:-unicos}
+ ;;
+ iris | iris4d)
+ cpu=mips
+ vendor=sgi
+ case $os in
+ irix*)
+ ;;
+ *)
+ os=irix4
+ ;;
+ esac
+ ;;
+ miniframe)
+ cpu=m68000
+ vendor=convergent
+ ;;
+ *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ cpu=m68k
+ vendor=atari
+ os=mint
+ ;;
+ news-3600 | risc-news)
+ cpu=mips
+ vendor=sony
+ os=newsos
+ ;;
+ next | m*-next)
+ cpu=m68k
+ vendor=next
+ case $os in
+ openstep*)
+ ;;
+ nextstep*)
+ ;;
+ ns2*)
+ os=nextstep2
+ ;;
+ *)
+ os=nextstep3
+ ;;
+ esac
+ ;;
+ np1)
+ cpu=np1
+ vendor=gould
+ ;;
+ op50n-* | op60c-*)
+ cpu=hppa1.1
+ vendor=oki
+ os=proelf
+ ;;
+ pa-hitachi)
+ cpu=hppa1.1
+ vendor=hitachi
+ os=hiuxwe2
+ ;;
+ pbd)
+ cpu=sparc
+ vendor=tti
+ ;;
+ pbb)
+ cpu=m68k
+ vendor=tti
+ ;;
+ pc532)
+ cpu=ns32k
+ vendor=pc532
+ ;;
+ pn)
+ cpu=pn
+ vendor=gould
+ ;;
+ power)
+ cpu=power
+ vendor=ibm
+ ;;
+ ps2)
+ cpu=i386
+ vendor=ibm
+ ;;
+ rm[46]00)
+ cpu=mips
+ vendor=siemens
+ ;;
+ rtpc | rtpc-*)
+ cpu=romp
+ vendor=ibm
+ ;;
+ sde)
+ cpu=mipsisa32
+ vendor=sde
+ os=${os:-elf}
+ ;;
+ simso-wrs)
+ cpu=sparclite
+ vendor=wrs
+ os=vxworks
+ ;;
+ tower | tower-32)
+ cpu=m68k
+ vendor=ncr
+ ;;
+ vpp*|vx|vx-*)
+ cpu=f301
+ vendor=fujitsu
+ ;;
+ w65)
+ cpu=w65
+ vendor=wdc
+ ;;
+ w89k-*)
+ cpu=hppa1.1
+ vendor=winbond
+ os=proelf
+ ;;
+ none)
+ cpu=none
+ vendor=none
+ ;;
+ leon|leon[3-9])
+ cpu=sparc
+ vendor=$basic_machine
+ ;;
+ leon-*|leon[3-9]-*)
+ cpu=sparc
+ vendor=`echo "$basic_machine" | sed 's/-.*//'`
+ ;;
+
+ *-*)
+ # shellcheck disable=SC2162
+ IFS="-" read cpu vendor <<EOF
+$basic_machine
+EOF
+ ;;
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ cpu=$basic_machine
+ vendor=pc
+ ;;
+ # These rules are duplicated from below for sake of the special case above;
+ # i.e. things that normalized to x86 arches should also default to "pc"
+ pc98)
+ cpu=i386
+ vendor=pc
+ ;;
+ x64 | amd64)
+ cpu=x86_64
+ vendor=pc
+ ;;
+ # Recognize the basic CPU types without company name.
+ *)
+ cpu=$basic_machine
+ vendor=unknown
+ ;;
+esac
+
+unset -v basic_machine
+
+# Decode basic machines in the full and proper CPU-Company form.
+case $cpu-$vendor in
+ # Here we handle the default manufacturer of certain CPU types in canonical form. It is in
+ # some cases the only manufacturer, in others, it is the most popular.
+ craynv-unknown)
+ vendor=cray
+ os=${os:-unicosmp}
+ ;;
+ c90-unknown | c90-cray)
+ vendor=cray
+ os=${os:-unicos}
+ ;;
+ fx80-unknown)
+ vendor=alliant
+ ;;
+ romp-unknown)
+ vendor=ibm
+ ;;
+ mmix-unknown)
+ vendor=knuth
+ ;;
+ microblaze-unknown | microblazeel-unknown)
+ vendor=xilinx
+ ;;
+ rs6000-unknown)
+ vendor=ibm
+ ;;
+ vax-unknown)
+ vendor=dec
+ ;;
+ pdp11-unknown)
+ vendor=dec
+ ;;
+ we32k-unknown)
+ vendor=att
+ ;;
+ cydra-unknown)
+ vendor=cydrome
+ ;;
+ i370-ibm*)
+ vendor=ibm
+ ;;
+ orion-unknown)
+ vendor=highlevel
+ ;;
+ xps-unknown | xps100-unknown)
+ cpu=xps100
+ vendor=honeywell
+ ;;
+
+ # Here we normalize CPU types with a missing or matching vendor
+ dpx20-unknown | dpx20-bull)
+ cpu=rs6000
+ vendor=bull
+ os=${os:-bosx}
+ ;;
+
+ # Here we normalize CPU types irrespective of the vendor
+ amd64-*)
+ cpu=x86_64
+ ;;
+ blackfin-*)
+ cpu=bfin
+ os=linux
+ ;;
+ c54x-*)
+ cpu=tic54x
+ ;;
+ c55x-*)
+ cpu=tic55x
+ ;;
+ c6x-*)
+ cpu=tic6x
+ ;;
+ e500v[12]-*)
+ cpu=powerpc
+ os=$os"spe"
+ ;;
+ mips3*-*)
+ cpu=mips64
+ ;;
+ ms1-*)
+ cpu=mt
+ ;;
+ m68knommu-*)
+ cpu=m68k
+ os=linux
+ ;;
+ m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*)
+ cpu=s12z
+ ;;
+ openrisc-*)
+ cpu=or32
+ ;;
+ parisc-*)
+ cpu=hppa
+ os=linux
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ cpu=i586
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-* | athalon_*-*)
+ cpu=i686
+ ;;
+ pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+ cpu=i686
+ ;;
+ pentium4-*)
+ cpu=i786
+ ;;
+ pc98-*)
+ cpu=i386
+ ;;
+ ppc-* | ppcbe-*)
+ cpu=powerpc
+ ;;
+ ppcle-* | powerpclittle-*)
+ cpu=powerpcle
+ ;;
+ ppc64-*)
+ cpu=powerpc64
+ ;;
+ ppc64le-* | powerpc64little-*)
+ cpu=powerpc64le
+ ;;
+ sb1-*)
+ cpu=mipsisa64sb1
+ ;;
+ sb1el-*)
+ cpu=mipsisa64sb1el
+ ;;
+ sh5e[lb]-*)
+ cpu=`echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/'`
+ ;;
+ spur-*)
+ cpu=spur
+ ;;
+ strongarm-* | thumb-*)
+ cpu=arm
+ ;;
+ tx39-*)
+ cpu=mipstx39
+ ;;
+ tx39el-*)
+ cpu=mipstx39el
+ ;;
+ x64-*)
+ cpu=x86_64
+ ;;
+ xscale-* | xscalee[bl]-*)
+ cpu=`echo "$cpu" | sed 's/^xscale/arm/'`
+ ;;
+
+ # Recognize the canonical CPU Types that limit and/or modify the
+ # company names they are paired with.
+ cr16-*)
+ os=${os:-elf}
+ ;;
+ crisv32-* | etraxfs*-*)
+ cpu=crisv32
+ vendor=axis
+ ;;
+ cris-* | etrax*-*)
+ cpu=cris
+ vendor=axis
+ ;;
+ crx-*)
+ os=${os:-elf}
+ ;;
+ neo-tandem)
+ cpu=neo
+ vendor=tandem
+ ;;
+ nse-tandem)
+ cpu=nse
+ vendor=tandem
+ ;;
+ nsr-tandem)
+ cpu=nsr
+ vendor=tandem
+ ;;
+ nsv-tandem)
+ cpu=nsv
+ vendor=tandem
+ ;;
+ nsx-tandem)
+ cpu=nsx
+ vendor=tandem
+ ;;
+ s390-*)
+ cpu=s390
+ vendor=ibm
+ ;;
+ s390x-*)
+ cpu=s390x
+ vendor=ibm
+ ;;
+ tile*-*)
+ os=${os:-linux-gnu}
+ ;;
+
+ *)
+ # Recognize the canonical CPU types that are allowed with any
+ # company name.
+ case $cpu in
+ 1750a | 580 \
+ | a29k \
+ | aarch64 | aarch64_be \
+ | abacus \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \
+ | alphapca5[67] | alpha64pca5[67] \
+ | am33_2.0 \
+ | amdgcn \
+ | arc | arceb \
+ | arm | arm[lb]e | arme[lb] | armv* \
+ | avr | avr32 \
+ | asmjs \
+ | ba \
+ | be32 | be64 \
+ | bfin | bpf | bs2000 \
+ | c[123]* | c30 | [cjt]90 | c4x \
+ | c8051 | clipper | craynv | csky | cydra \
+ | d10v | d30v | dlx | dsp16xx \
+ | e2k | elxsi | epiphany \
+ | f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \
+ | h8300 | h8500 \
+ | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | hexagon \
+ | i370 | i*86 | i860 | i960 | ia16 | ia64 \
+ | ip2k | iq2000 \
+ | k1om \
+ | le32 | le64 \
+ | lm32 \
+ | m32c | m32r | m32rle \
+ | m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \
+ | m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \
+ | m88110 | m88k | maxq | mb | mcore | mep | metag \
+ | microblaze | microblazeel \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64eb | mips64el \
+ | mips64octeon | mips64octeonel \
+ | mips64orion | mips64orionel \
+ | mips64r5900 | mips64r5900el \
+ | mips64vr | mips64vrel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mips64vr5900 | mips64vr5900el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa32r6 | mipsisa32r6el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64r6 | mipsisa64r6el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipsr5900 | mipsr5900el \
+ | mipstx39 | mipstx39el \
+ | mmix \
+ | mn10200 | mn10300 \
+ | moxie \
+ | mt \
+ | msp430 \
+ | nds32 | nds32le | nds32be \
+ | nfp \
+ | nios | nios2 | nios2eb | nios2el \
+ | none | np1 | ns16k | ns32k | nvptx \
+ | open8 \
+ | or1k* \
+ | or32 \
+ | orion \
+ | picochip \
+ | pdp10 | pdp11 | pj | pjl | pn | power \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \
+ | pru \
+ | pyramid \
+ | riscv | riscv32 | riscv64 \
+ | rl78 | romp | rs6000 | rx \
+ | score \
+ | sh | shl \
+ | sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \
+ | sh[1234]e[lb] | sh[12345][lb]e | sh[23]ele | sh64 | sh64le \
+ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet \
+ | sparclite \
+ | sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \
+ | spu \
+ | tahoe \
+ | tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \
+ | tron \
+ | ubicom32 \
+ | v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \
+ | vax \
+ | visium \
+ | w65 \
+ | wasm32 | wasm64 \
+ | we32k \
+ | x86 | x86_64 | xc16x | xgate | xps100 \
+ | xstormy16 | xtensa* \
+ | ymp \
+ | z8k | z80)
+ ;;
+
+ *)
+ echo Invalid configuration \`"$1"\': machine \`"$cpu-$vendor"\' not recognized 1>&2
+ exit 1
+ ;;
+ esac
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $vendor in
+ digital*)
+ vendor=dec
+ ;;
+ commodore*)
+ vendor=cbm
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x$os != x ]
+then
+case $os in
+ # First match some system type aliases that might get confused
+ # with valid system types.
+ # solaris* is a basic system type, with this one exception.
+ auroraux)
+ os=auroraux
+ ;;
+ bluegene*)
+ os=cnk
+ ;;
+ solaris1 | solaris1.*)
+ os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ ;;
+ solaris)
+ os=solaris2
+ ;;
+ unixware*)
+ os=sysv4.2uw
+ ;;
+ gnu/linux*)
+ os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ ;;
+ # es1800 is here to avoid being matched by es* (a different OS)
+ es1800*)
+ os=ose
+ ;;
+ # Some version numbers need modification
+ chorusos*)
+ os=chorusos
+ ;;
+ isc)
+ os=isc2.2
+ ;;
+ sco6)
+ os=sco5v6
+ ;;
+ sco5)
+ os=sco3.2v5
+ ;;
+ sco4)
+ os=sco3.2v4
+ ;;
+ sco3.2.[4-9]*)
+ os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+ ;;
+ sco3.2v[4-9]* | sco5v6*)
+ # Don't forget version if it is 3.2v4 or newer.
+ ;;
+ scout)
+ # Don't match below
+ ;;
+ sco*)
+ os=sco3.2v2
+ ;;
+ psos*)
+ os=psos
+ ;;
+ # Now accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST end in a * to match a version number.
+ # sysv* is not here because it comes later, after sysvr4.
+ gnu* | bsd* | mach* | minix* | genix* | ultrix* | irix* \
+ | *vms* | esix* | aix* | cnk* | sunos | sunos[34]*\
+ | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \
+ | sym* | kopensolaris* | plan9* \
+ | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \
+ | aos* | aros* | cloudabi* | sortix* | twizzler* \
+ | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \
+ | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \
+ | knetbsd* | mirbsd* | netbsd* \
+ | bitrig* | openbsd* | solidbsd* | libertybsd* | os108* \
+ | ekkobsd* | kfreebsd* | freebsd* | riscix* | lynxos* \
+ | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \
+ | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \
+ | udi* | eabi* | lites* | ieee* | go32* | aux* | hcos* \
+ | chorusrdb* | cegcc* | glidix* \
+ | cygwin* | msys* | pe* | moss* | proelf* | rtems* \
+ | midipix* | mingw32* | mingw64* | linux-gnu* | linux-android* \
+ | linux-newlib* | linux-musl* | linux-uclibc* \
+ | uxpv* | beos* | mpeix* | udk* | moxiebox* \
+ | interix* | uwin* | mks* | rhapsody* | darwin* \
+ | openstep* | oskit* | conix* | pw32* | nonstopux* \
+ | storm-chaos* | tops10* | tenex* | tops20* | its* \
+ | os2* | vos* | palmos* | uclinux* | nucleus* \
+ | morphos* | superux* | rtmk* | windiss* \
+ | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \
+ | skyos* | haiku* | rdos* | toppers* | drops* | es* \
+ | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
+ | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
+ | nsk* | powerunix)
+ # Remember, each alternative MUST END IN *, to match a version number.
+ ;;
+ qnx*)
+ case $cpu in
+ x86 | i*86)
+ ;;
+ *)
+ os=nto-$os
+ ;;
+ esac
+ ;;
+ hiux*)
+ os=hiuxwe2
+ ;;
+ nto-qnx*)
+ ;;
+ nto*)
+ os=`echo $os | sed -e 's|nto|nto-qnx|'`
+ ;;
+ sim | xray | os68k* | v88r* \
+ | windows* | osx | abug | netware* | os9* \
+ | macos* | mpw* | magic* | mmixware* | mon960* | lnews*)
+ ;;
+ linux-dietlibc)
+ os=linux-dietlibc
+ ;;
+ linux*)
+ os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ ;;
+ lynx*178)
+ os=lynxos178
+ ;;
+ lynx*5)
+ os=lynxos5
+ ;;
+ lynx*)
+ os=lynxos
+ ;;
+ mac*)
+ os=`echo "$os" | sed -e 's|mac|macos|'`
+ ;;
+ opened*)
+ os=openedition
+ ;;
+ os400*)
+ os=os400
+ ;;
+ sunos5*)
+ os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
+ ;;
+ sunos6*)
+ os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
+ ;;
+ wince*)
+ os=wince
+ ;;
+ utek*)
+ os=bsd
+ ;;
+ dynix*)
+ os=bsd
+ ;;
+ acis*)
+ os=aos
+ ;;
+ atheos*)
+ os=atheos
+ ;;
+ syllable*)
+ os=syllable
+ ;;
+ 386bsd)
+ os=bsd
+ ;;
+ ctix* | uts*)
+ os=sysv
+ ;;
+ nova*)
+ os=rtmk-nova
+ ;;
+ ns2)
+ os=nextstep2
+ ;;
+ # Preserve the version number of sinix5.
+ sinix5.*)
+ os=`echo $os | sed -e 's|sinix|sysv|'`
+ ;;
+ sinix*)
+ os=sysv4
+ ;;
+ tpf*)
+ os=tpf
+ ;;
+ triton*)
+ os=sysv3
+ ;;
+ oss*)
+ os=sysv3
+ ;;
+ svr4*)
+ os=sysv4
+ ;;
+ svr3)
+ os=sysv3
+ ;;
+ sysvr4)
+ os=sysv4
+ ;;
+ # This must come after sysvr4.
+ sysv*)
+ ;;
+ ose*)
+ os=ose
+ ;;
+ *mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
+ os=mint
+ ;;
+ zvmoe)
+ os=zvmoe
+ ;;
+ dicos*)
+ os=dicos
+ ;;
+ pikeos*)
+ # Until real need of OS specific support for
+ # particular features comes up, bare metal
+ # configurations are quite functional.
+ case $cpu in
+ arm*)
+ os=eabi
+ ;;
+ *)
+ os=elf
+ ;;
+ esac
+ ;;
+ nacl*)
+ ;;
+ ios)
+ ;;
+ none)
+ ;;
+ *-eabi)
+ ;;
+ *)
+ echo Invalid configuration \`"$1"\': system \`"$os"\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $cpu-$vendor in
+ score-*)
+ os=elf
+ ;;
+ spu-*)
+ os=elf
+ ;;
+ *-acorn)
+ os=riscix1.2
+ ;;
+ arm*-rebel)
+ os=linux
+ ;;
+ arm*-semi)
+ os=aout
+ ;;
+ c4x-* | tic4x-*)
+ os=coff
+ ;;
+ c8051-*)
+ os=elf
+ ;;
+ clipper-intergraph)
+ os=clix
+ ;;
+ hexagon-*)
+ os=elf
+ ;;
+ tic54x-*)
+ os=coff
+ ;;
+ tic55x-*)
+ os=coff
+ ;;
+ tic6x-*)
+ os=coff
+ ;;
+ # This must come before the *-dec entry.
+ pdp10-*)
+ os=tops20
+ ;;
+ pdp11-*)
+ os=none
+ ;;
+ *-dec | vax-*)
+ os=ultrix4.2
+ ;;
+ m68*-apollo)
+ os=domain
+ ;;
+ i386-sun)
+ os=sunos4.0.2
+ ;;
+ m68000-sun)
+ os=sunos3
+ ;;
+ m68*-cisco)
+ os=aout
+ ;;
+ mep-*)
+ os=elf
+ ;;
+ mips*-cisco)
+ os=elf
+ ;;
+ mips*-*)
+ os=elf
+ ;;
+ or32-*)
+ os=coff
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=sysv3
+ ;;
+ sparc-* | *-sun)
+ os=sunos4.1.1
+ ;;
+ pru-*)
+ os=elf
+ ;;
+ *-be)
+ os=beos
+ ;;
+ *-ibm)
+ os=aix
+ ;;
+ *-knuth)
+ os=mmixware
+ ;;
+ *-wec)
+ os=proelf
+ ;;
+ *-winbond)
+ os=proelf
+ ;;
+ *-oki)
+ os=proelf
+ ;;
+ *-hp)
+ os=hpux
+ ;;
+ *-hitachi)
+ os=hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=sysv
+ ;;
+ *-cbm)
+ os=amigaos
+ ;;
+ *-dg)
+ os=dgux
+ ;;
+ *-dolphin)
+ os=sysv3
+ ;;
+ m68k-ccur)
+ os=rtu
+ ;;
+ m88k-omron*)
+ os=luna
+ ;;
+ *-next)
+ os=nextstep
+ ;;
+ *-sequent)
+ os=ptx
+ ;;
+ *-crds)
+ os=unos
+ ;;
+ *-ns)
+ os=genix
+ ;;
+ i370-*)
+ os=mvs
+ ;;
+ *-gould)
+ os=sysv
+ ;;
+ *-highlevel)
+ os=bsd
+ ;;
+ *-encore)
+ os=bsd
+ ;;
+ *-sgi)
+ os=irix
+ ;;
+ *-siemens)
+ os=sysv4
+ ;;
+ *-masscomp)
+ os=rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=uxpv
+ ;;
+ *-rom68k)
+ os=coff
+ ;;
+ *-*bug)
+ os=coff
+ ;;
+ *-apple)
+ os=macos
+ ;;
+ *-atari*)
+ os=mint
+ ;;
+ *-wrs)
+ os=vxworks
+ ;;
+ *)
+ os=none
+ ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+case $vendor in
+ unknown)
+ case $os in
+ riscix*)
+ vendor=acorn
+ ;;
+ sunos*)
+ vendor=sun
+ ;;
+ cnk*|-aix*)
+ vendor=ibm
+ ;;
+ beos*)
+ vendor=be
+ ;;
+ hpux*)
+ vendor=hp
+ ;;
+ mpeix*)
+ vendor=hp
+ ;;
+ hiux*)
+ vendor=hitachi
+ ;;
+ unos*)
+ vendor=crds
+ ;;
+ dgux*)
+ vendor=dg
+ ;;
+ luna*)
+ vendor=omron
+ ;;
+ genix*)
+ vendor=ns
+ ;;
+ clix*)
+ vendor=intergraph
+ ;;
+ mvs* | opened*)
+ vendor=ibm
+ ;;
+ os400*)
+ vendor=ibm
+ ;;
+ ptx*)
+ vendor=sequent
+ ;;
+ tpf*)
+ vendor=ibm
+ ;;
+ vxsim* | vxworks* | windiss*)
+ vendor=wrs
+ ;;
+ aux*)
+ vendor=apple
+ ;;
+ hms*)
+ vendor=hitachi
+ ;;
+ mpw* | macos*)
+ vendor=apple
+ ;;
+ *mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
+ vendor=atari
+ ;;
+ vos*)
+ vendor=stratus
+ ;;
+ esac
+ ;;
+esac
+
+echo "$cpu-$vendor-$os"
+exit
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/contrib/ldapc++/configure b/contrib/ldapc++/configure
new file mode 100755
index 0000000..8cca14d
--- /dev/null
+++ b/contrib/ldapc++/configure
@@ -0,0 +1,18656 @@
+#! /bin/sh
+# From configure.ac Id: 9e53b7243116521c824456d5fb2d4d3c358c0d76 .
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.69 for ldapcpplib .
+#
+# Report bugs to <http://www.openldap.org/its/ >.
+#
+# Copyright 2000-2022 The OpenLDAP Foundation. All rights reserved.
+# Restrictions apply, see COPYRIGHT and LICENSE files.
+#
+#
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in #(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Use a proper internal environment variable to ensure we don't fall
+ # into an infinite loop, continuously re-executing ourselves.
+ if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+ _as_can_reexec=no; export _as_can_reexec;
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+ fi
+ # We don't want this to propagate to other subprocesses.
+ { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+ as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '\${1+\"\$@\"}'='\"\$@\"'
+ setopt NO_GLOB_SUBST
+else
+ case \`(set -o) 2>/dev/null\` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+"
+ as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+ exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
+ as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+ as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+ eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+ test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+
+ test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || (
+ ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+ ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO
+ ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO
+ PATH=/empty FPATH=/empty; export PATH FPATH
+ test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\
+ || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1"
+ if (eval "$as_required") 2>/dev/null; then :
+ as_have_required=yes
+else
+ as_have_required=no
+fi
+ if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ as_found=:
+ case $as_dir in #(
+ /*)
+ for as_base in sh bash ksh sh5; do
+ # Try only shells that exist, to save several forks.
+ as_shell=$as_dir/$as_base
+ if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ CONFIG_SHELL=$as_shell as_have_required=yes
+ if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ break 2
+fi
+fi
+ done;;
+ esac
+ as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+ CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+ if test "x$CONFIG_SHELL" != x; then :
+ export CONFIG_SHELL
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+ if test x$as_have_required = xno; then :
+ $as_echo "$0: This script requires a shell more modern than all"
+ $as_echo "$0: the shells that I found on your system."
+ if test x${ZSH_VERSION+set} = xset ; then
+ $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+ $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+ else
+ $as_echo "$0: Please tell bug-autoconf@gnu.org and
+$0: http://www.openldap.org/its/ about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+ fi
+ exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+ as_lineno_1=$LINENO as_lineno_1a=$LINENO
+ as_lineno_2=$LINENO as_lineno_2a=$LINENO
+ eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+ test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+ # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+ # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+ # already done that, so ensure we don't try to do so again and fall
+ # in an infinite loop. This has already happened in practice.
+ _as_can_reexec=no; export _as_can_reexec
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -pR'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -pR'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -pR'
+ fi
+else
+ as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='ldapcpplib'
+PACKAGE_TARNAME='ldapcpplib'
+PACKAGE_VERSION=' '
+PACKAGE_STRING='ldapcpplib '
+PACKAGE_BUGREPORT='http://www.openldap.org/its/ '
+PACKAGE_URL=''
+
+ac_unique_file="src/LDAPConnection.h"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif
+#ifdef HAVE_STRING_H
+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
+# include <memory.h>
+# endif
+# include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='am__EXEEXT_FALSE
+am__EXEEXT_TRUE
+LTLIBOBJS
+LIBOBJS
+CXXCPP
+CPP
+LT_SYS_LIBRARY_PATH
+OTOOL64
+OTOOL
+LIPO
+NMEDIT
+DSYMUTIL
+MANIFEST_TOOL
+RANLIB
+ac_ct_AR
+AR
+DLLTOOL
+OBJDUMP
+LN_S
+NM
+ac_ct_DUMPBIN
+DUMPBIN
+LD
+FGREP
+EGREP
+GREP
+SED
+am__fastdepCC_FALSE
+am__fastdepCC_TRUE
+CCDEPMODE
+ac_ct_CC
+CFLAGS
+CC
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+LIBTOOL
+am__fastdepCXX_FALSE
+am__fastdepCXX_TRUE
+CXXDEPMODE
+am__nodep
+AMDEPBACKSLASH
+AMDEP_FALSE
+AMDEP_TRUE
+am__quote
+am__include
+DEPDIR
+OBJEXT
+EXEEXT
+ac_ct_CXX
+CPPFLAGS
+LDFLAGS
+CXXFLAGS
+CXX
+OPENLDAP_CPP_API_VERSION
+AM_BACKSLASH
+AM_DEFAULT_VERBOSITY
+AM_DEFAULT_V
+AM_V
+am__untar
+am__tar
+AMTAR
+am__leading_dot
+SET_MAKE
+AWK
+mkdir_p
+MKDIR_P
+INSTALL_STRIP_PROGRAM
+STRIP
+install_sh
+MAKEINFO
+AUTOHEADER
+AUTOMAKE
+AUTOCONF
+ACLOCAL
+VERSION
+PACKAGE
+CYGPATH_W
+am__isrc
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+runstatedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_silent_rules
+enable_dependency_tracking
+enable_shared
+enable_static
+with_pic
+enable_fast_install
+with_aix_soname
+with_gnu_ld
+with_sysroot
+enable_libtool_lock
+enable_debug
+with_libldap
+with_ldap_includes
+'
+ ac_precious_vars='build_alias
+host_alias
+target_alias
+CXX
+CXXFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CCC
+CC
+CFLAGS
+LT_SYS_LIBRARY_PATH
+CPP
+CXXCPP'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval $ac_prev=\$ac_option
+ ac_prev=
+ continue
+ fi
+
+ case $ac_option in
+ *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+ *=) ac_optarg= ;;
+ *) ac_optarg=yes ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_dashdash$ac_option in
+ --)
+ ac_dashdash=yes ;;
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=*)
+ datadir=$ac_optarg ;;
+
+ -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+ | --dataroo | --dataro | --datar)
+ ac_prev=datarootdir ;;
+ -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+ datarootdir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=no ;;
+
+ -docdir | --docdir | --docdi | --doc | --do)
+ ac_prev=docdir ;;
+ -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+ docdir=$ac_optarg ;;
+
+ -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+ ac_prev=dvidir ;;
+ -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+ dvidir=$ac_optarg ;;
+
+ -enable-* | --enable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=\$ac_optarg ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+ ac_prev=htmldir ;;
+ -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+ | --ht=*)
+ htmldir=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localedir | --localedir | --localedi | --localed | --locale)
+ ac_prev=localedir ;;
+ -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+ localedir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst | --locals)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+ ac_prev=pdfdir ;;
+ -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+ pdfdir=$ac_optarg ;;
+
+ -psdir | --psdir | --psdi | --psd | --ps)
+ ac_prev=psdir ;;
+ -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+ psdir=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -runstatedir | --runstatedir | --runstatedi | --runstated \
+ | --runstate | --runstat | --runsta | --runst | --runs \
+ | --run | --ru | --r)
+ ac_prev=runstatedir ;;
+ -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+ | --run=* | --ru=* | --r=*)
+ runstatedir=$ac_optarg ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=\$ac_optarg ;;
+
+ -without-* | --without-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=no ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ case $ac_envvar in #(
+ '' | [0-9]* | *[!_$as_cr_alnum]* )
+ as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+ esac
+ eval $ac_envvar=\$ac_optarg
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+ case $enable_option_checking in
+ no) ;;
+ fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+ *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+ esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
+ datadir sysconfdir sharedstatedir localstatedir includedir \
+ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+ libdir localedir mandir runstatedir
+do
+ eval ac_val=\$$ac_var
+ # Remove trailing slashes.
+ case $ac_val in
+ */ )
+ ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+ eval $ac_var=\$ac_val;;
+ esac
+ # Be sure to have absolute directory names.
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) continue;;
+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+ esac
+ as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+ as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+ as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then the parent directory.
+ ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_myself" : 'X\(//\)[^/]' \| \
+ X"$as_myself" : 'X\(//\)$' \| \
+ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r "$srcdir/$ac_unique_file"; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+ as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+ cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+ pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+ srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+ eval ac_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_env_${ac_var}_value=\$${ac_var}
+ eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures ldapcpplib to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking ...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
+ --infodir=DIR info documentation [DATAROOTDIR/info]
+ --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
+ --mandir=DIR man documentation [DATAROOTDIR/man]
+ --docdir=DIR documentation root [DATAROOTDIR/doc/ldapcpplib]
+ --htmldir=DIR html documentation [DOCDIR]
+ --dvidir=DIR dvi documentation [DOCDIR]
+ --pdfdir=DIR pdf documentation [DOCDIR]
+ --psdir=DIR ps documentation [DOCDIR]
+_ACEOF
+
+ cat <<\_ACEOF
+
+Program names:
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM run sed PROGRAM on installed program names
+
+System types:
+ --build=BUILD configure for building on BUILD [guessed]
+ --host=HOST cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+ case $ac_init_help in
+ short | recursive ) echo "Configuration of ldapcpplib :";;
+ esac
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-option-checking ignore unrecognized --enable/--with options
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --enable-silent-rules less verbose build output (undo: "make V=1")
+ --disable-silent-rules verbose build output (undo: "make V=0")
+ --enable-dependency-tracking
+ do not reject slow dependency extractors
+ --disable-dependency-tracking
+ speeds up one-time build
+ --enable-shared[=PKGS] build shared libraries [default=yes]
+ --enable-static[=PKGS] build static libraries [default=yes]
+ --enable-fast-install[=PKGS]
+ optimize for fast installation [default=yes]
+ --disable-libtool-lock avoid locking (might break parallel builds)
+ --enable-debug
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use
+ both]
+ --with-aix-soname=aix|svr4|both
+ shared library versioning (aka "SONAME") variant to
+ provide on AIX, [default=aix].
+ --with-gnu-ld assume the C compiler uses GNU ld [default=no]
+ --with-sysroot[=DIR] Search for dependent libraries within DIR (or the
+ compiler's sysroot if not specified).
+ --with-libldap=DIR Path to the libldap library /usr/local/lib
+ --with-ldap-includes=DIR Path to the libldap include files /usr/local/include
+
+Some influential environment variables:
+ CXX C++ compiler command
+ CXXFLAGS C++ compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ LIBS libraries to pass to the linker, e.g. -l<library>
+ CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+ you have headers in a nonstandard directory <include dir>
+ CC C compiler command
+ CFLAGS C compiler flags
+ LT_SYS_LIBRARY_PATH
+ User-defined run-time library search path.
+ CPP C preprocessor
+ CXXCPP C++ preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <http://www.openldap.org/its/ >.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d "$ac_dir" ||
+ { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+ continue
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+ cd "$ac_dir" || { ac_status=$?; continue; }
+ # Check for guested configure.
+ if test -f "$ac_srcdir/configure.gnu"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+ elif test -f "$ac_srcdir/configure"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure" --help=recursive
+ else
+ $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi || ac_status=$?
+ cd "$ac_pwd" || { ac_status=$?; break; }
+ done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+ cat <<\_ACEOF
+ldapcpplib configure
+generated by GNU Autoconf 2.69
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+
+Copyright 2000-2022 The OpenLDAP Foundation. All rights reserved.
+Restrictions apply, see COPYRIGHT and LICENSE files.
+_ACEOF
+ exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_cxx_try_compile LINENO
+# ----------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext
+ if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_cxx_try_compile
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext
+ if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_c_try_link LINENO
+# -----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext conftest$ac_exeext
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ test -x conftest$ac_exeext
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+ # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+ # interfere with the next link command; also delete a directory that is
+ # left behind by Apple's compiler. We do this before executing the actions.
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_link
+
+# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists and can be compiled using the include files in
+# INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$3=yes"
+else
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_compile
+
+# ac_fn_c_try_cpp LINENO
+# ----------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } > conftest.i && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_cpp
+
+# ac_fn_c_try_run LINENO
+# ----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
+# that executables *can* be run.
+ac_fn_c_try_run ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: program exited with status $ac_status" >&5
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=$ac_status
+fi
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_run
+
+# ac_fn_c_check_func LINENO FUNC VAR
+# ----------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+ For example, HP-UX 11i <limits.h> declares gettimeofday. */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $2 (); below.
+ Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ <limits.h> exists even on freestanding compilers. */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ eval "$3=yes"
+else
+ eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_func
+
+# ac_fn_cxx_try_cpp LINENO
+# ------------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_cpp ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } > conftest.i && {
+ test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_cxx_try_cpp
+
+# ac_fn_cxx_try_link LINENO
+# -------------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_link ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext conftest$ac_exeext
+ if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_cxx_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ test -x conftest$ac_exeext
+ }; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+ # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+ # interfere with the next link command; also delete a directory that is
+ # left behind by Apple's compiler. We do this before executing the actions.
+ rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_cxx_try_link
+
+# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists, giving a warning if it cannot be compiled using
+# the include files in INCLUDES and setting the cache variable VAR
+# accordingly.
+ac_fn_c_check_header_mongrel ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if eval \${$3+:} false; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+else
+ # Is the header compilable?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
+$as_echo_n "checking $2 usability... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_header_compiler=yes
+else
+ ac_header_compiler=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
+$as_echo "$ac_header_compiler" >&6; }
+
+# Is the header present?
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
+$as_echo_n "checking $2 presence... " >&6; }
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <$2>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ ac_header_preproc=yes
+else
+ ac_header_preproc=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
+$as_echo "$ac_header_preproc" >&6; }
+
+# So? What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
+ yes:no: )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
+$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+ ;;
+ no:yes:* )
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
+$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5
+$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
+$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5
+$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
+$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
+( $as_echo "## -------------------------------------------- ##
+## Report this to http://www.openldap.org/its/ ##
+## -------------------------------------------- ##"
+ ) | sed "s/^/$as_me: WARNING: /" >&2
+ ;;
+esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ eval "$3=\$ac_header_compiler"
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_mongrel
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by ldapcpplib $as_me , which was
+generated by GNU Autoconf 2.69. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ $as_echo "PATH: $as_dir"
+ done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *\'*)
+ ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+ 2)
+ as_fn_append ac_configure_args1 " '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ as_fn_append ac_configure_args " '$ac_arg'"
+ ;;
+ esac
+ done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+(
+ for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+ (set) 2>&1 |
+ case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ sed -n \
+ "s/'\''/'\''\\\\'\'''\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+ ;; #(
+ *)
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+)
+ echo
+
+ $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+ echo
+ cat confdefs.h
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ $as_echo "$as_me: caught signal $ac_signal"
+ $as_echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core core.conftest.* &&
+ rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+ # We do not want a PATH search for config.site.
+ case $CONFIG_SITE in #((
+ -*) ac_site_file1=./$CONFIG_SITE;;
+ */*) ac_site_file1=$CONFIG_SITE;;
+ *) ac_site_file1=./$CONFIG_SITE;;
+ esac
+elif test "x$prefix" != xNONE; then
+ ac_site_file1=$prefix/share/config.site
+ ac_site_file2=$prefix/etc/config.site
+else
+ ac_site_file1=$ac_default_prefix/share/config.site
+ ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+ test "x$ac_site_file" = xNONE && continue
+ if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file" \
+ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special files
+ # actually), so we avoid doing that. DJGPP emulates it as a regular file.
+ if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . "$cache_file";;
+ *) . "./$cache_file";;
+ esac
+ fi
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val=\$ac_cv_env_${ac_var}_value
+ eval ac_new_val=\$ac_env_${ac_var}_value
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ # differences in whitespace do not lead to failure.
+ ac_old_val_w=`echo x $ac_old_val`
+ ac_new_val_w=`echo x $ac_new_val`
+ if test "$ac_old_val_w" != "$ac_new_val_w"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ ac_cache_corrupted=:
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+ eval $ac_var=\$ac_old_val
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5
+$as_echo "$as_me: former value: \`$ac_old_val'" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5
+$as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+am__api_version='1.15'
+
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+ if test -f "$ac_dir/install-sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f "$ac_dir/install.sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f "$ac_dir/shtool"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
+
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if ${ac_cv_path_install+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in #((
+ ./ | .// | /[cC]/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ rm -rf conftest.one conftest.two conftest.dir
+ echo one > conftest.one
+ echo two > conftest.two
+ mkdir conftest.dir
+ if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+ test -s conftest.one && test -s conftest.two &&
+ test -s conftest.dir/conftest.one &&
+ test -s conftest.dir/conftest.two
+ then
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+
+ done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ INSTALL=$ac_install_sh
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5
+$as_echo_n "checking whether build environment is sane... " >&6; }
+# Reject unsafe characters in $srcdir or the absolute working directory
+# name. Accept space and tab only in the latter.
+am_lf='
+'
+case `pwd` in
+ *[\\\"\#\$\&\'\`$am_lf]*)
+ as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;;
+esac
+case $srcdir in
+ *[\\\"\#\$\&\'\`$am_lf\ \ ]*)
+ as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;;
+esac
+
+# Do 'set' in a subshell so we don't clobber the current shell's
+# arguments. Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+ am_has_slept=no
+ for am_try in 1 2; do
+ echo "timestamp, slept: $am_has_slept" > conftest.file
+ set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+ if test "$*" = "X"; then
+ # -L didn't work.
+ set X `ls -t "$srcdir/configure" conftest.file`
+ fi
+ if test "$*" != "X $srcdir/configure conftest.file" \
+ && test "$*" != "X conftest.file $srcdir/configure"; then
+
+ # If neither matched, then we have a broken ls. This can happen
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+ as_fn_error $? "ls -t appears to fail. Make sure there is not a broken
+ alias in your environment" "$LINENO" 5
+ fi
+ if test "$2" = conftest.file || test $am_try -eq 2; then
+ break
+ fi
+ # Just in case.
+ sleep 1
+ am_has_slept=yes
+ done
+ test "$2" = conftest.file
+ )
+then
+ # Ok.
+ :
+else
+ as_fn_error $? "newly created file is older than distributed files!
+Check your system clock" "$LINENO" 5
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+# If we didn't sleep, we still need to ensure time stamps of config.status and
+# generated files are strictly newer.
+am_sleep_pid=
+if grep 'slept: no' conftest.file >/dev/null 2>&1; then
+ ( sleep 1 ) &
+ am_sleep_pid=$!
+fi
+
+rm -f conftest.file
+
+test "$program_prefix" != NONE &&
+ program_transform_name="s&^&$program_prefix&;$program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+ program_transform_name="s&\$&$program_suffix&;$program_transform_name"
+# Double any \ or $.
+# By default was `s,x,x', remove it if useless.
+ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
+program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
+
+# Expand $ac_aux_dir to an absolute path.
+am_aux_dir=`cd "$ac_aux_dir" && pwd`
+
+if test x"${MISSING+set}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
+ *)
+ MISSING="\${SHELL} $am_aux_dir/missing" ;;
+ esac
+fi
+# Use eval to expand $SHELL
+if eval "$MISSING --is-lightweight"; then
+ am_missing_run="$MISSING "
+else
+ am_missing_run=
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5
+$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;}
+fi
+
+if test x"${install_sh+set}" != xset; then
+ case $am_aux_dir in
+ *\ * | *\ *)
+ install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
+ *)
+ install_sh="\${SHELL} $am_aux_dir/install-sh"
+ esac
+fi
+
+# Installed binaries are usually stripped using 'strip' when the user
+# run "make install-strip". However 'strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the 'STRIP' environment variable to overrule this program.
+if test "$cross_compiling" != no; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$STRIP"; then
+ ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+ ac_ct_STRIP=$STRIP
+ # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_STRIP"; then
+ ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_STRIP="strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_STRIP" = x; then
+ STRIP=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ STRIP=$ac_ct_STRIP
+ fi
+else
+ STRIP="$ac_cv_prog_STRIP"
+fi
+
+fi
+INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5
+$as_echo_n "checking for a thread-safe mkdir -p... " >&6; }
+if test -z "$MKDIR_P"; then
+ if ${ac_cv_path_mkdir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in mkdir gmkdir; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue
+ case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
+ 'mkdir (GNU coreutils) '* | \
+ 'mkdir (coreutils) '* | \
+ 'mkdir (fileutils) '4.1*)
+ ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext
+ break 3;;
+ esac
+ done
+ done
+ done
+IFS=$as_save_IFS
+
+fi
+
+ test -d ./--version && rmdir ./--version
+ if test "${ac_cv_path_mkdir+set}" = set; then
+ MKDIR_P="$ac_cv_path_mkdir -p"
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for MKDIR_P within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ MKDIR_P="$ac_install_sh -d"
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5
+$as_echo "$MKDIR_P" >&6; }
+
+for ac_prog in gawk mawk nawk awk
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AWK+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AWK"; then
+ ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_AWK="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
+$as_echo "$AWK" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$AWK" && break
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
+set x ${MAKE-make}
+ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
+if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat >conftest.make <<\_ACEOF
+SHELL = /bin/sh
+all:
+ @echo '@@@%%%=$(MAKE)=@@@%%%'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
+case `${MAKE-make} -f conftest.make 2>/dev/null` in
+ *@@@%%%=?*=@@@%%%*)
+ eval ac_cv_prog_make_${ac_make}_set=yes;;
+ *)
+ eval ac_cv_prog_make_${ac_make}_set=no;;
+esac
+rm -f conftest.make
+fi
+if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ SET_MAKE=
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+ am__leading_dot=.
+else
+ am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+
+# Check whether --enable-silent-rules was given.
+if test "${enable_silent_rules+set}" = set; then :
+ enableval=$enable_silent_rules;
+fi
+
+case $enable_silent_rules in # (((
+ yes) AM_DEFAULT_VERBOSITY=0;;
+ no) AM_DEFAULT_VERBOSITY=1;;
+ *) AM_DEFAULT_VERBOSITY=1;;
+esac
+am_make=${MAKE-make}
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5
+$as_echo_n "checking whether $am_make supports nested variables... " >&6; }
+if ${am_cv_make_support_nested_variables+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if $as_echo 'TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+ @$(TRUE)
+.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then
+ am_cv_make_support_nested_variables=yes
+else
+ am_cv_make_support_nested_variables=no
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5
+$as_echo "$am_cv_make_support_nested_variables" >&6; }
+if test $am_cv_make_support_nested_variables = yes; then
+ AM_V='$(V)'
+ AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+ AM_V=$AM_DEFAULT_VERBOSITY
+ AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AM_BACKSLASH='\'
+
+if test "`cd $srcdir && pwd`" != "`pwd`"; then
+ # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
+ # is not polluted with repeated "-I."
+ am__isrc=' -I$(srcdir)'
+ # test to see if srcdir already configured
+ if test -f $srcdir/config.status; then
+ as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5
+ fi
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+ if (cygpath --version) >/dev/null 2>/dev/null; then
+ CYGPATH_W='cygpath -w'
+ else
+ CYGPATH_W=echo
+ fi
+fi
+
+
+# Define the identity of the package.
+ PACKAGE='ldapcpplib'
+ VERSION=' '
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE "$PACKAGE"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define VERSION "$VERSION"
+_ACEOF
+
+# Some tools Automake needs.
+
+ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
+
+
+AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
+
+
+AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
+
+
+AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
+
+
+MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
+
+# For better backward compatibility. To be removed once Automake 1.9.x
+# dies out for good. For more background, see:
+# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+mkdir_p='$(MKDIR_P)'
+
+# We need awk for the "check" target (and possibly the TAP driver). The
+# system "awk" is bad on some platforms.
+# Always define AMTAR for backward compatibility. Yes, it's still used
+# in the wild :-( We should find a proper way to deprecate it ...
+AMTAR='$${TAR-tar}'
+
+
+# We'll loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar pax cpio none'
+
+am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'
+
+
+
+
+
+
+# POSIX will say in a future version that running "rm -f" with no argument
+# is OK; and we want to be able to make that assumption in our Makefile
+# recipes. So use an aggressive probe to check that the usage we want is
+# actually supported "in the wild" to an acceptable degree.
+# See automake bug#10828.
+# To make any issue more visible, cause the running configure to be aborted
+# by default if the 'rm' program in use doesn't match our expectations; the
+# user can still override this though.
+if rm -f && rm -fr && rm -rf; then : OK; else
+ cat >&2 <<'END'
+Oops!
+
+Your 'rm' program seems unable to run without file operands specified
+on the command line, even when the '-f' option is present. This is contrary
+to the behaviour of most rm programs out there, and not conforming with
+the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>
+
+Please tell bug-automake@gnu.org about your system, including the value
+of your $PATH and any error possibly output before this message. This
+can help us improve future automake versions.
+
+END
+ if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
+ echo 'Configuration will proceed anyway, since you have set the' >&2
+ echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
+ echo >&2
+ else
+ cat >&2 <<'END'
+Aborting the configuration process, to ensure you take notice of the issue.
+
+You can download and install GNU coreutils to get an 'rm' implementation
+that behaves properly: <http://www.gnu.org/software/coreutils/>.
+
+If you want to complete the configuration process using your problematic
+'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
+to "yes", and re-run configure.
+
+END
+ as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5
+ fi
+fi
+
+ac_config_headers="$ac_config_headers src/config.h"
+
+
+eval `$ac_aux_dir/version.sh`
+if test -z "$OL_CPP_API_RELEASE"; then
+ as_fn_error $? "could not determine version" "$LINENO" 5
+fi
+
+VERSION=$OL_CPP_API_RELEASE
+OPENLDAP_CPP_API_VERSION=$OL_CPP_API_VERSION
+
+
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test -z "$CXX"; then
+ if test -n "$CCC"; then
+ CXX=$CCC
+ else
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CXX+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CXX"; then
+ ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5
+$as_echo "$CXX" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CXX" && break
+ done
+fi
+if test -z "$CXX"; then
+ ac_ct_CXX=$CXX
+ for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CXX+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CXX"; then
+ ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CXX="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5
+$as_echo "$ac_ct_CXX" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CXX" && break
+done
+
+ if test "x$ac_ct_CXX" = x; then
+ CXX="g++"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CXX=$ac_ct_CXX
+ fi
+fi
+
+ fi
+fi
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+ { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ sed '10a\
+... rest of stderr output deleted ...
+ 10q' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ fi
+ rm -f conftest.er1 conftest.err
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler works" >&5
+$as_echo_n "checking whether the C++ compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+ esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link_default") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile. We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+ then :; else
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ fi
+ # We set ac_cv_exeext here because the later test for it is not
+ # safe: cross compilers may not add the suffix if given an `-o'
+ # argument, so we may need to know it at that point already.
+ # Even if this section looks crufty: it has the advantage of
+ # actually working.
+ break;;
+ * )
+ break;;
+ esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+ ac_file=''
+fi
+if test -z "$ac_file"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C++ compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler default output file name" >&5
+$as_echo_n "checking for C++ compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+ { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ if { ac_try='./conftest$ac_cv_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C++ compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ for ac_file in conftest.o conftest.obj conftest.*; do
+ test -f "$ac_file" || continue;
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5
+$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; }
+if ${ac_cv_cxx_compiler_gnu+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+ ac_compiler_gnu=yes
+else
+ ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5
+$as_echo "$ac_cv_cxx_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+ GXX=yes
+else
+ GXX=
+fi
+ac_test_CXXFLAGS=${CXXFLAGS+set}
+ac_save_CXXFLAGS=$CXXFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5
+$as_echo_n "checking whether $CXX accepts -g... " >&6; }
+if ${ac_cv_prog_cxx_g+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_save_cxx_werror_flag=$ac_cxx_werror_flag
+ ac_cxx_werror_flag=yes
+ ac_cv_prog_cxx_g=no
+ CXXFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+ ac_cv_prog_cxx_g=yes
+else
+ CXXFLAGS=""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+
+else
+ ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+ CXXFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+ ac_cv_prog_cxx_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5
+$as_echo "$ac_cv_prog_cxx_g" >&6; }
+if test "$ac_test_CXXFLAGS" = set; then
+ CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+ if test "$GXX" = yes; then
+ CXXFLAGS="-g -O2"
+ else
+ CXXFLAGS="-g"
+ fi
+else
+ if test "$GXX" = yes; then
+ CXXFLAGS="-O2"
+ else
+ CXXFLAGS=
+ fi
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+DEPDIR="${am__leading_dot}deps"
+
+ac_config_commands="$ac_config_commands depfiles"
+
+
+am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+ @echo this is the am__doit target
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5
+$as_echo_n "checking for style of include used by $am_make... " >&6; }
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# Ignore all kinds of additional output from 'make'.
+case `$am_make -s -f confmf 2> /dev/null` in #(
+*the\ am__doit\ target*)
+ am__include=include
+ am__quote=
+ _am_result=GNU
+ ;;
+esac
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+ echo '.include "confinc"' > confmf
+ case `$am_make -s -f confmf 2> /dev/null` in #(
+ *the\ am__doit\ target*)
+ am__include=.include
+ am__quote="\""
+ _am_result=BSD
+ ;;
+ esac
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5
+$as_echo "$_am_result" >&6; }
+rm -f confinc confmf
+
+# Check whether --enable-dependency-tracking was given.
+if test "${enable_dependency_tracking+set}" = set; then :
+ enableval=$enable_dependency_tracking;
+fi
+
+if test "x$enable_dependency_tracking" != xno; then
+ am_depcomp="$ac_aux_dir/depcomp"
+ AMDEPBACKSLASH='\'
+ am__nodep='_no'
+fi
+ if test "x$enable_dependency_tracking" != xno; then
+ AMDEP_TRUE=
+ AMDEP_FALSE='#'
+else
+ AMDEP_TRUE='#'
+ AMDEP_FALSE=
+fi
+
+
+
+depcc="$CXX" am_compiler_list=
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
+$as_echo_n "checking dependency style of $depcc... " >&6; }
+if ${am_cv_CXX_dependencies_compiler_type+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named 'D' -- because '-MD' means "put the output
+ # in D".
+ rm -rf conftest.dir
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_CXX_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+ fi
+ am__universal=false
+ case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac
+
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
+ # Solaris 10 /bin/sh.
+ echo '/* dummy */' > sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ # We check with '-c' and '-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle '-M -o', and we need to detect this. Also, some Intel
+ # versions had trouble with output in subdirs.
+ am__obj=sub/conftest.${OBJEXT-o}
+ am__minus_obj="-o $am__obj"
+ case $depmode in
+ gcc)
+ # This depmode causes a compiler race in universal mode.
+ test "$am__universal" = false || continue
+ ;;
+ nosideeffect)
+ # After this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested.
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ msvc7 | msvc7msys | msvisualcpp | msvcmsys)
+ # This compiler won't grok '-c -o', but also, the minuso test has
+ # not run yet. These depmodes are late enough in the game, and
+ # so weak that their functioning should not be impacted.
+ am__obj=conftest.${OBJEXT-o}
+ am__minus_obj=
+ ;;
+ none) break ;;
+ esac
+ if depmode=$depmode \
+ source=sub/conftest.c object=$am__obj \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_CXX_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_CXX_dependencies_compiler_type=none
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5
+$as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; }
+CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type
+
+ if
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then
+ am__fastdepCXX_TRUE=
+ am__fastdepCXX_FALSE='#'
+else
+ am__fastdepCXX_TRUE='#'
+ am__fastdepCXX_FALSE=
+fi
+
+
+case `pwd` in
+ *\ * | *\ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5
+$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;;
+esac
+
+
+
+macro_version='2.4.6'
+macro_revision='2.4.6'
+
+
+
+
+
+
+
+
+
+
+
+
+
+ltmain=$ac_aux_dir/ltmain.sh
+
+# Make sure we can run config.sub.
+$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
+ as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+$as_echo_n "checking build system type... " >&6; }
+if ${ac_cv_build+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+ ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+test "x$ac_build_alias" = x &&
+ as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
+ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+$as_echo "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+$as_echo_n "checking host system type... " >&6; }
+if ${ac_cv_host+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test "x$host_alias" = x; then
+ ac_cv_host=$ac_cv_build
+else
+ ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+$as_echo "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+# Backslashify metacharacters that are still active within
+# double-quoted strings.
+sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+
+ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5
+$as_echo_n "checking how to print strings... " >&6; }
+# Test print first, because it will be a builtin if present.
+if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \
+ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then
+ ECHO='print -r --'
+elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then
+ ECHO='printf %s\n'
+else
+ # Use this function as a fallback that always works.
+ func_fallback_echo ()
+ {
+ eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+ }
+ ECHO='func_fallback_echo'
+fi
+
+# func_echo_all arg...
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+ $ECHO ""
+}
+
+case $ECHO in
+ printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5
+$as_echo "printf" >&6; } ;;
+ print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5
+$as_echo "print -r" >&6; } ;;
+ *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5
+$as_echo "cat" >&6; } ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+ { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ sed '10a\
+... rest of stderr output deleted ...
+ 10q' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ fi
+ rm -f conftest.er1 conftest.err
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_compiler_gnu=yes
+else
+ ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+else
+ CFLAGS=""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5
+$as_echo_n "checking whether $CC understands -c and -o together... " >&6; }
+if ${am_cv_prog_cc_c_o+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ # Make sure it works both with $CC and with simple cc.
+ # Following AC_PROG_CC_C_O, we do the test twice because some
+ # compilers refuse to overwrite an existing .o file with -o,
+ # though they will create one.
+ am_cv_prog_cc_c_o=yes
+ for am_i in 1 2; do
+ if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5
+ ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } \
+ && test -f conftest2.$ac_objext; then
+ : OK
+ else
+ am_cv_prog_cc_c_o=no
+ break
+ fi
+ done
+ rm -f core conftest*
+ unset am_i
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5
+$as_echo "$am_cv_prog_cc_c_o" >&6; }
+if test "$am_cv_prog_cc_c_o" != yes; then
+ # Losing compiler, so override with the script.
+ # FIXME: It is wrong to rewrite CC.
+ # But if we don't then we get into trouble of one sort or another.
+ # A longer-term fix would be to have automake use am__CC in this case,
+ # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+ CC="$am_aux_dir/compile $CC"
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+depcc="$CC" am_compiler_list=
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
+$as_echo_n "checking dependency style of $depcc... " >&6; }
+if ${am_cv_CC_dependencies_compiler_type+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+ # We make a subdir and do the tests there. Otherwise we can end up
+ # making bogus files that we don't know about and never remove. For
+ # instance it was reported that on HP-UX the gcc test will end up
+ # making a dummy file named 'D' -- because '-MD' means "put the output
+ # in D".
+ rm -rf conftest.dir
+ mkdir conftest.dir
+ # Copy depcomp to subdir because otherwise we won't find it if we're
+ # using a relative directory.
+ cp "$am_depcomp" conftest.dir
+ cd conftest.dir
+ # We will build objects and dependencies in a subdirectory because
+ # it helps to detect inapplicable dependency modes. For instance
+ # both Tru64's cc and ICC support -MD to output dependencies as a
+ # side effect of compilation, but ICC will put the dependencies in
+ # the current directory while Tru64 will put them in the object
+ # directory.
+ mkdir sub
+
+ am_cv_CC_dependencies_compiler_type=none
+ if test "$am_compiler_list" = ""; then
+ am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+ fi
+ am__universal=false
+ case " $depcc " in #(
+ *\ -arch\ *\ -arch\ *) am__universal=true ;;
+ esac
+
+ for depmode in $am_compiler_list; do
+ # Setup a source with many dependencies, because some compilers
+ # like to wrap large dependency lists on column 80 (with \), and
+ # we should not choose a depcomp mode which is confused by this.
+ #
+ # We need to recreate these files for each test, as the compiler may
+ # overwrite some of them when testing with obscure command lines.
+ # This happens at least with the AIX C compiler.
+ : > sub/conftest.c
+ for i in 1 2 3 4 5 6; do
+ echo '#include "conftst'$i'.h"' >> sub/conftest.c
+ # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
+ # Solaris 10 /bin/sh.
+ echo '/* dummy */' > sub/conftst$i.h
+ done
+ echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+ # We check with '-c' and '-o' for the sake of the "dashmstdout"
+ # mode. It turns out that the SunPro C++ compiler does not properly
+ # handle '-M -o', and we need to detect this. Also, some Intel
+ # versions had trouble with output in subdirs.
+ am__obj=sub/conftest.${OBJEXT-o}
+ am__minus_obj="-o $am__obj"
+ case $depmode in
+ gcc)
+ # This depmode causes a compiler race in universal mode.
+ test "$am__universal" = false || continue
+ ;;
+ nosideeffect)
+ # After this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested.
+ if test "x$enable_dependency_tracking" = xyes; then
+ continue
+ else
+ break
+ fi
+ ;;
+ msvc7 | msvc7msys | msvisualcpp | msvcmsys)
+ # This compiler won't grok '-c -o', but also, the minuso test has
+ # not run yet. These depmodes are late enough in the game, and
+ # so weak that their functioning should not be impacted.
+ am__obj=conftest.${OBJEXT-o}
+ am__minus_obj=
+ ;;
+ none) break ;;
+ esac
+ if depmode=$depmode \
+ source=sub/conftest.c object=$am__obj \
+ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
+ >/dev/null 2>conftest.err &&
+ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+ grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
+ ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+ # icc doesn't choke on unknown options, it will just issue warnings
+ # or remarks (even with -Werror). So we grep stderr for any message
+ # that says an option was ignored or not supported.
+ # When given -MP, icc 7.0 and 7.1 complain thusly:
+ # icc: Command line warning: ignoring option '-M'; no argument required
+ # The diagnosis changed in icc 8.0:
+ # icc: Command line remark: option '-MP' not supported
+ if (grep 'ignoring option' conftest.err ||
+ grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+ am_cv_CC_dependencies_compiler_type=$depmode
+ break
+ fi
+ fi
+ done
+
+ cd ..
+ rm -rf conftest.dir
+else
+ am_cv_CC_dependencies_compiler_type=none
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5
+$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; }
+CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
+
+ if
+ test "x$enable_dependency_tracking" != xno \
+ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
+ am__fastdepCC_TRUE=
+ am__fastdepCC_FALSE='#'
+else
+ am__fastdepCC_TRUE='#'
+ am__fastdepCC_FALSE=
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5
+$as_echo_n "checking for a sed that does not truncate output... " >&6; }
+if ${ac_cv_path_SED+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+ for ac_i in 1 2 3 4 5 6 7; do
+ ac_script="$ac_script$as_nl$ac_script"
+ done
+ echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
+ { ac_script=; unset ac_script;}
+ if test -z "$SED"; then
+ ac_path_SED_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in sed gsed; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_SED" || continue
+# Check for GNU ac_path_SED and select it if it is found.
+ # Check for GNU $ac_path_SED
+case `"$ac_path_SED" --version 2>&1` in
+*GNU*)
+ ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo '' >> "conftest.nl"
+ "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_SED_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_SED="$ac_path_SED"
+ ac_path_SED_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_SED_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_SED"; then
+ as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5
+ fi
+else
+ ac_cv_path_SED=$SED
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5
+$as_echo "$ac_cv_path_SED" >&6; }
+ SED="$ac_cv_path_SED"
+ rm -f conftest.sed
+
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
+if ${ac_cv_path_GREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$GREP"; then
+ ac_path_GREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in grep ggrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_GREP" || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+ # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'GREP' >> "conftest.nl"
+ "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_GREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_GREP="$ac_path_GREP"
+ ac_path_GREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_GREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_GREP"; then
+ as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+$as_echo "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+$as_echo_n "checking for egrep... " >&6; }
+if ${ac_cv_path_EGREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+ then ac_cv_path_EGREP="$GREP -E"
+ else
+ if test -z "$EGREP"; then
+ ac_path_EGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in egrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_EGREP" || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+ # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'EGREP' >> "conftest.nl"
+ "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_EGREP="$ac_path_EGREP"
+ ac_path_EGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_EGREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_EGREP"; then
+ as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_EGREP=$EGREP
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+$as_echo "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5
+$as_echo_n "checking for fgrep... " >&6; }
+if ${ac_cv_path_FGREP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1
+ then ac_cv_path_FGREP="$GREP -F"
+ else
+ if test -z "$FGREP"; then
+ ac_path_FGREP_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in fgrep; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_FGREP" || continue
+# Check for GNU ac_path_FGREP and select it if it is found.
+ # Check for GNU $ac_path_FGREP
+case `"$ac_path_FGREP" --version 2>&1` in
+*GNU*)
+ ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;;
+*)
+ ac_count=0
+ $as_echo_n 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ $as_echo 'FGREP' >> "conftest.nl"
+ "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_FGREP_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_FGREP="$ac_path_FGREP"
+ ac_path_FGREP_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_FGREP_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_FGREP"; then
+ as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_FGREP=$FGREP
+fi
+
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5
+$as_echo "$ac_cv_path_FGREP" >&6; }
+ FGREP="$ac_cv_path_FGREP"
+
+
+test -z "$GREP" && GREP=grep
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then :
+ withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes
+else
+ with_gnu_ld=no
+fi
+
+ac_prog=ld
+if test yes = "$GCC"; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5
+$as_echo_n "checking for ld used by $CC... " >&6; }
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return, which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [\\/]* | ?:[\\/]*)
+ re_direlt='/[^/][^/]*/\.\./'
+ # Canonicalize the pathname of ld
+ ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+ while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD=$ac_prog
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test yes = "$with_gnu_ld"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5
+$as_echo_n "checking for GNU ld... " >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5
+$as_echo_n "checking for non-GNU ld... " >&6; }
+fi
+if ${lt_cv_path_LD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$LD"; then
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ lt_cv_path_LD=$ac_dir/$ac_prog
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some variants of GNU ld only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+ *GNU* | *'with BFD'*)
+ test no != "$with_gnu_ld" && break
+ ;;
+ *)
+ test yes != "$with_gnu_ld" && break
+ ;;
+ esac
+ fi
+ done
+ IFS=$lt_save_ifs
+else
+ lt_cv_path_LD=$LD # Let the user override the test with a path.
+fi
+fi
+
+LD=$lt_cv_path_LD
+if test -n "$LD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
+$as_echo "$LD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
+$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
+if ${lt_cv_prog_gnu_ld+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ lt_cv_prog_gnu_ld=yes
+ ;;
+*)
+ lt_cv_prog_gnu_ld=no
+ ;;
+esac
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5
+$as_echo "$lt_cv_prog_gnu_ld" >&6; }
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5
+$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; }
+if ${lt_cv_path_NM+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$NM"; then
+ # Let the user override the test.
+ lt_cv_path_NM=$NM
+else
+ lt_nm_to_check=${ac_tool_prefix}nm
+ if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+ lt_nm_to_check="$lt_nm_to_check nm"
+ fi
+ for lt_tmp_nm in $lt_nm_to_check; do
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ tmp_nm=$ac_dir/$lt_tmp_nm
+ if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then
+ # Check to see if the nm accepts a BSD-compat flag.
+ # Adding the 'sed 1q' prevents false positives on HP-UX, which says:
+ # nm: unknown option "B" ignored
+ # Tru64's nm complains that /dev/null is an invalid object file
+ # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty
+ case $build_os in
+ mingw*) lt_bad_file=conftest.nm/nofile ;;
+ *) lt_bad_file=/dev/null ;;
+ esac
+ case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in
+ *$lt_bad_file* | *'Invalid file or object type'*)
+ lt_cv_path_NM="$tmp_nm -B"
+ break 2
+ ;;
+ *)
+ case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
+ */dev/null*)
+ lt_cv_path_NM="$tmp_nm -p"
+ break 2
+ ;;
+ *)
+ lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+ continue # so that we can try to find one that supports BSD flags
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ done
+ IFS=$lt_save_ifs
+ done
+ : ${lt_cv_path_NM=no}
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5
+$as_echo "$lt_cv_path_NM" >&6; }
+if test no != "$lt_cv_path_NM"; then
+ NM=$lt_cv_path_NM
+else
+ # Didn't find any BSD compatible name lister, look for dumpbin.
+ if test -n "$DUMPBIN"; then :
+ # Let the user override the test.
+ else
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in dumpbin "link -dump"
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DUMPBIN+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$DUMPBIN"; then
+ ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+DUMPBIN=$ac_cv_prog_DUMPBIN
+if test -n "$DUMPBIN"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5
+$as_echo "$DUMPBIN" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$DUMPBIN" && break
+ done
+fi
+if test -z "$DUMPBIN"; then
+ ac_ct_DUMPBIN=$DUMPBIN
+ for ac_prog in dumpbin "link -dump"
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_DUMPBIN"; then
+ ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_DUMPBIN="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN
+if test -n "$ac_ct_DUMPBIN"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5
+$as_echo "$ac_ct_DUMPBIN" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_DUMPBIN" && break
+done
+
+ if test "x$ac_ct_DUMPBIN" = x; then
+ DUMPBIN=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ DUMPBIN=$ac_ct_DUMPBIN
+ fi
+fi
+
+ case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in
+ *COFF*)
+ DUMPBIN="$DUMPBIN -symbols -headers"
+ ;;
+ *)
+ DUMPBIN=:
+ ;;
+ esac
+ fi
+
+ if test : != "$DUMPBIN"; then
+ NM=$DUMPBIN
+ fi
+fi
+test -z "$NM" && NM=nm
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5
+$as_echo_n "checking the name lister ($NM) interface... " >&6; }
+if ${lt_cv_nm_interface+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_nm_interface="BSD nm"
+ echo "int some_variable = 0;" > conftest.$ac_ext
+ (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5)
+ (eval "$ac_compile" 2>conftest.err)
+ cat conftest.err >&5
+ (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+ (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+ cat conftest.err >&5
+ (eval echo "\"\$as_me:$LINENO: output\"" >&5)
+ cat conftest.out >&5
+ if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+ lt_cv_nm_interface="MS dumpbin"
+ fi
+ rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5
+$as_echo "$lt_cv_nm_interface" >&6; }
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
+$as_echo_n "checking whether ln -s works... " >&6; }
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
+$as_echo "no, using $LN_S" >&6; }
+fi
+
+# find the maximum length of command line arguments
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5
+$as_echo_n "checking the maximum length of command line arguments... " >&6; }
+if ${lt_cv_sys_max_cmd_len+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ i=0
+ teststring=ABCD
+
+ case $build_os in
+ msdosdjgpp*)
+ # On DJGPP, this test can blow up pretty badly due to problems in libc
+ # (any single argument exceeding 2000 bytes causes a buffer overrun
+ # during glob expansion). Even if it were fixed, the result of this
+ # check would be larger than it should be.
+ lt_cv_sys_max_cmd_len=12288; # 12K is about right
+ ;;
+
+ gnu*)
+ # Under GNU Hurd, this test is not required because there is
+ # no limit to the length of command line arguments.
+ # Libtool will interpret -1 as no limit whatsoever
+ lt_cv_sys_max_cmd_len=-1;
+ ;;
+
+ cygwin* | mingw* | cegcc*)
+ # On Win9x/ME, this test blows up -- it succeeds, but takes
+ # about 5 minutes as the teststring grows exponentially.
+ # Worse, since 9x/ME are not pre-emptively multitasking,
+ # you end up with a "frozen" computer, even though with patience
+ # the test eventually succeeds (with a max line length of 256k).
+ # Instead, let's just punt: use the minimum linelength reported by
+ # all of the supported platforms: 8192 (on NT/2K/XP).
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ mint*)
+ # On MiNT this can take a long time and run out of memory.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ amigaos*)
+ # On AmigaOS with pdksh, this test takes hours, literally.
+ # So we just punt and use a minimum line length of 8192.
+ lt_cv_sys_max_cmd_len=8192;
+ ;;
+
+ bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*)
+ # This has been around since 386BSD, at least. Likely further.
+ if test -x /sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+ elif test -x /usr/sbin/sysctl; then
+ lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+ else
+ lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs
+ fi
+ # And add a safety zone
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ ;;
+
+ interix*)
+ # We know the value 262144 and hardcode it with a safety zone (like BSD)
+ lt_cv_sys_max_cmd_len=196608
+ ;;
+
+ os2*)
+ # The test takes a long time on OS/2.
+ lt_cv_sys_max_cmd_len=8192
+ ;;
+
+ osf*)
+ # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+ # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+ # nice to cause kernel panics so lets avoid the loop below.
+ # First set a reasonable default.
+ lt_cv_sys_max_cmd_len=16384
+ #
+ if test -x /sbin/sysconfig; then
+ case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+ *1*) lt_cv_sys_max_cmd_len=-1 ;;
+ esac
+ fi
+ ;;
+ sco3.2v5*)
+ lt_cv_sys_max_cmd_len=102400
+ ;;
+ sysv5* | sco5v6* | sysv4.2uw2*)
+ kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+ if test -n "$kargmax"; then
+ lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'`
+ else
+ lt_cv_sys_max_cmd_len=32768
+ fi
+ ;;
+ *)
+ lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+ if test -n "$lt_cv_sys_max_cmd_len" && \
+ test undefined != "$lt_cv_sys_max_cmd_len"; then
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+ else
+ # Make teststring a little bigger before we do anything with it.
+ # a 1K string should be a reasonable start.
+ for i in 1 2 3 4 5 6 7 8; do
+ teststring=$teststring$teststring
+ done
+ SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+ # If test is not a shell built-in, we'll probably end up computing a
+ # maximum length that is only half of the actual maximum length, but
+ # we can't tell.
+ while { test X`env echo "$teststring$teststring" 2>/dev/null` \
+ = "X$teststring$teststring"; } >/dev/null 2>&1 &&
+ test 17 != "$i" # 1/2 MB should be enough
+ do
+ i=`expr $i + 1`
+ teststring=$teststring$teststring
+ done
+ # Only check the string length outside the loop.
+ lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+ teststring=
+ # Add a significant safety factor because C++ compilers can tack on
+ # massive amounts of additional arguments before passing them to the
+ # linker. It appears as though 1/2 is a usable value.
+ lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+ fi
+ ;;
+ esac
+
+fi
+
+if test -n "$lt_cv_sys_max_cmd_len"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5
+$as_echo "$lt_cv_sys_max_cmd_len" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5
+$as_echo "none" >&6; }
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+
+
+
+
+
+
+: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+ lt_unset=unset
+else
+ lt_unset=false
+fi
+
+
+
+
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+ # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+ lt_SP2NL='tr \040 \012'
+ lt_NL2SP='tr \015\012 \040\040'
+ ;;
+ *) # EBCDIC based system
+ lt_SP2NL='tr \100 \n'
+ lt_NL2SP='tr \r\n \100\100'
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5
+$as_echo_n "checking how to convert $build file names to $host format... " >&6; }
+if ${lt_cv_to_host_file_cmd+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $host in
+ *-*-mingw* )
+ case $build in
+ *-*-mingw* ) # actually msys
+ lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32
+ ;;
+ *-*-cygwin* )
+ lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32
+ ;;
+ * ) # otherwise, assume *nix
+ lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32
+ ;;
+ esac
+ ;;
+ *-*-cygwin* )
+ case $build in
+ *-*-mingw* ) # actually msys
+ lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin
+ ;;
+ *-*-cygwin* )
+ lt_cv_to_host_file_cmd=func_convert_file_noop
+ ;;
+ * ) # otherwise, assume *nix
+ lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin
+ ;;
+ esac
+ ;;
+ * ) # unhandled hosts (and "normal" native builds)
+ lt_cv_to_host_file_cmd=func_convert_file_noop
+ ;;
+esac
+
+fi
+
+to_host_file_cmd=$lt_cv_to_host_file_cmd
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5
+$as_echo "$lt_cv_to_host_file_cmd" >&6; }
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5
+$as_echo_n "checking how to convert $build file names to toolchain format... " >&6; }
+if ${lt_cv_to_tool_file_cmd+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ #assume ordinary cross tools, or native build.
+lt_cv_to_tool_file_cmd=func_convert_file_noop
+case $host in
+ *-*-mingw* )
+ case $build in
+ *-*-mingw* ) # actually msys
+ lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32
+ ;;
+ esac
+ ;;
+esac
+
+fi
+
+to_tool_file_cmd=$lt_cv_to_tool_file_cmd
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5
+$as_echo "$lt_cv_to_tool_file_cmd" >&6; }
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5
+$as_echo_n "checking for $LD option to reload object files... " >&6; }
+if ${lt_cv_ld_reload_flag+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ld_reload_flag='-r'
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5
+$as_echo "$lt_cv_ld_reload_flag" >&6; }
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ if test yes != "$GCC"; then
+ reload_cmds=false
+ fi
+ ;;
+ darwin*)
+ if test yes = "$GCC"; then
+ reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs'
+ else
+ reload_cmds='$LD$reload_flag -o $output$reload_objs'
+ fi
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args.
+set dummy ${ac_tool_prefix}objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OBJDUMP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OBJDUMP"; then
+ ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OBJDUMP=$ac_cv_prog_OBJDUMP
+if test -n "$OBJDUMP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5
+$as_echo "$OBJDUMP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OBJDUMP"; then
+ ac_ct_OBJDUMP=$OBJDUMP
+ # Extract the first word of "objdump", so it can be a program name with args.
+set dummy objdump; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OBJDUMP"; then
+ ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_OBJDUMP="objdump"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP
+if test -n "$ac_ct_OBJDUMP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5
+$as_echo "$ac_ct_OBJDUMP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OBJDUMP" = x; then
+ OBJDUMP="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OBJDUMP=$ac_ct_OBJDUMP
+ fi
+else
+ OBJDUMP="$ac_cv_prog_OBJDUMP"
+fi
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5
+$as_echo_n "checking how to recognize dependent libraries... " >&6; }
+if ${lt_cv_deplibs_check_method+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# 'unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# that responds to the $file_magic_cmd with a given extended regex.
+# If you have 'file' or equivalent on your system and you're not sure
+# whether 'pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[4-9]*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+beos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+bsdi[45]*)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)'
+ lt_cv_file_magic_cmd='/usr/bin/file -L'
+ lt_cv_file_magic_test_file=/shlib/libc.so
+ ;;
+
+cygwin*)
+ # func_win32_libid is a shell function defined in ltmain.sh
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ ;;
+
+mingw* | pw32*)
+ # Base MSYS/MinGW do not provide the 'file' command needed by
+ # func_win32_libid shell function, so use a weaker test based on 'objdump',
+ # unless we find 'file', for example because we are cross-compiling.
+ if ( file / ) >/dev/null 2>&1; then
+ lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+ lt_cv_file_magic_cmd='func_win32_libid'
+ else
+ # Keep this pattern in sync with the one in func_win32_libid.
+ lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ fi
+ ;;
+
+cegcc*)
+ # use the weaker test based on 'objdump'. See mingw*.
+ lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+ lt_cv_file_magic_cmd='$OBJDUMP -f'
+ ;;
+
+darwin* | rhapsody*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+freebsd* | dragonfly*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ case $host_cpu in
+ i*86 )
+ # Not sure whether the presence of OpenBSD here was a mistake.
+ # Let's accept both of them until this is cleared up.
+ lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+ ;;
+ esac
+ else
+ lt_cv_deplibs_check_method=pass_all
+ fi
+ ;;
+
+haiku*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+hpux10.20* | hpux11*)
+ lt_cv_file_magic_cmd=/usr/bin/file
+ case $host_cpu in
+ ia64*)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64'
+ lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+ ;;
+ hppa*64*)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'
+ lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+ ;;
+ *)
+ lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library'
+ lt_cv_file_magic_test_file=/usr/lib/libc.sl
+ ;;
+ esac
+ ;;
+
+interix[3-9]*)
+ # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$'
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $LD in
+ *-32|*"-32 ") libmagic=32-bit;;
+ *-n32|*"-n32 ") libmagic=N32;;
+ *-64|*"-64 ") libmagic=64-bit;;
+ *) libmagic=never-match;;
+ esac
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+netbsd* | netbsdelf*-gnu)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$'
+ fi
+ ;;
+
+newos6*)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)'
+ lt_cv_file_magic_cmd=/usr/bin/file
+ lt_cv_file_magic_test_file=/usr/lib/libnls.so
+ ;;
+
+*nto* | *qnx*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+openbsd* | bitrig*)
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$'
+ else
+ lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+ fi
+ ;;
+
+osf3* | osf4* | osf5*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+rdos*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+solaris*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+
+sysv4 | sysv4.3*)
+ case $host_vendor in
+ motorola)
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]'
+ lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+ ;;
+ ncr)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ sequent)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'
+ ;;
+ sni)
+ lt_cv_file_magic_cmd='/bin/file'
+ lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib"
+ lt_cv_file_magic_test_file=/lib/libc.so
+ ;;
+ siemens)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ pc)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+ esac
+ ;;
+
+tpf*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+os2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
+esac
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5
+$as_echo "$lt_cv_deplibs_check_method" >&6; }
+
+file_magic_glob=
+want_nocaseglob=no
+if test "$build" = "$host"; then
+ case $host_os in
+ mingw* | pw32*)
+ if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then
+ want_nocaseglob=yes
+ else
+ file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"`
+ fi
+ ;;
+ esac
+fi
+
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dlltool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DLLTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$DLLTOOL"; then
+ ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+DLLTOOL=$ac_cv_prog_DLLTOOL
+if test -n "$DLLTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5
+$as_echo "$DLLTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DLLTOOL"; then
+ ac_ct_DLLTOOL=$DLLTOOL
+ # Extract the first word of "dlltool", so it can be a program name with args.
+set dummy dlltool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_DLLTOOL"; then
+ ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_DLLTOOL="dlltool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL
+if test -n "$ac_ct_DLLTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5
+$as_echo "$ac_ct_DLLTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_DLLTOOL" = x; then
+ DLLTOOL="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ DLLTOOL=$ac_ct_DLLTOOL
+ fi
+else
+ DLLTOOL="$ac_cv_prog_DLLTOOL"
+fi
+
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5
+$as_echo_n "checking how to associate runtime and link libraries... " >&6; }
+if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_sharedlib_from_linklib_cmd='unknown'
+
+case $host_os in
+cygwin* | mingw* | pw32* | cegcc*)
+ # two different shell functions defined in ltmain.sh;
+ # decide which one to use based on capabilities of $DLLTOOL
+ case `$DLLTOOL --help 2>&1` in
+ *--identify-strict*)
+ lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib
+ ;;
+ *)
+ lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback
+ ;;
+ esac
+ ;;
+*)
+ # fallback: assume linklib IS sharedlib
+ lt_cv_sharedlib_from_linklib_cmd=$ECHO
+ ;;
+esac
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5
+$as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; }
+sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd
+test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ for ac_prog in ar
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$AR"; then
+ ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_AR="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+$as_echo "$AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$AR" && break
+ done
+fi
+if test -z "$AR"; then
+ ac_ct_AR=$AR
+ for ac_prog in ar
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_AR+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_AR"; then
+ ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_AR="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
+$as_echo "$ac_ct_AR" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_AR" && break
+done
+
+ if test "x$ac_ct_AR" = x; then
+ AR="false"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ AR=$ac_ct_AR
+ fi
+fi
+
+: ${AR=ar}
+: ${AR_FLAGS=cru}
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5
+$as_echo_n "checking for archiver @FILE support... " >&6; }
+if ${lt_cv_ar_at_file+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ar_at_file=no
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ echo conftest.$ac_objext > conftest.lst
+ lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5'
+ { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5
+ (eval $lt_ar_try) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ if test 0 -eq "$ac_status"; then
+ # Ensure the archiver fails upon bogus file names.
+ rm -f conftest.$ac_objext libconftest.a
+ { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5
+ (eval $lt_ar_try) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ if test 0 -ne "$ac_status"; then
+ lt_cv_ar_at_file=@
+ fi
+ fi
+ rm -f conftest.* libconftest.a
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5
+$as_echo "$lt_cv_ar_at_file" >&6; }
+
+if test no = "$lt_cv_ar_at_file"; then
+ archiver_list_spec=
+else
+ archiver_list_spec=$lt_cv_ar_at_file
+fi
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$STRIP"; then
+ ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+$as_echo "$STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+ ac_ct_STRIP=$STRIP
+ # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_STRIP"; then
+ ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_STRIP="strip"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+$as_echo "$ac_ct_STRIP" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_STRIP" = x; then
+ STRIP=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ STRIP=$ac_ct_STRIP
+ fi
+else
+ STRIP="$ac_cv_prog_STRIP"
+fi
+
+test -z "$STRIP" && STRIP=:
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_RANLIB" = x; then
+ RANLIB=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ RANLIB=$ac_ct_RANLIB
+ fi
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+test -z "$RANLIB" && RANLIB=:
+
+
+
+
+
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+ case $host_os in
+ bitrig* | openbsd*)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib"
+ ;;
+ *)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib"
+ ;;
+ esac
+ old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib"
+fi
+
+case $host_os in
+ darwin*)
+ lock_old_archive_extraction=yes ;;
+ *)
+ lock_old_archive_extraction=no ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5
+$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; }
+if ${lt_cv_sys_global_symbol_pipe+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix. What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[BCDEGRST]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([_A-Za-z][_A-Za-z0-9]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+ symcode='[BCDT]'
+ ;;
+cygwin* | mingw* | pw32* | cegcc*)
+ symcode='[ABCDGISTW]'
+ ;;
+hpux*)
+ if test ia64 = "$host_cpu"; then
+ symcode='[ABCDEGRST]'
+ fi
+ ;;
+irix* | nonstopux*)
+ symcode='[BCDEGRST]'
+ ;;
+osf*)
+ symcode='[BCDEGQRST]'
+ ;;
+solaris*)
+ symcode='[BDRT]'
+ ;;
+sco3.2v5*)
+ symcode='[DT]'
+ ;;
+sysv4.2uw2*)
+ symcode='[DT]'
+ ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+ symcode='[ABDT]'
+ ;;
+sysv4)
+ symcode='[DFNSTU]'
+ ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+ symcode='[ABCDGIRSTW]' ;;
+esac
+
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Gets list of data symbols to import.
+ lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'"
+ # Adjust the below global symbol transforms to fixup imported variables.
+ lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'"
+ lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'"
+ lt_c_name_lib_hook="\
+ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\
+ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'"
+else
+ # Disable hooks by default.
+ lt_cv_sys_global_symbol_to_import=
+ lt_cdecl_hook=
+ lt_c_name_hook=
+ lt_c_name_lib_hook=
+fi
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="sed -n"\
+$lt_cdecl_hook\
+" -e 's/^T .* \(.*\)$/extern int \1();/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="sed -n"\
+$lt_c_name_hook\
+" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'"
+
+# Transform an extracted symbol line into symbol name with lib prefix and
+# symbol address.
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\
+$lt_c_name_lib_hook\
+" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+ opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+ ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+ # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+ symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+ # Write the raw and C identifiers.
+ if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Fake it for dumpbin and say T for any non-static function,
+ # D for any global variable and I for any imported variable.
+ # Also find C++ and __fastcall symbols from MSVC++,
+ # which start with @ or ?.
+ lt_cv_sys_global_symbol_pipe="$AWK '"\
+" {last_section=section; section=\$ 3};"\
+" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\
+" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+" /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\
+" /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\
+" /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\
+" \$ 0!~/External *\|/{next};"\
+" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+" {if(hide[section]) next};"\
+" {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\
+" {split(\$ 0,a,/\||\r/); split(a[2],s)};"\
+" s[1]~/^[@?]/{print f,s[1],s[1]; next};"\
+" s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\
+" ' prfx=^$ac_symprfx"
+ else
+ lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+ fi
+ lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'"
+
+ # Check to see that the pipe works correctly.
+ pipe_works=no
+
+ rm -f conftest*
+ cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ # Now try to grab the symbols.
+ nlist=conftest.nm
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5
+ (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s "$nlist"; then
+ # Try sorting and uniquifying the output.
+ if sort "$nlist" | uniq > "$nlist"T; then
+ mv -f "$nlist"T "$nlist"
+ else
+ rm -f "$nlist"T
+ fi
+
+ # Make sure that we snagged all the symbols we need.
+ if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+ if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+ cat <<_LT_EOF > conftest.$ac_ext
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */
+#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE
+/* DATA imports from DLLs on WIN32 can't be const, because runtime
+ relocations are performed -- see ld's documentation on pseudo-relocs. */
+# define LT_DLSYM_CONST
+#elif defined __osf__
+/* This system does not cope well with relocations in const data. */
+# define LT_DLSYM_CONST
+#else
+# define LT_DLSYM_CONST const
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+ # Now generate the symbol file.
+ eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+ cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols. */
+LT_DLSYM_CONST struct {
+ const char *name;
+ void *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[] =
+{
+ { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+ $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+ cat <<\_LT_EOF >> conftest.$ac_ext
+ {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+ # Now try linking the two files.
+ mv conftest.$ac_objext conftstm.$ac_objext
+ lt_globsym_save_LIBS=$LIBS
+ lt_globsym_save_CFLAGS=$CFLAGS
+ LIBS=conftstm.$ac_objext
+ CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag"
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s conftest$ac_exeext; then
+ pipe_works=yes
+ fi
+ LIBS=$lt_globsym_save_LIBS
+ CFLAGS=$lt_globsym_save_CFLAGS
+ else
+ echo "cannot find nm_test_func in $nlist" >&5
+ fi
+ else
+ echo "cannot find nm_test_var in $nlist" >&5
+ fi
+ else
+ echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5
+ fi
+ else
+ echo "$progname: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ fi
+ rm -rf conftest* conftst*
+
+ # Do not use the global_symbol_pipe unless it works.
+ if test yes = "$pipe_works"; then
+ break
+ else
+ lt_cv_sys_global_symbol_pipe=
+ fi
+done
+
+fi
+
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+ lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
+$as_echo "failed" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5
+$as_echo "ok" >&6; }
+fi
+
+# Response file support.
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ nm_file_list_spec='@'
+elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then
+ nm_file_list_spec='@'
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5
+$as_echo_n "checking for sysroot... " >&6; }
+
+# Check whether --with-sysroot was given.
+if test "${with_sysroot+set}" = set; then :
+ withval=$with_sysroot;
+else
+ with_sysroot=no
+fi
+
+
+lt_sysroot=
+case $with_sysroot in #(
+ yes)
+ if test yes = "$GCC"; then
+ lt_sysroot=`$CC --print-sysroot 2>/dev/null`
+ fi
+ ;; #(
+ /*)
+ lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"`
+ ;; #(
+ no|'')
+ ;; #(
+ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_sysroot" >&5
+$as_echo "$with_sysroot" >&6; }
+ as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5
+ ;;
+esac
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5
+$as_echo "${lt_sysroot:-no}" >&6; }
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a working dd" >&5
+$as_echo_n "checking for a working dd... " >&6; }
+if ${ac_cv_path_lt_DD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+: ${lt_DD:=$DD}
+if test -z "$lt_DD"; then
+ ac_path_lt_DD_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in dd; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_lt_DD="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_lt_DD" || continue
+if "$ac_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
+ cmp -s conftest.i conftest.out \
+ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=:
+fi
+ $ac_path_lt_DD_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_lt_DD"; then
+ :
+ fi
+else
+ ac_cv_path_lt_DD=$lt_DD
+fi
+
+rm -f conftest.i conftest2.i conftest.out
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_lt_DD" >&5
+$as_echo "$ac_cv_path_lt_DD" >&6; }
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to truncate binary pipes" >&5
+$as_echo_n "checking how to truncate binary pipes... " >&6; }
+if ${lt_cv_truncate_bin+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+lt_cv_truncate_bin=
+if "$ac_cv_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
+ cmp -s conftest.i conftest.out \
+ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1"
+fi
+rm -f conftest.i conftest2.i conftest.out
+test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_truncate_bin" >&5
+$as_echo "$lt_cv_truncate_bin" >&6; }
+
+
+
+
+
+
+
+# Calculate cc_basename. Skip known compiler wrappers and cross-prefix.
+func_cc_basename ()
+{
+ for cc_temp in $*""; do
+ case $cc_temp in
+ compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+ distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+ done
+ func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+}
+
+# Check whether --enable-libtool-lock was given.
+if test "${enable_libtool_lock+set}" = set; then :
+ enableval=$enable_libtool_lock;
+fi
+
+test no = "$enable_libtool_lock" || enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+ # Find out what ABI is being produced by ac_compile, and set mode
+ # options accordingly.
+ echo 'int i;' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *ELF-32*)
+ HPUX_IA64_MODE=32
+ ;;
+ *ELF-64*)
+ HPUX_IA64_MODE=64
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+*-*-irix6*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
+ echo '#line '$LINENO' "configure"' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ if test yes = "$lt_cv_prog_gnu_ld"; then
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -melf32bsmip"
+ ;;
+ *N32*)
+ LD="${LD-ld} -melf32bmipn32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -melf64bmip"
+ ;;
+ esac
+ else
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ LD="${LD-ld} -32"
+ ;;
+ *N32*)
+ LD="${LD-ld} -n32"
+ ;;
+ *64-bit*)
+ LD="${LD-ld} -64"
+ ;;
+ esac
+ fi
+ fi
+ rm -rf conftest*
+ ;;
+
+mips64*-*linux*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
+ echo '#line '$LINENO' "configure"' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ emul=elf
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ emul="${emul}32"
+ ;;
+ *64-bit*)
+ emul="${emul}64"
+ ;;
+ esac
+ case `/usr/bin/file conftest.$ac_objext` in
+ *MSB*)
+ emul="${emul}btsmip"
+ ;;
+ *LSB*)
+ emul="${emul}ltsmip"
+ ;;
+ esac
+ case `/usr/bin/file conftest.$ac_objext` in
+ *N32*)
+ emul="${emul}n32"
+ ;;
+ esac
+ LD="${LD-ld} -m $emul"
+ fi
+ rm -rf conftest*
+ ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly. Note that the listed cases only cover the
+ # situations where additional linker options are needed (such as when
+ # doing 32-bit compilation for a host where ld defaults to 64-bit, or
+ # vice versa); the common cases where no linker options are needed do
+ # not appear in the list.
+ echo 'int i;' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ case `/usr/bin/file conftest.o` in
+ *32-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_i386_fbsd"
+ ;;
+ x86_64-*linux*)
+ case `/usr/bin/file conftest.o` in
+ *x86-64*)
+ LD="${LD-ld} -m elf32_x86_64"
+ ;;
+ *)
+ LD="${LD-ld} -m elf_i386"
+ ;;
+ esac
+ ;;
+ powerpc64le-*linux*)
+ LD="${LD-ld} -m elf32lppclinux"
+ ;;
+ powerpc64-*linux*)
+ LD="${LD-ld} -m elf32ppclinux"
+ ;;
+ s390x-*linux*)
+ LD="${LD-ld} -m elf_s390"
+ ;;
+ sparc64-*linux*)
+ LD="${LD-ld} -m elf32_sparc"
+ ;;
+ esac
+ ;;
+ *64-bit*)
+ case $host in
+ x86_64-*kfreebsd*-gnu)
+ LD="${LD-ld} -m elf_x86_64_fbsd"
+ ;;
+ x86_64-*linux*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ powerpcle-*linux*)
+ LD="${LD-ld} -m elf64lppc"
+ ;;
+ powerpc-*linux*)
+ LD="${LD-ld} -m elf64ppc"
+ ;;
+ s390*-*linux*|s390*-*tpf*)
+ LD="${LD-ld} -m elf64_s390"
+ ;;
+ sparc*-*linux*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+
+*-*-sco3.2v5*)
+ # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+ SAVE_CFLAGS=$CFLAGS
+ CFLAGS="$CFLAGS -belf"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5
+$as_echo_n "checking whether the C compiler needs -belf... " >&6; }
+if ${lt_cv_cc_needs_belf+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ lt_cv_cc_needs_belf=yes
+else
+ lt_cv_cc_needs_belf=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5
+$as_echo "$lt_cv_cc_needs_belf" >&6; }
+ if test yes != "$lt_cv_cc_needs_belf"; then
+ # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+ CFLAGS=$SAVE_CFLAGS
+ fi
+ ;;
+*-*solaris*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
+ echo 'int i;' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ case `/usr/bin/file conftest.o` in
+ *64-bit*)
+ case $lt_cv_prog_gnu_ld in
+ yes*)
+ case $host in
+ i?86-*-solaris*|x86_64-*-solaris*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ sparc*-*-solaris*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ # GNU ld 2.21 introduced _sol2 emulations. Use them if available.
+ if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then
+ LD=${LD-ld}_sol2
+ fi
+ ;;
+ *)
+ if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+ LD="${LD-ld} -64"
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ fi
+ rm -rf conftest*
+ ;;
+esac
+
+need_locks=$enable_libtool_lock
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args.
+set dummy ${ac_tool_prefix}mt; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_MANIFEST_TOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$MANIFEST_TOOL"; then
+ ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL
+if test -n "$MANIFEST_TOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5
+$as_echo "$MANIFEST_TOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_MANIFEST_TOOL"; then
+ ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL
+ # Extract the first word of "mt", so it can be a program name with args.
+set dummy mt; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_MANIFEST_TOOL"; then
+ ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_MANIFEST_TOOL="mt"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL
+if test -n "$ac_ct_MANIFEST_TOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5
+$as_echo "$ac_ct_MANIFEST_TOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_MANIFEST_TOOL" = x; then
+ MANIFEST_TOOL=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL
+ fi
+else
+ MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL"
+fi
+
+test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5
+$as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; }
+if ${lt_cv_path_mainfest_tool+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_path_mainfest_tool=no
+ echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5
+ $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out
+ cat conftest.err >&5
+ if $GREP 'Manifest Tool' conftest.out > /dev/null; then
+ lt_cv_path_mainfest_tool=yes
+ fi
+ rm -f conftest*
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5
+$as_echo "$lt_cv_path_mainfest_tool" >&6; }
+if test yes != "$lt_cv_path_mainfest_tool"; then
+ MANIFEST_TOOL=:
+fi
+
+
+
+
+
+
+ case $host_os in
+ rhapsody* | darwin*)
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dsymutil; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_DSYMUTIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$DSYMUTIL"; then
+ ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+DSYMUTIL=$ac_cv_prog_DSYMUTIL
+if test -n "$DSYMUTIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5
+$as_echo "$DSYMUTIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DSYMUTIL"; then
+ ac_ct_DSYMUTIL=$DSYMUTIL
+ # Extract the first word of "dsymutil", so it can be a program name with args.
+set dummy dsymutil; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_DSYMUTIL"; then
+ ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_DSYMUTIL="dsymutil"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL
+if test -n "$ac_ct_DSYMUTIL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5
+$as_echo "$ac_ct_DSYMUTIL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_DSYMUTIL" = x; then
+ DSYMUTIL=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ DSYMUTIL=$ac_ct_DSYMUTIL
+ fi
+else
+ DSYMUTIL="$ac_cv_prog_DSYMUTIL"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args.
+set dummy ${ac_tool_prefix}nmedit; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_NMEDIT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$NMEDIT"; then
+ ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+NMEDIT=$ac_cv_prog_NMEDIT
+if test -n "$NMEDIT"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5
+$as_echo "$NMEDIT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_NMEDIT"; then
+ ac_ct_NMEDIT=$NMEDIT
+ # Extract the first word of "nmedit", so it can be a program name with args.
+set dummy nmedit; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_NMEDIT"; then
+ ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_NMEDIT="nmedit"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT
+if test -n "$ac_ct_NMEDIT"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5
+$as_echo "$ac_ct_NMEDIT" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_NMEDIT" = x; then
+ NMEDIT=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ NMEDIT=$ac_ct_NMEDIT
+ fi
+else
+ NMEDIT="$ac_cv_prog_NMEDIT"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args.
+set dummy ${ac_tool_prefix}lipo; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_LIPO+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$LIPO"; then
+ ac_cv_prog_LIPO="$LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_LIPO="${ac_tool_prefix}lipo"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+LIPO=$ac_cv_prog_LIPO
+if test -n "$LIPO"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5
+$as_echo "$LIPO" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_LIPO"; then
+ ac_ct_LIPO=$LIPO
+ # Extract the first word of "lipo", so it can be a program name with args.
+set dummy lipo; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_LIPO+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_LIPO"; then
+ ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_LIPO="lipo"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO
+if test -n "$ac_ct_LIPO"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5
+$as_echo "$ac_ct_LIPO" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_LIPO" = x; then
+ LIPO=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ LIPO=$ac_ct_LIPO
+ fi
+else
+ LIPO="$ac_cv_prog_LIPO"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OTOOL"; then
+ ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_OTOOL="${ac_tool_prefix}otool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL=$ac_cv_prog_OTOOL
+if test -n "$OTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5
+$as_echo "$OTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL"; then
+ ac_ct_OTOOL=$OTOOL
+ # Extract the first word of "otool", so it can be a program name with args.
+set dummy otool; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OTOOL+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OTOOL"; then
+ ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_OTOOL="otool"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL
+if test -n "$ac_ct_OTOOL"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5
+$as_echo "$ac_ct_OTOOL" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OTOOL" = x; then
+ OTOOL=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OTOOL=$ac_ct_OTOOL
+ fi
+else
+ OTOOL="$ac_cv_prog_OTOOL"
+fi
+
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool64; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_OTOOL64+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$OTOOL64"; then
+ ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL64=$ac_cv_prog_OTOOL64
+if test -n "$OTOOL64"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5
+$as_echo "$OTOOL64" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL64"; then
+ ac_ct_OTOOL64=$OTOOL64
+ # Extract the first word of "otool64", so it can be a program name with args.
+set dummy otool64; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_OTOOL64"; then
+ ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_OTOOL64="otool64"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64
+if test -n "$ac_ct_OTOOL64"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5
+$as_echo "$ac_ct_OTOOL64" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_OTOOL64" = x; then
+ OTOOL64=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ OTOOL64=$ac_ct_OTOOL64
+ fi
+else
+ OTOOL64="$ac_cv_prog_OTOOL64"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5
+$as_echo_n "checking for -single_module linker flag... " >&6; }
+if ${lt_cv_apple_cc_single_mod+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_apple_cc_single_mod=no
+ if test -z "$LT_MULTI_MODULE"; then
+ # By default we will add the -single_module flag. You can override
+ # by either setting the environment variable LT_MULTI_MODULE
+ # non-empty at configure time, or by adding -multi_module to the
+ # link flags.
+ rm -rf libconftest.dylib*
+ echo "int foo(void){return 1;}" > conftest.c
+ echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&5
+ $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+ _lt_result=$?
+ # If there is a non-empty error log, and "single_module"
+ # appears in it, assume the flag caused a linker warning
+ if test -s conftest.err && $GREP single_module conftest.err; then
+ cat conftest.err >&5
+ # Otherwise, if the output was created with a 0 exit code from
+ # the compiler, it worked.
+ elif test -f libconftest.dylib && test 0 = "$_lt_result"; then
+ lt_cv_apple_cc_single_mod=yes
+ else
+ cat conftest.err >&5
+ fi
+ rm -rf libconftest.dylib*
+ rm -f conftest.*
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5
+$as_echo "$lt_cv_apple_cc_single_mod" >&6; }
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5
+$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; }
+if ${lt_cv_ld_exported_symbols_list+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ld_exported_symbols_list=no
+ save_LDFLAGS=$LDFLAGS
+ echo "_main" > conftest.sym
+ LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ lt_cv_ld_exported_symbols_list=yes
+else
+ lt_cv_ld_exported_symbols_list=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS=$save_LDFLAGS
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5
+$as_echo "$lt_cv_ld_exported_symbols_list" >&6; }
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5
+$as_echo_n "checking for -force_load linker flag... " >&6; }
+if ${lt_cv_ld_force_load+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_ld_force_load=no
+ cat > conftest.c << _LT_EOF
+int forced_loaded() { return 2;}
+_LT_EOF
+ echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5
+ $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5
+ echo "$AR cru libconftest.a conftest.o" >&5
+ $AR cru libconftest.a conftest.o 2>&5
+ echo "$RANLIB libconftest.a" >&5
+ $RANLIB libconftest.a 2>&5
+ cat > conftest.c << _LT_EOF
+int main() { return 0;}
+_LT_EOF
+ echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5
+ $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err
+ _lt_result=$?
+ if test -s conftest.err && $GREP force_load conftest.err; then
+ cat conftest.err >&5
+ elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then
+ lt_cv_ld_force_load=yes
+ else
+ cat conftest.err >&5
+ fi
+ rm -f conftest.err libconftest.a conftest conftest.c
+ rm -rf conftest.dSYM
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5
+$as_echo "$lt_cv_ld_force_load" >&6; }
+ case $host_os in
+ rhapsody* | darwin1.[012])
+ _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;;
+ darwin1.*)
+ _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
+ darwin*) # darwin 5.x on
+ # if running on 10.5 or later, the deployment target defaults
+ # to the OS version, if on x86, and 10.4, the deployment
+ # target defaults to 10.4. Don't you love it?
+ case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
+ 10.0,*86*-darwin8*|10.0,*-darwin[91]*)
+ _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
+ 10.[012][,.]*)
+ _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
+ 10.*)
+ _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
+ esac
+ ;;
+ esac
+ if test yes = "$lt_cv_apple_cc_single_mod"; then
+ _lt_dar_single_mod='$single_module'
+ fi
+ if test yes = "$lt_cv_ld_exported_symbols_list"; then
+ _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym'
+ else
+ _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib'
+ fi
+ if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then
+ _lt_dsymutil='~$DSYMUTIL $lib || :'
+ else
+ _lt_dsymutil=
+ fi
+ ;;
+ esac
+
+# func_munge_path_list VARIABLE PATH
+# -----------------------------------
+# VARIABLE is name of variable containing _space_ separated list of
+# directories to be munged by the contents of PATH, which is string
+# having a format:
+# "DIR[:DIR]:"
+# string "DIR[ DIR]" will be prepended to VARIABLE
+# ":DIR[:DIR]"
+# string "DIR[ DIR]" will be appended to VARIABLE
+# "DIRP[:DIRP]::[DIRA:]DIRA"
+# string "DIRP[ DIRP]" will be prepended to VARIABLE and string
+# "DIRA[ DIRA]" will be appended to VARIABLE
+# "DIR[:DIR]"
+# VARIABLE will be replaced by "DIR[ DIR]"
+func_munge_path_list ()
+{
+ case x$2 in
+ x)
+ ;;
+ *:)
+ eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\"
+ ;;
+ x:*)
+ eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\"
+ ;;
+ *::*)
+ eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\"
+ eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\"
+ ;;
+ *)
+ eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\"
+ ;;
+ esac
+}
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if ${ac_cv_prog_CPP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # Double quotes because CPP needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
+$as_echo_n "checking for ANSI C header files... " >&6; }
+if ${ac_cv_header_stdc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_stdc=yes
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "memchr" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "free" >/dev/null 2>&1; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+ if test "$cross_compiling" = yes; then :
+ :
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ctype.h>
+#include <stdlib.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+ (('a' <= (c) && (c) <= 'i') \
+ || ('j' <= (c) && (c) <= 'r') \
+ || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ if (XOR (islower (i), ISLOWER (i))
+ || toupper (i) != TOUPPER (i))
+ return 2;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+ ac_cv_header_stdc=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
+$as_echo "$ac_cv_header_stdc" >&6; }
+if test $ac_cv_header_stdc = yes; then
+
+$as_echo "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+ inttypes.h stdint.h unistd.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in dlfcn.h
+do :
+ ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default
+"
+if test "x$ac_cv_header_dlfcn_h" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_DLFCN_H 1
+_ACEOF
+
+fi
+
+done
+
+
+
+
+func_stripname_cnf ()
+{
+ case $2 in
+ .*) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%\\\\$2\$%%"`;;
+ *) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%$2\$%%"`;;
+ esac
+} # func_stripname_cnf
+
+
+
+
+
+# Set options
+
+
+
+ enable_dlopen=no
+
+
+ enable_win32_dll=no
+
+
+ # Check whether --enable-shared was given.
+if test "${enable_shared+set}" = set; then :
+ enableval=$enable_shared; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_shared=yes ;;
+ no) enable_shared=no ;;
+ *)
+ enable_shared=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for pkg in $enableval; do
+ IFS=$lt_save_ifs
+ if test "X$pkg" = "X$p"; then
+ enable_shared=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac
+else
+ enable_shared=yes
+fi
+
+
+
+
+
+
+
+
+
+ # Check whether --enable-static was given.
+if test "${enable_static+set}" = set; then :
+ enableval=$enable_static; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_static=yes ;;
+ no) enable_static=no ;;
+ *)
+ enable_static=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for pkg in $enableval; do
+ IFS=$lt_save_ifs
+ if test "X$pkg" = "X$p"; then
+ enable_static=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac
+else
+ enable_static=yes
+fi
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-pic was given.
+if test "${with_pic+set}" = set; then :
+ withval=$with_pic; lt_p=${PACKAGE-default}
+ case $withval in
+ yes|no) pic_mode=$withval ;;
+ *)
+ pic_mode=default
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for lt_pkg in $withval; do
+ IFS=$lt_save_ifs
+ if test "X$lt_pkg" = "X$lt_p"; then
+ pic_mode=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac
+else
+ pic_mode=default
+fi
+
+
+
+
+
+
+
+
+ # Check whether --enable-fast-install was given.
+if test "${enable_fast_install+set}" = set; then :
+ enableval=$enable_fast_install; p=${PACKAGE-default}
+ case $enableval in
+ yes) enable_fast_install=yes ;;
+ no) enable_fast_install=no ;;
+ *)
+ enable_fast_install=no
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for pkg in $enableval; do
+ IFS=$lt_save_ifs
+ if test "X$pkg" = "X$p"; then
+ enable_fast_install=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac
+else
+ enable_fast_install=yes
+fi
+
+
+
+
+
+
+
+
+ shared_archive_member_spec=
+case $host,$enable_shared in
+power*-*-aix[5-9]*,yes)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking which variant of shared library versioning to provide" >&5
+$as_echo_n "checking which variant of shared library versioning to provide... " >&6; }
+
+# Check whether --with-aix-soname was given.
+if test "${with_aix_soname+set}" = set; then :
+ withval=$with_aix_soname; case $withval in
+ aix|svr4|both)
+ ;;
+ *)
+ as_fn_error $? "Unknown argument to --with-aix-soname" "$LINENO" 5
+ ;;
+ esac
+ lt_cv_with_aix_soname=$with_aix_soname
+else
+ if ${lt_cv_with_aix_soname+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_with_aix_soname=aix
+fi
+
+ with_aix_soname=$lt_cv_with_aix_soname
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_aix_soname" >&5
+$as_echo "$with_aix_soname" >&6; }
+ if test aix != "$with_aix_soname"; then
+ # For the AIX way of multilib, we name the shared archive member
+ # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o',
+ # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File.
+ # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag,
+ # the AIX toolchain works better with OBJECT_MODE set (default 32).
+ if test 64 = "${OBJECT_MODE-32}"; then
+ shared_archive_member_spec=shr_64
+ else
+ shared_archive_member_spec=shr
+ fi
+ fi
+ ;;
+*)
+ with_aix_soname=aix
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS=$ltmain
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+test -z "$LN_S" && LN_S="ln -s"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "${ZSH_VERSION+set}"; then
+ setopt NO_GLOB_SUBST
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5
+$as_echo_n "checking for objdir... " >&6; }
+if ${lt_cv_objdir+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+ lt_cv_objdir=.libs
+else
+ # MS-DOS does not allow filenames that begin with a dot.
+ lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5
+$as_echo "$lt_cv_objdir" >&6; }
+objdir=$lt_cv_objdir
+
+
+
+
+
+cat >>confdefs.h <<_ACEOF
+#define LT_OBJDIR "$lt_cv_objdir/"
+_ACEOF
+
+
+
+
+case $host_os in
+aix3*)
+ # AIX sometimes has problems with the GCC collect2 program. For some
+ # reason, if we set the COLLECT_NAMES environment variable, the problems
+ # vanish in a puff of smoke.
+ if test set != "${COLLECT_NAMES+set}"; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+ fi
+ ;;
+esac
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a '.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+old_CC=$CC
+old_CFLAGS=$CFLAGS
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+func_cc_basename $compiler
+cc_basename=$func_cc_basename_result
+
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+ if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5
+$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; }
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $MAGIC_CMD in
+[\\/*] | ?:[\\/]*)
+ lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD=$MAGIC_CMD
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+ ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+ for ac_dir in $ac_dummy; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/${ac_tool_prefix}file"; then
+ lt_cv_path_MAGIC_CMD=$ac_dir/"${ac_tool_prefix}file"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS=$lt_save_ifs
+ MAGIC_CMD=$lt_save_MAGIC_CMD
+ ;;
+esac
+fi
+
+MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+if test -n "$MAGIC_CMD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+
+
+
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+ if test -n "$ac_tool_prefix"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5
+$as_echo_n "checking for file... " >&6; }
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ case $MAGIC_CMD in
+[\\/*] | ?:[\\/]*)
+ lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.
+ ;;
+*)
+ lt_save_MAGIC_CMD=$MAGIC_CMD
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+ ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+ for ac_dir in $ac_dummy; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/file"; then
+ lt_cv_path_MAGIC_CMD=$ac_dir/"file"
+ if test -n "$file_magic_test_file"; then
+ case $deplibs_check_method in
+ "file_magic "*)
+ file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+ MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+ if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ :
+ else
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such. This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem. Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+ fi ;;
+ esac
+ fi
+ break
+ fi
+ done
+ IFS=$lt_save_ifs
+ MAGIC_CMD=$lt_save_MAGIC_CMD
+ ;;
+esac
+fi
+
+MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+if test -n "$MAGIC_CMD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+$as_echo "$MAGIC_CMD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ else
+ MAGIC_CMD=:
+ fi
+fi
+
+ fi
+ ;;
+esac
+
+# Use C for the default configuration in the libtool script
+
+lt_save_CC=$CC
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+objext=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+
+
+if test -n "$compiler"; then
+
+lt_prog_compiler_no_builtin_flag=
+
+if test yes = "$GCC"; then
+ case $cc_basename in
+ nvcc*)
+ lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;;
+ *)
+ lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;;
+ esac
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
+$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; }
+if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_rtti_exceptions=no
+ ac_outfile=conftest.$ac_objext
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="-fno-rtti -fno-exceptions" ## exclude from sc_useless_quotes_in_assignment
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_rtti_exceptions=yes
+ fi
+ fi
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5
+$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; }
+
+if test yes = "$lt_cv_prog_compiler_rtti_exceptions"; then
+ lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions"
+else
+ :
+fi
+
+fi
+
+
+
+
+
+
+ lt_prog_compiler_wl=
+lt_prog_compiler_pic=
+lt_prog_compiler_static=
+
+
+ if test yes = "$GCC"; then
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_static='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 now supports IA64 processor
+ lt_prog_compiler_static='-Bstatic'
+ fi
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the '-m68020' flag to GCC prevents building anything better,
+ # like '-m68040'.
+ lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ lt_prog_compiler_pic='-DDLL_EXPORT'
+ case $host_os in
+ os2*)
+ lt_prog_compiler_static='$wl-static'
+ ;;
+ esac
+ ;;
+
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ lt_prog_compiler_pic='-fno-common'
+ ;;
+
+ haiku*)
+ # PIC is the default for Haiku.
+ # The "-static" flag exists, but is broken.
+ lt_prog_compiler_static=
+ ;;
+
+ hpux*)
+ # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+ # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
+ # sets the default TLS model and affects inlining.
+ case $host_cpu in
+ hppa*64*)
+ # +Z the default
+ ;;
+ *)
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ esac
+ ;;
+
+ interix[3-9]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+
+ msdosdjgpp*)
+ # Just because we use GCC doesn't mean we suddenly get shared libraries
+ # on systems that don't support them.
+ lt_prog_compiler_can_build_shared=no
+ enable_shared=no
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ lt_prog_compiler_pic='-fPIC -shared'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ lt_prog_compiler_pic=-Kconform_pic
+ fi
+ ;;
+
+ *)
+ lt_prog_compiler_pic='-fPIC'
+ ;;
+ esac
+
+ case $cc_basename in
+ nvcc*) # Cuda Compiler Driver 2.2
+ lt_prog_compiler_wl='-Xlinker '
+ if test -n "$lt_prog_compiler_pic"; then
+ lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic"
+ fi
+ ;;
+ esac
+ else
+ # PORTME Check for flag to pass linker flags through the system compiler.
+ case $host_os in
+ aix*)
+ lt_prog_compiler_wl='-Wl,'
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 now supports IA64 processor
+ lt_prog_compiler_static='-Bstatic'
+ else
+ lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ lt_prog_compiler_pic='-fno-common'
+ case $cc_basename in
+ nagfor*)
+ # NAG Fortran compiler
+ lt_prog_compiler_wl='-Wl,-Wl,,'
+ lt_prog_compiler_pic='-PIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+ esac
+ ;;
+
+ mingw* | cygwin* | pw32* | os2* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ lt_prog_compiler_pic='-DDLL_EXPORT'
+ case $host_os in
+ os2*)
+ lt_prog_compiler_static='$wl-static'
+ ;;
+ esac
+ ;;
+
+ hpux9* | hpux10* | hpux11*)
+ lt_prog_compiler_wl='-Wl,'
+ # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+ # not for PA HP-UX.
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ lt_prog_compiler_pic='+Z'
+ ;;
+ esac
+ # Is there a better lt_prog_compiler_static that works with the bundled CC?
+ lt_prog_compiler_static='$wl-a ${wl}archive'
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ lt_prog_compiler_wl='-Wl,'
+ # PIC (with -KPIC) is the default.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ case $cc_basename in
+ # old Intel for x86_64, which still supported -KPIC.
+ ecc*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ # icc used to be incompatible with GCC.
+ # ICC 10 doesn't accept -KPIC any more.
+ icc* | ifort*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ # Lahey Fortran 8.1.
+ lf95*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='--shared'
+ lt_prog_compiler_static='--static'
+ ;;
+ nagfor*)
+ # NAG Fortran compiler
+ lt_prog_compiler_wl='-Wl,-Wl,,'
+ lt_prog_compiler_pic='-PIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+ tcc*)
+ # Fabrice Bellard et al's Tiny C Compiler
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*)
+ # Portland Group compilers (*not* the Pentium gcc compiler,
+ # which looks to be a dead project)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fpic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+ ccc*)
+ lt_prog_compiler_wl='-Wl,'
+ # All Alpha code is PIC.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+ xl* | bgxl* | bgf* | mpixl*)
+ # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-qpic'
+ lt_prog_compiler_static='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*)
+ # Sun Fortran 8.3 passes all unrecognized flags to the linker
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl=''
+ ;;
+ *Sun\ F* | *Sun*Fortran*)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl='-Qoption ld '
+ ;;
+ *Sun\ C*)
+ # Sun C 5.9
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl='-Wl,'
+ ;;
+ *Intel*\ [CF]*Compiler*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ *Portland\ Group*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fpic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ newsos6)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ *nto* | *qnx*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ lt_prog_compiler_pic='-fPIC -shared'
+ ;;
+
+ osf3* | osf4* | osf5*)
+ lt_prog_compiler_wl='-Wl,'
+ # All OSF/1 code is PIC.
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ rdos*)
+ lt_prog_compiler_static='-non_shared'
+ ;;
+
+ solaris*)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ case $cc_basename in
+ f77* | f90* | f95* | sunf77* | sunf90* | sunf95*)
+ lt_prog_compiler_wl='-Qoption ld ';;
+ *)
+ lt_prog_compiler_wl='-Wl,';;
+ esac
+ ;;
+
+ sunos4*)
+ lt_prog_compiler_wl='-Qoption ld '
+ lt_prog_compiler_pic='-PIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ sysv4 | sysv4.2uw2* | sysv4.3*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ lt_prog_compiler_pic='-Kconform_pic'
+ lt_prog_compiler_static='-Bstatic'
+ fi
+ ;;
+
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ unicos*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_can_build_shared=no
+ ;;
+
+ uts4*)
+ lt_prog_compiler_pic='-pic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+
+ *)
+ lt_prog_compiler_can_build_shared=no
+ ;;
+ esac
+ fi
+
+case $host_os in
+ # For platforms that do not support PIC, -DPIC is meaningless:
+ *djgpp*)
+ lt_prog_compiler_pic=
+ ;;
+ *)
+ lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC"
+ ;;
+esac
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5
+$as_echo_n "checking for $compiler option to produce PIC... " >&6; }
+if ${lt_cv_prog_compiler_pic+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_pic=$lt_prog_compiler_pic
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5
+$as_echo "$lt_cv_prog_compiler_pic" >&6; }
+lt_prog_compiler_pic=$lt_cv_prog_compiler_pic
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$lt_prog_compiler_pic"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5
+$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; }
+if ${lt_cv_prog_compiler_pic_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_pic_works=no
+ ac_outfile=conftest.$ac_objext
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="$lt_prog_compiler_pic -DPIC" ## exclude from sc_useless_quotes_in_assignment
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_pic_works=yes
+ fi
+ fi
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5
+$as_echo "$lt_cv_prog_compiler_pic_works" >&6; }
+
+if test yes = "$lt_cv_prog_compiler_pic_works"; then
+ case $lt_prog_compiler_pic in
+ "" | " "*) ;;
+ *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;;
+ esac
+else
+ lt_prog_compiler_pic=
+ lt_prog_compiler_can_build_shared=no
+fi
+
+fi
+
+
+
+
+
+
+
+
+
+
+
+#
+# Check to make sure the static flag actually works.
+#
+wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5
+$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; }
+if ${lt_cv_prog_compiler_static_works+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_static_works=no
+ save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS $lt_tmp_static_flag"
+ echo "$lt_simple_link_test_code" > conftest.$ac_ext
+ if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+ # The linker can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ # Append any errors to the config.log.
+ cat conftest.err 1>&5
+ $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_static_works=yes
+ fi
+ else
+ lt_cv_prog_compiler_static_works=yes
+ fi
+ fi
+ $RM -r conftest*
+ LDFLAGS=$save_LDFLAGS
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5
+$as_echo "$lt_cv_prog_compiler_static_works" >&6; }
+
+if test yes = "$lt_cv_prog_compiler_static_works"; then
+ :
+else
+ lt_prog_compiler_static=
+fi
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_c_o=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_c_o=yes
+ fi
+ fi
+ chmod u+w . 2>&5
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_c_o=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_c_o=yes
+ fi
+ fi
+ chmod u+w . 2>&5
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+$as_echo "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+hard_links=nottested
+if test no = "$lt_cv_prog_compiler_c_o" && test no != "$need_locks"; then
+ # do not overwrite the value of need_locks provided by the user
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5
+$as_echo_n "checking if we can lock with hard links... " >&6; }
+ hard_links=yes
+ $RM conftest*
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ touch conftest.a
+ ln conftest.a conftest.b 2>&5 || hard_links=no
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5
+$as_echo "$hard_links" >&6; }
+ if test no = "$hard_links"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5
+$as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;}
+ need_locks=warn
+ fi
+else
+ need_locks=no
+fi
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; }
+
+ runpath_var=
+ allow_undefined_flag=
+ always_export_symbols=no
+ archive_cmds=
+ archive_expsym_cmds=
+ compiler_needs_object=no
+ enable_shared_with_static_runtimes=no
+ export_dynamic_flag_spec=
+ export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ hardcode_automatic=no
+ hardcode_direct=no
+ hardcode_direct_absolute=no
+ hardcode_libdir_flag_spec=
+ hardcode_libdir_separator=
+ hardcode_minus_L=no
+ hardcode_shlibpath_var=unsupported
+ inherit_rpath=no
+ link_all_deplibs=unknown
+ module_cmds=
+ module_expsym_cmds=
+ old_archive_from_new_cmds=
+ old_archive_from_expsyms_cmds=
+ thread_safe_flag_spec=
+ whole_archive_flag_spec=
+ # include_expsyms should be a list of space-separated symbols to be *always*
+ # included in the symbol list
+ include_expsyms=
+ # exclude_expsyms can be an extended regexp of symbols to exclude
+ # it will be wrapped by ' (' and ')$', so one must not match beginning or
+ # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc',
+ # as well as any symbol that contains 'd'.
+ exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
+ # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+ # platforms (ab)use it in PIC code, but their linkers get confused if
+ # the symbol is explicitly referenced. Since portable code cannot
+ # rely on this symbol name, it's probably fine to never include it in
+ # preloaded symbol tables.
+ # Exclude shared library initialization/finalization symbols.
+ extract_expsyms_cmds=
+
+ case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ # FIXME: the MSVC++ port hasn't been tested in a loooong time
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ if test yes != "$GCC"; then
+ with_gnu_ld=no
+ fi
+ ;;
+ interix*)
+ # we just hope/assume this is gcc and not c89 (= MSVC++)
+ with_gnu_ld=yes
+ ;;
+ openbsd* | bitrig*)
+ with_gnu_ld=no
+ ;;
+ linux* | k*bsd*-gnu | gnu*)
+ link_all_deplibs=no
+ ;;
+ esac
+
+ ld_shlibs=yes
+
+ # On some targets, GNU ld is compatible enough with the native linker
+ # that we're better off using the native interface for both.
+ lt_use_gnu_ld_interface=no
+ if test yes = "$with_gnu_ld"; then
+ case $host_os in
+ aix*)
+ # The AIX port of GNU ld has always aspired to compatibility
+ # with the native linker. However, as the warning in the GNU ld
+ # block says, versions before 2.19.5* couldn't really create working
+ # shared libraries, regardless of the interface used.
+ case `$LD -v 2>&1` in
+ *\ \(GNU\ Binutils\)\ 2.19.5*) ;;
+ *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;;
+ *\ \(GNU\ Binutils\)\ [3-9]*) ;;
+ *)
+ lt_use_gnu_ld_interface=yes
+ ;;
+ esac
+ ;;
+ *)
+ lt_use_gnu_ld_interface=yes
+ ;;
+ esac
+ fi
+
+ if test yes = "$lt_use_gnu_ld_interface"; then
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ wlarc='$wl'
+
+ # Set some defaults for GNU ld with shared library support. These
+ # are reset later if shared libraries are not supported. Putting them
+ # here allows them to be overridden if necessary.
+ runpath_var=LD_RUN_PATH
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ export_dynamic_flag_spec='$wl--export-dynamic'
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+ whole_archive_flag_spec=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
+ else
+ whole_archive_flag_spec=
+ fi
+ supports_anon_versioning=no
+ case `$LD -v | $SED -e 's/(^)\+)\s\+//' 2>&1` in
+ *GNU\ gold*) supports_anon_versioning=yes ;;
+ *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11
+ *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+ *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+ *\ 2.11.*) ;; # other 2.11 versions
+ *) supports_anon_versioning=yes ;;
+ esac
+
+ # See if GNU ld supports shared libraries.
+ case $host_os in
+ aix[3-9]*)
+ # On AIX/PPC, the GNU linker is very broken
+ if test ia64 != "$host_cpu"; then
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.19, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support. If you
+*** really care for shared libraries, you may want to install binutils
+*** 2.20 or above, or modify your PATH so that a non-GNU linker is found.
+*** You will then need to restart the configuration process.
+
+_LT_EOF
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds=''
+ ;;
+ m68k)
+ archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ ;;
+ esac
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ allow_undefined_flag=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless,
+ # as there is no search path for DLLs.
+ hardcode_libdir_flag_spec='-L$libdir'
+ export_dynamic_flag_spec='$wl--export-all-symbols'
+ allow_undefined_flag=unsupported
+ always_export_symbols=no
+ enable_shared_with_static_runtimes=yes
+ export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols'
+ exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file, use it as
+ # is; otherwise, prepend EXPORTS...
+ archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ haiku*)
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ link_all_deplibs=yes
+ ;;
+
+ os2*)
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ allow_undefined_flag=unsupported
+ shrext_cmds=.dll
+ archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ prefix_cmds="$SED"~
+ if test EXPORTS = "`$SED 1q $export_symbols`"; then
+ prefix_cmds="$prefix_cmds -e 1d";
+ fi~
+ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+ enable_shared_with_static_runtimes=yes
+ ;;
+
+ interix[3-9]*)
+ hardcode_direct=no
+ hardcode_shlibpath_var=no
+ hardcode_libdir_flag_spec='$wl-rpath,$libdir'
+ export_dynamic_flag_spec='$wl-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ archive_expsym_cmds='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+
+ gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
+ tmp_diet=no
+ if test linux-dietlibc = "$host_os"; then
+ case $cc_basename in
+ diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn)
+ esac
+ fi
+ if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+ && test no = "$tmp_diet"
+ then
+ tmp_addflag=' $pic_flag'
+ tmp_sharedflag='-shared'
+ case $cc_basename,$host_cpu in
+ pgcc*) # Portland Group C compiler
+ whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ tmp_addflag=' $pic_flag'
+ ;;
+ pgf77* | pgf90* | pgf95* | pgfortran*)
+ # Portland Group f77 and f90 compilers
+ whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ tmp_addflag=' $pic_flag -Mnomain' ;;
+ ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64
+ tmp_addflag=' -i_dynamic' ;;
+ efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64
+ tmp_addflag=' -i_dynamic -nofor_main' ;;
+ ifc* | ifort*) # Intel Fortran compiler
+ tmp_addflag=' -nofor_main' ;;
+ lf95*) # Lahey Fortran 8.1
+ whole_archive_flag_spec=
+ tmp_sharedflag='--shared' ;;
+ nagfor*) # NAGFOR 5.3
+ tmp_sharedflag='-Wl,-shared' ;;
+ xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+ tmp_sharedflag='-qmkshrobj'
+ tmp_addflag= ;;
+ nvcc*) # Cuda Compiler Driver 2.2
+ whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ compiler_needs_object=yes
+ ;;
+ esac
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*) # Sun C 5.9
+ whole_archive_flag_spec='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ compiler_needs_object=yes
+ tmp_sharedflag='-G' ;;
+ *Sun\ F*) # Sun Fortran 8.3
+ tmp_sharedflag='-G' ;;
+ esac
+ archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+
+ if test yes = "$supports_anon_versioning"; then
+ archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib'
+ fi
+
+ case $cc_basename in
+ tcc*)
+ export_dynamic_flag_spec='-rdynamic'
+ ;;
+ xlf* | bgf* | bgxlf* | mpixlf*)
+ # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+ whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib'
+ if test yes = "$supports_anon_versioning"; then
+ archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ esac
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ netbsd* | netbsdelf*-gnu)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+ wlarc=
+ else
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ fi
+ ;;
+
+ solaris*)
+ if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+ case `$LD -v 2>&1` in
+ *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*)
+ ld_shlibs=no
+ cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot
+*** reliably create shared libraries on SCO systems. Therefore, libtool
+*** is disabling shared libraries support. We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer. Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+ ;;
+ *)
+ # For security reasons, it is highly recommended that you always
+ # use absolute paths for naming shared libraries, and exclude the
+ # DT_RUNPATH tag from executables and libraries. But doing so
+ # requires that you compile everything twice, which is a pain.
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ esac
+ ;;
+
+ sunos4*)
+ archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ wlarc=
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ *)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ else
+ ld_shlibs=no
+ fi
+ ;;
+ esac
+
+ if test no = "$ld_shlibs"; then
+ runpath_var=
+ hardcode_libdir_flag_spec=
+ export_dynamic_flag_spec=
+ whole_archive_flag_spec=
+ fi
+ else
+ # PORTME fill in a description of your system's linker (not GNU ld)
+ case $host_os in
+ aix3*)
+ allow_undefined_flag=unsupported
+ always_export_symbols=yes
+ archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+ # Note: this linker hardcodes the directories in LIBPATH if there
+ # are no directories specified by -L.
+ hardcode_minus_L=yes
+ if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then
+ # Neither direct hardcoding nor static linking is supported with a
+ # broken collect2.
+ hardcode_direct=unsupported
+ fi
+ ;;
+
+ aix[4-9]*)
+ if test ia64 = "$host_cpu"; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=
+ else
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to GNU nm, but means don't demangle to AIX nm.
+ # Without the "-l" option, or with the "-B" option, AIX nm treats
+ # weak defined symbols like other global defined symbols, whereas
+ # GNU nm marks them as "W".
+ # While the 'weak' keyword is ignored in the Export File, we need
+ # it in the Import File for the 'aix-soname' feature, so we have
+ # to replace the "-B" option with "-P" for AIX nm.
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols'
+ else
+ export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols'
+ fi
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # have runtime linking enabled, and use it for executables.
+ # For shared libraries, we enable/disable runtime linking
+ # depending on the kind of the shared library created -
+ # when "with_aix_soname,aix_use_runtimelinking" is:
+ # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables
+ # "aix,yes" lib.so shared, rtl:yes, for executables
+ # lib.a static archive
+ # "both,no" lib.so.V(shr.o) shared, rtl:yes
+ # lib.a(lib.so.V) shared, rtl:no, for executables
+ # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a(lib.so.V) shared, rtl:no
+ # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a static archive
+ case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
+ for ld_flag in $LDFLAGS; do
+ if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then
+ aix_use_runtimelinking=yes
+ break
+ fi
+ done
+ if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # With aix-soname=svr4, we create the lib.so.V shared archives only,
+ # so we don't have lib.a shared libs to link our executables.
+ # We have to force runtime linking in this case.
+ aix_use_runtimelinking=yes
+ LDFLAGS="$LDFLAGS -Wl,-brtl"
+ fi
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ archive_cmds=''
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ hardcode_libdir_separator=':'
+ link_all_deplibs=yes
+ file_list_spec='$wl-f,'
+ case $with_aix_soname,$aix_use_runtimelinking in
+ aix,*) ;; # traditional, no import file
+ svr4,* | *,yes) # use import file
+ # The Import File defines what to hardcode.
+ hardcode_direct=no
+ hardcode_direct_absolute=no
+ ;;
+ esac
+
+ if test yes = "$GCC"; then
+ case $host_os in aix4.[012]|aix4.[012].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`$CC -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ hardcode_direct=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ hardcode_minus_L=yes
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_libdir_separator=
+ fi
+ ;;
+ esac
+ shared_flag='-shared'
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag="$shared_flag "'$wl-G'
+ fi
+ # Need to ensure runtime linking is disabled for the traditional
+ # shared library, or the linker may eventually find shared libraries
+ # /with/ Import File - we do not want to mix them.
+ shared_flag_aix='-shared'
+ shared_flag_svr4='-shared $wl-G'
+ else
+ # not using gcc
+ if test ia64 = "$host_cpu"; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag='$wl-G'
+ else
+ shared_flag='$wl-bM:SRE'
+ fi
+ shared_flag_aix='$wl-bM:SRE'
+ shared_flag_svr4='$wl-G'
+ fi
+ fi
+
+ export_dynamic_flag_spec='$wl-bexpall'
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to export.
+ always_export_symbols=yes
+ if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ allow_undefined_flag='-berok'
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ if test set = "${lt_cv_aix_libpath+set}"; then
+ aix_libpath=$lt_cv_aix_libpath
+else
+ if ${lt_cv_aix_libpath_+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+ lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\([^ ]*\) *$/\1/
+ p
+ }
+ }'
+ lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ # Check for a 64-bit object if we didn't find anything.
+ if test -z "$lt_cv_aix_libpath_"; then
+ lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ if test -z "$lt_cv_aix_libpath_"; then
+ lt_cv_aix_libpath_=/usr/lib:/lib
+ fi
+
+fi
+
+ aix_libpath=$lt_cv_aix_libpath_
+fi
+
+ hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath"
+ archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag
+ else
+ if test ia64 = "$host_cpu"; then
+ hardcode_libdir_flag_spec='$wl-R $libdir:/usr/lib:/lib'
+ allow_undefined_flag="-z nodefs"
+ archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ if test set = "${lt_cv_aix_libpath+set}"; then
+ aix_libpath=$lt_cv_aix_libpath
+else
+ if ${lt_cv_aix_libpath_+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+ lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\([^ ]*\) *$/\1/
+ p
+ }
+ }'
+ lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ # Check for a 64-bit object if we didn't find anything.
+ if test -z "$lt_cv_aix_libpath_"; then
+ lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ if test -z "$lt_cv_aix_libpath_"; then
+ lt_cv_aix_libpath_=/usr/lib:/lib
+ fi
+
+fi
+
+ aix_libpath=$lt_cv_aix_libpath_
+fi
+
+ hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ no_undefined_flag=' $wl-bernotok'
+ allow_undefined_flag=' $wl-berok'
+ if test yes = "$with_gnu_ld"; then
+ # We only use this code for GNU lds that support --whole-archive.
+ whole_archive_flag_spec='$wl--whole-archive$convenience $wl--no-whole-archive'
+ else
+ # Exported symbols can be pulled into shared objects from archives
+ whole_archive_flag_spec='$convenience'
+ fi
+ archive_cmds_need_lc=yes
+ archive_expsym_cmds='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d'
+ # -brtl affects multiple linker settings, -berok does not and is overridden later
+ compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`'
+ if test svr4 != "$with_aix_soname"; then
+ # This is similar to how AIX traditionally builds its shared libraries.
+ archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname'
+ fi
+ if test aix != "$with_aix_soname"; then
+ archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp'
+ else
+ # used by -dlpreopen to get the symbols
+ archive_expsym_cmds="$archive_expsym_cmds"'~$MV $output_objdir/$realname.d/$soname $output_objdir'
+ fi
+ archive_expsym_cmds="$archive_expsym_cmds"'~$RM -r $output_objdir/$realname.d'
+ fi
+ fi
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds=''
+ ;;
+ m68k)
+ archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ ;;
+ esac
+ ;;
+
+ bsdi[45]*)
+ export_dynamic_flag_spec=-rdynamic
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ # When not using gcc, we currently assume that we are using
+ # Microsoft Visual C++.
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ case $cc_basename in
+ cl*)
+ # Native MSVC
+ hardcode_libdir_flag_spec=' '
+ allow_undefined_flag=unsupported
+ always_export_symbols=yes
+ file_list_spec='@'
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=.dll
+ # FIXME: Setting linknames here is a bad hack.
+ archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames='
+ archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then
+ cp "$export_symbols" "$output_objdir/$soname.def";
+ echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp";
+ else
+ $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp;
+ fi~
+ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+ linknames='
+ # The linker will not automatically build a static lib if we build a DLL.
+ # _LT_TAGVAR(old_archive_from_new_cmds, )='true'
+ enable_shared_with_static_runtimes=yes
+ exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+ export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols'
+ # Don't use ranlib
+ old_postinstall_cmds='chmod 644 $oldlib'
+ postlink_cmds='lt_outputfile="@OUTPUT@"~
+ lt_tool_outputfile="@TOOL_OUTPUT@"~
+ case $lt_outputfile in
+ *.exe|*.EXE) ;;
+ *)
+ lt_outputfile=$lt_outputfile.exe
+ lt_tool_outputfile=$lt_tool_outputfile.exe
+ ;;
+ esac~
+ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then
+ $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+ $RM "$lt_outputfile.manifest";
+ fi'
+ ;;
+ *)
+ # Assume MSVC wrapper
+ hardcode_libdir_flag_spec=' '
+ allow_undefined_flag=unsupported
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=.dll
+ # FIXME: Setting linknames here is a bad hack.
+ archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames='
+ # The linker will automatically build a .lib file if we build a DLL.
+ old_archive_from_new_cmds='true'
+ # FIXME: Should let the user specify the lib program.
+ old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs'
+ enable_shared_with_static_runtimes=yes
+ ;;
+ esac
+ ;;
+
+ darwin* | rhapsody*)
+
+
+ archive_cmds_need_lc=no
+ hardcode_direct=no
+ hardcode_automatic=yes
+ hardcode_shlibpath_var=unsupported
+ if test yes = "$lt_cv_ld_force_load"; then
+ whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
+
+ else
+ whole_archive_flag_spec=''
+ fi
+ link_all_deplibs=yes
+ allow_undefined_flag=$_lt_dar_allow_undefined
+ case $cc_basename in
+ ifort*|nagfor*) _lt_dar_can_shared=yes ;;
+ *) _lt_dar_can_shared=$GCC ;;
+ esac
+ if test yes = "$_lt_dar_can_shared"; then
+ output_verbose_link_cmd=func_echo_all
+ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil"
+ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil"
+ archive_expsym_cmds="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil"
+ module_expsym_cmds="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil"
+
+ else
+ ld_shlibs=no
+ fi
+
+ ;;
+
+ dgux*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_shlibpath_var=no
+ ;;
+
+ # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+ # support. Future versions do this automatically, but an explicit c++rt0.o
+ # does not break anything, and helps significantly (at the cost of a little
+ # extra space).
+ freebsd2.2*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+ freebsd2.*)
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+ freebsd* | dragonfly*)
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ hpux9*)
+ if test yes = "$GCC"; then
+ archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+ else
+ archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+ fi
+ hardcode_libdir_flag_spec='$wl+b $wl$libdir'
+ hardcode_libdir_separator=:
+ hardcode_direct=yes
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ export_dynamic_flag_spec='$wl-E'
+ ;;
+
+ hpux10*)
+ if test yes,no = "$GCC,$with_gnu_ld"; then
+ archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ if test no = "$with_gnu_ld"; then
+ hardcode_libdir_flag_spec='$wl+b $wl$libdir'
+ hardcode_libdir_separator=:
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ export_dynamic_flag_spec='$wl-E'
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ fi
+ ;;
+
+ hpux11*)
+ if test yes,no = "$GCC,$with_gnu_ld"; then
+ case $host_cpu in
+ hppa*64*)
+ archive_cmds='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ else
+ case $host_cpu in
+ hppa*64*)
+ archive_cmds='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ ia64*)
+ archive_cmds='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+
+ # Older versions of the 11.00 compiler do not understand -b yet
+ # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5
+$as_echo_n "checking if $CC understands -b... " >&6; }
+if ${lt_cv_prog_compiler__b+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler__b=no
+ save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS -b"
+ echo "$lt_simple_link_test_code" > conftest.$ac_ext
+ if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+ # The linker can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ # Append any errors to the config.log.
+ cat conftest.err 1>&5
+ $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler__b=yes
+ fi
+ else
+ lt_cv_prog_compiler__b=yes
+ fi
+ fi
+ $RM -r conftest*
+ LDFLAGS=$save_LDFLAGS
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5
+$as_echo "$lt_cv_prog_compiler__b" >&6; }
+
+if test yes = "$lt_cv_prog_compiler__b"; then
+ archive_cmds='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+else
+ archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+fi
+
+ ;;
+ esac
+ fi
+ if test no = "$with_gnu_ld"; then
+ hardcode_libdir_flag_spec='$wl+b $wl$libdir'
+ hardcode_libdir_separator=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ hardcode_direct=no
+ hardcode_shlibpath_var=no
+ ;;
+ *)
+ hardcode_direct=yes
+ hardcode_direct_absolute=yes
+ export_dynamic_flag_spec='$wl-E'
+
+ # hardcode_minus_L: Not really in the search PATH,
+ # but as the default location of the library.
+ hardcode_minus_L=yes
+ ;;
+ esac
+ fi
+ ;;
+
+ irix5* | irix6* | nonstopux*)
+ if test yes = "$GCC"; then
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ # Try to use the -exported_symbol ld option, if it does not
+ # work, assume that -exports_file does not work either and
+ # implicitly export all symbols.
+ # This should be the same for all languages, so no per-tag cache variable.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5
+$as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; }
+if ${lt_cv_irix_exported_symbol+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+int foo (void) { return 0; }
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ lt_cv_irix_exported_symbol=yes
+else
+ lt_cv_irix_exported_symbol=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS=$save_LDFLAGS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5
+$as_echo "$lt_cv_irix_exported_symbol" >&6; }
+ if test yes = "$lt_cv_irix_exported_symbol"; then
+ archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib'
+ fi
+ link_all_deplibs=no
+ else
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ hardcode_libdir_separator=:
+ inherit_rpath=yes
+ link_all_deplibs=yes
+ ;;
+
+ linux*)
+ case $cc_basename in
+ tcc*)
+ # Fabrice Bellard et al's Tiny C Compiler
+ ld_shlibs=yes
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
+ netbsd* | netbsdelf*-gnu)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
+ else
+ archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF
+ fi
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ newsos6)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ hardcode_libdir_separator=:
+ hardcode_shlibpath_var=no
+ ;;
+
+ *nto* | *qnx*)
+ ;;
+
+ openbsd* | bitrig*)
+ if test -f /usr/libexec/ld.so; then
+ hardcode_direct=yes
+ hardcode_shlibpath_var=no
+ hardcode_direct_absolute=yes
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols'
+ hardcode_libdir_flag_spec='$wl-rpath,$libdir'
+ export_dynamic_flag_spec='$wl-E'
+ else
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ hardcode_libdir_flag_spec='$wl-rpath,$libdir'
+ fi
+ else
+ ld_shlibs=no
+ fi
+ ;;
+
+ os2*)
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ allow_undefined_flag=unsupported
+ shrext_cmds=.dll
+ archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ prefix_cmds="$SED"~
+ if test EXPORTS = "`$SED 1q $export_symbols`"; then
+ prefix_cmds="$prefix_cmds -e 1d";
+ fi~
+ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+ enable_shared_with_static_runtimes=yes
+ ;;
+
+ osf3*)
+ if test yes = "$GCC"; then
+ allow_undefined_flag=' $wl-expect_unresolved $wl\*'
+ archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ else
+ allow_undefined_flag=' -expect_unresolved \*'
+ archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ hardcode_libdir_separator=:
+ ;;
+
+ osf4* | osf5*) # as osf3* with the addition of -msym flag
+ if test yes = "$GCC"; then
+ allow_undefined_flag=' $wl-expect_unresolved $wl\*'
+ archive_cmds='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ else
+ allow_undefined_flag=' -expect_unresolved \*'
+ archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp'
+
+ # Both c and cxx compiler support -rpath directly
+ hardcode_libdir_flag_spec='-rpath $libdir'
+ fi
+ archive_cmds_need_lc='no'
+ hardcode_libdir_separator=:
+ ;;
+
+ solaris*)
+ no_undefined_flag=' -z defs'
+ if test yes = "$GCC"; then
+ wlarc='$wl'
+ archive_cmds='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ else
+ case `$CC -V 2>&1` in
+ *"Compilers 5.0"*)
+ wlarc=''
+ archive_cmds='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+ ;;
+ *)
+ wlarc='$wl'
+ archive_cmds='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ ;;
+ esac
+ fi
+ hardcode_libdir_flag_spec='-R$libdir'
+ hardcode_shlibpath_var=no
+ case $host_os in
+ solaris2.[0-5] | solaris2.[0-5].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands '-z linker_flag'. GCC discards it without '$wl',
+ # but is careful enough not to reorder.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ if test yes = "$GCC"; then
+ whole_archive_flag_spec='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract'
+ else
+ whole_archive_flag_spec='-z allextract$convenience -z defaultextract'
+ fi
+ ;;
+ esac
+ link_all_deplibs=yes
+ ;;
+
+ sunos4*)
+ if test sequent = "$host_vendor"; then
+ # Use $CC to link under sequent, because it throws in some extra .o
+ # files that make .init and .fini sections work.
+ archive_cmds='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+ fi
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_direct=yes
+ hardcode_minus_L=yes
+ hardcode_shlibpath_var=no
+ ;;
+
+ sysv4)
+ case $host_vendor in
+ sni)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=yes # is this really true???
+ ;;
+ siemens)
+ ## LD is ld it makes a PLAMLIB
+ ## CC just makes a GrossModule.
+ archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+ reload_cmds='$CC -r -o $output$reload_objs'
+ hardcode_direct=no
+ ;;
+ motorola)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_direct=no #Motorola manual says yes, but my tests say they lie
+ ;;
+ esac
+ runpath_var='LD_RUN_PATH'
+ hardcode_shlibpath_var=no
+ ;;
+
+ sysv4.3*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_shlibpath_var=no
+ export_dynamic_flag_spec='-Bexport'
+ ;;
+
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_shlibpath_var=no
+ runpath_var=LD_RUN_PATH
+ hardcode_runpath_var=yes
+ ld_shlibs=yes
+ fi
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
+ no_undefined_flag='$wl-z,text'
+ archive_cmds_need_lc=no
+ hardcode_shlibpath_var=no
+ runpath_var='LD_RUN_PATH'
+
+ if test yes = "$GCC"; then
+ archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We CANNOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ no_undefined_flag='$wl-z,text'
+ allow_undefined_flag='$wl-z,nodefs'
+ archive_cmds_need_lc=no
+ hardcode_shlibpath_var=no
+ hardcode_libdir_flag_spec='$wl-R,$libdir'
+ hardcode_libdir_separator=':'
+ link_all_deplibs=yes
+ export_dynamic_flag_spec='$wl-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ if test yes = "$GCC"; then
+ archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ else
+ archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ fi
+ ;;
+
+ uts4*)
+ archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_shlibpath_var=no
+ ;;
+
+ *)
+ ld_shlibs=no
+ ;;
+ esac
+
+ if test sni = "$host_vendor"; then
+ case $host in
+ sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+ export_dynamic_flag_spec='$wl-Blargedynsym'
+ ;;
+ esac
+ fi
+ fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5
+$as_echo "$ld_shlibs" >&6; }
+test no = "$ld_shlibs" && can_build_shared=no
+
+with_gnu_ld=$with_gnu_ld
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$archive_cmds_need_lc" in
+x|xyes)
+ # Assume -lc should be added
+ archive_cmds_need_lc=yes
+
+ if test yes,yes = "$GCC,$enable_shared"; then
+ case $archive_cmds in
+ *'~'*)
+ # FIXME: we may have to deal with multi-command sequences.
+ ;;
+ '$CC '*)
+ # Test whether the compiler implicitly links with -lc since on some
+ # systems, -lgcc has to come before -lc. If gcc already passes -lc
+ # to ld, don't add -lc before -lgcc.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5
+$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; }
+if ${lt_cv_archive_cmds_need_lc+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ $RM conftest*
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } 2>conftest.err; then
+ soname=conftest
+ lib=conftest
+ libobjs=conftest.$ac_objext
+ deplibs=
+ wl=$lt_prog_compiler_wl
+ pic_flag=$lt_prog_compiler_pic
+ compiler_flags=-v
+ linker_flags=-v
+ verstring=
+ output_objdir=.
+ libname=conftest
+ lt_save_allow_undefined_flag=$allow_undefined_flag
+ allow_undefined_flag=
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5
+ (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ then
+ lt_cv_archive_cmds_need_lc=no
+ else
+ lt_cv_archive_cmds_need_lc=yes
+ fi
+ allow_undefined_flag=$lt_save_allow_undefined_flag
+ else
+ cat conftest.err 1>&5
+ fi
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5
+$as_echo "$lt_cv_archive_cmds_need_lc" >&6; }
+ archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc
+ ;;
+ esac
+ fi
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5
+$as_echo_n "checking dynamic linker characteristics... " >&6; }
+
+if test yes = "$GCC"; then
+ case $host_os in
+ darwin*) lt_awk_arg='/^libraries:/,/LR/' ;;
+ *) lt_awk_arg='/^libraries:/' ;;
+ esac
+ case $host_os in
+ mingw* | cegcc*) lt_sed_strip_eq='s|=\([A-Za-z]:\)|\1|g' ;;
+ *) lt_sed_strip_eq='s|=/|/|g' ;;
+ esac
+ lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq`
+ case $lt_search_path_spec in
+ *\;*)
+ # if the path contains ";" then we assume it to be the separator
+ # otherwise default to the standard path separator (i.e. ":") - it is
+ # assumed that no part of a normal pathname contains ";" but that should
+ # okay in the real world where ";" in dirpaths is itself problematic.
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'`
+ ;;
+ *)
+ lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"`
+ ;;
+ esac
+ # Ok, now we have the path, separated by spaces, we can step through it
+ # and add multilib dir if necessary...
+ lt_tmp_lt_search_path_spec=
+ lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+ # ...but if some path component already ends with the multilib dir we assume
+ # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer).
+ case "$lt_multi_os_dir; $lt_search_path_spec " in
+ "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*)
+ lt_multi_os_dir=
+ ;;
+ esac
+ for lt_sys_path in $lt_search_path_spec; do
+ if test -d "$lt_sys_path$lt_multi_os_dir"; then
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir"
+ elif test -n "$lt_multi_os_dir"; then
+ test -d "$lt_sys_path" && \
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+ fi
+ done
+ lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk '
+BEGIN {RS = " "; FS = "/|\n";} {
+ lt_foo = "";
+ lt_count = 0;
+ for (lt_i = NF; lt_i > 0; lt_i--) {
+ if ($lt_i != "" && $lt_i != ".") {
+ if ($lt_i == "..") {
+ lt_count++;
+ } else {
+ if (lt_count == 0) {
+ lt_foo = "/" $lt_i lt_foo;
+ } else {
+ lt_count--;
+ }
+ }
+ }
+ }
+ if (lt_foo != "") { lt_freq[lt_foo]++; }
+ if (lt_freq[lt_foo] == 1) { print lt_foo; }
+}'`
+ # AWK program above erroneously prepends '/' to C:/dos/paths
+ # for these hosts.
+ case $host_os in
+ mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\
+ $SED 's|/\([A-Za-z]:\)|\1|g'` ;;
+ esac
+ sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP`
+else
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=.so
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+
+
+case $host_os in
+aix3*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname.a'
+ shlibpath_var=LIBPATH
+
+ # AIX 3 has no versioning support, so we append a major version to the name.
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+
+aix[4-9]*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ hardcode_into_libs=yes
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 supports IA64
+ library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ else
+ # With GCC up to 2.95.x, collect2 would create an import file
+ # for dependence libraries. The import file would start with
+ # the line '#! .'. This would cause the generated library to
+ # depend on '.', always an invalid library. This was fixed in
+ # development snapshots of GCC prior to 3.0.
+ case $host_os in
+ aix4 | aix4.[01] | aix4.[01].*)
+ if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+ echo ' yes '
+ echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then
+ :
+ else
+ can_build_shared=no
+ fi
+ ;;
+ esac
+ # Using Import Files as archive members, it is possible to support
+ # filename-based versioning of shared library archives on AIX. While
+ # this would work for both with and without runtime linking, it will
+ # prevent static linking of such archives. So we do filename-based
+ # shared library versioning with .so extension only, which is used
+ # when both runtime linking and shared linking is enabled.
+ # Unfortunately, runtime linking may impact performance, so we do
+ # not want this to be the default eventually. Also, we use the
+ # versioned .so libs for executables only if there is the -brtl
+ # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only.
+ # To allow for filename-based versioning support, we need to create
+ # libNAME.so.V as an archive file, containing:
+ # *) an Import File, referring to the versioned filename of the
+ # archive as well as the shared archive member, telling the
+ # bitwidth (32 or 64) of that shared object, and providing the
+ # list of exported symbols of that shared object, eventually
+ # decorated with the 'weak' keyword
+ # *) the shared object with the F_LOADONLY flag set, to really avoid
+ # it being seen by the linker.
+ # At run time we better use the real file rather than another symlink,
+ # but for link time we create the symlink libNAME.so -> libNAME.so.V
+
+ case $with_aix_soname,$aix_use_runtimelinking in
+ # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct
+ # soname into executable. Probably we can add versioning support to
+ # collect2, so additional links can be useful in future.
+ aix,yes) # traditional libtool
+ dynamic_linker='AIX unversionable lib.so'
+ # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+ # instead of lib<name>.a to let people know that these are not
+ # typical AIX shared libraries.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ ;;
+ aix,no) # traditional AIX only
+ dynamic_linker='AIX lib.a(lib.so.V)'
+ # We preserve .a as extension for shared libraries through AIX4.2
+ # and later when we are not doing run time linking.
+ library_names_spec='$libname$release.a $libname.a'
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+ svr4,*) # full svr4 only
+ dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)"
+ library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
+ # We do not specify a path in Import Files, so LIBPATH fires.
+ shlibpath_overrides_runpath=yes
+ ;;
+ *,yes) # both, prefer svr4
+ dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)"
+ library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
+ # unpreferred sharedlib libNAME.a needs extra handling
+ postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"'
+ postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"'
+ # We do not specify a path in Import Files, so LIBPATH fires.
+ shlibpath_overrides_runpath=yes
+ ;;
+ *,no) # both, prefer aix
+ dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)"
+ library_names_spec='$libname$release.a $libname.a'
+ soname_spec='$libname$release$shared_ext$major'
+ # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling
+ postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)'
+ postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"'
+ ;;
+ esac
+ shlibpath_var=LIBPATH
+ fi
+ ;;
+
+amigaos*)
+ case $host_cpu in
+ powerpc)
+ # Since July 2007 AmigaOS4 officially supports .so libraries.
+ # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ ;;
+ m68k)
+ library_names_spec='$libname.ixlibrary $libname.a'
+ # Create ${libname}_ixlibrary.a entries in /sys/libs.
+ finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+ ;;
+ esac
+ ;;
+
+beos*)
+ library_names_spec='$libname$shared_ext'
+ dynamic_linker="$host_os ld.so"
+ shlibpath_var=LIBRARY_PATH
+ ;;
+
+bsdi[45]*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+ # the default ld.so.conf also contains /usr/contrib/lib and
+ # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+ # libtool to hard-code these into programs
+ ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+ version_type=windows
+ shrext_cmds=.dll
+ need_version=no
+ need_lib_prefix=no
+
+ case $GCC,$cc_basename in
+ yes,*)
+ # gcc
+ library_names_spec='$libname.dll.a'
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+
+ case $host_os in
+ cygwin*)
+ # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+ soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"
+ ;;
+ mingw* | cegcc*)
+ # MinGW DLLs use traditional 'lib' prefix
+ soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+ ;;
+ pw32*)
+ # pw32 DLLs use 'pw' prefix rather than 'lib'
+ library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+ ;;
+ esac
+ dynamic_linker='Win32 ld.exe'
+ ;;
+
+ *,cl*)
+ # Native MSVC
+ libname_spec='$name'
+ soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+ library_names_spec='$libname.dll.lib'
+
+ case $build_os in
+ mingw*)
+ sys_lib_search_path_spec=
+ lt_save_ifs=$IFS
+ IFS=';'
+ for lt_path in $LIB
+ do
+ IFS=$lt_save_ifs
+ # Let DOS variable expansion print the short 8.3 style file name.
+ lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"`
+ sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path"
+ done
+ IFS=$lt_save_ifs
+ # Convert to MSYS style.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'`
+ ;;
+ cygwin*)
+ # Convert to unix form, then to dos form, then back to unix form
+ # but this time dos style (no spaces!) so that the unix form looks
+ # like /cygdrive/c/PROGRA~1:/cygdr...
+ sys_lib_search_path_spec=`cygpath --path --unix "$LIB"`
+ sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null`
+ sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ ;;
+ *)
+ sys_lib_search_path_spec=$LIB
+ if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then
+ # It is most probably a Windows format PATH.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ # FIXME: find the short name or the path components, as spaces are
+ # common. (e.g. "Program Files" -> "PROGRA~1")
+ ;;
+ esac
+
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+ dynamic_linker='Win32 link.exe'
+ ;;
+
+ *)
+ # Assume MSVC wrapper
+ library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib'
+ dynamic_linker='Win32 ld.exe'
+ ;;
+ esac
+ # FIXME: first we should search . and the directory the executable is in
+ shlibpath_var=PATH
+ ;;
+
+darwin* | rhapsody*)
+ dynamic_linker="$host_os dyld"
+ version_type=darwin
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$major$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$major$shared_ext'
+ shlibpath_overrides_runpath=yes
+ shlibpath_var=DYLD_LIBRARY_PATH
+ shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"
+ sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+ ;;
+
+dgux*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+freebsd* | dragonfly*)
+ # DragonFly does not have aout. When/if they implement a new
+ # versioning mechanism, adjust this.
+ if test -x /usr/bin/objformat; then
+ objformat=`/usr/bin/objformat`
+ else
+ case $host_os in
+ freebsd[23].*) objformat=aout ;;
+ *) objformat=elf ;;
+ esac
+ fi
+ version_type=freebsd-$objformat
+ case $version_type in
+ freebsd-elf*)
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ need_version=no
+ need_lib_prefix=no
+ ;;
+ freebsd-*)
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ need_version=yes
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_os in
+ freebsd2.*)
+ shlibpath_overrides_runpath=yes
+ ;;
+ freebsd3.[01]* | freebsdelf3.[01]*)
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ freebsd3.[2-9]* | freebsdelf3.[2-9]* | \
+ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1)
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+ *) # from 4.6 on, and DragonFly
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ esac
+ ;;
+
+haiku*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ dynamic_linker="$host_os runtime_loader"
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'
+ hardcode_into_libs=yes
+ ;;
+
+hpux9* | hpux10* | hpux11*)
+ # Give a soname corresponding to the major version so that dld.sl refuses to
+ # link against other versions.
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ case $host_cpu in
+ ia64*)
+ shrext_cmds='.so'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.so"
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ if test 32 = "$HPUX_IA64_MODE"; then
+ sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+ sys_lib_dlsearch_path_spec=/usr/lib/hpux32
+ else
+ sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+ sys_lib_dlsearch_path_spec=/usr/lib/hpux64
+ fi
+ ;;
+ hppa*64*)
+ shrext_cmds='.sl'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ *)
+ shrext_cmds='.sl'
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=SHLIB_PATH
+ shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+ esac
+ # HP-UX runs *really* slowly unless shared libraries are mode 555, ...
+ postinstall_cmds='chmod 555 $lib'
+ # or fails outright, so override atomically:
+ install_override_mode=555
+ ;;
+
+interix[3-9]*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $host_os in
+ nonstopux*) version_type=nonstopux ;;
+ *)
+ if test yes = "$lt_cv_prog_gnu_ld"; then
+ version_type=linux # correct to gnu/linux during the next big refactor
+ else
+ version_type=irix
+ fi ;;
+ esac
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext'
+ case $host_os in
+ irix5* | nonstopux*)
+ libsuff= shlibsuff=
+ ;;
+ *)
+ case $LD in # libtool.m4 will add one of these switches to LD
+ *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+ libsuff= shlibsuff= libmagic=32-bit;;
+ *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+ libsuff=32 shlibsuff=N32 libmagic=N32;;
+ *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+ libsuff=64 shlibsuff=64 libmagic=64-bit;;
+ *) libsuff= shlibsuff= libmagic=never-match;;
+ esac
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff"
+ sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff"
+ hardcode_into_libs=yes
+ ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+ dynamic_linker=no
+ ;;
+
+linux*android*)
+ version_type=none # Android doesn't support versioned libraries.
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext'
+ soname_spec='$libname$release$shared_ext'
+ finish_cmds=
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ dynamic_linker='Android linker'
+ # Don't embed -rpath directories since the linker doesn't support them.
+ hardcode_libdir_flag_spec='-L$libdir'
+ ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+
+ # Some binutils ld are patched to set DT_RUNPATH
+ if ${lt_cv_shlibpath_overrides_runpath+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_shlibpath_overrides_runpath=no
+ save_LDFLAGS=$LDFLAGS
+ save_libdir=$libdir
+ eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \
+ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then :
+ lt_cv_shlibpath_overrides_runpath=yes
+fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS=$save_LDFLAGS
+ libdir=$save_libdir
+
+fi
+
+ shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ # Ideally, we could use ldconfig to report *all* directores which are
+ # searched for libraries, however this is still not possible. Aside from not
+ # being certain /sbin/ldconfig is available, command
+ # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64,
+ # even though it is searched at run-time. Try to do the best guess by
+ # appending ld.so.conf contents (and includes) to the search path.
+ if test -f /etc/ld.so.conf; then
+ lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
+ sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+ fi
+
+ # We used to test for /lib/ld.so.1 and disable shared libraries on
+ # powerpc, because MkLinux only supported shared libraries with the
+ # GNU dynamic linker. Since this was broken with cross compilers,
+ # most powerpc-linux boxes support dynamic linking these days and
+ # people can always --disable-shared, the test was removed, and we
+ # assume the GNU/Linux dynamic linker is in use.
+ dynamic_linker='GNU/Linux ld.so'
+ ;;
+
+netbsdelf*-gnu)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ dynamic_linker='NetBSD ld.elf_so'
+ ;;
+
+netbsd*)
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ dynamic_linker='NetBSD (a.out) ld.so'
+ else
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ dynamic_linker='NetBSD ld.elf_so'
+ fi
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+
+newsos6)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+*nto* | *qnx*)
+ version_type=qnx
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ dynamic_linker='ldqnx.so'
+ ;;
+
+openbsd* | bitrig*)
+ version_type=sunos
+ sys_lib_dlsearch_path_spec=/usr/lib
+ need_lib_prefix=no
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ need_version=no
+ else
+ need_version=yes
+ fi
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+os2*)
+ libname_spec='$name'
+ version_type=windows
+ shrext_cmds=.dll
+ need_version=no
+ need_lib_prefix=no
+ # OS/2 can only load a DLL with a base name of 8 characters or less.
+ soname_spec='`test -n "$os2dllname" && libname="$os2dllname";
+ v=$($ECHO $release$versuffix | tr -d .-);
+ n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _);
+ $ECHO $n$v`$shared_ext'
+ library_names_spec='${libname}_dll.$libext'
+ dynamic_linker='OS/2 ld.exe'
+ shlibpath_var=BEGINLIBPATH
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ ;;
+
+osf3* | osf4* | osf5*)
+ version_type=osf
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+
+rdos*)
+ dynamic_linker=no
+ ;;
+
+solaris*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ # ldd complains unless libraries are executable
+ postinstall_cmds='chmod +x $lib'
+ ;;
+
+sunos4*)
+ version_type=sunos
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ if test yes = "$with_gnu_ld"; then
+ need_lib_prefix=no
+ fi
+ need_version=yes
+ ;;
+
+sysv4 | sysv4.3*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_vendor in
+ sni)
+ shlibpath_overrides_runpath=no
+ need_lib_prefix=no
+ runpath_var=LD_RUN_PATH
+ ;;
+ siemens)
+ need_lib_prefix=no
+ ;;
+ motorola)
+ need_lib_prefix=no
+ need_version=no
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+ ;;
+ esac
+ ;;
+
+sysv4*MP*)
+ if test -d /usr/nec; then
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext'
+ soname_spec='$libname$shared_ext.$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ fi
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ version_type=sco
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ if test yes = "$with_gnu_ld"; then
+ sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+ else
+ sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+ case $host_os in
+ sco3.2v5*)
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+ ;;
+ esac
+ fi
+ sys_lib_dlsearch_path_spec='/usr/lib'
+ ;;
+
+tpf*)
+ # TPF is a cross-target only. Preferred cross-host = GNU/Linux.
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+uts4*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+*)
+ dynamic_linker=no
+ ;;
+esac
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5
+$as_echo "$dynamic_linker" >&6; }
+test no = "$dynamic_linker" && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test yes = "$GCC"; then
+ variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then
+ sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec
+fi
+
+if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then
+ sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec
+fi
+
+# remember unaugmented sys_lib_dlsearch_path content for libtool script decls...
+configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec
+
+# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code
+func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH"
+
+# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool
+configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5
+$as_echo_n "checking how to hardcode library paths into programs... " >&6; }
+hardcode_action=
+if test -n "$hardcode_libdir_flag_spec" ||
+ test -n "$runpath_var" ||
+ test yes = "$hardcode_automatic"; then
+
+ # We can hardcode non-existent directories.
+ if test no != "$hardcode_direct" &&
+ # If the only mechanism to avoid hardcoding is shlibpath_var, we
+ # have to relink, otherwise we might link with an installed library
+ # when we should be linking with a yet-to-be-installed one
+ ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, )" &&
+ test no != "$hardcode_minus_L"; then
+ # Linking always hardcodes the temporary library directory.
+ hardcode_action=relink
+ else
+ # We can link without hardcoding, and we can hardcode nonexisting dirs.
+ hardcode_action=immediate
+ fi
+else
+ # We cannot hardcode anything, or else we can only hardcode existing
+ # directories.
+ hardcode_action=unsupported
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5
+$as_echo "$hardcode_action" >&6; }
+
+if test relink = "$hardcode_action" ||
+ test yes = "$inherit_rpath"; then
+ # Fast installation is not supported
+ enable_fast_install=no
+elif test yes = "$shlibpath_overrides_runpath" ||
+ test no = "$enable_shared"; then
+ # Fast installation is not necessary
+ enable_fast_install=needless
+fi
+
+
+
+
+
+
+ if test yes != "$enable_dlopen"; then
+ enable_dlopen=unknown
+ enable_dlopen_self=unknown
+ enable_dlopen_self_static=unknown
+else
+ lt_cv_dlopen=no
+ lt_cv_dlopen_libs=
+
+ case $host_os in
+ beos*)
+ lt_cv_dlopen=load_add_on
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+ ;;
+
+ mingw* | pw32* | cegcc*)
+ lt_cv_dlopen=LoadLibrary
+ lt_cv_dlopen_libs=
+ ;;
+
+ cygwin*)
+ lt_cv_dlopen=dlopen
+ lt_cv_dlopen_libs=
+ ;;
+
+ darwin*)
+ # if libdl is installed we need to link against it
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dl_dlopen=yes
+else
+ ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+ lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl
+else
+
+ lt_cv_dlopen=dyld
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=yes
+
+fi
+
+ ;;
+
+ tpf*)
+ # Don't try to run any link tests for TPF. We know it's impossible
+ # because TPF is a cross-compiler, and we know how we open DSOs.
+ lt_cv_dlopen=dlopen
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=no
+ ;;
+
+ *)
+ ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load"
+if test "x$ac_cv_func_shl_load" = xyes; then :
+ lt_cv_dlopen=shl_load
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5
+$as_echo_n "checking for shl_load in -ldld... " >&6; }
+if ${ac_cv_lib_dld_shl_load+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char shl_load ();
+int
+main ()
+{
+return shl_load ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dld_shl_load=yes
+else
+ ac_cv_lib_dld_shl_load=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5
+$as_echo "$ac_cv_lib_dld_shl_load" >&6; }
+if test "x$ac_cv_lib_dld_shl_load" = xyes; then :
+ lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld
+else
+ ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen"
+if test "x$ac_cv_func_dlopen" = xyes; then :
+ lt_cv_dlopen=dlopen
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+$as_echo_n "checking for dlopen in -ldl... " >&6; }
+if ${ac_cv_lib_dl_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dl_dlopen=yes
+else
+ ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+ lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5
+$as_echo_n "checking for dlopen in -lsvld... " >&6; }
+if ${ac_cv_lib_svld_dlopen+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsvld $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen ();
+int
+main ()
+{
+return dlopen ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_svld_dlopen=yes
+else
+ ac_cv_lib_svld_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5
+$as_echo "$ac_cv_lib_svld_dlopen" >&6; }
+if test "x$ac_cv_lib_svld_dlopen" = xyes; then :
+ lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5
+$as_echo_n "checking for dld_link in -ldld... " >&6; }
+if ${ac_cv_lib_dld_dld_link+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dld_link ();
+int
+main ()
+{
+return dld_link ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_dld_dld_link=yes
+else
+ ac_cv_lib_dld_dld_link=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5
+$as_echo "$ac_cv_lib_dld_dld_link" >&6; }
+if test "x$ac_cv_lib_dld_dld_link" = xyes; then :
+ lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+ ;;
+ esac
+
+ if test no = "$lt_cv_dlopen"; then
+ enable_dlopen=no
+ else
+ enable_dlopen=yes
+ fi
+
+ case $lt_cv_dlopen in
+ dlopen)
+ save_CPPFLAGS=$CPPFLAGS
+ test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+ save_LDFLAGS=$LDFLAGS
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+ save_LIBS=$LIBS
+ LIBS="$lt_cv_dlopen_libs $LIBS"
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5
+$as_echo_n "checking whether a program can dlopen itself... " >&6; }
+if ${lt_cv_dlopen_self+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test yes = "$cross_compiling"; then :
+ lt_cv_dlopen_self=cross
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+#line $LINENO "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+/* When -fvisibility=hidden is used, assume the code has been annotated
+ correspondingly for the symbols needed. */
+#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+int fnord () __attribute__((visibility("default")));
+#endif
+
+int fnord () { return 42; }
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else
+ {
+ if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ else puts (dlerror ());
+ }
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ return status;
+}
+_LT_EOF
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then
+ (./conftest; exit; ) >&5 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;;
+ x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;;
+ x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;;
+ esac
+ else :
+ # compilation failed
+ lt_cv_dlopen_self=no
+ fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5
+$as_echo "$lt_cv_dlopen_self" >&6; }
+
+ if test yes = "$lt_cv_dlopen_self"; then
+ wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5
+$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; }
+if ${lt_cv_dlopen_self_static+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test yes = "$cross_compiling"; then :
+ lt_cv_dlopen_self_static=cross
+else
+ lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+ lt_status=$lt_dlunknown
+ cat > conftest.$ac_ext <<_LT_EOF
+#line $LINENO "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+# define LT_DLGLOBAL RTLD_GLOBAL
+#else
+# ifdef DL_GLOBAL
+# define LT_DLGLOBAL DL_GLOBAL
+# else
+# define LT_DLGLOBAL 0
+# endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+ find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+# ifdef RTLD_LAZY
+# define LT_DLLAZY_OR_NOW RTLD_LAZY
+# else
+# ifdef DL_LAZY
+# define LT_DLLAZY_OR_NOW DL_LAZY
+# else
+# ifdef RTLD_NOW
+# define LT_DLLAZY_OR_NOW RTLD_NOW
+# else
+# ifdef DL_NOW
+# define LT_DLLAZY_OR_NOW DL_NOW
+# else
+# define LT_DLLAZY_OR_NOW 0
+# endif
+# endif
+# endif
+# endif
+#endif
+
+/* When -fvisibility=hidden is used, assume the code has been annotated
+ correspondingly for the symbols needed. */
+#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+int fnord () __attribute__((visibility("default")));
+#endif
+
+int fnord () { return 42; }
+int main ()
+{
+ void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+ int status = $lt_dlunknown;
+
+ if (self)
+ {
+ if (dlsym (self,"fnord")) status = $lt_dlno_uscore;
+ else
+ {
+ if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore;
+ else puts (dlerror ());
+ }
+ /* dlclose (self); */
+ }
+ else
+ puts (dlerror ());
+
+ return status;
+}
+_LT_EOF
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+ (eval $ac_link) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then
+ (./conftest; exit; ) >&5 2>/dev/null
+ lt_status=$?
+ case x$lt_status in
+ x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;;
+ x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;;
+ x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;;
+ esac
+ else :
+ # compilation failed
+ lt_cv_dlopen_self_static=no
+ fi
+fi
+rm -fr conftest*
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5
+$as_echo "$lt_cv_dlopen_self_static" >&6; }
+ fi
+
+ CPPFLAGS=$save_CPPFLAGS
+ LDFLAGS=$save_LDFLAGS
+ LIBS=$save_LIBS
+ ;;
+ esac
+
+ case $lt_cv_dlopen_self in
+ yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+ *) enable_dlopen_self=unknown ;;
+ esac
+
+ case $lt_cv_dlopen_self_static in
+ yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+ *) enable_dlopen_self_static=unknown ;;
+ esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+striplib=
+old_striplib=
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5
+$as_echo_n "checking whether stripping libraries is possible... " >&6; }
+if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+ test -z "$old_striplib" && old_striplib="$STRIP --strip-debug"
+ test -z "$striplib" && striplib="$STRIP --strip-unneeded"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+else
+# FIXME - insert some real tests, host_os isn't really good enough
+ case $host_os in
+ darwin*)
+ if test -n "$STRIP"; then
+ striplib="$STRIP -x"
+ old_striplib="$STRIP -S"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ fi
+ ;;
+ *)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+ ;;
+ esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+ # Report what library types will actually be built
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5
+$as_echo_n "checking if libtool supports shared libraries... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5
+$as_echo "$can_build_shared" >&6; }
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5
+$as_echo_n "checking whether to build shared libraries... " >&6; }
+ test no = "$can_build_shared" && enable_shared=no
+
+ # On AIX, shared libraries and static libraries use the same namespace, and
+ # are all built from PIC.
+ case $host_os in
+ aix3*)
+ test yes = "$enable_shared" && enable_static=no
+ if test -n "$RANLIB"; then
+ archive_cmds="$archive_cmds~\$RANLIB \$lib"
+ postinstall_cmds='$RANLIB $lib'
+ fi
+ ;;
+
+ aix[4-9]*)
+ if test ia64 != "$host_cpu"; then
+ case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in
+ yes,aix,yes) ;; # shared object as lib.so file only
+ yes,svr4,*) ;; # shared object as lib.so archive member only
+ yes,*) enable_static=no ;; # shared object in lib.a archive as well
+ esac
+ fi
+ ;;
+ esac
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5
+$as_echo "$enable_shared" >&6; }
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5
+$as_echo_n "checking whether to build static libraries... " >&6; }
+ # Make sure either enable_shared or enable_static is yes.
+ test yes = "$enable_shared" || enable_static=yes
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5
+$as_echo "$enable_static" >&6; }
+
+
+
+
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+CC=$lt_save_CC
+
+ if test -n "$CXX" && ( test no != "$CXX" &&
+ ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) ||
+ (test g++ != "$CXX"))); then
+ ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5
+$as_echo_n "checking how to run the C++ preprocessor... " >&6; }
+if test -z "$CXXCPP"; then
+ if ${ac_cv_prog_CXXCPP+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # Double quotes because CXXCPP needs to be expanded
+ for CXXCPP in "$CXX -E" "/lib/cpp"
+ do
+ ac_preproc_ok=false
+for ac_cxx_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+ break
+fi
+
+ done
+ ac_cv_prog_CXXCPP=$CXXCPP
+
+fi
+ CXXCPP=$ac_cv_prog_CXXCPP
+else
+ ac_cv_prog_CXXCPP=$CXXCPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5
+$as_echo "$CXXCPP" >&6; }
+ac_preproc_ok=false
+for ac_cxx_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+ # <limits.h> exists even on freestanding compilers.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+ Syntax error
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+
+else
+ # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_cxx_try_cpp "$LINENO"; then :
+ # Broken: success on invalid input.
+continue
+else
+ # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+else
+ _lt_caught_CXX_error=yes
+fi
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+archive_cmds_need_lc_CXX=no
+allow_undefined_flag_CXX=
+always_export_symbols_CXX=no
+archive_expsym_cmds_CXX=
+compiler_needs_object_CXX=no
+export_dynamic_flag_spec_CXX=
+hardcode_direct_CXX=no
+hardcode_direct_absolute_CXX=no
+hardcode_libdir_flag_spec_CXX=
+hardcode_libdir_separator_CXX=
+hardcode_minus_L_CXX=no
+hardcode_shlibpath_var_CXX=unsupported
+hardcode_automatic_CXX=no
+inherit_rpath_CXX=no
+module_cmds_CXX=
+module_expsym_cmds_CXX=
+link_all_deplibs_CXX=unknown
+old_archive_cmds_CXX=$old_archive_cmds
+reload_flag_CXX=$reload_flag
+reload_cmds_CXX=$reload_cmds
+no_undefined_flag_CXX=
+whole_archive_flag_spec_CXX=
+enable_shared_with_static_runtimes_CXX=no
+
+# Source file extension for C++ test sources.
+ac_ext=cpp
+
+# Object file extension for compiled C++ test sources.
+objext=o
+objext_CXX=$objext
+
+# No sense in running all these tests if we already determined that
+# the CXX compiler isn't working. Some variables (like enable_shared)
+# are currently assumed to apply to all compilers on this platform,
+# and will be corrupted by setting them based on a non-working compiler.
+if test yes != "$_lt_caught_CXX_error"; then
+ # Code to be used in simple compile tests
+ lt_simple_compile_test_code="int some_variable = 0;"
+
+ # Code to be used in simple link tests
+ lt_simple_link_test_code='int main(int, char *[]) { return(0); }'
+
+ # ltmain only uses $CC for tagged configurations so make sure $CC is set.
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+
+ # save warnings/boilerplate of simple test code
+ ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+
+ ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+
+
+ # Allow CC to be a program name with arguments.
+ lt_save_CC=$CC
+ lt_save_CFLAGS=$CFLAGS
+ lt_save_LD=$LD
+ lt_save_GCC=$GCC
+ GCC=$GXX
+ lt_save_with_gnu_ld=$with_gnu_ld
+ lt_save_path_LD=$lt_cv_path_LD
+ if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then
+ lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx
+ else
+ $as_unset lt_cv_prog_gnu_ld
+ fi
+ if test -n "${lt_cv_path_LDCXX+set}"; then
+ lt_cv_path_LD=$lt_cv_path_LDCXX
+ else
+ $as_unset lt_cv_path_LD
+ fi
+ test -z "${LDCXX+set}" || LD=$LDCXX
+ CC=${CXX-"c++"}
+ CFLAGS=$CXXFLAGS
+ compiler=$CC
+ compiler_CXX=$CC
+ func_cc_basename $compiler
+cc_basename=$func_cc_basename_result
+
+
+ if test -n "$compiler"; then
+ # We don't want -fno-exception when compiling C++ code, so set the
+ # no_builtin_flag separately
+ if test yes = "$GXX"; then
+ lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin'
+ else
+ lt_prog_compiler_no_builtin_flag_CXX=
+ fi
+
+ if test yes = "$GXX"; then
+ # Set up default GNU C++ configuration
+
+
+
+# Check whether --with-gnu-ld was given.
+if test "${with_gnu_ld+set}" = set; then :
+ withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes
+else
+ with_gnu_ld=no
+fi
+
+ac_prog=ld
+if test yes = "$GCC"; then
+ # Check if gcc -print-prog-name=ld gives a path.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5
+$as_echo_n "checking for ld used by $CC... " >&6; }
+ case $host in
+ *-*-mingw*)
+ # gcc leaves a trailing carriage return, which upsets mingw
+ ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+ *)
+ ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+ esac
+ case $ac_prog in
+ # Accept absolute paths.
+ [\\/]* | ?:[\\/]*)
+ re_direlt='/[^/][^/]*/\.\./'
+ # Canonicalize the pathname of ld
+ ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+ while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+ ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+ done
+ test -z "$LD" && LD=$ac_prog
+ ;;
+ "")
+ # If it fails, then pretend we aren't using GCC.
+ ac_prog=ld
+ ;;
+ *)
+ # If it is relative, then search for the first ld in PATH.
+ with_gnu_ld=unknown
+ ;;
+ esac
+elif test yes = "$with_gnu_ld"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5
+$as_echo_n "checking for GNU ld... " >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5
+$as_echo_n "checking for non-GNU ld... " >&6; }
+fi
+if ${lt_cv_path_LD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -z "$LD"; then
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+ for ac_dir in $PATH; do
+ IFS=$lt_save_ifs
+ test -z "$ac_dir" && ac_dir=.
+ if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+ lt_cv_path_LD=$ac_dir/$ac_prog
+ # Check to see if the program is GNU ld. I'd rather use --version,
+ # but apparently some variants of GNU ld only accept -v.
+ # Break only if it was the GNU/non-GNU ld that we prefer.
+ case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+ *GNU* | *'with BFD'*)
+ test no != "$with_gnu_ld" && break
+ ;;
+ *)
+ test yes != "$with_gnu_ld" && break
+ ;;
+ esac
+ fi
+ done
+ IFS=$lt_save_ifs
+else
+ lt_cv_path_LD=$LD # Let the user override the test with a path.
+fi
+fi
+
+LD=$lt_cv_path_LD
+if test -n "$LD"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
+$as_echo "$LD" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
+$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
+if ${lt_cv_prog_gnu_ld+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ # I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+ lt_cv_prog_gnu_ld=yes
+ ;;
+*)
+ lt_cv_prog_gnu_ld=no
+ ;;
+esac
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5
+$as_echo "$lt_cv_prog_gnu_ld" >&6; }
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+
+
+
+
+
+
+ # Check if GNU C++ uses GNU ld as the underlying linker, since the
+ # archiving commands below assume that GNU ld is being used.
+ if test yes = "$with_gnu_ld"; then
+ archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+
+ hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir'
+ export_dynamic_flag_spec_CXX='$wl--export-dynamic'
+
+ # If archive_cmds runs LD, not CC, wlarc should be empty
+ # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to
+ # investigate it a little bit more. (MM)
+ wlarc='$wl'
+
+ # ancient GNU ld didn't support --whole-archive et. al.
+ if eval "`$CC -print-prog-name=ld` --help 2>&1" |
+ $GREP 'no-whole-archive' > /dev/null; then
+ whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
+ else
+ whole_archive_flag_spec_CXX=
+ fi
+ else
+ with_gnu_ld=no
+ wlarc=
+
+ # A generic and very simple default shared library creation
+ # command for GNU C++ for the case where it uses the native
+ # linker, instead of GNU ld. If possible, this setting should
+ # overridden to take advantage of the native linker features on
+ # the platform it is being used on.
+ archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+ fi
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+
+ else
+ GXX=no
+ with_gnu_ld=no
+ wlarc=
+ fi
+
+ # PORTME: fill in a description of your system's C++ link characteristics
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; }
+ ld_shlibs_CXX=yes
+ case $host_os in
+ aix3*)
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ ;;
+ aix[4-9]*)
+ if test ia64 = "$host_cpu"; then
+ # On IA64, the linker does run time linking by default, so we don't
+ # have to do anything special.
+ aix_use_runtimelinking=no
+ exp_sym_flag='-Bexport'
+ no_entry_flag=
+ else
+ aix_use_runtimelinking=no
+
+ # Test if we are trying to use run time linking or normal
+ # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+ # have runtime linking enabled, and use it for executables.
+ # For shared libraries, we enable/disable runtime linking
+ # depending on the kind of the shared library created -
+ # when "with_aix_soname,aix_use_runtimelinking" is:
+ # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables
+ # "aix,yes" lib.so shared, rtl:yes, for executables
+ # lib.a static archive
+ # "both,no" lib.so.V(shr.o) shared, rtl:yes
+ # lib.a(lib.so.V) shared, rtl:no, for executables
+ # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a(lib.so.V) shared, rtl:no
+ # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a static archive
+ case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
+ for ld_flag in $LDFLAGS; do
+ case $ld_flag in
+ *-brtl*)
+ aix_use_runtimelinking=yes
+ break
+ ;;
+ esac
+ done
+ if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # With aix-soname=svr4, we create the lib.so.V shared archives only,
+ # so we don't have lib.a shared libs to link our executables.
+ # We have to force runtime linking in this case.
+ aix_use_runtimelinking=yes
+ LDFLAGS="$LDFLAGS -Wl,-brtl"
+ fi
+ ;;
+ esac
+
+ exp_sym_flag='-bexport'
+ no_entry_flag='-bnoentry'
+ fi
+
+ # When large executables or shared objects are built, AIX ld can
+ # have problems creating the table of contents. If linking a library
+ # or program results in "error TOC overflow" add -mminimal-toc to
+ # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not
+ # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+ archive_cmds_CXX=''
+ hardcode_direct_CXX=yes
+ hardcode_direct_absolute_CXX=yes
+ hardcode_libdir_separator_CXX=':'
+ link_all_deplibs_CXX=yes
+ file_list_spec_CXX='$wl-f,'
+ case $with_aix_soname,$aix_use_runtimelinking in
+ aix,*) ;; # no import file
+ svr4,* | *,yes) # use import file
+ # The Import File defines what to hardcode.
+ hardcode_direct_CXX=no
+ hardcode_direct_absolute_CXX=no
+ ;;
+ esac
+
+ if test yes = "$GXX"; then
+ case $host_os in aix4.[012]|aix4.[012].*)
+ # We only want to do this on AIX 4.2 and lower, the check
+ # below for broken collect2 doesn't work under 4.3+
+ collect2name=`$CC -print-prog-name=collect2`
+ if test -f "$collect2name" &&
+ strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+ then
+ # We have reworked collect2
+ :
+ else
+ # We have old collect2
+ hardcode_direct_CXX=unsupported
+ # It fails to find uninstalled libraries when the uninstalled
+ # path is not listed in the libpath. Setting hardcode_minus_L
+ # to unsupported forces relinking
+ hardcode_minus_L_CXX=yes
+ hardcode_libdir_flag_spec_CXX='-L$libdir'
+ hardcode_libdir_separator_CXX=
+ fi
+ esac
+ shared_flag='-shared'
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag=$shared_flag' $wl-G'
+ fi
+ # Need to ensure runtime linking is disabled for the traditional
+ # shared library, or the linker may eventually find shared libraries
+ # /with/ Import File - we do not want to mix them.
+ shared_flag_aix='-shared'
+ shared_flag_svr4='-shared $wl-G'
+ else
+ # not using gcc
+ if test ia64 = "$host_cpu"; then
+ # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+ # chokes on -Wl,-G. The following line is correct:
+ shared_flag='-G'
+ else
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag='$wl-G'
+ else
+ shared_flag='$wl-bM:SRE'
+ fi
+ shared_flag_aix='$wl-bM:SRE'
+ shared_flag_svr4='$wl-G'
+ fi
+ fi
+
+ export_dynamic_flag_spec_CXX='$wl-bexpall'
+ # It seems that -bexpall does not export symbols beginning with
+ # underscore (_), so it is better to generate a list of symbols to
+ # export.
+ always_export_symbols_CXX=yes
+ if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # Warning - without using the other runtime loading flags (-brtl),
+ # -berok will link without error, but may produce a broken library.
+ # The "-G" linker flag allows undefined symbols.
+ no_undefined_flag_CXX='-bernotok'
+ # Determine the default libpath from the value encoded in an empty
+ # executable.
+ if test set = "${lt_cv_aix_libpath+set}"; then
+ aix_libpath=$lt_cv_aix_libpath
+else
+ if ${lt_cv_aix_libpath__CXX+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+
+ lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\([^ ]*\) *$/\1/
+ p
+ }
+ }'
+ lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ # Check for a 64-bit object if we didn't find anything.
+ if test -z "$lt_cv_aix_libpath__CXX"; then
+ lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ if test -z "$lt_cv_aix_libpath__CXX"; then
+ lt_cv_aix_libpath__CXX=/usr/lib:/lib
+ fi
+
+fi
+
+ aix_libpath=$lt_cv_aix_libpath__CXX
+fi
+
+ hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath"
+
+ archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag
+ else
+ if test ia64 = "$host_cpu"; then
+ hardcode_libdir_flag_spec_CXX='$wl-R $libdir:/usr/lib:/lib'
+ allow_undefined_flag_CXX="-z nodefs"
+ archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols"
+ else
+ # Determine the default libpath from the value encoded in an
+ # empty executable.
+ if test set = "${lt_cv_aix_libpath+set}"; then
+ aix_libpath=$lt_cv_aix_libpath
+else
+ if ${lt_cv_aix_libpath__CXX+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+
+ lt_aix_libpath_sed='
+ /Import File Strings/,/^$/ {
+ /^0/ {
+ s/^0 *\([^ ]*\) *$/\1/
+ p
+ }
+ }'
+ lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ # Check for a 64-bit object if we didn't find anything.
+ if test -z "$lt_cv_aix_libpath__CXX"; then
+ lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+ fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ if test -z "$lt_cv_aix_libpath__CXX"; then
+ lt_cv_aix_libpath__CXX=/usr/lib:/lib
+ fi
+
+fi
+
+ aix_libpath=$lt_cv_aix_libpath__CXX
+fi
+
+ hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath"
+ # Warning - without using the other run time loading flags,
+ # -berok will link without error, but may produce a broken library.
+ no_undefined_flag_CXX=' $wl-bernotok'
+ allow_undefined_flag_CXX=' $wl-berok'
+ if test yes = "$with_gnu_ld"; then
+ # We only use this code for GNU lds that support --whole-archive.
+ whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive'
+ else
+ # Exported symbols can be pulled into shared objects from archives
+ whole_archive_flag_spec_CXX='$convenience'
+ fi
+ archive_cmds_need_lc_CXX=yes
+ archive_expsym_cmds_CXX='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d'
+ # -brtl affects multiple linker settings, -berok does not and is overridden later
+ compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`'
+ if test svr4 != "$with_aix_soname"; then
+ # This is similar to how AIX traditionally builds its shared
+ # libraries. Need -bnortl late, we may have -brtl in LDFLAGS.
+ archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname'
+ fi
+ if test aix != "$with_aix_soname"; then
+ archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp'
+ else
+ # used by -dlpreopen to get the symbols
+ archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$MV $output_objdir/$realname.d/$soname $output_objdir'
+ fi
+ archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$RM -r $output_objdir/$realname.d'
+ fi
+ fi
+ ;;
+
+ beos*)
+ if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+ allow_undefined_flag_CXX=unsupported
+ # Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+ # support --undefined. This deserves some investigation. FIXME
+ archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ else
+ ld_shlibs_CXX=no
+ fi
+ ;;
+
+ chorus*)
+ case $cc_basename in
+ *)
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ ;;
+ esac
+ ;;
+
+ cygwin* | mingw* | pw32* | cegcc*)
+ case $GXX,$cc_basename in
+ ,cl* | no,cl*)
+ # Native MSVC
+ # hardcode_libdir_flag_spec is actually meaningless, as there is
+ # no search path for DLLs.
+ hardcode_libdir_flag_spec_CXX=' '
+ allow_undefined_flag_CXX=unsupported
+ always_export_symbols_CXX=yes
+ file_list_spec_CXX='@'
+ # Tell ltmain to make .lib files, not .a files.
+ libext=lib
+ # Tell ltmain to make .dll files, not .so files.
+ shrext_cmds=.dll
+ # FIXME: Setting linknames here is a bad hack.
+ archive_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames='
+ archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then
+ cp "$export_symbols" "$output_objdir/$soname.def";
+ echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp";
+ else
+ $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp;
+ fi~
+ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+ linknames='
+ # The linker will not automatically build a static lib if we build a DLL.
+ # _LT_TAGVAR(old_archive_from_new_cmds, CXX)='true'
+ enable_shared_with_static_runtimes_CXX=yes
+ # Don't use ranlib
+ old_postinstall_cmds_CXX='chmod 644 $oldlib'
+ postlink_cmds_CXX='lt_outputfile="@OUTPUT@"~
+ lt_tool_outputfile="@TOOL_OUTPUT@"~
+ case $lt_outputfile in
+ *.exe|*.EXE) ;;
+ *)
+ lt_outputfile=$lt_outputfile.exe
+ lt_tool_outputfile=$lt_tool_outputfile.exe
+ ;;
+ esac~
+ func_to_tool_file "$lt_outputfile"~
+ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then
+ $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+ $RM "$lt_outputfile.manifest";
+ fi'
+ ;;
+ *)
+ # g++
+ # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless,
+ # as there is no search path for DLLs.
+ hardcode_libdir_flag_spec_CXX='-L$libdir'
+ export_dynamic_flag_spec_CXX='$wl--export-all-symbols'
+ allow_undefined_flag_CXX=unsupported
+ always_export_symbols_CXX=no
+ enable_shared_with_static_runtimes_CXX=yes
+
+ if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+ archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file, use it as
+ # is; otherwise, prepend EXPORTS...
+ archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ else
+ ld_shlibs_CXX=no
+ fi
+ ;;
+ esac
+ ;;
+ darwin* | rhapsody*)
+
+
+ archive_cmds_need_lc_CXX=no
+ hardcode_direct_CXX=no
+ hardcode_automatic_CXX=yes
+ hardcode_shlibpath_var_CXX=unsupported
+ if test yes = "$lt_cv_ld_force_load"; then
+ whole_archive_flag_spec_CXX='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
+
+ else
+ whole_archive_flag_spec_CXX=''
+ fi
+ link_all_deplibs_CXX=yes
+ allow_undefined_flag_CXX=$_lt_dar_allow_undefined
+ case $cc_basename in
+ ifort*|nagfor*) _lt_dar_can_shared=yes ;;
+ *) _lt_dar_can_shared=$GCC ;;
+ esac
+ if test yes = "$_lt_dar_can_shared"; then
+ output_verbose_link_cmd=func_echo_all
+ archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil"
+ module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil"
+ archive_expsym_cmds_CXX="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil"
+ module_expsym_cmds_CXX="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil"
+ if test yes != "$lt_cv_apple_cc_single_mod"; then
+ archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil"
+ archive_expsym_cmds_CXX="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil"
+ fi
+
+ else
+ ld_shlibs_CXX=no
+ fi
+
+ ;;
+
+ os2*)
+ hardcode_libdir_flag_spec_CXX='-L$libdir'
+ hardcode_minus_L_CXX=yes
+ allow_undefined_flag_CXX=unsupported
+ shrext_cmds=.dll
+ archive_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ archive_expsym_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ prefix_cmds="$SED"~
+ if test EXPORTS = "`$SED 1q $export_symbols`"; then
+ prefix_cmds="$prefix_cmds -e 1d";
+ fi~
+ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ old_archive_From_new_cmds_CXX='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+ enable_shared_with_static_runtimes_CXX=yes
+ ;;
+
+ dgux*)
+ case $cc_basename in
+ ec++*)
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ ;;
+ ghcx*)
+ # Green Hills C++ Compiler
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ ;;
+ esac
+ ;;
+
+ freebsd2.*)
+ # C++ shared libraries reported to be fairly broken before
+ # switch to ELF
+ ld_shlibs_CXX=no
+ ;;
+
+ freebsd-elf*)
+ archive_cmds_need_lc_CXX=no
+ ;;
+
+ freebsd* | dragonfly*)
+ # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF
+ # conventions
+ ld_shlibs_CXX=yes
+ ;;
+
+ haiku*)
+ archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ link_all_deplibs_CXX=yes
+ ;;
+
+ hpux9*)
+ hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir'
+ hardcode_libdir_separator_CXX=:
+ export_dynamic_flag_spec_CXX='$wl-E'
+ hardcode_direct_CXX=yes
+ hardcode_minus_L_CXX=yes # Not in the search PATH,
+ # but as the default
+ # location of the library.
+
+ case $cc_basename in
+ CC*)
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ ;;
+ aCC*)
+ archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+ ;;
+ *)
+ if test yes = "$GXX"; then
+ archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+ else
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ fi
+ ;;
+ esac
+ ;;
+
+ hpux10*|hpux11*)
+ if test no = "$with_gnu_ld"; then
+ hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir'
+ hardcode_libdir_separator_CXX=:
+
+ case $host_cpu in
+ hppa*64*|ia64*)
+ ;;
+ *)
+ export_dynamic_flag_spec_CXX='$wl-E'
+ ;;
+ esac
+ fi
+ case $host_cpu in
+ hppa*64*|ia64*)
+ hardcode_direct_CXX=no
+ hardcode_shlibpath_var_CXX=no
+ ;;
+ *)
+ hardcode_direct_CXX=yes
+ hardcode_direct_absolute_CXX=yes
+ hardcode_minus_L_CXX=yes # Not in the search PATH,
+ # but as the default
+ # location of the library.
+ ;;
+ esac
+
+ case $cc_basename in
+ CC*)
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ ;;
+ aCC*)
+ case $host_cpu in
+ hppa*64*)
+ archive_cmds_CXX='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ ia64*)
+ archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ *)
+ archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ esac
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+ ;;
+ *)
+ if test yes = "$GXX"; then
+ if test no = "$with_gnu_ld"; then
+ case $host_cpu in
+ hppa*64*)
+ archive_cmds_CXX='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ ia64*)
+ archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ *)
+ archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ ;;
+ esac
+ fi
+ else
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ fi
+ ;;
+ esac
+ ;;
+
+ interix[3-9]*)
+ hardcode_direct_CXX=no
+ hardcode_shlibpath_var_CXX=no
+ hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir'
+ export_dynamic_flag_spec_CXX='$wl-E'
+ # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+ # Instead, shared libraries are loaded at an image base (0x10000000 by
+ # default) and relocated if they conflict, which is a slow very memory
+ # consuming and fragmenting process. To avoid this, we pick a random,
+ # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+ # time. Moving up from 0x10000000 also allows more sbrk(2) space.
+ archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ archive_expsym_cmds_CXX='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ ;;
+ irix5* | irix6*)
+ case $cc_basename in
+ CC*)
+ # SGI C++
+ archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+
+ # Archives containing C++ object files must be created using
+ # "CC -ar", where "CC" is the IRIX C++ compiler. This is
+ # necessary to make sure instantiated templates are included
+ # in the archive.
+ old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs'
+ ;;
+ *)
+ if test yes = "$GXX"; then
+ if test no = "$with_gnu_ld"; then
+ archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ else
+ archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib'
+ fi
+ fi
+ link_all_deplibs_CXX=yes
+ ;;
+ esac
+ hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir'
+ hardcode_libdir_separator_CXX=:
+ inherit_rpath_CXX=yes
+ ;;
+
+ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ case $cc_basename in
+ KCC*)
+ # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+ # KCC will only create a shared library if the output file
+ # ends with ".so" (or ".sl" for HP-UX), so rename the library
+ # to its proper name (with version) after linking.
+ archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+ archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib'
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+
+ hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir'
+ export_dynamic_flag_spec_CXX='$wl--export-dynamic'
+
+ # Archives containing C++ object files must be created using
+ # "CC -Bstatic", where "CC" is the KAI C++ compiler.
+ old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs'
+ ;;
+ icpc* | ecpc* )
+ # Intel C++
+ with_gnu_ld=yes
+ # version 8.0 and above of icpc choke on multiply defined symbols
+ # if we add $predep_objects and $postdep_objects, however 7.1 and
+ # earlier do not add the objects themselves.
+ case `$CC -V 2>&1` in
+ *"Version 7."*)
+ archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ *) # Version 8.0 or newer
+ tmp_idyn=
+ case $host_cpu in
+ ia64*) tmp_idyn=' -i_dynamic';;
+ esac
+ archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ esac
+ archive_cmds_need_lc_CXX=no
+ hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir'
+ export_dynamic_flag_spec_CXX='$wl--export-dynamic'
+ whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive'
+ ;;
+ pgCC* | pgcpp*)
+ # Portland Group C++ compiler
+ case `$CC -V` in
+ *pgCC\ [1-5].* | *pgcpp\ [1-5].*)
+ prelink_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+ old_archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+ $RANLIB $oldlib'
+ archive_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds_CXX='tpldir=Template.dir~
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ *) # Version 6 and above use weak symbols
+ archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+ ;;
+ esac
+
+ hardcode_libdir_flag_spec_CXX='$wl--rpath $wl$libdir'
+ export_dynamic_flag_spec_CXX='$wl--export-dynamic'
+ whole_archive_flag_spec_CXX='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ ;;
+ cxx*)
+ # Compaq C++
+ archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols'
+
+ runpath_var=LD_RUN_PATH
+ hardcode_libdir_flag_spec_CXX='-rpath $libdir'
+ hardcode_libdir_separator_CXX=:
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed'
+ ;;
+ xl* | mpixl* | bgxl*)
+ # IBM XL 8.0 on PPC, with GNU ld
+ hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir'
+ export_dynamic_flag_spec_CXX='$wl--export-dynamic'
+ archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ if test yes = "$supports_anon_versioning"; then
+ archive_expsym_cmds_CXX='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib'
+ fi
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C++ 5.9
+ no_undefined_flag_CXX=' -zdefs'
+ archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ archive_expsym_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols'
+ hardcode_libdir_flag_spec_CXX='-R$libdir'
+ whole_archive_flag_spec_CXX='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+ compiler_needs_object_CXX=yes
+
+ # Not sure whether something based on
+ # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1
+ # would be better.
+ output_verbose_link_cmd='func_echo_all'
+
+ # Archives containing C++ object files must be created using
+ # "CC -xar", where "CC" is the Sun C++ compiler. This is
+ # necessary to make sure instantiated templates are included
+ # in the archive.
+ old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs'
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+
+ lynxos*)
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ ;;
+
+ m88k*)
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ ;;
+
+ mvs*)
+ case $cc_basename in
+ cxx*)
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ ;;
+ esac
+ ;;
+
+ netbsd*)
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags'
+ wlarc=
+ hardcode_libdir_flag_spec_CXX='-R$libdir'
+ hardcode_direct_CXX=yes
+ hardcode_shlibpath_var_CXX=no
+ fi
+ # Workaround some broken pre-1.5 toolchains
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"'
+ ;;
+
+ *nto* | *qnx*)
+ ld_shlibs_CXX=yes
+ ;;
+
+ openbsd* | bitrig*)
+ if test -f /usr/libexec/ld.so; then
+ hardcode_direct_CXX=yes
+ hardcode_shlibpath_var_CXX=no
+ hardcode_direct_absolute_CXX=yes
+ archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
+ hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir'
+ if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then
+ archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib'
+ export_dynamic_flag_spec_CXX='$wl-E'
+ whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
+ fi
+ output_verbose_link_cmd=func_echo_all
+ else
+ ld_shlibs_CXX=no
+ fi
+ ;;
+
+ osf3* | osf4* | osf5*)
+ case $cc_basename in
+ KCC*)
+ # Kuck and Associates, Inc. (KAI) C++ Compiler
+
+ # KCC will only create a shared library if the output file
+ # ends with ".so" (or ".sl" for HP-UX), so rename the library
+ # to its proper name (with version) after linking.
+ archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+
+ hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir'
+ hardcode_libdir_separator_CXX=:
+
+ # Archives containing C++ object files must be created using
+ # the KAI C++ compiler.
+ case $host in
+ osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;;
+ *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;;
+ esac
+ ;;
+ RCC*)
+ # Rational C++ 2.4.1
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ ;;
+ cxx*)
+ case $host in
+ osf3*)
+ allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*'
+ archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir'
+ ;;
+ *)
+ allow_undefined_flag_CXX=' -expect_unresolved \*'
+ archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~
+ echo "-hidden">> $lib.exp~
+ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~
+ $RM $lib.exp'
+ hardcode_libdir_flag_spec_CXX='-rpath $libdir'
+ ;;
+ esac
+
+ hardcode_libdir_separator_CXX=:
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ #
+ # There doesn't appear to be a way to prevent this compiler from
+ # explicitly linking system object files so we need to strip them
+ # from the output so that they don't get included in the library
+ # dependencies.
+ output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+ ;;
+ *)
+ if test yes,no = "$GXX,$with_gnu_ld"; then
+ allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*'
+ case $host in
+ osf3*)
+ archive_cmds_CXX='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ ;;
+ *)
+ archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ ;;
+ esac
+
+ hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir'
+ hardcode_libdir_separator_CXX=:
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+
+ else
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ fi
+ ;;
+ esac
+ ;;
+
+ psos*)
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ ;;
+
+ sunos4*)
+ case $cc_basename in
+ CC*)
+ # Sun C++ 4.x
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ ;;
+ lcc*)
+ # Lucid
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ ;;
+ esac
+ ;;
+
+ solaris*)
+ case $cc_basename in
+ CC* | sunCC*)
+ # Sun C++ 4.2, 5.x and Centerline C++
+ archive_cmds_need_lc_CXX=yes
+ no_undefined_flag_CXX=' -zdefs'
+ archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+ hardcode_libdir_flag_spec_CXX='-R$libdir'
+ hardcode_shlibpath_var_CXX=no
+ case $host_os in
+ solaris2.[0-5] | solaris2.[0-5].*) ;;
+ *)
+ # The compiler driver will combine and reorder linker options,
+ # but understands '-z linker_flag'.
+ # Supported since Solaris 2.6 (maybe 2.5.1?)
+ whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract'
+ ;;
+ esac
+ link_all_deplibs_CXX=yes
+
+ output_verbose_link_cmd='func_echo_all'
+
+ # Archives containing C++ object files must be created using
+ # "CC -xar", where "CC" is the Sun C++ compiler. This is
+ # necessary to make sure instantiated templates are included
+ # in the archive.
+ old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs'
+ ;;
+ gcx*)
+ # Green Hills C++ Compiler
+ archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'
+
+ # The C++ compiler must be used to create the archive.
+ old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs'
+ ;;
+ *)
+ # GNU C++ compiler with Solaris linker
+ if test yes,no = "$GXX,$with_gnu_ld"; then
+ no_undefined_flag_CXX=' $wl-z ${wl}defs'
+ if $CC --version | $GREP -v '^2\.7' > /dev/null; then
+ archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'
+ archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+ else
+ # g++ 2.7 appears to require '-G' NOT '-shared' on this
+ # platform.
+ archive_cmds_CXX='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'
+ archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+ $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+
+ # Commands to make compiler produce verbose output that lists
+ # what "hidden" libraries, object files and flags are used when
+ # linking a shared library.
+ output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
+ fi
+
+ hardcode_libdir_flag_spec_CXX='$wl-R $wl$libdir'
+ case $host_os in
+ solaris2.[0-5] | solaris2.[0-5].*) ;;
+ *)
+ whole_archive_flag_spec_CXX='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract'
+ ;;
+ esac
+ fi
+ ;;
+ esac
+ ;;
+
+ sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
+ no_undefined_flag_CXX='$wl-z,text'
+ archive_cmds_need_lc_CXX=no
+ hardcode_shlibpath_var_CXX=no
+ runpath_var='LD_RUN_PATH'
+
+ case $cc_basename in
+ CC*)
+ archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ *)
+ archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
+ sysv5* | sco3.2v5* | sco5v6*)
+ # Note: We CANNOT use -z defs as we might desire, because we do not
+ # link with -lc, and that would cause any symbols used from libc to
+ # always be unresolved, which means just about no library would
+ # ever link correctly. If we're not using GNU ld we use -z text
+ # though, which does catch some bad symbols but isn't as heavy-handed
+ # as -z defs.
+ no_undefined_flag_CXX='$wl-z,text'
+ allow_undefined_flag_CXX='$wl-z,nodefs'
+ archive_cmds_need_lc_CXX=no
+ hardcode_shlibpath_var_CXX=no
+ hardcode_libdir_flag_spec_CXX='$wl-R,$libdir'
+ hardcode_libdir_separator_CXX=':'
+ link_all_deplibs_CXX=yes
+ export_dynamic_flag_spec_CXX='$wl-Bexport'
+ runpath_var='LD_RUN_PATH'
+
+ case $cc_basename in
+ CC*)
+ archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ old_archive_cmds_CXX='$CC -Tprelink_objects $oldobjs~
+ '"$old_archive_cmds_CXX"
+ reload_cmds_CXX='$CC -Tprelink_objects $reload_objs~
+ '"$reload_cmds_CXX"
+ ;;
+ *)
+ archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
+ tandem*)
+ case $cc_basename in
+ NCC*)
+ # NonStop-UX NCC 3.20
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ ;;
+ *)
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ ;;
+ esac
+ ;;
+
+ vxworks*)
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ ;;
+
+ *)
+ # FIXME: insert proper C++ library support
+ ld_shlibs_CXX=no
+ ;;
+ esac
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5
+$as_echo "$ld_shlibs_CXX" >&6; }
+ test no = "$ld_shlibs_CXX" && can_build_shared=no
+
+ GCC_CXX=$GXX
+ LD_CXX=$LD
+
+ ## CAVEAT EMPTOR:
+ ## There is no encapsulation within the following macros, do not change
+ ## the running order or otherwise move them around unless you know exactly
+ ## what you are doing...
+ # Dependencies to place before and after the object being linked:
+predep_objects_CXX=
+postdep_objects_CXX=
+predeps_CXX=
+postdeps_CXX=
+compiler_lib_search_path_CXX=
+
+cat > conftest.$ac_ext <<_LT_EOF
+class Foo
+{
+public:
+ Foo (void) { a = 0; }
+private:
+ int a;
+};
+_LT_EOF
+
+
+_lt_libdeps_save_CFLAGS=$CFLAGS
+case "$CC $CFLAGS " in #(
+*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;;
+*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;;
+*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;;
+esac
+
+if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ # Parse the compiler output and extract the necessary
+ # objects, libraries and library flags.
+
+ # Sentinel used to keep track of whether or not we are before
+ # the conftest object file.
+ pre_test_object_deps_done=no
+
+ for p in `eval "$output_verbose_link_cmd"`; do
+ case $prev$p in
+
+ -L* | -R* | -l*)
+ # Some compilers place space between "-{L,R}" and the path.
+ # Remove the space.
+ if test x-L = "$p" ||
+ test x-R = "$p"; then
+ prev=$p
+ continue
+ fi
+
+ # Expand the sysroot to ease extracting the directories later.
+ if test -z "$prev"; then
+ case $p in
+ -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;;
+ -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;;
+ -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;;
+ esac
+ fi
+ case $p in
+ =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;;
+ esac
+ if test no = "$pre_test_object_deps_done"; then
+ case $prev in
+ -L | -R)
+ # Internal compiler library paths should come after those
+ # provided the user. The postdeps already come after the
+ # user supplied libs so there is no need to process them.
+ if test -z "$compiler_lib_search_path_CXX"; then
+ compiler_lib_search_path_CXX=$prev$p
+ else
+ compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} $prev$p"
+ fi
+ ;;
+ # The "-l" case would never come before the object being
+ # linked, so don't bother handling this case.
+ esac
+ else
+ if test -z "$postdeps_CXX"; then
+ postdeps_CXX=$prev$p
+ else
+ postdeps_CXX="${postdeps_CXX} $prev$p"
+ fi
+ fi
+ prev=
+ ;;
+
+ *.lto.$objext) ;; # Ignore GCC LTO objects
+ *.$objext)
+ # This assumes that the test object file only shows up
+ # once in the compiler output.
+ if test "$p" = "conftest.$objext"; then
+ pre_test_object_deps_done=yes
+ continue
+ fi
+
+ if test no = "$pre_test_object_deps_done"; then
+ if test -z "$predep_objects_CXX"; then
+ predep_objects_CXX=$p
+ else
+ predep_objects_CXX="$predep_objects_CXX $p"
+ fi
+ else
+ if test -z "$postdep_objects_CXX"; then
+ postdep_objects_CXX=$p
+ else
+ postdep_objects_CXX="$postdep_objects_CXX $p"
+ fi
+ fi
+ ;;
+
+ *) ;; # Ignore the rest.
+
+ esac
+ done
+
+ # Clean up.
+ rm -f a.out a.exe
+else
+ echo "libtool.m4: error: problem compiling CXX test program"
+fi
+
+$RM -f confest.$objext
+CFLAGS=$_lt_libdeps_save_CFLAGS
+
+# PORTME: override above test on systems where it is broken
+case $host_os in
+interix[3-9]*)
+ # Interix 3.5 installs completely hosed .la files for C++, so rather than
+ # hack all around it, let's just trust "g++" to DTRT.
+ predep_objects_CXX=
+ postdep_objects_CXX=
+ postdeps_CXX=
+ ;;
+esac
+
+
+case " $postdeps_CXX " in
+*" -lc "*) archive_cmds_need_lc_CXX=no ;;
+esac
+ compiler_lib_search_dirs_CXX=
+if test -n "${compiler_lib_search_path_CXX}"; then
+ compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | $SED -e 's! -L! !g' -e 's!^ !!'`
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ lt_prog_compiler_wl_CXX=
+lt_prog_compiler_pic_CXX=
+lt_prog_compiler_static_CXX=
+
+
+ # C++ specific cases for pic, static, wl, etc.
+ if test yes = "$GXX"; then
+ lt_prog_compiler_wl_CXX='-Wl,'
+ lt_prog_compiler_static_CXX='-static'
+
+ case $host_os in
+ aix*)
+ # All AIX code is PIC.
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 now supports IA64 processor
+ lt_prog_compiler_static_CXX='-Bstatic'
+ fi
+ lt_prog_compiler_pic_CXX='-fPIC'
+ ;;
+
+ amigaos*)
+ case $host_cpu in
+ powerpc)
+ # see comment about AmigaOS4 .so support
+ lt_prog_compiler_pic_CXX='-fPIC'
+ ;;
+ m68k)
+ # FIXME: we need at least 68020 code to build shared libraries, but
+ # adding the '-m68020' flag to GCC prevents building anything better,
+ # like '-m68040'.
+ lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4'
+ ;;
+ esac
+ ;;
+
+ beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+ # PIC is the default for these OSes.
+ ;;
+ mingw* | cygwin* | os2* | pw32* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ # Although the cygwin gcc ignores -fPIC, still need this for old-style
+ # (--disable-auto-import) libraries
+ lt_prog_compiler_pic_CXX='-DDLL_EXPORT'
+ case $host_os in
+ os2*)
+ lt_prog_compiler_static_CXX='$wl-static'
+ ;;
+ esac
+ ;;
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ lt_prog_compiler_pic_CXX='-fno-common'
+ ;;
+ *djgpp*)
+ # DJGPP does not support shared libraries at all
+ lt_prog_compiler_pic_CXX=
+ ;;
+ haiku*)
+ # PIC is the default for Haiku.
+ # The "-static" flag exists, but is broken.
+ lt_prog_compiler_static_CXX=
+ ;;
+ interix[3-9]*)
+ # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+ # Instead, we relocate shared libraries at runtime.
+ ;;
+ sysv4*MP*)
+ if test -d /usr/nec; then
+ lt_prog_compiler_pic_CXX=-Kconform_pic
+ fi
+ ;;
+ hpux*)
+ # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+ # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag
+ # sets the default TLS model and affects inlining.
+ case $host_cpu in
+ hppa*64*)
+ ;;
+ *)
+ lt_prog_compiler_pic_CXX='-fPIC'
+ ;;
+ esac
+ ;;
+ *qnx* | *nto*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ lt_prog_compiler_pic_CXX='-fPIC -shared'
+ ;;
+ *)
+ lt_prog_compiler_pic_CXX='-fPIC'
+ ;;
+ esac
+ else
+ case $host_os in
+ aix[4-9]*)
+ # All AIX code is PIC.
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 now supports IA64 processor
+ lt_prog_compiler_static_CXX='-Bstatic'
+ else
+ lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp'
+ fi
+ ;;
+ chorus*)
+ case $cc_basename in
+ cxch68*)
+ # Green Hills C++ Compiler
+ # _LT_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a"
+ ;;
+ esac
+ ;;
+ mingw* | cygwin* | os2* | pw32* | cegcc*)
+ # This hack is so that the source file can tell whether it is being
+ # built for inclusion in a dll (and should export symbols for example).
+ lt_prog_compiler_pic_CXX='-DDLL_EXPORT'
+ ;;
+ dgux*)
+ case $cc_basename in
+ ec++*)
+ lt_prog_compiler_pic_CXX='-KPIC'
+ ;;
+ ghcx*)
+ # Green Hills C++ Compiler
+ lt_prog_compiler_pic_CXX='-pic'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ freebsd* | dragonfly*)
+ # FreeBSD uses GNU C++
+ ;;
+ hpux9* | hpux10* | hpux11*)
+ case $cc_basename in
+ CC*)
+ lt_prog_compiler_wl_CXX='-Wl,'
+ lt_prog_compiler_static_CXX='$wl-a ${wl}archive'
+ if test ia64 != "$host_cpu"; then
+ lt_prog_compiler_pic_CXX='+Z'
+ fi
+ ;;
+ aCC*)
+ lt_prog_compiler_wl_CXX='-Wl,'
+ lt_prog_compiler_static_CXX='$wl-a ${wl}archive'
+ case $host_cpu in
+ hppa*64*|ia64*)
+ # +Z the default
+ ;;
+ *)
+ lt_prog_compiler_pic_CXX='+Z'
+ ;;
+ esac
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ interix*)
+ # This is c89, which is MS Visual C++ (no shared libs)
+ # Anyone wants to do a port?
+ ;;
+ irix5* | irix6* | nonstopux*)
+ case $cc_basename in
+ CC*)
+ lt_prog_compiler_wl_CXX='-Wl,'
+ lt_prog_compiler_static_CXX='-non_shared'
+ # CC pic flag -KPIC is the default.
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ case $cc_basename in
+ KCC*)
+ # KAI C++ Compiler
+ lt_prog_compiler_wl_CXX='--backend -Wl,'
+ lt_prog_compiler_pic_CXX='-fPIC'
+ ;;
+ ecpc* )
+ # old Intel C++ for x86_64, which still supported -KPIC.
+ lt_prog_compiler_wl_CXX='-Wl,'
+ lt_prog_compiler_pic_CXX='-KPIC'
+ lt_prog_compiler_static_CXX='-static'
+ ;;
+ icpc* )
+ # Intel C++, used to be incompatible with GCC.
+ # ICC 10 doesn't accept -KPIC any more.
+ lt_prog_compiler_wl_CXX='-Wl,'
+ lt_prog_compiler_pic_CXX='-fPIC'
+ lt_prog_compiler_static_CXX='-static'
+ ;;
+ pgCC* | pgcpp*)
+ # Portland Group C++ compiler
+ lt_prog_compiler_wl_CXX='-Wl,'
+ lt_prog_compiler_pic_CXX='-fpic'
+ lt_prog_compiler_static_CXX='-Bstatic'
+ ;;
+ cxx*)
+ # Compaq C++
+ # Make sure the PIC flag is empty. It appears that all Alpha
+ # Linux and Compaq Tru64 Unix objects are PIC.
+ lt_prog_compiler_pic_CXX=
+ lt_prog_compiler_static_CXX='-non_shared'
+ ;;
+ xlc* | xlC* | bgxl[cC]* | mpixl[cC]*)
+ # IBM XL 8.0, 9.0 on PPC and BlueGene
+ lt_prog_compiler_wl_CXX='-Wl,'
+ lt_prog_compiler_pic_CXX='-qpic'
+ lt_prog_compiler_static_CXX='-qstaticlink'
+ ;;
+ *)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*)
+ # Sun C++ 5.9
+ lt_prog_compiler_pic_CXX='-KPIC'
+ lt_prog_compiler_static_CXX='-Bstatic'
+ lt_prog_compiler_wl_CXX='-Qoption ld '
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ lynxos*)
+ ;;
+ m88k*)
+ ;;
+ mvs*)
+ case $cc_basename in
+ cxx*)
+ lt_prog_compiler_pic_CXX='-W c,exportall'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ netbsd* | netbsdelf*-gnu)
+ ;;
+ *qnx* | *nto*)
+ # QNX uses GNU C++, but need to define -shared option too, otherwise
+ # it will coredump.
+ lt_prog_compiler_pic_CXX='-fPIC -shared'
+ ;;
+ osf3* | osf4* | osf5*)
+ case $cc_basename in
+ KCC*)
+ lt_prog_compiler_wl_CXX='--backend -Wl,'
+ ;;
+ RCC*)
+ # Rational C++ 2.4.1
+ lt_prog_compiler_pic_CXX='-pic'
+ ;;
+ cxx*)
+ # Digital/Compaq C++
+ lt_prog_compiler_wl_CXX='-Wl,'
+ # Make sure the PIC flag is empty. It appears that all Alpha
+ # Linux and Compaq Tru64 Unix objects are PIC.
+ lt_prog_compiler_pic_CXX=
+ lt_prog_compiler_static_CXX='-non_shared'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ psos*)
+ ;;
+ solaris*)
+ case $cc_basename in
+ CC* | sunCC*)
+ # Sun C++ 4.2, 5.x and Centerline C++
+ lt_prog_compiler_pic_CXX='-KPIC'
+ lt_prog_compiler_static_CXX='-Bstatic'
+ lt_prog_compiler_wl_CXX='-Qoption ld '
+ ;;
+ gcx*)
+ # Green Hills C++ Compiler
+ lt_prog_compiler_pic_CXX='-PIC'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ sunos4*)
+ case $cc_basename in
+ CC*)
+ # Sun C++ 4.x
+ lt_prog_compiler_pic_CXX='-pic'
+ lt_prog_compiler_static_CXX='-Bstatic'
+ ;;
+ lcc*)
+ # Lucid
+ lt_prog_compiler_pic_CXX='-pic'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+ case $cc_basename in
+ CC*)
+ lt_prog_compiler_wl_CXX='-Wl,'
+ lt_prog_compiler_pic_CXX='-KPIC'
+ lt_prog_compiler_static_CXX='-Bstatic'
+ ;;
+ esac
+ ;;
+ tandem*)
+ case $cc_basename in
+ NCC*)
+ # NonStop-UX NCC 3.20
+ lt_prog_compiler_pic_CXX='-KPIC'
+ ;;
+ *)
+ ;;
+ esac
+ ;;
+ vxworks*)
+ ;;
+ *)
+ lt_prog_compiler_can_build_shared_CXX=no
+ ;;
+ esac
+ fi
+
+case $host_os in
+ # For platforms that do not support PIC, -DPIC is meaningless:
+ *djgpp*)
+ lt_prog_compiler_pic_CXX=
+ ;;
+ *)
+ lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC"
+ ;;
+esac
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5
+$as_echo_n "checking for $compiler option to produce PIC... " >&6; }
+if ${lt_cv_prog_compiler_pic_CXX+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_pic_CXX=$lt_prog_compiler_pic_CXX
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_CXX" >&5
+$as_echo "$lt_cv_prog_compiler_pic_CXX" >&6; }
+lt_prog_compiler_pic_CXX=$lt_cv_prog_compiler_pic_CXX
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$lt_prog_compiler_pic_CXX"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5
+$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; }
+if ${lt_cv_prog_compiler_pic_works_CXX+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_pic_works_CXX=no
+ ac_outfile=conftest.$ac_objext
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+ lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" ## exclude from sc_useless_quotes_in_assignment
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ # The option is referenced via a variable to avoid confusing sed.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>conftest.err)
+ ac_status=$?
+ cat conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s "$ac_outfile"; then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings other than the usual output.
+ $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_pic_works_CXX=yes
+ fi
+ fi
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5
+$as_echo "$lt_cv_prog_compiler_pic_works_CXX" >&6; }
+
+if test yes = "$lt_cv_prog_compiler_pic_works_CXX"; then
+ case $lt_prog_compiler_pic_CXX in
+ "" | " "*) ;;
+ *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;;
+ esac
+else
+ lt_prog_compiler_pic_CXX=
+ lt_prog_compiler_can_build_shared_CXX=no
+fi
+
+fi
+
+
+
+
+
+#
+# Check to make sure the static flag actually works.
+#
+wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5
+$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; }
+if ${lt_cv_prog_compiler_static_works_CXX+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_static_works_CXX=no
+ save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS $lt_tmp_static_flag"
+ echo "$lt_simple_link_test_code" > conftest.$ac_ext
+ if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+ # The linker can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ if test -s conftest.err; then
+ # Append any errors to the config.log.
+ cat conftest.err 1>&5
+ $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+ $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+ if diff conftest.exp conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_static_works_CXX=yes
+ fi
+ else
+ lt_cv_prog_compiler_static_works_CXX=yes
+ fi
+ fi
+ $RM -r conftest*
+ LDFLAGS=$save_LDFLAGS
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5
+$as_echo "$lt_cv_prog_compiler_static_works_CXX" >&6; }
+
+if test yes = "$lt_cv_prog_compiler_static_works_CXX"; then
+ :
+else
+ lt_prog_compiler_static_CXX=
+fi
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o_CXX+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_c_o_CXX=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_c_o_CXX=yes
+ fi
+ fi
+ chmod u+w . 2>&5
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5
+$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; }
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if ${lt_cv_prog_compiler_c_o_CXX+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_prog_compiler_c_o_CXX=no
+ $RM -r conftest 2>/dev/null
+ mkdir conftest
+ cd conftest
+ mkdir out
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ lt_compiler_flag="-o out/conftest2.$ac_objext"
+ # Insert the option either (1) after the last *FLAGS variable, or
+ # (2) before a word containing "conftest.", or (3) at the end.
+ # Note that $ac_compile itself does not contain backslashes and begins
+ # with a dollar sign (not a hyphen), so the echo should work correctly.
+ lt_compile=`echo "$ac_compile" | $SED \
+ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+ -e 's:$: $lt_compiler_flag:'`
+ (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+ (eval "$lt_compile" 2>out/conftest.err)
+ ac_status=$?
+ cat out/conftest.err >&5
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ if (exit $ac_status) && test -s out/conftest2.$ac_objext
+ then
+ # The compiler can only warn and ignore the option if not recognized
+ # So say no if there are warnings
+ $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+ $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+ if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+ lt_cv_prog_compiler_c_o_CXX=yes
+ fi
+ fi
+ chmod u+w . 2>&5
+ $RM conftest*
+ # SGI C++ compiler will create directory out/ii_files/ for
+ # template instantiation
+ test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+ $RM out/* && rmdir out
+ cd ..
+ $RM -r conftest
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5
+$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; }
+
+
+
+
+hard_links=nottested
+if test no = "$lt_cv_prog_compiler_c_o_CXX" && test no != "$need_locks"; then
+ # do not overwrite the value of need_locks provided by the user
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5
+$as_echo_n "checking if we can lock with hard links... " >&6; }
+ hard_links=yes
+ $RM conftest*
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ touch conftest.a
+ ln conftest.a conftest.b 2>&5 || hard_links=no
+ ln conftest.a conftest.b 2>/dev/null && hard_links=no
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5
+$as_echo "$hard_links" >&6; }
+ if test no = "$hard_links"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5
+$as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;}
+ need_locks=warn
+ fi
+else
+ need_locks=no
+fi
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; }
+
+ export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
+ case $host_os in
+ aix[4-9]*)
+ # If we're using GNU nm, then we don't want the "-C" option.
+ # -C means demangle to GNU nm, but means don't demangle to AIX nm.
+ # Without the "-l" option, or with the "-B" option, AIX nm treats
+ # weak defined symbols like other global defined symbols, whereas
+ # GNU nm marks them as "W".
+ # While the 'weak' keyword is ignored in the Export File, we need
+ # it in the Import File for the 'aix-soname' feature, so we have
+ # to replace the "-B" option with "-P" for AIX nm.
+ if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+ export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols'
+ else
+ export_symbols_cmds_CXX='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols'
+ fi
+ ;;
+ pw32*)
+ export_symbols_cmds_CXX=$ltdll_cmds
+ ;;
+ cygwin* | mingw* | cegcc*)
+ case $cc_basename in
+ cl*)
+ exclude_expsyms_CXX='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+ ;;
+ *)
+ export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols'
+ exclude_expsyms_CXX='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'
+ ;;
+ esac
+ ;;
+ linux* | k*bsd*-gnu | gnu*)
+ link_all_deplibs_CXX=no
+ ;;
+ *)
+ export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+ ;;
+ esac
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5
+$as_echo "$ld_shlibs_CXX" >&6; }
+test no = "$ld_shlibs_CXX" && can_build_shared=no
+
+with_gnu_ld_CXX=$with_gnu_ld
+
+
+
+
+
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$archive_cmds_need_lc_CXX" in
+x|xyes)
+ # Assume -lc should be added
+ archive_cmds_need_lc_CXX=yes
+
+ if test yes,yes = "$GCC,$enable_shared"; then
+ case $archive_cmds_CXX in
+ *'~'*)
+ # FIXME: we may have to deal with multi-command sequences.
+ ;;
+ '$CC '*)
+ # Test whether the compiler implicitly links with -lc since on some
+ # systems, -lgcc has to come before -lc. If gcc already passes -lc
+ # to ld, don't add -lc before -lgcc.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5
+$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; }
+if ${lt_cv_archive_cmds_need_lc_CXX+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ $RM conftest*
+ echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } 2>conftest.err; then
+ soname=conftest
+ lib=conftest
+ libobjs=conftest.$ac_objext
+ deplibs=
+ wl=$lt_prog_compiler_wl_CXX
+ pic_flag=$lt_prog_compiler_pic_CXX
+ compiler_flags=-v
+ linker_flags=-v
+ verstring=
+ output_objdir=.
+ libname=conftest
+ lt_save_allow_undefined_flag=$allow_undefined_flag_CXX
+ allow_undefined_flag_CXX=
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5
+ (eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ then
+ lt_cv_archive_cmds_need_lc_CXX=no
+ else
+ lt_cv_archive_cmds_need_lc_CXX=yes
+ fi
+ allow_undefined_flag_CXX=$lt_save_allow_undefined_flag
+ else
+ cat conftest.err 1>&5
+ fi
+ $RM conftest*
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc_CXX" >&5
+$as_echo "$lt_cv_archive_cmds_need_lc_CXX" >&6; }
+ archive_cmds_need_lc_CXX=$lt_cv_archive_cmds_need_lc_CXX
+ ;;
+ esac
+ fi
+ ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5
+$as_echo_n "checking dynamic linker characteristics... " >&6; }
+
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=.so
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+
+
+case $host_os in
+aix3*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname.a'
+ shlibpath_var=LIBPATH
+
+ # AIX 3 has no versioning support, so we append a major version to the name.
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+
+aix[4-9]*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ hardcode_into_libs=yes
+ if test ia64 = "$host_cpu"; then
+ # AIX 5 supports IA64
+ library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ else
+ # With GCC up to 2.95.x, collect2 would create an import file
+ # for dependence libraries. The import file would start with
+ # the line '#! .'. This would cause the generated library to
+ # depend on '.', always an invalid library. This was fixed in
+ # development snapshots of GCC prior to 3.0.
+ case $host_os in
+ aix4 | aix4.[01] | aix4.[01].*)
+ if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+ echo ' yes '
+ echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then
+ :
+ else
+ can_build_shared=no
+ fi
+ ;;
+ esac
+ # Using Import Files as archive members, it is possible to support
+ # filename-based versioning of shared library archives on AIX. While
+ # this would work for both with and without runtime linking, it will
+ # prevent static linking of such archives. So we do filename-based
+ # shared library versioning with .so extension only, which is used
+ # when both runtime linking and shared linking is enabled.
+ # Unfortunately, runtime linking may impact performance, so we do
+ # not want this to be the default eventually. Also, we use the
+ # versioned .so libs for executables only if there is the -brtl
+ # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only.
+ # To allow for filename-based versioning support, we need to create
+ # libNAME.so.V as an archive file, containing:
+ # *) an Import File, referring to the versioned filename of the
+ # archive as well as the shared archive member, telling the
+ # bitwidth (32 or 64) of that shared object, and providing the
+ # list of exported symbols of that shared object, eventually
+ # decorated with the 'weak' keyword
+ # *) the shared object with the F_LOADONLY flag set, to really avoid
+ # it being seen by the linker.
+ # At run time we better use the real file rather than another symlink,
+ # but for link time we create the symlink libNAME.so -> libNAME.so.V
+
+ case $with_aix_soname,$aix_use_runtimelinking in
+ # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct
+ # soname into executable. Probably we can add versioning support to
+ # collect2, so additional links can be useful in future.
+ aix,yes) # traditional libtool
+ dynamic_linker='AIX unversionable lib.so'
+ # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+ # instead of lib<name>.a to let people know that these are not
+ # typical AIX shared libraries.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ ;;
+ aix,no) # traditional AIX only
+ dynamic_linker='AIX lib.a(lib.so.V)'
+ # We preserve .a as extension for shared libraries through AIX4.2
+ # and later when we are not doing run time linking.
+ library_names_spec='$libname$release.a $libname.a'
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+ svr4,*) # full svr4 only
+ dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)"
+ library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
+ # We do not specify a path in Import Files, so LIBPATH fires.
+ shlibpath_overrides_runpath=yes
+ ;;
+ *,yes) # both, prefer svr4
+ dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)"
+ library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
+ # unpreferred sharedlib libNAME.a needs extra handling
+ postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"'
+ postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"'
+ # We do not specify a path in Import Files, so LIBPATH fires.
+ shlibpath_overrides_runpath=yes
+ ;;
+ *,no) # both, prefer aix
+ dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)"
+ library_names_spec='$libname$release.a $libname.a'
+ soname_spec='$libname$release$shared_ext$major'
+ # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling
+ postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)'
+ postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"'
+ ;;
+ esac
+ shlibpath_var=LIBPATH
+ fi
+ ;;
+
+amigaos*)
+ case $host_cpu in
+ powerpc)
+ # Since July 2007 AmigaOS4 officially supports .so libraries.
+ # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ ;;
+ m68k)
+ library_names_spec='$libname.ixlibrary $libname.a'
+ # Create ${libname}_ixlibrary.a entries in /sys/libs.
+ finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+ ;;
+ esac
+ ;;
+
+beos*)
+ library_names_spec='$libname$shared_ext'
+ dynamic_linker="$host_os ld.so"
+ shlibpath_var=LIBRARY_PATH
+ ;;
+
+bsdi[45]*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+ # the default ld.so.conf also contains /usr/contrib/lib and
+ # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+ # libtool to hard-code these into programs
+ ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+ version_type=windows
+ shrext_cmds=.dll
+ need_version=no
+ need_lib_prefix=no
+
+ case $GCC,$cc_basename in
+ yes,*)
+ # gcc
+ library_names_spec='$libname.dll.a'
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+
+ case $host_os in
+ cygwin*)
+ # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+ soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+
+ ;;
+ mingw* | cegcc*)
+ # MinGW DLLs use traditional 'lib' prefix
+ soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+ ;;
+ pw32*)
+ # pw32 DLLs use 'pw' prefix rather than 'lib'
+ library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+ ;;
+ esac
+ dynamic_linker='Win32 ld.exe'
+ ;;
+
+ *,cl*)
+ # Native MSVC
+ libname_spec='$name'
+ soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+ library_names_spec='$libname.dll.lib'
+
+ case $build_os in
+ mingw*)
+ sys_lib_search_path_spec=
+ lt_save_ifs=$IFS
+ IFS=';'
+ for lt_path in $LIB
+ do
+ IFS=$lt_save_ifs
+ # Let DOS variable expansion print the short 8.3 style file name.
+ lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"`
+ sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path"
+ done
+ IFS=$lt_save_ifs
+ # Convert to MSYS style.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'`
+ ;;
+ cygwin*)
+ # Convert to unix form, then to dos form, then back to unix form
+ # but this time dos style (no spaces!) so that the unix form looks
+ # like /cygdrive/c/PROGRA~1:/cygdr...
+ sys_lib_search_path_spec=`cygpath --path --unix "$LIB"`
+ sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null`
+ sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ ;;
+ *)
+ sys_lib_search_path_spec=$LIB
+ if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then
+ # It is most probably a Windows format PATH.
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+ else
+ sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+ fi
+ # FIXME: find the short name or the path components, as spaces are
+ # common. (e.g. "Program Files" -> "PROGRA~1")
+ ;;
+ esac
+
+ # DLL is installed to $(libdir)/../bin by postinstall_cmds
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ shlibpath_overrides_runpath=yes
+ dynamic_linker='Win32 link.exe'
+ ;;
+
+ *)
+ # Assume MSVC wrapper
+ library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib'
+ dynamic_linker='Win32 ld.exe'
+ ;;
+ esac
+ # FIXME: first we should search . and the directory the executable is in
+ shlibpath_var=PATH
+ ;;
+
+darwin* | rhapsody*)
+ dynamic_linker="$host_os dyld"
+ version_type=darwin
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$major$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$major$shared_ext'
+ shlibpath_overrides_runpath=yes
+ shlibpath_var=DYLD_LIBRARY_PATH
+ shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+
+ sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+ ;;
+
+dgux*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+freebsd* | dragonfly*)
+ # DragonFly does not have aout. When/if they implement a new
+ # versioning mechanism, adjust this.
+ if test -x /usr/bin/objformat; then
+ objformat=`/usr/bin/objformat`
+ else
+ case $host_os in
+ freebsd[23].*) objformat=aout ;;
+ *) objformat=elf ;;
+ esac
+ fi
+ version_type=freebsd-$objformat
+ case $version_type in
+ freebsd-elf*)
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ need_version=no
+ need_lib_prefix=no
+ ;;
+ freebsd-*)
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ need_version=yes
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_os in
+ freebsd2.*)
+ shlibpath_overrides_runpath=yes
+ ;;
+ freebsd3.[01]* | freebsdelf3.[01]*)
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ freebsd3.[2-9]* | freebsdelf3.[2-9]* | \
+ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1)
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+ *) # from 4.6 on, and DragonFly
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+ esac
+ ;;
+
+haiku*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ dynamic_linker="$host_os runtime_loader"
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'
+ hardcode_into_libs=yes
+ ;;
+
+hpux9* | hpux10* | hpux11*)
+ # Give a soname corresponding to the major version so that dld.sl refuses to
+ # link against other versions.
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ case $host_cpu in
+ ia64*)
+ shrext_cmds='.so'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.so"
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ if test 32 = "$HPUX_IA64_MODE"; then
+ sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+ sys_lib_dlsearch_path_spec=/usr/lib/hpux32
+ else
+ sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+ sys_lib_dlsearch_path_spec=/usr/lib/hpux64
+ fi
+ ;;
+ hppa*64*)
+ shrext_cmds='.sl'
+ hardcode_into_libs=yes
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+ shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+ *)
+ shrext_cmds='.sl'
+ dynamic_linker="$host_os dld.sl"
+ shlibpath_var=SHLIB_PATH
+ shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+ esac
+ # HP-UX runs *really* slowly unless shared libraries are mode 555, ...
+ postinstall_cmds='chmod 555 $lib'
+ # or fails outright, so override atomically:
+ install_override_mode=555
+ ;;
+
+interix[3-9]*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+irix5* | irix6* | nonstopux*)
+ case $host_os in
+ nonstopux*) version_type=nonstopux ;;
+ *)
+ if test yes = "$lt_cv_prog_gnu_ld"; then
+ version_type=linux # correct to gnu/linux during the next big refactor
+ else
+ version_type=irix
+ fi ;;
+ esac
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext'
+ case $host_os in
+ irix5* | nonstopux*)
+ libsuff= shlibsuff=
+ ;;
+ *)
+ case $LD in # libtool.m4 will add one of these switches to LD
+ *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+ libsuff= shlibsuff= libmagic=32-bit;;
+ *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+ libsuff=32 shlibsuff=N32 libmagic=N32;;
+ *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+ libsuff=64 shlibsuff=64 libmagic=64-bit;;
+ *) libsuff= shlibsuff= libmagic=never-match;;
+ esac
+ ;;
+ esac
+ shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff"
+ sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff"
+ hardcode_into_libs=yes
+ ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+ dynamic_linker=no
+ ;;
+
+linux*android*)
+ version_type=none # Android doesn't support versioned libraries.
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext'
+ soname_spec='$libname$release$shared_ext'
+ finish_cmds=
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ dynamic_linker='Android linker'
+ # Don't embed -rpath directories since the linker doesn't support them.
+ hardcode_libdir_flag_spec_CXX='-L$libdir'
+ ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+
+ # Some binutils ld are patched to set DT_RUNPATH
+ if ${lt_cv_shlibpath_overrides_runpath+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_shlibpath_overrides_runpath=no
+ save_LDFLAGS=$LDFLAGS
+ save_libdir=$libdir
+ eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \
+ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+ if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then :
+ lt_cv_shlibpath_overrides_runpath=yes
+fi
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LDFLAGS=$save_LDFLAGS
+ libdir=$save_libdir
+
+fi
+
+ shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ # Ideally, we could use ldconfig to report *all* directores which are
+ # searched for libraries, however this is still not possible. Aside from not
+ # being certain /sbin/ldconfig is available, command
+ # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64,
+ # even though it is searched at run-time. Try to do the best guess by
+ # appending ld.so.conf contents (and includes) to the search path.
+ if test -f /etc/ld.so.conf; then
+ lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
+ sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
+ fi
+
+ # We used to test for /lib/ld.so.1 and disable shared libraries on
+ # powerpc, because MkLinux only supported shared libraries with the
+ # GNU dynamic linker. Since this was broken with cross compilers,
+ # most powerpc-linux boxes support dynamic linking these days and
+ # people can always --disable-shared, the test was removed, and we
+ # assume the GNU/Linux dynamic linker is in use.
+ dynamic_linker='GNU/Linux ld.so'
+ ;;
+
+netbsdelf*-gnu)
+ version_type=linux
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
+ soname_spec='${libname}${release}${shared_ext}$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ dynamic_linker='NetBSD ld.elf_so'
+ ;;
+
+netbsd*)
+ version_type=sunos
+ need_lib_prefix=no
+ need_version=no
+ if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ dynamic_linker='NetBSD (a.out) ld.so'
+ else
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ dynamic_linker='NetBSD ld.elf_so'
+ fi
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ ;;
+
+newsos6)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+*nto* | *qnx*)
+ version_type=qnx
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ dynamic_linker='ldqnx.so'
+ ;;
+
+openbsd* | bitrig*)
+ version_type=sunos
+ sys_lib_dlsearch_path_spec=/usr/lib
+ need_lib_prefix=no
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ need_version=no
+ else
+ need_version=yes
+ fi
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ ;;
+
+os2*)
+ libname_spec='$name'
+ version_type=windows
+ shrext_cmds=.dll
+ need_version=no
+ need_lib_prefix=no
+ # OS/2 can only load a DLL with a base name of 8 characters or less.
+ soname_spec='`test -n "$os2dllname" && libname="$os2dllname";
+ v=$($ECHO $release$versuffix | tr -d .-);
+ n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _);
+ $ECHO $n$v`$shared_ext'
+ library_names_spec='${libname}_dll.$libext'
+ dynamic_linker='OS/2 ld.exe'
+ shlibpath_var=BEGINLIBPATH
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
+ ;;
+
+osf3* | osf4* | osf5*)
+ version_type=osf
+ need_lib_prefix=no
+ need_version=no
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ ;;
+
+rdos*)
+ dynamic_linker=no
+ ;;
+
+solaris*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ # ldd complains unless libraries are executable
+ postinstall_cmds='chmod +x $lib'
+ ;;
+
+sunos4*)
+ version_type=sunos
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ if test yes = "$with_gnu_ld"; then
+ need_lib_prefix=no
+ fi
+ need_version=yes
+ ;;
+
+sysv4 | sysv4.3*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ case $host_vendor in
+ sni)
+ shlibpath_overrides_runpath=no
+ need_lib_prefix=no
+ runpath_var=LD_RUN_PATH
+ ;;
+ siemens)
+ need_lib_prefix=no
+ ;;
+ motorola)
+ need_lib_prefix=no
+ need_version=no
+ shlibpath_overrides_runpath=no
+ sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+ ;;
+ esac
+ ;;
+
+sysv4*MP*)
+ if test -d /usr/nec; then
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext'
+ soname_spec='$libname$shared_ext.$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ fi
+ ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+ version_type=sco
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+ hardcode_into_libs=yes
+ if test yes = "$with_gnu_ld"; then
+ sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+ else
+ sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+ case $host_os in
+ sco3.2v5*)
+ sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+ ;;
+ esac
+ fi
+ sys_lib_dlsearch_path_spec='/usr/lib'
+ ;;
+
+tpf*)
+ # TPF is a cross-target only. Preferred cross-host = GNU/Linux.
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=no
+ hardcode_into_libs=yes
+ ;;
+
+uts4*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ shlibpath_var=LD_LIBRARY_PATH
+ ;;
+
+*)
+ dynamic_linker=no
+ ;;
+esac
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5
+$as_echo "$dynamic_linker" >&6; }
+test no = "$dynamic_linker" && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test yes = "$GCC"; then
+ variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then
+ sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec
+fi
+
+if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then
+ sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec
+fi
+
+# remember unaugmented sys_lib_dlsearch_path content for libtool script decls...
+configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec
+
+# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code
+func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH"
+
+# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool
+configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5
+$as_echo_n "checking how to hardcode library paths into programs... " >&6; }
+hardcode_action_CXX=
+if test -n "$hardcode_libdir_flag_spec_CXX" ||
+ test -n "$runpath_var_CXX" ||
+ test yes = "$hardcode_automatic_CXX"; then
+
+ # We can hardcode non-existent directories.
+ if test no != "$hardcode_direct_CXX" &&
+ # If the only mechanism to avoid hardcoding is shlibpath_var, we
+ # have to relink, otherwise we might link with an installed library
+ # when we should be linking with a yet-to-be-installed one
+ ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" &&
+ test no != "$hardcode_minus_L_CXX"; then
+ # Linking always hardcodes the temporary library directory.
+ hardcode_action_CXX=relink
+ else
+ # We can link without hardcoding, and we can hardcode nonexisting dirs.
+ hardcode_action_CXX=immediate
+ fi
+else
+ # We cannot hardcode anything, or else we can only hardcode existing
+ # directories.
+ hardcode_action_CXX=unsupported
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5
+$as_echo "$hardcode_action_CXX" >&6; }
+
+if test relink = "$hardcode_action_CXX" ||
+ test yes = "$inherit_rpath_CXX"; then
+ # Fast installation is not supported
+ enable_fast_install=no
+elif test yes = "$shlibpath_overrides_runpath" ||
+ test no = "$enable_shared"; then
+ # Fast installation is not necessary
+ enable_fast_install=needless
+fi
+
+
+
+
+
+
+
+ fi # test -n "$compiler"
+
+ CC=$lt_save_CC
+ CFLAGS=$lt_save_CFLAGS
+ LDCXX=$LD
+ LD=$lt_save_LD
+ GCC=$lt_save_GCC
+ with_gnu_ld=$lt_save_with_gnu_ld
+ lt_cv_path_LDCXX=$lt_cv_path_LD
+ lt_cv_path_LD=$lt_save_path_LD
+ lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld
+ lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld
+fi # test yes != "$_lt_caught_CXX_error"
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ac_config_commands="$ac_config_commands libtool"
+
+
+
+
+# Only expand once:
+
+
+# Check whether --enable-debug was given.
+if test "${enable_debug+set}" = set; then :
+ enableval=$enable_debug;
+ CXXFLAGS="-g -O0 -Wall"
+
+$as_echo "#define WITH_DEBUG /**/" >>confdefs.h
+
+
+fi
+
+
+
+# Check whether --with-libldap was given.
+if test "${with_libldap+set}" = set; then :
+ withval=$with_libldap;
+ LIBS="-L$with_libldap $LIBS "
+
+else
+
+ LIBS="-L/usr/local/lib $LIBS "
+
+
+fi
+
+
+
+# Check whether --with-ldap-includes was given.
+if test "${with_ldap_includes+set}" = set; then :
+ withval=$with_ldap_includes;
+ CPPFLAGS="-I$with_ldap_includes $CPPFLAGS "
+
+else
+
+ CPPFLAGS="-I/usr/local/include $CPPFLAGS "
+
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lresolv" >&5
+$as_echo_n "checking for main in -lresolv... " >&6; }
+if ${ac_cv_lib_resolv_main+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lresolv $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+
+int
+main ()
+{
+return main ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_resolv_main=yes
+else
+ ac_cv_lib_resolv_main=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_resolv_main" >&5
+$as_echo "$ac_cv_lib_resolv_main" >&6; }
+if test "x$ac_cv_lib_resolv_main" = xyes; then :
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBRESOLV 1
+_ACEOF
+
+ LIBS="-lresolv $LIBS"
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ber_strdup in -llber" >&5
+$as_echo_n "checking for ber_strdup in -llber... " >&6; }
+if ${ac_cv_lib_lber_ber_strdup+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-llber $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ber_strdup ();
+int
+main ()
+{
+return ber_strdup ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_lber_ber_strdup=yes
+else
+ ac_cv_lib_lber_ber_strdup=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lber_ber_strdup" >&5
+$as_echo "$ac_cv_lib_lber_ber_strdup" >&6; }
+if test "x$ac_cv_lib_lber_ber_strdup" = xyes; then :
+
+ :
+
+else
+
+ echo " didn't find ber_strdup in liblber !";
+ echo " Check for the right version (>= 2.0) of the OpenLDAP libraries";
+ echo " or try the --with-libldap option.";
+ exit
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ldap_add_ext in -lldap" >&5
+$as_echo_n "checking for ldap_add_ext in -lldap... " >&6; }
+if ${ac_cv_lib_ldap_ldap_add_ext+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lldap
+ -llber
+ $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ldap_add_ext ();
+int
+main ()
+{
+return ldap_add_ext ();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_lib_ldap_ldap_add_ext=yes
+else
+ ac_cv_lib_ldap_ldap_add_ext=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ldap_ldap_add_ext" >&5
+$as_echo "$ac_cv_lib_ldap_ldap_add_ext" >&6; }
+if test "x$ac_cv_lib_ldap_ldap_add_ext" = xyes; then :
+
+ :
+
+else
+
+ echo " didn't find ldap_add_ext in libldap !";
+ echo " Check for the right version (>= 2.0) of the OpenLDAP libraries";
+ echo " or try the --with-libldap option.";
+ exit
+
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5
+$as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; }
+if ${ac_cv_header_time+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+
+int
+main ()
+{
+if ((struct tm *) 0)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_header_time=yes
+else
+ ac_cv_header_time=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_time" >&5
+$as_echo "$ac_cv_header_time" >&6; }
+if test $ac_cv_header_time = yes; then
+
+$as_echo "#define TIME_WITH_SYS_TIME 1" >>confdefs.h
+
+fi
+
+for ac_header in termios.h ldap.h
+do :
+ as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+ cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ldap.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "ldap_add_ext" >/dev/null 2>&1; then :
+
+ :
+
+else
+
+ echo " didn't find ldap_add_ext in ldap.h!";
+ echo " Check for the right version (>= 2.0) of the OpenLDAP includes";
+ echo " or try --with-ldap-includes option.";
+ exit
+
+fi
+rm -f conftest*
+
+ac_fn_c_check_header_mongrel "$LINENO" "lber.h" "ac_cv_header_lber_h" "$ac_includes_default"
+if test "x$ac_cv_header_lber_h" = xyes; then :
+
+fi
+
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <lber.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ $EGREP "ber_strdup" >/dev/null 2>&1; then :
+
+ :
+
+else
+
+ echo " didn't find ber_strdup in lber.h!";
+ echo " Check for the right version (>= 2.0) of the OpenLDAP includes";
+ echo " or try --with-ldap-includes option.";
+ exit
+
+fi
+rm -f conftest*
+
+
+
+
+ac_config_files="$ac_config_files Makefile src/Makefile examples/Makefile"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+
+ (set) 2>&1 |
+ case $as_nl`(ac_space=' '; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ # `set' does not quote correctly, so add quotes: double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \.
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;; #(
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+) |
+ sed '
+ /^ac_cv_env_/b end
+ t clear
+ :clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+ if test -w "$cache_file"; then
+ if test "x$cache_file" != "x/dev/null"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+ if test ! -f "$cache_file" || test -h "$cache_file"; then
+ cat confcache >"$cache_file"
+ else
+ case $cache_file in #(
+ */* | ?:*)
+ mv -f confcache "$cache_file"$$ &&
+ mv -f "$cache_file"$$ "$cache_file" ;; #(
+ *)
+ mv -f confcache "$cache_file" ;;
+ esac
+ fi
+ fi
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+ ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+ # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
+ # will be set to the directory where LIBOBJS objects are built.
+ as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+ as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5
+$as_echo_n "checking that generated files are newer than configure... " >&6; }
+ if test -n "$am_sleep_pid"; then
+ # Hide warnings about reused PIDs.
+ wait $am_sleep_pid 2>/dev/null
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5
+$as_echo "done" >&6; }
+ if test -n "$EXEEXT"; then
+ am__EXEEXT_TRUE=
+ am__EXEEXT_FALSE='#'
+else
+ am__EXEEXT_TRUE='#'
+ am__EXEEXT_FALSE=
+fi
+
+if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
+ as_fn_error $? "conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then
+ as_fn_error $? "conditional \"am__fastdepCXX\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
+ as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in #(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -pR'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -pR'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -pR'
+ fi
+else
+ as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by ldapcpplib $as_me , which was
+generated by GNU Autoconf 2.69. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+config_commands="$ac_config_commands"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration. Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number and configuration settings, then exit
+ --config print configuration, then exit
+ -q, --quiet, --silent
+ do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+ --header=FILE[:TEMPLATE]
+ instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to <http://www.openldap.org/its/ >."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+ldapcpplib config.status
+configured by $0, generated by GNU Autoconf 2.69,
+ with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+MKDIR_P='$MKDIR_P'
+AWK='$AWK'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=?*)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ --*=)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=
+ ac_shift=:
+ ;;
+ *)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+ $as_echo "$ac_cs_version"; exit ;;
+ --config | --confi | --conf | --con | --co | --c )
+ $as_echo "$ac_cs_config"; exit ;;
+ --debug | --debu | --deb | --de | --d | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ '') as_fn_error $? "missing file argument" ;;
+ esac
+ as_fn_append CONFIG_FILES " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --header | --heade | --head | --hea )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --he | --h)
+ # Conflict between --help and --header
+ as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+ --help | --hel | -h )
+ $as_echo "$ac_cs_usage"; exit ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+ *) as_fn_append ac_config_targets " $1"
+ ac_need_defaults=false ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+ set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+ shift
+ \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+ CONFIG_SHELL='$SHELL'
+ export CONFIG_SHELL
+ exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+ $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#
+# INIT-COMMANDS
+#
+AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
+
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`'
+macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`'
+enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`'
+enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`'
+pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`'
+enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`'
+shared_archive_member_spec='`$ECHO "$shared_archive_member_spec" | $SED "$delay_single_quote_subst"`'
+SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`'
+ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`'
+PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`'
+host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`'
+host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`'
+host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`'
+build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`'
+build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`'
+build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`'
+SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`'
+Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`'
+GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`'
+EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`'
+FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`'
+LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`'
+NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`'
+LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`'
+max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`'
+ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`'
+exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`'
+lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`'
+lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`'
+lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`'
+lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`'
+lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`'
+reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`'
+reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`'
+OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`'
+deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`'
+file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`'
+file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`'
+want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`'
+DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`'
+sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`'
+AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`'
+AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`'
+archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`'
+STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`'
+RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`'
+old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`'
+old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`'
+old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`'
+lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`'
+CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`'
+CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`'
+compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`'
+GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_import='`$ECHO "$lt_cv_sys_global_symbol_to_import" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`'
+lt_cv_nm_interface='`$ECHO "$lt_cv_nm_interface" | $SED "$delay_single_quote_subst"`'
+nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`'
+lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`'
+lt_cv_truncate_bin='`$ECHO "$lt_cv_truncate_bin" | $SED "$delay_single_quote_subst"`'
+objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`'
+MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`'
+lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`'
+need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`'
+MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`'
+DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`'
+NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`'
+LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`'
+OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`'
+OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`'
+libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`'
+shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`'
+extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`'
+archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`'
+enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`'
+export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`'
+whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`'
+compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`'
+old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`'
+old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`'
+archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`'
+archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`'
+module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`'
+module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`'
+with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`'
+allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`'
+no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`'
+hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`'
+hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`'
+hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`'
+hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`'
+hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`'
+hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`'
+inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`'
+link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`'
+always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`'
+export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`'
+exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`'
+include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`'
+prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`'
+postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`'
+file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`'
+variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`'
+need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`'
+need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`'
+version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`'
+runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`'
+shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`'
+shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`'
+libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`'
+library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`'
+soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`'
+install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`'
+postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`'
+postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`'
+finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`'
+finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`'
+hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`'
+sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`'
+configure_time_dlsearch_path='`$ECHO "$configure_time_dlsearch_path" | $SED "$delay_single_quote_subst"`'
+configure_time_lt_sys_library_path='`$ECHO "$configure_time_lt_sys_library_path" | $SED "$delay_single_quote_subst"`'
+hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`'
+enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`'
+enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`'
+enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`'
+old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`'
+striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`'
+compiler_lib_search_dirs='`$ECHO "$compiler_lib_search_dirs" | $SED "$delay_single_quote_subst"`'
+predep_objects='`$ECHO "$predep_objects" | $SED "$delay_single_quote_subst"`'
+postdep_objects='`$ECHO "$postdep_objects" | $SED "$delay_single_quote_subst"`'
+predeps='`$ECHO "$predeps" | $SED "$delay_single_quote_subst"`'
+postdeps='`$ECHO "$postdeps" | $SED "$delay_single_quote_subst"`'
+compiler_lib_search_path='`$ECHO "$compiler_lib_search_path" | $SED "$delay_single_quote_subst"`'
+LD_CXX='`$ECHO "$LD_CXX" | $SED "$delay_single_quote_subst"`'
+reload_flag_CXX='`$ECHO "$reload_flag_CXX" | $SED "$delay_single_quote_subst"`'
+reload_cmds_CXX='`$ECHO "$reload_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+old_archive_cmds_CXX='`$ECHO "$old_archive_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+compiler_CXX='`$ECHO "$compiler_CXX" | $SED "$delay_single_quote_subst"`'
+GCC_CXX='`$ECHO "$GCC_CXX" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "$lt_prog_compiler_no_builtin_flag_CXX" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_pic_CXX='`$ECHO "$lt_prog_compiler_pic_CXX" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_wl_CXX='`$ECHO "$lt_prog_compiler_wl_CXX" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_static_CXX='`$ECHO "$lt_prog_compiler_static_CXX" | $SED "$delay_single_quote_subst"`'
+lt_cv_prog_compiler_c_o_CXX='`$ECHO "$lt_cv_prog_compiler_c_o_CXX" | $SED "$delay_single_quote_subst"`'
+archive_cmds_need_lc_CXX='`$ECHO "$archive_cmds_need_lc_CXX" | $SED "$delay_single_quote_subst"`'
+enable_shared_with_static_runtimes_CXX='`$ECHO "$enable_shared_with_static_runtimes_CXX" | $SED "$delay_single_quote_subst"`'
+export_dynamic_flag_spec_CXX='`$ECHO "$export_dynamic_flag_spec_CXX" | $SED "$delay_single_quote_subst"`'
+whole_archive_flag_spec_CXX='`$ECHO "$whole_archive_flag_spec_CXX" | $SED "$delay_single_quote_subst"`'
+compiler_needs_object_CXX='`$ECHO "$compiler_needs_object_CXX" | $SED "$delay_single_quote_subst"`'
+old_archive_from_new_cmds_CXX='`$ECHO "$old_archive_from_new_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+old_archive_from_expsyms_cmds_CXX='`$ECHO "$old_archive_from_expsyms_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+archive_cmds_CXX='`$ECHO "$archive_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+archive_expsym_cmds_CXX='`$ECHO "$archive_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+module_cmds_CXX='`$ECHO "$module_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+module_expsym_cmds_CXX='`$ECHO "$module_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+with_gnu_ld_CXX='`$ECHO "$with_gnu_ld_CXX" | $SED "$delay_single_quote_subst"`'
+allow_undefined_flag_CXX='`$ECHO "$allow_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`'
+no_undefined_flag_CXX='`$ECHO "$no_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec_CXX='`$ECHO "$hardcode_libdir_flag_spec_CXX" | $SED "$delay_single_quote_subst"`'
+hardcode_libdir_separator_CXX='`$ECHO "$hardcode_libdir_separator_CXX" | $SED "$delay_single_quote_subst"`'
+hardcode_direct_CXX='`$ECHO "$hardcode_direct_CXX" | $SED "$delay_single_quote_subst"`'
+hardcode_direct_absolute_CXX='`$ECHO "$hardcode_direct_absolute_CXX" | $SED "$delay_single_quote_subst"`'
+hardcode_minus_L_CXX='`$ECHO "$hardcode_minus_L_CXX" | $SED "$delay_single_quote_subst"`'
+hardcode_shlibpath_var_CXX='`$ECHO "$hardcode_shlibpath_var_CXX" | $SED "$delay_single_quote_subst"`'
+hardcode_automatic_CXX='`$ECHO "$hardcode_automatic_CXX" | $SED "$delay_single_quote_subst"`'
+inherit_rpath_CXX='`$ECHO "$inherit_rpath_CXX" | $SED "$delay_single_quote_subst"`'
+link_all_deplibs_CXX='`$ECHO "$link_all_deplibs_CXX" | $SED "$delay_single_quote_subst"`'
+always_export_symbols_CXX='`$ECHO "$always_export_symbols_CXX" | $SED "$delay_single_quote_subst"`'
+export_symbols_cmds_CXX='`$ECHO "$export_symbols_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+exclude_expsyms_CXX='`$ECHO "$exclude_expsyms_CXX" | $SED "$delay_single_quote_subst"`'
+include_expsyms_CXX='`$ECHO "$include_expsyms_CXX" | $SED "$delay_single_quote_subst"`'
+prelink_cmds_CXX='`$ECHO "$prelink_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+postlink_cmds_CXX='`$ECHO "$postlink_cmds_CXX" | $SED "$delay_single_quote_subst"`'
+file_list_spec_CXX='`$ECHO "$file_list_spec_CXX" | $SED "$delay_single_quote_subst"`'
+hardcode_action_CXX='`$ECHO "$hardcode_action_CXX" | $SED "$delay_single_quote_subst"`'
+compiler_lib_search_dirs_CXX='`$ECHO "$compiler_lib_search_dirs_CXX" | $SED "$delay_single_quote_subst"`'
+predep_objects_CXX='`$ECHO "$predep_objects_CXX" | $SED "$delay_single_quote_subst"`'
+postdep_objects_CXX='`$ECHO "$postdep_objects_CXX" | $SED "$delay_single_quote_subst"`'
+predeps_CXX='`$ECHO "$predeps_CXX" | $SED "$delay_single_quote_subst"`'
+postdeps_CXX='`$ECHO "$postdeps_CXX" | $SED "$delay_single_quote_subst"`'
+compiler_lib_search_path_CXX='`$ECHO "$compiler_lib_search_path_CXX" | $SED "$delay_single_quote_subst"`'
+
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+\$1
+_LTECHO_EOF'
+}
+
+# Quote evaled strings.
+for var in SHELL \
+ECHO \
+PATH_SEPARATOR \
+SED \
+GREP \
+EGREP \
+FGREP \
+LD \
+NM \
+LN_S \
+lt_SP2NL \
+lt_NL2SP \
+reload_flag \
+OBJDUMP \
+deplibs_check_method \
+file_magic_cmd \
+file_magic_glob \
+want_nocaseglob \
+DLLTOOL \
+sharedlib_from_linklib_cmd \
+AR \
+AR_FLAGS \
+archiver_list_spec \
+STRIP \
+RANLIB \
+CC \
+CFLAGS \
+compiler \
+lt_cv_sys_global_symbol_pipe \
+lt_cv_sys_global_symbol_to_cdecl \
+lt_cv_sys_global_symbol_to_import \
+lt_cv_sys_global_symbol_to_c_name_address \
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \
+lt_cv_nm_interface \
+nm_file_list_spec \
+lt_cv_truncate_bin \
+lt_prog_compiler_no_builtin_flag \
+lt_prog_compiler_pic \
+lt_prog_compiler_wl \
+lt_prog_compiler_static \
+lt_cv_prog_compiler_c_o \
+need_locks \
+MANIFEST_TOOL \
+DSYMUTIL \
+NMEDIT \
+LIPO \
+OTOOL \
+OTOOL64 \
+shrext_cmds \
+export_dynamic_flag_spec \
+whole_archive_flag_spec \
+compiler_needs_object \
+with_gnu_ld \
+allow_undefined_flag \
+no_undefined_flag \
+hardcode_libdir_flag_spec \
+hardcode_libdir_separator \
+exclude_expsyms \
+include_expsyms \
+file_list_spec \
+variables_saved_for_relink \
+libname_spec \
+library_names_spec \
+soname_spec \
+install_override_mode \
+finish_eval \
+old_striplib \
+striplib \
+compiler_lib_search_dirs \
+predep_objects \
+postdep_objects \
+predeps \
+postdeps \
+compiler_lib_search_path \
+LD_CXX \
+reload_flag_CXX \
+compiler_CXX \
+lt_prog_compiler_no_builtin_flag_CXX \
+lt_prog_compiler_pic_CXX \
+lt_prog_compiler_wl_CXX \
+lt_prog_compiler_static_CXX \
+lt_cv_prog_compiler_c_o_CXX \
+export_dynamic_flag_spec_CXX \
+whole_archive_flag_spec_CXX \
+compiler_needs_object_CXX \
+with_gnu_ld_CXX \
+allow_undefined_flag_CXX \
+no_undefined_flag_CXX \
+hardcode_libdir_flag_spec_CXX \
+hardcode_libdir_separator_CXX \
+exclude_expsyms_CXX \
+include_expsyms_CXX \
+file_list_spec_CXX \
+compiler_lib_search_dirs_CXX \
+predep_objects_CXX \
+postdep_objects_CXX \
+predeps_CXX \
+postdeps_CXX \
+compiler_lib_search_path_CXX; do
+ case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+ *[\\\\\\\`\\"\\\$]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+# Double-quote double-evaled strings.
+for var in reload_cmds \
+old_postinstall_cmds \
+old_postuninstall_cmds \
+old_archive_cmds \
+extract_expsyms_cmds \
+old_archive_from_new_cmds \
+old_archive_from_expsyms_cmds \
+archive_cmds \
+archive_expsym_cmds \
+module_cmds \
+module_expsym_cmds \
+export_symbols_cmds \
+prelink_cmds \
+postlink_cmds \
+postinstall_cmds \
+postuninstall_cmds \
+finish_cmds \
+sys_lib_search_path_spec \
+configure_time_dlsearch_path \
+configure_time_lt_sys_library_path \
+reload_cmds_CXX \
+old_archive_cmds_CXX \
+old_archive_from_new_cmds_CXX \
+old_archive_from_expsyms_cmds_CXX \
+archive_cmds_CXX \
+archive_expsym_cmds_CXX \
+module_cmds_CXX \
+module_expsym_cmds_CXX \
+export_symbols_cmds_CXX \
+prelink_cmds_CXX \
+postlink_cmds_CXX; do
+ case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+ *[\\\\\\\`\\"\\\$]*)
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
+ ;;
+ *)
+ eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+ ;;
+ esac
+done
+
+ac_aux_dir='$ac_aux_dir'
+
+# See if we are running on zsh, and set the options that allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}"; then
+ setopt NO_GLOB_SUBST
+fi
+
+
+ PACKAGE='$PACKAGE'
+ VERSION='$VERSION'
+ RM='$RM'
+ ofile='$ofile'
+
+
+
+
+
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+ case $ac_config_target in
+ "src/config.h") CONFIG_HEADERS="$CONFIG_HEADERS src/config.h" ;;
+ "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
+ "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
+ "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;;
+ "examples/Makefile") CONFIG_FILES="$CONFIG_FILES examples/Makefile" ;;
+
+ *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+ esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+ test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+ test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+ tmp= ac_tmp=
+ trap 'exit_status=$?
+ : "${ac_tmp:=$tmp}"
+ { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+ trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+ test -d "$tmp"
+} ||
+{
+ tmp=./conf$$-$RANDOM
+ (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+ eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+ ac_cs_awk_cr='\\r'
+else
+ ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+ echo "cat >conf$$subs.awk <<_ACEOF" &&
+ echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+ echo "_ACEOF"
+} >conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ . ./conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+ ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+ if test $ac_delim_n = $ac_delim_num; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+ N
+ s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+ for (key in S) S_is_set[key] = 1
+ FS = ""
+
+}
+{
+ line = $ 0
+ nfields = split(line, field, "@")
+ substed = 0
+ len = length(field[1])
+ for (i = 2; i < nfields; i++) {
+ key = field[i]
+ keylen = length(key)
+ if (S_is_set[key]) {
+ value = S[key]
+ line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+ len += length(value) + length(field[++i])
+ substed = 1
+ } else
+ len += 1 + keylen
+ }
+
+ print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+ sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+ cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{
+h
+s///
+s/^/:/
+s/[ ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[ ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[ ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+ ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+ if test -z "$ac_tt"; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any. Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[ ]*#[ ]*define[ ][ ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ for (key in D) D_is_set[key] = 1
+ FS = ""
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+ line = \$ 0
+ split(line, arg, " ")
+ if (arg[1] == "#") {
+ defundef = arg[2]
+ mac1 = arg[3]
+ } else {
+ defundef = substr(arg[1], 2)
+ mac1 = arg[2]
+ }
+ split(mac1, mac2, "(") #)
+ macro = mac2[1]
+ prefix = substr(line, 1, index(line, defundef) - 1)
+ if (D_is_set[macro]) {
+ # Preserve the white space surrounding the "#".
+ print prefix "define", macro P[macro] D[macro]
+ next
+ } else {
+ # Replace #undef with comments. This is necessary, for example,
+ # in the case of _POSIX_SOURCE, which is predefined and required
+ # on some systems where configure will not decide to define it.
+ if (defundef == "undef") {
+ print "/*", prefix defundef, macro, "*/"
+ next
+ }
+ }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS"
+shift
+for ac_tag
+do
+ case $ac_tag in
+ :[FHLC]) ac_mode=$ac_tag; continue;;
+ esac
+ case $ac_mode$ac_tag in
+ :[FHL]*:*);;
+ :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+ :[FH]-) ac_tag=-:-;;
+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+ esac
+ ac_save_IFS=$IFS
+ IFS=:
+ set x $ac_tag
+ IFS=$ac_save_IFS
+ shift
+ ac_file=$1
+ shift
+
+ case $ac_mode in
+ :L) ac_source=$1;;
+ :[FH])
+ ac_file_inputs=
+ for ac_f
+ do
+ case $ac_f in
+ -) ac_f="$ac_tmp/stdin";;
+ *) # Look for the file first in the build tree, then in the source tree
+ # (if the path is not absolute). The absolute path cannot be DOS-style,
+ # because $ac_f cannot contain `:'.
+ test -f "$ac_f" ||
+ case $ac_f in
+ [\\/$]*) false;;
+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+ esac ||
+ as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+ esac
+ case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+ as_fn_append ac_file_inputs " '$ac_f'"
+ done
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ configure_input='Generated from '`
+ $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+ `' by configure.'
+ if test x"$ac_file" != x-; then
+ configure_input="$ac_file. $configure_input"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+ fi
+ # Neutralize special characters interpreted by sed in replacement strings.
+ case $configure_input in #(
+ *\&* | *\|* | *\\* )
+ ac_sed_conf_input=`$as_echo "$configure_input" |
+ sed 's/[\\\\&|]/\\\\&/g'`;; #(
+ *) ac_sed_conf_input=$configure_input;;
+ esac
+
+ case $ac_tag in
+ *:-:* | *:-) cat >"$ac_tmp/stdin" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+ esac
+ ;;
+ esac
+
+ ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ as_dir="$ac_dir"; as_fn_mkdir_p
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+ case $ac_mode in
+ :F)
+ #
+ # CONFIG_FILE
+ #
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+ esac
+ ac_MKDIR_P=$MKDIR_P
+ case $MKDIR_P in
+ [\\/$]* | ?:[\\/]* ) ;;
+ */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
+ esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+ p
+ q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ ac_datarootdir_hack='
+ s&@datadir@&$datadir&g
+ s&@docdir@&$docdir&g
+ s&@infodir@&$infodir&g
+ s&@localedir@&$localedir&g
+ s&@mandir@&$mandir&g
+ s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+s&@MKDIR_P@&$ac_MKDIR_P&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \
+ "$ac_tmp/out"`; test -z "$ac_out"; } &&
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&2;}
+
+ rm -f "$ac_tmp/stdin"
+ case $ac_file in
+ -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+ *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+ esac \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+ :H)
+ #
+ # CONFIG_HEADER
+ #
+ if test x"$ac_file" != x-; then
+ {
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+ } >"$ac_tmp/config.h" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+ else
+ rm -f "$ac_file"
+ mv "$ac_tmp/config.h" "$ac_file" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ fi
+ else
+ $as_echo "/* $configure_input */" \
+ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+ || as_fn_error $? "could not create -" "$LINENO" 5
+ fi
+# Compute "$ac_file"'s index in $config_headers.
+_am_arg="$ac_file"
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+ case $_am_header in
+ $_am_arg | $_am_arg:* )
+ break ;;
+ * )
+ _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+ esac
+done
+echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" ||
+$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$_am_arg" : 'X\(//\)[^/]' \| \
+ X"$_am_arg" : 'X\(//\)$' \| \
+ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$_am_arg" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`/stamp-h$_am_stamp_count
+ ;;
+
+ :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
+$as_echo "$as_me: executing $ac_file commands" >&6;}
+ ;;
+ esac
+
+
+ case $ac_file$ac_mode in
+ "depfiles":C) test x"$AMDEP_TRUE" != x"" || {
+ # Older Autoconf quotes --file arguments for eval, but not when files
+ # are listed without --file. Let's play safe and only enable the eval
+ # if we detect the quoting.
+ case $CONFIG_FILES in
+ *\'*) eval set x "$CONFIG_FILES" ;;
+ *) set x $CONFIG_FILES ;;
+ esac
+ shift
+ for mf
+ do
+ # Strip MF so we end up with the name of the file.
+ mf=`echo "$mf" | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile or not.
+ # We used to match only the files named 'Makefile.in', but
+ # some people rename them; so instead we look at the file content.
+ # Grep'ing the first line is not enough: some people post-process
+ # each Makefile.in and add a new line on top of each file to say so.
+ # Grep'ing the whole file is not good either: AIX grep has a line
+ # limit of 2048, but all sed's we know have understand at least 4000.
+ if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
+ dirpart=`$as_dirname -- "$mf" ||
+$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$mf" : 'X\(//\)[^/]' \| \
+ X"$mf" : 'X\(//\)$' \| \
+ X"$mf" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$mf" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ else
+ continue
+ fi
+ # Extract the definition of DEPDIR, am__include, and am__quote
+ # from the Makefile without running 'make'.
+ DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+ test -z "$DEPDIR" && continue
+ am__include=`sed -n 's/^am__include = //p' < "$mf"`
+ test -z "$am__include" && continue
+ am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+ # Find all dependency output files, they are included files with
+ # $(DEPDIR) in their names. We invoke sed twice because it is the
+ # simplest approach to changing $(DEPDIR) to its actual value in the
+ # expansion.
+ for file in `sed -n "
+ s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
+ # Make sure the directory exists.
+ test -f "$dirpart/$file" && continue
+ fdir=`$as_dirname -- "$file" ||
+$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$file" : 'X\(//\)[^/]' \| \
+ X"$file" : 'X\(//\)$' \| \
+ X"$file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ as_dir=$dirpart/$fdir; as_fn_mkdir_p
+ # echo "creating $dirpart/$file"
+ echo '# dummy' > "$dirpart/$file"
+ done
+ done
+}
+ ;;
+ "libtool":C)
+
+ # See if we are running on zsh, and set the options that allow our
+ # commands through without removal of \ escapes.
+ if test -n "${ZSH_VERSION+set}"; then
+ setopt NO_GLOB_SUBST
+ fi
+
+ cfgfile=${ofile}T
+ trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+ $RM "$cfgfile"
+
+ cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+# Generated automatically by $as_me ($PACKAGE) $VERSION
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+
+# Provide generalized library-building support services.
+# Written by Gordon Matzigkeit, 1996
+
+# Copyright (C) 2014 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of of the License, or
+# (at your option) any later version.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program or library that is built
+# using GNU Libtool, you may include this file under the same
+# distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# The names of the tagged configurations supported by this script.
+available_tags='CXX '
+
+# Configured defaults for sys_lib_dlsearch_path munging.
+: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"}
+
+# ### BEGIN LIBTOOL CONFIG
+
+# Which release of libtool.m4 was used?
+macro_version=$macro_version
+macro_revision=$macro_revision
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# What type of objects to build.
+pic_mode=$pic_mode
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# Shared archive member basename,for filename based shared library versioning on AIX.
+shared_archive_member_spec=$shared_archive_member_spec
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# An echo program that protects backslashes.
+ECHO=$lt_ECHO
+
+# The PATH separator for the build system.
+PATH_SEPARATOR=$lt_PATH_SEPARATOR
+
+# The host system.
+host_alias=$host_alias
+host=$host
+host_os=$host_os
+
+# The build system.
+build_alias=$build_alias
+build=$build
+build_os=$build_os
+
+# A sed program that does not truncate output.
+SED=$lt_SED
+
+# Sed that helps us avoid accidentally triggering echo(1) options like -n.
+Xsed="\$SED -e 1s/^X//"
+
+# A grep program that handles long lines.
+GREP=$lt_GREP
+
+# An ERE matcher.
+EGREP=$lt_EGREP
+
+# A literal string matcher.
+FGREP=$lt_FGREP
+
+# A BSD- or MS-compatible name lister.
+NM=$lt_NM
+
+# Whether we need soft or hard links.
+LN_S=$lt_LN_S
+
+# What is the maximum length of a command?
+max_cmd_len=$max_cmd_len
+
+# Object file suffix (normally "o").
+objext=$ac_objext
+
+# Executable file suffix (normally "").
+exeext=$exeext
+
+# whether the shell understands "unset".
+lt_unset=$lt_unset
+
+# turn spaces into newlines.
+SP2NL=$lt_lt_SP2NL
+
+# turn newlines into spaces.
+NL2SP=$lt_lt_NL2SP
+
+# convert \$build file names to \$host format.
+to_host_file_cmd=$lt_cv_to_host_file_cmd
+
+# convert \$build files to toolchain format.
+to_tool_file_cmd=$lt_cv_to_tool_file_cmd
+
+# An object symbol dumper.
+OBJDUMP=$lt_OBJDUMP
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method = "file_magic".
+file_magic_cmd=$lt_file_magic_cmd
+
+# How to find potential files when deplibs_check_method = "file_magic".
+file_magic_glob=$lt_file_magic_glob
+
+# Find potential files using nocaseglob when deplibs_check_method = "file_magic".
+want_nocaseglob=$lt_want_nocaseglob
+
+# DLL creation program.
+DLLTOOL=$lt_DLLTOOL
+
+# Command to associate shared and link libraries.
+sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd
+
+# The archiver.
+AR=$lt_AR
+
+# Flags to create an archive.
+AR_FLAGS=$lt_AR_FLAGS
+
+# How to feed a file listing to the archiver.
+archiver_list_spec=$lt_archiver_list_spec
+
+# A symbol stripping program.
+STRIP=$lt_STRIP
+
+# Commands used to install an old-style archive.
+RANLIB=$lt_RANLIB
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# Whether to use a lock for old archive extraction.
+lock_old_archive_extraction=$lock_old_archive_extraction
+
+# A C compiler.
+LTCC=$lt_CC
+
+# LTCC compiler flags.
+LTCFLAGS=$lt_CFLAGS
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration.
+global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+
+# Transform the output of nm into a list of symbols to manually relocate.
+global_symbol_to_import=$lt_lt_cv_sys_global_symbol_to_import
+
+# Transform the output of nm in a C name address pair.
+global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
+
+# Transform the output of nm in a C name address pair when lib prefix is needed.
+global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix
+
+# The name lister interface.
+nm_interface=$lt_lt_cv_nm_interface
+
+# Specify filename containing input files for \$NM.
+nm_file_list_spec=$lt_nm_file_list_spec
+
+# The root where to search for dependent libraries,and where our libraries should be installed.
+lt_sysroot=$lt_sysroot
+
+# Command to truncate a binary pipe.
+lt_truncate_bin=$lt_lt_cv_truncate_bin
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# Used to examine libraries when file_magic_cmd begins with "file".
+MAGIC_CMD=$MAGIC_CMD
+
+# Must we lock files when doing compilation?
+need_locks=$lt_need_locks
+
+# Manifest tool.
+MANIFEST_TOOL=$lt_MANIFEST_TOOL
+
+# Tool to manipulate archived DWARF debug symbol files on Mac OS X.
+DSYMUTIL=$lt_DSYMUTIL
+
+# Tool to change global to local symbols on Mac OS X.
+NMEDIT=$lt_NMEDIT
+
+# Tool to manipulate fat objects and archives on Mac OS X.
+LIPO=$lt_LIPO
+
+# ldd/readelf like tool for Mach-O binaries on Mac OS X.
+OTOOL=$lt_OTOOL
+
+# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4.
+OTOOL64=$lt_OTOOL64
+
+# Old archive suffix (normally "a").
+libext=$libext
+
+# Shared library suffix (normally ".so").
+shrext_cmds=$lt_shrext_cmds
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at link time.
+variables_saved_for_relink=$lt_variables_saved_for_relink
+
+# Do we need the "lib" prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Library versioning type.
+version_type=$version_type
+
+# Shared library runtime path variable.
+runpath_var=$runpath_var
+
+# Shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names. First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Permission mode override for installation of shared libraries.
+install_override_mode=$lt_install_override_mode
+
+# Command to use after installation of a shared archive.
+postinstall_cmds=$lt_postinstall_cmds
+
+# Command to use after uninstallation of a shared archive.
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# As "finish_cmds", except a single script fragment to be evaled but
+# not shown.
+finish_eval=$lt_finish_eval
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Compile-time system search path for libraries.
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Detected run-time system search path for libraries.
+sys_lib_dlsearch_path_spec=$lt_configure_time_dlsearch_path
+
+# Explicit LT_SYS_LIBRARY_PATH set during ./configure time.
+configure_time_lt_sys_library_path=$lt_configure_time_lt_sys_library_path
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+
+# The linker used to build libraries.
+LD=$lt_LD
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# Commands used to build an old-style archive.
+old_archive_cmds=$lt_old_archive_cmds
+
+# A language specific compiler.
+CC=$lt_compiler
+
+# Is the compiler the GNU compiler?
+with_gcc=$GCC
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc
+
+# Whether or not to disallow shared libs when runtime libs are static.
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec
+
+# Whether the compiler copes with passing no objects directly.
+compiler_needs_object=$lt_compiler_needs_object
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
+
+# Commands used to build a shared archive.
+archive_cmds=$lt_archive_cmds
+archive_expsym_cmds=$lt_archive_expsym_cmds
+
+# Commands used to build a loadable module if different from building
+# a shared archive.
+module_cmds=$lt_module_cmds
+module_expsym_cmds=$lt_module_expsym_cmds
+
+# Whether we are building with GNU ld or not.
+with_gnu_ld=$lt_with_gnu_ld
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag
+
+# Flag that enforces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
+
+# Whether we need a single "-rpath" flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator
+
+# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes
+# DIR into the resulting binary.
+hardcode_direct=$hardcode_direct
+
+# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes
+# DIR into the resulting binary and the resulting library dependency is
+# "absolute",i.e impossible to change by setting \$shlibpath_var if the
+# library is relocated.
+hardcode_direct_absolute=$hardcode_direct_absolute
+
+# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+# into the resulting binary.
+hardcode_minus_L=$hardcode_minus_L
+
+# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+# into the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var
+
+# Set to "yes" if building a shared library automatically hardcodes DIR
+# into the library and all subsequent libraries and executables linked
+# against it.
+hardcode_automatic=$hardcode_automatic
+
+# Set to yes if linker adds runtime paths of dependent libraries
+# to runtime path list.
+inherit_rpath=$inherit_rpath
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs
+
+# Set to "yes" if exported symbols are required.
+always_export_symbols=$always_export_symbols
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms
+
+# Commands necessary for linking programs (against libraries) with templates.
+prelink_cmds=$lt_prelink_cmds
+
+# Commands necessary for finishing linking programs.
+postlink_cmds=$lt_postlink_cmds
+
+# Specify filename containing input files.
+file_list_spec=$lt_file_list_spec
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action
+
+# The directories searched by this compiler when creating a shared library.
+compiler_lib_search_dirs=$lt_compiler_lib_search_dirs
+
+# Dependencies to place before and after the objects being linked to
+# create a shared library.
+predep_objects=$lt_predep_objects
+postdep_objects=$lt_postdep_objects
+predeps=$lt_predeps
+postdeps=$lt_postdeps
+
+# The library search path used internally by the compiler when linking
+# a shared library.
+compiler_lib_search_path=$lt_compiler_lib_search_path
+
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+ cat <<'_LT_EOF' >> "$cfgfile"
+
+# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE
+
+# func_munge_path_list VARIABLE PATH
+# -----------------------------------
+# VARIABLE is name of variable containing _space_ separated list of
+# directories to be munged by the contents of PATH, which is string
+# having a format:
+# "DIR[:DIR]:"
+# string "DIR[ DIR]" will be prepended to VARIABLE
+# ":DIR[:DIR]"
+# string "DIR[ DIR]" will be appended to VARIABLE
+# "DIRP[:DIRP]::[DIRA:]DIRA"
+# string "DIRP[ DIRP]" will be prepended to VARIABLE and string
+# "DIRA[ DIRA]" will be appended to VARIABLE
+# "DIR[:DIR]"
+# VARIABLE will be replaced by "DIR[ DIR]"
+func_munge_path_list ()
+{
+ case x$2 in
+ x)
+ ;;
+ *:)
+ eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\"
+ ;;
+ x:*)
+ eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\"
+ ;;
+ *::*)
+ eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\"
+ eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\"
+ ;;
+ *)
+ eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\"
+ ;;
+ esac
+}
+
+
+# Calculate cc_basename. Skip known compiler wrappers and cross-prefix.
+func_cc_basename ()
+{
+ for cc_temp in $*""; do
+ case $cc_temp in
+ compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+ distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+ done
+ func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+}
+
+
+# ### END FUNCTIONS SHARED WITH CONFIGURE
+
+_LT_EOF
+
+ case $host_os in
+ aix3*)
+ cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program. For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test set != "${COLLECT_NAMES+set}"; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+fi
+_LT_EOF
+ ;;
+ esac
+
+
+ltmain=$ac_aux_dir/ltmain.sh
+
+
+ # We use sed instead of cat because bash on DJGPP gets confused if
+ # if finds mixed CR/LF and LF-only lines. Since sed operates in
+ # text mode, it properly converts lines to CR/LF. This bash problem
+ # is reportedly fixed, but why not run on old versions too?
+ sed '$q' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ mv -f "$cfgfile" "$ofile" ||
+ (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+ chmod +x "$ofile"
+
+
+ cat <<_LT_EOF >> "$ofile"
+
+# ### BEGIN LIBTOOL TAG CONFIG: CXX
+
+# The linker used to build libraries.
+LD=$lt_LD_CXX
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag_CXX
+reload_cmds=$lt_reload_cmds_CXX
+
+# Commands used to build an old-style archive.
+old_archive_cmds=$lt_old_archive_cmds_CXX
+
+# A language specific compiler.
+CC=$lt_compiler_CXX
+
+# Is the compiler the GNU compiler?
+with_gcc=$GCC_CXX
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic_CXX
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl_CXX
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static_CXX
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc_CXX
+
+# Whether or not to disallow shared libs when runtime libs are static.
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX
+
+# Whether the compiler copes with passing no objects directly.
+compiler_needs_object=$lt_compiler_needs_object_CXX
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX
+
+# Commands used to build a shared archive.
+archive_cmds=$lt_archive_cmds_CXX
+archive_expsym_cmds=$lt_archive_expsym_cmds_CXX
+
+# Commands used to build a loadable module if different from building
+# a shared archive.
+module_cmds=$lt_module_cmds_CXX
+module_expsym_cmds=$lt_module_expsym_cmds_CXX
+
+# Whether we are building with GNU ld or not.
+with_gnu_ld=$lt_with_gnu_ld_CXX
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag_CXX
+
+# Flag that enforces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag_CXX
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX
+
+# Whether we need a single "-rpath" flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX
+
+# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes
+# DIR into the resulting binary.
+hardcode_direct=$hardcode_direct_CXX
+
+# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes
+# DIR into the resulting binary and the resulting library dependency is
+# "absolute",i.e impossible to change by setting \$shlibpath_var if the
+# library is relocated.
+hardcode_direct_absolute=$hardcode_direct_absolute_CXX
+
+# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+# into the resulting binary.
+hardcode_minus_L=$hardcode_minus_L_CXX
+
+# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+# into the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX
+
+# Set to "yes" if building a shared library automatically hardcodes DIR
+# into the library and all subsequent libraries and executables linked
+# against it.
+hardcode_automatic=$hardcode_automatic_CXX
+
+# Set to yes if linker adds runtime paths of dependent libraries
+# to runtime path list.
+inherit_rpath=$inherit_rpath_CXX
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs_CXX
+
+# Set to "yes" if exported symbols are required.
+always_export_symbols=$always_export_symbols_CXX
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds_CXX
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms_CXX
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms_CXX
+
+# Commands necessary for linking programs (against libraries) with templates.
+prelink_cmds=$lt_prelink_cmds_CXX
+
+# Commands necessary for finishing linking programs.
+postlink_cmds=$lt_postlink_cmds_CXX
+
+# Specify filename containing input files.
+file_list_spec=$lt_file_list_spec_CXX
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action_CXX
+
+# The directories searched by this compiler when creating a shared library.
+compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX
+
+# Dependencies to place before and after the objects being linked to
+# create a shared library.
+predep_objects=$lt_predep_objects_CXX
+postdep_objects=$lt_postdep_objects_CXX
+predeps=$lt_predeps_CXX
+postdeps=$lt_postdeps_CXX
+
+# The library search path used internally by the compiler when linking
+# a shared library.
+compiler_lib_search_path=$lt_compiler_lib_search_path_CXX
+
+# ### END LIBTOOL TAG CONFIG: CXX
+_LT_EOF
+
+ ;;
+
+ esac
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+ as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
diff --git a/contrib/ldapc++/configure.ac b/contrib/ldapc++/configure.ac
new file mode 100644
index 0000000..287a57a
--- /dev/null
+++ b/contrib/ldapc++/configure.ac
@@ -0,0 +1,101 @@
+dnl $OpenLDAP$
+
+dnl Copyright 2000-2022 The OpenLDAP Foundation. All Rights Reserved.
+dnl COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+
+dnl Process this file with autoconf to produce a configure script.
+
+AC_COPYRIGHT([[Copyright 2000-2022 The OpenLDAP Foundation. All rights reserved.
+Restrictions apply, see COPYRIGHT and LICENSE files.]])
+AC_REVISION([$Id: 98b3f7d81f7f9f1b793e68dc179b79b866fc8786 $])
+AC_INIT(ldapcpplib, [] , [http://www.openldap.org/its/] )
+AC_CONFIG_SRCDIR(src/LDAPConnection.h)
+AM_INIT_AUTOMAKE(foreign)
+AM_CONFIG_HEADER(src/config.h)
+
+eval `$ac_aux_dir/version.sh`
+if test -z "$OL_CPP_API_RELEASE"; then
+ AC_MSG_ERROR([could not determine version])
+fi
+
+VERSION=$OL_CPP_API_RELEASE
+OPENLDAP_CPP_API_VERSION=$OL_CPP_API_VERSION
+AC_SUBST(VERSION)
+AC_SUBST(OPENLDAP_CPP_API_VERSION)
+dnl Checks for programs.
+AC_PROG_INSTALL
+dnl AC_PROG_CC
+AC_PROG_CXX
+dnl AC_PROG_RANLIB
+dnl AM_DISABLE_SHARED
+AC_PROG_LIBTOOL
+dnl AC_PROG_MAKE_SET
+AC_ARG_ENABLE(debug,[ --enable-debug],[
+ CXXFLAGS="-g -O0 -Wall"
+ AC_DEFINE(WITH_DEBUG,[],[Define to 1 ot enable debug logging])
+ ],
+)
+
+AC_ARG_WITH(libldap,[ --with-libldap=DIR Path to the libldap library [/usr/local/lib]],[
+ LIBS="-L$with_libldap $LIBS "
+ ],[
+ LIBS="-L/usr/local/lib $LIBS "
+ ]
+)
+
+AC_ARG_WITH(ldap-includes,[ --with-ldap-includes=DIR Path to the libldap include files [/usr/local/include]],[
+ CPPFLAGS="-I$with_ldap_includes $CPPFLAGS "
+ ],[
+ CPPFLAGS="-I/usr/local/include $CPPFLAGS "
+ ]
+)
+dnl Checks for libraries.
+AC_CHECK_LIB(resolv,main)
+AC_CHECK_LIB(lber,ber_strdup,[
+dnl NOOP
+ :
+ ],[
+ echo " didn't find ber_strdup in liblber !";
+ echo " Check for the right version (>= 2.0) of the OpenLDAP libraries";
+ echo " or try the --with-libldap option.";
+ exit
+ ])
+AC_CHECK_LIB(ldap,ldap_add_ext,[
+dnl NOOP
+ :
+ ],[
+ echo " didn't find ldap_add_ext in libldap !";
+ echo " Check for the right version (>= 2.0) of the OpenLDAP libraries";
+ echo " or try the --with-libldap option.";
+ exit
+ ],[
+ -llber
+ ])
+dnl Checks for header files.
+AC_HEADER_TIME
+AC_CHECK_HEADERS(termios.h ldap.h)
+AC_EGREP_HEADER(ldap_add_ext,ldap.h,[
+dnl NOOP
+ :
+ ],[
+ echo " didn't find ldap_add_ext in ldap.h!";
+ echo " Check for the right version (>= 2.0) of the OpenLDAP includes";
+ echo " or try --with-ldap-includes option.";
+ exit
+ ])
+AC_CHECK_HEADER(lber.h)
+AC_EGREP_HEADER(ber_strdup,lber.h,[
+dnl NOOP
+ :
+ ],[
+ echo " didn't find ber_strdup in lber.h!";
+ echo " Check for the right version (>= 2.0) of the OpenLDAP includes";
+ echo " or try --with-ldap-includes option.";
+ exit
+ ])
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+
+dnl Checks for library functions.
+
+AC_OUTPUT(Makefile src/Makefile examples/Makefile)
diff --git a/contrib/ldapc++/depcomp b/contrib/ldapc++/depcomp
new file mode 100755
index 0000000..04701da
--- /dev/null
+++ b/contrib/ldapc++/depcomp
@@ -0,0 +1,530 @@
+#! /bin/sh
+# depcomp - compile a program generating dependencies as side-effects
+
+scriptversion=2005-07-09.11
+
+# Copyright (C) 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
+
+case $1 in
+ '')
+ echo "$0: No command. Try \`$0 --help' for more information." 1>&2
+ exit 1;
+ ;;
+ -h | --h*)
+ cat <<\EOF
+Usage: depcomp [--help] [--version] PROGRAM [ARGS]
+
+Run PROGRAMS ARGS to compile a file, generating dependencies
+as side-effects.
+
+Environment variables:
+ depmode Dependency tracking mode.
+ source Source file read by `PROGRAMS ARGS'.
+ object Object file output by `PROGRAMS ARGS'.
+ DEPDIR directory where to store dependencies.
+ depfile Dependency file to output.
+ tmpdepfile Temporary file to use when outputing dependencies.
+ libtool Whether libtool is used (yes/no).
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+ exit $?
+ ;;
+ -v | --v*)
+ echo "depcomp $scriptversion"
+ exit $?
+ ;;
+esac
+
+if test -z "$depmode" || test -z "$source" || test -z "$object"; then
+ echo "depcomp: Variables source, object and depmode must be set" 1>&2
+ exit 1
+fi
+
+# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
+depfile=${depfile-`echo "$object" |
+ sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
+tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
+
+rm -f "$tmpdepfile"
+
+# Some modes work just like other modes, but use different flags. We
+# parameterize here, but still list the modes in the big case below,
+# to make depend.m4 easier to write. Note that we *cannot* use a case
+# here, because this file can only contain one case statement.
+if test "$depmode" = hp; then
+ # HP compiler uses -M and no extra arg.
+ gccflag=-M
+ depmode=gcc
+fi
+
+if test "$depmode" = dashXmstdout; then
+ # This is just like dashmstdout with a different argument.
+ dashmflag=-xM
+ depmode=dashmstdout
+fi
+
+case "$depmode" in
+gcc3)
+## gcc 3 implements dependency tracking that does exactly what
+## we want. Yay! Note: for some reason libtool 1.4 doesn't like
+## it if -MD -MP comes after the -MF stuff. Hmm.
+ "$@" -MT "$object" -MD -MP -MF "$tmpdepfile"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ mv "$tmpdepfile" "$depfile"
+ ;;
+
+gcc)
+## There are various ways to get dependency output from gcc. Here's
+## why we pick this rather obscure method:
+## - Don't want to use -MD because we'd like the dependencies to end
+## up in a subdir. Having to rename by hand is ugly.
+## (We might end up doing this anyway to support other compilers.)
+## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
+## -MM, not -M (despite what the docs say).
+## - Using -M directly means running the compiler twice (even worse
+## than renaming).
+ if test -z "$gccflag"; then
+ gccflag=-MD,
+ fi
+ "$@" -Wp,"$gccflag$tmpdepfile"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+## The second -e expression handles DOS-style file names with drive letters.
+ sed -e 's/^[^:]*: / /' \
+ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
+## This next piece of magic avoids the `deleted header file' problem.
+## The problem is that when a header file which appears in a .P file
+## is deleted, the dependency causes make to die (because there is
+## typically no way to rebuild the header). We avoid this by adding
+## dummy dependencies for each header file. Too bad gcc doesn't do
+## this for us directly.
+ tr ' ' '
+' < "$tmpdepfile" |
+## Some versions of gcc put a space before the `:'. On the theory
+## that the space means something, we add a space to the output as
+## well.
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+hp)
+ # This case exists only to let depend.m4 do its work. It works by
+ # looking at the text of this script. This case will never be run,
+ # since it is checked for above.
+ exit 1
+ ;;
+
+sgi)
+ if test "$libtool" = yes; then
+ "$@" "-Wp,-MDupdate,$tmpdepfile"
+ else
+ "$@" -MDupdate "$tmpdepfile"
+ fi
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+
+ if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
+ echo "$object : \\" > "$depfile"
+
+ # Clip off the initial element (the dependent). Don't try to be
+ # clever and replace this with sed code, as IRIX sed won't handle
+ # lines with more than a fixed number of characters (4096 in
+ # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
+ # the IRIX cc adds comments like `#:fec' to the end of the
+ # dependency line.
+ tr ' ' '
+' < "$tmpdepfile" \
+ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
+ tr '
+' ' ' >> $depfile
+ echo >> $depfile
+
+ # The second pass generates a dummy entry for each header file.
+ tr ' ' '
+' < "$tmpdepfile" \
+ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
+ >> $depfile
+ else
+ # The sourcefile does not contain any dependencies, so just
+ # store a dummy comment line, to avoid errors with the Makefile
+ # "include basename.Plo" scheme.
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+aix)
+ # The C for AIX Compiler uses -M and outputs the dependencies
+ # in a .u file. In older versions, this file always lives in the
+ # current directory. Also, the AIX compiler puts `$object:' at the
+ # start of each line; $object doesn't have directory information.
+ # Version 6 uses the directory in both cases.
+ stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'`
+ tmpdepfile="$stripped.u"
+ if test "$libtool" = yes; then
+ "$@" -Wc,-M
+ else
+ "$@" -M
+ fi
+ stat=$?
+
+ if test -f "$tmpdepfile"; then :
+ else
+ stripped=`echo "$stripped" | sed 's,^.*/,,'`
+ tmpdepfile="$stripped.u"
+ fi
+
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+
+ if test -f "$tmpdepfile"; then
+ outname="$stripped.o"
+ # Each line is of the form `foo.o: dependent.h'.
+ # Do two passes, one to just change these to
+ # `$object: dependent.h' and one to simply `dependent.h:'.
+ sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile"
+ sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile"
+ else
+ # The sourcefile does not contain any dependencies, so just
+ # store a dummy comment line, to avoid errors with the Makefile
+ # "include basename.Plo" scheme.
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+icc)
+ # Intel's C compiler understands `-MD -MF file'. However on
+ # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
+ # ICC 7.0 will fill foo.d with something like
+ # foo.o: sub/foo.c
+ # foo.o: sub/foo.h
+ # which is wrong. We want:
+ # sub/foo.o: sub/foo.c
+ # sub/foo.o: sub/foo.h
+ # sub/foo.c:
+ # sub/foo.h:
+ # ICC 7.1 will output
+ # foo.o: sub/foo.c sub/foo.h
+ # and will wrap long lines using \ :
+ # foo.o: sub/foo.c ... \
+ # sub/foo.h ... \
+ # ...
+
+ "$@" -MD -MF "$tmpdepfile"
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile"
+ exit $stat
+ fi
+ rm -f "$depfile"
+ # Each line is of the form `foo.o: dependent.h',
+ # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
+ # Do two passes, one to just change these to
+ # `$object: dependent.h' and one to simply `dependent.h:'.
+ sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
+ # Some versions of the HPUX 10.20 sed can't process this invocation
+ # correctly. Breaking it into two sed invocations is a workaround.
+ sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
+ sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+tru64)
+ # The Tru64 compiler uses -MD to generate dependencies as a side
+ # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
+ # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
+ # dependencies in `foo.d' instead, so we check for that too.
+ # Subdirectories are respected.
+ dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+ test "x$dir" = "x$object" && dir=
+ base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+
+ if test "$libtool" = yes; then
+ # With Tru64 cc, shared objects can also be used to make a
+ # static library. This mecanism is used in libtool 1.4 series to
+ # handle both shared and static libraries in a single compilation.
+ # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
+ #
+ # With libtool 1.5 this exception was removed, and libtool now
+ # generates 2 separate objects for the 2 libraries. These two
+ # compilations output dependencies in in $dir.libs/$base.o.d and
+ # in $dir$base.o.d. We have to check for both files, because
+ # one of the two compilations can be disabled. We should prefer
+ # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
+ # automatically cleaned when .libs/ is deleted, while ignoring
+ # the former would cause a distcleancheck panic.
+ tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4
+ tmpdepfile2=$dir$base.o.d # libtool 1.5
+ tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5
+ tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504
+ "$@" -Wc,-MD
+ else
+ tmpdepfile1=$dir$base.o.d
+ tmpdepfile2=$dir$base.d
+ tmpdepfile3=$dir$base.d
+ tmpdepfile4=$dir$base.d
+ "$@" -MD
+ fi
+
+ stat=$?
+ if test $stat -eq 0; then :
+ else
+ rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
+ exit $stat
+ fi
+
+ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
+ do
+ test -f "$tmpdepfile" && break
+ done
+ if test -f "$tmpdepfile"; then
+ sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+ # That's a tab and a space in the [].
+ sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+ else
+ echo "#dummy" > "$depfile"
+ fi
+ rm -f "$tmpdepfile"
+ ;;
+
+#nosideeffect)
+ # This comment above is used by automake to tell side-effect
+ # dependency tracking mechanisms from slower ones.
+
+dashmstdout)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout, regardless of -o.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test $1 != '--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ # Remove `-o $object'.
+ IFS=" "
+ for arg
+ do
+ case $arg in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift # fnord
+ shift # $arg
+ ;;
+ esac
+ done
+
+ test -z "$dashmflag" && dashmflag=-M
+ # Require at least two characters before searching for `:'
+ # in the target name. This is to cope with DOS-style filenames:
+ # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise.
+ "$@" $dashmflag |
+ sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile"
+ rm -f "$depfile"
+ cat < "$tmpdepfile" > "$depfile"
+ tr ' ' '
+' < "$tmpdepfile" | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+dashXmstdout)
+ # This case only exists to satisfy depend.m4. It is never actually
+ # run, as this mode is specially recognized in the preamble.
+ exit 1
+ ;;
+
+makedepend)
+ "$@" || exit $?
+ # Remove any Libtool call
+ if test "$libtool" = yes; then
+ while test $1 != '--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+ # X makedepend
+ shift
+ cleared=no
+ for arg in "$@"; do
+ case $cleared in
+ no)
+ set ""; shift
+ cleared=yes ;;
+ esac
+ case "$arg" in
+ -D*|-I*)
+ set fnord "$@" "$arg"; shift ;;
+ # Strip any option that makedepend may not understand. Remove
+ # the object too, otherwise makedepend will parse it as a source file.
+ -*|$object)
+ ;;
+ *)
+ set fnord "$@" "$arg"; shift ;;
+ esac
+ done
+ obj_suffix="`echo $object | sed 's/^.*\././'`"
+ touch "$tmpdepfile"
+ ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
+ rm -f "$depfile"
+ cat < "$tmpdepfile" > "$depfile"
+ sed '1,2d' "$tmpdepfile" | tr ' ' '
+' | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly. Breaking it into two sed invocations is a workaround.
+ sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile" "$tmpdepfile".bak
+ ;;
+
+cpp)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout.
+ "$@" || exit $?
+
+ # Remove the call to Libtool.
+ if test "$libtool" = yes; then
+ while test $1 != '--mode=compile'; do
+ shift
+ done
+ shift
+ fi
+
+ # Remove `-o $object'.
+ IFS=" "
+ for arg
+ do
+ case $arg in
+ -o)
+ shift
+ ;;
+ $object)
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift # fnord
+ shift # $arg
+ ;;
+ esac
+ done
+
+ "$@" -E |
+ sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
+ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
+ sed '$ s: \\$::' > "$tmpdepfile"
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ cat < "$tmpdepfile" >> "$depfile"
+ sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+msvisualcpp)
+ # Important note: in order to support this mode, a compiler *must*
+ # always write the preprocessed file to stdout, regardless of -o,
+ # because we must use -o when running libtool.
+ "$@" || exit $?
+ IFS=" "
+ for arg
+ do
+ case "$arg" in
+ "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
+ set fnord "$@"
+ shift
+ shift
+ ;;
+ *)
+ set fnord "$@" "$arg"
+ shift
+ shift
+ ;;
+ esac
+ done
+ "$@" -E |
+ sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile"
+ rm -f "$depfile"
+ echo "$object : \\" > "$depfile"
+ . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile"
+ echo " " >> "$depfile"
+ . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile"
+ rm -f "$tmpdepfile"
+ ;;
+
+none)
+ exec "$@"
+ ;;
+
+*)
+ echo "Unknown depmode $depmode" 1>&2
+ exit 1
+ ;;
+esac
+
+exit 0
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/contrib/ldapc++/doxygen.rc b/contrib/ldapc++/doxygen.rc
new file mode 100644
index 0000000..594a3de
--- /dev/null
+++ b/contrib/ldapc++/doxygen.rc
@@ -0,0 +1,1313 @@
+# $OpenLDAP$
+
+# Doxyfile 1.5.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file that
+# follow. The default is UTF-8 which is also the encoding used for all text before
+# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into
+# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of
+# possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = ldapsdk
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = 0.0.1
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = srcdoc
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian,
+# Italian, Japanese, Japanese-en (Japanese with English messages), Korean,
+# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian,
+# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = yes
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = yes
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for Java.
+# For instance, namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to
+# include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct (or union) is
+# documented as struct with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code where the coding convention is that all structs are
+# typedef'ed and only the typedef is referenced never the struct's name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = NO
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be extracted
+# and appear in the documentation as a namespace called 'anonymous_namespace{file}',
+# where file will be replaced with the base name of the file that contains the anonymous
+# namespace. By default anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from the
+# version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text "
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = ./src
+
+# This tag can be used to specify the character encoding of the source files that
+# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default
+# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding.
+# See http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS = *.cpp \
+ *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = yes
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the output.
+# The symbol name can be a fully qualified name, a word, or if the wildcard * is used,
+# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output. If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO. If you have enabled CALL_GRAPH or CALLER_GRAPH
+# then you must also enable this option. If you don't then doxygen will produce
+# a warning and turn it on anyway
+
+SOURCE_BROWSER = no
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code. Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT =
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = no
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT =
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = no
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT =
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader. This is useful
+# if you want to understand what is going on. On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to
+# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to
+# specify the directory where the mscgen tool resides. If left empty the tool is assumed to
+# be found in the default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will
+# generate a call dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES then doxygen will
+# generate a caller dependency graph for every global function or class method.
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the number
+# of direct children of the root node in a graph is already larger than
+# MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, which results in a white background.
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+
+DOT_TRANSPARENT = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
diff --git a/contrib/ldapc++/examples/Makefile.am b/contrib/ldapc++/examples/Makefile.am
new file mode 100644
index 0000000..ae25cb4
--- /dev/null
+++ b/contrib/ldapc++/examples/Makefile.am
@@ -0,0 +1,20 @@
+# $OpenLDAP$
+
+##
+# Copyright 2003-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+##
+AM_CPPFLAGS = -I$(top_srcdir)/src
+noinst_PROGRAMS = main readSchema startTls urlTest
+
+main_SOURCES = main.cpp
+main_LDADD = ../src/libldapcpp.la
+
+readSchema_SOURCES = readSchema.cpp
+readSchema_LDADD = ../src/libldapcpp.la
+
+startTls_SOURCES = startTls.cpp
+startTls_LDADD = ../src/libldapcpp.la
+
+urlTest_SOURCES = urlTest.cpp
+urlTest_LDADD = ../src/libldapcpp.la
diff --git a/contrib/ldapc++/examples/Makefile.in b/contrib/ldapc++/examples/Makefile.in
new file mode 100644
index 0000000..3c7db97
--- /dev/null
+++ b/contrib/ldapc++/examples/Makefile.in
@@ -0,0 +1,506 @@
+# Makefile.in generated by automake 1.11 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# $OpenLDAP$
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+noinst_PROGRAMS = main$(EXEEXT) readSchema$(EXEEXT) startTls$(EXEEXT) \
+ urlTest$(EXEEXT)
+subdir = examples
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/src/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+PROGRAMS = $(noinst_PROGRAMS)
+am_main_OBJECTS = main.$(OBJEXT)
+main_OBJECTS = $(am_main_OBJECTS)
+main_DEPENDENCIES = ../src/libldapcpp.la
+am_readSchema_OBJECTS = readSchema.$(OBJEXT)
+readSchema_OBJECTS = $(am_readSchema_OBJECTS)
+readSchema_DEPENDENCIES = ../src/libldapcpp.la
+am_startTls_OBJECTS = startTls.$(OBJEXT)
+startTls_OBJECTS = $(am_startTls_OBJECTS)
+startTls_DEPENDENCIES = ../src/libldapcpp.la
+am_urlTest_OBJECTS = urlTest.$(OBJEXT)
+urlTest_OBJECTS = $(am_urlTest_OBJECTS)
+urlTest_DEPENDENCIES = ../src/libldapcpp.la
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/src
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(main_SOURCES) $(readSchema_SOURCES) $(startTls_SOURCES) \
+ $(urlTest_SOURCES)
+DIST_SOURCES = $(main_SOURCES) $(readSchema_SOURCES) \
+ $(startTls_SOURCES) $(urlTest_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENLDAP_CPP_API_VERSION = @OPENLDAP_CPP_API_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+# Copyright 2003-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+AM_CPPFLAGS = -I$(top_srcdir)/src
+main_SOURCES = main.cpp
+main_LDADD = ../src/libldapcpp.la
+readSchema_SOURCES = readSchema.cpp
+readSchema_LDADD = ../src/libldapcpp.la
+startTls_SOURCES = startTls.cpp
+startTls_LDADD = ../src/libldapcpp.la
+urlTest_SOURCES = urlTest.cpp
+urlTest_LDADD = ../src/libldapcpp.la
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .cpp .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign examples/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign examples/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+main$(EXEEXT): $(main_OBJECTS) $(main_DEPENDENCIES)
+ @rm -f main$(EXEEXT)
+ $(CXXLINK) $(main_OBJECTS) $(main_LDADD) $(LIBS)
+readSchema$(EXEEXT): $(readSchema_OBJECTS) $(readSchema_DEPENDENCIES)
+ @rm -f readSchema$(EXEEXT)
+ $(CXXLINK) $(readSchema_OBJECTS) $(readSchema_LDADD) $(LIBS)
+startTls$(EXEEXT): $(startTls_OBJECTS) $(startTls_DEPENDENCIES)
+ @rm -f startTls$(EXEEXT)
+ $(CXXLINK) $(startTls_OBJECTS) $(startTls_LDADD) $(LIBS)
+urlTest$(EXEEXT): $(urlTest_OBJECTS) $(urlTest_DEPENDENCIES)
+ @rm -f urlTest$(EXEEXT)
+ $(CXXLINK) $(urlTest_OBJECTS) $(urlTest_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/readSchema.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/startTls.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/urlTest.Po@am__quote@
+
+.cpp.o:
+@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
+
+.cpp.obj:
+@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cpp.lo:
+@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-noinstPROGRAMS ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/contrib/ldapc++/examples/main.cpp b/contrib/ldapc++/examples/main.cpp
new file mode 100644
index 0000000..c4b8001
--- /dev/null
+++ b/contrib/ldapc++/examples/main.cpp
@@ -0,0 +1,134 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include <iostream>
+#include <sstream>
+#include "LDAPConnection.h"
+#include "LDAPConstraints.h"
+#include "LDAPSearchReference.h"
+#include "LDAPSearchResults.h"
+#include "LDAPAttribute.h"
+#include "LDAPAttributeList.h"
+#include "LDAPEntry.h"
+#include "LDAPException.h"
+#include "LDAPModification.h"
+
+#include "debug.h"
+
+int main(){
+ LDAPConstraints* cons=new LDAPConstraints;
+ LDAPControlSet* ctrls=new LDAPControlSet;
+ ctrls->add(LDAPCtrl(LDAP_CONTROL_MANAGEDSAIT));
+ cons->setServerControls(ctrls);
+ LDAPConnection *lc=new LDAPConnection("localhost",9009);
+ lc->setConstraints(cons);
+ std::cout << "----------------------doing bind...." << std::endl;
+ try{
+ lc->bind("cn=Manager,o=Organisation,c=DE" , "secret",cons);
+ std::cout << lc->getHost() << std::endl;
+ bool result = lc->compare("cn=Manager,o=Organisation,c=DE",
+ LDAPAttribute("cn","Manager"));
+ std::cout << "Compare: " << result << std::endl;
+
+ LDAPAttributeList* attrs=new LDAPAttributeList();
+ StringList values;
+ StringList s2;
+ values.add("top");
+ values.add("Person");
+ attrs->addAttribute(LDAPAttribute("objectClass",values));
+ attrs->addAttribute(LDAPAttribute("cn","Peter"));
+ attrs->addAttribute(LDAPAttribute("sn","Peter,hallo"));
+ LDAPEntry* entry=new LDAPEntry(
+ "cn=Peter , o=Organisation, c=DE", attrs);
+// lc->add(entry);
+
+// lc->del("ou=Groups,o=Organisation,c=DE");
+
+ LDAPSearchResults* entries = lc->search("o=Organisation,c=DE",
+ LDAPConnection::SEARCH_ONE);
+ if (entries != 0){
+ LDAPEntry* entry = entries->getNext();
+ if(entry != 0){
+ std::cout << *(entry) << std::endl;
+ }
+ while(entry){
+ try{
+ entry = entries->getNext();
+ if(entry != 0){
+ std::cout << *(entry) << std::endl;
+ }
+ delete entry;
+ }catch(LDAPReferralException e){
+ std::cout << "Caught Referral" << std::endl;
+ }
+ }
+ }
+
+ lc->unbind();
+ delete lc;
+ }catch (LDAPException &e){
+ std::cout << "-------------- caught Exception ---------"<< std::endl;
+ std::cout << e << std::endl;
+ }
+
+ /*
+ std::cout << "--------------------starting search" << std::endl;
+ LDAPAttributeList* attrs=new LDAPAttributeList();
+ StringList values;
+ values.add("top");
+ values.add("organizationalUnit");
+ attrs->addAttribute(LDAPAttribute("objectClass",values));
+ attrs->addAttribute(LDAPAttribute("ou","Groups"));
+ LDAPEntry* entry=new LDAPEntry(
+ "ou=Groups, o=Organisation, c=DE", attrs);
+
+ LDAPAttribute newattr("description");
+ LDAPModification::mod_op op = LDAPModification::OP_DELETE;
+ LDAPModList *mod=new LDAPModList();
+ mod->addModification(LDAPModification(newattr,op));
+ LDAPMessageQueue* q=0;
+ try{
+ q=lc->search("o=Organisation,c=de",LDAPAsynConnection::SEARCH_SUB,
+ "objectClass=*",StringList());
+// q=lc->add(entry);
+// q=lc->modify("cn=Manager,o=Organisation,c=DE",
+// mod);
+ LDAPMsg *res=q->getNext();
+ bool cont=true;
+ while( cont ) {
+ switch(res->getMessageType()){
+ LDAPSearchResult *res2;
+ const LDAPEntry *entry;
+ case LDAP_RES_SEARCH_ENTRY :
+ res2= (LDAPSearchResult*)res;
+ entry= res2->getEntry();
+ std::cout << "Entry: " << *entry << std::endl;
+ delete res;
+ res=q->getNext();
+ break;
+ case LDAP_RES_SEARCH_REFERENCE :
+ std::cout << "Reference: " << std::endl;
+ delete res;
+ res=q->getNext();
+ break;
+ default :
+ std::cout << ( *(LDAPResult*) res) << std::endl;
+ delete res;
+ std::cout << "-----------------search done" << std::endl;
+ cont=false;
+ break;
+ }
+ }
+ delete q;
+ }catch (LDAPException e){
+ std::cout << "----------------error during search" << std::endl;
+ delete q;
+ std::cout << e << std::endl;
+ }
+ lc->unbind();
+ */
+}
+
diff --git a/contrib/ldapc++/examples/readSchema.cpp b/contrib/ldapc++/examples/readSchema.cpp
new file mode 100644
index 0000000..4a69cf1
--- /dev/null
+++ b/contrib/ldapc++/examples/readSchema.cpp
@@ -0,0 +1,73 @@
+// $OpenLDAP$
+/*
+ * Copyright 2008-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include <iostream>
+#include <sstream>
+#include "LDAPConnection.h"
+#include "LDAPConstraints.h"
+#include "LDAPSearchReference.h"
+#include "LDAPSearchResults.h"
+#include "LDAPAttribute.h"
+#include "LDAPAttributeList.h"
+#include "LDAPEntry.h"
+#include "LDAPException.h"
+#include "LDAPModification.h"
+#include "LDAPSchema.h"
+
+#include "debug.h"
+
+int main(){
+ LDAPConnection *lc=new LDAPConnection("192.168.3.128",389);
+ std::cout << "----------------------doing bind...." << std::endl;
+ try{
+ lc->bind("uid=admin,dc=home,dc=local" , "secret");
+ std::cout << lc->getHost() << std::endl;
+ StringList tmp;
+ tmp.add("subschemasubentry");
+ LDAPSearchResults* entries = lc->search("",
+ LDAPConnection::SEARCH_BASE,
+ "(objectClass=*)",
+ tmp );
+ LDAPEntry* rootDse = entries->getNext();
+ std::string schemabase="cn=subschema";
+
+ if(rootDse){
+ const LDAPAttribute* schemaAttr = rootDse->getAttributes()->getAttributeByName("subschemaSubentry");
+ schemabase = *(schemaAttr->getValues().begin());
+ }
+ StringList attrs;
+ attrs.add("objectClasses");
+ attrs.add("attributeTypes");
+ entries = lc->search(schemabase, LDAPConnection::SEARCH_BASE, "(objectClass=*)",
+ attrs);
+ if (entries != 0){
+ LDAPEntry* entry = entries->getNext();
+ if(entry != 0){
+ const LDAPAttribute* oc = entry->getAttributes()->getAttributeByName("objectClasses");
+ LDAPSchema schema;
+ schema.setObjectClasses((oc->getValues()));
+ LDAPObjClass test = schema.getObjectClassByName("inetOrgPerson");
+ std::cout << test.getDesc() << std::endl;
+// StringList mustAttr = test.getMay();
+// for( StringList::const_iterator i = mustAttr.begin(); i != mustAttr.end(); i++ ){
+// std::cout << *i << std::endl;
+// }
+ StringList sup = test.getSup();
+ for( StringList::const_iterator i = sup.begin(); i != sup.end(); i++ ){
+ std::cout << *i << std::endl;
+ }
+ }
+ }
+
+ lc->unbind();
+ delete lc;
+ }catch (LDAPException e){
+ std::cout << "---------------- caught Exception ---------"<< std::endl;
+ std::cout << e << std::endl;
+ }
+
+}
+
diff --git a/contrib/ldapc++/examples/startTls.cpp b/contrib/ldapc++/examples/startTls.cpp
new file mode 100644
index 0000000..b864cb9
--- /dev/null
+++ b/contrib/ldapc++/examples/startTls.cpp
@@ -0,0 +1,79 @@
+// $OpenLDAP$
+/*
+ * Copyright 2010-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include <iostream>
+#include <string>
+#include "LDAPAsynConnection.h"
+#include "TlsOptions.h"
+
+int main( int argc, char* argv[]){
+ if ( argc != 4 ){
+ std::cerr << "usage: " << argv[0] << " <ldap-uri> <cacertfile> <cacertdir>" << std::endl;
+ return(-1);
+ }
+ std::string uri(argv[1]);
+ std::string cacert(argv[2]);
+ std::string cadir(argv[3]);
+ TlsOptions tls;
+ std::cout << "Current global settings:" << std::endl;
+ std::cout << " CaCertfile: " << tls.getStringOption( TlsOptions::CACERTFILE) << std::endl;
+ std::cout << " CaCertDir: " << tls.getStringOption( TlsOptions::CACERTDIR ) << std::endl;
+ std::cout << " Require Cert: " << tls.getIntOption( TlsOptions::REQUIRE_CERT ) << std::endl;
+ std::cout << "Applying new settings:" << std::endl;
+ tls.setOption( TlsOptions::CACERTFILE, cacert );
+ tls.setOption( TlsOptions::REQUIRE_CERT, TlsOptions::DEMAND );
+ std::cout << " CaCertfile: " << tls.getStringOption( TlsOptions::CACERTFILE ) << std::endl;
+ std::cout << " Require Cert: " << tls.getIntOption( TlsOptions::REQUIRE_CERT ) << std::endl;
+
+ try {
+ // 1. connect using global options
+ LDAPAsynConnection l(uri);
+ try {
+ l.start_tls();
+ std::cout << "StartTLS successful." << std::endl;
+ l.unbind();
+ } catch ( LDAPException e ) {
+ std::cerr << e << std::endl;
+ }
+
+ // 2. connect using connection specific option
+ LDAPAsynConnection l1(uri);
+ tls=l1.getTlsOptions();
+ std::cout << "Current connection specific settings:" << std::endl;
+ std::cout << " CaCertfile: " << tls.getStringOption( TlsOptions::CACERTFILE) << std::endl;
+ std::cout << " CaCertDir: " << tls.getStringOption( TlsOptions::CACERTDIR ) << std::endl;
+ std::cout << " Require Cert: " << tls.getIntOption( TlsOptions::REQUIRE_CERT ) << std::endl;
+ std::cout << "Applying new settings:" << std::endl;
+ tls.setOption( TlsOptions::CACERTDIR, cadir );
+ tls.setOption( TlsOptions::REQUIRE_CERT, TlsOptions::DEMAND );
+ std::cout << " CaCertDir: " << tls.getStringOption( TlsOptions::CACERTDIR ) << std::endl;
+ std::cout << " Require Cert: " << tls.getIntOption( TlsOptions::REQUIRE_CERT ) << std::endl;
+ try {
+ l1.start_tls();
+ std::cout << "StartTLS successful." << std::endl;
+ l1.unbind();
+ } catch ( LDAPException e ) {
+ std::cerr << e << std::endl;
+ }
+
+ // 3. and once again using the globals
+ try {
+ LDAPAsynConnection l2(uri);
+ TlsOptions tls2;
+ std::cout << "Current global settings:" << std::endl;
+ std::cout << " CaCertfile: " << tls2.getStringOption( TlsOptions::CACERTFILE) << std::endl;
+ std::cout << " CaCertDir: " << tls2.getStringOption( TlsOptions::CACERTDIR ) << std::endl;
+ std::cout << " Require Cert: " << tls2.getIntOption( TlsOptions::REQUIRE_CERT ) << std::endl;
+ l2.start_tls();
+ std::cout << "StartTLS successful." << std::endl;
+ l2.unbind();
+ } catch ( LDAPException e ) {
+ std::cerr << e << std::endl;
+ }
+ } catch ( LDAPException e ) {
+ std::cerr << e << std::endl;
+ }
+}
diff --git a/contrib/ldapc++/examples/urlTest.cpp b/contrib/ldapc++/examples/urlTest.cpp
new file mode 100644
index 0000000..6223630
--- /dev/null
+++ b/contrib/ldapc++/examples/urlTest.cpp
@@ -0,0 +1,41 @@
+// $OpenLDAP$
+/*
+ * Copyright 2008-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include <LDAPUrl.h>
+#include <LDAPException.h>
+#include <cstdlib>
+#include <iostream>
+
+int main(int argc, char *argv[]) {
+ if ( argc != 2 ) {
+ std::cout << argc << std::endl;
+ std::cout << "urlTest <ldap-URI>" << std::endl;
+ exit(1);
+ }
+ std::string uristr = argv[1];
+ try {
+ LDAPUrl url(uristr);
+ std::cout << "Host: " << url.getHost() << std::endl;
+ std::cout << "Port: " << url.getPort() << std::endl;
+ std::cout << "BaseDN: " << url.getDN() << std::endl;
+ std::cout << "Scope: " << url.getScope() << std::endl;
+ StringList attrs = url.getAttrs();
+ std::cout << "Attrs: " << std::endl;
+ StringList::const_iterator i = attrs.begin();
+ for( ; i != attrs.end(); i++ ) {
+ std::cout << " " << *i << std::endl;
+ }
+ std::cout << "Filter: " << url.getFilter() << std::endl;
+ std::cout << "Setting new BaseDN" << std::endl;
+ url.setDN("o=Beispiel, c=DE");
+ std::cout << "Url: " << url.getURLString() << std::endl;
+ } catch (LDAPUrlException e) {
+ std::cout << e.getCode() << std::endl;
+ std::cout << e.getErrorMessage() << std::endl;
+ std::cout << e.getAdditionalInfo() << std::endl;
+ }
+
+}
diff --git a/contrib/ldapc++/install-sh b/contrib/ldapc++/install-sh
new file mode 100755
index 0000000..4d4a951
--- /dev/null
+++ b/contrib/ldapc++/install-sh
@@ -0,0 +1,323 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2005-05-14.22
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+chmodcmd="$chmodprog 0755"
+chowncmd=
+chgrpcmd=
+stripcmd=
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=
+dst=
+dir_arg=
+dstarg=
+no_target_directory=
+
+usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+ or: $0 [OPTION]... SRCFILES... DIRECTORY
+ or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+ or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+-c (ignored)
+-d create directories instead of installing files.
+-g GROUP $chgrpprog installed files to GROUP.
+-m MODE $chmodprog installed files to MODE.
+-o USER $chownprog installed files to USER.
+-s $stripprog installed files.
+-t DIRECTORY install into DIRECTORY.
+-T report an error if DSTFILE is a directory.
+--help display this help and exit.
+--version display version info and exit.
+
+Environment variables override the default commands:
+ CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG
+"
+
+while test -n "$1"; do
+ case $1 in
+ -c) shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ --help) echo "$usage"; exit $?;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd=$stripprog
+ shift
+ continue;;
+
+ -t) dstarg=$2
+ shift
+ shift
+ continue;;
+
+ -T) no_target_directory=true
+ shift
+ continue;;
+
+ --version) echo "$0 $scriptversion"; exit $?;;
+
+ *) # When -d is used, all remaining arguments are directories to create.
+ # When -t is used, the destination is already specified.
+ test -n "$dir_arg$dstarg" && break
+ # Otherwise, the last argument is the destination. Remove it from $@.
+ for arg
+ do
+ if test -n "$dstarg"; then
+ # $@ is not empty: it contains at least $arg.
+ set fnord "$@" "$dstarg"
+ shift # fnord
+ fi
+ shift # arg
+ dstarg=$arg
+ done
+ break;;
+ esac
+done
+
+if test -z "$1"; then
+ if test -z "$dir_arg"; then
+ echo "$0: no input file specified." >&2
+ exit 1
+ fi
+ # It's OK to call `install-sh -d' without argument.
+ # This can happen when creating conditional directories.
+ exit 0
+fi
+
+for src
+do
+ # Protect names starting with `-'.
+ case $src in
+ -*) src=./$src ;;
+ esac
+
+ if test -n "$dir_arg"; then
+ dst=$src
+ src=
+
+ if test -d "$dst"; then
+ mkdircmd=:
+ chmodcmd=
+ else
+ mkdircmd=$mkdirprog
+ fi
+ else
+ # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+ # might cause directories to be created, which would be especially bad
+ # if $src (and thus $dsttmp) contains '*'.
+ if test ! -f "$src" && test ! -d "$src"; then
+ echo "$0: $src does not exist." >&2
+ exit 1
+ fi
+
+ if test -z "$dstarg"; then
+ echo "$0: no destination specified." >&2
+ exit 1
+ fi
+
+ dst=$dstarg
+ # Protect names starting with `-'.
+ case $dst in
+ -*) dst=./$dst ;;
+ esac
+
+ # If destination is a directory, append the input filename; won't work
+ # if double slashes aren't ignored.
+ if test -d "$dst"; then
+ if test -n "$no_target_directory"; then
+ echo "$0: $dstarg: Is a directory" >&2
+ exit 1
+ fi
+ dst=$dst/`basename "$src"`
+ fi
+ fi
+
+ # This sed command emulates the dirname command.
+ dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'`
+
+ # Make sure that the destination directory exists.
+
+ # Skip lots of stat calls in the usual case.
+ if test ! -d "$dstdir"; then
+ defaultIFS='
+ '
+ IFS="${IFS-$defaultIFS}"
+
+ oIFS=$IFS
+ # Some sh's can't handle IFS=/ for some reason.
+ IFS='%'
+ set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'`
+ shift
+ IFS=$oIFS
+
+ pathcomp=
+
+ while test $# -ne 0 ; do
+ pathcomp=$pathcomp$1
+ shift
+ if test ! -d "$pathcomp"; then
+ $mkdirprog "$pathcomp"
+ # mkdir can fail with a `File exist' error in case several
+ # install-sh are creating the directory concurrently. This
+ # is OK.
+ test -d "$pathcomp" || exit
+ fi
+ pathcomp=$pathcomp/
+ done
+ fi
+
+ if test -n "$dir_arg"; then
+ $doit $mkdircmd "$dst" \
+ && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \
+ && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \
+ && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \
+ && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; }
+
+ else
+ dstfile=`basename "$dst"`
+
+ # Make a couple of temp file names in the proper directory.
+ dsttmp=$dstdir/_inst.$$_
+ rmtmp=$dstdir/_rm.$$_
+
+ # Trap to clean up those temp files at exit.
+ trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+ trap '(exit $?); exit' 1 2 13 15
+
+ # Copy the file name to the temp name.
+ $doit $cpprog "$src" "$dsttmp" &&
+
+ # and set any options; do chmod last to preserve setuid bits.
+ #
+ # If any of these fail, we abort the whole thing. If we want to
+ # ignore errors from any of these, just make sure not to ignore
+ # errors from the above "$doit $cpprog $src $dsttmp" command.
+ #
+ { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \
+ && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \
+ && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \
+ && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } &&
+
+ # Now rename the file to the real destination.
+ { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \
+ || {
+ # The rename failed, perhaps because mv can't rename something else
+ # to itself, or perhaps because mv is so ancient that it does not
+ # support -f.
+
+ # Now remove or move aside any old file at destination location.
+ # We try this two ways since rm can't unlink itself on some
+ # systems and the destination file might be busy for other
+ # reasons. In this case, the final cleanup might fail but the new
+ # file should still install successfully.
+ {
+ if test -f "$dstdir/$dstfile"; then
+ $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \
+ || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \
+ || {
+ echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2
+ (exit 1); exit 1
+ }
+ else
+ :
+ fi
+ } &&
+
+ # Now rename the file to the real destination.
+ $doit $mvcmd "$dsttmp" "$dstdir/$dstfile"
+ }
+ }
+ fi || { (exit 1); exit 1; }
+done
+
+# The final little trick to "correctly" pass the exit status to the exit trap.
+{
+ (exit 0); exit 0
+}
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/contrib/ldapc++/ltmain.sh b/contrib/ldapc++/ltmain.sh
new file mode 100644
index 0000000..0f0a2da
--- /dev/null
+++ b/contrib/ldapc++/ltmain.sh
@@ -0,0 +1,11147 @@
+#! /bin/sh
+## DO NOT EDIT - This file generated from ./build-aux/ltmain.in
+## by inline-source v2014-01-03.01
+
+# libtool (GNU libtool) 2.4.6
+# Provide generalized library-building support services.
+# Written by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+
+# Copyright (C) 1996-2015 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+PROGRAM=libtool
+PACKAGE=libtool
+VERSION=2.4.6
+package_revision=2.4.6
+
+
+## ------ ##
+## Usage. ##
+## ------ ##
+
+# Run './libtool --help' for help with using this script from the
+# command line.
+
+
+## ------------------------------- ##
+## User overridable command paths. ##
+## ------------------------------- ##
+
+# After configure completes, it has a better idea of some of the
+# shell tools we need than the defaults used by the functions shared
+# with bootstrap, so set those here where they can still be over-
+# ridden by the user, but otherwise take precedence.
+
+: ${AUTOCONF="autoconf"}
+: ${AUTOMAKE="automake"}
+
+
+## -------------------------- ##
+## Source external libraries. ##
+## -------------------------- ##
+
+# Much of our low-level functionality needs to be sourced from external
+# libraries, which are installed to $pkgauxdir.
+
+# Set a version string for this script.
+scriptversion=2015-01-20.17; # UTC
+
+# General shell script boiler plate, and helper functions.
+# Written by Gary V. Vaughan, 2004
+
+# Copyright (C) 2004-2015 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# As a special exception to the GNU General Public License, if you distribute
+# this file as part of a program or library that is built using GNU Libtool,
+# you may include this file under the same distribution terms that you use
+# for the rest of that program.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNES FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Please report bugs or propose patches to gary@gnu.org.
+
+
+## ------ ##
+## Usage. ##
+## ------ ##
+
+# Evaluate this file near the top of your script to gain access to
+# the functions and variables defined here:
+#
+# . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh
+#
+# If you need to override any of the default environment variable
+# settings, do that before evaluating this file.
+
+
+## -------------------- ##
+## Shell normalisation. ##
+## -------------------- ##
+
+# Some shells need a little help to be as Bourne compatible as possible.
+# Before doing anything else, make sure all that help has been provided!
+
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac
+fi
+
+# NLS nuisances: We save the old values in case they are required later.
+_G_user_locale=
+_G_safe_locale=
+for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+do
+ eval "if test set = \"\${$_G_var+set}\"; then
+ save_$_G_var=\$$_G_var
+ $_G_var=C
+ export $_G_var
+ _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\"
+ _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\"
+ fi"
+done
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Make sure IFS has a sensible default
+sp=' '
+nl='
+'
+IFS="$sp $nl"
+
+# There are apparently some retarded systems that use ';' as a PATH separator!
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+
+## ------------------------- ##
+## Locate command utilities. ##
+## ------------------------- ##
+
+
+# func_executable_p FILE
+# ----------------------
+# Check that FILE is an executable regular file.
+func_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+}
+
+
+# func_path_progs PROGS_LIST CHECK_FUNC [PATH]
+# --------------------------------------------
+# Search for either a program that responds to --version with output
+# containing "GNU", or else returned by CHECK_FUNC otherwise, by
+# trying all the directories in PATH with each of the elements of
+# PROGS_LIST.
+#
+# CHECK_FUNC should accept the path to a candidate program, and
+# set $func_check_prog_result if it truncates its output less than
+# $_G_path_prog_max characters.
+func_path_progs ()
+{
+ _G_progs_list=$1
+ _G_check_func=$2
+ _G_PATH=${3-"$PATH"}
+
+ _G_path_prog_max=0
+ _G_path_prog_found=false
+ _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:}
+ for _G_dir in $_G_PATH; do
+ IFS=$_G_save_IFS
+ test -z "$_G_dir" && _G_dir=.
+ for _G_prog_name in $_G_progs_list; do
+ for _exeext in '' .EXE; do
+ _G_path_prog=$_G_dir/$_G_prog_name$_exeext
+ func_executable_p "$_G_path_prog" || continue
+ case `"$_G_path_prog" --version 2>&1` in
+ *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;;
+ *) $_G_check_func $_G_path_prog
+ func_path_progs_result=$func_check_prog_result
+ ;;
+ esac
+ $_G_path_prog_found && break 3
+ done
+ done
+ done
+ IFS=$_G_save_IFS
+ test -z "$func_path_progs_result" && {
+ echo "no acceptable sed could be found in \$PATH" >&2
+ exit 1
+ }
+}
+
+
+# We want to be able to use the functions in this file before configure
+# has figured out where the best binaries are kept, which means we have
+# to search for them ourselves - except when the results are already set
+# where we skip the searches.
+
+# Unless the user overrides by setting SED, search the path for either GNU
+# sed, or the sed that truncates its output the least.
+test -z "$SED" && {
+ _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+ for _G_i in 1 2 3 4 5 6 7; do
+ _G_sed_script=$_G_sed_script$nl$_G_sed_script
+ done
+ echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed
+ _G_sed_script=
+
+ func_check_prog_sed ()
+ {
+ _G_path_prog=$1
+
+ _G_count=0
+ printf 0123456789 >conftest.in
+ while :
+ do
+ cat conftest.in conftest.in >conftest.tmp
+ mv conftest.tmp conftest.in
+ cp conftest.in conftest.nl
+ echo '' >> conftest.nl
+ "$_G_path_prog" -f conftest.sed <conftest.nl >conftest.out 2>/dev/null || break
+ diff conftest.out conftest.nl >/dev/null 2>&1 || break
+ _G_count=`expr $_G_count + 1`
+ if test "$_G_count" -gt "$_G_path_prog_max"; then
+ # Best one so far, save it but keep looking for a better one
+ func_check_prog_result=$_G_path_prog
+ _G_path_prog_max=$_G_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test 10 -lt "$_G_count" && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out
+ }
+
+ func_path_progs "sed gsed" func_check_prog_sed $PATH:/usr/xpg4/bin
+ rm -f conftest.sed
+ SED=$func_path_progs_result
+}
+
+
+# Unless the user overrides by setting GREP, search the path for either GNU
+# grep, or the grep that truncates its output the least.
+test -z "$GREP" && {
+ func_check_prog_grep ()
+ {
+ _G_path_prog=$1
+
+ _G_count=0
+ _G_path_prog_max=0
+ printf 0123456789 >conftest.in
+ while :
+ do
+ cat conftest.in conftest.in >conftest.tmp
+ mv conftest.tmp conftest.in
+ cp conftest.in conftest.nl
+ echo 'GREP' >> conftest.nl
+ "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' <conftest.nl >conftest.out 2>/dev/null || break
+ diff conftest.out conftest.nl >/dev/null 2>&1 || break
+ _G_count=`expr $_G_count + 1`
+ if test "$_G_count" -gt "$_G_path_prog_max"; then
+ # Best one so far, save it but keep looking for a better one
+ func_check_prog_result=$_G_path_prog
+ _G_path_prog_max=$_G_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test 10 -lt "$_G_count" && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out
+ }
+
+ func_path_progs "grep ggrep" func_check_prog_grep $PATH:/usr/xpg4/bin
+ GREP=$func_path_progs_result
+}
+
+
+## ------------------------------- ##
+## User overridable command paths. ##
+## ------------------------------- ##
+
+# All uppercase variable names are used for environment variables. These
+# variables can be overridden by the user before calling a script that
+# uses them if a suitable command of that name is not already available
+# in the command search PATH.
+
+: ${CP="cp -f"}
+: ${ECHO="printf %s\n"}
+: ${EGREP="$GREP -E"}
+: ${FGREP="$GREP -F"}
+: ${LN_S="ln -s"}
+: ${MAKE="make"}
+: ${MKDIR="mkdir"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+: ${SHELL="${CONFIG_SHELL-/bin/sh}"}
+
+
+## -------------------- ##
+## Useful sed snippets. ##
+## -------------------- ##
+
+sed_dirname='s|/[^/]*$||'
+sed_basename='s|^.*/||'
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s|\([`"$\\]\)|\\\1|g'
+
+# Same as above, but do not quote variable references.
+sed_double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Sed substitution that turns a string into a regex matching for the
+# string literally.
+sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g'
+
+# Sed substitution that converts a w32 file name or path
+# that contains forward slashes, into one that contains
+# (escaped) backslashes. A very naive implementation.
+sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g'
+
+# Re-'\' parameter expansions in output of sed_double_quote_subst that
+# were '\'-ed in input to the same. If an odd number of '\' preceded a
+# '$' in input to sed_double_quote_subst, that '$' was protected from
+# expansion. Since each input '\' is now two '\'s, look for any number
+# of runs of four '\'s followed by two '\'s and then a '$'. '\' that '$'.
+_G_bs='\\'
+_G_bs2='\\\\'
+_G_bs4='\\\\\\\\'
+_G_dollar='\$'
+sed_double_backslash="\
+ s/$_G_bs4/&\\
+/g
+ s/^$_G_bs2$_G_dollar/$_G_bs&/
+ s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g
+ s/\n//g"
+
+
+## ----------------- ##
+## Global variables. ##
+## ----------------- ##
+
+# Except for the global variables explicitly listed below, the following
+# functions in the '^func_' namespace, and the '^require_' namespace
+# variables initialised in the 'Resource management' section, sourcing
+# this file will not pollute your global namespace with anything
+# else. There's no portable way to scope variables in Bourne shell
+# though, so actually running these functions will sometimes place
+# results into a variable named after the function, and often use
+# temporary variables in the '^_G_' namespace. If you are careful to
+# avoid using those namespaces casually in your sourcing script, things
+# should continue to work as you expect. And, of course, you can freely
+# overwrite any of the functions or variables defined here before
+# calling anything to customize them.
+
+EXIT_SUCCESS=0
+EXIT_FAILURE=1
+EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing.
+EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake.
+
+# Allow overriding, eg assuming that you follow the convention of
+# putting '$debug_cmd' at the start of all your functions, you can get
+# bash to show function call trace with:
+#
+# debug_cmd='eval echo "${FUNCNAME[0]} $*" >&2' bash your-script-name
+debug_cmd=${debug_cmd-":"}
+exit_cmd=:
+
+# By convention, finish your script with:
+#
+# exit $exit_status
+#
+# so that you can set exit_status to non-zero if you want to indicate
+# something went wrong during execution without actually bailing out at
+# the point of failure.
+exit_status=$EXIT_SUCCESS
+
+# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh
+# is ksh but when the shell is invoked as "sh" and the current value of
+# the _XPG environment variable is not equal to 1 (one), the special
+# positional parameter $0, within a function call, is the name of the
+# function.
+progpath=$0
+
+# The name of this program.
+progname=`$ECHO "$progpath" |$SED "$sed_basename"`
+
+# Make sure we have an absolute progpath for reexecution:
+case $progpath in
+ [\\/]*|[A-Za-z]:\\*) ;;
+ *[\\/]*)
+ progdir=`$ECHO "$progpath" |$SED "$sed_dirname"`
+ progdir=`cd "$progdir" && pwd`
+ progpath=$progdir/$progname
+ ;;
+ *)
+ _G_IFS=$IFS
+ IFS=${PATH_SEPARATOR-:}
+ for progdir in $PATH; do
+ IFS=$_G_IFS
+ test -x "$progdir/$progname" && break
+ done
+ IFS=$_G_IFS
+ test -n "$progdir" || progdir=`pwd`
+ progpath=$progdir/$progname
+ ;;
+esac
+
+
+## ----------------- ##
+## Standard options. ##
+## ----------------- ##
+
+# The following options affect the operation of the functions defined
+# below, and should be set appropriately depending on run-time para-
+# meters passed on the command line.
+
+opt_dry_run=false
+opt_quiet=false
+opt_verbose=false
+
+# Categories 'all' and 'none' are always available. Append any others
+# you will pass as the first argument to func_warning from your own
+# code.
+warning_categories=
+
+# By default, display warnings according to 'opt_warning_types'. Set
+# 'warning_func' to ':' to elide all warnings, or func_fatal_error to
+# treat the next displayed warning as a fatal error.
+warning_func=func_warn_and_continue
+
+# Set to 'all' to display all warnings, 'none' to suppress all
+# warnings, or a space delimited list of some subset of
+# 'warning_categories' to display only the listed warnings.
+opt_warning_types=all
+
+
+## -------------------- ##
+## Resource management. ##
+## -------------------- ##
+
+# This section contains definitions for functions that each ensure a
+# particular resource (a file, or a non-empty configuration variable for
+# example) is available, and if appropriate to extract default values
+# from pertinent package files. Call them using their associated
+# 'require_*' variable to ensure that they are executed, at most, once.
+#
+# It's entirely deliberate that calling these functions can set
+# variables that don't obey the namespace limitations obeyed by the rest
+# of this file, in order that that they be as useful as possible to
+# callers.
+
+
+# require_term_colors
+# -------------------
+# Allow display of bold text on terminals that support it.
+require_term_colors=func_require_term_colors
+func_require_term_colors ()
+{
+ $debug_cmd
+
+ test -t 1 && {
+ # COLORTERM and USE_ANSI_COLORS environment variables take
+ # precedence, because most terminfo databases neglect to describe
+ # whether color sequences are supported.
+ test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"}
+
+ if test 1 = "$USE_ANSI_COLORS"; then
+ # Standard ANSI escape sequences
+ tc_reset=''
+ tc_bold=''; tc_standout=''
+ tc_red=''; tc_green=''
+ tc_blue=''; tc_cyan=''
+ else
+ # Otherwise trust the terminfo database after all.
+ test -n "`tput sgr0 2>/dev/null`" && {
+ tc_reset=`tput sgr0`
+ test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold`
+ tc_standout=$tc_bold
+ test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso`
+ test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1`
+ test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2`
+ test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4`
+ test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5`
+ }
+ fi
+ }
+
+ require_term_colors=:
+}
+
+
+## ----------------- ##
+## Function library. ##
+## ----------------- ##
+
+# This section contains a variety of useful functions to call in your
+# scripts. Take note of the portable wrappers for features provided by
+# some modern shells, which will fall back to slower equivalents on
+# less featureful shells.
+
+
+# func_append VAR VALUE
+# ---------------------
+# Append VALUE onto the existing contents of VAR.
+
+ # We should try to minimise forks, especially on Windows where they are
+ # unreasonably slow, so skip the feature probes when bash or zsh are
+ # being used:
+ if test set = "${BASH_VERSION+set}${ZSH_VERSION+set}"; then
+ : ${_G_HAVE_ARITH_OP="yes"}
+ : ${_G_HAVE_XSI_OPS="yes"}
+ # The += operator was introduced in bash 3.1
+ case $BASH_VERSION in
+ [12].* | 3.0 | 3.0*) ;;
+ *)
+ : ${_G_HAVE_PLUSEQ_OP="yes"}
+ ;;
+ esac
+ fi
+
+ # _G_HAVE_PLUSEQ_OP
+ # Can be empty, in which case the shell is probed, "yes" if += is
+ # useable or anything else if it does not work.
+ test -z "$_G_HAVE_PLUSEQ_OP" \
+ && (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \
+ && _G_HAVE_PLUSEQ_OP=yes
+
+if test yes = "$_G_HAVE_PLUSEQ_OP"
+then
+ # This is an XSI compatible shell, allowing a faster implementation...
+ eval 'func_append ()
+ {
+ $debug_cmd
+
+ eval "$1+=\$2"
+ }'
+else
+ # ...otherwise fall back to using expr, which is often a shell builtin.
+ func_append ()
+ {
+ $debug_cmd
+
+ eval "$1=\$$1\$2"
+ }
+fi
+
+
+# func_append_quoted VAR VALUE
+# ----------------------------
+# Quote VALUE and append to the end of shell variable VAR, separated
+# by a space.
+if test yes = "$_G_HAVE_PLUSEQ_OP"; then
+ eval 'func_append_quoted ()
+ {
+ $debug_cmd
+
+ func_quote_for_eval "$2"
+ eval "$1+=\\ \$func_quote_for_eval_result"
+ }'
+else
+ func_append_quoted ()
+ {
+ $debug_cmd
+
+ func_quote_for_eval "$2"
+ eval "$1=\$$1\\ \$func_quote_for_eval_result"
+ }
+fi
+
+
+# func_append_uniq VAR VALUE
+# --------------------------
+# Append unique VALUE onto the existing contents of VAR, assuming
+# entries are delimited by the first character of VALUE. For example:
+#
+# func_append_uniq options " --another-option option-argument"
+#
+# will only append to $options if " --another-option option-argument "
+# is not already present somewhere in $options already (note spaces at
+# each end implied by leading space in second argument).
+func_append_uniq ()
+{
+ $debug_cmd
+
+ eval _G_current_value='`$ECHO $'$1'`'
+ _G_delim=`expr "$2" : '\(.\)'`
+
+ case $_G_delim$_G_current_value$_G_delim in
+ *"$2$_G_delim"*) ;;
+ *) func_append "$@" ;;
+ esac
+}
+
+
+# func_arith TERM...
+# ------------------
+# Set func_arith_result to the result of evaluating TERMs.
+ test -z "$_G_HAVE_ARITH_OP" \
+ && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \
+ && _G_HAVE_ARITH_OP=yes
+
+if test yes = "$_G_HAVE_ARITH_OP"; then
+ eval 'func_arith ()
+ {
+ $debug_cmd
+
+ func_arith_result=$(( $* ))
+ }'
+else
+ func_arith ()
+ {
+ $debug_cmd
+
+ func_arith_result=`expr "$@"`
+ }
+fi
+
+
+# func_basename FILE
+# ------------------
+# Set func_basename_result to FILE with everything up to and including
+# the last / stripped.
+if test yes = "$_G_HAVE_XSI_OPS"; then
+ # If this shell supports suffix pattern removal, then use it to avoid
+ # forking. Hide the definitions single quotes in case the shell chokes
+ # on unsupported syntax...
+ _b='func_basename_result=${1##*/}'
+ _d='case $1 in
+ */*) func_dirname_result=${1%/*}$2 ;;
+ * ) func_dirname_result=$3 ;;
+ esac'
+
+else
+ # ...otherwise fall back to using sed.
+ _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`'
+ _d='func_dirname_result=`$ECHO "$1" |$SED "$sed_dirname"`
+ if test "X$func_dirname_result" = "X$1"; then
+ func_dirname_result=$3
+ else
+ func_append func_dirname_result "$2"
+ fi'
+fi
+
+eval 'func_basename ()
+{
+ $debug_cmd
+
+ '"$_b"'
+}'
+
+
+# func_dirname FILE APPEND NONDIR_REPLACEMENT
+# -------------------------------------------
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+eval 'func_dirname ()
+{
+ $debug_cmd
+
+ '"$_d"'
+}'
+
+
+# func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT
+# --------------------------------------------------------
+# Perform func_basename and func_dirname in a single function
+# call:
+# dirname: Compute the dirname of FILE. If nonempty,
+# add APPEND to the result, otherwise set result
+# to NONDIR_REPLACEMENT.
+# value returned in "$func_dirname_result"
+# basename: Compute filename of FILE.
+# value retuned in "$func_basename_result"
+# For efficiency, we do not delegate to the functions above but instead
+# duplicate the functionality here.
+eval 'func_dirname_and_basename ()
+{
+ $debug_cmd
+
+ '"$_b"'
+ '"$_d"'
+}'
+
+
+# func_echo ARG...
+# ----------------
+# Echo program name prefixed message.
+func_echo ()
+{
+ $debug_cmd
+
+ _G_message=$*
+
+ func_echo_IFS=$IFS
+ IFS=$nl
+ for _G_line in $_G_message; do
+ IFS=$func_echo_IFS
+ $ECHO "$progname: $_G_line"
+ done
+ IFS=$func_echo_IFS
+}
+
+
+# func_echo_all ARG...
+# --------------------
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+ $ECHO "$*"
+}
+
+
+# func_echo_infix_1 INFIX ARG...
+# ------------------------------
+# Echo program name, followed by INFIX on the first line, with any
+# additional lines not showing INFIX.
+func_echo_infix_1 ()
+{
+ $debug_cmd
+
+ $require_term_colors
+
+ _G_infix=$1; shift
+ _G_indent=$_G_infix
+ _G_prefix="$progname: $_G_infix: "
+ _G_message=$*
+
+ # Strip color escape sequences before counting printable length
+ for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan"
+ do
+ test -n "$_G_tc" && {
+ _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"`
+ _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"`
+ }
+ done
+ _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`" " ## exclude from sc_prohibit_nested_quotes
+
+ func_echo_infix_1_IFS=$IFS
+ IFS=$nl
+ for _G_line in $_G_message; do
+ IFS=$func_echo_infix_1_IFS
+ $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2
+ _G_prefix=$_G_indent
+ done
+ IFS=$func_echo_infix_1_IFS
+}
+
+
+# func_error ARG...
+# -----------------
+# Echo program name prefixed message to standard error.
+func_error ()
+{
+ $debug_cmd
+
+ $require_term_colors
+
+ func_echo_infix_1 " $tc_standout${tc_red}error$tc_reset" "$*" >&2
+}
+
+
+# func_fatal_error ARG...
+# -----------------------
+# Echo program name prefixed message to standard error, and exit.
+func_fatal_error ()
+{
+ $debug_cmd
+
+ func_error "$*"
+ exit $EXIT_FAILURE
+}
+
+
+# func_grep EXPRESSION FILENAME
+# -----------------------------
+# Check whether EXPRESSION matches any line of FILENAME, without output.
+func_grep ()
+{
+ $debug_cmd
+
+ $GREP "$1" "$2" >/dev/null 2>&1
+}
+
+
+# func_len STRING
+# ---------------
+# Set func_len_result to the length of STRING. STRING may not
+# start with a hyphen.
+ test -z "$_G_HAVE_XSI_OPS" \
+ && (eval 'x=a/b/c;
+ test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \
+ && _G_HAVE_XSI_OPS=yes
+
+if test yes = "$_G_HAVE_XSI_OPS"; then
+ eval 'func_len ()
+ {
+ $debug_cmd
+
+ func_len_result=${#1}
+ }'
+else
+ func_len ()
+ {
+ $debug_cmd
+
+ func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len`
+ }
+fi
+
+
+# func_mkdir_p DIRECTORY-PATH
+# ---------------------------
+# Make sure the entire path to DIRECTORY-PATH is available.
+func_mkdir_p ()
+{
+ $debug_cmd
+
+ _G_directory_path=$1
+ _G_dir_list=
+
+ if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then
+
+ # Protect directory names starting with '-'
+ case $_G_directory_path in
+ -*) _G_directory_path=./$_G_directory_path ;;
+ esac
+
+ # While some portion of DIR does not yet exist...
+ while test ! -d "$_G_directory_path"; do
+ # ...make a list in topmost first order. Use a colon delimited
+ # list incase some portion of path contains whitespace.
+ _G_dir_list=$_G_directory_path:$_G_dir_list
+
+ # If the last portion added has no slash in it, the list is done
+ case $_G_directory_path in */*) ;; *) break ;; esac
+
+ # ...otherwise throw away the child directory and loop
+ _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"`
+ done
+ _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'`
+
+ func_mkdir_p_IFS=$IFS; IFS=:
+ for _G_dir in $_G_dir_list; do
+ IFS=$func_mkdir_p_IFS
+ # mkdir can fail with a 'File exist' error if two processes
+ # try to create one of the directories concurrently. Don't
+ # stop in that case!
+ $MKDIR "$_G_dir" 2>/dev/null || :
+ done
+ IFS=$func_mkdir_p_IFS
+
+ # Bail out if we (or some other process) failed to create a directory.
+ test -d "$_G_directory_path" || \
+ func_fatal_error "Failed to create '$1'"
+ fi
+}
+
+
+# func_mktempdir [BASENAME]
+# -------------------------
+# Make a temporary directory that won't clash with other running
+# libtool processes, and avoids race conditions if possible. If
+# given, BASENAME is the basename for that directory.
+func_mktempdir ()
+{
+ $debug_cmd
+
+ _G_template=${TMPDIR-/tmp}/${1-$progname}
+
+ if test : = "$opt_dry_run"; then
+ # Return a directory name, but don't create it in dry-run mode
+ _G_tmpdir=$_G_template-$$
+ else
+
+ # If mktemp works, use that first and foremost
+ _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null`
+
+ if test ! -d "$_G_tmpdir"; then
+ # Failing that, at least try and use $RANDOM to avoid a race
+ _G_tmpdir=$_G_template-${RANDOM-0}$$
+
+ func_mktempdir_umask=`umask`
+ umask 0077
+ $MKDIR "$_G_tmpdir"
+ umask $func_mktempdir_umask
+ fi
+
+ # If we're not in dry-run mode, bomb out on failure
+ test -d "$_G_tmpdir" || \
+ func_fatal_error "cannot create temporary directory '$_G_tmpdir'"
+ fi
+
+ $ECHO "$_G_tmpdir"
+}
+
+
+# func_normal_abspath PATH
+# ------------------------
+# Remove doubled-up and trailing slashes, "." path components,
+# and cancel out any ".." path components in PATH after making
+# it an absolute path.
+func_normal_abspath ()
+{
+ $debug_cmd
+
+ # These SED scripts presuppose an absolute path with a trailing slash.
+ _G_pathcar='s|^/\([^/]*\).*$|\1|'
+ _G_pathcdr='s|^/[^/]*||'
+ _G_removedotparts=':dotsl
+ s|/\./|/|g
+ t dotsl
+ s|/\.$|/|'
+ _G_collapseslashes='s|/\{1,\}|/|g'
+ _G_finalslash='s|/*$|/|'
+
+ # Start from root dir and reassemble the path.
+ func_normal_abspath_result=
+ func_normal_abspath_tpath=$1
+ func_normal_abspath_altnamespace=
+ case $func_normal_abspath_tpath in
+ "")
+ # Empty path, that just means $cwd.
+ func_stripname '' '/' "`pwd`"
+ func_normal_abspath_result=$func_stripname_result
+ return
+ ;;
+ # The next three entries are used to spot a run of precisely
+ # two leading slashes without using negated character classes;
+ # we take advantage of case's first-match behaviour.
+ ///*)
+ # Unusual form of absolute path, do nothing.
+ ;;
+ //*)
+ # Not necessarily an ordinary path; POSIX reserves leading '//'
+ # and for example Cygwin uses it to access remote file shares
+ # over CIFS/SMB, so we conserve a leading double slash if found.
+ func_normal_abspath_altnamespace=/
+ ;;
+ /*)
+ # Absolute path, do nothing.
+ ;;
+ *)
+ # Relative path, prepend $cwd.
+ func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath
+ ;;
+ esac
+
+ # Cancel out all the simple stuff to save iterations. We also want
+ # the path to end with a slash for ease of parsing, so make sure
+ # there is one (and only one) here.
+ func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
+ -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"`
+ while :; do
+ # Processed it all yet?
+ if test / = "$func_normal_abspath_tpath"; then
+ # If we ascended to the root using ".." the result may be empty now.
+ if test -z "$func_normal_abspath_result"; then
+ func_normal_abspath_result=/
+ fi
+ break
+ fi
+ func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \
+ -e "$_G_pathcar"`
+ func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
+ -e "$_G_pathcdr"`
+ # Figure out what to do with it
+ case $func_normal_abspath_tcomponent in
+ "")
+ # Trailing empty path component, ignore it.
+ ;;
+ ..)
+ # Parent dir; strip last assembled component from result.
+ func_dirname "$func_normal_abspath_result"
+ func_normal_abspath_result=$func_dirname_result
+ ;;
+ *)
+ # Actual path component, append it.
+ func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent"
+ ;;
+ esac
+ done
+ # Restore leading double-slash if one was found on entry.
+ func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result
+}
+
+
+# func_notquiet ARG...
+# --------------------
+# Echo program name prefixed message only when not in quiet mode.
+func_notquiet ()
+{
+ $debug_cmd
+
+ $opt_quiet || func_echo ${1+"$@"}
+
+ # A bug in bash halts the script if the last line of a function
+ # fails when set -e is in force, so we need another command to
+ # work around that:
+ :
+}
+
+
+# func_relative_path SRCDIR DSTDIR
+# --------------------------------
+# Set func_relative_path_result to the relative path from SRCDIR to DSTDIR.
+func_relative_path ()
+{
+ $debug_cmd
+
+ func_relative_path_result=
+ func_normal_abspath "$1"
+ func_relative_path_tlibdir=$func_normal_abspath_result
+ func_normal_abspath "$2"
+ func_relative_path_tbindir=$func_normal_abspath_result
+
+ # Ascend the tree starting from libdir
+ while :; do
+ # check if we have found a prefix of bindir
+ case $func_relative_path_tbindir in
+ $func_relative_path_tlibdir)
+ # found an exact match
+ func_relative_path_tcancelled=
+ break
+ ;;
+ $func_relative_path_tlibdir*)
+ # found a matching prefix
+ func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir"
+ func_relative_path_tcancelled=$func_stripname_result
+ if test -z "$func_relative_path_result"; then
+ func_relative_path_result=.
+ fi
+ break
+ ;;
+ *)
+ func_dirname $func_relative_path_tlibdir
+ func_relative_path_tlibdir=$func_dirname_result
+ if test -z "$func_relative_path_tlibdir"; then
+ # Have to descend all the way to the root!
+ func_relative_path_result=../$func_relative_path_result
+ func_relative_path_tcancelled=$func_relative_path_tbindir
+ break
+ fi
+ func_relative_path_result=../$func_relative_path_result
+ ;;
+ esac
+ done
+
+ # Now calculate path; take care to avoid doubling-up slashes.
+ func_stripname '' '/' "$func_relative_path_result"
+ func_relative_path_result=$func_stripname_result
+ func_stripname '/' '/' "$func_relative_path_tcancelled"
+ if test -n "$func_stripname_result"; then
+ func_append func_relative_path_result "/$func_stripname_result"
+ fi
+
+ # Normalisation. If bindir is libdir, return '.' else relative path.
+ if test -n "$func_relative_path_result"; then
+ func_stripname './' '' "$func_relative_path_result"
+ func_relative_path_result=$func_stripname_result
+ fi
+
+ test -n "$func_relative_path_result" || func_relative_path_result=.
+
+ :
+}
+
+
+# func_quote_for_eval ARG...
+# --------------------------
+# Aesthetically quote ARGs to be evaled later.
+# This function returns two values:
+# i) func_quote_for_eval_result
+# double-quoted, suitable for a subsequent eval
+# ii) func_quote_for_eval_unquoted_result
+# has all characters that are still active within double
+# quotes backslashified.
+func_quote_for_eval ()
+{
+ $debug_cmd
+
+ func_quote_for_eval_unquoted_result=
+ func_quote_for_eval_result=
+ while test 0 -lt $#; do
+ case $1 in
+ *[\\\`\"\$]*)
+ _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;;
+ *)
+ _G_unquoted_arg=$1 ;;
+ esac
+ if test -n "$func_quote_for_eval_unquoted_result"; then
+ func_append func_quote_for_eval_unquoted_result " $_G_unquoted_arg"
+ else
+ func_append func_quote_for_eval_unquoted_result "$_G_unquoted_arg"
+ fi
+
+ case $_G_unquoted_arg in
+ # Double-quote args containing shell metacharacters to delay
+ # word splitting, command substitution and variable expansion
+ # for a subsequent eval.
+ # Many Bourne shells cannot handle close brackets correctly
+ # in scan sets, so we specify it separately.
+ *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"")
+ _G_quoted_arg=\"$_G_unquoted_arg\"
+ ;;
+ *)
+ _G_quoted_arg=$_G_unquoted_arg
+ ;;
+ esac
+
+ if test -n "$func_quote_for_eval_result"; then
+ func_append func_quote_for_eval_result " $_G_quoted_arg"
+ else
+ func_append func_quote_for_eval_result "$_G_quoted_arg"
+ fi
+ shift
+ done
+}
+
+
+# func_quote_for_expand ARG
+# -------------------------
+# Aesthetically quote ARG to be evaled later; same as above,
+# but do not quote variable references.
+func_quote_for_expand ()
+{
+ $debug_cmd
+
+ case $1 in
+ *[\\\`\"]*)
+ _G_arg=`$ECHO "$1" | $SED \
+ -e "$sed_double_quote_subst" -e "$sed_double_backslash"` ;;
+ *)
+ _G_arg=$1 ;;
+ esac
+
+ case $_G_arg in
+ # Double-quote args containing shell metacharacters to delay
+ # word splitting and command substitution for a subsequent eval.
+ # Many Bourne shells cannot handle close brackets correctly
+ # in scan sets, so we specify it separately.
+ *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"")
+ _G_arg=\"$_G_arg\"
+ ;;
+ esac
+
+ func_quote_for_expand_result=$_G_arg
+}
+
+
+# func_stripname PREFIX SUFFIX NAME
+# ---------------------------------
+# strip PREFIX and SUFFIX from NAME, and store in func_stripname_result.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+if test yes = "$_G_HAVE_XSI_OPS"; then
+ eval 'func_stripname ()
+ {
+ $debug_cmd
+
+ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
+ # positional parameters, so assign one to ordinary variable first.
+ func_stripname_result=$3
+ func_stripname_result=${func_stripname_result#"$1"}
+ func_stripname_result=${func_stripname_result%"$2"}
+ }'
+else
+ func_stripname ()
+ {
+ $debug_cmd
+
+ case $2 in
+ .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;;
+ *) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;;
+ esac
+ }
+fi
+
+
+# func_show_eval CMD [FAIL_EXP]
+# -----------------------------
+# Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is
+# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it.
+func_show_eval ()
+{
+ $debug_cmd
+
+ _G_cmd=$1
+ _G_fail_exp=${2-':'}
+
+ func_quote_for_expand "$_G_cmd"
+ eval "func_notquiet $func_quote_for_expand_result"
+
+ $opt_dry_run || {
+ eval "$_G_cmd"
+ _G_status=$?
+ if test 0 -ne "$_G_status"; then
+ eval "(exit $_G_status); $_G_fail_exp"
+ fi
+ }
+}
+
+
+# func_show_eval_locale CMD [FAIL_EXP]
+# ------------------------------------
+# Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is
+# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it. Use the saved locale for evaluation.
+func_show_eval_locale ()
+{
+ $debug_cmd
+
+ _G_cmd=$1
+ _G_fail_exp=${2-':'}
+
+ $opt_quiet || {
+ func_quote_for_expand "$_G_cmd"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+
+ $opt_dry_run || {
+ eval "$_G_user_locale
+ $_G_cmd"
+ _G_status=$?
+ eval "$_G_safe_locale"
+ if test 0 -ne "$_G_status"; then
+ eval "(exit $_G_status); $_G_fail_exp"
+ fi
+ }
+}
+
+
+# func_tr_sh
+# ----------
+# Turn $1 into a string suitable for a shell variable name.
+# Result is stored in $func_tr_sh_result. All characters
+# not in the set a-zA-Z0-9_ are replaced with '_'. Further,
+# if $1 begins with a digit, a '_' is prepended as well.
+func_tr_sh ()
+{
+ $debug_cmd
+
+ case $1 in
+ [0-9]* | *[!a-zA-Z0-9_]*)
+ func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'`
+ ;;
+ * )
+ func_tr_sh_result=$1
+ ;;
+ esac
+}
+
+
+# func_verbose ARG...
+# -------------------
+# Echo program name prefixed message in verbose mode only.
+func_verbose ()
+{
+ $debug_cmd
+
+ $opt_verbose && func_echo "$*"
+
+ :
+}
+
+
+# func_warn_and_continue ARG...
+# -----------------------------
+# Echo program name prefixed warning message to standard error.
+func_warn_and_continue ()
+{
+ $debug_cmd
+
+ $require_term_colors
+
+ func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2
+}
+
+
+# func_warning CATEGORY ARG...
+# ----------------------------
+# Echo program name prefixed warning message to standard error. Warning
+# messages can be filtered according to CATEGORY, where this function
+# elides messages where CATEGORY is not listed in the global variable
+# 'opt_warning_types'.
+func_warning ()
+{
+ $debug_cmd
+
+ # CATEGORY must be in the warning_categories list!
+ case " $warning_categories " in
+ *" $1 "*) ;;
+ *) func_internal_error "invalid warning category '$1'" ;;
+ esac
+
+ _G_category=$1
+ shift
+
+ case " $opt_warning_types " in
+ *" $_G_category "*) $warning_func ${1+"$@"} ;;
+ esac
+}
+
+
+# func_sort_ver VER1 VER2
+# -----------------------
+# 'sort -V' is not generally available.
+# Note this deviates from the version comparison in automake
+# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a
+# but this should suffice as we won't be specifying old
+# version formats or redundant trailing .0 in bootstrap.conf.
+# If we did want full compatibility then we should probably
+# use m4_version_compare from autoconf.
+func_sort_ver ()
+{
+ $debug_cmd
+
+ printf '%s\n%s\n' "$1" "$2" \
+ | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n
+}
+
+# func_lt_ver PREV CURR
+# ---------------------
+# Return true if PREV and CURR are in the correct order according to
+# func_sort_ver, otherwise false. Use it like this:
+#
+# func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..."
+func_lt_ver ()
+{
+ $debug_cmd
+
+ test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q`
+}
+
+
+# Local variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC"
+# time-stamp-time-zone: "UTC"
+# End:
+#! /bin/sh
+
+# Set a version string for this script.
+scriptversion=2014-01-07.03; # UTC
+
+# A portable, pluggable option parser for Bourne shell.
+# Written by Gary V. Vaughan, 2010
+
+# Copyright (C) 2010-2015 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Please report bugs or propose patches to gary@gnu.org.
+
+
+## ------ ##
+## Usage. ##
+## ------ ##
+
+# This file is a library for parsing options in your shell scripts along
+# with assorted other useful supporting features that you can make use
+# of too.
+#
+# For the simplest scripts you might need only:
+#
+# #!/bin/sh
+# . relative/path/to/funclib.sh
+# . relative/path/to/options-parser
+# scriptversion=1.0
+# func_options ${1+"$@"}
+# eval set dummy "$func_options_result"; shift
+# ...rest of your script...
+#
+# In order for the '--version' option to work, you will need to have a
+# suitably formatted comment like the one at the top of this file
+# starting with '# Written by ' and ending with '# warranty; '.
+#
+# For '-h' and '--help' to work, you will also need a one line
+# description of your script's purpose in a comment directly above the
+# '# Written by ' line, like the one at the top of this file.
+#
+# The default options also support '--debug', which will turn on shell
+# execution tracing (see the comment above debug_cmd below for another
+# use), and '--verbose' and the func_verbose function to allow your script
+# to display verbose messages only when your user has specified
+# '--verbose'.
+#
+# After sourcing this file, you can plug processing for additional
+# options by amending the variables from the 'Configuration' section
+# below, and following the instructions in the 'Option parsing'
+# section further down.
+
+## -------------- ##
+## Configuration. ##
+## -------------- ##
+
+# You should override these variables in your script after sourcing this
+# file so that they reflect the customisations you have added to the
+# option parser.
+
+# The usage line for option parsing errors and the start of '-h' and
+# '--help' output messages. You can embed shell variables for delayed
+# expansion at the time the message is displayed, but you will need to
+# quote other shell meta-characters carefully to prevent them being
+# expanded when the contents are evaled.
+usage='$progpath [OPTION]...'
+
+# Short help message in response to '-h' and '--help'. Add to this or
+# override it after sourcing this library to reflect the full set of
+# options your script accepts.
+usage_message="\
+ --debug enable verbose shell tracing
+ -W, --warnings=CATEGORY
+ report the warnings falling in CATEGORY [all]
+ -v, --verbose verbosely report processing
+ --version print version information and exit
+ -h, --help print short or long help message and exit
+"
+
+# Additional text appended to 'usage_message' in response to '--help'.
+long_help_message="
+Warning categories include:
+ 'all' show all warnings
+ 'none' turn off all the warnings
+ 'error' warnings are treated as fatal errors"
+
+# Help message printed before fatal option parsing errors.
+fatal_help="Try '\$progname --help' for more information."
+
+
+
+## ------------------------- ##
+## Hook function management. ##
+## ------------------------- ##
+
+# This section contains functions for adding, removing, and running hooks
+# to the main code. A hook is just a named list of of function, that can
+# be run in order later on.
+
+# func_hookable FUNC_NAME
+# -----------------------
+# Declare that FUNC_NAME will run hooks added with
+# 'func_add_hook FUNC_NAME ...'.
+func_hookable ()
+{
+ $debug_cmd
+
+ func_append hookable_fns " $1"
+}
+
+
+# func_add_hook FUNC_NAME HOOK_FUNC
+# ---------------------------------
+# Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must
+# first have been declared "hookable" by a call to 'func_hookable'.
+func_add_hook ()
+{
+ $debug_cmd
+
+ case " $hookable_fns " in
+ *" $1 "*) ;;
+ *) func_fatal_error "'$1' does not accept hook functions." ;;
+ esac
+
+ eval func_append ${1}_hooks '" $2"'
+}
+
+
+# func_remove_hook FUNC_NAME HOOK_FUNC
+# ------------------------------------
+# Remove HOOK_FUNC from the list of functions called by FUNC_NAME.
+func_remove_hook ()
+{
+ $debug_cmd
+
+ eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`'
+}
+
+
+# func_run_hooks FUNC_NAME [ARG]...
+# ---------------------------------
+# Run all hook functions registered to FUNC_NAME.
+# It is assumed that the list of hook functions contains nothing more
+# than a whitespace-delimited list of legal shell function names, and
+# no effort is wasted trying to catch shell meta-characters or preserve
+# whitespace.
+func_run_hooks ()
+{
+ $debug_cmd
+
+ case " $hookable_fns " in
+ *" $1 "*) ;;
+ *) func_fatal_error "'$1' does not support hook funcions.n" ;;
+ esac
+
+ eval _G_hook_fns=\$$1_hooks; shift
+
+ for _G_hook in $_G_hook_fns; do
+ eval $_G_hook '"$@"'
+
+ # store returned options list back into positional
+ # parameters for next 'cmd' execution.
+ eval _G_hook_result=\$${_G_hook}_result
+ eval set dummy "$_G_hook_result"; shift
+ done
+
+ func_quote_for_eval ${1+"$@"}
+ func_run_hooks_result=$func_quote_for_eval_result
+}
+
+
+
+## --------------- ##
+## Option parsing. ##
+## --------------- ##
+
+# In order to add your own option parsing hooks, you must accept the
+# full positional parameter list in your hook function, remove any
+# options that you action, and then pass back the remaining unprocessed
+# options in '<hooked_function_name>_result', escaped suitably for
+# 'eval'. Like this:
+#
+# my_options_prep ()
+# {
+# $debug_cmd
+#
+# # Extend the existing usage message.
+# usage_message=$usage_message'
+# -s, --silent don'\''t print informational messages
+# '
+#
+# func_quote_for_eval ${1+"$@"}
+# my_options_prep_result=$func_quote_for_eval_result
+# }
+# func_add_hook func_options_prep my_options_prep
+#
+#
+# my_silent_option ()
+# {
+# $debug_cmd
+#
+# # Note that for efficiency, we parse as many options as we can
+# # recognise in a loop before passing the remainder back to the
+# # caller on the first unrecognised argument we encounter.
+# while test $# -gt 0; do
+# opt=$1; shift
+# case $opt in
+# --silent|-s) opt_silent=: ;;
+# # Separate non-argument short options:
+# -s*) func_split_short_opt "$_G_opt"
+# set dummy "$func_split_short_opt_name" \
+# "-$func_split_short_opt_arg" ${1+"$@"}
+# shift
+# ;;
+# *) set dummy "$_G_opt" "$*"; shift; break ;;
+# esac
+# done
+#
+# func_quote_for_eval ${1+"$@"}
+# my_silent_option_result=$func_quote_for_eval_result
+# }
+# func_add_hook func_parse_options my_silent_option
+#
+#
+# my_option_validation ()
+# {
+# $debug_cmd
+#
+# $opt_silent && $opt_verbose && func_fatal_help "\
+# '--silent' and '--verbose' options are mutually exclusive."
+#
+# func_quote_for_eval ${1+"$@"}
+# my_option_validation_result=$func_quote_for_eval_result
+# }
+# func_add_hook func_validate_options my_option_validation
+#
+# You'll alse need to manually amend $usage_message to reflect the extra
+# options you parse. It's preferable to append if you can, so that
+# multiple option parsing hooks can be added safely.
+
+
+# func_options [ARG]...
+# ---------------------
+# All the functions called inside func_options are hookable. See the
+# individual implementations for details.
+func_hookable func_options
+func_options ()
+{
+ $debug_cmd
+
+ func_options_prep ${1+"$@"}
+ eval func_parse_options \
+ ${func_options_prep_result+"$func_options_prep_result"}
+ eval func_validate_options \
+ ${func_parse_options_result+"$func_parse_options_result"}
+
+ eval func_run_hooks func_options \
+ ${func_validate_options_result+"$func_validate_options_result"}
+
+ # save modified positional parameters for caller
+ func_options_result=$func_run_hooks_result
+}
+
+
+# func_options_prep [ARG]...
+# --------------------------
+# All initialisations required before starting the option parse loop.
+# Note that when calling hook functions, we pass through the list of
+# positional parameters. If a hook function modifies that list, and
+# needs to propogate that back to rest of this script, then the complete
+# modified list must be put in 'func_run_hooks_result' before
+# returning.
+func_hookable func_options_prep
+func_options_prep ()
+{
+ $debug_cmd
+
+ # Option defaults:
+ opt_verbose=false
+ opt_warning_types=
+
+ func_run_hooks func_options_prep ${1+"$@"}
+
+ # save modified positional parameters for caller
+ func_options_prep_result=$func_run_hooks_result
+}
+
+
+# func_parse_options [ARG]...
+# ---------------------------
+# The main option parsing loop.
+func_hookable func_parse_options
+func_parse_options ()
+{
+ $debug_cmd
+
+ func_parse_options_result=
+
+ # this just eases exit handling
+ while test $# -gt 0; do
+ # Defer to hook functions for initial option parsing, so they
+ # get priority in the event of reusing an option name.
+ func_run_hooks func_parse_options ${1+"$@"}
+
+ # Adjust func_parse_options positional parameters to match
+ eval set dummy "$func_run_hooks_result"; shift
+
+ # Break out of the loop if we already parsed every option.
+ test $# -gt 0 || break
+
+ _G_opt=$1
+ shift
+ case $_G_opt in
+ --debug|-x) debug_cmd='set -x'
+ func_echo "enabling shell trace mode"
+ $debug_cmd
+ ;;
+
+ --no-warnings|--no-warning|--no-warn)
+ set dummy --warnings none ${1+"$@"}
+ shift
+ ;;
+
+ --warnings|--warning|-W)
+ test $# = 0 && func_missing_arg $_G_opt && break
+ case " $warning_categories $1" in
+ *" $1 "*)
+ # trailing space prevents matching last $1 above
+ func_append_uniq opt_warning_types " $1"
+ ;;
+ *all)
+ opt_warning_types=$warning_categories
+ ;;
+ *none)
+ opt_warning_types=none
+ warning_func=:
+ ;;
+ *error)
+ opt_warning_types=$warning_categories
+ warning_func=func_fatal_error
+ ;;
+ *)
+ func_fatal_error \
+ "unsupported warning category: '$1'"
+ ;;
+ esac
+ shift
+ ;;
+
+ --verbose|-v) opt_verbose=: ;;
+ --version) func_version ;;
+ -\?|-h) func_usage ;;
+ --help) func_help ;;
+
+ # Separate optargs to long options (plugins may need this):
+ --*=*) func_split_equals "$_G_opt"
+ set dummy "$func_split_equals_lhs" \
+ "$func_split_equals_rhs" ${1+"$@"}
+ shift
+ ;;
+
+ # Separate optargs to short options:
+ -W*)
+ func_split_short_opt "$_G_opt"
+ set dummy "$func_split_short_opt_name" \
+ "$func_split_short_opt_arg" ${1+"$@"}
+ shift
+ ;;
+
+ # Separate non-argument short options:
+ -\?*|-h*|-v*|-x*)
+ func_split_short_opt "$_G_opt"
+ set dummy "$func_split_short_opt_name" \
+ "-$func_split_short_opt_arg" ${1+"$@"}
+ shift
+ ;;
+
+ --) break ;;
+ -*) func_fatal_help "unrecognised option: '$_G_opt'" ;;
+ *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;;
+ esac
+ done
+
+ # save modified positional parameters for caller
+ func_quote_for_eval ${1+"$@"}
+ func_parse_options_result=$func_quote_for_eval_result
+}
+
+
+# func_validate_options [ARG]...
+# ------------------------------
+# Perform any sanity checks on option settings and/or unconsumed
+# arguments.
+func_hookable func_validate_options
+func_validate_options ()
+{
+ $debug_cmd
+
+ # Display all warnings if -W was not given.
+ test -n "$opt_warning_types" || opt_warning_types=" $warning_categories"
+
+ func_run_hooks func_validate_options ${1+"$@"}
+
+ # Bail if the options were screwed!
+ $exit_cmd $EXIT_FAILURE
+
+ # save modified positional parameters for caller
+ func_validate_options_result=$func_run_hooks_result
+}
+
+
+
+## ----------------- ##
+## Helper functions. ##
+## ----------------- ##
+
+# This section contains the helper functions used by the rest of the
+# hookable option parser framework in ascii-betical order.
+
+
+# func_fatal_help ARG...
+# ----------------------
+# Echo program name prefixed message to standard error, followed by
+# a help hint, and exit.
+func_fatal_help ()
+{
+ $debug_cmd
+
+ eval \$ECHO \""Usage: $usage"\"
+ eval \$ECHO \""$fatal_help"\"
+ func_error ${1+"$@"}
+ exit $EXIT_FAILURE
+}
+
+
+# func_help
+# ---------
+# Echo long help message to standard output and exit.
+func_help ()
+{
+ $debug_cmd
+
+ func_usage_message
+ $ECHO "$long_help_message"
+ exit 0
+}
+
+
+# func_missing_arg ARGNAME
+# ------------------------
+# Echo program name prefixed message to standard error and set global
+# exit_cmd.
+func_missing_arg ()
+{
+ $debug_cmd
+
+ func_error "Missing argument for '$1'."
+ exit_cmd=exit
+}
+
+
+# func_split_equals STRING
+# ------------------------
+# Set func_split_equals_lhs and func_split_equals_rhs shell variables after
+# splitting STRING at the '=' sign.
+test -z "$_G_HAVE_XSI_OPS" \
+ && (eval 'x=a/b/c;
+ test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \
+ && _G_HAVE_XSI_OPS=yes
+
+if test yes = "$_G_HAVE_XSI_OPS"
+then
+ # This is an XSI compatible shell, allowing a faster implementation...
+ eval 'func_split_equals ()
+ {
+ $debug_cmd
+
+ func_split_equals_lhs=${1%%=*}
+ func_split_equals_rhs=${1#*=}
+ test "x$func_split_equals_lhs" = "x$1" \
+ && func_split_equals_rhs=
+ }'
+else
+ # ...otherwise fall back to using expr, which is often a shell builtin.
+ func_split_equals ()
+ {
+ $debug_cmd
+
+ func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'`
+ func_split_equals_rhs=
+ test "x$func_split_equals_lhs" = "x$1" \
+ || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'`
+ }
+fi #func_split_equals
+
+
+# func_split_short_opt SHORTOPT
+# -----------------------------
+# Set func_split_short_opt_name and func_split_short_opt_arg shell
+# variables after splitting SHORTOPT after the 2nd character.
+if test yes = "$_G_HAVE_XSI_OPS"
+then
+ # This is an XSI compatible shell, allowing a faster implementation...
+ eval 'func_split_short_opt ()
+ {
+ $debug_cmd
+
+ func_split_short_opt_arg=${1#??}
+ func_split_short_opt_name=${1%"$func_split_short_opt_arg"}
+ }'
+else
+ # ...otherwise fall back to using expr, which is often a shell builtin.
+ func_split_short_opt ()
+ {
+ $debug_cmd
+
+ func_split_short_opt_name=`expr "x$1" : 'x-\(.\)'`
+ func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'`
+ }
+fi #func_split_short_opt
+
+
+# func_usage
+# ----------
+# Echo short help message to standard output and exit.
+func_usage ()
+{
+ $debug_cmd
+
+ func_usage_message
+ $ECHO "Run '$progname --help |${PAGER-more}' for full usage"
+ exit 0
+}
+
+
+# func_usage_message
+# ------------------
+# Echo short help message to standard output.
+func_usage_message ()
+{
+ $debug_cmd
+
+ eval \$ECHO \""Usage: $usage"\"
+ echo
+ $SED -n 's|^# ||
+ /^Written by/{
+ x;p;x
+ }
+ h
+ /^Written by/q' < "$progpath"
+ echo
+ eval \$ECHO \""$usage_message"\"
+}
+
+
+# func_version
+# ------------
+# Echo version message to standard output and exit.
+func_version ()
+{
+ $debug_cmd
+
+ printf '%s\n' "$progname $scriptversion"
+ $SED -n '
+ /(C)/!b go
+ :more
+ /\./!{
+ N
+ s|\n# | |
+ b more
+ }
+ :go
+ /^# Written by /,/# warranty; / {
+ s|^# ||
+ s|^# *$||
+ s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2|
+ p
+ }
+ /^# Written by / {
+ s|^# ||
+ p
+ }
+ /^warranty; /q' < "$progpath"
+
+ exit $?
+}
+
+
+# Local variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC"
+# time-stamp-time-zone: "UTC"
+# End:
+
+# Set a version string.
+scriptversion='(GNU libtool) 2.4.6'
+
+
+# func_echo ARG...
+# ----------------
+# Libtool also displays the current mode in messages, so override
+# funclib.sh func_echo with this custom definition.
+func_echo ()
+{
+ $debug_cmd
+
+ _G_message=$*
+
+ func_echo_IFS=$IFS
+ IFS=$nl
+ for _G_line in $_G_message; do
+ IFS=$func_echo_IFS
+ $ECHO "$progname${opt_mode+: $opt_mode}: $_G_line"
+ done
+ IFS=$func_echo_IFS
+}
+
+
+# func_warning ARG...
+# -------------------
+# Libtool warnings are not categorized, so override funclib.sh
+# func_warning with this simpler definition.
+func_warning ()
+{
+ $debug_cmd
+
+ $warning_func ${1+"$@"}
+}
+
+
+## ---------------- ##
+## Options parsing. ##
+## ---------------- ##
+
+# Hook in the functions to make sure our own options are parsed during
+# the option parsing loop.
+
+usage='$progpath [OPTION]... [MODE-ARG]...'
+
+# Short help message in response to '-h'.
+usage_message="Options:
+ --config show all configuration variables
+ --debug enable verbose shell tracing
+ -n, --dry-run display commands without modifying any files
+ --features display basic configuration information and exit
+ --mode=MODE use operation mode MODE
+ --no-warnings equivalent to '-Wnone'
+ --preserve-dup-deps don't remove duplicate dependency libraries
+ --quiet, --silent don't print informational messages
+ --tag=TAG use configuration variables from tag TAG
+ -v, --verbose print more informational messages than default
+ --version print version information
+ -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all]
+ -h, --help, --help-all print short, long, or detailed help message
+"
+
+# Additional text appended to 'usage_message' in response to '--help'.
+func_help ()
+{
+ $debug_cmd
+
+ func_usage_message
+ $ECHO "$long_help_message
+
+MODE must be one of the following:
+
+ clean remove files from the build directory
+ compile compile a source file into a libtool object
+ execute automatically set library path, then run a program
+ finish complete the installation of libtool libraries
+ install install libraries or executables
+ link create a library or an executable
+ uninstall remove libraries from an installed directory
+
+MODE-ARGS vary depending on the MODE. When passed as first option,
+'--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that.
+Try '$progname --help --mode=MODE' for a more detailed description of MODE.
+
+When reporting a bug, please describe a test case to reproduce it and
+include the following information:
+
+ host-triplet: $host
+ shell: $SHELL
+ compiler: $LTCC
+ compiler flags: $LTCFLAGS
+ linker: $LD (gnu? $with_gnu_ld)
+ version: $progname (GNU libtool) 2.4.6
+ automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q`
+ autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q`
+
+Report bugs to <bug-libtool@gnu.org>.
+GNU libtool home page: <http://www.gnu.org/software/libtool/>.
+General help using GNU software: <http://www.gnu.org/gethelp/>."
+ exit 0
+}
+
+
+# func_lo2o OBJECT-NAME
+# ---------------------
+# Transform OBJECT-NAME from a '.lo' suffix to the platform specific
+# object suffix.
+
+lo2o=s/\\.lo\$/.$objext/
+o2lo=s/\\.$objext\$/.lo/
+
+if test yes = "$_G_HAVE_XSI_OPS"; then
+ eval 'func_lo2o ()
+ {
+ case $1 in
+ *.lo) func_lo2o_result=${1%.lo}.$objext ;;
+ * ) func_lo2o_result=$1 ;;
+ esac
+ }'
+
+ # func_xform LIBOBJ-OR-SOURCE
+ # ---------------------------
+ # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise)
+ # suffix to a '.lo' libtool-object suffix.
+ eval 'func_xform ()
+ {
+ func_xform_result=${1%.*}.lo
+ }'
+else
+ # ...otherwise fall back to using sed.
+ func_lo2o ()
+ {
+ func_lo2o_result=`$ECHO "$1" | $SED "$lo2o"`
+ }
+
+ func_xform ()
+ {
+ func_xform_result=`$ECHO "$1" | $SED 's|\.[^.]*$|.lo|'`
+ }
+fi
+
+
+# func_fatal_configuration ARG...
+# -------------------------------
+# Echo program name prefixed message to standard error, followed by
+# a configuration failure hint, and exit.
+func_fatal_configuration ()
+{
+ func__fatal_error ${1+"$@"} \
+ "See the $PACKAGE documentation for more information." \
+ "Fatal configuration error."
+}
+
+
+# func_config
+# -----------
+# Display the configuration for all the tags in this script.
+func_config ()
+{
+ re_begincf='^# ### BEGIN LIBTOOL'
+ re_endcf='^# ### END LIBTOOL'
+
+ # Default configuration.
+ $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath"
+
+ # Now print the configurations for the tags.
+ for tagname in $taglist; do
+ $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath"
+ done
+
+ exit $?
+}
+
+
+# func_features
+# -------------
+# Display the features supported by this script.
+func_features ()
+{
+ echo "host: $host"
+ if test yes = "$build_libtool_libs"; then
+ echo "enable shared libraries"
+ else
+ echo "disable shared libraries"
+ fi
+ if test yes = "$build_old_libs"; then
+ echo "enable static libraries"
+ else
+ echo "disable static libraries"
+ fi
+
+ exit $?
+}
+
+
+# func_enable_tag TAGNAME
+# -----------------------
+# Verify that TAGNAME is valid, and either flag an error and exit, or
+# enable the TAGNAME tag. We also add TAGNAME to the global $taglist
+# variable here.
+func_enable_tag ()
+{
+ # Global variable:
+ tagname=$1
+
+ re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$"
+ re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$"
+ sed_extractcf=/$re_begincf/,/$re_endcf/p
+
+ # Validate tagname.
+ case $tagname in
+ *[!-_A-Za-z0-9,/]*)
+ func_fatal_error "invalid tag name: $tagname"
+ ;;
+ esac
+
+ # Don't test for the "default" C tag, as we know it's
+ # there but not specially marked.
+ case $tagname in
+ CC) ;;
+ *)
+ if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then
+ taglist="$taglist $tagname"
+
+ # Evaluate the configuration. Be careful to quote the path
+ # and the sed script, to avoid splitting on whitespace, but
+ # also don't use non-portable quotes within backquotes within
+ # quotes we have to do it in 2 steps:
+ extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"`
+ eval "$extractedcf"
+ else
+ func_error "ignoring unknown tag $tagname"
+ fi
+ ;;
+ esac
+}
+
+
+# func_check_version_match
+# ------------------------
+# Ensure that we are using m4 macros, and libtool script from the same
+# release of libtool.
+func_check_version_match ()
+{
+ if test "$package_revision" != "$macro_revision"; then
+ if test "$VERSION" != "$macro_version"; then
+ if test -z "$macro_version"; then
+ cat >&2 <<_LT_EOF
+$progname: Version mismatch error. This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from an older release.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+ else
+ cat >&2 <<_LT_EOF
+$progname: Version mismatch error. This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from $PACKAGE $macro_version.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+ fi
+ else
+ cat >&2 <<_LT_EOF
+$progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision,
+$progname: but the definition of this LT_INIT comes from revision $macro_revision.
+$progname: You should recreate aclocal.m4 with macros from revision $package_revision
+$progname: of $PACKAGE $VERSION and run autoconf again.
+_LT_EOF
+ fi
+
+ exit $EXIT_MISMATCH
+ fi
+}
+
+
+# libtool_options_prep [ARG]...
+# -----------------------------
+# Preparation for options parsed by libtool.
+libtool_options_prep ()
+{
+ $debug_mode
+
+ # Option defaults:
+ opt_config=false
+ opt_dlopen=
+ opt_dry_run=false
+ opt_help=false
+ opt_mode=
+ opt_preserve_dup_deps=false
+ opt_quiet=false
+
+ nonopt=
+ preserve_args=
+
+ # Shorthand for --mode=foo, only valid as the first argument
+ case $1 in
+ clean|clea|cle|cl)
+ shift; set dummy --mode clean ${1+"$@"}; shift
+ ;;
+ compile|compil|compi|comp|com|co|c)
+ shift; set dummy --mode compile ${1+"$@"}; shift
+ ;;
+ execute|execut|execu|exec|exe|ex|e)
+ shift; set dummy --mode execute ${1+"$@"}; shift
+ ;;
+ finish|finis|fini|fin|fi|f)
+ shift; set dummy --mode finish ${1+"$@"}; shift
+ ;;
+ install|instal|insta|inst|ins|in|i)
+ shift; set dummy --mode install ${1+"$@"}; shift
+ ;;
+ link|lin|li|l)
+ shift; set dummy --mode link ${1+"$@"}; shift
+ ;;
+ uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u)
+ shift; set dummy --mode uninstall ${1+"$@"}; shift
+ ;;
+ esac
+
+ # Pass back the list of options.
+ func_quote_for_eval ${1+"$@"}
+ libtool_options_prep_result=$func_quote_for_eval_result
+}
+func_add_hook func_options_prep libtool_options_prep
+
+
+# libtool_parse_options [ARG]...
+# ---------------------------------
+# Provide handling for libtool specific options.
+libtool_parse_options ()
+{
+ $debug_cmd
+
+ # Perform our own loop to consume as many options as possible in
+ # each iteration.
+ while test $# -gt 0; do
+ _G_opt=$1
+ shift
+ case $_G_opt in
+ --dry-run|--dryrun|-n)
+ opt_dry_run=:
+ ;;
+
+ --config) func_config ;;
+
+ --dlopen|-dlopen)
+ opt_dlopen="${opt_dlopen+$opt_dlopen
+}$1"
+ shift
+ ;;
+
+ --preserve-dup-deps)
+ opt_preserve_dup_deps=: ;;
+
+ --features) func_features ;;
+
+ --finish) set dummy --mode finish ${1+"$@"}; shift ;;
+
+ --help) opt_help=: ;;
+
+ --help-all) opt_help=': help-all' ;;
+
+ --mode) test $# = 0 && func_missing_arg $_G_opt && break
+ opt_mode=$1
+ case $1 in
+ # Valid mode arguments:
+ clean|compile|execute|finish|install|link|relink|uninstall) ;;
+
+ # Catch anything else as an error
+ *) func_error "invalid argument for $_G_opt"
+ exit_cmd=exit
+ break
+ ;;
+ esac
+ shift
+ ;;
+
+ --no-silent|--no-quiet)
+ opt_quiet=false
+ func_append preserve_args " $_G_opt"
+ ;;
+
+ --no-warnings|--no-warning|--no-warn)
+ opt_warning=false
+ func_append preserve_args " $_G_opt"
+ ;;
+
+ --no-verbose)
+ opt_verbose=false
+ func_append preserve_args " $_G_opt"
+ ;;
+
+ --silent|--quiet)
+ opt_quiet=:
+ opt_verbose=false
+ func_append preserve_args " $_G_opt"
+ ;;
+
+ --tag) test $# = 0 && func_missing_arg $_G_opt && break
+ opt_tag=$1
+ func_append preserve_args " $_G_opt $1"
+ func_enable_tag "$1"
+ shift
+ ;;
+
+ --verbose|-v) opt_quiet=false
+ opt_verbose=:
+ func_append preserve_args " $_G_opt"
+ ;;
+
+ # An option not handled by this hook function:
+ *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;;
+ esac
+ done
+
+
+ # save modified positional parameters for caller
+ func_quote_for_eval ${1+"$@"}
+ libtool_parse_options_result=$func_quote_for_eval_result
+}
+func_add_hook func_parse_options libtool_parse_options
+
+
+
+# libtool_validate_options [ARG]...
+# ---------------------------------
+# Perform any sanity checks on option settings and/or unconsumed
+# arguments.
+libtool_validate_options ()
+{
+ # save first non-option argument
+ if test 0 -lt $#; then
+ nonopt=$1
+ shift
+ fi
+
+ # preserve --debug
+ test : = "$debug_cmd" || func_append preserve_args " --debug"
+
+ case $host in
+ # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452
+ # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788
+ *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*)
+ # don't eliminate duplications in $postdeps and $predeps
+ opt_duplicate_compiler_generated_deps=:
+ ;;
+ *)
+ opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps
+ ;;
+ esac
+
+ $opt_help || {
+ # Sanity checks first:
+ func_check_version_match
+
+ test yes != "$build_libtool_libs" \
+ && test yes != "$build_old_libs" \
+ && func_fatal_configuration "not configured to build any kind of library"
+
+ # Darwin sucks
+ eval std_shrext=\"$shrext_cmds\"
+
+ # Only execute mode is allowed to have -dlopen flags.
+ if test -n "$opt_dlopen" && test execute != "$opt_mode"; then
+ func_error "unrecognized option '-dlopen'"
+ $ECHO "$help" 1>&2
+ exit $EXIT_FAILURE
+ fi
+
+ # Change the help message to a mode-specific one.
+ generic_help=$help
+ help="Try '$progname --help --mode=$opt_mode' for more information."
+ }
+
+ # Pass back the unparsed argument list
+ func_quote_for_eval ${1+"$@"}
+ libtool_validate_options_result=$func_quote_for_eval_result
+}
+func_add_hook func_validate_options libtool_validate_options
+
+
+# Process options as early as possible so that --help and --version
+# can return quickly.
+func_options ${1+"$@"}
+eval set dummy "$func_options_result"; shift
+
+
+
+## ----------- ##
+## Main. ##
+## ----------- ##
+
+magic='%%%MAGIC variable%%%'
+magic_exe='%%%MAGIC EXE variable%%%'
+
+# Global variables.
+extracted_archives=
+extracted_serial=0
+
+# If this variable is set in any of the actions, the command in it
+# will be execed at the end. This prevents here-documents from being
+# left over by shells.
+exec_cmd=
+
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+}
+
+# func_generated_by_libtool
+# True iff stdin has been generated by Libtool. This function is only
+# a basic sanity check; it will hardly flush out determined imposters.
+func_generated_by_libtool_p ()
+{
+ $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1
+}
+
+# func_lalib_p file
+# True iff FILE is a libtool '.la' library or '.lo' object file.
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_lalib_p ()
+{
+ test -f "$1" &&
+ $SED -e 4q "$1" 2>/dev/null | func_generated_by_libtool_p
+}
+
+# func_lalib_unsafe_p file
+# True iff FILE is a libtool '.la' library or '.lo' object file.
+# This function implements the same check as func_lalib_p without
+# resorting to external programs. To this end, it redirects stdin and
+# closes it afterwards, without saving the original file descriptor.
+# As a safety measure, use it only where a negative result would be
+# fatal anyway. Works if 'file' does not exist.
+func_lalib_unsafe_p ()
+{
+ lalib_p=no
+ if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then
+ for lalib_p_l in 1 2 3 4
+ do
+ read lalib_p_line
+ case $lalib_p_line in
+ \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;;
+ esac
+ done
+ exec 0<&5 5<&-
+ fi
+ test yes = "$lalib_p"
+}
+
+# func_ltwrapper_script_p file
+# True iff FILE is a libtool wrapper script
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_script_p ()
+{
+ test -f "$1" &&
+ $lt_truncate_bin < "$1" 2>/dev/null | func_generated_by_libtool_p
+}
+
+# func_ltwrapper_executable_p file
+# True iff FILE is a libtool wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_executable_p ()
+{
+ func_ltwrapper_exec_suffix=
+ case $1 in
+ *.exe) ;;
+ *) func_ltwrapper_exec_suffix=.exe ;;
+ esac
+ $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1
+}
+
+# func_ltwrapper_scriptname file
+# Assumes file is an ltwrapper_executable
+# uses $file to determine the appropriate filename for a
+# temporary ltwrapper_script.
+func_ltwrapper_scriptname ()
+{
+ func_dirname_and_basename "$1" "" "."
+ func_stripname '' '.exe' "$func_basename_result"
+ func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper
+}
+
+# func_ltwrapper_p file
+# True iff FILE is a libtool wrapper script or wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_p ()
+{
+ func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1"
+}
+
+
+# func_execute_cmds commands fail_cmd
+# Execute tilde-delimited COMMANDS.
+# If FAIL_CMD is given, eval that upon failure.
+# FAIL_CMD may read-access the current command in variable CMD!
+func_execute_cmds ()
+{
+ $debug_cmd
+
+ save_ifs=$IFS; IFS='~'
+ for cmd in $1; do
+ IFS=$sp$nl
+ eval cmd=\"$cmd\"
+ IFS=$save_ifs
+ func_show_eval "$cmd" "${2-:}"
+ done
+ IFS=$save_ifs
+}
+
+
+# func_source file
+# Source FILE, adding directory component if necessary.
+# Note that it is not necessary on cygwin/mingw to append a dot to
+# FILE even if both FILE and FILE.exe exist: automatic-append-.exe
+# behavior happens only for exec(3), not for open(2)! Also, sourcing
+# 'FILE.' does not work on cygwin managed mounts.
+func_source ()
+{
+ $debug_cmd
+
+ case $1 in
+ */* | *\\*) . "$1" ;;
+ *) . "./$1" ;;
+ esac
+}
+
+
+# func_resolve_sysroot PATH
+# Replace a leading = in PATH with a sysroot. Store the result into
+# func_resolve_sysroot_result
+func_resolve_sysroot ()
+{
+ func_resolve_sysroot_result=$1
+ case $func_resolve_sysroot_result in
+ =*)
+ func_stripname '=' '' "$func_resolve_sysroot_result"
+ func_resolve_sysroot_result=$lt_sysroot$func_stripname_result
+ ;;
+ esac
+}
+
+# func_replace_sysroot PATH
+# If PATH begins with the sysroot, replace it with = and
+# store the result into func_replace_sysroot_result.
+func_replace_sysroot ()
+{
+ case $lt_sysroot:$1 in
+ ?*:"$lt_sysroot"*)
+ func_stripname "$lt_sysroot" '' "$1"
+ func_replace_sysroot_result='='$func_stripname_result
+ ;;
+ *)
+ # Including no sysroot.
+ func_replace_sysroot_result=$1
+ ;;
+ esac
+}
+
+# func_infer_tag arg
+# Infer tagged configuration to use if any are available and
+# if one wasn't chosen via the "--tag" command line option.
+# Only attempt this if the compiler in the base compile
+# command doesn't match the default compiler.
+# arg is usually of the form 'gcc ...'
+func_infer_tag ()
+{
+ $debug_cmd
+
+ if test -n "$available_tags" && test -z "$tagname"; then
+ CC_quoted=
+ for arg in $CC; do
+ func_append_quoted CC_quoted "$arg"
+ done
+ CC_expanded=`func_echo_all $CC`
+ CC_quoted_expanded=`func_echo_all $CC_quoted`
+ case $@ in
+ # Blanks in the command may have been stripped by the calling shell,
+ # but not from the CC environment variable when configure was run.
+ " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \
+ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;;
+ # Blanks at the start of $base_compile will cause this to fail
+ # if we don't check for them as well.
+ *)
+ for z in $available_tags; do
+ if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then
+ # Evaluate the configuration.
+ eval "`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`"
+ CC_quoted=
+ for arg in $CC; do
+ # Double-quote args containing other shell metacharacters.
+ func_append_quoted CC_quoted "$arg"
+ done
+ CC_expanded=`func_echo_all $CC`
+ CC_quoted_expanded=`func_echo_all $CC_quoted`
+ case "$@ " in
+ " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \
+ " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*)
+ # The compiler in the base compile command matches
+ # the one in the tagged configuration.
+ # Assume this is the tagged configuration we want.
+ tagname=$z
+ break
+ ;;
+ esac
+ fi
+ done
+ # If $tagname still isn't set, then no tagged configuration
+ # was found and let the user know that the "--tag" command
+ # line option must be used.
+ if test -z "$tagname"; then
+ func_echo "unable to infer tagged configuration"
+ func_fatal_error "specify a tag with '--tag'"
+# else
+# func_verbose "using $tagname tagged configuration"
+ fi
+ ;;
+ esac
+ fi
+}
+
+
+
+# func_write_libtool_object output_name pic_name nonpic_name
+# Create a libtool object file (analogous to a ".la" file),
+# but don't create it if we're doing a dry run.
+func_write_libtool_object ()
+{
+ write_libobj=$1
+ if test yes = "$build_libtool_libs"; then
+ write_lobj=\'$2\'
+ else
+ write_lobj=none
+ fi
+
+ if test yes = "$build_old_libs"; then
+ write_oldobj=\'$3\'
+ else
+ write_oldobj=none
+ fi
+
+ $opt_dry_run || {
+ cat >${write_libobj}T <<EOF
+# $write_libobj - a libtool object file
+# Generated by $PROGRAM (GNU $PACKAGE) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# Name of the PIC object.
+pic_object=$write_lobj
+
+# Name of the non-PIC object
+non_pic_object=$write_oldobj
+
+EOF
+ $MV "${write_libobj}T" "$write_libobj"
+ }
+}
+
+
+##################################################
+# FILE NAME AND PATH CONVERSION HELPER FUNCTIONS #
+##################################################
+
+# func_convert_core_file_wine_to_w32 ARG
+# Helper function used by file name conversion functions when $build is *nix,
+# and $host is mingw, cygwin, or some other w32 environment. Relies on a
+# correctly configured wine environment available, with the winepath program
+# in $build's $PATH.
+#
+# ARG is the $build file name to be converted to w32 format.
+# Result is available in $func_convert_core_file_wine_to_w32_result, and will
+# be empty on error (or when ARG is empty)
+func_convert_core_file_wine_to_w32 ()
+{
+ $debug_cmd
+
+ func_convert_core_file_wine_to_w32_result=$1
+ if test -n "$1"; then
+ # Unfortunately, winepath does not exit with a non-zero error code, so we
+ # are forced to check the contents of stdout. On the other hand, if the
+ # command is not found, the shell will set an exit code of 127 and print
+ # *an error message* to stdout. So we must check for both error code of
+ # zero AND non-empty stdout, which explains the odd construction:
+ func_convert_core_file_wine_to_w32_tmp=`winepath -w "$1" 2>/dev/null`
+ if test "$?" -eq 0 && test -n "$func_convert_core_file_wine_to_w32_tmp"; then
+ func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" |
+ $SED -e "$sed_naive_backslashify"`
+ else
+ func_convert_core_file_wine_to_w32_result=
+ fi
+ fi
+}
+# end: func_convert_core_file_wine_to_w32
+
+
+# func_convert_core_path_wine_to_w32 ARG
+# Helper function used by path conversion functions when $build is *nix, and
+# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly
+# configured wine environment available, with the winepath program in $build's
+# $PATH. Assumes ARG has no leading or trailing path separator characters.
+#
+# ARG is path to be converted from $build format to win32.
+# Result is available in $func_convert_core_path_wine_to_w32_result.
+# Unconvertible file (directory) names in ARG are skipped; if no directory names
+# are convertible, then the result may be empty.
+func_convert_core_path_wine_to_w32 ()
+{
+ $debug_cmd
+
+ # unfortunately, winepath doesn't convert paths, only file names
+ func_convert_core_path_wine_to_w32_result=
+ if test -n "$1"; then
+ oldIFS=$IFS
+ IFS=:
+ for func_convert_core_path_wine_to_w32_f in $1; do
+ IFS=$oldIFS
+ func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f"
+ if test -n "$func_convert_core_file_wine_to_w32_result"; then
+ if test -z "$func_convert_core_path_wine_to_w32_result"; then
+ func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result
+ else
+ func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result"
+ fi
+ fi
+ done
+ IFS=$oldIFS
+ fi
+}
+# end: func_convert_core_path_wine_to_w32
+
+
+# func_cygpath ARGS...
+# Wrapper around calling the cygpath program via LT_CYGPATH. This is used when
+# when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2)
+# $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or
+# (2), returns the Cygwin file name or path in func_cygpath_result (input
+# file name or path is assumed to be in w32 format, as previously converted
+# from $build's *nix or MSYS format). In case (3), returns the w32 file name
+# or path in func_cygpath_result (input file name or path is assumed to be in
+# Cygwin format). Returns an empty string on error.
+#
+# ARGS are passed to cygpath, with the last one being the file name or path to
+# be converted.
+#
+# Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH
+# environment variable; do not put it in $PATH.
+func_cygpath ()
+{
+ $debug_cmd
+
+ if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then
+ func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null`
+ if test "$?" -ne 0; then
+ # on failure, ensure result is empty
+ func_cygpath_result=
+ fi
+ else
+ func_cygpath_result=
+ func_error "LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'"
+ fi
+}
+#end: func_cygpath
+
+
+# func_convert_core_msys_to_w32 ARG
+# Convert file name or path ARG from MSYS format to w32 format. Return
+# result in func_convert_core_msys_to_w32_result.
+func_convert_core_msys_to_w32 ()
+{
+ $debug_cmd
+
+ # awkward: cmd appends spaces to result
+ func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null |
+ $SED -e 's/[ ]*$//' -e "$sed_naive_backslashify"`
+}
+#end: func_convert_core_msys_to_w32
+
+
+# func_convert_file_check ARG1 ARG2
+# Verify that ARG1 (a file name in $build format) was converted to $host
+# format in ARG2. Otherwise, emit an error message, but continue (resetting
+# func_to_host_file_result to ARG1).
+func_convert_file_check ()
+{
+ $debug_cmd
+
+ if test -z "$2" && test -n "$1"; then
+ func_error "Could not determine host file name corresponding to"
+ func_error " '$1'"
+ func_error "Continuing, but uninstalled executables may not work."
+ # Fallback:
+ func_to_host_file_result=$1
+ fi
+}
+# end func_convert_file_check
+
+
+# func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH
+# Verify that FROM_PATH (a path in $build format) was converted to $host
+# format in TO_PATH. Otherwise, emit an error message, but continue, resetting
+# func_to_host_file_result to a simplistic fallback value (see below).
+func_convert_path_check ()
+{
+ $debug_cmd
+
+ if test -z "$4" && test -n "$3"; then
+ func_error "Could not determine the host path corresponding to"
+ func_error " '$3'"
+ func_error "Continuing, but uninstalled executables may not work."
+ # Fallback. This is a deliberately simplistic "conversion" and
+ # should not be "improved". See libtool.info.
+ if test "x$1" != "x$2"; then
+ lt_replace_pathsep_chars="s|$1|$2|g"
+ func_to_host_path_result=`echo "$3" |
+ $SED -e "$lt_replace_pathsep_chars"`
+ else
+ func_to_host_path_result=$3
+ fi
+ fi
+}
+# end func_convert_path_check
+
+
+# func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG
+# Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT
+# and appending REPL if ORIG matches BACKPAT.
+func_convert_path_front_back_pathsep ()
+{
+ $debug_cmd
+
+ case $4 in
+ $1 ) func_to_host_path_result=$3$func_to_host_path_result
+ ;;
+ esac
+ case $4 in
+ $2 ) func_append func_to_host_path_result "$3"
+ ;;
+ esac
+}
+# end func_convert_path_front_back_pathsep
+
+
+##################################################
+# $build to $host FILE NAME CONVERSION FUNCTIONS #
+##################################################
+# invoked via '$to_host_file_cmd ARG'
+#
+# In each case, ARG is the path to be converted from $build to $host format.
+# Result will be available in $func_to_host_file_result.
+
+
+# func_to_host_file ARG
+# Converts the file name ARG from $build format to $host format. Return result
+# in func_to_host_file_result.
+func_to_host_file ()
+{
+ $debug_cmd
+
+ $to_host_file_cmd "$1"
+}
+# end func_to_host_file
+
+
+# func_to_tool_file ARG LAZY
+# converts the file name ARG from $build format to toolchain format. Return
+# result in func_to_tool_file_result. If the conversion in use is listed
+# in (the comma separated) LAZY, no conversion takes place.
+func_to_tool_file ()
+{
+ $debug_cmd
+
+ case ,$2, in
+ *,"$to_tool_file_cmd",*)
+ func_to_tool_file_result=$1
+ ;;
+ *)
+ $to_tool_file_cmd "$1"
+ func_to_tool_file_result=$func_to_host_file_result
+ ;;
+ esac
+}
+# end func_to_tool_file
+
+
+# func_convert_file_noop ARG
+# Copy ARG to func_to_host_file_result.
+func_convert_file_noop ()
+{
+ func_to_host_file_result=$1
+}
+# end func_convert_file_noop
+
+
+# func_convert_file_msys_to_w32 ARG
+# Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic
+# conversion to w32 is not available inside the cwrapper. Returns result in
+# func_to_host_file_result.
+func_convert_file_msys_to_w32 ()
+{
+ $debug_cmd
+
+ func_to_host_file_result=$1
+ if test -n "$1"; then
+ func_convert_core_msys_to_w32 "$1"
+ func_to_host_file_result=$func_convert_core_msys_to_w32_result
+ fi
+ func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_msys_to_w32
+
+
+# func_convert_file_cygwin_to_w32 ARG
+# Convert file name ARG from Cygwin to w32 format. Returns result in
+# func_to_host_file_result.
+func_convert_file_cygwin_to_w32 ()
+{
+ $debug_cmd
+
+ func_to_host_file_result=$1
+ if test -n "$1"; then
+ # because $build is cygwin, we call "the" cygpath in $PATH; no need to use
+ # LT_CYGPATH in this case.
+ func_to_host_file_result=`cygpath -m "$1"`
+ fi
+ func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_cygwin_to_w32
+
+
+# func_convert_file_nix_to_w32 ARG
+# Convert file name ARG from *nix to w32 format. Requires a wine environment
+# and a working winepath. Returns result in func_to_host_file_result.
+func_convert_file_nix_to_w32 ()
+{
+ $debug_cmd
+
+ func_to_host_file_result=$1
+ if test -n "$1"; then
+ func_convert_core_file_wine_to_w32 "$1"
+ func_to_host_file_result=$func_convert_core_file_wine_to_w32_result
+ fi
+ func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_nix_to_w32
+
+
+# func_convert_file_msys_to_cygwin ARG
+# Convert file name ARG from MSYS to Cygwin format. Requires LT_CYGPATH set.
+# Returns result in func_to_host_file_result.
+func_convert_file_msys_to_cygwin ()
+{
+ $debug_cmd
+
+ func_to_host_file_result=$1
+ if test -n "$1"; then
+ func_convert_core_msys_to_w32 "$1"
+ func_cygpath -u "$func_convert_core_msys_to_w32_result"
+ func_to_host_file_result=$func_cygpath_result
+ fi
+ func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_msys_to_cygwin
+
+
+# func_convert_file_nix_to_cygwin ARG
+# Convert file name ARG from *nix to Cygwin format. Requires Cygwin installed
+# in a wine environment, working winepath, and LT_CYGPATH set. Returns result
+# in func_to_host_file_result.
+func_convert_file_nix_to_cygwin ()
+{
+ $debug_cmd
+
+ func_to_host_file_result=$1
+ if test -n "$1"; then
+ # convert from *nix to w32, then use cygpath to convert from w32 to cygwin.
+ func_convert_core_file_wine_to_w32 "$1"
+ func_cygpath -u "$func_convert_core_file_wine_to_w32_result"
+ func_to_host_file_result=$func_cygpath_result
+ fi
+ func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_nix_to_cygwin
+
+
+#############################################
+# $build to $host PATH CONVERSION FUNCTIONS #
+#############################################
+# invoked via '$to_host_path_cmd ARG'
+#
+# In each case, ARG is the path to be converted from $build to $host format.
+# The result will be available in $func_to_host_path_result.
+#
+# Path separators are also converted from $build format to $host format. If
+# ARG begins or ends with a path separator character, it is preserved (but
+# converted to $host format) on output.
+#
+# All path conversion functions are named using the following convention:
+# file name conversion function : func_convert_file_X_to_Y ()
+# path conversion function : func_convert_path_X_to_Y ()
+# where, for any given $build/$host combination the 'X_to_Y' value is the
+# same. If conversion functions are added for new $build/$host combinations,
+# the two new functions must follow this pattern, or func_init_to_host_path_cmd
+# will break.
+
+
+# func_init_to_host_path_cmd
+# Ensures that function "pointer" variable $to_host_path_cmd is set to the
+# appropriate value, based on the value of $to_host_file_cmd.
+to_host_path_cmd=
+func_init_to_host_path_cmd ()
+{
+ $debug_cmd
+
+ if test -z "$to_host_path_cmd"; then
+ func_stripname 'func_convert_file_' '' "$to_host_file_cmd"
+ to_host_path_cmd=func_convert_path_$func_stripname_result
+ fi
+}
+
+
+# func_to_host_path ARG
+# Converts the path ARG from $build format to $host format. Return result
+# in func_to_host_path_result.
+func_to_host_path ()
+{
+ $debug_cmd
+
+ func_init_to_host_path_cmd
+ $to_host_path_cmd "$1"
+}
+# end func_to_host_path
+
+
+# func_convert_path_noop ARG
+# Copy ARG to func_to_host_path_result.
+func_convert_path_noop ()
+{
+ func_to_host_path_result=$1
+}
+# end func_convert_path_noop
+
+
+# func_convert_path_msys_to_w32 ARG
+# Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic
+# conversion to w32 is not available inside the cwrapper. Returns result in
+# func_to_host_path_result.
+func_convert_path_msys_to_w32 ()
+{
+ $debug_cmd
+
+ func_to_host_path_result=$1
+ if test -n "$1"; then
+ # Remove leading and trailing path separator characters from ARG. MSYS
+ # behavior is inconsistent here; cygpath turns them into '.;' and ';.';
+ # and winepath ignores them completely.
+ func_stripname : : "$1"
+ func_to_host_path_tmp1=$func_stripname_result
+ func_convert_core_msys_to_w32 "$func_to_host_path_tmp1"
+ func_to_host_path_result=$func_convert_core_msys_to_w32_result
+ func_convert_path_check : ";" \
+ "$func_to_host_path_tmp1" "$func_to_host_path_result"
+ func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+ fi
+}
+# end func_convert_path_msys_to_w32
+
+
+# func_convert_path_cygwin_to_w32 ARG
+# Convert path ARG from Cygwin to w32 format. Returns result in
+# func_to_host_file_result.
+func_convert_path_cygwin_to_w32 ()
+{
+ $debug_cmd
+
+ func_to_host_path_result=$1
+ if test -n "$1"; then
+ # See func_convert_path_msys_to_w32:
+ func_stripname : : "$1"
+ func_to_host_path_tmp1=$func_stripname_result
+ func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"`
+ func_convert_path_check : ";" \
+ "$func_to_host_path_tmp1" "$func_to_host_path_result"
+ func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+ fi
+}
+# end func_convert_path_cygwin_to_w32
+
+
+# func_convert_path_nix_to_w32 ARG
+# Convert path ARG from *nix to w32 format. Requires a wine environment and
+# a working winepath. Returns result in func_to_host_file_result.
+func_convert_path_nix_to_w32 ()
+{
+ $debug_cmd
+
+ func_to_host_path_result=$1
+ if test -n "$1"; then
+ # See func_convert_path_msys_to_w32:
+ func_stripname : : "$1"
+ func_to_host_path_tmp1=$func_stripname_result
+ func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1"
+ func_to_host_path_result=$func_convert_core_path_wine_to_w32_result
+ func_convert_path_check : ";" \
+ "$func_to_host_path_tmp1" "$func_to_host_path_result"
+ func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+ fi
+}
+# end func_convert_path_nix_to_w32
+
+
+# func_convert_path_msys_to_cygwin ARG
+# Convert path ARG from MSYS to Cygwin format. Requires LT_CYGPATH set.
+# Returns result in func_to_host_file_result.
+func_convert_path_msys_to_cygwin ()
+{
+ $debug_cmd
+
+ func_to_host_path_result=$1
+ if test -n "$1"; then
+ # See func_convert_path_msys_to_w32:
+ func_stripname : : "$1"
+ func_to_host_path_tmp1=$func_stripname_result
+ func_convert_core_msys_to_w32 "$func_to_host_path_tmp1"
+ func_cygpath -u -p "$func_convert_core_msys_to_w32_result"
+ func_to_host_path_result=$func_cygpath_result
+ func_convert_path_check : : \
+ "$func_to_host_path_tmp1" "$func_to_host_path_result"
+ func_convert_path_front_back_pathsep ":*" "*:" : "$1"
+ fi
+}
+# end func_convert_path_msys_to_cygwin
+
+
+# func_convert_path_nix_to_cygwin ARG
+# Convert path ARG from *nix to Cygwin format. Requires Cygwin installed in a
+# a wine environment, working winepath, and LT_CYGPATH set. Returns result in
+# func_to_host_file_result.
+func_convert_path_nix_to_cygwin ()
+{
+ $debug_cmd
+
+ func_to_host_path_result=$1
+ if test -n "$1"; then
+ # Remove leading and trailing path separator characters from
+ # ARG. msys behavior is inconsistent here, cygpath turns them
+ # into '.;' and ';.', and winepath ignores them completely.
+ func_stripname : : "$1"
+ func_to_host_path_tmp1=$func_stripname_result
+ func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1"
+ func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result"
+ func_to_host_path_result=$func_cygpath_result
+ func_convert_path_check : : \
+ "$func_to_host_path_tmp1" "$func_to_host_path_result"
+ func_convert_path_front_back_pathsep ":*" "*:" : "$1"
+ fi
+}
+# end func_convert_path_nix_to_cygwin
+
+
+# func_dll_def_p FILE
+# True iff FILE is a Windows DLL '.def' file.
+# Keep in sync with _LT_DLL_DEF_P in libtool.m4
+func_dll_def_p ()
+{
+ $debug_cmd
+
+ func_dll_def_p_tmp=`$SED -n \
+ -e 's/^[ ]*//' \
+ -e '/^\(;.*\)*$/d' \
+ -e 's/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p' \
+ -e q \
+ "$1"`
+ test DEF = "$func_dll_def_p_tmp"
+}
+
+
+# func_mode_compile arg...
+func_mode_compile ()
+{
+ $debug_cmd
+
+ # Get the compilation command and the source file.
+ base_compile=
+ srcfile=$nonopt # always keep a non-empty value in "srcfile"
+ suppress_opt=yes
+ suppress_output=
+ arg_mode=normal
+ libobj=
+ later=
+ pie_flag=
+
+ for arg
+ do
+ case $arg_mode in
+ arg )
+ # do not "continue". Instead, add this to base_compile
+ lastarg=$arg
+ arg_mode=normal
+ ;;
+
+ target )
+ libobj=$arg
+ arg_mode=normal
+ continue
+ ;;
+
+ normal )
+ # Accept any command-line options.
+ case $arg in
+ -o)
+ test -n "$libobj" && \
+ func_fatal_error "you cannot specify '-o' more than once"
+ arg_mode=target
+ continue
+ ;;
+
+ -pie | -fpie | -fPIE)
+ func_append pie_flag " $arg"
+ continue
+ ;;
+
+ -shared | -static | -prefer-pic | -prefer-non-pic)
+ func_append later " $arg"
+ continue
+ ;;
+
+ -no-suppress)
+ suppress_opt=no
+ continue
+ ;;
+
+ -Xcompiler)
+ arg_mode=arg # the next one goes into the "base_compile" arg list
+ continue # The current "srcfile" will either be retained or
+ ;; # replaced later. I would guess that would be a bug.
+
+ -Wc,*)
+ func_stripname '-Wc,' '' "$arg"
+ args=$func_stripname_result
+ lastarg=
+ save_ifs=$IFS; IFS=,
+ for arg in $args; do
+ IFS=$save_ifs
+ func_append_quoted lastarg "$arg"
+ done
+ IFS=$save_ifs
+ func_stripname ' ' '' "$lastarg"
+ lastarg=$func_stripname_result
+
+ # Add the arguments to base_compile.
+ func_append base_compile " $lastarg"
+ continue
+ ;;
+
+ *)
+ # Accept the current argument as the source file.
+ # The previous "srcfile" becomes the current argument.
+ #
+ lastarg=$srcfile
+ srcfile=$arg
+ ;;
+ esac # case $arg
+ ;;
+ esac # case $arg_mode
+
+ # Aesthetically quote the previous argument.
+ func_append_quoted base_compile "$lastarg"
+ done # for arg
+
+ case $arg_mode in
+ arg)
+ func_fatal_error "you must specify an argument for -Xcompile"
+ ;;
+ target)
+ func_fatal_error "you must specify a target with '-o'"
+ ;;
+ *)
+ # Get the name of the library object.
+ test -z "$libobj" && {
+ func_basename "$srcfile"
+ libobj=$func_basename_result
+ }
+ ;;
+ esac
+
+ # Recognize several different file suffixes.
+ # If the user specifies -o file.o, it is replaced with file.lo
+ case $libobj in
+ *.[cCFSifmso] | \
+ *.ada | *.adb | *.ads | *.asm | \
+ *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \
+ *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup)
+ func_xform "$libobj"
+ libobj=$func_xform_result
+ ;;
+ esac
+
+ case $libobj in
+ *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;;
+ *)
+ func_fatal_error "cannot determine name of library object from '$libobj'"
+ ;;
+ esac
+
+ func_infer_tag $base_compile
+
+ for arg in $later; do
+ case $arg in
+ -shared)
+ test yes = "$build_libtool_libs" \
+ || func_fatal_configuration "cannot build a shared library"
+ build_old_libs=no
+ continue
+ ;;
+
+ -static)
+ build_libtool_libs=no
+ build_old_libs=yes
+ continue
+ ;;
+
+ -prefer-pic)
+ pic_mode=yes
+ continue
+ ;;
+
+ -prefer-non-pic)
+ pic_mode=no
+ continue
+ ;;
+ esac
+ done
+
+ func_quote_for_eval "$libobj"
+ test "X$libobj" != "X$func_quote_for_eval_result" \
+ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \
+ && func_warning "libobj name '$libobj' may not contain shell special characters."
+ func_dirname_and_basename "$obj" "/" ""
+ objname=$func_basename_result
+ xdir=$func_dirname_result
+ lobj=$xdir$objdir/$objname
+
+ test -z "$base_compile" && \
+ func_fatal_help "you must specify a compilation command"
+
+ # Delete any leftover library objects.
+ if test yes = "$build_old_libs"; then
+ removelist="$obj $lobj $libobj ${libobj}T"
+ else
+ removelist="$lobj $libobj ${libobj}T"
+ fi
+
+ # On Cygwin there's no "real" PIC flag so we must build both object types
+ case $host_os in
+ cygwin* | mingw* | pw32* | os2* | cegcc*)
+ pic_mode=default
+ ;;
+ esac
+ if test no = "$pic_mode" && test pass_all != "$deplibs_check_method"; then
+ # non-PIC code in shared libraries is not supported
+ pic_mode=default
+ fi
+
+ # Calculate the filename of the output object if compiler does
+ # not support -o with -c
+ if test no = "$compiler_c_o"; then
+ output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.$objext
+ lockfile=$output_obj.lock
+ else
+ output_obj=
+ need_locks=no
+ lockfile=
+ fi
+
+ # Lock this critical section if it is needed
+ # We use this script file to make the link, it avoids creating a new file
+ if test yes = "$need_locks"; then
+ until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
+ func_echo "Waiting for $lockfile to be removed"
+ sleep 2
+ done
+ elif test warn = "$need_locks"; then
+ if test -f "$lockfile"; then
+ $ECHO "\
+*** ERROR, $lockfile exists and contains:
+`cat $lockfile 2>/dev/null`
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support '-c' and '-o' together. If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+ $opt_dry_run || $RM $removelist
+ exit $EXIT_FAILURE
+ fi
+ func_append removelist " $output_obj"
+ $ECHO "$srcfile" > "$lockfile"
+ fi
+
+ $opt_dry_run || $RM $removelist
+ func_append removelist " $lockfile"
+ trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15
+
+ func_to_tool_file "$srcfile" func_convert_file_msys_to_w32
+ srcfile=$func_to_tool_file_result
+ func_quote_for_eval "$srcfile"
+ qsrcfile=$func_quote_for_eval_result
+
+ # Only build a PIC object if we are building libtool libraries.
+ if test yes = "$build_libtool_libs"; then
+ # Without this assignment, base_compile gets emptied.
+ fbsd_hideous_sh_bug=$base_compile
+
+ if test no != "$pic_mode"; then
+ command="$base_compile $qsrcfile $pic_flag"
+ else
+ # Don't build PIC code
+ command="$base_compile $qsrcfile"
+ fi
+
+ func_mkdir_p "$xdir$objdir"
+
+ if test -z "$output_obj"; then
+ # Place PIC objects in $objdir
+ func_append command " -o $lobj"
+ fi
+
+ func_show_eval_locale "$command" \
+ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE'
+
+ if test warn = "$need_locks" &&
+ test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+ $ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support '-c' and '-o' together. If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+ $opt_dry_run || $RM $removelist
+ exit $EXIT_FAILURE
+ fi
+
+ # Just move the object if needed, then go on to compile the next one
+ if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then
+ func_show_eval '$MV "$output_obj" "$lobj"' \
+ 'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+ fi
+
+ # Allow error messages only from the first compilation.
+ if test yes = "$suppress_opt"; then
+ suppress_output=' >/dev/null 2>&1'
+ fi
+ fi
+
+ # Only build a position-dependent object if we build old libraries.
+ if test yes = "$build_old_libs"; then
+ if test yes != "$pic_mode"; then
+ # Don't build PIC code
+ command="$base_compile $qsrcfile$pie_flag"
+ else
+ command="$base_compile $qsrcfile $pic_flag"
+ fi
+ if test yes = "$compiler_c_o"; then
+ func_append command " -o $obj"
+ fi
+
+ # Suppress compiler output if we already did a PIC compilation.
+ func_append command "$suppress_output"
+ func_show_eval_locale "$command" \
+ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE'
+
+ if test warn = "$need_locks" &&
+ test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+ $ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support '-c' and '-o' together. If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+ $opt_dry_run || $RM $removelist
+ exit $EXIT_FAILURE
+ fi
+
+ # Just move the object if needed
+ if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then
+ func_show_eval '$MV "$output_obj" "$obj"' \
+ 'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+ fi
+ fi
+
+ $opt_dry_run || {
+ func_write_libtool_object "$libobj" "$objdir/$objname" "$objname"
+
+ # Unlock the critical section if it was locked
+ if test no != "$need_locks"; then
+ removelist=$lockfile
+ $RM "$lockfile"
+ fi
+ }
+
+ exit $EXIT_SUCCESS
+}
+
+$opt_help || {
+ test compile = "$opt_mode" && func_mode_compile ${1+"$@"}
+}
+
+func_mode_help ()
+{
+ # We need to display help for each of the modes.
+ case $opt_mode in
+ "")
+ # Generic help is extracted from the usage comments
+ # at the start of this file.
+ func_help
+ ;;
+
+ clean)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE...
+
+Remove files from the build directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed
+to RM.
+
+If FILE is a libtool library, object or program, all the files associated
+with it are deleted. Otherwise, only FILE itself is deleted using RM."
+ ;;
+
+ compile)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE
+
+Compile a source file into a libtool library object.
+
+This mode accepts the following additional options:
+
+ -o OUTPUT-FILE set the output file name to OUTPUT-FILE
+ -no-suppress do not suppress compiler output for multiple passes
+ -prefer-pic try to build PIC objects only
+ -prefer-non-pic try to build non-PIC objects only
+ -shared do not build a '.o' file suitable for static linking
+ -static only build a '.o' file suitable for static linking
+ -Wc,FLAG pass FLAG directly to the compiler
+
+COMPILE-COMMAND is a command to be used in creating a 'standard' object file
+from the given SOURCEFILE.
+
+The output file name is determined by removing the directory component from
+SOURCEFILE, then substituting the C source code suffix '.c' with the
+library object suffix, '.lo'."
+ ;;
+
+ execute)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]...
+
+Automatically set library path, then run a program.
+
+This mode accepts the following additional options:
+
+ -dlopen FILE add the directory containing FILE to the library path
+
+This mode sets the library path environment variable according to '-dlopen'
+flags.
+
+If any of the ARGS are libtool executable wrappers, then they are translated
+into their corresponding uninstalled binary, and any of their required library
+directories are added to the library path.
+
+Then, COMMAND is executed, with ARGS as arguments."
+ ;;
+
+ finish)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=finish [LIBDIR]...
+
+Complete the installation of libtool libraries.
+
+Each LIBDIR is a directory that contains libtool libraries.
+
+The commands that this mode executes may require superuser privileges. Use
+the '--dry-run' option if you just want to see what would be executed."
+ ;;
+
+ install)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND...
+
+Install executables or libraries.
+
+INSTALL-COMMAND is the installation command. The first component should be
+either the 'install' or 'cp' program.
+
+The following components of INSTALL-COMMAND are treated specially:
+
+ -inst-prefix-dir PREFIX-DIR Use PREFIX-DIR as a staging area for installation
+
+The rest of the components are interpreted as arguments to that command (only
+BSD-compatible install options are recognized)."
+ ;;
+
+ link)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=link LINK-COMMAND...
+
+Link object files or libraries together to form another library, or to
+create an executable program.
+
+LINK-COMMAND is a command using the C compiler that you would use to create
+a program from several object files.
+
+The following components of LINK-COMMAND are treated specially:
+
+ -all-static do not do any dynamic linking at all
+ -avoid-version do not add a version suffix if possible
+ -bindir BINDIR specify path to binaries directory (for systems where
+ libraries must be found in the PATH setting at runtime)
+ -dlopen FILE '-dlpreopen' FILE if it cannot be dlopened at runtime
+ -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols
+ -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3)
+ -export-symbols SYMFILE
+ try to export only the symbols listed in SYMFILE
+ -export-symbols-regex REGEX
+ try to export only the symbols matching REGEX
+ -LLIBDIR search LIBDIR for required installed libraries
+ -lNAME OUTPUT-FILE requires the installed library libNAME
+ -module build a library that can dlopened
+ -no-fast-install disable the fast-install mode
+ -no-install link a not-installable executable
+ -no-undefined declare that a library does not refer to external symbols
+ -o OUTPUT-FILE create OUTPUT-FILE from the specified objects
+ -objectlist FILE use a list of object files found in FILE to specify objects
+ -os2dllname NAME force a short DLL name on OS/2 (no effect on other OSes)
+ -precious-files-regex REGEX
+ don't remove output files matching REGEX
+ -release RELEASE specify package release information
+ -rpath LIBDIR the created library will eventually be installed in LIBDIR
+ -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries
+ -shared only do dynamic linking of libtool libraries
+ -shrext SUFFIX override the standard shared library file extension
+ -static do not do any dynamic linking of uninstalled libtool libraries
+ -static-libtool-libs
+ do not do any dynamic linking of libtool libraries
+ -version-info CURRENT[:REVISION[:AGE]]
+ specify library version info [each variable defaults to 0]
+ -weak LIBNAME declare that the target provides the LIBNAME interface
+ -Wc,FLAG
+ -Xcompiler FLAG pass linker-specific FLAG directly to the compiler
+ -Wl,FLAG
+ -Xlinker FLAG pass linker-specific FLAG directly to the linker
+ -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC)
+
+All other options (arguments beginning with '-') are ignored.
+
+Every other argument is treated as a filename. Files ending in '.la' are
+treated as uninstalled libtool libraries, other files are standard or library
+object files.
+
+If the OUTPUT-FILE ends in '.la', then a libtool library is created,
+only library objects ('.lo' files) may be specified, and '-rpath' is
+required, except when creating a convenience library.
+
+If OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created
+using 'ar' and 'ranlib', or on Windows using 'lib'.
+
+If OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file
+is created, otherwise an executable program is created."
+ ;;
+
+ uninstall)
+ $ECHO \
+"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE...
+
+Remove libraries from an installation directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed
+to RM.
+
+If FILE is a libtool library, all the files associated with it are deleted.
+Otherwise, only FILE itself is deleted using RM."
+ ;;
+
+ *)
+ func_fatal_help "invalid operation mode '$opt_mode'"
+ ;;
+ esac
+
+ echo
+ $ECHO "Try '$progname --help' for more information about other modes."
+}
+
+# Now that we've collected a possible --mode arg, show help if necessary
+if $opt_help; then
+ if test : = "$opt_help"; then
+ func_mode_help
+ else
+ {
+ func_help noexit
+ for opt_mode in compile link execute install finish uninstall clean; do
+ func_mode_help
+ done
+ } | $SED -n '1p; 2,$s/^Usage:/ or: /p'
+ {
+ func_help noexit
+ for opt_mode in compile link execute install finish uninstall clean; do
+ echo
+ func_mode_help
+ done
+ } |
+ $SED '1d
+ /^When reporting/,/^Report/{
+ H
+ d
+ }
+ $x
+ /information about other modes/d
+ /more detailed .*MODE/d
+ s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/'
+ fi
+ exit $?
+fi
+
+
+# func_mode_execute arg...
+func_mode_execute ()
+{
+ $debug_cmd
+
+ # The first argument is the command name.
+ cmd=$nonopt
+ test -z "$cmd" && \
+ func_fatal_help "you must specify a COMMAND"
+
+ # Handle -dlopen flags immediately.
+ for file in $opt_dlopen; do
+ test -f "$file" \
+ || func_fatal_help "'$file' is not a file"
+
+ dir=
+ case $file in
+ *.la)
+ func_resolve_sysroot "$file"
+ file=$func_resolve_sysroot_result
+
+ # Check to see that this really is a libtool archive.
+ func_lalib_unsafe_p "$file" \
+ || func_fatal_help "'$lib' is not a valid libtool archive"
+
+ # Read the libtool library.
+ dlname=
+ library_names=
+ func_source "$file"
+
+ # Skip this library if it cannot be dlopened.
+ if test -z "$dlname"; then
+ # Warn if it was a shared library.
+ test -n "$library_names" && \
+ func_warning "'$file' was not linked with '-export-dynamic'"
+ continue
+ fi
+
+ func_dirname "$file" "" "."
+ dir=$func_dirname_result
+
+ if test -f "$dir/$objdir/$dlname"; then
+ func_append dir "/$objdir"
+ else
+ if test ! -f "$dir/$dlname"; then
+ func_fatal_error "cannot find '$dlname' in '$dir' or '$dir/$objdir'"
+ fi
+ fi
+ ;;
+
+ *.lo)
+ # Just add the directory containing the .lo file.
+ func_dirname "$file" "" "."
+ dir=$func_dirname_result
+ ;;
+
+ *)
+ func_warning "'-dlopen' is ignored for non-libtool libraries and objects"
+ continue
+ ;;
+ esac
+
+ # Get the absolute pathname.
+ absdir=`cd "$dir" && pwd`
+ test -n "$absdir" && dir=$absdir
+
+ # Now add the directory to shlibpath_var.
+ if eval "test -z \"\$$shlibpath_var\""; then
+ eval "$shlibpath_var=\"\$dir\""
+ else
+ eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\""
+ fi
+ done
+
+ # This variable tells wrapper scripts just to set shlibpath_var
+ # rather than running their programs.
+ libtool_execute_magic=$magic
+
+ # Check if any of the arguments is a wrapper script.
+ args=
+ for file
+ do
+ case $file in
+ -* | *.la | *.lo ) ;;
+ *)
+ # Do a test to see if this is really a libtool program.
+ if func_ltwrapper_script_p "$file"; then
+ func_source "$file"
+ # Transform arg to wrapped name.
+ file=$progdir/$program
+ elif func_ltwrapper_executable_p "$file"; then
+ func_ltwrapper_scriptname "$file"
+ func_source "$func_ltwrapper_scriptname_result"
+ # Transform arg to wrapped name.
+ file=$progdir/$program
+ fi
+ ;;
+ esac
+ # Quote arguments (to preserve shell metacharacters).
+ func_append_quoted args "$file"
+ done
+
+ if $opt_dry_run; then
+ # Display what would be done.
+ if test -n "$shlibpath_var"; then
+ eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\""
+ echo "export $shlibpath_var"
+ fi
+ $ECHO "$cmd$args"
+ exit $EXIT_SUCCESS
+ else
+ if test -n "$shlibpath_var"; then
+ # Export the shlibpath_var.
+ eval "export $shlibpath_var"
+ fi
+
+ # Restore saved environment variables
+ for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+ do
+ eval "if test \"\${save_$lt_var+set}\" = set; then
+ $lt_var=\$save_$lt_var; export $lt_var
+ else
+ $lt_unset $lt_var
+ fi"
+ done
+
+ # Now prepare to actually exec the command.
+ exec_cmd=\$cmd$args
+ fi
+}
+
+test execute = "$opt_mode" && func_mode_execute ${1+"$@"}
+
+
+# func_mode_finish arg...
+func_mode_finish ()
+{
+ $debug_cmd
+
+ libs=
+ libdirs=
+ admincmds=
+
+ for opt in "$nonopt" ${1+"$@"}
+ do
+ if test -d "$opt"; then
+ func_append libdirs " $opt"
+
+ elif test -f "$opt"; then
+ if func_lalib_unsafe_p "$opt"; then
+ func_append libs " $opt"
+ else
+ func_warning "'$opt' is not a valid libtool archive"
+ fi
+
+ else
+ func_fatal_error "invalid argument '$opt'"
+ fi
+ done
+
+ if test -n "$libs"; then
+ if test -n "$lt_sysroot"; then
+ sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"`
+ sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;"
+ else
+ sysroot_cmd=
+ fi
+
+ # Remove sysroot references
+ if $opt_dry_run; then
+ for lib in $libs; do
+ echo "removing references to $lt_sysroot and '=' prefixes from $lib"
+ done
+ else
+ tmpdir=`func_mktempdir`
+ for lib in $libs; do
+ $SED -e "$sysroot_cmd s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \
+ > $tmpdir/tmp-la
+ mv -f $tmpdir/tmp-la $lib
+ done
+ ${RM}r "$tmpdir"
+ fi
+ fi
+
+ if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+ for libdir in $libdirs; do
+ if test -n "$finish_cmds"; then
+ # Do each command in the finish commands.
+ func_execute_cmds "$finish_cmds" 'admincmds="$admincmds
+'"$cmd"'"'
+ fi
+ if test -n "$finish_eval"; then
+ # Do the single finish_eval.
+ eval cmds=\"$finish_eval\"
+ $opt_dry_run || eval "$cmds" || func_append admincmds "
+ $cmds"
+ fi
+ done
+ fi
+
+ # Exit here if they wanted silent mode.
+ $opt_quiet && exit $EXIT_SUCCESS
+
+ if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+ echo "----------------------------------------------------------------------"
+ echo "Libraries have been installed in:"
+ for libdir in $libdirs; do
+ $ECHO " $libdir"
+ done
+ echo
+ echo "If you ever happen to want to link against installed libraries"
+ echo "in a given directory, LIBDIR, you must either use libtool, and"
+ echo "specify the full pathname of the library, or use the '-LLIBDIR'"
+ echo "flag during linking and do at least one of the following:"
+ if test -n "$shlibpath_var"; then
+ echo " - add LIBDIR to the '$shlibpath_var' environment variable"
+ echo " during execution"
+ fi
+ if test -n "$runpath_var"; then
+ echo " - add LIBDIR to the '$runpath_var' environment variable"
+ echo " during linking"
+ fi
+ if test -n "$hardcode_libdir_flag_spec"; then
+ libdir=LIBDIR
+ eval flag=\"$hardcode_libdir_flag_spec\"
+
+ $ECHO " - use the '$flag' linker flag"
+ fi
+ if test -n "$admincmds"; then
+ $ECHO " - have your system administrator run these commands:$admincmds"
+ fi
+ if test -f /etc/ld.so.conf; then
+ echo " - have your system administrator add LIBDIR to '/etc/ld.so.conf'"
+ fi
+ echo
+
+ echo "See any operating system documentation about shared libraries for"
+ case $host in
+ solaris2.[6789]|solaris2.1[0-9])
+ echo "more information, such as the ld(1), crle(1) and ld.so(8) manual"
+ echo "pages."
+ ;;
+ *)
+ echo "more information, such as the ld(1) and ld.so(8) manual pages."
+ ;;
+ esac
+ echo "----------------------------------------------------------------------"
+ fi
+ exit $EXIT_SUCCESS
+}
+
+test finish = "$opt_mode" && func_mode_finish ${1+"$@"}
+
+
+# func_mode_install arg...
+func_mode_install ()
+{
+ $debug_cmd
+
+ # There may be an optional sh(1) argument at the beginning of
+ # install_prog (especially on Windows NT).
+ if test "$SHELL" = "$nonopt" || test /bin/sh = "$nonopt" ||
+ # Allow the use of GNU shtool's install command.
+ case $nonopt in *shtool*) :;; *) false;; esac
+ then
+ # Aesthetically quote it.
+ func_quote_for_eval "$nonopt"
+ install_prog="$func_quote_for_eval_result "
+ arg=$1
+ shift
+ else
+ install_prog=
+ arg=$nonopt
+ fi
+
+ # The real first argument should be the name of the installation program.
+ # Aesthetically quote it.
+ func_quote_for_eval "$arg"
+ func_append install_prog "$func_quote_for_eval_result"
+ install_shared_prog=$install_prog
+ case " $install_prog " in
+ *[\\\ /]cp\ *) install_cp=: ;;
+ *) install_cp=false ;;
+ esac
+
+ # We need to accept at least all the BSD install flags.
+ dest=
+ files=
+ opts=
+ prev=
+ install_type=
+ isdir=false
+ stripme=
+ no_mode=:
+ for arg
+ do
+ arg2=
+ if test -n "$dest"; then
+ func_append files " $dest"
+ dest=$arg
+ continue
+ fi
+
+ case $arg in
+ -d) isdir=: ;;
+ -f)
+ if $install_cp; then :; else
+ prev=$arg
+ fi
+ ;;
+ -g | -m | -o)
+ prev=$arg
+ ;;
+ -s)
+ stripme=" -s"
+ continue
+ ;;
+ -*)
+ ;;
+ *)
+ # If the previous option needed an argument, then skip it.
+ if test -n "$prev"; then
+ if test X-m = "X$prev" && test -n "$install_override_mode"; then
+ arg2=$install_override_mode
+ no_mode=false
+ fi
+ prev=
+ else
+ dest=$arg
+ continue
+ fi
+ ;;
+ esac
+
+ # Aesthetically quote the argument.
+ func_quote_for_eval "$arg"
+ func_append install_prog " $func_quote_for_eval_result"
+ if test -n "$arg2"; then
+ func_quote_for_eval "$arg2"
+ fi
+ func_append install_shared_prog " $func_quote_for_eval_result"
+ done
+
+ test -z "$install_prog" && \
+ func_fatal_help "you must specify an install program"
+
+ test -n "$prev" && \
+ func_fatal_help "the '$prev' option requires an argument"
+
+ if test -n "$install_override_mode" && $no_mode; then
+ if $install_cp; then :; else
+ func_quote_for_eval "$install_override_mode"
+ func_append install_shared_prog " -m $func_quote_for_eval_result"
+ fi
+ fi
+
+ if test -z "$files"; then
+ if test -z "$dest"; then
+ func_fatal_help "no file or destination specified"
+ else
+ func_fatal_help "you must specify a destination"
+ fi
+ fi
+
+ # Strip any trailing slash from the destination.
+ func_stripname '' '/' "$dest"
+ dest=$func_stripname_result
+
+ # Check to see that the destination is a directory.
+ test -d "$dest" && isdir=:
+ if $isdir; then
+ destdir=$dest
+ destname=
+ else
+ func_dirname_and_basename "$dest" "" "."
+ destdir=$func_dirname_result
+ destname=$func_basename_result
+
+ # Not a directory, so check to see that there is only one file specified.
+ set dummy $files; shift
+ test "$#" -gt 1 && \
+ func_fatal_help "'$dest' is not a directory"
+ fi
+ case $destdir in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ for file in $files; do
+ case $file in
+ *.lo) ;;
+ *)
+ func_fatal_help "'$destdir' must be an absolute directory name"
+ ;;
+ esac
+ done
+ ;;
+ esac
+
+ # This variable tells wrapper scripts just to set variables rather
+ # than running their programs.
+ libtool_install_magic=$magic
+
+ staticlibs=
+ future_libdirs=
+ current_libdirs=
+ for file in $files; do
+
+ # Do each installation.
+ case $file in
+ *.$libext)
+ # Do the static libraries later.
+ func_append staticlibs " $file"
+ ;;
+
+ *.la)
+ func_resolve_sysroot "$file"
+ file=$func_resolve_sysroot_result
+
+ # Check to see that this really is a libtool archive.
+ func_lalib_unsafe_p "$file" \
+ || func_fatal_help "'$file' is not a valid libtool archive"
+
+ library_names=
+ old_library=
+ relink_command=
+ func_source "$file"
+
+ # Add the libdir to current_libdirs if it is the destination.
+ if test "X$destdir" = "X$libdir"; then
+ case "$current_libdirs " in
+ *" $libdir "*) ;;
+ *) func_append current_libdirs " $libdir" ;;
+ esac
+ else
+ # Note the libdir as a future libdir.
+ case "$future_libdirs " in
+ *" $libdir "*) ;;
+ *) func_append future_libdirs " $libdir" ;;
+ esac
+ fi
+
+ func_dirname "$file" "/" ""
+ dir=$func_dirname_result
+ func_append dir "$objdir"
+
+ if test -n "$relink_command"; then
+ # Determine the prefix the user has applied to our future dir.
+ inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"`
+
+ # Don't allow the user to place us outside of our expected
+ # location b/c this prevents finding dependent libraries that
+ # are installed to the same prefix.
+ # At present, this check doesn't affect windows .dll's that
+ # are installed into $libdir/../bin (currently, that works fine)
+ # but it's something to keep an eye on.
+ test "$inst_prefix_dir" = "$destdir" && \
+ func_fatal_error "error: cannot install '$file' to a directory not ending in $libdir"
+
+ if test -n "$inst_prefix_dir"; then
+ # Stick the inst_prefix_dir data into the link command.
+ relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"`
+ else
+ relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"`
+ fi
+
+ func_warning "relinking '$file'"
+ func_show_eval "$relink_command" \
+ 'func_fatal_error "error: relink '\''$file'\'' with the above command before installing it"'
+ fi
+
+ # See the names of the shared library.
+ set dummy $library_names; shift
+ if test -n "$1"; then
+ realname=$1
+ shift
+
+ srcname=$realname
+ test -n "$relink_command" && srcname=${realname}T
+
+ # Install the shared library and build the symlinks.
+ func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \
+ 'exit $?'
+ tstripme=$stripme
+ case $host_os in
+ cygwin* | mingw* | pw32* | cegcc*)
+ case $realname in
+ *.dll.a)
+ tstripme=
+ ;;
+ esac
+ ;;
+ os2*)
+ case $realname in
+ *_dll.a)
+ tstripme=
+ ;;
+ esac
+ ;;
+ esac
+ if test -n "$tstripme" && test -n "$striplib"; then
+ func_show_eval "$striplib $destdir/$realname" 'exit $?'
+ fi
+
+ if test "$#" -gt 0; then
+ # Delete the old symlinks, and create new ones.
+ # Try 'ln -sf' first, because the 'ln' binary might depend on
+ # the symlink we replace! Solaris /bin/ln does not understand -f,
+ # so we also need to try rm && ln -s.
+ for linkname
+ do
+ test "$linkname" != "$realname" \
+ && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })"
+ done
+ fi
+
+ # Do each command in the postinstall commands.
+ lib=$destdir/$realname
+ func_execute_cmds "$postinstall_cmds" 'exit $?'
+ fi
+
+ # Install the pseudo-library for information purposes.
+ func_basename "$file"
+ name=$func_basename_result
+ instname=$dir/${name}i
+ func_show_eval "$install_prog $instname $destdir/$name" 'exit $?'
+
+ # Maybe install the static library, too.
+ test -n "$old_library" && func_append staticlibs " $dir/$old_library"
+ ;;
+
+ *.lo)
+ # Install (i.e. copy) a libtool object.
+
+ # Figure out destination file name, if it wasn't already specified.
+ if test -n "$destname"; then
+ destfile=$destdir/$destname
+ else
+ func_basename "$file"
+ destfile=$func_basename_result
+ destfile=$destdir/$destfile
+ fi
+
+ # Deduce the name of the destination old-style object file.
+ case $destfile in
+ *.lo)
+ func_lo2o "$destfile"
+ staticdest=$func_lo2o_result
+ ;;
+ *.$objext)
+ staticdest=$destfile
+ destfile=
+ ;;
+ *)
+ func_fatal_help "cannot copy a libtool object to '$destfile'"
+ ;;
+ esac
+
+ # Install the libtool object if requested.
+ test -n "$destfile" && \
+ func_show_eval "$install_prog $file $destfile" 'exit $?'
+
+ # Install the old object if enabled.
+ if test yes = "$build_old_libs"; then
+ # Deduce the name of the old-style object file.
+ func_lo2o "$file"
+ staticobj=$func_lo2o_result
+ func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?'
+ fi
+ exit $EXIT_SUCCESS
+ ;;
+
+ *)
+ # Figure out destination file name, if it wasn't already specified.
+ if test -n "$destname"; then
+ destfile=$destdir/$destname
+ else
+ func_basename "$file"
+ destfile=$func_basename_result
+ destfile=$destdir/$destfile
+ fi
+
+ # If the file is missing, and there is a .exe on the end, strip it
+ # because it is most likely a libtool script we actually want to
+ # install
+ stripped_ext=
+ case $file in
+ *.exe)
+ if test ! -f "$file"; then
+ func_stripname '' '.exe' "$file"
+ file=$func_stripname_result
+ stripped_ext=.exe
+ fi
+ ;;
+ esac
+
+ # Do a test to see if this is really a libtool program.
+ case $host in
+ *cygwin* | *mingw*)
+ if func_ltwrapper_executable_p "$file"; then
+ func_ltwrapper_scriptname "$file"
+ wrapper=$func_ltwrapper_scriptname_result
+ else
+ func_stripname '' '.exe' "$file"
+ wrapper=$func_stripname_result
+ fi
+ ;;
+ *)
+ wrapper=$file
+ ;;
+ esac
+ if func_ltwrapper_script_p "$wrapper"; then
+ notinst_deplibs=
+ relink_command=
+
+ func_source "$wrapper"
+
+ # Check the variables that should have been set.
+ test -z "$generated_by_libtool_version" && \
+ func_fatal_error "invalid libtool wrapper script '$wrapper'"
+
+ finalize=:
+ for lib in $notinst_deplibs; do
+ # Check to see that each library is installed.
+ libdir=
+ if test -f "$lib"; then
+ func_source "$lib"
+ fi
+ libfile=$libdir/`$ECHO "$lib" | $SED 's%^.*/%%g'`
+ if test -n "$libdir" && test ! -f "$libfile"; then
+ func_warning "'$lib' has not been installed in '$libdir'"
+ finalize=false
+ fi
+ done
+
+ relink_command=
+ func_source "$wrapper"
+
+ outputname=
+ if test no = "$fast_install" && test -n "$relink_command"; then
+ $opt_dry_run || {
+ if $finalize; then
+ tmpdir=`func_mktempdir`
+ func_basename "$file$stripped_ext"
+ file=$func_basename_result
+ outputname=$tmpdir/$file
+ # Replace the output file specification.
+ relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'`
+
+ $opt_quiet || {
+ func_quote_for_expand "$relink_command"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+ if eval "$relink_command"; then :
+ else
+ func_error "error: relink '$file' with the above command before installing it"
+ $opt_dry_run || ${RM}r "$tmpdir"
+ continue
+ fi
+ file=$outputname
+ else
+ func_warning "cannot relink '$file'"
+ fi
+ }
+ else
+ # Install the binary that we compiled earlier.
+ file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"`
+ fi
+ fi
+
+ # remove .exe since cygwin /usr/bin/install will append another
+ # one anyway
+ case $install_prog,$host in
+ */usr/bin/install*,*cygwin*)
+ case $file:$destfile in
+ *.exe:*.exe)
+ # this is ok
+ ;;
+ *.exe:*)
+ destfile=$destfile.exe
+ ;;
+ *:*.exe)
+ func_stripname '' '.exe' "$destfile"
+ destfile=$func_stripname_result
+ ;;
+ esac
+ ;;
+ esac
+ func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?'
+ $opt_dry_run || if test -n "$outputname"; then
+ ${RM}r "$tmpdir"
+ fi
+ ;;
+ esac
+ done
+
+ for file in $staticlibs; do
+ func_basename "$file"
+ name=$func_basename_result
+
+ # Set up the ranlib parameters.
+ oldlib=$destdir/$name
+ func_to_tool_file "$oldlib" func_convert_file_msys_to_w32
+ tool_oldlib=$func_to_tool_file_result
+
+ func_show_eval "$install_prog \$file \$oldlib" 'exit $?'
+
+ if test -n "$stripme" && test -n "$old_striplib"; then
+ func_show_eval "$old_striplib $tool_oldlib" 'exit $?'
+ fi
+
+ # Do each command in the postinstall commands.
+ func_execute_cmds "$old_postinstall_cmds" 'exit $?'
+ done
+
+ test -n "$future_libdirs" && \
+ func_warning "remember to run '$progname --finish$future_libdirs'"
+
+ if test -n "$current_libdirs"; then
+ # Maybe just do a dry run.
+ $opt_dry_run && current_libdirs=" -n$current_libdirs"
+ exec_cmd='$SHELL "$progpath" $preserve_args --finish$current_libdirs'
+ else
+ exit $EXIT_SUCCESS
+ fi
+}
+
+test install = "$opt_mode" && func_mode_install ${1+"$@"}
+
+
+# func_generate_dlsyms outputname originator pic_p
+# Extract symbols from dlprefiles and create ${outputname}S.o with
+# a dlpreopen symbol table.
+func_generate_dlsyms ()
+{
+ $debug_cmd
+
+ my_outputname=$1
+ my_originator=$2
+ my_pic_p=${3-false}
+ my_prefix=`$ECHO "$my_originator" | $SED 's%[^a-zA-Z0-9]%_%g'`
+ my_dlsyms=
+
+ if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then
+ if test -n "$NM" && test -n "$global_symbol_pipe"; then
+ my_dlsyms=${my_outputname}S.c
+ else
+ func_error "not configured to extract global symbols from dlpreopened files"
+ fi
+ fi
+
+ if test -n "$my_dlsyms"; then
+ case $my_dlsyms in
+ "") ;;
+ *.c)
+ # Discover the nlist of each of the dlfiles.
+ nlist=$output_objdir/$my_outputname.nm
+
+ func_show_eval "$RM $nlist ${nlist}S ${nlist}T"
+
+ # Parse the name list into a source file.
+ func_verbose "creating $output_objdir/$my_dlsyms"
+
+ $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\
+/* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */
+/* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */
+
+#ifdef __cplusplus
+extern \"C\" {
+#endif
+
+#if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4))
+#pragma GCC diagnostic ignored \"-Wstrict-prototypes\"
+#endif
+
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */
+#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE
+/* DATA imports from DLLs on WIN32 can't be const, because runtime
+ relocations are performed -- see ld's documentation on pseudo-relocs. */
+# define LT_DLSYM_CONST
+#elif defined __osf__
+/* This system does not cope well with relocations in const data. */
+# define LT_DLSYM_CONST
+#else
+# define LT_DLSYM_CONST const
+#endif
+
+#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0)
+
+/* External symbol declarations for the compiler. */\
+"
+
+ if test yes = "$dlself"; then
+ func_verbose "generating symbol list for '$output'"
+
+ $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist"
+
+ # Add our own program objects to the symbol list.
+ progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP`
+ for progfile in $progfiles; do
+ func_to_tool_file "$progfile" func_convert_file_msys_to_w32
+ func_verbose "extracting global C symbols from '$func_to_tool_file_result'"
+ $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'"
+ done
+
+ if test -n "$exclude_expsyms"; then
+ $opt_dry_run || {
+ eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T'
+ eval '$MV "$nlist"T "$nlist"'
+ }
+ fi
+
+ if test -n "$export_symbols_regex"; then
+ $opt_dry_run || {
+ eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T'
+ eval '$MV "$nlist"T "$nlist"'
+ }
+ fi
+
+ # Prepare the list of exported symbols
+ if test -z "$export_symbols"; then
+ export_symbols=$output_objdir/$outputname.exp
+ $opt_dry_run || {
+ $RM $export_symbols
+ eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"'
+ case $host in
+ *cygwin* | *mingw* | *cegcc* )
+ eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+ eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"'
+ ;;
+ esac
+ }
+ else
+ $opt_dry_run || {
+ eval "$SED -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"'
+ eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T'
+ eval '$MV "$nlist"T "$nlist"'
+ case $host in
+ *cygwin* | *mingw* | *cegcc* )
+ eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+ eval 'cat "$nlist" >> "$output_objdir/$outputname.def"'
+ ;;
+ esac
+ }
+ fi
+ fi
+
+ for dlprefile in $dlprefiles; do
+ func_verbose "extracting global C symbols from '$dlprefile'"
+ func_basename "$dlprefile"
+ name=$func_basename_result
+ case $host in
+ *cygwin* | *mingw* | *cegcc* )
+ # if an import library, we need to obtain dlname
+ if func_win32_import_lib_p "$dlprefile"; then
+ func_tr_sh "$dlprefile"
+ eval "curr_lafile=\$libfile_$func_tr_sh_result"
+ dlprefile_dlbasename=
+ if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then
+ # Use subshell, to avoid clobbering current variable values
+ dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"`
+ if test -n "$dlprefile_dlname"; then
+ func_basename "$dlprefile_dlname"
+ dlprefile_dlbasename=$func_basename_result
+ else
+ # no lafile. user explicitly requested -dlpreopen <import library>.
+ $sharedlib_from_linklib_cmd "$dlprefile"
+ dlprefile_dlbasename=$sharedlib_from_linklib_result
+ fi
+ fi
+ $opt_dry_run || {
+ if test -n "$dlprefile_dlbasename"; then
+ eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"'
+ else
+ func_warning "Could not compute DLL name from $name"
+ eval '$ECHO ": $name " >> "$nlist"'
+ fi
+ func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+ eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe |
+ $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'"
+ }
+ else # not an import lib
+ $opt_dry_run || {
+ eval '$ECHO ": $name " >> "$nlist"'
+ func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+ eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'"
+ }
+ fi
+ ;;
+ *)
+ $opt_dry_run || {
+ eval '$ECHO ": $name " >> "$nlist"'
+ func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+ eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'"
+ }
+ ;;
+ esac
+ done
+
+ $opt_dry_run || {
+ # Make sure we have at least an empty file.
+ test -f "$nlist" || : > "$nlist"
+
+ if test -n "$exclude_expsyms"; then
+ $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T
+ $MV "$nlist"T "$nlist"
+ fi
+
+ # Try sorting and uniquifying the output.
+ if $GREP -v "^: " < "$nlist" |
+ if sort -k 3 </dev/null >/dev/null 2>&1; then
+ sort -k 3
+ else
+ sort +2
+ fi |
+ uniq > "$nlist"S; then
+ :
+ else
+ $GREP -v "^: " < "$nlist" > "$nlist"S
+ fi
+
+ if test -f "$nlist"S; then
+ eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"'
+ else
+ echo '/* NONE */' >> "$output_objdir/$my_dlsyms"
+ fi
+
+ func_show_eval '$RM "${nlist}I"'
+ if test -n "$global_symbol_to_import"; then
+ eval "$global_symbol_to_import"' < "$nlist"S > "$nlist"I'
+ fi
+
+ echo >> "$output_objdir/$my_dlsyms" "\
+
+/* The mapping between symbol names and symbols. */
+typedef struct {
+ const char *name;
+ void *address;
+} lt_dlsymlist;
+extern LT_DLSYM_CONST lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[];\
+"
+
+ if test -s "$nlist"I; then
+ echo >> "$output_objdir/$my_dlsyms" "\
+static void lt_syminit(void)
+{
+ LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols;
+ for (; symbol->name; ++symbol)
+ {"
+ $SED 's/.*/ if (STREQ (symbol->name, \"&\")) symbol->address = (void *) \&&;/' < "$nlist"I >> "$output_objdir/$my_dlsyms"
+ echo >> "$output_objdir/$my_dlsyms" "\
+ }
+}"
+ fi
+ echo >> "$output_objdir/$my_dlsyms" "\
+LT_DLSYM_CONST lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[] =
+{ {\"$my_originator\", (void *) 0},"
+
+ if test -s "$nlist"I; then
+ echo >> "$output_objdir/$my_dlsyms" "\
+ {\"@INIT@\", (void *) &lt_syminit},"
+ fi
+
+ case $need_lib_prefix in
+ no)
+ eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms"
+ ;;
+ *)
+ eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms"
+ ;;
+ esac
+ echo >> "$output_objdir/$my_dlsyms" "\
+ {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+ return lt_${my_prefix}_LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif\
+"
+ } # !$opt_dry_run
+
+ pic_flag_for_symtable=
+ case "$compile_command " in
+ *" -static "*) ;;
+ *)
+ case $host in
+ # compiling the symbol table file with pic_flag works around
+ # a FreeBSD bug that causes programs to crash when -lm is
+ # linked before any other PIC object. But we must not use
+ # pic_flag when linking with -static. The problem exists in
+ # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1.
+ *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*)
+ pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;;
+ *-*-hpux*)
+ pic_flag_for_symtable=" $pic_flag" ;;
+ *)
+ $my_pic_p && pic_flag_for_symtable=" $pic_flag"
+ ;;
+ esac
+ ;;
+ esac
+ symtab_cflags=
+ for arg in $LTCFLAGS; do
+ case $arg in
+ -pie | -fpie | -fPIE) ;;
+ *) func_append symtab_cflags " $arg" ;;
+ esac
+ done
+
+ # Now compile the dynamic symbol file.
+ func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?'
+
+ # Clean up the generated files.
+ func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T" "${nlist}I"'
+
+ # Transform the symbol file into the correct name.
+ symfileobj=$output_objdir/${my_outputname}S.$objext
+ case $host in
+ *cygwin* | *mingw* | *cegcc* )
+ if test -f "$output_objdir/$my_outputname.def"; then
+ compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+ finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+ else
+ compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+ finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+ fi
+ ;;
+ *)
+ compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+ finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+ ;;
+ esac
+ ;;
+ *)
+ func_fatal_error "unknown suffix for '$my_dlsyms'"
+ ;;
+ esac
+ else
+ # We keep going just in case the user didn't refer to
+ # lt_preloaded_symbols. The linker will fail if global_symbol_pipe
+ # really was required.
+
+ # Nullify the symbol file.
+ compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"`
+ finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"`
+ fi
+}
+
+# func_cygming_gnu_implib_p ARG
+# This predicate returns with zero status (TRUE) if
+# ARG is a GNU/binutils-style import library. Returns
+# with nonzero status (FALSE) otherwise.
+func_cygming_gnu_implib_p ()
+{
+ $debug_cmd
+
+ func_to_tool_file "$1" func_convert_file_msys_to_w32
+ func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'`
+ test -n "$func_cygming_gnu_implib_tmp"
+}
+
+# func_cygming_ms_implib_p ARG
+# This predicate returns with zero status (TRUE) if
+# ARG is an MS-style import library. Returns
+# with nonzero status (FALSE) otherwise.
+func_cygming_ms_implib_p ()
+{
+ $debug_cmd
+
+ func_to_tool_file "$1" func_convert_file_msys_to_w32
+ func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'`
+ test -n "$func_cygming_ms_implib_tmp"
+}
+
+# func_win32_libid arg
+# return the library type of file 'arg'
+#
+# Need a lot of goo to handle *both* DLLs and import libs
+# Has to be a shell function in order to 'eat' the argument
+# that is supplied when $file_magic_command is called.
+# Despite the name, also deal with 64 bit binaries.
+func_win32_libid ()
+{
+ $debug_cmd
+
+ win32_libid_type=unknown
+ win32_fileres=`file -L $1 2>/dev/null`
+ case $win32_fileres in
+ *ar\ archive\ import\ library*) # definitely import
+ win32_libid_type="x86 archive import"
+ ;;
+ *ar\ archive*) # could be an import, or static
+ # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD.
+ if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null |
+ $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then
+ case $nm_interface in
+ "MS dumpbin")
+ if func_cygming_ms_implib_p "$1" ||
+ func_cygming_gnu_implib_p "$1"
+ then
+ win32_nmres=import
+ else
+ win32_nmres=
+ fi
+ ;;
+ *)
+ func_to_tool_file "$1" func_convert_file_msys_to_w32
+ win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" |
+ $SED -n -e '
+ 1,100{
+ / I /{
+ s|.*|import|
+ p
+ q
+ }
+ }'`
+ ;;
+ esac
+ case $win32_nmres in
+ import*) win32_libid_type="x86 archive import";;
+ *) win32_libid_type="x86 archive static";;
+ esac
+ fi
+ ;;
+ *DLL*)
+ win32_libid_type="x86 DLL"
+ ;;
+ *executable*) # but shell scripts are "executable" too...
+ case $win32_fileres in
+ *MS\ Windows\ PE\ Intel*)
+ win32_libid_type="x86 DLL"
+ ;;
+ esac
+ ;;
+ esac
+ $ECHO "$win32_libid_type"
+}
+
+# func_cygming_dll_for_implib ARG
+#
+# Platform-specific function to extract the
+# name of the DLL associated with the specified
+# import library ARG.
+# Invoked by eval'ing the libtool variable
+# $sharedlib_from_linklib_cmd
+# Result is available in the variable
+# $sharedlib_from_linklib_result
+func_cygming_dll_for_implib ()
+{
+ $debug_cmd
+
+ sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"`
+}
+
+# func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs
+#
+# The is the core of a fallback implementation of a
+# platform-specific function to extract the name of the
+# DLL associated with the specified import library LIBNAME.
+#
+# SECTION_NAME is either .idata$6 or .idata$7, depending
+# on the platform and compiler that created the implib.
+#
+# Echos the name of the DLL associated with the
+# specified import library.
+func_cygming_dll_for_implib_fallback_core ()
+{
+ $debug_cmd
+
+ match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"`
+ $OBJDUMP -s --section "$1" "$2" 2>/dev/null |
+ $SED '/^Contents of section '"$match_literal"':/{
+ # Place marker at beginning of archive member dllname section
+ s/.*/====MARK====/
+ p
+ d
+ }
+ # These lines can sometimes be longer than 43 characters, but
+ # are always uninteresting
+ /:[ ]*file format pe[i]\{,1\}-/d
+ /^In archive [^:]*:/d
+ # Ensure marker is printed
+ /^====MARK====/p
+ # Remove all lines with less than 43 characters
+ /^.\{43\}/!d
+ # From remaining lines, remove first 43 characters
+ s/^.\{43\}//' |
+ $SED -n '
+ # Join marker and all lines until next marker into a single line
+ /^====MARK====/ b para
+ H
+ $ b para
+ b
+ :para
+ x
+ s/\n//g
+ # Remove the marker
+ s/^====MARK====//
+ # Remove trailing dots and whitespace
+ s/[\. \t]*$//
+ # Print
+ /./p' |
+ # we now have a list, one entry per line, of the stringified
+ # contents of the appropriate section of all members of the
+ # archive that possess that section. Heuristic: eliminate
+ # all those that have a first or second character that is
+ # a '.' (that is, objdump's representation of an unprintable
+ # character.) This should work for all archives with less than
+ # 0x302f exports -- but will fail for DLLs whose name actually
+ # begins with a literal '.' or a single character followed by
+ # a '.'.
+ #
+ # Of those that remain, print the first one.
+ $SED -e '/^\./d;/^.\./d;q'
+}
+
+# func_cygming_dll_for_implib_fallback ARG
+# Platform-specific function to extract the
+# name of the DLL associated with the specified
+# import library ARG.
+#
+# This fallback implementation is for use when $DLLTOOL
+# does not support the --identify-strict option.
+# Invoked by eval'ing the libtool variable
+# $sharedlib_from_linklib_cmd
+# Result is available in the variable
+# $sharedlib_from_linklib_result
+func_cygming_dll_for_implib_fallback ()
+{
+ $debug_cmd
+
+ if func_cygming_gnu_implib_p "$1"; then
+ # binutils import library
+ sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"`
+ elif func_cygming_ms_implib_p "$1"; then
+ # ms-generated import library
+ sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"`
+ else
+ # unknown
+ sharedlib_from_linklib_result=
+ fi
+}
+
+
+# func_extract_an_archive dir oldlib
+func_extract_an_archive ()
+{
+ $debug_cmd
+
+ f_ex_an_ar_dir=$1; shift
+ f_ex_an_ar_oldlib=$1
+ if test yes = "$lock_old_archive_extraction"; then
+ lockfile=$f_ex_an_ar_oldlib.lock
+ until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
+ func_echo "Waiting for $lockfile to be removed"
+ sleep 2
+ done
+ fi
+ func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \
+ 'stat=$?; rm -f "$lockfile"; exit $stat'
+ if test yes = "$lock_old_archive_extraction"; then
+ $opt_dry_run || rm -f "$lockfile"
+ fi
+ if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then
+ :
+ else
+ func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib"
+ fi
+}
+
+
+# func_extract_archives gentop oldlib ...
+func_extract_archives ()
+{
+ $debug_cmd
+
+ my_gentop=$1; shift
+ my_oldlibs=${1+"$@"}
+ my_oldobjs=
+ my_xlib=
+ my_xabs=
+ my_xdir=
+
+ for my_xlib in $my_oldlibs; do
+ # Extract the objects.
+ case $my_xlib in
+ [\\/]* | [A-Za-z]:[\\/]*) my_xabs=$my_xlib ;;
+ *) my_xabs=`pwd`"/$my_xlib" ;;
+ esac
+ func_basename "$my_xlib"
+ my_xlib=$func_basename_result
+ my_xlib_u=$my_xlib
+ while :; do
+ case " $extracted_archives " in
+ *" $my_xlib_u "*)
+ func_arith $extracted_serial + 1
+ extracted_serial=$func_arith_result
+ my_xlib_u=lt$extracted_serial-$my_xlib ;;
+ *) break ;;
+ esac
+ done
+ extracted_archives="$extracted_archives $my_xlib_u"
+ my_xdir=$my_gentop/$my_xlib_u
+
+ func_mkdir_p "$my_xdir"
+
+ case $host in
+ *-darwin*)
+ func_verbose "Extracting $my_xabs"
+ # Do not bother doing anything if just a dry run
+ $opt_dry_run || {
+ darwin_orig_dir=`pwd`
+ cd $my_xdir || exit $?
+ darwin_archive=$my_xabs
+ darwin_curdir=`pwd`
+ func_basename "$darwin_archive"
+ darwin_base_archive=$func_basename_result
+ darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true`
+ if test -n "$darwin_arches"; then
+ darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'`
+ darwin_arch=
+ func_verbose "$darwin_base_archive has multiple architectures $darwin_arches"
+ for darwin_arch in $darwin_arches; do
+ func_mkdir_p "unfat-$$/$darwin_base_archive-$darwin_arch"
+ $LIPO -thin $darwin_arch -output "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" "$darwin_archive"
+ cd "unfat-$$/$darwin_base_archive-$darwin_arch"
+ func_extract_an_archive "`pwd`" "$darwin_base_archive"
+ cd "$darwin_curdir"
+ $RM "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive"
+ done # $darwin_arches
+ ## Okay now we've a bunch of thin objects, gotta fatten them up :)
+ darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$sed_basename" | sort -u`
+ darwin_file=
+ darwin_files=
+ for darwin_file in $darwin_filelist; do
+ darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP`
+ $LIPO -create -output "$darwin_file" $darwin_files
+ done # $darwin_filelist
+ $RM -rf unfat-$$
+ cd "$darwin_orig_dir"
+ else
+ cd $darwin_orig_dir
+ func_extract_an_archive "$my_xdir" "$my_xabs"
+ fi # $darwin_arches
+ } # !$opt_dry_run
+ ;;
+ *)
+ func_extract_an_archive "$my_xdir" "$my_xabs"
+ ;;
+ esac
+ my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP`
+ done
+
+ func_extract_archives_result=$my_oldobjs
+}
+
+
+# func_emit_wrapper [arg=no]
+#
+# Emit a libtool wrapper script on stdout.
+# Don't directly open a file because we may want to
+# incorporate the script contents within a cygwin/mingw
+# wrapper executable. Must ONLY be called from within
+# func_mode_link because it depends on a number of variables
+# set therein.
+#
+# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR
+# variable will take. If 'yes', then the emitted script
+# will assume that the directory where it is stored is
+# the $objdir directory. This is a cygwin/mingw-specific
+# behavior.
+func_emit_wrapper ()
+{
+ func_emit_wrapper_arg1=${1-no}
+
+ $ECHO "\
+#! $SHELL
+
+# $output - temporary wrapper script for $objdir/$outputname
+# Generated by $PROGRAM (GNU $PACKAGE) $VERSION
+#
+# The $output program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='$sed_quote_subst'
+
+# Be Bourne compatible
+if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then
+ emulate sh
+ NULLCMD=:
+ # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '\${1+\"\$@\"}'='\"\$@\"'
+ setopt NO_GLOB_SUBST
+else
+ case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+relink_command=\"$relink_command\"
+
+# This environment variable determines our operation mode.
+if test \"\$libtool_install_magic\" = \"$magic\"; then
+ # install mode needs the following variables:
+ generated_by_libtool_version='$macro_version'
+ notinst_deplibs='$notinst_deplibs'
+else
+ # When we are sourced in execute mode, \$file and \$ECHO are already set.
+ if test \"\$libtool_execute_magic\" != \"$magic\"; then
+ file=\"\$0\""
+
+ qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"`
+ $ECHO "\
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+\$1
+_LTECHO_EOF'
+}
+ ECHO=\"$qECHO\"
+ fi
+
+# Very basic option parsing. These options are (a) specific to
+# the libtool wrapper, (b) are identical between the wrapper
+# /script/ and the wrapper /executable/ that is used only on
+# windows platforms, and (c) all begin with the string "--lt-"
+# (application programs are unlikely to have options that match
+# this pattern).
+#
+# There are only two supported options: --lt-debug and
+# --lt-dump-script. There is, deliberately, no --lt-help.
+#
+# The first argument to this parsing function should be the
+# script's $0 value, followed by "$@".
+lt_option_debug=
+func_parse_lt_options ()
+{
+ lt_script_arg0=\$0
+ shift
+ for lt_opt
+ do
+ case \"\$lt_opt\" in
+ --lt-debug) lt_option_debug=1 ;;
+ --lt-dump-script)
+ lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\`
+ test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=.
+ lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\`
+ cat \"\$lt_dump_D/\$lt_dump_F\"
+ exit 0
+ ;;
+ --lt-*)
+ \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2
+ exit 1
+ ;;
+ esac
+ done
+
+ # Print the debug banner immediately:
+ if test -n \"\$lt_option_debug\"; then
+ echo \"$outputname:$output:\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\" 1>&2
+ fi
+}
+
+# Used when --lt-debug. Prints its arguments to stdout
+# (redirection is the responsibility of the caller)
+func_lt_dump_args ()
+{
+ lt_dump_args_N=1;
+ for lt_arg
+ do
+ \$ECHO \"$outputname:$output:\$LINENO: newargv[\$lt_dump_args_N]: \$lt_arg\"
+ lt_dump_args_N=\`expr \$lt_dump_args_N + 1\`
+ done
+}
+
+# Core function for launching the target application
+func_exec_program_core ()
+{
+"
+ case $host in
+ # Backslashes separate directories on plain windows
+ *-*-mingw | *-*-os2* | *-cegcc*)
+ $ECHO "\
+ if test -n \"\$lt_option_debug\"; then
+ \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2
+ func_lt_dump_args \${1+\"\$@\"} 1>&2
+ fi
+ exec \"\$progdir\\\\\$program\" \${1+\"\$@\"}
+"
+ ;;
+
+ *)
+ $ECHO "\
+ if test -n \"\$lt_option_debug\"; then
+ \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir/\$program\" 1>&2
+ func_lt_dump_args \${1+\"\$@\"} 1>&2
+ fi
+ exec \"\$progdir/\$program\" \${1+\"\$@\"}
+"
+ ;;
+ esac
+ $ECHO "\
+ \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2
+ exit 1
+}
+
+# A function to encapsulate launching the target application
+# Strips options in the --lt-* namespace from \$@ and
+# launches target application with the remaining arguments.
+func_exec_program ()
+{
+ case \" \$* \" in
+ *\\ --lt-*)
+ for lt_wr_arg
+ do
+ case \$lt_wr_arg in
+ --lt-*) ;;
+ *) set x \"\$@\" \"\$lt_wr_arg\"; shift;;
+ esac
+ shift
+ done ;;
+ esac
+ func_exec_program_core \${1+\"\$@\"}
+}
+
+ # Parse options
+ func_parse_lt_options \"\$0\" \${1+\"\$@\"}
+
+ # Find the directory that this script lives in.
+ thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\`
+ test \"x\$thisdir\" = \"x\$file\" && thisdir=.
+
+ # Follow symbolic links until we get to the real thisdir.
+ file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\`
+ while test -n \"\$file\"; do
+ destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\`
+
+ # If there was a directory component, then change thisdir.
+ if test \"x\$destdir\" != \"x\$file\"; then
+ case \"\$destdir\" in
+ [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;;
+ *) thisdir=\"\$thisdir/\$destdir\" ;;
+ esac
+ fi
+
+ file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\`
+ file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\`
+ done
+
+ # Usually 'no', except on cygwin/mingw when embedded into
+ # the cwrapper.
+ WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1
+ if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then
+ # special case for '.'
+ if test \"\$thisdir\" = \".\"; then
+ thisdir=\`pwd\`
+ fi
+ # remove .libs from thisdir
+ case \"\$thisdir\" in
+ *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;;
+ $objdir ) thisdir=. ;;
+ esac
+ fi
+
+ # Try to get the absolute directory name.
+ absdir=\`cd \"\$thisdir\" && pwd\`
+ test -n \"\$absdir\" && thisdir=\"\$absdir\"
+"
+
+ if test yes = "$fast_install"; then
+ $ECHO "\
+ program=lt-'$outputname'$exeext
+ progdir=\"\$thisdir/$objdir\"
+
+ if test ! -f \"\$progdir/\$program\" ||
+ { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | $SED 1q\`; \\
+ test \"X\$file\" != \"X\$progdir/\$program\"; }; then
+
+ file=\"\$\$-\$program\"
+
+ if test ! -d \"\$progdir\"; then
+ $MKDIR \"\$progdir\"
+ else
+ $RM \"\$progdir/\$file\"
+ fi"
+
+ $ECHO "\
+
+ # relink executable if necessary
+ if test -n \"\$relink_command\"; then
+ if relink_command_output=\`eval \$relink_command 2>&1\`; then :
+ else
+ \$ECHO \"\$relink_command_output\" >&2
+ $RM \"\$progdir/\$file\"
+ exit 1
+ fi
+ fi
+
+ $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null ||
+ { $RM \"\$progdir/\$program\";
+ $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; }
+ $RM \"\$progdir/\$file\"
+ fi"
+ else
+ $ECHO "\
+ program='$outputname'
+ progdir=\"\$thisdir/$objdir\"
+"
+ fi
+
+ $ECHO "\
+
+ if test -f \"\$progdir/\$program\"; then"
+
+ # fixup the dll searchpath if we need to.
+ #
+ # Fix the DLL searchpath if we need to. Do this before prepending
+ # to shlibpath, because on Windows, both are PATH and uninstalled
+ # libraries must come first.
+ if test -n "$dllsearchpath"; then
+ $ECHO "\
+ # Add the dll search path components to the executable PATH
+ PATH=$dllsearchpath:\$PATH
+"
+ fi
+
+ # Export our shlibpath_var if we have one.
+ if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+ $ECHO "\
+ # Add our own library path to $shlibpath_var
+ $shlibpath_var=\"$temp_rpath\$$shlibpath_var\"
+
+ # Some systems cannot cope with colon-terminated $shlibpath_var
+ # The second colon is a workaround for a bug in BeOS R4 sed
+ $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\`
+
+ export $shlibpath_var
+"
+ fi
+
+ $ECHO "\
+ if test \"\$libtool_execute_magic\" != \"$magic\"; then
+ # Run the actual program with our arguments.
+ func_exec_program \${1+\"\$@\"}
+ fi
+ else
+ # The program doesn't exist.
+ \$ECHO \"\$0: error: '\$progdir/\$program' does not exist\" 1>&2
+ \$ECHO \"This script is just a wrapper for \$program.\" 1>&2
+ \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2
+ exit 1
+ fi
+fi\
+"
+}
+
+
+# func_emit_cwrapperexe_src
+# emit the source code for a wrapper executable on stdout
+# Must ONLY be called from within func_mode_link because
+# it depends on a number of variable set therein.
+func_emit_cwrapperexe_src ()
+{
+ cat <<EOF
+
+/* $cwrappersource - temporary wrapper executable for $objdir/$outputname
+ Generated by $PROGRAM (GNU $PACKAGE) $VERSION
+
+ The $output program cannot be directly executed until all the libtool
+ libraries that it depends on are installed.
+
+ This wrapper executable should never be moved out of the build directory.
+ If it is, it will not operate correctly.
+*/
+EOF
+ cat <<"EOF"
+#ifdef _MSC_VER
+# define _CRT_SECURE_NO_DEPRECATE 1
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _MSC_VER
+# include <direct.h>
+# include <process.h>
+# include <io.h>
+#else
+# include <unistd.h>
+# include <stdint.h>
+# ifdef __CYGWIN__
+# include <io.h>
+# endif
+#endif
+#include <malloc.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0)
+
+/* declarations of non-ANSI functions */
+#if defined __MINGW32__
+# ifdef __STRICT_ANSI__
+int _putenv (const char *);
+# endif
+#elif defined __CYGWIN__
+# ifdef __STRICT_ANSI__
+char *realpath (const char *, char *);
+int putenv (char *);
+int setenv (const char *, const char *, int);
+# endif
+/* #elif defined other_platform || defined ... */
+#endif
+
+/* portability defines, excluding path handling macros */
+#if defined _MSC_VER
+# define setmode _setmode
+# define stat _stat
+# define chmod _chmod
+# define getcwd _getcwd
+# define putenv _putenv
+# define S_IXUSR _S_IEXEC
+#elif defined __MINGW32__
+# define setmode _setmode
+# define stat _stat
+# define chmod _chmod
+# define getcwd _getcwd
+# define putenv _putenv
+#elif defined __CYGWIN__
+# define HAVE_SETENV
+# define FOPEN_WB "wb"
+/* #elif defined other platforms ... */
+#endif
+
+#if defined PATH_MAX
+# define LT_PATHMAX PATH_MAX
+#elif defined MAXPATHLEN
+# define LT_PATHMAX MAXPATHLEN
+#else
+# define LT_PATHMAX 1024
+#endif
+
+#ifndef S_IXOTH
+# define S_IXOTH 0
+#endif
+#ifndef S_IXGRP
+# define S_IXGRP 0
+#endif
+
+/* path handling portability macros */
+#ifndef DIR_SEPARATOR
+# define DIR_SEPARATOR '/'
+# define PATH_SEPARATOR ':'
+#endif
+
+#if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \
+ defined __OS2__
+# define HAVE_DOS_BASED_FILE_SYSTEM
+# define FOPEN_WB "wb"
+# ifndef DIR_SEPARATOR_2
+# define DIR_SEPARATOR_2 '\\'
+# endif
+# ifndef PATH_SEPARATOR_2
+# define PATH_SEPARATOR_2 ';'
+# endif
+#endif
+
+#ifndef DIR_SEPARATOR_2
+# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
+#else /* DIR_SEPARATOR_2 */
+# define IS_DIR_SEPARATOR(ch) \
+ (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
+#endif /* DIR_SEPARATOR_2 */
+
+#ifndef PATH_SEPARATOR_2
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR)
+#else /* PATH_SEPARATOR_2 */
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2)
+#endif /* PATH_SEPARATOR_2 */
+
+#ifndef FOPEN_WB
+# define FOPEN_WB "w"
+#endif
+#ifndef _O_BINARY
+# define _O_BINARY 0
+#endif
+
+#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type)))
+#define XFREE(stale) do { \
+ if (stale) { free (stale); stale = 0; } \
+} while (0)
+
+#if defined LT_DEBUGWRAPPER
+static int lt_debug = 1;
+#else
+static int lt_debug = 0;
+#endif
+
+const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */
+
+void *xmalloc (size_t num);
+char *xstrdup (const char *string);
+const char *base_name (const char *name);
+char *find_executable (const char *wrapper);
+char *chase_symlinks (const char *pathspec);
+int make_executable (const char *path);
+int check_executable (const char *path);
+char *strendzap (char *str, const char *pat);
+void lt_debugprintf (const char *file, int line, const char *fmt, ...);
+void lt_fatal (const char *file, int line, const char *message, ...);
+static const char *nonnull (const char *s);
+static const char *nonempty (const char *s);
+void lt_setenv (const char *name, const char *value);
+char *lt_extend_str (const char *orig_value, const char *add, int to_end);
+void lt_update_exe_path (const char *name, const char *value);
+void lt_update_lib_path (const char *name, const char *value);
+char **prepare_spawn (char **argv);
+void lt_dump_script (FILE *f);
+EOF
+
+ cat <<EOF
+#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 5)
+# define externally_visible volatile
+#else
+# define externally_visible __attribute__((externally_visible)) volatile
+#endif
+externally_visible const char * MAGIC_EXE = "$magic_exe";
+const char * LIB_PATH_VARNAME = "$shlibpath_var";
+EOF
+
+ if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+ func_to_host_path "$temp_rpath"
+ cat <<EOF
+const char * LIB_PATH_VALUE = "$func_to_host_path_result";
+EOF
+ else
+ cat <<"EOF"
+const char * LIB_PATH_VALUE = "";
+EOF
+ fi
+
+ if test -n "$dllsearchpath"; then
+ func_to_host_path "$dllsearchpath:"
+ cat <<EOF
+const char * EXE_PATH_VARNAME = "PATH";
+const char * EXE_PATH_VALUE = "$func_to_host_path_result";
+EOF
+ else
+ cat <<"EOF"
+const char * EXE_PATH_VARNAME = "";
+const char * EXE_PATH_VALUE = "";
+EOF
+ fi
+
+ if test yes = "$fast_install"; then
+ cat <<EOF
+const char * TARGET_PROGRAM_NAME = "lt-$outputname"; /* hopefully, no .exe */
+EOF
+ else
+ cat <<EOF
+const char * TARGET_PROGRAM_NAME = "$outputname"; /* hopefully, no .exe */
+EOF
+ fi
+
+
+ cat <<"EOF"
+
+#define LTWRAPPER_OPTION_PREFIX "--lt-"
+
+static const char *ltwrapper_option_prefix = LTWRAPPER_OPTION_PREFIX;
+static const char *dumpscript_opt = LTWRAPPER_OPTION_PREFIX "dump-script";
+static const char *debug_opt = LTWRAPPER_OPTION_PREFIX "debug";
+
+int
+main (int argc, char *argv[])
+{
+ char **newargz;
+ int newargc;
+ char *tmp_pathspec;
+ char *actual_cwrapper_path;
+ char *actual_cwrapper_name;
+ char *target_name;
+ char *lt_argv_zero;
+ int rval = 127;
+
+ int i;
+
+ program_name = (char *) xstrdup (base_name (argv[0]));
+ newargz = XMALLOC (char *, (size_t) argc + 1);
+
+ /* very simple arg parsing; don't want to rely on getopt
+ * also, copy all non cwrapper options to newargz, except
+ * argz[0], which is handled differently
+ */
+ newargc=0;
+ for (i = 1; i < argc; i++)
+ {
+ if (STREQ (argv[i], dumpscript_opt))
+ {
+EOF
+ case $host in
+ *mingw* | *cygwin* )
+ # make stdout use "unix" line endings
+ echo " setmode(1,_O_BINARY);"
+ ;;
+ esac
+
+ cat <<"EOF"
+ lt_dump_script (stdout);
+ return 0;
+ }
+ if (STREQ (argv[i], debug_opt))
+ {
+ lt_debug = 1;
+ continue;
+ }
+ if (STREQ (argv[i], ltwrapper_option_prefix))
+ {
+ /* however, if there is an option in the LTWRAPPER_OPTION_PREFIX
+ namespace, but it is not one of the ones we know about and
+ have already dealt with, above (inluding dump-script), then
+ report an error. Otherwise, targets might begin to believe
+ they are allowed to use options in the LTWRAPPER_OPTION_PREFIX
+ namespace. The first time any user complains about this, we'll
+ need to make LTWRAPPER_OPTION_PREFIX a configure-time option
+ or a configure.ac-settable value.
+ */
+ lt_fatal (__FILE__, __LINE__,
+ "unrecognized %s option: '%s'",
+ ltwrapper_option_prefix, argv[i]);
+ }
+ /* otherwise ... */
+ newargz[++newargc] = xstrdup (argv[i]);
+ }
+ newargz[++newargc] = NULL;
+
+EOF
+ cat <<EOF
+ /* The GNU banner must be the first non-error debug message */
+ lt_debugprintf (__FILE__, __LINE__, "libtool wrapper (GNU $PACKAGE) $VERSION\n");
+EOF
+ cat <<"EOF"
+ lt_debugprintf (__FILE__, __LINE__, "(main) argv[0]: %s\n", argv[0]);
+ lt_debugprintf (__FILE__, __LINE__, "(main) program_name: %s\n", program_name);
+
+ tmp_pathspec = find_executable (argv[0]);
+ if (tmp_pathspec == NULL)
+ lt_fatal (__FILE__, __LINE__, "couldn't find %s", argv[0]);
+ lt_debugprintf (__FILE__, __LINE__,
+ "(main) found exe (before symlink chase) at: %s\n",
+ tmp_pathspec);
+
+ actual_cwrapper_path = chase_symlinks (tmp_pathspec);
+ lt_debugprintf (__FILE__, __LINE__,
+ "(main) found exe (after symlink chase) at: %s\n",
+ actual_cwrapper_path);
+ XFREE (tmp_pathspec);
+
+ actual_cwrapper_name = xstrdup (base_name (actual_cwrapper_path));
+ strendzap (actual_cwrapper_path, actual_cwrapper_name);
+
+ /* wrapper name transforms */
+ strendzap (actual_cwrapper_name, ".exe");
+ tmp_pathspec = lt_extend_str (actual_cwrapper_name, ".exe", 1);
+ XFREE (actual_cwrapper_name);
+ actual_cwrapper_name = tmp_pathspec;
+ tmp_pathspec = 0;
+
+ /* target_name transforms -- use actual target program name; might have lt- prefix */
+ target_name = xstrdup (base_name (TARGET_PROGRAM_NAME));
+ strendzap (target_name, ".exe");
+ tmp_pathspec = lt_extend_str (target_name, ".exe", 1);
+ XFREE (target_name);
+ target_name = tmp_pathspec;
+ tmp_pathspec = 0;
+
+ lt_debugprintf (__FILE__, __LINE__,
+ "(main) libtool target name: %s\n",
+ target_name);
+EOF
+
+ cat <<EOF
+ newargz[0] =
+ XMALLOC (char, (strlen (actual_cwrapper_path) +
+ strlen ("$objdir") + 1 + strlen (actual_cwrapper_name) + 1));
+ strcpy (newargz[0], actual_cwrapper_path);
+ strcat (newargz[0], "$objdir");
+ strcat (newargz[0], "/");
+EOF
+
+ cat <<"EOF"
+ /* stop here, and copy so we don't have to do this twice */
+ tmp_pathspec = xstrdup (newargz[0]);
+
+ /* do NOT want the lt- prefix here, so use actual_cwrapper_name */
+ strcat (newargz[0], actual_cwrapper_name);
+
+ /* DO want the lt- prefix here if it exists, so use target_name */
+ lt_argv_zero = lt_extend_str (tmp_pathspec, target_name, 1);
+ XFREE (tmp_pathspec);
+ tmp_pathspec = NULL;
+EOF
+
+ case $host_os in
+ mingw*)
+ cat <<"EOF"
+ {
+ char* p;
+ while ((p = strchr (newargz[0], '\\')) != NULL)
+ {
+ *p = '/';
+ }
+ while ((p = strchr (lt_argv_zero, '\\')) != NULL)
+ {
+ *p = '/';
+ }
+ }
+EOF
+ ;;
+ esac
+
+ cat <<"EOF"
+ XFREE (target_name);
+ XFREE (actual_cwrapper_path);
+ XFREE (actual_cwrapper_name);
+
+ lt_setenv ("BIN_SH", "xpg4"); /* for Tru64 */
+ lt_setenv ("DUALCASE", "1"); /* for MSK sh */
+ /* Update the DLL searchpath. EXE_PATH_VALUE ($dllsearchpath) must
+ be prepended before (that is, appear after) LIB_PATH_VALUE ($temp_rpath)
+ because on Windows, both *_VARNAMEs are PATH but uninstalled
+ libraries must come first. */
+ lt_update_exe_path (EXE_PATH_VARNAME, EXE_PATH_VALUE);
+ lt_update_lib_path (LIB_PATH_VARNAME, LIB_PATH_VALUE);
+
+ lt_debugprintf (__FILE__, __LINE__, "(main) lt_argv_zero: %s\n",
+ nonnull (lt_argv_zero));
+ for (i = 0; i < newargc; i++)
+ {
+ lt_debugprintf (__FILE__, __LINE__, "(main) newargz[%d]: %s\n",
+ i, nonnull (newargz[i]));
+ }
+
+EOF
+
+ case $host_os in
+ mingw*)
+ cat <<"EOF"
+ /* execv doesn't actually work on mingw as expected on unix */
+ newargz = prepare_spawn (newargz);
+ rval = (int) _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz);
+ if (rval == -1)
+ {
+ /* failed to start process */
+ lt_debugprintf (__FILE__, __LINE__,
+ "(main) failed to launch target \"%s\": %s\n",
+ lt_argv_zero, nonnull (strerror (errno)));
+ return 127;
+ }
+ return rval;
+EOF
+ ;;
+ *)
+ cat <<"EOF"
+ execv (lt_argv_zero, newargz);
+ return rval; /* =127, but avoids unused variable warning */
+EOF
+ ;;
+ esac
+
+ cat <<"EOF"
+}
+
+void *
+xmalloc (size_t num)
+{
+ void *p = (void *) malloc (num);
+ if (!p)
+ lt_fatal (__FILE__, __LINE__, "memory exhausted");
+
+ return p;
+}
+
+char *
+xstrdup (const char *string)
+{
+ return string ? strcpy ((char *) xmalloc (strlen (string) + 1),
+ string) : NULL;
+}
+
+const char *
+base_name (const char *name)
+{
+ const char *base;
+
+#if defined HAVE_DOS_BASED_FILE_SYSTEM
+ /* Skip over the disk name in MSDOS pathnames. */
+ if (isalpha ((unsigned char) name[0]) && name[1] == ':')
+ name += 2;
+#endif
+
+ for (base = name; *name; name++)
+ if (IS_DIR_SEPARATOR (*name))
+ base = name + 1;
+ return base;
+}
+
+int
+check_executable (const char *path)
+{
+ struct stat st;
+
+ lt_debugprintf (__FILE__, __LINE__, "(check_executable): %s\n",
+ nonempty (path));
+ if ((!path) || (!*path))
+ return 0;
+
+ if ((stat (path, &st) >= 0)
+ && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+ return 1;
+ else
+ return 0;
+}
+
+int
+make_executable (const char *path)
+{
+ int rval = 0;
+ struct stat st;
+
+ lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n",
+ nonempty (path));
+ if ((!path) || (!*path))
+ return 0;
+
+ if (stat (path, &st) >= 0)
+ {
+ rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR);
+ }
+ return rval;
+}
+
+/* Searches for the full path of the wrapper. Returns
+ newly allocated full path name if found, NULL otherwise
+ Does not chase symlinks, even on platforms that support them.
+*/
+char *
+find_executable (const char *wrapper)
+{
+ int has_slash = 0;
+ const char *p;
+ const char *p_next;
+ /* static buffer for getcwd */
+ char tmp[LT_PATHMAX + 1];
+ size_t tmp_len;
+ char *concat_name;
+
+ lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n",
+ nonempty (wrapper));
+
+ if ((wrapper == NULL) || (*wrapper == '\0'))
+ return NULL;
+
+ /* Absolute path? */
+#if defined HAVE_DOS_BASED_FILE_SYSTEM
+ if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':')
+ {
+ concat_name = xstrdup (wrapper);
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ }
+ else
+ {
+#endif
+ if (IS_DIR_SEPARATOR (wrapper[0]))
+ {
+ concat_name = xstrdup (wrapper);
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ }
+#if defined HAVE_DOS_BASED_FILE_SYSTEM
+ }
+#endif
+
+ for (p = wrapper; *p; p++)
+ if (*p == '/')
+ {
+ has_slash = 1;
+ break;
+ }
+ if (!has_slash)
+ {
+ /* no slashes; search PATH */
+ const char *path = getenv ("PATH");
+ if (path != NULL)
+ {
+ for (p = path; *p; p = p_next)
+ {
+ const char *q;
+ size_t p_len;
+ for (q = p; *q; q++)
+ if (IS_PATH_SEPARATOR (*q))
+ break;
+ p_len = (size_t) (q - p);
+ p_next = (*q == '\0' ? q : q + 1);
+ if (p_len == 0)
+ {
+ /* empty path: current directory */
+ if (getcwd (tmp, LT_PATHMAX) == NULL)
+ lt_fatal (__FILE__, __LINE__, "getcwd failed: %s",
+ nonnull (strerror (errno)));
+ tmp_len = strlen (tmp);
+ concat_name =
+ XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+ memcpy (concat_name, tmp, tmp_len);
+ concat_name[tmp_len] = '/';
+ strcpy (concat_name + tmp_len + 1, wrapper);
+ }
+ else
+ {
+ concat_name =
+ XMALLOC (char, p_len + 1 + strlen (wrapper) + 1);
+ memcpy (concat_name, p, p_len);
+ concat_name[p_len] = '/';
+ strcpy (concat_name + p_len + 1, wrapper);
+ }
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ }
+ }
+ /* not found in PATH; assume curdir */
+ }
+ /* Relative path | not found in path: prepend cwd */
+ if (getcwd (tmp, LT_PATHMAX) == NULL)
+ lt_fatal (__FILE__, __LINE__, "getcwd failed: %s",
+ nonnull (strerror (errno)));
+ tmp_len = strlen (tmp);
+ concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+ memcpy (concat_name, tmp, tmp_len);
+ concat_name[tmp_len] = '/';
+ strcpy (concat_name + tmp_len + 1, wrapper);
+
+ if (check_executable (concat_name))
+ return concat_name;
+ XFREE (concat_name);
+ return NULL;
+}
+
+char *
+chase_symlinks (const char *pathspec)
+{
+#ifndef S_ISLNK
+ return xstrdup (pathspec);
+#else
+ char buf[LT_PATHMAX];
+ struct stat s;
+ char *tmp_pathspec = xstrdup (pathspec);
+ char *p;
+ int has_symlinks = 0;
+ while (strlen (tmp_pathspec) && !has_symlinks)
+ {
+ lt_debugprintf (__FILE__, __LINE__,
+ "checking path component for symlinks: %s\n",
+ tmp_pathspec);
+ if (lstat (tmp_pathspec, &s) == 0)
+ {
+ if (S_ISLNK (s.st_mode) != 0)
+ {
+ has_symlinks = 1;
+ break;
+ }
+
+ /* search backwards for last DIR_SEPARATOR */
+ p = tmp_pathspec + strlen (tmp_pathspec) - 1;
+ while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+ p--;
+ if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+ {
+ /* no more DIR_SEPARATORS left */
+ break;
+ }
+ *p = '\0';
+ }
+ else
+ {
+ lt_fatal (__FILE__, __LINE__,
+ "error accessing file \"%s\": %s",
+ tmp_pathspec, nonnull (strerror (errno)));
+ }
+ }
+ XFREE (tmp_pathspec);
+
+ if (!has_symlinks)
+ {
+ return xstrdup (pathspec);
+ }
+
+ tmp_pathspec = realpath (pathspec, buf);
+ if (tmp_pathspec == 0)
+ {
+ lt_fatal (__FILE__, __LINE__,
+ "could not follow symlinks for %s", pathspec);
+ }
+ return xstrdup (tmp_pathspec);
+#endif
+}
+
+char *
+strendzap (char *str, const char *pat)
+{
+ size_t len, patlen;
+
+ assert (str != NULL);
+ assert (pat != NULL);
+
+ len = strlen (str);
+ patlen = strlen (pat);
+
+ if (patlen <= len)
+ {
+ str += len - patlen;
+ if (STREQ (str, pat))
+ *str = '\0';
+ }
+ return str;
+}
+
+void
+lt_debugprintf (const char *file, int line, const char *fmt, ...)
+{
+ va_list args;
+ if (lt_debug)
+ {
+ (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line);
+ va_start (args, fmt);
+ (void) vfprintf (stderr, fmt, args);
+ va_end (args);
+ }
+}
+
+static void
+lt_error_core (int exit_status, const char *file,
+ int line, const char *mode,
+ const char *message, va_list ap)
+{
+ fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode);
+ vfprintf (stderr, message, ap);
+ fprintf (stderr, ".\n");
+
+ if (exit_status >= 0)
+ exit (exit_status);
+}
+
+void
+lt_fatal (const char *file, int line, const char *message, ...)
+{
+ va_list ap;
+ va_start (ap, message);
+ lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap);
+ va_end (ap);
+}
+
+static const char *
+nonnull (const char *s)
+{
+ return s ? s : "(null)";
+}
+
+static const char *
+nonempty (const char *s)
+{
+ return (s && !*s) ? "(empty)" : nonnull (s);
+}
+
+void
+lt_setenv (const char *name, const char *value)
+{
+ lt_debugprintf (__FILE__, __LINE__,
+ "(lt_setenv) setting '%s' to '%s'\n",
+ nonnull (name), nonnull (value));
+ {
+#ifdef HAVE_SETENV
+ /* always make a copy, for consistency with !HAVE_SETENV */
+ char *str = xstrdup (value);
+ setenv (name, str, 1);
+#else
+ size_t len = strlen (name) + 1 + strlen (value) + 1;
+ char *str = XMALLOC (char, len);
+ sprintf (str, "%s=%s", name, value);
+ if (putenv (str) != EXIT_SUCCESS)
+ {
+ XFREE (str);
+ }
+#endif
+ }
+}
+
+char *
+lt_extend_str (const char *orig_value, const char *add, int to_end)
+{
+ char *new_value;
+ if (orig_value && *orig_value)
+ {
+ size_t orig_value_len = strlen (orig_value);
+ size_t add_len = strlen (add);
+ new_value = XMALLOC (char, add_len + orig_value_len + 1);
+ if (to_end)
+ {
+ strcpy (new_value, orig_value);
+ strcpy (new_value + orig_value_len, add);
+ }
+ else
+ {
+ strcpy (new_value, add);
+ strcpy (new_value + add_len, orig_value);
+ }
+ }
+ else
+ {
+ new_value = xstrdup (add);
+ }
+ return new_value;
+}
+
+void
+lt_update_exe_path (const char *name, const char *value)
+{
+ lt_debugprintf (__FILE__, __LINE__,
+ "(lt_update_exe_path) modifying '%s' by prepending '%s'\n",
+ nonnull (name), nonnull (value));
+
+ if (name && *name && value && *value)
+ {
+ char *new_value = lt_extend_str (getenv (name), value, 0);
+ /* some systems can't cope with a ':'-terminated path #' */
+ size_t len = strlen (new_value);
+ while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1]))
+ {
+ new_value[--len] = '\0';
+ }
+ lt_setenv (name, new_value);
+ XFREE (new_value);
+ }
+}
+
+void
+lt_update_lib_path (const char *name, const char *value)
+{
+ lt_debugprintf (__FILE__, __LINE__,
+ "(lt_update_lib_path) modifying '%s' by prepending '%s'\n",
+ nonnull (name), nonnull (value));
+
+ if (name && *name && value && *value)
+ {
+ char *new_value = lt_extend_str (getenv (name), value, 0);
+ lt_setenv (name, new_value);
+ XFREE (new_value);
+ }
+}
+
+EOF
+ case $host_os in
+ mingw*)
+ cat <<"EOF"
+
+/* Prepares an argument vector before calling spawn().
+ Note that spawn() does not by itself call the command interpreter
+ (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") :
+ ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&v);
+ v.dwPlatformId == VER_PLATFORM_WIN32_NT;
+ }) ? "cmd.exe" : "command.com").
+ Instead it simply concatenates the arguments, separated by ' ', and calls
+ CreateProcess(). We must quote the arguments since Win32 CreateProcess()
+ interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a
+ special way:
+ - Space and tab are interpreted as delimiters. They are not treated as
+ delimiters if they are surrounded by double quotes: "...".
+ - Unescaped double quotes are removed from the input. Their only effect is
+ that within double quotes, space and tab are treated like normal
+ characters.
+ - Backslashes not followed by double quotes are not special.
+ - But 2*n+1 backslashes followed by a double quote become
+ n backslashes followed by a double quote (n >= 0):
+ \" -> "
+ \\\" -> \"
+ \\\\\" -> \\"
+ */
+#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+char **
+prepare_spawn (char **argv)
+{
+ size_t argc;
+ char **new_argv;
+ size_t i;
+
+ /* Count number of arguments. */
+ for (argc = 0; argv[argc] != NULL; argc++)
+ ;
+
+ /* Allocate new argument vector. */
+ new_argv = XMALLOC (char *, argc + 1);
+
+ /* Put quoted arguments into the new argument vector. */
+ for (i = 0; i < argc; i++)
+ {
+ const char *string = argv[i];
+
+ if (string[0] == '\0')
+ new_argv[i] = xstrdup ("\"\"");
+ else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL)
+ {
+ int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL);
+ size_t length;
+ unsigned int backslashes;
+ const char *s;
+ char *quoted_string;
+ char *p;
+
+ length = 0;
+ backslashes = 0;
+ if (quote_around)
+ length++;
+ for (s = string; *s != '\0'; s++)
+ {
+ char c = *s;
+ if (c == '"')
+ length += backslashes + 1;
+ length++;
+ if (c == '\\')
+ backslashes++;
+ else
+ backslashes = 0;
+ }
+ if (quote_around)
+ length += backslashes + 1;
+
+ quoted_string = XMALLOC (char, length + 1);
+
+ p = quoted_string;
+ backslashes = 0;
+ if (quote_around)
+ *p++ = '"';
+ for (s = string; *s != '\0'; s++)
+ {
+ char c = *s;
+ if (c == '"')
+ {
+ unsigned int j;
+ for (j = backslashes + 1; j > 0; j--)
+ *p++ = '\\';
+ }
+ *p++ = c;
+ if (c == '\\')
+ backslashes++;
+ else
+ backslashes = 0;
+ }
+ if (quote_around)
+ {
+ unsigned int j;
+ for (j = backslashes; j > 0; j--)
+ *p++ = '\\';
+ *p++ = '"';
+ }
+ *p = '\0';
+
+ new_argv[i] = quoted_string;
+ }
+ else
+ new_argv[i] = (char *) string;
+ }
+ new_argv[argc] = NULL;
+
+ return new_argv;
+}
+EOF
+ ;;
+ esac
+
+ cat <<"EOF"
+void lt_dump_script (FILE* f)
+{
+EOF
+ func_emit_wrapper yes |
+ $SED -n -e '
+s/^\(.\{79\}\)\(..*\)/\1\
+\2/
+h
+s/\([\\"]\)/\\\1/g
+s/$/\\n/
+s/\([^\n]*\).*/ fputs ("\1", f);/p
+g
+D'
+ cat <<"EOF"
+}
+EOF
+}
+# end: func_emit_cwrapperexe_src
+
+# func_win32_import_lib_p ARG
+# True if ARG is an import lib, as indicated by $file_magic_cmd
+func_win32_import_lib_p ()
+{
+ $debug_cmd
+
+ case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in
+ *import*) : ;;
+ *) false ;;
+ esac
+}
+
+# func_suncc_cstd_abi
+# !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!!
+# Several compiler flags select an ABI that is incompatible with the
+# Cstd library. Avoid specifying it if any are in CXXFLAGS.
+func_suncc_cstd_abi ()
+{
+ $debug_cmd
+
+ case " $compile_command " in
+ *" -compat=g "*|*\ -std=c++[0-9][0-9]\ *|*" -library=stdcxx4 "*|*" -library=stlport4 "*)
+ suncc_use_cstd_abi=no
+ ;;
+ *)
+ suncc_use_cstd_abi=yes
+ ;;
+ esac
+}
+
+# func_mode_link arg...
+func_mode_link ()
+{
+ $debug_cmd
+
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ # It is impossible to link a dll without this setting, and
+ # we shouldn't force the makefile maintainer to figure out
+ # what system we are compiling for in order to pass an extra
+ # flag for every libtool invocation.
+ # allow_undefined=no
+
+ # FIXME: Unfortunately, there are problems with the above when trying
+ # to make a dll that has undefined symbols, in which case not
+ # even a static library is built. For now, we need to specify
+ # -no-undefined on the libtool link line when we can be certain
+ # that all symbols are satisfied, otherwise we get a static library.
+ allow_undefined=yes
+ ;;
+ *)
+ allow_undefined=yes
+ ;;
+ esac
+ libtool_args=$nonopt
+ base_compile="$nonopt $@"
+ compile_command=$nonopt
+ finalize_command=$nonopt
+
+ compile_rpath=
+ finalize_rpath=
+ compile_shlibpath=
+ finalize_shlibpath=
+ convenience=
+ old_convenience=
+ deplibs=
+ old_deplibs=
+ compiler_flags=
+ linker_flags=
+ dllsearchpath=
+ lib_search_path=`pwd`
+ inst_prefix_dir=
+ new_inherited_linker_flags=
+
+ avoid_version=no
+ bindir=
+ dlfiles=
+ dlprefiles=
+ dlself=no
+ export_dynamic=no
+ export_symbols=
+ export_symbols_regex=
+ generated=
+ libobjs=
+ ltlibs=
+ module=no
+ no_install=no
+ objs=
+ os2dllname=
+ non_pic_objects=
+ precious_files_regex=
+ prefer_static_libs=no
+ preload=false
+ prev=
+ prevarg=
+ release=
+ rpath=
+ xrpath=
+ perm_rpath=
+ temp_rpath=
+ thread_safe=no
+ vinfo=
+ vinfo_number=no
+ weak_libs=
+ single_module=$wl-single_module
+ func_infer_tag $base_compile
+
+ # We need to know -static, to get the right output filenames.
+ for arg
+ do
+ case $arg in
+ -shared)
+ test yes != "$build_libtool_libs" \
+ && func_fatal_configuration "cannot build a shared library"
+ build_old_libs=no
+ break
+ ;;
+ -all-static | -static | -static-libtool-libs)
+ case $arg in
+ -all-static)
+ if test yes = "$build_libtool_libs" && test -z "$link_static_flag"; then
+ func_warning "complete static linking is impossible in this configuration"
+ fi
+ if test -n "$link_static_flag"; then
+ dlopen_self=$dlopen_self_static
+ fi
+ prefer_static_libs=yes
+ ;;
+ -static)
+ if test -z "$pic_flag" && test -n "$link_static_flag"; then
+ dlopen_self=$dlopen_self_static
+ fi
+ prefer_static_libs=built
+ ;;
+ -static-libtool-libs)
+ if test -z "$pic_flag" && test -n "$link_static_flag"; then
+ dlopen_self=$dlopen_self_static
+ fi
+ prefer_static_libs=yes
+ ;;
+ esac
+ build_libtool_libs=no
+ build_old_libs=yes
+ break
+ ;;
+ esac
+ done
+
+ # See if our shared archives depend on static archives.
+ test -n "$old_archive_from_new_cmds" && build_old_libs=yes
+
+ # Go through the arguments, transforming them on the way.
+ while test "$#" -gt 0; do
+ arg=$1
+ shift
+ func_quote_for_eval "$arg"
+ qarg=$func_quote_for_eval_unquoted_result
+ func_append libtool_args " $func_quote_for_eval_result"
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$prev"; then
+ case $prev in
+ output)
+ func_append compile_command " @OUTPUT@"
+ func_append finalize_command " @OUTPUT@"
+ ;;
+ esac
+
+ case $prev in
+ bindir)
+ bindir=$arg
+ prev=
+ continue
+ ;;
+ dlfiles|dlprefiles)
+ $preload || {
+ # Add the symbol object into the linking commands.
+ func_append compile_command " @SYMFILE@"
+ func_append finalize_command " @SYMFILE@"
+ preload=:
+ }
+ case $arg in
+ *.la | *.lo) ;; # We handle these cases below.
+ force)
+ if test no = "$dlself"; then
+ dlself=needless
+ export_dynamic=yes
+ fi
+ prev=
+ continue
+ ;;
+ self)
+ if test dlprefiles = "$prev"; then
+ dlself=yes
+ elif test dlfiles = "$prev" && test yes != "$dlopen_self"; then
+ dlself=yes
+ else
+ dlself=needless
+ export_dynamic=yes
+ fi
+ prev=
+ continue
+ ;;
+ *)
+ if test dlfiles = "$prev"; then
+ func_append dlfiles " $arg"
+ else
+ func_append dlprefiles " $arg"
+ fi
+ prev=
+ continue
+ ;;
+ esac
+ ;;
+ expsyms)
+ export_symbols=$arg
+ test -f "$arg" \
+ || func_fatal_error "symbol file '$arg' does not exist"
+ prev=
+ continue
+ ;;
+ expsyms_regex)
+ export_symbols_regex=$arg
+ prev=
+ continue
+ ;;
+ framework)
+ case $host in
+ *-*-darwin*)
+ case "$deplibs " in
+ *" $qarg.ltframework "*) ;;
+ *) func_append deplibs " $qarg.ltframework" # this is fixed later
+ ;;
+ esac
+ ;;
+ esac
+ prev=
+ continue
+ ;;
+ inst_prefix)
+ inst_prefix_dir=$arg
+ prev=
+ continue
+ ;;
+ mllvm)
+ # Clang does not use LLVM to link, so we can simply discard any
+ # '-mllvm $arg' options when doing the link step.
+ prev=
+ continue
+ ;;
+ objectlist)
+ if test -f "$arg"; then
+ save_arg=$arg
+ moreargs=
+ for fil in `cat "$save_arg"`
+ do
+# func_append moreargs " $fil"
+ arg=$fil
+ # A libtool-controlled object.
+
+ # Check to see that this really is a libtool object.
+ if func_lalib_unsafe_p "$arg"; then
+ pic_object=
+ non_pic_object=
+
+ # Read the .lo file
+ func_source "$arg"
+
+ if test -z "$pic_object" ||
+ test -z "$non_pic_object" ||
+ test none = "$pic_object" &&
+ test none = "$non_pic_object"; then
+ func_fatal_error "cannot find name of object for '$arg'"
+ fi
+
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir=$func_dirname_result
+
+ if test none != "$pic_object"; then
+ # Prepend the subdirectory the object is found in.
+ pic_object=$xdir$pic_object
+
+ if test dlfiles = "$prev"; then
+ if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then
+ func_append dlfiles " $pic_object"
+ prev=
+ continue
+ else
+ # If libtool objects are unsupported, then we need to preload.
+ prev=dlprefiles
+ fi
+ fi
+
+ # CHECK ME: I think I busted this. -Ossama
+ if test dlprefiles = "$prev"; then
+ # Preload the old-style object.
+ func_append dlprefiles " $pic_object"
+ prev=
+ fi
+
+ # A PIC object.
+ func_append libobjs " $pic_object"
+ arg=$pic_object
+ fi
+
+ # Non-PIC object.
+ if test none != "$non_pic_object"; then
+ # Prepend the subdirectory the object is found in.
+ non_pic_object=$xdir$non_pic_object
+
+ # A standard non-PIC object
+ func_append non_pic_objects " $non_pic_object"
+ if test -z "$pic_object" || test none = "$pic_object"; then
+ arg=$non_pic_object
+ fi
+ else
+ # If the PIC object exists, use it instead.
+ # $xdir was prepended to $pic_object above.
+ non_pic_object=$pic_object
+ func_append non_pic_objects " $non_pic_object"
+ fi
+ else
+ # Only an error if not doing a dry-run.
+ if $opt_dry_run; then
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir=$func_dirname_result
+
+ func_lo2o "$arg"
+ pic_object=$xdir$objdir/$func_lo2o_result
+ non_pic_object=$xdir$func_lo2o_result
+ func_append libobjs " $pic_object"
+ func_append non_pic_objects " $non_pic_object"
+ else
+ func_fatal_error "'$arg' is not a valid libtool object"
+ fi
+ fi
+ done
+ else
+ func_fatal_error "link input file '$arg' does not exist"
+ fi
+ arg=$save_arg
+ prev=
+ continue
+ ;;
+ os2dllname)
+ os2dllname=$arg
+ prev=
+ continue
+ ;;
+ precious_regex)
+ precious_files_regex=$arg
+ prev=
+ continue
+ ;;
+ release)
+ release=-$arg
+ prev=
+ continue
+ ;;
+ rpath | xrpath)
+ # We need an absolute path.
+ case $arg in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ func_fatal_error "only absolute run-paths are allowed"
+ ;;
+ esac
+ if test rpath = "$prev"; then
+ case "$rpath " in
+ *" $arg "*) ;;
+ *) func_append rpath " $arg" ;;
+ esac
+ else
+ case "$xrpath " in
+ *" $arg "*) ;;
+ *) func_append xrpath " $arg" ;;
+ esac
+ fi
+ prev=
+ continue
+ ;;
+ shrext)
+ shrext_cmds=$arg
+ prev=
+ continue
+ ;;
+ weak)
+ func_append weak_libs " $arg"
+ prev=
+ continue
+ ;;
+ xcclinker)
+ func_append linker_flags " $qarg"
+ func_append compiler_flags " $qarg"
+ prev=
+ func_append compile_command " $qarg"
+ func_append finalize_command " $qarg"
+ continue
+ ;;
+ xcompiler)
+ func_append compiler_flags " $qarg"
+ prev=
+ func_append compile_command " $qarg"
+ func_append finalize_command " $qarg"
+ continue
+ ;;
+ xlinker)
+ func_append linker_flags " $qarg"
+ func_append compiler_flags " $wl$qarg"
+ prev=
+ func_append compile_command " $wl$qarg"
+ func_append finalize_command " $wl$qarg"
+ continue
+ ;;
+ *)
+ eval "$prev=\"\$arg\""
+ prev=
+ continue
+ ;;
+ esac
+ fi # test -n "$prev"
+
+ prevarg=$arg
+
+ case $arg in
+ -all-static)
+ if test -n "$link_static_flag"; then
+ # See comment for -static flag below, for more details.
+ func_append compile_command " $link_static_flag"
+ func_append finalize_command " $link_static_flag"
+ fi
+ continue
+ ;;
+
+ -allow-undefined)
+ # FIXME: remove this flag sometime in the future.
+ func_fatal_error "'-allow-undefined' must not be used because it is the default"
+ ;;
+
+ -avoid-version)
+ avoid_version=yes
+ continue
+ ;;
+
+ -bindir)
+ prev=bindir
+ continue
+ ;;
+
+ -dlopen)
+ prev=dlfiles
+ continue
+ ;;
+
+ -dlpreopen)
+ prev=dlprefiles
+ continue
+ ;;
+
+ -export-dynamic)
+ export_dynamic=yes
+ continue
+ ;;
+
+ -export-symbols | -export-symbols-regex)
+ if test -n "$export_symbols" || test -n "$export_symbols_regex"; then
+ func_fatal_error "more than one -exported-symbols argument is not allowed"
+ fi
+ if test X-export-symbols = "X$arg"; then
+ prev=expsyms
+ else
+ prev=expsyms_regex
+ fi
+ continue
+ ;;
+
+ -framework)
+ prev=framework
+ continue
+ ;;
+
+ -inst-prefix-dir)
+ prev=inst_prefix
+ continue
+ ;;
+
+ # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:*
+ # so, if we see these flags be careful not to treat them like -L
+ -L[A-Z][A-Z]*:*)
+ case $with_gcc/$host in
+ no/*-*-irix* | /*-*-irix*)
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ ;;
+ esac
+ continue
+ ;;
+
+ -L*)
+ func_stripname "-L" '' "$arg"
+ if test -z "$func_stripname_result"; then
+ if test "$#" -gt 0; then
+ func_fatal_error "require no space between '-L' and '$1'"
+ else
+ func_fatal_error "need path for '-L' option"
+ fi
+ fi
+ func_resolve_sysroot "$func_stripname_result"
+ dir=$func_resolve_sysroot_result
+ # We need an absolute path.
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ *)
+ absdir=`cd "$dir" && pwd`
+ test -z "$absdir" && \
+ func_fatal_error "cannot determine absolute directory name of '$dir'"
+ dir=$absdir
+ ;;
+ esac
+ case "$deplibs " in
+ *" -L$dir "* | *" $arg "*)
+ # Will only happen for absolute or sysroot arguments
+ ;;
+ *)
+ # Preserve sysroot, but never include relative directories
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;;
+ *) func_append deplibs " -L$dir" ;;
+ esac
+ func_append lib_search_path " $dir"
+ ;;
+ esac
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'`
+ case :$dllsearchpath: in
+ *":$dir:"*) ;;
+ ::) dllsearchpath=$dir;;
+ *) func_append dllsearchpath ":$dir";;
+ esac
+ case :$dllsearchpath: in
+ *":$testbindir:"*) ;;
+ ::) dllsearchpath=$testbindir;;
+ *) func_append dllsearchpath ":$testbindir";;
+ esac
+ ;;
+ esac
+ continue
+ ;;
+
+ -l*)
+ if test X-lc = "X$arg" || test X-lm = "X$arg"; then
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*)
+ # These systems don't actually have a C or math library (as such)
+ continue
+ ;;
+ *-*-os2*)
+ # These systems don't actually have a C library (as such)
+ test X-lc = "X$arg" && continue
+ ;;
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*)
+ # Do not include libc due to us having libc/libc_r.
+ test X-lc = "X$arg" && continue
+ ;;
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # Rhapsody C and math libraries are in the System framework
+ func_append deplibs " System.ltframework"
+ continue
+ ;;
+ *-*-sco3.2v5* | *-*-sco5v6*)
+ # Causes problems with __ctype
+ test X-lc = "X$arg" && continue
+ ;;
+ *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+ # Compiler inserts libc in the correct place for threads to work
+ test X-lc = "X$arg" && continue
+ ;;
+ esac
+ elif test X-lc_r = "X$arg"; then
+ case $host in
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*)
+ # Do not include libc_r directly, use -pthread flag.
+ continue
+ ;;
+ esac
+ fi
+ func_append deplibs " $arg"
+ continue
+ ;;
+
+ -mllvm)
+ prev=mllvm
+ continue
+ ;;
+
+ -module)
+ module=yes
+ continue
+ ;;
+
+ # Tru64 UNIX uses -model [arg] to determine the layout of C++
+ # classes, name mangling, and exception handling.
+ # Darwin uses the -arch flag to determine output architecture.
+ -model|-arch|-isysroot|--sysroot)
+ func_append compiler_flags " $arg"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ prev=xcompiler
+ continue
+ ;;
+
+ -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \
+ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
+ func_append compiler_flags " $arg"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ case "$new_inherited_linker_flags " in
+ *" $arg "*) ;;
+ * ) func_append new_inherited_linker_flags " $arg" ;;
+ esac
+ continue
+ ;;
+
+ -multi_module)
+ single_module=$wl-multi_module
+ continue
+ ;;
+
+ -no-fast-install)
+ fast_install=no
+ continue
+ ;;
+
+ -no-install)
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*)
+ # The PATH hackery in wrapper scripts is required on Windows
+ # and Darwin in order for the loader to find any dlls it needs.
+ func_warning "'-no-install' is ignored for $host"
+ func_warning "assuming '-no-fast-install' instead"
+ fast_install=no
+ ;;
+ *) no_install=yes ;;
+ esac
+ continue
+ ;;
+
+ -no-undefined)
+ allow_undefined=no
+ continue
+ ;;
+
+ -objectlist)
+ prev=objectlist
+ continue
+ ;;
+
+ -os2dllname)
+ prev=os2dllname
+ continue
+ ;;
+
+ -o) prev=output ;;
+
+ -precious-files-regex)
+ prev=precious_regex
+ continue
+ ;;
+
+ -release)
+ prev=release
+ continue
+ ;;
+
+ -rpath)
+ prev=rpath
+ continue
+ ;;
+
+ -R)
+ prev=xrpath
+ continue
+ ;;
+
+ -R*)
+ func_stripname '-R' '' "$arg"
+ dir=$func_stripname_result
+ # We need an absolute path.
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]*) ;;
+ =*)
+ func_stripname '=' '' "$dir"
+ dir=$lt_sysroot$func_stripname_result
+ ;;
+ *)
+ func_fatal_error "only absolute run-paths are allowed"
+ ;;
+ esac
+ case "$xrpath " in
+ *" $dir "*) ;;
+ *) func_append xrpath " $dir" ;;
+ esac
+ continue
+ ;;
+
+ -shared)
+ # The effects of -shared are defined in a previous loop.
+ continue
+ ;;
+
+ -shrext)
+ prev=shrext
+ continue
+ ;;
+
+ -static | -static-libtool-libs)
+ # The effects of -static are defined in a previous loop.
+ # We used to do the same as -all-static on platforms that
+ # didn't have a PIC flag, but the assumption that the effects
+ # would be equivalent was wrong. It would break on at least
+ # Digital Unix and AIX.
+ continue
+ ;;
+
+ -thread-safe)
+ thread_safe=yes
+ continue
+ ;;
+
+ -version-info)
+ prev=vinfo
+ continue
+ ;;
+
+ -version-number)
+ prev=vinfo
+ vinfo_number=yes
+ continue
+ ;;
+
+ -weak)
+ prev=weak
+ continue
+ ;;
+
+ -Wc,*)
+ func_stripname '-Wc,' '' "$arg"
+ args=$func_stripname_result
+ arg=
+ save_ifs=$IFS; IFS=,
+ for flag in $args; do
+ IFS=$save_ifs
+ func_quote_for_eval "$flag"
+ func_append arg " $func_quote_for_eval_result"
+ func_append compiler_flags " $func_quote_for_eval_result"
+ done
+ IFS=$save_ifs
+ func_stripname ' ' '' "$arg"
+ arg=$func_stripname_result
+ ;;
+
+ -Wl,*)
+ func_stripname '-Wl,' '' "$arg"
+ args=$func_stripname_result
+ arg=
+ save_ifs=$IFS; IFS=,
+ for flag in $args; do
+ IFS=$save_ifs
+ func_quote_for_eval "$flag"
+ func_append arg " $wl$func_quote_for_eval_result"
+ func_append compiler_flags " $wl$func_quote_for_eval_result"
+ func_append linker_flags " $func_quote_for_eval_result"
+ done
+ IFS=$save_ifs
+ func_stripname ' ' '' "$arg"
+ arg=$func_stripname_result
+ ;;
+
+ -Xcompiler)
+ prev=xcompiler
+ continue
+ ;;
+
+ -Xlinker)
+ prev=xlinker
+ continue
+ ;;
+
+ -XCClinker)
+ prev=xcclinker
+ continue
+ ;;
+
+ # -msg_* for osf cc
+ -msg_*)
+ func_quote_for_eval "$arg"
+ arg=$func_quote_for_eval_result
+ ;;
+
+ # Flags to be passed through unchanged, with rationale:
+ # -64, -mips[0-9] enable 64-bit mode for the SGI compiler
+ # -r[0-9][0-9]* specify processor for the SGI compiler
+ # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler
+ # +DA*, +DD* enable 64-bit mode for the HP compiler
+ # -q* compiler args for the IBM compiler
+ # -m*, -t[45]*, -txscale* architecture-specific flags for GCC
+ # -F/path path to uninstalled frameworks, gcc on darwin
+ # -p, -pg, --coverage, -fprofile-* profiling flags for GCC
+ # -fstack-protector* stack protector flags for GCC
+ # @file GCC response files
+ # -tp=* Portland pgcc target processor selection
+ # --sysroot=* for sysroot support
+ # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization
+ # -stdlib=* select c++ std lib with clang
+ -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
+ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \
+ -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*)
+ func_quote_for_eval "$arg"
+ arg=$func_quote_for_eval_result
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ func_append compiler_flags " $arg"
+ continue
+ ;;
+
+ -Z*)
+ if test os2 = "`expr $host : '.*\(os2\)'`"; then
+ # OS/2 uses -Zxxx to specify OS/2-specific options
+ compiler_flags="$compiler_flags $arg"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ case $arg in
+ -Zlinker | -Zstack)
+ prev=xcompiler
+ ;;
+ esac
+ continue
+ else
+ # Otherwise treat like 'Some other compiler flag' below
+ func_quote_for_eval "$arg"
+ arg=$func_quote_for_eval_result
+ fi
+ ;;
+
+ # Some other compiler flag.
+ -* | +*)
+ func_quote_for_eval "$arg"
+ arg=$func_quote_for_eval_result
+ ;;
+
+ *.$objext)
+ # A standard object.
+ func_append objs " $arg"
+ ;;
+
+ *.lo)
+ # A libtool-controlled object.
+
+ # Check to see that this really is a libtool object.
+ if func_lalib_unsafe_p "$arg"; then
+ pic_object=
+ non_pic_object=
+
+ # Read the .lo file
+ func_source "$arg"
+
+ if test -z "$pic_object" ||
+ test -z "$non_pic_object" ||
+ test none = "$pic_object" &&
+ test none = "$non_pic_object"; then
+ func_fatal_error "cannot find name of object for '$arg'"
+ fi
+
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir=$func_dirname_result
+
+ test none = "$pic_object" || {
+ # Prepend the subdirectory the object is found in.
+ pic_object=$xdir$pic_object
+
+ if test dlfiles = "$prev"; then
+ if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then
+ func_append dlfiles " $pic_object"
+ prev=
+ continue
+ else
+ # If libtool objects are unsupported, then we need to preload.
+ prev=dlprefiles
+ fi
+ fi
+
+ # CHECK ME: I think I busted this. -Ossama
+ if test dlprefiles = "$prev"; then
+ # Preload the old-style object.
+ func_append dlprefiles " $pic_object"
+ prev=
+ fi
+
+ # A PIC object.
+ func_append libobjs " $pic_object"
+ arg=$pic_object
+ }
+
+ # Non-PIC object.
+ if test none != "$non_pic_object"; then
+ # Prepend the subdirectory the object is found in.
+ non_pic_object=$xdir$non_pic_object
+
+ # A standard non-PIC object
+ func_append non_pic_objects " $non_pic_object"
+ if test -z "$pic_object" || test none = "$pic_object"; then
+ arg=$non_pic_object
+ fi
+ else
+ # If the PIC object exists, use it instead.
+ # $xdir was prepended to $pic_object above.
+ non_pic_object=$pic_object
+ func_append non_pic_objects " $non_pic_object"
+ fi
+ else
+ # Only an error if not doing a dry-run.
+ if $opt_dry_run; then
+ # Extract subdirectory from the argument.
+ func_dirname "$arg" "/" ""
+ xdir=$func_dirname_result
+
+ func_lo2o "$arg"
+ pic_object=$xdir$objdir/$func_lo2o_result
+ non_pic_object=$xdir$func_lo2o_result
+ func_append libobjs " $pic_object"
+ func_append non_pic_objects " $non_pic_object"
+ else
+ func_fatal_error "'$arg' is not a valid libtool object"
+ fi
+ fi
+ ;;
+
+ *.$libext)
+ # An archive.
+ func_append deplibs " $arg"
+ func_append old_deplibs " $arg"
+ continue
+ ;;
+
+ *.la)
+ # A libtool-controlled library.
+
+ func_resolve_sysroot "$arg"
+ if test dlfiles = "$prev"; then
+ # This library was specified with -dlopen.
+ func_append dlfiles " $func_resolve_sysroot_result"
+ prev=
+ elif test dlprefiles = "$prev"; then
+ # The library was specified with -dlpreopen.
+ func_append dlprefiles " $func_resolve_sysroot_result"
+ prev=
+ else
+ func_append deplibs " $func_resolve_sysroot_result"
+ fi
+ continue
+ ;;
+
+ # Some other compiler argument.
+ *)
+ # Unknown arguments in both finalize_command and compile_command need
+ # to be aesthetically quoted because they are evaled later.
+ func_quote_for_eval "$arg"
+ arg=$func_quote_for_eval_result
+ ;;
+ esac # arg
+
+ # Now actually substitute the argument into the commands.
+ if test -n "$arg"; then
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ fi
+ done # argument parsing loop
+
+ test -n "$prev" && \
+ func_fatal_help "the '$prevarg' option requires an argument"
+
+ if test yes = "$export_dynamic" && test -n "$export_dynamic_flag_spec"; then
+ eval arg=\"$export_dynamic_flag_spec\"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ fi
+
+ oldlibs=
+ # calculate the name of the file, without its directory
+ func_basename "$output"
+ outputname=$func_basename_result
+ libobjs_save=$libobjs
+
+ if test -n "$shlibpath_var"; then
+ # get the directories listed in $shlibpath_var
+ eval shlib_search_path=\`\$ECHO \"\$$shlibpath_var\" \| \$SED \'s/:/ /g\'\`
+ else
+ shlib_search_path=
+ fi
+ eval sys_lib_search_path=\"$sys_lib_search_path_spec\"
+ eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\"
+
+ # Definition is injected by LT_CONFIG during libtool generation.
+ func_munge_path_list sys_lib_dlsearch_path "$LT_SYS_LIBRARY_PATH"
+
+ func_dirname "$output" "/" ""
+ output_objdir=$func_dirname_result$objdir
+ func_to_tool_file "$output_objdir/"
+ tool_output_objdir=$func_to_tool_file_result
+ # Create the object directory.
+ func_mkdir_p "$output_objdir"
+
+ # Determine the type of output
+ case $output in
+ "")
+ func_fatal_help "you must specify an output file"
+ ;;
+ *.$libext) linkmode=oldlib ;;
+ *.lo | *.$objext) linkmode=obj ;;
+ *.la) linkmode=lib ;;
+ *) linkmode=prog ;; # Anything else should be a program.
+ esac
+
+ specialdeplibs=
+
+ libs=
+ # Find all interdependent deplibs by searching for libraries
+ # that are linked more than once (e.g. -la -lb -la)
+ for deplib in $deplibs; do
+ if $opt_preserve_dup_deps; then
+ case "$libs " in
+ *" $deplib "*) func_append specialdeplibs " $deplib" ;;
+ esac
+ fi
+ func_append libs " $deplib"
+ done
+
+ if test lib = "$linkmode"; then
+ libs="$predeps $libs $compiler_lib_search_path $postdeps"
+
+ # Compute libraries that are listed more than once in $predeps
+ # $postdeps and mark them as special (i.e., whose duplicates are
+ # not to be eliminated).
+ pre_post_deps=
+ if $opt_duplicate_compiler_generated_deps; then
+ for pre_post_dep in $predeps $postdeps; do
+ case "$pre_post_deps " in
+ *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;;
+ esac
+ func_append pre_post_deps " $pre_post_dep"
+ done
+ fi
+ pre_post_deps=
+ fi
+
+ deplibs=
+ newdependency_libs=
+ newlib_search_path=
+ need_relink=no # whether we're linking any uninstalled libtool libraries
+ notinst_deplibs= # not-installed libtool libraries
+ notinst_path= # paths that contain not-installed libtool libraries
+
+ case $linkmode in
+ lib)
+ passes="conv dlpreopen link"
+ for file in $dlfiles $dlprefiles; do
+ case $file in
+ *.la) ;;
+ *)
+ func_fatal_help "libraries can '-dlopen' only libtool libraries: $file"
+ ;;
+ esac
+ done
+ ;;
+ prog)
+ compile_deplibs=
+ finalize_deplibs=
+ alldeplibs=false
+ newdlfiles=
+ newdlprefiles=
+ passes="conv scan dlopen dlpreopen link"
+ ;;
+ *) passes="conv"
+ ;;
+ esac
+
+ for pass in $passes; do
+ # The preopen pass in lib mode reverses $deplibs; put it back here
+ # so that -L comes before libs that need it for instance...
+ if test lib,link = "$linkmode,$pass"; then
+ ## FIXME: Find the place where the list is rebuilt in the wrong
+ ## order, and fix it there properly
+ tmp_deplibs=
+ for deplib in $deplibs; do
+ tmp_deplibs="$deplib $tmp_deplibs"
+ done
+ deplibs=$tmp_deplibs
+ fi
+
+ if test lib,link = "$linkmode,$pass" ||
+ test prog,scan = "$linkmode,$pass"; then
+ libs=$deplibs
+ deplibs=
+ fi
+ if test prog = "$linkmode"; then
+ case $pass in
+ dlopen) libs=$dlfiles ;;
+ dlpreopen) libs=$dlprefiles ;;
+ link) libs="$deplibs %DEPLIBS% $dependency_libs" ;;
+ esac
+ fi
+ if test lib,dlpreopen = "$linkmode,$pass"; then
+ # Collect and forward deplibs of preopened libtool libs
+ for lib in $dlprefiles; do
+ # Ignore non-libtool-libs
+ dependency_libs=
+ func_resolve_sysroot "$lib"
+ case $lib in
+ *.la) func_source "$func_resolve_sysroot_result" ;;
+ esac
+
+ # Collect preopened libtool deplibs, except any this library
+ # has declared as weak libs
+ for deplib in $dependency_libs; do
+ func_basename "$deplib"
+ deplib_base=$func_basename_result
+ case " $weak_libs " in
+ *" $deplib_base "*) ;;
+ *) func_append deplibs " $deplib" ;;
+ esac
+ done
+ done
+ libs=$dlprefiles
+ fi
+ if test dlopen = "$pass"; then
+ # Collect dlpreopened libraries
+ save_deplibs=$deplibs
+ deplibs=
+ fi
+
+ for deplib in $libs; do
+ lib=
+ found=false
+ case $deplib in
+ -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \
+ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
+ if test prog,link = "$linkmode,$pass"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ func_append compiler_flags " $deplib"
+ if test lib = "$linkmode"; then
+ case "$new_inherited_linker_flags " in
+ *" $deplib "*) ;;
+ * ) func_append new_inherited_linker_flags " $deplib" ;;
+ esac
+ fi
+ fi
+ continue
+ ;;
+ -l*)
+ if test lib != "$linkmode" && test prog != "$linkmode"; then
+ func_warning "'-l' is ignored for archives/objects"
+ continue
+ fi
+ func_stripname '-l' '' "$deplib"
+ name=$func_stripname_result
+ if test lib = "$linkmode"; then
+ searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path"
+ else
+ searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path"
+ fi
+ for searchdir in $searchdirs; do
+ for search_ext in .la $std_shrext .so .a; do
+ # Search the libtool library
+ lib=$searchdir/lib$name$search_ext
+ if test -f "$lib"; then
+ if test .la = "$search_ext"; then
+ found=:
+ else
+ found=false
+ fi
+ break 2
+ fi
+ done
+ done
+ if $found; then
+ # deplib is a libtool library
+ # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib,
+ # We need to do some special things here, and not later.
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+ case " $predeps $postdeps " in
+ *" $deplib "*)
+ if func_lalib_p "$lib"; then
+ library_names=
+ old_library=
+ func_source "$lib"
+ for l in $old_library $library_names; do
+ ll=$l
+ done
+ if test "X$ll" = "X$old_library"; then # only static version available
+ found=false
+ func_dirname "$lib" "" "."
+ ladir=$func_dirname_result
+ lib=$ladir/$old_library
+ if test prog,link = "$linkmode,$pass"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ deplibs="$deplib $deplibs"
+ test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs"
+ fi
+ continue
+ fi
+ fi
+ ;;
+ *) ;;
+ esac
+ fi
+ else
+ # deplib doesn't seem to be a libtool library
+ if test prog,link = "$linkmode,$pass"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ deplibs="$deplib $deplibs"
+ test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs"
+ fi
+ continue
+ fi
+ ;; # -l
+ *.ltframework)
+ if test prog,link = "$linkmode,$pass"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ deplibs="$deplib $deplibs"
+ if test lib = "$linkmode"; then
+ case "$new_inherited_linker_flags " in
+ *" $deplib "*) ;;
+ * ) func_append new_inherited_linker_flags " $deplib" ;;
+ esac
+ fi
+ fi
+ continue
+ ;;
+ -L*)
+ case $linkmode in
+ lib)
+ deplibs="$deplib $deplibs"
+ test conv = "$pass" && continue
+ newdependency_libs="$deplib $newdependency_libs"
+ func_stripname '-L' '' "$deplib"
+ func_resolve_sysroot "$func_stripname_result"
+ func_append newlib_search_path " $func_resolve_sysroot_result"
+ ;;
+ prog)
+ if test conv = "$pass"; then
+ deplibs="$deplib $deplibs"
+ continue
+ fi
+ if test scan = "$pass"; then
+ deplibs="$deplib $deplibs"
+ else
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ fi
+ func_stripname '-L' '' "$deplib"
+ func_resolve_sysroot "$func_stripname_result"
+ func_append newlib_search_path " $func_resolve_sysroot_result"
+ ;;
+ *)
+ func_warning "'-L' is ignored for archives/objects"
+ ;;
+ esac # linkmode
+ continue
+ ;; # -L
+ -R*)
+ if test link = "$pass"; then
+ func_stripname '-R' '' "$deplib"
+ func_resolve_sysroot "$func_stripname_result"
+ dir=$func_resolve_sysroot_result
+ # Make sure the xrpath contains only unique directories.
+ case "$xrpath " in
+ *" $dir "*) ;;
+ *) func_append xrpath " $dir" ;;
+ esac
+ fi
+ deplibs="$deplib $deplibs"
+ continue
+ ;;
+ *.la)
+ func_resolve_sysroot "$deplib"
+ lib=$func_resolve_sysroot_result
+ ;;
+ *.$libext)
+ if test conv = "$pass"; then
+ deplibs="$deplib $deplibs"
+ continue
+ fi
+ case $linkmode in
+ lib)
+ # Linking convenience modules into shared libraries is allowed,
+ # but linking other static libraries is non-portable.
+ case " $dlpreconveniencelibs " in
+ *" $deplib "*) ;;
+ *)
+ valid_a_lib=false
+ case $deplibs_check_method in
+ match_pattern*)
+ set dummy $deplibs_check_method; shift
+ match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+ if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \
+ | $EGREP "$match_pattern_regex" > /dev/null; then
+ valid_a_lib=:
+ fi
+ ;;
+ pass_all)
+ valid_a_lib=:
+ ;;
+ esac
+ if $valid_a_lib; then
+ echo
+ $ECHO "*** Warning: Linking the shared library $output against the"
+ $ECHO "*** static library $deplib is not portable!"
+ deplibs="$deplib $deplibs"
+ else
+ echo
+ $ECHO "*** Warning: Trying to link with static lib archive $deplib."
+ echo "*** I have the capability to make that library automatically link in when"
+ echo "*** you link to this library. But I can only do this if you have a"
+ echo "*** shared version of the library, which you do not appear to have"
+ echo "*** because the file extensions .$libext of this argument makes me believe"
+ echo "*** that it is just a static archive that I should not use here."
+ fi
+ ;;
+ esac
+ continue
+ ;;
+ prog)
+ if test link != "$pass"; then
+ deplibs="$deplib $deplibs"
+ else
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ fi
+ continue
+ ;;
+ esac # linkmode
+ ;; # *.$libext
+ *.lo | *.$objext)
+ if test conv = "$pass"; then
+ deplibs="$deplib $deplibs"
+ elif test prog = "$linkmode"; then
+ if test dlpreopen = "$pass" || test yes != "$dlopen_support" || test no = "$build_libtool_libs"; then
+ # If there is no dlopen support or we're linking statically,
+ # we need to preload.
+ func_append newdlprefiles " $deplib"
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ func_append newdlfiles " $deplib"
+ fi
+ fi
+ continue
+ ;;
+ %DEPLIBS%)
+ alldeplibs=:
+ continue
+ ;;
+ esac # case $deplib
+
+ $found || test -f "$lib" \
+ || func_fatal_error "cannot find the library '$lib' or unhandled argument '$deplib'"
+
+ # Check to see that this really is a libtool archive.
+ func_lalib_unsafe_p "$lib" \
+ || func_fatal_error "'$lib' is not a valid libtool archive"
+
+ func_dirname "$lib" "" "."
+ ladir=$func_dirname_result
+
+ dlname=
+ dlopen=
+ dlpreopen=
+ libdir=
+ library_names=
+ old_library=
+ inherited_linker_flags=
+ # If the library was installed with an old release of libtool,
+ # it will not redefine variables installed, or shouldnotlink
+ installed=yes
+ shouldnotlink=no
+ avoidtemprpath=
+
+
+ # Read the .la file
+ func_source "$lib"
+
+ # Convert "-framework foo" to "foo.ltframework"
+ if test -n "$inherited_linker_flags"; then
+ tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'`
+ for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do
+ case " $new_inherited_linker_flags " in
+ *" $tmp_inherited_linker_flag "*) ;;
+ *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";;
+ esac
+ done
+ fi
+ dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ if test lib,link = "$linkmode,$pass" ||
+ test prog,scan = "$linkmode,$pass" ||
+ { test prog != "$linkmode" && test lib != "$linkmode"; }; then
+ test -n "$dlopen" && func_append dlfiles " $dlopen"
+ test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen"
+ fi
+
+ if test conv = "$pass"; then
+ # Only check for convenience libraries
+ deplibs="$lib $deplibs"
+ if test -z "$libdir"; then
+ if test -z "$old_library"; then
+ func_fatal_error "cannot find name of link library for '$lib'"
+ fi
+ # It is a libtool convenience library, so add in its objects.
+ func_append convenience " $ladir/$objdir/$old_library"
+ func_append old_convenience " $ladir/$objdir/$old_library"
+ elif test prog != "$linkmode" && test lib != "$linkmode"; then
+ func_fatal_error "'$lib' is not a convenience library"
+ fi
+ tmp_libs=
+ for deplib in $dependency_libs; do
+ deplibs="$deplib $deplibs"
+ if $opt_preserve_dup_deps; then
+ case "$tmp_libs " in
+ *" $deplib "*) func_append specialdeplibs " $deplib" ;;
+ esac
+ fi
+ func_append tmp_libs " $deplib"
+ done
+ continue
+ fi # $pass = conv
+
+
+ # Get the name of the library we link against.
+ linklib=
+ if test -n "$old_library" &&
+ { test yes = "$prefer_static_libs" ||
+ test built,no = "$prefer_static_libs,$installed"; }; then
+ linklib=$old_library
+ else
+ for l in $old_library $library_names; do
+ linklib=$l
+ done
+ fi
+ if test -z "$linklib"; then
+ func_fatal_error "cannot find name of link library for '$lib'"
+ fi
+
+ # This library was specified with -dlopen.
+ if test dlopen = "$pass"; then
+ test -z "$libdir" \
+ && func_fatal_error "cannot -dlopen a convenience library: '$lib'"
+ if test -z "$dlname" ||
+ test yes != "$dlopen_support" ||
+ test no = "$build_libtool_libs"
+ then
+ # If there is no dlname, no dlopen support or we're linking
+ # statically, we need to preload. We also need to preload any
+ # dependent libraries so libltdl's deplib preloader doesn't
+ # bomb out in the load deplibs phase.
+ func_append dlprefiles " $lib $dependency_libs"
+ else
+ func_append newdlfiles " $lib"
+ fi
+ continue
+ fi # $pass = dlopen
+
+ # We need an absolute path.
+ case $ladir in
+ [\\/]* | [A-Za-z]:[\\/]*) abs_ladir=$ladir ;;
+ *)
+ abs_ladir=`cd "$ladir" && pwd`
+ if test -z "$abs_ladir"; then
+ func_warning "cannot determine absolute directory name of '$ladir'"
+ func_warning "passing it literally to the linker, although it might fail"
+ abs_ladir=$ladir
+ fi
+ ;;
+ esac
+ func_basename "$lib"
+ laname=$func_basename_result
+
+ # Find the relevant object directory and library name.
+ if test yes = "$installed"; then
+ if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+ func_warning "library '$lib' was moved."
+ dir=$ladir
+ absdir=$abs_ladir
+ libdir=$abs_ladir
+ else
+ dir=$lt_sysroot$libdir
+ absdir=$lt_sysroot$libdir
+ fi
+ test yes = "$hardcode_automatic" && avoidtemprpath=yes
+ else
+ if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+ dir=$ladir
+ absdir=$abs_ladir
+ # Remove this search path later
+ func_append notinst_path " $abs_ladir"
+ else
+ dir=$ladir/$objdir
+ absdir=$abs_ladir/$objdir
+ # Remove this search path later
+ func_append notinst_path " $abs_ladir"
+ fi
+ fi # $installed = yes
+ func_stripname 'lib' '.la' "$laname"
+ name=$func_stripname_result
+
+ # This library was specified with -dlpreopen.
+ if test dlpreopen = "$pass"; then
+ if test -z "$libdir" && test prog = "$linkmode"; then
+ func_fatal_error "only libraries may -dlpreopen a convenience library: '$lib'"
+ fi
+ case $host in
+ # special handling for platforms with PE-DLLs.
+ *cygwin* | *mingw* | *cegcc* )
+ # Linker will automatically link against shared library if both
+ # static and shared are present. Therefore, ensure we extract
+ # symbols from the import library if a shared library is present
+ # (otherwise, the dlopen module name will be incorrect). We do
+ # this by putting the import library name into $newdlprefiles.
+ # We recover the dlopen module name by 'saving' the la file
+ # name in a special purpose variable, and (later) extracting the
+ # dlname from the la file.
+ if test -n "$dlname"; then
+ func_tr_sh "$dir/$linklib"
+ eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname"
+ func_append newdlprefiles " $dir/$linklib"
+ else
+ func_append newdlprefiles " $dir/$old_library"
+ # Keep a list of preopened convenience libraries to check
+ # that they are being used correctly in the link pass.
+ test -z "$libdir" && \
+ func_append dlpreconveniencelibs " $dir/$old_library"
+ fi
+ ;;
+ * )
+ # Prefer using a static library (so that no silly _DYNAMIC symbols
+ # are required to link).
+ if test -n "$old_library"; then
+ func_append newdlprefiles " $dir/$old_library"
+ # Keep a list of preopened convenience libraries to check
+ # that they are being used correctly in the link pass.
+ test -z "$libdir" && \
+ func_append dlpreconveniencelibs " $dir/$old_library"
+ # Otherwise, use the dlname, so that lt_dlopen finds it.
+ elif test -n "$dlname"; then
+ func_append newdlprefiles " $dir/$dlname"
+ else
+ func_append newdlprefiles " $dir/$linklib"
+ fi
+ ;;
+ esac
+ fi # $pass = dlpreopen
+
+ if test -z "$libdir"; then
+ # Link the convenience library
+ if test lib = "$linkmode"; then
+ deplibs="$dir/$old_library $deplibs"
+ elif test prog,link = "$linkmode,$pass"; then
+ compile_deplibs="$dir/$old_library $compile_deplibs"
+ finalize_deplibs="$dir/$old_library $finalize_deplibs"
+ else
+ deplibs="$lib $deplibs" # used for prog,scan pass
+ fi
+ continue
+ fi
+
+
+ if test prog = "$linkmode" && test link != "$pass"; then
+ func_append newlib_search_path " $ladir"
+ deplibs="$lib $deplibs"
+
+ linkalldeplibs=false
+ if test no != "$link_all_deplibs" || test -z "$library_names" ||
+ test no = "$build_libtool_libs"; then
+ linkalldeplibs=:
+ fi
+
+ tmp_libs=
+ for deplib in $dependency_libs; do
+ case $deplib in
+ -L*) func_stripname '-L' '' "$deplib"
+ func_resolve_sysroot "$func_stripname_result"
+ func_append newlib_search_path " $func_resolve_sysroot_result"
+ ;;
+ esac
+ # Need to link against all dependency_libs?
+ if $linkalldeplibs; then
+ deplibs="$deplib $deplibs"
+ else
+ # Need to hardcode shared library paths
+ # or/and link against static libraries
+ newdependency_libs="$deplib $newdependency_libs"
+ fi
+ if $opt_preserve_dup_deps; then
+ case "$tmp_libs " in
+ *" $deplib "*) func_append specialdeplibs " $deplib" ;;
+ esac
+ fi
+ func_append tmp_libs " $deplib"
+ done # for deplib
+ continue
+ fi # $linkmode = prog...
+
+ if test prog,link = "$linkmode,$pass"; then
+ if test -n "$library_names" &&
+ { { test no = "$prefer_static_libs" ||
+ test built,yes = "$prefer_static_libs,$installed"; } ||
+ test -z "$old_library"; }; then
+ # We need to hardcode the library path
+ if test -n "$shlibpath_var" && test -z "$avoidtemprpath"; then
+ # Make sure the rpath contains only unique directories.
+ case $temp_rpath: in
+ *"$absdir:"*) ;;
+ *) func_append temp_rpath "$absdir:" ;;
+ esac
+ fi
+
+ # Hardcode the library path.
+ # Skip directories that are in the system default run-time
+ # search path.
+ case " $sys_lib_dlsearch_path " in
+ *" $absdir "*) ;;
+ *)
+ case "$compile_rpath " in
+ *" $absdir "*) ;;
+ *) func_append compile_rpath " $absdir" ;;
+ esac
+ ;;
+ esac
+ case " $sys_lib_dlsearch_path " in
+ *" $libdir "*) ;;
+ *)
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) func_append finalize_rpath " $libdir" ;;
+ esac
+ ;;
+ esac
+ fi # $linkmode,$pass = prog,link...
+
+ if $alldeplibs &&
+ { test pass_all = "$deplibs_check_method" ||
+ { test yes = "$build_libtool_libs" &&
+ test -n "$library_names"; }; }; then
+ # We only need to search for static libraries
+ continue
+ fi
+ fi
+
+ link_static=no # Whether the deplib will be linked statically
+ use_static_libs=$prefer_static_libs
+ if test built = "$use_static_libs" && test yes = "$installed"; then
+ use_static_libs=no
+ fi
+ if test -n "$library_names" &&
+ { test no = "$use_static_libs" || test -z "$old_library"; }; then
+ case $host in
+ *cygwin* | *mingw* | *cegcc* | *os2*)
+ # No point in relinking DLLs because paths are not encoded
+ func_append notinst_deplibs " $lib"
+ need_relink=no
+ ;;
+ *)
+ if test no = "$installed"; then
+ func_append notinst_deplibs " $lib"
+ need_relink=yes
+ fi
+ ;;
+ esac
+ # This is a shared library
+
+ # Warn about portability, can't link against -module's on some
+ # systems (darwin). Don't bleat about dlopened modules though!
+ dlopenmodule=
+ for dlpremoduletest in $dlprefiles; do
+ if test "X$dlpremoduletest" = "X$lib"; then
+ dlopenmodule=$dlpremoduletest
+ break
+ fi
+ done
+ if test -z "$dlopenmodule" && test yes = "$shouldnotlink" && test link = "$pass"; then
+ echo
+ if test prog = "$linkmode"; then
+ $ECHO "*** Warning: Linking the executable $output against the loadable module"
+ else
+ $ECHO "*** Warning: Linking the shared library $output against the loadable module"
+ fi
+ $ECHO "*** $linklib is not portable!"
+ fi
+ if test lib = "$linkmode" &&
+ test yes = "$hardcode_into_libs"; then
+ # Hardcode the library path.
+ # Skip directories that are in the system default run-time
+ # search path.
+ case " $sys_lib_dlsearch_path " in
+ *" $absdir "*) ;;
+ *)
+ case "$compile_rpath " in
+ *" $absdir "*) ;;
+ *) func_append compile_rpath " $absdir" ;;
+ esac
+ ;;
+ esac
+ case " $sys_lib_dlsearch_path " in
+ *" $libdir "*) ;;
+ *)
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) func_append finalize_rpath " $libdir" ;;
+ esac
+ ;;
+ esac
+ fi
+
+ if test -n "$old_archive_from_expsyms_cmds"; then
+ # figure out the soname
+ set dummy $library_names
+ shift
+ realname=$1
+ shift
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ # use dlname if we got it. it's perfectly good, no?
+ if test -n "$dlname"; then
+ soname=$dlname
+ elif test -n "$soname_spec"; then
+ # bleh windows
+ case $host in
+ *cygwin* | mingw* | *cegcc* | *os2*)
+ func_arith $current - $age
+ major=$func_arith_result
+ versuffix=-$major
+ ;;
+ esac
+ eval soname=\"$soname_spec\"
+ else
+ soname=$realname
+ fi
+
+ # Make a new name for the extract_expsyms_cmds to use
+ soroot=$soname
+ func_basename "$soroot"
+ soname=$func_basename_result
+ func_stripname 'lib' '.dll' "$soname"
+ newlib=libimp-$func_stripname_result.a
+
+ # If the library has no export list, then create one now
+ if test -f "$output_objdir/$soname-def"; then :
+ else
+ func_verbose "extracting exported symbol list from '$soname'"
+ func_execute_cmds "$extract_expsyms_cmds" 'exit $?'
+ fi
+
+ # Create $newlib
+ if test -f "$output_objdir/$newlib"; then :; else
+ func_verbose "generating import library for '$soname'"
+ func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?'
+ fi
+ # make sure the library variables are pointing to the new library
+ dir=$output_objdir
+ linklib=$newlib
+ fi # test -n "$old_archive_from_expsyms_cmds"
+
+ if test prog = "$linkmode" || test relink != "$opt_mode"; then
+ add_shlibpath=
+ add_dir=
+ add=
+ lib_linked=yes
+ case $hardcode_action in
+ immediate | unsupported)
+ if test no = "$hardcode_direct"; then
+ add=$dir/$linklib
+ case $host in
+ *-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;;
+ *-*-sysv4*uw2*) add_dir=-L$dir ;;
+ *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \
+ *-*-unixware7*) add_dir=-L$dir ;;
+ *-*-darwin* )
+ # if the lib is a (non-dlopened) module then we cannot
+ # link against it, someone is ignoring the earlier warnings
+ if /usr/bin/file -L $add 2> /dev/null |
+ $GREP ": [^:]* bundle" >/dev/null; then
+ if test "X$dlopenmodule" != "X$lib"; then
+ $ECHO "*** Warning: lib $linklib is a module, not a shared library"
+ if test -z "$old_library"; then
+ echo
+ echo "*** And there doesn't seem to be a static archive available"
+ echo "*** The link will probably fail, sorry"
+ else
+ add=$dir/$old_library
+ fi
+ elif test -n "$old_library"; then
+ add=$dir/$old_library
+ fi
+ fi
+ esac
+ elif test no = "$hardcode_minus_L"; then
+ case $host in
+ *-*-sunos*) add_shlibpath=$dir ;;
+ esac
+ add_dir=-L$dir
+ add=-l$name
+ elif test no = "$hardcode_shlibpath_var"; then
+ add_shlibpath=$dir
+ add=-l$name
+ else
+ lib_linked=no
+ fi
+ ;;
+ relink)
+ if test yes = "$hardcode_direct" &&
+ test no = "$hardcode_direct_absolute"; then
+ add=$dir/$linklib
+ elif test yes = "$hardcode_minus_L"; then
+ add_dir=-L$absdir
+ # Try looking first in the location we're being installed to.
+ if test -n "$inst_prefix_dir"; then
+ case $libdir in
+ [\\/]*)
+ func_append add_dir " -L$inst_prefix_dir$libdir"
+ ;;
+ esac
+ fi
+ add=-l$name
+ elif test yes = "$hardcode_shlibpath_var"; then
+ add_shlibpath=$dir
+ add=-l$name
+ else
+ lib_linked=no
+ fi
+ ;;
+ *) lib_linked=no ;;
+ esac
+
+ if test yes != "$lib_linked"; then
+ func_fatal_configuration "unsupported hardcode properties"
+ fi
+
+ if test -n "$add_shlibpath"; then
+ case :$compile_shlibpath: in
+ *":$add_shlibpath:"*) ;;
+ *) func_append compile_shlibpath "$add_shlibpath:" ;;
+ esac
+ fi
+ if test prog = "$linkmode"; then
+ test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs"
+ test -n "$add" && compile_deplibs="$add $compile_deplibs"
+ else
+ test -n "$add_dir" && deplibs="$add_dir $deplibs"
+ test -n "$add" && deplibs="$add $deplibs"
+ if test yes != "$hardcode_direct" &&
+ test yes != "$hardcode_minus_L" &&
+ test yes = "$hardcode_shlibpath_var"; then
+ case :$finalize_shlibpath: in
+ *":$libdir:"*) ;;
+ *) func_append finalize_shlibpath "$libdir:" ;;
+ esac
+ fi
+ fi
+ fi
+
+ if test prog = "$linkmode" || test relink = "$opt_mode"; then
+ add_shlibpath=
+ add_dir=
+ add=
+ # Finalize command for both is simple: just hardcode it.
+ if test yes = "$hardcode_direct" &&
+ test no = "$hardcode_direct_absolute"; then
+ add=$libdir/$linklib
+ elif test yes = "$hardcode_minus_L"; then
+ add_dir=-L$libdir
+ add=-l$name
+ elif test yes = "$hardcode_shlibpath_var"; then
+ case :$finalize_shlibpath: in
+ *":$libdir:"*) ;;
+ *) func_append finalize_shlibpath "$libdir:" ;;
+ esac
+ add=-l$name
+ elif test yes = "$hardcode_automatic"; then
+ if test -n "$inst_prefix_dir" &&
+ test -f "$inst_prefix_dir$libdir/$linklib"; then
+ add=$inst_prefix_dir$libdir/$linklib
+ else
+ add=$libdir/$linklib
+ fi
+ else
+ # We cannot seem to hardcode it, guess we'll fake it.
+ add_dir=-L$libdir
+ # Try looking first in the location we're being installed to.
+ if test -n "$inst_prefix_dir"; then
+ case $libdir in
+ [\\/]*)
+ func_append add_dir " -L$inst_prefix_dir$libdir"
+ ;;
+ esac
+ fi
+ add=-l$name
+ fi
+
+ if test prog = "$linkmode"; then
+ test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs"
+ test -n "$add" && finalize_deplibs="$add $finalize_deplibs"
+ else
+ test -n "$add_dir" && deplibs="$add_dir $deplibs"
+ test -n "$add" && deplibs="$add $deplibs"
+ fi
+ fi
+ elif test prog = "$linkmode"; then
+ # Here we assume that one of hardcode_direct or hardcode_minus_L
+ # is not unsupported. This is valid on all known static and
+ # shared platforms.
+ if test unsupported != "$hardcode_direct"; then
+ test -n "$old_library" && linklib=$old_library
+ compile_deplibs="$dir/$linklib $compile_deplibs"
+ finalize_deplibs="$dir/$linklib $finalize_deplibs"
+ else
+ compile_deplibs="-l$name -L$dir $compile_deplibs"
+ finalize_deplibs="-l$name -L$dir $finalize_deplibs"
+ fi
+ elif test yes = "$build_libtool_libs"; then
+ # Not a shared library
+ if test pass_all != "$deplibs_check_method"; then
+ # We're trying link a shared library against a static one
+ # but the system doesn't support it.
+
+ # Just print a warning and add the library to dependency_libs so
+ # that the program can be linked against the static library.
+ echo
+ $ECHO "*** Warning: This system cannot link to static lib archive $lib."
+ echo "*** I have the capability to make that library automatically link in when"
+ echo "*** you link to this library. But I can only do this if you have a"
+ echo "*** shared version of the library, which you do not appear to have."
+ if test yes = "$module"; then
+ echo "*** But as you try to build a module library, libtool will still create "
+ echo "*** a static module, that should work as long as the dlopening application"
+ echo "*** is linked with the -dlopen flag to resolve symbols at runtime."
+ if test -z "$global_symbol_pipe"; then
+ echo
+ echo "*** However, this would only work if libtool was able to extract symbol"
+ echo "*** lists from a program, using 'nm' or equivalent, but libtool could"
+ echo "*** not find such a program. So, this module is probably useless."
+ echo "*** 'nm' from GNU binutils and a full rebuild may help."
+ fi
+ if test no = "$build_old_libs"; then
+ build_libtool_libs=module
+ build_old_libs=yes
+ else
+ build_libtool_libs=no
+ fi
+ fi
+ else
+ deplibs="$dir/$old_library $deplibs"
+ link_static=yes
+ fi
+ fi # link shared/static library?
+
+ if test lib = "$linkmode"; then
+ if test -n "$dependency_libs" &&
+ { test yes != "$hardcode_into_libs" ||
+ test yes = "$build_old_libs" ||
+ test yes = "$link_static"; }; then
+ # Extract -R from dependency_libs
+ temp_deplibs=
+ for libdir in $dependency_libs; do
+ case $libdir in
+ -R*) func_stripname '-R' '' "$libdir"
+ temp_xrpath=$func_stripname_result
+ case " $xrpath " in
+ *" $temp_xrpath "*) ;;
+ *) func_append xrpath " $temp_xrpath";;
+ esac;;
+ *) func_append temp_deplibs " $libdir";;
+ esac
+ done
+ dependency_libs=$temp_deplibs
+ fi
+
+ func_append newlib_search_path " $absdir"
+ # Link against this library
+ test no = "$link_static" && newdependency_libs="$abs_ladir/$laname $newdependency_libs"
+ # ... and its dependency_libs
+ tmp_libs=
+ for deplib in $dependency_libs; do
+ newdependency_libs="$deplib $newdependency_libs"
+ case $deplib in
+ -L*) func_stripname '-L' '' "$deplib"
+ func_resolve_sysroot "$func_stripname_result";;
+ *) func_resolve_sysroot "$deplib" ;;
+ esac
+ if $opt_preserve_dup_deps; then
+ case "$tmp_libs " in
+ *" $func_resolve_sysroot_result "*)
+ func_append specialdeplibs " $func_resolve_sysroot_result" ;;
+ esac
+ fi
+ func_append tmp_libs " $func_resolve_sysroot_result"
+ done
+
+ if test no != "$link_all_deplibs"; then
+ # Add the search paths of all dependency libraries
+ for deplib in $dependency_libs; do
+ path=
+ case $deplib in
+ -L*) path=$deplib ;;
+ *.la)
+ func_resolve_sysroot "$deplib"
+ deplib=$func_resolve_sysroot_result
+ func_dirname "$deplib" "" "."
+ dir=$func_dirname_result
+ # We need an absolute path.
+ case $dir in
+ [\\/]* | [A-Za-z]:[\\/]*) absdir=$dir ;;
+ *)
+ absdir=`cd "$dir" && pwd`
+ if test -z "$absdir"; then
+ func_warning "cannot determine absolute directory name of '$dir'"
+ absdir=$dir
+ fi
+ ;;
+ esac
+ if $GREP "^installed=no" $deplib > /dev/null; then
+ case $host in
+ *-*-darwin*)
+ depdepl=
+ eval deplibrary_names=`$SED -n -e 's/^library_names=\(.*\)$/\1/p' $deplib`
+ if test -n "$deplibrary_names"; then
+ for tmp in $deplibrary_names; do
+ depdepl=$tmp
+ done
+ if test -f "$absdir/$objdir/$depdepl"; then
+ depdepl=$absdir/$objdir/$depdepl
+ darwin_install_name=`$OTOOL -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
+ if test -z "$darwin_install_name"; then
+ darwin_install_name=`$OTOOL64 -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
+ fi
+ func_append compiler_flags " $wl-dylib_file $wl$darwin_install_name:$depdepl"
+ func_append linker_flags " -dylib_file $darwin_install_name:$depdepl"
+ path=
+ fi
+ fi
+ ;;
+ *)
+ path=-L$absdir/$objdir
+ ;;
+ esac
+ else
+ eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+ test -z "$libdir" && \
+ func_fatal_error "'$deplib' is not a valid libtool archive"
+ test "$absdir" != "$libdir" && \
+ func_warning "'$deplib' seems to be moved"
+
+ path=-L$absdir
+ fi
+ ;;
+ esac
+ case " $deplibs " in
+ *" $path "*) ;;
+ *) deplibs="$path $deplibs" ;;
+ esac
+ done
+ fi # link_all_deplibs != no
+ fi # linkmode = lib
+ done # for deplib in $libs
+ if test link = "$pass"; then
+ if test prog = "$linkmode"; then
+ compile_deplibs="$new_inherited_linker_flags $compile_deplibs"
+ finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs"
+ else
+ compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ fi
+ fi
+ dependency_libs=$newdependency_libs
+ if test dlpreopen = "$pass"; then
+ # Link the dlpreopened libraries before other libraries
+ for deplib in $save_deplibs; do
+ deplibs="$deplib $deplibs"
+ done
+ fi
+ if test dlopen != "$pass"; then
+ test conv = "$pass" || {
+ # Make sure lib_search_path contains only unique directories.
+ lib_search_path=
+ for dir in $newlib_search_path; do
+ case "$lib_search_path " in
+ *" $dir "*) ;;
+ *) func_append lib_search_path " $dir" ;;
+ esac
+ done
+ newlib_search_path=
+ }
+
+ if test prog,link = "$linkmode,$pass"; then
+ vars="compile_deplibs finalize_deplibs"
+ else
+ vars=deplibs
+ fi
+ for var in $vars dependency_libs; do
+ # Add libraries to $var in reverse order
+ eval tmp_libs=\"\$$var\"
+ new_libs=
+ for deplib in $tmp_libs; do
+ # FIXME: Pedantically, this is the right thing to do, so
+ # that some nasty dependency loop isn't accidentally
+ # broken:
+ #new_libs="$deplib $new_libs"
+ # Pragmatically, this seems to cause very few problems in
+ # practice:
+ case $deplib in
+ -L*) new_libs="$deplib $new_libs" ;;
+ -R*) ;;
+ *)
+ # And here is the reason: when a library appears more
+ # than once as an explicit dependence of a library, or
+ # is implicitly linked in more than once by the
+ # compiler, it is considered special, and multiple
+ # occurrences thereof are not removed. Compare this
+ # with having the same library being listed as a
+ # dependency of multiple other libraries: in this case,
+ # we know (pedantically, we assume) the library does not
+ # need to be listed more than once, so we keep only the
+ # last copy. This is not always right, but it is rare
+ # enough that we require users that really mean to play
+ # such unportable linking tricks to link the library
+ # using -Wl,-lname, so that libtool does not consider it
+ # for duplicate removal.
+ case " $specialdeplibs " in
+ *" $deplib "*) new_libs="$deplib $new_libs" ;;
+ *)
+ case " $new_libs " in
+ *" $deplib "*) ;;
+ *) new_libs="$deplib $new_libs" ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ done
+ tmp_libs=
+ for deplib in $new_libs; do
+ case $deplib in
+ -L*)
+ case " $tmp_libs " in
+ *" $deplib "*) ;;
+ *) func_append tmp_libs " $deplib" ;;
+ esac
+ ;;
+ *) func_append tmp_libs " $deplib" ;;
+ esac
+ done
+ eval $var=\"$tmp_libs\"
+ done # for var
+ fi
+
+ # Add Sun CC postdeps if required:
+ test CXX = "$tagname" && {
+ case $host_os in
+ linux*)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*) # Sun C++ 5.9
+ func_suncc_cstd_abi
+
+ if test no != "$suncc_use_cstd_abi"; then
+ func_append postdeps ' -library=Cstd -library=Crun'
+ fi
+ ;;
+ esac
+ ;;
+
+ solaris*)
+ func_cc_basename "$CC"
+ case $func_cc_basename_result in
+ CC* | sunCC*)
+ func_suncc_cstd_abi
+
+ if test no != "$suncc_use_cstd_abi"; then
+ func_append postdeps ' -library=Cstd -library=Crun'
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ }
+
+ # Last step: remove runtime libs from dependency_libs
+ # (they stay in deplibs)
+ tmp_libs=
+ for i in $dependency_libs; do
+ case " $predeps $postdeps $compiler_lib_search_path " in
+ *" $i "*)
+ i=
+ ;;
+ esac
+ if test -n "$i"; then
+ func_append tmp_libs " $i"
+ fi
+ done
+ dependency_libs=$tmp_libs
+ done # for pass
+ if test prog = "$linkmode"; then
+ dlfiles=$newdlfiles
+ fi
+ if test prog = "$linkmode" || test lib = "$linkmode"; then
+ dlprefiles=$newdlprefiles
+ fi
+
+ case $linkmode in
+ oldlib)
+ if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then
+ func_warning "'-dlopen' is ignored for archives"
+ fi
+
+ case " $deplibs" in
+ *\ -l* | *\ -L*)
+ func_warning "'-l' and '-L' are ignored for archives" ;;
+ esac
+
+ test -n "$rpath" && \
+ func_warning "'-rpath' is ignored for archives"
+
+ test -n "$xrpath" && \
+ func_warning "'-R' is ignored for archives"
+
+ test -n "$vinfo" && \
+ func_warning "'-version-info/-version-number' is ignored for archives"
+
+ test -n "$release" && \
+ func_warning "'-release' is ignored for archives"
+
+ test -n "$export_symbols$export_symbols_regex" && \
+ func_warning "'-export-symbols' is ignored for archives"
+
+ # Now set the variables for building old libraries.
+ build_libtool_libs=no
+ oldlibs=$output
+ func_append objs "$old_deplibs"
+ ;;
+
+ lib)
+ # Make sure we only generate libraries of the form 'libNAME.la'.
+ case $outputname in
+ lib*)
+ func_stripname 'lib' '.la' "$outputname"
+ name=$func_stripname_result
+ eval shared_ext=\"$shrext_cmds\"
+ eval libname=\"$libname_spec\"
+ ;;
+ *)
+ test no = "$module" \
+ && func_fatal_help "libtool library '$output' must begin with 'lib'"
+
+ if test no != "$need_lib_prefix"; then
+ # Add the "lib" prefix for modules if required
+ func_stripname '' '.la' "$outputname"
+ name=$func_stripname_result
+ eval shared_ext=\"$shrext_cmds\"
+ eval libname=\"$libname_spec\"
+ else
+ func_stripname '' '.la' "$outputname"
+ libname=$func_stripname_result
+ fi
+ ;;
+ esac
+
+ if test -n "$objs"; then
+ if test pass_all != "$deplibs_check_method"; then
+ func_fatal_error "cannot build libtool library '$output' from non-libtool objects on this host:$objs"
+ else
+ echo
+ $ECHO "*** Warning: Linking the shared library $output against the non-libtool"
+ $ECHO "*** objects $objs is not portable!"
+ func_append libobjs " $objs"
+ fi
+ fi
+
+ test no = "$dlself" \
+ || func_warning "'-dlopen self' is ignored for libtool libraries"
+
+ set dummy $rpath
+ shift
+ test 1 -lt "$#" \
+ && func_warning "ignoring multiple '-rpath's for a libtool library"
+
+ install_libdir=$1
+
+ oldlibs=
+ if test -z "$rpath"; then
+ if test yes = "$build_libtool_libs"; then
+ # Building a libtool convenience library.
+ # Some compilers have problems with a '.al' extension so
+ # convenience libraries should have the same extension an
+ # archive normally would.
+ oldlibs="$output_objdir/$libname.$libext $oldlibs"
+ build_libtool_libs=convenience
+ build_old_libs=yes
+ fi
+
+ test -n "$vinfo" && \
+ func_warning "'-version-info/-version-number' is ignored for convenience libraries"
+
+ test -n "$release" && \
+ func_warning "'-release' is ignored for convenience libraries"
+ else
+
+ # Parse the version information argument.
+ save_ifs=$IFS; IFS=:
+ set dummy $vinfo 0 0 0
+ shift
+ IFS=$save_ifs
+
+ test -n "$7" && \
+ func_fatal_help "too many parameters to '-version-info'"
+
+ # convert absolute version numbers to libtool ages
+ # this retains compatibility with .la files and attempts
+ # to make the code below a bit more comprehensible
+
+ case $vinfo_number in
+ yes)
+ number_major=$1
+ number_minor=$2
+ number_revision=$3
+ #
+ # There are really only two kinds -- those that
+ # use the current revision as the major version
+ # and those that subtract age and use age as
+ # a minor version. But, then there is irix
+ # that has an extra 1 added just for fun
+ #
+ case $version_type in
+ # correct linux to gnu/linux during the next big refactor
+ darwin|freebsd-elf|linux|osf|windows|none)
+ func_arith $number_major + $number_minor
+ current=$func_arith_result
+ age=$number_minor
+ revision=$number_revision
+ ;;
+ freebsd-aout|qnx|sunos)
+ current=$number_major
+ revision=$number_minor
+ age=0
+ ;;
+ irix|nonstopux)
+ func_arith $number_major + $number_minor
+ current=$func_arith_result
+ age=$number_minor
+ revision=$number_minor
+ lt_irix_increment=no
+ ;;
+ esac
+ ;;
+ no)
+ current=$1
+ revision=$2
+ age=$3
+ ;;
+ esac
+
+ # Check that each of the things are valid numbers.
+ case $current in
+ 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+ *)
+ func_error "CURRENT '$current' must be a nonnegative integer"
+ func_fatal_error "'$vinfo' is not valid version information"
+ ;;
+ esac
+
+ case $revision in
+ 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+ *)
+ func_error "REVISION '$revision' must be a nonnegative integer"
+ func_fatal_error "'$vinfo' is not valid version information"
+ ;;
+ esac
+
+ case $age in
+ 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+ *)
+ func_error "AGE '$age' must be a nonnegative integer"
+ func_fatal_error "'$vinfo' is not valid version information"
+ ;;
+ esac
+
+ if test "$age" -gt "$current"; then
+ func_error "AGE '$age' is greater than the current interface number '$current'"
+ func_fatal_error "'$vinfo' is not valid version information"
+ fi
+
+ # Calculate the version variables.
+ major=
+ versuffix=
+ verstring=
+ case $version_type in
+ none) ;;
+
+ darwin)
+ # Like Linux, but with the current version available in
+ # verstring for coding it into the library header
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix=$major.$age.$revision
+ # Darwin ld doesn't like 0 for these options...
+ func_arith $current + 1
+ minor_current=$func_arith_result
+ xlcverstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision"
+ verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
+ # On Darwin other compilers
+ case $CC in
+ nagfor*)
+ verstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision"
+ ;;
+ *)
+ verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
+ ;;
+ esac
+ ;;
+
+ freebsd-aout)
+ major=.$current
+ versuffix=.$current.$revision
+ ;;
+
+ freebsd-elf)
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix=$major.$age.$revision
+ ;;
+
+ irix | nonstopux)
+ if test no = "$lt_irix_increment"; then
+ func_arith $current - $age
+ else
+ func_arith $current - $age + 1
+ fi
+ major=$func_arith_result
+
+ case $version_type in
+ nonstopux) verstring_prefix=nonstopux ;;
+ *) verstring_prefix=sgi ;;
+ esac
+ verstring=$verstring_prefix$major.$revision
+
+ # Add in all the interfaces that we are compatible with.
+ loop=$revision
+ while test 0 -ne "$loop"; do
+ func_arith $revision - $loop
+ iface=$func_arith_result
+ func_arith $loop - 1
+ loop=$func_arith_result
+ verstring=$verstring_prefix$major.$iface:$verstring
+ done
+
+ # Before this point, $major must not contain '.'.
+ major=.$major
+ versuffix=$major.$revision
+ ;;
+
+ linux) # correct to gnu/linux during the next big refactor
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix=$major.$age.$revision
+ ;;
+
+ osf)
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix=.$current.$age.$revision
+ verstring=$current.$age.$revision
+
+ # Add in all the interfaces that we are compatible with.
+ loop=$age
+ while test 0 -ne "$loop"; do
+ func_arith $current - $loop
+ iface=$func_arith_result
+ func_arith $loop - 1
+ loop=$func_arith_result
+ verstring=$verstring:$iface.0
+ done
+
+ # Make executables depend on our current version.
+ func_append verstring ":$current.0"
+ ;;
+
+ qnx)
+ major=.$current
+ versuffix=.$current
+ ;;
+
+ sco)
+ major=.$current
+ versuffix=.$current
+ ;;
+
+ sunos)
+ major=.$current
+ versuffix=.$current.$revision
+ ;;
+
+ windows)
+ # Use '-' rather than '.', since we only want one
+ # extension on DOS 8.3 file systems.
+ func_arith $current - $age
+ major=$func_arith_result
+ versuffix=-$major
+ ;;
+
+ *)
+ func_fatal_configuration "unknown library version type '$version_type'"
+ ;;
+ esac
+
+ # Clear the version info if we defaulted, and they specified a release.
+ if test -z "$vinfo" && test -n "$release"; then
+ major=
+ case $version_type in
+ darwin)
+ # we can't check for "0.0" in archive_cmds due to quoting
+ # problems, so we reset it completely
+ verstring=
+ ;;
+ *)
+ verstring=0.0
+ ;;
+ esac
+ if test no = "$need_version"; then
+ versuffix=
+ else
+ versuffix=.0.0
+ fi
+ fi
+
+ # Remove version info from name if versioning should be avoided
+ if test yes,no = "$avoid_version,$need_version"; then
+ major=
+ versuffix=
+ verstring=
+ fi
+
+ # Check to see if the archive will have undefined symbols.
+ if test yes = "$allow_undefined"; then
+ if test unsupported = "$allow_undefined_flag"; then
+ if test yes = "$build_old_libs"; then
+ func_warning "undefined symbols not allowed in $host shared libraries; building static only"
+ build_libtool_libs=no
+ else
+ func_fatal_error "can't build $host shared library unless -no-undefined is specified"
+ fi
+ fi
+ else
+ # Don't allow undefined symbols.
+ allow_undefined_flag=$no_undefined_flag
+ fi
+
+ fi
+
+ func_generate_dlsyms "$libname" "$libname" :
+ func_append libobjs " $symfileobj"
+ test " " = "$libobjs" && libobjs=
+
+ if test relink != "$opt_mode"; then
+ # Remove our outputs, but don't remove object files since they
+ # may have been created when compiling PIC objects.
+ removelist=
+ tempremovelist=`$ECHO "$output_objdir/*"`
+ for p in $tempremovelist; do
+ case $p in
+ *.$objext | *.gcno)
+ ;;
+ $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/$libname$release.*)
+ if test -n "$precious_files_regex"; then
+ if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1
+ then
+ continue
+ fi
+ fi
+ func_append removelist " $p"
+ ;;
+ *) ;;
+ esac
+ done
+ test -n "$removelist" && \
+ func_show_eval "${RM}r \$removelist"
+ fi
+
+ # Now set the variables for building old libraries.
+ if test yes = "$build_old_libs" && test convenience != "$build_libtool_libs"; then
+ func_append oldlibs " $output_objdir/$libname.$libext"
+
+ # Transform .lo files to .o files.
+ oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; $lo2o" | $NL2SP`
+ fi
+
+ # Eliminate all temporary directories.
+ #for path in $notinst_path; do
+ # lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"`
+ # deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"`
+ # dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"`
+ #done
+
+ if test -n "$xrpath"; then
+ # If the user specified any rpath flags, then add them.
+ temp_xrpath=
+ for libdir in $xrpath; do
+ func_replace_sysroot "$libdir"
+ func_append temp_xrpath " -R$func_replace_sysroot_result"
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) func_append finalize_rpath " $libdir" ;;
+ esac
+ done
+ if test yes != "$hardcode_into_libs" || test yes = "$build_old_libs"; then
+ dependency_libs="$temp_xrpath $dependency_libs"
+ fi
+ fi
+
+ # Make sure dlfiles contains only unique files that won't be dlpreopened
+ old_dlfiles=$dlfiles
+ dlfiles=
+ for lib in $old_dlfiles; do
+ case " $dlprefiles $dlfiles " in
+ *" $lib "*) ;;
+ *) func_append dlfiles " $lib" ;;
+ esac
+ done
+
+ # Make sure dlprefiles contains only unique files
+ old_dlprefiles=$dlprefiles
+ dlprefiles=
+ for lib in $old_dlprefiles; do
+ case "$dlprefiles " in
+ *" $lib "*) ;;
+ *) func_append dlprefiles " $lib" ;;
+ esac
+ done
+
+ if test yes = "$build_libtool_libs"; then
+ if test -n "$rpath"; then
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*)
+ # these systems don't actually have a c library (as such)!
+ ;;
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # Rhapsody C library is in the System framework
+ func_append deplibs " System.ltframework"
+ ;;
+ *-*-netbsd*)
+ # Don't link with libc until the a.out ld.so is fixed.
+ ;;
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+ # Do not include libc due to us having libc/libc_r.
+ ;;
+ *-*-sco3.2v5* | *-*-sco5v6*)
+ # Causes problems with __ctype
+ ;;
+ *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+ # Compiler inserts libc in the correct place for threads to work
+ ;;
+ *)
+ # Add libc to deplibs on all other systems if necessary.
+ if test yes = "$build_libtool_need_lc"; then
+ func_append deplibs " -lc"
+ fi
+ ;;
+ esac
+ fi
+
+ # Transform deplibs into only deplibs that can be linked in shared.
+ name_save=$name
+ libname_save=$libname
+ release_save=$release
+ versuffix_save=$versuffix
+ major_save=$major
+ # I'm not sure if I'm treating the release correctly. I think
+ # release should show up in the -l (ie -lgmp5) so we don't want to
+ # add it in twice. Is that correct?
+ release=
+ versuffix=
+ major=
+ newdeplibs=
+ droppeddeps=no
+ case $deplibs_check_method in
+ pass_all)
+ # Don't check for shared/static. Everything works.
+ # This might be a little naive. We might want to check
+ # whether the library exists or not. But this is on
+ # osf3 & osf4 and I'm not really sure... Just
+ # implementing what was already the behavior.
+ newdeplibs=$deplibs
+ ;;
+ test_compile)
+ # This code stresses the "libraries are programs" paradigm to its
+ # limits. Maybe even breaks it. We compile a program, linking it
+ # against the deplibs as a proxy for the library. Then we can check
+ # whether they linked in statically or dynamically with ldd.
+ $opt_dry_run || $RM conftest.c
+ cat > conftest.c <<EOF
+ int main() { return 0; }
+EOF
+ $opt_dry_run || $RM conftest
+ if $LTCC $LTCFLAGS -o conftest conftest.c $deplibs; then
+ ldd_output=`ldd conftest`
+ for i in $deplibs; do
+ case $i in
+ -l*)
+ func_stripname -l '' "$i"
+ name=$func_stripname_result
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+ case " $predeps $postdeps " in
+ *" $i "*)
+ func_append newdeplibs " $i"
+ i=
+ ;;
+ esac
+ fi
+ if test -n "$i"; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+ set dummy $deplib_matches; shift
+ deplib_match=$1
+ if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0; then
+ func_append newdeplibs " $i"
+ else
+ droppeddeps=yes
+ echo
+ $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+ echo "*** I have the capability to make that library automatically link in when"
+ echo "*** you link to this library. But I can only do this if you have a"
+ echo "*** shared version of the library, which I believe you do not have"
+ echo "*** because a test_compile did reveal that the linker did not use it for"
+ echo "*** its dynamic dependency list that programs get resolved with at runtime."
+ fi
+ fi
+ ;;
+ *)
+ func_append newdeplibs " $i"
+ ;;
+ esac
+ done
+ else
+ # Error occurred in the first compile. Let's try to salvage
+ # the situation: Compile a separate program for each library.
+ for i in $deplibs; do
+ case $i in
+ -l*)
+ func_stripname -l '' "$i"
+ name=$func_stripname_result
+ $opt_dry_run || $RM conftest
+ if $LTCC $LTCFLAGS -o conftest conftest.c $i; then
+ ldd_output=`ldd conftest`
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+ case " $predeps $postdeps " in
+ *" $i "*)
+ func_append newdeplibs " $i"
+ i=
+ ;;
+ esac
+ fi
+ if test -n "$i"; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+ set dummy $deplib_matches; shift
+ deplib_match=$1
+ if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0; then
+ func_append newdeplibs " $i"
+ else
+ droppeddeps=yes
+ echo
+ $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+ echo "*** I have the capability to make that library automatically link in when"
+ echo "*** you link to this library. But I can only do this if you have a"
+ echo "*** shared version of the library, which you do not appear to have"
+ echo "*** because a test_compile did reveal that the linker did not use this one"
+ echo "*** as a dynamic dependency that programs can get resolved with at runtime."
+ fi
+ fi
+ else
+ droppeddeps=yes
+ echo
+ $ECHO "*** Warning! Library $i is needed by this library but I was not able to"
+ echo "*** make it link in! You will probably need to install it or some"
+ echo "*** library that it depends on before this library will be fully"
+ echo "*** functional. Installing it before continuing would be even better."
+ fi
+ ;;
+ *)
+ func_append newdeplibs " $i"
+ ;;
+ esac
+ done
+ fi
+ ;;
+ file_magic*)
+ set dummy $deplibs_check_method; shift
+ file_magic_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+ for a_deplib in $deplibs; do
+ case $a_deplib in
+ -l*)
+ func_stripname -l '' "$a_deplib"
+ name=$func_stripname_result
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+ case " $predeps $postdeps " in
+ *" $a_deplib "*)
+ func_append newdeplibs " $a_deplib"
+ a_deplib=
+ ;;
+ esac
+ fi
+ if test -n "$a_deplib"; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ if test -n "$file_magic_glob"; then
+ libnameglob=`func_echo_all "$libname" | $SED -e $file_magic_glob`
+ else
+ libnameglob=$libname
+ fi
+ test yes = "$want_nocaseglob" && nocaseglob=`shopt -p nocaseglob`
+ for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+ if test yes = "$want_nocaseglob"; then
+ shopt -s nocaseglob
+ potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null`
+ $nocaseglob
+ else
+ potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null`
+ fi
+ for potent_lib in $potential_libs; do
+ # Follow soft links.
+ if ls -lLd "$potent_lib" 2>/dev/null |
+ $GREP " -> " >/dev/null; then
+ continue
+ fi
+ # The statement above tries to avoid entering an
+ # endless loop below, in case of cyclic links.
+ # We might still enter an endless loop, since a link
+ # loop can be closed while we follow links,
+ # but so what?
+ potlib=$potent_lib
+ while test -h "$potlib" 2>/dev/null; do
+ potliblink=`ls -ld $potlib | $SED 's/.* -> //'`
+ case $potliblink in
+ [\\/]* | [A-Za-z]:[\\/]*) potlib=$potliblink;;
+ *) potlib=`$ECHO "$potlib" | $SED 's|[^/]*$||'`"$potliblink";;
+ esac
+ done
+ if eval $file_magic_cmd \"\$potlib\" 2>/dev/null |
+ $SED -e 10q |
+ $EGREP "$file_magic_regex" > /dev/null; then
+ func_append newdeplibs " $a_deplib"
+ a_deplib=
+ break 2
+ fi
+ done
+ done
+ fi
+ if test -n "$a_deplib"; then
+ droppeddeps=yes
+ echo
+ $ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+ echo "*** I have the capability to make that library automatically link in when"
+ echo "*** you link to this library. But I can only do this if you have a"
+ echo "*** shared version of the library, which you do not appear to have"
+ echo "*** because I did check the linker path looking for a file starting"
+ if test -z "$potlib"; then
+ $ECHO "*** with $libname but no candidates were found. (...for file magic test)"
+ else
+ $ECHO "*** with $libname and none of the candidates passed a file format test"
+ $ECHO "*** using a file magic. Last file checked: $potlib"
+ fi
+ fi
+ ;;
+ *)
+ # Add a -L argument.
+ func_append newdeplibs " $a_deplib"
+ ;;
+ esac
+ done # Gone through all deplibs.
+ ;;
+ match_pattern*)
+ set dummy $deplibs_check_method; shift
+ match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+ for a_deplib in $deplibs; do
+ case $a_deplib in
+ -l*)
+ func_stripname -l '' "$a_deplib"
+ name=$func_stripname_result
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+ case " $predeps $postdeps " in
+ *" $a_deplib "*)
+ func_append newdeplibs " $a_deplib"
+ a_deplib=
+ ;;
+ esac
+ fi
+ if test -n "$a_deplib"; then
+ libname=`eval "\\$ECHO \"$libname_spec\""`
+ for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+ potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
+ for potent_lib in $potential_libs; do
+ potlib=$potent_lib # see symlink-check above in file_magic test
+ if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \
+ $EGREP "$match_pattern_regex" > /dev/null; then
+ func_append newdeplibs " $a_deplib"
+ a_deplib=
+ break 2
+ fi
+ done
+ done
+ fi
+ if test -n "$a_deplib"; then
+ droppeddeps=yes
+ echo
+ $ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+ echo "*** I have the capability to make that library automatically link in when"
+ echo "*** you link to this library. But I can only do this if you have a"
+ echo "*** shared version of the library, which you do not appear to have"
+ echo "*** because I did check the linker path looking for a file starting"
+ if test -z "$potlib"; then
+ $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)"
+ else
+ $ECHO "*** with $libname and none of the candidates passed a file format test"
+ $ECHO "*** using a regex pattern. Last file checked: $potlib"
+ fi
+ fi
+ ;;
+ *)
+ # Add a -L argument.
+ func_append newdeplibs " $a_deplib"
+ ;;
+ esac
+ done # Gone through all deplibs.
+ ;;
+ none | unknown | *)
+ newdeplibs=
+ tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'`
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+ for i in $predeps $postdeps; do
+ # can't use Xsed below, because $i might contain '/'
+ tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s|$i||"`
+ done
+ fi
+ case $tmp_deplibs in
+ *[!\ \ ]*)
+ echo
+ if test none = "$deplibs_check_method"; then
+ echo "*** Warning: inter-library dependencies are not supported in this platform."
+ else
+ echo "*** Warning: inter-library dependencies are not known to be supported."
+ fi
+ echo "*** All declared inter-library dependencies are being dropped."
+ droppeddeps=yes
+ ;;
+ esac
+ ;;
+ esac
+ versuffix=$versuffix_save
+ major=$major_save
+ release=$release_save
+ libname=$libname_save
+ name=$name_save
+
+ case $host in
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # On Rhapsody replace the C library with the System framework
+ newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'`
+ ;;
+ esac
+
+ if test yes = "$droppeddeps"; then
+ if test yes = "$module"; then
+ echo
+ echo "*** Warning: libtool could not satisfy all declared inter-library"
+ $ECHO "*** dependencies of module $libname. Therefore, libtool will create"
+ echo "*** a static module, that should work as long as the dlopening"
+ echo "*** application is linked with the -dlopen flag."
+ if test -z "$global_symbol_pipe"; then
+ echo
+ echo "*** However, this would only work if libtool was able to extract symbol"
+ echo "*** lists from a program, using 'nm' or equivalent, but libtool could"
+ echo "*** not find such a program. So, this module is probably useless."
+ echo "*** 'nm' from GNU binutils and a full rebuild may help."
+ fi
+ if test no = "$build_old_libs"; then
+ oldlibs=$output_objdir/$libname.$libext
+ build_libtool_libs=module
+ build_old_libs=yes
+ else
+ build_libtool_libs=no
+ fi
+ else
+ echo "*** The inter-library dependencies that have been dropped here will be"
+ echo "*** automatically added whenever a program is linked with this library"
+ echo "*** or is declared to -dlopen it."
+
+ if test no = "$allow_undefined"; then
+ echo
+ echo "*** Since this library must not contain undefined symbols,"
+ echo "*** because either the platform does not support them or"
+ echo "*** it was explicitly requested with -no-undefined,"
+ echo "*** libtool will only create a static version of it."
+ if test no = "$build_old_libs"; then
+ oldlibs=$output_objdir/$libname.$libext
+ build_libtool_libs=module
+ build_old_libs=yes
+ else
+ build_libtool_libs=no
+ fi
+ fi
+ fi
+ fi
+ # Done checking deplibs!
+ deplibs=$newdeplibs
+ fi
+ # Time to change all our "foo.ltframework" stuff back to "-framework foo"
+ case $host in
+ *-*-darwin*)
+ newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ ;;
+ esac
+
+ # move library search paths that coincide with paths to not yet
+ # installed libraries to the beginning of the library search list
+ new_libs=
+ for path in $notinst_path; do
+ case " $new_libs " in
+ *" -L$path/$objdir "*) ;;
+ *)
+ case " $deplibs " in
+ *" -L$path/$objdir "*)
+ func_append new_libs " -L$path/$objdir" ;;
+ esac
+ ;;
+ esac
+ done
+ for deplib in $deplibs; do
+ case $deplib in
+ -L*)
+ case " $new_libs " in
+ *" $deplib "*) ;;
+ *) func_append new_libs " $deplib" ;;
+ esac
+ ;;
+ *) func_append new_libs " $deplib" ;;
+ esac
+ done
+ deplibs=$new_libs
+
+ # All the library-specific variables (install_libdir is set above).
+ library_names=
+ old_library=
+ dlname=
+
+ # Test again, we may have decided not to build it any more
+ if test yes = "$build_libtool_libs"; then
+ # Remove $wl instances when linking with ld.
+ # FIXME: should test the right _cmds variable.
+ case $archive_cmds in
+ *\$LD\ *) wl= ;;
+ esac
+ if test yes = "$hardcode_into_libs"; then
+ # Hardcode the library paths
+ hardcode_libdirs=
+ dep_rpath=
+ rpath=$finalize_rpath
+ test relink = "$opt_mode" || rpath=$compile_rpath$rpath
+ for libdir in $rpath; do
+ if test -n "$hardcode_libdir_flag_spec"; then
+ if test -n "$hardcode_libdir_separator"; then
+ func_replace_sysroot "$libdir"
+ libdir=$func_replace_sysroot_result
+ if test -z "$hardcode_libdirs"; then
+ hardcode_libdirs=$libdir
+ else
+ # Just accumulate the unique libdirs.
+ case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+ *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+ ;;
+ *)
+ func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ func_append dep_rpath " $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$perm_rpath " in
+ *" $libdir "*) ;;
+ *) func_append perm_rpath " $libdir" ;;
+ esac
+ fi
+ done
+ # Substitute the hardcoded libdirs into the rpath.
+ if test -n "$hardcode_libdir_separator" &&
+ test -n "$hardcode_libdirs"; then
+ libdir=$hardcode_libdirs
+ eval "dep_rpath=\"$hardcode_libdir_flag_spec\""
+ fi
+ if test -n "$runpath_var" && test -n "$perm_rpath"; then
+ # We should set the runpath_var.
+ rpath=
+ for dir in $perm_rpath; do
+ func_append rpath "$dir:"
+ done
+ eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var"
+ fi
+ test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs"
+ fi
+
+ shlibpath=$finalize_shlibpath
+ test relink = "$opt_mode" || shlibpath=$compile_shlibpath$shlibpath
+ if test -n "$shlibpath"; then
+ eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var"
+ fi
+
+ # Get the real and link names of the library.
+ eval shared_ext=\"$shrext_cmds\"
+ eval library_names=\"$library_names_spec\"
+ set dummy $library_names
+ shift
+ realname=$1
+ shift
+
+ if test -n "$soname_spec"; then
+ eval soname=\"$soname_spec\"
+ else
+ soname=$realname
+ fi
+ if test -z "$dlname"; then
+ dlname=$soname
+ fi
+
+ lib=$output_objdir/$realname
+ linknames=
+ for link
+ do
+ func_append linknames " $link"
+ done
+
+ # Use standard objects if they are pic
+ test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP`
+ test "X$libobjs" = "X " && libobjs=
+
+ delfiles=
+ if test -n "$export_symbols" && test -n "$include_expsyms"; then
+ $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp"
+ export_symbols=$output_objdir/$libname.uexp
+ func_append delfiles " $export_symbols"
+ fi
+
+ orig_export_symbols=
+ case $host_os in
+ cygwin* | mingw* | cegcc*)
+ if test -n "$export_symbols" && test -z "$export_symbols_regex"; then
+ # exporting using user supplied symfile
+ func_dll_def_p "$export_symbols" || {
+ # and it's NOT already a .def file. Must figure out
+ # which of the given symbols are data symbols and tag
+ # them as such. So, trigger use of export_symbols_cmds.
+ # export_symbols gets reassigned inside the "prepare
+ # the list of exported symbols" if statement, so the
+ # include_expsyms logic still works.
+ orig_export_symbols=$export_symbols
+ export_symbols=
+ always_export_symbols=yes
+ }
+ fi
+ ;;
+ esac
+
+ # Prepare the list of exported symbols
+ if test -z "$export_symbols"; then
+ if test yes = "$always_export_symbols" || test -n "$export_symbols_regex"; then
+ func_verbose "generating symbol list for '$libname.la'"
+ export_symbols=$output_objdir/$libname.exp
+ $opt_dry_run || $RM $export_symbols
+ cmds=$export_symbols_cmds
+ save_ifs=$IFS; IFS='~'
+ for cmd1 in $cmds; do
+ IFS=$save_ifs
+ # Take the normal branch if the nm_file_list_spec branch
+ # doesn't work or if tool conversion is not needed.
+ case $nm_file_list_spec~$to_tool_file_cmd in
+ *~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*)
+ try_normal_branch=yes
+ eval cmd=\"$cmd1\"
+ func_len " $cmd"
+ len=$func_len_result
+ ;;
+ *)
+ try_normal_branch=no
+ ;;
+ esac
+ if test yes = "$try_normal_branch" \
+ && { test "$len" -lt "$max_cmd_len" \
+ || test "$max_cmd_len" -le -1; }
+ then
+ func_show_eval "$cmd" 'exit $?'
+ skipped_export=false
+ elif test -n "$nm_file_list_spec"; then
+ func_basename "$output"
+ output_la=$func_basename_result
+ save_libobjs=$libobjs
+ save_output=$output
+ output=$output_objdir/$output_la.nm
+ func_to_tool_file "$output"
+ libobjs=$nm_file_list_spec$func_to_tool_file_result
+ func_append delfiles " $output"
+ func_verbose "creating $NM input file list: $output"
+ for obj in $save_libobjs; do
+ func_to_tool_file "$obj"
+ $ECHO "$func_to_tool_file_result"
+ done > "$output"
+ eval cmd=\"$cmd1\"
+ func_show_eval "$cmd" 'exit $?'
+ output=$save_output
+ libobjs=$save_libobjs
+ skipped_export=false
+ else
+ # The command line is too long to execute in one step.
+ func_verbose "using reloadable object file for export list..."
+ skipped_export=:
+ # Break out early, otherwise skipped_export may be
+ # set to false by a later but shorter cmd.
+ break
+ fi
+ done
+ IFS=$save_ifs
+ if test -n "$export_symbols_regex" && test : != "$skipped_export"; then
+ func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+ func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+ fi
+ fi
+ fi
+
+ if test -n "$export_symbols" && test -n "$include_expsyms"; then
+ tmp_export_symbols=$export_symbols
+ test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols
+ $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"'
+ fi
+
+ if test : != "$skipped_export" && test -n "$orig_export_symbols"; then
+ # The given exports_symbols file has to be filtered, so filter it.
+ func_verbose "filter symbol list for '$libname.la' to tag DATA exports"
+ # FIXME: $output_objdir/$libname.filter potentially contains lots of
+ # 's' commands, which not all seds can handle. GNU sed should be fine
+ # though. Also, the filter scales superlinearly with the number of
+ # global variables. join(1) would be nice here, but unfortunately
+ # isn't a blessed tool.
+ $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+ func_append delfiles " $export_symbols $output_objdir/$libname.filter"
+ export_symbols=$output_objdir/$libname.def
+ $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+ fi
+
+ tmp_deplibs=
+ for test_deplib in $deplibs; do
+ case " $convenience " in
+ *" $test_deplib "*) ;;
+ *)
+ func_append tmp_deplibs " $test_deplib"
+ ;;
+ esac
+ done
+ deplibs=$tmp_deplibs
+
+ if test -n "$convenience"; then
+ if test -n "$whole_archive_flag_spec" &&
+ test yes = "$compiler_needs_object" &&
+ test -z "$libobjs"; then
+ # extract the archives, so we have objects to list.
+ # TODO: could optimize this to just extract one archive.
+ whole_archive_flag_spec=
+ fi
+ if test -n "$whole_archive_flag_spec"; then
+ save_libobjs=$libobjs
+ eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+ test "X$libobjs" = "X " && libobjs=
+ else
+ gentop=$output_objdir/${outputname}x
+ func_append generated " $gentop"
+
+ func_extract_archives $gentop $convenience
+ func_append libobjs " $func_extract_archives_result"
+ test "X$libobjs" = "X " && libobjs=
+ fi
+ fi
+
+ if test yes = "$thread_safe" && test -n "$thread_safe_flag_spec"; then
+ eval flag=\"$thread_safe_flag_spec\"
+ func_append linker_flags " $flag"
+ fi
+
+ # Make a backup of the uninstalled library when relinking
+ if test relink = "$opt_mode"; then
+ $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $?
+ fi
+
+ # Do each of the archive commands.
+ if test yes = "$module" && test -n "$module_cmds"; then
+ if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+ eval test_cmds=\"$module_expsym_cmds\"
+ cmds=$module_expsym_cmds
+ else
+ eval test_cmds=\"$module_cmds\"
+ cmds=$module_cmds
+ fi
+ else
+ if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+ eval test_cmds=\"$archive_expsym_cmds\"
+ cmds=$archive_expsym_cmds
+ else
+ eval test_cmds=\"$archive_cmds\"
+ cmds=$archive_cmds
+ fi
+ fi
+
+ if test : != "$skipped_export" &&
+ func_len " $test_cmds" &&
+ len=$func_len_result &&
+ test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+ :
+ else
+ # The command line is too long to link in one step, link piecewise
+ # or, if using GNU ld and skipped_export is not :, use a linker
+ # script.
+
+ # Save the value of $output and $libobjs because we want to
+ # use them later. If we have whole_archive_flag_spec, we
+ # want to use save_libobjs as it was before
+ # whole_archive_flag_spec was expanded, because we can't
+ # assume the linker understands whole_archive_flag_spec.
+ # This may have to be revisited, in case too many
+ # convenience libraries get linked in and end up exceeding
+ # the spec.
+ if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then
+ save_libobjs=$libobjs
+ fi
+ save_output=$output
+ func_basename "$output"
+ output_la=$func_basename_result
+
+ # Clear the reloadable object creation command queue and
+ # initialize k to one.
+ test_cmds=
+ concat_cmds=
+ objlist=
+ last_robj=
+ k=1
+
+ if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then
+ output=$output_objdir/$output_la.lnkscript
+ func_verbose "creating GNU ld script: $output"
+ echo 'INPUT (' > $output
+ for obj in $save_libobjs
+ do
+ func_to_tool_file "$obj"
+ $ECHO "$func_to_tool_file_result" >> $output
+ done
+ echo ')' >> $output
+ func_append delfiles " $output"
+ func_to_tool_file "$output"
+ output=$func_to_tool_file_result
+ elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then
+ output=$output_objdir/$output_la.lnk
+ func_verbose "creating linker input file list: $output"
+ : > $output
+ set x $save_libobjs
+ shift
+ firstobj=
+ if test yes = "$compiler_needs_object"; then
+ firstobj="$1 "
+ shift
+ fi
+ for obj
+ do
+ func_to_tool_file "$obj"
+ $ECHO "$func_to_tool_file_result" >> $output
+ done
+ func_append delfiles " $output"
+ func_to_tool_file "$output"
+ output=$firstobj\"$file_list_spec$func_to_tool_file_result\"
+ else
+ if test -n "$save_libobjs"; then
+ func_verbose "creating reloadable object files..."
+ output=$output_objdir/$output_la-$k.$objext
+ eval test_cmds=\"$reload_cmds\"
+ func_len " $test_cmds"
+ len0=$func_len_result
+ len=$len0
+
+ # Loop over the list of objects to be linked.
+ for obj in $save_libobjs
+ do
+ func_len " $obj"
+ func_arith $len + $func_len_result
+ len=$func_arith_result
+ if test -z "$objlist" ||
+ test "$len" -lt "$max_cmd_len"; then
+ func_append objlist " $obj"
+ else
+ # The command $test_cmds is almost too long, add a
+ # command to the queue.
+ if test 1 -eq "$k"; then
+ # The first file doesn't have a previous command to add.
+ reload_objs=$objlist
+ eval concat_cmds=\"$reload_cmds\"
+ else
+ # All subsequent reloadable object files will link in
+ # the last one created.
+ reload_objs="$objlist $last_robj"
+ eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\"
+ fi
+ last_robj=$output_objdir/$output_la-$k.$objext
+ func_arith $k + 1
+ k=$func_arith_result
+ output=$output_objdir/$output_la-$k.$objext
+ objlist=" $obj"
+ func_len " $last_robj"
+ func_arith $len0 + $func_len_result
+ len=$func_arith_result
+ fi
+ done
+ # Handle the remaining objects by creating one last
+ # reloadable object file. All subsequent reloadable object
+ # files will link in the last one created.
+ test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+ reload_objs="$objlist $last_robj"
+ eval concat_cmds=\"\$concat_cmds$reload_cmds\"
+ if test -n "$last_robj"; then
+ eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\"
+ fi
+ func_append delfiles " $output"
+
+ else
+ output=
+ fi
+
+ ${skipped_export-false} && {
+ func_verbose "generating symbol list for '$libname.la'"
+ export_symbols=$output_objdir/$libname.exp
+ $opt_dry_run || $RM $export_symbols
+ libobjs=$output
+ # Append the command to create the export file.
+ test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+ eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\"
+ if test -n "$last_robj"; then
+ eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\"
+ fi
+ }
+
+ test -n "$save_libobjs" &&
+ func_verbose "creating a temporary reloadable object file: $output"
+
+ # Loop through the commands generated above and execute them.
+ save_ifs=$IFS; IFS='~'
+ for cmd in $concat_cmds; do
+ IFS=$save_ifs
+ $opt_quiet || {
+ func_quote_for_expand "$cmd"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+ $opt_dry_run || eval "$cmd" || {
+ lt_exit=$?
+
+ # Restore the uninstalled library and exit
+ if test relink = "$opt_mode"; then
+ ( cd "$output_objdir" && \
+ $RM "${realname}T" && \
+ $MV "${realname}U" "$realname" )
+ fi
+
+ exit $lt_exit
+ }
+ done
+ IFS=$save_ifs
+
+ if test -n "$export_symbols_regex" && ${skipped_export-false}; then
+ func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+ func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+ fi
+ fi
+
+ ${skipped_export-false} && {
+ if test -n "$export_symbols" && test -n "$include_expsyms"; then
+ tmp_export_symbols=$export_symbols
+ test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols
+ $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"'
+ fi
+
+ if test -n "$orig_export_symbols"; then
+ # The given exports_symbols file has to be filtered, so filter it.
+ func_verbose "filter symbol list for '$libname.la' to tag DATA exports"
+ # FIXME: $output_objdir/$libname.filter potentially contains lots of
+ # 's' commands, which not all seds can handle. GNU sed should be fine
+ # though. Also, the filter scales superlinearly with the number of
+ # global variables. join(1) would be nice here, but unfortunately
+ # isn't a blessed tool.
+ $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+ func_append delfiles " $export_symbols $output_objdir/$libname.filter"
+ export_symbols=$output_objdir/$libname.def
+ $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+ fi
+ }
+
+ libobjs=$output
+ # Restore the value of output.
+ output=$save_output
+
+ if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then
+ eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+ test "X$libobjs" = "X " && libobjs=
+ fi
+ # Expand the library linking commands again to reset the
+ # value of $libobjs for piecewise linking.
+
+ # Do each of the archive commands.
+ if test yes = "$module" && test -n "$module_cmds"; then
+ if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+ cmds=$module_expsym_cmds
+ else
+ cmds=$module_cmds
+ fi
+ else
+ if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+ cmds=$archive_expsym_cmds
+ else
+ cmds=$archive_cmds
+ fi
+ fi
+ fi
+
+ if test -n "$delfiles"; then
+ # Append the command to remove temporary files to $cmds.
+ eval cmds=\"\$cmds~\$RM $delfiles\"
+ fi
+
+ # Add any objects from preloaded convenience libraries
+ if test -n "$dlprefiles"; then
+ gentop=$output_objdir/${outputname}x
+ func_append generated " $gentop"
+
+ func_extract_archives $gentop $dlprefiles
+ func_append libobjs " $func_extract_archives_result"
+ test "X$libobjs" = "X " && libobjs=
+ fi
+
+ save_ifs=$IFS; IFS='~'
+ for cmd in $cmds; do
+ IFS=$sp$nl
+ eval cmd=\"$cmd\"
+ IFS=$save_ifs
+ $opt_quiet || {
+ func_quote_for_expand "$cmd"
+ eval "func_echo $func_quote_for_expand_result"
+ }
+ $opt_dry_run || eval "$cmd" || {
+ lt_exit=$?
+
+ # Restore the uninstalled library and exit
+ if test relink = "$opt_mode"; then
+ ( cd "$output_objdir" && \
+ $RM "${realname}T" && \
+ $MV "${realname}U" "$realname" )
+ fi
+
+ exit $lt_exit
+ }
+ done
+ IFS=$save_ifs
+
+ # Restore the uninstalled library and exit
+ if test relink = "$opt_mode"; then
+ $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $?
+
+ if test -n "$convenience"; then
+ if test -z "$whole_archive_flag_spec"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+ fi
+
+ exit $EXIT_SUCCESS
+ fi
+
+ # Create links to the real library.
+ for linkname in $linknames; do
+ if test "$realname" != "$linkname"; then
+ func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?'
+ fi
+ done
+
+ # If -module or -export-dynamic was specified, set the dlname.
+ if test yes = "$module" || test yes = "$export_dynamic"; then
+ # On all known operating systems, these are identical.
+ dlname=$soname
+ fi
+ fi
+ ;;
+
+ obj)
+ if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then
+ func_warning "'-dlopen' is ignored for objects"
+ fi
+
+ case " $deplibs" in
+ *\ -l* | *\ -L*)
+ func_warning "'-l' and '-L' are ignored for objects" ;;
+ esac
+
+ test -n "$rpath" && \
+ func_warning "'-rpath' is ignored for objects"
+
+ test -n "$xrpath" && \
+ func_warning "'-R' is ignored for objects"
+
+ test -n "$vinfo" && \
+ func_warning "'-version-info' is ignored for objects"
+
+ test -n "$release" && \
+ func_warning "'-release' is ignored for objects"
+
+ case $output in
+ *.lo)
+ test -n "$objs$old_deplibs" && \
+ func_fatal_error "cannot build library object '$output' from non-libtool objects"
+
+ libobj=$output
+ func_lo2o "$libobj"
+ obj=$func_lo2o_result
+ ;;
+ *)
+ libobj=
+ obj=$output
+ ;;
+ esac
+
+ # Delete the old objects.
+ $opt_dry_run || $RM $obj $libobj
+
+ # Objects from convenience libraries. This assumes
+ # single-version convenience libraries. Whenever we create
+ # different ones for PIC/non-PIC, this we'll have to duplicate
+ # the extraction.
+ reload_conv_objs=
+ gentop=
+ # if reload_cmds runs $LD directly, get rid of -Wl from
+ # whole_archive_flag_spec and hope we can get by with turning comma
+ # into space.
+ case $reload_cmds in
+ *\$LD[\ \$]*) wl= ;;
+ esac
+ if test -n "$convenience"; then
+ if test -n "$whole_archive_flag_spec"; then
+ eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\"
+ test -n "$wl" || tmp_whole_archive_flags=`$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'`
+ reload_conv_objs=$reload_objs\ $tmp_whole_archive_flags
+ else
+ gentop=$output_objdir/${obj}x
+ func_append generated " $gentop"
+
+ func_extract_archives $gentop $convenience
+ reload_conv_objs="$reload_objs $func_extract_archives_result"
+ fi
+ fi
+
+ # If we're not building shared, we need to use non_pic_objs
+ test yes = "$build_libtool_libs" || libobjs=$non_pic_objects
+
+ # Create the old-style object.
+ reload_objs=$objs$old_deplibs' '`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; /\.lib$/d; $lo2o" | $NL2SP`' '$reload_conv_objs
+
+ output=$obj
+ func_execute_cmds "$reload_cmds" 'exit $?'
+
+ # Exit if we aren't doing a library object file.
+ if test -z "$libobj"; then
+ if test -n "$gentop"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+
+ exit $EXIT_SUCCESS
+ fi
+
+ test yes = "$build_libtool_libs" || {
+ if test -n "$gentop"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+
+ # Create an invalid libtool object if no PIC, so that we don't
+ # accidentally link it into a program.
+ # $show "echo timestamp > $libobj"
+ # $opt_dry_run || eval "echo timestamp > $libobj" || exit $?
+ exit $EXIT_SUCCESS
+ }
+
+ if test -n "$pic_flag" || test default != "$pic_mode"; then
+ # Only do commands if we really have different PIC objects.
+ reload_objs="$libobjs $reload_conv_objs"
+ output=$libobj
+ func_execute_cmds "$reload_cmds" 'exit $?'
+ fi
+
+ if test -n "$gentop"; then
+ func_show_eval '${RM}r "$gentop"'
+ fi
+
+ exit $EXIT_SUCCESS
+ ;;
+
+ prog)
+ case $host in
+ *cygwin*) func_stripname '' '.exe' "$output"
+ output=$func_stripname_result.exe;;
+ esac
+ test -n "$vinfo" && \
+ func_warning "'-version-info' is ignored for programs"
+
+ test -n "$release" && \
+ func_warning "'-release' is ignored for programs"
+
+ $preload \
+ && test unknown,unknown,unknown = "$dlopen_support,$dlopen_self,$dlopen_self_static" \
+ && func_warning "'LT_INIT([dlopen])' not used. Assuming no dlopen support."
+
+ case $host in
+ *-*-rhapsody* | *-*-darwin1.[012])
+ # On Rhapsody replace the C library is the System framework
+ compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'`
+ finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'`
+ ;;
+ esac
+
+ case $host in
+ *-*-darwin*)
+ # Don't allow lazy linking, it breaks C++ global constructors
+ # But is supposedly fixed on 10.4 or later (yay!).
+ if test CXX = "$tagname"; then
+ case ${MACOSX_DEPLOYMENT_TARGET-10.0} in
+ 10.[0123])
+ func_append compile_command " $wl-bind_at_load"
+ func_append finalize_command " $wl-bind_at_load"
+ ;;
+ esac
+ fi
+ # Time to change all our "foo.ltframework" stuff back to "-framework foo"
+ compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+ ;;
+ esac
+
+
+ # move library search paths that coincide with paths to not yet
+ # installed libraries to the beginning of the library search list
+ new_libs=
+ for path in $notinst_path; do
+ case " $new_libs " in
+ *" -L$path/$objdir "*) ;;
+ *)
+ case " $compile_deplibs " in
+ *" -L$path/$objdir "*)
+ func_append new_libs " -L$path/$objdir" ;;
+ esac
+ ;;
+ esac
+ done
+ for deplib in $compile_deplibs; do
+ case $deplib in
+ -L*)
+ case " $new_libs " in
+ *" $deplib "*) ;;
+ *) func_append new_libs " $deplib" ;;
+ esac
+ ;;
+ *) func_append new_libs " $deplib" ;;
+ esac
+ done
+ compile_deplibs=$new_libs
+
+
+ func_append compile_command " $compile_deplibs"
+ func_append finalize_command " $finalize_deplibs"
+
+ if test -n "$rpath$xrpath"; then
+ # If the user specified any rpath flags, then add them.
+ for libdir in $rpath $xrpath; do
+ # This is the magic to use -rpath.
+ case "$finalize_rpath " in
+ *" $libdir "*) ;;
+ *) func_append finalize_rpath " $libdir" ;;
+ esac
+ done
+ fi
+
+ # Now hardcode the library paths
+ rpath=
+ hardcode_libdirs=
+ for libdir in $compile_rpath $finalize_rpath; do
+ if test -n "$hardcode_libdir_flag_spec"; then
+ if test -n "$hardcode_libdir_separator"; then
+ if test -z "$hardcode_libdirs"; then
+ hardcode_libdirs=$libdir
+ else
+ # Just accumulate the unique libdirs.
+ case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+ *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+ ;;
+ *)
+ func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ func_append rpath " $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$perm_rpath " in
+ *" $libdir "*) ;;
+ *) func_append perm_rpath " $libdir" ;;
+ esac
+ fi
+ case $host in
+ *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ testbindir=`$ECHO "$libdir" | $SED -e 's*/lib$*/bin*'`
+ case :$dllsearchpath: in
+ *":$libdir:"*) ;;
+ ::) dllsearchpath=$libdir;;
+ *) func_append dllsearchpath ":$libdir";;
+ esac
+ case :$dllsearchpath: in
+ *":$testbindir:"*) ;;
+ ::) dllsearchpath=$testbindir;;
+ *) func_append dllsearchpath ":$testbindir";;
+ esac
+ ;;
+ esac
+ done
+ # Substitute the hardcoded libdirs into the rpath.
+ if test -n "$hardcode_libdir_separator" &&
+ test -n "$hardcode_libdirs"; then
+ libdir=$hardcode_libdirs
+ eval rpath=\" $hardcode_libdir_flag_spec\"
+ fi
+ compile_rpath=$rpath
+
+ rpath=
+ hardcode_libdirs=
+ for libdir in $finalize_rpath; do
+ if test -n "$hardcode_libdir_flag_spec"; then
+ if test -n "$hardcode_libdir_separator"; then
+ if test -z "$hardcode_libdirs"; then
+ hardcode_libdirs=$libdir
+ else
+ # Just accumulate the unique libdirs.
+ case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+ *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+ ;;
+ *)
+ func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+ ;;
+ esac
+ fi
+ else
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ func_append rpath " $flag"
+ fi
+ elif test -n "$runpath_var"; then
+ case "$finalize_perm_rpath " in
+ *" $libdir "*) ;;
+ *) func_append finalize_perm_rpath " $libdir" ;;
+ esac
+ fi
+ done
+ # Substitute the hardcoded libdirs into the rpath.
+ if test -n "$hardcode_libdir_separator" &&
+ test -n "$hardcode_libdirs"; then
+ libdir=$hardcode_libdirs
+ eval rpath=\" $hardcode_libdir_flag_spec\"
+ fi
+ finalize_rpath=$rpath
+
+ if test -n "$libobjs" && test yes = "$build_old_libs"; then
+ # Transform all the library objects into standard objects.
+ compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP`
+ finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP`
+ fi
+
+ func_generate_dlsyms "$outputname" "@PROGRAM@" false
+
+ # template prelinking step
+ if test -n "$prelink_cmds"; then
+ func_execute_cmds "$prelink_cmds" 'exit $?'
+ fi
+
+ wrappers_required=:
+ case $host in
+ *cegcc* | *mingw32ce*)
+ # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway.
+ wrappers_required=false
+ ;;
+ *cygwin* | *mingw* )
+ test yes = "$build_libtool_libs" || wrappers_required=false
+ ;;
+ *)
+ if test no = "$need_relink" || test yes != "$build_libtool_libs"; then
+ wrappers_required=false
+ fi
+ ;;
+ esac
+ $wrappers_required || {
+ # Replace the output file specification.
+ compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'`
+ link_command=$compile_command$compile_rpath
+
+ # We have no uninstalled library dependencies, so finalize right now.
+ exit_status=0
+ func_show_eval "$link_command" 'exit_status=$?'
+
+ if test -n "$postlink_cmds"; then
+ func_to_tool_file "$output"
+ postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+ func_execute_cmds "$postlink_cmds" 'exit $?'
+ fi
+
+ # Delete the generated files.
+ if test -f "$output_objdir/${outputname}S.$objext"; then
+ func_show_eval '$RM "$output_objdir/${outputname}S.$objext"'
+ fi
+
+ exit $exit_status
+ }
+
+ if test -n "$compile_shlibpath$finalize_shlibpath"; then
+ compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command"
+ fi
+ if test -n "$finalize_shlibpath"; then
+ finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command"
+ fi
+
+ compile_var=
+ finalize_var=
+ if test -n "$runpath_var"; then
+ if test -n "$perm_rpath"; then
+ # We should set the runpath_var.
+ rpath=
+ for dir in $perm_rpath; do
+ func_append rpath "$dir:"
+ done
+ compile_var="$runpath_var=\"$rpath\$$runpath_var\" "
+ fi
+ if test -n "$finalize_perm_rpath"; then
+ # We should set the runpath_var.
+ rpath=
+ for dir in $finalize_perm_rpath; do
+ func_append rpath "$dir:"
+ done
+ finalize_var="$runpath_var=\"$rpath\$$runpath_var\" "
+ fi
+ fi
+
+ if test yes = "$no_install"; then
+ # We don't need to create a wrapper script.
+ link_command=$compile_var$compile_command$compile_rpath
+ # Replace the output file specification.
+ link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'`
+ # Delete the old output file.
+ $opt_dry_run || $RM $output
+ # Link the executable and exit
+ func_show_eval "$link_command" 'exit $?'
+
+ if test -n "$postlink_cmds"; then
+ func_to_tool_file "$output"
+ postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+ func_execute_cmds "$postlink_cmds" 'exit $?'
+ fi
+
+ exit $EXIT_SUCCESS
+ fi
+
+ case $hardcode_action,$fast_install in
+ relink,*)
+ # Fast installation is not supported
+ link_command=$compile_var$compile_command$compile_rpath
+ relink_command=$finalize_var$finalize_command$finalize_rpath
+
+ func_warning "this platform does not like uninstalled shared libraries"
+ func_warning "'$output' will be relinked during installation"
+ ;;
+ *,yes)
+ link_command=$finalize_var$compile_command$finalize_rpath
+ relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'`
+ ;;
+ *,no)
+ link_command=$compile_var$compile_command$compile_rpath
+ relink_command=$finalize_var$finalize_command$finalize_rpath
+ ;;
+ *,needless)
+ link_command=$finalize_var$compile_command$finalize_rpath
+ relink_command=
+ ;;
+ esac
+
+ # Replace the output file specification.
+ link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'`
+
+ # Delete the old output files.
+ $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname
+
+ func_show_eval "$link_command" 'exit $?'
+
+ if test -n "$postlink_cmds"; then
+ func_to_tool_file "$output_objdir/$outputname"
+ postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+ func_execute_cmds "$postlink_cmds" 'exit $?'
+ fi
+
+ # Now create the wrapper script.
+ func_verbose "creating $output"
+
+ # Quote the relink command for shipping.
+ if test -n "$relink_command"; then
+ # Preserve any variables that may affect compiler behavior
+ for var in $variables_saved_for_relink; do
+ if eval test -z \"\${$var+set}\"; then
+ relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+ elif eval var_value=\$$var; test -z "$var_value"; then
+ relink_command="$var=; export $var; $relink_command"
+ else
+ func_quote_for_eval "$var_value"
+ relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+ fi
+ done
+ relink_command="(cd `pwd`; $relink_command)"
+ relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"`
+ fi
+
+ # Only actually do things if not in dry run mode.
+ $opt_dry_run || {
+ # win32 will think the script is a binary if it has
+ # a .exe suffix, so we strip it off here.
+ case $output in
+ *.exe) func_stripname '' '.exe' "$output"
+ output=$func_stripname_result ;;
+ esac
+ # test for cygwin because mv fails w/o .exe extensions
+ case $host in
+ *cygwin*)
+ exeext=.exe
+ func_stripname '' '.exe' "$outputname"
+ outputname=$func_stripname_result ;;
+ *) exeext= ;;
+ esac
+ case $host in
+ *cygwin* | *mingw* )
+ func_dirname_and_basename "$output" "" "."
+ output_name=$func_basename_result
+ output_path=$func_dirname_result
+ cwrappersource=$output_path/$objdir/lt-$output_name.c
+ cwrapper=$output_path/$output_name.exe
+ $RM $cwrappersource $cwrapper
+ trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15
+
+ func_emit_cwrapperexe_src > $cwrappersource
+
+ # The wrapper executable is built using the $host compiler,
+ # because it contains $host paths and files. If cross-
+ # compiling, it, like the target executable, must be
+ # executed on the $host or under an emulation environment.
+ $opt_dry_run || {
+ $LTCC $LTCFLAGS -o $cwrapper $cwrappersource
+ $STRIP $cwrapper
+ }
+
+ # Now, create the wrapper script for func_source use:
+ func_ltwrapper_scriptname $cwrapper
+ $RM $func_ltwrapper_scriptname_result
+ trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15
+ $opt_dry_run || {
+ # note: this script will not be executed, so do not chmod.
+ if test "x$build" = "x$host"; then
+ $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result
+ else
+ func_emit_wrapper no > $func_ltwrapper_scriptname_result
+ fi
+ }
+ ;;
+ * )
+ $RM $output
+ trap "$RM $output; exit $EXIT_FAILURE" 1 2 15
+
+ func_emit_wrapper no > $output
+ chmod +x $output
+ ;;
+ esac
+ }
+ exit $EXIT_SUCCESS
+ ;;
+ esac
+
+ # See if we need to build an old-fashioned archive.
+ for oldlib in $oldlibs; do
+
+ case $build_libtool_libs in
+ convenience)
+ oldobjs="$libobjs_save $symfileobj"
+ addlibs=$convenience
+ build_libtool_libs=no
+ ;;
+ module)
+ oldobjs=$libobjs_save
+ addlibs=$old_convenience
+ build_libtool_libs=no
+ ;;
+ *)
+ oldobjs="$old_deplibs $non_pic_objects"
+ $preload && test -f "$symfileobj" \
+ && func_append oldobjs " $symfileobj"
+ addlibs=$old_convenience
+ ;;
+ esac
+
+ if test -n "$addlibs"; then
+ gentop=$output_objdir/${outputname}x
+ func_append generated " $gentop"
+
+ func_extract_archives $gentop $addlibs
+ func_append oldobjs " $func_extract_archives_result"
+ fi
+
+ # Do each command in the archive commands.
+ if test -n "$old_archive_from_new_cmds" && test yes = "$build_libtool_libs"; then
+ cmds=$old_archive_from_new_cmds
+ else
+
+ # Add any objects from preloaded convenience libraries
+ if test -n "$dlprefiles"; then
+ gentop=$output_objdir/${outputname}x
+ func_append generated " $gentop"
+
+ func_extract_archives $gentop $dlprefiles
+ func_append oldobjs " $func_extract_archives_result"
+ fi
+
+ # POSIX demands no paths to be encoded in archives. We have
+ # to avoid creating archives with duplicate basenames if we
+ # might have to extract them afterwards, e.g., when creating a
+ # static archive out of a convenience library, or when linking
+ # the entirety of a libtool archive into another (currently
+ # not supported by libtool).
+ if (for obj in $oldobjs
+ do
+ func_basename "$obj"
+ $ECHO "$func_basename_result"
+ done | sort | sort -uc >/dev/null 2>&1); then
+ :
+ else
+ echo "copying selected object files to avoid basename conflicts..."
+ gentop=$output_objdir/${outputname}x
+ func_append generated " $gentop"
+ func_mkdir_p "$gentop"
+ save_oldobjs=$oldobjs
+ oldobjs=
+ counter=1
+ for obj in $save_oldobjs
+ do
+ func_basename "$obj"
+ objbase=$func_basename_result
+ case " $oldobjs " in
+ " ") oldobjs=$obj ;;
+ *[\ /]"$objbase "*)
+ while :; do
+ # Make sure we don't pick an alternate name that also
+ # overlaps.
+ newobj=lt$counter-$objbase
+ func_arith $counter + 1
+ counter=$func_arith_result
+ case " $oldobjs " in
+ *[\ /]"$newobj "*) ;;
+ *) if test ! -f "$gentop/$newobj"; then break; fi ;;
+ esac
+ done
+ func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj"
+ func_append oldobjs " $gentop/$newobj"
+ ;;
+ *) func_append oldobjs " $obj" ;;
+ esac
+ done
+ fi
+ func_to_tool_file "$oldlib" func_convert_file_msys_to_w32
+ tool_oldlib=$func_to_tool_file_result
+ eval cmds=\"$old_archive_cmds\"
+
+ func_len " $cmds"
+ len=$func_len_result
+ if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+ cmds=$old_archive_cmds
+ elif test -n "$archiver_list_spec"; then
+ func_verbose "using command file archive linking..."
+ for obj in $oldobjs
+ do
+ func_to_tool_file "$obj"
+ $ECHO "$func_to_tool_file_result"
+ done > $output_objdir/$libname.libcmd
+ func_to_tool_file "$output_objdir/$libname.libcmd"
+ oldobjs=" $archiver_list_spec$func_to_tool_file_result"
+ cmds=$old_archive_cmds
+ else
+ # the command line is too long to link in one step, link in parts
+ func_verbose "using piecewise archive linking..."
+ save_RANLIB=$RANLIB
+ RANLIB=:
+ objlist=
+ concat_cmds=
+ save_oldobjs=$oldobjs
+ oldobjs=
+ # Is there a better way of finding the last object in the list?
+ for obj in $save_oldobjs
+ do
+ last_oldobj=$obj
+ done
+ eval test_cmds=\"$old_archive_cmds\"
+ func_len " $test_cmds"
+ len0=$func_len_result
+ len=$len0
+ for obj in $save_oldobjs
+ do
+ func_len " $obj"
+ func_arith $len + $func_len_result
+ len=$func_arith_result
+ func_append objlist " $obj"
+ if test "$len" -lt "$max_cmd_len"; then
+ :
+ else
+ # the above command should be used before it gets too long
+ oldobjs=$objlist
+ if test "$obj" = "$last_oldobj"; then
+ RANLIB=$save_RANLIB
+ fi
+ test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+ eval concat_cmds=\"\$concat_cmds$old_archive_cmds\"
+ objlist=
+ len=$len0
+ fi
+ done
+ RANLIB=$save_RANLIB
+ oldobjs=$objlist
+ if test -z "$oldobjs"; then
+ eval cmds=\"\$concat_cmds\"
+ else
+ eval cmds=\"\$concat_cmds~\$old_archive_cmds\"
+ fi
+ fi
+ fi
+ func_execute_cmds "$cmds" 'exit $?'
+ done
+
+ test -n "$generated" && \
+ func_show_eval "${RM}r$generated"
+
+ # Now create the libtool archive.
+ case $output in
+ *.la)
+ old_library=
+ test yes = "$build_old_libs" && old_library=$libname.$libext
+ func_verbose "creating $output"
+
+ # Preserve any variables that may affect compiler behavior
+ for var in $variables_saved_for_relink; do
+ if eval test -z \"\${$var+set}\"; then
+ relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+ elif eval var_value=\$$var; test -z "$var_value"; then
+ relink_command="$var=; export $var; $relink_command"
+ else
+ func_quote_for_eval "$var_value"
+ relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+ fi
+ done
+ # Quote the link command for shipping.
+ relink_command="(cd `pwd`; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)"
+ relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"`
+ if test yes = "$hardcode_automatic"; then
+ relink_command=
+ fi
+
+ # Only create the output if not a dry run.
+ $opt_dry_run || {
+ for installed in no yes; do
+ if test yes = "$installed"; then
+ if test -z "$install_libdir"; then
+ break
+ fi
+ output=$output_objdir/${outputname}i
+ # Replace all uninstalled libtool libraries with the installed ones
+ newdependency_libs=
+ for deplib in $dependency_libs; do
+ case $deplib in
+ *.la)
+ func_basename "$deplib"
+ name=$func_basename_result
+ func_resolve_sysroot "$deplib"
+ eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result`
+ test -z "$libdir" && \
+ func_fatal_error "'$deplib' is not a valid libtool archive"
+ func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name"
+ ;;
+ -L*)
+ func_stripname -L '' "$deplib"
+ func_replace_sysroot "$func_stripname_result"
+ func_append newdependency_libs " -L$func_replace_sysroot_result"
+ ;;
+ -R*)
+ func_stripname -R '' "$deplib"
+ func_replace_sysroot "$func_stripname_result"
+ func_append newdependency_libs " -R$func_replace_sysroot_result"
+ ;;
+ *) func_append newdependency_libs " $deplib" ;;
+ esac
+ done
+ dependency_libs=$newdependency_libs
+ newdlfiles=
+
+ for lib in $dlfiles; do
+ case $lib in
+ *.la)
+ func_basename "$lib"
+ name=$func_basename_result
+ eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+ test -z "$libdir" && \
+ func_fatal_error "'$lib' is not a valid libtool archive"
+ func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name"
+ ;;
+ *) func_append newdlfiles " $lib" ;;
+ esac
+ done
+ dlfiles=$newdlfiles
+ newdlprefiles=
+ for lib in $dlprefiles; do
+ case $lib in
+ *.la)
+ # Only pass preopened files to the pseudo-archive (for
+ # eventual linking with the app. that links it) if we
+ # didn't already link the preopened objects directly into
+ # the library:
+ func_basename "$lib"
+ name=$func_basename_result
+ eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+ test -z "$libdir" && \
+ func_fatal_error "'$lib' is not a valid libtool archive"
+ func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name"
+ ;;
+ esac
+ done
+ dlprefiles=$newdlprefiles
+ else
+ newdlfiles=
+ for lib in $dlfiles; do
+ case $lib in
+ [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;;
+ *) abs=`pwd`"/$lib" ;;
+ esac
+ func_append newdlfiles " $abs"
+ done
+ dlfiles=$newdlfiles
+ newdlprefiles=
+ for lib in $dlprefiles; do
+ case $lib in
+ [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;;
+ *) abs=`pwd`"/$lib" ;;
+ esac
+ func_append newdlprefiles " $abs"
+ done
+ dlprefiles=$newdlprefiles
+ fi
+ $RM $output
+ # place dlname in correct position for cygwin
+ # In fact, it would be nice if we could use this code for all target
+ # systems that can't hard-code library paths into their executables
+ # and that have no shared library path variable independent of PATH,
+ # but it turns out we can't easily determine that from inspecting
+ # libtool variables, so we have to hard-code the OSs to which it
+ # applies here; at the moment, that means platforms that use the PE
+ # object format with DLL files. See the long comment at the top of
+ # tests/bindir.at for full details.
+ tdlname=$dlname
+ case $host,$output,$installed,$module,$dlname in
+ *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll)
+ # If a -bindir argument was supplied, place the dll there.
+ if test -n "$bindir"; then
+ func_relative_path "$install_libdir" "$bindir"
+ tdlname=$func_relative_path_result/$dlname
+ else
+ # Otherwise fall back on heuristic.
+ tdlname=../bin/$dlname
+ fi
+ ;;
+ esac
+ $ECHO > $output "\
+# $outputname - a libtool library file
+# Generated by $PROGRAM (GNU $PACKAGE) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# The name that we can dlopen(3).
+dlname='$tdlname'
+
+# Names of this library.
+library_names='$library_names'
+
+# The name of the static archive.
+old_library='$old_library'
+
+# Linker flags that cannot go in dependency_libs.
+inherited_linker_flags='$new_inherited_linker_flags'
+
+# Libraries that this one depends upon.
+dependency_libs='$dependency_libs'
+
+# Names of additional weak libraries provided by this library
+weak_library_names='$weak_libs'
+
+# Version information for $libname.
+current=$current
+age=$age
+revision=$revision
+
+# Is this an already installed library?
+installed=$installed
+
+# Should we warn about portability when linking against -modules?
+shouldnotlink=$module
+
+# Files to dlopen/dlpreopen
+dlopen='$dlfiles'
+dlpreopen='$dlprefiles'
+
+# Directory that this library needs to be installed in:
+libdir='$install_libdir'"
+ if test no,yes = "$installed,$need_relink"; then
+ $ECHO >> $output "\
+relink_command=\"$relink_command\""
+ fi
+ done
+ }
+
+ # Do a symbolic link so that the libtool archive can be found in
+ # LD_LIBRARY_PATH before the program is installed.
+ func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?'
+ ;;
+ esac
+ exit $EXIT_SUCCESS
+}
+
+if test link = "$opt_mode" || test relink = "$opt_mode"; then
+ func_mode_link ${1+"$@"}
+fi
+
+
+# func_mode_uninstall arg...
+func_mode_uninstall ()
+{
+ $debug_cmd
+
+ RM=$nonopt
+ files=
+ rmforce=false
+ exit_status=0
+
+ # This variable tells wrapper scripts just to set variables rather
+ # than running their programs.
+ libtool_install_magic=$magic
+
+ for arg
+ do
+ case $arg in
+ -f) func_append RM " $arg"; rmforce=: ;;
+ -*) func_append RM " $arg" ;;
+ *) func_append files " $arg" ;;
+ esac
+ done
+
+ test -z "$RM" && \
+ func_fatal_help "you must specify an RM program"
+
+ rmdirs=
+
+ for file in $files; do
+ func_dirname "$file" "" "."
+ dir=$func_dirname_result
+ if test . = "$dir"; then
+ odir=$objdir
+ else
+ odir=$dir/$objdir
+ fi
+ func_basename "$file"
+ name=$func_basename_result
+ test uninstall = "$opt_mode" && odir=$dir
+
+ # Remember odir for removal later, being careful to avoid duplicates
+ if test clean = "$opt_mode"; then
+ case " $rmdirs " in
+ *" $odir "*) ;;
+ *) func_append rmdirs " $odir" ;;
+ esac
+ fi
+
+ # Don't error if the file doesn't exist and rm -f was used.
+ if { test -L "$file"; } >/dev/null 2>&1 ||
+ { test -h "$file"; } >/dev/null 2>&1 ||
+ test -f "$file"; then
+ :
+ elif test -d "$file"; then
+ exit_status=1
+ continue
+ elif $rmforce; then
+ continue
+ fi
+
+ rmfiles=$file
+
+ case $name in
+ *.la)
+ # Possibly a libtool archive, so verify it.
+ if func_lalib_p "$file"; then
+ func_source $dir/$name
+
+ # Delete the libtool libraries and symlinks.
+ for n in $library_names; do
+ func_append rmfiles " $odir/$n"
+ done
+ test -n "$old_library" && func_append rmfiles " $odir/$old_library"
+
+ case $opt_mode in
+ clean)
+ case " $library_names " in
+ *" $dlname "*) ;;
+ *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;;
+ esac
+ test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i"
+ ;;
+ uninstall)
+ if test -n "$library_names"; then
+ # Do each command in the postuninstall commands.
+ func_execute_cmds "$postuninstall_cmds" '$rmforce || exit_status=1'
+ fi
+
+ if test -n "$old_library"; then
+ # Do each command in the old_postuninstall commands.
+ func_execute_cmds "$old_postuninstall_cmds" '$rmforce || exit_status=1'
+ fi
+ # FIXME: should reinstall the best remaining shared library.
+ ;;
+ esac
+ fi
+ ;;
+
+ *.lo)
+ # Possibly a libtool object, so verify it.
+ if func_lalib_p "$file"; then
+
+ # Read the .lo file
+ func_source $dir/$name
+
+ # Add PIC object to the list of files to remove.
+ if test -n "$pic_object" && test none != "$pic_object"; then
+ func_append rmfiles " $dir/$pic_object"
+ fi
+
+ # Add non-PIC object to the list of files to remove.
+ if test -n "$non_pic_object" && test none != "$non_pic_object"; then
+ func_append rmfiles " $dir/$non_pic_object"
+ fi
+ fi
+ ;;
+
+ *)
+ if test clean = "$opt_mode"; then
+ noexename=$name
+ case $file in
+ *.exe)
+ func_stripname '' '.exe' "$file"
+ file=$func_stripname_result
+ func_stripname '' '.exe' "$name"
+ noexename=$func_stripname_result
+ # $file with .exe has already been added to rmfiles,
+ # add $file without .exe
+ func_append rmfiles " $file"
+ ;;
+ esac
+ # Do a test to see if this is a libtool program.
+ if func_ltwrapper_p "$file"; then
+ if func_ltwrapper_executable_p "$file"; then
+ func_ltwrapper_scriptname "$file"
+ relink_command=
+ func_source $func_ltwrapper_scriptname_result
+ func_append rmfiles " $func_ltwrapper_scriptname_result"
+ else
+ relink_command=
+ func_source $dir/$noexename
+ fi
+
+ # note $name still contains .exe if it was in $file originally
+ # as does the version of $file that was added into $rmfiles
+ func_append rmfiles " $odir/$name $odir/${name}S.$objext"
+ if test yes = "$fast_install" && test -n "$relink_command"; then
+ func_append rmfiles " $odir/lt-$name"
+ fi
+ if test "X$noexename" != "X$name"; then
+ func_append rmfiles " $odir/lt-$noexename.c"
+ fi
+ fi
+ fi
+ ;;
+ esac
+ func_show_eval "$RM $rmfiles" 'exit_status=1'
+ done
+
+ # Try to remove the $objdir's in the directories where we deleted files
+ for dir in $rmdirs; do
+ if test -d "$dir"; then
+ func_show_eval "rmdir $dir >/dev/null 2>&1"
+ fi
+ done
+
+ exit $exit_status
+}
+
+if test uninstall = "$opt_mode" || test clean = "$opt_mode"; then
+ func_mode_uninstall ${1+"$@"}
+fi
+
+test -z "$opt_mode" && {
+ help=$generic_help
+ func_fatal_help "you must specify a MODE"
+}
+
+test -z "$exec_cmd" && \
+ func_fatal_help "invalid operation mode '$opt_mode'"
+
+if test -n "$exec_cmd"; then
+ eval exec "$exec_cmd"
+ exit $EXIT_FAILURE
+fi
+
+exit $exit_status
+
+
+# The TAGs below are defined such that we never get into a situation
+# where we disable both kinds of libraries. Given conflicting
+# choices, we go for a static library, that is the most portable,
+# since we can't tell whether shared libraries were disabled because
+# the user asked for that or because the platform doesn't support
+# them. This is particularly important on AIX, because we don't
+# support having both static and shared libraries enabled at the same
+# time on that platform, so we default to a shared-only configuration.
+# If a disable-shared tag is given, we'll fallback to a static-only
+# configuration. But we'll never go from static-only to shared-only.
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-shared
+build_libtool_libs=no
+build_old_libs=yes
+# ### END LIBTOOL TAG CONFIG: disable-shared
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-static
+build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac`
+# ### END LIBTOOL TAG CONFIG: disable-static
+
+# Local Variables:
+# mode:shell-script
+# sh-indentation:2
+# End:
diff --git a/contrib/ldapc++/missing b/contrib/ldapc++/missing
new file mode 100755
index 0000000..f62bbae
--- /dev/null
+++ b/contrib/ldapc++/missing
@@ -0,0 +1,215 @@
+#! /bin/sh
+# Common wrapper for a few potentially missing GNU programs.
+
+scriptversion=2013-10-28.13; # UTC
+
+# Copyright (C) 1996-2014 Free Software Foundation, Inc.
+# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+if test $# -eq 0; then
+ echo 1>&2 "Try '$0 --help' for more information"
+ exit 1
+fi
+
+case $1 in
+
+ --is-lightweight)
+ # Used by our autoconf macros to check whether the available missing
+ # script is modern enough.
+ exit 0
+ ;;
+
+ --run)
+ # Back-compat with the calling convention used by older automake.
+ shift
+ ;;
+
+ -h|--h|--he|--hel|--help)
+ echo "\
+$0 [OPTION]... PROGRAM [ARGUMENT]...
+
+Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
+to PROGRAM being missing or too old.
+
+Options:
+ -h, --help display this help and exit
+ -v, --version output version information and exit
+
+Supported PROGRAM values:
+ aclocal autoconf autoheader autom4te automake makeinfo
+ bison yacc flex lex help2man
+
+Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
+'g' are ignored when checking the name.
+
+Send bug reports to <bug-automake@gnu.org>."
+ exit $?
+ ;;
+
+ -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
+ echo "missing $scriptversion (GNU Automake)"
+ exit $?
+ ;;
+
+ -*)
+ echo 1>&2 "$0: unknown '$1' option"
+ echo 1>&2 "Try '$0 --help' for more information"
+ exit 1
+ ;;
+
+esac
+
+# Run the given program, remember its exit status.
+"$@"; st=$?
+
+# If it succeeded, we are done.
+test $st -eq 0 && exit 0
+
+# Also exit now if we it failed (or wasn't found), and '--version' was
+# passed; such an option is passed most likely to detect whether the
+# program is present and works.
+case $2 in --version|--help) exit $st;; esac
+
+# Exit code 63 means version mismatch. This often happens when the user
+# tries to use an ancient version of a tool on a file that requires a
+# minimum version.
+if test $st -eq 63; then
+ msg="probably too old"
+elif test $st -eq 127; then
+ # Program was missing.
+ msg="missing on your system"
+else
+ # Program was found and executed, but failed. Give up.
+ exit $st
+fi
+
+perl_URL=http://www.perl.org/
+flex_URL=http://flex.sourceforge.net/
+gnu_software_URL=http://www.gnu.org/software
+
+program_details ()
+{
+ case $1 in
+ aclocal|automake)
+ echo "The '$1' program is part of the GNU Automake package:"
+ echo "<$gnu_software_URL/automake>"
+ echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
+ echo "<$gnu_software_URL/autoconf>"
+ echo "<$gnu_software_URL/m4/>"
+ echo "<$perl_URL>"
+ ;;
+ autoconf|autom4te|autoheader)
+ echo "The '$1' program is part of the GNU Autoconf package:"
+ echo "<$gnu_software_URL/autoconf/>"
+ echo "It also requires GNU m4 and Perl in order to run:"
+ echo "<$gnu_software_URL/m4/>"
+ echo "<$perl_URL>"
+ ;;
+ esac
+}
+
+give_advice ()
+{
+ # Normalize program name to check for.
+ normalized_program=`echo "$1" | sed '
+ s/^gnu-//; t
+ s/^gnu//; t
+ s/^g//; t'`
+
+ printf '%s\n' "'$1' is $msg."
+
+ configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
+ case $normalized_program in
+ autoconf*)
+ echo "You should only need it if you modified 'configure.ac',"
+ echo "or m4 files included by it."
+ program_details 'autoconf'
+ ;;
+ autoheader*)
+ echo "You should only need it if you modified 'acconfig.h' or"
+ echo "$configure_deps."
+ program_details 'autoheader'
+ ;;
+ automake*)
+ echo "You should only need it if you modified 'Makefile.am' or"
+ echo "$configure_deps."
+ program_details 'automake'
+ ;;
+ aclocal*)
+ echo "You should only need it if you modified 'acinclude.m4' or"
+ echo "$configure_deps."
+ program_details 'aclocal'
+ ;;
+ autom4te*)
+ echo "You might have modified some maintainer files that require"
+ echo "the 'autom4te' program to be rebuilt."
+ program_details 'autom4te'
+ ;;
+ bison*|yacc*)
+ echo "You should only need it if you modified a '.y' file."
+ echo "You may want to install the GNU Bison package:"
+ echo "<$gnu_software_URL/bison/>"
+ ;;
+ lex*|flex*)
+ echo "You should only need it if you modified a '.l' file."
+ echo "You may want to install the Fast Lexical Analyzer package:"
+ echo "<$flex_URL>"
+ ;;
+ help2man*)
+ echo "You should only need it if you modified a dependency" \
+ "of a man page."
+ echo "You may want to install the GNU Help2man package:"
+ echo "<$gnu_software_URL/help2man/>"
+ ;;
+ makeinfo*)
+ echo "You should only need it if you modified a '.texi' file, or"
+ echo "any other file indirectly affecting the aspect of the manual."
+ echo "You might want to install the Texinfo package:"
+ echo "<$gnu_software_URL/texinfo/>"
+ echo "The spurious makeinfo call might also be the consequence of"
+ echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
+ echo "want to install GNU make:"
+ echo "<$gnu_software_URL/make/>"
+ ;;
+ *)
+ echo "You might have modified some files without having the proper"
+ echo "tools for further handling them. Check the 'README' file, it"
+ echo "often tells you about the needed prerequisites for installing"
+ echo "this package. You may also peek at any GNU archive site, in"
+ echo "case some other package contains this missing '$1' program."
+ ;;
+ esac
+}
+
+give_advice "$1" | sed -e '1s/^/WARNING: /' \
+ -e '2,$s/^/ /' >&2
+
+# Propagate the correct exit status (expected to be 127 for a program
+# not found, 63 for a program that failed due to version mismatch).
+exit $st
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/contrib/ldapc++/src/LDAPAddRequest.cpp b/contrib/ldapc++/src/LDAPAddRequest.cpp
new file mode 100644
index 0000000..8ae0ea1
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPAddRequest.cpp
@@ -0,0 +1,79 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#include <ldap.h>
+
+#include "debug.h"
+
+#include "LDAPAddRequest.h"
+#include "LDAPEntry.h"
+#include "LDAPException.h"
+#include "LDAPMessageQueue.h"
+#include "LDAPResult.h"
+
+using namespace std;
+
+LDAPAddRequest::LDAPAddRequest(const LDAPAddRequest& req) :
+ LDAPRequest(req){
+ DEBUG(LDAP_DEBUG_CONSTRUCT, "LDAPAddRequest::LDAPAddRequest(&)" << endl);
+ m_entry=new LDAPEntry(*(req.m_entry));
+}
+
+LDAPAddRequest::LDAPAddRequest(const LDAPEntry* entry,
+ LDAPAsynConnection *connect, const LDAPConstraints *cons,
+ bool isReferral, const LDAPRequest* parent)
+ : LDAPRequest(connect, cons, isReferral,parent){
+ DEBUG(LDAP_DEBUG_CONSTRUCT, "LDAPAddRequest::LDAPAddRequest()" << endl);
+ DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER,
+ " entry:" << entry << endl
+ << " isReferral:" << isReferral << endl);
+ m_requestType = LDAPRequest::ADD;
+ m_entry = new LDAPEntry(*entry);
+}
+
+LDAPAddRequest::~LDAPAddRequest(){
+ DEBUG(LDAP_DEBUG_DESTROY, "LDAPAddRequest::~LDAPAddRequest()" << endl);
+ delete m_entry;
+}
+
+LDAPMessageQueue* LDAPAddRequest::sendRequest(){
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPAddRequest::sendRequest()" << endl);
+ int msgID=0;
+ const LDAPAttributeList* list=m_entry->getAttributes();
+ LDAPMod** attrs=list->toLDAPModArray();
+ LDAPControl** tmpSrvCtrls = m_cons->getSrvCtrlsArray();
+ LDAPControl** tmpClCtrls = m_cons->getClCtrlsArray();
+ int err=ldap_add_ext(m_connection->getSessionHandle(),
+ m_entry->getDN().c_str(),attrs,tmpSrvCtrls,tmpClCtrls,&msgID);
+ LDAPControlSet::freeLDAPControlArray(tmpSrvCtrls);
+ LDAPControlSet::freeLDAPControlArray(tmpClCtrls);
+ ldap_mods_free(attrs,1);
+ if(err != LDAP_SUCCESS){
+ throw LDAPException(err);
+ }else{
+ m_msgID=msgID;
+ return new LDAPMessageQueue(this);
+ }
+}
+
+LDAPRequest* LDAPAddRequest::followReferral(LDAPMsg* ref){
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPAddRequest::followReferral()"<< endl);
+ LDAPUrlList::const_iterator usedUrl;
+ LDAPUrlList urls = ((LDAPResult*)ref)->getReferralUrls();
+ LDAPAsynConnection* con = 0;
+ try {
+ con = getConnection()->referralConnect(urls,usedUrl,m_cons);
+ } catch(LDAPException e){
+ delete con;
+ return 0;
+ }
+ if(con != 0){
+ return new LDAPAddRequest(m_entry, con, m_cons,true,this);
+ }
+ return 0;
+}
+
diff --git a/contrib/ldapc++/src/LDAPAddRequest.h b/contrib/ldapc++/src/LDAPAddRequest.h
new file mode 100644
index 0000000..c74f7b3
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPAddRequest.h
@@ -0,0 +1,30 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDAP_ADD_REQUEST_H
+#define LDAP_ADD_REQUEST_H
+
+#include <LDAPRequest.h>
+#include <LDAPEntry.h>
+
+class LDAPMessageQueue;
+
+class LDAPAddRequest : LDAPRequest {
+ public:
+ LDAPAddRequest(const LDAPAddRequest& req);
+ LDAPAddRequest(const LDAPEntry* entry,
+ LDAPAsynConnection *connect,
+ const LDAPConstraints *cons, bool isReferral=false,
+ const LDAPRequest* parent=0);
+ virtual ~LDAPAddRequest();
+ virtual LDAPMessageQueue* sendRequest();
+ virtual LDAPRequest* followReferral(LDAPMsg* refs);
+ private:
+ LDAPEntry* m_entry;
+
+};
+#endif // LDAP_ADD_REQUEST_H
+
diff --git a/contrib/ldapc++/src/LDAPAsynConnection.cpp b/contrib/ldapc++/src/LDAPAsynConnection.cpp
new file mode 100644
index 0000000..98fbd25
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPAsynConnection.cpp
@@ -0,0 +1,366 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#include "config.h"
+#include "debug.h"
+#include "LDAPAsynConnection.h"
+
+#include "LDAPAddRequest.h"
+#include "LDAPBindRequest.h"
+#include "LDAPCompareRequest.h"
+#include "LDAPDeleteRequest.h"
+#include "LDAPExtRequest.h"
+#include "LDAPEntry.h"
+#include "LDAPModDNRequest.h"
+#include "LDAPModifyRequest.h"
+#include "LDAPRequest.h"
+#include "LDAPRebind.h"
+#include "LDAPRebindAuth.h"
+#include "LDAPSearchRequest.h"
+#include <lber.h>
+#include <sstream>
+
+using namespace std;
+
+LDAPAsynConnection::LDAPAsynConnection(const string& url, int port,
+ LDAPConstraints *cons ){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPAsynConnection::LDAPAsynConnection()"
+ << endl);
+ DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER,
+ " URL:" << url << endl << " port:" << port << endl);
+ cur_session=0;
+ m_constr = 0;
+ // Is this an LDAP URI?
+ if ( url.find("://") == std::string::npos ) {
+ this->init(url, port);
+ } else {
+ this->initialize(url);
+ }
+ this->setConstraints(cons);
+}
+
+LDAPAsynConnection::~LDAPAsynConnection(){
+ unbind();
+ delete m_constr;
+}
+
+void LDAPAsynConnection::init(const string& hostname, int port){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAsynConnection::init" << endl);
+ DEBUG(LDAP_DEBUG_TRACE | LDAP_DEBUG_PARAMETER,
+ " hostname:" << hostname << endl
+ << " port:" << port << endl);
+
+ unbind();
+
+ m_uri.setScheme("ldap");
+ m_uri.setHost(hostname);
+ m_uri.setPort(port);
+
+ const char *ldapuri = m_uri.getURLString().c_str();
+ int ret = ldap_initialize(&cur_session, ldapuri);
+ if ( ret != LDAP_SUCCESS ) {
+ throw LDAPException( ret );
+ }
+ int opt=3;
+ ldap_set_option(cur_session, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
+ ldap_set_option(cur_session, LDAP_OPT_PROTOCOL_VERSION, &opt);
+}
+
+void LDAPAsynConnection::initialize(const std::string& uri){
+ unbind();
+
+ m_uri.setURLString(uri);
+ int ret = ldap_initialize(&cur_session, m_uri.getURLString().c_str());
+ if ( ret != LDAP_SUCCESS ) {
+ throw LDAPException( ret );
+ }
+ int opt=3;
+ ldap_set_option(cur_session, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
+ ldap_set_option(cur_session, LDAP_OPT_PROTOCOL_VERSION, &opt);
+}
+
+void LDAPAsynConnection::start_tls(){
+ int ret = ldap_start_tls_s( cur_session, NULL, NULL );
+ if( ret != LDAP_SUCCESS ) {
+ throw LDAPException(this);
+ }
+}
+
+LDAPMessageQueue* LDAPAsynConnection::bind(const string& dn,
+ const string& passwd, const LDAPConstraints *cons){
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPAsynConnection::bind()" << endl);
+ DEBUG(LDAP_DEBUG_TRACE | LDAP_DEBUG_PARAMETER, " dn:" << dn << endl
+ << " passwd:" << passwd << endl);
+ LDAPBindRequest *req = new LDAPBindRequest(dn,passwd,this,cons);
+ try{
+ LDAPMessageQueue *ret = req->sendRequest();
+ return ret;
+ }catch(LDAPException e){
+ delete req;
+ throw;
+ }
+}
+
+LDAPMessageQueue* LDAPAsynConnection::saslBind(const std::string &mech,
+ const std::string &cred,
+ const LDAPConstraints *cons)
+{
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPAsynConnection::saslBind()" << endl);
+ LDAPSaslBindRequest *req = new LDAPSaslBindRequest(mech, cred, this, cons);
+ try{
+ LDAPMessageQueue *ret = req->sendRequest();
+ return ret;
+ }catch(LDAPException e){
+ delete req;
+ throw;
+ }
+
+}
+
+LDAPMessageQueue* LDAPAsynConnection::saslInteractiveBind(
+ const std::string &mech,
+ int flags,
+ SaslInteractionHandler *sih,
+ const LDAPConstraints *cons)
+{
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPAsynConnection::saslInteractiveBind"
+ << std::endl);
+ LDAPSaslInteractiveBind *req =
+ new LDAPSaslInteractiveBind(mech, flags, sih, this, cons);
+ try {
+ LDAPMessageQueue *ret = req->sendRequest();
+ return ret;
+ }catch(LDAPException e){
+ delete req;
+ throw;
+ }
+}
+
+LDAPMessageQueue* LDAPAsynConnection::search(const string& base,int scope,
+ const string& filter,
+ const StringList& attrs,
+ bool attrsOnly,
+ const LDAPConstraints *cons){
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPAsynConnection::search()" << endl);
+ DEBUG(LDAP_DEBUG_TRACE | LDAP_DEBUG_PARAMETER, " base:" << base << endl
+ << " scope:" << scope << endl
+ << " filter:" << filter << endl );
+ LDAPSearchRequest *req = new LDAPSearchRequest(base, scope,filter, attrs,
+ attrsOnly, this, cons);
+ try{
+ LDAPMessageQueue *ret = req->sendRequest();
+ return ret;
+ }catch(LDAPException e){
+ delete req;
+ throw;
+ }
+}
+
+LDAPMessageQueue* LDAPAsynConnection::del(const string& dn,
+ const LDAPConstraints *cons){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAsynConnection::del()" << endl);
+ DEBUG(LDAP_DEBUG_TRACE | LDAP_DEBUG_PARAMETER," dn:" << dn << endl);
+ LDAPDeleteRequest *req = new LDAPDeleteRequest(dn, this, cons);
+ try{
+ LDAPMessageQueue *ret = req->sendRequest();
+ return ret;
+ }catch(LDAPException e){
+ delete req;
+ throw;
+ }
+}
+
+LDAPMessageQueue* LDAPAsynConnection::compare(const string& dn,
+ const LDAPAttribute& attr, const LDAPConstraints *cons){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAsynConnection::compare()" << endl);
+ DEBUG(LDAP_DEBUG_TRACE | LDAP_DEBUG_PARAMETER," dn:" << dn << endl
+ << " attr:" << attr << endl);
+ LDAPCompareRequest *req = new LDAPCompareRequest(dn, attr, this, cons);
+ try{
+ LDAPMessageQueue *ret = req->sendRequest();
+ return ret;
+ }catch(LDAPException e){
+ delete req;
+ throw;
+ }
+}
+
+LDAPMessageQueue* LDAPAsynConnection::add( const LDAPEntry* le,
+ const LDAPConstraints *cons){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAsynConnection::add()" << endl);
+ DEBUG(LDAP_DEBUG_TRACE | LDAP_DEBUG_PARAMETER," entry:" << *le << endl);
+ LDAPAddRequest *req = new LDAPAddRequest(le, this, cons);
+ try{
+ LDAPMessageQueue *ret = req->sendRequest();
+ return ret;
+ }catch(LDAPException e){
+ delete req;
+ throw;
+ }
+}
+
+LDAPMessageQueue* LDAPAsynConnection::modify(const string& dn,
+ const LDAPModList *mod, const LDAPConstraints *cons){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAsynConnection::modify()" << endl);
+ DEBUG(LDAP_DEBUG_TRACE | LDAP_DEBUG_PARAMETER," dn:" << dn << endl);
+ LDAPModifyRequest *req = new LDAPModifyRequest(dn, mod, this, cons);
+ try{
+ LDAPMessageQueue *ret = req->sendRequest();
+ return ret;
+ }catch(LDAPException e){
+ delete req;
+ throw;
+ }
+}
+
+LDAPMessageQueue* LDAPAsynConnection::rename(const string& dn,
+ const string& newRDN, bool delOldRDN, const string& newParentDN,
+ const LDAPConstraints *cons ){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAsynConnection::rename()" << endl);
+ DEBUG(LDAP_DEBUG_TRACE | LDAP_DEBUG_PARAMETER," dn:" << dn << endl
+ << " newRDN:" << newRDN << endl
+ << " newParentDN:" << newParentDN << endl
+ << " delOldRDN:" << delOldRDN << endl);
+ LDAPModDNRequest *req = new LDAPModDNRequest(dn, newRDN, delOldRDN,
+ newParentDN, this, cons );
+ try{
+ LDAPMessageQueue *ret = req->sendRequest();
+ return ret;
+ }catch(LDAPException e){
+ delete req;
+ throw;
+ }
+}
+
+
+LDAPMessageQueue* LDAPAsynConnection::extOperation(const string& oid,
+ const string& value, const LDAPConstraints *cons ){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAsynConnection::extOperation()" << endl);
+ DEBUG(LDAP_DEBUG_TRACE | LDAP_DEBUG_PARAMETER," oid:" << oid << endl);
+ LDAPExtRequest *req = new LDAPExtRequest(oid, value, this,cons);
+ try{
+ LDAPMessageQueue *ret = req->sendRequest();
+ return ret;
+ }catch(LDAPException e){
+ delete req;
+ throw;
+ }
+}
+
+
+void LDAPAsynConnection::abandon(LDAPMessageQueue *q){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAsynConnection::abandon()" << endl);
+ LDAPRequestStack *reqStack=q->getRequestStack();
+ LDAPRequest *req;
+ while(! reqStack->empty()){
+ req=reqStack->top();
+ if (ldap_abandon_ext(cur_session, req->getMsgID(), 0, 0)
+ != LDAP_SUCCESS){
+ throw LDAPException(this);
+ }
+ delete req;
+ reqStack->pop();
+ }
+}
+
+void LDAPAsynConnection::unbind(){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAsynConnection::unbind()" << endl);
+ if(cur_session){
+ LDAPControl** tmpSrvCtrls=m_constr->getSrvCtrlsArray();
+ LDAPControl** tmpClCtrls=m_constr->getClCtrlsArray();
+ int err=ldap_unbind_ext(cur_session, tmpSrvCtrls, tmpClCtrls);
+ cur_session=0;
+ LDAPControlSet::freeLDAPControlArray(tmpSrvCtrls);
+ LDAPControlSet::freeLDAPControlArray(tmpClCtrls);
+ if(err != LDAP_SUCCESS){
+ throw LDAPException(err);
+ }
+ }
+}
+
+void LDAPAsynConnection::setConstraints(LDAPConstraints *cons){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAsynConnection::setConstraints()" << endl);
+ delete m_constr;
+ m_constr=cons;
+}
+
+const LDAPConstraints* LDAPAsynConnection::getConstraints() const {
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAsynConnection::getConstraints()" << endl);
+ return m_constr;
+}
+
+TlsOptions LDAPAsynConnection::getTlsOptions() const {
+ return TlsOptions( cur_session );
+}
+
+LDAP* LDAPAsynConnection::getSessionHandle() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAsynConnection::getSessionHandle()" << endl);
+ return cur_session;
+}
+
+const string& LDAPAsynConnection::getHost() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAsynConnection::setHost()" << endl);
+ return m_uri.getHost();
+}
+
+int LDAPAsynConnection::getPort() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAsynConnection::getPort()" << endl);
+ return m_uri.getPort();
+}
+
+LDAPAsynConnection* LDAPAsynConnection::referralConnect(
+ const LDAPUrlList& urls, LDAPUrlList::const_iterator& usedUrl,
+ const LDAPConstraints* cons) const {
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPAsynConnection::referralConnect()" << endl)
+ LDAPUrlList::const_iterator conUrl;
+ LDAPAsynConnection* tmpConn=0;
+ const LDAPRebind* rebind = cons->getReferralRebind();
+ LDAPRebindAuth* auth = 0;
+
+ for(conUrl=urls.begin(); conUrl!=urls.end(); conUrl++){
+ string host= conUrl->getHost();
+ int port= conUrl->getPort();
+ DEBUG(LDAP_DEBUG_TRACE," connecting to: " << host << ":" <<
+ port << endl);
+ //Set the new connection's constraints-object ?
+ tmpConn=new LDAPAsynConnection(host.c_str(),port);
+ int err=0;
+
+ if(rebind){
+ auth=rebind->getRebindAuth(host, port);
+ }
+ if(auth){
+ string dn = auth->getDN();
+ string passwd = auth->getPassword();
+ const char* c_dn=0;
+ struct berval c_passwd = { 0, 0 };
+ if(dn != ""){
+ c_dn = dn.c_str();
+ }
+ if(passwd != ""){
+ c_passwd.bv_val = const_cast<char*>(passwd.c_str());
+ c_passwd.bv_len = passwd.size();
+ }
+ err = ldap_sasl_bind_s(tmpConn->getSessionHandle(), c_dn,
+ LDAP_SASL_SIMPLE, &c_passwd, NULL, NULL, NULL);
+ } else {
+ // Do anonymous bind
+ err = ldap_sasl_bind_s(tmpConn->getSessionHandle(),NULL,
+ LDAP_SASL_SIMPLE, NULL, NULL, NULL, NULL);
+ }
+ if( err == LDAP_SUCCESS ){
+ usedUrl=conUrl;
+ return tmpConn;
+ }else{
+ delete tmpConn;
+ tmpConn=0;
+ }
+ auth=0;
+ }
+ return 0;
+}
+
diff --git a/contrib/ldapc++/src/LDAPAsynConnection.h b/contrib/ldapc++/src/LDAPAsynConnection.h
new file mode 100644
index 0000000..ba2016e
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPAsynConnection.h
@@ -0,0 +1,338 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#ifndef LDAP_ASYN_CONNECTION_H
+#define LDAP_ASYN_CONNECTION_H
+
+#include<iostream>
+#include<string>
+
+#include<ldap.h>
+
+#include <LDAPEntry.h>
+#include <LDAPException.h>
+#include <LDAPMessageQueue.h>
+#include <LDAPConstraints.h>
+#include <LDAPModification.h>
+#include <LDAPModList.h>
+#include <LDAPUrl.h>
+#include <LDAPUrlList.h>
+#include <SaslInteractionHandler.h>
+#include <TlsOptions.h>
+
+//* Main class for an asynchronous LDAP connection
+/**
+ * This class represents an asynchronous connection to an LDAP-Server. It
+ * provides the methods for authentication, and all other LDAP-Operations
+ * (e.g. search, add, delete, etc.)
+ * All of the LDAP-Operations return a pointer to a LDAPMessageQueue-Object,
+ * which can be used to obtain the results of that operation.
+ * A basic example of this class could be like this: <BR>
+ * 1. Create a new LDAPAsynConnection Object: <BR>
+ * 2. Use the init-method to initialize the connection <BR>
+ * 3. Call the bind-method to authenticate to the directory <BR>
+ * 4. Obtain the bind results from the return LDAPMessageQueue-Object <BR>
+ * 5. Perform on of the operations on the directory (add, delete, search, ..)
+ * <BR>
+ * 6. Use the return LDAPMessageQueue to obtain the results of the operation
+ * <BR>
+ * 7. Close the connection (feature not implemented yet :) ) <BR>
+ */
+class LDAPAsynConnection{
+ public :
+ /**
+ * Constant for the Search-Operation to indicate a Base-Level
+ * Search
+ */
+ static const int SEARCH_BASE=0;
+
+ /**
+ * Constant for the Search-Operation to indicate a One-Level
+ * Search
+ */
+ static const int SEARCH_ONE=1;
+
+ /**
+ * Constant for the Search-Operation to indicate a subtree
+ * Search
+ */
+ static const int SEARCH_SUB=2;
+
+ /** Constructor that initializes a connection to a server
+ * @param hostname Name (or IP-Address) of the destination host
+ * @param port Port the LDAP server is running on
+ * @param cons Default constraints to use with operations over
+ * this connection
+ */
+ LDAPAsynConnection(const std::string& url=std::string("localhost"),
+ int port=0, LDAPConstraints *cons=new LDAPConstraints() );
+
+ //* Destructor
+ virtual ~LDAPAsynConnection();
+ /**
+ * Initializes a connection to a server.
+ *
+ * There actually no
+ * communication to the server. Just the object is initialized
+ * (e.g. this method is called within the
+ * LDAPAsynConnection(char*,int,LDAPConstraints) constructor.)
+ * @param hostname The Name or IP-Address of the destination
+ * LDAP-Server
+ * @param port The Network Port the server is running on
+ */
+ void init(const std::string& hostname, int port);
+
+ /**
+ * Initializes a connection to a server.
+ *
+ * There actually no communication to the server. Just the
+ * object is initialized
+ * @param uri The LDAP-Uri for the destination
+ */
+ void initialize(const std::string& uri);
+
+ /**
+ * Start TLS on this connection. This isn't in the constructor,
+ * because it could fail (i.e. server doesn't have SSL cert, client
+ * api wasn't compiled against OpenSSL, etc.).
+ * @throws LDAPException if the TLS Layer could not be setup
+ * correctly
+ */
+ void start_tls();
+
+ /** Simple authentication to a LDAP-Server
+ *
+ * @throws LDAPException If the Request could not be sent to the
+ * destination server, a LDAPException-object contains the
+ * error that occurred.
+ * This method does a simple (username, password) bind to the server.
+ * Other, saver, authentcation methods are provided later
+ * @param dn the distinguished name to bind as
+ * @param passwd cleartext password to use
+ */
+ LDAPMessageQueue* bind(const std::string& dn="",
+ const std::string& passwd="",
+ const LDAPConstraints *cons=0);
+
+ LDAPMessageQueue* saslBind(const std::string& mech,
+ const std::string& cred,
+ const LDAPConstraints *cons=0);
+
+ LDAPMessageQueue* saslInteractiveBind(const std::string& mech,
+ int flags=0,
+ SaslInteractionHandler *sih=0,
+ const LDAPConstraints *cons=0);
+
+ /** Performing a search on a directory tree.
+ *
+ * Use the search method to perform a search on the LDAP-Directory
+ * @throws LDAPException If the Request could not be sent to the
+ * destination server, a LDAPException-object contains the
+ * error that occurred.
+ * @param base The distinguished name of the starting point for the
+ * search operation
+ * @param scope The scope of the search. Possible values: <BR>
+ * LDAPAsynConnection::SEARCH_BASE, <BR>
+ * LDAPAsynConnection::SEARCH_ONE, <BR>
+ * LDAPAsynConnection::SEARCH_SUB
+ * @param filter The std::string representation of a search filter to
+ * use with this operation
+ * @param attrsOnly true if only the attributes names (no values)
+ * should be returned
+ * @param cons A set of constraints that should be used with this
+ * request
+ */
+ LDAPMessageQueue* search(const std::string& base="", int scope=0,
+ const std::string& filter="objectClass=*",
+ const StringList& attrs=StringList(),
+ bool attrsOnly=false,
+ const LDAPConstraints *cons=0);
+
+ /** Delete an entry from the directory
+ *
+ * This method sends a delete request to the server
+ * @throws LDAPException If the Request could not be sent to the
+ * destination server, a LDAPException-object contains the
+ * error that occurred.
+ * @param dn Distinguished name of the entry that should be deleted
+ * @param cons A set of constraints that should be used with this
+ * request
+ */
+ LDAPMessageQueue* del(const std::string& dn, const LDAPConstraints *cons=0);
+
+ /**
+ * Perform the COMPARE-operation on an attribute
+ *
+ * @throws LDAPException If the Request could not be sent to the
+ * destination server, a LDAPException-object contains the
+ * error that occurred.
+ * @param dn Distinguished name of the entry for which the compare
+ * should be performed
+ * @param attr An Attribute (one (!) value) to use for the
+ * compare operation
+ * @param cons A set of constraints that should be used with this
+ * request
+ */
+ LDAPMessageQueue* compare(const std::string& dn,
+ const LDAPAttribute& attr,
+ const LDAPConstraints *cons=0);
+
+ /** Add an entry to the directory
+ *
+ * @throws LDAPException If the Request could not be sent to the
+ * destination server, a LDAPException-object contains the
+ * error that occurred.
+ * @param le The entry that will be added to the directory
+ */
+ LDAPMessageQueue* add( const LDAPEntry* le,
+ const LDAPConstraints *const=0);
+
+ /** Apply modifications to attributes of an entry
+ *
+ * @throws LDAPException If the Request could not be sent to the
+ * destination server, a LDAPException-object contains the
+ * error that occurred.
+ * @param dn Distinguished Name of the Entry to modify
+ * @param modlist A set of modification that should be applied
+ * to the Entry
+ * @param cons A set of constraints that should be used with this
+ * request
+ */
+ LDAPMessageQueue* modify(const std::string& dn,
+ const LDAPModList *modlist,
+ const LDAPConstraints *cons=0);
+
+ /** modify the DN of an entry
+ *
+ * @throws LDAPException If the Request could not be sent to the
+ * destination server, a LDAPException-object contains the
+ * error that occurred.
+ * @param dn DN to modify
+ * @param newRDN The new relative DN for the entry
+ * @param delOldRDN true=The old RDN will be removed from the
+ * attributes <BR>
+ * false=The old RDN will still be present in the
+ * attributes of the entry
+ * @param newParentDN The DN of the new parent entry of the entry
+ * 0 to keep the old one
+ */
+ LDAPMessageQueue* rename(const std::string& dn,
+ const std::string& newRDN,
+ bool delOldRDN=false, const std::string& newParentDN="",
+ const LDAPConstraints* cons=0);
+
+ /** Perform a LDAP extended Operation
+ *
+ * @throws LDAPException If the Request could not be sent to the
+ * destination server, a LDAPException-object contains the
+ * error that occurred.
+ * @param oid The dotted decimal representation of the extended
+ * Operation that should be performed
+ * @param value The data associated with this operation
+ * @param cons A set of constraints that should be used with this
+ * request
+ */
+ LDAPMessageQueue* extOperation(const std::string& oid,
+ const std::string& value="", const LDAPConstraints *cons=0);
+
+ /** End an outstanding request
+ *
+ * @param q All outstanding request related to this LDAPMessageQueue
+ * will be abandoned
+ */
+ void abandon(LDAPMessageQueue *q);
+
+ /**
+ * Performs the UNBIND-operation on the destination server
+ *
+ * @throws LDAPException in any case of an error
+ */
+ void unbind();
+
+ /**
+ * @returns The C-APIs LDAP-structure that is associated with the
+ * current connection
+ */
+ LDAP* getSessionHandle() const ;
+
+ /**
+ * @returns The Hostname of the destination server of the
+ * connection.
+ */
+ const std::string& getHost() const;
+
+ /**
+ * @returns The Port to which this connection is connecting to on
+ * the remote server.
+ */
+ int getPort() const;
+
+ /** Change the default constraints of the connection
+ *
+ * @parameter cons cons New LDAPConstraints to use with the connection
+ */
+ void setConstraints(LDAPConstraints *cons);
+
+ /** Get the default constraints of the connection
+ *
+ * @return Pointer to the LDAPConstraints-Object that is currently
+ * used with the Connection
+ */
+ const LDAPConstraints* getConstraints() const;
+ TlsOptions getTlsOptions() const;
+ /**
+ * This method is used internally for automatic referral chasing.
+ * It tries to bind to a destination server of the URLs of a
+ * referral.
+ *
+ * @throws LDAPException in any case of an error
+ * @param urls Contains a std::list of LDAP-Urls that indicate the
+ * destinations of a referral
+ * @param usedUrl After this method has successfully bind to one of
+ * the Destination URLs this parameter contains the URLs
+ * which was contacted.
+ * @param cons An LDAPConstraints-Object that should be used for
+ * the new connection. If this object contains a
+ * LDAPRebind-object it is used to bind to the new server
+ */
+ LDAPAsynConnection* referralConnect(const LDAPUrlList& urls,
+ LDAPUrlList::const_iterator& usedUrl,
+ const LDAPConstraints* cons) const;
+
+ private :
+ /**
+ * Private copy constructor. So nobody can call it.
+ */
+ LDAPAsynConnection(const LDAPAsynConnection& lc){};
+
+ /**
+ * A pointer to the C-API LDAP-structure that is associated with
+ * this connection
+ */
+ LDAP *cur_session;
+
+ /**
+ * A pointer to the default LDAPConstrains-object that is used when
+ * no LDAPConstraints-parameter is provided with a call for a
+ * LDAP-operation
+ */
+ LDAPConstraints *m_constr;
+
+ /**
+ * The URI of this connection
+ */
+ LDAPUrl m_uri;
+
+ protected:
+ /**
+ * Is caching enabled?
+ */
+ bool m_cacheEnabled;
+};
+#endif //LDAP_ASYN_CONNECTION_H
+
+
diff --git a/contrib/ldapc++/src/LDAPAttrType.cpp b/contrib/ldapc++/src/LDAPAttrType.cpp
new file mode 100644
index 0000000..5f5d829
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPAttrType.cpp
@@ -0,0 +1,148 @@
+// $OpenLDAP$
+/*
+ * Copyright 2003-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "debug.h"
+#include "LDAPAttrType.h"
+
+
+LDAPAttrType::LDAPAttrType(){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,
+ "LDAPAttrType::LDAPAttrType( )" << endl);
+
+ oid = string ();
+ desc = string ();
+ names = StringList ();
+ single = false;
+ usage = 0;
+}
+
+LDAPAttrType::LDAPAttrType (string at_item, int flags ) {
+
+ DEBUG(LDAP_DEBUG_CONSTRUCT,
+ "LDAPAttrType::LDAPAttrType( )" << endl);
+
+ LDAPAttributeType *a;
+ int ret;
+ const char *errp;
+ a = ldap_str2attributetype (at_item.c_str(), &ret, &errp, flags);
+
+ if (a) {
+ this->setNames( a->at_names );
+ this->setDesc( a->at_desc );
+ this->setOid( a->at_oid );
+ this->setSingle( a->at_single_value );
+ this->setUsage( a->at_usage );
+ this->setSuperiorOid( a->at_sup_oid );
+ this->setEqualityOid( a->at_equality_oid );
+ this->setOrderingOid( a->at_ordering_oid );
+ this->setSubstringOid( a->at_substr_oid );
+ this->setSyntaxOid( a->at_syntax_oid );
+ }
+ // else? -> error
+}
+
+LDAPAttrType::~LDAPAttrType() {
+ DEBUG(LDAP_DEBUG_DESTROY,"LDAPAttrType::~LDAPAttrType()" << endl);
+}
+
+void LDAPAttrType::setSingle (int at_single) {
+ single = (at_single == 1);
+}
+
+void LDAPAttrType::setNames ( char **at_names ) {
+ names = StringList(at_names);
+}
+
+void LDAPAttrType::setDesc (const char *at_desc) {
+ desc = string ();
+ if (at_desc)
+ desc = at_desc;
+}
+
+void LDAPAttrType::setOid (const char *at_oid) {
+ oid = string ();
+ if (at_oid)
+ oid = at_oid;
+}
+
+void LDAPAttrType::setUsage (int at_usage) {
+ usage = at_usage;
+}
+
+void LDAPAttrType::setSuperiorOid( const char *oid ){
+ if ( oid )
+ superiorOid = oid;
+}
+
+void LDAPAttrType::setEqualityOid( const char *oid ){
+ if ( oid )
+ equalityOid = oid;
+}
+
+void LDAPAttrType::setOrderingOid( const char *oid ){
+ if ( oid )
+ orderingOid = oid;
+}
+
+void LDAPAttrType::setSubstringOid( const char *oid ){
+ if ( oid )
+ substringOid = oid;
+}
+
+void LDAPAttrType::setSyntaxOid( const char *oid ){
+ if ( oid )
+ syntaxOid = oid;
+}
+
+bool LDAPAttrType::isSingle() const {
+ return single;
+}
+
+string LDAPAttrType::getOid() const {
+ return oid;
+}
+
+string LDAPAttrType::getDesc() const {
+ return desc;
+}
+
+StringList LDAPAttrType::getNames() const {
+ return names;
+}
+
+string LDAPAttrType::getName() const {
+
+ if (names.empty())
+ return "";
+ else
+ return *(names.begin());
+}
+
+int LDAPAttrType::getUsage() const {
+ return usage;
+}
+
+std::string LDAPAttrType::getSuperiorOid() const {
+ return superiorOid;
+}
+
+std::string LDAPAttrType::getEqualityOid() const {
+ return equalityOid;
+}
+
+std::string LDAPAttrType::getOrderingOid() const {
+ return orderingOid;
+}
+
+std::string LDAPAttrType::getSubstringOid() const {
+ return substringOid;
+}
+
+std::string LDAPAttrType::getSyntaxOid() const {
+ return syntaxOid;
+}
+
+
diff --git a/contrib/ldapc++/src/LDAPAttrType.h b/contrib/ldapc++/src/LDAPAttrType.h
new file mode 100644
index 0000000..34948cc
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPAttrType.h
@@ -0,0 +1,101 @@
+// $OpenLDAP$
+/*
+ * Copyright 2003-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDAP_ATTRTYPE_H
+#define LDAP_ATTRTYPE_H
+
+#include <ldap_schema.h>
+#include <string>
+
+#include "StringList.h"
+
+using namespace std;
+
+/**
+ * Represents the Attribute Type (from LDAP schema)
+ */
+class LDAPAttrType{
+ private :
+ StringList names;
+ std::string desc, oid, superiorOid, equalityOid;
+ std::string orderingOid, substringOid, syntaxOid;
+ bool single;
+ int usage;
+
+ public :
+
+ /**
+ * Constructor
+ */
+ LDAPAttrType();
+
+ /**
+ * Constructs new object and fills the data structure by parsing the
+ * argument.
+ * @param at_item description of attribute type is string returned
+ * by the search command. It is in the form:
+ * "( SuSE.YaST.Attr:19 NAME ( 'skelDir' ) DESC ''
+ * EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )"
+ */
+ LDAPAttrType (string at_item, int flags = LDAP_SCHEMA_ALLOW_NO_OID |
+ LDAP_SCHEMA_ALLOW_QUOTED );
+
+ /**
+ * Destructor
+ */
+ virtual ~LDAPAttrType();
+
+
+ /**
+ * Returns attribute description
+ */
+ string getDesc() const;
+
+ /**
+ * Returns attribute oid
+ */
+ string getOid() const;
+
+ /**
+ * Returns attribute name (first one if there are more of them)
+ */
+ string getName() const;
+
+ /**
+ * Returns all attribute names
+ */
+ StringList getNames() const;
+
+ /**
+ * Returns true if attribute type allows only single value
+ */
+ bool isSingle() const;
+
+ /**
+ * Return the 'usage' value:
+ * (0=userApplications, 1=directoryOperation, 2=distributedOperation,
+ * 3=dSAOperation)
+ */
+ int getUsage () const;
+ std::string getSuperiorOid() const;
+ std::string getEqualityOid() const;
+ std::string getOrderingOid() const;
+ std::string getSubstringOid() const;
+ std::string getSyntaxOid() const;
+
+ void setNames( char **at_names);
+ void setDesc(const char *at_desc);
+ void setOid(const char *at_oid);
+ void setSingle(int at_single_value);
+ void setUsage(int at_usage );
+ void setSuperiorOid( const char *oid );
+ void setEqualityOid( const char *oid );
+ void setOrderingOid( const char *oid );
+ void setSubstringOid( const char *oid );
+ void setSyntaxOid( const char *oid );
+};
+
+#endif // LDAP_ATTRTYPE_H
diff --git a/contrib/ldapc++/src/LDAPAttribute.cpp b/contrib/ldapc++/src/LDAPAttribute.cpp
new file mode 100644
index 0000000..d6689ff
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPAttribute.cpp
@@ -0,0 +1,199 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+//TODO!!!
+// * some kind of iterator to step through the attribute values
+// * remove values from Attribute
+// * handling of subtypes (;de; and so on)
+// * some documentation
+
+
+#include <ldap.h>
+#include <cstdlib>
+
+#include "debug.h"
+#include "StringList.h"
+
+#include "LDAPAttribute.h"
+
+using namespace std;
+
+LDAPAttribute::LDAPAttribute(){
+ DEBUG(LDAP_DEBUG_CONSTRUCT, "LDAPAttribute::LDAPAttribute( )" << endl);
+ m_name=string();
+}
+
+LDAPAttribute::LDAPAttribute(const LDAPAttribute& attr){
+ DEBUG(LDAP_DEBUG_CONSTRUCT, "LDAPAttribute::LDAPAttribute(&)" << endl);
+ DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER,
+ " attr:" << attr << endl);
+ m_name=attr.m_name;
+ m_values=StringList(attr.m_values);
+}
+
+LDAPAttribute::LDAPAttribute(const string& name, const string& value){
+ DEBUG(LDAP_DEBUG_CONSTRUCT, "LDAPAttribute::LDAPAttribute()" << endl);
+ DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER,
+ " name:" << name << endl << " value:" << value << endl);
+ this->setName(name);
+ if(value != ""){
+ this->addValue(value);
+ }
+}
+
+
+LDAPAttribute::LDAPAttribute(const string& name, const StringList& values){
+ DEBUG(LDAP_DEBUG_CONSTRUCT, "LDAPAttribute::LDAPAttribute()" << endl);
+ DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER,
+ " name:" << name << endl);
+ m_name=name;
+ m_values=values;
+}
+
+LDAPAttribute::LDAPAttribute(const char *name, char **values){
+ DEBUG(LDAP_DEBUG_CONSTRUCT, "LDAPAttribute::LDAPAttribute()" << endl);
+ DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER,
+ " name:" << name << endl);
+ this->setName(name);
+ this->setValues(values);
+}
+
+LDAPAttribute::LDAPAttribute(const char *name, BerValue **values){
+ DEBUG(LDAP_DEBUG_CONSTRUCT, "LDAPAttribute::LDAPAttribute()" << endl);
+ DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER,
+ " name:" << name << endl);
+ this->setName(name);
+ this->setValues(values);
+}
+
+LDAPAttribute::~LDAPAttribute(){
+ DEBUG(LDAP_DEBUG_DESTROY,"LDAPAttribute::~LDAPAttribute()" << endl);
+}
+
+void LDAPAttribute::addValue(const string& value){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAttribute::addValue()" << endl);
+ m_values.add(value);
+}
+
+int LDAPAttribute::addValue(const BerValue *value){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAttribute::addValue()" << endl);
+ if(value!=0){
+ this->addValue(string(value->bv_val, value->bv_len));
+ return 0;
+ }
+ return -1;
+}
+
+int LDAPAttribute::setValues(char **values){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAttribute::setValues()" << endl);
+ if(values){
+ m_values.clear();
+ for( char **i=values; *i!=0; i++){
+ this->addValue(*i);
+ }
+ }
+ return 0;
+}
+
+int LDAPAttribute::setValues(BerValue **values){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAttribute::setValues()" << endl);
+ if(values){
+ m_values.clear();
+ for( BerValue **i=values; *i!=0; i++){
+ if( this->addValue(*i) ){
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
+void LDAPAttribute::setValues(const StringList& values){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAttribute::setValues()" << endl);
+ m_values=values;
+}
+
+const StringList& LDAPAttribute::getValues() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAttribute::getValues()" << endl);
+ return m_values;
+}
+
+BerValue** LDAPAttribute::getBerValues() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAttribute::getBerValues()" << endl);
+ size_t size=m_values.size();
+ if (size == 0){
+ return 0;
+ }else{
+ BerValue **temp = (BerValue**) malloc(sizeof(BerValue*) * (size+1));
+ StringList::const_iterator i;
+ int p=0;
+
+ for(i=m_values.begin(), p=0; i!=m_values.end(); i++,p++){
+ temp[p]=(BerValue*) malloc(sizeof(BerValue));
+ temp[p]->bv_len= i->size();
+ temp[p]->bv_val= (char*) malloc(sizeof(char) * (i->size()+1));
+ i->copy(temp[p]->bv_val,string::npos);
+ }
+ temp[size]=0;
+ return temp;
+ }
+}
+
+int LDAPAttribute::getNumValues() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAttribute::getNumValues()" << endl);
+ return m_values.size();
+}
+
+const string& LDAPAttribute::getName() const {
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPAttribute::getName()" << endl);
+ return m_name;
+}
+
+void LDAPAttribute::setName(const string& name){
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPAttribute::setName()" << endl);
+ DEBUG(LDAP_DEBUG_TRACE | LDAP_DEBUG_PARAMETER," name:" << name << endl);
+ m_name.erase();
+ m_name=name;
+}
+
+// The bin-FLAG of the mod_op is always set to LDAP_MOD_BVALUES (0x80)
+LDAPMod* LDAPAttribute::toLDAPMod() const {
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPAttribute::toLDAPMod()" << endl);
+ LDAPMod* ret= (LDAPMod*) malloc(sizeof(LDAPMod));
+ ret->mod_op=LDAP_MOD_BVALUES; //always assume binary-Values
+ ret->mod_type= (char*) malloc(sizeof(char) * (m_name.size()+1));
+ m_name.copy(ret->mod_type,string::npos);
+ ret->mod_type[m_name.size()]=0;
+ ret->mod_bvalues=this->getBerValues();
+ return ret;
+}
+
+bool LDAPAttribute::isNotPrintable() const {
+ StringList::const_iterator i;
+ for(i=m_values.begin(); i!=m_values.end(); i++){
+ size_t len = i->size();
+ for(size_t j=0; j<len; j++){
+ if (! isprint( (i->data())[j] ) ){
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+ostream& operator << (ostream& s, const LDAPAttribute& attr){
+ s << attr.m_name << "=";
+ StringList::const_iterator i;
+ if (attr.isNotPrintable()){
+ s << "NOT_PRINTABLE" ;
+ }else{
+ for(i=attr.m_values.begin(); i!=attr.m_values.end(); i++){
+ s << *i << " ";
+ }
+ }
+ return s;
+}
diff --git a/contrib/ldapc++/src/LDAPAttribute.h b/contrib/ldapc++/src/LDAPAttribute.h
new file mode 100644
index 0000000..e978137
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPAttribute.h
@@ -0,0 +1,181 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#ifndef LDAP_ATTRIBUTE_H
+#define LDAP_ATTRIBUTE_H
+
+#include<iostream>
+#include<string>
+#include<ldap.h>
+#include<lber.h>
+
+#include <StringList.h>
+
+/**
+ * Represents the name an value(s) of an Attribute
+ */
+class LDAPAttribute{
+ public :
+ /**
+ * Default constructor.
+ * initializes an empty object.
+ */
+ LDAPAttribute();
+
+ /**
+ * Copy constructor.
+ * Copies all values of an Attribute to a new one
+ * @param attr The Attribute that should be copied
+ */
+ LDAPAttribute(const LDAPAttribute& attr);
+
+ /**
+ * Construct an Attribute with a single string value
+ * @param name The attribute's name (type)
+ * @param value The string value of the attribute, if "" the
+ * attribute will have no values, for LDAPv3
+ * this values must be UTF-8 encoded
+ */
+ LDAPAttribute(const std::string& name, const std::string& value="");
+
+ /**
+ * Construct an attribute with multiple string values
+ * @param name The attribute's name (type)
+ * @param values A 0-terminated array of char*. Each char* specifies
+ * one value of the attribute (UTF-8 encoded)
+ */
+ LDAPAttribute(const char* name, char **values);
+
+ /**
+ * Construct an attribute with multiple string values
+ * @param name The attribute's name (type)
+ * @param values A list of strings. Each element specifies
+ * one value of the attribute (UTF-8 or binary
+ * encoded)
+ */
+ LDAPAttribute(const std::string& name, const StringList& values);
+
+ /**
+ * Construct an attribute with multiple binary coded values
+ * @param name The attribute's name (type)
+ * @param values 0-terminated array of binary attribute values
+ * The BerValue struct is declared as:<BR>
+ * struct berval{
+ * unsigned long bv_len;
+ * char *bv_val;
+ * } BerValue;
+ */
+ LDAPAttribute(const char* name, BerValue **values);
+
+ /**
+ * Destructor
+ */
+ ~LDAPAttribute();
+
+ /**
+ * Add a single string value(bin/char) to the Attribute
+ * @param value Value that should be added, it is copied inside the
+ * object
+ */
+ void addValue(const std::string& value);
+
+ /**
+ * Add a single binary value to the Attribute
+ * @param value The binary coded value that should be added to the
+ * Attribute.
+ * @return 0 no problem <BR>
+ * -1 failure (mem. allocation problem)
+ */
+ int addValue(const BerValue *value);
+
+ /**
+ * Set the values of the attribute. If the object contains some values
+ * already, they are deleted
+ * @param values 0-terminated array of char*, each char*
+ * representing a string value to add to the entry
+ *
+ * @return 0 no problem <BR>
+ * -1 failure (mem. allocation problem)
+ */
+ int setValues(char** values);
+
+ /**
+ * Set the values of the attribute. If the object does already contain
+ * some values, they will be deleted
+ * @param values 0-terminated array of BerValue*, each BerValue
+ * representing a binary value to add to the entry
+ *
+ * @return 0 no problem <BR>
+ * -1 failure (mem. allocation problem)
+ */
+ int setValues(BerValue** values);
+
+ /**
+ * Set the values of the attribute. If the object does already contain
+ * some values, they will be deleted
+ * @param values A list of string-Objects. Each string is
+ * representing a string or binary value to add to
+ * the entry
+ */
+ void setValues(const StringList& values);
+
+ /**
+ * For internal use only.
+ * This method is used to translate the values of the Attribute to
+ * 0-terminated Array of BerValue-structs as used by the C-API
+ * @return The Values of the Attribute as an 0-terminated Array of
+ * BerValue* (is dynamically allocated, delete it after usage)
+ * <BR>
+ * 0-pointer in case of error
+ */
+ BerValue** getBerValues() const;
+
+ /**
+ * @return The values of the array as a list of strings
+ */
+ const StringList& getValues() const;
+
+ /**
+ * @return The number of values of the attribute
+ */
+ int getNumValues() const;
+
+ /**
+ * @return The name(type) of the attribute
+ */
+ const std::string& getName() const ;
+
+ /**
+ * Sets the Attribute's name (type)
+ * @param the new name of the object
+ */
+ void setName(const std::string& name);
+
+ /**
+ * For internal use only.
+ *
+ * This method translate the attribute of the object into a
+ * LDAPMod-Structure as used by the C-API
+ */
+ LDAPMod* toLDAPMod() const ;
+
+ /**
+ * @return true If the attribute contains non-printable attributes
+ */
+ bool isNotPrintable() const ;
+
+ private :
+ std::string m_name;
+ StringList m_values;
+
+ /**
+ * This method can be used to dump the data of a LDAPResult-Object.
+ * It is only useful for debugging purposes at the moment
+ */
+ friend std::ostream& operator << (std::ostream& s, const LDAPAttribute& attr);
+};
+#endif //#ifndef LDAP_ATTRIBUTE_H
diff --git a/contrib/ldapc++/src/LDAPAttributeList.cpp b/contrib/ldapc++/src/LDAPAttributeList.cpp
new file mode 100644
index 0000000..9752191
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPAttributeList.cpp
@@ -0,0 +1,193 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#include "debug.h"
+
+#include "LDAPAttributeList.h"
+
+#include "LDAPException.h"
+#include "LDAPAttribute.h"
+#include "LDAPAsynConnection.h"
+#include "LDAPMessage.h"
+
+#include <cstdlib>
+
+using namespace std;
+
+// little helper function for doing case insensitive string comparison
+bool nocase_compare(char c1, char c2);
+
+LDAPAttributeList::LDAPAttributeList(){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,
+ "LDAPAttributeList::LDAPAttributeList( )" << endl);
+}
+
+LDAPAttributeList::LDAPAttributeList(const LDAPAttributeList& al){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,
+ "LDAPAttributeList::LDAPAttributeList(&)" << endl);
+ m_attrs=al.m_attrs;
+}
+
+LDAPAttributeList::LDAPAttributeList(const LDAPAsynConnection *ld,
+ LDAPMessage *msg){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,
+ "LDAPAttributeList::LDAPAttributeList()" << endl);
+ BerElement *ptr=0;
+ char *name=ldap_first_attribute(ld->getSessionHandle(), msg, &ptr);
+/*
+ This code was making problems if no attribute were returned
+ How am I supposed to find decoding errors? ldap_first/next_attribute
+ return 0 in case of error or if there are no more attributes. In either
+ case they set the LDAP* error code to 0x54 (Decoding error) ??? Strange..
+
+ There will be some changes in the new version of the C-API so that this
+ code should work in the future.
+ if(name == 0){
+ ber_free(ptr,0);
+ ldap_memfree(name);
+ throw LDAPException(ld);
+ }else{
+*/ BerValue **values;
+ for (;name !=0;
+ name=ldap_next_attribute(ld->getSessionHandle(),msg,ptr) ){
+ values=ldap_get_values_len(ld->getSessionHandle(),
+ msg, name);
+ this->addAttribute(LDAPAttribute(name, values));
+ ldap_memfree(name);
+ ldap_value_free_len(values);
+ }
+ ber_free(ptr,0);
+// }
+}
+
+LDAPAttributeList::~LDAPAttributeList(){
+ DEBUG(LDAP_DEBUG_DESTROY,"LDAPAttributeList::~LDAPAttributeList()" << endl);
+}
+
+size_t LDAPAttributeList::size() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAttribute::size()" << endl);
+ return m_attrs.size();
+}
+
+bool LDAPAttributeList::empty() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAttribute::empty()" << endl);
+ return m_attrs.empty();
+}
+
+LDAPAttributeList::const_iterator LDAPAttributeList::begin() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAttribute::begin()" << endl);
+ return m_attrs.begin();
+}
+
+LDAPAttributeList::const_iterator LDAPAttributeList::end() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAttribute::end()" << endl);
+ return m_attrs.end();
+}
+
+const LDAPAttribute* LDAPAttributeList::getAttributeByName(
+ const string& name) const {
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAttribute::getAttributeByName()" << endl);
+ DEBUG(LDAP_DEBUG_TRACE | LDAP_DEBUG_PARAMETER,
+ " name:" << name << endl);
+ LDAPAttributeList::const_iterator i;
+ for( i = m_attrs.begin(); i != m_attrs.end(); i++){
+ const std::string& tmpType = i->getName();
+ if(name.size() == tmpType.size()){
+ if(equal(name.begin(), name.end(), tmpType.begin(),
+ nocase_compare)){
+ return &(*i);
+ DEBUG(LDAP_DEBUG_TRACE," found:" << name << endl);
+ }
+ }
+ }
+ return 0;
+}
+
+void LDAPAttributeList::addAttribute(const LDAPAttribute& attr){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAttribute::addAttribute()" << endl);
+ DEBUG(LDAP_DEBUG_TRACE | LDAP_DEBUG_PARAMETER,
+ " attr:" << attr << endl);
+ const std::string attrType = attr.getName();
+ const std::string::size_type attrLen = attrType.size();
+ std::string::size_type tmpAttrLen = 0;
+ bool done=false;
+ LDAPAttributeList::iterator i;
+ for( i=m_attrs.begin(); i != m_attrs.end(); i++ ){
+ const std::string tmpAttrType = i->getName();
+ tmpAttrLen = tmpAttrType.size();
+ if(tmpAttrLen == attrLen){
+ if(equal(tmpAttrType.begin(), tmpAttrType.end(), attrType.begin(),
+ nocase_compare)){
+ const StringList& values = attr.getValues();
+ StringList::const_iterator j;
+ for(j = values.begin(); j != values.end(); j++){
+ i->addValue(*j);
+ }
+ DEBUG(LDAP_DEBUG_TRACE,"Attribute" << i->getName()
+ << "already present" << endl);
+ done=true;
+ break; // The AttributeType was already present,
+ // we are done here
+ }
+ }
+ }
+ if(! done){
+ m_attrs.push_back(attr);
+ }
+}
+
+void LDAPAttributeList::delAttribute(const std::string& type)
+{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAttribute::replaceAttribute()" << endl);
+ DEBUG(LDAP_DEBUG_TRACE | LDAP_DEBUG_PARAMETER, " type: " << type << endl);
+ LDAPAttributeList::iterator i;
+ for( i = m_attrs.begin(); i != m_attrs.end(); i++){
+ if(type.size() == i->getName().size()){
+ if(equal(type.begin(), type.end(), i->getName().begin(),
+ nocase_compare)){
+ m_attrs.erase(i);
+ break;
+ }
+ }
+ }
+}
+
+void LDAPAttributeList::replaceAttribute(const LDAPAttribute& attr)
+{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAttribute::replaceAttribute()" << endl);
+ DEBUG(LDAP_DEBUG_TRACE | LDAP_DEBUG_PARAMETER,
+ " attr:" << attr << endl);
+
+ LDAPAttributeList::iterator i;
+ this->delAttribute( attr.getName() );
+ m_attrs.push_back(attr);
+}
+
+LDAPMod** LDAPAttributeList::toLDAPModArray() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPAttribute::toLDAPModArray()" << endl);
+ LDAPMod **ret = (LDAPMod**) malloc((m_attrs.size()+1) * sizeof(LDAPMod*));
+ LDAPAttributeList::const_iterator i;
+ int j=0;
+ for (i=m_attrs.begin(); i!= m_attrs.end(); i++, j++){
+ ret[j]=i->toLDAPMod();
+ }
+ ret[m_attrs.size()]=0;
+ return ret;
+}
+
+ostream& operator << (ostream& s, const LDAPAttributeList& al){
+ LDAPAttributeList::const_iterator i;
+ for(i=al.m_attrs.begin(); i!=al.m_attrs.end(); i++){
+ s << *i << "; ";
+ }
+ return s;
+}
+
+bool nocase_compare( char c1, char c2){
+ return toupper(c1) == toupper(c2);
+}
+
diff --git a/contrib/ldapc++/src/LDAPAttributeList.h b/contrib/ldapc++/src/LDAPAttributeList.h
new file mode 100644
index 0000000..f03275e
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPAttributeList.h
@@ -0,0 +1,121 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#ifndef LDAP_ATTRIBUTE_LIST_H
+#define LDAP_ATTRIBUTE_LIST_H
+
+#include <ldap.h>
+#include <list>
+#include <string>
+
+class LDAPAttribute;
+class LDAPAsynConnection;
+class LDAPMsg;
+
+/**
+ * This container class is used to store multiple LDAPAttribute-objects.
+ */
+class LDAPAttributeList{
+ typedef std::list<LDAPAttribute> ListType;
+
+ private :
+ ListType m_attrs;
+
+ public :
+ typedef ListType::const_iterator const_iterator;
+ typedef ListType::iterator iterator;
+
+
+ /**
+ * Copy-constructor
+ */
+ LDAPAttributeList(const LDAPAttributeList& al);
+
+ /**
+ * For internal use only
+ *
+ * This constructor is used by the library internally to create a
+ * list of attributes from a LDAPMessage-struct that was return by
+ * the C-API
+ */
+ LDAPAttributeList(const LDAPAsynConnection *ld, LDAPMessage *msg);
+
+ /**
+ * Constructs an empty list.
+ */
+ LDAPAttributeList();
+
+ /**
+ * Destructor
+ */
+ virtual ~LDAPAttributeList();
+
+ /**
+ * @return The number of LDAPAttribute-objects that are currently
+ * stored in this list.
+ */
+ size_t size() const;
+
+ /**
+ * @return true if there are zero LDAPAttribute-objects currently
+ * stored in this list.
+ */
+ bool empty() const;
+
+ /**
+ * @return A iterator that points to the first element of the list.
+ */
+ const_iterator begin() const;
+
+ /**
+ * @return A iterator that points to the element after the last
+ * element of the list.
+ */
+ const_iterator end() const;
+
+ /**
+ * Get an Attribute by its AttributeType
+ * @param name The name of the Attribute to look for
+ * @return a pointer to the LDAPAttribute with the AttributeType
+ * "name" or 0, if there is no Attribute of that Type
+ */
+ const LDAPAttribute* getAttributeByName(const std::string& name) const;
+
+ /**
+ * Adds one element to the end of the list.
+ * @param attr The attribute to add to the list.
+ */
+ void addAttribute(const LDAPAttribute& attr);
+
+ /**
+ * Deletes all values of an Attribute for the list
+ * @param type The attribute type to be deleted.
+ */
+ void delAttribute(const std::string& type);
+
+ /**
+ * Replace an Attribute in the List
+ * @param attr The attribute to add to the list.
+ */
+ void replaceAttribute(const LDAPAttribute& attr);
+
+ /**
+ * Translates the list of Attributes to a 0-terminated array of
+ * LDAPMod-structures as needed by the C-API
+ */
+ LDAPMod** toLDAPModArray() const;
+
+ /**
+ * This method can be used to dump the data of a LDAPResult-Object.
+ * It is only useful for debugging purposes at the moment
+ */
+ friend std::ostream& operator << (std::ostream& s,
+ const LDAPAttributeList& al);
+};
+
+#endif // LDAP_ATTRIBUTE_LIST_H
+
diff --git a/contrib/ldapc++/src/LDAPBindRequest.cpp b/contrib/ldapc++/src/LDAPBindRequest.cpp
new file mode 100644
index 0000000..a67a0e1
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPBindRequest.cpp
@@ -0,0 +1,173 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include <ldap.h>
+
+#include "debug.h"
+
+#include "LDAPBindRequest.h"
+#include "LDAPException.h"
+#include "SaslInteractionHandler.h"
+#include "SaslInteraction.h"
+
+#include <cstdlib>
+#include <sasl/sasl.h>
+
+using namespace std;
+
+LDAPBindRequest::LDAPBindRequest(const LDAPBindRequest& req) :
+ LDAPRequest(req){
+ DEBUG(LDAP_DEBUG_CONSTRUCT, "LDAPBindRequest::LDAPBindRequest(&)" << endl);
+ m_dn=req.m_dn;
+ m_cred=req.m_cred;
+ m_mech=req.m_mech;
+}
+
+LDAPBindRequest::LDAPBindRequest(const string& dn,const string& passwd,
+ LDAPAsynConnection *connect, const LDAPConstraints *cons,
+ bool isReferral) : LDAPRequest(connect, cons, isReferral){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPBindRequest::LDAPBindRequest()" << endl);
+ DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER, " dn:" << dn << endl
+ << " passwd:" << passwd << endl);
+ m_dn = dn;
+ m_cred = passwd;
+ m_mech = "";
+}
+
+LDAPBindRequest::~LDAPBindRequest(){
+ DEBUG(LDAP_DEBUG_DESTROY,"LDAPBindRequest::~LDAPBindRequest()" << endl);
+}
+
+LDAPMessageQueue* LDAPBindRequest::sendRequest(){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPBindRequest::sendRequest()" << endl);
+ int msgID=0;
+
+ const char* mech = (m_mech == "" ? 0 : m_mech.c_str());
+ BerValue* tmpcred=0;
+ if(m_cred != ""){
+ char* tmppwd = (char*) malloc( (m_cred.size()+1) * sizeof(char));
+ m_cred.copy(tmppwd,string::npos);
+ tmppwd[m_cred.size()]=0;
+ tmpcred=ber_bvstr(tmppwd);
+ }else{
+ tmpcred=(BerValue*) malloc(sizeof(BerValue));
+ tmpcred->bv_len=0;
+ tmpcred->bv_val=0;
+ }
+ const char* dn = 0;
+ if(m_dn != ""){
+ dn = m_dn.c_str();
+ }
+ LDAPControl** tmpSrvCtrls=m_cons->getSrvCtrlsArray();
+ LDAPControl** tmpClCtrls=m_cons->getClCtrlsArray();
+ int err=ldap_sasl_bind(m_connection->getSessionHandle(),dn,
+ mech, tmpcred, tmpSrvCtrls, tmpClCtrls, &msgID);
+ LDAPControlSet::freeLDAPControlArray(tmpSrvCtrls);
+ LDAPControlSet::freeLDAPControlArray(tmpClCtrls);
+ ber_bvfree(tmpcred);
+
+ if(err != LDAP_SUCCESS){
+ throw LDAPException(err);
+ }else{
+ m_msgID=msgID;
+ return new LDAPMessageQueue(this);
+ }
+}
+
+LDAPSaslBindRequest::LDAPSaslBindRequest(const std::string& mech,
+ const std::string& cred,
+ LDAPAsynConnection *connect,
+ const LDAPConstraints *cons,
+ bool isReferral) : LDAPRequest(connect, cons, isReferral),m_mech(mech), m_cred(cred) {}
+
+LDAPMessageQueue* LDAPSaslBindRequest::sendRequest()
+{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPSaslBindRequest::sendRequest()" << endl);
+ int msgID=0;
+
+ BerValue tmpcred;
+ tmpcred.bv_val = (char*) malloc( m_cred.size() * sizeof(char));
+ m_cred.copy(tmpcred.bv_val,string::npos);
+ tmpcred.bv_len = m_cred.size();
+
+ LDAPControl** tmpSrvCtrls=m_cons->getSrvCtrlsArray();
+ LDAPControl** tmpClCtrls=m_cons->getClCtrlsArray();
+ int err=ldap_sasl_bind(m_connection->getSessionHandle(), "", m_mech.c_str(),
+ &tmpcred, tmpSrvCtrls, tmpClCtrls, &msgID);
+ LDAPControlSet::freeLDAPControlArray(tmpSrvCtrls);
+ LDAPControlSet::freeLDAPControlArray(tmpClCtrls);
+ free(tmpcred.bv_val);
+
+ if(err != LDAP_SUCCESS){
+ throw LDAPException(err);
+ }else{
+ m_msgID=msgID;
+ return new LDAPMessageQueue(this);
+ }
+}
+
+LDAPSaslBindRequest::~LDAPSaslBindRequest()
+{
+ DEBUG(LDAP_DEBUG_DESTROY,"LDAPSaslBindRequest::~LDAPSaslBindRequest()" << endl);
+}
+
+LDAPSaslInteractiveBind::LDAPSaslInteractiveBind( const std::string& mech,
+ int flags, SaslInteractionHandler *sih, LDAPAsynConnection *connect,
+ const LDAPConstraints *cons, bool isReferral) :
+ LDAPRequest(connect, cons, isReferral),
+ m_mech(mech), m_flags(flags), m_sih(sih), m_res(0)
+{
+}
+
+static int my_sasl_interact(LDAP *l, unsigned flags, void *cbh, void *interact)
+{
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPSaslInteractiveBind::my_sasl_interact()"
+ << std::endl );
+ std::list<SaslInteraction*> interactions;
+
+ sasl_interact_t *iter = (sasl_interact_t*) interact;
+ while ( iter->id != SASL_CB_LIST_END ) {
+ SaslInteraction *si = new SaslInteraction(iter);
+ interactions.push_back( si );
+ iter++;
+ }
+ ((SaslInteractionHandler*)cbh)->handleInteractions(interactions);
+ return LDAP_SUCCESS;
+}
+
+/* This kind of fakes an asynchronous operation, ldap_sasl_interactive_bind_s
+ * is synchronous */
+LDAPMessageQueue *LDAPSaslInteractiveBind::sendRequest()
+{
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPSaslInteractiveBind::sendRequest()" <<
+ m_mech << std::endl);
+
+ LDAPControl** tmpSrvCtrls=m_cons->getSrvCtrlsArray();
+ LDAPControl** tmpClCtrls=m_cons->getClCtrlsArray();
+ int res = ldap_sasl_interactive_bind_s( m_connection->getSessionHandle(),
+ "", m_mech.c_str(), tmpSrvCtrls, tmpClCtrls, m_flags,
+ my_sasl_interact, m_sih );
+
+ DEBUG(LDAP_DEBUG_TRACE, "ldap_sasl_interactive_bind_s returned: "
+ << res << std::endl);
+ if(res != LDAP_SUCCESS){
+ throw LDAPException(res);
+ } else {
+ m_res = new LDAPResult(LDAPMsg::BIND_RESPONSE, res, "");
+ }
+ return new LDAPMessageQueue(this);
+}
+
+LDAPMsg* LDAPSaslInteractiveBind::getNextMessage() const
+{
+ return m_res;
+}
+
+LDAPSaslInteractiveBind::~LDAPSaslInteractiveBind()
+{
+ DEBUG(LDAP_DEBUG_DESTROY,"LDAPSaslInteractiveBind::~LDAPSaslInteractiveBind()" << endl);
+}
+
diff --git a/contrib/ldapc++/src/LDAPBindRequest.h b/contrib/ldapc++/src/LDAPBindRequest.h
new file mode 100644
index 0000000..efadffb
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPBindRequest.h
@@ -0,0 +1,61 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDAP_BIND_REQUEST_H
+#define LDAP_BIND_REQUEST_H
+
+#include <LDAPRequest.h>
+#include <LDAPResult.h>
+#include <SaslInteractionHandler.h>
+
+class LDAPBindRequest : LDAPRequest {
+ private:
+ std::string m_dn;
+ std::string m_cred;
+ std::string m_mech;
+
+ public:
+ LDAPBindRequest( const LDAPBindRequest& req);
+ //just for simple authentication
+ LDAPBindRequest(const std::string&, const std::string& passwd,
+ LDAPAsynConnection *connect, const LDAPConstraints *cons,
+ bool isReferral=false);
+ virtual ~LDAPBindRequest();
+ virtual LDAPMessageQueue *sendRequest();
+};
+
+class LDAPSaslBindRequest : LDAPRequest
+{
+ public:
+ LDAPSaslBindRequest( const std::string& mech, const std::string& cred,
+ LDAPAsynConnection *connect, const LDAPConstraints *cons,
+ bool isReferral=false);
+ virtual LDAPMessageQueue *sendRequest();
+ virtual ~LDAPSaslBindRequest();
+
+ private:
+ std::string m_mech;
+ std::string m_cred;
+};
+
+class LDAPSaslInteractiveBind : LDAPRequest
+{
+ public:
+ LDAPSaslInteractiveBind( const std::string& mech, int flags,
+ SaslInteractionHandler *sih, LDAPAsynConnection *connect,
+ const LDAPConstraints *cons, bool isReferral=false);
+ virtual LDAPMessageQueue *sendRequest();
+ virtual LDAPMsg* getNextMessage() const;
+ virtual ~LDAPSaslInteractiveBind();
+
+ private:
+ std::string m_mech;
+ int m_flags;
+ SaslInteractionHandler *m_sih;
+ LDAPResult *m_res;
+};
+#endif //LDAP_BIND_REQUEST_H
+
diff --git a/contrib/ldapc++/src/LDAPCompareRequest.cpp b/contrib/ldapc++/src/LDAPCompareRequest.cpp
new file mode 100644
index 0000000..4edc646
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPCompareRequest.cpp
@@ -0,0 +1,79 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include <ldap.h>
+
+#include "debug.h"
+
+#include "LDAPCompareRequest.h"
+#include "LDAPException.h"
+#include "LDAPMessageQueue.h"
+#include "LDAPResult.h"
+
+using namespace std;
+
+LDAPCompareRequest::LDAPCompareRequest(const LDAPCompareRequest& req){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,
+ "LDAPCompareRequest::LDAPCompareRequest(&)" << endl);
+ m_dn=req.m_dn;
+ m_attr=req.m_attr;
+}
+
+LDAPCompareRequest::LDAPCompareRequest(const string& dn,
+ const LDAPAttribute& attr, LDAPAsynConnection *connect,
+ const LDAPConstraints *cons, bool isReferral,
+ const LDAPRequest* parent) :
+ LDAPRequest(connect, cons, isReferral,parent){
+ DEBUG(LDAP_DEBUG_CONSTRUCT, "LDAPCompareRequest::LDAPCompareRequest()"
+ << endl);
+ DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER, " dn:" << dn << endl
+ << " attr:" << attr << endl);
+ m_requestType=LDAPRequest::COMPARE;
+ m_dn=dn;
+ m_attr=attr;
+}
+
+LDAPCompareRequest::~LDAPCompareRequest(){
+ DEBUG(LDAP_DEBUG_DESTROY, "LDAPCompareRequest::~LDAPCompareRequest()"
+ << endl);
+}
+
+LDAPMessageQueue* LDAPCompareRequest::sendRequest(){
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPCompareRequest::sendRequest()" << endl);
+ int msgID=0;
+ BerValue **val=m_attr.getBerValues();
+ LDAPControl** tmpSrvCtrls=m_cons->getSrvCtrlsArray();
+ LDAPControl** tmpClCtrls=m_cons->getClCtrlsArray();
+ int err=ldap_compare_ext(m_connection->getSessionHandle(),m_dn.c_str(),
+ m_attr.getName().c_str(), val[0], tmpSrvCtrls,
+ tmpClCtrls, &msgID);
+ ber_bvecfree(val);
+ LDAPControlSet::freeLDAPControlArray(tmpSrvCtrls);
+ LDAPControlSet::freeLDAPControlArray(tmpClCtrls);
+ if(err != LDAP_SUCCESS){
+ throw LDAPException(err);
+ }else{
+ m_msgID=msgID;
+ return new LDAPMessageQueue(this);
+ }
+}
+
+LDAPRequest* LDAPCompareRequest::followReferral(LDAPMsg* ref){
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPCompareRequest::followReferral()" << endl);
+ LDAPUrlList::const_iterator usedUrl;
+ LDAPUrlList urls = ((LDAPResult*)ref)->getReferralUrls();
+ LDAPAsynConnection* con = 0;
+ try{
+ con=getConnection()->referralConnect(urls,usedUrl,m_cons);
+ }catch(LDAPException e){
+ return 0;
+ }
+ if(con != 0){
+ return new LDAPCompareRequest(m_dn, m_attr, con, m_cons, true, this);
+ }
+ return 0;
+}
+
diff --git a/contrib/ldapc++/src/LDAPCompareRequest.h b/contrib/ldapc++/src/LDAPCompareRequest.h
new file mode 100644
index 0000000..3202e04
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPCompareRequest.h
@@ -0,0 +1,31 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDAP_COMPARE_REQUEST_H
+#define LDAP_COMPARE_REQUEST_H
+
+#include <LDAPRequest.h>
+
+class LDAPMessageQueue;
+
+class LDAPCompareRequest : public LDAPRequest {
+ public :
+ LDAPCompareRequest(const LDAPCompareRequest& req);
+ LDAPCompareRequest(const std::string& dn, const LDAPAttribute& attr,
+ LDAPAsynConnection *connect, const LDAPConstraints *cons,
+ bool isReferral=false, const LDAPRequest* parent=0);
+ virtual ~LDAPCompareRequest();
+ virtual LDAPMessageQueue* sendRequest();
+ virtual LDAPRequest* followReferral(LDAPMsg* urls);
+
+ private :
+ std::string m_dn;
+ LDAPAttribute m_attr;
+
+};
+#endif //LDAP_COMPARE_REQUEST_H
+
+
diff --git a/contrib/ldapc++/src/LDAPConnection.cpp b/contrib/ldapc++/src/LDAPConnection.cpp
new file mode 100644
index 0000000..77111a1
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPConnection.cpp
@@ -0,0 +1,382 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "debug.h"
+
+#include "LDAPResult.h"
+#include "LDAPException.h"
+#include "LDAPUrlList.h"
+
+#include "LDAPConnection.h"
+const int LDAPConnection::SEARCH_BASE = LDAPAsynConnection::SEARCH_BASE;
+const int LDAPConnection::SEARCH_ONE = LDAPAsynConnection::SEARCH_ONE;
+const int LDAPConnection::SEARCH_SUB = LDAPAsynConnection::SEARCH_SUB;
+
+using namespace std;
+
+LDAPConnection::LDAPConnection(const string& hostname, int port,
+ LDAPConstraints* cons) :
+ LDAPAsynConnection(hostname, port, cons){
+}
+
+LDAPConnection::~LDAPConnection(){
+}
+
+void LDAPConnection::start_tls(){
+ LDAPAsynConnection::start_tls();
+}
+
+void LDAPConnection::bind(const string& dn, const string& passwd,
+ LDAPConstraints* cons){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConnection::bind" << endl);
+ LDAPMessageQueue* msg=0;
+ LDAPResult* res=0;
+ try{
+ msg = LDAPAsynConnection::bind(dn,passwd,cons);
+ res = (LDAPResult*)msg->getNext();
+ }catch(LDAPException e){
+ delete msg;
+ delete res;
+ throw;
+ }
+ int resCode=res->getResultCode();
+ if(resCode != LDAPResult::SUCCESS) {
+ if(resCode == LDAPResult::REFERRAL){
+ LDAPUrlList urls = res->getReferralUrls();
+ delete res;
+ delete msg;
+ throw LDAPReferralException(urls);
+ }else{
+ string srvMsg = res->getErrMsg();
+ delete res;
+ delete msg;
+ throw LDAPException(resCode, srvMsg);
+ }
+ }
+ delete res;
+ delete msg; // memcheck
+}
+
+void LDAPConnection::saslInteractiveBind( const std::string &mech,
+ int flags,
+ SaslInteractionHandler *sih,
+ const LDAPConstraints *cons)
+{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConnection::bind" << endl);
+ LDAPMessageQueue* msg=0;
+ LDAPResult* res=0;
+ try{
+ msg = LDAPAsynConnection::saslInteractiveBind(mech, flags, sih, cons);
+ res = (LDAPResult*)msg->getNext();
+ }catch(LDAPException e){
+ delete msg;
+ delete res;
+ throw;
+ }
+ int resCode=res->getResultCode();
+ if(resCode != LDAPResult::SUCCESS) {
+ if(resCode == LDAPResult::REFERRAL){
+ LDAPUrlList urls = res->getReferralUrls();
+ delete res;
+ delete msg;
+ throw LDAPReferralException(urls);
+ }else{
+ string srvMsg = res->getErrMsg();
+ delete res;
+ delete msg;
+ throw LDAPException(resCode, srvMsg);
+ }
+ }
+ delete res;
+ delete msg;
+}
+
+void LDAPConnection::unbind(){
+ LDAPAsynConnection::unbind();
+}
+
+bool LDAPConnection::compare(const string& dn, const LDAPAttribute& attr,
+ LDAPConstraints* cons){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConnection::compare" << endl);
+ LDAPMessageQueue* msg=0;
+ LDAPResult* res=0;
+ try{
+ msg = LDAPAsynConnection::compare(dn,attr,cons);
+ res = (LDAPResult*)msg->getNext();
+ }catch(LDAPException e){
+ delete msg;
+ delete res;
+ throw;
+ }
+ int resCode=res->getResultCode();
+ switch (resCode){
+ case LDAPResult::COMPARE_TRUE :
+ delete res;
+ delete msg;
+ return true;
+ break;
+ case LDAPResult::COMPARE_FALSE :
+ delete res;
+ delete msg;
+ return false;
+ break;
+ case LDAPResult::REFERRAL :
+ {
+ LDAPUrlList urls = res->getReferralUrls();
+ delete res;
+ delete msg;
+ throw LDAPReferralException(urls);
+ }
+ break;
+ default :
+ string srvMsg = res->getErrMsg();
+ delete res;
+ delete msg;
+ throw LDAPException(resCode, srvMsg);
+ }
+}
+
+void LDAPConnection::del(const string& dn, const LDAPConstraints* cons){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConnection::del" << endl);
+ LDAPMessageQueue* msg=0;
+ LDAPResult* res=0;
+ try{
+ msg = LDAPAsynConnection::del(dn,cons);
+ res = (LDAPResult*)msg->getNext();
+ }catch(LDAPException e){
+ delete msg;
+ delete res;
+ throw;
+ }
+ int resCode=res->getResultCode();
+ switch (resCode){
+ case LDAPResult::SUCCESS :
+ delete res;
+ delete msg;
+ break;
+ case LDAPResult::REFERRAL :
+ {
+ LDAPUrlList urls = res->getReferralUrls();
+ delete res;
+ delete msg;
+ throw LDAPReferralException(urls);
+ }
+ break;
+ default :
+ string srvMsg = res->getErrMsg();
+ delete res;
+ delete msg;
+ throw LDAPException(resCode, srvMsg);
+ }
+
+}
+
+void LDAPConnection::add(const LDAPEntry* le, const LDAPConstraints* cons){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConnection::add" << endl);
+ LDAPMessageQueue* msg=0;
+ LDAPResult* res=0;
+ try{
+ msg = LDAPAsynConnection::add(le,cons);
+ res = (LDAPResult*)msg->getNext();
+ }catch(LDAPException e){
+ delete msg;
+ delete res;
+ throw;
+ }
+ int resCode=res->getResultCode();
+ switch (resCode){
+ case LDAPResult::SUCCESS :
+ delete res;
+ delete msg;
+ break;
+ case LDAPResult::REFERRAL :
+ {
+ LDAPUrlList urls = res->getReferralUrls();
+ delete res;
+ delete msg;
+ throw LDAPReferralException(urls);
+ }
+ break;
+ default :
+ string srvMsg = res->getErrMsg();
+ delete res;
+ delete msg;
+ throw LDAPException(resCode, srvMsg);
+ }
+}
+
+void LDAPConnection::modify(const string& dn, const LDAPModList* mods,
+ const LDAPConstraints* cons){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConnection::modify" << endl);
+ LDAPMessageQueue* msg=0;
+ LDAPResult* res=0;
+ try{
+ msg = LDAPAsynConnection::modify(dn,mods,cons);
+ res = (LDAPResult*)msg->getNext();
+ }catch(LDAPException e){
+ delete msg;
+ delete res;
+ throw;
+ }
+ int resCode=res->getResultCode();
+ switch (resCode){
+ case LDAPResult::SUCCESS :
+ delete res;
+ delete msg;
+ break;
+ case LDAPResult::REFERRAL :
+ {
+ LDAPUrlList urls = res->getReferralUrls();
+ delete res;
+ delete msg;
+ throw LDAPReferralException(urls);
+ }
+ break;
+ default :
+ string srvMsg = res->getErrMsg();
+ delete res;
+ delete msg;
+ throw LDAPException(resCode, srvMsg);
+ }
+
+}
+
+void LDAPConnection::rename(const string& dn, const string& newRDN,
+ bool delOldRDN, const string& newParentDN,
+ const LDAPConstraints* cons){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConnection::rename" << endl);
+ LDAPMessageQueue* msg=0;
+ LDAPResult* res=0;
+ try{
+ msg = LDAPAsynConnection::rename(dn,newRDN,delOldRDN, newParentDN,
+ cons);
+ res = (LDAPResult*)msg->getNext();
+ }catch(LDAPException e){
+ delete msg;
+ delete res;
+ throw;
+ }
+ int resCode=res->getResultCode();
+ switch (resCode){
+ case LDAPResult::SUCCESS :
+ delete res;
+ delete msg;
+ break;
+ case LDAPResult::REFERRAL :
+ {
+ LDAPUrlList urls = res->getReferralUrls();
+ delete res;
+ delete msg;
+ throw LDAPReferralException(urls);
+ }
+ break;
+ default :
+ string srvMsg = res->getErrMsg();
+ delete res;
+ delete msg;
+ throw LDAPException(resCode, srvMsg);
+ }
+}
+
+LDAPSearchResults* LDAPConnection::search(const string& base, int scope,
+ const string& filter, const StringList& attrs, bool attrsOnly,
+ const LDAPConstraints* cons){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConnection::search" << endl);
+ LDAPMessageQueue* msgq=0;
+ LDAPResult* res=0;
+ LDAPSearchResults* results= 0;
+
+ try{
+ results = new LDAPSearchResults();
+ msgq = LDAPAsynConnection::search(base,scope, filter, attrs, attrsOnly,
+ cons);
+ res = results->readMessageQueue(msgq);
+ }catch(LDAPException e){
+ delete results; // memcheck
+ delete msgq;
+ throw;
+ }
+ if(res != 0){
+ int resCode=res->getResultCode();
+ switch (resCode){
+ case LDAPResult::SUCCESS :
+ delete res;
+ delete msgq;
+ return results;
+ break;
+ case LDAPResult::REFERRAL :
+ {
+ LDAPUrlList urls = res->getReferralUrls();
+ delete results; // memcheck
+ delete res;
+ delete msgq;
+ throw LDAPReferralException(urls);
+ }
+ break;
+ default :
+ string srvMsg = res->getErrMsg();
+ delete results; // memcheck
+ delete res;
+ delete msgq;
+ throw LDAPException(resCode, srvMsg);
+ }
+ }
+ return 0;
+}
+
+LDAPExtResult* LDAPConnection::extOperation(const string& oid,
+ const string& value, const LDAPConstraints *cons){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConnection::extOperation" << endl);
+ LDAPMessageQueue* msg=0;
+ LDAPExtResult* res=0;
+ try{
+ msg = LDAPAsynConnection::extOperation(oid,value,cons);
+ res = (LDAPExtResult*)msg->getNext();
+ }catch(LDAPException e){
+ delete msg;
+ delete res;
+ throw;
+ }
+ int resCode=res->getResultCode();
+ switch (resCode){
+ case LDAPResult::SUCCESS :
+ delete msg;
+ return res;
+ case LDAPResult::REFERRAL :
+ {
+ LDAPUrlList urls = res->getReferralUrls();
+ delete res;
+ delete msg;
+ throw LDAPReferralException(urls);
+ }
+ break;
+ default :
+ string srvMsg = res->getErrMsg();
+ delete res;
+ delete msg;
+ throw LDAPException(resCode, srvMsg);
+ }
+}
+
+const string& LDAPConnection::getHost() const{
+ return LDAPAsynConnection::getHost();
+}
+
+int LDAPConnection::getPort() const{
+ return LDAPAsynConnection::getPort();
+}
+
+void LDAPConnection::setConstraints(LDAPConstraints* cons){
+ LDAPAsynConnection::setConstraints(cons);
+}
+
+const LDAPConstraints* LDAPConnection::getConstraints() const{
+ return LDAPAsynConnection::getConstraints();
+}
+
+TlsOptions LDAPConnection::getTlsOptions() const {
+ return LDAPAsynConnection::getTlsOptions();
+}
diff --git a/contrib/ldapc++/src/LDAPConnection.h b/contrib/ldapc++/src/LDAPConnection.h
new file mode 100644
index 0000000..e2d7ccc
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPConnection.h
@@ -0,0 +1,241 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDAP_CONNECTION_H
+#define LDAP_CONNECTION_H
+
+#include <LDAPSearchResults.h>
+#include <LDAPExtResult.h>
+#include <LDAPAsynConnection.h>
+
+/** Main class for synchronous LDAP-Communication
+ *
+ * The class represent a LDAP-Connection to perform synchronous
+ * LDAP-Operations. This provides methods for the different
+ * LDAP-Operations. All the methods for the LDAP-operations block until
+ * all results for the operation are received or until an error occurs
+ */
+class LDAPConnection : private LDAPAsynConnection {
+
+ public :
+ /**
+ * Constant for the Search-Operation to indicate a Base-Level
+ * Search
+ */
+ static const int SEARCH_BASE;
+
+ /**
+ * Constant for the Search-Operation to indicate a One-Level
+ * Search
+ */
+ static const int SEARCH_ONE;
+
+ /**
+ * Constant for the Search-Operation to indicate a Subtree
+ * Search
+ */
+ static const int SEARCH_SUB;
+
+ /** This Constructor initializes synchronous LDAP-Connection
+ *
+ * During execution of this constructor no network communication
+ * is performed. Just some internal data structure are initialized
+ * @param hostname Name (or IP-Address) of the destination host
+ * @param port Port the LDAP server is running on
+ * @param cons Default constraints to use with operations over
+ * this connection
+ */
+ LDAPConnection(const std::string& hostname="localhost", int port=389,
+ LDAPConstraints* cons=new LDAPConstraints());
+
+ /**
+ * Destructor
+ */
+ ~LDAPConnection();
+
+ /**
+ * Initializes a synchronous connection to a server.
+ *
+ * There is actually no
+ * communication to the server. Just the object is initialized
+ * (e.g. this method is called within the
+ * LDAPConnection(char*,int,LDAPConstraints) constructor.)
+ * @param hostname The Name or IP-Address of the destination
+ * LDAP-Server
+ * @param port The Network Port the server is running on
+ */
+ void init(const std::string& hostname, int port);
+
+ /**
+ * Start TLS on this connection. This isn't in the constructor,
+ * because it could fail (i.e. server doesn't have SSL cert, client
+ * api wasn't compiled against OpenSSL, etc.).
+ * @throws LDAPException if the TLS Layer could not be setup
+ * correctly
+ */
+ void start_tls();
+
+ /**
+ * Performs a simple authentication with the server
+ *
+ * @throws LDAPReferralException if a referral is received
+ * @throws LDAPException for any other error occurring during the
+ * operation
+ * @param dn The name of the entry to bind as
+ * @param passwd The cleartext password for the entry
+ */
+ void bind(const std::string& dn="", const std::string& passwd="",
+ LDAPConstraints* cons=0);
+ void saslInteractiveBind(const std::string& mech,
+ int flags=0,
+ SaslInteractionHandler *sih=0,
+ const LDAPConstraints *cons=0);
+
+ /**
+ * Performs the UNBIND-operation on the destination server
+ *
+ * @throws LDAPException in any case of an error
+ */
+ void unbind();
+
+ /**
+ * Performs a COMPARE-operation on an entry of the destination
+ * server.
+ *
+ * @throws LDAPReferralException if a referral is received
+ * @throws LDAPException for any other error occurring during the
+ * operation
+ * @param dn Distinguished name of the entry for which the compare
+ * should be performed
+ * @param attr An Attribute (one (!) value) to use for the
+ * compare operation
+ * @param cons A set of constraints that should be used with this
+ * request
+ * @returns The result of the compare operation. true if the
+ * attr-parameter matched an Attribute of the entry. false if it
+ * did not match
+ */
+ bool compare(const std::string& dn, const LDAPAttribute& attr,
+ LDAPConstraints* cons=0);
+
+ /**
+ * Deletes an entry from the directory
+ *
+ * This method performs the DELETE operation on the server
+ * @throws LDAPReferralException if a referral is received
+ * @throws LDAPException for any other error occurring during the
+ * operation
+ * @param dn Distinguished name of the entry that should be deleted
+ * @param cons A set of constraints that should be used with this
+ * request
+ */
+ void del(const std::string& dn, const LDAPConstraints* cons=0);
+
+ /**
+ * Use this method to perform the ADD-operation
+ *
+ * @throws LDAPReferralException if a referral is received
+ * @throws LDAPException for any other error occurring during the
+ * operation
+ * @param le the entry to add to the directory
+ * @param cons A set of constraints that should be used with this
+ * request
+ */
+ void add(const LDAPEntry* le, const LDAPConstraints* cons=0);
+
+ /**
+ * To modify the attributes of an entry, this method can be used
+ *
+ * @throws LDAPReferralException if a referral is received
+ * @throws LDAPException for any other error occurring during the
+ * operation
+ * @param dn The DN of the entry which should be modified
+ * @param mods A set of modifications for that entry.
+ * @param cons A set of constraints that should be used with this
+ * request
+ */
+ void modify(const std::string& dn, const LDAPModList* mods,
+ const LDAPConstraints* cons=0);
+
+ /**
+ * This method performs the ModDN-operation.
+ *
+ * It can be used to rename or move an entry by modifying its DN.
+ *
+ * @throws LDAPReferralException if a referral is received
+ * @throws LDAPException for any other error occurring during the
+ * operation
+ * @param dn The DN that should be modified
+ * @param newRDN If the RDN of the entry should be modified the
+ * new RDN can be put here.
+ * @param delOldRDN If the old RDN should be removed from the
+ * entry's attribute this parameter has to be
+ * "true"
+ * @param newParentDN If the entry should be moved inside the
+ * DIT, the DN of the new parent of the entry
+ * can be given here.
+ * @param cons A set of constraints that should be used with this
+ * request
+ */
+ void rename(const std::string& dn, const std::string& newRDN,
+ bool delOldRDN=false, const std::string& newParentDN="",
+ const LDAPConstraints* cons=0);
+
+ /**
+ * This method can be used for the sync. SEARCH-operation.
+ *
+ * @throws LDAPReferralException if a referral is received
+ * @throws LDAPException for any other error occurring during the
+ * operation
+ * @param base The distinguished name of the starting point for the
+ * search
+ * @param scope The scope of the search. Possible values: <BR>
+ * LDAPAsynConnection::SEARCH_BASE, <BR>
+ * LDAPAsynConnection::SEARCH_ONE, <BR>
+ * LDAPAsynConnection::SEARCH_SUB
+ * @param filter The std::string representation of a search filter to
+ * use with this operation
+ * @param attrsOnly true if only the attributes names (no values)
+ * should be returned
+ * @param cons A set of constraints that should be used with this
+ * request
+ * @returns A pointer to a LDAPSearchResults-object that can be
+ * used to read the results of the search.
+ */
+ LDAPSearchResults* search(const std::string& base, int scope=0,
+ const std::string& filter="objectClass=*",
+ const StringList& attrs=StringList(), bool attrsOnly=false,
+ const LDAPConstraints* cons=0);
+
+ /**
+ * This method is for extended LDAP-Operations.
+ *
+ * @throws LDAPReferralException if a referral is received
+ * @throws LDAPException for any other error occurring during the
+ * operation
+ * @param oid The Object Identifier of the Extended Operation that
+ * should be performed.
+ * @param strint If the Extended Operation needs some additional
+ * data it can be passed to the server by this parameter.
+ * @param cons A set of constraints that should be used with this
+ * request
+ * @returns The result of the Extended Operation as an
+ * pointer to a LDAPExtResult-object.
+ */
+ LDAPExtResult* extOperation(const std::string& oid, const std::string&
+ value="", const LDAPConstraints *const = 0);
+
+ const std::string& getHost() const;
+
+ int getPort() const;
+
+ void setConstraints(LDAPConstraints *cons);
+
+ const LDAPConstraints* getConstraints() const ;
+ TlsOptions getTlsOptions() const;
+};
+
+#endif //LDAP_CONNECTION_H
diff --git a/contrib/ldapc++/src/LDAPConstraints.cpp b/contrib/ldapc++/src/LDAPConstraints.cpp
new file mode 100644
index 0000000..c91d7ca
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPConstraints.cpp
@@ -0,0 +1,178 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#include "debug.h"
+#include "config.h"
+#include "ac/time.h"
+#include "LDAPConstraints.h"
+#include "LDAPControlSet.h"
+
+using namespace std;
+
+LDAPConstraints::LDAPConstraints(){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPConstraints::LDAPConstraints()" << endl);
+ m_aliasDeref=LDAPConstraints::DEREF_NEVER;
+ m_maxTime=LDAP_NO_LIMIT;
+ m_maxSize=LDAP_NO_LIMIT;
+ m_referralChase=false;
+ m_HopLimit=7;
+ m_serverControls=0;
+ m_clientControls=0;
+ m_refRebind=0;
+}
+
+LDAPConstraints::LDAPConstraints(const LDAPConstraints& c){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPConstraints::LDAPConstraints(&)" << endl);
+ m_aliasDeref=c.m_aliasDeref;
+ m_maxTime=c.m_maxTime;
+ m_maxSize=c.m_maxSize;
+ m_referralChase=c.m_referralChase;
+ m_HopLimit=c.m_HopLimit;
+ m_deref=c.m_deref;
+ if(c.m_serverControls){
+ m_serverControls=new LDAPControlSet(*c.m_serverControls);
+ }else{
+ m_serverControls=0;
+ }
+ if(c.m_clientControls){
+ m_clientControls=new LDAPControlSet(*c.m_clientControls);
+ }else{
+ m_clientControls=0;
+ }
+ m_refRebind=c.m_refRebind;
+}
+
+LDAPConstraints::~LDAPConstraints(){
+ DEBUG(LDAP_DEBUG_DESTROY,"LDAPConstraints::~LDAPConstraints()" << endl);
+ delete m_clientControls;
+ delete m_serverControls;
+}
+
+void LDAPConstraints::setAliasDeref(int deref){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConstraints::setAliasDeref()" << endl);
+ DEBUG(LDAP_DEBUG_TRACE | LDAP_DEBUG_PARAMETER,
+ " deref:" << deref << endl);
+ if( (deref == LDAPConstraints::DEREF_NEVER) ||
+ (deref == LDAPConstraints::DEREF_SEARCHING) ||
+ (deref == LDAPConstraints::DEREF_FINDING) ||
+ (deref == LDAPConstraints::DEREF_ALWAYS)
+ ){
+ m_aliasDeref=deref;
+ }
+}
+
+
+void LDAPConstraints::setMaxTime(int t){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConstraints::setMaxTime()" << endl);
+ DEBUG(LDAP_DEBUG_TRACE | LDAP_DEBUG_PARAMETER," time:" << t << endl);
+ m_maxTime=t;
+}
+
+void LDAPConstraints::setSizeLimit(int s){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConstraints::setSizeLimit()" << endl);
+ DEBUG(LDAP_DEBUG_TRACE | LDAP_DEBUG_PARAMETER," size:" << s << endl);
+ m_maxSize=s;
+}
+
+void LDAPConstraints::setReferralChase(bool rc){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConstraints::setReferralChase()" << endl);
+ DEBUG(LDAP_DEBUG_TRACE | LDAP_DEBUG_PARAMETER," rc:" << rc << endl);
+ m_referralChase=rc;
+}
+
+void LDAPConstraints::setHopLimit(int limit){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConstraints::setHopLimit()" << endl);
+ DEBUG(LDAP_DEBUG_TRACE | LDAP_DEBUG_PARAMETER,
+ " limit:" << limit << endl);
+ m_HopLimit=limit;
+}
+
+void LDAPConstraints::setReferralRebind(const LDAPRebind* rebind){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConstraints::setReferralRebind()" << endl);
+ m_refRebind = rebind;
+}
+
+void LDAPConstraints::setServerControls(const LDAPControlSet* ctrls){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConstraints::setServerControls()" << endl);
+ m_serverControls=new LDAPControlSet(*ctrls);
+}
+
+void LDAPConstraints::setClientControls(const LDAPControlSet* ctrls){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConstraints::setClientControls()" << endl);
+ m_clientControls=new LDAPControlSet(*ctrls);
+}
+
+int LDAPConstraints::getAliasDeref() const {
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConstraints::getAliasDeref()" << endl);
+ return m_aliasDeref;
+}
+
+int LDAPConstraints::getMaxTime() const {
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConstraints::getMaxTime()" << endl);
+ return m_maxTime;
+}
+
+int LDAPConstraints::getSizeLimit() const {
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConstraints::getSizeLimit()" << endl);
+ return m_maxSize;
+}
+
+const LDAPRebind* LDAPConstraints::getReferralRebind() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConstraints::getReferralRebind()" << endl);
+ return m_refRebind;
+}
+
+const LDAPControlSet* LDAPConstraints::getServerControls() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConstraints::getServerControls()" << endl);
+ return m_serverControls;
+}
+
+const LDAPControlSet* LDAPConstraints::getClientControls() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConstraints::getClientControls()" << endl);
+ return m_clientControls;
+}
+
+LDAPControl** LDAPConstraints::getSrvCtrlsArray() const {
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConstraints::getSrvCtrlsArray()" << endl);
+ if(m_serverControls){
+ return m_serverControls->toLDAPControlArray();
+ }else{
+ return 0;
+ }
+}
+
+LDAPControl** LDAPConstraints::getClCtrlsArray() const {
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConstraints::getClCtrlsArray()" << endl);
+ if(m_clientControls){
+ return m_clientControls->toLDAPControlArray();
+ }else{
+ return 0;
+ }
+}
+
+timeval* LDAPConstraints::getTimeoutStruct() const {
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConstraints::getTimeoutStruct()" << endl);
+ if(m_maxTime == LDAP_NO_LIMIT){
+ return 0;
+ }else{
+ timeval *ret = new timeval;
+ ret->tv_sec=m_maxTime;
+ ret->tv_usec=0;
+ return ret;
+ }
+}
+
+bool LDAPConstraints::getReferralChase() const {
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConstraints::getReferralChase()" << endl);
+ return m_referralChase;
+}
+
+int LDAPConstraints::getHopLimit() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPConstraints::getHopLimit()" << endl);
+ return m_HopLimit;
+}
+
diff --git a/contrib/ldapc++/src/LDAPConstraints.h b/contrib/ldapc++/src/LDAPConstraints.h
new file mode 100644
index 0000000..32f1d1d
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPConstraints.h
@@ -0,0 +1,98 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#ifndef LDAP_CONSTRAINTS_H
+#define LDAP_CONSTRAINTS_H
+#include <list>
+
+#include <LDAPControl.h>
+#include <LDAPControlSet.h>
+#include <LDAPRebind.h>
+
+//TODO!!
+// * implement the Alias-Handling Option (OPT_DEREF)
+// * the Restart-Option ???
+// * default Server(s)
+
+//* Class for representing the various protocol options
+/** This class represents some options that can be set for a LDAPConnection
+ * operation. Namely these are time and size limits. Options for referral
+ * chasing and a default set of client of server controls to be used with
+ * every request
+ */
+class LDAPConstraints{
+
+ public :
+ static const int DEREF_NEVER = 0x00;
+ static const int DEREF_SEARCHING = 0x01;
+ static const int DEREF_FINDING = 0x02;
+ static const int DEREF_ALWAYS = 0x04;
+
+ //* Constructs a LDAPConstraints object with default values
+ LDAPConstraints();
+
+ //* Copy constructor
+ LDAPConstraints(const LDAPConstraints& c);
+
+ ~LDAPConstraints();
+
+ void setAliasDeref(int deref);
+ void setMaxTime(int t);
+ void setSizeLimit(int s);
+ void setReferralChase(bool rc);
+ void setHopLimit(int hop);
+ void setReferralRebind(const LDAPRebind* rebind);
+ void setServerControls(const LDAPControlSet* ctrls);
+ void setClientControls(const LDAPControlSet* ctrls);
+
+ int getAliasDeref() const;
+ int getMaxTime() const ;
+ int getSizeLimit() const;
+ const LDAPRebind* getReferralRebind() const;
+ const LDAPControlSet* getServerControls() const;
+ const LDAPControlSet* getClientControls() const;
+
+ //*for internal use only
+ LDAPControl** getSrvCtrlsArray() const;
+
+ //*for internal use only
+ LDAPControl** getClCtrlsArray() const;
+
+ //*for internal use only
+ timeval* getTimeoutStruct() const;
+ bool getReferralChase() const ;
+ int getHopLimit() const;
+
+ private :
+ int m_aliasDeref;
+
+ //* max. time the server may spend for a search request
+ int m_maxTime;
+
+ //* max number of entries to return from a search request
+ int m_maxSize;
+
+ //* Flag for enabling automatic referral/reference chasing
+ bool m_referralChase;
+
+ //* HopLimit for referral chasing
+ int m_HopLimit;
+
+ //* Alias dereferencing option
+ int m_deref;
+
+ //* Object used to do bind for Referral chasing
+ const LDAPRebind* m_refRebind;
+
+ //* List of Client Controls that should be used for each request
+ LDAPControlSet* m_clientControls;
+
+ //* List of Server Controls that should be used for each request
+ LDAPControlSet* m_serverControls;
+
+};
+#endif //LDAP_CONSTRAINTS_H
diff --git a/contrib/ldapc++/src/LDAPControl.cpp b/contrib/ldapc++/src/LDAPControl.cpp
new file mode 100644
index 0000000..7ca4445
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPControl.cpp
@@ -0,0 +1,94 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#include "LDAPControl.h"
+#include "debug.h"
+
+using namespace std;
+
+LDAPCtrl::LDAPCtrl(const char *oid, bool critical, const char* data,
+ int length){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPCtrl::LDAPCtrl()" << endl);
+ DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER,
+ " oid:" << oid << endl << " critical:" << critical << endl);
+ m_oid = oid;
+ m_isCritical=critical;
+ if (data != 0 && length !=0){
+ m_data.assign(data,length);
+ m_noData=false;
+ }else{
+ m_data=string();
+ m_noData=true;
+ }
+}
+
+LDAPCtrl::LDAPCtrl(const string& oid, bool critical, const string& data){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPCtrl::LDAPCtrl()" << endl);
+ DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER,
+ " oid:" << oid << endl << " critical:" << critical << endl);
+ m_oid=oid;
+ m_isCritical=critical;
+ m_data=data;
+ m_noData=false;
+}
+
+LDAPCtrl::LDAPCtrl(const LDAPControl* ctrl){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPCtrl::LDAPCtrl()" << endl);
+ m_oid = string(ctrl->ldctl_oid);
+ m_isCritical = ctrl->ldctl_iscritical ? true : false;
+ m_data = string(ctrl->ldctl_value.bv_val, ctrl->ldctl_value.bv_len );
+ m_noData = ctrl->ldctl_value.bv_len ? false : true;
+}
+
+LDAPCtrl::~LDAPCtrl(){
+ DEBUG(LDAP_DEBUG_DESTROY,"LDAPCtrl::~LDAPCtrl" << endl);
+}
+
+string LDAPCtrl::getOID() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPCtrl::getOID()" << endl);
+ return m_oid;
+}
+
+bool LDAPCtrl::isCritical()const {
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPCtrl::isCritical()" << endl);
+ return m_isCritical;
+}
+
+bool LDAPCtrl::hasData() const{
+ return !m_noData;
+}
+
+string LDAPCtrl::getData() const {
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPCtrl::getData()" << endl);
+ return m_data;
+}
+
+LDAPControl* LDAPCtrl::getControlStruct() const {
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPCtrl::getControlStruct()" << endl);
+ LDAPControl* ret = new LDAPControl;
+ ret->ldctl_oid= new char[m_oid.size() + 1];
+ m_oid.copy(ret->ldctl_oid,string::npos);
+ ret->ldctl_oid[m_oid.size()]=0;
+ if ( m_noData ) {
+ ret->ldctl_value.bv_len = 0;
+ ret->ldctl_value.bv_val = NULL;
+ } else {
+ ret->ldctl_value.bv_len=m_data.size();
+ ret->ldctl_value.bv_val= new char[m_data.size()];
+ m_data.copy(ret->ldctl_value.bv_val,string::npos);
+ }
+ ret->ldctl_iscritical = ( m_isCritical ? 1:0);
+ return ret;
+}
+
+void LDAPCtrl::freeLDAPControlStruct(LDAPControl *ctrl){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPCtrl::freeControlStruct()" << endl);
+ delete[] ctrl->ldctl_oid;
+ delete[] ctrl->ldctl_value.bv_val;
+ delete ctrl;
+}
+
diff --git a/contrib/ldapc++/src/LDAPControl.h b/contrib/ldapc++/src/LDAPControl.h
new file mode 100644
index 0000000..a8e0c94
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPControl.h
@@ -0,0 +1,87 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#ifndef LDAP_CONTROL_H
+#define LDAP_CONTROL_H
+#include <string>
+#include <ldap.h>
+
+/**
+ * This class is used to store Controls. Controls are a mechanism to extend
+ * and modify LDAP-Operations.
+ */
+class LDAPCtrl{
+ public :
+ /**
+ * Constructor.
+ * @param oid: The Object Identifier of the Control
+ * @param critical: "true" if the Control should be handled
+ * critical by the server.
+ * @param data: If there is data for the control, put it here.
+ * @param length: The length of the data field
+ */
+ LDAPCtrl(const char *oid, bool critical=false, const char *data=0,
+ int length=0);
+
+ /**
+ * Constructor.
+ * @param oid: The Object Identifier of the Control
+ * @param critical: "true" if the Control should be handled
+ * critical by the server.
+ * @param data: If there is data for the control, put it here.
+ */
+ LDAPCtrl(const std::string& oid, bool critical,
+ const std::string& data);
+
+ /**
+ * Creates a copy of the Control that "ctrl is pointing to
+ */
+ LDAPCtrl(const LDAPControl* ctrl);
+
+ /**
+ * Destructor
+ */
+ ~LDAPCtrl();
+
+ /**
+ * @return The OID of the control
+ */
+ std::string getOID() const;
+
+ /**
+ * @return true if there is no "Control Value" (there is a
+ * difference between no and an empty control value)
+ */
+ bool hasData() const;
+
+ /**
+ * @return The Data of the control as a std::string-Object
+ */
+ std::string getData() const;
+
+ /**
+ * @return "true" if the control is critical
+ */
+ bool isCritical() const;
+
+ /**
+ * For internal use only.
+ *
+ * Translates the control to a LDAPControl-structure as needed by
+ * the C-API
+ */
+ LDAPControl* getControlStruct() const;
+ static void freeLDAPControlStruct(LDAPControl *ctrl);
+
+ private :
+ std::string m_oid;
+ std::string m_data;
+ bool m_isCritical;
+ bool m_noData;
+};
+
+#endif //LDAP_CONTROL_H
diff --git a/contrib/ldapc++/src/LDAPControlSet.cpp b/contrib/ldapc++/src/LDAPControlSet.cpp
new file mode 100644
index 0000000..ce9f3de
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPControlSet.cpp
@@ -0,0 +1,84 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "debug.h"
+#include "LDAPControlSet.h"
+
+using namespace std;
+
+LDAPControlSet::LDAPControlSet(){
+}
+
+LDAPControlSet::LDAPControlSet(const LDAPControlSet& cs){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPControlSet::LDAPControlSet(&)" << endl);
+ data=cs.data;
+}
+
+LDAPControlSet::LDAPControlSet(LDAPControl** controls){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPControlSet::LDAPControlSet()" << endl);
+ if(controls != 0){
+ LDAPControl** i;
+ for( i=controls; *i!=0;i++) {
+ add(LDAPCtrl(*i));
+ }
+ }
+}
+
+LDAPControlSet::~LDAPControlSet(){
+ DEBUG(LDAP_DEBUG_DESTROY,"LDAPControlSet::~LDAPControlSet()" << endl);
+}
+
+size_t LDAPControlSet::size() const {
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPControlSet::size()" << endl);
+ return data.size();
+}
+
+bool LDAPControlSet::empty() const {
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPControlSet::empty()" << endl);
+ return data.empty();
+}
+
+LDAPControlSet::const_iterator LDAPControlSet::begin() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPControlSet::begin()" << endl);
+ return data.begin();
+}
+
+
+LDAPControlSet::const_iterator LDAPControlSet::end() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPControlSet::end()" << endl);
+ return data.end ();
+}
+
+void LDAPControlSet::add(const LDAPCtrl& ctrl){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPControlSet::add()" << endl);
+ data.push_back(ctrl);
+}
+
+LDAPControl** LDAPControlSet::toLDAPControlArray() const{
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPControlSet::toLDAPControlArray()" << endl);
+ if(data.empty()){
+ return 0;
+ }else{
+ LDAPControl** ret= new LDAPControl*[data.size()+1];
+ CtrlList::const_iterator i;
+ int j=0;
+ for(i=data.begin(); i!=data.end(); i++,j++){
+ ret[j] = i->getControlStruct();
+ }
+ ret[data.size()]=0;
+ return ret;
+ }
+}
+
+void LDAPControlSet::freeLDAPControlArray(LDAPControl **ctrl){
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPControlSet::freeLDAPControlArray()" << endl);
+ if( ctrl ){
+ for( LDAPControl **i = ctrl; *i != 0; ++i ){
+ LDAPCtrl::freeLDAPControlStruct(*i);
+ }
+ }
+ delete[] ctrl;
+}
diff --git a/contrib/ldapc++/src/LDAPControlSet.h b/contrib/ldapc++/src/LDAPControlSet.h
new file mode 100644
index 0000000..4c033be
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPControlSet.h
@@ -0,0 +1,89 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDAP_CONTROL_SET_H
+#define LDAP_CONTROL_SET_H
+
+#include <list>
+#include <ldap.h>
+#include <LDAPControl.h>
+
+typedef std::list<LDAPCtrl> CtrlList;
+
+/**
+ * This container class is used to store multiple LDAPCtrl-objects.
+ */
+class LDAPControlSet {
+ typedef CtrlList::const_iterator const_iterator;
+ public :
+ /**
+ * Constructs an empty std::list
+ */
+ LDAPControlSet();
+
+
+ /**
+ * Copy-constructor
+ */
+ LDAPControlSet(const LDAPControlSet& cs);
+
+ /**
+ * For internal use only
+ *
+ * This constructor creates a new LDAPControlSet for a
+ * 0-terminated array of LDAPControl-structures as used by the
+ * C-API
+ * @param controls: pointer to a 0-terminated array of pointers to
+ * LDAPControl-structures
+ * @note: untested til now. Due to lack of server that return
+ * Controls
+ */
+ LDAPControlSet(LDAPControl** controls);
+
+ /**
+ * Destructor
+ */
+ ~LDAPControlSet();
+
+ /**
+ * @return The number of LDAPCtrl-objects that are currently
+ * stored in this list.
+ */
+ size_t size() const ;
+
+ /**
+ * @return true if there are zero LDAPCtrl-objects currently
+ * stored in this list.
+ */
+ bool empty() const;
+
+ /**
+ * @return A iterator that points to the first element of the list.
+ */
+ const_iterator begin() const;
+
+ /**
+ * @return A iterator that points to the element after the last
+ * element of the list.
+ */
+ const_iterator end() const;
+
+ /**
+ * Adds one element to the end of the list.
+ * @param ctrl The Control to add to the list.
+ */
+ void add(const LDAPCtrl& ctrl);
+
+ /**
+ * Translates the list to a 0-terminated array of pointers to
+ * LDAPControl-structures as needed by the C-API
+ */
+ LDAPControl** toLDAPControlArray()const ;
+ static void freeLDAPControlArray(LDAPControl **ctrl);
+ private :
+ CtrlList data;
+} ;
+#endif //LDAP_CONTROL_SET_H
diff --git a/contrib/ldapc++/src/LDAPDeleteRequest.cpp b/contrib/ldapc++/src/LDAPDeleteRequest.cpp
new file mode 100644
index 0000000..8ae82b4
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPDeleteRequest.cpp
@@ -0,0 +1,75 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include <ldap.h>
+
+#include "debug.h"
+
+#include "LDAPDeleteRequest.h"
+#include "LDAPException.h"
+#include "LDAPMessageQueue.h"
+#include "LDAPResult.h"
+
+using namespace std;
+
+LDAPDeleteRequest::LDAPDeleteRequest( const LDAPDeleteRequest& req) :
+ LDAPRequest(req){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,
+ "LDAPDeleteRequest::LDAPDeleteRequest(&)" << endl);
+ m_dn = req.m_dn;
+}
+
+LDAPDeleteRequest::LDAPDeleteRequest(const string& dn,
+ LDAPAsynConnection *connect, const LDAPConstraints *cons,
+ bool isReferral, const LDAPRequest* parent)
+ : LDAPRequest(connect, cons, isReferral, parent) {
+ DEBUG(LDAP_DEBUG_CONSTRUCT,
+ "LDAPDeleteRequest::LDAPDeleteRequest()" << endl);
+ DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER, " dn:" << dn << endl);
+ m_requestType=LDAPRequest::DELETE;
+ m_dn=dn;
+}
+
+LDAPDeleteRequest::~LDAPDeleteRequest(){
+ DEBUG(LDAP_DEBUG_DESTROY,
+ "LDAPDeleteRequest::~LDAPDeleteRequest()" << endl);
+}
+
+LDAPMessageQueue* LDAPDeleteRequest::sendRequest(){
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPDeleteRequest::sendRequest()" << endl);
+ int msgID=0;
+ LDAPControl** tmpSrvCtrls=m_cons->getSrvCtrlsArray();
+ LDAPControl** tmpClCtrls=m_cons->getClCtrlsArray();
+ int err=ldap_delete_ext(m_connection->getSessionHandle(),m_dn.c_str(),
+ tmpSrvCtrls, tmpClCtrls ,&msgID);
+ LDAPControlSet::freeLDAPControlArray(tmpSrvCtrls);
+ LDAPControlSet::freeLDAPControlArray(tmpClCtrls);
+ if(err != LDAP_SUCCESS){
+ throw LDAPException(err);
+ }else{
+ m_msgID=msgID;
+ return new LDAPMessageQueue(this);
+ }
+}
+
+LDAPRequest* LDAPDeleteRequest::followReferral(LDAPMsg* refs){
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPDeleteRequest::followReferral()" << endl);
+ LDAPUrlList::const_iterator usedUrl;
+ LDAPUrlList urls= ((LDAPResult*)refs)->getReferralUrls();
+ LDAPAsynConnection* con=0;
+ try{
+ con = getConnection()->referralConnect(urls,usedUrl,m_cons);
+ }catch (LDAPException e){
+ delete con;
+ return 0;
+ }
+ if(con != 0){
+ return new LDAPDeleteRequest(m_dn, con, m_cons, true, this);
+ }
+ return 0;
+}
+
+
diff --git a/contrib/ldapc++/src/LDAPDeleteRequest.h b/contrib/ldapc++/src/LDAPDeleteRequest.h
new file mode 100644
index 0000000..f1250ff
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPDeleteRequest.h
@@ -0,0 +1,26 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDAP_DELETE_REQUEST_H
+#define LDAP_DELETE_REQUEST_H
+
+#include <LDAPRequest.h>
+class LDAPMessageQueue;
+
+class LDAPDeleteRequest : public LDAPRequest{
+ public :
+ LDAPDeleteRequest(const LDAPDeleteRequest& req);
+ LDAPDeleteRequest(const std::string& dn, LDAPAsynConnection *connect,
+ const LDAPConstraints *cons, bool isReferral=false,
+ const LDAPRequest* parent=0);
+ virtual ~LDAPDeleteRequest();
+ virtual LDAPMessageQueue* sendRequest();
+ virtual LDAPRequest* followReferral(LDAPMsg* refs);
+
+ private :
+ std::string m_dn;
+};
+#endif //LDAP_DELETE_REQUEST_H
diff --git a/contrib/ldapc++/src/LDAPEntry.cpp b/contrib/ldapc++/src/LDAPEntry.cpp
new file mode 100644
index 0000000..f597426
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPEntry.cpp
@@ -0,0 +1,104 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#include "debug.h"
+#include "LDAPEntry.h"
+
+#include "LDAPAsynConnection.h"
+#include "LDAPException.h"
+
+using namespace std;
+
+LDAPEntry::LDAPEntry(const LDAPEntry& entry){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPEntry::LDAPEntry(&)" << endl);
+ m_dn=entry.m_dn;
+ m_attrs=new LDAPAttributeList( *(entry.m_attrs));
+}
+
+
+LDAPEntry::LDAPEntry(const string& dn, const LDAPAttributeList *attrs){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPEntry::LDAPEntry()" << endl);
+ DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER,
+ " dn:" << dn << endl);
+ if ( attrs )
+ m_attrs=new LDAPAttributeList(*attrs);
+ else
+ m_attrs=new LDAPAttributeList();
+ m_dn=dn;
+}
+
+LDAPEntry::LDAPEntry(const LDAPAsynConnection *ld, LDAPMessage *msg){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPEntry::LDAPEntry()" << endl);
+ char* tmp=ldap_get_dn(ld->getSessionHandle(),msg);
+ m_dn=string(tmp);
+ ldap_memfree(tmp);
+ m_attrs = new LDAPAttributeList(ld, msg);
+}
+
+LDAPEntry::~LDAPEntry(){
+ DEBUG(LDAP_DEBUG_DESTROY,"LDAPEntry::~LDAPEntry()" << endl);
+ delete m_attrs;
+}
+
+LDAPEntry& LDAPEntry::operator=(const LDAPEntry& from){
+ m_dn = from.m_dn;
+ delete m_attrs;
+ m_attrs = new LDAPAttributeList( *(from.m_attrs));
+ return *this;
+}
+
+void LDAPEntry::setDN(const string& dn){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPEntry::setDN()" << endl);
+ DEBUG(LDAP_DEBUG_TRACE | LDAP_DEBUG_PARAMETER,
+ " dn:" << dn << endl);
+ m_dn=dn;
+}
+
+void LDAPEntry::setAttributes(LDAPAttributeList *attrs){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPEntry::setAttributes()" << endl);
+ DEBUG(LDAP_DEBUG_TRACE | LDAP_DEBUG_PARAMETER,
+ " attrs:" << *attrs << endl);
+ if (m_attrs != 0){
+ delete m_attrs;
+ }
+ m_attrs=attrs;
+}
+
+const string& LDAPEntry::getDN() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPEntry::getDN()" << endl);
+ return m_dn;
+}
+
+const LDAPAttributeList* LDAPEntry::getAttributes() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPEntry::getAttributes()" << endl);
+ return m_attrs;
+}
+
+const LDAPAttribute* LDAPEntry::getAttributeByName(const std::string& name) const
+{
+ return m_attrs->getAttributeByName(name);
+}
+
+void LDAPEntry::addAttribute(const LDAPAttribute& attr)
+{
+ m_attrs->addAttribute(attr);
+}
+
+void LDAPEntry::delAttribute(const std::string& type)
+{
+ m_attrs->delAttribute(type);
+}
+
+void LDAPEntry::replaceAttribute(const LDAPAttribute& attr)
+{
+ m_attrs->replaceAttribute(attr);
+}
+
+ostream& operator << (ostream& s, const LDAPEntry& le){
+ s << "DN: " << le.m_dn << ": " << *(le.m_attrs);
+ return s;
+}
diff --git a/contrib/ldapc++/src/LDAPEntry.h b/contrib/ldapc++/src/LDAPEntry.h
new file mode 100644
index 0000000..7155523
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPEntry.h
@@ -0,0 +1,116 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#ifndef LDAP_ENTRY_H
+#define LDAP_ENTRY_H
+#include <ldap.h>
+
+#include <LDAPAttributeList.h>
+
+class LDAPAsynConnection;
+
+/**
+ * This class is used to store every kind of LDAP Entry.
+ */
+class LDAPEntry{
+
+ public :
+ /**
+ * Copy-constructor
+ */
+ LDAPEntry(const LDAPEntry& entry);
+
+ /**
+ * Constructs a new entry (also used as standard constructor).
+ *
+ * @param dn The Distinguished Name for the new entry.
+ * @param attrs The attributes for the new entry.
+ */
+ LDAPEntry(const std::string& dn=std::string(),
+ const LDAPAttributeList *attrs=0);
+
+ /**
+ * Used internally only.
+ *
+ * The constructor is used internally to create a LDAPEntry from
+ * the C-API's data structures.
+ */
+ LDAPEntry(const LDAPAsynConnection *ld, LDAPMessage *msg);
+
+ /**
+ * Destructor
+ */
+ ~LDAPEntry();
+
+ /**
+ * Assignment operator
+ */
+ LDAPEntry& operator=(const LDAPEntry& from);
+
+ /**
+ * Sets the DN-attribute.
+ * @param dn: The new DN for the entry.
+ */
+ void setDN(const std::string& dn);
+
+ /**
+ * Sets the attributes of the entry.
+ * @param attr: A pointer to a std::list of the new attributes.
+ */
+ void setAttributes(LDAPAttributeList *attrs);
+
+ /**
+ * Get an Attribute by its AttributeType (simple wrapper around
+ * LDAPAttributeList::getAttributeByName() )
+ * @param name The name of the Attribute to look for
+ * @return a pointer to the LDAPAttribute with the AttributeType
+ * "name" or 0, if there is no Attribute of that Type
+ */
+ const LDAPAttribute* getAttributeByName(const std::string& name) const;
+
+ /**
+ * Adds one Attribute to the List of Attributes (simple wrapper around
+ * LDAPAttributeList::addAttribute() ).
+ * @param attr The attribute to add to the list.
+ */
+ void addAttribute(const LDAPAttribute& attr);
+
+ /**
+ * Deletes all values of an Attribute from the list of Attributes
+ * (simple wrapper around LDAPAttributeList::delAttribute() ).
+ * @param type The attribute to delete.
+ */
+ void delAttribute(const std::string& type);
+
+ /**
+ * Replace an Attribute in the List of Attributes (simple wrapper
+ * around LDAPAttributeList::replaceAttribute() ).
+ * @param attr The attribute to add to the list.
+ */
+ void replaceAttribute(const LDAPAttribute& attr);
+
+ /**
+ * @returns The current DN of the entry.
+ */
+ const std::string& getDN() const ;
+
+ /**
+ * @returns A const pointer to the attributes of the entry.
+ */
+ const LDAPAttributeList* getAttributes() const;
+
+ /**
+ * This method can be used to dump the data of a LDAPResult-Object.
+ * It is only useful for debugging purposes at the moment
+ */
+ friend std::ostream& operator << (std::ostream& s, const LDAPEntry& le);
+
+ private :
+ LDAPAttributeList *m_attrs;
+ std::string m_dn;
+};
+#endif //LDAP_ENTRY_H
diff --git a/contrib/ldapc++/src/LDAPEntryList.cpp b/contrib/ldapc++/src/LDAPEntryList.cpp
new file mode 100644
index 0000000..1d0b737
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPEntryList.cpp
@@ -0,0 +1,40 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#include "LDAPEntryList.h"
+#include "LDAPEntry.h"
+
+LDAPEntryList::LDAPEntryList(){
+}
+
+LDAPEntryList::LDAPEntryList(const LDAPEntryList& e){
+ m_entries = e.m_entries;
+}
+
+LDAPEntryList::~LDAPEntryList(){
+}
+
+size_t LDAPEntryList::size() const{
+ return m_entries.size();
+}
+
+bool LDAPEntryList::empty() const{
+ return m_entries.empty();
+}
+
+LDAPEntryList::const_iterator LDAPEntryList::begin() const{
+ return m_entries.begin();
+}
+
+LDAPEntryList::const_iterator LDAPEntryList::end() const{
+ return m_entries.end();
+}
+
+void LDAPEntryList::addEntry(const LDAPEntry& e){
+ m_entries.push_back(e);
+}
+
diff --git a/contrib/ldapc++/src/LDAPEntryList.h b/contrib/ldapc++/src/LDAPEntryList.h
new file mode 100644
index 0000000..d9aea6d
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPEntryList.h
@@ -0,0 +1,70 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDAP_ENTRY_LIST_H
+#define LDAP_ENTRY_LIST_H
+
+#include <cstdio>
+#include <list>
+
+class LDAPEntry;
+
+/**
+ * For internal use only.
+ *
+ * This class is used by LDAPSearchResults to store a std::list of
+ * LDAPEntry-Objects
+ */
+class LDAPEntryList{
+ typedef std::list<LDAPEntry> ListType;
+
+ public:
+ typedef ListType::const_iterator const_iterator;
+
+ /**
+ * Copy-Constructor
+ */
+ LDAPEntryList(const LDAPEntryList& el);
+
+ /**
+ * Default-Constructor
+ */
+ LDAPEntryList();
+
+ /**
+ * Destructor
+ */
+ ~LDAPEntryList();
+
+ /**
+ * @return The number of entries currently stored in the list.
+ */
+ size_t size() const;
+
+ /**
+ * @return true if there are zero entries currently stored in the list.
+ */
+ bool empty() const;
+
+ /**
+ * @return An iterator pointing to the first element of the list.
+ */
+ const_iterator begin() const;
+
+ /**
+ * @return An iterator pointing to the end of the list
+ */
+ const_iterator end() const;
+
+ /**
+ * Adds an Entry to the end of the list.
+ */
+ void addEntry(const LDAPEntry& e);
+
+ private:
+ ListType m_entries;
+};
+#endif // LDAP_ENTRY_LIST_H
diff --git a/contrib/ldapc++/src/LDAPException.cpp b/contrib/ldapc++/src/LDAPException.cpp
new file mode 100644
index 0000000..1bda281
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPException.cpp
@@ -0,0 +1,96 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include <ldap.h>
+#include "config.h"
+#include "LDAPException.h"
+
+#include "LDAPAsynConnection.h"
+#include "LDAPResult.h"
+
+using namespace std;
+
+LDAPException::LDAPException(int res_code, const string& err_string) throw()
+ : std::runtime_error(err_string)
+{
+ m_res_code=res_code;
+ m_res_string=string(ldap_err2string(res_code));
+ m_err_string=err_string;
+}
+
+LDAPException::LDAPException(const LDAPAsynConnection *lc) throw()
+ : std::runtime_error("")
+{
+ LDAP *l = lc->getSessionHandle();
+ ldap_get_option(l,LDAP_OPT_RESULT_CODE,&m_res_code);
+ const char *res_cstring = ldap_err2string(m_res_code);
+ if ( res_cstring ) {
+ m_res_string = string(res_cstring);
+ } else {
+ m_res_string = "";
+ }
+ const char* err_string;
+
+#ifdef LDAP_OPT_DIAGNOSTIC_MESSAGE
+ ldap_get_option(l,LDAP_OPT_DIAGNOSTIC_MESSAGE ,&err_string);
+#else
+ ldap_get_option(l,LDAP_OPT_ERROR_STRING,&err_string);
+#endif
+ if ( err_string ) {
+ m_err_string = string(err_string);
+ } else {
+ m_err_string = "";
+ }
+}
+
+LDAPException::~LDAPException() throw()
+{
+}
+
+int LDAPException::getResultCode() const throw()
+{
+ return m_res_code;
+}
+
+const string& LDAPException::getResultMsg() const throw()
+{
+ return m_res_string;
+}
+
+const string& LDAPException::getServerMsg() const throw()
+{
+ return m_err_string;
+}
+
+const char* LDAPException::what() const throw()
+{
+ return this->m_res_string.c_str();
+}
+
+ostream& operator << (ostream& s, LDAPException e) throw()
+{
+ s << "Error " << e.m_res_code << ": " << e.m_res_string;
+ if (!e.m_err_string.empty()) {
+ s << endl << "additional info: " << e.m_err_string ;
+ }
+ return s;
+}
+
+
+LDAPReferralException::LDAPReferralException(const LDAPUrlList& urls) throw()
+ : LDAPException(LDAPResult::REFERRAL) , m_urlList(urls)
+{
+}
+
+LDAPReferralException::~LDAPReferralException() throw()
+{
+}
+
+const LDAPUrlList& LDAPReferralException::getUrls() throw()
+{
+ return m_urlList;
+}
+
diff --git a/contrib/ldapc++/src/LDAPException.h b/contrib/ldapc++/src/LDAPException.h
new file mode 100644
index 0000000..391f855
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPException.h
@@ -0,0 +1,107 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#ifndef LDAP_EXCEPTION_H
+#define LDAP_EXCEPTION_H
+
+#include <iostream>
+#include <string>
+#include <stdexcept>
+
+#include <LDAPUrlList.h>
+
+class LDAPAsynConnection;
+
+/**
+ * This class is only thrown as an Exception and used to signalize error
+ * conditions during LDAP-operations
+ */
+class LDAPException : public std::runtime_error
+{
+
+ public :
+ /**
+ * Constructs a LDAPException-object from the parameters
+ * @param res_code A valid LDAP result code.
+ * @param err_string An additional error message for the error
+ * that happened (optional)
+ */
+ LDAPException(int res_code,
+ const std::string& err_string=std::string()) throw();
+
+ /**
+ * Constructs a LDAPException-object from the error state of a
+ * LDAPAsynConnection-object
+ * @param lc A LDAP-Connection for that an error has happened. The
+ * Constructor tries to read its error state.
+ */
+ LDAPException(const LDAPAsynConnection *lc) throw();
+
+ /**
+ * Destructor
+ */
+ virtual ~LDAPException() throw();
+
+ /**
+ * @return The Result code of the object
+ */
+ int getResultCode() const throw();
+
+ /**
+ * @return The error message that is corresponding to the result
+ * code .
+ */
+ const std::string& getResultMsg() const throw();
+
+ /**
+ * @return The additional error message of the error (if it was set)
+ */
+ const std::string& getServerMsg() const throw();
+
+
+ virtual const char* what() const throw();
+
+ /**
+ * This method can be used to dump the data of a LDAPResult-Object.
+ * It is only useful for debugging purposes at the moment
+ */
+ friend std::ostream& operator << (std::ostream &s, LDAPException e) throw();
+
+ private :
+ int m_res_code;
+ std::string m_res_string;
+ std::string m_err_string;
+};
+
+/**
+ * This class extends LDAPException and is used to signalize Referrals
+ * there were received during synchronous LDAP-operations
+ */
+class LDAPReferralException : public LDAPException
+{
+
+ public :
+ /**
+ * Creates an object that is initialized with a list of URLs
+ */
+ LDAPReferralException(const LDAPUrlList& urls) throw();
+
+ /**
+ * Destructor
+ */
+ ~LDAPReferralException() throw();
+
+ /**
+ * @return The List of URLs of the Referral/Search Reference
+ */
+ const LDAPUrlList& getUrls() throw();
+
+ private :
+ LDAPUrlList m_urlList;
+};
+
+#endif //LDAP_EXCEPTION_H
diff --git a/contrib/ldapc++/src/LDAPExtRequest.cpp b/contrib/ldapc++/src/LDAPExtRequest.cpp
new file mode 100644
index 0000000..a07e648
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPExtRequest.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include <ldap.h>
+#include <lber.h>
+
+#include "debug.h"
+
+#include "LDAPExtRequest.h"
+#include "LDAPException.h"
+#include "LDAPResult.h"
+
+#include <cstdlib>
+
+using namespace std;
+
+LDAPExtRequest::LDAPExtRequest(const LDAPExtRequest& req) :
+ LDAPRequest(req){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPExtRequest::LDAPExtRequest(&)" << endl);
+ m_data=req.m_data;
+ m_oid=req.m_oid;
+}
+
+LDAPExtRequest::LDAPExtRequest(const string& oid, const string& data,
+ LDAPAsynConnection *connect, const LDAPConstraints *cons,
+ bool isReferral, const LDAPRequest* parent)
+ : LDAPRequest(connect, cons, isReferral, parent){
+ DEBUG(LDAP_DEBUG_CONSTRUCT, "LDAPExtRequest::LDAPExtRequest()" << endl);
+ DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER,
+ " oid:" << oid << endl);
+ m_oid=oid;
+ m_data=data;
+}
+
+LDAPExtRequest::~LDAPExtRequest(){
+ DEBUG(LDAP_DEBUG_DESTROY, "LDAPExtRequest::~LDAPExtRequest()" << endl);
+}
+
+LDAPMessageQueue* LDAPExtRequest::sendRequest(){
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPExtRequest::sendRequest()" << endl);
+ int msgID=0;
+ BerValue* tmpdata=0;
+ if(m_data != ""){
+ tmpdata=(BerValue*) malloc(sizeof(BerValue));
+ tmpdata->bv_len = m_data.size();
+ tmpdata->bv_val = (char*) malloc(sizeof(char) * (m_data.size()) );
+ m_data.copy(tmpdata->bv_val, string::npos);
+ }
+ LDAPControl** tmpSrvCtrls=m_cons->getSrvCtrlsArray();
+ LDAPControl** tmpClCtrls=m_cons->getClCtrlsArray();
+ int err=ldap_extended_operation(m_connection->getSessionHandle(),
+ m_oid.c_str(), tmpdata, tmpSrvCtrls, tmpClCtrls, &msgID);
+ LDAPControlSet::freeLDAPControlArray(tmpSrvCtrls);
+ LDAPControlSet::freeLDAPControlArray(tmpClCtrls);
+ ber_bvfree(tmpdata);
+ if(err != LDAP_SUCCESS){
+ delete this;
+ throw LDAPException(err);
+ }else{
+ m_msgID=msgID;
+ return new LDAPMessageQueue(this);
+ }
+}
+
+LDAPRequest* LDAPExtRequest::followReferral(LDAPMsg* ref){
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPExtRequest::followReferral()" << endl);
+ LDAPUrlList::const_iterator usedUrl;
+ LDAPUrlList urls = ((LDAPResult*)ref)->getReferralUrls();
+ LDAPAsynConnection* con = 0;
+ try {
+ con = getConnection()->referralConnect(urls,usedUrl,m_cons);
+ } catch(LDAPException e){
+ delete con;
+ return 0;
+ }
+ if(con != 0){
+ return new LDAPExtRequest(m_oid, m_data, con, m_cons,true,this);
+ }
+ return 0;
+}
+
+
diff --git a/contrib/ldapc++/src/LDAPExtRequest.h b/contrib/ldapc++/src/LDAPExtRequest.h
new file mode 100644
index 0000000..6f9c9bc
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPExtRequest.h
@@ -0,0 +1,28 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDAP_EXT_REQUEST_H
+#define LDAP_EXT_REQUEST_H
+
+#include <LDAPRequest.h>
+
+class LDAPExtRequest : LDAPRequest {
+
+ public:
+ LDAPExtRequest(const LDAPExtRequest& req);
+ LDAPExtRequest(const std::string& oid, const std::string& data,
+ LDAPAsynConnection *connect, const LDAPConstraints *cons,
+ bool isReferral=false, const LDAPRequest* parent=0);
+ virtual ~LDAPExtRequest();
+ virtual LDAPMessageQueue* sendRequest();
+ virtual LDAPRequest* followReferral(LDAPMsg* urls);
+
+ private:
+ std::string m_oid;
+ std::string m_data;
+};
+
+#endif // LDAP_EXT_REQUEST_H
diff --git a/contrib/ldapc++/src/LDAPExtResult.cpp b/contrib/ldapc++/src/LDAPExtResult.cpp
new file mode 100644
index 0000000..f3177e8
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPExtResult.cpp
@@ -0,0 +1,49 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "debug.h"
+#include <lber.h>
+#include "LDAPRequest.h"
+#include "LDAPException.h"
+
+#include "LDAPResult.h"
+#include "LDAPExtResult.h"
+
+using namespace std;
+
+LDAPExtResult::LDAPExtResult(const LDAPRequest* req, LDAPMessage* msg) :
+ LDAPResult(req, msg){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPExtResult::LDAPExtResult()" << endl);
+ char* oid = 0;
+ BerValue* data = 0;
+ LDAP* lc = req->getConnection()->getSessionHandle();
+ int err=ldap_parse_extended_result(lc, msg, &oid, &data, 0);
+ if(err != LDAP_SUCCESS){
+ ber_bvfree(data);
+ ldap_memfree(oid);
+ throw LDAPException(err);
+ }else{
+ m_oid=string(oid);
+ ldap_memfree(oid);
+ if(data){
+ m_data=string(data->bv_val, data->bv_len);
+ ber_bvfree(data);
+ }
+ }
+}
+
+LDAPExtResult::~LDAPExtResult(){
+ DEBUG(LDAP_DEBUG_DESTROY,"LDAPExtResult::~LDAPExtResult()" << endl);
+}
+
+const string& LDAPExtResult::getResponseOid() const{
+ return m_oid;
+}
+
+const string& LDAPExtResult::getResponse() const{
+ return m_data;
+}
+
diff --git a/contrib/ldapc++/src/LDAPExtResult.h b/contrib/ldapc++/src/LDAPExtResult.h
new file mode 100644
index 0000000..90e81af
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPExtResult.h
@@ -0,0 +1,50 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDAP_EXT_RESULT_H
+#define LDAP_EXT_RESULT_H
+
+#include <ldap.h>
+
+#include <LDAPResult.h>
+
+class LDAPRequest;
+
+/**
+ * Object of this class are created by the LDAPMsg::create method if
+ * results for an Extended Operation were returned by a LDAP server.
+ */
+class LDAPExtResult : public LDAPResult {
+ public :
+ /**
+ * Constructor that creates an LDAPExtResult-object from the C-API
+ * structures
+ */
+ LDAPExtResult(const LDAPRequest* req, LDAPMessage* msg);
+
+ /**
+ * The Destructor
+ */
+ virtual ~LDAPExtResult();
+
+ /**
+ * @returns The OID of the Extended Operation that has returned
+ * this result.
+ */
+ const std::string& getResponseOid() const;
+
+ /**
+ * @returns If the result contained data this method will return
+ * the data to the caller as a std::string.
+ */
+ const std::string& getResponse() const;
+
+ private:
+ std::string m_oid;
+ std::string m_data;
+};
+
+#endif // LDAP_EXT_RESULT_H
diff --git a/contrib/ldapc++/src/LDAPMessage.cpp b/contrib/ldapc++/src/LDAPMessage.cpp
new file mode 100644
index 0000000..f63212a
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPMessage.cpp
@@ -0,0 +1,72 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#include "LDAPMessage.h"
+
+#include "LDAPResult.h"
+#include "LDAPExtResult.h"
+#include "LDAPSaslBindResult.h"
+#include "LDAPRequest.h"
+#include "LDAPSearchResult.h"
+#include "LDAPSearchReference.h"
+#include "debug.h"
+#include <iostream>
+
+using namespace std;
+
+LDAPMsg::LDAPMsg(LDAPMessage *msg){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPMsg::LDAPMsg()" << endl);
+ msgType=ldap_msgtype(msg);
+ m_hasControls=false;
+}
+
+LDAPMsg::LDAPMsg(int type, int id=0){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPMsg::LDAPMsg()" << endl);
+ msgType = type;
+ msgID = id;
+ m_hasControls=false;
+}
+
+LDAPMsg* LDAPMsg::create(const LDAPRequest *req, LDAPMessage *msg){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPMsg::create()" << endl);
+ switch(ldap_msgtype(msg)){
+ case SEARCH_ENTRY :
+ return new LDAPSearchResult(req,msg);
+ break;
+ case SEARCH_REFERENCE :
+ return new LDAPSearchReference(req, msg);
+ break;
+ case EXTENDED_RESPONSE :
+ return new LDAPExtResult(req,msg);
+ break;
+ case BIND_RESPONSE :
+ return new LDAPSaslBindResult(req,msg);
+ default :
+ return new LDAPResult(req, msg);
+ }
+ return 0;
+}
+
+
+int LDAPMsg::getMessageType(){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPMsg::getMessageType()" << endl);
+ return msgType;
+}
+
+int LDAPMsg::getMsgID(){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPMsg::getMsgID()" << endl);
+ return msgID;
+}
+
+bool LDAPMsg::hasControls() const{
+ return m_hasControls;
+}
+
+const LDAPControlSet& LDAPMsg::getSrvControls() const {
+ return m_srvControls;
+}
+
diff --git a/contrib/ldapc++/src/LDAPMessage.h b/contrib/ldapc++/src/LDAPMessage.h
new file mode 100644
index 0000000..a152d90
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPMessage.h
@@ -0,0 +1,127 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#ifndef LDAP_MSG_H
+#define LDAP_MSG_H
+#include <ldap.h>
+
+#include <LDAPControlSet.h>
+
+class LDAPRequest;
+/**
+ * This class represents any type of LDAP- Message returned
+ * from the server.
+ *
+ * This class is never not instantiated directly. Only
+ * its subclasses are used. The main feature of this class is the
+ * static method create() (see below)
+ */
+class LDAPMsg{
+ public:
+ //public Constants defining the response message types
+ static const int BIND_RESPONSE=LDAP_RES_BIND;
+ static const int SEARCH_ENTRY=LDAP_RES_SEARCH_ENTRY;
+ static const int SEARCH_DONE=LDAP_RES_SEARCH_RESULT;
+ static const int SEARCH_REFERENCE=LDAP_RES_SEARCH_REFERENCE;
+ static const int MODIFY_RESPONSE=LDAP_RES_MODIFY;
+ static const int ADD_RESPONSE=LDAP_RES_ADD;
+ static const int DEL_RESPONSE=LDAP_RES_DELETE;
+ static const int MODDN_RESPONSE=LDAP_RES_MODDN;
+ static const int COMPARE_RESPONSE=LDAP_RES_COMPARE;
+ static const int EXTENDED_RESPONSE=LDAP_RES_EXTENDED;
+ //public Constants defining the request message types
+ static const int BIND_REQUEST=LDAP_REQ_BIND;
+ static const int UNBIND_REQUEST=LDAP_REQ_UNBIND;
+ static const int SEARCH_REQUEST=LDAP_REQ_SEARCH;
+ static const int MODIFY_REQUEST=LDAP_REQ_MODIFY;
+ static const int ADD_REQUEST=LDAP_REQ_ADD;
+ static const int DELETE_REQUEST=LDAP_REQ_DELETE;
+ static const int MODRDN_REQUEST=LDAP_REQ_MODRDN;
+ static const int COMPARE_REQUEST=LDAP_REQ_COMPARE;
+ static const int ABANDON_REQUEST=LDAP_REQ_ABANDON;
+ static const int EXTENDED_REQUEST=LDAP_REQ_EXTENDED;
+
+ /**
+ * The destructor has no implementation, because this is an abstract
+ * class.
+ */
+ virtual ~LDAPMsg() {}
+
+ /**
+ * This method is used by the library to parse the results returned
+ * by the C-API.
+ *
+ * Based on msgtype-Value of the *msg-Parameter this method creates
+ * an Object of one of the subtypes of LDAPMsg (e.g. LDAPSearchResult
+ * or LDAPResult) that represents the same Message as the
+ * *msg-Parameter. *msg is e.g. a Message returned by the C-API's
+ * ldap_result call.
+ * @param req The LDAPRequest-object this result message is
+ * associated with.
+ * @param msg The LDAPMessage-structure from the C-API that
+ * contains the LDAP-message to parse.
+ * @return An Object of one of the subtypes of this class. It
+ * contains the parsed LDAP-message.
+ */
+ static LDAPMsg* create(const LDAPRequest *req, LDAPMessage *msg);
+
+ /**
+ * @returns The Type of message that this object contains. Possible
+ * values are: <BR>
+ * BIND_RESPONSE <BR>
+ * SEARCH_ENTRY <BR>
+ * SEARCH_DONE <BR>
+ * SEARCH_REFERENCE <BR>
+ * MODIFY_RESPONSE <BR>
+ * ADD_RESPONSE <BR>
+ * DEL_RESPONSE <BR>
+ * MODDN_RESPONSE <BR>
+ * COMPARE_RESPONSE <BR>
+ * EXTENDED_RESPONSE <BR>
+ */
+ int getMessageType();
+
+ /**
+ * @returns The message-ID that the C-API return for the
+ * Result-message.
+ */
+ int getMsgID();
+
+ /**
+ * @returns If any Control was sent back by the server this method
+ * returns true. Otherwise false is returned.
+ */
+ bool hasControls() const;
+
+ /**
+ * @returns Server controls that were sent back by the server.
+ * @note This feature is not test well yet.
+ */
+ const LDAPControlSet& getSrvControls() const;
+
+ protected:
+ /**
+ * This constructor make a copy of a LDAPMsg-pointer. The object
+ * itself (no the pointer) is copied.
+ * Only for internal use.
+ */
+ LDAPMsg(LDAPMessage *msg);
+ LDAPMsg(int msgType, int msgID);
+
+ /**
+ * This attribute stores Server-Control that were returned with the
+ * message.
+ */
+ LDAPControlSet m_srvControls;
+
+ bool m_hasControls;
+
+ private:
+ int msgType;
+ int msgID;
+};
+#endif //ifndef LDAP_MSG_H
diff --git a/contrib/ldapc++/src/LDAPMessageQueue.cpp b/contrib/ldapc++/src/LDAPMessageQueue.cpp
new file mode 100644
index 0000000..0cbc0d1
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPMessageQueue.cpp
@@ -0,0 +1,171 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#include "config.h"
+#include "debug.h"
+#include "LDAPMessageQueue.h"
+#include "LDAPRequest.h"
+#include "LDAPResult.h"
+#include "LDAPSearchReference.h"
+#include "LDAPSearchRequest.h"
+#include "LDAPUrl.h"
+#include "LDAPUrlList.h"
+#include "LDAPException.h"
+
+using namespace std;
+
+// TODO: How to handle unsolicited notifications, like notice of
+// disconnection
+
+LDAPMessageQueue::LDAPMessageQueue(LDAPRequest *req){
+ DEBUG(LDAP_DEBUG_CONSTRUCT, "LDAPMessageQueue::LDAPMessageQueue()" << endl);
+ m_activeReq.push(req);
+ m_issuedReq.push_back(req);
+}
+
+LDAPMessageQueue::~LDAPMessageQueue(){
+ DEBUG(LDAP_DEBUG_DESTROY, "LDAPMessageQueue::~LDAPMessageQueue()" << endl);
+ for(LDAPRequestList::iterator i=m_issuedReq.begin();
+ i != m_issuedReq.end(); i++){
+ delete *i;
+ }
+ m_issuedReq.clear();
+}
+
+
+LDAPMsg *LDAPMessageQueue::getNext(){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPMessageQueue::getNext()" << endl);
+
+ if ( m_activeReq.empty() ) {
+ return 0;
+ }
+
+ LDAPRequest *req=m_activeReq.top();
+ LDAPMsg *ret=0;
+
+ try{
+ ret = req->getNextMessage();
+ }catch(LDAPException e){
+ //do some clean up
+ m_activeReq.pop();
+ throw;
+ }
+
+ const LDAPConstraints *constr=req->getConstraints();
+ switch (ret->getMessageType()) {
+ case LDAPMsg::SEARCH_REFERENCE :
+ if (constr->getReferralChase() ){
+ //throws Exception (limit Exceeded)
+ LDAPRequest *refReq=chaseReferral(ret);
+ if(refReq != 0){
+ m_activeReq.push(refReq);
+ m_issuedReq.push_back(refReq);
+ delete ret;
+ return getNext();
+ }
+ }
+ return ret;
+ break;
+ case LDAPMsg::SEARCH_ENTRY :
+ return ret;
+ break;
+ case LDAPMsg::SEARCH_DONE :
+ if(req->isReferral()){
+ req->unbind();
+ }
+ switch ( ((LDAPResult*)ret)->getResultCode()) {
+ case LDAPResult::REFERRAL :
+ if(constr->getReferralChase()){
+ //throws Exception (limit Exceeded)
+ LDAPRequest *refReq=chaseReferral(ret);
+ if(refReq != 0){
+ m_activeReq.pop();
+ m_activeReq.push(refReq);
+ m_issuedReq.push_back(refReq);
+ delete ret;
+ return getNext();
+ }
+ }
+ return ret;
+ break;
+ case LDAPResult::SUCCESS :
+ if(req->isReferral()){
+ delete ret;
+ m_activeReq.pop();
+ return getNext();
+ }else{
+ m_activeReq.pop();
+ return ret;
+ }
+ break;
+ default:
+ m_activeReq.pop();
+ return ret;
+ break;
+ }
+ break;
+ //must be some kind of LDAPResultMessage
+ default:
+ if(req->isReferral()){
+ req->unbind();
+ }
+ LDAPResult* res_p=(LDAPResult*)ret;
+ switch (res_p->getResultCode()) {
+ case LDAPResult::REFERRAL :
+ if(constr->getReferralChase()){
+ //throws Exception (limit Exceeded)
+ LDAPRequest *refReq=chaseReferral(ret);
+ if(refReq != 0){
+ m_activeReq.pop();
+ m_activeReq.push(refReq);
+ m_issuedReq.push_back(refReq);
+ delete ret;
+ return getNext();
+ }
+ }
+ return ret;
+ break;
+ default:
+ m_activeReq.pop();
+ return ret;
+ }
+ break;
+ }
+}
+
+// TODO Maybe moved to LDAPRequest::followReferral seems more reasonable
+//there
+LDAPRequest* LDAPMessageQueue::chaseReferral(LDAPMsg* ref){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPMessageQueue::chaseReferral()" << endl);
+ LDAPRequest *req=m_activeReq.top();
+ LDAPRequest *refReq=req->followReferral(ref);
+ if(refReq !=0){
+ if(refReq->getConstraints()->getHopLimit() < refReq->getHopCount()){
+ delete(refReq);
+ throw LDAPException(LDAP_REFERRAL_LIMIT_EXCEEDED);
+ }
+ if(refReq->isCycle()){
+ delete(refReq);
+ throw LDAPException(LDAP_CLIENT_LOOP);
+ }
+ try {
+ refReq->sendRequest();
+ return refReq;
+ }catch (LDAPException e){
+ DEBUG(LDAP_DEBUG_TRACE," caught exception" << endl);
+ return 0;
+ }
+ }else{
+ return 0;
+ }
+}
+
+LDAPRequestStack* LDAPMessageQueue::getRequestStack(){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPMessageQueue::getRequestStack()" << endl);
+ return &m_activeReq;
+}
+
diff --git a/contrib/ldapc++/src/LDAPMessageQueue.h b/contrib/ldapc++/src/LDAPMessageQueue.h
new file mode 100644
index 0000000..9e42d80
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPMessageQueue.h
@@ -0,0 +1,72 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#ifndef LDAP_MESSAGE_QUEUE_H
+#define LDAP_MESSAGE_QUEUE_H
+
+#include <stack>
+
+#include <LDAPUrlList.h>
+#include <LDAPMessage.h>
+
+class LDAPAsynConnection;
+class LDAPRequest;
+class LDAPSearchRequest;
+class LDAPUrl;
+typedef std::stack<LDAPRequest*> LDAPRequestStack;
+typedef std::list<LDAPRequest*> LDAPRequestList;
+
+/**
+ * This class is created for the asynchronous LDAP-operations. And can be
+ * used by the client to retrieve the results of an operation.
+ */
+class LDAPMessageQueue{
+ public :
+
+ /**
+ * This creates a new LDAPMessageQueue. For a LDAP-request
+ *
+ * @param conn The Request for that is queue can be used to get
+ * the results.
+ */
+ LDAPMessageQueue(LDAPRequest *conn);
+ /**
+ * Destructor
+ */
+ ~LDAPMessageQueue();
+
+ /**
+ * This method reads exactly one Message from the results of a
+ * Request.
+ * @throws LDAPException
+ * @return A pointer to an object of one of the classes that were
+ * derived from LDAPMsg. The user has to cast it to the
+ * correct type (e.g. LDAPResult or LDAPSearchResult)
+ */
+ LDAPMsg* getNext();
+
+ /**
+ * For internat use only.
+ *
+ * The method is used to start the automatic referral chasing
+ */
+ LDAPRequest* chaseReferral(LDAPMsg* ref);
+
+ /**
+ * For internal use only
+ *
+ * The referral chasing algorithm needs this method to see the
+ * currently active requests.
+ */
+ LDAPRequestStack* getRequestStack();
+
+ private :
+ LDAPRequestStack m_activeReq;
+ LDAPRequestList m_issuedReq;
+};
+#endif //ifndef LDAP_MESSAGE_QUEUE_H
+
diff --git a/contrib/ldapc++/src/LDAPModDNRequest.cpp b/contrib/ldapc++/src/LDAPModDNRequest.cpp
new file mode 100644
index 0000000..c81484a
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPModDNRequest.cpp
@@ -0,0 +1,88 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include <ldap.h>
+
+#include "debug.h"
+
+#include "LDAPModDNRequest.h"
+#include "LDAPException.h"
+#include "LDAPResult.h"
+#include "LDAPUrlList.h"
+
+using namespace std;
+
+LDAPModDNRequest::LDAPModDNRequest(const LDAPModDNRequest& req) :
+ LDAPRequest(req){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,
+ "LDAPModDNRequest::LDAPModDNRequest(&)" << endl);
+ m_dn = req.m_dn;
+ m_newRDN = req.m_newRDN;
+ m_newParentDN = req.m_newParentDN;
+ m_deleteOld = req.m_deleteOld;
+}
+
+LDAPModDNRequest::LDAPModDNRequest(const string& dn, const string& newRDN,
+ bool deleteOld, const string& newParentDN,
+ LDAPAsynConnection *connect,
+ const LDAPConstraints *cons, bool isReferral,
+ const LDAPRequest* parent):
+ LDAPRequest(connect, cons, isReferral, parent){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,
+ "LDAPModDNRequest::LDAPModDNRequest(&)" << endl);
+ DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER,
+ " dn:" << dn << endl << " newRDN:" << newRDN << endl
+ << " deleteOld:" << deleteOld << endl
+ << " newParent:" << newParentDN << endl);
+ m_dn = dn;
+ m_newRDN = newRDN;
+ m_newParentDN = newParentDN;
+ m_deleteOld=deleteOld;
+}
+
+LDAPModDNRequest::~LDAPModDNRequest(){
+ DEBUG(LDAP_DEBUG_DESTROY, "LDAPModDNRequest::~LDAPModDNRequest()" << endl);
+}
+
+LDAPMessageQueue* LDAPModDNRequest::sendRequest(){
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPModDNRequest::sendRequest()" << endl);
+ int msg_id;
+ const char* newRDN = (m_newRDN == "" ? 0 :m_newRDN.c_str());
+ const char* newParentDN = (m_newParentDN == "" ?
+ 0 :
+ m_newParentDN.c_str());
+ LDAPControl** tmpSrvCtrls=m_cons->getSrvCtrlsArray();
+ LDAPControl** tmpClCtrls=m_cons->getClCtrlsArray();
+ int err=ldap_rename(m_connection->getSessionHandle(),m_dn.c_str(),newRDN,
+ newParentDN,m_deleteOld ? 1 : 0, tmpSrvCtrls, tmpClCtrls,&msg_id);
+ LDAPControlSet::freeLDAPControlArray(tmpSrvCtrls);
+ LDAPControlSet::freeLDAPControlArray(tmpClCtrls);
+ if(err!=LDAP_SUCCESS){
+ throw LDAPException(err);
+ }else{
+ m_msgID=msg_id;
+ return new LDAPMessageQueue(this);
+ }
+}
+
+LDAPRequest* LDAPModDNRequest::followReferral(LDAPMsg* ref){
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPModifyRequest::followReferral()" << endl);
+ LDAPUrlList::const_iterator usedUrl;
+ LDAPUrlList urls = ((LDAPResult*)ref)->getReferralUrls();
+ LDAPAsynConnection* con = 0;
+ try {
+ con = getConnection()->referralConnect(urls,usedUrl,m_cons);
+ } catch(LDAPException e){
+ delete con;
+ return 0;
+ }
+ if(con != 0){
+ return new LDAPModDNRequest(m_dn, m_newRDN, m_deleteOld, m_newParentDN,
+ con, m_cons,true,this);
+ }
+ return 0;
+}
+
diff --git a/contrib/ldapc++/src/LDAPModDNRequest.h b/contrib/ldapc++/src/LDAPModDNRequest.h
new file mode 100644
index 0000000..d45c6a2
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPModDNRequest.h
@@ -0,0 +1,33 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDAP_MOD_DN_REQUEST_H
+#define LDAP_MOD_DN_REQUEST_H
+
+#include <LDAPRequest.h>
+
+class LDAPModDNRequest : LDAPRequest {
+
+ public:
+ LDAPModDNRequest(const LDAPModDNRequest& req);
+ LDAPModDNRequest(const std::string& dn, const std::string& newRDN,
+ bool deleteOld, const std::string& newParentDN,
+ LDAPAsynConnection *connect, const LDAPConstraints *cons,
+ bool isReferral=false, const LDAPRequest* parent=0);
+ virtual ~LDAPModDNRequest();
+
+ virtual LDAPMessageQueue* sendRequest();
+ virtual LDAPRequest* followReferral(LDAPMsg* urls);
+
+ private:
+ std::string m_dn;
+ std::string m_newRDN;
+ std::string m_newParentDN;
+ bool m_deleteOld;
+};
+
+#endif // LDAP_MOD_DN_REQUEST_H
+
diff --git a/contrib/ldapc++/src/LDAPModList.cpp b/contrib/ldapc++/src/LDAPModList.cpp
new file mode 100644
index 0000000..1ce248a
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPModList.cpp
@@ -0,0 +1,48 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#include "LDAPModList.h"
+#include "debug.h"
+
+#include <cstdlib>
+
+using namespace std;
+
+LDAPModList::LDAPModList(){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPModList::LDAPModList()" << endl);
+}
+
+LDAPModList::LDAPModList(const LDAPModList& ml){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPModList::LDAPModList(&)" << endl);
+ m_modList=ml.m_modList;
+}
+
+void LDAPModList::addModification(const LDAPModification &mod){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPModList::addModification()" << endl);
+ m_modList.push_back(mod);
+}
+
+LDAPMod** LDAPModList::toLDAPModArray(){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPModList::toLDAPModArray()" << endl);
+ LDAPMod **ret = (LDAPMod**) malloc(
+ (m_modList.size()+1) * sizeof(LDAPMod*));
+ ret[m_modList.size()]=0;
+ LDAPModList::ListType::const_iterator i;
+ int j=0;
+ for (i=m_modList.begin(); i != m_modList.end(); i++ , j++){
+ ret[j]=i->toLDAPMod();
+ }
+ return ret;
+}
+
+bool LDAPModList::empty() const {
+ return m_modList.empty();
+}
+
+unsigned int LDAPModList::size() const {
+ return m_modList.size();
+}
diff --git a/contrib/ldapc++/src/LDAPModList.h b/contrib/ldapc++/src/LDAPModList.h
new file mode 100644
index 0000000..5b0323c
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPModList.h
@@ -0,0 +1,59 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#ifndef LDAP_MOD_LIST_H
+#define LDAP_MOD_LIST_H
+
+#include <ldap.h>
+#include <list>
+#include <LDAPModification.h>
+
+/**
+ * This container class is used to store multiple LDAPModification-objects.
+ */
+class LDAPModList{
+ typedef std::list<LDAPModification> ListType;
+
+ public :
+ /**
+ * Constructs an empty list.
+ */
+ LDAPModList();
+
+ /**
+ * Copy-constructor
+ */
+ LDAPModList(const LDAPModList&);
+
+ /**
+ * Adds one element to the end of the list.
+ * @param mod The LDAPModification to add to the std::list.
+ */
+ void addModification(const LDAPModification &mod);
+
+ /**
+ * Translates the list to a 0-terminated array of
+ * LDAPMod-structures as needed by the C-API
+ */
+ LDAPMod** toLDAPModArray();
+
+ /**
+ * @returns true, if the ModList contains no Operations
+ */
+ bool empty() const;
+
+ /**
+ * @returns number of Modifications in the ModList
+ */
+ unsigned int size() const;
+
+ private :
+ ListType m_modList;
+};
+#endif //LDAP_MOD_LIST_H
+
+
diff --git a/contrib/ldapc++/src/LDAPModification.cpp b/contrib/ldapc++/src/LDAPModification.cpp
new file mode 100644
index 0000000..f10a792
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPModification.cpp
@@ -0,0 +1,48 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#include "LDAPModification.h"
+#include "debug.h"
+
+using namespace std;
+
+LDAPModification::LDAPModification(const LDAPAttribute& attr, mod_op op){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPModification::LDAPModification()" << endl);
+ DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER,
+ " attr:" << attr << endl);
+ m_attr = attr;
+ m_mod_op = op;
+}
+
+LDAPMod* LDAPModification::toLDAPMod() const {
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPModification::toLDAPMod()" << endl);
+ LDAPMod* ret=m_attr.toLDAPMod();
+
+ //The mod_op value of the LDAPMod-struct needs to be ORed with the right
+ // LDAP_MOD_* constant to preserve the BIN-flag (see CAPI-draft for
+ // explanation of the LDAPMod struct)
+ switch (m_mod_op){
+ case OP_ADD :
+ ret->mod_op |= LDAP_MOD_ADD;
+ break;
+ case OP_DELETE :
+ ret->mod_op |= LDAP_MOD_DELETE;
+ break;
+ case OP_REPLACE :
+ ret->mod_op |= LDAP_MOD_REPLACE;
+ break;
+ }
+ return ret;
+}
+
+const LDAPAttribute* LDAPModification::getAttribute() const {
+ return &m_attr;
+}
+
+LDAPModification::mod_op LDAPModification::getOperation() const {
+ return m_mod_op;
+}
diff --git a/contrib/ldapc++/src/LDAPModification.h b/contrib/ldapc++/src/LDAPModification.h
new file mode 100644
index 0000000..25f5be4
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPModification.h
@@ -0,0 +1,30 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#ifndef LDAP_MODIFICATION_H
+#define LDAP_MODIFICATION_H
+
+#include <ldap.h>
+#include <LDAPAttribute.h>
+
+class LDAPModification{
+ public:
+ enum mod_op {OP_ADD, OP_DELETE, OP_REPLACE};
+
+ LDAPModification(const LDAPAttribute& attr, mod_op op);
+ LDAPMod *toLDAPMod() const;
+
+ const LDAPAttribute* getAttribute() const;
+ mod_op getOperation() const;
+
+ private:
+ LDAPAttribute m_attr;
+ mod_op m_mod_op;
+
+};
+#endif //LDAP_MODIFICATION_H
+
diff --git a/contrib/ldapc++/src/LDAPModifyRequest.cpp b/contrib/ldapc++/src/LDAPModifyRequest.cpp
new file mode 100644
index 0000000..a7ca55a
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPModifyRequest.cpp
@@ -0,0 +1,81 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include <ldap.h>
+
+#include "debug.h"
+
+#include "LDAPModifyRequest.h"
+#include "LDAPException.h"
+#include "LDAPMessageQueue.h"
+#include "LDAPResult.h"
+
+using namespace std;
+
+LDAPModifyRequest::LDAPModifyRequest(const LDAPModifyRequest& req) :
+ LDAPRequest(req){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,
+ "LDAPModifyRequest::LDAPModifyRequest(&)" << endl);
+ m_modList = new LDAPModList(*(req.m_modList));
+ m_dn = req.m_dn;
+}
+
+LDAPModifyRequest::LDAPModifyRequest(const string& dn,
+ const LDAPModList *modList, LDAPAsynConnection *connect,
+ const LDAPConstraints *cons, bool isReferral,
+ const LDAPRequest* parent) :
+ LDAPRequest(connect, cons, isReferral, parent){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,
+ "LDAPModifyRequest::LDAPModifyRequest(&)" << endl);
+ DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER,
+ " dn:" << dn << endl);
+ m_dn = dn;
+ m_modList = new LDAPModList(*modList);
+}
+
+LDAPModifyRequest::~LDAPModifyRequest(){
+ DEBUG(LDAP_DEBUG_DESTROY,
+ "LDAPModifyRequest::~LDAPModifyRequest()" << endl);
+ delete m_modList;
+}
+
+LDAPMessageQueue* LDAPModifyRequest::sendRequest(){
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPModifyRequest::sendRequest()" << endl);
+ int msgID=0;
+ LDAPControl** tmpSrvCtrls=m_cons->getSrvCtrlsArray();
+ LDAPControl** tmpClCtrls=m_cons->getClCtrlsArray();
+ LDAPMod** tmpMods=m_modList->toLDAPModArray();
+ int err=ldap_modify_ext(m_connection->getSessionHandle(),m_dn.c_str(),
+ tmpMods, tmpSrvCtrls, tmpClCtrls,&msgID);
+ LDAPControlSet::freeLDAPControlArray(tmpSrvCtrls);
+ LDAPControlSet::freeLDAPControlArray(tmpClCtrls);
+ ldap_mods_free(tmpMods,1);
+ if(err != LDAP_SUCCESS){
+ throw LDAPException(err);
+ }else{
+ m_msgID=msgID;
+ return new LDAPMessageQueue(this);
+ }
+}
+
+LDAPRequest* LDAPModifyRequest::followReferral(LDAPMsg* ref){
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPModifyRequest::followReferral()" << endl);
+ LDAPUrlList::const_iterator usedUrl;
+ LDAPUrlList urls = ((LDAPResult*)ref)->getReferralUrls();
+ LDAPAsynConnection* con = 0;
+ try {
+ con = getConnection()->referralConnect(urls,usedUrl,m_cons);
+ } catch(LDAPException e){
+ delete con;
+ return 0;
+ }
+ if(con != 0){
+ return new LDAPModifyRequest(m_dn, m_modList, con, m_cons,true,this);
+ }
+ return 0;
+}
+
+
diff --git a/contrib/ldapc++/src/LDAPModifyRequest.h b/contrib/ldapc++/src/LDAPModifyRequest.h
new file mode 100644
index 0000000..a1eccdd
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPModifyRequest.h
@@ -0,0 +1,30 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDAP_MODIFY_REQUEST_H
+#define LDAP_MODIFY_REQUEST_H
+
+#include <LDAPRequest.h>
+
+class LDAPMessageQueue;
+
+class LDAPModifyRequest : LDAPRequest {
+ private :
+ std::string m_dn;
+ LDAPModList *m_modList;
+
+ public:
+ LDAPModifyRequest(const LDAPModifyRequest& mod);
+ LDAPModifyRequest(const std::string& dn, const LDAPModList *modList,
+ LDAPAsynConnection *connect, const LDAPConstraints *cons,
+ bool isReferral=false, const LDAPRequest* req=0);
+ virtual ~LDAPModifyRequest();
+ virtual LDAPMessageQueue* sendRequest();
+ virtual LDAPRequest* followReferral(LDAPMsg* refs);
+};
+
+#endif // LDAP_MODIFY_REQUEST_H
+
diff --git a/contrib/ldapc++/src/LDAPObjClass.cpp b/contrib/ldapc++/src/LDAPObjClass.cpp
new file mode 100644
index 0000000..e3899f1
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPObjClass.cpp
@@ -0,0 +1,130 @@
+// $OpenLDAP$
+/*
+ * Copyright 2003-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "debug.h"
+#include "LDAPObjClass.h"
+
+
+LDAPObjClass::LDAPObjClass(){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,
+ "LDAPObjClass::LDAPObjClass( )" << endl);
+
+ oid = string ();
+ desc = string ();
+ names = StringList ();
+ must = StringList();
+ may = StringList();
+ sup = StringList();
+}
+
+LDAPObjClass::LDAPObjClass (const LDAPObjClass &oc){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,
+ "LDAPObjClass::LDAPObjClass( )" << endl);
+
+ oid = oc.oid;
+ desc = oc.desc;
+ names = oc.names;
+ must = oc.must;
+ may = oc.may;
+ kind = oc.kind;
+ sup = oc.sup;
+}
+
+LDAPObjClass::LDAPObjClass (string oc_item, int flags ) {
+
+ DEBUG(LDAP_DEBUG_CONSTRUCT,
+ "LDAPObjClass::LDAPObjClass( )" << endl);
+
+ LDAPObjectClass *o;
+ int ret;
+ const char *errp;
+ o = ldap_str2objectclass ( oc_item.c_str(), &ret, &errp, flags );
+
+ if (o) {
+ this->setNames (o->oc_names);
+ this->setDesc (o->oc_desc);
+ this->setOid (o->oc_oid);
+ this->setKind (o->oc_kind);
+ this->setMust (o->oc_at_oids_must);
+ this->setMay (o->oc_at_oids_may);
+ this->setSup (o->oc_sup_oids);
+ }
+ // else? -> error
+}
+
+LDAPObjClass::~LDAPObjClass() {
+ DEBUG(LDAP_DEBUG_DESTROY,"LDAPObjClass::~LDAPObjClass()" << endl);
+}
+
+void LDAPObjClass::setKind (int oc_kind) {
+ kind = oc_kind;
+}
+
+void LDAPObjClass::setNames (char **oc_names) {
+ names = StringList (oc_names);
+}
+
+void LDAPObjClass::setMust (char **oc_must) {
+ must = StringList (oc_must);
+}
+
+void LDAPObjClass::setMay (char **oc_may) {
+ may = StringList (oc_may);
+}
+
+void LDAPObjClass::setSup (char **oc_sup) {
+ sup = StringList (oc_sup);
+}
+
+void LDAPObjClass::setDesc (char *oc_desc) {
+ desc = string ();
+ if (oc_desc)
+ desc = oc_desc;
+}
+
+void LDAPObjClass::setOid (char *oc_oid) {
+ oid = string ();
+ if (oc_oid)
+ oid = oc_oid;
+}
+
+string LDAPObjClass::getOid() const {
+ return oid;
+}
+
+string LDAPObjClass::getDesc() const {
+ return desc;
+}
+
+StringList LDAPObjClass::getNames() const {
+ return names;
+}
+
+StringList LDAPObjClass::getMust() const {
+ return must;
+}
+
+StringList LDAPObjClass::getMay() const {
+ return may;
+}
+
+StringList LDAPObjClass::getSup() const {
+ return sup;
+}
+
+string LDAPObjClass::getName() const {
+
+ if (names.empty())
+ return "";
+ else
+ return *(names.begin());
+}
+
+int LDAPObjClass::getKind() const {
+ return kind;
+}
+
+
diff --git a/contrib/ldapc++/src/LDAPObjClass.h b/contrib/ldapc++/src/LDAPObjClass.h
new file mode 100644
index 0000000..e9c7b42
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPObjClass.h
@@ -0,0 +1,104 @@
+// $OpenLDAP$
+/*
+ * Copyright 2003-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDAP_OBJCLASS_H
+#define LDAP_OBJCLASS_H
+
+#include <ldap_schema.h>
+#include <string>
+
+#include "StringList.h"
+
+using namespace std;
+
+/**
+ * Represents the Object Class (from LDAP schema)
+ */
+class LDAPObjClass{
+ private :
+ StringList names, must, may, sup;
+ string desc, oid;
+ int kind;
+
+ public :
+
+ /**
+ * Constructs an empty object.
+ */
+ LDAPObjClass();
+
+ /**
+ * Copy constructor
+ */
+ LDAPObjClass( const LDAPObjClass& oc );
+
+ /**
+ * Constructs new object and fills the data structure by parsing the
+ * argument.
+ * @param oc_item description of object class is string returned
+ * by the search command. It is in the form:
+ * "( SuSE.YaST.OC:5 NAME 'userTemplate' SUP objectTemplate STRUCTURAL
+ * DESC 'User object template' MUST ( cn ) MAY ( secondaryGroup ))"
+ */
+ LDAPObjClass (string oc_item, int flags = LDAP_SCHEMA_ALLOW_NO_OID |
+ LDAP_SCHEMA_ALLOW_QUOTED);
+
+ /**
+ * Destructor
+ */
+ virtual ~LDAPObjClass();
+
+ /**
+ * Returns object class description
+ */
+ string getDesc() const;
+
+ /**
+ * Returns object class oid
+ */
+ string getOid() const;
+
+ /**
+ * Returns object class name (first one if there are more of them)
+ */
+ string getName() const;
+
+ /**
+ * Returns object class kind: 0=ABSTRACT, 1=STRUCTURAL, 2=AUXILIARY
+ */
+ int getKind() const;
+
+ /**
+ * Returns all object class names
+ */
+ StringList getNames() const;
+
+ /**
+ * Returns list of required attributes
+ */
+ StringList getMust() const;
+
+ /**
+ * Returns list of allowed (and not required) attributes
+ */
+ StringList getMay() const;
+
+ /**
+ * Returns list of the OIDs of the superior ObjectClasses
+ */
+ StringList getSup() const;
+
+ void setNames (char **oc_names);
+ void setMay (char **oc_may);
+ void setMust (char **oc_must);
+ void setDesc (char *oc_desc);
+ void setOid (char *oc_oid);
+ void setKind (int oc_kind);
+ void setSup (char **oc_sup);
+
+};
+
+#endif // LDAP_OBJCLASS_H
diff --git a/contrib/ldapc++/src/LDAPRebind.cpp b/contrib/ldapc++/src/LDAPRebind.cpp
new file mode 100644
index 0000000..cde229a
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPRebind.cpp
@@ -0,0 +1,9 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "LDAPRebind.h"
+
+
diff --git a/contrib/ldapc++/src/LDAPRebind.h b/contrib/ldapc++/src/LDAPRebind.h
new file mode 100644
index 0000000..9fe7737
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPRebind.h
@@ -0,0 +1,27 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDAP_REBIND_H
+#define LDAP_REBIND_H
+
+#include <string>
+#include <LDAPRebindAuth.h>
+
+/**
+ * Just an abstract class to provide a mechanism for rebind to another
+ * server when chasing referrals. Clients have to implement a class
+ * derived from this. To use authentication other than anonymous for
+ * referral chasing
+ */
+
+class LDAPRebind{
+ public:
+ virtual ~LDAPRebind() {}
+ virtual LDAPRebindAuth* getRebindAuth(const std::string& hostname,
+ int port) const = 0;
+};
+#endif //LDAP_REBIND_H
+
diff --git a/contrib/ldapc++/src/LDAPRebindAuth.cpp b/contrib/ldapc++/src/LDAPRebindAuth.cpp
new file mode 100644
index 0000000..80c726b
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPRebindAuth.cpp
@@ -0,0 +1,40 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include <iostream>
+
+#include "LDAPRebindAuth.h"
+#include "debug.h"
+
+using namespace std;
+
+LDAPRebindAuth::LDAPRebindAuth(const string& dn, const string& pwd){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPRebindAuth::LDAPRebindAuth()" << endl);
+ DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER," dn:" << dn << endl
+ << " pwd:" << pwd << endl);
+ m_dn=dn;
+ m_password=pwd;
+}
+
+LDAPRebindAuth::LDAPRebindAuth(const LDAPRebindAuth& lra){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPRebindAuth::LDAPRebindAuth(&)" << endl);
+ m_dn=lra.m_dn;
+ m_password=lra.m_password;
+}
+
+LDAPRebindAuth::~LDAPRebindAuth(){
+ DEBUG(LDAP_DEBUG_DESTROY,"LDAPRebindAuth::~LDAPRebindAuth()" << endl);
+}
+
+const string& LDAPRebindAuth::getDN() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPRebindAuth::getDN()" << endl);
+ return m_dn;
+}
+
+const string& LDAPRebindAuth::getPassword() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPRebindAuth::getPassword()" << endl);
+ return m_password;
+}
diff --git a/contrib/ldapc++/src/LDAPRebindAuth.h b/contrib/ldapc++/src/LDAPRebindAuth.h
new file mode 100644
index 0000000..60c12ac
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPRebindAuth.h
@@ -0,0 +1,55 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDAP_REBIND_AUTH_H
+#define LDAP_REBIND_AUTH_H
+
+#include<string>
+
+/**
+ * This class represent Authentication information for the case that the
+ * library is chasing referrals.
+ *
+ * The LDAPRebind::getRebindAuth() method returns an object of this type.
+ * And the library uses it to authentication to the destination server of a
+ * referral.
+ * @note currently only SIMPLE authentication is supported by the library
+ */
+class LDAPRebindAuth{
+ public:
+ /**
+ * @param dn The DN that should be used for the authentication
+ * @param pwd The password that belongs to the DN
+ */
+ LDAPRebindAuth(const std::string& dn="", const std::string& pwd="");
+
+ /**
+ * Copy-constructor
+ */
+ LDAPRebindAuth(const LDAPRebindAuth& lra);
+
+ /**
+ * Destructor
+ */
+ virtual ~LDAPRebindAuth();
+
+ /**
+ * @return The DN that was set in the constructor
+ */
+ const std::string& getDN() const;
+
+ /**
+ * @return The password that was set in the constructor
+ */
+ const std::string& getPassword() const;
+
+ private:
+ std::string m_dn;
+ std::string m_password;
+};
+
+#endif //LDAP_REBIND_AUTH_H
+
diff --git a/contrib/ldapc++/src/LDAPReferenceList.cpp b/contrib/ldapc++/src/LDAPReferenceList.cpp
new file mode 100644
index 0000000..53cef03
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPReferenceList.cpp
@@ -0,0 +1,40 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#include "LDAPReferenceList.h"
+#include "LDAPSearchReference.h"
+
+LDAPReferenceList::LDAPReferenceList(){
+}
+
+LDAPReferenceList::LDAPReferenceList(const LDAPReferenceList& e){
+ m_refs = e.m_refs;
+}
+
+LDAPReferenceList::~LDAPReferenceList(){
+}
+
+size_t LDAPReferenceList::size() const{
+ return m_refs.size();
+}
+
+bool LDAPReferenceList::empty() const{
+ return m_refs.empty();
+}
+
+LDAPReferenceList::const_iterator LDAPReferenceList::begin() const{
+ return m_refs.begin();
+}
+
+LDAPReferenceList::const_iterator LDAPReferenceList::end() const{
+ return m_refs.end();
+}
+
+void LDAPReferenceList::addReference(const LDAPSearchReference& e){
+ m_refs.push_back(e);
+}
+
diff --git a/contrib/ldapc++/src/LDAPReferenceList.h b/contrib/ldapc++/src/LDAPReferenceList.h
new file mode 100644
index 0000000..f4f60f3
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPReferenceList.h
@@ -0,0 +1,74 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDAP_REFERENCE_LIST_H
+#define LDAP_REFERENCE_LIST_H
+
+#include <cstdio>
+#include <list>
+
+class LDAPSearchReference;
+
+/**
+ * Container class for storing a list of Search References
+ *
+ * Used internally only by LDAPSearchResults
+ */
+class LDAPReferenceList{
+ typedef std::list<LDAPSearchReference> ListType;
+
+ public:
+ typedef ListType::const_iterator const_iterator;
+
+ /**
+ * Constructs an empty list.
+ */
+ LDAPReferenceList();
+
+ /**
+ * Copy-constructor
+ */
+ LDAPReferenceList(const LDAPReferenceList& rl);
+
+ /**
+ * Destructor
+ */
+ ~LDAPReferenceList();
+
+ /**
+ * @return The number of LDAPSearchReference-objects that are
+ * currently stored in this list.
+ */
+ size_t size() const;
+
+ /**
+ * @return true if there are zero LDAPSearchReference-objects
+ * currently stored in this list.
+ */
+ bool empty() const;
+
+ /**
+ * @return A iterator that points to the first element of the list.
+ */
+ const_iterator begin() const;
+
+ /**
+ * @return A iterator that points to the element after the last
+ * element of the list.
+ */
+ const_iterator end() const;
+
+ /**
+ * Adds one element to the end of the list.
+ * @param e The LDAPSearchReference to add to the list.
+ */
+ void addReference(const LDAPSearchReference& e);
+
+ private:
+ ListType m_refs;
+};
+#endif // LDAP_REFERENCE_LIST_H
+
diff --git a/contrib/ldapc++/src/LDAPRequest.cpp b/contrib/ldapc++/src/LDAPRequest.cpp
new file mode 100644
index 0000000..57839ce
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPRequest.cpp
@@ -0,0 +1,145 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#include "debug.h"
+#include "LDAPRequest.h"
+
+using namespace std;
+
+LDAPRequest::LDAPRequest(){
+ DEBUG(LDAP_DEBUG_CONSTRUCT, "LDAPRequest::LDAPRequest()" << endl);
+}
+
+LDAPRequest::LDAPRequest(const LDAPRequest& req){
+ DEBUG(LDAP_DEBUG_CONSTRUCT, "LDAPRequest::LDAPRequest(&)" << endl);
+ m_isReferral=req.m_isReferral;
+ m_cons = new LDAPConstraints(*(req.m_cons));
+ m_connection = req.m_connection;
+ m_parent = req.m_parent;
+ m_hopCount = req.m_hopCount;
+ m_msgID = req.m_msgID;
+}
+
+LDAPRequest::LDAPRequest(LDAPAsynConnection* con,
+ const LDAPConstraints* cons,bool isReferral, const LDAPRequest* parent){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPRequest::LDAPRequest()" << endl);
+ m_connection=con;
+ if(cons == 0){
+ m_cons=new LDAPConstraints( *(con->getConstraints()) );
+ }else{
+ m_cons=new LDAPConstraints( *cons);
+ }
+ m_isReferral=isReferral;
+ if(m_isReferral){
+ m_hopCount = (parent->getHopCount()+1);
+ m_parent= parent;
+ }else{
+ m_hopCount=0;
+ m_parent=0;
+ }
+}
+
+LDAPRequest::~LDAPRequest(){
+ DEBUG(LDAP_DEBUG_DESTROY,"LDAPRequest::~LDAPRequest()" << endl);
+ delete m_cons;
+}
+
+LDAPMsg* LDAPRequest::getNextMessage() const
+{
+ DEBUG(LDAP_DEBUG_DESTROY,"LDAPRequest::getNextMessage()" << endl);
+ int res;
+ LDAPMessage *msg;
+
+ res=ldap_result(this->m_connection->getSessionHandle(),
+ this->m_msgID,0,0,&msg);
+
+ if (res <= 0){
+ if(msg != 0){
+ ldap_msgfree(msg);
+ }
+ throw LDAPException(this->m_connection);
+ }else{
+ LDAPMsg *ret=0;
+ //this can throw an exception (Decoding Error)
+ ret = LDAPMsg::create(this,msg);
+ ldap_msgfree(msg);
+ return ret;
+ }
+}
+
+LDAPRequest* LDAPRequest::followReferral(LDAPMsg* /*urls*/){
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPBindRequest::followReferral()" << endl);
+ DEBUG(LDAP_DEBUG_TRACE,
+ "ReferralChasing not implemented for this operation" << endl);
+ return 0;
+}
+
+const LDAPConstraints* LDAPRequest::getConstraints() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPRequest::getConstraints()" << endl);
+ return m_cons;
+}
+
+const LDAPAsynConnection* LDAPRequest::getConnection() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPRequest::getConnection()" << endl);
+ return m_connection;
+}
+
+int LDAPRequest::getType() const {
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPRequest::getType()" << endl);
+ return m_requestType;
+}
+
+int LDAPRequest::getMsgID() const {
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPRequest::getMsgId()" << endl);
+ return m_msgID;
+}
+
+int LDAPRequest::getHopCount() const {
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPRequest::getHopCount()" << endl);
+ return m_hopCount;
+}
+
+const LDAPRequest* LDAPRequest::getParent() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPRequest::getParent()" << endl);
+ return m_parent;
+}
+
+bool LDAPRequest::isReferral() const {
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPRequest::isReferral()" << endl);
+ return m_isReferral;
+}
+
+bool LDAPRequest::equals(const LDAPRequest* req) const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPRequest::equals()" << endl);
+ if( (this->m_requestType == req->getType()) &&
+ (this->m_connection->getHost() == req->m_connection->getHost()) &&
+ (this->m_connection->getPort() == req->m_connection->getPort())
+ ){
+ return true;
+ }return false;
+}
+
+bool LDAPRequest::isCycle() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPRequest::isCycle()" << endl);
+ const LDAPRequest* parent=m_parent;
+ if(parent != 0){
+ do{
+ if(this->equals(parent)){
+ return true;
+ }else{
+ parent=parent->getParent();
+ }
+ }
+ while(parent != 0);
+ }
+ return false;
+}
+
+void LDAPRequest::unbind() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPRequest::unbind()" << endl);
+ m_connection->unbind();
+}
diff --git a/contrib/ldapc++/src/LDAPRequest.h b/contrib/ldapc++/src/LDAPRequest.h
new file mode 100644
index 0000000..059002b
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPRequest.h
@@ -0,0 +1,89 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#ifndef LDAP_REQUEST_H
+#define LDAP_REQUEST_H
+
+#include <LDAPConstraints.h>
+#include <LDAPAsynConnection.h>
+#include <LDAPMessageQueue.h>
+
+class LDAPUrl;
+
+/**
+ * For internal use only
+ *
+ * Each request that is sent to a LDAP-server by this library is
+ * represented by a special object that contains the parameters and some
+ * other info of the request. This virtual class is the common base class
+ * for these specialized request classes.
+ */
+class LDAPRequest{
+
+ public :
+ static const int BIND=0;
+ static const int UNBIND=2;
+ static const int SEARCH=3;
+ static const int MODIFY=7;
+ static const int ADD=8;
+ static const int DELETE=10;
+ static const int COMPARE=14;
+
+ LDAPRequest(const LDAPRequest& req);
+ LDAPRequest(LDAPAsynConnection* conn,
+ const LDAPConstraints* cons, bool isReferral=false,
+ const LDAPRequest* parent=0);
+ virtual ~LDAPRequest();
+
+ const LDAPConstraints* getConstraints() const;
+ const LDAPAsynConnection* getConnection() const;
+ virtual LDAPMsg *getNextMessage() const;
+ int getType()const;
+ int getMsgID() const;
+ int getHopCount() const;
+
+ /**
+ * @return The LDAPRequest that has created this object. Or 0 if
+ * this object was not created by another request.
+ */
+ const LDAPRequest* getParent() const;
+
+ /**
+ * @return true if this object was created during the automatic
+ * chasing of referrals. Otherwise false
+ */
+ bool isReferral() const;
+
+ void unbind() const;
+
+ /**
+ * This method encodes the request an calls the appropriate
+ * functions of the C-API to send the Request to a LDAP-Server
+ */
+ virtual LDAPMessageQueue* sendRequest()=0;
+ virtual LDAPRequest* followReferral(LDAPMsg* ref);
+
+ /**
+ * Compare this request with another on. And returns true if they
+ * have the same parameters.
+ */
+ virtual bool equals(const LDAPRequest* req) const;
+
+ bool isCycle() const;
+
+ protected :
+ bool m_isReferral;
+ int m_requestType;
+ LDAPConstraints *m_cons;
+ LDAPAsynConnection *m_connection;
+ const LDAPRequest* m_parent;
+ int m_hopCount;
+ int m_msgID; //the associated C-API Message ID
+ LDAPRequest();
+};
+#endif //LDAP_REQUEST_H
+
diff --git a/contrib/ldapc++/src/LDAPResult.cpp b/contrib/ldapc++/src/LDAPResult.cpp
new file mode 100644
index 0000000..fd35438
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPResult.cpp
@@ -0,0 +1,96 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#include "debug.h"
+#include"LDAPResult.h"
+#include"LDAPAsynConnection.h"
+#include "LDAPRequest.h"
+#include "LDAPException.h"
+
+#include <cstdlib>
+
+using namespace std;
+
+LDAPResult::LDAPResult(const LDAPRequest *req, LDAPMessage *msg) :
+ LDAPMsg(msg){
+ if(msg != 0){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPResult::LDAPResult()" << endl);
+ const LDAPAsynConnection *con=req->getConnection();
+ char **refs=0;
+ LDAPControl** srvctrls=0;
+ char* matchedDN=0;
+ char* errMsg=0;
+ int err=ldap_parse_result(con->getSessionHandle(),msg,&m_resCode,
+ &matchedDN, &errMsg,&refs,&srvctrls,0);
+ if(err != LDAP_SUCCESS){
+ ber_memvfree((void**) refs);
+ ldap_controls_free(srvctrls);
+ throw LDAPException(err);
+ }else{
+ if (refs){
+ m_referrals=LDAPUrlList(refs);
+ ber_memvfree((void**) refs);
+ }
+ if (srvctrls){
+ m_srvControls = LDAPControlSet(srvctrls);
+ m_hasControls = true;
+ ldap_controls_free(srvctrls);
+ }else{
+ m_hasControls = false;
+ }
+ if(matchedDN != 0){
+ m_matchedDN=string(matchedDN);
+ free(matchedDN);
+ }
+ if(errMsg != 0){
+ m_errMsg=string(errMsg);
+ free(errMsg);
+ }
+ }
+ }
+}
+
+LDAPResult::LDAPResult(int type, int resultCode, const std::string &msg) :
+ LDAPMsg(type,0), m_resCode(resultCode), m_errMsg(msg)
+{}
+
+
+LDAPResult::~LDAPResult(){
+ DEBUG(LDAP_DEBUG_DESTROY,"LDAPResult::~LDAPResult()" << endl);
+}
+
+int LDAPResult::getResultCode() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPResult::getResultCode()" << endl);
+ return m_resCode;
+}
+
+string LDAPResult::resToString() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPResult::resToString()" << endl);
+ return string(ldap_err2string(m_resCode));
+}
+
+const string& LDAPResult::getErrMsg() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPResult::getErrMsg()" << endl);
+ return m_errMsg;
+}
+
+const string& LDAPResult::getMatchedDN() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPResult::getMatchedDN()" << endl);
+ return m_matchedDN;
+}
+
+const LDAPUrlList& LDAPResult::getReferralUrls() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPResult::getReferralUrl()" << endl);
+ return m_referrals;
+}
+
+ostream& operator<<(ostream &s,LDAPResult &l){
+ return s << "Result: " << l.m_resCode << ": "
+ << ldap_err2string(l.m_resCode) << endl
+ << "Matched: " << l.m_matchedDN << endl << "ErrMsg: " << l.m_errMsg;
+}
+
diff --git a/contrib/ldapc++/src/LDAPResult.h b/contrib/ldapc++/src/LDAPResult.h
new file mode 100644
index 0000000..a90d010
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPResult.h
@@ -0,0 +1,162 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#ifndef LDAP_RESULT_H
+#define LDAP_RESULT_H
+
+#include<iostream>
+#include<ldap.h>
+#include <LDAPMessage.h>
+#include <LDAPControlSet.h>
+#include <LDAPUrlList.h>
+
+class LDAPRequest;
+class LDAPAsynConnection;
+
+/**
+ * This class is for representing LDAP-Result-Messages.
+ *
+ * It represents all Messages that were returned
+ * from LDAP-Operations except for Messages of the Type
+ * LDAPMsg::SEARCH_ENTRY, LDAPMsg::SEARCH_REFERENCE and
+ * LDAPMsg::EXTENDED_RESPONSE. <BR>
+ * It defines a integer constant for every possible result type that can be
+ * returned by the server.
+ */
+class LDAPResult : public LDAPMsg{
+ public :
+ //Error codes from RFC 2251
+ static const int SUCCESS = 0;
+ static const int OPERATIONS_ERROR = 1;
+ static const int PROTOCOL_ERROR = 2;
+ static const int TIME_LIMIT_EXCEEDED = 3;
+ static const int SIZE_LIMIT_EXCEEDED = 4;
+ static const int COMPARE_FALSE = 5;
+ static const int COMPARE_TRUE = 6;
+ static const int AUTH_METHOD_NOT_SUPPORTED = 7;
+ static const int STRONG_AUTH_REQUIRED = 8;
+
+ static const int REFERRAL = 10;
+ static const int ADMIN_LIMIT_EXCEEDED = 11;
+ static const int UNAVAILABLE_CRITICAL_EXTENSION = 12;
+ static const int CONFIDENTIALITY_REQUIRED = 13;
+ static const int SASL_BIND_IN_PROGRESS = 14;
+
+ static const int NO_SUCH_ATTRIBUTE = 16;
+ static const int UNDEFINED_ATTRIBUTE_TYP = 17;
+ static const int INAPPROPRIATE_MATCHING = 18;
+ static const int CONSTRAINT_VIOLATION = 19;
+ static const int ATTRIBUTE_OR_VALUE_EXISTS = 20;
+ static const int INVALID_ATTRIBUTE_SYNTAX = 21;
+
+ static const int NO_SUCH_OBJECT = 32;
+ static const int ALIAS_PROBLEM = 33;
+ static const int INVALID_DN_SYNTAX = 34;
+
+ static const int ALIAS_DEREFERENCING_PROBLEM = 36;
+
+ static const int INAPPROPRIATE_AUTHENTICATION = 48;
+ static const int INVALID_CREDENTIALS = 49;
+ static const int INSUFFICIENT_ACCESS = 50;
+ static const int BUSY = 51;
+ static const int UNAVAILABLE = 52;
+ static const int UNWILLING_TO_PERFORM = 53;
+ static const int LOOP_DETECT = 54;
+
+ static const int NAMING_VIOLATION = 64;
+ static const int OBJECT_CLASS_VIOLATION = 65;
+ static const int NOT_ALLOWED_ON_NONLEAF = 66;
+ static const int NOT_ALLOWED_ON_RDN = 67;
+ static const int ENTRY_ALREADY_EXISTS = 68;
+ static const int OBJECT_CLASS_MODS_PROHIBITED = 69;
+
+ static const int AFFECTS_MULTIPLE_DSAS = 71;
+
+ // some Errorcodes defined in the LDAP C API DRAFT
+ static const int OTHER = 80;
+ static const int SERVER_DOWN = 81;
+ static const int LOCAL_ERROR = 82;
+ static const int ENCODING_ERROR = 83;
+ static const int DECODING_ERROR = 84;
+ static const int TIMEOUT = 85;
+ static const int AUTH_UNKNOWN = 86;
+ static const int FILTER_ERROR = 87;
+ static const int USER_CANCELLED = 88;
+ static const int PARAM_ERROR = 89;
+ static const int NO_MEMORY = 90;
+ static const int CONNECT_ERROR = 91;
+ static const int NOT_SUPPORTED = 92;
+ static const int CONTROL_NOT_FOUND = 93;
+ static const int NO_RESULTS_RETURNED = 94;
+ static const int MORE_RESULTS_TO_RETURN = 95;
+ static const int CLIENT_LOOP = 96;
+ static const int REFERRAL_LIMIT_EXCEEDED = 97;
+
+ /**
+ * This constructor is called by the LDAPMsg::create method in
+ * order to parse a LDAPResult-Message
+ * @param req The request the result is associated with.
+ * @param msg The LDAPMessage-structure that contains the
+ * Message.
+ */
+ LDAPResult(const LDAPRequest *req, LDAPMessage *msg);
+ LDAPResult(int type, int resultCode, const std::string &msg);
+
+ /**
+ * The destructor.
+ */
+ virtual ~LDAPResult();
+
+ /**
+ * @returns The result code of the Message. Possible values are the
+ * integer constants defined in this class.
+ */
+ int getResultCode() const;
+
+ /**
+ * This method transforms the result code to a human-readable
+ * result message.
+ * @returns A std::string containing the result message.
+ */
+ std::string resToString() const;
+
+ /**
+ * In some case of error the server may return additional error
+ * messages.
+ * @returns The additional error message returned by the server.
+ */
+ const std::string& getErrMsg() const;
+
+ /**
+ * For messages with a result code of: NO_SUCH_OBJECT,
+ * ALIAS_PROBLEM, ALIAS_DEREFERENCING_PROBLEM or INVALID_DN_SYNTAX
+ * the server returns the DN of deepest entry in the DIT that could
+ * be found for this operation.
+ * @returns The Matched-DN value that was returned by the server.
+ */
+ const std::string& getMatchedDN() const;
+
+ /**
+ * @returns If the result code is REFERRAL this method returns the
+ * URLs of the referral that was sent by the server.
+ */
+ const LDAPUrlList& getReferralUrls() const;
+
+ private :
+ int m_resCode;
+ std::string m_matchedDN;
+ std::string m_errMsg;
+ LDAPUrlList m_referrals;
+
+ /**
+ * This method can be used to dump the data of a LDAPResult-Object.
+ * It is only useful for debugging purposes at the moment
+ */
+ friend std::ostream& operator<<(std::ostream &s,LDAPResult &l);
+};
+#endif //LDAP_RESULT_H
+
diff --git a/contrib/ldapc++/src/LDAPSaslBindResult.cpp b/contrib/ldapc++/src/LDAPSaslBindResult.cpp
new file mode 100644
index 0000000..ae15cfb
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPSaslBindResult.cpp
@@ -0,0 +1,45 @@
+// $OpenLDAP$
+/*
+ * Copyright 2007-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "debug.h"
+#include <lber.h>
+#include "LDAPRequest.h"
+#include "LDAPException.h"
+
+#include "LDAPResult.h"
+#include "LDAPSaslBindResult.h"
+
+using namespace std;
+
+LDAPSaslBindResult::LDAPSaslBindResult(const LDAPRequest* req, LDAPMessage* msg) :
+ LDAPResult(req, msg){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,"LDAPSaslBindResult::LDAPSaslBindResult()"
+ << std::endl);
+ BerValue* data = 0;
+ LDAP* lc = req->getConnection()->getSessionHandle();
+ int err = ldap_parse_sasl_bind_result(lc, msg, &data, 0);
+ if( err != LDAP_SUCCESS && err != LDAP_SASL_BIND_IN_PROGRESS ){
+ ber_bvfree(data);
+ throw LDAPException(err);
+ }else{
+ if(data){
+ DEBUG(LDAP_DEBUG_TRACE, " creds present" << std::endl);
+ m_creds=string(data->bv_val, data->bv_len);
+ ber_bvfree(data);
+ } else {
+ DEBUG(LDAP_DEBUG_TRACE, " no creds present" << std::endl);
+ }
+ }
+}
+
+LDAPSaslBindResult::~LDAPSaslBindResult(){
+ DEBUG(LDAP_DEBUG_DESTROY,"LDAPSaslBindResult::~LDAPSaslBindResult()" << endl);
+}
+
+const string& LDAPSaslBindResult::getServerCreds() const{
+ return m_creds;
+}
+
diff --git a/contrib/ldapc++/src/LDAPSaslBindResult.h b/contrib/ldapc++/src/LDAPSaslBindResult.h
new file mode 100644
index 0000000..b31e89e
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPSaslBindResult.h
@@ -0,0 +1,43 @@
+// $OpenLDAP$
+/*
+ * Copyright 2007-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDAP_SASL_BIND_RESULT_H
+#define LDAP_SASL_BIND_RESULT_H
+
+#include <ldap.h>
+
+#include <LDAPResult.h>
+
+class LDAPRequest;
+
+/**
+ * Object of this class are created by the LDAPMsg::create method if
+ * results for an Extended Operation were returned by a LDAP server.
+ */
+class LDAPSaslBindResult : public LDAPResult {
+ public :
+ /**
+ * Constructor that creates an LDAPExtResult-object from the C-API
+ * structures
+ */
+ LDAPSaslBindResult(const LDAPRequest* req, LDAPMessage* msg);
+
+ /**
+ * The Destructor
+ */
+ virtual ~LDAPSaslBindResult();
+
+ /**
+ * @returns If the result contained data this method will return
+ * the data to the caller as a std::string.
+ */
+ const std::string& getServerCreds() const;
+
+ private:
+ std::string m_creds;
+};
+
+#endif // LDAP_SASL_BIND_RESULT_H
diff --git a/contrib/ldapc++/src/LDAPSchema.cpp b/contrib/ldapc++/src/LDAPSchema.cpp
new file mode 100644
index 0000000..38fe282
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPSchema.cpp
@@ -0,0 +1,84 @@
+// $OpenLDAP$
+/*
+ * Copyright 2003-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "LDAPSchema.h"
+
+#include <ctype.h>
+#include <ldap.h>
+
+#include "debug.h"
+#include "StringList.h"
+
+
+using namespace std;
+
+LDAPSchema::LDAPSchema(){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,
+ "LDAPSchema::LDAPSchema( )" << endl);
+}
+
+LDAPSchema::~LDAPSchema() {
+ DEBUG(LDAP_DEBUG_DESTROY,"LDAPSchema::~LDAPSchema()" << endl);
+}
+
+void LDAPSchema::setObjectClasses (const StringList &ocs) {
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPSchema::setObjectClasses()" << endl);
+
+ // parse the stringlist and save it to global map...
+ StringList::const_iterator i,j;
+ for (i = ocs.begin(); i != ocs.end(); i++) {
+ LDAPObjClass oc ( (*i) );
+ StringList names = oc.getNames();
+ // there could be more names for one object...
+ for (j = names.begin(); j != names.end(); j++) {
+ string lc_name = *j;
+ string::iterator k;
+ for ( k = lc_name.begin(); k != lc_name.end(); k++ ) {
+ (*k) = tolower(*k);
+ }
+ object_classes [lc_name] = LDAPObjClass (oc);
+ }
+ }
+}
+
+void LDAPSchema::setAttributeTypes (const StringList &ats) {
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPSchema::setAttributeTypes()" << endl);
+
+ // parse the stringlist and save it to global map...
+ StringList::const_iterator i,j;
+ for (i = ats.begin(); i != ats.end(); i++) {
+ LDAPAttrType at ( (*i) );
+ StringList names = at.getNames();
+ // there could be more names for one object...
+ for (j = names.begin(); j != names.end(); j++) {
+ string lc_name = *j;
+ string::iterator k;
+ for ( k = lc_name.begin(); k != lc_name.end(); k++ ) {
+ (*k) = tolower(*k);
+ }
+ attr_types [lc_name] = LDAPAttrType (at);
+ }
+ }
+}
+
+LDAPObjClass LDAPSchema::getObjectClassByName (string name) {
+ string lc_name = name;
+ string::iterator k;
+ for ( k = lc_name.begin(); k != lc_name.end(); k++ ) {
+ (*k) = tolower(*k);
+ }
+ return object_classes [lc_name];
+}
+
+LDAPAttrType LDAPSchema::getAttributeTypeByName (string name) {
+ string lc_name = name;
+ string::iterator k;
+ for ( k = lc_name.begin(); k != lc_name.end(); k++ ) {
+ (*k) = tolower(*k);
+ }
+
+ return attr_types [lc_name];
+}
diff --git a/contrib/ldapc++/src/LDAPSchema.h b/contrib/ldapc++/src/LDAPSchema.h
new file mode 100644
index 0000000..c55c9af
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPSchema.h
@@ -0,0 +1,73 @@
+// $OpenLDAP$
+/*
+ * Copyright 2003-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDAP_SCHEMA_H
+#define LDAP_SCHEMA_H
+
+#include <string>
+#include <map>
+
+#include "LDAPObjClass.h"
+#include "LDAPAttrType.h"
+
+/**
+ * Represents the LDAP schema
+ */
+class LDAPSchema{
+ private :
+ /**
+ * map of object classes: index is name, value is LDAPObjClass object
+ */
+ map <string, LDAPObjClass> object_classes;
+
+ /**
+ * map of attribute types: index is name, value is LDAPAttrType object
+ */
+ map <string, LDAPAttrType> attr_types;
+
+ public :
+
+ /**
+ * Constructs an empty object
+ */
+ LDAPSchema();
+
+ /**
+ * Destructor
+ */
+ virtual ~LDAPSchema();
+
+ /**
+ * Fill the object_classes map
+ * @param oc description of one objectclass (string returned by search
+ * command), in form:
+ * "( 1.2.3.4.5 NAME '<name>' SUP <supname> STRUCTURAL
+ * DESC '<description>' MUST ( <attrtype> ) MAY ( <attrtype> ))"
+ */
+ void setObjectClasses (const StringList &oc);
+
+ /**
+ * Fill the attr_types map
+ * @param at description of one attribute type
+ * (string returned by search command), in form:
+ * "( 1.2.3.4.6 NAME ( '<name>' ) DESC '<desc>'
+ * EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )"
+ */
+ void setAttributeTypes (const StringList &at);
+
+ /**
+ * Returns object class object with given name
+ */
+ LDAPObjClass getObjectClassByName (std::string name);
+
+ /**
+ * Returns attribute type object with given name
+ */
+ LDAPAttrType getAttributeTypeByName (string name);
+
+};
+
+#endif // LDAP_SCHEMA_H
diff --git a/contrib/ldapc++/src/LDAPSearchReference.cpp b/contrib/ldapc++/src/LDAPSearchReference.cpp
new file mode 100644
index 0000000..d82348c
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPSearchReference.cpp
@@ -0,0 +1,53 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#include <iostream>
+
+#include "debug.h"
+#include "LDAPSearchReference.h"
+#include "LDAPException.h"
+#include "LDAPRequest.h"
+#include "LDAPUrl.h"
+
+using namespace std;
+
+LDAPSearchReference::LDAPSearchReference(const LDAPRequest *req,
+ LDAPMessage *msg) : LDAPMsg(msg){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,
+ "LDAPSearchReference::LDAPSearchReference()" << endl;)
+ char **ref=0;
+ LDAPControl** srvctrls=0;
+ const LDAPAsynConnection* con=req->getConnection();
+ int err = ldap_parse_reference(con->getSessionHandle(), msg, &ref,
+ &srvctrls,0);
+ if (err != LDAP_SUCCESS){
+ ber_memvfree((void**) ref);
+ ldap_controls_free(srvctrls);
+ throw LDAPException(err);
+ }else{
+ m_urlList=LDAPUrlList(ref);
+ ber_memvfree((void**) ref);
+ if (srvctrls){
+ m_srvControls = LDAPControlSet(srvctrls);
+ m_hasControls = true;
+ ldap_controls_free(srvctrls);
+ }else{
+ m_hasControls = false;
+ }
+ }
+}
+
+LDAPSearchReference::~LDAPSearchReference(){
+ DEBUG(LDAP_DEBUG_DESTROY,"LDAPSearchReference::~LDAPSearchReference()"
+ << endl);
+}
+
+const LDAPUrlList& LDAPSearchReference::getUrls() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPSearchReference::getUrls()" << endl);
+ return m_urlList;
+}
+
diff --git a/contrib/ldapc++/src/LDAPSearchReference.h b/contrib/ldapc++/src/LDAPSearchReference.h
new file mode 100644
index 0000000..209ae33
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPSearchReference.h
@@ -0,0 +1,46 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#ifndef LDAP_SEARCH_REFERENCE_H
+#define LDAP_SEARCH_REFERENCE_H
+
+#include <LDAPMessage.h>
+#include <LDAPUrlList.h>
+
+class LDAPRequest;
+class LDAPUrl;
+
+/**
+ * This class is used to represent Continuation References that were
+ * returned during a SEARCH-Operation.
+ */
+class LDAPSearchReference : public LDAPMsg{
+
+ public :
+ /**
+ * Constructor that create an object from the C-API structures
+ */
+ LDAPSearchReference(const LDAPRequest* req, LDAPMessage* msg);
+
+ /**
+ * The Destructor
+ */
+ ~LDAPSearchReference();
+
+ /**
+ * @returns The destination URLs that were send with this message
+ */
+ const LDAPUrlList& getUrls() const;
+
+ private :
+ LDAPUrlList m_urlList;
+ LDAPSearchReference();
+};
+
+
+
+#endif //LDAP_SEARCH_REFERENCE_H
diff --git a/contrib/ldapc++/src/LDAPSearchRequest.cpp b/contrib/ldapc++/src/LDAPSearchRequest.cpp
new file mode 100644
index 0000000..bc752c9
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPSearchRequest.cpp
@@ -0,0 +1,135 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "config.h"
+#include "ac/time.h"
+#include "debug.h"
+#include "LDAPSearchRequest.h"
+#include "LDAPException.h"
+#include "LDAPSearchReference.h"
+#include "LDAPResult.h"
+#include "LDAPRequest.h"
+#include "LDAPUrl.h"
+
+using namespace std;
+
+LDAPSearchRequest::LDAPSearchRequest(const LDAPSearchRequest& req ) :
+ LDAPRequest (req){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,
+ "LDAPSearchRequest::LDAPSearchRequest(&)" << endl);
+ m_base=req.m_base;
+ m_scope=req.m_scope;
+ m_filter=req.m_filter;
+ m_attrs=req.m_attrs;
+ m_attrsOnly=req.m_attrsOnly;
+}
+
+
+LDAPSearchRequest::LDAPSearchRequest(const string& base, int scope,
+ const string& filter, const StringList& attrs, bool attrsOnly,
+ LDAPAsynConnection *connect,
+ const LDAPConstraints* cons, bool isReferral,
+ const LDAPRequest* parent)
+ : LDAPRequest (connect,cons,isReferral,parent) {
+
+ DEBUG(LDAP_DEBUG_CONSTRUCT,
+ "LDAPSearchRequest:LDAPSearchRequest()" << endl);
+ DEBUG(LDAP_DEBUG_CONSTRUCT & LDAP_DEBUG_PARAMETER,
+ " base:" << base << endl << " scope:" << scope << endl
+ << " filter:" << filter << endl);
+ m_requestType=LDAPRequest::SEARCH;
+ //insert some validating and copying here
+ m_base=base;
+ m_scope=scope;
+ if(filter == ""){
+ m_filter="objectClass=*";
+ }else{
+ m_filter=filter;
+ }
+ m_attrs=attrs;
+ m_attrsOnly=attrsOnly;
+}
+
+LDAPSearchRequest::~LDAPSearchRequest(){
+ DEBUG(LDAP_DEBUG_DESTROY, "LDAPSearchRequest::~LDAPSearchRequest" << endl);
+}
+
+LDAPMessageQueue* LDAPSearchRequest::sendRequest(){
+ int msgID;
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPSearchRequest::sendRequest()" << endl);
+ timeval* tmptime=m_cons->getTimeoutStruct();
+ char** tmpattrs=m_attrs.toCharArray();
+ LDAPControl** tmpSrvCtrl=m_cons->getSrvCtrlsArray();
+ LDAPControl** tmpClCtrl=m_cons->getClCtrlsArray();
+ int aliasDeref = m_cons->getAliasDeref();
+ ldap_set_option(m_connection->getSessionHandle(), LDAP_OPT_DEREF,
+ &aliasDeref);
+ int err=ldap_search_ext(m_connection->getSessionHandle(), m_base.c_str(),
+ m_scope, m_filter.c_str(), tmpattrs, m_attrsOnly, tmpSrvCtrl,
+ tmpClCtrl, tmptime, m_cons->getSizeLimit(), &msgID );
+ delete tmptime;
+ ber_memvfree((void**)tmpattrs);
+ LDAPControlSet::freeLDAPControlArray(tmpSrvCtrl);
+ LDAPControlSet::freeLDAPControlArray(tmpClCtrl);
+
+ if (err != LDAP_SUCCESS){
+ throw LDAPException(err);
+ } else if (isReferral()){
+ m_msgID=msgID;
+ return 0;
+ }else{
+ m_msgID=msgID;
+ return new LDAPMessageQueue(this);
+ }
+}
+
+LDAPRequest* LDAPSearchRequest::followReferral(LDAPMsg* ref){
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPSearchRequest::followReferral()" << endl);
+ LDAPUrlList urls;
+ LDAPUrlList::const_iterator usedUrl;
+ LDAPAsynConnection* con;
+ string filter;
+ int scope;
+ if(ref->getMessageType() == LDAPMsg::SEARCH_REFERENCE){
+ urls = ((LDAPSearchReference *)ref)->getUrls();
+ }else{
+ urls = ((LDAPResult *)ref)->getReferralUrls();
+ }
+ con = getConnection()->referralConnect(urls,usedUrl,m_cons);
+ if(con != 0){
+ if((usedUrl->getFilter() != "") &&
+ (usedUrl->getFilter() != m_filter)){
+ filter=usedUrl->getFilter();
+ }else{
+ filter=m_filter;
+ }
+ if( (ref->getMessageType() == LDAPMsg::SEARCH_REFERENCE) &&
+ (m_scope == LDAPAsynConnection::SEARCH_ONE)
+ ){
+ scope = LDAPAsynConnection::SEARCH_BASE;
+ DEBUG(LDAP_DEBUG_TRACE," adjusted scope to BASE" << endl);
+ }else{
+ scope = m_scope;
+ }
+ }else{
+ return 0;
+ }
+ return new LDAPSearchRequest(usedUrl->getDN(), scope, filter,
+ m_attrs, m_attrsOnly, con, m_cons,true,this);
+}
+
+bool LDAPSearchRequest::equals(const LDAPRequest* req)const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPSearchRequest::equals()" << endl);
+ if( LDAPRequest::equals(req)){
+ LDAPSearchRequest* sreq = (LDAPSearchRequest*)req;
+ if ( (m_base == sreq->m_base) &&
+ (m_scope == sreq->m_scope)
+ ){
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/contrib/ldapc++/src/LDAPSearchRequest.h b/contrib/ldapc++/src/LDAPSearchRequest.h
new file mode 100644
index 0000000..59a63a8
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPSearchRequest.h
@@ -0,0 +1,43 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDAP_SEARCH_REQUEST_H
+#define LDAP_SEARCH_REQUEST_H
+
+#include <queue>
+#include <LDAPRequest.h>
+
+class LDAPSearchReference;
+class LDAPReferral;
+class LDAPUrl;
+
+class LDAPSearchRequest : public LDAPRequest{
+
+ public :
+ LDAPSearchRequest(const LDAPSearchRequest& req);
+
+ LDAPSearchRequest(const std::string& base, int scope, const std::string& filter,
+ const StringList& attrs, bool attrsOnly,
+ LDAPAsynConnection *connect,
+ const LDAPConstraints* cons, bool isReferral=false,
+ const LDAPRequest* parent=0);
+ virtual ~LDAPSearchRequest();
+ virtual LDAPMessageQueue* sendRequest();
+ virtual LDAPRequest* followReferral(LDAPMsg* ref);
+ virtual bool equals(const LDAPRequest* req) const;
+
+ private :
+ std::string m_base;
+ int m_scope;
+ std::string m_filter;
+ StringList m_attrs;
+ bool m_attrsOnly;
+
+ //no default constructor
+ LDAPSearchRequest(){};
+};
+
+#endif //LDAP_SEARCH_REQUEST_H
diff --git a/contrib/ldapc++/src/LDAPSearchResult.cpp b/contrib/ldapc++/src/LDAPSearchResult.cpp
new file mode 100644
index 0000000..cc850df
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPSearchResult.cpp
@@ -0,0 +1,52 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#include <iostream>
+
+#include "debug.h"
+#include "LDAPSearchResult.h"
+#include "LDAPRequest.h"
+
+using namespace std;
+
+LDAPSearchResult::LDAPSearchResult(const LDAPRequest *req,
+ LDAPMessage *msg) : LDAPMsg(msg){
+ DEBUG(LDAP_DEBUG_CONSTRUCT,
+ "LDAPSearchResult::LDAPSearchResult()" << endl);
+ entry = new LDAPEntry(req->getConnection(), msg);
+ //retrieve the controls here
+ LDAPControl** srvctrls=0;
+ int err = ldap_get_entry_controls(req->getConnection()->getSessionHandle(),
+ msg,&srvctrls);
+ if(err != LDAP_SUCCESS){
+ ldap_controls_free(srvctrls);
+ }else{
+ if (srvctrls){
+ m_srvControls = LDAPControlSet(srvctrls);
+ m_hasControls = true;
+ ldap_controls_free(srvctrls);
+ }else{
+ m_hasControls = false;
+ }
+ }
+}
+
+LDAPSearchResult::LDAPSearchResult(const LDAPSearchResult& res) :
+ LDAPMsg(res){
+ entry = new LDAPEntry(*(res.entry));
+}
+
+LDAPSearchResult::~LDAPSearchResult(){
+ DEBUG(LDAP_DEBUG_DESTROY,"LDAPSearchResult::~LDAPSearchResult()" << endl);
+ delete entry;
+}
+
+const LDAPEntry* LDAPSearchResult::getEntry() const{
+ DEBUG(LDAP_DEBUG_TRACE,"LDAPSearchResult::getEntry()" << endl);
+ return entry;
+}
+
diff --git a/contrib/ldapc++/src/LDAPSearchResult.h b/contrib/ldapc++/src/LDAPSearchResult.h
new file mode 100644
index 0000000..c7646e5
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPSearchResult.h
@@ -0,0 +1,45 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#ifndef LDAP_SEARCH_RESULT_H
+#define LDAP_SEARCH_RESULT_H
+
+#include <LDAPMessage.h>
+#include <LDAPEntry.h>
+
+class LDAPRequest;
+
+/**
+ * This class is used to represent the result entries of a
+ * SEARCH-operation.
+ */
+class LDAPSearchResult : public LDAPMsg{
+ public:
+ /**
+ * Constructor that create an object from the C-API structures
+ */
+ LDAPSearchResult(const LDAPRequest *req, LDAPMessage *msg);
+
+ /**
+ * Copy-Constructor
+ */
+ LDAPSearchResult(const LDAPSearchResult& res);
+
+ /**
+ * The Destructor
+ */
+ virtual ~LDAPSearchResult();
+
+ /**
+ * @returns The entry that has been sent with this result message.
+ */
+ const LDAPEntry* getEntry() const;
+
+ private:
+ LDAPEntry *entry;
+};
+#endif //LDAP_SEARCH_RESULT_H
diff --git a/contrib/ldapc++/src/LDAPSearchResults.cpp b/contrib/ldapc++/src/LDAPSearchResults.cpp
new file mode 100644
index 0000000..a25afe5
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPSearchResults.cpp
@@ -0,0 +1,60 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#include "LDAPException.h"
+#include "LDAPSearchResult.h"
+#include "LDAPResult.h"
+
+#include "LDAPSearchResults.h"
+
+LDAPSearchResults::LDAPSearchResults(){
+ entryPos = entryList.begin();
+ refPos = refList.begin();
+}
+
+LDAPResult* LDAPSearchResults::readMessageQueue(LDAPMessageQueue* msg){
+ if(msg != 0){
+ LDAPMsg* res=0;
+ for(;;){
+ try{
+ res = msg->getNext();
+ }catch (LDAPException e){
+ throw;
+ }
+ switch(res->getMessageType()){
+ case LDAPMsg::SEARCH_ENTRY :
+ entryList.addEntry(*((LDAPSearchResult*)res)->getEntry());
+ break;
+ case LDAPMsg::SEARCH_REFERENCE :
+ refList.addReference(*((LDAPSearchReference*)res));
+ break;
+ default:
+ entryPos=entryList.begin();
+ refPos=refList.begin();
+ return ((LDAPResult*) res);
+ }
+ delete res;
+ res=0;
+ }
+ }
+ return 0;
+}
+
+LDAPEntry* LDAPSearchResults::getNext(){
+ if( entryPos != entryList.end() ){
+ LDAPEntry* ret= new LDAPEntry(*entryPos);
+ entryPos++;
+ return ret;
+ }
+ if( refPos != refList.end() ){
+ LDAPUrlList urls= refPos->getUrls();
+ refPos++;
+ throw(LDAPReferralException(urls));
+ }
+ return 0;
+}
+
diff --git a/contrib/ldapc++/src/LDAPSearchResults.h b/contrib/ldapc++/src/LDAPSearchResults.h
new file mode 100644
index 0000000..f675de1
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPSearchResults.h
@@ -0,0 +1,56 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDAP_SEARCH_RESULTS_H
+#define LDAP_SEARCH_RESULTS_H
+
+#include <LDAPEntry.h>
+#include <LDAPEntryList.h>
+#include <LDAPMessage.h>
+#include <LDAPMessageQueue.h>
+#include <LDAPReferenceList.h>
+#include <LDAPSearchReference.h>
+
+class LDAPResult;
+
+/**
+ * The class stores the results of a synchronous SEARCH-Operation
+ */
+class LDAPSearchResults{
+ public:
+ /**
+ * Default-Constructor
+ */
+ LDAPSearchResults();
+
+ /**
+ * For internal use only.
+ *
+ * This method reads Search result entries from a
+ * LDAPMessageQueue-object.
+ * @param msg The message queue to read
+ */
+ LDAPResult* readMessageQueue(LDAPMessageQueue* msg);
+
+ /**
+ * The method is used by the client-application to read the
+ * result entries of the SEARCH-Operation. Every call of this
+ * method returns one entry. If all entries were read it return 0.
+ * @throws LDAPReferralException If a Search Reference was
+ * returned by the server
+ * @returns A LDAPEntry-object as a result of a SEARCH-Operation or
+ * 0 if no more entries are there to return.
+ */
+ LDAPEntry* getNext();
+ private :
+ LDAPEntryList entryList;
+ LDAPReferenceList refList;
+ LDAPEntryList::const_iterator entryPos;
+ LDAPReferenceList::const_iterator refPos;
+};
+#endif //LDAP_SEARCH_RESULTS_H
+
+
diff --git a/contrib/ldapc++/src/LDAPUrl.cpp b/contrib/ldapc++/src/LDAPUrl.cpp
new file mode 100644
index 0000000..b3a2ec0
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPUrl.cpp
@@ -0,0 +1,518 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#include "LDAPUrl.h"
+#include <sstream>
+#include <iomanip>
+#include "debug.h"
+
+using namespace std;
+
+#define PCT_ENCFLAG_NONE 0x0000U
+#define PCT_ENCFLAG_COMMA 0x0001U
+#define PCT_ENCFLAG_SLASH 0x0002U
+
+#define LDAP_DEFAULT_PORT 389
+#define LDAPS_DEFAULT_PORT 636
+
+LDAPUrl::LDAPUrl(const std::string &url)
+{
+ DEBUG(LDAP_DEBUG_CONSTRUCT, "LDAPUrl::LDAPUrl()" << endl);
+ DEBUG(LDAP_DEBUG_CONSTRUCT | LDAP_DEBUG_PARAMETER,
+ " url:" << url << endl);
+ m_urlString = url;
+ m_Filter = "";
+ m_Scheme = "ldap";
+ m_Scope = 0;
+ m_Port = 0;
+ regenerate = false;
+ if (url != "") {
+ this->parseUrl();
+ }
+}
+
+LDAPUrl::~LDAPUrl()
+{
+ DEBUG(LDAP_DEBUG_DESTROY, "LDAPUrl::~LDAPUrl()" << endl);
+ m_Attrs.clear();
+}
+
+int LDAPUrl::getPort() const
+{
+ return m_Port;
+}
+
+void LDAPUrl::setPort(int port)
+{
+ m_Port = port;
+ regenerate = true;
+}
+
+int LDAPUrl::getScope() const
+{
+ return m_Scope;
+}
+
+void LDAPUrl::setScope( const std::string &scope )
+{
+ if (scope == "base" || scope == "" ) {
+ m_Scope = 0;
+ } else if (scope == "one" ) {
+ m_Scope = 1;
+ } else if (scope == "sub" ) {
+ m_Scope = 2;
+ } else {
+ throw LDAPUrlException(LDAPUrlException::INVALID_SCOPE,
+ "Scope was:" + scope);
+ }
+ regenerate = true;
+}
+
+const string& LDAPUrl::getURLString() const
+{
+ if (regenerate){
+ this->components2Url();
+ regenerate=false;
+ }
+ return m_urlString;
+}
+
+void LDAPUrl::setURLString( const std::string &url )
+{
+ m_urlString = url;
+ if (url != "") {
+ this->parseUrl();
+ }
+ regenerate = false;
+}
+
+const string& LDAPUrl::getHost() const
+{
+ return m_Host;
+}
+
+void LDAPUrl::setHost( const std::string &host )
+{
+ m_Host = host;
+ regenerate = true;
+}
+
+const string& LDAPUrl::getDN() const
+{
+ return m_DN;
+}
+void LDAPUrl::setDN( const std::string &dn )
+{
+ m_DN = dn;
+ regenerate = true;
+}
+
+const string& LDAPUrl::getFilter() const
+{
+ return m_Filter;
+}
+void LDAPUrl::setFilter( const std::string &filter )
+{
+ m_Filter = filter;
+ regenerate = true;
+}
+
+const StringList& LDAPUrl::getAttrs() const
+{
+ return m_Attrs;
+}
+void LDAPUrl::setAttrs( const StringList &attrs )
+{
+ m_Attrs = attrs;
+ regenerate = true;
+}
+
+const StringList& LDAPUrl::getExtensions() const
+{
+ return m_Extensions;
+}
+
+void LDAPUrl::setExtensions( const StringList &ext )
+{
+ m_Extensions = ext;
+ regenerate = true;
+}
+
+const std::string& LDAPUrl::getScheme() const
+{
+ return m_Scheme;
+}
+
+void LDAPUrl::setScheme( const std::string &scheme )
+{
+ if (scheme == "ldap" || scheme == "ldaps" ||
+ scheme == "ldapi" || scheme == "cldap" )
+ {
+ m_Scheme = scheme;
+ regenerate = true;
+ } else {
+ throw LDAPUrlException(LDAPUrlException::INVALID_SCHEME,
+ "Unknown URL scheme: \"" + scheme + "\"");
+ }
+}
+
+void LDAPUrl::parseUrl()
+{
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPUrl::parseUrl()" << std::endl);
+ // reading Scheme
+ std::string::size_type pos = m_urlString.find(':');
+ std::string::size_type startpos = pos;
+ if (pos == std::string::npos) {
+ throw LDAPUrlException(LDAPUrlException::INVALID_URL,
+ "No colon found in URL");
+ }
+ std::string scheme = m_urlString.substr(0, pos);
+ DEBUG(LDAP_DEBUG_TRACE, " scheme is <" << scheme << ">" << std::endl);
+
+ if ( scheme == "ldap" ) {
+ m_Scheme = scheme;
+ } else if ( scheme == "ldaps" ) {
+ m_Scheme = scheme;
+ } else if ( scheme == "ldapi" ) {
+ m_Scheme = scheme;
+ } else if ( scheme == "cldap" ) {
+ m_Scheme = scheme;
+ } else {
+ throw LDAPUrlException(LDAPUrlException::INVALID_SCHEME,
+ "Unknown URL Scheme: \"" + scheme + "\"");
+ }
+
+ if ( m_urlString[pos+1] != '/' || m_urlString[pos+2] != '/' ) {
+ throw LDAPUrlException(LDAPUrlException::INVALID_URL);
+ } else {
+ startpos = pos + 3;
+ }
+ if ( m_urlString[startpos] == '/' ) {
+ // no hostname and port
+ startpos++;
+ } else {
+ std::string::size_type hostend, portstart=0;
+ pos = m_urlString.find('/', startpos);
+
+ // IPv6 Address?
+ if ( m_urlString[startpos] == '[' ) {
+ // skip
+ startpos++;
+ hostend = m_urlString.find(']', startpos);
+ if ( hostend == std::string::npos ){
+ throw LDAPUrlException(LDAPUrlException::INVALID_URL);
+ }
+ portstart = hostend + 1;
+ } else {
+ hostend = m_urlString.find(':', startpos);
+ if ( hostend == std::string::npos || portstart > pos ) {
+ hostend = pos;
+ }
+ portstart = hostend;
+ }
+ std::string host = m_urlString.substr(startpos, hostend - startpos);
+ DEBUG(LDAP_DEBUG_TRACE, " host: <" << host << ">" << std::endl);
+ percentDecode(host, m_Host);
+
+ if (portstart >= m_urlString.length() || portstart >= pos ) {
+ if ( m_Scheme == "ldap" || m_Scheme == "cldap" ) {
+ m_Port = LDAP_DEFAULT_PORT;
+ } else if ( m_Scheme == "ldaps" ) {
+ m_Port = LDAPS_DEFAULT_PORT;
+ }
+ } else {
+ std::string port = m_urlString.substr(portstart+1,
+ (pos == std::string::npos ? pos : pos-portstart-1) );
+ if ( port.length() > 0 ) {
+ std::istringstream i(port);
+ i >> m_Port;
+ if ( i.fail() ){
+ throw LDAPUrlException(LDAPUrlException::INVALID_PORT);
+ }
+ }
+ DEBUG(LDAP_DEBUG_TRACE, " Port: <" << m_Port << ">"
+ << std::endl);
+ }
+ startpos = pos + 1;
+ }
+ int parserMode = base;
+ while ( pos != std::string::npos ) {
+ pos = m_urlString.find('?', startpos);
+ std::string actComponent = m_urlString.substr(startpos,
+ pos - startpos);
+ DEBUG(LDAP_DEBUG_TRACE, " ParserMode:" << parserMode << std::endl);
+ DEBUG(LDAP_DEBUG_TRACE, " ActComponent: <" << actComponent << ">"
+ << std::endl);
+ std::string s_scope = "";
+ std::string s_ext = "";
+ switch(parserMode) {
+ case base :
+ percentDecode(actComponent, m_DN);
+ DEBUG(LDAP_DEBUG_TRACE, " BaseDN:" << m_DN << std::endl);
+ break;
+ case attrs :
+ DEBUG(LDAP_DEBUG_TRACE, " reading Attributes" << std::endl);
+ if (actComponent.length() != 0 ) {
+ string2list(actComponent,m_Attrs, true);
+ }
+ break;
+ case scope :
+ percentDecode(actComponent, s_scope);
+ if (s_scope == "base" || s_scope == "" ) {
+ m_Scope = 0;
+ } else if (s_scope == "one" ) {
+ m_Scope = 1;
+ } else if (s_scope == "sub" ) {
+ m_Scope = 2;
+ } else {
+ throw LDAPUrlException(LDAPUrlException::INVALID_SCOPE);
+ }
+ DEBUG(LDAP_DEBUG_TRACE, " Scope: <" << s_scope << ">"
+ << std::endl);
+ break;
+ case filter :
+ percentDecode(actComponent, m_Filter);
+ DEBUG(LDAP_DEBUG_TRACE, " filter: <" << m_Filter << ">"
+ << std::endl);
+ break;
+ case extensions :
+ DEBUG(LDAP_DEBUG_TRACE, " reading Extensions" << std::endl);
+ string2list(actComponent, m_Extensions, true);
+ break;
+ default :
+ DEBUG(LDAP_DEBUG_TRACE, " unknown state" << std::endl);
+ break;
+ }
+ startpos = pos + 1;
+ parserMode++;
+ }
+}
+
+void LDAPUrl::percentDecode(const std::string& src, std::string &out)
+{
+ DEBUG(LDAP_DEBUG_TRACE, "LDAPUrl::percentDecode()" << std::endl);
+ std::string::size_type pos = 0;
+ std::string::size_type startpos = 0;
+ pos = src.find('%', startpos);
+ while ( pos != std::string::npos ) {
+ out += src.substr(startpos, pos - startpos);
+ std::string istr(src.substr(pos+1, 2));
+ std::istringstream i(istr);
+ i.setf(std::ios::hex, std::ios::basefield);
+ i.unsetf(std::ios::showbase);
+ int hex;
+ i >> hex;
+ if ( i.fail() ){
+ throw LDAPUrlException(LDAPUrlException::URL_DECODING_ERROR,
+ "Invalid percent encoding");
+ }
+ char j = hex;
+ out.push_back(j);
+ startpos = pos+3;
+ pos = src.find('%', startpos);
+ }
+ out += src.substr(startpos, pos - startpos);
+}
+
+void LDAPUrl::string2list(const std::string &src, StringList& sl,
+ bool percentDecode)
+{
+ std::string::size_type comma_startpos = 0;
+ std::string::size_type comma_pos = 0;
+ std::string actItem;
+ while ( comma_pos != std::string::npos ) {
+ comma_pos = src.find(',', comma_startpos);
+ actItem = src.substr(comma_startpos, comma_pos - comma_startpos);
+ if (percentDecode){
+ std::string decoded;
+ this->percentDecode(actItem,decoded);
+ actItem = decoded;
+ }
+ sl.add(actItem);
+ comma_startpos = comma_pos + 1;
+ }
+}
+
+
+void LDAPUrl::components2Url() const
+{
+ std::ostringstream url;
+ std::string encoded = "";
+
+ url << m_Scheme << "://";
+ // IPv6 ?
+ if ( m_Host.find( ':', 0 ) != std::string::npos ) {
+ url << "[" << this->percentEncode(m_Host, encoded) << "]";
+ } else {
+ url << this->percentEncode(m_Host, encoded, PCT_ENCFLAG_SLASH);
+ }
+
+ if ( m_Port != 0 ) {
+ url << ":" << m_Port;
+ }
+
+ url << "/";
+ encoded = "";
+ if ( m_DN != "" ) {
+ this->percentEncode( m_DN, encoded );
+ url << encoded;
+ }
+ string qm = "";
+ if ( ! m_Attrs.empty() ){
+ url << "?";
+ bool first = true;
+ for ( StringList::const_iterator i = m_Attrs.begin();
+ i != m_Attrs.end(); i++)
+ {
+ this->percentEncode( *i, encoded );
+ if ( ! first ) {
+ url << ",";
+ } else {
+ first = false;
+ }
+ url << encoded;
+ }
+ } else {
+ qm.append("?");
+ }
+ if ( m_Scope == 1 ) {
+ url << qm << "?one";
+ qm = "";
+ } else if ( m_Scope == 2 ) {
+ url << qm << "?sub";
+ qm = "";
+ } else {
+ qm.append("?");
+ }
+ if (m_Filter != "" ){
+ this->percentEncode( m_Filter, encoded );
+ url << qm << "?" << encoded;
+ qm = "";
+ } else {
+ qm.append("?");
+ }
+
+ if ( ! m_Extensions.empty() ){
+ url << qm << "?";
+ bool first = true;
+ for ( StringList::const_iterator i = m_Extensions.begin();
+ i != m_Extensions.end(); i++)
+ {
+ this->percentEncode( *i, encoded, 1);
+ if ( ! first ) {
+ url << ",";
+ } else {
+ first = false;
+ }
+ url << encoded;
+ }
+ }
+ m_urlString=url.str();
+}
+
+
+std::string& LDAPUrl::percentEncode( const std::string &src,
+ std::string &dest,
+ int flags) const
+{
+ std::ostringstream o;
+ o.setf(std::ios::hex, std::ios::basefield);
+ o.setf(std::ios::uppercase);
+ o.unsetf(std::ios::showbase);
+ bool escape=false;
+ for ( std::string::const_iterator i = src.begin(); i != src.end(); i++ ){
+ switch(*i){
+ /* reserved */
+ case '?' :
+ escape = true;
+ break;
+ case ',' :
+ if ( flags & PCT_ENCFLAG_COMMA ) {
+ escape = true;
+ } else {
+ escape = false;
+ }
+ break;
+ case ':' :
+ case '/' :
+ if ( flags & PCT_ENCFLAG_SLASH ) {
+ escape = true;
+ } else {
+ escape = false;
+ }
+ break;
+ case '#' :
+ case '[' :
+ case ']' :
+ case '@' :
+ case '!' :
+ case '$' :
+ case '&' :
+ case '\'' :
+ case '(' :
+ case ')' :
+ case '*' :
+ case '+' :
+ case ';' :
+ case '=' :
+ /* unreserved */
+ case '-' :
+ case '.' :
+ case '_' :
+ case '~' :
+ escape = false;
+ break;
+ default :
+ if ( std::isalnum(*i) ) {
+ escape = false;
+ } else {
+ escape = true;
+ }
+ break;
+ }
+ if ( escape ) {
+ o << "%" << std::setw(2) << std::setfill('0') << (int)(unsigned char)*i ;
+ } else {
+ o.put(*i);
+ }
+ }
+ dest = o.str();
+ return dest;
+}
+
+const code2string_s LDAPUrlException::code2string[] = {
+ { INVALID_SCHEME, "Invalid URL Scheme" },
+ { INVALID_PORT, "Invalid Port in Url" },
+ { INVALID_SCOPE, "Invalid Search Scope in Url" },
+ { INVALID_URL, "Invalid LDAP Url" },
+ { URL_DECODING_ERROR, "Url-decoding Error" },
+ { 0, 0 }
+};
+
+LDAPUrlException::LDAPUrlException( int code, const std::string &msg) :
+ m_code(code), m_addMsg(msg) {}
+
+int LDAPUrlException::getCode() const
+{
+ return m_code;
+}
+
+const std::string LDAPUrlException::getAdditionalInfo() const
+{
+ return m_addMsg;
+}
+
+const std::string LDAPUrlException::getErrorMessage() const
+{
+ for ( int i = 0; code2string[i].string != 0; i++ ) {
+ if ( code2string[i].code == m_code ) {
+ return std::string(code2string[i].string);
+ }
+ }
+ return "";
+
+}
diff --git a/contrib/ldapc++/src/LDAPUrl.h b/contrib/ldapc++/src/LDAPUrl.h
new file mode 100644
index 0000000..9c1f3e3
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPUrl.h
@@ -0,0 +1,207 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+
+#ifndef LDAP_URL_H
+#define LDAP_URL_H
+
+#include <StringList.h>
+
+class LDAPUrlException;
+/**
+ * This class is used to analyze and store LDAP-Urls as returned by a
+ * LDAP-Server as Referrals and Search References. LDAP-URLs are defined
+ * in RFC1959 and have the following format: <BR>
+ * <code>
+ * ldap://host:port/baseDN[?attr[?scope[?filter]]] <BR>
+ * </code>
+ */
+class LDAPUrl{
+
+ public :
+ /**
+ * Create a new object from a string that contains a LDAP-Url
+ * @param url The URL String
+ */
+ LDAPUrl(const std::string &url="");
+
+ /**
+ * Destructor
+ */
+ ~LDAPUrl();
+
+ /**
+ * @return The part of the URL that is representing the network
+ * port
+ */
+ int getPort() const;
+
+ /**
+ * Set the port value of the URL
+ * @param dn The port value
+ */
+ void setPort(int port);
+
+ /**
+ * @return The scope part of the URL is returned.
+ */
+ int getScope() const;
+
+ /**
+ * Set the Scope part of the URL
+ * @param scope The new scope
+ */
+ void setScope(const std::string& scope);
+
+ /**
+ * @return The complete URL as a string
+ */
+ const std::string& getURLString() const;
+
+ /**
+ * Set the URL member attribute
+ * @param url The URL String
+ */
+ void setURLString(const std::string &url);
+
+ /**
+ * @return The hostname or IP-Address of the destination host.
+ */
+ const std::string& getHost() const;
+
+ /**
+ * Set the Host part of the URL
+ * @param host The new host part
+ */
+ void setHost( const std::string &host);
+
+ /**
+ * @return The Protocol Scheme of the URL.
+ */
+ const std::string& getScheme() const;
+
+ /**
+ * Set the Protocol Scheme of the URL
+ * @param host The Protocol scheme. Allowed values are
+ * ldap,ldapi,ldaps and cldap
+ */
+ void setScheme( const std::string &scheme );
+
+ /**
+ * @return The Base-DN part of the URL
+ */
+ const std::string& getDN() const;
+
+ /**
+ * Set the DN part of the URL
+ * @param dn The new DN part
+ */
+ void setDN( const std::string &dn);
+
+
+ /**
+ * @return The Filter part of the URL
+ */
+ const std::string& getFilter() const;
+
+ /**
+ * Set the Filter part of the URL
+ * @param filter The new Filter
+ */
+ void setFilter( const std::string &filter);
+
+ /**
+ * @return The List of attributes that was in the URL
+ */
+ const StringList& getAttrs() const;
+
+ /**
+ * Set the Attributes part of the URL
+ * @param attrs StringList containing the List of Attributes
+ */
+ void setAttrs( const StringList &attrs);
+ void setExtensions( const StringList &ext);
+ const StringList& getExtensions() const;
+
+ /**
+ * Percent-decode a string
+ * @param src The string that is to be decoded
+ * @param dest The decoded result string
+ */
+ void percentDecode( const std::string& src, std::string& dest );
+
+ /**
+ * Percent-encoded a string
+ * @param src The string that is to be encoded
+ * @param dest The encoded result string
+ * @param flags
+ */
+ std::string& percentEncode( const std::string& src,
+ std::string& dest,
+ int flags=0 ) const;
+
+ protected :
+ /**
+ * Split the url string that is associated with this Object into
+ * it components. The components of the URL can be access via the
+ * get...() methods.
+ * (this function is mostly for internal use and gets called
+ * automatically whenever necessary)
+ */
+ void parseUrl();
+
+ /**
+ * Generate an URL string from the components that were set with
+ * the various set...() methods
+ * (this function is mostly for internal use and gets called
+ * automatically whenever necessary)
+ */
+ void components2Url() const;
+
+ void string2list(const std::string &src, StringList& sl,
+ bool percentDecode=false);
+
+ protected :
+ mutable bool regenerate;
+ int m_Port;
+ int m_Scope;
+ std::string m_Host;
+ std::string m_DN;
+ std::string m_Filter;
+ StringList m_Attrs;
+ StringList m_Extensions;
+ mutable std::string m_urlString;
+ std::string m_Scheme;
+ enum mode { base, attrs, scope, filter, extensions };
+};
+
+/// @cond
+struct code2string_s {
+ int code;
+ const char* string;
+};
+/// @endcond
+
+class LDAPUrlException {
+ public :
+ LDAPUrlException(int code, const std::string &msg="" );
+
+ int getCode() const;
+ const std::string getErrorMessage() const;
+ const std::string getAdditionalInfo() const;
+
+ static const int INVALID_SCHEME = 1;
+ static const int INVALID_PORT = 2;
+ static const int INVALID_SCOPE = 3;
+ static const int INVALID_URL = 4;
+ static const int URL_DECODING_ERROR = 5;
+ static const code2string_s code2string[];
+
+ private:
+ int m_code;
+ std::string m_addMsg;
+};
+#endif //LDAP_URL_H
diff --git a/contrib/ldapc++/src/LDAPUrlList.cpp b/contrib/ldapc++/src/LDAPUrlList.cpp
new file mode 100644
index 0000000..6e4e921
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPUrlList.cpp
@@ -0,0 +1,57 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "LDAPUrlList.h"
+#include <assert.h>
+#include "debug.h"
+
+using namespace std;
+
+LDAPUrlList::LDAPUrlList(){
+ DEBUG(LDAP_DEBUG_CONSTRUCT," LDAPUrlList::LDAPUrlList()" << endl);
+ m_urls=LDAPUrlList::ListType();
+}
+
+LDAPUrlList::LDAPUrlList(const LDAPUrlList& urls){
+ DEBUG(LDAP_DEBUG_CONSTRUCT," LDAPUrlList::LDAPUrlList(&)" << endl);
+ m_urls = urls.m_urls;
+}
+
+
+LDAPUrlList::LDAPUrlList(char** url){
+ DEBUG(LDAP_DEBUG_CONSTRUCT," LDAPUrlList::LDAPUrlList()" << endl);
+ char** i;
+ assert(url);
+ for(i = url; *i != 0; i++){
+ add(LDAPUrl(*i));
+ }
+}
+
+LDAPUrlList::~LDAPUrlList(){
+ DEBUG(LDAP_DEBUG_DESTROY," LDAPUrlList::~LDAPUrlList()" << endl);
+ m_urls.clear();
+}
+
+size_t LDAPUrlList::size() const{
+ return m_urls.size();
+}
+
+bool LDAPUrlList::empty() const{
+ return m_urls.empty();
+}
+
+LDAPUrlList::const_iterator LDAPUrlList::begin() const{
+ return m_urls.begin();
+}
+
+LDAPUrlList::const_iterator LDAPUrlList::end() const{
+ return m_urls.end();
+}
+
+void LDAPUrlList::add(const LDAPUrl& url){
+ m_urls.push_back(url);
+}
+
diff --git a/contrib/ldapc++/src/LDAPUrlList.h b/contrib/ldapc++/src/LDAPUrlList.h
new file mode 100644
index 0000000..0b4f27c
--- /dev/null
+++ b/contrib/ldapc++/src/LDAPUrlList.h
@@ -0,0 +1,78 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDAP_URL_LIST_H
+#define LDAP_URL_LIST_H
+
+#include <list>
+#include <LDAPUrl.h>
+
+/**
+ * This container class is used to store multiple LDAPUrl-objects.
+ */
+class LDAPUrlList{
+ typedef std::list<LDAPUrl> ListType;
+
+ public:
+ typedef ListType::const_iterator const_iterator;
+
+ /**
+ * Constructs an empty list.
+ */
+ LDAPUrlList();
+
+ /**
+ * Copy-constructor
+ */
+ LDAPUrlList(const LDAPUrlList& urls);
+
+ /**
+ * For internal use only
+ *
+ * This constructor is used by the library internally to create a
+ * std::list of URLs from a array of C-strings that was return by
+ * the C-API
+ */
+ LDAPUrlList(char** urls);
+
+ /**
+ * Destructor
+ */
+ ~LDAPUrlList();
+
+ /**
+ * @return The number of LDAPUrl-objects that are currently
+ * stored in this list.
+ */
+ size_t size() const;
+
+ /**
+ * @return true if there are zero LDAPUrl-objects currently
+ * stored in this list.
+ */
+ bool empty() const;
+
+ /**
+ * @return A iterator that points to the first element of the list.
+ */
+ const_iterator begin() const;
+
+ /**
+ * @return A iterator that points to the element after the last
+ * element of the list.
+ */
+ const_iterator end() const;
+
+ /**
+ * Adds one element to the end of the list.
+ * @param attr The attribute to add to the list.
+ */
+ void add(const LDAPUrl& url);
+
+ private :
+ ListType m_urls;
+};
+#endif //LDAP_URL_LIST_H
diff --git a/contrib/ldapc++/src/LdifReader.cpp b/contrib/ldapc++/src/LdifReader.cpp
new file mode 100644
index 0000000..207806d
--- /dev/null
+++ b/contrib/ldapc++/src/LdifReader.cpp
@@ -0,0 +1,350 @@
+// $OpenLDAP$
+/*
+ * Copyright 2008-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "LdifReader.h"
+#include "LDAPMessage.h"
+#include "LDAPEntry.h"
+#include "LDAPAttributeList.h"
+#include "LDAPAttribute.h"
+#include "LDAPUrl.h"
+#include "debug.h"
+
+#include <string>
+#include <sstream>
+#include <stdexcept>
+
+#include <sasl/saslutil.h> // For base64 routines
+
+typedef std::pair<std::string, std::string> stringpair;
+
+LdifReader::LdifReader( std::istream &input )
+ : m_ldifstream(input), m_lineNumber(0)
+{
+ DEBUG(LDAP_DEBUG_TRACE, "<> LdifReader::LdifReader()" << std::endl);
+ this->m_version = 0;
+ // read the first record to find out version and type of the LDIF
+ this->readNextRecord(true);
+ this->m_currentIsFirst = true;
+}
+
+int LdifReader::readNextRecord( bool first )
+{
+ DEBUG(LDAP_DEBUG_TRACE, "-> LdifReader::readRecord()" << std::endl);
+ std::string line;
+ std::string type;
+ std::string value;
+ int numLine = 0;
+ int recordType = 0;
+
+ if ( (! first) && this->m_currentIsFirst == true )
+ {
+ this->m_currentIsFirst = false;
+ return m_curRecType;
+ }
+
+ m_currentRecord.clear();
+
+ while ( !this->getLdifLine(line) )
+ {
+ DEBUG(LDAP_DEBUG_TRACE, " Line: " << line << std::endl );
+
+ // skip comments and empty lines between entries
+ if ( line[0] == '#' || ( numLine == 0 && line.size() == 0 ) )
+ {
+ DEBUG(LDAP_DEBUG_TRACE, "skipping empty line or comment" << std::endl );
+ continue;
+ }
+ if ( line.size() == 0 )
+ {
+ // End of Entry
+ break;
+ }
+
+ this->splitLine(line, type, value);
+
+ if ( numLine == 0 )
+ {
+ if ( type == "version" )
+ {
+ std::istringstream valuestream(value);
+ valuestream >> this->m_version;
+ if ( this->m_version != 1 ) // there is no other Version than LDIFv1
+ {
+ std::ostringstream err;
+ err << "Line " << this->m_lineNumber
+ << ": Unsupported LDIF Version";
+ throw( std::runtime_error(err.str()) );
+ }
+ continue;
+ }
+ if ( type == "dn" ) // Record should start with the DN ...
+ {
+ DEBUG(LDAP_DEBUG_TRACE, " Record DN:" << value << std::endl);
+ }
+ else if ( type == "include" ) // ... or it might be an "include" line
+ {
+ DEBUG(LDAP_DEBUG_TRACE, " Include directive: " << value << std::endl);
+ if ( this->m_version == 1 )
+ {
+ std::ostringstream err;
+ err << "Line " << this->m_lineNumber
+ << ": \"include\" not allowed in LDIF version 1.";
+ throw( std::runtime_error(err.str()) );
+ }
+ else
+ {
+ std::ostringstream err;
+ err << "Line " << this->m_lineNumber
+ << ": \"include\" not yet supported.";
+ throw( std::runtime_error(err.str()) );
+ }
+ }
+ else
+ {
+ DEBUG(LDAP_DEBUG_TRACE, " Record doesn't start with a DN"
+ << std::endl);
+ std::ostringstream err;
+ err << "Line " << this->m_lineNumber
+ << ": LDIF record does not start with a DN.";
+ throw( std::runtime_error(err.str()) );
+ }
+ }
+ if ( numLine == 1 ) // might contain "changtype" to indicate a change request
+ {
+ if ( type == "changetype" )
+ {
+ if ( first )
+ {
+ this->m_ldifTypeRequest = true;
+ }
+ else if (! this->m_ldifTypeRequest )
+ {
+ // Change Request in Entry record LDIF, should we accept it?
+ std::ostringstream err;
+ err << "Line " << this->m_lineNumber
+ << ": Change Request in an entry-only LDIF.";
+ throw( std::runtime_error(err.str()) );
+ }
+ if ( value == "modify" )
+ {
+ recordType = LDAPMsg::MODIFY_REQUEST;
+ }
+ else if ( value == "add" )
+ {
+ recordType = LDAPMsg::ADD_REQUEST;
+ }
+ else if ( value == "delete" )
+ {
+ recordType = LDAPMsg::DELETE_REQUEST;
+ }
+ else if ( value == "modrdn" )
+ {
+ recordType = LDAPMsg::MODRDN_REQUEST;
+ }
+ else
+ {
+ DEBUG(LDAP_DEBUG_TRACE, " Unknown change request <"
+ << value << ">" << std::endl);
+ std::ostringstream err;
+ err << "Line " << this->m_lineNumber
+ << ": Unknown changetype: \"" << value << "\".";
+ throw( std::runtime_error(err.str()) );
+ }
+ }
+ else
+ {
+ if ( first )
+ {
+ this->m_ldifTypeRequest = false;
+ }
+ else if (this->m_ldifTypeRequest )
+ {
+ // Entry record in Change record LDIF, should we accept
+ // it (e.g. as AddRequest)?
+ }
+ recordType = LDAPMsg::SEARCH_ENTRY;
+ }
+ }
+ m_currentRecord.push_back( stringpair(type, value) );
+ numLine++;
+ }
+ DEBUG(LDAP_DEBUG_TRACE, "<- LdifReader::readRecord() return: "
+ << recordType << std::endl);
+ m_curRecType = recordType;
+ return recordType;
+}
+
+LDAPEntry LdifReader::getEntryRecord()
+{
+ std::list<stringpair>::const_iterator i = m_currentRecord.begin();
+ if ( m_curRecType != LDAPMsg::SEARCH_ENTRY )
+ {
+ throw( std::runtime_error( "The LDIF record: '" + i->second +
+ "' is not a valid LDAP Entry" ));
+ }
+ LDAPEntry resEntry(i->second);
+ i++;
+ LDAPAttribute curAttr(i->first);
+ LDAPAttributeList *curAl = new LDAPAttributeList();
+ for ( ; i != m_currentRecord.end(); i++ )
+ {
+ if ( i->first == curAttr.getName() )
+ {
+ curAttr.addValue(i->second);
+ }
+ else
+ {
+ const LDAPAttribute* existing = curAl->getAttributeByName( i->first );
+ if ( existing )
+ {
+ // Attribute exists already (handle gracefully)
+ curAl->addAttribute( curAttr );
+ curAttr = LDAPAttribute( *existing );
+ curAttr.addValue(i->second);
+ curAl->delAttribute( i->first );
+ }
+ else
+ {
+ curAl->addAttribute( curAttr );
+ curAttr = LDAPAttribute( i->first, i->second );
+ }
+ }
+ }
+ curAl->addAttribute( curAttr );
+ resEntry.setAttributes( curAl );
+ return resEntry;
+}
+
+int LdifReader::getLdifLine(std::string &ldifline)
+{
+ DEBUG(LDAP_DEBUG_TRACE, "-> LdifReader::getLdifLine()" << std::endl);
+
+ this->m_lineNumber++;
+ if ( ! getline(m_ldifstream, ldifline) )
+ {
+ return -1;
+ }
+ while ( m_ldifstream &&
+ (m_ldifstream.peek() == ' ' || m_ldifstream.peek() == '\t'))
+ {
+ std::string cat;
+ m_ldifstream.ignore();
+ getline(m_ldifstream, cat);
+ ldifline += cat;
+ this->m_lineNumber++;
+ }
+
+ DEBUG(LDAP_DEBUG_TRACE, "<- LdifReader::getLdifLine()" << std::endl);
+ return 0;
+}
+
+void LdifReader::splitLine(
+ const std::string& line,
+ std::string &type,
+ std::string &value) const
+{
+ std::string::size_type pos = line.find(':');
+ if ( pos == std::string::npos )
+ {
+ DEBUG(LDAP_DEBUG_ANY, "Invalid LDIF line. No `:` separator"
+ << std::endl );
+ std::ostringstream err;
+ err << "Line " << this->m_lineNumber << ": Invalid LDIF line. No `:` separator";
+ throw( std::runtime_error( err.str() ));
+ }
+
+ type = line.substr(0, pos);
+ if ( pos == line.size() )
+ {
+ // empty value
+ value = "";
+ return;
+ }
+
+ pos++;
+ char delim = line[pos];
+ if ( delim == ':' || delim == '<' )
+ {
+ pos++;
+ }
+
+ for( ; pos < line.size() && isspace(line[pos]); pos++ )
+ { /* empty */ }
+
+ value = line.substr(pos);
+
+ if ( delim == ':' )
+ {
+ // Base64 encoded value
+ DEBUG(LDAP_DEBUG_TRACE, " base64 encoded value" << std::endl );
+ char outbuf[value.size()];
+ int rc = sasl_decode64(value.c_str(), value.size(),
+ outbuf, value.size(), NULL);
+ if( rc == SASL_OK )
+ {
+ value = std::string(outbuf);
+ }
+ else if ( rc == SASL_BADPROT )
+ {
+ value = "";
+ DEBUG( LDAP_DEBUG_TRACE, " invalid base64 content" << std::endl );
+ std::ostringstream err;
+ err << "Line " << this->m_lineNumber << ": Can't decode Base64 data";
+ throw( std::runtime_error( err.str() ));
+ }
+ else if ( rc == SASL_BUFOVER )
+ {
+ value = "";
+ DEBUG( LDAP_DEBUG_TRACE, " not enough space in output buffer"
+ << std::endl );
+ std::ostringstream err;
+ err << "Line " << this->m_lineNumber
+ << ": Can't decode Base64 data. Buffer too small";
+ throw( std::runtime_error( err.str() ));
+ }
+ }
+ else if ( delim == '<' )
+ {
+ // URL value
+ DEBUG(LDAP_DEBUG_TRACE, " url value" << std::endl );
+ std::ostringstream err;
+ err << "Line " << this->m_lineNumber
+ << ": URLs are currently not supported";
+ throw( std::runtime_error( err.str() ));
+ }
+ else
+ {
+ // "normal" value
+ DEBUG(LDAP_DEBUG_TRACE, " string value" << std::endl );
+ }
+ DEBUG(LDAP_DEBUG_TRACE, " Type: <" << type << ">" << std::endl );
+ DEBUG(LDAP_DEBUG_TRACE, " Value: <" << value << ">" << std::endl );
+ return;
+}
+
+std::string LdifReader::readIncludeLine( const std::string& line ) const
+{
+ std::string::size_type pos = sizeof("file:") - 1;
+ std::string scheme = line.substr( 0, pos );
+ std::string file;
+
+ // only file:// URLs supported currently
+ if ( scheme != "file:" )
+ {
+ DEBUG( LDAP_DEBUG_TRACE, "unsupported scheme: " << scheme
+ << std::endl);
+ }
+ else if ( line[pos] == '/' )
+ {
+ if ( line[pos+1] == '/' )
+ {
+ pos += 2;
+ }
+ file = line.substr(pos, std::string::npos);
+ DEBUG( LDAP_DEBUG_TRACE, "target file: " << file << std::endl);
+ }
+ return file;
+}
diff --git a/contrib/ldapc++/src/LdifReader.h b/contrib/ldapc++/src/LdifReader.h
new file mode 100644
index 0000000..7fe6d4d
--- /dev/null
+++ b/contrib/ldapc++/src/LdifReader.h
@@ -0,0 +1,57 @@
+// $OpenLDAP$
+/*
+ * Copyright 2008-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDIF_READER_H
+#define LDIF_READER_H
+
+#include <LDAPEntry.h>
+#include <iosfwd>
+#include <list>
+
+typedef std::list< std::pair<std::string, std::string> > LdifRecord;
+class LdifReader
+{
+ public:
+ LdifReader( std::istream &input );
+
+ inline bool isEntryRecords() const
+ {
+ return !m_ldifTypeRequest;
+ }
+
+ inline bool isChangeRecords() const
+ {
+ return m_ldifTypeRequest;
+ }
+
+ inline int getVersion() const
+ {
+ return m_version;
+ }
+
+ LDAPEntry getEntryRecord();
+ int readNextRecord( bool first=false );
+ //LDAPRequest getChangeRecord();
+
+ private:
+ int getLdifLine(std::string &line);
+
+ void splitLine(const std::string& line,
+ std::string &type,
+ std::string &value ) const;
+
+ std::string readIncludeLine( const std::string &line) const;
+
+ std::istream &m_ldifstream;
+ LdifRecord m_currentRecord;
+ int m_version;
+ int m_curRecType;
+ int m_lineNumber;
+ bool m_ldifTypeRequest;
+ bool m_currentIsFirst;
+};
+
+#endif /* LDIF_READER_H */
diff --git a/contrib/ldapc++/src/LdifWriter.cpp b/contrib/ldapc++/src/LdifWriter.cpp
new file mode 100644
index 0000000..5dcbd41
--- /dev/null
+++ b/contrib/ldapc++/src/LdifWriter.cpp
@@ -0,0 +1,116 @@
+// $OpenLDAP$
+/*
+ * Copyright 2008-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "LdifWriter.h"
+#include "StringList.h"
+#include "LDAPAttribute.h"
+#include "debug.h"
+#include <sstream>
+#include <stdexcept>
+
+LdifWriter::LdifWriter( std::ostream& output, int version ) :
+ m_ldifstream(output), m_version(version), m_addSeparator(false)
+{
+ if ( version )
+ {
+ if ( version == 1 )
+ {
+ m_ldifstream << "version: " << version << std::endl;
+ m_addSeparator = true;
+ } else {
+ std::ostringstream err;
+ err << "Unsupported LDIF Version";
+ throw( std::runtime_error(err.str()) );
+ }
+ }
+
+}
+
+void LdifWriter::writeRecord(const LDAPEntry& le)
+{
+ std::ostringstream line;
+
+ if ( m_addSeparator )
+ {
+ m_ldifstream << std::endl;
+ } else {
+ m_addSeparator = true;
+ }
+
+ line << "dn: " << le.getDN();
+ this->breakline( line.str(), m_ldifstream );
+
+ const LDAPAttributeList *al = le.getAttributes();
+ LDAPAttributeList::const_iterator i = al->begin();
+ for ( ; i != al->end(); i++ )
+ {
+ StringList values = i->getValues();
+ StringList::const_iterator j = values.begin();
+ for( ; j != values.end(); j++)
+ {
+ // clear output stream
+ line.str("");
+ line << i->getName() << ": " << *j;
+ this->breakline( line.str(), m_ldifstream );
+ }
+ }
+}
+
+void LdifWriter::writeIncludeRecord( const std::string& target )
+{
+ DEBUG(LDAP_DEBUG_TRACE, "writeIncludeRecord: " << target << std::endl);
+ std::string scheme = target.substr( 0, sizeof("file:")-1 );
+
+ if ( m_version == 1 )
+ {
+ std::ostringstream err;
+ err << "\"include\" not allowed in LDIF version 1.";
+ throw( std::runtime_error(err.str()) );
+ }
+
+ if ( m_addSeparator )
+ {
+ m_ldifstream << std::endl;
+ } else {
+ m_addSeparator = true;
+ }
+
+ m_ldifstream << "include: ";
+ if ( scheme != "file:" )
+ {
+ m_ldifstream << "file://";
+ }
+
+ m_ldifstream << target << std::endl;
+}
+
+void LdifWriter::breakline( const std::string &line, std::ostream &out )
+{
+ std::string::size_type pos = 0;
+ std::string::size_type linelength = 76;
+ bool first = true;
+
+ if ( line.length() >= linelength )
+ {
+ while ( pos < line.length() )
+ {
+ if (! first )
+ {
+ out << " ";
+ }
+ out << line.substr(pos, linelength) << std::endl;
+ pos += linelength;
+ if ( first )
+ {
+ first = false;
+ linelength--; //account for the leading space
+ }
+ }
+ } else {
+ out << line << std::endl;
+ }
+}
+
diff --git a/contrib/ldapc++/src/LdifWriter.h b/contrib/ldapc++/src/LdifWriter.h
new file mode 100644
index 0000000..10c487d
--- /dev/null
+++ b/contrib/ldapc++/src/LdifWriter.h
@@ -0,0 +1,31 @@
+// $OpenLDAP$
+/*
+ * Copyright 2008-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef LDIF_WRITER_H
+#define LDIF_WRITER_H
+
+#include <LDAPEntry.h>
+#include <iosfwd>
+#include <list>
+
+class LdifWriter
+{
+ public:
+ LdifWriter( std::ostream& output, int version = 0 );
+ void writeRecord(const LDAPEntry& le);
+ void writeIncludeRecord(const std::string& target);
+
+ private:
+ void breakline( const std::string &line, std::ostream &out );
+
+ std::ostream& m_ldifstream;
+ int m_version;
+ bool m_addSeparator;
+
+};
+
+#endif /* LDIF_WRITER_H */
+
diff --git a/contrib/ldapc++/src/Makefile.am b/contrib/ldapc++/src/Makefile.am
new file mode 100644
index 0000000..83e3abc
--- /dev/null
+++ b/contrib/ldapc++/src/Makefile.am
@@ -0,0 +1,103 @@
+# $OpenLDAP$
+
+###
+# Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+##
+
+lib_LTLIBRARIES = libldapcpp.la
+
+libldapcpp_la_SOURCES = LDAPAddRequest.cpp \
+ LDAPAsynConnection.cpp \
+ LDAPAttribute.cpp \
+ LDAPAttributeList.cpp \
+ LDAPAttrType.cpp \
+ LDAPBindRequest.cpp \
+ LDAPCompareRequest.cpp \
+ LDAPConnection.cpp \
+ LDAPConstraints.cpp \
+ LDAPControl.cpp \
+ LDAPControlSet.cpp \
+ LDAPDeleteRequest.cpp \
+ LDAPEntry.cpp \
+ LDAPEntryList.cpp \
+ LDAPException.cpp \
+ LDAPExtRequest.cpp \
+ LDAPExtResult.cpp \
+ LDAPMessage.cpp \
+ LDAPMessageQueue.cpp \
+ LDAPModDNRequest.cpp \
+ LDAPModification.cpp \
+ LDAPModifyRequest.cpp \
+ LDAPModList.cpp \
+ LDAPObjClass.cpp \
+ LDAPRebind.cpp \
+ LDAPRebindAuth.cpp \
+ LDAPReferenceList.cpp \
+ LDAPRequest.cpp \
+ LDAPResult.cpp \
+ LDAPSaslBindResult.cpp \
+ LDAPSchema.cpp \
+ LDAPSearchReference.cpp \
+ LDAPSearchRequest.cpp \
+ LDAPSearchResult.cpp \
+ LDAPSearchResults.cpp \
+ LDAPUrl.cpp \
+ LDAPUrlList.cpp \
+ LdifReader.cpp \
+ LdifWriter.cpp \
+ SaslInteraction.cpp \
+ SaslInteractionHandler.cpp \
+ StringList.cpp \
+ TlsOptions.cpp
+
+include_HEADERS = LDAPAsynConnection.h \
+ LDAPAttribute.h \
+ LDAPAttributeList.h \
+ LDAPAttrType.h \
+ LDAPConnection.h \
+ LDAPConstraints.h \
+ LDAPControl.h \
+ LDAPControlSet.h \
+ LDAPEntry.h \
+ LDAPEntryList.h \
+ LDAPException.h \
+ LDAPExtResult.h \
+ LDAPMessage.h \
+ LDAPMessageQueue.h \
+ LDAPModification.h \
+ LDAPModList.h \
+ LDAPObjClass.h \
+ LDAPRebind.h \
+ LDAPRebindAuth.h \
+ LDAPReferenceList.h \
+ LDAPResult.h \
+ LDAPSaslBindResult.h \
+ LDAPSchema.h \
+ LDAPSearchReference.h \
+ LDAPSearchResult.h \
+ LDAPSearchResults.h \
+ LDAPUrl.h \
+ LDAPUrlList.h \
+ LdifReader.h \
+ LdifWriter.h \
+ SaslInteraction.h \
+ SaslInteractionHandler.h \
+ StringList.h \
+ TlsOptions.h
+
+noinst_HEADERS = ac/time.h \
+ debug.h \
+ LDAPAddRequest.h \
+ LDAPBindRequest.h \
+ LDAPCompareRequest.h \
+ LDAPDeleteRequest.h \
+ LDAPExtRequest.h \
+ LDAPModDNRequest.h \
+ LDAPModifyRequest.h \
+ LDAPRequest.h \
+ LDAPSearchRequest.h
+
+libldapcpp_la_LIBADD = -lldap -llber
+libldapcpp_la_LDFLAGS = -version-info @OPENLDAP_CPP_API_VERSION@
+
diff --git a/contrib/ldapc++/src/Makefile.in b/contrib/ldapc++/src/Makefile.in
new file mode 100644
index 0000000..aaa3cf0
--- /dev/null
+++ b/contrib/ldapc++/src/Makefile.in
@@ -0,0 +1,717 @@
+# Makefile.in generated by automake 1.11 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# $OpenLDAP$
+
+###
+# Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src
+DIST_COMMON = $(include_HEADERS) $(noinst_HEADERS) \
+ $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
+ $(srcdir)/config.h.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+libldapcpp_la_DEPENDENCIES =
+am_libldapcpp_la_OBJECTS = LDAPAddRequest.lo LDAPAsynConnection.lo \
+ LDAPAttribute.lo LDAPAttributeList.lo LDAPAttrType.lo \
+ LDAPBindRequest.lo LDAPCompareRequest.lo LDAPConnection.lo \
+ LDAPConstraints.lo LDAPControl.lo LDAPControlSet.lo \
+ LDAPDeleteRequest.lo LDAPEntry.lo LDAPEntryList.lo \
+ LDAPException.lo LDAPExtRequest.lo LDAPExtResult.lo \
+ LDAPMessage.lo LDAPMessageQueue.lo LDAPModDNRequest.lo \
+ LDAPModification.lo LDAPModifyRequest.lo LDAPModList.lo \
+ LDAPObjClass.lo LDAPRebind.lo LDAPRebindAuth.lo \
+ LDAPReferenceList.lo LDAPRequest.lo LDAPResult.lo \
+ LDAPSaslBindResult.lo LDAPSchema.lo LDAPSearchReference.lo \
+ LDAPSearchRequest.lo LDAPSearchResult.lo LDAPSearchResults.lo \
+ LDAPUrl.lo LDAPUrlList.lo LdifReader.lo LdifWriter.lo \
+ SaslInteraction.lo SaslInteractionHandler.lo StringList.lo \
+ TlsOptions.lo
+libldapcpp_la_OBJECTS = $(am_libldapcpp_la_OBJECTS)
+libldapcpp_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(libldapcpp_la_LDFLAGS) $(LDFLAGS) -o $@
+DEFAULT_INCLUDES = -I.@am__isrc@
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(libldapcpp_la_SOURCES)
+DIST_SOURCES = $(libldapcpp_la_SOURCES)
+HEADERS = $(include_HEADERS) $(noinst_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENLDAP_CPP_API_VERSION = @OPENLDAP_CPP_API_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+lib_LTLIBRARIES = libldapcpp.la
+libldapcpp_la_SOURCES = LDAPAddRequest.cpp \
+ LDAPAsynConnection.cpp \
+ LDAPAttribute.cpp \
+ LDAPAttributeList.cpp \
+ LDAPAttrType.cpp \
+ LDAPBindRequest.cpp \
+ LDAPCompareRequest.cpp \
+ LDAPConnection.cpp \
+ LDAPConstraints.cpp \
+ LDAPControl.cpp \
+ LDAPControlSet.cpp \
+ LDAPDeleteRequest.cpp \
+ LDAPEntry.cpp \
+ LDAPEntryList.cpp \
+ LDAPException.cpp \
+ LDAPExtRequest.cpp \
+ LDAPExtResult.cpp \
+ LDAPMessage.cpp \
+ LDAPMessageQueue.cpp \
+ LDAPModDNRequest.cpp \
+ LDAPModification.cpp \
+ LDAPModifyRequest.cpp \
+ LDAPModList.cpp \
+ LDAPObjClass.cpp \
+ LDAPRebind.cpp \
+ LDAPRebindAuth.cpp \
+ LDAPReferenceList.cpp \
+ LDAPRequest.cpp \
+ LDAPResult.cpp \
+ LDAPSaslBindResult.cpp \
+ LDAPSchema.cpp \
+ LDAPSearchReference.cpp \
+ LDAPSearchRequest.cpp \
+ LDAPSearchResult.cpp \
+ LDAPSearchResults.cpp \
+ LDAPUrl.cpp \
+ LDAPUrlList.cpp \
+ LdifReader.cpp \
+ LdifWriter.cpp \
+ SaslInteraction.cpp \
+ SaslInteractionHandler.cpp \
+ StringList.cpp \
+ TlsOptions.cpp
+
+include_HEADERS = LDAPAsynConnection.h \
+ LDAPAttribute.h \
+ LDAPAttributeList.h \
+ LDAPAttrType.h \
+ LDAPConnection.h \
+ LDAPConstraints.h \
+ LDAPControl.h \
+ LDAPControlSet.h \
+ LDAPEntry.h \
+ LDAPEntryList.h \
+ LDAPException.h \
+ LDAPExtResult.h \
+ LDAPMessage.h \
+ LDAPMessageQueue.h \
+ LDAPModification.h \
+ LDAPModList.h \
+ LDAPObjClass.h \
+ LDAPRebind.h \
+ LDAPRebindAuth.h \
+ LDAPReferenceList.h \
+ LDAPResult.h \
+ LDAPSaslBindResult.h \
+ LDAPSchema.h \
+ LDAPSearchReference.h \
+ LDAPSearchResult.h \
+ LDAPSearchResults.h \
+ LDAPUrl.h \
+ LDAPUrlList.h \
+ LdifReader.h \
+ LdifWriter.h \
+ SaslInteraction.h \
+ SaslInteractionHandler.h \
+ StringList.h \
+ TlsOptions.h
+
+noinst_HEADERS = ac/time.h \
+ debug.h \
+ LDAPAddRequest.h \
+ LDAPBindRequest.h \
+ LDAPCompareRequest.h \
+ LDAPDeleteRequest.h \
+ LDAPExtRequest.h \
+ LDAPModDNRequest.h \
+ LDAPModifyRequest.h \
+ LDAPRequest.h \
+ LDAPSearchRequest.h
+
+libldapcpp_la_LIBADD = -lldap -llber
+libldapcpp_la_LDFLAGS = -version-info @OPENLDAP_CPP_API_VERSION@
+all: config.h
+ $(MAKE) $(AM_MAKEFLAGS) all-am
+
+.SUFFIXES:
+.SUFFIXES: .cpp .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+config.h: stamp-h1
+ @if test ! -f $@; then \
+ rm -f stamp-h1; \
+ $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \
+ else :; fi
+
+stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
+ @rm -f stamp-h1
+ cd $(top_builddir) && $(SHELL) ./config.status src/config.h
+$(srcdir)/config.h.in: $(am__configure_deps)
+ ($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+ rm -f stamp-h1
+ touch $@
+
+distclean-hdr:
+ -rm -f config.h stamp-h1
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libldapcpp.la: $(libldapcpp_la_OBJECTS) $(libldapcpp_la_DEPENDENCIES)
+ $(libldapcpp_la_LINK) -rpath $(libdir) $(libldapcpp_la_OBJECTS) $(libldapcpp_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPAddRequest.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPAsynConnection.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPAttrType.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPAttribute.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPAttributeList.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPBindRequest.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPCompareRequest.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPConnection.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPConstraints.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPControl.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPControlSet.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPDeleteRequest.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPEntry.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPEntryList.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPException.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPExtRequest.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPExtResult.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPMessage.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPMessageQueue.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPModDNRequest.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPModList.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPModification.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPModifyRequest.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPObjClass.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPRebind.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPRebindAuth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPReferenceList.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPRequest.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPResult.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPSaslBindResult.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPSchema.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPSearchReference.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPSearchRequest.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPSearchResult.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPSearchResults.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPUrl.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LDAPUrlList.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LdifReader.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LdifWriter.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SaslInteraction.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/SaslInteractionHandler.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/StringList.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TlsOptions.Plo@am__quote@
+
+.cpp.o:
+@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
+
+.cpp.obj:
+@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cpp.lo:
+@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-includeHEADERS: $(include_HEADERS)
+ @$(NORMAL_INSTALL)
+ test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \
+ done
+
+uninstall-includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(includedir)" && rm -f $$files
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS) config.h
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-hdr distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-includeHEADERS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES
+
+.MAKE: all install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool ctags distclean \
+ distclean-compile distclean-generic distclean-hdr \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am \
+ install-includeHEADERS install-info install-info-am \
+ install-libLTLIBRARIES install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-includeHEADERS \
+ uninstall-libLTLIBRARIES
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/contrib/ldapc++/src/SaslInteraction.cpp b/contrib/ldapc++/src/SaslInteraction.cpp
new file mode 100644
index 0000000..a4ad187
--- /dev/null
+++ b/contrib/ldapc++/src/SaslInteraction.cpp
@@ -0,0 +1,44 @@
+// $OpenLDAP$
+/*
+ * Copyright 2007-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include <SaslInteraction.h>
+#include <iostream>
+#include "debug.h"
+
+SaslInteraction::SaslInteraction( sasl_interact_t *interact ) :
+ m_interact(interact) {}
+
+SaslInteraction::~SaslInteraction()
+{
+ DEBUG(LDAP_DEBUG_TRACE, "SaslInteraction::~SaslInteraction()" << std::endl);
+}
+
+unsigned long SaslInteraction::getId() const
+{
+ return m_interact->id;
+}
+
+const std::string SaslInteraction::getPrompt() const
+{
+ return std::string(m_interact->prompt);
+}
+
+const std::string SaslInteraction::getChallenge() const
+{
+ return std::string(m_interact->challenge);
+}
+
+const std::string SaslInteraction::getDefaultResult() const
+{
+ return std::string(m_interact->defresult);
+}
+
+void SaslInteraction::setResult(const std::string &res)
+{
+ m_result = res;
+ m_interact->result = m_result.data();
+ m_interact->len = m_result.size();
+}
diff --git a/contrib/ldapc++/src/SaslInteraction.h b/contrib/ldapc++/src/SaslInteraction.h
new file mode 100644
index 0000000..2033a41
--- /dev/null
+++ b/contrib/ldapc++/src/SaslInteraction.h
@@ -0,0 +1,29 @@
+// $OpenLDAP$
+/*
+ * Copyright 2007-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef SASL_INTERACTION_H
+#define SASL_INTERACTION_H
+
+#include <string>
+#include <sasl/sasl.h>
+
+class SaslInteraction {
+ public:
+ SaslInteraction( sasl_interact_t *interact );
+ ~SaslInteraction();
+ unsigned long getId() const;
+ const std::string getPrompt() const;
+ const std::string getChallenge() const;
+ const std::string getDefaultResult() const;
+
+ void setResult(const std::string &res);
+
+ private:
+ sasl_interact_t *m_interact;
+ std::string m_result;
+
+};
+#endif /* SASL_INTERACTION_H */
diff --git a/contrib/ldapc++/src/SaslInteractionHandler.cpp b/contrib/ldapc++/src/SaslInteractionHandler.cpp
new file mode 100644
index 0000000..cb5d37d
--- /dev/null
+++ b/contrib/ldapc++/src/SaslInteractionHandler.cpp
@@ -0,0 +1,101 @@
+// $OpenLDAP$
+/*
+ * Copyright 2007-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include <iostream>
+#include <iomanip>
+#include <limits>
+#include "config.h"
+
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#include <unistd.h>
+#include <cstdio>
+#endif
+
+#include <string.h>
+#include "SaslInteractionHandler.h"
+#include "SaslInteraction.h"
+#include "debug.h"
+
+void DefaultSaslInteractionHandler::handleInteractions(
+ const std::list<SaslInteraction*> &cb )
+{
+ DEBUG(LDAP_DEBUG_TRACE, "DefaultSaslInteractionHandler::handleCallbacks()"
+ << std::endl );
+ std::list<SaslInteraction*>::const_iterator i;
+
+ for (i = cb.begin(); i != cb.end(); i++ ) {
+ bool noecho;
+
+ cleanupList.push_back(*i);
+
+ std::cout << (*i)->getPrompt();
+ if (! (*i)->getDefaultResult().empty() ) {
+ std::cout << "(" << (*i)->getDefaultResult() << ")" ;
+ }
+ std:: cout << ": ";
+
+ switch ( (*i)->getId() ) {
+ case SASL_CB_PASS:
+ case SASL_CB_ECHOPROMPT:
+ noecho = true;
+ noecho = true;
+ break;
+ default:
+ noecho = false;
+ break;
+ }
+#ifdef HAVE_TERMIOS_H
+ /* turn off terminal echo if needed */
+ struct termios old_attr;
+ if ( noecho ) {
+ struct termios attr;
+ if (tcgetattr(STDIN_FILENO, &attr) < 0) {
+ perror("tcgetattr");
+ }
+
+ /* save terminal attributes */
+ memcpy(&old_attr, &attr, sizeof(attr));
+
+ /* disable echo */
+ attr.c_lflag &= ~(ECHO);
+
+ /* write attributes to terminal */
+ if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr) < 0) {
+ perror("tcsetattr");
+ }
+ }
+#endif /* HAVE_TERMIOS_H */
+ std::string input;
+ std::cin >> std::noskipws >> input;
+ std::cin >> std::skipws;
+ (*i)->setResult(input);
+ if( std::cin.fail() ) {
+ std::cin.clear();
+ }
+ /* ignore the rest of the input line */
+ std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
+
+#ifdef HAVE_TERMIOS_H
+ /* restore terminal settings */
+ if ( noecho ) {
+ tcsetattr(STDIN_FILENO, TCSANOW, &old_attr);
+ std::cout << std::endl;
+ }
+#endif /* HAVE_TERMIOS_H */
+ }
+}
+
+DefaultSaslInteractionHandler::~DefaultSaslInteractionHandler()
+{
+ DEBUG(LDAP_DEBUG_TRACE, "DefaultSaslInteractionHandler::~DefaultSaslInteractionHandler()"
+ << std::endl );
+
+ std::list<SaslInteraction*>::const_iterator i;
+ for (i = cleanupList.begin(); i != cleanupList.end(); i++ ) {
+ delete(*i);
+ }
+}
diff --git a/contrib/ldapc++/src/SaslInteractionHandler.h b/contrib/ldapc++/src/SaslInteractionHandler.h
new file mode 100644
index 0000000..d356983
--- /dev/null
+++ b/contrib/ldapc++/src/SaslInteractionHandler.h
@@ -0,0 +1,27 @@
+// $OpenLDAP$
+/*
+ * Copyright 2007-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef SASL_INTERACTION_HANDLER_H
+#define SASL_INTERACTION_HANDLER_H
+#include <list>
+
+class SaslInteraction;
+
+class SaslInteractionHandler {
+ public:
+ virtual void handleInteractions( const std::list<SaslInteraction*> &cb )=0;
+ virtual ~SaslInteractionHandler() {}
+};
+
+class DefaultSaslInteractionHandler {
+ public:
+ virtual void handleInteractions( const std::list<SaslInteraction*> &cb );
+ virtual ~DefaultSaslInteractionHandler();
+
+ private:
+ std::list<SaslInteraction*> cleanupList;
+};
+#endif /* SASL_INTERACTION_HANDLER_H */
diff --git a/contrib/ldapc++/src/StringList.cpp b/contrib/ldapc++/src/StringList.cpp
new file mode 100644
index 0000000..c53f6a4
--- /dev/null
+++ b/contrib/ldapc++/src/StringList.cpp
@@ -0,0 +1,77 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include "StringList.h"
+#include "debug.h"
+
+#include <cstdlib>
+
+using namespace std;
+
+StringList::StringList(){
+}
+
+StringList::StringList(const StringList& sl){
+ m_data= StringList::ListType(sl.m_data);
+}
+
+StringList::StringList(char** values){
+ if(values == 0){
+ m_data=StringList::ListType();
+ }else{
+ char** i;
+ for(i=values; *i != 0; i++){
+ m_data.push_back(string(*i));
+ }
+ }
+}
+
+StringList::~StringList(){
+ DEBUG(LDAP_DEBUG_TRACE,"StringList::~StringList()" << endl);
+}
+
+char** StringList::toCharArray() const{
+ if(!empty()){
+ char** ret = (char**) malloc(sizeof(char*) * (size()+1));
+ StringList::const_iterator i;
+ int j=0;
+ for(i=begin(); i != end(); i++,j++){
+ ret[j]=(char*) malloc(sizeof(char) * (i->size()+1));
+ i->copy(ret[j],string::npos);
+ ret[j][i->size()]=0;
+ }
+ ret[size()]=0;
+ return ret;
+ }else{
+ return 0;
+ }
+}
+
+void StringList::add(const string& value){
+ m_data.push_back(value);
+}
+
+size_t StringList::size() const{
+ return m_data.size();
+}
+
+bool StringList::empty() const{
+ return m_data.empty();
+}
+
+StringList::const_iterator StringList::begin() const{
+ return m_data.begin();
+}
+
+StringList::const_iterator StringList::end() const{
+ return m_data.end();
+}
+
+
+void StringList::clear(){
+ m_data.clear();
+}
+
diff --git a/contrib/ldapc++/src/StringList.h b/contrib/ldapc++/src/StringList.h
new file mode 100644
index 0000000..95c8c8c
--- /dev/null
+++ b/contrib/ldapc++/src/StringList.h
@@ -0,0 +1,88 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef STRING_LIST_H
+#define STRING_LIST_H
+
+#include <string>
+#include <list>
+/**
+ * Container class to store multiple string-objects
+ */
+class StringList{
+ typedef std::list<std::string> ListType;
+
+ private:
+ ListType m_data;
+
+ public:
+ typedef ListType::const_iterator const_iterator;
+
+ /**
+ * Constructs an empty list.
+ */
+ StringList();
+
+ /**
+ * Copy-constructor
+ */
+ StringList(const StringList& sl);
+
+ /**
+ * For internal use only
+ *
+ * This constructor is used by the library internally to create a
+ * list of string from a array for c-Strings (char*)thar was
+ * returned by the C-API
+ */
+ StringList(char** values);
+
+ /**
+ * Destructor
+ */
+ ~StringList();
+
+ /**
+ * The methods converts the list to a 0-terminated array of
+ * c-Strings.
+ */
+ char** toCharArray() const;
+
+ /**
+ * Adds one element to the end of the list.
+ * @param attr The attribute to add to the list.
+ */
+ void add(const std::string& value);
+
+ /**
+ * @return The number of strings that are currently
+ * stored in this list.
+ */
+ size_t size() const;
+
+ /**
+ * @return true if there are zero strings currently
+ * stored in this list.
+ */
+ bool empty() const;
+
+ /**
+ * @return A iterator that points to the first element of the list.
+ */
+ const_iterator begin() const;
+
+ /**
+ * @return A iterator that points to the element after the last
+ * element of the list.
+ */
+ const_iterator end() const;
+
+ /**
+ * removes all elements from the list
+ */
+ void clear();
+};
+#endif //STRING_LIST_H
diff --git a/contrib/ldapc++/src/TlsOptions.cpp b/contrib/ldapc++/src/TlsOptions.cpp
new file mode 100644
index 0000000..609f127
--- /dev/null
+++ b/contrib/ldapc++/src/TlsOptions.cpp
@@ -0,0 +1,163 @@
+// $OpenLDAP$
+/*
+ * Copyright 2010-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#include <fstream>
+#include <sstream>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <cstring>
+#include "TlsOptions.h"
+#include "LDAPException.h"
+
+enum opttype {
+ INT=0,
+ STRING,
+ OTHER
+};
+
+typedef struct tls_optmap {
+ int optval;
+ opttype type;
+} tls_optmap_t;
+
+static tls_optmap_t optmap[] = {
+ { LDAP_OPT_X_TLS_CACERTFILE, STRING },
+ { LDAP_OPT_X_TLS_CACERTDIR, STRING },
+ { LDAP_OPT_X_TLS_CERTFILE, STRING },
+ { LDAP_OPT_X_TLS_KEYFILE, STRING },
+ { LDAP_OPT_X_TLS_REQUIRE_CERT, INT },
+ { LDAP_OPT_X_TLS_PROTOCOL_MIN, INT },
+ { LDAP_OPT_X_TLS_CIPHER_SUITE, STRING },
+ { LDAP_OPT_X_TLS_RANDOM_FILE, STRING },
+ { LDAP_OPT_X_TLS_CRLCHECK, INT },
+ { LDAP_OPT_X_TLS_DHFILE, STRING },
+ { LDAP_OPT_X_TLS_NEWCTX, INT }
+};
+#if 0 /* not implemented currently */
+ static const int TLS_CRLFILE /* GNUtls only */
+ static const int TLS_SSL_CTX /* OpenSSL SSL* */
+ static const int TLS_CONNECT_CB
+ static const int TLS_CONNECT_ARG
+#endif
+
+static void checkOpt( TlsOptions::tls_option opt, opttype type ) {
+ if ( opt < TlsOptions::CACERTFILE || opt >= TlsOptions::LASTOPT ){
+ throw( LDAPException( LDAP_PARAM_ERROR, "unknown Option" ) );
+ }
+
+ if ( optmap[opt].type != type ){
+ throw( LDAPException( LDAP_PARAM_ERROR, "not a string option" ) );
+ }
+}
+
+TlsOptions::TlsOptions() : m_ld(NULL) {}
+
+TlsOptions::TlsOptions( LDAP* ld ): m_ld(ld) { }
+
+void TlsOptions::setOption( tls_option opt, const std::string& value ) const {
+ checkOpt(opt, STRING);
+ switch(opt) {
+ case TlsOptions::CACERTFILE :
+ case TlsOptions::CERTFILE :
+ case TlsOptions::KEYFILE :
+ {
+ // check if the supplied file is actually readable
+ std::ifstream ifile(value.c_str());
+ if ( !ifile ) {
+ throw( LDAPException( LDAP_LOCAL_ERROR, "Unable to open the supplied file for reading" ) );
+ }
+ }
+ break;
+ case TlsOptions::CACERTDIR :
+ {
+ struct stat st;
+ std::ostringstream msg;
+ bool fail=false;
+ int err = stat(value.c_str(),&st);
+ if ( err ) {
+ msg << strerror(errno);
+ fail = true;
+ } else {
+ if ( !S_ISDIR(st.st_mode) ){
+ msg << "The supplied path is not a directory.";
+ fail = true;
+ }
+ }
+ if ( fail ) {
+ std::ostringstream errstr;
+ errstr << "Error while setting Certificate Directory (" << value << "): " << msg.str();
+ throw( LDAPException( LDAP_LOCAL_ERROR, errstr.str() ) );
+ }
+ }
+ break;
+ }
+ this->setOption( opt, value.empty() ? NULL : (void*) value.c_str() );
+}
+
+void TlsOptions::setOption( tls_option opt, int value ) const {
+ checkOpt(opt, INT);
+ this->setOption( opt, (void*) &value);
+}
+
+void TlsOptions::setOption( tls_option opt, void *value ) const {
+ int ret = ldap_set_option( m_ld, optmap[opt].optval, value);
+ if ( ret != LDAP_OPT_SUCCESS )
+ {
+ if ( ret != LDAP_OPT_ERROR ){
+ throw( LDAPException( ret ));
+ } else {
+ throw( LDAPException( LDAP_PARAM_ERROR, "error while setting TLS option" ) );
+ }
+ }
+ this->newCtx();
+}
+
+void TlsOptions::getOption( tls_option opt, void* value ) const {
+ int ret = ldap_get_option( m_ld, optmap[opt].optval, value);
+ if ( ret != LDAP_OPT_SUCCESS )
+ {
+ if ( ret != LDAP_OPT_ERROR ){
+ throw( LDAPException( ret ));
+ } else {
+ throw( LDAPException( LDAP_PARAM_ERROR, "error while reading TLS option" ) );
+ }
+ }
+}
+
+int TlsOptions::getIntOption( tls_option opt ) const {
+ int value;
+ checkOpt(opt, INT);
+ ldap_get_option( m_ld, optmap[opt].optval, (void*) &value);
+ return value;
+}
+
+std::string TlsOptions::getStringOption( tls_option opt ) const {
+ char *value;
+ checkOpt(opt, STRING);
+ ldap_get_option( m_ld, optmap[opt].optval, (void*) &value);
+ std::string strval;
+ if (value)
+ {
+ strval=std::string(value);
+ ldap_memfree(value);
+ }
+ return strval;
+}
+
+void TlsOptions::newCtx() const {
+ int val = 0;
+ int ret = ldap_set_option( m_ld, LDAP_OPT_X_TLS_NEWCTX, &val);
+ if ( ret != LDAP_OPT_SUCCESS )
+ {
+ if ( ret != LDAP_OPT_ERROR ){
+ throw( LDAPException( ret ));
+ } else {
+ throw( LDAPException( LDAP_LOCAL_ERROR, "error while renewing TLS context" ) );
+ }
+ }
+}
diff --git a/contrib/ldapc++/src/TlsOptions.h b/contrib/ldapc++/src/TlsOptions.h
new file mode 100644
index 0000000..41d6ee3
--- /dev/null
+++ b/contrib/ldapc++/src/TlsOptions.h
@@ -0,0 +1,162 @@
+// $OpenLDAP$
+/*
+ * Copyright 2010-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+#ifndef TLS_OPTIONS_H
+#define TLS_OPTIONS_H
+#include <string>
+#include <ldap.h>
+
+/**
+ * Class to access the global (and connection specific) TLS Settings
+ * To access the global TLS Settings just instantiate a TlsOption object
+ * using the default constructor.
+ *
+ * To access connection specific settings instantiate a TlsOption object
+ * through the getTlsOptions() method from the corresponding
+ * LDAPConnection/LDAPAsynConnection object.
+ *
+ */
+class TlsOptions {
+ public:
+
+ /**
+ * Available TLS Options
+ */
+ enum tls_option {
+ CACERTFILE=0,
+ CACERTDIR,
+ CERTFILE,
+ KEYFILE,
+ REQUIRE_CERT,
+ PROTOCOL_MIN,
+ CIPHER_SUITE,
+ RANDOM_FILE,
+ CRLCHECK,
+ DHFILE,
+ /// @cond
+ LASTOPT /* dummy */
+ /// @endcond
+ };
+
+ /**
+ * Possible Values for the REQUIRE_CERT option
+ */
+ enum verifyMode {
+ NEVER=0,
+ HARD,
+ DEMAND,
+ ALLOW,
+ TRY
+ };
+
+ /**
+ * Possible Values for the CRLCHECK option
+ */
+ enum crlMode {
+ CRL_NONE=0,
+ CRL_PEER,
+ CRL_ALL
+ };
+
+
+ /**
+ * Default constructor. Gives access to the global TlsSettings
+ */
+ TlsOptions();
+
+ /**
+ * Set string valued options.
+ * @param opt The following string valued options are available:
+ * - TlsOptions::CACERTFILE
+ * - TlsOptions::CACERTDIR
+ * - TlsOptions::CERTFILE
+ * - TlsOptions::KEYFILE
+ * - TlsOptions::CIPHER_SUITE
+ * - TlsOptions::RANDOM_FILE
+ * - TlsOptions::DHFILE
+ * @param value The value to apply to that option,
+ * - TlsOptions::CACERTFILE:
+ * The path to the file containing all recognized Certificate
+ * Authorities
+ * - TlsOptions::CACERTDIR:
+ * The path to a directory containing individual files of all
+ * recognized Certificate Authority certificates
+ * - TlsOptions::CERTFILE:
+ * The path to the client certificate
+ * - TlsOptions::KEYFILE:
+ * The path to the file containing the private key matching the
+ * Certificate that as configured with TlsOptions::CERTFILE
+ * - TlsOptions::CIPHER_SUITE
+ * Specifies the cipher suite and preference order
+ * - TlsOptions::RANDOM_FILE
+ * Specifies the file to obtain random bits from when
+ * /dev/[u]random is not available.
+ * - TlsOptions::DHFILE
+ * File containing DH parameters
+ */
+ void setOption(tls_option opt, const std::string& value) const;
+
+ /**
+ * Set integer valued options.
+ * @param opt The following string valued options are available:
+ * - TlsOptions::REQUIRE_CERT
+ * - TlsOptions::PROTOCOL_MIN
+ * - TlsOptions::CRLCHECK
+ * @param value The value to apply to that option,
+ * - TlsOptions::REQUIRE_CERT:
+ * Possible Values (For details see the ldap.conf(5) man-page):
+ * - TlsOptions::NEVER
+ * - TlsOptions::DEMAND
+ * - TlsOptions::ALLOW
+ * - TlsOptions::TRY
+ * - TlsOptions::PROTOCOL_MIN
+ * - TlsOptions::CRLCHECK
+ * Possible Values:
+ * - TlsOptions::CRL_NONE
+ * - TlsOptions::CRL_PEER
+ * - TlsOptions::CRL_ALL
+ */
+ void setOption(tls_option opt, int value) const;
+
+ /**
+ * Generic setOption variant. Generally you should prefer to use one
+ * of the other variants
+ */
+ void setOption(tls_option opt, void *value) const;
+
+ /**
+ * Read integer valued options
+ * @return Option value
+ * @throws LDAPException in case of error (invalid on non-integer
+ * valued option is requested)
+ */
+ int getIntOption(tls_option opt) const;
+
+ /**
+ * Read string valued options
+ * @return Option value
+ * @throws LDAPException in case of error (invalid on non-string
+ * valued option is requested)
+ */
+ std::string getStringOption(tls_option opt) const;
+
+ /**
+ * Read options value. Usually you should prefer to use either
+ * getIntOption() or getStringOption()
+ * @param value points to a buffer containing the option value
+ * @throws LDAPException in case of error (invalid on non-string
+ * valued option is requested)
+ */
+ void getOption(tls_option opt, void *value ) const;
+
+ private:
+ TlsOptions( LDAP* ld );
+ void newCtx() const;
+ LDAP *m_ld;
+
+ friend class LDAPAsynConnection;
+};
+
+#endif /* TLS_OPTIONS_H */
diff --git a/contrib/ldapc++/src/ac/time.h b/contrib/ldapc++/src/ac/time.h
new file mode 100644
index 0000000..cc89f70
--- /dev/null
+++ b/contrib/ldapc++/src/ac/time.h
@@ -0,0 +1,28 @@
+/* Generic time.h */
+/* $OpenLDAP$ */
+/*
+ * Copyright 1998-2022 The OpenLDAP Foundation, Redwood City, California, USA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted only
+ * as authorized by the OpenLDAP Public License. A copy of this
+ * license is available at http://www.OpenLDAP.org/license.html or
+ * in file LICENSE in the top-level directory of the distribution.
+ */
+
+#ifndef _AC_TIME_H
+#define _AC_TIME_H
+
+#ifdef TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#elif defined HAVE_SYS_TIME_H
+# include <sys/time.h>
+# ifdef HAVE_SYS_TIMEB_H
+# include <sys/timeb.h>
+# endif
+#else
+# include <time.h>
+#endif
+
+#endif /* _AC_TIME_H */
diff --git a/contrib/ldapc++/src/config.h.in b/contrib/ldapc++/src/config.h.in
new file mode 100644
index 0000000..3042472
--- /dev/null
+++ b/contrib/ldapc++/src/config.h.in
@@ -0,0 +1,70 @@
+/* src/config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <ldap.h> header file. */
+#undef HAVE_LDAP_H
+
+/* Define to 1 if you have the `resolv' library (-lresolv). */
+#undef HAVE_LIBRESOLV
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <termios.h> header file. */
+#undef HAVE_TERMIOS_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#undef TIME_WITH_SYS_TIME
+
+/* Version number of package */
+#undef VERSION
+
+/* Define to 1 ot enable debug logging */
+#undef WITH_DEBUG
diff --git a/contrib/ldapc++/src/debug.h b/contrib/ldapc++/src/debug.h
new file mode 100644
index 0000000..e80b760
--- /dev/null
+++ b/contrib/ldapc++/src/debug.h
@@ -0,0 +1,33 @@
+// $OpenLDAP$
+/*
+ * Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+ * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+ */
+
+#ifndef DEBUG_H
+#define DEBUG_H
+#include <iostream>
+#include "config.h"
+#define LDAP_DEBUG_NONE 0x0000
+#define LDAP_DEBUG_TRACE 0x0001
+#define LDAP_DEBUG_CONSTRUCT 0x0002
+#define LDAP_DEBUG_DESTROY 0x0004
+#define LDAP_DEBUG_PARAMETER 0x0008
+#define LDAP_DEBUG_ANY 0xffff
+
+#define DEBUGLEVEL LDAP_DEBUG_ANY
+
+#define PRINT_FILE \
+ std::cerr << "file: " __FILE__ << " line: " << __LINE__
+
+#ifdef WITH_DEBUG
+#define DEBUG(level, arg) \
+ if((level) & DEBUGLEVEL){ \
+ std::cerr << arg ; \
+ }
+#else
+#undef DEBUG
+#define DEBUG(level,arg)
+#endif //WITH_DEBUG
+
+#endif // DEBUG_H
diff --git a/contrib/ldapc++/src/stamp-h.in b/contrib/ldapc++/src/stamp-h.in
new file mode 100644
index 0000000..9788f70
--- /dev/null
+++ b/contrib/ldapc++/src/stamp-h.in
@@ -0,0 +1 @@
+timestamp
diff --git a/contrib/ldapc++/version.sh b/contrib/ldapc++/version.sh
new file mode 100755
index 0000000..1e52ade
--- /dev/null
+++ b/contrib/ldapc++/version.sh
@@ -0,0 +1,10 @@
+#! /bin/sh
+# $OpenLDAP$
+#
+# Copyright 2008-2022 The OpenLDAP Foundation. All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+DIR=`dirname $0`
+. $DIR/version.var
+
+echo OL_CPP_API_VERSION=$ol_cpp_api_current:$ol_cpp_api_revision:$ol_cpp_api_age
+echo OL_CPP_API_RELEASE=$ol_cpp_api_rel_major.$ol_cpp_api_rel_minor.$ol_cpp_api_rel_patch
diff --git a/contrib/ldapc++/version.var b/contrib/ldapc++/version.var
new file mode 100644
index 0000000..7692c6c
--- /dev/null
+++ b/contrib/ldapc++/version.var
@@ -0,0 +1,13 @@
+#! /bin/sh
+# $OpenLDAP$
+#
+# Copyright 2008-2022 The OpenLDAP Foundation. All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+
+ol_cpp_api_rel_major=0
+ol_cpp_api_rel_minor=0
+ol_cpp_api_rel_patch=0
+
+ol_cpp_api_current=0
+ol_cpp_api_revision=0
+ol_cpp_api_age=0
diff --git a/contrib/ldaptcl/CHANGES b/contrib/ldaptcl/CHANGES
new file mode 100644
index 0000000..7a48c09
--- /dev/null
+++ b/contrib/ldaptcl/CHANGES
@@ -0,0 +1,30 @@
+Package version 2.0:
+- Detects OpenLDAP 2.0 and builds correctly with it.
+- Increment major version to 2, library file to libldaptcl2.0.so.
+- Can now perform add/delete/replace modifications in a single command.
+- Replaced calls to TclX_WrongArgs with core Tcl_WrongNumArgs to reduce
+ dependency on Extended Tcl.
+- Wrap dereference search control with #ifdef LDAP_OPT_DEREF.
+- Deref during search should work.
+- Add protocol_version option to ldap init command.
+- Add LDAPTCL_PROTOCOL_VERSION_DEFAULT to allow specifying the default
+ protocol version used.
+- Add controlArray(timeout) to control timeouts during searches.
+- Add controlArray(cache) to control caching current search results.
+ (Experience has shown this to be not very useful or not working correctly.
+ Caching search results should probably be done in Ldaptcl rather than
+ letting the LDAP API do it.)
+- Add "compare" subcommand
+- Add experimental trap subcommand (undocumented -- use at your own risk).
+
+Package version 1.2:
+
+- Filter no longer a required controlArray member, defaults to objectclass=*.
+- Sets errorCode with LDAP macro string value (better to test than the more
+ human readable values).
+- Shorten minimum required characters for search scope definitions: now allows
+ "base", "one", and "sub". For the latter two, additional characters are
+ ignored.
+- Now compiles successfully with -devel branch.
+- Client cache management code enabled for OpenLDAP versions <= 1.2.4. (This
+ code is relatively untested and feedback is welcome.)
diff --git a/contrib/ldaptcl/COPYRIGHT b/contrib/ldaptcl/COPYRIGHT
new file mode 100644
index 0000000..74651ee
--- /dev/null
+++ b/contrib/ldaptcl/COPYRIGHT
@@ -0,0 +1,31 @@
+Copyright 1998-2022 The OpenLDAP Foundation. All rights reserved.
+
+COPYING RESTRICTIONS APPLY.
+
+See COPYRIGHT and LICENSE files in the top-level directory of this
+distribution (i.e., ../../COPYRIGHT and ../../LICENSE, respectively).
+
+---
+NeoSoft Tcl client extensions to Lightweight Directory Access Protocol.
+
+Copyright (c) 1998-1999 NeoSoft, Inc.
+All Rights Reserved.
+
+This software may be used, modified, copied, distributed, and sold,
+in both source and binary form provided that these copyrights are
+retained and their terms are followed.
+
+Under no circumstances are the authors or NeoSoft Inc. responsible
+for the proper functioning of this software, nor do the authors
+assume any liability for damages incurred with its use.
+
+Redistribution and use in source and binary forms are permitted
+provided that this notice is preserved and that due credit is given
+to NeoSoft, Inc.
+
+NeoSoft, Inc. may not be used to endorse or promote products derived
+from this software without specific prior written permission. This
+software is provided ``as is'' without express or implied warranty.
+
+Requests for permission may be sent to NeoSoft Inc, 1770 St. James Place,
+Suite 500, Houston, TX, 77056.
diff --git a/contrib/ldaptcl/Makefile.in b/contrib/ldaptcl/Makefile.in
new file mode 100644
index 0000000..8f2323c
--- /dev/null
+++ b/contrib/ldaptcl/Makefile.in
@@ -0,0 +1,196 @@
+#
+# This file is a Makefile for Neo, the NeoSoft extensions to Tcl.
+# If it has the name "Makefile.in" then it is a template for a
+# Makefile; to generate the actual Makefile, run "./configure",
+# which is a configuration script generated by the "autoconf" program
+# (constructs like "@foo@" will get replaced in the actual Makefile.
+#
+
+VERSION = @NEO_VERSION@
+LIBNAME = @NEO_SHARED_LIB_FILE@
+
+# Default top-level directories in which to install architecture-
+# specific files (exec_prefix) and machine-independent files such
+# as scripts (prefix). The values specified here may be overridden
+# at configure-time with the --exec-prefix and --prefix options
+# to the "configure" script.
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+# The following definition can be set to non-null for special systems
+# like AFS with replication. It allows the pathnames used for installation
+# to be different than those used for actually reference files at
+# run-time. DESTDIR is prepended to $prefix and $exec_prefix
+# when installing files.
+DESTDIR =
+
+# Directory in which to search for tcl libraries
+NEO_LIBRARY = $(exec_prefix)/lib/ldaptcl$(VERSION)
+
+# Directory in which to install the ldaptcl binary:
+BIN_INSTALL_DIR = $(DESTDIR)$(exec_prefix)/bin
+
+# Directory in which to install the .a or .so binary for the Neo library:
+LIB_INSTALL_DIR = $(DESTDIR)$(exec_prefix)/lib
+
+# Path to use at runtime to refer to LIB_INSTALL_DIR:
+LIB_RUNTIME_DIR = $(exec_prefix)/lib
+
+# Top-level directory for man entries:
+MANN_INSTALL_DIR = $(DESTDIR)$(prefix)/man/mann
+
+
+# The symbols below provide support for dynamic loading and shared
+# libraries. The values of the symbols are normally set by the
+# configure script. You shouldn't normally need to modify any of
+# these definitions by hand.
+
+SHLIB_CFLAGS = @NEO_SHLIB_CFLAGS@
+
+NEO_LIB_FILE = @NEO_LIB_FILE@
+
+NEO_SHARED_LIB_FILE = @NEO_SHARED_LIB_FILE@
+
+# The directory containing the Tcl sources and headers appropriate
+# for this version of Neo ("srcdir" will be replaced or has already
+# been replaced by the configure script):
+TCL_GENERIC_DIR = @TCL_SRC_DIR@/generic
+
+# The top of the TclX directory tree
+TCLX_TOP_DIR = @TCLX_TOP_DIR@
+
+# The directory where tclExtend.h will be:
+TCLX_TCL_GEN_DIR = ${TCLX_TOP_DIR}/tcl/generic
+
+# The directory where tclXunixPort.h will be:
+TCLX_TCL_UNIX_DIR = ${TCLX_TOP_DIR}/tcl/unix
+
+# The path to tclX the runtcl script:
+TCLX_RUNTCL = ${TCLX_TOP_DIR}/unix/runtcl
+
+# The directory containing the Tcl library archive file appropriate
+# for this version of Neo:
+TCL_BIN_DIR = @TCL_BIN_DIR@
+
+
+# The symbol below provides support for dynamic loading and shared
+# libraries. See configure.ac for a description of what it means.
+# The values of the symbolis normally set by the configure script.
+
+SHLIB_LD = @SHLIB_LD@
+
+# Set to the options to include libldap.a and liblber.a
+# (eg. -L../tools/blah -lldap -llber)
+
+LDAP_LIBFLAGS = @ldaplibflags@
+LDAP_CFLAGS = @ldapinclude@
+LDAP_INCDIR = @ldapincdir@
+LDAP_BUILD = @ldapbuild@
+LDAP_DIR = @ldapdir@
+
+
+#----------------------------------------------------------------
+# The information below is modified by the configure script when
+# Makefile is generated from Makefile.in. You shouldn't normally
+# modify any of this stuff by hand.
+#----------------------------------------------------------------
+
+AC_FLAGS = @DEFS@
+INSTALL= @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+RANLIB = @RANLIB@
+SRC_DIR = @srcdir@/..
+TOP_DIR = @srcdir@/..
+GENERIC_DIR = $(TOP_DIR)/generic
+
+#----------------------------------------------------------------
+# The information below should be usable as is. The configure
+# script won't modify it and you shouldn't need to modify it
+# either.
+#----------------------------------------------------------------
+
+
+OBJS= neoXldap.o
+
+LIBDIR=$(exec_prefix)/lib
+INCDIR=$(prefix)/include
+
+LIBS= @LIBS@ @TCLX_LIB_SPEC@ @TCL_LIB_SPEC@ @TCL_LIBS@ $(LDAP_LIBFLAGS) -lc
+TK_LIBS=@TKX_LIB_SPEC@ @TK_LIB_SPEC@ @TK_LIBS@
+TK_VERSION=@TK_VERSION@
+
+CC = @CC@
+CC_SWITCHES = ${CFLAGS} @NEO_SHLIB_CFLAGS@ -I. \
+-I@prefix@/include ${AC_FLAGS} ${PROTO_FLAGS} \
+${SECURITY_FLAGS} ${MEM_DEBUG_FLAGS} ${KEYSYM_FLAGS} \
+-DNEO_LIBRARY=\"${NEO_LIBRARY}\" -DVERSION=\"${VERSION}\"
+
+TK_SWITCHES = ${CC_SWITCHES} @TK_XINCLUDES@
+
+.c.o:
+ $(CC) -c $(CC_SWITCHES) $<
+
+all: @NEO_LIB_FILE@ ldaptclsh @LDAPWISH@
+
+@NEO_LIB_FILE@: $(OBJS)
+ rm -f @NEO_LIB_FILE@
+ @MAKE_LIB@
+ $(RANLIB) @NEO_LIB_FILE@
+
+neoXldap.o: neoXldap.c ldaptclerr.h
+ $(CC) -c $(LDAP_CFLAGS) $(CC_SWITCHES) neoXldap.c
+
+ldaptclerr.h: ldaperr.tcl
+ tcl ldaperr.tcl $(LDAP_INCDIR)/ldap.h > ldaptclerr.h
+
+
+clean:
+ -rm -f ldaptclsh ldapwish
+ -rm -f *.o *.a *.so*
+
+distclean: clean
+ rm -f Makefile pkgIndex.tcl config.cache config.log config.status \
+ ldaptclerr.h
+
+install: install-binaries install-man
+
+install-binaries: @NEO_LIB_FILE@ ldaptclsh @LDAPWISH@
+ @-mkdir -p $(BIN_INSTALL_DIR)
+ $(INSTALL_PROGRAM) ldaptclsh $(BIN_INSTALL_DIR)/ldaptclsh
+ @if [ -n "@LDAPWISH@" ] ; then \
+ echo $(INSTALL_PROGRAM) ldapwish $(BIN_INSTALL_DIR)/ldapwish; \
+ $(INSTALL_PROGRAM) ldapwish $(BIN_INSTALL_DIR)/ldapwish; \
+ fi
+ $(INSTALL_DATA) @NEO_LIB_FILE@ $(LIB_INSTALL_DIR)
+ @if [ "$(NEO_LIB_FILE)" = "$(NEO_SHARED_LIB_FILE)" ] ; then \
+ echo Installing pkgIndex.tcl in $(NEO_LIBRARY); \
+ mkdir -p $(NEO_LIBRARY); \
+ $(INSTALL_DATA) pkgIndex.tcl $(NEO_LIBRARY); \
+ fi
+
+
+install-man:
+ @for i in ldap.n; \
+ do \
+ echo "Installing $$i"; \
+ rm -f $(MANN_INSTALL_DIR)/$$i; \
+ sed -e '/man\.macros/r man.macros' -e '/man\.macros/d' \
+ $$i > $(MANN_INSTALL_DIR)/$$i; \
+ chmod 444 $(MANN_INSTALL_DIR)/$$i; \
+ done;
+
+
+TCLOFILES= tclAppInit.o
+
+ldaptclsh:$(TCLOFILES) @NEO_LIB_FILE@
+ $(CC) @LD_FLAGS@ $(TCLOFILES) @NEO_BUILD_LIB_SPEC@ $(LIBS) \
+ @TCL_LD_SEARCH_FLAGS@ -o ldaptclsh
+
+tkAppInit.o: tkAppInit.c
+ $(CC) -c ${TK_SWITCHES} tkAppInit.c
+
+ldapwish:tkAppInit.o @NEO_LIB_FILE@
+ $(CC) @LD_FLAGS@ tkAppInit.o @NEO_BUILD_LIB_SPEC@ $(TK_LIBS) $(LIBS) \
+ @TCL_LD_SEARCH_FLAGS@ -o ldapwish
diff --git a/contrib/ldaptcl/README b/contrib/ldaptcl/README
new file mode 100644
index 0000000..1b70f59
--- /dev/null
+++ b/contrib/ldaptcl/README
@@ -0,0 +1,67 @@
+Copyright (c) 1998-1999 NeoSoft, Inc.
+
+For licensing information, see the file neoXldap.c and/or the COPYRIGHT
+file contained in the directory you found this file.
+
+This directory contains an extension to Tcl to interface with an
+LDAP server. While this software is being released to the OpenLDAP
+community, it is the authors' intention that support continue (and
+be added) for other client libraries as well. As time goes on, it
+is expected that code will converge rather than diverge.
+
+Support is provided for University of Michigan LDAP version 3.3,
+OpenLDAP, and Netscape. The default configuration supports
+OpenLDAP 1.2.4 and above.
+
+OpenLDAP 2.x is supported, but there is not yet any support for
+using SASL or TLS. There may be interface changes in the LDAP API
+which the author is unaware of (a leak was recently fixed for the
+return values of ldap_first/next_attribute() calls).
+
+It uses GNU autoconf. It builds and installs without requiring
+parallel directories, but it does require that Tcl and Extended Tcl
+are installed in the directory pointed to by --prefix (/usr/local
+by default).
+
+For further info, try "./configure --help".
+
+For example, I run:
+
+ ./configure --prefix=/opt/neotcl --enable-shared \
+ --with-ldap=/usr/local/ldap
+
+Remember that --prefix must be the same prefix used when building
+and installint Tcl.
+
+Netscape configuration has not been well tested, and you may have to
+play with the resulting Makefile to get it to work. In particular,
+you will probably need to modify the LDAP_LIBFLAGS. However, the
+C code itself is reasonably well tested with Netscape.
+
+This module will install a regular shell (ldaptclsh) a windowing
+shell (ldapwish) a library, a pkgIndex.tcl, and a manpage (ldap.n).
+
+If your Tcl installation has been configured with --enable-shared,
+then you must also use --enable-shared here.
+
+Shared libraries and Tcl packages.
+
+If Tcl is built with --enable-shared, AND OpenLDAP (or another version
+for that matter) has been build to create -llber and -lldap as shared
+libraries, AND you build ldaptcl with --enable-shared, it should be
+possible to run a plain Tcl interpreter (eg. tclsh8.0) and do
+
+ package require Ldaptcl
+
+which will install the "ldap" command into the interpreter.
+
+You may need to set the LD_LIBRARY_PATH environment variable appropriately,
+or use -R or -W,-rpath ld command options to resolve the search for ldap
+and lber libraries.
+
+This package was test built on a Alpha OSF4.0e with the native C
+compiler.
+
+Please email comments or bug fixes to openldap-devel@OpenLDAP.org,
+or to kunkee@OpenLDAP.org. I would also like to know if you are
+using this interface, so I invite you to drop me an email if you do.
diff --git a/contrib/ldaptcl/configure b/contrib/ldaptcl/configure
new file mode 100755
index 0000000..8fbe5a9
--- /dev/null
+++ b/contrib/ldaptcl/configure
@@ -0,0 +1,4221 @@
+#! /bin/sh
+# From configure.ac Id: 16b135293616700c63077e9a1a601681d4442fdd .
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.69.
+#
+#
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in #(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Use a proper internal environment variable to ensure we don't fall
+ # into an infinite loop, continuously re-executing ourselves.
+ if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+ _as_can_reexec=no; export _as_can_reexec;
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+ fi
+ # We don't want this to propagate to other subprocesses.
+ { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+ as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '\${1+\"\$@\"}'='\"\$@\"'
+ setopt NO_GLOB_SUBST
+else
+ case \`(set -o) 2>/dev/null\` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+"
+ as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+ exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
+ as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+ as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+ eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+ test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1"
+ if (eval "$as_required") 2>/dev/null; then :
+ as_have_required=yes
+else
+ as_have_required=no
+fi
+ if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ as_found=:
+ case $as_dir in #(
+ /*)
+ for as_base in sh bash ksh sh5; do
+ # Try only shells that exist, to save several forks.
+ as_shell=$as_dir/$as_base
+ if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ CONFIG_SHELL=$as_shell as_have_required=yes
+ if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ break 2
+fi
+fi
+ done;;
+ esac
+ as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+ CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+ if test "x$CONFIG_SHELL" != x; then :
+ export CONFIG_SHELL
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+ if test x$as_have_required = xno; then :
+ $as_echo "$0: This script requires a shell more modern than all"
+ $as_echo "$0: the shells that I found on your system."
+ if test x${ZSH_VERSION+set} = xset ; then
+ $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+ $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+ else
+ $as_echo "$0: Please tell bug-autoconf@gnu.org about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+ fi
+ exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+ as_lineno_1=$LINENO as_lineno_1a=$LINENO
+ as_lineno_2=$LINENO as_lineno_2a=$LINENO
+ eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+ test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+ # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-)
+ sed -n '
+ p
+ /[$]LINENO/=
+ ' <$as_myself |
+ sed '
+ s/[$]LINENO.*/&-/
+ t lineno
+ b
+ :lineno
+ N
+ :loop
+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+ t loop
+ s/-\n.*//
+ ' >$as_me.lineno &&
+ chmod +x "$as_me.lineno" ||
+ { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+ # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+ # already done that, so ensure we don't try to do so again and fall
+ # in an infinite loop. This has already happened in practice.
+ _as_can_reexec=no; export _as_can_reexec
+ # Don't try to exec as it changes $[0], causing all sort of problems
+ # (the dirname of $[0] is not the place where we might find the
+ # original and so on. Autoconf is especially sensitive to this).
+ . "./$as_me.lineno"
+ # Exit status is that of the last command.
+ exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -pR'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -pR'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -pR'
+ fi
+else
+ as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME=
+PACKAGE_TARNAME=
+PACKAGE_VERSION=
+PACKAGE_STRING=
+PACKAGE_BUGREPORT=
+PACKAGE_URL=
+
+ac_unique_file="neoXldap.c"
+ac_subst_vars='LTLIBOBJS
+LIBOBJS
+NEO_VERSION
+NEO_SHLIB_CFLAGS
+NEO_MINOR_VERSION
+NEO_MAJOR_VERSION
+NEO_LIB_SPEC
+NEO_LIB_FILE
+NEO_UNSHARED_LIB_FILE
+NEO_SHARED_LIB_FILE
+NEO_LD_SEARCH_FLAGS
+NEO_BUILD_LIB_SPEC
+TCL_VERSION
+TCL_LIB_HNAME
+TCL_LD_SEARCH_FLAGS
+TCL_LIB_SPEC
+TCL_BIN_DIR
+TCL_SRC_DIR
+TCL_LIBS
+ITCL_LIB_SPEC
+TCLX_LIB_SPEC
+TCLX_TCL_DIR
+TCLX_TOP_DIR
+SHLIB_VERSION
+SHLIB_SUFFIX
+SHLIB_LD_LIBS
+SHLIB_LD
+SHLIB_CFLAGS
+MAKE_LIB
+MATH_LIBS
+LD_FLAGS
+DL_LIBS
+ldapincdir
+ldapdir
+ldapbuild
+ldapinclude
+ldaplibflags
+LDAPWISH
+TKX_LIB_SPEC
+TK_VERSION
+TK_XINCLUDES
+TK_LIB_SPEC
+TK_LIBS
+RANLIB
+INSTALL_DATA
+INSTALL_SCRIPT
+INSTALL_PROGRAM
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+runstatedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_gcc
+with_tk
+with_x
+enable_shared
+with_ldap
+with_ldap_incdir
+with_ldap_libdir
+with_ldap_libraries
+'
+ ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval $ac_prev=\$ac_option
+ ac_prev=
+ continue
+ fi
+
+ case $ac_option in
+ *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+ *=) ac_optarg= ;;
+ *) ac_optarg=yes ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case $ac_dashdash$ac_option in
+ --)
+ ac_dashdash=yes ;;
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir=$ac_optarg ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build_alias ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build_alias=$ac_optarg ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file=$ac_optarg ;;
+
+ --config-cache | -C)
+ cache_file=config.cache ;;
+
+ -datadir | --datadir | --datadi | --datad)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=*)
+ datadir=$ac_optarg ;;
+
+ -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+ | --dataroo | --dataro | --datar)
+ ac_prev=datarootdir ;;
+ -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+ datarootdir=$ac_optarg ;;
+
+ -disable-* | --disable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=no ;;
+
+ -docdir | --docdir | --docdi | --doc | --do)
+ ac_prev=docdir ;;
+ -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+ docdir=$ac_optarg ;;
+
+ -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+ ac_prev=dvidir ;;
+ -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+ dvidir=$ac_optarg ;;
+
+ -enable-* | --enable-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid feature name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"enable_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval enable_$ac_useropt=\$ac_optarg ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix=$ac_optarg ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he | -h)
+ ac_init_help=long ;;
+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+ ac_init_help=recursive ;;
+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+ ac_init_help=short ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host_alias ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host_alias=$ac_optarg ;;
+
+ -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+ ac_prev=htmldir ;;
+ -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+ | --ht=*)
+ htmldir=$ac_optarg ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir=$ac_optarg ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir=$ac_optarg ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir=$ac_optarg ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir=$ac_optarg ;;
+
+ -localedir | --localedir | --localedi | --localed | --locale)
+ ac_prev=localedir ;;
+ -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+ localedir=$ac_optarg ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst | --locals)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+ localstatedir=$ac_optarg ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir=$ac_optarg ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c | -n)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir=$ac_optarg ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=$ac_optarg ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix=$ac_optarg ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix=$ac_optarg ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name=$ac_optarg ;;
+
+ -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+ ac_prev=pdfdir ;;
+ -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+ pdfdir=$ac_optarg ;;
+
+ -psdir | --psdir | --psdi | --psd | --ps)
+ ac_prev=psdir ;;
+ -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+ psdir=$ac_optarg ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -runstatedir | --runstatedir | --runstatedi | --runstated \
+ | --runstate | --runstat | --runsta | --runst | --runs \
+ | --run | --ru | --r)
+ ac_prev=runstatedir ;;
+ -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+ | --run=* | --ru=* | --r=*)
+ runstatedir=$ac_optarg ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir=$ac_optarg ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir=$ac_optarg ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site=$ac_optarg ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir=$ac_optarg ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir=$ac_optarg ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target_alias ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target_alias=$ac_optarg ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers | -V)
+ ac_init_version=: ;;
+
+ -with-* | --with-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=\$ac_optarg ;;
+
+ -without-* | --without-*)
+ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+ # Reject names that are not valid shell variable names.
+ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+ as_fn_error $? "invalid package name: $ac_useropt"
+ ac_useropt_orig=$ac_useropt
+ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ case $ac_user_opts in
+ *"
+"with_$ac_useropt"
+"*) ;;
+ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+ ac_unrecognized_sep=', ';;
+ esac
+ eval with_$ac_useropt=no ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes=$ac_optarg ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries=$ac_optarg ;;
+
+ -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+ ;;
+
+ *=*)
+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+ # Reject names that are not valid shell variable names.
+ case $ac_envvar in #(
+ '' | [0-9]* | *[!_$as_cr_alnum]* )
+ as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+ esac
+ eval $ac_envvar=\$ac_optarg
+ export $ac_envvar ;;
+
+ *)
+ # FIXME: should be removed in autoconf 3.0.
+ $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+ $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+ as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+ case $enable_option_checking in
+ no) ;;
+ fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+ *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+ esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
+ datadir sysconfdir sharedstatedir localstatedir includedir \
+ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+ libdir localedir mandir runstatedir
+do
+ eval ac_val=\$$ac_var
+ # Remove trailing slashes.
+ case $ac_val in
+ */ )
+ ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+ eval $ac_var=\$ac_val;;
+ esac
+ # Be sure to have absolute directory names.
+ case $ac_val in
+ [\\/$]* | ?:[\\/]* ) continue;;
+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+ esac
+ as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+ if test "x$build_alias" = x; then
+ cross_compiling=maybe
+ elif test "x$build_alias" != "x$host_alias"; then
+ cross_compiling=yes
+ fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+ as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+ as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then the parent directory.
+ ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_myself" : 'X\(//\)[^/]' \| \
+ X"$as_myself" : 'X\(//\)$' \| \
+ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ srcdir=$ac_confdir
+ if test ! -r "$srcdir/$ac_unique_file"; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+ as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+ cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+ pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+ srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+ eval ac_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_env_${ac_var}_value=\$${ac_var}
+ eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+ eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat <<_ACEOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE. See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+ -h, --help display this help and exit
+ --help=short display options specific to this package
+ --help=recursive display the short help of all the included packages
+ -V, --version display version information and exit
+ -q, --quiet, --silent do not print \`checking ...' messages
+ --cache-file=FILE cache test results in FILE [disabled]
+ -C, --config-cache alias for \`--cache-file=config.cache'
+ -n, --no-create do not create output files
+ --srcdir=DIR find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+ --bindir=DIR user executables [EPREFIX/bin]
+ --sbindir=DIR system admin executables [EPREFIX/sbin]
+ --libexecdir=DIR program executables [EPREFIX/libexec]
+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]
+ --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
+ --libdir=DIR object code libraries [EPREFIX/lib]
+ --includedir=DIR C header files [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc [/usr/include]
+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]
+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]
+ --infodir=DIR info documentation [DATAROOTDIR/info]
+ --localedir=DIR locale-dependent data [DATAROOTDIR/locale]
+ --mandir=DIR man documentation [DATAROOTDIR/man]
+ --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE]
+ --htmldir=DIR html documentation [DOCDIR]
+ --dvidir=DIR dvi documentation [DOCDIR]
+ --pdfdir=DIR pdf documentation [DOCDIR]
+ --psdir=DIR ps documentation [DOCDIR]
+_ACEOF
+
+ cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+
+ cat <<\_ACEOF
+
+Optional Features:
+ --disable-option-checking ignore unrecognized --enable/--with options
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --enable-gcc allow use of gcc if available
+ --enable-shared build libldaptcl as a shared library
+
+Optional Packages:
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --with-tk=DIR use Tk 8.0 binaries from DIR
+ --without-x do not build/install ldapwish
+ --with-ldap=<dir> common parent of ldap include and lib dirs
+ --with-ldap-incdir=<dir> path to ldap.h
+ --with-ldap-libdir=<dir> path to ldap and lber libs
+ --with-ldap-libflags=<libnames> -l flags for ldap libraries
+
+Some influential environment variables:
+ CC C compiler command
+ CFLAGS C compiler flags
+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a
+ nonstandard directory <lib dir>
+ LIBS libraries to pass to the linker, e.g. -l<library>
+ CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+ you have headers in a nonstandard directory <include dir>
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to the package provider.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+ # If there are subdirs, report their specific --help.
+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+ test -d "$ac_dir" ||
+ { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+ continue
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+ cd "$ac_dir" || { ac_status=$?; continue; }
+ # Check for guested configure.
+ if test -f "$ac_srcdir/configure.gnu"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+ elif test -f "$ac_srcdir/configure"; then
+ echo &&
+ $SHELL "$ac_srcdir/configure" --help=recursive
+ else
+ $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ fi || ac_status=$?
+ cd "$ac_pwd" || { ac_status=$?; break; }
+ done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+ cat <<\_ACEOF
+configure
+generated by GNU Autoconf 2.69
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+ exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ rm -f conftest.$ac_objext
+ if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest.$ac_objext; then :
+ ac_retval=0
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by $as_me, which was
+generated by GNU Autoconf 2.69. Invocation command line was
+
+ $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`
+
+/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`
+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`
+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ $as_echo "PATH: $as_dir"
+ done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+ for ac_arg
+ do
+ case $ac_arg in
+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ continue ;;
+ *\'*)
+ ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ case $ac_pass in
+ 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+ 2)
+ as_fn_append ac_configure_args1 " '$ac_arg'"
+ if test $ac_must_keep_next = true; then
+ ac_must_keep_next=false # Got value, back to normal.
+ else
+ case $ac_arg in
+ *=* | --config-cache | -C | -disable-* | --disable-* \
+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+ | -with-* | --with-* | -without-* | --without-* | --x)
+ case "$ac_configure_args0 " in
+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+ esac
+ ;;
+ -* ) ac_must_keep_next=true ;;
+ esac
+ fi
+ as_fn_append ac_configure_args " '$ac_arg'"
+ ;;
+ esac
+ done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log. We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+ # Save into config.log some information that might help in debugging.
+ {
+ echo
+
+ $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+ echo
+ # The following way of writing the cache mishandles newlines in values,
+(
+ for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+ (set) 2>&1 |
+ case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ sed -n \
+ "s/'\''/'\''\\\\'\'''\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+ ;; #(
+ *)
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+)
+ echo
+
+ $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+ echo
+ for ac_var in $ac_subst_vars
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+
+ if test -n "$ac_subst_files"; then
+ $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+ echo
+ for ac_var in $ac_subst_files
+ do
+ eval ac_val=\$$ac_var
+ case $ac_val in
+ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ esac
+ $as_echo "$ac_var='\''$ac_val'\''"
+ done | sort
+ echo
+ fi
+
+ if test -s confdefs.h; then
+ $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+ echo
+ cat confdefs.h
+ echo
+ fi
+ test "$ac_signal" != 0 &&
+ $as_echo "$as_me: caught signal $ac_signal"
+ $as_echo "$as_me: exit $exit_status"
+ } >&5
+ rm -f core *.core core.conftest.* &&
+ rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+ exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+ trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+ # We do not want a PATH search for config.site.
+ case $CONFIG_SITE in #((
+ -*) ac_site_file1=./$CONFIG_SITE;;
+ */*) ac_site_file1=$CONFIG_SITE;;
+ *) ac_site_file1=./$CONFIG_SITE;;
+ esac
+elif test "x$prefix" != xNONE; then
+ ac_site_file1=$prefix/share/config.site
+ ac_site_file2=$prefix/etc/config.site
+else
+ ac_site_file1=$ac_default_prefix/share/config.site
+ ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+ test "x$ac_site_file" = xNONE && continue
+ if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+ sed 's/^/| /' "$ac_site_file" >&5
+ . "$ac_site_file" \
+ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+done
+
+if test -r "$cache_file"; then
+ # Some versions of bash will fail to source /dev/null (special files
+ # actually), so we avoid doing that. DJGPP emulates it as a regular file.
+ if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+ case $cache_file in
+ [\\/]* | ?:[\\/]* ) . "$cache_file";;
+ *) . "./$cache_file";;
+ esac
+ fi
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+ >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+ eval ac_old_set=\$ac_cv_env_${ac_var}_set
+ eval ac_new_set=\$ac_env_${ac_var}_set
+ eval ac_old_val=\$ac_cv_env_${ac_var}_value
+ eval ac_new_val=\$ac_env_${ac_var}_value
+ case $ac_old_set,$ac_new_set in
+ set,)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,set)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ ac_cache_corrupted=: ;;
+ ,);;
+ *)
+ if test "x$ac_old_val" != "x$ac_new_val"; then
+ # differences in whitespace do not lead to failure.
+ ac_old_val_w=`echo x $ac_old_val`
+ ac_new_val_w=`echo x $ac_new_val`
+ if test "$ac_old_val_w" != "$ac_new_val_w"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ ac_cache_corrupted=:
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+ eval $ac_var=\$ac_old_val
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5
+$as_echo "$as_me: former value: \`$ac_old_val'" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5
+$as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
+ fi;;
+ esac
+ # Pass precious variables to config.status.
+ if test "$ac_new_set" = set; then
+ case $ac_new_val in
+ *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *) ac_arg=$ac_var=$ac_new_val ;;
+ esac
+ case " $ac_configure_args " in
+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.
+ *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+ esac
+ fi
+done
+if $ac_cache_corrupted; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+ as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# $OpenLDAP$
+
+
+
+NEO_VERSION=2.0
+NEO_MAJOR_VERSION=2
+NEO_MINOR_VERSION=0
+VERSION=${NEO_VERSION}
+
+if test "${prefix}" = "NONE"; then
+ prefix=/usr/local
+fi
+if test "${exec_prefix}" = "NONE"; then
+ exec_prefix=$prefix
+fi
+
+# Check whether --enable-gcc was given.
+if test "${enable_gcc+set}" = set; then :
+ enableval=$enable_gcc; neo_ok=$enableval
+else
+ neo_ok=no
+fi
+
+if test "$neo_ok" = "yes"; then
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="gcc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ fi
+fi
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# != 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ for ac_prog in cl.exe
+ do
+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$CC" && break
+ done
+fi
+if test -z "$CC"; then
+ ac_ct_CC=$CC
+ for ac_prog in cl.exe
+do
+ # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_CC"; then
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="$ac_prog"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+ test -n "$ac_ct_CC" && break
+done
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+ { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ sed '10a\
+... rest of stderr output deleted ...
+ 10q' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ fi
+ rm -f conftest.er1 conftest.err
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+ esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link_default") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile. We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+ ;;
+ [ab].out )
+ # We found the default executable, but exeext='' is most
+ # certainly right.
+ break;;
+ *.* )
+ if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+ then :; else
+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ fi
+ # We set ac_cv_exeext here because the later test for it is not
+ # safe: cross compilers may not add the suffix if given an `-o'
+ # argument, so we may need to know it at that point already.
+ # Even if this section looks crufty: it has the advantage of
+ # actually working.
+ break;;
+ * )
+ break;;
+ esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+ ac_file=''
+fi
+if test -z "$ac_file"; then :
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+ test -f "$ac_file" || continue
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+ break;;
+ * ) break;;
+ esac
+done
+else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run. If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+ { { ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_link") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+ if { ac_try='./conftest$ac_cv_exeext'
+ { { case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_try") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }; then
+ cross_compiling=no
+ else
+ if test "$cross_compiling" = maybe; then
+ cross_compiling=yes
+ else
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+ (eval "$ac_compile") 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then :
+ for ac_file in conftest.o conftest.obj conftest.*; do
+ test -f "$ac_file" || continue;
+ case $ac_file in
+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+ break;;
+ esac
+done
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+#ifndef __GNUC__
+ choke me
+#endif
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_compiler_gnu=yes
+else
+ ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+ GCC=yes
+else
+ GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_save_c_werror_flag=$ac_c_werror_flag
+ ac_c_werror_flag=yes
+ ac_cv_prog_cc_g=no
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+else
+ CFLAGS=""
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+ ac_c_werror_flag=$ac_save_c_werror_flag
+ CFLAGS="-g"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+ CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+ if test "$GCC" = yes; then
+ CFLAGS="-g -O2"
+ else
+ CFLAGS="-g"
+ fi
+else
+ if test "$GCC" = yes; then
+ CFLAGS="-O2"
+ else
+ CFLAGS=
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <stdarg.h>
+#include <stdio.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+ char **p;
+ int i;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
+ function prototypes and stuff, but not '\xHH' hex character constants.
+ These don't provoke an error unfortunately, instead are silently treated
+ as 'x'. The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
+ array size at least. It's necessary to write '\x00'==0 to get something
+ that's true only with -std. */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
+ ;
+ return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+ test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+ x)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+ xno)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+ *)
+ CC="$CC $ac_cv_prog_cc_c89"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+else
+ CC=${CC-cc}
+
+fi
+
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+ if test -f "$ac_dir/install-sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f "$ac_dir/install.sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f "$ac_dir/shtool"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
+
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+# Reject install programs that cannot install multiple files.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+$as_echo_n "checking for a BSD-compatible install... " >&6; }
+if test -z "$INSTALL"; then
+if ${ac_cv_path_install+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in #((
+ ./ | .// | /[cC]/* | \
+ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
+ /usr/ucb/* ) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+ if test $ac_prog = install &&
+ grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ elif test $ac_prog = install &&
+ grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ # program-specific install script used by HP pwplus--don't use.
+ :
+ else
+ rm -rf conftest.one conftest.two conftest.dir
+ echo one > conftest.one
+ echo two > conftest.two
+ mkdir conftest.dir
+ if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+ test -s conftest.one && test -s conftest.two &&
+ test -s conftest.dir/conftest.one &&
+ test -s conftest.dir/conftest.two
+ then
+ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ break 3
+ fi
+ fi
+ fi
+ done
+ done
+ ;;
+esac
+
+ done
+IFS=$as_save_IFS
+
+rm -rf conftest.one conftest.two conftest.dir
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL=$ac_cv_path_install
+ else
+ # As a last resort, use the slow shell script. Don't cache a
+ # value for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the value is a relative name.
+ INSTALL=$ac_install_sh
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+$as_echo "$INSTALL" >&6; }
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+$as_echo "$RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+ ac_ct_RANLIB=$RANLIB
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$ac_ct_RANLIB"; then
+ ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_RANLIB="ranlib"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+$as_echo "$ac_ct_RANLIB" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+ if test "x$ac_ct_RANLIB" = x; then
+ RANLIB=":"
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ RANLIB=$ac_ct_RANLIB
+ fi
+else
+ RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+
+if test ! -f $exec_prefix/lib/tclConfig.sh
+then
+ as_fn_error $? "Tcl must be installed first" "$LINENO" 5
+fi
+
+. $exec_prefix/lib/tclConfig.sh
+
+if test ! -f $exec_prefix/lib/tclxConfig.sh
+then
+ as_fn_error $? "Extended Tcl must be installed first" "$LINENO" 5
+fi
+. $exec_prefix/lib/tclxConfig.sh
+
+
+#--------------------------------------------------------------------
+# See if there was a command-line option for where Tk is; if
+# not, assume that its top-level directory is a sibling of ours.
+#--------------------------------------------------------------------
+
+
+# Check whether --with-tk was given.
+if test "${with_tk+set}" = set; then :
+ withval=$with_tk;
+else
+ with_tk=yes
+fi
+
+
+case "$with_tk" in
+ yes)
+ if test -f $exec_prefix/lib/tkConfig.sh &&
+ test -f $exec_prefix/lib/tkxConfig.sh
+ then
+ :
+ else
+ as_fn_error $? "Tk does not appear to be installed at $exec_prefix" "$LINENO" 5
+ fi
+ ;;
+ no) ;;
+ *) as_fn_error $? "Tk cannot be specified and must be in $exec_prefix" "$LINENO" 5
+ ;;
+esac
+
+
+# Check whether --with-x was given.
+if test "${with_x+set}" = set; then :
+ withval=$with_x;
+fi
+
+if test "$with_x" = "no"
+then
+ with_tk=no
+fi
+
+if test "$with_tk" != "no"
+then
+ LDAPWISH=ldapwish
+ . $exec_prefix/lib/tkConfig.sh
+ . $exec_prefix/lib/tkxConfig.sh
+fi
+
+
+
+
+
+
+
+#--------------------------------------------------------------------
+# Read in configuration information generated by Tcl for shared
+# libraries, and arrange for it to be substituted into our
+# Makefile.
+#--------------------------------------------------------------------
+
+CC=$TCL_CC
+SHLIB_CFLAGS=$TCL_SHLIB_CFLAGS
+SHLIB_LD=$TCL_SHLIB_LD
+SHLIB_LD_LIBS=$TCL_SHLIB_LD_LIBS
+SHLIB_SUFFIX=$TCL_SHLIB_SUFFIX
+SHLIB_VERSION=$TCL_SHLIB_VERSION
+DL_LIBS=$TCL_DL_LIBS
+LD_FLAGS=$TCL_LD_FLAGS
+NEO_LD_SEARCH_FLAGS=$TCL_LD_SEARCH_FLAGS
+
+eval "NEO_SHARED_LIB_FILE=libldaptcl${TCL_SHARED_LIB_SUFFIX}"
+eval "NEO_UNSHARED_LIB_FILE=libldaptcl${TCL_UNSHARED_LIB_SUFFIX}"
+
+#--------------------------------------------------------------------
+# The statements below define a collection of symbols related to
+# building libldap as a shared library instead of a static library.
+#--------------------------------------------------------------------
+
+# Warning: in order to use the following code for libldap and libdb versions,
+# the VERSION shell variable is modified, and then is restored after.
+
+# Check whether --enable-shared was given.
+if test "${enable_shared+set}" = set; then :
+ enableval=$enable_shared; ok=$enableval
+else
+ ok=no
+fi
+
+if test "$ok" = "yes" && test "${SHLIB_SUFFIX}" != ""; then
+ NEO_SHLIB_CFLAGS="${SHLIB_CFLAGS}"
+ eval "NEO_LIB_FILE=libldaptcl${TCL_SHARED_LIB_SUFFIX}"
+ MAKE_LIB="\${SHLIB_LD} $TCL_LIB_HNAME -o ${NEO_LIB_FILE} \${OBJS} \${LDAP_LIBFLAGS}"
+ RANLIB=":"
+else
+ NEO_SHLIB_CFLAGS=""
+ eval "NEO_LIB_FILE=libldaptcl${TCL_UNSHARED_LIB_SUFFIX}"
+ MAKE_LIB="ar cr ${NEO_LIB_FILE} \${OBJS}"
+fi
+
+
+# Check whether --with-ldap was given.
+if test "${with_ldap+set}" = set; then :
+ withval=$with_ldap; neo_ldap=$withval
+ case $withval in
+ yes) ldapdir=/usr/local
+ ;;
+ no) ;;
+ *) ldapdir=$withval
+ neo_ldap=yes
+ ;;
+ esac
+
+else
+
+ neo_ldap=yes
+ ldapdir=/usr/local
+
+fi
+
+
+ldapincdir=$ldapdir/include
+
+# Check whether --with-ldap-incdir was given.
+if test "${with_ldap_incdir+set}" = set; then :
+ withval=$with_ldap_incdir; ldapincdir=$withval
+fi
+
+
+ldaplibdir=$ldapdir/lib
+
+# Check whether --with-ldap-libdir was given.
+if test "${with_ldap_libdir+set}" = set; then :
+ withval=$with_ldap_libdir; ldaplibdir=$withval
+fi
+
+
+
+# Check whether --with-ldap-libraries was given.
+if test "${with_ldap_libraries+set}" = set; then :
+ withval=$with_ldap_libraries; ldaplibflags="-L$ldaplibdir $withval"
+else
+ ldaplibflags="-L$ldaplibdir -lldap -llber"
+fi
+
+
+ldapinclude="-I$ldapincdir"
+
+ldapbuild=yes
+
+
+
+
+
+
+
+
+VERSION=${NEO_VERSION}
+# Note: in the following variable, it's important to use the absolute
+# path name of the Tcl directory rather than "..": this is because
+# AIX remembers this path and will attempt to use it at run-time to look
+# up the Tcl library.
+
+if test "${TCL_LIB_VERSIONS_OK}" = "ok"; then
+ NEO_BUILD_LIB_SPEC="-L`pwd` -lldaptcl${VERSION}"
+ NEO_LIB_SPEC="-L${exec_prefix}/lib -lldaptcl${VERSION}"
+else
+ NEO_BUILD_LIB_SPEC="-L`pwd` -lldaptcl`echo ${VERSION} | tr -d .`"
+ NEO_LIB_SPEC="-L${exec_prefix}/lib -lldaptcl`echo ${VERSION} | tr -d .`"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ac_config_files="$ac_config_files Makefile pkgIndex.tcl"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems. If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+ eval ac_val=\$$ac_var
+ case $ac_val in #(
+ *${as_nl}*)
+ case $ac_var in #(
+ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ esac
+ case $ac_var in #(
+ _ | IFS | as_nl) ;; #(
+ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+ *) { eval $ac_var=; unset $ac_var;} ;;
+ esac ;;
+ esac
+ done
+
+ (set) 2>&1 |
+ case $as_nl`(ac_space=' '; set) 2>&1` in #(
+ *${as_nl}ac_space=\ *)
+ # `set' does not quote correctly, so add quotes: double-quote
+ # substitution turns \\\\ into \\, and sed turns \\ into \.
+ sed -n \
+ "s/'/'\\\\''/g;
+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+ ;; #(
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+ ;;
+ esac |
+ sort
+) |
+ sed '
+ /^ac_cv_env_/b end
+ t clear
+ :clear
+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ t end
+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+ :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+ if test -w "$cache_file"; then
+ if test "x$cache_file" != "x/dev/null"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+ if test ! -f "$cache_file" || test -h "$cache_file"; then
+ cat confcache >"$cache_file"
+ else
+ case $cache_file in #(
+ */* | ?:*)
+ mv -f confcache "$cache_file"$$ &&
+ mv -f "$cache_file"$$ "$cache_file" ;; #(
+ *)
+ mv -f confcache "$cache_file" ;;
+ esac
+ fi
+ fi
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+ fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+#
+# If the first sed substitution is executed (which looks for macros that
+# take arguments), then branch to the quote section. Otherwise,
+# look for a macro that doesn't take arguments.
+ac_script='
+:mline
+/\\$/{
+ N
+ s,\\\n,,
+ b mline
+}
+t clear
+:clear
+s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g
+t quote
+s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g
+t quote
+b any
+:quote
+s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g
+s/\[/\\&/g
+s/\]/\\&/g
+s/\$/$$/g
+H
+:any
+${
+ g
+ s/^\n//
+ s/\n/ /g
+ p
+}
+'
+DEFS=`sed -n "$ac_script" confdefs.h`
+
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+ # 1. Remove the extension, and $U if already installed.
+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+ ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+ # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
+ # will be set to the directory where LIBOBJS objects are built.
+ as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+ as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in #(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -pR'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -pR'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -pR'
+ fi
+else
+ as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by $as_me, which was
+generated by GNU Autoconf 2.69. Invocation command line was
+
+ CONFIG_FILES = $CONFIG_FILES
+ CONFIG_HEADERS = $CONFIG_HEADERS
+ CONFIG_LINKS = $CONFIG_LINKS
+ CONFIG_COMMANDS = $CONFIG_COMMANDS
+ $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration. Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+ -h, --help print this help, then exit
+ -V, --version print version number and configuration settings, then exit
+ --config print configuration, then exit
+ -q, --quiet, --silent
+ do not print progress messages
+ -d, --debug don't remove temporary files
+ --recheck update $as_me by reconfiguring in the same conditions
+ --file=FILE[:TEMPLATE]
+ instantiate the configuration file FILE
+
+Configuration files:
+$config_files
+
+Report bugs to the package provider."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+config.status
+configured by $0, generated by GNU Autoconf 2.69,
+ with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+INSTALL='$INSTALL'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+ case $1 in
+ --*=?*)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+ ac_shift=:
+ ;;
+ --*=)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=
+ ac_shift=:
+ ;;
+ *)
+ ac_option=$1
+ ac_optarg=$2
+ ac_shift=shift
+ ;;
+ esac
+
+ case $ac_option in
+ # Handling of the options.
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ ac_cs_recheck=: ;;
+ --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+ $as_echo "$ac_cs_version"; exit ;;
+ --config | --confi | --conf | --con | --co | --c )
+ $as_echo "$ac_cs_config"; exit ;;
+ --debug | --debu | --deb | --de | --d | -d )
+ debug=: ;;
+ --file | --fil | --fi | --f )
+ $ac_shift
+ case $ac_optarg in
+ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ '') as_fn_error $? "missing file argument" ;;
+ esac
+ as_fn_append CONFIG_FILES " '$ac_optarg'"
+ ac_need_defaults=false;;
+ --he | --h | --help | --hel | -h )
+ $as_echo "$ac_cs_usage"; exit ;;
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil | --si | --s)
+ ac_cs_silent=: ;;
+
+ # This is an error.
+ -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+ *) as_fn_append ac_config_targets " $1"
+ ac_need_defaults=false ;;
+
+ esac
+ shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+ exec 6>/dev/null
+ ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+ set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+ shift
+ \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+ CONFIG_SHELL='$SHELL'
+ export CONFIG_SHELL
+ exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+ $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+ case $ac_config_target in
+ "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+ "pkgIndex.tcl") CONFIG_FILES="$CONFIG_FILES pkgIndex.tcl" ;;
+
+ *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+ esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used. Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+fi
+
+# Have a temporary directory for convenience. Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+ tmp= ac_tmp=
+ trap 'exit_status=$?
+ : "${ac_tmp:=$tmp}"
+ { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+ trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+ tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+ test -d "$tmp"
+} ||
+{
+ tmp=./conf$$-$RANDOM
+ (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+ eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+ ac_cs_awk_cr='\\r'
+else
+ ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+ echo "cat >conf$$subs.awk <<_ACEOF" &&
+ echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+ echo "_ACEOF"
+} >conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+ . ./conf$$subs.sh ||
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+ ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+ if test $ac_delim_n = $ac_delim_num; then
+ break
+ elif $ac_last_try; then
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ else
+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+ fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+ N
+ s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+ for (key in S) S_is_set[key] = 1
+ FS = ""
+
+}
+{
+ line = $ 0
+ nfields = split(line, field, "@")
+ substed = 0
+ len = length(field[1])
+ for (i = 2; i < nfields; i++) {
+ key = field[i]
+ keylen = length(key)
+ if (S_is_set[key]) {
+ value = S[key]
+ line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+ len += length(value) + length(field[++i])
+ substed = 1
+ } else
+ len += 1 + keylen
+ }
+
+ print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+ sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+ cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{
+h
+s///
+s/^/:/
+s/[ ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[ ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[ ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+
+eval set X " :F $CONFIG_FILES "
+shift
+for ac_tag
+do
+ case $ac_tag in
+ :[FHLC]) ac_mode=$ac_tag; continue;;
+ esac
+ case $ac_mode$ac_tag in
+ :[FHL]*:*);;
+ :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+ :[FH]-) ac_tag=-:-;;
+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+ esac
+ ac_save_IFS=$IFS
+ IFS=:
+ set x $ac_tag
+ IFS=$ac_save_IFS
+ shift
+ ac_file=$1
+ shift
+
+ case $ac_mode in
+ :L) ac_source=$1;;
+ :[FH])
+ ac_file_inputs=
+ for ac_f
+ do
+ case $ac_f in
+ -) ac_f="$ac_tmp/stdin";;
+ *) # Look for the file first in the build tree, then in the source tree
+ # (if the path is not absolute). The absolute path cannot be DOS-style,
+ # because $ac_f cannot contain `:'.
+ test -f "$ac_f" ||
+ case $ac_f in
+ [\\/$]*) false;;
+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+ esac ||
+ as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+ esac
+ case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+ as_fn_append ac_file_inputs " '$ac_f'"
+ done
+
+ # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # use $as_me), people would be surprised to read:
+ # /* config.h. Generated by config.status. */
+ configure_input='Generated from '`
+ $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+ `' by configure.'
+ if test x"$ac_file" != x-; then
+ configure_input="$ac_file. $configure_input"
+ { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+ fi
+ # Neutralize special characters interpreted by sed in replacement strings.
+ case $configure_input in #(
+ *\&* | *\|* | *\\* )
+ ac_sed_conf_input=`$as_echo "$configure_input" |
+ sed 's/[\\\\&|]/\\\\&/g'`;; #(
+ *) ac_sed_conf_input=$configure_input;;
+ esac
+
+ case $ac_tag in
+ *:-:* | *:-) cat >"$ac_tmp/stdin" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+ esac
+ ;;
+ esac
+
+ ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$ac_file" : 'X\(//\)[^/]' \| \
+ X"$ac_file" : 'X\(//\)$' \| \
+ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ as_dir="$ac_dir"; as_fn_mkdir_p
+ ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ # A ".." for each directory in $ac_dir_suffix.
+ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ case $ac_top_builddir_sub in
+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+ esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+ .) # We are building in place.
+ ac_srcdir=.
+ ac_top_srcdir=$ac_top_builddir_sub
+ ac_abs_top_srcdir=$ac_pwd ;;
+ [\\/]* | ?:[\\/]* ) # Absolute name.
+ ac_srcdir=$srcdir$ac_dir_suffix;
+ ac_top_srcdir=$srcdir
+ ac_abs_top_srcdir=$srcdir ;;
+ *) # Relative name.
+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+ ac_top_srcdir=$ac_top_build_prefix$srcdir
+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+ case $ac_mode in
+ :F)
+ #
+ # CONFIG_FILE
+ #
+
+ case $INSTALL in
+ [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+ *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
+ esac
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+ p
+ q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ ac_datarootdir_hack='
+ s&@datadir@&$datadir&g
+ s&@docdir@&$docdir&g
+ s&@infodir@&$infodir&g
+ s&@localedir@&$localedir&g
+ s&@mandir@&$mandir&g
+ s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+s&@INSTALL@&$ac_INSTALL&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \
+ "$ac_tmp/out"`; test -z "$ac_out"; } &&
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined. Please make sure it is defined" >&2;}
+
+ rm -f "$ac_tmp/stdin"
+ case $ac_file in
+ -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+ *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+ esac \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+
+
+
+ esac
+
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+ as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded. So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status. When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+ ac_cs_success=:
+ ac_config_status_args=
+ test "$silent" = yes &&
+ ac_config_status_args="$ac_config_status_args --quiet"
+ exec 5>/dev/null
+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+ exec 5>>config.log
+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+ # would make configure fail if this is the last instruction.
+ $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
diff --git a/contrib/ldaptcl/configure.ac b/contrib/ldaptcl/configure.ac
new file mode 100644
index 0000000..f62975d
--- /dev/null
+++ b/contrib/ldaptcl/configure.ac
@@ -0,0 +1,218 @@
+dnl This file is an input file used by the GNU "autoconf" program to
+dnl generate the file "configure", which is run during Tk installation
+dnl to configure the system for the local environment.
+AC_INIT(neoXldap.c)
+# $OpenLDAP$
+
+AC_REVISION([$Id: 16b135293616700c63077e9a1a601681d4442fdd $])
+
+NEO_VERSION=2.0
+NEO_MAJOR_VERSION=2
+NEO_MINOR_VERSION=0
+VERSION=${NEO_VERSION}
+
+if test "${prefix}" = "NONE"; then
+ prefix=/usr/local
+fi
+if test "${exec_prefix}" = "NONE"; then
+ exec_prefix=$prefix
+fi
+
+AC_ARG_ENABLE(gcc, [ --enable-gcc allow use of gcc if available],
+ [neo_ok=$enableval], [neo_ok=no])
+if test "$neo_ok" = "yes"; then
+ AC_PROG_CC
+else
+ CC=${CC-cc}
+AC_SUBST(CC)
+fi
+
+AC_PROG_INSTALL(install-sh)
+AC_PROG_RANLIB
+
+if test ! -f $exec_prefix/lib/tclConfig.sh
+then
+ AC_MSG_ERROR(Tcl must be installed first)
+fi
+
+. $exec_prefix/lib/tclConfig.sh
+
+if test ! -f $exec_prefix/lib/tclxConfig.sh
+then
+ AC_MSG_ERROR(Extended Tcl must be installed first)
+fi
+. $exec_prefix/lib/tclxConfig.sh
+
+
+#--------------------------------------------------------------------
+# See if there was a command-line option for where Tk is; if
+# not, assume that its top-level directory is a sibling of ours.
+#--------------------------------------------------------------------
+
+AC_ARG_WITH(tk, [ --with-tk=DIR use Tk 8.0 binaries from DIR],
+ , with_tk=yes)
+
+case "$with_tk" in
+ yes)
+ if test -f $exec_prefix/lib/tkConfig.sh &&
+ test -f $exec_prefix/lib/tkxConfig.sh
+ then
+ :
+ else
+ AC_MSG_ERROR(Tk does not appear to be installed at $exec_prefix)
+ fi
+ ;;
+ no) ;;
+ *) AC_MSG_ERROR(Tk cannot be specified and must be in $exec_prefix)
+ ;;
+esac
+
+AC_ARG_WITH(x, [ --without-x do not build/install ldapwish])
+if test "$with_x" = "no"
+then
+ with_tk=no
+fi
+
+if test "$with_tk" != "no"
+then
+ LDAPWISH=ldapwish
+ . $exec_prefix/lib/tkConfig.sh
+ . $exec_prefix/lib/tkxConfig.sh
+fi
+AC_SUBST(TK_LIBS)
+AC_SUBST(TK_LIB_SPEC)
+AC_SUBST(TK_XINCLUDES)
+AC_SUBST(TK_VERSION)
+AC_SUBST(TKX_LIB_SPEC)
+AC_SUBST(LDAPWISH)
+
+#--------------------------------------------------------------------
+# Read in configuration information generated by Tcl for shared
+# libraries, and arrange for it to be substituted into our
+# Makefile.
+#--------------------------------------------------------------------
+
+CC=$TCL_CC
+SHLIB_CFLAGS=$TCL_SHLIB_CFLAGS
+SHLIB_LD=$TCL_SHLIB_LD
+SHLIB_LD_LIBS=$TCL_SHLIB_LD_LIBS
+SHLIB_SUFFIX=$TCL_SHLIB_SUFFIX
+SHLIB_VERSION=$TCL_SHLIB_VERSION
+DL_LIBS=$TCL_DL_LIBS
+LD_FLAGS=$TCL_LD_FLAGS
+NEO_LD_SEARCH_FLAGS=$TCL_LD_SEARCH_FLAGS
+
+eval "NEO_SHARED_LIB_FILE=libldaptcl${TCL_SHARED_LIB_SUFFIX}"
+eval "NEO_UNSHARED_LIB_FILE=libldaptcl${TCL_UNSHARED_LIB_SUFFIX}"
+
+#--------------------------------------------------------------------
+# The statements below define a collection of symbols related to
+# building libldap as a shared library instead of a static library.
+#--------------------------------------------------------------------
+
+# Warning: in order to use the following code for libldap and libdb versions,
+# the VERSION shell variable is modified, and then is restored after.
+
+AC_ARG_ENABLE(shared,
+ [ --enable-shared build libldaptcl as a shared library],
+ [ok=$enableval], [ok=no])
+if test "$ok" = "yes" && test "${SHLIB_SUFFIX}" != ""; then
+ NEO_SHLIB_CFLAGS="${SHLIB_CFLAGS}"
+ eval "NEO_LIB_FILE=libldaptcl${TCL_SHARED_LIB_SUFFIX}"
+ MAKE_LIB="\${SHLIB_LD} $TCL_LIB_HNAME -o ${NEO_LIB_FILE} \${OBJS} \${LDAP_LIBFLAGS}"
+ RANLIB=":"
+else
+ NEO_SHLIB_CFLAGS=""
+ eval "NEO_LIB_FILE=libldaptcl${TCL_UNSHARED_LIB_SUFFIX}"
+ MAKE_LIB="ar cr ${NEO_LIB_FILE} \${OBJS}"
+fi
+
+AC_ARG_WITH(ldap, [ --with-ldap=<dir> common parent of ldap include and lib dirs],
+ [neo_ldap=$withval
+ case $withval in
+ yes) ldapdir=/usr/local
+ ;;
+ no) ;;
+ *) ldapdir=$withval
+ neo_ldap=yes
+ ;;
+ esac
+ ], [
+ neo_ldap=yes
+ ldapdir=/usr/local
+ ])
+
+ldapincdir=$ldapdir/include
+AC_ARG_WITH(ldap-incdir, [ --with-ldap-incdir=<dir> path to ldap.h],
+ [ldapincdir=$withval])
+
+ldaplibdir=$ldapdir/lib
+AC_ARG_WITH(ldap-libdir, [ --with-ldap-libdir=<dir> path to ldap and lber libs],
+ [ldaplibdir=$withval])
+
+AC_ARG_WITH(ldap-libraries, [ --with-ldap-libflags=<libnames> -l flags for ldap libraries],
+ [ldaplibflags="-L$ldaplibdir $withval"],
+ [ldaplibflags="-L$ldaplibdir -lldap -llber"])
+
+ldapinclude="-I$ldapincdir"
+
+ldapbuild=yes
+
+AC_SUBST(ldaplibflags)
+AC_SUBST(ldapinclude)
+AC_SUBST(ldapbuild)
+AC_SUBST(ldapdir)
+AC_SUBST(ldapincdir)
+
+
+VERSION=${NEO_VERSION}
+# Note: in the following variable, it's important to use the absolute
+# path name of the Tcl directory rather than "..": this is because
+# AIX remembers this path and will attempt to use it at run-time to look
+# up the Tcl library.
+
+if test "${TCL_LIB_VERSIONS_OK}" = "ok"; then
+ NEO_BUILD_LIB_SPEC="-L`pwd` -lldaptcl${VERSION}"
+ NEO_LIB_SPEC="-L${exec_prefix}/lib -lldaptcl${VERSION}"
+else
+ NEO_BUILD_LIB_SPEC="-L`pwd` -lldaptcl`echo ${VERSION} | tr -d .`"
+ NEO_LIB_SPEC="-L${exec_prefix}/lib -lldaptcl`echo ${VERSION} | tr -d .`"
+fi
+
+AC_SUBST(CC)
+AC_SUBST(LIBS)
+AC_SUBST(DL_LIBS)
+AC_SUBST(LD_FLAGS)
+AC_SUBST(MATH_LIBS)
+AC_SUBST(MAKE_LIB)
+AC_SUBST(SHLIB_CFLAGS)
+AC_SUBST(SHLIB_LD)
+AC_SUBST(SHLIB_LD_LIBS)
+AC_SUBST(SHLIB_SUFFIX)
+AC_SUBST(SHLIB_VERSION)
+AC_SUBST(TCLX_TOP_DIR)
+AC_SUBST(TCLX_TCL_DIR)
+AC_SUBST(TCLX_LIB_SPEC)
+AC_SUBST(ITCL_LIB_SPEC)
+AC_SUBST(TCL_LIBS)
+AC_SUBST(TCL_SRC_DIR)
+AC_SUBST(TCL_BIN_DIR)
+AC_SUBST(TCL_LIB_SPEC)
+AC_SUBST(TCL_LD_SEARCH_FLAGS)
+AC_SUBST(TCL_LIB_HNAME)
+AC_SUBST(TCL_SRC_DIR)
+AC_SUBST(TCL_VERSION)
+AC_SUBST(NEO_BUILD_LIB_SPEC)
+AC_SUBST(NEO_LD_SEARCH_FLAGS)
+AC_SUBST(NEO_SHARED_LIB_FILE)
+AC_SUBST(NEO_UNSHARED_LIB_FILE)
+AC_SUBST(NEO_LIB_FILE)
+AC_SUBST(NEO_LIB_SPEC)
+AC_SUBST(NEO_MAJOR_VERSION)
+AC_SUBST(NEO_MINOR_VERSION)
+AC_SUBST(NEO_SHLIB_CFLAGS)
+AC_SUBST(NEO_VERSION)
+dnl AC_SUBST(XINCLUDES)
+dnl AC_SUBST(XLIBSW)
+
+AC_OUTPUT(Makefile pkgIndex.tcl)
diff --git a/contrib/ldaptcl/install-sh b/contrib/ldaptcl/install-sh
new file mode 100755
index 0000000..0ff4b6a
--- /dev/null
+++ b/contrib/ldaptcl/install-sh
@@ -0,0 +1,119 @@
+#!/bin/sh
+
+#
+# install - install a program, script, or datafile
+# This comes from X11R5; it is not part of GNU.
+#
+# $XConsortium: install.sh,v 1.2 89/12/18 14:47:22 jim Exp $
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+
+instcmd="$mvprog"
+chmodcmd=""
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+fi
+
+if [ x"$dst" = x ]
+then
+ echo "install: no destination specified"
+ exit 1
+fi
+
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+if [ -d $dst ]
+then
+ dst="$dst"/`basename $src`
+fi
+
+# Make a temp file name in the proper directory.
+
+dstdir=`dirname $dst`
+dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+$doit $instcmd $src $dsttmp
+
+# and set any options; do chmod last to preserve setuid bits
+
+if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; fi
+if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; fi
+if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; fi
+if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; fi
+
+# Now rename the file to the real destination.
+
+$doit $rmcmd $dst
+$doit $mvcmd $dsttmp $dst
+
+
+exit 0
diff --git a/contrib/ldaptcl/ldap.n b/contrib/ldaptcl/ldap.n
new file mode 100644
index 0000000..ec75e3d
--- /dev/null
+++ b/contrib/ldaptcl/ldap.n
@@ -0,0 +1,395 @@
+'\"
+'\" Copyright (c) 1998 NeoSoft, Inc.
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+.so man.macros
+.TH ldap n "" Ldap "Ldap Tcl Extension"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+ldap \- connect to and query an LDAP server
+.SH SYNOPSIS
+\fBldap \fBopen \fR \fIcommand\fR \fIhostlist\fR
+.br
+\fBldap \fBinit \fR \fIcommand\fR \fIhostlist\fR ?protocol_version [2|3]?
+.br
+\fBldap \fBexplode ?-nonames|-list?\fR \fIdn\fR
+.br
+\fIcommand \fBsubcommand \fIoptions ...\fR
+.BE
+
+.SH OVERVIEW
+.PP
+A new command by the name of \fIcommand\fR will be created to access
+the LDAP database at \fIhostlist\fR. \fIhostlist\fR may contain elements
+of the format \fBhost:port\fR if a port other than the default LDAP port
+of 389 is required. The LDAP library will attempt to connect to each
+host in turn until it succeeds or exhausts the list.
+.PP
+The \fBexplode\fR form provides a means (via ldap_explode(3)) to explode a DN
+into its component parts. \fB-nonames\fR strips off the attribute names,
+and -list returns a list suitable for \fBarray set\fR.
+.PP
+Finally, the last form, described in more detail below, refers generically
+to how the command created by the first two examples is used.
+.SH DESCRIPTION
+
+The Lightweight Directory Access Protocol provides TCP/IP access to
+X.500 directory services and/or to a stand-alone LDAP server.
+
+This code provides a Tcl interface to the
+Lightweight Directory Access Protocol package using the Netscape
+Software Development Kit. It can also be used with the freely
+redistributable University of
+Michigan (http://www.umich.edu/~rsug/ldap) version by defining the
+UMICH_LDAP macro during compilation.
+
+.SH CONNECTING TO AN LDAP SERVER
+
+To create an ldap interface entity, we use the "ldap" command.
+
+ ldap open foo foo.bar.com
+
+This opens a connection to a LDAP server on foo.bar.com, and makes
+a new Tcl command, foo, through which we will manipulate the interface
+and make queries to the remote LDAP server.
+
+ ldap init foo foo.bar.com
+
+Same as above, foo is created, but for "init", opening the connection is
+deferred until we actually try to do something.
+
+The init command also allows some optional values to be set for the connection.
+Currently, the only useful option is \fBprotocol_version\fR which take a
+single argument to specify to use LDAP protocol 2 or 3. This may be required
+when connecting to older LDAP server.
+
+For the purposes of this example, we're going to assume that "foo" is the
+command created by opening a connection using "ldap open".
+
+.SH BINDING
+
+After a connection is made to an LDAP server, an LDAP bind operation must
+be performed before other operations can be attempted over the connection.
+
+Both simple authentication and kerberos authentication are available.
+LDAP version 3 supports many new "SSL"-style authentication and encryption
+systems, which are not currently supported by the OpenLDAP v1.2 server, and
+hence by this interface package.
+
+Currently simple and kerberos-based authentication, are supported.
+
+To use LDAP and still have reasonable security in a networked,
+Internet/Intranet environment, secure shell can be used to setup
+secure, encrypted connections between client machines and the LDAP
+server, and between all LDAP nodes that might be used.
+
+To perform the LDAP "bind" operation:
+
+ foo bind simple dn password
+
+ foo bind kerberos_ldap
+ foo bind kerberos_dsa
+ foo bind kerberos_both
+
+It either returns nothing (success), or a Tcl error with appropriate error
+text.
+
+For example,
+
+ foo bind simple "cn=Manager,o=NeoSoft Inc,c=us" "secret"
+
+If you attempt to bind with one of the kerberos authentication types
+described above and your LDAP library was not built with KERBEROS
+defined, you will get an unknown auth type error.
+
+To unbind an LDAP connection previously bound with "bind":
+
+ foo unbind
+
+Note that unbinding also deletes the command (\fBfoo\fR in this case).
+Deleting the command has the same affect.
+
+The ability of the library to callback to the client, enabling re-binding
+while following referrals, is not currently supported.
+
+.SH DELETING OBJECTS
+
+To delete an object in the LDAP database, use
+
+ foo delete dn
+
+To rename an object to another relative distinguished name, use
+
+ foo rename_rdn dn rdn
+
+To rename an object to another relative distinguished name, leaving
+the old entry as some kind of attribute (FIX: not sure if this is
+right or how it works)
+
+ foo modify_rdn dn rdn
+
+
+.SH ADDING NEW OBJECTS
+
+ foo add dn attributePairList
+
+This creates a new distinguished name and defines zero or more attributes.
+
+"attributePairList" is a list of key-value pairs, the same as would
+be returned by "array get" if an array had been set up containing the
+key-value pairs.
+
+ foo add "cn=karl, ou=People, o=NeoSoft Inc, c=US" {cn karl ...}
+
+Some directory servers and/or their client SDKs will automatically
+add the leaf attribute value for you.
+
+Here is a more precise description of how an attributePairList looks:
+
+ {cn {karl {Karl Lehenbauer}} telephone 713-968-5800}
+
+Note here that two cn values, "karl" and "Karl Lehenbauer", are added.
+Is it an error to write:
+
+ {cn {Karl Lehenbauer}}
+
+Which adds two cn values, "Karl" and "Lehenbauer", when the intention
+was to give a single cn value of "Karl Lehenbauer". In real life, one
+finds oneself making prodigous use of the \fBlist\fR command rather than
+typing hard-coded lists.
+
+We have noticed that the Netscape server will automatically add the
+left-most rdn portion of the DN (ie. cn=karl), whereas the University
+of Michigan and OpenLDAP 1.2 versions do not.
+
+.SH ADDING, DELETING, AND REPLACING OBJECT ATTRIBUTES
+
+You can have multiple values for a given attribute in an LDAP object.
+These are represented in search results, through the Tcl interface,
+as a list.
+
+ foo add_attributes dn attributePairList
+
+This adds key-value pairs to an existing DN. If an attribute being
+added already exists, the new value will be appended to the list.
+If a particular value being added to an attribute already exists in
+the object a Tcl error is raised.
+
+ foo replace_attributes dn attributePairList
+
+This replaces the specified attributes in an existing DN, leaving
+unnamed ones untouched. Any previous values for the supplied attributes
+(if any) are discarded.
+
+ foo delete_attributes dn attributePairList
+
+This deletes attributes in the list. If an attribute "foo" has the
+value list {bar snap}, and you delete using the attributePairList "foo bar",
+"foo" will still have "snap".
+
+If you provide an empty string ("") for the value list,
+the entire attribute will be deleted.
+
+In Ldaptcl version 2.0, multiple operations may be combined into a single
+transaction, ie. as in:
+
+ foo add_attributes dn attributePairList replace attributePairList \
+ delete attributePairList
+
+.SH SEARCHING
+
+The Tcl interface to searching takes a control array, which contains
+a couple of mandatory key-value pairs, and can contain a number of
+optional key-value pairs as well, for controlling the search, a
+destination array, into which the specified attributes (or all attributes
+of matching DNs if none are specified) and values are stored.
+
+The "code" part is executed repeatedly, once for each DN matching the
+search criteria.
+
+.nf
+ foo search controlArray destArray code
+
+ Using data in the control array, a search is performed of the
+ LDAP server opened when foo was created. Possible elements
+ of the control array are enumerated blow.
+
+ controlArray(base) is the DN being searched from. (required)
+
+ controlArray(filter) contains the search criteria. (required)
+
+ controlArray(scope) must be "base", "one_level", or "subtree".
+ If not specified, scope defaults to "subtree".
+
+ controlArray(deref) must be "never", "search", "find", or "always"
+ If not specified, deref defaults to "never"
+
+ controlArray(attributes) is a list of attributes to be fetched.
+ If not specified, all attributes are fetched.
+
+ controlArray(timeout) a timeout value in seconds (may contain
+ fractional values -- extremely very small values are useful
+ for forcing timeout conditions to test timeouts).
+.fi
+
+For each matching record, destArray is populated with none,
+some or all attribute-value pairs as determined by the request and
+access control lists on the server.
+
+Note: There are some additional parameters that can be set, such as
+how long the synchronous version of the routines should wait before
+timing out, the interfaces for which are not available in the current
+version.
+
+.SH COMPARE
+
+ foo compare dn attribute value
+
+Interface to the ldap_compare_s() command.
+Compares the value of \fIattribute\fR in the object at \fIdn\fR to the
+\fIvalue\fR given in the command line. Returns an error if \fIdn\fR
+does not exist. Otherwise, a
+
+.SH CACHING (Note: Netscape clients do not have caching interfaces).
+
+The UMich and OpenLDAP client libraries offers the client application fairly
+fine-grained control of caching of results retrieved from searches,
+offering significant performance improvement and reduced
+network traffic.
+
+By default, the cache is disabled.
+
+To enable caching of data received from an LDAP connection,
+
+ foo cache enable timeout maxmem
+
+ ...where timeout is specified in seconds, and maxmem is the
+ maximum memory to be used for caching, in bytes.
+
+ If maxmem is 0, the cache size is restricted only by the timeout.
+
+ foo cache disable
+
+ ...temporarily inhibits use of the cache (while disabled, new requests
+ are not cached and the cache is not checked when returning results).
+
+ Disabling the cache does not delete its contents.
+
+ foo cache destroy
+
+ ...turns off caching and completely removes the cache from memory.
+
+ foo cache flush
+
+ ...deletes the entire cache contents, but does not affect
+ whether or not the cache is being used.
+
+ foo cache uncache dn
+
+ ...removes from the cache all request results that make reference
+ to the specified DN.
+
+ This should be used, for example, after doing an add_attributes,
+ delete_attributes, or replace_attributes (ldap_modify(3))
+ involving the requested DN. Generally this should not be needed,
+ as the Tcl interface automatically performs this operation on
+ any dn that is modified (add,replace,delete) while caching is
+ enabled.
+
+ foo cache no_errors
+
+ ...suppresses caching of any requests that result in an error.
+
+ foo cache size_errors
+
+ ...suppresses caching of any requests that result in an error,
+ except for requests resulting in "sizelimit exceeded", which
+ are cached. This is the default.
+
+ foo cache all_errors
+
+ ...enables caching of all requests, including those that result
+ in errors.
+
+.SH IMPLEMENTATION DECISIONS
+
+Because we used the new "Tcl object" C interfaces, this package only works
+with Tcl 8.0 or above.
+
+This package interfaces with the University of Michigan LDAP protocol
+package, version 3.3, and OpenLDAP version 1.2, both of which are
+implementations of version 2 of the LDAP protocol.
+
+Although an LDAP client (or server) could be written in native Tcl 8.0,
+as Tcl 8.0 and above can do binary I/O, and Tcl 8 and above have strings
+that are fully eight-bit clean, for a first implementation, to minimize
+compatibility problems, we created a C interface to the UMich LDAP library.
+
+A native Tcl implementation would be cool because we could bring the receiving
+of messages into the normal Tcl event loop and run the LDAP interface fully
+asynchronous.
+
+This implementation is blocking, and blocking only. That is to say that
+the Tcl event loop is frozen while the ldap routines are waiting on data.
+
+This could be fixed either by recoding all of the I/O in the LDAP library
+to use Tcl's I/O system instead, or by simply coding the LDAP interface in
+native Tcl, as mentioned above.
+
+Another advantage of coding in high-level Tcl, of course, is that the
+client would immediately be cross-platform to Windows and the Mac, as
+well as Unix.
+
+Binary data is not currently supported. It will probably be trivial to
+add, we just haven't dug into it yet.
+
+
+.SH FOR MORE INFORMATION
+
+This document principally describes how to use our Tcl interface to the
+LDAP library works.
+
+For more information on LDAP and the University of Michigan LDAP package,
+please visit the website mentioned above. The package includes substantial
+documentation in the form of UNIX manual pages, a SLAPD/SLURPD guide
+in Adobe Portable Document Format (pdf), and a number of Internet RFCs
+related to LDAP services.
+
+.SH AUTHORS
+It was written by Karl Lehenbauer, of NeoSoft, Inc., in August and
+September of 1997. Ldap explode, and numerous bug fixes and extensions
+by Randy Kunkee, also of NeoSoft, Inc., in 1998-1999.
+
+.SH KEYWORDS
+element, join, list, separator
+.SH BUGS
+The \fBldap init\fR syntax fails to return anything useful. Use
+\fBldap open\fR instead.
+
+\fBPackage require Ldaptcl\fR won't work unless the ldap and lber libraries
+are also shared, and ldaptcl.so is itself created with the correct flags
+(eg. -R for Solaris). In short there's a lot of details to make this part
+work, but it should work out of the box for Solaris. Other systems may
+require that LD_LIBRARY_PATH or other appropriate environment variables
+be set at build and/or runtime.
+
+An asynchronous interface should be provided with callbacks.
+
+We have never tested Kerberos authentication.
+
+It does not tolerate some illegal operations very well.
+
+It is possible to create empty attributes, ie. attributes which are present
+but have no value. This is done by deleting the attribute values rather
+than, eg. "foo delete_attributes dn {telephone {}}" which would delete
+the telephone attribute altogether. A search for presence of the attribute
+may return an object, and yet it may have no value. This interface presents
+such an object as not having the attribute at all (ie. you cannot tell).
+The Netscape SDK does this for you, so this makes the behavior consistent
+when using UMICH_LDAP.
+
+\--enable-netscape configuration support has not been tested and probably
+has bugs.
diff --git a/contrib/ldaptcl/ldaperr.tcl b/contrib/ldaptcl/ldaperr.tcl
new file mode 100644
index 0000000..e9f8568
--- /dev/null
+++ b/contrib/ldaptcl/ldaperr.tcl
@@ -0,0 +1,54 @@
+#
+# ldaperr.tcl: scan ldap.h for error return codes for initializing
+# errorCode table.
+#
+
+proc genstrings {path} {
+ set fp [open $path]
+ while {[gets $fp line] != -1 &&
+ ![string match "#define LDAP_SUCCESS*" $line]} { }
+ puts "/* This file automatically generated, hand edit at your own risk! */"
+ puts -nonewline "char *ldaptclerrorcode\[\] = {
+ NULL"
+ while {[gets $fp line] != -1} {
+ if {[clength $line] == 0 || [ctype space $line]} continue
+ if {[string match *typedef* $line]} break
+ if {![string match #define* $line]} continue
+ if {![string match "#define LDAP_*" $line]} continue
+ if {[string match "*LDAP_RANGE*" $line]} continue
+ if {[string match "*LDAP_API_RESULT*" $line]} continue
+ if {[string match {*\\} $line]} {
+ append line [gets $fp]
+ }
+ lassign $line define macro value
+ set ldap_errcode($macro) $value
+ }
+ #parray ldap_errcode
+ foreach i [array names ldap_errcode] {
+ set value $ldap_errcode($i)
+ #puts stderr "checking $value"
+ if [regexp {^[A-Z_]} $value] {
+ if [info exists ldap_errcode($value)] {
+ set value $ldap_errcode($value)
+ set ldap_errcode($i) $value
+ }
+ }
+ set ldap_errname($value) $i
+ }
+ set lasterr 0
+ foreach value [lsort -integer [array names ldap_errname]] {
+ incr lasterr
+ while {$lasterr < $value} {
+ puts -nonewline ",\n\tNULL"
+ incr lasterr
+ }
+ puts -nonewline ",\n\t\"$ldap_errname($value)\""
+ }
+ puts "\n};"
+ puts "#define LDAPTCL_MAXERR\t$value"
+}
+
+#cmdtrace on
+if !$tcl_interactive {
+ genstrings [lindex $argv 0]
+}
diff --git a/contrib/ldaptcl/man.macros b/contrib/ldaptcl/man.macros
new file mode 100644
index 0000000..3af2da9
--- /dev/null
+++ b/contrib/ldaptcl/man.macros
@@ -0,0 +1,236 @@
+'\" The definitions below are for supplemental macros used in Tcl/Tk
+'\" manual entries.
+'\"
+'\" .AP type name in/out ?indent?
+'\" Start paragraph describing an argument to a library procedure.
+'\" type is type of argument (int, etc.), in/out is either "in", "out",
+'\" or "in/out" to describe whether procedure reads or modifies arg,
+'\" and indent is equivalent to second arg of .IP (shouldn't ever be
+'\" needed; use .AS below instead)
+'\"
+'\" .AS ?type? ?name?
+'\" Give maximum sizes of arguments for setting tab stops. Type and
+'\" name are examples of largest possible arguments that will be passed
+'\" to .AP later. If args are omitted, default tab stops are used.
+'\"
+'\" .BS
+'\" Start box enclosure. From here until next .BE, everything will be
+'\" enclosed in one large box.
+'\"
+'\" .BE
+'\" End of box enclosure.
+'\"
+'\" .CS
+'\" Begin code excerpt.
+'\"
+'\" .CE
+'\" End code excerpt.
+'\"
+'\" .VS ?version? ?br?
+'\" Begin vertical sidebar, for use in marking newly-changed parts
+'\" of man pages. The first argument is ignored and used for recording
+'\" the version when the .VS was added, so that the sidebars can be
+'\" found and removed when they reach a certain age. If another argument
+'\" is present, then a line break is forced before starting the sidebar.
+'\"
+'\" .VE
+'\" End of vertical sidebar.
+'\"
+'\" .DS
+'\" Begin an indented unfilled display.
+'\"
+'\" .DE
+'\" End of indented unfilled display.
+'\"
+'\" .SO
+'\" Start of list of standard options for a Tk widget. The
+'\" options follow on successive lines, in four columns separated
+'\" by tabs.
+'\"
+'\" .SE
+'\" End of list of standard options for a Tk widget.
+'\"
+'\" .OP cmdName dbName dbClass
+'\" Start of description of a specific option. cmdName gives the
+'\" option's name as specified in the class command, dbName gives
+'\" the option's name in the option database, and dbClass gives
+'\" the option's class in the option database.
+'\"
+'\" .UL arg1 arg2
+'\" Print arg1 underlined, then print arg2 normally.
+'\"
+'\" SCCS: @(#) man.macros 1.9 97/08/22 18:50:59
+'\"
+'\" # Set up traps and other miscellaneous stuff for Tcl/Tk man pages.
+.if t .wh -1.3i ^B
+.nr ^l \n(.l
+.ad b
+'\" # Start an argument description
+.de AP
+.ie !"\\$4"" .TP \\$4
+.el \{\
+. ie !"\\$2"" .TP \\n()Cu
+. el .TP 15
+.\}
+.ie !"\\$3"" \{\
+.ta \\n()Au \\n()Bu
+\&\\$1 \\fI\\$2\\fP (\\$3)
+.\".b
+.\}
+.el \{\
+.br
+.ie !"\\$2"" \{\
+\&\\$1 \\fI\\$2\\fP
+.\}
+.el \{\
+\&\\fI\\$1\\fP
+.\}
+.\}
+..
+'\" # define tabbing values for .AP
+.de AS
+.nr )A 10n
+.if !"\\$1"" .nr )A \\w'\\$1'u+3n
+.nr )B \\n()Au+15n
+.\"
+.if !"\\$2"" .nr )B \\w'\\$2'u+\\n()Au+3n
+.nr )C \\n()Bu+\\w'(in/out)'u+2n
+..
+.AS Tcl_Interp Tcl_CreateInterp in/out
+'\" # BS - start boxed text
+'\" # ^y = starting y location
+'\" # ^b = 1
+.de BS
+.br
+.mk ^y
+.nr ^b 1u
+.if n .nf
+.if n .ti 0
+.if n \l'\\n(.lu\(ul'
+.if n .fi
+..
+'\" # BE - end boxed text (draw box now)
+.de BE
+.nf
+.ti 0
+.mk ^t
+.ie n \l'\\n(^lu\(ul'
+.el \{\
+.\" Draw four-sided box normally, but don't draw top of
+.\" box if the box started on an earlier page.
+.ie !\\n(^b-1 \{\
+\h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
+.\}
+.el \}\
+\h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\l'|0u-1.5n\(ul'
+.\}
+.\}
+.fi
+.br
+.nr ^b 0
+..
+'\" # VS - start vertical sidebar
+'\" # ^Y = starting y location
+'\" # ^v = 1 (for troff; for nroff this doesn't matter)
+.de VS
+.if !"\\$2"" .br
+.mk ^Y
+.ie n 'mc \s12\(br\s0
+.el .nr ^v 1u
+..
+'\" # VE - end of vertical sidebar
+.de VE
+.ie n 'mc
+.el \{\
+.ev 2
+.nf
+.ti 0
+.mk ^t
+\h'|\\n(^lu+3n'\L'|\\n(^Yu-1v\(bv'\v'\\n(^tu+1v-\\n(^Yu'\h'-|\\n(^lu+3n'
+.sp -1
+.fi
+.ev
+.\}
+.nr ^v 0
+..
+'\" # Special macro to handle page bottom: finish off current
+'\" # box/sidebar if in box/sidebar mode, then invoked standard
+'\" # page bottom macro.
+.de ^B
+.ev 2
+'ti 0
+'nf
+.mk ^t
+.if \\n(^b \{\
+.\" Draw three-sided box if this is the box's first page,
+.\" draw two sides but no top otherwise.
+.ie !\\n(^b-1 \h'-1.5n'\L'|\\n(^yu-1v'\l'\\n(^lu+3n\(ul'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
+.el \h'-1.5n'\L'|\\n(^yu-1v'\h'\\n(^lu+3n'\L'\\n(^tu+1v-\\n(^yu'\h'|0u'\c
+.\}
+.if \\n(^v \{\
+.nr ^x \\n(^tu+1v-\\n(^Yu
+\kx\h'-\\nxu'\h'|\\n(^lu+3n'\ky\L'-\\n(^xu'\v'\\n(^xu'\h'|0u'\c
+.\}
+.bp
+'fi
+.ev
+.if \\n(^b \{\
+.mk ^y
+.nr ^b 2
+.\}
+.if \\n(^v \{\
+.mk ^Y
+.\}
+..
+'\" # DS - begin display
+.de DS
+.RS
+.nf
+.sp
+..
+'\" # DE - end display
+.de DE
+.fi
+.RE
+.sp
+..
+'\" # SO - start of list of standard options
+.de SO
+.SH "STANDARD OPTIONS"
+.LP
+.nf
+.ta 4c 8c 12c
+.ft B
+..
+'\" # SE - end of list of standard options
+.de SE
+.fi
+.ft R
+.LP
+See the \\fBoptions\\fR manual entry for details on the standard options.
+..
+'\" # OP - start of full description for a single option
+.de OP
+.LP
+.nf
+.ta 4c
+Command-Line Name: \\fB\\$1\\fR
+Database Name: \\fB\\$2\\fR
+Database Class: \\fB\\$3\\fR
+.fi
+.IP
+..
+'\" # CS - begin code excerpt
+.de CS
+.RS
+.nf
+.ta .25i .5i .75i 1i
+..
+'\" # CE - end code excerpt
+.de CE
+.fi
+.RE
+..
+.de UL
+\\$1\l'|0\(ul'\\$2
+..
diff --git a/contrib/ldaptcl/neoXldap.c b/contrib/ldaptcl/neoXldap.c
new file mode 100644
index 0000000..1adf22d
--- /dev/null
+++ b/contrib/ldaptcl/neoXldap.c
@@ -0,0 +1,1470 @@
+/*
+ * NeoSoft Tcl client extensions to Lightweight Directory Access Protocol.
+ *
+ * Copyright (c) 1998-1999 NeoSoft, Inc.
+ * All Rights Reserved.
+ *
+ * This software may be used, modified, copied, distributed, and sold,
+ * in both source and binary form provided that these copyrights are
+ * retained and their terms are followed.
+ *
+ * Under no circumstances are the authors or NeoSoft Inc. responsible
+ * for the proper functioning of this software, nor do the authors
+ * assume any liability for damages incurred with its use.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to NeoSoft, Inc.
+ *
+ * NeoSoft, Inc. may not be used to endorse or promote products derived
+ * from this software without specific prior written permission. This
+ * software is provided ``as is'' without express or implied warranty.
+ *
+ * Requests for permission may be sent to NeoSoft Inc, 1770 St. James Place,
+ * Suite 500, Houston, TX, 77056.
+ *
+ * $OpenLDAP$
+ *
+ */
+
+/*
+ * This code was originally developed by Karl Lehenbauer to work with
+ * Umich-3.3 LDAP. It was debugged against the Netscape LDAP server
+ * and their much more reliable SDK, and again backported to the
+ * Umich-3.3 client code. The UMICH_LDAP define is used to include
+ * code that will work with the Umich-3.3 LDAP, but not with Netscape's
+ * SDK. OpenLDAP may support some of these, but they have not been tested.
+ * Currently supported by Randy Kunkee (kunkee@OpenLDAP.org).
+ */
+
+/*
+ * Add timeout to controlArray to set timeout for ldap_result.
+ * 4/14/99 - Randy
+ */
+
+#include "tclExtend.h"
+
+#include <lber.h>
+#include <ldap.h>
+#include <string.h>
+#include <sys/time.h>
+#include <math.h>
+
+/*
+ * Macros to do string compares. They pre-check the first character before
+ * checking of the strings are equal.
+ */
+
+#define STREQU(str1, str2) \
+ (((str1) [0] == (str2) [0]) && (strcmp (str1, str2) == 0))
+#define STRNEQU(str1, str2, n) \
+ (((str1) [0] == (str2) [0]) && (strncmp (str1, str2, n) == 0))
+
+/*
+ * The following section defines some common macros used by the rest
+ * of the code. It's ugly, and can use some work. This code was
+ * originally developed to work with Umich-3.3 LDAP. It was debugged
+ * against the Netscape LDAP server and the much more reliable SDK,
+ * and then again backported to the Umich-3.3 client code.
+ */
+#define OPEN_LDAP 1
+#if defined(OPEN_LDAP)
+ /* LDAP_API_VERSION must be defined per the current draft spec
+ ** it's value will be assigned RFC number. However, as
+ ** no RFC is defined, it's value is currently implementation
+ ** specific (though I would hope it's value is greater than 1823).
+ ** In OpenLDAP 2.x-devel, its 2000 + the draft number, ie 2002.
+ ** This section is for OPENLDAP.
+ */
+#ifndef LDAP_API_FEATURE_X_OPENLDAP
+#define ldap_memfree(p) free(p)
+#endif
+#ifdef LDAP_OPT_ERROR_NUMBER
+#define ldap_get_lderrno(ld) (ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &lderrno), lderrno)
+#else
+#define ldap_get_lderrno(ld) (ld->ld_errno)
+#endif
+#define LDAP_ERR_STRING(ld) \
+ ldap_err2string(ldap_get_lderrno(ld))
+#elif defined( LDAP_OPT_SIZELIMIT )
+ /*
+ ** Netscape SDK w/ ldap_set_option, ldap_get_option
+ */
+#define LDAP_ERR_STRING(ld) \
+ ldap_err2string(ldap_get_lderrno(ldap))
+#else
+ /* U-Mich/OpenLDAP 1.x API */
+ /* RFC-1823 w/ changes */
+#define UMICH_LDAP 1
+#define ldap_memfree(p) free(p)
+#define ldap_ber_free(p, n) ber_free(p, n)
+#define ldap_value_free_len(bvals) ber_bvecfree(bvals)
+#define ldap_get_lderrno(ld) (ld->ld_errno)
+#define LDAP_ERR_STRING(ld) \
+ ldap_err2string(ld->ld_errno)
+#endif
+
+typedef struct ldaptclobj {
+ LDAP *ldap;
+ int caching; /* flag 1/0 if caching is enabled */
+ long timeout; /* timeout from last cache enable */
+ long maxmem; /* maxmem from last cache enable */
+ Tcl_Obj *trapCmdObj; /* error handler */
+ int *traplist; /* list of errorCodes to trap */
+ int flags;
+} LDAPTCL;
+
+
+#define LDAPTCL_INTERRCODES 0x001
+
+#include "ldaptclerr.h"
+
+static
+LDAP_SetErrorCode(LDAPTCL *ldaptcl, int code, Tcl_Interp *interp)
+{
+ char shortbuf[16];
+ char *errp;
+ int lderrno;
+
+ if (code == -1)
+ code = ldap_get_lderrno(ldaptcl->ldap);
+ if ((ldaptcl->flags & LDAPTCL_INTERRCODES) || code > LDAPTCL_MAXERR ||
+ ldaptclerrorcode[code] == NULL) {
+ sprintf(shortbuf, "0x%03x", code);
+ errp = shortbuf;
+ } else
+ errp = ldaptclerrorcode[code];
+
+ Tcl_SetErrorCode(interp, errp, NULL);
+ if (ldaptcl->trapCmdObj) {
+ int *i;
+ Tcl_Obj *cmdObj;
+ if (ldaptcl->traplist != NULL) {
+ for (i = ldaptcl->traplist; *i && *i != code; i++)
+ ;
+ if (*i == 0) return;
+ }
+ (void) Tcl_EvalObj(interp, ldaptcl->trapCmdObj);
+ }
+}
+
+static
+LDAP_ErrorStringToCode(Tcl_Interp *interp, char *s)
+{
+ int offset;
+ int code;
+
+ offset = (strncasecmp(s, "LDAP_", 5) == 0) ? 0 : 5;
+ for (code = 0; code < LDAPTCL_MAXERR; code++) {
+ if (!ldaptclerrorcode[code]) continue;
+ if (strcasecmp(s, ldaptclerrorcode[code]+offset) == 0)
+ return code;
+ }
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, s, " is an invalid code", (char *) NULL);
+ return -1;
+}
+
+/*-----------------------------------------------------------------------------
+ * LDAP_ProcessOneSearchResult --
+ *
+ * Process one result return from an LDAP search.
+ *
+ * Parameters:
+ * o interp - Tcl interpreter; Errors are returned in result.
+ * o ldap - LDAP structure pointer.
+ * o entry - LDAP message pointer.
+ * o destArrayNameObj - Name of Tcl array in which to store attributes.
+ * o evalCodeObj - Tcl_Obj pointer to code to eval against this result.
+ * Returns:
+ * o TCL_OK if processing succeeded..
+ * o TCL_ERROR if an error occurred, with error message in interp.
+ *-----------------------------------------------------------------------------
+ */
+int
+LDAP_ProcessOneSearchResult (interp, ldap, entry, destArrayNameObj, evalCodeObj)
+ Tcl_Interp *interp;
+ LDAP *ldap;
+ LDAPMessage *entry;
+ Tcl_Obj *destArrayNameObj;
+ Tcl_Obj *evalCodeObj;
+{
+ char *attributeName;
+ Tcl_Obj *attributeNameObj;
+ Tcl_Obj *attributeDataObj;
+ int i;
+ BerElement *ber;
+ struct berval **bvals;
+ char *dn;
+ int lderrno;
+
+ Tcl_UnsetVar (interp, Tcl_GetStringFromObj (destArrayNameObj, NULL), 0);
+
+ dn = ldap_get_dn(ldap, entry);
+ if (dn != NULL) {
+ if (Tcl_SetVar2(interp, /* set dn */
+ Tcl_GetStringFromObj(destArrayNameObj, NULL),
+ "dn",
+ dn,
+ TCL_LEAVE_ERR_MSG) == NULL)
+ return TCL_ERROR;
+ ldap_memfree(dn);
+ }
+ attributeNameObj = Tcl_NewObj();
+ Tcl_IncrRefCount (attributeNameObj);
+
+ /* Note that attributeName below is allocated for OL2+ libldap, so it
+ must be freed with ldap_memfree(). Test below is admittedly a hack.
+ */
+
+ for (attributeName = ldap_first_attribute (ldap, entry, &ber);
+ attributeName != NULL;
+ attributeName = ldap_next_attribute(ldap, entry, ber)) {
+
+ bvals = ldap_get_values_len(ldap, entry, attributeName);
+
+ if (bvals != NULL) {
+ /* Note here that the U.of.M. ldap will return a null bvals
+ when the last attribute value has been deleted, but still
+ retains the attributeName. Even though this is documented
+ as an error, we ignore it to present a consistent interface
+ with Netscape's server
+ */
+ attributeDataObj = Tcl_NewObj();
+ Tcl_SetStringObj(attributeNameObj, attributeName, -1);
+#if LDAP_API_VERSION >= 2004
+ ldap_memfree(attributeName); /* free if newer API */
+#endif
+ for (i = 0; bvals[i] != NULL; i++) {
+ Tcl_Obj *singleAttributeValueObj;
+
+ singleAttributeValueObj = Tcl_NewStringObj(bvals[i]->bv_val, bvals[i]->bv_len);
+ if (Tcl_ListObjAppendElement (interp,
+ attributeDataObj,
+ singleAttributeValueObj)
+ == TCL_ERROR) {
+ ber_free(ber, 0);
+ return TCL_ERROR;
+ }
+ }
+
+ ldap_value_free_len(bvals);
+
+ if (Tcl_ObjSetVar2 (interp,
+ destArrayNameObj,
+ attributeNameObj,
+ attributeDataObj,
+ TCL_LEAVE_ERR_MSG) == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ }
+ Tcl_DecrRefCount (attributeNameObj);
+ return Tcl_EvalObj (interp, evalCodeObj);
+}
+
+/*-----------------------------------------------------------------------------
+ * LDAP_PerformSearch --
+ *
+ * Perform an LDAP search.
+ *
+ * Parameters:
+ * o interp - Tcl interpreter; Errors are returned in result.
+ * o ldap - LDAP structure pointer.
+ * o base - Base DN from which to perform search.
+ * o scope - LDAP search scope, must be one of LDAP_SCOPE_BASE,
+ * LDAP_SCOPE_ONELEVEL, or LDAP_SCOPE_SUBTREE.
+ * o attrs - Pointer to array of char * pointers of desired
+ * attribute names, or NULL for all attributes.
+ * o filtpatt LDAP filter pattern.
+ * o value Value to get sprintf'ed into filter pattern.
+ * o destArrayNameObj - Name of Tcl array in which to store attributes.
+ * o evalCodeObj - Tcl_Obj pointer to code to eval against this result.
+ * Returns:
+ * o TCL_OK if processing succeeded..
+ * o TCL_ERROR if an error occurred, with error message in interp.
+ *-----------------------------------------------------------------------------
+ */
+int
+LDAP_PerformSearch (interp, ldaptcl, base, scope, attrs, filtpatt, value,
+ destArrayNameObj, evalCodeObj, timeout_p, all, sortattr)
+ Tcl_Interp *interp;
+ LDAPTCL *ldaptcl;
+ char *base;
+ int scope;
+ char **attrs;
+ char *filtpatt;
+ char *value;
+ Tcl_Obj *destArrayNameObj;
+ Tcl_Obj *evalCodeObj;
+ struct timeval *timeout_p;
+ int all;
+ char *sortattr;
+{
+ LDAP *ldap = ldaptcl->ldap;
+ char filter[BUFSIZ];
+ int resultCode;
+ int errorCode;
+ int abandon;
+ int tclResult = TCL_OK;
+ int msgid;
+ LDAPMessage *resultMessage = 0;
+ LDAPMessage *entryMessage = 0;
+ char *sortKey;
+
+ int lderrno;
+
+ sprintf(filter, filtpatt, value);
+
+ fflush(stderr);
+ if ((msgid = ldap_search (ldap, base, scope, filter, attrs, 0)) == -1) {
+ Tcl_AppendResult (interp,
+ "LDAP start search error: ",
+ LDAP_ERR_STRING(ldap),
+ (char *)NULL);
+ LDAP_SetErrorCode(ldaptcl, -1, interp);
+ return TCL_ERROR;
+ }
+
+ abandon = 0;
+ if (sortattr)
+ all = 1;
+ tclResult = TCL_OK;
+ while (!abandon) {
+ resultCode = ldap_result (ldap, msgid, all, timeout_p, &resultMessage);
+ if (resultCode != LDAP_RES_SEARCH_RESULT &&
+ resultCode != LDAP_RES_SEARCH_ENTRY)
+ break;
+
+ if (sortattr) {
+ sortKey = (strcasecmp(sortattr, "dn") == 0) ? NULL : sortattr;
+ ldap_sort_entries(ldap, &resultMessage, sortKey, strcasecmp);
+ }
+ entryMessage = ldap_first_entry(ldap, resultMessage);
+
+ while (entryMessage) {
+ tclResult = LDAP_ProcessOneSearchResult (interp,
+ ldap,
+ entryMessage,
+ destArrayNameObj,
+ evalCodeObj);
+ if (tclResult != TCL_OK) {
+ if (tclResult == TCL_CONTINUE) {
+ tclResult = TCL_OK;
+ } else if (tclResult == TCL_BREAK) {
+ tclResult = TCL_OK;
+ abandon = 1;
+ break;
+ } else if (tclResult == TCL_ERROR) {
+ char msg[100];
+ sprintf(msg, "\n (\"search\" body line %d)",
+ interp->errorLine);
+ Tcl_AddObjErrorInfo(interp, msg, -1);
+ abandon = 1;
+ break;
+ } else {
+ abandon = 1;
+ break;
+ }
+ }
+ entryMessage = ldap_next_entry(ldap, entryMessage);
+ }
+ if (resultCode == LDAP_RES_SEARCH_RESULT || all)
+ break;
+ if (resultMessage)
+ ldap_msgfree(resultMessage);
+ resultMessage = NULL;
+ }
+ if (abandon) {
+ if (resultMessage)
+ ldap_msgfree(resultMessage);
+ if (resultCode == LDAP_RES_SEARCH_ENTRY)
+ ldap_abandon(ldap, msgid);
+ return tclResult;
+ }
+ if (resultCode == -1) {
+ Tcl_ResetResult (interp);
+ Tcl_AppendResult (interp,
+ "LDAP result search error: ",
+ LDAP_ERR_STRING(ldap),
+ (char *)NULL);
+ LDAP_SetErrorCode(ldaptcl, -1, interp);
+ return TCL_ERROR;
+ }
+
+ if ((errorCode = ldap_result2error (ldap, resultMessage, 0))
+ != LDAP_SUCCESS) {
+ Tcl_ResetResult (interp);
+ Tcl_AppendResult (interp,
+ "LDAP search error: ",
+ ldap_err2string(errorCode),
+ (char *)NULL);
+ if (resultMessage)
+ ldap_msgfree(resultMessage);
+ LDAP_SetErrorCode(ldaptcl, errorCode, interp);
+ return TCL_ERROR;
+ }
+ if (resultMessage)
+ ldap_msgfree(resultMessage);
+ return tclResult;
+}
+
+/*-----------------------------------------------------------------------------
+ * NeoX_LdapTargetObjCmd --
+ *
+ * Implements the body of commands created by Neo_LdapObjCmd.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *-----------------------------------------------------------------------------
+ */
+int
+NeoX_LdapTargetObjCmd (clientData, interp, objc, objv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST objv[];
+{
+ char *command;
+ char *subCommand;
+ LDAPTCL *ldaptcl = (LDAPTCL *)clientData;
+ LDAP *ldap = ldaptcl->ldap;
+ char *dn;
+ int is_add = 0;
+ int is_add_or_modify = 0;
+ int mod_op = 0;
+ char *m, *s, *errmsg;
+ int errcode;
+ int tclResult;
+ int lderrno; /* might be used by LDAP_ERR_STRING macro */
+
+ Tcl_Obj *resultObj = Tcl_GetObjResult (interp);
+
+ if (objc < 2) {
+ Tcl_WrongNumArgs (interp, 1, objv, "subcommand [args...]");
+ return TCL_ERROR;
+ }
+
+ command = Tcl_GetStringFromObj (objv[0], NULL);
+ subCommand = Tcl_GetStringFromObj (objv[1], NULL);
+
+ /* object bind authtype name password */
+ if (STREQU (subCommand, "bind")) {
+ char *binddn;
+ char *passwd;
+ int stringLength;
+ char *ldap_authString;
+ int ldap_authInt;
+
+ if (objc != 5) {
+ Tcl_WrongNumArgs (interp, 2, objv, "authtype dn passwd");
+ return TCL_ERROR;
+ }
+
+ ldap_authString = Tcl_GetStringFromObj (objv[2], NULL);
+
+ if (STREQU (ldap_authString, "simple")) {
+ ldap_authInt = LDAP_AUTH_SIMPLE;
+ }
+#ifdef UMICH_LDAP
+ else if (STREQU (ldap_authString, "kerberos_ldap")) {
+ ldap_authInt = LDAP_AUTH_KRBV41;
+ } else if (STREQU (ldap_authString, "kerberos_dsa")) {
+ ldap_authInt = LDAP_AUTH_KRBV42;
+ } else if (STREQU (ldap_authString, "kerberos_both")) {
+ ldap_authInt = LDAP_AUTH_KRBV4;
+ }
+#endif
+ else {
+ Tcl_AppendStringsToObj (resultObj,
+ "\"",
+ command,
+ " ",
+ subCommand,
+#ifdef UMICH_LDAP
+ "\" authtype must be one of \"simple\", ",
+ "\"kerberos_ldap\", \"kerberos_dsa\" ",
+ "or \"kerberos_both\"",
+#else
+ "\" authtype must be \"simple\", ",
+#endif
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ binddn = Tcl_GetStringFromObj (objv[3], &stringLength);
+ if (stringLength == 0)
+ binddn = NULL;
+
+ passwd = Tcl_GetStringFromObj (objv[4], &stringLength);
+ if (stringLength == 0)
+ passwd = NULL;
+
+/* ldap_bind_s(ldap, dn, pw, method) */
+
+#ifdef UMICH_LDAP
+#define LDAP_BIND(ldap, dn, pw, method) \
+ ldap_bind_s(ldap, dn, pw, method)
+#else
+#define LDAP_BIND(ldap, dn, pw, method) \
+ ldap_simple_bind_s(ldap, dn, pw)
+#endif
+ if ((errcode = LDAP_BIND (ldap,
+ binddn,
+ passwd,
+ ldap_authInt)) != LDAP_SUCCESS) {
+
+ Tcl_AppendStringsToObj (resultObj,
+ "LDAP bind error: ",
+ ldap_err2string(errcode),
+ (char *)NULL);
+ LDAP_SetErrorCode(ldaptcl, errcode, interp);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+ }
+
+ if (STREQU (subCommand, "unbind")) {
+ if (objc != 2) {
+ Tcl_WrongNumArgs (interp, 2, objv, "");
+ return TCL_ERROR;
+ }
+
+ return Tcl_DeleteCommand(interp, Tcl_GetStringFromObj(objv[0], NULL));
+ }
+
+ /* object delete dn */
+ if (STREQU (subCommand, "delete")) {
+ if (objc != 3) {
+ Tcl_WrongNumArgs (interp, 2, objv, "dn");
+ return TCL_ERROR;
+ }
+
+ dn = Tcl_GetStringFromObj (objv [2], NULL);
+ if ((errcode = ldap_delete_s(ldap, dn)) != LDAP_SUCCESS) {
+ Tcl_AppendStringsToObj (resultObj,
+ "LDAP delete error: ",
+ ldap_err2string(errcode),
+ (char *)NULL);
+ LDAP_SetErrorCode(ldaptcl, errcode, interp);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+ }
+
+ /* object rename_rdn dn rdn */
+ /* object modify_rdn dn rdn */
+ if (STREQU (subCommand, "rename_rdn") || STREQU (subCommand, "modify_rdn")) {
+ char *rdn;
+ int deleteOldRdn;
+
+ if (objc != 4) {
+ Tcl_WrongNumArgs (interp, 2, objv, "dn rdn");
+ return TCL_ERROR;
+ }
+
+ dn = Tcl_GetStringFromObj (objv [2], NULL);
+ rdn = Tcl_GetStringFromObj (objv [3], NULL);
+
+ deleteOldRdn = (*subCommand == 'r');
+
+ if ((errcode = ldap_modrdn2_s (ldap, dn, rdn, deleteOldRdn)) != LDAP_SUCCESS) {
+ Tcl_AppendStringsToObj (resultObj,
+ "LDAP ",
+ subCommand,
+ " error: ",
+ ldap_err2string(errcode),
+ (char *)NULL);
+ LDAP_SetErrorCode(ldaptcl, errcode, interp);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+ }
+
+ /* object add dn attributePairList */
+ /* object add_attributes dn attributePairList */
+ /* object replace_attributes dn attributePairList */
+ /* object delete_attributes dn attributePairList */
+
+ if (STREQU (subCommand, "add")) {
+ is_add = 1;
+ is_add_or_modify = 1;
+ } else {
+ is_add = 0;
+ if (STREQU (subCommand, "add_attributes")) {
+ is_add_or_modify = 1;
+ mod_op = LDAP_MOD_ADD;
+ } else if (STREQU (subCommand, "replace_attributes")) {
+ is_add_or_modify = 1;
+ mod_op = LDAP_MOD_REPLACE;
+ } else if (STREQU (subCommand, "delete_attributes")) {
+ is_add_or_modify = 1;
+ mod_op = LDAP_MOD_DELETE;
+ }
+ }
+
+ if (is_add_or_modify) {
+ int result;
+ LDAPMod **modArray;
+ LDAPMod *mod;
+ char **valPtrs = NULL;
+ int attribObjc;
+ Tcl_Obj **attribObjv;
+ int valuesObjc;
+ Tcl_Obj **valuesObjv;
+ int nPairs, allPairs;
+ int i;
+ int j;
+ int pairIndex;
+ int modIndex;
+
+ Tcl_Obj *resultObj = Tcl_GetObjResult (interp);
+
+ if (objc < 4 || objc > 4 && is_add || is_add == 0 && objc&1) {
+ Tcl_AppendStringsToObj (resultObj,
+ "wrong # args: ",
+ Tcl_GetStringFromObj (objv [0], NULL),
+ " ",
+ subCommand,
+ " dn attributePairList",
+ (char *)NULL);
+ if (!is_add)
+ Tcl_AppendStringsToObj (resultObj,
+ " ?[add|delete|replace] attributePairList ...?", (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ dn = Tcl_GetStringFromObj (objv [2], NULL);
+
+ allPairs = 0;
+ for (i = 3; i < objc; i += 2) {
+ if (Tcl_ListObjLength (interp, objv[i], &j) == TCL_ERROR)
+ return TCL_ERROR;
+ if (j & 1) {
+ Tcl_AppendStringsToObj (resultObj,
+ "attribute list does not contain an ",
+ "even number of key-value elements",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ allPairs += j / 2;
+ }
+
+ modArray = (LDAPMod **)malloc (sizeof(LDAPMod *) * (allPairs + 1));
+
+ pairIndex = 3;
+ modIndex = 0;
+
+ do {
+
+ if (Tcl_ListObjGetElements (interp, objv [pairIndex], &attribObjc, &attribObjv)
+ == TCL_ERROR) {
+ mod_op = -1;
+ goto badop;
+ }
+
+ nPairs = attribObjc / 2;
+
+ for (i = 0; i < nPairs; i++) {
+ mod = modArray[modIndex++] = (LDAPMod *) malloc (sizeof(LDAPMod));
+ mod->mod_op = mod_op;
+ mod->mod_type = Tcl_GetStringFromObj (attribObjv [i * 2], NULL);
+
+ if (Tcl_ListObjGetElements (interp, attribObjv [i * 2 + 1], &valuesObjc, &valuesObjv) == TCL_ERROR) {
+ /* FIX: cleanup memory here */
+ mod_op = -1;
+ goto badop;
+ }
+
+ valPtrs = mod->mod_vals.modv_strvals = \
+ (char **)malloc (sizeof (char *) * (valuesObjc + 1));
+ valPtrs[valuesObjc] = (char *)NULL;
+
+ for (j = 0; j < valuesObjc; j++) {
+ valPtrs [j] = Tcl_GetStringFromObj (valuesObjv[j], NULL);
+
+ /* If it's "delete" and value is an empty string, make
+ * value be NULL to indicate entire attribute is to be
+ * deleted */
+ if ((*valPtrs [j] == '\0')
+ && (mod->mod_op == LDAP_MOD_DELETE || mod->mod_op == LDAP_MOD_REPLACE)) {
+ valPtrs [j] = NULL;
+ }
+ }
+ }
+
+ pairIndex += 2;
+ if (mod_op != -1 && pairIndex < objc) {
+ subCommand = Tcl_GetStringFromObj (objv[pairIndex - 1], NULL);
+ mod_op = -1;
+ if (STREQU (subCommand, "add")) {
+ mod_op = LDAP_MOD_ADD;
+ } else if (STREQU (subCommand, "replace")) {
+ mod_op = LDAP_MOD_REPLACE;
+ } else if (STREQU (subCommand, "delete")) {
+ mod_op = LDAP_MOD_DELETE;
+ }
+ if (mod_op == -1) {
+ Tcl_SetStringObj (resultObj,
+ "Additional operators must be one of"
+ " add, replace, or delete", -1);
+ mod_op = -1;
+ goto badop;
+ }
+ }
+
+ } while (mod_op != -1 && pairIndex < objc);
+ modArray[modIndex] = (LDAPMod *) NULL;
+
+ if (is_add) {
+ result = ldap_add_s (ldap, dn, modArray);
+ } else {
+ result = ldap_modify_s (ldap, dn, modArray);
+ if (ldaptcl->caching)
+ ldap_uncache_entry (ldap, dn);
+ }
+
+ /* free the modArray elements, then the modArray itself. */
+badop:
+ for (i = 0; i < modIndex; i++) {
+ free ((char *) modArray[i]->mod_vals.modv_strvals);
+ free ((char *) modArray[i]);
+ }
+ free ((char *) modArray);
+
+ /* after modArray is allocated, mod_op = -1 upon error for cleanup */
+ if (mod_op == -1)
+ return TCL_ERROR;
+
+ /* FIX: memory cleanup required all over the place here */
+ if (result != LDAP_SUCCESS) {
+ Tcl_AppendStringsToObj (resultObj,
+ "LDAP ",
+ subCommand,
+ " error: ",
+ ldap_err2string(result),
+ (char *)NULL);
+ LDAP_SetErrorCode(ldaptcl, result, interp);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+ }
+
+ /* object search controlArray dn pattern */
+ if (STREQU (subCommand, "search")) {
+ char *controlArrayName;
+ Tcl_Obj *controlArrayNameObj;
+
+ char *scopeString;
+ int scope;
+
+ char *derefString;
+ int deref;
+
+ char *baseString;
+
+ char **attributesArray;
+ char *attributesString;
+ int attributesArgc;
+
+ char *filterPatternString;
+
+ char *timeoutString;
+ double timeoutTime;
+ struct timeval timeout, *timeout_p;
+
+ char *paramString;
+ int cacheThis = -1;
+ int all = 0;
+
+ char *sortattr;
+
+ Tcl_Obj *destArrayNameObj;
+ Tcl_Obj *evalCodeObj;
+
+ if (objc != 5) {
+ Tcl_WrongNumArgs (interp, 2, objv,
+ "controlArray destArray code");
+ return TCL_ERROR;
+ }
+
+ controlArrayNameObj = objv [2];
+ controlArrayName = Tcl_GetStringFromObj (controlArrayNameObj, NULL);
+
+ destArrayNameObj = objv [3];
+
+ evalCodeObj = objv [4];
+
+ baseString = Tcl_GetVar2 (interp,
+ controlArrayName,
+ "base",
+ 0);
+
+ if (baseString == (char *)NULL) {
+ Tcl_AppendStringsToObj (resultObj,
+ "required element \"base\" ",
+ "is missing from ldap control array \"",
+ controlArrayName,
+ "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ filterPatternString = Tcl_GetVar2 (interp,
+ controlArrayName,
+ "filter",
+ 0);
+ if (filterPatternString == (char *)NULL) {
+ filterPatternString = "(objectclass=*)";
+ }
+
+ /* Fetch scope setting from control array.
+ * If it doesn't exist, default to subtree scoping.
+ */
+ scopeString = Tcl_GetVar2 (interp, controlArrayName, "scope", 0);
+ if (scopeString == NULL) {
+ scope = LDAP_SCOPE_SUBTREE;
+ } else {
+ if (STREQU(scopeString, "base"))
+ scope = LDAP_SCOPE_BASE;
+ else if (STRNEQU(scopeString, "one", 3))
+ scope = LDAP_SCOPE_ONELEVEL;
+ else if (STRNEQU(scopeString, "sub", 3))
+ scope = LDAP_SCOPE_SUBTREE;
+ else {
+ Tcl_AppendStringsToObj (resultObj,
+ "\"scope\" element of \"",
+ controlArrayName,
+ "\" array is not one of ",
+ "\"base\", \"onelevel\", ",
+ "or \"subtree\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+
+#ifdef LDAP_OPT_DEREF
+ /* Fetch dereference control setting from control array.
+ * If it doesn't exist, default to never dereference. */
+ derefString = Tcl_GetVar2 (interp,
+ controlArrayName,
+ "deref",
+ 0);
+ if (derefString == (char *)NULL) {
+ deref = LDAP_DEREF_NEVER;
+ } else {
+ if (STREQU(derefString, "never"))
+ deref = LDAP_DEREF_NEVER;
+ else if (STREQU(derefString, "search"))
+ deref = LDAP_DEREF_SEARCHING;
+ else if (STREQU(derefString, "find"))
+ deref = LDAP_DEREF_FINDING;
+ else if (STREQU(derefString, "always"))
+ deref = LDAP_DEREF_ALWAYS;
+ else {
+ Tcl_AppendStringsToObj (resultObj,
+ "\"deref\" element of \"",
+ controlArrayName,
+ "\" array is not one of ",
+ "\"never\", \"search\", \"find\", ",
+ "or \"always\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+#endif
+
+ /* Fetch list of attribute names from control array.
+ * If entry doesn't exist, default to NULL (all).
+ */
+ attributesString = Tcl_GetVar2 (interp,
+ controlArrayName,
+ "attributes",
+ 0);
+ if (attributesString == (char *)NULL) {
+ attributesArray = NULL;
+ } else {
+ if ((Tcl_SplitList (interp,
+ attributesString,
+ &attributesArgc,
+ &attributesArray)) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+
+ /* Fetch timeout value if there is one
+ */
+ timeoutString = Tcl_GetVar2 (interp,
+ controlArrayName,
+ "timeout",
+ 0);
+ timeout.tv_usec = 0;
+ if (timeoutString == (char *)NULL) {
+ timeout_p = NULL;
+ timeout.tv_sec = 0;
+ } else {
+ if (Tcl_GetDouble(interp, timeoutString, &timeoutTime) != TCL_OK)
+ return TCL_ERROR;
+ timeout.tv_sec = floor(timeoutTime);
+ timeout.tv_usec = (timeoutTime-timeout.tv_sec) * 1000000;
+ timeout_p = &timeout;
+ }
+
+ paramString = Tcl_GetVar2 (interp, controlArrayName, "cache", 0);
+ if (paramString) {
+ if (Tcl_GetInt(interp, paramString, &cacheThis) == TCL_ERROR)
+ return TCL_ERROR;
+ }
+
+ paramString = Tcl_GetVar2 (interp, controlArrayName, "all", 0);
+ if (paramString) {
+ if (Tcl_GetInt(interp, paramString, &all) == TCL_ERROR)
+ return TCL_ERROR;
+ }
+
+ sortattr = Tcl_GetVar2 (interp, controlArrayName, "sort", 0);
+
+#ifdef UMICH_LDAP
+ ldap->ld_deref = deref;
+ ldap->ld_timelimit = 0;
+ ldap->ld_sizelimit = 0;
+ ldap->ld_options = 0;
+#endif
+
+ /* Caching control within the search: if the "cache" control array */
+ /* value is set, disable/enable caching accordingly */
+
+#if 0
+ if (cacheThis >= 0 && ldaptcl->caching != cacheThis) {
+ if (cacheThis) {
+ if (ldaptcl->timeout == 0) {
+ Tcl_SetStringObj(resultObj, "Caching never before enabled, I have no timeout value to use", -1);
+ return TCL_ERROR;
+ }
+ ldap_enable_cache(ldap, ldaptcl->timeout, ldaptcl->maxmem);
+ }
+ else
+ ldap_disable_cache(ldap);
+ }
+#endif
+
+#ifdef LDAP_OPT_DEREF
+ ldap_set_option(ldap, LDAP_OPT_DEREF, &deref);
+#endif
+
+ tclResult = LDAP_PerformSearch (interp,
+ ldaptcl,
+ baseString,
+ scope,
+ attributesArray,
+ filterPatternString,
+ "",
+ destArrayNameObj,
+ evalCodeObj,
+ timeout_p,
+ all,
+ sortattr);
+ /* Following the search, if we changed the caching behavior, change */
+ /* it back. */
+#if 0
+ if (cacheThis >= 0 && ldaptcl->caching != cacheThis) {
+ if (cacheThis)
+ ldap_disable_cache(ldap);
+ else
+ ldap_enable_cache(ldap, ldaptcl->timeout, ldaptcl->maxmem);
+ }
+#ifdef LDAP_OPT_DEREF
+ deref = LDAP_DEREF_NEVER;
+ ldap_set_option(ldap, LDAP_OPT_DEREF, &deref);
+#endif
+#endif
+ return tclResult;
+ }
+
+ /* object compare dn attr value */
+ if (STREQU (subCommand, "compare")) {
+ char *dn;
+ char *attr;
+ char *value;
+ int result;
+ int lderrno;
+
+ if (objc != 5) {
+ Tcl_WrongNumArgs (interp,
+ 2, objv,
+ "dn attribute value");
+ return TCL_ERROR;
+ }
+
+ dn = Tcl_GetStringFromObj (objv[2], NULL);
+ attr = Tcl_GetStringFromObj (objv[3], NULL);
+ value = Tcl_GetStringFromObj (objv[4], NULL);
+
+ result = ldap_compare_s (ldap, dn, attr, value);
+ if (result == LDAP_COMPARE_TRUE || result == LDAP_COMPARE_FALSE) {
+ Tcl_SetBooleanObj(resultObj, result == LDAP_COMPARE_TRUE);
+ return TCL_OK;
+ }
+ LDAP_SetErrorCode(ldaptcl, result, interp);
+ Tcl_AppendStringsToObj (resultObj,
+ "LDAP compare error: ",
+ LDAP_ERR_STRING(ldap),
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+
+ if (STREQU (subCommand, "cache")) {
+#if defined(UMICH_LDAP) || (defined(OPEN_LDAP) && !defined(LDAP_API_VERSION))
+ char *cacheCommand;
+
+ if (objc < 3) {
+ badargs:
+ Tcl_WrongNumArgs (interp, 2, objv [0], "command [args...]");
+ return TCL_ERROR;
+ }
+
+ cacheCommand = Tcl_GetStringFromObj (objv [2], NULL);
+
+ if (STREQU (cacheCommand, "uncache")) {
+ char *dn;
+
+ if (objc != 4) {
+ Tcl_WrongNumArgs (interp,
+ 3, objv,
+ "dn");
+ return TCL_ERROR;
+ }
+
+ dn = Tcl_GetStringFromObj (objv [3], NULL);
+ ldap_uncache_entry (ldap, dn);
+ return TCL_OK;
+ }
+
+ if (STREQU (cacheCommand, "enable")) {
+ long timeout = ldaptcl->timeout;
+ long maxmem = ldaptcl->maxmem;
+
+ if (objc > 5) {
+ Tcl_WrongNumArgs (interp, 3, objv, "?timeout? ?maxmem?");
+ return TCL_ERROR;
+ }
+
+ if (objc > 3) {
+ if (Tcl_GetLongFromObj (interp, objv [3], &timeout) == TCL_ERROR)
+ return TCL_ERROR;
+ }
+ if (timeout == 0) {
+ Tcl_SetStringObj(resultObj,
+ objc > 3 ? "timeouts must be greater than 0" :
+ "no previous timeout to reference", -1);
+ return TCL_ERROR;
+ }
+
+ if (objc > 4)
+ if (Tcl_GetLongFromObj (interp, objv [4], &maxmem) == TCL_ERROR)
+ return TCL_ERROR;
+
+ if (ldap_enable_cache (ldap, timeout, maxmem) == -1) {
+ Tcl_AppendStringsToObj (resultObj,
+ "LDAP cache enable error: ",
+ LDAP_ERR_STRING(ldap),
+ (char *)NULL);
+ LDAP_SetErrorCode(ldaptcl, -1, interp);
+ return TCL_ERROR;
+ }
+ ldaptcl->caching = 1;
+ ldaptcl->timeout = timeout;
+ ldaptcl->maxmem = maxmem;
+ return TCL_OK;
+ }
+
+ if (objc != 3) goto badargs;
+
+ if (STREQU (cacheCommand, "disable")) {
+ ldap_disable_cache (ldap);
+ ldaptcl->caching = 0;
+ return TCL_OK;
+ }
+
+ if (STREQU (cacheCommand, "destroy")) {
+ ldap_destroy_cache (ldap);
+ ldaptcl->caching = 0;
+ return TCL_OK;
+ }
+
+ if (STREQU (cacheCommand, "flush")) {
+ ldap_flush_cache (ldap);
+ return TCL_OK;
+ }
+
+ if (STREQU (cacheCommand, "no_errors")) {
+ ldap_set_cache_options (ldap, LDAP_CACHE_OPT_CACHENOERRS);
+ return TCL_OK;
+ }
+
+ if (STREQU (cacheCommand, "all_errors")) {
+ ldap_set_cache_options (ldap, LDAP_CACHE_OPT_CACHEALLERRS);
+ return TCL_OK;
+ }
+
+ if (STREQU (cacheCommand, "size_errors")) {
+ ldap_set_cache_options (ldap, 0);
+ return TCL_OK;
+ }
+ Tcl_AppendStringsToObj (resultObj,
+ "\"",
+ command,
+ " ",
+ subCommand,
+ "\" subcommand",
+ " must be one of \"enable\", ",
+ "\"disable\", ",
+ "\"destroy\", \"flush\", \"uncache\", ",
+ "\"no_errors\", \"size_errors\",",
+ " or \"all_errors\"",
+ (char *)NULL);
+ return TCL_ERROR;
+#else
+ return TCL_OK;
+#endif
+ }
+ if (STREQU (subCommand, "trap")) {
+ Tcl_Obj *listObj, *resultObj;
+ int *p, l, i, code;
+
+ if (objc > 4) {
+ Tcl_WrongNumArgs (interp, 2, objv,
+ "command ?errorCode-list?");
+ return TCL_ERROR;
+ }
+ if (objc == 2) {
+ if (!ldaptcl->trapCmdObj)
+ return TCL_OK;
+ resultObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, resultObj, ldaptcl->trapCmdObj);
+ if (ldaptcl->traplist) {
+ listObj = Tcl_NewObj();
+ for (p = ldaptcl->traplist; *p; p++) {
+ Tcl_ListObjAppendElement(interp, listObj,
+ Tcl_NewStringObj(ldaptclerrorcode[*p], -1));
+ }
+ Tcl_ListObjAppendElement(interp, resultObj, listObj);
+ }
+ Tcl_SetObjResult(interp, resultObj);
+ return TCL_OK;
+ }
+ if (ldaptcl->trapCmdObj) {
+ Tcl_DecrRefCount (ldaptcl->trapCmdObj);
+ ldaptcl->trapCmdObj = NULL;
+ }
+ if (ldaptcl->traplist) {
+ free(ldaptcl->traplist);
+ ldaptcl->traplist = NULL;
+ }
+ Tcl_GetStringFromObj(objv[2], &l);
+ if (l == 0)
+ return TCL_OK; /* just turn off trap */
+ ldaptcl->trapCmdObj = objv[2];
+ Tcl_IncrRefCount (ldaptcl->trapCmdObj);
+ if (objc < 4)
+ return TCL_OK; /* no code list */
+ if (Tcl_ListObjLength(interp, objv[3], &l) != TCL_OK)
+ return TCL_ERROR;
+ if (l == 0)
+ return TCL_OK; /* empty code list */
+ ldaptcl->traplist = (int*)malloc(sizeof(int) * (l + 1));
+ ldaptcl->traplist[l] = 0;
+ for (i = 0; i < l; i++) {
+ Tcl_ListObjIndex(interp, objv[3], i, &resultObj);
+ code = LDAP_ErrorStringToCode(interp, Tcl_GetStringFromObj(resultObj, NULL));
+ if (code == -1) {
+ free(ldaptcl->traplist);
+ ldaptcl->traplist = NULL;
+ return TCL_ERROR;
+ }
+ ldaptcl->traplist[i] = code;
+ }
+ return TCL_OK;
+ }
+ if (STREQU (subCommand, "trapcodes")) {
+ int code;
+ Tcl_Obj *resultObj;
+ Tcl_Obj *stringObj;
+ resultObj = Tcl_GetObjResult(interp);
+
+ for (code = 0; code < LDAPTCL_MAXERR; code++) {
+ if (!ldaptclerrorcode[code]) continue;
+ Tcl_ListObjAppendElement(interp, resultObj,
+ Tcl_NewStringObj(ldaptclerrorcode[code], -1));
+ }
+ return TCL_OK;
+ }
+#ifdef LDAP_DEBUG
+ if (STREQU (subCommand, "debug")) {
+ if (objc != 3) {
+ Tcl_AppendStringsToObj(resultObj, "Wrong # of arguments",
+ (char*)NULL);
+ return TCL_ERROR;
+ }
+ return Tcl_GetIntFromObj(interp, objv[2], &ldap_debug);
+ }
+#endif
+
+ /* FIX: this needs to enumerate all the possibilities */
+ Tcl_AppendStringsToObj (resultObj,
+ "subcommand \"",
+ subCommand,
+ "\" must be one of \"add\", ",
+ "\"add_attributes\", ",
+ "\"bind\", \"cache\", \"delete\", ",
+ "\"delete_attributes\", \"modify\", ",
+ "\"modify_rdn\", \"rename_rdn\", ",
+ "\"replace_attributes\", ",
+ "\"search\" or \"unbind\".",
+ (char *)NULL);
+ return TCL_ERROR;
+}
+
+/*
+ * Delete and LDAP command object
+ *
+ */
+static void
+NeoX_LdapObjDeleteCmd(clientData)
+ ClientData clientData;
+{
+ LDAPTCL *ldaptcl = (LDAPTCL *)clientData;
+ LDAP *ldap = ldaptcl->ldap;
+
+ if (ldaptcl->trapCmdObj)
+ Tcl_DecrRefCount (ldaptcl->trapCmdObj);
+ if (ldaptcl->traplist)
+ free(ldaptcl->traplist);
+ ldap_unbind(ldap);
+ free((char*) ldaptcl);
+}
+
+/*-----------------------------------------------------------------------------
+ * NeoX_LdapObjCmd --
+ *
+ * Implements the `ldap' command:
+ * ldap open newObjName host [port]
+ * ldap init newObjName host [port]
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * See the user documentation.
+ *-----------------------------------------------------------------------------
+ */
+static int
+NeoX_LdapObjCmd (clientData, interp, objc, objv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST objv[];
+{
+ extern int errno;
+ char *subCommand;
+ char *newCommand;
+ char *ldapHost;
+ int ldapPort = LDAP_PORT;
+ LDAP *ldap;
+ LDAPTCL *ldaptcl;
+
+ Tcl_Obj *resultObj = Tcl_GetObjResult (interp);
+
+ if (objc < 3) {
+ Tcl_WrongNumArgs (interp, 1, objv,
+ "(open|init) new_command host [port]|explode dn");
+ return TCL_ERROR;
+ }
+
+ subCommand = Tcl_GetStringFromObj (objv[1], NULL);
+
+ if (STREQU(subCommand, "explode")) {
+ char *param;
+ int nonames = 0;
+ int list = 0;
+ char **exploded, **p;
+
+ param = Tcl_GetStringFromObj (objv[2], NULL);
+ if (param[0] == '-') {
+ if (STREQU(param, "-nonames")) {
+ nonames = 1;
+ } else if (STREQU(param, "-list")) {
+ list = 1;
+ } else {
+ Tcl_WrongNumArgs (interp, 1, objv, "explode ?-nonames|-list? dn");
+ return TCL_ERROR;
+ }
+ }
+ if (nonames || list)
+ param = Tcl_GetStringFromObj (objv[3], NULL);
+ exploded = ldap_explode_dn(param, nonames);
+ for (p = exploded; *p; p++) {
+ if (list) {
+ char *q = strchr(*p, '=');
+ if (!q) {
+ Tcl_SetObjLength(resultObj, 0);
+ Tcl_AppendStringsToObj(resultObj, "rdn ", *p,
+ " missing '='", NULL);
+ ldap_value_free(exploded);
+ return TCL_ERROR;
+ }
+ *q = '\0';
+ if (Tcl_ListObjAppendElement(interp, resultObj,
+ Tcl_NewStringObj(*p, -1)) != TCL_OK ||
+ Tcl_ListObjAppendElement(interp, resultObj,
+ Tcl_NewStringObj(q+1, -1)) != TCL_OK) {
+ ldap_value_free(exploded);
+ return TCL_ERROR;
+ }
+ } else {
+ if (Tcl_ListObjAppendElement(interp, resultObj,
+ Tcl_NewStringObj(*p, -1))) {
+ ldap_value_free(exploded);
+ return TCL_ERROR;
+ }
+ }
+ }
+ ldap_value_free(exploded);
+ return TCL_OK;
+ }
+
+#ifdef UMICH_LDAP
+ if (STREQU(subCommand, "friendly")) {
+ char *friendly = ldap_dn2ufn(Tcl_GetStringFromObj(objv[2], NULL));
+ Tcl_SetStringObj(resultObj, friendly, -1);
+ free(friendly);
+ return TCL_OK;
+ }
+#endif
+
+ newCommand = Tcl_GetStringFromObj (objv[2], NULL);
+ ldapHost = Tcl_GetStringFromObj (objv[3], NULL);
+
+ if (objc == 5) {
+ if (Tcl_GetIntFromObj (interp, objv [4], &ldapPort) == TCL_ERROR) {
+ Tcl_AppendStringsToObj (resultObj,
+ "LDAP port number is non-numeric",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+
+ if (STREQU (subCommand, "open")) {
+ ldap = ldap_open (ldapHost, ldapPort);
+ } else if (STREQU (subCommand, "init")) {
+ int version = -1;
+ int i;
+ int value;
+ char *subOption;
+ char *subValue;
+
+#if LDAPTCL_PROTOCOL_VERSION_DEFAULT
+ version = LDAPTCL_PROTOCOL_VERSION_DEFAULT;
+#endif
+
+ for (i = 6; i < objc; i += 2) {
+ subOption = Tcl_GetStringFromObj(objv[i-1], NULL);
+ if (STREQU (subOption, "protocol_version")) {
+#ifdef LDAP_OPT_PROTOCOL_VERSION
+ subValue = Tcl_GetStringFromObj(objv[i], NULL);
+ if (STREQU (subValue, "2")) {
+ version = LDAP_VERSION2;
+ }
+ else if (STREQU (subValue, "3")) {
+#ifdef LDAP_VERSION3
+ version = LDAP_VERSION3;
+#else
+ Tcl_SetStringObj (resultObj, "protocol_version 3 not supported", -1);
+ return TCL_ERROR;
+#endif
+ }
+ else {
+ Tcl_SetStringObj (resultObj, "protocol_version must be '2' or '3'", -1);
+ return TCL_ERROR;
+ }
+#else
+ Tcl_SetStringObj (resultObj, "protocol_version not supported", -1);
+ return TCL_ERROR;
+#endif
+ } else if (STREQU (subOption, "port")) {
+ if (Tcl_GetIntFromObj (interp, objv [i], &ldapPort) == TCL_ERROR) {
+ Tcl_AppendStringsToObj (resultObj,
+ "LDAP port number is non-numeric",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ } else {
+ Tcl_SetStringObj (resultObj, "valid options: protocol_version, port", -1);
+ return TCL_ERROR;
+ }
+ }
+ ldap = ldap_init (ldapHost, ldapPort);
+
+#ifdef LDAP_OPT_PROTOCOL_VERSION
+ if (version != -1)
+ ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
+#endif
+ } else {
+ Tcl_AppendStringsToObj (resultObj,
+ "option was not \"open\" or \"init\"");
+ return TCL_ERROR;
+ }
+
+ if (ldap == (LDAP *)NULL) {
+ Tcl_SetErrno(errno);
+ Tcl_AppendStringsToObj (resultObj,
+ Tcl_PosixError (interp),
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+
+#ifdef UMICH_LDAP
+ ldap->ld_deref = LDAP_DEREF_NEVER; /* Turn off alias dereferencing */
+#endif
+
+ ldaptcl = (LDAPTCL *) malloc(sizeof(LDAPTCL));
+ ldaptcl->ldap = ldap;
+ ldaptcl->caching = 0;
+ ldaptcl->timeout = 0;
+ ldaptcl->maxmem = 0;
+ ldaptcl->trapCmdObj = NULL;
+ ldaptcl->traplist = NULL;
+ ldaptcl->flags = 0;
+
+ Tcl_CreateObjCommand (interp,
+ newCommand,
+ NeoX_LdapTargetObjCmd,
+ (ClientData) ldaptcl,
+ NeoX_LdapObjDeleteCmd);
+ return TCL_OK;
+}
+
+/*-----------------------------------------------------------------------------
+ * Neo_initLDAP --
+ * Initialize the LDAP interface.
+ *-----------------------------------------------------------------------------
+ */
+int
+Ldaptcl_Init (interp)
+Tcl_Interp *interp;
+{
+ Tcl_CreateObjCommand (interp,
+ "ldap",
+ NeoX_LdapObjCmd,
+ (ClientData) NULL,
+ (Tcl_CmdDeleteProc*) NULL);
+ /*
+ if (Neo_initLDAPX(interp) != TCL_OK)
+ return TCL_ERROR;
+ */
+ Tcl_PkgProvide(interp, "Ldaptcl", VERSION);
+ return TCL_OK;
+}
diff --git a/contrib/ldaptcl/pkgIndex.tcl.in b/contrib/ldaptcl/pkgIndex.tcl.in
new file mode 100644
index 0000000..f257556
--- /dev/null
+++ b/contrib/ldaptcl/pkgIndex.tcl.in
@@ -0,0 +1 @@
+package ifneeded Ldaptcl @NEO_VERSION@ "package require Tclx 8.0; load [file join $dir .. @NEO_SHARED_LIB_FILE@] Ldaptcl"
diff --git a/contrib/ldaptcl/tclAppInit.c b/contrib/ldaptcl/tclAppInit.c
new file mode 100644
index 0000000..f84e182
--- /dev/null
+++ b/contrib/ldaptcl/tclAppInit.c
@@ -0,0 +1,140 @@
+/*
+ * tclAppInit.c --
+ *
+ * Provides a default version of the main program and Tcl_AppInit
+ * procedure for Tcl applications (without Tk).
+ *
+ * Copyright (c) 1993 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tclAppInit.c 1.17 96/03/26 12:45:29
+ */
+
+#include "tcl.h"
+
+/*
+ * The following variable is a special hack that is needed in order for
+ * Sun shared libraries to be used for Tcl.
+ */
+
+extern int matherr();
+int *tclDummyMathPtr = (int *) matherr;
+
+#ifdef TCL_TEST
+EXTERN int Tcltest_Init _ANSI_ARGS_((Tcl_Interp *interp));
+#endif /* TCL_TEST */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * main --
+ *
+ * This is the main program for the application.
+ *
+ * Results:
+ * None: Tcl_Main never returns here, so this procedure never
+ * returns either.
+ *
+ * Side effects:
+ * Whatever the application does.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+main(argc, argv)
+ int argc; /* Number of command-line arguments. */
+ char **argv; /* Values of command-line arguments. */
+{
+#ifdef USE_TCLX
+ TclX_Main(argc, argv, Tcl_AppInit);
+#else
+ Tcl_Main(argc, argv, Tcl_AppInit);
+#endif
+ return 0; /* Needed only to prevent compiler warning. */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_AppInit --
+ *
+ * This procedure performs application-specific initialization.
+ * Most applications, especially those that incorporate additional
+ * packages, will have their own version of this procedure.
+ *
+ * Results:
+ * Returns a standard Tcl completion code, and leaves an error
+ * message in interp->result if an error occurs.
+ *
+ * Side effects:
+ * Depends on the startup script.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_AppInit(interp)
+ Tcl_Interp *interp; /* Interpreter for application. */
+{
+ if (Tcl_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+
+#ifdef USE_ITCL
+ if (Itcl_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ Tcl_StaticPackage (interp, "Itcl", Itcl_Init, NULL);
+#endif
+
+#ifdef TCL_TEST
+ if (Tcltest_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ Tcl_StaticPackage(interp, "Tcltest", Tcltest_Init,
+ (Tcl_PackageInitProc *) NULL);
+#endif /* TCL_TEST */
+
+#ifdef USE_TCLX
+ if (Tclx_Init (interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ Tcl_StaticPackage (interp, "Tclx", Tclx_Init, NULL);
+#endif
+
+ if (Ldaptcl_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ Tcl_StaticPackage(interp, "Ldaptcl", Ldaptcl_Init,
+ (Tcl_PackageInitProc *) NULL);
+
+ /*
+ * Call the init procedures for included packages. Each call should
+ * look like this:
+ *
+ * if (Mod_Init(interp) == TCL_ERROR) {
+ * return TCL_ERROR;
+ * }
+ *
+ * where "Mod" is the name of the module.
+ */
+
+ /*
+ * Call Tcl_CreateCommand for application-specific commands, if
+ * they weren't already created by the init procedures called above.
+ */
+
+ /*
+ * Specify a user-specific startup file to invoke if the application
+ * is run interactively. Typically the startup file is "~/.apprc"
+ * where "app" is the name of the application. If this line is deleted
+ * then no user-specific startup file will be run under any conditions.
+ */
+
+ Tcl_SetVar(interp, "tcl_rcFileName", "~/.tclshrc", TCL_GLOBAL_ONLY);
+ return TCL_OK;
+}
diff --git a/contrib/ldaptcl/tkAppInit.c b/contrib/ldaptcl/tkAppInit.c
new file mode 100644
index 0000000..68fd956
--- /dev/null
+++ b/contrib/ldaptcl/tkAppInit.c
@@ -0,0 +1,119 @@
+/*
+ * tkXAppInit.c --
+ *
+ * Provides a default version of the Tcl_AppInit procedure for use with
+ * applications built with Extended Tcl and Tk on Unix systems. This is based
+ * on the the UCB Tk file tkAppInit.c
+ *-----------------------------------------------------------------------------
+ * Copyright 1991-1996 Karl Lehenbauer and Mark Diekhans.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies. Karl Lehenbauer and
+ * Mark Diekhans make no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *-----------------------------------------------------------------------------
+ * $OpenLDAP$
+ *-----------------------------------------------------------------------------
+ */
+
+#include "tclExtend.h"
+#include "tk.h"
+
+/*
+ * The following variable is a special hack that insures the tcl
+ * version of matherr() is used when linking against shared libraries
+ * Even if matherr is not used on this system, there is a dummy version
+ * in libtcl.
+ */
+EXTERN int matherr ();
+int (*tclDummyMathPtr)() = matherr;
+
+
+/*-----------------------------------------------------------------------------
+ * main --
+ *
+ * This is the main program for the application.
+ *-----------------------------------------------------------------------------
+ */
+#ifdef __cplusplus
+int
+main (int argc,
+ char **argv)
+#else
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+#endif
+{
+#ifdef USE_TCLX
+ TkX_Main(argc, argv, Tcl_AppInit);
+#else
+ Tk_Main(argc, argv, Tcl_AppInit);
+#endif
+ return 0; /* Needed only to prevent compiler warning. */
+}
+
+/*-----------------------------------------------------------------------------
+ * Tcl_AppInit --
+ *
+ * This procedure performs application-specific initialization. Most
+ * applications, especially those that incorporate additional packages, will
+ * have their own version of this procedure.
+ *
+ * Results:
+ * Returns a standard Tcl completion code, and leaves an error message in
+ * interp->result if an error occurs.
+ *-----------------------------------------------------------------------------
+ */
+#ifdef __cplusplus
+int
+Tcl_AppInit (Tcl_Interp *interp)
+#else
+int
+Tcl_AppInit (interp)
+ Tcl_Interp *interp;
+#endif
+{
+ if (Tcl_Init (interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+#ifdef USE_TCLX
+ if (Tclx_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ Tcl_StaticPackage(interp, "Tclx", Tclx_Init, Tclx_SafeInit);
+#endif
+ if (Tk_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ Tcl_StaticPackage(interp, "Tk", Tk_Init, (Tcl_PackageInitProc *) NULL);
+#ifdef USE_TCLX
+ if (Tkx_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ Tcl_StaticPackage(interp, "Tkx", Tkx_Init, (Tcl_PackageInitProc *) NULL);
+#endif
+
+ if (Ldaptcl_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ Tcl_StaticPackage(interp, "Ldaptcl", Ldaptcl_Init,
+ (Tcl_PackageInitProc *) NULL);
+
+ /*
+ * Call Tcl_CreateCommand for application-specific commands, if
+ * they weren't already created by the init procedures called above.
+ */
+
+ /*
+ * Specify a user-specific startup file to invoke if the application
+ * is run interactively. Typically the startup file is "~/.apprc"
+ * where "app" is the name of the application. If this line is deleted
+ * then no user-specific startup file will be run under any conditions.
+ */
+ Tcl_SetVar(interp, "tcl_rcFileName", "~/.wishxrc", TCL_GLOBAL_ONLY);
+ return TCL_OK;
+}
diff --git a/contrib/slapd-modules/README b/contrib/slapd-modules/README
new file mode 100644
index 0000000..136f720
--- /dev/null
+++ b/contrib/slapd-modules/README
@@ -0,0 +1,64 @@
+Copyright 2008-2022 The OpenLDAP Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+This directory contains native-API slapd modules (overlays etc):
+
+acl (plugins)
+ Plugins implementing access rules. Currently one plugin
+ which implements access control based on posixGroup membership.
+
+addpartial (overlay)
+ Treat Add requests as Modify requests if the entry exists.
+
+allop (overlay)
+ Return operational attributes for root DSE even when not
+ requested, since some clients expect this.
+
+autogroup (overlay)
+ Automated updates of group memberships.
+
+cloak (overlay)
+ Hide specific attributes unless explicitly requested
+
+comp_match (plugin)
+ Component Matching rules (RFC 3687).
+
+denyop (overlay)
+ Deny selected operations, returning unwillingToPerform.
+
+dsaschema (plugin)
+ Permit loading DSA-specific schema, including operational attrs.
+
+lastbind (overlay)
+ Record the last successful authentication on an entry.
+
+lastmod (overlay)
+ Track the time of the last write operation to a database.
+
+nops (overlay)
+ Remove null operations, e.g. changing a value to same as before.
+
+nssov (listener overlay)
+ Handle NSS lookup requests through a local Unix Domain socket.
+
+passwd (plugins)
+ Support additional password mechanisms.
+ Currently Kerberos, Netscape MTA-MD5 and RADIUS.
+
+proxyOld (plugin)
+ Proxy Authorization compatibility with obsolete internet-draft.
+
+smbk5pwd (overlay)
+ Make the PasswordModify Extended Operation update Kerberos
+ keys and Samba password hashes as well as userPassword.
+
+trace (overlay)
+ Trace overlay invocation.
+
+usn (overlay)
+ Maintain usnCreated and usnChanged attrs similar to Microsoft AD.
+
+$OpenLDAP$
diff --git a/contrib/slapd-modules/acl/Makefile b/contrib/slapd-modules/acl/Makefile
new file mode 100644
index 0000000..090575d
--- /dev/null
+++ b/contrib/slapd-modules/acl/Makefile
@@ -0,0 +1,50 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = posixgroup.la gssacl.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+posixgroup.la: posixgroup.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+gssacl.la: gssacl.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/acl/README.gssacl b/contrib/slapd-modules/acl/README.gssacl
new file mode 100644
index 0000000..368b178
--- /dev/null
+++ b/contrib/slapd-modules/acl/README.gssacl
@@ -0,0 +1,32 @@
+This directory contains native slapd plugins that implement access rules.
+
+gssacl.c contains a simple example that implements access control
+based on GSS naming extensions attributes.
+
+To use the acl-gssacl plugin, add:
+
+moduleload acl-gssacl.so
+
+to your slapd configuration file.
+It is configured using
+
+access to <what>
+ by dynacl/gss/<attribute>.[.{base,regex,expand}]=<valpat> {<level>|<priv(s)>}
+
+The default is "exact"; in case of "expand", "<valpat>" results from
+the expansion of submatches in the "<what>" portion. "<level>|<priv(s)>"
+describe the level of privilege this rule can assume.
+
+Use Makefile to compile this plugin or use a command line similar to:
+
+gcc -shared -I../../../include -I../../../servers/slapd -Wall -g \
+ -o acl-gssacl.so gssacl.c
+
+
+---
+Copyright 2011 PADL Software Pty Ltd. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
diff --git a/contrib/slapd-modules/acl/README.posixgroup b/contrib/slapd-modules/acl/README.posixgroup
new file mode 100644
index 0000000..5e0460d
--- /dev/null
+++ b/contrib/slapd-modules/acl/README.posixgroup
@@ -0,0 +1,35 @@
+This directory contains native slapd plugins that implement access rules.
+
+posixgroup.c contains a simple example that implements access control
+based on posixGroup membership, loosely inspired by ITS#3849. It should
+be made clear that this access control policy does not reflect any
+standard track model of handling access control, and should be
+essentially viewed as an illustration of the use of the dynamic
+extension of access control within slapd.
+
+To use the acl-posixgroup plugin, add:
+
+moduleload acl-posixgroup.so
+
+to your slapd configuration file; it requires "nis.schema" to be loaded.
+It is configured using
+
+access to <what>
+ by dynacl/posixGroup[.{exact,expand}]=<dnpat> {<level>|<priv(s)}
+
+The default is "exact"; in case of "expand", "<dnpat>" results from
+the expansion of submatches in the "<what>" portion. "<level>|<priv(s)>"
+describe the level of privilege this rule can assume.
+
+Use Makefile to compile this plugin or use a command line similar to:
+
+gcc -shared -I../../../include -I../../../servers/slapd -Wall -g \
+ -o acl-posixgroup.so posixgroup.c
+
+---
+Copyright 2005-2022 The OpenLDAP Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
diff --git a/contrib/slapd-modules/acl/gssacl.c b/contrib/slapd-modules/acl/gssacl.c
new file mode 100644
index 0000000..12d3b9a
--- /dev/null
+++ b/contrib/slapd-modules/acl/gssacl.c
@@ -0,0 +1,316 @@
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2011 PADL Software Pty Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include <portable.h>
+
+#include <ac/string.h>
+#include <slap.h>
+#include <lutil.h>
+
+#include <sasl/sasl.h>
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_ext.h>
+
+#define ACL_BUF_SIZE 1024
+
+typedef struct gssattr_t {
+ slap_style_t gssattr_style;
+ struct berval gssattr_name; /* asserted name */
+ struct berval gssattr_value; /* asserted value */
+} gssattr_t;
+
+static int gssattr_dynacl_destroy( void *priv );
+
+static int
+regex_matches(
+ struct berval *pat, /* pattern to expand and match against */
+ char *str, /* string to match against pattern */
+ struct berval *dn_matches, /* buffer with $N expansion variables from DN */
+ struct berval *val_matches, /* buffer with $N expansion variables from val */
+ AclRegexMatches *matches /* offsets in buffer for $N expansion variables */
+);
+
+static int
+gssattr_dynacl_parse(
+ const char *fname,
+ int lineno,
+ const char *opts,
+ slap_style_t style,
+ const char *pattern,
+ void **privp )
+{
+ gssattr_t *gssattr;
+
+ gssattr = (gssattr_t *)ch_calloc( 1, sizeof( gssattr_t ) );
+
+ if ( opts == NULL || opts[0] == '\0' ) {
+ fprintf( stderr, "%s line %d: GSS ACL: no attribute specified.\n",
+ fname, lineno );
+ goto cleanup;
+ }
+
+ if ( pattern == NULL || pattern[0] == '\0' ) {
+ fprintf( stderr, "%s line %d: GSS ACL: no attribute value specified.\n",
+ fname, lineno );
+ goto cleanup;
+ }
+
+ gssattr->gssattr_style = style;
+
+ switch ( gssattr->gssattr_style ) {
+ case ACL_STYLE_BASE:
+ case ACL_STYLE_REGEX:
+ case ACL_STYLE_EXPAND:
+ break;
+ default:
+ fprintf( stderr, "%s line %d: GSS ACL: unsupported style \"%s\".\n",
+ fname, lineno, style_strings[style] );
+ goto cleanup;
+ break;
+ }
+
+ ber_str2bv( opts, 0, 1, &gssattr->gssattr_name );
+ ber_str2bv( pattern, 0, 1, &gssattr->gssattr_value );
+
+ *privp = (void *)gssattr;
+ return 0;
+
+cleanup:
+ (void)gssattr_dynacl_destroy( (void *)gssattr );
+
+ return 1;
+}
+
+static int
+gssattr_dynacl_unparse(
+ void *priv,
+ struct berval *bv )
+{
+ gssattr_t *gssattr = (gssattr_t *)priv;
+ char *ptr;
+
+ bv->bv_len = STRLENOF( " dynacl/gss/.expand=" ) +
+ gssattr->gssattr_name.bv_len +
+ gssattr->gssattr_value.bv_len;
+ bv->bv_val = ch_malloc( bv->bv_len + 1 );
+
+ ptr = lutil_strcopy( bv->bv_val, " dynacl/gss/" );
+ ptr = lutil_strncopy( ptr, gssattr->gssattr_name.bv_val,
+ gssattr->gssattr_name.bv_len );
+ switch ( gssattr->gssattr_style ) {
+ case ACL_STYLE_BASE:
+ ptr = lutil_strcopy( ptr, ".exact=" );
+ break;
+ case ACL_STYLE_REGEX:
+ ptr = lutil_strcopy( ptr, ".regex=" );
+ break;
+ case ACL_STYLE_EXPAND:
+ ptr = lutil_strcopy( ptr, ".expand=" );
+ break;
+ default:
+ assert( 0 );
+ break;
+ }
+
+ ptr = lutil_strncopy( ptr, gssattr->gssattr_value.bv_val,
+ gssattr->gssattr_value.bv_len );
+
+ ptr[ 0 ] = '\0';
+
+ bv->bv_len = ptr - bv->bv_val;
+
+ return 0;
+}
+
+static int
+gssattr_dynacl_mask(
+ void *priv,
+ Operation *op,
+ Entry *target,
+ AttributeDescription *desc,
+ struct berval *val,
+ int nmatch,
+ regmatch_t *matches,
+ slap_access_t *grant,
+ slap_access_t *deny )
+{
+ gssattr_t *gssattr = (gssattr_t *)priv;
+ sasl_conn_t *sasl_ctx = op->o_conn->c_sasl_authctx;
+ gss_name_t gss_name = GSS_C_NO_NAME;
+ OM_uint32 major, minor;
+ int more = -1;
+ int authenticated, complete;
+ gss_buffer_desc attr = GSS_C_EMPTY_BUFFER;
+ int granted = 0;
+
+ ACL_INVALIDATE( *deny );
+
+ if ( sasl_ctx == NULL ||
+ sasl_getprop( sasl_ctx, SASL_GSS_PEER_NAME, (const void **)&gss_name) != 0 ||
+ gss_name == GSS_C_NO_NAME ) {
+ return 0;
+ }
+
+ attr.length = gssattr->gssattr_name.bv_len;
+ attr.value = gssattr->gssattr_name.bv_val;
+
+ while ( more != 0 ) {
+ AclRegexMatches amatches = { 0 };
+ gss_buffer_desc gss_value = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc gss_display_value = GSS_C_EMPTY_BUFFER;
+ struct berval bv_value;
+
+ major = gss_get_name_attribute( &minor, gss_name, &attr,
+ &authenticated, &complete,
+ &gss_value, &gss_display_value, &more );
+ if ( GSS_ERROR( major ) ) {
+ break;
+ } else if ( authenticated == 0 ) {
+ gss_release_buffer( &minor, &gss_value );
+ gss_release_buffer( &minor, &gss_display_value );
+ continue;
+ }
+
+ bv_value.bv_len = gss_value.length;
+ bv_value.bv_val = (char *)gss_value.value;
+
+ if ( !ber_bvccmp( &gssattr->gssattr_value, '*' ) ) {
+ if ( gssattr->gssattr_style != ACL_STYLE_BASE ) {
+ amatches.dn_count = nmatch;
+ AC_MEMCPY( amatches.dn_data, matches, sizeof( amatches.dn_data ) );
+ }
+
+ switch ( gssattr->gssattr_style ) {
+ case ACL_STYLE_REGEX:
+ /* XXX assumes value NUL terminated */
+ granted = regex_matches( &gssattr->gssattr_value, bv_value.bv_val,
+ &target->e_nname, val, &amatches );
+ break;
+ case ACL_STYLE_EXPAND: {
+ struct berval bv;
+ char buf[ACL_BUF_SIZE];
+
+ bv.bv_len = sizeof( buf ) - 1;
+ bv.bv_val = buf;
+
+ granted = ( acl_string_expand( &bv, &gssattr->gssattr_value,
+ &target->e_nname, val,
+ &amatches ) == 0 ) &&
+ ( ber_bvstrcmp( &bv, &bv_value) == 0 );
+ break;
+ }
+ case ACL_STYLE_BASE:
+ granted = ( ber_bvstrcmp( &gssattr->gssattr_value, &bv_value ) == 0 );
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ } else {
+ granted = 1;
+ }
+
+ gss_release_buffer( &minor, &gss_value );
+ gss_release_buffer( &minor, &gss_display_value );
+
+ if ( granted ) {
+ break;
+ }
+ }
+
+ if ( granted ) {
+ ACL_LVL_ASSIGN_WRITE( *grant );
+ }
+
+ return 0;
+}
+
+static int
+gssattr_dynacl_destroy(
+ void *priv )
+{
+ gssattr_t *gssattr = (gssattr_t *)priv;
+
+ if ( gssattr != NULL ) {
+ if ( !BER_BVISNULL( &gssattr->gssattr_name ) ) {
+ ber_memfree( gssattr->gssattr_name.bv_val );
+ }
+ if ( !BER_BVISNULL( &gssattr->gssattr_value ) ) {
+ ber_memfree( gssattr->gssattr_value.bv_val );
+ }
+ ch_free( gssattr );
+ }
+
+ return 0;
+}
+
+static struct slap_dynacl_t gssattr_dynacl = {
+ "gss",
+ gssattr_dynacl_parse,
+ gssattr_dynacl_unparse,
+ gssattr_dynacl_mask,
+ gssattr_dynacl_destroy
+};
+
+int
+init_module( int argc, char *argv[] )
+{
+ return slap_dynacl_register( &gssattr_dynacl );
+}
+
+
+static int
+regex_matches(
+ struct berval *pat, /* pattern to expand and match against */
+ char *str, /* string to match against pattern */
+ struct berval *dn_matches, /* buffer with $N expansion variables from DN */
+ struct berval *val_matches, /* buffer with $N expansion variables from val */
+ AclRegexMatches *matches /* offsets in buffer for $N expansion variables */
+)
+{
+ regex_t re;
+ char newbuf[ACL_BUF_SIZE];
+ struct berval bv;
+ int rc;
+
+ bv.bv_len = sizeof( newbuf ) - 1;
+ bv.bv_val = newbuf;
+
+ if (str == NULL) {
+ str = "";
+ };
+
+ acl_string_expand( &bv, pat, dn_matches, val_matches, matches );
+ rc = regcomp( &re, newbuf, REG_EXTENDED|REG_ICASE );
+ if ( rc ) {
+ char error[ACL_BUF_SIZE];
+ regerror( rc, &re, error, sizeof( error ) );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "compile( \"%s\", \"%s\") failed %s\n",
+ pat->bv_val, str, error );
+ return( 0 );
+ }
+
+ rc = regexec( &re, str, 0, NULL, 0 );
+ regfree( &re );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> regex_matches: string: %s\n", str );
+ Debug( LDAP_DEBUG_TRACE,
+ "=> regex_matches: rc: %d %s\n",
+ rc, !rc ? "matches" : "no matches" );
+ return( !rc );
+}
+
diff --git a/contrib/slapd-modules/acl/posixgroup.c b/contrib/slapd-modules/acl/posixgroup.c
new file mode 100644
index 0000000..9a9a5a8
--- /dev/null
+++ b/contrib/slapd-modules/acl/posixgroup.c
@@ -0,0 +1,329 @@
+/* posixgroup.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include <portable.h>
+
+#include <ac/string.h>
+#include <slap.h>
+#include <lutil.h>
+
+/* Need dynacl... */
+
+#ifdef SLAP_DYNACL
+
+typedef struct pg_t {
+ slap_style_t pg_style;
+ struct berval pg_pat;
+} pg_t;
+
+static ObjectClass *pg_posixGroup;
+static AttributeDescription *pg_memberUid;
+static ObjectClass *pg_posixAccount;
+static AttributeDescription *pg_uidNumber;
+
+static int pg_dynacl_destroy( void *priv );
+
+static int
+pg_dynacl_parse(
+ const char *fname,
+ int lineno,
+ const char *opts,
+ slap_style_t style,
+ const char *pattern,
+ void **privp )
+{
+ pg_t *pg;
+ int rc;
+ const char *text = NULL;
+ struct berval pat;
+
+ ber_str2bv( pattern, 0, 0, &pat );
+
+ pg = ch_calloc( 1, sizeof( pg_t ) );
+
+ pg->pg_style = style;
+
+ switch ( pg->pg_style ) {
+ case ACL_STYLE_BASE:
+ rc = dnNormalize( 0, NULL, NULL, &pat, &pg->pg_pat, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s line %d: posixGroup ACL: "
+ "unable to normalize DN \"%s\".\n",
+ fname, lineno, pattern );
+ goto cleanup;
+ }
+ break;
+
+ case ACL_STYLE_EXPAND:
+ ber_dupbv( &pg->pg_pat, &pat );
+ break;
+
+ default:
+ fprintf( stderr, "%s line %d: posixGroup ACL: "
+ "unsupported style \"%s\".\n",
+ fname, lineno, style_strings[ pg->pg_style ] );
+ goto cleanup;
+ }
+
+ /* TODO: use opts to allow the use of different
+ * group objects and member attributes */
+ if ( pg_posixGroup == NULL ) {
+ pg_posixGroup = oc_find( "posixGroup" );
+ if ( pg_posixGroup == NULL ) {
+ fprintf( stderr, "%s line %d: posixGroup ACL: "
+ "unable to lookup \"posixGroup\" "
+ "objectClass.\n",
+ fname, lineno );
+ goto cleanup;
+ }
+
+ pg_posixAccount = oc_find( "posixAccount" );
+ if ( pg_posixGroup == NULL ) {
+ fprintf( stderr, "%s line %d: posixGroup ACL: "
+ "unable to lookup \"posixAccount\" "
+ "objectClass.\n",
+ fname, lineno );
+ goto cleanup;
+ }
+
+ rc = slap_str2ad( "memberUid", &pg_memberUid, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s line %d: posixGroup ACL: "
+ "unable to lookup \"memberUid\" "
+ "attributeDescription (%d: %s).\n",
+ fname, lineno, rc, text );
+ goto cleanup;
+ }
+
+ rc = slap_str2ad( "uidNumber", &pg_uidNumber, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s line %d: posixGroup ACL: "
+ "unable to lookup \"uidNumber\" "
+ "attributeDescription (%d: %s).\n",
+ fname, lineno, rc, text );
+ goto cleanup;
+ }
+ }
+
+ *privp = (void *)pg;
+ return 0;
+
+cleanup:
+ (void)pg_dynacl_destroy( (void *)pg );
+
+ return 1;
+}
+
+static int
+pg_dynacl_unparse(
+ void *priv,
+ struct berval *bv )
+{
+ pg_t *pg = (pg_t *)priv;
+ char *ptr;
+
+ bv->bv_len = STRLENOF( " dynacl/posixGroup.expand=" ) + pg->pg_pat.bv_len;
+ bv->bv_val = ch_malloc( bv->bv_len + 1 );
+
+ ptr = lutil_strcopy( bv->bv_val, " dynacl/posixGroup" );
+
+ switch ( pg->pg_style ) {
+ case ACL_STYLE_BASE:
+ ptr = lutil_strcopy( ptr, ".exact=" );
+ break;
+
+ case ACL_STYLE_EXPAND:
+ ptr = lutil_strcopy( ptr, ".expand=" );
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+ ptr = lutil_strncopy( ptr, pg->pg_pat.bv_val, pg->pg_pat.bv_len );
+ ptr[ 0 ] = '\0';
+
+ bv->bv_len = ptr - bv->bv_val;
+
+ return 0;
+}
+
+static int
+pg_dynacl_mask(
+ void *priv,
+ Operation *op,
+ Entry *target,
+ AttributeDescription *desc,
+ struct berval *val,
+ int nmatch,
+ regmatch_t *matches,
+ slap_access_t *grant,
+ slap_access_t *deny )
+{
+ pg_t *pg = (pg_t *)priv;
+ Entry *group = NULL,
+ *user = NULL;
+ int rc;
+ Backend *be = op->o_bd,
+ *group_be = NULL,
+ *user_be = NULL;
+ struct berval group_ndn;
+
+ ACL_INVALIDATE( *deny );
+
+ /* get user */
+ if ( target && dn_match( &target->e_nname, &op->o_ndn ) ) {
+ user = target;
+ rc = LDAP_SUCCESS;
+
+ } else {
+ user_be = op->o_bd = select_backend( &op->o_ndn, 0 );
+ if ( op->o_bd == NULL ) {
+ op->o_bd = be;
+ return 0;
+ }
+ rc = be_entry_get_rw( op, &op->o_ndn, pg_posixAccount, pg_uidNumber, 0, &user );
+ }
+
+ if ( rc != LDAP_SUCCESS || user == NULL ) {
+ op->o_bd = be;
+ return 0;
+ }
+
+ /* get target */
+ if ( pg->pg_style == ACL_STYLE_EXPAND ) {
+ char buf[ 1024 ];
+ struct berval bv;
+ AclRegexMatches amatches = { 0 };
+
+ amatches.dn_count = nmatch;
+ AC_MEMCPY( amatches.dn_data, matches, sizeof( amatches.dn_data ) );
+
+ bv.bv_len = sizeof( buf ) - 1;
+ bv.bv_val = buf;
+
+ if ( acl_string_expand( &bv, &pg->pg_pat,
+ &target->e_nname,
+ NULL, &amatches ) )
+ {
+ goto cleanup;
+ }
+
+ if ( dnNormalize( 0, NULL, NULL, &bv, &group_ndn,
+ op->o_tmpmemctx ) != LDAP_SUCCESS )
+ {
+ /* did not expand to a valid dn */
+ goto cleanup;
+ }
+
+ } else {
+ group_ndn = pg->pg_pat;
+ }
+
+ if ( target && dn_match( &target->e_nname, &group_ndn ) ) {
+ group = target;
+ rc = LDAP_SUCCESS;
+
+ } else {
+ group_be = op->o_bd = select_backend( &group_ndn, 0 );
+ if ( op->o_bd == NULL ) {
+ goto cleanup;
+ }
+ rc = be_entry_get_rw( op, &group_ndn, pg_posixGroup, pg_memberUid, 0, &group );
+ }
+
+ if ( group_ndn.bv_val != pg->pg_pat.bv_val ) {
+ op->o_tmpfree( group_ndn.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( rc == LDAP_SUCCESS && group != NULL ) {
+ Attribute *a_uid,
+ *a_member;
+
+ a_uid = attr_find( user->e_attrs, pg_uidNumber );
+ if ( !a_uid || !BER_BVISNULL( &a_uid->a_nvals[ 1 ] ) ) {
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+
+ } else {
+ a_member = attr_find( group->e_attrs, pg_memberUid );
+ if ( !a_member ) {
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+
+ } else {
+ rc = value_find_ex( pg_memberUid,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ a_member->a_nvals, &a_uid->a_nvals[ 0 ],
+ op->o_tmpmemctx );
+ }
+ }
+
+ } else {
+ rc = LDAP_NO_SUCH_OBJECT;
+ }
+
+
+ if ( rc == LDAP_SUCCESS ) {
+ ACL_LVL_ASSIGN_WRITE( *grant );
+ }
+
+cleanup:;
+ if ( group != NULL && group != target ) {
+ op->o_bd = group_be;
+ be_entry_release_r( op, group );
+ op->o_bd = be;
+ }
+
+ if ( user != NULL && user != target ) {
+ op->o_bd = user_be;
+ be_entry_release_r( op, user );
+ op->o_bd = be;
+ }
+
+ return 0;
+}
+
+static int
+pg_dynacl_destroy(
+ void *priv )
+{
+ pg_t *pg = (pg_t *)priv;
+
+ if ( pg != NULL ) {
+ if ( !BER_BVISNULL( &pg->pg_pat ) ) {
+ ber_memfree( pg->pg_pat.bv_val );
+ }
+ ch_free( pg );
+ }
+
+ return 0;
+}
+
+static struct slap_dynacl_t pg_dynacl = {
+ "posixGroup",
+ pg_dynacl_parse,
+ pg_dynacl_unparse,
+ pg_dynacl_mask,
+ pg_dynacl_destroy
+};
+
+int
+init_module( int argc, char *argv[] )
+{
+ return slap_dynacl_register( &pg_dynacl );
+}
+
+#endif /* SLAP_DYNACL */
diff --git a/contrib/slapd-modules/addpartial/Makefile b/contrib/slapd-modules/addpartial/Makefile
new file mode 100644
index 0000000..ecb7cd0
--- /dev/null
+++ b/contrib/slapd-modules/addpartial/Makefile
@@ -0,0 +1,46 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = addpartial.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+addpartial.la: addpartial-overlay.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/addpartial/README b/contrib/slapd-modules/addpartial/README
new file mode 100644
index 0000000..968cdd8
--- /dev/null
+++ b/contrib/slapd-modules/addpartial/README
@@ -0,0 +1,72 @@
+addpartial Overlay README
+
+DESCRIPTION
+ This package contains an OpenLDAP overlay called "addpartial" that
+ intercepts add requests, determines if the entry exists, determines what
+ attributes, if any, have changed, and modifies those attributes. If the
+ entry does not exist, the add request falls through and proceeds normally.
+ If the entry exists but no changes have been detected, the client receives
+ LDAP_SUCCESS (I suppose it is debatable what to do in this case, but this is
+ the most clean for my use. The LDAP_SUCCESS lets me know that the entry I
+ sent slapd == the entry already in my slapd DB. Perhaps this behavior
+ should be configurable in the future).
+
+ When a change is found, the addpartial overlay will replace all values for
+ the attribute (if an attribute does not exist in the new entry but exists
+ in the entry in the slapd DB, a replace will be done with an empty list of
+ values).
+
+ Once a modify takes place, the syncprov overlay will properly process the
+ change, provided that addpartial is the first overlay to run. Please see
+ the CAVEATS for more specifics about this.
+
+ The addpartial overlay makes it easy to replicate full entries to a slapd
+ instance without worrying about the differences between entries or even if
+ the entry exists. Using ldapadd to add entries, the addpartial overlay can
+ compare about 500 records per second. The intent of the addpartial overlay
+ is to make it easy to replicate records from a source that is not an LDAP
+ instance, such as a database. The overlay is also useful in places where it
+ is easier to create full entries rather than comparing an entry with an
+ entry that must be retrieved (with ldapsearch or similar) from an existing
+ slapd DB to find changes.
+
+ The addpartial overlay has been used in production since August 2004 and has
+ processed millions of records without incident.
+
+BUILDING
+ A Makefile is included, please set your LDAP_SRC directory properly.
+
+INSTALLATION
+ After compiling the addpartial overlay, add the following to your
+ slapd.conf:
+
+ ### slapd.conf
+ ...
+ moduleload addpartial.so
+ ...
+ # after database directive...
+ # this overlay should be the last overlay in the config file to ensure that
+ # it properly intercepts the add request
+ overlay addpartial
+ ...
+ ### end slapd.conf
+
+CAVEATS
+ - In order to ensure that addpartial does what it needs to do, it should be
+ the last overlay configured so it will run before the other overlays.
+ This is especially important if you are using syncrepl, as the modify that
+ addpartial does will muck with the locking that takes place in the
+ syncprov overlay.
+
+---
+Copyright 2004-2022 The OpenLDAP Foundation.
+Portions Copyright (C) Virginia Tech, David Hawes.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+http://www.OpenLDAP.org/license.html.
diff --git a/contrib/slapd-modules/addpartial/addpartial-overlay.c b/contrib/slapd-modules/addpartial/addpartial-overlay.c
new file mode 100644
index 0000000..b1d637b
--- /dev/null
+++ b/contrib/slapd-modules/addpartial/addpartial-overlay.c
@@ -0,0 +1,349 @@
+/* addpartial-overlay.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * Portions Copyright (C) 2004 Virginia Tech, David Hawes.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * http://www.OpenLDAP.org/license.html.
+ */
+/* ACKNOLEDGEDMENTS:
+ * This work was initially developed by David Hawes of Virginia Tech
+ * for inclusion in OpenLDAP Software.
+ */
+/* addpartial-overlay
+ *
+ * This is an OpenLDAP overlay that intercepts ADD requests, determines if a
+ * change has actually taken place for that record, and then performs a modify
+ * request for those values that have changed (modified, added, deleted). If
+ * the record has not changed in any way, it is ignored. If the record does not
+ * exist, the record falls through to the normal add mechanism. This overlay is
+ * useful for replicating from sources that are not LDAPs where it is easier to
+ * build entire records than to determine the changes (i.e. a database).
+ */
+
+#include "portable.h"
+#include "slap.h"
+
+static int collect_error_msg_cb( Operation *op, SlapReply *rs);
+
+static slap_overinst addpartial;
+
+/**
+ * The meat of the overlay. Search for the record, determine changes, take
+ * action or fall through.
+ */
+static int addpartial_add( Operation *op, SlapReply *rs)
+{
+ Operation nop = *op;
+ Entry *toAdd = NULL;
+ Entry *found = NULL;
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ int rc;
+
+ toAdd = op->oq_add.rs_e;
+
+ Debug(LDAP_DEBUG_TRACE, "%s: toAdd->e_nname.bv_val: %s\n",
+ addpartial.on_bi.bi_type, toAdd->e_nname.bv_val );
+
+ /* if the user doesn't have access, fall through to the normal ADD */
+ if(!access_allowed(op, toAdd, slap_schema.si_ad_entry,
+ NULL, ACL_WRITE, NULL))
+ {
+ return SLAP_CB_CONTINUE;
+ }
+
+ rc = overlay_entry_get_ov(&nop, &nop.o_req_ndn, NULL, NULL, 0, &found, on);
+
+ if(rc != LDAP_SUCCESS)
+ {
+ Debug(LDAP_DEBUG_TRACE,
+ "%s: no entry found, falling through to normal add\n",
+ addpartial.on_bi.bi_type );
+ return SLAP_CB_CONTINUE;
+ }
+ else
+ {
+ Debug(LDAP_DEBUG_TRACE, "%s: found the dn\n", addpartial.on_bi.bi_type );
+
+ if(found)
+ {
+ Attribute *attr = NULL;
+ Attribute *at = NULL;
+ int ret;
+ Modifications *mods = NULL;
+ Modifications **modtail = &mods;
+ Modifications *mod = NULL;
+
+ Debug(LDAP_DEBUG_TRACE, "%s: have an entry!\n",
+ addpartial.on_bi.bi_type );
+
+ /* determine if the changes are in the found entry */
+ for(attr = toAdd->e_attrs; attr; attr = attr->a_next)
+ {
+ if(attr->a_desc->ad_type->sat_atype.at_usage != 0) continue;
+
+ at = attr_find(found->e_attrs, attr->a_desc);
+ if(!at)
+ {
+ Debug(LDAP_DEBUG_TRACE, "%s: Attribute %s not found!\n",
+ addpartial.on_bi.bi_type,
+ attr->a_desc->ad_cname.bv_val );
+ mod = (Modifications *) ch_malloc(sizeof(
+ Modifications));
+ mod->sml_flags = 0;
+ mod->sml_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
+ mod->sml_op &= LDAP_MOD_OP;
+ mod->sml_next = NULL;
+ mod->sml_desc = attr->a_desc;
+ mod->sml_type = attr->a_desc->ad_cname;
+ mod->sml_values = attr->a_vals;
+ mod->sml_nvalues = attr->a_nvals;
+ mod->sml_numvals = attr->a_numvals;
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ }
+ else
+ {
+ MatchingRule *mr = attr->a_desc->ad_type->sat_equality;
+ struct berval *bv;
+ const char *text;
+ int acount , bcount;
+ Debug(LDAP_DEBUG_TRACE, "%s: Attribute %s found\n",
+ addpartial.on_bi.bi_type,
+ attr->a_desc->ad_cname.bv_val );
+
+ for(bv = attr->a_vals, acount = 0; bv->bv_val != NULL;
+ bv++, acount++)
+ {
+ /* count num values for attr */
+ }
+ for(bv = at->a_vals, bcount = 0; bv->bv_val != NULL;
+ bv++, bcount++)
+ {
+ /* count num values for attr */
+ }
+ if(acount != bcount)
+ {
+ Debug(LDAP_DEBUG_TRACE, "%s: acount != bcount, %s\n",
+ addpartial.on_bi.bi_type,
+ "replace all" );
+ mod = (Modifications *) ch_malloc(sizeof(
+ Modifications));
+ mod->sml_flags = 0;
+ mod->sml_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
+ mod->sml_op &= LDAP_MOD_OP;
+ mod->sml_next = NULL;
+ mod->sml_desc = attr->a_desc;
+ mod->sml_type = attr->a_desc->ad_cname;
+ mod->sml_values = attr->a_vals;
+ mod->sml_nvalues = attr->a_nvals;
+ mod->sml_numvals = attr->a_numvals;
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ continue;
+ }
+
+ for(bv = attr->a_vals; bv->bv_val != NULL; bv++)
+ {
+ struct berval *v;
+ ret = -1;
+
+ for(v = at->a_vals; v->bv_val != NULL; v++)
+ {
+ int r;
+ if(mr && ((r = value_match(&ret, attr->a_desc, mr,
+ SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
+ bv, v, &text)) == 0))
+ {
+ if(ret == 0)
+ break;
+ }
+ else
+ {
+ Debug(LDAP_DEBUG_TRACE,
+ "%s: \tvalue DNE, r: %d \n",
+ addpartial.on_bi.bi_type,
+ r );
+ ret = strcmp(bv->bv_val, v->bv_val);
+ if(ret == 0)
+ break;
+ }
+ }
+
+ if(ret == 0)
+ {
+ Debug(LDAP_DEBUG_TRACE,
+ "%s: \tvalue %s exists, ret: %d\n",
+ addpartial.on_bi.bi_type, bv->bv_val, ret);
+ }
+ else
+ {
+ Debug(LDAP_DEBUG_TRACE,
+ "%s: \tvalue %s DNE, ret: %d\n",
+ addpartial.on_bi.bi_type, bv->bv_val, ret);
+ mod = (Modifications *) ch_malloc(sizeof(
+ Modifications));
+ mod->sml_flags = 0;
+ mod->sml_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
+ mod->sml_op &= LDAP_MOD_OP;
+ mod->sml_next = NULL;
+ mod->sml_desc = attr->a_desc;
+ mod->sml_type = attr->a_desc->ad_cname;
+ mod->sml_values = attr->a_vals;
+ mod->sml_nvalues = attr->a_nvals;
+ mod->sml_numvals = attr->a_numvals;
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ break;
+ }
+ }
+ }
+ }
+
+ /* determine if any attributes were deleted */
+ for(attr = found->e_attrs; attr; attr = attr->a_next)
+ {
+ if(attr->a_desc->ad_type->sat_atype.at_usage != 0) continue;
+
+ at = NULL;
+ at = attr_find(toAdd->e_attrs, attr->a_desc);
+ if(!at)
+ {
+ Debug(LDAP_DEBUG_TRACE,
+ "%s: Attribute %s not found in new entry!!!\n",
+ addpartial.on_bi.bi_type,
+ attr->a_desc->ad_cname.bv_val );
+ mod = (Modifications *) ch_malloc(sizeof(
+ Modifications));
+ mod->sml_flags = 0;
+ mod->sml_op = LDAP_MOD_REPLACE;
+ mod->sml_next = NULL;
+ mod->sml_desc = attr->a_desc;
+ mod->sml_type = attr->a_desc->ad_cname;
+ mod->sml_values = NULL;
+ mod->sml_nvalues = NULL;
+ mod->sml_numvals = 0;
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ }
+ else
+ {
+ Debug(LDAP_DEBUG_TRACE,
+ "%s: Attribute %s found in new entry\n",
+ addpartial.on_bi.bi_type,
+ at->a_desc->ad_cname.bv_val );
+ }
+ }
+
+ overlay_entry_release_ov(&nop, found, 0, on);
+
+ if(mods)
+ {
+ Modifications *m = NULL;
+ Modifications *toDel;
+ int modcount;
+ slap_callback nullcb = { NULL, collect_error_msg_cb,
+ NULL, NULL };
+
+ Debug(LDAP_DEBUG_TRACE, "%s: mods to do...\n",
+ addpartial.on_bi.bi_type );
+
+ nop.o_tag = LDAP_REQ_MODIFY;
+ nop.orm_modlist = mods;
+ nop.orm_no_opattrs = 0;
+ nop.o_callback = &nullcb;
+ nop.o_bd->bd_info = (BackendInfo *) on->on_info;
+
+ for(m = mods, modcount = 0; m; m = m->sml_next,
+ modcount++)
+ {
+ /* count number of mods */
+ }
+
+ Debug(LDAP_DEBUG_TRACE, "%s: number of mods: %d\n",
+ addpartial.on_bi.bi_type, modcount );
+
+ if(nop.o_bd->be_modify)
+ {
+ SlapReply nrs = { REP_RESULT };
+ rc = (nop.o_bd->be_modify)(&nop, &nrs);
+ }
+
+ if(rc == LDAP_SUCCESS)
+ {
+ Debug(LDAP_DEBUG_TRACE,
+ "%s: modify successful\n",
+ addpartial.on_bi.bi_type );
+ }
+ else
+ {
+ Debug(LDAP_DEBUG_TRACE, "%s: modify unsuccessful: %d\n",
+ addpartial.on_bi.bi_type, rc );
+ rs->sr_err = rc;
+ if(nullcb.sc_private)
+ {
+ rs->sr_text = nullcb.sc_private;
+ }
+ }
+
+ Debug(LDAP_DEBUG_TRACE, "%s: freeing mods...\n",
+ addpartial.on_bi.bi_type );
+
+ for(toDel = mods; toDel; toDel = mods)
+ {
+ mods = mods->sml_next;
+ ch_free(toDel);
+ }
+ }
+ else
+ {
+ Debug(LDAP_DEBUG_TRACE, "%s: no mods to process\n",
+ addpartial.on_bi.bi_type );
+ }
+ }
+ else
+ {
+ Debug(LDAP_DEBUG_TRACE, "%s: no entry!\n",
+ addpartial.on_bi.bi_type );
+ }
+
+ op->o_callback = NULL;
+ send_ldap_result( op, rs );
+ ch_free((void *)rs->sr_text);
+ rs->sr_text = NULL;
+
+ return LDAP_SUCCESS;
+ }
+}
+
+static int collect_error_msg_cb( Operation *op, SlapReply *rs)
+{
+ if(rs->sr_text)
+ {
+ op->o_callback->sc_private = (void *) ch_strdup(rs->sr_text);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int addpartial_init()
+{
+ addpartial.on_bi.bi_type = "addpartial";
+ addpartial.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ addpartial.on_bi.bi_op_add = addpartial_add;
+
+ return (overlay_register(&addpartial));
+}
+
+int init_module(int argc, char *argv[])
+{
+ return addpartial_init();
+}
diff --git a/contrib/slapd-modules/adremap/Makefile b/contrib/slapd-modules/adremap/Makefile
new file mode 100644
index 0000000..b008eab
--- /dev/null
+++ b/contrib/slapd-modules/adremap/Makefile
@@ -0,0 +1,68 @@
+# $OpenLDAP$
+# Copyright 2015 Howard Chu <hyc@symas.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted only as authorized by the OpenLDAP
+# Public License.
+#
+# A copy of this license is available in the file LICENSE in the
+# top-level directory of the distribution or, alternatively, at
+# <http://www.OpenLDAP.org/license.html>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_ADREMAP=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = adremap.la
+MANPAGES = slapo-adremap.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+adremap.la: adremap.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/adremap/adremap.c b/contrib/slapd-modules/adremap/adremap.c
new file mode 100644
index 0000000..5a7b8f1
--- /dev/null
+++ b/contrib/slapd-modules/adremap/adremap.c
@@ -0,0 +1,652 @@
+/* adremap.c - Case-folding and DN-value remapping for AD proxies */
+/* $OpenLDAP$ */
+/*
+ * Copyright 2015 Howard Chu <hyc@symas.com>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+/*
+ * This file implements an overlay that performs two remapping functions
+ * to allow older POSIX clients to use Microsoft AD:
+ * 1: downcase the values of a configurable list of attributes
+ * 2: dereference some DN-valued attributes and convert to their simple names
+ * e.g. generate memberUid based on member
+ */
+
+#ifdef SLAPD_OVER_ADREMAP
+
+#include <ldap.h>
+#include "lutil.h"
+#include "slap.h"
+#include <ac/errno.h>
+#include <ac/time.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include "slap-config.h"
+
+typedef struct adremap_dnv {
+ struct adremap_dnv *ad_next;
+ AttributeDescription *ad_dnattr; /* DN-valued attr to deref */
+ AttributeDescription *ad_deref; /* target attr's value to retrieve */
+ AttributeDescription *ad_newattr; /* New attr to collect new values */
+ ObjectClass *ad_group; /* group objectclass on target */
+ ObjectClass *ad_mapgrp; /* group objectclass to map */
+ ObjectClass *ad_refgrp; /* objectclass of target DN */
+ struct berval ad_refbase; /* base DN of target entries */
+} adremap_dnv;
+/* example: member uid memberUid */
+
+typedef struct adremap_case {
+ struct adremap_case *ac_next;
+ AttributeDescription *ac_attr;
+} adremap_case;
+
+/* Per-instance configuration information */
+typedef struct adremap_info {
+ adremap_case *ai_case; /* attrs to downcase */
+ adremap_dnv *ai_dnv; /* DN attrs to remap */
+} adremap_info;
+
+enum {
+ ADREMAP_CASE = 1,
+ ADREMAP_DNV
+};
+
+static ConfigDriver adremap_cf_case;
+static ConfigDriver adremap_cf_dnv;
+
+/* configuration attribute and objectclass */
+static ConfigTable adremapcfg[] = {
+ { "adremap-downcase", "attrs", 2, 0, 0,
+ ARG_MAGIC|ADREMAP_CASE, adremap_cf_case,
+ "( OLcfgCtAt:6.1 "
+ "NAME 'olcADremapDowncase' "
+ "DESC 'List of attributes to casefold to lower case' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "adremap-dnmap", "dnattr targetattr newattr remoteOC localOC targetOC baseDN", 8, 8, 0,
+ ARG_MAGIC|ADREMAP_DNV, adremap_cf_dnv,
+ "( OLcfgCtAt:6.2 "
+ "NAME 'olcADremapDNmap' "
+ "DESC 'DN attr to map, attr from target to use, attr to generate, objectclass of remote"
+ " group, objectclass mapped group, objectclass of target entry, base DN of target entry' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs adremapocs[] = {
+ { "( OLcfgCtOc:6.1 "
+ "NAME 'olcADremapConfig' "
+ "DESC 'AD remap configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcADremapDowncase $ olcADremapDNmap ) )",
+ Cft_Overlay, adremapcfg, NULL, NULL },
+ { NULL, 0, NULL }
+};
+
+static int
+adremap_cf_case(ConfigArgs *c)
+{
+ BackendDB *be = (BackendDB *)c->be;
+ slap_overinst *on = (slap_overinst *)c->bi;
+ adremap_info *ai = on->on_bi.bi_private;
+ adremap_case *ac, **a2;
+ int rc = ARG_BAD_CONF;
+
+ switch(c->op) {
+ case SLAP_CONFIG_EMIT:
+ for (ac = ai->ai_case; ac; ac=ac->ac_next) {
+ rc = value_add_one(&c->rvalue_vals, &ac->ac_attr->ad_cname);
+ if (rc) break;
+ }
+ break;
+ case LDAP_MOD_DELETE:
+ if (c->valx < 0) {
+ for (ac = ai->ai_case; ac; ac=ai->ai_case) {
+ ai->ai_case = ac->ac_next;
+ ch_free(ac);
+ }
+ } else {
+ int i;
+ for (i=0, a2 = &ai->ai_case; i<c->valx; i++, a2 = &(*a2)->ac_next);
+ ac = *a2;
+ *a2 = ac->ac_next;
+ ch_free(ac);
+ }
+ rc = 0;
+ break;
+ default: {
+ const char *text;
+ adremap_case ad;
+ ad.ac_attr = NULL;
+ rc = slap_str2ad(c->argv[1], &ad.ac_attr, &text);
+ if (rc) break;
+ for (a2 = &ai->ai_case; *a2; a2 = &(*a2)->ac_next);
+ ac = ch_malloc(sizeof(adremap_case));
+ ac->ac_next = NULL;
+ ac->ac_attr = ad.ac_attr;
+ *a2 = ac;
+ break;
+ }
+ }
+ return rc;
+}
+
+static int
+adremap_cf_dnv(ConfigArgs *c)
+{
+ BackendDB *be = (BackendDB *)c->be;
+ slap_overinst *on = (slap_overinst *)c->bi;
+ adremap_info *ai = on->on_bi.bi_private;
+ adremap_dnv *ad, **a2;
+ int rc = ARG_BAD_CONF;
+
+ switch(c->op) {
+ case SLAP_CONFIG_EMIT:
+ for (ad = ai->ai_dnv; ad; ad=ad->ad_next) {
+ char *ptr;
+ struct berval bv;
+ bv.bv_len = ad->ad_dnattr->ad_cname.bv_len + ad->ad_deref->ad_cname.bv_len + ad->ad_newattr->ad_cname.bv_len + 2;
+ bv.bv_len += ad->ad_group->soc_cname.bv_len + ad->ad_mapgrp->soc_cname.bv_len + ad->ad_refgrp->soc_cname.bv_len + 3;
+ bv.bv_len += ad->ad_refbase.bv_len + 3;
+ bv.bv_val = ch_malloc(bv.bv_len + 1);
+ ptr = lutil_strcopy(bv.bv_val, ad->ad_dnattr->ad_cname.bv_val);
+ *ptr++ = ' ';
+ ptr = lutil_strcopy(ptr, ad->ad_deref->ad_cname.bv_val);
+ *ptr++ = ' ';
+ ptr = lutil_strcopy(ptr, ad->ad_newattr->ad_cname.bv_val);
+ *ptr++ = ' ';
+ ptr = lutil_strcopy(ptr, ad->ad_group->soc_cname.bv_val);
+ *ptr++ = ' ';
+ ptr = lutil_strcopy(ptr, ad->ad_mapgrp->soc_cname.bv_val);
+ *ptr++ = ' ';
+ ptr = lutil_strcopy(ptr, ad->ad_refgrp->soc_cname.bv_val);
+ *ptr++ = ' ';
+ *ptr++ = '"';
+ ptr = lutil_strcopy(ptr, ad->ad_refbase.bv_val);
+ *ptr++ = '"';
+ *ptr = '\0';
+ ber_bvarray_add(&c->rvalue_vals, &bv);
+ }
+ if (ai->ai_dnv) rc = 0;
+ break;
+ case LDAP_MOD_DELETE:
+ if (c->valx < 0) {
+ for (ad = ai->ai_dnv; ad; ad=ai->ai_dnv) {
+ ai->ai_dnv = ad->ad_next;
+ ch_free(ad);
+ }
+ } else {
+ int i;
+ for (i=0, a2 = &ai->ai_dnv; i<c->valx; i++, a2 = &(*a2)->ad_next);
+ ad = *a2;
+ *a2 = ad->ad_next;
+ ch_free(ad);
+ }
+ rc = 0;
+ break;
+ default: {
+ const char *text;
+ adremap_dnv av = {0};
+ struct berval dn;
+ rc = slap_str2ad(c->argv[1], &av.ad_dnattr, &text);
+ if (rc) break;
+ if (av.ad_dnattr->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName) {
+ rc = 1;
+ snprintf(c->cr_msg, sizeof(c->cr_msg), "<%s> not a DN-valued attribute",
+ c->argv[0]);
+ Debug(LDAP_DEBUG_ANY, "%s: %s(%s)\n", c->log, c->cr_msg, c->argv[1]);
+ break;
+ }
+ rc = slap_str2ad(c->argv[2], &av.ad_deref, &text);
+ if (rc) break;
+ rc = slap_str2ad(c->argv[3], &av.ad_newattr, &text);
+ if (rc) break;
+ av.ad_group = oc_find(c->argv[4]);
+ if (!av.ad_group) {
+ rc = 1;
+ break;
+ }
+ av.ad_mapgrp = oc_find(c->argv[5]);
+ if (!av.ad_mapgrp) {
+ rc = 1;
+ break;
+ }
+ av.ad_refgrp = oc_find(c->argv[6]);
+ if (!av.ad_refgrp) {
+ rc = 1;
+ break;
+ }
+ ber_str2bv(c->argv[7], 0, 0, &dn);
+ rc = dnNormalize(0, NULL, NULL, &dn, &av.ad_refbase, NULL);
+ if (rc) break;
+
+ for (a2 = &ai->ai_dnv; *a2; a2 = &(*a2)->ad_next);
+ ad = ch_malloc(sizeof(adremap_dnv));
+ ad->ad_next = NULL;
+ ad->ad_dnattr = av.ad_dnattr;
+ ad->ad_deref = av.ad_deref;
+ ad->ad_newattr = av.ad_newattr;
+ ad->ad_group = av.ad_group;
+ ad->ad_mapgrp = av.ad_mapgrp;
+ ad->ad_refgrp = av.ad_refgrp;
+ ad->ad_refbase = av.ad_refbase;
+ *a2 = ad;
+ break;
+ }
+ }
+ return rc;
+}
+
+typedef struct adremap_ctx {
+ slap_overinst *on;
+ AttributeName an;
+ AttributeDescription *ad;
+ int an_swap;
+} adremap_ctx;
+
+static int
+adremap_search_resp(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ adremap_ctx *ctx = op->o_callback->sc_private;
+ slap_overinst *on = ctx->on;
+ adremap_info *ai = on->on_bi.bi_private;
+ adremap_case *ac;
+ adremap_dnv *ad;
+ Attribute *a;
+ Entry *e;
+
+ if (rs->sr_type != REP_SEARCH)
+ return SLAP_CB_CONTINUE;
+
+ /* we munged the attr list, restore it to original */
+ if (ctx->an_swap) {
+ int i;
+ ctx->an_swap = 0;
+ for (i=0; rs->sr_attrs[i].an_name.bv_val; i++) {
+ if (rs->sr_attrs[i].an_desc == ctx->ad) {
+ rs->sr_attrs[i] = ctx->an;
+ break;
+ }
+ }
+ /* Usually rs->sr_attrs is just op->ors_attrs, but
+ * overlays like rwm may make a new copy. Fix both
+ * if needed.
+ */
+ if (op->ors_attrs != rs->sr_attrs) {
+ for (i=0; op->ors_attrs[i].an_name.bv_val; i++) {
+ if (op->ors_attrs[i].an_desc == ctx->ad) {
+ op->ors_attrs[i] = ctx->an;
+ break;
+ }
+ }
+ }
+ }
+ e = rs->sr_entry;
+ for (ac = ai->ai_case; ac; ac = ac->ac_next) {
+ a = attr_find(e->e_attrs, ac->ac_attr);
+ if (a) {
+ int i, j;
+ if (!(rs->sr_flags & REP_ENTRY_MODIFIABLE)) {
+ e = entry_dup(e);
+ rs_replace_entry(op, rs, on, e);
+ rs->sr_flags |= REP_ENTRY_MODIFIABLE|REP_ENTRY_MUSTBEFREED;
+ a = attr_find(e->e_attrs, ac->ac_attr);
+ }
+ for (i=0; i<a->a_numvals; i++) {
+ unsigned char *c = a->a_vals[i].bv_val;
+ for (j=0; j<a->a_vals[i].bv_len; j++)
+ if (isupper(c[j]))
+ c[j] = tolower(c[j]);
+ }
+ }
+ }
+ for (ad = ai->ai_dnv; ad; ad = ad->ad_next) {
+ a = attr_find(e->e_attrs, ad->ad_dnattr);
+ if (a) {
+ Entry *n;
+ Attribute *dr;
+ int i, rc;
+ if (!(rs->sr_flags & REP_ENTRY_MODIFIABLE)) {
+ e = entry_dup(e);
+ rs_replace_entry(op, rs, on, e);
+ rs->sr_flags |= REP_ENTRY_MODIFIABLE|REP_ENTRY_MUSTBEFREED;
+ a = attr_find(e->e_attrs, ad->ad_dnattr);
+ }
+ for (i=0; i<a->a_numvals; i++) {
+ struct berval dv;
+ dv = ad->ad_deref->ad_cname;
+ /* If the RDN uses the deref attr, just use it directly */
+ if (a->a_nvals[i].bv_val[dv.bv_len] == '=' &&
+ !memcmp(a->a_nvals[i].bv_val, dv.bv_val, dv.bv_len)) {
+ struct berval bv, nv;
+ char *ptr;
+ bv = a->a_vals[i];
+ nv = a->a_nvals[i];
+ bv.bv_val += dv.bv_len + 1;
+ ptr = strchr(bv.bv_val, ',');
+ if (ptr)
+ bv.bv_len = ptr - bv.bv_val;
+ else
+ bv.bv_len -= dv.bv_len+1;
+ nv.bv_val += dv.bv_len + 1;
+ ptr = strchr(nv.bv_val, ',');
+ if (ptr)
+ nv.bv_len = ptr - nv.bv_val;
+ else
+ nv.bv_len -= dv.bv_len+1;
+ attr_merge_one(e, ad->ad_newattr, &bv, &nv);
+ } else {
+ /* otherwise look up the deref attr */
+ n = NULL;
+ rc = be_entry_get_rw(op, &a->a_nvals[i], NULL, ad->ad_deref, 0, &n);
+ if (!rc && n) {
+ dr = attr_find(n->e_attrs, ad->ad_deref);
+ if (dr)
+ attr_merge_one(e, ad->ad_newattr, dr->a_vals, dr->a_nvals);
+ be_entry_release_r(op, n);
+ }
+ }
+ }
+ }
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int adremap_refsearch(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ if (rs->sr_type == REP_SEARCH) {
+ slap_callback *sc = op->o_callback;
+ struct berval *dn = sc->sc_private;
+ ber_dupbv_x(dn, &rs->sr_entry->e_nname, op->o_tmpmemctx);
+ return LDAP_SUCCESS;
+ }
+ return rs->sr_err;
+}
+
+static adremap_dnv *adremap_filter(
+ Operation *op,
+ adremap_info *ai
+)
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ Filter *f = op->ors_filter, *fn = NULL;
+ adremap_dnv *ad = NULL;
+ struct berval bv;
+ int fextra = 0;
+
+ /* Do we need to munge the filter? First see if it's of
+ * the form (objectClass=<mapgrp>)
+ * or form (&(objectClass=<mapgrp>)...)
+ * or form (&(&(objectClass=<mapgrp>)...)...)
+ */
+ if (f->f_choice == LDAP_FILTER_AND && f->f_and) {
+ fextra = 1;
+ f = f->f_and;
+ fn = f->f_next;
+ }
+ if (f->f_choice == LDAP_FILTER_AND && f->f_and) {
+ fextra = 2;
+ f = f->f_and;
+ }
+ if (f->f_choice == LDAP_FILTER_EQUALITY &&
+ f->f_av_desc == slap_schema.si_ad_objectClass) {
+ struct berval bv = f->f_av_value;
+
+ for (ad = ai->ai_dnv; ad; ad = ad->ad_next) {
+ if (!ber_bvstrcasecmp( &bv, &ad->ad_mapgrp->soc_cname )) {
+ /* Now check to see if next element is (<newattr>=foo) */
+ Filter *fnew;
+ if (fn && fn->f_choice == LDAP_FILTER_EQUALITY &&
+ fn->f_av_desc == ad->ad_newattr) {
+ Filter fr[3];
+ AttributeAssertion aa[2] = {0};
+ Operation op2;
+ slap_callback cb = {0};
+ SlapReply rs = {REP_RESULT};
+ struct berval dn = BER_BVNULL;
+
+ /* It's a match, setup a search with filter
+ * (&(objectclass=<refgrp>)(<deref>=foo))
+ */
+ fr[0].f_choice = LDAP_FILTER_AND;
+ fr[0].f_and = &fr[1];
+ fr[0].f_next = NULL;
+
+ fr[1].f_choice = LDAP_FILTER_EQUALITY;
+ fr[1].f_ava = &aa[0];
+ fr[1].f_av_desc = slap_schema.si_ad_objectClass;
+ fr[1].f_av_value = ad->ad_refgrp->soc_cname;
+ fr[1].f_next = &fr[2];
+
+ fr[2].f_choice = LDAP_FILTER_EQUALITY;
+ fr[2].f_ava = &aa[1];
+ fr[2].f_av_desc = ad->ad_deref;
+ fr[2].f_av_value = fn->f_av_value;
+ fr[2].f_next = NULL;
+
+ /* Search with this filter to retrieve target DN */
+ op2 = *op;
+ op2.o_callback = &cb;
+ cb.sc_response = adremap_refsearch;
+ cb.sc_private = &dn;
+ op2.o_req_dn = ad->ad_refbase;
+ op2.o_req_ndn = ad->ad_refbase;
+ op2.ors_filter = fr;
+ filter2bv_x(op, fr, &op2.ors_filterstr);
+ op2.ors_deref = LDAP_DEREF_NEVER;
+ op2.ors_slimit = 1;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+ op2.ors_attrs = slap_anlist_no_attrs;
+ op2.ors_attrsonly = 1;
+ op2.o_no_schema_check = 1;
+ op2.o_bd->bd_info = (BackendInfo *)on->on_info;
+ op2.o_bd->be_search(&op2, &rs);
+ op2.o_bd->bd_info = (BackendInfo *)on;
+ op->o_tmpfree(op2.ors_filterstr.bv_val, op->o_tmpmemctx);
+
+ if (!dn.bv_len) { /* no match was found */
+ ad = NULL;
+ break;
+ }
+
+ if (rs.sr_err) { /* sizelimit exceeded, etc.: invalid name */
+ op->o_tmpfree(dn.bv_val, op->o_tmpmemctx);
+ ad = NULL;
+ break;
+ }
+
+ /* Build a new filter of form
+ * (&(objectclass=<group>)(<dnattr>=foo-DN)...)
+ */
+ f = op->o_tmpalloc(sizeof(Filter), op->o_tmpmemctx);
+ f->f_choice = LDAP_FILTER_AND;
+ fnew = f;
+ f->f_next = NULL;
+
+ f->f_and = op->o_tmpalloc(sizeof(Filter), op->o_tmpmemctx);
+ f = f->f_and;
+ f->f_choice = LDAP_FILTER_EQUALITY;
+ f->f_ava = op->o_tmpcalloc(1, sizeof(AttributeAssertion), op->o_tmpmemctx);
+ f->f_av_desc = slap_schema.si_ad_objectClass;
+ ber_dupbv_x(&f->f_av_value, &ad->ad_group->soc_cname, op->o_tmpmemctx);
+
+ f->f_next = op->o_tmpalloc(sizeof(Filter), op->o_tmpmemctx);
+ f = f->f_next;
+ f->f_choice = LDAP_FILTER_EQUALITY;
+ f->f_ava = op->o_tmpcalloc(1, sizeof(AttributeAssertion), op->o_tmpmemctx);
+ f->f_av_desc = ad->ad_dnattr;
+ f->f_av_value = dn;
+
+ f->f_next = fn->f_next;
+ fn->f_next = NULL;
+ } else {
+ /* Build a new filter of form
+ * (objectclass=<group>)
+ */
+ f->f_next = NULL; /* disconnect old chain */
+
+ f = op->o_tmpalloc(sizeof(Filter), op->o_tmpmemctx);
+ f->f_choice = LDAP_FILTER_EQUALITY;
+ f->f_ava = op->o_tmpcalloc(1, sizeof(AttributeAssertion), op->o_tmpmemctx);
+ f->f_av_desc = slap_schema.si_ad_objectClass;
+ ber_dupbv_x(&f->f_av_value, &ad->ad_group->soc_cname, op->o_tmpmemctx);
+
+ /* If there was a wrapping (&), attach it. */
+ if (fextra) {
+ fnew = op->o_tmpalloc(sizeof(Filter), op->o_tmpmemctx);
+ fnew->f_choice = LDAP_FILTER_AND;
+ fnew->f_and = f;
+ fnew->f_next = NULL;
+ f->f_next = fn;
+ } else {
+ fnew = f;
+ f->f_next = NULL;
+ }
+ }
+ if (fextra > 1) {
+ f = op->o_tmpalloc(sizeof(Filter), op->o_tmpmemctx);
+ f->f_choice = LDAP_FILTER_AND;
+ f->f_and = fnew->f_and;
+ f->f_next = f->f_and->f_next;
+ f->f_and->f_next = op->ors_filter->f_and->f_and->f_next;
+ op->ors_filter->f_and->f_and->f_next = NULL;
+ fnew->f_and = f;
+ }
+ filter_free_x(op, op->ors_filter, 1);
+ op->o_tmpfree(op->ors_filterstr.bv_val, op->o_tmpmemctx);
+ op->ors_filter = fnew;
+ filter2bv_x(op, op->ors_filter, &op->ors_filterstr);
+ break;
+ }
+ }
+ }
+ return ad;
+}
+
+static int
+adremap_search(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ adremap_info *ai = (adremap_info *) on->on_bi.bi_private;
+ adremap_ctx *ctx;
+ adremap_dnv *ad = NULL;
+ slap_callback *cb;
+
+ /* Is this our own internal search? Ignore it */
+ if (op->o_no_schema_check)
+ return SLAP_CB_CONTINUE;
+
+ if (ai->ai_dnv)
+ /* check for filter match, fallthru if none */
+ ad = adremap_filter(op, ai);
+
+ cb = op->o_tmpcalloc(1, sizeof(slap_callback)+sizeof(adremap_ctx), op->o_tmpmemctx);
+ cb->sc_response = adremap_search_resp;
+ cb->sc_private = cb+1;
+ cb->sc_next = op->o_callback;
+ op->o_callback = cb;
+ ctx = cb->sc_private;
+ ctx->on = on;
+ if (ad && op->ors_attrs) { /* see if we need to remap a search attr */
+ int i;
+ for (i=0; op->ors_attrs[i].an_name.bv_val; i++) {
+ if (op->ors_attrs[i].an_desc == ad->ad_newattr) {
+ ctx->an_swap = 1;
+ ctx->ad = ad->ad_dnattr;
+ ctx->an = op->ors_attrs[i];
+ op->ors_attrs[i].an_desc = ad->ad_dnattr;
+ op->ors_attrs[i].an_name = ad->ad_dnattr->ad_cname;
+ break;
+ }
+ }
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+adremap_db_init(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+
+ /* initialize private structure to store configuration */
+ on->on_bi.bi_private = ch_calloc( 1, sizeof(adremap_info) );
+
+ return 0;
+}
+
+static int
+adremap_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ adremap_info *ai = (adremap_info *) on->on_bi.bi_private;
+ adremap_case *ac;
+ adremap_dnv *ad;
+
+ /* free config */
+ for (ac = ai->ai_case; ac; ac = ai->ai_case) {
+ ai->ai_case = ac->ac_next;
+ ch_free(ac);
+ }
+ for (ad = ai->ai_dnv; ad; ad = ai->ai_dnv) {
+ ai->ai_dnv = ad->ad_next;
+ ch_free(ad);
+ }
+ free( ai );
+
+ return 0;
+}
+
+static slap_overinst adremap;
+
+int adremap_initialize()
+{
+ int i, code;
+
+ adremap.on_bi.bi_type = "adremap";
+ adremap.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ adremap.on_bi.bi_db_init = adremap_db_init;
+ adremap.on_bi.bi_db_destroy = adremap_db_destroy;
+ adremap.on_bi.bi_op_search = adremap_search;
+
+ /* register configuration directives */
+ adremap.on_bi.bi_cf_ocs = adremapocs;
+ code = config_register_schema( adremapcfg, adremapocs );
+ if ( code ) return code;
+
+ return overlay_register( &adremap );
+}
+
+#if SLAPD_OVER_ADREMAP == SLAPD_MOD_DYNAMIC
+int init_module(int argc, char *argv[]) {
+ return adremap_initialize();
+}
+#endif
+
+#endif /* defined(SLAPD_OVER_ADREMAP) */
diff --git a/contrib/slapd-modules/adremap/slapo-adremap.5 b/contrib/slapd-modules/adremap/slapo-adremap.5
new file mode 100644
index 0000000..8b1fa45
--- /dev/null
+++ b/contrib/slapd-modules/adremap/slapo-adremap.5
@@ -0,0 +1,104 @@
+.TH SLAPO-ADREMAP 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2015 Howard Chu, All Rights Reserved.
+.\" $OpenLDAP$
+.SH NAME
+slapo-adremap \- AD Remap overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B adremap
+overlay to
+.BR slapd (8)
+remaps some attribute values for compatibility between Microsoft AD
+and older POSIX systems' PAM/NSS clients. It can be configured to
+convert values of given attributes to lower case, and it can be
+configured to generate RFC2307-compliant group memberships based
+on RFC2307bis groups. All mapping is only performed on entries
+returned as search responses.
+
+.SH CONFIGURATION
+The config directives that are specific to the
+.B adremap
+overlay must be prefixed by
+.BR adremap\- ,
+to avoid potential conflicts with directives specific to the underlying
+database or to other stacked overlays.
+
+.TP
+.B overlay adremap
+This directive adds the
+.B adremap
+overlay to the current database, see
+.BR slapd.conf (5)
+for details.
+
+.LP
+These
+.B slapd.conf
+configuration options are defined for the adremap overlay. They must
+appear after the
+.B overlay
+directive. They can each be specified multiple times:
+.TP
+.B adremap-downcase <attr>
+Specify an attributeType whose values will all be mapped to lowercase
+when returned in search responses.
+.TP
+.B adremap-dnmap <dnattr> <targetattr> <newattr> <remoteOC> <localOC> <targetOC> <baseDN>
+Specify a DN-valued attributeType whose values will be dereferenced. The
+.B <targetattr>
+of the target entry will be retrieved and its value will be added to the
+.B <newattr>
+in the entry. In addition, searches using a filter of the form
+.B (&(objectClass=<localOC>)(<newattr>=xxx))
+will be rewritten into the form
+.BR (&(objectClass=<remoteOC>)(<dnattr>=xxx-DN)) .
+This rewrite will accomplished by performing an additional internal search,
+with subtree scope, using the specified baseDN and a filter of the form
+.BR (&(objectClass=<targetOC>)(<targetattr>=xxx)) .
+
+
+.SH EXAMPLE
+This example configures the
+.B adremap
+overlay to map all
+.B uid
+attributes to lowercase, and create
+.B memberUid
+values for group entries. The mapping will turn requests for posixGroup
+entries into requests for groupOfNames entries, and the internal search
+will use inetOrgPerson entries under the ou=People,dc=example,dc=com subtree.
+
+Add the following to
+.BR slapd.conf (5):
+
+.LP
+.nf
+ database <database>
+ # ...
+
+ overlay adremap
+ adremap-downcase uid
+ adremap-dnmap member uid memberUid groupOfNames posixGroup inetOrgPerson ou=people,dc=example,dc=com
+.fi
+.LP
+.B slapd
+must also load
+.B adremap.la,
+if compiled as a run-time module;
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd (8).
+The
+.BR slapo-adremap (5)
+overlay supports dynamic configuration via
+.BR back-config.
+.SH ACKNOWLEDGEMENTS
+.P
+This module was written in 2015 by Howard Chu.
diff --git a/contrib/slapd-modules/allop/Makefile b/contrib/slapd-modules/allop/Makefile
new file mode 100644
index 0000000..053f377
--- /dev/null
+++ b/contrib/slapd-modules/allop/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = allop.la
+MANPAGES = slapo-allop.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+allop.la: allop.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/allop/README b/contrib/slapd-modules/allop/README
new file mode 100644
index 0000000..3768e6a
--- /dev/null
+++ b/contrib/slapd-modules/allop/README
@@ -0,0 +1,26 @@
+This directory contains a slapd overlay, allop.
+The intended usage is as a global overlay for use with those clients
+that do not make use of the RFC3673 allOp ("+") in the requested
+attribute list, but expect all operational attributes to be returned.
+Usage: add to slapd.conf(5)
+
+moduleload path/to/allop.so
+
+overlay allop
+allop-URI <ldapURI>
+
+if the allop-URI is not given, the rootDSE, i.e. "ldap:///??base",
+is assumed.
+
+Use Makefile to compile this plugin or use a command line similar to:
+
+gcc -shared -I../../../include -I../../../servers/slapd -Wall -g \
+ -o allop.so allop.c
+
+---
+Copyright 2004-2022 The OpenLDAP Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
diff --git a/contrib/slapd-modules/allop/allop.c b/contrib/slapd-modules/allop/allop.c
new file mode 100644
index 0000000..52fab3a
--- /dev/null
+++ b/contrib/slapd-modules/allop/allop.c
@@ -0,0 +1,262 @@
+/* allop.c - returns all operational attributes when appropriate */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion in
+ * OpenLDAP Software.
+ */
+
+/*
+ * The intended usage is as a global overlay for use with those clients
+ * that do not make use of the RFC3673 allOp ("+") in the requested
+ * attribute list, but expect all operational attributes to be returned.
+ * Usage: add
+ *
+
+overlay allop
+allop-URI <ldapURI>
+
+ *
+ * if the allop-URI is not given, the rootDSE, i.e. "ldap:///??base",
+ * is assumed.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+
+#define SLAP_OVER_VERSION_REQUIRE(major,minor,patch) \
+ ( \
+ ( LDAP_VENDOR_VERSION_MAJOR == X || LDAP_VENDOR_VERSION_MAJOR >= (major) ) \
+ && ( LDAP_VENDOR_VERSION_MINOR == X || LDAP_VENDOR_VERSION_MINOR >= (minor) ) \
+ && ( LDAP_VENDOR_VERSION_PATCH == X || LDAP_VENDOR_VERSION_PATCH >= (patch) ) \
+ )
+
+#if !SLAP_OVER_VERSION_REQUIRE(2,3,0)
+#error "version mismatch"
+#endif
+
+typedef struct allop_t {
+ struct berval ao_ndn;
+ int ao_scope;
+} allop_t;
+
+static int
+allop_db_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ allop_t *ao = (allop_t *)on->on_bi.bi_private;
+
+ if ( strcasecmp( argv[ 0 ], "allop-uri" ) == 0 ) {
+ LDAPURLDesc *lud;
+ struct berval dn,
+ ndn;
+ int scope,
+ rc = LDAP_SUCCESS;
+
+ if ( argc != 2 ) {
+ fprintf( stderr, "%s line %d: "
+ "need exactly 1 arg "
+ "in \"allop-uri <ldapURI>\" "
+ "directive.\n",
+ fname, lineno );
+ return 1;
+ }
+
+ if ( ldap_url_parse( argv[ 1 ], &lud ) != LDAP_URL_SUCCESS ) {
+ return -1;
+ }
+
+ scope = lud->lud_scope;
+ if ( scope == LDAP_SCOPE_DEFAULT ) {
+ scope = LDAP_SCOPE_BASE;
+ }
+
+ if ( lud->lud_dn == NULL || lud->lud_dn[ 0 ] == '\0' ) {
+ if ( scope == LDAP_SCOPE_BASE ) {
+ BER_BVZERO( &ndn );
+
+ } else {
+ ber_str2bv( "", 0, 1, &ndn );
+ }
+
+ } else {
+
+ ber_str2bv( lud->lud_dn, 0, 0, &dn );
+ rc = dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL );
+ }
+
+ ldap_free_urldesc( lud );
+ if ( rc != LDAP_SUCCESS ) {
+ return -1;
+ }
+
+ if ( BER_BVISNULL( &ndn ) ) {
+ /* rootDSE */
+ if ( ao != NULL ) {
+ ch_free( ao->ao_ndn.bv_val );
+ ch_free( ao );
+ on->on_bi.bi_private = NULL;
+ }
+
+ } else {
+ if ( ao == NULL ) {
+ ao = ch_calloc( 1, sizeof( allop_t ) );
+ on->on_bi.bi_private = (void *)ao;
+
+ } else {
+ ch_free( ao->ao_ndn.bv_val );
+ }
+
+ ao->ao_ndn = ndn;
+ ao->ao_scope = scope;
+ }
+
+ } else {
+ return SLAP_CONF_UNKNOWN;
+ }
+
+ return 0;
+}
+
+static int
+allop_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ allop_t *ao = (allop_t *)on->on_bi.bi_private;
+
+ if ( ao != NULL ) {
+ assert( !BER_BVISNULL( &ao->ao_ndn ) );
+
+ ch_free( ao->ao_ndn.bv_val );
+ ch_free( ao );
+ on->on_bi.bi_private = NULL;
+ }
+
+ return 0;
+}
+
+static int
+allop_op_search( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ allop_t *ao = (allop_t *)on->on_bi.bi_private;
+
+ slap_mask_t mask;
+ int i,
+ add_allUser = 0;
+
+ if ( ao == NULL ) {
+ if ( !BER_BVISEMPTY( &op->o_req_ndn )
+ || op->ors_scope != LDAP_SCOPE_BASE )
+ {
+ return SLAP_CB_CONTINUE;
+ }
+
+ } else {
+ if ( !dnIsSuffix( &op->o_req_ndn, &ao->ao_ndn ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ switch ( ao->ao_scope ) {
+ case LDAP_SCOPE_BASE:
+ if ( op->o_req_ndn.bv_len != ao->ao_ndn.bv_len ) {
+ return SLAP_CB_CONTINUE;
+ }
+ break;
+
+ case LDAP_SCOPE_ONELEVEL:
+ if ( op->ors_scope == LDAP_SCOPE_BASE ) {
+ struct berval rdn = op->o_req_ndn;
+
+ rdn.bv_len -= ao->ao_ndn.bv_len + STRLENOF( "," );
+ if ( !dnIsOneLevelRDN( &rdn ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ break;
+ }
+ return SLAP_CB_CONTINUE;
+
+ case LDAP_SCOPE_SUBTREE:
+ break;
+ }
+ }
+
+ mask = slap_attr_flags( op->ors_attrs );
+ if ( SLAP_OPATTRS( mask ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( !SLAP_USERATTRS( mask ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ i = 0;
+ if ( op->ors_attrs == NULL ) {
+ add_allUser = 1;
+
+ } else {
+ for ( ; !BER_BVISNULL( &op->ors_attrs[ i ].an_name ); i++ )
+ ;
+ }
+
+ op->ors_attrs = op->o_tmprealloc( op->ors_attrs,
+ sizeof( AttributeName ) * ( i + add_allUser + 2 ),
+ op->o_tmpmemctx );
+
+ if ( add_allUser ) {
+ op->ors_attrs[ i ] = slap_anlist_all_user_attributes[ 0 ];
+ i++;
+ }
+
+ op->ors_attrs[ i ] = slap_anlist_all_operational_attributes[ 0 ];
+
+ BER_BVZERO( &op->ors_attrs[ i + 1 ].an_name );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static slap_overinst allop;
+
+int
+allop_init()
+{
+ allop.on_bi.bi_type = "allop";
+
+ allop.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ allop.on_bi.bi_db_config = allop_db_config;
+ allop.on_bi.bi_db_destroy = allop_db_destroy;
+
+ allop.on_bi.bi_op_search = allop_op_search;
+
+ return overlay_register( &allop );
+}
+
+int
+init_module( int argc, char *argv[] )
+{
+ return allop_init();
+}
+
diff --git a/contrib/slapd-modules/allop/slapo-allop.5 b/contrib/slapd-modules/allop/slapo-allop.5
new file mode 100644
index 0000000..9e7fdc9
--- /dev/null
+++ b/contrib/slapd-modules/allop/slapo-allop.5
@@ -0,0 +1,63 @@
+.TH SLAPO-ALLOP 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2005-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo-allop \- All Operational Attributes overlay
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The All Operational Attributes overlay is designed to allow slapd to
+interoperate with dumb clients that expect all attributes, including
+operational ones, to be returned when "*" or an empty attribute list
+is requested, as opposed to RFC2251 and RFC3673.
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the All Operational overlay.
+They should appear after the
+.B overlay
+directive and before any subsequent
+.B database
+directive.
+.TP
+.B allop-URI <ldapURI>
+Specify the base and the scope of search operations that trigger the overlay.
+By default, it is "ldap:///??base", i.e. it only applies to the rootDSE.
+This requires the overlay to be instantiated as global.
+
+.SH EXAMPLES
+.LP
+default behavior: only affects requests to the rootDSE
+.nf
+ # global
+ overlay allop
+.fi
+.LP
+affects all requests
+.nf
+ # global
+ overlay allop
+ allop-URI "ldap:///??sub"
+.fi
+.LP
+affects only requests directed to the suffix of a database
+.nf
+ # per database
+ database mdb
+ suffix "dc=example,dc=com"
+ # database specific directives ...
+ overlay allop
+ allop-URI "ldap:///dc=example,dc=com??base"
+.fi
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5).
+
+.SH ACKNOWLEDGEMENTS
+.P
+This module was written in 2005 by Pierangelo Masarati for SysNet s.n.c.
diff --git a/contrib/slapd-modules/allowed/Makefile b/contrib/slapd-modules/allowed/Makefile
new file mode 100644
index 0000000..32cb432
--- /dev/null
+++ b/contrib/slapd-modules/allowed/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+# Copyright 2004 Howard Chu, Symas Corp. All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted only as authorized by the OpenLDAP
+# Public License.
+#
+# A copy of this license is available in the file LICENSE in the
+# top-level directory of the distribution or, alternatively, at
+# <http://www.OpenLDAP.org/license.html>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_ALLOWED=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = allowed.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+allowed.la: allowed.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/allowed/README b/contrib/slapd-modules/allowed/README
new file mode 100644
index 0000000..a1267cf
--- /dev/null
+++ b/contrib/slapd-modules/allowed/README
@@ -0,0 +1,73 @@
+This directory contains a slapd overlay, "allowed".
+
+ --- o --- o --- o ---
+
+It adds to entries returned by search operations the value of attributes
+
+"allowedAttributes"
+ <http://msdn.microsoft.com/en-us/library/ms675217(VS.85).aspx>
+
+"allowedAttributesEffective"
+ <http://msdn.microsoft.com/en-us/library/ms675218(VS.85).aspx>
+
+"allowedChildClasses"
+ <http://msdn.microsoft.com/en-us/library/ms675219(VS.85).aspx>
+
+"allowedChildClassesEffective"
+ <http://msdn.microsoft.com/en-us/library/ms675220(VS.85).aspx>
+
+No other use is made of those attributes: they cannot be compared,
+they cannot be used in search filters, they cannot be used in ACLs, ...
+
+ --- o --- o --- o ---
+
+Usage: add to slapd.conf(5)
+
+
+moduleload path/to/allowed.so
+overlay allowed
+
+or add
+
+dn: olcOverlay={0}allowed,olcDatabase={1}bdb,cn=config
+objectClass: olcOverlayConfig
+olcOverlay: {0}allowed
+
+as a child of the database that's intended to support this feature
+(replace "olcDatabase={1}bdb,cn=config" with the appropriate parent);
+or use
+
+dn: olcOverlay={0}allowed,olcDatabase={-1}frontend,cn=config
+objectClass: olcOverlayConfig
+olcOverlay: {0}allowed
+
+if it's supposed to be global.
+
+ --- o --- o --- o ---
+
+Use Makefile to compile this plugin or use a command line similar to:
+
+gcc -shared -I../../../include -I../../../servers/slapd -Wall -g \
+ -o allowed.so allowed.c
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2006-2022 The OpenLDAP Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
+ACKNOWLEDGEMENTS:
+This work was initially developed by Pierangelo Masarati for inclusion in
+OpenLDAP Software.
+
diff --git a/contrib/slapd-modules/allowed/allowed.c b/contrib/slapd-modules/allowed/allowed.c
new file mode 100644
index 0000000..26e3106
--- /dev/null
+++ b/contrib/slapd-modules/allowed/allowed.c
@@ -0,0 +1,504 @@
+/* allowed.c - add allowed attributes based on ACL */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2006-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion in
+ * OpenLDAP Software.
+ */
+
+/*
+ * Rationale: return in allowedAttributes the attributes required/allowed
+ * by the objectClasses that are currently present in an object; return
+ * in allowedAttributesEffective the subset of the above that can be written
+ * by the identity that performs the search.
+ *
+ * Caveats:
+ * - right now, the overlay assumes that all values of the objectClass
+ * attribute will be returned in rs->sr_entry; this may not be true
+ * in general, but it usually is for back-mdb. To generalize,
+ * the search request should be analyzed, and if allowedAttributes or
+ * allowedAttributesEffective are requested, add objectClass to the
+ * requested attributes
+ * - it assumes that there is no difference between write-add and
+ * write-delete
+ * - it assumes that access rules do not depend on the values of the
+ * attributes or on the contents of the entry (attr/val, filter, ...)
+ * allowedAttributes and allowedAttributesEffective cannot be used
+ * in filters or in compare
+ */
+
+#include "portable.h"
+
+/* define SLAPD_OVER_ALLOWED=2 to build as run-time loadable module */
+#ifdef SLAPD_OVER_ALLOWED
+
+#include "slap.h"
+
+/*
+ * NOTE: part of the schema definition reported below is taken
+ * from Microsoft schema definitions (OID, NAME, SYNTAX);
+ *
+ * EQUALITY is taken from
+ * <http://www.redhat.com/archives/fedora-directory-devel/2006-August/msg00007.html>
+ * (posted by Andrew Bartlett)
+ *
+ * The rest is guessed. Specifically
+ *
+ * DESC briefly describes the purpose
+ *
+ * NO-USER-MODIFICATION is added to make attributes operational
+ *
+ * USAGE is set to "dSAOperation" as per ITS#7493,
+ * to prevent replication, since this information
+ * is generated (based on ACL and identity of request)
+ * and not stored.
+ */
+
+#define AA_SCHEMA_AT "1.2.840.113556.1.4"
+
+static AttributeDescription
+ *ad_allowedChildClasses,
+ *ad_allowedChildClassesEffective,
+ *ad_allowedAttributes,
+ *ad_allowedAttributesEffective;
+
+static struct {
+ char *at;
+ AttributeDescription **ad;
+} aa_attrs[] = {
+ { "( " AA_SCHEMA_AT ".911 "
+ "NAME 'allowedChildClasses' "
+ "EQUALITY objectIdentifierMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
+ /* added by me :) */
+ "DESC 'Child classes allowed for a given object' "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", &ad_allowedChildClasses },
+ { "( " AA_SCHEMA_AT ".912 "
+ "NAME 'allowedChildClassesEffective' "
+ "EQUALITY objectIdentifierMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
+ /* added by me :) */
+ "DESC 'Child classes allowed for a given object according to ACLs' "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", &ad_allowedChildClassesEffective },
+ { "( " AA_SCHEMA_AT ".913 "
+ "NAME 'allowedAttributes' "
+ "EQUALITY objectIdentifierMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
+ /* added by me :) */
+ "DESC 'Attributes allowed for a given object' "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", &ad_allowedAttributes },
+ { "( " AA_SCHEMA_AT ".914 "
+ "NAME 'allowedAttributesEffective' "
+ "EQUALITY objectIdentifierMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
+ /* added by me :) */
+ "DESC 'Attributes allowed for a given object according to ACLs' "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", &ad_allowedAttributesEffective },
+
+ /* TODO: add objectClass stuff? */
+
+ { NULL, NULL }
+};
+
+static int
+aa_add_at( AttributeType *at, AttributeType ***atpp )
+{
+ int i = 0;
+
+ if ( *atpp ) {
+ for ( i = 0; (*atpp)[ i ] != NULL; i++ ) {
+ if ( (*atpp)[ i ] == at ) {
+ break;
+ }
+ }
+
+ if ( (*atpp)[ i ] != NULL ) {
+ return 0;
+ }
+ }
+
+ *atpp = ch_realloc( *atpp, sizeof( AttributeType * ) * ( i + 2 ) );
+ (*atpp)[ i ] = at;
+ (*atpp)[ i + 1 ] = NULL;
+
+ return 0;
+}
+
+static int
+aa_add_oc( ObjectClass *oc, ObjectClass ***ocpp, AttributeType ***atpp )
+{
+ int i = 0;
+
+ if ( *ocpp ) {
+ for ( ; (*ocpp)[ i ] != NULL; i++ ) {
+ if ( (*ocpp)[ i ] == oc ) {
+ break;
+ }
+ }
+
+ if ( (*ocpp)[ i ] != NULL ) {
+ return 0;
+ }
+ }
+
+ *ocpp = ch_realloc( *ocpp, sizeof( ObjectClass * ) * ( i + 2 ) );
+ (*ocpp)[ i ] = oc;
+ (*ocpp)[ i + 1 ] = NULL;
+
+ if ( oc->soc_required ) {
+ int i;
+
+ for ( i = 0; oc->soc_required[ i ] != NULL; i++ ) {
+ aa_add_at( oc->soc_required[ i ], atpp );
+ }
+ }
+
+ if ( oc->soc_allowed ) {
+ int i;
+
+ for ( i = 0; oc->soc_allowed[ i ] != NULL; i++ ) {
+ aa_add_at( oc->soc_allowed[ i ], atpp );
+ }
+ }
+
+ return 0;
+}
+
+static int
+aa_operational( Operation *op, SlapReply *rs )
+{
+ Attribute *a, **ap;
+ AccessControlState acl_state = ACL_STATE_INIT;
+ struct berval *v;
+ AttributeType **atp = NULL;
+ ObjectClass **ocp = NULL;
+
+#define GOT_NONE (0x0U)
+#define GOT_C (0x1U)
+#define GOT_CE (0x2U)
+#define GOT_A (0x4U)
+#define GOT_AE (0x8U)
+#define GOT_ALL (GOT_C|GOT_CE|GOT_A|GOT_AE)
+ int got = GOT_NONE;
+
+ /* only add if requested */
+ if ( SLAP_OPATTRS( rs->sr_attr_flags ) ) {
+ got = GOT_ALL;
+
+ } else {
+ if ( ad_inlist( ad_allowedChildClasses, rs->sr_attrs ) ) {
+ got |= GOT_C;
+ }
+
+ if ( ad_inlist( ad_allowedChildClassesEffective, rs->sr_attrs ) ) {
+ got |= GOT_CE;
+ }
+
+ if ( ad_inlist( ad_allowedAttributes, rs->sr_attrs ) ) {
+ got |= GOT_A;
+ }
+
+ if ( ad_inlist( ad_allowedAttributesEffective, rs->sr_attrs ) ) {
+ got |= GOT_AE;
+ }
+ }
+
+ if ( got == GOT_NONE ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* shouldn't be called without an entry; please check */
+ assert( rs->sr_entry != NULL );
+
+ for ( ap = &rs->sr_operational_attrs; *ap != NULL; ap = &(*ap)->a_next )
+ /* go to last */ ;
+
+ /* see caveats; this is not guaranteed for all backends */
+ a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_objectClass );
+ if ( a == NULL ) {
+ goto do_oc;
+ }
+
+ /* if client has no access to objectClass attribute; don't compute */
+ if ( !access_allowed( op, rs->sr_entry, slap_schema.si_ad_objectClass,
+ NULL, ACL_READ, &acl_state ) )
+ {
+ return SLAP_CB_CONTINUE;
+ }
+
+ for ( v = a->a_nvals; !BER_BVISNULL( v ); v++ ) {
+ ObjectClass *oc = oc_bvfind( v );
+
+ assert( oc != NULL );
+
+ /* if client has no access to specific value, don't compute */
+ if ( !access_allowed( op, rs->sr_entry,
+ slap_schema.si_ad_objectClass,
+ &oc->soc_cname, ACL_READ, &acl_state ) )
+ {
+ continue;
+ }
+
+ aa_add_oc( oc, &ocp, &atp );
+
+ if ( oc->soc_sups ) {
+ int i;
+
+ for ( i = 0; oc->soc_sups[ i ] != NULL; i++ ) {
+ aa_add_oc( oc->soc_sups[ i ], &ocp, &atp );
+ }
+ }
+ }
+
+ ch_free( ocp );
+
+ if ( atp != NULL ) {
+ BerVarray bv_allowed = NULL,
+ bv_effective = NULL;
+ int i, ja = 0, je = 0;
+
+ for ( i = 0; atp[ i ] != NULL; i++ )
+ /* just count */ ;
+
+ if ( got & GOT_A ) {
+ bv_allowed = ch_calloc( i + 1, sizeof( struct berval ) );
+ }
+ if ( got & GOT_AE ) {
+ bv_effective = ch_calloc( i + 1, sizeof( struct berval ) );
+ }
+
+ for ( i = 0, ja = 0, je = 0; atp[ i ] != NULL; i++ ) {
+ if ( got & GOT_A ) {
+ ber_dupbv( &bv_allowed[ ja ], &atp[ i ]->sat_cname );
+ ja++;
+ }
+
+ if ( got & GOT_AE ) {
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+
+ if ( slap_bv2ad( &atp[ i ]->sat_cname, &ad, &text ) ) {
+ /* log? */
+ continue;
+ }
+
+ if ( access_allowed( op, rs->sr_entry,
+ ad, NULL, ACL_WRITE, NULL ) )
+ {
+ ber_dupbv( &bv_effective[ je ], &atp[ i ]->sat_cname );
+ je++;
+ }
+ }
+ }
+
+ ch_free( atp );
+
+ if ( ( got & GOT_A ) && ja > 0 ) {
+ *ap = attr_alloc( ad_allowedAttributes );
+ (*ap)->a_vals = bv_allowed;
+ (*ap)->a_nvals = bv_allowed;
+ (*ap)->a_numvals = ja;
+ ap = &(*ap)->a_next;
+ }
+
+ if ( ( got & GOT_AE ) && je > 0 ) {
+ *ap = attr_alloc( ad_allowedAttributesEffective );
+ (*ap)->a_vals = bv_effective;
+ (*ap)->a_nvals = bv_effective;
+ (*ap)->a_numvals = je;
+ ap = &(*ap)->a_next;
+ }
+
+ *ap = NULL;
+ }
+
+do_oc:;
+ if ( ( got & GOT_C ) || ( got & GOT_CE ) ) {
+ BerVarray bv_allowed = NULL,
+ bv_effective = NULL;
+ int i, ja = 0, je = 0;
+
+ ObjectClass *oc;
+
+ for ( i = 0, oc_start( &oc ); oc != NULL; oc_next( &oc ) ) {
+ /* we can only add AUXILIARY objectClasses */
+ if ( oc->soc_kind != LDAP_SCHEMA_AUXILIARY ) {
+ continue;
+ }
+
+ i++;
+ }
+
+ if ( got & GOT_C ) {
+ bv_allowed = ch_calloc( i + 1, sizeof( struct berval ) );
+ }
+ if ( got & GOT_CE ) {
+ bv_effective = ch_calloc( i + 1, sizeof( struct berval ) );
+ }
+
+ for ( oc_start( &oc ); oc != NULL; oc_next( &oc ) ) {
+ /* we can only add AUXILIARY objectClasses */
+ if ( oc->soc_kind != LDAP_SCHEMA_AUXILIARY ) {
+ continue;
+ }
+
+ if ( got & GOT_C ) {
+ ber_dupbv( &bv_allowed[ ja ], &oc->soc_cname );
+ ja++;
+ }
+
+ if ( got & GOT_CE ) {
+ if ( !access_allowed( op, rs->sr_entry,
+ slap_schema.si_ad_objectClass,
+ &oc->soc_cname, ACL_WRITE, NULL ) )
+ {
+ goto done_ce;
+ }
+
+ if ( oc->soc_required ) {
+ for ( i = 0; oc->soc_required[ i ] != NULL; i++ ) {
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+
+ if ( slap_bv2ad( &oc->soc_required[ i ]->sat_cname, &ad, &text ) ) {
+ /* log? */
+ continue;
+ }
+
+ if ( !access_allowed( op, rs->sr_entry,
+ ad, NULL, ACL_WRITE, NULL ) )
+ {
+ goto done_ce;
+ }
+ }
+ }
+
+ ber_dupbv( &bv_effective[ je ], &oc->soc_cname );
+ je++;
+ }
+done_ce:;
+ }
+
+ if ( ( got & GOT_C ) && ja > 0 ) {
+ *ap = attr_alloc( ad_allowedChildClasses );
+ (*ap)->a_vals = bv_allowed;
+ (*ap)->a_nvals = bv_allowed;
+ (*ap)->a_numvals = ja;
+ ap = &(*ap)->a_next;
+ }
+
+ if ( ( got & GOT_CE ) && je > 0 ) {
+ *ap = attr_alloc( ad_allowedChildClassesEffective );
+ (*ap)->a_vals = bv_effective;
+ (*ap)->a_nvals = bv_effective;
+ (*ap)->a_numvals = je;
+ ap = &(*ap)->a_next;
+ }
+
+ *ap = NULL;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static slap_overinst aa;
+
+#if LDAP_VENDOR_VERSION_MINOR != X && LDAP_VENDOR_VERSION_MINOR <= 3
+/* backport register_at() from HEAD, to allow building with OL <= 2.3 */
+static int
+register_at( char *def, AttributeDescription **rad, int dupok )
+{
+ LDAPAttributeType *at;
+ int code, freeit = 0;
+ const char *err;
+ AttributeDescription *ad = NULL;
+
+ at = ldap_str2attributetype( def, &code, &err, LDAP_SCHEMA_ALLOW_ALL );
+ if ( !at ) {
+ Debug( LDAP_DEBUG_ANY,
+ "register_at: AttributeType \"%s\": %s, %s\n",
+ def, ldap_scherr2str(code), err );
+ return code;
+ }
+
+ code = at_add( at, 0, NULL, &err );
+ if ( code ) {
+ if ( code == SLAP_SCHERR_ATTR_DUP && dupok ) {
+ freeit = 1;
+
+ } else {
+ ldap_attributetype_free( at );
+ Debug( LDAP_DEBUG_ANY,
+ "register_at: AttributeType \"%s\": %s, %s\n",
+ def, scherr2str(code), err );
+ return code;
+ }
+ }
+ code = slap_str2ad( at->at_names[0], &ad, &err );
+ if ( freeit || code ) {
+ ldap_attributetype_free( at );
+ } else {
+ ldap_memfree( at );
+ }
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY, "register_at: AttributeType \"%s\": %s\n",
+ def, err );
+ }
+ if ( rad ) *rad = ad;
+ return code;
+}
+#endif
+
+#if SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC
+static
+#endif /* SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC */
+int
+aa_initialize( void )
+{
+ int i;
+
+ aa.on_bi.bi_type = "allowed";
+
+ aa.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ aa.on_bi.bi_operational = aa_operational;
+
+ /* aa schema integration */
+ for ( i = 0; aa_attrs[i].at; i++ ) {
+ int code;
+
+ code = register_at( aa_attrs[i].at, aa_attrs[i].ad, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "aa_initialize: register_at failed\n" );
+ return -1;
+ }
+ }
+
+ return overlay_register( &aa );
+}
+
+#if SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return aa_initialize();
+}
+#endif /* SLAPD_OVER_ALLOWED == SLAPD_MOD_DYNAMIC */
+
+#endif /* SLAPD_OVER_ALLOWED */
diff --git a/contrib/slapd-modules/authzid/Makefile b/contrib/slapd-modules/authzid/Makefile
new file mode 100644
index 0000000..ef9c4bc
--- /dev/null
+++ b/contrib/slapd-modules/authzid/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+# Copyright 2004 Howard Chu, Symas Corp. All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted only as authorized by the OpenLDAP
+# Public License.
+#
+# A copy of this license is available in the file LICENSE in the
+# top-level directory of the distribution or, alternatively, at
+# <http://www.OpenLDAP.org/license.html>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = authzid.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+authzid.la: authzid.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/authzid/authzid.c b/contrib/slapd-modules/authzid/authzid.c
new file mode 100644
index 0000000..37264bf
--- /dev/null
+++ b/contrib/slapd-modules/authzid/authzid.c
@@ -0,0 +1,390 @@
+/* authzid.c - RFC 3829 Authzid Control */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2010-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+/*
+ * RFC 3829 Authzid
+ *
+ * must be instantiated as a global overlay
+ */
+
+#include "portable.h"
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+#include "ac/string.h"
+
+typedef struct authzid_conn_t {
+ Connection *conn;
+ int refcnt;
+ char authzid_flag;
+} authzid_conn_t;
+
+static ldap_pvt_thread_mutex_t authzid_mutex;
+static Avlnode *authzid_tree;
+
+static int
+authzid_conn_cmp( const void *c1, const void *c2 )
+{
+ const authzid_conn_t *ac1 = (const authzid_conn_t *)c1;
+ const authzid_conn_t *ac2 = (const authzid_conn_t *)c2;
+
+ return SLAP_PTRCMP( ac1->conn, ac2->conn );
+}
+
+static int
+authzid_conn_dup( void *c1, void *c2 )
+{
+ authzid_conn_t *ac1 = (authzid_conn_t *)c1;
+ authzid_conn_t *ac2 = (authzid_conn_t *)c2;
+
+ if ( ac1->conn == ac2->conn ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int authzid_cid;
+static slap_overinst authzid;
+
+static authzid_conn_t *
+authzid_conn_find( Connection *c )
+{
+ authzid_conn_t *ac = NULL, tmp = { 0 };
+
+ tmp.conn = c;
+ ac = (authzid_conn_t *)ldap_avl_find( authzid_tree, (caddr_t)&tmp, authzid_conn_cmp );
+ if ( ac == NULL || ( ac != NULL && ac->refcnt != 0 ) ) {
+ ac = NULL;
+ }
+ if ( ac ) {
+ ac->refcnt++;
+ }
+
+ return ac;
+}
+
+static authzid_conn_t *
+authzid_conn_get( Connection *c )
+{
+ authzid_conn_t *ac = NULL;
+
+ ldap_pvt_thread_mutex_lock( &authzid_mutex );
+ ac = authzid_conn_find( c );
+ if ( ac && ac->refcnt ) ac = NULL;
+ if ( ac ) ac->refcnt++;
+ ldap_pvt_thread_mutex_unlock( &authzid_mutex );
+
+ return ac;
+}
+
+static void
+authzid_conn_release( authzid_conn_t *ac )
+{
+ ldap_pvt_thread_mutex_lock( &authzid_mutex );
+ ac->refcnt--;
+ ldap_pvt_thread_mutex_unlock( &authzid_mutex );
+}
+
+static int
+authzid_conn_insert( Connection *c, char flag )
+{
+ authzid_conn_t *ac;
+ int rc;
+
+ ldap_pvt_thread_mutex_lock( &authzid_mutex );
+ ac = authzid_conn_find( c );
+ if ( ac ) {
+ ldap_pvt_thread_mutex_unlock( &authzid_mutex );
+ return -1;
+ }
+
+ ac = ch_malloc( sizeof( authzid_conn_t ) );
+ ac->conn = c;
+ ac->refcnt = 0;
+ ac->authzid_flag = flag;
+ rc = ldap_avl_insert( &authzid_tree, (caddr_t)ac,
+ authzid_conn_cmp, authzid_conn_dup );
+ ldap_pvt_thread_mutex_unlock( &authzid_mutex );
+
+ return rc;
+}
+
+static int
+authzid_conn_remove( Connection *c )
+{
+ authzid_conn_t *ac, *tmp;
+
+ ldap_pvt_thread_mutex_lock( &authzid_mutex );
+ ac = authzid_conn_find( c );
+ if ( !ac ) {
+ ldap_pvt_thread_mutex_unlock( &authzid_mutex );
+ return -1;
+ }
+ tmp = ldap_avl_delete( &authzid_tree, (caddr_t)ac, authzid_conn_cmp );
+ ldap_pvt_thread_mutex_unlock( &authzid_mutex );
+
+ assert( tmp == ac );
+ ch_free( ac );
+
+ return 0;
+}
+
+static int
+authzid_response(
+ Operation *op,
+ SlapReply *rs )
+{
+ LDAPControl **ctrls;
+ struct berval edn = BER_BVNULL;
+ ber_len_t len = 0;
+ int n = 0;
+
+ assert( rs->sr_tag = LDAP_RES_BIND );
+
+ if ( rs->sr_err == LDAP_SASL_BIND_IN_PROGRESS ) {
+ authzid_conn_t *ac = op->o_controls[ authzid_cid ];
+ if ( ac ) {
+ authzid_conn_release( ac );
+ } else {
+ (void)authzid_conn_insert( op->o_conn, op->o_ctrlflag[ authzid_cid ] );
+ }
+ return SLAP_CB_CONTINUE;
+ }
+
+ (void)authzid_conn_remove( op->o_conn );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( !BER_BVISEMPTY( &op->orb_edn ) ) {
+ edn = op->orb_edn;
+
+ } else if ( !BER_BVISEMPTY( &op->o_conn->c_dn ) ) {
+ edn = op->o_conn->c_dn;
+ }
+
+ if ( !BER_BVISEMPTY( &edn ) ) {
+ ber_tag_t save_tag = op->o_tag;
+ struct berval save_dn = op->o_dn;
+ struct berval save_ndn = op->o_ndn;
+ int rc;
+
+ /* pretend it's an extop without data,
+ * so it is treated as a generic write
+ */
+ op->o_tag = LDAP_REQ_EXTENDED;
+ op->o_dn = edn;
+ op->o_ndn = edn;
+ rc = backend_check_restrictions( op, rs, NULL );
+ op->o_tag = save_tag;
+ op->o_dn = save_dn;
+ op->o_ndn = save_ndn;
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_err = LDAP_CONFIDENTIALITY_REQUIRED;
+ return SLAP_CB_CONTINUE;
+ }
+
+ len = STRLENOF("dn:") + edn.bv_len;
+ }
+
+ /* save original controls in sc_private;
+ * will be restored by sc_cleanup
+ */
+ if ( rs->sr_ctrls != NULL ) {
+ op->o_callback->sc_private = rs->sr_ctrls;
+ for ( ; rs->sr_ctrls[n] != NULL; n++ )
+ ;
+ }
+
+ ctrls = op->o_tmpalloc( sizeof( LDAPControl * )*( n + 2 ), op->o_tmpmemctx );
+ n = 0;
+ if ( rs->sr_ctrls ) {
+ for ( ; rs->sr_ctrls[n] != NULL; n++ ) {
+ ctrls[n] = rs->sr_ctrls[n];
+ }
+ }
+
+ /* anonymous: "", otherwise "dn:<dn>" */
+ ctrls[n] = op->o_tmpalloc( sizeof( LDAPControl ) + len + 1, op->o_tmpmemctx );
+ ctrls[n]->ldctl_oid = LDAP_CONTROL_AUTHZID_RESPONSE;
+ ctrls[n]->ldctl_iscritical = 0;
+ ctrls[n]->ldctl_value.bv_len = len;
+ ctrls[n]->ldctl_value.bv_val = (char *)&ctrls[n][1];
+ if ( len ) {
+ char *ptr;
+
+ ptr = lutil_strcopy( ctrls[n]->ldctl_value.bv_val, "dn:" );
+ ptr = lutil_strncopy( ptr, edn.bv_val, edn.bv_len );
+ }
+ ctrls[n]->ldctl_value.bv_val[len] = '\0';
+ ctrls[n + 1] = NULL;
+
+ rs->sr_ctrls = ctrls;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+authzid_cleanup(
+ Operation *op,
+ SlapReply *rs )
+{
+ if ( rs->sr_ctrls ) {
+ LDAPControl *ctrl;
+
+ /* if ours, cleanup */
+ ctrl = ldap_control_find( LDAP_CONTROL_AUTHZID_RESPONSE, rs->sr_ctrls, NULL );
+ if ( ctrl ) {
+ op->o_tmpfree( rs->sr_ctrls, op->o_tmpmemctx );
+ rs->sr_ctrls = NULL;
+ }
+
+ if ( op->o_callback->sc_private != NULL ) {
+ rs->sr_ctrls = (LDAPControl **)op->o_callback->sc_private;
+ op->o_callback->sc_private = NULL;
+ }
+ }
+
+ op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
+ op->o_callback = NULL;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+authzid_op_bind(
+ Operation *op,
+ SlapReply *rs )
+{
+ slap_callback *sc;
+
+ if ( op->o_ctrlflag[ authzid_cid ] <= SLAP_CONTROL_IGNORED ) {
+ authzid_conn_t *ac = authzid_conn_get( op->o_conn );
+ if ( ac ) {
+ op->o_ctrlflag[ authzid_cid ] = ac->authzid_flag;
+ op->o_controls[ authzid_cid] = ac;
+ }
+ }
+
+ if ( op->o_ctrlflag[ authzid_cid ] > SLAP_CONTROL_IGNORED ) {
+ sc = op->o_callback;
+ op->o_callback = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
+ op->o_callback->sc_response = authzid_response;
+ op->o_callback->sc_cleanup = authzid_cleanup;
+ op->o_callback->sc_private = NULL;
+ op->o_callback->sc_writewait = NULL;
+ op->o_callback->sc_next = sc;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+parse_authzid_ctrl(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ if ( op->o_ctrlflag[ authzid_cid ] != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "authzid control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "authzid control value not absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ /* drop ongoing requests */
+ (void)authzid_conn_remove( op->o_conn );
+
+ op->o_ctrlflag[ authzid_cid ] = ctrl->ldctl_iscritical ? SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+authzid_db_init( BackendDB *be, ConfigReply *cr )
+{
+ if ( !SLAP_ISGLOBALOVERLAY( be ) ) {
+ /* do not allow slapo-ppolicy to be global by now (ITS#5858) */
+ if ( cr ) {
+ snprintf( cr->msg, sizeof(cr->msg),
+ "slapo-authzid must be global" );
+ Debug( LDAP_DEBUG_ANY, "%s\n", cr->msg );
+ }
+ return 1;
+ }
+
+ int rc;
+
+ rc = register_supported_control( LDAP_CONTROL_AUTHZID_REQUEST,
+ SLAP_CTRL_GLOBAL|SLAP_CTRL_BIND|SLAP_CTRL_HIDE, NULL,
+ parse_authzid_ctrl, &authzid_cid );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "authzid_initialize: Failed to register control '%s' (%d)\n",
+ LDAP_CONTROL_AUTHZID_REQUEST, rc );
+ return rc;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Almost pointless, by now, since this overlay needs to be global,
+ * and global overlays deletion is not supported yet.
+ */
+static int
+authzid_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+#ifdef SLAP_CONFIG_DELETE
+ overlay_unregister_control( be, LDAP_CONTROL_AUTHZID_REQUEST );
+#endif /* SLAP_CONFIG_DELETE */
+
+ unregister_supported_control( LDAP_CONTROL_AUTHZID_REQUEST );
+
+ return 0;
+}
+
+static int
+authzid_initialize( void )
+{
+ ldap_pvt_thread_mutex_init( &authzid_mutex );
+
+ authzid.on_bi.bi_type = "authzid";
+
+ authzid.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ authzid.on_bi.bi_db_init = authzid_db_init;
+ authzid.on_bi.bi_db_destroy = authzid_db_destroy;
+ authzid.on_bi.bi_op_bind = authzid_op_bind;
+
+ return overlay_register( &authzid );
+}
+
+int
+init_module( int argc, char *argv[] )
+{
+ return authzid_initialize();
+}
+
diff --git a/contrib/slapd-modules/autogroup/Makefile b/contrib/slapd-modules/autogroup/Makefile
new file mode 100644
index 0000000..7dd6613
--- /dev/null
+++ b/contrib/slapd-modules/autogroup/Makefile
@@ -0,0 +1,46 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = autogroup.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+autogroup.la: autogroup.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/autogroup/README b/contrib/slapd-modules/autogroup/README
new file mode 100644
index 0000000..b68dd75
--- /dev/null
+++ b/contrib/slapd-modules/autogroup/README
@@ -0,0 +1,120 @@
+autogroup overlay Readme
+
+DESCRIPTION
+ The autogroup overlay allows automated updates of group memberships which
+ meet the requirements of any filter contained in the group definition.
+ The filters are built from LDAP URI-valued attributes. Any time an object
+ is added/deleted/updated, it is tested for compliance with the filters,
+ and its membership is accordingly updated. For searches and compares
+ it behaves like a static group.
+ If the attribute part of the URI is filled, the group entry is populated
+ by the values of this attribute in the entries resulting from the search.
+
+BUILDING
+ A Makefile is included.
+
+CONFIGURATION
+ # dyngroup.schema:
+ The dyngroup schema must be modified, adding the 'member' attribute
+ to the MAY clause of the groupOfURLs object class, i.e.:
+
+ objectClass ( NetscapeLDAPobjectClass:33
+ NAME 'groupOfURLs'
+ SUP top STRUCTURAL
+ MUST cn
+ MAY ( memberURL $ businessCategory $ description $ o $ ou $
+ owner $ seeAlso $ member) )
+
+
+ # slapd.conf:
+
+ moduleload /path/to/autogroup.so
+ Loads the overlay (OpenLDAP must be built with --enable-modules).
+
+ overlay autogroup
+ This directive adds the autogroup overlay to the current database.
+
+ autogroup-attrset <group-oc> <URL-ad> <member-ad>
+ This configuration option is defined for the autogroup overlay.
+ It may have multiple occurrences, and it must appear after the
+ overlay directive.
+
+ The value <group-oc> is the name of the objectClass that represents
+ the group.
+
+ The value <URL-ad> is the name of the attributeDescription that
+ contains the URI that is converted to the filters. If no URI is
+ present, there will be no members in that group. It must be a subtype
+ of labeledURI.
+
+ The value <member-ad> is the name of the attributeDescription that
+ specifies the member attribute. User modification of this attribute
+ is disabled for consistency.
+
+ autogroup-memberof-ad <memberof-ad>
+ This configuration option is defined for the autogroup overlay.
+
+ It defines the attribute that is used by the memberOf overlay
+ to store the names of groups that an entry is member of; it must be
+ DN-valued. It should be set to the same value as
+ memberof-memberof-ad. It defaults to 'memberOf'.
+
+
+EXAMPLE
+ ### slapd.conf
+ include /path/to/dyngroup.schema
+ # ...
+ moduleload /path/to/autogroup.so
+ # ...
+
+ database <database>
+ # ...
+
+ overlay autogroup
+ autogroup-attrset groupOfURLs memberURL member
+ ### end slapd.conf
+
+ ### slapd.conf
+ include /path/to/dyngroup.schema
+ # ...
+ moduleload /path/to/autogroup.so
+ moduleload /path/to/memberof.so
+ # ...
+
+ database <database>
+ #...
+
+ overlay memberof
+ memberof-memberof-ad foo
+
+ overlay autogroup
+ autogroup-attrset groupOfURLs memberURL member
+ autogroup-memberof-ad foo
+ ### end slapd.conf
+
+CAVEATS
+ As with static groups, update operations on groups with a large number
+ of members may be slow.
+ If the attribute part of the URI is specified, modify and delete operations
+ are more difficult to handle. In these cases the overlay will try to detect
+ if groups have been modified and then simply refresh them. This can cause
+ performance hits if the search specified by the URI deals with a significant
+ number of entries.
+
+ACKNOWLEDGEMENTS
+ This module was originally written in 2007 by Michał Szulczyński. Further
+ enhancements were contributed by Howard Chu, Raphael Ouazana,
+ Norbert Pueschel, and Christian Manal.
+
+---
+Copyright 1998-2022 The OpenLDAP Foundation.
+Portions Copyright (C) 2007 Michał Szulczyński.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+http://www.OpenLDAP.org/license.html.
diff --git a/contrib/slapd-modules/autogroup/autogroup.c b/contrib/slapd-modules/autogroup/autogroup.c
new file mode 100644
index 0000000..cbcedfe
--- /dev/null
+++ b/contrib/slapd-modules/autogroup/autogroup.c
@@ -0,0 +1,2236 @@
+/* autogroup.c - automatic group overlay */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2007 Michał Szulczyński.
+ * Portions Copyright 2009 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Michał Szulczyński for inclusion in
+ * OpenLDAP Software. Additional significant contributors include:
+ * Howard Chu
+ * Raphael Ouazana
+ * Norbert Pueschel
+ * Christian Manal
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#ifndef SLAPD_MEMBEROF_ATTR
+#define SLAPD_MEMBEROF_ATTR "memberOf"
+#endif
+
+static slap_overinst autogroup;
+
+/* Filter represents the memberURL of a group. */
+typedef struct autogroup_filter_t {
+ struct berval agf_dn; /* The base DN in memberURL */
+ struct berval agf_ndn;
+ struct berval agf_filterstr;
+ Filter *agf_filter;
+ int agf_scope;
+ AttributeName *agf_anlist;
+ struct autogroup_filter_t *agf_next;
+} autogroup_filter_t;
+
+/* Description of group attributes. */
+typedef struct autogroup_def_t {
+ ObjectClass *agd_oc;
+ AttributeDescription *agd_member_url_ad;
+ AttributeDescription *agd_member_ad;
+ struct autogroup_def_t *agd_next;
+} autogroup_def_t;
+
+/* Represents the group entry. */
+typedef struct autogroup_entry_t {
+ BerValue age_dn;
+ BerValue age_ndn;
+ autogroup_filter_t *age_filter; /* List of filters made from memberURLs */
+ autogroup_def_t *age_def; /* Attribute definition */
+ ldap_pvt_thread_mutex_t age_mutex;
+ int age_mustrefresh; /* Defined in request to refresh in response */
+ int age_modrdn_olddnmodified; /* Defined in request to refresh in response */
+ struct autogroup_entry_t *age_next;
+} autogroup_entry_t;
+
+/* Holds pointers to attribute definitions and groups. */
+typedef struct autogroup_info_t {
+ autogroup_def_t *agi_def; /* Group attributes definitions. */
+ autogroup_entry_t *agi_entry; /* Group entries. */
+ AttributeDescription *agi_memberof_ad; /* memberOf attribute description */
+ ldap_pvt_thread_mutex_t agi_mutex;
+} autogroup_info_t;
+
+/* Search callback for adding groups initially. */
+typedef struct autogroup_sc_t {
+ autogroup_info_t *ags_info; /* Group definitions and entries. */
+ autogroup_def_t *ags_def; /* Attributes definition of the group being added. */
+} autogroup_sc_t;
+
+/* Used for adding members, found when searching, to a group. */
+typedef struct autogroup_ga_t {
+ autogroup_entry_t *agg_group; /* The group to which the members will be added. */
+ autogroup_filter_t *agg_filter; /* Current filter */
+ Entry *agg_entry; /* Used in autogroup_member_search_cb to modify
+ this entry with the search results. */
+
+ Modifications *agg_mod; /* Used in autogroup_member_search_modify_cb to hold the
+ search results which will be added to the group. */
+
+ Modifications *agg_mod_last; /* Used in autogroup_member_search_modify_cb so we don't
+ have to search for the last mod added. */
+} autogroup_ga_t;
+
+
+/*
+** dn, ndn - the DN of the member to add
+** age - the group to which the member DN will be added
+*/
+static int
+autogroup_add_member_to_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ Modifications *modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
+ SlapReply sreply = {REP_RESULT};
+ BerValue *vals, *nvals;
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+ Operation o = *op;
+ unsigned long opid = op->o_opid;
+ OpExtra oex;
+
+ assert( dn != NULL );
+ assert( ndn != NULL );
+ Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_to_group adding <%s> to <%s>\n",
+ dn->bv_val, age->age_dn.bv_val );
+
+ vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
+ nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
+ ber_dupbv( vals, dn );
+ BER_BVZERO( &vals[ 1 ] );
+ ber_dupbv( nvals, ndn );
+ BER_BVZERO( &nvals[ 1 ] );
+
+ modlist->sml_op = LDAP_MOD_ADD;
+ modlist->sml_desc = age->age_def->agd_member_ad;
+ modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
+ modlist->sml_values = vals;
+ modlist->sml_nvalues = nvals;
+ modlist->sml_numvals = 1;
+ modlist->sml_flags = SLAP_MOD_INTERNAL;
+ modlist->sml_next = NULL;
+
+ o.o_opid = 0; /* shared with op, saved above */
+ o.o_tag = LDAP_REQ_MODIFY;
+ o.o_callback = &cb;
+ o.orm_modlist = modlist;
+ o.o_dn = op->o_bd->be_rootdn;
+ o.o_ndn = op->o_bd->be_rootndn;
+ o.o_req_dn = age->age_dn;
+ o.o_req_ndn = age->age_ndn;
+ o.o_permissive_modify = 1;
+ o.o_dont_replicate = 1;
+ o.orm_no_opattrs = 1;
+ o.o_managedsait = SLAP_CONTROL_CRITICAL;
+ o.o_relax = SLAP_CONTROL_CRITICAL;
+
+ oex.oe_key = (void *)&autogroup;
+ LDAP_SLIST_INSERT_HEAD( &o.o_extra, &oex, oe_next );
+
+ o.o_bd->bd_info = (BackendInfo *)on->on_info;
+ (void)op->o_bd->be_modify( &o, &sreply );
+ o.o_bd->bd_info = (BackendInfo *)on;
+
+ LDAP_SLIST_REMOVE( &o.o_extra, &oex, OpExtra, oe_next );
+
+ slap_mods_free( modlist, 1 );
+ op->o_opid = opid;
+
+ return sreply.sr_err;
+}
+
+/*
+** e - the entry where to get the attribute values
+** age - the group to which the values will be added
+*/
+static int
+autogroup_add_member_values_to_group( Operation *op, struct berval *dn, autogroup_entry_t *age, Attribute *attr )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ Modifications modlist;
+ SlapReply sreply = {REP_RESULT};
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+ Operation o = *op;
+ unsigned long opid = op->o_opid;
+ OpExtra oex;
+
+ Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_values_to_group adding <%s> to <%s>\n",
+ dn->bv_val, age->age_dn.bv_val );
+
+ modlist.sml_op = LDAP_MOD_ADD;
+ modlist.sml_desc = age->age_def->agd_member_ad;
+ modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
+ modlist.sml_values = attr->a_vals;
+ modlist.sml_nvalues = attr->a_nvals;
+ modlist.sml_numvals = attr->a_numvals;
+ modlist.sml_flags = SLAP_MOD_INTERNAL;
+ modlist.sml_next = NULL;
+
+ o.o_opid = 0;
+ o.o_tag = LDAP_REQ_MODIFY;
+ o.o_callback = &cb;
+ o.orm_modlist = &modlist;
+ o.o_dn = op->o_bd->be_rootdn;
+ o.o_ndn = op->o_bd->be_rootndn;
+ o.o_req_dn = age->age_dn;
+ o.o_req_ndn = age->age_ndn;
+ o.o_permissive_modify = 1;
+ o.o_dont_replicate = 1;
+ o.orm_no_opattrs = 1;
+ o.o_managedsait = SLAP_CONTROL_CRITICAL;
+ o.o_relax = SLAP_CONTROL_CRITICAL;
+
+ oex.oe_key = (void *)&autogroup;
+ LDAP_SLIST_INSERT_HEAD( &o.o_extra, &oex, oe_next );
+
+ o.o_bd->bd_info = (BackendInfo *)on->on_info;
+ (void)op->o_bd->be_modify( &o, &sreply );
+ o.o_bd->bd_info = (BackendInfo *)on;
+ op->o_opid = opid;
+ LDAP_SLIST_REMOVE( &o.o_extra, &oex, OpExtra, oe_next );
+
+ return sreply.sr_err;
+}
+
+/*
+** dn,ndn - the DN to be deleted
+** age - the group from which the DN will be deleted
+** If we pass a NULL dn and ndn, all members are deleted from the group.
+*/
+static int
+autogroup_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ Modifications *modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
+ SlapReply sreply = {REP_RESULT};
+ BerValue *vals, *nvals;
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+ Operation o = *op;
+ unsigned long opid = op->o_opid;
+ OpExtra oex;
+
+ if ( dn == NULL || ndn == NULL ) {
+ Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing all members from <%s>\n",
+ age->age_dn.bv_val );
+
+ modlist->sml_values = NULL;
+ modlist->sml_nvalues = NULL;
+ modlist->sml_numvals = 0;
+ } else {
+ Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing <%s> from <%s>\n",
+ dn->bv_val, age->age_dn.bv_val );
+
+ vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
+ nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
+ ber_dupbv( vals, dn );
+ BER_BVZERO( &vals[ 1 ] );
+ ber_dupbv( nvals, ndn );
+ BER_BVZERO( &nvals[ 1 ] );
+
+ modlist->sml_values = vals;
+ modlist->sml_nvalues = nvals;
+ modlist->sml_numvals = 1;
+ }
+
+
+ modlist->sml_op = LDAP_MOD_DELETE;
+ modlist->sml_desc = age->age_def->agd_member_ad;
+ modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
+ modlist->sml_flags = SLAP_MOD_INTERNAL;
+ modlist->sml_next = NULL;
+
+ o.o_opid = 0;
+ o.o_callback = &cb;
+ o.o_tag = LDAP_REQ_MODIFY;
+ o.orm_modlist = modlist;
+ o.o_dn = op->o_bd->be_rootdn;
+ o.o_ndn = op->o_bd->be_rootndn;
+ o.o_req_dn = age->age_dn;
+ o.o_req_ndn = age->age_ndn;
+ o.o_relax = SLAP_CONTROL_CRITICAL;
+ o.o_managedsait = SLAP_CONTROL_CRITICAL;
+ o.o_permissive_modify = 1;
+ o.o_dont_replicate = 1;
+ o.orm_no_opattrs = 1;
+
+ oex.oe_key = (void *)&autogroup;
+ LDAP_SLIST_INSERT_HEAD( &o.o_extra, &oex, oe_next );
+
+ o.o_bd->bd_info = (BackendInfo *)on->on_info;
+ (void)op->o_bd->be_modify( &o, &sreply );
+ o.o_bd->bd_info = (BackendInfo *)on;
+
+ LDAP_SLIST_REMOVE( &o.o_extra, &oex, OpExtra, oe_next );
+
+ slap_mods_free( modlist, 1 );
+
+ op->o_opid = opid;
+ return sreply.sr_err;
+}
+
+/*
+** e - the entry where to get the attribute values
+** age - the group from which the values will be deleted
+*/
+static int
+autogroup_delete_member_values_from_group( Operation *op, struct berval *dn, autogroup_entry_t *age, Attribute *attr )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ Modifications modlist;
+ SlapReply sreply = {REP_RESULT};
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+ Operation o = *op;
+ unsigned long opid = op->o_opid;
+ OpExtra oex;
+
+ Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_values_from_group removing <%s> from <%s>\n",
+ dn->bv_val, age->age_dn.bv_val );
+
+ modlist.sml_op = LDAP_MOD_DELETE;
+ modlist.sml_desc = age->age_def->agd_member_ad;
+ modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
+ modlist.sml_values = attr->a_vals;
+ modlist.sml_nvalues = attr->a_nvals;
+ modlist.sml_numvals = attr->a_numvals;
+ modlist.sml_flags = SLAP_MOD_INTERNAL;
+ modlist.sml_next = NULL;
+
+ o.o_opid = 0;
+ o.o_tag = LDAP_REQ_MODIFY;
+ o.o_callback = &cb;
+ o.orm_modlist = &modlist;
+ o.o_dn = op->o_bd->be_rootdn;
+ o.o_ndn = op->o_bd->be_rootndn;
+ o.o_req_dn = age->age_dn;
+ o.o_req_ndn = age->age_ndn;
+ o.o_permissive_modify = 1;
+ o.o_dont_replicate = 1;
+ o.orm_no_opattrs = 1;
+ o.o_managedsait = SLAP_CONTROL_CRITICAL;
+ o.o_relax = SLAP_CONTROL_CRITICAL;
+
+ oex.oe_key = (void *)&autogroup;
+ LDAP_SLIST_INSERT_HEAD( &o.o_extra, &oex, oe_next );
+
+ o.o_bd->bd_info = (BackendInfo *)on->on_info;
+ (void)op->o_bd->be_modify( &o, &sreply );
+ o.o_bd->bd_info = (BackendInfo *)on;
+ op->o_opid = opid;
+
+ LDAP_SLIST_REMOVE( &o.o_extra, &oex, OpExtra, oe_next );
+
+ return sreply.sr_err;
+}
+
+/*
+** Callback used to add entries to a group,
+** which are going to be written in the database
+** (used in bi_op_add)
+** The group is passed in autogroup_ga_t->agg_group
+*/
+static int
+autogroup_member_search_cb( Operation *op, SlapReply *rs )
+{
+ assert( op->o_tag == LDAP_REQ_SEARCH );
+
+ if ( rs->sr_type == REP_SEARCH ) {
+ autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
+ autogroup_entry_t *age = agg->agg_group;
+ autogroup_filter_t *agf = agg->agg_filter;
+ Modification mod;
+ const char *text = NULL;
+ char textbuf[1024];
+ struct berval *vals, *nvals;
+ struct berval lvals[ 2 ], lnvals[ 2 ];
+ int numvals;
+
+ Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_cb <%s>\n",
+ rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN" );
+
+ if ( agf->agf_anlist ) {
+ Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
+ if (attr) {
+ vals = attr->a_vals;
+ nvals = attr->a_nvals;
+ numvals = attr->a_numvals;
+ } else {
+ // Nothing to add
+ return 0;
+ }
+ } else {
+ lvals[ 0 ] = rs->sr_entry->e_name;
+ BER_BVZERO( &lvals[ 1 ] );
+ lnvals[ 0 ] = rs->sr_entry->e_nname;
+ BER_BVZERO( &lnvals[ 1 ] );
+ vals = lvals;
+ nvals = lnvals;
+ numvals = 1;
+ }
+
+ mod.sm_op = LDAP_MOD_ADD;
+ mod.sm_desc = age->age_def->agd_member_ad;
+ mod.sm_type = age->age_def->agd_member_ad->ad_cname;
+ mod.sm_values = vals;
+ mod.sm_nvalues = nvals;
+ mod.sm_numvals = numvals;
+
+ modify_add_values( agg->agg_entry, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
+ }
+
+ return 0;
+}
+
+/*
+** Callback used to add entries to a group, which is already in the database.
+** (used in on_response)
+** The group is passed in autogroup_ga_t->agg_group
+** NOTE: Very slow.
+*/
+static int
+autogroup_member_search_modify_cb( Operation *op, SlapReply *rs )
+{
+ assert( op->o_tag == LDAP_REQ_SEARCH );
+
+ if ( rs->sr_type == REP_SEARCH ) {
+ autogroup_ga_t *agg = (autogroup_ga_t *)op->o_callback->sc_private;
+ autogroup_entry_t *age = agg->agg_group;
+ autogroup_filter_t *agf = agg->agg_filter;
+ Modifications *modlist;
+ struct berval *vals, *nvals;
+ struct berval lvals[ 2 ], lnvals[ 2 ];
+ int numvals;
+
+ Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_modify_cb <%s>\n",
+ rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN" );
+
+ if ( agf->agf_anlist ) {
+ Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
+ if (attr) {
+ vals = attr->a_vals;
+ nvals = attr->a_nvals;
+ numvals = attr->a_numvals;
+ } else {
+ // Nothing to add
+ return 0;
+ }
+ } else {
+ lvals[ 0 ] = rs->sr_entry->e_name;
+ BER_BVZERO( &lvals[ 1 ] );
+ lnvals[ 0 ] = rs->sr_entry->e_nname;
+ BER_BVZERO( &lnvals[ 1 ] );
+ vals = lvals;
+ nvals = lnvals;
+ numvals = 1;
+ }
+
+ if ( numvals ) {
+ modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
+
+ modlist->sml_op = LDAP_MOD_ADD;
+ modlist->sml_desc = age->age_def->agd_member_ad;
+ modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
+
+ ber_bvarray_dup_x( &modlist->sml_values, vals, NULL );
+ ber_bvarray_dup_x( &modlist->sml_nvalues, nvals, NULL );
+ modlist->sml_numvals = numvals;
+
+ modlist->sml_flags = SLAP_MOD_INTERNAL;
+ modlist->sml_next = NULL;
+
+ if ( agg->agg_mod == NULL ) {
+ agg->agg_mod = modlist;
+ agg->agg_mod_last = modlist;
+ } else {
+ agg->agg_mod_last->sml_next = modlist;
+ agg->agg_mod_last = modlist;
+ }
+ }
+
+ }
+
+ return 0;
+}
+
+
+/*
+** Adds all entries matching the passed filter to the specified group.
+** If modify == 1, then we modify the group's entry in the database using be_modify.
+** If modify == 0, then, we must supply a rw entry for the group,
+** because we only modify the entry, without calling be_modify.
+** e - the group entry, to which the members will be added
+** age - the group
+** agf - the filter
+*/
+static int
+autogroup_add_members_from_filter( Operation *op, Entry *e, autogroup_entry_t *age, autogroup_filter_t *agf, int modify)
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ Operation o = *op;
+ SlapReply rs = { REP_SEARCH };
+ slap_callback cb = { 0 };
+ slap_callback null_cb = { NULL, slap_null_cb, NULL, NULL };
+ autogroup_ga_t agg;
+ OpExtra oex;
+
+ Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_members_from_filter <%s>\n",
+ age->age_dn.bv_val );
+
+ o.ors_attrsonly = 0;
+ o.o_tag = LDAP_REQ_SEARCH;
+
+ o.o_dn = op->o_bd->be_rootdn;
+ o.o_ndn = op->o_bd->be_rootndn;
+ o.o_req_dn = agf->agf_dn;
+ o.o_req_ndn = agf->agf_ndn;
+
+ o.ors_filterstr = agf->agf_filterstr;
+ o.ors_filter = agf->agf_filter;
+
+ o.ors_scope = agf->agf_scope;
+ o.ors_deref = LDAP_DEREF_NEVER;
+ o.ors_limit = NULL;
+ o.ors_tlimit = SLAP_NO_LIMIT;
+ o.ors_slimit = SLAP_NO_LIMIT;
+ o.ors_attrs = agf->agf_anlist ? agf->agf_anlist : slap_anlist_no_attrs;
+ o.o_do_not_cache = 1;
+
+ agg.agg_group = age;
+ agg.agg_filter = agf;
+ agg.agg_mod = NULL;
+ agg.agg_mod_last = NULL;
+ agg.agg_entry = e;
+ cb.sc_private = &agg;
+
+ if ( modify == 1 ) {
+ cb.sc_response = autogroup_member_search_modify_cb;
+ } else {
+ cb.sc_response = autogroup_member_search_cb;
+ }
+
+ cb.sc_cleanup = NULL;
+ cb.sc_next = NULL;
+
+ o.o_callback = &cb;
+
+ o.o_bd->bd_info = (BackendInfo *)on->on_info;
+ op->o_bd->be_search( &o, &rs );
+ o.o_bd->bd_info = (BackendInfo *)on;
+
+ if ( modify == 1 && agg.agg_mod ) {
+ unsigned long opid = op->o_opid;
+
+ rs_reinit( &rs, REP_RESULT );
+
+ o = *op;
+ o.o_opid = 0;
+ o.o_callback = &null_cb;
+ o.o_tag = LDAP_REQ_MODIFY;
+ o.orm_modlist = agg.agg_mod;
+ o.o_dn = op->o_bd->be_rootdn;
+ o.o_ndn = op->o_bd->be_rootndn;
+ o.o_req_dn = age->age_dn;
+ o.o_req_ndn = age->age_ndn;
+ o.o_relax = SLAP_CONTROL_CRITICAL;
+ o.o_managedsait = SLAP_CONTROL_NONCRITICAL;
+ o.o_permissive_modify = 1;
+ o.o_dont_replicate = 1;
+ o.orm_no_opattrs = 1;
+
+ oex.oe_key = (void *)&autogroup;
+ LDAP_SLIST_INSERT_HEAD( &o.o_extra, &oex, oe_next );
+
+ o.o_bd->bd_info = (BackendInfo *)on->on_info;
+ (void)op->o_bd->be_modify( &o, &rs );
+ o.o_bd->bd_info = (BackendInfo *)on;
+
+ LDAP_SLIST_REMOVE( &o.o_extra, &oex, OpExtra, oe_next );
+
+ slap_mods_free(agg.agg_mod, 1);
+ op->o_opid = opid;
+ }
+
+ return 0;
+}
+
+/*
+** Adds a group to the internal list from the passed entry.
+** scan specifies whether to add all matching members to the group.
+** modify specifies whether to modify the given group entry (when modify == 0),
+** or to modify the group entry in the database (when modify == 1 and e = NULL and ndn != NULL).
+** agi - pointer to the groups and the attribute definitions
+** agd - the attribute definition of the added group
+** e - the entry representing the group, can be NULL if the ndn is specified, and modify == 1
+** ndn - the DN of the group, can be NULL if we give a non-NULL e
+*/
+static int
+autogroup_add_group( Operation *op, autogroup_info_t *agi, autogroup_def_t *agd, Entry *e, BerValue *ndn, int scan, int modify)
+{
+ autogroup_entry_t **agep = &agi->agi_entry;
+ autogroup_filter_t *agf, *agf_prev = NULL;
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ LDAPURLDesc *lud = NULL;
+ Attribute *a;
+ BerValue *bv, dn;
+ int rc = 0, match = 1, null_entry = 0;
+
+ if ( e == NULL ) {
+ if ( overlay_entry_get_ov( op, ndn, NULL, NULL, 0, &e, on ) !=
+ LDAP_SUCCESS || e == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot get entry for <%s>\n", ndn->bv_val );
+ return 1;
+ }
+
+ null_entry = 1;
+ }
+
+ Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_group <%s>\n",
+ e->e_name.bv_val );
+
+ if ( agi->agi_entry != NULL ) {
+ for ( ; *agep ; agep = &(*agep)->age_next ) {
+ dnMatch( &match, 0, NULL, NULL, &e->e_nname, &(*agep)->age_ndn );
+ if ( match == 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group already exists: <%s>\n", e->e_name.bv_val );
+ return 1;
+ }
+ /* goto last */;
+ }
+ }
+
+
+ *agep = (autogroup_entry_t *)ch_calloc( 1, sizeof( autogroup_entry_t ) );
+ ldap_pvt_thread_mutex_init( &(*agep)->age_mutex );
+ (*agep)->age_def = agd;
+ (*agep)->age_filter = NULL;
+ (*agep)->age_mustrefresh = 0;
+ (*agep)->age_modrdn_olddnmodified = 0;
+
+ ber_dupbv( &(*agep)->age_dn, &e->e_name );
+ ber_dupbv( &(*agep)->age_ndn, &e->e_nname );
+
+ a = attrs_find( e->e_attrs, agd->agd_member_url_ad );
+
+ if ( null_entry == 1 ) {
+ a = attrs_dup( a );
+ overlay_entry_release_ov( op, e, 0, on );
+ }
+
+ if( a == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group has no memberURL\n" );
+ } else {
+ for ( bv = a->a_nvals; !BER_BVISNULL( bv ); bv++ ) {
+
+ agf = (autogroup_filter_t*)ch_calloc( 1, sizeof( autogroup_filter_t ) );
+
+ if ( ldap_url_parse( bv->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot parse url <%s>\n", bv->bv_val );
+ /* FIXME: error? */
+ ch_free( agf );
+ continue;
+ }
+
+ agf->agf_scope = lud->lud_scope;
+
+ if ( lud->lud_dn == NULL ) {
+ BER_BVSTR( &dn, "" );
+ } else {
+ ber_str2bv( lud->lud_dn, 0, 0, &dn );
+ }
+
+ rc = dnPrettyNormal( NULL, &dn, &agf->agf_dn, &agf->agf_ndn, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot normalize DN <%s>\n", dn.bv_val );
+ /* FIXME: error? */
+ goto cleanup;
+ }
+
+ if ( lud->lud_filter != NULL ) {
+ ber_str2bv( lud->lud_filter, 0, 1, &agf->agf_filterstr);
+ agf->agf_filter = str2filter( lud->lud_filter );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: URL filter is missing <%s>\n", bv->bv_val );
+ /* FIXME: error? */
+ goto cleanup;
+ }
+
+ if ( lud->lud_attrs != NULL ) {
+ int i;
+
+ for ( i=0 ; lud->lud_attrs[i]!=NULL ; i++) {
+ /* Just counting */;
+ }
+
+ if ( i > 1 ) {
+ Debug( LDAP_DEBUG_ANY, "autogroup_add_group: too many attributes specified in url <%s>\n",
+ bv->bv_val );
+ /* FIXME: error? */
+ filter_free( agf->agf_filter );
+ ch_free( agf->agf_filterstr.bv_val );
+ ch_free( agf->agf_dn.bv_val );
+ ch_free( agf->agf_ndn.bv_val );
+ ldap_free_urldesc( lud );
+ ch_free( agf );
+ continue;
+ }
+
+ agf->agf_anlist = str2anlist( NULL, lud->lud_attrs[0], "," );
+
+ if ( agf->agf_anlist == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "autogroup_add_group: unable to find AttributeDescription \"%s\".\n",
+ lud->lud_attrs[0] );
+ /* FIXME: error? */
+ filter_free( agf->agf_filter );
+ ch_free( agf->agf_filterstr.bv_val );
+ ch_free( agf->agf_dn.bv_val );
+ ch_free( agf->agf_ndn.bv_val );
+ ldap_free_urldesc( lud );
+ ch_free( agf );
+ continue;
+ }
+ }
+
+ agf->agf_next = NULL;
+
+ if( (*agep)->age_filter == NULL ) {
+ (*agep)->age_filter = agf;
+ }
+
+ if( agf_prev != NULL ) {
+ agf_prev->agf_next = agf;
+ }
+
+ agf_prev = agf;
+
+ if ( scan == 1 ){
+ autogroup_add_members_from_filter( op, e, (*agep), agf, modify );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: added memberURL DN <%s> with filter <%s>\n",
+ agf->agf_ndn.bv_val, agf->agf_filterstr.bv_val );
+
+ ldap_free_urldesc( lud );
+
+ continue;
+
+
+cleanup:;
+
+ ch_free( agf->agf_ndn.bv_val );
+ ch_free( agf->agf_dn.bv_val );
+ ldap_free_urldesc( lud );
+ ch_free( agf );
+ }
+ }
+
+ if ( null_entry == 1 ) {
+ attrs_free( a );
+ }
+ return rc;
+}
+
+/*
+** Used when opening the database to add all existing
+** groups from the database to our internal list.
+*/
+static int
+autogroup_group_add_cb( Operation *op, SlapReply *rs )
+{
+ assert( op->o_tag == LDAP_REQ_SEARCH );
+
+ if ( rs->sr_type == REP_SEARCH ) {
+ autogroup_sc_t *ags = (autogroup_sc_t *)op->o_callback->sc_private;
+
+ Debug(LDAP_DEBUG_TRACE, "==> autogroup_group_add_cb <%s>\n",
+ rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN" );
+
+ autogroup_add_group( op, ags->ags_info, ags->ags_def, rs->sr_entry, NULL, 0, 0);
+ }
+
+ return 0;
+}
+
+typedef struct ag_addinfo {
+ slap_overinst *on;
+ Entry *e;
+ autogroup_def_t *agd;
+} ag_addinfo;
+
+static int
+autogroup_add_entry_cb( Operation *op, SlapReply *rs )
+{
+ slap_callback *sc = op->o_callback;
+ ag_addinfo *aa = sc->sc_private;
+ slap_overinst *on = aa->on;
+ autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
+ BackendInfo *bi = op->o_bd->bd_info;
+
+ if ( rs->sr_err != LDAP_SUCCESS )
+ goto done;
+
+ op->o_bd->bd_info = (BackendInfo *)on;
+ ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
+ if ( aa->agd ) {
+ autogroup_add_group( op, agi, aa->agd, aa->e, NULL, 1 , 0);
+ } else {
+ autogroup_entry_t *age;
+ autogroup_filter_t *agf;
+ struct berval odn, ondn;
+ int rc;
+
+ /* must use rootdn when calling test_filter */
+ odn = op->o_dn;
+ ondn = op->o_ndn;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ for ( age = agi->agi_entry; age ; age = age->age_next ) {
+ ldap_pvt_thread_mutex_lock( &age->age_mutex );
+
+ /* Check if any of the filters are the suffix to the entry DN.
+ If yes, we can test that filter against the entry. */
+
+ for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
+ if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
+ rc = test_filter( op, aa->e, agf->agf_filter );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ if ( agf->agf_anlist ) {
+ Attribute *a = attr_find( aa->e->e_attrs, agf->agf_anlist[0].an_desc );
+ if ( a )
+ autogroup_add_member_values_to_group( op, &op->o_req_dn, age, a );
+ } else {
+ autogroup_add_member_to_group( op, &aa->e->e_name, &aa->e->e_nname, age );
+ }
+ break;
+ }
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ }
+ op->o_dn = odn;
+ op->o_ndn = ondn;
+ }
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+
+ op->o_bd->bd_info = bi;
+
+done:
+ op->o_callback = sc->sc_next;
+ op->o_tmpfree( sc, op->o_tmpmemctx );
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+** When adding a group, we first strip any existing members,
+** and add all which match the filters ourselves.
+*/
+static int
+autogroup_add_entry( Operation *op, SlapReply *rs)
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
+ autogroup_def_t *agd = agi->agi_def;
+ slap_callback *sc = NULL;
+ ag_addinfo *aa = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n",
+ op->ora_e->e_name.bv_val );
+
+ sc = op->o_tmpcalloc( sizeof(slap_callback) + sizeof(ag_addinfo), 1, op->o_tmpmemctx );
+ sc->sc_private = (sc+1);
+ sc->sc_response = autogroup_add_entry_cb;
+ aa = sc->sc_private;
+ aa->on = on;
+ aa->e = op->ora_e;
+ sc->sc_next = op->o_callback;
+ op->o_callback = sc;
+
+ /* Check if it's a group. */
+ for ( ; agd ; agd = agd->agd_next ) {
+ if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
+ Modification mod;
+ const char *text = NULL;
+ char textbuf[1024];
+
+ mod.sm_op = LDAP_MOD_DELETE;
+ mod.sm_desc = agd->agd_member_ad;
+ mod.sm_type = agd->agd_member_ad->ad_cname;
+ mod.sm_values = NULL;
+ mod.sm_nvalues = NULL;
+
+ /* We don't want any member attributes added by the user. */
+ modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
+
+ aa->agd = agd;
+
+ break;
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+** agi - internal group and attribute definitions list
+** e - the group to remove from the internal list
+*/
+static int
+autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
+{
+ autogroup_entry_t *age = agi->agi_entry,
+ *age_prev = NULL,
+ *age_next;
+ int rc = 1;
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n",
+ age->age_dn.bv_val );
+
+ for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
+ age_next = age->age_next;
+
+ if ( age == e ) {
+ autogroup_filter_t *agf = age->age_filter,
+ *agf_next;
+
+ if ( age_prev != NULL ) {
+ age_prev->age_next = age_next;
+ } else {
+ agi->agi_entry = NULL;
+ }
+
+ ch_free( age->age_dn.bv_val );
+ ch_free( age->age_ndn.bv_val );
+
+ for( agf_next = agf ; agf_next ; agf = agf_next ){
+ agf_next = agf->agf_next;
+
+ filter_free( agf->agf_filter );
+ ch_free( agf->agf_filterstr.bv_val );
+ ch_free( agf->agf_dn.bv_val );
+ ch_free( agf->agf_ndn.bv_val );
+ anlist_free( agf->agf_anlist, 1, NULL );
+ ch_free( agf );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ ldap_pvt_thread_mutex_destroy( &age->age_mutex );
+ ch_free( age );
+
+ rc = 0;
+ return rc;
+
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val );
+
+ return rc;
+
+}
+
+static int
+autogroup_delete_entry( Operation *op, SlapReply *rs)
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
+ autogroup_entry_t *age, *age_prev, *age_next;
+ autogroup_filter_t *agf;
+ Entry *e;
+ int matched_group = 0, rc = 0;
+ struct berval odn, ondn;
+ OpExtra *oex;
+
+ LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
+ if ( oex->oe_key == (void *)&autogroup )
+ return SLAP_CB_CONTINUE;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val );
+
+ ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
+
+ if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
+ LDAP_SUCCESS || e == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* Check if the entry to be deleted is one of our groups. */
+ for ( age_next = agi->agi_entry ; age_next ; age_prev = age ) {
+ age = age_next;
+ ldap_pvt_thread_mutex_lock( &age->age_mutex );
+ age_next = age->age_next;
+
+ if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
+ int match = 1;
+
+ matched_group = 1;
+
+ dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
+
+ if ( match == 0 ) {
+ autogroup_delete_group( agi, age );
+ break;
+ }
+ }
+
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ }
+
+ if ( matched_group == 1 ) {
+ overlay_entry_release_ov( op, e, 0, on );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* Check if the entry matches any of the groups.
+ If yes, we can delete the entry from that group. */
+
+ odn = op->o_dn;
+ ondn = op->o_ndn;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ for ( age = agi->agi_entry ; age ; age = age->age_next ) {
+ ldap_pvt_thread_mutex_lock( &age->age_mutex );
+
+ for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
+ if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
+ rc = test_filter( op, e, agf->agf_filter );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ /* If the attribute is retrieved from the entry, we don't know what to delete
+ ** So the group must be entirely refreshed
+ ** But the refresh can't be done now because the entry is not deleted
+ ** So the group is marked as mustrefresh
+ */
+ if ( agf->agf_anlist ) {
+ age->age_mustrefresh = 1;
+ } else {
+ autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
+ }
+ break;
+ }
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ }
+ op->o_dn = odn;
+ op->o_ndn = ondn;
+
+ overlay_entry_release_ov( op, e, 0, on );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+autogroup_response( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
+ autogroup_def_t *agd = agi->agi_def;
+ autogroup_entry_t *age;
+ autogroup_filter_t *agf;
+ BerValue new_dn, new_ndn, pdn;
+ Entry *e, *group;
+ Attribute *a, *ea, *attrs;
+ int is_olddn, is_newdn, is_value_refresh, dn_equal;
+ OpExtra *oex;
+
+ LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
+ if ( oex->oe_key == (void *)&autogroup )
+ break;
+ }
+
+ /* Handle all cases where a refresh of the group is needed */
+ if ( op->o_tag == LDAP_REQ_DELETE || op->o_tag == LDAP_REQ_MODIFY ) {
+ if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !oex ) {
+
+ ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
+
+ for ( age = agi->agi_entry ; age ; age = age->age_next ) {
+ /* Request detected that the group must be refreshed */
+
+ ldap_pvt_thread_mutex_lock( &age->age_mutex );
+
+ if ( age->age_mustrefresh ) {
+ autogroup_delete_member_from_group( op, NULL, NULL, age) ;
+
+ for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
+ autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
+ }
+ }
+
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ }
+ } else if ( op->o_tag == LDAP_REQ_MODRDN ) {
+ if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !oex ) {
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val );
+
+ ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
+
+ if ( op->oq_modrdn.rs_newSup ) {
+ pdn = *op->oq_modrdn.rs_newSup;
+ } else {
+ dnParent( &op->o_req_dn, &pdn );
+ }
+ build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
+
+ if ( op->oq_modrdn.rs_nnewSup ) {
+ pdn = *op->oq_modrdn.rs_nnewSup;
+ } else {
+ dnParent( &op->o_req_ndn, &pdn );
+ }
+ build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
+
+ Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val );
+
+ dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
+
+ if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
+ LDAP_SUCCESS || e == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+ a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
+
+
+ if ( a == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val );
+ overlay_entry_release_ov( op, e, 0, on );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+
+ /* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
+ for ( ; agd; agd = agd->agd_next ) {
+
+ if ( value_find_ex( slap_schema.si_ad_objectClass,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ a->a_nvals, &agd->agd_oc->soc_cname,
+ op->o_tmpmemctx ) == 0 )
+ {
+ for ( age = agi->agi_entry ; age ; age = age->age_next ) {
+ int match = 1;
+
+ dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
+ if ( match == 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val );
+ ber_dupbv( &age->age_dn, &new_dn );
+ ber_dupbv( &age->age_ndn, &new_ndn );
+
+ op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
+ overlay_entry_release_ov( op, e, 0, on );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+ }
+
+ }
+ }
+
+ /* For each group:
+ 1. check if the original entry's DN is in the group.
+ 2. check if the any of the group filter's base DN is a suffix of the new DN
+
+ If 1 and 2 are both false, we do nothing.
+ If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
+ If 1 is false, and 2 is true, we check the entry against the group's filters,
+ and add it's DN to the group.
+ If 1 is true, and 2 is false, we delete the entry's DN from the group.
+ */
+ attrs = attrs_dup( e->e_attrs );
+ overlay_entry_release_ov( op, e, 0, on );
+ for ( age = agi->agi_entry ; age ; age = age->age_next ) {
+ is_olddn = 0;
+ is_newdn = 0;
+ is_value_refresh = 0;
+
+ ldap_pvt_thread_mutex_lock( &age->age_mutex );
+
+ if ( age->age_filter && age->age_filter->agf_anlist ) {
+ ea = attrs_find( attrs, age->age_filter->agf_anlist[0].an_desc );
+ }
+ else {
+ ea = NULL;
+ }
+
+ if ( age->age_modrdn_olddnmodified ) {
+ /* Request already marked this group to be updated */
+ is_olddn = 1;
+ is_value_refresh = 1;
+ age->age_modrdn_olddnmodified = 0;
+ } else {
+
+ if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
+ LDAP_SUCCESS || group == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val );
+
+ op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
+
+ attrs_free( attrs );
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+ a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
+
+ if ( a != NULL ) {
+ if ( value_find_ex( age->age_def->agd_member_ad,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
+ {
+ is_olddn = 1;
+ }
+
+ }
+
+ overlay_entry_release_ov( op, group, 0, on );
+
+ }
+
+ for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
+ if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
+ /* TODO: should retest filter as it could imply conditions on the dn */
+ is_newdn = 1;
+ break;
+ }
+ }
+
+
+ if ( is_value_refresh ) {
+ if ( is_olddn != is_newdn ) {
+ /* group refresh */
+ autogroup_delete_member_from_group( op, NULL, NULL, age) ;
+
+ for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
+ autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ continue;
+ }
+ if ( is_olddn == 1 && is_newdn == 0 ) {
+ if ( ea )
+ autogroup_delete_member_values_from_group( op, &new_dn, age, ea );
+ else
+ autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
+ } else
+ if ( is_olddn == 0 && is_newdn == 1 ) {
+ Entry etmp;
+ struct berval odn, ondn;
+ etmp.e_name = op->o_req_dn;
+ etmp.e_nname = op->o_req_ndn;
+ etmp.e_attrs = attrs;
+ odn = op->o_dn;
+ ondn = op->o_ndn;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
+ if ( test_filter( op, &etmp, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
+ if ( ea ) {
+ autogroup_add_member_values_to_group( op, &new_dn, age, ea );
+ } else
+ autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
+ break;
+ }
+ }
+ op->o_dn = odn;
+ op->o_ndn = ondn;
+ } else
+ if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
+ if ( ea ) {
+ /* group refresh */
+ autogroup_delete_member_from_group( op, NULL, NULL, age) ;
+
+ for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
+ autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
+ }
+ }
+ else {
+ autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
+ autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
+ }
+ }
+
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ }
+
+ op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
+
+ attrs_free( attrs );
+
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ }
+ }
+
+ if ( op->o_tag == LDAP_REQ_MODIFY ) {
+ if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !oex ) {
+ Entry etmp;
+ struct berval odn, ondn;
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val );
+
+ ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
+
+ if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
+ LDAP_SUCCESS || e == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+ a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
+
+
+ if ( a == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val );
+ overlay_entry_release_ov( op, e, 0, on );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* If we modify a group's memberURL, we have to delete all of it's members,
+ and add them anew, because we cannot tell from which memberURL a member was added. */
+ for ( ; agd; agd = agd->agd_next ) {
+
+ if ( value_find_ex( slap_schema.si_ad_objectClass,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ a->a_nvals, &agd->agd_oc->soc_cname,
+ op->o_tmpmemctx ) == 0 )
+ {
+ Modifications *m;
+ int match = 1;
+
+ m = op->orm_modlist;
+
+ for ( age = agi->agi_entry ; age ; age = age->age_next ) {
+ ldap_pvt_thread_mutex_lock( &age->age_mutex );
+
+ dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
+
+ if ( match == 0 ) {
+ for ( ; m ; m = m->sml_next ) {
+ if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
+ autogroup_def_t *group_agd = age->age_def;
+ Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n",
+ op->o_req_dn.bv_val );
+
+ overlay_entry_release_ov( op, e, 0, on );
+
+ autogroup_delete_member_from_group( op, NULL, NULL, age );
+ autogroup_delete_group( agi, age );
+
+ autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
+
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+ }
+
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ break;
+ }
+
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ }
+
+ overlay_entry_release_ov( op, e, 0, on );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+ }
+
+ /* When modifying any of the attributes of an entry, we must
+ check if the entry is in any of our groups, and if
+ the modified entry matches any of the filters of that group.
+
+ If the entry exists in a group, but the modified attributes do
+ not match any of the group's filters, we delete the entry from that group.
+ If the entry doesn't exist in a group, but matches a filter,
+ we add it to that group.
+ */
+ attrs = attrs_dup( e->e_attrs );
+ overlay_entry_release_ov( op, e, 0, on );
+ etmp.e_name = op->o_req_dn;
+ etmp.e_nname = op->o_req_ndn;
+ etmp.e_attrs = attrs;
+ odn = op->o_dn;
+ ondn = op->o_ndn;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ for ( age = agi->agi_entry ; age ; age = age->age_next ) {
+ is_olddn = 0;
+ is_newdn = 0;
+
+ ldap_pvt_thread_mutex_lock( &age->age_mutex );
+
+ if ( age->age_filter && age->age_filter->agf_anlist ) {
+ ea = attrs_find( attrs, age->age_filter->agf_anlist[0].an_desc );
+ }
+ else {
+ ea = NULL;
+ }
+
+ if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
+ LDAP_SUCCESS || group == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n",
+ age->age_dn.bv_val );
+
+ attrs_free( attrs );
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ op->o_dn = odn;
+ op->o_ndn = ondn;
+ return SLAP_CB_CONTINUE;
+ }
+
+ a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
+
+ if ( a != NULL ) {
+ if ( value_find_ex( age->age_def->agd_member_ad,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 )
+ {
+ is_olddn = 1;
+ }
+
+ }
+
+ overlay_entry_release_ov( op, group, 0, on );
+
+ for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
+ if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
+ if ( test_filter( op, &etmp, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
+ is_newdn = 1;
+ break;
+ }
+ }
+ }
+
+ if ( is_olddn == 1 && is_newdn == 0 ) {
+ if(ea)
+ autogroup_delete_member_values_from_group( op, &op->o_req_dn, age, ea );
+ else
+ autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
+ } else
+ if ( is_olddn == 0 && is_newdn == 1 ) {
+ if(ea)
+ autogroup_add_member_values_to_group( op, &op->o_req_dn, age, ea );
+ else
+ autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &age->age_mutex );
+ }
+
+ op->o_dn = odn;
+ op->o_ndn = ondn;
+ attrs_free( attrs );
+
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+** Detect if filter contains a memberOf check for dn
+*/
+static int
+autogroup_memberOf_filter( Filter *f, BerValue *dn, AttributeDescription *memberof_ad )
+{
+ int result = 0;
+ if ( f == NULL ) return 0;
+
+ switch ( f->f_choice & SLAPD_FILTER_MASK ) {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ for ( f = f->f_un.f_un_complex; f && !result; f = f->f_next ) {
+ result = result || autogroup_memberOf_filter( f, dn, memberof_ad );
+ }
+ break;
+ case LDAP_FILTER_EQUALITY:
+ result = ( f->f_ava->aa_desc == memberof_ad &&
+ ber_bvcmp( &f->f_ava->aa_value, dn ) == 0 );
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+/*
+** When modifying a group, we must deny any modifications to the member attribute,
+** because the group would be inconsistent.
+*/
+static int
+autogroup_modify_entry( Operation *op, SlapReply *rs)
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
+ autogroup_def_t *agd = agi->agi_def;
+ autogroup_entry_t *age;
+ Entry *e;
+ Attribute *a;
+ struct berval odn, ondn;
+ OpExtra *oex;
+
+ LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
+ if ( oex->oe_key == (void *)&autogroup )
+ return SLAP_CB_CONTINUE;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val );
+ ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
+
+ if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
+ LDAP_SUCCESS || e == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+ odn = op->o_dn;
+ ondn = op->o_ndn;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ /* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
+ for ( age = agi->agi_entry; age ; age = age->age_next ) {
+ autogroup_filter_t *agf;
+ for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
+ if ( agf->agf_anlist ) {
+ Modifications *m;
+ for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
+ if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
+ if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
+ int rc = test_filter( op, e, agf->agf_filter );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ age->age_mustrefresh = 1;
+ }
+ }
+ }
+ }
+ }
+
+ if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
+ age->age_mustrefresh = 1;
+ }
+ }
+ }
+ op->o_dn = odn;
+ op->o_ndn = ondn;
+
+ a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
+
+ if ( a == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+
+ for ( ; agd; agd = agd->agd_next ) {
+
+ if ( value_find_ex( slap_schema.si_ad_objectClass,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ a->a_nvals, &agd->agd_oc->soc_cname,
+ op->o_tmpmemctx ) == 0 )
+ {
+ Modifications *m;
+ int match = 1;
+
+ m = op->orm_modlist;
+
+ for ( age = agi->agi_entry ; age ; age = age->age_next ) {
+ dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
+
+ if ( match == 0 ) {
+ for ( ; m ; m = m->sml_next ) {
+ if ( m->sml_desc == age->age_def->agd_member_ad ) {
+ overlay_entry_release_ov( op, e, 0, on );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val );
+ send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ }
+ break;
+ }
+ }
+
+ /* an entry may only have one dynamic group class */
+ break;
+ }
+ }
+
+ overlay_entry_release_ov( op, e, 0, on );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+** Detect if the olddn is part of a group and so if the group should be refreshed
+*/
+static int
+autogroup_modrdn_entry( Operation *op, SlapReply *rs)
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
+ autogroup_entry_t *age;
+ Entry *e;
+ struct berval odn, ondn;
+ OpExtra *oex;
+
+ LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
+ if ( oex->oe_key == (void *)&autogroup )
+ return SLAP_CB_CONTINUE;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val );
+ ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
+
+ if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
+ LDAP_SUCCESS || e == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+ odn = op->o_dn;
+ ondn = op->o_ndn;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ /* Must check if a dn is modified */
+ for ( age = agi->agi_entry; age ; age = age->age_next ) {
+ autogroup_filter_t *agf;
+ for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
+ if ( agf->agf_anlist ) {
+ if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
+ int rc = test_filter( op, e, agf->agf_filter );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ age->age_modrdn_olddnmodified = 1;
+ }
+ }
+ }
+ }
+ }
+ op->o_dn = odn;
+ op->o_ndn = ondn;
+
+ overlay_entry_release_ov( op, e, 0, on );
+ ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+** Builds a filter for searching for the
+** group entries, according to the objectClass.
+*/
+static int
+autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
+{
+ char *ptr;
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n" );
+
+ op->ors_filterstr.bv_len = STRLENOF( "(=)" )
+ + slap_schema.si_ad_objectClass->ad_cname.bv_len
+ + agd->agd_oc->soc_cname.bv_len;
+ ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
+ *ptr++ = '(';
+ ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
+ *ptr++ = '=';
+ ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
+ *ptr++ = ')';
+ *ptr = '\0';
+
+ op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
+
+ assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
+
+ return 0;
+}
+
+enum {
+ AG_ATTRSET = 1,
+ AG_MEMBER_OF_AD,
+ AG_LAST
+};
+
+static ConfigDriver ag_cfgen;
+
+static ConfigTable agcfg[] = {
+ { "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
+ 4, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
+ "( OLcfgCtAt:2.1 NAME ( 'olcAutoGroupAttrSet' 'olcAGattrSet' ) "
+ "DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "X-ORDERED 'VALUES' )",
+ NULL, NULL },
+
+ { "autogroup-memberof-ad", "memberOf attribute",
+ 2, 2, 0, ARG_MAGIC|AG_MEMBER_OF_AD, ag_cfgen,
+ "( OLcfgCtAt:2.2 NAME ( 'olcAutoGroupMemberOfAd' 'olcAGmemberOfAd' ) "
+ "DESC 'memberOf attribute' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )",
+ NULL, NULL },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs agocs[] = {
+ { "( OLcfgCtOc:2.1 "
+ "NAME ( 'olcAutoGroupConfig' 'olcAutomaticGroups' ) "
+ "DESC 'Automatic groups configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( "
+ "olcAutoGroupAttrSet "
+ "$ olcAutoGroupMemberOfAd "
+ ")"
+ ")",
+ Cft_Overlay, agcfg, NULL, NULL },
+ { NULL, 0, NULL }
+};
+
+
+static int
+ag_cfgen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ autogroup_info_t *agi = (autogroup_info_t *)on->on_bi.bi_private;
+ autogroup_def_t *agd;
+ autogroup_entry_t *age;
+
+ int rc = 0, i;
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n" );
+
+ if( agi == NULL ) {
+ agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
+ ldap_pvt_thread_mutex_init( &agi->agi_mutex );
+ agi->agi_def = NULL;
+ agi->agi_entry = NULL;
+ on->on_bi.bi_private = (void *)agi;
+ }
+
+ agd = agi->agi_def;
+ age = agi->agi_entry;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+
+ switch( c->type ){
+ case AG_ATTRSET:
+ for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
+ struct berval bv;
+ char *ptr = c->cr_msg;
+
+ assert(agd->agd_oc != NULL);
+ assert(agd->agd_member_url_ad != NULL);
+ assert(agd->agd_member_ad != NULL);
+
+ ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ SLAP_X_ORDERED_FMT "%s %s %s", i,
+ agd->agd_oc->soc_cname.bv_val,
+ agd->agd_member_url_ad->ad_cname.bv_val,
+ agd->agd_member_ad->ad_cname.bv_val );
+
+ bv.bv_val = c->cr_msg;
+ bv.bv_len = ptr - bv.bv_val;
+ value_add_one ( &c->rvalue_vals, &bv );
+
+ }
+ break;
+
+ case AG_MEMBER_OF_AD:
+ if ( agi->agi_memberof_ad != NULL ){
+ value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
+ }
+ break;
+
+ default:
+ assert( 0 );
+ return 1;
+ }
+
+ return rc;
+
+ }else if ( c->op == LDAP_MOD_DELETE ) {
+ if ( c->valx < 0) {
+ autogroup_def_t *agd_next;
+ autogroup_entry_t *age_next;
+ autogroup_filter_t *agf = age->age_filter,
+ *agf_next;
+
+ for ( agd_next = agd; agd_next; agd = agd_next ) {
+ agd_next = agd->agd_next;
+
+ ch_free( agd );
+ }
+
+ for ( age_next = age ; age_next ; age = age_next ) {
+ age_next = age->age_next;
+
+ ch_free( age->age_dn.bv_val );
+ ch_free( age->age_ndn.bv_val );
+
+ for( agf_next = agf ; agf_next ; agf = agf_next ){
+ agf_next = agf->agf_next;
+
+ filter_free( agf->agf_filter );
+ ch_free( agf->agf_filterstr.bv_val );
+ ch_free( agf->agf_dn.bv_val );
+ ch_free( agf->agf_ndn.bv_val );
+ anlist_free( agf->agf_anlist, 1, NULL );
+ ch_free( agf );
+ }
+
+ ldap_pvt_thread_mutex_init( &age->age_mutex );
+ ch_free( age );
+ }
+
+ ch_free( agi );
+ on->on_bi.bi_private = NULL;
+
+ } else {
+ autogroup_def_t **agdp;
+ autogroup_entry_t *age_next, *age_prev;
+ autogroup_filter_t *agf,
+ *agf_next;
+
+ for ( i = 0, agdp = &agi->agi_def;
+ i < c->valx; i++ )
+ {
+ if ( *agdp == NULL) {
+ return 1;
+ }
+ agdp = &(*agdp)->agd_next;
+ }
+
+ agd = *agdp;
+ *agdp = agd->agd_next;
+
+ for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
+ age_next = age->age_next;
+
+ if( age->age_def == agd ) {
+ agf = age->age_filter;
+
+ ch_free( age->age_dn.bv_val );
+ ch_free( age->age_ndn.bv_val );
+
+ for ( agf_next = agf; agf_next ; agf = agf_next ) {
+ agf_next = agf->agf_next;
+ filter_free( agf->agf_filter );
+ ch_free( agf->agf_filterstr.bv_val );
+ ch_free( agf->agf_dn.bv_val );
+ ch_free( agf->agf_ndn.bv_val );
+ anlist_free( agf->agf_anlist, 1, NULL );
+ ch_free( agf );
+ }
+
+ ldap_pvt_thread_mutex_destroy( &age->age_mutex );
+ ch_free( age );
+
+ age = age_prev;
+
+ if( age_prev != NULL ) {
+ age_prev->age_next = age_next;
+ }
+ }
+ }
+
+ ch_free( agd );
+ agd = agi->agi_def;
+
+ }
+
+ return rc;
+ }
+
+ switch(c->type){
+ case AG_ATTRSET: {
+ autogroup_def_t **agdp,
+ *agd_next = NULL;
+ ObjectClass *oc = NULL;
+ AttributeDescription *member_url_ad = NULL,
+ *member_ad = NULL;
+ const char *text;
+
+
+ oc = oc_find( c->argv[ 1 ] );
+ if( oc == NULL ){
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
+ "unable to find ObjectClass \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+
+ rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
+ if( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
+ "unable to find AttributeDescription \"%s\"",
+ c->argv[ 2 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+ if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
+ "AttributeDescription \"%s\" ",
+ "must be of a subtype \"labeledURI\"",
+ c->argv[ 2 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+ rc = slap_str2ad( c->argv[3], &member_ad, &text );
+ if( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
+ "unable to find AttributeDescription \"%s\"",
+ c->argv[ 3 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+ for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
+ /* The same URL attribute / member attribute pair
+ * cannot be repeated */
+
+ if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
+ "URL attributeDescription \"%s\" already mapped",
+ member_ad->ad_cname.bv_val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+/* return 1; //warning*/
+ }
+ }
+
+ if ( c->valx >= 0 ) {
+ int i;
+
+ for ( i = 0, agdp = &agi->agi_def ;
+ i < c->valx; i++ )
+ {
+ if ( *agdp == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
+ "invalid index {%d}",
+ c->valx );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+
+ return 1;
+ }
+ agdp = &(*agdp)->agd_next;
+ }
+ agd_next = *agdp;
+
+ } else {
+ for ( agdp = &agi->agi_def; *agdp;
+ agdp = &(*agdp)->agd_next )
+ /* goto last */;
+ }
+
+ *agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
+
+ (*agdp)->agd_oc = oc;
+ (*agdp)->agd_member_url_ad = member_url_ad;
+ (*agdp)->agd_member_ad = member_ad;
+ (*agdp)->agd_next = agd_next;
+
+ } break;
+
+ case AG_MEMBER_OF_AD: {
+ AttributeDescription *memberof_ad = NULL;
+ const char *text;
+
+ rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
+ if( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"autogroup-memberof-ad <memberof-ad>\": "
+ "unable to find AttributeDescription \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX ) /* e.g. "member" */
+ && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) ) /* e.g. "uniqueMember" */
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "memberof attribute=\"%s\" must either "
+ "have DN (%s) or nameUID (%s) syntax",
+ c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+ agi->agi_memberof_ad = memberof_ad;
+
+ } break;
+
+ default:
+ rc = 1;
+ break;
+ }
+
+ return rc;
+}
+
+extern int slapMode;
+
+/*
+** Do a search for all the groups in the
+** database, and add them to out internal list.
+*/
+static int
+autogroup_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ autogroup_info_t *agi = on->on_bi.bi_private;
+ autogroup_def_t *agd;
+ autogroup_sc_t ags;
+ Operation *op;
+ slap_callback cb = { 0 };
+
+ void *thrctx = ldap_pvt_thread_pool_context();
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n" );
+
+ if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
+ return 0;
+ }
+
+ connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+ op = &opbuf.ob_op;
+
+ op->ors_attrsonly = 0;
+ op->o_tag = LDAP_REQ_SEARCH;
+ op->o_dn = be->be_rootdn;
+ op->o_ndn = be->be_rootndn;
+
+ op->o_req_dn = be->be_suffix[0];
+ op->o_req_ndn = be->be_nsuffix[0];
+
+ op->ors_scope = LDAP_SCOPE_SUBTREE;
+ op->ors_deref = LDAP_DEREF_NEVER;
+ op->ors_limit = NULL;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_slimit = SLAP_NO_LIMIT;
+ op->ors_attrs = slap_anlist_no_attrs;
+ op->o_do_not_cache = 1;
+
+ op->o_bd = be;
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+
+ ags.ags_info = agi;
+ cb.sc_private = &ags;
+ cb.sc_response = autogroup_group_add_cb;
+ cb.sc_cleanup = NULL;
+ cb.sc_next = NULL;
+
+ op->o_callback = &cb;
+
+ for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
+ SlapReply rs = { REP_RESULT };
+
+ autogroup_build_def_filter(agd, op);
+
+ ags.ags_def = agd;
+
+ op->o_bd->be_search( op, &rs );
+
+ filter_free_x( op, op->ors_filter, 1 );
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ }
+
+ if( ! agi->agi_memberof_ad ){
+ int rc;
+ const char *text = NULL;
+
+ rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
+ "unable to find attribute=\"%s\": %s (%d)\n",
+ SLAPD_MEMBEROF_ATTR, text, rc );
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int
+autogroup_db_close(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n" );
+
+ if ( on->on_bi.bi_private ) {
+ autogroup_info_t *agi = on->on_bi.bi_private;
+ autogroup_entry_t *age = agi->agi_entry,
+ *age_next;
+ autogroup_filter_t *agf, *agf_next;
+
+ for ( age_next = age; age_next; age = age_next ) {
+ age_next = age->age_next;
+
+ ch_free( age->age_dn.bv_val );
+ ch_free( age->age_ndn.bv_val );
+
+ agf = age->age_filter;
+
+ for ( agf_next = agf; agf_next; agf = agf_next ) {
+ agf_next = agf->agf_next;
+
+ filter_free( agf->agf_filter );
+ ch_free( agf->agf_filterstr.bv_val );
+ ch_free( agf->agf_dn.bv_val );
+ ch_free( agf->agf_ndn.bv_val );
+ anlist_free( agf->agf_anlist, 1, NULL );
+ ch_free( agf );
+ }
+
+ ldap_pvt_thread_mutex_destroy( &age->age_mutex );
+ ch_free( age );
+ }
+ }
+
+ return 0;
+}
+
+static int
+autogroup_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+
+ Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n" );
+
+ if ( on->on_bi.bi_private ) {
+ autogroup_info_t *agi = on->on_bi.bi_private;
+ autogroup_def_t *agd = agi->agi_def,
+ *agd_next;
+
+ for ( agd_next = agd; agd_next; agd = agd_next ) {
+ agd_next = agd->agd_next;
+
+ ch_free( agd );
+ }
+
+ ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
+ ch_free( agi );
+ }
+
+ return 0;
+}
+
+static
+int
+autogroup_initialize(void)
+{
+ int rc = 0;
+ autogroup.on_bi.bi_type = "autogroup";
+
+ autogroup.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ autogroup.on_bi.bi_db_open = autogroup_db_open;
+ autogroup.on_bi.bi_db_close = autogroup_db_close;
+ autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
+
+ autogroup.on_bi.bi_op_add = autogroup_add_entry;
+ autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
+ autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
+ autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
+
+ autogroup.on_response = autogroup_response;
+
+ autogroup.on_bi.bi_cf_ocs = agocs;
+
+ rc = config_register_schema( agcfg, agocs );
+ if ( rc ) {
+ return rc;
+ }
+
+ return overlay_register( &autogroup );
+}
+
+int
+init_module( int argc, char *argv[] )
+{
+ return autogroup_initialize();
+}
diff --git a/contrib/slapd-modules/autogroup/slapo-autogroup.5 b/contrib/slapd-modules/autogroup/slapo-autogroup.5
new file mode 100644
index 0000000..4c6414d
--- /dev/null
+++ b/contrib/slapd-modules/autogroup/slapo-autogroup.5
@@ -0,0 +1,116 @@
+.TH SLAPO-AUTOGROUP 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Portions Copyright \[u00A9] 2007 Michał Szulczyński.
+.\" Copying restrictions apply. See the COPYRIGHT file.
+.\" $OpenLDAP$
+.SH NAME
+\FCslapo-autogroup\FT \- automatic updates of group memberships which meet the
+requirements of any filter contained in the group definition.
+.SH SYNOPSIS
+In \FCslapd.conf\FT:
+ ...
+ \FCinclude ETCDIR/schema/dyngroup.schema\FT
+ ...
+ \FCmoduleload autogroup.so\FT
+ ...
+ \FCdatabase\FT ...
+ ...
+ \FCoverlay autogroup\FT
+ \FCautogroup-attrset groupOfURLs memberURL member\FT
+.SH DESCRIPTION
+The
+.B autogroup
+overlay to
+.BR slapd (8)
+allows automated updates of group memberships which meet the requirements
+of any filter contained in the group definition. The filters are built from
+LDAP URI-valued attributes. Any time an object is added/deleted/updated, it is
+tested for compliance with the filters, and its membership is accordingly
+updated. For searches and compares, it behaves like a static group.
+If the attribute part of the URI is filled, the group entry is populated by
+the values of this attribute in the entries resulting from the search.
+.SH CONFIGURATION
+Either
+.BR \FCslapd.conf\FT (5)
+or the \FCcn=config\FT methodology of
+.BR \FCslapd-config\FT (5)
+may be used for configuring autogroup. Both syntaxes are provided
+here for convenience:
+.TP
+.B \FCautogroup-attrset\FT <group-oc> <URL-ad> <member-ad>
+.TP
+.B \FColcAutoGroupAttrSet:\FT <group-oc> <URL-ad> <member-ad>
+This defines the objectclass-attribute-URI mappings defining the
+automatically managed groups, and may appear multiple times.
+
+The value <group-oc> is the name of the objectClass that represents
+the group.
+
+The value <URL-ad> is the name of the attributeDescription that
+contains the URI that is converted to the filters. If no URI is
+present, there will be no members in that group. It must be a subtype
+of labeledURI.
+
+The value <member-ad> is the name of the attributeDescription that
+specifies the member attribute. User modification of this attribute is
+disabled for consistency.
+.TP
+.B \FCautogroup-memberof-ad\FT <memberof-ad>
+.TP
+.B \FColcAutoGroupMemberOfAd\FT <memberof-ad>
+This defines the attribute that is used by the memberOf overlay to
+store the names of groups that an entry is member of; it must be
+DN-valued. It should be set to the same value as
+memberof-memberof-ad. It defaults to 'memberOf'.
+.SH EXAMPLES
+As above in SYNOPSIS, or with memberof:
+
+ ...
+ \FCinclude ETCDIR/schema/dyngroup.schema\FT
+ \FCinclude ETCDIR/schema/memberof.schema\FT
+ ...
+ \FCmoduleload autogroup.so\FT
+ \FCmoduleload memberof.so\FT
+ ...
+ \FCdatabase\FT ...
+ ...
+ \FCoverlay memberof\FT
+ \FCmemberof-memberof-ad\FT foo
+ ...
+ \FCoverlay autogroup\FT
+ \FCautogroup-attrset groupOfURLs memberURL member\FT
+ \FCautogroup-memberof-ad\FT foo
+.SH CAVEATS
+As with static groups, update operations on groups with a large number
+of members may be slow. If the attribute part of the URI is specified,
+modify and delete operations are more difficult to handle. In these
+cases the overlay will try to detect if groups have been modified and
+then simply refresh them. This can cause performance hits if the
+search specified by the URI deals with a significant number of
+entries.
+.SH BACKWARD COMPATIBILITY
+The autogroup overlay has been reworked with the 2.5 release to use
+a consistent namespace as with other overlays. As a side-effect the
+following cn=config parameters are deprecated and will be removed in
+a future release:
+.IP \[bu] 2
+.B olcAGattrSet
+is replaced with olcAutoGroupAttrSet
+.IP \[bu]
+.B olcAGmemberOfAd
+is replaced with olcAutoGroupMemberOfAd
+.IP \[bu]
+.B olcAutomaticGroups
+is replaced with olcAutoGroupConfig
+.SH ACKNOWLEDGEMENTS
+This module was originally written in 2007 by Michał
+Szulczyński. Further enhancements were contributed by Howard
+Chu, Raphael Ouazana, Norbert Pueschel, and Christian Manal. Manpage
+updates provided by Emily Backes.
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd (8).
+.SH Copyrights
+Copyright 1998-2022 The OpenLDAP Foundation.
+Portions Copyright \[u00A9] 2007 Michał Szulczyński.
+All rights reserved.
diff --git a/contrib/slapd-modules/cloak/Makefile b/contrib/slapd-modules/cloak/Makefile
new file mode 100644
index 0000000..c54e6b1
--- /dev/null
+++ b/contrib/slapd-modules/cloak/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_CLOAK=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = cloak.la
+MANPAGES = slapo-cloak.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+cloak.la: cloak.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/cloak/cloak.c b/contrib/slapd-modules/cloak/cloak.c
new file mode 100644
index 0000000..ced7a80
--- /dev/null
+++ b/contrib/slapd-modules/cloak/cloak.c
@@ -0,0 +1,354 @@
+/* cloak.c - Overlay to hide some attribute except if explicitly requested */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 Emmanuel Dreyfus
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by the Emmanuel Dreyfus for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_CLOAK
+
+#include <stdio.h>
+
+#include "ac/string.h"
+#include "ac/socket.h"
+
+#include "lutil.h"
+#include "slap.h"
+#include "slap-config.h"
+
+enum { CLOAK_ATTR = 1 };
+
+typedef struct cloak_info_t {
+ ObjectClass *ci_oc;
+ AttributeDescription *ci_ad;
+ struct cloak_info_t *ci_next;
+} cloak_info_t;
+
+#define CLOAK_USAGE "\"cloak-attr <attr> [<class>]\": "
+
+static int
+cloak_cfgen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ cloak_info_t *ci = (cloak_info_t *)on->on_bi.bi_private;
+
+ int rc = 0, i;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ switch( c->type ) {
+ case CLOAK_ATTR:
+ for ( i = 0; ci; i++, ci = ci->ci_next ) {
+ struct berval bv;
+ int len;
+
+ assert( ci->ci_ad != NULL );
+
+ if ( ci->ci_oc != NULL )
+ len = snprintf( c->cr_msg,
+ sizeof( c->cr_msg ),
+ SLAP_X_ORDERED_FMT "%s %s", i,
+ ci->ci_ad->ad_cname.bv_val,
+ ci->ci_oc->soc_cname.bv_val );
+ else
+ len = snprintf( c->cr_msg,
+ sizeof( c->cr_msg ),
+ SLAP_X_ORDERED_FMT "%s", i,
+ ci->ci_ad->ad_cname.bv_val );
+
+ bv.bv_val = c->cr_msg;
+ bv.bv_len = len;
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
+
+ default:
+ rc = 1;
+ break;
+ }
+
+ return rc;
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ cloak_info_t *ci_next;
+
+ switch( c->type ) {
+ case CLOAK_ATTR:
+ for ( ci_next = ci, i = 0;
+ ci_next, c->valx < 0 || i < c->valx;
+ ci = ci_next, i++ ){
+
+ ci_next = ci->ci_next;
+
+ ch_free ( ci->ci_ad );
+ if ( ci->ci_oc != NULL )
+ ch_free ( ci->ci_oc );
+
+ ch_free( ci );
+ }
+ ci = (cloak_info_t *)on->on_bi.bi_private;
+ break;
+
+ default:
+ rc = 1;
+ break;
+ }
+
+ return rc;
+ }
+
+ switch( c->type ) {
+ case CLOAK_ATTR: {
+ ObjectClass *oc = NULL;
+ AttributeDescription *ad = NULL;
+ const char *text;
+ cloak_info_t **cip = NULL;
+ cloak_info_t *ci_next = NULL;
+
+ if ( c->argc == 3 ) {
+ oc = oc_find( c->argv[ 2 ] );
+ if ( oc == NULL ) {
+ snprintf( c->cr_msg,
+ sizeof( c->cr_msg ),
+ CLOAK_USAGE
+ "unable to find ObjectClass \"%s\"",
+ c->argv[ 2 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+ }
+
+ rc = slap_str2ad( c->argv[ 1 ], &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), CLOAK_USAGE
+ "unable to find AttributeDescription \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+ for ( i = 0, cip = (cloak_info_t **)&on->on_bi.bi_private;
+ c->valx < 0 || i < c->valx, *cip;
+ i++, cip = &(*cip)->ci_next ) {
+ if ( c->valx >= 0 && *cip == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ CLOAK_USAGE
+ "invalid index {%d}\n",
+ c->valx );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+ ci_next = *cip;
+ }
+
+ *cip = (cloak_info_t *)SLAP_CALLOC( 1, sizeof( cloak_info_t ) );
+ (*cip)->ci_oc = oc;
+ (*cip)->ci_ad = ad;
+ (*cip)->ci_next = ci_next;
+
+ rc = 0;
+ break;
+ }
+
+ default:
+ rc = 1;
+ break;
+ }
+
+ return rc;
+}
+
+static int
+cloak_search_response_cb( Operation *op, SlapReply *rs )
+{
+ slap_callback *sc;
+ cloak_info_t *ci;
+ Entry *e = NULL;
+ Entry *me = NULL;
+
+ assert( op && op->o_callback && rs );
+
+ if ( rs->sr_type != REP_SEARCH || !rs->sr_entry ) {
+ return ( SLAP_CB_CONTINUE );
+ }
+
+ sc = op->o_callback;
+ e = rs->sr_entry;
+
+ /*
+ * First perform a quick scan for an attribute to cloak
+ */
+ for ( ci = (cloak_info_t *)sc->sc_private; ci; ci = ci->ci_next ) {
+ Attribute *a;
+
+ if ( ci->ci_oc != NULL &&
+ !is_entry_objectclass_or_sub( e, ci->ci_oc ) )
+ continue;
+
+ for ( a = e->e_attrs; a; a = a->a_next )
+ if ( a->a_desc == ci->ci_ad )
+ break;
+
+ if ( a != NULL )
+ break;
+ }
+
+ /*
+ * Nothing found to cloak
+ */
+ if ( ci == NULL )
+ return ( SLAP_CB_CONTINUE );
+
+ /*
+ * We are now committed to cloak an attribute.
+ */
+ rs_entry2modifiable( op, rs, (slap_overinst *) op->o_bd->bd_info );
+ me = rs->sr_entry;
+
+ for ( ci = (cloak_info_t *)sc->sc_private; ci; ci = ci->ci_next ) {
+ Attribute *a;
+ Attribute *pa;
+
+ for ( pa = NULL, a = me->e_attrs;
+ a;
+ pa = a, a = a->a_next ) {
+
+ if ( a->a_desc != ci->ci_ad )
+ continue;
+
+ Debug( LDAP_DEBUG_TRACE, "cloak_search_response_cb: cloak %s\n",
+ a->a_desc->ad_cname.bv_val );
+
+ if ( pa != NULL )
+ pa->a_next = a->a_next;
+ else
+ me->e_attrs = a->a_next;
+
+ attr_clean( a );
+ }
+
+ }
+
+ return ( SLAP_CB_CONTINUE );
+}
+
+static int
+cloak_search_cleanup_cb( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_RESULT || rs->sr_err != LDAP_SUCCESS ) {
+ slap_freeself_cb( op, rs );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+cloak_search( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ cloak_info_t *ci = (cloak_info_t *)on->on_bi.bi_private;
+ slap_callback *sc;
+
+ if ( op->ors_attrsonly ||
+ op->ors_attrs ||
+ get_manageDSAit( op ) )
+ return SLAP_CB_CONTINUE;
+
+ sc = op->o_tmpcalloc( 1, sizeof( *sc ), op->o_tmpmemctx );
+ sc->sc_response = cloak_search_response_cb;
+ sc->sc_cleanup = cloak_search_cleanup_cb;
+ sc->sc_next = op->o_callback;
+ sc->sc_private = ci;
+ op->o_callback = sc;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static slap_overinst cloak_ovl;
+
+static ConfigTable cloakcfg[] = {
+ { "cloak-attr", "attribute [class]",
+ 2, 3, 0, ARG_MAGIC|CLOAK_ATTR, cloak_cfgen,
+ "( OLcfgCtAt:4.1 NAME 'olcCloakAttribute' "
+ "DESC 'Cloaked attribute: attribute [class]' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "X-ORDERED 'VALUES' )",
+ NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static int
+cloak_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ cloak_info_t *ci = (cloak_info_t *)on->on_bi.bi_private;
+
+ for ( ; ci; ) {
+ cloak_info_t *tmp = ci;
+ ci = ci->ci_next;
+ SLAP_FREE( tmp );
+ }
+
+ on->on_bi.bi_private = NULL;
+
+ return 0;
+}
+
+static ConfigOCs cloakocs[] = {
+ { "( OLcfgCtOc:4.1 "
+ "NAME 'olcCloakConfig' "
+ "DESC 'Attribute cloak configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcCloakAttribute ) )",
+ Cft_Overlay, cloakcfg },
+ { NULL, 0, NULL }
+};
+
+#if SLAPD_OVER_CLOAK == SLAPD_MOD_DYNAMIC
+static
+#endif
+int
+cloak_initialize( void ) {
+ int rc;
+ cloak_ovl.on_bi.bi_type = "cloak";
+ cloak_ovl.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ cloak_ovl.on_bi.bi_db_destroy = cloak_db_destroy;
+ cloak_ovl.on_bi.bi_op_search = cloak_search;
+ cloak_ovl.on_bi.bi_cf_ocs = cloakocs;
+
+ rc = config_register_schema ( cloakcfg, cloakocs );
+ if ( rc )
+ return rc;
+
+ return overlay_register( &cloak_ovl );
+}
+
+#if SLAPD_OVER_CLOAK == SLAPD_MOD_DYNAMIC
+int init_module(int argc, char *argv[]) {
+ return cloak_initialize();
+}
+#endif
+
+#endif /* defined(SLAPD_OVER_CLOAK) */
+
diff --git a/contrib/slapd-modules/cloak/slapo-cloak.5 b/contrib/slapd-modules/cloak/slapo-cloak.5
new file mode 100644
index 0000000..2655d2e
--- /dev/null
+++ b/contrib/slapd-modules/cloak/slapo-cloak.5
@@ -0,0 +1,82 @@
+.TH SLAPO-CLOAK 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation, All Rights Reserved.
+.\" Copying restrictions apply. See the COPYRIGHT file.
+.\" $OpenLDAP$
+.SH NAME
+slapo-cloak \- Attribute cloak overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B cloak
+overlay to
+.BR slapd (8)
+allows the server to hide specific attributes, unless explicitly requested
+by the client. This improve performance when a client requests all attributes
+and get a huge binary attribute that is of no interest for it.
+This behavior is disabled when the \fImanageDSAit\fP
+control (RFC 3296) is used.
+
+.SH CONFIGURATION
+The config directives that are specific to the
+.B cloak
+overlay must be prefixed by
+.BR cloak\- ,
+to avoid potential conflicts with directives specific to the underlying
+database or to other stacked overlays.
+
+.TP
+.B overlay cloak
+This directive adds the cloak overlay to the current database,
+or to the frontend, if used before any database instantiation; see
+.BR slapd.conf (5)
+for details.
+
+.LP
+This
+.B slapd.conf
+configuration option is defined for the cloak overlay. It may have multiple
+occurrences, and it must appear after the
+.B overlay
+directive:
+.TP
+.B cloak-attr <attribute> [<class>]
+The value
+.B <attribute>
+is the name of the attribute that will be cloaked.
+
+The optional
+.B <class>
+restricts cloaking only to entries of the named
+.B <class>.
+
+.SH EXAMPLE
+This example hide the
+.B jpegPhoto
+attribute. Add the following to slapd.conf:
+
+.LP
+.nf
+ database <database>
+ # ...
+
+ overlay cloak
+ cloak-attr jpegPhoto
+.fi
+.LP
+and that slapd loads cloak.la, if compiled as a run-time module;
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd (8).
+The
+.BR slapo-cloak (5)
+overlay supports dynamic configuration via
+.BR back-config .
+.SH ACKNOWLEDGEMENTS
+.P
+This module was originally written in 2008 by Emmanuel Dreyfus.
diff --git a/contrib/slapd-modules/comp_match/Makefile b/contrib/slapd-modules/comp_match/Makefile
new file mode 100644
index 0000000..9b78c5c
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/Makefile
@@ -0,0 +1,69 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 2003-2022 The OpenLDAP Foundation.
+# Portions Copyright 2004 by IBM Corporation.
+# All rights reserved.
+
+# Copyright 2004 Sang Seok Lim, IBM Corp. All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted only as authorized by the OpenLDAP
+# Public License.
+#
+# A copy of this license is available in the file LICENSE in the
+# top-level directory of the distribution or, alternatively, at
+# <http://www.OpenLDAP.org/license.html>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+SNACC_DIR = ../$(LDAP_SRC)/snacc
+SNACC_INC = -I$(SNACC_DIR) -I$(SNACC_DIR)/c-lib/inc
+SNACC_LIB = $(SNACC_DIR)/c-lib/libcasn1.a
+
+SSL_DIR = /usr/local
+SSL_INC = -I$(SSL_DIR)/include/openssl
+SSL_LIB = -lcrypto -L$(SSL_DIR)/lib
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS = -DLDAP_COMPONENT
+INCS = $(LDAP_INC) $(SNACC_INC) $(SSL_INC)
+LIBS = $(LDAP_LIB) $(SNACC_LIB) $(SSL_LIB)
+
+PROGRAMS = compmatch.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+compmatch.la: componentlib.lo init.lo certificate.lo asn_to_syn_mr.lo authorityKeyIdentifier.lo crl.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/comp_match/README b/contrib/slapd-modules/comp_match/README
new file mode 100644
index 0000000..133757c
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/README
@@ -0,0 +1,127 @@
+Copyright 2004 Sang Seok Lim, IBM . All rights reserved.
+
+Redistribution and use in source and binary forms, with
+or without modification, are permitted only as authorized
+by the OpenLDAP Public License.
+
+A copy of this license is available in the file LICENSE in
+the top-level directory of the distribution or, alternatively,
+at <http://www.OpenLDAP.org/license.html>.
+
+This directory contains a Component Matching module and
+a X.509 Certificate example. In order to understand Component
+Matching, see RFC 3687 and
+http://www.openldap.org/conf/odd-sandiego-2004/Sangseok.pdf
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+A) Brief introduction about files in this directory
+%%%%%%%%%%55%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+1) init.c
+module_init() and functions which are dynamically linked
+into the main slapd codes.
+
+2) componentlib.c and componentlib.h
+GSER and BER decoder library of each primitive ASN.1 type.
+They use component representation to store ASN.1 values.
+
+3) certificate.c/.h authorityKeyIdentifier.c/.h
+eSNACC generated BER and GSER decoder routines of the X.509
+certificate specification and one of its extensions,
+authorityKeyIdentifier.
+
+4) asn_to_syn_mr.c asn.h
+An mapping table from ASN.1 types to corresponding Syntaxes,
+matching rules, and component description in slapd.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+B) How to use Component Matching on X.509 certificates
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+1) be sure to configure slapd with enable-modules on.
+2) install the GSER-support eSNACC compiler. You can find
+only in www.openldap.org. At least, you need the library
+(libcasn1.a) and header files for compiling this module.
+3) modify Makefile accordingly. then run make.
+you will get compmatch.la and other necessary files in ./libs
+4) modify slapd.conf to include the following module command
+ moduleload <path to>compmatch.la
+5) run slapd and perform search operations against
+the attribute, userCertificate. You need to read through
+RFC 3687 in order to understand how to compose component
+filters.
+Ex) component search filter examples
+"(userCertificate:componentFilterMatch:=item:{ component
+\"toBeSigned.serialNumber\", rule integerMatch, value 2 })"
+You can find more examples in "test031-component-filter"
+in the OpenLDAP source directory.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+C) How to add a new ASN.1 syntax
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+1) download and install the eSNACC compiler supporting
+Component Matching. You can find the compiler only in
+www.openldap.org. Before compiling, be sure to define
+the "LDAP_COMPONENT" macro to obtain component
+supported version of C library and back-ends of eSNACC.
+Otherwise compiled library will fail to be linked to
+the module.
+2) using eSNACC, compile your ASN.1 specifications and
+copy the generated .c and .h files to this directory
+Ex)
+$ esnacc -E BER_COMP -E GSER -t -d -f example.asn
+For Component Matching, set BOTH BER_COMP and GSER on.
+After compiling, you will get example.c and example.h
+3) modify example.c accordingly, seeing certificate.c
+and certificate.asn as a reference.
+- add init_module_xxx() located in generated .c file
+into init_module() in init.c.
+- modify the arguments of InstallOidDecoderMapping(...)
+accordingly
+- in the generated .c file, you need to write
+"DecComponentxxxTop(...)" function for yourself.
+You can copy BDecComponentCertificateTop in the
+generated .c file and modify it accordingly.
+4) register a new attribute syntax with a new OID
+in a schema file
+5) then goto 3) of B) section.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D) How to configure Component Indexing
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+You can generate indices on each component of
+a given attribute whose values are in either GSER or
+BER. Currently primitive ASN.1 types, DN, and RDN
+can be indexed for equality matching in BDB.
+In order to generate indices, put following line
+in the slapd configuration file, slapd.conf.
+
+index [attribute name].[component reference] eq
+
+Ex)
+index userCertificate eq
+index userCertificate.toBeSigned.issuer.rdnSequence eq
+index userCertificate.toBeSigned.serialNumber eq
+index userCertificate.toBeSigned.version eq
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+D) How to configure Attribute Alias
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+If your client is unable to use component filters,
+attribute aliasing can be used instead. Attribute
+Alias maps a virtual attribute type to an attribute
+component and a component matching rule.
+You can create your own aliases by following steps.
+
+1) register aliasing attributes in the schema file.
+Sample aliasing attributes are in test.schema.
+2) compose component filters for aliasing attributes
+and put them in "preprocessed_comp_filter" array
+in "init.c".
+3) add "add_aa_entry" function calls in
+"init_attribute_aliasing_table()" in "init.c"
+4) perform searching against the aliasing attribute
+Ex)
+"(x509CertificateIssuer:distinguishedNameMatch=
+cn=ray,L=yorktown,o=ibm,c=us)"
diff --git a/contrib/slapd-modules/comp_match/asn.h b/contrib/slapd-modules/comp_match/asn.h
new file mode 100644
index 0000000..a7d5b16
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/asn.h
@@ -0,0 +1,57 @@
+/* Copyright 2004 IBM Corporation
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ */
+/* ACKNOWLEDGEMENTS
+ * This work originally developed by Sang Seok Lim
+ * 2004/06/18 03:20:00 slim@OpenLDAP.org
+ */
+#ifndef _H_ASN_MODULE
+#define _H_ASN_MODULE
+
+typedef enum { BER, GSER } EncRulesType;
+
+typedef enum AsnTypeId {
+ BASICTYPE_BOOLEAN = 0,
+ BASICTYPE_INTEGER,
+ BASICTYPE_BITSTRING,
+ BASICTYPE_OCTETSTRING,
+ BASICTYPE_NULL,
+ BASICTYPE_OID,
+ BASICTYPE_REAL,
+ BASICTYPE_ENUMERATED,
+ BASICTYPE_NUMERIC_STR,
+ BASICTYPE_PRINTABLE_STR,
+ BASICTYPE_UNIVERSAL_STR,
+ BASICTYPE_IA5_STR,
+ BASICTYPE_BMP_STR,
+ BASICTYPE_UTF8_STR,
+ BASICTYPE_UTCTIME,
+ BASICTYPE_GENERALIZEDTIME,
+ BASICTYPE_GRAPHIC_STR,
+ BASICTYPE_VISIBLE_STR,
+ BASICTYPE_GENERAL_STR,
+ BASICTYPE_OBJECTDESCRIPTOR,
+ BASICTYPE_VIDEOTEX_STR,
+ BASICTYPE_T61_STR,
+ BASICTYPE_OCTETCONTAINING,
+ BASICTYPE_BITCONTAINING,
+ BASICTYPE_RELATIVE_OID, /* 25 */
+ BASICTYPE_ANY,
+ /* Embedded Composite Types*/
+ COMPOSITE_ASN1_TYPE,
+ /* A New ASN.1 types including type reference */
+ RDNSequence,
+ RelativeDistinguishedName,
+ TelephoneNumber,
+ FacsimileTelephoneNumber__telephoneNumber,
+ DirectoryString,
+ /* Newly Defined ASN.1 Type, Manually registered */
+ ASN_COMP_CERTIFICATE,
+ /* ASN.1 Type End */
+ ASNTYPE_END
+} AsnTypeId;
+
+#endif
diff --git a/contrib/slapd-modules/comp_match/asn_to_syn_mr.c b/contrib/slapd-modules/comp_match/asn_to_syn_mr.c
new file mode 100644
index 0000000..9c3f7e7
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/asn_to_syn_mr.c
@@ -0,0 +1,282 @@
+#include <component.h>
+#include "asn.h"
+#include "componentlib.h"
+#include "certificate.h"
+
+AsnTypetoMatchingRuleTable directory_component_matching_table[] = {
+ "1.2.36.79672281.1.13.7",
+{
+ { BASICTYPE_BOOLEAN,NULL,"1.3.6.1.4.1.1466.115.121.1.7", NULL },
+ { BASICTYPE_INTEGER,NULL ,"1.3.6.1.4.1.1466.115.121.1.27", NULL },
+ { BASICTYPE_BITSTRING,NULL ,"1.3.6.1.4.1.1466.115.121.1.6", NULL },
+ { BASICTYPE_OCTETSTRING,NULL , "1.3.6.1.4.1.1466.115.121.1.40", NULL },
+ { BASICTYPE_NULL,NULL , NULL, NULL },
+ { BASICTYPE_OID,NULL ,"1.3.6.1.4.1.1466.115.121.1.38", NULL },
+ { BASICTYPE_REAL,NULL , NULL, NULL },
+ { BASICTYPE_ENUMERATED,NULL , NULL, NULL },
+ { BASICTYPE_NUMERIC_STR, "numericStringMatch", "1.3.6.1.4.1.1466.115.121.1.36", NULL },
+ { BASICTYPE_PRINTABLE_STR, "caseIgnoreMatch", "1.3.6.1.4.1.1466.115.121.1.44", NULL },
+ { BASICTYPE_UNIVERSAL_STR, "caseIgnoreMatch" , NULL, NULL },
+ { BASICTYPE_IA5_STR, "caseIgnoreMatch", "1.3.6.1.4.1.1466.115.121.1.26", NULL },
+ { BASICTYPE_BMP_STR, "caseIgnoreMatch" , NULL, NULL },
+ { BASICTYPE_UTF8_STR, "caseIgnoreMatch" , NULL, NULL },
+ { BASICTYPE_UTCTIME, "uTCTimeMatch" , "1.3.6.1.4.1.1466.115.121.1.53", NULL },
+ { BASICTYPE_GENERALIZEDTIME, "generalizedTimeMatch" ,"1.3.6.1.4.1.1466.115.121.1.24", NULL },
+ { BASICTYPE_GRAPHIC_STR, "caseIgnoreMatch", NULL, NULL },
+ { BASICTYPE_VISIBLE_STR, "caseIgnoreMatch", NULL, NULL },
+ { BASICTYPE_GENERAL_STR, "caseIgnoreMatch", NULL, NULL },
+ { BASICTYPE_OBJECTDESCRIPTOR, NULL , NULL, NULL },
+ { BASICTYPE_VIDEOTEX_STR, "caseIgnoreMatch", NULL, NULL },
+ { BASICTYPE_T61_STR, "caseIgnoreMatch", NULL, NULL },
+ { BASICTYPE_OCTETCONTAINING, NULL , NULL, NULL },
+ { BASICTYPE_BITCONTAINING, NULL , NULL, NULL },
+ { BASICTYPE_RELATIVE_OID, NULL, "1.2.36.79672281.1.5.0", NULL },
+ { RDNSequence, "distinguishedNameMatch" , NULL, NULL },
+ { RelativeDistinguishedName, NULL , NULL, NULL },
+ { TelephoneNumber, "telephoneNumberMatch" , "1.3.6.1.4.1.1466.115.121.1.50", NULL },
+ { FacsimileTelephoneNumber__telephoneNumber, "telephoneNumberMatch","1.3.6.1.4.1.1466.115.121.1.22", NULL },
+ { DirectoryString, "caseIgnoreMatch" ,"1.3.6.1.4.1.1466.115.121.1.15", NULL },
+ { ASN_COMP_CERTIFICATE, NULL , "1.2.36.79672281.1.5.2" , NULL },
+ { ASNTYPE_END , NULL , NULL, NULL }
+},
+ NULL
+};
+
+AsnTypetoSyntax asn_to_syntax_mapping_tbl[] = {
+{ BASICTYPE_BOOLEAN,"Boolean","1.3.6.1.4.1.1466.115.121.1.7", NULL },
+{ BASICTYPE_INTEGER,"Integer","1.3.6.1.4.1.1466.115.121.1.27", NULL },
+{ BASICTYPE_BITSTRING,"Bit String","1.3.6.1.4.1.1466.115.121.1.6", NULL },
+{ BASICTYPE_OCTETSTRING,"Octet String", "1.3.6.1.4.1.1466.115.121.1.40", NULL },
+{ BASICTYPE_NULL,NULL, NULL, NULL },
+{ BASICTYPE_OID,"OID","1.3.6.1.4.1.1466.115.121.1.38", NULL },
+{ BASICTYPE_REAL,NULL, NULL, NULL },
+{ BASICTYPE_ENUMERATED,"Integer", "1.3.6.1.4.1.1466.115.121.1.27", NULL },
+{ BASICTYPE_NUMERIC_STR, "Numeric String", "1.3.6.1.4.1.1466.115.121.1.36", NULL },
+{ BASICTYPE_PRINTABLE_STR, "Printable String", "1.3.6.1.4.1.1466.115.121.1.44", NULL },
+{ BASICTYPE_UNIVERSAL_STR, NULL , NULL, NULL },
+{ BASICTYPE_IA5_STR, "IA5 String", "1.3.6.1.4.1.1466.115.121.1.26", NULL },
+{ BASICTYPE_BMP_STR, NULL , NULL, NULL },
+{ BASICTYPE_UTF8_STR, "Directory String" , "1.3.6.1.4.1.1466.115.121.1.15", NULL },
+{ BASICTYPE_UTCTIME, "UTC Time" , "1.3.6.1.4.1.1466.115.121.1.53", NULL },
+{ BASICTYPE_GENERALIZEDTIME, "Generalized Time" ,"1.3.6.1.4.1.1466.115.121.1.24", NULL },
+{ BASICTYPE_GRAPHIC_STR, NULL, NULL, NULL },
+{ BASICTYPE_VISIBLE_STR, "Directory String", "1.3.6.1.4.1.1466.115.121.1.15", NULL },
+{ BASICTYPE_GENERAL_STR, NULL, NULL, NULL },
+{ BASICTYPE_OBJECTDESCRIPTOR, "Object Class Description", "1.3.6.1.4.1.1466.115.121.1.37", NULL },
+{ BASICTYPE_VIDEOTEX_STR, NULL, NULL, NULL },
+{ BASICTYPE_T61_STR, NULL, NULL, NULL },
+{ BASICTYPE_OCTETCONTAINING, NULL , NULL, NULL },
+{ BASICTYPE_BITCONTAINING, NULL , NULL, NULL },
+{ BASICTYPE_RELATIVE_OID, "OID", "1.3.6.1.4.1.1466.115.121.1.38", NULL },
+{ BASICTYPE_ANY, NULL, NULL, NULL },
+{ COMPOSITE_ASN1_TYPE, NULL , NULL, NULL },
+{ RDNSequence, "Distinguished Name" , "1.3.6.1.4.1.1466.115.121.1.12", NULL },
+{ RelativeDistinguishedName, "RDN", "1.2.36.79672281.1.5.0", NULL },
+{ TelephoneNumber, "Telephone Number" , "1.3.6.1.4.1.1466.115.121.1.50", NULL },
+{ FacsimileTelephoneNumber__telephoneNumber, "Facsimile Telephone Number","1.3.6.1.4.1.1466.115.121.1.22", NULL },
+{ DirectoryString, "Directory String" ,"1.3.6.1.4.1.1466.115.121.1.15", NULL },
+{ ASN_COMP_CERTIFICATE, "componentCertificate", "1.2.36.79672281.1.5.2" , NULL },
+{ ASNTYPE_END , NULL , NULL, NULL }
+};
+
+/*
+ * This table describes relationship between an ASN.1 type and its
+ * potential matching rules such as equality, approx, ordering, and substring
+ * Based on the description of this table, the following ComponentType
+ * table is initialized
+ */
+AsnTypetoCompMatchingRule asntype_to_compMR_mapping_tbl[] = {
+{ BASICTYPE_BOOLEAN, "booleanMatch", NULL, NULL, NULL },
+{ BASICTYPE_INTEGER, "integerMatch", NULL, "integerOrderingMatch", NULL },
+{ BASICTYPE_BITSTRING, "bitStringMatch", NULL, NULL, NULL },
+{ BASICTYPE_OCTETSTRING, "octetStringMatch", NULL, "octetStringOrderingMatch", NULL },
+{ BASICTYPE_NULL, NULL, NULL, NULL, NULL },
+{ BASICTYPE_OID, "objectIdentifierMatch", NULL, NULL, NULL },
+{ BASICTYPE_REAL, NULL, NULL, NULL, NULL },
+{ BASICTYPE_ENUMERATED, "integerMatch", NULL, "integerOrderingMatch", NULL },
+{ BASICTYPE_NUMERIC_STR, "numericStringMatch", NULL, "numericStringOrderingMatch", "numericStringSubstringsMatch"},
+{ BASICTYPE_PRINTABLE_STR, "caseIgnoreMatch", "directoryStringApproxMatch", "caseIgnoreOrderingMatch", "caseIgnoreSubstringsMatch" },
+{ BASICTYPE_UNIVERSAL_STR, "caseIgnoreMatch", "directoryStringApproxMatch", "caseIgnoreOrderingMatch", "caseIgnoreSubstringsMatch" },
+{ BASICTYPE_IA5_STR, "caseIgnoreMatch", "IA5StringApproxMatch", "caseIgnoreOrderingMatch", "caseIgnoreSubstringsMatch" },
+{ BASICTYPE_BMP_STR, "caseIgnoreMatch", "directoryStringApproxMatch", "caseIgnoreOrderingMatch", "caseIgnoreSubstringsMatch" },
+{ BASICTYPE_UTF8_STR, "caseIgnoreMatch", "directoryStringApproxMatch", "caseIgnoreOrderingMatch", "caseIgnoreSubstringsMatch" },
+{ BASICTYPE_UTCTIME, NULL, NULL, NULL, NULL },
+{ BASICTYPE_GENERALIZEDTIME, NULL, NULL, NULL, NULL },
+{ BASICTYPE_GRAPHIC_STR, NULL, NULL, NULL, NULL },
+{ BASICTYPE_VISIBLE_STR, "caseIgnoreMatch", "directoryStringApproxMatch", "caseIgnoreOrderingMatch", "caseIgnoreSubstringsMatch" },
+{ BASICTYPE_GENERAL_STR, NULL, NULL, NULL, NULL },
+{ BASICTYPE_OBJECTDESCRIPTOR, "objectIdentifierFirstComponentMatch", NULL, NULL, NULL },
+{ BASICTYPE_VIDEOTEX_STR, NULL, NULL, NULL, NULL },
+{ BASICTYPE_T61_STR, NULL, NULL, NULL, NULL },
+{ BASICTYPE_OCTETCONTAINING, NULL, NULL, NULL, NULL },
+{ BASICTYPE_BITCONTAINING, NULL, NULL, NULL, NULL },
+{ BASICTYPE_RELATIVE_OID, "objectIdentifierFirstComponentMatch", NULL, NULL, NULL },
+{ BASICTYPE_ANY, NULL, NULL, NULL, NULL },
+{ COMPOSITE_ASN1_TYPE, NULL, NULL, NULL, NULL },
+{ RDNSequence, "distinguishedNameMatch", NULL, NULL, NULL },
+{ RelativeDistinguishedName, "rdnMatch" , NULL, NULL, NULL },
+{ TelephoneNumber, NULL, NULL, NULL, NULL },
+{ FacsimileTelephoneNumber__telephoneNumber, "caseIgnoreMatch", "directoryStringApproxMatch", "caseIgnoreOrderingMatch", "caseIgnoreSubstringsMatch" },
+{ DirectoryString, "caseIgnoreMatch", "directoryStringApproxMatch", "caseIgnoreOrderingMatch", "caseIgnoreSubstringsMatch"},
+{ ASN_COMP_CERTIFICATE, "componentFilterMatch", NULL, NULL, NULL },
+{ ASNTYPE_END, NULL, NULL, NULL, NULL }
+};
+
+/*
+ * This table maps an ASN type to a corresponding ComponentType which has
+ * equivalent contents of an existing AttributeType
+ */
+AsnTypetoCompType asntype_to_compType_mapping_tbl[] = {
+{ BASICTYPE_BOOLEAN,{}},
+{ BASICTYPE_INTEGER, {}},
+{ BASICTYPE_BITSTRING, {}},
+{ BASICTYPE_OCTETSTRING, {}},
+{ BASICTYPE_NULL, {}},
+{ BASICTYPE_OID, {}},
+{ BASICTYPE_REAL, {}},
+{ BASICTYPE_ENUMERATED, {}},
+{ BASICTYPE_NUMERIC_STR, {}},
+{ BASICTYPE_PRINTABLE_STR, {}},
+{ BASICTYPE_UNIVERSAL_STR, {}},
+{ BASICTYPE_IA5_STR, {}},
+{ BASICTYPE_BMP_STR, {}},
+{ BASICTYPE_UTF8_STR, {}},
+{ BASICTYPE_UTCTIME, {}},
+{ BASICTYPE_GENERALIZEDTIME, {}},
+{ BASICTYPE_GRAPHIC_STR, {}},
+{ BASICTYPE_VISIBLE_STR, {}},
+{ BASICTYPE_GENERAL_STR,{}},
+{ BASICTYPE_OBJECTDESCRIPTOR, {}},
+{ BASICTYPE_VIDEOTEX_STR, {}},
+{ BASICTYPE_T61_STR, {}},
+{ BASICTYPE_OCTETCONTAINING, {}},
+{ BASICTYPE_BITCONTAINING, {}},
+{ BASICTYPE_RELATIVE_OID, {}},
+{ BASICTYPE_ANY, {}},
+{ COMPOSITE_ASN1_TYPE, {}},
+{ RDNSequence, {}},
+{ RelativeDistinguishedName, {}},
+{ TelephoneNumber, {}},
+{ FacsimileTelephoneNumber__telephoneNumber, {}},
+{ DirectoryString, {}},
+{ ASN_COMP_CERTIFICATE, {}},
+{ ASNTYPE_END , {}}
+};
+
+AsnTypetoCompDesc asntype_to_compdesc_mapping_tbl[] = {
+{ BASICTYPE_BOOLEAN, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_BOOLEAN,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentBool,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentBool,(ber_decoder_func*)BDecComponentBool,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentBool}},
+{ BASICTYPE_INTEGER, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_INTEGER,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentInt,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentInt,(ber_decoder_func*)BDecComponentInt,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentInt}},
+{ BASICTYPE_BITSTRING, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_BITSTRING,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentBits,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentBits,(ber_decoder_func*)BDecComponentBits,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentBits}},
+{ BASICTYPE_OCTETSTRING, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_OCTETSTRING,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentOcts,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentOcts,(ber_decoder_func*)BDecComponentOcts,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentOcts}},
+{ BASICTYPE_NULL, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_NULL,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentNull,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentNull,(ber_decoder_func*)BDecComponentNull,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentNull}},
+{ BASICTYPE_OID, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_OID,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentOid,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentOid,(ber_decoder_func*)BDecComponentOid,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentOid}},
+{ BASICTYPE_REAL, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_REAL,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentReal,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentReal,(ber_decoder_func*)BDecComponentReal,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentReal}},
+{ BASICTYPE_ENUMERATED, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_ENUMERATED,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentEnum,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentEnum,(ber_decoder_func*)BDecComponentEnum,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentEnum}},
+{ BASICTYPE_NUMERIC_STR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_NUMERIC_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentNumericString,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentNumericString,(ber_decoder_func*)BDecComponentNumericString,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentNumericString}},
+{ BASICTYPE_PRINTABLE_STR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_PRINTABLE_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentPrintableString,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentPrintableString,(ber_decoder_func*)BDecComponentPrintableString,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentPrintableString}},
+{ BASICTYPE_UNIVERSAL_STR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_UNIVERSAL_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentUniversalString,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentUniversalString,(ber_decoder_func*)BDecComponentUniversalString,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentUniversalString}},
+{ BASICTYPE_IA5_STR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_IA5_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentIA5String,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentIA5String,(ber_decoder_func*)BDecComponentIA5String,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentIA5String}},
+{ BASICTYPE_BMP_STR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_BMP_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentBMPString,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentBMPString,(ber_decoder_func*)BDecComponentBMPString,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentBMPString}},
+{ BASICTYPE_UTF8_STR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_UTF8_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentUTF8String,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentUTF8String,(ber_decoder_func*)BDecComponentUTF8String,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentUTF8String}},
+{ BASICTYPE_UTCTIME, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_UTCTIME,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentUTCTime,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentUTCTime,(ber_decoder_func*)BDecComponentUTCTime,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentUTCTime}},
+{ BASICTYPE_GENERALIZEDTIME, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_GENERALIZEDTIME,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentUTCTime,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentUTCTime,(ber_decoder_func*)BDecComponentUTCTime,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentUTCTime}},
+{ BASICTYPE_GRAPHIC_STR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_GRAPHIC_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentPrintableString,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentPrintableString,(ber_decoder_func*)BDecComponentPrintableString,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentPrintableString}},
+{ BASICTYPE_VISIBLE_STR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_VISIBLE_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentVisibleString,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentVisibleString,(ber_decoder_func*)BDecComponentVisibleString,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentVisibleString}},
+{ BASICTYPE_GENERAL_STR,{ -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_GENERAL_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentUTF8String,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentUTF8String,(ber_decoder_func*)BDecComponentUTF8String,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentUTF8String}},
+{ BASICTYPE_OBJECTDESCRIPTOR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_OBJECTDESCRIPTOR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentUTF8String,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentUTF8String,(ber_decoder_func*)BDecComponentUTF8String,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentUTF8String}},
+{ BASICTYPE_VIDEOTEX_STR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_VIDEOTEX_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentTeletexString,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentTeletexString,(ber_decoder_func*)BDecComponentTeletexString,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentTeletexString}},
+{ BASICTYPE_T61_STR, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_T61_STR,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentUTF8String,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentUTF8String,(ber_decoder_func*)BDecComponentUTF8String,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentUTF8String}},
+{ BASICTYPE_OCTETCONTAINING, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_OCTETCONTAINING,
+ (encoder_func*)NULL,(encoder_func*)NULL,(encoder_func*)NULL,
+ (gser_decoder_func*)NULL,(ber_decoder_func*)NULL,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,NULL}},
+{ BASICTYPE_BITCONTAINING, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_BITCONTAINING,
+ (encoder_func*)NULL,(encoder_func*)NULL,(encoder_func*)NULL,
+ (gser_decoder_func*)NULL,(ber_decoder_func*)NULL,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,NULL}},
+{ BASICTYPE_RELATIVE_OID, { -1, NULL, {},{},0,ASN_BASIC,BASICTYPE_RELATIVE_OID,
+ (encoder_func*)NULL,(encoder_func*)GEncComponentRelativeOid,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentRelativeOid,(ber_decoder_func*)BDecComponentRelativeOid,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentRelativeOid}},
+{ BASICTYPE_ANY, {}},
+{ COMPOSITE_ASN1_TYPE, {}},
+{ RDNSequence, { -1, NULL, {},{},0,ASN_COMPOSITE,RDNSequence,
+ (encoder_func*)ConvertRDNSequence2RFC2253,(encoder_func*)NULL,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentRDNSequence,(ber_decoder_func*)BDecComponentRDNSequence,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentRDNSequence}},
+{ RelativeDistinguishedName, { -1, NULL, {},{},0,ASN_COMPOSITE,RDNSequence,
+ (encoder_func*)ConvertRDNSequence2RFC2253,(encoder_func*)NULL,(encoder_func*)NULL,
+ (gser_decoder_func*)GDecComponentRDNSequence,(ber_decoder_func*)BDecComponentRDNSequence,
+ (comp_free_func*)NULL,(extract_component_from_id_func*)NULL,MatchingComponentRDNSequence}},
+{ TelephoneNumber, {}},
+{ FacsimileTelephoneNumber__telephoneNumber, {}},
+{ DirectoryString, {}},
+{ ASN_COMP_CERTIFICATE, {}},
+{ ASNTYPE_END , {}}
+};
diff --git a/contrib/slapd-modules/comp_match/authorityKeyIdentifier.asn b/contrib/slapd-modules/comp_match/authorityKeyIdentifier.asn
new file mode 100644
index 0000000..85ac92d
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/authorityKeyIdentifier.asn
@@ -0,0 +1,65 @@
+AuthorityKeyIdentifierDefinition DEFINITIONS ::=
+BEGIN
+AuthorityKeyIdentifier ::= SEQUENCE {
+ keyIdentifier [0] IMPLICIT KeyIdentifier OPTIONAL,
+ authorityCertIssuer [1] IMPLICIT GeneralNames OPTIONAL,
+ authorityCertSerialNumber [2] IMPLICIT CertificateSerialNumber OPTIONAL }
+ -- authorityCertIssuer and authorityCertSerialNumber MUST both
+ -- be present or both be absent
+
+KeyIdentifier ::= OCTET STRING
+
+CertificateSerialNumber ::= INTEGER
+
+GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+
+GeneralName ::= CHOICE {
+ otherName [0] OtherName,
+ rfc822Name [1] IA5String,
+ dNSName [2] IA5String,
+ x400Address [3] ORAddress,
+ directoryName [4] Name,
+ ediPartyName [5] EDIPartyName,
+ uniformResourceIdentifier [6] IA5String,
+ iPAddress [7] OCTET STRING,
+ registeredID [8] OBJECT IDENTIFIER }
+
+OtherName ::= SEQUENCE {
+ type-id OBJECT IDENTIFIER,
+ value [0] EXPLICIT ANY DEFINED BY type-id }
+
+EDIPartyName ::= SEQUENCE {
+ nameAssigner [0] DirectoryString OPTIONAL,
+ partyName [1] DirectoryString }
+
+-- following ORAddress may not conform original def. in ASN.1
+ORAddress ::= SEQUENCE {
+-- built-in-standard-attributes BuiltInStandardAttributes,
+ type-id OBJECT IDENTIFIER,
+-- built-in-domain-defined-attributes
+ value ANY DEFINED BY type-id,
+-- BuiltInDomainDefinedAttributes OPTIONAL,
+-- see also teletex-domain-defined-attributes
+--extension-attributes ExtensionAttributes OPTIONAL }
+ extension OCTET STRING OPTIONAL }
+
+
+Name ::= CHOICE {
+ rdnSequence RDNSequence }
+
+RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+
+RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
+
+AttributeTypeAndValue ::= SEQUENCE {
+ type OBJECT IDENTIFIER,
+ value ANY DEFINED BY type}
+
+DirectoryString ::= CHOICE {
+ teletexString TeletexString (SIZE (1..MAX)),
+ printableString PrintableString (SIZE (1..MAX)),
+ universalString UniversalString (SIZE (1..MAX)),
+ utf8String UTF8String (SIZE (1..MAX)),
+ bmpString BMPString (SIZE (1..MAX)) }
+
+END
diff --git a/contrib/slapd-modules/comp_match/authorityKeyIdentifier.c b/contrib/slapd-modules/comp_match/authorityKeyIdentifier.c
new file mode 100644
index 0000000..84e83b8
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/authorityKeyIdentifier.c
@@ -0,0 +1,2058 @@
+/*
+ * authorityKeyIdentifier.c
+ * "AuthorityKeyIdentifierDefinition" ASN.1 module encode/decode/extracting/matching/free C src.
+ * This file was generated by modified eSMACC compiler Wed Dec 8 22:22:49 2004
+ * The generated files are supposed to be compiled as a module for OpenLDAP Software
+ */
+
+#include "authorityKeyIdentifier.h"
+
+BDecComponentAuthorityKeyIdentifierTop( void* mem_op, GenBuf* b, void *v, AsnLen* bytesDecoded,int mode) {
+ AsnTag tag;
+ AsnLen elmtLen;
+
+ tag = BDecTag ( b, bytesDecoded );
+ elmtLen = BDecLen ( b, bytesDecoded );
+ if ( elmtLen <= 0 ) return (-1);
+ if ( tag != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE) ) {
+ return (-1);
+ }
+
+ return BDecComponentAuthorityKeyIdentifier( mem_op, b, tag, elmtLen, ( ComponentAuthorityKeyIdentifier**)v, (AsnLen*)bytesDecoded, mode );
+}
+
+
+void init_module_AuthorityKeyIdentifierDefinition() {
+ InstallOidDecoderMapping( "2.5.29.35", NULL,
+ GDecComponentAuthorityKeyIdentifier,
+ BDecComponentAuthorityKeyIdentifierTop,
+ ExtractingComponentAuthorityKeyIdentifier,
+ MatchingComponentAuthorityKeyIdentifier );
+}
+
+int
+MatchingComponentOtherName ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentOid ( oid, (ComponentSyntaxInfo*)&((ComponentOtherName*)csi_attr)->type_id, (ComponentSyntaxInfo*)&((ComponentOtherName*)csi_assert)->type_id );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = SetAnyTypeByComponentOid ((ComponentSyntaxInfo*)&((ComponentOtherName*)csi_attr)->value, (&((ComponentOtherName*)csi_attr)->type_id));
+ rc = MatchingComponentAnyDefinedBy ( oid, (ComponentAny*)&((ComponentOtherName*)csi_attr)->value, (ComponentAny*)&((ComponentOtherName*)csi_assert)->value);
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentOtherName */
+
+void*
+ExtractingComponentOtherName ( void* mem_op, ComponentReference* cr, ComponentOtherName *comp )
+{
+
+ if ( ( comp->type_id.identifier.bv_val && strncmp(comp->type_id.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->type_id.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->type_id;
+ else
+ return NULL;
+ }
+ if ( ( comp->value.identifier.bv_val && strncmp(comp->value.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->value.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->value;
+ else
+ return NULL;
+ }
+ return NULL;
+} /* ExtractingComponentOtherName */
+
+int
+BDecComponentOtherName PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentOtherName **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ AsnLen totalElmtsLen2 = 0;
+ AsnLen elmtLen2;
+ AsnTag tagId2;
+ int old_mode = mode;
+ int rc;
+ ComponentOtherName *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, OID_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentOid (mem_op, b, tagId1, elmtLen1, (&k->type_id), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->type_id)->identifier.bv_val = (&k->type_id)->id_buf;
+ (&k->type_id)->identifier.bv_len = strlen("type_id");
+ strcpy( (&k->type_id)->identifier.bv_val, "type_id");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (CNTX, CONS, 0))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = SetAnyTypeByComponentOid ((&k->value), (&k->type_id));
+ rc = BDecComponentAnyDefinedBy (mem_op,b, (&k->value), &totalElmtsLen1, mode );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->value)->identifier.bv_val = (&k->value)->id_buf;
+ (&k->value)->identifier.bv_len = strlen("value");
+ strcpy( (&k->value)->identifier.bv_val, "value");
+ if (elmtLen1 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+ else
+ return -1;
+
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentOtherName*) CompAlloc( mem_op, sizeof(ComponentOtherName) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentOtherName ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentOtherName ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentOtherName;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentOtherName;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecOtherName*/
+
+int
+GDecComponentOtherName PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentOtherName **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentOtherName *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "type_id", strlen("type_id") ) == 0 ) {
+ rc = GDecComponentOid (mem_op, b, (&k->type_id), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->type_id)->identifier.bv_val = peek_head;
+ (&k->type_id)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "value", strlen("value") ) == 0 ) {
+ rc = SetAnyTypeByComponentOid ((&k->value), (&k->type_id));
+ rc = GDecComponentAnyDefinedBy (mem_op, b, (&k->value), bytesDecoded, mode );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->value)->identifier.bv_val = peek_head;
+ (&k->value)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentOtherName*) CompAlloc( mem_op, sizeof(ComponentOtherName) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentOtherName ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentOtherName ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentOtherName;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentOtherName;
+ return LDAP_SUCCESS;
+} /* GDecOtherName*/
+
+
+int
+MatchingComponentORAddress ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentOid ( oid, (ComponentSyntaxInfo*)&((ComponentORAddress*)csi_attr)->type_id, (ComponentSyntaxInfo*)&((ComponentORAddress*)csi_assert)->type_id );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = SetAnyTypeByComponentOid ((ComponentSyntaxInfo*)&((ComponentORAddress*)csi_attr)->value, (&((ComponentORAddress*)csi_attr)->type_id));
+ rc = MatchingComponentAnyDefinedBy ( oid, (ComponentAny*)&((ComponentORAddress*)csi_attr)->value, (ComponentAny*)&((ComponentORAddress*)csi_assert)->value);
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentOcts ( oid, (ComponentSyntaxInfo*)&((ComponentORAddress*)csi_attr)->extension, (ComponentSyntaxInfo*)&((ComponentORAddress*)csi_assert)->extension );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentORAddress */
+
+void*
+ExtractingComponentORAddress ( void* mem_op, ComponentReference* cr, ComponentORAddress *comp )
+{
+
+ if ( ( comp->type_id.identifier.bv_val && strncmp(comp->type_id.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->type_id.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->type_id;
+ else
+ return NULL;
+ }
+ if ( ( comp->value.identifier.bv_val && strncmp(comp->value.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->value.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->value;
+ else
+ return NULL;
+ }
+ if ( ( comp->extension.identifier.bv_val && strncmp(comp->extension.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->extension.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->extension;
+ else
+ return NULL;
+ }
+ return NULL;
+} /* ExtractingComponentORAddress */
+
+int
+BDecComponentORAddress PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentORAddress **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentORAddress *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, OID_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentOid (mem_op, b, tagId1, elmtLen1, (&k->type_id), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->type_id)->identifier.bv_val = (&k->type_id)->id_buf;
+ (&k->type_id)->identifier.bv_len = strlen("type_id");
+ strcpy( (&k->type_id)->identifier.bv_val, "type_id");
+ }
+ else
+ return -1;
+
+
+
+ {
+ rc = SetAnyTypeByComponentOid ((&k->value), (&k->type_id));
+ rc = BDecComponentAnyDefinedBy (mem_op,b, (&k->value), &totalElmtsLen1, mode );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->value)->identifier.bv_val = (&k->value)->id_buf;
+ (&k->value)->identifier.bv_len = strlen("value");
+ strcpy( (&k->value)->identifier.bv_val, "value");
+ if ((elmtLen0 != INDEFINITE_LEN) && (totalElmtsLen1 == elmtLen0))
+ seqDone = TRUE;
+ else
+ {
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((elmtLen0 == INDEFINITE_LEN) && (tagId1 == EOC_TAG_ID))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ seqDone = TRUE;
+ }
+ }
+ }
+
+
+ if ((!seqDone) && ((tagId1 == MAKE_TAG_ID (UNIV, PRIM, OCTETSTRING_TAG_CODE)) ||
+(tagId1 == MAKE_TAG_ID (UNIV, CONS, OCTETSTRING_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentOcts (mem_op, b, tagId1, elmtLen1, (&k->extension), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->extension)->identifier.bv_val = (&k->extension)->id_buf;
+ (&k->extension)->identifier.bv_len = strlen("extension");
+ strcpy( (&k->extension)->identifier.bv_val, "extension");
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentORAddress*) CompAlloc( mem_op, sizeof(ComponentORAddress) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentORAddress ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentORAddress ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentORAddress;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentORAddress;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecORAddress*/
+
+int
+GDecComponentORAddress PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentORAddress **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentORAddress *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "type_id", strlen("type_id") ) == 0 ) {
+ rc = GDecComponentOid (mem_op, b, (&k->type_id), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->type_id)->identifier.bv_val = peek_head;
+ (&k->type_id)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "value", strlen("value") ) == 0 ) {
+ rc = SetAnyTypeByComponentOid ((&k->value), (&k->type_id));
+ rc = GDecComponentAnyDefinedBy (mem_op, b, (&k->value), bytesDecoded, mode );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->value)->identifier.bv_val = peek_head;
+ (&k->value)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "extension", strlen("extension") ) == 0 ) {
+ rc = GDecComponentOcts (mem_op, b, (&k->extension), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->extension)->identifier.bv_val = peek_head;
+ (&k->extension)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentORAddress*) CompAlloc( mem_op, sizeof(ComponentORAddress) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentORAddress ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentORAddress ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentORAddress;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentORAddress;
+ return LDAP_SUCCESS;
+} /* GDecORAddress*/
+
+
+int
+MatchingComponentDirectoryString ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+ ComponentDirectoryString *v1, *v2;
+
+
+ v1 = (ComponentDirectoryString*)csi_attr;
+ v2 = (ComponentDirectoryString*)csi_assert;
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ if( (v1->choiceId != v2->choiceId ) )
+ return LDAP_COMPARE_FALSE;
+ switch( v1->choiceId )
+ {
+ case DIRECTORYSTRING_TELETEXSTRING :
+ rc = MatchingComponentTeletexString ( oid, (ComponentSyntaxInfo*)(v1->a.teletexString), (ComponentSyntaxInfo*)(v2->a.teletexString) );
+ break;
+ case DIRECTORYSTRING_PRINTABLESTRING :
+ rc = MatchingComponentPrintableString ( oid, (ComponentSyntaxInfo*)(v1->a.printableString), (ComponentSyntaxInfo*)(v2->a.printableString) );
+ break;
+ case DIRECTORYSTRING_UNIVERSALSTRING :
+ rc = MatchingComponentUniversalString ( oid, (ComponentSyntaxInfo*)(v1->a.universalString), (ComponentSyntaxInfo*)(v2->a.universalString) );
+ break;
+ case DIRECTORYSTRING_UTF8STRING :
+ rc = MatchingComponentUTF8String ( oid, (ComponentSyntaxInfo*)(v1->a.utf8String), (ComponentSyntaxInfo*)(v2->a.utf8String) );
+ break;
+ case DIRECTORYSTRING_BMPSTRING :
+ rc = MatchingComponentBMPString ( oid, (ComponentSyntaxInfo*)(v1->a.bmpString), (ComponentSyntaxInfo*)(v2->a.bmpString) );
+ break;
+ default :
+ return LDAP_PROTOCOL_ERROR;
+ }
+ return rc;
+} /* BMatchingComponentDirectoryStringContent */
+
+void*
+ExtractingComponentDirectoryString ( void* mem_op, ComponentReference* cr, ComponentDirectoryString *comp )
+{
+
+
+ if( (comp->choiceId) == DIRECTORYSTRING_TELETEXSTRING &&
+ (( comp->a.teletexString->identifier.bv_val && strncmp(comp->a.teletexString->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.teletexString->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.teletexString);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTeletexString ( mem_op, cr, (comp->a.teletexString) );
+ };
+ }
+ if( (comp->choiceId) == DIRECTORYSTRING_PRINTABLESTRING &&
+ (( comp->a.printableString->identifier.bv_val && strncmp(comp->a.printableString->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.printableString->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.printableString);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentPrintableString ( mem_op, cr, (comp->a.printableString) );
+ };
+ }
+ if( (comp->choiceId) == DIRECTORYSTRING_UNIVERSALSTRING &&
+ (( comp->a.universalString->identifier.bv_val && strncmp(comp->a.universalString->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.universalString->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.universalString);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentUniversalString ( mem_op, cr, (comp->a.universalString) );
+ };
+ }
+ if( (comp->choiceId) == DIRECTORYSTRING_UTF8STRING &&
+ (( comp->a.utf8String->identifier.bv_val && strncmp(comp->a.utf8String->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.utf8String->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.utf8String);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentUTF8String ( mem_op, cr, (comp->a.utf8String) );
+ };
+ }
+ if( (comp->choiceId) == DIRECTORYSTRING_BMPSTRING &&
+ (( comp->a.bmpString->identifier.bv_val && strncmp(comp->a.bmpString->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.bmpString->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.bmpString);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentBMPString ( mem_op, cr, (comp->a.bmpString) );
+ };
+ }
+ return NULL;
+} /* ExtractingComponentDirectoryString */
+
+int
+BDecComponentDirectoryString PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentDirectoryString **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentDirectoryString *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ switch (tagId0)
+ {
+ case MAKE_TAG_ID (UNIV, PRIM, TELETEXSTRING_TAG_CODE):
+ case MAKE_TAG_ID (UNIV, CONS, TELETEXSTRING_TAG_CODE):
+ (k->choiceId) = DIRECTORYSTRING_TELETEXSTRING;
+ rc = BDecComponentTeletexString (mem_op, b, tagId0, elmtLen0, (&k->a.teletexString), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.teletexString)->identifier.bv_val = (k->a.teletexString)->id_buf;
+ (k->a.teletexString)->identifier.bv_len = strlen("teletexString");
+ strcpy( (k->a.teletexString)->identifier.bv_val, "teletexString");
+ break;
+
+ case MAKE_TAG_ID (UNIV, PRIM, PRINTABLESTRING_TAG_CODE):
+ case MAKE_TAG_ID (UNIV, CONS, PRINTABLESTRING_TAG_CODE):
+ (k->choiceId) = DIRECTORYSTRING_PRINTABLESTRING;
+ rc = BDecComponentPrintableString (mem_op, b, tagId0, elmtLen0, (&k->a.printableString), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.printableString)->identifier.bv_val = (k->a.printableString)->id_buf;
+ (k->a.printableString)->identifier.bv_len = strlen("printableString");
+ strcpy( (k->a.printableString)->identifier.bv_val, "printableString");
+ break;
+
+ case MAKE_TAG_ID (UNIV, PRIM, UNIVERSALSTRING_TAG_CODE):
+ case MAKE_TAG_ID (UNIV, CONS, UNIVERSALSTRING_TAG_CODE):
+ (k->choiceId) = DIRECTORYSTRING_UNIVERSALSTRING;
+ rc = BDecComponentUniversalString (mem_op, b, tagId0, elmtLen0, (&k->a.universalString), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.universalString)->identifier.bv_val = (k->a.universalString)->id_buf;
+ (k->a.universalString)->identifier.bv_len = strlen("universalString");
+ strcpy( (k->a.universalString)->identifier.bv_val, "universalString");
+ break;
+
+ case MAKE_TAG_ID (UNIV, PRIM, UTF8STRING_TAG_CODE):
+ case MAKE_TAG_ID (UNIV, CONS, UTF8STRING_TAG_CODE):
+ (k->choiceId) = DIRECTORYSTRING_UTF8STRING;
+ rc = BDecComponentUTF8String (mem_op, b, tagId0, elmtLen0, (&k->a.utf8String), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.utf8String)->identifier.bv_val = (k->a.utf8String)->id_buf;
+ (k->a.utf8String)->identifier.bv_len = strlen("utf8String");
+ strcpy( (k->a.utf8String)->identifier.bv_val, "utf8String");
+ break;
+
+ case MAKE_TAG_ID (UNIV, PRIM, BMPSTRING_TAG_CODE):
+ case MAKE_TAG_ID (UNIV, CONS, BMPSTRING_TAG_CODE):
+ (k->choiceId) = DIRECTORYSTRING_BMPSTRING;
+ rc = BDecComponentBMPString (mem_op, b, tagId0, elmtLen0, (&k->a.bmpString), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.bmpString)->identifier.bv_val = (k->a.bmpString)->id_buf;
+ (k->a.bmpString)->identifier.bv_len = strlen("bmpString");
+ strcpy( (k->a.bmpString)->identifier.bv_val, "bmpString");
+ break;
+
+ default:
+ Asn1Error ("ERROR - unexpected tag in CHOICE\n");
+ return -1;
+ break;
+ } /* end switch */
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentDirectoryString*) CompAlloc( mem_op, sizeof(ComponentDirectoryString) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentDirectoryString ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentDirectoryString ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentDirectoryString;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentDirectoryString;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecDirectoryStringContent */
+
+int
+GDecComponentDirectoryString PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentDirectoryString **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentDirectoryString *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen2 = LocateNextGSERToken(mem_op,b,&peek_head2,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head2 != ':'){
+ Asn1Error("Missing : in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( strncmp("teletexString",peek_head, strlen("teletexString")) == 0){
+ (k->choiceId) = DIRECTORYSTRING_TELETEXSTRING;
+ rc = GDecComponentTeletexString (mem_op, b, (&k->a.teletexString), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.teletexString)->identifier.bv_val = peek_head;
+ (k->a.teletexString)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("printableString",peek_head,strlen("printableString")) == 0){
+ (k->choiceId) = DIRECTORYSTRING_PRINTABLESTRING;
+ rc = GDecComponentPrintableString (mem_op, b, (&k->a.printableString), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.printableString)->identifier.bv_val = peek_head;
+ (k->a.printableString)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("universalString",peek_head,strlen("universalString")) == 0){
+ (k->choiceId) = DIRECTORYSTRING_UNIVERSALSTRING;
+ rc = GDecComponentUniversalString (mem_op, b, (&k->a.universalString), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.universalString)->identifier.bv_val = peek_head;
+ (k->a.universalString)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("utf8String",peek_head,strlen("utf8String")) == 0){
+ (k->choiceId) = DIRECTORYSTRING_UTF8STRING;
+ rc = GDecComponentUTF8String (mem_op, b, (&k->a.utf8String), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.utf8String)->identifier.bv_val = peek_head;
+ (k->a.utf8String)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("bmpString",peek_head,strlen("bmpString")) == 0){
+ (k->choiceId) = DIRECTORYSTRING_BMPSTRING;
+ rc = GDecComponentBMPString (mem_op, b, (&k->a.bmpString), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.bmpString)->identifier.bv_val = peek_head;
+ (k->a.bmpString)->identifier.bv_len = strLen;
+ }
+ else {
+ Asn1Error("Undefined Identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentDirectoryString*) CompAlloc( mem_op, sizeof(ComponentDirectoryString) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentDirectoryString ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentDirectoryString ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentDirectoryString;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentDirectoryString;
+ return LDAP_SUCCESS;
+} /* GDecDirectoryStringContent */
+
+
+int
+MatchingComponentEDIPartyName ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ if(COMPONENTNOT_NULL( ((ComponentEDIPartyName*)csi_attr)->nameAssigner ) ) {
+ rc = MatchingComponentDirectoryString ( oid, (ComponentSyntaxInfo*)((ComponentEDIPartyName*)csi_attr)->nameAssigner, (ComponentSyntaxInfo*)((ComponentEDIPartyName*)csi_assert)->nameAssigner );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ }
+ rc = MatchingComponentDirectoryString ( oid, (ComponentSyntaxInfo*)((ComponentEDIPartyName*)csi_attr)->partyName, (ComponentSyntaxInfo*)((ComponentEDIPartyName*)csi_assert)->partyName );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentEDIPartyName */
+
+void*
+ExtractingComponentEDIPartyName ( void* mem_op, ComponentReference* cr, ComponentEDIPartyName *comp )
+{
+
+ if ( ( comp->nameAssigner->identifier.bv_val && strncmp(comp->nameAssigner->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->nameAssigner->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->nameAssigner;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentDirectoryString ( mem_op, cr, comp->nameAssigner );
+ }
+ }
+ if ( ( comp->partyName->identifier.bv_val && strncmp(comp->partyName->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->partyName->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->partyName;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentDirectoryString ( mem_op, cr, comp->partyName );
+ }
+ }
+ return NULL;
+} /* ExtractingComponentEDIPartyName */
+
+int
+BDecComponentEDIPartyName PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentEDIPartyName **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ AsnLen totalElmtsLen2 = 0;
+ AsnLen elmtLen2;
+ AsnTag tagId2;
+ AsnLen totalElmtsLen3 = 0;
+ AsnLen elmtLen3;
+ AsnTag tagId3;
+ int old_mode = mode;
+ int rc;
+ ComponentEDIPartyName *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (CNTX, CONS, 0))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = tagId2 = BDecTag (b, &totalElmtsLen1 );
+ elmtLen2 = BDecLen (b, &totalElmtsLen1 );
+ BDecComponentDirectoryString (mem_op, b, tagId2, elmtLen2, (&k->nameAssigner), &totalElmtsLen1, mode);
+ if (elmtLen1 == INDEFINITE_LEN)
+ BDecEoc(b, &totalElmtsLen1 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->nameAssigner)->identifier.bv_val = (k->nameAssigner)->id_buf;
+ (k->nameAssigner)->identifier.bv_len = strlen("nameAssigner");
+ strcpy( (k->nameAssigner)->identifier.bv_val, "nameAssigner");
+ if (elmtLen1 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+
+
+ if (((tagId1 == MAKE_TAG_ID (CNTX, CONS, 1))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = tagId2 = BDecTag (b, &totalElmtsLen1 );
+ elmtLen2 = BDecLen (b, &totalElmtsLen1 );
+ BDecComponentDirectoryString (mem_op, b, tagId2, elmtLen2, (&k->partyName), &totalElmtsLen1, mode);
+ if (elmtLen1 == INDEFINITE_LEN)
+ BDecEoc(b, &totalElmtsLen1 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->partyName)->identifier.bv_val = (k->partyName)->id_buf;
+ (k->partyName)->identifier.bv_len = strlen("partyName");
+ strcpy( (k->partyName)->identifier.bv_val, "partyName");
+ if (elmtLen1 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+ else
+ return -1;
+
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentEDIPartyName*) CompAlloc( mem_op, sizeof(ComponentEDIPartyName) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentEDIPartyName ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentEDIPartyName ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentEDIPartyName;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentEDIPartyName;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecEDIPartyName*/
+
+int
+GDecComponentEDIPartyName PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentEDIPartyName **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentEDIPartyName *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "nameAssigner", strlen("nameAssigner") ) == 0 ) {
+ rc = GDecComponentDirectoryString (mem_op, b, (&k->nameAssigner), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->nameAssigner)->identifier.bv_val = peek_head;
+ ( k->nameAssigner)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "partyName", strlen("partyName") ) == 0 ) {
+ rc = GDecComponentDirectoryString (mem_op, b, (&k->partyName), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->partyName)->identifier.bv_val = peek_head;
+ ( k->partyName)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentEDIPartyName*) CompAlloc( mem_op, sizeof(ComponentEDIPartyName) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentEDIPartyName ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentEDIPartyName ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentEDIPartyName;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentEDIPartyName;
+ return LDAP_SUCCESS;
+} /* GDecEDIPartyName*/
+
+
+
+int
+MatchingComponentGeneralName ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+ ComponentGeneralName *v1, *v2;
+
+
+ v1 = (ComponentGeneralName*)csi_attr;
+ v2 = (ComponentGeneralName*)csi_assert;
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ if( (v1->choiceId != v2->choiceId ) )
+ return LDAP_COMPARE_FALSE;
+ switch( v1->choiceId )
+ {
+ case GENERALNAME_OTHERNAME :
+ rc = MatchingComponentOtherName ( oid, (ComponentSyntaxInfo*)(v1->a.otherName), (ComponentSyntaxInfo*)(v2->a.otherName) );
+ break;
+ case GENERALNAME_RFC822NAME :
+ rc = MatchingComponentIA5String ( oid, (ComponentSyntaxInfo*)(v1->a.rfc822Name), (ComponentSyntaxInfo*)(v2->a.rfc822Name) );
+ break;
+ case GENERALNAME_DNSNAME :
+ rc = MatchingComponentIA5String ( oid, (ComponentSyntaxInfo*)(v1->a.dNSName), (ComponentSyntaxInfo*)(v2->a.dNSName) );
+ break;
+ case GENERALNAME_X400ADDRESS :
+ rc = MatchingComponentORAddress ( oid, (ComponentSyntaxInfo*)(v1->a.x400Address), (ComponentSyntaxInfo*)(v2->a.x400Address) );
+ break;
+ case GENERALNAME_DIRECTORYNAME :
+ rc = MatchingComponentName ( oid, (ComponentSyntaxInfo*)(v1->a.directoryName), (ComponentSyntaxInfo*)(v2->a.directoryName) );
+ break;
+ case GENERALNAME_EDIPARTYNAME :
+ rc = MatchingComponentEDIPartyName ( oid, (ComponentSyntaxInfo*)(v1->a.ediPartyName), (ComponentSyntaxInfo*)(v2->a.ediPartyName) );
+ break;
+ case GENERALNAME_UNIFORMRESOURCEIDENTIFIER :
+ rc = MatchingComponentIA5String ( oid, (ComponentSyntaxInfo*)(v1->a.uniformResourceIdentifier), (ComponentSyntaxInfo*)(v2->a.uniformResourceIdentifier) );
+ break;
+ case GENERALNAME_IPADDRESS :
+ rc = MatchingComponentOcts ( oid, (ComponentSyntaxInfo*)(v1->a.iPAddress), (ComponentSyntaxInfo*)(v2->a.iPAddress) );
+ break;
+ case GENERALNAME_REGISTEREDID :
+ rc = MatchingComponentOid ( oid, (ComponentSyntaxInfo*)(v1->a.registeredID), (ComponentSyntaxInfo*)(v2->a.registeredID) );
+ break;
+ default :
+ return LDAP_PROTOCOL_ERROR;
+ }
+ return rc;
+} /* BMatchingComponentGeneralNameContent */
+
+void*
+ExtractingComponentGeneralName ( void* mem_op, ComponentReference* cr, ComponentGeneralName *comp )
+{
+
+
+ if( (comp->choiceId) == GENERALNAME_OTHERNAME &&
+ (( comp->a.otherName->identifier.bv_val && strncmp(comp->a.otherName->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.otherName->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.otherName);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentOtherName ( mem_op, cr, (comp->a.otherName) );
+ };
+ }
+ if( (comp->choiceId) == GENERALNAME_RFC822NAME &&
+ (( comp->a.rfc822Name->identifier.bv_val && strncmp(comp->a.rfc822Name->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.rfc822Name->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.rfc822Name);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentIA5String ( mem_op, cr, (comp->a.rfc822Name) );
+ };
+ }
+ if( (comp->choiceId) == GENERALNAME_DNSNAME &&
+ (( comp->a.dNSName->identifier.bv_val && strncmp(comp->a.dNSName->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.dNSName->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.dNSName);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentIA5String ( mem_op, cr, (comp->a.dNSName) );
+ };
+ }
+ if( (comp->choiceId) == GENERALNAME_X400ADDRESS &&
+ (( comp->a.x400Address->identifier.bv_val && strncmp(comp->a.x400Address->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.x400Address->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.x400Address);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentORAddress ( mem_op, cr, (comp->a.x400Address) );
+ };
+ }
+ if( (comp->choiceId) == GENERALNAME_DIRECTORYNAME &&
+ (( comp->a.directoryName->identifier.bv_val && strncmp(comp->a.directoryName->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.directoryName->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.directoryName);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentName ( mem_op, cr, (comp->a.directoryName) );
+ };
+ }
+ if( (comp->choiceId) == GENERALNAME_EDIPARTYNAME &&
+ (( comp->a.ediPartyName->identifier.bv_val && strncmp(comp->a.ediPartyName->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.ediPartyName->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.ediPartyName);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentEDIPartyName ( mem_op, cr, (comp->a.ediPartyName) );
+ };
+ }
+ if( (comp->choiceId) == GENERALNAME_UNIFORMRESOURCEIDENTIFIER &&
+ (( comp->a.uniformResourceIdentifier->identifier.bv_val && strncmp(comp->a.uniformResourceIdentifier->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.uniformResourceIdentifier->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.uniformResourceIdentifier);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentIA5String ( mem_op, cr, (comp->a.uniformResourceIdentifier) );
+ };
+ }
+ if( (comp->choiceId) == GENERALNAME_IPADDRESS &&
+ (( comp->a.iPAddress->identifier.bv_val && strncmp(comp->a.iPAddress->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.iPAddress->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.iPAddress);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentOcts ( mem_op, cr, (comp->a.iPAddress) );
+ };
+ }
+ if( (comp->choiceId) == GENERALNAME_REGISTEREDID &&
+ (( comp->a.registeredID->identifier.bv_val && strncmp(comp->a.registeredID->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.registeredID->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.registeredID);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentOid ( mem_op, cr, (comp->a.registeredID) );
+ };
+ }
+ return NULL;
+} /* ExtractingComponentGeneralName */
+
+int
+BDecComponentGeneralName PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentGeneralName **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ AsnLen totalElmtsLen2 = 0;
+ AsnLen elmtLen2;
+ AsnTag tagId2;
+ AsnLen totalElmtsLen3 = 0;
+ AsnLen elmtLen3;
+ AsnTag tagId3;
+ int old_mode = mode;
+ int rc;
+ ComponentGeneralName *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ switch (tagId0)
+ {
+ case MAKE_TAG_ID (CNTX, CONS, 0):
+if (BDecTag (b, &totalElmtsLen1 ) != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ (k->choiceId) = GENERALNAME_OTHERNAME;
+ rc = BDecComponentOtherName (mem_op, b, tagId1, elmtLen1, (&k->a.otherName), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.otherName)->identifier.bv_val = (k->a.otherName)->id_buf;
+ (k->a.otherName)->identifier.bv_len = strlen("otherName");
+ strcpy( (k->a.otherName)->identifier.bv_val, "otherName");
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ break;
+
+ case MAKE_TAG_ID (CNTX, CONS, 1):
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+if ((tagId1 != MAKE_TAG_ID (UNIV, PRIM, IA5STRING_TAG_CODE)) &&
+ (tagId1 != MAKE_TAG_ID (UNIV, CONS, IA5STRING_TAG_CODE)))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ (k->choiceId) = GENERALNAME_RFC822NAME;
+ rc = BDecComponentIA5String (mem_op, b, tagId1, elmtLen1, (&k->a.rfc822Name), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.rfc822Name)->identifier.bv_val = (k->a.rfc822Name)->id_buf;
+ (k->a.rfc822Name)->identifier.bv_len = strlen("rfc822Name");
+ strcpy( (k->a.rfc822Name)->identifier.bv_val, "rfc822Name");
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ break;
+
+ case MAKE_TAG_ID (CNTX, CONS, 2):
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+if ((tagId1 != MAKE_TAG_ID (UNIV, PRIM, IA5STRING_TAG_CODE)) &&
+ (tagId1 != MAKE_TAG_ID (UNIV, CONS, IA5STRING_TAG_CODE)))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ (k->choiceId) = GENERALNAME_DNSNAME;
+ rc = BDecComponentIA5String (mem_op, b, tagId1, elmtLen1, (&k->a.dNSName), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.dNSName)->identifier.bv_val = (k->a.dNSName)->id_buf;
+ (k->a.dNSName)->identifier.bv_len = strlen("dNSName");
+ strcpy( (k->a.dNSName)->identifier.bv_val, "dNSName");
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ break;
+
+ case MAKE_TAG_ID (CNTX, CONS, 3):
+if (BDecTag (b, &totalElmtsLen1 ) != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ (k->choiceId) = GENERALNAME_X400ADDRESS;
+ rc = BDecComponentORAddress (mem_op, b, tagId1, elmtLen1, (&k->a.x400Address), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.x400Address)->identifier.bv_val = (k->a.x400Address)->id_buf;
+ (k->a.x400Address)->identifier.bv_len = strlen("x400Address");
+ strcpy( (k->a.x400Address)->identifier.bv_val, "x400Address");
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ break;
+
+ case MAKE_TAG_ID (CNTX, CONS, 4):
+ (k->choiceId) = GENERALNAME_DIRECTORYNAME;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentName (mem_op, b, tagId1, elmtLen1, (&k->a.directoryName), &totalElmtsLen1, mode);
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc(b, &totalElmtsLen1 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.directoryName)->identifier.bv_val = (k->a.directoryName)->id_buf;
+ (k->a.directoryName)->identifier.bv_len = strlen("directoryName");
+ strcpy( (k->a.directoryName)->identifier.bv_val, "directoryName");
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ break;
+
+ case MAKE_TAG_ID (CNTX, CONS, 5):
+if (BDecTag (b, &totalElmtsLen1 ) != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ (k->choiceId) = GENERALNAME_EDIPARTYNAME;
+ rc = BDecComponentEDIPartyName (mem_op, b, tagId1, elmtLen1, (&k->a.ediPartyName), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.ediPartyName)->identifier.bv_val = (k->a.ediPartyName)->id_buf;
+ (k->a.ediPartyName)->identifier.bv_len = strlen("ediPartyName");
+ strcpy( (k->a.ediPartyName)->identifier.bv_val, "ediPartyName");
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ break;
+
+ case MAKE_TAG_ID (CNTX, CONS, 6):
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+if ((tagId1 != MAKE_TAG_ID (UNIV, PRIM, IA5STRING_TAG_CODE)) &&
+ (tagId1 != MAKE_TAG_ID (UNIV, CONS, IA5STRING_TAG_CODE)))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ (k->choiceId) = GENERALNAME_UNIFORMRESOURCEIDENTIFIER;
+ rc = BDecComponentIA5String (mem_op, b, tagId1, elmtLen1, (&k->a.uniformResourceIdentifier), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.uniformResourceIdentifier)->identifier.bv_val = (k->a.uniformResourceIdentifier)->id_buf;
+ (k->a.uniformResourceIdentifier)->identifier.bv_len = strlen("uniformResourceIdentifier");
+ strcpy( (k->a.uniformResourceIdentifier)->identifier.bv_val, "uniformResourceIdentifier");
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ break;
+
+ case MAKE_TAG_ID (CNTX, CONS, 7):
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+if ((tagId1 != MAKE_TAG_ID (UNIV, PRIM, OCTETSTRING_TAG_CODE)) &&
+ (tagId1 != MAKE_TAG_ID (UNIV, CONS, OCTETSTRING_TAG_CODE)))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ (k->choiceId) = GENERALNAME_IPADDRESS;
+ rc = BDecComponentOcts (mem_op, b, tagId1, elmtLen1, (&k->a.iPAddress), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.iPAddress)->identifier.bv_val = (k->a.iPAddress)->id_buf;
+ (k->a.iPAddress)->identifier.bv_len = strlen("iPAddress");
+ strcpy( (k->a.iPAddress)->identifier.bv_val, "iPAddress");
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ break;
+
+ case MAKE_TAG_ID (CNTX, CONS, 8):
+if (BDecTag (b, &totalElmtsLen1 ) != MAKE_TAG_ID (UNIV, PRIM, OID_TAG_CODE))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ (k->choiceId) = GENERALNAME_REGISTEREDID;
+ rc = BDecComponentOid (mem_op, b, tagId1, elmtLen1, (&k->a.registeredID), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.registeredID)->identifier.bv_val = (k->a.registeredID)->id_buf;
+ (k->a.registeredID)->identifier.bv_len = strlen("registeredID");
+ strcpy( (k->a.registeredID)->identifier.bv_val, "registeredID");
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ break;
+
+ default:
+ Asn1Error ("ERROR - unexpected tag in CHOICE\n");
+ return -1;
+ break;
+ } /* end switch */
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentGeneralName*) CompAlloc( mem_op, sizeof(ComponentGeneralName) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentGeneralName ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentGeneralName ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentGeneralName;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentGeneralName;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecGeneralNameContent */
+
+int
+GDecComponentGeneralName PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentGeneralName **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentGeneralName *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen2 = LocateNextGSERToken(mem_op,b,&peek_head2,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head2 != ':'){
+ Asn1Error("Missing : in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( strncmp("otherName",peek_head, strlen("otherName")) == 0){
+ (k->choiceId) = GENERALNAME_OTHERNAME;
+ rc = GDecComponentOtherName (mem_op, b, (&k->a.otherName), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.otherName)->identifier.bv_val = peek_head;
+ (k->a.otherName)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("rfc822Name",peek_head,strlen("rfc822Name")) == 0){
+ (k->choiceId) = GENERALNAME_RFC822NAME;
+ rc = GDecComponentIA5String (mem_op, b, (&k->a.rfc822Name), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.rfc822Name)->identifier.bv_val = peek_head;
+ (k->a.rfc822Name)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("dNSName",peek_head,strlen("dNSName")) == 0){
+ (k->choiceId) = GENERALNAME_DNSNAME;
+ rc = GDecComponentIA5String (mem_op, b, (&k->a.dNSName), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.dNSName)->identifier.bv_val = peek_head;
+ (k->a.dNSName)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("x400Address",peek_head,strlen("x400Address")) == 0){
+ (k->choiceId) = GENERALNAME_X400ADDRESS;
+ rc = GDecComponentORAddress (mem_op, b, (&k->a.x400Address), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.x400Address)->identifier.bv_val = peek_head;
+ (k->a.x400Address)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("directoryName",peek_head,strlen("directoryName")) == 0){
+ (k->choiceId) = GENERALNAME_DIRECTORYNAME;
+ rc = GDecComponentName (mem_op, b, (&k->a.directoryName), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.directoryName)->identifier.bv_val = peek_head;
+ (k->a.directoryName)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("ediPartyName",peek_head,strlen("ediPartyName")) == 0){
+ (k->choiceId) = GENERALNAME_EDIPARTYNAME;
+ rc = GDecComponentEDIPartyName (mem_op, b, (&k->a.ediPartyName), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.ediPartyName)->identifier.bv_val = peek_head;
+ (k->a.ediPartyName)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("uniformResourceIdentifier",peek_head,strlen("uniformResourceIdentifier")) == 0){
+ (k->choiceId) = GENERALNAME_UNIFORMRESOURCEIDENTIFIER;
+ rc = GDecComponentIA5String (mem_op, b, (&k->a.uniformResourceIdentifier), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.uniformResourceIdentifier)->identifier.bv_val = peek_head;
+ (k->a.uniformResourceIdentifier)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("iPAddress",peek_head,strlen("iPAddress")) == 0){
+ (k->choiceId) = GENERALNAME_IPADDRESS;
+ rc = GDecComponentOcts (mem_op, b, (&k->a.iPAddress), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.iPAddress)->identifier.bv_val = peek_head;
+ (k->a.iPAddress)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("registeredID",peek_head,strlen("registeredID")) == 0){
+ (k->choiceId) = GENERALNAME_REGISTEREDID;
+ rc = GDecComponentOid (mem_op, b, (&k->a.registeredID), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.registeredID)->identifier.bv_val = peek_head;
+ (k->a.registeredID)->identifier.bv_len = strLen;
+ }
+ else {
+ Asn1Error("Undefined Identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentGeneralName*) CompAlloc( mem_op, sizeof(ComponentGeneralName) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentGeneralName ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentGeneralName ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentGeneralName;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentGeneralName;
+ return LDAP_SUCCESS;
+} /* GDecGeneralNameContent */
+
+
+int
+MatchingComponentGeneralNames ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+ void* component1, *component2;
+ AsnList *v1, *v2, t_list;
+
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ v1 = &((ComponentGeneralNames*)csi_attr)->comp_list;
+ v2 = &((ComponentGeneralNames*)csi_assert)->comp_list;
+ FOR_EACH_LIST_PAIR_ELMT(component1, component2, v1, v2)
+ {
+ if( MatchingComponentGeneralName(oid, (ComponentSyntaxInfo*)component1, (ComponentSyntaxInfo*)component2) == LDAP_COMPARE_FALSE) {
+ return LDAP_COMPARE_FALSE;
+ }
+ } /* end of for */
+
+ AsnListFirst( v1 );
+ AsnListFirst( v2 );
+ if( (!component1 && component2) || (component1 && !component2))
+ return LDAP_COMPARE_FALSE;
+ else
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentGeneralNamesContent */
+
+void*
+ExtractingComponentGeneralNames ( void* mem_op, ComponentReference* cr, ComponentGeneralNames *comp )
+{
+ int count = 0;
+ int total;
+ AsnList *v = &comp->comp_list;
+ ComponentInt *k;
+ ComponentGeneralName *component;
+
+
+ switch ( cr->cr_curr->ci_type ) {
+ case LDAP_COMPREF_FROM_BEGINNING :
+ count = cr->cr_curr->ci_val.ci_from_beginning;
+ FOR_EACH_LIST_ELMT( component , v ) {
+ if( --count == 0 ) {
+ if( cr->cr_curr->ci_next == NULL )
+ return component;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentGeneralName ( mem_op, cr, component );
+ }
+ }
+ }
+ break;
+ case LDAP_COMPREF_FROM_END :
+ total = AsnListCount ( v );
+ count = cr->cr_curr->ci_val.ci_from_end;
+ count = total + count +1;
+ FOR_EACH_LIST_ELMT ( component, v ) {
+ if( --count == 0 ) {
+ if( cr->cr_curr->ci_next == NULL )
+ return component;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentGeneralName ( mem_op, cr, component );
+ }
+ }
+ }
+ break;
+ case LDAP_COMPREF_ALL :
+ return comp;
+ case LDAP_COMPREF_COUNT :
+ k = (ComponentInt*)CompAlloc( mem_op, sizeof(ComponentInt));
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ k->comp_desc->cd_tag = (-1);
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentInt;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentInt;
+ k->comp_desc->cd_extract_i = (extract_component_from_id_func*)NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_INTEGER;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentInt;
+ k->value = AsnListCount(v);
+ return k;
+ default :
+ return NULL;
+ }
+} /* ExtractingComponentGeneralNames */
+
+int
+BDecComponentGeneralNames PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentGeneralNames **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentGeneralNames *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ AsnListInit(&k->comp_list,sizeof(ComponentGeneralName));
+ for (totalElmtsLen1 = 0; (totalElmtsLen1 < elmtLen0) || (elmtLen0 == INDEFINITE_LEN);)
+ {
+ ComponentGeneralName **tmpVar;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((tagId1 == EOC_TAG_ID) && (elmtLen0 == INDEFINITE_LEN))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ break; /* got EOC so can exit this SET OF/SEQ OF's for loop*/
+ }
+ elmtLen1 = BDecLen (b, &totalElmtsLen1);
+ tmpVar = (ComponentGeneralName**) CompAsnListAppend (mem_op,&k->comp_list);
+ rc = BDecComponentGeneralName (mem_op, b, tagId1, elmtLen1, tmpVar, &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ } /* end of for */
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentGeneralNames*) CompAlloc( mem_op, sizeof(ComponentGeneralNames) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentGeneralNames ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentGeneralNames ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentGeneralNames;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentGeneralNames;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecGeneralNamesContent */
+
+int
+GDecComponentGeneralNames PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentGeneralNames **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentGeneralNames *k,*t, c_temp;
+
+
+ int ElmtsLen1;
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ AsnListInit( &k->comp_list, sizeof( ComponentGeneralName ) );
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b, &peek_head, GSER_PEEK)) ){
+ Asn1Error("Error during Reading { in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ for (ElmtsLen1 = 0; ElmtsLen1 >= INDEFINITE_LEN; ElmtsLen1++)
+ {
+ ComponentGeneralName **tmpVar;
+ if( !(strLen = LocateNextGSERToken(mem_op,b, &peek_head, GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading{ in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head == '}') break;
+ if( !(*peek_head == '{' || *peek_head ==',') ) {
+ return LDAP_PROTOCOL_ERROR;
+ }
+ tmpVar = (ComponentGeneralName**) CompAsnListAppend (mem_op, &k->comp_list);
+ if ( tmpVar == NULL ) {
+ Asn1Error("Error during Reading{ in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ rc = GDecComponentGeneralName (mem_op, b, tmpVar, bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ } /* end of for */
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentGeneralNames*) CompAlloc( mem_op, sizeof(ComponentGeneralNames) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentGeneralNames ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentGeneralNames ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentGeneralNames;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentGeneralNames;
+ return LDAP_SUCCESS;
+} /* GDecGeneralNamesContent */
+
+
+int
+MatchingComponentAuthorityKeyIdentifier ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentKeyIdentifier ( oid, (ComponentSyntaxInfo*)&((ComponentAuthorityKeyIdentifier*)csi_attr)->keyIdentifier, (ComponentSyntaxInfo*)&((ComponentAuthorityKeyIdentifier*)csi_assert)->keyIdentifier );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ if(COMPONENTNOT_NULL( ((ComponentAuthorityKeyIdentifier*)csi_attr)->authorityCertIssuer ) ) {
+ rc = MatchingComponentGeneralNames ( oid, (ComponentSyntaxInfo*)((ComponentAuthorityKeyIdentifier*)csi_attr)->authorityCertIssuer, (ComponentSyntaxInfo*)((ComponentAuthorityKeyIdentifier*)csi_assert)->authorityCertIssuer );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ }
+ if(COMPONENTNOT_NULL( ((ComponentAuthorityKeyIdentifier*)csi_attr)->authorityCertSerialNumber ) ) {
+ rc = MatchingComponentCertificateSerialNumber ( oid, (ComponentSyntaxInfo*)((ComponentAuthorityKeyIdentifier*)csi_attr)->authorityCertSerialNumber, (ComponentSyntaxInfo*)((ComponentAuthorityKeyIdentifier*)csi_assert)->authorityCertSerialNumber );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ }
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentAuthorityKeyIdentifier */
+
+void*
+ExtractingComponentAuthorityKeyIdentifier ( void* mem_op, ComponentReference* cr, ComponentAuthorityKeyIdentifier *comp )
+{
+
+ if ( ( comp->keyIdentifier.identifier.bv_val && strncmp(comp->keyIdentifier.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->keyIdentifier.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->keyIdentifier;
+ else
+ return NULL;
+ }
+ if ( ( comp->authorityCertIssuer->identifier.bv_val && strncmp(comp->authorityCertIssuer->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->authorityCertIssuer->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->authorityCertIssuer;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentGeneralNames ( mem_op, cr, comp->authorityCertIssuer );
+ }
+ }
+ if ( ( comp->authorityCertSerialNumber->identifier.bv_val && strncmp(comp->authorityCertSerialNumber->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->authorityCertSerialNumber->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->authorityCertSerialNumber;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentCertificateSerialNumber ( mem_op, cr, comp->authorityCertSerialNumber );
+ }
+ }
+ return NULL;
+} /* ExtractingComponentAuthorityKeyIdentifier */
+
+int
+BDecComponentAuthorityKeyIdentifier PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentAuthorityKeyIdentifier **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ AsnLen totalElmtsLen2 = 0;
+ AsnLen elmtLen2;
+ AsnTag tagId2;
+ int old_mode = mode;
+ int rc;
+ ComponentAuthorityKeyIdentifier *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ if ((elmtLen0 != INDEFINITE_LEN) && (totalElmtsLen1 == elmtLen0))
+ seqDone = TRUE;
+ else
+ {
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((elmtLen0 == INDEFINITE_LEN) && (tagId1 == EOC_TAG_ID))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ seqDone = TRUE;
+ }
+ }
+
+ if ((!seqDone) && ((tagId1 == MAKE_TAG_ID (CNTX, PRIM, 0)) ||
+(tagId1 == MAKE_TAG_ID (CNTX, CONS, 0))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentKeyIdentifier (mem_op, b, tagId1, elmtLen1, (&k->keyIdentifier), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->keyIdentifier)->identifier.bv_val = (&k->keyIdentifier)->id_buf;
+ (&k->keyIdentifier)->identifier.bv_len = strlen("keyIdentifier");
+ strcpy( (&k->keyIdentifier)->identifier.bv_val, "keyIdentifier");
+ if ((elmtLen0 != INDEFINITE_LEN) && (totalElmtsLen1 == elmtLen0))
+ seqDone = TRUE;
+ else
+ {
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((elmtLen0 == INDEFINITE_LEN) && (tagId1 == EOC_TAG_ID))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ seqDone = TRUE;
+ }
+ }
+ }
+
+ if ((!seqDone) && ((tagId1 == MAKE_TAG_ID (CNTX, CONS, 1))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentGeneralNames (mem_op, b, tagId1, elmtLen1,
+(&k->authorityCertIssuer), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->authorityCertIssuer)->identifier.bv_val = (k->authorityCertIssuer)->id_buf;
+ (k->authorityCertIssuer)->identifier.bv_len = strlen("authorityCertIssuer");
+ strcpy( (k->authorityCertIssuer)->identifier.bv_val, "authorityCertIssuer");
+ if ((elmtLen0 != INDEFINITE_LEN) && (totalElmtsLen1 == elmtLen0))
+ seqDone = TRUE;
+ else
+ {
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((elmtLen0 == INDEFINITE_LEN) && (tagId1 == EOC_TAG_ID))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ seqDone = TRUE;
+ }
+ }
+ }
+
+ if ((!seqDone) && ((tagId1 == MAKE_TAG_ID (CNTX, PRIM, 2))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentCertificateSerialNumber (mem_op, b, tagId1, elmtLen1, (&k->authorityCertSerialNumber), &totalElmtsLen1, DEC_ALLOC_MODE_0 ); if ( rc != LDAP_SUCCESS ) return rc;
+ (k->authorityCertSerialNumber)->identifier.bv_val = (k->authorityCertSerialNumber)->id_buf;
+ (k->authorityCertSerialNumber)->identifier.bv_len = strlen("authorityCertSerialNumber");
+ strcpy( (k->authorityCertSerialNumber)->identifier.bv_val, "authorityCertSerialNumber");
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentAuthorityKeyIdentifier*) CompAlloc( mem_op, sizeof(ComponentAuthorityKeyIdentifier) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentAuthorityKeyIdentifier ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentAuthorityKeyIdentifier ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentAuthorityKeyIdentifier;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentAuthorityKeyIdentifier;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecAuthorityKeyIdentifier*/
+
+int
+GDecComponentAuthorityKeyIdentifier PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentAuthorityKeyIdentifier **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentAuthorityKeyIdentifier *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "keyIdentifier", strlen("keyIdentifier") ) == 0 ) {
+ rc = GDecComponentKeyIdentifier (mem_op, b, (&k->keyIdentifier), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->keyIdentifier)->identifier.bv_val = peek_head;
+ (&k->keyIdentifier)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "authorityCertIssuer", strlen("authorityCertIssuer") ) == 0 ) {
+ rc = GDecComponentGeneralNames (mem_op, b, (&k->authorityCertIssuer), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->authorityCertIssuer)->identifier.bv_val = peek_head;
+ ( k->authorityCertIssuer)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "authorityCertSerialNumber", strlen("authorityCertSerialNumber") ) == 0 ) {
+ rc = GDecComponentCertificateSerialNumber (mem_op, b, (&k->authorityCertSerialNumber), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->authorityCertSerialNumber)->identifier.bv_val = peek_head;
+ ( k->authorityCertSerialNumber)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentAuthorityKeyIdentifier*) CompAlloc( mem_op, sizeof(ComponentAuthorityKeyIdentifier) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentAuthorityKeyIdentifier ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentAuthorityKeyIdentifier ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentAuthorityKeyIdentifier;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentAuthorityKeyIdentifier;
+ return LDAP_SUCCESS;
+} /* GDecAuthorityKeyIdentifier*/
+
+
diff --git a/contrib/slapd-modules/comp_match/authorityKeyIdentifier.h b/contrib/slapd-modules/comp_match/authorityKeyIdentifier.h
new file mode 100644
index 0000000..5fa4ab5
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/authorityKeyIdentifier.h
@@ -0,0 +1,327 @@
+
+#include "asn-incl.h"
+/*
+ * authorityKeyIdentifier.h
+ * "AuthorityKeyIdentifierDefinition" ASN.1 module encode/decode/extracting/matching/free C src.
+ * This file was generated by modified eSMACC compiler Sat Dec 11 10:15:39 2004
+ * The generated files are strongly encouraged to be
+ * compiled as a module for OpenLDAP Software
+ */
+
+#ifndef _authorityKeyIdentifier_h_
+#define _authorityKeyIdentifier_h_
+
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef _WIN32
+#pragma warning( disable : 4101 )
+#endif
+#include "componentlib.h"
+typedef ComponentOcts ComponentKeyIdentifier; /* OCTET STRING */
+
+#define MatchingComponentKeyIdentifier MatchingComponentOcts
+
+#define ExtractingComponentKeyIdentifier ExtractingComponentOcts
+
+#define BDecComponentKeyIdentifier BDecComponentOcts
+
+#define GDecComponentKeyIdentifier GDecComponentOcts
+
+
+typedef ComponentInt ComponentCertificateSerialNumber; /* INTEGER */
+
+#define MatchingComponentCertificateSerialNumber MatchingComponentInt
+
+#define ExtractingComponentCertificateSerialNumber ExtractingComponentInt
+
+#define BDecComponentCertificateSerialNumber BDecComponentInt
+
+#define GDecComponentCertificateSerialNumber GDecComponentInt
+
+
+typedef struct OtherName /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentOid type_id; /* OBJECT IDENTIFIER */
+ ComponentAnyDefinedBy value; /* [0] EXPLICIT ANY DEFINED BY type-id */
+} ComponentOtherName;
+
+int MatchingComponentOtherName PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentOtherName PROTO (( void* mem_op, ComponentReference *cr, ComponentOtherName *comp ));
+
+
+int BDecComponentOtherName PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentOtherName **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentOtherName PROTO (( void* mem_op, GenBuf * b, ComponentOtherName **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct ORAddress /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentOid type_id; /* OBJECT IDENTIFIER */
+ ComponentAnyDefinedBy value; /* ANY DEFINED BY type-id */
+ ComponentOcts extension; /* OCTET STRING OPTIONAL */
+} ComponentORAddress;
+
+int MatchingComponentORAddress PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentORAddress PROTO (( void* mem_op, ComponentReference *cr, ComponentORAddress *comp ));
+
+
+int BDecComponentORAddress PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentORAddress **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentORAddress PROTO (( void* mem_op, GenBuf * b, ComponentORAddress **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct AttributeTypeAndValue /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentOid type; /* OBJECT IDENTIFIER */
+ ComponentAnyDefinedBy value; /* ANY DEFINED BY type */
+} ComponentAttributeTypeAndValue;
+
+int MatchingComponentAttributeTypeAndValue PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentAttributeTypeAndValue PROTO (( void* mem_op, ComponentReference *cr, ComponentAttributeTypeAndValue *comp ));
+
+
+int BDecComponentAttributeTypeAndValue PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentAttributeTypeAndValue **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentAttributeTypeAndValue PROTO (( void* mem_op, GenBuf * b, ComponentAttributeTypeAndValue **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct DirectoryString /* CHOICE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ enum DirectoryStringChoiceId
+ {
+ DIRECTORYSTRING_TELETEXSTRING,
+ DIRECTORYSTRING_PRINTABLESTRING,
+ DIRECTORYSTRING_UNIVERSALSTRING,
+ DIRECTORYSTRING_UTF8STRING,
+ DIRECTORYSTRING_BMPSTRING
+ } choiceId;
+ union DirectoryStringChoiceUnion
+ {
+ ComponentTeletexString* teletexString; /* TeletexString SIZE 1..MAX */
+ ComponentPrintableString* printableString; /* PrintableString SIZE 1..MAX */
+ ComponentUniversalString* universalString; /* UniversalString SIZE 1..MAX */
+ ComponentUTF8String* utf8String; /* UTF8String SIZE 1..MAX */
+ ComponentBMPString* bmpString; /* BMPString SIZE 1..MAX */
+ } a;
+} ComponentDirectoryString;
+
+int MatchingComponentDirectoryString PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentDirectoryString PROTO (( void* mem_op, ComponentReference *cr, ComponentDirectoryString *comp ));
+
+
+int BDecComponentDirectoryString PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentDirectoryString **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentDirectoryString PROTO (( void* mem_op, GenBuf * b, ComponentDirectoryString **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct EDIPartyName /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentDirectoryString* nameAssigner; /* [0] DirectoryString OPTIONAL */
+ ComponentDirectoryString* partyName; /* [1] DirectoryString */
+} ComponentEDIPartyName;
+
+int MatchingComponentEDIPartyName PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentEDIPartyName PROTO (( void* mem_op, ComponentReference *cr, ComponentEDIPartyName *comp ));
+
+
+int BDecComponentEDIPartyName PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentEDIPartyName **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentEDIPartyName PROTO (( void* mem_op, GenBuf * b, ComponentEDIPartyName **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef ComponentList ComponentRelativeDistinguishedName; /* SET OF AttributeTypeAndValue */
+
+int MatchingComponentRelativeDistinguishedName PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentRelativeDistinguishedName PROTO (( void* mem_op, ComponentReference *cr, ComponentRelativeDistinguishedName *comp ));
+
+
+int BDecComponentRelativeDistinguishedName PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentRelativeDistinguishedName **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentRelativeDistinguishedName PROTO (( void* mem_op, GenBuf * b, ComponentRelativeDistinguishedName **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef ComponentList ComponentRDNSequence; /* SEQUENCE OF RelativeDistinguishedName */
+
+int MatchingComponentRDNSequence PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentRDNSequence PROTO (( void* mem_op, ComponentReference *cr, ComponentRDNSequence *comp ));
+
+
+int BDecComponentRDNSequence PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentRDNSequence **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentRDNSequence PROTO (( void* mem_op, GenBuf * b, ComponentRDNSequence **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct Name /* CHOICE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ enum NameChoiceId
+ {
+ NAME_RDNSEQUENCE
+ } choiceId;
+ union NameChoiceUnion
+ {
+ ComponentRDNSequence* rdnSequence; /* RDNSequence */
+ } a;
+} ComponentName;
+
+int MatchingComponentName PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentName PROTO (( void* mem_op, ComponentReference *cr, ComponentName *comp ));
+
+
+int BDecComponentName PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentName **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentName PROTO (( void* mem_op, GenBuf * b, ComponentName **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct GeneralName /* CHOICE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ enum GeneralNameChoiceId
+ {
+ GENERALNAME_OTHERNAME,
+ GENERALNAME_RFC822NAME,
+ GENERALNAME_DNSNAME,
+ GENERALNAME_X400ADDRESS,
+ GENERALNAME_DIRECTORYNAME,
+ GENERALNAME_EDIPARTYNAME,
+ GENERALNAME_UNIFORMRESOURCEIDENTIFIER,
+ GENERALNAME_IPADDRESS,
+ GENERALNAME_REGISTEREDID
+ } choiceId;
+ union GeneralNameChoiceUnion
+ {
+ ComponentOtherName* otherName; /* [0] OtherName */
+ ComponentIA5String* rfc822Name; /* [1] IA5String */
+ ComponentIA5String* dNSName; /* [2] IA5String */
+ ComponentORAddress* x400Address; /* [3] ORAddress */
+ ComponentName* directoryName; /* [4] Name */
+ ComponentEDIPartyName* ediPartyName; /* [5] EDIPartyName */
+ ComponentIA5String* uniformResourceIdentifier; /* [6] IA5String */
+ ComponentOcts* iPAddress; /* [7] OCTET STRING */
+ ComponentOid* registeredID; /* [8] OBJECT IDENTIFIER */
+ } a;
+} ComponentGeneralName;
+
+int MatchingComponentGeneralName PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentGeneralName PROTO (( void* mem_op, ComponentReference *cr, ComponentGeneralName *comp ));
+
+
+int BDecComponentGeneralName PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentGeneralName **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentGeneralName PROTO (( void* mem_op, GenBuf * b, ComponentGeneralName **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef ComponentList ComponentGeneralNames; /* SEQUENCE SIZE 1..MAX OF GeneralName */
+
+int MatchingComponentGeneralNames PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentGeneralNames PROTO (( void* mem_op, ComponentReference *cr, ComponentGeneralNames *comp ));
+
+
+int BDecComponentGeneralNames PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentGeneralNames **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentGeneralNames PROTO (( void* mem_op, GenBuf * b, ComponentGeneralNames **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct AuthorityKeyIdentifier /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentKeyIdentifier keyIdentifier; /* [0] KeyIdentifier OPTIONAL */
+ ComponentGeneralNames* authorityCertIssuer; /* [1] GeneralNames OPTIONAL */
+ ComponentCertificateSerialNumber* authorityCertSerialNumber; /* [2] CertificateSerialNumber OPTIONAL */
+} ComponentAuthorityKeyIdentifier;
+
+int MatchingComponentAuthorityKeyIdentifier PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentAuthorityKeyIdentifier PROTO (( void* mem_op, ComponentReference *cr, ComponentAuthorityKeyIdentifier *comp ));
+
+
+int BDecComponentAuthorityKeyIdentifier PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentAuthorityKeyIdentifier **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentAuthorityKeyIdentifier PROTO (( void* mem_op, GenBuf * b, ComponentAuthorityKeyIdentifier **v, AsnLen *bytesDecoded, int mode));
+
+
+
+/* ========== Object Declarations ========== */
+
+
+/* ========== Object Set Declarations ========== */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#endif /* conditional include of authorityKeyIdentifier.h */
diff --git a/contrib/slapd-modules/comp_match/certificate.asn1 b/contrib/slapd-modules/comp_match/certificate.asn1
new file mode 100644
index 0000000..db81897
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/certificate.asn1
@@ -0,0 +1,175 @@
+AuthenticationFramework {joint-iso-itu-t ds(5) module(1) authenticationFramework(7) 4} DEFINITIONS ::=
+BEGIN
+-- based on RFC 3280 and X.509
+
+Certificate ::= SEQUENCE {
+ toBeSigned TBSCertificate,
+ signatureAlgorithm AlgorithmIdentifier,
+ signature BIT STRING
+}
+
+TBSCertificate ::= SEQUENCE {
+ version [0] Version DEFAULT v1,
+ serialNumber CertificateSerialNumber,
+ signature AlgorithmIdentifier,
+ issuer Name,
+ validity Validity,
+ subject Name,
+ subjectPublicKeyInfo SubjectPublicKeyInfo,
+ issuerUniqueIdentifier [1] IMPLICIT UniqueIdentifier OPTIONAL,
+ -- if present, version shall be v2 or v3
+ subjectUniqueIdentifier [2] IMPLICIT UniqueIdentifier OPTIONAL,
+ -- if present, version shall be v2 or v3
+ extensions [3] Extensions OPTIONAL
+ -- If present, version shall be v3 -- }
+
+Version ::= INTEGER { v1(0), v2(1), v3(2) }
+
+CertificateSerialNumber ::= INTEGER
+
+AlgorithmIdentifier ::= SEQUENCE {
+ algorithm OBJECT IDENTIFIER,
+ parameters ANY DEFINED BY algorithm OPTIONAL -- DSA, SHA-1--
+}
+
+Name ::= CHOICE {
+ rdnSequence RDNSequence }
+
+RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+
+RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
+
+AttributeTypeAndValue ::= SEQUENCE {
+ type AttributeType,
+ value ANY DEFINED BY type}
+
+AttributeType ::= OBJECT IDENTIFIER
+
+Validity ::= SEQUENCE {
+ notBefore Time,
+ notAfter Time }
+
+UniqueIdentifier ::= BIT STRING
+
+SubjectPublicKeyInfo ::= SEQUENCE {
+ algorithm AlgorithmIdentifier,
+ subjectPublicKey BIT STRING }
+
+Time ::= CHOICE {
+ utcTime UTCTime,
+ generalizedTime GeneralizedTime }
+
+Extensions ::= SEQUENCE SIZE(1..MAX) OF Extension
+
+Extension ::= SEQUENCE {
+ extnID OBJECT IDENTIFIER,
+ critical BOOLEAN DEFAULT FALSE,
+ extnValue OCTET STRING
+-- contains a DER encoding of a value of type &ExtnType
+-- for the extension object identified by extnId --
+}
+
+nullOid OBJECT-TYPE
+ SYNTAX NULL
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 1 2 840 113549 1 1 4 }
+
+nullOid2 OBJECT-TYPE
+ SYNTAX NULL
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 1 2 840 113549 1 1 1 }
+
+nullOid3 OBJECT-TYPE
+ SYNTAX NULL
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 1 2 840 113549 1 1 5 }
+
+printableStringOid OBJECT-TYPE
+ SYNTAX PrintableString
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 4 3 }
+
+printableStringOid2 OBJECT-TYPE
+ SYNTAX PrintableString
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 4 6 }
+
+printableStringOid3 OBJECT-TYPE
+ SYNTAX PrintableString
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 4 7 }
+
+printableStringOid4 OBJECT-TYPE
+ SYNTAX PrintableString
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 4 8 }
+
+printableStringOid5 OBJECT-TYPE
+ SYNTAX PrintableString
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 4 10 }
+
+printableStringOid6 OBJECT-TYPE
+ SYNTAX PrintableString
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 4 11 }
+
+printableStringOid7 OBJECT-TYPE
+ SYNTAX PrintableString
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 0 9 2342 19200300 100 1 3 }
+
+
+iA5StringOid OBJECT-TYPE
+ SYNTAX IA5String
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 1 2 840 113549 1 9 1 }
+
+octetStringOid OBJECT-TYPE
+ SYNTAX OCTET STRING
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 29 19 }
+
+octetStringOid2 OBJECT-TYPE
+ SYNTAX OCTET STRING
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 16 840 1 113730 1 13 }
+
+octetStringOid3 OBJECT-TYPE
+ SYNTAX OCTET STRING
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 29 14 }
+
+octetStringOid4 OBJECT-TYPE
+ SYNTAX OCTET STRING
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 29 21 }
+
+octetStringOid5 OBJECT-TYPE
+ SYNTAX OCTET STRING
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 29 20 }
+
+octetStringOid7 OBJECT-TYPE
+ SYNTAX OCTET STRING
+ ACCESS read-write
+ STATUS mandatory
+ ::= { 2 5 29 28 }
+
+END
diff --git a/contrib/slapd-modules/comp_match/certificate.c b/contrib/slapd-modules/comp_match/certificate.c
new file mode 100644
index 0000000..8b58bdb
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/certificate.c
@@ -0,0 +1,3249 @@
+/*
+ * certificate.c
+ * "AuthenticationFramework" ASN.1 module encode/decode/extracting/matching/free C src.
+ * This file was generated by modified eSMACC compiler Sat Dec 11 11:22:49 2004
+ * The generated files are supposed to be compiled as a module for OpenLDAP Software
+ */
+
+#include "certificate.h"
+
+BDecComponentCertificateTop( void* mem_op, GenBuf* b, void **v, AsnLen* bytesDecoded,int mode) {
+ AsnTag tag;
+ AsnLen elmtLen;
+
+ tag = BDecTag ( b, bytesDecoded );
+ elmtLen = BDecLen ( b, bytesDecoded );
+ if ( elmtLen <= 0 ) return (-1);
+ if ( tag != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE) ) {
+ return (-1);
+ }
+
+ return BDecComponentCertificate( mem_op, b, tag, elmtLen, (ComponentCertificate**)v,(AsnLen*)bytesDecoded, mode );
+}
+
+void init_module_AuthenticationFramework() {
+ /* Register Certificate OID and its decoder */
+ InstallOidDecoderMapping( "2.5.4.36", NULL,
+ GDecComponentCertificate,
+ BDecComponentCertificateTop,
+ ExtractingComponentCertificate,
+ MatchingComponentCertificate );
+ InitAnyAuthenticationFramework();
+}
+
+void InitAnyAuthenticationFramework()
+{
+ AsnOid oid0 ={ 9, "\52\206\110\206\367\15\1\1\4" };
+ AsnOid oid1 ={ 9, "\52\206\110\206\367\15\1\1\1" };
+ AsnOid oid2 ={ 9, "\52\206\110\206\367\15\1\1\5" };
+ AsnOid oid3 ={ 3, "\125\4\3" };
+ AsnOid oid4 ={ 3, "\125\4\6" };
+ AsnOid oid5 ={ 3, "\125\4\7" };
+ AsnOid oid6 ={ 3, "\125\4\10" };
+ AsnOid oid7 ={ 3, "\125\4\12" };
+ AsnOid oid8 ={ 3, "\125\4\13" };
+ AsnOid oid9 ={ 10, "\11\222\46\211\223\362\54\144\1\3" };
+ AsnOid oid10 ={ 9, "\52\206\110\206\367\15\1\11\1" };
+ AsnOid oid11 ={ 3, "\125\35\23" };
+ AsnOid oid12 ={ 9, "\140\206\110\1\206\370\102\1\15" };
+ AsnOid oid13 ={ 3, "\125\35\16" };
+ AsnOid oid14 ={ 3, "\125\35\25" };
+ AsnOid oid15 ={ 3, "\125\35\24" };
+ AsnOid oid17 ={ 3, "\125\35\34" };
+
+
+ InstallAnyByComponentOid (nullOid_ANY_ID, &oid0, sizeof (ComponentNull), (EncodeFcn)BEncAsnNull, (gser_decoder_func*)GDecComponentNull, (ber_tag_decoder_func*)BDecComponentNullTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentNull,(FreeFcn)FreeComponentNull, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (nullOid2_ANY_ID, &oid1, sizeof (ComponentNull), (EncodeFcn)BEncAsnNull, (gser_decoder_func*)GDecComponentNull, (ber_tag_decoder_func*)BDecComponentNullTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentNull,(FreeFcn)FreeComponentNull, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (nullOid3_ANY_ID, &oid2, sizeof (ComponentNull), (EncodeFcn)BEncAsnNull, (gser_decoder_func*)GDecComponentNull, (ber_tag_decoder_func*)BDecComponentNullTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentNull,(FreeFcn)FreeComponentNull, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (printableStringOid_ANY_ID, &oid3, sizeof (ComponentPrintableString), (EncodeFcn)BEncPrintableString, (gser_decoder_func*)GDecComponentPrintableString, (ber_tag_decoder_func*)BDecComponentPrintableStringTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentPrintableString,(FreeFcn)FreeComponentPrintableString, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (printableStringOid2_ANY_ID, &oid4, sizeof (ComponentPrintableString), (EncodeFcn)BEncPrintableString, (gser_decoder_func*)GDecComponentPrintableString, (ber_tag_decoder_func*)BDecComponentPrintableStringTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentPrintableString,(FreeFcn)FreeComponentPrintableString, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (printableStringOid3_ANY_ID, &oid5, sizeof (ComponentPrintableString), (EncodeFcn)BEncPrintableString, (gser_decoder_func*)GDecComponentPrintableString, (ber_tag_decoder_func*)BDecComponentPrintableStringTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentPrintableString,(FreeFcn)FreeComponentPrintableString, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (printableStringOid4_ANY_ID, &oid6, sizeof (ComponentPrintableString), (EncodeFcn)BEncPrintableString, (gser_decoder_func*)GDecComponentPrintableString, (ber_tag_decoder_func*)BDecComponentPrintableStringTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentPrintableString,(FreeFcn)FreeComponentPrintableString, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (printableStringOid5_ANY_ID, &oid7, sizeof (ComponentPrintableString), (EncodeFcn)BEncPrintableString, (gser_decoder_func*)GDecComponentPrintableString, (ber_tag_decoder_func*)BDecComponentPrintableStringTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentPrintableString,(FreeFcn)FreeComponentPrintableString, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (printableStringOid6_ANY_ID, &oid8, sizeof (ComponentPrintableString), (EncodeFcn)BEncPrintableString, (gser_decoder_func*)GDecComponentPrintableString, (ber_tag_decoder_func*)BDecComponentPrintableStringTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentPrintableString,(FreeFcn)FreeComponentPrintableString, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (printableStringOid7_ANY_ID, &oid9, sizeof (ComponentTeletexString), (EncodeFcn)BEncTeletexString, (gser_decoder_func*)GDecComponentTeletexString, (ber_tag_decoder_func*)BDecComponentTeletexStringTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentTeletexString,(FreeFcn)FreeComponentTeletexString, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (iA5StringOid_ANY_ID, &oid10, sizeof (ComponentIA5String), (EncodeFcn)BEncIA5String, (gser_decoder_func*)GDecComponentIA5String, (ber_tag_decoder_func*)BDecComponentIA5StringTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentIA5String,(FreeFcn)FreeComponentIA5String, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (octetStringOid_ANY_ID, &oid11, sizeof (ComponentOcts), (EncodeFcn)BEncAsnOcts, (gser_decoder_func*)GDecComponentOcts, (ber_tag_decoder_func*)BDecComponentOctsTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentOcts,(FreeFcn)FreeComponentOcts, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (octetStringOid2_ANY_ID, &oid12, sizeof (ComponentOcts), (EncodeFcn)BEncAsnOcts, (gser_decoder_func*)GDecComponentOcts, (ber_tag_decoder_func*)BDecComponentOctsTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentOcts,(FreeFcn)FreeComponentOcts, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (octetStringOid3_ANY_ID, &oid13, sizeof (ComponentOcts), (EncodeFcn)BEncAsnOcts, (gser_decoder_func*)GDecComponentOcts, (ber_tag_decoder_func*)BDecComponentOctsTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentOcts,(FreeFcn)FreeComponentOcts, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (octetStringOid4_ANY_ID, &oid14, sizeof (ComponentOcts), (EncodeFcn)BEncAsnOcts, (gser_decoder_func*)GDecComponentOcts, (ber_tag_decoder_func*)BDecComponentOctsTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentOcts,(FreeFcn)FreeComponentOcts, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (octetStringOid5_ANY_ID, &oid15, sizeof (ComponentOcts), (EncodeFcn)BEncAsnOcts, (gser_decoder_func*)GDecComponentOcts, (ber_tag_decoder_func*)BDecComponentOctsTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentOcts,(FreeFcn)FreeComponentOcts, (PrintFcn)NULL);
+
+ InstallAnyByComponentOid (octetStringOid7_ANY_ID, &oid17, sizeof (ComponentOcts), (EncodeFcn)BEncAsnOcts, (gser_decoder_func*)GDecComponentOcts, (ber_tag_decoder_func*)BDecComponentOctsTag, (ExtractFcn)NULL,(MatchFcn)MatchingComponentOcts,(FreeFcn)FreeComponentOcts, (PrintFcn)NULL);
+
+} /* InitAnyAuthenticationFramework */
+
+int
+MatchingComponentAlgorithmIdentifier ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentOid ( oid, (ComponentSyntaxInfo*)&((ComponentAlgorithmIdentifier*)csi_attr)->algorithm, (ComponentSyntaxInfo*)&((ComponentAlgorithmIdentifier*)csi_assert)->algorithm );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = SetAnyTypeByComponentOid ((ComponentSyntaxInfo*)&((ComponentAlgorithmIdentifier*)csi_attr)->parameters, (&((ComponentAlgorithmIdentifier*)csi_attr)->algorithm));
+ rc = MatchingComponentAnyDefinedBy ( oid, (ComponentAny*)&((ComponentAlgorithmIdentifier*)csi_attr)->parameters, (ComponentAny*)&((ComponentAlgorithmIdentifier*)csi_assert)->parameters);
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentAlgorithmIdentifier */
+
+void*
+ExtractingComponentAlgorithmIdentifier ( void* mem_op, ComponentReference* cr, ComponentAlgorithmIdentifier *comp )
+{
+
+ if ( ( comp->algorithm.identifier.bv_val && strncmp(comp->algorithm.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->algorithm.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->algorithm;
+ else
+ return NULL;
+ }
+ if ( ( comp->parameters.identifier.bv_val && strncmp(comp->parameters.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->parameters.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->parameters;
+ else if ( cr->cr_curr->ci_next->ci_type == LDAP_COMPREF_CONTENT) {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return &comp->parameters;
+ } else {
+ return NULL;
+ }
+ }
+ return NULL;
+} /* ExtractingComponentAlgorithmIdentifier */
+
+int
+BDecComponentAlgorithmIdentifier PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentAlgorithmIdentifier **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentAlgorithmIdentifier *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, OID_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentOid (mem_op, b, tagId1, elmtLen1, (&k->algorithm), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->algorithm)->identifier.bv_val = (&k->algorithm)->id_buf;
+ (&k->algorithm)->identifier.bv_len = strlen("algorithm");
+ strcpy( (&k->algorithm)->identifier.bv_val, "algorithm");
+ if ((elmtLen0 != INDEFINITE_LEN) && (totalElmtsLen1 == elmtLen0))
+ seqDone = TRUE;
+ else
+ {
+ tagId1 = BufPeekByte (b);
+ if ((elmtLen0 == INDEFINITE_LEN) && (tagId1 == EOC_TAG_ID))
+ {
+ BDecEoc (b, &totalElmtsLen1 );
+ seqDone = TRUE;
+ }
+ }
+ }
+ else
+ return -1;
+
+
+
+ if (!seqDone) {
+ rc = SetAnyTypeByComponentOid ((&k->parameters), (&k->algorithm));
+ rc = BDecComponentAnyDefinedBy (mem_op,b, (&k->parameters), &totalElmtsLen1, mode );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->parameters)->identifier.bv_val = (&k->parameters)->id_buf;
+ (&k->parameters)->identifier.bv_len = strlen("parameters");
+ strcpy( (&k->parameters)->identifier.bv_val, "parameters");
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentAlgorithmIdentifier*) CompAlloc( mem_op, sizeof(ComponentAlgorithmIdentifier) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentAlgorithmIdentifier ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentAlgorithmIdentifier ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentAlgorithmIdentifier;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentAlgorithmIdentifier;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecAlgorithmIdentifier*/
+
+int
+GDecComponentAlgorithmIdentifier PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentAlgorithmIdentifier **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentAlgorithmIdentifier *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "algorithm", strlen("algorithm") ) == 0 ) {
+ rc = GDecComponentOid (mem_op, b, (&k->algorithm), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->algorithm)->identifier.bv_val = peek_head;
+ (&k->algorithm)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "parameters", strlen("parameters") ) == 0 ) {
+ rc = rc = SetAnyTypeByComponentOid ((&k->parameters), (&k->algorithm));
+ rc = GDecComponentAnyDefinedBy (mem_op, b, (&k->parameters), bytesDecoded, mode );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->parameters)->identifier.bv_val = peek_head;
+ (&k->parameters)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentAlgorithmIdentifier*) CompAlloc( mem_op, sizeof(ComponentAlgorithmIdentifier) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentAlgorithmIdentifier ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentAlgorithmIdentifier ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentAlgorithmIdentifier;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentAlgorithmIdentifier;
+ return LDAP_SUCCESS;
+} /* GDecAlgorithmIdentifier*/
+
+
+int
+MatchingComponentTime ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+ ComponentTime *v1, *v2;
+
+
+ v1 = (ComponentTime*)csi_attr;
+ v2 = (ComponentTime*)csi_assert;
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ if( (v1->choiceId != v2->choiceId ) )
+ return LDAP_COMPARE_FALSE;
+ switch( v1->choiceId )
+ {
+ case TIME_UTCTIME :
+ rc = MatchingComponentUTCTime ( oid, (ComponentSyntaxInfo*)(v1->a.utcTime), (ComponentSyntaxInfo*)(v2->a.utcTime) );
+ break;
+ case TIME_GENERALIZEDTIME :
+ rc = MatchingComponentGeneralizedTime ( oid, (ComponentSyntaxInfo*)(v1->a.generalizedTime), (ComponentSyntaxInfo*)(v2->a.generalizedTime) );
+ break;
+ default :
+ return LDAP_PROTOCOL_ERROR;
+ }
+ return rc;
+} /* BMatchingComponentTimeContent */
+
+void*
+ExtractingComponentTime ( void* mem_op, ComponentReference* cr, ComponentTime *comp )
+{
+
+
+ if( (comp->choiceId) == TIME_UTCTIME &&
+ (( comp->a.utcTime->identifier.bv_val && strncmp(comp->a.utcTime->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.utcTime->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.utcTime);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentUTCTime ( mem_op, cr, (comp->a.utcTime) );
+ };
+ }
+ if( (comp->choiceId) == TIME_GENERALIZEDTIME &&
+ (( comp->a.generalizedTime->identifier.bv_val && strncmp(comp->a.generalizedTime->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.generalizedTime->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.generalizedTime);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentGeneralizedTime ( mem_op, cr, (comp->a.generalizedTime) );
+ };
+ }
+ return NULL;
+} /* ExtractingComponentTime */
+
+int
+BDecComponentTime PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentTime **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentTime *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ switch (tagId0)
+ {
+ case MAKE_TAG_ID (UNIV, PRIM, UTCTIME_TAG_CODE):
+ case MAKE_TAG_ID (UNIV, CONS, UTCTIME_TAG_CODE):
+ (k->choiceId) = TIME_UTCTIME;
+ rc = BDecComponentUTCTime (mem_op, b, tagId0, elmtLen0, (&k->a.utcTime), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.utcTime)->identifier.bv_val = (k->a.utcTime)->id_buf;
+ (k->a.utcTime)->identifier.bv_len = strlen("utcTime");
+ strcpy( (k->a.utcTime)->identifier.bv_val, "utcTime");
+ break;
+
+ case MAKE_TAG_ID (UNIV, PRIM, GENERALIZEDTIME_TAG_CODE):
+ case MAKE_TAG_ID (UNIV, CONS, GENERALIZEDTIME_TAG_CODE):
+ (k->choiceId) = TIME_GENERALIZEDTIME;
+ rc = BDecComponentGeneralizedTime (mem_op, b, tagId0, elmtLen0, (&k->a.generalizedTime), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.generalizedTime)->identifier.bv_val = (k->a.generalizedTime)->id_buf;
+ (k->a.generalizedTime)->identifier.bv_len = strlen("generalizedTime");
+ strcpy( (k->a.generalizedTime)->identifier.bv_val, "generalizedTime");
+ break;
+
+ default:
+ Asn1Error ("ERROR - unexpected tag in CHOICE\n");
+ return -1;
+ break;
+ } /* end switch */
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentTime*) CompAlloc( mem_op, sizeof(ComponentTime) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentTime ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentTime ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentTime;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentTime;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecTimeContent */
+
+int
+GDecComponentTime PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentTime **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentTime *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen2 = LocateNextGSERToken(mem_op,b,&peek_head2,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head2 != ':'){
+ Asn1Error("Missing : in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( strncmp("utcTime",peek_head, strlen("utcTime")) == 0){
+ (k->choiceId) = TIME_UTCTIME;
+ rc = GDecComponentUTCTime (mem_op, b, (&k->a.utcTime), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.utcTime)->identifier.bv_val = peek_head;
+ (k->a.utcTime)->identifier.bv_len = strLen;
+ }
+ else if( strncmp("generalizedTime",peek_head,strlen("generalizedTime")) == 0){
+ (k->choiceId) = TIME_GENERALIZEDTIME;
+ rc = GDecComponentGeneralizedTime (mem_op, b, (&k->a.generalizedTime), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.generalizedTime)->identifier.bv_val = peek_head;
+ (k->a.generalizedTime)->identifier.bv_len = strLen;
+ }
+ else {
+ Asn1Error("Undefined Identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentTime*) CompAlloc( mem_op, sizeof(ComponentTime) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentTime ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentTime ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentTime;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentTime;
+ return LDAP_SUCCESS;
+} /* GDecTimeContent */
+
+
+int
+MatchingComponentExtension ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentOid ( oid, (ComponentSyntaxInfo*)&((ComponentExtension*)csi_attr)->extnID, (ComponentSyntaxInfo*)&((ComponentExtension*)csi_assert)->extnID );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentBool ( oid, (ComponentSyntaxInfo*)((ComponentExtension*)csi_attr)->critical, (ComponentSyntaxInfo*)((ComponentExtension*)csi_assert)->critical );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentOcts ( oid, (ComponentSyntaxInfo*)&((ComponentExtension*)csi_attr)->extnValue, (ComponentSyntaxInfo*)&((ComponentExtension*)csi_assert)->extnValue );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentExtension */
+
+void*
+ExtractingComponentExtension ( void* mem_op, ComponentReference* cr, ComponentExtension *comp )
+{
+
+ if ( ( comp->extnID.identifier.bv_val && strncmp(comp->extnID.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->extnID.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->extnID;
+ else
+ return NULL;
+ }
+ if ( ( comp->critical->identifier.bv_val && strncmp(comp->critical->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->critical->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->critical;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentBool ( mem_op, cr, comp->critical );
+ }
+ }
+ if ( ( comp->extnValue.identifier.bv_val && strncmp(comp->extnValue.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->extnValue.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->extnValue;
+ else if ( cr->cr_curr->ci_next->ci_type == LDAP_COMPREF_CONTENT) {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return &comp->extnValue;
+ } else {
+ return NULL;
+ }
+ }
+ return NULL;
+} /* ExtractingComponentExtension */
+
+int
+BDecComponentExtension PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentExtension **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentExtension *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, OID_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentOid (mem_op, b, tagId1, elmtLen1, (&k->extnID), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->extnID)->identifier.bv_val = (&k->extnID)->id_buf;
+ (&k->extnID)->identifier.bv_len = strlen("extnID");
+ strcpy( (&k->extnID)->identifier.bv_val, "extnID");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, BOOLEAN_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentBool (mem_op, b, tagId1, elmtLen1, (&k->critical), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->critical)->identifier.bv_val = (k->critical)->id_buf;
+ (k->critical)->identifier.bv_len = strlen("critical");
+ strcpy( (k->critical)->identifier.bv_val, "critical");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, OCTETSTRING_TAG_CODE)) ||
+(tagId1 == MAKE_TAG_ID (UNIV, CONS, OCTETSTRING_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentOcts (mem_op, b, tagId1, elmtLen1, (&k->extnValue), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->extnValue)->identifier.bv_val = (&k->extnValue)->id_buf;
+ (&k->extnValue)->identifier.bv_len = strlen("extnValue");
+ strcpy( (&k->extnValue)->identifier.bv_val, "extnValue");
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+ else
+ return -1;
+
+
+
+ if (!seqDone)
+ return -1;
+
+ if(!COMPONENTNOT_NULL ((k->critical)))
+ {
+(k->critical) = CompAlloc( mem_op, sizeof(ComponentBool));
+ (k->critical)->identifier.bv_val = (k->critical)->id_buf;
+ (k->critical)->identifier.bv_len = strlen("critical");
+ strcpy( (k->critical)->identifier.bv_val, "critical");
+ (k->critical)->value = 0;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentExtension*) CompAlloc( mem_op, sizeof(ComponentExtension) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentExtension ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentExtension ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentExtension;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentExtension;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecExtension*/
+
+int
+GDecComponentExtension PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentExtension **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentExtension *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "extnID", strlen("extnID") ) == 0 ) {
+ rc = GDecComponentOid (mem_op, b, (&k->extnID), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->extnID)->identifier.bv_val = peek_head;
+ (&k->extnID)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "critical", strlen("critical") ) == 0 ) {
+ rc = GDecComponentBool (mem_op, b, (&k->critical), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->critical)->identifier.bv_val = peek_head;
+ ( k->critical)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ else {
+(k->critical) = CompAlloc( mem_op, sizeof(ComponentBool));
+ (k->critical)->value = 0;
+ }
+ if ( strncmp( peek_head, "extnValue", strlen("extnValue") ) == 0 ) {
+ rc = GDecComponentOcts (mem_op, b, (&k->extnValue), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->extnValue)->identifier.bv_val = peek_head;
+ (&k->extnValue)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentExtension*) CompAlloc( mem_op, sizeof(ComponentExtension) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentExtension ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentExtension ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentExtension;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentExtension;
+ return LDAP_SUCCESS;
+} /* GDecExtension*/
+
+
+int
+MatchingComponentAttributeTypeAndValue ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentAttributeType ( oid, (ComponentSyntaxInfo*)&((ComponentAttributeTypeAndValue*)csi_attr)->type, (ComponentSyntaxInfo*)&((ComponentAttributeTypeAndValue*)csi_assert)->type );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = SetAnyTypeByComponentOid ((ComponentSyntaxInfo*)&((ComponentAttributeTypeAndValue*)csi_attr)->value, (&((ComponentAttributeTypeAndValue*)csi_attr)->type));
+ rc = MatchingComponentAnyDefinedBy ( oid, (ComponentAny*)&((ComponentAttributeTypeAndValue*)csi_attr)->value, (ComponentAny*)&((ComponentAttributeTypeAndValue*)csi_assert)->value);
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentAttributeTypeAndValue */
+
+void*
+ExtractingComponentAttributeTypeAndValue ( void* mem_op, ComponentReference* cr, ComponentAttributeTypeAndValue *comp )
+{
+
+ if ( ( comp->type.identifier.bv_val && strncmp(comp->type.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->type.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->type;
+ else
+ return NULL;
+ }
+ if ( ( comp->value.identifier.bv_val && strncmp(comp->value.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->value.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->value;
+ else if ( cr->cr_curr->ci_next->ci_type == LDAP_COMPREF_SELECT ) {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return &comp->value;
+ } else {
+ return NULL;
+ }
+ }
+ return NULL;
+} /* ExtractingComponentAttributeTypeAndValue */
+
+int
+BDecComponentAttributeTypeAndValue PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentAttributeTypeAndValue **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentAttributeTypeAndValue *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, OID_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentAttributeType (mem_op, b, tagId1, elmtLen1, (&k->type), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->type)->identifier.bv_val = (&k->type)->id_buf;
+ (&k->type)->identifier.bv_len = strlen("type");
+ strcpy( (&k->type)->identifier.bv_val, "type");
+ }
+ else
+ return -1;
+
+
+
+ {
+ rc = SetAnyTypeByComponentOid ((&k->value), (&k->type));
+ rc = BDecComponentAnyDefinedBy (mem_op,b, (&k->value), &totalElmtsLen1, mode );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->value)->identifier.bv_val = (&k->value)->id_buf;
+ (&k->value)->identifier.bv_len = strlen("value");
+ strcpy( (&k->value)->identifier.bv_val, "value");
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentAttributeTypeAndValue*) CompAlloc( mem_op, sizeof(ComponentAttributeTypeAndValue) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentAttributeTypeAndValue ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentAttributeTypeAndValue ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentAttributeTypeAndValue;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentAttributeTypeAndValue;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecAttributeTypeAndValue*/
+
+int
+GDecComponentAttributeTypeAndValue PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentAttributeTypeAndValue **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentAttributeTypeAndValue *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "type", strlen("type") ) == 0 ) {
+ rc = GDecComponentAttributeType (mem_op, b, (&k->type), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->type)->identifier.bv_val = peek_head;
+ (&k->type)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "value", strlen("value") ) == 0 ) {
+ rc = rc = SetAnyTypeByComponentOid ((&k->value), (&k->type));
+ rc = GDecComponentAnyDefinedBy (mem_op, b, (&k->value), bytesDecoded, mode );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->value)->identifier.bv_val = peek_head;
+ (&k->value)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentAttributeTypeAndValue*) CompAlloc( mem_op, sizeof(ComponentAttributeTypeAndValue) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentAttributeTypeAndValue ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentAttributeTypeAndValue ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentAttributeTypeAndValue;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentAttributeTypeAndValue;
+ return LDAP_SUCCESS;
+} /* GDecAttributeTypeAndValue*/
+
+
+int
+MatchingComponentValidity ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentTime ( oid, (ComponentSyntaxInfo*)((ComponentValidity*)csi_attr)->notBefore, (ComponentSyntaxInfo*)((ComponentValidity*)csi_assert)->notBefore );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentTime ( oid, (ComponentSyntaxInfo*)((ComponentValidity*)csi_attr)->notAfter, (ComponentSyntaxInfo*)((ComponentValidity*)csi_assert)->notAfter );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentValidity */
+
+void*
+ExtractingComponentValidity ( void* mem_op, ComponentReference* cr, ComponentValidity *comp )
+{
+
+ if ( ( comp->notBefore->identifier.bv_val && strncmp(comp->notBefore->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->notBefore->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->notBefore;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTime ( mem_op, cr, comp->notBefore );
+ }
+ }
+ if ( ( comp->notAfter->identifier.bv_val && strncmp(comp->notAfter->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->notAfter->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->notAfter;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTime ( mem_op, cr, comp->notAfter );
+ }
+ }
+ return NULL;
+} /* ExtractingComponentValidity */
+
+int
+BDecComponentValidity PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentValidity **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ AsnLen totalElmtsLen2 = 0;
+ AsnLen elmtLen2;
+ AsnTag tagId2;
+ int old_mode = mode;
+ int rc;
+ ComponentValidity *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, UTCTIME_TAG_CODE)) ||
+(tagId1 == MAKE_TAG_ID (UNIV, CONS, UTCTIME_TAG_CODE)) ||
+ (tagId1 ==MAKE_TAG_ID (UNIV, PRIM, GENERALIZEDTIME_TAG_CODE))||
+ (tagId1 == MAKE_TAG_ID (UNIV, CONS, GENERALIZEDTIME_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentTime (mem_op, b, tagId1, elmtLen1, (&k->notBefore), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->notBefore)->identifier.bv_val = (k->notBefore)->id_buf;
+ (k->notBefore)->identifier.bv_len = strlen("notBefore");
+ strcpy( (k->notBefore)->identifier.bv_val, "notBefore");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, UTCTIME_TAG_CODE)) ||
+(tagId1 == MAKE_TAG_ID (UNIV, CONS, UTCTIME_TAG_CODE)) ||
+ (tagId1 ==MAKE_TAG_ID (UNIV, PRIM, GENERALIZEDTIME_TAG_CODE))||
+ (tagId1 == MAKE_TAG_ID (UNIV, CONS, GENERALIZEDTIME_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentTime (mem_op, b, tagId1, elmtLen1, (&k->notAfter), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->notAfter)->identifier.bv_val = (k->notAfter)->id_buf;
+ (k->notAfter)->identifier.bv_len = strlen("notAfter");
+ strcpy( (k->notAfter)->identifier.bv_val, "notAfter");
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+ else
+ return -1;
+
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentValidity*) CompAlloc( mem_op, sizeof(ComponentValidity) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentValidity ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentValidity ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentValidity;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentValidity;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecValidity*/
+
+int
+GDecComponentValidity PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentValidity **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentValidity *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "notBefore", strlen("notBefore") ) == 0 ) {
+ rc = GDecComponentTime (mem_op, b, (&k->notBefore), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->notBefore)->identifier.bv_val = peek_head;
+ ( k->notBefore)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "notAfter", strlen("notAfter") ) == 0 ) {
+ rc = GDecComponentTime (mem_op, b, (&k->notAfter), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->notAfter)->identifier.bv_val = peek_head;
+ ( k->notAfter)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentValidity*) CompAlloc( mem_op, sizeof(ComponentValidity) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentValidity ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentValidity ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentValidity;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentValidity;
+ return LDAP_SUCCESS;
+} /* GDecValidity*/
+
+
+int
+MatchingComponentSubjectPublicKeyInfo ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentAlgorithmIdentifier ( oid, (ComponentSyntaxInfo*)((ComponentSubjectPublicKeyInfo*)csi_attr)->algorithm, (ComponentSyntaxInfo*)((ComponentSubjectPublicKeyInfo*)csi_assert)->algorithm );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentBits ( oid, (ComponentSyntaxInfo*)&((ComponentSubjectPublicKeyInfo*)csi_attr)->subjectPublicKey, (ComponentSyntaxInfo*)&((ComponentSubjectPublicKeyInfo*)csi_assert)->subjectPublicKey );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentSubjectPublicKeyInfo */
+
+void*
+ExtractingComponentSubjectPublicKeyInfo ( void* mem_op, ComponentReference* cr, ComponentSubjectPublicKeyInfo *comp )
+{
+
+ if ( ( comp->algorithm->identifier.bv_val && strncmp(comp->algorithm->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->algorithm->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->algorithm;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentAlgorithmIdentifier ( mem_op, cr, comp->algorithm );
+ }
+ }
+ if ( ( comp->subjectPublicKey.identifier.bv_val && strncmp(comp->subjectPublicKey.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->subjectPublicKey.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->subjectPublicKey;
+ else if ( cr->cr_curr->ci_next->ci_type == LDAP_COMPREF_CONTENT) {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return &comp->subjectPublicKey;
+ } else {
+ return NULL;
+ }
+ }
+ return NULL;
+} /* ExtractingComponentSubjectPublicKeyInfo */
+
+int
+BDecComponentSubjectPublicKeyInfo PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentSubjectPublicKeyInfo **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentSubjectPublicKeyInfo *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentAlgorithmIdentifier (mem_op, b, tagId1, elmtLen1, (&k->algorithm), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->algorithm)->identifier.bv_val = (k->algorithm)->id_buf;
+ (k->algorithm)->identifier.bv_len = strlen("algorithm");
+ strcpy( (k->algorithm)->identifier.bv_val, "algorithm");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, BITSTRING_TAG_CODE)) ||
+(tagId1 == MAKE_TAG_ID (UNIV, CONS, BITSTRING_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentBits (mem_op, b, tagId1, elmtLen1, (&k->subjectPublicKey), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->subjectPublicKey)->identifier.bv_val = (&k->subjectPublicKey)->id_buf;
+ (&k->subjectPublicKey)->identifier.bv_len = strlen("subjectPublicKey");
+ strcpy( (&k->subjectPublicKey)->identifier.bv_val, "subjectPublicKey");
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+ else
+ return -1;
+
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentSubjectPublicKeyInfo*) CompAlloc( mem_op, sizeof(ComponentSubjectPublicKeyInfo) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentSubjectPublicKeyInfo ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentSubjectPublicKeyInfo ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentSubjectPublicKeyInfo;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentSubjectPublicKeyInfo;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecSubjectPublicKeyInfo*/
+
+int
+GDecComponentSubjectPublicKeyInfo PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentSubjectPublicKeyInfo **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentSubjectPublicKeyInfo *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "algorithm", strlen("algorithm") ) == 0 ) {
+ rc = GDecComponentAlgorithmIdentifier (mem_op, b, (&k->algorithm), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->algorithm)->identifier.bv_val = peek_head;
+ ( k->algorithm)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "subjectPublicKey", strlen("subjectPublicKey") ) == 0 ) {
+ rc = GDecComponentBits (mem_op, b, (&k->subjectPublicKey), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->subjectPublicKey)->identifier.bv_val = peek_head;
+ (&k->subjectPublicKey)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentSubjectPublicKeyInfo*) CompAlloc( mem_op, sizeof(ComponentSubjectPublicKeyInfo) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentSubjectPublicKeyInfo ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentSubjectPublicKeyInfo ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentSubjectPublicKeyInfo;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentSubjectPublicKeyInfo;
+ return LDAP_SUCCESS;
+} /* GDecSubjectPublicKeyInfo*/
+
+
+int
+MatchingComponentExtensions ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+ void* component1, *component2;
+ AsnList *v1, *v2, t_list;
+
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ v1 = &((ComponentExtensions*)csi_attr)->comp_list;
+ v2 = &((ComponentExtensions*)csi_assert)->comp_list;
+ FOR_EACH_LIST_PAIR_ELMT(component1, component2, v1, v2)
+ {
+ if( MatchingComponentExtension(oid, (ComponentSyntaxInfo*)component1, (ComponentSyntaxInfo*)component2) == LDAP_COMPARE_FALSE) {
+ return LDAP_COMPARE_FALSE;
+ }
+ } /* end of for */
+
+ AsnListFirst( v1 );
+ AsnListFirst( v2 );
+ if( (!component1 && component2) || (component1 && !component2))
+ return LDAP_COMPARE_FALSE;
+ else
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentExtensionsContent */
+
+void*
+ExtractingComponentExtensions ( void* mem_op, ComponentReference* cr, ComponentExtensions *comp )
+{
+ int count = 0;
+ int total;
+ AsnList *v = &comp->comp_list;
+ ComponentInt *k;
+ ComponentExtension *component;
+
+
+ switch ( cr->cr_curr->ci_type ) {
+ case LDAP_COMPREF_FROM_BEGINNING :
+ count = cr->cr_curr->ci_val.ci_from_beginning;
+ FOR_EACH_LIST_ELMT( component , v ) {
+ if( --count == 0 ) {
+ if( cr->cr_curr->ci_next == NULL )
+ return component;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentExtension ( mem_op, cr, component );
+ }
+ }
+ }
+ break;
+ case LDAP_COMPREF_FROM_END :
+ total = AsnListCount ( v );
+ count = cr->cr_curr->ci_val.ci_from_end;
+ count = total + count +1;
+ FOR_EACH_LIST_ELMT ( component, v ) {
+ if( --count == 0 ) {
+ if( cr->cr_curr->ci_next == NULL )
+ return component;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentExtension ( mem_op, cr, component );
+ }
+ }
+ }
+ break;
+ case LDAP_COMPREF_ALL :
+ return comp;
+ case LDAP_COMPREF_COUNT :
+ k = (ComponentInt*)CompAlloc( mem_op, sizeof(ComponentInt));
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ k->comp_desc->cd_tag = (-1);
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentInt;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentInt;
+ k->comp_desc->cd_extract_i = (extract_component_from_id_func*)NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_INTEGER;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentInt;
+ k->value = AsnListCount(v);
+ return k;
+ default :
+ return NULL;
+ }
+} /* ExtractingComponentExtensions */
+
+int
+BDecComponentExtensions PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentExtensions **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentExtensions *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ AsnListInit(&k->comp_list,sizeof(ComponentExtension));
+ for (totalElmtsLen1 = 0; (totalElmtsLen1 < elmtLen0) || (elmtLen0 == INDEFINITE_LEN);)
+ {
+ ComponentExtension **tmpVar;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((tagId1 == EOC_TAG_ID) && (elmtLen0 == INDEFINITE_LEN))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ break; /* got EOC so can exit this SET OF/SEQ OF's for loop*/
+ }
+ if ((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE)))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ tmpVar = (ComponentExtension**) CompAsnListAppend (mem_op,&k->comp_list);
+ rc = BDecComponentExtension (mem_op, b, tagId1, elmtLen1, tmpVar, &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ } /* end of tag check if */
+ else /* wrong tag */
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+ } /* end of for */
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentExtensions*) CompAlloc( mem_op, sizeof(ComponentExtensions) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentExtensions ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentExtensions ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentExtensions;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentExtensions;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecExtensionsContent */
+
+int
+GDecComponentExtensions PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentExtensions **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentExtensions *k,*t, c_temp;
+
+
+ int ElmtsLen1;
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ AsnListInit( &k->comp_list, sizeof( ComponentExtension ) );
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b, &peek_head, GSER_PEEK)) ){
+ Asn1Error("Error during Reading { in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ for (ElmtsLen1 = 0; ElmtsLen1 >= INDEFINITE_LEN; ElmtsLen1++)
+ {
+ ComponentExtension **tmpVar;
+ if( !(strLen = LocateNextGSERToken(mem_op,b, &peek_head, GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading{ in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head == '}') break;
+ if( !(*peek_head == '{' || *peek_head ==',') ) {
+ return LDAP_PROTOCOL_ERROR;
+ }
+ tmpVar = (ComponentExtension**) CompAsnListAppend (mem_op, &k->comp_list);
+ if ( tmpVar == NULL ) {
+ Asn1Error("Error during Reading{ in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ rc = GDecComponentExtension (mem_op, b, tmpVar, bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ } /* end of for */
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentExtensions*) CompAlloc( mem_op, sizeof(ComponentExtensions) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentExtensions ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentExtensions ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentExtensions;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentExtensions;
+ return LDAP_SUCCESS;
+} /* GDecExtensionsContent */
+
+
+int
+MatchingComponentRelativeDistinguishedName ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+ void* component1, *component2;
+ AsnList *v1, *v2, t_list;
+
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ v1 = &((ComponentRelativeDistinguishedName*)csi_attr)->comp_list;
+ v2 = &((ComponentRelativeDistinguishedName*)csi_assert)->comp_list;
+ AsnListInit( &t_list, 0 );
+ if( AsnListCount( v1 ) != AsnListCount( v2 ) )
+ return LDAP_COMPARE_FALSE;
+ FOR_EACH_LIST_ELMT (component1, v1)
+ {
+ FOR_EACH_LIST_ELMT(component2, v2)
+ {
+ if( MatchingComponentAttributeTypeAndValue(oid, (ComponentSyntaxInfo*)component1,(ComponentSyntaxInfo*)component2) == LDAP_COMPARE_TRUE ) {
+ AsnElmtMove( v2, &t_list );
+ break;
+ }
+ } /* end of inner for */
+ } /* end of outer for */
+
+ if( AsnListCount( v2 ) == 0 )
+ rc = LDAP_COMPARE_TRUE;
+ else
+ rc = LDAP_COMPARE_FALSE;
+ AsnListMove( &t_list, v2 );
+ AsnListFirst( v1 );
+ AsnListFirst( v2 );
+ return rc;
+} /* BMatchingComponentRelativeDistinguishedNameContent */
+
+void*
+ExtractingComponentRelativeDistinguishedName ( void* mem_op, ComponentReference* cr, ComponentRelativeDistinguishedName *comp )
+{
+ int count = 0;
+ int total;
+ AsnList *v = &comp->comp_list;
+ ComponentInt *k;
+ ComponentAttributeTypeAndValue *component;
+
+
+ switch ( cr->cr_curr->ci_type ) {
+ case LDAP_COMPREF_FROM_BEGINNING :
+ count = cr->cr_curr->ci_val.ci_from_beginning;
+ FOR_EACH_LIST_ELMT( component , v ) {
+ if( --count == 0 ) {
+ if( cr->cr_curr->ci_next == NULL )
+ return component;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentAttributeTypeAndValue ( mem_op, cr, component );
+ }
+ }
+ }
+ break;
+ case LDAP_COMPREF_FROM_END :
+ total = AsnListCount ( v );
+ count = cr->cr_curr->ci_val.ci_from_end;
+ count = total + count +1;
+ FOR_EACH_LIST_ELMT ( component, v ) {
+ if( --count == 0 ) {
+ if( cr->cr_curr->ci_next == NULL )
+ return component;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentAttributeTypeAndValue ( mem_op, cr, component );
+ }
+ }
+ }
+ break;
+ case LDAP_COMPREF_ALL :
+ return comp;
+ case LDAP_COMPREF_COUNT :
+ k = (ComponentInt*)CompAlloc( mem_op, sizeof(ComponentInt));
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ k->comp_desc->cd_tag = (-1);
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentInt;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentInt;
+ k->comp_desc->cd_extract_i = (extract_component_from_id_func*)NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_INTEGER;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentInt;
+ k->value = AsnListCount(v);
+ return k;
+ default :
+ return NULL;
+ }
+} /* ExtractingComponentRelativeDistinguishedName */
+
+int
+BDecComponentRelativeDistinguishedName PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentRelativeDistinguishedName **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentRelativeDistinguishedName *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ AsnListInit(&k->comp_list,sizeof(ComponentAttributeTypeAndValue));
+ for (totalElmtsLen1 = 0; (totalElmtsLen1 < elmtLen0) || (elmtLen0 == INDEFINITE_LEN);)
+ {
+ ComponentAttributeTypeAndValue **tmpVar;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((tagId1 == EOC_TAG_ID) && (elmtLen0 == INDEFINITE_LEN))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ break; /* got EOC so can exit this SET OF/SEQ OF's for loop*/
+ }
+ if ((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE)))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ tmpVar = (ComponentAttributeTypeAndValue**) CompAsnListAppend (mem_op,&k->comp_list);
+ rc = BDecComponentAttributeTypeAndValue (mem_op, b, tagId1, elmtLen1, tmpVar, &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ } /* end of tag check if */
+ else /* wrong tag */
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+ } /* end of for */
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentRelativeDistinguishedName*) CompAlloc( mem_op, sizeof(ComponentRelativeDistinguishedName) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ldap_encoder = (encoder_func*)ConvertRDN2RFC2253;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentRelativeDistinguishedName ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentRelativeDistinguishedName ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentRelativeDistinguishedName;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = RelativeDistinguishedName;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentRelativeDistinguishedName;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecRelativeDistinguishedNameContent */
+
+int
+GDecComponentRelativeDistinguishedName PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentRelativeDistinguishedName **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentRelativeDistinguishedName *k,*t, c_temp;
+
+
+ int ElmtsLen1;
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ AsnListInit( &k->comp_list, sizeof( ComponentAttributeTypeAndValue ) );
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b, &peek_head, GSER_PEEK)) ){
+ Asn1Error("Error during Reading { in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ for (ElmtsLen1 = 0; ElmtsLen1 >= INDEFINITE_LEN; ElmtsLen1++)
+ {
+ ComponentAttributeTypeAndValue **tmpVar;
+ if( !(strLen = LocateNextGSERToken(mem_op,b, &peek_head, GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading{ in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head == '}') break;
+ if( !(*peek_head == '{' || *peek_head ==',') ) {
+ return LDAP_PROTOCOL_ERROR;
+ }
+ tmpVar = (ComponentAttributeTypeAndValue**) CompAsnListAppend (mem_op, &k->comp_list);
+ if ( tmpVar == NULL ) {
+ Asn1Error("Error during Reading{ in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ rc = GDecComponentAttributeTypeAndValue (mem_op, b, tmpVar, bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ } /* end of for */
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentRelativeDistinguishedName*) CompAlloc( mem_op, sizeof(ComponentRelativeDistinguishedName) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentRelativeDistinguishedName ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentRelativeDistinguishedName ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentRelativeDistinguishedName;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = RelativeDistinguishedName;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentRelativeDistinguishedName;
+ return LDAP_SUCCESS;
+} /* GDecRelativeDistinguishedNameContent */
+
+
+int
+MatchingComponentRDNSequence ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+ void* component1, *component2;
+ AsnList *v1, *v2, t_list;
+
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ v1 = &((ComponentRDNSequence*)csi_attr)->comp_list;
+ v2 = &((ComponentRDNSequence*)csi_assert)->comp_list;
+ FOR_EACH_LIST_PAIR_ELMT(component1, component2, v1, v2)
+ {
+ if( MatchingComponentRelativeDistinguishedName(oid, (ComponentSyntaxInfo*)component1, (ComponentSyntaxInfo*)component2) == LDAP_COMPARE_FALSE) {
+ return LDAP_COMPARE_FALSE;
+ }
+ } /* end of for */
+
+ AsnListFirst( v1 );
+ AsnListFirst( v2 );
+ if( (!component1 && component2) || (component1 && !component2))
+ return LDAP_COMPARE_FALSE;
+ else
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentRDNSequenceContent */
+
+void*
+ExtractingComponentRDNSequence ( void* mem_op, ComponentReference* cr, ComponentRDNSequence *comp )
+{
+ int count = 0;
+ int total;
+ AsnList *v = &comp->comp_list;
+ ComponentInt *k;
+ ComponentRelativeDistinguishedName *component;
+
+
+ switch ( cr->cr_curr->ci_type ) {
+ case LDAP_COMPREF_FROM_BEGINNING :
+ count = cr->cr_curr->ci_val.ci_from_beginning;
+ FOR_EACH_LIST_ELMT( component , v ) {
+ if( --count == 0 ) {
+ if( cr->cr_curr->ci_next == NULL )
+ return component;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentRelativeDistinguishedName ( mem_op, cr, component );
+ }
+ }
+ }
+ break;
+ case LDAP_COMPREF_FROM_END :
+ total = AsnListCount ( v );
+ count = cr->cr_curr->ci_val.ci_from_end;
+ count = total + count +1;
+ FOR_EACH_LIST_ELMT ( component, v ) {
+ if( --count == 0 ) {
+ if( cr->cr_curr->ci_next == NULL )
+ return component;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentRelativeDistinguishedName ( mem_op, cr, component );
+ }
+ }
+ }
+ break;
+ case LDAP_COMPREF_ALL :
+ return comp;
+ case LDAP_COMPREF_COUNT :
+ k = (ComponentInt*)CompAlloc( mem_op, sizeof(ComponentInt));
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ k->comp_desc->cd_tag = (-1);
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentInt;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentInt;
+ k->comp_desc->cd_extract_i = (extract_component_from_id_func*)NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_INTEGER;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentInt;
+ k->value = AsnListCount(v);
+ return k;
+ default :
+ return NULL;
+ }
+} /* ExtractingComponentRDNSequence */
+
+int
+BDecComponentRDNSequence PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentRDNSequence **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentRDNSequence *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ AsnListInit(&k->comp_list,sizeof(ComponentRelativeDistinguishedName));
+ for (totalElmtsLen1 = 0; (totalElmtsLen1 < elmtLen0) || (elmtLen0 == INDEFINITE_LEN);)
+ {
+ ComponentRelativeDistinguishedName **tmpVar;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((tagId1 == EOC_TAG_ID) && (elmtLen0 == INDEFINITE_LEN))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ break; /* got EOC so can exit this SET OF/SEQ OF's for loop*/
+ }
+ if ((tagId1 == MAKE_TAG_ID (UNIV, CONS, SET_TAG_CODE)))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ tmpVar = (ComponentRelativeDistinguishedName**) CompAsnListAppend (mem_op,&k->comp_list);
+ rc = BDecComponentRelativeDistinguishedName (mem_op, b, tagId1, elmtLen1, tmpVar, &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ } /* end of tag check if */
+ else /* wrong tag */
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+ } /* end of for */
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentRDNSequence*) CompAlloc( mem_op, sizeof(ComponentRDNSequence) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ldap_encoder = (encoder_func*) ConvertRDNSequence2RFC2253;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentRDNSequence ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentRDNSequence ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentRDNSequence;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = RDNSequence;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentRDNSequence;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecRDNSequenceContent */
+
+int
+GDecComponentRDNSequence PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentRDNSequence **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentRDNSequence *k,*t, c_temp;
+
+
+ int ElmtsLen1;
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ AsnListInit( &k->comp_list, sizeof( ComponentRelativeDistinguishedName ) );
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b, &peek_head, GSER_PEEK)) ){
+ Asn1Error("Error during Reading { in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ for (ElmtsLen1 = 0; ElmtsLen1 >= INDEFINITE_LEN; ElmtsLen1++)
+ {
+ ComponentRelativeDistinguishedName **tmpVar;
+ if( !(strLen = LocateNextGSERToken(mem_op,b, &peek_head, GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading{ in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head == '}') break;
+ if( !(*peek_head == '{' || *peek_head ==',') ) {
+ return LDAP_PROTOCOL_ERROR;
+ }
+ tmpVar = (ComponentRelativeDistinguishedName**) CompAsnListAppend (mem_op, &k->comp_list);
+ if ( tmpVar == NULL ) {
+ Asn1Error("Error during Reading{ in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ rc = GDecComponentRelativeDistinguishedName (mem_op, b, tmpVar, bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ } /* end of for */
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentRDNSequence*) CompAlloc( mem_op, sizeof(ComponentRDNSequence) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ldap_encoder = (encoder_func*)ConvertRDNSequence2RFC2253;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentRDNSequence ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentRDNSequence ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentRDNSequence;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = RDNSequence ;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentRDNSequence;
+ return LDAP_SUCCESS;
+} /* GDecRDNSequenceContent */
+
+
+int
+MatchingComponentName ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+ ComponentName *v1, *v2;
+
+
+ v1 = (ComponentName*)csi_attr;
+ v2 = (ComponentName*)csi_assert;
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ if( (v1->choiceId != v2->choiceId ) )
+ return LDAP_COMPARE_FALSE;
+ switch( v1->choiceId )
+ {
+ case NAME_RDNSEQUENCE :
+ rc = MatchingComponentRDNSequence ( oid, (ComponentSyntaxInfo*)(v1->a.rdnSequence), (ComponentSyntaxInfo*)(v2->a.rdnSequence) );
+ break;
+ default :
+ return LDAP_PROTOCOL_ERROR;
+ }
+ return rc;
+} /* BMatchingComponentNameContent */
+
+void*
+ExtractingComponentName ( void* mem_op, ComponentReference* cr, ComponentName *comp )
+{
+
+
+ if( (comp->choiceId) == NAME_RDNSEQUENCE &&
+ (( comp->a.rdnSequence->identifier.bv_val && strncmp(comp->a.rdnSequence->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0) ||
+ ( strncmp(comp->a.rdnSequence->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0))) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return (comp->a.rdnSequence);
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentRDNSequence ( mem_op, cr, (comp->a.rdnSequence) );
+ };
+ }
+ return NULL;
+} /* ExtractingComponentName */
+
+int
+BDecComponentName PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentName **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentName *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ switch (tagId0)
+ {
+ case MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE):
+ (k->choiceId) = NAME_RDNSEQUENCE;
+ rc = BDecComponentRDNSequence (mem_op, b, tagId0, elmtLen0, (&k->a.rdnSequence), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.rdnSequence)->identifier.bv_val = (k->a.rdnSequence)->id_buf;
+ (k->a.rdnSequence)->identifier.bv_len = strlen("rdnSequence");
+ strcpy( (k->a.rdnSequence)->identifier.bv_val, "rdnSequence");
+ break;
+
+ default:
+ Asn1Error ("ERROR - unexpected tag in CHOICE\n");
+ return -1;
+ break;
+ } /* end switch */
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentName*) CompAlloc( mem_op, sizeof(ComponentName) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentName ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentName ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentName;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentName;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecNameContent */
+
+int
+GDecComponentName PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentName **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentName *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen2 = LocateNextGSERToken(mem_op,b,&peek_head2,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head2 != ':'){
+ Asn1Error("Missing : in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( strncmp("rdnSequence",peek_head, strlen("rdnSequence")) == 0){
+ (k->choiceId) = NAME_RDNSEQUENCE;
+ rc = GDecComponentRDNSequence (mem_op, b, (&k->a.rdnSequence), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->a.rdnSequence)->identifier.bv_val = peek_head;
+ (k->a.rdnSequence)->identifier.bv_len = strLen;
+ }
+ else {
+ Asn1Error("Undefined Identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentName*) CompAlloc( mem_op, sizeof(ComponentName) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentName ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentName ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentName;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentName;
+ return LDAP_SUCCESS;
+} /* GDecNameContent */
+
+
+int
+MatchingComponentTBSCertificate ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentVersion ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_attr)->version, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_assert)->version );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentCertificateSerialNumber ( oid, (ComponentSyntaxInfo*)&((ComponentTBSCertificate*)csi_attr)->serialNumber, (ComponentSyntaxInfo*)&((ComponentTBSCertificate*)csi_assert)->serialNumber );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentAlgorithmIdentifier ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_attr)->signature, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_assert)->signature );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentName ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_attr)->issuer, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_assert)->issuer );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentValidity ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_attr)->validity, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_assert)->validity );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentName ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_attr)->subject, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_assert)->subject );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentSubjectPublicKeyInfo ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_attr)->subjectPublicKeyInfo, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_assert)->subjectPublicKeyInfo );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentUniqueIdentifier ( oid, (ComponentSyntaxInfo*)&((ComponentTBSCertificate*)csi_attr)->issuerUniqueIdentifier, (ComponentSyntaxInfo*)&((ComponentTBSCertificate*)csi_assert)->issuerUniqueIdentifier );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentUniqueIdentifier ( oid, (ComponentSyntaxInfo*)&((ComponentTBSCertificate*)csi_attr)->subjectUniqueIdentifier, (ComponentSyntaxInfo*)&((ComponentTBSCertificate*)csi_assert)->subjectUniqueIdentifier );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ if(COMPONENTNOT_NULL( ((ComponentTBSCertificate*)csi_attr)->extensions ) ) {
+ rc = MatchingComponentExtensions ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_attr)->extensions, (ComponentSyntaxInfo*)((ComponentTBSCertificate*)csi_assert)->extensions );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ }
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentTBSCertificate */
+
+void*
+ExtractingComponentTBSCertificate ( void* mem_op, ComponentReference* cr, ComponentTBSCertificate *comp )
+{
+
+ if ( ( comp->version->identifier.bv_val && strncmp(comp->version->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->version->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->version;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentVersion ( mem_op, cr, comp->version );
+ }
+ }
+ if ( ( comp->serialNumber.identifier.bv_val && strncmp(comp->serialNumber.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->serialNumber.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->serialNumber;
+ else
+ return NULL;
+ }
+ if ( ( comp->signature->identifier.bv_val && strncmp(comp->signature->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->signature->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->signature;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentAlgorithmIdentifier ( mem_op, cr, comp->signature );
+ }
+ }
+ if ( ( comp->issuer->identifier.bv_val && strncmp(comp->issuer->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->issuer->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->issuer;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentName ( mem_op, cr, comp->issuer );
+ }
+ }
+ if ( ( comp->validity->identifier.bv_val && strncmp(comp->validity->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->validity->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->validity;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentValidity ( mem_op, cr, comp->validity );
+ }
+ }
+ if ( ( comp->subject->identifier.bv_val && strncmp(comp->subject->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->subject->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->subject;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentName ( mem_op, cr, comp->subject );
+ }
+ }
+ if ( ( comp->subjectPublicKeyInfo->identifier.bv_val && strncmp(comp->subjectPublicKeyInfo->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->subjectPublicKeyInfo->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->subjectPublicKeyInfo;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentSubjectPublicKeyInfo ( mem_op, cr, comp->subjectPublicKeyInfo );
+ }
+ }
+ if ( ( comp->issuerUniqueIdentifier.identifier.bv_val && strncmp(comp->issuerUniqueIdentifier.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->issuerUniqueIdentifier.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->issuerUniqueIdentifier;
+ else if ( cr->cr_curr->ci_next->ci_type == LDAP_COMPREF_CONTENT) {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return &comp->issuerUniqueIdentifier;
+ } else {
+ return NULL;
+ }
+ }
+ if ( ( comp->subjectUniqueIdentifier.identifier.bv_val && strncmp(comp->subjectUniqueIdentifier.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->subjectUniqueIdentifier.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->subjectUniqueIdentifier;
+ else if ( cr->cr_curr->ci_next->ci_type == LDAP_COMPREF_CONTENT) {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return &comp->subjectUniqueIdentifier;
+ } else {
+ return NULL;
+ }
+ }
+ if ( ( comp->extensions->identifier.bv_val && strncmp(comp->extensions->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->extensions->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->extensions;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentExtensions ( mem_op, cr, comp->extensions );
+ }
+ }
+ return NULL;
+} /* ExtractingComponentTBSCertificate */
+
+int
+BDecComponentTBSCertificate PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentTBSCertificate **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ AsnLen totalElmtsLen2 = 0;
+ AsnLen elmtLen2;
+ AsnTag tagId2;
+ int old_mode = mode;
+ int rc;
+ ComponentTBSCertificate *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (CNTX, CONS, 0))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ tagId2 = BDecTag (b, &totalElmtsLen1 );
+
+ if (tagId2 != MAKE_TAG_ID (UNIV, PRIM, INTEGER_TAG_CODE))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen2 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentVersion (mem_op, b, tagId2, elmtLen2, (&k->version), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->version)->identifier.bv_val = (k->version)->id_buf;
+ (k->version)->identifier.bv_len = strlen("version");
+ strcpy( (k->version)->identifier.bv_val, "version");
+ if (elmtLen1 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, INTEGER_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentCertificateSerialNumber (mem_op, b, tagId1, elmtLen1, (&k->serialNumber), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->serialNumber)->identifier.bv_val = (&k->serialNumber)->id_buf;
+ (&k->serialNumber)->identifier.bv_len = strlen("serialNumber");
+ strcpy( (&k->serialNumber)->identifier.bv_val, "serialNumber");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentAlgorithmIdentifier (mem_op, b, tagId1, elmtLen1, (&k->signature), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->signature)->identifier.bv_val = (k->signature)->id_buf;
+ (k->signature)->identifier.bv_len = strlen("signature");
+ strcpy( (k->signature)->identifier.bv_val, "signature");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentName (mem_op, b, tagId1, elmtLen1, (&k->issuer), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->issuer)->identifier.bv_val = (k->issuer)->id_buf;
+ (k->issuer)->identifier.bv_len = strlen("issuer");
+ strcpy( (k->issuer)->identifier.bv_val, "issuer");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentValidity (mem_op, b, tagId1, elmtLen1, (&k->validity), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->validity)->identifier.bv_val = (k->validity)->id_buf;
+ (k->validity)->identifier.bv_len = strlen("validity");
+ strcpy( (k->validity)->identifier.bv_val, "validity");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentName (mem_op, b, tagId1, elmtLen1, (&k->subject), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->subject)->identifier.bv_val = (k->subject)->id_buf;
+ (k->subject)->identifier.bv_len = strlen("subject");
+ strcpy( (k->subject)->identifier.bv_val, "subject");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentSubjectPublicKeyInfo (mem_op, b, tagId1, elmtLen1, (&k->subjectPublicKeyInfo), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->subjectPublicKeyInfo)->identifier.bv_val = (k->subjectPublicKeyInfo)->id_buf;
+ (k->subjectPublicKeyInfo)->identifier.bv_len = strlen("subjectPublicKeyInfo");
+ strcpy( (k->subjectPublicKeyInfo)->identifier.bv_val, "subjectPublicKeyInfo");
+ if ((elmtLen0 != INDEFINITE_LEN) && (totalElmtsLen1 == elmtLen0))
+ seqDone = TRUE;
+ else
+ {
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((elmtLen0 == INDEFINITE_LEN) && (tagId1 == EOC_TAG_ID))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ seqDone = TRUE;
+ }
+ }
+ }
+ else
+ return -1;
+
+
+
+ if ((!seqDone) && ((tagId1 == MAKE_TAG_ID (CNTX, PRIM, 1)) ||
+(tagId1 == MAKE_TAG_ID (CNTX, CONS, 1))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentUniqueIdentifier (mem_op, b, tagId1, elmtLen1, (&k->issuerUniqueIdentifier), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->issuerUniqueIdentifier)->identifier.bv_val = (&k->issuerUniqueIdentifier)->id_buf;
+ (&k->issuerUniqueIdentifier)->identifier.bv_len = strlen("issuerUniqueIdentifier");
+ strcpy( (&k->issuerUniqueIdentifier)->identifier.bv_val, "issuerUniqueIdentifier");
+ if ((elmtLen0 != INDEFINITE_LEN) && (totalElmtsLen1 == elmtLen0))
+ seqDone = TRUE;
+ else
+ {
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((elmtLen0 == INDEFINITE_LEN) && (tagId1 == EOC_TAG_ID))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ seqDone = TRUE;
+ }
+ }
+ }
+
+
+ if ((!seqDone) && ((tagId1 == MAKE_TAG_ID (CNTX, PRIM, 2)) ||
+(tagId1 == MAKE_TAG_ID (CNTX, CONS, 2))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentUniqueIdentifier (mem_op, b, tagId1, elmtLen1, (&k->subjectUniqueIdentifier), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->subjectUniqueIdentifier)->identifier.bv_val = (&k->subjectUniqueIdentifier)->id_buf;
+ (&k->subjectUniqueIdentifier)->identifier.bv_len = strlen("subjectUniqueIdentifier");
+ strcpy( (&k->subjectUniqueIdentifier)->identifier.bv_val, "subjectUniqueIdentifier");
+ if ((elmtLen0 != INDEFINITE_LEN) && (totalElmtsLen1 == elmtLen0))
+ seqDone = TRUE;
+ else
+ {
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((elmtLen0 == INDEFINITE_LEN) && (tagId1 == EOC_TAG_ID))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ seqDone = TRUE;
+ }
+ }
+ }
+
+
+ if ((!seqDone) && ((tagId1 == MAKE_TAG_ID (CNTX, CONS, 3))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ tagId2 = BDecTag (b, &totalElmtsLen1 );
+
+ if (tagId2 != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen2 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentExtensions (mem_op, b, tagId2, elmtLen2, (&k->extensions), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->extensions)->identifier.bv_val = (k->extensions)->id_buf;
+ (k->extensions)->identifier.bv_len = strlen("extensions");
+ strcpy( (k->extensions)->identifier.bv_val, "extensions");
+ if (elmtLen1 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+
+
+ if (!seqDone)
+ return -1;
+
+ if(!COMPONENTNOT_NULL ((k->version)))
+ {
+(k->version) = CompAlloc( mem_op, sizeof(ComponentVersion));
+ (k->version)->identifier.bv_val = (k->version)->id_buf;
+ (k->version)->identifier.bv_len = strlen("version");
+ strcpy( (k->version)->identifier.bv_val, "version");
+ (k->version)->value = 0;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentTBSCertificate*) CompAlloc( mem_op, sizeof(ComponentTBSCertificate) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentTBSCertificate ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentTBSCertificate ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentTBSCertificate;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentTBSCertificate;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecTBSCertificate*/
+
+int
+GDecComponentTBSCertificate PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentTBSCertificate **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentTBSCertificate *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "version", strlen("version") ) == 0 ) {
+ rc = GDecComponentVersion (mem_op, b, (&k->version), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->version)->identifier.bv_val = peek_head;
+ ( k->version)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ else {
+(k->version) = CompAlloc( mem_op, sizeof(ComponentVersion));
+ (k->version)->value = 0;
+ }
+ if ( strncmp( peek_head, "serialNumber", strlen("serialNumber") ) == 0 ) {
+ rc = GDecComponentCertificateSerialNumber (mem_op, b, (&k->serialNumber), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->serialNumber)->identifier.bv_val = peek_head;
+ (&k->serialNumber)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "signature", strlen("signature") ) == 0 ) {
+ rc = GDecComponentAlgorithmIdentifier (mem_op, b, (&k->signature), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->signature)->identifier.bv_val = peek_head;
+ ( k->signature)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "issuer", strlen("issuer") ) == 0 ) {
+ rc = GDecComponentName (mem_op, b, (&k->issuer), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->issuer)->identifier.bv_val = peek_head;
+ ( k->issuer)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "validity", strlen("validity") ) == 0 ) {
+ rc = GDecComponentValidity (mem_op, b, (&k->validity), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->validity)->identifier.bv_val = peek_head;
+ ( k->validity)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "subject", strlen("subject") ) == 0 ) {
+ rc = GDecComponentName (mem_op, b, (&k->subject), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->subject)->identifier.bv_val = peek_head;
+ ( k->subject)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "subjectPublicKeyInfo", strlen("subjectPublicKeyInfo") ) == 0 ) {
+ rc = GDecComponentSubjectPublicKeyInfo (mem_op, b, (&k->subjectPublicKeyInfo), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->subjectPublicKeyInfo)->identifier.bv_val = peek_head;
+ ( k->subjectPublicKeyInfo)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "issuerUniqueIdentifier", strlen("issuerUniqueIdentifier") ) == 0 ) {
+ rc = GDecComponentUniqueIdentifier (mem_op, b, (&k->issuerUniqueIdentifier), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->issuerUniqueIdentifier)->identifier.bv_val = peek_head;
+ (&k->issuerUniqueIdentifier)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "subjectUniqueIdentifier", strlen("subjectUniqueIdentifier") ) == 0 ) {
+ rc = GDecComponentUniqueIdentifier (mem_op, b, (&k->subjectUniqueIdentifier), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->subjectUniqueIdentifier)->identifier.bv_val = peek_head;
+ (&k->subjectUniqueIdentifier)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "extensions", strlen("extensions") ) == 0 ) {
+ rc = GDecComponentExtensions (mem_op, b, (&k->extensions), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->extensions)->identifier.bv_val = peek_head;
+ ( k->extensions)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentTBSCertificate*) CompAlloc( mem_op, sizeof(ComponentTBSCertificate) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentTBSCertificate ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentTBSCertificate ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentTBSCertificate;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentTBSCertificate;
+ return LDAP_SUCCESS;
+} /* GDecTBSCertificate*/
+
+
+int
+MatchingComponentCertificate ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentTBSCertificate ( oid, (ComponentSyntaxInfo*)((ComponentCertificate*)csi_attr)->toBeSigned, (ComponentSyntaxInfo*)((ComponentCertificate*)csi_assert)->toBeSigned );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentAlgorithmIdentifier ( oid, (ComponentSyntaxInfo*)((ComponentCertificate*)csi_attr)->signatureAlgorithm, (ComponentSyntaxInfo*)((ComponentCertificate*)csi_assert)->signatureAlgorithm );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentBits ( oid, (ComponentSyntaxInfo*)&((ComponentCertificate*)csi_attr)->signature, (ComponentSyntaxInfo*)&((ComponentCertificate*)csi_assert)->signature );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentCertificate */
+
+void*
+ExtractingComponentCertificate ( void* mem_op, ComponentReference* cr, ComponentCertificate *comp )
+{
+
+ if ( ( comp->toBeSigned->identifier.bv_val && strncmp(comp->toBeSigned->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->toBeSigned->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->toBeSigned;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTBSCertificate ( mem_op, cr, comp->toBeSigned );
+ }
+ }
+ if ( ( comp->signatureAlgorithm->identifier.bv_val && strncmp(comp->signatureAlgorithm->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->signatureAlgorithm->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->signatureAlgorithm;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentAlgorithmIdentifier ( mem_op, cr, comp->signatureAlgorithm );
+ }
+ }
+ if ( ( comp->signature.identifier.bv_val && strncmp(comp->signature.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->signature.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->signature;
+ else if ( cr->cr_curr->ci_next->ci_type == LDAP_COMPREF_CONTENT) {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return &comp->signature;
+ } else {
+ return NULL;
+ }
+ }
+ return NULL;
+} /* ExtractingComponentCertificate */
+
+int
+BDecComponentCertificate PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentCertificate **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentCertificate *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentTBSCertificate (mem_op, b, tagId1, elmtLen1, (&k->toBeSigned), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->toBeSigned)->identifier.bv_val = (k->toBeSigned)->id_buf;
+ (k->toBeSigned)->identifier.bv_len = strlen("toBeSigned");
+ strcpy( (k->toBeSigned)->identifier.bv_val, "toBeSigned");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentAlgorithmIdentifier (mem_op, b, tagId1, elmtLen1, (&k->signatureAlgorithm), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->signatureAlgorithm)->identifier.bv_val = (k->signatureAlgorithm)->id_buf;
+ (k->signatureAlgorithm)->identifier.bv_len = strlen("signatureAlgorithm");
+ strcpy( (k->signatureAlgorithm)->identifier.bv_val, "signatureAlgorithm");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, BITSTRING_TAG_CODE)) ||
+(tagId1 == MAKE_TAG_ID (UNIV, CONS, BITSTRING_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentBits (mem_op, b, tagId1, elmtLen1, (&k->signature), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->signature)->identifier.bv_val = (&k->signature)->id_buf;
+ (&k->signature)->identifier.bv_len = strlen("signature");
+ strcpy( (&k->signature)->identifier.bv_val, "signature");
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+ else
+ return -1;
+
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentCertificate*) CompAlloc( mem_op, sizeof(ComponentCertificate) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentCertificate ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentCertificate ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentCertificate;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentCertificate;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecCertificate*/
+
+int
+GDecComponentCertificate PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentCertificate **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentCertificate *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "toBeSigned", strlen("toBeSigned") ) == 0 ) {
+ rc = GDecComponentTBSCertificate (mem_op, b, (&k->toBeSigned), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->toBeSigned)->identifier.bv_val = peek_head;
+ ( k->toBeSigned)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "signatureAlgorithm", strlen("signatureAlgorithm") ) == 0 ) {
+ rc = GDecComponentAlgorithmIdentifier (mem_op, b, (&k->signatureAlgorithm), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->signatureAlgorithm)->identifier.bv_val = peek_head;
+ ( k->signatureAlgorithm)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "signature", strlen("signature") ) == 0 ) {
+ rc = GDecComponentBits (mem_op, b, (&k->signature), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->signature)->identifier.bv_val = peek_head;
+ (&k->signature)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentCertificate*) CompAlloc( mem_op, sizeof(ComponentCertificate) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentCertificate ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentCertificate ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentCertificate;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentCertificate;
+ return LDAP_SUCCESS;
+} /* GDecCertificate*/
+
+
diff --git a/contrib/slapd-modules/comp_match/certificate.h b/contrib/slapd-modules/comp_match/certificate.h
new file mode 100644
index 0000000..d1df75f
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/certificate.h
@@ -0,0 +1,379 @@
+
+#include "asn-incl.h"
+/*
+ * certificate.h
+ * "AuthenticationFramework" ASN.1 module encode/decode/extracting/matching/free C src.
+ * This file was generated by modified eSMACC compiler Sat Dec 11 11:22:49 2004
+ * The generated files are strongly encouraged to be
+ * compiled as a module for OpenLDAP Software
+ */
+
+#ifndef _certificate_h_
+#define _certificate_h_
+
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef _WIN32
+#pragma warning( disable : 4101 )
+#endif
+#include "componentlib.h"
+typedef enum AuthenticationFrameworkAnyId
+{
+ nullOid_ANY_ID = 0,
+ nullOid2_ANY_ID = 1,
+ nullOid3_ANY_ID = 2,
+ printableStringOid_ANY_ID = 3,
+ printableStringOid2_ANY_ID = 4,
+ printableStringOid3_ANY_ID = 5,
+ printableStringOid4_ANY_ID = 6,
+ printableStringOid5_ANY_ID = 7,
+ printableStringOid6_ANY_ID = 8,
+ printableStringOid7_ANY_ID = 9,
+ iA5StringOid_ANY_ID = 10,
+ octetStringOid_ANY_ID = 11,
+ octetStringOid2_ANY_ID = 12,
+ octetStringOid3_ANY_ID = 13,
+ octetStringOid4_ANY_ID = 14,
+ octetStringOid5_ANY_ID = 15,
+ octetStringOid7_ANY_ID = 17} AuthenticationFrameworkAnyId;
+
+void InitAnyAuthenticationFramework();
+
+
+#define V1 0
+#define V2 1
+#define V3 2
+
+typedef ComponentInt ComponentVersion; /* INTEGER { V1 (0), V2 (1), V3 (2) } */
+
+#define MatchingComponentVersion MatchingComponentInt
+
+#define ExtractingComponentVersion ExtractingComponentInt
+
+#define BDecComponentVersion BDecComponentInt
+
+#define GDecComponentVersion GDecComponentInt
+
+
+typedef ComponentInt ComponentCertificateSerialNumber; /* INTEGER */
+
+#define MatchingComponentCertificateSerialNumber MatchingComponentInt
+
+#define ExtractingComponentCertificateSerialNumber ExtractingComponentInt
+
+#define BDecComponentCertificateSerialNumber BDecComponentInt
+
+#define GDecComponentCertificateSerialNumber GDecComponentInt
+
+
+typedef ComponentOid ComponentAttributeType; /* OBJECT IDENTIFIER */
+
+#define MatchingComponentAttributeType MatchingComponentOid
+
+#define ExtractingComponentAttributeType ExtractingComponentOid
+
+#define BDecComponentAttributeType BDecComponentOid
+
+#define GDecComponentAttributeType GDecComponentOid
+
+
+typedef ComponentBits ComponentUniqueIdentifier; /* BIT STRING */
+
+#define MatchingComponentUniqueIdentifier MatchingComponentBits
+
+#define ExtractingComponentUniqueIdentifier ExtractingComponentBits
+
+#define BDecComponentUniqueIdentifier BDecComponentBits
+
+#define GDecComponentUniqueIdentifier GDecComponentBits
+
+
+typedef struct AlgorithmIdentifier /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentOid algorithm; /* OBJECT IDENTIFIER */
+ ComponentAnyDefinedBy parameters; /* ANY DEFINED BY algorithm OPTIONAL */
+} ComponentAlgorithmIdentifier;
+
+int MatchingComponentAlgorithmIdentifier PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentAlgorithmIdentifier PROTO (( void* mem_op, ComponentReference *cr, ComponentAlgorithmIdentifier *comp ));
+
+
+int BDecComponentAlgorithmIdentifier PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentAlgorithmIdentifier **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentAlgorithmIdentifier PROTO (( void* mem_op, GenBuf * b, ComponentAlgorithmIdentifier **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct Time /* CHOICE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ enum TimeChoiceId
+ {
+ TIME_UTCTIME,
+ TIME_GENERALIZEDTIME
+ } choiceId;
+ union TimeChoiceUnion
+ {
+ ComponentUTCTime* utcTime; /* < unknown type id ?! > */
+ ComponentGeneralizedTime* generalizedTime; /* < unknown type id ?! > */
+ } a;
+} ComponentTime;
+
+int MatchingComponentTime PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentTime PROTO (( void* mem_op, ComponentReference *cr, ComponentTime *comp ));
+
+
+int BDecComponentTime PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentTime **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentTime PROTO (( void* mem_op, GenBuf * b, ComponentTime **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct Extension /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentOid extnID; /* OBJECT IDENTIFIER */
+ ComponentBool* critical; /* BOOLEAN DEFAULT FALSE */
+ ComponentOcts extnValue; /* OCTET STRING */
+} ComponentExtension;
+
+int MatchingComponentExtension PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentExtension PROTO (( void* mem_op, ComponentReference *cr, ComponentExtension *comp ));
+
+
+int BDecComponentExtension PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentExtension **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentExtension PROTO (( void* mem_op, GenBuf * b, ComponentExtension **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct AttributeTypeAndValue /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentAttributeType type; /* AttributeType */
+ ComponentAnyDefinedBy value; /* ANY DEFINED BY type */
+} ComponentAttributeTypeAndValue;
+
+int MatchingComponentAttributeTypeAndValue PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentAttributeTypeAndValue PROTO (( void* mem_op, ComponentReference *cr, ComponentAttributeTypeAndValue *comp ));
+
+
+int BDecComponentAttributeTypeAndValue PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentAttributeTypeAndValue **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentAttributeTypeAndValue PROTO (( void* mem_op, GenBuf * b, ComponentAttributeTypeAndValue **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct Validity /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentTime* notBefore; /* Time */
+ ComponentTime* notAfter; /* Time */
+} ComponentValidity;
+
+int MatchingComponentValidity PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentValidity PROTO (( void* mem_op, ComponentReference *cr, ComponentValidity *comp ));
+
+
+int BDecComponentValidity PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentValidity **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentValidity PROTO (( void* mem_op, GenBuf * b, ComponentValidity **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct SubjectPublicKeyInfo /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentAlgorithmIdentifier* algorithm; /* AlgorithmIdentifier */
+ ComponentBits subjectPublicKey; /* BIT STRING */
+} ComponentSubjectPublicKeyInfo;
+
+int MatchingComponentSubjectPublicKeyInfo PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentSubjectPublicKeyInfo PROTO (( void* mem_op, ComponentReference *cr, ComponentSubjectPublicKeyInfo *comp ));
+
+
+int BDecComponentSubjectPublicKeyInfo PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentSubjectPublicKeyInfo **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentSubjectPublicKeyInfo PROTO (( void* mem_op, GenBuf * b, ComponentSubjectPublicKeyInfo **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef ComponentList ComponentExtensions; /* SEQUENCE SIZE 1..MAX OF Extension */
+
+int MatchingComponentExtensions PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentExtensions PROTO (( void* mem_op, ComponentReference *cr, ComponentExtensions *comp ));
+
+
+int BDecComponentExtensions PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentExtensions **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentExtensions PROTO (( void* mem_op, GenBuf * b, ComponentExtensions **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef ComponentList ComponentRelativeDistinguishedName; /* SET OF AttributeTypeAndValue */
+
+int MatchingComponentRelativeDistinguishedName PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentRelativeDistinguishedName PROTO (( void* mem_op, ComponentReference *cr, ComponentRelativeDistinguishedName *comp ));
+
+
+int BDecComponentRelativeDistinguishedName PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentRelativeDistinguishedName **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentRelativeDistinguishedName PROTO (( void* mem_op, GenBuf * b, ComponentRelativeDistinguishedName **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef ComponentList ComponentRDNSequence; /* SEQUENCE OF RelativeDistinguishedName */
+
+int MatchingComponentRDNSequence PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentRDNSequence PROTO (( void* mem_op, ComponentReference *cr, ComponentRDNSequence *comp ));
+
+
+int BDecComponentRDNSequence PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentRDNSequence **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentRDNSequence PROTO (( void* mem_op, GenBuf * b, ComponentRDNSequence **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct Name /* CHOICE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ enum NameChoiceId
+ {
+ NAME_RDNSEQUENCE
+ } choiceId;
+ union NameChoiceUnion
+ {
+ ComponentRDNSequence* rdnSequence; /* RDNSequence */
+ } a;
+} ComponentName;
+
+int MatchingComponentName PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentName PROTO (( void* mem_op, ComponentReference *cr, ComponentName *comp ));
+
+
+int BDecComponentName PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentName **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentName PROTO (( void* mem_op, GenBuf * b, ComponentName **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct TBSCertificate /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentVersion* version; /* [0] Version DEFAULT v1 */
+ ComponentCertificateSerialNumber serialNumber; /* CertificateSerialNumber */
+ ComponentAlgorithmIdentifier* signature; /* AlgorithmIdentifier */
+ ComponentName* issuer; /* Name */
+ ComponentValidity* validity; /* Validity */
+ ComponentName* subject; /* Name */
+ ComponentSubjectPublicKeyInfo* subjectPublicKeyInfo; /* SubjectPublicKeyInfo */
+ ComponentUniqueIdentifier issuerUniqueIdentifier; /* [1] IMPLICIT UniqueIdentifier OPTIONAL */
+ ComponentUniqueIdentifier subjectUniqueIdentifier; /* [2] IMPLICIT UniqueIdentifier OPTIONAL */
+ ComponentExtensions* extensions; /* [3] Extensions OPTIONAL */
+} ComponentTBSCertificate;
+
+int MatchingComponentTBSCertificate PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentTBSCertificate PROTO (( void* mem_op, ComponentReference *cr, ComponentTBSCertificate *comp ));
+
+
+int BDecComponentTBSCertificate PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentTBSCertificate **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentTBSCertificate PROTO (( void* mem_op, GenBuf * b, ComponentTBSCertificate **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct Certificate /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentTBSCertificate* toBeSigned; /* TBSCertificate */
+ ComponentAlgorithmIdentifier* signatureAlgorithm; /* AlgorithmIdentifier */
+ ComponentBits signature; /* BIT STRING */
+} ComponentCertificate;
+
+int MatchingComponentCertificate PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentCertificate PROTO (( void* mem_op, ComponentReference *cr, ComponentCertificate *comp ));
+
+
+int BDecComponentCertificate PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentCertificate **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentCertificate PROTO (( void* mem_op, GenBuf * b, ComponentCertificate **v, AsnLen *bytesDecoded, int mode));
+
+
+
+/* ========== Object Declarations ========== */
+
+
+/* ========== Object Set Declarations ========== */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#endif /* conditional include of certificate.h */
diff --git a/contrib/slapd-modules/comp_match/componentlib.c b/contrib/slapd-modules/comp_match/componentlib.c
new file mode 100644
index 0000000..6f5460a
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/componentlib.c
@@ -0,0 +1,2370 @@
+/* Copyright 2004 IBM Corporation
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ */
+/* ACKNOWLEDGEMENTS
+ * This work originally developed by Sang Seok Lim
+ * 2004/06/18 03:20:00 slim@OpenLDAP.org
+ */
+
+#include "portable.h"
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ldap_pvt.h>
+#include "lutil.h"
+#include <ldap.h>
+#include "slap.h"
+#include "component.h"
+
+#include "componentlib.h"
+#include "asn.h"
+#include <asn-gser.h>
+#include <stdlib.h>
+
+#include <string.h>
+
+#ifndef SLAPD_COMP_MATCH
+#define SLAPD_COMP_MATCH SLAPD_MOD_DYNAMIC
+#endif
+
+#ifdef SLAPD_COMP_MATCH
+/*
+ * Matching function : BIT STRING
+ */
+int
+MatchingComponentBits ( char* oid, ComponentSyntaxInfo *csi_attr,
+ ComponentSyntaxInfo *csi_assert )
+{
+ int rc;
+ MatchingRule* mr;
+ ComponentBits *a, *b;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule(oid, (AsnTypeId)csi_attr->csi_comp_desc->cd_type_id );
+ if ( mr )
+ return component_value_match( mr, csi_attr , csi_assert );
+ }
+ a = ((ComponentBits*)csi_attr);
+ b = ((ComponentBits*)csi_assert);
+ rc = ( a->value.bitLen == b->value.bitLen &&
+ strncmp( a->value.bits,b->value.bits,a->value.bitLen ) == 0 );
+ return rc ? LDAP_COMPARE_TRUE:LDAP_COMPARE_FALSE;
+}
+
+/*
+ * Free function: BIT STRING
+ */
+void
+FreeComponentBits ( ComponentBits* v ) {
+ FreeAsnBits( &v->value );
+}
+
+/*
+ * GSER Encoder : BIT STRING
+ */
+int
+GEncComponentBits ( GenBuf *b, ComponentBits *in )
+{
+ GAsnBits bits = {0};
+
+ bits.value = in->value;
+ if ( !in )
+ return (-1);
+ return GEncAsnBitsContent ( b, &bits);
+}
+
+
+/*
+ * GSER Decoder : BIT STRING
+ */
+int
+GDecComponentBits ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen;
+ void* component_values;
+ ComponentBits* k, **k2;
+ GAsnBits result;
+
+ k = (ComponentBits*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentBits**) v;
+ *k2 = (ComponentBits*) CompAlloc( mem_op, sizeof( ComponentBits ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( GDecAsnBitsContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result.value;
+ k->comp_desc = get_component_description (BASICTYPE_BITSTRING);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : BIT STRING
+ */
+int
+BDecComponentBitsTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentBits ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentBits ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v,
+ AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentBits* k, **k2;
+ AsnBits result;
+
+ k = (ComponentBits*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentBits**) v;
+ *k2 = (ComponentBits*) CompAlloc( mem_op, sizeof( ComponentBits ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecAsnBits ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecAsnBitsContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+
+ if ( rc < 0 ) {
+ if ( k ) CompFree( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value = result;
+ k->comp_desc = get_component_description (BASICTYPE_BITSTRING);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component GSER BMPString Encoder
+ */
+int
+GEncComponentBMPString ( GenBuf *b, ComponentBMPString *in )
+{
+ GBMPString t = {0};
+
+ if ( !in || in->value.octetLen <= 0 )
+ return (-1);
+ t.value = in->value;
+ return GEncBMPStringContent ( b, &t );
+}
+
+/*
+ * Component GSER BMPString Decoder
+ */
+int
+GDecComponentBMPString ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode)
+{
+ char* peek_head;
+ int i, strLen;
+ void* component_values;
+ ComponentBMPString* k, **k2;
+ GBMPString result;
+
+ k = (ComponentBMPString*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentBMPString**) v;
+ *k2 = (ComponentBMPString*) CompAlloc( mem_op, sizeof( ComponentBMPString ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ *bytesDecoded = 0;
+
+ if ( GDecBMPStringContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value = result.value;
+ k->comp_desc = get_component_description (BASICTYPE_BMP_STR);
+
+ return LDAP_SUCCESS;
+
+}
+
+/*
+ * Component BER BMPString Decoder
+ */
+int
+BDecComponentBMPStringTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentBMPString ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentBMPString ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v,
+ AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentBMPString* k, **k2;
+ BMPString result;
+
+ k = (ComponentBMPString*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentBMPString**) v;
+ *k2 = (ComponentBMPString*) CompAlloc( mem_op, sizeof( ComponentBMPString ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecBMPString ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecBMPStringContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+
+ if ( rc < 0 ) {
+ if ( k ) CompFree( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value = result;
+ k->comp_desc = get_component_description (BASICTYPE_BMP_STR);
+
+ return LDAP_SUCCESS;
+
+}
+
+/*
+ * Component GSER Encoder : UTF8 String
+ */
+int
+GEncComponentUTF8String ( GenBuf *b, ComponentUTF8String *in )
+{
+ GUTF8String t = {0};
+ if ( !in || in->value.octetLen <= 0 )
+ return (-1);
+ t.value = in->value;
+ return GEncUTF8StringContent ( b, &t );
+}
+
+/*
+ * Component GSER Decoder : UTF8 String
+ */
+int
+GDecComponentUTF8String ( void* mem_op, GenBuf *b, void *v,
+ AsnLen *bytesDecoded, int mode) {
+ char* peek_head;
+ int i, strLen;
+ void* component_values;
+ ComponentUTF8String* k, **k2;
+ GUTF8String result;
+
+ k = (ComponentUTF8String*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentUTF8String**) v;
+ *k2 = (ComponentUTF8String*)CompAlloc( mem_op, sizeof( ComponentUTF8String ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ *bytesDecoded = 0;
+
+ if ( GDecUTF8StringContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value = result.value;
+ k->comp_desc = get_component_description (BASICTYPE_UTF8_STR);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : UTF8String
+ */
+int
+BDecComponentUTF8StringTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentUTF8String ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentUTF8String ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len,
+ void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentUTF8String* k, **k2;
+ UTF8String result;
+
+ k = (ComponentUTF8String*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentUTF8String**) v;
+ *k2 = (ComponentUTF8String*) CompAlloc( mem_op, sizeof( ComponentUTF8String ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecUTF8String ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecUTF8StringContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value = result;
+ k->comp_desc = get_component_description (BASICTYPE_UTF8_STR);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component GSER Encoder : Teletex String
+ */
+int
+GEncComponentTeletexString ( GenBuf *b, ComponentTeletexString *in )
+{
+ GTeletexString t = {0};
+
+ if ( !in || in->value.octetLen <= 0 )
+ return (-1);
+ t.value = in->value;
+ return GEncTeletexStringContent ( b, &t );
+}
+
+/*
+ * Component GSER Decoder : Teletex String
+ */
+int
+GDecComponentTeletexString ( void* mem_op, GenBuf *b, void *v,
+ AsnLen *bytesDecoded, int mode) {
+ char* peek_head;
+ int i, strLen;
+ void* component_values;
+ ComponentTeletexString* k, **k2;
+ GTeletexString result;
+
+ k = (ComponentTeletexString*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentTeletexString**) v;
+ *k2 = (ComponentTeletexString*)CompAlloc( mem_op, sizeof( ComponentTeletexString ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ *bytesDecoded = 0;
+
+ if ( GDecTeletexStringContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value = result.value;
+ k->comp_desc = get_component_description (BASICTYPE_VIDEOTEX_STR);
+
+ return LDAP_SUCCESS;
+}
+
+
+/*
+ * Matching function : BOOLEAN
+ */
+int
+MatchingComponentBool(char* oid, ComponentSyntaxInfo* csi_attr,
+ ComponentSyntaxInfo* csi_assert )
+{
+ MatchingRule* mr;
+ ComponentBool *a, *b;
+
+ if( oid ) {
+ mr = retrieve_matching_rule(oid, csi_attr->csi_comp_desc->cd_type_id );
+ if ( mr )
+ return component_value_match( mr, csi_attr , csi_assert );
+ }
+
+ a = ((ComponentBool*)csi_attr);
+ b = ((ComponentBool*)csi_assert);
+
+ return (a->value == b->value) ? LDAP_COMPARE_TRUE:LDAP_COMPARE_FALSE;
+}
+
+/*
+ * GSER Encoder : BOOLEAN
+ */
+int
+GEncComponentBool ( GenBuf *b, ComponentBool *in )
+{
+ GAsnBool t = {0};
+
+ if ( !in )
+ return (-1);
+ t.value = in->value;
+ return GEncAsnBoolContent ( b, &t );
+}
+
+/*
+ * GSER Decoder : BOOLEAN
+ */
+int
+GDecComponentBool ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen;
+ ComponentBool* k, **k2;
+ GAsnBool result;
+
+ k = (ComponentBool*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentBool**) v;
+ *k2 = (ComponentBool*) CompAlloc( mem_op, sizeof( ComponentBool ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( GDecAsnBoolContent( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value = result.value;
+ k->comp_desc = get_component_description (BASICTYPE_BOOLEAN);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : BOOLEAN
+ */
+int
+BDecComponentBoolTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentBool ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentBool ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v,
+ AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ ComponentBool* k, **k2;
+ AsnBool result;
+
+ k = (ComponentBool*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentBool**) v;
+ *k2 = (ComponentBool*) CompAlloc( mem_op, sizeof( ComponentBool ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecAsnBool ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecAsnBoolContent( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value = result;
+ k->comp_desc = get_component_description (BASICTYPE_BOOLEAN);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Matching function : ENUMERATE
+ */
+int
+MatchingComponentEnum ( char* oid, ComponentSyntaxInfo *csi_attr,
+ ComponentSyntaxInfo *csi_assert )
+{
+ int rc;
+ MatchingRule* mr;
+ ComponentEnum *a, *b;
+
+ if( oid ) {
+ mr = retrieve_matching_rule(oid, csi_attr->csi_comp_desc->cd_type_id );
+ if ( mr )
+ return component_value_match( mr, csi_attr , csi_assert );
+ }
+ a = ((ComponentEnum*)csi_attr);
+ b = ((ComponentEnum*)csi_assert);
+ rc = (a->value == b->value);
+
+ return rc ? LDAP_COMPARE_TRUE:LDAP_COMPARE_FALSE;
+}
+
+/*
+ * GSER Encoder : ENUMERATE
+ */
+int
+GEncComponentEnum ( GenBuf *b, ComponentEnum *in )
+{
+ GAsnEnum t = {0};
+
+ if ( !in )
+ return (-1);
+ t.value = in->value;
+ return GEncAsnEnumContent ( b, &t );
+}
+
+/*
+ * GSER Decoder : ENUMERATE
+ */
+int
+GDecComponentEnum ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen;
+ void* component_values;
+ ComponentEnum* k, **k2;
+ GAsnEnum result;
+
+ k = (ComponentEnum*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentEnum**) v;
+ *k2 = (ComponentEnum*) CompAlloc( mem_op, sizeof( ComponentEnum ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( GDecAsnEnumContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value_identifier.bv_val = result.value_identifier;
+ k->value_identifier.bv_len = result.len;
+
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentEnum;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentEnum;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentEnum;
+ k->comp_desc->cd_free = (comp_free_func*)NULL;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_ENUMERATED;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentEnum;
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : ENUMERATE
+ */
+int
+BDecComponentEnumTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentEnum ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentEnum ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v,
+ AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentEnum* k, **k2;
+ AsnEnum result;
+
+ k = (ComponentEnum*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentEnum**) v;
+ *k2 = (ComponentEnum*) CompAlloc( mem_op, sizeof( ComponentEnum ) );
+ if ( k ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecAsnEnum ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecAsnEnumContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value = result;
+
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentEnum;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentEnum;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentEnum;
+ k->comp_desc->cd_free = (comp_free_func*)NULL;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_ENUMERATED;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentEnum;
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component GSER Encoder : IA5String
+ */
+int
+GEncComponentIA5Stirng ( GenBuf *b, ComponentIA5String* in )
+{
+ GIA5String t = {0};
+ t.value = in->value;
+ if ( !in || in->value.octetLen <= 0 ) return (-1);
+ return GEncIA5StringContent( b, &t );
+}
+
+/*
+ * Component BER Decoder : IA5String
+ */
+int
+BDecComponentIA5StringTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentIA5String ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentIA5String ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v,
+ AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentIA5String* k, **k2;
+ IA5String result;
+
+ k = (ComponentIA5String*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentIA5String**) v;
+ *k2 = (ComponentIA5String*) CompAlloc( mem_op, sizeof( ComponentIA5String ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecIA5String ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecIA5StringContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+
+ k->value = result;
+
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentIA5String;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentIA5String;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentIA5String;
+ k->comp_desc->cd_free = (comp_free_func*)FreeComponentIA5String;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_IA5_STR;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentIA5String;
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Matching function : INTEGER
+ */
+int
+MatchingComponentInt(char* oid, ComponentSyntaxInfo* csi_attr,
+ ComponentSyntaxInfo* csi_assert )
+{
+ MatchingRule* mr;
+ ComponentInt *a, *b;
+
+ if( oid ) {
+ /* check if this ASN type's matching rule is overridden */
+ mr = retrieve_matching_rule(oid, csi_attr->csi_comp_desc->cd_type_id );
+ /* if existing function is overridden, call the overriding
+function*/
+ if ( mr )
+ return component_value_match( mr, csi_attr , csi_assert );
+ }
+ a = ((ComponentInt*)csi_attr);
+ b = ((ComponentInt*)csi_assert);
+
+ return ( a->value == b->value ) ? LDAP_COMPARE_TRUE:LDAP_COMPARE_FALSE;
+}
+
+/*
+ * GSER Encoder : INTEGER
+ */
+int
+GEncComponentInt ( GenBuf *b, ComponentInt* in )
+{
+ GAsnInt t = {0};
+
+ if ( !in )
+ return (-1);
+ t.value = in->value;
+ return GEncAsnIntContent ( b, &t );
+}
+
+/*
+ * GSER Decoder : INTEGER
+ */
+int
+GDecComponentInt( void* mem_op, GenBuf * b, void *v, AsnLen *bytesDecoded, int mode)
+{
+ char* peek_head;
+ int i, strLen;
+ void* component_values;
+ ComponentInt* k, **k2;
+ GAsnInt result;
+
+ k = (ComponentInt*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentInt**) v;
+ *k2 = (ComponentInt*) CompAlloc( mem_op, sizeof( ComponentInt ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( GDecAsnIntContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result.value;
+ k->comp_desc = get_component_description (BASICTYPE_INTEGER );
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : INTEGER
+ */
+int
+BDecComponentIntTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentInt ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentInt ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v,
+ AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentInt* k, **k2;
+ AsnInt result;
+
+ k = (ComponentInt*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentInt**) v;
+ *k2 = (ComponentInt*) CompAlloc( mem_op, sizeof( ComponentInt ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecAsnInt ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecAsnIntContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ k->value = result;
+
+ k->comp_desc = get_component_description (BASICTYPE_INTEGER );
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Matching function : NULL
+ */
+int
+MatchingComponentNull ( char *oid, ComponentSyntaxInfo *csi_attr,
+ ComponentSyntaxInfo *csi_assert )
+{
+ MatchingRule* mr;
+ ComponentNull *a, *b;
+
+ if( oid ) {
+ mr = retrieve_matching_rule(oid, csi_attr->csi_comp_desc->cd_type_id );
+ if ( mr )
+ return component_value_match( mr, csi_attr , csi_assert );
+ }
+ a = ((ComponentNull*)csi_attr);
+ b = ((ComponentNull*)csi_assert);
+
+ return (a->value == b->value) ? LDAP_COMPARE_TRUE:LDAP_COMPARE_FALSE;
+}
+
+/*
+ * GSER Encoder : NULL
+ */
+int
+GEncComponentNull ( GenBuf *b, ComponentNull *in )
+{
+ GAsnNull t = {0};
+
+ if ( !in )
+ return (-1);
+ t.value = in->value;
+ return GEncAsnNullContent ( b, &t );
+}
+
+/*
+ * GSER Decoder : NULL
+ */
+int
+GDecComponentNull ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen;
+ void* component_values;
+ ComponentNull* k, **k2;
+ GAsnNull result;
+
+ k = (ComponentNull*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentNull**) v;
+ *k2 = (ComponentNull*) CompAlloc( mem_op, sizeof( ComponentNull ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( GDecAsnNullContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result.value;
+
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentNull;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentNull;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentNull;
+ k->comp_desc->cd_free = (comp_free_func*)FreeComponentNull;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_NULL;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentNull;
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : NULL
+ */
+int
+BDecComponentNullTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ return BDecComponentNull ( mem_op, b, 0, 0, v,bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentNull ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v,
+ AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentNull* k, **k2;
+ AsnNull result;
+
+ k = (ComponentNull*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentNull**) v;
+ *k2 = (ComponentNull*) CompAlloc( mem_op, sizeof( ComponentNull ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecAsnNull ( mem_op, b, &result, bytesDecoded );
+ }
+ else {
+ rc = BDecAsnNullContent ( mem_op, b, tagId, len, &result, bytesDecoded);
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result;
+
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentNull;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentNull;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentNull;
+ k->comp_desc->cd_free = (comp_free_func*)FreeComponentNull;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_NULL;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentNull;
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : NumericString
+ */
+int
+BDecComponentNumericStringTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentNumericString ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentNumericString ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentNumericString* k, **k2;
+ NumericString result;
+
+ k = (ComponentNumericString*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentNumericString**) v;
+ *k2 = (ComponentNumericString*) CompAlloc( mem_op, sizeof( ComponentNumericString ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecNumericString ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecNumericStringContent ( mem_op, b, tagId, len, &result, bytesDecoded);
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result;
+
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentNumericString;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentNumericString;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentNumericString;
+ k->comp_desc->cd_free = (comp_free_func*)FreeComponentNumericString;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_NUMERIC_STR;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentNumericString;
+
+ return LDAP_SUCCESS;
+}
+
+
+/*
+ * Free function : OCTET STRING
+ */
+void
+FreeComponentOcts ( ComponentOcts* v) {
+ FreeAsnOcts( &v->value );
+}
+
+/*
+ * Matching function : OCTET STRING
+ */
+int
+MatchingComponentOcts ( char* oid, ComponentSyntaxInfo* csi_attr,
+ ComponentSyntaxInfo* csi_assert )
+{
+ int rc;
+ MatchingRule* mr;
+ ComponentOcts *a, *b;
+
+ if( oid ) {
+ mr = retrieve_matching_rule(oid, csi_attr->csi_comp_desc->cd_type_id );
+ if ( mr )
+ return component_value_match( mr, csi_attr , csi_assert );
+ }
+ a = (ComponentOcts*) csi_attr;
+ b = (ComponentOcts*) csi_assert;
+ /* Assume that both of OCTET string has end of string character */
+ if ( (a->value.octetLen == b->value.octetLen) &&
+ strncmp ( a->value.octs, b->value.octs, a->value.octetLen ) == 0 )
+ return LDAP_COMPARE_TRUE;
+ else
+ return LDAP_COMPARE_FALSE;
+}
+
+/*
+ * GSER Encoder : OCTET STRING
+ */
+int
+GEncComponentOcts ( GenBuf* b, ComponentOcts *in )
+{
+ GAsnOcts t = {0};
+ if ( !in || in->value.octetLen <= 0 )
+ return (-1);
+
+ t.value = in->value;
+ return GEncAsnOctsContent ( b, &t );
+}
+
+/*
+ * GSER Decoder : OCTET STRING
+ */
+int
+GDecComponentOcts ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char *peek_head, *data;
+ int i, j, strLen;
+ void* component_values;
+ ComponentOcts* k, **k2;
+ GAsnOcts result;
+
+ k = (ComponentOcts*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentOcts**) v;
+ *k2 = (ComponentOcts*) CompAlloc( mem_op, sizeof( ComponentOcts ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( GDecAsnOctsContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result.value;
+
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentOcts;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentOcts;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentOcts;
+ k->comp_desc->cd_free = (comp_free_func*)FreeComponentOcts;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_OCTETSTRING;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentOcts;
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : OCTET STRING
+ */
+int
+BDecComponentOctsTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentOcts ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentOcts ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v,
+ AsnLen *bytesDecoded, int mode )
+{
+ char *peek_head, *data;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentOcts* k, **k2;
+ AsnOcts result;
+
+ k = (ComponentOcts*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentOcts**) v;
+ *k2 = (ComponentOcts*) CompAlloc( mem_op, sizeof( ComponentOcts ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecAsnOcts ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecAsnOctsContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result;
+
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentOcts;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentOcts;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentOcts;
+ k->comp_desc->cd_free = (comp_free_func*)FreeComponentOcts;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_OCTETSTRING;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentOcts;
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Matching function : OBJECT IDENTIFIER
+ */
+int
+MatchingComponentOid ( char *oid, ComponentSyntaxInfo *csi_attr ,
+ ComponentSyntaxInfo *csi_assert )
+{
+ int rc;
+ MatchingRule* mr;
+ ComponentOid *a, *b;
+
+ if( oid ) {
+ mr = retrieve_matching_rule(oid, csi_attr->csi_comp_desc->cd_type_id );
+ if ( mr )
+ return component_value_match( mr, csi_attr , csi_assert );
+ }
+
+ a = (ComponentOid*)csi_attr;
+ b = (ComponentOid*)csi_assert;
+ if ( a->value.octetLen != b->value.octetLen )
+ return LDAP_COMPARE_FALSE;
+ rc = ( strncmp( a->value.octs, b->value.octs, a->value.octetLen ) == 0 );
+
+ return rc ? LDAP_COMPARE_TRUE:LDAP_COMPARE_FALSE;
+}
+
+/*
+ * GSER Encoder : OID
+ */
+GEncComponentOid ( GenBuf *b, ComponentOid *in )
+{
+ GAsnOid t = {0};
+
+ if ( !in || in->value.octetLen <= 0 )
+ return (-1);
+ t.value = in->value;
+ return GEncAsnOidContent( b, (GAsnOcts*)&t );
+}
+
+/*
+ * GSER Decoder : OID
+ */
+int
+GDecAsnDescOidContent ( void* mem_op, GenBuf *b, GAsnOid *result, AsnLen *bytesDecoded ){
+ AttributeType *ad_type;
+ struct berval name;
+ char* peek_head;
+ int strLen;
+
+ strLen = LocateNextGSERToken ( mem_op, b, &peek_head, GSER_NO_COPY );
+ name.bv_val = peek_head;
+ name.bv_len = strLen;
+
+ ad_type = at_bvfind( &name );
+
+ if ( !ad_type )
+ return LDAP_DECODING_ERROR;
+
+ peek_head = ad_type->sat_atype.at_oid;
+ strLen = strlen ( peek_head );
+
+ result->value.octs = (char*)EncodeComponentOid ( mem_op, peek_head , &strLen );
+ result->value.octetLen = strLen;
+ return LDAP_SUCCESS;
+}
+
+int
+IsNumericOid ( char* peek_head , int strLen ) {
+ int i;
+ int num_dot;
+ for ( i = 0, num_dot = 0 ; i < strLen ; i++ ) {
+ if ( peek_head[i] == '.' ) num_dot++;
+ else if ( peek_head[i] > '9' || peek_head[i] < '0' )
+ return (-1);
+ }
+ if ( num_dot )
+ return (1);
+ else
+ return (-1);
+}
+
+int
+GDecComponentOid ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentOid* k, **k2;
+ GAsnOid result;
+
+ k = (ComponentOid*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentOid**) v;
+ *k2 = (ComponentOid*) CompAlloc( mem_op, sizeof( ComponentOid ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ strLen = LocateNextGSERToken ( mem_op, b, &peek_head, GSER_PEEK );
+ if ( IsNumericOid ( peek_head , strLen ) >= 1 ) {
+ /* numeric-oid */
+ if ( GDecAsnOidContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ }
+ else {
+ /*descr*/
+ if ( GDecAsnDescOidContent ( mem_op, b, &result, bytesDecoded ) < 0 ){
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ }
+ k->value = result.value;
+ k->comp_desc = get_component_description (BASICTYPE_OID);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : OID
+ */
+int
+BDecComponentOidTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentOid ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentOid ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v,
+ AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentOid* k, **k2;
+ AsnOid result;
+
+ k = (ComponentOid*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentOid**) v;
+ *k2 = (ComponentOid*) CompAlloc( mem_op, sizeof( ComponentOid ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecAsnOid ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecAsnOidContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result;
+
+ k->comp_desc = get_component_description (BASICTYPE_OID);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : PrintableString
+ */
+
+int
+BDecComponentPrintableStringTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ return BDecComponentPrintableString ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentPrintableString( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentPrintableString* k, **k2;
+ AsnOid result;
+
+ k = (ComponentPrintableString*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentPrintableString**) v;
+ *k2 = (ComponentPrintableString*) CompAlloc( mem_op, sizeof( ComponentPrintableString ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ) {
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecPrintableString ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecPrintableStringContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result;
+
+ k->comp_desc = get_component_description (BASICTYPE_PRINTABLE_STR);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : TeletexString
+ */
+
+int
+BDecComponentTeletexStringTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ return BDecComponentTeletexString ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentTeletexString( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentTeletexString* k, **k2;
+ AsnOid result;
+
+ k = (ComponentTeletexString*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentTeletexString**) v;
+ *k2 = (ComponentTeletexString*) CompAlloc( mem_op, sizeof( ComponentTeletexString ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ) {
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecTeletexString ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecTeletexStringContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result;
+
+ k->comp_desc = get_component_description (BASICTYPE_T61_STR);
+
+ return LDAP_SUCCESS;
+}
+
+
+/*
+ * Matching function : Real
+ */
+int
+MatchingComponentReal (char* oid, ComponentSyntaxInfo *csi_attr,
+ ComponentSyntaxInfo *csi_assert )
+{
+ int rc;
+ MatchingRule* mr;
+ ComponentReal *a, *b;
+
+ if( oid ) {
+ mr = retrieve_matching_rule(oid, csi_attr->csi_comp_desc->cd_type_id );
+ if ( mr )
+ return component_value_match( mr, csi_attr , csi_assert );
+ }
+ a = (ComponentReal*)csi_attr;
+ b = (ComponentReal*)csi_assert;
+ rc = (a->value == b->value);
+
+ return rc ? LDAP_COMPARE_TRUE:LDAP_COMPARE_FALSE;
+}
+
+/*
+ * GSER Encoder : Real
+ */
+int
+GEncComponentReal ( GenBuf *b, ComponentReal *in )
+{
+ GAsnReal t = {0};
+ if ( !in )
+ return (-1);
+ t.value = in->value;
+ return GEncAsnRealContent ( b, &t );
+}
+
+/*
+ * GSER Decoder : Real
+ */
+int
+GDecComponentReal ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen;
+ void* component_values;
+ ComponentReal* k, **k2;
+ GAsnReal result;
+
+ k = (ComponentReal*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentReal**) v;
+ *k2 = (ComponentReal*) CompAlloc( mem_op, sizeof( ComponentReal ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( GDecAsnRealContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result.value;
+ k->comp_desc = get_component_description (BASICTYPE_REAL);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : Real
+ */
+int
+BDecComponentRealTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentReal ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentReal ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentReal* k, **k2;
+ AsnReal result;
+
+ k = (ComponentReal*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentReal**) v;
+ *k2 = (ComponentReal*) CompAlloc( mem_op, sizeof( ComponentReal ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecAsnReal ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecAsnRealContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result;
+ k->comp_desc = get_component_description (BASICTYPE_REAL);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Matching function : Relative OID
+ */
+int
+MatchingComponentRelativeOid ( char* oid, ComponentSyntaxInfo *csi_attr,
+ ComponentSyntaxInfo *csi_assert )
+{
+ int rc;
+ MatchingRule* mr;
+ ComponentRelativeOid *a, *b;
+
+ if( oid ) {
+ mr = retrieve_matching_rule(oid, csi_attr->csi_comp_desc->cd_type_id );
+ if ( mr )
+ return component_value_match( mr, csi_attr , csi_assert );
+ }
+
+ a = (ComponentRelativeOid*)csi_attr;
+ b = (ComponentRelativeOid*)csi_assert;
+
+ if ( a->value.octetLen != b->value.octetLen )
+ return LDAP_COMPARE_FALSE;
+ rc = ( strncmp( a->value.octs, b->value.octs, a->value.octetLen ) == 0 );
+
+ return rc ? LDAP_COMPARE_TRUE:LDAP_COMPARE_FALSE;
+}
+
+/*
+ * GSER Encoder : RELATIVE_OID.
+ */
+int
+GEncComponentRelativeOid ( GenBuf *b, ComponentRelativeOid *in )
+{
+ GAsnRelativeOid t = {0};
+
+ if ( !in || in->value.octetLen <= 0 )
+ return (-1);
+ t.value = in->value;
+ return GEncAsnRelativeOidContent ( b , (GAsnOcts*)&t );
+}
+
+/*
+ * GSER Decoder : RELATIVE_OID.
+ */
+int
+GDecComponentRelativeOid ( void* mem_op, GenBuf *b,void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen;
+ void* component_values;
+ ComponentRelativeOid* k, **k2;
+ GAsnRelativeOid result;
+
+ k = (ComponentRelativeOid*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentRelativeOid**) v;
+ *k2 = (ComponentRelativeOid*) CompAlloc( mem_op, sizeof( ComponentRelativeOid ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( GDecAsnRelativeOidContent ( mem_op, b, &result, bytesDecoded ) < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result.value;
+ k->comp_desc = get_component_description (BASICTYPE_OID);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : RELATIVE_OID.
+ */
+int
+BDecComponentRelativeOidTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentRelativeOid ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentRelativeOid ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentRelativeOid* k, **k2;
+ AsnRelativeOid result;
+
+ k = (ComponentRelativeOid*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentRelativeOid**) v;
+ *k2 = (ComponentRelativeOid*) CompAlloc( mem_op, sizeof( ComponentRelativeOid ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecAsnRelativeOid ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecAsnRelativeOidContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result;
+ k->comp_desc = get_component_description (BASICTYPE_OID);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * GSER Encoder : UniversalString
+ */
+int
+GEncComponentUniversalString ( GenBuf *b, ComponentUniversalString *in )
+{
+ GUniversalString t = {0};
+ if ( !in || in->value.octetLen <= 0 )
+ return (-1);
+ t.value = in->value;
+ return GEncUniversalStringContent( b, &t );
+}
+
+/*
+ * GSER Decoder : UniversalString
+ */
+static int
+UTF8toUniversalString( char* octs, int len){
+ /* Need to be Implemented */
+ return LDAP_SUCCESS;
+}
+
+int
+GDecComponentUniversalString ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode )
+{
+ if ( GDecComponentUTF8String ( mem_op, b, v, bytesDecoded, mode) < 0 )
+ UTF8toUniversalString( ((ComponentUniversalString*)v)->value.octs, ((ComponentUniversalString*)v)->value.octetLen );
+ return LDAP_DECODING_ERROR;
+}
+
+/*
+ * Component BER Decoder : UniverseString
+ */
+int
+BDecComponentUniversalStringTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentUniversalString ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentUniversalString ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentUniversalString* k, **k2;
+ UniversalString result;
+
+ k = (ComponentUniversalString*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentUniversalString**) v;
+ *k2 = (ComponentUniversalString*) CompAlloc( mem_op, sizeof( ComponentUniversalString ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecUniversalString ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecUniversalStringContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ if ( rc < 0 ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->value = result;
+ k->comp_desc = get_component_description (BASICTYPE_UNIVERSAL_STR);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Component BER Decoder : VisibleString
+ */
+int
+BDecComponentVisibleStringTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode ) {
+ return BDecComponentVisibleString ( mem_op, b, 0, 0, v, bytesDecoded, mode|CALL_TAG_DECODER );
+}
+
+int
+BDecComponentVisibleString ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v, AsnLen *bytesDecoded, int mode )
+{
+ char* peek_head;
+ int i, strLen, rc;
+ void* component_values;
+ ComponentVisibleString* k, **k2;
+ VisibleString result;
+
+ k = (ComponentVisibleString*) v;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentVisibleString**) v;
+ *k2 = (ComponentVisibleString*) CompAlloc( mem_op, sizeof( ComponentVisibleString ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ( mode & CALL_TAG_DECODER ){
+ mode = mode & CALL_CONTENT_DECODER;
+ rc = BDecVisibleString ( mem_op, b, &result, bytesDecoded );
+ } else {
+ rc = BDecVisibleStringContent ( mem_op, b, tagId, len, &result, bytesDecoded );
+ }
+ k->value = result;
+ k->comp_desc = get_component_description (BASICTYPE_VISIBLE_STR);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Routines for handling an ANY DEFINED Type
+ */
+
+/* Check if the <select> type CR and the OID of the given ANY type */
+int
+CheckSelectTypeCorrect ( void* mem_op, ComponentAnyInfo* cai, struct berval* select ) {
+ int strLen;
+ AttributeType* ad_type;
+ char* oid;
+ char* result;
+
+ if ( IsNumericOid ( select->bv_val , select->bv_len ) ) {
+ oid = select->bv_val;
+ strLen = select->bv_len;
+ } else {
+ ad_type = at_bvfind( select );
+
+ if ( !ad_type )
+ return LDAP_DECODING_ERROR;
+
+ oid = ad_type->sat_atype.at_oid;
+ strLen = strlen ( oid );
+ }
+ result = EncodeComponentOid ( mem_op, oid , &strLen );
+ if ( !result || strLen <= 0 ) return (-1);
+
+ if ( cai->oid.octetLen == strLen &&
+ strncmp ( cai->oid.octs, result, strLen ) == 0 )
+ return (1);
+ else
+ return (-1);
+}
+
+int
+SetAnyTypeByComponentOid ( ComponentAny *v, ComponentOid *id ) {
+ Hash hash;
+ void *anyInfo;
+
+ /* use encoded oid as hash string */
+ hash = MakeHash (id->value.octs, id->value.octetLen);
+ if (CheckForAndReturnValue (anyOidHashTblG, hash, &anyInfo))
+ v->cai = (ComponentAnyInfo*) anyInfo;
+ else
+ v->cai = NULL;
+
+ if ( !v->cai ) {
+ /*
+ * If not found, the data considered as octet chunk
+ * Yet-to-be-Implemented
+ */
+ }
+ return LDAP_SUCCESS;
+}
+
+void
+SetAnyTypeByComponentInt( ComponentAny *v, ComponentInt id) {
+ Hash hash;
+ void *anyInfo;
+
+ hash = MakeHash ((char*)&id, sizeof (id));
+ if (CheckForAndReturnValue (anyIntHashTblG, hash, &anyInfo))
+ v->cai = (ComponentAnyInfo*) anyInfo;
+ else
+ v->cai = NULL;
+}
+
+int
+GEncComponentAny ( GenBuf *b, ComponentAny *in )
+{
+ if ( in->cai != NULL && in->cai->Encode != NULL )
+ return in->cai->Encode(b, &in->value );
+ else
+ return (-1);
+}
+
+int
+BEncComponentAny ( void* mem_op, GenBuf *b, ComponentAny *result, AsnLen *bytesDecoded, int mode)
+{
+ ComponentAny *k, **k2;
+
+ k = (ComponentAny*) result;
+
+ if ( !k ) return (-1);
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentAny**) result;
+ *k2 = (ComponentAny*) CompAlloc( mem_op, sizeof( ComponentAny ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ((result->cai != NULL) && (result->cai->BER_Decode != NULL)) {
+ result->value = (void*) CompAlloc ( mem_op, result->cai->size );
+ if ( !result->value ) return 0;
+ result->cai->BER_Decode ( mem_op, b, result->value, (int*)bytesDecoded, DEC_ALLOC_MODE_1);
+
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentAny;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentAny;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentAny;
+ k->comp_desc->cd_free = (comp_free_func*)FreeComponentAny;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_ANY;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentAny;
+ return LDAP_SUCCESS;
+ }
+ else {
+ Asn1Error ("ERROR - Component ANY Decode routine is NULL\n");
+ return 0;
+ }
+}
+
+int
+BDecComponentAny ( void* mem_op, GenBuf *b, ComponentAny *result, AsnLen *bytesDecoded, int mode) {
+ int rc;
+ ComponentAny *k, **k2;
+
+ k = (ComponentAny*) result;
+
+ if ( !k ) return (-1);
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentAny**) result;
+ *k2 = (ComponentAny*) CompAlloc( mem_op, sizeof( ComponentAny ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+
+ if ((result->cai != NULL) && (result->cai->BER_Decode != NULL)) {
+ result->cai->BER_Decode ( mem_op, b, (ComponentSyntaxInfo*)&result->value, (int*)bytesDecoded, DEC_ALLOC_MODE_0 );
+
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentAny;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentAny;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentAny;
+ k->comp_desc->cd_free = (comp_free_func*)FreeComponentAny;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_ANY;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentAny;
+ return LDAP_SUCCESS;
+ }
+ else {
+ Asn1Error ("ERROR - Component ANY Decode routine is NULL\n");
+ return 0;
+ }
+}
+
+int
+GDecComponentAny ( void* mem_op, GenBuf *b, ComponentAny *result, AsnLen *bytesDecoded, int mode) {
+ ComponentAny *k, **k2;
+
+ k = (ComponentAny*) result;
+
+ if ( mode & DEC_ALLOC_MODE_0 ) {
+ k2 = (ComponentAny**) result;
+ *k2 = (ComponentAny*) CompAlloc( mem_op, sizeof( ComponentAny ) );
+ if ( !*k2 ) return LDAP_DECODING_ERROR;
+ k = *k2;
+ }
+ if ((result->cai != NULL) && (result->cai->GSER_Decode != NULL)) {
+ result->value = (void*) CompAlloc ( mem_op, result->cai->size );
+ if ( !result->value ) return 0;
+ result->cai->GSER_Decode ( mem_op, b, result->value, (int*)bytesDecoded, DEC_ALLOC_MODE_1);
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !k->comp_desc ) {
+ if ( k ) CompFree ( mem_op, k );
+ return LDAP_DECODING_ERROR;
+ }
+ k->comp_desc->cd_gser_encoder = (encoder_func*)GEncComponentAny;
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentAny;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentAny;
+ k->comp_desc->cd_free = (comp_free_func*)FreeComponentAny;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_extract_i = NULL;
+ k->comp_desc->cd_type_id = BASICTYPE_ANY;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentAny;
+ return LDAP_SUCCESS;
+ }
+ else {
+ Asn1Error ("ERROR - ANY Decode routine is NULL\n");
+ return 0;
+ }
+}
+
+int
+MatchingComponentAny (char* oid, ComponentAny *result, ComponentAny *result2) {
+ void *comp1, *comp2;
+
+ if ( result->comp_desc->cd_type_id == BASICTYPE_ANY )
+ comp1 = result->value;
+ else
+ comp1 = result;
+
+ if ( result2->comp_desc->cd_type_id == BASICTYPE_ANY )
+ comp2 = result2->value;
+ else
+ comp2 = result2;
+
+ if ((result->cai != NULL) && (result->cai->Match != NULL)) {
+ if ( result->comp_desc->cd_type_id == BASICTYPE_ANY )
+ return result->cai->Match(oid, comp1, comp2 );
+ else if ( result2->comp_desc->cd_type_id == BASICTYPE_ANY )
+ return result2->cai->Match(oid, comp1, comp2);
+ else
+ return LDAP_INVALID_SYNTAX;
+ }
+ else {
+ Asn1Error ("ERROR - ANY Matching routine is NULL\n");
+ return LDAP_INVALID_SYNTAX;
+ }
+}
+
+void*
+ExtractingComponentAny ( void* mem_op, ComponentReference* cr, ComponentAny *result ) {
+ if ((result->cai != NULL) && (result->cai->Extract != NULL)) {
+ return (void*) result->cai->Extract( mem_op, cr , result->value );
+ }
+ else {
+ Asn1Error ("ERROR - ANY Extracting routine is NULL\n");
+ return (void*)NULL;
+ }
+}
+
+void
+FreeComponentAny (ComponentAny* any) {
+ if ( any->cai != NULL && any->cai->Free != NULL ) {
+ any->cai->Free( any->value );
+ free ( ((ComponentSyntaxInfo*)any->value)->csi_comp_desc );
+ free ( any->value );
+ }
+ else
+ Asn1Error ("ERROR - ANY Free routine is NULL\n");
+}
+
+void
+InstallAnyByComponentInt (int anyId, ComponentInt intId, unsigned int size,
+ EncodeFcn encode, gser_decoder_func* G_decode,
+ ber_tag_decoder_func* B_decode, ExtractFcn extract,
+ MatchFcn match, FreeFcn free,
+ PrintFcn print)
+{
+ ComponentAnyInfo *a;
+ Hash h;
+
+ a = (ComponentAnyInfo*) malloc(sizeof (ComponentAnyInfo));
+ a->anyId = anyId;
+ a->oid.octs = NULL;
+ a->oid.octetLen = 0;
+ a->intId = intId;
+ a->size = size;
+ a->Encode = encode;
+ a->GSER_Decode = G_decode;
+ a->BER_Decode = B_decode;
+ a->Match = match;
+ a->Extract = extract;
+ a->Free = free;
+ a->Print = print;
+
+ if (anyIntHashTblG == NULL)
+ anyIntHashTblG = InitHash();
+
+ h = MakeHash ((char*)&intId, sizeof (intId));
+
+ if(anyIntHashTblG != NULL)
+ Insert(anyIntHashTblG, a, h);
+}
+
+
+/*
+ * OID and its corresponding decoder can be registered with this func.
+ * If contained types constrained by <select> are used,
+ * their OID and decoder MUST be registered, otherwise it will return no entry.
+ * An open type(ANY type) also need be registered.
+ */
+void
+InstallOidDecoderMapping ( char* ch_oid, EncodeFcn encode, gser_decoder_func* G_decode, ber_tag_decoder_func* B_decode, ExtractFcn extract, MatchFcn match ) {
+ AsnOid oid;
+ int strLen;
+ void* mem_op;
+
+ strLen = strlen( ch_oid );
+ if( strLen <= 0 ) return;
+ mem_op = comp_nibble_memory_allocator ( 128, 16 );
+ oid.octs = EncodeComponentOid ( mem_op, ch_oid, &strLen );
+ oid.octetLen = strLen;
+ if( strLen <= 0 ) return;
+
+
+ InstallAnyByComponentOid ( 0, &oid, 0, encode, G_decode, B_decode,
+ extract, match, NULL, NULL);
+ comp_nibble_memory_free(mem_op);
+}
+
+/*
+ * Look up Oid-decoder mapping table by berval have either
+ * oid or description
+ */
+OidDecoderMapping*
+RetrieveOidDecoderMappingbyBV( struct berval* in ) {
+ if ( IsNumericOid ( in->bv_val, in->bv_len ) )
+ return RetrieveOidDecoderMappingbyOid( in->bv_val, in->bv_len );
+ else
+ return RetrieveOidDecoderMappingbyDesc( in->bv_val, in->bv_len );
+}
+
+/*
+ * Look up Oid-decoder mapping table by dotted OID
+ */
+OidDecoderMapping*
+RetrieveOidDecoderMappingbyOid( char* ch_oid, int oid_len ) {
+ Hash hash;
+ void *anyInfo;
+ AsnOid oid;
+ int strLen;
+ void* mem_op;
+
+ mem_op = comp_nibble_memory_allocator ( 128, 16 );
+ oid.octs = EncodeComponentOid ( mem_op, ch_oid, &oid_len);
+ oid.octetLen = oid_len;
+ if( oid_len <= 0 ) {
+ comp_nibble_memory_free( mem_op );
+ return NULL;
+ }
+
+ /* use encoded oid as hash string */
+ hash = MakeHash ( oid.octs, oid.octetLen);
+ comp_nibble_memory_free( mem_op );
+ if (CheckForAndReturnValue (anyOidHashTblG, hash, &anyInfo))
+ return (OidDecoderMapping*) anyInfo;
+ else
+ return (OidDecoderMapping*) NULL;
+
+}
+
+/*
+ * Look up Oid-decoder mapping table by description
+ */
+OidDecoderMapping*
+RetrieveOidDecoderMappingbyDesc( char* desc, int desc_len ) {
+ Hash hash;
+ void *anyInfo;
+ AsnOid oid;
+ AttributeType* ad_type;
+ struct berval bv;
+ void* mem_op;
+
+ bv.bv_val = desc;
+ bv.bv_len = desc_len;
+ ad_type = at_bvfind( &bv );
+
+ oid.octs = ad_type->sat_atype.at_oid;
+ oid.octetLen = strlen ( oid.octs );
+
+ if ( !ad_type )
+ return (OidDecoderMapping*) NULL;
+
+ mem_op = comp_nibble_memory_allocator ( 128, 16 );
+
+ oid.octs = EncodeComponentOid ( mem_op, oid.octs , (int*)&oid.octetLen );
+ if( oid.octetLen <= 0 ) {
+ comp_nibble_memory_free( mem_op );
+ return (OidDecoderMapping*) NULL;
+ }
+
+ /* use encoded oid as hash string */
+ hash = MakeHash ( oid.octs, oid.octetLen);
+ comp_nibble_memory_free( mem_op );
+ if (CheckForAndReturnValue (anyOidHashTblG, hash, &anyInfo))
+ return (OidDecoderMapping*) anyInfo;
+ else
+ return (OidDecoderMapping*) NULL;
+
+}
+void
+InstallAnyByComponentOid (int anyId, AsnOid *oid, unsigned int size,
+ EncodeFcn encode, gser_decoder_func* G_decode,
+ ber_tag_decoder_func* B_decode, ExtractFcn extract,
+ MatchFcn match, FreeFcn free, PrintFcn print)
+{
+ ComponentAnyInfo *a;
+ Hash h;
+
+ a = (ComponentAnyInfo*) malloc (sizeof (ComponentAnyInfo));
+ a->anyId = anyId;
+ if ( oid ) {
+ a->oid.octs = malloc( oid->octetLen );
+ memcpy ( a->oid.octs, oid->octs, oid->octetLen );
+ a->oid.octetLen = oid->octetLen;
+ }
+ a->size = size;
+ a->Encode = encode;
+ a->GSER_Decode = G_decode;
+ a->BER_Decode = B_decode;
+ a->Match = match;
+ a->Extract = extract;
+ a->Free = free;
+ a->Print = print;
+
+ h = MakeHash (oid->octs, oid->octetLen);
+
+ if (anyOidHashTblG == NULL)
+ anyOidHashTblG = InitHash();
+
+ if(anyOidHashTblG != NULL)
+ Insert(anyOidHashTblG, a, h);
+}
+
+int
+BDecComponentTop (
+ber_decoder_func *decoder _AND_
+void* mem_op _AND_
+GenBuf *b _AND_
+AsnTag tag _AND_
+AsnLen elmtLen _AND_
+void **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode) {
+ tag = BDecTag ( b, bytesDecoded );
+ elmtLen = BDecLen ( b, bytesDecoded );
+ if ( elmtLen <= 0 ) return (-1);
+ if ( tag != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE) ) {
+ return (-1);
+ }
+
+ return (*decoder)( mem_op, b, tag, elmtLen, (ComponentSyntaxInfo*)v,(int*)bytesDecoded, mode );
+}
+
+/*
+ * ASN.1 specification of a distinguished name
+ * DistinguishedName ::= RDNSequence
+ * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+ * RelativeDistinguishedName ::= SET SIZE(1..MAX) OF AttributeTypeandValue
+ * AttributeTypeandValue ::= SEQUENCE {
+ * type AttributeType
+ * value AttributeValue
+ * }
+ * When dnMatch/rdnMatch is used in a component assertion value
+ * the component in DistinguishedName/RelativeDistinguishedName
+ * need to be converted to the LDAP encodings in RFC2253
+ * in order to be matched against the assertion value
+ * If allComponentMatch is used, the assertion value may be
+ * decoded into the Internal Representation(Component Tree)
+ * by the corresponding GSER or BER decoder
+ * Following routine converts a component tree(DistinguishedName) into
+ * LDAP encodings in RFC2253
+ * Example)
+ * IR : ComponentRDNSequence
+ * GSER : { { type cn, value sang },{ type o, value ibm}, {type c, value us} }
+ * LDAP Encodings : cn=sang,o=ibm,c=us
+ */
+
+increment_bv_mem_by_size ( struct berval* in, int size ) {
+ int new_size = in->bv_len + size;
+ in->bv_val = realloc( in->bv_val, new_size );
+ in->bv_len = new_size;
+}
+
+int
+ConvertBER2Desc( char* in, int size, struct berval* out, int* pos ) {
+ int desc_size;
+ char* desc_ptr;
+ unsigned int firstArcNum;
+ unsigned int arcNum;
+ int i, rc, start_pos = *pos;
+ char buf[MAX_OID_LEN];
+ AttributeType *at;
+ struct berval bv_name;
+
+ /*convert BER oid to desc*/
+ for ( i = 0, arcNum = 0; (i < size) && (in[i] & 0x80 ); i++ )
+ arcNum = (arcNum << 7) + (in[i] & 0x7f);
+ arcNum = (arcNum << 7) + (in[i] & 0x7f);
+ i++;
+ firstArcNum = (unsigned short)(arcNum/40);
+ if ( firstArcNum > 2 )
+ firstArcNum = 2;
+
+ arcNum = arcNum - (firstArcNum * 40 );
+
+ rc = intToAscii ( arcNum, buf );
+
+ /*check if the buffer can store the first/second arc and two dots*/
+ if ( out->bv_len < *pos + 2 + 1 + rc )
+ increment_bv_mem_by_size ( out, INCREMENT_SIZE );
+
+ if ( firstArcNum == 1)
+ out->bv_val[*pos] = '1';
+ else
+ out->bv_val[*pos] = '2';
+ (*pos)++;
+ out->bv_val[*pos] = '.';
+ (*pos)++;
+
+ memcpy( out->bv_val + *pos, buf, rc );
+ *pos += rc;
+ out->bv_val[*pos] = '.';
+ (*pos)++;
+
+ for ( ; i < size ; ) {
+ for ( arcNum=0; (i < size) && (in[i] & 0x80) ; i++ )
+ arcNum = (arcNum << 7) + (in[i] & 0x7f);
+ arcNum = (arcNum << 7) + (in[i] & 0x7f);
+ i++;
+
+ rc = intToAscii ( arcNum, buf );
+
+ if ( out->bv_len < *pos + rc + 1 )
+ increment_bv_mem_by_size ( out, INCREMENT_SIZE );
+
+ memcpy( out->bv_val + *pos, buf, rc );
+ *pos += rc;
+ out->bv_val[*pos] = '.';
+ (*pos)++;
+ }
+ (*pos)--;/*remove the last '.'*/
+
+ /*
+ * lookup OID database to locate desc
+ * then overwrite OID with desc in *out
+ * If failed to look up desc, OID form is used
+ */
+ bv_name.bv_val = out->bv_val + start_pos;
+ bv_name.bv_len = *pos - start_pos;
+ at = at_bvfind( &bv_name );
+ if ( !at )
+ return LDAP_SUCCESS;
+ desc_size = at->sat_cname.bv_len;
+ memcpy( out->bv_val + start_pos, at->sat_cname.bv_val, desc_size );
+ *pos = start_pos + desc_size;
+ return LDAP_SUCCESS;
+}
+
+int
+ConvertComponentAttributeTypeAndValue2RFC2253 ( irAttributeTypeAndValue* in, struct berval* out, int *pos ) {
+ int rc;
+ int value_size = ((ComponentUTF8String*)in->value.value)->value.octetLen;
+ char* value_ptr = ((ComponentUTF8String*)in->value.value)->value.octs;
+
+ rc = ConvertBER2Desc( in->type.value.octs, in->type.value.octetLen, out, pos );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ if ( out->bv_len < *pos + 1/*for '='*/ )
+ increment_bv_mem_by_size ( out, INCREMENT_SIZE );
+ /*Between type and value, put '='*/
+ out->bv_val[*pos] = '=';
+ (*pos)++;
+
+ /*Assume it is string*/
+ if ( out->bv_len < *pos + value_size )
+ increment_bv_mem_by_size ( out, INCREMENT_SIZE );
+ memcpy( out->bv_val + *pos, value_ptr, value_size );
+ out->bv_len += value_size;
+ *pos += value_size;
+
+ return LDAP_SUCCESS;
+}
+
+int
+ConvertRelativeDistinguishedName2RFC2253 ( irRelativeDistinguishedName* in, struct berval *out , int* pos) {
+ irAttributeTypeAndValue* attr_typeNvalue;
+ int rc;
+
+
+ FOR_EACH_LIST_ELMT( attr_typeNvalue, &in->comp_list)
+ {
+ rc = ConvertComponentAttributeTypeAndValue2RFC2253( attr_typeNvalue, out, pos );
+ if ( rc != LDAP_SUCCESS ) return LDAP_INVALID_SYNTAX;
+
+ if ( out->bv_len < *pos + 1/*for '+'*/ )
+ increment_bv_mem_by_size ( out, INCREMENT_SIZE );
+ /*between multivalued RDNs, put comma*/
+ out->bv_val[(*pos)++] = '+';
+ }
+ (*pos)--;/*remove the last '+'*/
+ return LDAP_SUCCESS;
+}
+
+int
+ConvertRDN2RFC2253 ( irRelativeDistinguishedName* in, struct berval *out ) {
+ int rc, pos = 0;
+ out->bv_val = (char*)malloc( INITIAL_DN_SIZE );
+ out->bv_len = INITIAL_DN_SIZE;
+
+ rc = ConvertRelativeDistinguishedName2RFC2253 ( in, out , &pos);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ out->bv_val[pos] = '\0';
+ out->bv_len = pos;
+ return LDAP_SUCCESS;
+}
+
+int
+ConvertRDNSequence2RFC2253( irRDNSequence *in, struct berval* out ) {
+ irRelativeDistinguishedName* rdn_seq;
+ AsnList* seq = &in->comp_list;
+ int pos = 0, rc ;
+
+ out->bv_val = (char*)malloc( INITIAL_DN_SIZE );
+ out->bv_len = INITIAL_DN_SIZE;
+
+ FOR_EACH_LIST_ELMT( rdn_seq, seq )
+ {
+ rc = ConvertRelativeDistinguishedName2RFC2253( rdn_seq, out, &pos );
+ if ( rc != LDAP_SUCCESS ) return LDAP_INVALID_SYNTAX;
+
+ if ( out->bv_len < pos + 1/*for ','*/ )
+ increment_bv_mem_by_size ( out, INCREMENT_SIZE );
+ /*Between RDN, put comma*/
+ out->bv_val[pos++] = ',';
+ }
+ pos--;/*remove the last '+'*/
+ out->bv_val[pos] = '\0';
+ out->bv_len =pos;
+ return LDAP_SUCCESS;
+}
+
+#endif
diff --git a/contrib/slapd-modules/comp_match/componentlib.h b/contrib/slapd-modules/comp_match/componentlib.h
new file mode 100644
index 0000000..1ebd11e
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/componentlib.h
@@ -0,0 +1,593 @@
+/* Copyright 2004 IBM Corporation
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ */
+/* ACKNOWLEDGEMENTS
+ * This work originally developed by Sang Seok Lim
+ * 2004/06/18 03:20:00 slim@OpenLDAP.org
+ */
+
+#ifndef _H_COMPONENT_MODULE
+#define _H_COMPONENT_MODULE
+
+#include "portable.h"
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ldap_pvt.h>
+#include "lutil.h"
+#include <ldap.h>
+#include <slap.h>
+#include <component.h>
+
+#include <asn-incl.h>
+#include "asn.h"
+#include <asn-gser.h>
+#include <string.h>
+
+#define MAX_IDENTIFIER_LEN 32
+#define COMPONENTNOT_NULL(ptr) ((ptr) != NULL)
+
+typedef struct slap_component_type {
+ /*
+ * Don't change the order of following fields
+ * They are identical the first 9 fields of
+ * AttributeType
+ */
+ LDAPAttributeType ct_atype;
+ struct berval ct_cname;
+ struct slap_attribute_type *ct_sup;
+ struct slap_attribute_type **ct_subtypes;
+ MatchingRule *ct_equality;
+ MatchingRule *ct_approx;
+ MatchingRule *ct_ordering;
+ MatchingRule *ct_substr;
+ Syntax *ct_syntax;
+} ComponentType;
+
+
+/*
+ * BIT STRING
+ */
+typedef struct ComponentBits {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ AsnBits value;
+} ComponentBits;
+
+#define GASNBITS_PRESENT(abits) ((abits)->value.bits != NULL)
+#define COMPONENTBITS_PRESENT(abits) ((abits)->value.bits != NULL)
+int GEncComponentBits (GenBuf *b, ComponentBits* bits);
+int GDecComponentBits (void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentBits (void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+int MatchingComponentBits (char* oid, ComponentSyntaxInfo *bits1 , ComponentSyntaxInfo* bits2);
+#define ExtractingComponentBits( mem_op, cr,data ) NULL
+
+/*
+ * BMP String
+ */
+typedef struct ComponentBMPString {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ BMPString value;
+} ComponentBMPString;
+
+int GEncComponentBMPString (GenBuf *b, ComponentBMPString* bmp);
+int GDecComponentBMPString (void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentBMPString (void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+#define MatchingComponentBMPString MatchingComponentOcts
+#define ExtractingComponentBMPString( mem_op, cr, data ) NULL
+#define FreeComponentBMPString FreeComponentOcts
+
+/*
+ * BOOLEAN
+ */
+typedef struct ComponentBool {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ AsnBool value;
+} ComponentBool;
+
+int GEncComponentBool (GenBuf *b, ComponentBool * bool );
+int GDecComponentBool ( void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentBool ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+int MatchingComponentBool (char* oid, ComponentSyntaxInfo *a, ComponentSyntaxInfo *b);
+#define ExtractingComponentBool( mem_op, cr, data ) NULL
+#define FreeComponentBool(v) NULL
+
+/*
+ * ENUMERATED
+ */
+typedef struct ComponentEnum {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ AsnEnum value;
+ struct berval value_identifier;/*Why this value is defined here?*/
+} ComponentEnum;
+
+int GEncComponentEnum (GenBuf *b, ComponentEnum* comp_enum);
+int GDecComponentEnum ( void* mem_op, GenBuf *a, void *result, AsnLen *bytesDecoded,int mode);
+int BDecComponentEnum ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+int MatchingComponentEnum (char *oid, ComponentSyntaxInfo *a, ComponentSyntaxInfo * b);
+#define ExtractingComponentEnum( mem_op, cr, data ) NULL
+#define FreeComponentEnum FreeComponentInt
+
+/*
+ * IA5 String
+ */
+typedef struct ComponentIA5String {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ IA5String value;
+} ComponentIA5String;
+
+#define GEncComponentIA5String GEncComponentUTF8String
+#define GDecComponentIA5String GDecComponentUTF8String
+int
+BDecComponentIA5StringTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode );
+int BDecComponentIA5String ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+#define MatchingComponentIA5String MatchingComponentOcts
+#define ExtractingComponentIA5String(mem_op, cr,data) NULL
+#define FreeComponentIA5String FreeComponentOcts
+
+
+/*
+ * INTEGER
+ */
+typedef struct ComponentInt {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ int value;
+} ComponentInt;
+
+#define GNOT_NULL(ptr) ((ptr) != NULL)
+int GEncComponentInt (GenBuf *b, ComponentInt *comp_int);
+int GDecComponentInt ( void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode );
+int BDecComponentInt ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+int MatchingComponentInt (char* oid, ComponentSyntaxInfo *a, ComponentSyntaxInfo *b);
+#define ExtractingComponentInt(mem_op, cr,data) NULL
+#define FreeComponentInt(v) NULL
+
+/*
+ * LIST Data Structure for C_LIST
+ */
+typedef struct ComponentList {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ AsnList comp_list;
+} ComponentList;
+
+/*
+ * NULL
+ */
+typedef struct ComponentNull {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ AsnNull value;
+} ComponentNull;
+
+int GEncComponentNull (GenBuf *b, ComponentNull* comp_null);
+int GDecComponentNull ( void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentNull ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentNullTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode );
+int MatchingComponentNull (char* oid, ComponentSyntaxInfo *a, ComponentSyntaxInfo *b);
+#define ExtractingComponentNull(mem_op, cr, data) NULL
+#define FreeComponentNull NULL
+
+/*
+ * Numeric String
+ */
+typedef struct ComponentNumericString {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ NumericString value;
+} ComponentNumericString;
+
+#define GEncComponentNumericString GEncComponentUTF8String
+#define GDecComponentNumericString GDecComponentUTF8String
+int BDecComponentNumericString ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+#define MatchingComponentNumericString MatchingComponentOcts
+#define ExtractingComponentNumericString(mem_op, cr,data) NULL
+#define FreeComponentNumericString FreeComponentOcts
+
+/*
+ * OCTETS STRING
+ */
+typedef struct ComponentOcts {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ AsnOcts value;
+} ComponentOcts;
+
+#define GASNOCTS_PRESENT(aocts) ((aocts)->value.octs != NULL)
+int GEncComponentOcts (GenBuf *b, ComponentOcts *octs);
+int GDecComponentOcts (void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentOctsTag ( void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode );
+int BDecComponentOcts (void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+int MatchingComponentOcts (char* oid, ComponentSyntaxInfo *a, ComponentSyntaxInfo *b);
+#define ExtractingComponentOcts(mem_op,cr,data) NULL
+void FreeComponentOcts( ComponentOcts* octs );
+
+/*
+ * OID (Object Identifier)
+ */
+typedef struct ComponentOid {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ AsnOid value;
+} ComponentOid;
+
+#define GASNOID_PRESENT(aoid) ASNOCTS_PRESENT(aoid)
+int GEncComponentOid (GenBuf *b, ComponentOid *oid);
+int GDecComponentOid (void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentOid (void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+int MatchingComponentOid (char* oid, ComponentSyntaxInfo *a, ComponentSyntaxInfo *b);
+#define ExtractingComponentOid(mem_op, cr, data) NULL
+#define FreeComponentOid FreeComponentOcts
+
+/*
+ * Printable String
+ */
+typedef struct ComponentPrintableString{
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ PrintableString value;
+} ComponentPrintableString;
+#define GEncComponentPrintableString GEncComponentUTF8String
+#define GDecComponentPrintableString GDecComponentUTF8String
+int BDecComponentPrintableString (void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentPrintableStringTag (void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode );
+#define MatchingComponentPrintableString MatchingComponentOcts
+#define ExtractingComponentPrintableString(mem_op, cr, data) NULL
+#define FreeComponentPrintableString FreeComponentOcts
+
+/*
+ * REAL
+ */
+typedef struct ComponentReal{
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ AsnReal value;
+} ComponentReal;
+
+int GEncComponentReal (GenBuf *b, ComponentReal* comp_real);
+int GDecComponentReal (void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentReal (void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+int MatchingComponentReal (char* oid, ComponentSyntaxInfo *a, ComponentSyntaxInfo *b);
+#define ExtractingComponentReal( mem_op, cr, data ) NULL
+#define FreeComponentReal(v) NULL
+
+/*
+ * Relative OID
+ */
+
+typedef struct ComponentRelativeOid {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ AsnRelativeOid value;
+} ComponentRelativeOid;
+
+int GEncComponentRelativeOid (GenBuf *b, ComponentRelativeOid *r_oid);
+int GDecComponentRelativeOid ( void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentRelativeOid ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+int MatchingComponentRelativeOid (char* oid, ComponentSyntaxInfo *a, ComponentSyntaxInfo *b);
+#define ExtractingComponentRelativeOid( mem_op, cr, data ) NULL
+#define FreeComponentRelativeOid FreeComponentOid
+
+/*
+ * Teletex String
+ */
+typedef struct ComponentTeletexString {
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ TeletexString value;
+} ComponentTeletexString;
+
+int GEncComponentTeletexString (GenBuf *b, ComponentTeletexString * tel_str);
+int GDecComponentTeletexString ( void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode );
+int BDecComponentTeletexStringTag (void* mem_op, GenBuf *b, void *v, AsnLen *bytesDecoded, int mode );
+int BDecComponentTeletexString( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *v, AsnLen *bytesDecoded, int mode );
+#define MatchingComponentTeletexString MatchingComponentOcts
+#define ExtractingComponentTeletexString(mem_op,cr,data)
+#define FreeComponentTeletexString FreeComponentOcts
+
+
+/*
+ * Universal String
+ */
+typedef struct ComponentUniversalString{
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ UniversalString value;
+} ComponentUniversalString;
+
+int GEncComponentUniversalString (GenBuf *b, ComponentUniversalString* uni_str);
+int GDecComponentUniversalString ( void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentUniversalString ( void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+#define MatchingComponentUniversalString MatchingComponentOcts
+#define ExtractingComponentUniversalString(mem_op,cr,data)
+#define FreeComponentUniversalString FreeComponentOcts
+
+/*
+ * UTF8 String
+ */
+typedef struct ComponentUTF8String{
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ UTF8String value;
+} ComponentUTF8String;
+
+int GEncComponentUTF8String (GenBuf *b, ComponentUTF8String * utf_str);
+int GDecComponentUTF8String (void* mem_op, GenBuf *b, void *result, AsnLen *bytesDecoded, int mode);
+int BDecComponentUTF8String (void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+#define MatchingComponentUTF8String MatchingComponentOcts
+#define ExtractingComponentUTF8String(mem_op,cr,data)
+#define FreeComponentUTF8String FreeComponentOcts
+
+/*
+ * Visible String
+ */
+typedef struct ComponentVisibleString{
+ void* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ VisibleString value;
+} ComponentVisibleString;
+
+#define GEncComponentVisibleString GEncComponentUTF8String
+#define GDecComponentVisibleString GDecComponentUTF8String
+int BDecComponentVisibleString (void* mem_op, GenBuf *b, AsnTag tagId, AsnLen len, void *result, AsnLen *bytesDecoded, int mode);
+#define MatchingComponentVisibleString MatchingComponentOcts
+#define ExtractingComponentVisibleString(mem_op,cr,data)
+#define FreeComponentVisibleString FreeComponentOcts
+
+/*
+ * ANY and ANY DEFINED BY
+ */
+
+typedef int (*MatchFcn) (char*, void*, void*);
+typedef void* (*ExtractFcn) (void*, ComponentReference*, void * );
+
+typedef struct ComponentAnyInfo
+{
+ int anyId;
+ AsnOid oid;
+ ComponentInt intId;
+ unsigned int size;
+ EncodeFcn Encode;
+ gser_decoder_func* GSER_Decode;
+ ber_tag_decoder_func* BER_Decode;
+ ExtractFcn Extract;
+ MatchFcn Match;
+ FreeFcn Free;
+ PrintFcn Print;
+} ComponentAnyInfo;
+
+typedef struct ComponentAnyInfo OidDecoderMapping ;
+
+typedef struct ComponentAny{
+ void* syntax;
+ ComponentDesc *comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentAnyInfo *cai;
+ void *value;
+} ComponentAny;
+
+typedef ComponentAny ComponentAnyDefinedBy;
+
+#define BDecComponentAnyDefinedBy BDecComponentAny
+#define GDecComponentAnyDefinedBy GDecComponentAny
+#define MatchingComponentAnyDefinedBy MatchingComponentAny
+#define FreeComponentAnyDefinedBy FreeComponentAny
+
+int GEncComponentAny (GenBuf *b, ComponentAny *comp_any);
+int BDecComponentAny ( void* mem_op, GenBuf *b, ComponentAny *result, AsnLen *bytesDecoded, int mode);
+int GDecComponentAny ( void* mem_op, GenBuf *b, ComponentAny *result, AsnLen *bytesDecoded, int mode);
+int MatchingComponentAny (char* oid, ComponentAny *a, ComponentAny *b);
+void FreeComponentAny ( ComponentAny*);
+
+void InstallAnyByComponentInt (int anyId, ComponentInt intId, unsigned int size, EncodeFcn encode, gser_decoder_func* G_decode, ber_tag_decoder_func B_decode, ExtractFcn extract, MatchFcn match, FreeFcn free, PrintFcn print);
+
+void InstallAnyByComponentOid (int anyId, AsnOid *oid, unsigned int size, EncodeFcn encode, gser_decoder_func* G_decode, ber_tag_decoder_func* B_decode, ExtractFcn extract, MatchFcn match, FreeFcn free, PrintFcn print);
+
+int CheckSelectTypeCorrect ( void* mem_op, ComponentAnyInfo *v, struct berval* select );
+
+OidDecoderMapping* RetrieveOidDecoderMappingbyBV( struct berval* in );
+OidDecoderMapping* RetrieveOidDecoderMappingbyOid( char* ch_oid, int oid_len );
+OidDecoderMapping* RetrieveOidDecoderMappingbyDesc( char* desc, int desc_len );
+/*
+ * UTCTime
+ */
+typedef ComponentVisibleString ComponentUTCTime;
+#define GEncComponentUTCTime GEncComponentUTF8String
+#define GDecComponentUTCTime GDecComponentVisibleString
+#define BDecComponentUTCTime BDecComponentOcts
+#define MatchingComponentUTCTime MatchingComponentOcts
+#define ExtractingComponentUTCTime(mem_op,cr,data) NULL
+#define FreeComponentUTCTime FreeComponentOcts
+
+/*
+ * GeneralizedTime
+ */
+typedef ComponentVisibleString ComponentGeneralizedTime;
+int GEncComponentGeneralizedTime (GenBuf *b, ComponentGeneralizedTime *gen_time);
+#define GDecComponentGeneralizedTime GDecComponentVisibleString
+#define BDecComponentGeneralizedTime BDecComponentOcts
+#define MatchingComponentGeneralizedTime MatchingComponentOcts
+#define ExtractingComponentGeneralizedTime(mem_op,cr,data) NULL
+#define FreeComponentGeneralizedTime FreeComponentOcts
+
+typedef int converter_func LDAP_P ((
+ struct berval* in ));
+
+typedef struct asntype_to_syntax {
+ AsnTypeId ats_typeId;
+ /* Syntax Descriptor */
+ char *ats_syn_name;
+ /* Syntax OID */
+ char *ats_syn_oid;
+ Syntax *ats_syn;
+} AsnTypetoSyntax;
+
+typedef struct asntype_to_comp_matchingrule {
+ AsnTypeId atc_typeId;
+ char* atc_equality;
+ char* atc_approx;
+ char* atc_ordering;
+ char* atc_substr;
+} AsnTypetoCompMatchingRule;
+
+typedef struct asntype_to_comp_desc {
+ AsnTypeId atcd_typeId;
+ ComponentDesc atcd_cd;
+} AsnTypetoCompDesc;
+
+typedef struct asntype_to_comp_type {
+ AsnTypeId ac_asn_id;
+ ComponentType ac_comp_type;
+} AsnTypetoCompType;
+
+/* refined matching purpose */
+typedef struct asntype_to_matchingrule {
+ AsnTypeId atmr_typeId;
+ char* atmr_mr_name;
+ /*Implicitly corresponding LDAP syntax OID*/
+ char* atmr_syn_oid;
+ MatchingRule *atmr_mr;
+} AsnTypetoMatchingRule;
+
+typedef struct asntype_to_matchingrule_table {
+ char* atmr_oid;
+ struct asntype_to_matchingrule atmr_table[ASNTYPE_END];
+ struct asntype_to_matchingrule_table* atmr_table_next;
+} AsnTypetoMatchingRuleTable;
+
+#define MAX_OID_LEN 256
+#define MAX_OD_ENTRY 8
+
+/*
+ * Object Identifier and corresponding Syntax Decoder Table
+ */
+typedef struct OID_Decoder_entry {
+ char oe_oid[MAX_OID_LEN];
+ gser_decoder_func* oe_gser_decoder;
+ ber_decoder_func* oe_ber_decoder;
+ converter_func* oe_converter;
+ struct OID_Decoder_entry* oe_next;
+ struct OID_Decoder_entry* oe_prev;
+} OD_entry;
+
+void
+m_convert_asn_to_ldap ( ComponentSyntaxInfo* csi, struct berval* bv);
+int
+m_convert_assert_to_comp ( gser_decoder_func* decoder, struct berval* bv,
+ ComponentSyntaxInfo** csi, int len, int mode );
+void*
+m_convert_attr_to_comp ( Attribute* a, struct berval* bv );
+
+/*
+ * Decoder Modes
+ * Different operation is required to handle Decoding(2), Extracted Component
+ * decoding(0), ANY DEFINED TYPe(2)
+ * b0 : Component Alloc(yes)
+ * Constructed type : Component Alloc (Yes)
+ * Primitive type : Component Alloc (Yes)
+ * set to mode 2 in inner decoders
+ * b1 : Component Alloc (No)
+ * Constructed type : Component Alloc (No)
+ * Primitive type : Component Alloc (No)
+ * set to mode 2 in inner decoders
+ * b2 : Default Mode
+ * Constructed type : Component Alloc (Yes)
+ * Primitive type : Component Alloc (No)
+ * in addition to above modes, the 4th bit has special meaning,
+ * b4 : if the 4th bit is clear, DecxxxContent is called
+ * b4 : if the 4th bit is set, Decxxx is called, then it is cleared.
+ */
+#define DEC_ALLOC_MODE_0 0x01
+#define DEC_ALLOC_MODE_1 0x02
+#define DEC_ALLOC_MODE_2 0x04
+#define CALL_TAG_DECODER 0x08
+#define CALL_CONTENT_DECODER ~0x08
+
+#define OID_ALL_COMP_MATCH "1.2.36.79672281.1.13.6"
+#define OID_COMP_FILTER_MATCH "1.2.36.79672281.1.13.2"
+#define MAX_LDAP_STR_LEN 128
+
+MatchingRule*
+retrieve_matching_rule( char* mr_oid, AsnTypeId type );
+
+#define INITIAL_DN_SIZE 128
+#define INITIAL_ATTR_SIZE 256
+#define INCREMENT_SIZE 32
+/*
+ * The following are for conversion from ASN.1 RDN and DN to
+ * LDAP encodings
+ */
+#define MAX_ALIASING_ENTRY 128
+int increment_bv_mem ( struct berval* in );
+int intToAscii ( int value, char* buf );
+typedef ComponentList irRDNSequence;
+typedef ComponentList irRelativeDistinguishedName;
+typedef ComponentOid irAttributeType;
+typedef struct comp_irAttributeTypeAndValue /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ irAttributeType type; /* AttributeType */
+ ComponentAnyDefinedBy value; /* ANY DEFINED BY type */
+} irAttributeTypeAndValue;
+#define RDN_MATCH_OID "1.2.36.79672281.1.13.3"
+#define DN_MATCH_OID "2.5.13.1"
+
+extern AsnTypetoSyntax asn_to_syntax_mapping_tbl[];
+extern AsnTypetoCompMatchingRule asntype_to_compMR_mapping_tbl[];
+extern AsnTypetoCompType asntype_to_compType_mapping_tbl[];
+extern AsnTypetoCompDesc asntype_to_compdesc_mapping_tbl[];
+
+int ConvertRDN2RFC2253 ( irRelativeDistinguishedName* in, struct berval *out );
+int ConvertRDNSequence2RFC2253( irRDNSequence *in, struct berval* out );
+
+void* comp_nibble_memory_allocator ( int init_mem, int inc_mem );
+
+ComponentDesc* get_ComponentDesc( int id );
+#endif
diff --git a/contrib/slapd-modules/comp_match/crl.c b/contrib/slapd-modules/comp_match/crl.c
new file mode 100644
index 0000000..abd82cd
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/crl.c
@@ -0,0 +1,1294 @@
+/*
+ * crl.c
+ * "CertificateRevokationList" ASN.1 module encode/decode/extracting/matching/free C src.
+ * This file was generated by modified eSMACC compiler Fri Jan 21 11:25:24 2005
+ * The generated files are supposed to be compiled as a module for OpenLDAP Software
+ */
+
+#include "crl.h"
+
+BDecComponentCertificateListTop( void* mem_op, GenBuf* b, void *v, AsnLen* bytesDecoded,int mode) {
+ AsnTag tag;
+ AsnLen elmtLen;
+
+ tag = BDecTag ( b, bytesDecoded );
+ elmtLen = BDecLen ( b, bytesDecoded );
+ if ( elmtLen <= 0 ) return (-1);
+ if ( tag != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE) ) {
+ return (-1);
+ }
+
+ return BDecComponentCertificateList( mem_op, b, tag, elmtLen, ( ComponentCertificateList**)v, (AsnLen*)bytesDecoded, mode );
+}
+
+
+void init_module_CertificateRevokationList() {
+ InstallOidDecoderMapping( "2.5.4.39", NULL,
+ GDecComponentCertificateList,
+ BDecComponentCertificateListTop,
+ ExtractingComponentCertificateList,
+ MatchingComponentCertificateList);
+}
+
+int
+MatchingComponentTBSCertListSeqOfSeq ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentCertificateSerialNumber ( oid, (ComponentSyntaxInfo*)&((ComponentTBSCertListSeqOfSeq*)csi_attr)->userCertificate, (ComponentSyntaxInfo*)&((ComponentTBSCertListSeqOfSeq*)csi_assert)->userCertificate );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentTime ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertListSeqOfSeq*)csi_attr)->revocationDate, (ComponentSyntaxInfo*)((ComponentTBSCertListSeqOfSeq*)csi_assert)->revocationDate );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ if(COMPONENTNOT_NULL( ((ComponentTBSCertListSeqOfSeq*)csi_attr)->crlEntryExtensions ) ) {
+ rc = MatchingComponentExtensions ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertListSeqOfSeq*)csi_attr)->crlEntryExtensions, (ComponentSyntaxInfo*)((ComponentTBSCertListSeqOfSeq*)csi_assert)->crlEntryExtensions );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ }
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentTBSCertListSeqOfSeq */
+
+void*
+ExtractingComponentTBSCertListSeqOfSeq ( void* mem_op, ComponentReference* cr, ComponentTBSCertListSeqOfSeq *comp )
+{
+
+ if ( ( comp->userCertificate.identifier.bv_val && strncmp(comp->userCertificate.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->userCertificate.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->userCertificate;
+ else
+ return NULL;
+ }
+ if ( ( comp->revocationDate->identifier.bv_val && strncmp(comp->revocationDate->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->revocationDate->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->revocationDate;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTime ( mem_op, cr, comp->revocationDate );
+ }
+ }
+ if ( ( comp->crlEntryExtensions->identifier.bv_val && strncmp(comp->crlEntryExtensions->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->crlEntryExtensions->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->crlEntryExtensions;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentExtensions ( mem_op, cr, comp->crlEntryExtensions );
+ }
+ }
+ return NULL;
+} /* ExtractingComponentTBSCertListSeqOfSeq */
+
+int
+BDecComponentTBSCertListSeqOfSeq PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentTBSCertListSeqOfSeq **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ AsnLen totalElmtsLen2 = 0;
+ AsnLen elmtLen2;
+ AsnTag tagId2;
+ int old_mode = mode;
+ int rc;
+ ComponentTBSCertListSeqOfSeq *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, INTEGER_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentCertificateSerialNumber (mem_op, b, tagId1, elmtLen1, (&k->userCertificate), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->userCertificate)->identifier.bv_val = (&k->userCertificate)->id_buf;
+ (&k->userCertificate)->identifier.bv_len = strlen("userCertificate");
+ strcpy( (&k->userCertificate)->identifier.bv_val, "userCertificate");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, UTCTIME_TAG_CODE)) ||
+(tagId1 == MAKE_TAG_ID (UNIV, CONS, UTCTIME_TAG_CODE)) ||
+ (tagId1 ==MAKE_TAG_ID (UNIV, PRIM, GENERALIZEDTIME_TAG_CODE))||
+ (tagId1 == MAKE_TAG_ID (UNIV, CONS, GENERALIZEDTIME_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentTime (mem_op, b, tagId1, elmtLen1, (&k->revocationDate), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->revocationDate)->identifier.bv_val = (k->revocationDate)->id_buf;
+ (k->revocationDate)->identifier.bv_len = strlen("revocationDate");
+ strcpy( (k->revocationDate)->identifier.bv_val, "revocationDate");
+ if ((elmtLen0 != INDEFINITE_LEN) && (totalElmtsLen1 == elmtLen0))
+ seqDone = TRUE;
+ else
+ {
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((elmtLen0 == INDEFINITE_LEN) && (tagId1 == EOC_TAG_ID))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ seqDone = TRUE;
+ }
+ }
+ }
+ else
+ return -1;
+
+
+
+ if ((!seqDone) && ((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentExtensions (mem_op, b, tagId1, elmtLen1, (&k->crlEntryExtensions), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->crlEntryExtensions)->identifier.bv_val = (k->crlEntryExtensions)->id_buf;
+ (k->crlEntryExtensions)->identifier.bv_len = strlen("crlEntryExtensions");
+ strcpy( (k->crlEntryExtensions)->identifier.bv_val, "crlEntryExtensions");
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentTBSCertListSeqOfSeq*) CompAlloc( mem_op, sizeof(ComponentTBSCertListSeqOfSeq) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_ldap_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentTBSCertListSeqOfSeq ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentTBSCertListSeqOfSeq ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentTBSCertListSeqOfSeq;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentTBSCertListSeqOfSeq;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecTBSCertListSeqOfSeq*/
+
+int
+GDecComponentTBSCertListSeqOfSeq PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentTBSCertListSeqOfSeq **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentTBSCertListSeqOfSeq *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "userCertificate", strlen("userCertificate") ) == 0 ) {
+ rc = GDecComponentCertificateSerialNumber (mem_op, b, (&k->userCertificate), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->userCertificate)->identifier.bv_val = peek_head;
+ (&k->userCertificate)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "revocationDate", strlen("revocationDate") ) == 0 ) {
+ rc = GDecComponentTime (mem_op, b, (&k->revocationDate), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->revocationDate)->identifier.bv_val = peek_head;
+ ( k->revocationDate)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "crlEntryExtensions", strlen("crlEntryExtensions") ) == 0 ) {
+ rc = GDecComponentExtensions (mem_op, b, (&k->crlEntryExtensions), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->crlEntryExtensions)->identifier.bv_val = peek_head;
+ ( k->crlEntryExtensions)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentTBSCertListSeqOfSeq*) CompAlloc( mem_op, sizeof(ComponentTBSCertListSeqOfSeq) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_ldap_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentTBSCertListSeqOfSeq ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentTBSCertListSeqOfSeq ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentTBSCertListSeqOfSeq;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentTBSCertListSeqOfSeq;
+ return LDAP_SUCCESS;
+} /* GDecTBSCertListSeqOfSeq*/
+
+
+int
+MatchingComponentTBSCertListSeqOf ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+ void* component1, *component2;
+ AsnList *v1, *v2, t_list;
+
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ v1 = &((ComponentTBSCertListSeqOf*)csi_attr)->comp_list;
+ v2 = &((ComponentTBSCertListSeqOf*)csi_assert)->comp_list;
+ FOR_EACH_LIST_PAIR_ELMT(component1, component2, v1, v2)
+ {
+ if( MatchingComponentTBSCertListSeqOfSeq(oid, (ComponentSyntaxInfo*)component1, (ComponentSyntaxInfo*)component2) == LDAP_COMPARE_FALSE) {
+ return LDAP_COMPARE_FALSE;
+ }
+ } /* end of for */
+
+ AsnListFirst( v1 );
+ AsnListFirst( v2 );
+ if( (!component1 && component2) || (component1 && !component2))
+ return LDAP_COMPARE_FALSE;
+ else
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentTBSCertListSeqOfContent */
+
+void*
+ExtractingComponentTBSCertListSeqOf ( void* mem_op, ComponentReference* cr, ComponentTBSCertListSeqOf *comp )
+{
+ int count = 0;
+ int total;
+ AsnList *v = &comp->comp_list;
+ ComponentInt *k;
+ ComponentTBSCertListSeqOfSeq *component;
+
+
+ switch ( cr->cr_curr->ci_type ) {
+ case LDAP_COMPREF_FROM_BEGINNING :
+ count = cr->cr_curr->ci_val.ci_from_beginning;
+ FOR_EACH_LIST_ELMT( component , v ) {
+ if( --count == 0 ) {
+ if( cr->cr_curr->ci_next == NULL )
+ return component;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTBSCertListSeqOfSeq ( mem_op, cr, component );
+ }
+ }
+ }
+ break;
+ case LDAP_COMPREF_FROM_END :
+ total = AsnListCount ( v );
+ count = cr->cr_curr->ci_val.ci_from_end;
+ count = total + count +1;
+ FOR_EACH_LIST_ELMT ( component, v ) {
+ if( --count == 0 ) {
+ if( cr->cr_curr->ci_next == NULL )
+ return component;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTBSCertListSeqOfSeq ( mem_op, cr, component );
+ }
+ }
+ }
+ break;
+ case LDAP_COMPREF_ALL :
+ return comp;
+ case LDAP_COMPREF_COUNT :
+ k = (ComponentInt*)CompAlloc( mem_op, sizeof(ComponentInt));
+ k->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ k->comp_desc->cd_tag = (-1);
+ k->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentInt;
+ k->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentInt;
+ k->comp_desc->cd_extract_i = (extract_component_from_id_func*)NULL;
+ k->comp_desc->cd_type = ASN_BASIC;
+ k->comp_desc->cd_type_id = BASICTYPE_INTEGER;
+ k->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentInt;
+ k->value = AsnListCount(v);
+ return k;
+ default :
+ return NULL;
+ }
+} /* ExtractingComponentTBSCertListSeqOf */
+
+int
+BDecComponentTBSCertListSeqOf PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentTBSCertListSeqOf **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentTBSCertListSeqOf *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ AsnListInit(&k->comp_list,sizeof(ComponentTBSCertListSeqOfSeq));
+ for (totalElmtsLen1 = 0; (totalElmtsLen1 < elmtLen0) || (elmtLen0 == INDEFINITE_LEN);)
+ {
+ ComponentTBSCertListSeqOfSeq **tmpVar;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((tagId1 == EOC_TAG_ID) && (elmtLen0 == INDEFINITE_LEN))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ break; /* got EOC so can exit this SET OF/SEQ OF's for loop*/
+ }
+ if ((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE)))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ tmpVar = (ComponentTBSCertListSeqOfSeq**) CompAsnListAppend (mem_op,&k->comp_list);
+ rc = BDecComponentTBSCertListSeqOfSeq (mem_op, b, tagId1, elmtLen1, tmpVar, &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ } /* end of tag check if */
+ else /* wrong tag */
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+ } /* end of for */
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentTBSCertListSeqOf*) CompAlloc( mem_op, sizeof(ComponentTBSCertListSeqOf) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_ldap_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentTBSCertListSeqOf ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentTBSCertListSeqOf ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentTBSCertListSeqOf;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentTBSCertListSeqOf;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecTBSCertListSeqOfContent */
+
+int
+GDecComponentTBSCertListSeqOf PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentTBSCertListSeqOf **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentTBSCertListSeqOf *k,*t, c_temp;
+
+
+ int ElmtsLen1;
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ AsnListInit( &k->comp_list, sizeof( ComponentTBSCertListSeqOfSeq ) );
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b, &peek_head, GSER_PEEK)) ){
+ Asn1Error("Error during Reading { in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ for (ElmtsLen1 = 0; ElmtsLen1 >= INDEFINITE_LEN; ElmtsLen1++)
+ {
+ ComponentTBSCertListSeqOfSeq **tmpVar;
+ if( !(strLen = LocateNextGSERToken(mem_op,b, &peek_head, GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading{ in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head == '}') break;
+ if( !(*peek_head == '{' || *peek_head ==',') ) {
+ return LDAP_PROTOCOL_ERROR;
+ }
+ tmpVar = (ComponentTBSCertListSeqOfSeq**) CompAsnListAppend (mem_op, &k->comp_list);
+ if ( tmpVar == NULL ) {
+ Asn1Error("Error during Reading{ in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ rc = GDecComponentTBSCertListSeqOfSeq (mem_op, b, tmpVar, bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ } /* end of for */
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentTBSCertListSeqOf*) CompAlloc( mem_op, sizeof(ComponentTBSCertListSeqOf) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_ldap_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentTBSCertListSeqOf ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentTBSCertListSeqOf ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentTBSCertListSeqOf;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentTBSCertListSeqOf;
+ return LDAP_SUCCESS;
+} /* GDecTBSCertListSeqOfContent */
+
+int
+MatchingComponentTBSCertList ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ if(COMPONENTNOT_NULL( ((ComponentTBSCertList*)csi_attr)->version ) ) {
+ rc = MatchingComponentVersion ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_attr)->version, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_assert)->version );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ }
+ rc = MatchingComponentAlgorithmIdentifier ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_attr)->signature, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_assert)->signature );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentName ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_attr)->issuer, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_assert)->issuer );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentTime ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_attr)->thisUpdate, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_assert)->thisUpdate );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ if(COMPONENTNOT_NULL( ((ComponentTBSCertList*)csi_attr)->nextUpdate ) ) {
+ rc = MatchingComponentTime ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_attr)->nextUpdate, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_assert)->nextUpdate );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ }
+ rc = MatchingComponentTBSCertListSeqOf ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_attr)->revokedCertificates, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_assert)->revokedCertificates );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ if(COMPONENTNOT_NULL( ((ComponentTBSCertList*)csi_attr)->crlExtensions ) ) {
+ rc = MatchingComponentExtensions ( oid, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_attr)->crlExtensions, (ComponentSyntaxInfo*)((ComponentTBSCertList*)csi_assert)->crlExtensions );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ }
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentTBSCertList */
+
+void*
+ExtractingComponentTBSCertList ( void* mem_op, ComponentReference* cr, ComponentTBSCertList *comp )
+{
+
+ if ( ( comp->version->identifier.bv_val && strncmp(comp->version->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->version->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->version;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentVersion ( mem_op, cr, comp->version );
+ }
+ }
+ if ( ( comp->signature->identifier.bv_val && strncmp(comp->signature->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->signature->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->signature;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentAlgorithmIdentifier ( mem_op, cr, comp->signature );
+ }
+ }
+ if ( ( comp->issuer->identifier.bv_val && strncmp(comp->issuer->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->issuer->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->issuer;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentName ( mem_op, cr, comp->issuer );
+ }
+ }
+ if ( ( comp->thisUpdate->identifier.bv_val && strncmp(comp->thisUpdate->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->thisUpdate->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->thisUpdate;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTime ( mem_op, cr, comp->thisUpdate );
+ }
+ }
+ if ( ( comp->nextUpdate->identifier.bv_val && strncmp(comp->nextUpdate->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->nextUpdate->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->nextUpdate;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTime ( mem_op, cr, comp->nextUpdate );
+ }
+ }
+ if ( ( comp->revokedCertificates->identifier.bv_val && strncmp(comp->revokedCertificates->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->revokedCertificates->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->revokedCertificates;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTBSCertListSeqOf ( mem_op, cr, comp->revokedCertificates );
+ }
+ }
+ if ( ( comp->crlExtensions->identifier.bv_val && strncmp(comp->crlExtensions->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->crlExtensions->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->crlExtensions;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentExtensions ( mem_op, cr, comp->crlExtensions );
+ }
+ }
+ return NULL;
+} /* ExtractingComponentTBSCertList */
+
+int
+BDecComponentTBSCertList PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentTBSCertList **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ AsnLen totalElmtsLen2 = 0;
+ AsnLen elmtLen2;
+ AsnTag tagId2;
+ int old_mode = mode;
+ int rc;
+ ComponentTBSCertList *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, INTEGER_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentVersion (mem_op, b, tagId1, elmtLen1, (&k->version), &totalElmtsLen1, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->version)->identifier.bv_val = (k->version)->id_buf;
+ (k->version)->identifier.bv_len = strlen("version");
+ strcpy( (k->version)->identifier.bv_val, "version");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentAlgorithmIdentifier (mem_op, b, tagId1, elmtLen1, (&k->signature), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->signature)->identifier.bv_val = (k->signature)->id_buf;
+ (k->signature)->identifier.bv_len = strlen("signature");
+ strcpy( (k->signature)->identifier.bv_val, "signature");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentName (mem_op, b, tagId1, elmtLen1, (&k->issuer), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->issuer)->identifier.bv_val = (k->issuer)->id_buf;
+ (k->issuer)->identifier.bv_len = strlen("issuer");
+ strcpy( (k->issuer)->identifier.bv_val, "issuer");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, UTCTIME_TAG_CODE)) ||
+(tagId1 == MAKE_TAG_ID (UNIV, CONS, UTCTIME_TAG_CODE)) ||
+ (tagId1 ==MAKE_TAG_ID (UNIV, PRIM, GENERALIZEDTIME_TAG_CODE))||
+ (tagId1 == MAKE_TAG_ID (UNIV, CONS, GENERALIZEDTIME_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentTime (mem_op, b, tagId1, elmtLen1, (&k->thisUpdate), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->thisUpdate)->identifier.bv_val = (k->thisUpdate)->id_buf;
+ (k->thisUpdate)->identifier.bv_len = strlen("thisUpdate");
+ strcpy( (k->thisUpdate)->identifier.bv_val, "thisUpdate");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, UTCTIME_TAG_CODE)) ||
+(tagId1 == MAKE_TAG_ID (UNIV, CONS, UTCTIME_TAG_CODE)) ||
+ (tagId1 ==MAKE_TAG_ID (UNIV, PRIM, GENERALIZEDTIME_TAG_CODE))||
+ (tagId1 == MAKE_TAG_ID (UNIV, CONS, GENERALIZEDTIME_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentTime (mem_op, b, tagId1, elmtLen1, (&k->nextUpdate), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->nextUpdate)->identifier.bv_val = (k->nextUpdate)->id_buf;
+ (k->nextUpdate)->identifier.bv_len = strlen("nextUpdate");
+ strcpy( (k->nextUpdate)->identifier.bv_val, "nextUpdate");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentTBSCertListSeqOf (mem_op, b, tagId1, elmtLen1, (&k->revokedCertificates), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->revokedCertificates)->identifier.bv_val = (k->revokedCertificates)->id_buf;
+ (k->revokedCertificates)->identifier.bv_len = strlen("revokedCertificates");
+ strcpy( (k->revokedCertificates)->identifier.bv_val, "revokedCertificates");
+ if ((elmtLen0 != INDEFINITE_LEN) && (totalElmtsLen1 == elmtLen0))
+ seqDone = TRUE;
+ else
+ {
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if ((elmtLen0 == INDEFINITE_LEN) && (tagId1 == EOC_TAG_ID))
+ {
+ BDEC_2ND_EOC_OCTET (b, &totalElmtsLen1 )
+ seqDone = TRUE;
+ }
+ }
+ }
+ else
+ return -1;
+
+
+
+ if ((!seqDone) && ((tagId1 == MAKE_TAG_ID (CNTX, CONS, 0))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ tagId2 = BDecTag (b, &totalElmtsLen1 );
+
+ if (tagId2 != MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))
+ {
+ Asn1Error ("Unexpected Tag\n");
+ return -1;
+ }
+
+ elmtLen2 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentExtensions (mem_op, b, tagId2, elmtLen2, (&k->crlExtensions), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->crlExtensions)->identifier.bv_val = (k->crlExtensions)->id_buf;
+ (k->crlExtensions)->identifier.bv_len = strlen("crlExtensions");
+ strcpy( (k->crlExtensions)->identifier.bv_val, "crlExtensions");
+ if (elmtLen1 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentTBSCertList*) CompAlloc( mem_op, sizeof(ComponentTBSCertList) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_ldap_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentTBSCertList ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentTBSCertList ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentTBSCertList;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentTBSCertList;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecTBSCertList*/
+
+int
+GDecComponentTBSCertList PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentTBSCertList **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentTBSCertList *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "version", strlen("version") ) == 0 ) {
+ rc = GDecComponentVersion (mem_op, b, (&k->version), bytesDecoded, DEC_ALLOC_MODE_0 );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->version)->identifier.bv_val = peek_head;
+ ( k->version)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "signature", strlen("signature") ) == 0 ) {
+ rc = GDecComponentAlgorithmIdentifier (mem_op, b, (&k->signature), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->signature)->identifier.bv_val = peek_head;
+ ( k->signature)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "issuer", strlen("issuer") ) == 0 ) {
+ rc = GDecComponentName (mem_op, b, (&k->issuer), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->issuer)->identifier.bv_val = peek_head;
+ ( k->issuer)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "thisUpdate", strlen("thisUpdate") ) == 0 ) {
+ rc = GDecComponentTime (mem_op, b, (&k->thisUpdate), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->thisUpdate)->identifier.bv_val = peek_head;
+ ( k->thisUpdate)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "nextUpdate", strlen("nextUpdate") ) == 0 ) {
+ rc = GDecComponentTime (mem_op, b, (&k->nextUpdate), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->nextUpdate)->identifier.bv_val = peek_head;
+ ( k->nextUpdate)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "revokedCertificates", strlen("revokedCertificates") ) == 0 ) {
+ rc = GDecComponentTBSCertListSeqOf (mem_op, b, (&k->revokedCertificates), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->revokedCertificates)->identifier.bv_val = peek_head;
+ ( k->revokedCertificates)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "crlExtensions", strlen("crlExtensions") ) == 0 ) {
+ rc = GDecComponentExtensions (mem_op, b, (&k->crlExtensions), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->crlExtensions)->identifier.bv_val = peek_head;
+ ( k->crlExtensions)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentTBSCertList*) CompAlloc( mem_op, sizeof(ComponentTBSCertList) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_ldap_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentTBSCertList ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentTBSCertList ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentTBSCertList;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentTBSCertList;
+ return LDAP_SUCCESS;
+} /* GDecTBSCertList*/
+
+
+int
+MatchingComponentCertificateList ( char* oid, ComponentSyntaxInfo* csi_attr, ComponentSyntaxInfo* csi_assert ) {
+ int rc;
+ MatchingRule* mr;
+
+ if ( oid ) {
+ mr = retrieve_matching_rule( oid, csi_attr->csi_comp_desc->cd_type_id);
+ if ( mr ) return component_value_match( mr, csi_attr, csi_assert );
+ }
+
+ rc = 1;
+ rc = MatchingComponentTBSCertList ( oid, (ComponentSyntaxInfo*)((ComponentCertificateList*)csi_attr)->tbsCertList, (ComponentSyntaxInfo*)((ComponentCertificateList*)csi_assert)->tbsCertList );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentAlgorithmIdentifier ( oid, (ComponentSyntaxInfo*)((ComponentCertificateList*)csi_attr)->signatureAlgorithm, (ComponentSyntaxInfo*)((ComponentCertificateList*)csi_assert)->signatureAlgorithm );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ rc = MatchingComponentBits ( oid, (ComponentSyntaxInfo*)&((ComponentCertificateList*)csi_attr)->signature, (ComponentSyntaxInfo*)&((ComponentCertificateList*)csi_assert)->signature );
+ if ( rc != LDAP_COMPARE_TRUE )
+ return rc;
+ return LDAP_COMPARE_TRUE;
+} /* BMatchingComponentCertificateList */
+
+void*
+ExtractingComponentCertificateList ( void* mem_op, ComponentReference* cr, ComponentCertificateList *comp )
+{
+
+ if ( ( comp->tbsCertList->identifier.bv_val && strncmp(comp->tbsCertList->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->tbsCertList->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->tbsCertList;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentTBSCertList ( mem_op, cr, comp->tbsCertList );
+ }
+ }
+ if ( ( comp->signatureAlgorithm->identifier.bv_val && strncmp(comp->signatureAlgorithm->identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->signatureAlgorithm->id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return comp->signatureAlgorithm;
+ else {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return ExtractingComponentAlgorithmIdentifier ( mem_op, cr, comp->signatureAlgorithm );
+ }
+ }
+ if ( ( comp->signature.identifier.bv_val && strncmp(comp->signature.identifier.bv_val, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) || ( strncmp(comp->signature.id_buf, cr->cr_curr->ci_val.ci_identifier.bv_val,cr->cr_curr->ci_val.ci_identifier.bv_len) == 0 ) ) {
+ if ( cr->cr_curr->ci_next == NULL )
+ return &comp->signature;
+ else if ( cr->cr_curr->ci_next->ci_type == LDAP_COMPREF_CONTENT) {
+ cr->cr_curr = cr->cr_curr->ci_next;
+ return &comp->signature;
+ } else {
+ return NULL;
+ }
+ }
+ return NULL;
+} /* ExtractingComponentCertificateList */
+
+int
+BDecComponentCertificateList PARAMS ((b, tagId0, elmtLen0, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+AsnTag tagId0 _AND_
+AsnLen elmtLen0 _AND_
+ComponentCertificateList **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ int seqDone = FALSE;
+ AsnLen totalElmtsLen1 = 0;
+ AsnLen elmtLen1;
+ AsnTag tagId1;
+ int mandatoryElmtCount1 = 0;
+ int old_mode = mode;
+ int rc;
+ ComponentCertificateList *k, *t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ tagId1 = BDecTag (b, &totalElmtsLen1 );
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentTBSCertList (mem_op, b, tagId1, elmtLen1, (&k->tbsCertList), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->tbsCertList)->identifier.bv_val = (k->tbsCertList)->id_buf;
+ (k->tbsCertList)->identifier.bv_len = strlen("tbsCertList");
+ strcpy( (k->tbsCertList)->identifier.bv_val, "tbsCertList");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, CONS, SEQ_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentAlgorithmIdentifier (mem_op, b, tagId1, elmtLen1, (&k->signatureAlgorithm), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (k->signatureAlgorithm)->identifier.bv_val = (k->signatureAlgorithm)->id_buf;
+ (k->signatureAlgorithm)->identifier.bv_len = strlen("signatureAlgorithm");
+ strcpy( (k->signatureAlgorithm)->identifier.bv_val, "signatureAlgorithm");
+ tagId1 = BDecTag (b, &totalElmtsLen1);
+ }
+ else
+ return -1;
+
+
+
+ if (((tagId1 == MAKE_TAG_ID (UNIV, PRIM, BITSTRING_TAG_CODE)) ||
+(tagId1 == MAKE_TAG_ID (UNIV, CONS, BITSTRING_TAG_CODE))))
+ {
+ elmtLen1 = BDecLen (b, &totalElmtsLen1 );
+ rc = BDecComponentBits (mem_op, b, tagId1, elmtLen1, (&k->signature), &totalElmtsLen1, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->signature)->identifier.bv_val = (&k->signature)->id_buf;
+ (&k->signature)->identifier.bv_len = strlen("signature");
+ strcpy( (&k->signature)->identifier.bv_val, "signature");
+ seqDone = TRUE;
+ if (elmtLen0 == INDEFINITE_LEN)
+ BDecEoc (b, &totalElmtsLen1 );
+ else if (totalElmtsLen1 != elmtLen0)
+ return -1;
+
+ }
+ else
+ return -1;
+
+
+
+ if (!seqDone)
+ return -1;
+
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentCertificateList*) CompAlloc( mem_op, sizeof(ComponentCertificateList) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_ldap_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentCertificateList ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentCertificateList ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentCertificateList;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentCertificateList;
+ (*bytesDecoded) += totalElmtsLen1;
+ return LDAP_SUCCESS;
+} /* BDecCertificateList*/
+
+int
+GDecComponentCertificateList PARAMS (( mem_op,b, v, bytesDecoded, mode),
+void* mem_op _AND_
+GenBuf * b _AND_
+ComponentCertificateList **v _AND_
+AsnLen *bytesDecoded _AND_
+int mode)
+{
+ char* peek_head,*peek_head2;
+ int i, strLen,strLen2, rc, old_mode = mode;
+ ComponentCertificateList *k,*t, c_temp;
+
+
+ if ( !(mode & DEC_ALLOC_MODE_1) ) {
+ memset(&c_temp,0,sizeof(c_temp));
+ k = &c_temp;
+ } else
+ k = t = *v;
+ mode = DEC_ALLOC_MODE_2;
+ *bytesDecoded = 0;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '{'){
+ Asn1Error("Missing { in encoded data");
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( strncmp( peek_head, "tbsCertList", strlen("tbsCertList") ) == 0 ) {
+ rc = GDecComponentTBSCertList (mem_op, b, (&k->tbsCertList), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->tbsCertList)->identifier.bv_val = peek_head;
+ ( k->tbsCertList)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "signatureAlgorithm", strlen("signatureAlgorithm") ) == 0 ) {
+ rc = GDecComponentAlgorithmIdentifier (mem_op, b, (&k->signatureAlgorithm), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ ( k->signatureAlgorithm)->identifier.bv_val = peek_head;
+ ( k->signatureAlgorithm)->identifier.bv_len = strLen;
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading , ");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != ','){
+ Asn1Error("Missing , in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ){
+ Asn1Error("Error during Reading identifier");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if ( strncmp( peek_head, "signature", strlen("signature") ) == 0 ) {
+ rc = GDecComponentBits (mem_op, b, (&k->signature), bytesDecoded, mode);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (&k->signature)->identifier.bv_val = peek_head;
+ (&k->signature)->identifier.bv_len = strLen;
+ }
+ if( !(strLen = LocateNextGSERToken(mem_op,b,&peek_head,GSER_NO_COPY)) ) {
+ Asn1Error("Error during Reading } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if(*peek_head != '}'){
+ Asn1Error("Missing } in encoding");
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( !(old_mode & DEC_ALLOC_MODE_1) ) {
+ *v = t = (ComponentCertificateList*) CompAlloc( mem_op, sizeof(ComponentCertificateList) );
+ if ( !t ) return -1;
+ *t = *k;
+ }
+ t->syntax = (Syntax*)NULL;
+ t->comp_desc = CompAlloc( mem_op, sizeof( ComponentDesc ) );
+ if ( !t->comp_desc ) {
+ free ( t );
+ return -1;
+ }
+ t->comp_desc->cd_ldap_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_ber_encoder = (encoder_func*)NULL;
+ t->comp_desc->cd_gser_decoder = (gser_decoder_func*)GDecComponentCertificateList ;
+ t->comp_desc->cd_ber_decoder = (ber_decoder_func*)BDecComponentCertificateList ;
+ t->comp_desc->cd_free = (comp_free_func*)NULL;
+ t->comp_desc->cd_extract_i = (extract_component_from_id_func*)ExtractingComponentCertificateList;
+ t->comp_desc->cd_type = ASN_COMPOSITE;
+ t->comp_desc->cd_type_id = COMPOSITE_ASN1_TYPE;
+ t->comp_desc->cd_all_match = (allcomponent_matching_func*)MatchingComponentCertificateList;
+ return LDAP_SUCCESS;
+} /* GDecCertificateList*/
diff --git a/contrib/slapd-modules/comp_match/crl.h b/contrib/slapd-modules/comp_match/crl.h
new file mode 100644
index 0000000..f2b4a24
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/crl.h
@@ -0,0 +1,359 @@
+
+#include "asn-incl.h"
+/*
+ * crl.h
+ * "CertificateRevokationList" ASN.1 module encode/decode/extracting/matching/free C src.
+ * This file was generated by modified eSMACC compiler Fri Jan 21 11:25:24 2005
+ * The generated files are strongly encouraged to be
+ * compiled as a module for OpenLDAP Software
+ */
+
+#ifndef _crl_h_
+#define _crl_h_
+
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef _WIN32
+#pragma warning( disable : 4101 )
+#endif
+#include "componentlib.h"
+
+#define V1 0
+#define V2 1
+#define V3 2
+
+typedef ComponentInt ComponentVersion; /* INTEGER { V1 (0), V2 (1), V3 (2) } */
+
+#define MatchingComponentVersion MatchingComponentInt
+
+#define ExtractingComponentVersion ExtractingComponentInt
+
+#define BDecComponentVersion BDecComponentInt
+
+#define GDecComponentVersion GDecComponentInt
+
+
+typedef ComponentInt ComponentCertificateSerialNumber; /* INTEGER */
+
+#define MatchingComponentCertificateSerialNumber MatchingComponentInt
+
+#define ExtractingComponentCertificateSerialNumber ExtractingComponentInt
+
+#define BDecComponentCertificateSerialNumber BDecComponentInt
+
+#define GDecComponentCertificateSerialNumber GDecComponentInt
+
+
+typedef ComponentOid ComponentAttributeType; /* OBJECT IDENTIFIER */
+
+#define MatchingComponentAttributeType MatchingComponentOid
+
+#define ExtractingComponentAttributeType ExtractingComponentOid
+
+#define BDecComponentAttributeType BDecComponentOid
+
+#define GDecComponentAttributeType GDecComponentOid
+
+
+typedef struct AlgorithmIdentifier /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentOid algorithm; /* OBJECT IDENTIFIER */
+ ComponentAnyDefinedBy parameters; /* ANY DEFINED BY algorithm OPTIONAL */
+} ComponentAlgorithmIdentifier;
+
+int MatchingComponentAlgorithmIdentifier PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentAlgorithmIdentifier PROTO (( void* mem_op, ComponentReference *cr, ComponentAlgorithmIdentifier *comp ));
+
+
+int BDecComponentAlgorithmIdentifier PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentAlgorithmIdentifier **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentAlgorithmIdentifier PROTO (( void* mem_op, GenBuf * b, ComponentAlgorithmIdentifier **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct Time /* CHOICE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ enum TimeChoiceId
+ {
+ TIME_UTCTIME,
+ TIME_GENERALIZEDTIME
+ } choiceId;
+ union TimeChoiceUnion
+ {
+ ComponentUTCTime* utcTime; /* < unknown type id ?! > */
+ ComponentGeneralizedTime* generalizedTime; /* < unknown type id ?! > */
+ } a;
+} ComponentTime;
+
+int MatchingComponentTime PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentTime PROTO (( void* mem_op, ComponentReference *cr, ComponentTime *comp ));
+
+
+int BDecComponentTime PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentTime **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentTime PROTO (( void* mem_op, GenBuf * b, ComponentTime **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct Extension /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentOid extnID; /* OBJECT IDENTIFIER */
+ ComponentBool* critical; /* BOOLEAN DEFAULT FALSE */
+ ComponentOcts extnValue; /* OCTET STRING */
+} ComponentExtension;
+
+int MatchingComponentExtension PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentExtension PROTO (( void* mem_op, ComponentReference *cr, ComponentExtension *comp ));
+
+
+int BDecComponentExtension PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentExtension **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentExtension PROTO (( void* mem_op, GenBuf * b, ComponentExtension **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct AttributeTypeAndValue /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentAttributeType type; /* AttributeType */
+ ComponentAnyDefinedBy value; /* ANY DEFINED BY type */
+} ComponentAttributeTypeAndValue;
+
+int MatchingComponentAttributeTypeAndValue PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentAttributeTypeAndValue PROTO (( void* mem_op, ComponentReference *cr, ComponentAttributeTypeAndValue *comp ));
+
+
+int BDecComponentAttributeTypeAndValue PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentAttributeTypeAndValue **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentAttributeTypeAndValue PROTO (( void* mem_op, GenBuf * b, ComponentAttributeTypeAndValue **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef ComponentList ComponentExtensions; /* SEQUENCE SIZE 1..MAX OF Extension */
+
+int MatchingComponentExtensions PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentExtensions PROTO (( void* mem_op, ComponentReference *cr, ComponentExtensions *comp ));
+
+
+int BDecComponentExtensions PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentExtensions **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentExtensions PROTO (( void* mem_op, GenBuf * b, ComponentExtensions **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct TBSCertListSeqOfSeq /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentCertificateSerialNumber userCertificate; /* CertificateSerialNumber */
+ ComponentTime* revocationDate; /* Time */
+ ComponentExtensions* crlEntryExtensions; /* Extensions OPTIONAL */
+} ComponentTBSCertListSeqOfSeq;
+
+int MatchingComponentTBSCertListSeqOfSeq PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentTBSCertListSeqOfSeq PROTO (( void* mem_op, ComponentReference *cr, ComponentTBSCertListSeqOfSeq *comp ));
+
+
+int BDecComponentTBSCertListSeqOfSeq PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentTBSCertListSeqOfSeq **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentTBSCertListSeqOfSeq PROTO (( void* mem_op, GenBuf * b, ComponentTBSCertListSeqOfSeq **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef ComponentList ComponentTBSCertListSeqOf; /* SEQUENCE OF TBSCertListSeqOfSeq */
+
+int MatchingComponentTBSCertListSeqOf PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentTBSCertListSeqOf PROTO (( void* mem_op, ComponentReference *cr, ComponentTBSCertListSeqOf *comp ));
+
+
+int BDecComponentTBSCertListSeqOf PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentTBSCertListSeqOf **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentTBSCertListSeqOf PROTO (( void* mem_op, GenBuf * b, ComponentTBSCertListSeqOf **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef ComponentList ComponentRelativeDistinguishedName; /* SET OF AttributeTypeAndValue */
+
+int MatchingComponentRelativeDistinguishedName PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentRelativeDistinguishedName PROTO (( void* mem_op, ComponentReference *cr, ComponentRelativeDistinguishedName *comp ));
+
+
+int BDecComponentRelativeDistinguishedName PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentRelativeDistinguishedName **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentRelativeDistinguishedName PROTO (( void* mem_op, GenBuf * b, ComponentRelativeDistinguishedName **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef ComponentList ComponentRDNSequence; /* SEQUENCE OF RelativeDistinguishedName */
+
+int MatchingComponentRDNSequence PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentRDNSequence PROTO (( void* mem_op, ComponentReference *cr, ComponentRDNSequence *comp ));
+
+
+int BDecComponentRDNSequence PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentRDNSequence **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentRDNSequence PROTO (( void* mem_op, GenBuf * b, ComponentRDNSequence **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct Name /* CHOICE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ enum NameChoiceId
+ {
+ NAME_RDNSEQUENCE
+ } choiceId;
+ union NameChoiceUnion
+ {
+ ComponentRDNSequence* rdnSequence; /* RDNSequence */
+ } a;
+} ComponentName;
+
+int MatchingComponentName PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentName PROTO (( void* mem_op, ComponentReference *cr, ComponentName *comp ));
+
+
+int BDecComponentName PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentName **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentName PROTO (( void* mem_op, GenBuf * b, ComponentName **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct TBSCertList /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentVersion* version; /* Version OPTIONAL */
+ ComponentAlgorithmIdentifier* signature; /* AlgorithmIdentifier */
+ ComponentName* issuer; /* Name */
+ ComponentTime* thisUpdate; /* Time */
+ ComponentTime* nextUpdate; /* Time OPTIONAL */
+ ComponentTBSCertListSeqOf* revokedCertificates; /* TBSCertListSeqOf */
+ ComponentExtensions* crlExtensions; /* [0] EXPLICIT Extensions OPTIONAL */
+} ComponentTBSCertList;
+
+int MatchingComponentTBSCertList PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentTBSCertList PROTO (( void* mem_op, ComponentReference *cr, ComponentTBSCertList *comp ));
+
+
+int BDecComponentTBSCertList PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentTBSCertList **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentTBSCertList PROTO (( void* mem_op, GenBuf * b, ComponentTBSCertList **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct CertificateList /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentTBSCertList* tbsCertList; /* TBSCertList */
+ ComponentAlgorithmIdentifier* signatureAlgorithm; /* AlgorithmIdentifier */
+ ComponentBits signature; /* BIT STRING */
+} ComponentCertificateList;
+
+int MatchingComponentCertificateList PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentCertificateList PROTO (( void* mem_op, ComponentReference *cr, ComponentCertificateList *comp ));
+
+
+int BDecComponentCertificateList PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentCertificateList **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentCertificateList PROTO (( void* mem_op, GenBuf * b, ComponentCertificateList **v, AsnLen *bytesDecoded, int mode));
+
+
+
+typedef struct Validity /* SEQUENCE */
+{
+ Syntax* syntax;
+ ComponentDesc* comp_desc;
+ struct berval identifier;
+ char id_buf[MAX_IDENTIFIER_LEN];
+ ComponentTime* notBefore; /* Time */
+ ComponentTime* notAfter; /* Time */
+} ComponentValidity;
+
+int MatchingComponentValidity PROTO (( char *oid, ComponentSyntaxInfo *, ComponentSyntaxInfo *v2 ));
+
+
+void* ExtractingComponentValidity PROTO (( void* mem_op, ComponentReference *cr, ComponentValidity *comp ));
+
+
+int BDecComponentValidity PROTO ((void* mem_op, GenBuf * b, AsnTag tagId0, AsnLen elmtLen0, ComponentValidity **v, AsnLen *bytesDecoded, int mode));
+
+
+int GDecComponentValidity PROTO (( void* mem_op, GenBuf * b, ComponentValidity **v, AsnLen *bytesDecoded, int mode));
+
+
+
+/* ========== Object Declarations ========== */
+
+
+/* ========== Object Set Declarations ========== */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#endif /* conditional include of crl.h */
diff --git a/contrib/slapd-modules/comp_match/init.c b/contrib/slapd-modules/comp_match/init.c
new file mode 100644
index 0000000..c3ab83c
--- /dev/null
+++ b/contrib/slapd-modules/comp_match/init.c
@@ -0,0 +1,839 @@
+/* Copyright 2004 IBM Corporation
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ */
+/* ACKNOWLEDGEMENTS
+ * This work originally developed by Sang Seok Lim
+ * 2004/06/18 03:20:00 slim@OpenLDAP.org
+ */
+
+#include "portable.h"
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ldap_pvt.h>
+#include "lutil.h"
+#include <ldap.h>
+#include "slap.h"
+#include "component.h"
+
+#include "componentlib.h"
+#include "asn.h"
+#include <asn-gser.h>
+
+#include <string.h>
+
+#ifndef SLAPD_COMP_MATCH
+#define SLAPD_COMP_MATCH SLAPD_MOD_DYNAMIC
+#endif
+
+/*
+ * Attribute and MatchingRule aliasing table
+ */
+AttributeAliasing aa_table [ MAX_ALIASING_ENTRY ];
+MatchingRuleAliasing mra_table [ MAX_ALIASING_ENTRY ];
+
+OD_entry* gOD_table = NULL;
+AsnTypetoMatchingRuleTable* gATMR_table = NULL;
+
+int
+load_derived_matching_rule ( char* cfg_path ){
+}
+
+AttributeAliasing*
+comp_is_aliased_attribute( void *in )
+{
+ AttributeAliasing* curr_aa;
+ int i;
+ AttributeDescription *ad = (AttributeDescription*)in;
+
+ for ( i = 0; aa_table[i].aa_aliasing_ad && i < MAX_ALIASING_ENTRY; i++ ) {
+ if ( strncmp(aa_table[i].aa_aliasing_ad->ad_cname.bv_val , ad->ad_cname.bv_val, ad->ad_cname.bv_len) == 0 )
+ return &aa_table[i];
+ }
+ return NULL;
+}
+
+static int
+add_aa_entry( int index, char* aliasing_at_name, char* aliased_at_name, char* mr_name, char* component_filter )
+{
+ char text[1][128];
+ int rc;
+ struct berval type;
+
+ /* get and store aliasing AttributeDescription */
+ type.bv_val = aliasing_at_name;
+ type.bv_len = strlen ( aliasing_at_name );
+ rc = slap_bv2ad ( &type, &aa_table[index].aa_aliasing_ad,(const char**)text );
+ if ( rc != LDAP_SUCCESS ) return rc;
+
+ /* get and store aliased AttributeDescription */
+ type.bv_val = aliased_at_name;
+ type.bv_len = strlen ( aliased_at_name );
+ rc = slap_bv2ad ( &type, &aa_table[index].aa_aliased_ad,(const char**)text );
+ if ( rc != LDAP_SUCCESS ) return rc;
+
+ /* get and store componentFilterMatch */
+ type.bv_val = mr_name;
+ type.bv_len = strlen ( mr_name);
+ aa_table[index].aa_mr = mr_bvfind ( &type );
+
+ /* get and store a component filter */
+ type.bv_val = component_filter;
+ type.bv_len = strlen ( component_filter );
+ rc = get_comp_filter( NULL, &type, &aa_table[index].aa_cf,(const char**)text);
+
+ aa_table[index].aa_cf_str = component_filter;
+
+ return rc;
+}
+
+/*
+ * Initialize attribute aliasing table when this module is loaded
+ * add_aa_entry ( index for the global table,
+ * name of the aliasing attribute,
+ * component filter with filling value parts "xxx"
+ * )
+ * "xxx" will be replaced with effective values later.
+ * See RFC3687 to understand the content of a component filter.
+ */
+char* pre_processed_comp_filter[] = {
+/*1*/"item:{ component \"toBeSigned.issuer.rdnSequence\", rule distinguishedNameMatch, value xxx }",
+/*2*/"item:{ component \"toBeSigned.serialNumber\", rule integerMatch, value xxx }",
+/*3*/"and:{ item:{ component \"toBeSigned.serialNumber\", rule integerMatch, value xxx }, item:{ component \"toBeSigned.issuer.rdnSequence\", rule distinguishedNameMatch, value xxx } }"
+};
+
+static int
+init_attribute_aliasing_table ()
+{
+ int rc;
+ int index = 0 ;
+
+ rc = add_aa_entry ( index, "x509CertificateIssuer", "userCertificate","componentFilterMatch", pre_processed_comp_filter[index] );
+ if ( rc != LDAP_SUCCESS ) return LDAP_PARAM_ERROR;
+ index++;
+
+ rc = add_aa_entry ( index, "x509CertificateSerial","userCertificate", "componentFilterMatch", pre_processed_comp_filter[index] );
+ if ( rc != LDAP_SUCCESS ) return LDAP_PARAM_ERROR;
+ index++;
+
+ rc = add_aa_entry ( index, "x509CertificateSerialAndIssuer", "userCertificate", "componentFilterMatch", pre_processed_comp_filter[index] );
+ if ( rc != LDAP_SUCCESS ) return LDAP_PARAM_ERROR;
+ index++;
+
+ return LDAP_SUCCESS;
+}
+
+void
+init_component_description_table () {
+ AsnTypeId id;
+ struct berval mr;
+ AsnTypetoSyntax* asn_to_syn;
+ Syntax* syn;
+
+ for ( id = BASICTYPE_BOOLEAN; id != ASNTYPE_END ; id++ ) {
+ asntype_to_compType_mapping_tbl[id].ac_comp_type.ct_subtypes = NULL;
+ asntype_to_compType_mapping_tbl[id].ac_comp_type.ct_syntax = NULL;
+
+ /* Equality Matching Rule */
+ if ( asntype_to_compMR_mapping_tbl[id].atc_equality ) {
+ mr.bv_val = asntype_to_compMR_mapping_tbl[id].atc_equality;
+ mr.bv_len = strlen(asntype_to_compMR_mapping_tbl[id].atc_equality);
+ asntype_to_compType_mapping_tbl[id].ac_comp_type.ct_equality = mr_bvfind( &mr );
+ }
+ /* Approx Matching Rule */
+ if ( asntype_to_compMR_mapping_tbl[id].atc_approx ) {
+ mr.bv_val = asntype_to_compMR_mapping_tbl[id].atc_approx;
+ mr.bv_len = strlen(asntype_to_compMR_mapping_tbl[id].atc_approx);
+ asntype_to_compType_mapping_tbl[id].ac_comp_type.ct_approx = mr_bvfind( &mr );
+ }
+
+ /* Ordering Matching Rule */
+ if ( asntype_to_compMR_mapping_tbl[id].atc_ordering ) {
+ mr.bv_val = asntype_to_compMR_mapping_tbl[id].atc_ordering;
+ mr.bv_len = strlen(asntype_to_compMR_mapping_tbl[id].atc_ordering);
+ asntype_to_compType_mapping_tbl[id].ac_comp_type.ct_ordering= mr_bvfind( &mr );
+ }
+
+ /* Substr Matching Rule */
+ if ( asntype_to_compMR_mapping_tbl[id].atc_substr ) {
+ mr.bv_val = asntype_to_compMR_mapping_tbl[id].atc_substr;
+ mr.bv_len = strlen(asntype_to_compMR_mapping_tbl[id].atc_substr);
+ asntype_to_compType_mapping_tbl[id].ac_comp_type.ct_substr = mr_bvfind( &mr );
+ }
+ /* Syntax */
+
+ asn_to_syn = &asn_to_syntax_mapping_tbl[ id ];
+ if ( asn_to_syn->ats_syn_oid )
+ syn = syn_find ( asn_to_syn->ats_syn_oid );
+ else
+ syn = NULL;
+ asntype_to_compType_mapping_tbl[id].ac_comp_type.ct_syntax = syn;
+
+ /* Initialize Component Descriptions of primitive ASN.1 types */
+ asntype_to_compdesc_mapping_tbl[id].atcd_cd.cd_comp_type = (AttributeType*)&asntype_to_compType_mapping_tbl[id].ac_comp_type;
+ }
+}
+
+MatchingRule*
+retrieve_matching_rule( char* mr_oid, AsnTypeId type ) {
+ char* tmp;
+ struct berval mr_name = BER_BVNULL;
+ AsnTypetoMatchingRuleTable* atmr;
+
+ for ( atmr = gATMR_table ; atmr ; atmr = atmr->atmr_table_next ) {
+ if ( strcmp( atmr->atmr_oid, mr_oid ) == 0 ) {
+ tmp = atmr->atmr_table[type].atmr_mr_name;
+ if ( tmp ) {
+ mr_name.bv_val = tmp;
+ mr_name.bv_len = strlen( tmp );
+ return mr_bvfind ( &mr_name );
+ }
+ }
+ }
+ return (MatchingRule*)NULL;
+}
+
+void*
+comp_convert_attr_to_comp LDAP_P (( Attribute* a, Syntax *syn, struct berval* bv ))
+{
+ char* peek_head;
+ int mode, bytesDecoded, size, rc;
+ void* component;
+ char* oid = a->a_desc->ad_type->sat_atype.at_oid ;
+ GenBuf* b = NULL;
+ ExpBuf* buf = NULL;
+ OidDecoderMapping* odm;
+
+ /* look for the decoder registered for the given attribute */
+ odm = RetrieveOidDecoderMappingbyOid( oid, strlen(oid) );
+
+ if ( !odm || (!odm->BER_Decode && !odm->GSER_Decode) )
+ return (void*)NULL;
+
+ buf = ExpBufAllocBuf();
+ ExpBuftoGenBuf( buf, &b );
+ ExpBufInstallDataInBuf ( buf, bv->bv_val, bv->bv_len );
+ BufResetInReadMode( b );
+
+ mode = DEC_ALLOC_MODE_2;
+ /*
+ * How can we decide which decoder will be called, GSER or BER?
+ * Currently BER decoder is called for a certificate.
+ * The flag of Attribute will say something about it in the future
+ */
+ if ( syn && slap_syntax_is_ber ( syn ) ) {
+#if 0
+ rc =BDecComponentTop(odm->BER_Decode, a->a_comp_data->cd_mem_op, b, 0,0, &component,&bytesDecoded,mode ) ;
+#endif
+ rc = odm->BER_Decode ( a->a_comp_data->cd_mem_op, b, (ComponentSyntaxInfo*)&component, &bytesDecoded, mode );
+ }
+ else {
+ rc = odm->GSER_Decode( a->a_comp_data->cd_mem_op, b, (ComponentSyntaxInfo**)component, &bytesDecoded, mode);
+ }
+
+ ExpBufFreeBuf( buf );
+ GenBufFreeBuf( b );
+ if ( rc == -1 ) {
+#if 0
+ ShutdownNibbleMemLocal ( a->a_comp_data->cd_mem_op );
+ free ( a->a_comp_data );
+ a->a_comp_data = NULL;
+#endif
+ return (void*)NULL;
+ }
+ else {
+ return component;
+ }
+}
+
+#include <nibble-alloc.h>
+void
+comp_free_component ( void* mem_op ) {
+ ShutdownNibbleMemLocal( (NibbleMem*)mem_op );
+ return;
+}
+
+void
+comp_convert_assert_to_comp (
+ void* mem_op,
+ ComponentSyntaxInfo *csi_attr,
+ struct berval* bv,
+ ComponentSyntaxInfo** csi, int* len, int mode )
+{
+ int rc;
+ GenBuf* genBuf;
+ ExpBuf* buf;
+ gser_decoder_func *decoder = csi_attr->csi_comp_desc->cd_gser_decoder;
+
+ buf = ExpBufAllocBuf();
+ ExpBuftoGenBuf( buf, &genBuf );
+ ExpBufInstallDataInBuf ( buf, bv->bv_val, bv->bv_len );
+ BufResetInReadMode( genBuf );
+
+ if ( csi_attr->csi_comp_desc->cd_type_id == BASICTYPE_ANY )
+ decoder = ((ComponentAny*)csi_attr)->cai->GSER_Decode;
+
+ rc = (*decoder)( mem_op, genBuf, csi, len, mode );
+ ExpBufFreeBuf ( buf );
+ GenBufFreeBuf( genBuf );
+}
+
+int intToAscii( int value, char* buf ) {
+ int minus=0,i,temp;
+ int total_num_digits;
+
+ if ( value == 0 ){
+ buf[0] = '0';
+ return 1;
+ }
+
+ if ( value < 0 ){
+ minus = 1;
+ value = value*(-1);
+ buf[0] = '-';
+ }
+
+ /* How many digits */
+ for ( temp = value, total_num_digits=0 ; temp ; total_num_digits++ )
+ temp = temp/10;
+
+ total_num_digits += minus;
+
+ for ( i = minus ; value ; i++ ) {
+ buf[ total_num_digits - i - 1 ]= (char)(value%10 + '0');
+ value = value/10;
+ }
+ return i;
+}
+
+int
+comp_convert_asn_to_ldap ( MatchingRule* mr, ComponentSyntaxInfo* csi, struct berval* bv, int *allocated )
+{
+ int rc;
+ struct berval prettied;
+ Syntax* syn;
+
+ AsnTypetoSyntax* asn_to_syn =
+ &asn_to_syntax_mapping_tbl[csi->csi_comp_desc->cd_type_id];
+ if ( asn_to_syn->ats_syn_oid )
+ csi->csi_syntax = syn_find ( asn_to_syn->ats_syn_oid );
+ else
+ csi->csi_syntax = NULL;
+
+
+ switch ( csi->csi_comp_desc->cd_type_id ) {
+ case BASICTYPE_BOOLEAN :
+ bv->bv_val = (char*)malloc( 5 );
+ *allocated = 1;
+ bv->bv_len = 5;
+ if ( ((ComponentBool*)csi)->value > 0 ) {
+ strcpy ( bv->bv_val , "TRUE" );
+ bv->bv_len = 4;
+ }
+ else {
+ strcpy ( bv->bv_val , "FALSE" );
+ bv->bv_len = 5;
+ }
+ break ;
+ case BASICTYPE_NULL :
+ bv->bv_len = 0;
+ break;
+ case BASICTYPE_INTEGER :
+ bv->bv_val = (char*)malloc( INITIAL_ATTR_SIZE );
+ *allocated = 1;
+ bv->bv_len = INITIAL_ATTR_SIZE;
+ bv->bv_len = intToAscii(((ComponentInt*)csi)->value, bv->bv_val );
+ if ( bv->bv_len <= 0 )
+ return LDAP_INVALID_SYNTAX;
+ break;
+ case BASICTYPE_REAL :
+ return LDAP_INVALID_SYNTAX;
+ case BASICTYPE_ENUMERATED :
+ bv->bv_val = (char*)malloc( INITIAL_ATTR_SIZE );
+ *allocated = 1;
+ bv->bv_len = INITIAL_ATTR_SIZE;
+ bv->bv_len = intToAscii(((ComponentEnum*)csi)->value, bv->bv_val );
+ if ( bv->bv_len <= 0 )
+ return LDAP_INVALID_SYNTAX;
+ break;
+ case BASICTYPE_OID :
+ case BASICTYPE_OCTETSTRING :
+ case BASICTYPE_BITSTRING :
+ case BASICTYPE_NUMERIC_STR :
+ case BASICTYPE_PRINTABLE_STR :
+ case BASICTYPE_UNIVERSAL_STR :
+ case BASICTYPE_IA5_STR :
+ case BASICTYPE_BMP_STR :
+ case BASICTYPE_UTF8_STR :
+ case BASICTYPE_UTCTIME :
+ case BASICTYPE_GENERALIZEDTIME :
+ case BASICTYPE_GRAPHIC_STR :
+ case BASICTYPE_VISIBLE_STR :
+ case BASICTYPE_GENERAL_STR :
+ case BASICTYPE_OBJECTDESCRIPTOR :
+ case BASICTYPE_VIDEOTEX_STR :
+ case BASICTYPE_T61_STR :
+ case BASICTYPE_OCTETCONTAINING :
+ case BASICTYPE_BITCONTAINING :
+ case BASICTYPE_RELATIVE_OID :
+ bv->bv_val = ((ComponentOcts*)csi)->value.octs;
+ bv->bv_len = ((ComponentOcts*)csi)->value.octetLen;
+ break;
+ case BASICTYPE_ANY :
+ csi = ((ComponentAny*)csi)->value;
+ if ( csi->csi_comp_desc->cd_type != ASN_BASIC ||
+ csi->csi_comp_desc->cd_type_id == BASICTYPE_ANY )
+ return LDAP_INVALID_SYNTAX;
+ return comp_convert_asn_to_ldap( mr, csi, bv, allocated );
+ case COMPOSITE_ASN1_TYPE :
+ break;
+ case RDNSequence :
+ /*dnMatch*/
+ if( strncmp( mr->smr_mrule.mr_oid, DN_MATCH_OID, strlen(DN_MATCH_OID) ) != 0 )
+ return LDAP_INVALID_SYNTAX;
+ *allocated = 1;
+ rc = ConvertRDNSequence2RFC2253( (irRDNSequence*)csi, bv );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ break;
+ case RelativeDistinguishedName :
+ /*rdnMatch*/
+ if( strncmp( mr->smr_mrule.mr_oid, RDN_MATCH_OID, strlen(RDN_MATCH_OID) ) != 0 )
+ return LDAP_INVALID_SYNTAX;
+ *allocated = 1;
+ rc = ConvertRDN2RFC2253((irRelativeDistinguishedName*)csi,bv);
+ if ( rc != LDAP_SUCCESS ) return rc;
+ break;
+ case TelephoneNumber :
+ case FacsimileTelephoneNumber__telephoneNumber :
+ break;
+ case DirectoryString :
+ return LDAP_INVALID_SYNTAX;
+ case ASN_COMP_CERTIFICATE :
+ case ASNTYPE_END :
+ break;
+ default :
+ /*Only ASN Basic Type can be converted into LDAP string*/
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( csi->csi_syntax ) {
+ if ( csi->csi_syntax->ssyn_validate ) {
+ rc = csi->csi_syntax->ssyn_validate(csi->csi_syntax, bv);
+ if ( rc != LDAP_SUCCESS )
+ return LDAP_INVALID_SYNTAX;
+ }
+ if ( csi->csi_syntax->ssyn_pretty ) {
+ rc = csi->csi_syntax->ssyn_pretty(csi->csi_syntax, bv, &prettied , NULL );
+ if ( rc != LDAP_SUCCESS )
+ return LDAP_INVALID_SYNTAX;
+#if 0
+ free ( bv->bv_val );/*potential memory leak?*/
+#endif
+ bv->bv_val = prettied.bv_val;
+ bv->bv_len = prettied.bv_len;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * If <all> type component referenced is used
+ * more than one component will be tested
+ */
+#define IS_TERMINAL_COMPREF(cr) (cr->cr_curr->ci_next == NULL)
+int
+comp_test_all_components (
+ void* attr_mem_op,
+ void* assert_mem_op,
+ ComponentSyntaxInfo *csi_attr,
+ ComponentAssertion* ca )
+{
+ int rc;
+ ComponentSyntaxInfo *csi_temp = NULL, *csi_assert = NULL, *comp_elmt = NULL;
+ ComponentReference *cr = ca->ca_comp_ref;
+ struct berval *ca_val = &ca->ca_ma_value;
+
+ switch ( cr->cr_curr->ci_type ) {
+ case LDAP_COMPREF_ALL:
+ if ( IS_TERMINAL_COMPREF(cr) ) {
+ FOR_EACH_LIST_ELMT( comp_elmt, &((ComponentList*)csi_attr)->comp_list )
+ {
+ rc = comp_test_one_component( attr_mem_op, assert_mem_op, comp_elmt, ca );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ break;
+ }
+ }
+ } else {
+ ComponentId *start_compid = ca->ca_comp_ref->cr_curr->ci_next;
+ FOR_EACH_LIST_ELMT( comp_elmt, &((ComponentList*)csi_attr)->comp_list )
+ {
+ cr->cr_curr = start_compid;
+ rc = comp_test_components ( attr_mem_op, assert_mem_op, comp_elmt, ca );
+ if ( rc != LDAP_COMPARE_FALSE ) {
+ break;
+ }
+#if 0
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ break;
+ }
+#endif
+ }
+ }
+ break;
+ case LDAP_COMPREF_CONTENT:
+ case LDAP_COMPREF_SELECT:
+ case LDAP_COMPREF_DEFINED:
+ case LDAP_COMPREF_UNDEFINED:
+ case LDAP_COMPREF_IDENTIFIER:
+ case LDAP_COMPREF_FROM_BEGINNING:
+ case LDAP_COMPREF_FROM_END:
+ case LDAP_COMPREF_COUNT:
+ rc = LDAP_OPERATIONS_ERROR;
+ break;
+ default:
+ rc = LDAP_OPERATIONS_ERROR;
+ }
+ return rc;
+}
+
+void
+eat_bv_whsp ( struct berval* in )
+{
+ char* end = in->bv_val + in->bv_len;
+ for ( ; ( *in->bv_val == ' ' ) && ( in->bv_val < end ) ; ) {
+ in->bv_val++;
+ }
+}
+
+/*
+ * Perform matching one referenced component against assertion
+ * If the matching rule in a component filter is allComponentsMatch
+ * or its derivatives the extracted component's ASN.1 specification
+ * is applied to the assertion value as its syntax
+ * Otherwise, the matching rule's syntax is applied to the assertion value
+ * By RFC 3687
+ */
+int
+comp_test_one_component (
+ void* attr_mem_op,
+ void* assert_mem_op,
+ ComponentSyntaxInfo *csi_attr,
+ ComponentAssertion *ca )
+{
+ int len, rc;
+ ComponentSyntaxInfo *csi_assert = NULL;
+ char* oid = NULL;
+ MatchingRule* mr = ca->ca_ma_rule;
+
+ if ( mr->smr_usage & SLAP_MR_COMPONENT ) {
+ /* If allComponentsMatch or its derivatives */
+ if ( !ca->ca_comp_data.cd_tree ) {
+ comp_convert_assert_to_comp( assert_mem_op, csi_attr, &ca->ca_ma_value, &csi_assert, &len, DEC_ALLOC_MODE_0 );
+ ca->ca_comp_data.cd_tree = (void*)csi_assert;
+ } else {
+ csi_assert = ca->ca_comp_data.cd_tree;
+ }
+
+ if ( !csi_assert )
+ return LDAP_PROTOCOL_ERROR;
+
+ if ( strcmp( mr->smr_mrule.mr_oid, OID_ALL_COMP_MATCH ) != 0 )
+ {
+ /* allComponentMatch's derivatives */
+ oid = mr->smr_mrule.mr_oid;
+ }
+ return csi_attr->csi_comp_desc->cd_all_match(
+ oid, csi_attr, csi_assert );
+
+ } else {
+ /* LDAP existing matching rules */
+ struct berval attr_bv = BER_BVNULL;
+ struct berval n_attr_bv = BER_BVNULL;
+ struct berval* assert_bv = &ca->ca_ma_value;
+ int allocated = 0;
+ /*Attribute is converted to compatible LDAP encodings*/
+ if ( comp_convert_asn_to_ldap( mr, csi_attr, &attr_bv, &allocated ) != LDAP_SUCCESS )
+ return LDAP_INAPPROPRIATE_MATCHING;
+ /* extracted component value is not normalized */
+ if ( ca->ca_ma_rule->smr_normalize ) {
+ rc = ca->ca_ma_rule->smr_normalize (
+ SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
+ NULL, ca->ca_ma_rule,
+ &attr_bv, &n_attr_bv, NULL );
+ if ( rc != LDAP_SUCCESS )
+ return rc;
+ if ( allocated && attr_bv.bv_val )
+ free (attr_bv.bv_val);
+ } else {
+ n_attr_bv = attr_bv;
+ }
+#if 0
+ /*Assertion value is validated by MR's syntax*/
+ if ( !ca->ca_comp_data.cd_tree ) {
+ ca->ca_comp_data.cd_tree = assert_bv;
+ }
+ else {
+ assert_bv = ca->ca_comp_data.cd_tree;
+ }
+#endif
+ if ( !n_attr_bv.bv_val )
+ return LDAP_COMPARE_FALSE;
+ rc = csi_value_match( mr, &n_attr_bv, assert_bv );
+ if ( n_attr_bv.bv_val )
+ free ( n_attr_bv.bv_val );
+ return rc;
+ }
+}
+
+int
+comp_test_components( void* attr_nm, void* assert_nm, ComponentSyntaxInfo* csi_attr, ComponentAssertion* ca) {
+ char* peek_head;
+ int mode, bytesDecoded = 0, rc;
+ GenBuf* b;
+ ExpBuf* buf;
+ OidDecoderMapping* odm;
+ struct berval bv;
+ char oid[MAX_OID_LEN];
+ void* contained_comp, *anytype_comp;
+ ComponentReference* cr = ca->ca_comp_ref;
+
+ if ( !cr )
+ return comp_test_one_component ( attr_nm, assert_nm, csi_attr, ca );
+ /* Extracting the component referenced by ca->ca_comp_ref */
+ csi_attr = (ComponentSyntaxInfo*)csi_attr->csi_comp_desc->cd_extract_i( attr_nm, cr, csi_attr );
+ if ( !csi_attr ) return LDAP_INVALID_SYNTAX;
+ /* perform matching, considering the type of a Component Reference(CR)*/
+ switch( cr->cr_curr->ci_type ) {
+ case LDAP_COMPREF_IDENTIFIER:
+ case LDAP_COMPREF_FROM_BEGINNING:
+ case LDAP_COMPREF_FROM_END:
+ case LDAP_COMPREF_COUNT:
+ /*
+ * Exactly one component is referenced
+ * Fast Path for matching for this case
+ */
+ rc = comp_test_one_component ( attr_nm, assert_nm, csi_attr, ca );
+ break;
+ case LDAP_COMPREF_ALL:
+ /*
+ * If <all> type CR is used
+ * more than one component will be tested
+ */
+ rc = comp_test_all_components ( attr_nm, assert_nm, csi_attr, ca );
+ break;
+
+ case LDAP_COMPREF_CONTENT:
+ /*
+ * <content> type CR is used
+ * check if it is followed by <select> type CR.
+ * 1) If so, look up the corresponding decoder in the mapping
+ * table(OID to decoder) by <select>
+ * and then decode the OCTET/BIT STRING with the decoder
+ * Finally, extract the target component with the remaining CR.
+ * 2) If not, just return the current component, It SHOULD not be
+ * extracted further, because the component MUST be BIT/OCTET
+ * string.
+ */
+
+ cr->cr_curr = cr->cr_curr->ci_next;
+ if ( !cr->cr_curr ) {
+ /* case 2) in above description */
+ rc = comp_test_one_component ( attr_nm, assert_nm, csi_attr, ca );
+ break;
+ }
+
+ if ( cr->cr_curr->ci_type == LDAP_COMPREF_SELECT ) {
+ /* Look up OID mapping table */
+ odm = RetrieveOidDecoderMappingbyBV( &cr->cr_curr->ci_val.ci_select_value );
+
+ if ( !odm || !odm->BER_Decode )
+ return LDAP_PROTOCOL_ERROR;
+
+ /* current component MUST be either BIT or OCTET STRING */
+ if ( csi_attr->csi_comp_desc->cd_type_id != BASICTYPE_BITSTRING ) {
+ bv.bv_val = ((ComponentBits*)csi_attr)->value.bits;
+ bv.bv_len = ((ComponentBits*)csi_attr)->value.bitLen;
+ }
+ else if ( csi_attr->csi_comp_desc->cd_type_id != BASICTYPE_BITSTRING ) {
+ bv.bv_val = ((ComponentOcts*)csi_attr)->value.octs;
+ bv.bv_len = ((ComponentOcts*)csi_attr)->value.octetLen;
+ }
+ else
+ return LDAP_PROTOCOL_ERROR;
+
+ buf = ExpBufAllocBuf();
+ ExpBuftoGenBuf( buf, &b );
+ ExpBufInstallDataInBuf ( buf, bv.bv_val, bv.bv_len );
+ BufResetInReadMode( b );
+ mode = DEC_ALLOC_MODE_2;
+
+ /* Try to decode with BER/DER decoder */
+ rc = odm->BER_Decode ( attr_nm, b, (ComponentSyntaxInfo*)&contained_comp, &bytesDecoded, mode );
+
+ ExpBufFreeBuf( buf );
+ GenBufFreeBuf( b );
+
+ if ( rc != LDAP_SUCCESS ) return LDAP_PROTOCOL_ERROR;
+
+ /* xxx.content.(x.xy.xyz).rfc822Name */
+ /* In the aboe Ex. move CR to the right to (x.xy.xyz)*/
+ cr->cr_curr = cr->cr_curr->ci_next;
+ if (!cr->cr_curr )
+ rc = comp_test_one_component ( attr_nm, assert_nm, csi_attr, ca );
+ else
+ rc = comp_test_components( attr_nm, assert_nm, contained_comp, ca );
+ }
+ else {
+ /* Invalid Component reference */
+ rc = LDAP_PROTOCOL_ERROR;
+ }
+ break;
+ case LDAP_COMPREF_SELECT:
+ if (csi_attr->csi_comp_desc->cd_type_id != BASICTYPE_ANY )
+ return LDAP_INVALID_SYNTAX;
+ rc = CheckSelectTypeCorrect( attr_nm, ((ComponentAny*)csi_attr)->cai, &cr->cr_curr->ci_val.ci_select_value );
+ if ( rc < 0 ) return LDAP_INVALID_SYNTAX;
+
+ /* point to the real component, not any type component */
+ csi_attr = ((ComponentAny*)csi_attr)->value;
+ cr->cr_curr = cr->cr_curr->ci_next;
+ if ( cr->cr_curr )
+ rc = comp_test_components( attr_nm, assert_nm, csi_attr, ca);
+ else
+ rc = comp_test_one_component( attr_nm, assert_nm, csi_attr, ca);
+ break;
+ default:
+ rc = LDAP_INVALID_SYNTAX;
+ }
+ return rc;
+}
+
+
+void*
+comp_nibble_memory_allocator ( int init_mem, int inc_mem ) {
+ void* nm;
+ nm = (void*)InitNibbleMemLocal( (unsigned long)init_mem, (unsigned long)inc_mem );
+ if ( !nm ) return NULL;
+ else return (void*)nm;
+}
+
+void
+comp_nibble_memory_free ( void* nm ) {
+ ShutdownNibbleMemLocal( nm );
+}
+
+void*
+comp_get_component_description ( int id ) {
+ if ( asntype_to_compdesc_mapping_tbl[id].atcd_typeId == id )
+ return &asntype_to_compdesc_mapping_tbl[id].atcd_cd;
+ else
+ return NULL;
+}
+
+int
+comp_component_encoder ( void* mem_op, ComponentSyntaxInfo* csi , struct berval* nval ) {
+ int size, rc;
+ GenBuf* b;
+ ExpBuf* buf;
+ struct berval bv;
+
+ buf = ExpBufAllocBufAndData();
+ ExpBufResetInWriteRvsMode(buf);
+ ExpBuftoGenBuf( buf, &b );
+
+ if ( !csi->csi_comp_desc->cd_gser_encoder && !csi->csi_comp_desc->cd_ldap_encoder )
+ return (-1);
+
+ /*
+ * if an LDAP specific encoder is provided :
+ * dn and rdn have their LDAP specific encoder
+ */
+ if ( csi->csi_comp_desc->cd_ldap_encoder ) {
+ rc = csi->csi_comp_desc->cd_ldap_encoder( csi, &bv );
+ if ( rc != LDAP_SUCCESS )
+ return rc;
+ if ( mem_op )
+ nval->bv_val = CompAlloc( mem_op, bv.bv_len );
+ else
+ nval->bv_val = malloc( size );
+ memcpy( nval->bv_val, bv.bv_val, bv.bv_len );
+ nval->bv_len = bv.bv_len;
+ /*
+ * This free will be eliminated by making ldap_encoder
+ * use nibble memory in it
+ */
+ free ( bv.bv_val );
+ GenBufFreeBuf( b );
+ BufFreeBuf( buf );
+ return LDAP_SUCCESS;
+ }
+
+ rc = csi->csi_comp_desc->cd_gser_encoder( b, csi );
+ if ( rc < 0 ) {
+ GenBufFreeBuf( b );
+ BufFreeBuf( buf );
+ return rc;
+ }
+
+ size = ExpBufDataSize( buf );
+ if ( size > 0 ) {
+ if ( mem_op )
+ nval->bv_val = CompAlloc ( mem_op, size );
+ else
+ nval->bv_val = malloc( size );
+ nval->bv_len = size;
+ BufResetInReadMode(b);
+ BufCopy( nval->bv_val, b, size );
+ }
+ ExpBufFreeBuf( buf );
+ GenBufFreeBuf( b );
+
+ return LDAP_SUCCESS;
+}
+
+#if SLAPD_COMP_MATCH == SLAPD_MOD_DYNAMIC
+
+#include "certificate.h"
+
+extern convert_attr_to_comp_func* attr_converter;
+extern convert_assert_to_comp_func* assert_converter;
+extern convert_asn_to_ldap_func* csi_converter;
+extern free_component_func* component_destructor;
+extern test_component_func* test_components;
+extern alloc_nibble_func* nibble_mem_allocator;
+extern free_nibble_func* nibble_mem_free;
+extern test_membership_func* is_aliased_attribute;
+extern get_component_info_func* get_component_description;
+extern component_encoder_func* component_encoder;
+
+
+int init_module(int argc, char *argv[]) {
+ /*
+ * Initialize function pointers in slapd
+ */
+ attr_converter = (convert_attr_to_comp_func*)comp_convert_attr_to_comp;
+ assert_converter = (convert_assert_to_comp_func*)comp_convert_assert_to_comp;
+ component_destructor = (free_component_func*)comp_free_component;
+ test_components = (test_component_func*)comp_test_components;
+ nibble_mem_allocator = (free_nibble_func*)comp_nibble_memory_allocator;
+ nibble_mem_free = (free_nibble_func*)comp_nibble_memory_free;
+ is_aliased_attribute = (test_membership_func*)comp_is_aliased_attribute;
+ get_component_description = (get_component_info_func*)comp_get_component_description;
+ component_encoder = (component_encoder_func*)comp_component_encoder;
+
+ /* file path needs to be */
+ load_derived_matching_rule ("derived_mr.cfg");
+
+ /* the initialization for example X.509 certificate */
+ init_module_AuthenticationFramework();
+ init_module_AuthorityKeyIdentifierDefinition();
+ init_module_CertificateRevokationList();
+ init_attribute_aliasing_table ();
+ init_component_description_table ();
+ return 0;
+}
+
+#endif /* SLAPD_PASSWD */
diff --git a/contrib/slapd-modules/datamorph/Makefile b/contrib/slapd-modules/datamorph/Makefile
new file mode 100644
index 0000000..82bce49
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/Makefile
@@ -0,0 +1,77 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+# Copyright 2017 Ondřej Kuzník, Symas Corp. All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted only as authorized by the OpenLDAP
+# Public License.
+#
+# A copy of this license is available in the file LICENSE in the
+# top-level directory of the distribution or, alternatively, at
+# <http://www.OpenLDAP.org/license.html>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+SRCDIR = ./
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_DATAMORPH=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = datamorph.la
+MANPAGES = slapo-datamorph.5
+CLEAN = *.o *.lo *.la .libs
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+all: $(PROGRAMS)
+
+d :=
+sp :=
+dir := tests
+include $(dir)/Rules.mk
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+datamorph.la: datamorph.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf $(CLEAN)
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/datamorph/datamorph.c b/contrib/slapd-modules/datamorph/datamorph.c
new file mode 100644
index 0000000..7767586
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/datamorph.c
@@ -0,0 +1,2091 @@
+/* datamorph.c - enumerated and native integer value support */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed in 2016 by Ondřej Kuzník for Symas Corp.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_DATAMORPH
+
+#include <inttypes.h>
+#include <ac/stdlib.h>
+
+#if defined(__linux__)
+#include <endian.h>
+
+#elif defined(sun)
+
+#define be16toh(x) BE_16(x)
+#define le16toh(x) LE_16(x)
+#define htobe16(x) BE_16(x)
+#define htole16(x) LE_16(x)
+
+#define be32toh(x) BE_32(x)
+#define le32toh(x) LE_32(x)
+#define htobe32(x) BE_32(x)
+#define htole32(x) LE_32(x)
+
+#define be64toh(x) BE_64(x)
+#define le64toh(x) LE_64(x)
+#define htobe64(x) BE_64(x)
+#define htole64(x) LE_64(x)
+
+#elif defined(__NetBSD__) || defined(__FreeBSD__)
+#include <sys/endian.h>
+
+#elif defined(__OpenBSD__)
+#include <sys/endian.h>
+
+#define be16toh(x) betoh16(x)
+#define le16toh(x) letoh16(x)
+
+#define be32toh(x) betoh32(x)
+#define le32toh(x) letoh32(x)
+
+#define be64toh(x) betoh64(x)
+#define le64toh(x) letoh64(x)
+
+#elif defined(__BYTE_ORDER__) && \
+ ( defined(__GNUC__) || defined(__clang__) )
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define be16toh(x) __builtin_bswap16(x)
+#define le16toh(x) (x)
+#define htobe16(x) __builtin_bswap16(x)
+#define htole16(x) (x)
+
+#define be32toh(x) __builtin_bswap32(x)
+#define le32toh(x) (x)
+#define htobe32(x) __builtin_bswap32(x)
+#define htole32(x) (x)
+
+#define be64toh(x) __builtin_bswap64(x)
+#define le64toh(x) (x)
+#define htobe64(x) __builtin_bswap64(x)
+#define htole64(x) (x)
+
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define be16toh(x) (x)
+#define le16toh(x) __builtin_bswap16(x)
+#define htobe16(x) (x)
+#define htole16(x) __builtin_bswap16(x)
+
+#define be32toh(x) (x)
+#define le32toh(x) __builtin_bswap32(x)
+#define htobe32(x) (x)
+#define htole32(x) __builtin_bswap32(x)
+
+#define be64toh(x) (x)
+#define le64toh(x) __builtin_bswap64(x)
+#define htobe64(x) (x)
+#define htole64(x) __builtin_bswap64(x)
+
+#else
+#error "Only support pure big and little endian at the moment"
+#endif
+
+#else
+#error "I lack the way to check my endianness and convert to/from big-endian"
+#endif
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+#include "ldap_queue.h"
+
+typedef enum datamorph_type_t {
+ DATAMORPH_UNSET,
+ DATAMORPH_ENUM,
+ DATAMORPH_INT,
+} datamorph_type;
+
+typedef enum datamorph_flags_t {
+ DATAMORPH_FLAG_SIGNED = 1 << 0,
+ DATAMORPH_FLAG_LOWER = 1 << 1,
+ DATAMORPH_FLAG_UPPER = 1 << 2,
+} datamorph_flags;
+
+typedef union datamorph_interval_bound_t {
+ int64_t i;
+ uint64_t u;
+} datamorph_interval_bound;
+
+typedef struct transformation_info_t {
+ AttributeDescription *attr;
+ datamorph_type type;
+ union {
+ struct {
+ Avlnode *to_db;
+ struct berval from_db[256];
+ } maps;
+#define ti_enum info.maps
+ struct {
+ datamorph_flags flags;
+ unsigned int size;
+ datamorph_interval_bound lower, upper;
+ } interval;
+#define ti_int info.interval
+ } info;
+} transformation_info;
+
+typedef struct datamorph_enum_mapping_t {
+ struct berval wire_value;
+ uint8_t db_value;
+ transformation_info *transformation;
+} datamorph_enum_mapping;
+
+typedef struct datamorph_info_t {
+ Avlnode *transformations;
+ transformation_info *wip_transformation;
+} datamorph_info;
+
+static int
+transformation_mapping_cmp( const void *l, const void *r )
+{
+ const datamorph_enum_mapping *left = l, *right = r;
+
+ return ber_bvcmp( &left->wire_value, &right->wire_value );
+}
+
+static int
+transformation_info_cmp( const void *l, const void *r )
+{
+ const transformation_info *left = l, *right = r;
+
+ return ( left->attr == right->attr ) ? 0 :
+ ( left->attr < right->attr ) ? -1 :
+ 1;
+}
+
+static int
+transform_to_db_format_one(
+ Operation *op,
+ transformation_info *definition,
+ struct berval *value,
+ struct berval *outval )
+{
+ switch ( definition->type ) {
+ case DATAMORPH_ENUM: {
+ datamorph_enum_mapping *mapping, needle = { .wire_value = *value };
+ struct berval db_value = { .bv_len = 1 };
+
+ mapping = ldap_avl_find( definition->ti_enum.to_db, &needle,
+ transformation_mapping_cmp );
+ if ( !mapping ) {
+ Debug( LDAP_DEBUG_ANY, "transform_to_db_format_one: "
+ "value '%s' not mapped\n",
+ value->bv_val );
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ db_value.bv_val = (char *)&mapping->db_value;
+ ber_dupbv( outval, &db_value );
+ assert( outval->bv_val );
+ break;
+ }
+
+ case DATAMORPH_INT: {
+ union {
+ char s[8];
+ uint8_t be8;
+ uint16_t be16;
+ uint32_t be32;
+ uint64_t be64;
+ } buf;
+ struct berval db_value = { .bv_val = buf.s };
+ char *ptr = value->bv_val + value->bv_len;
+ uint64_t unsigned_value;
+ int64_t signed_value;
+
+ assert( definition->ti_int.size == 1 ||
+ definition->ti_int.size == 2 ||
+ definition->ti_int.size == 4 ||
+ definition->ti_int.size == 8 );
+
+ /* Read number */
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ signed_value = strtoll( value->bv_val, &ptr, 10 );
+ } else {
+ unsigned_value = strtoull( value->bv_val, &ptr, 10 );
+ }
+ if ( *value->bv_val == '\0' || *ptr != '\0' ) {
+ Debug( LDAP_DEBUG_ANY, "transform_to_db_format_one: "
+ "value '%s' not an integer\n",
+ value->bv_val );
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ /* Check it's within configured bounds */
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ if ( signed_value < definition->ti_int.lower.i ||
+ signed_value > definition->ti_int.upper.i ) {
+ Debug( LDAP_DEBUG_ANY, "transform_to_db_format_one: "
+ "value '%s' doesn't fit configured constraints\n",
+ value->bv_val );
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ } else {
+ if ( unsigned_value < definition->ti_int.lower.u ||
+ unsigned_value > definition->ti_int.upper.u ) {
+ Debug( LDAP_DEBUG_ANY, "transform_to_db_format_one: "
+ "value '%s' doesn't fit configured constraints\n",
+ value->bv_val );
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ }
+
+ db_value.bv_len = definition->ti_int.size;
+ switch ( definition->ti_int.size ) {
+ case 1: {
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ buf.be8 = (unsigned char)((char)signed_value);
+ } else {
+ buf.be8 = unsigned_value;
+ }
+ break;
+ }
+ case 2: {
+ uint16_t h16;
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ h16 = signed_value;
+ } else {
+ h16 = unsigned_value;
+ }
+ buf.be16 = htobe16( h16 );
+ break;
+ }
+ case 4: {
+ uint32_t h32;
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ h32 = signed_value;
+ } else {
+ h32 = unsigned_value;
+ }
+ buf.be32 = htobe32( h32 );
+ break;
+ }
+ case 8: {
+ uint64_t h64;
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ h64 = signed_value;
+ } else {
+ h64 = unsigned_value;
+ }
+ buf.be64 = htobe64( h64 );
+ break;
+ }
+ }
+ ber_dupbv( outval, &db_value );
+ assert( outval->bv_val );
+ break;
+ }
+
+ default:
+ assert(0);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+transform_to_db_format(
+ Operation *op,
+ transformation_info *definition,
+ BerVarray values,
+ int numvals,
+ BerVarray *out )
+{
+ struct berval *value;
+ int i, rc = LDAP_SUCCESS;
+
+ if ( numvals == 0 ) {
+ for ( value = values; value; value++, numvals++ )
+ ; /* Count them */
+ }
+
+ assert( out );
+ *out = ch_calloc( numvals + 1, sizeof(struct berval) );
+
+ for ( i = 0; i < numvals; i++ ) {
+ rc = transform_to_db_format_one(
+ op, definition, &values[i], &(*out)[i] );
+ if ( rc ) {
+ break;
+ }
+ }
+
+ if ( rc ) {
+ for ( ; i >= 0; i-- ) {
+ ch_free((*out)[i].bv_val);
+ }
+ ch_free(*out);
+ }
+
+ return rc;
+}
+
+static int
+transform_from_db_format_one(
+ Operation *op,
+ transformation_info *definition,
+ struct berval *value,
+ struct berval *outval )
+{
+ switch ( definition->type ) {
+ case DATAMORPH_ENUM: {
+ uint8_t index = value->bv_val[0];
+ struct berval *val = &definition->info.maps.from_db[index];
+
+ if ( !BER_BVISNULL( val ) ) {
+ ber_dupbv( outval, val );
+ assert( outval->bv_val );
+ } else {
+ Debug( LDAP_DEBUG_ANY, "transform_from_db_format_one: "
+ "DB value %d has no mapping!\n",
+ index );
+ /* FIXME: probably still need to return an error */
+ BER_BVZERO( outval );
+ }
+ break;
+ }
+
+ case DATAMORPH_INT: {
+ char buf[24];
+ struct berval wire_value = { .bv_val = buf };
+ union lens_t {
+ uint8_t be8;
+ uint16_t be16;
+ uint32_t be32;
+ uint64_t be64;
+ } *lens = (union lens_t *)value->bv_val;
+ uint64_t unsigned_value;
+ int64_t signed_value;
+
+ if ( value->bv_len != definition->ti_int.size ) {
+ Debug( LDAP_DEBUG_ANY, "transform_from_db_format_one(%s): "
+ "unexpected DB value of length %lu when configured "
+ "for %u!\n",
+ definition->attr->ad_cname.bv_val, value->bv_len,
+ definition->ti_int.size );
+ /* FIXME: probably still need to return an error */
+ BER_BVZERO( outval );
+ break;
+ }
+
+ assert( definition->ti_int.size == 1 ||
+ definition->ti_int.size == 2 ||
+ definition->ti_int.size == 4 ||
+ definition->ti_int.size == 8 );
+
+ switch ( definition->ti_int.size ) {
+ case 1: {
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ signed_value = (int8_t)lens->be8;
+ } else {
+ unsigned_value = (uint8_t)lens->be8;
+ }
+ break;
+ }
+ case 2: {
+ uint16_t h16 = be16toh( lens->be16 );
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ signed_value = (int16_t)h16;
+ } else {
+ unsigned_value = (uint16_t)h16;
+ }
+ break;
+ }
+ case 4: {
+ uint32_t h32 = be32toh( lens->be32 );
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ signed_value = (int32_t)h32;
+ } else {
+ unsigned_value = (uint32_t)h32;
+ }
+ break;
+ }
+ case 8: {
+ uint64_t h64 = be64toh( lens->be64 );
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ signed_value = (int64_t)h64;
+ } else {
+ unsigned_value = (uint64_t)h64;
+ }
+ break;
+ }
+ }
+ if ( definition->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ wire_value.bv_len = sprintf( buf, "%" PRId64, signed_value );
+ } else {
+ wire_value.bv_len = sprintf( buf, "%" PRIu64, unsigned_value );
+ }
+ ber_dupbv( outval, &wire_value );
+ assert( outval->bv_val );
+ break;
+ }
+
+ default:
+ assert(0);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+transform_from_db_format(
+ Operation *op,
+ transformation_info *definition,
+ BerVarray values,
+ int numvals,
+ BerVarray *out )
+{
+ struct berval *value;
+ int i, rc = LDAP_SUCCESS;
+
+ if ( numvals == 0 ) {
+ for ( value = values; value; value++, numvals++ )
+ ; /* Count them */
+ }
+
+ assert( out );
+ *out = ch_calloc( numvals + 1, sizeof(struct berval) );
+
+ for ( i = 0; i < numvals; i++ ) {
+ struct berval bv;
+ rc = transform_from_db_format_one( op, definition, &values[i], &bv );
+ if ( !BER_BVISNULL( &bv ) ) {
+ ber_bvarray_add( out, &bv );
+ }
+ if ( rc ) {
+ break;
+ }
+ }
+
+ if ( rc ) {
+ for ( ; i >= 0; i-- ) {
+ ch_free( (*out)[i].bv_val );
+ }
+ ch_free( *out );
+ }
+
+ return rc;
+}
+
+static int
+datamorph_filter( Operation *op, datamorph_info *ov, Filter *f )
+{
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_PRESENT:
+ /* The matching rules are not in place,
+ * so the filter will be ignored */
+ case LDAP_FILTER_APPROX:
+ case LDAP_FILTER_SUBSTRINGS:
+ default:
+ break;
+ return LDAP_SUCCESS;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR: {
+ for ( f = f->f_and; f; f = f->f_next ) {
+ int rc = datamorph_filter( op, ov, f );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+ }
+ } break;
+
+ case LDAP_FILTER_NOT:
+ return datamorph_filter( op, ov, f->f_not );
+
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE: {
+ transformation_info *t, needle = { .attr = f->f_ava->aa_desc };
+
+ t = ldap_avl_find(
+ ov->transformations, &needle, transformation_info_cmp );
+ if ( t ) {
+ struct berval new_val;
+ int rc = transform_to_db_format_one(
+ op, t, &f->f_ava->aa_value, &new_val );
+ ch_free( f->f_ava->aa_value.bv_val );
+
+ if ( rc != LDAP_SUCCESS ) {
+ f->f_choice = SLAPD_FILTER_COMPUTED;
+ f->f_result = SLAPD_COMPARE_UNDEFINED;
+ } else {
+ f->f_ava->aa_value = new_val;
+ }
+ }
+ } break;
+ }
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_op_add( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+ Entry *e = op->ora_e;
+ Attribute *a, *next;
+ AttributeDescription *stop = NULL;
+ int rc = LDAP_SUCCESS;
+
+ if ( !BER_BVISNULL( &e->e_nname ) && !BER_BVISEMPTY( &e->e_nname ) ) {
+ LDAPRDN rDN;
+ const char *p;
+ int i;
+
+ rc = ldap_bv2rdn_x( &e->e_nname, &rDN, (char **)&p, LDAP_DN_FORMAT_LDAP,
+ op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "datamorph_op_add: "
+ "can't parse rdn: dn=%s\n",
+ op->o_req_ndn.bv_val );
+ return SLAP_CB_CONTINUE;
+ }
+
+ for ( i = 0; rDN[i]; i++ ) {
+ transformation_info needle = {};
+
+ /* If we can't resolve the attribute, ignore it */
+ if ( slap_bv2ad( &rDN[i]->la_attr, &needle.attr, &p ) ) {
+ continue;
+ }
+
+ if ( ldap_avl_find( ov->transformations, &needle,
+ transformation_info_cmp ) ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ Debug( LDAP_DEBUG_TRACE, "datamorph_op_add: "
+ "attempted to add transformed attribute in RDN\n" );
+ break;
+ }
+ }
+
+ ldap_rdnfree_x( rDN, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_error( op, rs, rc,
+ "datamorph: trying to add transformed attribute in RDN" );
+ return rc;
+ }
+ }
+
+ for ( a = e->e_attrs; a && a->a_desc != stop; a = next ) {
+ transformation_info *t, needle = { .attr = a->a_desc };
+ BerVarray new_vals;
+
+ next = a->a_next;
+
+ t = ldap_avl_find( ov->transformations, &needle, transformation_info_cmp );
+ if ( !t ) continue;
+
+ rc = transform_to_db_format(
+ op, t, a->a_vals, a->a_numvals, &new_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ goto fail;
+ }
+
+ (void)attr_delete( &e->e_attrs, needle.attr );
+
+ rc = attr_merge( e, needle.attr, new_vals, NULL );
+ ber_bvarray_free( new_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ goto fail;
+ }
+ if ( !stop ) {
+ stop = needle.attr;
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+
+fail:
+ send_ldap_error(
+ op, rs, rc, "datamorph: trying to add values outside definitions" );
+ return rc;
+}
+
+static int
+datamorph_op_compare( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+ transformation_info *t, needle = { .attr = op->orc_ava->aa_desc };
+ int rc = SLAP_CB_CONTINUE;
+
+ t = ldap_avl_find( ov->transformations, &needle, transformation_info_cmp );
+ if ( t ) {
+ struct berval new_val;
+
+ rc = transform_to_db_format_one(
+ op, t, &op->orc_ava->aa_value, &new_val );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "datamorph_op_compare: "
+ "transformation failed for '%s', rc=%d\n",
+ op->orc_ava->aa_value.bv_val, rc );
+ rs->sr_err = rc = LDAP_COMPARE_FALSE;
+ send_ldap_result( op, rs );
+ return rc;
+ }
+ ch_free( op->orc_ava->aa_value.bv_val );
+ op->orc_ava->aa_value = new_val;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+datamorph_op_mod( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+ Modifications *mod;
+ int rc = SLAP_CB_CONTINUE;
+
+ for ( mod = op->orm_modlist; mod; mod = mod->sml_next ) {
+ transformation_info *t, needle = { .attr = mod->sml_desc };
+ BerVarray new_vals = NULL;
+
+ if ( mod->sml_numvals == 0 ) continue; /* Nothing to transform */
+
+ t = ldap_avl_find( ov->transformations, &needle, transformation_info_cmp );
+ if ( !t ) continue;
+
+ assert( !mod->sml_nvalues );
+ rc = transform_to_db_format(
+ op, t, mod->sml_values, mod->sml_numvals, &new_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ goto fail;
+ }
+ ber_bvarray_free( mod->sml_values );
+ mod->sml_values = new_vals;
+ }
+
+ return SLAP_CB_CONTINUE;
+
+fail:
+ Debug( LDAP_DEBUG_TRACE, "datamorph_op_mod: "
+ "dn=%s failed rc=%d\n",
+ op->o_req_ndn.bv_val, rc );
+ send_ldap_error( op, rs, rc,
+ "datamorph: trying to operate on values outside definitions" );
+ return rc;
+}
+
+static int
+datamorph_op_modrdn( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+ LDAPRDN rDN;
+ const char *p;
+ int i, rc;
+
+ rc = ldap_bv2rdn_x( &op->orr_nnewrdn, &rDN, (char **)&p,
+ LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "datamorph_op_modrdn: "
+ "can't parse rdn for dn=%s\n",
+ op->o_req_ndn.bv_val );
+ return SLAP_CB_CONTINUE;
+ }
+
+ for ( i = 0; rDN[i]; i++ ) {
+ transformation_info needle = {};
+
+ /* If we can't resolve the attribute, ignore it */
+ if ( slap_bv2ad( &rDN[i]->la_attr, &needle.attr, &p ) ) {
+ continue;
+ }
+
+ if ( ldap_avl_find(
+ ov->transformations, &needle, transformation_info_cmp ) ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ Debug( LDAP_DEBUG_TRACE, "datamorph_op_modrdn: "
+ "attempted to add transformed values in RDN\n" );
+ break;
+ }
+ }
+
+ ldap_rdnfree_x( rDN, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_error( op, rs, rc,
+ "datamorph: trying to put transformed values in RDN" );
+ return rc;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+datamorph_response( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+ Entry *e = NULL, *e_orig = rs->sr_entry;
+ AttributeDescription *stop = NULL;
+ Attribute *a, *next = NULL;
+ int rc = SLAP_CB_CONTINUE;
+
+ if ( rs->sr_type != REP_SEARCH ) {
+ return rc;
+ }
+
+ for ( a = e_orig->e_attrs; a && a->a_desc != stop; a = next ) {
+ transformation_info *t, needle = { .attr = a->a_desc };
+ BerVarray new_vals;
+
+ next = a->a_next;
+
+ t = ldap_avl_find( ov->transformations, &needle, transformation_info_cmp );
+ if ( !t ) continue;
+
+ rc = transform_from_db_format(
+ op, t, a->a_vals, a->a_numvals, &new_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ break;
+ }
+ if ( !e ) {
+ if ( rs->sr_flags & REP_ENTRY_MODIFIABLE ) {
+ e = e_orig;
+ } else {
+ e = entry_dup( e_orig );
+ }
+ }
+
+ (void)attr_delete( &e->e_attrs, needle.attr );
+
+ rc = attr_merge( e, needle.attr, new_vals, NULL );
+ ber_bvarray_free( new_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ break;
+ }
+ if ( !stop ) {
+ stop = needle.attr;
+ }
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ rc = SLAP_CB_CONTINUE;
+ if ( e && e != e_orig ) {
+ rs_replace_entry( op, rs, on, e );
+ rs->sr_flags &= ~REP_ENTRY_MASK;
+ rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED;
+ }
+ } else if ( e && e != e_orig ) {
+ entry_free( e );
+ }
+
+ return rc;
+}
+
+static int
+datamorph_op_search( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+ int rc = SLAP_CB_CONTINUE;
+
+ /*
+ * 1. check all requested attributes -> register callback if one matches
+ * 2. check filter: parse filter, traverse, for configured attributes:
+ * - presence -> do not touch
+ * - ava -> replace assertion value with db value if possible, assertion with undefined otherwise
+ * - inequality -> ???
+ * - anything else -> undefined
+ * - might just check for equality and leave the rest to syntax?
+ * 3. unparse filter
+ */
+ if ( datamorph_filter( op, ov, op->ors_filter ) ) {
+ send_ldap_error(
+ op, rs, LDAP_OTHER, "datamorph: failed to process filter" );
+ return LDAP_OTHER;
+ }
+
+ return rc;
+}
+
+static int
+datamorph_entry_release_rw( Operation *op, Entry *e, int rw )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ int rc = LDAP_SUCCESS;
+
+ if ( on->on_next ) {
+ rc = overlay_entry_release_ov( op, e, rw, on->on_next );
+ } else if ( on->on_info->oi_orig->bi_entry_release_rw ) {
+ /* FIXME: there should be a better way */
+ rc = on->on_info->oi_orig->bi_entry_release_rw( op, e, rw );
+ } else {
+ entry_free( e );
+ }
+
+ return rc;
+}
+
+static int
+datamorph_entry_get_rw(
+ Operation *op,
+ struct berval *ndn,
+ ObjectClass *oc,
+ AttributeDescription *at,
+ int rw,
+ Entry **ep )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+ Entry *e_orig, *e = NULL;
+ int rc;
+
+ if ( on->on_next ) {
+ rc = overlay_entry_get_ov( op, ndn, oc, at, rw, ep, on->on_next );
+ } else {
+ /* FIXME: there should be a better way */
+ rc = on->on_info->oi_orig->bi_entry_get_rw( op, ndn, oc, at, rw, ep );
+ }
+ e_orig = *ep;
+
+ if ( rc == LDAP_SUCCESS && e_orig ) {
+ AttributeDescription *stop = NULL;
+ Attribute *a;
+
+ for ( a = e_orig->e_attrs; a; a = a->a_next ) {
+ transformation_info *t, needle = { .attr = a->a_desc };
+ BerVarray new_vals;
+
+ t = ldap_avl_find(
+ ov->transformations, &needle, transformation_info_cmp );
+ if ( !t ) continue;
+
+ rc = transform_from_db_format(
+ op, t, a->a_vals, a->a_numvals, &new_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ goto fail;
+ }
+ if ( !e ) {
+ e = entry_dup( e_orig );
+ }
+
+ (void)attr_delete( &e->e_attrs, needle.attr );
+
+ rc = attr_merge( e, needle.attr, new_vals, NULL );
+ ber_bvarray_free( new_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ goto fail;
+ }
+ if ( !stop ) {
+ stop = needle.attr;
+ }
+ }
+ }
+ if ( e ) {
+ datamorph_entry_release_rw( op, e_orig, rw );
+ *ep = e;
+ }
+
+ return rc;
+
+fail:
+ if ( e ) {
+ entry_free( e );
+ }
+ (void)datamorph_entry_release_rw( op, *ep, rw );
+ return rc;
+}
+
+/* Schema */
+
+static int
+datamorphBlobValidate( Syntax *syntax, struct berval *in )
+{
+ /* any value allowed */
+ return LDAP_SUCCESS;
+}
+
+int
+datamorphBinarySignedOrderingMatch( int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ struct berval *asserted = assertedValue;
+ ber_len_t v_len = value->bv_len;
+ ber_len_t av_len = asserted->bv_len;
+
+ /* Ordering:
+ * 1. Negative always before non-negative
+ * 2. Shorter before longer
+ * 3. Rest ordered by memory contents (they are big-endian numbers)
+ */
+ int match = ( *value->bv_val >= 0 ) - ( *asserted->bv_val >= 0 );
+
+ if ( match == 0 ) match = (int)v_len - (int)av_len;
+
+ if ( match == 0 ) match = memcmp( value->bv_val, asserted->bv_val, v_len );
+
+ /* If used in extensible match filter, match if value < asserted */
+ if ( flags & SLAP_MR_EXT ) match = ( match >= 0 );
+
+ *matchp = match;
+ return LDAP_SUCCESS;
+}
+
+/* Index generation function: Ordered index */
+int
+datamorphUnsignedIndexer( slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ BerVarray values,
+ BerVarray *keysp,
+ void *ctx )
+{
+ int i;
+ BerVarray keys;
+
+ for ( i = 0; values[i].bv_val != NULL; i++ ) {
+ /* just count them */
+ }
+
+ /* we should have at least one value at this point */
+ assert( i > 0 );
+
+ keys = slap_sl_malloc( sizeof(struct berval) * ( i + 1 ), ctx );
+
+ for ( i = 0; values[i].bv_val != NULL; i++ ) {
+ ber_dupbv_x( &keys[i], &values[i], ctx );
+ }
+
+ BER_BVZERO( &keys[i] );
+
+ *keysp = keys;
+
+ return LDAP_SUCCESS;
+}
+
+/* Index generation function: Ordered index */
+int
+datamorphUnsignedFilter(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ void *assertedValue,
+ BerVarray *keysp,
+ void *ctx )
+{
+ BerVarray keys;
+ BerValue *value = assertedValue;
+
+ keys = slap_sl_malloc( sizeof(struct berval) * 2, ctx );
+ ber_dupbv_x( &keys[0], value, ctx );
+
+ BER_BVZERO( &keys[1] );
+
+ *keysp = keys;
+
+ return LDAP_SUCCESS;
+}
+
+/* Index generation function: Ordered index */
+int
+datamorphSignedIndexer(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ BerVarray values,
+ BerVarray *keysp,
+ void *ctx )
+{
+ int i;
+ BerVarray keys;
+
+ for ( i = 0; values[i].bv_val != NULL; i++ ) {
+ /* just count them */
+ }
+
+ /* we should have at least one value at this point */
+ assert( i > 0 );
+
+ keys = slap_sl_malloc( sizeof(struct berval) * ( i + 1 ), ctx );
+
+ for ( i = 0; values[i].bv_val != NULL; i++ ) {
+ keys[i].bv_len = values[i].bv_len + 1;
+ keys[i].bv_val = slap_sl_malloc( keys[i].bv_len, ctx );
+
+ /* if positive (highest bit is not set), note that in the first byte */
+ *keys[i].bv_val = ~( *values[i].bv_val & 0x80 );
+ AC_MEMCPY( keys[i].bv_val + 1, values[i].bv_val, values[i].bv_len );
+ }
+
+ BER_BVZERO( &keys[i] );
+
+ *keysp = keys;
+
+ return LDAP_SUCCESS;
+}
+
+/* Index generation function: Ordered index */
+int
+datamorphSignedFilter(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ void *assertedValue,
+ BerVarray *keysp,
+ void *ctx )
+{
+ BerVarray keys;
+ BerValue *value = assertedValue;
+
+ keys = slap_sl_malloc( sizeof(struct berval) * 2, ctx );
+
+ keys[0].bv_len = value->bv_len + 1;
+ keys[0].bv_val = slap_sl_malloc( keys[0].bv_len, ctx );
+
+ /* if positive (highest bit is not set), note that in the first byte */
+ *keys[0].bv_val = ~( *value->bv_val & 0x80 );
+ AC_MEMCPY( keys[0].bv_val + 1, value->bv_val, value->bv_len );
+
+ BER_BVZERO( &keys[1] );
+
+ *keysp = keys;
+
+ return LDAP_SUCCESS;
+}
+
+#define DATAMORPH_ARC "1.3.6.1.4.1.4203.666.11.12"
+
+#define DATAMORPH_SYNTAXES DATAMORPH_ARC ".1"
+#define DATAMORPH_SYNTAX_BASE DATAMORPH_SYNTAXES ".1"
+#define DATAMORPH_SYNTAX_ENUM DATAMORPH_SYNTAXES ".2"
+#define DATAMORPH_SYNTAX_INT DATAMORPH_SYNTAXES ".3"
+#define DATAMORPH_SYNTAX_SIGNED_INT DATAMORPH_SYNTAXES ".4"
+
+#define DATAMORPH_MATCHES DATAMORPH_ARC ".2"
+#define DATAMORPH_MATCH_EQUALITY DATAMORPH_MATCHES ".1"
+#define DATAMORPH_MATCH_SIGNED_EQUALITY DATAMORPH_MATCHES ".2"
+#define DATAMORPH_MATCH_ORDERING DATAMORPH_MATCHES ".3"
+#define DATAMORPH_MATCH_SIGNED_ORDERING DATAMORPH_MATCHES ".4"
+
+static char *datamorph_sups[] = {
+ DATAMORPH_SYNTAX_BASE,
+ NULL
+};
+
+static char *datamorphSyntaxes[] = {
+ DATAMORPH_SYNTAX_SIGNED_INT,
+ DATAMORPH_SYNTAX_ENUM,
+ DATAMORPH_SYNTAX_INT,
+
+ NULL
+};
+
+static slap_syntax_defs_rec datamorph_syntax_defs[] = {
+ { "( " DATAMORPH_SYNTAX_BASE " DESC 'Fixed size value' )",
+ 0, NULL, NULL, NULL
+ },
+ { "( " DATAMORPH_SYNTAX_ENUM " DESC 'Enumerated value' )",
+ 0, datamorph_sups, datamorphBlobValidate, NULL
+ },
+ { "( " DATAMORPH_SYNTAX_INT " DESC 'Fixed-size integer' )",
+ 0, datamorph_sups, datamorphBlobValidate, NULL
+ },
+ { "( " DATAMORPH_SYNTAX_SIGNED_INT " DESC 'Fixed-size signed integer' )",
+ 0, datamorph_sups, datamorphBlobValidate, NULL
+ },
+
+ { NULL, 0, NULL, NULL, NULL }
+};
+
+static Syntax *datamorph_base_syntax;
+
+static slap_mrule_defs_rec datamorph_mrule_defs[] = {
+ { "( " DATAMORPH_MATCH_EQUALITY
+ " NAME 'fixedSizeIntegerMatch'"
+ " SYNTAX " DATAMORPH_SYNTAX_INT " )",
+ SLAP_MR_EQUALITY|SLAP_MR_EXT|SLAP_MR_ORDERED_INDEX,
+ datamorphSyntaxes + 1,
+ NULL, NULL, octetStringOrderingMatch,
+ datamorphUnsignedIndexer, datamorphUnsignedFilter,
+ NULL
+ },
+
+ { "( " DATAMORPH_MATCH_SIGNED_EQUALITY
+ " NAME 'fixedSizeSignedIntegerMatch'"
+ " SYNTAX " DATAMORPH_SYNTAX_SIGNED_INT " )",
+ SLAP_MR_EQUALITY|SLAP_MR_EXT|SLAP_MR_ORDERED_INDEX,
+ NULL,
+ NULL, NULL, datamorphBinarySignedOrderingMatch,
+ datamorphSignedIndexer, datamorphSignedFilter,
+ NULL
+ },
+
+ { "( " DATAMORPH_MATCH_ORDERING
+ " NAME 'fixedSizeIntegerOrderingMatch'"
+ " SYNTAX " DATAMORPH_SYNTAX_INT " )",
+ SLAP_MR_ORDERING|SLAP_MR_EXT|SLAP_MR_ORDERED_INDEX,
+ datamorphSyntaxes + 1,
+ NULL, NULL, octetStringOrderingMatch,
+ datamorphUnsignedIndexer, datamorphUnsignedFilter,
+ "octetStringMatch" },
+
+ { "( " DATAMORPH_MATCH_SIGNED_ORDERING
+ " NAME 'fixedSizeSignedIntegerOrderingMatch'"
+ " SYNTAX " DATAMORPH_SYNTAX_SIGNED_INT " )",
+ SLAP_MR_ORDERING|SLAP_MR_EXT|SLAP_MR_ORDERED_INDEX,
+ NULL,
+ NULL, NULL, datamorphBinarySignedOrderingMatch,
+ datamorphSignedIndexer, datamorphSignedFilter,
+ "octetStringMatch" },
+
+ { NULL, SLAP_MR_NONE, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+/* Configuration */
+
+static ConfigLDAPadd datamorph_ldadd_enum;
+static ConfigLDAPadd datamorph_ldadd_interval;
+static ConfigLDAPadd datamorph_ldadd_mapping;
+
+static ConfigDriver datamorph_set_attribute;
+static ConfigDriver datamorph_set_size;
+static ConfigDriver datamorph_set_signed;
+static ConfigDriver datamorph_set_bounds;
+static ConfigDriver datamorph_set_index;
+static ConfigDriver datamorph_set_value;
+static ConfigDriver datamorph_add_mapping;
+static ConfigDriver datamorph_add_transformation;
+
+static ConfigCfAdd datamorph_cfadd;
+
+enum {
+ DATAMORPH_INT_SIZE = 1,
+ DATAMORPH_INT_SIGNED,
+ DATAMORPH_INT_LOWER,
+ DATAMORPH_INT_UPPER,
+
+ DATAMORPH_INT_LAST,
+};
+
+static ConfigTable datamorph_cfg[] = {
+ { "datamorph_attribute", "attr", 2, 2, 0,
+ ARG_STRING|ARG_QUOTE|ARG_MAGIC,
+ datamorph_set_attribute,
+ "( OLcfgCtAt:7.1 NAME 'olcDatamorphAttribute' "
+ "DESC 'Attribute to transform' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "datamorph_size", "<1|2|4|8>", 2, 2, 0,
+ ARG_INT|ARG_MAGIC|DATAMORPH_INT_SIZE,
+ datamorph_set_size,
+ "( OLcfgCtAt:7.2 NAME 'olcDatamorphIntegerBytes' "
+ "DESC 'Integer size in bytes' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "datamorph_signed", "TRUE|FALSE", 2, 2, 0,
+ ARG_ON_OFF|ARG_MAGIC|DATAMORPH_INT_SIGNED,
+ datamorph_set_signed,
+ "( OLcfgCtAt:7.3 NAME 'olcDatamorphIntegerSigned' "
+ "DESC 'Whether integers maintain sign' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "datamorph_lower_bound", "int", 2, 2, 0,
+ ARG_BERVAL|ARG_MAGIC|DATAMORPH_INT_LOWER,
+ datamorph_set_bounds,
+ "( OLcfgCtAt:7.4 NAME 'olcDatamorphIntegerLowerBound' "
+ "DESC 'Lowest valid value for the attribute' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "datamorph_upper_bound", "int", 2, 2, 0,
+ ARG_BERVAL|ARG_MAGIC|DATAMORPH_INT_UPPER,
+ datamorph_set_bounds,
+ "( OLcfgCtAt:7.5 NAME 'olcDatamorphIntegerUpperBound' "
+ "DESC 'Highest valid value for the attribute' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+
+ /* These have no equivalent in slapd.conf */
+ { "", NULL, 2, 2, 0,
+ ARG_INT|ARG_MAGIC,
+ datamorph_set_index,
+ "( OLcfgCtAt:7.6 NAME 'olcDatamorphIndex' "
+ "DESC 'Internal DB value' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "", NULL, 2, 2, 0,
+ ARG_BERVAL|ARG_QUOTE|ARG_MAGIC,
+ datamorph_set_value,
+ "( OLcfgCtAt:7.7 NAME 'olcDatamorphValue' "
+ "DESC 'Wire value' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+
+ /* slapd.conf alternative for the two above */
+ { "datamorph_value", "int> <name", 3, 3, 0,
+ ARG_QUOTE|ARG_MAGIC,
+ datamorph_add_mapping,
+ NULL, NULL, NULL
+ },
+
+ /* slapd.conf alternative for objectclasses below */
+ { "datamorph", "enum|int> <attr", 3, 3, 0,
+ ARG_QUOTE|ARG_MAGIC,
+ datamorph_add_transformation,
+ NULL, NULL, NULL
+ },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs datamorph_ocs[] = {
+ { "( OLcfgCtOc:7.1 "
+ "NAME 'olcDatamorphConfig' "
+ "DESC 'Datamorph overlay configuration' "
+ "SUP olcOverlayConfig )",
+ Cft_Overlay, datamorph_cfg, NULL, datamorph_cfadd },
+ { "( OLcfgCtOc:7.2 "
+ "NAME 'olcTransformation' "
+ "DESC 'Transformation configuration' "
+ "MUST ( olcDatamorphAttribute ) "
+ "SUP top "
+ "ABSTRACT )",
+ Cft_Misc, datamorph_cfg, NULL },
+ { "( OLcfgCtOc:7.3 "
+ "NAME 'olcDatamorphEnum' "
+ "DESC 'Configuration for an enumerated attribute' "
+ "SUP olcTransformation "
+ "STRUCTURAL )",
+ Cft_Misc, datamorph_cfg, datamorph_ldadd_enum },
+ { "( OLcfgCtOc:7.4 "
+ "NAME 'olcDatamorphInteger' "
+ "DESC 'Configuration for a compact integer attribute' "
+ "MUST ( olcDatamorphIntegerBytes ) "
+ "MAY ( olcDatamorphIntegerLowerBound $ "
+ "olcDatamorphIntegerUpperBound $ "
+ "olcDatamorphIntegerSigned "
+ ") "
+ "SUP olcTransformation "
+ "STRUCTURAL )",
+ Cft_Misc, datamorph_cfg, datamorph_ldadd_interval },
+ { "( OLcfgCtOc:7.5 "
+ "NAME 'olcDatamorphEnumValue' "
+ "DESC 'Configuration for an enumerated attribute' "
+ "MUST ( olcDatamorphIndex $ "
+ "olcDatamorphValue "
+ ") "
+ "STRUCTURAL )",
+ Cft_Misc, datamorph_cfg, datamorph_ldadd_mapping },
+
+ { NULL, 0, NULL }
+};
+
+static void
+datamorph_mapping_free( void *arg )
+{
+ datamorph_enum_mapping *mapping = arg;
+
+ ch_free( mapping->wire_value.bv_val );
+ ch_free( mapping );
+}
+
+static void
+datamorph_info_free( void *arg )
+{
+ transformation_info *info = arg;
+
+ if ( info->type == DATAMORPH_ENUM ) {
+ ldap_avl_free( info->ti_enum.to_db, datamorph_mapping_free );
+ }
+ ch_free( info );
+}
+
+static int
+datamorph_set_attribute( ConfigArgs *ca )
+{
+ transformation_info needle = {}, *info = ca->ca_private;
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ char *s = ca->value_string;
+ const char *text;
+ int rc = LDAP_SUCCESS;
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ ca->value_string = info->attr->ad_cname.bv_val;
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ info = ldap_avl_delete( &ov->transformations, info,
+ transformation_info_cmp );
+ assert( info );
+
+ info->attr = NULL;
+ return LDAP_SUCCESS;
+ }
+
+ if ( *s == '{' ) {
+ s = strchr( s, '}' );
+ if ( !s ) {
+ rc = LDAP_UNDEFINED_TYPE;
+ goto done;
+ }
+ s += 1;
+ }
+
+ rc = slap_str2ad( s, &info->attr, &text );
+ ch_free( ca->value_string );
+ if ( rc ) {
+ goto done;
+ }
+
+ /* The type has to be set appropriately */
+ if ( !info->attr->ad_type->sat_syntax->ssyn_sups ||
+ info->attr->ad_type->sat_syntax->ssyn_sups[0] !=
+ datamorph_base_syntax ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "improper syntax for attribute %s",
+ info->attr->ad_cname.bv_val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+
+ needle.attr = info->attr;
+ if ( ldap_avl_find( ov->transformations, &needle, transformation_info_cmp ) ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+
+done:
+ if ( rc ) {
+ ca->reply.err = rc;
+ }
+ return rc;
+}
+
+static int
+datamorph_set_size( ConfigArgs *ca )
+{
+ transformation_info *info = ca->ca_private;
+
+ if ( !info ) {
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ info = ov->wip_transformation;
+ assert( ca->op == SLAP_CONFIG_ADD );
+ }
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ ca->value_int = info->ti_int.size;
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ info->ti_int.size = 0;
+ return LDAP_SUCCESS;
+ }
+
+ if ( ca->value_int != 1 &&
+ ca->value_int != 2 &&
+ ca->value_int != 4 &&
+ ca->value_int != 8 ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg), "invalid size %d",
+ ca->value_int );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+ info->ti_int.size = ca->value_int;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_set_signed( ConfigArgs *ca )
+{
+ transformation_info *info = ca->ca_private;
+
+ if ( !info ) {
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ info = ov->wip_transformation;
+ assert( ca->op == SLAP_CONFIG_ADD );
+ }
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ ca->value_int = info->ti_int.flags & DATAMORPH_FLAG_SIGNED;
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ info->ti_int.flags &= ~DATAMORPH_FLAG_SIGNED;
+ return LDAP_SUCCESS;
+ }
+
+ info->ti_int.flags &= ~DATAMORPH_FLAG_SIGNED;
+ if ( ca->value_int ) {
+ info->ti_int.flags |= DATAMORPH_FLAG_SIGNED;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_set_bounds( ConfigArgs *ca )
+{
+ transformation_info *info = ca->ca_private;
+ datamorph_interval_bound *bound;
+ uint64_t unsigned_bound;
+ int64_t signed_bound;
+ char *ptr = ca->value_bv.bv_val + ca->value_bv.bv_len;
+ int flag;
+
+ if ( !info ) {
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ info = ov->wip_transformation;
+ assert( ca->op == SLAP_CONFIG_ADD );
+ }
+
+ switch ( ca->type ) {
+ case DATAMORPH_INT_LOWER:
+ bound = &info->ti_int.lower;
+ flag = DATAMORPH_FLAG_LOWER;
+ break;
+ case DATAMORPH_INT_UPPER:
+ bound = &info->ti_int.upper;
+ flag = DATAMORPH_FLAG_UPPER;
+ break;
+ default:
+ assert(0);
+ }
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ char buf[24];
+ struct berval bv = { .bv_val = buf };
+
+ if ( !(info->ti_int.flags & flag) ) {
+ /* Bound not set, do not emit */
+ return LDAP_SUCCESS;
+ }
+ if ( info->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ bv.bv_len = sprintf( buf, "%" PRId64, bound->i );
+ } else {
+ bv.bv_len = sprintf( buf, "%" PRIu64, bound->u );
+ }
+ ber_dupbv_x( &ca->value_bv, &bv, ca->ca_op->o_tmpmemctx );
+
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ info->ti_int.flags &= ~flag;
+ if ( info->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ bound->i = (flag == DATAMORPH_FLAG_LOWER) ? INT64_MIN : INT64_MAX;
+ } else {
+ bound->u = (flag == DATAMORPH_FLAG_LOWER) ? 0 : UINT64_MAX;
+ }
+ return LDAP_SUCCESS;
+ }
+
+ /* FIXME: if attributes in the Add operation come in the wrong order
+ * (signed=true after the bound definition), we can't check the interval
+ * sanity. */
+ /*
+ if ( info->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ signed_bound = strtoll( ca->value_bv.bv_val, &ptr, 10 );
+ } else {
+ unsigned_bound = strtoull( ca->value_bv.bv_val, &ptr, 10 );
+ }
+ */
+ /* Also, no idea what happens in the case of big-endian, hopefully,
+ * it behaves the same */
+ unsigned_bound = strtoull( ca->value_bv.bv_val, &ptr, 10 );
+ signed_bound = (int64_t)unsigned_bound;
+
+ if ( *ca->value_bv.bv_val == '\0' || *ptr != '\0' ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "failed to parse '%s' as integer",
+ ca->value_bv.bv_val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+ ch_free( ca->value_bv.bv_val );
+
+ info->ti_int.flags |= flag;
+ switch ( info->ti_int.size ) {
+ case 1:
+ if ( info->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ /* See FIXME above
+ if ( signed_bound < INT8_MIN || signed_bound > INT8_MAX ) {
+ goto fail;
+ }
+ */
+ } else {
+ /* See FIXME above
+ if ( unsigned_bound > UINT8_MAX ) {
+ goto fail;
+ }
+ */
+ }
+ break;
+ case 2:
+ if ( info->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ /* See FIXME above
+ if ( signed_bound < INT16_MIN || signed_bound > INT16_MAX ) {
+ goto fail;
+ }
+ */
+ } else {
+ /* See FIXME above
+ if ( unsigned_bound > UINT16_MAX ) {
+ goto fail;
+ }
+ */
+ }
+ break;
+ case 4:
+ if ( info->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ /* See FIXME above
+ if ( signed_bound < INT32_MIN || signed_bound > INT32_MAX ) {
+ goto fail;
+ }
+ */
+ } else {
+ /* See FIXME above
+ if ( unsigned_bound > UINT32_MAX ) {
+ goto fail;
+ }
+ */
+ }
+ break;
+ case 8:
+ break;
+ default:
+ /* Should only happen in these two cases:
+ * 1. datamorph_size not yet encountered for this one (when
+ * processing slapd.conf)
+ * 2. When someone runs a fun modification on the config entry
+ * messing with more attributes at once
+ *
+ * The error message is expected to be helpful only for the former,
+ * so use the slapd.conf name.
+ */
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "datamorph_size has to be set first!" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+ if ( info->ti_int.flags & DATAMORPH_FLAG_SIGNED ) {
+ bound->i = signed_bound;
+ } else {
+ bound->u = unsigned_bound;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_set_value( ConfigArgs *ca )
+{
+ datamorph_enum_mapping *mapping = ca->ca_private;
+ char *s = ca->value_bv.bv_val;
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ /* We generate the value as part of the RDN, don't add anything */
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ ch_free( mapping->wire_value.bv_val );
+ BER_BVZERO( &mapping->wire_value );
+ /* TODO: remove from info->ti_enum.to_db? */
+ return LDAP_SUCCESS;
+ }
+
+ /* As long as this attribute can be in the RDN,
+ * we have to expect the '{n}' prefix */
+ if ( *s == '{' ) {
+ ber_len_t len;
+ s = memchr( s, '}', ca->value_bv.bv_len );
+ if ( !s ) {
+ ca->reply.err = LDAP_UNDEFINED_TYPE;
+ return ca->reply.err;
+ }
+ s += 1;
+
+ len = ca->value_bv.bv_len - ( s - ca->value_bv.bv_val );
+ ber_str2bv( s, len, 1, &mapping->wire_value );
+ ch_free( ca->value_bv.bv_val );
+ } else {
+ mapping->wire_value = ca->value_bv;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_set_index( ConfigArgs *ca )
+{
+ datamorph_enum_mapping *mapping = ca->ca_private;
+ struct berval *from_db = mapping->transformation->ti_enum.from_db;
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ ca->value_int = mapping->db_value;
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ BER_BVZERO( &from_db[mapping->db_value] );
+ return LDAP_SUCCESS;
+ }
+
+ if ( ca->value_int < 0 || ca->value_int >= 256 ) {
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ } else if ( !BER_BVISNULL( &from_db[ca->value_int] ) ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg), "duplicate index %d",
+ ca->value_int );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+ mapping->db_value = ca->value_int;
+ from_db[ca->value_int] = mapping->wire_value;
+
+ return LDAP_SUCCESS;
+}
+
+/* Called when processing slapd.conf only,
+ * cn=config uses the objectclass to decide which type we're dealing with.
+ */
+static int
+datamorph_add_transformation( ConfigArgs *ca )
+{
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ transformation_info *info;
+
+ if ( ov->wip_transformation ) {
+ /* We checked everything as were processing the lines */
+ int rc = ldap_avl_insert( &ov->transformations, ov->wip_transformation,
+ transformation_info_cmp, ldap_avl_dup_error );
+ assert( rc == LDAP_SUCCESS );
+ }
+
+ info = ch_calloc( 1, sizeof(transformation_info) );
+ ov->wip_transformation = ca->ca_private = info;
+
+ if ( !strcasecmp( ca->argv[1], "enum" ) ) {
+ info->type = DATAMORPH_ENUM;
+ } else if ( !strcasecmp( ca->argv[1], "int" ) ) {
+ info->type = DATAMORPH_INT;
+ } else {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "unknown transformation type '%s'", ca->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+
+ ca->value_string = strdup( ca->argv[2] );
+
+ return datamorph_set_attribute( ca );
+}
+
+static int
+datamorph_add_mapping( ConfigArgs *ca )
+{
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ transformation_info *info = ov->wip_transformation;
+ datamorph_enum_mapping *mapping;
+ int rc = LDAP_CONSTRAINT_VIOLATION;
+
+ if ( !info ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg), "no attribute configured" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ goto done;
+ }
+
+ mapping = ch_calloc( 1, sizeof(datamorph_enum_mapping) );
+ mapping->transformation = info;
+ ca->ca_private = mapping;
+
+ ber_str2bv( ca->argv[2], 0, 1, &ca->value_bv );
+ rc = datamorph_set_value( ca );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ rc = lutil_atoix( &ca->value_int, ca->argv[1], 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg), "invalid integer %s",
+ ca->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", ca->log, ca->cr_msg );
+ goto done;
+ }
+
+ rc = datamorph_set_index( ca );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+done:
+ if ( rc == LDAP_SUCCESS ) {
+ rc = ldap_avl_insert( &info->ti_enum.to_db, mapping,
+ transformation_mapping_cmp, ldap_avl_dup_error );
+ }
+ if ( rc ) {
+ ca->reply.err = rc;
+ }
+
+ return rc;
+}
+
+static int
+datamorph_ldadd_info_cleanup( ConfigArgs *ca )
+{
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ transformation_info *info = ca->ca_private;
+
+ if ( ca->reply.err != LDAP_SUCCESS ) {
+ /* Not reached since cleanup is only called on success */
+fail:
+ ch_free( info );
+ return LDAP_SUCCESS;
+ }
+
+ if ( ldap_avl_insert( &ov->transformations, info, transformation_info_cmp,
+ ldap_avl_dup_error ) ) {
+ goto fail;
+ }
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_ldadd_transformation(
+ CfEntryInfo *cei,
+ Entry *e,
+ ConfigArgs *ca,
+ datamorph_type type )
+{
+ transformation_info *info;
+
+ if ( cei->ce_type != Cft_Overlay || !cei->ce_bi ||
+ cei->ce_bi->bi_cf_ocs != datamorph_ocs )
+ return LDAP_CONSTRAINT_VIOLATION;
+
+ info = ch_calloc( 1, sizeof(transformation_info) );
+ info->type = type;
+
+ ca->bi = cei->ce_bi;
+ ca->ca_private = info;
+ config_push_cleanup( ca, datamorph_ldadd_info_cleanup );
+ /* config_push_cleanup is only run in the case of online config but we use it to
+ * enable the new config when done with the entry */
+ ca->lineno = 0;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_ldadd_enum( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
+{
+ return datamorph_ldadd_transformation( cei, e, ca, DATAMORPH_ENUM );
+}
+
+static int
+datamorph_ldadd_interval( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
+{
+ return datamorph_ldadd_transformation( cei, e, ca, DATAMORPH_INT );
+}
+
+static int
+datamorph_ldadd_mapping_cleanup( ConfigArgs *ca )
+{
+ datamorph_enum_mapping *mapping = ca->ca_private;
+ transformation_info *info = mapping->transformation;
+
+ if ( ca->reply.err != LDAP_SUCCESS ) {
+ /* Not reached since cleanup is only called on success */
+fail:
+ datamorph_mapping_free( mapping );
+ return LDAP_SUCCESS;
+ }
+
+ if ( ldap_avl_insert( &info->ti_enum.to_db, mapping, transformation_mapping_cmp,
+ ldap_avl_dup_error ) ) {
+ goto fail;
+ }
+ info->ti_enum.from_db[mapping->db_value] = mapping->wire_value;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_ldadd_mapping( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
+{
+ transformation_info *info;
+ datamorph_enum_mapping *mapping;
+ CfEntryInfo *parent = cei->ce_parent;
+
+ if ( cei->ce_type != Cft_Misc || !parent || !parent->ce_bi ||
+ parent->ce_bi->bi_cf_ocs != datamorph_ocs )
+ return LDAP_CONSTRAINT_VIOLATION;
+
+ info = cei->ce_private;
+
+ mapping = ch_calloc( 1, sizeof(datamorph_enum_mapping) );
+ mapping->transformation = info;
+
+ ca->ca_private = mapping;
+ config_push_cleanup( ca, datamorph_ldadd_mapping_cleanup );
+ /* config_push_cleanup is only run in the case of online config but we use it to
+ * enable the new config when done with the entry */
+ ca->lineno = 0;
+
+ return LDAP_SUCCESS;
+}
+
+struct datamorph_cfadd_args {
+ Operation *op;
+ SlapReply *rs;
+ Entry *p;
+ ConfigArgs *ca;
+ int index;
+};
+
+static int
+datamorph_config_build_enum( void *item, void *arg )
+{
+ datamorph_enum_mapping *mapping = item;
+ struct datamorph_cfadd_args *args = arg;
+ struct berval rdn;
+ Entry *e;
+ char *p;
+ ber_len_t index;
+
+ rdn.bv_len = snprintf( args->ca->cr_msg, sizeof(args->ca->cr_msg),
+ "olcDatamorphValue={%d}", args->index++ );
+ rdn.bv_val = args->ca->cr_msg;
+ p = rdn.bv_val + rdn.bv_len;
+
+ rdn.bv_len += mapping->wire_value.bv_len;
+ for ( index = 0; index < mapping->wire_value.bv_len; index++ ) {
+ if ( RDN_NEEDSESCAPE(mapping->wire_value.bv_val[index]) ) {
+ rdn.bv_len++;
+ *p++ = '\\';
+ }
+ *p++ = mapping->wire_value.bv_val[index];
+ }
+ *p = '\0';
+
+ args->ca->ca_private = mapping;
+ args->ca->ca_op = args->op;
+ e = config_build_entry( args->op, args->rs, args->p->e_private, args->ca,
+ &rdn, &datamorph_ocs[4], NULL );
+ assert( e );
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_config_build_attr( void *item, void *arg )
+{
+ transformation_info *info = item;
+ struct datamorph_cfadd_args *args = arg;
+ struct berval rdn;
+ ConfigOCs *oc;
+ Entry *e;
+
+ rdn.bv_len = snprintf( args->ca->cr_msg, sizeof(args->ca->cr_msg),
+ "olcDatamorphAttribute={%d}%s", args->index++,
+ info->attr->ad_cname.bv_val );
+ rdn.bv_val = args->ca->cr_msg;
+
+ switch ( info->type ) {
+ case DATAMORPH_ENUM:
+ oc = &datamorph_ocs[2];
+ break;
+ case DATAMORPH_INT:
+ oc = &datamorph_ocs[3];
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ args->ca->ca_private = info;
+ args->ca->ca_op = args->op;
+ e = config_build_entry(
+ args->op, args->rs, args->p->e_private, args->ca, &rdn, oc, NULL );
+ assert( e );
+
+ if ( info->type == DATAMORPH_ENUM ) {
+ struct datamorph_cfadd_args new_args = *args;
+ new_args.p = e;
+ new_args.index = 0;
+
+ return ldap_avl_apply( info->ti_enum.to_db, datamorph_config_build_enum,
+ &new_args, 1, AVL_PREORDER );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
+{
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ datamorph_info *ov = on->on_bi.bi_private;
+ struct datamorph_cfadd_args args = {
+ .op = op,
+ .rs = rs,
+ .p = p,
+ .ca = ca,
+ .index = 0,
+ };
+
+ if ( ov->wip_transformation ) {
+ /* There is one last item that is unfinished */
+ int rc = ldap_avl_insert( &ov->transformations, ov->wip_transformation,
+ transformation_info_cmp, ldap_avl_dup_error );
+ assert( rc == LDAP_SUCCESS );
+ }
+
+ return ldap_avl_apply( ov->transformations, &datamorph_config_build_attr, &args,
+ 1, AVL_PREORDER );
+}
+
+static slap_overinst datamorph;
+
+static int
+datamorph_db_init( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ datamorph_info *ov;
+
+ /* TODO: can this be global? */
+ if ( SLAP_ISGLOBALOVERLAY(be) ) {
+ Debug( LDAP_DEBUG_ANY, "datamorph overlay must be instantiated "
+ "within a database.\n" );
+ return 1;
+ }
+
+ ov = ch_calloc( 1, sizeof(datamorph_info) );
+ on->on_bi.bi_private = ov;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+datamorph_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ datamorph_info *ov = on->on_bi.bi_private;
+
+ if ( ov ) {
+ ldap_avl_free( ov->transformations, datamorph_info_free );
+ }
+ ch_free( ov );
+
+ return LDAP_SUCCESS;
+}
+
+int
+datamorph_initialize()
+{
+ int rc, i;
+
+ datamorph.on_bi.bi_type = "datamorph";
+ datamorph.on_bi.bi_db_init = datamorph_db_init;
+ datamorph.on_bi.bi_db_destroy = datamorph_db_destroy;
+
+ datamorph.on_bi.bi_op_add = datamorph_op_add;
+ datamorph.on_bi.bi_op_compare = datamorph_op_compare;
+ datamorph.on_bi.bi_op_modify = datamorph_op_mod;
+ datamorph.on_bi.bi_op_modrdn = datamorph_op_modrdn;
+ datamorph.on_bi.bi_op_search = datamorph_op_search;
+ datamorph.on_response = datamorph_response;
+
+ datamorph.on_bi.bi_entry_release_rw = datamorph_entry_release_rw;
+ datamorph.on_bi.bi_entry_get_rw = datamorph_entry_get_rw;
+
+ datamorph.on_bi.bi_cf_ocs = datamorph_ocs;
+
+ for ( i = 0; datamorph_syntax_defs[i].sd_desc != NULL; i++ ) {
+ rc = register_syntax( &datamorph_syntax_defs[i] );
+
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY, "datamorph_initialize: "
+ "error registering syntax %s\n",
+ datamorph_syntax_defs[i].sd_desc );
+ return rc;
+ }
+ }
+
+ datamorph_base_syntax = syn_find( DATAMORPH_SYNTAX_BASE );
+ assert( datamorph_base_syntax );
+
+ for ( i = 0; datamorph_mrule_defs[i].mrd_desc != NULL; i++ ) {
+ rc = register_matching_rule( &datamorph_mrule_defs[i] );
+
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY, "datamorph_initialize: "
+ "error registering matching rule %s\n",
+ datamorph_mrule_defs[i].mrd_desc );
+ return rc;
+ }
+ }
+
+ rc = config_register_schema( datamorph_cfg, datamorph_ocs );
+ if ( rc ) return rc;
+
+ return overlay_register( &datamorph );
+}
+
+#if SLAPD_OVER_DATAMORPH == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return datamorph_initialize();
+}
+#endif
+
+#endif /* SLAPD_OVER_DATAMORPH */
diff --git a/contrib/slapd-modules/datamorph/slapo-datamorph.5 b/contrib/slapd-modules/datamorph/slapo-datamorph.5
new file mode 100644
index 0000000..0ce0c6a
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/slapo-datamorph.5
@@ -0,0 +1,338 @@
+.TH SLAPO-DATAMORPH 5 "RELEASEDATE" "OpenLDAP"
+.\" Copyright 2016-2017 Symas Corp. All Rights Reserved.
+.\" Copying restrictions apply. See LICENSE.
+.SH NAME
+slapo\-datamorph \- store enumerated values and fixed size integers
+.SH SYNOPSIS
+olcOverlay=datamorph
+.SH DESCRIPTION
+The
+.B datamorph
+overlay to
+.BR slapd (8)
+allows attributes with a few pre-defined values to be saved more
+space-efficiently as well as signed or unsigned integer attributes.
+
+.LP
+The overlay operates on configured attributes that must have their syntax
+compatible with
+.BR 1.3.6.1.4.1.4203.666.11.12.1.1 ,
+there are three such syntaxes defined by the overlay:
+.B 1.3.6.1.4.1.4203.666.11.12.1.2
+(Enumerated value),
+.B 1.3.6.1.4.1.4203.666.11.12.1.3
+(Fixed-size integer), and
+.B 1.3.6.1.4.1.4203.666.11.12.1.4
+(Fixed-size signed integer).
+.LP
+
+While transforming the request, if a value for an attribute is not permitted by the configuration, the behaviour depends on the operation:
+
+.RS
+.TP
+.B Search
+The affected value assertions in a
+.B Search
+request filter are replaced by a filter returning
+.B Undefined .
+.TP
+.B Compare
+Request returns
+.B Compare
+.BR False .
+.TP
+.B Add, Modify
+Requests are rejected with a
+.B Constraint
+.BR Violation .
+.RE
+
+The supported allowed matching rules for the attribute types above are:
+
+.RS
+.TP
+.B EQUALITY fixedSizeIntegerMatch
+Appropriate for syntaxes
+.B 1.3.6.1.4.1.4203.666.11.12.1.2
+(Enumerated value), and
+.B 1.3.6.1.4.1.4203.666.11.12.1.3
+(Fixed-size integer).
+.TP
+.B EQUALITY fixedSizeSignedIntegerMatch
+Appropriate for syntax
+.B 1.3.6.1.4.1.4203.666.11.12.1.4
+(Fixed-size signed integer) only.
+.TP
+.B ORDERING fixedSizeIntegerOrderingMatch
+Appropriate for syntaxes
+.B 1.3.6.1.4.1.4203.666.11.12.1.2
+(Enumerated value), and
+.B 1.3.6.1.4.1.4203.666.11.12.1.3
+(Fixed-size integer). Enumerated value attributes are compared according to
+their stored database value.
+.TP
+.B ORDERING fixedSizeSignedIntegerOrderingMatch
+Appropriate for syntax
+.B 1.3.6.1.4.1.4203.666.11.12.1.4
+(Fixed-size signed integer) only.
+
+
+.SH CONFIGURATION LAYOUT
+
+The overlay has to be instantiated under a database adding an entry of
+.B olcOverlay=datamorph
+with objectClass of
+.BR olcDatamorphConfig.
+
+The overlay configuration subtree consists of the following levels:
+.RS
+.TP
+.B objectClass=olcDatamorphConfig
+Main overlay configuration. Created directly under the database
+configuration entry.
+.TP
+.B objectClass=olcDatamorphInteger
+Specifies a
+.B fixed-size integer
+attribute and must be a child of an entry with
+.BR objectClass=olcDatamorphConfig .
+There may be as many such entries as necessary provided they all specify a
+different attribute in the
+.B olcDatamorphAttribute
+attribute.
+.TP
+.B objectClass=olcDatamorphEnum
+Specifies an
+.B enumerated
+attribute and must be a child of an entry with
+.BR objectClass=olcDatamorphConfig .
+There may be as many such entries as necessary provided they all specify a
+different attribute in the
+.B olcDatamorphAttribute
+attribute.
+.TP
+.B objectClass=olcDatamorphEnumValue
+Specifies a permitted value for the enumerated attribute and its database
+representation. Must be a child of an entry with
+.BR objectClass=olcDatamorphEnum .
+There may be as many such entries as necessary provided they all specify a
+different value and index in the corresponding fields.
+.RE
+
+In the case of
+.BR slapd.conf (5),
+the attribute definition is delimited by the keyword
+.B datamorph
+to define an integer or enumerated attribute followed by an arbitrary number of
+.B datamorph_value
+lines in the case of an enumerated one. Each new
+.B datamorph
+line starts configuring a new attribute.
+
+.SH ENUMERATED ATTRIBUTE CONFIGURATION ENTRY
+
+The enumerated attribute entry configuration
+.RB ( olcDatamorphEnum )
+only has the following option available:
+
+.RS
+.TP
+.B olcDatamorphAttribute: <attribute>
+Mandatory attribute, indicates that the named attribute is to be handled by the
+overlay. The
+.BR slapd.conf (5)
+equivalent is
+.B datamorph "int"
+.BR <attribute> .
+.RE
+
+The children of this entry then define how the string values map to the
+database values. They use the objectclass
+.BR olcDatamorphEnumValue ,
+which asks for the following attributes:
+
+.RS
+.TP
+.B olcDatamorphValue: <value>
+A permitted value for the attribute being configured.
+.TP
+.B olcDatamorphIndex: <0-255>
+The corresponding database value.
+.RE
+
+The
+.BR slapd.conf (5)
+equivalent of the above two is
+.B datamorph_value <0-255> <value>
+.RB .
+
+.SH FIXED-WIDTH INTEGER CONFIGURATION ENTRY
+
+The fixed-width integer configuration entry
+.RB ( olcDatamorphInteger )
+has the following options available:
+
+.RS
+.TP
+.B olcDatamorphAttribute: <attribute>
+Mandatory attribute, indicates that the named attribute is to be handled by the
+overlay. The
+.BR slapd.conf (5)
+equivalent is
+.B datamorph "int"
+.BR <attribute> .
+.TP
+.B olcDatamorphIntegerBytes: <1|2|4|8>
+Size of the integer as stored in the backend. The
+.BR slapd.conf (5)
+equivalent is
+.B datamorph_size
+.BR <1|2|4|8> .
+.TP
+.B olcDatamorphIntegerSigned: <TRUE|FALSE>
+Whether the integer is to be treated as signed. Note that the overlay will not
+enforce consistency between this option and the attribute's syntax. The
+.BR slapd.conf (5)
+equivalent is
+.B datamorph_signed
+.BR <TRUE|FALSE> .
+.TP
+.B olcDatamorphIntegerLowerBound: <number>
+The lowest value that the configured attribute will be allowed to have. This
+affects all operations where values are mentioned. The
+.BR slapd.conf (5)
+equivalent is
+.B datamorph_lower_bound
+.BR <number> .
+.TP
+.B olcDatamorphIntegerUpperBound: <number>
+The highest value that the configured attribute will be allowed to have. This
+affects all operations where values are mentioned. The
+.BR slapd.conf (5)
+equivalent is
+.B datamorph_upper_bound
+.BR <number> .
+.RE
+
+.SH EXAMPLE
+
+The following is an example of a configured overlay, substitute
+.B $DATABASE
+for the DN of the database it is attached to and
+.B {x}
+with the desired position of the overlay in the overlay stack.
+
+.nf
+dn: olcOverlay={x}datamorph,$DATABASE
+objectClass: olcDatamorphConfig
+olcOverlay: datamorph
+
+# to handle attribute 'enumeratedAttribute'
+dn: olcDatamorphAttribute=enumeratedAttribute,olcOverlay={x}datamorph,$DATABASE
+objectClass: olcDatamorphEnum
+
+# value 'value1' corresponds to 'AQ==' (0x01)
+dn: olcDatamorphValue=value1,olcDatamorphAttribute={0}enumeratedAttribute,olcOv
+ erlay={x}datamorph,$DATABASE
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 1
+
+# value 'value11' corresponds to 'Cw==' (0x0B)
+dn: olcDatamorphValue=value11,olcDatamorphAttribute={0}enumeratedAttribute,olcO
+ verlay={x}datamorph,$DATABASE
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 11
+
+# handle attribute 'signedInteger' as a 2-byte signed integer with values
+# between -20000 and 30000 (inclusive on both sides)
+dn: olcDatamorphAttribute=signedInteger,olcOverlay={x}datamorph,$DATABASE
+objectclass: olcDatamorphInteger
+olcDatamorphIntegerBytes: 2
+olcDatamorphIntegerSigned: TRUE
+olcDatamorphIntegerLowerBound: -20000
+olcDatamorphIntegerUpperBound: 30000
+
+# handle attribute 'shortInteger' as a 1-byte unsigned integer with only values
+# 0 and 1 allowed (effectively a true/false)
+dn: olcDatamorphAttribute=shortInteger,olcOverlay={x}datamorph,$DATABASE
+objectclass: olcDatamorphInteger
+olcDatamorphIntegerBytes: 1
+olcDatamorphIntegerUpperBound: 1
+olcDatamorphIntegerSigned: FALSE
+.fi
+
+The
+.BR slapd.conf (5)
+equivalent of the above follows:
+
+.nf
+overlay datamorph
+
+datamorph enum enumeratedAttribute
+datamorph_value 1 value1
+datamorph_value 11 value11
+
+datamorph int signedInteger
+datamorph_size 2
+datamorph_signed TRUE
+datamorph_lower_bound -20000
+datamorph_upper_bound 30000
+
+datamorph int shortInteger
+datamorph_size 1
+datamorph_signed no
+datamorph_upper_bound 1
+.fi
+
+.SH REPLICATION
+
+Given that there are syntaxes and matching rules provided by the overlay, it
+should be configured on each replica to guarantee consistency.
+
+.SH BUGS AND LIMITATIONS
+Due to the fact that overlays are not active in the
+.BR slapcat (8)
+nor
+.BR slapadd (8)
+processes, backups of the database will be made exactly as stored. This means
+that backups made using
+.BR ldapsearch (1)
+cannot be used by
+.BR slapadd (8)
+nor can backups made using
+.BR slapcat (8)
+be loaded using
+.BR ldapadd (8).
+
+Value based ACLs that involve values of the transformed attributes are not
+supported.
+
+The overlay will refuse operations that add or rename entries with any of the
+configured attributes in their RDN.
+
+No controls are explicitly handled in the overlay, attaching any controls that
+reference configured attributes might lead to unexpected behaviour and is
+therefore discouraged.
+
+Increment modification of the configured attributes is not supported either.
+
+If a transformation is configured to be signed yet the attribute's schema uses the
+unsigned syntax and matching rules, inequality matching will not work as
+intended and will treat negative numbers as higher than positive numbers.
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.TP
+ETCDIR/slapd.d
+default slapd configuration directory
+.SH SEE ALSO
+.BR slapd-config (5),
+.BR slapd.conf (5),
+.BR slapd.overlays (5),
+.BR slapd (8),
+.BR slapcat (8),
+.BR slapadd (8)
+.SH ACKNOWLEDGEMENTS
+This module was developed in 2016 by Ondřej Kuzník for Symas Corp.
diff --git a/contrib/slapd-modules/datamorph/tests/Rules.mk b/contrib/slapd-modules/datamorph/tests/Rules.mk
new file mode 100644
index 0000000..c25c1d2
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/Rules.mk
@@ -0,0 +1,23 @@
+sp := $(sp).x
+dirstack_$(sp) := $(d)
+d := $(dir)
+
+.PHONY: test
+
+CLEAN += clients servers tests/progs tests/schema tests/testdata tests/testrun
+
+test: all clients servers tests/progs
+
+test:
+ cd tests; \
+ SRCDIR=$(abspath $(LDAP_SRC)) \
+ LDAP_BUILD=$(abspath $(LDAP_BUILD)) \
+ TOPDIR=$(abspath $(SRCDIR)) \
+ LIBTOOL=$(abspath $(LIBTOOL)) \
+ $(abspath $(SRCDIR))/tests/run all
+
+servers clients tests/progs:
+ ln -s $(abspath $(LDAP_BUILD))/$@ $@
+
+d := $(dirstack_$(sp))
+sp := $(basename $(sp))
diff --git a/contrib/slapd-modules/datamorph/tests/data/config.ldif b/contrib/slapd-modules/datamorph/tests/data/config.ldif
new file mode 100644
index 0000000..91f2e60
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/config.ldif
@@ -0,0 +1,108 @@
+dn: cn=datamorph,cn=schema,cn=config
+changetype: add
+objectClass: olcSchemaConfig
+olcAttributeTypes: ( 1.3.6.1.4.1.4203.666.11.12.123.1
+ NAME 'enumerated'
+ DESC 'Enumerated attribute'
+ EQUALITY fixedSizeIntegerMatch
+ ORDERING fixedSizeIntegerOrderingMatch
+ SYNTAX 1.3.6.1.4.1.4203.666.11.12.1.2 )
+olcAttributeTypes: ( 1.3.6.1.4.1.4203.666.11.12.123.2
+ NAME 'number'
+ DESC 'Integer attribute'
+ EQUALITY fixedSizeIntegerMatch
+ ORDERING fixedSizeIntegerOrderingMatch
+ SYNTAX 1.3.6.1.4.1.4203.666.11.12.1.3 )
+olcAttributeTypes: ( 1.3.6.1.4.1.4203.666.11.12.123.3
+ NAME 'signed'
+ DESC 'Signed integer attribute'
+ EQUALITY fixedSizeSignedIntegerMatch
+ ORDERING fixedSizeSignedIntegerOrderingMatch
+ SYNTAX 1.3.6.1.4.1.4203.666.11.12.1.4 )
+olcObjectClasses: ( 1.3.6.1.4.1.4203.666.11.12.123.4
+ NAME 'transformedObject'
+ DESC 'Testing objectclass'
+ SUP top AUXILIARY
+ MAY ( enumerated $ number $ signed ) )
+
+dn: olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectclass: olcDatamorphConfig
+
+# a basic enum
+dn: olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnum
+
+dn: olcDatamorphValue=bjensen,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 1
+
+dn: olcDatamorphValue=bjorn,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 11
+
+dn: olcDatamorphValue=dots,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 12
+
+dn: olcDatamorphValue=jaj,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 13
+
+dn: olcDatamorphValue=jjones,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 14
+
+dn: olcDatamorphValue=jdoe,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 10
+
+dn: olcDatamorphValue=jen,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 101
+
+dn: olcDatamorphValue=johnd,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 20
+
+dn: olcDatamorphValue=melliot,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 51
+
+dn: olcDatamorphValue=uham,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 31
+
+dn: olcDatamorphValue=\5Cno \22name\22,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 200
+
+# an interval
+dn: olcDatamorphAttribute=signed,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphInteger
+olcDatamorphIntegerBytes: 2
+olcDatamorphIntegerSigned: TRUE
+olcDatamorphIntegerLowerBound: -20000
+olcDatamorphIntegerUpperBound: 30000
+
+# an number interval (essentially TRUE/FALSE)
+dn: olcDatamorphAttribute=number,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphInteger
+olcDatamorphIntegerBytes: 1
+olcDatamorphIntegerUpperBound: 1
+olcDatamorphIntegerSigned: FALSE
diff --git a/contrib/slapd-modules/datamorph/tests/data/datamorph.conf b/contrib/slapd-modules/datamorph/tests/data/datamorph.conf
new file mode 100644
index 0000000..7cc4899
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/datamorph.conf
@@ -0,0 +1,49 @@
+overlay datamorph
+
+# they depend on the syntaxes defined by the overlay
+attributetype ( 1.3.6.1.4.1.4203.666.11.12.123.1 NAME 'enumerated'
+ DESC 'Enumerated attribute'
+ EQUALITY fixedSizeIntegerMatch
+ ORDERING fixedSizeIntegerOrderingMatch
+ SYNTAX 1.3.6.1.4.1.4203.666.11.12.1.2 )
+
+attributetype ( 1.3.6.1.4.1.4203.666.11.12.123.2 NAME 'number'
+ DESC 'Integer attribute'
+ EQUALITY fixedSizeIntegerMatch
+ ORDERING fixedSizeIntegerOrderingMatch
+ SYNTAX 1.3.6.1.4.1.4203.666.11.12.1.3 )
+
+attributetype ( 1.3.6.1.4.1.4203.666.11.12.123.3 NAME 'signed'
+ DESC 'Signed integer attribute'
+ EQUALITY fixedSizeSignedIntegerMatch
+ ORDERING fixedSizeSignedIntegerOrderingMatch
+ SYNTAX 1.3.6.1.4.1.4203.666.11.12.1.4 )
+
+objectclass ( 1.3.6.1.4.1.4203.666.11.12.123.4 NAME 'transformedObject'
+ DESC 'Testing objectclass'
+ SUP top AUXILIARY
+ MAY ( enumerated $ number $ signed ) )
+
+datamorph eNuM enumerated
+datamorph_value 1 bjensen
+datamorph_value 11 bjorn
+datamorph_value 12 dots
+datamorph_value "13" jaj
+datamorph_value 14 jjones
+datamorph_value 10 jdoe
+datamorph_value 101 jen
+datamorph_value 20 johnd
+datamorph_value 51 "melliot"
+datamorph_value 31 uham
+datamorph_value 200 "\\no \"name\""
+
+datamorph int signed
+datamorph_size 2
+datamorph_signed TRUE
+datamorph_lower_bound -20000
+datamorph_upper_bound 30000
+
+datamorph iNT number
+datamorph_size 1
+datamorph_signed no
+datamorph_upper_bound 1
diff --git a/contrib/slapd-modules/datamorph/tests/data/test.ldif b/contrib/slapd-modules/datamorph/tests/data/test.ldif
new file mode 100644
index 0000000..67971f3
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test.ldif
@@ -0,0 +1,434 @@
+#LEAD COMMENT
+dn: dc=example,dc=com
+#EMBEDDED COMMENT
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcobject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+signed:: sm4=
+number:: AA==
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupofnames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupofnames
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+enumerated:: AQ==
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+enumerated:: Cw==
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+enumerated:: DA==
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupofuniquenames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,OU=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+enumerated:: DQ==
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+enumerated:: Dg==
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+enumerated:: Cg==
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+enumerated:: ZQ==
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+enumerated:: FA==
+
+dn: cn=Manager,dc=example,dc=com
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+enumerated:: Mw==
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+enumerated:: Hw==
+
diff --git a/contrib/slapd-modules/datamorph/tests/data/test001-01-same-attr.ldif b/contrib/slapd-modules/datamorph/tests/data/test001-01-same-attr.ldif
new file mode 100644
index 0000000..b9ba88f
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test001-01-same-attr.ldif
@@ -0,0 +1,3 @@
+dn: olcDatamorphAttribute=enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnum
diff --git a/contrib/slapd-modules/datamorph/tests/data/test001-02-same-index.ldif b/contrib/slapd-modules/datamorph/tests/data/test001-02-same-index.ldif
new file mode 100644
index 0000000..1dac5dc
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test001-02-same-index.ldif
@@ -0,0 +1,4 @@
+dn: olcDatamorphValue=nope,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 1
diff --git a/contrib/slapd-modules/datamorph/tests/data/test001-02a-same-index.ldif b/contrib/slapd-modules/datamorph/tests/data/test001-02a-same-index.ldif
new file mode 100644
index 0000000..f31ab0a
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test001-02a-same-index.ldif
@@ -0,0 +1,4 @@
+dn: olcDatamorphValue={0}bjensen,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIndex
+olcDatamorphIndex: 11
diff --git a/contrib/slapd-modules/datamorph/tests/data/test001-03-invalid-attr.ldif b/contrib/slapd-modules/datamorph/tests/data/test001-03-invalid-attr.ldif
new file mode 100644
index 0000000..01f21c2
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test001-03-invalid-attr.ldif
@@ -0,0 +1,3 @@
+dn: olcDatamorphAttribute=uid,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnum
diff --git a/contrib/slapd-modules/datamorph/tests/data/test002-config.ldif b/contrib/slapd-modules/datamorph/tests/data/test002-config.ldif
new file mode 100644
index 0000000..2aed906
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test002-config.ldif
@@ -0,0 +1,9 @@
+dn: olcDatamorphValue=gjensen,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcDatamorphEnumValue
+olcDatamorphIndex: 55
+
+dn: olcDatamorphAttribute={1}signed,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+delete: olcDatamorphIntegerUpperBound
+olcDatamorphIntegerUpperBound: 30000
diff --git a/contrib/slapd-modules/datamorph/tests/data/test002-entry.ldif b/contrib/slapd-modules/datamorph/tests/data/test002-entry.ldif
new file mode 100644
index 0000000..0df14c4
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test002-entry.ldif
@@ -0,0 +1,31 @@
+dn: cn=Gern Jensen,ou=Information Technology Division,ou=People,dc=example,dc=
+ com
+changetype: add
+objectClass: testPerson
+objectClass: transformedObject
+cn: Gern Jensen
+sn: Jensen
+uid: gjensen
+title: Chief Investigator, ITD
+postalAddress: ITD $ 535 W. William St $ Anytown, MI 48103
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Coffee
+homePostalAddress: 844 Brown St. Apt. 4 $ Anytown, MI 48104
+description: Very odd
+facsimileTelephonenumber: +1 313 555 7557
+telephoneNumber: +1 313 555 8343
+mail: gjensen@mailgw.example.com
+homePhone: +1 313 555 8844
+testTime: 20050304001801.234Z
+enumerated: gjensen
+
+dn: ou=New Unit,dc=example,dc=com
+changetype: add
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: New Unit
+uidNumber: 32345
+gidNumber: 1
+signed: 32345
+number: 1
+
diff --git a/contrib/slapd-modules/datamorph/tests/data/test002-fail.ldif b/contrib/slapd-modules/datamorph/tests/data/test002-fail.ldif
new file mode 100644
index 0000000..f834997
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test002-fail.ldif
@@ -0,0 +1,23 @@
+dn: uid=bjensen+cn=Barbara Jensen+enumerated=bjensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+changetype: add
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
diff --git a/contrib/slapd-modules/datamorph/tests/data/test002-transformed-rdn.ldif b/contrib/slapd-modules/datamorph/tests/data/test002-transformed-rdn.ldif
new file mode 100644
index 0000000..cbcb14a
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test002-transformed-rdn.ldif
@@ -0,0 +1,5 @@
+dn: ou=New Unit,dc=www+number=1,dc=example,dc=com
+changetype: add
+objectClass: organizationalUnit
+ou: New Unit
+
diff --git a/contrib/slapd-modules/datamorph/tests/data/test003-config.ldif b/contrib/slapd-modules/datamorph/tests/data/test003-config.ldif
new file mode 100644
index 0000000..f74717c
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test003-config.ldif
@@ -0,0 +1,30 @@
+dn: olcDatamorphValue={2}dots,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIndex
+olcDatamorphIndex: 110
+
+dn: olcDatamorphValue={3}jaj,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIndex
+olcDatamorphIndex: 12
+
+dn: olcDatamorphValue={4}jjones,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIndex
+olcDatamorphIndex: 13
+
+dn: olcDatamorphValue={2}dots,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIndex
+olcDatamorphIndex: 14
+
+dn: olcDatamorphAttribute={1}signed,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIntegerSigned
+olcDatamorphIntegerSigned: FALSE
+-
+replace: olcDatamorphIntegerUpperBound
+olcDatamorphIntegerUpperBound: 50000
+-
+replace: olcDatamorphIntegerLowerBound
+olcDatamorphIntegerLowerBound: 50
diff --git a/contrib/slapd-modules/datamorph/tests/data/test003-out.ldif b/contrib/slapd-modules/datamorph/tests/data/test003-out.ldif
new file mode 100644
index 0000000..3dbd8bc
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test003-out.ldif
@@ -0,0 +1,125 @@
+# List regular entries
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcobject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+
+# List entries with transformed attributes
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+enumerated: johnd
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+enumerated: bjorn
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+enumerated: bjensen
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+enumerated: jjones
+
+
+# Search for transformed attributes listing only those
+dn: ou=People,dc=example,dc=com
+signed: -19858
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+enumerated: bjensen
+
+
+# Search for transformed attributes after reconfiguring mapping
+dn: ou=People,dc=example,dc=com
+signed: 45678
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+enumerated: jaj
+
diff --git a/contrib/slapd-modules/datamorph/tests/data/test005-01-fail.ldif b/contrib/slapd-modules/datamorph/tests/data/test005-01-fail.ldif
new file mode 100644
index 0000000..694aacc
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test005-01-fail.ldif
@@ -0,0 +1,5 @@
+# invalid enum value
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+replace: enumerated
+enumerated: 2dots
diff --git a/contrib/slapd-modules/datamorph/tests/data/test005-02-fail.ldif b/contrib/slapd-modules/datamorph/tests/data/test005-02-fail.ldif
new file mode 100644
index 0000000..1ce687f
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test005-02-fail.ldif
@@ -0,0 +1,5 @@
+# enums are case sensitive
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+replace: enumerated
+enumerated: Dots
diff --git a/contrib/slapd-modules/datamorph/tests/data/test005-03-fail.ldif b/contrib/slapd-modules/datamorph/tests/data/test005-03-fail.ldif
new file mode 100644
index 0000000..54bb9b5
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test005-03-fail.ldif
@@ -0,0 +1,5 @@
+# value does not exist in entry
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+delete: enumerated
+enumerated: uham
diff --git a/contrib/slapd-modules/datamorph/tests/data/test005-03a-fail.ldif b/contrib/slapd-modules/datamorph/tests/data/test005-03a-fail.ldif
new file mode 100644
index 0000000..601d895
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test005-03a-fail.ldif
@@ -0,0 +1,5 @@
+# value does not exist in entry
+dn: ou=People,dc=example,dc=com
+changetype: modify
+delete: signed
+signed: 2
diff --git a/contrib/slapd-modules/datamorph/tests/data/test005-04-fail.ldif b/contrib/slapd-modules/datamorph/tests/data/test005-04-fail.ldif
new file mode 100644
index 0000000..d97effc
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test005-04-fail.ldif
@@ -0,0 +1,10 @@
+# a value outside the bounds
+dn: ou=People,dc=example,dc=com
+changetype: modify
+replace: signed
+signed: 2
+-
+replace: number
+number: -1
+-
+
diff --git a/contrib/slapd-modules/datamorph/tests/data/test005-04a-fail.ldif b/contrib/slapd-modules/datamorph/tests/data/test005-04a-fail.ldif
new file mode 100644
index 0000000..63b3263
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test005-04a-fail.ldif
@@ -0,0 +1,6 @@
+# a value outside the bounds
+dn: ou=People,dc=example,dc=com
+changetype: modify
+replace: signed
+signed: 32000
+-
diff --git a/contrib/slapd-modules/datamorph/tests/data/test005-changes.ldif b/contrib/slapd-modules/datamorph/tests/data/test005-changes.ldif
new file mode 100644
index 0000000..17a72da
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test005-changes.ldif
@@ -0,0 +1,30 @@
+dn: ou=People,dc=example,dc=com
+changetype: modify
+replace: signed
+signed: -1
+-
+replace: number
+number: 1
+-
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+replace: enumerated
+enumerated: jaj
+-
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+changetype: modify
+add: enumerated
+enumerated: bjorn
+enumerated: uham
+-
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+changetype: modify
+delete: enumerated
+enumerated: johnd
+-
+add: enumerated
+enumerated: melliot
+-
diff --git a/contrib/slapd-modules/datamorph/tests/data/test005-out.ldif b/contrib/slapd-modules/datamorph/tests/data/test005-out.ldif
new file mode 100644
index 0000000..4c8c360
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test005-out.ldif
@@ -0,0 +1,212 @@
+# Test1: list entries that should have been changed by ldapmodify
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+signed: -1
+number: 1
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+enumerated: bjensen
+enumerated: bjorn
+enumerated: uham
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+enumerated: bjorn
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+enumerated: jaj
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+enumerated: jaj
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+enumerated: jjones
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+enumerated: jdoe
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+enumerated: jen
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+enumerated: melliot
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+enumerated: melliot
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: transformedObject
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+enumerated: uham
+
diff --git a/contrib/slapd-modules/datamorph/tests/data/test007-config.ldif b/contrib/slapd-modules/datamorph/tests/data/test007-config.ldif
new file mode 100644
index 0000000..3820831
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/data/test007-config.ldif
@@ -0,0 +1,30 @@
+dn: olcDatamorphValue={2}dots,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIndex
+olcDatamorphIndex: 110
+
+dn: olcDatamorphValue={4}jjones,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIndex
+olcDatamorphIndex: 14
+
+dn: olcDatamorphValue={3}jaj,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIndex
+olcDatamorphIndex: 13
+
+dn: olcDatamorphValue={2}dots,olcDatamorphAttribute={0}enumerated,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIndex
+olcDatamorphIndex: 12
+
+dn: olcDatamorphAttribute={1}signed,olcOverlay={0}datamorph,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcDatamorphIntegerSigned
+olcDatamorphIntegerSigned: TRUE
+-
+replace: olcDatamorphIntegerLowerBound
+olcDatamorphIntegerLowerBound: -20000
+-
+replace: olcDatamorphIntegerUpperBound
+olcDatamorphIntegerUpperBound: 30000
diff --git a/contrib/slapd-modules/datamorph/tests/run b/contrib/slapd-modules/datamorph/tests/run
new file mode 100755
index 0000000..6a38431
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/run
@@ -0,0 +1,229 @@
+#!/bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+USAGE="$0 [-b <backend>] [-c] [-k] [-l #] [-p] [-s {ro|rp}] [-u] [-w] <script>"
+
+TOPSRCDIR="${SRCDIR-$LDAP_SRC}"
+SRCDIR="${TOPSRCDIR}/tests"
+eval `grep EGREP_CMD= ${LDAP_BUILD}/tests/run`
+eval `$EGREP_CMD -e '^LN_S=' ${LDAP_BUILD}/tests/run`
+
+export SRCDIR TOPSRCDIR LN_S EGREP_CMD
+
+. "${SRCDIR}/scripts/defines.sh"
+
+BACKEND=
+CLEAN=no
+WAIT=0
+KILLSERVERS=yes
+PRESERVE=${PRESERVE-no}
+SYNCMODE=${SYNCMODE-rp}
+USERDATA=no
+LOOP=1
+COUNTER=1
+
+while test $# -gt 0 ; do
+ case "$1" in
+ -b | -backend)
+ BACKEND="$2"
+ shift; shift ;;
+
+ -c | -clean)
+ CLEAN=yes
+ shift ;;
+
+ -k | -kill)
+ KILLSERVERS=no
+ shift ;;
+ -l | -loop)
+ NUM="`echo $2 | sed 's/[0-9]//g'`"
+ if [ -z "$NUM" ]; then
+ LOOP=$2
+ else
+ echo "Loop variable not an int: $2"
+ echo "$USAGE"; exit 1
+ fi
+ shift ;
+ shift ;;
+
+ -p | -preserve)
+ PRESERVE=yes
+ shift ;;
+
+ -s | -syncmode)
+ case "$2" in
+ ro | rp)
+ SYNCMODE="$2"
+ ;;
+ *)
+ echo "unknown sync mode $2"
+ echo "$USAGE"; exit 1
+ ;;
+ esac
+ shift; shift ;;
+
+ -u | -userdata)
+ USERDATA=yes
+ shift ;;
+
+ -w | -wait)
+ WAIT=1
+ shift ;;
+
+ -)
+ shift
+ break ;;
+
+ -*)
+ echo "$USAGE"; exit 1
+ ;;
+
+ *)
+ break ;;
+ esac
+done
+
+eval `$EGREP_CMD -e '^AC' ${LDAP_BUILD}/tests/run`
+export `$EGREP_CMD -e '^AC' ${LDAP_BUILD}/tests/run | sed 's/=.*//'`
+
+if test -z "$BACKEND" ; then
+ for b in mdb ; do
+ if eval "test \"\$AC_$b\" != no" ; then
+ BACKEND=$b
+ break
+ fi
+ done
+ if test -z "$BACKEND" ; then
+ echo "No suitable default database backend configured" >&2
+ exit 1
+ fi
+fi
+
+BACKENDTYPE=`eval 'echo $AC_'$BACKEND`
+if test "x$BACKENDTYPE" = "x" ; then
+ BACKENDTYPE="unknown"
+fi
+
+# Backend features. indexdb: indexing and unchecked limit.
+# maindb: main storage backend. Currently index,limits,mode,paged results.
+INDEXDB=noindexdb MAINDB=nomaindb
+case $BACKEND in
+ mdb) INDEXDB=indexdb MAINDB=maindb ;;
+ ndb) INDEXDB=indexdb ;;
+esac
+
+export BACKEND BACKENDTYPE INDEXDB MAINDB \
+ WAIT KILLSERVERS PRESERVE SYNCMODE USERDATA \
+ SRCDIR
+
+if test $# = 0 ; then
+ echo "$USAGE"; exit 1
+fi
+
+# need defines.sh for the definitions of the directories
+. $SRCDIR/scripts/defines.sh
+
+SCRIPTDIR="${TOPDIR}/tests/scripts"
+
+export SCRIPTDIR
+
+SCRIPTNAME="$1"
+shift
+
+if test -x "${SCRIPTDIR}/${SCRIPTNAME}" ; then
+ SCRIPT="${SCRIPTDIR}/${SCRIPTNAME}"
+elif test -x "`echo ${SCRIPTDIR}/test*-${SCRIPTNAME}`"; then
+ SCRIPT="`echo ${SCRIPTDIR}/test*-${SCRIPTNAME}`"
+elif test -x "`echo ${SCRIPTDIR}/${SCRIPTNAME}-*`"; then
+ SCRIPT="`echo ${SCRIPTDIR}/${SCRIPTNAME}-*`"
+else
+ echo "run: ${SCRIPTNAME} not found (or not executable)"
+ exit 1;
+fi
+
+if test ! -r ${DATADIR}/test.ldif ; then
+ ${LN_S} ${SRCDIR}/data ${DATADIR}
+fi
+if test ! -r ${SCHEMADIR}/core.schema ; then
+ ${LN_S} ${TOPSRCDIR}/servers/slapd/schema ${SCHEMADIR}
+fi
+if test ! -r ./data; then
+ ${LN_S} ${TOPDIR}/tests/data ./
+fi
+
+if test -d ${TESTDIR} ; then
+ if test $PRESERVE = no ; then
+ echo "Cleaning up test run directory leftover from previous run."
+ /bin/rm -rf ${TESTDIR}
+ elif test $PRESERVE = yes ; then
+ echo "Cleaning up only database directories leftover from previous run."
+ /bin/rm -rf ${TESTDIR}/db.*
+ fi
+fi
+if test $BACKEND = ndb ; then
+ mysql --user root <<EOF
+ drop database if exists db_1;
+ drop database if exists db_2;
+ drop database if exists db_3;
+ drop database if exists db_4;
+ drop database if exists db_5;
+ drop database if exists db_6;
+EOF
+fi
+mkdir -p ${TESTDIR}
+
+if test $USERDATA = yes ; then
+ if test ! -d userdata ; then
+ echo "User data directory (userdata) does not exist."
+ exit 1
+ fi
+ cp -R userdata/* ${TESTDIR}
+fi
+
+# disable LDAP initialization
+LDAPNOINIT=true; export LDAPNOINIT
+
+echo "Running ${SCRIPT} for ${BACKEND}..."
+while [ $COUNTER -le $LOOP ]; do
+ if [ $LOOP -gt 1 ]; then
+ echo "Running $COUNTER of $LOOP iterations"
+ fi
+ $SCRIPT $*
+ RC=$?
+
+ if test $CLEAN = yes ; then
+ echo "Cleaning up test run directory from this run."
+ /bin/rm -rf ${TESTDIR}
+ echo "Cleaning up symlinks."
+ /bin/rm -f ${DATADIR} ${SCHEMADIR}
+ fi
+
+ if [ $RC -ne 0 ]; then
+ if [ $LOOP -gt 1 ]; then
+ echo "Failed after $COUNTER of $LOOP iterations"
+ fi
+ exit $RC
+ else
+ COUNTER=`expr $COUNTER + 1`
+ if [ $COUNTER -le $LOOP ]; then
+ echo "Cleaning up test run directory from this run."
+ /bin/rm -rf ${TESTDIR}
+ fi
+ fi
+done
+exit $RC
diff --git a/contrib/slapd-modules/datamorph/tests/scripts/all b/contrib/slapd-modules/datamorph/tests/scripts/all
new file mode 100755
index 0000000..d6d6dc7
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/scripts/all
@@ -0,0 +1,102 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+. $SRCDIR/scripts/defines.sh
+
+TB="" TN=""
+if test -t 1 ; then
+ TB=`$SHTOOL echo -e "%B" 2>/dev/null`
+ TN=`$SHTOOL echo -e "%b" 2>/dev/null`
+fi
+
+FAILCOUNT=0
+SKIPCOUNT=0
+SLEEPTIME=10
+
+echo ">>>>> Executing all LDAP tests for $BACKEND"
+
+if [ -n "$NOEXIT" ]; then
+ echo "Result Test" > $TESTWD/results
+fi
+
+for CMD in ${SCRIPTDIR}/test*; do
+ case "$CMD" in
+ *~) continue;;
+ *.bak) continue;;
+ *.orig) continue;;
+ *.sav) continue;;
+ *) test -f "$CMD" || continue;;
+ esac
+
+ # remove cruft from prior test
+ if test $PRESERVE = yes ; then
+ /bin/rm -rf $TESTDIR/db.*
+ else
+ /bin/rm -rf $TESTDIR
+ fi
+ if test $BACKEND = ndb ; then
+ mysql --user root <<EOF
+ drop database if exists db_1;
+ drop database if exists db_2;
+ drop database if exists db_3;
+ drop database if exists db_4;
+ drop database if exists db_5;
+ drop database if exists db_6;
+EOF
+ fi
+
+ BCMD=`basename $CMD`
+ if [ -x "$CMD" ]; then
+ echo ">>>>> Starting ${TB}$BCMD${TN} for $BACKEND..."
+ $CMD
+ RC=$?
+ if test $RC -eq 0 ; then
+ echo ">>>>> $BCMD completed ${TB}OK${TN} for $BACKEND."
+ else
+ echo ">>>>> $BCMD ${TB}failed${TN} for $BACKEND"
+ FAILCOUNT=`expr $FAILCOUNT + 1`
+
+ if [ -n "$NOEXIT" ]; then
+ echo "Continuing."
+ else
+ echo "(exit $RC)"
+ exit $RC
+ fi
+ fi
+ else
+ echo ">>>>> Skipping ${TB}$BCMD${TN} for $BACKEND."
+ SKIPCOUNT=`expr $SKIPCOUNT + 1`
+ RC="-"
+ fi
+
+ if [ -n "$NOEXIT" ]; then
+ echo "$RC $BCMD" >> $TESTWD/results
+ fi
+
+# echo ">>>>> waiting $SLEEPTIME seconds for things to exit"
+# sleep $SLEEPTIME
+ echo ""
+done
+
+if [ -n "$NOEXIT" ]; then
+ if [ "$FAILCOUNT" -gt 0 ]; then
+ cat $TESTWD/results
+ echo "$FAILCOUNT tests for $BACKEND ${TB}failed${TN}. Please review the test log."
+ else
+ echo "All executed tests for $BACKEND ${TB}succeeded${TN}."
+ fi
+fi
+
+echo "$SKIPCOUNT tests for $BACKEND were ${TB}skipped${TN}."
diff --git a/contrib/slapd-modules/datamorph/tests/scripts/common.sh b/contrib/slapd-modules/datamorph/tests/scripts/common.sh
new file mode 100755
index 0000000..a468732
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/scripts/common.sh
@@ -0,0 +1,152 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+OVERLAY_CONFIG=${OVERLAY_CONFIG-data/config.ldif}
+
+mkdir -p $TESTDIR $DBDIR1
+
+mkdir $TESTDIR/confdir
+. $CONFFILTER $BACKEND $MONITORDB < $CONF > $CONF1
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "database config" >>$CONF1
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >>$CONF1
+
+echo "Starting slapd on TCP/IP port $PORT1 for configuration..."
+$SLAPD -f $CONF1 -F $TESTDIR/confdir -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+$LDAPSEARCH -D cn=config -H $URI1 -y $CONFIGPWF \
+ -s base -b 'cn=module{0},cn=config' 1.1 >$TESTOUT 2>&1
+RC=$?
+case $RC in
+0)
+ $LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: cn=module{0},cn=config
+changetype: modify
+add: olcModuleLoad
+olcModuleLoad: `pwd`/../datamorph.la
+EOMOD
+ ;;
+32)
+ $LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: cn=module,cn=config
+changetype: add
+objectClass: olcModuleList
+olcModuleLoad: `pwd`/../datamorph.la
+EOMOD
+ ;;
+*)
+ echo "Failed testing for module load entry"
+ exit $RC;
+ ;;
+esac
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Loading test datamorph configuration..."
+. $CONFFILTER $BACKEND $MONITORDB < $OVERLAY_CONFIG | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if test $INDEXDB = indexdb ; then
+ echo "Configure indexing for transformed attributes..."
+ $LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcDbIndex
+olcDbIndex: enumerated pres,eq
+olcDbIndex: number pres,eq
+olcDbIndex: signed pres,eq
+EOMOD
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+else
+ echo "Skipping indexing setup for this database"
+fi
+
+echo "Stopping slapd on TCP/IP port $PORT1..."
+kill -HUP $KILLPIDS
+KILLPIDS=""
+sleep $SLEEP0
+
+echo "Running slapadd to build slapd database..."
+$SLAPADD -F $TESTDIR/confdir -l data/test.ldif
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -F $TESTDIR/confdir -h $URI1 -d $LVL >> $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
diff --git a/contrib/slapd-modules/datamorph/tests/scripts/test001-config b/contrib/slapd-modules/datamorph/tests/scripts/test001-config
new file mode 100755
index 0000000..c4bfdf0
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/scripts/test001-config
@@ -0,0 +1,248 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Applying invalid changes to config (should fail)..."
+for CHANGE in data/test001-*.ldif; do
+ echo "... $CHANGE"
+ . $CONFFILTER $BACKEND $MONITORDB < $CHANGE | \
+ $LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+ RC=$?
+ case $RC in
+ 0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+ 80)
+ echo "ldapmodify failed ($RC)"
+ ;;
+ *)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+ esac
+done
+
+# We run this search after the changes above and before restart so we can also
+# check the reconfiguration attempts actually had no side effects
+echo "Saving search output before server restart..."
+echo "# search output from dynamically configured server..." >> $SERVER6OUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ >> $SERVER6OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Stopping slapd on TCP/IP port $PORT1..."
+kill -HUP $KILLPIDS
+KILLPIDS=""
+sleep $SLEEP0
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -F $TESTDIR/confdir -h $URI1 -d $LVL >> $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+echo "Testing slapd.conf support..."
+mkdir $TESTDIR/conftest $DBDIR2
+. $CONFFILTER $BACKEND $MONITORDB < $CONFTWO \
+ | sed -e '/^argsfile.*/a\
+moduleload ../datamorph.la' \
+ -e '/database.*monitor/i\
+include data/datamorph.conf' \
+ > $CONF2
+echo "database config" >>$CONF2
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >>$CONF2
+
+$SLAPADD -f $CONF2 -l data/test.ldif
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT2..."
+$SLAPD -f $CONF2 -h $URI2 -d $LVL >> $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+echo "# search output from server running from slapd.conf..." >> $SERVER2OUT
+$LDAPSEARCH -b "$BASEDN" -H $URI2 \
+ >> $SERVER2OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Stopping slapd on TCP/IP port $PORT2..."
+kill -HUP $PID
+
+$SLAPD -Tt -f $CONF2 -F $TESTDIR/conftest -d $LVL >> $LOG3 2>&1
+
+echo "Starting slapd on TCP/IP port $PORT2..."
+$SLAPD -F $TESTDIR/conftest -h $URI2 -d $LVL >> $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+echo "Gathering overlay configuration from both servers..."
+echo "# overlay configuration from dynamically configured server..." >> $SERVER1OUT
+$LDAPSEARCH -D cn=config -H $URI1 -y $CONFIGPWF \
+ -b "olcOverlay={0}datamorph,olcDatabase={1}$BACKEND,cn=config" \
+ | sed -e "s/ {[0-9]*}/ /" -e "s/={[0-9]*}/=/g" \
+ >> $SERVER1OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "# overlay configuration from server configured from slapd.conf..." >> $SERVER3OUT
+$LDAPSEARCH -D cn=config -H $URI2 -y $CONFIGPWF \
+ -b "olcOverlay={0}datamorph,olcDatabase={1}$BACKEND,cn=config" \
+ | sed -e "s/ {[0-9]*}/ /" -e "s/={[0-9]*}/=/g" \
+ >> $SERVER3OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# We've already filtered out the ordering markers, now sort the entries
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SERVER3OUT > $SERVER3FLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $SERVER1OUT > $SERVER1FLT
+echo "Comparing filter output..."
+$CMP $SERVER3FLT $SERVER1FLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+rm $SERVER1OUT $SERVER3OUT
+
+echo "Comparing search output on both servers..."
+echo "# search output from dynamically configured server..." >> $SERVER1OUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ >> $SERVER1OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "# search output from server configured from slapd.conf..." >> $SERVER3OUT
+$LDAPSEARCH -b "$BASEDN" -H $URI2 \
+ >> $SERVER3OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SERVER1OUT > $SERVER1FLT
+$LDIFFILTER -s e < $SERVER2OUT > $SERVER2FLT
+$LDIFFILTER -s e < $SERVER3OUT > $SERVER3FLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $SERVER6OUT > $SERVER6FLT
+echo "Comparing filter output..."
+$CMP $SERVER6FLT $SERVER1FLT > $CMPOUT && \
+$CMP $SERVER6FLT $SERVER2FLT > $CMPOUT && \
+$CMP $SERVER6FLT $SERVER3FLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/datamorph/tests/scripts/test002-add-delete b/contrib/slapd-modules/datamorph/tests/scripts/test002-add-delete
new file mode 100755
index 0000000..f947d09
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/scripts/test002-add-delete
@@ -0,0 +1,147 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Adding entries (should fail this time)..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -c -f data/test002-entry.ldif >> $TESTOUT 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+19)
+ echo "ldapmodify failed ($RC)"
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Adding other entries (should fail)..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test002-fail.ldif >> $TESTOUT 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+19)
+ echo "ldapmodify failed ($RC)"
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test002-transformed-rdn.ldif >> $TESTOUT 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+32)
+ echo "ldapmodify failed ($RC)"
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Configuring new value..."
+. $CONFFILTER $BACKEND $MONITORDB < data/test002-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding some of the entries again..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test002-entry.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Saving search output..."
+$LDAPSEARCH -H $URI1 -b "$BASEDN" \
+ "(|(cn=Gern Jensen)(ou=New Unit))" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Removing entry..."
+$LDAPDELETE -D $MANAGERDN -H $URI1 -w $PASSWD \
+ "cn=Gern Jensen,ou=Information Technology Division,ou=People,$BASEDN" \
+ "ou=New Unit,$BASEDN" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapdelete failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test002-entry.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s ae < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s ae < $LDIF | grep -v '^changetype:' > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/datamorph/tests/scripts/test003-search b/contrib/slapd-modules/datamorph/tests/scripts/test003-search
new file mode 100755
index 0000000..9afe677
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/scripts/test003-search
@@ -0,0 +1,106 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Testing searches against regular entries..."
+echo "# Testing searches against regular entries..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 "(|(ou=Groups)(st=*))" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches listing transformed attributes..."
+echo >> $SEARCHOUT
+echo "# Testing searches listing transformed attributes..." >> $SEARCHOUT
+$LDAPSEARCH -b "ou=Information Technology Division,ou=People,$BASEDN" -s one \
+ -H $URI1 >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches filtering on transformed attributes..."
+echo >> $SEARCHOUT
+echo "# Testing searches filtering on transformed attributes..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ "(|(enumerated=bjensen)(&(signed=-19858)(signed<=0)(signed>=-20000)))" \
+ enumerated signed \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Reconfiguring transformation definition..."
+. $CONFFILTER $BACKEND $MONITORDB < data/test003-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches filtering on the new values..."
+echo >> $SEARCHOUT
+echo "# Testing searches filtering on the new values..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ "(|(enumerated=not a value)(enumerated=jaj)(&(signed=45678)(!(signed>=50000))(signed>=44444)))" \
+ enumerated signed \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test003-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/datamorph/tests/scripts/test004-compare b/contrib/slapd-modules/datamorph/tests/scripts/test004-compare
new file mode 100755
index 0000000..d4b535b
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/scripts/test004-compare
@@ -0,0 +1,62 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Comparing a regular entry..."
+$LDAPCOMPARE -H $URI1 \
+ "cn=Mark Elliot,ou=Alumni Association,ou=People,$BASEDN" \
+ "cn:Mark Elliot" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 6 && test $RC,$BACKEND != 5,null ; then
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Comparing a transformed enum entry..."
+$LDAPCOMPARE -H $URI1 \
+ "cn=Jane Doe,ou=Alumni Association,ou=People,$BASEDN" \
+ "enumerated:jdoe" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 6 && test $RC,$BACKEND != 5,null ; then
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Comparing a transformed interval entry..."
+$LDAPCOMPARE -H $URI1 "ou=People,$BASEDN" \
+ "signed:-19858" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 6 && test $RC,$BACKEND != 5,null ; then
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/datamorph/tests/scripts/test005-modify b/contrib/slapd-modules/datamorph/tests/scripts/test005-modify
new file mode 100755
index 0000000..94cf1c0
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/scripts/test005-modify
@@ -0,0 +1,89 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Modifying entry..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test005-changes.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Applying invalid changes (should fail)..."
+for CHANGE in data/test005-*fail.ldif; do
+ echo "... $CHANGE"
+ $LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f $CHANGE >> $TESTOUT 2>&1
+ RC=$?
+ case $RC in
+ 0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+ 16|19)
+ echo "ldapmodify failed ($RC)"
+ ;;
+ *)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+ esac
+done
+
+echo "Reading affected entries back..."
+echo "# Reading affected entries back..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ '(|(objectClass=OpenLDAPperson)(ou=people))' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test005-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/datamorph/tests/scripts/test006-modrdn b/contrib/slapd-modules/datamorph/tests/scripts/test006-modrdn
new file mode 100755
index 0000000..56e2f2a
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/scripts/test006-modrdn
@@ -0,0 +1,52 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Renaming an entry to add new value (should fail)..."
+$LDAPMODRDN -D $MANAGERDN -H $URI1 -w $PASSWD \
+ "cn=Mark Elliot,ou=Alumni Association,ou=People,$BASEDN" \
+ "cn=Mark Elliot+enumerated=melliot" \
+ >> $TESTOUT 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapmodrdn should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+19)
+ echo "ldapmodrdn failed ($RC)"
+ ;;
+*)
+ echo "ldapmodrdn failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/datamorph/tests/scripts/test007-transformed-replication b/contrib/slapd-modules/datamorph/tests/scripts/test007-transformed-replication
new file mode 100755
index 0000000..5b2ea4d
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/scripts/test007-transformed-replication
@@ -0,0 +1,296 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test "$SYNCPROV" = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+. ${SCRIPTDIR}/common.sh
+
+if test "$SYNCPROV" = syncprovmod; then
+ $LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: cn=module,cn=config
+changetype: add
+objectClass: olcModuleList
+olcModuleLoad: $LDAP_BUILD/servers/slapd/overlays/syncprov.la
+EOMOD
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+mkdir $DBDIR4 $TESTDIR/confdir-consumer
+
+echo "Starting consumer slapd on TCP/IP port $PORT4..."
+. $CONFFILTER $BACKEND $MONITORDB < $P1SRCONSUMERCONF > $CONF4
+
+echo "database config" >>$CONF4
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >>$CONF4
+
+$SLAPD -f $CONF4 -F $TESTDIR/confdir-consumer -h $URI4 -d $LVL > $LOG4 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep $SLEEP0
+
+echo "Setting up overlay on consumer..."
+$LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: cn=module,cn=config
+changetype: add
+objectClass: olcModuleList
+olcModuleLoad: `pwd`/../datamorph.la
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Configuring syncprov on provider..."
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: olcOverlay=syncprov,olcDatabase={1}$BACKEND,cn=config
+changetype: add
+objectclass: olcSyncProvConfig
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+. $CONFFILTER $BACKEND $MONITORDB < $OVERLAY_CONFIG | \
+$LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$BASEDN" -H $URI4 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for consumer to start replication..."
+ sleep ${SLEEP1}
+done
+
+echo "Waiting ${SLEEP1} seconds for consumer to finish replicating..."
+sleep ${SLEEP1}
+
+echo "Testing searches against regular replicated entries..."
+echo "# Testing searches against regular replicated entries..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI4 "(|(ou=Groups)(st=*))" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches listing replicated transformed attributes..."
+echo >> $SEARCHOUT
+echo "# Testing searches listing replicated transformed attributes..." >> $SEARCHOUT
+$LDAPSEARCH -b "ou=Information Technology Division,ou=People,$BASEDN" \
+ -s one -H $URI4 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches filtering on replicated transformed attributes..."
+echo >> $SEARCHOUT
+echo "# Testing searches filtering on replicated transformed attributes..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI4 \
+ "(|(enumerated=bjensen)(&(signed=-19858)(signed<=0)(signed>=-20000)))" \
+ enumerated signed \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Reconfiguring transformation definition..."
+. $CONFFILTER $BACKEND $MONITORDB < data/test003-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+. $CONFFILTER $BACKEND $MONITORDB < data/test003-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches filtering on the new replicated values..."
+echo >> $SEARCHOUT
+echo "# Testing searches filtering on the new replicated values..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI4 \
+ "(|(enumerated=not a value)(enumerated=jaj)(&(signed=45678)(!(signed>=50000))(signed>=44444)))" \
+ enumerated signed \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+LDIF=data/test003-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+rm $SEARCHOUT
+
+echo "Reverting part of the above configuration for remainder of the test..."
+. $CONFFILTER $BACKEND $MONITORDB < data/test007-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+. $CONFFILTER $BACKEND $MONITORDB < data/test007-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Modifying entry..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test005-changes.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Applying invalid changes (should fail)..."
+for CHANGE in data/test005-*fail.ldif; do
+ echo "... $CHANGE"
+ $LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f $CHANGE >> $TESTOUT 2>&1
+ RC=$?
+ case $RC in
+ 0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+ 16|19)
+ echo "ldapmodify failed ($RC)"
+ ;;
+ *)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+ esac
+done
+
+echo "Waiting ${SLEEP1} seconds for consumer to finish replicating..."
+sleep ${SLEEP1}
+
+echo "Reading affected entries back..."
+echo "# Reading affected entries back..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ '(|(objectClass=OpenLDAPperson)(ou=people))' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test005-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/datamorph/tests/scripts/test008-ignored-replication b/contrib/slapd-modules/datamorph/tests/scripts/test008-ignored-replication
new file mode 100755
index 0000000..a1fcb71
--- /dev/null
+++ b/contrib/slapd-modules/datamorph/tests/scripts/test008-ignored-replication
@@ -0,0 +1,299 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test "$SYNCPROV" = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+. ${SCRIPTDIR}/common.sh
+
+if test "$SYNCPROV" = syncprovmod; then
+ $LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: cn=module,cn=config
+changetype: add
+objectClass: olcModuleList
+olcModuleLoad: $LDAP_BUILD/servers/slapd/overlays/syncprov.la
+EOMOD
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+mkdir $DBDIR4 $TESTDIR/confdir-consumer
+
+echo "Starting consumer slapd on TCP/IP port $PORT4..."
+. $CONFFILTER $BACKEND $MONITORDB < $P1SRCONSUMERCONF > $CONF4
+
+echo "database config" >>$CONF4
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >>$CONF4
+
+$SLAPD -f $CONF4 -F $TESTDIR/confdir-consumer -h $URI4 -d $LVL > $LOG4 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep $SLEEP0
+
+echo "Setting up overlay on consumer..."
+$LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: cn=module,cn=config
+changetype: add
+objectClass: olcModuleList
+olcModuleLoad: `pwd`/../datamorph.la
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Configuring syncprov on provider..."
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: olcOverlay={0}syncprov,olcDatabase={1}$BACKEND,cn=config
+changetype: add
+objectclass: olcSyncProvConfig
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+. $CONFFILTER $BACKEND $MONITORDB < $OVERLAY_CONFIG | \
+$LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$BASEDN" -H $URI4 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for consumer to start replication..."
+ sleep ${SLEEP1}
+done
+
+echo "Waiting ${SLEEP1} seconds for consumer to finish replicating..."
+sleep ${SLEEP1}
+
+echo "Testing searches against regular replicated entries..."
+echo "# Testing searches against regular replicated entries..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI4 "(|(ou=Groups)(st=*))" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches listing replicated transformed attributes..."
+echo >> $SEARCHOUT
+echo "# Testing searches listing replicated transformed attributes..." >> $SEARCHOUT
+$LDAPSEARCH -b "ou=Information Technology Division,ou=People,$BASEDN" -s one \
+ -H $URI4 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches filtering on replicated transformed attributes..."
+echo >> $SEARCHOUT
+echo "# Testing searches filtering on replicated transformed attributes..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI4 \
+ "(|(enumerated=bjensen)(&(signed=-19858)(signed<=0)(signed>=-20000)))" \
+ enumerated signed \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Reconfiguring transformation definition..."
+. $CONFFILTER $BACKEND $MONITORDB < data/test003-config.ldif | \
+sed 's/{0}datamorph/{1}datamorph/' | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+. $CONFFILTER $BACKEND $MONITORDB < data/test003-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches filtering on the new replicated values..."
+echo >> $SEARCHOUT
+echo "# Testing searches filtering on the new replicated values..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI4 \
+ "(|(enumerated=not a value)(enumerated=jaj)(&(signed=45678)(!(signed>=50000))(signed>=44444)))" \
+ enumerated signed \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+LDIF=data/test003-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+rm $SEARCHOUT
+
+echo "Reverting part of the above configuration for remainder of the test..."
+. $CONFFILTER $BACKEND $MONITORDB < data/test007-config.ldif | \
+sed 's/{0}datamorph/{1}datamorph/' | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+. $CONFFILTER $BACKEND $MONITORDB < data/test007-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Modifying entry..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test005-changes.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Applying invalid changes (should fail)..."
+for CHANGE in data/test005-*fail.ldif; do
+ echo "... $CHANGE"
+ $LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f $CHANGE >> $TESTOUT 2>&1
+ RC=$?
+ case $RC in
+ 0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+ 16|19)
+ echo "ldapmodify failed ($RC)"
+ ;;
+ *)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+ esac
+done
+
+echo "Waiting ${SLEEP1} seconds for consumer to finish replicating..."
+sleep ${SLEEP1}
+
+echo "Reading affected entries back..."
+echo "# Reading affected entries back..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ '(|(objectClass=OpenLDAPperson)(ou=people))' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test005-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/denyop/Makefile b/contrib/slapd-modules/denyop/Makefile
new file mode 100644
index 0000000..2cea18b
--- /dev/null
+++ b/contrib/slapd-modules/denyop/Makefile
@@ -0,0 +1,46 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_DENYOP=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = denyop.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+denyop.la: denyop.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/denyop/denyop.c b/contrib/slapd-modules/denyop/denyop.c
new file mode 100644
index 0000000..dd3e13c
--- /dev/null
+++ b/contrib/slapd-modules/denyop/denyop.c
@@ -0,0 +1,260 @@
+/* denyop.c - Denies operations */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_DENYOP
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+/* This overlay provides a quick'n'easy way to deny selected operations
+ * for a database whose backend implements the operations. It is intended
+ * to be less expensive than ACLs because its evaluation occurs before
+ * any backend specific operation is actually even initiated.
+ */
+
+enum {
+ denyop_add = 0,
+ denyop_bind,
+ denyop_compare,
+ denyop_delete,
+ denyop_extended,
+ denyop_modify,
+ denyop_modrdn,
+ denyop_search,
+ denyop_unbind
+} denyop_e;
+
+typedef struct denyop_info {
+ int do_op[denyop_unbind + 1];
+} denyop_info;
+
+static int
+denyop_func( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ denyop_info *oi = (denyop_info *)on->on_bi.bi_private;
+ int deny = 0;
+
+ switch( op->o_tag ) {
+ case LDAP_REQ_BIND:
+ deny = oi->do_op[denyop_bind];
+ break;
+
+ case LDAP_REQ_ADD:
+ deny = oi->do_op[denyop_add];
+ break;
+
+ case LDAP_REQ_DELETE:
+ deny = oi->do_op[denyop_delete];
+ break;
+
+ case LDAP_REQ_MODRDN:
+ deny = oi->do_op[denyop_modrdn];
+ break;
+
+ case LDAP_REQ_MODIFY:
+ deny = oi->do_op[denyop_modify];
+ break;
+
+ case LDAP_REQ_COMPARE:
+ deny = oi->do_op[denyop_compare];
+ break;
+
+ case LDAP_REQ_SEARCH:
+ deny = oi->do_op[denyop_search];
+ break;
+
+ case LDAP_REQ_EXTENDED:
+ deny = oi->do_op[denyop_extended];
+ break;
+
+ case LDAP_REQ_UNBIND:
+ deny = oi->do_op[denyop_unbind];
+ break;
+ }
+
+ if ( !deny ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "operation not allowed within namingContext" );
+
+ return 0;
+}
+
+static int
+denyop_over_init(
+ BackendDB *be, ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ denyop_info *oi;
+
+ oi = (denyop_info *)ch_malloc(sizeof(denyop_info));
+ memset(oi, 0, sizeof(denyop_info));
+ on->on_bi.bi_private = oi;
+
+ return 0;
+}
+
+static int
+denyop_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ denyop_info *oi = (denyop_info *)on->on_bi.bi_private;
+
+ if ( strcasecmp( argv[0], "denyop" ) == 0 ) {
+ char *op;
+
+ if ( argc != 2 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "operation list missing in "
+ "\"denyop <op-list>\" line.\n",
+ fname, lineno );
+ return( 1 );
+ }
+
+ /* The on->on_bi.bi_private pointer can be used for
+ * anything this instance of the overlay needs.
+ */
+
+ op = argv[1];
+ do {
+ char *next = strchr( op, ',' );
+
+ if ( next ) {
+ next[0] = '\0';
+ next++;
+ }
+
+ if ( strcmp( op, "add" ) == 0 ) {
+ oi->do_op[denyop_add] = 1;
+
+ } else if ( strcmp( op, "bind" ) == 0 ) {
+ oi->do_op[denyop_bind] = 1;
+
+ } else if ( strcmp( op, "compare" ) == 0 ) {
+ oi->do_op[denyop_compare] = 1;
+
+ } else if ( strcmp( op, "delete" ) == 0 ) {
+ oi->do_op[denyop_delete] = 1;
+
+ } else if ( strcmp( op, "extended" ) == 0 ) {
+ oi->do_op[denyop_extended] = 1;
+
+ } else if ( strcmp( op, "modify" ) == 0 ) {
+ oi->do_op[denyop_modify] = 1;
+
+ } else if ( strcmp( op, "modrdn" ) == 0 ) {
+ oi->do_op[denyop_modrdn] = 1;
+
+ } else if ( strcmp( op, "search" ) == 0 ) {
+ oi->do_op[denyop_search] = 1;
+
+ } else if ( strcmp( op, "unbind" ) == 0 ) {
+ oi->do_op[denyop_unbind] = 1;
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "unknown operation \"%s\" at "
+ "\"denyop <op-list>\" line.\n",
+ fname, lineno, op );
+ return( 1 );
+ }
+
+ op = next;
+ } while ( op );
+
+ } else {
+ return SLAP_CONF_UNKNOWN;
+ }
+ return 0;
+}
+
+static int
+denyop_destroy(
+ BackendDB *be, ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ denyop_info *oi = (denyop_info *)on->on_bi.bi_private;
+
+ if ( oi ) {
+ ch_free( oi );
+ }
+
+ return 0;
+}
+
+/* This overlay is set up for dynamic loading via moduleload. For static
+ * configuration, you'll need to arrange for the slap_overinst to be
+ * initialized and registered by some other function inside slapd.
+ */
+
+static slap_overinst denyop;
+
+int
+denyop_initialize( void )
+{
+ memset( &denyop, 0, sizeof( slap_overinst ) );
+ denyop.on_bi.bi_type = "denyop";
+ denyop.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ denyop.on_bi.bi_db_init = denyop_over_init;
+ denyop.on_bi.bi_db_config = denyop_config;
+ denyop.on_bi.bi_db_destroy = denyop_destroy;
+
+ denyop.on_bi.bi_op_bind = denyop_func;
+ denyop.on_bi.bi_op_search = denyop_func;
+ denyop.on_bi.bi_op_compare = denyop_func;
+ denyop.on_bi.bi_op_modify = denyop_func;
+ denyop.on_bi.bi_op_modrdn = denyop_func;
+ denyop.on_bi.bi_op_add = denyop_func;
+ denyop.on_bi.bi_op_delete = denyop_func;
+ denyop.on_bi.bi_extended = denyop_func;
+ denyop.on_bi.bi_op_unbind = denyop_func;
+
+ denyop.on_response = NULL /* denyop_response */ ;
+
+ return overlay_register( &denyop );
+}
+
+#if SLAPD_OVER_DENYOP == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return denyop_initialize();
+}
+#endif /* SLAPD_OVER_DENYOP == SLAPD_MOD_DYNAMIC */
+
+#endif /* defined(SLAPD_OVER_DENYOP) */
diff --git a/contrib/slapd-modules/dsaschema/Makefile b/contrib/slapd-modules/dsaschema/Makefile
new file mode 100644
index 0000000..3a88fc0
--- /dev/null
+++ b/contrib/slapd-modules/dsaschema/Makefile
@@ -0,0 +1,46 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = dsaschema.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+dsaschema.la: dsaschema.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/dsaschema/README b/contrib/slapd-modules/dsaschema/README
new file mode 100644
index 0000000..fdf932e
--- /dev/null
+++ b/contrib/slapd-modules/dsaschema/README
@@ -0,0 +1,23 @@
+Copyright 2004-2022 The OpenLDAP Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+This directory contains a native slapd plugin, dsaschema, that permits the
+loading of DSA-specific schema from configuration files (including operational
+attributes).
+
+To use the plugin, add:
+
+moduleload dsaschema.so
+ /etc/openldap/schema/foo1.schema
+ ...etc...
+ /etc/openldap/schema/fooN.schema
+
+to your slapd configuration file.
+
+Use Makefile to compile this plugin or use a command line similar to:
+
+gcc -shared -I../../../include -Wall -g -o dsaschema.so dsaschema.c
+
diff --git a/contrib/slapd-modules/dsaschema/dsaschema.c b/contrib/slapd-modules/dsaschema/dsaschema.c
new file mode 100644
index 0000000..31defae
--- /dev/null
+++ b/contrib/slapd-modules/dsaschema/dsaschema.c
@@ -0,0 +1,369 @@
+/* dsaschema.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include <portable.h>
+
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/signal.h>
+#include <ac/errno.h>
+#include <ac/stdlib.h>
+#include <ac/ctype.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include <stdio.h>
+
+/*
+ * Schema reader that allows us to define DSA schema (including
+ * operational attributes and non-user object classes)
+ *
+ * A kludge, at best, and in order to avoid including slapd
+ * headers we use fprintf() rather than slapd's native logging,
+ * which may confuse users...
+ *
+ */
+
+#include <ldap.h>
+#include <ldap_schema.h>
+
+#include <slap.h>
+#include <slap-config.h>
+
+#define ARGS_STEP 512
+
+static char *fp_getline(FILE *fp, int *lineno);
+static void fp_getline_init(int *lineno);
+static int fp_parse_line(int lineno, char *line);
+static char *strtok_quote( char *line, char *sep );
+
+static char **cargv = NULL;
+static int cargv_size = 0;
+static int cargc = 0;
+static char *strtok_quote_ptr;
+
+int init_module(int argc, char *argv[]);
+
+static int dsaschema_parse_cr(const char *fname, int lineno, char *line, char **argv)
+{
+ struct config_args_s c = { .line = line };
+
+ if ( parse_cr( &c, NULL ) ) {
+ Debug( LDAP_DEBUG_ANY, "dsaschema_parse_cr: "
+ "ditcontentrule definition invalid at %s:%d\n",
+ fname, lineno );
+ return 1;
+ }
+
+ return 0;
+}
+
+static int dsaschema_read_config(const char *fname, int depth)
+{
+ FILE *fp;
+ char *line, *savefname, *saveline = NULL;
+ int savelineno, lineno;
+ int rc;
+
+ if (depth == 0) {
+ cargv = ch_calloc(ARGS_STEP + 1, sizeof(*cargv));
+ cargv_size = ARGS_STEP + 1;
+ }
+
+ fp = fopen(fname, "r");
+ if (fp == NULL) {
+ char ebuf[128];
+ int saved_errno = errno;
+ fprintf(stderr, "could not open config file \"%s\": %s (%d)\n",
+ fname, AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)), saved_errno);
+ return 1;
+ }
+ fp_getline_init(&lineno);
+
+ while ((line = fp_getline(fp, &lineno)) != NULL) {
+ /* skip comments and blank lines */
+ if (line[0] == '#' || line[0] == '\0') {
+ continue;
+ }
+
+ saveline = ch_strdup(line);
+
+ if (fp_parse_line(lineno, line) != 0) {
+ rc = 1;
+ break;
+ }
+
+ if (cargc < 1) {
+ continue;
+ }
+
+ if (strcasecmp(cargv[0], "attributetype") == 0 ||
+ strcasecmp(cargv[0], "attribute") == 0) {
+ if (cargc < 2) {
+ fprintf(stderr, "%s: line %d: illegal attribute type format\n",
+ fname, lineno);
+ rc = 1;
+ break;
+ } else if (*cargv[1] == '(' /*')'*/) {
+ char *p;
+
+ p = strchr(saveline, '(' /*')'*/);
+ rc = register_at(p, NULL, 0);
+ if (rc != 0) {
+ Debug( LDAP_DEBUG_ANY, "dsaschema_read_config: "
+ "attribute definition invalid at %s:%d\n",
+ fname, lineno );
+ break;
+ }
+ } else {
+ fprintf(stderr, "%s: line %d: old attribute type format not supported\n",
+ fname, lineno);
+ }
+ } else if (strcasecmp(cargv[0], "ditcontentrule") == 0) {
+ char *p;
+ p = strchr(saveline, '(' /*')'*/);
+ rc = dsaschema_parse_cr(fname, lineno, p, cargv);
+ if (rc != 0)
+ break;
+ } else if (strcasecmp(cargv[0], "objectclass") == 0) {
+ if (cargc < 2) {
+ fprintf(stderr, "%s: line %d: illegal objectclass format\n",
+ fname, lineno);
+ rc = 1;
+ break;
+ } else if (*cargv[1] == '(' /*')'*/) {
+ char *p;
+
+ p = strchr(saveline, '(' /*')'*/);
+ rc = register_oc(p, NULL, 0);
+ if (rc != 0) {
+ Debug( LDAP_DEBUG_ANY, "dsaschema_read_config: "
+ "objectclass definition invalid at %s:%d\n",
+ fname, lineno );
+ break;
+ }
+ } else {
+ fprintf(stderr, "%s: line %d: object class format not supported\n",
+ fname, lineno);
+ }
+ } else if (strcasecmp(cargv[0], "include") == 0) {
+ if (cargc < 2) {
+ fprintf(stderr, "%s: line %d: missing file name in \"include <filename>\" line",
+ fname, lineno);
+ rc = 1;
+ break;
+ }
+ savelineno = lineno;
+ savefname = ch_strdup(cargv[1]);
+
+ rc = dsaschema_read_config(savefname, depth + 1);
+ ch_free(savefname);
+ lineno = savelineno - 1;
+ if (rc != 0) {
+ break;
+ }
+ } else {
+ fprintf(stderr, "%s: line %d: unknown directive \"%s\" (ignored)\n",
+ fname, lineno, cargv[0]);
+ }
+
+ ch_free(saveline);
+ saveline = NULL;
+ }
+
+ fclose(fp);
+
+ if (depth == 0)
+ ch_free(cargv);
+
+ if (saveline != NULL)
+ ch_free(saveline);
+
+ return rc;
+}
+
+int init_module(int argc, char *argv[])
+{
+ int i;
+ int rc;
+
+ for (i = 0; i < argc; i++) {
+ rc = dsaschema_read_config(argv[i], 0);
+ if (rc != 0) {
+ break;
+ }
+ }
+
+ return rc;
+}
+
+
+static int
+fp_parse_line(
+ int lineno,
+ char *line
+)
+{
+ char * token;
+
+ cargc = 0;
+ token = strtok_quote( line, " \t" );
+
+ if ( strtok_quote_ptr ) {
+ *strtok_quote_ptr = ' ';
+ }
+
+ if ( strtok_quote_ptr ) {
+ *strtok_quote_ptr = '\0';
+ }
+
+ for ( ; token != NULL; token = strtok_quote( NULL, " \t" ) ) {
+ if ( cargc == cargv_size - 1 ) {
+ char **tmp;
+ tmp = ch_realloc( cargv, (cargv_size + ARGS_STEP) *
+ sizeof(*cargv) );
+ cargv = tmp;
+ cargv_size += ARGS_STEP;
+ }
+ cargv[cargc++] = token;
+ }
+ cargv[cargc] = NULL;
+ return 0;
+}
+
+static char *
+strtok_quote( char *line, char *sep )
+{
+ int inquote;
+ char *tmp;
+ static char *next;
+
+ strtok_quote_ptr = NULL;
+ if ( line != NULL ) {
+ next = line;
+ }
+ while ( *next && strchr( sep, *next ) ) {
+ next++;
+ }
+
+ if ( *next == '\0' ) {
+ next = NULL;
+ return( NULL );
+ }
+ tmp = next;
+
+ for ( inquote = 0; *next; ) {
+ switch ( *next ) {
+ case '"':
+ if ( inquote ) {
+ inquote = 0;
+ } else {
+ inquote = 1;
+ }
+ AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
+ break;
+
+ case '\\':
+ if ( next[1] )
+ AC_MEMCPY( next,
+ next + 1, strlen( next + 1 ) + 1 );
+ next++; /* dont parse the escaped character */
+ break;
+
+ default:
+ if ( ! inquote ) {
+ if ( strchr( sep, *next ) != NULL ) {
+ strtok_quote_ptr = next;
+ *next++ = '\0';
+ return( tmp );
+ }
+ }
+ next++;
+ break;
+ }
+ }
+
+ return( tmp );
+}
+
+static char buf[BUFSIZ];
+static char *line;
+static size_t lmax, lcur;
+
+#define CATLINE( buf ) \
+ do { \
+ size_t len = strlen( buf ); \
+ while ( lcur + len + 1 > lmax ) { \
+ lmax += BUFSIZ; \
+ line = (char *) ch_realloc( line, lmax ); \
+ } \
+ strcpy( line + lcur, buf ); \
+ lcur += len; \
+ } while( 0 )
+
+static char *
+fp_getline( FILE *fp, int *lineno )
+{
+ char *p;
+
+ lcur = 0;
+ CATLINE( buf );
+ (*lineno)++;
+
+ /* hack attack - keeps us from having to keep a stack of bufs... */
+ if ( strncasecmp( line, "include", 7 ) == 0 ) {
+ buf[0] = '\0';
+ return( line );
+ }
+
+ while ( fgets( buf, sizeof(buf), fp ) != NULL ) {
+ /* trim off \r\n or \n */
+ if ( (p = strchr( buf, '\n' )) != NULL ) {
+ if( p > buf && p[-1] == '\r' ) --p;
+ *p = '\0';
+ }
+
+ /* trim off trailing \ and append the next line */
+ if ( line[ 0 ] != '\0'
+ && (p = line + strlen( line ) - 1)[ 0 ] == '\\'
+ && p[ -1 ] != '\\' ) {
+ p[ 0 ] = '\0';
+ lcur--;
+
+ } else {
+ if ( ! isspace( (unsigned char) buf[0] ) ) {
+ return( line );
+ }
+
+ /* change leading whitespace to a space */
+ buf[0] = ' ';
+ }
+
+ CATLINE( buf );
+ (*lineno)++;
+ }
+ buf[0] = '\0';
+
+ return( line[0] ? line : NULL );
+}
+
+static void
+fp_getline_init( int *lineno )
+{
+ *lineno = -1;
+ buf[0] = '\0';
+}
+
diff --git a/contrib/slapd-modules/dupent/Makefile b/contrib/slapd-modules/dupent/Makefile
new file mode 100644
index 0000000..6b35438
--- /dev/null
+++ b/contrib/slapd-modules/dupent/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+# Copyright 2004 Howard Chu, Symas Corp. All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted only as authorized by the OpenLDAP
+# Public License.
+#
+# A copy of this license is available in the file LICENSE in the
+# top-level directory of the distribution or, alternatively, at
+# <http://www.OpenLDAP.org/license.html>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_DUPENT=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = dupent.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+dupent.la: dupent.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/dupent/dupent.c b/contrib/slapd-modules/dupent/dupent.c
new file mode 100644
index 0000000..89ad622
--- /dev/null
+++ b/contrib/slapd-modules/dupent/dupent.c
@@ -0,0 +1,558 @@
+/* dupent.c - LDAP Control for a Duplicate Entry Representation of Search Results */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2006-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+/*
+ * LDAP Control for a Duplicate Entry Representation of Search Results
+ * <draft-ietf-ldapext-ldapv3-dupent-08.txt> (EXPIRED)
+ * <http://tools.ietf.org/id/draft-ietf-ldapext-ldapv3-dupent-08.txt>
+ */
+
+#include "portable.h"
+
+/* define SLAPD_OVER_DUPENT=2 to build as run-time loadable module */
+#ifdef SLAPD_OVER_DUPENT
+
+/*
+ * The macros
+ *
+ * LDAP_CONTROL_DUPENT_REQUEST "2.16.840.1.113719.1.27.101.1"
+ * LDAP_CONTROL_DUPENT_RESPONSE "2.16.840.1.113719.1.27.101.2"
+ * LDAP_CONTROL_DUPENT_ENTRY "2.16.840.1.113719.1.27.101.3"
+ *
+ * are already defined in <ldap.h>
+ */
+
+/*
+ * support for no attrs and "*" in AttributeDescriptionList is missing
+ */
+
+#include "slap.h"
+#include "ac/string.h"
+
+#define o_dupent o_ctrlflag[dupent_cid]
+#define o_ctrldupent o_controls[dupent_cid]
+
+static int dupent_cid;
+static slap_overinst dupent;
+
+typedef struct dupent_t {
+ AttributeName *ds_an;
+ ber_len_t ds_nattrs;
+ slap_mask_t ds_flags;
+ ber_int_t ds_paa;
+} dupent_t;
+
+static int
+dupent_parseCtrl (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ ber_tag_t tag;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_len_t len;
+ BerVarray AttributeDescriptionList = NULL;
+ ber_len_t cnt = sizeof(struct berval);
+ ber_len_t off = 0;
+ ber_int_t PartialApplicationAllowed = 1;
+ dupent_t *ds = NULL;
+ int i;
+
+ if ( op->o_dupent != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "Dupent control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "Dupent control value is absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "Dupent control value is empty";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ ber_init2( ber, &ctrl->ldctl_value, 0 );
+
+ /*
+
+ DuplicateEntryRequest ::= SEQUENCE {
+ AttributeDescriptionList, -- from [RFC2251]
+ PartialApplicationAllowed BOOLEAN DEFAULT TRUE }
+
+ AttributeDescriptionList ::= SEQUENCE OF
+ AttributeDescription
+
+ AttributeDescription ::= LDAPString
+
+ attributeDescription = AttributeType [ ";" <options> ]
+
+ */
+
+ tag = ber_skip_tag( ber, &len );
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ if ( ber_scanf( ber, "{M}", &AttributeDescriptionList, &cnt, off )
+ == LBER_ERROR )
+ {
+ rs->sr_text = "Dupent control: dupentSpec decoding error";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+ tag = ber_skip_tag( ber, &len );
+ if ( tag == LBER_BOOLEAN ) {
+ /* NOTE: PartialApplicationAllowed is ignored, since the control
+ * can always be honored
+ */
+ if ( ber_scanf( ber, "b", &PartialApplicationAllowed ) == LBER_ERROR )
+ {
+ rs->sr_text = "Dupent control: dupentSpec decoding error";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+ tag = ber_skip_tag( ber, &len );
+ }
+ if ( len || tag != LBER_DEFAULT ) {
+ rs->sr_text = "Dupent control: dupentSpec decoding error";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ ds = (dupent_t *)op->o_tmpcalloc( 1,
+ sizeof(dupent_t) + sizeof(AttributeName)*cnt,
+ op->o_tmpmemctx );
+
+ ds->ds_paa = PartialApplicationAllowed;
+
+ if ( cnt == 0 ) {
+ ds->ds_flags |= SLAP_USERATTRS_YES;
+
+ } else {
+ int c;
+
+ ds->ds_an = (AttributeName *)&ds[ 1 ];
+
+ for ( i = 0, c = 0; i < cnt; i++ ) {
+ const char *text;
+ int j;
+ int rc;
+ AttributeDescription *ad = NULL;
+
+ if ( bvmatch( &AttributeDescriptionList[i],
+ slap_bv_all_user_attrs ) )
+ {
+ if ( ds->ds_flags & SLAP_USERATTRS_YES ) {
+ rs->sr_text = "Dupent control: AttributeDescription decoding error";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ ds->ds_flags |= SLAP_USERATTRS_YES;
+ continue;
+ }
+
+ rc = slap_bv2ad( &AttributeDescriptionList[i], &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ continue;
+ }
+
+ ds->ds_an[c].an_desc = ad;
+ ds->ds_an[c].an_name = ad->ad_cname;
+
+ /* FIXME: not specified; consider this an error, just in case */
+ for ( j = 0; j < c; j++ ) {
+ if ( ds->ds_an[c].an_desc == ds->ds_an[j].an_desc ) {
+ rs->sr_text = "Dupent control: AttributeDescription must be unique within AttributeDescriptionList";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+ }
+
+ c++;
+ }
+
+ ds->ds_nattrs = c;
+
+ if ( ds->ds_flags & SLAP_USERATTRS_YES ) {
+ /* purge user attrs */
+ for ( i = 0; i < ds->ds_nattrs; ) {
+ if ( is_at_operational( ds->ds_an[i].an_desc->ad_type ) ) {
+ i++;
+ continue;
+ }
+
+ ds->ds_nattrs--;
+ if ( i < ds->ds_nattrs ) {
+ ds->ds_an[i] = ds->ds_an[ds->ds_nattrs];
+ }
+ }
+ }
+ }
+
+ op->o_ctrldupent = (void *)ds;
+
+ op->o_dupent = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ rs->sr_err = LDAP_SUCCESS;
+
+done:;
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ op->o_tmpfree( ds, op->o_tmpmemctx );
+ }
+
+ if ( AttributeDescriptionList != NULL ) {
+ ber_memfree_x( AttributeDescriptionList, op->o_tmpmemctx );
+ }
+
+ return rs->sr_err;
+}
+
+typedef struct dupent_cb_t {
+ slap_overinst *dc_on;
+ dupent_t *dc_ds;
+ int dc_skip;
+} dupent_cb_t;
+
+typedef struct valnum_t {
+ Attribute *ap;
+ Attribute a;
+ struct berval vals[2];
+ struct berval nvals[2];
+ int cnt;
+} valnum_t;
+
+static int
+dupent_response_done( Operation *op, SlapReply *rs )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *) &berbuf;
+ struct berval ctrlval;
+ LDAPControl *ctrl, *ctrlsp[2];
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+
+ /*
+
+ DuplicateEntryResponseDone ::= SEQUENCE {
+ resultCode, -- From [RFC2251]
+ errorMessage [0] LDAPString OPTIONAL,
+ attribute [1] AttributeDescription OPTIONAL }
+
+ */
+
+ ber_printf( ber, "{i}", rs->sr_err );
+ if ( ber_flatten2( ber, &ctrlval, 0 ) == -1 ) {
+ ber_free_buf( ber );
+ if ( op->o_dupent == SLAP_CONTROL_CRITICAL ) {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ return SLAP_CB_CONTINUE;
+ }
+
+ ctrl = op->o_tmpcalloc( 1,
+ sizeof( LDAPControl ) + ctrlval.bv_len + 1,
+ op->o_tmpmemctx );
+ ctrl->ldctl_value.bv_val = (char *)&ctrl[ 1 ];
+ ctrl->ldctl_oid = LDAP_CONTROL_DUPENT_RESPONSE;
+ ctrl->ldctl_iscritical = 0;
+ ctrl->ldctl_value.bv_len = ctrlval.bv_len;
+ AC_MEMCPY( ctrl->ldctl_value.bv_val, ctrlval.bv_val, ctrlval.bv_len );
+ ctrl->ldctl_value.bv_val[ ctrl->ldctl_value.bv_len ] = '\0';
+
+ ber_free_buf( ber );
+
+ ctrlsp[0] = ctrl;
+ ctrlsp[1] = NULL;
+ slap_add_ctrls( op, rs, ctrlsp );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+dupent_response_entry_1level(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ valnum_t *valnum,
+ int nattrs,
+ int level )
+{
+ int i, rc = LDAP_SUCCESS;
+
+ for ( i = 0; i < valnum[level].ap->a_numvals; i++ ) {
+ LDAPControl *ctrl = NULL, *ctrlsp[2];
+
+ valnum[level].a.a_vals[0] = valnum[level].ap->a_vals[i];
+ if ( valnum[level].ap->a_nvals != valnum[level].ap->a_vals ) {
+ valnum[level].a.a_nvals[0] = valnum[level].ap->a_nvals[i];
+ }
+
+ if ( level < nattrs - 1 ) {
+ rc = dupent_response_entry_1level( op, rs,
+ e, valnum, nattrs, level + 1 );
+ if ( rc != LDAP_SUCCESS ) {
+ break;
+ }
+
+ continue;
+ }
+
+ /* NOTE: add the control all times, under the assumption
+ * send_search_entry() honors the REP_CTRLS_MUSTBEFREED
+ * set by slap_add_ctrls(); this is not true (ITS#6629)
+ */
+ ctrl = op->o_tmpcalloc( 1, sizeof( LDAPControl ), op->o_tmpmemctx );
+ ctrl->ldctl_oid = LDAP_CONTROL_DUPENT_ENTRY;
+ ctrl->ldctl_iscritical = 0;
+
+ ctrlsp[0] = ctrl;
+ ctrlsp[1] = NULL;
+ slap_add_ctrls( op, rs, ctrlsp );
+
+ /* do the real send */
+ rs->sr_entry = e;
+ rc = send_search_entry( op, rs );
+ if ( rc != LDAP_SUCCESS ) {
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static void
+dupent_attr_prepare( dupent_t *ds, Entry *e, valnum_t *valnum, int nattrs, int c, Attribute **app, Attribute **ap_listp )
+{
+ valnum[c].ap = *app;
+ *app = (*app)->a_next;
+
+ valnum[c].ap->a_next = *ap_listp;
+ *ap_listp = valnum[c].ap;
+
+ valnum[c].a = *valnum[c].ap;
+ if ( c < nattrs - 1 ) {
+ valnum[c].a.a_next = &valnum[c + 1].a;
+ } else {
+ valnum[c].a.a_next = NULL;
+ }
+ valnum[c].a.a_numvals = 1;
+ valnum[c].a.a_vals = valnum[c].vals;
+ BER_BVZERO( &valnum[c].vals[1] );
+ if ( valnum[c].ap->a_nvals != valnum[c].ap->a_vals ) {
+ valnum[c].a.a_nvals = valnum[c].nvals;
+ BER_BVZERO( &valnum[c].nvals[1] );
+ } else {
+ valnum[c].a.a_nvals = valnum[c].a.a_vals;
+ }
+}
+
+static int
+dupent_response_entry( Operation *op, SlapReply *rs )
+{
+ dupent_cb_t *dc = (dupent_cb_t *)op->o_callback->sc_private;
+ int nattrs = 0;
+ valnum_t *valnum = NULL;
+ Attribute **app, *ap_list = NULL;
+ int i, c;
+ Entry *e = NULL;
+ int rc;
+
+ assert( rs->sr_type == REP_SEARCH );
+
+ for ( i = 0; i < dc->dc_ds->ds_nattrs; i++ ) {
+ Attribute *ap;
+
+ ap = attr_find( rs->sr_entry->e_attrs,
+ dc->dc_ds->ds_an[ i ].an_desc );
+ if ( ap && ap->a_numvals > 1 ) {
+ nattrs++;
+ }
+ }
+
+ if ( dc->dc_ds->ds_flags & SLAP_USERATTRS_YES ) {
+ Attribute *ap;
+
+ for ( ap = rs->sr_entry->e_attrs; ap != NULL; ap = ap->a_next ) {
+ if ( !is_at_operational( ap->a_desc->ad_type ) && ap->a_numvals > 1 ) {
+ nattrs++;
+ }
+ }
+ }
+
+ if ( !nattrs ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ rs_entry2modifiable( op, rs, dc->dc_on );
+ rs->sr_flags &= ~(REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED);
+ e = rs->sr_entry;
+
+ valnum = op->o_tmpcalloc( sizeof(valnum_t), nattrs, op->o_tmpmemctx );
+
+ for ( c = 0, i = 0; i < dc->dc_ds->ds_nattrs; i++ ) {
+ for ( app = &e->e_attrs; *app != NULL; app = &(*app)->a_next ) {
+ if ( (*app)->a_desc == dc->dc_ds->ds_an[ i ].an_desc ) {
+ break;
+ }
+ }
+
+ if ( *app != NULL && (*app)->a_numvals > 1 ) {
+ assert( c < nattrs );
+ dupent_attr_prepare( dc->dc_ds, e, valnum, nattrs, c, app, &ap_list );
+ c++;
+ }
+ }
+
+ if ( dc->dc_ds->ds_flags & SLAP_USERATTRS_YES ) {
+ for ( app = &e->e_attrs; *app != NULL; app = &(*app)->a_next ) {
+ if ( !is_at_operational( (*app)->a_desc->ad_type ) && (*app)->a_numvals > 1 ) {
+ assert( c < nattrs );
+ dupent_attr_prepare( dc->dc_ds, e, valnum, nattrs, c, app, &ap_list );
+ c++;
+ }
+ }
+ }
+
+ for ( app = &e->e_attrs; *app != NULL; app = &(*app)->a_next )
+ /* goto tail */ ;
+
+ *app = &valnum[0].a;
+
+ /* NOTE: since send_search_entry() does not honor the
+ * REP_CTRLS_MUSTBEFREED flag set by slap_add_ctrls(),
+ * the control could be added here once for all (ITS#6629)
+ */
+
+ dc->dc_skip = 1;
+ rc = dupent_response_entry_1level( op, rs, e, valnum, nattrs, 0 );
+ dc->dc_skip = 0;
+
+ *app = ap_list;
+
+ entry_free( e );
+
+ op->o_tmpfree( valnum, op->o_tmpmemctx );
+
+ return rc;
+}
+
+static int
+dupent_response( Operation *op, SlapReply *rs )
+{
+ dupent_cb_t *dc = (dupent_cb_t *)op->o_callback->sc_private;
+
+ if ( dc->dc_skip ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ switch ( rs->sr_type ) {
+ case REP_RESULT:
+ return dupent_response_done( op, rs );
+
+ case REP_SEARCH:
+ return dupent_response_entry( op, rs );
+
+ case REP_SEARCHREF:
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+dupent_cleanup( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_RESULT || rs->sr_err == SLAPD_ABANDON ) {
+ op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
+ op->o_callback = NULL;
+
+ op->o_tmpfree( op->o_ctrldupent, op->o_tmpmemctx );
+ op->o_ctrldupent = NULL;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+dupent_op_search( Operation *op, SlapReply *rs )
+{
+ if ( op->o_dupent != SLAP_CONTROL_NONE ) {
+ slap_callback *sc;
+ dupent_cb_t *dc;
+
+ sc = op->o_tmpcalloc( 1, sizeof( slap_callback ) + sizeof( dupent_cb_t ), op->o_tmpmemctx );
+
+ dc = (dupent_cb_t *)&sc[ 1 ];
+ dc->dc_on = (slap_overinst *)op->o_bd->bd_info;
+ dc->dc_ds = (dupent_t *)op->o_ctrldupent;
+ dc->dc_skip = 0;
+
+ sc->sc_response = dupent_response;
+ sc->sc_cleanup = dupent_cleanup;
+ sc->sc_private = (void *)dc;
+
+ sc->sc_next = op->o_callback->sc_next;
+ op->o_callback->sc_next = sc;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+#if SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC
+static
+#endif /* SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC */
+int
+dupent_initialize( void )
+{
+ int rc;
+
+ rc = register_supported_control( LDAP_CONTROL_DUPENT,
+ SLAP_CTRL_SEARCH, NULL,
+ dupent_parseCtrl, &dupent_cid );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "dupent_initialize: Failed to register control (%d)\n",
+ rc );
+ return -1;
+ }
+
+ dupent.on_bi.bi_type = "dupent";
+
+ dupent.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ dupent.on_bi.bi_op_search = dupent_op_search;
+
+ return overlay_register( &dupent );
+}
+
+#if SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return dupent_initialize();
+}
+#endif /* SLAPD_OVER_DUPENT == SLAPD_MOD_DYNAMIC */
+
+#endif /* SLAPD_OVER_DUPENT */
diff --git a/contrib/slapd-modules/emptyds/Makefile b/contrib/slapd-modules/emptyds/Makefile
new file mode 100644
index 0000000..654f856
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/Makefile
@@ -0,0 +1,78 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted only as authorized by the OpenLDAP
+# Public License.
+#
+# A copy of this license is available in the file LICENSE in the
+# top-level directory of the distribution or, alternatively, at
+# <http://www.OpenLDAP.org/license.html>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+SRCDIR = ./
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_EDS=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = emptyds.la
+MANPAGES = slapo-emptyds.5
+CLEAN = *.o *.lo *.la .libs
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+all: $(PROGRAMS)
+
+d :=
+sp :=
+dir := tests
+include $(dir)/Rules.mk
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+emptyds.la: emptyds.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf $(CLEAN)
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/emptyds/README b/contrib/slapd-modules/emptyds/README
new file mode 100644
index 0000000..914d4e7
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/README
@@ -0,0 +1,66 @@
+emptyds Overlay README
+
+DESCRIPTION
+ This package contains an OpenLDAP overlay called "emptyds" (empty
+ directory string) that eliminates empty values of type directory string
+ (OID 1.3.6.1.4.1.1466.115.121.1.15) from the list of the values in the
+ following manner:
+
+ - add: All empty attribute values will be removed before the add request
+ is executed
+ - mod-replace: A replace with empty values will be modified to a replace
+ without values. As result the attribute will be deleted
+ - mod-add: All empty attribute values will be removed before the mod-add
+ request is executed
+ - mod-delete: All empty attribute values will be removed before the
+ mod-delete request is executed
+
+ If removing all empty values from a modification makes it a no-op, that
+ modification is removed from the list.
+
+ At module load time the emptyds overlay manipulates the syntax checking
+ so that it intercepts the syntax check and allows empty values for
+ attributes of type directory string only. Non-empty values continue to
+ go through the normal check routines. It is therefore very important to
+ configure the overlays in a way that ensures that the emptyds overlay gets
+ the control over the operation before any other overlay. Otherwise it
+ could come to the situation with empty attribute values in the data base.
+
+ David Hawes' addpartial overlay has been used as starting point for this
+ overlay.
+
+BUILDING
+ A Makefile is included, please set your LDAP_SRC directory properly.
+
+INSTALLATION
+ After compiling the emptyds overlay, add the following to your
+ slapd.conf:
+
+ ### slapd.conf
+ ...
+ moduleload emptyds.la
+ ...
+ overlay emptyds
+ ...
+ # before database directive...
+ # this overlay must be the last overlay in the config file to ensure that
+ # requests are modified before other overlays get them.
+ ...
+ ### end slapd.conf
+
+CAVEATS
+ - In order to ensure that emptyds does what it needs to do, it must be
+ the last overlay configured so it will run before the other overlays.
+
+---
+Copyright 2014-2022 The OpenLDAP Foundation.
+Portions Copyright (C) DAASI International GmbH, Tamim Ziai.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+http://www.OpenLDAP.org/license.html.
diff --git a/contrib/slapd-modules/emptyds/emptyds.c b/contrib/slapd-modules/emptyds/emptyds.c
new file mode 100644
index 0000000..bb3202e
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/emptyds.c
@@ -0,0 +1,325 @@
+/* emptyds.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2014-2022 The OpenLDAP Foundation.
+ * Portions Copyright (C) 2014 DAASI International GmbH, Tamim Ziai.
+ * Portions Copyright (C) 2022 Ondřej Kuzník, Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * http://www.OpenLDAP.org/license.html.
+ */
+/* ACKNOLEDGEDMENTS:
+ * This work was initially developed by Tamim Ziai of DAASI International GmbH
+ * for inclusion in OpenLDAP Software.
+ */
+/* slapo-emptyds
+ *
+ * This is an OpenLDAP overlay that accepts empty strings as attribute values
+ * without syntax violation but never actually stores them. This allows
+ * applications that used to work with LDAP implementations allowing empty
+ * strings (such as Novel eDirectory) to continue to work with OpenLDAP without
+ * any modifications. Add and modify change types will be proceeded as follows,
+ * other operations will be forwarded without modifications:
+ *
+ * changeType: add changeType: add
+ * sn: <empty> --> sn: blah
+ * sn: blah
+ *
+ * changeType: modify changeType: modify
+ * add: sn --> add: sn
+ * sn: <empty> sn: blah
+ * sn: blah
+ *
+ * changeType: modify changeType: modify
+ * delete: sn --> delete: sn
+ * sn: <empty> sn: blah
+ * sn: blah
+ *
+ * changeType: modify changeType: modify
+ * replace: sn --> replace: sn
+ * sn: <empty>
+ *
+ */
+
+#include "portable.h"
+#include "slap.h"
+
+static slap_overinst emptyds;
+
+static const char ds_oid[] = "1.3.6.1.4.1.1466.115.121.1.15";
+
+static slap_syntax_validate_func *ssyn_validate_original = NULL;
+static slap_syntax_transform_func *ssyn_pretty_original = NULL;
+static int emptyds_instances = 0;
+
+static unsigned int
+remove_empty_values( Modification *m, Attribute *a )
+{
+ BerVarray vals = m ? m->sm_values : a->a_vals,
+ nvals = m ? m->sm_nvalues : a->a_nvals;
+ unsigned int i, j, numvals = m ? m->sm_numvals : a->a_numvals;
+
+ for ( i = 0; i < numvals && !BER_BVISEMPTY( &vals[i] ); i++ )
+ /* Find first empty */;
+
+ if ( i == numvals ) return i;
+
+ /*
+ * We have an empty value at index i, move all of them to the end of the
+ * list, preserving the order of non-empty values.
+ */
+ j = i + 1;
+ for ( j = i + 1; j < numvals; j++ ) {
+ struct berval tmp;
+
+ if ( BER_BVISEMPTY( &vals[j] ) ) continue;
+
+ tmp = vals[i];
+ vals[i] = vals[j];
+ vals[j] = tmp;
+
+ if ( nvals && vals != nvals ) {
+ tmp = nvals[i];
+ nvals[i] = nvals[j];
+ nvals[j] = tmp;
+ }
+
+ if ( m && a && m->sm_values != a->a_vals ) {
+ tmp = a->a_vals[i];
+ a->a_vals[i] = a->a_vals[j];
+ a->a_vals[j] = tmp;
+
+ if ( a->a_nvals && a->a_vals != a->a_nvals ) {
+ tmp = a->a_nvals[i];
+ a->a_nvals[i] = a->a_nvals[j];
+ a->a_nvals[j] = tmp;
+ }
+ }
+ i++;
+ }
+
+ /* Free empty vals */
+ for ( ; j && i < j--; ) {
+ ber_memfree( vals[j].bv_val );
+ if ( nvals && vals != nvals ) {
+ ber_memfree( nvals[j].bv_val );
+ BER_BVZERO( &nvals[j] );
+ }
+
+ if ( m && a && m->sm_values != a->a_vals ) {
+ if ( m->sm_values[j].bv_val != a->a_vals[j].bv_val ) {
+ ber_memfree( a->a_vals[j].bv_val );
+ BER_BVZERO( &a->a_vals[j] );
+
+ if ( a->a_nvals && a->a_vals != a->a_nvals ) {
+ ber_memfree( a->a_nvals[j].bv_val );
+ BER_BVZERO( &a->a_nvals[j] );
+ }
+ }
+ }
+ BER_BVZERO( &vals[j] );
+ }
+
+ return i;
+}
+
+/**
+ * Remove all operations with empty strings.
+ */
+static int
+emptyds_op_add( Operation *op, SlapReply *rs )
+{
+ Attribute **ap, **nexta, *a;
+ Modifications **mlp, **nextp = NULL, *ml;
+ Entry *e = op->ora_e;
+
+ /*
+ * op->ora_modlist can be NULL, at least accesslog doesn't always populate
+ * it on an add.
+ */
+ for ( ap = &e->e_attrs, a = e->e_attrs, mlp = &op->ora_modlist,
+ ml = op->ora_modlist;
+ a != NULL;
+ ap = nexta, a = *ap, mlp = nextp, ml = ml ? *mlp : NULL ) {
+ AttributeType *at = a->a_desc->ad_type;
+ unsigned int remaining;
+
+ nexta = &a->a_next;
+ if ( ml ) {
+ nextp = &ml->sml_next;
+ }
+
+ if ( at->sat_syntax != slap_schema.si_syn_directoryString ||
+ at->sat_atype.at_usage != LDAP_SCHEMA_USER_APPLICATIONS )
+ continue;
+
+ remaining = remove_empty_values( &ml->sml_mod, a );
+ if ( remaining == a->a_numvals ) continue;
+ /* Empty values found */
+
+ if ( !remaining ) {
+ /* All values are empty */
+ *ap = a->a_next;
+ a->a_next = NULL;
+ nexta = ap;
+
+ if ( ml ) {
+ *mlp = ml->sml_next;
+ ml->sml_next = NULL;
+ nextp = mlp;
+ /* Values are generally shared with attribute */
+ slap_mods_free( ml, ml->sml_values != a->a_vals );
+ }
+ attr_free( a );
+ } else {
+ a->a_numvals = remaining;
+ if ( ml ) {
+ ml->sml_mod.sm_numvals = remaining;
+ }
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+emptyds_op_modify( Operation *op, SlapReply *rs )
+{
+ Modifications **mlp, **nextp, *ml;
+
+ for ( mlp = &op->orm_modlist, ml = op->orm_modlist; ml != NULL;
+ mlp = nextp, ml = *mlp ) {
+ AttributeType *at = ml->sml_desc->ad_type;
+ unsigned int remaining;
+
+ nextp = &ml->sml_next;
+
+ if ( at->sat_syntax != slap_schema.si_syn_directoryString ||
+ at->sat_atype.at_usage != LDAP_SCHEMA_USER_APPLICATIONS )
+ continue;
+
+ remaining = remove_empty_values( &ml->sml_mod, NULL );
+ if ( remaining == ml->sml_numvals ) continue;
+
+ if ( !remaining ) {
+ /* All values are empty */
+ if ( ml->sml_op == LDAP_MOD_REPLACE ) {
+ /* Replace is kept */
+ if ( ml->sml_nvalues && ml->sml_nvalues != ml->sml_values ) {
+ ber_bvarray_free( ml->sml_nvalues );
+ }
+ if ( ml->sml_values ) {
+ ber_bvarray_free( ml->sml_values );
+ }
+
+ ml->sml_numvals = 0;
+ ml->sml_values = NULL;
+ ml->sml_nvalues = NULL;
+ } else {
+ /* Remove modification */
+ *mlp = ml->sml_next;
+ ml->sml_next = NULL;
+ nextp = mlp;
+ slap_mods_free( ml, 1 );
+ }
+ } else {
+ ml->sml_numvals = remaining;
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+emptyds_ssyn_validate( Syntax *syntax, struct berval *in )
+{
+ if ( BER_BVISEMPTY( in ) && syntax == slap_schema.si_syn_directoryString ) {
+ return LDAP_SUCCESS;
+ }
+ return ssyn_validate_original( syntax, in );
+}
+
+static int
+emptyds_ssyn_pretty( Syntax *syntax,
+ struct berval *in,
+ struct berval *out,
+ void *memctx )
+{
+ if ( BER_BVISEMPTY( in ) && syntax == slap_schema.si_syn_directoryString ) {
+ return LDAP_SUCCESS;
+ }
+ return ssyn_pretty_original( syntax, in, out, memctx );
+}
+
+static int
+emptyds_db_init( BackendDB *be, ConfigReply *cr )
+{
+ Syntax *syntax = syn_find( ds_oid );
+
+ if ( syntax == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "emptyds_db_init: "
+ "Syntax %s not found\n",
+ ds_oid );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "emptyds_db_init: "
+ "Found syntax: %s\n",
+ syntax->ssyn_bvoid.bv_val );
+ if ( ssyn_validate_original == NULL && syntax->ssyn_validate != NULL ) {
+ ssyn_validate_original = syntax->ssyn_validate;
+ syntax->ssyn_validate = emptyds_ssyn_validate;
+ }
+ if ( ssyn_pretty_original == NULL && syntax->ssyn_pretty != NULL ) {
+ ssyn_pretty_original = syntax->ssyn_pretty;
+ syntax->ssyn_pretty = &emptyds_ssyn_pretty;
+ }
+ }
+
+ emptyds_instances++;
+ return LDAP_SUCCESS;
+}
+
+static int
+emptyds_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ Syntax *syntax = syn_find( ds_oid );
+
+ if ( --emptyds_instances == 0 && syntax != NULL ) {
+ if ( syntax->ssyn_validate == emptyds_ssyn_validate ) {
+ syntax->ssyn_validate = ssyn_validate_original;
+ }
+ ssyn_validate_original = NULL;
+
+ if ( syntax->ssyn_pretty == emptyds_ssyn_pretty ) {
+ syntax->ssyn_pretty = ssyn_pretty_original;
+ }
+ ssyn_pretty_original = NULL;
+ }
+
+ assert( emptyds_instances >= 0 );
+ return LDAP_SUCCESS;
+}
+
+int
+emptyds_init()
+{
+ emptyds.on_bi.bi_type = "emptyds";
+ emptyds.on_bi.bi_op_add = emptyds_op_add;
+ emptyds.on_bi.bi_op_modify = emptyds_op_modify;
+ emptyds.on_bi.bi_db_init = emptyds_db_init;
+ emptyds.on_bi.bi_db_destroy = emptyds_db_destroy;
+
+ return overlay_register( &emptyds );
+}
+
+int
+init_module( int argc, char *argv[] )
+{
+ return emptyds_init();
+}
diff --git a/contrib/slapd-modules/emptyds/slapo-emptyds.5 b/contrib/slapd-modules/emptyds/slapo-emptyds.5
new file mode 100644
index 0000000..75b1059
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/slapo-emptyds.5
@@ -0,0 +1,68 @@
+.TH SLAPO-EDS 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2022 The OpenLDAP Foundation, All Rights Reserved.
+.\" Copyright 2018 Tamim Ziai
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo-emptyds \- Remove Empty values from Directory String attributes
+Overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+Some non-conformant clients will provide empty values for Directory String
+attributes with certain operations. This overlay makes empty values acceptable
+for the Directory String syntax and will adjust all operations to make sure
+these values are never actually stored in the database.
+.LP
+.nf
+.ft tt
+ dn: cn=alex,cn=people,dc=example,dc=org
+ changeType: add changeType: add
+ sn: <empty> --> sn: blah
+ sn: blah
+
+ dn: cn=alex,cn=people,dc=example,dc=org
+ changeType: modify changeType: modify
+ add: sn --> add: sn
+ sn: <empty> sn: blah
+ sn: blah
+
+ dn: cn=alex,cn=people,dc=example,dc=org
+ changeType: modify changeType: modify
+ delete: sn --> delete: sn
+ sn: <empty> sn: blah
+ sn: blah
+
+ dn: cn=alex,cn=people,dc=example,dc=org
+ changeType: modify changeType: modify
+ replace: sn --> replace: sn
+ sn: <empty>
+
+ dn: cn=alex,cn=people,dc=example,dc=org
+ changeType: modify changeType: modify
+ replace: sn --> replace: sn
+ sn: <empty> sn: blah
+ sn: blah
+.ft
+.fi
+.LP
+.SH CONFIGURATION
+This overlay has no specific configuration, however in order to ensure that it
+does what it needs to do, it should be the last overlay configured so it will
+run before the other overlays.
+.SH EXAMPLES
+.LP
+.RS
+.nf
+overlay emptyds
+.RE
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5).
+.SH ACKNOWLEDGEMENTS
+This module was written in 2014 by Tamim Ziai for DAASI International and
+updated in 2022 by Ondřej Kuzník for inclusion in the OpenLDAP project.
+.so ../Project
diff --git a/contrib/slapd-modules/emptyds/tests/Rules.mk b/contrib/slapd-modules/emptyds/tests/Rules.mk
new file mode 100644
index 0000000..c25c1d2
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/tests/Rules.mk
@@ -0,0 +1,23 @@
+sp := $(sp).x
+dirstack_$(sp) := $(d)
+d := $(dir)
+
+.PHONY: test
+
+CLEAN += clients servers tests/progs tests/schema tests/testdata tests/testrun
+
+test: all clients servers tests/progs
+
+test:
+ cd tests; \
+ SRCDIR=$(abspath $(LDAP_SRC)) \
+ LDAP_BUILD=$(abspath $(LDAP_BUILD)) \
+ TOPDIR=$(abspath $(SRCDIR)) \
+ LIBTOOL=$(abspath $(LIBTOOL)) \
+ $(abspath $(SRCDIR))/tests/run all
+
+servers clients tests/progs:
+ ln -s $(abspath $(LDAP_BUILD))/$@ $@
+
+d := $(dirstack_$(sp))
+sp := $(basename $(sp))
diff --git a/contrib/slapd-modules/emptyds/tests/data/emptyds.conf b/contrib/slapd-modules/emptyds/tests/data/emptyds.conf
new file mode 100644
index 0000000..221fe81
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/tests/data/emptyds.conf
@@ -0,0 +1,54 @@
+# basic slapd config -- for testing of slapo-emptyds
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+include @DATADIR@/test.schema
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#accesslogmod#modulepath ../servers/slapd/overlays/
+#accesslogmod#moduleload accesslog.la
+moduleload ../emptyds.la
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+
+overlay accesslog
+logdb cn=log
+logops writes
+logsuccess true
+
+overlay emptyds
+
+database @BACKEND@
+suffix "cn=log"
+rootdn "cn=Manager,dc=example,dc=com"
+#~null~#directory @TESTDIR@/db.1.b
+
+## This one makes no difference except we want to make sure we can
+## safely instantiate the overlay on multiple databases
+overlay emptyds
+
+database monitor
diff --git a/contrib/slapd-modules/emptyds/tests/data/test001.ldif b/contrib/slapd-modules/emptyds/tests/data/test001.ldif
new file mode 100644
index 0000000..b7f289a
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/tests/data/test001.ldif
@@ -0,0 +1,71 @@
+# slapd prevents us from adding the same value multiple times
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+add: description
+description: one
+description:
+description: two
+description: three
+description: four
+# a space is distinct from an empty value
+description:: ICAg
+-
+replace: drink
+drink: Earl Grey, hot
+-
+delete: description
+description:
+-
+replace: drink
+drink: Earl Grey, hot
+
+# there is no such restriction on deletes, so we exercise this part of the overlay here
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+delete: description
+description:
+description: four
+description:
+description: three
+description: two
+description:
+description:
+description: one
+description:
+-
+add: description
+description:
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+replace: drink
+drink:
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: member
+-
+add: member
+# an empty DN should not be stripped
+member:
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+
+dn: cn=Gern Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+changetype: add
+objectclass: testPerson
+cn: Gern Jensen
+sn: Jensen
+uid: gjensen
+title:
+postaladdress: ITD $ 535 W. William St $ Anytown, MI 48103
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Coffee
+homepostaladdress: 844 Brown St. Apt. 4 $ Anytown, MI 48104
+description: Very odd
+description:
+description: More than you think
+facsimiletelephonenumber: +1 313 555 7557
+telephonenumber: +1 313 555 8343
+mail: gjensen@mailgw.example.com
+homephone: +1 313 555 8844
+testTime: 20050304001801.234Z
diff --git a/contrib/slapd-modules/emptyds/tests/data/test001.out b/contrib/slapd-modules/emptyds/tests/data/test001.out
new file mode 100644
index 0000000..6f41247
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/tests/data/test001.out
@@ -0,0 +1,54 @@
+dn: reqStart=timestamp,cn=log
+reqDN: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+reqMod: description:+ one
+reqMod: description:+ two
+reqMod: description:+ three
+reqMod: description:+ four
+# "description:+ " that's a space, then 3 spaces for value
+reqMod:: ZGVzY3JpcHRpb246KyAgICA=
+reqMod: drink:= Earl Grey, hot
+# second mod was removed, so we have two replaces in succession now and need
+# to separate them (":")
+reqMod:: Og==
+reqMod: drink:= Earl Grey, hot
+
+dn: reqStart=timestamp,cn=log
+reqDN: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+reqMod: description:- four
+reqMod: description:- three
+reqMod: description:- two
+reqMod: description:- one
+# second mod is removed
+
+dn: reqStart=timestamp,cn=log
+reqDN: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+reqMod: drink:=
+
+dn: reqStart=timestamp,cn=log
+reqDN: cn=All Staff,ou=Groups,dc=example,dc=com
+reqMod: member:-
+# "member:+ " adding an empty DN
+reqMod:: bWVtYmVyOisg
+reqMod: member:+ cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example
+ ,dc=com
+
+dn: reqStart=timestamp,cn=log
+reqDN: cn=Gern Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+reqMod: objectClass:+ testPerson
+reqMod: cn:+ Gern Jensen
+reqMod: sn:+ Jensen
+reqMod: uid:+ gjensen
+reqMod: postalAddress:+ ITD $ 535 W. William St $ Anytown, MI 48103
+reqMod: seeAlso:+ cn=All Staff,ou=Groups,dc=example,dc=com
+reqMod: drink:+ Coffee
+reqMod: homePostalAddress:+ 844 Brown St. Apt. 4 $ Anytown, MI 48104
+reqMod: description:+ Very odd
+reqMod: description:+ More than you think
+reqMod: facsimileTelephoneNumber:+ +1 313 555 7557
+reqMod: telephoneNumber:+ +1 313 555 8343
+reqMod: mail:+ gjensen@mailgw.example.com
+reqMod: homePhone:+ +1 313 555 8844
+reqMod: testTime:+ 20050304001801.234Z
+reqMod: structuralObjectClass:+ testPerson
+
diff --git a/contrib/slapd-modules/emptyds/tests/run b/contrib/slapd-modules/emptyds/tests/run
new file mode 100755
index 0000000..e28820c
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/tests/run
@@ -0,0 +1,218 @@
+#!/bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+USAGE="$0 [-b <backend>] [-c] [-k] [-l #] [-p] [-s {ro|rp}] [-u] [-w] <script>"
+
+TOPSRCDIR="${SRCDIR-$LDAP_SRC}"
+SRCDIR="${TOPSRCDIR}/tests"
+eval `grep EGREP_CMD= ${LDAP_BUILD}/tests/run`
+eval `$EGREP_CMD -e '^LN_S=' ${LDAP_BUILD}/tests/run`
+
+export SRCDIR TOPSRCDIR LN_S EGREP_CMD
+
+. "${SRCDIR}/scripts/defines.sh"
+
+BACKEND=
+CLEAN=no
+WAIT=0
+KILLSERVERS=yes
+PRESERVE=${PRESERVE-no}
+SYNCMODE=${SYNCMODE-rp}
+USERDATA=no
+LOOP=1
+COUNTER=1
+
+while test $# -gt 0 ; do
+ case "$1" in
+ -b | -backend)
+ BACKEND="$2"
+ shift; shift ;;
+
+ -c | -clean)
+ CLEAN=yes
+ shift ;;
+
+ -k | -kill)
+ KILLSERVERS=no
+ shift ;;
+ -l | -loop)
+ NUM="`echo $2 | sed 's/[0-9]//g'`"
+ if [ -z "$NUM" ]; then
+ LOOP=$2
+ else
+ echo "Loop variable not an int: $2"
+ echo "$USAGE"; exit 1
+ fi
+ shift ;
+ shift ;;
+
+ -p | -preserve)
+ PRESERVE=yes
+ shift ;;
+
+ -s | -syncmode)
+ case "$2" in
+ ro | rp)
+ SYNCMODE="$2"
+ ;;
+ *)
+ echo "unknown sync mode $2"
+ echo "$USAGE"; exit 1
+ ;;
+ esac
+ shift; shift ;;
+
+ -u | -userdata)
+ USERDATA=yes
+ shift ;;
+
+ -w | -wait)
+ WAIT=1
+ shift ;;
+
+ -)
+ shift
+ break ;;
+
+ -*)
+ echo "$USAGE"; exit 1
+ ;;
+
+ *)
+ break ;;
+ esac
+done
+
+eval `$EGREP_CMD -e '^AC' ${LDAP_BUILD}/tests/run`
+export `$EGREP_CMD -e '^AC' ${LDAP_BUILD}/tests/run | sed 's/=.*//'`
+
+if test -z "$BACKEND" ; then
+ for b in mdb ; do
+ if eval "test \"\$AC_$b\" != no" ; then
+ BACKEND=$b
+ break
+ fi
+ done
+ if test -z "$BACKEND" ; then
+ echo "No suitable default database backend configured" >&2
+ exit 1
+ fi
+fi
+
+BACKENDTYPE=`eval 'echo $AC_'$BACKEND`
+if test "x$BACKENDTYPE" = "x" ; then
+ BACKENDTYPE="unknown"
+fi
+
+# Backend features. indexdb: indexing and unchecked limit.
+# maindb: main storage backend. Currently index,limits,mode,paged results.
+INDEXDB=noindexdb MAINDB=nomaindb
+case $BACKEND in
+ mdb) INDEXDB=indexdb MAINDB=maindb ;;
+esac
+
+export BACKEND BACKENDTYPE INDEXDB MAINDB \
+ WAIT KILLSERVERS PRESERVE SYNCMODE USERDATA \
+ SRCDIR
+
+if test $# = 0 ; then
+ echo "$USAGE"; exit 1
+fi
+
+# need defines.sh for the definitions of the directories
+. $SRCDIR/scripts/defines.sh
+
+SCRIPTDIR="${TOPDIR}/tests/scripts"
+
+export SCRIPTDIR
+
+SCRIPTNAME="$1"
+shift
+
+if test -x "${SCRIPTDIR}/${SCRIPTNAME}" ; then
+ SCRIPT="${SCRIPTDIR}/${SCRIPTNAME}"
+elif test -x "`echo ${SCRIPTDIR}/test*-${SCRIPTNAME}`"; then
+ SCRIPT="`echo ${SCRIPTDIR}/test*-${SCRIPTNAME}`"
+elif test -x "`echo ${SCRIPTDIR}/${SCRIPTNAME}-*`"; then
+ SCRIPT="`echo ${SCRIPTDIR}/${SCRIPTNAME}-*`"
+else
+ echo "run: ${SCRIPTNAME} not found (or not executable)"
+ exit 1;
+fi
+
+if test ! -r ${DATADIR}/test.ldif ; then
+ ${LN_S} ${SRCDIR}/data ${DATADIR}
+fi
+if test ! -r ${SCHEMADIR}/core.schema ; then
+ ${LN_S} ${TOPSRCDIR}/servers/slapd/schema ${SCHEMADIR}
+fi
+if test ! -r ./data; then
+ ${LN_S} ${TOPDIR}/tests/data ./
+fi
+
+if test -d ${TESTDIR} ; then
+ if test $PRESERVE = no ; then
+ echo "Cleaning up test run directory leftover from previous run."
+ /bin/rm -rf ${TESTDIR}
+ elif test $PRESERVE = yes ; then
+ echo "Cleaning up only database directories leftover from previous run."
+ /bin/rm -rf ${TESTDIR}/db.*
+ fi
+fi
+mkdir -p ${TESTDIR}
+
+if test $USERDATA = yes ; then
+ if test ! -d userdata ; then
+ echo "User data directory (userdata) does not exist."
+ exit 1
+ fi
+ cp -R userdata/* ${TESTDIR}
+fi
+
+# disable LDAP initialization
+LDAPNOINIT=true; export LDAPNOINIT
+
+echo "Running ${SCRIPT} for ${BACKEND}..."
+while [ $COUNTER -le $LOOP ]; do
+ if [ $LOOP -gt 1 ]; then
+ echo "Running $COUNTER of $LOOP iterations"
+ fi
+ $SCRIPT $*
+ RC=$?
+
+ if test $CLEAN = yes ; then
+ echo "Cleaning up test run directory from this run."
+ /bin/rm -rf ${TESTDIR}
+ echo "Cleaning up symlinks."
+ /bin/rm -f ${DATADIR} ${SCHEMADIR}
+ fi
+
+ if [ $RC -ne 0 ]; then
+ if [ $LOOP -gt 1 ]; then
+ echo "Failed after $COUNTER of $LOOP iterations"
+ fi
+ exit $RC
+ else
+ COUNTER=`expr $COUNTER + 1`
+ if [ $COUNTER -le $LOOP ]; then
+ echo "Cleaning up test run directory from this run."
+ /bin/rm -rf ${TESTDIR}
+ fi
+ fi
+done
+exit $RC
diff --git a/contrib/slapd-modules/emptyds/tests/scripts/all b/contrib/slapd-modules/emptyds/tests/scripts/all
new file mode 100755
index 0000000..a5c1774
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/tests/scripts/all
@@ -0,0 +1,92 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+. $SRCDIR/scripts/defines.sh
+
+TB="" TN=""
+if test -t 1 ; then
+ TB=`$SHTOOL echo -e "%B" 2>/dev/null`
+ TN=`$SHTOOL echo -e "%b" 2>/dev/null`
+fi
+
+FAILCOUNT=0
+SKIPCOUNT=0
+SLEEPTIME=10
+
+echo ">>>>> Executing all LDAP tests for $BACKEND"
+
+if [ -n "$NOEXIT" ]; then
+ echo "Result Test" > $TESTWD/results
+fi
+
+for CMD in ${SCRIPTDIR}/test*; do
+ case "$CMD" in
+ *~) continue;;
+ *.bak) continue;;
+ *.orig) continue;;
+ *.sav) continue;;
+ *) test -f "$CMD" || continue;;
+ esac
+
+ # remove cruft from prior test
+ if test $PRESERVE = yes ; then
+ /bin/rm -rf $TESTDIR/db.*
+ else
+ /bin/rm -rf $TESTDIR
+ fi
+
+ BCMD=`basename $CMD`
+ if [ -x "$CMD" ]; then
+ echo ">>>>> Starting ${TB}$BCMD${TN} for $BACKEND..."
+ $CMD
+ RC=$?
+ if test $RC -eq 0 ; then
+ echo ">>>>> $BCMD completed ${TB}OK${TN} for $BACKEND."
+ else
+ echo ">>>>> $BCMD ${TB}failed${TN} for $BACKEND"
+ FAILCOUNT=`expr $FAILCOUNT + 1`
+
+ if [ -n "$NOEXIT" ]; then
+ echo "Continuing."
+ else
+ echo "(exit $RC)"
+ exit $RC
+ fi
+ fi
+ else
+ echo ">>>>> Skipping ${TB}$BCMD${TN} for $BACKEND."
+ SKIPCOUNT=`expr $SKIPCOUNT + 1`
+ RC="-"
+ fi
+
+ if [ -n "$NOEXIT" ]; then
+ echo "$RC $BCMD" >> $TESTWD/results
+ fi
+
+# echo ">>>>> waiting $SLEEPTIME seconds for things to exit"
+# sleep $SLEEPTIME
+ echo ""
+done
+
+if [ -n "$NOEXIT" ]; then
+ if [ "$FAILCOUNT" -gt 0 ]; then
+ cat $TESTWD/results
+ echo "$FAILCOUNT tests for $BACKEND ${TB}failed${TN}. Please review the test log."
+ else
+ echo "All executed tests for $BACKEND ${TB}succeeded${TN}."
+ fi
+fi
+
+echo "$SKIPCOUNT tests for $BACKEND were ${TB}skipped${TN}."
diff --git a/contrib/slapd-modules/emptyds/tests/scripts/test001-emptyds b/contrib/slapd-modules/emptyds/tests/scripts/test001-emptyds
new file mode 100755
index 0000000..b8d715a
--- /dev/null
+++ b/contrib/slapd-modules/emptyds/tests/scripts/test001-emptyds
@@ -0,0 +1,137 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2019 by Tamim Ziai for DAASI International
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+LDIF=${TOPDIR}/tests/data/test001.out
+
+if test $ACCESSLOG = accesslogno; then
+ echo "Accesslog overlay not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1A $DBDIR1B
+
+. $CONFFILTER $BACKEND < "${TOPDIR}/tests/data/emptyds.conf" > $CONF1
+
+echo "Running slapadd to build slapd database... "
+$SLAPADD -f $CONF1 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL >> $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+echo "Checking add/modify handling... "
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ > $TESTOUT -f "${TOPDIR}/tests/data/test001.ldif"
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Checking modrdn handling (should still fail with invalidDNSyntax)... "
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modrdn
+newrdn: cn=
+deleteoldrdn: 0
+EOMOD
+RC=$?
+case $RC in
+34)
+ echo " ldapmodify failed ($RC)"
+ ;;
+0)
+ echo " ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+*)
+ echo " ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Dumping accesslog..."
+
+$LDAPSEARCH -b "cn=log" -H $URI1 \
+ 'objectClass=auditWriteObject' reqDN reqMod | \
+ grep -v -e 'entryCSN' -e '\(create\|modify\)Timestamp' \
+ -e '\(modifier\|creator\)sName' -e 'entryUUID' | \
+ sed -e 's/reqStart=[^,]*,/reqStart=timestamp,/' \
+ > $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+# Expectations:
+# - all empty values for directoryString pruned
+# - empty adds/deletes removed, empty replaces kept
+# - remaining values keep the same order as submitted
+# - other syntaxes (especially DNs) are kept intact
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+$LDIFFILTER < $LDIF > $LDIFFLT
+
+echo "Comparing filter output..."
+$CMP $LDIFFLT $SEARCHFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/kinit/Makefile b/contrib/slapd-modules/kinit/Makefile
new file mode 100644
index 0000000..7b25ced
--- /dev/null
+++ b/contrib/slapd-modules/kinit/Makefile
@@ -0,0 +1,46 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB) -lkrb5
+
+PROGRAMS = kinit.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+kinit.la: kinit.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/kinit/README b/contrib/slapd-modules/kinit/README
new file mode 100644
index 0000000..7e3ebe8
--- /dev/null
+++ b/contrib/slapd-modules/kinit/README
@@ -0,0 +1,36 @@
+This directory contains the "kinit" slapd module. It is a simple plugin to
+have slapd request a Kerberos TGT and keep it renewed as long as slapd is
+running.
+
+The current implementation has only been tested against the MIT variant of
+the Kerberos libraries. (Heimdal support might come later)
+
+To use the overlay just load it into the slapd process:
+
+ moduleload </path/to>/kinit.so <principal> </path/to/key.tab>
+
+The module accepts two arguments. The first one being the principal for which
+to request the TGT (it defaults to "ldap/<your hostname>@<DEFAULTREALM>")
+and the second one is the path to the keytab file to use for
+authentication, defaulting to whatever your system wide kerberos settings
+default to).
+
+Use Makefile or the following commands should work to
+build it from inside the unpacked slapd sources, provided the required KRB5
+header files and libraries are installed on your system:
+
+ gcc -fPIC -c -I ../../../include/ -I ../../../servers/slapd kinit.c
+ gcc -shared -o kinit.so kinit.o -lkrb5
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2010-2022 The OpenLDAP Foundation.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
diff --git a/contrib/slapd-modules/kinit/kinit.c b/contrib/slapd-modules/kinit/kinit.c
new file mode 100644
index 0000000..630b6bf
--- /dev/null
+++ b/contrib/slapd-modules/kinit/kinit.c
@@ -0,0 +1,295 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2010-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include <portable.h>
+
+#ifndef SLAPD_MOD_KINIT
+#define SLAPD_MOD_KINIT SLAPD_MOD_DYNAMIC
+#endif
+
+#ifdef SLAPD_MOD_KINIT
+
+#include <slap.h>
+#include "ldap_rq.h"
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <krb5/krb5.h>
+
+typedef struct kinit_data {
+ krb5_context ctx;
+ krb5_ccache ccache;
+ krb5_keytab keytab;
+ krb5_principal princ;
+ krb5_get_init_creds_opt *opts;
+} kinit_data;
+
+static char* principal;
+static char* kt_name;
+static kinit_data *kid;
+
+static void
+log_krb5_errmsg( krb5_context ctx, const char* func, krb5_error_code rc )
+{
+ const char* errmsg = krb5_get_error_message(ctx, rc);
+ Log(LDAP_DEBUG_ANY, LDAP_LEVEL_ERR, "slapd-kinit: %s: %s\n", func, errmsg );
+ krb5_free_error_message(ctx, errmsg);
+ return;
+}
+
+static int
+kinit_check_tgt(kinit_data *kid, int *remaining)
+{
+ int ret=3;
+ krb5_principal princ;
+ krb5_error_code rc;
+ krb5_cc_cursor cursor;
+ krb5_creds creds;
+ char *name;
+ time_t now=time(NULL);
+
+ rc = krb5_cc_get_principal(kid->ctx, kid->ccache, &princ);
+ if (rc) {
+ log_krb5_errmsg(kid->ctx, "krb5_cc_get_principal", rc);
+ return 2;
+ } else {
+ if (!krb5_principal_compare(kid->ctx, kid->princ, princ)) {
+ Log(LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "Principal in ccache does not match requested principal\n");
+ krb5_free_principal(kid->ctx, princ);
+ return 2;
+ }
+ }
+
+ rc = krb5_cc_start_seq_get(kid->ctx, kid->ccache, &cursor);
+ if (rc) {
+ log_krb5_errmsg(kid->ctx, "krb5_cc_start_seq_get", rc);
+ krb5_free_principal(kid->ctx, princ);
+ return -1;
+ }
+
+ while (!(rc = krb5_cc_next_cred(kid->ctx, kid->ccache, &cursor, &creds))) {
+ if (krb5_is_config_principal(kid->ctx, creds.server)) {
+ krb5_free_cred_contents(kid->ctx, &creds);
+ continue;
+ }
+
+ if (creds.server->length==2 &&
+ (!strcmp(creds.server->data[0].data, "krbtgt")) &&
+ (!strcmp(creds.server->data[1].data, princ->realm.data))) {
+
+ krb5_unparse_name(kid->ctx, creds.server, &name);
+
+ *remaining = (time_t)creds.times.endtime-now;
+ if ( *remaining <= 0) {
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
+ "kinit_qtask: TGT (%s) expired\n", name);
+ } else {
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
+ "kinit_qtask: TGT (%s) expires in %dh:%02dm:%02ds\n",
+ name, *remaining/3600, (*remaining%3600)/60, *remaining%60);
+ }
+ free(name);
+
+ if (*remaining <= 30) {
+ if (creds.times.renew_till-60 > now) {
+ int renewal=creds.times.renew_till-now;
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
+ "kinit_qtask: Time remaining for renewal: %dh:%02dm:%02ds\n",
+ renewal/3600, (renewal%3600)/60, renewal%60);
+ ret = 1;
+ } else {
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
+ "kinit_qtask: Only short time left for renewal. "
+ "Trying to re-init.\n");
+ ret = 2;
+ }
+ } else {
+ ret=0;
+ }
+ krb5_free_cred_contents(kid->ctx, &creds);
+ break;
+ }
+ krb5_free_cred_contents(kid->ctx, &creds);
+
+ }
+ krb5_cc_end_seq_get(kid->ctx, kid->ccache, &cursor);
+ krb5_free_principal(kid->ctx, princ);
+ return ret;
+}
+
+void*
+kinit_qtask( void *ctx, void *arg )
+{
+ struct re_s *rtask = arg;
+ kinit_data *kid = (kinit_data*)rtask->arg;
+ krb5_error_code rc;
+ krb5_creds creds;
+ int nextcheck, remaining, renew=0;
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "kinit_qtask: running TGT check\n" );
+
+ memset(&creds, 0, sizeof(creds));
+
+ renew = kinit_check_tgt(kid, &remaining);
+
+ if (renew > 0) {
+ if (renew==1) {
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
+ "kinit_qtask: Trying to renew TGT: ");
+ rc = krb5_get_renewed_creds(kid->ctx, &creds, kid->princ, kid->ccache, NULL);
+ if (rc!=0) {
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Failed\n" );
+ log_krb5_errmsg( kid->ctx,
+ "kinit_qtask, Renewal failed: krb5_get_renewed_creds", rc );
+ renew++;
+ } else {
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Success\n" );
+ krb5_cc_initialize(kid->ctx, kid->ccache, creds.client);
+ krb5_cc_store_cred(kid->ctx, kid->ccache, &creds);
+ krb5_free_cred_contents(kid->ctx, &creds);
+ renew=kinit_check_tgt(kid, &remaining);
+ }
+ }
+ if (renew > 1) {
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
+ "kinit_qtask: Trying to get new TGT: ");
+ rc = krb5_get_init_creds_keytab( kid->ctx, &creds, kid->princ,
+ kid->keytab, 0, NULL, kid->opts);
+ if (rc) {
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Failed\n" );
+ log_krb5_errmsg(kid->ctx, "krb5_get_init_creds_keytab", rc);
+ } else {
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Success\n" );
+ renew=kinit_check_tgt(kid, &remaining);
+ }
+ krb5_free_cred_contents(kid->ctx, &creds);
+ }
+ }
+ if (renew == 0) {
+ nextcheck = remaining-30;
+ } else {
+ nextcheck = 60;
+ }
+
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
+ ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
+ }
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG,
+ "kinit_qtask: Next TGT check in %dh:%02dm:%02ds\n",
+ nextcheck/3600, (nextcheck%3600)/60, nextcheck%60);
+ rtask->interval.tv_sec = nextcheck;
+ ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 );
+ slap_wake_listener();
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ return NULL;
+}
+
+int
+kinit_initialize(void)
+{
+ Log( LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "kinit_initialize\n" );
+ krb5_error_code rc;
+ struct re_s *task = NULL;
+
+ kid = ch_calloc(1, sizeof(kinit_data) );
+
+ rc = krb5_init_context( &kid->ctx );
+ if ( !rc )
+ rc = krb5_cc_default(kid->ctx, &kid->ccache );
+
+ if ( !rc ) {
+ if (!principal) {
+ int len=STRLENOF("ldap/")+global_host_bv.bv_len+1;
+ principal=ch_calloc(len, 1);
+ snprintf(principal, len, "ldap/%s", global_host_bv.bv_val);
+ Log(LDAP_DEBUG_TRACE, LDAP_LEVEL_DEBUG, "Principal <%s>\n", principal );
+
+ }
+ rc = krb5_parse_name(kid->ctx, principal, &kid->princ);
+ }
+
+ if ( !rc && kt_name) {
+ rc = krb5_kt_resolve(kid->ctx, kt_name, &kid->keytab);
+ }
+
+ if ( !rc )
+ rc = krb5_get_init_creds_opt_alloc(kid->ctx, &kid->opts);
+
+ if ( !rc )
+ rc = krb5_get_init_creds_opt_set_out_ccache( kid->ctx, kid->opts, kid->ccache);
+
+ if ( !rc ) {
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ task = ldap_pvt_runqueue_insert( &slapd_rq, 10, kinit_qtask, (void*)kid,
+ "kinit_qtask", "ldap/bronsted.g17.lan@G17.LAN" );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+
+ if (rc) {
+ log_krb5_errmsg(kid->ctx, "kinit_initialize", rc);
+ rc = -1;
+ }
+ return rc;
+}
+
+#if SLAPD_MOD_KINIT == SLAPD_MOD_DYNAMIC
+int init_module(int argc, char *argv[]) {
+ if (argc > 0) {
+ principal = ch_strdup(argv[0]);
+ }
+ if (argc > 1) {
+ kt_name = ch_strdup(argv[1]);
+ }
+ if (argc > 2) {
+ return -1;
+ }
+ return kinit_initialize();
+}
+
+int
+term_module() {
+ if (principal)
+ ch_free(principal);
+ if (kt_name)
+ ch_free(kt_name);
+ if (kid) {
+ struct re_s *task;
+
+ task=ldap_pvt_runqueue_find( &slapd_rq, kinit_qtask, (void*)kid);
+ if (task) {
+ if ( ldap_pvt_runqueue_isrunning(&slapd_rq, task) ) {
+ ldap_pvt_runqueue_stoptask(&slapd_rq, task);
+ }
+ ldap_pvt_runqueue_remove(&slapd_rq, task);
+ }
+ if ( kid->ctx ) {
+ if ( kid->princ )
+ krb5_free_principal(kid->ctx, kid->princ);
+ if ( kid->ccache )
+ krb5_cc_close(kid->ctx, kid->ccache);
+ if ( kid->keytab )
+ krb5_kt_close(kid->ctx, kid->keytab);
+ if ( kid->opts )
+ krb5_get_init_creds_opt_free(kid->ctx, kid->opts);
+ krb5_free_context(kid->ctx);
+ }
+ ch_free(kid);
+ }
+ return 0;
+}
+#endif
+
+#endif /* SLAPD_MOD_KINIT */
+
diff --git a/contrib/slapd-modules/lastbind/Makefile b/contrib/slapd-modules/lastbind/Makefile
new file mode 100644
index 0000000..c273cd3
--- /dev/null
+++ b/contrib/slapd-modules/lastbind/Makefile
@@ -0,0 +1,68 @@
+# $OpenLDAP$
+# Copyright 2009 Jonathan Clarke <jonathan@phillipoux.net>.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted only as authorized by the OpenLDAP
+# Public License.
+#
+# A copy of this license is available in the file LICENSE in the
+# top-level directory of the distribution or, alternatively, at
+# <http://www.OpenLDAP.org/license.html>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_LASTBIND=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = lastbind.la
+MANPAGES = slapo-lastbind.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+lastbind.la: lastbind.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/lastbind/lastbind.c b/contrib/slapd-modules/lastbind/lastbind.c
new file mode 100644
index 0000000..bd8e782
--- /dev/null
+++ b/contrib/slapd-modules/lastbind/lastbind.c
@@ -0,0 +1,320 @@
+/* lastbind.c - Record timestamp of the last successful bind to entries */
+/* $OpenLDAP$ */
+/*
+ * Copyright 2009 Jonathan Clarke <jonathan@phillipoux.net>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work is loosely derived from the ppolicy overlay.
+ */
+
+#include "portable.h"
+
+/*
+ * This file implements an overlay that stores the timestamp of the
+ * last successful bind operation in a directory entry.
+ *
+ * Optimization: to avoid performing a write on each bind,
+ * a precision for this timestamp may be configured, causing it to
+ * only be updated if it is older than a given number of seconds.
+ */
+
+#ifdef SLAPD_OVER_LASTBIND
+
+#include <ldap.h>
+#include "lutil.h"
+#include "slap.h"
+#include <ac/errno.h>
+#include <ac/time.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include "slap-config.h"
+
+/* Per-instance configuration information */
+typedef struct lastbind_info {
+ /* precision to update timestamp in authTimestamp attribute */
+ int timestamp_precision;
+ int forward_updates; /* use frontend for authTimestamp updates */
+} lastbind_info;
+
+/* Operational attributes */
+static AttributeDescription *ad_authTimestamp;
+
+/* This is the definition used by ISODE, as supplied to us in
+ * ITS#6238 Followup #9
+ */
+static struct schema_info {
+ char *def;
+ AttributeDescription **ad;
+} lastBind_OpSchema[] = {
+ { "( 1.3.6.1.4.1.453.16.2.188 "
+ "NAME 'authTimestamp' "
+ "DESC 'last successful authentication using any method/mech' "
+ "EQUALITY generalizedTimeMatch "
+ "ORDERING generalizedTimeOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE dsaOperation )",
+ &ad_authTimestamp},
+ { NULL, NULL }
+};
+
+/* configuration attribute and objectclass */
+static ConfigTable lastbindcfg[] = {
+ { "lastbind-precision", "seconds", 2, 2, 0,
+ ARG_INT|ARG_OFFSET,
+ (void *)offsetof(lastbind_info, timestamp_precision),
+ "( OLcfgCtAt:5.1 "
+ "NAME 'olcLastBindPrecision' "
+ "DESC 'Precision of authTimestamp attribute' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "lastbind_forward_updates", "on|off", 1, 2, 0,
+ ARG_ON_OFF|ARG_OFFSET,
+ (void *)offsetof(lastbind_info,forward_updates),
+ "( OLcfgAt:5.2 NAME 'olcLastBindForwardUpdates' "
+ "DESC 'Allow authTimestamp updates to be forwarded via updateref' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs lastbindocs[] = {
+ { "( OLcfgCtOc:5.1 "
+ "NAME 'olcLastBindConfig' "
+ "DESC 'Last Bind configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcLastBindPrecision $ olcLastBindForwardUpdates) )",
+ Cft_Overlay, lastbindcfg, NULL, NULL },
+ { NULL, 0, NULL }
+};
+
+static time_t
+parse_time( char *atm )
+{
+ struct lutil_tm tm;
+ struct lutil_timet tt;
+ time_t ret = (time_t)-1;
+
+ if ( lutil_parsetime( atm, &tm ) == 0) {
+ lutil_tm2time( &tm, &tt );
+ ret = tt.tt_sec;
+ }
+ return ret;
+}
+
+static int
+lastbind_bind_response( Operation *op, SlapReply *rs )
+{
+ Modifications *mod = NULL;
+ BackendInfo *bi = op->o_bd->bd_info;
+ Entry *e;
+ int rc;
+
+ /* we're only interested if the bind was successful */
+ if ( rs->sr_err != LDAP_SUCCESS )
+ return SLAP_CB_CONTINUE;
+
+ rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
+ op->o_bd->bd_info = bi;
+
+ if ( rc != LDAP_SUCCESS ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ {
+ lastbind_info *lbi = (lastbind_info *) op->o_callback->sc_private;
+
+ time_t now, bindtime = (time_t)-1;
+ Attribute *a;
+ Modifications *m;
+ char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+ struct berval timestamp;
+
+ /* get the current time */
+ now = slap_get_time();
+
+ /* get authTimestamp attribute, if it exists */
+ if ((a = attr_find( e->e_attrs, ad_authTimestamp)) != NULL) {
+ bindtime = parse_time( a->a_nvals[0].bv_val );
+
+ if (bindtime != (time_t)-1) {
+ /* if the recorded bind time is within our precision, we're done
+ * it doesn't need to be updated (save a write for nothing) */
+ if ((now - bindtime) < lbi->timestamp_precision) {
+ goto done;
+ }
+ }
+ }
+
+ /* update the authTimestamp in the user's entry with the current time */
+ timestamp.bv_val = nowstr;
+ timestamp.bv_len = sizeof(nowstr);
+ slap_timestamp( &now, &timestamp );
+
+ m = ch_calloc( sizeof(Modifications), 1 );
+ m->sml_op = LDAP_MOD_REPLACE;
+ m->sml_flags = 0;
+ m->sml_type = ad_authTimestamp->ad_cname;
+ m->sml_desc = ad_authTimestamp;
+ m->sml_numvals = 1;
+ m->sml_values = ch_calloc( sizeof(struct berval), 2 );
+ m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
+
+ ber_dupbv( &m->sml_values[0], &timestamp );
+ ber_dupbv( &m->sml_nvalues[0], &timestamp );
+ m->sml_next = mod;
+ mod = m;
+ }
+
+done:
+ be_entry_release_r( op, e );
+
+ /* perform the update, if necessary */
+ if ( mod ) {
+ Operation op2 = *op;
+ SlapReply r2 = { REP_RESULT };
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+ LDAPControl c, *ca[2];
+ lastbind_info *lbi = (lastbind_info *) op->o_callback->sc_private;
+
+ /* This is a DSA-specific opattr, it never gets replicated. */
+ op2.o_tag = LDAP_REQ_MODIFY;
+ op2.o_callback = &cb;
+ op2.orm_modlist = mod;
+ op2.orm_no_opattrs = 0;
+ op2.o_dn = op->o_bd->be_rootdn;
+ op2.o_ndn = op->o_bd->be_rootndn;
+
+ /*
+ * Code for forwarding of updates adapted from ppolicy.c of slapo-ppolicy
+ *
+ * If this server is a shadow and forward_updates is true,
+ * use the frontend to perform this modify. That will trigger
+ * the update referral, which can then be forwarded by the
+ * chain overlay. Obviously the updateref and chain overlay
+ * must be configured appropriately for this to be useful.
+ */
+ if ( SLAP_SHADOW( op->o_bd ) && lbi->forward_updates ) {
+ op2.o_bd = frontendDB;
+
+ /* Must use Relax control since these are no-user-mod */
+ op2.o_relax = SLAP_CONTROL_CRITICAL;
+ op2.o_ctrls = ca;
+ ca[0] = &c;
+ ca[1] = NULL;
+ BER_BVZERO( &c.ldctl_value );
+ c.ldctl_iscritical = 1;
+ c.ldctl_oid = LDAP_CONTROL_RELAX;
+ } else {
+ /* If not forwarding, don't update opattrs and don't replicate */
+ if ( SLAP_SINGLE_SHADOW( op->o_bd )) {
+ op2.orm_no_opattrs = 1;
+ op2.o_dont_replicate = 1;
+ }
+ /* TODO: not sure what this does in slapo-ppolicy */
+ /*
+ op2.o_bd->bd_info = (BackendInfo *)on->on_info;
+ */
+ }
+
+ rc = op2.o_bd->be_modify( &op2, &r2 );
+ slap_mods_free( mod, 1 );
+ }
+
+ op->o_bd->bd_info = bi;
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+lastbind_bind( Operation *op, SlapReply *rs )
+{
+ slap_callback *cb;
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+
+ /* setup a callback to intercept result of this bind operation
+ * and pass along the lastbind_info struct */
+ cb = op->o_tmpcalloc( sizeof(slap_callback), 1, op->o_tmpmemctx );
+ cb->sc_response = lastbind_bind_response;
+ cb->sc_next = op->o_callback->sc_next;
+ cb->sc_private = on->on_bi.bi_private;
+ op->o_callback->sc_next = cb;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+lastbind_db_init(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+
+ /* initialize private structure to store configuration */
+ on->on_bi.bi_private = ch_calloc( 1, sizeof(lastbind_info) );
+
+ return 0;
+}
+
+static int
+lastbind_db_close(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ lastbind_info *lbi = (lastbind_info *) on->on_bi.bi_private;
+
+ /* free private structure to store configuration */
+ free( lbi );
+
+ return 0;
+}
+
+static slap_overinst lastbind;
+
+int lastbind_initialize()
+{
+ int i, code;
+
+ /* register operational schema for this overlay (authTimestamp attribute) */
+ for (i=0; lastBind_OpSchema[i].def; i++) {
+ code = register_at( lastBind_OpSchema[i].def, lastBind_OpSchema[i].ad, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "lastbind_initialize: register_at failed\n" );
+ return code;
+ }
+ }
+
+ ad_authTimestamp->ad_type->sat_flags |= SLAP_AT_MANAGEABLE;
+
+ lastbind.on_bi.bi_type = "lastbind";
+ lastbind.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ lastbind.on_bi.bi_db_init = lastbind_db_init;
+ lastbind.on_bi.bi_db_close = lastbind_db_close;
+ lastbind.on_bi.bi_op_bind = lastbind_bind;
+
+ /* register configuration directives */
+ lastbind.on_bi.bi_cf_ocs = lastbindocs;
+ code = config_register_schema( lastbindcfg, lastbindocs );
+ if ( code ) return code;
+
+ return overlay_register( &lastbind );
+}
+
+#if SLAPD_OVER_LASTBIND == SLAPD_MOD_DYNAMIC
+int init_module(int argc, char *argv[]) {
+ return lastbind_initialize();
+}
+#endif
+
+#endif /* defined(SLAPD_OVER_LASTBIND) */
diff --git a/contrib/slapd-modules/lastbind/slapo-lastbind.5 b/contrib/slapd-modules/lastbind/slapo-lastbind.5
new file mode 100644
index 0000000..d0da8b8
--- /dev/null
+++ b/contrib/slapd-modules/lastbind/slapo-lastbind.5
@@ -0,0 +1,108 @@
+.TH SLAPO-LASTBIND 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2009 Jonathan Clarke, All Rights Reserved.
+.\" $OpenLDAP$
+.SH NAME
+slapo-lastbind \- lastbind overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B lastbind
+overlay to
+.BR slapd (8)
+allows recording the timestamp of the last successful bind to entries
+in the directory, in the
+.B authTimestamp
+attribute.
+The overlay can be configured to update this timestamp only if it is
+older than a given value, thus avoiding large numbers of write
+operations penalizing performance.
+One sample use for this overlay would be to detect unused accounts.
+
+.SH CONFIGURATION
+The config directives that are specific to the
+.B lastbind
+overlay must be prefixed by
+.BR lastbind\- ,
+to avoid potential conflicts with directives specific to the underlying
+database or to other stacked overlays.
+
+.TP
+.B overlay lastbind
+This directive adds the
+.B lastbind
+overlay to the current database, see
+.BR slapd.conf (5)
+for details.
+
+.LP
+This
+.B slapd.conf
+configuration option is defined for the lastbind overlay. It must
+appear after the
+.B overlay
+directive:
+.TP
+.B lastbind-precision <seconds>
+The value
+.B <seconds>
+is the number of seconds after which to update the
+.B authTimestamp
+attribute in an entry. If the existing value of
+.B authTimestamp
+is less than
+.B <seconds>
+old, it will not be changed.
+If this configuration option is omitted, the
+.B authTimestamp
+attribute is updated on each successful bind operation.
+.TP
+.B lastbind_forward_updates
+Specify that updates of the authTimestamp attribute
+on a consumer should be forwarded
+to a provider instead of being written directly into the consumer's local
+database. This setting is only useful on a replication consumer, and
+also requires the
+.B updateref
+setting and
+.B chain
+overlay to be appropriately configured.
+
+.SH EXAMPLE
+This example configures the
+.B lastbind
+overlay to store
+.B authTimestamp
+in all entries in a database, with a 1 week precision.
+Add the following to
+.BR slapd.conf (5):
+
+.LP
+.nf
+ database <database>
+ # ...
+
+ overlay lastbind
+ lastbind-precision 604800
+.fi
+.LP
+.B slapd
+must also load
+.B lastbind.la,
+if compiled as a run-time module;
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd (8).
+The
+.BR slapo-lastbind (5)
+overlay supports dynamic configuration via
+.BR back-config.
+.SH ACKNOWLEDGEMENTS
+.P
+This module was written in 2009 by Jonathan Clarke. It is loosely
+derived from the password policy overlay.
diff --git a/contrib/slapd-modules/lastmod/Makefile b/contrib/slapd-modules/lastmod/Makefile
new file mode 100644
index 0000000..9b06c28
--- /dev/null
+++ b/contrib/slapd-modules/lastmod/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_LASTMOD=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = lastmod.la
+MANPAGES = slapo-lastmod.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+lastmod.la: lastmod.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/lastmod/lastmod.c b/contrib/slapd-modules/lastmod/lastmod.c
new file mode 100644
index 0000000..116b9ae
--- /dev/null
+++ b/contrib/slapd-modules/lastmod/lastmod.c
@@ -0,0 +1,963 @@
+/* lastmod.c - returns last modification info */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_LASTMOD
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "lutil.h"
+
+typedef struct lastmod_info_t {
+ struct berval lmi_rdnvalue;
+ Entry *lmi_e;
+ ldap_pvt_thread_mutex_t lmi_entry_mutex;
+ int lmi_enabled;
+} lastmod_info_t;
+
+struct lastmod_schema_t {
+ ObjectClass *lms_oc_lastmod;
+ AttributeDescription *lms_ad_lastmodDN;
+ AttributeDescription *lms_ad_lastmodType;
+ AttributeDescription *lms_ad_lastmodEnabled;
+} lastmod_schema;
+
+enum lastmodType_e {
+ LASTMOD_ADD = 0,
+ LASTMOD_DELETE,
+ LASTMOD_EXOP,
+ LASTMOD_MODIFY,
+ LASTMOD_MODRDN,
+ LASTMOD_UNKNOWN
+};
+
+struct berval lastmodType[] = {
+ BER_BVC( "add" ),
+ BER_BVC( "delete" ),
+ BER_BVC( "exop" ),
+ BER_BVC( "modify" ),
+ BER_BVC( "modrdn" ),
+ BER_BVC( "unknown" ),
+ BER_BVNULL
+};
+
+static struct m_s {
+ char *schema;
+ slap_mask_t flags;
+ int offset;
+} moc[] = {
+ { "( 1.3.6.1.4.1.4203.666.3.13"
+ "NAME 'lastmod' "
+ "DESC 'OpenLDAP per-database last modification monitoring' "
+ "STRUCTURAL "
+ "SUP top "
+ "MUST cn "
+ "MAY ( "
+ "lastmodDN "
+ "$ lastmodType "
+ "$ description "
+ "$ seeAlso "
+ ") )", SLAP_OC_OPERATIONAL|SLAP_OC_HIDE,
+ offsetof( struct lastmod_schema_t, lms_oc_lastmod ) },
+ { NULL }
+}, mat[] = {
+ { "( 1.3.6.1.4.1.4203.666.1.28"
+ "NAME 'lastmodDN' "
+ "DESC 'DN of last modification' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
+ "NO-USER-MODIFICATION "
+ "USAGE directoryOperation )", SLAP_AT_HIDE,
+ offsetof( struct lastmod_schema_t, lms_ad_lastmodDN ) },
+ { "( 1.3.6.1.4.1.4203.666.1.29"
+ "NAME 'lastmodType' "
+ "DESC 'Type of last modification' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
+ "EQUALITY caseIgnoreMatch "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION "
+ "USAGE directoryOperation )", SLAP_AT_HIDE,
+ offsetof( struct lastmod_schema_t, lms_ad_lastmodType ) },
+ { "( 1.3.6.1.4.1.4203.666.1.30"
+ "NAME 'lastmodEnabled' "
+ "DESC 'Lastmod overlay state' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
+ "EQUALITY booleanMatch "
+ "SINGLE-VALUE )", 0,
+ offsetof( struct lastmod_schema_t, lms_ad_lastmodEnabled ) },
+ { NULL }
+
+ /* FIXME: what about UUID of last modified entry? */
+};
+
+static int
+lastmod_search( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private;
+ int rc;
+
+ /* if we get here, it must be a success */
+ rs->sr_err = LDAP_SUCCESS;
+
+ ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
+
+ rc = test_filter( op, lmi->lmi_e, op->oq_search.rs_filter );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_flags = 0;
+ rs->sr_entry = lmi->lmi_e;
+ rs->sr_err = send_search_entry( op, rs );
+ rs->sr_entry = NULL;
+ rs->sr_flags = 0;
+ rs->sr_attrs = NULL;
+ }
+
+ ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
+
+ send_ldap_result( op, rs );
+
+ return 0;
+}
+
+static int
+lastmod_compare( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private;
+ Attribute *a;
+
+ ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, lmi->lmi_e, get_assertion( op ) ) != LDAP_COMPARE_TRUE ) )
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+ rs->sr_err = access_allowed( op, lmi->lmi_e, op->oq_compare.rs_ava->aa_desc,
+ &op->oq_compare.rs_ava->aa_value, ACL_COMPARE, NULL );
+ if ( ! rs->sr_err ) {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto return_results;
+ }
+
+ rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
+
+ for ( a = attr_find( lmi->lmi_e->e_attrs, op->oq_compare.rs_ava->aa_desc );
+ a != NULL;
+ a = attr_find( a->a_next, op->oq_compare.rs_ava->aa_desc ) )
+ {
+ rs->sr_err = LDAP_COMPARE_FALSE;
+
+ if ( value_find_ex( op->oq_compare.rs_ava->aa_desc,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ a->a_nvals, &op->oq_compare.rs_ava->aa_value, op->o_tmpmemctx ) == 0 )
+ {
+ rs->sr_err = LDAP_COMPARE_TRUE;
+ break;
+ }
+ }
+
+return_results:;
+
+ ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
+
+ send_ldap_result( op, rs );
+
+ if( rs->sr_err == LDAP_COMPARE_FALSE || rs->sr_err == LDAP_COMPARE_TRUE ) {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+
+ return rs->sr_err;
+}
+
+static int
+lastmod_exop( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+
+ /* Temporary */
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "not allowed within namingContext";
+ send_ldap_result( op, rs );
+ rs->sr_text = NULL;
+
+ return -1;
+}
+
+static int
+lastmod_modify( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private;
+ Modifications *ml;
+
+ ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
+
+ if ( !acl_check_modlist( op, lmi->lmi_e, op->orm_modlist ) ) {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto cleanup;
+ }
+
+ for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
+ Attribute *a;
+
+ if ( ml->sml_desc != lastmod_schema.lms_ad_lastmodEnabled ) {
+ continue;
+ }
+
+ if ( ml->sml_op != LDAP_MOD_REPLACE ) {
+ rs->sr_text = "unsupported mod type";
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ goto cleanup;
+ }
+
+ a = attr_find( lmi->lmi_e->e_attrs, ml->sml_desc );
+
+ if ( a == NULL ) {
+ rs->sr_text = "lastmod overlay internal error";
+ rs->sr_err = LDAP_OTHER;
+ goto cleanup;
+ }
+
+ ch_free( a->a_vals[ 0 ].bv_val );
+ ber_dupbv( &a->a_vals[ 0 ], &ml->sml_values[ 0 ] );
+ if ( a->a_nvals ) {
+ ch_free( a->a_nvals[ 0 ].bv_val );
+ if ( ml->sml_nvalues && !BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
+ ber_dupbv( &a->a_nvals[ 0 ], &ml->sml_nvalues[ 0 ] );
+ } else {
+ ber_dupbv( &a->a_nvals[ 0 ], &ml->sml_values[ 0 ] );
+ }
+ }
+
+ if ( strcmp( ml->sml_values[ 0 ].bv_val, "TRUE" ) == 0 ) {
+ lmi->lmi_enabled = 1;
+ } else if ( strcmp( ml->sml_values[ 0 ].bv_val, "FALSE" ) == 0 ) {
+ lmi->lmi_enabled = 0;
+ } else {
+ assert( 0 );
+ }
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+
+cleanup:;
+ ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
+
+ send_ldap_result( op, rs );
+ rs->sr_text = NULL;
+
+ return rs->sr_err;
+}
+
+static int
+lastmod_op_func( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private;
+ Modifications *ml;
+
+ if ( dn_match( &op->o_req_ndn, &lmi->lmi_e->e_nname ) ) {
+ switch ( op->o_tag ) {
+ case LDAP_REQ_SEARCH:
+ if ( op->ors_scope != LDAP_SCOPE_BASE ) {
+ goto return_referral;
+ }
+ /* process */
+ return lastmod_search( op, rs );
+
+ case LDAP_REQ_COMPARE:
+ return lastmod_compare( op, rs );
+
+ case LDAP_REQ_EXTENDED:
+ /* if write, reject; otherwise process */
+ if ( exop_is_write( op )) {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "not allowed within namingContext";
+ goto return_error;
+ }
+ return lastmod_exop( op, rs );
+
+ case LDAP_REQ_MODIFY:
+ /* allow only changes to overlay status */
+ for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
+ if ( ad_cmp( ml->sml_desc, slap_schema.si_ad_modifiersName ) != 0
+ && ad_cmp( ml->sml_desc, slap_schema.si_ad_modifyTimestamp ) != 0
+ && ad_cmp( ml->sml_desc, slap_schema.si_ad_entryCSN ) != 0
+ && ad_cmp( ml->sml_desc, lastmod_schema.lms_ad_lastmodEnabled ) != 0 )
+ {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "not allowed within namingContext";
+ goto return_error;
+ }
+ }
+ return lastmod_modify( op, rs );
+
+ default:
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "not allowed within namingContext";
+ goto return_error;
+ }
+ }
+
+ if ( dnIsSuffix( &op->o_req_ndn, &lmi->lmi_e->e_nname ) ) {
+ goto return_referral;
+ }
+
+ return SLAP_CB_CONTINUE;
+
+return_referral:;
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rs->sr_ref = referral_rewrite( default_referral,
+ NULL, &op->o_req_dn, op->ors_scope );
+
+ if ( !rs->sr_ref ) {
+ rs->sr_ref = default_referral;
+ }
+ rs->sr_err = LDAP_REFERRAL;
+ send_ldap_result( op, rs );
+
+ if ( rs->sr_ref != default_referral ) {
+ ber_bvarray_free( rs->sr_ref );
+ }
+ rs->sr_ref = NULL;
+
+ return -1;
+
+return_error:;
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_result( op, rs );
+ rs->sr_text = NULL;
+
+ return -1;
+}
+
+static int
+best_guess( Operation *op,
+ struct berval *bv_entryCSN, struct berval *bv_nentryCSN,
+ struct berval *bv_modifyTimestamp, struct berval *bv_nmodifyTimestamp,
+ struct berval *bv_modifiersName, struct berval *bv_nmodifiersName )
+{
+ if ( bv_entryCSN ) {
+ char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE ];
+ struct berval entryCSN;
+
+ entryCSN.bv_val = csnbuf;
+ entryCSN.bv_len = sizeof( csnbuf );
+ slap_get_csn( NULL, &entryCSN, 0 );
+
+ ber_dupbv( bv_entryCSN, &entryCSN );
+ ber_dupbv( bv_nentryCSN, &entryCSN );
+ }
+
+ if ( bv_modifyTimestamp ) {
+ char tmbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+ struct berval timestamp;
+ time_t currtime;
+
+ /* best guess */
+#if 0
+ currtime = slap_get_time();
+#endif
+ /* maybe we better use the time the operation was initiated */
+ currtime = op->o_time;
+
+ timestamp.bv_val = tmbuf;
+ timestamp.bv_len = sizeof(tmbuf);
+ slap_timestamp( &currtime, &timestamp );
+
+ ber_dupbv( bv_modifyTimestamp, &timestamp );
+ ber_dupbv( bv_nmodifyTimestamp, bv_modifyTimestamp );
+ }
+
+ if ( bv_modifiersName ) {
+ /* best guess */
+ ber_dupbv( bv_modifiersName, &op->o_dn );
+ ber_dupbv( bv_nmodifiersName, &op->o_ndn );
+ }
+
+ return 0;
+}
+
+static int
+lastmod_update( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private;
+ Attribute *a;
+ Modifications *ml = NULL;
+ struct berval bv_entryCSN = BER_BVNULL,
+ bv_nentryCSN = BER_BVNULL,
+ bv_modifyTimestamp = BER_BVNULL,
+ bv_nmodifyTimestamp = BER_BVNULL,
+ bv_modifiersName = BER_BVNULL,
+ bv_nmodifiersName = BER_BVNULL,
+ bv_name = BER_BVNULL,
+ bv_nname = BER_BVNULL;
+ enum lastmodType_e lmt = LASTMOD_UNKNOWN;
+ Entry *e = NULL;
+ int rc = -1;
+
+ /* FIXME: timestamp? modifier? */
+ switch ( op->o_tag ) {
+ case LDAP_REQ_ADD:
+ lmt = LASTMOD_ADD;
+ e = op->ora_e;
+ a = attr_find( e->e_attrs, slap_schema.si_ad_entryCSN );
+ if ( a != NULL ) {
+ ber_dupbv( &bv_entryCSN, &a->a_vals[0] );
+ if ( a->a_nvals && !BER_BVISNULL( &a->a_nvals[0] ) ) {
+ ber_dupbv( &bv_nentryCSN, &a->a_nvals[0] );
+ } else {
+ ber_dupbv( &bv_nentryCSN, &a->a_vals[0] );
+ }
+ }
+ a = attr_find( e->e_attrs, slap_schema.si_ad_modifyTimestamp );
+ if ( a != NULL ) {
+ ber_dupbv( &bv_modifyTimestamp, &a->a_vals[0] );
+ if ( a->a_nvals && !BER_BVISNULL( &a->a_nvals[0] ) ) {
+ ber_dupbv( &bv_nmodifyTimestamp, &a->a_nvals[0] );
+ } else {
+ ber_dupbv( &bv_nmodifyTimestamp, &a->a_vals[0] );
+ }
+ }
+ a = attr_find( e->e_attrs, slap_schema.si_ad_modifiersName );
+ if ( a != NULL ) {
+ ber_dupbv( &bv_modifiersName, &a->a_vals[0] );
+ ber_dupbv( &bv_nmodifiersName, &a->a_nvals[0] );
+ }
+ ber_dupbv( &bv_name, &e->e_name );
+ ber_dupbv( &bv_nname, &e->e_nname );
+ break;
+
+ case LDAP_REQ_DELETE:
+ lmt = LASTMOD_DELETE;
+
+ best_guess( op, &bv_entryCSN, &bv_nentryCSN,
+ &bv_modifyTimestamp, &bv_nmodifyTimestamp,
+ &bv_modifiersName, &bv_nmodifiersName );
+
+ ber_dupbv( &bv_name, &op->o_req_dn );
+ ber_dupbv( &bv_nname, &op->o_req_ndn );
+ break;
+
+ case LDAP_REQ_EXTENDED:
+ lmt = LASTMOD_EXOP;
+
+ /* actually, password change is wrapped around a backend
+ * call to modify, so it never shows up as an exop... */
+ best_guess( op, &bv_entryCSN, &bv_nentryCSN,
+ &bv_modifyTimestamp, &bv_nmodifyTimestamp,
+ &bv_modifiersName, &bv_nmodifiersName );
+
+ ber_dupbv( &bv_name, &op->o_req_dn );
+ ber_dupbv( &bv_nname, &op->o_req_ndn );
+ break;
+
+ case LDAP_REQ_MODIFY:
+ lmt = LASTMOD_MODIFY;
+ rc = 3;
+
+ for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
+ if ( ad_cmp( ml->sml_desc , slap_schema.si_ad_modifiersName ) == 0 ) {
+ ber_dupbv( &bv_modifiersName, &ml->sml_values[0] );
+ ber_dupbv( &bv_nmodifiersName, &ml->sml_nvalues[0] );
+
+ rc--;
+ if ( !rc ) {
+ break;
+ }
+
+ } else if ( ad_cmp( ml->sml_desc, slap_schema.si_ad_entryCSN ) == 0 ) {
+ ber_dupbv( &bv_entryCSN, &ml->sml_values[0] );
+ if ( ml->sml_nvalues && !BER_BVISNULL( &ml->sml_nvalues[0] ) ) {
+ ber_dupbv( &bv_nentryCSN, &ml->sml_nvalues[0] );
+ } else {
+ ber_dupbv( &bv_nentryCSN, &ml->sml_values[0] );
+ }
+
+ rc --;
+ if ( !rc ) {
+ break;
+ }
+
+ } else if ( ad_cmp( ml->sml_desc, slap_schema.si_ad_modifyTimestamp ) == 0 ) {
+ ber_dupbv( &bv_modifyTimestamp, &ml->sml_values[0] );
+ if ( ml->sml_nvalues && !BER_BVISNULL( &ml->sml_nvalues[0] ) ) {
+ ber_dupbv( &bv_nmodifyTimestamp, &ml->sml_nvalues[0] );
+ } else {
+ ber_dupbv( &bv_nmodifyTimestamp, &ml->sml_values[0] );
+ }
+
+ rc --;
+ if ( !rc ) {
+ break;
+ }
+ }
+ }
+
+ /* if rooted at global overlay, opattrs are not yet in place */
+ if ( BER_BVISNULL( &bv_modifiersName ) ) {
+ best_guess( op, NULL, NULL, NULL, NULL, &bv_modifiersName, &bv_nmodifiersName );
+ }
+
+ if ( BER_BVISNULL( &bv_entryCSN ) ) {
+ best_guess( op, &bv_entryCSN, &bv_nentryCSN, NULL, NULL, NULL, NULL );
+ }
+
+ if ( BER_BVISNULL( &bv_modifyTimestamp ) ) {
+ best_guess( op, NULL, NULL, &bv_modifyTimestamp, &bv_nmodifyTimestamp, NULL, NULL );
+ }
+
+ ber_dupbv( &bv_name, &op->o_req_dn );
+ ber_dupbv( &bv_nname, &op->o_req_ndn );
+ break;
+
+ case LDAP_REQ_MODRDN:
+ lmt = LASTMOD_MODRDN;
+ e = NULL;
+
+ if ( op->orr_newSup && !BER_BVISNULL( op->orr_newSup ) ) {
+ build_new_dn( &bv_name, op->orr_newSup, &op->orr_newrdn, NULL );
+ build_new_dn( &bv_nname, op->orr_nnewSup, &op->orr_nnewrdn, NULL );
+
+ } else {
+ struct berval pdn;
+
+ dnParent( &op->o_req_dn, &pdn );
+ build_new_dn( &bv_name, &pdn, &op->orr_newrdn, NULL );
+
+ dnParent( &op->o_req_ndn, &pdn );
+ build_new_dn( &bv_nname, &pdn, &op->orr_nnewrdn, NULL );
+ }
+
+ if ( on->on_info->oi_orig->bi_entry_get_rw ) {
+ BackendInfo *bi = op->o_bd->bd_info;
+ int rc;
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info->oi_orig;
+ rc = op->o_bd->bd_info->bi_entry_get_rw( op, &bv_name, NULL, NULL, 0, &e );
+ if ( rc == LDAP_SUCCESS ) {
+ a = attr_find( e->e_attrs, slap_schema.si_ad_modifiersName );
+ if ( a != NULL ) {
+ ber_dupbv( &bv_modifiersName, &a->a_vals[0] );
+ ber_dupbv( &bv_nmodifiersName, &a->a_nvals[0] );
+ }
+ a = attr_find( e->e_attrs, slap_schema.si_ad_entryCSN );
+ if ( a != NULL ) {
+ ber_dupbv( &bv_entryCSN, &a->a_vals[0] );
+ if ( a->a_nvals && !BER_BVISNULL( &a->a_nvals[0] ) ) {
+ ber_dupbv( &bv_nentryCSN, &a->a_nvals[0] );
+ } else {
+ ber_dupbv( &bv_nentryCSN, &a->a_vals[0] );
+ }
+ }
+ a = attr_find( e->e_attrs, slap_schema.si_ad_modifyTimestamp );
+ if ( a != NULL ) {
+ ber_dupbv( &bv_modifyTimestamp, &a->a_vals[0] );
+ if ( a->a_nvals && !BER_BVISNULL( &a->a_nvals[0] ) ) {
+ ber_dupbv( &bv_nmodifyTimestamp, &a->a_nvals[0] );
+ } else {
+ ber_dupbv( &bv_nmodifyTimestamp, &a->a_vals[0] );
+ }
+ }
+
+ assert( dn_match( &bv_name, &e->e_name ) );
+ assert( dn_match( &bv_nname, &e->e_nname ) );
+
+ op->o_bd->bd_info->bi_entry_release_rw( op, e, 0 );
+ }
+
+ op->o_bd->bd_info = bi;
+
+ }
+
+ /* if !bi_entry_get_rw || bi_entry_get_rw failed for any reason... */
+ if ( e == NULL ) {
+ best_guess( op, &bv_entryCSN, &bv_nentryCSN,
+ &bv_modifyTimestamp, &bv_nmodifyTimestamp,
+ &bv_modifiersName, &bv_nmodifiersName );
+ }
+
+ break;
+
+ default:
+ return -1;
+ }
+
+ ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
+
+#if 0
+ fprintf( stderr, "### lastmodDN: %s %s\n", bv_name.bv_val, bv_nname.bv_val );
+#endif
+
+ a = attr_find( lmi->lmi_e->e_attrs, lastmod_schema.lms_ad_lastmodDN );
+ if ( a == NULL ) {
+ goto error_return;
+ }
+ ch_free( a->a_vals[0].bv_val );
+ a->a_vals[0] = bv_name;
+ ch_free( a->a_nvals[0].bv_val );
+ a->a_nvals[0] = bv_nname;
+
+#if 0
+ fprintf( stderr, "### lastmodType: %s %s\n", lastmodType[ lmt ].bv_val, lastmodType[ lmt ].bv_val );
+#endif
+
+ a = attr_find( lmi->lmi_e->e_attrs, lastmod_schema.lms_ad_lastmodType );
+ if ( a == NULL ) {
+ goto error_return;
+ }
+ ch_free( a->a_vals[0].bv_val );
+ ber_dupbv( &a->a_vals[0], &lastmodType[ lmt ] );
+ ch_free( a->a_nvals[0].bv_val );
+ ber_dupbv( &a->a_nvals[0], &lastmodType[ lmt ] );
+
+#if 0
+ fprintf( stderr, "### modifiersName: %s %s\n", bv_modifiersName.bv_val, bv_nmodifiersName.bv_val );
+#endif
+
+ a = attr_find( lmi->lmi_e->e_attrs, slap_schema.si_ad_modifiersName );
+ if ( a == NULL ) {
+ goto error_return;
+ }
+ ch_free( a->a_vals[0].bv_val );
+ a->a_vals[0] = bv_modifiersName;
+ ch_free( a->a_nvals[0].bv_val );
+ a->a_nvals[0] = bv_nmodifiersName;
+
+#if 0
+ fprintf( stderr, "### modifyTimestamp: %s %s\n", bv_nmodifyTimestamp.bv_val, bv_modifyTimestamp.bv_val );
+#endif
+
+ a = attr_find( lmi->lmi_e->e_attrs, slap_schema.si_ad_modifyTimestamp );
+ if ( a == NULL ) {
+ goto error_return;
+ }
+ ch_free( a->a_vals[0].bv_val );
+ a->a_vals[0] = bv_modifyTimestamp;
+ ch_free( a->a_nvals[0].bv_val );
+ a->a_nvals[0] = bv_nmodifyTimestamp;
+
+#if 0
+ fprintf( stderr, "### entryCSN: %s %s\n", bv_nentryCSN.bv_val, bv_entryCSN.bv_val );
+#endif
+
+ a = attr_find( lmi->lmi_e->e_attrs, slap_schema.si_ad_entryCSN );
+ if ( a == NULL ) {
+ goto error_return;
+ }
+ ch_free( a->a_vals[0].bv_val );
+ a->a_vals[0] = bv_entryCSN;
+ ch_free( a->a_nvals[0].bv_val );
+ a->a_nvals[0] = bv_nentryCSN;
+
+ rc = 0;
+
+error_return:;
+ ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
+
+ return rc;
+}
+
+static int
+lastmod_response( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private;
+
+ /* don't record failed operations */
+ switch ( rs->sr_err ) {
+ case LDAP_SUCCESS:
+ /* FIXME: other cases? */
+ break;
+
+ default:
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* record only write operations */
+ switch ( op->o_tag ) {
+ case LDAP_REQ_ADD:
+ case LDAP_REQ_MODIFY:
+ case LDAP_REQ_MODRDN:
+ case LDAP_REQ_DELETE:
+ break;
+
+ case LDAP_REQ_EXTENDED:
+ /* if write, process */
+ if ( exop_is_write( op ))
+ break;
+
+ /* fall thru */
+ default:
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* skip if disabled */
+ ldap_pvt_thread_mutex_lock( &lmi->lmi_entry_mutex );
+ if ( !lmi->lmi_enabled ) {
+ ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+ ldap_pvt_thread_mutex_unlock( &lmi->lmi_entry_mutex );
+
+ (void)lastmod_update( op, rs );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+lastmod_db_init( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ lastmod_info_t *lmi;
+
+ if ( lastmod_schema.lms_oc_lastmod == NULL ) {
+ int i;
+ const char *text;
+
+ /* schema integration */
+ for ( i = 0; mat[i].schema; i++ ) {
+ int code;
+ AttributeDescription **ad =
+ ((AttributeDescription **)&(((char *)&lastmod_schema)[mat[i].offset]));
+ ad[0] = NULL;
+
+ code = register_at( mat[i].schema, ad, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "lastmod_init: register_at failed\n" );
+ return -1;
+ }
+ (*ad)->ad_type->sat_flags |= mat[i].flags;
+ }
+
+ for ( i = 0; moc[i].schema; i++ ) {
+ int code;
+ ObjectClass **Oc =
+ ((ObjectClass **)&(((char *)&lastmod_schema)[moc[i].offset]));
+
+ code = register_oc( moc[i].schema, Oc, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "lastmod_init: register_oc failed\n" );
+ return -1;
+ }
+ (*Oc)->soc_flags |= moc[i].flags;
+ }
+ }
+
+ lmi = (lastmod_info_t *)ch_malloc( sizeof( lastmod_info_t ) );
+
+ memset( lmi, 0, sizeof( lastmod_info_t ) );
+ lmi->lmi_enabled = 1;
+
+ on->on_bi.bi_private = lmi;
+
+ return 0;
+}
+
+static int
+lastmod_db_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private;
+
+ if ( strcasecmp( argv[ 0 ], "lastmod-rdnvalue" ) == 0 ) {
+ if ( lmi->lmi_rdnvalue.bv_val ) {
+ /* already defined! */
+ ch_free( lmi->lmi_rdnvalue.bv_val );
+ }
+
+ ber_str2bv( argv[ 1 ], 0, 1, &lmi->lmi_rdnvalue );
+
+ } else if ( strcasecmp( argv[ 0 ], "lastmod-enabled" ) == 0 ) {
+ if ( strcasecmp( argv[ 1 ], "yes" ) == 0 ) {
+ lmi->lmi_enabled = 1;
+
+ } else if ( strcasecmp( argv[ 1 ], "no" ) == 0 ) {
+ lmi->lmi_enabled = 0;
+
+ } else {
+ return -1;
+ }
+
+ } else {
+ return SLAP_CONF_UNKNOWN;
+ }
+
+ return 0;
+}
+
+static int
+lastmod_db_open( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private;
+ char buf[ 8192 ];
+ static char tmbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+
+ char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE ];
+ struct berval entryCSN;
+ struct berval timestamp;
+
+ if ( !SLAP_LASTMOD( be ) ) {
+ fprintf( stderr, "set \"lastmod on\" to make this overlay effective\n" );
+ return -1;
+ }
+
+ /*
+ * Start
+ */
+ timestamp.bv_val = tmbuf;
+ timestamp.bv_len = sizeof(tmbuf);
+ slap_timestamp( &starttime, &timestamp );
+
+ entryCSN.bv_val = csnbuf;
+ entryCSN.bv_len = sizeof( csnbuf );
+ slap_get_csn( NULL, &entryCSN, 0 );
+
+ if ( BER_BVISNULL( &lmi->lmi_rdnvalue ) ) {
+ ber_str2bv( "Lastmod", 0, 1, &lmi->lmi_rdnvalue );
+ }
+
+ snprintf( buf, sizeof( buf ),
+ "dn: cn=%s%s%s\n"
+ "objectClass: %s\n"
+ "structuralObjectClass: %s\n"
+ "cn: %s\n"
+ "description: This object contains the last modification to this database\n"
+ "%s: cn=%s%s%s\n"
+ "%s: %s\n"
+ "%s: %s\n"
+ "createTimestamp: %s\n"
+ "creatorsName: %s\n"
+ "entryCSN: %s\n"
+ "modifyTimestamp: %s\n"
+ "modifiersName: %s\n"
+ "hasSubordinates: FALSE\n",
+ lmi->lmi_rdnvalue.bv_val, BER_BVISEMPTY( &be->be_suffix[ 0 ] ) ? "" : ",", be->be_suffix[ 0 ].bv_val,
+ lastmod_schema.lms_oc_lastmod->soc_cname.bv_val,
+ lastmod_schema.lms_oc_lastmod->soc_cname.bv_val,
+ lmi->lmi_rdnvalue.bv_val,
+ lastmod_schema.lms_ad_lastmodDN->ad_cname.bv_val,
+ lmi->lmi_rdnvalue.bv_val, BER_BVISEMPTY( &be->be_suffix[ 0 ] ) ? "" : ",", be->be_suffix[ 0 ].bv_val,
+ lastmod_schema.lms_ad_lastmodType->ad_cname.bv_val, lastmodType[ LASTMOD_ADD ].bv_val,
+ lastmod_schema.lms_ad_lastmodEnabled->ad_cname.bv_val, lmi->lmi_enabled ? "TRUE" : "FALSE",
+ tmbuf,
+ BER_BVISNULL( &be->be_rootdn ) ? SLAPD_ANONYMOUS : be->be_rootdn.bv_val,
+ entryCSN.bv_val,
+ tmbuf,
+ BER_BVISNULL( &be->be_rootdn ) ? SLAPD_ANONYMOUS : be->be_rootdn.bv_val );
+
+#if 0
+ fprintf( stderr, "# entry:\n%s\n", buf );
+#endif
+
+ lmi->lmi_e = str2entry( buf );
+ if ( lmi->lmi_e == NULL ) {
+ return -1;
+ }
+
+ ldap_pvt_thread_mutex_init( &lmi->lmi_entry_mutex );
+
+ return 0;
+}
+
+static int
+lastmod_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ lastmod_info_t *lmi = (lastmod_info_t *)on->on_bi.bi_private;
+
+ if ( lmi ) {
+ if ( !BER_BVISNULL( &lmi->lmi_rdnvalue ) ) {
+ ch_free( lmi->lmi_rdnvalue.bv_val );
+ }
+
+ if ( lmi->lmi_e ) {
+ entry_free( lmi->lmi_e );
+
+ ldap_pvt_thread_mutex_destroy( &lmi->lmi_entry_mutex );
+ }
+
+ ch_free( lmi );
+ }
+
+ return 0;
+}
+
+/* This overlay is set up for dynamic loading via moduleload. For static
+ * configuration, you'll need to arrange for the slap_overinst to be
+ * initialized and registered by some other function inside slapd.
+ */
+
+static slap_overinst lastmod;
+
+int
+lastmod_initialize()
+{
+ lastmod.on_bi.bi_type = "lastmod";
+ lastmod.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ lastmod.on_bi.bi_db_init = lastmod_db_init;
+ lastmod.on_bi.bi_db_config = lastmod_db_config;
+ lastmod.on_bi.bi_db_destroy = lastmod_db_destroy;
+ lastmod.on_bi.bi_db_open = lastmod_db_open;
+
+ lastmod.on_bi.bi_op_add = lastmod_op_func;
+ lastmod.on_bi.bi_op_compare = lastmod_op_func;
+ lastmod.on_bi.bi_op_delete = lastmod_op_func;
+ lastmod.on_bi.bi_op_modify = lastmod_op_func;
+ lastmod.on_bi.bi_op_modrdn = lastmod_op_func;
+ lastmod.on_bi.bi_op_search = lastmod_op_func;
+ lastmod.on_bi.bi_extended = lastmod_op_func;
+
+ lastmod.on_response = lastmod_response;
+
+ return overlay_register( &lastmod );
+}
+
+#if SLAPD_OVER_LASTMOD == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return lastmod_initialize();
+}
+#endif /* SLAPD_OVER_LASTMOD == SLAPD_MOD_DYNAMIC */
+
+#endif /* defined(SLAPD_OVER_LASTMOD) */
diff --git a/contrib/slapd-modules/lastmod/slapo-lastmod.5 b/contrib/slapd-modules/lastmod/slapo-lastmod.5
new file mode 100644
index 0000000..ea0ca23
--- /dev/null
+++ b/contrib/slapd-modules/lastmod/slapo-lastmod.5
@@ -0,0 +1,185 @@
+.\" Copyright 2004-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.TH SLAPO_LASTMOD 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.SH NAME
+slapo-lastmod \- Last Modification overlay
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+.LP
+The
+.B lastmod
+overlay creates a service entry rooted at the suffix of the database
+it's stacked onto, which holds the DN, the modification type,
+the modifiersName and the modifyTimestamp of the last write operation
+performed on that database.
+The lastmod overlay cannot be used when the "lastmod" feature
+is disabled, i.e. "lastmod off" is used.
+.P
+All operations targeted at the DN of the lastmod entry are rejected,
+except reads, i.e. searches with
+.B base
+scope.
+Regular operations are ignored, unless they result in writing; then,
+in case of success, the lastmod entry is updated accordingly,
+if possible.
+
+.SH CONFIGURATION
+These
+.B slapd.conf
+configuration options apply to the lastmod overlay. They must appear
+after the
+.B overlay
+directive.
+.TP
+.B lastmod-rdnvalue <RDN value>
+Specify the value of the RDN used for the service entry. By default
+.I Lastmod
+is used.
+.TP
+.B lastmod-enabled {yes|no}
+Specify whether the overlay must be enabled or not at startup.
+By default, the overlay is enabled; however, by changing the boolean
+value of the attribute
+.IR lastmodEnabled ,
+one can affect the status of the overlay.
+This is useful, for instance, to inhibit the overlay from keeping track
+of large bulk loads or deletions.
+
+.SH OBJECT CLASS
+The
+.B lastmod
+overlay depends on the
+.B lastmod
+objectClass. The definition of that class is as follows:
+.LP
+.RS 4
+( 1.3.6.1.4.1.4203.666.3.13 "
+ NAME 'lastmod'
+ DESC 'OpenLDAP per-database last modification monitoring'
+ STRUCTURAL
+ SUP top
+ MUST ( cn $ lastmodDN $ lastmodType )
+ MAY ( description $ seeAlso ) )
+.RE
+
+.SH ATTRIBUTES
+.P
+Each one of the sections below details the meaning and use of a particular
+attribute of this
+.B lastmod
+objectClass.
+Most of the attributes that are specific to the lastmod objectClass are
+operational, since they can logically be altered only by the DSA.
+The most notable exception is the
+.I lastmodEnabled
+attributeType, which can be altered via protocol to change the status
+of the overlay.
+.P
+
+.B lastmodEnabled
+.P
+This attribute contains a boolean flag that determines the status
+of the overlay. It can be altered via protocol by issuing a modify
+operation that replaces the value of the attribute.
+.LP
+.RS 4
+( 1.3.6.1.4.1.4203.666.1.30
+ NAME 'lastmodEnabled'
+ DESC 'Lastmod overlay state'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+ EQUALITY booleanMatch
+ SINGLE-VALUE )
+.RE
+
+.SH OPERATIONAL ATTRIBUTES
+.P
+Each one of the sections below details the meaning and use of a particular
+attribute of this
+.B lastmod
+objectClass.
+Most of the attributes that are specific to the lastmod objectClass are
+operational, since they can logically be altered only by the DSA.
+.P
+
+.B lastmodDN
+.P
+This attribute contains the distinguished name of the entry
+that was last modified within the naming context of a database.
+.LP
+.RS 4
+( 1.3.6.1.4.1.4203.666.1.28
+ NAME 'lastmodDN'
+ DESC 'DN of last modification'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ NO-USER-MODIFICATION
+ USAGE directoryOperation )
+.RE
+
+.B lastmodType
+.P
+This attribute contains the type of the modification that occurred
+to the last modified entry. Legal values are
+.BR add ,
+.BR delete ,
+.BR exop ,
+.BR modify ,
+.B modrdn
+and
+.BR unknown .
+The latter should only be used as a fall-thru in case of unhandled
+request types that are considered equivalent to a write operation.
+.LP
+.RS 4
+( 1.3.6.1.4.1.4203.666.1.29
+ NAME 'lastmodType'
+ DESC 'Type of last modification'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ EQUALITY caseIgnoreMatch
+ SINGLE-VALUE
+ NO-USER-MODIFICATION
+ USAGE directoryOperation )
+.RE
+
+
+.SH EXAMPLES
+.LP
+.RS
+.nf
+database mdb
+suffix dc=example,dc=com
+\...
+overlay lastmod
+lastmod-rdnvalue "Last Modification"
+.fi
+.RE
+
+.SH SEE ALSO
+.BR ldap (3),
+.BR slapd.conf (5),
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+
+.SH BUGS
+It is unclear whether this overlay can safely interoperate
+with other overlays.
+If the underlying backend does not implement entry_get/entry_release
+handlers, modrdn update can become tricky.
+The code needs some cleanup and more consistent error handling.
+So far, the OIDs for the schema haven't been assigned yet.
+
+.SH ACKNOWLEDGEMENTS
+.P
+This module was written in 2004 by Pierangelo Masarati in fulfillment
+of requirements from SysNet s.n.c.; this man page has been copied
+from
+.BR slapo-ppolicy (5),
+and most of the overlays ever written are copied from Howard Chu's
+first overlays.
+.P
+.B OpenLDAP
+is developed and maintained by The OpenLDAP Project (http://www.openldap.org/).
+.B OpenLDAP
+is derived from University of Michigan LDAP 3.3 Release.
diff --git a/contrib/slapd-modules/noopsrch/Makefile b/contrib/slapd-modules/noopsrch/Makefile
new file mode 100644
index 0000000..2934a32
--- /dev/null
+++ b/contrib/slapd-modules/noopsrch/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+# Copyright 2004 Howard Chu, Symas Corp. All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted only as authorized by the OpenLDAP
+# Public License.
+#
+# A copy of this license is available in the file LICENSE in the
+# top-level directory of the distribution or, alternatively, at
+# <http://www.OpenLDAP.org/license.html>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2 -Wall
+DEFS = -DSLAPD_OVER_NOOPSRCH=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = noopsrch.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+noopsrch.la: noopsrch.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/noopsrch/noopsrch.c b/contrib/slapd-modules/noopsrch/noopsrch.c
new file mode 100644
index 0000000..24f0f53
--- /dev/null
+++ b/contrib/slapd-modules/noopsrch/noopsrch.c
@@ -0,0 +1,255 @@
+/* noopsrch.c - LDAP Control that counts entries a search would return */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2010-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+/* define SLAPD_OVER_NOOPSRCH=2 to build as run-time loadable module */
+#ifdef SLAPD_OVER_NOOPSRCH
+
+/*
+ * Control OID
+ */
+#define LDAP_CONTROL_X_NOOPSRCH "1.3.6.1.4.1.4203.666.5.18"
+
+#include "slap.h"
+#include "ac/string.h"
+
+#define o_noopsrch o_ctrlflag[noopsrch_cid]
+#define o_ctrlnoopsrch o_controls[noopsrch_cid]
+
+static int noopsrch_cid;
+static slap_overinst noopsrch;
+
+static int
+noopsrch_parseCtrl (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ if ( op->o_noopsrch != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "No-op Search control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "No-op Search control value is present";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ op->o_ctrlnoopsrch = (void *)NULL;
+
+ op->o_noopsrch = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ rs->sr_err = LDAP_SUCCESS;
+
+ return rs->sr_err;
+}
+
+int dummy;
+
+typedef struct noopsrch_cb_t {
+ slap_overinst *nc_on;
+ ber_int_t nc_nentries;
+ ber_int_t nc_nsearchref;
+ AttributeName *nc_save_attrs;
+ int *nc_pdummy;
+ int nc_save_slimit;
+} noopsrch_cb_t;
+
+static int
+noopsrch_response( Operation *op, SlapReply *rs )
+{
+ noopsrch_cb_t *nc = (noopsrch_cb_t *)op->o_callback->sc_private;
+
+ /* if the control is global, limits are not computed yet */
+ if ( nc->nc_pdummy == &dummy ) {
+ nc->nc_save_slimit = op->ors_slimit;
+ op->ors_slimit = SLAP_NO_LIMIT;
+ nc->nc_pdummy = NULL;
+ }
+
+ if ( rs->sr_type == REP_SEARCH ) {
+ nc->nc_nentries++;
+#ifdef NOOPSRCH_DEBUG
+ Debug( LDAP_DEBUG_TRACE, "noopsrch_response(REP_SEARCH): nentries=%d\n", nc->nc_nentries );
+#endif
+ return 0;
+
+ } else if ( rs->sr_type == REP_SEARCHREF ) {
+ nc->nc_nsearchref++;
+ return 0;
+
+ } else if ( rs->sr_type == REP_RESULT ) {
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *) &berbuf;
+ struct berval ctrlval;
+ LDAPControl *ctrl, *ctrlsp[2];
+ int rc = rs->sr_err;
+
+ if ( nc->nc_save_slimit >= 0 && nc->nc_nentries >= nc->nc_save_slimit ) {
+ rc = LDAP_SIZELIMIT_EXCEEDED;
+ }
+
+#ifdef NOOPSRCH_DEBUG
+ Debug( LDAP_DEBUG_TRACE, "noopsrch_response(REP_RESULT): err=%d nentries=%d nref=%d\n", rc, nc->nc_nentries, nc->nc_nsearchref );
+#endif
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+
+ ber_printf( ber, "{iii}", rc, nc->nc_nentries, nc->nc_nsearchref );
+ if ( ber_flatten2( ber, &ctrlval, 0 ) == -1 ) {
+ ber_free_buf( ber );
+ if ( op->o_noopsrch == SLAP_CONTROL_CRITICAL ) {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ return SLAP_CB_CONTINUE;
+ }
+
+ ctrl = op->o_tmpcalloc( 1,
+ sizeof( LDAPControl ) + ctrlval.bv_len + 1,
+ op->o_tmpmemctx );
+ ctrl->ldctl_value.bv_val = (char *)&ctrl[ 1 ];
+ ctrl->ldctl_oid = LDAP_CONTROL_X_NOOPSRCH;
+ ctrl->ldctl_iscritical = 0;
+ ctrl->ldctl_value.bv_len = ctrlval.bv_len;
+ AC_MEMCPY( ctrl->ldctl_value.bv_val, ctrlval.bv_val, ctrlval.bv_len );
+ ctrl->ldctl_value.bv_val[ ctrl->ldctl_value.bv_len ] = '\0';
+
+ ber_free_buf( ber );
+
+ ctrlsp[0] = ctrl;
+ ctrlsp[1] = NULL;
+ slap_add_ctrls( op, rs, ctrlsp );
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+noopsrch_cleanup( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_RESULT || rs->sr_err == SLAPD_ABANDON ) {
+ noopsrch_cb_t *nc = (noopsrch_cb_t *)op->o_callback->sc_private;
+ op->ors_attrs = nc->nc_save_attrs;
+ if ( nc->nc_pdummy == NULL ) {
+ op->ors_slimit = nc->nc_save_slimit;
+ }
+
+ op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
+ op->o_callback = NULL;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+noopsrch_op_search( Operation *op, SlapReply *rs )
+{
+ if ( op->o_noopsrch != SLAP_CONTROL_NONE ) {
+ slap_callback *sc;
+ noopsrch_cb_t *nc;
+
+ sc = op->o_tmpcalloc( 1, sizeof( slap_callback ) + sizeof( noopsrch_cb_t ), op->o_tmpmemctx );
+
+ nc = (noopsrch_cb_t *)&sc[ 1 ];
+ nc->nc_on = (slap_overinst *)op->o_bd->bd_info;
+ nc->nc_nentries = 0;
+ nc->nc_nsearchref = 0;
+ nc->nc_save_attrs = op->ors_attrs;
+ nc->nc_pdummy = &dummy;
+
+ sc->sc_response = noopsrch_response;
+ sc->sc_cleanup = noopsrch_cleanup;
+ sc->sc_private = (void *)nc;
+
+ op->ors_attrs = slap_anlist_no_attrs;
+
+ sc->sc_next = op->o_callback->sc_next;
+ op->o_callback->sc_next = sc;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int noopsrch_cnt;
+
+static int
+noopsrch_db_init( BackendDB *be, ConfigReply *cr)
+{
+ if ( noopsrch_cnt++ == 0 ) {
+ int rc;
+
+ rc = register_supported_control( LDAP_CONTROL_X_NOOPSRCH,
+ SLAP_CTRL_SEARCH | SLAP_CTRL_GLOBAL_SEARCH, NULL,
+ noopsrch_parseCtrl, &noopsrch_cid );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "noopsrch_initialize: Failed to register control '%s' (%d)\n",
+ LDAP_CONTROL_X_NOOPSRCH, rc );
+ return rc;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+noopsrch_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ assert( noopsrch_cnt > 0 );
+
+#ifdef SLAP_CONFIG_DELETE
+ overlay_unregister_control( be, LDAP_CONTROL_X_NOOPSRCH );
+ if ( --noopsrch_cnt == 0 ) {
+ unregister_supported_control( LDAP_CONTROL_X_NOOPSRCH );
+ }
+
+#endif /* SLAP_CONFIG_DELETE */
+
+ return 0;
+}
+
+#if SLAPD_OVER_NOOPSRCH == SLAPD_MOD_DYNAMIC
+static
+#endif /* SLAPD_OVER_NOOPSRCH == SLAPD_MOD_DYNAMIC */
+int
+noopsrch_initialize( void )
+{
+
+ noopsrch.on_bi.bi_type = "noopsrch";
+
+ noopsrch.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ noopsrch.on_bi.bi_db_init = noopsrch_db_init;
+ noopsrch.on_bi.bi_db_destroy = noopsrch_db_destroy;
+ noopsrch.on_bi.bi_op_search = noopsrch_op_search;
+
+ return overlay_register( &noopsrch );
+}
+
+#if SLAPD_OVER_NOOPSRCH == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return noopsrch_initialize();
+}
+#endif /* SLAPD_OVER_NOOPSRCH == SLAPD_MOD_DYNAMIC */
+
+#endif /* SLAPD_OVER_NOOPSRCH */
diff --git a/contrib/slapd-modules/nops/Makefile b/contrib/slapd-modules/nops/Makefile
new file mode 100644
index 0000000..94a5bcd
--- /dev/null
+++ b/contrib/slapd-modules/nops/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_NOPS=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = nops.la
+MANPAGES = slapo-nops.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+nops.la: nops.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/nops/nops.c b/contrib/slapd-modules/nops/nops.c
new file mode 100644
index 0000000..6dffb6b
--- /dev/null
+++ b/contrib/slapd-modules/nops/nops.c
@@ -0,0 +1,178 @@
+/* nops.c - Overlay to filter idempotent operations */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Copyright 2008 Emmanuel Dreyfus.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by the Emmanuel Dreyfus for
+ * inclusion in OpenLDAP Software.
+ */
+#include "portable.h"
+
+#ifdef SLAPD_OVER_NOPS
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "lutil.h"
+#include "slap.h"
+#include "slap-config.h"
+
+static ConfigDriver nops_cf_gen;
+
+static int nops_cf_gen( ConfigArgs *c ) { return 0; }
+
+static void
+nops_rm_mod( Modifications **mods, Modifications *mod ) {
+ Modifications *next, *m;
+
+ next = mod->sml_next;
+ if (*mods == mod) {
+ *mods = next;
+ } else {
+ Modifications *m;
+
+ for (m = *mods; m; m = m->sml_next) {
+ if (m->sml_next == mod) {
+ m->sml_next = next;
+ break;
+ }
+ }
+ }
+
+ mod->sml_next = NULL;
+ slap_mods_free(mod, 1);
+
+ return;
+}
+
+static int
+nops_modify( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ Backend *be = op->o_bd;
+ Entry *target_entry = NULL;
+ Modifications *m;
+ int rc;
+
+ if ((m = op->orm_modlist) == NULL) {
+ op->o_bd->bd_info = (BackendInfo *)(on->on_info);
+ send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
+ "nops() got null orm_modlist");
+ return(rs->sr_err);
+ }
+
+ op->o_bd = on->on_info->oi_origdb;
+ rc = be_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &target_entry);
+ op->o_bd = be;
+
+ if (rc != 0 || target_entry == NULL)
+ return 0;
+
+ /*
+ * For each attribute modification, check if the
+ * modification and the old entry are the same.
+ */
+ while (m) {
+ int i, j;
+ int found;
+ Attribute *a;
+ BerVarray bm;
+ BerVarray bt;
+ Modifications *mc;
+
+ mc = m;
+ m = m->sml_next;
+
+ /* Check only replace sub-operations */
+ if ((mc->sml_op & LDAP_MOD_OP) != LDAP_MOD_REPLACE)
+ continue;
+
+ /* If there is no values, skip */
+ if (((bm = mc->sml_values ) == NULL ) || (bm[0].bv_val == NULL))
+ continue;
+
+ /* If the attribute does not exist in old entry, skip */
+ if ((a = attr_find(target_entry->e_attrs, mc->sml_desc)) == NULL)
+ continue;
+ if ((bt = a->a_vals) == NULL)
+ continue;
+
+ /* For each value replaced, do we find it in old entry? */
+ found = 0;
+ for (i = 0; bm[i].bv_val; i++) {
+ for (j = 0; bt[j].bv_val; j++) {
+ if (bm[i].bv_len != bt[j].bv_len)
+ continue;
+ if (memcmp(bm[i].bv_val, bt[j].bv_val, bt[j].bv_len) != 0)
+ continue;
+
+ found++;
+ break;
+ }
+ }
+
+ /* Did we find as many values as we had in old entry? */
+ if (i != a->a_numvals || found != a->a_numvals)
+ continue;
+
+ /* This is a nop, remove it */
+ Debug(LDAP_DEBUG_TRACE, "removing nop on %s",
+ a->a_desc->ad_cname.bv_val );
+
+ nops_rm_mod(&op->orm_modlist, mc);
+ }
+ if (target_entry) {
+ op->o_bd = on->on_info->oi_origdb;
+ be_entry_release_r(op, target_entry);
+ op->o_bd = be;
+ }
+
+ if ((m = op->orm_modlist) == NULL) {
+ slap_callback *cb = op->o_callback;
+
+ op->o_bd->bd_info = (BackendInfo *)(on->on_info);
+ op->o_callback = NULL;
+ send_ldap_error(op, rs, LDAP_SUCCESS, "");
+ op->o_callback = cb;
+
+ return (rs->sr_err);
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static slap_overinst nops_ovl;
+
+#if SLAPD_OVER_NOPS == SLAPD_MOD_DYNAMIC
+static
+#endif
+int
+nops_initialize( void ) {
+ nops_ovl.on_bi.bi_type = "nops";
+ nops_ovl.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ nops_ovl.on_bi.bi_op_modify = nops_modify;
+ return overlay_register( &nops_ovl );
+}
+
+#if SLAPD_OVER_NOPS == SLAPD_MOD_DYNAMIC
+int init_module(int argc, char *argv[]) {
+ return nops_initialize();
+}
+#endif
+
+#endif /* defined(SLAPD_OVER_NOPS) */
+
diff --git a/contrib/slapd-modules/nops/slapo-nops.5 b/contrib/slapd-modules/nops/slapo-nops.5
new file mode 100644
index 0000000..c27915e
--- /dev/null
+++ b/contrib/slapd-modules/nops/slapo-nops.5
@@ -0,0 +1,32 @@
+.TH SLAPO-NOPS 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2008 Emmanuel Dreyfus
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo-nops \- Remove Null Operations Overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+Some broken client tend to implement modifications as replace operations
+where all attributes are replaced, most of the time by the same values
+they had before. This can cause undesirable load on logs, ACL evaluation,
+or replication traffic.
+
+This overlay detects idempotent replace operations and filter them out.
+.SH CONFIGURATION
+This overlay had no specific configuration.
+.SH EXAMPLES
+.LP
+.RS
+.nf
+overlay nops
+.RE
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5).
+.SH ACKNOWLEDGEMENTS
+This module was written in 2008 by Emmanuel Dreyfus.
+.so ../Project
diff --git a/contrib/slapd-modules/nssov/Makefile b/contrib/slapd-modules/nssov/Makefile
new file mode 100644
index 0000000..13987c2
--- /dev/null
+++ b/contrib/slapd-modules/nssov/Makefile
@@ -0,0 +1,86 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 2008-2022 The OpenLDAP Foundation.
+# Portions Copyright 2008 Howard Chu, Symas Corp. All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted only as authorized by the OpenLDAP
+# Public License.
+#
+# A copy of this license is available in the file LICENSE in the
+# top-level directory of the distribution or, alternatively, at
+# <http://www.OpenLDAP.org/license.html>.
+
+# Path to the OpenLDAP source tree
+LDAP_SRC=../../..
+
+# Path to the OpenLDAP object tree - same as above unless
+# you're doing out-of-tree builds.
+LDAP_BUILD=$(LDAP_SRC)
+
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+NLDAPD_INC=-Inss-pam-ldapd
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+OPT = -g -O2
+CC = gcc
+DEFS =
+INCS = $(LDAP_INC) $(NLDAPD_INC)
+LIBS = $(LDAP_LIB)
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+sysconfdir = $(prefix)/etc$(ldap_subdir)
+schemadir = $(sysconfdir)/schema
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+all: nssov.la
+
+XOBJS = tio.lo
+
+OBJS = alias.lo ether.lo group.lo host.lo netgroup.lo network.lo \
+ nssov.lo passwd.lo protocol.lo rpc.lo service.lo shadow.lo pam.lo
+
+MANPAGES = slapo-nssov.5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+tio.lo: nss-pam-ldapd/tio.c
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $?
+
+$(OBJS): nssov.h
+
+nssov.la: $(OBJS) $(XOBJS)
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info 0:0:0 \
+ -rpath $(moduledir) -module -o $@ $(OBJS) $(XOBJS) $(LIBS)
+
+install: install-lib install-man FORCE
+
+install-lib: nssov.la
+ mkdir -p $(DESTDIR)$(moduledir)
+ $(LIBTOOL) --mode=install cp nssov.la $(DESTDIR)$(moduledir)
+ cp ldapns.schema $(DESTDIR)$(schemadir)
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
+clean:
+ rm -f *.*o *.la .libs/*
+ rm -rf .libs
diff --git a/contrib/slapd-modules/nssov/README b/contrib/slapd-modules/nssov/README
new file mode 100644
index 0000000..af8631e
--- /dev/null
+++ b/contrib/slapd-modules/nssov/README
@@ -0,0 +1,134 @@
+This directory contains a slapd overlay, nssov, that handles
+NSS lookup requests through a local Unix Domain socket. It uses the
+same IPC protocol as Arthur de Jong's nss-ldapd, and a complete
+copy of the nss-ldapd source is included here. It also handles
+PAM requests.
+
+To use this code, you will need the client-side stuf library from
+nss-pam-ldapd. You can get it from:
+http://arthurdejong.org/nss-pam-ldapd
+You will not need the nslcd daemon; this overlay replaces that part.
+To disable building of the nslcd daemon in nss-pam-ldapd, add the
+--disable-nslcd option to the nss-pam-ldapd configure script. You
+should already be familiar with the RFC2307 and RFC2307bis schema
+to use this overlay. See the nss-pam-ldapd README for more information
+on the schema and which features are supported.
+
+To use the overlay, add:
+
+ include <path to>nis.schema
+
+ moduleload <path to>nssov.so
+ ...
+
+ database mdb
+ ...
+ overlay nssov
+
+to your slapd configuration file. (The nis.schema file contains
+the original RFC2307 schema. Some modifications will be needed to
+use RFC2307bis.)
+
+The overlay may be configured with Service Search Descriptors (SSDs)
+for each NSS service that will be used. SSDs are configured using
+
+ nssov-ssd <service> <url>
+
+where the <service> may be one of
+ aliases
+ ethers
+ group
+ hosts
+ netgroup
+ networks
+ passwd
+ protocols
+ rpc
+ services
+ shadow
+
+and the <url> must be of the form
+ ldap:///[<basedn>][??[<scope>][?<filter>]]
+
+The <basedn> will default to the first suffix of the current database.
+The <scope> defaults to "subtree". The default <filter> depends on which
+service is being used.
+
+If the local database is actually a proxy to a foreign LDAP server, some
+mapping of schema may be needed. Some simple attribute substitutions may
+be performed using
+
+ nssov-map <service> <orig> <new>
+
+See the nss-ldapd/README for the original attribute names used in this code.
+
+The overlay also supports dynamic configuration in cn=config. The layout
+of the config entry is
+
+ dn: olcOverlay={0}nssov,olcDatabase={1}mdb,cn=config
+ objectClass: olcOverlayConfig
+ objectClass: olcNssOvConfig
+ olcOverlay: {0}nssov
+ olcNssSsd: passwd ldap:///ou=users,dc=example,dc=com??one
+ olcNssMap: passwd uid accountName
+
+which enables the passwd service, and uses the accountName attribute to
+fetch what is usually retrieved from the uid attribute.
+
+PAM authentication, account management, session management, and password
+management are supported.
+
+Authentication is performed using Simple Binds. Since all operations occur
+inside the slapd overlay, "fake" connections are used and they are
+inherently secure. Two methods of mapping the PAM username to an LDAP DN
+are provided:
+ the mapping can be accomplished using slapd's authz-regexp facility. In
+this case, a DN of the form
+ cn=<service>+uid=<user>,cn=<hostname>,cn=pam,cn=auth
+is fed into the regexp matcher. If a match is produced, the resulting DN
+is used.
+ otherwise, the NSS passwd map is invoked (which means it must already
+be configured).
+
+If no DN is found, the overlay returns PAM_USER_UNKNOWN. If the DN is
+found, and Password Policy is supported, then the Bind will use the
+Password Policy control and return expiration information to PAM.
+
+Account management also uses two methods. These methods depend on the
+ldapns.schema included with the nssov source.
+ The first is identical to the method used in PADL's pam_ldap module:
+host and authorizedService attributes may be looked up in the user's entry,
+and checked to determine access. Also a check may be performed to see if
+the user is a member of a particular group. This method is pretty
+inflexible and doesn't scale well to large networks of users, hosts,
+and services.
+ The second uses slapd's ACL engine to check if the user has "compare"
+privilege on an ipHost object whose name matches the current hostname, and
+whose authorizedService attribute matches the current service name. This
+method is preferred, since it allows authorization to be centralized in
+the ipHost entries instead of scattered across the entire user population.
+The ipHost entries must have an authorizedService attribute (e.g. by way
+of the authorizedServiceObject auxiliary class) to use this method.
+
+Session management: the overlay may optionally add a "logged in" attribute
+to a user's entry for successful logins, and delete the corresponding
+value upon logout. The attribute value is of the form
+ <generalizedTime> <host> <service> <tty> (<ruser@rhost>)
+
+Password management: the overlay will perform a PasswordModify exop
+in the server for the given user.
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 1998-2022 The OpenLDAP Foundation.
+Portions Copyright 2008-2009 Howard Chu, Symas Corp. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
diff --git a/contrib/slapd-modules/nssov/alias.c b/contrib/slapd-modules/nssov/alias.c
new file mode 100644
index 0000000..ae131db
--- /dev/null
+++ b/contrib/slapd-modules/nssov/alias.c
@@ -0,0 +1,116 @@
+/* alias.c - mail alias lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+/* Vendor-specific attributes and object classes.
+ * (Mainly from Sun.)
+ * ( 1.3.6.1.4.1.42.2.27.1.2.5 NAME 'nisMailAlias' SUP top STRUCTURAL
+ * DESC 'NIS mail alias'
+ * MUST cn
+ * MAY rfc822MailMember )
+ */
+
+/* the basic search filter for searches */
+static struct berval alias_filter = BER_BVC("(objectClass=nisMailAlias)");
+
+/* the attributes to request with searches */
+static struct berval alias_keys[] = {
+ BER_BVC("cn"),
+ BER_BVC("rfc822MailMember"),
+ BER_BVNULL
+};
+
+NSSOV_INIT(alias)
+
+NSSOV_CBPRIV(alias,
+ struct berval name;
+ char buf[256];);
+
+static int write_alias(nssov_alias_cbp *cbp,Entry *entry)
+{
+ int32_t tmpint32,tmp2int32,tmp3int32;
+ struct berval tmparr[2], empty;
+ struct berval *names, *members;
+ Attribute *a;
+ int i;
+
+ /* get the name of the alias */
+ if (BER_BVISNULL(&cbp->name))
+ {
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[0].an_desc);
+ if ( !a )
+ {
+ Debug(LDAP_DEBUG_ANY,"alias entry %s does not contain %s value\n",
+ entry->e_name.bv_val,cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ names = a->a_vals;
+ }
+ else
+ {
+ names=tmparr;
+ names[0]=cbp->name;
+ BER_BVZERO(&names[1]);
+ }
+ /* get the members of the alias */
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[1].an_desc);
+ if ( !a ) {
+ BER_BVZERO( &empty );
+ members = &empty;
+ } else {
+ members = a->a_vals;
+ }
+ /* for each name, write an entry */
+ for (i=0;!BER_BVISNULL(&names[i]);i++)
+ {
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ WRITE_BERVAL(cbp->fp,&names[i]);
+ WRITE_BVARRAY(cbp->fp,members);
+ }
+ return 0;
+}
+
+NSSOV_CB(alias)
+
+NSSOV_HANDLE(
+ alias,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_STRING(fp,cbp.buf);
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.buf;,
+ Debug(LDAP_DEBUG_TRACE,"nssov_alias_byname(%s)\n",cbp.name.bv_val);,
+ NSLCD_ACTION_ALIAS_BYNAME,
+ nssov_filter_byname(cbp.mi,0,&cbp.name,&filter)
+)
+
+NSSOV_HANDLE(
+ alias,all,
+ struct berval filter;
+ /* no parameters to read */
+ BER_BVZERO(&cbp.name);,
+ Debug(LDAP_DEBUG,"nssov_alias_all()\n");,
+ NSLCD_ACTION_ALIAS_ALL,
+ (filter=cbp.mi->mi_filter,0)
+)
diff --git a/contrib/slapd-modules/nssov/ether.c b/contrib/slapd-modules/nssov/ether.c
new file mode 100644
index 0000000..cb18f1b
--- /dev/null
+++ b/contrib/slapd-modules/nssov/ether.c
@@ -0,0 +1,167 @@
+/* ether.c - ethernet address lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Copyright 2008 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+struct ether_addr {
+ uint8_t ether_addr_octet[6];
+};
+
+/* ( nisSchema.2.11 NAME 'ieee802Device' SUP top AUXILIARY
+ * DESC 'A device with a MAC address; device SHOULD be
+ * used as a structural class'
+ * MAY macAddress )
+ */
+
+/* the basic search filter for searches */
+static struct berval ether_filter = BER_BVC("(objectClass=ieee802Device)");
+
+/* the attributes to request with searches */
+static struct berval ether_keys[] = {
+ BER_BVC("cn"),
+ BER_BVC("macAddress"),
+ BER_BVNULL
+};
+
+NSSOV_INIT(ether)
+
+NSSOV_CBPRIV(ether,
+ char buf[256];
+ struct berval name;
+ struct berval addr;);
+
+#define WRITE_ETHER(fp,addr) \
+ {int ao[6]; \
+ sscanf(addr.bv_val,"%02x:%02x:%02x:%02x:%02x:%02x", \
+ &ao[0], &ao[1], &ao[2], &ao[3], &ao[4], &ao[5] );\
+ tmpaddr.ether_addr_octet[0] = ao[0]; \
+ tmpaddr.ether_addr_octet[1] = ao[1]; \
+ tmpaddr.ether_addr_octet[2] = ao[2]; \
+ tmpaddr.ether_addr_octet[3] = ao[3]; \
+ tmpaddr.ether_addr_octet[4] = ao[4]; \
+ tmpaddr.ether_addr_octet[5] = ao[5]; } \
+ WRITE(fp,&tmpaddr,sizeof(uint8_t[6]));
+
+static int write_ether(nssov_ether_cbp *cbp,Entry *entry)
+{
+ int32_t tmpint32;
+ struct ether_addr tmpaddr;
+ struct berval tmparr[2];
+ struct berval *names,*ethers;
+ Attribute *a;
+ int i,j;
+
+ /* get the name of the ether entry */
+ if (BER_BVISNULL(&cbp->name))
+ {
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[0].an_desc);
+ if ( !a )
+ {
+ Debug(LDAP_DEBUG_ANY,"ether entry %s does not contain %s value\n",
+ entry->e_name.bv_val,cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ names = a->a_vals;
+ }
+ else
+ {
+ names=tmparr;
+ names[0]=cbp->name;
+ BER_BVZERO(&names[1]);
+ }
+ /* get the addresses */
+ if (BER_BVISNULL(&cbp->addr))
+ {
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[1].an_desc);
+ if ( !a )
+ {
+ Debug(LDAP_DEBUG_ANY,"ether entry %s does not contain %s value\n",
+ entry->e_name.bv_val,cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ ethers = a->a_vals;
+ /* TODO: move parsing of addresses up here */
+ }
+ else
+ {
+ ethers=tmparr;
+ ethers[0]=cbp->addr;
+ BER_BVZERO(&ethers[1]);
+ }
+ /* write entries for all names and addresses */
+ for (i=0;!BER_BVISNULL(&names[i]);i++)
+ for (j=0;!BER_BVISNULL(&ethers[j]);j++)
+ {
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ WRITE_BERVAL(cbp->fp,&names[i]);
+ WRITE_ETHER(cbp->fp,ethers[j]);
+ }
+ return 0;
+}
+
+NSSOV_CB(ether)
+
+NSSOV_HANDLE(
+ ether,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ BER_BVZERO(&cbp.addr);
+ READ_STRING(fp,cbp.buf);
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.buf;,
+ Debug(LDAP_DEBUG_TRACE,"nssov_ether_byname(%s)\n",cbp.name.bv_val);,
+ NSLCD_ACTION_ETHER_BYNAME,
+ nssov_filter_byname(cbp.mi,0,&cbp.name,&filter)
+)
+
+NSSOV_HANDLE(
+ ether,byether,
+ struct ether_addr addr;
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ BER_BVZERO(&cbp.name);
+ READ(fp,&addr,sizeof(uint8_t[6]));
+ cbp.addr.bv_len = snprintf(cbp.buf,sizeof(cbp.buf), "%x:%x:%x:%x:%x:%x",
+ addr.ether_addr_octet[0],
+ addr.ether_addr_octet[1],
+ addr.ether_addr_octet[2],
+ addr.ether_addr_octet[3],
+ addr.ether_addr_octet[4],
+ addr.ether_addr_octet[5]);
+ cbp.addr.bv_val = cbp.buf;,
+ Debug(LDAP_DEBUG_TRACE,"nssov_ether_byether(%s)\n",cbp.addr.bv_val);,
+ NSLCD_ACTION_ETHER_BYETHER,
+ nssov_filter_byid(cbp.mi,1,&cbp.addr,&filter)
+)
+
+NSSOV_HANDLE(
+ ether,all,
+ struct berval filter;
+ /* no parameters to read */
+ BER_BVZERO(&cbp.name);
+ BER_BVZERO(&cbp.addr);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_ether_all()\n");,
+ NSLCD_ACTION_ETHER_ALL,
+ (filter=cbp.mi->mi_filter,0)
+)
diff --git a/contrib/slapd-modules/nssov/group.c b/contrib/slapd-modules/nssov/group.c
new file mode 100644
index 0000000..1d93451
--- /dev/null
+++ b/contrib/slapd-modules/nssov/group.c
@@ -0,0 +1,346 @@
+/* group.c - group lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008-2009 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+/* for gid_t */
+#include <grp.h>
+
+/* ( nisSchema.2.2 NAME 'posixGroup' SUP top STRUCTURAL
+ * DESC 'Abstraction of a group of accounts'
+ * MUST ( cn $ gidNumber )
+ * MAY ( userPassword $ memberUid $ description ) )
+ *
+ * apart from that the above the uniqueMember attributes may be
+ * supported in a coming release (they map to DNs, which is an extra
+ * lookup step)
+ *
+ * using nested groups (groups that are member of a group) is currently
+ * not supported, this may be added in a later release
+ */
+
+/* the basic search filter for searches */
+static struct berval group_filter = BER_BVC("(objectClass=posixGroup)");
+
+/* the attributes to request with searches */
+static struct berval group_keys[] = {
+ BER_BVC("cn"),
+ BER_BVC("userPassword"),
+ BER_BVC("gidNumber"),
+ BER_BVC("memberUid"),
+ BER_BVC("uniqueMember"),
+ BER_BVNULL
+};
+
+#define CN_KEY 0
+#define PWD_KEY 1
+#define GID_KEY 2
+#define UID_KEY 3
+#define MEM_KEY 4
+
+/* default values for attributes */
+static struct berval default_group_userPassword = BER_BVC("*"); /* unmatchable */
+
+NSSOV_CBPRIV(group,
+ nssov_info *ni;
+ char buf[256];
+ struct berval name;
+ struct berval gidnum;
+ struct berval user;
+ int wantmembers;);
+
+/* create a search filter for searching a group entry
+ by member uid, return -1 on errors */
+static int mkfilter_group_bymember(nssov_group_cbp *cbp,struct berval *buf)
+{
+ struct berval dn;
+ /* try to translate uid to DN */
+ nssov_uid2dn(cbp->op,cbp->ni,&cbp->user,&dn);
+ if (BER_BVISNULL(&dn)) {
+ if (cbp->user.bv_len + cbp->mi->mi_filter.bv_len + cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_len + 6 >
+ buf->bv_len )
+ return -1;
+ buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))",
+ cbp->mi->mi_filter.bv_val, cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val,
+ cbp->user.bv_val );
+ } else { /* also lookup using user DN */
+ if (cbp->user.bv_len + cbp->mi->mi_filter.bv_len + cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_len +
+ dn.bv_len + cbp->mi->mi_attrs[MEM_KEY].an_desc->ad_cname.bv_len + 12 > buf->bv_len )
+ return -1;
+ buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(|(%s=%s)(%s=%s)))",
+ cbp->mi->mi_filter.bv_val,
+ cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val, cbp->user.bv_val,
+ cbp->mi->mi_attrs[MEM_KEY].an_desc->ad_cname.bv_val, dn.bv_val );
+ }
+ return 0;
+}
+
+NSSOV_INIT(group)
+
+/*
+ Checks to see if the specified name is a valid group name.
+
+ This test is based on the definition from POSIX (IEEE Std 1003.1, 2004,
+ 3.189 Group Name and 3.276 Portable Filename Character Set):
+ http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_189
+ http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_276
+
+ The standard defines group names valid if they only contain characters from
+ the set [A-Za-z0-9._-] where the hyphen should not be used as first
+ character.
+*/
+static int isvalidgroupname(struct berval *name)
+{
+ int i;
+
+ if ( !name->bv_val || !name->bv_len )
+ return 0;
+ /* check first character */
+ if ( ! ( (name->bv_val[0]>='A' && name->bv_val[0] <= 'Z') ||
+ (name->bv_val[0]>='a' && name->bv_val[0] <= 'z') ||
+ (name->bv_val[0]>='0' && name->bv_val[0] <= '9') ||
+ name->bv_val[0]=='.' || name->bv_val[0]=='_' ) )
+ return 0;
+ /* check other characters */
+ for (i=1;i<name->bv_len;i++)
+ {
+#ifndef STRICT_GROUPS
+ /* allow spaces too */
+ if (name->bv_val[i] == ' ') continue;
+#endif
+ if ( ! ( (name->bv_val[i]>='A' && name->bv_val[i] <= 'Z') ||
+ (name->bv_val[i]>='a' && name->bv_val[i] <= 'z') ||
+ (name->bv_val[i]>='0' && name->bv_val[i] <= '9') ||
+ name->bv_val[i]=='.' || name->bv_val[i]=='_' || name->bv_val[i]=='-') )
+ return 0;
+ }
+ /* no test failed so it must be good */
+ return -1;
+}
+
+static int write_group(nssov_group_cbp *cbp,Entry *entry)
+{
+ struct berval tmparr[2], tmpgid[2];
+ struct berval *names,*gids,*members;
+ struct berval passwd = {0};
+ Attribute *a;
+ int i,j,nummembers,rc = 0;
+
+ /* get group name (cn) */
+ if (BER_BVISNULL(&cbp->name))
+ {
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[CN_KEY].an_desc);
+ if ( !a )
+ {
+ Debug(LDAP_DEBUG_ANY,"group entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ names = a->a_vals;
+ }
+ else
+ {
+ names=tmparr;
+ names[0]=cbp->name;
+ BER_BVZERO(&names[1]);
+ }
+ /* get the group id(s) */
+ if (BER_BVISNULL(&cbp->gidnum))
+ {
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GID_KEY].an_desc);
+ if ( !a )
+ {
+ Debug(LDAP_DEBUG_ANY,"group entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[GID_KEY].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ gids = a->a_vals;
+ }
+ else
+ {
+ gids=tmpgid;
+ gids[0]=cbp->gidnum;
+ BER_BVZERO(&gids[1]);
+ }
+ /* get group passwd (userPassword) (use only first entry) */
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[PWD_KEY].an_desc);
+ if (a)
+ get_userpassword(&a->a_vals[0], &passwd);
+ if (BER_BVISNULL(&passwd))
+ passwd=default_group_userPassword;
+ /* get group members (memberUid&uniqueMember) */
+ if (cbp->wantmembers) {
+ Attribute *b;
+ i = 0; j = 0;
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UID_KEY].an_desc);
+ b = attr_find(entry->e_attrs, cbp->mi->mi_attrs[MEM_KEY].an_desc);
+ if ( a )
+ i += a->a_numvals;
+ if ( b )
+ i += b->a_numvals;
+ if ( i ) {
+ members = cbp->op->o_tmpalloc( (i+1) * sizeof(struct berval), cbp->op->o_tmpmemctx );
+
+ if ( a ) {
+ for (i=0; i<a->a_numvals; i++) {
+ if (isvalidusername(&a->a_vals[i])) {
+ ber_dupbv_x(&members[j],&a->a_vals[i],cbp->op->o_tmpmemctx);
+ j++;
+ }
+ }
+ }
+ a = b;
+ if ( a ) {
+ for (i=0; i<a->a_numvals; i++) {
+ if (nssov_dn2uid(cbp->op,cbp->ni,&a->a_nvals[i],&members[j]))
+ j++;
+ }
+ }
+ nummembers = j;
+ BER_BVZERO(&members[j]);
+ } else {
+ members=NULL;
+ nummembers = 0;
+ }
+
+ } else {
+ members=NULL;
+ nummembers = 0;
+ }
+ /* write entries for all names and gids */
+ for (i=0;!BER_BVISNULL(&names[i]);i++)
+ {
+ if (!isvalidgroupname(&names[i]))
+ {
+ Debug(LDAP_DEBUG_ANY,"nssov: group entry %s contains invalid group name: \"%s\"\n",
+ entry->e_name.bv_val,names[i].bv_val );
+ }
+ else
+ {
+ for (j=0;!BER_BVISNULL(&gids[j]);j++)
+ {
+ char *tmp;
+ int tmpint32;
+ gid_t gid;
+ gid = strtol(gids[j].bv_val, &tmp, 0);
+ if ( *tmp ) {
+ Debug(LDAP_DEBUG_ANY,"nssov: group entry %s contains non-numeric %s value: \"%s\"\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[GID_KEY].an_desc->ad_cname.bv_val,
+ names[i].bv_val);
+ continue;
+ }
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ WRITE_BERVAL(cbp->fp,&names[i]);
+ WRITE_BERVAL(cbp->fp,&passwd);
+ WRITE_INT32(cbp->fp,gid);
+ /* write a list of values */
+ WRITE_INT32(cbp->fp,nummembers);
+ if (nummembers)
+ {
+ int k;
+ for (k=0;k<nummembers;k++) {
+ WRITE_BERVAL(cbp->fp,&members[k]);
+ }
+ }
+ }
+ }
+ }
+ /* free and return */
+ if (members!=NULL)
+ ber_bvarray_free_x( members, cbp->op->o_tmpmemctx );
+ return rc;
+}
+
+NSSOV_CB(group)
+
+NSSOV_HANDLE(
+ group,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_STRING(fp,cbp.buf);
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.buf;
+ if (!isvalidgroupname(&cbp.name)) {
+ Debug(LDAP_DEBUG_ANY,"nssov_group_byname(%s): invalid group name\n",cbp.name.bv_val);
+ return -1;
+ }
+ cbp.wantmembers = 1;
+ cbp.ni = ni;
+ BER_BVZERO(&cbp.gidnum);
+ BER_BVZERO(&cbp.user);,
+ Debug(LDAP_DEBUG_TRACE,"nslcd_group_byname(%s)\n",cbp.name.bv_val);,
+ NSLCD_ACTION_GROUP_BYNAME,
+ nssov_filter_byname(cbp.mi,CN_KEY,&cbp.name,&filter)
+)
+
+NSSOV_HANDLE(
+ group,bygid,
+ gid_t gid;
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_INT32(fp,gid);
+ cbp.gidnum.bv_val = cbp.buf;
+ cbp.gidnum.bv_len = snprintf(cbp.buf,sizeof(cbp.buf),"%d",gid);
+ cbp.wantmembers = 1;
+ cbp.ni = ni;
+ BER_BVZERO(&cbp.name);
+ BER_BVZERO(&cbp.user);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_group_bygid(%s)\n",cbp.gidnum.bv_val);,
+ NSLCD_ACTION_GROUP_BYGID,
+ nssov_filter_byid(cbp.mi,GID_KEY,&cbp.gidnum,&filter)
+)
+
+NSSOV_HANDLE(
+ group,bymember,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_STRING(fp,cbp.buf);
+ cbp.user.bv_len = tmpint32;
+ cbp.user.bv_val = cbp.buf;
+ if (!isvalidusername(&cbp.user)) {
+ Debug(LDAP_DEBUG_ANY,"nssov_group_bymember(%s): invalid user name\n",cbp.user.bv_val);
+ return -1;
+ }
+ cbp.wantmembers = 0;
+ cbp.ni = ni;
+ BER_BVZERO(&cbp.name);
+ BER_BVZERO(&cbp.gidnum);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_group_bymember(%s)\n",cbp.user.bv_val);,
+ NSLCD_ACTION_GROUP_BYMEMBER,
+ mkfilter_group_bymember(&cbp,&filter)
+)
+
+NSSOV_HANDLE(
+ group,all,
+ struct berval filter;
+ /* no parameters to read */
+ cbp.wantmembers = 1;
+ cbp.ni = ni;
+ BER_BVZERO(&cbp.name);
+ BER_BVZERO(&cbp.gidnum);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_group_all()\n");,
+ NSLCD_ACTION_GROUP_ALL,
+ (filter=cbp.mi->mi_filter,0)
+)
diff --git a/contrib/slapd-modules/nssov/host.c b/contrib/slapd-modules/nssov/host.c
new file mode 100644
index 0000000..008b454
--- /dev/null
+++ b/contrib/slapd-modules/nssov/host.c
@@ -0,0 +1,161 @@
+/* host.c - host lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+/* ( nisSchema.2.6 NAME 'ipHost' SUP top AUXILIARY
+ * DESC 'Abstraction of a host, an IP device. The distinguished
+ * value of the cn attribute denotes the host's canonical
+ * name. Device SHOULD be used as a structural class'
+ * MUST ( cn $ ipHostNumber )
+ * MAY ( l $ description $ manager ) )
+ */
+
+/* the basic search filter for searches */
+static struct berval host_filter = BER_BVC("(objectClass=ipHost)");
+
+/* the attributes to request with searches */
+static struct berval host_keys[] = {
+ BER_BVC("cn"),
+ BER_BVC("ipHostNumber"),
+ BER_BVNULL
+};
+
+NSSOV_INIT(host)
+
+NSSOV_CBPRIV(host,
+ char buf[1024];
+ struct berval name;
+ struct berval addr;);
+
+/* write a single host entry to the stream */
+static int write_host(nssov_host_cbp *cbp,Entry *entry)
+{
+ int32_t tmpint32;
+ int numaddr,i,numname,dupname;
+ struct berval name,*names,*addrs;
+ Attribute *a;
+
+ /* get the most canonical name */
+ nssov_find_rdnval( &entry->e_nname, cbp->mi->mi_attrs[0].an_desc, &name );
+ /* get the other names for the host */
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[0].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"host entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ names = a->a_vals;
+ numname = a->a_numvals;
+ /* if the name is not yet found, get the first entry from names */
+ if (BER_BVISNULL(&name)) {
+ name=names[0];
+ dupname = 0;
+ } else {
+ dupname = -1;
+ for (i=0; i<numname; i++) {
+ if ( bvmatch(&name, &a->a_nvals[i])) {
+ dupname = i;
+ break;
+ }
+ }
+ }
+ /* get the addresses */
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[1].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"host entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ addrs = a->a_vals;
+ numaddr = a->a_numvals;
+ /* write the entry */
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ WRITE_BERVAL(cbp->fp,&name);
+ if ( dupname >= 0 ) {
+ WRITE_INT32(cbp->fp,numname-1);
+ } else {
+ WRITE_INT32(cbp->fp,numname);
+ }
+ for (i=0;i<numname;i++) {
+ if (i == dupname) continue;
+ WRITE_BERVAL(cbp->fp,&names[i]);
+ }
+ WRITE_INT32(cbp->fp,numaddr);
+ for (i=0;i<numaddr;i++)
+ {
+ WRITE_ADDRESS(cbp->fp,&addrs[i]);
+ }
+ return 0;
+}
+
+NSSOV_CB(host)
+
+NSSOV_HANDLE(
+ host,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ BER_BVZERO(&cbp.addr);
+ READ_STRING(fp,cbp.buf);
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.buf;,
+ Debug(LDAP_DEBUG_TRACE,"nssov_host_byname(%s)\n",cbp.name.bv_val);,
+ NSLCD_ACTION_HOST_BYNAME,
+ nssov_filter_byname(cbp.mi,0,&cbp.name,&filter)
+)
+
+NSSOV_HANDLE(
+ host,byaddr,
+ int af;
+ char addr[64];
+ int len=sizeof(addr);
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ BER_BVZERO(&cbp.name);
+ READ_ADDRESS(fp,addr,len,af);
+ /* translate the address to a string */
+ if (inet_ntop(af,addr,cbp.buf,sizeof(cbp.buf))==NULL)
+ {
+ Debug(LDAP_DEBUG_ANY,"nssov: unable to convert address to string\n");
+ return -1;
+ }
+ cbp.addr.bv_val = cbp.buf;
+ cbp.addr.bv_len = strlen(cbp.buf);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_host_byaddr(%s)\n",cbp.addr.bv_val);,
+ NSLCD_ACTION_HOST_BYADDR,
+ nssov_filter_byid(cbp.mi,1,&cbp.addr,&filter)
+)
+
+NSSOV_HANDLE(
+ host,all,
+ struct berval filter;
+ /* no parameters to read */
+ BER_BVZERO(&cbp.name);
+ BER_BVZERO(&cbp.addr);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_host_all()\n");,
+ NSLCD_ACTION_HOST_ALL,
+ (filter=cbp.mi->mi_filter,0)
+)
diff --git a/contrib/slapd-modules/nssov/ldapns.schema b/contrib/slapd-modules/nssov/ldapns.schema
new file mode 100644
index 0000000..f6f7c9f
--- /dev/null
+++ b/contrib/slapd-modules/nssov/ldapns.schema
@@ -0,0 +1,38 @@
+# $OpenLDAP$
+# $Id: ldapns.schema,v 1.3 2009-10-01 19:17:20 tedcheng Exp $
+# LDAP Name Service Additional Schema
+# http://www.iana.org/assignments/gssapi-service-names
+
+#
+# Not part of the distribution: this is a workaround!
+#
+
+attributetype ( 1.3.6.1.4.1.5322.17.2.1 NAME 'authorizedService'
+ DESC 'IANA GSS-API authorized service name'
+ EQUALITY caseIgnoreMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+
+attributetype ( 1.3.6.1.4.1.5322.17.2.2 NAME 'loginStatus'
+ DESC 'Currently logged in sessions for a user'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ ORDERING caseIgnoreOrderingMatch
+ SYNTAX OMsDirectoryString )
+
+objectclass ( 1.3.6.1.4.1.5322.17.1.1 NAME 'authorizedServiceObject'
+ DESC 'Auxiliary object class for adding authorizedService attribute'
+ SUP top
+ AUXILIARY
+ MAY authorizedService )
+
+objectclass ( 1.3.6.1.4.1.5322.17.1.2 NAME 'hostObject'
+ DESC 'Auxiliary object class for adding host attribute'
+ SUP top
+ AUXILIARY
+ MAY host )
+
+objectclass ( 1.3.6.1.4.1.5322.17.1.3 NAME 'loginStatusObject'
+ DESC 'Auxiliary object class for login status attribute'
+ SUP top
+ AUXILIARY
+ MAY loginStatus )
diff --git a/contrib/slapd-modules/nssov/netgroup.c b/contrib/slapd-modules/nssov/netgroup.c
new file mode 100644
index 0000000..7211a9a
--- /dev/null
+++ b/contrib/slapd-modules/nssov/netgroup.c
@@ -0,0 +1,199 @@
+/* netgroup.c - netgroup lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+#include <ac/ctype.h>
+
+/* ( nisSchema.2.8 NAME 'nisNetgroup' SUP top STRUCTURAL
+ * DESC 'Abstraction of a netgroup. May refer to other netgroups'
+ * MUST cn
+ * MAY ( nisNetgroupTriple $ memberNisNetgroup $ description ) )
+ */
+
+/* the basic search filter for searches */
+static struct berval netgroup_filter = BER_BVC("(objectClass=nisNetgroup)");
+
+/* the attributes to request with searches */
+static struct berval netgroup_keys[] = {
+ BER_BVC("cn"),
+ BER_BVC("nisNetgroupTriple"),
+ BER_BVC("memberNisNetgroup"),
+ BER_BVNULL
+};
+
+NSSOV_INIT(netgroup)
+
+NSSOV_CBPRIV(netgroup,
+ char buf[256];
+ struct berval name;);
+
+static int write_string_stripspace_len(TFILE *fp,const char *str,int len)
+{
+ int32_t tmpint32;
+ int i,j;
+ DEBUG_PRINT("WRITE_STRING: var="__STRING(str)" string=\"%s\"",str);
+ if (str==NULL)
+ {
+ WRITE_INT32(fp,0);
+ }
+ else
+ {
+ /* skip leading spaces */
+ for (i=0;(str[i]!='\0')&&(isspace(str[i]));i++)
+ /* nothing else to do */ ;
+ /* skip trailing spaces */
+ for (j=len;(j>i)&&(isspace(str[j-1]));j--)
+ /* nothing else to do */ ;
+ /* write length of string */
+ WRITE_INT32(fp,j-i);
+ /* write string itself */
+ if (j>i)
+ {
+ WRITE(fp,str+i,j-i);
+ }
+ }
+ /* we're done */
+ return 0;
+}
+
+#define WRITE_STRING_STRIPSPACE_LEN(fp,str,len) \
+ if (write_string_stripspace_len(fp,str,len)) \
+ return -1;
+
+#define WRITE_STRING_STRIPSPACE(fp,str) \
+ WRITE_STRING_STRIPSPACE_LEN(fp,str,strlen(str))
+
+static int write_netgroup_triple(TFILE *fp,const char *triple)
+{
+ int32_t tmpint32;
+ int i;
+ int hostb,hoste,userb,usere,domainb,domaine;
+ /* skip leading spaces */
+ for (i=0;(triple[i]!='\0')&&(isspace(triple[i]));i++)
+ /* nothing else to do */ ;
+ /* we should have a bracket now */
+ if (triple[i]!='(')
+ {
+ Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): entry does not begin with '(' (entry skipped)\n" );
+ return 0;
+ }
+ i++;
+ hostb=i;
+ /* find comma (end of host string) */
+ for (;(triple[i]!='\0')&&(triple[i]!=',');i++)
+ /* nothing else to do */ ;
+ if (triple[i]!=',')
+ {
+ Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): missing ',' (entry skipped)\n" );
+ return 0;
+ }
+ hoste=i;
+ i++;
+ userb=i;
+ /* find comma (end of user string) */
+ for (;(triple[i]!='\0')&&(triple[i]!=',');i++)
+ /* nothing else to do */ ;
+ if (triple[i]!=',')
+ {
+ Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): missing ',' (entry skipped)\n" );
+ return 0;
+ }
+ usere=i;
+ i++;
+ domainb=i;
+ /* find closing bracket (end of domain string) */
+ for (;(triple[i]!='\0')&&(triple[i]!=')');i++)
+ /* nothing else to do */ ;
+ if (triple[i]!=')')
+ {
+ Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): missing ')' (entry skipped)\n" );
+ return 0;
+ }
+ domaine=i;
+ i++;
+ /* skip trailing spaces */
+ for (;(triple[i]!='\0')&&(isspace(triple[i]));i++)
+ /* nothing else to do */ ;
+ /* if anything is left in the string we have a problem */
+ if (triple[i]!='\0')
+ {
+ Debug(LDAP_DEBUG_ANY,"write_netgroup_triple(): string contains trailing data (entry skipped)\n" );
+ return 0;
+ }
+ /* write strings */
+ WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
+ WRITE_INT32(fp,NSLCD_NETGROUP_TYPE_TRIPLE);
+ WRITE_STRING_STRIPSPACE_LEN(fp,triple+hostb,hoste-hostb)
+ WRITE_STRING_STRIPSPACE_LEN(fp,triple+userb,usere-userb)
+ WRITE_STRING_STRIPSPACE_LEN(fp,triple+domainb,domaine-domainb)
+ /* we're done */
+ return 0;
+}
+
+static int write_netgroup(nssov_netgroup_cbp *cbp,Entry *entry)
+{
+ int32_t tmpint32;
+ int i;
+ Attribute *a;
+
+ /* get the netgroup triples and member */
+ a = attr_find(entry->e_attrs,cbp->mi->mi_attrs[1].an_desc);
+ if ( a ) {
+ /* write the netgroup triples */
+ for (i=0;i<a->a_numvals;i++)
+ {
+ if (write_netgroup_triple(cbp->fp, a->a_vals[i].bv_val))
+ return -1;
+ }
+ }
+ a = attr_find(entry->e_attrs,cbp->mi->mi_attrs[2].an_desc);
+ if ( a ) {
+ /* write netgroup members */
+ for (i=0;i<a->a_numvals;i++)
+ {
+ /* write the result code */
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ /* write triple indicator */
+ WRITE_INT32(cbp->fp,NSLCD_NETGROUP_TYPE_NETGROUP);
+ /* write netgroup name */
+ if (write_string_stripspace_len(cbp->fp,a->a_vals[i].bv_val,a->a_vals[i].bv_len))
+ return -1;
+ }
+ }
+ /* we're done */
+ return 0;
+}
+
+NSSOV_CB(netgroup)
+
+NSSOV_HANDLE(
+ netgroup,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_STRING(fp,cbp.buf);,
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.buf;
+ Debug(LDAP_DEBUG_TRACE,"nssov_netgroup_byname(%s)\n",cbp.name.bv_val);,
+ NSLCD_ACTION_NETGROUP_BYNAME,
+ nssov_filter_byname(cbp.mi,0,&cbp.name,&filter)
+)
diff --git a/contrib/slapd-modules/nssov/network.c b/contrib/slapd-modules/nssov/network.c
new file mode 100644
index 0000000..0f67fa8
--- /dev/null
+++ b/contrib/slapd-modules/nssov/network.c
@@ -0,0 +1,161 @@
+/* network.c - network address lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+#include <ac/socket.h>
+
+/* ( nisSchema.2.7 NAME 'ipNetwork' SUP top STRUCTURAL
+ * DESC 'Abstraction of a network. The distinguished value of
+ * MUST ( cn $ ipNetworkNumber )
+ * MAY ( ipNetmaskNumber $ l $ description $ manager ) )
+ */
+
+/* the basic search filter for searches */
+static struct berval network_filter = BER_BVC("(objectClass=ipNetwork)");
+
+/* the attributes used in searches */
+static struct berval network_keys[] = {
+ BER_BVC("cn"),
+ BER_BVC("ipNetworkNumber"),
+ BER_BVNULL
+};
+
+NSSOV_INIT(network)
+
+NSSOV_CBPRIV(network,
+ char buf[1024];
+ struct berval name;
+ struct berval addr;);
+
+/* write a single network entry to the stream */
+static int write_network(nssov_network_cbp *cbp,Entry *entry)
+{
+ int32_t tmpint32;
+ int numaddr,i,numname,dupname;
+ struct berval name, *names, *addrs;
+ Attribute *a;
+
+ /* get the most canonical name */
+ nssov_find_rdnval( &entry->e_nname, cbp->mi->mi_attrs[0].an_desc, &name);
+ /* get the other names for the network */
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[0].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"network entry %s does not contain %s value\n",
+ entry->e_name.bv_val,cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ names = a->a_vals;
+ numname = a->a_numvals;
+ /* if the name is not yet found, get the first entry from names */
+ if (BER_BVISNULL(&name)) {
+ name=names[0];
+ dupname = 0;
+ } else {
+ dupname = -1;
+ for (i=0; i<numname; i++) {
+ if ( bvmatch(&name, &a->a_nvals[i])) {
+ dupname = i;
+ break;
+ }
+ }
+ }
+ /* get the addresses */
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[1].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"network entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ addrs = a->a_vals;
+ numaddr = a->a_numvals;
+ /* write the entry */
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ WRITE_BERVAL(cbp->fp,&name);
+ if ( dupname >= 0 ) {
+ WRITE_INT32(cbp->fp,numname-1);
+ } else {
+ WRITE_INT32(cbp->fp,numname);
+ }
+ for (i=0;i<numname;i++) {
+ if (i == dupname) continue;
+ WRITE_BERVAL(cbp->fp,&names[i]);
+ }
+ WRITE_INT32(cbp->fp,numaddr);
+ for (i=0;i<numaddr;i++)
+ {
+ WRITE_ADDRESS(cbp->fp,&addrs[i]);
+ }
+ return 0;
+}
+
+NSSOV_CB(network)
+
+NSSOV_HANDLE(
+ network,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ BER_BVZERO(&cbp.addr);
+ READ_STRING(fp,cbp.buf);
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.buf;,
+ Debug(LDAP_DEBUG_TRACE,"nssov_network_byname(%s)\n",cbp.name.bv_val);,
+ NSLCD_ACTION_NETWORK_BYNAME,
+ nssov_filter_byname(cbp.mi,0,&cbp.name,&filter)
+)
+
+NSSOV_HANDLE(
+ network,byaddr,
+ int af;
+ char addr[64];
+ int len=sizeof(addr);
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ BER_BVZERO(&cbp.name);
+ READ_ADDRESS(fp,addr,len,af);
+ /* translate the address to a string */
+ if (inet_ntop(af,addr,cbp.buf,sizeof(cbp.buf))==NULL)
+ {
+ Debug(LDAP_DEBUG_ANY,"nssov: unable to convert address to string\n");
+ return -1;
+ }
+ cbp.addr.bv_val = cbp.buf;
+ cbp.addr.bv_len = strlen(cbp.buf);,
+ Debug(LDAP_DEBUG_TRACE,"nslcd_network_byaddr(%s)\n",cbp.addr.bv_val);,
+ NSLCD_ACTION_NETWORK_BYADDR,
+ nssov_filter_byid(cbp.mi,1,&cbp.addr,&filter)
+)
+
+NSSOV_HANDLE(
+ network,all,
+ struct berval filter;
+ /* no parameters to read */
+ BER_BVZERO(&cbp.name);
+ BER_BVZERO(&cbp.addr);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_network_all()\n");,
+ NSLCD_ACTION_NETWORK_ALL,
+ (filter=cbp.mi->mi_filter,0)
+)
diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/README b/contrib/slapd-modules/nssov/nss-pam-ldapd/README
new file mode 100644
index 0000000..4176ad7
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/README
@@ -0,0 +1,15 @@
+These files were pulled from the nss-pam-ldapd project version 0.9.4.
+Copyright notices are in the individual files.
+
+This is not the full distribution of nss-pam-ldapd, and does not
+include the client-side stub libraries. Get the latest release of
+nss-pam-ldapd from http://arthurdejong.org/nss-pam-ldapd/ to use
+this overlay.
+
+If your system already has the nss-pam-ldapd stub libraries
+installed, make sure the versions match the version number
+shown above. Otherwise, there may be incompatible differences in
+the protocols being used. Currently nssov requires at least
+version 0.9.0. If your system's version is older, you will need
+to install the client-side stubs from source.
+
diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/attrs.h b/contrib/slapd-modules/nssov/nss-pam-ldapd/attrs.h
new file mode 100644
index 0000000..2efedc6
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/attrs.h
@@ -0,0 +1,91 @@
+/*
+ attrs.h - wrapper macros for the gcc __attribute__(()) directive
+
+ Copyright (C) 2007, 2008, 2012 Arthur de Jong
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA
+*/
+
+#ifndef COMPAT__ATTRS_H
+#define COMPAT__ATTRS_H 1
+
+/* macro for testing the version of GCC */
+#define GCC_VERSION(major, minor) \
+ ((__GNUC__ > (major)) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor)))
+
+/* These are macros to use some gcc-specific flags in case they're available
+ and otherwise define them to empty strings. This allows us to give
+ the compiler some extra information.
+ See http://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html
+ for a list of attributes supported by gcc */
+
+/* this is used to flag function parameters that are not used in the function
+ body. */
+#if GCC_VERSION(3, 0)
+#define UNUSED(x) x __attribute__((__unused__))
+#else
+#define UNUSED(x) x
+#endif
+
+/* this is used to add extra format checking to the function calls as if this
+ was a printf()-like function */
+#if GCC_VERSION(3, 0)
+#define LIKE_PRINTF(format_idx, arg_idx) \
+ __attribute__((__format__(__printf__, format_idx, arg_idx)))
+#else
+#define LIKE_PRINTF(format_idx, arg_idx) /* no attribute */
+#endif
+
+/* indicates that the function is "pure": its result is purely based on
+ the parameters and has no side effects or used static data */
+#if GCC_VERSION(3, 0)
+#define PURE __attribute__((__pure__))
+#else
+#define PURE /* no attribute */
+#endif
+
+/* the function returns a new data structure that has been freshly
+ allocated */
+#if GCC_VERSION(3, 0)
+#define LIKE_MALLOC __attribute__((__malloc__))
+#else
+#define LIKE_MALLOC /* no attribute */
+#endif
+
+/* the function's return value should be used by the caller */
+#if GCC_VERSION(3, 4)
+#define MUST_USE __attribute__((__warn_unused_result__))
+#else
+#define MUST_USE /* no attribute */
+#endif
+
+/* the function's return value should be used by the caller */
+#if GCC_VERSION(2, 5)
+#define NORETURN __attribute__((__noreturn__))
+#else
+#define NORETURN /* no attribute */
+#endif
+
+/* define __STRING if it's not yet defined */
+#ifndef __STRING
+#ifdef __STDC__
+#define __STRING(x) #x
+#else /* __STDC__ */
+#define __STRING(x) "x"
+#endif /* not __STDC__ */
+#endif /* not __STRING */
+
+#endif /* not COMPAT__ATTRS_H */
diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd-prot.h b/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd-prot.h
new file mode 100644
index 0000000..21ec7c2
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd-prot.h
@@ -0,0 +1,391 @@
+/*
+ nslcd-prot.h - helper macros for reading and writing in protocol streams
+
+ Copyright (C) 2006 West Consulting
+ Copyright (C) 2006-2014 Arthur de Jong
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA
+*/
+
+#ifndef COMMON__NSLCD_PROT_H
+#define COMMON__NSLCD_PROT_H 1
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include "tio.h"
+
+/* If you use these macros you should define the following macros to
+ handle error conditions (these marcos should clean up and return from the
+ function):
+ ERROR_OUT_WRITEERROR(fp)
+ ERROR_OUT_READERROR(fp)
+ ERROR_OUT_BUFERROR(fp)
+ ERROR_OUT_NOSUCCESS(fp) */
+
+
+/* Debugging marcos that can be used to enable detailed protocol logging,
+ pass -DDEBUG_PROT to do overall protocol debugging, and -DDEBUG_PROT_DUMP
+ to dump the actual bytestream. */
+
+#ifdef DEBUG_PROT
+/* define a debugging macro to output logging */
+#include <string.h>
+#include <errno.h>
+#define DEBUG_PRINT(fmt, arg) \
+ fprintf(stderr, "%s:%d:%s: " fmt "\n", __FILE__, __LINE__, \
+ __PRETTY_FUNCTION__, arg);
+#else /* DEBUG_PROT */
+/* define an empty debug macro to disable logging */
+#define DEBUG_PRINT(fmt, arg)
+#endif /* not DEBUG_PROT */
+
+#ifdef DEBUG_PROT_DUMP
+/* define a debugging macro to output detailed logging */
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif /* HAVE_STDINT_H */
+static void debug_dump(const void *ptr, size_t size)
+{
+ int i;
+ for (i = 0; i < size; i++)
+ fprintf(stderr, " %02x", ((const uint8_t *)ptr)[i]);
+ fprintf(stderr, "\n");
+}
+#define DEBUG_DUMP(ptr, size) \
+ fprintf(stderr, "%s:%d:%s:", __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ debug_dump(ptr, size);
+#else /* DEBUG_PROT_DUMP */
+/* define an empty debug macro to disable logging */
+#define DEBUG_DUMP(ptr, size)
+#endif /* not DEBUG_PROT_DUMP */
+
+
+/* WRITE marcos, used for writing data, on write error they will
+ call the ERROR_OUT_WRITEERROR macro
+ these macros may require the availability of the following
+ variables:
+ int32_t tmpint32; - temporary variable
+ */
+
+#define WRITE(fp, ptr, size) \
+ DEBUG_PRINT("WRITE : var="__STRING(ptr)" size=%d", (int)size); \
+ DEBUG_DUMP(ptr, size); \
+ if (tio_write(fp, ptr, (size_t)size)) \
+ { \
+ char ebuf[128]; \
+ int saved_errno = errno; \
+ DEBUG_PRINT("WRITE : var="__STRING(ptr)" error: %s", \
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf))); \
+ ERROR_OUT_WRITEERROR(fp); \
+ }
+
+#define WRITE_INT32(fp, i) \
+ DEBUG_PRINT("WRITE_INT32 : var="__STRING(i)" int32=%08x", (int)i); \
+ tmpint32 = htonl((int32_t)(i)); \
+ WRITE(fp, &tmpint32, sizeof(int32_t))
+
+#define WRITE_STRING(fp, str) \
+ DEBUG_PRINT("WRITE_STRING: var="__STRING(str)" string=\"%s\"", (str)); \
+ if ((str) == NULL) \
+ { \
+ WRITE_INT32(fp, 0); \
+ } \
+ else \
+ { \
+ WRITE_INT32(fp, strlen(str)); \
+ tmpint32 = ntohl(tmpint32); \
+ if (tmpint32 > 0) \
+ { \
+ WRITE(fp, (str), tmpint32); \
+ } \
+ }
+
+#define WRITE_STRINGLIST(fp, arr) \
+ if ((arr) == NULL) \
+ { \
+ DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", 0); \
+ WRITE_INT32(fp, 0); \
+ } \
+ else \
+ { \
+ /* first determine length of array */ \
+ for (tmp3int32 = 0; (arr)[tmp3int32] != NULL; tmp3int32++) \
+ /* noting */ ; \
+ /* write number of strings */ \
+ DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \
+ WRITE_INT32(fp, tmp3int32); \
+ /* write strings */ \
+ for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \
+ { \
+ WRITE_STRING(fp, (arr)[tmp2int32]); \
+ } \
+ }
+
+#define WRITE_STRINGLIST_EXCEPT(fp, arr, not) \
+ /* first determine length of array */ \
+ tmp3int32 = 0; \
+ for (tmp2int32 = 0; (arr)[tmp2int32] != NULL; tmp2int32++) \
+ if (strcmp((arr)[tmp2int32], (not)) != 0) \
+ tmp3int32++; \
+ /* write number of strings (mius one because we intend to skip one) */ \
+ DEBUG_PRINT("WRITE_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \
+ WRITE_INT32(fp, tmp3int32); \
+ /* write strings */ \
+ for (tmp2int32 = 0; (arr)[tmp2int32] != NULL; tmp2int32++) \
+ { \
+ if (strcmp((arr)[tmp2int32], (not)) != 0) \
+ { \
+ WRITE_STRING(fp, (arr)[tmp2int32]); \
+ } \
+ }
+
+/* READ macros, used for reading data, on read error they will
+ call the ERROR_OUT_READERROR or ERROR_OUT_BUFERROR macro
+ these macros may require the availability of the following
+ variables:
+ int32_t tmpint32; - temporary variable
+ */
+
+#define READ(fp, ptr, size) \
+ if (tio_read(fp, ptr, (size_t)size)) \
+ { \
+ char ebuf[128]; \
+ int saved_errno = errno; \
+ DEBUG_PRINT("READ : var="__STRING(ptr)" error: %s", \
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf))); \
+ ERROR_OUT_READERROR(fp); \
+ } \
+ DEBUG_PRINT("READ : var="__STRING(ptr)" size=%d", (int)(size)); \
+ DEBUG_DUMP(ptr, size);
+
+#define READ_INT32(fp, i) \
+ READ(fp, &tmpint32, sizeof(int32_t)); \
+ (i) = (int32_t)ntohl(tmpint32); \
+ DEBUG_PRINT("READ_INT32 : var="__STRING(i)" int32==%08x", (int)(i));
+
+/* read a string in a fixed-size "normal" buffer */
+#define READ_STRING(fp, buffer) \
+ /* read the size of the string */ \
+ READ(fp, &tmpint32, sizeof(int32_t)); \
+ tmpint32 = ntohl(tmpint32); \
+ DEBUG_PRINT("READ_STRING: var="__STRING(buffer)" strlen=%d", tmpint32); \
+ /* check if read would fit */ \
+ if (((size_t)tmpint32) >= sizeof(buffer)) \
+ { \
+ /* will not fit */ \
+ tmpint32 = (tmpint32 - sizeof(buffer)) + 1; \
+ DEBUG_PRINT("READ : buffer %d bytes too small", tmpint32); \
+ ERROR_OUT_BUFERROR(fp); \
+ } \
+ /* read string from the stream */ \
+ if (tmpint32 > 0) \
+ { \
+ READ(fp, buffer, (size_t)tmpint32); \
+ } \
+ /* null-terminate string in buffer */ \
+ buffer[tmpint32] = '\0'; \
+ DEBUG_PRINT("READ_STRING: var="__STRING(buffer)" string=\"%s\"", buffer);
+
+
+/* READ BUF macros that read data into a pre-allocated buffer.
+ these macros may require the availability of the following
+ variables:
+ int32_t tmpint32; - temporary variable
+ char *buffer; - pointer to a buffer for reading strings
+ size_t buflen; - the size of the buffer
+ size_t bufptr; - the current position in the buffer
+ */
+
+/* current position in the buffer */
+#define BUF_CUR \
+ (buffer + bufptr)
+
+/* check that the buffer has sz bytes left in it */
+#define BUF_CHECK(fp, sz) \
+ if ((bufptr + (size_t)(sz)) > buflen) \
+ { \
+ /* will not fit */ \
+ tmpint32 = bufptr + (sz) - (buflen); \
+ DEBUG_PRINT("READ : buffer %d bytes too small", tmpint32); \
+ ERROR_OUT_BUFERROR(fp); \
+ }
+
+/* move the buffer pointer */
+#define BUF_SKIP(sz) \
+ bufptr += (size_t)(sz);
+
+/* move BUF_CUR forward so that it is aligned to the specified
+ type width */
+#define BUF_ALIGN(fp, type) \
+ /* figure out number of bytes to skip forward */ \
+ tmp2int32 = (sizeof(type) - ((BUF_CUR - (char *)NULL) % sizeof(type))) \
+ % sizeof(type); \
+ /* check and skip */ \
+ BUF_CHECK(fp, tmp2int32); \
+ BUF_SKIP(tmp2int32);
+
+/* allocate a piece of the buffer to store an array in */
+#define BUF_ALLOC(fp, ptr, type, num) \
+ /* align to the specified type width */ \
+ BUF_ALIGN(fp, type); \
+ /* check that we have enough room */ \
+ BUF_CHECK(fp, (size_t)(num) * sizeof(type)); \
+ /* store the pointer */ \
+ (ptr) = (type *)BUF_CUR; \
+ /* reserve the space */ \
+ BUF_SKIP((size_t)(num) * sizeof(type));
+
+/* read a binary blob into the buffer */
+#define READ_BUF(fp, ptr, sz) \
+ /* check that there is enough room and read */ \
+ BUF_CHECK(fp, sz); \
+ READ(fp, BUF_CUR, (size_t)sz); \
+ /* store pointer and skip */ \
+ (ptr) = BUF_CUR; \
+ BUF_SKIP(sz);
+
+/* read string in the buffer (using buffer, buflen and bufptr)
+ and store the actual location of the string in field */
+#define READ_BUF_STRING(fp, field) \
+ /* read the size of the string */ \
+ READ(fp, &tmpint32, sizeof(int32_t)); \
+ tmpint32 = ntohl(tmpint32); \
+ DEBUG_PRINT("READ_BUF_STRING: var="__STRING(field)" strlen=%d", tmpint32); \
+ /* check if read would fit */ \
+ BUF_CHECK(fp, tmpint32 + 1); \
+ /* read string from the stream */ \
+ if (tmpint32 > 0) \
+ { \
+ READ(fp, BUF_CUR, (size_t)tmpint32); \
+ } \
+ /* null-terminate string in buffer */ \
+ BUF_CUR[tmpint32] = '\0'; \
+ DEBUG_PRINT("READ_BUF_STRING: var="__STRING(field)" string=\"%s\"", BUF_CUR); \
+ /* prepare result */ \
+ (field) = BUF_CUR; \
+ BUF_SKIP(tmpint32 + 1);
+
+/* read an array from a stream and store it as a null-terminated
+ array list (size for the array is allocated) */
+#define READ_BUF_STRINGLIST(fp, arr) \
+ /* read the number of entries */ \
+ READ(fp, &tmp3int32, sizeof(int32_t)); \
+ tmp3int32 = ntohl(tmp3int32); \
+ DEBUG_PRINT("READ_STRLST: var="__STRING(arr)" num=%d", (int)tmp3int32); \
+ /* allocate room for *char[num + 1] */ \
+ BUF_ALLOC(fp, arr, char *, tmp3int32 + 1); \
+ /* read all entries */ \
+ for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \
+ { \
+ READ_BUF_STRING(fp, (arr)[tmp2int32]); \
+ } \
+ /* set last entry to NULL */ \
+ (arr)[tmp2int32] = NULL;
+
+
+/* SKIP macros for skipping over certain parts of the protocol stream. */
+
+/* skip a number of bytes forward */
+#define SKIP(fp, sz) \
+ DEBUG_PRINT("READ : skip %d bytes", (int)(sz)); \
+ /* read (skip) the specified number of bytes */ \
+ if (tio_skip(fp, sz)) \
+ { \
+ char ebuf[128]; \
+ int saved_errno = errno; \
+ DEBUG_PRINT("READ : skip error: %s", \
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf))); \
+ ERROR_OUT_READERROR(fp); \
+ }
+
+/* read a string from the stream but don't do anything with the result */
+#define SKIP_STRING(fp) \
+ /* read the size of the string */ \
+ READ(fp, &tmpint32, sizeof(int32_t)); \
+ tmpint32 = ntohl(tmpint32); \
+ DEBUG_PRINT("READ_STRING: skip %d bytes", (int)tmpint32); \
+ /* read (skip) the specified number of bytes */ \
+ SKIP(fp, tmpint32);
+
+/* skip a list of strings */
+#define SKIP_STRINGLIST(fp) \
+ /* read the number of entries */ \
+ READ(fp, &tmp3int32, sizeof(int32_t)); \
+ tmp3int32 = ntohl(tmp3int32); \
+ DEBUG_PRINT("READ_STRLST: skip %d strings", (int)tmp3int32); \
+ /* read all entries */ \
+ for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \
+ { \
+ SKIP_STRING(fp); \
+ }
+
+
+/* These are functions and macros for performing common operations in
+ the nslcd request/response protocol. */
+
+/* returns a socket to the server or NULL on error (see errno),
+ socket should be closed with tio_close() */
+TFILE *nslcd_client_open(void)
+ MUST_USE;
+
+/* generic request code */
+#define NSLCD_REQUEST(fp, action, writefn) \
+ /* open a client socket */ \
+ if ((fp = nslcd_client_open()) == NULL) \
+ { \
+ ERROR_OUT_OPENERROR; \
+ } \
+ /* write a request header with a request code */ \
+ WRITE_INT32(fp, (int32_t)NSLCD_VERSION) \
+ WRITE_INT32(fp, (int32_t)action) \
+ /* write the request parameters (if any) */ \
+ writefn; \
+ /* flush the stream */ \
+ if (tio_flush(fp) < 0) \
+ { \
+ char ebuf[128]; \
+ int saved_errno = errno; \
+ DEBUG_PRINT("WRITE_FLUSH : error: %s", \
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf))); \
+ ERROR_OUT_WRITEERROR(fp); \
+ } \
+ /* read and check response version number */ \
+ READ(fp, &tmpint32, sizeof(int32_t)); \
+ tmpint32 = ntohl(tmpint32); \
+ if (tmpint32 != (int32_t)NSLCD_VERSION) \
+ { \
+ ERROR_OUT_READERROR(fp); \
+ } \
+ /* read and check response request number */ \
+ READ(fp, &tmpint32, sizeof(int32_t)); \
+ tmpint32 = ntohl(tmpint32); \
+ if (tmpint32 != (int32_t)(action)) \
+ { \
+ ERROR_OUT_READERROR(fp); \
+ }
+
+/* Read the response code (the result code of the query) from
+ the stream. */
+#define READ_RESPONSE_CODE(fp) \
+ READ(fp, &tmpint32, sizeof(int32_t)); \
+ tmpint32 = ntohl(tmpint32); \
+ if (tmpint32 != (int32_t)NSLCD_RESULT_BEGIN) \
+ { \
+ ERROR_OUT_NOSUCCESS(fp); \
+ }
+
+#endif /* not COMMON__NSLCD_PROT_H */
diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd.h b/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd.h
new file mode 100644
index 0000000..c7dc013
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/nslcd.h
@@ -0,0 +1,305 @@
+/*
+ nslcd.h - file describing client/server protocol
+
+ Copyright (C) 2006 West Consulting
+ Copyright (C) 2006, 2007, 2009, 2010, 2011, 2012, 2013 Arthur de Jong
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA
+*/
+
+#ifndef _NSLCD_H
+#define _NSLCD_H 1
+
+/*
+ The protocol used between the nslcd client and server is a simple binary
+ protocol. It is request/response based where the client initiates a
+ connection, does a single request and closes the connection again. Any
+ mangled or not understood messages will be silently ignored by the server.
+
+ A request looks like:
+ INT32 NSLCD_VERSION
+ INT32 NSLCD_ACTION_*
+ [request parameters if any]
+ A response looks like:
+ INT32 NSLCD_VERSION
+ INT32 NSLCD_ACTION_* (the original request type)
+ [result(s)]
+ INT32 NSLCD_RESULT_END
+ A single result entry looks like:
+ INT32 NSLCD_RESULT_BEGIN
+ [result value(s)]
+ If a response would return multiple values (e.g. for NSLCD_ACTION_*_ALL
+ functions) each return value will be preceded by a NSLCD_RESULT_BEGIN
+ value. After the last returned result the server sends
+ NSLCD_RESULT_END. If some error occurs (e.g. LDAP server unavailable,
+ error in the request, etc) the server terminates the connection to signal
+ an error condition (breaking the protocol).
+
+ These are the available basic data types:
+ INT32 - 32-bit integer value
+ TYPE - a typed field that is transferred using sizeof()
+ STRING - a string length (32bit) followed by the string value (not
+ null-terminated) the string itself is assumed to be UTF-8
+ STRINGLIST - a 32-bit number noting the number of strings followed by
+ the strings one at a time
+
+ Furthermore the ADDRESS compound data type is defined as:
+ INT32 type of address: e.g. AF_INET or AF_INET6
+ INT32 length of address
+ RAW the address itself
+ With the ADDRESSLIST using the same construct as with STRINGLIST.
+
+ The protocol uses network byte order for all types.
+*/
+
+/* The current version of the protocol. This protocol should only be
+ updated with major backwards-incompatible changes. */
+#define NSLCD_VERSION 0x00000002
+
+/* Get a NSLCD configuration option. There is one request parameter:
+ INT32 NSLCD_CONFIG_*
+ the result value is:
+ STRING value, interpretation depending on request */
+#define NSLCD_ACTION_CONFIG_GET 0x00010001
+
+/* return the message, if any, that is presented to the user when password
+ modification through PAM is prohibited */
+#define NSLCD_CONFIG_PAM_PASSWORD_PROHIBIT_MESSAGE 1
+
+/* Email alias (/etc/aliases) NSS requests. The result values for a
+ single entry are:
+ STRING alias name
+ STRINGLIST alias rcpts */
+#define NSLCD_ACTION_ALIAS_BYNAME 0x00020001
+#define NSLCD_ACTION_ALIAS_ALL 0x00020008
+
+/* Ethernet address/name mapping NSS requests. The result values for a
+ single entry are:
+ STRING ether name
+ TYPE(uint8_t[6]) ether address */
+#define NSLCD_ACTION_ETHER_BYNAME 0x00030001
+#define NSLCD_ACTION_ETHER_BYETHER 0x00030002
+#define NSLCD_ACTION_ETHER_ALL 0x00030008
+
+/* Group and group membership related NSS requests. The result values
+ for a single entry are:
+ STRING group name
+ STRING group password
+ INT32 group id
+ STRINGLIST members (usernames) of the group
+ (not that the BYMEMER call returns an empty members list) */
+#define NSLCD_ACTION_GROUP_BYNAME 0x00040001
+#define NSLCD_ACTION_GROUP_BYGID 0x00040002
+#define NSLCD_ACTION_GROUP_BYMEMBER 0x00040006
+#define NSLCD_ACTION_GROUP_ALL 0x00040008
+
+/* Hostname (/etc/hosts) lookup NSS requests. The result values
+ for an entry are:
+ STRING host name
+ STRINGLIST host aliases
+ ADDRESSLIST host addresses */
+#define NSLCD_ACTION_HOST_BYNAME 0x00050001
+#define NSLCD_ACTION_HOST_BYADDR 0x00050002
+#define NSLCD_ACTION_HOST_ALL 0x00050008
+
+/* Netgroup NSS result entries contain a number of parts. A result entry
+ starts with:
+ STRING netgroup name
+ followed by zero or more references to other netgroups or netgroup
+ triples. A reference to another netgroup looks like:
+ INT32 NSLCD_NETGROUP_TYPE_NETGROUP
+ STRING other netgroup name
+ A a netgroup triple looks like:
+ INT32 NSLCD_NETGROUP_TYPE_TRIPLE
+ STRING host
+ STRING user
+ STRING domain
+ A netgroup result entry is terminated by:
+ INT32 NSLCD_NETGROUP_TYPE_END
+ */
+#define NSLCD_ACTION_NETGROUP_BYNAME 0x00060001
+#define NSLCD_ACTION_NETGROUP_ALL 0x00060008
+#define NSLCD_NETGROUP_TYPE_NETGROUP 1
+#define NSLCD_NETGROUP_TYPE_TRIPLE 2
+#define NSLCD_NETGROUP_TYPE_END 3
+
+/* Network name (/etc/networks) NSS requests. Result values for a single
+ entry are:
+ STRING network name
+ STRINGLIST network aliases
+ ADDRESSLIST network addresses */
+#define NSLCD_ACTION_NETWORK_BYNAME 0x00070001
+#define NSLCD_ACTION_NETWORK_BYADDR 0x00070002
+#define NSLCD_ACTION_NETWORK_ALL 0x00070008
+
+/* User account (/etc/passwd) NSS requests. Result values are:
+ STRING user name
+ STRING user password
+ INT32 user id
+ INT32 group id
+ STRING gecos information
+ STRING home directory
+ STRING login shell */
+#define NSLCD_ACTION_PASSWD_BYNAME 0x00080001
+#define NSLCD_ACTION_PASSWD_BYUID 0x00080002
+#define NSLCD_ACTION_PASSWD_ALL 0x00080008
+
+/* Protocol information requests. Result values are:
+ STRING protocol name
+ STRINGLIST protocol aliases
+ INT32 protocol number */
+#define NSLCD_ACTION_PROTOCOL_BYNAME 0x00090001
+#define NSLCD_ACTION_PROTOCOL_BYNUMBER 0x00090002
+#define NSLCD_ACTION_PROTOCOL_ALL 0x00090008
+
+/* RPC information requests. Result values are:
+ STRING rpc name
+ STRINGLIST rpc aliases
+ INT32 rpc number */
+#define NSLCD_ACTION_RPC_BYNAME 0x000a0001
+#define NSLCD_ACTION_RPC_BYNUMBER 0x000a0002
+#define NSLCD_ACTION_RPC_ALL 0x000a0008
+
+/* Service (/etc/services) information requests. The BYNAME and BYNUMBER
+ requests contain an extra protocol string in the request which, if not
+ blank, will filter the services by this protocol. Result values are:
+ STRING service name
+ STRINGLIST service aliases
+ INT32 service (port) number
+ STRING service protocol */
+#define NSLCD_ACTION_SERVICE_BYNAME 0x000b0001
+#define NSLCD_ACTION_SERVICE_BYNUMBER 0x000b0002
+#define NSLCD_ACTION_SERVICE_ALL 0x000b0008
+
+/* Extended user account (/etc/shadow) information requests. Result
+ values for a single entry are:
+ STRING user name
+ STRING user password
+ INT32 last password change
+ INT32 mindays
+ INT32 maxdays
+ INT32 warn
+ INT32 inact
+ INT32 expire
+ INT32 flag */
+#define NSLCD_ACTION_SHADOW_BYNAME 0x000c0001
+#define NSLCD_ACTION_SHADOW_ALL 0x000c0008
+
+/* PAM-related requests. The request parameters for all these requests
+ begin with:
+ STRING user name
+ STRING service name
+ STRING ruser
+ STRING rhost
+ STRING tty
+ If the user is not known in LDAP no result may be returned (immediately
+ return NSLCD_RESULT_END instead of a PAM error code). */
+
+/* PAM authentication check request. The extra request values are:
+ STRING password
+ and the result value consists of:
+ INT32 authc NSLCD_PAM_* result code
+ STRING user name (the canonical user name)
+ INT32 authz NSLCD_PAM_* result code
+ STRING authorisation error message
+ If the username is empty in this request an attempt is made to
+ authenticate as the administrator (set using rootpwmoddn).
+ Some authorisation checks are already done during authentication so the
+ response also includes authorisation information. */
+#define NSLCD_ACTION_PAM_AUTHC 0x000d0001
+
+/* PAM authorisation check request. The result value consists of:
+ INT32 authz NSLCD_PAM_* result code
+ STRING authorisation error message
+ The authentication check may have already returned some authorisation
+ information. The authorisation error message, if supplied, will be used
+ by the PAM module instead of a message that is generated by the PAM
+ module itself. */
+#define NSLCD_ACTION_PAM_AUTHZ 0x000d0002
+
+/* PAM session open request. The result value consists of:
+ STRING session id
+ This session id may be used to close this session with. */
+#define NSLCD_ACTION_PAM_SESS_O 0x000d0003
+
+/* PAM session close request. This request has the following
+ extra request value:
+ STRING session id
+ and this calls only returns an empty response value. */
+#define NSLCD_ACTION_PAM_SESS_C 0x000d0004
+
+/* PAM password modification request. This requests has the following extra
+ request values:
+ INT32 asroot: 0=oldpasswd is user passwd, 1=oldpasswd is root passwd
+ STRING old password
+ STRING new password
+ and returns there extra result values:
+ INT32 NSLCD_PAM_* result code
+ STRING error message */
+#define NSLCD_ACTION_PAM_PWMOD 0x000d0005
+
+/* User information change request. This request allows one to change
+ their full name and other information. The request parameters for this
+ request are:
+ STRING user name
+ INT32 asroot: 0=passwd is user passwd, 1=passwd is root passwd
+ STRING password
+ followed by one or more of the below, terminated by NSLCD_USERMOD_END
+ INT32 NSLCD_USERMOD_*
+ STRING new value
+ the response consists of one or more of the entries below, terminated
+ by NSLCD_USERMOD_END:
+ INT32 NSLCD_USERMOD_*
+ STRING response
+ (if the response is blank, the change went OK, otherwise the string
+ contains an error message)
+ */
+#define NSLCD_ACTION_USERMOD 0x000e0001
+
+/* These are the possible values for the NSLCD_ACTION_USERMOD operation
+ above. */
+#define NSLCD_USERMOD_END 0 /* end of change values */
+#define NSLCD_USERMOD_RESULT 1 /* global result value */
+#define NSLCD_USERMOD_FULLNAME 2 /* full name */
+#define NSLCD_USERMOD_ROOMNUMBER 3 /* room number */
+#define NSLCD_USERMOD_WORKPHONE 4 /* office phone number */
+#define NSLCD_USERMOD_HOMEPHONE 5 /* home phone number */
+#define NSLCD_USERMOD_OTHER 6 /* other info */
+#define NSLCD_USERMOD_HOMEDIR 7 /* home directory */
+#define NSLCD_USERMOD_SHELL 8 /* login shell */
+
+/* Request result codes. */
+#define NSLCD_RESULT_BEGIN 1
+#define NSLCD_RESULT_END 2
+
+/* Partial list of PAM result codes. */
+#define NSLCD_PAM_SUCCESS 0 /* everything ok */
+#define NSLCD_PAM_PERM_DENIED 6 /* Permission denied */
+#define NSLCD_PAM_AUTH_ERR 7 /* Authc failure */
+#define NSLCD_PAM_CRED_INSUFFICIENT 8 /* Cannot access authc data */
+#define NSLCD_PAM_AUTHINFO_UNAVAIL 9 /* Cannot retrieve authc info */
+#define NSLCD_PAM_USER_UNKNOWN 10 /* User not known */
+#define NSLCD_PAM_MAXTRIES 11 /* Retry limit reached */
+#define NSLCD_PAM_NEW_AUTHTOK_REQD 12 /* Password expired */
+#define NSLCD_PAM_ACCT_EXPIRED 13 /* Account expired */
+#define NSLCD_PAM_SESSION_ERR 14 /* Cannot make/remove session record */
+#define NSLCD_PAM_AUTHTOK_ERR 20 /* Authentication token manipulation error */
+#define NSLCD_PAM_AUTHTOK_DISABLE_AGING 23 /* Password aging disabled */
+#define NSLCD_PAM_IGNORE 25 /* Ignore module */
+#define NSLCD_PAM_ABORT 26 /* Fatal error */
+#define NSLCD_PAM_AUTHTOK_EXPIRED 27 /* authentication token has expired */
+
+#endif /* not _NSLCD_H */
diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.c b/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.c
new file mode 100644
index 0000000..012e680
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.c
@@ -0,0 +1,520 @@
+/*
+ tio.c - timed io functions
+ This file is part of the nss-pam-ldapd library.
+
+ Copyright (C) 2007-2014 Arthur de Jong
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA
+*/
+
+#include "portable.h"
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif /* HAVE_STDINT_H */
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <stdio.h>
+#include <limits.h>
+#include <poll.h>
+#include <time.h>
+
+#include "tio.h"
+
+/* for platforms that don't have ETIME use ETIMEDOUT */
+#ifndef ETIME
+#define ETIME ETIMEDOUT
+#endif /* ETIME */
+
+/* structure that holds a buffer
+ the buffer contains the data that is between the application and the
+ file descriptor that is used for efficient transfer
+ the buffer is built up as follows:
+ |.....********......|
+ ^start ^size
+ ^--len--^ */
+struct tio_buffer {
+ uint8_t *buffer;
+ size_t size; /* the size of the buffer */
+ size_t maxsize; /* the maximum size of the buffer */
+ size_t start; /* the start of the data (before start is unused) */
+ size_t len; /* size of the data (from the start) */
+};
+
+/* structure that holds all the state for files */
+struct tio_fileinfo {
+ int fd;
+ struct tio_buffer readbuffer;
+ struct tio_buffer writebuffer;
+ int readtimeout;
+ int writetimeout;
+ int read_resettable; /* whether the tio_reset() function can be called */
+#ifdef DEBUG_TIO_STATS
+ /* this is used to collect statistics on the use of the streams
+ and can be used to tune the buffer sizes */
+ size_t byteswritten;
+ size_t bytesread;
+#endif /* DEBUG_TIO_STATS */
+};
+
+/* some older versions of Solaris don't provide CLOCK_MONOTONIC but do have
+ a CLOCK_HIGHRES that has the same properties we need */
+#ifndef CLOCK_MONOTONIC
+#ifdef CLOCK_HIGHRES
+#define CLOCK_MONOTONIC CLOCK_HIGHRES
+#endif /* CLOCK_HIGHRES */
+#endif /* not CLOCK_MONOTONIC */
+
+/* update the timeout to the value that is remaining before the deadline
+ returns the number of milliseconds before the deadline (or a negative
+ value of the deadline has expired) */
+static inline int tio_time_remaining(struct timespec *deadline, int timeout)
+{
+ struct timespec tv;
+ /* if this is the first call, set the deadline and return the full time */
+ if ((deadline->tv_sec == 0) && (deadline->tv_nsec == 0))
+ {
+ if (clock_gettime(CLOCK_MONOTONIC, deadline) == 0)
+ {
+ deadline->tv_sec += timeout / 1000;
+ deadline->tv_nsec += (timeout % 1000) * 1000000;
+ }
+ return timeout;
+ }
+ /* get the current time (fall back to full time on error) */
+ if (clock_gettime(CLOCK_MONOTONIC, &tv))
+ return timeout;
+ /* calculate time remaining in milliseconds */
+ return (deadline->tv_sec - tv.tv_sec) * 1000 +
+ (deadline->tv_nsec - tv.tv_nsec) / 1000000;
+}
+
+/* open a new TFILE based on the file descriptor */
+TFILE *tio_fdopen(int fd, int readtimeout, int writetimeout,
+ size_t initreadsize, size_t maxreadsize,
+ size_t initwritesize, size_t maxwritesize)
+{
+ struct tio_fileinfo *fp;
+ fp = (struct tio_fileinfo *)malloc(sizeof(struct tio_fileinfo));
+ if (fp == NULL)
+ return NULL;
+ fp->fd = fd;
+ /* initialize read buffer */
+ fp->readbuffer.buffer = (uint8_t *)malloc(initreadsize);
+ if (fp->readbuffer.buffer == NULL)
+ {
+ free(fp);
+ return NULL;
+ }
+ fp->readbuffer.size = initreadsize;
+ fp->readbuffer.maxsize = maxreadsize;
+ fp->readbuffer.start = 0;
+ fp->readbuffer.len = 0;
+ /* initialize write buffer */
+ fp->writebuffer.buffer = (uint8_t *)malloc(initwritesize);
+ if (fp->writebuffer.buffer == NULL)
+ {
+ free(fp->readbuffer.buffer);
+ free(fp);
+ return NULL;
+ }
+ fp->writebuffer.size = initwritesize;
+ fp->writebuffer.maxsize = maxwritesize;
+ fp->writebuffer.start = 0;
+ fp->writebuffer.len = 0;
+ /* initialize other attributes */
+ fp->readtimeout = readtimeout;
+ fp->writetimeout = writetimeout;
+ fp->read_resettable = 0;
+#ifdef DEBUG_TIO_STATS
+ fp->byteswritten = 0;
+ fp->bytesread = 0;
+#endif /* DEBUG_TIO_STATS */
+ return fp;
+}
+
+/* wait for any activity on the specified file descriptor using
+ the specified deadline */
+static int tio_wait(int fd, short events, int timeout,
+ struct timespec *deadline)
+{
+ int t;
+ struct pollfd fds[1];
+ int rv;
+ while (1)
+ {
+ fds[0].fd = fd;
+ fds[0].events = events;
+ /* figure out the time we need to wait */
+ if ((t = tio_time_remaining(deadline, timeout)) < 0)
+ {
+ errno = ETIME;
+ return -1;
+ }
+ /* sanity check for moving clock */
+ if (t > timeout)
+ t = timeout;
+ /* wait for activity */
+ rv = poll(fds, 1, t);
+ if (rv > 0)
+ return 0; /* we have activity */
+ else if (rv == 0)
+ {
+ /* no file descriptors were available within the specified time */
+ errno = ETIME;
+ return -1;
+ }
+ else if ((errno != EINTR) && (errno != EAGAIN))
+ /* some error occurred */
+ return -1;
+ /* we just try again on EINTR or EAGAIN */
+ }
+}
+
+/* do a read on the file descriptor, returning the data in the buffer
+ if no data was read in the specified time an error is returned */
+int tio_read(TFILE *fp, void *buf, size_t count)
+{
+ struct timespec deadline = {0, 0};
+ int rv;
+ uint8_t *tmp;
+ size_t newsz;
+ size_t len;
+ /* have a more convenient storage type for the buffer */
+ uint8_t *ptr = (uint8_t *)buf;
+ /* loop until we have returned all the needed data */
+ while (1)
+ {
+ /* check if we have enough data in the buffer */
+ if (fp->readbuffer.len >= count)
+ {
+ if (count > 0)
+ {
+ if (ptr != NULL)
+ memcpy(ptr, fp->readbuffer.buffer + fp->readbuffer.start, count);
+ /* adjust buffer position */
+ fp->readbuffer.start += count;
+ fp->readbuffer.len -= count;
+ }
+ return 0;
+ }
+ /* empty what we have and continue from there */
+ if (fp->readbuffer.len > 0)
+ {
+ if (ptr != NULL)
+ {
+ memcpy(ptr, fp->readbuffer.buffer + fp->readbuffer.start,
+ fp->readbuffer.len);
+ ptr += fp->readbuffer.len;
+ }
+ count -= fp->readbuffer.len;
+ fp->readbuffer.start += fp->readbuffer.len;
+ fp->readbuffer.len = 0;
+ }
+ /* after this point until the read fp->readbuffer.len is 0 */
+ if (!fp->read_resettable)
+ {
+ /* the stream is not resettable, re-use the buffer */
+ fp->readbuffer.start = 0;
+ }
+ else if (fp->readbuffer.start >= (fp->readbuffer.size - 4))
+ {
+ /* buffer is running empty, try to grow buffer */
+ if (fp->readbuffer.size < fp->readbuffer.maxsize)
+ {
+ newsz = fp->readbuffer.size * 2;
+ if (newsz > fp->readbuffer.maxsize)
+ newsz = fp->readbuffer.maxsize;
+ tmp = realloc(fp->readbuffer.buffer, newsz);
+ if (tmp != NULL)
+ {
+ fp->readbuffer.buffer = tmp;
+ fp->readbuffer.size = newsz;
+ }
+ }
+ /* if buffer still does not contain enough room, clear resettable */
+ if (fp->readbuffer.start >= (fp->readbuffer.size - 4))
+ {
+ fp->readbuffer.start = 0;
+ fp->read_resettable = 0;
+ }
+ }
+ /* wait until we have input */
+ if (tio_wait(fp->fd, POLLIN, fp->readtimeout, &deadline))
+ return -1;
+ /* read the input in the buffer */
+ len = fp->readbuffer.size - fp->readbuffer.start;
+#ifdef SSIZE_MAX
+ if (len > SSIZE_MAX)
+ len = SSIZE_MAX;
+#endif /* SSIZE_MAX */
+ rv = read(fp->fd, fp->readbuffer.buffer + fp->readbuffer.start, len);
+ /* check for errors */
+ if (rv == 0)
+ {
+ errno = ECONNRESET;
+ return -1;
+ }
+ else if ((rv < 0) && (errno != EINTR) && (errno != EAGAIN))
+ return -1; /* something went wrong with the read */
+ else if (rv > 0)
+ fp->readbuffer.len = rv; /* skip the read part in the buffer */
+#ifdef DEBUG_TIO_STATS
+ fp->bytesread += rv;
+#endif /* DEBUG_TIO_STATS */
+ }
+}
+
+/* Read and discard the specified number of bytes from the stream. */
+int tio_skip(TFILE *fp, size_t count)
+{
+ return tio_read(fp, NULL, count);
+}
+
+/* Read all available data from the stream and empty the read buffer. */
+int tio_skipall(TFILE *fp, int timeout)
+{
+ struct timespec deadline = {0, 0};
+ int rv;
+ size_t len;
+ /* clear the read buffer */
+ fp->readbuffer.start = 0;
+ fp->readbuffer.len = 0;
+ fp->read_resettable = 0;
+ /* read until we can't read no more */
+ len = fp->readbuffer.size;
+#ifdef SSIZE_MAX
+ if (len > SSIZE_MAX)
+ len = SSIZE_MAX;
+#endif /* SSIZE_MAX */
+ while (1)
+ {
+ /* wait until we have input */
+ if (tio_wait(fp->fd, POLLIN, timeout, &deadline))
+ return -1;
+ /* read data from the stream */
+ rv = read(fp->fd, fp->readbuffer.buffer, len);
+ if (rv == 0)
+ return 0; /* end-of-file */
+ if ((rv < 0) && (errno == EWOULDBLOCK))
+ return 0; /* we've ready everything we can without blocking */
+ if ((rv < 0) && (errno != EINTR) && (errno != EAGAIN))
+ return -1; /* something went wrong with the read */
+ }
+}
+
+/* the caller has assured us that we can write to the file descriptor
+ and we give it a shot */
+static int tio_writebuf(TFILE *fp)
+{
+ int rv;
+ /* write the buffer */
+#ifdef MSG_NOSIGNAL
+ rv = send(fp->fd, fp->writebuffer.buffer + fp->writebuffer.start,
+ fp->writebuffer.len, MSG_NOSIGNAL);
+#else /* not MSG_NOSIGNAL */
+ /* on platforms that cannot use send() with masked signals, we change the
+ signal mask and change it back after the write (note that there is a
+ race condition here) */
+ struct sigaction act, oldact;
+ /* set up sigaction */
+ memset(&act, 0, sizeof(struct sigaction));
+ act.sa_sigaction = NULL;
+ act.sa_handler = SIG_IGN;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+ /* ignore SIGPIPE */
+ if (sigaction(SIGPIPE, &act, &oldact) != 0)
+ return -1; /* error setting signal handler */
+ /* write the buffer */
+ rv = write(fp->fd, fp->writebuffer.buffer + fp->writebuffer.start,
+ fp->writebuffer.len);
+ /* restore the old handler for SIGPIPE */
+ if (sigaction(SIGPIPE, &oldact, NULL) != 0)
+ return -1; /* error restoring signal handler */
+#endif
+ /* check for errors */
+ if ((rv == 0) || ((rv < 0) && (errno != EINTR) && (errno != EAGAIN)))
+ return -1; /* something went wrong with the write */
+ /* skip the written part in the buffer */
+ if (rv > 0)
+ {
+ fp->writebuffer.start += rv;
+ fp->writebuffer.len -= rv;
+#ifdef DEBUG_TIO_STATS
+ fp->byteswritten += rv;
+#endif /* DEBUG_TIO_STATS */
+ /* reset start if len is 0 */
+ if (fp->writebuffer.len == 0)
+ fp->writebuffer.start = 0;
+ /* move contents of the buffer to the front if it will save enough room */
+ if (fp->writebuffer.start >= (fp->writebuffer.size / 4))
+ {
+ memmove(fp->writebuffer.buffer,
+ fp->writebuffer.buffer + fp->writebuffer.start,
+ fp->writebuffer.len);
+ fp->writebuffer.start = 0;
+ }
+ }
+ return 0;
+}
+
+/* write all the data in the buffer to the stream */
+int tio_flush(TFILE *fp)
+{
+ struct timespec deadline = {0, 0};
+ /* loop until we have written our buffer */
+ while (fp->writebuffer.len > 0)
+ {
+ /* wait until we can write */
+ if (tio_wait(fp->fd, POLLOUT, fp->writetimeout, &deadline))
+ return -1;
+ /* write one block */
+ if (tio_writebuf(fp))
+ return -1;
+ }
+ return 0;
+}
+
+/* try a single write of data in the buffer if the file descriptor
+ will accept data */
+static int tio_flush_nonblock(TFILE *fp)
+{
+ struct pollfd fds[1];
+ int rv;
+ /* see if we can write without blocking */
+ fds[0].fd = fp->fd;
+ fds[0].events = POLLOUT;
+ rv = poll(fds, 1, 0);
+ /* check if any file descriptors were ready (timeout) or we were
+ interrupted */
+ if ((rv == 0) || ((rv < 0) && ((errno == EINTR) || (errno == EAGAIN))))
+ return 0;
+ /* any other errors? */
+ if (rv < 0)
+ return -1;
+ /* so file descriptor will accept writes */
+ return tio_writebuf(fp);
+}
+
+int tio_write(TFILE *fp, const void *buf, size_t count)
+{
+ size_t fr;
+ uint8_t *tmp;
+ size_t newsz;
+ const uint8_t *ptr = (const uint8_t *)buf;
+ /* keep filling the buffer until we have buffered everything */
+ while (count > 0)
+ {
+ /* figure out free size in buffer */
+ fr = fp->writebuffer.size - (fp->writebuffer.start + fp->writebuffer.len);
+ if (count <= fr)
+ {
+ /* the data fits in the buffer */
+ memcpy(fp->writebuffer.buffer + fp->writebuffer.start +
+ fp->writebuffer.len, ptr, count);
+ fp->writebuffer.len += count;
+ return 0;
+ }
+ else if (fr > 0)
+ {
+ /* fill the buffer with data that will fit */
+ memcpy(fp->writebuffer.buffer + fp->writebuffer.start +
+ fp->writebuffer.len, ptr, fr);
+ fp->writebuffer.len += fr;
+ ptr += fr;
+ count -= fr;
+ }
+ /* try to flush some of the data that is in the buffer */
+ if (tio_flush_nonblock(fp))
+ return -1;
+ /* if we have room now, try again */
+ if (fp->writebuffer.size > (fp->writebuffer.start + fp->writebuffer.len))
+ continue;
+ /* try to grow the buffer */
+ if (fp->writebuffer.size < fp->writebuffer.maxsize)
+ {
+ newsz = fp->writebuffer.size * 2;
+ if (newsz > fp->writebuffer.maxsize)
+ newsz = fp->writebuffer.maxsize;
+ tmp = realloc(fp->writebuffer.buffer, newsz);
+ if (tmp != NULL)
+ {
+ fp->writebuffer.buffer = tmp;
+ fp->writebuffer.size = newsz;
+ continue; /* try again */
+ }
+ }
+ /* write the buffer to the stream */
+ if (tio_flush(fp))
+ return -1;
+ }
+ return 0;
+}
+
+int tio_close(TFILE *fp)
+{
+ int retv;
+ /* write any buffered data */
+ retv = tio_flush(fp);
+#ifdef DEBUG_TIO_STATS
+ /* dump statistics to stderr */
+ fprintf(stderr, "DEBUG_TIO_STATS READ=%d WRITTEN=%d\n", fp->bytesread,
+ fp->byteswritten);
+#endif /* DEBUG_TIO_STATS */
+ /* close file descriptor */
+ if (close(fp->fd))
+ retv = -1;
+ /* free any allocated buffers */
+ memset(fp->readbuffer.buffer, 0, fp->readbuffer.size);
+ memset(fp->writebuffer.buffer, 0, fp->writebuffer.size);
+ free(fp->readbuffer.buffer);
+ free(fp->writebuffer.buffer);
+ /* free the tio struct itself */
+ free(fp);
+ /* return the result of the earlier operations */
+ return retv;
+}
+
+void tio_mark(TFILE *fp)
+{
+ /* move any data in the buffer to the start of the buffer */
+ if ((fp->readbuffer.start > 0) && (fp->readbuffer.len > 0))
+ {
+ memmove(fp->readbuffer.buffer,
+ fp->readbuffer.buffer + fp->readbuffer.start, fp->readbuffer.len);
+ fp->readbuffer.start = 0;
+ }
+ /* mark the stream as resettable */
+ fp->read_resettable = 1;
+}
+
+int tio_reset(TFILE *fp)
+{
+ /* check if the stream is (still) resettable */
+ if (!fp->read_resettable)
+ return -1;
+ /* reset the buffer */
+ fp->readbuffer.len += fp->readbuffer.start;
+ fp->readbuffer.start = 0;
+ return 0;
+}
diff --git a/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.h b/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.h
new file mode 100644
index 0000000..95f9812
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nss-pam-ldapd/tio.h
@@ -0,0 +1,83 @@
+/*
+ tio.h - timed io functions
+ This file is part of the nss-pam-ldapd library.
+
+ Copyright (C) 2007, 2008, 2010, 2012, 2013 Arthur de Jong
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA
+*/
+
+/*
+
+ TODO: Add some documentation here.
+
+ the SIGPIPE signal should be ignored (is ignored in this code)
+
+ This library is not thread safe. You cannot share TFILE objects between
+ threads and expect to be able to read and write from them in different
+ threads. All the state is in the TFILE object so calls to this library on
+ different objects can be done in parallel.
+
+*/
+
+#ifndef COMMON__TIO_H
+#define COMMON__TIO_H
+
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include "attrs.h"
+
+/* This is a generic file handle used for reading and writing
+ (something like FILE from stdio.h). */
+typedef struct tio_fileinfo TFILE;
+
+/* Open a new TFILE based on the file descriptor. The timeout is set for any
+ operation (value in milliseconds). */
+TFILE *tio_fdopen(int fd, int readtimeout, int writetimeout,
+ size_t initreadsize, size_t maxreadsize,
+ size_t initwritesize, size_t maxwritesize)
+ LIKE_MALLOC MUST_USE;
+
+/* Read the specified number of bytes from the stream. */
+int tio_read(TFILE *fp, void *buf, size_t count);
+
+/* Read and discard the specified number of bytes from the stream. */
+int tio_skip(TFILE *fp, size_t count);
+
+/* Read all available data from the stream and empty the read buffer. */
+int tio_skipall(TFILE *fp, int timeout);
+
+/* Write the specified buffer to the stream. */
+int tio_write(TFILE *fp, const void *buf, size_t count);
+
+/* Write out all buffered data to the stream. */
+int tio_flush(TFILE *fp);
+
+/* Flush the streams and closes the underlying file descriptor. */
+int tio_close(TFILE *fp);
+
+/* Store the current position in the stream so that we can jump back to it
+ with the tio_reset() function. */
+void tio_mark(TFILE *fp);
+
+/* Rewinds the stream to the point set by tio_mark(). Note that this only
+ resets the read stream and not the write stream. This function returns
+ whether the reset was successful (this function may fail if the buffers
+ were full). */
+int tio_reset(TFILE *fp);
+
+#endif /* COMMON__TIO_H */
diff --git a/contrib/slapd-modules/nssov/nssov.c b/contrib/slapd-modules/nssov/nssov.c
new file mode 100644
index 0000000..c8e4187
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nssov.c
@@ -0,0 +1,1045 @@
+/* nssov.c - nss-ldap overlay for slapd */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * Portions Copyright 2013 by Ted C. Cheng, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+#ifndef SLAPD_OVER_NSSOV
+#define SLAPD_OVER_NSSOV SLAPD_MOD_DYNAMIC
+#endif
+
+#include "slap-config.h"
+
+#include "lutil.h"
+
+#include <ac/errno.h>
+#include <ac/unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+AttributeDescription *nssov_pam_host_ad;
+AttributeDescription *nssov_pam_svc_ad;
+
+/* buffer sizes for I/O */
+#define READBUFFER_MINSIZE 32
+#define READBUFFER_MAXSIZE 64
+#define WRITEBUFFER_MINSIZE 64
+#define WRITEBUFFER_MAXSIZE 64*1024
+
+/* Find the given attribute's value in the RDN of the DN */
+void nssov_find_rdnval(struct berval *dn, AttributeDescription *ad, struct berval *value)
+{
+ struct berval rdn;
+ char *next;
+
+ BER_BVZERO(value);
+ dnRdn( dn, &rdn );
+ do {
+ next = ber_bvchr( &rdn, '+' );
+ if ( rdn.bv_val[ad->ad_cname.bv_len] == '=' &&
+ !ber_bvcmp( &rdn, &ad->ad_cname )) {
+ if ( next )
+ rdn.bv_len = next - rdn.bv_val;
+ value->bv_val = rdn.bv_val + ad->ad_cname.bv_len + 1;
+ value->bv_len = rdn.bv_len - ad->ad_cname.bv_len - 1;
+ break;
+ }
+ if ( !next )
+ break;
+ next++;
+ rdn.bv_len -= next - rdn.bv_val;
+ rdn.bv_val = next;
+ } while (1);
+}
+
+/* create a search filter using a name that requires escaping */
+int nssov_filter_byname(nssov_mapinfo *mi,int key,struct berval *name,struct berval *buf)
+{
+ char buf2[1024];
+ struct berval bv2 = {sizeof(buf2),buf2};
+
+ /* escape attribute */
+ if (nssov_escape(name,&bv2))
+ return -1;
+ /* build filter */
+ if (bv2.bv_len + mi->mi_filter.bv_len + mi->mi_attrs[key].an_desc->ad_cname.bv_len + 6 >
+ buf->bv_len )
+ return -1;
+ buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))",
+ mi->mi_filter.bv_val, mi->mi_attrs[key].an_desc->ad_cname.bv_val,
+ bv2.bv_val );
+ return 0;
+}
+
+/* create a search filter using a string converted from an int */
+int nssov_filter_byid(nssov_mapinfo *mi,int key,struct berval *id,struct berval *buf)
+{
+ /* build filter */
+ if (id->bv_len + mi->mi_filter.bv_len + mi->mi_attrs[key].an_desc->ad_cname.bv_len + 6 >
+ buf->bv_len )
+ return -1;
+ buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))",
+ mi->mi_filter.bv_val, mi->mi_attrs[key].an_desc->ad_cname.bv_val,
+ id->bv_val );
+ return 0;
+}
+
+void get_userpassword(struct berval *attr,struct berval *pw)
+{
+ int i;
+ /* go over the entries and return the remainder of the value if it
+ starts with {crypt} or crypt$ */
+ for (i=0;!BER_BVISNULL(&attr[i]);i++)
+ {
+ if (strncasecmp(attr[i].bv_val,"{crypt}",7)==0) {
+ pw->bv_val = attr[i].bv_val + 7;
+ pw->bv_len = attr[i].bv_len - 7;
+ return;
+ }
+ if (strncasecmp(attr[i].bv_val,"crypt$",6)==0) {
+ pw->bv_val = attr[i].bv_val + 6;
+ pw->bv_len = attr[i].bv_len - 6;
+ return;
+ }
+ }
+ /* just return the first value completely */
+ *pw = *attr;
+ /* TODO: support more password formats e.g. SMD5
+ (which is $1$ but in a different format)
+ (any code for this is more than welcome) */
+}
+
+/* this writes a single address to the stream */
+int write_address(TFILE *fp,struct berval *addr)
+{
+ int32_t tmpint32;
+ struct in_addr ipv4addr;
+ struct in6_addr ipv6addr;
+ /* try to parse the address as IPv4 first, fall back to IPv6 */
+ if (inet_pton(AF_INET,addr->bv_val,&ipv4addr)>0)
+ {
+ /* write address type */
+ WRITE_INT32(fp,AF_INET);
+ /* write the address length */
+ WRITE_INT32(fp,sizeof(struct in_addr));
+ /* write the address itself (in network byte order) */
+ WRITE(fp,&ipv4addr,sizeof(struct in_addr));
+ }
+ else if (inet_pton(AF_INET6,addr->bv_val,&ipv6addr)>0)
+ {
+ /* write address type */
+ WRITE_INT32(fp,AF_INET6);
+ /* write the address length */
+ WRITE_INT32(fp,sizeof(struct in6_addr));
+ /* write the address itself (in network byte order) */
+ WRITE(fp,&ipv6addr,sizeof(struct in6_addr));
+ }
+ else
+ {
+ /* failure, log but write simple invalid address
+ (otherwise the address list is messed up) */
+ /* TODO: have error message in correct format */
+ Debug(LDAP_DEBUG_ANY,"nssov: unparsable address: %s\n",addr->bv_val );
+ /* write an illegal address type */
+ WRITE_INT32(fp,-1);
+ /* write an empty address */
+ WRITE_INT32(fp,0);
+ }
+ /* we're done */
+ return 0;
+}
+
+int read_address(TFILE *fp,char *addr,int *addrlen,int *af)
+{
+ int32_t tmpint32;
+ int len;
+ /* read address family */
+ READ_INT32(fp,*af);
+ if ((*af!=AF_INET)&&(*af!=AF_INET6))
+ {
+ Debug(LDAP_DEBUG_ANY,"nssov: incorrect address family specified: %d\n",*af );
+ return -1;
+ }
+ /* read address length */
+ READ_INT32(fp,len);
+ if ((len>*addrlen)||(len<=0))
+ {
+ Debug(LDAP_DEBUG_ANY,"nssov: address length incorrect: %d\n",len );
+ return -1;
+ }
+ *addrlen=len;
+ /* read address */
+ READ(fp,addr,len);
+ /* we're done */
+ return 0;
+}
+
+int nssov_escape(struct berval *src,struct berval *dst)
+{
+ size_t pos=0;
+ int i;
+ /* go over all characters in source string */
+ for (i=0;i<src->bv_len;i++)
+ {
+ /* check if char will fit */
+ if (pos>=(dst->bv_len-4))
+ return -1;
+ /* do escaping for some characters */
+ switch (src->bv_val[i])
+ {
+ case '*':
+ strcpy(dst->bv_val+pos,"\\2a");
+ pos+=3;
+ break;
+ case '(':
+ strcpy(dst->bv_val+pos,"\\28");
+ pos+=3;
+ break;
+ case ')':
+ strcpy(dst->bv_val+pos,"\\29");
+ pos+=3;
+ break;
+ case '\\':
+ strcpy(dst->bv_val+pos,"\\5c");
+ pos+=3;
+ break;
+ default:
+ /* just copy character */
+ dst->bv_val[pos++]=src->bv_val[i];
+ break;
+ }
+ }
+ /* terminate destination string */
+ dst->bv_val[pos]='\0';
+ dst->bv_len = pos;
+ return 0;
+}
+
+/* read the version information and action from the stream
+ this function returns the read action in location pointer to by action */
+static int read_header(TFILE *fp,int32_t *action)
+{
+ int32_t tmpint32;
+ /* read the protocol version */
+ READ_INT32(fp,tmpint32);
+ if (tmpint32 != (int32_t)NSLCD_VERSION)
+ {
+ Debug( LDAP_DEBUG_TRACE,"nssov: wrong nslcd version id (%d)\n",(int)tmpint32 );
+ return -1;
+ }
+ /* read the request type */
+ READ_INT32(fp,*action);
+ return 0;
+}
+
+int nssov_config(nssov_info *ni,TFILE *fp,Operation *op)
+{
+ int opt;
+ int32_t tmpint32;
+
+ READ_INT32(fp,opt);
+
+ Debug(LDAP_DEBUG_TRACE, "nssov_config (%d)\n",opt );
+
+ WRITE_INT32(fp,NSLCD_VERSION);
+ WRITE_INT32(fp,NSLCD_ACTION_CONFIG_GET);
+ WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
+
+ switch (opt) {
+ case NSLCD_CONFIG_PAM_PASSWORD_PROHIBIT_MESSAGE:
+ /* request for pam password_prohibit_message */
+ /* nssov_pam prohibits password */
+ if (!BER_BVISEMPTY(&ni->ni_pam_password_prohibit_message)) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_config(): %s (%s)\n",
+ "password_prohibit_message",
+ ni->ni_pam_password_prohibit_message.bv_val );
+ WRITE_STRING(fp,ni->ni_pam_password_prohibit_message.bv_val);
+ }
+ default:
+ /* all other config options are ignored */
+ break;
+ }
+
+ WRITE_INT32(fp,NSLCD_RESULT_END);
+ return 0;
+}
+
+
+/* read a request message, returns <0 in case of errors,
+ this function closes the socket */
+static void handleconnection(nssov_info *ni,int sock,Operation *op)
+{
+ TFILE *fp;
+ int32_t action;
+ int readtimeout,writetimeout;
+ uid_t uid;
+ gid_t gid;
+ char authid[sizeof("gidNumber=4294967295+uidNumber=424967295,cn=peercred,cn=external,cn=auth")];
+ char peerbuf[8];
+ struct berval peerbv = { sizeof(peerbuf), peerbuf };
+
+ /* log connection */
+ if (LUTIL_GETPEEREID(sock,&uid,&gid,&peerbv)) {
+ char ebuf[128];
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_TRACE,"nssov: connection from unknown client: %s\n",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ } else {
+ Debug( LDAP_DEBUG_TRACE,"nssov: connection from uid=%d gid=%d\n",
+ (int)uid,(int)gid );
+ }
+
+ /* Should do authid mapping too */
+ op->o_dn.bv_len = sprintf(authid,"gidNumber=%d+uidNumber=%d,cn=peercred,cn=external,cn=auth",
+ (int)gid, (int)uid );
+ op->o_dn.bv_val = authid;
+ op->o_ndn = op->o_dn;
+
+ /* set the timeouts:
+ * read timeout is half a second because clients should send their request
+ * quickly, write timeout is 60 seconds because clients could be taking some
+ * time to process the results
+ */
+ readtimeout = 500;
+ writetimeout = 60000;
+ /* create a stream object */
+ if ((fp=tio_fdopen(sock,readtimeout,writetimeout,
+ READBUFFER_MINSIZE,READBUFFER_MAXSIZE,
+ WRITEBUFFER_MINSIZE,WRITEBUFFER_MAXSIZE))==NULL)
+ {
+ char ebuf[128];
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: cannot create stream for writing: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ (void)close(sock);
+ return;
+ }
+ /* read request */
+ if (read_header(fp,&action))
+ {
+ (void)tio_close(fp);
+ return;
+ }
+ /* handle request */
+ switch (action)
+ {
+ case NSLCD_ACTION_ALIAS_BYNAME: (void)nssov_alias_byname(ni,fp,op); break;
+ case NSLCD_ACTION_ALIAS_ALL: (void)nssov_alias_all(ni,fp,op); break;
+ case NSLCD_ACTION_ETHER_BYNAME: (void)nssov_ether_byname(ni,fp,op); break;
+ case NSLCD_ACTION_ETHER_BYETHER: (void)nssov_ether_byether(ni,fp,op); break;
+ case NSLCD_ACTION_ETHER_ALL: (void)nssov_ether_all(ni,fp,op); break;
+ case NSLCD_ACTION_GROUP_BYNAME: (void)nssov_group_byname(ni,fp,op); break;
+ case NSLCD_ACTION_GROUP_BYGID: (void)nssov_group_bygid(ni,fp,op); break;
+ case NSLCD_ACTION_GROUP_BYMEMBER: (void)nssov_group_bymember(ni,fp,op); break;
+ case NSLCD_ACTION_GROUP_ALL: (void)nssov_group_all(ni,fp,op); break;
+ case NSLCD_ACTION_HOST_BYNAME: (void)nssov_host_byname(ni,fp,op); break;
+ case NSLCD_ACTION_HOST_BYADDR: (void)nssov_host_byaddr(ni,fp,op); break;
+ case NSLCD_ACTION_HOST_ALL: (void)nssov_host_all(ni,fp,op); break;
+ case NSLCD_ACTION_NETGROUP_BYNAME: (void)nssov_netgroup_byname(ni,fp,op); break;
+ case NSLCD_ACTION_NETWORK_BYNAME: (void)nssov_network_byname(ni,fp,op); break;
+ case NSLCD_ACTION_NETWORK_BYADDR: (void)nssov_network_byaddr(ni,fp,op); break;
+ case NSLCD_ACTION_NETWORK_ALL: (void)nssov_network_all(ni,fp,op); break;
+ case NSLCD_ACTION_PASSWD_BYNAME: (void)nssov_passwd_byname(ni,fp,op); break;
+ case NSLCD_ACTION_PASSWD_BYUID: (void)nssov_passwd_byuid(ni,fp,op); break;
+ case NSLCD_ACTION_PASSWD_ALL: (void)nssov_passwd_all(ni,fp,op); break;
+ case NSLCD_ACTION_PROTOCOL_BYNAME: (void)nssov_protocol_byname(ni,fp,op); break;
+ case NSLCD_ACTION_PROTOCOL_BYNUMBER:(void)nssov_protocol_bynumber(ni,fp,op); break;
+ case NSLCD_ACTION_PROTOCOL_ALL: (void)nssov_protocol_all(ni,fp,op); break;
+ case NSLCD_ACTION_RPC_BYNAME: (void)nssov_rpc_byname(ni,fp,op); break;
+ case NSLCD_ACTION_RPC_BYNUMBER: (void)nssov_rpc_bynumber(ni,fp,op); break;
+ case NSLCD_ACTION_RPC_ALL: (void)nssov_rpc_all(ni,fp,op); break;
+ case NSLCD_ACTION_SERVICE_BYNAME: (void)nssov_service_byname(ni,fp,op); break;
+ case NSLCD_ACTION_SERVICE_BYNUMBER: (void)nssov_service_bynumber(ni,fp,op); break;
+ case NSLCD_ACTION_SERVICE_ALL: (void)nssov_service_all(ni,fp,op); break;
+ case NSLCD_ACTION_SHADOW_BYNAME: if (uid==0) (void)nssov_shadow_byname(ni,fp,op); break;
+ case NSLCD_ACTION_SHADOW_ALL: if (uid==0) (void)nssov_shadow_all(ni,fp,op); break;
+ case NSLCD_ACTION_PAM_AUTHC: (void)pam_authc(ni,fp,op,uid); break;
+ case NSLCD_ACTION_PAM_AUTHZ: (void)pam_authz(ni,fp,op); break;
+ case NSLCD_ACTION_PAM_SESS_O: if (uid==0) (void)pam_sess_o(ni,fp,op); break;
+ case NSLCD_ACTION_PAM_SESS_C: if (uid==0) (void)pam_sess_c(ni,fp,op); break;
+ case NSLCD_ACTION_PAM_PWMOD: (void)pam_pwmod(ni,fp,op,uid); break;
+ case NSLCD_ACTION_CONFIG_GET: (void)nssov_config(ni,fp,op); break;
+ default:
+ Debug( LDAP_DEBUG_ANY,"nssov: invalid request id: %d",(int)action );
+ break;
+ }
+ /* we're done with the request */
+ (void)tio_close(fp);
+ return;
+}
+
+/* accept a connection on the socket */
+static void *acceptconn(void *ctx, void *arg)
+{
+ nssov_info *ni = arg;
+ Connection conn = {0};
+ OperationBuffer opbuf;
+ Operation *op;
+ int csock;
+
+ if ( slapd_shutdown )
+ return NULL;
+
+ {
+ struct sockaddr_storage addr;
+ socklen_t alen;
+ int j;
+
+ /* accept a new connection */
+ alen=(socklen_t)sizeof(struct sockaddr_storage);
+ csock=accept(ni->ni_socket,(struct sockaddr *)&addr,&alen);
+ connection_client_enable(ni->ni_conn);
+ if (csock<0)
+ {
+ char ebuf[128];
+ int saved_errno = errno;
+ if ((errno==EINTR)||(errno==EAGAIN)||(errno==EWOULDBLOCK))
+ {
+ Debug( LDAP_DEBUG_TRACE,"nssov: accept() failed (ignored): %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ return NULL;
+ }
+ Debug( LDAP_DEBUG_ANY,"nssov: accept() failed: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ return NULL;
+ }
+ /* make sure O_NONBLOCK is not inherited */
+ if ((j=fcntl(csock,F_GETFL,0))<0)
+ {
+ char ebuf[128];
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: fcntl(F_GETFL) failed: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ if (close(csock)) {
+ saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ }
+ return NULL;
+ }
+ if (fcntl(csock,F_SETFL,j&~O_NONBLOCK)<0)
+ {
+ char ebuf[128];
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: fcntl(F_SETFL,~O_NONBLOCK) failed: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ if (close(csock)) {
+ saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ }
+ return NULL;
+ }
+ }
+ connection_fake_init( &conn, &opbuf, ctx );
+ op=&opbuf.ob_op;
+ conn.c_ssf = conn.c_transport_ssf = local_ssf;
+ op->o_bd = ni->ni_db;
+ op->o_tag = LDAP_REQ_SEARCH;
+
+ /* handle the connection */
+ handleconnection(ni,csock,op);
+
+ return NULL;
+}
+
+static slap_verbmasks nss_svcs[] = {
+ { BER_BVC("aliases"), NM_alias },
+ { BER_BVC("ethers"), NM_ether },
+ { BER_BVC("group"), NM_group },
+ { BER_BVC("hosts"), NM_host },
+ { BER_BVC("netgroup"), NM_netgroup },
+ { BER_BVC("networks"), NM_network },
+ { BER_BVC("passwd"), NM_passwd },
+ { BER_BVC("protocols"), NM_protocol },
+ { BER_BVC("rpc"), NM_rpc },
+ { BER_BVC("services"), NM_service },
+ { BER_BVC("shadow"), NM_shadow },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks pam_opts[] = {
+ { BER_BVC("userhost"), NI_PAM_USERHOST },
+ { BER_BVC("userservice"), NI_PAM_USERSVC },
+ { BER_BVC("usergroup"), NI_PAM_USERGRP },
+ { BER_BVC("hostservice"), NI_PAM_HOSTSVC },
+ { BER_BVC("authz2dn"), NI_PAM_SASL2DN },
+ { BER_BVC("uid2dn"), NI_PAM_UID2DN },
+ { BER_BVNULL, 0 }
+};
+
+enum {
+ NSS_SSD=1,
+ NSS_MAP,
+ NSS_PAM,
+ NSS_PAMGROUP,
+ NSS_PAMSESS
+};
+
+static ConfigDriver nss_cf_gen;
+
+static ConfigTable nsscfg[] = {
+ { "nssov-ssd", "service> <url", 3, 3, 0, ARG_MAGIC|NSS_SSD,
+ nss_cf_gen, "(OLcfgCtAt:3.1 NAME 'olcNssSsd' "
+ "DESC 'URL for searches in a given service' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "nssov-map", "service> <orig> <new", 4, 4, 0, ARG_MAGIC|NSS_MAP,
+ nss_cf_gen, "(OLcfgCtAt:3.2 NAME 'olcNssMap' "
+ "DESC 'Map <service> lookups of <orig> attr to <new> attr' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "nssov-pam", "options", 2, 0, 0, ARG_MAGIC|NSS_PAM,
+ nss_cf_gen, "(OLcfgCtAt:3.3 NAME 'olcNssPam' "
+ "DESC 'PAM authentication and authorization options' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "nssov-pam-defhost", "hostname", 2, 2, 0, ARG_OFFSET|ARG_BERVAL,
+ (void *)offsetof(struct nssov_info, ni_pam_defhost),
+ "(OLcfgCtAt:3.4 NAME 'olcNssPamDefHost' "
+ "DESC 'Default hostname for service checks' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "nssov-pam-group-dn", "DN", 2, 2, 0, ARG_MAGIC|ARG_DN|NSS_PAMGROUP,
+ nss_cf_gen, "(OLcfgCtAt:3.5 NAME 'olcNssPamGroupDN' "
+ "DESC 'DN of group in which membership is required' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
+ { "nssov-pam-group-ad", "attr", 2, 2, 0, ARG_OFFSET|ARG_ATDESC,
+ (void *)offsetof(struct nssov_info, ni_pam_group_ad),
+ "(OLcfgCtAt:3.6 NAME 'olcNssPamGroupAD' "
+ "DESC 'Member attribute to use for group check' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "nssov-pam-min-uid", "uid", 2, 2, 0, ARG_OFFSET|ARG_INT,
+ (void *)offsetof(struct nssov_info, ni_pam_min_uid),
+ "(OLcfgCtAt:3.7 NAME 'olcNssPamMinUid' "
+ "DESC 'Minimum UID allowed to login' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "nssov-pam-max-uid", "uid", 2, 2, 0, ARG_OFFSET|ARG_INT,
+ (void *)offsetof(struct nssov_info, ni_pam_max_uid),
+ "(OLcfgCtAt:3.8 NAME 'olcNssPamMaxUid' "
+ "DESC 'Maximum UID allowed to login' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "nssov-pam-template-ad", "attr", 2, 2, 0, ARG_OFFSET|ARG_ATDESC,
+ (void *)offsetof(struct nssov_info, ni_pam_template_ad),
+ "(OLcfgCtAt:3.9 NAME 'olcNssPamTemplateAD' "
+ "DESC 'Attribute to use for template login name' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "nssov-pam-template", "name", 2, 2, 0, ARG_OFFSET|ARG_BERVAL,
+ (void *)offsetof(struct nssov_info, ni_pam_template),
+ "(OLcfgCtAt:3.10 NAME 'olcNssPamTemplate' "
+ "DESC 'Default template login name' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "nssov-pam-session", "service", 2, 2, 0, ARG_MAGIC|NSS_PAMSESS,
+ nss_cf_gen, "(OLcfgCtAt:3.11 NAME 'olcNssPamSession' "
+ "DESC 'Services for which sessions will be recorded' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "nssov-pam-password-prohibit-message",
+ "password_prohibit_message", 2, 2, 0,
+ ARG_OFFSET|ARG_BERVAL,
+ (void *)offsetof(struct nssov_info, ni_pam_password_prohibit_message),
+ "(OLcfgCtAt:3.12 NAME 'olcNssPamPwdProhibitMsg' "
+ "DESC 'Prohibit password modification message' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "nssov-pam-pwdmgr-dn",
+ "pwdmgr_dn", 2, 2, 0,
+ ARG_OFFSET|ARG_BERVAL,
+ (void *)offsetof(struct nssov_info, ni_pam_pwdmgr_dn),
+ "(OLcfgCtAt:3.13 NAME 'olcPamPwdmgrDn' "
+ "DESC 'Password Manager DN' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
+ { "nssov-pam-pwdmgr-pwd",
+ "pwdmgr_pwd", 2, 2, 0,
+ ARG_OFFSET|ARG_BERVAL,
+ (void *)offsetof(struct nssov_info, ni_pam_pwdmgr_pwd),
+ "(OLcfgCtAt:3.14 NAME 'olcPamPwdmgrPwd' "
+ "DESC 'Password Manager Pwd' "
+ "EQUALITY octetStringMatch "
+ "SYNTAX OMsOctetString SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0,0,0, ARG_IGNORED }
+};
+
+static ConfigOCs nssocs[] = {
+ { "( OLcfgCtOc:3.1 "
+ "NAME 'olcNssOvConfig' "
+ "DESC 'NSS lookup configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcNssSsd $ olcNssMap $ olcNssPam $ olcNssPamDefHost $ "
+ "olcNssPamGroupDN $ olcNssPamGroupAD $ "
+ "olcNssPamMinUid $ olcNssPamMaxUid $ olcNssPamSession $ "
+ "olcNssPamTemplateAD $ olcNssPamTemplate ) )",
+ Cft_Overlay, nsscfg },
+ { NULL, 0, NULL }
+};
+
+static int
+nss_cf_gen(ConfigArgs *c)
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ nssov_info *ni = on->on_bi.bi_private;
+ nssov_mapinfo *mi;
+ int i, j, rc = 0;
+ slap_mask_t m;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ switch(c->type) {
+ case NSS_SSD:
+ rc = 1;
+ for (i=NM_alias;i<NM_NONE;i++) {
+ struct berval scope;
+ struct berval ssd;
+ struct berval base;
+
+ mi = &ni->ni_maps[i];
+
+ /* ignore all-default services */
+ if ( mi->mi_scope == LDAP_SCOPE_DEFAULT &&
+ bvmatch( &mi->mi_filter, &mi->mi_filter0 ) &&
+ BER_BVISNULL( &mi->mi_base ))
+ continue;
+
+ if ( BER_BVISNULL( &mi->mi_base ))
+ base = ni->ni_db->be_nsuffix[0];
+ else
+ base = mi->mi_base;
+ ldap_pvt_scope2bv(mi->mi_scope == LDAP_SCOPE_DEFAULT ?
+ LDAP_SCOPE_SUBTREE : mi->mi_scope, &scope);
+ ssd.bv_len = STRLENOF(" ldap:///???") + nss_svcs[i].word.bv_len +
+ base.bv_len + scope.bv_len + mi->mi_filter.bv_len;
+ ssd.bv_val = ch_malloc( ssd.bv_len + 1 );
+ sprintf(ssd.bv_val, "%s ldap:///%s??%s?%s", nss_svcs[i].word.bv_val,
+ base.bv_val, scope.bv_val, mi->mi_filter.bv_val );
+ ber_bvarray_add( &c->rvalue_vals, &ssd );
+ rc = 0;
+ }
+ break;
+ case NSS_MAP:
+ rc = 1;
+ for (i=NM_alias;i<NM_NONE;i++) {
+
+ mi = &ni->ni_maps[i];
+ for (j=0;!BER_BVISNULL(&mi->mi_attrkeys[j]);j++) {
+ if ( ber_bvstrcasecmp(&mi->mi_attrkeys[j],
+ &mi->mi_attrs[j].an_name)) {
+ struct berval map;
+
+ map.bv_len = nss_svcs[i].word.bv_len +
+ mi->mi_attrkeys[j].bv_len +
+ mi->mi_attrs[j].an_desc->ad_cname.bv_len + 2;
+ map.bv_val = ch_malloc(map.bv_len + 1);
+ sprintf(map.bv_val, "%s %s %s", nss_svcs[i].word.bv_val,
+ mi->mi_attrkeys[j].bv_val, mi->mi_attrs[j].an_desc->ad_cname.bv_val );
+ ber_bvarray_add( &c->rvalue_vals, &map );
+ rc = 0;
+ }
+ }
+ }
+ break;
+ case NSS_PAM:
+ rc = mask_to_verbs( pam_opts, ni->ni_pam_opts, &c->rvalue_vals );
+ break;
+ case NSS_PAMGROUP:
+ if (!BER_BVISEMPTY( &ni->ni_pam_group_dn )) {
+ value_add_one( &c->rvalue_vals, &ni->ni_pam_group_dn );
+ value_add_one( &c->rvalue_nvals, &ni->ni_pam_group_dn );
+ } else {
+ rc = 1;
+ }
+ break;
+ case NSS_PAMSESS:
+ if (ni->ni_pam_sessions) {
+ ber_bvarray_dup_x( &c->rvalue_vals, ni->ni_pam_sessions, NULL );
+ } else {
+ rc = 1;
+ }
+ break;
+ }
+ return rc;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ /* FIXME */
+ return 1;
+ }
+ switch( c->type ) {
+ case NSS_SSD: {
+ LDAPURLDesc *lud;
+
+ i = verb_to_mask(c->argv[1], nss_svcs);
+ if ( i == NM_NONE )
+ return 1;
+
+ mi = &ni->ni_maps[i];
+ rc = ldap_url_parse(c->argv[2], &lud);
+ if ( rc )
+ return 1;
+ do {
+ struct berval base;
+ /* Must be LDAP scheme */
+ if (strcasecmp(lud->lud_scheme,"ldap")) {
+ rc = 1;
+ break;
+ }
+ /* Host part, attrs, and extensions must be empty */
+ if (( lud->lud_host && *lud->lud_host ) ||
+ lud->lud_attrs || lud->lud_exts ) {
+ rc = 1;
+ break;
+ }
+ ber_str2bv( lud->lud_dn,0,0,&base);
+ rc = dnNormalize( 0,NULL,NULL,&base,&mi->mi_base,NULL);
+ if ( rc )
+ break;
+ if ( lud->lud_filter ) {
+ /* steal this */
+ ber_str2bv( lud->lud_filter,0,0,&mi->mi_filter);
+ lud->lud_filter = NULL;
+ }
+ mi->mi_scope = lud->lud_scope;
+ } while(0);
+ ldap_free_urldesc( lud );
+ }
+ break;
+ case NSS_MAP:
+ i = verb_to_mask(c->argv[1], nss_svcs);
+ if ( i == NM_NONE )
+ return 1;
+ rc = 1;
+ mi = &ni->ni_maps[i];
+ for (j=0; !BER_BVISNULL(&mi->mi_attrkeys[j]); j++) {
+ if (!strcasecmp(c->argv[2],mi->mi_attrkeys[j].bv_val)) {
+ AttributeDescription *ad = NULL;
+ const char *text;
+ rc = slap_str2ad( c->argv[3], &ad, &text);
+ if ( rc == 0 ) {
+ mi->mi_attrs[j].an_desc = ad;
+ mi->mi_attrs[j].an_name = ad->ad_cname;
+ }
+ break;
+ }
+ }
+ break;
+ case NSS_PAM:
+ m = ni->ni_pam_opts;
+ i = verbs_to_mask(c->argc, c->argv, pam_opts, &m);
+ if (i == 0) {
+ ni->ni_pam_opts = m;
+ if ((m & NI_PAM_USERHOST) && !nssov_pam_host_ad) {
+ const char *text;
+ i = slap_str2ad("host", &nssov_pam_host_ad, &text);
+ if (i != LDAP_SUCCESS) {
+ snprintf(c->cr_msg, sizeof(c->cr_msg),
+ "nssov: host attr unknown: %s", text);
+ Debug(LDAP_DEBUG_ANY,"%s\n",c->cr_msg );
+ rc = 1;
+ break;
+ }
+ }
+ if ((m & (NI_PAM_USERSVC|NI_PAM_HOSTSVC)) && !nssov_pam_svc_ad) {
+ const char *text;
+ i = slap_str2ad("authorizedService", &nssov_pam_svc_ad, &text);
+ if (i != LDAP_SUCCESS) {
+ snprintf(c->cr_msg, sizeof(c->cr_msg),
+ "nssov: authorizedService attr unknown: %s", text);
+ Debug(LDAP_DEBUG_ANY,"%s\n",c->cr_msg );
+ rc = 1;
+ break;
+ }
+ }
+ } else {
+ rc = 1;
+ }
+ break;
+ case NSS_PAMGROUP:
+ ni->ni_pam_group_dn = c->value_ndn;
+ ch_free( c->value_dn.bv_val );
+ break;
+ case NSS_PAMSESS:
+ ber_str2bv( c->argv[1], 0, 1, &c->value_bv );
+ ber_bvarray_add( &ni->ni_pam_sessions, &c->value_bv );
+ break;
+ }
+ return rc;
+}
+
+static int
+nssov_db_init(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ nssov_info *ni;
+ int rc;
+
+ rc = nssov_pam_init();
+ if (rc) return rc;
+
+ ni = ch_calloc( 1, sizeof(nssov_info) );
+ on->on_bi.bi_private = ni;
+
+ /* set up map keys */
+ nssov_alias_init(ni);
+ nssov_ether_init(ni);
+ nssov_group_init(ni);
+ nssov_host_init(ni);
+ nssov_netgroup_init(ni);
+ nssov_network_init(ni);
+ nssov_passwd_init(ni);
+ nssov_protocol_init(ni);
+ nssov_rpc_init(ni);
+ nssov_service_init(ni);
+ nssov_shadow_init(ni);
+
+ ni->ni_db = be->bd_self;
+ ni->ni_pam_opts = NI_PAM_UID2DN;
+
+ return 0;
+}
+
+static int
+nssov_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ return 0;
+}
+
+static int
+nssov_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ nssov_info *ni = on->on_bi.bi_private;
+ nssov_mapinfo *mi;
+
+ int i, sock;
+ struct sockaddr_un addr;
+
+ /* Set default bases */
+ for (i=0; i<NM_NONE; i++) {
+ if ( BER_BVISNULL( &ni->ni_maps[i].mi_base )) {
+ ber_dupbv( &ni->ni_maps[i].mi_base, &be->be_nsuffix[0] );
+ }
+ if ( ni->ni_maps[i].mi_scope == LDAP_SCOPE_DEFAULT )
+ ni->ni_maps[i].mi_scope = LDAP_SCOPE_SUBTREE;
+ }
+ /* validate attribute maps */
+ mi = ni->ni_maps;
+ for ( i=0; i<NM_NONE; i++,mi++) {
+ const char *text;
+ int j;
+ for (j=0; !BER_BVISNULL(&mi->mi_attrkeys[j]); j++) {
+ /* skip attrs we already validated */
+ if ( mi->mi_attrs[j].an_desc ) continue;
+ if ( slap_bv2ad( &mi->mi_attrs[j].an_name,
+ &mi->mi_attrs[j].an_desc, &text )) {
+ Debug(LDAP_DEBUG_ANY,"nssov: invalid attr \"%s\": %s\n",
+ mi->mi_attrs[j].an_name.bv_val, text );
+ return -1;
+ }
+ }
+ BER_BVZERO(&mi->mi_attrs[j].an_name);
+ mi->mi_attrs[j].an_desc = NULL;
+ }
+
+ /* Find host and authorizedService definitions */
+ if ((ni->ni_pam_opts & NI_PAM_USERHOST) && !nssov_pam_host_ad)
+ {
+ const char *text;
+ i = slap_str2ad("host", &nssov_pam_host_ad, &text);
+ if (i != LDAP_SUCCESS) {
+ Debug(LDAP_DEBUG_ANY,"nssov: host attr unknown: %s\n",
+ text );
+ return -1;
+ }
+ }
+ if ((ni->ni_pam_opts & (NI_PAM_USERSVC|NI_PAM_HOSTSVC)) &&
+ !nssov_pam_svc_ad)
+ {
+ const char *text;
+ i = slap_str2ad("authorizedService", &nssov_pam_svc_ad, &text);
+ if (i != LDAP_SUCCESS) {
+ Debug(LDAP_DEBUG_ANY,"nssov: authorizedService attr unknown: %s\n",
+ text );
+ return -1;
+ }
+ }
+ if ( slapMode & SLAP_SERVER_MODE ) {
+ char ebuf[128];
+ /* make sure /var/run/nslcd exists */
+ if (mkdir(NSLCD_PATH, (mode_t) 0555)) {
+ int saved_errno = errno;
+ Debug(LDAP_DEBUG_TRACE,"nssov: mkdir(%s) failed (ignored): %s\n",
+ NSLCD_PATH, AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ } else {
+ Debug(LDAP_DEBUG_TRACE,"nssov: created %s\n",NSLCD_PATH );
+ }
+
+ /* create a socket */
+ if ( (sock=socket(PF_UNIX,SOCK_STREAM,0))<0 )
+ {
+ int saved_errno = errno;
+ Debug(LDAP_DEBUG_ANY,"nssov: cannot create socket: %s\n",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ return -1;
+ }
+ /* remove existing named socket */
+ if (unlink(NSLCD_SOCKET)<0)
+ {
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_TRACE,"nssov: unlink() of "NSLCD_SOCKET" failed (ignored): %s\n",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ }
+ /* create socket address structure */
+ memset(&addr,0,sizeof(struct sockaddr_un));
+ addr.sun_family=AF_UNIX;
+ strncpy(addr.sun_path,NSLCD_SOCKET,sizeof(addr.sun_path));
+ addr.sun_path[sizeof(addr.sun_path)-1]='\0';
+ /* bind to the named socket */
+ if (bind(sock,(struct sockaddr *)&addr,sizeof(struct sockaddr_un)))
+ {
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: bind() to "NSLCD_SOCKET" failed: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ if (close(sock)) {
+ saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ }
+ return -1;
+ }
+ /* close the file descriptor on exit */
+ if (fcntl(sock,F_SETFD,FD_CLOEXEC)<0)
+ {
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: fcntl(F_SETFL,O_NONBLOCK) failed: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ if (close(sock)) {
+ saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ }
+ return -1;
+ }
+ /* set permissions of socket so anybody can do requests */
+ /* Note: we use chmod() here instead of fchmod() because
+ fchmod does not work on sockets
+ http://www.opengroup.org/onlinepubs/009695399/functions/fchmod.html
+ http://lkml.org/lkml/2005/5/16/11 */
+ if (chmod(NSLCD_SOCKET,(mode_t)0666))
+ {
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: chmod(0666) failed: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ if (close(sock)) {
+ saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ }
+ return -1;
+ }
+ /* start listening for connections */
+ if (listen(sock,SOMAXCONN)<0)
+ {
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: listen() failed: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ if (close(sock)) {
+ saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"nssov: problem closing socket: %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ }
+ return -1;
+ }
+ ni->ni_socket = sock;
+ ni->ni_conn = connection_client_setup( sock, acceptconn, ni );
+ }
+
+ return 0;
+}
+
+static int
+nssov_db_close(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ nssov_info *ni = on->on_bi.bi_private;
+
+ if ( slapMode & SLAP_SERVER_MODE ) {
+ char ebuf[128];
+ /* close socket if it's still in use */
+ if (ni->ni_socket >= 0)
+ {
+ if (close(ni->ni_socket)) {
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,"problem closing server socket (ignored): %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ }
+ ni->ni_socket = -1;
+ }
+ /* remove existing named socket */
+ if (unlink(NSLCD_SOCKET)<0)
+ {
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_TRACE,"unlink() of "NSLCD_SOCKET" failed (ignored): %s",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof(ebuf)) );
+ }
+ }
+ return 0;
+}
+
+static slap_overinst nssov;
+
+int
+nssov_initialize( void )
+{
+ int rc;
+
+ nssov.on_bi.bi_type = "nssov";
+ nssov.on_bi.bi_db_init = nssov_db_init;
+ nssov.on_bi.bi_db_destroy = nssov_db_destroy;
+ nssov.on_bi.bi_db_open = nssov_db_open;
+ nssov.on_bi.bi_db_close = nssov_db_close;
+
+ nssov.on_bi.bi_cf_ocs = nssocs;
+
+ rc = config_register_schema( nsscfg, nssocs );
+ if ( rc ) return rc;
+
+ return overlay_register(&nssov);
+}
+
+#if SLAPD_OVER_NSSOV == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return nssov_initialize();
+}
+#endif
diff --git a/contrib/slapd-modules/nssov/nssov.h b/contrib/slapd-modules/nssov/nssov.h
new file mode 100644
index 0000000..ce1ecd7
--- /dev/null
+++ b/contrib/slapd-modules/nssov/nssov.h
@@ -0,0 +1,348 @@
+/* nssov.h - NSS overlay header file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 Howard Chu.
+ * Portions Copyright 2013 Ted C. Cheng, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef NSSOV_H
+#define NSSOV_H
+
+#ifndef NSLCD_PATH
+#define NSLCD_PATH "/var/run/nslcd"
+#endif
+
+#ifndef NSLCD_SOCKET
+#define NSLCD_SOCKET NSLCD_PATH "/socket"
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "nslcd.h"
+#include "nslcd-prot.h"
+#include "tio.h"
+#include "attrs.h"
+
+#undef PACKAGE_BUGREPORT
+#undef PACKAGE_NAME
+#undef PACKAGE_STRING
+#undef PACKAGE_TARNAME
+#undef PACKAGE_VERSION
+
+#include "portable.h"
+#include "slap.h"
+#include <ac/string.h>
+
+/* selectors for different maps */
+enum nssov_map_selector
+{
+ NM_alias,
+ NM_ether,
+ NM_group,
+ NM_host,
+ NM_netgroup,
+ NM_network,
+ NM_passwd,
+ NM_protocol,
+ NM_rpc,
+ NM_service,
+ NM_shadow,
+ NM_NONE
+};
+
+typedef struct nssov_mapinfo {
+ struct berval mi_base;
+ int mi_scope;
+ struct berval mi_filter0;
+ struct berval mi_filter;
+ struct berval *mi_attrkeys;
+ AttributeName *mi_attrs;
+} nssov_mapinfo;
+
+typedef struct nssov_info
+{
+ /* search timelimit */
+ int ni_timelimit;
+ struct nssov_mapinfo ni_maps[NM_NONE];
+ int ni_socket;
+ Connection *ni_conn;
+ BackendDB *ni_db;
+
+ /* PAM authz support... */
+ slap_mask_t ni_pam_opts;
+ struct berval ni_pam_group_dn;
+ AttributeDescription *ni_pam_group_ad;
+ int ni_pam_min_uid;
+ int ni_pam_max_uid;
+ AttributeDescription *ni_pam_template_ad;
+ struct berval ni_pam_template;
+ struct berval ni_pam_defhost;
+ struct berval *ni_pam_sessions;
+ struct berval ni_pam_password_prohibit_message;
+ struct berval ni_pam_pwdmgr_dn;
+ struct berval ni_pam_pwdmgr_pwd;
+} nssov_info;
+
+#define NI_PAM_USERHOST 1 /* old style host checking */
+#define NI_PAM_USERSVC 2 /* old style service checking */
+#define NI_PAM_USERGRP 4 /* old style group checking */
+#define NI_PAM_HOSTSVC 8 /* new style authz checking */
+#define NI_PAM_SASL2DN 0x10 /* use sasl2dn */
+#define NI_PAM_UID2DN 0x20 /* use uid2dn */
+
+#define NI_PAM_OLD (NI_PAM_USERHOST|NI_PAM_USERSVC|NI_PAM_USERGRP)
+#define NI_PAM_NEW NI_PAM_HOSTSVC
+
+extern AttributeDescription *nssov_pam_host_ad;
+extern AttributeDescription *nssov_pam_svc_ad;
+
+/* Read the default configuration file. */
+void nssov_cfg_init(nssov_info *ni,const char *fname);
+
+/* macros for basic read and write operations, the following
+ ERROR_OUT* marcos define the action taken on errors
+ the stream is not closed because the caller closes the
+ stream */
+
+#define ERROR_OUT_WRITEERROR(fp) \
+ Debug(LDAP_DEBUG_ANY,"nssov: error writing to client\n"); \
+ return -1;
+
+#define ERROR_OUT_READERROR(fp) \
+ Debug(LDAP_DEBUG_ANY,"nssov: error reading from client\n"); \
+ return -1;
+
+#define ERROR_OUT_BUFERROR(fp) \
+ Debug(LDAP_DEBUG_ANY,"nssov: client supplied argument too large\n"); \
+ return -1;
+
+#define WRITE_BERVAL(fp, bv) \
+ DEBUG_PRINT("WRITE_BERVAL: var="__STRING(bv)" bv_val=\"%s\"", (bv)->bv_val); \
+ if ((bv) == NULL) \
+ { \
+ WRITE_INT32(fp, 0); \
+ } \
+ else \
+ { \
+ WRITE_INT32(fp, (bv)->bv_len); \
+ tmpint32 = ntohl(tmpint32); \
+ if (tmpint32 > 0) \
+ { \
+ WRITE(fp, (bv)->bv_val, tmpint32); \
+ } \
+ } \
+
+#define WRITE_BVARRAY(fp, arr) \
+ if ((arr) == NULL) \
+ { \
+ DEBUG_PRINT("WRITE_BVARRAY: var="__STRING(arr)" num=%d", 0); \
+ WRITE_INT32(fp, 0); \
+ } \
+ else \
+ { \
+ /* first determine length of array */ \
+ for (tmp3int32 = 0; (arr)[tmp3int32].bv_val != NULL; tmp3int32++) \
+ /* nothing */ ; \
+ /* write number of strings */ \
+ DEBUG_PRINT("WRITE_BVARRAY: var="__STRING(arr)" num=%d", (int)tmp3int32); \
+ WRITE_INT32(fp, tmp3int32); \
+ /* write strings */ \
+ for (tmp2int32 = 0; tmp2int32 < tmp3int32; tmp2int32++) \
+ { \
+ WRITE_BERVAL(fp, &(arr)[tmp2int32]); \
+ } \
+ } \
+
+/* Find the given attribute's value in the RDN of the DN. */
+void nssov_find_rdnval(struct berval *dn,AttributeDescription *ad,struct berval *value);
+
+/* This tries to get the user password attribute from the entry.
+ It will try to return an encrypted password as it is used in /etc/passwd,
+ /etc/group or /etc/shadow depending upon what is in the directory.
+ This function will return NULL if no passwd is found and will return the
+ literal value in the directory if conversion is not possible. */
+void get_userpassword(struct berval *attr, struct berval *pw);
+
+/* write out an address, parsing the addr value */
+int write_address(TFILE *fp,struct berval *addr);
+
+/* a helper macro to write out addresses and bail out on errors */
+#define WRITE_ADDRESS(fp,addr) \
+ if (write_address(fp,addr)) \
+ return -1;
+
+/* read an address from the stream */
+int read_address(TFILE *fp,char *addr,int *addrlen,int *af);
+
+/* helper macro to read an address from the stream */
+#define READ_ADDRESS(fp,addr,len,af) \
+ len=(int)sizeof(addr); \
+ if (read_address(fp,addr,&(len),&(af))) \
+ return -1;
+
+/* checks to see if the specified string is a valid username */
+int isvalidusername(struct berval *name);
+
+/* transforms the DN into a uid doing an LDAP lookup if needed */
+int nssov_dn2uid(Operation *op,nssov_info *ni,struct berval *dn,struct berval *uid);
+
+/* transforms the uid into a DN by doing an LDAP lookup */
+int nssov_uid2dn(Operation *op,nssov_info *ni,struct berval *uid,struct berval *dn);
+int nssov_name2dn_cb(Operation *op, SlapReply *rs);
+
+/* Escapes characters in a string for use in a search filter. */
+int nssov_escape(struct berval *src,struct berval *dst);
+
+int nssov_filter_byname(nssov_mapinfo *mi,int key,struct berval *name,struct berval *buf);
+int nssov_filter_byid(nssov_mapinfo *mi,int key,struct berval *id,struct berval *buf);
+
+void nssov_alias_init(nssov_info *ni);
+void nssov_ether_init(nssov_info *ni);
+void nssov_group_init(nssov_info *ni);
+void nssov_host_init(nssov_info *ni);
+void nssov_netgroup_init(nssov_info *ni);
+void nssov_network_init(nssov_info *ni);
+void nssov_passwd_init(nssov_info *ni);
+void nssov_protocol_init(nssov_info *ni);
+void nssov_rpc_init(nssov_info *ni);
+void nssov_service_init(nssov_info *ni);
+void nssov_shadow_init(nssov_info *ni);
+
+int nssov_pam_init(void);
+
+/* these are the different functions that handle the database
+ specific actions, see nslcd.h for the action descriptions */
+int nssov_alias_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_alias_all(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_ether_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_ether_byether(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_ether_all(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_group_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_group_bygid(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_group_bymember(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_group_all(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_host_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_host_byaddr(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_host_all(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_netgroup_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_network_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_network_byaddr(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_network_all(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_passwd_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_passwd_byuid(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_passwd_all(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_protocol_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_protocol_bynumber(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_protocol_all(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_rpc_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_rpc_bynumber(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_rpc_all(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_service_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_service_bynumber(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_service_all(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_shadow_byname(nssov_info *ni,TFILE *fp,Operation *op);
+int nssov_shadow_all(nssov_info *ni,TFILE *fp,Operation *op);
+int pam_authc(nssov_info *ni,TFILE *fp,Operation *op,uid_t calleruid);
+int pam_authz(nssov_info *ni,TFILE *fp,Operation *op);
+int pam_sess_o(nssov_info *ni,TFILE *fp,Operation *op);
+int pam_sess_c(nssov_info *ni,TFILE *fp,Operation *op);
+int pam_pwmod(nssov_info *ni,TFILE *fp,Operation *op,uid_t calleruid);
+
+/* config initialization */
+#define NSSOV_INIT(db) \
+ void nssov_##db##_init(nssov_info *ni) \
+ { \
+ nssov_mapinfo *mi = &ni->ni_maps[NM_##db]; \
+ int i; \
+ for (i=0;!BER_BVISNULL(&db##_keys[i]);i++); \
+ i++; \
+ mi->mi_attrs = ch_malloc( i*sizeof(AttributeName)); \
+ for (i=0;!BER_BVISNULL(&db##_keys[i]);i++) { \
+ mi->mi_attrs[i].an_name = db##_keys[i]; \
+ mi->mi_attrs[i].an_desc = NULL; \
+ } \
+ mi->mi_scope = LDAP_SCOPE_DEFAULT; \
+ mi->mi_filter0 = db##_filter; \
+ ber_dupbv( &mi->mi_filter, &mi->mi_filter0 ); \
+ mi->mi_filter = db##_filter; \
+ mi->mi_attrkeys = db##_keys; \
+ BER_BVZERO(&mi->mi_base); \
+ }
+
+/* param structure for search callback */
+#define NSSOV_CBPRIV(db,parms) \
+ typedef struct nssov_##db##_cbp { \
+ nssov_mapinfo *mi; \
+ TFILE *fp; \
+ Operation *op; \
+ parms \
+ } nssov_##db##_cbp
+
+/* callback for writing search results */
+#define NSSOV_CB(db) \
+ static int nssov_##db##_cb(Operation *op, SlapReply *rs) \
+ { \
+ if ( rs->sr_type == REP_SEARCH ) { \
+ nssov_##db##_cbp *cbp = op->o_callback->sc_private; \
+ if (write_##db(cbp,rs->sr_entry)) return LDAP_OTHER; \
+ } \
+ return LDAP_SUCCESS; \
+ } \
+
+/* macro for generating service handling code */
+#define NSSOV_HANDLE(db,fn,readfn,logcall,action,mkfilter) \
+ int nssov_##db##_##fn(nssov_info *ni,TFILE *fp,Operation *op) \
+ { \
+ /* define common variables */ \
+ int32_t tmpint32; \
+ nssov_##db##_cbp cbp; \
+ slap_callback cb = {0}; \
+ SlapReply rs = {REP_RESULT}; \
+ cbp.mi = &ni->ni_maps[NM_##db]; \
+ cbp.fp = fp; \
+ cbp.op = op; \
+ /* read request parameters */ \
+ readfn; \
+ /* log call */ \
+ logcall; \
+ /* write the response header */ \
+ WRITE_INT32(fp,NSLCD_VERSION); \
+ WRITE_INT32(fp,action); \
+ /* prepare the search filter */ \
+ if (mkfilter) \
+ { \
+ Debug(LDAP_DEBUG_ANY,"nssov_" __STRING(db) "_" __STRING(fn) "(): filter buffer too small"); \
+ return -1; \
+ } \
+ cb.sc_private = &cbp; \
+ op->o_callback = &cb; \
+ cb.sc_response = nssov_##db##_cb; \
+ slap_op_time( &op->o_time, &op->o_tincr ); \
+ op->o_req_dn = cbp.mi->mi_base; \
+ op->o_req_ndn = cbp.mi->mi_base; \
+ op->ors_scope = cbp.mi->mi_scope; \
+ op->ors_filterstr = filter; \
+ op->ors_filter = str2filter_x( op, filter.bv_val ); \
+ op->ors_attrs = cbp.mi->mi_attrs; \
+ op->ors_tlimit = SLAP_NO_LIMIT; \
+ op->ors_slimit = SLAP_NO_LIMIT; \
+ /* do the internal search */ \
+ op->o_bd->be_search( op, &rs ); \
+ filter_free_x( op, op->ors_filter, 1 ); \
+ WRITE_INT32(fp,NSLCD_RESULT_END); \
+ return 0; \
+ }
+
+#endif /* NSSOV_H */
diff --git a/contrib/slapd-modules/nssov/pam.c b/contrib/slapd-modules/nssov/pam.c
new file mode 100644
index 0000000..1d416c7
--- /dev/null
+++ b/contrib/slapd-modules/nssov/pam.c
@@ -0,0 +1,862 @@
+/* pam.c - pam processing routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * Portions Copyright 2013 by Ted C. Cheng, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "nssov.h"
+#include "lutil.h"
+
+#undef ldap_debug /* silence a warning in ldap-int.h */
+#include "../../../libraries/libldap/ldap-int.h" /* for ldap_ld_free */
+
+static int ppolicy_cid;
+static AttributeDescription *ad_loginStatus;
+
+struct paminfo {
+ struct berval uid;
+ struct berval dn;
+ struct berval svc;
+ struct berval ruser;
+ struct berval rhost;
+ struct berval tty;
+ struct berval pwd;
+ int authz;
+ struct berval msg;
+ int ispwdmgr;
+};
+
+static int pam_bindcb(
+ Operation *op, SlapReply *rs)
+{
+ struct paminfo *pi = op->o_callback->sc_private;
+ LDAPControl *ctrl = ldap_control_find(LDAP_CONTROL_PASSWORDPOLICYRESPONSE,
+ rs->sr_ctrls, NULL);
+ if (ctrl) {
+ LDAP *ld;
+ ber_int_t expire, grace;
+ LDAPPasswordPolicyError error;
+
+ ldap_create(&ld);
+ if (ld) {
+ int rc = ldap_parse_passwordpolicy_control(ld,ctrl,
+ &expire,&grace,&error);
+ if (rc == LDAP_SUCCESS) {
+ if (expire >= 0) {
+ char *unit = "seconds";
+ if (expire > 60) {
+ expire /= 60;
+ unit = "minutes";
+ }
+ if (expire > 60) {
+ expire /= 60;
+ unit = "hours";
+ }
+ if (expire > 24) {
+ expire /= 24;
+ unit = "days";
+ }
+#if 0 /* Who warns about expiration so far in advance? */
+ if (expire > 7) {
+ expire /= 7;
+ unit = "weeks";
+ }
+ if (expire > 4) {
+ expire /= 4;
+ unit = "months";
+ }
+ if (expire > 12) {
+ expire /= 12;
+ unit = "years";
+ }
+#endif
+ pi->msg.bv_len = sprintf(pi->msg.bv_val,
+ "\nWARNING: Password expires in %d %s\n", expire, unit);
+ } else if (grace > 0) {
+ pi->msg.bv_len = sprintf(pi->msg.bv_val,
+ "Password expired; %d grace logins remaining",
+ grace);
+ pi->authz = NSLCD_PAM_NEW_AUTHTOK_REQD;
+ } else if (error != PP_noError) {
+ ber_str2bv(ldap_passwordpolicy_err2txt(error), 0, 0,
+ &pi->msg);
+ switch (error) {
+ case PP_passwordExpired:
+ /* report this during authz */
+ rs->sr_err = LDAP_SUCCESS;
+ /* fallthru */
+ case PP_changeAfterReset:
+ pi->authz = NSLCD_PAM_NEW_AUTHTOK_REQD;
+ }
+ }
+ }
+ ldap_ld_free(ld,0,NULL,NULL);
+ }
+ }
+ return LDAP_SUCCESS;
+}
+
+static int pam_uid2dn(nssov_info *ni, Operation *op,
+ struct paminfo *pi)
+{
+ struct berval sdn;
+
+ BER_BVZERO(&pi->dn);
+
+ if (!isvalidusername(&pi->uid)) {
+ Debug(LDAP_DEBUG_ANY,"nssov_pam_uid2dn(%s): invalid user name\n",
+ pi->uid.bv_val ? pi->uid.bv_val : "NULL" );
+ return NSLCD_PAM_USER_UNKNOWN;
+ }
+
+ if (ni->ni_pam_opts & NI_PAM_SASL2DN) {
+ int hlen = global_host_bv.bv_len;
+
+ /* cn=<service>+uid=<user>,cn=<host>,cn=pam,cn=auth */
+ sdn.bv_len = pi->uid.bv_len + pi->svc.bv_len + hlen +
+ STRLENOF( "cn=+uid=,cn=,cn=pam,cn=auth" );
+ sdn.bv_val = op->o_tmpalloc( sdn.bv_len + 1, op->o_tmpmemctx );
+ sprintf(sdn.bv_val, "cn=%s+uid=%s,cn=%s,cn=pam,cn=auth",
+ pi->svc.bv_val, pi->uid.bv_val, global_host_bv.bv_val);
+ slap_sasl2dn(op, &sdn, &pi->dn, 0);
+ op->o_tmpfree( sdn.bv_val, op->o_tmpmemctx );
+ }
+
+ /* If no luck, do a basic uid search */
+ if (BER_BVISEMPTY(&pi->dn) && (ni->ni_pam_opts & NI_PAM_UID2DN)) {
+ nssov_uid2dn(op, ni, &pi->uid, &pi->dn);
+ if (!BER_BVISEMPTY(&pi->dn)) {
+ sdn = pi->dn;
+ dnNormalize( 0, NULL, NULL, &sdn, &pi->dn, op->o_tmpmemctx );
+ }
+ }
+ if (BER_BVISEMPTY(&pi->dn)) {
+ return NSLCD_PAM_USER_UNKNOWN;
+ }
+ return 0;
+}
+
+int pam_do_bind(nssov_info *ni,TFILE *fp,Operation *op,
+ struct paminfo *pi)
+{
+ int rc;
+ slap_callback cb = {0};
+ SlapReply rs = {REP_RESULT};
+
+ pi->msg.bv_val = pi->pwd.bv_val;
+ pi->msg.bv_len = 0;
+ pi->authz = NSLCD_PAM_SUCCESS;
+
+ if (!pi->ispwdmgr) {
+
+ rc = pam_uid2dn(ni, op, pi);
+ if (rc) goto finish;
+
+ if (BER_BVISEMPTY(&pi->pwd)) {
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto finish;
+ }
+
+ /* Should only need to do this once at open time, but there's always
+ * the possibility that ppolicy will get loaded later.
+ */
+ if (!ppolicy_cid) {
+ rc = slap_find_control_id(LDAP_CONTROL_PASSWORDPOLICYREQUEST,
+ &ppolicy_cid);
+ }
+ /* of course, 0 is a valid cid, but it won't be ppolicy... */
+ if (ppolicy_cid) {
+ op->o_ctrlflag[ppolicy_cid] = SLAP_CONTROL_NONCRITICAL;
+ }
+ }
+
+ cb.sc_response = pam_bindcb;
+ cb.sc_private = pi;
+ op->o_callback = &cb;
+ op->o_dn.bv_val[0] = 0;
+ op->o_dn.bv_len = 0;
+ op->o_ndn.bv_val[0] = 0;
+ op->o_ndn.bv_len = 0;
+ op->o_tag = LDAP_REQ_BIND;
+ op->o_protocol = LDAP_VERSION3;
+ op->orb_method = LDAP_AUTH_SIMPLE;
+ op->orb_cred = pi->pwd;
+ op->o_req_dn = pi->dn;
+ op->o_req_ndn = pi->dn;
+ slap_op_time( &op->o_time, &op->o_tincr );
+ rc = op->o_bd->be_bind( op, &rs );
+ memset(pi->pwd.bv_val,0,pi->pwd.bv_len);
+ /* quirk: on successful bind, caller has to send result. we need
+ * to make sure callbacks run.
+ */
+ if (rc == LDAP_SUCCESS)
+ send_ldap_result(op, &rs);
+ switch(rs.sr_err) {
+ case LDAP_SUCCESS: rc = NSLCD_PAM_SUCCESS; break;
+ case LDAP_INVALID_CREDENTIALS: rc = NSLCD_PAM_AUTH_ERR; break;
+ default: rc = NSLCD_PAM_AUTH_ERR; break;
+ }
+finish:
+ Debug(LDAP_DEBUG_ANY,"pam_do_bind (%s): rc (%d)\n",
+ pi->dn.bv_val ? pi->dn.bv_val : "NULL", rc );
+ return rc;
+}
+
+int pam_authc(nssov_info *ni,TFILE *fp,Operation *op,uid_t calleruid)
+{
+ int32_t tmpint32;
+ int rc;
+ char uidc[32];
+ char svcc[256];
+ char ruserc[32];
+ char rhostc[256];
+ char ttyc[256];
+ char pwdc[256];
+ struct paminfo pi;
+
+
+ READ_STRING(fp,uidc);
+ pi.uid.bv_val = uidc;
+ pi.uid.bv_len = tmpint32;
+ READ_STRING(fp,svcc);
+ pi.svc.bv_val = svcc;
+ pi.svc.bv_len = tmpint32;
+ READ_STRING(fp,ruserc);
+ pi.ruser.bv_val = ruserc;
+ pi.ruser.bv_len = tmpint32;
+ READ_STRING(fp,rhostc);
+ pi.rhost.bv_val = rhostc;
+ pi.rhost.bv_len = tmpint32;
+ READ_STRING(fp,ttyc);
+ pi.tty.bv_val = ttyc;
+ pi.tty.bv_len = tmpint32;
+ READ_STRING(fp,pwdc);
+ pi.pwd.bv_val = pwdc;
+ pi.pwd.bv_len = tmpint32;
+
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(%s)\n",
+ pi.uid.bv_val ? pi.uid.bv_val : "NULL" );
+
+ BER_BVZERO(&pi.msg);
+ pi.ispwdmgr = 0;
+
+ /* if service is "passwd" and "nssov-pam-password-prohibit-message */
+ /* is set, deny the auth request */
+ if (!strcmp(svcc, "passwd") &&
+ !BER_BVISEMPTY(&ni->ni_pam_password_prohibit_message)) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(): %s (%s)\n",
+ "password_prohibit_message for passwd",
+ ni->ni_pam_password_prohibit_message.bv_val );
+ ber_str2bv(ni->ni_pam_password_prohibit_message.bv_val, 0, 0, &pi.msg);
+ pi.authz = NSLCD_PAM_PERM_DENIED;
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto finish;
+ }
+
+ /* if username is null, pwdmgr password preliminary check */
+ if (BER_BVISEMPTY(&pi.uid)) {
+ if (BER_BVISEMPTY(&ni->ni_pam_pwdmgr_dn)) {
+ /* pwdmgr dn not configured */
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(prelim check): %s\n",
+ "pwdmgr dn not configured" );
+ ber_str2bv("pwdmgr dn not configured", 0, 0, &pi.msg);
+ pi.authz = NSLCD_PAM_PERM_DENIED;
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto finish;
+ } else if (calleruid != 0) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(prelim check): %s\n",
+ "caller is not root" );
+ ber_str2bv("only root may do that", 0, 0, &pi.msg);
+ pi.authz = NSLCD_PAM_PERM_DENIED;
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto finish;
+ } else {
+ /* use pwdmgr dn */
+ ber_str2bv(ni->ni_pam_pwdmgr_dn.bv_val, 0, 0, &pi.dn);
+ }
+
+ /* use pwdmgr pwd if configured */
+ if (BER_BVISEMPTY(&pi.pwd)) {
+ if (BER_BVISEMPTY(&ni->ni_pam_pwdmgr_pwd)) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(prelim check): %s\n",
+ "no pwdmgr pwd" );
+ ber_str2bv("pwdmgr pwd not configured", 0, 0, &pi.msg);
+ pi.authz = NSLCD_PAM_PERM_DENIED;
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto finish;
+ }
+ /* use configured pwdmgr pwd */
+ memset((void *) pwdc, 0, 256);
+ strncpy(pi.pwd.bv_val, ni->ni_pam_pwdmgr_pwd.bv_val,
+ ni->ni_pam_pwdmgr_pwd.bv_len);
+ pi.pwd.bv_len = ni->ni_pam_pwdmgr_pwd.bv_len;
+ }
+ pi.ispwdmgr = 1;
+ }
+
+
+ rc = pam_do_bind(ni, fp, op, &pi);
+
+finish:
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authc(%s): rc (%d)\n",
+ pi.dn.bv_val ? pi.dn.bv_val : "NULL",rc );
+ WRITE_INT32(fp,NSLCD_VERSION);
+ WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHC);
+ WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
+ WRITE_INT32(fp,rc);
+ WRITE_BERVAL(fp,&pi.uid);
+ WRITE_INT32(fp,pi.authz); /* authz */
+ WRITE_BERVAL(fp,&pi.msg); /* authzmsg */
+ WRITE_INT32(fp,NSLCD_RESULT_END);
+ return 0;
+}
+
+static struct berval grpmsg =
+ BER_BVC("Access denied by group check");
+static struct berval hostmsg =
+ BER_BVC("Access denied for this host");
+static struct berval svcmsg =
+ BER_BVC("Access denied for this service");
+static struct berval uidmsg =
+ BER_BVC("Access denied by UID check");
+
+static int pam_compare_cb(Operation *op, SlapReply *rs)
+{
+ if (rs->sr_err == LDAP_COMPARE_TRUE)
+ op->o_callback->sc_private = (void *)1;
+ return LDAP_SUCCESS;
+}
+
+int pam_authz(nssov_info *ni,TFILE *fp,Operation *op)
+{
+ struct berval authzmsg = BER_BVNULL;
+ int32_t tmpint32;
+ char uidc[32];
+ char svcc[256];
+ char ruserc[32];
+ char rhostc[256];
+ char ttyc[256];
+ int rc;
+ struct paminfo pi;
+ Entry *e = NULL;
+ Attribute *a;
+ slap_callback cb = {0};
+
+ READ_STRING(fp,uidc);
+ pi.uid.bv_val = uidc;
+ pi.uid.bv_len = tmpint32;
+ READ_STRING(fp,svcc);
+ pi.svc.bv_val = svcc;
+ pi.svc.bv_len = tmpint32;
+ READ_STRING(fp,ruserc);
+ pi.ruser.bv_val = ruserc;
+ pi.ruser.bv_len = tmpint32;
+ READ_STRING(fp,rhostc);
+ pi.rhost.bv_val = rhostc;
+ pi.rhost.bv_len = tmpint32;
+ READ_STRING(fp,ttyc);
+ pi.tty.bv_val = ttyc;
+ pi.tty.bv_len = tmpint32;
+
+ rc = pam_uid2dn(ni, op, &pi);
+ if (rc) goto finish;
+
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authz(%s)\n",
+ pi.dn.bv_val ? pi.dn.bv_val : "NULL" );
+
+ /* See if they have access to the host and service */
+ if ((ni->ni_pam_opts & NI_PAM_HOSTSVC) && nssov_pam_svc_ad) {
+ AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
+ struct berval hostdn = BER_BVNULL;
+ struct berval odn = op->o_ndn;
+ SlapReply rs = {REP_RESULT};
+ op->o_dn = pi.dn;
+ op->o_ndn = pi.dn;
+ {
+ nssov_mapinfo *mi = &ni->ni_maps[NM_host];
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf),fbuf};
+ SlapReply rs2 = {REP_RESULT};
+
+ /* Lookup the host entry */
+ nssov_filter_byname(mi,0,&global_host_bv,&filter);
+ cb.sc_private = &hostdn;
+ cb.sc_response = nssov_name2dn_cb;
+ op->o_callback = &cb;
+ op->o_req_dn = mi->mi_base;
+ op->o_req_ndn = mi->mi_base;
+ op->ors_scope = mi->mi_scope;
+ op->ors_filterstr = filter;
+ op->ors_filter = str2filter_x(op, filter.bv_val);
+ op->ors_attrs = slap_anlist_no_attrs;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_slimit = 2;
+ rc = op->o_bd->be_search(op, &rs2);
+ filter_free_x(op, op->ors_filter, 1);
+
+ if (BER_BVISEMPTY(&hostdn) &&
+ !BER_BVISEMPTY(&ni->ni_pam_defhost)) {
+ filter.bv_len = sizeof(fbuf);
+ filter.bv_val = fbuf;
+ rs_reinit(&rs2, REP_RESULT);
+ nssov_filter_byname(mi,0,&ni->ni_pam_defhost,&filter);
+ op->ors_filterstr = filter;
+ op->ors_filter = str2filter_x(op, filter.bv_val);
+ rc = op->o_bd->be_search(op, &rs2);
+ filter_free_x(op, op->ors_filter, 1);
+ }
+
+ /* no host entry, no default host -> deny */
+ if (BER_BVISEMPTY(&hostdn)) {
+ rc = NSLCD_PAM_PERM_DENIED;
+ authzmsg = hostmsg;
+ goto finish;
+ }
+ }
+
+ cb.sc_response = pam_compare_cb;
+ cb.sc_private = NULL;
+ op->o_tag = LDAP_REQ_COMPARE;
+ op->o_req_dn = hostdn;
+ op->o_req_ndn = hostdn;
+ ava.aa_desc = nssov_pam_svc_ad;
+ ava.aa_value = pi.svc;
+ op->orc_ava = &ava;
+ rc = op->o_bd->be_compare( op, &rs );
+ if ( cb.sc_private == NULL ) {
+ authzmsg = svcmsg;
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto finish;
+ }
+ op->o_dn = odn;
+ op->o_ndn = odn;
+ }
+
+ /* See if they're a member of the group */
+ if ((ni->ni_pam_opts & NI_PAM_USERGRP) &&
+ !BER_BVISEMPTY(&ni->ni_pam_group_dn) &&
+ ni->ni_pam_group_ad) {
+ AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
+ SlapReply rs = {REP_RESULT};
+ op->o_callback = &cb;
+ cb.sc_response = pam_compare_cb;
+ cb.sc_private = NULL;
+ op->o_tag = LDAP_REQ_COMPARE;
+ op->o_req_dn = ni->ni_pam_group_dn;
+ op->o_req_ndn = ni->ni_pam_group_dn;
+ ava.aa_desc = ni->ni_pam_group_ad;
+ ava.aa_value = pi.dn;
+ op->orc_ava = &ava;
+ rc = op->o_bd->be_compare( op, &rs );
+ if ( cb.sc_private == NULL ) {
+ authzmsg = grpmsg;
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto finish;
+ }
+ }
+
+ /* We need to check the user's entry for these bits */
+ if ((ni->ni_pam_opts & (NI_PAM_USERHOST|NI_PAM_USERSVC)) ||
+ ni->ni_pam_template_ad ||
+ ni->ni_pam_min_uid || ni->ni_pam_max_uid ) {
+ rc = be_entry_get_rw( op, &pi.dn, NULL, NULL, 0, &e );
+ if (rc != LDAP_SUCCESS) {
+ rc = NSLCD_PAM_USER_UNKNOWN;
+ goto finish;
+ }
+ }
+ if ((ni->ni_pam_opts & NI_PAM_USERHOST) && nssov_pam_host_ad) {
+ a = attr_find(e->e_attrs, nssov_pam_host_ad);
+ if (!a || attr_valfind( a,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_VALUE_OF_SYNTAX,
+ &global_host_bv, NULL, op->o_tmpmemctx )) {
+ rc = NSLCD_PAM_PERM_DENIED;
+ authzmsg = hostmsg;
+ goto finish;
+ }
+ }
+ if ((ni->ni_pam_opts & NI_PAM_USERSVC) && nssov_pam_svc_ad) {
+ a = attr_find(e->e_attrs, nssov_pam_svc_ad);
+ if (!a || attr_valfind( a,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_VALUE_OF_SYNTAX,
+ &pi.svc, NULL, op->o_tmpmemctx )) {
+ rc = NSLCD_PAM_PERM_DENIED;
+ authzmsg = svcmsg;
+ goto finish;
+ }
+ }
+
+/* from passwd.c */
+#define UIDN_KEY 2
+
+ if (ni->ni_pam_min_uid || ni->ni_pam_max_uid) {
+ int id;
+ char *tmp;
+ nssov_mapinfo *mi = &ni->ni_maps[NM_passwd];
+ a = attr_find(e->e_attrs, mi->mi_attrs[UIDN_KEY].an_desc);
+ if (!a) {
+ rc = NSLCD_PAM_PERM_DENIED;
+ authzmsg = uidmsg;
+ goto finish;
+ }
+ id = (int)strtol(a->a_vals[0].bv_val,&tmp,0);
+ if (a->a_vals[0].bv_val[0] == '\0' || *tmp != '\0') {
+ rc = NSLCD_PAM_PERM_DENIED;
+ authzmsg = uidmsg;
+ goto finish;
+ }
+ if ((ni->ni_pam_min_uid && id < ni->ni_pam_min_uid) ||
+ (ni->ni_pam_max_uid && id > ni->ni_pam_max_uid)) {
+ rc = NSLCD_PAM_PERM_DENIED;
+ authzmsg = uidmsg;
+ goto finish;
+ }
+ }
+
+ if (ni->ni_pam_template_ad) {
+ a = attr_find(e->e_attrs, ni->ni_pam_template_ad);
+ if (a)
+ pi.uid = a->a_vals[0];
+ else if (!BER_BVISEMPTY(&ni->ni_pam_template))
+ pi.uid = ni->ni_pam_template;
+ }
+ rc = NSLCD_PAM_SUCCESS;
+
+finish:
+ WRITE_INT32(fp,NSLCD_VERSION);
+ WRITE_INT32(fp,NSLCD_ACTION_PAM_AUTHZ);
+ WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
+ WRITE_INT32(fp,rc);
+ WRITE_BERVAL(fp,&authzmsg);
+ WRITE_INT32(fp,NSLCD_RESULT_END);
+ if (e) {
+ be_entry_release_r(op, e);
+ }
+ switch (rc) {
+ case NSLCD_PAM_SUCCESS:
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authz(): success\n" );
+ break;
+ case NSLCD_PAM_PERM_DENIED:
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_authz(): %s\n",
+ authzmsg.bv_val ? authzmsg.bv_val : "NULL" );
+ break;
+ default:
+ Debug(LDAP_DEBUG_TRACE,
+ "nssov_pam_authz(): permission denied, rc (%d)\n",
+ rc );
+ }
+ return 0;
+}
+
+static int pam_sess(nssov_info *ni,TFILE *fp,Operation *op,int action)
+{
+ int32_t tmpint32;
+ char svcc[256];
+ char uidc[32];
+ char ttyc[32];
+ char rhostc[256];
+ char ruserc[32];
+ char sessionID[64];
+ struct paminfo pi;
+ slap_callback cb = {0};
+ SlapReply rs = {REP_RESULT};
+ char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE];
+ struct berval timestamp, bv[2], *nbv;
+ time_t stamp;
+ Modifications mod;
+ int rc = 0;
+
+ READ_STRING(fp,uidc);
+ pi.uid.bv_val = uidc;
+ pi.uid.bv_len = tmpint32;
+ READ_STRING(fp,svcc);
+ pi.svc.bv_val = svcc;
+ pi.svc.bv_len = tmpint32;
+ READ_STRING(fp,ruserc);
+ pi.ruser.bv_val = ruserc;
+ pi.ruser.bv_len = tmpint32;
+ READ_STRING(fp,rhostc);
+ pi.rhost.bv_val = rhostc;
+ pi.rhost.bv_len = tmpint32;
+ READ_STRING(fp,ttyc);
+ pi.tty.bv_val = ttyc;
+ pi.tty.bv_len = tmpint32;
+
+ if (action==NSLCD_ACTION_PAM_SESS_O) {
+ slap_op_time( &op->o_time, &op->o_tincr );
+ timestamp.bv_len = sizeof(timebuf);
+ timestamp.bv_val = timebuf;
+ stamp = op->o_time;
+ slap_timestamp( &stamp, &timestamp );
+ } else {
+ READ_STRING(fp,sessionID);
+ timestamp.bv_val = sessionID;
+ timestamp.bv_len = tmpint32;
+ }
+
+ rc = pam_uid2dn(ni, op, &pi);
+ if (rc) goto done;
+
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_sess_%c(%s)\n",
+ action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c', pi.dn.bv_val );
+
+ if (!ni->ni_pam_sessions) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_sess_%c(): %s\n",
+ action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c',
+ "pam session(s) not configured, ignored" );
+ rc = -1;
+ goto done;
+ }
+
+ {
+ int i, found=0;
+ for (i=0; !BER_BVISNULL(&ni->ni_pam_sessions[i]); i++) {
+ if (ni->ni_pam_sessions[i].bv_len != pi.svc.bv_len)
+ continue;
+ if (!strcasecmp(ni->ni_pam_sessions[i].bv_val, pi.svc.bv_val)) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ Debug(LDAP_DEBUG_TRACE,
+ "nssov_pam_sess_%c(): service(%s) not configured, ignored\n",
+ action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c',
+ pi.svc.bv_val );
+ rc = -1;
+ goto done;
+ }
+ }
+
+ bv[0].bv_len = timestamp.bv_len + global_host_bv.bv_len + pi.svc.bv_len +
+ pi.tty.bv_len + pi.ruser.bv_len + pi.rhost.bv_len + STRLENOF(" (@)");
+ bv[0].bv_val = op->o_tmpalloc( bv[0].bv_len+1, op->o_tmpmemctx );
+ sprintf(bv[0].bv_val, "%s %s %s %s (%s@%s)",
+ timestamp.bv_val, global_host_bv.bv_val, pi.svc.bv_val, pi.tty.bv_val,
+ pi.ruser.bv_val, pi.rhost.bv_val);
+
+ Debug(LDAP_DEBUG_TRACE, "nssov_pam_sess_%c(): loginStatus (%s) \n",
+ action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c', bv[0].bv_val );
+
+ mod.sml_numvals = 1;
+ mod.sml_values = bv;
+ BER_BVZERO(&bv[1]);
+ attr_normalize( ad_loginStatus, bv, &nbv, op->o_tmpmemctx );
+ mod.sml_nvalues = nbv;
+ mod.sml_desc = ad_loginStatus;
+ mod.sml_op = action == NSLCD_ACTION_PAM_SESS_O ? LDAP_MOD_ADD :
+ LDAP_MOD_DELETE;
+ mod.sml_flags = SLAP_MOD_INTERNAL;
+ mod.sml_next = NULL;
+
+ cb.sc_response = slap_null_cb;
+ op->o_callback = &cb;
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ op->orm_modlist = &mod;
+ op->orm_no_opattrs = 1;
+ op->o_req_dn = pi.dn;
+ op->o_req_ndn = pi.dn;
+ if (op->o_bd->be_modify( op, &rs ) != LDAP_SUCCESS) {
+ Debug(LDAP_DEBUG_TRACE,
+ "nssov_pam_sess_%c(): modify op failed\n",
+ action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c' );
+ rc = -1;
+ }
+
+ if ( mod.sml_next ) {
+ slap_mods_free( mod.sml_next, 1 );
+ }
+ ber_bvarray_free_x( nbv, op->o_tmpmemctx );
+
+done:;
+
+ if (rc == 0) {
+ Debug(LDAP_DEBUG_TRACE,
+ "nssov_pam_sess_%c(): success\n",
+ action==NSLCD_ACTION_PAM_SESS_O ? 'o' : 'c' );
+ }
+ WRITE_INT32(fp,NSLCD_VERSION);
+ WRITE_INT32(fp,action);
+ WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
+ if (action==NSLCD_ACTION_PAM_SESS_O)
+ WRITE_STRING(fp,timestamp.bv_val);
+ WRITE_INT32(fp,NSLCD_RESULT_END);
+ return 0;
+}
+
+int pam_sess_o(nssov_info *ni,TFILE *fp,Operation *op)
+{
+ return pam_sess(ni,fp,op,NSLCD_ACTION_PAM_SESS_O);
+}
+
+int pam_sess_c(nssov_info *ni,TFILE *fp,Operation *op)
+{
+ return pam_sess(ni,fp,op,NSLCD_ACTION_PAM_SESS_C);
+}
+
+int pam_pwmod(nssov_info *ni,TFILE *fp,Operation *op,uid_t calleruid)
+{
+ struct berval npw;
+ int32_t tmpint32;
+ char uidc[32];
+ char svcc[256];
+ char ruserc[32];
+ char rhostc[256];
+ char ttyc[256];
+ int asroot;
+ char opwc[256];
+ char npwc[256];
+ struct paminfo pi;
+ int rc;
+
+ READ_STRING(fp,uidc);
+ pi.uid.bv_val = uidc;
+ pi.uid.bv_len = tmpint32;
+ READ_STRING(fp,svcc);
+ pi.svc.bv_val = svcc;
+ pi.svc.bv_len = tmpint32;
+ READ_STRING(fp,ruserc);
+ pi.ruser.bv_val = svcc;
+ pi.ruser.bv_len = tmpint32;
+ READ_STRING(fp,rhostc);
+ pi.rhost.bv_val = svcc;
+ pi.rhost.bv_len = tmpint32;
+ READ_STRING(fp,ttyc);
+ pi.tty.bv_val = svcc;
+ pi.tty.bv_len = tmpint32;
+ READ_INT32(fp, asroot);
+ READ_STRING(fp,opwc);
+ pi.pwd.bv_val = opwc;
+ pi.pwd.bv_len = tmpint32;
+ READ_STRING(fp,npwc);
+ npw.bv_val = npwc;
+ npw.bv_len = tmpint32;
+
+ rc = pam_uid2dn(ni, op, &pi);
+ if (rc) goto done;
+
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(%s), %s %s\n",
+ pi.dn.bv_val ? pi.dn.bv_val : "NULL",
+ pi.uid.bv_val ? pi.uid.bv_val : "NULL",
+ asroot ? "as root" : "as user");
+
+ BER_BVZERO(&pi.msg);
+ pi.ispwdmgr = 0;
+
+ /* nssov_pam prohibits password mod */
+ if (!BER_BVISEMPTY(&ni->ni_pam_password_prohibit_message)) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(): %s (%s)\n",
+ "password_prohibit_message",
+ ni->ni_pam_password_prohibit_message.bv_val );
+ ber_str2bv(ni->ni_pam_password_prohibit_message.bv_val, 0, 0, &pi.msg);
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto done;
+ }
+
+ if (asroot) {
+ if (BER_BVISEMPTY(&ni->ni_pam_pwdmgr_dn)) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(), %s\n",
+ "pwdmgr not configured" );
+ ber_str2bv("pwdmgr not configured", 0, 0, &pi.msg);
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto done;
+ }
+ if (calleruid != 0) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(): %s\n",
+ "caller is not root" );
+ ber_str2bv("only root may do that", 0, 0, &pi.msg);
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto done;
+ }
+ /* root user requesting pwmod */
+ pi.ispwdmgr = 1;
+ }
+
+ if (!pi.ispwdmgr && BER_BVISEMPTY(&pi.pwd)) {
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(), %s\n",
+ "not pwdmgr and old pwd empty" );
+ ber_str2bv("must provide old password", 0, 0, &pi.msg);
+ rc = NSLCD_PAM_PERM_DENIED;
+ goto done;
+ }
+
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ struct berval bv;
+ SlapReply rs = {REP_RESULT};
+ slap_callback cb = {0};
+
+ ber_init_w_nullc(ber, LBER_USE_DER);
+ ber_printf(ber, "{");
+ if (!BER_BVISEMPTY(&pi.dn))
+ ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_ID,
+ &pi.dn);
+ /* supply old pwd whenever it's given */
+ if (!BER_BVISEMPTY(&pi.pwd))
+ ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_OLD,
+ &pi.pwd);
+ if (!BER_BVISEMPTY(&npw))
+ ber_printf(ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_NEW,
+ &npw);
+ ber_printf(ber, "N}");
+ ber_flatten2(ber, &bv, 0);
+ op->o_tag = LDAP_REQ_EXTENDED;
+ op->ore_reqoid = slap_EXOP_MODIFY_PASSWD;
+ op->ore_reqdata = &bv;
+
+ if (pi.ispwdmgr) {
+ /* root user changing end-user passwords */
+ op->o_dn = ni->ni_pam_pwdmgr_dn;
+ op->o_ndn = ni->ni_pam_pwdmgr_dn;
+ } else {
+ /* end-user self-pwd-mod */
+ op->o_dn = pi.dn;
+ op->o_ndn = pi.dn;
+ }
+ op->o_callback = &cb;
+ op->o_conn->c_authz_backend = op->o_bd;
+ cb.sc_response = slap_null_cb;
+ op->o_bd = frontendDB;
+ rc = op->o_bd->be_extended(op, &rs);
+ if (rs.sr_text)
+ ber_str2bv(rs.sr_text, 0, 0, &pi.msg);
+ if (rc == LDAP_SUCCESS)
+ rc = NSLCD_PAM_SUCCESS;
+ else
+ rc = NSLCD_PAM_PERM_DENIED;
+
+done:;
+ Debug(LDAP_DEBUG_TRACE,"nssov_pam_pwmod(), rc (%d)\n", rc );
+ WRITE_INT32(fp,NSLCD_VERSION);
+ WRITE_INT32(fp,NSLCD_ACTION_PAM_PWMOD);
+ WRITE_INT32(fp,NSLCD_RESULT_BEGIN);
+ WRITE_INT32(fp,rc);
+ WRITE_BERVAL(fp,&pi.msg);
+ return 0;
+}
+
+int nssov_pam_init()
+{
+ int code = 0;
+ const char *text;
+ if (!ad_loginStatus)
+ code = slap_str2ad("loginStatus", &ad_loginStatus, &text);
+
+ return code;
+}
diff --git a/contrib/slapd-modules/nssov/passwd.c b/contrib/slapd-modules/nssov/passwd.c
new file mode 100644
index 0000000..137106d
--- /dev/null
+++ b/contrib/slapd-modules/nssov/passwd.c
@@ -0,0 +1,435 @@
+/* passwd.c - password lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+/* ( nisSchema.2.0 NAME 'posixAccount' SUP top AUXILIARY
+ * DESC 'Abstraction of an account with POSIX attributes'
+ * MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
+ * MAY ( userPassword $ loginShell $ gecos $ description ) )
+ */
+
+/* the basic search filter for searches */
+static struct berval passwd_filter = BER_BVC("(objectClass=posixAccount)");
+
+/* the attributes used in searches */
+static struct berval passwd_keys[] = {
+ BER_BVC("uid"),
+ BER_BVC("userPassword"),
+ BER_BVC("uidNumber"),
+ BER_BVC("gidNumber"),
+ BER_BVC("gecos"),
+ BER_BVC("cn"),
+ BER_BVC("homeDirectory"),
+ BER_BVC("loginShell"),
+ BER_BVC("objectClass"),
+ BER_BVNULL
+};
+
+#define UID_KEY 0
+#define PWD_KEY 1
+#define UIDN_KEY 2
+#define GIDN_KEY 3
+#define GEC_KEY 4
+#define CN_KEY 5
+#define DIR_KEY 6
+#define SHL_KEY 7
+
+/* default values for attributes */
+static struct berval default_passwd_userPassword = BER_BVC("*"); /* unmatchable */
+static struct berval default_passwd_homeDirectory = BER_BVC("");
+static struct berval default_passwd_loginShell = BER_BVC("");
+
+static struct berval shadow_passwd = BER_BVC("x");
+
+NSSOV_INIT(passwd)
+
+/*
+ Checks to see if the specified name is a valid user name.
+
+ This test is based on the definition from POSIX (IEEE Std 1003.1, 2004, 3.426 User Name
+ and 3.276 Portable Filename Character Set):
+ http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_426
+ http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_276
+
+ The standard defines user names valid if they contain characters from
+ the set [A-Za-z0-9._-] where the hyphen should not be used as first
+ character. As an extension this test allows the dolar '$' sign as the last
+ character to support Samba special accounts.
+*/
+int isvalidusername(struct berval *bv)
+{
+ int i;
+ char *name = bv->bv_val;
+ if ((name==NULL)||(name[0]=='\0'))
+ return 0;
+ /* check first character */
+ if ( ! ( (name[0]>='A' && name[0] <= 'Z') ||
+ (name[0]>='a' && name[0] <= 'z') ||
+ (name[0]>='0' && name[0] <= '9') ||
+ name[0]=='.' || name[0]=='_' ) )
+ return 0;
+ /* check other characters */
+ for (i=1;i<bv->bv_len;i++)
+ {
+ if ( name[i]=='$' )
+ {
+ /* if the char is $ we require it to be the last char */
+ if (name[i+1]!='\0')
+ return 0;
+ }
+ else if ( ! ( (name[i]>='A' && name[i] <= 'Z') ||
+ (name[i]>='a' && name[i] <= 'z') ||
+ (name[i]>='0' && name[i] <= '9') ||
+ name[i]=='.' || name[i]=='_' || name[i]=='-') )
+ return 0;
+ }
+ /* no test failed so it must be good */
+ return -1;
+}
+
+/* return 1 on success */
+int nssov_dn2uid(Operation *op,nssov_info *ni,struct berval *dn,struct berval *uid)
+{
+ nssov_mapinfo *mi = &ni->ni_maps[NM_passwd];
+ AttributeDescription *ad = mi->mi_attrs[UID_KEY].an_desc;
+ Entry *e;
+
+ /* check for empty string */
+ if (!dn->bv_len)
+ return 0;
+ /* try to look up uid within DN string */
+ if (!strncmp(dn->bv_val,ad->ad_cname.bv_val,ad->ad_cname.bv_len) &&
+ dn->bv_val[ad->ad_cname.bv_len] == '=')
+ {
+ struct berval bv, rdn;
+ dnRdn(dn, &rdn);
+ /* check if it is valid */
+ bv.bv_val = dn->bv_val + ad->ad_cname.bv_len + 1;
+ bv.bv_len = rdn.bv_len - ad->ad_cname.bv_len - 1;
+ if (!isvalidusername(&bv))
+ return 0;
+ ber_dupbv_x( uid, &bv, op->o_tmpmemctx );
+ return 1;
+ }
+ /* look up the uid from the entry itself */
+ if (be_entry_get_rw( op, dn, NULL, ad, 0, &e) == LDAP_SUCCESS)
+ {
+ Attribute *a = attr_find(e->e_attrs, ad);
+ if (a) {
+ ber_dupbv_x(uid, &a->a_vals[0], op->o_tmpmemctx);
+ }
+ be_entry_release_r(op, e);
+ if (a)
+ return 1;
+ }
+ return 0;
+}
+
+int nssov_name2dn_cb(Operation *op,SlapReply *rs)
+{
+ if ( rs->sr_type == REP_SEARCH )
+ {
+ struct berval *bv = op->o_callback->sc_private;
+ if ( !BER_BVISNULL(bv)) {
+ op->o_tmpfree( bv->bv_val, op->o_tmpmemctx );
+ BER_BVZERO(bv);
+ return LDAP_ALREADY_EXISTS;
+ }
+ ber_dupbv_x(bv, &rs->sr_entry->e_name, op->o_tmpmemctx);
+ }
+ return LDAP_SUCCESS;
+}
+
+int nssov_uid2dn(Operation *op,nssov_info *ni,struct berval *uid,struct berval *dn)
+{
+ nssov_mapinfo *mi = &ni->ni_maps[NM_passwd];
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf),fbuf};
+ slap_callback cb = {0};
+ SlapReply rs = {REP_RESULT};
+ Operation op2;
+ int rc;
+
+ /* if it isn't a valid username, just bail out now */
+ if (!isvalidusername(uid))
+ return 0;
+ /* we have to look up the entry */
+ nssov_filter_byid(mi,UID_KEY,uid,&filter);
+ BER_BVZERO(dn);
+ cb.sc_private = dn;
+ cb.sc_response = nssov_name2dn_cb;
+ op2 = *op;
+ op2.o_callback = &cb;
+ op2.o_req_dn = mi->mi_base;
+ op2.o_req_ndn = mi->mi_base;
+ op2.ors_scope = mi->mi_scope;
+ op2.ors_filterstr = filter;
+ op2.ors_filter = str2filter_x( op, filter.bv_val );
+ op2.ors_attrs = slap_anlist_no_attrs;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+ op2.ors_slimit = SLAP_NO_LIMIT;
+ rc = op2.o_bd->be_search( &op2, &rs );
+ filter_free_x( op, op2.ors_filter, 1 );
+ return rc == LDAP_SUCCESS && !BER_BVISNULL(dn);
+}
+
+/* the maximum number of uidNumber attributes per entry */
+#define MAXUIDS_PER_ENTRY 5
+
+NSSOV_CBPRIV(passwd,
+ char buf[256];
+ struct berval name;
+ struct berval id;);
+
+static struct berval shadowclass = BER_BVC("shadowAccount");
+
+static int write_passwd(nssov_passwd_cbp *cbp,Entry *entry)
+{
+ int32_t tmpint32;
+ struct berval tmparr[2], tmpuid[2];
+ char *tmp;
+ struct berval *names;
+ struct berval *uids;
+ struct berval passwd = {0};
+ gid_t gid;
+ struct berval gecos;
+ struct berval homedir;
+ struct berval shell;
+ Attribute *a;
+ int i,j;
+ int use_shadow = 0;
+ /* get the usernames for this entry */
+ if (BER_BVISNULL(&cbp->name))
+ {
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UID_KEY].an_desc);
+ if (!a)
+ {
+ Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ names = a->a_vals;
+ }
+ else
+ {
+ names=tmparr;
+ names[0]=cbp->name;
+ BER_BVZERO(&names[1]);
+ }
+ /* get the password for this entry */
+ a = attr_find(entry->e_attrs, slap_schema.si_ad_objectClass);
+ if ( a ) {
+ for ( i=0; i<a->a_numvals; i++) {
+ if ( bvmatch( &shadowclass, &a->a_nvals[i] )) {
+ use_shadow = 1;
+ break;
+ }
+ }
+ }
+ if ( use_shadow )
+ {
+ /* if the entry has a shadowAccount entry, point to that instead */
+ passwd = shadow_passwd;
+ }
+ else
+ {
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[PWD_KEY].an_desc);
+ if (a)
+ get_userpassword(&a->a_vals[0], &passwd);
+ if (BER_BVISNULL(&passwd))
+ passwd=default_passwd_userPassword;
+ }
+ /* get the uids for this entry */
+ if (BER_BVISNULL(&cbp->id))
+ {
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UIDN_KEY].an_desc);
+ if ( !a )
+ {
+ Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ uids = a->a_vals;
+ }
+ else
+ {
+ uids = tmpuid;
+ uids[0] = cbp->id;
+ BER_BVZERO(&uids[1]);
+ }
+ /* get the gid for this entry */
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GIDN_KEY].an_desc);
+ if (!a)
+ {
+ Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ else if (a->a_numvals != 1)
+ {
+ Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val );
+ }
+ gid=(gid_t)strtol(a->a_vals[0].bv_val,&tmp,0);
+ if ((a->a_vals[0].bv_val[0]=='\0')||(*tmp!='\0'))
+ {
+ Debug(LDAP_DEBUG_ANY,"passwd entry %s contains non-numeric %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[GIDN_KEY].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ /* get the gecos for this entry (fall back to cn) */
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[GEC_KEY].an_desc);
+ if (!a)
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[CN_KEY].an_desc);
+ if (!a || !a->a_numvals)
+ {
+ Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s or %s value\n",
+ entry->e_name.bv_val,
+ cbp->mi->mi_attrs[GEC_KEY].an_desc->ad_cname.bv_val,
+ cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val);
+ return 0;
+ }
+ else if (a->a_numvals > 1)
+ {
+ Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s or %s values\n",
+ entry->e_name.bv_val,
+ cbp->mi->mi_attrs[GEC_KEY].an_desc->ad_cname.bv_val,
+ cbp->mi->mi_attrs[CN_KEY].an_desc->ad_cname.bv_val);
+ }
+ gecos=a->a_vals[0];
+ /* get the home directory for this entry */
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[DIR_KEY].an_desc);
+ if (!a)
+ {
+ Debug(LDAP_DEBUG_ANY,"passwd entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[DIR_KEY].an_desc->ad_cname.bv_val );
+ homedir=default_passwd_homeDirectory;
+ }
+ else
+ {
+ if (a->a_numvals > 1)
+ {
+ Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[DIR_KEY].an_desc->ad_cname.bv_val );
+ }
+ homedir=a->a_vals[0];
+ if (homedir.bv_val[0]=='\0')
+ homedir=default_passwd_homeDirectory;
+ }
+ /* get the shell for this entry */
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[SHL_KEY].an_desc);
+ if (!a)
+ {
+ shell=default_passwd_loginShell;
+ }
+ else
+ {
+ if (a->a_numvals > 1)
+ {
+ Debug(LDAP_DEBUG_ANY,"passwd entry %s contains multiple %s values\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[SHL_KEY].an_desc->ad_cname.bv_val );
+ }
+ shell=a->a_vals[0];
+ if (shell.bv_val[0]=='\0')
+ shell=default_passwd_loginShell;
+ }
+ /* write the entries */
+ for (i=0;!BER_BVISNULL(&names[i]);i++)
+ {
+ if (!isvalidusername(&names[i]))
+ {
+ Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains invalid user name: \"%s\"\n",
+ entry->e_name.bv_val,names[i].bv_val );
+ }
+ else
+ {
+ for (j=0;!BER_BVISNULL(&uids[j]);j++)
+ {
+ char *tmp;
+ uid_t uid;
+ uid = strtol(uids[j].bv_val, &tmp, 0);
+ if ( *tmp ) {
+ Debug(LDAP_DEBUG_ANY,"nssov: passwd entry %s contains non-numeric %s value: \"%s\"\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[UIDN_KEY].an_desc->ad_cname.bv_val,
+ names[i].bv_val);
+ continue;
+ }
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ WRITE_BERVAL(cbp->fp,&names[i]);
+ WRITE_BERVAL(cbp->fp,&passwd);
+ WRITE_INT32(cbp->fp,uid);
+ WRITE_INT32(cbp->fp,gid);
+ WRITE_BERVAL(cbp->fp,&gecos);
+ WRITE_BERVAL(cbp->fp,&homedir);
+ WRITE_BERVAL(cbp->fp,&shell);
+ }
+ }
+ }
+ return 0;
+}
+
+NSSOV_CB(passwd)
+
+NSSOV_HANDLE(
+ passwd,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_STRING(fp,cbp.buf);
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.buf;
+ if (!isvalidusername(&cbp.name)) {
+ Debug(LDAP_DEBUG_ANY,"nssov_passwd_byname(%s): invalid user name\n",cbp.name.bv_val);
+ return -1;
+ }
+ BER_BVZERO(&cbp.id); ,
+ Debug(LDAP_DEBUG_TRACE,"nssov_passwd_byname(%s)\n",cbp.name.bv_val);,
+ NSLCD_ACTION_PASSWD_BYNAME,
+ nssov_filter_byname(cbp.mi,UID_KEY,&cbp.name,&filter)
+)
+
+NSSOV_HANDLE(
+ passwd,byuid,
+ uid_t uid;
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_INT32(fp,uid);
+ cbp.id.bv_val = cbp.buf;
+ cbp.id.bv_len = snprintf(cbp.buf,sizeof(cbp.buf),"%d",uid);
+ BER_BVZERO(&cbp.name);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_passwd_byuid(%s)\n",cbp.id.bv_val);,
+ NSLCD_ACTION_PASSWD_BYUID,
+ nssov_filter_byid(cbp.mi,UIDN_KEY,&cbp.id,&filter)
+)
+
+NSSOV_HANDLE(
+ passwd,all,
+ struct berval filter;
+ /* no parameters to read */
+ BER_BVZERO(&cbp.name);
+ BER_BVZERO(&cbp.id);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_passwd_all()\n");,
+ NSLCD_ACTION_PASSWD_ALL,
+ (filter=cbp.mi->mi_filter,0)
+)
diff --git a/contrib/slapd-modules/nssov/protocol.c b/contrib/slapd-modules/nssov/protocol.c
new file mode 100644
index 0000000..1d131ca
--- /dev/null
+++ b/contrib/slapd-modules/nssov/protocol.c
@@ -0,0 +1,156 @@
+/* protocol.c - network protocol lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/*
+ * ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+/* ( nisSchema.2.4 NAME 'ipProtocol' SUP top STRUCTURAL
+ * DESC 'Abstraction of an IP protocol. Maps a protocol number
+ * to one or more names. The distinguished value of the cn
+ * attribute denotes the protocol's canonical name'
+ * MUST ( cn $ ipProtocolNumber )
+ * MAY description )
+ */
+
+/* the basic search filter for searches */
+static struct berval protocol_filter = BER_BVC("(objectClass=ipProtocol)");
+
+/* the attributes used in searches */
+static struct berval protocol_keys[] = {
+ BER_BVC("cn"),
+ BER_BVC("ipProtocolNumber"),
+ BER_BVNULL
+};
+
+NSSOV_INIT(protocol)
+
+NSSOV_CBPRIV(protocol,
+ char buf[256];
+ struct berval name;
+ struct berval numb;);
+
+static int write_protocol(nssov_protocol_cbp *cbp,Entry *entry)
+{
+ int32_t tmpint32;
+ int i,numname,dupname,proto;
+ struct berval name,*names;
+ Attribute *a;
+ char *tmp;
+
+ /* get the most canonical name */
+ nssov_find_rdnval( &entry->e_nname, cbp->mi->mi_attrs[0].an_desc, &name );
+ /* get the other names for the protocol */
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[0].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"protocol entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ names = a->a_vals;
+ numname = a->a_numvals;
+ /* if the name is not yet found, get the first entry from names */
+ if (BER_BVISNULL(&name)) {
+ name=names[0];
+ dupname = 0;
+ } else {
+ dupname = -1;
+ for (i=0; i<numname; i++) {
+ if ( bvmatch(&name, &a->a_nvals[i])) {
+ dupname = i;
+ break;
+ }
+ }
+ }
+ /* get the protocol number */
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[1].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"protocol entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ return 0;
+ } else if ( a->a_numvals > 1 ) {
+ Debug(LDAP_DEBUG_ANY,"protocol entry %s contains multiple %s values\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ }
+ proto=(int)strtol(a->a_vals[0].bv_val,&tmp,0);
+ if (*tmp)
+ {
+ Debug(LDAP_DEBUG_ANY,"protocol entry %s contains non-numeric %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ /* write the entry */
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ WRITE_BERVAL(cbp->fp,&name);
+ if ( dupname >= 0 ) {
+ WRITE_INT32(cbp->fp,numname-1);
+ } else {
+ WRITE_INT32(cbp->fp,numname);
+ }
+ for (i=0;i<numname;i++) {
+ if (i == dupname) continue;
+ WRITE_BERVAL(cbp->fp,&names[i]);
+ }
+ WRITE_INT32(cbp->fp,proto);
+ return 0;
+}
+
+NSSOV_CB(protocol)
+
+NSSOV_HANDLE(
+ protocol,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ BER_BVZERO(&cbp.numb);
+ READ_STRING(fp,cbp.buf);
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.buf;,
+ Debug(LDAP_DEBUG_TRACE,"nssov_protocol_byname(%s)\n",cbp.name.bv_val);,
+ NSLCD_ACTION_PROTOCOL_BYNAME,
+ nssov_filter_byname(cbp.mi,0,&cbp.name,&filter)
+)
+
+NSSOV_HANDLE(
+ protocol,bynumber,
+ int protocol;
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_INT32(fp,protocol);
+ cbp.numb.bv_val = cbp.buf;
+ cbp.numb.bv_len = snprintf(cbp.buf,sizeof(cbp.buf),"%d",protocol);
+ BER_BVZERO(&cbp.name);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_protocol_bynumber(%s)\n",cbp.numb.bv_val);,
+ NSLCD_ACTION_PROTOCOL_BYNUMBER,
+ nssov_filter_byid(cbp.mi,1,&cbp.numb,&filter)
+)
+
+NSSOV_HANDLE(
+ protocol,all,
+ struct berval filter;
+ /* no parameters to read */,
+ Debug(LDAP_DEBUG_TRACE,"nssov_protocol_all()\n");,
+ NSLCD_ACTION_PROTOCOL_ALL,
+ (filter=cbp.mi->mi_filter,0)
+)
diff --git a/contrib/slapd-modules/nssov/rpc.c b/contrib/slapd-modules/nssov/rpc.c
new file mode 100644
index 0000000..7d2045f
--- /dev/null
+++ b/contrib/slapd-modules/nssov/rpc.c
@@ -0,0 +1,158 @@
+/* rpc.c - rpc lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+/* ( nisSchema.2.5 NAME 'oncRpc' SUP top STRUCTURAL
+ * DESC 'Abstraction of an Open Network Computing (ONC)
+ * [RFC1057] Remote Procedure Call (RPC) binding.
+ * This class maps an ONC RPC number to a name.
+ * The distinguished value of the cn attribute denotes
+ * the RPC service's canonical name'
+ * MUST ( cn $ oncRpcNumber )
+ * MAY description )
+ */
+
+/* the basic search filter for searches */
+static struct berval rpc_filter = BER_BVC("(objectClass=oncRpc)");
+
+/* the attributes to request with searches */
+static struct berval rpc_keys[] = {
+ BER_BVC("cn"),
+ BER_BVC("oncRpcNumber"),
+ BER_BVNULL
+};
+
+NSSOV_INIT(rpc)
+
+NSSOV_CBPRIV(rpc,
+ char buf[256];
+ struct berval name;
+ struct berval numb;);
+
+/* write a single rpc entry to the stream */
+static int write_rpc(nssov_rpc_cbp *cbp,Entry *entry)
+{
+ int32_t tmpint32;
+ int i,numname,dupname,number;
+ struct berval name,*names;
+ Attribute *a;
+ char *tmp;
+
+ /* get the most canonical name */
+ nssov_find_rdnval( &entry->e_nname, cbp->mi->mi_attrs[0].an_desc, &name );
+ /* get the other names for the rpc */
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[0].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"rpc entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ names = a->a_vals;
+ numname = a->a_numvals;
+ /* if the name is not yet found, get the first entry from names */
+ if (BER_BVISNULL(&name)) {
+ name=names[0];
+ dupname = 0;
+ } else {
+ dupname = -1;
+ for (i=0; i<numname; i++) {
+ if ( bvmatch(&name, &a->a_nvals[i])) {
+ dupname = i;
+ break;
+ }
+ }
+ }
+ /* get the rpc number */
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[1].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"rpc entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ return 0;
+ } else if ( a->a_numvals > 1 ) {
+ Debug(LDAP_DEBUG_ANY,"rpc entry %s contains multiple %s values\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ }
+ number=(int)strtol(a->a_vals[0].bv_val,&tmp,0);
+ if (*tmp)
+ {
+ Debug(LDAP_DEBUG_ANY,"rpc entry %s contains non-numeric %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ /* write the entry */
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ WRITE_BERVAL(cbp->fp,&name);
+ if ( dupname >= 0 ) {
+ WRITE_INT32(cbp->fp,numname-1);
+ } else {
+ WRITE_INT32(cbp->fp,numname);
+ }
+ for (i=0;i<numname;i++) {
+ if (i == dupname) continue;
+ WRITE_BERVAL(cbp->fp,&names[i]);
+ }
+ WRITE_INT32(cbp->fp,number);
+ return 0;
+}
+
+NSSOV_CB(rpc)
+
+NSSOV_HANDLE(
+ rpc,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ BER_BVZERO(&cbp.numb);
+ READ_STRING(fp,cbp.buf);
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.buf;,
+ Debug(LDAP_DEBUG_TRACE,"nssov_rpc_byname(%s)\n",cbp.name.bv_val);,
+ NSLCD_ACTION_RPC_BYNAME,
+ nssov_filter_byname(cbp.mi,0,&cbp.name,&filter)
+)
+
+NSSOV_HANDLE(
+ rpc,bynumber,
+ int number;
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_INT32(fp,number);
+ cbp.numb.bv_val = cbp.buf;
+ cbp.numb.bv_len = snprintf(cbp.buf,sizeof(cbp.buf),"%d",number);
+ BER_BVZERO(&cbp.name);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_rpc_bynumber(%s)\n",cbp.numb.bv_val);,
+ NSLCD_ACTION_RPC_BYNUMBER,
+ nssov_filter_byid(cbp.mi,1,&cbp.numb,&filter)
+)
+
+NSSOV_HANDLE(
+ rpc,all,
+ struct berval filter;
+ /* no parameters to read */,
+ Debug(LDAP_DEBUG_TRACE,"nssov_rpc_all()\n");,
+ NSLCD_ACTION_RPC_ALL,
+ (filter=cbp.mi->mi_filter,0)
+)
diff --git a/contrib/slapd-modules/nssov/service.c b/contrib/slapd-modules/nssov/service.c
new file mode 100644
index 0000000..d3704e4
--- /dev/null
+++ b/contrib/slapd-modules/nssov/service.c
@@ -0,0 +1,250 @@
+/* service.c - service lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+/* ( nisSchema.2.3 NAME 'ipService' SUP top STRUCTURAL
+ * DESC 'Abstraction an Internet Protocol service.
+ * Maps an IP port and protocol (such as tcp or udp)
+ * to one or more names; the distinguished value of
+ * the cn attribute denotes the service's canonical
+ * name'
+ * MUST ( cn $ ipServicePort $ ipServiceProtocol )
+ * MAY ( description ) )
+ */
+
+/* the basic search filter for searches */
+static struct berval service_filter = BER_BVC("(objectClass=ipService)");
+
+/* the attributes to request with searches */
+static struct berval service_keys[] = {
+ BER_BVC("cn"),
+ BER_BVC("ipServicePort"),
+ BER_BVC("ipServiceProtocol"),
+ BER_BVNULL
+};
+
+static int mkfilter_service_byname(nssov_mapinfo *mi,struct berval *name,
+ struct berval *protocol,struct berval *buf)
+{
+ char buf2[1024],buf3[1024];
+ struct berval bv2 = {sizeof(buf2),buf2};
+ struct berval bv3 = {sizeof(buf3),buf3};
+
+ /* escape attributes */
+ if (nssov_escape(name,&bv2))
+ return -1;
+ if (!BER_BVISNULL(protocol)) {
+ if (nssov_escape(protocol,&bv3))
+ return -1;
+ if (bv2.bv_len + mi->mi_filter.bv_len + mi->mi_attrs[0].an_desc->ad_cname.bv_len +
+ bv3.bv_len + mi->mi_attrs[2].an_desc->ad_cname.bv_len + 9 > buf->bv_len )
+ return -1;
+ buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s)(%s=%s))",
+ mi->mi_filter.bv_val,
+ mi->mi_attrs[0].an_desc->ad_cname.bv_val, bv2.bv_val,
+ mi->mi_attrs[2].an_desc->ad_cname.bv_val, bv3.bv_val );
+ } else {
+ if (bv2.bv_len + mi->mi_filter.bv_len + mi->mi_attrs[0].an_desc->ad_cname.bv_len + 6 >
+ buf->bv_len )
+ return -1;
+ buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))",
+ mi->mi_filter.bv_val, mi->mi_attrs[0].an_desc->ad_cname.bv_val,
+ bv2.bv_val );
+ }
+ return 0;
+}
+
+static int mkfilter_service_bynumber(nssov_mapinfo *mi,struct berval *numb,
+ struct berval *protocol,struct berval *buf)
+{
+ char buf2[1024];
+ struct berval bv2 = {sizeof(buf2),buf2};
+
+ /* escape attribute */
+ if (!BER_BVISNULL(protocol)) {
+ if (nssov_escape(protocol,&bv2))
+ return -1;
+ if (numb->bv_len + mi->mi_filter.bv_len + mi->mi_attrs[1].an_desc->ad_cname.bv_len +
+ bv2.bv_len + mi->mi_attrs[2].an_desc->ad_cname.bv_len + 9 > buf->bv_len )
+ return -1;
+ buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s)(%s=%s))",
+ mi->mi_filter.bv_val,
+ mi->mi_attrs[1].an_desc->ad_cname.bv_val, numb->bv_val,
+ mi->mi_attrs[2].an_desc->ad_cname.bv_val, bv2.bv_val );
+ } else {
+ if (numb->bv_len + mi->mi_filter.bv_len + mi->mi_attrs[1].an_desc->ad_cname.bv_len + 6 >
+ buf->bv_len )
+ return -1;
+ buf->bv_len = snprintf(buf->bv_val, buf->bv_len, "(&%s(%s=%s))",
+ mi->mi_filter.bv_val, mi->mi_attrs[1].an_desc->ad_cname.bv_val,
+ numb->bv_val );
+ }
+ return 0;
+}
+
+NSSOV_INIT(service)
+
+NSSOV_CBPRIV(service,
+ char nbuf[256];
+ char pbuf[256];
+ struct berval name;
+ struct berval prot;);
+
+static int write_service(nssov_service_cbp *cbp,Entry *entry)
+{
+ int32_t tmpint32;
+ struct berval name,*names,*protos;
+ struct berval tmparr[2];
+ Attribute *a;
+ char *tmp;
+ int port;
+ int i,numname,dupname,numprot;
+
+ /* get the most canonical name */
+ nssov_find_rdnval( &entry->e_nname, cbp->mi->mi_attrs[0].an_desc, &name );
+ /* get the other names for the rpc */
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[0].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"service entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[0].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ names = a->a_vals;
+ numname = a->a_numvals;
+ /* if the name is not yet found, get the first entry from names */
+ if (BER_BVISNULL(&name)) {
+ name=names[0];
+ dupname = 0;
+ } else {
+ dupname = -1;
+ for (i=0; i<numname; i++) {
+ if ( bvmatch(&name, &a->a_nvals[i])) {
+ dupname = i;
+ break;
+ }
+ }
+ }
+ /* get the service number */
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[1].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"service entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ return 0;
+ } else if ( a->a_numvals > 1 ) {
+ Debug(LDAP_DEBUG_ANY,"service entry %s contains multiple %s values\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ }
+ port=(int)strtol(a->a_vals[0].bv_val,&tmp,0);
+ if (*tmp)
+ {
+ Debug(LDAP_DEBUG_ANY,"service entry %s contains non-numeric %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[1].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ /* get protocols */
+ if (BER_BVISNULL(&cbp->prot))
+ {
+ a = attr_find( entry->e_attrs, cbp->mi->mi_attrs[2].an_desc );
+ if ( !a || !a->a_vals )
+ {
+ Debug(LDAP_DEBUG_ANY,"service entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[2].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ protos = a->a_vals;
+ numprot = a->a_numvals;
+ }
+ else
+ {
+ protos=tmparr;
+ protos[0]=cbp->prot;
+ BER_BVZERO(&protos[1]);
+ numprot = 1;
+ }
+ /* write the entries */
+ for (i=0;i<numprot;i++)
+ {
+ int j;
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ WRITE_BERVAL(cbp->fp,&name);
+ if ( dupname >= 0 ) {
+ WRITE_INT32(cbp->fp,numname-1);
+ } else {
+ WRITE_INT32(cbp->fp,numname);
+ }
+ for (j=0;j<numname;j++) {
+ if (j == dupname) continue;
+ WRITE_BERVAL(cbp->fp,&names[j]);
+ }
+ WRITE_INT32(cbp->fp,port);
+ WRITE_BERVAL(cbp->fp,&protos[i]);
+ }
+ return 0;
+}
+
+NSSOV_CB(service)
+
+NSSOV_HANDLE(
+ service,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_STRING(fp,cbp.nbuf);
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.nbuf;
+ READ_STRING(fp,cbp.pbuf);
+ cbp.prot.bv_len = tmpint32;
+ cbp.prot.bv_val = tmpint32 ? cbp.pbuf : NULL;,
+ Debug(LDAP_DEBUG_TRACE,"nssov_service_byname(%s,%s)\n",cbp.name.bv_val,cbp.prot.bv_val ? cbp.prot.bv_val : "");,
+ NSLCD_ACTION_SERVICE_BYNAME,
+ mkfilter_service_byname(cbp.mi,&cbp.name,&cbp.prot,&filter)
+)
+
+NSSOV_HANDLE(
+ service,bynumber,
+ int number;
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_INT32(fp,number);
+ cbp.name.bv_val = cbp.nbuf;
+ cbp.name.bv_len = snprintf(cbp.nbuf,sizeof(cbp.nbuf),"%d",number);
+ READ_STRING(fp,cbp.pbuf);
+ cbp.prot.bv_len = tmpint32;
+ cbp.prot.bv_val = tmpint32 ? cbp.pbuf : NULL;,
+ Debug(LDAP_DEBUG_TRACE,"nssov_service_bynumber(%s,%s)\n",cbp.name.bv_val,cbp.prot.bv_val);,
+ NSLCD_ACTION_SERVICE_BYNUMBER,
+ mkfilter_service_bynumber(cbp.mi,&cbp.name,&cbp.prot,&filter)
+)
+
+NSSOV_HANDLE(
+ service,all,
+ struct berval filter;
+ /* no parameters to read */
+ BER_BVZERO(&cbp.prot);,
+ Debug(LDAP_DEBUG_TRACE,"nssov_service_all()\n");,
+ NSLCD_ACTION_SERVICE_ALL,
+ (filter=cbp.mi->mi_filter,0)
+)
diff --git a/contrib/slapd-modules/nssov/shadow.c b/contrib/slapd-modules/nssov/shadow.c
new file mode 100644
index 0000000..477ce50
--- /dev/null
+++ b/contrib/slapd-modules/nssov/shadow.c
@@ -0,0 +1,257 @@
+/* shadow.c - shadow account lookup routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This code references portions of the nss-ldapd package
+ * written by Arthur de Jong. The nss-ldapd code was forked
+ * from the nss-ldap library written by Luke Howard.
+ */
+
+#include "nssov.h"
+
+/* ( nisSchema.2.1 NAME 'shadowAccount' SUP top AUXILIARY
+ * DESC 'Additional attributes for shadow passwords'
+ * MUST uid
+ * MAY ( userPassword $ shadowLastChange $ shadowMin
+ * shadowMax $ shadowWarning $ shadowInactive $
+ * shadowExpire $ shadowFlag $ description ) )
+ */
+
+/* the basic search filter for searches */
+static struct berval shadow_filter = BER_BVC("(objectClass=shadowAccount)");
+
+/* the attributes to request with searches */
+static struct berval shadow_keys[] = {
+ BER_BVC("uid"),
+ BER_BVC("userPassword"),
+ BER_BVC("shadowLastChange"),
+ BER_BVC("shadowMin"),
+ BER_BVC("shadowMax"),
+ BER_BVC("shadowWarning"),
+ BER_BVC("shadowInactive"),
+ BER_BVC("shadowExpire"),
+ BER_BVC("shadowFlag"),
+ BER_BVNULL
+};
+
+#define UID_KEY 0
+#define PWD_KEY 1
+#define CHG_KEY 2
+#define MIN_KEY 3
+#define MAX_KEY 4
+#define WRN_KEY 5
+#define INA_KEY 6
+#define EXP_KEY 7
+#define FLG_KEY 8
+
+/* default values for attributes */
+static struct berval default_shadow_userPassword = BER_BVC("*"); /* unmatchable */
+static int default_nums[] = { 0,0,
+ -1, /* LastChange */
+ -1, /* Min */
+ -1, /* Max */
+ -1, /* Warning */
+ -1, /* Inactive */
+ -1, /* Expire */
+ 0 /* Flag */
+};
+
+NSSOV_INIT(shadow)
+
+static long to_date(struct berval *date,AttributeDescription *attr)
+{
+ long value;
+ char *tmp;
+ /* do some special handling for date values on AD */
+ if (strcasecmp(attr->ad_cname.bv_val,"pwdLastSet")==0)
+ {
+ char buffer[8];
+ size_t l;
+ /* we expect an AD 64-bit datetime value;
+ we should do date=date/864000000000-134774
+ but that causes problems on 32-bit platforms,
+ first we divide by 1000000000 by stripping the
+ last 9 digits from the string and going from there */
+ l=date->bv_len-9;
+ if (l<1 || l>(sizeof(buffer)-1))
+ return 0; /* error */
+ strncpy(buffer,date->bv_val,l);
+ buffer[l]='\0';
+ value=strtol(buffer,&tmp,0);
+ if ((buffer[0]=='\0')||(*tmp!='\0'))
+ {
+ Debug(LDAP_DEBUG_ANY,"shadow entry contains non-numeric %s value\n",
+ attr->ad_cname.bv_val );
+ return 0;
+ }
+ return value/864-134774;
+ /* note that AD does not have expiry dates but a lastchangeddate
+ and some value that needs to be added */
+ }
+ value=strtol(date->bv_val,&tmp,0);
+ if ((date->bv_val[0]=='\0')||(*tmp!='\0'))
+ {
+ Debug(LDAP_DEBUG_ANY,"shadow entry contains non-numeric %s value\n",
+ attr->ad_cname.bv_val );
+ return 0;
+ }
+ return value;
+}
+
+#ifndef UF_DONT_EXPIRE_PASSWD
+#define UF_DONT_EXPIRE_PASSWD 0x10000
+#endif
+
+#define GET_OPTIONAL_LONG(var,key) \
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[key].an_desc); \
+ if ( !a || BER_BVISNULL(&a->a_vals[0])) \
+ var = default_nums[key]; \
+ else \
+ { \
+ if (a->a_numvals > 1) \
+ { \
+ Debug(LDAP_DEBUG_ANY,"shadow entry %s contains multiple %s values\n", \
+ entry->e_name.bv_val, cbp->mi->mi_attrs[key].an_desc->ad_cname.bv_val); \
+ } \
+ var=strtol(a->a_vals[0].bv_val,&tmp,0); \
+ if ((a->a_vals[0].bv_val[0]=='\0')||(*tmp!='\0')) \
+ { \
+ Debug(LDAP_DEBUG_ANY,"shadow entry %s contains non-numeric %s value\n", \
+ entry->e_name.bv_val, cbp->mi->mi_attrs[key].an_desc->ad_cname.bv_val); \
+ return 0; \
+ } \
+ }
+
+#define GET_OPTIONAL_DATE(var,key) \
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[key].an_desc); \
+ if ( !a || BER_BVISNULL(&a->a_vals[0])) \
+ var = default_nums[key]; \
+ else \
+ { \
+ if (a->a_numvals > 1) \
+ { \
+ Debug(LDAP_DEBUG_ANY,"shadow entry %s contains multiple %s values\n", \
+ entry->e_name.bv_val, cbp->mi->mi_attrs[key].an_desc->ad_cname.bv_val); \
+ } \
+ var=to_date(&a->a_vals[0],cbp->mi->mi_attrs[key].an_desc); \
+ }
+
+NSSOV_CBPRIV(shadow,
+ char buf[256];
+ struct berval name;);
+
+static int write_shadow(nssov_shadow_cbp *cbp,Entry *entry)
+{
+ struct berval tmparr[2];
+ struct berval *names;
+ Attribute *a;
+ char *tmp;
+ struct berval passwd = {0};
+ long lastchangedate;
+ long mindays;
+ long maxdays;
+ long warndays;
+ long inactdays;
+ long expiredate;
+ unsigned long flag;
+ int i;
+ int32_t tmpint32;
+ /* get username */
+ if (BER_BVISNULL(&cbp->name))
+ {
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[UID_KEY].an_desc);
+ if (!a)
+ {
+ Debug(LDAP_DEBUG_ANY,"shadow entry %s does not contain %s value\n",
+ entry->e_name.bv_val, cbp->mi->mi_attrs[UID_KEY].an_desc->ad_cname.bv_val );
+ return 0;
+ }
+ names = a->a_vals;
+ }
+ else
+ {
+ names=tmparr;
+ names[0]=cbp->name;
+ BER_BVZERO(&names[1]);
+ }
+ /* get password */
+ a = attr_find(entry->e_attrs, cbp->mi->mi_attrs[PWD_KEY].an_desc);
+ if ( a )
+ get_userpassword(&a->a_vals[0], &passwd);
+ if (BER_BVISNULL(&passwd))
+ passwd=default_shadow_userPassword;
+ /* get lastchange date */
+ GET_OPTIONAL_DATE(lastchangedate,CHG_KEY);
+ /* get mindays */
+ GET_OPTIONAL_LONG(mindays,MIN_KEY);
+ /* get maxdays */
+ GET_OPTIONAL_LONG(maxdays,MAX_KEY);
+ /* get warndays */
+ GET_OPTIONAL_LONG(warndays,WRN_KEY);
+ /* get inactdays */
+ GET_OPTIONAL_LONG(inactdays,INA_KEY);
+ /* get expire date */
+ GET_OPTIONAL_LONG(expiredate,EXP_KEY);
+ /* get flag */
+ GET_OPTIONAL_LONG(flag,FLG_KEY);
+ /* if we're using AD handle the flag specially */
+ if (strcasecmp(cbp->mi->mi_attrs[CHG_KEY].an_desc->ad_cname.bv_val,"pwdLastSet")==0)
+ {
+ if (flag&UF_DONT_EXPIRE_PASSWD)
+ maxdays=99999;
+ flag=0;
+ }
+ /* write the entries */
+ for (i=0;!BER_BVISNULL(&names[i]);i++)
+ {
+ WRITE_INT32(cbp->fp,NSLCD_RESULT_BEGIN);
+ WRITE_BERVAL(cbp->fp,&names[i]);
+ WRITE_BERVAL(cbp->fp,&passwd);
+ WRITE_INT32(cbp->fp,lastchangedate);
+ WRITE_INT32(cbp->fp,mindays);
+ WRITE_INT32(cbp->fp,maxdays);
+ WRITE_INT32(cbp->fp,warndays);
+ WRITE_INT32(cbp->fp,inactdays);
+ WRITE_INT32(cbp->fp,expiredate);
+ WRITE_INT32(cbp->fp,flag);
+ }
+ return 0;
+}
+
+NSSOV_CB(shadow)
+
+NSSOV_HANDLE(
+ shadow,byname,
+ char fbuf[1024];
+ struct berval filter = {sizeof(fbuf)};
+ filter.bv_val = fbuf;
+ READ_STRING(fp,cbp.buf);,
+ cbp.name.bv_len = tmpint32;
+ cbp.name.bv_val = cbp.buf;
+ Debug(LDAP_DEBUG_ANY,"nssov_shadow_byname(%s)\n",cbp.name.bv_val);,
+ NSLCD_ACTION_SHADOW_BYNAME,
+ nssov_filter_byname(cbp.mi,UID_KEY,&cbp.name,&filter)
+)
+
+NSSOV_HANDLE(
+ shadow,all,
+ struct berval filter;
+ /* no parameters to read */
+ BER_BVZERO(&cbp.name);,
+ Debug(LDAP_DEBUG_ANY,"nssov_shadow_all()\n");,
+ NSLCD_ACTION_SHADOW_ALL,
+ (filter=cbp.mi->mi_filter,0)
+)
diff --git a/contrib/slapd-modules/nssov/slapo-nssov.5 b/contrib/slapd-modules/nssov/slapo-nssov.5
new file mode 100644
index 0000000..6128de9
--- /dev/null
+++ b/contrib/slapd-modules/nssov/slapo-nssov.5
@@ -0,0 +1,316 @@
+.TH SLAPO-NSSOV 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation, All Rights Reserved.
+.\" Copying restrictions apply. See the COPYRIGHT file.
+.\" $OpenLDAP$
+.SH NAME
+slapo-nssov \- NSS and PAM requests through a local Unix Domain socket
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B nssov
+overlay to
+.BR slapd (8)
+services NSS and PAM requests through a local Unix Domain socket.
+It uses the same IPC protocol as Arthur de Jong's nss-pam-ldapd.
+An extract of the nss-ldapd source is included along with the
+nssov source code to allow the overlay to communicate with the
+nss-pam-ldapd client stubs.
+.LP
+Using a separate IPC protocol for NSS and PAM requests eliminates the
+libldap dependencies/clashes that the current pam_ldap/nss_ldap solutions
+all suffer from. Both the original nss-ldapd and this nssov solution
+are free from these library issues.
+.LP
+Unlike nss-pam-ldapd, since this overlay executes inside slapd it allows for
+the possibility of sophisticated caching, without any of the weaknesses of
+nscd and other related caching solutions. E.g., a remote LDAP database can
+be accessed using back-ldap with proxy caching (see
+.BR slapd-ldap (5)
+and
+.BR slapo-pcache (5)
+) to leverage back-ldap's
+connection pooling as well as pcache's persistent caching, to provide
+high performance and a measure of support for disconnected operation.
+Alternatively, cache considerations can be completely eliminated by running
+a regular database with syncrepl to maintain synchronization with a remote
+LDAP database.
+.LP
+Another major benefit of nssov is that it allows all security policy to be
+administered centrally via LDAP, instead of having fragile rules scattered
+across multiple flat files. As such, there is no client-side configuration at
+all for the NSS/PAM stub libraries. (The stubs talk to the server via a Unix
+domain socket whose path is hardcoded to NSLCDPATH). As a side benefit,
+this can finally eliminate the perpetual confusion between OpenLDAP's
+ldap.conf file in ETCDIR/ldap.conf and the similarly named files typically
+used by pam_ldap and nss_ldap.
+.LP
+User authentication is performed by internal simple Binds. User authorization
+leverages the slapd ACL engine, which offers much more power and flexibility
+than the simple group/hostname checks in the old pam_ldap code.
+.LP
+To use this code, you will need the client-side stub library from
+nss-pam-ldapd. You can get it from:
+http://arthurdejong.org/nss-pam-ldapd
+You will not need the nslcd daemon; this overlay replaces that part.
+To disable building of the nslcd daemon in nss-pam-ldapd, add the
+--disable-nslcd option to the nss-pam-ldapd configure script. You
+should already be familiar with the RFC2307 and RFC2307bis schema
+to use this overlay. See the nss-pam-ldapd README for more information
+on the schema and which features are supported.
+.LP
+You will also need to include the nis.schema in your slapd configuration
+for RFC2307 support. If you wish to use RFC2307bis you will need a slightly
+different schema. You will also need the ldapns.schema for PAM authorization
+management.
+.LP
+You must select
+.B ldap
+in the appropriate services in
+.I /etc/nsswitch.conf
+in order for these NSS features to take effect. Likewise, you must
+enable
+.B pam_ldap
+for the authenticate, account, session, and password services in
+.I /etc/pam.conf
+or
+.I /etc/pam.d
+for these PAM features to take effect.
+
+.TP
+.B overlay nssov
+This directive adds the nssov overlay to the current backend.
+.TP
+.B nssov-ssd <service> <url>
+This directive configures a Service Search Descriptor (SSD) for each NSS
+service that will be used. The <service> may be one of
+.RS
+.nf
+ aliases
+ ethers
+ group
+ hosts
+ netgroup
+ networks
+ passwd
+ protocols
+ rpc
+ services
+ shadow
+.fi
+.RE
+and the <url> must be of the form
+.RS
+.TP
+.B ldap:///[<basedn>][??[<scope>][?<filter>]]
+.RE
+The
+.B <basedn>
+will default to the first suffix of the current database.
+The
+.B <scope>
+defaults to "subtree". The default
+.B <filter>
+depends on which service is being used.
+.TP
+.B nssov-map <service> <orig> <new>
+If the local database is actually a proxy to a foreign LDAP server, some
+mapping of schema may be needed. This directive allows some simple attribute
+substitutions to be performed. See the
+.B nss-ldapd/README
+for the original attribute names used in this code.
+.TP
+.B nssov-pam <option> [...]
+This directive determines a number of PAM behaviors. Multiple options may
+be used at once, and available levels are:
+.RS
+.RS
+.PD 0
+.TP
+.B userhost
+check host attribute in user entry for authorization
+.TP
+.B userservice
+check authorizedService attribute in user entry for authorization
+.TP
+.B usergroup
+check that user is a member of specific group for authorization
+.TP
+.B hostservice
+check authorizedService attribute in host entry for authorization
+.TP
+.B authz2dn
+use authz-regexp mapping to map uid to LDAP DN
+.TP
+.B uid2dn
+use NSS passwd SSD to map uid to LDAP DN
+.PD
+.RE
+
+Setting the
+.BR userhost ,
+.BR userservice ,
+and
+.B usergroup
+options duplicates the original pam_ldap authorization behavior.
+
+The recommended approach is to use
+.B hostservice
+instead. In this case, ipHost entries must be created for all hosts
+being managed, and they must also have the authorizedServiceObject
+class to allow authorizedService attributes to be used. Also the
+NSS host SSD must be configured so that ipHost entries can be found.
+Authorization is checked by performing an LDAP Compare operation
+looking for the PAM service name in the authorizedService attribute.
+.B slapd
+ACLs should be set to grant or deny
+.B Compare
+privilege to the appropriate users or groups as desired.
+
+If the
+.B authz2dn
+option is set then authz-regexp mappings will be used to map the
+PAM username to an LDAP DN. The authentication DN will be of the
+form
+.RS
+.B cn=<service>+uid=<user>,cn=<hostname>,cn=pam,cn=auth
+.RE
+
+If no mapping is found for this authentication DN, then this
+mapping will be ignored.
+
+If the
+.B uid2dn
+option is set then the NSS passwd SSD will be used to map the
+PAM username to an LDAP DN. The passwd SSD must have already been
+configured for this mapping to succeed.
+
+If neither the authz2dn nor the uid2dn mapping succeeds, the module
+will return a PAM_USER_UNKNOWN failure code. If both options are set,
+the authz mapping is attempted first; if it succeeds the uid2dn mapping
+will be skipped.
+
+By default only the
+.B uid2dn
+option is set.
+.RE
+.TP
+.B nssov-pam-defhost <hostname>
+Specify a default hostname to check if an ipHost entry for the current
+hostname cannot be found. This setting is only relevant if the
+.B hostservice
+option has been set.
+.TP
+.B nssov-pam-group-dn <DN>
+Specify the DN of an LDAP group to check for authorization. The LDAP user
+must be a member of this group for the login to be allowed. There is no
+default value. This setting is only relevant if the
+.B usergroup
+option has been set.
+.TP
+.B nssov-pam-group-ad <attribute>
+Specify the attribute to use for group membership checks.
+There is no default value. This setting is only relevant if the
+.B usergroup
+option has been set.
+.TP
+.B nssov-pam-min-uid <integer>
+Specify a minimum uid that is allowed to login. Users with a uidNumber
+lower than this value will be denied access. The default is zero, which
+disables this setting.
+.TP
+.B nssov-pam-max-uid <integer>
+Specify a maximum uid that is allowed to login. Users with a uidNumber
+higher than this value will be denied access. The default is zero, which
+disables this setting.
+.TP
+.B nssov-pam-template-ad <attribute>
+Specify an attribute to check in a user's entry for a template login name.
+The template login feature is used by FreeBSD's PAM framework. It can be
+viewed as a form of proxying, where a user can authenticate with one
+username/password pair, but is assigned the identity and credentials of
+the template user. This setting is disabled by default.
+.TP
+.B nssov-pam-template <name>
+Specify a default username to be used if no template attribute is found
+in the user's entry. The
+.B nssov-pam-template-ad
+directive must be configured for this setting to have any effect.
+.TP
+.B nssov-pam-session <service>
+Specify a PAM service name whose sessions will be recorded. For the
+configured services, logins will be recorded in the
+.TP
+.B nssov-pam-password-prohibit-message <message>
+Disable password change service and return the specified message to
+users.
+.TP
+.B nssov-pam-pwdmgr-dn <dn>
+Specify the dn of the password manager.
+.TP
+.B nssov-pam-pwdmgr-pwd <pwd>
+Specify the pwd of the password manager.
+.TP
+.B loginStatus
+operational attribute of the user's entry. The attribute's values are
+of the form
+.RS
+.RS
+.B <generalizedTime> <host> <service> <tty> (<ruser@rhost>)
+.RE
+.RE
+Upon logout the corresponding value will be deleted. This feature allows
+a single LDAP Search to be used to check which users are logged in across
+all the hosts of a network. The rootdn of the database is used to perform
+the updates of the loginStatus attribute, so a rootdn must already be
+configured for this feature to work. By default no services are configured.
+.LP
+The PAM functions support LDAP Password Policy as well. If the password
+policy overlay is in use (see
+.BR slapo-ppolicy (5)),
+policy
+information (e.g. password expiration, password quality, etc.)
+may be returned to the PAM client as a result of authentication,
+account management, and password modification requests.
+
+The overlay also supports dynamic configuration in cn=config. An example
+of the config entry is
+.LP
+.RS
+.nf
+ dn: olcOverlay={0}nssov,ocDatabase={1}mdb,cn=config
+ objectClass: olcOverlayConfig
+ objectClass: olcNssOvConfig
+ olcOverlay: {0}nssov
+ olcNssSsd: passwd ldap:///ou=users,dc=example,dc=com??one
+ olcNssMap: passwd uid accountName
+ olcNssPam: hostservice uid2dn
+ olcNssPamDefHost: defaulthost
+ olcNssPamMinUid: 500
+ olcNssPamMaxUid: 32000
+ olcNssPamSession: login
+ olcNssPamSession: sshd
+.fi
+.RE
+.LP
+which enables the passwd service, and uses the accountName attribute to
+fetch what is usually retrieved from the uid attribute. It also enables
+some PAM authorization controls, and specifies that the PAM
+.B login
+and
+.B sshd
+services should have their logins recorded.
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd\-ldap (5),
+.BR slapo\-pcache (5),
+.BR slapo\-ppolicy (5),
+.BR slapd (8).
+.SH AUTHOR
+Howard Chu, inspired by nss-ldapd by Arthur de Jong and pam_ldap by Luke Howard
+Enhancements by Ted C. Cheng, Symas Corp.
diff --git a/contrib/slapd-modules/passwd/Makefile b/contrib/slapd-modules/passwd/Makefile
new file mode 100644
index 0000000..6346496
--- /dev/null
+++ b/contrib/slapd-modules/passwd/Makefile
@@ -0,0 +1,70 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = pw-kerberos.la pw-netscape.la pw-radius.la pw-apr1.la
+MANPAGES = slapd-pw-radius.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+pw-kerberos.la: kerberos.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? -lkrb5
+
+pw-netscape.la: netscape.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $?
+
+pw-radius.la: radius.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? -lradius
+
+pw-apr1.la: apr1.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $?
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/passwd/README b/contrib/slapd-modules/passwd/README
new file mode 100644
index 0000000..069555f
--- /dev/null
+++ b/contrib/slapd-modules/passwd/README
@@ -0,0 +1,69 @@
+This directory contains native slapd plugins for password mechanisms that
+are not actively supported by the project. Currently this includes the
+Kerberos, Netscape MTA-MD5 and RADIUS password mechanisms. The Apache
+APR1 MD5 and BSD/Paul Henning Kamp MD5 mechanisms are also included.
+
+To use the Kerberos plugin, add:
+
+moduleload pw-kerberos.so
+
+to your slapd configuration file.
+
+To use the Netscape plugin, add:
+
+moduleload pw-netscape.so
+
+to your slapd configuration file.
+
+To use the APR1/BSD/MD5 plugin, add:
+
+moduleload pw-apr1.so
+
+to your slapd configuration file.
+
+To use the RADIUS plugin, add:
+
+moduleload pw-radius.so
+
+to your slapd configuration file; optionally, the path to a configuration
+file can be appended in the form
+
+moduleload pw-radius.so config="/etc/radius.conf"
+
+Use Makefile to compile this plugin or use a command line similar to:
+
+gcc -shared -I../../../include -Wall -g -DHAVE_KRB5 -o pw-kerberos.so kerberos.c
+
+Replace HAVE_KRB5 with HAVE_KRB4 if you want to use Kerberos IV.
+If your Kerberos header files are not in the C compiler's
+default path, you will need to add a "-I" directive for that as well.
+
+The corresponding command for the Netscape plugin would be:
+
+gcc -shared -I../../../include -Wall -g -o pw-netscape.so netscape.c
+
+The corresponding command for the RADIUS plugin would be:
+
+gcc -shared -I../../../include -Wall -g -o pw-radius.so radius.c -lradius
+
+(Actually, you might want to statically link the RADIUS client library
+libradius.a into the module).
+
+The corresponding command for the APR1 plugin would be:
+
+gcc -shared -I../../../include -Wall -g -o pw-apr1.so apr1.c
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2004-2022 The OpenLDAP Foundation.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
diff --git a/contrib/slapd-modules/passwd/apr1-atol.pl b/contrib/slapd-modules/passwd/apr1-atol.pl
new file mode 100644
index 0000000..d6eaee7
--- /dev/null
+++ b/contrib/slapd-modules/passwd/apr1-atol.pl
@@ -0,0 +1,29 @@
+#!/usr/bin/perl -w
+
+# Apache $apr1$ to OpenLDAP {APR1} hash converter
+# (C) 2011 Devin J. Pohly
+# You may use this code freely. It would be nice to be credited.
+
+use MIME::Base64;
+
+while (<>) {
+ ($user, $hash) = split(/:/, $_);
+ unless ($hash =~ /^\$apr1\$/) {
+ print STDERR "Not an Apache MD5 hash\n";
+ exit 1;
+ }
+
+ chomp $hash;
+ ($_,$_,$salt,$hash) = split(/\$/, $hash);
+
+ $hash =~ tr|./0-9A-Za-z|A-Za-z0-9+/|;
+ $hash .= "AA";
+ $hash =~ s/(.)(.)(.)(.)/$4$3$2$1/gs;
+ $hash = decode_base64($hash);
+ $hash =~ s/(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)..(.)/$1$4$7$10$13$15$2$5$8$11$14$16$3$6$9$12/s;
+ $hash .= $salt;
+ $hash = encode_base64($hash);
+ chop $hash;
+
+ print "$user:{APR1}$hash\n";
+} \ No newline at end of file
diff --git a/contrib/slapd-modules/passwd/apr1-ltoa.pl b/contrib/slapd-modules/passwd/apr1-ltoa.pl
new file mode 100644
index 0000000..ee628ec
--- /dev/null
+++ b/contrib/slapd-modules/passwd/apr1-ltoa.pl
@@ -0,0 +1,31 @@
+#!/usr/bin/perl -w
+
+# OpenLDAP {APR1} to Apache $apr1$ hash converter
+# (C) 2011 Devin J. Pohly
+# You may use this code freely. It would be nice to be credited.
+
+use MIME::Base64;
+
+while (<>) {
+ ($user, $hash) = split(/:/, $_);
+ unless ($hash =~ /^{APR1}/) {
+ print STDERR "Not an Apache MD5 hash\n";
+ next;
+ }
+
+ chomp $hash;
+ $hash = decode_base64(substr($hash, 6));
+ ($hash, $salt) = (substr($hash, 0, 16), substr($hash, 16));
+ $hash = $hash;
+ $hash =~ s/(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)/$1$7$13$2$8$14$3$9$15$4$10$16$5$11$6\0\0$12/s;
+ $hash = encode_base64($hash);
+ chomp $hash;
+ $hash =~ s/(.)(.)(.)(.)/$4$3$2$1/gs;
+ unless ($hash =~ /AA$/) {
+ #print "Problem with hash\n";
+ next;
+ }
+ $hash =~ s/AA$//;
+ $hash =~ tr|A-Za-z0-9+/|./0-9A-Za-z|;
+ print "$user:\$apr1\$$salt\$$hash\n"
+} \ No newline at end of file
diff --git a/contrib/slapd-modules/passwd/apr1.c b/contrib/slapd-modules/passwd/apr1.c
new file mode 100644
index 0000000..36880f3
--- /dev/null
+++ b/contrib/slapd-modules/passwd/apr1.c
@@ -0,0 +1,236 @@
+/* $OpenLDAP$ */
+/*
+ * This file is derived from OpenLDAP Software. All of the modifications to
+ * OpenLDAP Software represented in the following file were developed by
+ * Devin J. Pohly <djpohly@gmail.com>. I have not assigned rights and/or
+ * interest in this work to any party.
+ *
+ * The extensions to OpenLDAP Software herein are subject to the following
+ * notice:
+ *
+ * Copyright 2011 Devin J. Pohly
+ * Portions Copyright 2011 Howard Chu
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP Public
+ * License.
+ *
+ * A portion of this code is used in accordance with the Beer-ware License,
+ * revision 42, as noted.
+ *
+ */
+
+#include "portable.h"
+
+#include <lber.h>
+#include <lber_pvt.h>
+#include "lutil.h"
+#include "lutil_md5.h"
+#include <ac/string.h>
+
+#include <assert.h>
+
+/* the only difference between this and straight PHK is the magic */
+static LUTIL_PASSWD_CHK_FUNC chk_apr1;
+static LUTIL_PASSWD_HASH_FUNC hash_apr1;
+static const struct berval scheme_apr1 = BER_BVC("{APR1}");
+static const struct berval magic_apr1 = BER_BVC("$apr1$");
+
+static LUTIL_PASSWD_CHK_FUNC chk_bsdmd5;
+static LUTIL_PASSWD_HASH_FUNC hash_bsdmd5;
+static const struct berval scheme_bsdmd5 = BER_BVC("{BSDMD5}");
+static const struct berval magic_bsdmd5 = BER_BVC("$1$");
+
+static const unsigned char apr64[] =
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+#define APR_SALT_SIZE 8
+
+/* The algorithm implemented in this function was created by Poul-Henning
+ * Kamp and released under the following license:
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ */
+static void do_phk_hash(
+ const struct berval *passwd,
+ const struct berval *salt,
+ const struct berval *magic,
+ unsigned char *digest)
+{
+ lutil_MD5_CTX ctx, ctx1;
+ int n;
+
+ /* Start hashing */
+ lutil_MD5Init(&ctx);
+ lutil_MD5Update(&ctx, (const unsigned char *) passwd->bv_val, passwd->bv_len);
+ lutil_MD5Update(&ctx, (const unsigned char *) magic->bv_val, magic->bv_len);
+ lutil_MD5Update(&ctx, (const unsigned char *) salt->bv_val, salt->bv_len);
+ /* Inner hash */
+ lutil_MD5Init(&ctx1);
+ lutil_MD5Update(&ctx1, (const unsigned char *) passwd->bv_val, passwd->bv_len);
+ lutil_MD5Update(&ctx1, (const unsigned char *) salt->bv_val, salt->bv_len);
+ lutil_MD5Update(&ctx1, (const unsigned char *) passwd->bv_val, passwd->bv_len);
+ lutil_MD5Final(digest, &ctx1);
+ /* Nom start mixing things up */
+ for (n = passwd->bv_len; n > 0; n -= LUTIL_MD5_BYTES)
+ lutil_MD5Update(&ctx, digest,
+ (n > LUTIL_MD5_BYTES ? LUTIL_MD5_BYTES : n));
+ memset(digest, 0, LUTIL_MD5_BYTES);
+ /* Curiouser and curiouser... */
+ for (n = passwd->bv_len; n; n >>= 1)
+ if (n & 1)
+ lutil_MD5Update(&ctx, digest, 1);
+ else
+ lutil_MD5Update(&ctx, (const unsigned char *) passwd->bv_val, 1);
+ lutil_MD5Final(digest, &ctx);
+ /*
+ * Repeatedly hash things into the final value. This was originally
+ * intended to slow the algorithm down.
+ */
+ for (n = 0; n < 1000; n++) {
+ lutil_MD5Init(&ctx1);
+ if (n & 1)
+ lutil_MD5Update(&ctx1,
+ (const unsigned char *) passwd->bv_val, passwd->bv_len);
+ else
+ lutil_MD5Update(&ctx1, digest, LUTIL_MD5_BYTES);
+
+ if (n % 3)
+ lutil_MD5Update(&ctx1,
+ (const unsigned char *) salt->bv_val, salt->bv_len);
+ if (n % 7)
+ lutil_MD5Update(&ctx1,
+ (const unsigned char *) passwd->bv_val, passwd->bv_len);
+
+ if (n & 1)
+ lutil_MD5Update(&ctx1, digest, LUTIL_MD5_BYTES);
+ else
+ lutil_MD5Update(&ctx1,
+ (const unsigned char *) passwd->bv_val, passwd->bv_len);
+ lutil_MD5Final(digest, &ctx1);
+ }
+}
+
+static int chk_phk(
+ const struct berval *magic,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ unsigned char digest[LUTIL_MD5_BYTES];
+ unsigned char *orig_pass;
+ int rc;
+ struct berval salt;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len <= sizeof(digest))
+ return LUTIL_PASSWD_ERR;
+
+ /* base64 un-encode password hash */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if (orig_pass == NULL)
+ return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if (rc <= (int) sizeof(digest)) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ salt.bv_val = (char *) &orig_pass[sizeof(digest)];
+ salt.bv_len = rc - sizeof(digest);
+
+ do_phk_hash(cred, &salt, magic, digest);
+
+ if (text)
+ *text = NULL;
+
+ /* compare */
+ rc = memcmp((char *) orig_pass, (char *) digest, sizeof(digest));
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+static int chk_apr1(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_phk(&magic_apr1, passwd, cred, text);
+}
+
+static int chk_bsdmd5(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_phk(&magic_bsdmd5, passwd, cred, text);
+}
+
+static int hash_phk(
+ const struct berval *scheme,
+ const struct berval *magic,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+ unsigned char digest_buf[LUTIL_MD5_BYTES];
+ char salt_buf[APR_SALT_SIZE];
+ struct berval digest;
+ struct berval salt;
+ int n;
+
+ digest.bv_val = (char *) digest_buf;
+ digest.bv_len = sizeof(digest_buf);
+ salt.bv_val = salt_buf;
+ salt.bv_len = APR_SALT_SIZE;
+
+ /* generate random salt */
+ if (lutil_entropy( (unsigned char *) salt.bv_val, salt.bv_len) < 0)
+ return LUTIL_PASSWD_ERR;
+ /* limit it to characters in the 64-char set */
+ for (n = 0; n < salt.bv_len; n++)
+ salt.bv_val[n] = apr64[salt.bv_val[n] % (sizeof(apr64) - 1)];
+
+ do_phk_hash(passwd, &salt, magic, digest_buf);
+
+ if (text)
+ *text = NULL;
+
+ return lutil_passwd_string64(scheme, &digest, hash, &salt);
+}
+
+static int hash_apr1(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+ return hash_phk(scheme, &magic_apr1, passwd, hash, text);
+}
+
+static int hash_bsdmd5(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+ return hash_phk(scheme, &magic_bsdmd5, passwd, hash, text);
+}
+
+int init_module(int argc, char *argv[]) {
+ int rc;
+ rc = lutil_passwd_add((struct berval *) &scheme_apr1, chk_apr1, hash_apr1);
+ if ( !rc )
+ rc = lutil_passwd_add((struct berval *) &scheme_bsdmd5,
+ chk_bsdmd5, hash_bsdmd5);
+ return rc;
+}
diff --git a/contrib/slapd-modules/passwd/kerberos.c b/contrib/slapd-modules/passwd/kerberos.c
new file mode 100644
index 0000000..bebcbd0
--- /dev/null
+++ b/contrib/slapd-modules/passwd/kerberos.c
@@ -0,0 +1,211 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <unistd.h>
+
+#include <lber.h>
+#include <lber_pvt.h> /* BER_BVC definition */
+#include "lutil.h"
+#include <ac/string.h>
+
+#ifdef HAVE_KRB5
+#include <krb5.h>
+#elif defined(HAVE_KRB4)
+#include <krb.h>
+#endif
+
+/* From <ldap_pvt.h> */
+LDAP_F( char *) ldap_pvt_get_fqdn LDAP_P(( char * ));
+
+static LUTIL_PASSWD_CHK_FUNC chk_kerberos;
+static const struct berval scheme = BER_BVC("{KERBEROS}");
+
+static int chk_kerberos(
+ const struct berval *sc,
+ const struct berval * passwd,
+ const struct berval * cred,
+ const char **text )
+{
+ unsigned int i;
+ int rtn;
+
+ for( i=0; i<cred->bv_len; i++) {
+ if(cred->bv_val[i] == '\0') {
+ return LUTIL_PASSWD_ERR; /* NUL character in password */
+ }
+ }
+
+ if( cred->bv_val[i] != '\0' ) {
+ return LUTIL_PASSWD_ERR; /* cred must behave like a string */
+ }
+
+ for( i=0; i<passwd->bv_len; i++) {
+ if(passwd->bv_val[i] == '\0') {
+ return LUTIL_PASSWD_ERR; /* NUL character in password */
+ }
+ }
+
+ if( passwd->bv_val[i] != '\0' ) {
+ return LUTIL_PASSWD_ERR; /* passwd must behave like a string */
+ }
+
+ rtn = LUTIL_PASSWD_ERR;
+
+#ifdef HAVE_KRB5 /* HAVE_HEIMDAL_KRB5 */
+ {
+/* Portions:
+ * Copyright (c) 1997, 1998, 1999 Kungliga Tekniska H\xf6gskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+ krb5_context context;
+ krb5_error_code ret;
+ krb5_creds creds;
+ krb5_get_init_creds_opt get_options;
+ krb5_verify_init_creds_opt verify_options;
+ krb5_principal client, server;
+#ifdef notdef
+ krb5_preauthtype pre_auth_types[] = {KRB5_PADATA_ENC_TIMESTAMP};
+#endif
+
+ ret = krb5_init_context( &context );
+ if (ret) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+#ifdef notdef
+ krb5_get_init_creds_opt_set_preauth_list(&get_options,
+ pre_auth_types, 1);
+#endif
+
+ krb5_get_init_creds_opt_init( &get_options );
+
+ krb5_verify_init_creds_opt_init( &verify_options );
+
+ ret = krb5_parse_name( context, passwd->bv_val, &client );
+
+ if (ret) {
+ krb5_free_context( context );
+ return LUTIL_PASSWD_ERR;
+ }
+
+ ret = krb5_get_init_creds_password( context,
+ &creds, client, cred->bv_val, NULL,
+ NULL, 0, NULL, &get_options );
+
+ if (ret) {
+ krb5_free_principal( context, client );
+ krb5_free_context( context );
+ return LUTIL_PASSWD_ERR;
+ }
+
+ {
+ char *host = ldap_pvt_get_fqdn( NULL );
+
+ if( host == NULL ) {
+ krb5_free_principal( context, client );
+ krb5_free_context( context );
+ return LUTIL_PASSWD_ERR;
+ }
+
+ ret = krb5_sname_to_principal( context,
+ host, "ldap", KRB5_NT_SRV_HST, &server );
+
+ ber_memfree( host );
+ }
+
+ if (ret) {
+ krb5_free_principal( context, client );
+ krb5_free_context( context );
+ return LUTIL_PASSWD_ERR;
+ }
+
+ ret = krb5_verify_init_creds( context,
+ &creds, server, NULL, NULL, &verify_options );
+
+ krb5_free_principal( context, client );
+ krb5_free_principal( context, server );
+ krb5_free_cred_contents( context, &creds );
+ krb5_free_context( context );
+
+ rtn = ret ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+ }
+#elif defined(HAVE_KRB4)
+ {
+ /* Borrowed from Heimdal kpopper */
+/* Portions:
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+ int status;
+ char lrealm[REALM_SZ];
+ char tkt[MAXHOSTNAMELEN];
+
+ status = krb_get_lrealm(lrealm,1);
+ if (status == KFAILURE) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ snprintf(tkt, sizeof(tkt), "%s_slapd.%u",
+ TKT_ROOT, (unsigned)getpid());
+ krb_set_tkt_string (tkt);
+
+ status = krb_verify_user( passwd->bv_val, "", lrealm,
+ cred->bv_val, 1, "ldap");
+
+ dest_tkt(); /* no point in keeping the tickets */
+
+ return status == KFAILURE ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+ }
+#endif
+
+ return rtn;
+}
+
+int init_module(int argc, char *argv[]) {
+ return lutil_passwd_add( (struct berval *)&scheme, chk_kerberos, NULL );
+}
diff --git a/contrib/slapd-modules/passwd/netscape.c b/contrib/slapd-modules/passwd/netscape.c
new file mode 100644
index 0000000..8e2de7b
--- /dev/null
+++ b/contrib/slapd-modules/passwd/netscape.c
@@ -0,0 +1,83 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <unistd.h>
+
+#include <lber.h>
+#include <lber_pvt.h>
+#include "lutil.h"
+#include "lutil_md5.h"
+#include <ac/string.h>
+
+static LUTIL_PASSWD_CHK_FUNC chk_ns_mta_md5;
+static const struct berval scheme = BER_BVC("{NS-MTA-MD5}");
+
+#define NS_MTA_MD5_PASSLEN 64
+static int chk_ns_mta_md5(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text )
+{
+ lutil_MD5_CTX MD5context;
+ unsigned char MD5digest[LUTIL_MD5_BYTES], c;
+ char buffer[LUTIL_MD5_BYTES*2];
+ int i;
+
+ if( passwd->bv_len != NS_MTA_MD5_PASSLEN ) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ lutil_MD5Init(&MD5context);
+ lutil_MD5Update(&MD5context,
+ (const unsigned char *) &passwd->bv_val[32],
+ 32 );
+
+ c = 0x59;
+ lutil_MD5Update(&MD5context,
+ (const unsigned char *) &c,
+ 1 );
+
+ lutil_MD5Update(&MD5context,
+ (const unsigned char *) cred->bv_val,
+ cred->bv_len );
+
+ c = 0xF7;
+ lutil_MD5Update(&MD5context,
+ (const unsigned char *) &c,
+ 1 );
+
+ lutil_MD5Update(&MD5context,
+ (const unsigned char *) &passwd->bv_val[32],
+ 32 );
+
+ lutil_MD5Final(MD5digest, &MD5context);
+
+ for( i=0; i < sizeof( MD5digest ); i++ ) {
+ buffer[i+i] = "0123456789abcdef"[(MD5digest[i]>>4) & 0x0F];
+ buffer[i+i+1] = "0123456789abcdef"[ MD5digest[i] & 0x0F];
+ }
+
+ /* compare */
+ return memcmp((char *)passwd->bv_val,
+ (char *)buffer, sizeof(buffer)) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+int init_module(int argc, char *argv[]) {
+ return lutil_passwd_add( (struct berval *)&scheme, chk_ns_mta_md5, NULL );
+}
diff --git a/contrib/slapd-modules/passwd/pbkdf2/Makefile b/contrib/slapd-modules/passwd/pbkdf2/Makefile
new file mode 100644
index 0000000..6279f50
--- /dev/null
+++ b/contrib/slapd-modules/passwd/pbkdf2/Makefile
@@ -0,0 +1,62 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../../..
+LDAP_BUILD = ../../../..
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+#DEFS = -DSLAPD_PBKDF2_DEBUG
+
+SSL_INC =
+SSL_LIB = -lcrypto
+
+INCS = $(LDAP_INC) $(SSL_INC)
+LIBS = $(LDAP_LIB) $(SSL_LIB)
+
+PROGRAMS = pw-pbkdf2.la
+MANPAGES = slapd-pw-pbkdf2.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+pw-pbkdf2.la: pw-pbkdf2.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/passwd/pbkdf2/README b/contrib/slapd-modules/passwd/pbkdf2/README
new file mode 100644
index 0000000..d4d99d2
--- /dev/null
+++ b/contrib/slapd-modules/passwd/pbkdf2/README
@@ -0,0 +1,99 @@
+PBKDF2 for OpenLDAP
+=======================
+
+pw-pbkdf2.c provides PBKDF2 key derivation functions in OpenLDAP.
+
+Schemes:
+
+ * {PBKDF2} - alias to {PBKDF2-SHA1}
+ * {PBKDF2-SHA1}
+ * {PBKDF2-SHA256}
+ * {PBKDF2-SHA512}
+
+# Requirements
+
+ * OpenSSL 1.0.0 or later
+
+# Installations
+
+First, You need to configure and build OpenLDAP.
+
+ $ cd <OPENLDAP_BUILD_DIR>/contrib/slapd-modules/passwd/
+ $ git clone https://github.com/hamano/openldap-pbkdf2.git
+ $ cd openldap-pbkdf2/
+ $ make
+ # make install
+
+# Configuration
+
+In slapd.conf:
+
+ moduleload pw-pbkdf2.so
+
+You can also tell OpenLDAP to use the schemes when processing LDAP
+Password Modify Extended Operations, thanks to the password-hash
+option in slapd.conf. For example:
+
+ password-hash {PBKDF2}
+or
+ password-hash {PBKDF2-SHA256}
+or
+ password-hash {PBKDF2-SHA512}
+
+# Testing
+
+You can get hash to use slappasswd.
+
+ $ slappasswd -o module-load=pw-pbkdf2.la -h {PBKDF2} -s secret
+ {PBKDF2}60000$Y6ZHtTTbeUgpIbIW0QDmDA$j/aU7jFKUSbH4UobNQDm9OEIwuw
+
+A quick way to test whether it's working is to customize the rootdn and
+rootpw in slapd.conf, eg:
+
+ rootdn "cn=Manager,dc=example,dc=com"
+ rootpw {PBKDF2}60000$Y6ZHtTTbeUgpIbIW0QDmDA$j/aU7jFKUSbH4UobNQDm9OEIwuw
+
+Then to test, run something like:
+
+ $ ldapsearch -x -b "dc=example,dc=com" -D "cn=Manager,dc=example,dc=com" -w secret
+
+# Debugging
+You can specify -DSLAPD_PBKDF2_DEBUG flag for debugging.
+
+# Message Format
+
+ {PBKDF2}<Iteration>$<Adapted Base64 Salt>$<Adapted Base64 DK>
+
+# References
+
+* [RFC 2898 Password-Based Cryptography][^1]
+[^1]: http://tools.ietf.org/html/rfc2898
+
+* [PKCS #5 PBKDF2 Test Vectors][^2]
+[^2]: http://tools.ietf.org/html/draft-josefsson-pbkdf2-test-vectors-06
+
+* [RFC 2307 Using LDAP as a Network Information Service][^3]
+[^3]: http://tools.ietf.org/html/rfc2307
+
+* [Python Passlib][^4]
+[^4]: http://pythonhosted.org/passlib/
+
+* [Adapted Base64 Encoding][^5]
+[^5]: http://pythonhosted.org/passlib/lib/passlib.utils.html#passlib.utils.ab64_encode
+
+# License
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2009-2022 The OpenLDAP Foundation.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
+# ACKNOWLEDGEMENT
+This work was initially developed by HAMANO Tsukasa <hamano@osstech.co.jp>
diff --git a/contrib/slapd-modules/passwd/pbkdf2/pw-pbkdf2.c b/contrib/slapd-modules/passwd/pbkdf2/pw-pbkdf2.c
new file mode 100644
index 0000000..1cc2770
--- /dev/null
+++ b/contrib/slapd-modules/passwd/pbkdf2/pw-pbkdf2.c
@@ -0,0 +1,451 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2009-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ */
+
+#define _GNU_SOURCE
+
+#include "portable.h"
+#include <ac/string.h>
+#include "lber_pvt.h"
+#include "lutil.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_OPENSSL
+#include <openssl/evp.h>
+#elif HAVE_GNUTLS
+#include <nettle/pbkdf2.h>
+#include <nettle/hmac.h>
+typedef void (*pbkdf2_hmac_update)(void *, unsigned, const uint8_t *);
+typedef void (*pbkdf2_hmac_digest)(void *, unsigned, uint8_t *);
+#else
+#error Unsupported crypto backend.
+#endif
+
+#define PBKDF2_ITERATION 10000
+#define PBKDF2_SALT_SIZE 16
+#define PBKDF2_SHA1_DK_SIZE 20
+#define PBKDF2_SHA256_DK_SIZE 32
+#define PBKDF2_SHA512_DK_SIZE 64
+#define PBKDF2_MAX_DK_SIZE 64
+
+const struct berval pbkdf2_scheme = BER_BVC("{PBKDF2}");
+const struct berval pbkdf2_sha1_scheme = BER_BVC("{PBKDF2-SHA1}");
+const struct berval pbkdf2_sha256_scheme = BER_BVC("{PBKDF2-SHA256}");
+const struct berval pbkdf2_sha512_scheme = BER_BVC("{PBKDF2-SHA512}");
+
+/*
+ * Converting base64 string to adapted base64 string.
+ * Adapted base64 encode is identical to general base64 encode except
+ * that it uses '.' instead of '+', and omits trailing padding '=' and
+ * whitespace.
+ * see http://pythonhosted.org/passlib/lib/passlib.utils.html
+ * This is destructive function.
+ */
+static int b64_to_ab64(char *str)
+{
+ char *p = str;
+ do {
+ if(*p == '+'){
+ *p = '.';
+ }
+ if(*p == '='){
+ *p = '\0';
+ }
+ } while(*p++);
+ return 0;
+}
+
+/*
+ * Converting adapted base64 string to base64 string.
+ * dstsize will require src length + 2, due to output string have
+ * potential to append "=" or "==".
+ * return -1 if few output buffer.
+ */
+static int ab64_to_b64(char *src, char *dst, size_t dstsize){
+ int i;
+ char *p = src;
+ for(i=0; p[i] && p[i] != '$'; i++){
+ if(i >= dstsize){
+ dst[0] = '\0';
+ return -1;
+ }
+ if(p[i] == '.'){
+ dst[i] = '+';
+ }else{
+ dst[i] = p[i];
+ }
+ }
+ for(;i%4;i++){
+ if(i >= dstsize){
+ dst[0] = '\0';
+ return -1;
+ }
+ dst[i] = '=';
+ }
+ dst[i] = '\0';
+ return 0;
+}
+
+static int pbkdf2_format(
+ const struct berval *sc,
+ int iteration,
+ const struct berval *salt,
+ const struct berval *dk,
+ struct berval *msg)
+{
+
+ int rc, msg_len;
+ char salt_b64[LUTIL_BASE64_ENCODE_LEN(PBKDF2_SALT_SIZE) + 1];
+ char dk_b64[LUTIL_BASE64_ENCODE_LEN(PBKDF2_MAX_DK_SIZE) + 1];
+
+ rc = lutil_b64_ntop((unsigned char *)salt->bv_val, salt->bv_len,
+ salt_b64, sizeof(salt_b64));
+ if(rc < 0){
+ return LUTIL_PASSWD_ERR;
+ }
+ b64_to_ab64(salt_b64);
+ rc = lutil_b64_ntop((unsigned char *)dk->bv_val, dk->bv_len,
+ dk_b64, sizeof(dk_b64));
+ if(rc < 0){
+ return LUTIL_PASSWD_ERR;
+ }
+ b64_to_ab64(dk_b64);
+ msg_len = asprintf(&msg->bv_val, "%s%d$%s$%s",
+ sc->bv_val, iteration,
+ salt_b64, dk_b64);
+ if(msg_len < 0){
+ msg->bv_len = 0;
+ return LUTIL_PASSWD_ERR;
+ }
+
+ msg->bv_len = msg_len;
+ return LUTIL_PASSWD_OK;
+}
+
+static int pbkdf2_encrypt(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *msg,
+ const char **text)
+{
+ unsigned char salt_value[PBKDF2_SALT_SIZE];
+ struct berval salt;
+ unsigned char dk_value[PBKDF2_MAX_DK_SIZE];
+ struct berval dk;
+ int iteration = PBKDF2_ITERATION;
+ int rc;
+#ifdef HAVE_OPENSSL
+ const EVP_MD *md;
+#elif HAVE_GNUTLS
+ struct hmac_sha1_ctx sha1_ctx;
+ struct hmac_sha256_ctx sha256_ctx;
+ struct hmac_sha512_ctx sha512_ctx;
+ void * current_ctx = NULL;
+ pbkdf2_hmac_update current_hmac_update = NULL;
+ pbkdf2_hmac_digest current_hmac_digest = NULL;
+#endif
+
+ salt.bv_val = (char *)salt_value;
+ salt.bv_len = sizeof(salt_value);
+ dk.bv_val = (char *)dk_value;
+
+#ifdef HAVE_OPENSSL
+ if(!ber_bvcmp(scheme, &pbkdf2_scheme)){
+ dk.bv_len = PBKDF2_SHA1_DK_SIZE;
+ md = EVP_sha1();
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha1_scheme)){
+ dk.bv_len = PBKDF2_SHA1_DK_SIZE;
+ md = EVP_sha1();
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha256_scheme)){
+ dk.bv_len = PBKDF2_SHA256_DK_SIZE;
+ md = EVP_sha256();
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha512_scheme)){
+ dk.bv_len = PBKDF2_SHA512_DK_SIZE;
+ md = EVP_sha512();
+ }else{
+ return LUTIL_PASSWD_ERR;
+ }
+#elif HAVE_GNUTLS
+ if(!ber_bvcmp(scheme, &pbkdf2_scheme)){
+ dk.bv_len = PBKDF2_SHA1_DK_SIZE;
+ current_ctx = &sha1_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha1_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha1_digest;
+ hmac_sha1_set_key(current_ctx, passwd->bv_len, (const uint8_t *) passwd->bv_val);
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha1_scheme)){
+ dk.bv_len = PBKDF2_SHA1_DK_SIZE;
+ current_ctx = &sha1_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha1_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha1_digest;
+ hmac_sha1_set_key(current_ctx, passwd->bv_len, (const uint8_t *) passwd->bv_val);
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha256_scheme)){
+ dk.bv_len = PBKDF2_SHA256_DK_SIZE;
+ current_ctx = &sha256_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha256_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha256_digest;
+ hmac_sha256_set_key(current_ctx, passwd->bv_len, (const uint8_t *) passwd->bv_val);
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha512_scheme)){
+ dk.bv_len = PBKDF2_SHA512_DK_SIZE;
+ current_ctx = &sha512_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha512_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha512_digest;
+ hmac_sha512_set_key(current_ctx, passwd->bv_len, (const uint8_t *) passwd->bv_val);
+ }else{
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+
+ if(lutil_entropy((unsigned char *)salt.bv_val, salt.bv_len) < 0){
+ return LUTIL_PASSWD_ERR;
+ }
+
+#ifdef HAVE_OPENSSL
+ if(!PKCS5_PBKDF2_HMAC(passwd->bv_val, passwd->bv_len,
+ (unsigned char *)salt.bv_val, salt.bv_len,
+ iteration, md, dk.bv_len, dk_value)){
+ return LUTIL_PASSWD_ERR;
+ }
+#elif HAVE_GNUTLS
+ PBKDF2(current_ctx, current_hmac_update, current_hmac_digest,
+ dk.bv_len, iteration,
+ salt.bv_len, (const uint8_t *) salt.bv_val,
+ dk.bv_len, dk_value);
+#endif
+
+#ifdef SLAPD_PBKDF2_DEBUG
+ printf("Encrypt for %s\n", scheme->bv_val);
+ printf(" Password:\t%s\n", passwd->bv_val);
+
+ printf(" Salt:\t\t");
+ int i;
+ for(i=0; i<salt.bv_len; i++){
+ printf("%02x", salt_value[i]);
+ }
+ printf("\n");
+ printf(" Iteration:\t%d\n", iteration);
+
+ printf(" DK:\t\t");
+ for(i=0; i<dk.bv_len; i++){
+ printf("%02x", dk_value[i]);
+ }
+ printf("\n");
+#endif
+
+ rc = pbkdf2_format(scheme, iteration, &salt, &dk, msg);
+
+#ifdef SLAPD_PBKDF2_DEBUG
+ printf(" Output:\t%s\n", msg->bv_val);
+#endif
+
+ return rc;
+}
+
+static int pbkdf2_check(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ int rc;
+ int iteration;
+
+ /* salt_value require PBKDF2_SALT_SIZE + 1 in lutil_b64_pton. */
+ unsigned char salt_value[PBKDF2_SALT_SIZE + 1];
+ char salt_b64[LUTIL_BASE64_ENCODE_LEN(PBKDF2_SALT_SIZE) + 1];
+ /* dk_value require PBKDF2_MAX_DK_SIZE + 1 in lutil_b64_pton. */
+ unsigned char dk_value[PBKDF2_MAX_DK_SIZE + 1];
+ char dk_b64[LUTIL_BASE64_ENCODE_LEN(PBKDF2_MAX_DK_SIZE) + 1];
+ unsigned char input_dk_value[PBKDF2_MAX_DK_SIZE];
+ size_t dk_len;
+#ifdef HAVE_OPENSSL
+ const EVP_MD *md;
+#elif HAVE_GNUTLS
+ struct hmac_sha1_ctx sha1_ctx;
+ struct hmac_sha256_ctx sha256_ctx;
+ struct hmac_sha512_ctx sha512_ctx;
+ void * current_ctx = NULL;
+ pbkdf2_hmac_update current_hmac_update = NULL;
+ pbkdf2_hmac_digest current_hmac_digest = NULL;
+#endif
+
+#ifdef SLAPD_PBKDF2_DEBUG
+ printf("Checking for %s\n", scheme->bv_val);
+ printf(" Stored Value:\t%s\n", passwd->bv_val);
+ printf(" Input Cred:\t%s\n", cred->bv_val);
+#endif
+
+#ifdef HAVE_OPENSSL
+ if(!ber_bvcmp(scheme, &pbkdf2_scheme)){
+ dk_len = PBKDF2_SHA1_DK_SIZE;
+ md = EVP_sha1();
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha1_scheme)){
+ dk_len = PBKDF2_SHA1_DK_SIZE;
+ md = EVP_sha1();
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha256_scheme)){
+ dk_len = PBKDF2_SHA256_DK_SIZE;
+ md = EVP_sha256();
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha512_scheme)){
+ dk_len = PBKDF2_SHA512_DK_SIZE;
+ md = EVP_sha512();
+ }else{
+ return LUTIL_PASSWD_ERR;
+ }
+#elif HAVE_GNUTLS
+ if(!ber_bvcmp(scheme, &pbkdf2_scheme)){
+ dk_len = PBKDF2_SHA1_DK_SIZE;
+ current_ctx = &sha1_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha1_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha1_digest;
+ hmac_sha1_set_key(current_ctx, cred->bv_len, (const uint8_t *) cred->bv_val);
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha1_scheme)){
+ dk_len = PBKDF2_SHA1_DK_SIZE;
+ current_ctx = &sha1_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha1_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha1_digest;
+ hmac_sha1_set_key(current_ctx, cred->bv_len, (const uint8_t *) cred->bv_val);
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha256_scheme)){
+ dk_len = PBKDF2_SHA256_DK_SIZE;
+ current_ctx = &sha256_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha256_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha256_digest;
+ hmac_sha256_set_key(current_ctx, cred->bv_len, (const uint8_t *) cred->bv_val);
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha512_scheme)){
+ dk_len = PBKDF2_SHA512_DK_SIZE;
+ current_ctx = &sha512_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha512_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha512_digest;
+ hmac_sha512_set_key(current_ctx, cred->bv_len, (const uint8_t *) cred->bv_val);
+ }else{
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+
+ iteration = atoi(passwd->bv_val);
+ if(iteration < 1){
+ return LUTIL_PASSWD_ERR;
+ }
+
+ char *ptr;
+ ptr = strchr(passwd->bv_val, '$');
+ if(!ptr){
+ return LUTIL_PASSWD_ERR;
+ }
+ ptr++; /* skip '$' */
+ rc = ab64_to_b64(ptr, salt_b64, sizeof(salt_b64));
+ if(rc < 0){
+ return LUTIL_PASSWD_ERR;
+ }
+
+ ptr = strchr(ptr, '$');
+ if(!ptr){
+ return LUTIL_PASSWD_ERR;
+ }
+ ptr++; /* skip '$' */
+ rc = ab64_to_b64(ptr, dk_b64, sizeof(dk_b64));
+ if(rc < 0){
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* The targetsize require PBKDF2_SALT_SIZE + 1 in lutil_b64_pton. */
+ rc = lutil_b64_pton(salt_b64, salt_value, PBKDF2_SALT_SIZE + 1);
+ if(rc < 0){
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* consistency check */
+ if(rc != PBKDF2_SALT_SIZE){
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* The targetsize require PBKDF2_MAX_DK_SIZE + 1 in lutil_b64_pton. */
+ rc = lutil_b64_pton(dk_b64, dk_value, sizeof(dk_value));
+ if(rc < 0){
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* consistency check */
+ if(rc != dk_len){
+ return LUTIL_PASSWD_ERR;
+ }
+
+#ifdef HAVE_OPENSSL
+ if(!PKCS5_PBKDF2_HMAC(cred->bv_val, cred->bv_len,
+ salt_value, PBKDF2_SALT_SIZE,
+ iteration, md, dk_len, input_dk_value)){
+ return LUTIL_PASSWD_ERR;
+ }
+#elif HAVE_GNUTLS
+ PBKDF2(current_ctx, current_hmac_update, current_hmac_digest,
+ dk_len, iteration,
+ PBKDF2_SALT_SIZE, salt_value,
+ dk_len, input_dk_value);
+#endif
+
+ rc = memcmp(dk_value, input_dk_value, dk_len);
+#ifdef SLAPD_PBKDF2_DEBUG
+ printf(" Iteration:\t%d\n", iteration);
+ printf(" Base64 Salt:\t%s\n", salt_b64);
+ printf(" Base64 DK:\t%s\n", dk_b64);
+ int i;
+ printf(" Stored Salt:\t");
+ for(i=0; i<PBKDF2_SALT_SIZE; i++){
+ printf("%02x", salt_value[i]);
+ }
+ printf("\n");
+
+ printf(" Stored DK:\t");
+ for(i=0; i<dk_len; i++){
+ printf("%02x", dk_value[i]);
+ }
+ printf("\n");
+
+ printf(" Input DK:\t");
+ for(i=0; i<dk_len; i++){
+ printf("%02x", input_dk_value[i]);
+ }
+ printf("\n");
+ printf(" Result:\t%d\n", rc);
+#endif
+ return rc?LUTIL_PASSWD_ERR:LUTIL_PASSWD_OK;
+}
+
+int init_module(int argc, char *argv[]) {
+ int rc;
+ rc = lutil_passwd_add((struct berval *)&pbkdf2_scheme,
+ pbkdf2_check, pbkdf2_encrypt);
+ if(rc) return rc;
+ rc = lutil_passwd_add((struct berval *)&pbkdf2_sha1_scheme,
+ pbkdf2_check, pbkdf2_encrypt);
+ if(rc) return rc;
+
+ rc = lutil_passwd_add((struct berval *)&pbkdf2_sha256_scheme,
+ pbkdf2_check, pbkdf2_encrypt);
+ if(rc) return rc;
+
+ rc = lutil_passwd_add((struct berval *)&pbkdf2_sha512_scheme,
+ pbkdf2_check, pbkdf2_encrypt);
+ return rc;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/contrib/slapd-modules/passwd/pbkdf2/slapd-pw-pbkdf2.5 b/contrib/slapd-modules/passwd/pbkdf2/slapd-pw-pbkdf2.5
new file mode 100644
index 0000000..3bacf62
--- /dev/null
+++ b/contrib/slapd-modules/passwd/pbkdf2/slapd-pw-pbkdf2.5
@@ -0,0 +1,112 @@
+.TH SLAPD-PW-PBKDF2 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2015-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd-pw-pbkdf2 \- PBKDF2 password module to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.RS
+.LP
+.B moduleload
+.B pw-pbkdf2
+.RE
+.SH DESCRIPTION
+.LP
+The
+.B pw-pbkdf2
+module to
+.BR slapd (8)
+provides support for the use of the key stretching function
+PBKDF2 (Password-Based Key Derivation Function 2) following RFC 2898
+in hashed passwords in OpenLDAP.
+.LP
+It does so by providing the following additional password schemes for use in slapd:
+.RS
+.TP
+.B {PBKDF2}
+alias to {PBKDF2-SHA1}
+.TP
+.B {PBKDF2-SHA1}
+PBKDF2 using HMAC-SHA-1 as the underlying pseudorandom function
+.TP
+.B {PBKDF2-SHA256}
+PBKDF2 using HMAC-SHA-256 as the underlying pseudorandom function
+.TP
+.B {PBKDF2-SHA512}
+PBKDF2 using HMAC-SHA-512 as the underlying pseudorandom function
+.RE
+
+.SH CONFIGURATION
+The
+.B pw-pbkdf2
+module does not need any configuration.
+.LP
+After loading the module, the password schemes
+{PBKDF2}, {PBKDF2-SHA1}, {PBKDF2-SHA256}, and {PBKDF2-SHA512}
+will be recognised in values of the
+.I userPassword
+attribute.
+.LP
+You can then instruct OpenLDAP to use these schemes when processing
+the LDAPv3 Password Modify (RFC 3062) extended operations by using the
+.BR password-hash
+option in
+.BR slapd.conf (5).
+
+.SH NOTES
+If you want to use the schemes described here with
+.BR slappasswd (8),
+remember to load the module using its command line options.
+The relevant option/value is:
+.RS
+.LP
+.B \-o
+.BR module\-load = pw-pbkdf2
+.LP
+.RE
+Depending on
+.BR pw-pbkdf2 's
+location, you may also need:
+.RS
+.LP
+.B \-o
+.BR module\-path = \fIpathspec\fP
+.RE
+
+.SH EXAMPLES
+All of the userPassword LDAP attributes below encode the password
+.RI ' secret '.
+.EX
+.LP
+userPassword: {PBKDF2-SHA512}10000$/oQ4xZi382mk7kvCd3ZdkA$2wqjpuyV2l0U/a1QwoQPOtlQL.UcJGNACj1O24balruqQb/NgPW6OCvvrrJP8.SzA3/5iYvLnwWPzeX8IK/bEQ
+.LP
+userPassword: {PBKDF2-SHA256}10000$jq40ImWtmpTE.aYDYV1GfQ$mpiL4ui02ACmYOAnCjp/MI1gQk50xLbZ54RZneU0fCg
+.LP
+userPassword: {PBKDF2-SHA1}10000$QJTEclnXgh9Cz3ChCWpdAg$9.s98jwFJM.NXJK9ca/oJ5AyoAQ
+.EE
+.LP
+To make {PBKDF2-SHA512} the password hash used in Password Modify extended operations,
+simply set this line in slapd.conf(5):
+.EX
+.LP
+password-hash {PBKDF2-SHA512}
+.EX
+
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR ldappasswd (1),
+.BR slappasswd (8),
+.BR ldap (3),
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+
+.SH ACKNOWLEDGEMENTS
+This manual page has been written by Peter Marschall based on the
+module's README file written by HAMANO Tsukasa <hamano@osstech.co.jp>
+.LP
+.B OpenLDAP
+is developed and maintained by The OpenLDAP Project (http://www.openldap.org/).
+.B OpenLDAP
+is derived from University of Michigan LDAP 3.3 Release.
diff --git a/contrib/slapd-modules/passwd/radius.c b/contrib/slapd-modules/passwd/radius.c
new file mode 100644
index 0000000..8474bf5
--- /dev/null
+++ b/contrib/slapd-modules/passwd/radius.c
@@ -0,0 +1,149 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <lber.h>
+#include <lber_pvt.h> /* BER_BVC definition */
+#include "lutil.h"
+#include <ldap_pvt_thread.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include <radlib.h>
+
+extern char *global_host; /* from slapd */
+static LUTIL_PASSWD_CHK_FUNC chk_radius;
+static const struct berval scheme = BER_BVC("{RADIUS}");
+static char *config_filename;
+static ldap_pvt_thread_mutex_t libradius_mutex;
+
+static int
+chk_radius(
+ const struct berval *sc,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text )
+{
+ unsigned int i;
+ int rc = LUTIL_PASSWD_ERR;
+
+ struct rad_handle *h = NULL;
+
+ for ( i = 0; i < cred->bv_len; i++ ) {
+ if ( cred->bv_val[ i ] == '\0' ) {
+ return LUTIL_PASSWD_ERR; /* NUL character in cred */
+ }
+ }
+
+ if ( cred->bv_val[ i ] != '\0' ) {
+ return LUTIL_PASSWD_ERR; /* cred must behave like a string */
+ }
+
+ for ( i = 0; i < passwd->bv_len; i++ ) {
+ if ( passwd->bv_val[ i ] == '\0' ) {
+ return LUTIL_PASSWD_ERR; /* NUL character in password */
+ }
+ }
+
+ if ( passwd->bv_val[ i ] != '\0' ) {
+ return LUTIL_PASSWD_ERR; /* passwd must behave like a string */
+ }
+
+ ldap_pvt_thread_mutex_lock( &libradius_mutex );
+
+ h = rad_auth_open();
+ if ( h == NULL ) {
+ ldap_pvt_thread_mutex_unlock( &libradius_mutex );
+ return LUTIL_PASSWD_ERR;
+ }
+
+ if ( rad_config( h, config_filename ) != 0 ) {
+ goto done;
+ }
+
+ if ( rad_create_request( h, RAD_ACCESS_REQUEST ) ) {
+ goto done;
+ }
+
+ if ( rad_put_string( h, RAD_USER_NAME, passwd->bv_val ) != 0 ) {
+ goto done;
+ }
+
+ if ( rad_put_string( h, RAD_USER_PASSWORD, cred->bv_val ) != 0 ) {
+ goto done;
+ }
+
+ if ( rad_put_string( h, RAD_NAS_IDENTIFIER, global_host ) != 0 ) {
+ goto done;
+ }
+
+ switch ( rad_send_request( h ) ) {
+ case RAD_ACCESS_ACCEPT:
+ rc = LUTIL_PASSWD_OK;
+ break;
+
+ case RAD_ACCESS_REJECT:
+ rc = LUTIL_PASSWD_ERR;
+ break;
+
+ case RAD_ACCESS_CHALLENGE:
+ rc = LUTIL_PASSWD_ERR;
+ break;
+
+ case -1:
+ /* no valid response is received */
+ break;
+ }
+
+done:;
+ rad_close( h );
+
+ ldap_pvt_thread_mutex_unlock( &libradius_mutex );
+ return rc;
+}
+
+int
+term_module()
+{
+ return ldap_pvt_thread_mutex_destroy( &libradius_mutex );
+}
+
+int
+init_module( int argc, char *argv[] )
+{
+ int i;
+
+ for ( i = 0; i < argc; i++ ) {
+ if ( strncasecmp( argv[ i ], "config=", STRLENOF( "config=" ) ) == 0 ) {
+ /* FIXME: what if multiple loads of same module?
+ * does it make sense (e.g. override an existing one)? */
+ if ( config_filename == NULL ) {
+ config_filename = ber_strdup( &argv[ i ][ STRLENOF( "config=" ) ] );
+ }
+
+ } else {
+ fprintf( stderr, "init_module(radius): unknown arg#%d=\"%s\".\n",
+ i, argv[ i ] );
+ return 1;
+ }
+ }
+
+ ldap_pvt_thread_mutex_init( &libradius_mutex );
+
+ return lutil_passwd_add( (struct berval *)&scheme, chk_radius, NULL );
+}
diff --git a/contrib/slapd-modules/passwd/sha2/Makefile b/contrib/slapd-modules/passwd/sha2/Makefile
new file mode 100644
index 0000000..2d20756
--- /dev/null
+++ b/contrib/slapd-modules/passwd/sha2/Makefile
@@ -0,0 +1,59 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS =
+#DEFS = -DSLAPD_SHA2_DEBUG
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = pw-sha2.la
+MANPAGES = slapd-pw-sha2.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+pw-sha2.la: slapd-sha2.lo sha2.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/passwd/sha2/README b/contrib/slapd-modules/passwd/sha2/README
new file mode 100644
index 0000000..bab1dcd
--- /dev/null
+++ b/contrib/slapd-modules/passwd/sha2/README
@@ -0,0 +1,144 @@
+SHA-2 OpenLDAP support
+----------------------
+
+slapd-sha2.c provides support for SSHA-512, SSHA-384, SSHA-256,
+SHA-512, SHA-384 and SHA-256 hashed passwords in OpenLDAP. For
+instance, one could have the LDAP attribute:
+
+userPassword: {SHA512}vSsar3708Jvp9Szi2NWZZ02Bqp1qRCFpbcTZPdBhnWgs5WtNZKnvCXdhztmeD2cmW192CF5bDufKRpayrW/isg==
+
+or:
+
+userPassword: {SHA384}WKd1ukESvjAFrkQHznV9iP2nHUBJe7gCbsrFTU4//HIyzo3jq1rLMK45dg/ufFPt
+
+or:
+
+userPassword: {SHA256}K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=
+
+all of which encode the password 'secret'.
+
+
+Building
+--------
+
+1) Customize the OPENLDAP variable in Makefile to point to the OpenLDAP
+source root.
+
+For initial testing you might also want to edit DEFS to define
+SLAPD_SHA2_DEBUG, which enables logging to stderr (don't leave this on
+in production, as it prints passwords in cleartext).
+
+2) Run 'make' to produce slapd-sha2.so
+
+3) Copy slapd-sha2.so somewhere permanent.
+
+4) Edit your slapd.conf (eg. /etc/ldap/slapd.conf), and add:
+
+moduleload ...path/to/slapd-sha2.so
+
+5) Restart slapd.
+
+
+Configuring
+-----------
+
+The {SSHA256}, {SSHA384}, {SSHA512}, {SSHA256}, {SHA384} and {SHA512}
+password schemes should now be recognised.
+
+You can also tell OpenLDAP to use one of these new schemes when processing LDAP
+Password Modify Extended Operations, thanks to the password-hash option in
+slapd.conf. For example:
+
+password-hash {SSHA512}
+
+
+Testing
+-------
+
+A quick way to test whether it's working is to customize the rootdn and
+rootpw in slapd.conf, eg:
+
+rootdn "cn=admin,dc=example,dc=com"
+# This encrypts the string 'secret'
+
+rootpw {SHA256}K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=
+
+Then to test, run something like:
+
+ldapsearch -b "dc=example,dc=com" -D "cn=admin,dc=example,dc=com" -x -w secret
+
+
+-- Test hashes:
+
+Test hashes can be generated with openssl:
+
+$ echo -n "secret" | openssl dgst -sha256 -binary | openssl enc -base64
+K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=
+$ echo -n "secret" | openssl dgst -sha384 -binary | openssl enc -base64
+WKd1ukESvjAFrkQHznV9iP2nHUBJe7gCbsrFTU4//HIyzo3jq1rLMK45dg/ufFPt
+$ echo -n "secret" | openssl dgst -sha512 -binary | openssl enc -base64
+vSsar3708Jvp9Szi2NWZZ02Bqp1qRCFpbcTZPdBhnWgs5WtNZKnvCXdhztmeD2cm
+W192CF5bDufKRpayrW/isg==
+
+(join those lines up to form the full hash)
+
+
+
+Alternatively we could modify an existing user's password with
+ldappasswd, and then test binding as that user:
+
+$ ldappasswd -D "cn=admin,dc=example,dc=com" -x -W -S uid=jturner,ou=People,dc=example,dc=com
+New password: secret
+Re-enter new password: secret
+Enter LDAP Password: <cn=admin's password>
+
+$ ldapsearch -b "dc=example,dc=com" -D "uid=jturner,ou=People,dc=example,dc=com" -x -w secret
+
+
+Debugging (SHA-512, SHA-384 and SHA-256 only)
+---------------------------------------------
+
+To see what's going on, recompile with SLAPD_SHA2_DEBUG (use the
+commented-out DEFS in Makefile), and then run slapd from the console
+to see stderr:
+
+$ sudo /etc/init.d/slapd stop
+Stopping OpenLDAP: slapd.
+$ sudo /usr/sbin/slapd -f /etc/ldap/slapd.conf -h ldap://localhost:389 -d stats
+@(#) $OpenLDAP$
+ buildd@palmer:/build/buildd/openldap2.3-2.4.9/debian/build/servers/slapd
+slapd starting
+...
+Validating password
+ Hash scheme: {SHA256}
+ Password to validate: secret
+ Password hash: K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=
+ Stored password hash: K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=
+ Result: match
+conn=0 op=0 BIND dn="cn=admin,dc=example,dc=com" mech=SIMPLE ssf=0
+conn=0 op=0 RESULT tag=97 err=0 text=
+conn=0 op=1 SRCH base="dc=example,dc=com" scope=2 deref=0 filter="(objectClass=*)"
+conn=0 fd=12 closed (connection lost)
+
+---
+
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2009-2022 The OpenLDAP Foundation.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
+---
+
+ACKNOWLEDGEMENT:
+This work was initially developed by Jeff Turner for inclusion in
+OpenLDAP Software, based upon the SHA-2 implementation independently
+developed by Aaron Gifford.
+
diff --git a/contrib/slapd-modules/passwd/sha2/sha2.c b/contrib/slapd-modules/passwd/sha2/sha2.c
new file mode 100644
index 0000000..047741a
--- /dev/null
+++ b/contrib/slapd-modules/passwd/sha2/sha2.c
@@ -0,0 +1,1070 @@
+/* $OpenLDAP$ */
+/*
+ * FILE: sha2.c
+ * AUTHOR: Aaron D. Gifford - http://www.aarongifford.com/
+ *
+ * Copyright (c) 2000-2001, Aaron D. Gifford
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sha2.c,v 1.1 2001/11/08 00:01:51 adg Exp adg $
+ */
+
+#include <string.h> /* memcpy()/memset() or bcopy()/bzero() */
+#include <assert.h> /* assert() */
+#include "sha2.h"
+
+/*
+ * ASSERT NOTE:
+ * Some sanity checking code is included using assert(). On my FreeBSD
+ * system, this additional code can be removed by compiling with NDEBUG
+ * defined. Check your own systems manpage on assert() to see how to
+ * compile WITHOUT the sanity checking code on your system.
+ *
+ * UNROLLED TRANSFORM LOOP NOTE:
+ * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform
+ * loop version for the hash transform rounds (defined using macros
+ * later in this file). Either define on the command line, for example:
+ *
+ * cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c
+ *
+ * or define below:
+ *
+ * #define SHA2_UNROLL_TRANSFORM
+ *
+ */
+
+
+/*** SHA-256/384/512 Machine Architecture Definitions *****************/
+/*
+ * BYTE_ORDER NOTE:
+ *
+ * Please make sure that your system defines BYTE_ORDER. If your
+ * architecture is little-endian, make sure it also defines
+ * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are
+ * equivalent.
+ *
+ * If your system does not define the above, then you can do so by
+ * hand like this:
+ *
+ * #define LITTLE_ENDIAN 1234
+ * #define BIG_ENDIAN 4321
+ *
+ * And for little-endian machines, add:
+ *
+ * #define BYTE_ORDER LITTLE_ENDIAN
+ *
+ * Or for big-endian machines:
+ *
+ * #define BYTE_ORDER BIG_ENDIAN
+ *
+ * The FreeBSD machine this was written on defines BYTE_ORDER
+ * appropriately by including <sys/types.h> (which in turn includes
+ * <machine/endian.h> where the appropriate definitions are actually
+ * made).
+ */
+#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN)
+#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN
+#endif
+
+/*
+ * Define the followingsha2_* types to types of the correct length on
+ * the native architecture. Most BSD systems and Linux define u_intXX_t
+ * types. Machines with very recent ANSI C headers, can use the
+ * uintXX_t definitions from inttypes.h by defining SHA2_USE_INTTYPES_H
+ * during compile or in the sha.h header file.
+ *
+ * Machines that support neither u_intXX_t nor inttypes.h's uintXX_t
+ * will need to define these three typedefs below (and the appropriate
+ * ones in sha.h too) by hand according to their system architecture.
+ *
+ * Thank you, Jun-ichiro itojun Hagino, for suggesting using u_intXX_t
+ * types and pointing out recent ANSI C support for uintXX_t in inttypes.h.
+ */
+#ifdef SHA2_USE_INTTYPES_H
+
+typedef uint8_t sha2_byte; /* Exactly 1 byte */
+typedef uint32_t sha2_word32; /* Exactly 4 bytes */
+typedef uint64_t sha2_word64; /* Exactly 8 bytes */
+
+#else /* SHA2_USE_INTTYPES_H */
+
+typedef u_int8_t sha2_byte; /* Exactly 1 byte */
+typedef u_int32_t sha2_word32; /* Exactly 4 bytes */
+typedef u_int64_t sha2_word64; /* Exactly 8 bytes */
+
+#endif /* SHA2_USE_INTTYPES_H */
+
+
+/*** SHA-256/384/512 Various Length Definitions ***********************/
+/* NOTE: Most of these are in sha2.h */
+#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8)
+#define SHA384_SHORT_BLOCK_LENGTH (SHA384_BLOCK_LENGTH - 16)
+#define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16)
+
+
+/*** ENDIAN REVERSAL MACROS *******************************************/
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define REVERSE32(w,x) { \
+ sha2_word32 tmp = (w); \
+ tmp = (tmp >> 16) | (tmp << 16); \
+ (x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \
+}
+#define REVERSE64(w,x) { \
+ sha2_word64 tmp = (w); \
+ tmp = (tmp >> 32) | (tmp << 32); \
+ tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \
+ ((tmp & 0x00ff00ff00ff00ffULL) << 8); \
+ (x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \
+ ((tmp & 0x0000ffff0000ffffULL) << 16); \
+}
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+/*
+ * Macro for incrementally adding the unsigned 64-bit integer n to the
+ * unsigned 128-bit integer (represented using a two-element array of
+ * 64-bit words):
+ */
+#define ADDINC128(w,n) { \
+ (w)[0] += (sha2_word64)(n); \
+ if ((w)[0] < (n)) { \
+ (w)[1]++; \
+ } \
+}
+
+/*
+ * Macros for copying blocks of memory and for zeroing out ranges
+ * of memory. Using these macros makes it easy to switch from
+ * using memset()/memcpy() and using bzero()/bcopy().
+ *
+ * Please define either SHA2_USE_MEMSET_MEMCPY or define
+ * SHA2_USE_BZERO_BCOPY depending on which function set you
+ * choose to use:
+ */
+#if !defined(SHA2_USE_MEMSET_MEMCPY) && !defined(SHA2_USE_BZERO_BCOPY)
+/* Default to memset()/memcpy() if no option is specified */
+#define SHA2_USE_MEMSET_MEMCPY 1
+#endif
+#if defined(SHA2_USE_MEMSET_MEMCPY) && defined(SHA2_USE_BZERO_BCOPY)
+/* Abort with an error if BOTH options are defined */
+#error Define either SHA2_USE_MEMSET_MEMCPY or SHA2_USE_BZERO_BCOPY, not both!
+#endif
+
+#ifdef SHA2_USE_MEMSET_MEMCPY
+#define MEMSET_BZERO(p,l) memset((p), 0, (l))
+#define MEMCPY_BCOPY(d,s,l) memcpy((d), (s), (l))
+#endif
+#ifdef SHA2_USE_BZERO_BCOPY
+#define MEMSET_BZERO(p,l) bzero((p), (l))
+#define MEMCPY_BCOPY(d,s,l) bcopy((s), (d), (l))
+#endif
+
+
+/*** THE SIX LOGICAL FUNCTIONS ****************************************/
+/*
+ * Bit shifting and rotation (used by the six SHA-XYZ logical functions:
+ *
+ * NOTE: The naming of R and S appears backwards here (R is a SHIFT and
+ * S is a ROTATION) because the SHA-256/384/512 description document
+ * (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this
+ * same "backwards" definition.
+ */
+/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */
+#define R(b,x) ((x) >> (b))
+/* 32-bit Rotate-right (used in SHA-256): */
+#define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b))))
+/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */
+#define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b))))
+
+/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */
+#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z)))
+#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+
+/* Four of six logical functions used in SHA-256: */
+#define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x)))
+#define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x)))
+#define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x)))
+#define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x)))
+
+/* Four of six logical functions used in SHA-384 and SHA-512: */
+#define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x)))
+#define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x)))
+#define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7, (x)))
+#define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ R( 6, (x)))
+
+/*** INTERNAL FUNCTION PROTOTYPES *************************************/
+/* NOTE: These should not be accessed directly from outside this
+ * library -- they are intended for private internal visibility/use
+ * only.
+ */
+static void SHA512_Last(SHA512_CTX*);
+static void SHA256_Transform(SHA256_CTX*, const sha2_word32*);
+static void SHA512_Transform(SHA512_CTX*, const sha2_word64*);
+
+
+/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/
+/* Hash constant words K for SHA-256: */
+const static sha2_word32 K256[64] = {
+ 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
+ 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
+ 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
+ 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
+ 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+ 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
+ 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
+ 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
+ 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
+ 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+ 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
+ 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
+ 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
+ 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
+ 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+ 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+/* Initial hash value H for SHA-256: */
+const static sha2_word32 sha256_initial_hash_value[8] = {
+ 0x6a09e667UL,
+ 0xbb67ae85UL,
+ 0x3c6ef372UL,
+ 0xa54ff53aUL,
+ 0x510e527fUL,
+ 0x9b05688cUL,
+ 0x1f83d9abUL,
+ 0x5be0cd19UL
+};
+
+/* Hash constant words K for SHA-384 and SHA-512: */
+const static sha2_word64 K512[80] = {
+ 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
+ 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
+ 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
+ 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
+ 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
+ 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
+ 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
+ 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
+ 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
+ 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
+ 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
+ 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
+ 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
+ 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
+ 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
+ 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
+ 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
+ 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
+ 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
+ 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
+ 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
+ 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
+ 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
+ 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
+ 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
+ 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
+ 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
+ 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
+ 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
+ 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
+ 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
+ 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
+ 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
+ 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
+ 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
+ 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
+ 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
+ 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
+ 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
+ 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
+};
+
+/* Initial hash value H for SHA-384 */
+const static sha2_word64 sha384_initial_hash_value[8] = {
+ 0xcbbb9d5dc1059ed8ULL,
+ 0x629a292a367cd507ULL,
+ 0x9159015a3070dd17ULL,
+ 0x152fecd8f70e5939ULL,
+ 0x67332667ffc00b31ULL,
+ 0x8eb44a8768581511ULL,
+ 0xdb0c2e0d64f98fa7ULL,
+ 0x47b5481dbefa4fa4ULL
+};
+
+/* Initial hash value H for SHA-512 */
+const static sha2_word64 sha512_initial_hash_value[8] = {
+ 0x6a09e667f3bcc908ULL,
+ 0xbb67ae8584caa73bULL,
+ 0x3c6ef372fe94f82bULL,
+ 0xa54ff53a5f1d36f1ULL,
+ 0x510e527fade682d1ULL,
+ 0x9b05688c2b3e6c1fULL,
+ 0x1f83d9abfb41bd6bULL,
+ 0x5be0cd19137e2179ULL
+};
+
+/*
+ * Constant used by SHA256/384/512_End() functions for converting the
+ * digest to a readable hexadecimal character string:
+ */
+static const char *sha2_hex_digits = "0123456789abcdef";
+
+
+/*** SHA-256: *********************************************************/
+void SHA256_Init(SHA256_CTX* context) {
+ if (context == (SHA256_CTX*)0) {
+ return;
+ }
+ MEMCPY_BCOPY(context->state, sha256_initial_hash_value, SHA256_DIGEST_LENGTH);
+ MEMSET_BZERO(context->buffer, SHA256_BLOCK_LENGTH);
+ context->bitcount = 0;
+}
+
+#ifdef SHA2_UNROLL_TRANSFORM
+
+/* Unrolled SHA-256 round macros: */
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \
+ REVERSE32(*data++, W256[j]); \
+ T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \
+ K256[j] + W256[j]; \
+ (d) += T1; \
+ (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
+ j++
+
+
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \
+ T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \
+ K256[j] + (W256[j] = *data++); \
+ (d) += T1; \
+ (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
+ j++
+
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#define ROUND256(a,b,c,d,e,f,g,h) \
+ s0 = W256[(j+1)&0x0f]; \
+ s0 = sigma0_256(s0); \
+ s1 = W256[(j+14)&0x0f]; \
+ s1 = sigma1_256(s1); \
+ T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + \
+ (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \
+ (d) += T1; \
+ (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
+ j++
+
+void SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) {
+ sha2_word32 a, b, c, d, e, f, g, h, s0, s1;
+ sha2_word32 T1, *W256;
+ int j;
+
+ W256 = (sha2_word32*)context->buffer;
+
+ /* Initialize registers with the prev. intermediate value */
+ a = context->state[0];
+ b = context->state[1];
+ c = context->state[2];
+ d = context->state[3];
+ e = context->state[4];
+ f = context->state[5];
+ g = context->state[6];
+ h = context->state[7];
+
+ j = 0;
+ do {
+ /* Rounds 0 to 15 (unrolled): */
+ ROUND256_0_TO_15(a,b,c,d,e,f,g,h);
+ ROUND256_0_TO_15(h,a,b,c,d,e,f,g);
+ ROUND256_0_TO_15(g,h,a,b,c,d,e,f);
+ ROUND256_0_TO_15(f,g,h,a,b,c,d,e);
+ ROUND256_0_TO_15(e,f,g,h,a,b,c,d);
+ ROUND256_0_TO_15(d,e,f,g,h,a,b,c);
+ ROUND256_0_TO_15(c,d,e,f,g,h,a,b);
+ ROUND256_0_TO_15(b,c,d,e,f,g,h,a);
+ } while (j < 16);
+
+ /* Now for the remaining rounds to 64: */
+ do {
+ ROUND256(a,b,c,d,e,f,g,h);
+ ROUND256(h,a,b,c,d,e,f,g);
+ ROUND256(g,h,a,b,c,d,e,f);
+ ROUND256(f,g,h,a,b,c,d,e);
+ ROUND256(e,f,g,h,a,b,c,d);
+ ROUND256(d,e,f,g,h,a,b,c);
+ ROUND256(c,d,e,f,g,h,a,b);
+ ROUND256(b,c,d,e,f,g,h,a);
+ } while (j < 64);
+
+ /* Compute the current intermediate hash value */
+ context->state[0] += a;
+ context->state[1] += b;
+ context->state[2] += c;
+ context->state[3] += d;
+ context->state[4] += e;
+ context->state[5] += f;
+ context->state[6] += g;
+ context->state[7] += h;
+
+ /* Clean up */
+ a = b = c = d = e = f = g = h = T1 = 0;
+}
+
+#else /* SHA2_UNROLL_TRANSFORM */
+
+void SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) {
+ sha2_word32 a, b, c, d, e, f, g, h, s0, s1;
+ sha2_word32 T1, T2, *W256;
+ int j;
+
+ W256 = (sha2_word32*)context->buffer;
+
+ /* Initialize registers with the prev. intermediate value */
+ a = context->state[0];
+ b = context->state[1];
+ c = context->state[2];
+ d = context->state[3];
+ e = context->state[4];
+ f = context->state[5];
+ g = context->state[6];
+ h = context->state[7];
+
+ j = 0;
+ do {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ /* Copy data while converting to host byte order */
+ REVERSE32(*data++,W256[j]);
+ /* Apply the SHA-256 compression function to update a..h */
+ T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j];
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+ /* Apply the SHA-256 compression function to update a..h with copy */
+ T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + (W256[j] = *data++);
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+ T2 = Sigma0_256(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+
+ j++;
+ } while (j < 16);
+
+ do {
+ /* Part of the message block expansion: */
+ s0 = W256[(j+1)&0x0f];
+ s0 = sigma0_256(s0);
+ s1 = W256[(j+14)&0x0f];
+ s1 = sigma1_256(s1);
+
+ /* Apply the SHA-256 compression function to update a..h */
+ T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] +
+ (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0);
+ T2 = Sigma0_256(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+
+ j++;
+ } while (j < 64);
+
+ /* Compute the current intermediate hash value */
+ context->state[0] += a;
+ context->state[1] += b;
+ context->state[2] += c;
+ context->state[3] += d;
+ context->state[4] += e;
+ context->state[5] += f;
+ context->state[6] += g;
+ context->state[7] += h;
+
+ /* Clean up */
+ a = b = c = d = e = f = g = h = T1 = T2 = 0;
+}
+
+#endif /* SHA2_UNROLL_TRANSFORM */
+
+void SHA256_Update(SHA256_CTX* context, const sha2_byte *data, size_t len) {
+ unsigned int freespace, usedspace;
+
+ if (len == 0) {
+ /* Calling with no data is valid - we do nothing */
+ return;
+ }
+
+ /* Sanity check: */
+ assert(context != (SHA256_CTX*)0 && data != (sha2_byte*)0);
+
+ usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;
+ if (usedspace > 0) {
+ /* Calculate how much free space is available in the buffer */
+ freespace = SHA256_BLOCK_LENGTH - usedspace;
+
+ if (len >= freespace) {
+ /* Fill the buffer completely and process it */
+ MEMCPY_BCOPY(&context->buffer[usedspace], data, freespace);
+ context->bitcount += freespace << 3;
+ len -= freespace;
+ data += freespace;
+ SHA256_Transform(context, (sha2_word32*)context->buffer);
+ } else {
+ /* The buffer is not yet full */
+ MEMCPY_BCOPY(&context->buffer[usedspace], data, len);
+ context->bitcount += len << 3;
+ /* Clean up: */
+ usedspace = freespace = 0;
+ return;
+ }
+ }
+ while (len >= SHA256_BLOCK_LENGTH) {
+ /* Process as many complete blocks as we can */
+ SHA256_Transform(context, (sha2_word32*)data);
+ context->bitcount += SHA256_BLOCK_LENGTH << 3;
+ len -= SHA256_BLOCK_LENGTH;
+ data += SHA256_BLOCK_LENGTH;
+ }
+ if (len > 0) {
+ /* There's left-overs, so save 'em */
+ MEMCPY_BCOPY(context->buffer, data, len);
+ context->bitcount += len << 3;
+ }
+ /* Clean up: */
+ usedspace = freespace = 0;
+}
+
+void SHA256_Final(sha2_byte digest[], SHA256_CTX* context) {
+ sha2_word32 *d = (sha2_word32*)digest;
+ sha2_word64 *p;
+ unsigned int usedspace;
+
+ /* Sanity check: */
+ assert(context != (SHA256_CTX*)0);
+
+ /* If no digest buffer is passed, we don't bother doing this: */
+ if (digest != (sha2_byte*)0) {
+ usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ /* Convert FROM host byte order */
+ REVERSE64(context->bitcount,context->bitcount);
+#endif
+ if (usedspace > 0) {
+ /* Begin padding with a 1 bit: */
+ context->buffer[usedspace++] = 0x80;
+
+ if (usedspace <= SHA256_SHORT_BLOCK_LENGTH) {
+ /* Set-up for the last transform: */
+ MEMSET_BZERO(&context->buffer[usedspace], SHA256_SHORT_BLOCK_LENGTH - usedspace);
+ } else {
+ if (usedspace < SHA256_BLOCK_LENGTH) {
+ MEMSET_BZERO(&context->buffer[usedspace], SHA256_BLOCK_LENGTH - usedspace);
+ }
+ /* Do second-to-last transform: */
+ SHA256_Transform(context, (sha2_word32*)context->buffer);
+
+ /* And set-up for the last transform: */
+ MEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH);
+ }
+ } else {
+ /* Set-up for the last transform: */
+ MEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH);
+
+ /* Begin padding with a 1 bit: */
+ *context->buffer = 0x80;
+ }
+ /* Set the bit count: */
+ p = (sha2_word64 *)&context->buffer[SHA256_SHORT_BLOCK_LENGTH];
+ *p = context->bitcount;
+
+ /* Final transform: */
+ SHA256_Transform(context, (sha2_word32*)context->buffer);
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+ {
+ /* Convert TO host byte order */
+ int j;
+ for (j = 0; j < 8; j++) {
+ REVERSE32(context->state[j],context->state[j]);
+ *d++ = context->state[j];
+ }
+ }
+#else
+ MEMCPY_BCOPY(d, context->state, SHA256_DIGEST_LENGTH);
+#endif
+ }
+
+ /* Clean up state data: */
+ MEMSET_BZERO(context, sizeof(*context));
+ usedspace = 0;
+}
+
+char *SHA256_End(SHA256_CTX* context, char buffer[]) {
+ sha2_byte digest[SHA256_DIGEST_LENGTH], *d = digest;
+ int i;
+
+ /* Sanity check: */
+ assert(context != (SHA256_CTX*)0);
+
+ if (buffer != (char*)0) {
+ SHA256_Final(digest, context);
+
+ for (i = 0; i < SHA256_DIGEST_LENGTH; i++) {
+ *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4];
+ *buffer++ = sha2_hex_digits[*d & 0x0f];
+ d++;
+ }
+ *buffer = (char)0;
+ } else {
+ MEMSET_BZERO(context, sizeof(*context));
+ }
+ MEMSET_BZERO(digest, SHA256_DIGEST_LENGTH);
+ return buffer;
+}
+
+char* SHA256_Data(const sha2_byte* data, size_t len, char digest[SHA256_DIGEST_STRING_LENGTH]) {
+ SHA256_CTX context;
+
+ SHA256_Init(&context);
+ SHA256_Update(&context, data, len);
+ return SHA256_End(&context, digest);
+}
+
+
+/*** SHA-512: *********************************************************/
+void SHA512_Init(SHA512_CTX* context) {
+ if (context == (SHA512_CTX*)0) {
+ return;
+ }
+ MEMCPY_BCOPY(context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH);
+ MEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH);
+ context->bitcount[0] = context->bitcount[1] = 0;
+}
+
+#ifdef SHA2_UNROLL_TRANSFORM
+
+/* Unrolled SHA-512 round macros: */
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \
+ REVERSE64(*data++, W512[j]); \
+ T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \
+ K512[j] + W512[j]; \
+ (d) += T1, \
+ (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)), \
+ j++
+
+
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \
+ T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \
+ K512[j] + (W512[j] = *data++); \
+ (d) += T1; \
+ (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \
+ j++
+
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#define ROUND512(a,b,c,d,e,f,g,h) \
+ s0 = W512[(j+1)&0x0f]; \
+ s0 = sigma0_512(s0); \
+ s1 = W512[(j+14)&0x0f]; \
+ s1 = sigma1_512(s1); \
+ T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + K512[j] + \
+ (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \
+ (d) += T1; \
+ (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \
+ j++
+
+void SHA512_Transform(SHA512_CTX* context, const sha2_word64* data) {
+ sha2_word64 a, b, c, d, e, f, g, h, s0, s1;
+ sha2_word64 T1, *W512 = (sha2_word64*)context->buffer;
+ int j;
+
+ /* Initialize registers with the prev. intermediate value */
+ a = context->state[0];
+ b = context->state[1];
+ c = context->state[2];
+ d = context->state[3];
+ e = context->state[4];
+ f = context->state[5];
+ g = context->state[6];
+ h = context->state[7];
+
+ j = 0;
+ do {
+ ROUND512_0_TO_15(a,b,c,d,e,f,g,h);
+ ROUND512_0_TO_15(h,a,b,c,d,e,f,g);
+ ROUND512_0_TO_15(g,h,a,b,c,d,e,f);
+ ROUND512_0_TO_15(f,g,h,a,b,c,d,e);
+ ROUND512_0_TO_15(e,f,g,h,a,b,c,d);
+ ROUND512_0_TO_15(d,e,f,g,h,a,b,c);
+ ROUND512_0_TO_15(c,d,e,f,g,h,a,b);
+ ROUND512_0_TO_15(b,c,d,e,f,g,h,a);
+ } while (j < 16);
+
+ /* Now for the remaining rounds up to 79: */
+ do {
+ ROUND512(a,b,c,d,e,f,g,h);
+ ROUND512(h,a,b,c,d,e,f,g);
+ ROUND512(g,h,a,b,c,d,e,f);
+ ROUND512(f,g,h,a,b,c,d,e);
+ ROUND512(e,f,g,h,a,b,c,d);
+ ROUND512(d,e,f,g,h,a,b,c);
+ ROUND512(c,d,e,f,g,h,a,b);
+ ROUND512(b,c,d,e,f,g,h,a);
+ } while (j < 80);
+
+ /* Compute the current intermediate hash value */
+ context->state[0] += a;
+ context->state[1] += b;
+ context->state[2] += c;
+ context->state[3] += d;
+ context->state[4] += e;
+ context->state[5] += f;
+ context->state[6] += g;
+ context->state[7] += h;
+
+ /* Clean up */
+ a = b = c = d = e = f = g = h = T1 = 0;
+}
+
+#else /* SHA2_UNROLL_TRANSFORM */
+
+void SHA512_Transform(SHA512_CTX* context, const sha2_word64* data) {
+ sha2_word64 a, b, c, d, e, f, g, h, s0, s1;
+ sha2_word64 T1, T2, *W512 = (sha2_word64*)context->buffer;
+ int j;
+
+ /* Initialize registers with the prev. intermediate value */
+ a = context->state[0];
+ b = context->state[1];
+ c = context->state[2];
+ d = context->state[3];
+ e = context->state[4];
+ f = context->state[5];
+ g = context->state[6];
+ h = context->state[7];
+
+ j = 0;
+ do {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ /* Convert TO host byte order */
+ REVERSE64(*data++, W512[j]);
+ /* Apply the SHA-512 compression function to update a..h */
+ T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j];
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+ /* Apply the SHA-512 compression function to update a..h with copy */
+ T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + (W512[j] = *data++);
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+ T2 = Sigma0_512(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+
+ j++;
+ } while (j < 16);
+
+ do {
+ /* Part of the message block expansion: */
+ s0 = W512[(j+1)&0x0f];
+ s0 = sigma0_512(s0);
+ s1 = W512[(j+14)&0x0f];
+ s1 = sigma1_512(s1);
+
+ /* Apply the SHA-512 compression function to update a..h */
+ T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] +
+ (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0);
+ T2 = Sigma0_512(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+
+ j++;
+ } while (j < 80);
+
+ /* Compute the current intermediate hash value */
+ context->state[0] += a;
+ context->state[1] += b;
+ context->state[2] += c;
+ context->state[3] += d;
+ context->state[4] += e;
+ context->state[5] += f;
+ context->state[6] += g;
+ context->state[7] += h;
+
+ /* Clean up */
+ a = b = c = d = e = f = g = h = T1 = T2 = 0;
+}
+
+#endif /* SHA2_UNROLL_TRANSFORM */
+
+void SHA512_Update(SHA512_CTX* context, const sha2_byte *data, size_t len) {
+ unsigned int freespace, usedspace;
+
+ if (len == 0) {
+ /* Calling with no data is valid - we do nothing */
+ return;
+ }
+
+ /* Sanity check: */
+ assert(context != (SHA512_CTX*)0 && data != (sha2_byte*)0);
+
+ usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
+ if (usedspace > 0) {
+ /* Calculate how much free space is available in the buffer */
+ freespace = SHA512_BLOCK_LENGTH - usedspace;
+
+ if (len >= freespace) {
+ /* Fill the buffer completely and process it */
+ MEMCPY_BCOPY(&context->buffer[usedspace], data, freespace);
+ ADDINC128(context->bitcount, freespace << 3);
+ len -= freespace;
+ data += freespace;
+ SHA512_Transform(context, (sha2_word64*)context->buffer);
+ } else {
+ /* The buffer is not yet full */
+ MEMCPY_BCOPY(&context->buffer[usedspace], data, len);
+ ADDINC128(context->bitcount, len << 3);
+ /* Clean up: */
+ usedspace = freespace = 0;
+ return;
+ }
+ }
+ while (len >= SHA512_BLOCK_LENGTH) {
+ /* Process as many complete blocks as we can */
+ SHA512_Transform(context, (sha2_word64*)data);
+ ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3);
+ len -= SHA512_BLOCK_LENGTH;
+ data += SHA512_BLOCK_LENGTH;
+ }
+ if (len > 0) {
+ /* There's left-overs, so save 'em */
+ MEMCPY_BCOPY(context->buffer, data, len);
+ ADDINC128(context->bitcount, len << 3);
+ }
+ /* Clean up: */
+ usedspace = freespace = 0;
+}
+
+void SHA512_Last(SHA512_CTX* context) {
+ sha2_word64 *p;
+ unsigned int usedspace;
+
+ usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ /* Convert FROM host byte order */
+ REVERSE64(context->bitcount[0],context->bitcount[0]);
+ REVERSE64(context->bitcount[1],context->bitcount[1]);
+#endif
+ if (usedspace > 0) {
+ /* Begin padding with a 1 bit: */
+ context->buffer[usedspace++] = 0x80;
+
+ if (usedspace <= SHA512_SHORT_BLOCK_LENGTH) {
+ /* Set-up for the last transform: */
+ MEMSET_BZERO(&context->buffer[usedspace], SHA512_SHORT_BLOCK_LENGTH - usedspace);
+ } else {
+ if (usedspace < SHA512_BLOCK_LENGTH) {
+ MEMSET_BZERO(&context->buffer[usedspace], SHA512_BLOCK_LENGTH - usedspace);
+ }
+ /* Do second-to-last transform: */
+ SHA512_Transform(context, (sha2_word64*)context->buffer);
+
+ /* And set-up for the last transform: */
+ MEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH - 2);
+ }
+ } else {
+ /* Prepare for final transform: */
+ MEMSET_BZERO(context->buffer, SHA512_SHORT_BLOCK_LENGTH);
+
+ /* Begin padding with a 1 bit: */
+ *context->buffer = 0x80;
+ }
+ /* Store the length of input data (in bits): */
+ p = (sha2_word64 *)&context->buffer[SHA512_SHORT_BLOCK_LENGTH];
+ p[0] = context->bitcount[1];
+ p[1] = context->bitcount[0];
+
+ /* Final transform: */
+ SHA512_Transform(context, (sha2_word64*)context->buffer);
+}
+
+void SHA512_Final(sha2_byte digest[], SHA512_CTX* context) {
+ sha2_word64 *d = (sha2_word64*)digest;
+
+ /* Sanity check: */
+ assert(context != (SHA512_CTX*)0);
+
+ /* If no digest buffer is passed, we don't bother doing this: */
+ if (digest != (sha2_byte*)0) {
+ SHA512_Last(context);
+
+ /* Save the hash data for output: */
+#if BYTE_ORDER == LITTLE_ENDIAN
+ {
+ /* Convert TO host byte order */
+ int j;
+ for (j = 0; j < 8; j++) {
+ REVERSE64(context->state[j],context->state[j]);
+ *d++ = context->state[j];
+ }
+ }
+#else
+ MEMCPY_BCOPY(d, context->state, SHA512_DIGEST_LENGTH);
+#endif
+ }
+
+ /* Zero out state data */
+ MEMSET_BZERO(context, sizeof(*context));
+}
+
+char *SHA512_End(SHA512_CTX* context, char buffer[]) {
+ sha2_byte digest[SHA512_DIGEST_LENGTH], *d = digest;
+ int i;
+
+ /* Sanity check: */
+ assert(context != (SHA512_CTX*)0);
+
+ if (buffer != (char*)0) {
+ SHA512_Final(digest, context);
+
+ for (i = 0; i < SHA512_DIGEST_LENGTH; i++) {
+ *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4];
+ *buffer++ = sha2_hex_digits[*d & 0x0f];
+ d++;
+ }
+ *buffer = (char)0;
+ } else {
+ MEMSET_BZERO(context, sizeof(*context));
+ }
+ MEMSET_BZERO(digest, SHA512_DIGEST_LENGTH);
+ return buffer;
+}
+
+char* SHA512_Data(const sha2_byte* data, size_t len, char digest[SHA512_DIGEST_STRING_LENGTH]) {
+ SHA512_CTX context;
+
+ SHA512_Init(&context);
+ SHA512_Update(&context, data, len);
+ return SHA512_End(&context, digest);
+}
+
+
+/*** SHA-384: *********************************************************/
+void SHA384_Init(SHA384_CTX* context) {
+ if (context == (SHA384_CTX*)0) {
+ return;
+ }
+ MEMCPY_BCOPY(context->state, sha384_initial_hash_value, SHA512_DIGEST_LENGTH);
+ MEMSET_BZERO(context->buffer, SHA384_BLOCK_LENGTH);
+ context->bitcount[0] = context->bitcount[1] = 0;
+}
+
+void SHA384_Update(SHA384_CTX* context, const sha2_byte* data, size_t len) {
+ SHA512_Update((SHA512_CTX*)context, data, len);
+}
+
+void SHA384_Final(sha2_byte digest[], SHA384_CTX* context) {
+ sha2_word64 *d = (sha2_word64*)digest;
+
+ /* Sanity check: */
+ assert(context != (SHA384_CTX*)0);
+
+ /* If no digest buffer is passed, we don't bother doing this: */
+ if (digest != (sha2_byte*)0) {
+ SHA512_Last((SHA512_CTX*)context);
+
+ /* Save the hash data for output: */
+#if BYTE_ORDER == LITTLE_ENDIAN
+ {
+ /* Convert TO host byte order */
+ int j;
+ for (j = 0; j < 6; j++) {
+ REVERSE64(context->state[j],context->state[j]);
+ *d++ = context->state[j];
+ }
+ }
+#else
+ MEMCPY_BCOPY(d, context->state, SHA384_DIGEST_LENGTH);
+#endif
+ }
+
+ /* Zero out state data */
+ MEMSET_BZERO(context, sizeof(*context));
+}
+
+char *SHA384_End(SHA384_CTX* context, char buffer[]) {
+ sha2_byte digest[SHA384_DIGEST_LENGTH], *d = digest;
+ int i;
+
+ /* Sanity check: */
+ assert(context != (SHA384_CTX*)0);
+
+ if (buffer != (char*)0) {
+ SHA384_Final(digest, context);
+
+ for (i = 0; i < SHA384_DIGEST_LENGTH; i++) {
+ *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4];
+ *buffer++ = sha2_hex_digits[*d & 0x0f];
+ d++;
+ }
+ *buffer = (char)0;
+ } else {
+ MEMSET_BZERO(context, sizeof(*context));
+ }
+ MEMSET_BZERO(digest, SHA384_DIGEST_LENGTH);
+ return buffer;
+}
+
+char* SHA384_Data(const sha2_byte* data, size_t len, char digest[SHA384_DIGEST_STRING_LENGTH]) {
+ SHA384_CTX context;
+
+ SHA384_Init(&context);
+ SHA384_Update(&context, data, len);
+ return SHA384_End(&context, digest);
+}
+
diff --git a/contrib/slapd-modules/passwd/sha2/sha2.h b/contrib/slapd-modules/passwd/sha2/sha2.h
new file mode 100644
index 0000000..7fff142
--- /dev/null
+++ b/contrib/slapd-modules/passwd/sha2/sha2.h
@@ -0,0 +1,236 @@
+/* $OpenLDAP$ */
+/*
+ * FILE: sha2.h
+ * AUTHOR: Aaron D. Gifford - http://www.aarongifford.com/
+ *
+ * Copyright (c) 2000-2001, Aaron D. Gifford
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $
+ */
+
+#ifndef __SHA2_H__
+#define __SHA2_H__
+
+#include "portable.h"
+
+#ifdef HAVE_INTTYPES_H
+# define SHA2_USE_INTTYPES_H 1
+#endif
+
+#ifndef LITTLE_ENDIAN
+# define LITTLE_ENDIAN 1234
+#endif
+#ifndef BIG_ENDIAN
+# define BIG_ENDIAN 4321
+#endif
+#ifndef BYTE_ORDER
+# ifdef WORDS_BIGENDIAN
+# define BYTE_ORDER BIG_ENDIAN
+# else
+# define BYTE_ORDER LITTLE_ENDIAN
+# endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Import u_intXX_t size_t type definitions from system headers. You
+ * may need to change this, or define these things yourself in this
+ * file.
+ */
+#include <sys/types.h>
+
+#ifdef SHA2_USE_INTTYPES_H
+
+#include <inttypes.h>
+
+#endif /* SHA2_USE_INTTYPES_H */
+
+
+/*** SHA-256/384/512 Various Length Definitions ***********************/
+#define SHA256_BLOCK_LENGTH 64
+#define SHA256_DIGEST_LENGTH 32
+#define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1)
+#define SHA384_BLOCK_LENGTH 128
+#define SHA384_DIGEST_LENGTH 48
+#define SHA384_DIGEST_STRING_LENGTH (SHA384_DIGEST_LENGTH * 2 + 1)
+#define SHA512_BLOCK_LENGTH 128
+#define SHA512_DIGEST_LENGTH 64
+#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1)
+
+
+/*** SHA-256/384/512 Context Structures *******************************/
+/* NOTE: If your architecture does not define either u_intXX_t types or
+ * uintXX_t (from inttypes.h), you may need to define things by hand
+ * for your system:
+ */
+#if 0
+typedef unsigned char u_int8_t; /* 1-byte (8-bits) */
+typedef unsigned int u_int32_t; /* 4-bytes (32-bits) */
+typedef unsigned long long u_int64_t; /* 8-bytes (64-bits) */
+#endif
+/*
+ * Most BSD systems already define u_intXX_t types, as does Linux.
+ * Some systems, however, like Compaq's Tru64 Unix instead can use
+ * uintXX_t types defined by very recent ANSI C standards and included
+ * in the file:
+ *
+ * #include <inttypes.h>
+ *
+ * If you choose to use <inttypes.h> then please define:
+ *
+ * #define SHA2_USE_INTTYPES_H
+ *
+ * Or on the command line during compile:
+ *
+ * cc -DSHA2_USE_INTTYPES_H ...
+ */
+#ifdef SHA2_USE_INTTYPES_H
+
+typedef struct _SHA256_CTX {
+ uint32_t state[8];
+ uint64_t bitcount;
+ uint8_t buffer[SHA256_BLOCK_LENGTH];
+} SHA256_CTX;
+typedef struct _SHA512_CTX {
+ uint64_t state[8];
+ uint64_t bitcount[2];
+ uint8_t buffer[SHA512_BLOCK_LENGTH];
+} SHA512_CTX;
+
+#else /* SHA2_USE_INTTYPES_H */
+
+typedef struct _SHA256_CTX {
+ u_int32_t state[8];
+ u_int64_t bitcount;
+ u_int8_t buffer[SHA256_BLOCK_LENGTH];
+} SHA256_CTX;
+typedef struct _SHA512_CTX {
+ u_int64_t state[8];
+ u_int64_t bitcount[2];
+ u_int8_t buffer[SHA512_BLOCK_LENGTH];
+} SHA512_CTX;
+
+#endif /* SHA2_USE_INTTYPES_H */
+
+typedef SHA512_CTX SHA384_CTX;
+
+
+/*** SHA-256/384/512 Function Prototypes ******************************/
+/* avoid symbol clash with other crypto libs */
+#define SHA256_Init pw_SHA256_Init
+#define SHA256_Update pw_SHA256_Update
+#define SHA256_Final pw_SHA256_Final
+#define SHA256_End pw_SHA256_End
+#define SHA256_Data pw_SHA256_Data
+
+#define SHA384_Init pw_SHA384_Init
+#define SHA384_Update pw_SHA384_Update
+#define SHA384_Final pw_SHA384_Final
+#define SHA384_End pw_SHA384_End
+#define SHA384_Data pw_SHA384_Data
+
+#define SHA512_Init pw_SHA512_Init
+#define SHA512_Update pw_SHA512_Update
+#define SHA512_Final pw_SHA512_Final
+#define SHA512_End pw_SHA512_End
+#define SHA512_Data pw_SHA512_Data
+
+#ifndef NOPROTO
+#ifdef SHA2_USE_INTTYPES_H
+
+void SHA256_Init(SHA256_CTX *);
+void SHA256_Update(SHA256_CTX*, const uint8_t*, size_t);
+void SHA256_Final(uint8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*);
+char* SHA256_End(SHA256_CTX*, char[SHA256_DIGEST_STRING_LENGTH]);
+char* SHA256_Data(const uint8_t*, size_t, char[SHA256_DIGEST_STRING_LENGTH]);
+
+void SHA384_Init(SHA384_CTX*);
+void SHA384_Update(SHA384_CTX*, const uint8_t*, size_t);
+void SHA384_Final(uint8_t[SHA384_DIGEST_LENGTH], SHA384_CTX*);
+char* SHA384_End(SHA384_CTX*, char[SHA384_DIGEST_STRING_LENGTH]);
+char* SHA384_Data(const uint8_t*, size_t, char[SHA384_DIGEST_STRING_LENGTH]);
+
+void SHA512_Init(SHA512_CTX*);
+void SHA512_Update(SHA512_CTX*, const uint8_t*, size_t);
+void SHA512_Final(uint8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*);
+char* SHA512_End(SHA512_CTX*, char[SHA512_DIGEST_STRING_LENGTH]);
+char* SHA512_Data(const uint8_t*, size_t, char[SHA512_DIGEST_STRING_LENGTH]);
+
+#else /* SHA2_USE_INTTYPES_H */
+
+void SHA256_Init(SHA256_CTX *);
+void SHA256_Update(SHA256_CTX*, const u_int8_t*, size_t);
+void SHA256_Final(u_int8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*);
+char* SHA256_End(SHA256_CTX*, char[SHA256_DIGEST_STRING_LENGTH]);
+char* SHA256_Data(const u_int8_t*, size_t, char[SHA256_DIGEST_STRING_LENGTH]);
+
+void SHA384_Init(SHA384_CTX*);
+void SHA384_Update(SHA384_CTX*, const u_int8_t*, size_t);
+void SHA384_Final(u_int8_t[SHA384_DIGEST_LENGTH], SHA384_CTX*);
+char* SHA384_End(SHA384_CTX*, char[SHA384_DIGEST_STRING_LENGTH]);
+char* SHA384_Data(const u_int8_t*, size_t, char[SHA384_DIGEST_STRING_LENGTH]);
+
+void SHA512_Init(SHA512_CTX*);
+void SHA512_Update(SHA512_CTX*, const u_int8_t*, size_t);
+void SHA512_Final(u_int8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*);
+char* SHA512_End(SHA512_CTX*, char[SHA512_DIGEST_STRING_LENGTH]);
+char* SHA512_Data(const u_int8_t*, size_t, char[SHA512_DIGEST_STRING_LENGTH]);
+
+#endif /* SHA2_USE_INTTYPES_H */
+
+#else /* NOPROTO */
+
+void SHA256_Init();
+void SHA256_Update();
+void SHA256_Final();
+char* SHA256_End();
+char* SHA256_Data();
+
+void SHA384_Init();
+void SHA384_Update();
+void SHA384_Final();
+char* SHA384_End();
+char* SHA384_Data();
+
+void SHA512_Init();
+void SHA512_Update();
+void SHA512_Final();
+char* SHA512_End();
+char* SHA512_Data();
+
+#endif /* NOPROTO */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __SHA2_H__ */
+
diff --git a/contrib/slapd-modules/passwd/sha2/slapd-pw-sha2.5 b/contrib/slapd-modules/passwd/sha2/slapd-pw-sha2.5
new file mode 100644
index 0000000..f700b52
--- /dev/null
+++ b/contrib/slapd-modules/passwd/sha2/slapd-pw-sha2.5
@@ -0,0 +1,118 @@
+.TH SLAPD-PW-SHA2 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2015-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd-pw-sha2 \- SHA-2 password module to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.RS
+.LP
+.B moduleload
+.B pw-sha2
+.RE
+.SH DESCRIPTION
+.LP
+The
+.B pw-sha2
+module to
+.BR slapd (8)
+provides support for the use of SSHA-512, SSHA-384, SSHA-256, SHA-512,
+SHA-384 and SHA-256 from the SHA-2 family (FIPS 180-2) of hash functions
+in hashed passwords in OpenLDAP.
+.LP
+It does so by providing the following additional password schemes for use in slapd:
+.RS
+.TP
+.B {SSHA256}
+SHA-256 with salt, giving hash values of 256 bits length
+.TP
+.B {SHA256}
+plain SHA-256 giving hash values of 256 bits length
+.TP
+.B {SSHA384}
+SHA-384 with salt, giving hash values of 384 bits length
+.TP
+.B {SHA384}
+plain SHA-384 giving hash values of 384 bits length
+.TP
+.B {SSHA512}
+SHA-512 with salt, giving hash values of 512 bits length
+.TP
+.B {SHA512}
+plain SHA-512 giving hash values of 512 bits length
+.RE
+
+.SH CONFIGURATION
+The
+.B pw-sha2
+module does not need any configuration.
+.LP
+After loading the module, the password schemes
+{SSHA256}, {SSHA384}, {SSHA512}, {SSHA256}, {SHA384}, and {SHA512}
+will be recognised in values of the
+.I userPassword
+attribute.
+.LP
+You can then instruct OpenLDAP to use these schemes when processing
+the LDAPv3 Password Modify (RFC 3062) extended operations by using the
+.BR password-hash
+option in
+.BR slapd.conf (5).
+
+.SH NOTES
+If you want to use the schemes described here with
+.BR slappasswd (8),
+don't forget to load the module using its command line options.
+The relevant option/value is:
+.RS
+.LP
+.B \-o
+.BR module\-load = pw-sha2
+.LP
+.RE
+Depending on
+.BR pw-sha2 's
+location, you may also need:
+.RS
+.LP
+.B \-o
+.BR module\-path = \fIpathspec\fP
+.RE
+
+.SH EXAMPLES
+All of the userPassword LDAP attributes below encode the password
+.RI ' secret '.
+.EX
+.LP
+userPassword: {SHA512}vSsar3708Jvp9Szi2NWZZ02Bqp1qRCFpbcTZPdBhnWgs5WtNZKnvCXdhztmeD2cmW192CF5bDufKRpayrW/isg==
+.LP
+userPassword: {SHA384}WKd1ukESvjAFrkQHznV9iP2nHUBJe7gCbsrFTU4//HIyzo3jq1rLMK45dg/ufFPt
+.LP
+userPassword: {SHA256}K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=
+.EE
+.LP
+To make {SSHA512} the password hash used in Password Modify extended operations,
+simply set this line in slapd.conf(5):
+.EX
+.LP
+password-hash {SSHA512}
+.EX
+
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR ldappasswd (1),
+.BR slappasswd (8),
+.BR ldap (3),
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+
+.SH ACKNOWLEDGEMENTS
+This manual page has been written by Peter Marschall based on the
+module's README file written by Jeff Turner.
+.LP
+.B OpenLDAP
+is developed and maintained by The OpenLDAP Project (http://www.openldap.org/).
+.B OpenLDAP
+is derived from University of Michigan LDAP 3.3 Release.
diff --git a/contrib/slapd-modules/passwd/sha2/slapd-sha2.c b/contrib/slapd-modules/passwd/sha2/slapd-sha2.c
new file mode 100644
index 0000000..d67afda
--- /dev/null
+++ b/contrib/slapd-modules/passwd/sha2/slapd-sha2.c
@@ -0,0 +1,508 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2009-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Jeff Turner for inclusion
+ * in OpenLDAP Software.
+ *
+ * Hash methods for passwords generation added by Cédric Delfosse.
+ *
+ * SSHA256 / SSHA384 / SSHA512 support added, and chk_sha*() replaced
+ * with libraries/liblutil/passwd.c:chk_sha1() implementation to
+ * fix a race by SATOH Fumiyasu @ OSS Technology, Inc.
+ */
+
+#include "portable.h"
+
+#include <ac/string.h>
+
+#include "lber_pvt.h"
+#include "lutil.h"
+#include "sha2.h"
+
+#ifdef SLAPD_SHA2_DEBUG
+#include <stdio.h>
+#endif
+
+#define SHA2_SALT_SIZE 8
+
+static int hash_ssha256(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ SHA256_CTX ct;
+ unsigned char hash256[SHA256_DIGEST_LENGTH];
+ char saltdata[SHA2_SALT_SIZE];
+ struct berval digest;
+ struct berval salt;
+
+ digest.bv_val = (char *) hash256;
+ digest.bv_len = sizeof(hash256);
+ salt.bv_val = saltdata;
+ salt.bv_len = sizeof(saltdata);
+
+ if (lutil_entropy((unsigned char *)salt.bv_val, salt.bv_len) < 0) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ SHA256_Init(&ct);
+ SHA256_Update(&ct, (const uint8_t*)passwd->bv_val, passwd->bv_len);
+ SHA256_Update(&ct, (const uint8_t*)salt.bv_val, salt.bv_len);
+ SHA256_Final(hash256, &ct);
+
+ return lutil_passwd_string64(scheme, &digest, hash, &salt);
+}
+
+static int hash_sha256(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ SHA256_CTX ct;
+ unsigned char hash256[SHA256_DIGEST_LENGTH];
+ struct berval digest;
+ digest.bv_val = (char *) hash256;
+ digest.bv_len = sizeof(hash256);
+
+ SHA256_Init(&ct);
+ SHA256_Update(&ct, (const uint8_t*)passwd->bv_val, passwd->bv_len);
+ SHA256_Final(hash256, &ct);
+
+ return lutil_passwd_string64(scheme, &digest, hash, NULL);
+}
+
+static int hash_ssha384(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ SHA384_CTX ct;
+ unsigned char hash384[SHA384_DIGEST_LENGTH];
+ char saltdata[SHA2_SALT_SIZE];
+ struct berval digest;
+ struct berval salt;
+
+ digest.bv_val = (char *) hash384;
+ digest.bv_len = sizeof(hash384);
+ salt.bv_val = saltdata;
+ salt.bv_len = sizeof(saltdata);
+
+ if (lutil_entropy((unsigned char *)salt.bv_val, salt.bv_len) < 0) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ SHA384_Init(&ct);
+ SHA384_Update(&ct, (const uint8_t*)passwd->bv_val, passwd->bv_len);
+ SHA384_Update(&ct, (const uint8_t*)salt.bv_val, salt.bv_len);
+ SHA384_Final(hash384, &ct);
+
+ return lutil_passwd_string64(scheme, &digest, hash, &salt);
+}
+
+static int hash_sha384(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ SHA384_CTX ct;
+ unsigned char hash384[SHA384_DIGEST_LENGTH];
+ struct berval digest;
+ digest.bv_val = (char *) hash384;
+ digest.bv_len = sizeof(hash384);
+
+ SHA384_Init(&ct);
+ SHA384_Update(&ct, (const uint8_t*)passwd->bv_val, passwd->bv_len);
+ SHA384_Final(hash384, &ct);
+
+ return lutil_passwd_string64(scheme, &digest, hash, NULL);
+}
+
+static int hash_ssha512(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ SHA512_CTX ct;
+ unsigned char hash512[SHA512_DIGEST_LENGTH];
+ char saltdata[SHA2_SALT_SIZE];
+ struct berval digest;
+ struct berval salt;
+
+ digest.bv_val = (char *) hash512;
+ digest.bv_len = sizeof(hash512);
+ salt.bv_val = saltdata;
+ salt.bv_len = sizeof(saltdata);
+
+ if (lutil_entropy((unsigned char *)salt.bv_val, salt.bv_len) < 0) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ SHA512_Init(&ct);
+ SHA512_Update(&ct, (const uint8_t*)passwd->bv_val, passwd->bv_len);
+ SHA512_Update(&ct, (const uint8_t*)salt.bv_val, salt.bv_len);
+ SHA512_Final(hash512, &ct);
+
+ return lutil_passwd_string64(scheme, &digest, hash, &salt);
+}
+
+static int hash_sha512(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ SHA512_CTX ct;
+ unsigned char hash512[SHA512_DIGEST_LENGTH];
+ struct berval digest;
+ digest.bv_val = (char *) hash512;
+ digest.bv_len = sizeof(hash512);
+
+ SHA512_Init(&ct);
+ SHA512_Update(&ct, (const uint8_t*)passwd->bv_val, passwd->bv_len);
+ SHA512_Final(hash512, &ct);
+
+ return lutil_passwd_string64(scheme, &digest, hash, NULL);
+}
+
+#ifdef SLAPD_SHA2_DEBUG
+static void chk_sha_debug(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char *cred_hash,
+ size_t cred_len,
+ int cmp_rc)
+{
+ int rc;
+ struct berval cred_b64;
+
+ cred_b64.bv_len = LUTIL_BASE64_ENCODE_LEN(cred_len) + 1;
+ cred_b64.bv_val = ber_memalloc(cred_b64.bv_len + 1);
+
+ if( cred_b64.bv_val == NULL ) {
+ return;
+ }
+
+ rc = lutil_b64_ntop(
+ (unsigned char *) cred_hash, cred_len,
+ cred_b64.bv_val, cred_b64.bv_len );
+
+ if( rc < 0 ) {
+ ber_memfree(cred_b64.bv_val);
+ return;
+ }
+
+ fprintf(stderr, "Validating password\n");
+ fprintf(stderr, " Hash scheme:\t\t%s\n", scheme->bv_val);
+ fprintf(stderr, " Password to validate: %s\n", cred->bv_val);
+ fprintf(stderr, " Password hash:\t%s\n", cred_b64.bv_val);
+ fprintf(stderr, " Stored password hash:\t%s\n", passwd->bv_val);
+ fprintf(stderr, " Result:\t\t%s\n", cmp_rc ? "do not match" : "match");
+
+ ber_memfree(cred_b64.bv_val);
+}
+#endif
+
+static int chk_ssha256(
+ const struct berval *scheme, /* Scheme of hashed reference password */
+ const struct berval *passwd, /* Hashed reference password to check against */
+ const struct berval *cred, /* user-supplied password to check */
+ const char **text )
+{
+ SHA256_CTX SHAcontext;
+ unsigned char SHAdigest[SHA256_DIGEST_LENGTH];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len <= sizeof(SHAdigest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* base64 un-encode password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if( rc <= (int)(sizeof(SHAdigest)) ) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ SHA256_Init(&SHAcontext);
+ SHA256_Update(&SHAcontext,
+ (const unsigned char *) cred->bv_val, cred->bv_len);
+ SHA256_Update(&SHAcontext,
+ (const unsigned char *) &orig_pass[sizeof(SHAdigest)],
+ rc - sizeof(SHAdigest));
+ SHA256_Final(SHAdigest, &SHAcontext);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)SHAdigest, sizeof(SHAdigest));
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+static int chk_sha256(
+ const struct berval *scheme, /* Scheme of hashed reference password */
+ const struct berval *passwd, /* Hashed reference password to check against */
+ const struct berval *cred, /* user-supplied password to check */
+ const char **text )
+{
+ SHA256_CTX SHAcontext;
+ unsigned char SHAdigest[SHA256_DIGEST_LENGTH];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len < sizeof(SHAdigest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* base64 un-encode password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if( rc != sizeof(SHAdigest) ) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ SHA256_Init(&SHAcontext);
+ SHA256_Update(&SHAcontext,
+ (const unsigned char *) cred->bv_val, cred->bv_len);
+ SHA256_Final(SHAdigest, &SHAcontext);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)SHAdigest, sizeof(SHAdigest));
+#ifdef SLAPD_SHA2_DEBUG
+ chk_sha_debug(scheme, passwd, cred, (char *)SHAdigest, sizeof(SHAdigest), rc);
+#endif
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+static int chk_ssha384(
+ const struct berval *scheme, /* Scheme of hashed reference password */
+ const struct berval *passwd, /* Hashed reference password to check against */
+ const struct berval *cred, /* user-supplied password to check */
+ const char **text )
+{
+ SHA384_CTX SHAcontext;
+ unsigned char SHAdigest[SHA384_DIGEST_LENGTH];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len <= sizeof(SHAdigest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* base64 un-encode password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if( rc <= (int)(sizeof(SHAdigest)) ) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ SHA384_Init(&SHAcontext);
+ SHA384_Update(&SHAcontext,
+ (const unsigned char *) cred->bv_val, cred->bv_len);
+ SHA384_Update(&SHAcontext,
+ (const unsigned char *) &orig_pass[sizeof(SHAdigest)],
+ rc - sizeof(SHAdigest));
+ SHA384_Final(SHAdigest, &SHAcontext);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)SHAdigest, sizeof(SHAdigest));
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+static int chk_sha384(
+ const struct berval *scheme, /* Scheme of hashed reference password */
+ const struct berval *passwd, /* Hashed reference password to check against */
+ const struct berval *cred, /* user-supplied password to check */
+ const char **text )
+{
+ SHA384_CTX SHAcontext;
+ unsigned char SHAdigest[SHA384_DIGEST_LENGTH];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len < sizeof(SHAdigest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* base64 un-encode password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if( rc != sizeof(SHAdigest) ) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ SHA384_Init(&SHAcontext);
+ SHA384_Update(&SHAcontext,
+ (const unsigned char *) cred->bv_val, cred->bv_len);
+ SHA384_Final(SHAdigest, &SHAcontext);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)SHAdigest, sizeof(SHAdigest));
+#ifdef SLAPD_SHA2_DEBUG
+ chk_sha_debug(scheme, passwd, cred, (char *)SHAdigest, sizeof(SHAdigest), rc);
+#endif
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+static int chk_ssha512(
+ const struct berval *scheme, /* Scheme of hashed reference password */
+ const struct berval *passwd, /* Hashed reference password to check against */
+ const struct berval *cred, /* user-supplied password to check */
+ const char **text )
+{
+ SHA512_CTX SHAcontext;
+ unsigned char SHAdigest[SHA512_DIGEST_LENGTH];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len <= sizeof(SHAdigest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* base64 un-encode password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if( rc <= (int)(sizeof(SHAdigest)) ) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ SHA512_Init(&SHAcontext);
+ SHA512_Update(&SHAcontext,
+ (const unsigned char *) cred->bv_val, cred->bv_len);
+ SHA512_Update(&SHAcontext,
+ (const unsigned char *) &orig_pass[sizeof(SHAdigest)],
+ rc - sizeof(SHAdigest));
+ SHA512_Final(SHAdigest, &SHAcontext);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)SHAdigest, sizeof(SHAdigest));
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+static int chk_sha512(
+ const struct berval *scheme, /* Scheme of hashed reference password */
+ const struct berval *passwd, /* Hashed reference password to check against */
+ const struct berval *cred, /* user-supplied password to check */
+ const char **text )
+{
+ SHA512_CTX SHAcontext;
+ unsigned char SHAdigest[SHA512_DIGEST_LENGTH];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len < sizeof(SHAdigest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* base64 un-encode password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if( rc != sizeof(SHAdigest) ) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ SHA512_Init(&SHAcontext);
+ SHA512_Update(&SHAcontext,
+ (const unsigned char *) cred->bv_val, cred->bv_len);
+ SHA512_Final(SHAdigest, &SHAcontext);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)SHAdigest, sizeof(SHAdigest));
+#ifdef SLAPD_SHA2_DEBUG
+ chk_sha_debug(scheme, passwd, cred, (char *)SHAdigest, sizeof(SHAdigest), rc);
+#endif
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+const struct berval ssha256scheme = BER_BVC("{SSHA256}");
+const struct berval sha256scheme = BER_BVC("{SHA256}");
+const struct berval ssha384scheme = BER_BVC("{SSHA384}");
+const struct berval sha384scheme = BER_BVC("{SHA384}");
+const struct berval ssha512scheme = BER_BVC("{SSHA512}");
+const struct berval sha512scheme = BER_BVC("{SHA512}");
+
+int init_module(int argc, char *argv[]) {
+ int result = 0;
+ result = lutil_passwd_add( (struct berval *)&ssha256scheme, chk_ssha256, hash_ssha256 );
+ if (result != 0) return result;
+ result = lutil_passwd_add( (struct berval *)&sha256scheme, chk_sha256, hash_sha256 );
+ if (result != 0) return result;
+ result = lutil_passwd_add( (struct berval *)&ssha384scheme, chk_ssha384, hash_ssha384 );
+ if (result != 0) return result;
+ result = lutil_passwd_add( (struct berval *)&sha384scheme, chk_sha384, hash_sha384 );
+ if (result != 0) return result;
+ result = lutil_passwd_add( (struct berval *)&ssha512scheme, chk_ssha512, hash_ssha512 );
+ if (result != 0) return result;
+ result = lutil_passwd_add( (struct berval *)&sha512scheme, chk_sha512, hash_sha512 );
+ return result;
+}
diff --git a/contrib/slapd-modules/passwd/slapd-pw-radius.5 b/contrib/slapd-modules/passwd/slapd-pw-radius.5
new file mode 100644
index 0000000..9a74847
--- /dev/null
+++ b/contrib/slapd-modules/passwd/slapd-pw-radius.5
@@ -0,0 +1,110 @@
+.TH SLAPD-PW-RADIUS 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2015-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd-pw-radius \- Radius backend password module to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.RS
+.LP
+.B moduleload
+.B pw-radius
+.I /path/to/radius.conf
+.RE
+.SH DESCRIPTION
+.LP
+The
+.B pw-radius
+module to
+.BR slapd (8)
+provides support for using a RADIUS infrastructure as backend to
+verify the password provided in Simple Bind operations to OpenLDAP.
+.LP
+It does so by providing an additional password scheme for use in slapd:
+.RS
+.TP
+.B {RADIUS}
+RADIUS password scheme
+.RE
+.LP
+Unlike in other password schemes, the value following the scheme is not
+a - potentially hashed - password, but the name of the corresponding
+RADIUS user in the RADIUS infrastructure.
+.LP
+This value, together with the password used in the Simple Bind operation,
+will be sent to the RADIUS server for authentication.
+.LP
+If the RADIUS server successfully authenticates the user,
+then the password verification succeeds, resulting in the LDAP Bind
+operation's success.
+.LP
+Conversely, failed RADIUS authentications leads to failing LDAP Binds.
+
+.SH CONFIGURATION
+The
+.B pw-radius
+module needs no configuration beyond the additional
+.I filename
+argument to
+.BR slapd.conf (5)'s
+.B moduleload
+directive.
+This filename is expected to point to a valid
+.BR radius.conf (5).
+file adhering to
+.BR libradius (3).
+.LP
+After loading the module, the password scheme
+.B {RADIUS}
+will be recognised in values of the
+.I userPassword
+attribute.
+
+.SH NOTES
+Owing to its construction, using the
+.B {RADIUS}
+scheme as argument to the
+.BR password-hash
+option in
+.BR slapd.conf (5)
+does not make much sense, because of the scheme's construction.
+.LP
+This also applies to the use of the
+.B {RADIUS}
+scheme in
+.B slappasswd
+or
+.BR ldappasswd .
+
+
+.SH EXAMPLES
+To indicate that Simple Bind operations shall use the RADIUS user
+.B johndoe
+when validating passwords against the RADIUS infrastructure,
+set a user's LDAP attribute userPassword to:
+.EX
+.LP
+userPassword: {RADIUS}johndoe
+.EE
+
+.SH LIMITATIONS
+Due to the way the configuration is loaded (additional argument
+to slapd.conf's moduleload directive), this module cannot be used
+with table-driven configuration.
+
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR libradius (3)
+.BR ldap (3),
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+
+.SH ACKNOWLEDGEMENTS
+This manual page has been written by Peter Marschall.
+.LP
+.B OpenLDAP
+is developed and maintained by The OpenLDAP Project (http://www.openldap.org/).
+.B OpenLDAP
+is derived from University of Michigan LDAP 3.3 Release.
diff --git a/contrib/slapd-modules/passwd/totp/Makefile b/contrib/slapd-modules/passwd/totp/Makefile
new file mode 100644
index 0000000..f7dff4b
--- /dev/null
+++ b/contrib/slapd-modules/passwd/totp/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = pw-totp.la
+MANPAGES = slapo-totp.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+pw-totp.la: slapd-totp.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/passwd/totp/README b/contrib/slapd-modules/passwd/totp/README
new file mode 100644
index 0000000..e6867f2
--- /dev/null
+++ b/contrib/slapd-modules/passwd/totp/README
@@ -0,0 +1,87 @@
+TOTP OpenLDAP support
+----------------------
+
+slapd-totp.c provides support for RFC 6238 TOTP Time-based One
+Time Passwords in OpenLDAP using SHA-1, SHA-256, and SHA-512.
+For instance, one could have the LDAP attribute:
+
+userPassword: {TOTP1}GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ
+
+which encodes the key '12345678901234567890'.
+
+It can also encode credentials consisting of a TOTP and a static
+password. The format for this is:
+
+userPassword: {TOTP1ANDPW}GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ|<some_other_passwd>
+
+where <some_other_passwd> can be any scheme currently understood
+by OpenLDAP. For example, using '{SHA}5en6G6MezRroT3XKqkdPOmY/BfQ='
+would encode the above TOTP with a static password of 'secret'. To
+authenticate using this scheme, enter the static password immediately
+followed by the TOTP, for example 'secret123456'.
+
+
+Building
+--------
+
+1) Customize the LDAP_SRC variable in Makefile to point to the OpenLDAP
+source root.
+
+2) Run 'make' to produce slapd-totp.so
+
+3) Copy slapd-totp.so somewhere permanent.
+
+4) Edit your slapd.conf (eg. /etc/ldap/slapd.conf), and add:
+
+moduleload ...path/to/slapd-totp.so
+
+5) This module replaces the function of the slapo-lastbind overlay. You
+cannot use that overlay on the same database as this one.
+
+6) Restart slapd.
+
+
+Configuring
+-----------
+
+The {TOTP1}, {TOTP256}, {TOTP512}, {TOTP1ANDPW}, {TOTP256ANDPW},
+and {TOTP512ANDPW} password schemes should now be recognised.
+
+You can also tell OpenLDAP to use one of these new schemes when processing LDAP
+Password Modify Extended Operations, thanks to the password-hash option in
+slapd.conf. For example:
+
+password-hash {TOTP1}
+
+TOTP password schemes will only work on databases that have a rootdn and the
+totp overlay configured:
+
+database mdb
+rootdn "..."
+...
+
+overlay totp
+
+
+
+Testing
+-------
+
+The TOTP1 algorithm is compatible with Google Authenticator.
+
+---
+
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2015-2022 The OpenLDAP Foundation.
+Portions Copyright 2015 by Howard Chu, Symas Corp.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
diff --git a/contrib/slapd-modules/passwd/totp/slapd-totp.c b/contrib/slapd-modules/passwd/totp/slapd-totp.c
new file mode 100644
index 0000000..25081e1
--- /dev/null
+++ b/contrib/slapd-modules/passwd/totp/slapd-totp.c
@@ -0,0 +1,1000 @@
+/* slapd-totp.c - Password module and overlay for TOTP */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2015-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2015 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work includes code from the lastbind overlay.
+ */
+
+#include <portable.h>
+
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+#include <lber.h>
+#include <lber_pvt.h>
+#include "lutil.h"
+#include <ac/stdlib.h>
+#include <ac/ctype.h>
+#include <ac/string.h>
+/* include socket.h to get sys/types.h and/or winsock2.h */
+#include <ac/socket.h>
+
+#if HAVE_OPENSSL
+#include <openssl/sha.h>
+#include <openssl/hmac.h>
+
+#define TOTP_SHA512_DIGEST_LENGTH SHA512_DIGEST_LENGTH
+#define TOTP_SHA1 EVP_sha1()
+#define TOTP_SHA256 EVP_sha256()
+#define TOTP_SHA512 EVP_sha512()
+#define TOTP_HMAC_CTX HMAC_CTX *
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+static HMAC_CTX *HMAC_CTX_new(void)
+{
+ HMAC_CTX *ctx = OPENSSL_malloc(sizeof(*ctx));
+ if (ctx != NULL) {
+ HMAC_CTX_init(ctx);
+ }
+ return ctx;
+}
+
+static void HMAC_CTX_free(HMAC_CTX *ctx)
+{
+ if (ctx != NULL) {
+ HMAC_CTX_cleanup(ctx);
+ OPENSSL_free(ctx);
+ }
+}
+#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
+
+#define HMAC_setup(ctx, key, len, hash) \
+ ctx = HMAC_CTX_new(); \
+ HMAC_Init_ex(ctx, key, len, hash, 0)
+#define HMAC_crunch(ctx, buf, len) HMAC_Update(ctx, buf, len)
+#define HMAC_finish(ctx, dig, dlen) \
+ HMAC_Final(ctx, dig, &dlen); \
+ HMAC_CTX_free(ctx)
+
+#elif HAVE_GNUTLS
+#include <nettle/hmac.h>
+
+#define TOTP_SHA512_DIGEST_LENGTH SHA512_DIGEST_SIZE
+#define TOTP_SHA1 &nettle_sha1
+#define TOTP_SHA256 &nettle_sha256
+#define TOTP_SHA512 &nettle_sha512
+#define TOTP_HMAC_CTX struct hmac_sha512_ctx
+
+#define HMAC_setup(ctx, key, len, hash) \
+ const struct nettle_hash *h=hash;\
+ hmac_set_key(&ctx.outer, &ctx.inner, &ctx.state, h, len, key)
+#define HMAC_crunch(ctx, buf, len) hmac_update(&ctx.state, h, len, buf)
+#define HMAC_finish(ctx, dig, dlen) \
+ hmac_digest(&ctx.outer, &ctx.inner, &ctx.state, h, h->digest_size, dig);\
+ dlen = h->digest_size
+
+#else
+# error Unsupported crypto backend.
+#endif
+
+#include "slap.h"
+#include "slap-config.h"
+
+static LUTIL_PASSWD_CHK_FUNC chk_totp1, chk_totp256, chk_totp512,
+ chk_totp1andpw, chk_totp256andpw, chk_totp512andpw;
+static LUTIL_PASSWD_HASH_FUNC hash_totp1, hash_totp256, hash_totp512,
+ hash_totp1andpw, hash_totp256andpw, hash_totp512andpw;
+static const struct berval scheme_totp1 = BER_BVC("{TOTP1}");
+static const struct berval scheme_totp256 = BER_BVC("{TOTP256}");
+static const struct berval scheme_totp512 = BER_BVC("{TOTP512}");
+static const struct berval scheme_totp1andpw = BER_BVC("{TOTP1ANDPW}");
+static const struct berval scheme_totp256andpw = BER_BVC("{TOTP256ANDPW}");
+static const struct berval scheme_totp512andpw = BER_BVC("{TOTP512ANDPW}");
+
+static AttributeDescription *ad_authTimestamp;
+
+/* This is the definition used by ISODE, as supplied to us in
+ * ITS#6238 Followup #9
+ */
+static struct schema_info {
+ char *def;
+ AttributeDescription **ad;
+} totp_OpSchema[] = {
+ { "( 1.3.6.1.4.1.453.16.2.188 "
+ "NAME 'authTimestamp' "
+ "DESC 'last successful authentication using any method/mech' "
+ "EQUALITY generalizedTimeMatch "
+ "ORDERING generalizedTimeOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE dsaOperation )",
+ &ad_authTimestamp},
+ { NULL, NULL }
+};
+
+/* RFC3548 base32 encoding/decoding */
+
+static const char Base32[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+static const char Pad32 = '=';
+
+static int
+totp_b32_ntop(
+ u_char const *src,
+ size_t srclength,
+ char *target,
+ size_t targsize)
+{
+ size_t datalength = 0;
+ u_char input0;
+ u_int input1; /* assumed to be at least 32 bits */
+ u_char output[8];
+ int i;
+
+ while (4 < srclength) {
+ if (datalength + 8 > targsize)
+ return (-1);
+ input0 = *src++;
+ input1 = *src++;
+ input1 <<= 8;
+ input1 |= *src++;
+ input1 <<= 8;
+ input1 |= *src++;
+ input1 <<= 8;
+ input1 |= *src++;
+ srclength -= 5;
+
+ for (i=7; i>1; i--) {
+ output[i] = input1 & 0x1f;
+ input1 >>= 5;
+ }
+ output[0] = input0 >> 3;
+ output[1] = (input0 & 0x07) << 2 | input1;
+
+ for (i=0; i<8; i++)
+ target[datalength++] = Base32[output[i]];
+ }
+
+ /* Now we worry about padding. */
+ if (0 != srclength) {
+ static const int outlen[] = { 2,4,5,7 };
+ int n;
+ if (datalength + 8 > targsize)
+ return (-1);
+
+ /* Get what's left. */
+ input1 = *src++;
+ for (i = 1; i < srclength; i++) {
+ input1 <<= 8;
+ input1 |= *src++;
+ }
+ input1 <<= 8 * (4-srclength);
+ n = outlen[srclength-1];
+ for (i=0; i<n; i++) {
+ target[datalength++] = Base32[(input1 & 0xf8000000) >> 27];
+ input1 <<= 5;
+ }
+ for (; i<8; i++)
+ target[datalength++] = Pad32;
+ }
+ if (datalength >= targsize)
+ return (-1);
+ target[datalength] = '\0'; /* Returned value doesn't count \0. */
+ return (datalength);
+}
+
+/* converts characters, eight at a time, starting at src
+ from base - 32 numbers into five 8 bit bytes in the target area.
+ it returns the number of data bytes stored at the target, or -1 on error.
+ */
+
+static int
+totp_b32_pton(
+ char const *src,
+ u_char *target,
+ size_t targsize)
+{
+ int tarindex, state, ch;
+ char *pos;
+
+ state = 0;
+ tarindex = 0;
+
+ while ((ch = *src++) != '\0') {
+ if (ch == Pad32)
+ break;
+
+ pos = strchr(Base32, ch);
+ if (pos == 0) /* A non-base32 character. */
+ return (-1);
+
+ switch (state) {
+ case 0:
+ if (target) {
+ if ((size_t)tarindex >= targsize)
+ return (-1);
+ target[tarindex] = (pos - Base32) << 3;
+ }
+ state = 1;
+ break;
+ case 1:
+ if (target) {
+ if ((size_t)tarindex + 1 >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base32) >> 2;
+ target[tarindex+1] = ((pos - Base32) & 0x3)
+ << 6 ;
+ }
+ tarindex++;
+ state = 2;
+ break;
+ case 2:
+ if (target) {
+ target[tarindex] |= (pos - Base32) << 1;
+ }
+ state = 3;
+ break;
+ case 3:
+ if (target) {
+ if ((size_t)tarindex + 1 >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base32) >> 4;
+ target[tarindex+1] = ((pos - Base32) & 0xf)
+ << 4 ;
+ }
+ tarindex++;
+ state = 4;
+ break;
+ case 4:
+ if (target) {
+ if ((size_t)tarindex + 1 >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base32) >> 1;
+ target[tarindex+1] = ((pos - Base32) & 0x1)
+ << 7 ;
+ }
+ tarindex++;
+ state = 5;
+ break;
+ case 5:
+ if (target) {
+ target[tarindex] |= (pos - Base32) << 2;
+ }
+ state = 6;
+ break;
+ case 6:
+ if (target) {
+ if ((size_t)tarindex + 1 >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base32) >> 3;
+ target[tarindex+1] = ((pos - Base32) & 0x7)
+ << 5 ;
+ }
+ tarindex++;
+ state = 7;
+ break;
+ case 7:
+ if (target) {
+ target[tarindex] |= (pos - Base32);
+ }
+ state = 0;
+ tarindex++;
+ break;
+
+ default:
+ abort();
+ }
+ }
+
+ /*
+ * We are done decoding Base-32 chars. Let's see if we ended
+ * on a byte boundary, and/or with erroneous trailing characters.
+ */
+
+ if (ch == Pad32) { /* We got a pad char. */
+ int i = 0;
+
+ /* count pad chars */
+ for (; ch; ch = *src++) {
+ if (ch != Pad32)
+ return (-1);
+ i++;
+ }
+ /* there are only 4 valid ending states with a
+ * pad character, make sure the number of pads is valid.
+ */
+ switch(state) {
+ case 2: if (i != 6) return -1;
+ break;
+ case 4: if (i != 4) return -1;
+ break;
+ case 5: if (i != 3) return -1;
+ break;
+ case 7: if (i != 1) return -1;
+ break;
+ default:
+ return -1;
+ }
+ /*
+ * Now make sure that the "extra" bits that slopped past
+ * the last full byte were zeros. If we don't check them,
+ * they become a subliminal channel.
+ */
+ if (target && target[tarindex] != 0)
+ return (-1);
+ } else {
+ /*
+ * We ended by seeing the end of the string. Make sure we
+ * have no partial bytes lying around.
+ */
+ if (state != 0)
+ return (-1);
+ }
+
+ return (tarindex);
+}
+
+/* RFC6238 TOTP */
+
+
+typedef struct myval {
+ ber_len_t mv_len;
+ void *mv_val;
+} myval;
+
+static void do_hmac(
+ const void *hash,
+ myval *key,
+ myval *data,
+ myval *out)
+{
+ TOTP_HMAC_CTX ctx;
+ unsigned int digestLen;
+
+ HMAC_setup(ctx, key->mv_val, key->mv_len, hash);
+ HMAC_crunch(ctx, data->mv_val, data->mv_len);
+ HMAC_finish(ctx, out->mv_val, digestLen);
+ out->mv_len = digestLen;
+}
+
+static const int DIGITS_POWER[] = {
+ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
+
+static void generate(
+ myval *key,
+ uint64_t tval,
+ int digits,
+ myval *out,
+ const void *mech)
+{
+ unsigned char digest[TOTP_SHA512_DIGEST_LENGTH];
+ myval digval;
+ myval data;
+ unsigned char msg[8];
+ int i, offset, res, otp;
+
+#if WORDS_BIGENDIAN
+ *(uint64_t *)msg = tval;
+#else
+ for (i=7; i>=0; i--) {
+ msg[i] = tval & 0xff;
+ tval >>= 8;
+ }
+#endif
+
+ data.mv_val = msg;
+ data.mv_len = sizeof(msg);
+
+ digval.mv_val = digest;
+ digval.mv_len = sizeof(digest);
+ do_hmac(mech, key, &data, &digval);
+
+ offset = digest[digval.mv_len-1] & 0xf;
+ res = ((digest[offset] & 0x7f) << 24) |
+ ((digest[offset+1] & 0xff) << 16) |
+ ((digest[offset+2] & 0xff) << 8) |
+ (digest[offset+3] & 0xff);
+
+ otp = res % DIGITS_POWER[digits];
+ out->mv_len = snprintf(out->mv_val, out->mv_len, "%0*d", digits, otp);
+}
+
+static int totp_op_cleanup( Operation *op, SlapReply *rs );
+static int totp_bind_response( Operation *op, SlapReply *rs );
+
+#define TIME_STEP 30
+#define DIGITS 6
+#define DELIM '|' /* a single character */
+#define TOTP_AND_PW_HASH_SCHEME "{SSHA}"
+
+static int chk_totp(
+ const struct berval *passwd,
+ const struct berval *cred,
+ const void *mech,
+ const char **text)
+{
+ void *ctx, *op_tmp;
+ Operation *op;
+ Entry *e;
+ Attribute *a;
+ long t, told = 0;
+ int rc;
+ myval out, key;
+ char outbuf[32];
+
+ /* Find our thread context, find our Operation */
+ ctx = ldap_pvt_thread_pool_context();
+ if (ldap_pvt_thread_pool_getkey(ctx, totp_op_cleanup, &op_tmp, NULL) ||
+ !op_tmp)
+ return LUTIL_PASSWD_ERR;
+ op = op_tmp;
+
+ rc = be_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &e);
+ if (rc != LDAP_SUCCESS) return LUTIL_PASSWD_ERR;
+
+ /* Make sure previous login is older than current time */
+ t = op->o_time / TIME_STEP;
+ a = attr_find(e->e_attrs, ad_authTimestamp);
+ if (a) {
+ struct lutil_tm tm;
+ struct lutil_timet tt;
+ if (lutil_parsetime(a->a_vals[0].bv_val, &tm) == 0 &&
+ lutil_tm2time(&tm, &tt) == 0) {
+ told = tt.tt_sec / TIME_STEP;
+ if (told >= t)
+ rc = LUTIL_PASSWD_ERR;
+ }
+ if (!rc) { /* seems OK, remember old stamp */
+ slap_callback *sc;
+ for (sc = op->o_callback; sc; sc = sc->sc_next) {
+ if (sc->sc_response == totp_bind_response) {
+ sc->sc_private = ber_dupbv_x(NULL, &a->a_vals[0], op->o_tmpmemctx);
+ break;
+ }
+ }
+ }
+ } /* else no previous login, 1st use is OK */
+
+ be_entry_release_r(op, e);
+ if (rc) return rc;
+
+ /* Key is stored in base32 */
+ key.mv_len = passwd->bv_len * 5 / 8;
+ key.mv_val = ber_memalloc(key.mv_len+1);
+
+ if (!key.mv_val)
+ return LUTIL_PASSWD_ERR;
+
+ rc = totp_b32_pton(passwd->bv_val, key.mv_val, key.mv_len);
+ if (rc < 1) {
+ rc = LUTIL_PASSWD_ERR;
+ goto out;
+ }
+
+ out.mv_val = outbuf;
+ out.mv_len = sizeof(outbuf);
+ generate(&key, t, DIGITS, &out, mech);
+
+ /* compare */
+ if (out.mv_len != cred->bv_len) {
+ rc = LUTIL_PASSWD_ERR;
+ goto out;
+ }
+
+ rc = memcmp(out.mv_val, cred->bv_val, out.mv_len) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+
+ /* If current value doesn't match, try again with previous value
+ * but only if the most recent login is older than the previous
+ * time step but still set */
+ if (rc == LUTIL_PASSWD_ERR && told < t - 1 && told > 0) {
+ out.mv_val = outbuf;
+ out.mv_len = sizeof(outbuf);
+ generate(&key, t - 1, DIGITS, &out, mech);
+ /* compare */
+ if (out.mv_len != cred->bv_len)
+ goto out;
+ rc = memcmp(out.mv_val, cred->bv_val, out.mv_len) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+ }
+
+out:
+ memset(key.mv_val, 0, key.mv_len);
+ ber_memfree(key.mv_val);
+ return rc;
+}
+
+static int chk_totp_and_pw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text,
+ const void *mech)
+{
+ char *s;
+ int rc = LUTIL_PASSWD_ERR, rc_pass, rc_otp;
+ ber_len_t len;
+ struct berval cred_pass, cred_otp, passwd_pass, passwd_otp;
+
+ /* Check credential length, no point to continue if too short */
+ if (cred->bv_len <= DIGITS)
+ return rc;
+
+ /* The OTP seed of the stored password */
+ s = strchr(passwd->bv_val, DELIM);
+ if (s) {
+ len = s - passwd->bv_val;
+ } else {
+ return rc;
+ }
+ if (!ber_str2bv(passwd->bv_val, len, 1, &passwd_otp))
+ return rc;
+
+ /* The password part of the stored password */
+ s++;
+ ber_str2bv(s, 0, 0, &passwd_pass);
+
+ /* The OTP part of the entered credential */
+ ber_str2bv(&cred->bv_val[cred->bv_len - DIGITS], DIGITS, 0, &cred_otp);
+
+ /* The password part of the entered credential */
+ if (!ber_str2bv(cred->bv_val, cred->bv_len - DIGITS, 0, &cred_pass)) {
+ /* Cleanup */
+ memset(passwd_otp.bv_val, 0, passwd_otp.bv_len);
+ ber_memfree(passwd_otp.bv_val);
+ return rc;
+ }
+
+ rc_otp = chk_totp(&passwd_otp, &cred_otp, mech, text);
+ rc_pass = lutil_passwd(&passwd_pass, &cred_pass, NULL, text);
+ if (rc_otp == LUTIL_PASSWD_OK && rc_pass == LUTIL_PASSWD_OK)
+ rc = LUTIL_PASSWD_OK;
+
+ /* Cleanup and return */
+ memset(passwd_otp.bv_val, 0, passwd_otp.bv_len);
+ ber_memfree(passwd_otp.bv_val);
+
+ return rc;
+}
+
+static int chk_totp1(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_totp(passwd, cred, TOTP_SHA1, text);
+}
+
+static int chk_totp256(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_totp(passwd, cred, TOTP_SHA256, text);
+}
+
+static int chk_totp512(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_totp(passwd, cred, TOTP_SHA512, text);
+}
+
+static int chk_totp1andpw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_totp_and_pw(scheme, passwd, cred, text, TOTP_SHA1);
+}
+
+static int chk_totp256andpw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_totp_and_pw(scheme, passwd, cred, text, TOTP_SHA256);
+}
+
+static int chk_totp512andpw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_totp_and_pw(scheme, passwd, cred, text, TOTP_SHA512);
+}
+
+static int passwd_string32(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash)
+{
+ int b32len = (passwd->bv_len + 4)/5 * 8;
+ int rc;
+ hash->bv_len = scheme->bv_len + b32len;
+ hash->bv_val = ber_memalloc(hash->bv_len + 1);
+ AC_MEMCPY(hash->bv_val, scheme->bv_val, scheme->bv_len);
+ rc = totp_b32_ntop((unsigned char *)passwd->bv_val, passwd->bv_len,
+ hash->bv_val + scheme->bv_len, b32len+1);
+ if (rc < 0) {
+ ber_memfree(hash->bv_val);
+ hash->bv_val = NULL;
+ return LUTIL_PASSWD_ERR;
+ }
+ return LUTIL_PASSWD_OK;
+}
+
+static int hash_totp_and_pw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+ struct berval otp, pass, hash_otp, hash_pass;
+ ber_len_t len;
+ char *s;
+ int rc = LUTIL_PASSWD_ERR;
+
+ /* The OTP seed part */
+ s = strchr(passwd->bv_val, DELIM);
+ if (s) {
+ len = s - passwd->bv_val;
+ } else {
+ return rc;
+ }
+ if (!ber_str2bv(passwd->bv_val, len, 0, &otp))
+ return rc;
+
+ /* The static password part */
+ s++;
+ ber_str2bv(s, 0, 0, &pass);
+
+ /* Hash the OTP seed */
+ rc = passwd_string32(scheme, &otp, &hash_otp);
+
+ /* If successful, hash the static password, else cleanup and return */
+ if (rc == LUTIL_PASSWD_OK) {
+ rc = lutil_passwd_hash(&pass, TOTP_AND_PW_HASH_SCHEME,
+ &hash_pass, text);
+ } else {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* If successful, allocate memory to combine them, else cleanup
+ * and return */
+ if (rc == LUTIL_PASSWD_OK) {
+ /* Add 1 character to bv_len to hold DELIM */
+ hash->bv_len = hash_pass.bv_len + hash_otp.bv_len + 1;
+ hash->bv_val = ber_memalloc(hash->bv_len + 1);
+ if (!hash->bv_val)
+ rc = LUTIL_PASSWD_ERR;
+ } else {
+ memset(hash_otp.bv_val, 0, hash_otp.bv_len);
+ ber_memfree(hash_otp.bv_val);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* If successful, combine the two hashes with the delimiter */
+ if (rc == LUTIL_PASSWD_OK) {
+ AC_MEMCPY(hash->bv_val, hash_otp.bv_val, hash_otp.bv_len);
+ hash->bv_val[hash_otp.bv_len] = DELIM;
+ AC_MEMCPY(hash->bv_val + hash_otp.bv_len + 1,
+ hash_pass.bv_val, hash_pass.bv_len);
+ hash->bv_val[hash->bv_len] = '\0';
+ }
+
+ /* Cleanup and return */
+ memset(hash_otp.bv_val, 0, hash_otp.bv_len);
+ memset(hash_pass.bv_val, 0, hash_pass.bv_len);
+ ber_memfree(hash_otp.bv_val);
+ ber_memfree(hash_pass.bv_val);
+
+ return rc;
+}
+
+static int hash_totp1(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+#if 0
+ if (passwd->bv_len != SHA_DIGEST_LENGTH) {
+ *text = "invalid key length";
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+ return passwd_string32(scheme, passwd, hash);
+}
+
+static int hash_totp256(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+#if 0
+ if (passwd->bv_len != SHA256_DIGEST_LENGTH) {
+ *text = "invalid key length";
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+ return passwd_string32(scheme, passwd, hash);
+}
+
+static int hash_totp512(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+#if 0
+ if (passwd->bv_len != SHA512_DIGEST_LENGTH) {
+ *text = "invalid key length";
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+ return passwd_string32(scheme, passwd, hash);
+}
+
+static int hash_totp1andpw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+#if 0
+ if (passwd->bv_len != SHA_DIGEST_LENGTH) {
+ *text = "invalid key length";
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+ return hash_totp_and_pw(scheme, passwd, hash, text);
+}
+
+static int hash_totp256andpw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+#if 0
+ if (passwd->bv_len != SHA256_DIGEST_LENGTH) {
+ *text = "invalid key length";
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+ return hash_totp_and_pw(scheme, passwd, hash, text);
+}
+
+static int hash_totp512andpw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+#if 0
+ if (passwd->bv_len != SHA512_DIGEST_LENGTH) {
+ *text = "invalid key length";
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+ return hash_totp_and_pw(scheme, passwd, hash, text);
+}
+
+static int totp_op_cleanup(
+ Operation *op,
+ SlapReply *rs )
+{
+ slap_callback *cb;
+
+ /* clear out the current key */
+ ldap_pvt_thread_pool_setkey( op->o_threadctx, totp_op_cleanup,
+ NULL, 0, NULL, NULL );
+
+ /* free the callback */
+ cb = op->o_callback;
+ op->o_callback = cb->sc_next;
+ if (cb->sc_private)
+ ber_bvfree_x(cb->sc_private, op->o_tmpmemctx);
+ op->o_tmpfree( cb, op->o_tmpmemctx );
+ return 0;
+}
+
+static int
+totp_bind_response( Operation *op, SlapReply *rs )
+{
+ Modifications *mod = NULL;
+ BackendInfo *bi = op->o_bd->bd_info;
+ Entry *e;
+ int rc;
+
+ /* we're only interested if the bind was successful */
+ if ( rs->sr_err != LDAP_SUCCESS )
+ return SLAP_CB_CONTINUE;
+
+ rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
+ op->o_bd->bd_info = bi;
+
+ if ( rc != LDAP_SUCCESS ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ {
+ time_t now;
+ Attribute *a;
+ Modifications *m;
+ char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+ struct berval timestamp;
+
+ /* get the current time */
+ now = op->o_time;
+
+ /* update the authTimestamp in the user's entry with the current time */
+ timestamp.bv_val = nowstr;
+ timestamp.bv_len = sizeof(nowstr);
+ slap_timestamp( &now, &timestamp );
+
+ m = ch_calloc( sizeof(Modifications), 1 );
+ m->sml_op = LDAP_MOD_REPLACE;
+ m->sml_flags = 0;
+ m->sml_type = ad_authTimestamp->ad_cname;
+ m->sml_desc = ad_authTimestamp;
+ m->sml_numvals = 1;
+ m->sml_values = ch_calloc( sizeof(struct berval), 2 );
+ m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
+
+ ber_dupbv( &m->sml_values[0], &timestamp );
+ ber_dupbv( &m->sml_nvalues[0], &timestamp );
+ m->sml_next = mod;
+ mod = m;
+
+ /* get authTimestamp attribute, if it exists */
+ if ((a = attr_find( e->e_attrs, ad_authTimestamp)) != NULL && op->o_callback->sc_private) {
+ struct berval *bv = op->o_callback->sc_private;
+ m = ch_calloc( sizeof(Modifications), 1 );
+ m->sml_op = LDAP_MOD_DELETE;
+ m->sml_flags = 0;
+ m->sml_type = ad_authTimestamp->ad_cname;
+ m->sml_desc = ad_authTimestamp;
+ m->sml_numvals = 1;
+ m->sml_values = ch_calloc( sizeof(struct berval), 2 );
+ m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
+
+ ber_dupbv( &m->sml_values[0], bv );
+ ber_dupbv( &m->sml_nvalues[0], bv );
+ m->sml_next = mod;
+ mod = m;
+ }
+ }
+
+ be_entry_release_r( op, e );
+
+ /* perform the update */
+ if ( mod ) {
+ Operation op2 = *op;
+ SlapReply r2 = { REP_RESULT };
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+
+ /* This is a DSA-specific opattr, it never gets replicated. */
+ op2.o_tag = LDAP_REQ_MODIFY;
+ op2.o_callback = &cb;
+ op2.orm_modlist = mod;
+ op2.o_dn = op->o_bd->be_rootdn;
+ op2.o_ndn = op->o_bd->be_rootndn;
+ op2.o_dont_replicate = 1;
+ rc = op->o_bd->be_modify( &op2, &r2 );
+ slap_mods_free( mod, 1 );
+ if (rc != LDAP_SUCCESS) {
+ /* slapd has logged this as a success already, but we
+ * need to fail it because the authTimestamp changed
+ * out from under us.
+ */
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ connection2anonymous(op->o_conn);
+ op2 = *op;
+ op2.o_callback = NULL;
+ send_ldap_result(&op2, rs);
+ op->o_bd->bd_info = bi;
+ return rs->sr_err;
+ }
+ }
+
+ op->o_bd->bd_info = bi;
+ return SLAP_CB_CONTINUE;
+}
+
+static int totp_op_bind(
+ Operation *op,
+ SlapReply *rs )
+{
+ /* If this is a simple Bind, stash the Op pointer so our chk
+ * function can find it. Set a cleanup callback to clear it
+ * out when the Bind completes.
+ */
+ if ( op->oq_bind.rb_method == LDAP_AUTH_SIMPLE ) {
+ slap_callback *cb;
+ ldap_pvt_thread_pool_setkey( op->o_threadctx,
+ totp_op_cleanup, op, 0, NULL, NULL );
+ cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
+ cb->sc_response = totp_bind_response;
+ cb->sc_cleanup = totp_op_cleanup;
+ cb->sc_next = op->o_callback;
+ op->o_callback = cb;
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int totp_db_open(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ int rc = 0;
+
+ if (!ad_authTimestamp) {
+ const char *text = NULL;
+ rc = slap_str2ad("authTimestamp", &ad_authTimestamp, &text);
+ if (rc) {
+ rc = register_at(totp_OpSchema[0].def, totp_OpSchema[0].ad, 0 );
+ if (rc) {
+ snprintf(cr->msg, sizeof(cr->msg), "unable to find or register authTimestamp attribute: %s (%d)",
+ text, rc);
+ Debug(LDAP_DEBUG_ANY, "totp: %s.\n", cr->msg );
+ }
+ ad_authTimestamp->ad_type->sat_flags |= SLAP_AT_MANAGEABLE;
+ }
+ }
+ return rc;
+}
+
+static slap_overinst totp;
+
+int
+totp_initialize(void)
+{
+ int rc;
+
+ totp.on_bi.bi_type = "totp";
+
+ totp.on_bi.bi_db_open = totp_db_open;
+ totp.on_bi.bi_op_bind = totp_op_bind;
+
+ rc = lutil_passwd_add((struct berval *) &scheme_totp1, chk_totp1, hash_totp1);
+ if (!rc)
+ rc = lutil_passwd_add((struct berval *) &scheme_totp256, chk_totp256, hash_totp256);
+ if (!rc)
+ rc = lutil_passwd_add((struct berval *) &scheme_totp512, chk_totp512, hash_totp512);
+ if (!rc)
+ rc = lutil_passwd_add((struct berval *) &scheme_totp1andpw, chk_totp1andpw, hash_totp1andpw);
+ if (!rc)
+ rc = lutil_passwd_add((struct berval *) &scheme_totp256andpw, chk_totp256andpw, hash_totp256andpw);
+ if (!rc)
+ rc = lutil_passwd_add((struct berval *) &scheme_totp512andpw, chk_totp512andpw, hash_totp512andpw);
+ if (rc)
+ return rc;
+
+ return overlay_register(&totp);
+}
+
+int init_module(int argc, char *argv[]) {
+ return totp_initialize();
+}
diff --git a/contrib/slapd-modules/passwd/totp/slapo-totp.5 b/contrib/slapd-modules/passwd/totp/slapo-totp.5
new file mode 100644
index 0000000..7c99bf1
--- /dev/null
+++ b/contrib/slapd-modules/passwd/totp/slapo-totp.5
@@ -0,0 +1,109 @@
+.TH PW-TOTP 5 "2015/7/2" "PW-TOTP"
+.\" Copyright 2015-2022 The OpenLDAP Foundation.
+.\" Portions Copyright 2015 by Howard Chu, Symas Corp. All rights reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+pw-totp \- TOTP Password handling module
+.SH SYNOPSIS
+.B moduleload
+.I pw-totp.la
+.SH DESCRIPTION
+The
+.B pw-totp
+module allows time-based one-time password, AKA "authenticator-style",
+authentication to be added to applications that use LDAP for
+authentication. In most cases no changes to the applications are needed to switch
+to this type of authentication.
+
+With this module, the password needed for a user to authenticate is calculated
+based on the current time and a key that is stored in the user's LDAP entry. Since
+the password is based on the time, it changes periodically. Once used, it cannot be
+used again so keyloggers and shoulder-surfers are thwarted. A mobile
+phone application, such as the Google Authenticator (a 'prover'), can be used
+to calculate the user's current password, which is expressed as a six-digit
+number.
+Alternatively, the value can be calculated by some other application with access
+to the user's key and delivered to the user through SMS or some other channel.
+When prompted to authenticate, the user merely enters the six-digit code provided by
+the prover.
+
+Additionally, the overlay can also authenticate TOTP passwords
+combined with a static password. To do this, utilize one of the
+{TOTP1ANDPW}, {TOTP256ANDPW}, or {TOTP512ANDPW} password schemes
+and append the static password scheme value to the end of the
+userPassword attribute, separated by a pipe (|) character.
+
+This implementation complies with
+.B RFC 6238 TOTP Time-based One Time Passwords
+and includes support for the SHA-1, SHA-256, and SHA-512 HMAC
+algorithms.
+
+The HMAC key used in the TOTP computation is stored in the userPassword attribute
+of the user's LDAP entry and the LDAP Password Modify Extended Operation is used to
+set and change the value. The
+value should correspond to that used by the the prover (authenticator).
+
+.SH CONFIGURATION
+Once the module is loaded with the moduleload command from the synopsis,
+the {TOTP1}, {TOTP256}, {TOTP512}
+{TOTP1ANDPW}, {TOTP256ANDPW}, and {TOTP512ANDPW}
+password schemes will be recognized.
+
+On the databases where your users reside you must configure the
+totp overlay:
+
+.nf
+ database mdb
+ \...
+ overlay totp
+ \...
+.fi
+
+You can tell OpenLDAP to use one of these new schemes when processing LDAP
+Password Modify Extended Operations, thanks to the password-hash option in
+slapd.conf. For example:
+
+.nf
+ password-hash {TOTP256}
+.fi
+
+.SH NOTES
+This module includes functionality implemented by the slapo-lastbind overlay
+and cannot coexist with it in the same database. Also note
+that since the time that the last bind occurred
+is needed to properly implement TOTP, provisions need to be made to propagate
+the authTimestamp attribute to other servers that are providing authentication
+services.
+
+The hash functions for the {TOTP1ANDPW}, {TOTP256ANDPW}, and {TOTP512ANDPW}
+schemes expect the secret to be entered in the form:
+<OTP seed><DELIM><static password>, where DELIM is currently defined
+as the pipe character (|).
+
+.SH BUGS
+The time step is hard-coded to thirty seconds. This should be OK for many use cases,
+but it would be nice if the value
+could be changed with a configuration keyword or in an attribute value.
+However, after one successful initial authentication (to verify
+the clocks on the server and the user's prover are in sync) the TOTP
+value of the previous time window may also be used to successfully
+authenticate, provided no successful bind has been performed already
+in the current or previous time window. This eliminates false
+negatives caused by user or network delays
+entering or transmitting the TOTP value.
+
+The authenticator code that is generated is hard-coded to a length of six digits.
+While in most cases
+this is probably better than the alternative length of four digits, there may be
+cases where a four-digit value is preferred.
+
+In cases where password-hash lists multiple mechanisms, the TOTP key will also
+be changed at the same time. This is likely to be undesirable behavior.
+
+.SH "SEE ALSO"
+.BR slapd.conf (5) ldappasswd (1)
+.SH ACKNOWLEDGEMENT
+This work was developed by Howard Chu of Symas Corporation for inclusion in
+OpenLDAP Software.
+
+Password + TOTP support added by Greg Veldman on behalf of SCinet.
diff --git a/contrib/slapd-modules/ppm/CHANGELOG.md b/contrib/slapd-modules/ppm/CHANGELOG.md
new file mode 100644
index 0000000..d0e4ed7
--- /dev/null
+++ b/contrib/slapd-modules/ppm/CHANGELOG.md
@@ -0,0 +1,38 @@
+# CHANGELOG
+
+* 2021-02-23 David Coutadeur <david.coutadeur@gmail.com>
+ remove maxLength attribute (#21)
+ adapt the readme and documentation of ppm (#22)
+ prepare ppolicy10 in OpenLDAP 2.5 (#20, #23 and #24)
+ add pwdCheckModuleArg feature
+ Version 2.0
+* 2019-08-20 David Coutadeur <david.coutadeur@gmail.com>
+ adding debug symbols for ppm_test,
+ improve tests with the possibility to add username,
+ fix openldap crash when checkRDN=1 and username contains too short parts
+ Version 1.8
+* 2018-03-30 David Coutadeur <david.coutadeur@gmail.com>
+ various minor improvements provided by Tim Bishop (tdb) (compilation, test program,
+ imprvts in Makefile: new OLDAP_SOURCES variable pointing to OLDAP install. directory
+ Version 1.7
+* 2017-05-19 David Coutadeur <david.coutadeur@gmail.com>
+ Adds cracklib support
+ Readme adaptations and cleaning
+ Version 1.6
+* 2017-02-07 David Coutadeur <david.coutadeur@gmail.com>
+ Adds maxConsecutivePerClass (idea from Trevor Vaughan / tvaughan@onyxpoint.com)
+ Version 1.5
+* 2016-08-22 David Coutadeur <david.coutadeur@gmail.com>
+ Get config file from environment variable
+ Version 1.4
+* 2014-12-20 Daly Chikhaoui <dchikhaoui@janua.fr>
+ Adding checkRDN parameter
+ Version 1.3
+* 2014-10-28 David Coutadeur <david.coutadeur@gmail.com>
+ Adding maxLength parameter
+ Version 1.2
+* 2014-07-27 David Coutadeur <david.coutadeur@gmail.com>
+ Changing the configuration file and the configuration data structure
+ Version 1.1
+* 2014-04-04 David Coutadeur <david.coutadeur@gmail.com>
+ Version 1.0
diff --git a/contrib/slapd-modules/ppm/CONTRIBUTIONS.md b/contrib/slapd-modules/ppm/CONTRIBUTIONS.md
new file mode 100644
index 0000000..0d563d8
--- /dev/null
+++ b/contrib/slapd-modules/ppm/CONTRIBUTIONS.md
@@ -0,0 +1,5 @@
+# CONTRIBUTIONS
+
+* 2014 - 2021 - David Coutadeur <david.coutadeur@gmail.com> - maintainer
+* 2015 - Daly Chikhaoui - Janua <dchikhaoui@janua.fr> - contribution on RDN checks
+* 2017 - tdb - Tim Bishop - contribution on some compilation improvements
diff --git a/contrib/slapd-modules/ppm/INSTALL.md b/contrib/slapd-modules/ppm/INSTALL.md
new file mode 100644
index 0000000..6052dc6
--- /dev/null
+++ b/contrib/slapd-modules/ppm/INSTALL.md
@@ -0,0 +1,51 @@
+INSTALLATION
+============
+
+Dependencies
+------------------
+ppm is provided along with OpenLDAP sources. By default, it is available into contrib/slapd-modules.
+ - make sure both OpenLDAP sources and ppm are available for building.
+ - install cracklib development files if you want to test passwords against cracklib
+ - install pandoc if you want to build the man page
+
+
+Build
+-----
+Enter contrib/slapd-modules/ppm directory
+
+You can optionally customize some variables if you don't want the default ones:
+- prefix: prefix of the path where ppm is to be installed (defaults to /usr/local)
+- ldap_subdir: OpenLDAP specific subdirectory for modules and configurations (defaults to openldap )
+- moduledir: where the ppm module is to be deployed (defaults to $prefix/$libexecdir/$ldap_subdir)
+- etcdir: used to compose default sysconfdir location (defaults to $prefix/etc)
+- sysconfdir: where the ppm example policy is to be deployed (defaults to $prefix/$etcdir/$ldap_subdir)
+- LDAP_SRC: path to OpenLDAP source directory
+- Options in OPTS variable:
+ CONFIG_FILE: (DEPRECATED) path to a ppm configuration file (see PPM_READ_FILE in ppm.h)
+ note: ppm configuration now lies into pwdCheckModuleArg password policy attribute
+ provided example file is only helpful as an example or for testing
+ CRACKLIB: if defined, link against cracklib
+ DEBUG: If defined, ppm logs its actions with syslog
+
+
+To build ppm, simply run these commands:
+(based upon the default prefix /usr/local of OpenLDAP)
+
+```
+make clean
+make
+make test
+make doc
+make install
+```
+
+Here is an illustrative example showing how to overload some options:
+
+```
+make clean
+make LDAP_SRC=../../.. prefix=/usr/local libdir=/usr/local/lib
+make test LDAP_SRC=../../..
+make doc prefix=/usr/local
+make install prefix=/usr/local libdir=/usr/local/lib
+```
+
diff --git a/contrib/slapd-modules/ppm/LICENSE b/contrib/slapd-modules/ppm/LICENSE
new file mode 100644
index 0000000..03f692b
--- /dev/null
+++ b/contrib/slapd-modules/ppm/LICENSE
@@ -0,0 +1,50 @@
+OpenLDAP Public License
+
+The OpenLDAP Public License
+ Version 2.8.1, 25 November 2003
+
+Redistribution and use of this software and associated documentation
+("Software"), with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Redistributions in source form must retain copyright statements
+ and notices,
+
+2. Redistributions in binary form must reproduce applicable copyright
+ statements and notices, this list of conditions, and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution, and
+
+3. Redistributions must contain a verbatim copy of this document.
+
+The OpenLDAP Foundation may revise this license from time to time.
+Each revision is distinguished by a version number. You may use
+this Software under terms of this license revision or under the
+terms of any subsequent revision of the license.
+
+THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS
+CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S)
+OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+The names of the authors and copyright holders must not be used in
+advertising or otherwise to promote the sale, use or other dealing
+in this Software without specific, written prior permission. Title
+to copyright in this Software shall at all times remain with copyright
+holders.
+
+OpenLDAP is a registered trademark of the OpenLDAP Foundation.
+
+Copyright 1999-2003 The OpenLDAP Foundation, Redwood City,
+California, USA. All rights reserved. Permission to copy and
+distribute verbatim copies of this document is granted.
+
diff --git a/contrib/slapd-modules/ppm/Makefile b/contrib/slapd-modules/ppm/Makefile
new file mode 100644
index 0000000..7b6efad
--- /dev/null
+++ b/contrib/slapd-modules/ppm/Makefile
@@ -0,0 +1,97 @@
+# $OpenLDAP$
+# Copyright 2014 David Coutadeur, Paris.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted only as authorized by the OpenLDAP
+# Public License.
+#
+# A copy of this license is available in the file LICENSE in the
+# top-level directory of the distribution or, alternatively, at
+# <http://www.OpenLDAP.org/license.html>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/liblber/liblber.la $(LDAP_BUILD)/libraries/libldap/libldap.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2 -fpic
+
+# To skip linking against CRACKLIB make CRACK=no
+CRACK=yes
+CRACKDEF_yes= -DCRACKLIB
+CRACKDEF_no=
+
+CRACKLIB_yes= -lcrack
+CRACKLIB_no=
+
+CRACKDEF=$(CRACKDEF_$(CRACK))
+CRACKLIB=$(CRACKLIB_$(CRACK))
+
+DEFS = -DDEBUG $(CRACKDEF)
+# Define if using a config file:
+# -DCONFIG_FILE="\"$(sysconfdir)/$(EXAMPLE)\""
+
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS=ppm.so
+LTVER = 0:0:0
+
+LDAP_LIBS = -L$(LDAP_BUILD)/libraries/liblber/.libs -L$(LDAP_BUILD)/libraries/libldap/.libs -lldap -llber
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+etcdir = $(exec_prefix)/etc
+sysconfdir = $(etcdir)$(ldap_subdir)
+
+TEST=ppm_test
+EXAMPLE=ppm.example
+TESTS=./unit_tests.sh
+
+MANDOC=slapm-ppm.5
+MDDOC=ppm.md
+
+all: ppm $(TEST)
+
+$(TEST): ppm
+ $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(LDFLAGS) $(INCS) $(LDAP_LIBS) -Wl,-rpath=. -o $(TEST) ppm_test.c $(PROGRAMS) $(LDAP_LIBS) $(CRACKLIB)
+
+ppm.o:
+ $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) -c $(INCS) ppm.c
+
+ppm: ppm.o
+ $(CC) $(LDFLAGS) $(INCS) -shared -o $(PROGRAMS) ppm.o $(CRACKLIB)
+
+install: ppm
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS); do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)/$(moduledir) ; \
+ done
+ $(INSTALL) -m 644 $(EXAMPLE) $(DESTDIR)$(sysconfdir)/
+ $(INSTALL) -m 644 $(MANDOC) $(DESTDIR)$(man5dir)/
+# $(INSTALL) -m 755 $(TEST) $(libdir)
+
+.PHONY: clean
+
+clean:
+ $(RM) -f ppm.o $(PROGRAMS) ppm.lo $(TEST)
+ $(RM) -rf .libs
+
+test: ppm $(TEST)
+ LDAP_SRC=$(LDAP_SRC) $(TESTS)
+
+doc:
+ pandoc $(MDDOC) -s -t man -o $(MANDOC)
+ sed -i -e 's#ETCDIR#$(DESTDIR)$(sysconfdir)#g' $(MANDOC)
+
diff --git a/contrib/slapd-modules/ppm/README.md b/contrib/slapd-modules/ppm/README.md
new file mode 100644
index 0000000..129f788
--- /dev/null
+++ b/contrib/slapd-modules/ppm/README.md
@@ -0,0 +1 @@
+See ppm.md manual and INSTALL.md
diff --git a/contrib/slapd-modules/ppm/ppm.c b/contrib/slapd-modules/ppm/ppm.c
new file mode 100644
index 0000000..801ab6c
--- /dev/null
+++ b/contrib/slapd-modules/ppm/ppm.c
@@ -0,0 +1,684 @@
+/*
+ * ppm.c for OpenLDAP
+ *
+ * See LICENSE, README and INSTALL files
+ */
+
+
+/*
+ password policy module is called with:
+ int check_password (char *pPasswd, char **ppErrStr, Entry *e, void *pArg)
+
+ *pPasswd: new password
+ **ppErrStr: pointer to the string containing the error message
+ *e: pointer to the current user entry
+ *pArg: pointer to a struct berval holding the value of pwdCheckModuleArg attr
+*/
+
+#include <stdlib.h> // for type conversion, such as atoi...
+#include <regex.h> // for matching allowedParameters / conf file
+#include <string.h>
+#include <ctype.h>
+#include <portable.h>
+#include <slap.h>
+#include <stdarg.h> // for variable nb of arguments functions
+#include "ppm.h"
+
+#ifdef CRACKLIB
+#include "crack.h" // use cracklib to check password
+#endif
+
+void
+ppm_log(int priority, const char *format, ...)
+{
+ // if DEBUG flag is set
+ // logs into syslog (for OpenLDAP) or to stdout (for tests)
+#if defined(DEBUG)
+ if(ppm_test != 1)
+ {
+ va_list syslog_args;
+ va_start(syslog_args, format);
+ vsyslog(priority, format, syslog_args);
+ va_end(syslog_args);
+ }
+ else
+ {
+ va_list stdout_args;
+ va_start(stdout_args, format);
+ vprintf(format, stdout_args);
+ printf("\n");
+ fflush(stdout);
+ va_end(stdout_args);
+ }
+#endif
+}
+
+void
+strcpy_safe(char *dest, char *src, int length_dest)
+{
+ if(src == NULL)
+ {
+ dest[0] = '\0';
+ }
+ else
+ {
+ int length_src = strlen(src);
+ int n = (length_dest < length_src) ? length_dest : length_src;
+ // Copy the string — don’t copy too many bytes.
+ strncpy(dest, src, n);
+ // Ensure null-termination.
+ dest[n] = '\0';
+ }
+}
+
+genValue*
+getValue(conf *fileConf, int numParam, char* param)
+{
+ int i = 0;
+
+ // First scan parameters
+ for (i = 0; i < numParam; i++) {
+ if ((strlen(param) == strlen(fileConf[i].param))
+ && (strncmp(param, fileConf[i].param, strlen(fileConf[i].param))
+ == 0)) {
+ return &(fileConf[i].value);
+ }
+ }
+ return NULL;
+}
+
+int maxConsPerClass(char *password, char *charClass)
+{
+ // find maximum number of consecutive class characters in the password
+
+ int bestMax = 0;
+ int max = 0;
+ int i;
+
+ for(i=0 ; i<strlen(password) ; i++)
+ {
+ if(strchr(charClass,password[i]) != NULL)
+ {
+ // current character is in class
+ max++;
+ // is the new max a better candidate to maxConsecutivePerClass?
+ if(max > bestMax)
+ {
+ // found a better maxConsecutivePerClass
+ bestMax = max;
+ }
+ }
+ else
+ {
+ // current character is not in class
+ // reinitialize max
+ max=0;
+ }
+ }
+ return bestMax;
+}
+
+void
+storeEntry(char *param, char *value, valueType valType,
+ char *min, char *minForPoint, conf * fileConf, int *numParam)
+{
+ int i = 0;
+ int iMin;
+ int iMinForPoint;
+ if (min == NULL || strcmp(min,"") == 0)
+ iMin = 0;
+ else
+ iMin = atoi(min);
+
+ if (minForPoint == NULL || strcmp(minForPoint,"") == 0)
+ iMinForPoint = 0;
+ else
+ iMinForPoint = atoi(minForPoint);
+
+ // First scan parameters
+ for (i = 0; i < *numParam; i++) {
+ if ((strlen(param) == strlen(fileConf[i].param))
+ && (strncmp(param, fileConf[i].param, strlen(fileConf[i].param))
+ == 0)) {
+ // entry found, replace values
+ if(valType == typeInt)
+ fileConf[i].value.iVal = atoi(value);
+ else
+ strcpy_safe(fileConf[i].value.sVal, value, VALUE_MAX_LEN);
+ fileConf[i].min = iMin;
+ fileConf[i].minForPoint = iMinForPoint;
+ if(valType == typeInt)
+ ppm_log(LOG_NOTICE, "ppm: Accepted replaced value: %d",
+ fileConf[i].value.iVal);
+ else
+ ppm_log(LOG_NOTICE, "ppm: Accepted replaced value: %s",
+ fileConf[i].value.sVal);
+ return;
+ }
+ }
+ // entry not found, add values
+ strcpy_safe(fileConf[*numParam].param, param, PARAM_MAX_LEN);
+ fileConf[*numParam].iType = valType;
+ if(valType == typeInt)
+ fileConf[i].value.iVal = atoi(value);
+ else
+ strcpy_safe(fileConf[i].value.sVal, value, VALUE_MAX_LEN);
+ fileConf[*numParam].min = iMin;
+ fileConf[*numParam].minForPoint = iMinForPoint;
+ ++(*numParam);
+ if(valType == typeInt)
+ ppm_log(LOG_NOTICE, "ppm: Accepted new value: %d",
+ fileConf[*numParam].value.iVal);
+ else
+ ppm_log(LOG_NOTICE, "ppm: Accepted new value: %s",
+ fileConf[*numParam].value.sVal);
+}
+
+int
+typeParam(char* param)
+{
+ int i;
+ int n = sizeof(allowedParameters)/sizeof(params);
+
+ regex_t regex;
+ int reti;
+
+ for(i = 0 ; i < n ; i++ )
+ {
+ // Compile regular expression
+ reti = regcomp(&regex, allowedParameters[i].param, 0);
+ if (reti) {
+ ppm_log(LOG_ERR, "ppm: Cannot compile regex: %s",
+ allowedParameters[i].param);
+ return n;
+ }
+
+ // Execute regular expression
+ reti = regexec(&regex, param, 0, NULL, 0);
+ if (!reti)
+ {
+ regfree(&regex);
+ return i;
+ }
+ regfree(&regex);
+ }
+ return n;
+}
+
+#ifndef PPM_READ_FILE
+
+ /*
+ * read configuration into pwdCheckModuleArg attribute
+ * */
+ static void
+ read_config_attr(conf * fileConf, int *numParam, char *ppm_config_attr)
+ {
+ int nParam = 0; // position of found parameter in allowedParameters
+ int sAllowedParameters = sizeof(allowedParameters)/sizeof(params);
+ char arg[260*256];
+ char *token;
+ char *saveptr1;
+ char *saveptr2;
+
+ strcpy_safe(arg, ppm_config_attr, 260*256);
+ ppm_log(LOG_NOTICE, "ppm: Parsing pwdCheckModuleArg attribute");
+ token = strtok_r(arg, "\n", &saveptr1);
+
+ while (token != NULL) {
+ ppm_log(LOG_NOTICE, "ppm: get line: %s",token);
+ char *start = token;
+ char *word, *value;
+ char *min, *minForPoint;;
+
+ while (isspace(*start) && isascii(*start))
+ start++;
+
+ if (!isascii(*start))
+ {
+ token = strtok_r(NULL, "\n", &saveptr1);
+ continue;
+ }
+ if (start[0] == '#')
+ {
+ token = strtok_r(NULL, "\n", &saveptr1);
+ continue;
+ }
+
+ if ((word = strtok_r(start, " \t", &saveptr2))) {
+ if ((value = strtok_r(NULL, " \t", &saveptr2)) == NULL)
+ {
+ saveptr2 = NULL;
+ ppm_log(LOG_NOTICE, "ppm: No value, goto next parameter");
+ token = strtok_r(NULL, "\n", &saveptr1);
+ continue;
+ }
+ if (strchr(value, '\n') != NULL)
+ strchr(value, '\n')[0] = '\0';
+ min = strtok_r(NULL, " \t", &saveptr2);
+ if (min != NULL)
+ if (strchr(min, '\n') != NULL)
+ strchr(min, '\n')[0] = '\0';
+ minForPoint = strtok_r(NULL, " \t", &saveptr2);
+ if (minForPoint != NULL)
+ if (strchr(minForPoint, '\n') != NULL)
+ strchr(minForPoint, '\n')[0] = '\0';
+
+
+ nParam = typeParam(word); // search for param in allowedParameters
+ if (nParam != sAllowedParameters) // param has been found
+ {
+ ppm_log(LOG_NOTICE,
+ "ppm: Param = %s, value = %s, min = %s, minForPoint= %s",
+ word, value, min, minForPoint);
+
+ storeEntry(word, value, allowedParameters[nParam].iType,
+ min, minForPoint, fileConf, numParam);
+ }
+ else
+ {
+ ppm_log(LOG_NOTICE,
+ "ppm: Parameter '%s' rejected", word);
+ }
+
+ }
+ token = strtok_r(NULL, "\n", &saveptr1);
+ }
+
+ }
+
+#endif
+
+#ifdef PPM_READ_FILE
+
+ /*
+ * read configuration file (DEPRECATED)
+ * */
+ static void
+ read_config_file(conf * fileConf, int *numParam, char *ppm_config_file)
+ {
+ FILE *config;
+ char line[260] = "";
+ int nParam = 0; // position of found parameter in allowedParameters
+ int sAllowedParameters = sizeof(allowedParameters)/sizeof(params);
+
+ ppm_log(LOG_NOTICE, "ppm: Opening file %s", ppm_config_file);
+ if ((config = fopen(ppm_config_file, "r")) == NULL) {
+ ppm_log(LOG_ERR, "ppm: Opening file %s failed", ppm_config_file);
+ exit(EXIT_FAILURE);
+ }
+
+ while (fgets(line, 256, config) != NULL) {
+ char *start = line;
+ char *word, *value;
+ char *min, *minForPoint;;
+
+ while (isspace(*start) && isascii(*start))
+ start++;
+
+ if (!isascii(*start))
+ continue;
+ if (start[0] == '#')
+ continue;
+
+ if ((word = strtok(start, " \t"))) {
+ if ((value = strtok(NULL, " \t")) == NULL)
+ continue;
+ if (strchr(value, '\n') != NULL)
+ strchr(value, '\n')[0] = '\0';
+ min = strtok(NULL, " \t");
+ if (min != NULL)
+ if (strchr(min, '\n') != NULL)
+ strchr(min, '\n')[0] = '\0';
+ minForPoint = strtok(NULL, " \t");
+ if (minForPoint != NULL)
+ if (strchr(minForPoint, '\n') != NULL)
+ strchr(minForPoint, '\n')[0] = '\0';
+
+
+ nParam = typeParam(word); // search for param in allowedParameters
+ if (nParam != sAllowedParameters) // param has been found
+ {
+ ppm_log(LOG_NOTICE,
+ "ppm: Param = %s, value = %s, min = %s, minForPoint= %s",
+ word, value, min, minForPoint);
+
+ storeEntry(word, value, allowedParameters[nParam].iType,
+ min, minForPoint, fileConf, numParam);
+ }
+ else
+ {
+ ppm_log(LOG_NOTICE,
+ "ppm: Parameter '%s' rejected", word);
+ }
+
+ }
+ }
+
+ fclose(config);
+ }
+
+#endif
+
+static int
+realloc_error_message(char **target, int curlen, int nextlen)
+{
+ if (curlen < nextlen + MEMORY_MARGIN) {
+ ppm_log(LOG_WARNING,
+ "ppm: Reallocating szErrStr from %d to %d", curlen,
+ nextlen + MEMORY_MARGIN);
+ ber_memfree(*target);
+ curlen = nextlen + MEMORY_MARGIN;
+ *target = (char *) ber_memalloc(curlen);
+ }
+
+ return curlen;
+}
+
+// Does the password contains a token from the RDN ?
+int
+containsRDN(char* passwd, char* DN)
+{
+ char lDN[DN_MAX_LEN];
+ char * tmpToken;
+ char * token;
+ regex_t regex;
+ int reti;
+
+ strcpy_safe(lDN, DN, DN_MAX_LEN);
+
+ // Extract the RDN from the DN
+ tmpToken = strtok(lDN, ",+");
+ tmpToken = strtok(tmpToken, "=");
+ tmpToken = strtok(NULL, "=");
+
+ // Search for each token in the password */
+ token = strtok(tmpToken, TOKENS_DELIMITERS);
+
+ while (token != NULL)
+ {
+ if (strlen(token) > 2)
+ {
+ ppm_log(LOG_NOTICE, "ppm: Checking if %s part of RDN matches the password", token);
+ // Compile regular expression
+ reti = regcomp(&regex, token, REG_ICASE);
+ if (reti) {
+ ppm_log(LOG_ERR, "ppm: Cannot compile regex: %s", token);
+ return 0;
+ }
+
+ // Execute regular expression
+ reti = regexec(&regex, passwd, 0, NULL, 0);
+ if (!reti)
+ {
+ regfree(&regex);
+ return 1;
+ }
+
+ regfree(&regex);
+ }
+ else
+ {
+ ppm_log(LOG_NOTICE, "ppm: %s part of RDN is too short to be checked", token);
+ }
+ token = strtok(NULL, TOKENS_DELIMITERS);
+ }
+
+ return 0;
+}
+
+
+int
+check_password(char *pPasswd, char **ppErrStr, Entry *e, void *pArg)
+{
+
+ Entry *pEntry = e;
+ struct berval *pwdCheckModuleArg = pArg;
+ char *szErrStr = (char *) ber_memalloc(MEM_INIT_SZ);
+ int mem_len = MEM_INIT_SZ;
+ int numParam = 0; // Number of params in current configuration
+
+ int useCracklib;
+ char cracklibDict[VALUE_MAX_LEN];
+ char cracklibDictFiles[3][(VALUE_MAX_LEN+5)];
+ char const* cracklibExt[] = { ".hwm", ".pwd", ".pwi" };
+ FILE* fd;
+ char* res;
+ int minQuality;
+ int checkRDN;
+ char forbiddenChars[VALUE_MAX_LEN];
+ int nForbiddenChars = 0;
+ int nQuality = 0;
+ int maxConsecutivePerClass;
+ int nbInClass[CONF_MAX_SIZE];
+ int i,j;
+
+ ppm_log(LOG_NOTICE, "ppm: entry %s", pEntry->e_nname.bv_val);
+
+#ifdef PPM_READ_FILE
+ /* Determine if config file is to be read (DEPRECATED) */
+ char ppm_config_file[FILENAME_MAX_LEN];
+
+ ppm_log(LOG_NOTICE, "ppm: Not reading pwdCheckModuleArg attribute");
+ ppm_log(LOG_NOTICE, "ppm: instead, read configuration file (deprecated)");
+
+ strcpy_safe(ppm_config_file, getenv("PPM_CONFIG_FILE"), FILENAME_MAX_LEN);
+ if (ppm_config_file[0] == '\0') {
+ strcpy_safe(ppm_config_file, CONFIG_FILE, FILENAME_MAX_LEN);
+ }
+ ppm_log(LOG_NOTICE, "ppm: reading config file from %s", ppm_config_file);
+#else
+ if ( !pwdCheckModuleArg || !pwdCheckModuleArg->bv_val ) {
+ ppm_log(LOG_ERR, "ppm: No config provided in pwdCheckModuleArg");
+ mem_len = realloc_error_message(&szErrStr, mem_len,
+ strlen(GENERIC_ERROR));
+ sprintf(szErrStr, GENERIC_ERROR);
+ goto fail;
+ }
+
+ ppm_log(LOG_NOTICE, "ppm: Reading pwdCheckModuleArg attribute");
+ ppm_log(LOG_NOTICE, "ppm: RAW configuration: %s", pwdCheckModuleArg->bv_val);
+#endif
+
+ for (i = 0; i < CONF_MAX_SIZE; i++)
+ nbInClass[i] = 0;
+
+ /* Set default values */
+ conf fileConf[CONF_MAX_SIZE] = {
+ {"minQuality", typeInt, {.iVal = DEFAULT_QUALITY}, 0, 0
+ }
+ ,
+ {"checkRDN", typeInt, {.iVal = 0}, 0, 0
+ }
+ ,
+ {"forbiddenChars", typeStr, {.sVal = ""}, 0, 0
+ }
+ ,
+ {"maxConsecutivePerClass", typeInt, {.iVal = 0}, 0, 0
+ }
+ ,
+ {"useCracklib", typeInt, {.iVal = 0}, 0, 0
+ }
+ ,
+ {"cracklibDict", typeStr, {.sVal = "/var/cache/cracklib/cracklib_dict"}, 0, 0
+ }
+ ,
+ {"class-upperCase", typeStr, {.sVal = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"}, 0, 1
+ }
+ ,
+ {"class-lowerCase", typeStr, {.sVal = "abcdefghijklmnopqrstuvwxyz"}, 0, 1
+ }
+ ,
+ {"class-digit", typeStr, {.sVal = "0123456789"}, 0, 1
+ }
+ ,
+ {"class-special", typeStr,
+ {.sVal = "<>,?;.:/!§ù%*µ^¨$£²&é~\"#'{([-|è`_\\ç^à@)]°=}+"}, 0, 1
+ }
+ };
+ numParam = 10;
+
+ #ifdef PPM_READ_FILE
+ /* Read configuration file (DEPRECATED) */
+ read_config_file(fileConf, &numParam, ppm_config_file);
+ #else
+ /* Read configuration attribute (pwdCheckModuleArg) */
+ read_config_attr(fileConf, &numParam, (*(struct berval*)pwdCheckModuleArg).bv_val);
+ #endif
+
+ minQuality = getValue(fileConf, numParam, "minQuality")->iVal;
+ checkRDN = getValue(fileConf, numParam, "checkRDN")->iVal;
+ strcpy_safe(forbiddenChars,
+ getValue(fileConf, numParam, "forbiddenChars")->sVal,
+ VALUE_MAX_LEN);
+ maxConsecutivePerClass = getValue(fileConf, numParam, "maxConsecutivePerClass")->iVal;
+ useCracklib = getValue(fileConf, numParam, "useCracklib")->iVal;
+ strcpy_safe(cracklibDict,
+ getValue(fileConf, numParam, "cracklibDict")->sVal,
+ VALUE_MAX_LEN);
+
+
+ /*The password must have at least minQuality strength points with one
+ * point granted if the password contains at least minForPoint characters for each class
+ * It must contains at least min chars of each class
+ * It must not contain any char in forbiddenChar */
+
+ for (i = 0; i < strlen(pPasswd); i++) {
+
+ int n;
+ for (n = 0; n < numParam; n++) {
+ if (strstr(fileConf[n].param, "class-") != NULL) {
+ if (strchr(fileConf[n].value.sVal, pPasswd[i]) != NULL) {
+ ++(nbInClass[n]);
+ }
+ }
+ }
+ if (strchr(forbiddenChars, pPasswd[i]) != NULL) {
+ nForbiddenChars++;
+ }
+ }
+
+ // Password checking done, now loocking for minForPoint criteria
+ for (i = 0; i < CONF_MAX_SIZE; i++) {
+ if (strstr(fileConf[i].param, "class-") != NULL) {
+ if ((nbInClass[i] >= fileConf[i].minForPoint)
+ && strlen(fileConf[i].value.sVal) != 0) {
+ // 1 point granted
+ ++nQuality;
+ ppm_log(LOG_NOTICE, "ppm: 1 point granted for class %s",
+ fileConf[i].param);
+ }
+ }
+ }
+
+ if (nQuality < minQuality) {
+ mem_len = realloc_error_message(&szErrStr, mem_len,
+ strlen(PASSWORD_QUALITY_SZ) +
+ strlen(pEntry->e_nname.bv_val) + 4);
+ sprintf(szErrStr, PASSWORD_QUALITY_SZ, pEntry->e_nname.bv_val,
+ nQuality, minQuality);
+ goto fail;
+ }
+ // Password checking done, now loocking for constraintClass criteria
+ for (i = 0; i < CONF_MAX_SIZE; i++) {
+ if (strstr(fileConf[i].param, "class-") != NULL) {
+ if ((nbInClass[i] < fileConf[i].min) &&
+ strlen(fileConf[i].value.sVal) != 0) {
+ // constraint is not satisfied... goto fail
+ mem_len = realloc_error_message(&szErrStr, mem_len,
+ strlen(PASSWORD_CRITERIA) +
+ strlen(pEntry->e_nname.bv_val) +
+ 2 + PARAM_MAX_LEN);
+ sprintf(szErrStr, PASSWORD_CRITERIA, pEntry->e_nname.bv_val,
+ fileConf[i].min, fileConf[i].param);
+ goto fail;
+ }
+ }
+ }
+
+ // Password checking done, now loocking for forbiddenChars criteria
+ if (nForbiddenChars > 0) { // at least 1 forbidden char... goto fail
+ mem_len = realloc_error_message(&szErrStr, mem_len,
+ strlen(PASSWORD_FORBIDDENCHARS) +
+ strlen(pEntry->e_nname.bv_val) + 2 +
+ VALUE_MAX_LEN);
+ sprintf(szErrStr, PASSWORD_FORBIDDENCHARS, pEntry->e_nname.bv_val,
+ nForbiddenChars, forbiddenChars);
+ goto fail;
+ }
+
+ // Password checking done, now loocking for maxConsecutivePerClass criteria
+ for (i = 0; i < CONF_MAX_SIZE; i++) {
+ if (strstr(fileConf[i].param, "class-") != NULL) {
+ if ( maxConsecutivePerClass != 0 &&
+ (maxConsPerClass(pPasswd,fileConf[i].value.sVal)
+ > maxConsecutivePerClass)) {
+ // Too much consecutive characters of the same class
+ ppm_log(LOG_NOTICE, "ppm: Too much consecutive chars for class %s",
+ fileConf[i].param);
+ mem_len = realloc_error_message(&szErrStr, mem_len,
+ strlen(PASSWORD_MAXCONSECUTIVEPERCLASS) +
+ strlen(pEntry->e_nname.bv_val) + 2 +
+ PARAM_MAX_LEN);
+ sprintf(szErrStr, PASSWORD_MAXCONSECUTIVEPERCLASS, pEntry->e_nname.bv_val,
+ maxConsecutivePerClass, fileConf[i].param);
+ goto fail;
+ }
+ }
+ }
+#ifdef CRACKLIB
+ // Password checking done, now loocking for cracklib criteria
+ if ( useCracklib > 0 ) {
+
+ for( j = 0 ; j < 3 ; j++) {
+ strcpy_safe(cracklibDictFiles[j], cracklibDict, VALUE_MAX_LEN);
+ strcat(cracklibDictFiles[j], cracklibExt[j]);
+ if (( fd = fopen ( cracklibDictFiles[j], "r")) == NULL ) {
+ ppm_log(LOG_NOTICE, "ppm: Error while reading %s file",
+ cracklibDictFiles[j]);
+ mem_len = realloc_error_message(&szErrStr, mem_len,
+ strlen(GENERIC_ERROR));
+ sprintf(szErrStr, GENERIC_ERROR);
+ goto fail;
+
+ }
+ else {
+ fclose (fd);
+ }
+ }
+ res = (char *) FascistCheck (pPasswd, cracklibDict);
+ if ( res != NULL ) {
+ ppm_log(LOG_NOTICE, "ppm: cracklib does not validate password for entry %s",
+ pEntry->e_nname.bv_val);
+ mem_len = realloc_error_message(&szErrStr, mem_len,
+ strlen(PASSWORD_CRACKLIB) +
+ strlen(pEntry->e_nname.bv_val));
+ sprintf(szErrStr, PASSWORD_CRACKLIB, pEntry->e_nname.bv_val);
+ goto fail;
+
+ }
+
+ }
+#endif
+
+ // Password checking done, now looking for checkRDN criteria
+ if (checkRDN == 1 && containsRDN(pPasswd, pEntry->e_nname.bv_val))
+ // RDN check enabled and a token from RDN is found in password: goto fail
+ {
+ mem_len = realloc_error_message(&szErrStr, mem_len,
+ strlen(RDN_TOKEN_FOUND) +
+ strlen(pEntry->e_nname.bv_val));
+ sprintf(szErrStr, RDN_TOKEN_FOUND, pEntry->e_nname.bv_val);
+
+ goto fail;
+ }
+
+ *ppErrStr = strdup("");
+ ber_memfree(szErrStr);
+ return (LDAP_SUCCESS);
+
+ fail:
+ *ppErrStr = strdup(szErrStr);
+ ber_memfree(szErrStr);
+ return (EXIT_FAILURE);
+
+}
diff --git a/contrib/slapd-modules/ppm/ppm.example b/contrib/slapd-modules/ppm/ppm.example
new file mode 100644
index 0000000..10cf132
--- /dev/null
+++ b/contrib/slapd-modules/ppm/ppm.example
@@ -0,0 +1,85 @@
+# Example of ppm configuration
+
+# Such configuration must be stored into pwdCheckModuleArg attribute
+# of a password policy entry
+# See slapo-ppolicy for more details
+# Here is an example of such password policy:
+# dn: cn=default,ou=policies,dc=my-domain,dc=com
+# objectClass: pwdPolicy
+# objectClass: top
+# objectClass: pwdPolicyChecker
+# objectClass: person
+# pwdCheckQuality: 2
+# pwdAttribute: userPassword
+# sn: default
+# cn: default
+# pwdMinLength: 6
+# pwdCheckModule: /usr/local/lib/ppm.so
+# pwdCheckModuleArg:: bWluUXVhbGl0eSAzCmNoZWNrUkROIDAKZm9yYmlkZGVuQ2hhcnMKbWF4Q29uc2VjdXRpdmVQZXJDbGFzcyAwCnVzZUNyYWNrbGliIDAKY3JhY2tsaWJEaWN0IC92YXIvY2FjaGUvY3JhY2tsaWIvY3JhY2tsaWJfZGljdApjbGFzcy11cHBlckNhc2UgQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogMCAxCmNsYXNzLWxvd2VyQ2FzZSBhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5eiAwIDEKY2xhc3MtZGlnaXQgMDEyMzQ1Njc4OSAwIDEKY2xhc3Mtc3BlY2lhbCA8Piw/Oy46LyHCp8O5JSrCtV7CqCTCo8KyJsOpfiIjJ3soWy18w6hgX1zDp17DoEApXcKwPX0rIDAgMQ==
+#
+# Different parameters are separated by a linefeed (\n)
+# Parameters starting with a # are ignored
+# Use a base64 tool to code / decode the content of pwdCheckModuleArg
+
+
+
+# Parameters
+
+# minQuality parameter
+# Format:
+# minQuality [NUMBER]
+# Description:
+# One point is granted for each class for which MIN_FOR_POINT criteria is fulfilled.
+# defines the minimum point numbers for the password to be accepted.
+minQuality 3
+
+# checkRDN parameter
+# Format:
+# checkRDN [0 | 1]
+# Description:
+# If set to 1, password must not contain a token from the RDN.
+# Tokens are separated by these delimiters : space tabulation _ - , ; £
+checkRDN 0
+
+# forbiddenChars parameter
+# Format:
+# forbiddenChars [CHARACTERS_FORBIDDEN]
+# Description:
+# Defines the forbidden characters list (no separator).
+# If one of them is found in the password, then it is rejected.
+forbiddenChars
+
+# maxConsecutivePerClass parameter
+# Format:
+# maxConsecutivePerClass [NUMBER]
+# Description:
+# Defines the maximum number of consecutive character allowed for any class
+maxConsecutivePerClass 0
+
+# useCracklib parameter
+# Format:
+# useCracklib [0 | 1]
+# Description:
+# If set to 1, the password must pass the cracklib check
+useCracklib 0
+
+# cracklibDict parameter
+# Format:
+# cracklibDict [path_to_cracklib_dictionary]
+# Description:
+# directory+filename-prefix that your version of CrackLib will go hunting for
+# For example, /var/pw_dict resolves as /var/pw_dict.pwd,
+# /var/pw_dict.pwi and /var/pw_dict.hwm dictionary files
+cracklibDict /var/cache/cracklib/cracklib_dict
+
+# classes parameter
+# Format:
+# class-[CLASS_NAME] [CHARACTERS_DEFINING_CLASS] [MIN] [MIN_FOR_POINT]
+# Description:
+# [CHARACTERS_DEFINING_CLASS]: characters defining the class (no separator)
+# [MIN]: If at least [MIN] characters of this class is not found in the password, then it is rejected
+# [MIN_FOR_POINT]: one point is granted if password contains at least [MIN_FOR_POINT] character numbers of this class
+class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1
+class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1
+class-digit 0123456789 0 1
+class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+ 0 1
diff --git a/contrib/slapd-modules/ppm/ppm.h b/contrib/slapd-modules/ppm/ppm.h
new file mode 100644
index 0000000..25b360d
--- /dev/null
+++ b/contrib/slapd-modules/ppm/ppm.h
@@ -0,0 +1,125 @@
+/*
+ * ppm.h for OpenLDAP
+ *
+ * See LICENSE, README and INSTALL files
+ */
+
+#ifndef PPM_H_
+#define PPM_H_
+
+#include <stdlib.h> // for type conversion, such as atoi...
+#include <regex.h> // for matching allowedParameters / conf file
+#include <string.h>
+#include <ctype.h>
+#include <portable.h>
+#include <slap.h>
+
+#if defined(DEBUG)
+#include <syslog.h>
+#endif
+
+//#define PPM_READ_FILE 1 // old deprecated configuration mode
+ // 1: (deprecated) don't read pwdCheckModuleArg
+ // attribute, instead read config file
+ // 0: read pwdCheckModuleArg attribute
+
+/* config file parameters (DEPRECATED) */
+#ifndef CONFIG_FILE
+#define CONFIG_FILE "/etc/openldap/ppm.example"
+#endif
+#define FILENAME_MAX_LEN 512
+
+#define DEFAULT_QUALITY 3
+#define MEMORY_MARGIN 50
+#define MEM_INIT_SZ 64
+#define DN_MAX_LEN 512
+
+#define CONF_MAX_SIZE 50
+#define PARAM_MAX_LEN 32
+#define VALUE_MAX_LEN 128
+#define ATTR_NAME_MAX_LEN 150
+
+#define PARAM_PREFIX_CLASS "class-"
+#define TOKENS_DELIMITERS " ,;-_£\t"
+
+
+#define DEBUG_MSG_MAX_LEN 256
+
+#define PASSWORD_QUALITY_SZ \
+ "Password for dn=\"%s\" does not pass required number of strength checks (%d of %d)"
+#define PASSWORD_CRITERIA \
+ "Password for dn=\"%s\" has not reached the minimum number of characters (%d) for class %s"
+#define PASSWORD_MAXCONSECUTIVEPERCLASS \
+ "Password for dn=\"%s\" has reached the maximum number of characters (%d) for class %s"
+#define PASSWORD_FORBIDDENCHARS \
+ "Password for dn=\"%s\" contains %d forbidden characters in %s"
+#define RDN_TOKEN_FOUND \
+ "Password for dn=\"%s\" contains tokens from the RDN"
+#define GENERIC_ERROR \
+ "Error while checking password"
+#define PASSWORD_CRACKLIB \
+ "Password for dn=\"%s\" is too weak"
+#define BAD_PASSWORD_SZ \
+ "Bad password for dn=\"%s\" because %s"
+
+
+
+typedef union genValue {
+ int iVal;
+ char sVal[VALUE_MAX_LEN];
+} genValue;
+
+typedef enum {
+ typeInt,
+ typeStr
+} valueType;
+
+typedef struct params {
+ char param[PARAM_MAX_LEN];
+ valueType iType;
+} params;
+
+// allowed parameters loaded into configuration structure
+// it also contains the type of the corresponding value
+params allowedParameters[7] = {
+ {"^minQuality", typeInt},
+ {"^checkRDN", typeInt},
+ {"^forbiddenChars", typeStr},
+ {"^maxConsecutivePerClass", typeInt},
+ {"^useCracklib", typeInt},
+ {"^cracklibDict", typeStr},
+ {"^class-.*", typeStr}
+};
+
+
+// configuration structure, containing a parameter, a value,
+// a corresponding min and minForPoint indicators if necessary
+// and a type for the value (typeInt or typeStr)
+typedef struct conf {
+ char param[PARAM_MAX_LEN];
+ valueType iType;
+ genValue value;
+ int min;
+ int minForPoint;
+} conf;
+
+void ppm_log(int priority, const char *format, ...);
+int min(char *str1, char *str2);
+#ifndef PPM_READ_FILE
+ static void read_config_attr(conf * fileConf, int *numParam, char *ppm_config_attr);
+#endif
+#ifdef PPM_READ_FILE
+ static void read_config_file(conf * fileConf, int *numParam, char *ppm_config_file);
+#endif
+int check_password(char *pPasswd, char **ppErrStr, Entry *e, void *pArg);
+int maxConsPerClass(char *password, char *charClass);
+void storeEntry(char *param, char *value, valueType valType,
+ char *min, char *minForPoint, conf * fileConf, int *numParam);
+int typeParam(char* param);
+genValue* getValue(conf *fileConf, int numParam, char* param);
+void strcpy_safe(char *dest, char *src, int length_dest);
+
+
+int ppm_test = 0;
+
+#endif
diff --git a/contrib/slapd-modules/ppm/ppm.md b/contrib/slapd-modules/ppm/ppm.md
new file mode 100644
index 0000000..5b1accb
--- /dev/null
+++ b/contrib/slapd-modules/ppm/ppm.md
@@ -0,0 +1,343 @@
+---
+title: ppm
+section: 5
+header: File Formats Manual
+footer: ppm
+date: August 24, 2021
+---
+
+# NAME
+
+ppm (Password Policy Module) - extension of the password policy overlay
+
+# SYNOPSIS
+
+ETCDIR/ppm.example
+
+# DESCRIPTION
+
+**ppm** is an OpenLDAP module for checking password quality when they are modified.
+Passwords are checked against the presence or absence of certain character classes.
+
+This module is used as an extension of the OpenLDAP password policy controls,
+see slapo-ppolicy(5) section **pwdCheckModule**.
+
+
+# USAGE
+
+Create a password policy entry and indicate the path of the ppm.so library
+and the content of the desired policy.
+Use a base64 tool to code / decode the content of the policy stored into
+**pwdCheckModuleArg**. Here is an example:
+
+```
+dn: cn=default,ou=policies,dc=my-domain,dc=com
+objectClass: pwdPolicy
+objectClass: top
+objectClass: pwdPolicyChecker
+objectClass: person
+pwdCheckQuality: 2
+pwdAttribute: userPassword
+sn: default
+cn: default
+pwdMinLength: 6
+pwdCheckModule: /usr/local/lib/ppm.so
+pwdCheckModuleArg:: bWluUXVhbGl0eSAzCmNoZWNrUkROIDAKZm9yYmlkZGVuQ2hhcnMKbWF4Q29uc2VjdXRpdmVQZXJDbGFzcyAwCnVzZUNyYWNrbGliIDAKY3JhY2tsaWJEaWN0IC92YXIvY2FjaGUvY3JhY2tsaWIvY3JhY2tsaWJfZGljdApjbGFzcy11cHBlckNhc2UgQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogMCAxCmNsYXNzLWxvd2VyQ2FzZSBhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5eiAwIDEKY2xhc3MtZGlnaXQgMDEyMzQ1Njc4OSAwIDEKY2xhc3Mtc3BlY2lhbCA8Piw/Oy46LyHCp8O5JSrCtV7CqCTCo8KyJsOpfiIjJ3soWy18w6hgX1zDp17DoEApXcKwPX0rIDAgMQ==
+```
+
+
+See **slapo-ppolicy** for more information, but to sum up:
+
+- enable ppolicy overlay in your database.
+- define a default password policy in OpenLDAP configuration or use pwdPolicySubentry attribute to point to the given policy.
+
+This example show the activation for a **slapd.conf** file
+(see **slapd-config** and **slapo-ppolicy** for more information for
+ **cn=config** configuration)
+
+```
+overlay ppolicy
+ppolicy_default "cn=default,ou=policies,dc=my-domain,dc=com"
+#ppolicy_use_lockout # for having more infos about the lockout
+```
+
+
+# FEATURES
+
+Here are the main features:
+
+- 4 character classes are defined by default:
+upper case, lower case, digits and special characters.
+
+- more character classes can be defined, just write your own.
+
+- passwords must match the amount of quality points.
+A point is validated when at least m characters of the corresponding
+character class are present in the password.
+
+- passwords must have at least n of the corresponding character class
+present, else they are rejected.
+
+- the two previous criteria are checked against any specific character class
+defined.
+
+- if a password contains any of the forbidden characters, then it is
+rejected.
+
+- if a password contains tokens from the RDN, then it is rejected.
+
+- if a password does not pass cracklib check, then it is rejected.
+
+
+# CONFIGURATION
+
+Since OpenLDAP 2.5 version, ppm configuration is held in a binary
+attribute of the password policy: **pwdCheckModuleArg**
+
+The example file (**ETCDIR/ppm.example** by default) is to be
+considered as an example configuration, to import in the **pwdCheckModuleArg**
+attribute. It is also used for testing passwords with the test program
+provided.
+
+If for some reasons, any parameter is not found, it will be given its
+default value.
+
+Note: you can still compile ppm to use the configuration file, by enabling
+**PPM_READ_FILE** in **ppm.h** (but this is deprecated now). If you decide to do so,
+you can use the **PPM_CONFIG_FILE** environment variable for overloading the
+configuration file path.
+
+The syntax of a configuration line is:
+
+```
+parameter value [min] [minForPoint]
+```
+
+with spaces being delimiters and Line Feed (LF) ending the line.
+
+Parameter names **are** case sensitive.
+
+Lines beginning by a **#** are considered as comments.
+
+The default configuration is the following:
+
+```
+# minQuality parameter
+# Format:
+# minQuality [NUMBER]
+# Description:
+# One point is granted for each class for which MIN_FOR_POINT criteria is fulfilled.
+# defines the minimum point numbers for the password to be accepted.
+minQuality 3
+
+# checkRDN parameter
+# Format:
+# checkRDN [0 | 1]
+# Description:
+# If set to 1, password must not contain a token from the RDN.
+# Tokens are separated by the following delimiters : space tabulation _ - , ; £
+checkRDN 0
+
+# forbiddenChars parameter
+# Format:
+# forbiddenChars [CHARACTERS_FORBIDDEN]
+# Description:
+# Defines the forbidden characters list (no separator).
+# If one of them is found in the password, then it is rejected.
+forbiddenChars
+
+# maxConsecutivePerClass parameter
+# Format:
+# maxConsecutivePerClass [NUMBER]
+# Description:
+# Defines the maximum number of consecutive character allowed for any class
+maxConsecutivePerClass 0
+
+# useCracklib parameter
+# Format:
+# useCracklib [0 | 1]
+# Description:
+# If set to 1, the password must pass the cracklib check
+useCracklib 0
+
+# cracklibDict parameter
+# Format:
+# cracklibDict [path_to_cracklib_dictionary]
+# Description:
+# directory+filename-prefix that your version of CrackLib will go hunting for
+# For example, /var/pw_dict resolves as /var/pw_dict.pwd,
+# /var/pw_dict.pwi and /var/pw_dict.hwm dictionary files
+cracklibDict /var/cache/cracklib/cracklib_dict
+
+# classes parameter
+# Format:
+# class-[CLASS_NAME] [CHARACTERS_DEFINING_CLASS] [MIN] [MIN_FOR_POINT]
+# Description:
+# [CHARACTERS_DEFINING_CLASS]: characters defining the class (no separator)
+# [MIN]: If at least [MIN] characters of this class is not found in the password, then it is rejected
+# [MIN_FOR_POINT]: one point is granted if password contains at least [MIN_FOR_POINT] character numbers of this class
+class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1
+class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1
+class-digit 0123456789 0 1
+class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+ 0 1
+```
+
+# EXAMPLE
+
+With this policy:
+```
+minQuality 4
+forbiddenChars .?,
+checkRDN 1
+class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 5
+class-lowerCase abcdefghijklmnopqrstuvwxyz 0 12
+class-digit 0123456789 0 1
+class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+ 0 1
+class-myClass :) 1 1``
+```
+
+the password **ThereIsNoCowLevel)** is working, because:
+
+- it has 4 character classes validated : upper, lower, special, and myClass
+- it has no character among .?,
+- it has at least one character among : or )
+
+but it won't work for the user uid=John Cowlevel,ou=people,cn=example,cn=com,
+because the token "Cowlevel" from his RDN exists in the password (case insensitive).
+
+
+# LOGS
+
+If a user password is rejected by **ppm**, the user will get this type of message:
+
+Typical user message from ldappasswd(5):
+
+```
+ Result: Constraint violation (19)
+ Additional info: Password for dn=\"%s\" does not pass required number of strength checks (2 of 3)
+```
+
+A more detailed message is written to the server log.
+
+Server log:
+
+```
+Feb 26 14:46:10 debian-11-64 slapd[1981]: conn=1000 op=16 MOD dn="uid=user,ou=persons,dc=my-domain,dc=com"
+Feb 26 14:46:10 debian-11-64 slapd[1981]: conn=1000 op=16 MOD attr=userPassword
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: entry uid=user,ou=persons,dc=my-domain,dc=com
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Reading pwdCheckModuleArg attribute
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: RAW configuration: # minQuality parameter#012# Format:#012# minQuality [NUMBER]#012# Description:#012# One point is granted for each class for which MIN_FOR_POINT criteria is fulfilled.#012# defines the minimum point numbers for the password to be accepted.#012minQuality 3#012#012# checkRDN parameter#012# Format:#012# checkRDN [0 | 1]#012# Description:#012# If set to 1, password must not contain a token from the RDN.#012# Tokens are separated by the following delimiters : space tabulation _ - , ; £#012checkRDN 0#012#012# forbiddenChars parameter#012# Format:#012# forbiddenChars [CHARACTERS_FORBIDDEN]#012# Description:#012# Defines the forbidden characters list (no separator).#012# If one of them is found in the password, then it is rejected.#012forbiddenChars#012#012# maxConsecutivePerClass parameter#012# Format:#012# maxConsecutivePerClass [NUMBER]#012# Description:#012# Defines the maximum number of consecutive character allowed for any class#012maxConsecutivePerClass 0#012#012# useCracklib parameter#012# Format:#012# useCracklib [0 | 1]#012# Description:#012# If set to 1, the password must pass the cracklib check#012useCracklib 0#012#012# cracklibDict parameter#012# Format:#012# cracklibDict [path_to_cracklib_dictionary]#012# Description:#012# directory+filename-prefix that your version of CrackLib will go hunting for#012# For example, /var/pw_dict resolves as /var/pw_dict.pwd,#012# /var/pw_dict.pwi and /var/pw_dict.hwm dictionary files#012cracklibDict /var/cache/cracklib/cracklib_dict#012#012# classes parameter#012# Format:#012# class-[CLASS_NAME] [CHARACTERS_DEFINING_CLASS] [MIN] [MIN_FOR_POINT]#012# Description:#012# [CHARACTERS_DEFINING_CLASS]: characters defining the class (no separator)#012# [MIN]: If at least [MIN] characters of this class is not found in the password, then it is rejected#012# [MIN_FOR_POINT]: one point is granted if password contains at least [MIN_FOR_POINT] character numbers of this class#012class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1#012class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1#012class-digit 0123456789 0 1#012class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+ 0 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Parsing pwdCheckModuleArg attribute
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # minQuality parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # minQuality [NUMBER]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # One point is granted for each class for which MIN_FOR_POINT criteria is fulfilled.
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # defines the minimum point numbers for the password to be accepted.
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: minQuality 3
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = minQuality, value = 3, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: 3
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # checkRDN parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # checkRDN [0 | 1]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # If set to 1, password must not contain a token from the RDN.
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Tokens are separated by the following delimiters : space tabulation _ - , ; £
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: checkRDN 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = checkRDN, value = 0, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # forbiddenChars parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # forbiddenChars [CHARACTERS_FORBIDDEN]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Defines the forbidden characters list (no separator).
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # If one of them is found in the password, then it is rejected.
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: forbiddenChars
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: No value, goto next parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # maxConsecutivePerClass parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # maxConsecutivePerClass [NUMBER]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Defines the maximum number of consecutive character allowed for any class
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: maxConsecutivePerClass 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = maxConsecutivePerClass, value = 0, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # useCracklib parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # useCracklib [0 | 1]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # If set to 1, the password must pass the cracklib check
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: useCracklib 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = useCracklib, value = 0, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # cracklibDict parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # cracklibDict [path_to_cracklib_dictionary]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # directory+filename-prefix that your version of CrackLib will go hunting for
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # For example, /var/pw_dict resolves as /var/pw_dict.pwd,
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # /var/pw_dict.pwi and /var/pw_dict.hwm dictionary files
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: cracklibDict /var/cache/cracklib/cracklib_dict
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = cracklibDict, value = /var/cache/cracklib/cracklib_dict, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: /var/cache/cracklib/cracklib_dict
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # classes parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # class-[CLASS_NAME] [CHARACTERS_DEFINING_CLASS] [MIN] [MIN_FOR_POINT]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # [CHARACTERS_DEFINING_CLASS]: characters defining the class (no separator)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # [MIN]: If at least [MIN] characters of this class is not found in the password, then it is rejected
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # [MIN_FOR_POINT]: one point is granted if password contains at least [MIN_FOR_POINT] character numbers of this class
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = class-upperCase, value = ABCDEFGHIJKLMNOPQRSTUVWXYZ, min = 0, minForPoint= 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: ABCDEFGHIJKLMNOPQRSTUVWXYZ
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = class-lowerCase, value = abcdefghijklmnopqrstuvwxyz, min = 0, minForPoint= 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: abcdefghijklmnopqrstuvwxyz
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: class-digit 0123456789 0 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = class-digit, value = 0123456789, min = 0, minForPoint= 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: 0123456789
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+ 0 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = class-special, value = <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+, min = 0, minForPoint= 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: <>,?;.:/!§ù%*µ^¨$£²&é~"#'{([-|è`_\ç^à@)]°=}+
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: 1 point granted for class class-lowerCase
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: 1 point granted for class class-digit
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Reallocating szErrStr from 64 to 173
+Feb 26 14:46:10 debian-11-64 slapd[1981]: check_password_quality: module error: (/usr/local/lib/ppm.so) Password for dn="uid=user,ou=persons,dc=my-domain,dc=com" does not pass required number of strength checks (2 of 3).[1]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: conn=1000 op=16 RESULT tag=103 err=19 qtime=0.000020 etime=0.001496 text=Password for dn="uid=user,ou=persons,dc=my-domain,dc=com" does not pass required number of strength checks (2 of 3)
+```
+
+
+# TESTS
+
+There is a unit test script: **unit_tests.sh** that illustrates checking some passwords.
+
+It is possible to test one particular password using directly the test program:
+
+```
+cd /usr/local/lib
+LD_LIBRARY_PATH=. ./ppm_test "uid=test,ou=users,dc=my-domain,dc=com" "my_password" "/usr/local/etc/openldap/ppm.example" && echo OK
+```
+
+
+# FILES
+
+**ETCDIR/ppm.example**
+
+> example of ppm configuration to be inserted in **pwdCheckModuleArg** attribute of given password policy
+
+**ppm.so**
+
+> ppm library, loaded by the **pwdCheckModule** attribute of given password policy
+
+**ppm_test**
+
+> small test program for checking password in a command-line
+
+
+# SEE ALSO
+
+**slapo-ppolicy**(5), **slapd-config**(5), **slapd.conf**(5)
+
+# ACKNOWLEDGEMENTS
+
+This module was developed in 2014-2021 by David Coutadeur.
diff --git a/contrib/slapd-modules/ppm/ppm_test.c b/contrib/slapd-modules/ppm/ppm_test.c
new file mode 100644
index 0000000..520aa0a
--- /dev/null
+++ b/contrib/slapd-modules/ppm/ppm_test.c
@@ -0,0 +1,66 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "ppm.h"
+
+int main(int argc, char *argv[])
+{
+ /*
+ * argv[1]: user
+ * argv[2]: password
+ * argv[3]: configuration file
+ */
+
+ int ret = 1;
+
+ if(argc > 2)
+ {
+ printf("Testing user %s password: '%s' against %s policy config file \n",
+ argv[1], argv[2], argv[3]
+ );
+
+ /* format user entry */
+ char *errmsg = NULL;
+ Entry pEntry;
+ pEntry.e_nname.bv_val=argv[1];
+ pEntry.e_name.bv_val=argv[1];
+
+ /* get configuration file content */
+ struct berval pArg;
+ FILE *fp;
+ if ((fp = fopen(argv[3],"r")) == NULL)
+ {
+ fprintf(stderr,"Unable to open config file for reading\n");
+ return ret;
+ }
+ char *fcontent = NULL;
+ fseek(fp, 0, SEEK_END);
+ long fsize = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ fcontent = malloc(fsize);
+ fread(fcontent, 1, fsize, fp);
+ fclose(fp);
+ pArg.bv_val = fcontent;
+
+ ppm_test=1; // enable ppm_test for informing ppm not to use syslog
+
+ ret = check_password(argv[2], &errmsg, &pEntry, &pArg);
+
+ if(ret == 0)
+ {
+ printf("Password is OK!\n");
+ }
+ else
+ {
+ printf("Password failed checks : %s\n", errmsg);
+ }
+
+ ber_memfree(errmsg);
+ return ret;
+
+ }
+
+ return ret;
+}
+
+
+
diff --git a/contrib/slapd-modules/ppm/slapm-ppm.5 b/contrib/slapd-modules/ppm/slapm-ppm.5
new file mode 100644
index 0000000..10e9c8d
--- /dev/null
+++ b/contrib/slapd-modules/ppm/slapm-ppm.5
@@ -0,0 +1,360 @@
+.\" Automatically generated by Pandoc 2.9.2.1
+.\"
+.TH "ppm" "5" "August 24, 2021" "ppm" "File Formats Manual"
+.hy
+.SH NAME
+.PP
+ppm (Password Policy Module) - extension of the password policy overlay
+.SH SYNOPSIS
+.PP
+ETCDIR/ppm.example
+.SH DESCRIPTION
+.PP
+\f[B]ppm\f[R] is an OpenLDAP module for checking password quality when
+they are modified.
+Passwords are checked against the presence or absence of certain
+character classes.
+.PP
+This module is used as an extension of the OpenLDAP password policy
+controls, see slapo-ppolicy(5) section \f[B]pwdCheckModule\f[R].
+.SH USAGE
+.PP
+Create a password policy entry and indicate the path of the ppm.so
+library and the content of the desired policy.
+Use a base64 tool to code / decode the content of the policy stored into
+\f[B]pwdCheckModuleArg\f[R].
+Here is an example:
+.IP
+.nf
+\f[C]
+dn: cn=default,ou=policies,dc=my-domain,dc=com
+objectClass: pwdPolicy
+objectClass: top
+objectClass: pwdPolicyChecker
+objectClass: person
+pwdCheckQuality: 2
+pwdAttribute: userPassword
+sn: default
+cn: default
+pwdMinLength: 6
+pwdCheckModule: /usr/local/lib/ppm.so
+pwdCheckModuleArg:: bWluUXVhbGl0eSAzCmNoZWNrUkROIDAKZm9yYmlkZGVuQ2hhcnMKbWF4Q29uc2VjdXRpdmVQZXJDbGFzcyAwCnVzZUNyYWNrbGliIDAKY3JhY2tsaWJEaWN0IC92YXIvY2FjaGUvY3JhY2tsaWIvY3JhY2tsaWJfZGljdApjbGFzcy11cHBlckNhc2UgQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogMCAxCmNsYXNzLWxvd2VyQ2FzZSBhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5eiAwIDEKY2xhc3MtZGlnaXQgMDEyMzQ1Njc4OSAwIDEKY2xhc3Mtc3BlY2lhbCA8Piw/Oy46LyHCp8O5JSrCtV7CqCTCo8KyJsOpfiIjJ3soWy18w6hgX1zDp17DoEApXcKwPX0rIDAgMQ==
+\f[R]
+.fi
+.PP
+See \f[B]slapo-ppolicy\f[R] for more information, but to sum up:
+.IP \[bu] 2
+enable ppolicy overlay in your database.
+.IP \[bu] 2
+define a default password policy in OpenLDAP configuration or use
+pwdPolicySubentry attribute to point to the given policy.
+.PP
+This example show the activation for a \f[B]slapd.conf\f[R] file (see
+\f[B]slapd-config\f[R] and \f[B]slapo-ppolicy\f[R] for more information
+for \f[B]cn=config\f[R] configuration)
+.IP
+.nf
+\f[C]
+overlay ppolicy
+ppolicy_default \[dq]cn=default,ou=policies,dc=my-domain,dc=com\[dq]
+#ppolicy_use_lockout # for having more infos about the lockout
+\f[R]
+.fi
+.SH FEATURES
+.PP
+Here are the main features:
+.IP \[bu] 2
+4 character classes are defined by default: upper case, lower case,
+digits and special characters.
+.IP \[bu] 2
+more character classes can be defined, just write your own.
+.IP \[bu] 2
+passwords must match the amount of quality points.
+A point is validated when at least m characters of the corresponding
+character class are present in the password.
+.IP \[bu] 2
+passwords must have at least n of the corresponding character class
+present, else they are rejected.
+.IP \[bu] 2
+the two previous criteria are checked against any specific character
+class defined.
+.IP \[bu] 2
+if a password contains any of the forbidden characters, then it is
+rejected.
+.IP \[bu] 2
+if a password contains tokens from the RDN, then it is rejected.
+.IP \[bu] 2
+if a password does not pass cracklib check, then it is rejected.
+.SH CONFIGURATION
+.PP
+Since OpenLDAP 2.5 version, ppm configuration is held in a binary
+attribute of the password policy: \f[B]pwdCheckModuleArg\f[R]
+.PP
+The example file (\f[B]ETCDIR/ppm.example\f[R] by default) is to be
+considered as an example configuration, to import in the
+\f[B]pwdCheckModuleArg\f[R] attribute.
+It is also used for testing passwords with the test program provided.
+.PP
+If for some reasons, any parameter is not found, it will be given its
+default value.
+.PP
+Note: you can still compile ppm to use the configuration file, by
+enabling \f[B]PPM_READ_FILE\f[R] in \f[B]ppm.h\f[R] (but this is
+deprecated now).
+If you decide to do so, you can use the \f[B]PPM_CONFIG_FILE\f[R]
+environment variable for overloading the configuration file path.
+.PP
+The syntax of a configuration line is:
+.IP
+.nf
+\f[C]
+parameter value [min] [minForPoint]
+\f[R]
+.fi
+.PP
+with spaces being delimiters and Line Feed (LF) ending the line.
+.PP
+Parameter names \f[B]are\f[R] case sensitive.
+.PP
+Lines beginning by a \f[B]#\f[R] are considered as comments.
+.PP
+The default configuration is the following:
+.IP
+.nf
+\f[C]
+# minQuality parameter
+# Format:
+# minQuality [NUMBER]
+# Description:
+# One point is granted for each class for which MIN_FOR_POINT criteria is fulfilled.
+# defines the minimum point numbers for the password to be accepted.
+minQuality 3
+
+# checkRDN parameter
+# Format:
+# checkRDN [0 | 1]
+# Description:
+# If set to 1, password must not contain a token from the RDN.
+# Tokens are separated by the following delimiters : space tabulation _ - , ; \[Po]
+checkRDN 0
+
+# forbiddenChars parameter
+# Format:
+# forbiddenChars [CHARACTERS_FORBIDDEN]
+# Description:
+# Defines the forbidden characters list (no separator).
+# If one of them is found in the password, then it is rejected.
+forbiddenChars
+
+# maxConsecutivePerClass parameter
+# Format:
+# maxConsecutivePerClass [NUMBER]
+# Description:
+# Defines the maximum number of consecutive character allowed for any class
+maxConsecutivePerClass 0
+
+# useCracklib parameter
+# Format:
+# useCracklib [0 | 1]
+# Description:
+# If set to 1, the password must pass the cracklib check
+useCracklib 0
+
+# cracklibDict parameter
+# Format:
+# cracklibDict [path_to_cracklib_dictionary]
+# Description:
+# directory+filename-prefix that your version of CrackLib will go hunting for
+# For example, /var/pw_dict resolves as /var/pw_dict.pwd,
+# /var/pw_dict.pwi and /var/pw_dict.hwm dictionary files
+cracklibDict /var/cache/cracklib/cracklib_dict
+
+# classes parameter
+# Format:
+# class-[CLASS_NAME] [CHARACTERS_DEFINING_CLASS] [MIN] [MIN_FOR_POINT]
+# Description:
+# [CHARACTERS_DEFINING_CLASS]: characters defining the class (no separator)
+# [MIN]: If at least [MIN] characters of this class is not found in the password, then it is rejected
+# [MIN_FOR_POINT]: one point is granted if password contains at least [MIN_FOR_POINT] character numbers of this class
+class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1
+class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1
+class-digit 0123456789 0 1
+class-special <>,?;.:/!\[sc]\[`u]%*\[mc]\[ha]\[ad]$\[Po]\[S2]&\['e]\[ti]\[dq]#\[aq]{([-|\[`e]\[ga]_\[rs]\[,c]\[ha]\[`a]\[at])]\[de]=}+ 0 1
+\f[R]
+.fi
+.SH EXAMPLE
+.PP
+With this policy:
+.IP
+.nf
+\f[C]
+minQuality 4
+forbiddenChars .?,
+checkRDN 1
+class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 5
+class-lowerCase abcdefghijklmnopqrstuvwxyz 0 12
+class-digit 0123456789 0 1
+class-special <>,?;.:/!\[sc]\[`u]%*\[mc]\[ha]\[ad]$\[Po]\[S2]&\['e]\[ti]\[dq]#\[aq]{([-|\[`e]\[ga]_\[rs]\[,c]\[ha]\[`a]\[at])]\[de]=}+ 0 1
+class-myClass :) 1 1\[ga]\[ga]
+\f[R]
+.fi
+.PP
+the password \f[B]ThereIsNoCowLevel)\f[R] is working, because:
+.IP \[bu] 2
+it has 4 character classes validated : upper, lower, special, and
+myClass
+.IP \[bu] 2
+it has no character among .?,
+.IP \[bu] 2
+it has at least one character among : or )
+.PP
+but it won\[cq]t work for the user uid=John
+Cowlevel,ou=people,cn=example,cn=com, because the token
+\[lq]Cowlevel\[rq] from his RDN exists in the password (case
+insensitive).
+.SH LOGS
+.PP
+If a user password is rejected by \f[B]ppm\f[R], the user will get this
+type of message:
+.PP
+Typical user message from ldappasswd(5):
+.IP
+.nf
+\f[C]
+ Result: Constraint violation (19)
+ Additional info: Password for dn=\[rs]\[dq]%s\[rs]\[dq] does not pass required number of strength checks (2 of 3)
+\f[R]
+.fi
+.PP
+A more detailed message is written to the server log.
+.PP
+Server log:
+.IP
+.nf
+\f[C]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: conn=1000 op=16 MOD dn=\[dq]uid=user,ou=persons,dc=my-domain,dc=com\[dq]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: conn=1000 op=16 MOD attr=userPassword
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: entry uid=user,ou=persons,dc=my-domain,dc=com
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Reading pwdCheckModuleArg attribute
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: RAW configuration: # minQuality parameter#012# Format:#012# minQuality [NUMBER]#012# Description:#012# One point is granted for each class for which MIN_FOR_POINT criteria is fulfilled.#012# defines the minimum point numbers for the password to be accepted.#012minQuality 3#012#012# checkRDN parameter#012# Format:#012# checkRDN [0 | 1]#012# Description:#012# If set to 1, password must not contain a token from the RDN.#012# Tokens are separated by the following delimiters : space tabulation _ - , ; \[Po]#012checkRDN 0#012#012# forbiddenChars parameter#012# Format:#012# forbiddenChars [CHARACTERS_FORBIDDEN]#012# Description:#012# Defines the forbidden characters list (no separator).#012# If one of them is found in the password, then it is rejected.#012forbiddenChars#012#012# maxConsecutivePerClass parameter#012# Format:#012# maxConsecutivePerClass [NUMBER]#012# Description:#012# Defines the maximum number of consecutive character allowed for any class#012maxConsecutivePerClass 0#012#012# useCracklib parameter#012# Format:#012# useCracklib [0 | 1]#012# Description:#012# If set to 1, the password must pass the cracklib check#012useCracklib 0#012#012# cracklibDict parameter#012# Format:#012# cracklibDict [path_to_cracklib_dictionary]#012# Description:#012# directory+filename-prefix that your version of CrackLib will go hunting for#012# For example, /var/pw_dict resolves as /var/pw_dict.pwd,#012# /var/pw_dict.pwi and /var/pw_dict.hwm dictionary files#012cracklibDict /var/cache/cracklib/cracklib_dict#012#012# classes parameter#012# Format:#012# class-[CLASS_NAME] [CHARACTERS_DEFINING_CLASS] [MIN] [MIN_FOR_POINT]#012# Description:#012# [CHARACTERS_DEFINING_CLASS]: characters defining the class (no separator)#012# [MIN]: If at least [MIN] characters of this class is not found in the password, then it is rejected#012# [MIN_FOR_POINT]: one point is granted if password contains at least [MIN_FOR_POINT] character numbers of this class#012class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1#012class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1#012class-digit 0123456789 0 1#012class-special <>,?;.:/!\[sc]\[`u]%*\[mc]\[ha]\[ad]$\[Po]\[S2]&\['e]\[ti]\[dq]#\[aq]{([-|\[`e]\[ga]_\[rs]\[,c]\[ha]\[`a]\[at])]\[de]=}+ 0 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Parsing pwdCheckModuleArg attribute
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # minQuality parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # minQuality [NUMBER]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # One point is granted for each class for which MIN_FOR_POINT criteria is fulfilled.
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # defines the minimum point numbers for the password to be accepted.
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: minQuality 3
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = minQuality, value = 3, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: 3
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # checkRDN parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # checkRDN [0 | 1]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # If set to 1, password must not contain a token from the RDN.
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Tokens are separated by the following delimiters : space tabulation _ - , ; \[Po]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: checkRDN 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = checkRDN, value = 0, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # forbiddenChars parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # forbiddenChars [CHARACTERS_FORBIDDEN]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Defines the forbidden characters list (no separator).
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # If one of them is found in the password, then it is rejected.
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: forbiddenChars
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: No value, goto next parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # maxConsecutivePerClass parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # maxConsecutivePerClass [NUMBER]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Defines the maximum number of consecutive character allowed for any class
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: maxConsecutivePerClass 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = maxConsecutivePerClass, value = 0, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # useCracklib parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # useCracklib [0 | 1]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # If set to 1, the password must pass the cracklib check
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: useCracklib 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = useCracklib, value = 0, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: 0
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # cracklibDict parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # cracklibDict [path_to_cracklib_dictionary]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # directory+filename-prefix that your version of CrackLib will go hunting for
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # For example, /var/pw_dict resolves as /var/pw_dict.pwd,
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # /var/pw_dict.pwi and /var/pw_dict.hwm dictionary files
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: cracklibDict /var/cache/cracklib/cracklib_dict
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = cracklibDict, value = /var/cache/cracklib/cracklib_dict, min = (null), minForPoint= (null)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: /var/cache/cracklib/cracklib_dict
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # classes parameter
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Format:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # class-[CLASS_NAME] [CHARACTERS_DEFINING_CLASS] [MIN] [MIN_FOR_POINT]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # Description:
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # [CHARACTERS_DEFINING_CLASS]: characters defining the class (no separator)
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # [MIN]: If at least [MIN] characters of this class is not found in the password, then it is rejected
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: # [MIN_FOR_POINT]: one point is granted if password contains at least [MIN_FOR_POINT] character numbers of this class
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = class-upperCase, value = ABCDEFGHIJKLMNOPQRSTUVWXYZ, min = 0, minForPoint= 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: ABCDEFGHIJKLMNOPQRSTUVWXYZ
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = class-lowerCase, value = abcdefghijklmnopqrstuvwxyz, min = 0, minForPoint= 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: abcdefghijklmnopqrstuvwxyz
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: class-digit 0123456789 0 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = class-digit, value = 0123456789, min = 0, minForPoint= 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: 0123456789
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: get line: class-special <>,?;.:/!\[sc]\[`u]%*\[mc]\[ha]\[ad]$\[Po]\[S2]&\['e]\[ti]\[dq]#\[aq]{([-|\[`e]\[ga]_\[rs]\[,c]\[ha]\[`a]\[at])]\[de]=}+ 0 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Param = class-special, value = <>,?;.:/!\[sc]\[`u]%*\[mc]\[ha]\[ad]$\[Po]\[S2]&\['e]\[ti]\[dq]#\[aq]{([-|\[`e]\[ga]_\[rs]\[,c]\[ha]\[`a]\[at])]\[de]=}+, min = 0, minForPoint= 1
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Accepted replaced value: <>,?;.:/!\[sc]\[`u]%*\[mc]\[ha]\[ad]$\[Po]\[S2]&\['e]\[ti]\[dq]#\[aq]{([-|\[`e]\[ga]_\[rs]\[,c]\[ha]\[`a]\[at])]\[de]=}+
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: 1 point granted for class class-lowerCase
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: 1 point granted for class class-digit
+Feb 26 14:46:10 debian-11-64 slapd[1981]: ppm: Reallocating szErrStr from 64 to 173
+Feb 26 14:46:10 debian-11-64 slapd[1981]: check_password_quality: module error: (/usr/local/lib/ppm.so) Password for dn=\[dq]uid=user,ou=persons,dc=my-domain,dc=com\[dq] does not pass required number of strength checks (2 of 3).[1]
+Feb 26 14:46:10 debian-11-64 slapd[1981]: conn=1000 op=16 RESULT tag=103 err=19 qtime=0.000020 etime=0.001496 text=Password for dn=\[dq]uid=user,ou=persons,dc=my-domain,dc=com\[dq] does not pass required number of strength checks (2 of 3)
+\f[R]
+.fi
+.SH TESTS
+.PP
+There is a unit test script: \f[B]unit_tests.sh\f[R] that illustrates
+checking some passwords.
+.PP
+It is possible to test one particular password using directly the test
+program:
+.IP
+.nf
+\f[C]
+cd /usr/local/lib
+LD_LIBRARY_PATH=. ./ppm_test \[dq]uid=test,ou=users,dc=my-domain,dc=com\[dq] \[dq]my_password\[dq] \[dq]/usr/local/etc/openldap/ppm.example\[dq] && echo OK
+\f[R]
+.fi
+.SH FILES
+.PP
+\f[B]ETCDIR/ppm.example\f[R]
+.RS
+.PP
+example of ppm configuration to be inserted in
+\f[B]pwdCheckModuleArg\f[R] attribute of given password policy
+.RE
+.PP
+\f[B]ppm.so\f[R]
+.RS
+.PP
+ppm library, loaded by the \f[B]pwdCheckModule\f[R] attribute of given
+password policy
+.RE
+.PP
+\f[B]ppm_test\f[R]
+.RS
+.PP
+small test program for checking password in a command-line
+.RE
+.SH SEE ALSO
+.PP
+\f[B]slapo-ppolicy\f[R](5), \f[B]slapd-config\f[R](5),
+\f[B]slapd.conf\f[R](5)
+.SH ACKNOWLEDGEMENTS
+.PP
+This module was developed in 2014-2021 by David Coutadeur.
diff --git a/contrib/slapd-modules/ppm/unit_tests.sh b/contrib/slapd-modules/ppm/unit_tests.sh
new file mode 100755
index 0000000..c152c96
--- /dev/null
+++ b/contrib/slapd-modules/ppm/unit_tests.sh
@@ -0,0 +1,118 @@
+#!/bin/bash
+
+# Launch unitary tests
+#
+
+
+CONFIG_FILE="ppm.example"
+
+LDAP_SRC="${LDAP_SRC:-../../..}"
+LDAP_BUILD=${LDAP_BUILD:-${LDAP_SRC}}
+CURRENT_DIR=$( dirname $0 )
+LIB_PATH="${LD_LIBRARY_PATH}:${CURRENT_DIR}:${LDAP_BUILD}/libraries/liblber/.libs:${LDAP_BUILD}/libraries/libldap/.libs"
+
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+NC='\033[0m'
+
+RESULT=0
+
+PPM_CONF_1='minQuality 3
+checkRDN 0
+forbiddenChars
+maxConsecutivePerClass 0
+useCracklib 0
+cracklibDict /var/cache/cracklib/cracklib_dict
+class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1
+class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1
+class-digit 0123456789 0 1
+class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'\''{([-|è`_\ç^à@)]°=}+ 0 1'
+
+PPM_CONF_2='minQuality 3
+checkRDN 0
+forbiddenChars à
+maxConsecutivePerClass 5
+useCracklib 0
+cracklibDict /var/cache/cracklib/cracklib_dict
+class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 2 4
+class-lowerCase abcdefghijklmnopqrstuvwxyz 3 4
+class-digit 0123456789 2 4
+class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'\''{([-|è`_\ç^à@)]°=}+ 0 4'
+
+PPM_CONF_3='minQuality 3
+checkRDN 1
+forbiddenChars
+maxConsecutivePerClass 0
+useCracklib 0
+cracklibDict /var/cache/cracklib/cracklib_dict
+class-upperCase ABCDEFGHIJKLMNOPQRSTUVWXYZ 0 1
+class-lowerCase abcdefghijklmnopqrstuvwxyz 0 1
+class-digit 0123456789 0 1
+class-special <>,?;.:/!§ù%*µ^¨$£²&é~"#'\''{([-|è`_\ç^à@)]°=}+ 0 1'
+
+
+echo "$PPM_CONF_1" > ppm1.conf
+echo "$PPM_CONF_2" > ppm2.conf
+echo "$PPM_CONF_3" > ppm3.conf
+
+
+launch_test()
+{
+ # launch tests
+ # FORMAT: launch_test [conf_file] [password] [expected_result]
+ # [expected_result] = [PASS|FAIL]
+
+ local CONF="$1"
+ local USER="$2"
+ local PASS="$3"
+ local EXPECT="$4"
+
+ [[ $EXPECT == "PASS" ]] && EXP="0" || EXP="1"
+
+ LD_LIBRARY_PATH="${LIB_PATH}" ./ppm_test "${USER}" "${PASS}" "${CONF}"
+ RES="$?"
+
+ if [ "$RES" -eq "$EXP" ] ; then
+ echo -e "conf=${CONF} user=${USER} pass=${PASS} expect=${EXPECT}... ${GREEN}PASS${NC}"
+ else
+ echo -e "conf=${CONF} user=${USER} pass=${PASS} expect=${EXPECT}... ${RED}FAIL${NC}"
+ ((RESULT+=1))
+ fi
+
+ echo
+}
+
+
+
+
+launch_test "ppm1.conf" "uid=test,ou=users,dc=my-domain,dc=com" "azerty" "FAIL"
+launch_test "ppm1.conf" "uid=test,ou=users,dc=my-domain,dc=com" "azeRTY" "FAIL"
+launch_test "ppm1.conf" "uid=test,ou=users,dc=my-domain,dc=com" "azeRTY123" "PASS"
+launch_test "ppm1.conf" "uid=test,ou=users,dc=my-domain,dc=com" "azeRTY." "PASS"
+
+
+launch_test "ppm2.conf" "uid=test,ou=users,dc=my-domain,dc=com" "AAaaa01AAaaa01AAaaa0" "PASS"
+# forbidden char
+launch_test "ppm2.conf" "uid=test,ou=users,dc=my-domain,dc=com" "AAaaa01AAaaa01AAaaaà" "FAIL"
+# too much consecutive for upper
+launch_test "ppm2.conf" "uid=test,ou=users,dc=my-domain,dc=com" "AAaaa01AAaaa01AAAAAA" "FAIL"
+# not enough upper
+launch_test "ppm2.conf" "uid=test,ou=users,dc=my-domain,dc=com" "Aaaaa01aaaaa01aa.;.;" "FAIL"
+# not enough lower
+launch_test "ppm2.conf" "uid=test,ou=users,dc=my-domain,dc=com" "aaAAA01BB0123AAA.;.;" "FAIL"
+# not enough digit
+launch_test "ppm2.conf" "uid=test,ou=users,dc=my-domain,dc=com" "1AAAA.;BBB.;.;AA.;.;" "FAIL"
+# not enough points (no point for digit)
+launch_test "ppm2.conf" "uid=test,ou=users,dc=my-domain,dc=com" "AAaaaBBBBaaa01AAaaaa" "FAIL"
+
+# password in RDN
+launch_test "ppm3.conf" "uid=User_Password10-test,ou=users,dc=my-domain,dc=com" "Password10" "FAIL"
+launch_test "ppm3.conf" "uid=User_Passw0rd-test,ou=users,dc=my-domain,dc=com" "Password10" "PASS"
+launch_test "ppm3.conf" "uid=User-Pw-Test,ou=users,dc=my-domain,dc=com" "Password10" "PASS"
+
+
+echo "${RESULT} error(s) encountered"
+
+rm ppm1.conf ppm2.conf ppm3.conf
+exit ${RESULT}
+
diff --git a/contrib/slapd-modules/proxyOld/Makefile b/contrib/slapd-modules/proxyOld/Makefile
new file mode 100644
index 0000000..d92967c
--- /dev/null
+++ b/contrib/slapd-modules/proxyOld/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 2005-2022 The OpenLDAP Foundation.
+# Portions Copyright 2005 Howard Chu, Symas Corp. All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted only as authorized by the OpenLDAP
+# Public License.
+#
+# A copy of this license is available in the file LICENSE in the
+# top-level directory of the distribution or, alternatively, at
+# <http://www.OpenLDAP.org/license.html>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = proxyOld.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+proxyOld.la: proxyOld.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/proxyOld/README b/contrib/slapd-modules/proxyOld/README
new file mode 100644
index 0000000..bc5e4ab
--- /dev/null
+++ b/contrib/slapd-modules/proxyOld/README
@@ -0,0 +1,31 @@
+This directory contains a slapd module proxyOld that provides support
+for the obsolete draft-weltman-ldapb3-proxy-05 revision of the LDAP
+Proxy Authorization control. It is merely intended to provide compatibility
+in environments where other servers only recognize this old control.
+New installations should not use this code.
+
+To use the module, add:
+
+ moduleload <path to>proxyOld.so
+ ...
+
+to your slapd configuration file. Since this is an obsolete feature,
+the control is registered with the SLAP_CTRL_HIDE flag so that it will
+not be advertised in the rootDSE's supportedControls attribute.
+
+This code only works as a dynamically loaded module.
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 1998-2022 The OpenLDAP Foundation.
+Portions Copyright 2005 Howard Chu, Symas Corp. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
diff --git a/contrib/slapd-modules/proxyOld/proxyOld.c b/contrib/slapd-modules/proxyOld/proxyOld.c
new file mode 100644
index 0000000..2da6888
--- /dev/null
+++ b/contrib/slapd-modules/proxyOld/proxyOld.c
@@ -0,0 +1,128 @@
+/* proxyOld.c - module for supporting obsolete (rev 05) proxyAuthz control */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2005 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include <portable.h>
+
+#include <slap.h>
+
+#include <lber.h>
+/*
+#include <lber_pvt.h>
+#include <lutil.h>
+*/
+
+/* This code is based on draft-weltman-ldapv3-proxy-05. There are a lot
+ * of holes in that draft, it doesn't specify that the control is legal
+ * for Add operations, and it makes no mention of Extended operations.
+ * It also doesn't specify whether an empty LDAPDN is allowed in the
+ * control value.
+ *
+ * For usability purposes, we're copying the op / exop behavior from the
+ * newer -12 draft.
+ */
+#define LDAP_CONTROL_PROXY_AUTHZ05 "2.16.840.1.113730.3.4.12"
+
+static char *proxyOld_extops[] = {
+ LDAP_EXOP_MODIFY_PASSWD,
+ LDAP_EXOP_X_WHO_AM_I,
+ NULL
+};
+
+static int
+proxyOld_parse(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ int rc;
+ BerElement *ber;
+ ber_tag_t tag;
+ struct berval dn = BER_BVNULL;
+ struct berval authzDN = BER_BVNULL;
+
+
+ /* We hijack the flag for the new control. Clearly only one or the
+ * other can be used at any given time.
+ */
+ if ( op->o_proxy_authz != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "proxy authorization control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ op->o_proxy_authz = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ /* Parse the control value
+ * proxyAuthzControlValue ::= SEQUENCE {
+ * proxyDN LDAPDN
+ * }
+ */
+ ber = ber_init( &ctrl->ldctl_value );
+ if ( ber == NULL ) {
+ rs->sr_text = "ber_init failed";
+ return LDAP_OTHER;
+ }
+
+ tag = ber_scanf( ber, "{m}", &dn );
+
+ if ( tag == LBER_ERROR ) {
+ rs->sr_text = "proxyOld control could not be decoded";
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ if ( BER_BVISEMPTY( &dn )) {
+ Debug( LDAP_DEBUG_TRACE,
+ "proxyOld_parse: conn=%lu anonymous\n",
+ op->o_connid );
+ authzDN.bv_val = ch_strdup("");
+ } else {
+ Debug( LDAP_DEBUG_ARGS,
+ "proxyOld_parse: conn %lu ctrl DN=\"%s\"\n",
+ op->o_connid, dn.bv_val );
+ rc = dnNormalize( 0, NULL, NULL, &dn, &authzDN, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ rc = slap_sasl_authorized( op, &op->o_ndn, &authzDN );
+ if ( rc ) {
+ op->o_tmpfree( authzDN.bv_val, op->o_tmpmemctx );
+ rs->sr_text = "not authorized to assume identity";
+ /* new spec uses LDAP_PROXY_AUTHZ_FAILURE */
+ rc = LDAP_INSUFFICIENT_ACCESS;
+ goto done;
+ }
+ }
+ free( op->o_ndn.bv_val );
+ free( op->o_dn.bv_val );
+ op->o_ndn = authzDN;
+ ber_dupbv( &op->o_dn, &authzDN );
+
+ Debug( LDAP_DEBUG_STATS, "conn=%lu op=%lu PROXYOLD dn=\"%s\"\n",
+ op->o_connid, op->o_opid,
+ authzDN.bv_len ? authzDN.bv_val : "anonymous" );
+ rc = LDAP_SUCCESS;
+done:
+ ber_free( ber, 1 );
+ return rc;
+}
+
+int init_module(int argc, char *argv[]) {
+ return register_supported_control( LDAP_CONTROL_PROXY_AUTHZ05,
+ SLAP_CTRL_GLOBAL|SLAP_CTRL_HIDE|SLAP_CTRL_ACCESS, proxyOld_extops,
+ proxyOld_parse, NULL );
+}
diff --git a/contrib/slapd-modules/rbac/Makefile b/contrib/slapd-modules/rbac/Makefile
new file mode 100755
index 0000000..1180bd6
--- /dev/null
+++ b/contrib/slapd-modules/rbac/Makefile
@@ -0,0 +1,63 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_RBAC=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = rbac.la
+MANPAGES = slapo-rbac.5
+
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+SRCS = rbac.c rbacperm.c rbacsess.c rbacuser.c rbacreq.c rbacaudit.c init.c rbacacl.c util.c jts.c
+OBJS = $(patsubst %.c,%.o,$(SRCS))
+LOBJS = $(patsubst %.c,%.lo,$(SRCS))
+
+.SUFFIXES: .c .lo
+
+%.lo: %.c rbac.h
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+rbac.la: $(LOBJS)
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $^ $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/rbac/init.c b/contrib/slapd-modules/rbac/init.c
new file mode 100644
index 0000000..1925ae5
--- /dev/null
+++ b/contrib/slapd-modules/rbac/init.c
@@ -0,0 +1,324 @@
+/* init.c - RBAC initialization */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+static slap_callback nullsc = { NULL, NULL, NULL, NULL };
+
+struct slap_rbac_internal_schema slap_rbac_schema;
+
+extern rbac_tenant_t rbac_tenants;
+extern int initialize_jts( void );
+
+rbac_ad_t rbac_session_ads[] = {
+ { RBAC_SESSION_ID,
+ BER_BVC("rbacSessid"), &slap_rbac_schema.ad_session_id },
+ { RBAC_USER_DN,
+ BER_BVC("rbacUserDN"), &slap_rbac_schema.ad_session_user_dn },
+ { RBAC_ROLES,
+ BER_BVC("rbacRoles"), &slap_rbac_schema.ad_session_roles },
+ { RBAC_ROLE_CONSTRAINTS,
+ BER_BVC("rbacRoleConstraints"),
+ &slap_rbac_schema.ad_session_role_constraints },
+ { RBAC_UID,
+ BER_BVC("uid"), &slap_rbac_schema.ad_uid},
+ { RBAC_TENANT_ID,
+ BER_BVC("tenantid"), &slap_rbac_schema.ad_tenant_id },
+
+ { RBAC_NONE, BER_BVNULL, NULL }
+};
+
+rbac_ad_t rbac_session_permission_ads[] = {
+ { RBAC_OP_NAME,
+ BER_BVC("rbacOpName"), &slap_rbac_schema.ad_permission_opname },
+ { RBAC_OBJ_NAME,
+ BER_BVC("rbacObjName"), &slap_rbac_schema.ad_permission_objname },
+ { RBAC_ROLE_NAME,
+ BER_BVC("rbacRoleName"), &slap_rbac_schema.ad_permission_rolename },
+
+ { RBAC_NONE, BER_BVNULL, NULL }
+};
+
+rbac_ad_t audit_ads[] = {
+ { RBAC_AUDIT_OP,
+ BER_BVC("rbacAuditOp"), &slap_rbac_schema.ad_audit_op },
+ { RBAC_AUDIT_ID,
+ BER_BVC("rbacAuditId"), &slap_rbac_schema.ad_audit_id },
+ { RBAC_AUDIT_ROLES,
+ BER_BVC("rbacAuditRoles"), &slap_rbac_schema.ad_audit_roles },
+ { RBAC_AUDIT_REQUESTED_ROLES,
+ BER_BVC("rbacAuditRequestedRoles"),
+ &slap_rbac_schema.ad_audit_requested_roles
+ },
+ { RBAC_AUDIT_TIMESTAMP,
+ BER_BVC("rbacAuditTimestamp"), &slap_rbac_schema.ad_audit_timestamp },
+ { RBAC_AUDIT_RESOURCES,
+ BER_BVC("rbacAuditResources"), &slap_rbac_schema.ad_audit_resources },
+ { RBAC_AUDIT_OBJS,
+ BER_BVC("rbacAuditObjects"), &slap_rbac_schema.ad_audit_objects },
+ { RBAC_AUDIT_OPS,
+ BER_BVC("rbacAuditOperations"), &slap_rbac_schema.ad_audit_operations },
+ { RBAC_AUDIT_RESULT,
+ BER_BVC("rbacAuditResult"), &slap_rbac_schema.ad_audit_result },
+ { RBAC_AUDIT_PROPERTIES,
+ BER_BVC("rbacAuditProperties"), &slap_rbac_schema.ad_audit_properties },
+ { RBAC_AUDIT_MSGS,
+ BER_BVC("rbacAuditMessages"), &slap_rbac_schema.ad_audit_messages },
+
+ { RBAC_NONE, BER_BVNULL, NULL }
+};
+
+/* initialize repository attribute descriptions */
+
+static int
+initialize_sessions()
+{
+ int i, nattrs, rc = LDAP_SUCCESS;
+ const char *text;
+
+ for ( nattrs = 0; !BER_BVISNULL( &rbac_session_ads[nattrs].attr );
+ nattrs++ )
+ ; /* count the number of attrs */
+
+ slap_rbac_schema.session_attrs =
+ slap_sl_calloc( sizeof(AttributeName), nattrs + 1, NULL );
+
+ for ( i = 0; !BER_BVISNULL( &rbac_session_ads[i].attr ); i++ ) {
+ rc = slap_bv2ad(
+ &rbac_session_ads[i].attr, rbac_session_ads[i].ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ slap_rbac_schema.session_attrs[i].an_name = rbac_session_ads[i].attr;
+ slap_rbac_schema.session_attrs[i].an_desc = *rbac_session_ads[i].ad;
+ }
+
+ BER_BVZERO( &slap_rbac_schema.session_attrs[nattrs].an_name );
+
+done:;
+ return rc;
+}
+
+static int
+initialize_audit()
+{
+ int i, rc = LDAP_SUCCESS;
+ const char *text;
+
+ /* for audit */
+ for ( i = 0; !BER_BVISNULL( &audit_ads[i].attr ); i++ ) {
+ rc = slap_bv2ad( &audit_ads[i].attr, audit_ads[i].ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ }
+
+done:;
+ return rc;
+}
+
+static int
+initialize_tenant(
+ BackendDB *be,
+ ConfigReply *cr,
+ tenant_info_t *tenantp,
+ int init_op )
+{
+ int rc = LDAP_SUCCESS;
+ Entry *e = NULL;
+ OperationBuffer opbuf;
+ Operation *op2;
+ SlapReply rs2 = { REP_RESULT };
+ Connection conn = { 0 };
+ struct berval rbac_container_oc = BER_BVC("rbacContainer");
+ struct berval rbac_audit_container = BER_BVC("audit");
+ struct berval rbac_session_container = BER_BVC("rbac");
+ void *thrctx = ldap_pvt_thread_pool_context();
+
+ e = entry_alloc();
+
+ switch ( init_op ) {
+ case INIT_AUDIT_CONTAINER:
+ ber_dupbv( &e->e_name, &tenantp->audit_basedn );
+ ber_dupbv( &e->e_nname, &tenantp->audit_basedn );
+
+ /* container cn */
+ attr_merge_one(
+ e, slap_schema.si_ad_cn, &rbac_audit_container, NULL );
+ break;
+ case INIT_SESSION_CONTAINER:
+ ber_dupbv( &e->e_name, &tenantp->sessions_basedn );
+ ber_dupbv( &e->e_nname, &tenantp->sessions_basedn );
+
+ /* rendered dynmaicObject for session */
+ attr_merge_one( e, slap_schema.si_ad_objectClass,
+ &slap_schema.si_oc_dynamicObject->soc_cname, NULL );
+
+ /* container cn */
+ attr_merge_one(
+ e, slap_schema.si_ad_cn, &rbac_session_container, NULL );
+ break;
+ default:
+ break;
+ }
+
+ attr_merge_one(
+ e, slap_schema.si_ad_objectClass, &rbac_container_oc, NULL );
+ attr_merge_one( e, slap_schema.si_ad_structuralObjectClass,
+ &rbac_container_oc, NULL );
+
+ /* store RBAC session */
+ connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+ op2 = &opbuf.ob_op;
+ op2->o_callback = &nullsc;
+ op2->o_tag = LDAP_REQ_ADD;
+ op2->o_protocol = LDAP_VERSION3;
+ op2->o_req_dn = e->e_name;
+ op2->o_req_ndn = e->e_nname;
+ op2->ora_e = e;
+ op2->o_bd = select_backend( &op2->o_req_ndn, 0 );
+ op2->o_dn = op2->o_bd->be_rootdn;
+ op2->o_ndn = op2->o_bd->be_rootndn;
+ rc = op2->o_bd->be_add( op2, &rs2 );
+
+ if ( e ) entry_free( e );
+
+ return rc;
+}
+
+int
+rbac_initialize_tenants( BackendDB *be, ConfigReply *cr )
+{
+ int rc = LDAP_SUCCESS;
+ rbac_tenant_t *tenantp = NULL;
+
+ for ( tenantp = &rbac_tenants; tenantp; tenantp = tenantp->next ) {
+ rc = initialize_tenant(
+ be, cr, &tenantp->tenant_info, INIT_AUDIT_CONTAINER );
+ if ( rc != LDAP_SUCCESS ) {
+ if ( rc == LDAP_ALREADY_EXISTS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "audit container exists, tenant (%s)\n",
+ tenantp->tenant_info.tid.bv_val ?
+ tenantp->tenant_info.tid.bv_val :
+ "NULL" );
+ rc = LDAP_SUCCESS;
+ } else {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "failed to initialize (%s): rc (%d)\n",
+ tenantp->tenant_info.tid.bv_val ?
+ tenantp->tenant_info.tid.bv_val :
+ "NULL",
+ rc );
+ goto done;
+ }
+ } else {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "created audit container for tenant (%s):\n",
+ tenantp->tenant_info.tid.bv_val ?
+ tenantp->tenant_info.tid.bv_val :
+ "NULL" );
+ }
+ rc = initialize_tenant(
+ be, cr, &tenantp->tenant_info, INIT_SESSION_CONTAINER );
+ if ( rc != LDAP_SUCCESS ) {
+ if ( rc == LDAP_ALREADY_EXISTS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "session container exists, tenant (%s)\n",
+ tenantp->tenant_info.tid.bv_val ?
+ tenantp->tenant_info.tid.bv_val :
+ "NULL" );
+ rc = LDAP_SUCCESS;
+ } else {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "failed to initialize (%s): rc (%d)\n",
+ tenantp->tenant_info.tid.bv_val ?
+ tenantp->tenant_info.tid.bv_val :
+ "NULL",
+ rc );
+ goto done;
+ }
+ } else {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "created session container for tenant (%s):\n",
+ tenantp->tenant_info.tid.bv_val ?
+ tenantp->tenant_info.tid.bv_val :
+ "NULL" );
+ }
+ }
+
+done:;
+
+ return rc;
+}
+
+static int
+initialize_rbac_session_permissions()
+{
+ int i, rc = LDAP_SUCCESS;
+ const char *text;
+
+ for ( i = 0; !BER_BVISNULL( &rbac_session_permission_ads[i].attr ); i++ ) {
+ rc = slap_bv2ad( &rbac_session_permission_ads[i].attr,
+ rbac_session_permission_ads[i].ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ }
+
+done:;
+ return rc;
+}
+
+int
+rbac_initialize_repository()
+{
+ int rc = LDAP_SUCCESS;
+
+ rc = initialize_jts();
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ rc = initialize_rbac_session_permissions();
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ rc = initialize_sessions();
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ rc = initialize_audit();
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ return rc;
+}
diff --git a/contrib/slapd-modules/rbac/jts.c b/contrib/slapd-modules/rbac/jts.c
new file mode 100644
index 0000000..c7c072b
--- /dev/null
+++ b/contrib/slapd-modules/rbac/jts.c
@@ -0,0 +1,198 @@
+/* jts.c - RBAC JTS initialization */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+struct slap_rbac_tenant_schema slap_rbac_jts_schema;
+
+/* to replace all JTS schema initialization */
+rbac_ad_t ft_ads[] = {
+ { RBAC_ROLE_ASSIGNMENT,
+ BER_BVC("ftRA"), &slap_rbac_jts_schema.ad_role },
+ { RBAC_ROLE_CONSTRAINTS,
+ BER_BVC("ftRC"), &slap_rbac_jts_schema.ad_role_constraint },
+ { RBAC_USER_CONSTRAINTS,
+ BER_BVC("ftCstr"), &slap_rbac_jts_schema.ad_user_constraint },
+ { RBAC_UID,
+ BER_BVC("uid"), &slap_rbac_jts_schema.ad_uid },
+ { RBAC_USERS,
+ BER_BVC("ftUsers"), &slap_rbac_jts_schema.ad_permission_users },
+ { RBAC_ROLES,
+ BER_BVC("ftRoles"), &slap_rbac_jts_schema.ad_permission_roles },
+ { RBAC_OBJ_NAME,
+ BER_BVC("ftObjNm"), &slap_rbac_jts_schema.ad_permission_objname },
+ { RBAC_OP_NAME,
+ BER_BVC("ftOpNm"), &slap_rbac_jts_schema.ad_permission_opname },
+
+ { RBAC_NONE, BER_BVNULL, NULL }
+};
+
+rbac_ad_t ft_user_ads[] = {
+ { RBAC_ROLE_ASSIGNMENT,
+ BER_BVC("ftRA"), &slap_rbac_jts_schema.ad_role },
+ { RBAC_ROLE_CONSTRAINTS,
+ BER_BVC("ftRC"), &slap_rbac_jts_schema.ad_role_constraint },
+ { RBAC_USER_CONSTRAINTS,
+ BER_BVC("ftCstr"), &slap_rbac_jts_schema.ad_user_constraint },
+ { RBAC_UID,
+ BER_BVC("uid"), &slap_rbac_jts_schema.ad_uid },
+
+ { RBAC_NONE, BER_BVNULL, NULL }
+};
+
+rbac_ad_t ft_perm_ads[] = {
+ { RBAC_USERS,
+ BER_BVC("ftUsers"), &slap_rbac_jts_schema.ad_permission_users },
+ { RBAC_ROLES,
+ BER_BVC("ftRoles"), &slap_rbac_jts_schema.ad_permission_roles },
+
+ { RBAC_NONE, BER_BVNULL, NULL }
+};
+
+rbac_ad_t ft_session_perm_ads[] = {
+ { RBAC_USERS,
+ BER_BVC("ftUsers"), &slap_rbac_jts_schema.ad_permission_users },
+ { RBAC_ROLES,
+ BER_BVC("ftRoles"), &slap_rbac_jts_schema.ad_permission_roles },
+ { RBAC_OBJ_NAME,
+ BER_BVC("ftObjNm"), &slap_rbac_jts_schema.ad_permission_objname },
+ { RBAC_OP_NAME,
+ BER_BVC("ftOpNm"), &slap_rbac_jts_schema.ad_permission_opname },
+
+ { RBAC_NONE, BER_BVNULL, NULL }
+};
+
+static int
+initialize_jts_session_permission_ads()
+{
+ int i, nattrs, rc = LDAP_SUCCESS;
+
+ for ( nattrs = 0; !BER_BVISNULL( &ft_session_perm_ads[nattrs].attr );
+ nattrs++ )
+ ; /* count the number of attrs */
+
+ slap_rbac_jts_schema.session_perm_attrs =
+ slap_sl_calloc( sizeof(AttributeName), nattrs + 1, NULL );
+
+ for ( i = 0; !BER_BVISNULL( &ft_session_perm_ads[i].attr ); i++ ) {
+ slap_rbac_jts_schema.session_perm_attrs[i].an_name =
+ ft_session_perm_ads[i].attr;
+ slap_rbac_jts_schema.session_perm_attrs[i].an_desc =
+ *ft_session_perm_ads[i].ad;
+ }
+
+ BER_BVZERO( &slap_rbac_jts_schema.session_perm_attrs[nattrs].an_name );
+
+ slap_rbac_jts_schema.session_permissions_ads = ft_session_perm_ads;
+
+ return rc;
+}
+
+static int
+initialize_jts_permission_ads()
+{
+ int i, nattrs, rc = LDAP_SUCCESS;
+
+ /* jts permissions configuration */
+
+ for ( nattrs = 0; !BER_BVISNULL( &ft_perm_ads[nattrs].attr ); nattrs++ )
+ ; /* count the number of attrs */
+
+ slap_rbac_jts_schema.perm_attrs =
+ slap_sl_calloc( sizeof(AttributeName), nattrs + 1, NULL );
+
+ for ( i = 0; !BER_BVISNULL( &ft_perm_ads[i].attr ); i++ ) {
+ slap_rbac_jts_schema.perm_attrs[i].an_name = ft_perm_ads[i].attr;
+ slap_rbac_jts_schema.perm_attrs[i].an_desc = *ft_perm_ads[i].ad;
+ }
+
+ BER_BVZERO( &slap_rbac_jts_schema.perm_attrs[nattrs].an_name );
+
+ slap_rbac_jts_schema.permission_ads = ft_perm_ads;
+
+ return rc;
+}
+
+static int
+initialize_jts_user_ads()
+{
+ int i, nattrs, rc = LDAP_SUCCESS;
+
+ /* jts user attribute descriptions */
+
+ /* jts user attributes */
+ for ( nattrs = 0; !BER_BVISNULL( &ft_user_ads[nattrs].attr ); nattrs++ )
+ ; /* count the number of attrs */
+
+ slap_rbac_jts_schema.user_attrs =
+ slap_sl_calloc( sizeof(AttributeName), nattrs + 1, NULL );
+
+ for ( i = 0; !BER_BVISNULL( &ft_user_ads[i].attr ); i++ ) {
+ slap_rbac_jts_schema.user_attrs[i].an_name = ft_user_ads[i].attr;
+ slap_rbac_jts_schema.user_attrs[i].an_desc = *ft_user_ads[i].ad;
+ }
+
+ BER_BVZERO( &slap_rbac_jts_schema.user_attrs[nattrs].an_name );
+
+ slap_rbac_jts_schema.user_ads = ft_user_ads;
+
+ return rc;
+}
+
+int
+initialize_jts()
+{
+ int i, rc;
+ const char *text;
+
+ /* jts attributes */
+ for ( i = 0; !BER_BVISNULL( &ft_ads[i].attr ); i++ ) {
+ rc = slap_bv2ad( &ft_ads[i].attr, ft_ads[i].ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ }
+
+ rc = initialize_jts_user_ads();
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ rc = initialize_jts_permission_ads();
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ rc = initialize_jts_session_permission_ads();
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+done:;
+ return rc;
+}
diff --git a/contrib/slapd-modules/rbac/ldap_rbac.h b/contrib/slapd-modules/rbac/ldap_rbac.h
new file mode 100644
index 0000000..d57fe6e
--- /dev/null
+++ b/contrib/slapd-modules/rbac/ldap_rbac.h
@@ -0,0 +1,55 @@
+#ifndef LDAP_RBAC_H
+#define LDAP_RBAC_H
+
+/* extended operations for RBAC */
+#define LDAP_RBAC_EXOP_CREATE_SESSION "1.3.6.1.4.1.4203.555.1" /* RFC xxxx */
+#define LDAP_RBAC_EXOP_CHECK_ACCESS "1.3.6.1.4.1.4203.555.2"
+#define LDAP_RBAC_EXOP_ADD_ACTIVE_ROLE "1.3.6.1.4.1.4203.555.3"
+#define LDAP_RBAC_EXOP_DROP_ACTIVE_ROLE "1.3.6.1.4.1.4203.555.4"
+#define LDAP_RBAC_EXOP_DELETE_SESSION "1.3.6.1.4.1.4203.555.5"
+#define LDAP_RBAC_EXOP_SESSION_ROLES "1.3.6.1.4.1.4203.555.6"
+#define LDAP_RBAC_EXOP_SESSION_PERMISSIONS "1.3.6.1.4.1.4203.555.7"
+
+#define LDAP_TAG_EXOP_RBAC_SESSION_ID ((ber_tag_t)0x80U)
+#define LDAP_TAG_EXOP_RBAC_TENANT_ID ((ber_tag_t)0x81U)
+#define LDAP_TAG_EXOP_RBAC_USER_ID ((ber_tag_t)0x82U)
+#define LDAP_TAG_EXOP_RBAC_USER ((ber_tag_t)0x80U)
+#define LDAP_TAG_EXOP_RBAC_AUTHTOK ((ber_tag_t)0x83U)
+#define LDAP_TAG_EXOP_RBAC_ACTIVE_ROLE ((ber_tag_t)0xA4U)
+#define LDAP_TAG_EXOP_RBAC_OPNAME ((ber_tag_t)0x81U)
+#define LDAP_TAG_EXOP_RBAC_OBJNAME ((ber_tag_t)0x82U)
+#define LDAP_TAG_EXOP_RBAC_OBJID ((ber_tag_t)0x83U)
+#define LDAP_TAG_EXOP_RBAC_PWPOLICY_STATE ((ber_tag_t)0x85U)
+#define LDAP_TAG_EXOP_RBAC_PWPOLICY_VALUE ((ber_tag_t)0x86U)
+#define LDAP_TAG_EXOP_RBAC_ROLES ((ber_tag_t)0x04U)
+
+#define LDAP_TAG_EXOP_RBAC_USER_ID_SESS ((ber_tag_t)0x80U)
+#define LDAP_TAG_EXOP_RBAC_SESSION_ID_SESS ((ber_tag_t)0x81U)
+#define LDAP_TAG_EXOP_RBAC_ROLE_NM_SESS ((ber_tag_t)0x82U)
+
+#define RBAC_REQ_CREATE_SESSION 0
+#define RBAC_REQ_CHECK_ACCESS 1
+#define RBAC_REQ_ADD_ACTIVE_ROLE 2
+#define RBAC_REQ_DROP_ACTIVE_ROLE 3
+#define RBAC_REQ_DELETE_SESSION 4
+#define RBAC_REQ_SESSION_PERMISSIONS 5
+#define RBAC_REQ_SESSION_ROLES 6
+
+/* defines for password policy */
+#define RBAC_BIND_NEW_AUTHTOK_REQD 1
+
+#define RBAC_PASSWORD_GOOD 0
+#define RBAC_PASSWORD_EXPIRATION_WARNING 11
+#define RBAC_PASSWORD_GRACE_WARNING 12
+#define RBAC_PASSWORD_HAS_EXPIRED 100
+#define RBAC_ACCOUNT_LOCKED 101
+#define RBAC_CHANGE_AFTER_RESET 102
+#define RBAC_NO_MODIFICATIONS 103
+#define RBAC_MUST_SUPPLY_OLD 104
+#define RBAC_INSUFFICIENT_QUALITY 105
+#define RBAC_PASSWORD_TOO_SHORT 106
+#define RBAC_PASSWORD_TOO_YOUNG 107
+#define RBAC_HISTORY_VIOLATION 108
+#define RBAC_ACCOUNT_LOCKED_CONSTRAINTS 109
+
+#endif /* LDAP_RBAC_H */
diff --git a/contrib/slapd-modules/rbac/rbac.c b/contrib/slapd-modules/rbac/rbac.c
new file mode 100644
index 0000000..4d2cff5
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbac.c
@@ -0,0 +1,2169 @@
+/* rbac.c - RBAC main file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2013-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+#define RBAC_REQ 1
+
+static slap_overinst rbac;
+
+static struct berval slap_EXOP_CREATE_SESSION =
+ BER_BVC(LDAP_RBAC_EXOP_CREATE_SESSION);
+static struct berval slap_EXOP_CHECK_ACCESS =
+ BER_BVC(LDAP_RBAC_EXOP_CHECK_ACCESS);
+static struct berval slap_EXOP_ADD_ACTIVE_ROLE =
+ BER_BVC(LDAP_RBAC_EXOP_ADD_ACTIVE_ROLE);
+static struct berval slap_EXOP_DROP_ACTIVE_ROLE =
+ BER_BVC(LDAP_RBAC_EXOP_DROP_ACTIVE_ROLE);
+static struct berval slap_EXOP_DELETE_SESSION =
+ BER_BVC(LDAP_RBAC_EXOP_DELETE_SESSION);
+static struct berval slap_EXOP_SESSION_ROLES =
+ BER_BVC(LDAP_RBAC_EXOP_SESSION_ROLES);
+
+rbac_tenant_t rbac_tenants = {
+ {
+ .schema = &slap_rbac_jts_schema,
+ },
+ NULL
+};
+
+static ConfigDriver rbac_cf_gen;
+
+static ConfigTable rbaccfg[] = {
+ { "rbac-default-users-base-dn", "usersDN", 2, 2, 0,
+ ARG_MAGIC|ARG_DN|RBAC_DEFAULT_USERS_BASE_DN,
+ rbac_cf_gen,
+ "(OLcfgCtAt:7.1 NAME 'olcRBACDefaultUsersBaseDn' "
+ "DESC 'default Base DN for RBAC users ' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "rbac-default-roles-base-dn", "rolesDN", 2, 2, 0,
+ ARG_MAGIC|ARG_DN|RBAC_DEFAULT_ROLES_BASE_DN,
+ rbac_cf_gen,
+ "(OLcfgCtAt:7.2 NAME 'olcRBACDefaultRolesBaseDn' "
+ "DESC 'default base DN for RBAC roles ' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "rbac-default-permissions-base-dn", "permissionsDN", 2, 2, 0,
+ ARG_MAGIC|ARG_DN|RBAC_DEFAULT_PERMISSIONS_BASE_DN,
+ rbac_cf_gen,
+ "(OLcfgCtAt:7.3 NAME 'olcRBACDefaultPermissionsBaseDn' "
+ "DESC 'default base DN for RBAC permissions ' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "rbac-default-sessions-base-dn", "sessionsDN", 2, 2, 0,
+ ARG_MAGIC|ARG_DN|RBAC_DEFAULT_SESSIONS_BASE_DN,
+ rbac_cf_gen,
+ "(OLcfgCtAt:7.4 NAME 'olcRBACDefaultSessionsBaseDn' "
+ "DESC 'default base DN for RBAC permissions ' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "rbac-admin", "adminDN", 2, 2, 0,
+ ARG_MAGIC|ARG_DN|RBAC_ADMIN_DN,
+ rbac_cf_gen,
+ "(OLcfgCtAt:7.5 NAME 'olcRBACAdminDn' "
+ "DESC 'default admin DN for RBAC repository ' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "rbac-pwd", "adminPwd", 2, 2, 0,
+ ARG_MAGIC|RBAC_ADMIN_PWD,
+ rbac_cf_gen,
+ "(OLcfgCtAt:7.6 NAME 'olcRBACAdminPwd' "
+ "DESC 'default admin pwd for RBAC repository ' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "rbac-session-admin", "sessionAdminDN", 2, 2, 0,
+ ARG_MAGIC|ARG_DN|RBAC_SESSION_ADMIN_DN,
+ rbac_cf_gen,
+ "(OLcfgCtAt:7.7 NAME 'olcRBACSessionAdminDn' "
+ "DESC 'admin DN for RBAC session repository ' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "rbac-session-admin-pwd", "sessionAdminPwd", 2, 2, 0,
+ ARG_MAGIC|RBAC_SESSION_ADMIN_PWD,
+ rbac_cf_gen,
+ "(OLcfgCtAt:7.8 NAME 'olcRBACSessionAdminPwd' "
+ "DESC 'admin pwd for RBAC session repository ' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "tenant", "tenant", 2, 2, 0,
+ ARG_MAGIC|ARG_DN|RBAC_TENANT,
+ rbac_cf_gen, "(OLcfgCtAt:7.9 NAME 'olcRBACTenant' "
+ "DESC 'RBAC tenant ' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "rbac-default-audit-base-dn", "auditDN", 2, 2, 0,
+ ARG_MAGIC|ARG_DN|RBAC_DEFAULT_AUDIT_BASE_DN,
+ rbac_cf_gen,
+ "(OLcfgCtAt:7.10 NAME 'olcRBACDefaultAuditBaseDn' "
+ "DESC 'default base DN for RBAC audit records ' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "rbac-default-tenant-id", "tenantId", 2, 2, 0,
+ ARG_MAGIC|RBAC_DEFAULT_TENANT_ID,
+ rbac_cf_gen,
+ "(OLcfgCtAt:7.11 NAME 'olcRBACDefaultTenantId' "
+ "DESC 'default tenant id' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs rbac_ocs[] = {
+ { "( OLcfgCtOc:7.1 "
+ "NAME 'olcRBACConfig' "
+ "DESC 'RBAC configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcRBACDefaultUsersBaseDn $ olcRBACDefaultRolesBaseDn $ "
+ "olcRBACDefaultPermissionsBaseDn $ olcRBACDefaultSessionsBaseDn $ "
+ "olcRBACAdminDn $ olcRBACAdminPwd $ olcRBACSessionAdminDn $ "
+ "olcRBACSessionAdminPwd) )",
+ Cft_Overlay, rbaccfg },
+
+ { NULL, 0, NULL }
+};
+
+static slap_verbmasks rbac_keys[] = {
+ { BER_BVC("default_users_base_dn"), RBAC_DEFAULT_USERS_BASE_DN },
+ { BER_BVC("default_roles_base_dn"), RBAC_DEFAULT_ROLES_BASE_DN },
+ { BER_BVC("default_permissions_base_dn"),
+ RBAC_DEFAULT_PERMISSIONS_BASE_DN },
+ { BER_BVC("tenant"), RBAC_TENANT },
+
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks rbac_tenant_keys[] = {
+ { BER_BVC("id"), RBAC_TENANT_ID },
+ { BER_BVC("users_base_dn"), RBAC_USERS_BASE_DN },
+ { BER_BVC("roles_base_dn"), RBAC_ROLES_BASE_DN },
+ { BER_BVC("permissions_base_dn"), RBAC_PERMISSIONS_BASE_DN },
+
+ { BER_BVNULL, 0 }
+};
+
+static void
+rbac_tenant_parse( char *tenent_info, tenant_info_t *tenants )
+{
+ return;
+}
+
+static int
+rbac_cf_gen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ rbac_tenant_t *ri = &rbac_tenants;
+ int rc = 0;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ switch ( c->type ) {
+ case RBAC_DEFAULT_USERS_BASE_DN:
+ value_add_one( &c->rvalue_vals, &ri->tenant_info.users_basedn );
+ break;
+ case RBAC_DEFAULT_ROLES_BASE_DN:
+ value_add_one( &c->rvalue_vals, &ri->tenant_info.roles_basedn );
+ break;
+ case RBAC_DEFAULT_PERMISSIONS_BASE_DN:
+ value_add_one(
+ &c->rvalue_vals, &ri->tenant_info.permissions_basedn );
+ break;
+ case RBAC_DEFAULT_SESSIONS_BASE_DN:
+ value_add_one(
+ &c->rvalue_vals, &ri->tenant_info.sessions_basedn );
+ break;
+ case RBAC_DEFAULT_AUDIT_BASE_DN:
+ value_add_one( &c->rvalue_vals, &ri->tenant_info.audit_basedn );
+ break;
+ case RBAC_ADMIN_DN:
+ value_add_one( &c->rvalue_vals, &ri->tenant_info.admin );
+ break;
+ case RBAC_ADMIN_PWD:
+ value_add_one( &c->rvalue_vals, &ri->tenant_info.pwd );
+ break;
+ case RBAC_SESSION_ADMIN_DN:
+ value_add_one(
+ &c->rvalue_vals, &ri->tenant_info.session_admin );
+ break;
+ case RBAC_DEFAULT_TENANT_ID:
+ value_add_one( &c->rvalue_vals, &ri->tenant_info.tid );
+ break;
+ default:
+ break;
+ }
+ return rc;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ /* FIXME */
+ return 1;
+ }
+ switch ( c->type ) {
+ case RBAC_DEFAULT_USERS_BASE_DN: {
+ struct berval dn = BER_BVNULL;
+ ber_str2bv( c->argv[1], 0, 0, &dn );
+ rc = dnNormalize(
+ 0, NULL, NULL, &dn, &ri->tenant_info.users_basedn, NULL );
+ break;
+ }
+ case RBAC_DEFAULT_ROLES_BASE_DN: {
+ ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.roles_basedn );
+ break;
+ }
+ case RBAC_DEFAULT_PERMISSIONS_BASE_DN: {
+ ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.permissions_basedn );
+ break;
+ }
+ case RBAC_DEFAULT_SESSIONS_BASE_DN: {
+ ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.sessions_basedn );
+ break;
+ }
+ case RBAC_DEFAULT_AUDIT_BASE_DN: {
+ ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.audit_basedn );
+ break;
+ }
+ case RBAC_ADMIN_DN: {
+ ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.admin );
+ break;
+ }
+ case RBAC_ADMIN_PWD: {
+ ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.pwd );
+ break;
+ }
+ case RBAC_SESSION_ADMIN_DN: {
+ ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.session_admin );
+ break;
+ }
+ case RBAC_SESSION_ADMIN_PWD: {
+ ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.session_admin_pwd );
+ break;
+ }
+ case RBAC_DEFAULT_TENANT_ID: {
+ ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.tid );
+ break;
+ }
+ case RBAC_TENANT: {
+ rbac_tenant_parse( c->argv[1], &ri->tenant_info );
+ break;
+ }
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+/*
+ * rbac configuration
+ */
+
+tenant_info_t *
+rbac_tid2tenant( struct berval *tid )
+{
+ /* return the only tenant for now */
+ return &rbac_tenants.tenant_info;
+}
+
+//{ BER_BVC(LDAP_RBAC_EXOP_SESSION_ROLES), rbac_session_roles },
+
+static int
+slap_parse_rbac_session_roles(
+ struct berval *in,
+ rbac_req_t **reqpp,
+ const char **text,
+ void *ctx )
+{
+ int rc = LDAP_SUCCESS;
+ struct berval reqdata = BER_BVNULL;
+ rbac_req_t *reqp = NULL;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len = -1;
+
+ *text = NULL;
+
+ if ( in == NULL || in->bv_len == 0 ) {
+ *text = "empty request data field in rbac_session_roles exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ reqp = rbac_alloc_req( RBAC_REQ_SESSION_ROLES );
+
+ if ( !reqp ) {
+ *text = "unable to allocate memory for rbac_session_roles exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ ber_dupbv_x( &reqdata, in, ctx );
+
+ /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+ ber_init2( ber, &reqdata, 0 );
+
+ tag = ber_scanf( ber, "{" /*}*/ );
+
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_session_roles: "
+ "decoding error.\n" );
+ goto decoding_error;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LDAP_TAG_EXOP_RBAC_USER_ID_SESS ) {
+ struct berval uid;
+ tag = ber_scanf( ber, "m", &uid );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_session_roles: "
+ "user id parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->uid, &uid, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ //tag = ber_peek_tag( ber, &len );
+ if ( tag == LDAP_TAG_EXOP_RBAC_SESSION_ID_SESS ) {
+ struct berval sessid;
+ tag = ber_scanf( ber, "m", &sessid );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_session_roles: "
+ "session id parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->sessid, &sessid, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag != LBER_DEFAULT || len != 0 ) {
+decoding_error:;
+
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_session_roles: "
+ "decoding error, len=%ld\n",
+ (long)len );
+ rc = LDAP_PROTOCOL_ERROR;
+ *text = "data decoding error";
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ *reqpp = reqp;
+ } else {
+ rbac_free_req( reqp );
+ *reqpp = NULL;
+ }
+
+ if ( !BER_BVISNULL( &reqdata ) ) {
+ ber_memfree_x( reqdata.bv_val, ctx );
+ }
+
+ return rc;
+}
+
+static int
+rbac_session_roles( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ const struct berval rbac_op = BER_BVC("SessionRoles");
+ rbac_req_t *reqp = NULL;
+ rbac_session_t *sessp;
+ int rc;
+
+ rs->sr_err = slap_parse_rbac_session_roles(
+ op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ /* get the session using the session id */
+ sessp = rbac_session_byid( op, reqp );
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_roles: "
+ "session not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* checking whether the session is owned by the user */
+ if ( !rbac_is_session_owner( sessp, reqp ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_roles: "
+ "session not owned by user\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ rc = rbac_int_delete_session( op, sessp );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_roles: "
+ "unable to delete session\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /*
+ * If we wanted to...
+ * load these roles into a response with a sequence nested within a
+ * sequence: (No, we're not actually doing this here.)
+ * 0x30 LL ber_printf( ber, "{" );
+ * 0x04 L1
+ * 0x04 L2 a b c d
+ * 0x04 L3 e f g h
+ * 0x04 L4 i j k l
+ * add all three ber_bvarray_add_x( &roles, &tmpbv, NULL );
+ * close it ber_printf( ber, "t{W}", LDAP_TAG_EXOP_RBAC_ROLES, roles );
+ */
+
+ /*
+ * Instead we are...
+ * loading these roles into the response within a sequence: (Yes, we are doing this here.)
+ * 0x30 LL ber_printf( ber, "{" );
+ * 0x04 L1 a b c d
+ * 0x04 L2 e f g h
+ * 0x04 L3 i j k l
+ * add all three ber_bvarray_add_x( &roles, &tmpbv, NULL );
+ * close it ber_printf( ber, "tW", LDAP_TAG_EXOP_RBAC_ROLES, roles );
+ */
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_init_w_nullc( ber, LBER_USE_DER );
+ BerVarray roles = NULL;
+ if ( sessp->roles ) {
+ struct berval tmpbv;
+ // open the sequence:
+ ber_printf( ber, "{" /*}*/ );
+ //char *role;
+ int i = 0;
+ //BerVarray roles = NULL;
+ for ( i = 0; !BER_BVISNULL( &sessp->roles[i] ); i++ ) {
+ tmpbv.bv_val = sessp->roles[i].bv_val;
+ tmpbv.bv_len = sessp->roles[i].bv_len;
+ // add role name:
+ ber_bvarray_add_x( &roles, &tmpbv, NULL );
+
+ //LBER_F( int )
+ //ber_bvecadd_x LDAP_P(( struct berval ***bvec,
+ // struct berval *bv, void *ctx ));
+
+ // first attempt at sequence within a sequence...
+ // open another sequence:
+ /*
+ ber_printf( ber, "{" } );
+ // add role name (again):
+ ber_bvarray_add_x(&roles, &tmpbv, NULL);
+ // close the nested sequence:
+ ber_printf( ber, { "}" );
+*/
+ // end 2nd sequence
+ }
+ /*
+ * This is how we add several octet strings at one time. An array of struct berval's is supplied.
+ * The array is terminated by a struct berval with a NULL bv_val.
+ * Note that a construct like '{W}' is required to get an actual SEQUENCE OF octet strings.
+ * But here we are using 'tW' which allows passing a collection of octets w/out a nesting within a sequence.
+ */
+ ber_printf( ber, "tW",
+ LDAP_TAG_EXOP_RBAC_ROLES, roles);
+
+ // TODO: determine why free on roles array causes a seg fault:
+ //ber_bvarray_free_x(roles, NULL);
+
+ // close the sequence:
+ ber_printf( ber, /*{*/ "N}" );
+ }
+
+ if ( rc < 0 ) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ } else {
+ (void)ber_flatten( ber, &rs->sr_rspdata );
+ rs->sr_err = LDAP_SUCCESS;
+ }
+ ber_free_buf( ber );
+ // END LOAD ROLES INTO RESPONSE
+
+done:;
+ rs->sr_err = rc;
+
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_SESSION_ROLES.bv_val );
+
+ /* generate audit log */
+ rbac_audit(
+ op, SessionRoles, sessp, reqp, rs->sr_err, (char *)rs->sr_text );
+ rbac_free_session( sessp );
+ rbac_free_req( reqp );
+ return rs->sr_err;
+}
+
+static int
+rbac_session_rolesx( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ const struct berval rbac_op = BER_BVC("SessionRoles");
+ rbac_session_t *sessp = NULL;
+ rbac_req_t *reqp = NULL;
+ int rc;
+
+ rs->sr_err = slap_parse_rbac_session_roles(
+ op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ /* get the session using the session id */
+ sessp = rbac_session_byid( op, reqp );
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_roles: "
+ "session not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* checking whether the session is owned by the user */
+ if ( !rbac_is_session_owner( sessp, reqp ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_roles: "
+ "session not owned by user\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ rc = rbac_int_delete_session( op, sessp );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_roles: "
+ "unable to delete session\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /*
+ * If we wanted to...
+ * load these roles into a response with a sequence nested within a
+ * sequence: (No, we're not actually doing this here.)
+ * 0x30 LL ber_printf( ber, "{" );
+ * 0x04 L1
+ * 0x04 L2 a b c d
+ * 0x04 L3 e f g h
+ * 0x04 L4 i j k l
+ * add all three ber_bvarray_add_x( &roles, &tmpbv, NULL );
+ * close it ber_printf( ber, "t{W}", LDAP_TAG_EXOP_RBAC_ROLES, roles );
+ */
+
+ /*
+ * Instead we are...
+ * loading these roles into the response within a sequence: (Yes, we are doing this here.)
+ * 0x30 LL ber_printf( ber, "{" );
+ * 0x04 L1 a b c d
+ * 0x04 L2 e f g h
+ * 0x04 L3 i j k l
+ * add all three ber_bvarray_add_x( &roles, &tmpbv, NULL );
+ * close it ber_printf( ber, "tW", LDAP_TAG_EXOP_RBAC_ROLES, roles );
+ */
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_init_w_nullc( ber, LBER_USE_DER );
+ BerVarray roles = NULL;
+ if ( sessp->roles ) {
+ struct berval tmpbv;
+ // open the sequence:
+ ber_printf( ber, "{" /*}*/ );
+ //char *role;
+ int i = 0;
+ //BerVarray roles = NULL;
+ for ( i = 0; !BER_BVISNULL( &sessp->roles[i] ); i++ ) {
+ tmpbv.bv_val = sessp->roles[i].bv_val;
+ tmpbv.bv_len = sessp->roles[i].bv_len;
+ // add role name:
+ ber_bvarray_add_x( &roles, &tmpbv, NULL );
+
+ // first attempt at sequence within a sequence...
+ // open another sequence:
+ /*
+ ber_printf( ber, "{" } );
+ // add role name (again):
+ ber_bvarray_add_x(&roles, &tmpbv, NULL);
+ // close the nested sequence:
+ ber_printf( ber, { "}" );
+*/
+ // end 2nd sequence
+ }
+ /*
+ * This is how we add several octet strings at one time. An array of struct berval's is supplied.
+ * The array is terminated by a struct berval with a NULL bv_val.
+ * Note that a construct like '{W}' is required to get an actual SEQUENCE OF octet strings.
+ * But here we are using 'tW' which allows passing a collection of octets w/out a nesting within a sequence.
+ */
+ ber_printf( ber, "tW",
+ LDAP_TAG_EXOP_RBAC_ROLES, roles);
+
+ // TODO: determine why free on roles array causes a seg fault:
+ //ber_bvarray_free_x(roles, NULL);
+
+ // close the sequence:
+ ber_printf( ber, /*{*/ "N}" );
+ }
+
+ if ( rc < 0 ) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ } else {
+ (void)ber_flatten( ber, &rs->sr_rspdata );
+ rs->sr_err = LDAP_SUCCESS;
+ }
+ ber_free_buf( ber );
+ // END LOAD ROLES INTO RESPONSE
+
+done:;
+ rs->sr_err = rc;
+
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_SESSION_ROLES.bv_val );
+
+ /* generate audit log */
+ rbac_audit(
+ op, SessionRoles, sessp, reqp, rs->sr_err, (char *)rs->sr_text );
+ rbac_free_session( sessp );
+ rbac_free_req( reqp );
+ return rs->sr_err;
+}
+
+/*
+ * slap_parse_rbac_create_session
+ */
+static int
+slap_parse_rbac_create_session(
+ struct berval *in,
+ rbac_req_t **reqpp,
+ const char **text,
+ void *ctx )
+{
+ int rc = LDAP_SUCCESS;
+ struct berval reqdata = BER_BVNULL;
+ rbac_req_t *reqp = NULL;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len = -1;
+
+ *text = NULL;
+
+ if ( in == NULL || in->bv_len == 0 ) {
+ *text = "empty request data field in rbac_create_session exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ reqp = rbac_alloc_req( RBAC_REQ_CREATE_SESSION );
+
+ if ( !reqp ) {
+ *text = "unable to allocate memory for bac_create_session exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ ber_dupbv_x( &reqdata, in, ctx );
+
+ /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+ ber_init2( ber, &reqdata, 0 );
+
+ tag = ber_scanf( ber, "{" /*}*/ );
+
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+ "decoding error.\n" );
+ goto decoding_error;
+ }
+
+ // Order: 1. sessionId, 2. tenantId, 3. userId, 4. password and 5. roles
+ /* must-have */
+ tag = ber_peek_tag( ber, &len );
+
+ // 1. SESSIONID
+ if ( tag == LDAP_TAG_EXOP_RBAC_SESSION_ID ) {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+ "session id parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->sessid, &bv, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ // 2. TENANT ID
+ if ( tag == LDAP_TAG_EXOP_RBAC_TENANT_ID ) {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+ "tenant id parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->tenantid, &bv, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ // 3. USERID
+ if ( tag == LDAP_TAG_EXOP_RBAC_USER_ID ) {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+ "user id parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->uid, &bv, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ // 4. PASSWORD
+ if ( tag == LDAP_TAG_EXOP_RBAC_AUTHTOK ) {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv);
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+ "authtok parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->authtok, &bv, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ // 5. ROLES
+ if ( tag == LDAP_TAG_EXOP_RBAC_ACTIVE_ROLE ) {
+ tag = ber_scanf( ber, "W", &reqp->roles);
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+ "role parse failed.\n" );
+ goto decoding_error;
+ }
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag != LBER_DEFAULT || len != 0 ) {
+decoding_error:;
+
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+ "decoding error, len=%ld\n",
+ (long)len );
+ rc = LDAP_PROTOCOL_ERROR;
+ *text = "data decoding error";
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "slap_parse_rbac_create_session: "
+ "SUCCESS\n" );
+
+ *reqpp = reqp;
+ } else {
+ Debug( LDAP_DEBUG_ANY, "slap_parse_rbac_create_session: "
+ "NO SUCCESS RC=%d\n", rc );
+
+ rbac_free_req( reqp );
+ *reqpp = NULL;
+ }
+
+ if ( !BER_BVISNULL( &reqdata ) ) {
+ ber_memfree_x( reqdata.bv_val, ctx );
+ }
+
+ return rc;
+}
+
+/*
+ * rbac_create_session:
+ * 1. authenticate user
+ * 2. evaluate pwd policy
+ * 3. create session
+ */
+static int
+rbac_create_session( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ struct berval rbac_op = BER_BVC("CreateSession");
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ int rc = LDAP_SUCCESS;
+ rbac_session_t *sessp = NULL;
+ rbac_user_t *userp = NULL;
+ rbac_req_t *reqp = NULL;
+
+ rs->sr_err = slap_parse_rbac_create_session(
+ op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ /* read user entry */
+ userp = rbac_read_user( op, reqp );
+ if ( !userp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_create_session: "
+ "unable to read user entry\n" );
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = "rbac_create_session: unable to read user entry";
+ goto done;
+ }
+
+ if ( !BER_BVISNULL( &userp->password ) ) {
+ /* if request is with pwd, authenticate the user */
+ rc = rbac_authenticate_user( op, userp );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_create_session: "
+ "rbac_authenticate_user failed!\n" );
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ rs->sr_text = "rbac_create_session: invalid credential";
+ goto done;
+ }
+ /*
+ rbac_user_t *ui = op->o_callback->sc_private;
+ int pVal = ui->authz;
+ printf("password reset val=%d", pVal );
+*/
+
+ } else {
+ /* no pwd is provided, check whether the requesting session */
+ /* id has the access privilege to create a session on behalf */
+ /* of the user */
+ rc = rbac_create_session_acl_check( &reqp->sessid, userp );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_create_session: "
+ "rbac_authenticate_user failed!\n" );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "rbac_create_session: session permission denied";
+ goto done;
+ }
+ }
+
+ /* check user temporal constraint */
+ rc = rbac_user_temporal_constraint( userp );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_create_session: "
+ "rbac_user_temporal_constraint() failed!\n" );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "rbac_create_session: temporal constraint violation";
+ goto done;
+ }
+
+ sessp = rbac_alloc_session();
+ if ( !sessp ) {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "rbac_create_session: unable to allocate session";
+ goto done;
+ }
+
+ rc = activate_session_roles( sessp, reqp, userp );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_create_session: "
+ "failed to activate roles to session!\n" );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text =
+ "rbac_create_session: failed to activate roles into session";
+ goto done;
+ }
+
+ /* store uid and tenant id in session */
+ ber_dupbv( &sessp->userdn, &userp->dn );
+ ber_dupbv( &sessp->uid, &reqp->uid );
+ ber_dupbv( &sessp->tenantid, &reqp->tenantid );
+
+ /* register RBAC session */
+ rc = rbac_register_session( op, rs, sessp );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ ber_init_w_nullc( ber, LBER_USE_DER );
+ rc = ber_printf( ber, "{tO}", LDAP_TAG_EXOP_RBAC_SESSION_ID,
+ &sessp->sessid );
+ if ( rc < 0 ) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ } else {
+ (void)ber_flatten( ber, &rs->sr_rspdata );
+ rs->sr_rspoid = ch_strdup( slap_EXOP_CREATE_SESSION.bv_val );
+ rs->sr_err = LDAP_SUCCESS;
+ }
+
+ ber_free_buf( ber );
+
+done:;
+
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_CREATE_SESSION.bv_val );
+ /* generate audit log */
+ rbac_audit(
+ op, CreateSession, sessp, reqp, rs->sr_err, (char *)rs->sr_text );
+
+ rbac_free_req( reqp );
+ rbac_free_session( sessp );
+
+ //if (rs->sr_err != LDAP_SUCCESS) {
+ //send_ldap_result( op, rs );
+ //}
+
+ return rs->sr_err;
+}
+
+/*
+ * slap_parse_rbac_check_access
+ */
+static int
+slap_parse_rbac_check_access(
+ struct berval *in,
+ rbac_req_t **reqpp,
+ const char **text,
+ void *ctx )
+{
+ int rc = LDAP_SUCCESS;
+ struct berval reqdata = BER_BVNULL;
+ rbac_req_t *reqp = NULL;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len;
+
+ *text = NULL;
+ reqp = rbac_alloc_req( RBAC_REQ_CHECK_ACCESS );
+
+ if ( !reqp ) {
+ *text = "unable to allocate memory for slap_parse_rbac_check_access";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( in == NULL || in->bv_len == 0 ) {
+ *text = "empty request data field in rbac_check_access exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ ber_dupbv_x( &reqdata, in, ctx );
+
+ /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+ ber_init2( ber, &reqdata, 0 );
+
+ tag = ber_scanf( ber, "{" /*}*/ );
+
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+ "decoding error.\n" );
+ goto decoding_error;
+ }
+
+ // sessionId is required:
+ tag = ber_peek_tag( ber, &len );
+ if ( tag != LDAP_TAG_EXOP_RBAC_SESSION_ID ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+ "decoding error.\n" );
+ goto decoding_error;
+ } else {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+ "session id parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->sessid, &bv, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ // operationName is required:
+ if ( tag != LDAP_TAG_EXOP_RBAC_OPNAME ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+ "decoding error.\n" );
+ goto decoding_error;
+ } else {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+ "opname parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->opname, &bv, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ // objectName is required:
+ if ( tag != LDAP_TAG_EXOP_RBAC_OBJNAME ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+ "decoding error.\n" );
+ goto decoding_error;
+ } else {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+ "objname parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->objname, &bv, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ // objectId is optional:
+ if ( tag == LDAP_TAG_EXOP_RBAC_OBJID ) {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+ "objid parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->objid, &bv, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag != LBER_DEFAULT || len != 0 ) {
+decoding_error:;
+
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+ "decoding error, len=%ld\n",
+ (long)len );
+ rc = LDAP_PROTOCOL_ERROR;
+ *text = "data decoding error";
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "slap_parse_rbac_check_access: "
+ "SUCCESS\n" );
+ *reqpp = reqp;
+ } else {
+ Debug( LDAP_DEBUG_ANY, "slap_parse_rbac_check_access: "
+ "FAIL\n" );
+ rbac_free_req( reqp );
+ }
+
+ if ( !BER_BVISNULL( &reqdata ) ) {
+ ber_memfree_x( reqdata.bv_val, ctx );
+ }
+
+ return rc;
+}
+
+// checkAcess F (ALL)
+static int
+rbac_check_access( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ rbac_session_t *sessp = NULL;
+ rbac_permission_t *permp = NULL;
+ rbac_constraint_t *cp = NULL;
+ rbac_req_t *reqp = NULL;
+ const struct berval rbac_op = BER_BVC("CheckAccess");
+ int rc = LDAP_SUCCESS;
+ int found = 0;
+
+ rs->sr_err = slap_parse_rbac_check_access(
+ op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ BER_BVZERO( &op->o_req_dn );
+ BER_BVZERO( &op->o_req_ndn );
+
+ /* get the session using the session id */
+ sessp = rbac_session_byid( op, reqp );
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access: "
+ "session not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* read the permission using objectName and OpName */
+ permp = rbac_read_permission( op, reqp );
+ if ( !permp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access: "
+ "permission not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ // Convert the user-role constraint data from BerVarray to rbac_constraint_t format
+ cp = rbac_user_role_constraints( sessp->role_constraints );
+
+ // Now do the actual rbac checkAccess:
+ rc = rbac_check_session_permission( sessp, permp, cp );
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_check_user_permission: "
+ "failed\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+done:
+
+ rs->sr_err = rc;
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_CHECK_ACCESS.bv_val );
+
+ /* generate audit log */
+ rbac_audit( op, CheckAccess, sessp, reqp, rs->sr_err, (char *)rs->sr_text );
+
+ rbac_free_permission( permp );
+ rbac_free_req( reqp );
+ rbac_free_session( sessp );
+ rbac_free_constraints( cp );
+
+ return rs->sr_err;
+}
+
+// checkAcess A loop back
+static int
+rbac_check_accessA( Operation *op, SlapReply *rs )
+{
+ int rc = LDAP_SUCCESS;
+
+ //rs->sr_err = slap_parse_rbac_check_access(op->ore_reqdata,
+ // &reqp, &rs->sr_text, NULL);
+
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_CHECK_ACCESS.bv_val );
+ rs->sr_err = rc;
+
+ return rc;
+}
+
+// checkAcess B parse
+static int
+rbac_check_accessB( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ rbac_req_t *reqp = NULL;
+ const struct berval rbac_op = BER_BVC("CheckAccess");
+ int rc = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access\n" );
+
+ rs->sr_err = slap_parse_rbac_check_access(
+ op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ BER_BVZERO( &op->o_req_dn );
+ BER_BVZERO( &op->o_req_ndn );
+
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_CHECK_ACCESS.bv_val );
+ rs->sr_err = rc;
+
+ rbac_free_req( reqp );
+
+ return rc;
+}
+
+// checkAcess C - parse request & read session record
+static int
+rbac_check_accessC( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ rbac_session_t *sessp = NULL;
+ rbac_req_t *reqp = NULL;
+ const struct berval rbac_op = BER_BVC("CheckAccess");
+ int rc = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access\n" );
+
+ rs->sr_err = slap_parse_rbac_check_access(
+ op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ BER_BVZERO( &op->o_req_dn );
+ BER_BVZERO( &op->o_req_ndn );
+
+ /* get the session using the session id */
+ sessp = rbac_session_byid( op, reqp );
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access: "
+ "session not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+done:
+
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_CHECK_ACCESS.bv_val );
+ rs->sr_err = rc;
+
+ rbac_free_req( reqp );
+ rbac_free_session( sessp );
+ return rc;
+}
+
+// checkAcess D, parse, read perm
+static int
+rbac_check_accessD( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ rbac_permission_t *permp = NULL;
+ rbac_req_t *reqp = NULL;
+ const struct berval rbac_op = BER_BVC("CheckAccess");
+ int rc = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access\n" );
+
+ rs->sr_err = slap_parse_rbac_check_access(
+ op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ BER_BVZERO( &op->o_req_dn );
+ BER_BVZERO( &op->o_req_ndn );
+
+ /* get the session using the session id */
+ /*
+ sessp = rbac_session_byid(op, reqp);
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access: "
+ "session not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+*/
+
+ /* read the permission using objectName and OpName */
+ permp = rbac_read_permission( op, reqp );
+ if ( !permp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access: "
+ "permission not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+done:
+
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_CHECK_ACCESS.bv_val );
+ rs->sr_err = rc;
+
+ rbac_free_permission( permp );
+ rbac_free_req( reqp );
+
+ return rc;
+}
+
+// checkAcess E everything but the audit insert
+static int
+rbac_check_accessE( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ rbac_session_t *sessp = NULL;
+ rbac_permission_t *permp = NULL;
+ rbac_constraint_t *cp = NULL;
+ rbac_req_t *reqp = NULL;
+ const struct berval rbac_op = BER_BVC("CheckAccess");
+ int rc = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access\n" );
+
+ rs->sr_err = slap_parse_rbac_check_access(
+ op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ BER_BVZERO( &op->o_req_dn );
+ BER_BVZERO( &op->o_req_ndn );
+
+ /* get the session using the session id */
+ sessp = rbac_session_byid( op, reqp );
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access: "
+ "session not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* read the permission using objectName and OpName */
+ permp = rbac_read_permission( op, reqp );
+ if ( !permp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_check_access: "
+ "permission not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ // Convert the user-role constraint data from BerVarray to rbac_constraint_t format
+ cp = rbac_user_role_constraints( sessp->role_constraints );
+
+ // Now do the actual rbac checkAccess:
+ rc = rbac_check_session_permission( sessp, permp, cp );
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_check_user_permission: "
+ "failed\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+done:
+
+ rs->sr_err = rc;
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_CHECK_ACCESS.bv_val );
+
+ /* generate audit log */
+ //rbac_audit(op, CheckAccess, sessp, reqp, rs->sr_err,
+ // (char *) rs->sr_text);
+
+ rbac_free_permission( permp );
+ rbac_free_req( reqp );
+ rbac_free_session( sessp );
+ rbac_free_constraints( cp );
+
+ return rs->sr_err;
+}
+
+/* check whether role exists and role assigned to the user */
+static int
+rbac_check_user_role(
+ rbac_req_t *reqp,
+ rbac_session_t *sessp,
+ rbac_user_t *userp )
+{
+ int rc = 0;
+ int i;
+
+ //assert(!BER_BVISEMPTY(&reqp->roles[0]));
+ assert( !BER_BVISEMPTY( &reqp->role ) );
+
+ for ( i = 0; !BER_BVISNULL( &userp->roles[i] ); i++ ) {
+ //if (!ber_bvstrcasecmp(&userp->roles[i], &reqp->roles[0])) {
+ if ( !ber_bvstrcasecmp( &userp->roles[i], &reqp->role ) ) {
+ rc = 1; /* found the match */
+ goto done;
+ }
+ }
+
+done:;
+
+ return rc;
+}
+
+/* check whether role exists and role assigned to the session */
+static int
+rbac_check_session_role( rbac_req_t *reqp, rbac_session_t *sessp )
+{
+ int rc = 0;
+ int i;
+
+ for ( i = 0; !BER_BVISNULL( &sessp->roles[i] ); i++ ) {
+ //if (!ber_bvstrcasecmp(&sessp->roles[i], &reqp->roles[0])) {
+ if ( !ber_bvstrcasecmp( &sessp->roles[i], &reqp->role ) ) {
+ rc = 1; /* found the match */
+ goto done;
+ }
+ }
+
+done:;
+
+ return rc;
+}
+
+/* make sure user is the owner of the session */
+static int
+rbac_check_user_session( rbac_session_t *sessp, rbac_req_t *reqp )
+{
+ int rc = 0;
+
+ if ( BER_BVISNULL( &sessp->uid ) || BER_BVISNULL( &reqp->uid ) ||
+ sessp->uid.bv_len != reqp->uid.bv_len ) {
+ goto done;
+ }
+
+ if ( !strncasecmp(
+ sessp->uid.bv_val, reqp->uid.bv_val, reqp->uid.bv_len ) ) {
+ rc = 1;
+ goto done;
+ }
+
+done:;
+
+ return rc;
+}
+
+/*
+ * slap_parse_rbac_active_role
+ */
+static int
+slap_parse_rbac_active_role(
+ struct berval *in,
+ int add_or_drop_role,
+ rbac_req_t **reqpp,
+ const char **text,
+ void *ctx )
+{
+ int rc = LDAP_SUCCESS;
+ struct berval reqdata = BER_BVNULL;
+ rbac_req_t *reqp = NULL;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len = -1;
+
+ *text = NULL;
+
+ if ( in == NULL || in->bv_len == 0 ) {
+ *text = "empty request data field in rbac_create_session exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ reqp = rbac_alloc_req( add_or_drop_role );
+
+ if ( !reqp ) {
+ *text = "unable to allocate memory for rbac_add_drop_active_role exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ ber_dupbv_x( &reqdata, in, ctx );
+
+ /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+ ber_init2( ber, &reqdata, 0 );
+
+ tag = ber_scanf( ber, "{" /*}*/ );
+
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_active_role: "
+ "decoding error.\n" );
+ goto decoding_error;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ //if ( tag == LDAP_TAG_EXOP_RBAC_USER_ID ) {
+ if ( tag == LDAP_TAG_EXOP_RBAC_USER_ID_SESS ) {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_active_role: "
+ "user id parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv( &reqp->uid, &bv );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag == LDAP_TAG_EXOP_RBAC_SESSION_ID_SESS ) {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_active_role: "
+ "session id parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv( &reqp->sessid, &bv );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag == LDAP_TAG_EXOP_RBAC_ROLE_NM_SESS ) {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv );
+ //tag = ber_scanf( ber, "W", &reqp->roles);
+ //tag = ber_scanf( ber, "m", &reqp->roles);
+ //tag = ber_scanf( ber, "m", &reqp->roles[0]);
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+ "role parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv( &reqp->role, &bv );
+ //ber_dupbv(&reqp->roles[0], &bv);
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag != LBER_DEFAULT || len != 0 ) {
+decoding_error:;
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+ "decoding error, len=%ld\n",
+ (long)len );
+ rc = LDAP_PROTOCOL_ERROR;
+ *text = "data decoding error";
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ *reqpp = reqp;
+ } else {
+ rbac_free_req( reqp );
+ *reqpp = NULL;
+ }
+
+ if ( !BER_BVISNULL( &reqdata ) ) {
+ ber_memfree_x( reqdata.bv_val, ctx );
+ }
+
+ return rc;
+}
+
+static int
+rbac_add_active_role( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ struct berval rbac_op = BER_BVC("AddActiveRole");
+ rbac_req_t *reqp = NULL;
+ rbac_user_t *userp = NULL;
+ rbac_session_t *sessp;
+ int rc = LDAP_SUCCESS;
+
+ rs->sr_err = slap_parse_rbac_active_role( op->ore_reqdata,
+ RBAC_REQ_ADD_ACTIVE_ROLE, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ /* get the session using the session id */
+ sessp = rbac_session_byid( op, reqp );
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_add_active_role: "
+ "session not found\n" );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "rbac_add_active_role: session not found";
+ goto done;
+ }
+
+ /* read user entry */
+ userp = rbac_read_user( op, reqp );
+ if ( !userp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_add_active_role: "
+ "unable to read user entry\n" );
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = "rbac_add_active_role: unable to read user entry";
+ goto done;
+ }
+
+ /* make sure role exists and role assigned to the user */
+ if ( !rbac_check_user_role( reqp, sessp, userp ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_add_active_role: "
+ "role not assigned to the user\n" );
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = "rbac_add_active_role: role not assigned to the user";
+ goto done;
+ }
+
+ /* make sure user is the owner of the session */
+ if ( !rbac_check_user_session( sessp, reqp ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_add_active_role: "
+ "user not owner of session\n" );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "rbac_add_active_role: user not owner of the session";
+ goto done;
+ }
+
+ /* add the role to the session */
+ rc = rbac_session_add_role( op, sessp, reqp );
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_err = rc;
+ if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
+ rs->sr_text =
+ "rbac_add_active_role: role already activated in session";
+ Debug( LDAP_DEBUG_ANY, "rbac_add_active_role: "
+ "role already activated in session\n" );
+ } else {
+ rs->sr_text = "rbac_add_active_role: unable to add role to session";
+ Debug( LDAP_DEBUG_ANY, "rbac_add_active_role: "
+ "unable to add role to session\n" );
+ }
+ goto done;
+ }
+
+done:
+
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_ADD_ACTIVE_ROLE.bv_val );
+
+ /* generate audit log */
+ rbac_audit(
+ op, AddActiveRole, sessp, reqp, rs->sr_err, (char *)rs->sr_text );
+
+ rbac_free_session( sessp );
+ rbac_free_user( userp );
+ rbac_free_req( reqp );
+
+ return rs->sr_err;
+}
+
+static int
+rbac_drop_active_role( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ const struct berval rbac_op = BER_BVC("DropActiveRole");
+ rbac_session_t *sessp;
+ rbac_req_t *reqp = NULL;
+ int rc = LDAP_SUCCESS;
+
+ rs->sr_err = slap_parse_rbac_active_role( op->ore_reqdata,
+ RBAC_REQ_DROP_ACTIVE_ROLE, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ /* get the session using the session id */
+ sessp = rbac_session_byid( op, reqp );
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_drop_active_role: "
+ "session not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ if ( BER_BVISNULL( &reqp->role ) || !sessp->roles ||
+ BER_BVISNULL( &sessp->roles[0] ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_drop_active_role: "
+ "unavailable role\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* make sure role exists and role assigned to the user */
+ if ( !rbac_check_session_role( reqp, sessp ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_drop_active_role: "
+ "role not assigned to session\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* make sure user is the owner of the session */
+ if ( !rbac_check_user_session( sessp, reqp ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_drop_active_role: "
+ "user not owner of session\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "rbac_drop_active_role: user not owner of the session";
+ goto done;
+ }
+
+ /* drop the role to the session */
+ rc = rbac_session_drop_role( op, sessp, reqp );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_drop_active_role: "
+ "unable to drop active role from session\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "rbac_drop_active_role: unable to drop role from session";
+ goto done;
+ }
+
+done:
+ rs->sr_err = rc;
+
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_DROP_ACTIVE_ROLE.bv_val );
+
+ /* generate audit log */
+ rbac_audit(
+ op, DropActiveRole, sessp, reqp, rs->sr_err, (char *)rs->sr_text );
+
+ rbac_free_session( sessp );
+ rbac_free_req( reqp );
+
+ return rs->sr_err;
+}
+
+/*
+ * slap_parse_rbac_delete_session
+ */
+static int
+slap_parse_rbac_delete_session(
+ struct berval *in,
+ rbac_req_t **reqpp,
+ const char **text,
+ void *ctx )
+{
+ int rc = LDAP_SUCCESS;
+ struct berval reqdata = BER_BVNULL;
+ rbac_req_t *reqp = NULL;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len = -1;
+
+ *text = NULL;
+
+ if ( in == NULL || in->bv_len == 0 ) {
+ *text = "empty request data field in rbac_delete_session exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ reqp = rbac_alloc_req( RBAC_REQ_DELETE_SESSION );
+
+ if ( !reqp ) {
+ *text = "unable to allocate memory for rbac_delete_session exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ ber_dupbv_x( &reqdata, in, ctx );
+
+ /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+ ber_init2( ber, &reqdata, 0 );
+
+ tag = ber_scanf( ber, "{" /*}*/ );
+
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_delete_session: "
+ "decoding error.\n" );
+ goto decoding_error;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LDAP_TAG_EXOP_RBAC_USER_ID_SESS ) {
+ struct berval uid;
+ tag = ber_scanf( ber, "m", &uid );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_delete_session: "
+ "user id parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->uid, &uid, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ //tag = ber_peek_tag( ber, &len );
+ if ( tag == LDAP_TAG_EXOP_RBAC_SESSION_ID_SESS ) {
+ struct berval sessid;
+ tag = ber_scanf( ber, "m", &sessid );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_delete_session: "
+ "session id parse failed.\n" );
+ goto decoding_error;
+ }
+ ber_dupbv_x( &reqp->sessid, &sessid, ctx );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag != LBER_DEFAULT || len != 0 ) {
+decoding_error:;
+
+ Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_delete_session: "
+ "decoding error, len=%ld\n",
+ (long)len );
+ rc = LDAP_PROTOCOL_ERROR;
+ *text = "data decoding error";
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ *reqpp = reqp;
+ } else {
+ rbac_free_req( reqp );
+ *reqpp = NULL;
+ }
+
+ if ( !BER_BVISNULL( &reqdata ) ) {
+ ber_memfree_x( reqdata.bv_val, ctx );
+ }
+
+ return rc;
+}
+
+static int
+rbac_delete_session( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ const struct berval rbac_op = BER_BVC("DeleteSession");
+ rbac_session_t *sessp = NULL;
+ rbac_req_t *reqp = NULL;
+ int rc;
+
+ rs->sr_err = slap_parse_rbac_delete_session(
+ op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ /* get the session using the session id */
+ sessp = rbac_session_byid( op, reqp );
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_delete_session: "
+ "session not found\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* checking whether the session is owned by the user */
+ if ( !rbac_is_session_owner( sessp, reqp ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_delete_session: "
+ "session not owned by user\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ rc = rbac_int_delete_session( op, sessp );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_int_delete_session: "
+ "unable to delete session\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+done:;
+
+ rs->sr_err = rc;
+
+ // always put the OID in the response:
+ rs->sr_rspoid = ch_strdup( slap_EXOP_DELETE_SESSION.bv_val );
+
+ /* generate audit log */
+ rbac_audit(
+ op, DeleteSession, sessp, reqp, rs->sr_err, (char *)rs->sr_text );
+
+ rbac_free_session( sessp );
+ rbac_free_req( reqp );
+
+ return rs->sr_err;
+}
+
+/* returns the permissions associated with a session */
+static int
+rbac_session_permissions( Operation *op, SlapReply *rs, rbac_req_t *reqp )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ const struct berval rbac_op = BER_BVC("SessionPermissions");
+ rbac_session_t *sessp;
+
+ sessp = rbac_session_byid( op, reqp );
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_permissions: "
+ "session id not found\n" );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ rs->sr_err = rbac_int_session_permissions( op, rs, reqp, sessp );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_permissions: "
+ "permissions not found\n" );
+ goto done;
+ }
+
+done:;
+ return rs->sr_err;
+}
+
+/* extract session permission info from op */
+int
+rbac_search_parse_session_permissions_req(
+ Operation *op,
+ rbac_req_t **reqpp,
+ const char **text,
+ void *ctx )
+{
+ int rc = LDAP_SUCCESS;
+ struct berval *sessid = NULL;
+ rbac_req_t *reqp = NULL;
+ *text = NULL;
+ struct berval rbac_session_id = BER_BVC("sessionID");
+ struct berval rbac_session_permissions_attr =
+ BER_BVC("sessionPermissions");
+ AttributeDescription *ad = NULL;
+ Filter *f;
+
+ /* check simple assertion (sessionID=<session id>) */
+ f = op->ors_filter;
+ ad = f->f_ava->aa_desc;
+ if ( !ad || ber_bvstrcasecmp( &rbac_session_id, &ad->ad_cname ) ) {
+ goto done;
+ }
+ sessid = &f->f_ava->aa_value;
+
+ if ( !rbac_is_valid_session_id( sessid ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_search_parse_session_permissions_req: "
+ "invalid session id\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* check requested attr */
+
+ if ( !op->oq_search.rs_attrs ||
+ BER_BVISNULL( &op->oq_search.rs_attrs[0].an_name ) ||
+ ber_bvstrcasecmp( &op->oq_search.rs_attrs[0].an_name,
+ &rbac_session_permissions_attr ) ||
+ !BER_BVISNULL( &op->oq_search.rs_attrs[1].an_name ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_search_parse_session_permissions_req: "
+ "only sessionPermissions allowed\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ reqp = rbac_alloc_req( RBAC_REQ_SESSION_PERMISSIONS );
+ if ( !reqp ) {
+ *text = "unable to allocate memory for rbac_session_permissions req";
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* retrieve session id from search filter */
+ ber_dupbv_x( &reqp->sessid, sessid, ctx );
+
+done:;
+
+ if ( rc == LDAP_SUCCESS ) {
+ *reqpp = reqp;
+ } else {
+ rbac_free_req( reqp );
+ *reqpp = NULL;
+ }
+
+ return rc;
+}
+
+static int
+rbac_search( Operation *op, SlapReply *rs )
+{
+ Debug( LDAP_DEBUG_ANY, "rbac_search entry\n" );
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+static int
+rbac_search( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ rbac_req_t *reqp = NULL;
+ int rc = SLAP_CB_CONTINUE;
+
+ only session_permissions is implemented for now
+ rc = rbac_search_parse_session_permissions_req(
+ op, &reqp, &rs->sr_text, NULL );
+ if ( !reqp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_search: "
+ "invalid search for session permissions\n" );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ rc = rbac_session_permissions( op, rs, reqp );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_search: "
+ "session permissions failed\n" );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+
+done:;
+ send_ldap_result( op, rs );
+
+ return rc;
+}
+*/
+
+static struct exop {
+ struct berval oid;
+ BI_op_extended *extended;
+} rbac_exop_table[] = {
+ { BER_BVC(LDAP_RBAC_EXOP_CREATE_SESSION), rbac_create_session },
+ { BER_BVC(LDAP_RBAC_EXOP_CHECK_ACCESS), rbac_check_access },
+ { BER_BVC(LDAP_RBAC_EXOP_ADD_ACTIVE_ROLE), rbac_add_active_role },
+ { BER_BVC(LDAP_RBAC_EXOP_DROP_ACTIVE_ROLE), rbac_drop_active_role },
+ { BER_BVC(LDAP_RBAC_EXOP_DELETE_SESSION), rbac_delete_session },
+ { BER_BVC(LDAP_RBAC_EXOP_SESSION_ROLES), rbac_session_roles },
+
+ { BER_BVNULL, NULL }
+};
+
+static int
+rbac_add( Operation *op, SlapReply *rs )
+{
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rbac_bind( Operation *op, SlapReply *rs )
+{
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rbac_compare( Operation *op, SlapReply *rs )
+{
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rbac_delete( Operation *op, SlapReply *rs )
+{
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rbac_modify( Operation *op, SlapReply *rs )
+{
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rbac_extended( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ int rc = SLAP_CB_CONTINUE;
+ int i;
+
+ for ( i = 0; rbac_exop_table[i].extended != NULL; i++ ) {
+ if ( bvmatch( &rbac_exop_table[i].oid, &op->oq_extended.rs_reqoid ) ) {
+ rc = rbac_exop_table[i].extended( op, rs );
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ break;
+ case SLAP_CB_CONTINUE:
+ case SLAPD_ABANDON:
+ return rc;
+ default:
+ send_ldap_result( op, rs );
+ return rc;
+ }
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static int
+rbac_db_init( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+
+ return 0;
+}
+
+static int
+rbac_db_open( BackendDB *be, ConfigReply *cr )
+{
+ int rc = LDAP_SUCCESS;
+
+ rc = rbac_initialize_tenants( be, cr );
+
+ return rc;
+}
+
+static int
+rbac_db_close( BackendDB *be, ConfigReply *cr )
+{
+ return 0;
+}
+
+int
+rbac_initialize()
+{
+ int rc;
+
+ rc = load_extop2( (struct berval *)&slap_EXOP_CREATE_SESSION,
+ SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, rbac_create_session, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "unable to register rbac_create_session exop: %d\n",
+ rc );
+ return rc;
+ }
+
+ rc = load_extop2( (struct berval *)&slap_EXOP_CHECK_ACCESS,
+ SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, rbac_check_access, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "unable to register rbac_check_access exop: %d\n",
+ rc );
+ return rc;
+ }
+
+ rc = load_extop2( (struct berval *)&slap_EXOP_ADD_ACTIVE_ROLE,
+ SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, rbac_add_active_role, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "unable to register rbac_add_active_role exop: %d\n",
+ rc );
+ return rc;
+ }
+
+ rc = load_extop2( (struct berval *)&slap_EXOP_DROP_ACTIVE_ROLE,
+ SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, rbac_drop_active_role, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "unable to register rbac_drop_active_role exop: %d\n",
+ rc );
+ return rc;
+ }
+
+ rc = load_extop2( (struct berval *)&slap_EXOP_DELETE_SESSION,
+ SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, rbac_delete_session, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "unable to register rbac_delete_session exop: %d\n",
+ rc );
+ return rc;
+ }
+
+ rc = load_extop2( (struct berval *)&slap_EXOP_SESSION_ROLES,
+ SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, rbac_session_roles, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+ "unable to register rbac_session_roles exop: %d\n",
+ rc );
+ return rc;
+ }
+
+ rbac.on_bi.bi_type = "rbac";
+ rbac.on_bi.bi_db_init = rbac_db_init;
+ rbac.on_bi.bi_db_open = rbac_db_open;
+ rbac.on_bi.bi_db_close = rbac_db_close;
+
+ rbac.on_bi.bi_op_add = rbac_add;
+ rbac.on_bi.bi_op_bind = rbac_bind;
+ rbac.on_bi.bi_op_compare = rbac_compare;
+ rbac.on_bi.bi_op_delete = rbac_delete;
+ rbac.on_bi.bi_op_modify = rbac_modify;
+ rbac.on_bi.bi_op_search = rbac_search;
+ rbac.on_bi.bi_extended = rbac_extended;
+ rbac.on_bi.bi_cf_ocs = rbac_ocs;
+
+ /* rbac.on_bi.bi_connection_destroy = rbac_connection_destroy; */
+
+ rc = config_register_schema( rbaccfg, rbac_ocs );
+ if ( rc ) return rc;
+
+ rc = rbac_initialize_repository();
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ return overlay_register( &rbac );
+}
+
+int
+init_module( int argc, char *argv[] )
+{
+ return rbac_initialize();
+}
diff --git a/contrib/slapd-modules/rbac/rbac.h b/contrib/slapd-modules/rbac/rbac.h
new file mode 100644
index 0000000..4461236
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbac.h
@@ -0,0 +1,402 @@
+/* rbac.h - */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ *
+ */
+
+#ifndef RBAC_H
+#define RBAC_H
+
+LDAP_BEGIN_DECL
+
+#include "ldap_rbac.h"
+
+#define USE_NEW_THREAD_CONTEXT 1
+#define RBAC_BUFLEN 1024
+
+/* tenant initialization op */
+#define INIT_AUDIT_CONTAINER 0x01
+#define INIT_SESSION_CONTAINER 0x02
+
+typedef struct rbac_ad {
+ int type;
+ struct berval attr;
+ AttributeDescription **ad;
+} rbac_ad_t;
+
+/* RBAC AttributeDescriptions */
+struct slap_rbac_internal_schema {
+ /* slapd schema */
+ AttributeDescription *ad_uid;
+
+ /* RBAC tenant */
+ AttributeDescription *ad_tenant_id;
+
+ /* RBAC sessions */
+ AttributeDescription *ad_session_id;
+ AttributeDescription *ad_session_user_dn;
+ AttributeDescription *ad_session_roles;
+ AttributeDescription *ad_session_role_constraints;
+
+ /* RBAC session permissions */
+ AttributeDescription *ad_permission_opname;
+ AttributeDescription *ad_permission_objname;
+ AttributeDescription *ad_permission_rolename;
+
+ /* RBAC audit */
+ AttributeDescription *ad_audit_op; /* rbac op: create_session */
+ AttributeDescription *ad_audit_id;
+ AttributeDescription *ad_audit_roles;
+ AttributeDescription *ad_audit_requested_roles;
+ AttributeDescription *ad_audit_timestamp;
+ AttributeDescription *ad_audit_resources;
+ AttributeDescription *ad_audit_objects;
+ AttributeDescription *ad_audit_operations; /* resource ops */
+ AttributeDescription *ad_audit_result;
+ AttributeDescription *ad_audit_properties;
+ AttributeDescription *ad_audit_messages;
+
+ /* RBAC session attributes */
+ AttributeName *session_attrs;
+};
+
+extern struct slap_rbac_internal_schema slap_rbac_schema;
+
+/* attributes in tenant repository */
+struct slap_rbac_tenant_schema {
+ /* user role assignments, role constraints, and user constraint */
+ AttributeDescription *ad_role;
+ AttributeDescription *ad_role_constraint;
+ AttributeDescription *ad_user_constraint;
+ AttributeDescription *ad_uid;
+
+ /* session permission */
+ AttributeDescription *ad_permission_users;
+ AttributeDescription *ad_permission_roles;
+ AttributeDescription *ad_permission_objname;
+ AttributeDescription *ad_permission_opname;
+
+ /* the list of attributes when doing searches in the jts repo */
+ AttributeName *user_attrs;
+ AttributeName *perm_attrs; /* attrs to retrieve for check access */
+ AttributeName *session_perm_attrs; /* attrs for session permissions */
+
+ /* the corresponding list of attribute description mapping */
+ rbac_ad_t *user_ads;
+ rbac_ad_t *permission_ads;
+ rbac_ad_t *session_permissions_ads;
+};
+
+extern struct slap_rbac_tenant_schema slap_rbac_jts_schema;
+
+/* types of RBAC requests */
+typedef struct rbac_request {
+ int req_type;
+ struct berval sessid;
+ struct berval tenantid;
+
+ /* session creation */
+ struct berval uid;
+ struct berval authtok;
+ BerVarray roles;
+ struct berval role;
+
+ /* check access */
+ struct berval opname;
+ struct berval objname;
+ struct berval objid;
+} rbac_req_t;
+
+typedef struct rbac_constraint {
+ struct berval name; /* user name or role name */
+ int allowed_inactivity; /* secs */
+ int begin_time; /* secs */
+ int end_time; /* secs */
+ lutil_timet begin_date;
+ lutil_timet end_date;
+ lutil_timet begin_lock_date;
+ lutil_timet end_lock_date;
+ int day_mask;
+ struct rbac_constraint *next;
+} rbac_constraint_t;
+
+/* holds RBAC info */
+typedef struct tenant_info {
+ struct berval tid; /* tenant id */
+ struct berval admin;
+ struct berval pwd;
+ struct berval users_basedn;
+ struct berval roles_basedn;
+ struct berval audit_basedn;
+ struct berval permissions_basedn;
+ struct berval sessions_basedn;
+ struct berval session_admin;
+ struct berval session_admin_pwd;
+ struct slap_rbac_tenant_schema *schema;
+} tenant_info_t;
+
+typedef struct rbac_tenant {
+ tenant_info_t tenant_info;
+ struct rbac_tenant *next;
+} rbac_tenant_t;
+
+/* for RBAC callback */
+typedef struct rbac_callback_info {
+ tenant_info_t *tenantp;
+ void *private;
+} rbac_callback_info_t;
+
+/* RBAC user */
+typedef struct rbac_user {
+ struct berval tenantid;
+ struct berval uid;
+ struct berval dn;
+ struct berval constraints;
+ struct berval password;
+ struct berval msg;
+ int authz; /* flag for bind (pwd policy) info */
+ BerVarray roles;
+ BerVarray role_constraints;
+#if 0 /* additional parameters from Fortress */
+ private String userId;
+ @XmlElement(nillable = true)
+ private char[] password;
+ @XmlElement(nillable = true)
+ private char[] newPassword;
+ private String internalId;
+ @XmlElement(nillable = true)
+ private List<UserRole> roles;
+ @XmlElement(nillable = true)
+ private List<UserAdminRole> adminRoles;
+ private String pwPolicy;
+ private String cn;
+ private String sn;
+ private String dn;
+ private String ou;
+ private String description;
+ private String beginTime;
+ private String endTime;
+ private String beginDate;
+ private String endDate;
+ private String beginLockDate;
+ private String endLockDate;
+ private String dayMask;
+ private String name;
+ private int timeout;
+ private boolean reset;
+ private boolean locked;
+ private Boolean system;
+ @XmlElement(nillable = true)
+ private Props props = new Props();
+ @XmlElement(nillable = true)
+ private Address address;
+ @XmlElement(nillable = true)
+ private List<String> phones;
+ @XmlElement(nillable = true)
+ private List<String> mobiles;
+ @XmlElement(nillable = true)
+ private List<String> emails;
+#endif /* 0 */
+} rbac_user_t;
+
+enum {
+ RBAC_NONE = 0,
+ RBAC_TENANT,
+ RBAC_TENANT_ID,
+ RBAC_USERS_BASE_DN,
+ RBAC_ROLES_BASE_DN,
+ RBAC_PERMISSIONS_BASE_DN,
+ RBAC_ADMIN_DN,
+ RBAC_ADMIN_PWD,
+ RBAC_SESSIONS_BASE_DN,
+ RBAC_SESSION_ADMIN_DN,
+ RBAC_SESSION_ADMIN_PWD,
+ RBAC_ROLE_ASSIGNMENT,
+ RBAC_ROLE_CONSTRAINTS,
+ RBAC_USER_CONSTRAINTS,
+ RBAC_UID,
+ RBAC_USERS,
+ RBAC_ROLES,
+ RBAC_OBJ_NAME,
+ RBAC_OP_NAME,
+ RBAC_ROLE_NAME,
+ RBAC_SESSION_ID,
+ RBAC_USER_DN,
+ RBAC_AUDIT_ROLES,
+ RBAC_AUDIT_RESOURCES,
+ RBAC_AUDIT_RESULT,
+ RBAC_AUDIT_TIMESTAMP,
+ RBAC_AUDIT_PROPERTIES,
+ RBAC_AUDIT_OP,
+ RBAC_AUDIT_ID,
+ RBAC_AUDIT_REQUESTED_ROLES,
+ RBAC_AUDIT_OBJS,
+ RBAC_AUDIT_OPS,
+ RBAC_AUDIT_MSGS,
+ RBAC_LAST
+};
+
+enum {
+ RBAC_DEFAULT_TENANT_ID = RBAC_LAST,
+ RBAC_DEFAULT_USERS_BASE_DN,
+ RBAC_DEFAULT_PERMISSIONS_BASE_DN,
+ RBAC_DEFAULT_ROLES_BASE_DN,
+ RBAC_DEFAULT_SESSIONS_BASE_DN,
+ RBAC_DEFAULT_AUDIT_BASE_DN
+};
+
+typedef struct rbac_user_idlist {
+ char *user_id;
+ struct rbac_user_idlist *next;
+} rbac_user_idlist_t;
+
+/* RBAC sessions */
+#define RBAC_SESSION_RDN_EQ "rbacSessid="
+#define RBAC_AUDIT_RDN_EQ "rbacAuditId="
+
+typedef struct rbac_session {
+ rbac_user_t *user;
+ struct berval tenantid;
+ struct berval sessid;
+ struct berval uid;
+ struct berval userdn;
+ char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
+ struct berval sessdn;
+ long last_access;
+ int timeout;
+ int warning_id;
+ int error_id;
+ int grace_logins;
+ int expiration_secs;
+ int is_authenticated; /* boolean */
+ struct berval message;
+ BerVarray roles;
+ BerVarray role_constraints;
+} rbac_session_t;
+
+/* RBAC roles */
+typedef struct rbac_role {
+ char *name;
+ char *description;
+ struct rbac_role *parent;
+ struct rbac_role *next;
+} rbac_role_t;
+
+typedef struct rbac_role_list {
+ char *name;
+ struct rbac_role_list *next;
+} rbac_role_list_t;
+
+/* RBAC permissions */
+typedef struct rbac_permission {
+ struct berval dn;
+ int admin; /* boolean */
+ struct berval internalId;
+ BerVarray opName;
+ BerVarray objName;
+ struct berval objectId;
+ struct berval abstractName;
+ struct berval type;
+ BerVarray roles;
+ BerVarray uids;
+ struct rbac_permission *next;
+} rbac_permission_t;
+
+/* RBAC Audit */
+typedef enum {
+ CreateSession = 0,
+ CheckAccess,
+ AddActiveRole,
+ DropActiveRole,
+ SessionPermissions,
+ DeleteSession,
+ SessionRoles
+} audit_op_t;
+
+/* function prototypes */
+
+int rbac_initialize_repository( void );
+int rbac_initialize_tenants( BackendDB *be, ConfigReply *cr );
+
+/* RBAC tenant information */
+tenant_info_t *rbac_tid2tenant( struct berval *tid );
+
+rbac_req_t *rbac_alloc_req( int type );
+void rbac_free_req( rbac_req_t *reqp );
+
+rbac_user_t *rbac_read_user( Operation *op, rbac_req_t *rabc_reqp );
+int rbac_authenticate_user( Operation *op, rbac_user_t *user );
+int rbac_user_temporal_constraint( rbac_user_t *userp );
+void rbac_free_user( rbac_user_t *user );
+
+rbac_session_t *rbac_alloc_session( void );
+int rbac_is_valid_session_id( struct berval *sessid );
+rbac_session_t *rbac_session_byid( Operation *op, rbac_req_t *reqp );
+int rbac_is_session_owner( rbac_session_t *sessp, rbac_req_t *reqp );
+int rbac_register_session( Operation *op, SlapReply *rs, rbac_session_t *sess );
+int rbac_int_delete_session( Operation *op, rbac_session_t *sessp );
+int rbac_session_add_role(
+ Operation *op,
+ rbac_session_t *sessp,
+ rbac_req_t *reqp );
+int rbac_session_drop_role(
+ Operation *op,
+ rbac_session_t *sessp,
+ rbac_req_t *reqp );
+int rbac_int_session_permissions(
+ Operation *op,
+ SlapReply *rs,
+ rbac_req_t *reqp,
+ rbac_session_t *sessp );
+int activate_session_roles(
+ rbac_session_t *sessp,
+ rbac_req_t *reqp,
+ rbac_user_t *userp );
+void rbac_free_session( rbac_session_t *sessp );
+
+rbac_constraint_t *rbac_user_role_constraints( BerVarray values );
+rbac_constraint_t *rbac_role2constraint(
+ struct berval *role,
+ rbac_constraint_t *role_constraints );
+rbac_constraint_t *rbac_bv2constraint( struct berval *bv );
+int rbac_check_time_constraint( rbac_constraint_t *cp );
+void rbac_free_constraint( rbac_constraint_t *cp );
+void rbac_free_constraints( rbac_constraint_t *constraints );
+
+rbac_permission_t *rbac_read_permission( Operation *op, rbac_req_t *rbac_reqp );
+int rbac_check_session_permission(
+ rbac_session_t *sessp,
+ rbac_permission_t *permp,
+ rbac_constraint_t *role_constraints );
+void rbac_free_permission( rbac_permission_t *permp );
+
+/* audit functions */
+void rbac_audit(
+ Operation *op,
+ audit_op_t rbac_op,
+ rbac_session_t *sessp,
+ rbac_req_t *reqp,
+ int result,
+ char *msg );
+
+/* acl functions */
+int rbac_create_session_acl_check( struct berval *sessid, rbac_user_t *userp );
+
+void rbac_to_lower( struct berval *bv );
+
+LDAP_END_DECL
+
+#endif /* RBAC_H */
diff --git a/contrib/slapd-modules/rbac/rbacacl.c b/contrib/slapd-modules/rbac/rbacacl.c
new file mode 100644
index 0000000..269dcf5
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbacacl.c
@@ -0,0 +1,37 @@
+/* rbacacl.c - RBAC ACL */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+int
+rbac_create_session_acl_check( struct berval *sessid, rbac_user_t *userp )
+{
+ int rc = LDAP_SUCCESS;
+
+ return rc;
+}
diff --git a/contrib/slapd-modules/rbac/rbacaudit.c b/contrib/slapd-modules/rbac/rbacaudit.c
new file mode 100644
index 0000000..ef04ece
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbacaudit.c
@@ -0,0 +1,233 @@
+/* rbacaudit.c - RBAC Audit */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+static struct rbac_audit_op {
+ audit_op_t op;
+ struct berval op_bv;
+} rbac_audit_ops[] = {
+ { CreateSession, BER_BVC("CreateSession") },
+ { CheckAccess, BER_BVC("CheckAccess") },
+ { AddActiveRole, BER_BVC("AddActiveRole") },
+ { DropActiveRole, BER_BVC("DropActiveRole") },
+ { SessionPermissions, BER_BVC("SessionPermissions") },
+ { DeleteSession, BER_BVC("DeleteSession") },
+ { SessionRoles, BER_BVC("SessionRoles") },
+
+ { -1, BER_BVNULL }
+};
+
+static int
+rbac_audit_fake_cb( Operation *op, SlapReply *rs )
+{
+ Debug( LDAP_DEBUG_ANY, "rbac_audit_fake_cb\n" );
+
+ return 0;
+}
+
+void
+rbac_audit(
+ Operation *op,
+ audit_op_t rbac_op,
+ rbac_session_t *sessp,
+ rbac_req_t *reqp,
+ int result,
+ char *msg )
+{
+ int op_idx, rc = LDAP_SUCCESS;
+ int found = 0;
+ struct berval timestamp;
+ tenant_info_t *tenantp = rbac_tid2tenant( &sessp->tenantid );
+ slap_callback cb = { 0 };
+ SlapReply rs2 = { REP_RESULT };
+ Entry *e = NULL;
+ struct berval auditObjectClass = BER_BVC("rbacAudit");
+ struct berval auditResultSuccess = BER_BVC("success");
+ struct berval auditResultFailed = BER_BVC("failed");
+ struct berval bv, rdn, nrdn;
+ char rdnbuf[RBAC_BUFLEN];
+ time_t now;
+ char nowstr[LDAP_LUTIL_GENTIME_BUFSIZE];
+
+ for ( op_idx = 0; rbac_audit_ops[op_idx].op != -1; op_idx++ ) {
+ if ( rbac_op == rbac_audit_ops[op_idx].op ) {
+ /* legit audit op */
+ found = 1;
+ break;
+ }
+ }
+
+ if ( !found ) goto done;
+
+ e = entry_alloc();
+
+ /* audit timestamp */
+ now = slap_get_time(); /* stored for later consideration */
+ timestamp.bv_val = nowstr;
+ timestamp.bv_len = sizeof(nowstr);
+ slap_timestamp( &now, &timestamp );
+
+ /* construct audit record DN; FIXME: random() call */
+ sprintf( rdnbuf, "%s%d", RBAC_AUDIT_RDN_EQ, (int)op->o_tid );
+ strcat( rdnbuf, "-" );
+ strncat( rdnbuf, timestamp.bv_val, timestamp.bv_len );
+ bv.bv_val = &rdnbuf[0];
+ bv.bv_len = strlen( &rdnbuf[0] );
+
+ rc = dnPrettyNormal( NULL, &bv, &rdn, &nrdn, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_audit: "
+ "unable to normalize audit rDN (rc=%d)\n", rc );
+ goto done;
+ }
+
+ /* FIXME: audit_basedn should have been normalized */
+ build_new_dn( &e->e_name, &tenantp->audit_basedn, &rdn, NULL );
+ build_new_dn( &e->e_nname, &tenantp->audit_basedn, &nrdn, NULL );
+
+ ch_free( rdn.bv_val );
+ ch_free( nrdn.bv_val );
+
+ /* add timestamp */
+ attr_merge_one( e, slap_rbac_schema.ad_audit_timestamp, &timestamp, NULL );
+
+ /* add rbac audit objectClass */
+
+ attr_merge_one( e, slap_schema.si_ad_objectClass, &auditObjectClass, NULL );
+ attr_merge_one( e, slap_schema.si_ad_structuralObjectClass,
+ &auditObjectClass, NULL );
+
+ /* audit op */
+ attr_merge_one( e, slap_rbac_schema.ad_audit_op,
+ &rbac_audit_ops[op_idx].op_bv, NULL );
+
+ /* userid */
+ if ( sessp && !BER_BVISNULL( &sessp->uid ) ) {
+ attr_merge_one( e, slap_schema.si_ad_uid, &sessp->uid, NULL );
+ }
+
+ /* session id */
+
+ if ( sessp && !BER_BVISNULL( &sessp->sessid ) ) {
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+ struct berval sessid = BER_BVC("rbacSessid");
+
+ rc = slap_bv2ad( &sessid, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ attr_merge_one( e, ad, &sessp->sessid, NULL );
+ }
+
+ /* audit result */
+ attr_merge_one( e, slap_rbac_schema.ad_audit_result,
+ result == LDAP_SUCCESS ? &auditResultSuccess : &auditResultFailed,
+ NULL );
+
+ switch ( rbac_op ) {
+ case CreateSession:
+ /* audit roles */
+ if ( sessp && sessp->roles ) {
+ attr_merge( e, slap_rbac_schema.ad_audit_roles, sessp->roles,
+ NULL );
+ }
+ if ( reqp && reqp->roles ) {
+ attr_merge( e, slap_rbac_schema.ad_audit_requested_roles,
+ reqp->roles, NULL );
+ }
+ break;
+
+ case CheckAccess:
+ if ( sessp && sessp->roles ) {
+ attr_merge( e, slap_rbac_schema.ad_audit_roles, sessp->roles,
+ NULL );
+ }
+ if ( reqp && !BER_BVISEMPTY( &reqp->opname ) ) {
+ attr_merge_one( e, slap_rbac_schema.ad_audit_operations,
+ &reqp->opname, NULL );
+ }
+ if ( reqp && !BER_BVISEMPTY( &reqp->objname ) ) {
+ attr_merge_one( e, slap_rbac_schema.ad_audit_objects,
+ &reqp->objname, NULL );
+ }
+ break;
+
+ case AddActiveRole:
+ if ( reqp && reqp->roles ) {
+ attr_merge( e, slap_rbac_schema.ad_audit_requested_roles,
+ reqp->roles, NULL );
+ }
+ break;
+
+ case DropActiveRole:
+ /* audit roles */
+ if ( reqp && reqp->roles ) {
+ attr_merge( e, slap_rbac_schema.ad_audit_requested_roles,
+ reqp->roles, NULL );
+ }
+ break;
+
+ case SessionPermissions:
+ if ( sessp && sessp->roles ) {
+ attr_merge( e, slap_rbac_schema.ad_audit_roles, sessp->roles,
+ NULL );
+ }
+ break;
+
+ case DeleteSession:
+ case SessionRoles:
+ default:
+ break;
+ }
+
+ /* record the audit record */
+ Operation op2 = *op;
+ rbac_callback_info_t rbac_cb;
+ cb.sc_private = &rbac_cb;
+ cb.sc_response = rbac_audit_fake_cb;
+ op2.o_callback = &cb;
+
+ op2.o_tag = LDAP_REQ_ADD;
+ op2.o_protocol = LDAP_VERSION3;
+ op2.o_req_dn = e->e_name;
+ op2.o_req_ndn = e->e_nname;
+ op2.ora_e = e;
+ op2.o_bd = select_backend( &op2.o_req_ndn, 0 );
+ op2.o_dn = op2.o_bd->be_rootdn;
+ op2.o_ndn = op2.o_bd->be_rootndn;
+
+ op2.ors_limit = NULL;
+ rc = op2.o_bd->be_add( &op2, &rs2 );
+
+done:
+ if ( e ) entry_free( e );
+
+ return;
+}
diff --git a/contrib/slapd-modules/rbac/rbacperm.c b/contrib/slapd-modules/rbac/rbacperm.c
new file mode 100644
index 0000000..e1f6d79
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbacperm.c
@@ -0,0 +1,233 @@
+/* rbacperm.c - RBAC permission */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+static int
+rbac_read_permission_cb( Operation *op, SlapReply *rs )
+{
+ rbac_callback_info_t *cbp = op->o_callback->sc_private;
+ rbac_ad_t *permission_ads;
+ rbac_permission_t *permp;
+ int i;
+
+ if ( rs->sr_type != REP_SEARCH ) return 0;
+
+ assert( cbp );
+
+ permp = ch_calloc( 1, sizeof(rbac_permission_t) );
+ permission_ads = cbp->tenantp->schema->permission_ads;
+
+ ber_dupbv( &permp->dn, &rs->sr_entry->e_name );
+ for ( i = 0; !BER_BVISNULL( &permission_ads[i].attr ); i++ ) {
+ Attribute *attr = NULL;
+ attr = attr_find( rs->sr_entry->e_attrs, *permission_ads[i].ad );
+ if ( attr != NULL ) {
+ switch ( permission_ads[i].type ) {
+ case RBAC_USERS:
+ ber_bvarray_dup_x( &permp->uids, attr->a_nvals, NULL );
+ break;
+ case RBAC_ROLES:
+ ber_bvarray_dup_x( &permp->roles, attr->a_nvals, NULL );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ cbp->private = (void *)permp;
+
+ return 0;
+}
+
+/*
+ * check whether roles assigned to a user allows access to roles in
+ * a permission, subject to role constraints
+ */
+int
+rbac_check_session_permission(
+ rbac_session_t *sessp,
+ rbac_permission_t *permp,
+ rbac_constraint_t *role_constraints )
+{
+ int rc = LDAP_INSUFFICIENT_ACCESS;
+ rbac_constraint_t *cp = NULL;
+ int i, j;
+
+ if ( !sessp->roles || !permp->roles ) goto done;
+
+ for ( i = 0; !BER_BVISNULL( &sessp->roles[i] ); i++ ) {
+ for ( j = 0; !BER_BVISNULL( &permp->roles[j] ); j++ ) {
+ if ( ber_bvstrcasecmp( &sessp->roles[i], &permp->roles[j] ) == 0 ) {
+ /* role temporal constraint */
+ cp = rbac_role2constraint( &permp->roles[j], role_constraints );
+ if ( !cp || rbac_check_time_constraint( cp ) == LDAP_SUCCESS ) {
+ rc = LDAP_SUCCESS;
+ goto done;
+ }
+ }
+ }
+ }
+done:;
+ return rc;
+}
+
+rbac_permission_t *
+rbac_read_permission( Operation *op, rbac_req_t *reqp )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ rbac_callback_info_t rbac_cb;
+ int rc = LDAP_SUCCESS;
+ char fbuf[1024];
+ struct berval filter = { sizeof(fbuf), fbuf };
+ char permbuf[1024];
+ struct berval permdn = { sizeof(permbuf), permbuf };
+ struct berval permndn = BER_BVNULL;
+ char pcls[] = "(objectClass=ftOperation)";
+ SlapReply rs2 = { REP_RESULT };
+ slap_callback cb = { 0 };
+ tenant_info_t *tenantp = rbac_tid2tenant( &reqp->tenantid );
+
+#if 0 /* check valid object name and op name */
+ if ( !is_valid_opname( &reqp->opname ) ||
+ !is_valid_objname( &reqp->objname ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_read_permission: "
+ "invalid opname (%s) or objname (%s)\n",
+ reqp->opname.bv_val, reqp->objname.bv_val );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+#endif
+
+ if ( !tenantp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_read_permission: "
+ "missing tenant information\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ if ( reqp->objid.bv_val != NULL ) {
+ permdn.bv_len = snprintf( permdn.bv_val, permdn.bv_len,
+ "ftObjId=%s+ftOpNm=%s,ftObjNm=%s,%s", reqp->objid.bv_val,
+ reqp->opname.bv_val, reqp->objname.bv_val,
+ tenantp->permissions_basedn.bv_val );
+ } else {
+ permdn.bv_len = snprintf( permdn.bv_val, permdn.bv_len,
+ "ftOpNm=%s,ftObjNm=%s,%s", reqp->opname.bv_val,
+ reqp->objname.bv_val, tenantp->permissions_basedn.bv_val );
+ }
+
+ rc = dnNormalize( 0, NULL, NULL, &permdn, &permndn, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_read_permission: "
+ "unable to normalize permission DN\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ filter.bv_val = pcls;
+ filter.bv_len = strlen( pcls );
+ rbac_cb.tenantp = tenantp;
+ rbac_cb.private = NULL;
+
+ Operation op2 = *op;
+ cb.sc_private = &rbac_cb;
+ cb.sc_response = rbac_read_permission_cb;
+ op2.o_callback = &cb;
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2.o_dn = tenantp->admin;
+ op2.o_ndn = tenantp->admin;
+ op2.o_req_dn = permdn;
+ op2.o_req_ndn = permndn;
+ op2.ors_filterstr = filter;
+ op2.ors_filter = str2filter_x( &op2, filter.bv_val );
+ op2.ors_scope = LDAP_SCOPE_BASE;
+ op2.ors_attrs = tenantp->schema->perm_attrs;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+ op2.ors_slimit = SLAP_NO_LIMIT;
+ op2.ors_attrsonly = 0;
+ op2.ors_limit = NULL;
+ op2.o_bd = frontendDB;
+ rc = op2.o_bd->be_search( &op2, &rs2 );
+ filter_free_x( &op2, op2.ors_filter, 1 );
+
+done:;
+ ch_free( permndn.bv_val );
+
+ if ( rc != LDAP_SUCCESS ) {
+ rbac_free_permission((rbac_permission_t *)rbac_cb.private);
+ }
+
+ return (rbac_permission_t *)rbac_cb.private;
+}
+
+void
+rbac_free_permission( rbac_permission_t *permp )
+{
+ if ( !permp ) return;
+
+ if ( !BER_BVISNULL( &permp->dn ) ) {
+ ber_memfree( permp->dn.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &permp->internalId ) ) {
+ ber_memfree( permp->internalId.bv_val );
+ }
+
+ if ( permp->opName ) {
+ ber_bvarray_free( permp->opName );
+ }
+
+ if ( permp->objName ) {
+ ber_bvarray_free( permp->objName );
+ }
+
+ if ( !BER_BVISNULL( &permp->objectId ) ) {
+ ber_memfree( permp->objectId.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &permp->abstractName ) ) {
+ ber_memfree( permp->abstractName.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &permp->type ) ) {
+ ber_memfree( permp->type.bv_val );
+ }
+
+ if ( permp->roles ) {
+ ber_bvarray_free( permp->roles );
+ }
+
+ if ( permp->uids ) {
+ ber_bvarray_free( permp->uids );
+ }
+ ch_free( permp );
+
+ return;
+}
diff --git a/contrib/slapd-modules/rbac/rbacreq.c b/contrib/slapd-modules/rbac/rbacreq.c
new file mode 100644
index 0000000..9942a00
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbacreq.c
@@ -0,0 +1,89 @@
+/* rbacreq.c - RBAC requests */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+rbac_req_t *
+rbac_alloc_req( int type )
+{
+ rbac_req_t *reqp = NULL;
+
+ reqp = ch_calloc( 1, sizeof(rbac_req_t) );
+
+ reqp->req_type = type;
+ BER_BVZERO( &reqp->sessid );
+ BER_BVZERO( &reqp->tenantid );
+ /* session creation */
+ BER_BVZERO( &reqp->uid );
+ BER_BVZERO( &reqp->authtok );
+ reqp->roles = NULL;
+ /* check access */
+ BER_BVZERO( &reqp->opname );
+ BER_BVZERO( &reqp->objname );
+ BER_BVZERO( &reqp->objid );
+ /* add/drop role */
+ BER_BVZERO( &reqp->role );
+
+ return reqp;
+}
+
+void
+rbac_free_req( rbac_req_t *reqp )
+{
+ if ( !reqp ) return;
+
+ if ( !BER_BVISNULL( &reqp->sessid ) )
+ ber_memfree( reqp->sessid.bv_val );
+
+ if ( !BER_BVISNULL( &reqp->tenantid ) )
+ ber_memfree( reqp->tenantid.bv_val );
+
+ /* session creation */
+ if ( !BER_BVISNULL( &reqp->uid ) )
+ ber_memfree( reqp->uid.bv_val );
+
+ if ( !BER_BVISNULL( &reqp->authtok ) )
+ ber_memfree( reqp->authtok.bv_val );
+
+ if ( reqp->roles )
+ ber_bvarray_free( reqp->roles );
+
+ /* check access */
+ if ( !BER_BVISNULL( &reqp->opname ) )
+ ber_memfree( reqp->opname.bv_val );
+
+ if ( !BER_BVISNULL( &reqp->objname ) )
+ ber_memfree( reqp->objname.bv_val );
+
+ if ( !BER_BVISNULL( &reqp->objid ) )
+ ber_memfree( reqp->objid.bv_val );
+
+ ch_free( reqp );
+
+ return;
+}
diff --git a/contrib/slapd-modules/rbac/rbacsess.c b/contrib/slapd-modules/rbac/rbacsess.c
new file mode 100644
index 0000000..d18e312
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbacsess.c
@@ -0,0 +1,999 @@
+/* rbacsess.c - RBAC session */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+static slap_callback nullsc = { NULL, NULL, NULL, NULL };
+
+extern rbac_ad_t rbac_session_permission_ads[];
+extern rbac_ad_t rbac_session_ads[];
+
+struct berval slapo_session_oc = BER_BVC("rbacSession");
+
+typedef struct session_perm_req {
+ Operation *op;
+ SlapReply *rs;
+ struct berval *sessid;
+ struct berval permdn;
+ tenant_info_t *tenantp;
+} session_perm_req_t;
+
+static int
+rbac_sess_fake_cb( Operation *op, SlapReply *rs )
+{
+ Debug( LDAP_DEBUG_ANY, "rbac_sess_fake_cb\n" );
+
+ return 0;
+}
+
+static int
+rbac_send_session_permission(
+ session_perm_req_t *sess_perm_reqp,
+ rbac_permission_t *perm )
+{
+ int i, rc = LDAP_SUCCESS;
+ Operation *op = sess_perm_reqp->op;
+ SlapReply *rs = sess_perm_reqp->rs;
+ struct berval *sessidp = sess_perm_reqp->sessid;
+ struct berval *permdnp = &sess_perm_reqp->permdn;
+
+ Entry *e = entry_alloc();
+ e->e_attrs = NULL;
+ ber_dupbv( &e->e_name, permdnp );
+ ber_dupbv( &e->e_nname, permdnp );
+ e->e_private = NULL;
+ attr_merge_one( e, slap_rbac_schema.ad_session_id, sessidp, NULL );
+
+ for ( i = 0; !BER_BVISNULL( &rbac_session_permission_ads[i].attr ); i++ ) {
+ switch ( rbac_session_permission_ads[i].type ) {
+ case RBAC_OP_NAME:
+ attr_merge_one( e, *rbac_session_permission_ads[i].ad,
+ &perm->opName[0], NULL );
+ break;
+ case RBAC_OBJ_NAME:
+ attr_merge_one( e, *rbac_session_permission_ads[i].ad,
+ &perm->objName[0], NULL );
+ break;
+ case RBAC_ROLE_NAME:
+ attr_merge( e, *rbac_session_permission_ads[i].ad, perm->roles,
+ NULL );
+ break;
+ default:
+ break;
+ }
+ }
+
+ rs->sr_entry = e;
+ rs->sr_flags = REP_ENTRY_MUSTRELEASE;
+ rc = send_search_entry( op, rs );
+
+ return rc;
+}
+
+static int
+rbac_session_permissions_cb( Operation *op, SlapReply *rs )
+{
+ session_perm_req_t *sess_perm_reqp = op->o_callback->sc_private;
+ tenant_info_t *tenantp = NULL;
+ rbac_permission_t *permp = NULL;
+ rbac_ad_t *session_permissions_ads;
+ int i;
+
+ if ( rs->sr_type != REP_SEARCH ) return 0;
+
+ assert( sess_perm_reqp );
+
+ tenantp = sess_perm_reqp->tenantp;
+ session_permissions_ads = tenantp->schema->session_permissions_ads;
+
+ permp = ch_calloc( 1, sizeof(rbac_permission_t) );
+
+ for ( i = 0; !BER_BVISNULL( &session_permissions_ads[i].attr ); i++ ) {
+ Attribute *attr = NULL;
+
+ attr = attr_find(
+ rs->sr_entry->e_attrs, *session_permissions_ads[i].ad );
+ if ( attr != NULL ) {
+ switch ( session_permissions_ads[i].type ) {
+ case RBAC_USERS:
+ ber_bvarray_dup_x( &permp->uids, attr->a_nvals, NULL );
+ break;
+ case RBAC_ROLES:
+ ber_bvarray_dup_x( &permp->roles, attr->a_nvals, NULL );
+ break;
+ case RBAC_OBJ_NAME:
+ ber_bvarray_dup_x( &permp->objName, attr->a_nvals, NULL );
+ break;
+ case RBAC_OP_NAME:
+ ber_bvarray_dup_x( &permp->opName, attr->a_nvals, NULL );
+ break;
+ }
+ }
+ }
+
+ rbac_send_session_permission( sess_perm_reqp, permp );
+ rbac_free_permission( permp );
+ permp = NULL;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rbac_read_session_cb( Operation *op, SlapReply *rs )
+{
+ rbac_session_t *sessp = op->o_callback->sc_private;
+ int i;
+
+ if ( rs->sr_type != REP_SEARCH ) return 0;
+
+ ber_dupbv( &sessp->sessdn, &rs->sr_entry->e_name );
+
+ for ( i = 0; !BER_BVISNULL( &rbac_session_ads[i].attr ); i++ ) {
+ Attribute *attr = NULL;
+ attr = attr_find( rs->sr_entry->e_attrs, *rbac_session_ads[i].ad );
+ if ( attr != NULL ) {
+ switch ( rbac_session_ads[i].type ) {
+ case RBAC_SESSION_ID:
+ ber_dupbv( &sessp->sessid, &attr->a_vals[0] );
+ break;
+ case RBAC_USER_DN:
+ ber_dupbv( &sessp->userdn, &attr->a_vals[0] );
+ break;
+ case RBAC_ROLES:
+ ber_bvarray_dup_x( &sessp->roles, attr->a_nvals, NULL );
+ break;
+ case RBAC_ROLE_CONSTRAINTS:
+ ber_bvarray_dup_x(
+ &sessp->role_constraints, attr->a_nvals, NULL );
+ break;
+ case RBAC_UID:
+ ber_dupbv( &sessp->uid, &attr->a_vals[0] );
+ break;
+ case RBAC_TENANT_ID:
+ ber_dupbv( &sessp->tenantid, &attr->a_vals[0] );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ //return SLAP_CB_CONTINUE;
+ return 0;
+}
+
+/* check whether the session is owned by the user */
+int
+rbac_is_session_owner( rbac_session_t *sessp, rbac_req_t *reqp )
+{
+ int rc = 0;
+
+ if ( BER_BVISEMPTY( &sessp->uid ) || BER_BVISEMPTY( &reqp->uid ) ) {
+ Debug( LDAP_DEBUG_ANY, "session not owned by user\n" );
+ rc = 0;
+ goto done;
+ }
+
+ if ( !ber_bvstrcasecmp( &sessp->uid, &reqp->uid ) ) {
+ rc = 1;
+ goto done;
+ }
+
+done:;
+ return rc;
+}
+
+int
+rbac_session_add_role( Operation *op, rbac_session_t *sessp, rbac_req_t *reqp )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ slap_callback cb = { 0 };
+ SlapReply rs2 = { REP_RESULT };
+ Operation op2 = *op;
+ rbac_callback_info_t rbac_cb;
+ tenant_info_t *tenantp = NULL;
+ struct berval vals[2];
+ Modifications mod;
+ int rc = LDAP_SUCCESS;
+
+ tenantp = rbac_tid2tenant( &reqp->tenantid );
+ if ( !tenantp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_add_role: "
+ "no tenant info with the req\n" );
+ goto done;
+ }
+
+ // convert the role name to lower case:
+ rbac_to_lower( &reqp->role );
+
+ //ber_dupbv( &vals[0], &reqp->roles[0]);
+ ber_dupbv( &vals[0], &reqp->role );
+ BER_BVZERO( &vals[1] );
+
+ /* create mod list */
+ mod.sml_op = LDAP_MOD_ADD;
+ mod.sml_flags = 0;
+ mod.sml_type = slap_rbac_schema.ad_session_roles->ad_cname;
+ mod.sml_desc = slap_rbac_schema.ad_session_roles;
+ mod.sml_numvals = 1;
+ mod.sml_values = vals;
+ mod.sml_nvalues = NULL;
+ mod.sml_next = NULL;
+
+ cb.sc_private = &rbac_cb;
+ cb.sc_response = rbac_sess_fake_cb;
+ op2.o_callback = &cb;
+
+ op2.o_tag = LDAP_REQ_MODIFY;
+ op2.orm_modlist = &mod;
+ op2.o_req_dn = sessp->sessdn;
+ op2.o_req_ndn = sessp->sessdn;
+ op2.o_bd = select_backend( &op2.o_req_ndn, 0 );
+ op2.o_dn = op2.o_bd->be_rootdn;
+ op2.o_ndn = op2.o_bd->be_rootdn;
+ op2.ors_limit = NULL;
+ rc = op2.o_bd->be_modify( &op2, &rs2 );
+ ch_free( vals[0].bv_val );
+
+done:;
+ if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_add_active_role: "
+ "role already activated in session\n" );
+ }
+ return rc;
+}
+
+int
+rbac_session_drop_role( Operation *op, rbac_session_t *sessp, rbac_req_t *reqp )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ slap_callback cb = { 0 };
+ SlapReply rs2 = { REP_RESULT };
+ Operation op2 = *op;
+ rbac_callback_info_t rbac_cb;
+ tenant_info_t *tenantp = NULL;
+ Modifications *m = NULL;
+ int rc = LDAP_SUCCESS;
+
+ tenantp = rbac_tid2tenant( &reqp->tenantid );
+ if ( !tenantp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_drop_role: "
+ "no tenant info with the req\n" );
+ goto done;
+ }
+
+ /* create mod list */
+ m = ch_calloc( sizeof(Modifications), 1 );
+ m->sml_op = LDAP_MOD_DELETE;
+ m->sml_flags = 0;
+ m->sml_type = slap_rbac_schema.ad_session_roles->ad_cname;
+ m->sml_desc = slap_rbac_schema.ad_session_roles;
+ m->sml_numvals = 1;
+ m->sml_values = ch_calloc( sizeof(struct berval), 2 );
+ m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
+ //ber_dupbv( &m->sml_values[0], &reqp->roles[0]);
+
+ // convert the role name to lower case:
+ rbac_to_lower( &reqp->role );
+
+ ber_dupbv( &m->sml_values[0], &reqp->role );
+
+ // todo: determine if this needs to be done:
+ //BER_BVZERO(&m->sml_values[1]);
+
+ ber_dupbv( &m->sml_nvalues[0], &reqp->role );
+ BER_BVZERO( &m->sml_nvalues[1] );
+
+ //ber_dupbv( &m->sml_nvalues[0], &reqp->roles[0]);
+ //ber_dupbv( &m->sml_nvalues[0], &reqp->role);
+ //BER_BVZERO(&m->sml_nvalues[1]);
+
+ m->sml_next = NULL;
+
+ cb.sc_private = &rbac_cb;
+ cb.sc_response = rbac_sess_fake_cb;
+ op2.o_callback = &cb;
+
+ op2.o_dn = tenantp->session_admin;
+ op2.o_ndn = tenantp->session_admin;
+ op2.o_tag = LDAP_REQ_MODIFY;
+ op2.orm_modlist = m;
+ op2.o_req_dn = sessp->sessdn;
+ op2.o_req_ndn = sessp->sessdn;
+ op2.o_bd = select_backend( &op2.o_req_ndn, 0 );
+
+ op2.ors_limit = NULL;
+ rc = op2.o_bd->be_modify( &op2, &rs2 );
+
+done:;
+ if ( m ) {
+ slap_mods_free( m, 1 );
+ }
+
+ return rc;
+}
+
+/* delete the session */
+int
+rbac_int_delete_session( Operation *op, rbac_session_t *sessp )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ slap_callback cb = { 0 };
+ SlapReply rs2 = { REP_RESULT };
+ Operation op2 = *op;
+ rbac_callback_info_t rbac_cb;
+ tenant_info_t *tenantp = NULL;
+ int rc = LDAP_SUCCESS;
+
+ tenantp = rbac_tid2tenant( &sessp->tenantid );
+ if ( !tenantp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_drop_role: "
+ "no tenant info with the req\n" );
+ goto done;
+ }
+
+ /* delete RBAC session */
+ cb.sc_private = &rbac_cb;
+ cb.sc_response = rbac_sess_fake_cb;
+ op2.o_callback = &cb;
+
+ op2.o_dn = tenantp->session_admin;
+ op2.o_ndn = tenantp->session_admin;
+ op2.o_tag = LDAP_REQ_DELETE;
+ op2.o_req_dn = sessp->sessdn;
+ op2.o_req_ndn = sessp->sessdn;
+ op2.o_bd = select_backend( &op2.o_req_ndn, 0 );
+ rc = op2.o_bd->be_delete( &op2, &rs2 );
+
+done:;
+ return rc;
+}
+
+rbac_session_t *
+rbac_alloc_session()
+{
+ rbac_session_t *sessp = NULL;
+
+ sessp = ch_malloc( sizeof(rbac_session_t) );
+ sessp->sessid.bv_len =
+ lutil_uuidstr( sessp->uuidbuf, sizeof(sessp->uuidbuf) );
+ sessp->sessid.bv_val = sessp->uuidbuf;
+
+ sessp->user = NULL;
+ BER_BVZERO( &sessp->tenantid );
+ BER_BVZERO( &sessp->uid );
+ BER_BVZERO( &sessp->userdn );
+ BER_BVZERO( &sessp->sessdn );
+ BER_BVZERO( &sessp->message );
+
+ sessp->last_access = 0;
+ sessp->timeout = 0;
+ sessp->warning_id = 0;
+ sessp->error_id = 0;
+ sessp->grace_logins = 0;
+ sessp->expiration_secs = 0;
+ sessp->is_authenticated = 0;
+
+ sessp->roles = NULL;
+ sessp->role_constraints = NULL;
+
+ return sessp;
+}
+
+int
+rbac_register_session( Operation *op, SlapReply *rs, rbac_session_t *sessp )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ struct berval rdn, nrdn;
+ SlapReply rs2 = { REP_RESULT };
+ OperationBuffer opbuf;
+ Operation *op2;
+ Connection conn = { 0 };
+ Entry *e = NULL;
+ int rc = LDAP_SUCCESS;
+ char rdnbuf[
+ STRLENOF(RBAC_SESSION_RDN_EQ) + LDAP_LUTIL_UUIDSTR_BUFSIZE + 1];
+ tenant_info_t *tenantp = rbac_tid2tenant( &sessp->tenantid );
+#ifdef USE_NEW_THREAD_CONTEXT
+ void *thrctx = ldap_pvt_thread_pool_context();
+#else
+ void *thrctx = op->o_tmpmemctx;
+#endif
+
+ if ( !sessp ) {
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* dynamic objects */
+ e = entry_alloc();
+
+ strcpy( rdnbuf, RBAC_SESSION_RDN_EQ );
+ strncat( rdnbuf, sessp->sessid.bv_val, sessp->sessid.bv_len );
+ rdn.bv_val = rdnbuf;
+ rdn.bv_len = STRLENOF(RBAC_SESSION_RDN_EQ) + sessp->sessid.bv_len;
+ nrdn.bv_val = rdnbuf;
+ nrdn.bv_len = STRLENOF(RBAC_SESSION_RDN_EQ) + sessp->sessid.bv_len;
+
+ build_new_dn( &e->e_name, &tenantp->sessions_basedn, &rdn, NULL );
+ build_new_dn( &e->e_nname, &tenantp->sessions_basedn, &nrdn, NULL );
+
+ attr_merge_one( e, slap_schema.si_ad_objectClass, &slapo_session_oc, NULL );
+ attr_merge_one( e, slap_schema.si_ad_structuralObjectClass,
+ &slapo_session_oc, NULL );
+ attr_merge_one( e, slap_rbac_schema.ad_session_id, &sessp->sessid, NULL );
+
+ if ( !BER_BVISNULL( &sessp->uid ) ) {
+ attr_merge_one( e, slap_schema.si_ad_uid, &sessp->uid, NULL );
+ }
+
+ /* add tenant id */
+ if ( !BER_BVISNULL( &sessp->tenantid ) ) {
+ attr_merge_one(
+ e, slap_rbac_schema.ad_tenant_id, &sessp->tenantid, NULL );
+ }
+
+ /* add the userdn */
+ if ( !BER_BVISNULL( &sessp->userdn ) ) {
+ attr_merge_one(
+ e, slap_rbac_schema.ad_session_user_dn, &sessp->userdn, NULL );
+ }
+
+ if ( sessp->roles ) {
+ attr_merge( e, slap_rbac_schema.ad_session_roles, sessp->roles, NULL );
+ }
+
+ // TODO: ensure this is correct way to store constraints in session:
+ if ( sessp->role_constraints ) {
+ attr_merge( e, slap_rbac_schema.ad_session_role_constraints,
+ sessp->role_constraints, NULL );
+ }
+ /* rendered dynmaicObject */
+ attr_merge_one( e, slap_schema.si_ad_objectClass,
+ &slap_schema.si_oc_dynamicObject->soc_cname, NULL );
+
+ /* store RBAC session */
+ connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+ op2 = &opbuf.ob_op;
+ //Operation op2 = *op;
+ //op2.o_callback = &nullsc;
+ //rbac_callback_info_t rbac_cb;
+ //cb.sc_private = &rbac_cb;
+ //cb.sc_response = rbac_sess_fake_cb;
+ //op2.o_callback = &cb;
+ //op2.ors_limit = NULL;
+ op->o_callback = &nullsc;
+ op2->o_dn = tenantp->session_admin;
+ op2->o_ndn = tenantp->session_admin;
+ op2->o_tag = LDAP_REQ_ADD;
+ op2->o_protocol = LDAP_VERSION3;
+ op2->o_req_dn = e->e_name;
+ op2->o_req_ndn = e->e_nname;
+ op2->ora_e = e;
+ op2->o_bd = frontendDB;
+
+ rc = op2->o_bd->be_add( op2, &rs2 );
+
+done:;
+ if ( e ) entry_free( e );
+ return rc;
+}
+
+int
+rbac_register_session2( Operation *op, SlapReply *rs, rbac_session_t *sessp )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ struct berval rdn, nrdn;
+ SlapReply rs2 = { REP_RESULT };
+ Operation op2 = *op;
+ rbac_callback_info_t rbac_cb;
+ //OperationBuffer opbuf;
+ //Connection conn = {0};
+ Entry *e = NULL;
+ int rc = LDAP_SUCCESS;
+ char rdnbuf[STRLENOF(RBAC_SESSION_RDN_EQ) + LDAP_LUTIL_UUIDSTR_BUFSIZE +
+ 1];
+ tenant_info_t *tenantp = rbac_tid2tenant( &sessp->tenantid );
+ slap_callback cb = { 0 };
+ //#ifdef USE_NEW_THREAD_CONTEXT
+ // void *thrctx = ldap_pvt_thread_pool_context();
+ //#else
+ // void *thrctx = op->o_tmpmemctx;
+ //#endif
+
+ if ( !sessp ) {
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* dynamic objects */
+ e = entry_alloc();
+
+ strcpy( rdnbuf, RBAC_SESSION_RDN_EQ );
+ strncat( rdnbuf, sessp->sessid.bv_val, sessp->sessid.bv_len );
+ rdn.bv_val = rdnbuf;
+ rdn.bv_len = STRLENOF(RBAC_SESSION_RDN_EQ) + sessp->sessid.bv_len;
+ nrdn.bv_val = rdnbuf;
+ nrdn.bv_len = STRLENOF(RBAC_SESSION_RDN_EQ) + sessp->sessid.bv_len;
+
+ build_new_dn( &e->e_name, &tenantp->sessions_basedn, &rdn, NULL );
+ build_new_dn( &e->e_nname, &tenantp->sessions_basedn, &nrdn, NULL );
+
+ attr_merge_one( e, slap_schema.si_ad_objectClass, &slapo_session_oc, NULL );
+ attr_merge_one( e, slap_schema.si_ad_structuralObjectClass,
+ &slapo_session_oc, NULL );
+ attr_merge_one( e, slap_rbac_schema.ad_session_id, &sessp->sessid, NULL );
+
+ if ( !BER_BVISNULL( &sessp->uid ) ) {
+ attr_merge_one( e, slap_schema.si_ad_uid, &sessp->uid, NULL );
+ }
+
+ /* add tenant id */
+ if ( !BER_BVISNULL( &sessp->tenantid ) ) {
+ attr_merge_one(
+ e, slap_rbac_schema.ad_tenant_id, &sessp->tenantid, NULL );
+ }
+
+ /* add the userdn */
+ if ( !BER_BVISNULL( &sessp->userdn ) ) {
+ attr_merge_one(
+ e, slap_rbac_schema.ad_session_user_dn, &sessp->userdn, NULL );
+ }
+
+ if ( sessp->roles ) {
+ attr_merge( e, slap_rbac_schema.ad_session_roles, sessp->roles, NULL );
+ }
+
+ // TODO: ensure this is correct way to store constraints in session:
+ if ( sessp->role_constraints ) {
+ attr_merge( e, slap_rbac_schema.ad_session_role_constraints,
+ sessp->role_constraints, NULL );
+ }
+ /* rendered dynmaicObject */
+ attr_merge_one( e, slap_schema.si_ad_objectClass,
+ &slap_schema.si_oc_dynamicObject->soc_cname, NULL );
+
+ /* store RBAC session */
+ //connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+ //op2 = &opbuf.ob_op;
+ //op2.o_ctrlflag = op->o_ctrlflag;
+ // todo this ain't right"
+ //op2.o_ctrlflag = 0;
+ //OperationBuffer *opbuf;
+ //memset( opbuf, 0, sizeof(OperationBuffer));
+ //op2.o_hdr = &opbuf->ob_hdr;
+ //op2.o_controls = opbuf->ob_controls;
+
+ // fails on modify.c:353 with segfault
+
+ //op2.o_callback = &nullsc;
+ cb.sc_private = &rbac_cb;
+ cb.sc_response = rbac_sess_fake_cb;
+ op2.o_callback = &cb;
+ op2.o_dn = tenantp->session_admin;
+ op2.o_ndn = tenantp->session_admin;
+ op2.o_tag = LDAP_REQ_ADD;
+ op2.o_protocol = LDAP_VERSION3;
+ op2.o_req_dn = e->e_name;
+ op2.o_req_ndn = e->e_nname;
+ op2.ora_e = e;
+ op2.o_bd = frontendDB;
+ //op2.ors_limit = NULL;
+
+ rc = op2.o_bd->be_add( &op2, &rs2 );
+
+done:;
+ if ( e ) entry_free( e );
+
+ return rc;
+}
+
+int
+rbac_is_valid_session_id( struct berval *sessid )
+{
+ /* TODO: simple test */
+ if ( !sessid || sessid->bv_len != 36 ) {
+ if ( !sessid ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_is_valid_session_id: "
+ "null sessid\n" );
+ } else {
+ Debug( LDAP_DEBUG_ANY, "rbac_is_valid_session_id: "
+ "len (%lu)\n",
+ sessid->bv_len );
+ }
+ return 0;
+ }
+
+ else {
+ return 1;
+ }
+}
+
+/* create an rbac request with the session ID */
+rbac_req_t *
+rbac_is_search_session_permissions( Operation *op )
+{
+ rbac_req_t *reqp = NULL;
+
+ /* check whether the search for sessionPermissions and *
+ * with a valid sessionID */
+
+ return reqp;
+}
+
+rbac_session_t *
+rbac_session_byid_fake( Operation *op, rbac_req_t *reqp )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ rbac_session_t *sessp = NULL;
+ int rc = LDAP_SUCCESS;
+ char fbuf[RBAC_BUFLEN];
+ struct berval filter = { sizeof(fbuf), fbuf };
+ SlapReply rs2 = { REP_RESULT };
+ Operation op2 = *op;
+ rbac_callback_info_t rbac_cb;
+ slap_callback cb = { 0 };
+ tenant_info_t *tenantp = NULL;
+
+ if ( !rbac_is_valid_session_id( &reqp->sessid ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_byid: "
+ "invalid session id (%s)\n",
+ reqp->sessid.bv_val );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ sessp = rbac_alloc_session();
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_byid: "
+ "unable to allocate session memory\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ tenantp = rbac_tid2tenant( &reqp->tenantid );
+
+ /* session id filter */
+ memset( fbuf, 0, sizeof(fbuf) );
+ strcpy( fbuf, RBAC_SESSION_RDN_EQ );
+ strncpy( &fbuf[0] + sizeof(RBAC_SESSION_RDN_EQ) - 1, reqp->sessid.bv_val,
+ reqp->sessid.bv_len );
+ filter.bv_val = fbuf;
+ filter.bv_len = strlen( fbuf );
+
+ //cb.sc_private = sessp;
+ //cb.sc_response = rbac_read_session_cb;
+ cb.sc_private = &rbac_cb;
+ cb.sc_response = rbac_sess_fake_cb;
+ op2.o_callback = &cb;
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2.o_dn = tenantp->session_admin;
+ op2.o_ndn = tenantp->session_admin;
+ op2.o_req_dn = tenantp->sessions_basedn;
+ op2.o_req_ndn = tenantp->sessions_basedn;
+ op2.ors_filterstr = filter;
+ op2.ors_filter = str2filter_x( &op2, filter.bv_val );
+ op2.ors_scope = LDAP_SCOPE_SUBTREE;
+ op2.ors_attrs = slap_rbac_schema.session_attrs;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+ op2.ors_slimit = SLAP_NO_LIMIT;
+ op2.o_bd = frontendDB;
+ // hyc change to fix seg fault:
+ op2.ors_limit = NULL;
+
+ rc = op2.o_bd->be_search( &op2, &rs2 );
+ filter_free_x( &op2, op2.ors_filter, 1 );
+
+done:
+ // TODO: find equivalent way of check nentries (broke with fake connection fix)
+ //if ( rc != LDAP_SUCCESS || rs2.sr_nentries <= 0 ) {
+ if ( rc != LDAP_SUCCESS ) {
+ rbac_free_session( sessp );
+ sessp = NULL;
+ }
+
+ return sessp;
+}
+
+rbac_session_t *
+rbac_session_byid( Operation *op, rbac_req_t *reqp )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ rbac_session_t *sessp = NULL;
+ int rc = LDAP_SUCCESS;
+ char fbuf[RBAC_BUFLEN];
+ struct berval filter = { sizeof(fbuf), fbuf };
+ SlapReply rs2 = { REP_RESULT };
+ Operation op2 = *op;
+ slap_callback cb = { 0 };
+ tenant_info_t *tenantp = NULL;
+
+ if ( !rbac_is_valid_session_id( &reqp->sessid ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_byid: "
+ "invalid session id (%s)\n",
+ reqp->sessid.bv_val );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ sessp = rbac_alloc_session();
+ if ( !sessp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_session_byid: "
+ "unable to allocate session memory\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ tenantp = rbac_tid2tenant( &reqp->tenantid );
+
+ /* session id filter */
+ memset( fbuf, 0, sizeof(fbuf) );
+ strcpy( fbuf, RBAC_SESSION_RDN_EQ );
+ strncpy( &fbuf[0] + sizeof(RBAC_SESSION_RDN_EQ) - 1, reqp->sessid.bv_val,
+ reqp->sessid.bv_len );
+ filter.bv_val = fbuf;
+ filter.bv_len = strlen( fbuf );
+
+ cb.sc_private = sessp;
+ cb.sc_response = rbac_read_session_cb;
+ op2.o_callback = &cb;
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2.o_dn = tenantp->session_admin;
+ op2.o_ndn = tenantp->session_admin;
+ op2.o_req_dn = tenantp->sessions_basedn;
+ op2.o_req_ndn = tenantp->sessions_basedn;
+ op2.ors_filterstr = filter;
+ op2.ors_filter = str2filter_x( &op2, filter.bv_val );
+ op2.ors_scope = LDAP_SCOPE_SUBTREE;
+ op2.ors_attrs = slap_rbac_schema.session_attrs;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+ op2.ors_slimit = SLAP_NO_LIMIT;
+ op2.o_bd = frontendDB;
+ // hyc change to fix seg fault:
+ op2.ors_limit = NULL;
+
+ rc = op2.o_bd->be_search( &op2, &rs2 );
+ filter_free_x( &op2, op2.ors_filter, 1 );
+
+done:
+ // TODO: find equivalent way of check nentries (broke with fake connection fix)
+ //if ( rc != LDAP_SUCCESS || rs2.sr_nentries <= 0 ) {
+ if ( rc != LDAP_SUCCESS ) {
+ rbac_free_session( sessp );
+ sessp = NULL;
+ }
+
+ return sessp;
+}
+
+static char *
+rbac_int_session_permissions_filterstr( Operation *op, rbac_session_t *sessp )
+{
+ char filterbuf[RBAC_BUFLEN];
+ int i;
+
+ memset( filterbuf, 0, sizeof(filterbuf) );
+
+ strcat( filterbuf, "(&(objectClass=ftOperation)(|" );
+ strcat( filterbuf, "(ftUsers=" );
+ strcat( filterbuf, sessp->uid.bv_val );
+ strcat( filterbuf, ")" );
+
+ /* add ftRoles filters */
+ for ( i = 0; !BER_BVISEMPTY( &sessp->roles[i] ); i++ ) {
+ strcat( filterbuf, "(ftRoles=" );
+ strncat( filterbuf, sessp->roles[i].bv_val, sessp->roles[i].bv_len );
+ strcat( filterbuf, ")" );
+ }
+ strcat( filterbuf, "))" );
+ return strdup( filterbuf );
+}
+
+int
+rbac_int_session_permissions(
+ Operation *op,
+ SlapReply *rs,
+ rbac_req_t *reqp,
+ rbac_session_t *sessp )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ tenant_info_t *tenantp = NULL;
+ int rc;
+ struct berval filter;
+ char *filterstr;
+ struct berval permndn = BER_BVNULL;
+ OperationBuffer opbuf;
+ Connection conn = { 0 };
+ SlapReply rs2 = { REP_RESULT };
+ Operation *op2;
+ slap_callback cb = { 0 };
+ char permbuf[1024];
+ session_perm_req_t sess_perm_req;
+#ifdef USE_NEW_THREAD_CONTEXT
+ void *thrctx = ldap_pvt_thread_pool_context();
+#else
+ void *thrctx = op->o_tmpmemctx;
+#endif
+
+ tenantp = rbac_tid2tenant( &reqp->tenantid );
+
+ /* construct session permissions dn */
+ memset( permbuf, 0, sizeof(permbuf) );
+ strcat( permbuf, "rbacSessid=" );
+ strncat( permbuf, sessp->sessid.bv_val, sessp->sessid.bv_len );
+ strcat( permbuf, ",dc=rbac" );
+ sess_perm_req.op = op;
+ sess_perm_req.rs = rs;
+ sess_perm_req.permdn.bv_val = permbuf;
+ sess_perm_req.permdn.bv_len = strlen( permbuf );
+ sess_perm_req.sessid = &reqp->sessid;
+ sess_perm_req.tenantp = tenantp;
+
+ filterstr = rbac_int_session_permissions_filterstr( op, sessp );
+ if ( !filterstr ) {
+ Debug( LDAP_DEBUG_ANY, "unable to construct filter for session permissions\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+ filter.bv_val = filterstr;
+ filter.bv_len = strlen( filterstr );
+
+ rc = dnNormalize(
+ 0, NULL, NULL, &tenantp->permissions_basedn, &permndn, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_read_permission: "
+ "unable to normalize permission DN\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+ op2 = &opbuf.ob_op;
+ //Operation op2 = *op;
+ cb.sc_private = &sess_perm_req;
+ cb.sc_response = rbac_session_permissions_cb;
+ op2->o_callback = &cb;
+ op2->o_tag = LDAP_REQ_SEARCH;
+ op2->o_dn = tenantp->admin;
+ op2->o_ndn = tenantp->admin;
+ op2->o_req_dn = tenantp->permissions_basedn;
+ op2->o_req_ndn = permndn;
+ op2->ors_filterstr = filter;
+ op2->ors_filter = str2filter_x( op, filter.bv_val );
+ op2->ors_scope = LDAP_SCOPE_SUB;
+ op2->ors_attrs = tenantp->schema->session_perm_attrs;
+ op2->ors_tlimit = SLAP_NO_LIMIT;
+ op2->ors_slimit = SLAP_NO_LIMIT;
+ op2->ors_attrsonly = 0;
+ op2->o_bd = frontendDB;
+ //op2.ors_limit = NULL;
+ rc = op2->o_bd->be_search( op2, &rs2 );
+ filter_free_x( op, op2->ors_filter, 1 );
+
+done:;
+ /* generate audit log */
+ rbac_audit( op, SessionPermissions, sessp, reqp, rc, (char *)rs->sr_text );
+
+ rs->sr_err = rc;
+ return rs->sr_err;
+}
+
+void
+rbac_free_session( rbac_session_t *sessp )
+{
+ if ( !sessp ) return;
+
+ if ( sessp->user ) {
+ rbac_free_user( sessp->user );
+ }
+
+ if ( !BER_BVISNULL( &sessp->uid ) ) {
+ ber_memfree( sessp->uid.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &sessp->tenantid ) ) {
+ ber_memfree( sessp->tenantid.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &sessp->userdn ) ) {
+ ber_memfree( sessp->userdn.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &sessp->sessdn ) ) {
+ ber_memfree( sessp->sessdn.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &sessp->message ) ) {
+ ber_memfree( sessp->message.bv_val );
+ }
+
+ if ( sessp->roles ) {
+ ber_bvarray_free( sessp->roles );
+ }
+
+ if ( sessp->role_constraints ) {
+ ber_bvarray_free( sessp->role_constraints );
+ }
+
+ ch_free( sessp );
+
+ return;
+}
+
+/* roles included from request are activated into a session only when
+ * they exist and have been assigned to the user. If no roles included in request, all
+ * roles assigned to the user are activated into the rbac session.
+ */
+int
+activate_session_roles(
+ rbac_session_t *sessp,
+ rbac_req_t *reqp,
+ rbac_user_t *userp )
+{
+ int i, j, rc = LDAP_UNWILLING_TO_PERFORM;
+ if ( !sessp || !reqp || !userp ) {
+ goto done;
+ }
+
+ /* no role requested, assign all roles from the user to the session. */
+ if ( reqp->roles == NULL || BER_BVISNULL( &reqp->roles[0] ) ) {
+ //if (!reqp->roles || BER_BVISNULL(&reqp->roles[0])) {
+ /* no roles assigned to the user */
+ if ( !userp->roles || BER_BVISNULL( &userp->roles[0] ) ) goto done;
+ for ( i = 0; !BER_BVISNULL( &userp->roles[i] ); i++ ) {
+ struct berval role;
+ ber_dupbv_x( &role, &userp->roles[i], NULL );
+ ber_bvarray_add( &sessp->roles, &role );
+ rc = LDAP_SUCCESS;
+ }
+
+ // TODO: smm 20141218 - make sure this is correct way to add constraints to user session.
+ for ( i = 0; !BER_BVISNULL( &userp->role_constraints[i] ); i++ ) {
+ struct berval roleconstraint;
+ ber_dupbv_x( &roleconstraint, &userp->role_constraints[i], NULL );
+ ber_bvarray_add( &sessp->role_constraints, &roleconstraint );
+ rc = LDAP_SUCCESS;
+ }
+
+ } else {
+ for ( i = 0; !BER_BVISNULL( &reqp->roles[i] ); i++ ) {
+ for ( j = 0; !BER_BVISNULL( &userp->roles[j] ); j++ ) {
+ if ( !ber_bvstrcasecmp( &reqp->roles[i], &userp->roles[j] ) ) {
+ /* requested role is assigned to the user */
+ struct berval role;
+ ber_dupbv_x( &role, &userp->roles[i], NULL );
+ ber_bvarray_add( &sessp->roles, &role );
+ rc = LDAP_SUCCESS;
+ }
+ }
+ }
+ }
+
+done:;
+ return rc;
+}
diff --git a/contrib/slapd-modules/rbac/rbacuser.c b/contrib/slapd-modules/rbac/rbacuser.c
new file mode 100644
index 0000000..59d3c01
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbacuser.c
@@ -0,0 +1,620 @@
+/* rbacuser.c - RBAC users */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+static int ppolicy_cid = -1;
+
+static rbac_user_t *
+rbac_alloc_user()
+{
+ rbac_user_t *userp = ch_calloc( 1, sizeof(rbac_user_t) );
+
+ BER_BVZERO( &userp->tenantid );
+ BER_BVZERO( &userp->uid );
+ BER_BVZERO( &userp->dn );
+ BER_BVZERO( &userp->password );
+ BER_BVZERO( &userp->constraints );
+ BER_BVZERO( &userp->msg );
+ userp->roles = NULL;
+ userp->role_constraints = NULL;
+
+ return userp;
+}
+
+static int
+rbac_read_user_cb( Operation *op, SlapReply *rs )
+{
+ rbac_callback_info_t *cbp = op->o_callback->sc_private;
+ rbac_ad_t *user_ads;
+ rbac_user_t *userp = NULL;
+ int rc = 0, i;
+
+ Debug( LDAP_DEBUG_ANY, "rbac_read_user_cb\n" );
+
+ if ( rs->sr_type != REP_SEARCH ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_read_user_cb: "
+ "sr_type != REP_SEARCH\n" );
+ return 0;
+ }
+
+ assert( cbp );
+
+ user_ads = cbp->tenantp->schema->user_ads;
+
+ userp = rbac_alloc_user();
+ if ( !userp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_read_user_cb: "
+ "rbac_alloc_user failed\n" );
+
+ goto done;
+ }
+
+ ber_dupbv( &userp->dn, &rs->sr_entry->e_name );
+
+ Debug( LDAP_DEBUG_ANY, "DEBUG rbac_read_user_cb (%s): "
+ "rc (%d)\n",
+ userp->dn.bv_val, rc );
+
+ for ( i = 0; !BER_BVISNULL( &user_ads[i].attr ); i++ ) {
+ Attribute *attr = NULL;
+
+ attr = attr_find( rs->sr_entry->e_attrs, *user_ads[i].ad );
+ if ( attr != NULL ) {
+ switch ( user_ads[i].type ) {
+ case RBAC_ROLE_ASSIGNMENT:
+ ber_bvarray_dup_x( &userp->roles, attr->a_nvals, NULL );
+ break;
+ case RBAC_ROLE_CONSTRAINTS:
+ ber_bvarray_dup_x(
+ &userp->role_constraints, attr->a_nvals, NULL );
+ break;
+ case RBAC_USER_CONSTRAINTS:
+ ber_dupbv_x( &userp->constraints, &attr->a_nvals[0], NULL );
+ break;
+ case RBAC_UID:
+ ber_dupbv_x( &userp->uid, &attr->a_nvals[0], NULL );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+done:;
+ cbp->private = userp;
+
+ return 0;
+}
+
+static int
+rbac_bind_cb( Operation *op, SlapReply *rs )
+{
+ rbac_user_t *ui = op->o_callback->sc_private;
+
+ LDAPControl *ctrl = ldap_control_find(
+ LDAP_CONTROL_PASSWORDPOLICYRESPONSE, rs->sr_ctrls, NULL );
+ if ( ctrl ) {
+ LDAP *ld;
+ ber_int_t expire, grace;
+ LDAPPasswordPolicyError error;
+
+ ldap_create( &ld );
+ if ( ld ) {
+ int rc = ldap_parse_passwordpolicy_control(
+ ld, ctrl, &expire, &grace, &error );
+ if ( rc == LDAP_SUCCESS ) {
+ ui->authz = RBAC_PASSWORD_GOOD;
+ if ( grace > 0 ) {
+ //ui->msg.bv_len = sprintf(ui->msg.bv_val,
+ // "Password expired; %d grace logins remaining",
+ // grace);
+ ui->authz = RBAC_BIND_NEW_AUTHTOK_REQD;
+ } else if ( error != PP_noError ) {
+ ber_str2bv( ldap_passwordpolicy_err2txt( error ), 0, 0,
+ &ui->msg );
+
+ switch ( error ) {
+ case PP_passwordExpired:
+ ui->authz = RBAC_PASSWORD_EXPIRATION_WARNING;
+
+ if ( expire >= 0 ) {
+ char *unit = "seconds";
+ if ( expire > 60 ) {
+ expire /= 60;
+ unit = "minutes";
+ }
+ if ( expire > 60 ) {
+ expire /= 60;
+ unit = "hours";
+ }
+ if ( expire > 24 ) {
+ expire /= 24;
+ unit = "days";
+ }
+#if 0 /* Who warns about expiration so far in advance? */
+ if (expire > 7) {
+ expire /= 7;
+ unit = "weeks";
+ }
+ if (expire > 4) {
+ expire /= 4;
+ unit = "months";
+ }
+ if (expire > 12) {
+ expire /= 12;
+ unit = "years";
+ }
+#endif
+ }
+
+ //rs->sr_err = ;
+ break;
+ case PP_accountLocked:
+ ui->authz = RBAC_ACCOUNT_LOCKED;
+ //rs->sr_err = ;
+ break;
+ case PP_changeAfterReset:
+ ui->authz = RBAC_CHANGE_AFTER_RESET;
+ rs->sr_err = LDAP_SUCCESS;
+ break;
+ case PP_passwordModNotAllowed:
+ ui->authz = RBAC_NO_MODIFICATIONS;
+ //rs->sr_err = ;
+ break;
+ case PP_mustSupplyOldPassword:
+ ui->authz = RBAC_MUST_SUPPLY_OLD;
+ //rs->sr_err = ;
+ break;
+ case PP_insufficientPasswordQuality:
+ ui->authz = RBAC_INSUFFICIENT_QUALITY;
+ //rs->sr_err = ;
+ break;
+ case PP_passwordTooShort:
+ ui->authz = RBAC_PASSWORD_TOO_SHORT;
+ //rs->sr_err = ;
+ break;
+ case PP_passwordTooYoung:
+ ui->authz = RBAC_PASSWORD_TOO_YOUNG;
+ //rs->sr_err = ;
+ break;
+ case PP_passwordInHistory:
+ ui->authz = RBAC_HISTORY_VIOLATION;
+ //rs->sr_err = ;
+ break;
+ case PP_noError:
+ default:
+ // do nothing
+ //ui->authz = RBAC_PASSWORD_GOOD;
+ rs->sr_err = LDAP_SUCCESS;
+ break;
+ }
+
+// switch (error) {
+// case PP_passwordExpired:
+ /* report this during authz */
+// rs->sr_err = LDAP_SUCCESS;
+ /* fallthru */
+// case PP_changeAfterReset:
+// ui->authz = RBAC_BIND_NEW_AUTHTOK_REQD;
+// }
+ }
+ }
+ ldap_unbind_ext( ld, NULL, NULL );
+ }
+ }
+
+ return 0;
+}
+
+/* exported user functions */
+int
+rbac_authenticate_user( Operation *op, rbac_user_t *userp )
+{
+ int rc = LDAP_SUCCESS;
+ slap_callback cb = { 0 };
+ SlapReply rs2 = { REP_RESULT };
+ Operation op2 = *op;
+ LDAPControl *sctrls[4];
+ LDAPControl sctrl[3];
+ int nsctrls = 0;
+ LDAPControl c;
+ struct berval ber_bvnull = BER_BVNULL;
+ struct berval dn, ndn;
+
+ rc = dnPrettyNormal( 0, &userp->dn, &dn, &ndn, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ cb.sc_response = rbac_bind_cb;
+ cb.sc_private = userp;
+ op2.o_callback = &cb;
+ op2.o_dn = ber_bvnull;
+ op2.o_ndn = ber_bvnull;
+ op2.o_tag = LDAP_REQ_BIND;
+ op2.o_protocol = LDAP_VERSION3;
+ op2.orb_method = LDAP_AUTH_SIMPLE;
+ op2.orb_cred = userp->password;
+ op2.o_req_dn = dn;
+ op2.o_req_ndn = ndn;
+
+ // loading the ldap pw policy controls loaded into here, added by smm:
+ c.ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST;
+ c.ldctl_value.bv_val = NULL;
+ c.ldctl_value.bv_len = 0;
+ c.ldctl_iscritical = 0;
+ sctrl[nsctrls] = c;
+ sctrls[nsctrls] = &sctrl[nsctrls];
+ sctrls[++nsctrls] = NULL;
+ op2.o_ctrls = sctrls;
+
+ if ( ppolicy_cid < 0 ) {
+ rc = slap_find_control_id( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
+ &ppolicy_cid );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ }
+ // smm - need to set the control flag too:
+ op2.o_ctrlflag[ppolicy_cid] = SLAP_CONTROL_CRITICAL;
+
+ slap_op_time( &op2.o_time, &op2.o_tincr );
+ op2.o_bd = frontendDB;
+ rc = op2.o_bd->be_bind( &op2, &rs2 );
+ if ( userp->authz > 0 ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_authenticate_user (%s): "
+ "password policy violation (%d)\n",
+ userp->dn.bv_val ? userp->dn.bv_val : "NULL", userp->authz );
+ }
+
+done:;
+ ch_free( dn.bv_val );
+ ch_free( ndn.bv_val );
+
+ Debug( LDAP_DEBUG_ANY, "rbac_authenticate_user (%s): "
+ "rc (%d)\n",
+ userp->dn.bv_val ? userp->dn.bv_val : "NULL", rc );
+ return rc;
+}
+
+/*
+ isvalidusername(): from OpenLDAP ~/contrib/slapd-modules/nssov/passwd.c
+ Checks to see if the specified name is a valid user name.
+
+ This test is based on the definition from POSIX (IEEE Std 1003.1, 2004, 3.426 User Name
+ and 3.276 Portable Filename Character Set):
+ http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_426
+ http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_276
+
+ The standard defines user names valid if they contain characters from
+ the set [A-Za-z0-9._-] where the hyphen should not be used as first
+ character. As an extension this test allows the dolar '$' sign as the last
+ character to support Samba special accounts.
+*/
+static int
+isvalidusername( struct berval *bv )
+{
+ int i;
+ char *name = bv->bv_val;
+ if ( (name == NULL) || ( name[0] == '\0' ) ) return 0;
+ /* check first character */
+ if ( !( ( name[0] >= 'A' && name[0] <= 'Z' ) ||
+ ( name[0] >= 'a' && name[0] <= 'z' ) ||
+ ( name[0] >= '0' && name[0] <= '9' ) || name[0] == '.' ||
+ name[0] == '_' ) )
+ return 0;
+ /* check other characters */
+ for ( i = 1; i < bv->bv_len; i++ ) {
+ if ( name[i] == '$' ) {
+ /* if the char is $ we require it to be the last char */
+ if ( name[i + 1] != '\0' ) return 0;
+ } else if ( !( ( name[i] >= 'A' && name[i] <= 'Z' ) ||
+ ( name[i] >= 'a' && name[i] <= 'z' ) ||
+ ( name[i] >= '0' && name[i] <= '9' ) ||
+ name[i] == '.' || name[i] == '_' ||
+ name[i] == '-' ) )
+ return 0;
+ }
+ /* no test failed so it must be good */
+ return -1;
+}
+
+rbac_user_t *
+rbac_read_user( Operation *op, rbac_req_t *reqp )
+{
+ int rc = LDAP_SUCCESS;
+ tenant_info_t *tenantp = rbac_tid2tenant( &reqp->tenantid );
+ rbac_user_t *userp = NULL;
+ char fbuf[RBAC_BUFLEN];
+ struct berval filter = { sizeof(fbuf), fbuf };
+ SlapReply rs2 = { REP_RESULT };
+ Operation op2 = *op;
+ slap_callback cb = { 0 };
+ rbac_callback_info_t rbac_cb;
+
+ if ( !tenantp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_read_user: "
+ "missing tenant information\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* uid is a pre-requisite for reading the user information */
+ if ( BER_BVISNULL( &reqp->uid ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_read_user: "
+ "missing uid, unable to read user entry\n" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ if ( !isvalidusername( &reqp->uid ) ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_read_user: "
+ "invalid user id\n" );
+ rc = LDAP_NO_SUCH_OBJECT;
+ goto done;
+ }
+
+ rbac_cb.tenantp = tenantp;
+ rbac_cb.private = NULL;
+
+ memset( fbuf, 0, sizeof(fbuf) );
+ strcpy( fbuf, "uid=" );
+ strncat( fbuf, reqp->uid.bv_val, reqp->uid.bv_len );
+ filter.bv_val = fbuf;
+ filter.bv_len = strlen( fbuf );
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_create_session: "
+ "invalid DN syntax\n" );
+ goto done;
+ }
+
+ cb.sc_private = &rbac_cb;
+ cb.sc_response = rbac_read_user_cb;
+ op2.o_callback = &cb;
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2.o_dn = tenantp->admin;
+ op2.o_ndn = tenantp->admin;
+ op2.o_req_dn = tenantp->users_basedn;
+ op2.o_req_ndn = tenantp->users_basedn;
+ op2.ors_filterstr = filter;
+ op2.ors_filter = str2filter_x( &op2, filter.bv_val );
+ op2.ors_scope = LDAP_SCOPE_SUBTREE;
+ op2.ors_attrs = tenantp->schema->user_attrs;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+ op2.ors_slimit = SLAP_NO_LIMIT;
+ op2.ors_attrsonly = 0;
+ op2.o_bd = frontendDB;
+ op2.ors_limit = NULL;
+ rc = op2.o_bd->be_search( &op2, &rs2 );
+ filter_free_x( &op2, op2.ors_filter, 1 );
+
+done:;
+ if ( rc == LDAP_SUCCESS && rbac_cb.private ) {
+ userp = (rbac_user_t *)rbac_cb.private;
+ if ( !BER_BVISNULL( &reqp->authtok ) )
+ ber_dupbv( &userp->password, &reqp->authtok );
+ rbac_cb.private = NULL;
+ return userp;
+ } else {
+ userp = (rbac_user_t *)rbac_cb.private;
+ rbac_free_user( userp );
+ return NULL;
+ }
+}
+
+/* evaluate temporal constraints for the user */
+int
+rbac_user_temporal_constraint( rbac_user_t *userp )
+{
+ int rc = LDAP_SUCCESS;
+ rbac_constraint_t *cp = NULL;
+
+ if ( BER_BVISNULL( &userp->constraints ) ) {
+ /* no temporal constraint */
+ goto done;
+ }
+
+ cp = rbac_bv2constraint( &userp->constraints );
+ if ( !cp ) {
+ Debug( LDAP_DEBUG_ANY, "rbac_user_temporal_constraint: "
+ "invalid user constraint \n" );
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ rc = rbac_check_time_constraint( cp );
+
+done:;
+ rbac_free_constraint( cp );
+
+ return rc;
+}
+
+/*
+rbac_constraint_t *
+rbac_user_role_constraintsx(rbac_user_t *userp)
+{
+ rbac_constraint_t *tmp, *cp = NULL;
+ int i = 0;
+
+ if (!userp || !userp->role_constraints)
+ goto done;
+
+ while (!BER_BVISNULL(&userp->role_constraints[i])) {
+ tmp = rbac_bv2constraint(&userp->role_constraints[i++]);
+ if (tmp) {
+ if (!cp) {
+ cp = tmp;
+ } else {
+ tmp->next = cp;
+ cp = tmp;
+ }
+ }
+ }
+
+done:;
+ return cp;
+}
+*/
+
+rbac_constraint_t *
+rbac_user_role_constraints( BerVarray values )
+{
+ rbac_constraint_t *curr, *head = NULL;
+ int i = 0;
+
+ if ( values ) {
+ while ( !BER_BVISNULL( &values[i] ) ) {
+ curr = rbac_bv2constraint( &values[i++] );
+ if ( curr ) {
+ curr->next = head;
+ head = curr;
+ }
+ }
+ }
+
+ return head;
+}
+
+/*
+
+void main() {
+ item * curr, * head;
+ int i;
+
+ head = NULL;
+
+ for(i=1;i<=10;i++) {
+ curr = (item *)malloc(sizeof(item));
+ curr->val = i;
+ curr->next = head;
+ head = curr;
+ }
+
+ curr = head;
+
+ while(curr) {
+ printf("%d\n", curr->val);
+ curr = curr->next ;
+ }
+}
+
+ */
+
+/*
+ *
+rbac_user_role_constraints2(BerVarray values)
+{
+ rbac_constraint_t *tmp, *cp = NULL;
+ int i = 0;
+
+ if (!values)
+ goto done;
+
+ while (!BER_BVISNULL(&values[i])) {
+ tmp = rbac_bv2constraint(&values[i++]);
+ if (tmp) {
+ if (!cp) {
+ cp = tmp;
+ } else {
+ tmp->next = cp;
+ cp = tmp;
+ //cp->next = tmp;
+ //cp = tmp->next;
+
+ }
+ }
+ }
+
+done:;
+ return cp;
+}
+
+
+rbac_user_role_constraints3(rbac_constraint_t *values)
+{
+ rbac_constraint_t *tmp, *cp = NULL;
+ int i = 0;
+
+ if (!values)
+ goto done;
+
+ while (!BER_BVISNULL(values[i])) {
+ tmp = rbac_bv2constraint(&values[i++]);
+ if (tmp) {
+ if (!cp) {
+ cp = tmp;
+ } else {
+ tmp->next = cp;
+ cp = tmp;
+ }
+ }
+ }
+
+done:;
+ return cp;
+}
+*/
+
+void
+rbac_free_user( rbac_user_t *userp )
+{
+ if ( !userp ) return;
+
+ if ( !BER_BVISNULL( &userp->tenantid ) ) {
+ ber_memfree( userp->tenantid.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &userp->uid ) ) {
+ ber_memfree( userp->uid.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &userp->dn ) ) {
+ ber_memfree( userp->dn.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &userp->constraints ) ) {
+ ber_memfree( userp->constraints.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &userp->password ) ) {
+ ber_memfree( userp->password.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &userp->msg ) ) {
+ ber_memfree( userp->msg.bv_val );
+ }
+
+ if ( userp->roles ) ber_bvarray_free( userp->roles );
+
+ if ( userp->role_constraints ) ber_bvarray_free( userp->role_constraints );
+
+ ch_free( userp );
+}
diff --git a/contrib/slapd-modules/rbac/slapo-rbac.5 b/contrib/slapd-modules/rbac/slapo-rbac.5
new file mode 100644
index 0000000..453bcbc
--- /dev/null
+++ b/contrib/slapd-modules/rbac/slapo-rbac.5
@@ -0,0 +1,157 @@
+.TH SLAPO_RBAC 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1999-2021 SYMAS Corporation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-rbac \- RBAC0 overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+.LP
+The
+.B slapo-rbac
+overlay
+is an implementation of the ANSI INCITS 359 Role-Based Access Control (RBAC) Core.
+When instantiated, it intercepts, decodes and enforces specific RBAC policies per the Apache Fortress RBAC data formats.
+.P
+The overlay provides a set of extended operations.
+They include session create/delete, checkAccess, addActiveRole, dropActiveRole and sessionRoles.
+.P
+
+.SH CONFIGURATION
+These
+.B slapd.conf
+configuration options apply to the slapo-rbac overlay.
+
+.TP
+.B overlay rbac
+This tag gets applied to the RBAC configuration db (see example below).
+.TP
+.B rbac-default-users-base-dn "ou=People,dc=example,dc=com"
+Points to the container that contains the Apache Fortress users.
+.TP
+.B rbac-default-roles-base-dn "ou=Roles,ou=RBAC,dc=example,dc=com"
+Points to the container that contains the Apache Fortress roles.
+.TP
+.B rbac-default-permissions-base-dn "ou=Permissions,ou=RBAC,dc=example,dc=com"
+Points to the container that contains the Apache Fortress perms.
+.TP
+.B rbac-default-sessions-base-dn "cn=rbac"
+Points to the suffix of the RBAC sessions.
+.TP
+.B rbac-default-audit-base-dn "cn=audit"
+Points to the suffix where the audit records are stored.
+.TP
+.B rbac-admin "cn=manager,dc=example,dc=com"
+A service account that has read access to the entire Apache Fortress DIT.
+.TP
+.B rbac-pwd "{SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU"
+The password according to the service account.
+.TP
+.B rbac-session-admin "cn=manager,cn=rbac"
+The root dn of the RBAC sessions database.
+.TP
+.B rbac-session-admin-pwd {SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU
+The password corresponding with the session database.
+.TP
+.RE
+
+.SH EXAMPLES
+.LP
+.RS
+.nf
+
+This overlay requires the
+.B rbac.schema
+loaded and three additional database config sections, one to store rbac
+sessions, second to store the audit records and third to hold the overlay's
+config parameters. They should appear after the existing Apache Fortress db
+config.
+
+.TP
+1. Session Database: Used to store the RBAC sessions corresponding to a logged in user.
+.B database mdb
+.B suffix "cn=rbac"
+.B rootdn "cn=manager,cn=rbac"
+.B rootpw {SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU
+.B index rbacSessid eq
+.B directory "/var/openldap/rbacsess"
+.B overlay dds
+.B dds-default-ttl 3600
+.B dds-max-dynamicObjects 100000
+.B dbnosync
+.B checkpoint 64 5
+.PP
+
+.TP
+2. Audit Database: Stores records that track user's activities.
+.B database mdb
+.B suffix "cn=audit"
+.B rootdn "cn=manager,cn=audit"
+.B rootpw {SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU
+.B directory "/var/openldap/rbacaudit"
+.B dbnosync
+.B checkpoint 64 5
+
+.PP
+
+.TP
+3. Config Database: Stores the parameters needed for this overlay to work.
+.B database mdb
+.B suffix "dc=rbac"
+.B rootdn "cn=manager,dc=rbac"
+.B rootpw {SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU
+.B directory "/var/openldap/rbacoverlay"
+.B overlay rbac
+.B rbac-default-tenant-id "example"
+.B rbac-default-users-base-dn "ou=People,dc=example,dc=com"
+.B rbac-default-roles-base-dn "ou=Roles,ou=RBAC,dc=example,dc=com"
+.B rbac-default-permissions-base-dn "ou=Permissions,ou=RBAC,dc=example,dc=com"
+.B rbac-default-sessions-base-dn "cn=rbac"
+.B rbac-default-audit-base-dn "cn=audit"
+.B rbac-admin "cn=manager,dc=example,dc=com"
+.B rbac-pwd "{SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU"
+.B rbac-session-admin "cn=manager,cn=rbac"
+.B rbac-session-admin-pwd {SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU
+
+.fi
+.RE
+
+.SH SEE ALSO
+.BR ldap (3),
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapo\-chain (5).
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+
+.BR ldap (3),
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapo\-chain (5).
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+
+.UR https://profsandhu.com/journals/tissec/ANSI+INCITS+359-2004.pdf
+.UE ANSI INCITS 359 Role-Based Access Control specification
+
+.UR https://github.com/apache/directory-fortress-core/blob/master/README.md
+.UE Apache Fortress README
+
+.UR https://github.com/apache/directory-fortress-core/blob/master/README-QUICKSTART-SLAPD.md
+.UE Apache Fortress OpenLDAP Quickstart
+
+.UR https://github.com/apache/directory-fortress-core/blob/master/ldap/schema/fortress.schema
+.UE Apache Fortress RBAC schema
+
+.SH BUGS
+This overlay is experimental.
+
+.SH ACKNOWLEDGEMENTS
+.P
+This module was written in 2013 by Ted Cheng of Symas Corporation
+with a little help from Matt Hardin, Howard Chu, Shawn McKinney.
+.P
+.so ../Project
diff --git a/contrib/slapd-modules/rbac/util.c b/contrib/slapd-modules/rbac/util.c
new file mode 100644
index 0000000..11a5e54
--- /dev/null
+++ b/contrib/slapd-modules/rbac/util.c
@@ -0,0 +1,531 @@
+/* util.c - RBAC utility */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+#define DELIMITER '$'
+
+#define SUNDAY 0x01
+#define MONDAY 0x02
+#define TUESDAY 0x04
+#define WEDNESDAY 0x08
+#define THURSDAY 0x10
+#define FRIDAY 0x20
+#define SATURDAY 0x40
+
+#define ALL_WEEK "all"
+
+void
+rbac_free_constraint( rbac_constraint_t *cp )
+{
+ if ( !cp ) return;
+
+ if ( !BER_BVISNULL( &cp->name ) ) {
+ ch_free( cp->name.bv_val );
+ }
+
+ ch_free( cp );
+}
+
+void
+rbac_free_constraints( rbac_constraint_t *constraints )
+{
+ rbac_constraint_t *cp, *tmp;
+
+ if ( !constraints ) return;
+
+ tmp = constraints;
+ while ( tmp ) {
+ cp = tmp->next;
+ rbac_free_constraint( tmp );
+ tmp = cp;
+ }
+
+ return;
+}
+
+rbac_constraint_t *
+rbac_alloc_constraint()
+{
+ rbac_constraint_t *cp = NULL;
+
+ cp = ch_calloc( 1, sizeof(rbac_constraint_t) );
+ return cp;
+}
+
+static int
+is_well_formed_constraint( struct berval *bv )
+{
+ int rc = LDAP_SUCCESS;
+
+ /* assume well-formed role/user-constraints, for the moment */
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "is_well_formed_constraint: "
+ "rbac role/user constraint not well-formed: %s\n",
+ bv->bv_val );
+ }
+
+ return rc;
+}
+
+/* input contains 4 digits, representing time */
+/* in hhmm format */
+static int
+constraint_parse_time( char *input )
+{
+ int btime;
+ char *ptr = input;
+
+ btime = ( *ptr++ - '0' ) * 12;
+ btime += ( *ptr++ - '0' );
+ btime *= 60; /* turning into mins */
+ btime += ( *ptr++ - '0' ) * 10;
+ btime += ( *ptr++ - '0' );
+ btime *= 60; /* turning into secs */
+
+ return btime;
+}
+
+/* input contains 4 digits, representing year */
+/* in yyyy format */
+static int
+constraint_parse_year( char *input )
+{
+ int i;
+ int year = 0;
+ char *ptr = input;
+
+ for ( i = 0; i <= 3; i++, ptr++ ) {
+ year = year * 10 + *ptr - '0';
+ }
+
+ return year;
+}
+
+/* input contains 2 digits, representing month */
+/* in mm format */
+static int
+constraint_parse_month( char *input )
+{
+ int i;
+ int month = 0;
+ char *ptr = input;
+
+ for ( i = 0; i < 2; i++, ptr++ ) {
+ month = month * 10 + *ptr - '0';
+ }
+
+ return month;
+}
+
+/* input contains 2 digits, representing day in month */
+/* in dd format */
+static int
+constraint_parse_day_in_month( char *input )
+{
+ int i;
+ int day_in_month = 0;
+ char *ptr = input;
+
+ for ( i = 0; i < 2; i++, ptr++ ) {
+ day_in_month = day_in_month * 10 + *ptr - '0';
+ }
+
+ return day_in_month;
+}
+
+rbac_constraint_t *
+rbac_bv2constraint( struct berval *bv )
+{
+ rbac_constraint_t *cp = NULL;
+ int rc = LDAP_SUCCESS;
+ char *ptr, *endp = NULL;
+ int len = 0;
+ int year, month, mday;
+
+ if ( !bv || BER_BVISNULL( bv ) ) goto done;
+
+ rc = is_well_formed_constraint( bv );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ cp = rbac_alloc_constraint();
+ if ( !cp ) {
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* constraint name */
+ ptr = bv->bv_val;
+ endp = ptr;
+ while ( *endp != DELIMITER ) {
+ endp++;
+ len++;
+ }
+
+ if ( len > 0 ) {
+ cp->name.bv_val = ch_malloc( len + 1 );
+ strncpy( cp->name.bv_val, ptr, len );
+ cp->name.bv_val[len] = '\0';
+ cp->name.bv_len = len;
+ } else {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ /* allowed inactivity period */
+ ptr = endp;
+ endp++;
+ if ( isdigit( *endp ) ) {
+ int secs = 0;
+ while ( isdigit( *endp ) ) {
+ secs = secs * 10 + *endp - '0';
+ endp++;
+ }
+ cp->allowed_inactivity = secs;
+ } else if ( *endp != DELIMITER ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ ptr = endp;
+ endp = ptr + 1;
+
+ /* begin time */
+ if ( isdigit( *endp ) ) {
+ cp->begin_time = constraint_parse_time( endp );
+ while ( isdigit( *endp ) )
+ endp++;
+ }
+
+ ptr = endp;
+ while ( *ptr != DELIMITER )
+ ptr++;
+ endp = ptr + 1;
+
+ /* end time */
+ if ( isdigit( *endp ) ) {
+ cp->end_time = constraint_parse_time( endp );
+ while ( isdigit( *endp ) )
+ endp++;
+ }
+
+ ptr = endp;
+ while ( *ptr != DELIMITER )
+ ptr++;
+ endp = ptr + 1;
+
+ /* begin year/month/day_in_month */
+ if ( isdigit( *endp ) ) {
+ lutil_tm tm;
+ year = constraint_parse_year( endp );
+ endp += 4;
+ month = constraint_parse_month( endp );
+ endp += 2;
+ mday = constraint_parse_day_in_month( endp );
+ endp += 2;
+
+ tm.tm_year = year - 1900;
+ tm.tm_mon = month - 1;
+ tm.tm_mday = mday;
+ tm.tm_sec = 0;
+ tm.tm_min = 0;
+ tm.tm_hour = 0;
+
+ lutil_tm2time( &tm, &cp->begin_date );
+ }
+
+ ptr = endp;
+ while ( *ptr != DELIMITER )
+ ptr++;
+ endp = ptr + 1;
+
+ /* end year/month/day_in_month */
+ if ( isdigit( *endp ) ) {
+ lutil_tm tm;
+ year = constraint_parse_year( endp );
+ endp += 4;
+ month = constraint_parse_month( endp );
+ endp += 2;
+ mday = constraint_parse_day_in_month( endp );
+ endp += 2;
+
+ tm.tm_year = year - 1900;
+ tm.tm_mon = month - 1;
+ tm.tm_mday = mday;
+ tm.tm_sec = 0;
+ tm.tm_min = 0;
+ tm.tm_hour = 0;
+
+ lutil_tm2time( &tm, &cp->end_date );
+ }
+
+ ptr = endp;
+ while ( *ptr != DELIMITER )
+ ptr++;
+ endp = ptr + 1;
+
+ /* begin lock year/month/day_in_month */
+ if ( isdigit( *endp ) ) {
+ lutil_tm tm;
+ year = constraint_parse_year( endp );
+ endp += 4;
+ month = constraint_parse_month( endp );
+ endp += 2;
+ mday = constraint_parse_day_in_month( endp );
+ endp += 2;
+
+ tm.tm_year = year - 1900;
+ tm.tm_mon = month - 1;
+ tm.tm_mday = mday;
+ tm.tm_sec = 0;
+ tm.tm_min = 0;
+ tm.tm_hour = 0;
+
+ lutil_tm2time( &tm, &cp->begin_lock_date );
+ }
+
+ ptr = endp;
+ while ( *ptr != DELIMITER )
+ ptr++;
+ endp = ptr + 1;
+
+ /* end lock year/month/day_in_month */
+ if ( isdigit( *endp ) ) {
+ lutil_tm tm;
+
+ year = constraint_parse_year( endp );
+ endp += 4;
+ month = constraint_parse_month( endp );
+ endp += 2;
+ mday = constraint_parse_day_in_month( endp );
+ endp += 2;
+
+ tm.tm_year = year - 1900;
+ tm.tm_mon = month - 1;
+ tm.tm_mday = mday;
+ tm.tm_sec = 0;
+ tm.tm_min = 0;
+ tm.tm_hour = 0;
+
+ lutil_tm2time( &tm, &cp->end_lock_date );
+ }
+
+ ptr = endp;
+ while ( *ptr != DELIMITER )
+ ptr++;
+ endp = ptr + 1;
+
+ /* dayMask */
+
+ /* allow "all" to mean the entire week */
+ if ( strncasecmp( endp, ALL_WEEK, strlen( ALL_WEEK ) ) == 0 ) {
+ cp->day_mask = SUNDAY | MONDAY | TUESDAY | WEDNESDAY | THURSDAY |
+ FRIDAY | SATURDAY;
+ }
+
+ while ( *endp && isdigit( *endp ) ) {
+ switch ( *endp - '0' ) {
+ case 1:
+ cp->day_mask |= SUNDAY;
+ break;
+ case 2:
+ cp->day_mask |= MONDAY;
+ break;
+ case 3:
+ cp->day_mask |= TUESDAY;
+ break;
+ case 4:
+ cp->day_mask |= WEDNESDAY;
+ break;
+ case 5:
+ cp->day_mask |= THURSDAY;
+ break;
+ case 6:
+ cp->day_mask |= FRIDAY;
+ break;
+ case 7:
+ cp->day_mask |= SATURDAY;
+ break;
+ default:
+ /* should not be here */
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ endp++;
+ }
+
+done:;
+ if ( rc != LDAP_SUCCESS ) {
+ rbac_free_constraint( cp );
+ cp = NULL;
+ }
+
+ return cp;
+}
+
+static int
+constraint_day_of_week( rbac_constraint_t *cp, int wday )
+{
+ int rc = LDAP_UNWILLING_TO_PERFORM;
+
+ /* assumption: Monday is 1st day of a week */
+ switch ( wday ) {
+ case 1:
+ if ( !(cp->day_mask & MONDAY) ) goto done;
+ break;
+ case 2:
+ if ( !(cp->day_mask & TUESDAY) ) goto done;
+ break;
+ case 3:
+ if ( !(cp->day_mask & WEDNESDAY) ) goto done;
+ break;
+ case 4:
+ if ( !(cp->day_mask & THURSDAY) ) goto done;
+ break;
+ case 5:
+ if ( !(cp->day_mask & FRIDAY) ) goto done;
+ break;
+ case 6:
+ if ( !(cp->day_mask & SATURDAY) ) goto done;
+ break;
+ case 0:
+ case 7:
+ if ( !(cp->day_mask & SUNDAY) ) goto done;
+ break;
+ default:
+ /* should not be here */
+ goto done;
+ }
+
+ rc = LDAP_SUCCESS;
+
+done:;
+ return rc;
+}
+
+int
+rbac_check_time_constraint( rbac_constraint_t *cp )
+{
+ int rc = LDAP_UNWILLING_TO_PERFORM;
+ time_t now;
+ struct tm result, *resultp;
+
+ now = slap_get_time();
+
+ /*
+ * does slapd support day-of-week (wday)?
+ * using native routine for now.
+ * Win32's gmtime call is already thread-safe, to the _r
+ * decorator is unneeded.
+ */
+#ifdef _WIN32
+ resultp = gmtime( &now );
+#else
+ resultp = gmtime_r( &now, &result );
+#endif
+ if ( !resultp ) goto done;
+#if 0
+ timestamp.bv_val = timebuf;
+ timestamp.bv_len = sizeof(timebuf);
+ slap_timestamp(&now, &timestamp);
+ lutil_parsetime(timestamp.bv_val, &now_tm);
+ lutil_tm2time(&now_tm, &now_tt);
+#endif
+
+ if ( ( cp->begin_date.tt_sec > 0 && cp->begin_date.tt_sec > now ) ||
+ ( cp->end_date.tt_sec > 0 && cp->end_date.tt_sec < now ) ) {
+ /* not within allowed time period */
+ goto done;
+ }
+
+ /* allowed time period during a day */
+ if ( cp->begin_time > 0 && cp->end_time > 0 ) {
+ int timeofday = ( resultp->tm_hour * 60 + resultp->tm_min ) * 60 +
+ resultp->tm_sec;
+ if ( timeofday < cp->begin_time || timeofday > cp->end_time ) {
+ /* not within allowed time period in a day */
+ goto done;
+ }
+ }
+
+ /* allowed day in a week */
+ if ( cp->day_mask > 0 ) {
+ rc = constraint_day_of_week( cp, resultp->tm_wday );
+ if ( rc != LDAP_SUCCESS ) goto done;
+ }
+
+ /* during lock-out period? */
+ if ( ( cp->begin_lock_date.tt_sec > 0 &&
+ cp->begin_lock_date.tt_sec < now ) &&
+ ( cp->end_lock_date.tt_sec > 0 &&
+ cp->end_lock_date.tt_sec > now ) ) {
+ /* within locked out period */
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* passed all tests */
+ rc = LDAP_SUCCESS;
+
+done:;
+ return rc;
+}
+
+rbac_constraint_t *
+rbac_role2constraint( struct berval *role, rbac_constraint_t *role_constraints )
+{
+ rbac_constraint_t *cp = NULL;
+
+ if ( !role_constraints || !role ) goto done;
+
+ cp = role_constraints;
+ while ( cp ) {
+ if ( ber_bvstrcasecmp( role, &cp->name ) == 0 ) {
+ /* found the role constraint */
+ goto done;
+ }
+ cp = cp->next;
+ }
+
+done:;
+ return cp;
+}
+
+void
+rbac_to_lower( struct berval *bv )
+{
+ // convert the berval to lower case:
+ int i;
+ for ( i = 0; i < bv->bv_len; i++ ) {
+ bv->bv_val[i] = tolower( bv->bv_val[i] );
+ }
+}
diff --git a/contrib/slapd-modules/samba4/Makefile b/contrib/slapd-modules/samba4/Makefile
new file mode 100644
index 0000000..f53d130
--- /dev/null
+++ b/contrib/slapd-modules/samba4/Makefile
@@ -0,0 +1,68 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+# Copyright 2004 Howard Chu, Symas Corp. All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted only as authorized by the OpenLDAP
+# Public License.
+#
+# A copy of this license is available in the file LICENSE in the
+# top-level directory of the distribution or, alternatively, at
+# <http://www.OpenLDAP.org/license.html>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_RDNVAL=SLAPD_MOD_DYNAMIC \
+ -DSLAPD_OVER_PGUID=SLAPD_MOD_DYNAMIC \
+ -DSLAPD_OVER_VERNUM=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = pguid.la rdnval.la vernum.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+pguid.la: pguid.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+rdnval.la: rdnval.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+vernum.la: vernum.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/samba4/README b/contrib/slapd-modules/samba4/README
new file mode 100644
index 0000000..65745b1
--- /dev/null
+++ b/contrib/slapd-modules/samba4/README
@@ -0,0 +1,72 @@
+# $OpenLDAP$
+
+This directory contains slapd overlays specific to samba4 LDAP backend:
+
+ - pguid (not used)
+ - rdnval (under evaluation)
+ - vernum (under evaluation)
+
+
+ - PGUID
+
+This overlay maintains the operational attribute "parentUUID". It contains
+the entryUUID of the parent entry. This overlay is not being considered
+right now.
+
+
+ - RDNVAL
+
+This overlay maintains the operational attribute "rdnValue". It contains
+the value of the entry's RDN. This attribute is defined by the overlay
+itself as
+
+ ( 1.3.6.1.4.1.4203.666.1.58
+ NAME 'rdnValue'
+ DESC 'the value of the naming attributes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ EQUALITY caseIgnoreMatch
+ USAGE dSAOperation
+ NO-USER-MODIFICATION )
+
+under OpenLDAP's development OID arc. This OID is temporary.
+
+To use the overlay, add:
+
+ moduleload <path to>rdnval.so
+ ...
+
+ database <whatever>
+ ...
+ overlay rdnval
+
+to your slapd configuration file. An instance is required for each database
+that needs to maintain this attribute.
+
+
+ - VERNUM
+
+This overlay increments a counter any time an attribute is modified.
+It is intended to increment the counter 'msDS-KeyVersionNumber' when
+the attribute 'unicodePwd' is modified.
+
+
+These overlays are only set up to be built as a dynamically loaded modules.
+On most platforms, in order for the modules to be usable, all of the
+library dependencies must also be available as shared libraries.
+
+If you need to build the overlays statically, you will have to move them
+into the slapd/overlays directory and edit the Makefile and overlays.c
+to reference them.
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+Copyright 2009-2022 The OpenLDAP Foundation.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
diff --git a/contrib/slapd-modules/samba4/pguid.c b/contrib/slapd-modules/samba4/pguid.c
new file mode 100644
index 0000000..4b0b066
--- /dev/null
+++ b/contrib/slapd-modules/samba4/pguid.c
@@ -0,0 +1,460 @@
+/* pguid.c - Parent GUID value overlay */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati
+ * for inclusion in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_PGUID
+
+#include <stdio.h>
+
+#include "ac/string.h"
+#include "ac/socket.h"
+
+#include "slap.h"
+#include "slap-config.h"
+
+#include "lutil.h"
+
+/*
+ * Maintain an attribute (parentUUID) that contains the value
+ * of the entryUUID of the parent entry (used by Samba4)
+ */
+
+static AttributeDescription *ad_parentUUID;
+
+static slap_overinst pguid;
+
+static int
+pguid_op_add( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+
+ struct berval pdn, pndn;
+ Entry *e = NULL;
+ Attribute *a;
+ int rc;
+
+ /* don't care about suffix entry */
+ if ( dn_match( &op->o_req_ndn, &op->o_bd->be_nsuffix[0] ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ dnParent( &op->o_req_dn, &pdn );
+ dnParent( &op->o_req_ndn, &pndn );
+
+ rc = overlay_entry_get_ov( op, &pndn, NULL, slap_schema.si_ad_entryUUID, 0, &e, on );
+ if ( rc != LDAP_SUCCESS || e == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: pguid_op_add: unable to get parent entry DN=\"%s\" (%d)\n",
+ op->o_log_prefix, pdn.bv_val, rc );
+ return SLAP_CB_CONTINUE;
+ }
+
+ a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
+ if ( a == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: pguid_op_add: unable to find entryUUID of parent entry DN=\"%s\" (%d)\n",
+ op->o_log_prefix, pdn.bv_val, rc );
+
+ } else {
+ assert( a->a_numvals == 1 );
+
+ if ( op->ora_e != NULL ) {
+ attr_merge_one( op->ora_e, ad_parentUUID, &a->a_vals[0], a->a_nvals == a->a_vals ? NULL : &a->a_nvals[0] );
+
+ } else {
+ Modifications *ml;
+ Modifications *mod;
+
+ assert( op->ora_modlist != NULL );
+
+ for ( ml = op->ora_modlist; ml != NULL; ml = ml->sml_next ) {
+ if ( ml->sml_mod.sm_desc == slap_schema.si_ad_entryUUID ) {
+ break;
+ }
+ }
+
+ if ( ml == NULL ) {
+ ml = op->ora_modlist;
+ }
+
+ mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+ mod->sml_flags = SLAP_MOD_INTERNAL;
+ mod->sml_op = LDAP_MOD_ADD;
+ mod->sml_desc = ad_parentUUID;
+ mod->sml_type = ad_parentUUID->ad_cname;
+ mod->sml_values = ch_malloc( sizeof( struct berval ) * 2 );
+ mod->sml_nvalues = NULL;
+ mod->sml_numvals = 1;
+
+ ber_dupbv( &mod->sml_values[0], &a->a_vals[0] );
+ BER_BVZERO( &mod->sml_values[1] );
+
+ mod->sml_next = ml->sml_next;
+ ml->sml_next = mod;
+ }
+ }
+
+ if ( e != NULL ) {
+ (void)overlay_entry_release_ov( op, e, 0, on );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+pguid_op_rename( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+
+ Entry *e = NULL;
+ Attribute *a;
+ int rc;
+
+ if ( op->orr_nnewSup == NULL ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ rc = overlay_entry_get_ov( op, op->orr_nnewSup, NULL, slap_schema.si_ad_entryUUID, 0, &e, on );
+ if ( rc != LDAP_SUCCESS || e == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: pguid_op_rename: unable to get newSuperior entry DN=\"%s\" (%d)\n",
+ op->o_log_prefix, op->orr_newSup->bv_val, rc );
+ return SLAP_CB_CONTINUE;
+ }
+
+ a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
+ if ( a == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: pguid_op_rename: unable to find entryUUID of newSuperior entry DN=\"%s\" (%d)\n",
+ op->o_log_prefix, op->orr_newSup->bv_val, rc );
+
+ } else {
+ Modifications *mod;
+
+ assert( a->a_numvals == 1 );
+
+ mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+ mod->sml_flags = SLAP_MOD_INTERNAL;
+ mod->sml_op = LDAP_MOD_REPLACE;
+ mod->sml_desc = ad_parentUUID;
+ mod->sml_type = ad_parentUUID->ad_cname;
+ mod->sml_values = ch_malloc( sizeof( struct berval ) * 2 );
+ mod->sml_nvalues = NULL;
+ mod->sml_numvals = 1;
+
+ ber_dupbv( &mod->sml_values[0], &a->a_vals[0] );
+ BER_BVZERO( &mod->sml_values[1] );
+
+ mod->sml_next = op->orr_modlist;
+ op->orr_modlist = mod;
+ }
+
+ if ( e != NULL ) {
+ (void)overlay_entry_release_ov( op, e, 0, on );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+pguid_db_init(
+ BackendDB *be,
+ ConfigReply *cr)
+{
+ if ( SLAP_ISGLOBALOVERLAY( be ) ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "pguid_db_init: pguid cannot be used as global overlay.\n" );
+ return 1;
+ }
+
+ if ( be->be_nsuffix == NULL ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "pguid_db_init: database must have suffix\n" );
+ return 1;
+ }
+
+ if ( BER_BVISNULL( &be->be_rootndn ) || BER_BVISEMPTY( &be->be_rootndn ) ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "pguid_db_init: missing rootdn for database DN=\"%s\", YMMV\n",
+ be->be_suffix[ 0 ].bv_val );
+ }
+
+ return 0;
+}
+
+typedef struct pguid_mod_t {
+ struct berval ndn;
+ struct berval pguid;
+ struct pguid_mod_t *next;
+} pguid_mod_t;
+
+typedef struct {
+ slap_overinst *on;
+ pguid_mod_t *mods;
+} pguid_repair_cb_t;
+
+static int
+pguid_repair_cb( Operation *op, SlapReply *rs )
+{
+ int rc;
+ pguid_repair_cb_t *pcb = op->o_callback->sc_private;
+ Entry *e = NULL;
+ Attribute *a;
+ struct berval pdn, pndn;
+
+ switch ( rs->sr_type ) {
+ case REP_SEARCH:
+ break;
+
+ case REP_SEARCHREF:
+ case REP_RESULT:
+ return rs->sr_err;
+
+ default:
+ assert( 0 );
+ }
+
+ assert( rs->sr_entry != NULL );
+
+ dnParent( &rs->sr_entry->e_name, &pdn );
+ dnParent( &rs->sr_entry->e_nname, &pndn );
+
+ rc = overlay_entry_get_ov( op, &pndn, NULL, slap_schema.si_ad_entryUUID, 0, &e, pcb->on );
+ if ( rc != LDAP_SUCCESS || e == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: pguid_repair_cb: unable to get parent entry DN=\"%s\" (%d)\n",
+ op->o_log_prefix, pdn.bv_val, rc );
+ return 0;
+ }
+
+ a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
+ if ( a == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: pguid_repair_cb: unable to find entryUUID of parent entry DN=\"%s\" (%d)\n",
+ op->o_log_prefix, pdn.bv_val, rc );
+
+ } else {
+ ber_len_t len;
+ pguid_mod_t *mod;
+
+ assert( a->a_numvals == 1 );
+
+ len = sizeof( pguid_mod_t ) + rs->sr_entry->e_nname.bv_len + 1 + a->a_vals[0].bv_len + 1;
+ mod = op->o_tmpalloc( len, op->o_tmpmemctx );
+ mod->ndn.bv_len = rs->sr_entry->e_nname.bv_len;
+ mod->ndn.bv_val = (char *)&mod[1];
+ mod->pguid.bv_len = a->a_vals[0].bv_len;
+ mod->pguid.bv_val = (char *)&mod->ndn.bv_val[mod->ndn.bv_len + 1];
+ lutil_strncopy( mod->ndn.bv_val, rs->sr_entry->e_nname.bv_val, rs->sr_entry->e_nname.bv_len );
+ lutil_strncopy( mod->pguid.bv_val, a->a_vals[0].bv_val, a->a_vals[0].bv_len );
+
+ mod->next = pcb->mods;
+ pcb->mods = mod;
+
+ Debug( LDAP_DEBUG_TRACE, "%s: pguid_repair_cb: scheduling entry DN=\"%s\" for repair\n",
+ op->o_log_prefix, rs->sr_entry->e_name.bv_val );
+ }
+
+ if ( e != NULL ) {
+ (void)overlay_entry_release_ov( op, e, 0, pcb->on );
+ }
+
+ return 0;
+}
+
+static int
+pguid_repair( BackendDB *be )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ void *ctx = ldap_pvt_thread_pool_context();
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+ Operation *op;
+ slap_callback sc = { 0 };
+ pguid_repair_cb_t pcb = { 0 };
+ SlapReply rs = { REP_RESULT };
+ pguid_mod_t *pmod;
+ int nrepaired = 0;
+
+ connection_fake_init2( &conn, &opbuf, ctx, 0 );
+ op = &opbuf.ob_op;
+
+ op->o_tag = LDAP_REQ_SEARCH;
+ memset( &op->oq_search, 0, sizeof( op->oq_search ) );
+
+ op->o_bd = select_backend( &be->be_nsuffix[ 0 ], 0 );
+
+ op->o_req_dn = op->o_bd->be_suffix[ 0 ];
+ op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
+
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ op->ors_scope = LDAP_SCOPE_SUBORDINATE;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_slimit = SLAP_NO_LIMIT;
+ op->ors_attrs = slap_anlist_no_attrs;
+
+ op->ors_filterstr.bv_len = STRLENOF( "(!(=*))" ) + ad_parentUUID->ad_cname.bv_len;
+ op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
+ snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
+ "(!(%s=*))", ad_parentUUID->ad_cname.bv_val );
+
+ op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
+ if ( op->ors_filter == NULL ) {
+ rs.sr_err = LDAP_OTHER;
+ goto done_search;
+ }
+
+ op->o_callback = &sc;
+ sc.sc_response = pguid_repair_cb;
+ sc.sc_private = &pcb;
+ pcb.on = on;
+
+ (void)op->o_bd->bd_info->bi_op_search( op, &rs );
+
+ op->o_tag = LDAP_REQ_MODIFY;
+ sc.sc_response = slap_null_cb;
+ sc.sc_private = NULL;
+ memset( &op->oq_modify, 0, sizeof( req_modify_s ) );
+
+ for ( pmod = pcb.mods; pmod != NULL; ) {
+ pguid_mod_t *pnext;
+
+ Modifications *mod;
+ SlapReply rs2 = { REP_RESULT };
+
+ mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+ mod->sml_flags = SLAP_MOD_INTERNAL;
+ mod->sml_op = LDAP_MOD_REPLACE;
+ mod->sml_desc = ad_parentUUID;
+ mod->sml_type = ad_parentUUID->ad_cname;
+ mod->sml_values = ch_malloc( sizeof( struct berval ) * 2 );
+ mod->sml_nvalues = NULL;
+ mod->sml_numvals = 1;
+ mod->sml_next = NULL;
+
+ ber_dupbv( &mod->sml_values[0], &pmod->pguid );
+ BER_BVZERO( &mod->sml_values[1] );
+
+ op->o_req_dn = pmod->ndn;
+ op->o_req_ndn = pmod->ndn;
+
+ op->orm_modlist = mod;
+ op->o_bd->be_modify( op, &rs2 );
+ slap_mods_free( op->orm_modlist, 1 );
+ if ( rs2.sr_err == LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "%s: pguid_repair: entry DN=\"%s\" repaired\n",
+ op->o_log_prefix, pmod->ndn.bv_val );
+ nrepaired++;
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s: pguid_repair: entry DN=\"%s\" repair failed (%d)\n",
+ op->o_log_prefix, pmod->ndn.bv_val, rs2.sr_err );
+ }
+
+ pnext = pmod->next;
+ op->o_tmpfree( pmod, op->o_tmpmemctx );
+ pmod = pnext;
+ }
+
+done_search:;
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ filter_free_x( op, op->ors_filter, 1 );
+
+ Log( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
+ "pguid: repaired=%d\n", nrepaired );
+
+ return rs.sr_err;
+}
+
+/* search all entries without parentUUID; "repair" them */
+static int
+pguid_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ if ( SLAP_SINGLE_SHADOW( be ) ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "pguid incompatible with shadow database \"%s\".\n",
+ be->be_suffix[ 0 ].bv_val );
+ return 1;
+ }
+
+ pguid_repair( be );
+
+ return 0;
+}
+
+static struct {
+ char *desc;
+ AttributeDescription **adp;
+} as[] = {
+ { "( 1.3.6.1.4.1.4203.666.1.59 "
+ "NAME 'parentUUID' "
+ "DESC 'the value of the entryUUID of the parent' "
+ "EQUALITY UUIDMatch "
+ "ORDERING UUIDOrderingMatch "
+ "SYNTAX 1.3.6.1.1.16.1 "
+ "USAGE dSAOperation "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION "
+ ")",
+ &ad_parentUUID },
+ { NULL }
+};
+
+int
+pguid_initialize(void)
+{
+ int code, i;
+
+ for ( i = 0; as[ i ].desc != NULL; i++ ) {
+ code = register_at( as[ i ].desc, as[ i ].adp, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "pguid_initialize: register_at #%d failed\n",
+ i );
+ return code;
+ }
+
+ /* Allow Manager to set these as needed */
+ if ( is_at_no_user_mod( (*as[ i ].adp)->ad_type ) ) {
+ (*as[ i ].adp)->ad_type->sat_flags |=
+ SLAP_AT_MANAGEABLE;
+ }
+ }
+
+ pguid.on_bi.bi_type = "pguid";
+
+ pguid.on_bi.bi_op_add = pguid_op_add;
+ pguid.on_bi.bi_op_modrdn = pguid_op_rename;
+
+ pguid.on_bi.bi_db_init = pguid_db_init;
+ pguid.on_bi.bi_db_open = pguid_db_open;
+
+ return overlay_register( &pguid );
+}
+
+#if SLAPD_OVER_PGUID == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return pguid_initialize();
+}
+#endif /* SLAPD_OVER_PGUID == SLAPD_MOD_DYNAMIC */
+
+#endif /* SLAPD_OVER_PGUID */
diff --git a/contrib/slapd-modules/samba4/rdnval.c b/contrib/slapd-modules/samba4/rdnval.c
new file mode 100644
index 0000000..dfe0e47
--- /dev/null
+++ b/contrib/slapd-modules/samba4/rdnval.c
@@ -0,0 +1,657 @@
+/* rdnval.c - RDN value overlay */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati
+ * for inclusion in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_RDNVAL
+
+#include <stdio.h>
+
+#include "ac/string.h"
+#include "ac/socket.h"
+
+#include "slap.h"
+#include "slap-config.h"
+
+#include "lutil.h"
+
+/*
+ * Maintain an attribute (rdnValue) that contains the values of each AVA
+ * that builds up the RDN of an entry. This is required for interoperation
+ * with Samba4. It mimics the "name" attribute provided by Active Directory.
+ * The naming attributes must be directoryString-valued, or compatible.
+ * For example, IA5String values are cast into directoryString unless
+ * consisting of the empty string ("").
+ */
+
+static AttributeDescription *ad_rdnValue;
+static Syntax *syn_IA5String;
+
+static slap_overinst rdnval;
+
+static int
+rdnval_is_valid( AttributeDescription *desc, struct berval *value )
+{
+ if ( desc->ad_type->sat_syntax == slap_schema.si_syn_directoryString ) {
+ return 1;
+ }
+
+ if ( desc->ad_type->sat_syntax == syn_IA5String
+ && !BER_BVISEMPTY( value ) )
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+rdnval_unique_check_cb( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_SEARCH ) {
+ int *p = (int *)op->o_callback->sc_private;
+ (*p)++;
+ }
+
+ return 0;
+}
+
+static int
+rdnval_unique_check( Operation *op, BerVarray vals )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+
+ BackendDB db = *op->o_bd;
+ Operation op2 = *op;
+ SlapReply rs2 = { 0 };
+ int i;
+ BerVarray fvals;
+ char *ptr;
+ int gotit = 0;
+ slap_callback cb = { 0 };
+
+ /* short-circuit attempts to add suffix entry */
+ if ( op->o_tag == LDAP_REQ_ADD
+ && be_issuffix( op->o_bd, &op->o_req_ndn ) )
+ {
+ return LDAP_SUCCESS;
+ }
+
+ op2.o_bd = &db;
+ op2.o_bd->bd_info = (BackendInfo *)on->on_info;
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2.o_dn = op->o_bd->be_rootdn;
+ op2.o_ndn = op->o_bd->be_rootndn;
+ op2.o_callback = &cb;
+ cb.sc_response = rdnval_unique_check_cb;
+ cb.sc_private = (void *)&gotit;
+
+ dnParent( &op->o_req_ndn, &op2.o_req_dn );
+ op2.o_req_ndn = op2.o_req_dn;
+
+ op2.ors_limit = NULL;
+ op2.ors_slimit = 1;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+ op2.ors_attrs = slap_anlist_no_attrs;
+ op2.ors_attrsonly = 1;
+ op2.ors_deref = LDAP_DEREF_NEVER;
+ op2.ors_scope = LDAP_SCOPE_ONELEVEL;
+
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ )
+ /* just count */ ;
+
+ fvals = op->o_tmpcalloc( sizeof( struct berval ), i + 1,
+ op->o_tmpmemctx );
+
+ op2.ors_filterstr.bv_len = 0;
+ if ( i > 1 ) {
+ op2.ors_filterstr.bv_len = STRLENOF( "(&)" );
+ }
+
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
+ ldap_bv2escaped_filter_value_x( &vals[ i ], &fvals[ i ],
+ 1, op->o_tmpmemctx );
+ op2.ors_filterstr.bv_len += ad_rdnValue->ad_cname.bv_len
+ + fvals[ i ].bv_len + STRLENOF( "(=)" );
+ }
+
+ op2.ors_filterstr.bv_val = op->o_tmpalloc( op2.ors_filterstr.bv_len + 1, op->o_tmpmemctx );
+
+ ptr = op2.ors_filterstr.bv_val;
+ if ( i > 1 ) {
+ ptr = lutil_strcopy( ptr, "(&" );
+ }
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
+ *ptr++ = '(';
+ ptr = lutil_strncopy( ptr, ad_rdnValue->ad_cname.bv_val, ad_rdnValue->ad_cname.bv_len );
+ *ptr++ = '=';
+ ptr = lutil_strncopy( ptr, fvals[ i ].bv_val, fvals[ i ].bv_len );
+ *ptr++ = ')';
+ }
+
+ if ( i > 1 ) {
+ *ptr++ = ')';
+ }
+ *ptr = '\0';
+
+ assert( ptr == op2.ors_filterstr.bv_val + op2.ors_filterstr.bv_len );
+ op2.ors_filter = str2filter_x( op, op2.ors_filterstr.bv_val );
+ assert( op2.ors_filter != NULL );
+
+ (void)op2.o_bd->be_search( &op2, &rs2 );
+
+ filter_free_x( op, op2.ors_filter, 1 );
+ op->o_tmpfree( op2.ors_filterstr.bv_val, op->o_tmpmemctx );
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
+ if ( vals[ i ].bv_val != fvals[ i ].bv_val ) {
+ op->o_tmpfree( fvals[ i ].bv_val, op->o_tmpmemctx );
+ }
+ }
+ op->o_tmpfree( fvals, op->o_tmpmemctx );
+
+ if ( rs2.sr_err != LDAP_SUCCESS || gotit > 0 ) {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+rdnval_rdn2vals(
+ Operation *op,
+ SlapReply *rs,
+ struct berval *dn,
+ struct berval *ndn,
+ BerVarray *valsp,
+ BerVarray *nvalsp,
+ int *numvalsp )
+{
+ LDAPRDN rdn = NULL, nrdn = NULL;
+ int nAVA, i;
+
+ assert( *valsp == NULL );
+ assert( *nvalsp == NULL );
+
+ *numvalsp = 0;
+
+ if ( ldap_bv2rdn_x( dn, &rdn, (char **)&rs->sr_text,
+ LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "%s rdnval: can't figure out "
+ "type(s)/value(s) of rdn DN=\"%s\"\n",
+ op->o_log_prefix, dn->bv_val );
+ rs->sr_err = LDAP_INVALID_DN_SYNTAX;
+ rs->sr_text = "unknown type(s) used in RDN";
+
+ goto done;
+ }
+
+ if ( ldap_bv2rdn_x( ndn, &nrdn,
+ (char **)&rs->sr_text, LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "%s rdnval: can't figure out "
+ "type(s)/value(s) of normalized rdn DN=\"%s\"\n",
+ op->o_log_prefix, ndn->bv_val );
+ rs->sr_err = LDAP_INVALID_DN_SYNTAX;
+ rs->sr_text = "unknown type(s) used in RDN";
+
+ goto done;
+ }
+
+ for ( nAVA = 0; rdn[ nAVA ]; nAVA++ )
+ /* count'em */ ;
+
+ /* NOTE: we assume rdn and nrdn contain the same AVAs! */
+
+ *valsp = ch_calloc( sizeof( struct berval ), nAVA + 1 );
+ *nvalsp = ch_calloc( sizeof( struct berval ), nAVA + 1 );
+
+ /* Add new attribute values to the entry */
+ for ( i = 0; rdn[ i ]; i++ ) {
+ AttributeDescription *desc = NULL;
+
+ rs->sr_err = slap_bv2ad( &rdn[ i ]->la_attr,
+ &desc, &rs->sr_text );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "%s rdnval: %s: %s\n",
+ op->o_log_prefix,
+ rs->sr_text,
+ rdn[ i ]->la_attr.bv_val );
+ goto done;
+ }
+
+ if ( !rdnval_is_valid( desc, &rdn[ i ]->la_value ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "%s rdnval: syntax of naming attribute '%s' "
+ "not compatible with directoryString",
+ op->o_log_prefix, rdn[ i ]->la_attr.bv_val );
+ continue;
+ }
+
+ if ( value_find_ex( desc,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ *nvalsp,
+ &nrdn[ i ]->la_value,
+ op->o_tmpmemctx )
+ == LDAP_NO_SUCH_ATTRIBUTE )
+ {
+ ber_dupbv( &(*valsp)[ *numvalsp ], &rdn[ i ]->la_value );
+ ber_dupbv( &(*nvalsp)[ *numvalsp ], &nrdn[ i ]->la_value );
+
+ (*numvalsp)++;
+ }
+ }
+
+ if ( rdnval_unique_check( op, *valsp ) != LDAP_SUCCESS ) {
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ rs->sr_text = "rdnValue not unique within siblings";
+ goto done;
+ }
+
+done:;
+ if ( rdn != NULL ) {
+ ldap_rdnfree_x( rdn, op->o_tmpmemctx );
+ }
+
+ if ( nrdn != NULL ) {
+ ldap_rdnfree_x( nrdn, op->o_tmpmemctx );
+ }
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ if ( *valsp != NULL ) {
+ ber_bvarray_free( *valsp );
+ ber_bvarray_free( *nvalsp );
+ *valsp = NULL;
+ *nvalsp = NULL;
+ *numvalsp = 0;
+ }
+ }
+
+ return rs->sr_err;
+}
+
+static int
+rdnval_op_add( Operation *op, SlapReply *rs )
+{
+ Attribute *a, **ap;
+ int numvals = 0;
+ BerVarray vals = NULL, nvals = NULL;
+ int rc;
+
+ /* NOTE: should we accept an entry still in mods format? */
+ assert( op->ora_e != NULL );
+
+ if ( BER_BVISEMPTY( &op->ora_e->e_nname ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ a = attr_find( op->ora_e->e_attrs, ad_rdnValue );
+ if ( a != NULL ) {
+ /* TODO: check consistency? */
+ return SLAP_CB_CONTINUE;
+ }
+
+ rc = rdnval_rdn2vals( op, rs, &op->ora_e->e_name, &op->ora_e->e_nname,
+ &vals, &nvals, &numvals );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ }
+
+ a = attr_alloc( ad_rdnValue );
+
+ a->a_vals = vals;
+ a->a_nvals = nvals;
+ a->a_numvals = numvals;
+
+ for ( ap = &op->ora_e->e_attrs; *ap != NULL; ap = &(*ap)->a_next )
+ /* goto tail */ ;
+
+ *ap = a;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rdnval_op_rename( Operation *op, SlapReply *rs )
+{
+ Modifications *ml, **mlp;
+ int numvals = 0;
+ BerVarray vals = NULL, nvals = NULL;
+ struct berval old;
+ int rc;
+
+ dnRdn( &op->o_req_ndn, &old );
+ if ( dn_match( &old, &op->orr_nnewrdn ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ rc = rdnval_rdn2vals( op, rs, &op->orr_newrdn, &op->orr_nnewrdn,
+ &vals, &nvals, &numvals );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ }
+
+ ml = ch_calloc( sizeof( Modifications ), 1 );
+ ml->sml_values = vals;
+ ml->sml_nvalues = nvals;
+
+ ml->sml_numvals = numvals;
+
+ ml->sml_op = LDAP_MOD_REPLACE;
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+ ml->sml_desc = ad_rdnValue;
+ ml->sml_type = ad_rdnValue->ad_cname;
+
+ for ( mlp = &op->orr_modlist; *mlp != NULL; mlp = &(*mlp)->sml_next )
+ /* goto tail */ ;
+
+ *mlp = ml;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rdnval_db_init(
+ BackendDB *be,
+ ConfigReply *cr)
+{
+ if ( SLAP_ISGLOBALOVERLAY( be ) ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "rdnval_db_init: rdnval cannot be used as global overlay.\n" );
+ return 1;
+ }
+
+ if ( be->be_nsuffix == NULL ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "rdnval_db_init: database must have suffix\n" );
+ return 1;
+ }
+
+ if ( BER_BVISNULL( &be->be_rootndn ) || BER_BVISEMPTY( &be->be_rootndn ) ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "rdnval_db_init: missing rootdn for database DN=\"%s\", YMMV\n",
+ be->be_suffix[ 0 ].bv_val );
+ }
+
+ return 0;
+}
+
+typedef struct rdnval_mod_t {
+ struct berval ndn;
+ BerVarray vals;
+ BerVarray nvals;
+ int numvals;
+ struct rdnval_mod_t *next;
+} rdnval_mod_t;
+
+typedef struct {
+ BackendDB *bd;
+ rdnval_mod_t *mods;
+} rdnval_repair_cb_t;
+
+static int
+rdnval_repair_cb( Operation *op, SlapReply *rs )
+{
+ int rc;
+ rdnval_repair_cb_t *rcb = op->o_callback->sc_private;
+ rdnval_mod_t *mod;
+ BerVarray vals = NULL, nvals = NULL;
+ int numvals = 0;
+ ber_len_t len;
+ BackendDB *save_bd = op->o_bd;
+
+ switch ( rs->sr_type ) {
+ case REP_SEARCH:
+ break;
+
+ case REP_SEARCHREF:
+ case REP_RESULT:
+ return rs->sr_err;
+
+ default:
+ assert( 0 );
+ }
+
+ assert( rs->sr_entry != NULL );
+
+ op->o_bd = rcb->bd;
+ rc = rdnval_rdn2vals( op, rs, &rs->sr_entry->e_name, &rs->sr_entry->e_nname,
+ &vals, &nvals, &numvals );
+ op->o_bd = save_bd;
+ if ( rc != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ len = sizeof( rdnval_mod_t ) + rs->sr_entry->e_nname.bv_len + 1;
+ mod = op->o_tmpalloc( len, op->o_tmpmemctx );
+ mod->ndn.bv_len = rs->sr_entry->e_nname.bv_len;
+ mod->ndn.bv_val = (char *)&mod[1];
+ lutil_strncopy( mod->ndn.bv_val, rs->sr_entry->e_nname.bv_val, rs->sr_entry->e_nname.bv_len );
+ mod->vals = vals;
+ mod->nvals = nvals;
+ mod->numvals = numvals;
+
+ mod->next = rcb->mods;
+ rcb->mods = mod;
+
+ Debug( LDAP_DEBUG_TRACE, "%s: rdnval_repair_cb: scheduling entry DN=\"%s\" for repair\n",
+ op->o_log_prefix, rs->sr_entry->e_name.bv_val );
+
+ return 0;
+}
+
+static int
+rdnval_repair( BackendDB *be )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ void *ctx = ldap_pvt_thread_pool_context();
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+ Operation *op;
+ BackendDB db;
+ slap_callback sc = { 0 };
+ rdnval_repair_cb_t rcb = { 0 };
+ SlapReply rs = { REP_RESULT };
+ rdnval_mod_t *rmod;
+ int nrepaired = 0;
+
+ connection_fake_init2( &conn, &opbuf, ctx, 0 );
+ op = &opbuf.ob_op;
+
+ op->o_tag = LDAP_REQ_SEARCH;
+ memset( &op->oq_search, 0, sizeof( op->oq_search ) );
+
+ assert( !BER_BVISNULL( &be->be_nsuffix[ 0 ] ) );
+
+ op->o_bd = select_backend( &be->be_nsuffix[ 0 ], 0 );
+ assert( op->o_bd != NULL );
+ assert( op->o_bd->be_nsuffix != NULL );
+
+ op->o_req_dn = op->o_bd->be_suffix[ 0 ];
+ op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
+
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ op->ors_scope = LDAP_SCOPE_SUBTREE;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_slimit = SLAP_NO_LIMIT;
+ op->ors_attrs = slap_anlist_no_attrs;
+
+ op->ors_filterstr.bv_len = STRLENOF( "(!(=*))" ) + ad_rdnValue->ad_cname.bv_len;
+ op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
+ snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
+ "(!(%s=*))", ad_rdnValue->ad_cname.bv_val );
+
+ op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
+ if ( op->ors_filter == NULL ) {
+ rs.sr_err = LDAP_OTHER;
+ goto done_search;
+ }
+
+ op->o_callback = &sc;
+ sc.sc_response = rdnval_repair_cb;
+ sc.sc_private = &rcb;
+ rcb.bd = &db;
+ db = *be;
+ db.bd_info = (BackendInfo *)on;
+
+ (void)op->o_bd->bd_info->bi_op_search( op, &rs );
+
+ op->o_tag = LDAP_REQ_MODIFY;
+ sc.sc_response = slap_null_cb;
+ sc.sc_private = NULL;
+ memset( &op->oq_modify, 0, sizeof( req_modify_s ) );
+
+ for ( rmod = rcb.mods; rmod != NULL; ) {
+ rdnval_mod_t *rnext;
+
+ Modifications *mod;
+ SlapReply rs2 = { REP_RESULT };
+
+ mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+ mod->sml_flags = SLAP_MOD_INTERNAL;
+ mod->sml_op = LDAP_MOD_REPLACE;
+ mod->sml_desc = ad_rdnValue;
+ mod->sml_type = ad_rdnValue->ad_cname;
+ mod->sml_values = rmod->vals;
+ mod->sml_nvalues = rmod->nvals;
+ mod->sml_numvals = rmod->numvals;
+ mod->sml_next = NULL;
+
+ op->o_req_dn = rmod->ndn;
+ op->o_req_ndn = rmod->ndn;
+
+ op->orm_modlist = mod;
+
+ op->o_bd->be_modify( op, &rs2 );
+
+ slap_mods_free( op->orm_modlist, 1 );
+ if ( rs2.sr_err == LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "%s: rdnval_repair: entry DN=\"%s\" repaired\n",
+ op->o_log_prefix, rmod->ndn.bv_val );
+ nrepaired++;
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s: rdnval_repair: entry DN=\"%s\" repair failed (%d)\n",
+ op->o_log_prefix, rmod->ndn.bv_val, rs2.sr_err );
+ }
+
+ rnext = rmod->next;
+ op->o_tmpfree( rmod, op->o_tmpmemctx );
+ rmod = rnext;
+ }
+
+done_search:;
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ filter_free_x( op, op->ors_filter, 1 );
+
+ Log( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
+ "rdnval: repaired=%d\n", nrepaired );
+
+ return 0;
+}
+
+/* search all entries without parentUUID; "repair" them */
+static int
+rdnval_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ if ( SLAP_SINGLE_SHADOW( be ) ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "rdnval incompatible with shadow database \"%s\".\n",
+ be->be_suffix[ 0 ].bv_val );
+ return 1;
+ }
+
+ return rdnval_repair( be );
+}
+
+static struct {
+ char *desc;
+ AttributeDescription **adp;
+} as[] = {
+ { "( 1.3.6.1.4.1.4203.666.1.58 "
+ "NAME 'rdnValue' "
+ "DESC 'the value of the naming attributes' "
+ "SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' "
+ "EQUALITY caseIgnoreMatch "
+ "USAGE dSAOperation "
+ "NO-USER-MODIFICATION "
+ ")",
+ &ad_rdnValue },
+ { NULL }
+};
+
+int
+rdnval_initialize(void)
+{
+ int code, i;
+
+ for ( i = 0; as[ i ].desc != NULL; i++ ) {
+ code = register_at( as[ i ].desc, as[ i ].adp, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "rdnval_initialize: register_at #%d failed\n",
+ i );
+ return code;
+ }
+
+ /* Allow Manager to set these as needed */
+ if ( is_at_no_user_mod( (*as[ i ].adp)->ad_type ) ) {
+ (*as[ i ].adp)->ad_type->sat_flags |=
+ SLAP_AT_MANAGEABLE;
+ }
+ }
+
+ syn_IA5String = syn_find( "1.3.6.1.4.1.1466.115.121.1.26" );
+ if ( syn_IA5String == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "rdnval_initialize: unable to find syntax '1.3.6.1.4.1.1466.115.121.1.26' (IA5String)\n" );
+ return LDAP_OTHER;
+ }
+
+ rdnval.on_bi.bi_type = "rdnval";
+
+ rdnval.on_bi.bi_op_add = rdnval_op_add;
+ rdnval.on_bi.bi_op_modrdn = rdnval_op_rename;
+
+ rdnval.on_bi.bi_db_init = rdnval_db_init;
+ rdnval.on_bi.bi_db_open = rdnval_db_open;
+
+ return overlay_register( &rdnval );
+}
+
+#if SLAPD_OVER_RDNVAL == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return rdnval_initialize();
+}
+#endif /* SLAPD_OVER_RDNVAL == SLAPD_MOD_DYNAMIC */
+
+#endif /* SLAPD_OVER_RDNVAL */
diff --git a/contrib/slapd-modules/samba4/vernum.c b/contrib/slapd-modules/samba4/vernum.c
new file mode 100644
index 0000000..d70dc92
--- /dev/null
+++ b/contrib/slapd-modules/samba4/vernum.c
@@ -0,0 +1,459 @@
+/* vernum.c - RDN value overlay */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati
+ * for inclusion in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_VERNUM
+
+#include <stdio.h>
+
+#include "ac/string.h"
+#include "ac/socket.h"
+
+#include "slap.h"
+#include "slap-config.h"
+
+#include "lutil.h"
+
+/*
+ * Maintain an attribute (e.g. msDS-KeyVersionNumber) that consists
+ * in a counter of modifications of another attribute (e.g. unicodePwd).
+ */
+
+typedef struct vernum_t {
+ AttributeDescription *vn_attr;
+ AttributeDescription *vn_vernum;
+} vernum_t;
+
+static AttributeDescription *ad_msDS_KeyVersionNumber;
+
+static struct berval val_init = BER_BVC( "0" );
+static slap_overinst vernum;
+
+static int
+vernum_op_add( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ vernum_t *vn = (vernum_t *)on->on_bi.bi_private;
+
+ Attribute *a, **ap;
+ int rc;
+
+ /* NOTE: should we accept an entry still in mods format? */
+ assert( op->ora_e != NULL );
+
+ if ( BER_BVISEMPTY( &op->ora_e->e_nname ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ a = attr_find( op->ora_e->e_attrs, vn->vn_attr );
+ if ( a == NULL ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( attr_find( op->ora_e->e_attrs, vn->vn_vernum ) != NULL ) {
+ /* already present - leave it alone */
+ return SLAP_CB_CONTINUE;
+ }
+
+ a = attr_alloc( vn->vn_vernum );
+
+ value_add_one( &a->a_vals, &val_init );
+ a->a_nvals = a->a_vals;
+ a->a_numvals = 1;
+
+ for ( ap = &op->ora_e->e_attrs; *ap != NULL; ap = &(*ap)->a_next )
+ /* goto tail */ ;
+
+ *ap = a;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+vernum_op_modify( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ vernum_t *vn = (vernum_t *)on->on_bi.bi_private;
+
+ Modifications *ml, **mlp;
+ struct berval val = BER_BVC( "1" );
+ int rc;
+ unsigned got = 0;
+
+ for ( ml = op->orm_modlist; ml != NULL; ml = ml->sml_next ) {
+ if ( ml->sml_desc == vn->vn_vernum ) {
+ /* already present - leave it alone
+ * (or should we increment it anyway?) */
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( ml->sml_desc == vn->vn_attr ) {
+ got = 1;
+ }
+ }
+
+ if ( !got ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ for ( mlp = &op->orm_modlist; *mlp != NULL; mlp = &(*mlp)->sml_next )
+ /* goto tail */ ;
+
+ /* ITS#6561 */
+#ifdef SLAP_MOD_ADD_IF_NOT_PRESENT
+ /* the initial value is only added if the vernum attr is not present */
+ ml = ch_calloc( sizeof( Modifications ), 1 );
+ ml->sml_values = ch_calloc( sizeof( struct berval ) , 2 );
+ value_add_one( &ml->sml_values, &val_init );
+ ml->sml_nvalues = NULL;
+ ml->sml_numvals = 1;
+ ml->sml_op = SLAP_MOD_ADD_IF_NOT_PRESENT;
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+ ml->sml_desc = vn->vn_vernum;
+ ml->sml_type = vn->vn_vernum->ad_cname;
+
+ *mlp = ml;
+ mlp = &ml->sml_next;
+#endif /* SLAP_MOD_ADD_IF_NOT_PRESENT */
+
+ /* this increments by 1 the vernum attr */
+ ml = ch_calloc( sizeof( Modifications ), 1 );
+ ml->sml_values = ch_calloc( sizeof( struct berval ) , 2 );
+ value_add_one( &ml->sml_values, &val );
+ ml->sml_nvalues = NULL;
+ ml->sml_numvals = 1;
+ ml->sml_op = LDAP_MOD_INCREMENT;
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+ ml->sml_desc = vn->vn_vernum;
+ ml->sml_type = vn->vn_vernum->ad_cname;
+
+ *mlp = ml;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+vernum_db_init(
+ BackendDB *be,
+ ConfigReply *cr)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ vernum_t *vn = NULL;
+
+ if ( SLAP_ISGLOBALOVERLAY( be ) ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "vernum_db_init: vernum cannot be used as global overlay.\n" );
+ return 1;
+ }
+
+ if ( be->be_nsuffix == NULL ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "vernum_db_init: database must have suffix\n" );
+ return 1;
+ }
+
+ if ( BER_BVISNULL( &be->be_rootndn ) || BER_BVISEMPTY( &be->be_rootndn ) ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "vernum_db_init: missing rootdn for database DN=\"%s\", YMMV\n",
+ be->be_suffix[ 0 ].bv_val );
+ }
+
+ vn = (vernum_t *)ch_calloc( 1, sizeof( vernum_t ) );
+
+ on->on_bi.bi_private = (void *)vn;
+
+ return 0;
+}
+
+typedef struct vernum_mod_t {
+ struct berval ndn;
+ struct vernum_mod_t *next;
+} vernum_mod_t;
+
+typedef struct {
+ BackendDB *bd;
+ vernum_mod_t *mods;
+} vernum_repair_cb_t;
+
+static int
+vernum_repair_cb( Operation *op, SlapReply *rs )
+{
+ int rc;
+ vernum_repair_cb_t *rcb = op->o_callback->sc_private;
+ vernum_mod_t *mod;
+ ber_len_t len;
+ BackendDB *save_bd = op->o_bd;
+
+ switch ( rs->sr_type ) {
+ case REP_SEARCH:
+ break;
+
+ case REP_SEARCHREF:
+ case REP_RESULT:
+ return rs->sr_err;
+
+ default:
+ assert( 0 );
+ }
+
+ assert( rs->sr_entry != NULL );
+
+ len = sizeof( vernum_mod_t ) + rs->sr_entry->e_nname.bv_len + 1;
+ mod = op->o_tmpalloc( len, op->o_tmpmemctx );
+ mod->ndn.bv_len = rs->sr_entry->e_nname.bv_len;
+ mod->ndn.bv_val = (char *)&mod[1];
+ lutil_strncopy( mod->ndn.bv_val, rs->sr_entry->e_nname.bv_val, rs->sr_entry->e_nname.bv_len );
+
+ mod->next = rcb->mods;
+ rcb->mods = mod;
+
+ Debug( LDAP_DEBUG_TRACE, "%s: vernum_repair_cb: scheduling entry DN=\"%s\" for repair\n",
+ op->o_log_prefix, rs->sr_entry->e_name.bv_val );
+
+ return 0;
+}
+
+static int
+vernum_repair( BackendDB *be )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ vernum_t *vn = (vernum_t *)on->on_bi.bi_private;
+ void *ctx = ldap_pvt_thread_pool_context();
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+ Operation *op;
+ BackendDB db;
+ slap_callback sc = { 0 };
+ vernum_repair_cb_t rcb = { 0 };
+ SlapReply rs = { REP_RESULT };
+ vernum_mod_t *rmod;
+ int nrepaired = 0;
+
+ connection_fake_init2( &conn, &opbuf, ctx, 0 );
+ op = &opbuf.ob_op;
+
+ op->o_tag = LDAP_REQ_SEARCH;
+ memset( &op->oq_search, 0, sizeof( op->oq_search ) );
+
+ assert( !BER_BVISNULL( &be->be_nsuffix[ 0 ] ) );
+
+ op->o_bd = select_backend( &be->be_nsuffix[ 0 ], 0 );
+ assert( op->o_bd != NULL );
+ assert( op->o_bd->be_nsuffix != NULL );
+
+ op->o_req_dn = op->o_bd->be_suffix[ 0 ];
+ op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
+
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ op->ors_scope = LDAP_SCOPE_SUBTREE;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_slimit = SLAP_NO_LIMIT;
+ op->ors_attrs = slap_anlist_no_attrs;
+
+ op->ors_filterstr.bv_len = STRLENOF( "(&(=*)(!(=*)))" )
+ + vn->vn_attr->ad_cname.bv_len
+ + vn->vn_vernum->ad_cname.bv_len;
+ op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
+ snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
+ "(&(%s=*)(!(%s=*)))",
+ vn->vn_attr->ad_cname.bv_val,
+ vn->vn_vernum->ad_cname.bv_val );
+
+ op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
+ if ( op->ors_filter == NULL ) {
+ rs.sr_err = LDAP_OTHER;
+ goto done_search;
+ }
+
+ op->o_callback = &sc;
+ sc.sc_response = vernum_repair_cb;
+ sc.sc_private = &rcb;
+ rcb.bd = &db;
+ db = *be;
+ db.bd_info = (BackendInfo *)on;
+
+ (void)op->o_bd->bd_info->bi_op_search( op, &rs );
+
+ op->o_tag = LDAP_REQ_MODIFY;
+ sc.sc_response = slap_null_cb;
+ sc.sc_private = NULL;
+ memset( &op->oq_modify, 0, sizeof( req_modify_s ) );
+
+ for ( rmod = rcb.mods; rmod != NULL; ) {
+ vernum_mod_t *rnext;
+ Modifications mod;
+ struct berval vals[2] = { BER_BVNULL };
+ SlapReply rs2 = { REP_RESULT };
+
+ mod.sml_flags = SLAP_MOD_INTERNAL;
+ mod.sml_op = LDAP_MOD_REPLACE;
+ mod.sml_desc = vn->vn_vernum;
+ mod.sml_type = vn->vn_vernum->ad_cname;
+ mod.sml_values = vals;
+ mod.sml_values[0] = val_init;
+ mod.sml_nvalues = NULL;
+ mod.sml_numvals = 1;
+ mod.sml_next = NULL;
+
+ op->o_req_dn = rmod->ndn;
+ op->o_req_ndn = rmod->ndn;
+
+ op->orm_modlist = &mod;
+
+ op->o_bd->be_modify( op, &rs2 );
+
+ slap_mods_free( op->orm_modlist->sml_next, 1 );
+ if ( rs2.sr_err == LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "%s: vernum_repair: entry DN=\"%s\" repaired\n",
+ op->o_log_prefix, rmod->ndn.bv_val );
+ nrepaired++;
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s: vernum_repair: entry DN=\"%s\" repair failed (%d)\n",
+ op->o_log_prefix, rmod->ndn.bv_val, rs2.sr_err );
+ }
+
+ rnext = rmod->next;
+ op->o_tmpfree( rmod, op->o_tmpmemctx );
+ rmod = rnext;
+ }
+
+done_search:;
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ filter_free_x( op, op->ors_filter, 1 );
+
+ Log( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
+ "vernum: repaired=%d\n", nrepaired );
+
+ return 0;
+}
+
+static int
+vernum_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ vernum_t *vn = (vernum_t *)on->on_bi.bi_private;
+
+ if ( SLAP_SINGLE_SHADOW( be ) ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "vernum incompatible with shadow database \"%s\".\n",
+ be->be_suffix[ 0 ].bv_val );
+ return 1;
+ }
+
+ /* default: unicodePwd & msDS-KeyVersionNumber */
+ if ( vn->vn_attr == NULL ) {
+ const char *text = NULL;
+ int rc;
+
+ rc = slap_str2ad( "unicodePwd", &vn->vn_attr, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "vernum: unable to find attribute 'unicodePwd' (%d: %s)\n",
+ rc, text );
+ return 1;
+ }
+
+ vn->vn_vernum = ad_msDS_KeyVersionNumber;
+ }
+
+ return vernum_repair( be );
+}
+
+static int
+vernum_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ vernum_t *vn = (vernum_t *)on->on_bi.bi_private;
+
+ if ( vn ) {
+ ch_free( vn );
+ on->on_bi.bi_private = NULL;
+ }
+
+ return 0;
+}
+
+static struct {
+ char *desc;
+ AttributeDescription **adp;
+} as[] = {
+ { "( 1.2.840.113556.1.4.1782 "
+ "NAME 'msDS-KeyVersionNumber' "
+ "DESC 'in the original specification the syntax is 2.5.5.9' "
+ "SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' "
+ "EQUALITY integerMatch "
+ "SINGLE-VALUE "
+ "USAGE dSAOperation "
+ "NO-USER-MODIFICATION "
+ ")",
+ &ad_msDS_KeyVersionNumber },
+ { NULL }
+};
+
+int
+vernum_initialize(void)
+{
+ int code, i;
+
+ for ( i = 0; as[ i ].desc != NULL; i++ ) {
+ code = register_at( as[ i ].desc, as[ i ].adp, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "vernum_initialize: register_at #%d failed\n",
+ i );
+ return code;
+ }
+
+ /* Allow Manager to set these as needed */
+ if ( is_at_no_user_mod( (*as[ i ].adp)->ad_type ) ) {
+ (*as[ i ].adp)->ad_type->sat_flags |=
+ SLAP_AT_MANAGEABLE;
+ }
+ }
+
+ vernum.on_bi.bi_type = "vernum";
+
+ vernum.on_bi.bi_op_add = vernum_op_add;
+ vernum.on_bi.bi_op_modify = vernum_op_modify;
+
+ vernum.on_bi.bi_db_init = vernum_db_init;
+ vernum.on_bi.bi_db_open = vernum_db_open;
+ vernum.on_bi.bi_db_destroy = vernum_db_destroy;
+
+ return overlay_register( &vernum );
+}
+
+#if SLAPD_OVER_VERNUM == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return vernum_initialize();
+}
+#endif /* SLAPD_OVER_VERNUM == SLAPD_MOD_DYNAMIC */
+
+#endif /* SLAPD_OVER_VERNUM */
diff --git a/contrib/slapd-modules/smbk5pwd/Makefile b/contrib/slapd-modules/smbk5pwd/Makefile
new file mode 100644
index 0000000..a1c2c8e
--- /dev/null
+++ b/contrib/slapd-modules/smbk5pwd/Makefile
@@ -0,0 +1,77 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+# Copyright 2004 Howard Chu, Symas Corp. All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted only as authorized by the OpenLDAP
+# Public License.
+#
+# A copy of this license is available in the file LICENSE in the
+# top-level directory of the distribution or, alternatively, at
+# <http://www.OpenLDAP.org/license.html>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+SSL_INC =
+SSL_LIB = -lcrypto
+
+HEIMDAL_INC = -I/usr/heimdal/include
+HEIMDAL_LIB = -L/usr/heimdal/lib -lkrb5 -lkadm5srv
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+# Omit DO_KRB5, DO_SAMBA or DO_SHADOW if you don't want to support it.
+DEFS = -DDO_KRB5 -DDO_SAMBA -DDO_SHADOW
+INCS = $(LDAP_INC) $(HEIMDAL_INC) $(SSL_INC)
+LIBS = $(LDAP_LIB) $(HEIMDAL_LIB) $(SSL_LIB)
+
+PROGRAMS = smbk5pwd.la
+MANPAGES = slapo-smbk5pwd.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+smbk5pwd.la: smbk5pwd.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/smbk5pwd/README b/contrib/slapd-modules/smbk5pwd/README
new file mode 100644
index 0000000..2f02195
--- /dev/null
+++ b/contrib/slapd-modules/smbk5pwd/README
@@ -0,0 +1,94 @@
+This directory contains a slapd overlay, smbk5pwd, that extends the
+PasswordModify Extended Operation to update Kerberos keys and Samba
+password hashes for an LDAP user.
+
+The Kerberos support is written for Heimdal using its hdb-ldap backend.
+If a PasswordModify is performed on an entry that has the krb5KDCEntry
+objectclass, then the krb5Key and krb5KeyVersionNumber will be updated
+using the new password in the PasswordModify request. Additionally, a
+new "{K5KEY}" password hash mechanism is provided. For krb5KDCEntries that
+have this hash specifier in their userPassword attribute, Simple Binds
+will be checked against the Kerberos keys of the Entry. No data is
+needed after the "{K5KEY}" hash specifier in the userPassword, it is
+looked up from the Entry directly.
+
+The Samba support is written using the Samba 3.0 LDAP schema. If a
+PasswordModify is performed on an entry that has the sambaSamAccount
+objectclass, then the sambaNTPassword and sambaPwdLastSet attributes
+will be updated accordingly.
+
+To use the overlay, add:
+
+ include <path to>/krb5-kdc.schema
+ include <path to>/samba.schema
+
+ moduleload <path to>smbk5pwd.so
+ ...
+
+ database mdb
+ ...
+ overlay smbk5pwd
+
+to your slapd configuration file. (You should obtain the necessary schema
+files from the Heimdal and/or Samba distributions. At this time, there
+are several known errors in these schema files that you will have to
+correct before they will load in slapd. As of Samba 3.0 the schema looks
+fine as shipped.)
+
+All modules compiled in (i.e. krb5 and samba) are enabled; the statement
+
+ smbk5pwd-enable <module>
+
+can be used to enable only the desired one(s); legal values for <module>
+are "krb5", "samba" and "shadow", if they are respectively enabled by defining
+DO_KRB5, DO_SAMBA and DO_SHADOW.
+
+The samba module also supports the
+
+ smbk5pwd-must-change <seconds>
+
+which sets the "sambaPwdMustChange" attribute accordingly to force passwd
+expiry. A value of 0 disables this feature.
+
+The overlay now supports table-driven configuration, and thus can be run-time
+loaded and configured via back-config. The layout of the entry is
+
+ # {0}smbk5pwd, {1}bdb, config
+ dn: olcOverlay={0}smbk5pwd,olcDatabase={1}bdb,cn=config
+ objectClass: olcOverlayConfig
+ objectClass: olcSmbK5PwdConfig
+ olcOverlay: {0}smbk5pwd
+ olcSmbK5PwdEnable: krb5
+ olcSmbK5PwdEnable: samba
+ olcSmbK5PwdMustChange: 2592000
+
+which enables both krb5 and samba modules with a password expiry time
+of 30 days.
+
+The provided Makefile builds both Kerberos and Samba support by default.
+You must edit the Makefile to insure that the correct include and library
+paths are used. You can change the DEFS macro if you only want one or the
+other of Kerberos or Samba support.
+
+This overlay is only set up to be built as a dynamically loaded module.
+On most platforms, in order for the module to be usable, all of the
+library dependencies must also be available as shared libraries.
+
+If you need to build the overlay statically, you will have to move it into the
+slapd/overlays directory and edit the Makefile and overlays.c to reference
+it. You will also have to define SLAPD_OVER_SMBK5PWD to SLAPD_MOD_STATIC,
+and add the relevant libraries to the main slapd link command.
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+Copyright 2004-2022 The OpenLDAP Foundation.
+Portions Copyright 2004-2005 Howard Chu, Symas Corp. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
diff --git a/contrib/slapd-modules/smbk5pwd/slapo-smbk5pwd.5 b/contrib/slapd-modules/smbk5pwd/slapo-smbk5pwd.5
new file mode 100644
index 0000000..c9a0162
--- /dev/null
+++ b/contrib/slapd-modules/smbk5pwd/slapo-smbk5pwd.5
@@ -0,0 +1,177 @@
+.TH SLAPO-SMBK5PWD 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2015-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo-smbk5pwd \- Samba & Kerberos password sync overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.RS
+.LP
+include
+.B "<path to>/krb5-kdc.schema"
+.LP
+include
+.B "<path to>/samba.schema"
+.LP
+moduleload
+.B smbk5pwd.so
+.LP
+ ...
+.LP
+database mdb
+.LP
+ ...
+.LP
+overlay
+.B smbk5pwd
+.RE
+
+.SH DESCRIPTION
+.LP
+The
+.B smbk5pwd
+overlay to
+.BR slapd (8)
+overloads the Password Modify Extended Operation (RFC 3062) to update
+Kerberos keys and Samba password hashes for an LDAP user, as well as
+updating password change related attributes for Kerberos, Samba and/or
+UNIX user accounts.
+.LP
+The Samba support is written using the Samba 3.0 LDAP schema;
+Kerberos support is written for Heimdal using its hdb-ldap backend.
+.LP
+Additionally, a new
+.B {K5KEY}
+password hash mechanism is provided.
+For
+.B krb5KDCEntry
+objects that have this scheme specifier in their
+.I userPassword
+attribute, Simple Binds will be checked against the Kerberos keys of the entry.
+No data is needed after the
+.B {K5KEY}
+scheme specifier in the
+.IR userPassword ,
+it is looked up from the entry directly.
+
+.SH CONFIGURATION
+The
+.B smbk5pwd
+overlay supports the following
+.B slapd.conf
+configuration options, which should appear after the
+.B overlay
+directive:
+.TP
+.BI smbk5pwd-enable " <module>"
+can be used to enable only the desired modules.
+Legal values for
+.I <module>
+are
+.LP
+.RS
+.TP
+.B krb5
+If the user has the
+.B krb5KDCEntry
+objectclass, update the
+.B krb5Key
+and
+.B krb5KeyVersionNumber
+attributes using the new password in the Password Modify operation,
+provided the Kerberos account is not expired.
+Exiration is determined by evaluating the
+.B krb5ValidEnd
+attribute.
+.TP
+.B samba
+If the user is a
+.B sambaSamAccount
+object, synchronize the
+.B sambaNTPassword
+to the password entered in the Password Modify operation, and update
+.B sambaPwdLastSet
+accordingly.
+.TP
+.B shadow
+Update the attribute
+.BR shadowLastChange ,
+if the entry has the objectclass
+.BR shadowAccount .
+.LP
+By default all modules compiled in are enabled.
+Setting the config statement restricts the enabled modules to the ones
+explicitly mentioned.
+.RE
+.TP
+.BI smbk5pwd-can-change " <seconds>"
+If the
+.B samba
+module is enabled and the user is a
+.BR sambaSamAccount ,
+update the attribute
+.B sambaPwdCanChange
+to point
+.I <seconds>
+into the future, essentially denying any Samba password change until then.
+A value of
+.B 0
+disables this feature.
+.TP
+.BI smbk5pwd-must-change " <seconds>"
+If the
+.B samba
+module is enabled and the user is a
+.BR sambaSamAccount ,
+update the attribute
+.B sambaPwdMustChange
+to point
+.I <seconds>
+into the future, essentially setting the Samba password expiration time.
+A value of
+.B 0
+disables this feature.
+.LP
+Alternatively, the overlay supports table-driven configuration,
+and thus can be run-time loaded and configured via back-config.
+
+.SH EXAMPLE
+The layout of a slapd.d based, table-driven configuration entry looks like:
+.LP
+.EX
+ # {0}smbk5pwd, {1}mdb, config
+ dn: olcOverlay={0}smbk5pwd,olcDatabase={1}mdb,cn=config
+ objectClass: olcOverlayConfig
+ objectClass: olcSmbK5PwdConfig
+ olcOverlay: {0}smbk5pwd
+ olcSmbK5PwdEnable: krb5
+ olcSmbK5PwdEnable: samba
+ olcSmbK5PwdMustChange: 2592000
+.EE
+.LP
+which enables both
+.B krb5
+and
+.B samba
+modules with a Samba password expiration time of 30 days (=
+.B 2592000
+seconds).
+
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR ldappasswd (1),
+.BR ldap (3),
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+
+.SH ACKNOWLEDGEMENTS
+This manual page has been written by Peter Marschall based on the
+module's README file written by Howard Chu.
+.LP
+.B OpenLDAP
+is developed and maintained by The OpenLDAP Project (http://www.openldap.org/).
+.B OpenLDAP
+is derived from University of Michigan LDAP 3.3 Release.
+
diff --git a/contrib/slapd-modules/smbk5pwd/smbk5pwd.c b/contrib/slapd-modules/smbk5pwd/smbk5pwd.c
new file mode 100644
index 0000000..642140d
--- /dev/null
+++ b/contrib/slapd-modules/smbk5pwd/smbk5pwd.c
@@ -0,0 +1,1084 @@
+/* smbk5pwd.c - Overlay for managing Samba and Heimdal passwords */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2004-2005 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * Support for table-driven configuration added by Pierangelo Masarati.
+ * Support for sambaPwdMustChange and sambaPwdCanChange added by Marco D'Ettorre.
+ * Support for shadowLastChange added by SATOH Fumiyasu @ OSS Technology, Inc.
+ */
+
+#include <portable.h>
+
+#ifndef SLAPD_OVER_SMBK5PWD
+#define SLAPD_OVER_SMBK5PWD SLAPD_MOD_DYNAMIC
+#endif
+
+#ifdef SLAPD_OVER_SMBK5PWD
+
+#include <slap.h>
+#include <ac/errno.h>
+#include <ac/string.h>
+
+#include "slap-config.h"
+
+#ifdef DO_KRB5
+#include <lber.h>
+#include <lber_pvt.h>
+#include <lutil.h>
+
+/* make ASN1_MALLOC_ENCODE use our allocator */
+#define malloc ch_malloc
+
+#include <krb5.h>
+#include <kadm5/admin.h>
+#include <hdb.h>
+
+#ifndef HDB_INTERFACE_VERSION
+#define HDB_MASTER_KEY_SET master_key_set
+#else
+#define HDB_MASTER_KEY_SET hdb_master_key_set
+#endif
+
+static krb5_context context;
+static void *kadm_context;
+static kadm5_config_params conf;
+static HDB *db;
+
+static AttributeDescription *ad_krb5Key;
+static AttributeDescription *ad_krb5KeyVersionNumber;
+static AttributeDescription *ad_krb5PrincipalName;
+static AttributeDescription *ad_krb5ValidEnd;
+static ObjectClass *oc_krb5KDCEntry;
+#endif
+
+#ifdef DO_SAMBA
+#ifdef HAVE_GNUTLS
+#include <nettle/md4.h>
+#elif HAVE_OPENSSL
+#include <openssl/md4.h>
+#else
+#error Unsupported crypto backend.
+#endif
+#include "ldap_utf8.h"
+
+static AttributeDescription *ad_sambaNTPassword;
+static AttributeDescription *ad_sambaPwdLastSet;
+static AttributeDescription *ad_sambaPwdMustChange;
+static AttributeDescription *ad_sambaPwdCanChange;
+static ObjectClass *oc_sambaSamAccount;
+#endif
+
+#ifdef DO_SHADOW
+static AttributeDescription *ad_shadowLastChange;
+static ObjectClass *oc_shadowAccount;
+#endif
+
+/* Per-instance configuration information */
+typedef struct smbk5pwd_t {
+ unsigned mode;
+#define SMBK5PWD_F_KRB5 (0x1U)
+#define SMBK5PWD_F_SAMBA (0x2U)
+#define SMBK5PWD_F_SHADOW (0x4U)
+
+#define SMBK5PWD_DO_KRB5(pi) ((pi)->mode & SMBK5PWD_F_KRB5)
+#define SMBK5PWD_DO_SAMBA(pi) ((pi)->mode & SMBK5PWD_F_SAMBA)
+#define SMBK5PWD_DO_SHADOW(pi) ((pi)->mode & SMBK5PWD_F_SHADOW)
+
+#ifdef DO_KRB5
+ /* nothing yet */
+#endif
+
+#ifdef DO_SAMBA
+ /* How many seconds before forcing a password change? */
+ time_t smb_must_change;
+ /* How many seconds after allowing a password change? */
+ time_t smb_can_change;
+#endif
+
+#ifdef DO_SHADOW
+ /* nothing yet */
+#endif
+} smbk5pwd_t;
+
+static const unsigned SMBK5PWD_F_ALL =
+ 0
+#ifdef DO_KRB5
+ | SMBK5PWD_F_KRB5
+#endif
+#ifdef DO_SAMBA
+ | SMBK5PWD_F_SAMBA
+#endif
+#ifdef DO_SHADOW
+ | SMBK5PWD_F_SHADOW
+#endif
+;
+
+static int smbk5pwd_modules_init( smbk5pwd_t *pi );
+
+#ifdef DO_SAMBA
+static const char hex[] = "0123456789abcdef";
+
+#define MAX_PWLEN 256
+#define HASHLEN 16
+
+static void hexify(
+ const char in[HASHLEN],
+ struct berval *out
+)
+{
+ int i;
+ char *a;
+ unsigned char *b;
+
+ out->bv_val = ch_malloc(HASHLEN*2 + 1);
+ out->bv_len = HASHLEN*2;
+
+ a = out->bv_val;
+ b = (unsigned char *)in;
+ for (i=0; i<HASHLEN; i++) {
+ *a++ = hex[*b >> 4];
+ *a++ = hex[*b++ & 0x0f];
+ }
+ *a++ = '\0';
+}
+
+static void nthash(
+ struct berval *passwd,
+ struct berval *hash
+)
+{
+ /* Windows currently only allows 14 character passwords, but
+ * may support up to 256 in the future. We assume this means
+ * 256 UCS2 characters, not 256 bytes...
+ */
+ char hbuf[HASHLEN];
+#ifdef HAVE_OPENSSL
+ MD4_CTX ctx;
+#elif defined(HAVE_GNUTLS)
+ struct md4_ctx ctx;
+#endif
+
+ if (passwd->bv_len > MAX_PWLEN*2)
+ passwd->bv_len = MAX_PWLEN*2;
+
+#ifdef HAVE_OPENSSL
+ MD4_Init( &ctx );
+ MD4_Update( &ctx, passwd->bv_val, passwd->bv_len );
+ MD4_Final( (unsigned char *)hbuf, &ctx );
+#elif defined(HAVE_GNUTLS)
+ md4_init( &ctx );
+ md4_update( &ctx, passwd->bv_len, (unsigned char *)passwd->bv_val );
+ md4_digest( &ctx, sizeof(hbuf), (unsigned char *)hbuf );
+#endif
+
+ hexify( hbuf, hash );
+}
+#endif /* DO_SAMBA */
+
+#ifdef DO_KRB5
+
+static int smbk5pwd_op_cleanup(
+ Operation *op,
+ SlapReply *rs )
+{
+ slap_callback *cb;
+
+ /* clear out the current key */
+ ldap_pvt_thread_pool_setkey( op->o_threadctx, smbk5pwd_op_cleanup,
+ NULL, 0, NULL, NULL );
+
+ /* free the callback */
+ cb = op->o_callback;
+ op->o_callback = cb->sc_next;
+ op->o_tmpfree( cb, op->o_tmpmemctx );
+ return 0;
+}
+
+static int smbk5pwd_op_bind(
+ Operation *op,
+ SlapReply *rs )
+{
+ /* If this is a simple Bind, stash the Op pointer so our chk
+ * function can find it. Set a cleanup callback to clear it
+ * out when the Bind completes.
+ */
+ if ( op->oq_bind.rb_method == LDAP_AUTH_SIMPLE ) {
+ slap_callback *cb;
+ ldap_pvt_thread_pool_setkey( op->o_threadctx,
+ smbk5pwd_op_cleanup, op, 0, NULL, NULL );
+ cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
+ cb->sc_cleanup = smbk5pwd_op_cleanup;
+ cb->sc_next = op->o_callback;
+ op->o_callback = cb;
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static LUTIL_PASSWD_CHK_FUNC k5key_chk;
+static LUTIL_PASSWD_HASH_FUNC k5key_hash;
+static const struct berval k5key_scheme = BER_BVC("{K5KEY}");
+
+/* This password scheme stores no data in the userPassword attribute
+ * other than the scheme name. It assumes the invoking entry is a
+ * krb5KDCentry and compares the passed-in credentials against the
+ * krb5Key attribute. The krb5Key may be multi-valued, but they are
+ * simply multiple keytypes generated from the same input string, so
+ * only the first value needs to be compared here.
+ *
+ * Since the lutil_passwd API doesn't pass the Entry object in, we
+ * have to fetch it ourselves in order to get access to the other
+ * attributes. We accomplish this with the help of the overlay's Bind
+ * function, which stores the current Operation pointer in thread-specific
+ * storage so we can retrieve it here. The Operation provides all
+ * the necessary context for us to get Entry from the database.
+ */
+static int k5key_chk(
+ const struct berval *sc,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text )
+{
+ void *ctx, *op_tmp;
+ Operation *op;
+ int rc;
+ Entry *e;
+ Attribute *a;
+ krb5_error_code ret;
+ krb5_keyblock key;
+ krb5_salt salt;
+ hdb_entry ent;
+
+ /* Find our thread context, find our Operation */
+ ctx = ldap_pvt_thread_pool_context();
+
+ if ( ldap_pvt_thread_pool_getkey( ctx, smbk5pwd_op_cleanup, &op_tmp, NULL )
+ || !op_tmp )
+ return LUTIL_PASSWD_ERR;
+ op = op_tmp;
+
+ rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
+ if ( rc != LDAP_SUCCESS ) return LUTIL_PASSWD_ERR;
+
+ rc = LUTIL_PASSWD_ERR;
+ do {
+ size_t l;
+ Key ekey = {0};
+
+ a = attr_find( e->e_attrs, ad_krb5PrincipalName );
+ if (!a ) break;
+
+ memset( &ent, 0, sizeof(ent) );
+ ret = krb5_parse_name(context, a->a_vals[0].bv_val, &ent.principal);
+ if ( ret ) break;
+
+ a = attr_find( e->e_attrs, ad_krb5ValidEnd );
+ if (a) {
+ struct lutil_tm tm;
+ struct lutil_timet tt;
+ if ( lutil_parsetime( a->a_vals[0].bv_val, &tm ) == 0 &&
+ lutil_tm2time( &tm, &tt ) == 0 && tt.tt_sec < op->o_time ) {
+ /* Account is expired */
+ rc = LUTIL_PASSWD_ERR;
+ break;
+ }
+ }
+
+ krb5_get_pw_salt( context, ent.principal, &salt );
+ krb5_free_principal( context, ent.principal );
+
+ a = attr_find( e->e_attrs, ad_krb5Key );
+ if ( !a ) break;
+
+ ent.keys.len = 1;
+ ent.keys.val = &ekey;
+ decode_Key((unsigned char *) a->a_vals[0].bv_val,
+ (size_t) a->a_vals[0].bv_len, &ent.keys.val[0], &l);
+ if ( db->HDB_MASTER_KEY_SET )
+ hdb_unseal_keys( context, db, &ent );
+
+ krb5_string_to_key_salt( context, ekey.key.keytype, cred->bv_val,
+ salt, &key );
+
+ krb5_free_salt( context, salt );
+
+ if ( memcmp( ekey.key.keyvalue.data, key.keyvalue.data,
+ key.keyvalue.length ) == 0 ) rc = LUTIL_PASSWD_OK;
+
+ krb5_free_keyblock_contents( context, &key );
+ krb5_free_keyblock_contents( context, &ekey.key );
+
+ } while(0);
+ be_entry_release_r( op, e );
+ return rc;
+}
+
+static int k5key_hash(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ ber_dupbv( hash, (struct berval *)&k5key_scheme );
+ return LUTIL_PASSWD_OK;
+}
+#endif /* DO_KRB5 */
+
+static int smbk5pwd_exop_passwd(
+ Operation *op,
+ SlapReply *rs )
+{
+ int rc;
+ req_pwdexop_s *qpw = &op->oq_pwdexop;
+ Entry *e;
+ Modifications *ml;
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ smbk5pwd_t *pi = on->on_bi.bi_private;
+ char term;
+
+ /* Not the operation we expected, pass it on... */
+ if ( ber_bvcmp( &slap_EXOP_MODIFY_PASSWD, &op->ore_reqoid ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
+ if ( rc != LDAP_SUCCESS ) return rc;
+
+ term = qpw->rs_new.bv_val[qpw->rs_new.bv_len];
+ qpw->rs_new.bv_val[qpw->rs_new.bv_len] = '\0';
+
+#ifdef DO_KRB5
+ /* Kerberos stuff */
+ do {
+ krb5_error_code ret;
+ hdb_entry ent;
+ struct berval *keys;
+ size_t nkeys;
+ int kvno, i;
+ Attribute *a;
+
+ if ( !SMBK5PWD_DO_KRB5( pi ) ) break;
+
+ if ( !is_entry_objectclass(e, oc_krb5KDCEntry, 0 ) ) break;
+
+ a = attr_find( e->e_attrs, ad_krb5PrincipalName );
+ if ( !a ) break;
+
+ memset( &ent, 0, sizeof(ent) );
+ ret = krb5_parse_name(context, a->a_vals[0].bv_val, &ent.principal);
+ if ( ret ) break;
+
+ a = attr_find( e->e_attrs, ad_krb5KeyVersionNumber );
+ kvno = 0;
+ if ( a ) {
+ if ( lutil_atoi( &kvno, a->a_vals[0].bv_val ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s smbk5pwd EXOP: "
+ "dn=\"%s\" unable to parse krb5KeyVersionNumber=\"%s\"\n",
+ op->o_log_prefix, e->e_name.bv_val, a->a_vals[0].bv_val );
+ }
+
+ } else {
+ /* shouldn't happen, this is a required attr */
+ Debug( LDAP_DEBUG_ANY, "%s smbk5pwd EXOP: "
+ "dn=\"%s\" missing krb5KeyVersionNumber\n",
+ op->o_log_prefix, e->e_name.bv_val );
+ }
+
+ ret = hdb_generate_key_set_password(context, ent.principal,
+ qpw->rs_new.bv_val, &ent.keys.val, &nkeys);
+ ent.keys.len = nkeys;
+ hdb_seal_keys(context, db, &ent);
+ krb5_free_principal( context, ent.principal );
+
+ keys = ch_malloc( (ent.keys.len + 1) * sizeof(struct berval));
+
+ for (i = 0; i < ent.keys.len; i++) {
+ unsigned char *buf;
+ size_t len;
+
+ ASN1_MALLOC_ENCODE(Key, buf, len, &ent.keys.val[i], &len, ret);
+ if (ret != 0)
+ break;
+
+ keys[i].bv_val = (char *)buf;
+ keys[i].bv_len = len;
+ }
+ BER_BVZERO( &keys[i] );
+
+ hdb_free_keys(context, ent.keys.len, ent.keys.val);
+
+ if ( i != ent.keys.len ) {
+ ber_bvarray_free( keys );
+ break;
+ }
+
+ ml = ch_malloc(sizeof(Modifications));
+ if (!qpw->rs_modtail) qpw->rs_modtail = &ml->sml_next;
+ ml->sml_next = qpw->rs_mods;
+ qpw->rs_mods = ml;
+
+ ml->sml_desc = ad_krb5Key;
+ ml->sml_op = LDAP_MOD_REPLACE;
+#ifdef SLAP_MOD_INTERNAL
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+#endif
+ ml->sml_numvals = i;
+ ml->sml_values = keys;
+ ml->sml_nvalues = NULL;
+
+ ml = ch_malloc(sizeof(Modifications));
+ ml->sml_next = qpw->rs_mods;
+ qpw->rs_mods = ml;
+
+ ml->sml_desc = ad_krb5KeyVersionNumber;
+ ml->sml_op = LDAP_MOD_REPLACE;
+#ifdef SLAP_MOD_INTERNAL
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+#endif
+ ml->sml_numvals = 1;
+ ml->sml_values = ch_malloc( 2 * sizeof(struct berval));
+ ml->sml_values[0].bv_val = ch_malloc( 64 );
+ ml->sml_values[0].bv_len = sprintf(ml->sml_values[0].bv_val,
+ "%d", kvno+1 );
+ BER_BVZERO( &ml->sml_values[1] );
+ ml->sml_nvalues = NULL;
+ } while ( 0 );
+#endif /* DO_KRB5 */
+
+#ifdef DO_SAMBA
+ /* Samba stuff */
+ if ( SMBK5PWD_DO_SAMBA( pi ) && is_entry_objectclass(e, oc_sambaSamAccount, 0 ) ) {
+ struct berval *keys;
+ ber_len_t j,l;
+ wchar_t *wcs, wc;
+ char *c;
+ struct berval pwd;
+
+ /* Expand incoming UTF8 string to UCS4 */
+ l = ldap_utf8_chars(qpw->rs_new.bv_val);
+ wcs = ch_malloc((l+1) * sizeof(wchar_t));
+
+ ldap_x_utf8s_to_wcs( wcs, qpw->rs_new.bv_val, l );
+
+ /* Truncate UCS4 to UCS2 */
+ c = (char *)wcs;
+ for (j=0; j<l; j++) {
+ wc = wcs[j];
+ *c++ = wc & 0xff;
+ *c++ = (wc >> 8) & 0xff;
+ }
+ *c++ = 0;
+ pwd.bv_val = (char *)wcs;
+ pwd.bv_len = l * 2;
+
+ ml = ch_malloc(sizeof(Modifications));
+ if (!qpw->rs_modtail) qpw->rs_modtail = &ml->sml_next;
+ ml->sml_next = qpw->rs_mods;
+ qpw->rs_mods = ml;
+
+ keys = ch_malloc( 2 * sizeof(struct berval) );
+ BER_BVZERO( &keys[1] );
+ nthash( &pwd, keys );
+
+ ml->sml_desc = ad_sambaNTPassword;
+ ml->sml_op = LDAP_MOD_REPLACE;
+#ifdef SLAP_MOD_INTERNAL
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+#endif
+ ml->sml_numvals = 1;
+ ml->sml_values = keys;
+ ml->sml_nvalues = NULL;
+
+ ch_free(wcs);
+
+ ml = ch_malloc(sizeof(Modifications));
+ ml->sml_next = qpw->rs_mods;
+ qpw->rs_mods = ml;
+
+ keys = ch_malloc( 2 * sizeof(struct berval) );
+ keys[0].bv_val = ch_malloc( LDAP_PVT_INTTYPE_CHARS(long) );
+ keys[0].bv_len = snprintf(keys[0].bv_val,
+ LDAP_PVT_INTTYPE_CHARS(long),
+ "%ld", slap_get_time());
+ BER_BVZERO( &keys[1] );
+
+ ml->sml_desc = ad_sambaPwdLastSet;
+ ml->sml_op = LDAP_MOD_REPLACE;
+#ifdef SLAP_MOD_INTERNAL
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+#endif
+ ml->sml_numvals = 1;
+ ml->sml_values = keys;
+ ml->sml_nvalues = NULL;
+
+ if (pi->smb_must_change)
+ {
+ ml = ch_malloc(sizeof(Modifications));
+ ml->sml_next = qpw->rs_mods;
+ qpw->rs_mods = ml;
+
+ keys = ch_malloc( 2 * sizeof(struct berval) );
+ keys[0].bv_val = ch_malloc( LDAP_PVT_INTTYPE_CHARS(long) );
+ keys[0].bv_len = snprintf(keys[0].bv_val,
+ LDAP_PVT_INTTYPE_CHARS(long),
+ "%ld", slap_get_time() + pi->smb_must_change);
+ BER_BVZERO( &keys[1] );
+
+ ml->sml_desc = ad_sambaPwdMustChange;
+ ml->sml_op = LDAP_MOD_REPLACE;
+#ifdef SLAP_MOD_INTERNAL
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+#endif
+ ml->sml_numvals = 1;
+ ml->sml_values = keys;
+ ml->sml_nvalues = NULL;
+ }
+
+ if (pi->smb_can_change)
+ {
+ ml = ch_malloc(sizeof(Modifications));
+ ml->sml_next = qpw->rs_mods;
+ qpw->rs_mods = ml;
+
+ keys = ch_malloc( 2 * sizeof(struct berval) );
+ keys[0].bv_val = ch_malloc( LDAP_PVT_INTTYPE_CHARS(long) );
+ keys[0].bv_len = snprintf(keys[0].bv_val,
+ LDAP_PVT_INTTYPE_CHARS(long),
+ "%ld", slap_get_time() + pi->smb_can_change);
+ BER_BVZERO( &keys[1] );
+
+ ml->sml_desc = ad_sambaPwdCanChange;
+ ml->sml_op = LDAP_MOD_REPLACE;
+#ifdef SLAP_MOD_INTERNAL
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+#endif
+ ml->sml_numvals = 1;
+ ml->sml_values = keys;
+ ml->sml_nvalues = NULL;
+ }
+ }
+#endif /* DO_SAMBA */
+
+#ifdef DO_SHADOW
+ /* shadow stuff */
+ if ( SMBK5PWD_DO_SHADOW( pi ) && is_entry_objectclass(e, oc_shadowAccount, 0 ) ) {
+ struct berval *keys;
+
+ ml = ch_malloc(sizeof(Modifications));
+ if (!qpw->rs_modtail) qpw->rs_modtail = &ml->sml_next;
+ ml->sml_next = qpw->rs_mods;
+ qpw->rs_mods = ml;
+
+ keys = ch_malloc( sizeof(struct berval) * 2);
+ BER_BVZERO( &keys[1] );
+ keys[0].bv_val = ch_malloc( LDAP_PVT_INTTYPE_CHARS(long) );
+ keys[0].bv_len = snprintf(keys[0].bv_val,
+ LDAP_PVT_INTTYPE_CHARS(long),
+ "%ld", (long)(slap_get_time() / (60 * 60 * 24)));
+
+ ml->sml_desc = ad_shadowLastChange;
+ ml->sml_op = LDAP_MOD_REPLACE;
+#ifdef SLAP_MOD_INTERNAL
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+#endif
+ ml->sml_numvals = 1;
+ ml->sml_values = keys;
+ ml->sml_nvalues = NULL;
+ }
+#endif /* DO_SHADOW */
+
+ be_entry_release_r( op, e );
+ qpw->rs_new.bv_val[qpw->rs_new.bv_len] = term;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static slap_overinst smbk5pwd;
+
+/* back-config stuff */
+enum {
+ PC_SMB_MUST_CHANGE = 1,
+ PC_SMB_CAN_CHANGE,
+ PC_SMB_ENABLE
+};
+
+static ConfigDriver smbk5pwd_cf_func;
+
+/*
+ * NOTE: uses OID arcs OLcfgCtAt:1 and OLcfgCtOc:1
+ */
+
+static ConfigTable smbk5pwd_cfats[] = {
+ { "smbk5pwd-enable", "arg",
+ 2, 0, 0, ARG_MAGIC|PC_SMB_ENABLE, smbk5pwd_cf_func,
+ "( OLcfgCtAt:1.1 NAME 'olcSmbK5PwdEnable' "
+ "DESC 'Modules to be enabled' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "smbk5pwd-must-change", "time",
+ 2, 2, 0, ARG_MAGIC|ARG_INT|PC_SMB_MUST_CHANGE, smbk5pwd_cf_func,
+ "( OLcfgCtAt:1.2 NAME 'olcSmbK5PwdMustChange' "
+ "DESC 'Credentials validity interval' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "smbk5pwd-can-change", "time",
+ 2, 2, 0, ARG_MAGIC|ARG_INT|PC_SMB_CAN_CHANGE, smbk5pwd_cf_func,
+ "( OLcfgCtAt:1.3 NAME 'olcSmbK5PwdCanChange' "
+ "DESC 'Credentials minimum validity interval' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs smbk5pwd_cfocs[] = {
+ { "( OLcfgCtOc:1.1 "
+ "NAME 'olcSmbK5PwdConfig' "
+ "DESC 'smbk5pwd overlay configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( "
+ "olcSmbK5PwdEnable "
+ "$ olcSmbK5PwdMustChange "
+ "$ olcSmbK5PwdCanChange "
+ ") )", Cft_Overlay, smbk5pwd_cfats },
+
+ { NULL, 0, NULL }
+};
+
+/*
+ * add here other functionalities; handle their initialization
+ * as appropriate in smbk5pwd_modules_init().
+ */
+static slap_verbmasks smbk5pwd_modules[] = {
+ { BER_BVC( "krb5" ), SMBK5PWD_F_KRB5 },
+ { BER_BVC( "samba" ), SMBK5PWD_F_SAMBA },
+ { BER_BVC( "shadow" ), SMBK5PWD_F_SHADOW },
+ { BER_BVNULL, -1 }
+};
+
+static int
+smbk5pwd_cf_func( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+
+ int rc = 0;
+ smbk5pwd_t *pi = on->on_bi.bi_private;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ switch( c->type ) {
+ case PC_SMB_MUST_CHANGE:
+#ifdef DO_SAMBA
+ c->value_int = pi->smb_must_change;
+#else /* ! DO_SAMBA */
+ c->value_int = 0;
+#endif /* ! DO_SAMBA */
+ break;
+
+ case PC_SMB_CAN_CHANGE:
+#ifdef DO_SAMBA
+ c->value_int = pi->smb_can_change;
+#else /* ! DO_SAMBA */
+ c->value_int = 0;
+#endif /* ! DO_SAMBA */
+ break;
+
+ case PC_SMB_ENABLE:
+ c->rvalue_vals = NULL;
+ if ( pi->mode ) {
+ mask_to_verbs( smbk5pwd_modules, pi->mode, &c->rvalue_vals );
+ if ( c->rvalue_vals == NULL ) {
+ rc = 1;
+ }
+ }
+ break;
+
+ default:
+ assert( 0 );
+ rc = 1;
+ }
+ return rc;
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ switch( c->type ) {
+ case PC_SMB_MUST_CHANGE:
+ break;
+
+ case PC_SMB_CAN_CHANGE:
+ break;
+
+ case PC_SMB_ENABLE:
+ if ( !c->line ) {
+ pi->mode = 0;
+
+ } else {
+ int i;
+
+ i = verb_to_mask( c->line, smbk5pwd_modules );
+ pi->mode &= ~smbk5pwd_modules[i].mask;
+ }
+ break;
+
+ default:
+ assert( 0 );
+ rc = 1;
+ }
+ return rc;
+ }
+
+ switch( c->type ) {
+ case PC_SMB_MUST_CHANGE:
+#ifdef DO_SAMBA
+ if ( c->value_int < 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
+ "<%s> invalid negative value \"%d\".",
+ c->log, c->argv[ 0 ], c->value_int );
+ return 1;
+ }
+ pi->smb_must_change = c->value_int;
+#else /* ! DO_SAMBA */
+ Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
+ "<%s> only meaningful "
+ "when compiled with -DDO_SAMBA.\n",
+ c->log, c->argv[ 0 ] );
+ return 1;
+#endif /* ! DO_SAMBA */
+ break;
+
+ case PC_SMB_CAN_CHANGE:
+#ifdef DO_SAMBA
+ if ( c->value_int < 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
+ "<%s> invalid negative value \"%d\".",
+ c->log, c->argv[ 0 ], c->value_int );
+ return 1;
+ }
+ pi->smb_can_change = c->value_int;
+#else /* ! DO_SAMBA */
+ Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
+ "<%s> only meaningful "
+ "when compiled with -DDO_SAMBA.\n",
+ c->log, c->argv[ 0 ] );
+ return 1;
+#endif /* ! DO_SAMBA */
+ break;
+
+ case PC_SMB_ENABLE: {
+ slap_mask_t mode = pi->mode, m = 0;
+
+ rc = verbs_to_mask( c->argc, c->argv, smbk5pwd_modules, &m );
+ if ( rc > 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
+ "<%s> unknown module \"%s\".\n",
+ c->log, c->argv[ 0 ], c->argv[ rc ] );
+ return 1;
+ }
+
+ /* we can hijack the smbk5pwd_t structure because
+ * from within the configuration, this is the only
+ * active thread. */
+ pi->mode |= m;
+
+#ifndef DO_KRB5
+ if ( SMBK5PWD_DO_KRB5( pi ) ) {
+ Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
+ "<%s> module \"%s\" only allowed when compiled with -DDO_KRB5.\n",
+ c->log, c->argv[ 0 ], c->argv[ rc ] );
+ pi->mode = mode;
+ return 1;
+ }
+#endif /* ! DO_KRB5 */
+
+#ifndef DO_SAMBA
+ if ( SMBK5PWD_DO_SAMBA( pi ) ) {
+ Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
+ "<%s> module \"%s\" only allowed when compiled with -DDO_SAMBA.\n",
+ c->log, c->argv[ 0 ], c->argv[ rc ] );
+ pi->mode = mode;
+ return 1;
+ }
+#endif /* ! DO_SAMBA */
+
+#ifndef DO_SHADOW
+ if ( SMBK5PWD_DO_SHADOW( pi ) ) {
+ Debug( LDAP_DEBUG_ANY, "%s: smbk5pwd: "
+ "<%s> module \"%s\" only allowed when compiled with -DDO_SHADOW.\n",
+ c->log, c->argv[ 0 ], c->argv[ rc ] );
+ pi->mode = mode;
+ return 1;
+ }
+#endif /* ! DO_SHADOW */
+
+ /* Re-initialize the module, because
+ * the configuration might have changed */
+ rc = smbk5pwd_modules_init( pi );
+ if ( rc ) {
+ pi->mode = mode;
+ return 1;
+ }
+
+ } break;
+
+ default:
+ assert( 0 );
+ return 1;
+ }
+ return rc;
+}
+
+static int
+smbk5pwd_modules_init( smbk5pwd_t *pi )
+{
+ static struct {
+ const char *name;
+ AttributeDescription **adp;
+ }
+#ifdef DO_KRB5
+ krb5_ad[] = {
+ { "krb5Key", &ad_krb5Key },
+ { "krb5KeyVersionNumber", &ad_krb5KeyVersionNumber },
+ { "krb5PrincipalName", &ad_krb5PrincipalName },
+ { "krb5ValidEnd", &ad_krb5ValidEnd },
+ { NULL }
+ },
+#endif /* DO_KRB5 */
+#ifdef DO_SAMBA
+ samba_ad[] = {
+ { "sambaNTPassword", &ad_sambaNTPassword },
+ { "sambaPwdLastSet", &ad_sambaPwdLastSet },
+ { "sambaPwdMustChange", &ad_sambaPwdMustChange },
+ { "sambaPwdCanChange", &ad_sambaPwdCanChange },
+ { NULL }
+ },
+#endif /* DO_SAMBA */
+#ifdef DO_SHADOW
+ shadow_ad[] = {
+ { "shadowLastChange", &ad_shadowLastChange },
+ { NULL }
+ },
+#endif /* DO_SHADOW */
+ dummy_ad;
+
+ /* this is to silence the unused var warning */
+ (void) dummy_ad;
+
+#ifdef DO_KRB5
+ if ( SMBK5PWD_DO_KRB5( pi ) && oc_krb5KDCEntry == NULL ) {
+ krb5_error_code ret;
+ extern HDB *_kadm5_s_get_db(void *);
+
+ int i, rc;
+
+ /* Make sure all of our necessary schema items are loaded */
+ oc_krb5KDCEntry = oc_find( "krb5KDCEntry" );
+ if ( !oc_krb5KDCEntry ) {
+ Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
+ "unable to find \"krb5KDCEntry\" objectClass.\n" );
+ return -1;
+ }
+
+ for ( i = 0; krb5_ad[ i ].name != NULL; i++ ) {
+ const char *text;
+
+ *(krb5_ad[ i ].adp) = NULL;
+
+ rc = slap_str2ad( krb5_ad[ i ].name, krb5_ad[ i ].adp, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
+ "unable to find \"%s\" attributeType: %s (%d).\n",
+ krb5_ad[ i ].name, text, rc );
+ oc_krb5KDCEntry = NULL;
+ return rc;
+ }
+ }
+
+ /* Initialize Kerberos context */
+ ret = krb5_init_context(&context);
+ if (ret) {
+ Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
+ "unable to initialize krb5 context (%d).\n",
+ ret );
+ oc_krb5KDCEntry = NULL;
+ return -1;
+ }
+
+ ret = kadm5_s_init_with_password_ctx( context,
+ KADM5_ADMIN_SERVICE,
+ NULL,
+ KADM5_ADMIN_SERVICE,
+ &conf, 0, 0, &kadm_context );
+ if (ret) {
+ char *err_str, *err_msg = "<unknown error>";
+ err_str = krb5_get_error_string( context );
+ if (!err_str)
+ err_msg = (char *)krb5_get_err_text( context, ret );
+ Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
+ "unable to initialize krb5 admin context: %s (%d).\n",
+ err_str ? err_str : err_msg, ret );
+ if (err_str)
+ krb5_free_error_string( context, err_str );
+ krb5_free_context( context );
+ oc_krb5KDCEntry = NULL;
+ return -1;
+ }
+
+ db = _kadm5_s_get_db( kadm_context );
+ }
+#endif /* DO_KRB5 */
+
+#ifdef DO_SAMBA
+ if ( SMBK5PWD_DO_SAMBA( pi ) && oc_sambaSamAccount == NULL ) {
+ int i, rc;
+
+ oc_sambaSamAccount = oc_find( "sambaSamAccount" );
+ if ( !oc_sambaSamAccount ) {
+ Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
+ "unable to find \"sambaSamAccount\" objectClass.\n" );
+ return -1;
+ }
+
+ for ( i = 0; samba_ad[ i ].name != NULL; i++ ) {
+ const char *text;
+
+ *(samba_ad[ i ].adp) = NULL;
+
+ rc = slap_str2ad( samba_ad[ i ].name, samba_ad[ i ].adp, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
+ "unable to find \"%s\" attributeType: %s (%d).\n",
+ samba_ad[ i ].name, text, rc );
+ oc_sambaSamAccount = NULL;
+ return rc;
+ }
+ }
+ }
+#endif /* DO_SAMBA */
+
+#ifdef DO_SHADOW
+ if ( SMBK5PWD_DO_SHADOW( pi ) && oc_shadowAccount == NULL ) {
+ int i, rc;
+
+ oc_shadowAccount = oc_find( "shadowAccount" );
+ if ( !oc_shadowAccount ) {
+ Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
+ "unable to find \"shadowAccount\" objectClass.\n" );
+ return -1;
+ }
+
+ for ( i = 0; shadow_ad[ i ].name != NULL; i++ ) {
+ const char *text;
+
+ *(shadow_ad[ i ].adp) = NULL;
+
+ rc = slap_str2ad( shadow_ad[ i ].name, shadow_ad[ i ].adp, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "smbk5pwd: "
+ "unable to find \"%s\" attributeType: %s (%d).\n",
+ shadow_ad[ i ].name, text, rc );
+ oc_shadowAccount = NULL;
+ return rc;
+ }
+ }
+ }
+#endif /* DO_SHADOW */
+
+ return 0;
+}
+
+static int
+smbk5pwd_db_init(BackendDB *be, ConfigReply *cr)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ smbk5pwd_t *pi;
+
+ pi = ch_calloc( 1, sizeof( smbk5pwd_t ) );
+ if ( pi == NULL ) {
+ return 1;
+ }
+ on->on_bi.bi_private = (void *)pi;
+
+ return 0;
+}
+
+static int
+smbk5pwd_db_open(BackendDB *be, ConfigReply *cr)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ smbk5pwd_t *pi = (smbk5pwd_t *)on->on_bi.bi_private;
+
+ int rc;
+
+ if ( pi->mode == 0 ) {
+ pi->mode = SMBK5PWD_F_ALL;
+ }
+
+ rc = smbk5pwd_modules_init( pi );
+ if ( rc ) {
+ return rc;
+ }
+
+ return 0;
+}
+
+static int
+smbk5pwd_db_destroy(BackendDB *be, ConfigReply *cr)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ smbk5pwd_t *pi = (smbk5pwd_t *)on->on_bi.bi_private;
+
+ if ( pi ) {
+ ch_free( pi );
+ }
+
+ return 0;
+}
+
+int
+smbk5pwd_initialize(void)
+{
+ int rc;
+
+ smbk5pwd.on_bi.bi_type = "smbk5pwd";
+
+ smbk5pwd.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ smbk5pwd.on_bi.bi_db_init = smbk5pwd_db_init;
+ smbk5pwd.on_bi.bi_db_open = smbk5pwd_db_open;
+ smbk5pwd.on_bi.bi_db_destroy = smbk5pwd_db_destroy;
+
+ smbk5pwd.on_bi.bi_extended = smbk5pwd_exop_passwd;
+
+#ifdef DO_KRB5
+ smbk5pwd.on_bi.bi_op_bind = smbk5pwd_op_bind;
+
+ lutil_passwd_add( (struct berval *)&k5key_scheme, k5key_chk, k5key_hash );
+#endif
+
+ smbk5pwd.on_bi.bi_cf_ocs = smbk5pwd_cfocs;
+
+ rc = config_register_schema( smbk5pwd_cfats, smbk5pwd_cfocs );
+ if ( rc ) {
+ return rc;
+ }
+
+ return overlay_register( &smbk5pwd );
+}
+
+#if SLAPD_OVER_SMBK5PWD == SLAPD_MOD_DYNAMIC
+int init_module(int argc, char *argv[]) {
+ return smbk5pwd_initialize();
+}
+#endif
+
+#endif /* defined(SLAPD_OVER_SMBK5PWD) */
diff --git a/contrib/slapd-modules/trace/Makefile b/contrib/slapd-modules/trace/Makefile
new file mode 100644
index 0000000..1bf8a55
--- /dev/null
+++ b/contrib/slapd-modules/trace/Makefile
@@ -0,0 +1,46 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_TRACE=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = trace.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+trace.la: trace.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/trace/trace.c b/contrib/slapd-modules/trace/trace.c
new file mode 100644
index 0000000..1e61025
--- /dev/null
+++ b/contrib/slapd-modules/trace/trace.c
@@ -0,0 +1,256 @@
+/* trace.c - traces overlay invocation */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2006-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_TRACE
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "lutil.h"
+
+static int
+trace_op2str( Operation *op, char **op_strp )
+{
+ switch ( op->o_tag ) {
+ case LDAP_REQ_BIND:
+ *op_strp = "BIND";
+ break;
+
+ case LDAP_REQ_UNBIND:
+ *op_strp = "UNBIND";
+ break;
+
+ case LDAP_REQ_SEARCH:
+ *op_strp = "SEARCH";
+ break;
+
+ case LDAP_REQ_MODIFY:
+ *op_strp = "MODIFY";
+ break;
+
+ case LDAP_REQ_ADD:
+ *op_strp = "ADD";
+ break;
+
+ case LDAP_REQ_DELETE:
+ *op_strp = "DELETE";
+ break;
+
+ case LDAP_REQ_MODRDN:
+ *op_strp = "MODRDN";
+ break;
+
+ case LDAP_REQ_COMPARE:
+ *op_strp = "COMPARE";
+ break;
+
+ case LDAP_REQ_ABANDON:
+ *op_strp = "ABANDON";
+ break;
+
+ case LDAP_REQ_EXTENDED:
+ *op_strp = "EXTENDED";
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+ return 0;
+}
+
+static int
+trace_op_func( Operation *op, SlapReply *rs )
+{
+ char *op_str = NULL;
+
+ (void)trace_op2str( op, &op_str );
+
+ switch ( op->o_tag ) {
+ case LDAP_REQ_EXTENDED:
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "%s trace op=EXTENDED dn=\"%s\" reqoid=%s\n",
+ op->o_log_prefix,
+ BER_BVISNULL( &op->o_req_ndn ) ? "(null)" : op->o_req_ndn.bv_val,
+ BER_BVISNULL( &op->ore_reqoid ) ? "" : op->ore_reqoid.bv_val );
+ break;
+
+ default:
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "%s trace op=%s dn=\"%s\"\n",
+ op->o_log_prefix, op_str,
+ BER_BVISNULL( &op->o_req_ndn ) ? "(null)" : op->o_req_ndn.bv_val );
+ break;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+trace_response( Operation *op, SlapReply *rs )
+{
+ char *op_str = NULL;
+
+ (void)trace_op2str( op, &op_str );
+
+ switch ( op->o_tag ) {
+ case LDAP_REQ_EXTENDED:
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "%s trace op=EXTENDED RESPONSE dn=\"%s\" reqoid=%s rspoid=%s err=%d\n",
+ op->o_log_prefix,
+ BER_BVISNULL( &op->o_req_ndn ) ? "(null)" : op->o_req_ndn.bv_val,
+ BER_BVISNULL( &op->ore_reqoid ) ? "" : op->ore_reqoid.bv_val,
+ rs->sr_rspoid == NULL ? "" : rs->sr_rspoid,
+ rs->sr_err );
+ break;
+
+ case LDAP_REQ_SEARCH:
+ switch ( rs->sr_type ) {
+ case REP_SEARCH:
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "%s trace op=SEARCH ENTRY dn=\"%s\"\n",
+ op->o_log_prefix,
+ rs->sr_entry->e_name.bv_val );
+ goto done;
+
+ case REP_SEARCHREF:
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "%s trace op=SEARCH REFERENCE ref=\"%s\"\n",
+ op->o_log_prefix,
+ rs->sr_ref[ 0 ].bv_val );
+ goto done;
+
+ case REP_RESULT:
+ break;
+
+ default:
+ assert( 0 );
+ }
+ /* fallthru */
+
+ default:
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "%s trace op=%s RESPONSE dn=\"%s\" err=%d\n",
+ op->o_log_prefix,
+ op_str,
+ BER_BVISNULL( &op->o_req_ndn ) ? "(null)" : op->o_req_ndn.bv_val,
+ rs->sr_err );
+ break;
+ }
+
+done:;
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+trace_db_init( BackendDB *be, ConfigReply *cr )
+{
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "trace DB_INIT\n" );
+
+ return 0;
+}
+
+static int
+trace_db_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv )
+{
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "trace DB_CONFIG argc=%d argv[0]=\"%s\"\n",
+ argc, argv[ 0 ] );
+
+ return 0;
+}
+
+static int
+trace_db_open( BackendDB *be, ConfigReply *cr )
+{
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "trace DB_OPEN\n" );
+
+ return 0;
+}
+
+static int
+trace_db_close( BackendDB *be, ConfigReply *cr )
+{
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "trace DB_CLOSE\n" );
+
+ return 0;
+}
+
+static int
+trace_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_INFO,
+ "trace DB_DESTROY\n" );
+
+ return 0;
+}
+
+static slap_overinst trace;
+
+int
+trace_initialize()
+{
+ trace.on_bi.bi_type = "trace";
+
+ trace.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ trace.on_bi.bi_db_init = trace_db_init;
+ trace.on_bi.bi_db_open = trace_db_open;
+ trace.on_bi.bi_db_config = trace_db_config;
+ trace.on_bi.bi_db_close = trace_db_close;
+ trace.on_bi.bi_db_destroy = trace_db_destroy;
+
+ trace.on_bi.bi_op_add = trace_op_func;
+ trace.on_bi.bi_op_bind = trace_op_func;
+ trace.on_bi.bi_op_unbind = trace_op_func;
+ trace.on_bi.bi_op_compare = trace_op_func;
+ trace.on_bi.bi_op_delete = trace_op_func;
+ trace.on_bi.bi_op_modify = trace_op_func;
+ trace.on_bi.bi_op_modrdn = trace_op_func;
+ trace.on_bi.bi_op_search = trace_op_func;
+ trace.on_bi.bi_op_abandon = trace_op_func;
+ trace.on_bi.bi_extended = trace_op_func;
+
+ trace.on_response = trace_response;
+
+ return overlay_register( &trace );
+}
+
+#if SLAPD_OVER_TRACE == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return trace_initialize();
+}
+#endif /* SLAPD_OVER_TRACE == SLAPD_MOD_DYNAMIC */
+
+#endif /* defined(SLAPD_OVER_TRACE) */
diff --git a/contrib/slapd-modules/usn/Makefile b/contrib/slapd-modules/usn/Makefile
new file mode 100644
index 0000000..d7af04b
--- /dev/null
+++ b/contrib/slapd-modules/usn/Makefile
@@ -0,0 +1,46 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_USN=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = usn.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+usn.la: usn.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/usn/README b/contrib/slapd-modules/usn/README
new file mode 100644
index 0000000..3bfb096
--- /dev/null
+++ b/contrib/slapd-modules/usn/README
@@ -0,0 +1,44 @@
+This directory contains a slapd overlay, usn, that extends slapd
+to maintain the usnCreated and usnChanged operational attributes
+normally used by Microsoft ActiveDirectory.
+
+To use the overlay, add:
+
+ moduleload <path to>usn.so
+ ...
+
+ database mdb
+ ...
+ overlay usn
+
+to your slapd configuration file. The schema definitions for the
+two USN attributes are hardcoded in this overlay.
+
+Use Makefile to compile this plugin or use a command line similar to:
+
+ gcc -c -I ../../include/ -I ../../servers/slapd -DSLAPD_OVER_USN=SLAPD_MOD_DYNAMIC usn.c
+ gcc -shared -o usn.so usn.o
+
+This overlay is only set up to be built as a dynamically loaded module.
+On most platforms, in order for the module to be usable, all of the
+library dependencies must also be available as shared libraries.
+
+If you need to build the overlay statically, you will have to move it into the
+slapd/overlays directory and edit the Makefile and overlays.c to reference
+it. You will also have to define SLAPD_OVER_USN to SLAPD_MOD_STATIC,
+and add the relevant libraries to the main slapd link command.
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2007-2022 The OpenLDAP Foundation.
+Portions Copyright 2007 Howard Chu, Symas Corp. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
diff --git a/contrib/slapd-modules/usn/usn.c b/contrib/slapd-modules/usn/usn.c
new file mode 100644
index 0000000..abd6d13
--- /dev/null
+++ b/contrib/slapd-modules/usn/usn.c
@@ -0,0 +1,330 @@
+/* usn.c - Maintain Microsoft-style Update Sequence Numbers */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_USN
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "slap-config.h"
+
+/* This overlay intercepts write operations and adds a Microsoft-style
+ * USN to the target entry.
+ */
+
+typedef struct usn_info {
+ int ui_current;
+ ldap_pvt_thread_mutex_t ui_mutex;
+} usn_info_t;
+
+static AttributeDescription *ad_usnCreated, *ad_usnChanged;
+
+static struct {
+ char *desc;
+ AttributeDescription **adp;
+} as[] = {
+ { "( 1.2.840.113556.1.2.19 "
+ "NAME 'uSNCreated' "
+ "SYNTAX '1.2.840.113556.1.4.906' "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION )",
+ &ad_usnCreated },
+ { "( 1.2.840.113556.1.2.120 "
+ "NAME 'uSNChanged' "
+ "SYNTAX '1.2.840.113556.1.4.906' "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION )",
+ &ad_usnChanged },
+ { NULL }
+};
+
+static int
+usn_func( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ usn_info_t *ui = on->on_bi.bi_private;
+ int my_usn;
+ char intbuf[64];
+ struct berval bv[2];
+
+ ldap_pvt_thread_mutex_lock( &ui->ui_mutex );
+ ui->ui_current++;
+ my_usn = ui->ui_current;
+ ldap_pvt_thread_mutex_unlock( &ui->ui_mutex );
+
+ BER_BVZERO(&bv[1]);
+ bv[0].bv_val = intbuf;
+ bv[0].bv_len = snprintf( intbuf, sizeof(intbuf), "%d", my_usn );
+ switch(op->o_tag) {
+ case LDAP_REQ_ADD:
+ attr_merge( op->ora_e, ad_usnCreated, bv, NULL );
+ attr_merge( op->ora_e, ad_usnChanged, bv, NULL );
+ break;
+ case LDAP_REQ_DELETE:
+ /* Probably need to update root usnLastObjRem */
+ break;
+ default: {
+ /* Modify, ModDN */
+ Modifications *ml, *mod = ch_calloc( sizeof( Modifications ), 1 );
+ for ( ml = op->orm_modlist; ml && ml->sml_next; ml = ml->sml_next );
+ ml->sml_next = mod;
+ mod->sml_desc = ad_usnChanged;
+ mod->sml_numvals = 1;
+ value_add_one( &mod->sml_values, &bv[0] );
+ mod->sml_nvalues = NULL;
+ mod->sml_op = LDAP_MOD_REPLACE;
+ mod->sml_flags = 0;
+ mod->sml_next = NULL;
+ break;
+ }
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+usn_operational(
+ Operation *op,
+ SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ usn_info_t *ui = (usn_info_t *)on->on_bi.bi_private;
+
+ if ( rs->sr_entry &&
+ dn_match( &rs->sr_entry->e_nname, op->o_bd->be_nsuffix )) {
+
+ if ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
+ ad_inlist( ad_usnChanged, rs->sr_attrs )) {
+ Attribute *a, **ap = NULL;
+ char intbuf[64];
+ struct berval bv;
+ int my_usn;
+
+ for ( a=rs->sr_entry->e_attrs; a; a=a->a_next ) {
+ if ( a->a_desc == ad_usnChanged )
+ break;
+ }
+
+ if ( !a ) {
+ for ( ap = &rs->sr_operational_attrs; *ap;
+ ap=&(*ap)->a_next );
+
+ a = attr_alloc( ad_usnChanged );
+ *ap = a;
+ }
+
+ if ( !ap ) {
+ if ( rs_entry2modifiable( op,rs, on )) {
+ a = attr_find( rs->sr_entry->e_attrs,
+ ad_usnChanged );
+ }
+ ber_bvarray_free( a->a_vals );
+ a->a_vals = NULL;
+ a->a_numvals = 0;
+ }
+ ldap_pvt_thread_mutex_lock( &ui->ui_mutex );
+ my_usn = ui->ui_current;
+ ldap_pvt_thread_mutex_unlock( &ui->ui_mutex );
+ bv.bv_len = snprintf( intbuf, sizeof(intbuf), "%d", my_usn );
+ bv.bv_val = intbuf;
+ attr_valadd( a, &bv, NULL, 1 );
+ }
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+/* Read the old USN from the underlying DB. This code is
+ * stolen from the syncprov overlay.
+ */
+static int
+usn_db_open(
+ BackendDB *be,
+ ConfigReply *cr)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ usn_info_t *ui = (usn_info_t *)on->on_bi.bi_private;
+
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+ Operation *op;
+ Entry *e = NULL;
+ Attribute *a;
+ int rc;
+ void *thrctx = NULL;
+
+ thrctx = ldap_pvt_thread_pool_context();
+ connection_fake_init( &conn, &opbuf, thrctx );
+ op = &opbuf.ob_op;
+ op->o_bd = be;
+ op->o_dn = be->be_rootdn;
+ op->o_ndn = be->be_rootndn;
+
+ rc = overlay_entry_get_ov( op, be->be_nsuffix, NULL,
+ slap_schema.si_ad_contextCSN, 0, &e, on );
+
+ if ( e ) {
+ a = attr_find( e->e_attrs, ad_usnChanged );
+ if ( a ) {
+ ui->ui_current = atoi( a->a_vals[0].bv_val );
+ }
+ overlay_entry_release_ov( op, e, 0, on );
+ }
+ return 0;
+}
+
+static int
+usn_db_init(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ usn_info_t *ui;
+
+ if ( SLAP_ISGLOBALOVERLAY( be ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "usn must be instantiated within a database.\n" );
+ return 1;
+ }
+
+ ui = ch_calloc(1, sizeof(usn_info_t));
+ ldap_pvt_thread_mutex_init( &ui->ui_mutex );
+ on->on_bi.bi_private = ui;
+ return 0;
+}
+
+static int
+usn_db_close(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ usn_info_t *ui = on->on_bi.bi_private;
+ Connection conn = {0};
+ OperationBuffer opbuf;
+ Operation *op;
+ SlapReply rs = {REP_RESULT};
+ void *thrctx;
+
+ Modifications mod;
+ slap_callback cb = {0};
+ char intbuf[64];
+ struct berval bv[2];
+
+ thrctx = ldap_pvt_thread_pool_context();
+ connection_fake_init( &conn, &opbuf, thrctx );
+ op = &opbuf.ob_op;
+ op->o_bd = be;
+ BER_BVZERO( &bv[1] );
+ bv[0].bv_len = snprintf( intbuf, sizeof(intbuf), "%d", ui->ui_current );
+ bv[0].bv_val = intbuf;
+ mod.sml_numvals = 1;
+ mod.sml_values = bv;
+ mod.sml_nvalues = NULL;
+ mod.sml_desc = ad_usnChanged;
+ mod.sml_op = LDAP_MOD_REPLACE;
+ mod.sml_flags = 0;
+ mod.sml_next = NULL;
+
+ cb.sc_response = slap_null_cb;
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->o_callback = &cb;
+ op->orm_modlist = &mod;
+ op->orm_no_opattrs = 1;
+ op->o_dn = be->be_rootdn;
+ op->o_ndn = be->be_rootndn;
+ op->o_req_dn = op->o_bd->be_suffix[0];
+ op->o_req_ndn = op->o_bd->be_nsuffix[0];
+ op->o_bd->bd_info = on->on_info->oi_orig;
+ op->o_managedsait = SLAP_CONTROL_NONCRITICAL;
+ op->o_no_schema_check = 1;
+ op->o_bd->be_modify( op, &rs );
+ if ( mod.sml_next != NULL ) {
+ slap_mods_free( mod.sml_next, 1 );
+ }
+ return 0;
+}
+
+static int
+usn_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ usn_info_t *ui = on->on_bi.bi_private;
+
+ ldap_pvt_thread_mutex_destroy( &ui->ui_mutex );
+ ch_free( ui );
+ on->on_bi.bi_private = NULL;
+ return 0;
+}
+
+/* This overlay is set up for dynamic loading via moduleload. For static
+ * configuration, you'll need to arrange for the slap_overinst to be
+ * initialized and registered by some other function inside slapd.
+ */
+
+static slap_overinst usn;
+
+int
+usn_init( void )
+{
+ int i, code;
+
+ memset( &usn, 0, sizeof( slap_overinst ) );
+ usn.on_bi.bi_type = "usn";
+ usn.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ usn.on_bi.bi_db_init = usn_db_init;
+ usn.on_bi.bi_db_destroy = usn_db_destroy;
+ usn.on_bi.bi_db_open = usn_db_open;
+ usn.on_bi.bi_db_close = usn_db_close;
+
+ usn.on_bi.bi_op_modify = usn_func;
+ usn.on_bi.bi_op_modrdn = usn_func;
+ usn.on_bi.bi_op_add = usn_func;
+ usn.on_bi.bi_op_delete = usn_func;
+ usn.on_bi.bi_operational = usn_operational;
+
+ for ( i = 0; as[i].desc; i++ ) {
+ code = register_at( as[i].desc, as[i].adp, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "usn_init: register_at #%d failed\n", i );
+ return code;
+ }
+ }
+ return overlay_register( &usn );
+}
+
+#if SLAPD_OVER_USN == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return usn_init();
+}
+#endif /* SLAPD_OVER_USN == SLAPD_MOD_DYNAMIC */
+
+#endif /* defined(SLAPD_OVER_USN) */
diff --git a/contrib/slapd-modules/variant/Makefile b/contrib/slapd-modules/variant/Makefile
new file mode 100644
index 0000000..07effed
--- /dev/null
+++ b/contrib/slapd-modules/variant/Makefile
@@ -0,0 +1,77 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+# Copyright 2017 Ondřej Kuzník, Symas Corp. All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted only as authorized by the OpenLDAP
+# Public License.
+#
+# A copy of this license is available in the file LICENSE in the
+# top-level directory of the distribution or, alternatively, at
+# <http://www.OpenLDAP.org/license.html>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+SRCDIR = ./
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_VARIANT=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = variant.la
+MANPAGES = slapo-variant.5
+CLEAN = *.o *.lo *.la .libs
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+all: $(PROGRAMS)
+
+d :=
+sp :=
+dir := tests
+include $(dir)/Rules.mk
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+variant.la: variant.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf $(CLEAN)
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/variant/slapo-variant.5 b/contrib/slapd-modules/variant/slapo-variant.5
new file mode 100644
index 0000000..a480744
--- /dev/null
+++ b/contrib/slapd-modules/variant/slapo-variant.5
@@ -0,0 +1,472 @@
+.TH SLAPO-VARIANT 5 "RELEASEDATE" "OpenLDAP"
+.\" Copyright 2016-2017 Symas Corp. All Rights Reserved.
+.\" Copying restrictions apply. See LICENSE.
+.SH NAME
+slapo\-variant \- share values between entries
+.SH SYNOPSIS
+olcOverlay=variant
+.SH DESCRIPTION
+The
+.B variant
+overlay to
+.BR slapd (8)
+allows attributes/values to be shared between several entries. In some ways
+this is similar to
+.BR slapo-collect (5)
+with the exception that the source and target attributes can be different.
+.LP
+The overlay operates on configured
+.B variant
+entries which can have several
+.B attributes
+each configured to borrow values from an attribute in the
+.B alternate
+entry.
+.LP
+Two types of
+.B variant
+entries can be configured,
+.B regular
+and
+.BR regex ,
+where the latter are configured with a regular expression and patterns to
+locate each alternate entry, with access to the variant DN and first nine
+submatches captured by the regular expression.
+.LP
+For most purposes (see
+.BR LIMITATIONS ,
+especially for
+.B regex
+variants), the resulting entry is completely transparent to the operations
+performed on it, e.g. a modify operation on the
+.B variant
+attribute gets transformed
+into an operation on the
+.B alternate
+entry+attribute. As such, the usual ACL rules apply, appropriate
+access to both the
+.B variant
+and
+.B alternate
+entry is checked.
+.LP
+As a special case,
+.B Add
+and
+.B Delete
+operations will not affect the
+.B alternate
+entries. Should an attempt be made to add a configured
+.B variant
+entry with the
+.B variant
+attributes already populated, the operation will be rejected with a
+.B Constraint
+.BR Violation .
+
+.SH CONFIGURATION LAYOUT
+
+The overlay has to be instantiated under a database adding an entry of
+.B olcOverlay=variant
+with objectClass of
+.BR olcVariantConfig .
+
+The overlay configuration subtree consists of the following levels:
+.RS
+.TP
+.B objectClass=olcVariantConfig
+Main overlay configuration. Created directly under the database
+configuration entry.
+.TP
+.B objectClass=olcVariantVariant
+Specifies a
+.B regular variant
+entry and must be a child of an entry with
+.BR objectClass=olcVariantConfig .
+There may be as many such entries as necessary provided they all specify a
+different DN in the
+.BR olcVariantEntry
+attribute.
+.TP
+.B objectClass=olcVariantAttribute
+Specifies a
+.B regular variant
+attribute together with information where the
+.B alternate
+attribute is stored. Must be a child of an entry with
+.BR objectClass=olcVariantVariant .
+There may be as many such entries as necessary provided they all specify a
+different attribute in
+.BR olcVariantVariantAttribute .
+.TP
+.B objectClass=olcVariantRegex
+Specifies a
+.B regex variant
+entry and must be a child of an entry with
+.BR objectClass=olcVariantConfig .
+There may be as many such entries as necessary provided they all specify a
+different DN in the
+.BR olcVariantEntryRegex
+attribute.
+.TP
+.B objectClass=olcVariantAttributePattern
+Specifies a
+.B regex variant
+attribute together with information where the
+.B alternate
+attribute is stored. Must be a child of an entry with
+.BR objectClass=olcVariantRegex .
+There may be as many such entries as necessary provided they all specify a
+different attribute in
+.BR olcVariantVariantAttribute .
+.RE
+
+In the case of
+.BR slapd.conf (5),
+the variant definition is delimited by the keyword
+.B variantDN
+followed by an arbitrary number of
+.B variantSpec
+providing the attribute definitions following it. Each new
+.B variantDN
+line starts configuring a new variant.
+
+.SH OVERLAY CONFIGURATION ENTRY
+
+The top entry
+.RB ( olcVariantConfig )
+has the following options available:
+
+.RS
+.TP
+.B olcVariantPassReplication: TRUE | FALSE
+If set to
+.BR TRUE ,
+.B search
+operations with the
+.B SyncReplication
+control will be passed unchanged so that replication can be unaffected.
+Defaults to
+.B FALSE
+while unset. The
+.BR slapd.conf (5)
+equivalent is
+.BR passReplication .
+.RE
+
+.SH VARIANT CONFIGURATION ENTRY
+
+The
+.B regular variant entry
+configuration
+.RB ( olcVariantVariant )
+has the following options available:
+
+.RS
+.TP
+.B olcVariantEntry: <dn>
+Mandatory attribute, indicates that the named entry is to be treated as a
+.B variant
+entry. The
+.BR slapd.conf (5)
+equivalent is
+.BR variantDN .
+.TP
+.B name: <reference>
+Name of the entry for reference, usually the attribute present in the
+configuration entry's RDN. There is no
+.BR slapd.conf (5)
+equivalent as this has no effect on the overlay operation.
+.RE
+
+Similarly, the
+.B regex variant entry
+configuration
+.RB ( olcVariantRegex )
+has these options available:
+
+.RS
+.TP
+.B olcVariantRegex: <regex>
+Mandatory attribute, indicates that the entries whose normalised DN matches is
+to be treated as a
+.B regex variant
+entry. The (POSIX.2) regex can use submatches to capture parts of the DN for
+later use in locating the
+.B alternative
+.BR entry .
+The
+.BR slapd.conf (5)
+equivalent is
+.BR variantRegex .
+.TP
+.B name: <reference>
+Name of the entry for reference, usually the attribute present in the
+configuration entry's RDN. There is no
+.BR slapd.conf (5)
+equivalent as this has no effect on the overlay operation.
+.RE
+
+.SH CONFIGURATION PRECEDENCE
+
+While several
+.B regex variants
+can match the same entry, only one can apply at a time. The list of the
+.B regular variants
+is checked first. Should none match, the list of
+.B regex variants
+is checked in the order they have been configured using only the first one that
+matches.
+
+.SH VARIANT ATTRIBUTE CONFIGURATION ENTRY
+
+The
+.B regular variant
+attribute configuration
+.RB ( olcVariantAttribute )
+and
+.B regex variant
+attribute configuration
+.RB ( olcVariantAttributePattern )
+have the following options available:
+
+.RS
+.TP
+.B name: <reference>
+Name of the attribute configuration for reference and/or documentation, if
+present, usually found in the configuration entry's RDN. There is no
+.BR slapd.conf (5)
+equivalent as this has no effect on the overlay operation.
+.TP
+.B olcVariantVariantAttribute: <attr>
+Mandatory attribute, indicates that the named attribute is not present in
+the
+.B variant
+entry but is to be retrieved from the
+.B alternate
+entry.
+.TP
+.B olcVariantAlternativeAttribute: <attr>
+Mandatory attribute, indicates that the values of the named attribute is to
+be retrieved from the
+.B alternate
+entry for use as the values of the
+.B variant
+attribute. The syntaxes of the corresponding
+.B variant
+and
+.B alternate
+attributes have to match or the configuration will be rejected.
+.TP
+.B olcVariantAlternativeEntry: <dn>
+Attribute mandatory for
+.B regular
+.BR variants ,
+indicates the
+.B alternate
+entry to use when retrieving the attribute from.
+.TP
+.B olcVariantAlternativeEntryPattern: <pattern>
+Attribute mandatory for
+.B regex
+.BR variants ,
+indicates the
+.B alternate
+entry to use when retrieving the attribute from. Substitution patterns
+.RB ( $n )
+can be used to insert parts of the variant entry's DN.
+.B $0
+will place the entire variant DN,
+.B $1
+to
+.B $9
+can be used to place respective capture patterns from the
+.B variant
+entry.
+.TP
+.B variantSpec <attr> <attr2> <dn>
+.BR slapd.conf (5)
+only. The equivalent to options above, where
+.B <attr>
+represents the
+.BR olcVariantVariantAttribute ,
+.B <attr2>
+represents the
+.B olcVariantAlternativeAttribute
+and
+.B <dn>
+has the same meaning as the content of
+.BR olcVariantAlternativeEntry .
+Has to follow a
+.B variantDN
+line in the overlay's configuration.
+.TP
+.B variantRegexSpec <attr> <attr2> <pattern>
+.BR slapd.conf (5)
+only. The equivalent to options above, where
+.B <attr>
+represents the
+.BR olcVariantVariantAttribute ,
+.B <attr2>
+represents the
+.B olcVariantAlternativeAttribute
+and
+.B <pattern>
+has the same meaning as the content of
+.BR olcVariantAlternativeEntryPattern .
+Has to follow a
+.B variantRegex
+line in the overlay's configuration.
+.RE
+
+.SH EXAMPLE
+
+The following is an example of a configured overlay, substitute
+.B $DATABASE
+for the DN of the database it is attached to and
+.B {x}
+with the desired position of the overlay in the overlay stack.
+
+.nf
+dn: olcOverlay={x}variant,$DATABASE
+objectClass: olcVariantConfig
+olcOverlay: variant
+# Let replication requests pass through unmodified
+olcVariantPassReplication: TRUE
+
+# when an operation considers dc=example,dc=com
+dn: name=example,olcOverlay={x}variant,$DATABASE
+objectClass: olcVariantVariant
+olcVariantEntry: dc=example,dc=com
+
+# share the Headquarters' address as the company address
+dn: olcVariantVariantAttribute=postaladdress,name={0}example,olcOverlay={x}variant,$DATABASE
+objectClass: olcVariantVariantAttribute
+olcVariantVariantAttribute: postaladdress
+olcVariantAlternativeAttribute: postaladdress
+olcVariantAlternativeEntry: ou=Headquarters,dc=example,dc=com
+
+# populate telephonenumber from CEO's home phone
+dn: name=Take phone from CEO entry,name={0}example,olcOverlay={x}variant,$DATABASE
+objectClass: olcVariantVariantAttribute
+olcVariantVariantAttribute: telephonenumber
+olcVariantAlternativeAttribute: homephone
+olcVariantAlternativeEntry: cn=John Doe,ou=People,dc=example,dc=com
+
+# Match all entries with example in the DN
+#
+# It will not match dc=example,dc=com as that's already configured as a regular
+# variant
+dn: name=example 2,olcOverlay={x}variant,$DATABASE
+objectClass: olcVariantRegex
+olcVariantEntryRegex: .*example[^,]*,(.*)
+
+dn: olcVariantVariantAttribute=location,name={1}example 2,olcOverlay={x}variant,$DATABASE
+objectClass: olcVariantAttributePattern
+olcVariantVariantAttribute: location
+olcVariantAlternativeAttribute: location
+olcVariantAlternativeEntryPattern: ou=object with location,$1
+.fi
+
+The
+.BR slapd.conf (5)
+equivalent of the above follows (note that the converted
+.B cn=config
+will differ in the first variant attribute configuration entry):
+
+.nf
+overlay variant
+passReplication TRUE
+
+variantDN dc=example,dc=com
+variantSpec telephonenumber homephone "cn=John Doe,ou=People,dc=example,dc=com"
+variantSpec postaladdress postaladdress ou=Headquarters,dc=example,dc=com
+
+variantRegex .*example[^,]*,(.*)
+variantRegexSpec location location "ou=object with location,$1"
+.fi
+
+.SH REPLICATION
+
+There are two ways that a database with
+.BR slapo-variant (5)
+might be replicated, either replicating the data as stored in the database,
+or as seen by the clients interacting with the server.
+
+The former can be achieved by setting the overlay option
+.B olcVariantPassReplication
+on the provider and configuring
+.BR slapo-syncprov (5)
+to appear before (with a lower index than)
+.BR slapo-variant (5).
+This is the preferred way and the only to work with
+.B regex variants
+or support multi-provider replication,
+but care must be taken to configure
+.BR slapo-variant (5)
+correctly on each replica.
+
+The latter is mostly possible by keeping the option
+.B olcVariantPassReplication
+set to
+.B FALSE
+on the provider and configuring
+.BR slapo-syncprov (5)
+to appear after (with a higher index than)
+.BR slapo-variant (5).
+However, it will only really work for replication set-ups that do not
+utilise
+.B regex
+.BR variants ,
+delta-replication nor the refresh and persist mode and is therefore
+discouraged.
+
+.SH LIMITATIONS
+For
+.B regex
+.BR variants ,
+the
+.B Search
+operation will only apply if the search scope is set to
+.BR base .
+
+The
+.B ModRDN
+operation is not currently handled and will always modify only the entry in
+question, not the configured
+.B alternate
+entry.
+
+The
+.B Modify
+operation is not atomic with respect to the alternate entries. Currently,
+the overlay processes the operations on the entry, sends the result message
+and, if successful, starts modifying the
+.B alternate
+entries accordingly.
+There is currently no support to indicate whether modifications to the
+.B alternate
+entries have been successful or whether they have finished.
+
+The only control explicitly handled is the
+.B SyncReplication
+control if enabled through the
+.B olcVariantPassReplication
+setting, adding any controls to an operation that is handled by the overlay
+might lead to unexpected behaviour and is therefore discouraged.
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.TP
+ETCDIR/slapd.d
+default slapd configuration directory
+.SH SEE ALSO
+.BR slapd-config (5),
+.BR slapd.conf (5),
+.BR slapd.overlays (5),
+.BR regex (7),
+.BR slapd (8)
+.SH ACKNOWLEDGEMENTS
+This module was developed in 2016-2017 by Ondřej Kuzník for Symas Corp.
diff --git a/contrib/slapd-modules/variant/tests/Rules.mk b/contrib/slapd-modules/variant/tests/Rules.mk
new file mode 100644
index 0000000..c25c1d2
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/Rules.mk
@@ -0,0 +1,23 @@
+sp := $(sp).x
+dirstack_$(sp) := $(d)
+d := $(dir)
+
+.PHONY: test
+
+CLEAN += clients servers tests/progs tests/schema tests/testdata tests/testrun
+
+test: all clients servers tests/progs
+
+test:
+ cd tests; \
+ SRCDIR=$(abspath $(LDAP_SRC)) \
+ LDAP_BUILD=$(abspath $(LDAP_BUILD)) \
+ TOPDIR=$(abspath $(SRCDIR)) \
+ LIBTOOL=$(abspath $(LIBTOOL)) \
+ $(abspath $(SRCDIR))/tests/run all
+
+servers clients tests/progs:
+ ln -s $(abspath $(LDAP_BUILD))/$@ $@
+
+d := $(dirstack_$(sp))
+sp := $(basename $(sp))
diff --git a/contrib/slapd-modules/variant/tests/data/additional-config.ldif b/contrib/slapd-modules/variant/tests/data/additional-config.ldif
new file mode 100644
index 0000000..6a286fe
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/additional-config.ldif
@@ -0,0 +1,23 @@
+dn: name={4}test002,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantVariant
+olcVariantEntry: cn=Gern Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+
+dn: name=attribute 1,name={4}test002,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantVariantAttribute: cn
+olcVariantAlternativeAttribute: description
+olcVariantAlternativeEntry: dc=example,dc=com
+
+dn: name=attribute 2,name={4}test002,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantVariantAttribute: pager
+olcVariantAlternativeAttribute: telephonenumber
+olcVariantAlternativeEntry: dc=example,dc=com
+
+dn: name={0}attribute 1,name={4}test002,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcVariantVariantAttribute
+olcVariantVariantAttribute: description
diff --git a/contrib/slapd-modules/variant/tests/data/config.ldif b/contrib/slapd-modules/variant/tests/data/config.ldif
new file mode 100644
index 0000000..6e323b9
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/config.ldif
@@ -0,0 +1,89 @@
+dn: olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectclass: olcVariantConfig
+
+dn: olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcVariantPassReplication
+olcVariantPassReplication: TRUE
+
+dn: name={0}variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantVariant
+olcVariantEntry: ou=People,dc=example,dc=com
+
+# a basic variant
+dn: olcVariantVariantAttribute=description,name={0}variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: description
+olcVariantAlternativeEntry: dc=example,dc=com
+
+# a nonexistent alternate
+dn: olcVariantVariantAttribute=seealso,name={0}variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: seealso
+olcVariantAlternativeEntry: ou=Societies,dc=example,dc=com
+
+dn: name={1}variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantVariant
+olcVariantEntry: ou=Groups,dc=example,dc=com
+
+# recursive retrieval is not done
+dn: olcVariantVariantAttribute=description,name={1}variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: description
+olcVariantAlternativeEntry: ou=People,dc=example,dc=com
+
+# a variant taking data from a different attribute (after the changes below)
+dn: olcVariantVariantAttribute=st,name={1}variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: st
+olcVariantAlternativeEntry: cn=Manager,dc=example,dc=com
+
+# configuration changes
+dn: olcVariantVariantAttribute={1}st,name={1}variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcVariantAlternativeAttribute
+olcVariantAlternativeAttribute: ou
+-
+replace: olcVariantAlternativeEntry
+olcVariantAlternativeEntry: ou=Alumni Association,ou=People,dc=example,dc=com
+-
+
+# a regex variant
+dn: name={2}regex,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantRegex
+olcVariantEntryRegex: (.*),(ou=.*technology.*)(,)dc=example,dc=com
+
+dn: olcVariantVariantAttribute=ou,name={2}regex,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttributePattern
+olcVariantAlternativeAttribute: ou
+olcVariantAlternativeEntryPattern: $2$3dc=example$3dc=com
+
+# Duplicate description into title
+dn: olcVariantVariantAttribute=title,name={2}regex,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttributePattern
+olcVariantAlternativeAttribute: description
+olcVariantAlternativeEntryPattern: $0
+
+# everything
+dn: name={3}regex,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantRegex
+olcVariantEntryRegex: .*
+
+dn: olcVariantVariantAttribute=l,name={3}regex,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttributePattern
+olcVariantAlternativeAttribute: l
+olcVariantAlternativeEntryPattern: dc=example,dc=com
+
diff --git a/contrib/slapd-modules/variant/tests/data/hidden.ldif b/contrib/slapd-modules/variant/tests/data/hidden.ldif
new file mode 100644
index 0000000..d219746
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/hidden.ldif
@@ -0,0 +1,4 @@
+dn: ou=Groups,dc=example,dc=com
+changetype: modify
+add: description
+description: This is hidden by the overlay config
diff --git a/contrib/slapd-modules/variant/tests/data/test001-01-same-dn.ldif b/contrib/slapd-modules/variant/tests/data/test001-01-same-dn.ldif
new file mode 100644
index 0000000..880e035
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test001-01-same-dn.ldif
@@ -0,0 +1,4 @@
+dn: name=variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantVariant
+olcVariantEntry: ou=Groups,dc=example,dc=com
diff --git a/contrib/slapd-modules/variant/tests/data/test001-01a-same-dn.ldif b/contrib/slapd-modules/variant/tests/data/test001-01a-same-dn.ldif
new file mode 100644
index 0000000..0fb8b2b
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test001-01a-same-dn.ldif
@@ -0,0 +1,4 @@
+dn: name={0}variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcVariantEntry
+olcVariantEntry: ou=Groups,dc=example,dc=com
diff --git a/contrib/slapd-modules/variant/tests/data/test001-02-same-attribute.ldif b/contrib/slapd-modules/variant/tests/data/test001-02-same-attribute.ldif
new file mode 100644
index 0000000..8447018
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test001-02-same-attribute.ldif
@@ -0,0 +1,6 @@
+dn: olcVariantAlternativeAttribute=description,name={1}variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantVariantAttribute: description
+olcVariantAlternativeAttribute: description
+olcVariantAlternativeEntry: ou=People,dc=example,dc=com
diff --git a/contrib/slapd-modules/variant/tests/data/test001-03-different-types.ldif b/contrib/slapd-modules/variant/tests/data/test001-03-different-types.ldif
new file mode 100644
index 0000000..dfbde5b
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test001-03-different-types.ldif
@@ -0,0 +1,4 @@
+dn: olcVariantVariantAttribute={1}st,name={1}variant,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: modify
+replace: olcVariantAlternativeAttribute
+olcVariantAlternativeAttribute: userPassword
diff --git a/contrib/slapd-modules/variant/tests/data/test002-01-entry.ldif b/contrib/slapd-modules/variant/tests/data/test002-01-entry.ldif
new file mode 100644
index 0000000..21b5b14
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test002-01-entry.ldif
@@ -0,0 +1,16 @@
+dn: cn=Gern Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+changetype: add
+objectclass: testPerson
+cn: Gern Jensen
+sn: Jensen
+uid: gjensen
+postaladdress: ITD $ 535 W. William St $ Anytown, MI 48103
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Coffee
+homepostaladdress: 844 Brown St. Apt. 4 $ Anytown, MI 48104
+description: Very odd
+facsimiletelephonenumber: +1 313 555 7557
+telephonenumber: +1 313 555 8343
+mail: gjensen@mailgw.example.com
+homephone: +1 313 555 8844
+testTime: 20050304001801.234Z
diff --git a/contrib/slapd-modules/variant/tests/data/test002-02-regex.ldif b/contrib/slapd-modules/variant/tests/data/test002-02-regex.ldif
new file mode 100644
index 0000000..8f0f439
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test002-02-regex.ldif
@@ -0,0 +1,7 @@
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+cn: Rosco P. Coltrane
+sn: Coltrane
+uid: rosco
+title: Chief Investigator, ITD
diff --git a/contrib/slapd-modules/variant/tests/data/test003-out.ldif b/contrib/slapd-modules/variant/tests/data/test003-out.ldif
new file mode 100644
index 0000000..1c3ca5d
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test003-out.ldif
@@ -0,0 +1,124 @@
+# Test 1, list two unrelated entries
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+
+# Test 2, list some of the variant entries, checking that attributes have been populated
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+st: Alumni Association
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+description: The Example, Inc. at Anytown
+
+dn: cn=Manager,dc=example,dc=com
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+
+# Return $BASEDN, location is rewritten to end
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+l: Anytown, Michigan
+
+
+# Make sure only the first regex applies
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+title: Hiker, biker
+ou: Information Technology Division
+
+
+# Exercise the last regex
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+l: Anytown, Michigan
+
+
+# Test 3, check filters pick up the new data
+dn: ou=Groups,dc=example,dc=com
+st: Alumni Association
+
diff --git a/contrib/slapd-modules/variant/tests/data/test005-changes.ldif b/contrib/slapd-modules/variant/tests/data/test005-changes.ldif
new file mode 100644
index 0000000..767f48a
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test005-changes.ldif
@@ -0,0 +1,35 @@
+dn: ou=People,dc=example,dc=com
+changetype: modify
+add: description
+description: Everyone's heard of them
+-
+increment: uidNumber
+uidNumber: 1
+-
+
+dn: ou=Groups,dc=example,dc=com
+changetype: modify
+add: st
+st: Alabama
+-
+
+# check regex
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+changetype: modify
+replace: description
+description: A mouthful
+-
+add: ou
+ou: The IT Crowd
+-
+
+# have the two mods merge
+dn: dc=example,dc=com
+changetype: modify
+add: l
+l: Locally
+-
+replace: st
+st: Antarctica
+-
diff --git a/contrib/slapd-modules/variant/tests/data/test005-modify-missing.ldif b/contrib/slapd-modules/variant/tests/data/test005-modify-missing.ldif
new file mode 100644
index 0000000..ce9c007
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test005-modify-missing.ldif
@@ -0,0 +1,4 @@
+dn: cn=Gern Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+changetype: modify
+replace: description
+description: Ghost
diff --git a/contrib/slapd-modules/variant/tests/data/test005-out.ldif b/contrib/slapd-modules/variant/tests/data/test005-out.ldif
new file mode 100644
index 0000000..67e441b
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test005-out.ldif
@@ -0,0 +1,206 @@
+# Test1: list entries that should have been changed by ldapmodify
+dn: dc=example,dc=com
+objectclass: top
+objectclass: organization
+objectclass: domainRelatedObject
+objectclass: dcobject
+dc: example
+l: Anytown, Michigan
+l: Locally
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+description: Everyone's heard of them
+postaladdress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephonenumber: +1 313 555 1817
+associateddomain: example.com
+st: Antarctica
+
+dn: ou=People,dc=example,dc=com
+objectclass: organizationalUnit
+objectclass: extensibleObject
+ou: People
+uidNumber: 1
+gidNumber: 0
+description: The Example, Inc. at Anytown
+description: Everyone's heard of them
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Alumni Association
+ou: Alabama
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+st: alumni association
+st: alabama
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Information Technology Division
+ou: The IT Crowd
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+title: Hiker, biker
+ou: Information Technology Division
+ou: The IT Crowd
+
diff --git a/contrib/slapd-modules/variant/tests/data/test005-variant-missing.ldif b/contrib/slapd-modules/variant/tests/data/test005-variant-missing.ldif
new file mode 100644
index 0000000..54fd3a5
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test005-variant-missing.ldif
@@ -0,0 +1,4 @@
+dn: ou=People,dc=example,dc=com
+changetype: modify
+replace: seealso
+seealso: dc=example,dc=com
diff --git a/contrib/slapd-modules/variant/tests/data/test006-config.ldif b/contrib/slapd-modules/variant/tests/data/test006-config.ldif
new file mode 100644
index 0000000..c668134
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test006-config.ldif
@@ -0,0 +1,61 @@
+dn: name={4}Mark,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantVariant
+olcVariantEntry: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+
+dn: olcVariantVariantAttribute=description,name={4}Mark,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: cn
+olcVariantAlternativeEntry: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+
+dn: name={5}Elliot,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantVariant
+olcVariantEntry: sn=Elliot,ou=Add & Delete,dc=example,dc=com
+
+dn: olcVariantVariantAttribute=title,name={5}Elliot,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: cn
+olcVariantAlternativeEntry: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+
+dn: olcVariantVariantAttribute=description,name={5}Elliot,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: description
+olcVariantAlternativeEntry: cn=Added by Bjorn,ou=Add & Delete,dc=example,dc=com
+
+dn: name={6}Doe,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantVariant
+olcVariantEntry: sn=Doe,ou=Add & Delete,dc=example,dc=com
+
+dn: olcVariantVariantAttribute=title,name={6}Doe,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: cn
+olcVariantAlternativeEntry: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+
+dn: olcVariantVariantAttribute=description,name={6}Doe,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: description
+olcVariantAlternativeEntry: cn=Added by Bjorn,ou=Add & Delete,dc=example,dc=com
+
+dn: name={7}Group,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantVariant
+olcVariantEntry: cn=Group,ou=Add & Delete,dc=example,dc=com
+
+dn: olcVariantVariantAttribute=seeAlso,name={7}Group,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: member
+olcVariantAlternativeEntry: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+
+dn: olcVariantVariantAttribute=description,name={7}Group,olcOverlay={0}variant,olcDatabase={1}@BACKEND@,cn=config
+changetype: add
+objectclass: olcVariantAttribute
+olcVariantAlternativeAttribute: description
+olcVariantAlternativeEntry: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
diff --git a/contrib/slapd-modules/variant/tests/data/test006-out.ldif b/contrib/slapd-modules/variant/tests/data/test006-out.ldif
new file mode 100644
index 0000000..03910c0
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test006-out.ldif
@@ -0,0 +1,151 @@
+# reading Mark Elliot as anonymous
+
+# reading the same as various users
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+description: Mark A Elliot
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+description: Mark Elliot
+
+
+# Add & Delete subtree contents as seen by Babs
+dn: ou=Add & Delete,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Add & Delete
+
+dn: sn=Doe,ou=Add & Delete,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: John
+uid: jd
+sn: Doe
+title: John Doe
+
+dn: sn=Elliot,ou=Add & Delete,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark
+uid: me
+sn: Elliot
+title: Mark A Elliot
+
+dn: cn=group,ou=Add & Delete,dc=example,dc=com
+objectClass: groupOfNames
+member: dc=example,dc=com
+cn: group
+description: All Alumni Assoc Staff
+seeAlso: cn=Manager,dc=example,dc=com
+seeAlso: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+
+
+# Add & Delete subtree contents as seen by Bjorn
+dn: ou=Add & Delete,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Add & Delete
+
+dn: sn=Doe,ou=Add & Delete,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: John
+uid: jd
+sn: Doe
+title: Jonathon Doe
+
+dn: sn=Elliot,ou=Add & Delete,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark
+uid: me
+sn: Elliot
+title: Mark Elliot
+
+dn: cn=group,ou=Add & Delete,dc=example,dc=com
+objectClass: groupOfNames
+member: dc=example,dc=com
+cn: group
+description: All Alumni Assoc Staff
+seeAlso: cn=Manager,dc=example,dc=com
+seeAlso: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+
+
+# Final state of ou=Add & Delete,dc=example,dc=com as seen by the Manager
+dn: ou=Add & Delete,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Add & Delete
+
+dn: cn=Added by Bjorn,ou=Add & Delete,dc=example,dc=com
+objectClass: inetOrgPerson
+sn: Jensen
+cn: Added by Bjorn
+description: added by jaj (should succeed)
+
+dn: sn=Doe,ou=Add & Delete,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: John
+uid: jd
+sn: Doe
+description: added by jaj (should succeed)
+title: John Doe
+title: Jonathon Doe
+
+dn: sn=Elliot,ou=Add & Delete,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark
+uid: me
+sn: Elliot
+description: added by jaj (should succeed)
+title: Mark Elliot
+title: Mark A Elliot
+
+dn: cn=group,ou=Add & Delete,dc=example,dc=com
+objectClass: groupOfNames
+member: dc=example,dc=com
+cn: group
+description: All Alumni Assoc Staff
+description: another one added by bjorn (should succeed)
+seeAlso: cn=Manager,dc=example,dc=com
+seeAlso: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+seeAlso: cn=Bjorn Jensen,ou=Information Technology DivisioN,ou=People,dc=examp
+ le,dc=com
+seeAlso: cn=Barbara Jensen,ou=Information Technology DivisioN,ou=People,dc=exa
+ mple,dc=com
+
diff --git a/contrib/slapd-modules/variant/tests/data/test007-out.ldif b/contrib/slapd-modules/variant/tests/data/test007-out.ldif
new file mode 100644
index 0000000..cf1aac8
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test007-out.ldif
@@ -0,0 +1,6 @@
+# Testing searches against attribute supertypes...
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+st: Alumni Association
+
diff --git a/contrib/slapd-modules/variant/tests/data/test010-out.ldif b/contrib/slapd-modules/variant/tests/data/test010-out.ldif
new file mode 100644
index 0000000..28603e1
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test010-out.ldif
@@ -0,0 +1,52 @@
+# Test 1, trigger sizelimit without overlay interference
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+Size limit exceeded (4)
+
+# Test 2, check sizelimit is not triggered when it matches the number of entries returned
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+st: Alumni Association
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+description: The Example, Inc. at Anytown
+
+dn: cn=Manager,dc=example,dc=com
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+# Test 3, check sizelimit will stop at the right time
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+st: Alumni Association
+Size limit exceeded (4)
+
diff --git a/contrib/slapd-modules/variant/tests/data/test011-out.ldif b/contrib/slapd-modules/variant/tests/data/test011-out.ldif
new file mode 100644
index 0000000..07604f8
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test011-out.ldif
@@ -0,0 +1,10 @@
+# ldapsearch does not return anything tangible in the output if it enounters a referral
+
+# Asking for the referral will return LDAP_REFERRAL
+Referral (10)
+Matched DN: cn=Gern Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+Referral: ldap://hostB/cn=Gern%20Jensen,ou=Information%20Technology%20Division,ou=People,dc=example,dc=com??sub
+# Asking for anything under a referral will do the same
+Referral (10)
+Matched DN: cn=Gern Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+Referral: ldap://hostB/cn=child,cn=Gern%20Jensen,ou=Information%20Technology%20Division,ou=People,dc=example,dc=com??sub
diff --git a/contrib/slapd-modules/variant/tests/data/test012-data.ldif b/contrib/slapd-modules/variant/tests/data/test012-data.ldif
new file mode 100644
index 0000000..8b8d8b3
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test012-data.ldif
@@ -0,0 +1,13 @@
+dn: dc=demonstration,dc=com
+changetype: add
+objectclass: organization
+objectclass: domainRelatedObject
+objectclass: dcobject
+o: demo
+associateddomain: demonstration.com
+
+dn: ou=Societies,dc=demonstration,dc=com
+changetype: add
+objectclass: organizationalUnit
+ou: Societies
+seealso: dc=example,dc=com
diff --git a/contrib/slapd-modules/variant/tests/data/test012-out.ldif b/contrib/slapd-modules/variant/tests/data/test012-out.ldif
new file mode 100644
index 0000000..bd31fa0
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/test012-out.ldif
@@ -0,0 +1,9 @@
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+seealso: dc=example,dc=com
+description: The Example, Inc. at Anytown
+
diff --git a/contrib/slapd-modules/variant/tests/data/variant.conf b/contrib/slapd-modules/variant/tests/data/variant.conf
new file mode 100644
index 0000000..dba6c46
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/data/variant.conf
@@ -0,0 +1,17 @@
+overlay variant
+passReplication TRUE
+
+variantDN ou=People,dc=example,dc=com
+variantSpec seealso seealso ou=Societies,dc=example,dc=com
+variantSpec description description dc=example,dc=com
+
+variantRegex "(.*),(ou=.*technology.*)(,)dc=example,dc=com"
+variantRegexSpec title description $0
+variantRegexSpec ou ou "$2$3dc=example$3dc=com"
+
+variantDN ou=Groups,dc=example,dc=com
+variantSpec st ou "ou=Alumni Association,ou=People,dc=example,dc=com"
+variantSpec description description ou=People,dc=example,dc=com
+
+variantRegex .*
+variantRegexSpec l l dc=example,dc=com
diff --git a/contrib/slapd-modules/variant/tests/run b/contrib/slapd-modules/variant/tests/run
new file mode 100755
index 0000000..6a38431
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/run
@@ -0,0 +1,229 @@
+#!/bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+USAGE="$0 [-b <backend>] [-c] [-k] [-l #] [-p] [-s {ro|rp}] [-u] [-w] <script>"
+
+TOPSRCDIR="${SRCDIR-$LDAP_SRC}"
+SRCDIR="${TOPSRCDIR}/tests"
+eval `grep EGREP_CMD= ${LDAP_BUILD}/tests/run`
+eval `$EGREP_CMD -e '^LN_S=' ${LDAP_BUILD}/tests/run`
+
+export SRCDIR TOPSRCDIR LN_S EGREP_CMD
+
+. "${SRCDIR}/scripts/defines.sh"
+
+BACKEND=
+CLEAN=no
+WAIT=0
+KILLSERVERS=yes
+PRESERVE=${PRESERVE-no}
+SYNCMODE=${SYNCMODE-rp}
+USERDATA=no
+LOOP=1
+COUNTER=1
+
+while test $# -gt 0 ; do
+ case "$1" in
+ -b | -backend)
+ BACKEND="$2"
+ shift; shift ;;
+
+ -c | -clean)
+ CLEAN=yes
+ shift ;;
+
+ -k | -kill)
+ KILLSERVERS=no
+ shift ;;
+ -l | -loop)
+ NUM="`echo $2 | sed 's/[0-9]//g'`"
+ if [ -z "$NUM" ]; then
+ LOOP=$2
+ else
+ echo "Loop variable not an int: $2"
+ echo "$USAGE"; exit 1
+ fi
+ shift ;
+ shift ;;
+
+ -p | -preserve)
+ PRESERVE=yes
+ shift ;;
+
+ -s | -syncmode)
+ case "$2" in
+ ro | rp)
+ SYNCMODE="$2"
+ ;;
+ *)
+ echo "unknown sync mode $2"
+ echo "$USAGE"; exit 1
+ ;;
+ esac
+ shift; shift ;;
+
+ -u | -userdata)
+ USERDATA=yes
+ shift ;;
+
+ -w | -wait)
+ WAIT=1
+ shift ;;
+
+ -)
+ shift
+ break ;;
+
+ -*)
+ echo "$USAGE"; exit 1
+ ;;
+
+ *)
+ break ;;
+ esac
+done
+
+eval `$EGREP_CMD -e '^AC' ${LDAP_BUILD}/tests/run`
+export `$EGREP_CMD -e '^AC' ${LDAP_BUILD}/tests/run | sed 's/=.*//'`
+
+if test -z "$BACKEND" ; then
+ for b in mdb ; do
+ if eval "test \"\$AC_$b\" != no" ; then
+ BACKEND=$b
+ break
+ fi
+ done
+ if test -z "$BACKEND" ; then
+ echo "No suitable default database backend configured" >&2
+ exit 1
+ fi
+fi
+
+BACKENDTYPE=`eval 'echo $AC_'$BACKEND`
+if test "x$BACKENDTYPE" = "x" ; then
+ BACKENDTYPE="unknown"
+fi
+
+# Backend features. indexdb: indexing and unchecked limit.
+# maindb: main storage backend. Currently index,limits,mode,paged results.
+INDEXDB=noindexdb MAINDB=nomaindb
+case $BACKEND in
+ mdb) INDEXDB=indexdb MAINDB=maindb ;;
+ ndb) INDEXDB=indexdb ;;
+esac
+
+export BACKEND BACKENDTYPE INDEXDB MAINDB \
+ WAIT KILLSERVERS PRESERVE SYNCMODE USERDATA \
+ SRCDIR
+
+if test $# = 0 ; then
+ echo "$USAGE"; exit 1
+fi
+
+# need defines.sh for the definitions of the directories
+. $SRCDIR/scripts/defines.sh
+
+SCRIPTDIR="${TOPDIR}/tests/scripts"
+
+export SCRIPTDIR
+
+SCRIPTNAME="$1"
+shift
+
+if test -x "${SCRIPTDIR}/${SCRIPTNAME}" ; then
+ SCRIPT="${SCRIPTDIR}/${SCRIPTNAME}"
+elif test -x "`echo ${SCRIPTDIR}/test*-${SCRIPTNAME}`"; then
+ SCRIPT="`echo ${SCRIPTDIR}/test*-${SCRIPTNAME}`"
+elif test -x "`echo ${SCRIPTDIR}/${SCRIPTNAME}-*`"; then
+ SCRIPT="`echo ${SCRIPTDIR}/${SCRIPTNAME}-*`"
+else
+ echo "run: ${SCRIPTNAME} not found (or not executable)"
+ exit 1;
+fi
+
+if test ! -r ${DATADIR}/test.ldif ; then
+ ${LN_S} ${SRCDIR}/data ${DATADIR}
+fi
+if test ! -r ${SCHEMADIR}/core.schema ; then
+ ${LN_S} ${TOPSRCDIR}/servers/slapd/schema ${SCHEMADIR}
+fi
+if test ! -r ./data; then
+ ${LN_S} ${TOPDIR}/tests/data ./
+fi
+
+if test -d ${TESTDIR} ; then
+ if test $PRESERVE = no ; then
+ echo "Cleaning up test run directory leftover from previous run."
+ /bin/rm -rf ${TESTDIR}
+ elif test $PRESERVE = yes ; then
+ echo "Cleaning up only database directories leftover from previous run."
+ /bin/rm -rf ${TESTDIR}/db.*
+ fi
+fi
+if test $BACKEND = ndb ; then
+ mysql --user root <<EOF
+ drop database if exists db_1;
+ drop database if exists db_2;
+ drop database if exists db_3;
+ drop database if exists db_4;
+ drop database if exists db_5;
+ drop database if exists db_6;
+EOF
+fi
+mkdir -p ${TESTDIR}
+
+if test $USERDATA = yes ; then
+ if test ! -d userdata ; then
+ echo "User data directory (userdata) does not exist."
+ exit 1
+ fi
+ cp -R userdata/* ${TESTDIR}
+fi
+
+# disable LDAP initialization
+LDAPNOINIT=true; export LDAPNOINIT
+
+echo "Running ${SCRIPT} for ${BACKEND}..."
+while [ $COUNTER -le $LOOP ]; do
+ if [ $LOOP -gt 1 ]; then
+ echo "Running $COUNTER of $LOOP iterations"
+ fi
+ $SCRIPT $*
+ RC=$?
+
+ if test $CLEAN = yes ; then
+ echo "Cleaning up test run directory from this run."
+ /bin/rm -rf ${TESTDIR}
+ echo "Cleaning up symlinks."
+ /bin/rm -f ${DATADIR} ${SCHEMADIR}
+ fi
+
+ if [ $RC -ne 0 ]; then
+ if [ $LOOP -gt 1 ]; then
+ echo "Failed after $COUNTER of $LOOP iterations"
+ fi
+ exit $RC
+ else
+ COUNTER=`expr $COUNTER + 1`
+ if [ $COUNTER -le $LOOP ]; then
+ echo "Cleaning up test run directory from this run."
+ /bin/rm -rf ${TESTDIR}
+ fi
+ fi
+done
+exit $RC
diff --git a/contrib/slapd-modules/variant/tests/scripts/all b/contrib/slapd-modules/variant/tests/scripts/all
new file mode 100755
index 0000000..d6d6dc7
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/all
@@ -0,0 +1,102 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+. $SRCDIR/scripts/defines.sh
+
+TB="" TN=""
+if test -t 1 ; then
+ TB=`$SHTOOL echo -e "%B" 2>/dev/null`
+ TN=`$SHTOOL echo -e "%b" 2>/dev/null`
+fi
+
+FAILCOUNT=0
+SKIPCOUNT=0
+SLEEPTIME=10
+
+echo ">>>>> Executing all LDAP tests for $BACKEND"
+
+if [ -n "$NOEXIT" ]; then
+ echo "Result Test" > $TESTWD/results
+fi
+
+for CMD in ${SCRIPTDIR}/test*; do
+ case "$CMD" in
+ *~) continue;;
+ *.bak) continue;;
+ *.orig) continue;;
+ *.sav) continue;;
+ *) test -f "$CMD" || continue;;
+ esac
+
+ # remove cruft from prior test
+ if test $PRESERVE = yes ; then
+ /bin/rm -rf $TESTDIR/db.*
+ else
+ /bin/rm -rf $TESTDIR
+ fi
+ if test $BACKEND = ndb ; then
+ mysql --user root <<EOF
+ drop database if exists db_1;
+ drop database if exists db_2;
+ drop database if exists db_3;
+ drop database if exists db_4;
+ drop database if exists db_5;
+ drop database if exists db_6;
+EOF
+ fi
+
+ BCMD=`basename $CMD`
+ if [ -x "$CMD" ]; then
+ echo ">>>>> Starting ${TB}$BCMD${TN} for $BACKEND..."
+ $CMD
+ RC=$?
+ if test $RC -eq 0 ; then
+ echo ">>>>> $BCMD completed ${TB}OK${TN} for $BACKEND."
+ else
+ echo ">>>>> $BCMD ${TB}failed${TN} for $BACKEND"
+ FAILCOUNT=`expr $FAILCOUNT + 1`
+
+ if [ -n "$NOEXIT" ]; then
+ echo "Continuing."
+ else
+ echo "(exit $RC)"
+ exit $RC
+ fi
+ fi
+ else
+ echo ">>>>> Skipping ${TB}$BCMD${TN} for $BACKEND."
+ SKIPCOUNT=`expr $SKIPCOUNT + 1`
+ RC="-"
+ fi
+
+ if [ -n "$NOEXIT" ]; then
+ echo "$RC $BCMD" >> $TESTWD/results
+ fi
+
+# echo ">>>>> waiting $SLEEPTIME seconds for things to exit"
+# sleep $SLEEPTIME
+ echo ""
+done
+
+if [ -n "$NOEXIT" ]; then
+ if [ "$FAILCOUNT" -gt 0 ]; then
+ cat $TESTWD/results
+ echo "$FAILCOUNT tests for $BACKEND ${TB}failed${TN}. Please review the test log."
+ else
+ echo "All executed tests for $BACKEND ${TB}succeeded${TN}."
+ fi
+fi
+
+echo "$SKIPCOUNT tests for $BACKEND were ${TB}skipped${TN}."
diff --git a/contrib/slapd-modules/variant/tests/scripts/common.sh b/contrib/slapd-modules/variant/tests/scripts/common.sh
new file mode 100755
index 0000000..3b155ad
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/common.sh
@@ -0,0 +1,115 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016-2017 by Ondřej Kuzník for Symas Corp.
+
+OVERLAY_CONFIG=${OVERLAY_CONFIG-data/config.ldif}
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND $MONITORDB < $CONF > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+mkdir $TESTDIR/confdir
+. $CONFFILTER $BACKEND $MONITORDB < $CONF > $CONF1
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "database config" >>$CONF1
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >>$CONF1
+
+echo "Starting slapd on TCP/IP port $PORT1 for configuration..."
+$SLAPD -f $CONF1 -F $TESTDIR/confdir -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+echo "Making a modification that will be hidden by the test config..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/hidden.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -D cn=config -H $URI1 -y $CONFIGPWF \
+ -s base -b 'cn=module{0},cn=config' 1.1 >$TESTOUT 2>&1
+RC=$?
+case $RC in
+0)
+ $LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: cn=module{0},cn=config
+changetype: modify
+add: olcModuleLoad
+olcModuleLoad: `pwd`/../variant.la
+EOMOD
+ ;;
+32)
+ $LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: cn=module,cn=config
+changetype: add
+objectClass: olcModuleList
+olcModuleLoad: `pwd`/../variant.la
+EOMOD
+ ;;
+*)
+ echo "Failed testing for module load entry"
+ exit $RC;
+ ;;
+esac
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Loading test variant configuration..."
+. $CONFFILTER $BACKEND $MONITORDB < $OVERLAY_CONFIG | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
diff --git a/contrib/slapd-modules/variant/tests/scripts/test001-config b/contrib/slapd-modules/variant/tests/scripts/test001-config
new file mode 100755
index 0000000..7a5559f
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test001-config
@@ -0,0 +1,209 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Applying invalid changes to config (should fail)..."
+for CHANGE in data/test001-*.ldif; do
+ echo "... $CHANGE"
+ . $CONFFILTER $BACKEND $MONITORDB < $CHANGE | \
+ $LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+ RC=$?
+ case $RC in
+ 0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+ 80)
+ echo "ldapmodify failed ($RC)"
+ ;;
+ *)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+ esac
+done
+
+# We run this search after the changes above and before restart so we can also
+# check the reconfiguration attempts actually had no side effects
+echo "Saving search output before server restart..."
+echo "# search output from dynamically configured server..." >> $SERVER3OUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ >> $SERVER3OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Stopping slapd on TCP/IP port $PORT1..."
+kill -HUP $KILLPIDS
+KILLPIDS=""
+sleep $SLEEP0
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -F $TESTDIR/confdir -h $URI1 -d $LVL >> $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+echo "Testing slapd.conf support..."
+mkdir $TESTDIR/conftest $DBDIR2
+. $CONFFILTER $BACKEND $MONITORDB < $CONFTWO \
+ | sed -e '/^argsfile.*/a\
+moduleload ../variant.la' \
+ -e '/database.*monitor/i\
+include data/variant.conf' \
+ > $CONF2
+echo "database config" >>$CONF2
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >>$CONF2
+
+$SLAPADD -f $CONF2 -l $LDIFORDERED
+$SLAPD -Tt -f $CONF2 -F $TESTDIR/conftest -d $LVL >> $LOG2 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slaptest failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT2..."
+$SLAPD -F $TESTDIR/conftest -h $URI2 -d $LVL >> $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+echo "Gathering overlay configuration from both servers..."
+echo "# overlay configuration from dynamically configured server..." >> $SERVER1OUT
+$LDAPSEARCH -D cn=config -H $URI1 -y $CONFIGPWF \
+ -b "olcOverlay={0}variant,olcDatabase={1}$BACKEND,cn=config" \
+ >> $SERVER1OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "# overlay configuration from server configured from slapd.conf..." >> $SERVER2OUT
+$LDAPSEARCH -D cn=config -H $URI2 -y $CONFIGPWF \
+ -b "olcOverlay={0}variant,olcDatabase={1}$BACKEND,cn=config" \
+ >> $SERVER2OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# We've already filtered out the ordering markers, now sort the entries
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s a < $SERVER2OUT > $SERVER2FLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s a < $SERVER1OUT > $SERVER1FLT
+echo "Comparing filter output..."
+$CMP $SERVER2FLT $SERVER1FLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+rm $SERVER1OUT $SERVER2OUT
+
+echo "Comparing search output on both servers..."
+echo "# search output from dynamically configured server..." >> $SERVER1OUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ >> $SERVER1OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "# search output from server configured from slapd.conf..." >> $SERVER2OUT
+$LDAPSEARCH -b "$BASEDN" -H $URI2 \
+ >> $SERVER2OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SERVER1OUT > $SERVER1FLT
+$LDIFFILTER -s e < $SERVER2OUT > $SERVER2FLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $SERVER3OUT > $SERVER3FLT
+echo "Comparing filter output..."
+$CMP $SERVER3FLT $SERVER1FLT > $CMPOUT && \
+$CMP $SERVER3FLT $SERVER2FLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test002-add-delete b/contrib/slapd-modules/variant/tests/scripts/test002-add-delete
new file mode 100755
index 0000000..bd316b2
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test002-add-delete
@@ -0,0 +1,113 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Adding entry..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test002-01-entry.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Configuring entry as variant..."
+. $CONFFILTER $BACKEND $MONITORDB < data/additional-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Removing entry..."
+$LDAPDELETE -D $MANAGERDN -H $URI1 -w $PASSWD \
+ "cn=Gern Jensen,ou=Information Technology Division,ou=People,$BASEDN" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapdelete failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding entry again (should fail)..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test002-01-entry.ldif >> $TESTOUT 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+19)
+ echo "ldapmodify failed ($RC)"
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Adding a regex entry (should fail)..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test002-02-regex.ldif >> $TESTOUT 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+19)
+ echo "ldapmodify failed ($RC)"
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Adding entry with offending attributes removed..."
+grep -v '^description:' data/test002-01-entry.ldif | \
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test003-search b/contrib/slapd-modules/variant/tests/scripts/test003-search
new file mode 100755
index 0000000..2284ab7
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test003-search
@@ -0,0 +1,113 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Testing searches against regular entries..."
+echo "# Testing searches against regular entries..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ "(|(name=Elliot)(description=*hiker*))" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches listing variants..."
+echo >> $SEARCHOUT
+echo "# Testing searches listing variants..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -s one -H $URI1 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -s base -H $URI1 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo >> $SEARCHOUT
+$LDAPSEARCH -s base -H $URI1 \
+ -b "cn=Bjorn Jensen,ou=Information Technology Division,ou=People,$BASEDN" \
+ '(ou=Information Technology Division)' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo >> $SEARCHOUT
+$LDAPSEARCH -b "cn=ITD Staff,ou=Groups,$BASEDN" -s base -H $URI1 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches filtering on variants..."
+echo >> $SEARCHOUT
+echo "# Testing searches filtering on variants..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ "(st=Alumni Association)" st \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test003-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test004-compare b/contrib/slapd-modules/variant/tests/scripts/test004-compare
new file mode 100755
index 0000000..c87d347
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test004-compare
@@ -0,0 +1,63 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Comparing a regular entry..."
+$LDAPCOMPARE -H $URI1 \
+ "cn=Mark Elliot,ou=Alumni Association,ou=People,$BASEDN" \
+ "cn:Mark Elliot" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 6 && test $RC,$BACKEND != 5,null ; then
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Comparing a variant entry..."
+$LDAPCOMPARE -H $URI1 \
+ "ou=People,$BASEDN" \
+ "description:The Example, Inc. at Anytown" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 6 && test $RC,$BACKEND != 5,null ; then
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Comparing a regex entry..."
+$LDAPCOMPARE -H $URI1 \
+ "cn=Barbara Jensen,ou=Information Technology Division,ou=People,$BASEDN" \
+ "ou:Information Technology Division" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 6 && test $RC,$BACKEND != 5,null ; then
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test005-modify b/contrib/slapd-modules/variant/tests/scripts/test005-modify
new file mode 100755
index 0000000..4cbf289
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test005-modify
@@ -0,0 +1,120 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Modifying entry..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test005-changes.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# for now, overlay returns success just after the modifications to the main
+# entry succeed, ignoring the rest should they fail
+echo "Modifying a nonexistent variant of an existing entry..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test005-variant-missing.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Configuring nonexistent entry as variant..."
+. $CONFFILTER $BACKEND $MONITORDB < data/additional-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Modifying an existing variant of above missing entry..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test005-modify-missing.ldif >> $TESTOUT 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+32)
+ echo "ldapmodify failed ($RC)"
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Reading affected entries back..."
+echo "# Reading affected entries back..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ '(|(description=*heard*)(st=*)(ou=alabama)(ou=*IT*))' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo >>$SEARCHOUT
+$LDAPSEARCH -H $URI1 -s base \
+ -b "cn=Bjorn Jensen,ou=Information Technology Division,ou=People,$BASEDN" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test005-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test006-acl b/contrib/slapd-modules/variant/tests/scripts/test006-acl
new file mode 100755
index 0000000..6b34fb8
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test006-acl
@@ -0,0 +1,323 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+case "$BACKEND" in ldif | null)
+ echo "$BACKEND backend does not support access controls, test skipped"
+ exit 0
+esac
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+CONF=$ACLCONF
+. ${SCRIPTDIR}/common.sh
+
+echo "Applying test-specific configuration..."
+. $CONFFILTER $BACKEND $MONITORDB < data/test006-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: ou=Add & Delete,dc=example,dc=com
+changetype: add
+objectClass: organizationalUnit
+ou: Add & Delete
+
+dn: cn=group,ou=Add & Delete,dc=example,dc=com
+changetype: add
+objectclass: groupOfNames
+member: dc=example,dc=com
+
+dn: sn=Doe,ou=Add & Delete,dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+cn: John
+uid: jd
+
+dn: sn=Elliot,ou=Add & Delete,dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+cn: Mark
+uid: me
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing search ACL processing..."
+
+echo "# Try to read an entry inside the Alumni Association container.
+# It should give us noSuchObject if we're not bound..." \
+>> $SEARCHOUT
+# FIXME: temporarily remove the "No such object" message to make
+# the test succeed even if SLAP_ACL_HONOR_DISCLOSE is not #define'd
+$LDAPSEARCH -b "$MELLIOTDN" -H $URI1 "(objectclass=*)" \
+ 2>&1 | grep -v "No such object" >> $SEARCHOUT
+
+echo >>$SEARCHOUT
+echo "# ... and should return appropriate attributes if we're bound as anyone
+# under Example." \
+>> $SEARCHOUT
+$LDAPSEARCH -b "$MELLIOTDN" -H $URI1 \
+ -D "$BABSDN" -w bjensen "(objectclass=*)" >> $SEARCHOUT 2>&1
+
+$LDAPSEARCH -b "$MELLIOTDN" -H $URI1 \
+ -D "$BJORNSDN" -w bjorn "(objectclass=*)" >> $SEARCHOUT 2>&1
+
+echo >>$SEARCHOUT
+echo "# Add & Delete subtree contents as seen by Babs" >> $SEARCHOUT
+$LDAPSEARCH -b "ou=Add & Delete,dc=example,dc=com" -H $URI1 \
+ -D "$BABSDN" -w bjensen "(objectclass=*)" >> $SEARCHOUT 2>&1
+
+echo >>$SEARCHOUT
+echo "# Add & Delete subtree contents as seen by Bjorn" >> $SEARCHOUT
+$LDAPSEARCH -b "ou=Add & Delete,dc=example,dc=com" -H $URI1 \
+ -D "$BJORNSDN" -w bjorn "(objectclass=*)" >> $SEARCHOUT 2>&1
+
+echo "Testing modifications..."
+echo "... ACL on the alternative entry"
+$LDAPMODIFY -D "$BJORNSDN" -H $URI1 -w bjorn >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=group,ou=Add & Delete,dc=example,dc=com
+changetype: modify
+add: seealso
+seealso: $BJORNSDN
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$BABSDN" -H $URI1 -w bjensen >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Alumni Assoc Staff, ou=Groups, dc=example, dc=com
+changetype: modify
+add: description
+description: added by bjensen (should fail)
+EOMODS
+RC=$?
+case $RC in
+50)
+ ;;
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=group,ou=Add & Delete,dc=example,dc=com
+changetype: modify
+add: seealso
+seealso: $BABSDN
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$BJORNSDN" -H $URI1 -w bjorn >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Alumni Assoc Staff, ou=Groups, dc=example, dc=com
+changetype: modify
+add: description
+description: added by bjorn (removed later)
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$BABSDN" -H $URI1 -w bjensen >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Group,ou=Add & Delete,dc=example,dc=com
+changetype: modify
+delete: description
+description: added by bjorn (removed later)
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$BJORNSDN" -H $URI1 -w bjorn >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Added by Bjorn,ou=Add & Delete,dc=example,dc=com
+changetype: add
+objectClass: inetOrgPerson
+sn: Jensen
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$BJORNSDN" -H $URI1 -w bjorn >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Group,ou=Add & Delete,dc=example,dc=com
+changetype: modify
+add: description
+description: another one added by bjorn (should succeed)
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "... ACL on the variant entry"
+$LDAPMODIFY -D "$BABSDN" -H $URI1 -w bjensen >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Group,ou=Add & Delete,dc=example,dc=com
+changetype: modify
+add: description
+description: added by bjensen (should fail)
+EOMODS
+RC=$?
+case $RC in
+50)
+ ;;
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$BJORNSDN" -H $URI1 -w bjorn >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: sn=Doe,ou=Add & Delete,dc=example,dc=com
+changetype: modify
+add: description
+description: added by bjorn (will be removed)
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$BABSDN" -H $URI1 -w bjensen >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Added by Bjorn,ou=Add & Delete,dc=example,dc=com
+changetype: modify
+replace: description
+description: added by bjensen (should fail)
+EOMODS
+RC=$?
+case $RC in
+50)
+ ;;
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$JAJDN" -H $URI1 -w jaj >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: sn=Elliot,ou=Add & Delete,dc=example,dc=com
+changetype: modify
+delete: description
+description: added by bjorn (will be removed)
+-
+add: description
+description: added by jaj (should succeed)
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep $SLEEP0
+
+echo >>$SEARCHOUT
+echo "Using ldapsearch to retrieve all the entries..."
+echo "# Using ldapsearch to retrieve all the entries..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "ou=Add & Delete,dc=example,dc=com" \
+ -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ 'objectClass=*' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test006-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - operations did not complete correctly"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test007-subtypes b/contrib/slapd-modules/variant/tests/scripts/test007-subtypes
new file mode 100755
index 0000000..177fc33
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test007-subtypes
@@ -0,0 +1,67 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Comparing supertype of a variant attribute..."
+$LDAPCOMPARE -H $URI1 \
+ "ou=Groups,$BASEDN" \
+ "name:Alumni Association" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 6 && test $RC,$BACKEND != 5,null ; then
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing searches against attribute supertypes..."
+echo "# Testing searches against attribute supertypes..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ "(&(name=groups)(name=Alumni Association))" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test007-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test008-variant-replication b/contrib/slapd-modules/variant/tests/scripts/test008-variant-replication
new file mode 100755
index 0000000..63e2d7e
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test008-variant-replication
@@ -0,0 +1,194 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test "$SYNCPROV" = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+. ${SCRIPTDIR}/common.sh
+
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: olcOverlay={0}variant,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcVariantPassReplication
+olcVariantPassReplication: FALSE
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if test "$SYNCPROV" = syncprovmod; then
+ $LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: cn=module{0},cn=config
+changetype: modify
+add: olcModuleLoad
+olcModuleLoad: $LDAP_BUILD/servers/slapd/overlays/syncprov.la
+EOMOD
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+echo "Configuring syncprov on the provider..."
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: olcOverlay=syncprov,olcDatabase={1}$BACKEND,cn=config
+changetype: add
+objectclass: olcSyncProvConfig
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+mkdir $DBDIR4
+
+echo "Starting consumer slapd on TCP/IP port $PORT4..."
+. $CONFFILTER $BACKEND $MONITORDB < $P1SRCONSUMERCONF > $CONF4
+$SLAPD -f $CONF4 -h $URI4 -d $LVL > $LOG4 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$BASEDN" -H $URI4 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for consumer to start replication..."
+ sleep ${SLEEP1}
+done
+
+echo "Waiting ${SLEEP1} seconds for consumer to finish replicating..."
+sleep ${SLEEP1}
+
+echo "Testing searches against regular entries..."
+echo "# Testing searches against regular entries..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI4 \
+ "(|(name=Elliot)(description=*hiker*))" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches listing replicated variants..."
+echo >> $SEARCHOUT
+echo "# Testing searches listing replicated variants..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -s one -H $URI4 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# regex variants do not replicate correctly and this is documented
+echo >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -s base -H $URI1 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# regex variants do not replicate correctly and this is documented
+echo >> $SEARCHOUT
+$LDAPSEARCH -s base -H $URI1 \
+ -b "cn=Bjorn Jensen,ou=Information Technology Division,ou=People,$BASEDN" \
+ '(ou=Information Technology Division)' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# regex variants do not replicate correctly and this is documented
+echo >> $SEARCHOUT
+$LDAPSEARCH -b "cn=ITD Staff,ou=Groups,$BASEDN" -s base -H $URI1 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches filtering on replicated variants..."
+echo >> $SEARCHOUT
+echo "# Testing searches filtering on replicated variants..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI4 \
+ "(st=Alumni Association)" st \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test003-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test009-ignored-replication b/contrib/slapd-modules/variant/tests/scripts/test009-ignored-replication
new file mode 100755
index 0000000..aefbfa9
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test009-ignored-replication
@@ -0,0 +1,227 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test "$SYNCPROV" = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+. ${SCRIPTDIR}/common.sh
+
+if test "$SYNCPROV" = syncprovmod; then
+ $LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: cn=module{0},cn=config
+changetype: modify
+add: olcModuleLoad
+olcModuleLoad: $LDAP_BUILD/servers/slapd/overlays/syncprov.la
+EOMOD
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+echo "Configuring syncprov on the provider..."
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOMOD
+dn: olcOverlay={0}syncprov,olcDatabase={1}$BACKEND,cn=config
+changetype: add
+objectclass: olcSyncProvConfig
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+mkdir $DBDIR4 $TESTDIR/confdir-consumer
+
+echo "Starting consumer slapd on TCP/IP port $PORT4..."
+. $CONFFILTER $BACKEND $MONITORDB < $P1SRCONSUMERCONF > $CONF4
+
+echo "database config" >>$CONF4
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >>$CONF4
+
+$SLAPD -f $CONF4 -F $TESTDIR/confdir-consumer -h $URI4 -d $LVL > $LOG4 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep $SLEEP0
+
+echo "Setting up variant overlay on consumer..."
+$LDAPSEARCH -D cn=config -H $URI4 -y $CONFIGPWF \
+ -s base -b 'cn=module{0},cn=config' 1.1 >$TESTOUT 2>&1
+RC=$?
+case $RC in
+0)
+ $LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: cn=module{0},cn=config
+changetype: modify
+add: olcModuleLoad
+olcModuleLoad: `pwd`/../variant.la
+EOMOD
+ ;;
+32)
+ $LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: cn=module,cn=config
+changetype: add
+objectClass: olcModuleList
+olcModuleLoad: `pwd`/../variant.la
+EOMOD
+ ;;
+*)
+ echo "Failed testing for module load entry"
+ exit $RC;
+ ;;
+esac
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+. $CONFFILTER $BACKEND $MONITORDB < $OVERLAY_CONFIG | \
+$LDAPMODIFY -v -D cn=config -H $URI4 -y $CONFIGPWF \
+ > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$BASEDN" -H $URI4 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for consumer to start replication..."
+ sleep ${SLEEP1}
+done
+
+echo "Waiting ${SLEEP1} seconds for consumer to finish replicating..."
+sleep ${SLEEP1}
+
+echo "Testing searches against regular entries..."
+echo "# Testing searches against regular entries..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI4 \
+ "(|(name=Elliot)(description=*hiker*))" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches listing replicated variants..."
+echo >> $SEARCHOUT
+echo "# Testing searches listing replicated variants..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -s one -H $URI4 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -s base -H $URI4 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo >> $SEARCHOUT
+$LDAPSEARCH -s base -H $URI4 \
+ -b "cn=Bjorn Jensen,ou=Information Technology Division,ou=People,$BASEDN" \
+ '(ou=Information Technology Division)' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo >> $SEARCHOUT
+$LDAPSEARCH -b "cn=ITD Staff,ou=Groups,$BASEDN" -s base -H $URI4 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches filtering on replicated variants..."
+echo >> $SEARCHOUT
+echo "# Testing searches filtering on replicated variants..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI4 \
+ "(st=Alumni Association)" st \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test003-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test010-limits b/contrib/slapd-modules/variant/tests/scripts/test010-limits
new file mode 100755
index 0000000..5828922
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test010-limits
@@ -0,0 +1,99 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Testing searches against regular entries..."
+echo "# Testing searches against regular entries..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ -z 1 "(|(name=Elliot)(description=*hiker*))" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapsearch should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+4)
+ echo "sizelimit reached ($RC)"
+ ;;
+*)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Testing searches listing variants where limits just fit..."
+echo "# Testing searches listing variants where limits just fit..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -s one -H $URI1 \
+ -z 3 >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing searches filtering on variants going over the specified limit..."
+echo "# Testing searches filtering on variants going over the specified limit..." >> $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ -z 1 "(name=Alumni Association)" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapsearch should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+4)
+ echo "sizelimit reached ($RC)"
+ ;;
+*)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test010-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test011-referral b/contrib/slapd-modules/variant/tests/scripts/test011-referral
new file mode 100755
index 0000000..37d6d8c
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test011-referral
@@ -0,0 +1,169 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+TESTDN="cn=Gern Jensen,ou=Information Technology Division,ou=People,$BASEDN"
+
+echo "Adding referral..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: $TESTDN
+changetype: add
+objectclass: referral
+objectclass: extensibleObject
+ref: ldap://hostB HostB
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Configuring referral as variant..."
+. $CONFFILTER $BACKEND $MONITORDB < data/additional-config.ldif | \
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Retrieving a referral variant..."
+echo "# Retrieving a referral variant..." >> $SEARCHOUT
+$LDAPSEARCH -LLL -b "$BASEDN" -H $URI1 \
+ '(cn=Gern Jensen)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch: unexpected result ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Retrieving a referral variant (returns a referral)..."
+echo "# Retrieving a referral variant (returns a referral)..." >> $SEARCHOUT
+$LDAPSEARCH -b "$TESTDN" -H $URI1 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 10 ; then
+ echo "ldapsearch: unexpected result ($RC)! (referral expected)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Modifying a referral variant (returns a referral)..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: $TESTDN
+changetype: modify
+delete: description
+EOMOD
+RC=$?
+if test $RC != 10 ; then
+ echo "ldapmodify: unexpected result ($RC)! (referral expected)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Comparing a referral variant (returns a referral)..."
+$LDAPCOMPARE -H $URI1 "$TESTDN" \
+ "description:The Example, Inc. at Anytown" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 10; then
+ echo "ldapcompare: unexpected result ($RC)! (referral expected)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Reconfiguring variant underneath a referral..."
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: name={4}test002,olcOverlay={0}variant,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcVariantEntry
+olcVariantEntry: cn=child,$TESTDN
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Retrieving a variant under a referral (returns a referral)..."
+echo "# Retrieving a variant under a referral (returns a referral)..." >> $SEARCHOUT
+$LDAPSEARCH -b "cn=child,$TESTDN" -H $URI1 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 10 ; then
+ echo "ldapsearch: unexpected result ($RC)! (referral expected)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Modifying a variant under a referral (returns a referral)..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: cn=child,$TESTDN
+changetype: modify
+delete: description
+EOMOD
+RC=$?
+if test $RC != 10 ; then
+ echo "ldapmodify: unexpected result ($RC)! (referral expected)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Comparing a variant under a referral (returns a referral)..."
+$LDAPCOMPARE -H $URI1 "cn=child,$TESTDN" \
+ "description:The Example, Inc. at Anytown" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 10; then
+ echo "ldapcompare: unexpected result ($RC)! (referral expected)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test011-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/tests/scripts/test012-crossdb b/contrib/slapd-modules/variant/tests/scripts/test012-crossdb
new file mode 100755
index 0000000..8854a1b
--- /dev/null
+++ b/contrib/slapd-modules/variant/tests/scripts/test012-crossdb
@@ -0,0 +1,90 @@
+#! /bin/sh
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This module was written in 2016 by Ondřej Kuzník for Symas Corp.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+. ${SCRIPTDIR}/common.sh
+
+echo "Setting up another database and variant using an alternate there..."
+mkdir $DBDIR2
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ <<EOMOD >> $TESTOUT 2>&1
+dn: olcDatabase=ldif,cn=config
+changetype: add
+objectclass: olcLdifConfig
+olcSuffix: dc=demonstration,dc=com
+olcDbDirectory: $DBDIR2
+olcRootDn: $MANAGERDN
+
+dn: olcVariantVariantAttribute={1}seealso,name={0}variant,olcOverlay={0}variant,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcVariantAlternativeEntry
+olcVariantAlternativeEntry: ou=Societies,dc=demonstration,dc=com
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding alternate entry..."
+$LDAPMODIFY -D $MANAGERDN -H $URI1 -w $PASSWD \
+ -f data/test012-data.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Checking the variant gets resolved correctly..."
+echo "# Testing a search against a variant using another DB..." >> $SEARCHOUT
+#$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+# "seealso=dc=example,dc=com" \
+$LDAPSEARCH -b "ou=People,$BASEDN" -s base -H $URI1 \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=data/test012-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected entries..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/contrib/slapd-modules/variant/variant.c b/contrib/slapd-modules/variant/variant.c
new file mode 100644
index 0000000..edf4832
--- /dev/null
+++ b/contrib/slapd-modules/variant/variant.c
@@ -0,0 +1,1424 @@
+/* variant.c - variant overlay */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2021 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed in 2016-2017 by Ondřej Kuzník for Symas Corp.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_VARIANT
+
+#include "slap.h"
+#include "slap-config.h"
+#include "ldap_queue.h"
+
+typedef enum variant_type_t {
+ VARIANT_INFO_PLAIN = 1 << 0,
+ VARIANT_INFO_REGEX = 1 << 1,
+
+ VARIANT_INFO_ALL = ~0
+} variant_type_t;
+
+typedef struct variant_info_t {
+ int passReplication;
+ LDAP_STAILQ_HEAD(variant_list, variantEntry_info) variants, regex_variants;
+} variant_info_t;
+
+typedef struct variantEntry_info {
+ variant_info_t *ov;
+ struct berval dn;
+ variant_type_t type;
+ regex_t *regex;
+ LDAP_SLIST_HEAD(attribute_list, variantAttr_info) attributes;
+ LDAP_STAILQ_ENTRY(variantEntry_info) next;
+} variantEntry_info;
+
+typedef struct variantAttr_info {
+ variantEntry_info *variant;
+ struct berval dn;
+ AttributeDescription *attr, *alternative;
+ LDAP_SLIST_ENTRY(variantAttr_info) next;
+} variantAttr_info;
+
+static int
+variant_build_dn(
+ Operation *op,
+ variantAttr_info *vai,
+ int nmatch,
+ regmatch_t *pmatch,
+ struct berval *out )
+{
+ struct berval dn, *ndn = &op->o_req_ndn;
+ char *dest, *p, *prev, *end = vai->dn.bv_val + vai->dn.bv_len;
+ size_t len = vai->dn.bv_len;
+ int rc;
+
+ p = vai->dn.bv_val;
+ while ( (p = memchr( p, '$', end - p )) != NULL ) {
+ len -= 1;
+ p += 1;
+
+ if ( ( *p >= '0' ) && ( *p <= '9' ) ) {
+ int i = *p - '0';
+
+ len += ( pmatch[i].rm_eo - pmatch[i].rm_so );
+ } else if ( *p != '$' ) {
+ /* Should have been checked at configuration time */
+ assert(0);
+ }
+ len -= 1;
+ p += 1;
+ }
+
+ dest = dn.bv_val = ch_realloc( out->bv_val, len + 1 );
+ dn.bv_len = len;
+
+ prev = vai->dn.bv_val;
+ while ( (p = memchr( prev, '$', end - prev )) != NULL ) {
+ len = p - prev;
+ AC_MEMCPY( dest, prev, len );
+ dest += len;
+ p += 1;
+
+ if ( ( *p >= '0' ) && ( *p <= '9' ) ) {
+ int i = *p - '0';
+ len = pmatch[i].rm_eo - pmatch[i].rm_so;
+
+ AC_MEMCPY( dest, ndn->bv_val + pmatch[i].rm_so, len );
+ dest += len;
+ } else if ( *p == '$' ) {
+ *dest++ = *p;
+ }
+ prev = p + 1;
+ }
+ len = end - prev;
+ AC_MEMCPY( dest, prev, len );
+ dest += len;
+ *dest = '\0';
+
+ rc = dnNormalize( 0, NULL, NULL, &dn, out, NULL );
+ ch_free( dn.bv_val );
+
+ return rc;
+}
+
+static int
+variant_build_entry(
+ Operation *op,
+ variantEntry_info *vei,
+ struct berval *dn,
+ Entry **ep,
+ int nmatch,
+ regmatch_t *pmatch )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ BackendDB *be_orig = op->o_bd, *db;
+ struct berval ndn = BER_BVNULL;
+ variantAttr_info *vai;
+ Attribute *a;
+ BerVarray nvals;
+ Entry *e;
+ unsigned int i;
+ int rc;
+
+ assert( ep );
+ assert( !*ep );
+
+ rc = overlay_entry_get_ov( op, dn, NULL, NULL, 0, &e, on );
+ if ( rc == LDAP_SUCCESS && is_entry_referral( e ) ) {
+ overlay_entry_release_ov( op, e, 0, on );
+ rc = LDAP_REFERRAL;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ *ep = entry_dup( e );
+ overlay_entry_release_ov( op, e, 0, on );
+
+ LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) {
+ if ( vei->type == VARIANT_INFO_REGEX ) {
+ rc = variant_build_dn( op, vai, nmatch, pmatch, &ndn );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ } else {
+ ndn = vai->dn;
+ }
+
+ (void)attr_delete( &(*ep)->e_attrs, vai->attr );
+ op->o_bd = be_orig;
+
+ /* only select backend if not served by ours, would retrace all
+ * overlays again */
+ db = select_backend( &ndn, 0 );
+ if ( db && db != be_orig->bd_self ) {
+ op->o_bd = db;
+ rc = be_entry_get_rw( op, &ndn, NULL, vai->alternative, 0, &e );
+ } else {
+ rc = overlay_entry_get_ov(
+ op, &ndn, NULL, vai->alternative, 0, &e, on );
+ }
+
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ break;
+ case LDAP_INSUFFICIENT_ACCESS:
+ case LDAP_NO_SUCH_ATTRIBUTE:
+ case LDAP_NO_SUCH_OBJECT:
+ rc = LDAP_SUCCESS;
+ continue;
+ break;
+ default:
+ goto done;
+ break;
+ }
+
+ a = attr_find( e->e_attrs, vai->alternative );
+
+ /* back-ldif doesn't check the attribute exists in the entry before
+ * returning it */
+ if ( a ) {
+ if ( a->a_nvals ) {
+ nvals = a->a_nvals;
+ } else {
+ nvals = a->a_vals;
+ }
+
+ for ( i = 0; i < a->a_numvals; i++ ) {
+ if ( backend_access( op, e, &ndn, vai->alternative, &nvals[i],
+ ACL_READ, NULL ) != LDAP_SUCCESS ) {
+ continue;
+ }
+
+ rc = attr_merge_one( *ep, vai->attr, &a->a_vals[i], &nvals[i] );
+ if ( rc != LDAP_SUCCESS ) {
+ break;
+ }
+ }
+ }
+
+ if ( db && db != be_orig->bd_self ) {
+ be_entry_release_rw( op, e, 0 );
+ } else {
+ overlay_entry_release_ov( op, e, 0, on );
+ }
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ }
+
+done:
+ op->o_bd = be_orig;
+ if ( rc != LDAP_SUCCESS && *ep ) {
+ entry_free( *ep );
+ *ep = NULL;
+ }
+ if ( vei->type == VARIANT_INFO_REGEX ) {
+ ch_free( ndn.bv_val );
+ }
+
+ return rc;
+}
+
+static int
+variant_find_config(
+ Operation *op,
+ variant_info_t *ov,
+ struct berval *ndn,
+ int which,
+ variantEntry_info **veip,
+ size_t nmatch,
+ regmatch_t *pmatch )
+{
+ variantEntry_info *vei;
+
+ assert( veip );
+
+ if ( which & VARIANT_INFO_PLAIN ) {
+ int diff;
+
+ LDAP_STAILQ_FOREACH( vei, &ov->variants, next ) {
+ dnMatch( &diff, 0, NULL, NULL, ndn, &vei->dn );
+ if ( diff ) continue;
+
+ *veip = vei;
+ return LDAP_SUCCESS;
+ }
+ }
+
+ if ( which & VARIANT_INFO_REGEX ) {
+ LDAP_STAILQ_FOREACH( vei, &ov->regex_variants, next ) {
+ if ( regexec( vei->regex, ndn->bv_val, nmatch, pmatch, 0 ) ) {
+ continue;
+ }
+
+ *veip = vei;
+ return LDAP_SUCCESS;
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+variant_op_add( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ variant_info_t *ov = on->on_bi.bi_private;
+ variantEntry_info *vei;
+ int rc;
+
+ /* Replication always uses the rootdn */
+ if ( ov->passReplication && SLAPD_SYNC_IS_SYNCCONN(op->o_connid) &&
+ be_isroot( op ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "variant_op_add: "
+ "dn=%s\n", op->o_req_ndn.bv_val );
+
+ rc = variant_find_config(
+ op, ov, &op->o_req_ndn, VARIANT_INFO_ALL, &vei, 0, NULL );
+ if ( rc == LDAP_SUCCESS ) {
+ variantAttr_info *vai;
+
+ LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) {
+ Attribute *a;
+ for ( a = op->ora_e->e_attrs; a; a = a->a_next ) {
+ if ( a->a_desc == vai->attr ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ send_ldap_error( op, rs, rc,
+ "variant: trying to add variant attributes" );
+ goto done;
+ }
+ }
+ }
+ }
+ rc = SLAP_CB_CONTINUE;
+
+done:
+ Debug( LDAP_DEBUG_TRACE, "variant_op_add: "
+ "finished with %d\n",
+ rc );
+ return rc;
+}
+
+static int
+variant_op_compare( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ variant_info_t *ov = on->on_bi.bi_private;
+ variantEntry_info *vei;
+ regmatch_t pmatch[10];
+ int rc, nmatch = sizeof(pmatch) / sizeof(regmatch_t);
+
+ Debug( LDAP_DEBUG_TRACE, "variant_op_compare: "
+ "dn=%s\n", op->o_req_ndn.bv_val );
+
+ rc = variant_find_config(
+ op, ov, &op->o_req_ndn, VARIANT_INFO_ALL, &vei, nmatch, pmatch );
+ if ( rc == LDAP_SUCCESS ) {
+ Entry *e = NULL;
+
+ rc = variant_build_entry( op, vei, &op->o_req_ndn, &e, nmatch, pmatch );
+ /* in case of error, just let the backend deal with the mod and the
+ * client should get a meaningful error back */
+ if ( rc != LDAP_SUCCESS ) {
+ rc = SLAP_CB_CONTINUE;
+ } else {
+ rc = slap_compare_entry( op, e, op->orc_ava );
+
+ entry_free( e );
+ e = NULL;
+ }
+ }
+
+ if ( rc != SLAP_CB_CONTINUE ) {
+ rs->sr_err = rc;
+ send_ldap_result( op, rs );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "variant_op_compare: "
+ "finished with %d\n", rc );
+ return rc;
+}
+
+static int
+variant_cmp_op( const void *l, const void *r )
+{
+ const Operation *left = l, *right = r;
+ int diff;
+
+ dnMatch( &diff, 0, NULL, NULL, (struct berval *)&left->o_req_ndn,
+ (void *)&right->o_req_ndn );
+
+ return diff;
+}
+
+static int
+variant_run_mod( void *nop, void *arg )
+{
+ SlapReply nrs = { REP_RESULT };
+ slap_callback cb = { 0 };
+ Operation *op = nop;
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ int *rc = arg;
+
+ cb.sc_response = slap_null_cb;
+ op->o_callback = &cb;
+
+ Debug( LDAP_DEBUG_TRACE, "variant_run_mod: "
+ "running mod on dn=%s\n",
+ op->o_req_ndn.bv_val );
+ *rc = on->on_info->oi_orig->bi_op_modify( op, &nrs );
+ Debug( LDAP_DEBUG_TRACE, "variant_run_mod: "
+ "finished with %d\n", *rc );
+
+ return ( *rc != LDAP_SUCCESS );
+}
+
+/** Move the Modifications back to the original Op so that they can be disposed
+ * of by the original creator
+ */
+static int
+variant_reassign_mods( void *nop, void *arg )
+{
+ Operation *op = nop, *orig_op = arg;
+ Modifications *mod;
+
+ assert( op->orm_modlist );
+
+ for ( mod = op->orm_modlist; mod->sml_next; mod = mod->sml_next )
+ /* get the tail mod */;
+
+ mod->sml_next = orig_op->orm_modlist;
+ orig_op->orm_modlist = op->orm_modlist;
+
+ return LDAP_SUCCESS;
+}
+
+void
+variant_free_op( void *op )
+{
+ ch_free( ((Operation *)op)->o_req_ndn.bv_val );
+ ch_free( op );
+}
+
+static int
+variant_op_mod( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ variant_info_t *ov = on->on_bi.bi_private;
+ variantEntry_info *vei;
+ variantAttr_info *vai;
+ Avlnode *ops = NULL;
+ Entry *e = NULL;
+ Modifications *mod, *nextmod;
+ regmatch_t pmatch[10];
+ int rc, nmatch = sizeof(pmatch) / sizeof(regmatch_t);
+
+ /* Replication always uses the rootdn */
+ if ( ov->passReplication && SLAPD_SYNC_IS_SYNCCONN(op->o_connid) &&
+ be_isroot( op ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "variant_op_mod: "
+ "dn=%s\n", op->o_req_ndn.bv_val );
+
+ rc = variant_find_config(
+ op, ov, &op->o_req_ndn, VARIANT_INFO_ALL, &vei, nmatch, pmatch );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "variant_op_mod: "
+ "not a variant\n" );
+ rc = SLAP_CB_CONTINUE;
+ goto done;
+ }
+
+ rc = variant_build_entry( op, vei, &op->o_req_ndn, &e, nmatch, pmatch );
+ /* in case of error, just let the backend deal with the mod and the client
+ * should get a meaningful error back */
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "variant_op_mod: "
+ "failed to retrieve entry\n" );
+ rc = SLAP_CB_CONTINUE;
+ goto done;
+ }
+
+ rc = acl_check_modlist( op, e, op->orm_modlist );
+ entry_free( e );
+
+ if ( !rc ) {
+ rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ send_ldap_error( op, rs, rc, "" );
+ return rc;
+ }
+
+ for ( mod = op->orm_modlist; mod; mod = nextmod ) {
+ Operation needle = { .o_req_ndn = BER_BVNULL }, *nop;
+
+ nextmod = mod->sml_next;
+
+ LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) {
+ if ( vai->attr == mod->sml_desc ) {
+ break;
+ }
+ }
+
+ if ( vai ) {
+ if ( vei->type == VARIANT_INFO_REGEX ) {
+ rc = variant_build_dn(
+ op, vai, nmatch, pmatch, &needle.o_req_ndn );
+ if ( rc != LDAP_SUCCESS ) {
+ continue;
+ }
+ } else {
+ needle.o_req_ndn = vai->dn;
+ }
+
+ nop = ldap_avl_find( ops, &needle, variant_cmp_op );
+ if ( nop == NULL ) {
+ nop = ch_calloc( 1, sizeof(Operation) );
+ *nop = *op;
+
+ ber_dupbv( &nop->o_req_ndn, &needle.o_req_ndn );
+ nop->o_req_dn = nop->o_req_ndn;
+ nop->orm_modlist = NULL;
+
+ rc = ldap_avl_insert( &ops, nop, variant_cmp_op, ldap_avl_dup_error );
+ assert( rc == 0 );
+ }
+ mod->sml_desc = vai->alternative;
+
+ op->orm_modlist = nextmod;
+ mod->sml_next = nop->orm_modlist;
+ nop->orm_modlist = mod;
+
+ if ( vei->type == VARIANT_INFO_REGEX ) {
+ ch_free( needle.o_req_ndn.bv_val );
+ }
+ }
+ }
+
+ if ( !ops ) {
+ Debug( LDAP_DEBUG_TRACE, "variant_op_mod: "
+ "no variant attributes in mod\n" );
+ return SLAP_CB_CONTINUE;
+ }
+
+ /*
+ * First run original Operation
+ * This will take care of making sure the entry exists as well.
+ *
+ * FIXME?
+ * Since we cannot make the subsequent Ops atomic wrt. this one, we just
+ * let it send the response as well. After all, the changes on the main DN
+ * have finished by then
+ */
+ rc = on->on_info->oi_orig->bi_op_modify( op, rs );
+ if ( rc == LDAP_SUCCESS ) {
+ /* FIXME: if a mod fails, should we attempt to apply the rest? */
+ ldap_avl_apply( ops, variant_run_mod, &rc, -1, AVL_INORDER );
+ }
+
+ ldap_avl_apply( ops, variant_reassign_mods, op, -1, AVL_INORDER );
+ ldap_avl_free( ops, variant_free_op );
+
+done:
+ Debug( LDAP_DEBUG_TRACE, "variant_op_mod: "
+ "finished with %d\n", rc );
+ return rc;
+}
+
+static int
+variant_search_response( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = op->o_callback->sc_private;
+ variant_info_t *ov = on->on_bi.bi_private;
+ variantEntry_info *vei;
+ int rc;
+
+ if ( rs->sr_type == REP_RESULT ) {
+ ch_free( op->o_callback );
+ op->o_callback = NULL;
+ }
+
+ if ( rs->sr_type != REP_SEARCH ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ rc = variant_find_config(
+ op, ov, &rs->sr_entry->e_nname, VARIANT_INFO_PLAIN, &vei, 0, NULL );
+ if ( rc == LDAP_SUCCESS ) {
+ rs->sr_nentries--;
+ return rc;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+variant_op_search( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ variant_info_t *ov = on->on_bi.bi_private;
+ variantEntry_info *vei;
+ slap_callback *cb;
+ Entry *e = NULL;
+ regmatch_t pmatch[10];
+ int variantInScope = 0, rc = SLAP_CB_CONTINUE,
+ nmatch = sizeof(pmatch) / sizeof(regmatch_t);
+
+ if ( ov->passReplication && ( op->o_sync > SLAP_CONTROL_IGNORED ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
+ "dn=%s, scope=%d\n",
+ op->o_req_ndn.bv_val, op->ors_scope );
+
+ LDAP_STAILQ_FOREACH( vei, &ov->variants, next ) {
+ if ( !dnIsSuffixScope( &vei->dn, &op->o_req_ndn, op->ors_scope ) )
+ continue;
+
+ variantInScope = 1;
+
+ rc = variant_build_entry( op, vei, &vei->dn, &e, 0, NULL );
+ if ( rc == LDAP_NO_SUCH_OBJECT || rc == LDAP_REFERRAL ) {
+ rc = SLAP_CB_CONTINUE;
+ continue;
+ } else if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
+ "failed to retrieve entry: dn=%s\n",
+ vei->dn.bv_val );
+ goto done;
+ }
+
+ if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE ) {
+ Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
+ "entry matched: dn=%s\n",
+ vei->dn.bv_val );
+ rs->sr_entry = e;
+ rs->sr_attrs = op->ors_attrs;
+ rc = send_search_entry( op, rs );
+ }
+ entry_free( e );
+ e = NULL;
+ }
+
+ /* Three options:
+ * - the entry has been handled above, in that case vei->type is VARIANT_INFO_PLAIN
+ * - the entry matches a regex, use the first one and we're finished
+ * - no configuration matches entry - do nothing
+ */
+ if ( op->ors_scope == LDAP_SCOPE_BASE &&
+ variant_find_config( op, ov, &op->o_req_ndn, VARIANT_INFO_ALL, &vei,
+ nmatch, pmatch ) == LDAP_SUCCESS &&
+ vei->type == VARIANT_INFO_REGEX ) {
+ rc = variant_build_entry( op, vei, &op->o_req_ndn, &e, nmatch, pmatch );
+ if ( rc == LDAP_NO_SUCH_OBJECT || rc == LDAP_REFERRAL ) {
+ rc = SLAP_CB_CONTINUE;
+ } else if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
+ "failed to retrieve entry: dn=%s\n",
+ vei->dn.bv_val );
+ goto done;
+ } else {
+ if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE ) {
+ Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
+ "entry matched: dn=%s\n",
+ vei->dn.bv_val );
+ rs->sr_entry = e;
+ rs->sr_attrs = op->ors_attrs;
+ rc = send_search_entry( op, rs );
+ }
+ entry_free( e );
+ e = NULL;
+ goto done;
+ }
+ }
+ rc = SLAP_CB_CONTINUE;
+
+ if ( variantInScope ) {
+ cb = ch_calloc( 1, sizeof(slap_callback) );
+ cb->sc_private = on;
+ cb->sc_response = variant_search_response;
+ cb->sc_next = op->o_callback;
+
+ op->o_callback = cb;
+ }
+
+done:
+ if ( rc != SLAP_CB_CONTINUE ) {
+ rs->sr_err = (rc == LDAP_SUCCESS) ? rc : LDAP_OTHER;
+ send_ldap_result( op, rs );
+ }
+ Debug( LDAP_DEBUG_TRACE, "variant_op_search: "
+ "finished with %d\n", rc );
+ return rc;
+}
+
+/* Configuration */
+
+static ConfigLDAPadd variant_ldadd;
+static ConfigLDAPadd variant_regex_ldadd;
+static ConfigLDAPadd variant_attr_ldadd;
+
+static ConfigDriver variant_set_dn;
+static ConfigDriver variant_set_regex;
+static ConfigDriver variant_set_alt_dn;
+static ConfigDriver variant_set_alt_pattern;
+static ConfigDriver variant_set_attribute;
+static ConfigDriver variant_add_alt_attr;
+static ConfigDriver variant_add_alt_attr_regex;
+
+static ConfigCfAdd variant_cfadd;
+
+enum
+{
+ VARIANT_ATTR = 1,
+ VARIANT_ATTR_ALT,
+
+ VARIANT_LAST,
+};
+
+static ConfigTable variant_cfg[] = {
+ { "passReplication", "on|off", 2, 2, 0,
+ ARG_ON_OFF|ARG_OFFSET,
+ (void *)offsetof( variant_info_t, passReplication ),
+ "( OLcfgOvAt:FIXME.1 NAME 'olcVariantPassReplication' "
+ "DESC 'Whether to let searches with replication control "
+ "pass unmodified' "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "variantDN", "dn", 2, 2, 0,
+ ARG_DN|ARG_QUOTE|ARG_MAGIC,
+ variant_set_dn,
+ "( OLcfgOvAt:FIXME.2 NAME 'olcVariantEntry' "
+ "DESC 'DN of the variant entry' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "variantRegex", "regex", 2, 2, 0,
+ ARG_BERVAL|ARG_QUOTE|ARG_MAGIC,
+ variant_set_regex,
+ "( OLcfgOvAt:FIXME.6 NAME 'olcVariantEntryRegex' "
+ "DESC 'Pattern for the variant entry' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ /* These have no equivalent in slapd.conf */
+ { "", NULL, 2, 2, 0,
+ ARG_STRING|ARG_MAGIC|VARIANT_ATTR,
+ variant_set_attribute,
+ "( OLcfgOvAt:FIXME.3 NAME 'olcVariantVariantAttribute' "
+ "DESC 'Attribute to fill in the entry' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "", NULL, 2, 2, 0,
+ ARG_STRING|ARG_MAGIC|VARIANT_ATTR_ALT,
+ variant_set_attribute,
+ "( OLcfgOvAt:FIXME.4 NAME 'olcVariantAlternativeAttribute' "
+ "DESC 'Attribute to take from the alternative entry' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "", NULL, 2, 2, 0,
+ ARG_DN|ARG_QUOTE|ARG_MAGIC,
+ variant_set_alt_dn,
+ "( OLcfgOvAt:FIXME.5 NAME 'olcVariantAlternativeEntry' "
+ "DESC 'DN of the alternative entry' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "", NULL, 2, 2, 0,
+ ARG_BERVAL|ARG_QUOTE|ARG_MAGIC,
+ variant_set_alt_pattern,
+ "( OLcfgOvAt:FIXME.7 NAME 'olcVariantAlternativeEntryPattern' "
+ "DESC 'Replacement pattern to locate the alternative entry' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ /* slapd.conf alternatives for the four above */
+ { "variantSpec", "attr attr2 dn", 4, 4, 0,
+ ARG_QUOTE|ARG_MAGIC,
+ variant_add_alt_attr,
+ NULL, NULL, NULL
+ },
+ { "variantRegexSpec", "attr attr2 pattern", 4, 4, 0,
+ ARG_QUOTE|ARG_MAGIC,
+ variant_add_alt_attr_regex,
+ NULL, NULL, NULL
+ },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs variant_ocs[] = {
+ { "( OLcfgOvOc:FIXME.1 "
+ "NAME 'olcVariantConfig' "
+ "DESC 'Variant overlay configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcVariantPassReplication ) )",
+ Cft_Overlay, variant_cfg, NULL, variant_cfadd },
+ { "( OLcfgOvOc:FIXME.2 "
+ "NAME 'olcVariantVariant' "
+ "DESC 'Variant configuration' "
+ "MUST ( olcVariantEntry ) "
+ "MAY ( name ) "
+ "SUP top "
+ "STRUCTURAL )",
+ Cft_Misc, variant_cfg, variant_ldadd },
+ { "( OLcfgOvOc:FIXME.3 "
+ "NAME 'olcVariantAttribute' "
+ "DESC 'Variant attribute description' "
+ "MUST ( olcVariantVariantAttribute $ "
+ "olcVariantAlternativeAttribute $ "
+ "olcVariantAlternativeEntry "
+ ") "
+ "MAY name "
+ "SUP top "
+ "STRUCTURAL )",
+ Cft_Misc, variant_cfg, variant_attr_ldadd },
+ { "( OLcfgOvOc:FIXME.4 "
+ "NAME 'olcVariantRegex' "
+ "DESC 'Variant configuration' "
+ "MUST ( olcVariantEntryRegex ) "
+ "MAY ( name ) "
+ "SUP top "
+ "STRUCTURAL )",
+ Cft_Misc, variant_cfg, variant_regex_ldadd },
+ { "( OLcfgOvOc:FIXME.5 "
+ "NAME 'olcVariantAttributePattern' "
+ "DESC 'Variant attribute description' "
+ "MUST ( olcVariantVariantAttribute $ "
+ "olcVariantAlternativeAttribute $ "
+ "olcVariantAlternativeEntryPattern "
+ ") "
+ "MAY name "
+ "SUP top "
+ "STRUCTURAL )",
+ Cft_Misc, variant_cfg, variant_attr_ldadd },
+
+ { NULL, 0, NULL }
+};
+
+static int
+variant_set_dn( ConfigArgs *ca )
+{
+ variantEntry_info *vei2, *vei = ca->ca_private;
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ variant_info_t *ov = on->on_bi.bi_private;
+ int diff;
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ value_add_one( &ca->rvalue_vals, &vei->dn );
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ ber_memfree( vei->dn.bv_val );
+ BER_BVZERO( &vei->dn );
+ return LDAP_SUCCESS;
+ }
+
+ if ( !vei ) {
+ vei = ch_calloc( 1, sizeof(variantEntry_info) );
+ vei->ov = ov;
+ vei->type = VARIANT_INFO_PLAIN;
+ LDAP_SLIST_INIT(&vei->attributes);
+ LDAP_STAILQ_ENTRY_INIT(vei, next);
+ LDAP_STAILQ_INSERT_TAIL(&ov->variants, vei, next);
+
+ ca->ca_private = vei;
+ }
+ vei->dn = ca->value_ndn;
+ ber_memfree( ca->value_dn.bv_val );
+
+ /* Each DN should only be listed once */
+ LDAP_STAILQ_FOREACH( vei2, &vei->ov->variants, next ) {
+ if ( vei == vei2 ) continue;
+
+ dnMatch( &diff, 0, NULL, NULL, &vei->dn, &vei2->dn );
+ if ( !diff ) {
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_set_regex( ConfigArgs *ca )
+{
+ variantEntry_info *vei2, *vei = ca->ca_private;
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ variant_info_t *ov = on->on_bi.bi_private;
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ ca->value_bv = vei->dn;
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ ber_memfree( vei->dn.bv_val );
+ BER_BVZERO( &vei->dn );
+ regfree( vei->regex );
+ return LDAP_SUCCESS;
+ }
+
+ if ( !vei ) {
+ vei = ch_calloc( 1, sizeof(variantEntry_info) );
+ vei->ov = ov;
+ vei->type = VARIANT_INFO_REGEX;
+ LDAP_SLIST_INIT(&vei->attributes);
+ LDAP_STAILQ_ENTRY_INIT(vei, next);
+ LDAP_STAILQ_INSERT_TAIL(&ov->regex_variants, vei, next);
+
+ ca->ca_private = vei;
+ }
+ vei->dn = ca->value_bv;
+
+ /* Each regex should only be listed once */
+ LDAP_STAILQ_FOREACH( vei2, &vei->ov->regex_variants, next ) {
+ if ( vei == vei2 ) continue;
+
+ if ( !ber_bvcmp( &ca->value_bv, &vei2->dn ) ) {
+ ch_free( vei );
+ ca->ca_private = NULL;
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+ }
+
+ vei->regex = ch_calloc( 1, sizeof(regex_t) );
+ if ( regcomp( vei->regex, vei->dn.bv_val, REG_EXTENDED ) ) {
+ ch_free( vei->regex );
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_set_alt_dn( ConfigArgs *ca )
+{
+ variantAttr_info *vai = ca->ca_private;
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ value_add_one( &ca->rvalue_vals, &vai->dn );
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ ber_memfree( vai->dn.bv_val );
+ BER_BVZERO( &vai->dn );
+ return LDAP_SUCCESS;
+ }
+
+ vai->dn = ca->value_ndn;
+ ber_memfree( ca->value_dn.bv_val );
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_set_alt_pattern( ConfigArgs *ca )
+{
+ variantAttr_info *vai = ca->ca_private;
+ char *p = ca->value_bv.bv_val,
+ *end = ca->value_bv.bv_val + ca->value_bv.bv_len;
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ ca->value_bv = vai->dn;
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ ber_memfree( vai->dn.bv_val );
+ BER_BVZERO( &vai->dn );
+ return LDAP_SUCCESS;
+ }
+
+ while ( (p = memchr( p, '$', end - p )) != NULL ) {
+ p += 1;
+
+ if ( ( ( *p >= '0' ) && ( *p <= '9' ) ) || ( *p == '$' ) ) {
+ p += 1;
+ } else {
+ Debug( LDAP_DEBUG_ANY, "variant_set_alt_pattern: "
+ "invalid replacement pattern supplied '%s'\n",
+ ca->value_bv.bv_val );
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+ }
+
+ vai->dn = ca->value_bv;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_set_attribute( ConfigArgs *ca )
+{
+ variantAttr_info *vai2, *vai = ca->ca_private;
+ char *s = ca->value_string;
+ const char *text;
+ AttributeDescription **ad;
+ int rc;
+
+ if ( ca->type == VARIANT_ATTR ) {
+ ad = &vai->attr;
+ } else {
+ ad = &vai->alternative;
+ }
+
+ if ( ca->op == SLAP_CONFIG_EMIT ) {
+ ca->value_string = ch_strdup( (*ad)->ad_cname.bv_val );
+ return LDAP_SUCCESS;
+ } else if ( ca->op == LDAP_MOD_DELETE ) {
+ *ad = NULL;
+ return LDAP_SUCCESS;
+ }
+
+ if ( *s == '{' ) {
+ s = strchr( s, '}' );
+ if ( !s ) {
+ ca->reply.err = LDAP_UNDEFINED_TYPE;
+ return ca->reply.err;
+ }
+ s += 1;
+ }
+
+ rc = slap_str2ad( s, ad, &text );
+ ber_memfree( ca->value_string );
+ if ( rc ) {
+ return rc;
+ }
+
+ /* Both attributes have to share the same syntax */
+ if ( vai->attr && vai->alternative &&
+ vai->attr->ad_type->sat_syntax !=
+ vai->alternative->ad_type->sat_syntax ) {
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+
+ if ( ca->type == VARIANT_ATTR ) {
+ /* Each attribute should only be listed once */
+ LDAP_SLIST_FOREACH( vai2, &vai->variant->attributes, next ) {
+ if ( vai == vai2 ) continue;
+ if ( vai->attr == vai2->attr ) {
+ ca->reply.err = LDAP_CONSTRAINT_VIOLATION;
+ return ca->reply.err;
+ }
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_add_alt_attr( ConfigArgs *ca )
+{
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ variant_info_t *ov = on->on_bi.bi_private;
+ variantEntry_info *vei =
+ LDAP_STAILQ_LAST( &ov->variants, variantEntry_info, next );
+ variantAttr_info *vai;
+ struct berval dn, ndn;
+ int rc;
+
+ vai = ch_calloc( 1, sizeof(variantAttr_info) );
+ vai->variant = vei;
+ LDAP_SLIST_ENTRY_INIT( vai, next );
+ ca->ca_private = vai;
+
+ ca->value_string = ch_strdup( ca->argv[1] );
+ ca->type = VARIANT_ATTR;
+ rc = variant_set_attribute( ca );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ ca->value_string = ch_strdup( ca->argv[2] );
+ ca->type = VARIANT_ATTR_ALT;
+ rc = variant_set_attribute( ca );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ dn.bv_val = ca->argv[3];
+ dn.bv_len = strlen( dn.bv_val );
+ rc = dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ ca->type = 0;
+ BER_BVZERO( &ca->value_dn );
+ ca->value_ndn = ndn;
+ rc = variant_set_alt_dn( ca );
+ if ( rc != LDAP_SUCCESS ) {
+ ch_free( ndn.bv_val );
+ goto done;
+ }
+
+done:
+ if ( rc == LDAP_SUCCESS ) {
+ LDAP_SLIST_INSERT_HEAD( &vei->attributes, vai, next );
+ } else {
+ ca->reply.err = rc;
+ }
+
+ return rc;
+}
+
+static int
+variant_add_alt_attr_regex( ConfigArgs *ca )
+{
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ variant_info_t *ov = on->on_bi.bi_private;
+ variantEntry_info *vei =
+ LDAP_STAILQ_LAST( &ov->regex_variants, variantEntry_info, next );
+ variantAttr_info *vai;
+ int rc;
+
+ vai = ch_calloc( 1, sizeof(variantAttr_info) );
+ vai->variant = vei;
+ LDAP_SLIST_ENTRY_INIT( vai, next );
+ ca->ca_private = vai;
+
+ ca->value_string = ch_strdup( ca->argv[1] );
+ ca->type = VARIANT_ATTR;
+ rc = variant_set_attribute( ca );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ ca->value_string = ch_strdup( ca->argv[2] );
+ ca->type = VARIANT_ATTR_ALT;
+ rc = variant_set_attribute( ca );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ ca->type = 0;
+ ber_str2bv( ca->argv[3], 0, 1, &ca->value_bv );
+ rc = variant_set_alt_pattern( ca );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+done:
+ if ( rc == LDAP_SUCCESS ) {
+ LDAP_SLIST_INSERT_HEAD( &vei->attributes, vai, next );
+ } else {
+ ca->reply.err = rc;
+ }
+
+ return rc;
+}
+
+static int
+variant_ldadd_cleanup( ConfigArgs *ca )
+{
+ variantEntry_info *vei = ca->ca_private;
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ variant_info_t *ov = on->on_bi.bi_private;
+
+ if ( ca->reply.err != LDAP_SUCCESS ) {
+ assert( LDAP_SLIST_EMPTY(&vei->attributes) );
+ ch_free( vei );
+ return LDAP_SUCCESS;
+ }
+
+ if ( vei->type == VARIANT_INFO_PLAIN ) {
+ LDAP_STAILQ_INSERT_TAIL(&ov->variants, vei, next);
+ } else {
+ LDAP_STAILQ_INSERT_TAIL(&ov->regex_variants, vei, next);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_ldadd( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
+{
+ slap_overinst *on;
+ variant_info_t *ov;
+ variantEntry_info *vei;
+
+ if ( cei->ce_type != Cft_Overlay || !cei->ce_bi ||
+ cei->ce_bi->bi_cf_ocs != variant_ocs )
+ return LDAP_CONSTRAINT_VIOLATION;
+
+ on = (slap_overinst *)cei->ce_bi;
+ ov = on->on_bi.bi_private;
+
+ vei = ch_calloc( 1, sizeof(variantEntry_info) );
+ vei->ov = ov;
+ vei->type = VARIANT_INFO_PLAIN;
+ LDAP_SLIST_INIT(&vei->attributes);
+ LDAP_STAILQ_ENTRY_INIT(vei, next);
+
+ ca->bi = cei->ce_bi;
+ ca->ca_private = vei;
+ config_push_cleanup( ca, variant_ldadd_cleanup );
+ /* config_push_cleanup is only run in the case of online config but we use it to
+ * save the new config when done with the entry */
+ ca->lineno = 0;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_regex_ldadd( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
+{
+ slap_overinst *on;
+ variant_info_t *ov;
+ variantEntry_info *vei;
+
+ if ( cei->ce_type != Cft_Overlay || !cei->ce_bi ||
+ cei->ce_bi->bi_cf_ocs != variant_ocs )
+ return LDAP_CONSTRAINT_VIOLATION;
+
+ on = (slap_overinst *)cei->ce_bi;
+ ov = on->on_bi.bi_private;
+
+ vei = ch_calloc( 1, sizeof(variantEntry_info) );
+ vei->ov = ov;
+ vei->type = VARIANT_INFO_REGEX;
+ LDAP_SLIST_INIT(&vei->attributes);
+ LDAP_STAILQ_ENTRY_INIT(vei, next);
+
+ ca->bi = cei->ce_bi;
+ ca->ca_private = vei;
+ config_push_cleanup( ca, variant_ldadd_cleanup );
+ /* config_push_cleanup is only run in the case of online config but we use it to
+ * save the new config when done with the entry */
+ ca->lineno = 0;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_attr_ldadd_cleanup( ConfigArgs *ca )
+{
+ variantAttr_info *vai = ca->ca_private;
+ variantEntry_info *vei = vai->variant;
+
+ if ( ca->reply.err != LDAP_SUCCESS ) {
+ ch_free( vai );
+ return LDAP_SUCCESS;
+ }
+
+ LDAP_SLIST_INSERT_HEAD(&vei->attributes, vai, next);
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_attr_ldadd( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
+{
+ variantEntry_info *vei;
+ variantAttr_info *vai;
+ CfEntryInfo *parent = cei->ce_parent;
+
+ if ( cei->ce_type != Cft_Misc || !parent || !parent->ce_bi ||
+ parent->ce_bi->bi_cf_ocs != variant_ocs )
+ return LDAP_CONSTRAINT_VIOLATION;
+
+ vei = (variantEntry_info *)cei->ce_private;
+
+ vai = ch_calloc( 1, sizeof(variantAttr_info) );
+ vai->variant = vei;
+ LDAP_SLIST_ENTRY_INIT(vai, next);
+
+ ca->ca_private = vai;
+ config_push_cleanup( ca, variant_attr_ldadd_cleanup );
+ /* config_push_cleanup is only run in the case of online config but we use it to
+ * save the new config when done with the entry */
+ ca->lineno = 0;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
+{
+ slap_overinst *on = (slap_overinst *)ca->bi;
+ variant_info_t *ov = on->on_bi.bi_private;
+ variantEntry_info *vei;
+ variantAttr_info *vai;
+ Entry *e;
+ struct berval rdn;
+ int i = 0;
+
+ LDAP_STAILQ_FOREACH( vei, &ov->variants, next ) {
+ int j = 0;
+ rdn.bv_len = snprintf(
+ ca->cr_msg, sizeof(ca->cr_msg), "name={%d}variant", i++ );
+ rdn.bv_val = ca->cr_msg;
+
+ ca->ca_private = vei;
+ e = config_build_entry(
+ op, rs, p->e_private, ca, &rdn, &variant_ocs[1], NULL );
+ assert( e );
+
+ LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) {
+ rdn.bv_len = snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "olcVariantVariantAttribute={%d}%s", j++,
+ vai->attr->ad_cname.bv_val );
+ rdn.bv_val = ca->cr_msg;
+
+ ca->ca_private = vai;
+ config_build_entry(
+ op, rs, e->e_private, ca, &rdn, &variant_ocs[2], NULL );
+ }
+ }
+
+ LDAP_STAILQ_FOREACH( vei, &ov->regex_variants, next ) {
+ int j = 0;
+ rdn.bv_len = snprintf(
+ ca->cr_msg, sizeof(ca->cr_msg), "name={%d}regex", i++ );
+ rdn.bv_val = ca->cr_msg;
+
+ ca->ca_private = vei;
+ e = config_build_entry(
+ op, rs, p->e_private, ca, &rdn, &variant_ocs[3], NULL );
+ assert( e );
+
+ LDAP_SLIST_FOREACH( vai, &vei->attributes, next ) {
+ rdn.bv_len = snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "olcVariantVariantAttribute={%d}%s", j++,
+ vai->attr->ad_cname.bv_val );
+ rdn.bv_val = ca->cr_msg;
+
+ ca->ca_private = vai;
+ config_build_entry(
+ op, rs, e->e_private, ca, &rdn, &variant_ocs[4], NULL );
+ }
+ }
+ return LDAP_SUCCESS;
+}
+
+static slap_overinst variant;
+
+static int
+variant_db_init( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ variant_info_t *ov;
+
+ if ( SLAP_ISGLOBALOVERLAY(be) ) {
+ Debug( LDAP_DEBUG_ANY, "variant overlay must be instantiated within "
+ "a database.\n" );
+ return 1;
+ }
+
+ ov = ch_calloc( 1, sizeof(variant_info_t) );
+ LDAP_STAILQ_INIT(&ov->variants);
+ LDAP_STAILQ_INIT(&ov->regex_variants);
+
+ on->on_bi.bi_private = ov;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+variant_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ variant_info_t *ov = on->on_bi.bi_private;
+
+ if ( ov ) {
+ while ( !LDAP_STAILQ_EMPTY( &ov->variants ) ) {
+ variantEntry_info *vei = LDAP_STAILQ_FIRST( &ov->variants );
+ LDAP_STAILQ_REMOVE_HEAD( &ov->variants, next );
+
+ while ( !LDAP_SLIST_EMPTY( &vei->attributes ) ) {
+ variantAttr_info *vai = LDAP_SLIST_FIRST( &vei->attributes );
+ LDAP_SLIST_REMOVE_HEAD( &vei->attributes, next );
+
+ ber_memfree( vai->dn.bv_val );
+ ch_free( vai );
+ }
+ ber_memfree( vei->dn.bv_val );
+ ch_free( vei );
+ }
+ while ( !LDAP_STAILQ_EMPTY( &ov->regex_variants ) ) {
+ variantEntry_info *vei = LDAP_STAILQ_FIRST( &ov->regex_variants );
+ LDAP_STAILQ_REMOVE_HEAD( &ov->regex_variants, next );
+
+ while ( !LDAP_SLIST_EMPTY( &vei->attributes ) ) {
+ variantAttr_info *vai = LDAP_SLIST_FIRST( &vei->attributes );
+ LDAP_SLIST_REMOVE_HEAD( &vei->attributes, next );
+
+ ber_memfree( vai->dn.bv_val );
+ ch_free( vai );
+ }
+ ber_memfree( vei->dn.bv_val );
+ ch_free( vei );
+ }
+ ch_free( ov );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int
+variant_initialize()
+{
+ int rc;
+
+ variant.on_bi.bi_type = "variant";
+ variant.on_bi.bi_db_init = variant_db_init;
+ variant.on_bi.bi_db_destroy = variant_db_destroy;
+
+ variant.on_bi.bi_op_add = variant_op_add;
+ variant.on_bi.bi_op_compare = variant_op_compare;
+ variant.on_bi.bi_op_modify = variant_op_mod;
+ variant.on_bi.bi_op_search = variant_op_search;
+
+ variant.on_bi.bi_cf_ocs = variant_ocs;
+
+ rc = config_register_schema( variant_cfg, variant_ocs );
+ if ( rc ) return rc;
+
+ return overlay_register( &variant );
+}
+
+#if SLAPD_OVER_VARIANT == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return variant_initialize();
+}
+#endif
+
+#endif /* SLAPD_OVER_VARIANT */
diff --git a/contrib/slapd-modules/vc/Makefile b/contrib/slapd-modules/vc/Makefile
new file mode 100644
index 0000000..9fe67ab
--- /dev/null
+++ b/contrib/slapd-modules/vc/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+# Copyright 2004 Howard Chu, Symas Corp. All Rights Reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted only as authorized by the OpenLDAP
+# Public License.
+#
+# A copy of this license is available in the file LICENSE in the
+# top-level directory of the distribution or, alternatively, at
+# <http://www.OpenLDAP.org/license.html>.
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = vc.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+vc.la: vc.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
diff --git a/contrib/slapd-modules/vc/vc.c b/contrib/slapd-modules/vc/vc.c
new file mode 100644
index 0000000..0760af2
--- /dev/null
+++ b/contrib/slapd-modules/vc/vc.c
@@ -0,0 +1,439 @@
+/* vc.c - LDAP Verify Credentials extop (no spec yet) */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2010-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+/*
+ * LDAP Verify Credentials: suggested by Kurt Zeilenga
+ * no spec yet
+ */
+
+#include "portable.h"
+
+#include "slap.h"
+#include "ac/string.h"
+
+typedef struct vc_conn_t {
+ struct vc_conn_t *conn;
+ Connection connbuf;
+ OperationBuffer opbuf;
+ Operation *op;
+ int refcnt;
+} vc_conn_t;
+
+static const struct berval vc_exop_oid_bv = BER_BVC(LDAP_EXOP_VERIFY_CREDENTIALS);
+static ldap_pvt_thread_mutex_t vc_mutex;
+static Avlnode *vc_tree;
+
+static int
+vc_conn_cmp( const void *c1, const void *c2 )
+{
+ const vc_conn_t *vc1 = (const vc_conn_t *)c1;
+ const vc_conn_t *vc2 = (const vc_conn_t *)c2;
+
+ return SLAP_PTRCMP( vc1->conn, vc2->conn );
+}
+
+static int
+vc_conn_dup( void *c1, void *c2 )
+{
+ vc_conn_t *vc1 = (vc_conn_t *)c1;
+ vc_conn_t *vc2 = (vc_conn_t *)c2;
+
+ if ( vc1->conn == vc2->conn ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+vc_create_response(
+ void *conn,
+ int resultCode,
+ const char *diagnosticMessage,
+ struct berval *servercred,
+ struct berval *authzid,
+ LDAPControl **ctrls,
+ struct berval **val )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ struct berval bv;
+ int rc;
+
+ assert( val != NULL );
+
+ *val = NULL;
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+
+ (void)ber_printf( ber, "{is" /*}*/ , resultCode, diagnosticMessage ? diagnosticMessage : "" );
+
+ if ( conn ) {
+ struct berval cookie;
+
+ cookie.bv_len = sizeof( conn );
+ cookie.bv_val = (char *)&conn;
+ (void)ber_printf( ber, "tO", 0, LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE, &cookie );
+ }
+
+ if ( servercred ) {
+ ber_printf( ber, "tO", LDAP_TAG_EXOP_VERIFY_CREDENTIALS_SCREDS, servercred );
+ }
+
+#if 0
+ if ( authzid ) {
+ ber_printf( ber, "tO", LDAP_TAG_EXOP_VERIFY_CREDENTIALS_AUTHZID, authzid );
+ }
+#endif
+
+ if ( ctrls ) {
+ int c;
+
+ rc = ber_printf( ber, "t{"/*}*/, LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS );
+ if ( rc == -1 ) goto done;
+
+ for ( c = 0; ctrls[c] != NULL; c++ ) {
+ rc = ber_printf( ber, "{s" /*}*/, ctrls[c]->ldctl_oid );
+
+ if ( ctrls[c]->ldctl_iscritical ) {
+ rc = ber_printf( ber, "b", (ber_int_t)ctrls[c]->ldctl_iscritical ) ;
+ if ( rc == -1 ) goto done;
+ }
+
+ if ( ctrls[c]->ldctl_value.bv_val != NULL ) {
+ rc = ber_printf( ber, "O", &ctrls[c]->ldctl_value );
+ if( rc == -1 ) goto done;
+ }
+
+ rc = ber_printf( ber, /*{*/"N}" );
+ if ( rc == -1 ) goto done;
+ }
+
+ rc = ber_printf( ber, /*{*/"N}" );
+ if ( rc == -1 ) goto done;
+ }
+
+ rc = ber_printf( ber, /*{*/ "}" );
+ if ( rc == -1 ) goto done;
+
+ rc = ber_flatten2( ber, &bv, 0 );
+ if ( rc == 0 ) {
+ *val = ber_bvdup( &bv );
+ }
+
+done:;
+ ber_free_buf( ber );
+
+ return rc;
+}
+
+typedef struct vc_cb_t {
+ struct berval sasldata;
+ LDAPControl **ctrls;
+} vc_cb_t;
+
+static int
+vc_cb(
+ Operation *op,
+ SlapReply *rs )
+{
+ vc_cb_t *vc = (vc_cb_t *)op->o_callback->sc_private;
+
+ if ( rs->sr_tag == LDAP_RES_BIND ) {
+ if ( rs->sr_sasldata != NULL ) {
+ ber_dupbv( &vc->sasldata, rs->sr_sasldata );
+ }
+
+ if ( rs->sr_ctrls != NULL ) {
+ vc->ctrls = ldap_controls_dup( rs->sr_ctrls );
+ }
+ }
+
+ return 0;
+}
+
+static int
+vc_exop(
+ Operation *op,
+ SlapReply *rs )
+{
+ int rc = LDAP_SUCCESS;
+ ber_tag_t tag;
+ ber_len_t len = -1;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ struct berval reqdata = BER_BVNULL;
+
+ struct berval cookie = BER_BVNULL;
+ struct berval bdn = BER_BVNULL;
+ ber_tag_t authtag;
+ struct berval cred = BER_BVNULL;
+ struct berval ndn = BER_BVNULL;
+ struct berval mechanism = BER_BVNULL;
+
+ vc_conn_t *conn = NULL;
+ vc_cb_t vc = { 0 };
+ slap_callback sc = { 0 };
+ SlapReply rs2 = { 0 };
+
+ if ( op->ore_reqdata == NULL || op->ore_reqdata->bv_len == 0 ) {
+ rs->sr_text = "empty request data field in VerifyCredentials exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ /* optimistic */
+ rs->sr_err = LDAP_SUCCESS;
+
+ ber_dupbv_x( &reqdata, op->ore_reqdata, op->o_tmpmemctx );
+
+ /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+ ber_init2( ber, &reqdata, 0 );
+
+ tag = ber_scanf( ber, "{" /*}*/ );
+ if ( tag != LBER_SEQUENCE ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE ) {
+ /*
+ * cookie: the pointer to the connection
+ * of this operation
+ */
+
+ ber_scanf( ber, "m", &cookie );
+ if ( cookie.bv_len != sizeof(Connection *) ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+ }
+
+ /* DN, authtag */
+ tag = ber_scanf( ber, "mt", &bdn, &authtag );
+ if ( tag == LBER_ERROR ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ rc = dnNormalize( 0, NULL, NULL, &bdn, &ndn, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ switch ( authtag ) {
+ case LDAP_AUTH_SIMPLE:
+ /* cookie only makes sense for SASL bind (so far) */
+ if ( !BER_BVISNULL( &cookie ) ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ tag = ber_scanf( ber, "m", &cred );
+ if ( tag == LBER_ERROR ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+ break;
+
+ case LDAP_AUTH_SASL:
+ tag = ber_scanf( ber, "{m" /*}*/ , &mechanism );
+ if ( tag == LBER_ERROR ||
+ BER_BVISNULL( &mechanism ) || BER_BVISEMPTY( &mechanism ) )
+ {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LBER_OCTETSTRING ) {
+ ber_scanf( ber, "m", &cred );
+ }
+
+ tag = ber_scanf( ber, /*{*/ "}" );
+ break;
+
+ default:
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ if ( !BER_BVISNULL( &cookie ) ) {
+ vc_conn_t tmp = { 0 };
+
+ AC_MEMCPY( (char *)&tmp.conn, (const char *)cookie.bv_val, cookie.bv_len );
+ ldap_pvt_thread_mutex_lock( &vc_mutex );
+ conn = (vc_conn_t *)ldap_avl_find( vc_tree, (caddr_t)&tmp, vc_conn_cmp );
+ if ( conn == NULL || ( conn != NULL && conn->refcnt != 0 ) ) {
+ conn = NULL;
+ ldap_pvt_thread_mutex_unlock( &vc_mutex );
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+ conn->refcnt++;
+ operation_counter_init( conn->op, op->o_threadctx );
+ ldap_pvt_thread_mutex_unlock( &vc_mutex );
+
+ } else {
+ conn = (vc_conn_t *)SLAP_CALLOC( 1, sizeof( vc_conn_t ) );
+ conn->refcnt = 1;
+
+ connection_fake_init2( &conn->connbuf, &conn->opbuf, op->o_threadctx, 0 );
+ conn->op = &conn->opbuf.ob_op;
+ snprintf( conn->op->o_log_prefix, sizeof( conn->op->o_log_prefix ),
+ "%s VERIFYCREDENTIALS", op->o_log_prefix );
+ }
+
+ conn->op->o_tag = LDAP_REQ_BIND;
+ memset( &conn->op->oq_bind, 0, sizeof( conn->op->oq_bind ) );
+ conn->op->o_req_dn = ndn;
+ conn->op->o_req_ndn = ndn;
+ conn->op->o_protocol = LDAP_VERSION3;
+ conn->op->orb_method = authtag;
+ conn->op->o_callback = &sc;
+
+ /* TODO: controls */
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS ) {
+ conn->op->o_ber = ber;
+ rc = get_ctrls2( conn->op, &rs2, 0, LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS );
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+ }
+
+ tag = ber_skip_tag( ber, &len );
+ if ( len || tag != LBER_DEFAULT ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ switch ( authtag ) {
+ case LDAP_AUTH_SIMPLE:
+ break;
+
+ case LDAP_AUTH_SASL:
+ conn->op->orb_mech = mechanism;
+ break;
+ }
+
+ conn->op->orb_cred = cred;
+ sc.sc_response = vc_cb;
+ sc.sc_private = &vc;
+
+ conn->op->o_bd = frontendDB;
+ rs->sr_err = frontendDB->be_bind( conn->op, &rs2 );
+
+ if ( conn->op->o_conn->c_sasl_bind_in_progress ) {
+ rc = vc_create_response( conn, rs2.sr_err, rs2.sr_text,
+ !BER_BVISEMPTY( &vc.sasldata ) ? &vc.sasldata : NULL,
+ NULL,
+ vc.ctrls, &rs->sr_rspdata );
+
+ } else {
+ rc = vc_create_response( NULL, rs2.sr_err, rs2.sr_text,
+ NULL,
+ &conn->op->o_conn->c_dn,
+ vc.ctrls, &rs->sr_rspdata );
+ }
+
+ if ( rc != 0 ) {
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+
+ if ( !BER_BVISNULL( &conn->op->o_conn->c_dn ) &&
+ conn->op->o_conn->c_dn.bv_val != conn->op->o_conn->c_ndn.bv_val )
+ ber_memfree( conn->op->o_conn->c_dn.bv_val );
+ if ( !BER_BVISNULL( &conn->op->o_conn->c_ndn ) )
+ ber_memfree( conn->op->o_conn->c_ndn.bv_val );
+
+done:;
+ if ( conn ) {
+ if ( conn->op->o_conn->c_sasl_bind_in_progress ) {
+ if ( conn->conn == NULL ) {
+ conn->conn = conn;
+ conn->refcnt--;
+ ldap_pvt_thread_mutex_lock( &vc_mutex );
+ rc = ldap_avl_insert( &vc_tree, (caddr_t)conn,
+ vc_conn_cmp, vc_conn_dup );
+ ldap_pvt_thread_mutex_unlock( &vc_mutex );
+ assert( rc == 0 );
+
+ } else {
+ ldap_pvt_thread_mutex_lock( &vc_mutex );
+ conn->refcnt--;
+ ldap_pvt_thread_mutex_unlock( &vc_mutex );
+ }
+
+ } else {
+ if ( conn->conn != NULL ) {
+ vc_conn_t *tmp;
+
+ ldap_pvt_thread_mutex_lock( &vc_mutex );
+ tmp = ldap_avl_delete( &vc_tree, (caddr_t)conn, vc_conn_cmp );
+ ldap_pvt_thread_mutex_unlock( &vc_mutex );
+ }
+ SLAP_FREE( conn );
+ }
+ }
+
+ if ( vc.ctrls ) {
+ ldap_controls_free( vc.ctrls );
+ vc.ctrls = NULL;
+ }
+
+ if ( !BER_BVISNULL( &ndn ) ) {
+ op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &ndn );
+ }
+
+ op->o_tmpfree( reqdata.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &reqdata );
+
+ return rs->sr_err;
+}
+
+static int
+vc_initialize( void )
+{
+ int rc;
+
+ rc = load_extop2( (struct berval *)&vc_exop_oid_bv,
+ SLAP_EXOP_HIDE, vc_exop, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "vc_initialize: unable to register VerifyCredentials exop: %d.\n",
+ rc );
+ }
+
+ ldap_pvt_thread_mutex_init( &vc_mutex );
+
+ return rc;
+}
+
+int
+init_module( int argc, char *argv[] )
+{
+ return vc_initialize();
+}
+
diff --git a/contrib/slapd-tools/README b/contrib/slapd-tools/README
new file mode 100644
index 0000000..9098a20
--- /dev/null
+++ b/contrib/slapd-tools/README
@@ -0,0 +1,23 @@
+Directory contents:
+
+statslog
+ Program to output selected parts of slapd's statslog output
+ (LDAP request/response log), grouping log lines by LDAP
+ connection. Useful to search and inspect the server log.
+
+wrap_slap_ops
+ Update source code to use the wrapper macros SLAP_OP() & co
+ for LDAP-operation backend calls. They can help debug the
+ SlapReply. They compile like the old code by default.
+
+---
+Copyright 2004-2022 The OpenLDAP Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
diff --git a/contrib/slapd-tools/statslog b/contrib/slapd-tools/statslog
new file mode 100755
index 0000000..840bd4e
--- /dev/null
+++ b/contrib/slapd-tools/statslog
@@ -0,0 +1,171 @@
+#!/usr/bin/perl -w
+# statslog - Rearrange and output selected parts of slapd's statslog output.
+# $OpenLDAP$
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 1998-2022 The OpenLDAP Foundation.
+# Portions Copyright 2004 Hallvard B. Furuseth.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted only as authorized by the OpenLDAP
+# Public License.
+#
+# A copy of this license is available in the file LICENSE in the
+# top-level directory of the distribution or, alternatively, at
+# <http://www.OpenLDAP.org/license.html>.
+
+sub usage {
+ die join("", @_, <<'EOM');
+Usage: statslog [options] [logfiles; may be .gz or .bz2 files]
+
+ Output selected parts of slapd's statslog output (LDAP request/response
+ log to syslog or stderr; loglevel 256), grouping log lines by LDAP
+ connection. Lines with no connection are excluded by default.
+
+Options:
+ --brief -b Brief output (omit time, host/process name/ID).
+ --exclude=RE -e RE Exclude connections whose output matches REgexp.
+ --include=RE -i RE Only include connections matching REgexp.
+ --EXCLUDE=RE -E RE Case-sensitive '--exclude'.
+ --INCLUDE=RE -I RE Case-sensitive '--include'.
+ --loose -l Include "loose" lines (lines with no connection).
+ --no-loose -L RE Only exclude the "loose" lines that match RE.
+ --join -j Join the inputs as if they were one big log file.
+ Each file must start where the previous left off.
+ --no-join -J Do not --join. (Can be useful with --sort.)
+ --sort -s Sort input files by age. Implies --join.
+ --trace -t Print file names when read. Implies --no-join.
+All --exclude/include options are applied. Note: --exclude/include are
+unreliable without --join/sort for connections spanning several log files.
+EOM
+}
+
+########################################################################
+
+use bytes;
+use strict;
+use Getopt::Long;
+
+# Globals
+my %conns; # Hash (connection number -> output)
+my @loose; # Collected output with no connection number
+
+# Command line options
+my($brief, @filters, @conditions, $no_loose);
+my($join_files, $sort_files, $trace, $getopt_ok);
+
+# Handle --include/INCLUDE/exclude/EXCLUDE options
+sub filter_opt {
+ my($opt, $regexp) = @_;
+ push(@conditions, sprintf('$lines %s /$filters[%d]/om%s',
+ (lc($opt) eq 'include' ? "=~" : "!~"),
+ scalar(@filters),
+ ($opt eq lc($opt) ? "i" : "")));
+ push(@filters, $regexp);
+}
+
+# Parse options at compile time so some can become constants to optimize away
+BEGIN {
+ &Getopt::Long::Configure(qw(bundling no_ignore_case));
+ $getopt_ok = GetOptions("brief|b" => \$brief,
+ "include|i=s" => \&filter_opt,
+ "exclude|e=s" => \&filter_opt,
+ "INCLUDE|I=s" => \&filter_opt,
+ "EXCLUDE|E=s" => \&filter_opt,
+ "join|j" => \$join_files,
+ "no-join|J" => sub { $join_files = 0; },
+ "sort|s" => \$sort_files,
+ "loose|l" => sub { $no_loose = ".^"; },
+ "no-loose|L=s" => \$no_loose,
+ "trace|t" => \$trace);
+}
+usage() unless $getopt_ok;
+usage("--trace is incompatible with --join.\n") if $trace && $join_files;
+
+$join_files = 1 if !defined($join_files) && $sort_files && !$trace;
+use constant BRIEF => !!$brief;
+use constant LOOSE => defined($no_loose) && ($no_loose eq ".^" ? 2 : 1);
+
+# Build sub out(header, connection number) to output one connection's data
+my $out_body = (LOOSE
+ ? ' if (@loose) { print "\n", @loose; @loose = (); } '
+ : '');
+$out_body .= ' print "\n", $_[0], $lines; ';
+$out_body = " if (" . join("\n && ", @conditions) . ") {\n$out_body\n}"
+ if @conditions;
+eval <<EOM;
+sub out {
+ my \$lines = delete(\$conns{\$_[1]});
+ $out_body
+}
+1;
+EOM
+die $@ if $@;
+
+# Read and output log lines from one file
+sub do_file {
+ local(@ARGV) = @_;
+ my($conn, $line, $act);
+ while (<>) {
+ if (BRIEF
+ ? (($conn, $line, $act) = /\bconn=(\d+) (\S+ (\S+).*\n)/)
+ : (($conn, $act) = /\bconn=(\d+) \S+ (\S+)/ )) {
+ $conns{$conn} .= (BRIEF ? $line : $_);
+ out("", $conn) if $act eq 'closed';
+ } elsif (LOOSE && (LOOSE > 1 || !/$no_loose/omi)) {
+ s/^\w{3} [ \d]+:\d\d:\d\d [^:]*: // if BRIEF;
+ push(@loose, $_);
+ }
+ }
+ final() unless $join_files;
+}
+
+# Output log lines for unfinished connections
+sub final {
+ if (%conns) {
+ for my $conn (sort keys %conns) {
+ out("UNFINISHED:\n", $conn);
+ }
+ die if %conns;
+ }
+ if (LOOSE && @loose) { print "\n", @loose; @loose = (); }
+}
+
+# Main program
+if (!@ARGV) {
+ # Read from stdin
+ do_file();
+} else {
+ if ($sort_files && @ARGV > 1) {
+ # Sort files by last modified time; oldest first
+ my @fileinfo;
+ for my $file (@ARGV) {
+ my $age = -M $file;
+ if (defined($age)) {
+ push(@fileinfo, [$age, $file]);
+ } else {
+ print STDERR "File not found: $file\n";
+ }
+ }
+ exit(1) unless @fileinfo;
+ @ARGV = map { $_->[1] } sort { $b->[0] <=> $a->[0] } @fileinfo;
+ }
+
+ # Prepare to pipe .gz, .bz2 and .bz files through gunzip or bunzip2
+ my %type2prog = ("gz" => "gunzip", "bz2" => "bunzip2", "bz" => "bunzip2");
+ for (@ARGV) {
+ if (/\.(gz|bz2?)$/) {
+ my $type = $1;
+ die "Bad filename: $_\n" if /^[+-]|[^\w\/.,:%=+-]|^$/;
+ $_ = "$type2prog{$type} -c $_ |";
+ }
+ }
+
+ # Process the files
+ for my $file (@ARGV) {
+ print "\n$file:\n" if $trace;
+ do_file($file);
+ }
+}
+final();
diff --git a/contrib/slapd-tools/wrap_slap_ops b/contrib/slapd-tools/wrap_slap_ops
new file mode 100755
index 0000000..16b0461
--- /dev/null
+++ b/contrib/slapd-tools/wrap_slap_ops
@@ -0,0 +1,162 @@
+#!/usr/bin/perl -wn0777
+# wrap_slap_ops - Help update code to use SLAP_OP() & co.
+#
+# This work is part of OpenLDAP Software <http://www.openldap.org/>.
+#
+# Copyright 2011-2022 The OpenLDAP Foundation.
+# Portions Copyright 2011-2013 Hallvard B. Furuseth.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted only as authorized by the OpenLDAP
+# Public License.
+#
+# A copy of this license is available in the file LICENSE in the
+# top-level directory of the distribution or, alternatively, at
+# <http://www.OpenLDAP.org/license.html>.
+
+use strict;
+
+sub usage() {
+ warn "Usage: $0 {-l | -u | -U<num>} {file | dir}...
+
+Update slapd source code to wrap LDAP operation calls in the debug
+macros SLAP_OP() & co. They compile like the old code by default.
+Define USE_RS_ASSERT to enable asserts which verify the SlapReply.
+See servers/slapd/result.c.
+
+Options:
+ -u, -U<n> Output unidiffs with n lines of context (-u = default for diff).
+ -l List files which would change. Show remaining cases on stderr.\n";
+ exit(1);
+}
+
+#### File/option handling. Skips symlinks, handles filenames =~ /\.[ch]+p*$/i.
+
+sub ls_R {
+ map { -l $_ ? () : -d _ ? ls_R(<$_/*>) : /\.[ch]+p*$/i ? $_ : () } @_;
+}
+
+use constant Mode => shift(@ARGV) || "";
+use vars qw($ccnt $rcnt);
+INIT {
+ usage() unless Mode =~ /^-(l|[uU]\d*)$/ && ($ARGV[0]||"") =~ /^[^\-]/;
+ exit(0) unless @ARGV = ls_R(@ARGV); # Expand ARGV, exit if no files
+ $| = 1;
+ $ccnt = $rcnt = 0;
+}
+
+sub file_result( $$ ) {
+ my($contents, $changed) = @_;
+ $ccnt++ if $changed;
+ $rcnt += scalar( my @rest = remaining($contents) );
+ if (Mode eq "-l") {
+ print "$ARGV\n" if $changed;
+ print STDERR "$ARGV:\t$_\n" foreach @rest;
+ } elsif ($changed) {
+ (my $file = "$ARGV") =~ s%^-%./-%;
+ print "Index: $file\n";
+ (open(D, "|-", "diff", Mode, $file, "-")
+ && (print D $contents)
+ && (close(D) || $! == 0)) or die "$0: diff failed: $!\n";
+ }
+}
+
+END {
+ print STDERR <<EOMSG if defined $ccnt;
+$ccnt files to change. $rcnt suspicious lines remain. (Expect three in slapd).
+EOMSG
+}
+
+#### Edit the contents of a file
+
+use vars qw($obj_re %addr %func2op $func_re $todo_re);
+INIT {
+ $obj_re = qr/(?:\w+ (?:\s* (?:->|\.) \s* \w+)*?)/x;
+ %addr = ("." => "&", "->" => ""); # x.y => (&x)->y, x->y => x->y
+ %func2op = map { /(\w+) \s+ (?= .*?=>\s* (\w+))/gx } <DATA>;
+ $func_re = '\b(?=b[ei]_)(?:' . join("|", keys %func2op) . ')\b';
+ my %both = (%func2op, reverse %func2op);
+ $todo_re = '\b(?=[bo][eip]_)(?:' . join("|", keys %both) . ')\b';
+}
+next if !/$todo_re/;
+my $orig = "$_";
+
+# x->func(op, rs) ==> slap_bi_op( x, <enum op_func>, op, rs)
+# x. func(op, rs) ==> slap_bi_op(&x, <enum op_func>, op, rs)
+s%( # 1: entire match: "<delim><function>("
+ ((?: [\)!=\;{}\\] | \*/ | \b if\s*\( | \b return \b ) \s*) # 2: delim
+ (\(\s* (?:\* \s*)?)? # 3: optional "(*" or "(" in (*f)()
+ ($obj_re) \s* (->|\.) \s* # 4: object, 5: "->" or "."
+ (?=(b[ie]_))($func_re) \s* # 6: "bi_" or "be_", 7: function
+ (\)\s*)? # 8: optional ")" in (*f),
+ (\(\s*) # 9: "(" + whitespace
+)% (!$3) == (!$8) ? "$2slap_$6op$9$addr{$5}$4, $func2op{$7}, " : $1 %egox;
+
+# (&x->bi_op_bind)[which](op, rs) ==> slap_bi_op(x, which, op, rs)
+# (&x->be_bind)[which](op, rs) ==> slap_be_op(x, which, op, rs)
+s/\(&(\w+(?:(?:->|\.)\w+)*)->b(?=([ei]))(?:e|i_op)_bind\)\[\s* (\w+) \s*\] \((\s*) ([^()]*)\)
+ /slap_b$2_op($4$1, $3, $5)/gox;
+
+# slap_bi_op(x->bd_info, which, op, rs) ==> slap_be_op( x, which, op, rs)
+# slap_bi_op(x. bd_info, which, op, rs) ==> slap_be_op(&x, which, op, rs)
+s/\b slap_bi_op (\(\s*) ($obj_re) \s* (->|\.) \s* bd_info \s*,
+ /slap_be_op$1$addr{$3}$2,/gox;
+
+# slap_be_op(op->o_bd, which, &op, rs) ==> SLAP_OP(which, op, rs)
+# slap_be_op(op. o_bd, which, &op, rs) ==> SLAP_OP(which, &op, rs)
+s/\b(slap_be_op (\(\s*) ($obj_re) \s*(->|\.)\s* o_bd, \s (\w+, \s (&?)\3,))
+ / $addr{$4} eq $6 ? "SLAP_OP$2$5" : die "$ARGV: Bad syntax: $1\n" /egox;
+
+my $changed = $_ ne $orig;
+
+# When changing a file, do some whitespace cleanup too
+if ($changed) {
+ s/\b ((SLAP_OP|slap_b[ei](func)?_op) \b .*?) [\ \t]+$ /$1/gmx;
+ s/\A\s*\n//;
+ s/\s*\z/\n/;
+}
+
+file_result($_, $changed);
+
+####
+
+# Return remaining lines that contain operation method names
+sub remaining {
+ my($contents) = @_;
+ return $contents !~ /$func_re/o ? () : grep {
+ !/^\# [ \t]* define \s+ ($func_re|slap_bi_op\b) /x &&
+ # Skip "if ( (&bi->bi_op_bind)[ which ] )" and variants
+ !/^(\} \s* else \s*)? if \s* \( \s*
+ \(& (\w+ | \(\s*\w+\s*=\s*$obj_re\s*\)) -> bi_op_bind\)
+ \s* \[ \s* \w+ \s* \]
+ \s* [&|\)]/ox;
+ } $contents =~ m% ^[\ \t]* (?=\S) (
+ # The line contains a member opfunction
+ .*? (?:->|\.) \s* $func_re
+
+ # Skip if the member function is assigned, compared,
+ # 'and/or'ed, followed by a word (this is a comment), or by
+ # ') {' or ') word' (function is the boolean in an if/while).
+ (?! \s* (?: [!=&|\w] | \)\s*[\{\w] ))
+
+ .*?
+ ) \s*?$ %gmox;
+}
+
+# %func2op: Member functions => slap_operation_t
+__DATA__
+be_bind bi_op_bind => op_bind
+be_unbind bi_op_unbind => op_unbind
+be_search bi_op_search => op_search
+be_compare bi_op_compare => op_compare
+be_modify bi_op_modify => op_modify
+be_modrdn bi_op_modrdn => op_modrdn
+be_add bi_op_add => op_add
+be_delete bi_op_delete => op_delete
+be_abandon bi_op_abandon => op_abandon
+be_extended bi_extended => op_extended
+be_cancel bi_op_cancel => op_cancel
+be_operational bi_operational => op_aux_operational
+be_chk_referrals bi_chk_referrals => op_aux_chk_referrals
+be_chk_controls bi_chk_controls => op_aux_chk_controls
diff --git a/contrib/slapi-plugins/addrdnvalues/README b/contrib/slapi-plugins/addrdnvalues/README
new file mode 100644
index 0000000..da699c8
--- /dev/null
+++ b/contrib/slapi-plugins/addrdnvalues/README
@@ -0,0 +1,29 @@
+This directory contains a SLAPI plugin, addrdnvalues, which will add to
+an entry any attribute values that appear in the entry's RDN but not in
+the entry. This is necessary for compliance with some "broken" clients.
+
+To use the plugin, add:
+
+plugin preoperation libaddrdnvalues-plugin.so addrdnvalues_preop_init
+
+to your slapd configuration file.
+
+No Makefile is provided. Use a command line similar to:
+
+gcc -shared -I../../../include -Wall -g -o libaddrdnvalues-plugin.so addrdnvalues.c
+
+to compile this plugin.
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2003-2022 The OpenLDAP Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
diff --git a/contrib/slapi-plugins/addrdnvalues/addrdnvalues.c b/contrib/slapi-plugins/addrdnvalues/addrdnvalues.c
new file mode 100644
index 0000000..8dd305e
--- /dev/null
+++ b/contrib/slapi-plugins/addrdnvalues/addrdnvalues.c
@@ -0,0 +1,75 @@
+/* addrdnvalues.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Copyright 2003-2004 PADL Software Pty Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Luke Howard of PADL Software
+ * for inclusion in OpenLDAP Software.
+ */
+
+#include <string.h>
+#include <unistd.h>
+
+#include <ldap.h>
+#include <lber.h>
+
+#include <slapi-plugin.h>
+
+int addrdnvalues_preop_init(Slapi_PBlock *pb);
+
+static Slapi_PluginDesc pluginDescription = {
+ "addrdnvalues-plugin",
+ "PADL",
+ "1.0",
+ "RDN values addition plugin"
+};
+
+static int addrdnvalues_preop_add(Slapi_PBlock *pb)
+{
+ int rc;
+ Slapi_Entry *e;
+
+ if (slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &e) != 0) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, "addrdnvalues_preop_add",
+ "Error retrieving target entry\n");
+ return -1;
+ }
+
+ rc = slapi_entry_add_rdn_values(e);
+ if (rc != LDAP_SUCCESS) {
+ slapi_send_ldap_result(pb, LDAP_OTHER, NULL,
+ "Failed to parse distinguished name", 0, NULL);
+ slapi_log_error(SLAPI_LOG_PLUGIN, "addrdnvalues_preop_add",
+ "Failed to parse distinguished name: %s\n",
+ ldap_err2string(rc));
+ return -1;
+ }
+
+ return 0;
+}
+
+int addrdnvalues_preop_init(Slapi_PBlock *pb)
+{
+ if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_03) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, &pluginDescription) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN, (void *)addrdnvalues_preop_add) != 0) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, "addrdnvalues_preop_init",
+ "Error registering %s\n", pluginDescription.spd_description);
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/doc/Makefile.in b/doc/Makefile.in
new file mode 100644
index 0000000..8860f64
--- /dev/null
+++ b/doc/Makefile.in
@@ -0,0 +1,16 @@
+## doc Makefile.in for OpenLDAP
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SUBDIRS= man
diff --git a/doc/devel/OIDs b/doc/devel/OIDs
new file mode 100644
index 0000000..af2bf88
--- /dev/null
+++ b/doc/devel/OIDs
@@ -0,0 +1,119 @@
+OpenLDAProot 1.3.6.1.4.1.4203
+
+OpenLDAP OpenLDAProot:1
+
+OpenLDAPsyntax OpenLDAP:1
+ authPasswordSyntax OpenLDAPsyntax:2
+
+OpenLDAPmatchingrule OpenLDAP:2
+ authPasswordExactMatch OpenLDAPmatchingrule:2
+ authPasswordMatch OpenLDAPmatchingrule:3
+
+OpenLDAPattributeType OpenLDAP:3
+ supportedAuthPasswordSchemas OpenLDAPattributeType:3
+ authPassword OpenLDAPattributeType:4
+ supportedFeatures OpenLDAPattributeType:5
+
+OpenLDAPobjectClass OpenLDAP:4
+ OpenLDAPorg OpenLDAPObjectClass:3
+ OpenLDAPou OpenLDAPObjectClass:4
+ OpenLDAPperson OpenLDAPObjectClass:5
+ OpenLDAPdisplayableObject OpenLDAPObjectClass:6
+ authPasswordObject OpenLDAPobjectClass:7
+
+OpenLDAPfeatures OpenLDAP:5
+ allOperationalAttrs OpenLDAPfeatures:1
+ OC AD lists OpenLDAPfeatures:2
+ TrueFalseFilters OpenLDAPfeatures:3
+ languageTagOptions OpenLDAPfeatures:4
+ languageRangetags OpenLDAPfeatures:5
+
+Syncrepl OpenLDAP:9
+
+OpenLDAPcontrol OpenLDAP:10
+ SubentriesControl OpenLDAPcontrol:1
+
+OpenLDAPexop OpenLDAP:11
+ passwordModify OpenLDAPexop:1
+ whoAmI OpenLDAPexop:3
+
+OpenLDAPinternal OpenLDAP:12
+ OpenLDAPtesting OpenLDAPinternal:1
+ OpenLDAPconfig OpenLDAPinternal:2
+
+
+OpenLDAPexperimental OpenLDAProot:666
+
+ExperimentalAttr OpenLDAPexperimental:1
+ OpenLDAPaci ExperimentalAttr:5
+ entryCSN ExperimentalAttr:7
+ authzTo ExperimentalAttr:8
+ authzFrom ExperimentalAttr:9
+ monitorContext ExperimentalAttr:10
+ superiorUUID ExperimentalAttr:11 check - is this dup of parentUUID?
+ namingCSN ExperimentalAttr:13
+ syncreplCookie ExperimentalAttr:23
+ contextCSN ExperimentalAttr:25
+ syncTimestamp ExperimentalAttr:26
+ lastmodDN ExperimentalAttr:28 (contrib/slapd-modules/lastmod)
+ lastmodType ExperimentalAttr:29
+ lastmodEnabled ExperimentalAttr:30
+ monitorAttrs ExperimentalAttr:55 (back-monitor)
+ entryExpireTimestamp ExperimentalAttr:57 (slapo-dds)
+ rdnValue ExperimentalAttr:58 (contrib/slapd-modules/samba4)
+ parentUUID ExperimentalAttr:59 (...samba4)
+ x509PrivateKey ExperimentalAttr:60
+
+
+ExperimentalSyntax OpenLDAPexperimental:2
+ ACIsyntax ExperimentalSyntax:1
+ authPassword ExperimentalSyntax:2 check - this was promoted to RFC3112
+ authz ExperimentalSyntax:7
+ privateKey ExperimentalSyntax:13
+
+ExperimentalObjectClass OpenLDAPexperimental:3
+ glue ExperimentalObjectClass:4
+ syncConsumerSubentry ExperimentalObjectClass:5
+ syncProviderSubentry ExperimentalObjectClass:6
+ lastmod ExperimentalObjectClass:13
+ monitorClasses ExperimentalObjectClass:16
+
+ExperimentalMatchingRule OpenLDAPexperimental:4
+ authPaswordMatch ExperimentalMatchingRule:1 check - this was promoted to RFC3112
+ ACImatch ExperimentalMatchingRule:2
+ direectoryStringApproxMatch ExperimentalMatchingRule:4
+ IA5stringApproxMatch ExperimentalMatchingRule:5
+ dnOneLevelMatch ExperimentalMatchingRule:8
+ dnSubtreeMatch ExperimentalMatchingRule:9
+ dnSubordinateMatch ExperimentalMatchingRule:10
+ dnSuperiorMatch ExperimentalMatchingRule:11
+ authzMatch ExperimentalMatchingRule:12
+ privateKeyMatch ExperimentalMatchingRule:13
+
+ExperimentalControl OpenLDAPexperimental:5
+ noop ExperimentalControl:2
+ noSubordinates ExperimentalControl:11
+ relax ExperimentalControl:12
+ slurp ExperimentalControl:13
+ valsort ExperimentalControl:14
+ deref ExperimentalControl:16
+ whatfailed ExperimentalControl:17
+ noopsrch ExperimentalControl:18
+
+ExperimentalExop OpenLDAPexperimental:6
+ verifyCredentials ExperimentalExop:5
+
+ExperimentalFeatures OpenLDAPexperimental:8
+ subordinateScope ExperimentalFeatures:1
+
+SelfContainedWorks OpenLDAPexperimental:11
+ CSNs SelfContainedWorks:2
+ chaining SelfContainedWorks:3
+ retcode SelfContainedWorks:4
+ accesslog SelfContainedWorks:5
+ distProc SelfContainedWorks:6
+ LDAP txns SelfContainedWorks:7 (replaced by 1.3.6.1.1.21 RFC 5805)
+ dyngroup SelfContainedWorks:8
+ proxyCache SelfContainedWorks:9
+ X509 PMI SelfContainedWorks:10
+ autoca SelfContainedWorks:11
diff --git a/doc/devel/README b/doc/devel/README
new file mode 100644
index 0000000..3a0cb3d
--- /dev/null
+++ b/doc/devel/README
@@ -0,0 +1,9 @@
+The OpenLDAP Developer's FAQ is available at:
+ http://www.openldap.org/faq/index.cgi?file=4
+
+Additional developer pages are at:
+ http://www.openldap.org/devel/
+
+
+---
+$OpenLDAP$
diff --git a/doc/devel/args b/doc/devel/args
new file mode 100644
index 0000000..c5aa02f
--- /dev/null
+++ b/doc/devel/args
@@ -0,0 +1,64 @@
+Tools ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+ldapcompare * DE**HI** MNOPQR UVWXYZ de *h*** *nop* vwxyz
+ldapdelete *CDE**HI** MNOPQR UVWXYZ cdef*h*** *nop* vwxyz
+ldapexop * D **HI** NO QR UVWXYZ de *h*** *nop vwxy
+ldapmodify *CDE**HI** MNOPQRS UVWXYZabcde *h*** *nop*r t vwxy
+ldapmodrdn *CDE**HI** MNOPQR UVWXYZ cdef*h*** *nop*rs vwxy
+ldappasswd A*CDE**HI** NO QRS UVWXYZa def*h*** * o * s vwxy
+ldapsearch A*CDE**HI**LMNOPQRSTUVWXYZab def*h***l*nop* stuvwxyz
+ldapurl * E**H ** S ab f*h*** * p* s
+ldapvc * DE**HI** NO QRS UVWXYZa cdef*h*** *nop* vwxy
+ldapwhoami * DE**HI** NO QR UVWXYZ def*h*** *nop* vwxy
+
+
+* reserved
+ BFGJgijmq01235789
+
+* General flags:
+ -C Chase Referrals
+ -D Bind DN
+ -E Tool-specific Extensions (e.g., -E <[!]oid[=options]>*)
+ -e General Extensions (e.g., -e <[!]oid[=options]>*)
+ -f file
+ -H URI
+ -P protocol version
+ -V version information
+ -W prompt for bind password
+ -d debug
+ -h host
+ -n no-op
+ -N no (SASLprep) normalization of simple bind password
+ -o general libldap options (plus ldif_wrap and nettimeout for backwards comp.)
+ -p port
+ -v verbose
+ -V version
+ -x simple bind
+ -y Bind password-file
+ -w Bind password
+
+Not used
+ -4 IPv4 only
+ -6 IPv6 only
+
+
+* LDAPv3 Only
+ -M ManageDSAIT
+ -Z StartTLS
+
+ -Y SASL Mechanism (defaults to "best")
+ -R SASL Realm (defaults to empty)
+ -O SASL Security Options (defaults to "noanonymous,noplain")
+ -U SASL Authentication Identity (defaults to USER)
+ -X SASL Authorization Identity (defaults to empty)
+
+ -I SASL interactive mode (default: automatic)
+ -Q SASL quiet mode (default: automatic)
+
+
+* LDAPv2+ Only (REMOVED)
+ -K LDAPv2 Kerberos Bind (Step 1 only)
+ -k LDAPv2 Kerberos Bind
+
+
+---
+$OpenLDAP$
diff --git a/doc/devel/lloadd/design.md b/doc/devel/lloadd/design.md
new file mode 100644
index 0000000..62fcd88
--- /dev/null
+++ b/doc/devel/lloadd/design.md
@@ -0,0 +1,282 @@
+TODO:
+- [ ] keep a global op in-flight counter? (might need locking)
+- [-] scheduling (who does what, more than one select thread? How does the proxy
+ work get distributed between threads?)
+- [ ] managing timeouts?
+- [X] outline locking policy: seems like there might be a lock inversion in the
+ design looming: when working with op, might need a lock on both client and
+ upstream but depending on where we started, we might want to start with
+ locking one, then other
+- [ ] how to deal with the balancer running out of fds? Especially when we hit
+ the limit, then lose an upstream connection and accept() a client, we
+ wouldn't be able to initiate a new one. A bit of a DoS... But probably not
+ a concern for Ericsson
+- [ ] non-Linux? No idea how anything other than poll works (moot if building a
+ libevent/libuv-based load balancer since they take care of that, except
+ edge-triggered I/O?)
+- [-] rootDSE? Controls and exops might have different semantics and need
+ binding to the same upstream connection.
+- [ ] Just piggybacking on OpenLDAP as a module? Would still need some updates
+ in the core and the module/subsystem would be a very invasive one. On the
+ other hand, allows to expose live configuration and monitoring over LDAP
+ over the current slapd listeners without re-inventing the wheel.
+
+
+Expecting to handle only LDAPv3
+
+terms:
+ server - configured target
+ upstream - a single connection to a server
+ client - an incoming connection
+
+To maintain fairness `G( requested => ( F( progressed | failed ) ) )`, use
+queues and put timeouts in
+
+Runtime organisation
+------
+- main thread with its own event base handling signals
+- one thread (later possibly more) listening on the rendezvous sockets, handing
+ the new sockets to worker threads
+- n worker threads dealing with client and server I/O (dispatching actual work
+ to the thread pool most likely)
+- a thread pool to handle actual work
+
+Operational behaviour
+------
+
+- client read -> upstream write:
+ - client read:
+ - if TLS_SETUP, keep processing, set state back when finished and note that
+ we're under TLS
+ - ber_get_next(), if we don't have a tag, finished (unless we have true
+ edge-triggered I/O, also put the fd back into the ones we're waiting for)
+ - peek at op tag:
+ - unbind:
+ - with a single lock, mark all pending ops in upstreams abandoned, clear
+ client link (would it be fast enough if we remove them from upstream
+ map instead?)
+ - locked per op:
+ - remove op from upstream map
+ - check upstream is not write-suspended, if it is ...
+ - try to write the abandon op to upstream, suspend upstream if not
+ fully sent
+ - remove op from client map (how if we're in avl_apply?, another pass?)
+ - would be nice if we could wipe the complete client map then, otherwise
+ we need to queue it to have it freed when all abandons get passed onto
+ the upstream (just dropping them might put extra strain on upstreams,
+ will probably have a queue on each client/upstream anyway, not just a
+ single Ber)
+ - bind:
+ - check mechanism is not EXTERNAL (or implement it)
+ - abandon existing ops (see unbind)
+ - set state to BINDING, put DN into authzid
+ - pick upstream, create PDU and sent
+ - abandon:
+ - find op, mark for abandon, send to appropriate upstream
+ - Exop:
+ - check not BINDING (unless it's a cancel?)
+ - check OID:
+ - STARTTLS:
+ - check we don't have TLS yet
+ - abandon all
+ - set state to TLS_SETUP
+ - send the hello
+ - VC(?):
+ - similar to bind except for the abandons/state change
+ - other:
+ - check not BINDING
+ - pick an upstream
+ - create a PDU, send (marking upstream suspended if not written in full)
+ - check if should read again (keep a counter of number of times to read
+ off a connection in a single pass so that we maintain fairness)
+ - if read enough requests and can still read, re-queue ourselves (if we
+ don't have true edge-triggered I/O, we can just register the fd again)
+ - upstream write (only when suspended):
+ - flush the current BER
+ - there shouldn't be anything else?
+- upstream read -> client write:
+ - upstream read:
+ - ber_get_next(), if we don't have a tag, finished (unless we have true
+ edge-triggered I/O, also put the fd back into the ones we're waiting for)
+ - when we get it, peek at msgid, resolve client connection, lock, check:
+ - if unsolicited, handle as close (and mark connection closing)
+ - if op is abandoned or does not exist, drop PDU and op, update counters
+ - if client backlogged, suspend upstream, register callback to unsuspend
+ (on progress when writing to client or abandon from client (connection
+ death, abandon proper, ...))
+ - reconstruct final PDU, write BER to client, if did not write fully,
+ suspend client
+ - if a final response, decrement operation counts on upstream and client
+ - check if should read again (keep a counter of number of responses to read
+ off a connection in a single pass so that we don't starve any?)
+ - client write ready (only checked for when suspended):
+ - write the rest of pending BER if any
+ - on successful write, pick all pending ops that need failure response, push
+ to client (are there any controls that need to be present in response even
+ in the case of failure?, what to do with them?)
+ - on successfully flushing them, walk through suspended upstreams, picking
+ the pending PDU (unsuspending the upstream) and writing, if PDU flushed
+ successfully, pick next upstream
+ - if we successfully flushed all suspended upstreams, unsuspend client
+ (and disable the write callback)
+- upstream close/error:
+ - look up pending ops, try to write to clients, mark clients suspended that
+ have ops that need responses (another queue associated with client to speed
+ up?)
+ - schedule a new connection open
+- client close/error:
+ - same as unbind
+- client inactive (no pending ops and nothing happened in x seconds)
+ - might just send notice of disconnection and close
+- op timeout handling:
+ - mark for abandon
+ - send abandon
+ - send timeLimitExceeded/adminLimitExceeded to client
+
+Picking an upstream:
+- while there is a level available:
+ - pick a random ordering of upstreams based on weights
+ - while there is an upstream in the level:
+ - check number of ops in-flight (this is where we lock the upstream map)
+ - find the least busy connection (and check if a new connection should be
+ opened)
+ - try to lock for socket write, if available (no BER queued) we have our
+ upstream
+
+PDU processing:
+- request (have an upstream selected):
+ - get new msgid from upstream
+ - create an Op structure (actually, with the need for freelist lock, we can
+ make it a cache for freed operation structures, avoiding some malloc
+ traffic, to reset, we need slap_sl_mem_create( ,,, 1 ))
+ - check proxyauthz is not present? or just let upstream reject it if there are
+ two?
+ - add own controls at the end:
+ - construct proxyauthz from authzid
+ - construct session tracking from remote IP, own name, authzid
+ - send over
+ - insert Op into client and upstream maps
+- response/intermediate/entry:
+ - look up Op in upstream's map
+ - write old msgid, rest of the response can go unchanged
+ - if a response, remove Op from all maps (client and upstream)
+
+Managing upstreams:
+- async connect up to min_connections (is there a point in having a connection
+ count range if we can't use it when needed since all of the below is async?)
+- when connected, set up TLS (if requested)
+- when done, send a bind
+- go for the bind interaction
+- when done, add it to the upstream's connection list
+- (if a connection is suspended or connections are over 75 % op limit, schedule
+ creating a new connection setup unless connection limit has been hit)
+
+Managing timeouts:
+- two options:
+ - maintain a separate locked priority queue to give a perfect ordering to when
+ each operation is to time out, would need to maintain yet another place
+ where operations can be found.
+ - the locking protocol for disposing of the operation would need to be
+ adjusted and might become even more complicated, might do the alternative
+ initially and then attempt this if it helps performance
+ - just do a sweep over all clients (that mutex is less contended) every so
+ often. With many in-flight operations might be a lot of wasted work.
+ - we still need to sweep over all clients to check if they should be killed
+ anyway
+
+Dispatcher thread (2^n of them, fd x is handled by thread no x % (2^n)):
+- poll on all registered fds
+- remove each fd that's ready from the registered list and schedule the work
+- work threads can put their fd back in if they deem necessary (=not suspended)
+- this works as a poor man's edge-triggered polling, with enough workers, should
+ we do proper edge triggered I/O? What about non-Linux?
+
+Listener thread:
+- slapd has just one, which then reassigns the sockets to separate I/O
+ threads
+
+Threading:
+- if using slap_sl_malloc, how much perf do we gain? To allocate a context per
+ op, we should have a dedicated parent context so that when we free it, we can
+ use that exclusively. The parent context's parent would be the main thread's
+ context. This implies a lot of slap_sl_mem_setctx/slap_sl_mem_create( ,,, 0 )
+ and making sure an op does not allocate/free things from two threads at the
+ same time (might need an Op mutex after all? Not such a huge cost if we
+ routinely reuse Op structures)
+
+Locking policy:
+- read mutexes are unnecessary, we only have one thread receiving data from the
+ connection - the one started from the dispatcher
+- two reference counters of operation structures (an op is accessible from
+ client and upstream map, each counter is consistent when thread has a lock on
+ corresponding map), when decreasing the counter to zero, start freeing
+ procedure
+- place to mark disposal finished for each side, consistency enforced by holding
+ the freelist lock when reading/manipulating
+- when op is created, we already have a write lock on upstream socket and map,
+ start writing, insert to upstream map with upstream refcount 1, unlock, lock
+ client, insert (client refcount 0), unlock, lock upstream, decrement refcount
+ (triggers a test if we need to drop it now), unlock upstream, done
+- when upstream processes a PDU, locks its map, increments counter, (potentially
+ removes if it's a response), unlocks, locks client's map, write mutex (this
+ order?) and full client mutex (if a bind response)
+- when client side wants to work with a PDU (abandon, (un)bind), locks its map,
+ increase refcount, unlocks, locks upstream map, write mutex, sends or queues
+ abandon, unlocks write mutex, initiates freeing procedure from upstream side
+ (or if having to remember we've already increased client-side refcount, mark
+ for deletion, lose upstream lock, lock client, decref, either triggering
+ deletion from client or mark for it)
+- if we have operation lock, we can simplify a bit (no need for three-stage
+ locking above)
+
+Shutdown:
+- stop accept() thread(s) - potentially add a channel to hand these listening
+ sockets over for zero-downtime restart
+- if very gentle, mark connections as closing, start timeout and:
+ - when a new non-abandon PDU comes in from client - return LDAP_UNAVAILABLE
+ - when receiving a PDU from upstream, send over to client, if no ops pending,
+ send unsolicited response and close (RFC4511 suggests unsolicited response
+ is the last PDU coming from the upstream and libldap agrees, so we can't
+ send it for a socket we want to shut down more gracefully)
+- gentle (or very gentle timed out):
+ - set timeout
+ - mark all ops as abandoned
+ - send unbind to all upstreams
+ - send unsolicited to all clients
+- imminent (or gentle timed out):
+ - async close all connections?
+ - exit()
+
+RootDSE:
+- default option is not to care and if a control/exop has special restrictions,
+ it is the admin's job to flag it as such in the load-balancer's config
+- another is not to care about the search request but check each search entry
+ being passed back, check DN and if it's a rootDSE, filter the list of
+ controls/exops/sasl mechs (external!) that are supported
+- last one is to check all search requests for the DN/scope and synthesise the
+ response locally - probably not (would need to configure the complete list of
+ controls, exops, sasl mechs, naming contexts in the balancer)
+
+Potential red flags:
+- we suspend upstreams, if we ever suspend clients we need to be sure we can't
+ create dependency cycles
+ - is this an issue when only suspending the read side of each? Because even if
+ we stop reading from everything, we should eventually flush data to those we
+ can still talk to, as upstreams are flushed, we can start sending new
+ requests from live clients (those that are suspended are due to their own
+ inability to accept data)
+ - we might need to suspend a client if there is a reason to choose a
+ particular upstream (multi-request operation - bind, VC, PR, TXN, ...)
+ - a SASL bind, but that means there are no outstanding ops to receive
+ it holds that !suspended(client) \or !suspended(upstream), so they
+ cannot participate in a cycle
+ - VC - multiple binds at the same time - !!! more analysis needed
+ - PR - should only be able to have one per connection (that's a problem
+ for later, maybe even needs a dedicated upstream connection)
+ - TXN - ??? probably same situation as PR
+ - or if we have a queue for pending Bers on the server, we not need to suspend
+ clients, upstream is only chosen if the queue is free or there is a reason
+ to send it to that particular upstream (multi-stage bind/VC, PR, ...), but
+ that still makes it possible for a client to exhaust all our memory by
+ sending requests (VC or other ones bound to a slow upstream or by not
+ reading the responses at all)
diff --git a/doc/devel/template.c b/doc/devel/template.c
new file mode 100644
index 0000000..28e028d
--- /dev/null
+++ b/doc/devel/template.c
@@ -0,0 +1,26 @@
+/* template.c -- example OpenLDAP source file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright YEAR The OpenLDAP Foundation.
+ * Portions Copyright YEAR Secondary Rights Holder.
+ * Portions Copyright YEAR Another Rights Holder.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Additional (custom) notices (where necessary).
+ * Please consult Kurt Zeilenga <kurt@openldap.org> before adding
+ * additional notices.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Jane Doe for inclusion in
+ * OpenLDAP Software. Additional significant contributors include:
+ * John Doe
+ */
diff --git a/doc/devel/todo b/doc/devel/todo
new file mode 100644
index 0000000..670e9cc
--- /dev/null
+++ b/doc/devel/todo
@@ -0,0 +1,67 @@
+OpenLDAP Software To Do List
+----------------------------
+
+This is a list of projects that need getting done. They are defined
+by scale of the effort as opposed to priority. Contribute to
+projects based upon your personal priorities.
+
+If you would like to work on any of these projects, please coordinate
+by posting to OpenLDAP-devel mailing list:
+ http://www.OpenLDAP.org/lists
+
+If you have a project you'd like added to the list, talk it up on
+Developer's list or just do it.
+
+Please read:
+ http://www.OpenLDAP.org/devel/programming.html
+ http://www.OpenLDAP.org/devel/contributing.html
+
+
+OpenLDAP 2.x Projects
+---------------------
+ SLAPD
+ Complete Unicode Support (ACLs, etc.)
+ client C API update
+ Implement per referral/continuation callback
+ clients (e.g. ldapsearch(1))
+ Implement referral chasing options w/ referral callback
+ Update manual pages
+
+
+Large projects
+--------------
+Implement character string localization
+Implement X.500 administrative models (e.g. subentries (RFC 3672), etc.)
+Implement LDAP sorted search results control (RFC 2891)
+
+
+Medium projects
+---------------
+Add syncrepl turn
+Implement DIT Structure Rules and Name Forms
+Implement LDAPprep
+Implement native support for simple SASL mechanisms
+ (e.g. EXTERNAL and PLAIN)
+Redesign slapd memory allocation fault handling
+Localize tools
+
+
+Small projects
+--------------
+Add DSML capabilities to command line tools
+Add LDIFv2 (XML) support to command line tools
+Implement authPassword (RFC 3112)
+Implement SASLprep (RFC 4013) for LDAP (draft-ietf-ldapbis-*)
+Implement additional matching rules (RFC 3698)
+Add dumpasn1 logging support
+Add tests to test suite
+Recode linked-list structs to use <ldap_queue.h> macros
+Convert utfconv.txt into man page(s).
+Update manual pages as needed.
+
+
+For additional TODO items, see:
+ https://bugs.openldap.org
+
+---
+$OpenLDAP$
diff --git a/doc/devel/toolargs b/doc/devel/toolargs
new file mode 100644
index 0000000..f0f8d9f
--- /dev/null
+++ b/doc/devel/toolargs
@@ -0,0 +1,31 @@
+Tools ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+slapacl D F U X b d f o uv
+slapadd F S bcd fg j l no q s uvw
+slapauth F M R U X d f o v
+slapcat F H abcd fg l no s v
+slapdn F N P d f o v
+slapindex F bcd fg no q t v
+slapmodify F S bcd fg j l no q s uvw
+slappasswd T c h s uv
+slapschema F H abcd fg l no s v
+slaptest F Q d f no uv
+
+* General flags:
+ -F config directory
+ -U authcID
+ -X authzID
+ -b suffix (slapacl: entryDN)
+ -c continue mode
+ -d debug level
+ -f config file
+ -g disable subordinate gluing
+ -l LDIF file
+ -n database number
+ -o options
+ -q "quick" mode
+ -s disable schema checking (slapcat: subtree, slappasswd: secret)
+ -u dryrun (slappasswd: RFC2307 userPassword)
+ -v verbose
+
+---
+$OpenLDAP$
diff --git a/doc/devel/utfconv.txt b/doc/devel/utfconv.txt
new file mode 100644
index 0000000..1adaab5
--- /dev/null
+++ b/doc/devel/utfconv.txt
@@ -0,0 +1,291 @@
+ Dec 5, 2000
+ Dave Steck
+ Novell, Inc.
+
+ UTF-8 Conversion Functions
+
+
+1. Strings in the LDAP C SDK should be encoded in UTF-8 format.
+ However, most platforms do not provide APIs for converting to
+ this format. If they do, they are platform-specific.
+
+ As a result, most applications (knowingly or not) use local strings
+ with LDAP functions. This works fine for 7-bit ASCII characters,
+ but will fail with 8-bit European characters, Asian characters, etc.
+
+ We propose adding the following platform-independent conversion functions
+ to the OpenLDAP SDK. There are 4 functions for converting between UTF-8
+ and wide characters, and 4 functions for converting between UTF-8 and
+ multibyte characters.
+
+ For multibyte to UTF-8 conversions, charset translation is necessary.
+ While a full charset translator is not practical or appropriate for the
+ LDAP SDK, we can pass the translator function in as an argument.
+ A NULL for this argument will use the ANSI C functions mbtowc, mbstowcs,
+ wctomb, and wcstombs.
+
+2. UTF-8 <--> Wide Character conversions
+
+The following new conversion routines will be added, following the pattern of
+the ANSI C conversion routines (mbtowc, mbstowcs, etc). These routines use
+the wchar_t type. wchar_t is 2 bytes on some systems and 4 bytes on others.
+However the advantage of using wchar_t is that all the standard wide character
+string functions may be used on these strings: wcslen, wcscpy, etc.
+
+ int ldap_x_utf8_to_wc - Convert a single UTF-8 encoded character to a wide character.
+ int ldap_x_utf8s_to_wcs - Convert a UTF-8 string to a wide character string.
+ int ldap_x_wc_to_utf8 - Convert a single wide character to a UTF-8 sequence.
+ int ldap_x_wcs_to_utf8s - Convert a wide character string to a UTF-8 string.
+
+
+2.1 ldap_x_utf8_to_wc - Convert a single UTF-8 encoded character to a wide character.
+
+int ldap_x_utf8_to_wc ( wchar_t *wchar, const char *utf8char )
+
+ wchar (OUT) Points to a wide character code to receive the
+ converted character.
+
+ utf8char (IN) Address of the UTF8 sequence of bytes.
+
+Return Value:
+ If successful, the function returns the length in
+ bytes of the UTF-8 input character.
+
+ If utf8char is NULL or points to an empty string, the
+ function returns 1 and a NULL is written to wchar.
+
+ If utf8char contains an invalid UTF-8 sequence -1 is returned.
+
+
+2.2 ldap_x_utf8s_to_wcs - Convert a UTF-8 string to a wide character string.
+
+int ldap_x_utf8s_to_wcs (wchar_t *wcstr, const char *utf8str, size_t count)
+
+ wcstr (OUT) Points to a wide char buffer to receive the
+ converted wide char string. The output string will be
+ null terminated if there is space for it in the
+ buffer.
+
+ utf8str (IN) Address of the null-terminated UTF-8 string to convert.
+
+ count (IN) The number of UTF-8 characters to convert, or
+ equivalently, the size of the output buffer in wide
+ characters.
+
+Return Value:
+ If successful, the function returns the number of wide
+ characters written to wcstr, excluding the null termination
+ character, if any.
+
+ If wcstr is NULL, the function returns the number of wide
+ characters required to contain the converted string,
+ excluding the null termination character.
+
+ If an invalid UTF-8 sequence is encountered, the
+ function returns -1.
+
+ If the return value equals count, there was not enough space to fit the
+ string and the null terminator in the buffer.
+
+
+2.3 ldap_x_wc_to_utf8 - Convert a single wide character to a UTF-8 sequence.
+
+int ldap_x_wc_to_utf8 ( char *utf8char, wchar_t wchar, count )
+
+ utf8char (OUT) Points to a byte array to receive the converted UTF-8
+ string.
+
+ wchar (IN) The wide character to convert.
+
+ count (IN) The maximum number of bytes to write to the output
+ buffer. Normally set this to LDAP_MAX_UTF8_LEN, which
+ is defined as 3 or 6 depending on the size of wchar_t.
+ A partial character will not be written.
+
+Return Value:
+ If successful, the function returns the length in bytes of
+ the converted UTF-8 output character.
+
+ If wchar is NULL, the function returns 1 and a NULL is
+ written to utf8char.
+
+ If wchar cannot be converted to a UTF-8 character, the
+ function returns -1.
+
+
+2.4 int ldap_x_wcs_to_utf8s - Convert a wide character string to a UTF-8 string.
+
+int ldap_x_wcs_to_utf8s (char *utf8str, const wchar_t *wcstr, size_t count)
+
+ utf8str (OUT) Points to a byte array to receive the converted
+ UTF-8 string. The output string will be null
+ terminated if there is space for it in the
+ buffer.
+
+
+ wcstr (IN) Address of the null-terminated wide char string to convert.
+
+ count (IN) The size of the output buffer in bytes.
+
+Return Value:
+ If successful, the function returns the number of bytes
+ written to utf8str, excluding the null termination
+ character, if any.
+
+ If utf8str is NULL, the function returns the number of
+ bytes required to contain the converted string, excluding
+ the null termination character. The 'count' parameter is ignored.
+
+ If the function encounters a wide character that cannot
+ be mapped to a UTF-8 sequence, the function returns -1.
+
+ If the return value equals count, there was not enough space to fit
+ the string and the null terminator in the buffer.
+
+
+
+3. Multi-byte <--> UTF-8 Conversions
+
+These functions convert the string in a two-step process, from multibyte
+to Wide, then from Wide to UTF8, or vice versa. This conversion requires a
+charset translation routine, which is passed in as an argument.
+
+ ldap_x_mb_to_utf8 - Convert a multi-byte character to a UTF-8 character.
+ ldap_x_mbs_to_utf8s - Convert a multi-byte string to a UTF-8 string.
+ ldap_x_utf8_to_mb - Convert a UTF-8 character to a multi-byte character.
+ ldap_x_utf8s_to_mbs - Convert a UTF-8 string to a multi-byte string.
+
+3.1 ldap_x_mb_to_utf8 - Convert a multi-byte character to a UTF-8 character.
+
+int ldap_x_mb_to_utf8 ( char *utf8char, const char *mbchar, size_t mbsize, int (*f_mbtowc)(wchar_t *wchar, const char *mbchar, size_t count) )
+
+ utf8char (OUT) Points to a byte buffer to receive the converted
+ UTF-8 character. May be NULL. The output is not
+ null-terminated.
+
+ mbchar (IN) Address of a sequence of bytes forming a multibyte character.
+
+ mbsize (IN) The maximum number of bytes of the mbchar argument to
+ check. This should normally be MB_CUR_MAX.
+
+ f_mbtowc (IN) The function to use for converting a multibyte
+ character to a wide character. If NULL, the local
+ ANSI C routine mbtowc is used.
+
+Return Value:
+ If successful, the function returns the length in bytes of
+ the UTF-8 output character.
+
+ If utf8char is NULL, count is ignored and the function
+ returns the number of bytes that would be written to the
+ output char.
+
+ If count is zero, 0 is returned and nothing is written to
+ utf8char.
+
+ If mbchar is NULL or points to an empty string, the
+ function returns 1 and a null byte is written to utf8char.
+
+ If mbchar contains an invalid multi-byte character, -1 is returned.
+
+
+3.2 ldap_x_mbs_to_utf8s - Convert a multi-byte string to a UTF-8 string.
+
+int ldap_x_mbs_to_utf8s (char *utf8str, const char *mbstr, size_t count,
+ size_t (*f_mbstowcs)(wchar_t *wcstr, const char *mbstr, size_t count))
+
+utf8str (OUT) Points to a buffer to receive the converted UTF-8 string.
+ May be NULL.
+
+ mbchar (IN) Address of the null-terminated multi-byte input string.
+
+ count (IN) The size of the output buffer in bytes.
+
+ f_mbstowcs (IN) The function to use for converting a multibyte string
+ to a wide character string. If NULL, the local ANSI
+ C routine mbstowcs is used.
+
+Return Value:
+ If successful, the function returns the length in
+ bytes of the UTF-8 output string, excluding the null
+ terminator, if present.
+
+ If utf8str is NULL, count is ignored and the function
+ returns the number of bytes required for the output string,
+ excluding the NULL.
+
+ If count is zero, 0 is returned and nothing is written to utf8str.
+
+ If mbstr is NULL or points to an empty string, the
+ function returns 1 and a null byte is written to utf8str.
+
+ If mbstr contains an invalid multi-byte character, -1 is returned.
+
+ If the returned value is equal to count, the entire null-terminated
+ string would not fit in the output buffer.
+
+
+3.3 ldap_x_utf8_to_mb - Convert a UTF-8 character to a multi-byte character.
+
+int ldap_x_utf8_to_mb ( char *mbchar, const char *utf8char,
+ int (*f_wctomb)(char *mbchar, wchar_t wchar) )
+
+mbchar (OUT) Points to a byte buffer to receive the converted multi-byte
+ character. May be NULL.
+
+ utf8char (IN) Address of the UTF-8 character sequence.
+
+ f_wctomb (IN) The function to use for converting a wide character
+ to a multibyte character. If NULL, the local
+ ANSI C routine wctomb is used.
+
+
+Return Value:
+ If successful, the function returns the length in
+ bytes of the multi-byte output character.
+
+ If utf8char is NULL or points to an empty string, the
+ function returns 1 and a null byte is written to mbchar.
+
+ If utf8char contains an invalid UTF-8 sequence, -1 is returned.
+
+
+3.4 int ldap_x_utf8s_to_mbs - Convert a UTF-8 string to a multi-byte string.
+
+
+int ldap_x_utf8s_to_mbs ( char *mbstr, const char *utf8str, size_t count,
+ size_t (*f_wcstombs)(char *mbstr, const wchar_t *wcstr, size_t count) )
+
+ mbstr (OUT) Points to a byte buffer to receive the converted
+ multi-byte string. May be NULL.
+
+ utf8str (IN) Address of the null-terminated UTF-8 string to convert.
+
+ count (IN) The size of the output buffer in bytes.
+
+ f_wcstombs (IN) The function to use for converting a wide character
+ string to a multibyte string. If NULL, the local
+ ANSI C routine wcstombs is used.
+
+Return Value:
+ If successful, the function returns the number of bytes
+ written to mbstr, excluding the null termination
+ character, if any.
+
+ If mbstr is NULL, count is ignored and the function
+ returns the number of bytes required for the output string,
+ excluding the NULL.
+
+ If count is zero, 0 is returned and nothing is written to
+ mbstr.
+
+ If utf8str is NULL or points to an empty string, the
+ function returns 1 and a null byte is written to mbstr.
+
+ If an invalid UTF-8 character is encountered, the
+ function returns -1.
+
+The output string will be null terminated if there is space for it in
+the output buffer.
+
+
diff --git a/doc/devel/variadic_debug/03-libldap_Debug.cocci b/doc/devel/variadic_debug/03-libldap_Debug.cocci
new file mode 100644
index 0000000..8353e64
--- /dev/null
+++ b/doc/devel/variadic_debug/03-libldap_Debug.cocci
@@ -0,0 +1,70 @@
+using "equivalence.iso"
+
+@initialize:ocaml@
+@@
+// count the number of % characters in the format string
+let fmtn(fmt,n) =
+ List.length (Str.split_delim (Str.regexp_string "%") fmt) = n + 1
+
+# replace osip_debug/oslocal_debug with Debug() macros first
+@@
+expression E;
+expression list args;
+@@
+(
+-osip_debug
+|
+-oslocal_debug
+)
++Debug
+ (
+-E,
++LDAP_DEBUG_TRACE,
+ args );
+
+// replace Debug( ..., arg1, arg2, 0 ) with Debug2( ..., arg1, arg2 )
+@@
+char[] fmt : script:ocaml() { fmtn(fmt,2) };
+expression list[2] args;
+expression E;
+@@
+
+-Debug
++Debug2
+ ( E, _(fmt), args
+-, 0
+ );
+
+// replace Debug( ..., arg1, 0, 0 ) with Debug1()
+@@
+char[] fmt : script:ocaml() { fmtn(fmt,1) };
+expression list[1] args;
+expression E;
+@@
+
+-Debug
++Debug1
+ ( E, _(fmt), args
+-, 0, 0
+ );
+
+// Zero-argument Debug() -> Debug0()
+@@
+expression E, S;
+@@
+
+-Debug
++Debug0
+ ( E, S
+-, 0, 0, 0
+ );
+
+// everything else is a regular 3-argument debug macro, replace with Debug3()
+@@
+expression E, S;
+expression list[3] args;
+@@
+
+-Debug
++Debug3
+ ( E, S, args );
diff --git a/doc/devel/variadic_debug/04-variadic.cocci b/doc/devel/variadic_debug/04-variadic.cocci
new file mode 100644
index 0000000..bd5fbea
--- /dev/null
+++ b/doc/devel/variadic_debug/04-variadic.cocci
@@ -0,0 +1,165 @@
+@initialize:ocaml@
+@@
+// count the number of % characters in the format string
+let fmtn(fmt,n) =
+ List.length (Str.split_delim (Str.regexp_string "%") fmt) = n + 1
+
+@@
+identifier Logs =~ "Log[0-9]";
+@@
+-Logs
++Log
+
+@@
+@@
+-StatslogTest
++LogTest
+
+// Process two-argument Debug() macros with an extra zero
+@@
+char[] fmt : script:ocaml() { fmtn(fmt,2) };
+expression list[2] args;
+expression E;
+@@
+
+Debug( E, fmt, args
+-, 0
+ );
+
+@@
+char[] fmt : script:ocaml() { fmtn(fmt,2) };
+expression list[2] args;
+expression E;
+@@
+
+Debug( E, fmt, args
+-, NULL
+ );
+
+// Single argument Debug() macros with two extra zeroes
+@@
+char[] fmt : script:ocaml() { fmtn(fmt,1) };
+expression list[1] args;
+expression E;
+@@
+
+Debug( E, fmt, args
+-, 0, 0
+ );
+
+@@
+char[] fmt : script:ocaml() { fmtn(fmt,1) };
+expression list[1] args;
+expression E;
+@@
+
+Debug( E, fmt, args
+-, NULL, NULL
+ );
+
+// Debug() macros with no arguments just padded with zeroes
+@@
+expression E, S;
+@@
+
+Debug( E, S
+-, 0, 0, 0
+ );
+
+@@
+expression E, S;
+@@
+
+Debug( E, S
+-, NULL, NULL, NULL
+ );
+
+// Similar to above, just for Statslog
+@@
+char[] fmt : script:ocaml() { fmtn(fmt,5) };
+expression list[5] args;
+expression E;
+@@
+
+-Statslog
++Debug
+ ( E, fmt, args );
+
+@@
+char[] fmt : script:ocaml() { fmtn(fmt,4) };
+expression list[4] args;
+expression E;
+@@
+
+-Statslog
++Debug
+ ( E, fmt, args
+-, 0
+ );
+
+@@
+char[] fmt : script:ocaml() { fmtn(fmt,3) };
+expression list[3] args;
+expression E;
+@@
+
+-Statslog
++Debug
+ ( E, fmt, args
+-, 0, 0
+ );
+
+@@
+char[] fmt : script:ocaml() { fmtn(fmt,2) };
+expression list[2] args;
+expression E;
+@@
+
+-Statslog
++Debug
+ ( E, fmt, args
+-, 0, 0, 0
+ );
+
+@@
+char[] fmt : script:ocaml() { fmtn(fmt,1) };
+expression list[1] args;
+expression E;
+@@
+
+-Statslog
++Debug
+ ( E, fmt, args
+-, 0, 0, 0, 0
+ );
+
+@@
+expression E, S;
+@@
+
+-Statslog
++Debug
+ ( E, S
+-, 0, 0, 0, 0, 0
+ );
+
+// And StatslogEtime
+@@
+char[] fmt : script:ocaml() { fmtn(fmt,4) };
+expression list[4] args;
+expression E;
+@@
+
+StatslogEtime( E, fmt, args
+-, 0
+ );
+
+@@
+identifier Stats =~ "^Statslog";
+@@
+(
+ StatslogEtime
+|
+-Stats
++Debug
+)
diff --git a/doc/devel/variadic_debug/07-shortcut.cocci b/doc/devel/variadic_debug/07-shortcut.cocci
new file mode 100644
index 0000000..99b3b55
--- /dev/null
+++ b/doc/devel/variadic_debug/07-shortcut.cocci
@@ -0,0 +1,216 @@
+// Splice string `s` into the format string `fmtstring` replacing the
+// %-parameter at position `pos`
+@initialize:python@
+@@
+
+# regex from https://stackoverflow.com/questions/30011379/how-can-i-parse-a-c-format-string-in-python
+import re
+fmtstring = '''\
+( # start of capture group 1
+% # literal "%"
+(?: # first option
+(?:[-+0 #]{0,5}) # optional flags
+(?:\d+|\*)? # width
+(?:\.(?:\d+|\*))? # precision
+(?:h|l|ll|w|I|I32|I64)? # size
+[cCdiouxXeEfgGaAnpsSZ] # type
+) | # OR
+%%) # literal "%%"
+'''
+
+regex = re.compile(fmtstring, re.X)
+
+def parse_format(f):
+ return tuple((m.span(), m.group()) for m in
+ regex.finditer(f))
+
+def insert_at_pos(fmt, s, pos):
+ formats = parse_format(fmt)
+ span, format = formats[pos]
+ acc = fmt[:span[0]]
+ if s.startswith('"'):
+ acc += s[1:]
+ else:
+ acc += '" '
+ acc += s
+ if acc.endswith('"'):
+ acc = acc[:-1] + fmt[span[1]:]
+ else:
+ acc += ' "'
+ acc += fmt[span[1]:]
+ return acc
+
+// rest of the file implements the same as 09-merge.cocci
+// The main difference is that we only match on snprintf and Debug that are
+// directly adjacent, not based on control flow information which trips
+// coccinelle's model-checker
+@shortcut@
+identifier buf;
+expression E, L;
+expression list args_before, args, args_after;
+expression format1, format2;
+position p1, p2;
+@@
+
+snprintf@p1( buf, E, format1, args );
+Debug@p2( L, format2, args_before, buf, args_after );
+
+// use insert_at_pos above to construct the new format-string
+@script:python shortcut_process@
+format1 << shortcut.format1;
+format2 << shortcut.format2;
+args_before << shortcut.args_before;
+merged;
+@@
+
+pos = len(args_before.elements)
+coccinelle.merged = insert_at_pos(format2, format1, pos)
+
+@shortcut_replace@
+position shortcut.p1, shortcut.p2;
+identifier shortcut_process.merged;
+
+identifier buf;
+expression E, L;
+expression list args_before, args, args_after;
+expression format1, format2;
+@@
+
+-snprintf@p1( buf, E, format1, args );
+-Debug@p2( L, format2, args_before, buf, args_after );
++Debug( L, merged, args_before, args, args_after );
+
+@shortcut_locked@
+identifier buf;
+expression E, L, lock;
+expression list args_before, args, args_after;
+expression format1, format2;
+position p1, p2;
+@@
+
+ldap_pvt_thread_mutex_lock(lock);
+snprintf@p1( buf, E, format1, args );
+ldap_pvt_thread_mutex_unlock(lock);
+Debug@p2( L, format2, args_before, buf, args_after );
+
+// use insert_at_pos above to construct the new format-string
+@script:python shortcut_locked_process@
+format1 << shortcut_locked.format1;
+format2 << shortcut_locked.format2;
+args_before << shortcut_locked.args_before;
+merged;
+@@
+
+pos = len(args_before.elements)
+coccinelle.merged = insert_at_pos(format2, format1, pos)
+
+@shortcut_locked_replace@
+position shortcut_locked.p1, shortcut_locked.p2;
+identifier shortcut_locked_process.merged;
+
+identifier buf;
+expression E, L, lock;
+expression list args_before, args, args_after;
+expression format1, format2;
+@@
+
+ldap_pvt_thread_mutex_lock(lock);
+-snprintf@p1( buf, E, format1, args );
++Debug( L, merged, args_before, args, args_after );
+ldap_pvt_thread_mutex_unlock(lock);
+-Debug@p2( L, format2, args_before, buf, args_after );
+
+// so long as we don't reference 'buf' afterwards, no need to keep it defined.
+// A lot of pattern-matching is spelled out explicitly to work around the fact
+// that the state space doesn't get compressed otherwise.
+@@
+type T;
+identifier buf, id;
+expression E, lock;
+initializer I;
+@@
+{
+-\( T buf = I; \| T buf; \)
+(
+ ldap_pvt_thread_mutex_lock(lock);
+|
+)
+(
+ Debug( ... );
+&
+ ... when != buf
+)
+(
+ ldap_pvt_thread_mutex_unlock(lock);
+|
+)
+(
+|
+ continue;
+|
+ break;
+|
+ goto id;
+|
+ \(
+ return E;
+ \&
+ ... when != buf
+ \)
+)
+}
+
+// the rest identifies and removes a (newly-)redundant LogTest check
+@if_guard@
+position p;
+statement s;
+@@
+
+(
+ if ( ... ) {@p
+ Debug( ... );
+ } else s
+|
+ if ( ... ) {@p
+ Debug( ... );
+ }
+)
+
+@else_guard@
+position p;
+statement s;
+@@
+
+if ( ... ) s
+else {@p
+ Debug( ... );
+}
+
+@loop_guard@
+position p;
+@@
+
+(
+ while ( ... ) {@p
+ Debug( ... );
+ }
+|
+ for ( ...;...;... ) {@p
+ Debug( ... );
+ }
+)
+
+@@
+position p != { if_guard.p , else_guard.p, loop_guard.p };
+@@
+-{@p
+ Debug( ... );
+-}
+
+@useless_if@
+expression L;
+@@
+
+-if ( LogTest( L ) ) {
+ Debug( L, ... );
+-}
diff --git a/doc/devel/variadic_debug/09-merge.cocci b/doc/devel/variadic_debug/09-merge.cocci
new file mode 100644
index 0000000..4b0c1b2
--- /dev/null
+++ b/doc/devel/variadic_debug/09-merge.cocci
@@ -0,0 +1,147 @@
+// Note that this file has not actually been used in the end, since
+// 07-shortcut.cocci covers everything we needed in the project, but being
+// simpler, it makes the intent of 07-shortcut.cocci clearer
+
+
+// Splice string `s` into the format string `fmtstring` replacing the
+// %-parameter at position `pos`
+@initialize:python@
+@@
+
+#regex from https://stackoverflow.com/questions/30011379/how-can-i-parse-a-c-format-string-in-python
+import re
+fmtstring = '''\
+( # start of capture group 1
+% # literal "%"
+(?: # first option
+(?:[-+0 #]{0,5}) # optional flags
+(?:\d+|\*)? # width
+(?:\.(?:\d+|\*))? # precision
+(?:h|l|ll|w|I|I32|I64)? # size
+[cCdiouxXeEfgGaAnpsSZ] # type
+) | # OR
+%%) # literal "%%"
+'''
+
+regex = re.compile(fmtstring, re.X)
+
+def parse_format(f):
+ return tuple((m.span(), m.group()) for m in
+ regex.finditer(f))
+
+def insert_at_pos(fmt, s, pos):
+ formats = parse_format(fmt)
+ span, format = formats[pos]
+ acc = fmt[:span[0]]
+ if s.startswith('"'):
+ acc += s[1:]
+ else:
+ acc += '" '
+ acc += s
+ if acc.endswith('"'):
+ acc = acc[:-1] + fmt[span[1]:]
+ else:
+ acc += ' "'
+ acc += fmt[span[1]:]
+ return acc
+
+// Identify the redundant snprintfs (within a locked region)
+@a exists@
+expression lock, E, L;
+expression list args_before, args, args_after;
+identifier buf;
+expression format1, format2;
+type T;
+position p1, p2;
+@@
+
+{
+...
+T buf;
+...
+ldap_pvt_thread_mutex_lock(lock);
+...
+snprintf@p1( buf, E, format1, args );
+...
+ldap_pvt_thread_mutex_unlock(lock);
+...
+Debug@p2( L, format2, args_before, buf, args_after );
+...
+}
+
+// Merge the format strings with insert_at_pos above
+@script:python a_process@
+format1 << a.format1;
+format2 << a.format2;
+args_before << a.args_before;
+merged;
+@@
+
+pos = len(args_before.elements)
+coccinelle.merged = insert_at_pos(format2, format1, pos)
+
+// And merge the two together, replacing the extra buffer that's not used anymore
+@a_replace@
+position a.p1, a.p2;
+identifier a_process.merged;
+
+expression lock, E, L;
+expression list args_before, args, args_after;
+identifier buf;
+expression format1, format2;
+type T;
+@@
+
+{
+...
+-T buf;
+...
+ldap_pvt_thread_mutex_lock(lock);
+...
+-snprintf@p1( buf, E, format1, args );
++Debug( L, merged, args_before, args, args_after );
+...
+ldap_pvt_thread_mutex_unlock(lock);
+...
+-Debug@p2( L, format2, args_before, buf, args_after );
+...
+}
+
+// Once again (same as the 'a' series above, but those that remain to be sorted
+// now don't need to stay within a locked region
+@b exists@
+expression E, L;
+expression list args_before, args, args_after;
+identifier buf;
+expression format1, format2;
+position p1, p2;
+@@
+
+snprintf@p1( buf, E, format1, args );
+...
+Debug@p2( L, format2, args_before, buf, args_after );
+
+@script:python b_process@
+format1 << b.format1;
+format2 << b.format2;
+args_before << b.args_before;
+merged;
+@@
+
+pos = len(args_before.elements)
+coccinelle.merged = insert_at_pos(format2, format1, pos)
+
+@b_replace@
+position b.p1, b.p2;
+identifier b_process.merged;
+
+expression E, L;
+expression list args_before, args, args_after;
+identifier buf;
+expression format1, format2;
+@@
+
+-snprintf@p1( buf, E, format1, args );
++Debug( L, merged, args_before, args, args_after );
+...
+-Debug@p2( L, format2, args_before, buf, args_after );
diff --git a/doc/devel/variadic_debug/README b/doc/devel/variadic_debug/README
new file mode 100644
index 0000000..3ccbea2
--- /dev/null
+++ b/doc/devel/variadic_debug/README
@@ -0,0 +1,39 @@
+Most of the project now depends on the compiler supporting C99 variadic
+macros. This is used in the Debug() macro everywhere except libldap and
+its dependencies.
+
+From now on, any time Debug( level, fmt, args... ) is used, you can and
+should provide the appropriate number of arguments. The coccinelle
+patches in this directory implement the transformations used to bring
+the project in line with this.
+
+As we still aim to support libldap on platforms that only provide C89,
+Debug0/1/2/3 macros are used instead.
+
+If you need to adapt your own fork, see ITS#8731, the rest of this
+README and scripts in this directory on what you'll need to achieve
+this.
+
+Coccinelle as of git hash e65a7bdc04ac9122acdae2353422c5736b7998ba from
+https://github.com/coccinelle/coccinelle has been used to run the
+transformations performed. One notable limitation at the time of writing
+is that multi-part (format) strings are always merged onto the same line.
+
+Some sources cannot be processed, nssov overlay being a prime example,
+being wrapped in non-trivial macros.
+
+The following semantic patches are involved:
+- 03-libldap_Debug.cocci: converts the libraries to use the Debug[0123]
+ macros as appropriate
+- 04-variadic.cocci: converts the rest of the project to use the Debug
+ macro with the right number of arguments (as opposed to padding with
+ zeroes)
+- 09-merge.cocci will merge an 'snprintf(s, len, "fmt", args...);
+ Debug(level, "... %s ...", ..., s, ...);' sequence together
+- 07-shortcut.cocci is actually used to apply the above since
+ coccinelle's model-checker seems to struggle with state space
+ explosion in some of the very long and complex functions we have -
+ 09-merge.cocci doesn't finish in any reasonable time
+
+The equivalence.iso and macros.h files aid coccinelle to parse our
+sources correctly and simplify the semantic patches.
diff --git a/doc/devel/variadic_debug/equivalence.iso b/doc/devel/variadic_debug/equivalence.iso
new file mode 100644
index 0000000..07372fb
--- /dev/null
+++ b/doc/devel/variadic_debug/equivalence.iso
@@ -0,0 +1,12 @@
+Expression
+@ NULL @
+@@
+
+NULL <=> 0
+
+Expression
+@ underscore_func @
+expression E;
+@@
+
+_(E) => E
diff --git a/doc/devel/variadic_debug/macros.h b/doc/devel/variadic_debug/macros.h
new file mode 100644
index 0000000..265c549
--- /dev/null
+++ b/doc/devel/variadic_debug/macros.h
@@ -0,0 +1,23 @@
+#define LDAP_PF_LOCAL_SENDMSG_ARG(x)
+
+#define LDAP_P(x) x
+#define LDAP_F(x) extern x
+#define LDAP_V(x) extern x
+
+#define LDAP_GCCATTR(x)
+#define LDAP_XSTRING(x) ""
+#define LDAP_CONCAT(x,y) x
+
+#define LDAP_CONST const
+#define LDAP_BEGIN_DECL
+#define LDAP_END_DECL
+
+#define SLAP_EVENT_DECL
+#define SLAP_EVENT_FNAME
+
+/* contrib/slapd-modules/smbk5pwd/smbk5pwd.c */
+#define HDB int*
+
+#define BACKSQL_ARBITRARY_KEY
+#define BACKSQL_IDNUMFMT "%llu"
+#define BACKSQL_IDFMT "%s"
diff --git a/doc/devel/variadic_debug/script.sh b/doc/devel/variadic_debug/script.sh
new file mode 100755
index 0000000..b9fd9f0
--- /dev/null
+++ b/doc/devel/variadic_debug/script.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+
+set -e
+
+PATCH_DIR=doc/devel/variadic_debug
+
+SPATCH=${SPATCH:-spatch}
+SPATCH_OPTS=( --macro-file-builtins "$PATCH_DIR/macros.h" )
+#SPATCH_OPTS+=( --timeout 300 )
+
+SED_TRANSFORMATIONS=()
+
+# split out multipart strings back to original form (one per line)
+SED_TRANSFORMATIONS+=( -e 's/^\(+\s*\)\(.*"\) \(".*\)"$/\1\2\n+\1\3/' )
+
+# re-add whitespace around parentheses
+SED_TRANSFORMATIONS+=( -e 's/^\(+.*Debug[0-3]\?(\)\s*/\1 /' )
+SED_TRANSFORMATIONS+=( -e 's/^\(+.*[^ ]\));$/\1 );/' )
+
+# strip trailing whitespace copied from source on affected lines
+SED_TRANSFORMATIONS+=( -e 's/^\(+.*\)\s\+$/\1/' )
+
+# fix whitespace errors in source we touch
+SED_TRANSFORMATIONS+=( -e 's/^\(+.*\) \t/\1\t\t/' )
+SED_TRANSFORMATIONS+=( -e 's/^\(+\t*\) \{1,3\}\t/\1\t/' )
+
+normalise() {
+ patch="$1"
+ shift
+
+ # iterate until we've reached fixpoint
+ while ! cmp "$patch" "${patch}.new" 2>/dev/null; do
+ if [ -e "${patch}.new" ]; then
+ mv -- "${patch}.new" "$patch"
+ fi
+ sed "${SED_TRANSFORMATIONS[@]}" -- "$patch" >"${patch}.new"
+ done
+ rediff "$patch" >"${patch}.new"
+ mv -- "${patch}.new" "$patch"
+}
+
+git add "$PATCH_DIR"
+git commit -m "ITS#8731 Add the documentation and scripts"
+
+git am "$PATCH_DIR/00-fixes.patch"
+git am "$PATCH_DIR/01-logging.patch"
+git am "$PATCH_DIR/02-manual.patch"
+
+$SPATCH "${SPATCH_OPTS[@]}" -sp_file "$PATCH_DIR/03-libldap_Debug.cocci" \
+ -dir libraries/libldap \
+ >"$PATCH_DIR/03-libldap_Debug.patch"
+normalise "$PATCH_DIR/03-libldap_Debug.patch"
+git apply --index --directory libraries/libldap "$PATCH_DIR/03-libldap_Debug.patch"
+git commit -m "ITS#8731 Apply $PATCH_DIR/03-libldap_Debug.cocci"
+
+$SPATCH "${SPATCH_OPTS[@]}" -sp_file "$PATCH_DIR/04-variadic.cocci" \
+ -dir . \
+ >"$PATCH_DIR/04-variadic.patch"
+normalise "$PATCH_DIR/04-variadic.patch"
+git apply --index "$PATCH_DIR/04-variadic.patch"
+git commit -m "ITS#8731 Apply $PATCH_DIR/04-variadic.cocci"
+
+git am "$PATCH_DIR/05-back-sql.patch"
+git am "$PATCH_DIR/06-nssov.patch"
+
+$SPATCH "${SPATCH_OPTS[@]}" -sp_file "$PATCH_DIR/07-shortcut.cocci" \
+ -dir . \
+ >"$PATCH_DIR/07-shortcut.patch"
+normalise "$PATCH_DIR/07-shortcut.patch"
+git apply --index "$PATCH_DIR/07-shortcut.patch"
+git commit -m "ITS#8731 Apply $PATCH_DIR/07-shortcut.cocci"
+
+git am "$PATCH_DIR/08-snprintf-manual.patch"
diff --git a/doc/guide/README b/doc/guide/README
new file mode 100644
index 0000000..87b3bce
--- /dev/null
+++ b/doc/guide/README
@@ -0,0 +1,16 @@
+This module contains OpenLDAP guides in Simple Document Format (SDF).
+
+SDF is a freely available documentation system. Based on a
+simple, readable markup language, SDF generates high quality
+output in multiple formats.
+
+ cd admin # OpenLDAP Administrator's Guide
+ sdf -2topics index.sdf # generate HTML for WWW publishing
+ sdf -2html guide.sdf # generate HTML for release
+ sdf -2txt guide.sdf # generate TXT for release
+
+More information about STF can be obtained from the CPAN at:
+ http://search.cpan.org/src/IANC/sdf-2.001/doc/catalog.html
+
+SDF itself can be obtained at:
+ http://search.cpan.org/~ianc/sdf-2.001/
diff --git a/doc/guide/admin/Makefile b/doc/guide/admin/Makefile
new file mode 100644
index 0000000..08fa870
--- /dev/null
+++ b/doc/guide/admin/Makefile
@@ -0,0 +1,101 @@
+## Makefile for OpenLDAP Administrator's Guide
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2005-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+all: guide.html index.html
+
+# for website building (for webmaster use, don't change)
+www: guide.html booktitle.html index.html OpenLDAP-Admin-Guide.pdf
+
+sdf-src: \
+ ../plain.sdf \
+ ../preamble.sdf \
+ abstract.sdf \
+ access-control.sdf \
+ appendix-changes.sdf \
+ appendix-common-errors.sdf \
+ appendix-configs.sdf \
+ appendix-contrib.sdf \
+ appendix-deployments.sdf \
+ appendix-ldap-result-codes.sdf \
+ appendix-recommended-versions.sdf \
+ appendix-upgrading.sdf \
+ backends.sdf \
+ config.sdf \
+ dbtools.sdf \
+ glossary.sdf \
+ guide.sdf \
+ install.sdf \
+ intro.sdf \
+ loadbalancer.sdf \
+ maintenance.sdf \
+ master.sdf \
+ monitoringslapd.sdf \
+ overlays.sdf \
+ preface.sdf \
+ quickstart.sdf \
+ referrals.sdf \
+ replication.sdf \
+ runningslapd.sdf \
+ sasl.sdf \
+ schema.sdf \
+ security.sdf \
+ slapdconfig.sdf \
+ title.sdf \
+ tls.sdf \
+ troubleshooting.sdf \
+ tuning.sdf
+
+sdf-img: \
+ ../images/LDAPlogo.gif \
+ allmail-en.png \
+ allusersgroup-en.png \
+ config_dit.png \
+ config_local.png \
+ config_ref.png \
+ config_repl.png \
+ delta-syncrepl.png \
+ dual_dc.png \
+ intro_dctree.png \
+ intro_tree.png \
+ ldap-sync-refreshandpersist.png \
+ ldap-sync-refreshonly.png \
+ load-balancer-scenario.png \
+ n-way-multi-provider.png \
+ push-based-complete.png \
+ push-based-standalone.png \
+ refint.png \
+ set-following-references.png \
+ set-memberUid.png \
+ set-recursivegroup.png
+
+guide.html: guide.sdf sdf-src sdf-img
+ sdf -2html guide.sdf
+
+index.html: index.sdf sdf-src sdf-img
+ sdf -2topics index.sdf
+
+admin.html: admin.sdf sdf-src sdf-img
+ sdf -DPDF -2html admin.sdf
+
+booktitle.html: booktitle.sdf
+ sdf -2html booktitle.sdf
+
+guide.pdf: admin.html booktitle.html guide.book
+ htmldoc --batch guide.book -f guide.pdf
+
+OpenLDAP-Admin-Guide.pdf: admin.html guide.book
+ htmldoc --batch guide.book -f OpenLDAP-Admin-Guide.pdf
+
+clean:
+ rm -f *.pdf *.html *~ *.bak
diff --git a/doc/guide/admin/README.spellcheck b/doc/guide/admin/README.spellcheck
new file mode 100644
index 0000000..fa436bc
--- /dev/null
+++ b/doc/guide/admin/README.spellcheck
@@ -0,0 +1,16 @@
+# $OpenLDAP$
+# Copyright 2007-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+#
+# README.spellcheck
+#
+
+aspell.en.pws
+ We use aspell to spell check the Admin Guide and Man Pages.
+
+ Please move aspell.en.pws to ~/.aspell.en.pws and run:
+
+ aspell --lang=en_US -c <filename>
+
+ If you add additional words and terms, please add
+ them or copy them to aspell.en.pws and commit.
diff --git a/doc/guide/admin/abstract.sdf b/doc/guide/admin/abstract.sdf
new file mode 100644
index 0000000..8d5ee2c
--- /dev/null
+++ b/doc/guide/admin/abstract.sdf
@@ -0,0 +1,7 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+#
+# OpenLDAP Administrator's Guide: Abstract
+
+
diff --git a/doc/guide/admin/access-control.sdf b/doc/guide/admin/access-control.sdf
new file mode 100644
index 0000000..edcc5a1
--- /dev/null
+++ b/doc/guide/admin/access-control.sdf
@@ -0,0 +1,1342 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: Access Control
+
+H2: Introduction
+
+As the directory gets populated with more and more data of varying sensitivity,
+controlling the kinds of access granted to the directory becomes more and more
+critical. For instance, the directory may contain data of a confidential nature
+that you may need to protect by contract or by law. Or, if using the directory
+to control access to other services, inappropriate access to the directory may
+create avenues of attack to your sites security that result in devastating
+damage to your assets.
+
+Access to your directory can be configured via two methods, the first using
+{{SECT:The slapd Configuration File}} and the second using the {{slapd-config}}(5)
+format ({{SECT:Configuring slapd}}).
+
+The default access control policy is allow read by all clients. Regardless of
+what access control policy is defined, the {{rootdn}} is always allowed full
+rights (i.e. auth, search, compare, read and write) on everything and anything.
+
+As a consequence, it's useless (and results in a performance penalty) to explicitly
+list the {{rootdn}} among the {{<by>}} clauses.
+
+The following sections will describe Access Control Lists in greater depth and
+follow with some examples and recommendations. See {{slapd.access}}(5) for
+complete details.
+
+H2: Access Control via Static Configuration
+
+Access to entries and attributes is controlled by the
+access configuration file directive. The general form of an
+access line is:
+
+> <access directive> ::= access to <what>
+> [by <who> [<access>] [<control>] ]+
+> <what> ::= * |
+> [dn[.<basic-style>]=<regex> | dn.<scope-style>=<DN>]
+> [filter=<ldapfilter>] [attrs=<attrlist>]
+> <basic-style> ::= regex | exact
+> <scope-style> ::= base | one | subtree | children
+> <attrlist> ::= <attr> [val[.<basic-style>]=<regex>] | <attr> , <attrlist>
+> <attr> ::= <attrname> | entry | children
+> <who> ::= * | [anonymous | users | self
+> | dn[.<basic-style>]=<regex> | dn.<scope-style>=<DN>]
+> [dnattr=<attrname>]
+> [group[/<objectclass>[/<attrname>][.<basic-style>]]=<regex>]
+> [peername[.<basic-style>]=<regex>]
+> [sockname[.<basic-style>]=<regex>]
+> [domain[.<basic-style>]=<regex>]
+> [sockurl[.<basic-style>]=<regex>]
+> [set=<setspec>]
+> [aci=<attrname>]
+> <access> ::= [self]{<level>|<priv>}
+> <level> ::= none | disclose | auth | compare | search | read | write | manage
+> <priv> ::= {=|+|-}{m|w|r|s|c|x|d|0}+
+> <control> ::= [stop | continue | break]
+
+where the <what> part selects the entries and/or attributes to which
+the access applies, the {{EX:<who>}} part specifies which entities
+are granted access, and the {{EX:<access>}} part specifies the
+access granted. Multiple {{EX:<who> <access> <control>}} triplets
+are supported, allowing many entities to be granted different access
+to the same set of entries and attributes. Not all of these access
+control options are described here; for more details see the
+{{slapd.access}}(5) man page.
+
+
+H3: What to control access to
+
+The <what> part of an access specification determines the entries
+and attributes to which the access control applies. Entries are
+commonly selected in two ways: by DN and by filter. The following
+qualifiers select entries by DN:
+
+> to *
+> to dn[.<basic-style>]=<regex>
+> to dn.<scope-style>=<DN>
+
+The first form is used to select all entries. The second form may
+be used to select entries by matching a regular expression against
+the target entry's {{normalized DN}}. (The second form is not
+discussed further in this document.) The third form is used to
+select entries which are within the requested scope of DN. The
+<DN> is a string representation of the Distinguished Name, as
+described in {{REF:RFC4514}}.
+
+The scope can be either {{EX:base}}, {{EX:one}}, {{EX:subtree}},
+or {{EX:children}}. Where {{EX:base}} matches only the entry with
+provided DN, {{EX:one}} matches the entries whose parent is the
+provided DN, {{EX:subtree}} matches all entries in the subtree whose
+root is the provided DN, and {{EX:children}} matches all entries
+under the DN (but not the entry named by the DN).
+
+For example, if the directory contained entries named:
+
+> 0: o=suffix
+> 1: cn=Manager,o=suffix
+> 2: ou=people,o=suffix
+> 3: uid=kdz,ou=people,o=suffix
+> 4: cn=addresses,uid=kdz,ou=people,o=suffix
+> 5: uid=hyc,ou=people,o=suffix
+
+\Then:
+. {{EX:dn.base="ou=people,o=suffix"}} match 2;
+. {{EX:dn.one="ou=people,o=suffix"}} match 3, and 5;
+. {{EX:dn.subtree="ou=people,o=suffix"}} match 2, 3, 4, and 5; and
+. {{EX:dn.children="ou=people,o=suffix"}} match 3, 4, and 5.
+
+
+Entries may also be selected using a filter:
+
+> to filter=<ldap filter>
+
+where <ldap filter> is a string representation of an LDAP
+search filter, as described in {{REF:RFC4515}}. For example:
+
+> to filter=(objectClass=person)
+
+Note that entries may be selected by both DN and filter by
+including both qualifiers in the <what> clause.
+
+> to dn.one="ou=people,o=suffix" filter=(objectClass=person)
+
+Attributes within an entry are selected by including a comma-separated
+list of attribute names in the <what> selector:
+
+> attrs=<attribute list>
+
+A specific value of an attribute is selected by using a single
+attribute name and also using a value selector:
+
+> attrs=<attribute> val[.<style>]=<regex>
+
+There are two special {{pseudo}} attributes {{EX:entry}} and
+{{EX:children}}. To read (and hence return) a target entry, the
+subject must have {{EX:read}} access to the target's {{entry}}
+attribute. To perform a search, the subject must have
+{{EX:search}} access to the search base's {{entry}} attribute.
+To add or delete an entry, the subject must have
+{{EX:write}} access to the entry's {{EX:entry}} attribute AND must
+have {{EX:write}} access to the entry's parent's {{EX:children}}
+attribute. To rename an entry, the subject must have {{EX:write}}
+access to entry's {{EX:entry}} attribute AND have {{EX:write}}
+access to both the old parent's and new parent's {{EX:children}}
+attributes. The complete examples at the end of this section should
+help clear things up.
+
+Lastly, there is a special entry selector {{EX:"*"}} that is used to
+select any entry. It is used when no other {{EX:<what>}}
+selector has been provided. It's equivalent to "{{EX:dn=.*}}"
+
+
+H3: Who to grant access to
+
+The <who> part identifies the entity or entities being granted
+access. Note that access is granted to "entities" not "entries."
+The following table summarizes entity specifiers:
+
+!block table; align=Center; coltags="EX,N"; \
+ title="Table 6.3: Access Entity Specifiers"
+Specifier|Entities
+*|All, including anonymous and authenticated users
+anonymous|Anonymous (non-authenticated) users
+users|Authenticated users
+self|User associated with target entry
+dn[.<basic-style>]=<regex>|Users matching a regular expression
+dn.<scope-style>=<DN>|Users within scope of a DN
+!endblock
+
+The DN specifier behaves much like <what> clause DN specifiers.
+
+Other control factors are also supported. For example, a {{EX:<who>}}
+can be restricted by an entry listed in a DN-valued attribute in
+the entry to which the access applies:
+
+> dnattr=<dn-valued attribute name>
+
+The dnattr specification is used to give access to an entry
+whose DN is listed in an attribute of the entry (e.g., give
+access to a group entry to whoever is listed as the owner of
+the group entry).
+
+Some factors may not be appropriate in all environments (or any).
+For example, the domain factor relies on IP to domain name lookups.
+As these can easily be spoofed, the domain factor should be avoided.
+
+
+H3: The access to grant
+
+The kind of <access> granted can be one of the following:
+
+!block table; colaligns="LRL"; coltags="EX,EX,N"; align=Center; \
+ title="Table 6.4: Access Levels"
+Level Privileges Description
+none =0 no access
+disclose =d needed for information disclosure on error
+auth =dx needed to authenticate (bind)
+compare =cdx needed to compare
+search =scdx needed to apply search filters
+read =rscdx needed to read search results
+write =wrscdx needed to modify/rename
+manage =mwrscdx needed to manage
+!endblock
+
+Each level implies all lower levels of access. So, for example,
+granting someone {{EX:write}} access to an entry also grants them
+{{EX:read}}, {{EX:search}}, {{EX:compare}}, {{EX:auth}} and
+{{EX:disclose}} access. However, one may use the privileges specifier
+to grant specific permissions.
+
+
+H3: Access Control Evaluation
+
+When evaluating whether some requester should be given access to
+an entry and/or attribute, slapd compares the entry and/or attribute
+to the {{EX:<what>}} selectors given in the configuration file.
+For each entry, access controls provided in the database which holds
+the entry (or the global access directives if not held in any database) apply
+first, followed by the global access directives. However, when dealing with
+an access list, because the global access list is effectively appended
+to each per-database list, if the resulting list is non-empty then the
+access list will end with an implicit {{EX:access to * by * none}} directive.
+If there are no access directives applicable to a backend, then a default
+read is used.
+
+Within this
+priority, access directives are examined in the order in which they
+appear in the config file. Slapd stops with the first {{EX:<what>}}
+selector that matches the entry and/or attribute. The corresponding
+access directive is the one slapd will use to evaluate access.
+
+Next, slapd compares the entity requesting access to the {{EX:<who>}}
+selectors within the access directive selected above in the order
+in which they appear. It stops with the first {{EX:<who>}} selector
+that matches the requester. This determines the access the entity
+requesting access has to the entry and/or attribute.
+
+Finally, slapd compares the access granted in the selected
+{{EX:<access>}} clause to the access requested by the client. If
+it allows greater or equal access, access is granted. Otherwise,
+access is denied.
+
+The order of evaluation of access directives makes their placement
+in the configuration file important. If one access directive is
+more specific than another in terms of the entries it selects, it
+should appear first in the config file. Similarly, if one {{EX:<who>}}
+selector is more specific than another it should come first in the
+access directive. The access control examples given below should
+help make this clear.
+
+
+
+H3: Access Control Examples
+
+The access control facility described above is quite powerful. This
+section shows some examples of its use for descriptive purposes.
+
+A simple example:
+
+> access to * by * read
+
+This access directive grants read access to everyone.
+
+> access to *
+> by self write
+> by anonymous auth
+> by * read
+
+This directive allows the user to modify their entry, allows anonymous
+to authenticate against these entries, and allows all others to
+read these entries. Note that only the first {{EX:by <who>}} clause
+which matches applies. Hence, the anonymous users are granted
+{{EX:auth}}, not {{EX:read}}. The last clause could just as well
+have been "{{EX:by users read}}".
+
+It is often desirable to restrict operations based upon the level
+of protection in place. The following shows how security strength
+factors (SSF) can be used.
+
+> access to *
+> by ssf=128 self write
+> by ssf=64 anonymous auth
+> by ssf=64 users read
+
+This directive allows users to modify their own entries if security
+protections have of strength 128 or better have been established,
+allows authentication access to anonymous users, and read access
+when 64 or better security protections have been established. If
+client has not establish sufficient security protections, the
+implicit {{EX:by * none}} clause would be applied.
+
+The following example shows the use of a style specifiers to select
+the entries by DN in two access directives where ordering is
+significant.
+
+> access to dn.children="dc=example,dc=com"
+> by * search
+> access to dn.children="dc=com"
+> by * read
+
+Read access is granted to entries under the {{EX:dc=com}} subtree,
+except for those entries under the {{EX:dc=example,dc=com}} subtree,
+to which search access is granted. No access is granted to
+{{EX:dc=com}} as neither access directive matches this DN. If the
+order of these access directives was reversed, the trailing directive
+would never be reached, since all entries under {{EX:dc=example,dc=com}}
+are also under {{EX:dc=com}} entries.
+
+Also note that if no {{EX:access to}} directive matches or no {{EX:by
+<who>}} clause, {{B:access is denied}}. That is, every {{EX:access
+to}} directive ends with an implicit {{EX:by * none}} clause. When dealing
+with an access list, because the global access list is effectively appended
+to each per-database list, if the resulting list is non-empty then the access
+list will end with an implicit {{EX:access to * by * none}} directive. If
+there are no access directives applicable to a backend, then a default read is
+used.
+
+The next example again shows the importance of ordering, both of
+the access directives and the {{EX:by <who>}} clauses. It also
+shows the use of an attribute selector to grant access to a specific
+attribute and various {{EX:<who>}} selectors.
+
+> access to dn.subtree="dc=example,dc=com" attrs=homePhone
+> by self write
+> by dn.children="dc=example,dc=com" search
+> by peername.regex=IP=10\..+ read
+> access to dn.subtree="dc=example,dc=com"
+> by self write
+> by dn.children="dc=example,dc=com" search
+> by anonymous auth
+
+This example applies to entries in the "{{EX:dc=example,dc=com}}"
+subtree. To all attributes except {{EX:homePhone}}, an entry can
+write to itself, entries under {{EX:example.com}} entries can search
+by them, anybody else has no access (implicit {{EX:by * none}})
+excepting for authentication/authorization (which is always done
+anonymously). The {{EX:homePhone}} attribute is writable by the
+entry, searchable by entries under {{EX:example.com}}, readable by
+clients connecting from network 10, and otherwise not readable
+(implicit {{EX:by * none}}). All other access is denied by the
+implicit {{EX:access to * by * none}}.
+
+Sometimes it is useful to permit a particular DN to add or
+remove itself from an attribute. For example, if you would like to
+create a group and allow people to add and remove only
+their own DN from the member attribute, you could accomplish
+it with an access directive like this:
+
+> access to attrs=member,entry
+> by dnattr=member selfwrite
+
+The dnattr {{EX:<who>}} selector says that the access applies to
+entries listed in the {{EX:member}} attribute. The {{EX:selfwrite}} access
+selector says that such members can only add or delete their
+own DN from the attribute, not other values. The addition of
+the entry attribute is required because access to the entry is
+required to access any of the entry's attributes.
+
+!if 0
+For more details on how to use the {{EX:access}} directive,
+consult the {{Advanced Access Control}} chapter.
+!endif
+
+
+H2: Access Control via Dynamic Configuration
+
+Access to slapd entries and attributes is controlled by the
+olcAccess attribute, whose values are a sequence of access directives.
+The general form of the olcAccess configuration is:
+
+> olcAccess: <access directive>
+> <access directive> ::= to <what>
+> [by <who> [<access>] [<control>] ]+
+> <what> ::= * |
+> [dn[.<basic-style>]=<regex> | dn.<scope-style>=<DN>]
+> [filter=<ldapfilter>] [attrs=<attrlist>]
+> <basic-style> ::= regex | exact
+> <scope-style> ::= base | one | subtree | children
+> <attrlist> ::= <attr> [val[.<basic-style>]=<regex>] | <attr> , <attrlist>
+> <attr> ::= <attrname> | entry | children
+> <who> ::= * | [anonymous | users | self
+> | dn[.<basic-style>]=<regex> | dn.<scope-style>=<DN>]
+> [dnattr=<attrname>]
+> [group[/<objectclass>[/<attrname>][.<basic-style>]]=<regex>]
+> [peername[.<basic-style>]=<regex>]
+> [sockname[.<basic-style>]=<regex>]
+> [domain[.<basic-style>]=<regex>]
+> [sockurl[.<basic-style>]=<regex>]
+> [set=<setspec>]
+> [aci=<attrname>]
+> <access> ::= [self]{<level>|<priv>}
+> <level> ::= none | disclose | auth | compare | search | read | write | manage
+> <priv> ::= {=|+|-}{m|w|r|s|c|x|d|0}+
+> <control> ::= [stop | continue | break]
+
+where the <what> part selects the entries and/or attributes to which
+the access applies, the {{EX:<who>}} part specifies which entities
+are granted access, and the {{EX:<access>}} part specifies the
+access granted. Multiple {{EX:<who> <access> <control>}} triplets
+are supported, allowing many entities to be granted different access
+to the same set of entries and attributes. Not all of these access
+control options are described here; for more details see the
+{{slapd.access}}(5) man page.
+
+
+H3: What to control access to
+
+The <what> part of an access specification determines the entries
+and attributes to which the access control applies. Entries are
+commonly selected in two ways: by DN and by filter. The following
+qualifiers select entries by DN:
+
+> to *
+> to dn[.<basic-style>]=<regex>
+> to dn.<scope-style>=<DN>
+
+The first form is used to select all entries. The second form may
+be used to select entries by matching a regular expression against
+the target entry's {{normalized DN}}. (The second form is not
+discussed further in this document.) The third form is used to
+select entries which are within the requested scope of DN. The
+<DN> is a string representation of the Distinguished Name, as
+described in {{REF:RFC4514}}.
+
+The scope can be either {{EX:base}}, {{EX:one}}, {{EX:subtree}},
+or {{EX:children}}. Where {{EX:base}} matches only the entry with
+provided DN, {{EX:one}} matches the entries whose parent is the
+provided DN, {{EX:subtree}} matches all entries in the subtree whose
+root is the provided DN, and {{EX:children}} matches all entries
+under the DN (but not the entry named by the DN).
+
+For example, if the directory contained entries named:
+
+> 0: o=suffix
+> 1: cn=Manager,o=suffix
+> 2: ou=people,o=suffix
+> 3: uid=kdz,ou=people,o=suffix
+> 4: cn=addresses,uid=kdz,ou=people,o=suffix
+> 5: uid=hyc,ou=people,o=suffix
+
+\Then:
+. {{EX:dn.base="ou=people,o=suffix"}} match 2;
+. {{EX:dn.one="ou=people,o=suffix"}} match 3, and 5;
+. {{EX:dn.subtree="ou=people,o=suffix"}} match 2, 3, 4, and 5; and
+. {{EX:dn.children="ou=people,o=suffix"}} match 3, 4, and 5.
+
+
+Entries may also be selected using a filter:
+
+> to filter=<ldap filter>
+
+where <ldap filter> is a string representation of an LDAP
+search filter, as described in {{REF:RFC4515}}. For example:
+
+> to filter=(objectClass=person)
+
+Note that entries may be selected by both DN and filter by
+including both qualifiers in the <what> clause.
+
+> to dn.one="ou=people,o=suffix" filter=(objectClass=person)
+
+Attributes within an entry are selected by including a comma-separated
+list of attribute names in the <what> selector:
+
+> attrs=<attribute list>
+
+A specific value of an attribute is selected by using a single
+attribute name and also using a value selector:
+
+> attrs=<attribute> val[.<style>]=<regex>
+
+There are two special {{pseudo}} attributes {{EX:entry}} and
+{{EX:children}}. To read (and hence return) a target entry, the
+subject must have {{EX:read}} access to the target's {{entry}}
+attribute. To perform a search, the subject must have
+{{EX:search}} access to the search base's {{entry}} attribute.
+To add or delete an entry, the subject must have
+{{EX:write}} access to the entry's {{EX:entry}} attribute AND must
+have {{EX:write}} access to the entry's parent's {{EX:children}}
+attribute. To rename an entry, the subject must have {{EX:write}}
+access to entry's {{EX:entry}} attribute AND have {{EX:write}}
+access to both the old parent's and new parent's {{EX:children}}
+attributes. The complete examples at the end of this section should
+help clear things up.
+
+Lastly, there is a special entry selector {{EX:"*"}} that is used to
+select any entry. It is used when no other {{EX:<what>}}
+selector has been provided. It's equivalent to "{{EX:dn=.*}}"
+
+
+H3: Who to grant access to
+
+The <who> part identifies the entity or entities being granted
+access. Note that access is granted to "entities" not "entries."
+The following table summarizes entity specifiers:
+
+!block table; align=Center; coltags="EX,N"; \
+ title="Table 5.3: Access Entity Specifiers"
+Specifier|Entities
+*|All, including anonymous and authenticated users
+anonymous|Anonymous (non-authenticated) users
+users|Authenticated users
+self|User associated with target entry
+dn[.<basic-style>]=<regex>|Users matching a regular expression
+dn.<scope-style>=<DN>|Users within scope of a DN
+!endblock
+
+The DN specifier behaves much like <what> clause DN specifiers.
+
+Other control factors are also supported. For example, a {{EX:<who>}}
+can be restricted by an entry listed in a DN-valued attribute in
+the entry to which the access applies:
+
+> dnattr=<dn-valued attribute name>
+
+The dnattr specification is used to give access to an entry
+whose DN is listed in an attribute of the entry (e.g., give
+access to a group entry to whoever is listed as the owner of
+the group entry).
+
+Some factors may not be appropriate in all environments (or any).
+For example, the domain factor relies on IP to domain name lookups.
+As these can easily be spoofed, the domain factor should be avoided.
+
+
+H3: The access to grant
+
+The kind of <access> granted can be one of the following:
+
+!block table; colaligns="LRL"; coltags="EX,EX,N"; align=Center; \
+ title="Table 5.4: Access Levels"
+Level Privileges Description
+none =0 no access
+disclose =d needed for information disclosure on error
+auth =dx needed to authenticate (bind)
+compare =cdx needed to compare
+search =scdx needed to apply search filters
+read =rscdx needed to read search results
+write =wrscdx needed to modify/rename
+manage =mwrscdx needed to manage
+!endblock
+
+Each level implies all lower levels of access. So, for example,
+granting someone {{EX:write}} access to an entry also grants them
+{{EX:read}}, {{EX:search}}, {{EX:compare}}, {{EX:auth}} and
+{{EX:disclose}} access. However, one may use the privileges specifier
+to grant specific permissions.
+
+
+H3: Access Control Evaluation
+
+When evaluating whether some requester should be given access to
+an entry and/or attribute, slapd compares the entry and/or attribute
+to the {{EX:<what>}} selectors given in the configuration. For
+each entry, access controls provided in the database which holds
+the entry (or the global access directives if not held in any database) apply
+first, followed by the global access directives (which are held in
+the {{EX:frontend}} database definition). However, when dealing with
+an access list, because the global access list is effectively appended
+to each per-database list, if the resulting list is non-empty then the
+access list will end with an implicit {{EX:access to * by * none}} directive.
+If there are no access directives applicable to a backend, then a default
+read is used.
+
+Within this priority,
+access directives are examined in the order in which they appear
+in the configuration attribute. Slapd stops with the first
+{{EX:<what>}} selector that matches the entry and/or attribute. The
+corresponding access directive is the one slapd will use to evaluate
+access.
+
+Next, slapd compares the entity requesting access to the {{EX:<who>}}
+selectors within the access directive selected above in the order
+in which they appear. It stops with the first {{EX:<who>}} selector
+that matches the requester. This determines the access the entity
+requesting access has to the entry and/or attribute.
+
+Finally, slapd compares the access granted in the selected
+{{EX:<access>}} clause to the access requested by the client. If
+it allows greater or equal access, access is granted. Otherwise,
+access is denied.
+
+The order of evaluation of access directives makes their placement
+in the configuration file important. If one access directive is
+more specific than another in terms of the entries it selects, it
+should appear first in the configuration. Similarly, if one {{EX:<who>}}
+selector is more specific than another it should come first in the
+access directive. The access control examples given below should
+help make this clear.
+
+
+
+H3: Access Control Examples
+
+The access control facility described above is quite powerful. This
+section shows some examples of its use for descriptive purposes.
+
+A simple example:
+
+> olcAccess: to * by * read
+
+This access directive grants read access to everyone.
+
+> olcAccess: to *
+> by self write
+> by anonymous auth
+> by * read
+
+This directive allows the user to modify their entry, allows anonymous
+to authenticate against these entries, and allows all others to
+read these entries. Note that only the first {{EX:by <who>}} clause
+which matches applies. Hence, the anonymous users are granted
+{{EX:auth}}, not {{EX:read}}. The last clause could just as well
+have been "{{EX:by users read}}".
+
+It is often desirable to restrict operations based upon the level
+of protection in place. The following shows how security strength
+factors (SSF) can be used.
+
+> olcAccess: to *
+> by ssf=128 self write
+> by ssf=64 anonymous auth
+> by ssf=64 users read
+
+This directive allows users to modify their own entries if security
+protections of strength 128 or better have been established,
+allows authentication access to anonymous users, and read access
+when strength 64 or better security protections have been established. If
+the client has not establish sufficient security protections, the
+implicit {{EX:by * none}} clause would be applied.
+
+The following example shows the use of style specifiers to select
+the entries by DN in two access directives where ordering is
+significant.
+
+> olcAccess: to dn.children="dc=example,dc=com"
+> by * search
+> olcAccess: to dn.children="dc=com"
+> by * read
+
+Read access is granted to entries under the {{EX:dc=com}} subtree,
+except for those entries under the {{EX:dc=example,dc=com}} subtree,
+to which search access is granted. No access is granted to
+{{EX:dc=com}} as neither access directive matches this DN. If the
+order of these access directives was reversed, the trailing directive
+would never be reached, since all entries under {{EX:dc=example,dc=com}}
+are also under {{EX:dc=com}} entries.
+
+Also note that if no {{EX:olcAccess: to}} directive matches or no {{EX:by
+<who>}} clause, {{B:access is denied}}. When dealing with an access list,
+because the global access list is effectively appended to each per-database
+list, if the resulting list is non-empty then the access list will end with
+an implicit {{EX:access to * by * none}} directive. If there are no access
+directives applicable to a backend, then a default read is used.
+
+The next example again shows the importance of ordering, both of
+the access directives and the {{EX:by <who>}} clauses. It also
+shows the use of an attribute selector to grant access to a specific
+attribute and various {{EX:<who>}} selectors.
+
+> olcAccess: to dn.subtree="dc=example,dc=com" attrs=homePhone
+> by self write
+> by dn.children=dc=example,dc=com" search
+> by peername.regex=IP=10\..+ read
+> olcAccess: to dn.subtree="dc=example,dc=com"
+> by self write
+> by dn.children="dc=example,dc=com" search
+> by anonymous auth
+
+This example applies to entries in the "{{EX:dc=example,dc=com}}"
+subtree. To all attributes except {{EX:homePhone}}, an entry can
+write to itself, entries under {{EX:example.com}} entries can search
+by them, anybody else has no access (implicit {{EX:by * none}})
+excepting for authentication/authorization (which is always done
+anonymously). The {{EX:homePhone}} attribute is writable by the
+entry, searchable by entries under {{EX:example.com}}, readable by
+clients connecting from network 10, and otherwise not readable
+(implicit {{EX:by * none}}). All other access is denied by the
+implicit {{EX:access to * by * none}}.
+
+Sometimes it is useful to permit a particular DN to add or
+remove itself from an attribute. For example, if you would like to
+create a group and allow people to add and remove only
+their own DN from the member attribute, you could accomplish
+it with an access directive like this:
+
+> olcAccess: to attrs=member,entry
+> by dnattr=member selfwrite
+
+The dnattr {{EX:<who>}} selector says that the access applies to
+entries listed in the {{EX:member}} attribute. The {{EX:selfwrite}} access
+selector says that such members can only add or delete their
+own DN from the attribute, not other values. The addition of
+the entry attribute is required because access to the entry is
+required to access any of the entry's attributes.
+
+
+
+H3: Access Control Ordering
+
+Since the ordering of {{EX:olcAccess}} directives is essential to their
+proper evaluation, but LDAP attributes normally do not preserve the
+ordering of their values, OpenLDAP uses a custom schema extension to
+maintain a fixed ordering of these values. This ordering is maintained
+by prepending a {{EX:"{X}"}} numeric index to each value, similarly to
+the approach used for ordering the configuration entries. These index
+tags are maintained automatically by slapd and do not need to be specified
+when originally defining the values. For example, when you create the
+settings
+
+> olcAccess: to attrs=member,entry
+> by dnattr=member selfwrite
+> olcAccess: to dn.children="dc=example,dc=com"
+> by * search
+> olcAccess: to dn.children="dc=com"
+> by * read
+
+when you read them back using slapcat or ldapsearch they will contain
+
+> olcAccess: {0}to attrs=member,entry
+> by dnattr=member selfwrite
+> olcAccess: {1}to dn.children="dc=example,dc=com"
+> by * search
+> olcAccess: {2}to dn.children="dc=com"
+> by * read
+
+The numeric index may be used to specify a particular value to change
+when using ldapmodify to edit the access rules. This index can be used
+instead of (or in addition to) the actual access value. Using this
+numeric index is very helpful when multiple access rules are being managed.
+
+For example, if we needed to change the second rule above to grant
+write access instead of search, we could try this LDIF:
+
+> changetype: modify
+> delete: olcAccess
+> olcAccess: to dn.children="dc=example,dc=com" by * search
+> -
+> add: olcAccess
+> olcAccess: to dn.children="dc=example,dc=com" by * write
+> -
+
+But this example {{B:will not}} guarantee that the existing values remain in
+their original order, so it will most likely yield a broken security
+configuration. Instead, the numeric index should be used:
+
+> changetype: modify
+> delete: olcAccess
+> olcAccess: {1}
+> -
+> add: olcAccess
+> olcAccess: {1}to dn.children="dc=example,dc=com" by * write
+> -
+
+This example deletes whatever rule is in value #1 of the {{EX:olcAccess}}
+attribute (regardless of its value) and adds a new value that is
+explicitly inserted as value #1. The result will be
+
+> olcAccess: {0}to attrs=member,entry
+> by dnattr=member selfwrite
+> olcAccess: {1}to dn.children="dc=example,dc=com"
+> by * write
+> olcAccess: {2}to dn.children="dc=com"
+> by * read
+
+which is exactly what was intended.
+
+!if 0
+For more details on how to use the {{EX:access}} directive,
+consult the {{Advanced Access Control}} chapter.
+!endif
+
+
+H2: Access Control Common Examples
+
+H3: Basic ACLs
+
+Generally one should start with some basic ACLs such as:
+
+> access to attrs=userPassword
+> by self =xw
+> by anonymous auth
+> by * none
+>
+>
+> access to *
+> by self write
+> by users read
+> by * none
+
+The first ACL allows users to update (but not read) their passwords, anonymous
+users to authenticate against this attribute, and (implicitly) denying all
+access to others.
+
+The second ACL allows users full access to their entry, authenticated users read
+access to anything, and (implicitly) denying all access to others (in this case,
+anonymous users).
+
+
+H3: Matching Anonymous and Authenticated users
+
+An anonymous user has a empty DN. While the {{dn.exact=""}} or {{dn.regex="^$"}}
+ could be used, {{slapd}}(8)) offers an anonymous shorthand which should be
+used instead.
+
+> access to *
+> by anonymous none
+> by * read
+
+denies all access to anonymous users while granting others read.
+
+Authenticated users have a subject DN. While {{dn.regex=".+"}} will match any
+authenticated user, OpenLDAP provides the users short hand which should be used
+instead.
+
+> access to *
+> by users read
+> by * none
+
+This ACL grants read permissions to authenticated users while denying others
+(i.e.: anonymous users).
+
+
+H3: Controlling rootdn access
+
+You could specify the {{rootdn}} in {{slapd.conf}}(5) or {{slapd.d}} without
+specifying a {{rootpw}}. Then you have to add an actual directory entry with
+the same dn, e.g.:
+
+> dn: cn=Manager,o=MyOrganization
+> cn: Manager
+> sn: Manager
+> objectClass: person
+> objectClass: top
+> userPassword: {SSHA}someSSHAdata
+
+Then binding as the {{rootdn}} will require a regular bind to that DN, which
+in turn requires auth access to that entry's DN and {{userPassword}}, and this
+can be restricted via ACLs. E.g.:
+
+> access to dn.base="cn=Manager,o=MyOrganization"
+> by peername.regex=127\.0\.0\.1 auth
+> by peername.regex=192\.168\.0\..* auth
+> by users none
+> by * none
+
+The ACLs above will only allow binding using rootdn from localhost and
+192.168.0.0/24.
+
+
+H3: Managing access with Groups
+
+There are a few ways to do this. One approach is illustrated here. Consider the
+following DIT layout:
+
+> +-dc=example,dc=com
+> +---cn=administrators,dc=example,dc=com
+> +---cn=fred blogs,dc=example,dc=com
+
+and the following group object (in LDIF format):
+
+> dn: cn=administrators,dc=example,dc=com
+> cn: administrators of this region
+> objectclass: groupOfNames (important for the group acl feature)
+> member: cn=fred blogs,dc=example,dc=com
+> member: cn=somebody else,dc=example,dc=com
+
+One can then grant access to the members of this this group by adding appropriate
+{{by group}} clause to an access directive in {{slapd.conf}}(5). For instance,
+
+> access to dn.children="dc=example,dc=com"
+> by self write
+> by group.exact="cn=Administrators,dc=example,dc=com" write
+> by * auth
+
+Like by {{dn}} clauses, one can also use {{expand}} to expand the group name
+based upon the regular expression matching of the target, that is, the to {{dn.regex}}).
+For instance,
+
+> access to dn.regex="(.+,)?ou=People,(dc=[^,]+,dc=[^,]+)$"
+> attrs=children,entry,uid
+> by group.expand="cn=Managers,$2" write
+> by users read
+> by * auth
+
+
+The above illustration assumed that the group members are to be found in the
+{{member}} attribute type of the {{groupOfNames}} object class. If you need to
+use a different group object and/or a different attribute type then use the
+following {{slapd.conf}}(5) (abbreviated) syntax:
+
+> access to <what>
+> by group/<objectclass>/<attributename>=<DN> <access>
+
+For example:
+
+> access to *
+> by group/organizationalRole/roleOccupant="cn=Administrator,dc=example,dc=com" write
+
+In this case, we have an ObjectClass {{organizationalRole}} which contains the
+administrator DN's in the {{roleOccupant}} attribute. For instance:
+
+> dn: cn=Administrator,dc=example,dc=com
+> cn: Administrator
+> objectclass: organizationalRole
+> roleOccupant: cn=Jane Doe,dc=example,dc=com
+
+Note: the specified member attribute type MUST be of DN or {{NameAndOptionalUID}} syntax,
+and the specified object class SHOULD allow the attribute type.
+
+Dynamic Groups are also supported in Access Control. Please see {{slapo-dynlist}}(5)
+and the {{SECT:Dynamic Lists}} overlay section.
+
+
+H3: Granting access to a subset of attributes
+
+You can grant access to a set of attributes by specifying a list of attribute names
+in the ACL {{to}} clause. To be useful, you also need to grant access to the
+{{entry}} itself. Also note how {{children}} controls the ability to add, delete,
+and rename entries.
+
+> # mail: self may write, authenticated users may read
+> access to attrs=mail
+> by self write
+> by users read
+> by * none
+>
+> # cn, sn: self my write, all may read
+> access to attrs=cn,sn
+> by self write
+> by * read
+>
+> # immediate children: only self can add/delete entries under this entry
+> access to attrs=children
+> by self write
+>
+> # entry itself: self may write, all may read
+> access to attrs=entry
+> by self write
+> by * read
+>
+> # other attributes: self may write, others have no access
+> access to *
+> by self write
+> by * none
+
+ObjectClass names may also be specified in this list, which will affect
+all the attributes that are required and/or allowed by that {{objectClass}}.
+Actually, names in {{attrlist}} that are prefixed by {{@}} are directly treated
+as objectClass names. A name prefixed by {{!}} is also treated as an objectClass,
+but in this case the access rule affects the attributes that are not required
+nor allowed by that {{objectClass}}.
+
+
+H3: Allowing a user write to all entries below theirs
+
+For a setup where a user can write to its own record and to all of its children:
+
+> access to dn.regex="(.+,)?(uid=[^,]+,o=Company)$"
+> by dn.exact,expand="$2" write
+> by anonymous auth
+
+(Add more examples for above)
+
+
+H3: Allowing entry creation
+
+Let's say, you have it like this:
+
+> o=<basedn>
+> ou=domains
+> associatedDomain=<somedomain>
+> ou=users
+> uid=<someuserid>
+> uid=<someotheruserid>
+> ou=addressbooks
+> uid=<someuserid>
+> cn=<someone>
+> cn=<someoneelse>
+
+and, for another domain <someotherdomain>:
+
+> o=<basedn>
+> ou=domains
+> associatedDomain=<someotherdomain>
+> ou=users
+> uid=<someuserid>
+> uid=<someotheruserid>
+> ou=addressbooks
+> uid=<someotheruserid>
+> cn=<someone>
+> cn=<someoneelse>
+
+then, if you wanted user {{uid=<someuserid>}} to {{B:ONLY}} create an entry
+for its own thing, you could write an ACL like this:
+
+> # this rule lets users of "associatedDomain=<matcheddomain>"
+> # write under "ou=addressbook,associatedDomain=<matcheddomain>,ou=domains,o=<basedn>",
+> # i.e. a user can write ANY entry below its domain's address book;
+> # this permission is necessary, but not sufficient, the next
+> # will restrict this permission further
+>
+>
+> access to dn.regex="^ou=addressbook,associatedDomain=([^,]+),ou=domains,o=<basedn>$" attrs=children
+> by dn.regex="^uid=([^,]+),ou=users,associatedDomain=$1,ou=domains,o=<basedn>$$" write
+> by * none
+>
+>
+> # Note that above the "by" clause needs a "regex" style to make sure
+> # it expands to a DN that starts with a "uid=<someuserid>" pattern
+> # while substituting the associatedDomain submatch from the "what" clause.
+>
+>
+> # This rule lets a user with "uid=<matcheduid>" of "<associatedDomain=matcheddomain>"
+> # write (i.e. add, modify, delete) the entry whose DN is exactly
+> # "uid=<matcheduid>,ou=addressbook,associatedDomain=<matcheddomain>,ou=domains,o=<basedn>"
+> # and ANY entry as subtree of it
+>
+>
+> access to dn.regex="^(.+,)?uid=([^,]+),ou=addressbook,associatedDomain=([^,]+),ou=domains,o=<basedn>$"
+> by dn.exact,expand="uid=$2,ou=users,associatedDomain=$3,ou=domains,o=<basedn>" write
+> by * none
+>
+>
+> # Note that above the "by" clause uses the "exact" style with the "expand"
+> # modifier because now the whole pattern can be rebuilt by means of the
+> # submatches from the "what" clause, so a "regex" compilation and evaluation
+> # is no longer required.
+
+
+H3: Tips for using regular expressions in Access Control
+
+Always use {{dn.regex=<pattern>}} when you intend to use regular expression
+matching. {{dn=<pattern>}} alone defaults to {{dn.exact<pattern>}}.
+
+Use {{(.+)}} instead of {{(.*)}} when you want at least one char to be matched.
+{{(.*)}} matches the empty string as well.
+
+Don't use regular expressions for matches that can be done otherwise in a safer
+and cheaper manner. Examples:
+
+> dn.regex=".*dc=example,dc=com"
+
+is unsafe and expensive:
+
+ * unsafe because any string containing {{dc=example,dc=com }}will match,
+not only those that end with the desired pattern; use {{.*dc=example,dc=com$}} instead.
+ * unsafe also because it would allow any {{attributeType}} ending with {{dc}}
+ as naming attribute for the first RDN in the string, e.g. a custom attributeType
+{{mydc}} would match as well. If you really need a regular expression that allows
+just {{dc=example,dc=com}} or any of its subtrees, use {{^(.+,)?dc=example,dc=com$}},
+which means: anything to the left of dc=..., if any (the question mark after the
+pattern within brackets), must end with a comma;
+ * expensive because if you don't need submatches, you could use scoping styles, e.g.
+
+> dn.subtree="dc=example,dc=com"
+
+to include {{dc=example,dc=com}} in the matching patterns,
+
+> dn.children="dc=example,dc=com"
+
+to exclude {{dc=example,dc=com}} from the matching patterns, or
+
+> dn.onelevel="dc=example,dc=com"
+
+to allow exactly one sublevel matches only.
+
+Always use {{^}} and {{$}} in regexes, whenever appropriate, because
+{{ou=(.+),ou=(.+),ou=addressbooks,o=basedn}} will match
+{{something=bla,ou=xxx,ou=yyy,ou=addressbooks,o=basedn,ou=addressbooks,o=basedn,dc=some,dc=org}}
+
+Always use {{([^,]+)}} to indicate exactly one RDN, because {{(.+)}} can
+include any number of RDNs; e.g. {{ou=(.+),dc=example,dc=com}} will match
+{{ou=My,o=Org,dc=example,dc=com}}, which might not be what you want.
+
+Never add the rootdn to the by clauses. ACLs are not even processed for operations
+performed with rootdn identity (otherwise there would be no reason to define a
+rootdn at all).
+
+Use shorthands. The user directive matches authenticated users and the anonymous
+directive matches anonymous users.
+
+Don't use the {{dn.regex}} form for <by> clauses if all you need is scoping
+and/or substring replacement; use scoping styles (e.g. {{exact}}, {{onelevel}},
+{{children}} or {{subtree}}) and the style modifier expand to cause substring expansion.
+
+For instance,
+
+> access to dn.regex=".+,dc=([^,]+),dc=([^,]+)$"
+> by dn.regex="^[^,],ou=Admin,dc=$1,dc=$2$$" write
+
+although correct, can be safely and efficiently replaced by
+
+> access to dn.regex=".+,(dc=[^,]+,dc=[^,]+)$"
+> by dn.onelevel,expand="ou=Admin,$1" write
+
+where the regex in the {{<what>}} clause is more compact, and the one in the {{<by>}}
+clause is replaced by a much more efficient scoping style of onelevel with substring expansion.
+
+
+H3: Granting and Denying access based on security strength factors (ssf)
+
+You can restrict access based on the security strength factor (SSF)
+
+> access to dn="cn=example,cn=edu"
+> by * ssf=256 read
+
+0 (zero) implies no protection,
+1 implies integrity protection only,
+56 DES or other weak ciphers,
+112 triple DES and similar ciphers,
+128 RC4, Blowfish and other similar ciphers,
+256 modern ciphers.
+
+Other possibilities:
+
+> transport_ssf=<n>
+> tls_ssf=<n>
+> sasl_ssf=<n>
+
+256 is recommended.
+
+See {{slapd.conf}}(5) for information on {{ssf}}.
+
+
+H3: When things aren't working as expected
+
+Consider this example:
+
+> access to *
+> by anonymous auth
+>
+> access to *
+> by self write
+>
+> access to *
+> by users read
+
+You may think this will allow any user to login, to read everything and change
+his own data if he is logged in. But in this example only the login works and
+an ldapsearch returns no data. The Problem is that SLAPD goes through its access
+config line by line and stops as soon as it finds a match in the part of the
+access rule.(here: {{to *}})
+
+To get what we wanted the file has to read:
+
+> access to *
+> by anonymous auth
+> by self write
+> by users read
+
+The general rule is: "special access rules first, generic access rules last"
+
+See also {{slapd.access}}(5), loglevel 128 and {{slapacl}}(8) for debugging
+information.
+
+
+H2: Sets - Granting rights based on relationships
+
+Sets are best illustrated via examples. The following sections will present
+a few set ACL examples in order to facilitate their understanding.
+
+(Sets in Access Controls FAQ Entry: {{URL:http://www.openldap.org/faq/data/cache/1133.html}})
+
+Note: Sets are considered experimental.
+
+
+H3: Groups of Groups
+
+The OpenLDAP ACL for groups doesn't expand groups within groups, which are
+groups that have another group as a member. For example:
+
+> dn: cn=sudoadm,ou=group,dc=example,dc=com
+> cn: sudoadm
+> objectClass: groupOfNames
+> member: uid=john,ou=people,dc=example,dc=com
+> member: cn=accountadm,ou=group,dc=example,dc=com
+>
+> dn: cn=accountadm,ou=group,dc=example,dc=com
+> cn: accountadm
+> objectClass: groupOfNames
+> member: uid=mary,ou=people,dc=example,dc=com
+
+If we use standard group ACLs with the above entries and allow members of the
+{{F:sudoadm}} group to write somewhere, {{F:mary}} won't be included:
+
+> access to dn.subtree="ou=sudoers,dc=example,dc=com"
+> by group.exact="cn=sudoadm,ou=group,dc=example,dc=com" write
+> by * read
+
+With sets we can make the ACL be recursive and consider group within groups. So
+for each member that is a group, it is further expanded:
+
+> access to dn.subtree="ou=sudoers,dc=example,dc=com"
+> by set="[cn=sudoadm,ou=group,dc=example,dc=com]/member* & user" write
+> by * read
+
+This set ACL means: take the {{F:cn=sudoadm}} DN, check its {{F:member}}
+attribute(s) (where the "{{F:*}}" means recursively) and intersect the result
+with the authenticated user's DN. If the result is non-empty, the ACL is
+considered a match and write access is granted.
+
+The following drawing explains how this set is built:
+!import "set-recursivegroup.png"; align="center"; title="Building a recursive group"
+FT[align="Center"] Figure X.Y: Populating a recursive group set
+
+First we get the {{F:uid=john}} DN. This entry doesn't have a {{F:member}}
+attribute, so the expansion stops here. Now we get to {{F:cn=accountadm}}.
+This one does have a {{F:member}} attribute, which is {{F:uid=mary}}. The
+{{F:uid=mary}} entry, however, doesn't have member, so we stop here again. The
+end comparison is:
+
+> {"uid=john,ou=people,dc=example,dc=com","uid=mary,ou=people,dc=example,dc=com"} & user
+
+If the authenticated user's DN is any one of those two, write access is
+granted. So this set will include {{F:mary}} in the {{F:sudoadm}} group and she
+will be allowed the write access.
+
+H3: Group ACLs without DN syntax
+
+The traditional group ACLs, and even the previous example about recursive groups, require
+that the members are specified as DNs instead of just usernames.
+
+With sets, however, it's also possible to use simple names in group ACLs, as this example will
+show.
+
+Let's say we want to allow members of the {{F:sudoadm}} group to write to the
+{{F:ou=sudoers}} branch of our tree. But our group definition now is using {{F:memberUid}} for
+the group members:
+
+> dn: cn=sudoadm,ou=group,dc=example,dc=com
+> cn: sudoadm
+> objectClass: posixGroup
+> gidNumber: 1000
+> memberUid: john
+
+With this type of group, we can't use group ACLs. But with a set ACL we can
+grant the desired access:
+
+> access to dn.subtree="ou=sudoers,dc=example,dc=com"
+> by set="[cn=sudoadm,ou=group,dc=example,dc=com]/memberUid & user/uid" write
+> by * read
+
+We use a simple intersection where we compare the {{F:uid}} attribute
+of the connecting (and authenticated) user with the {{F:memberUid}} attributes
+of the group. If they match, the intersection is non-empty and the ACL will
+grant write access.
+
+This drawing illustrates this set when the connecting user is authenticated as
+{{F:uid=john,ou=people,dc=example,dc=com}}:
+!import "set-memberUid.png"; align="center"; title="Sets with memberUid"
+FT[align="Center"] Figure X.Y: Sets with {{F:memberUid}}
+
+In this case, it's a match. If it were {{F:mary}} authenticating, however, she
+would be denied write access to {{F:ou=sudoers}} because her {{F:uid}}
+attribute is not listed in the group's {{F:memberUid}}.
+
+H3: Following references
+
+We will now show a quite powerful example of what can be done with sets. This
+example tends to make OpenLDAP administrators smile after they have understood
+it and its implications.
+
+Let's start with an user entry:
+
+> dn: uid=john,ou=people,dc=example,dc=com
+> uid: john
+> objectClass: inetOrgPerson
+> givenName: John
+> sn: Smith
+> cn: john
+> manager: uid=mary,ou=people,dc=example,dc=com
+
+Writing an ACL to allow the manager to update some attributes is quite simple
+using sets:
+
+> access to dn.exact="uid=john,ou=people,dc=example,dc=com"
+> attrs=carLicense,homePhone,mobile,pager,telephoneNumber
+> by self write
+> by set="this/manager & user" write
+> by * read
+
+In that set, {{F:this}} expands to the entry being accessed, so that
+{{F:this/manager}} expands to {{F:uid=mary,ou=people,dc=example,dc=com}} when
+john's entry is accessed. If the manager herself is accessing John's entry,
+the ACL will match and write access to those attributes will be granted.
+
+So far, this same behavior can be obtained with the {{F:dnattr}} keyword. With
+sets, however, we can further enhance this ACL. Let's say we want to allow the
+secretary of the manager to also update these attributes. This is how we do it:
+
+> access to dn.exact="uid=john,ou=people,dc=example,dc=com"
+> attrs=carLicense,homePhone,mobile,pager,telephoneNumber
+> by self write
+> by set="this/manager & user" write
+> by set="this/manager/secretary & user" write
+> by * read
+
+Now we need a picture to help explain what is happening here (entries shortened
+for clarity):
+
+!import "set-following-references.png"; align="center"; title="Sets jumping through entries"
+FT[align="Center"] Figure X.Y: Sets jumping through entries
+
+In this example, Jane is the secretary of Mary, which is the manager of John.
+This whole relationship is defined with the {{F:manager}} and {{F:secretary}}
+attributes, which are both of the distinguishedName syntax (i.e., full DNs).
+So, when the {{F:uid=john}} entry is being accessed, the
+{{F:this/manager/secretary}} set becomes
+{{F:{"uid=jane,ou=people,dc=example,dc=com"}}} (follow the references in the
+picture):
+
+> this = [uid=john,ou=people,dc=example,dc=com]
+> this/manager = \
+> [uid=john,ou=people,dc=example,dc=com]/manager = uid=mary,ou=people,dc=example,dc=com
+> this/manager/secretary = \
+> [uid=mary,ou=people,dc=example,dc=com]/secretary = uid=jane,ou=people,dc=example,dc=com
+
+The end result is that when Jane accesses John's entry, she will be granted
+write access to the specified attributes. Better yet, this will happen to any
+entry she accesses which has Mary as the manager.
+
+This is all cool and nice, but perhaps gives too much power to secretaries. Maybe we need to further
+restrict it. For example, let's only allow executive secretaries to have this power:
+
+> access to dn.exact="uid=john,ou=people,dc=example,dc=com"
+> attrs=carLicense,homePhone,mobile,pager,telephoneNumber
+> by self write
+> by set="this/manager & user" write
+> by set="this/manager/secretary &
+> [cn=executive,ou=group,dc=example,dc=com]/member* &
+> user" write
+> by * read
+
+It's almost the same ACL as before, but we now also require that the connecting user be a member
+of the (possibly nested) {{F:cn=executive}} group.
+
+
diff --git a/doc/guide/admin/admin.sdf b/doc/guide/admin/admin.sdf
new file mode 100644
index 0000000..7bb1985
--- /dev/null
+++ b/doc/guide/admin/admin.sdf
@@ -0,0 +1,11 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+#
+# guide.sdf
+#
+
+!macro build_html_cover
+!endmacro
+
+!include "master.sdf"
diff --git a/doc/guide/admin/allmail-en.png b/doc/guide/admin/allmail-en.png
new file mode 100644
index 0000000..a50a0ff
--- /dev/null
+++ b/doc/guide/admin/allmail-en.png
Binary files differ
diff --git a/doc/guide/admin/allusersgroup-en.png b/doc/guide/admin/allusersgroup-en.png
new file mode 100644
index 0000000..76f1569
--- /dev/null
+++ b/doc/guide/admin/allusersgroup-en.png
Binary files differ
diff --git a/doc/guide/admin/appendix-changes.sdf b/doc/guide/admin/appendix-changes.sdf
new file mode 100644
index 0000000..2638af5
--- /dev/null
+++ b/doc/guide/admin/appendix-changes.sdf
@@ -0,0 +1,74 @@
+# $OpenLDAP$
+# Copyright 2007-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: Changes Since Previous Release
+
+The following sections attempt to summarize the new features and changes in OpenLDAP
+software since the 2.4.x release and the OpenLDAP Admin Guide.
+
+H2: New Guide Sections
+
+In order to make the Admin Guide more thorough and cover the majority of questions
+asked on the OpenLDAP mailing lists and scenarios discussed there, we have added the following new sections:
+
+* {{SECT:When should I use LDAP?}}
+* {{SECT:When should I not use LDAP?}}
+* {{SECT:LDAP vs RDBMS}}
+* {{SECT:Access Control}}
+* {{SECT:Backends}}
+* {{SECT:Overlays}}
+* {{SECT:Replication}}
+* {{SECT:Maintenance}}
+* {{SECT:Monitoring}}
+* {{SECT:Tuning}}
+* {{SECT:Troubleshooting}}
+* {{SECT:Changes Since Previous Release}}
+* {{SECT:Upgrading from 2.4.x}}
+* {{SECT:Common errors encountered when using OpenLDAP Software}}
+* {{SECT:Recommended OpenLDAP Software Dependency Versions}}
+* {{SECT:Real World OpenLDAP Deployments and Examples}}
+* {{SECT:OpenLDAP Software Contributions}}
+* {{SECT:Configuration File Examples}}
+* {{SECT:LDAP Result Codes}}
+* {{SECT:Glossary}}
+
+Also, the table of contents is now 3 levels deep to ease navigation.
+
+
+H2: New Features and Enhancements in 2.5
+
+H3: Better {{B:cn=config}} functionality
+
+H3: Better {{B:cn=schema}} functionality
+
+H3: More sophisticated Syncrepl configurations
+
+H3: Replicating {{slapd}} Configuration (syncrepl and {{B:cn=config}})
+
+H3: More extensive TLS configuration control
+
+H3: Performance enhancements
+
+H3: New overlays
+
+H3: New features in existing Overlays
+
+H3: New features in slapd
+
+H3: New features in libldap
+
+H3: New clients, tools and tool enhancements
+
+H3: New build options
+
+H2: Obsolete Features Removed From 2.5
+
+These features were strongly deprecated in 2.4 and removed in 2.5.
+
+H3: back-bdb and back-hdb
+
+back-bdb and back-hdb were significantly slower than back-mdb and
+required significant tuning of multiple parameters to maximize
+performance. back-mdb requires no tuning and provides all the
+functionality previously provided via back-bdb and back-hdb.
diff --git a/doc/guide/admin/appendix-common-errors.sdf b/doc/guide/admin/appendix-common-errors.sdf
new file mode 100644
index 0000000..1112b5b
--- /dev/null
+++ b/doc/guide/admin/appendix-common-errors.sdf
@@ -0,0 +1,650 @@
+# $OpenLDAP$
+# Copyright 2007-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: Common errors encountered when using OpenLDAP Software
+
+The following sections attempt to summarize the most common causes of LDAP errors
+when using OpenLDAP
+
+H2: Common causes of LDAP errors
+
+H3: ldap_*: Can't contact LDAP server
+
+The {{B:Can't contact LDAP server}} error is usually returned when the LDAP
+server cannot be contacted. This may occur for many reasons:
+
+* the LDAP server is not running; this can be checked by running, for example,
+
+> telnet <host> <port>
+
+replacing {{<host>}} and {{<port>}} with the hostname and the port the server
+is supposed to listen on.
+* the client has not been instructed to contact a running server; with OpenLDAP
+command-line tools this is accomplished by providing the -H switch, whose
+argument is a valid LDAP url corresponding to the interface the server is
+supposed to be listening on.
+
+H3: ldap_*: No such object
+
+The {{B:no such object}} error is generally returned when the target DN of the
+operation cannot be located. This section details reasons common to all
+operations. You should also look for answers specific to the operation
+(as indicated in the error message).
+
+The most common reason for this error is non-existence of the named object. First,
+check for typos.
+
+Also note that, by default, a new directory server holds no objects
+(except for a few system entries). So, if you are setting up a new directory
+server and get this message, it may simply be that you have yet to add the
+object you are trying to locate.
+
+The error commonly occurs because a DN was not specified and a default was not
+properly configured.
+
+If you have a suffix specified in slapd.conf eg.
+
+> suffix "dc=example,dc=com"
+
+You should use
+
+> ldapsearch -b 'dc=example,dc=com' '(cn=jane*)'
+
+to tell it where to start the search.
+
+The {{F:-b}} should be specified for all LDAP commands unless you have an
+{{ldap.conf}}(5) default configured.
+
+See {{ldapsearch}}(1), {{ldapmodify}}(1)
+
+Also, {{slapadd}}(8) and its ancillary programs are very strict about the
+syntax of the LDIF file.
+
+Some liberties in the LDIF file may result in an apparently successful creation
+of the database, but accessing some parts of it may be difficult.
+
+One known common error in database creation is putting a blank line before the
+first entry in the LDIF file. {{B:There must be no leading blank lines in the
+LDIF file.}}
+
+It is generally recommended that {{ldapadd}}(1) be used instead of {{slapadd}}(8)
+when adding new entries your directory. {{slapadd}}(8) should be used to bulk
+load entries known to be valid.
+
+Another cause of this message is a referral
+({SECT:Constructing a Distributed Directory Service}}) entry to an unpopulated
+directory.
+
+Either remove the referral, or add a single record with the referral base DN
+to the empty directory.
+
+This error may also occur when slapd is unable to access the contents of its
+database because of file permission problems. For instance, on a Red Hat Linux
+system, slapd runs as user 'ldap'. When slapadd is run as root to create a
+database from scratch, the contents of {{F:/var/lib/ldap}} are created with
+user and group root and with permission 600, making the contents inaccessible
+to the slapd server.
+
+H3: ldap_*: Can't chase referral
+
+This is caused by the line
+
+> referral ldap://root.openldap.org
+
+In {{F:slapd.conf}}, it was provided as an example for how to use referrals
+in the original file. However if your machine is not permanently connected to
+the Internet, it will fail to find the server, and hence produce an error message.
+
+To resolve, just place a # in front of line and restart slapd or point it to
+an available ldap server.
+
+See also: {{ldapadd}}(1), {{ldapmodify}}(1) and {{slapd.conf}}(5)
+
+H3: ldap_*: server is unwilling to perform
+
+slapd will return an unwilling to perform error if the backend holding the
+target entry does not support the given operation.
+
+The password backend is only willing to perform searches. It will return an
+unwilling to perform error for all other operations.
+
+H3: ldap_*: Insufficient access
+
+This error occurs when server denies the operation due to insufficient access.
+This is usually caused by binding to a DN with insufficient privileges
+(or binding anonymously) to perform the operation.
+
+You can bind as the rootdn/rootpw specified in {{slapd.conf}}(5) to gain full
+access. Otherwise, you must bind to an entry which has been granted the
+appropriate rights through access controls.
+
+
+H3: ldap_*: Invalid DN syntax
+
+The target (or other) DN of the operation is invalid. This implies that either
+the string representation of the DN is not in the required form, one of the
+types in the attribute value assertions is not defined, or one of the values
+in the attribute value assertions does not conform to the appropriate syntax.
+
+H3: ldap_*: Referral hop limit exceeded
+
+This error generally occurs when the client chases a referral which refers
+itself back to a server it already contacted. The server responds as it did
+before and the client loops. This loop is detected when the hop limit is exceeded.
+
+This is most often caused through misconfiguration of the server's default
+referral. The default referral should not be itself:
+
+That is, on {{F:ldap://myldap/}} the default referral should not be {{F:ldap://myldap/}}
+ (or any hostname/ip which is equivalent to myldap).
+
+H3: ldap_*: operations error
+
+In some versions of {{slapd}}(8), {{operationsError}} was returned instead of other.
+
+H3: ldap_*: other error
+
+The other result code indicates an internal error has occurred.
+While the additional information provided with the result code might provide
+some hint as to the problem, often one will need to consult the server's log files.
+
+H3: ldap_add/modify: Invalid syntax
+
+This error is reported when a value of an attribute does not conform to syntax
+restrictions. Additional information is commonly provided stating which value
+of which attribute was found to be invalid. Double check this value and other
+values (the server will only report the first error it finds).
+
+Common causes include:
+
+* extraneous whitespace (especially trailing whitespace)
+* improperly encoded characters (LDAPv3 uses UTF-8 encoded Unicode)
+* empty values (few syntaxes allow empty values)
+
+
+For certain syntax, like OBJECT IDENTIFIER (OID), this error can indicate that
+the OID descriptor (a "short name") provided is unrecognized. For instance,
+this error is returned if the {{objectClass}} value provided is unrecognized.
+
+H3: ldap_add/modify: Object class violation
+
+This error is returned with the entry to be added or the entry as modified
+violates the object class schema rules. Normally additional information is
+returned the error detailing the violation. Some of these are detailed below.
+
+Violations related to the entry's attributes:
+
+> Attribute not allowed
+
+A provided attribute is not allowed by the entry's object class(es).
+
+> Missing required attribute
+
+An attribute required by the entry's object class(es) was not provided.
+
+Violations related to the entry's class(es):
+
+> Entry has no objectClass attribute
+
+The entry did not state which object classes it belonged to.
+
+> Unrecognized objectClass
+
+One (or more) of the listed objectClass values is not recognized.
+
+> No structural object class provided
+
+None of the listed objectClass values is structural.
+
+> Invalid structural object class chain
+
+Two or more structural objectClass values are not in same structural object
+class chain.
+
+> Structural object class modification
+
+Modify operation attempts to change the structural class of the entry.
+
+> Instantiation of abstract objectClass.
+
+An abstract class is not subordinate to any listed structural or auxiliary class.
+
+> Invalid structural object class
+
+Other structural object class problem.
+
+> No structuralObjectClass operational attribute
+
+This is commonly returned when a shadow server is provided an entry which does
+not contain the structuralObjectClass operational attribute.
+
+
+Note that the above error messages as well as the above answer assumes basic
+knowledge of LDAP/X.500 schema.
+
+H3: ldap_add: No such object
+
+The "ldap_add: No such object" error is commonly returned if parent of the
+entry being added does not exist. Add the parent entry first...
+
+For example, if you are adding "cn=bob,dc=domain,dc=com" and you get:
+
+> ldap_add: No such object
+
+The entry "dc=domain,dc=com" likely doesn't exist. You can use ldapsearch to
+see if does exist:
+
+> ldapsearch -b 'dc=domain,dc=com' -s base '(objectclass=*)'
+
+If it doesn't, add it. See {{SECT:A Quick-Start Guide}} for assistance.
+
+Note: if the entry being added is the same as database suffix, it's parent
+isn't required. i.e.: if your suffix is "dc=domain,dc=com", "dc=com" doesn't
+need to exist to add "dc=domain,dc=com".
+
+This error will also occur if you try to add any entry that the server is not
+configured to hold.
+
+For example, if your database suffix is "dc=domain,dc=com" and you attempt to
+add "dc=domain2,dc=com", "dc=com", "dc=domain,dc=org", "o=domain,c=us", or an
+other DN in the "dc=domain,dc=com" subtree, the server will return a
+ "No such object" (or referral) error.
+
+{{slapd}}(8) will generally return "no global superior knowledge" as additional
+information indicating its return noSuchObject instead of a referral as the
+server is not configured with knowledge of a global superior server.
+
+
+H3: ldap add: invalid structural object class chain
+
+This particular error refers to the rule about STRUCTURAL objectclasses, which
+states that an object is of one STRUCTURAL class, the structural class of the
+object. The object is said to belong to this class, zero or more auxiliaries
+ classes, and their super classes.
+
+While all of these classes are commonly listed in the objectClass attribute of
+the entry, one of these classes is the structural object class of the entry.
+Thus, it is OK for an objectClass attribute
+to contain inetOrgPerson, organizationalPerson, and person because they inherit
+ one from another to form a single super class chain. That is, inetOrgPerson SUPs
+organizationPerson SUPs person. On the other hand, it is invalid for both inetOrgPerson
+and account to be listed in objectClass as inetOrgPerson and account are not
+part of the same super class chain (unless some other class is also listed
+with is a subclass of both).
+
+To resolve this problem, one must determine which class will better serve
+structural object class for the entry, adding this class to the objectClass
+attribute (if not already present), and remove any other structural class from
+the entry's objectClass attribute which is not a super class of the structural
+object class.
+
+Which object class is better depends on the particulars of the situation.
+One generally should consult the documentation for the applications one is
+using for help in making the determination.
+
+H3: ldap_add: no structuralObjectClass operational attribute
+
+ldapadd(1) may error:
+
+> adding new entry "uid=XXX,ou=People,o=campus,c=ru"
+> ldap_add: Internal (implementation specific) error (80)
+> additional info: no structuralObjectClass operational attribute
+
+when slapd(8) cannot determine, based upon the contents of the objectClass
+attribute, what the structural class of the object should be.
+
+
+H3: ldap_add/modify/rename: Naming violation
+
+OpenLDAP's slapd checks for naming attributes and distinguished values consistency,
+according to RFC 4512.
+
+Naming attributes are those attributeTypes that appear in an entry's RDN;
+ distinguished values are the values of the naming attributes that appear in
+an entry's RDN, e.g, in
+
+> cn=Someone+mail=someone@example.com,dc=example,dc=com
+
+the naming attributes are cn and mail, and the distinguished values are
+Someone and someone@example.com.
+
+OpenLDAP's slapd checks for consistency when:
+
+* adding an entry
+* modifying an entry, if the values of the naming attributes are changed
+* renaming an entry, if the RDN of the entry changes
+
+Possible causes of error are:
+
+* the naming attributes are not present in the entry; for example:
+
+> dn: dc=example,dc=com
+> objectClass: organization
+> o: Example
+> # note: "dc: example" is missing
+
+* the naming attributes are present in the entry, but in the attributeType
+definition they are marked as:
+- collective
+- operational
+- obsolete
+
+* the naming attributes are present in the entry, but the distinguished values
+are not; for example:
+
+> dn: dc=example,dc=com
+> objectClass: domain
+> dc: foobar
+> # note: "dc" is present, but the value is not "example"
+
+* the naming attributes are present in the entry, with the distinguished values, but the naming attributes:
+- do not have an equality field, so equality cannot be asserted
+- the matching rule is not supported (yet)
+- the matching rule is not appropriate
+
+* the given distinguished values do not comply with their syntax
+
+* other errors occurred during the validation/normalization/match process;
+this is a catchall: look at previous logs for details in case none of the above
+apply to your case.
+
+In any case, make sure that the attributeType definition for the naming attributes
+contains an appropriate EQUALITY field; or that of the superior, if they are
+defined based on a superior attributeType (look at the SUP field). See RFC 4512 for details.
+
+
+H3: ldap_add/delete/modify/rename: no global superior knowledge
+
+If the target entry name places is not within any of the databases the server
+is configured to hold and the server has no knowledge of a global superior,
+the server will indicate it is unwilling to perform the operation and provide
+the text "no global superior knowledge" as additional text.
+
+Likely the entry name is incorrect, or the server is not properly configured
+to hold the named entry, or, in distributed directory environments, a default
+referral was not configured.
+
+
+H3: ldap_bind: Insufficient access
+
+Current versions of slapd(8) requires that clients have authentication
+permission to attribute types used for authentication purposes before accessing
+them to perform the bind operation. As all bind operations are done anonymously
+(regardless of previous bind success), the auth access must be granted to anonymous.
+
+In the example ACL below grants the following access:
+
+* to anonymous users:
+- permission to authenticate using values of userPassword
+* to authenticated users:
+- permission to update (but not read) their userPassword
+- permission to read any object excepting values of userPassword
+
+All other access is denied.
+
+> access to attr=userPassword
+> by self =w
+> by anonymous auth
+
+> access *
+> by self write
+> by users read
+
+
+H3: ldap_bind: Invalid credentials
+
+The error usually occurs when the credentials (password) provided does not
+match the userPassword held in entry you are binding to.
+
+The error can also occur when the bind DN specified is not known to the server.
+
+Check both! In addition to the cases mentioned above you should check if the
+server denied access to userPassword on selected parts of the directory. In
+fact, slapd always returns "Invalid credentials" in case of failed bind,
+regardless of the failure reason, since other return codes could reveal the
+validity of the user's name.
+
+To debug access rules defined in slapd.conf, add "ACL" to log level.
+
+H3: ldap_bind: Protocol error
+
+There error is generally occurs when the LDAP version requested by the
+client is not supported by the server.
+
+The OpenLDAP Software 2.x server, by default, only accepts version 3 LDAP Bind
+requests but can be configured to accept a version 2 LDAP Bind request.
+
+Note: The 2.x server expects LDAPv3 [RFC4510] to be used when the client
+requests version 3 and expects a limited LDAPv3 variant (basically, LDAPv3
+syntax and semantics in an LDAPv2 PDUs) to be used when version 2 is expected.
+
+This variant is also sometimes referred to as LDAPv2+, but differs from the U-Mich
+LDAP variant in a number of ways.
+
+H3: ldap_modify: cannot modify object class
+
+This message is commonly returned when attempting to modify the objectClass
+attribute in a manner inconsistent with the LDAP/X.500 information model. In
+particular, it commonly occurs when one tries to change the structure of the
+object from one class to another, for instance, trying to change an 'apple'
+into a 'pear' or a 'fruit' into a 'pear'.
+
+Such changes are disallowed by the slapd(8) in accordance with LDAP and X.500 restrictions.
+
+
+H3: ldap_sasl_interactive_bind_s: ...
+
+If you intended to bind using a DN and password and get an error from
+ldap_sasl_interactive_bind_s, you likely forgot to provide a '-x' option to
+the command. By default, SASL authentication is used. '-x' is necessary to
+select "simple" authentication.
+
+
+H3: ldap_sasl_interactive_bind_s: No such Object
+
+This indicates that LDAP SASL authentication function could not read the
+Root DSE.
+The error will occur when the server doesn't provide a root DSE. This may be
+due to access controls.
+
+
+H3: ldap_sasl_interactive_bind_s: No such attribute
+
+This indicates that LDAP SASL authentication function could read the Root
+DSE but it contained no supportedSASLMechanism attribute.
+
+The supportedSASLmechanism attribute lists mechanisms currently available.
+The list may be empty because none of the supported mechanisms are currently
+available. For example, EXTERNAL is listed only if the client has established
+its identity by authenticating at a lower level (e.g. TLS).
+
+Note: the attribute may not be visible due to access controls
+
+Note: SASL bind is the default for all OpenLDAP tools, e.g. ldapsearch(1), ldapmodify(1). To force use of "simple" bind, use the "-x" option. Use of "simple" bind is not recommended unless one has adequate confidentiality protection in place (e.g. TLS/SSL, IPSEC).
+
+H3: ldap_sasl_interactive_bind_s: Unknown authentication method
+
+This indicates that none of the SASL authentication supported by the server
+are supported by the client, or that they are too weak or otherwise inappropriate
+for use by the client. Note that the default security options disallows the use
+of certain mechanisms such as ANONYMOUS and PLAIN (without TLS).
+
+Note: SASL bind is the default for all OpenLDAP tools. To force use of "simple" bind, use the "-x" option. Use of "simple" bind is not recommended unless one has adequate confidentiality protection in place (e.g. TLS/SSL, IPSEC).
+
+H3: ldap_sasl_interactive_bind_s: Local error (82)
+
+Apparently not having forward and reverse DNS entries for the LDAP server can result in this error.
+
+
+H3: ldap_search: Partial results and referral received
+
+This error is returned with the server responses to an LDAPv2 search query
+with both results (zero or more matched entries) and references (referrals to other servers).
+See also: ldapsearch(1).
+
+If the updatedn on the replica does not exist, a referral will be returned.
+It may do this as well if the ACL needs tweaking.
+
+H3: ldap_start_tls: Operations error
+
+ldapsearch(1) and other tools will return
+
+> ldap_start_tls: Operations error (1)
+> additional info: TLS already started
+
+When the user (though command line options and/or ldap.conf(5)) has requested
+TLS (SSL) be started twice. For instance, when specifying both "-H ldaps://server.do.main" and "-ZZ".
+
+H2: Other Errors
+
+H3: ber_get_next on fd X failed errno=34 (Numerical result out of range)
+
+This slapd error generally indicates that the client sent a message that
+exceeded an administrative limit. See sockbuf_max_incoming and sockbuf_max_incoming_auth
+configuration directives in slapd.conf(5).
+
+H3: ber_get_next on fd X failed errno=11 (Resource temporarily unavailable)
+
+This message is not indicative of abnormal behavior or error. It simply means
+that expected data is not yet available from the resource, in this context, a
+network socket. slapd(8) will process the data once it does becomes available.
+
+H3: daemon: socket() failed errno=97 (Address family not supported)
+
+This message indicates that the operating system does not support one of the
+(protocol) address families which slapd(8) was configured to support. Most
+commonly, this occurs when slapd(8) was configured to support IPv6 yet the
+operating system kernel wasn't. In such cases, the message can be ignored.
+
+H3: GSSAPI: gss_acquire_cred: Miscellaneous failure; Permission denied;
+
+This message means that slapd is not running as root and, thus, it cannot get
+its Kerberos 5 key from the keytab, usually file /etc/krb5.keytab.
+
+A keytab file is used to store keys that are to be used by services or daemons
+that are started at boot time. It is very important that these secrets are kept
+beyond reach of intruders.
+
+That's why the default keytab file is owned by root and protected from being
+read by others. Do not mess with these permissions, build a different keytab
+file for slapd instead, and make sure it is owned by the user that slapd
+runs as.
+
+To do this, start kadmin, and enter the following commands:
+
+> addprinc -randkey ldap/ldap.example.com@EXAMPLE.COM
+> ktadd -k /etc/openldap/ldap.keytab ldap/ldap.example.com@EXAMPLE.COM
+
+Then, on the shell, do:
+
+> chown ldap:ldap /etc/openldap/ldap.keytab
+> chmod 600 /etc/openldap/ldap.keytab
+
+Now you have to tell slapd (well, actually tell the gssapi library in Kerberos 5
+that is invoked by Cyrus SASL) where to find the new keytab. You do this by
+setting the environment variable KRB5_KTNAME like this:
+
+> export KRB5_KTNAME="FILE:/etc/openldap/ldap.keytab"
+
+Set that environment variable on the slapd start script (Red Hat users might
+find /etc/sysconfig/ldap a perfect place).
+
+This only works if you are using MIT kerberos. It doesn't work with Heimdal,
+for instance.
+
+
+In Heimdal there is a function gsskrb5_register_acceptor_identity() that sets
+the path of the keytab file you want to use. In Cyrus SASL 2 you can add
+
+> keytab: /path/to/file
+
+to your application's SASL config file to use this feature. This only works with Heimdal.
+
+
+H3: access from unknown denied
+
+This related to TCP wrappers. See hosts_access(5) for more information.
+in the log file: "access from unknown denied" This related to TCP wrappers.
+See hosts_access(5) for more information.
+for example: add the line "slapd: .hosts.you.want.to.allow" in /etc/hosts.allow
+to get rid of the error.
+
+H3: ldap_read: want=# error=Resource temporarily unavailable
+
+This message occurs normally. It means that pending data is not yet available
+from the resource, a network socket. slapd(8) will process the data once it
+becomes available.
+
+H3: `make test' fails
+
+Some times, `make test' fails at the very first test with an obscure message like
+
+> make test
+> make[1]: Entering directory `/ldap_files/openldap-2.5.0/tests'
+> make[2]: Entering directory `/ldap_files/openldap-2.5.0/tests'
+> Initiating LDAP tests for MDB...
+> Cleaning up test run directory leftover from previous run.
+> Running ./scripts/all...
+> >>>>> Executing all LDAP tests for mdb
+> >>>>> Starting test000-rootdse ...
+> running defines.sh
+> Starting slapd on TCP/IP port 9011...
+> Using ldapsearch to retrieve the root DSE...
+> Waiting 5 seconds for slapd to start...
+> ./scripts/test000-rootdse: line 40: 10607 Segmentation fault $SLAPD -f $CONF1 -h $URI1 -d $LVL $TIMING >$LOG1 2>&1
+> Waiting 5 seconds for slapd to start...
+> Waiting 5 seconds for slapd to start...
+> Waiting 5 seconds for slapd to start...
+> Waiting 5 seconds for slapd to start...
+> Waiting 5 seconds for slapd to start...
+> ./scripts/test000-rootdse: kill: (10607) - No such pid
+> ldap_sasl_bind_s: Can't contact LDAP server (-1)
+> >>>>> Test failed
+> >>>>> ./scripts/test000-rootdse failed (exit 1)
+> make[2]: *** [mdb-yes] Error 1
+> make[2]: Leaving directory `/ldap_files/openldap-2.5.0/tests'
+> make[1]: *** [test] Error 2
+> make[1]: Leaving directory `/ldap_files/openldap-2.5.0/tests'
+> make: *** [test] Error 2
+
+or so. Usually, the five lines
+
+ Waiting 5 seconds for slapd to start...
+
+indicate that slapd didn't start at all.
+
+In tests/testrun/slapd.1.log there is a full log of what slapd wrote while
+trying to start. The log level can be increased by setting the environment
+variable SLAPD_DEBUG to the corresponding value; see loglevel in slapd.conf(5)
+for the meaning of log levels.
+
+A typical reason for this behavior is a runtime link problem, i.e. slapd cannot
+find some dynamic libraries it was linked against. Try running ldd(1) on slapd
+(for those architectures that support runtime linking).
+
+There might well be other reasons; the contents of the log file should help
+clarifying them.
+
+Tests that fire up multiple instances of slapd typically log to tests/testrun/slapd.<n>.log,
+with a distinct <n> for each instance of slapd; list tests/testrun/ for possible
+values of <n>.
+
+H3: ldap_*: Internal (implementation specific) error (80) - additional info: entry index delete failed
+
+This seems to be related with wrong ownership of the MDB's dir (/var/lib/ldap)
+and files. The files must be owned by the user that slapd runs as.
+
+> chown -R ldap:ldap /var/lib/ldap
+
+fixes it in Debian
+
+
+H3: ldap_sasl_interactive_bind_s: Can't contact LDAP server (-1)
+
+Using SASL, when a client contacts LDAP server, the slapd service dies
+immediately and client gets an error :
+
+> SASL/GSSAPI authentication started ldap_sasl_interactive_bind_s: Can't contact LDAP server (-1)
+
+Then check the slapd service, it stopped.
diff --git a/doc/guide/admin/appendix-configs.sdf b/doc/guide/admin/appendix-configs.sdf
new file mode 100644
index 0000000..78f7395
--- /dev/null
+++ b/doc/guide/admin/appendix-configs.sdf
@@ -0,0 +1,14 @@
+# $OpenLDAP$
+# Copyright 2007-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: Configuration File Examples
+
+
+H2: slapd.conf
+
+
+H2: ldap.conf
+
+
+H2: a-n-other.conf
diff --git a/doc/guide/admin/appendix-contrib.sdf b/doc/guide/admin/appendix-contrib.sdf
new file mode 100644
index 0000000..3e5898c
--- /dev/null
+++ b/doc/guide/admin/appendix-contrib.sdf
@@ -0,0 +1,116 @@
+# $OpenLDAP$
+# Copyright 2007-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: OpenLDAP Software Contributions
+
+The following sections attempt to summarize the various contributions in OpenLDAP
+software, as found in {{F:openldap_src/contrib}}
+
+H2: Client APIs
+
+Intro and discuss
+
+H3: ldapc++
+
+Intro and discuss
+
+H3: ldaptcl
+
+Intro and discuss
+
+H2: Overlays
+
+H3: acl
+
+Plugins that implement access rules. Currently only posixGroup,
+which implements access control based on posixGroup membership.
+
+
+H3: addpartial
+
+Treat Add requests as Modify requests if the entry exists.
+
+
+H3: allop
+
+Return operational attributes for root DSE even when not
+requested, since some clients expect this.
+
+
+H3: autogroup
+
+Automated updates of group memberships.
+
+
+H3: comp_match
+
+Component Matching rules (RFC 3687).
+
+
+H3: denyop
+
+Deny selected operations, returning {{unwillingToPerform}}.
+
+
+H3: dsaschema
+
+Permit loading DSA-specific schema, including operational attrs.
+
+
+H3: lastmod
+
+Track the time of the last write operation to a database.
+
+
+H3: nops
+
+Remove null operations, e.g. changing a value to same as before.
+
+
+H3: nssov
+
+Handle NSS lookup requests through a local Unix Domain socket.
+
+
+H3: passwd
+
+Support additional password mechanisms.
+
+
+H3: proxyOld
+
+Proxy Authorization compatibility with obsolete internet-draft.
+
+
+H3: smbk5pwd
+
+Make the PasswordModify Extended Operation update Kerberos
+keys and Samba password hashes as well as {{userPassword}}.
+
+
+H3: trace
+
+Trace overlay invocation.
+
+
+H3: usn
+
+Maintain {{usnCreated}} and {{usnChanged}} attrs similar to Microsoft AD.
+
+
+H2: Tools
+
+Intro and discuss
+
+H3: Statistic Logging
+
+statslog
+
+H2: SLAPI Plugins
+
+Intro and discuss
+
+H3: addrdnvalues
+
+More
diff --git a/doc/guide/admin/appendix-deployments.sdf b/doc/guide/admin/appendix-deployments.sdf
new file mode 100644
index 0000000..2d772d3
--- /dev/null
+++ b/doc/guide/admin/appendix-deployments.sdf
@@ -0,0 +1,7 @@
+# $OpenLDAP$
+# Copyright 2007-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: Real World OpenLDAP Deployments and Examples
+
+Examples and discussions
diff --git a/doc/guide/admin/appendix-ldap-result-codes.sdf b/doc/guide/admin/appendix-ldap-result-codes.sdf
new file mode 100644
index 0000000..d54d6f5
--- /dev/null
+++ b/doc/guide/admin/appendix-ldap-result-codes.sdf
@@ -0,0 +1,269 @@
+# $OpenLDAP$
+# Copyright 2007-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: LDAP Result Codes
+
+For the purposes of this guide, we have incorporated the standard LDAP result
+codes from {{Appendix A. LDAP Result Codes}} of {{REF:RFC4511}}, a copy of which can
+be found in {{F:doc/rfc}} of the OpenLDAP source code.
+
+We have expanded the description of each error in relation to the OpenLDAP
+toolsets.
+LDAP extensions may introduce extension-specific result codes, which are not part
+of RFC4511.
+OpenLDAP returns the result codes related to extensions it implements.
+Their meaning is documented in the extension they are related to.
+
+H2: Non-Error Result Codes
+
+These result codes (called "non-error" result codes) do not indicate
+an error condition:
+
+> success (0),
+> compareFalse (5),
+> compareTrue (6),
+> referral (10), and
+> saslBindInProgress (14).
+
+The {{success}}, {{compareTrue}}, and {{compareFalse}} result codes indicate
+successful completion (and, hence, are referred to as "successful"
+result codes).
+
+The {{referral}} and {{saslBindInProgress}} result codes indicate the client
+needs to take additional action to complete the operation.
+
+H2: Result Codes
+
+Existing LDAP result codes are described as follows:
+
+H2: success (0)
+
+Indicates the successful completion of an operation.
+
+Note: this code is not used with the Compare operation. See {{SECT:compareFalse (5)}}
+and {{SECT:compareTrue (6)}}.
+
+H2: operationsError (1)
+
+Indicates that the operation is not properly sequenced with
+relation to other operations (of same or different type).
+
+For example, this code is returned if the client attempts to
+StartTLS ({{REF:RFC4511}} Section 4.14) while there are other uncompleted operations
+or if a TLS layer was already installed.
+
+H2: protocolError (2)
+
+Indicates the server received data that is not well-formed.
+
+For Bind operation only, this code is also used to indicate
+that the server does not support the requested protocol
+version.
+
+For Extended operations only, this code is also used to
+indicate that the server does not support (by design or
+configuration) the Extended operation associated with the
+{{requestName}}.
+
+For request operations specifying multiple controls, this may
+be used to indicate that the server cannot ignore the order
+of the controls as specified, or that the combination of the
+specified controls is invalid or unspecified.
+
+H2: timeLimitExceeded (3)
+
+Indicates that the time limit specified by the client was
+exceeded before the operation could be completed.
+
+H2: sizeLimitExceeded (4)
+
+Indicates that the size limit specified by the client was
+exceeded before the operation could be completed.
+
+H2: compareFalse (5)
+
+Indicates that the Compare operation has successfully
+completed and the assertion has evaluated to FALSE or
+Undefined.
+
+H2: compareTrue (6)
+
+Indicates that the Compare operation has successfully
+completed and the assertion has evaluated to TRUE.
+
+H2: authMethodNotSupported (7)
+
+Indicates that the authentication method or mechanism is not
+supported.
+
+H2: strongerAuthRequired (8)
+
+Indicates the server requires strong(er) authentication in
+order to complete the operation.
+
+When used with the Notice of Disconnection operation, this
+code indicates that the server has detected that an
+established security association between the client and
+server has unexpectedly failed or been compromised.
+
+H2: referral (10)
+
+Indicates that a referral needs to be chased to complete the
+operation (see {{REF:RFC4511}} Section 4.1.10).
+
+H2: adminLimitExceeded (11)
+
+Indicates that an administrative limit has been exceeded.
+
+H2: unavailableCriticalExtension (12)
+
+Indicates a critical control is unrecognized (see {{REF:RFC4511}} Section
+4.1.11).
+
+H2: confidentialityRequired (13)
+
+Indicates that data confidentiality protections are required.
+
+H2: saslBindInProgress (14)
+
+Indicates the server requires the client to send a new bind
+request, with the same SASL mechanism, to continue the
+authentication process (see {{REF:RFC4511}} Section 4.2).
+
+H2: noSuchAttribute (16)
+
+Indicates that the named entry does not contain the specified
+attribute or attribute value.
+
+H2: undefinedAttributeType (17)
+
+Indicates that a request field contains an unrecognized
+attribute description.
+
+H2: inappropriateMatching (18)
+
+Indicates that an attempt was made (e.g., in an assertion) to
+use a matching rule not defined for the attribute type
+concerned.
+
+H2: constraintViolation (19)
+
+Indicates that the client supplied an attribute value that
+does not conform to the constraints placed upon it by the
+data model.
+
+For example, this code is returned when multiple values are
+supplied to an attribute that has a SINGLE-VALUE constraint.
+
+H2: attributeOrValueExists (20)
+
+Indicates that the client supplied an attribute or value to
+be added to an entry, but the attribute or value already
+exists.
+
+H2: invalidAttributeSyntax (21)
+
+Indicates that a purported attribute value does not conform
+to the syntax of the attribute.
+
+H2: noSuchObject (32)
+
+Indicates that the object does not exist in the DIT.
+
+H2: aliasProblem (33)
+
+Indicates that an alias problem has occurred. For example,
+the code may used to indicate an alias has been dereferenced
+that names no object.
+
+H2: invalidDNSyntax (34)
+
+Indicates that an LDAPDN or RelativeLDAPDN field (e.g., search
+base, target entry, ModifyDN newrdn, etc.) of a request does
+not conform to the required syntax or contains attribute
+values that do not conform to the syntax of the attribute's
+type.
+
+H2: aliasDereferencingProblem (36)
+
+Indicates that a problem occurred while dereferencing an
+alias. Typically, an alias was encountered in a situation
+where it was not allowed or where access was denied.
+
+H2: inappropriateAuthentication (48)
+
+Indicates the server requires the client that had attempted
+to bind anonymously or without supplying credentials to
+provide some form of credentials.
+
+H2: invalidCredentials (49)
+
+Indicates that the provided credentials (e.g., the user's name
+and password) are invalid.
+
+H2: insufficientAccessRights (50)
+
+Indicates that the client does not have sufficient access
+rights to perform the operation.
+
+H2: busy (51)
+
+Indicates that the server is too busy to service the
+operation.
+
+H2: unavailable (52)
+
+Indicates that the server is shutting down or a subsystem
+necessary to complete the operation is offline.
+
+H2: unwillingToPerform (53)
+
+Indicates that the server is unwilling to perform the
+operation.
+
+H2: loopDetect (54)
+
+Indicates that the server has detected an internal loop (e.g.,
+while dereferencing aliases or chaining an operation).
+
+H2: namingViolation (64)
+
+Indicates that the entry's name violates naming restrictions.
+
+H2: objectClassViolation (65)
+
+Indicates that the entry violates object class restrictions.
+
+H2: notAllowedOnNonLeaf (66)
+
+Indicates that the operation is inappropriately acting upon a
+non-leaf entry.
+
+H2: notAllowedOnRDN (67)
+
+Indicates that the operation is inappropriately attempting to
+remove a value that forms the entry's relative distinguished
+name.
+
+H2: entryAlreadyExists (68)
+
+Indicates that the request cannot be fulfilled (added, moved,
+or renamed) as the target entry already exists.
+
+H2: objectClassModsProhibited (69)
+
+Indicates that an attempt to modify the object class(es) of
+an entry's 'objectClass' attribute is prohibited.
+
+For example, this code is returned when a client attempts to
+modify the structural object class of an entry.
+
+H2: affectsMultipleDSAs (71)
+
+Indicates that the operation cannot be performed as it would
+affect multiple servers (DSAs).
+
+H2: other (80)
+
+Indicates the server has encountered an internal error.
diff --git a/doc/guide/admin/appendix-recommended-versions.sdf b/doc/guide/admin/appendix-recommended-versions.sdf
new file mode 100644
index 0000000..6c87b8c
--- /dev/null
+++ b/doc/guide/admin/appendix-recommended-versions.sdf
@@ -0,0 +1,23 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: Recommended OpenLDAP Software Dependency Versions
+
+This appendix details the recommended versions of the software
+that OpenLDAP depends on.
+
+Please read the {{SECT:Prerequisite software}} section for more
+information on the following software dependencies.
+
+H2: Dependency Versions
+
+!block table; align=Center; coltags="N,EX,EX"; title="Table 8.5: OpenLDAP Software Dependency Versions"
+Feature|Software|Version
+{{TERM[expand]TLS}}:
+|{{PRD:OpenSSL}}|1.1.1+
+|{{PRD:GnuTLS}}|3.6.0+
+{{TERM[expand]SASL}}|{{PRD:Cyrus SASL}}|2.1.27+
+{{TERM[expand]lloadd}}|{{PRD:libevent}}|2.1+
+Threads:|POSIX {{pthreads}}|Version
+!endblock
diff --git a/doc/guide/admin/appendix-upgrading.sdf b/doc/guide/admin/appendix-upgrading.sdf
new file mode 100644
index 0000000..d39d69a
--- /dev/null
+++ b/doc/guide/admin/appendix-upgrading.sdf
@@ -0,0 +1,74 @@
+# $OpenLDAP$
+# Copyright 2007-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: Upgrading from 2.4.x
+
+The following sections attempt to document the steps you will need to take in order
+to upgrade from the latest 2.4.x OpenLDAP version.
+
+The normal upgrade procedure, as discussed in the {{SECT:Maintenance}} section, should
+of course still be followed prior to doing any of this.
+
+H2: {{B:cn=config}} olc* attributes
+
+The {{olcMirrorMode}} attribute has been renamed to {{olcMultiProvider}}. Existing configurations
+will continue to work with the old parameter name, but it is advised to update to the new name as a
+part of the upgrade process.
+
+H2: ppolicy overlay
+
+The overlay now implements version 10 of the ppolicy draft in full. This includes the notion of a password
+administrator where applicable (as determined by having a {{manage}} permission to the {{userPassword}} attribute)
+and skips certain processing when there is no valid policy in effect or where the operation is initiated by
+a password administrator. Many attributes are now tagged with {{NO-USER-MODIFICATION}} in the schema, requiring
+the use of {{relax}} control to modify them.
+
+In OpenLDAP 2.4 the {{slapo-ppolicy}}(5) overlay relied on a separate schema file to be included for it to function.
+This schema is now implemented internally in the slapo-ppolicy module. When upgrading {{slapd.conf}}(5) deployments
+the include statement for the schema must be removed. For {{slapd-config}}(5) deployments, the config database
+must be exported via slapcat and the old ppolicy schema removed from the export. The resulting config database
+can then be imported.
+
+H2: unique overlay
+
+In OpenLDAP 2.4 it was possible to bypass {{slapo-unique}}(5) checks by using the manageDSAIT control as a part of the
+request. This is no longer possible. To achieve the same functionality the relax control must be used instead, and the
+binding identity must have manage permissions on the entry being modified.
+
+With OpenLDAP 2.5 a new keyword "serialize" has been added as a part of the unique_uri configuration parameter. This
+will cause all write operations requiring uniqueness to be serialized so as to avoid the scenario where multiple
+concurrent updates can prevent uniqueness from being enforced. See the {{slapo-unique}}(5) man page for further details.
+
+H2: ldap and meta backends
+
+Several deprecated configuration directives for {{slapd-ldap}}(5) and {{slapd-meta}}(5) have been removed. Configurations
+using those directive must be updated to use supported directives prior to upgrade. See the {{slapd-ldap}}(5) and
+{{slapd-meta}}(5) man pages from OpenLDAP 2.4 for a list of deprecated directives.
+
+H2: shell backend
+
+This deprecated backend has been removed from OpenLDAP 2.5. Configurations making use of this backend must remove it
+prior to upgrade. The {{slapd-sock}}(5) backend is recommended as an alternative.
+
+H2: perl and sql backends
+
+The {{slapd-perl}}(5) and {{slapd-sql}}(5) backends are now deprecated and no longer automatically enabled with
+the --enable-backends configure flag.
+
+H2: hdb and bdb backends
+
+The Berkeley DB based slapd-bdb and slapd-hdb backends have been removed from OpenLDAP 2.5. Deployments making use
+of these backends must migrate their configurations to use {{slapd-mdb}}(5) prior to upgrade.
+
+H2: mdb backend
+
+It is advised to determine if the new {{slapd-mdb}}(5) idlexp backend directive and/or
+the multival database directive should be added to the OpenLDAP 2.5 configuration as well as the existing global
+sortvals directive. Configuring any of these items requires that existing databases be reloaded for them to take full
+effect. This can be done separately from the overall upgrade from OpenLDAP 2.4 to OpenLDAP 2.5 if desired.
+
+H2: Client utility changes
+
+The deprecated "-h" (host) and "-p" (port) options for the ldap client utilities have been removed. It is required to
+use a properly formatted LDAP URI with the "-H" option in OpenLDAP 2.5 and later.
diff --git a/doc/guide/admin/aspell.en.pws b/doc/guide/admin/aspell.en.pws
new file mode 100644
index 0000000..63d3f0e
--- /dev/null
+++ b/doc/guide/admin/aspell.en.pws
@@ -0,0 +1,1675 @@
+personal_ws-1.1 en 1687
+commonName
+bla
+Masarati
+subjectAltName
+api
+usnCreated
+BhY
+olcSyncRepl
+olcSyncrepl
+adamsom
+adamson
+CER
+intermediateResponse
+bjensen
+cdx
+CGI
+DCE
+DAP
+chainingRequired
+arg
+ddd
+DAs
+TLSCACertificateFile
+BNF
+TLSDHParamFile
+ppolicy
+gavin
+ASN
+ava
+Chu
+del
+libexecdir
+DDR
+numericoid
+dsaschema
+ECC
+cli
+DIB
+dev
+reqNewSuperior
+librewrite
+memberof
+memberOf
+BSI
+updateref
+buf
+changetype
+dir
+EGD
+pwdMustChange
+Debian
+dit
+AlmostASearchRequest
+EXEEXT
+edu
+Heimdal
+organizationalPerson
+olcTimeLimit
+CAPI
+tokenization
+INSTALLFLAGS
+CRL
+reqcert
+CRP
+postread
+csn
+laura
+checkpass
+xvfB
+neverDerefaliases
+dns
+DN's
+DNs
+dn's
+cdef
+Helvetica
+DOP
+requestdata
+gcc
+gecos
+reqData
+CWD
+ando
+reqDeleteOldRDN
+DSA
+dontusecopy
+msgfree
+DSE
+keycol
+dlopen
+eng
+AttributeValue
+attributevalue
+DUA
+EOF
+inputfile
+DSP
+refreshDone
+dst
+NOSYNC
+env
+pagedResultsControl
+dup
+LDIFv
+syslog
+monitorTimestamp
+subschemaSubentry
+interoperate
+gid
+testdb
+gif
+memfree
+struct
+dirsync
+IAB
+fmt
+SysNet
+olcConstraintAttribute
+GHz
+Bint
+memalloc
+FSF
+usernames
+strtol
+idl
+IDN
+DESTDIR
+iff
+contextCSN
+auditModify
+auditSearch
+OpenLDAP
+openldap
+resultcode
+resultCode
+sysconfig
+indices
+blen
+APIs
+lresolv
+uidObject
+Contribware
+directoryString
+database's
+iscritical
+qbuaQ
+gss
+ZKKuqbEKJfKSXhUbHG
+employeeType
+invalidAttributeSyntax
+subtree
+Kartik
+newparent
+DkMTwBl
+memcalloc
+ing
+filtertype
+ini
+XKqkdPOmY
+regcomp
+ldapmodify
+includedir
+IPC
+resync
+ldapsearch
+reqAttr
+dynlist
+args
+hardcoded
+pgsql
+argv
+kdz
+notAllowedOnRDN
+hostport
+StartTLS
+starttls
+ldb
+servercredp
+ldd
+IPv
+ipv
+hyc
+joe
+bindmethods
+armijo
+ldp
+ISP
+len
+carLicense
+Choi
+Clatworthy
+scherr
+virtualnamingcontext
+ITU
+XXXX
+Stringprep
+Apurva
+labeledURI
+DEFS
+MDn
+attrstyle
+directoryOperation
+creatorsName
+mem
+oldPasswdFile
+oldpasswdfile
+uniqueMember
+krb
+libpath
+acknowledgements
+jts
+createTimestamp
+MIB
+LLL
+OpenSSL
+openssl
+LOF
+AVAs
+associatedDomain
+organizationalRole
+initgroups
+ETCDIR
+colaligns
+olcReadOnly
+olcReadonly
+reqResult
+LDAPMatchingRule
+bool
+LRL
+CPPFLAGS
+yWpR
+schemadir
+desc
+lud
+newrdn
+LRU
+memvfree
+dbtools
+nis
+rewriteRule
+postoperation
+LVL
+oid
+msg
+attr
+TmkzUAb
+caseExactOrderingMatch
+Subbarao
+aeeiib
+oidlen
+submatches
+PEM
+olc
+OLF
+PDU
+LDAPSchemaExtensionItem
+auth
+Pierangelo
+authzFrom
+pid
+subdirectories
+OLP
+pwdPolicyChecker
+subst
+mux
+singleLevel
+cleartext
+numattrsets
+requestDN
+caseExactSubstringsMatch
+NSS
+PKI
+olcSyncProvConfig
+ple
+jones
+NTP
+auditModRDN
+checkpointing
+NUL
+num
+objectIdentifierMatch
+sharedstatedir
+png
+CPAN
+OSI
+extendedop
+distinguishedName
+distinguishedname
+preinstalled
+rfc
+LDAPCONF
+rdn
+wZFQrDD
+OTP
+olcSizeLimit
+PRD
+sbi
+pos
+pre
+sudoadm
+stringal
+retoidp
+sdf
+efgh
+PSH
+accesslog
+sed
+cond
+qdescrs
+modifyDN
+conf
+ldapmodrdn
+sel
+bvec
+HtZhZS
+TBC
+stringbv
+SHA
+Sep
+ptr
+conn
+pwd
+DISP
+newsup
+rnd
+TCL
+shm
+DITs
+tcp
+INCPATH
+RPC
+myOID
+supportedSASLMechanism
+supportedSASLmechanism
+realnamingcontext
+UCD
+SMD
+keytab
+portnumber
+uncached
+slp
+derefInSearching
+UMich's
+TGT
+numbits
+sasldb
+UCS
+searchDN
+keytbl
+UDP
+tgz
+freemods
+prepend
+nssov
+errText
+groupnaam
+UFl
+src
+matchedDN
+ufn
+allusersgroup
+FIXME
+sql
+uid
+crit
+objectClassViolation
+ssf
+ldapfilter
+vec
+TOC
+rwm
+pwdChangedTime
+tls
+peernamestyle
+xpasswd
+SRP
+tmp
+SSL
+dupbv
+CPUs
+itsupport
+SRV
+entrymods
+sss
+rwx
+reqNewRDN
+nopresent
+rebindproc
+olcOverlayConfig
+str
+syncIdSet
+cron
+accesslevel
+czBJdDqS
+accessor's
+keyval
+alloc
+saslpasswd
+README
+QWGWZpj
+maxentries
+ttl
+undefinedAttributeType
+peercred
+sys
+allop
+memberUid
+CSNs
+wildcards
+uri
+tty
+url
+sambaGroupMapping
+XED
+sortKey
+UTF
+vlv
+TXN
+usn
+auditExtended
+usr
+txt
+UTR
+XER
+roomNumber
+namespace
+LDAPControl
+olcAttributeOptions
+dsaparam
+searchResult
+ctrl
+ldapwhoami
+extensibleObject
+clientctrls
+monitorServer
+MANCOMPRESSSUFFIX
+memberAttr
+multiclassing
+memberURL
+sudoers
+pwdMaxFailure
+pseudorootdn
+MezRroT
+GDBM
+LIBRELEASE
+DSA's
+DSAs
+realloc
+booleanMatch
+compareTrue
+mySQL
+passwd
+printf
+idassert
+rwxrwxrwx
+al
+realself
+cd
+aQ
+ar
+olcDatabaseConfig
+de
+derated
+auditDelete
+cn
+ee
+versa
+cp
+bv
+eg
+fd
+dn
+fG
+DS
+fi
+EO
+allmail
+du
+eq
+pwdAllowUserChange
+dx
+et
+eu
+syncUUIDs
+hh
+regexec
+IG
+msgidp
+noEstimate
+kb
+organizationalUnit
+Warper
+logfilter
+io
+ip
+referralsRequired
+ld
+Matic
+regexes
+subfinal
+pseudorootpw
+md
+preread
+pwdMinLength
+iZ
+ldapdelete
+xyz
+rdbms
+RDBMs
+extparam
+mk
+ng
+oc
+FIPS
+NL
+logfiles
+mr
+octetStringSubstringsMatch
+ok
+mv
+LTVERSION
+someotheruserid
+rc
+realdn
+ou
+yyy
+sb
+enum
+auditContext
+QN
+contrib
+RL
+errMatchedDN
+auditContainer
+ro
+rp
+th
+sn
+ru
+UG
+ss
+behera
+TP
+su
+invalidCredentials
+tt
+wildcard
+wi
+syslogd
+newPasswd
+xf
+deallocation
+whitespaces
+retdatap
+attrlist
+Vu
+Za
+PDkzODdASFxOQ
+MyOrganization
+ws
+cacert
+notAllowedOnNonLeaf
+attrname
+olcTLSCipherSuite
+Xr
+x's
+xw
+octetStringMatch
+mechs
+ZZ
+LDVERSION
+testAttr
+backend
+backends
+backend's
+BerValues
+Solaris
+structs
+reqTimeLimit
+judgmentday
+reqAuthzID
+errp
+ostring
+policyDN
+testObject
+pwdMaxAge
+binddn
+bindDN
+bindDn
+distributedOperation
+schemachecking
+strvals
+dataflow
+robert
+fqdn
+prtotal
+admittable
+Makefile
+IANA
+localhost
+offsite
+bindir
+fred
+olcUpdateref
+bindwhen
+UMLDAP
+searchResultDone
+MAXLEN
+pwdInHistory
+realtime
+reqAttrsOnly
+sysconfdir
+searchResultReference
+olcAttributeTypes
+everytime
+protocolError
+errno
+errOp
+serverctrls
+recursivegroup
+BlpQmtczb
+integerMatch
+moduledir
+dynstyle
+bindpw
+AUTHNAME
+UniqueName
+blahblah
+saslmech
+pthreads
+IEEE
+regex
+SIGINT
+slappasswd
+errABsObject
+errAbsObject
+ldapexop
+objectIdentifier
+objectidentifier
+deallocators
+multiprovider
+MultiProvider
+loopDetect
+SIGHUP
+authMethodNotSupported
+IDNA
+bvecfree
+pwdLockoutDuration
+attrset
+displayName
+subentry
+reqScope
+oldPasswd
+exop
+filtercomp
+expr
+syntaxes
+memrealloc
+returncode
+returnCode
+OpenLDAP's
+exts
+bitstringa
+caseIgnoreOrderingMatch
+searchFilterAttrDN
+func
+jane
+IESG
+llber
+attrval
+ietf
+olcSchemaConfig
+bitstrings
+bvalues
+hmev
+realdnattr
+attrpair
+affectsMultipleDSAs
+Preprocessor
+lastName
+lldap
+cachesize
+slapauth
+attributeType
+attributetype
+GSER
+olcDbNosync
+typedef
+bjorn
+datagram
+strcasecmp
+selfstyle
+preoperation
+FQDNs
+exopPasswdDN
+userid
+subentries
+monitoredObject
+TLSVerifyClient
+noidlen
+LDAPNOINIT
+henry
+pwdGraceAuthnLimit
+pwdGraceAuthNLimit
+hnPk
+userpassword
+userPassword
+noanonymous
+LIBVERSION
+anyuser
+symas
+dcedn
+glibc
+sublevel
+chroot
+posixGroup
+nretries
+testgroup
+ldaphost
+frontend
+someotherdomain
+proxying
+IMAP
+organisations
+rewriteMap
+monitoredInfo
+modrDN
+ModRDN
+modrdn
+HREF
+DQTxCYEApdUtNXGgdUac
+inline
+ConnSettings
+ShowSystemTables
+multiproxy
+reqSizeLimit
+kerberos
+loglevel
+bvstrdup
+reqReferral
+rlookups
+siiiib
+LTSTATIC
+timelimitExceeded
+timeLimitExceeded
+XKYnrjvGT
+subtrees
+unixODBC
+hostnames
+AutoConfig
+libtool
+submatch
+reqDN
+dnstyle
+inet
+schemas
+pwdPolicySubentry
+pwdPolicySubEntry
+reqId
+backsql
+scanf
+olcBackend
+TLSCACertificatePath
+Arial
+init
+runtime
+onelevel
+YtNFk
+impl
+Autoconf
+stderr
+ascii
+MANCOMPRESS
+authPassword
+attrdescN
+aspell
+allusers
+statslog
+alwaysDerefAliases
+RELEASEDATE
+olcModuleList
+pwdSafeModify
+html
+GCmfuqEvm
+multimaster
+testrun
+olcUniqueURI
+rewriteEngine
+slapdindex
+LTFINISH
+olcOverlay
+lber
+serverID
+blogs
+numResponses
+lang
+POSIX
+pathname
+noSuchObject
+proxyOld
+BerElement
+berelement
+sbiod
+plugin
+http
+olcModuleLoad
+ldap
+ldbm
+numericStringSubstringsMatch
+internet
+storages
+WhoAmI
+whoami
+criticality
+addBlanks
+logins
+syncrepl
+dbnum
+operationsError
+homePhone
+octetStringOrderingMatch
+testTwo
+BmIwN
+ldif
+entryAlreadyExists
+plaintext
+someoneelse
+errDisconnect
+UserName
+username
+accessee
+LDAPURLDesc
+ISOC
+IRTF
+jpeg
+ktadd
+tuple
+refint
+makeinfo
+chmod
+auditWriteObject
+Jong
+addressbooks
+setspec
+syncprov
+dctree
+hallvard
+cctrls
+debuglevel
+dSAOperation
+datadir
+slapadd
+reqFilter
+matcheddomain
+slapacl
+requestName
+randkey
+Cryptosystem
+groupOfNames
+themself
+jsmith
+filesystems
+lineno
+SASL's
+lockdetect
+addrdnvalues
+Hyuk
+rewriteContext
+soelim
+slapdconfig
+entrylimit
+departmentNumber
+immSupr
+addressbook
+pidfile
+online
+logold
+proxyattrset
+proxyAttrSet
+proxyAttrset
+mary
+crlcheck
+kadmin
+mech
+slapcat
+insufficientAccessRights
+XDEFS
+olcDbLinearIndex
+MKDEPFLAG
+rootdns
+caseExactIA
+notypes
+numericStringMatch
+octothorpe
+lltdl
+rootDSE
+rootdse
+logops
+rewriter
+chown
+attributeUsage
+slapdconf
+olcDbUri
+subany
+Authorizaiton
+bvalue
+manpage
+olcLimits
+PRNGD
+BerVarray
+abcdefgh
+matchingrule
+matchingRule
+modifiersName
+inetOrgPerson
+inetorgperson
+secprops
+logdb
+postaladdress
+postalAddress
+quanah
+ManageDsaIT
+manageDSAit
+subinitial
+procs
+varchar
+RDBMSes
+XLDFLAGS
+caseExactMatch
+urldesc
+usnChanged
+liblutil
+olcObjectIdentifier
+subdir
+suffixmassage
+auditAdd
+pwdMinAge
+olcModulePath
+URLattr
+reqSession
+login
+RetCodes
+userApplications
+NDBM
+newSuperiorDN
+auditBind
+setstyle
+newSuperior
+newsuperior
+concat
+realanonymous
+invalue
+refreshOnly
+pwcheck
+filesystem
+Naur
+unwillingToPerform
+PhotoURI
+MyCompany
+mkdep
+irresponsive
+PasswordModify
+readOnly
+readonly
+CLDAP
+proto
+mkdir
+peername
+pwdFailureTime
+compareDN
+reqVersion
+negttl
+logevels
+AAQSkZJRgABAAAAAQABAAD
+strcast
+aUihad
+failover
+constraintViolation
+cacheable
+sambaPwdCanChange
+errCode
+queryid
+olcReferral
+dynacl
+mkln
+structuralObjectClass
+proxyAuthz
+config
+IDSET
+odbc
+searchFilter
+wholeSubtree
+SASLprep
+nisMailAlias
+libodbcpsqlS
+OxObjects
+attributeDescription
+groupnummer
+lsei
+kurt
+OrgPerson
+generalizedTime
+filename
+pwdCheckQuality
+methodp
+Verdana
+deref
+proxied
+endmacro
+backload
+ECHOPROMPT
+bvarray
+ltdl
+slapdconfigfile
+modv
+ObjectClassDescription
+truelies
+basename
+groupOfUniqueNames
+DHAVE
+oPdklp
+ludp
+entryUUID
+ldapapiinfo
+SampleLDAP
+compareAttrDN
+lssl
+newentry
+applicatio
+addpartial
+confdir
+entryDN
+pwdFailureCountInterval
+XXXLIBS
+Kumar
+LTHREAD
+distinguishedNameMatch
+timestamp
+UUIDs
+olcDbCheckpoint
+LTINSTALL
+gssapi
+continuated
+localstatedir
+devel
+errcodep
+Elfrink
+olcPidFile
+attribute's
+pPasswd
+metadirectory
+Mitya
+myObjectClass
+OIDs
+oids
+sermersheim
+chainingPreferred
+CFLAGS
+minssf
+ModName
+attrs
+typeA
+objclasses
+typeB
+nelems
+subord
+namingViolation
+PCOq
+inappropriateAuthentication
+mixin
+suders
+syntaxOID
+olcTLSCACertificateFile
+IGJlZ
+userPrincipalName
+TLSCipherSuite
+auditlog
+runningslapd
+myLDAP
+myldap
+configs
+datasource
+refreshAndPersist
+authc
+PENs
+referralDN
+MANAGERDN
+noop
+errObject
+XXLIBS
+reqAssertion
+nops
+PDUs
+baseObject
+bvecadd
+perl
+inplace
+lossy
+pers
+authz
+pwdReset
+wrscdx
+adminLimitExceeded
+LDAPMessage
+serverctrlsp
+simplebinddn
+nonleaf
+compareFalse
+lsasl
+caseIgnoreSubstringsMatch
+AUTOREMOVE
+mydc
+searchResultEntry
+PIII
+olcDbShmKey
+substr
+testsaslauthd
+reqRespControls
+XXXXXXXXXX
+MANSECT
+bindmethod
+KTNAME
+referralsp
+pwdExpireWarning
+suretecsystems
+timeval
+LTLINK
+gsMatch
+attributeTypes
+pwdCheckModule
+olcDatabase
+PKCS
+syncuser
+oOjM
+extern
+dcObject
+supportedControl
+addprinc
+logbase
+oMxg
+filterlist
+generalizedTimeMatch
+strongAuthRequired
+Kovalev
+Google
+sessionlog
+balancer
+NSSR
+PKIX
+urandom
+derefFindingBaseObj
+Poitou
+dereferencing
+dereferenced
+ORed
+caseIgnoreSubstrin
+superset
+Locators
+qdstring
+olcAccess
+dereferences
+shoesize
+monitorContext
+RDBM
+PostgreSQL
+ppErrStr
+olcFrontendConfig
+aliasDereferencingProblem
+gsskrb
+unindexed
+whitespace
+seeAlso
+monitorRuntimeConfig
+olcAuditlogFile
+namingContexts
+referralAttrDN
+moddn
+calloc
+LDFLAGS
+attributeOrValueExists
+bsize
+auditObject
+dnssrv
+dynamicObject
+objectclass
+objectClass
+sizeLimitExceeded
+accountadm
+reqControls
+modme
+shtool
+aXRoIGEgc
+RDNs
+rdns
+modifyTimestamp
+objectIdentiferMatch
+sleeptime
+derefAliases
+pagedResults
+denyop
+sctrls
+ldapport
+octetString
+repl
+FakeOidIndex
+ERXRTc
+LxsdLy
+lastmod
+integerOrderingMatch
+sambaGroupType
+RowVersioning
+searchEntryDN
+pwdLockout
+sbin
+olcSuffix
+sbio
+posp
+TLSCertificateKeyFile
+george
+LDAPSyntax
+apache's
+scdx
+someuserid
+attrtype
+msgtype
+pathtest
+ldapcompare
+coltags
+sasl
+unixusers
+bvfree
+xeXBkeFxlZ
+priv
+proxyTemplates
+FileUsage
+bvals
+givenName
+givenname
+jensen
+auditReadObject
+proc
+unavailableCriticalExtension
+slapdn
+noSuchAttribute
+retcode
+slapds
+slapd's
+DLDAP
+TABs
+dyngroup
+pathspec
+domainstyle
+requestoid
+rpath
+Blowfish
+dryrun
+Poobah
+searchable
+SDSE
+olcDbDirectory
+ludpp
+spellcheck
+logsuccess
+lucyB
+entryUUIDs
+reqEntries
+sockbuf
+wrongpassword
+olcSaslSecprops
+olcSaslSecProps
+dnSubtreeMatch
+conns
+pcache
+ChangeLog
+changelog
+ursula
+monitorConnectionLocalAddress
+requestor's
+requestors
+TLSCertificateFile
+pwdPolicy
+infodir
+suretec
+tbls
+const
+bvdup
+mkversion
+olcDbSearchStack
+numericStringOrderingMatch
+checkpointed
+strongerAuthRequired
+treedelete
+olcObjectClasses
+berptr
+errSleepTime
+substrings
+slapd
+sambaNTPassword
+slapi
+lcrypto
+slapo
+mwrscdx
+credlen
+deleteDN
+substring
+prepending
+sldb
+credp
+numEntries
+searchBase
+searchbase
+berval
+slen
+metadata
+lookup
+databasetype
+rewriteRules
+smbk
+userCertificate
+entryCSN
+errAuxObject
+replogfile
+reloadhint
+reloadHint
+moduleload
+hasSubordinates
+ShowOidColumn
+contextp
+LDAPModifying
+nameAndOptionalUID
+addDN
+berval's
+bervals
+passwdfile
+reqDerefAliases
+authcDN
+groupstyle
+cancelled
+stateful
+proxytemplate
+proxyTemplate
+entryExpireTimestamp
+referralsPreferred
+authcID
+authcid
+AuthcId
+MChAODQ
+lookups
+GnuTLS
+gnutls
+LTONLY
+SNMP
+timelimit
+UCASE
+thru
+saslauthd
+logpurge
+SMTP
+srvtab
+ldapadd
+spasswd
+sprintf
+monitorCounterObject
+Instanstantiation
+olcLastMod
+vals
+param
+matcheddnp
+malloc
+XLIBS
+freeit
+invalidDNSyntax
+sambaSID
+zeilenga
+addAttrDN
+syncdata
+somedomain
+attrsonly
+attrsOnly
+numericString
+libexec
+entryCSNs
+noprompt
+LTCOMPILE
+ldapbis
+SSHA
+mandir
+RXER
+SSFs
+auditCompare
+pEntry
+strongAuthNotSupported
+endblock
+LDAPAVA
+startup
+sharedemail
+olcReplicationInterval
+TLSv
+libtool's
+slapindex
+rscdx
+dhparam
+subr
+SSLv
+SIGTERM
+liblunicode
+uint
+stringa
+reindex
+stringb
+lutil
+inetd
+SERATGCgaGBYWGDEjJR
+wahl
+olcDbQuarantine
+reqEnd
+modifyAttrDN
+monitorContainer
+searchstack
+cachefree
+errUnsolicitedOID
+WebUpdate
+RelativeLDAPDN
+URLlist
+monitorInfo
+argsfile
+attrvalue
+deallocate
+autogroup
+msgid
+ilOzQ
+modulepath
+logfile
+Supr
+inappropriateMatching
+SUPs
+myAttributeType
+BerValue
+basedn
+baseDN
+bvstr
+replog
+adressbooks
+databasenumber
+subschema
+PhotoObject
+INADDR
+pthread
+errlist
+olcDbIndex
+olcDbindex
+ldapext
+caseIgnoreMatch
+suffixalias
+sbindir
+gidNumber
+LDAPSync
+bitstring
+objclass
+oplist
+libodbcpsql
+LDAPObjectClass
+sockurl
+somevalue
+businessCategory
+getpid
+monitorIsShadow
+confidentialityRequired
+groupOfURLs
+preallocated
+hostname
+TTLs
+attrdesc
+ghenry
+odbcinst
+reqType
+slapover
+attributename
+lwrap
+reqStart
+errUnsolicitedData
+objectclasses
+objectClasses
+countp
+dereference
+sizelimit
+use'd
+rootdn
+RootDN
+LTFLAGS
+Bourne
+URIs
+pwdAttribute
+uppercased
+cacertdir
+ciphersuite
+URL's
+urls
+olcAuditLogConfig
+reqMod
+joebloggs
+pwdHistory
+entryTtl
+olcIdleTimeout
+TLSRandFile
+unmassaged
+LDAPMod
+ldapmod
+srcdir
+someSSHAdata
+whsp
+exattrs
+reqOld
+kbyte
+monitorCounter
+quickstart
+UUID
+olcConstraintConfig
+roleOccupant
+rootpw
+veryclean
+syslogged
+olcRootDN
+idletimeout
+sockname
+telephoneNumber
+telephonenumber
+objectClassModsProhibited
+nattrsets
+saslargs
+OBJEXT
+LDAPAttributeType
+newpasswdfile
+newPasswdFile
+boolean
+liblber
+ucdata
+toolsets
+builddir
+builtin
+matcheduid
+Locator
+ldapmaster
+olcMultiProvider
+libldap
+refreshDeletes
+aliasProblem
+eMail
+outvalue
+LDAPRDN
+olcBackendConfig
+wBDABALD
+libdir
+deleteoldrdn
+abcd
+olcRootPW
+dnattr
+Servername
+AttributeTypeDescription
+strdup
+domainScope
+prepended
+saslBindInProgress
+olcDbMode
+selfwrite
+olcLdapConfig
+pwdGraceUseTime
+titleCatalog
+woid
+organizationPerson
+ldaptcl
+INCDIR
+ACDF
+realusers
+ranlib
+eatBlanks
+reqMessage
+paramName
+ctrlp
+freebuf
+ctrls
+firstName
+ABNF
+dnpattern
+perror
+MSSQL
+VUld
+SmVuc
+ACIs
+errmsgp
+authzDN
+gunzip
+jpegPhoto
+supportedSASLMechanisms
+ACLs
+reqMethod
+authzId
+authzid
+authzID
+hasSubordintes
+proxyCache
+proxycache
+slaptest
+olcLogLevel
+LDAPDN
+XINCPATH
+monitoringslapd
+babs
+DSAIT
+olcHidden
+mySNMP
+metainformation
+ldapuri
+auditAbandon
+RANDFILE
+ldapurl
+strlen
+pwdAccountLockedTime
+searchAttrDN
+dbcache
+sambaPwdLastSet
+wBDARESEhgVG
+multi
+aaa
+ldaprc
+UpdateDN
+updatedn
+LDAPBASE
+LDAPAPIFeatureInfo
+authzTo
+valsort
+plugins
+Diffie
+ldappasswd
+olcGlobal
+ABI
+aci
+endif
+unescaped
+acl
+ADH
+olcPasswordHash
+ldapc
+loopback
+ldapi
+GETREALM
+functionalities
+noplain
+NOECHOPROMPT
+AES
+ldaps
+notoc
+LDAPv
+IPsec
+olcServerID
+BCP
+baz
+params
+generalizedTimeOrderingMatch
+ber
+slimit
+ali
+attributeoptions
+BfQ
+uidNumber
+CA's
+CAs
+namingContext
+mdb
+lmdb
+LMDB
+LMDB's
+MDB
+slapmodify
diff --git a/doc/guide/admin/backends.sdf b/doc/guide/admin/backends.sdf
new file mode 100644
index 0000000..37b4d3e
--- /dev/null
+++ b/doc/guide/admin/backends.sdf
@@ -0,0 +1,556 @@
+# $OpenLDAP$
+# Copyright 2007-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: Backends
+
+Backends do the actual work of storing or retrieving data in response
+to LDAP requests. Backends may be compiled statically into {{slapd}},
+or when module support is enabled, they may be dynamically loaded.
+
+If your installation uses dynamic modules, you may need to add the
+relevant {{moduleload}} directives to the examples that follow. The
+name of the module for a backend is usually of the form:
+
+> back_<backend name>.la
+
+So for example, if you need to load the {{mdb}} backend, you would configure
+
+> moduleload back_mdb.la
+
+H2: LDAP
+
+
+H3: Overview
+
+The LDAP backend to {{slapd}}(8) is not an actual database; instead it acts
+as a proxy to forward incoming requests to another LDAP server. While
+processing requests it will also chase referrals, so that referrals are fully
+processed instead of being returned to the {{slapd}} client.
+
+Sessions that explicitly {{Bind}} to the {{back-ldap}} database always create
+their own private connection to the remote LDAP server. Anonymous sessions
+will share a single anonymous connection to the remote server. For sessions
+bound through other mechanisms, all sessions with the same DN will share the
+same connection. This connection pooling strategy can enhance the proxy's
+efficiency by reducing the overhead of repeatedly making/breaking multiple
+connections.
+
+The ldap database can also act as an information service, i.e. the identity
+of locally authenticated clients is asserted to the remote server, possibly
+in some modified form. For this purpose, the proxy binds to the remote server
+with some administrative identity, and, if required, authorizes the asserted
+identity.
+
+It is heavily used by a lot of other {{SECT: Backends}} and {{SECT: Overlays}}.
+
+H3: back-ldap Configuration
+
+As previously mentioned, {{slapd-ldap(5)}} is used behind the scenes by many
+other {{SECT: Backends}} and {{SECT: Overlays}}. Some of them merely provide a
+few configuration directive themselves, but have available to the administrator
+the whole of the {{slapd-ldap(5)}} options.
+
+For example, the {{SECT: Translucent Proxy}}, which retrieves entries from a
+remote LDAP server that can be partially overridden by the defined database, has
+only four specific {{translucent-}} directives, but can be configured using any
+of the normal {{slapd-ldap(5)}} options. See {{slapo-translucent(5)}} for details.
+
+Other {{SECT: Overlays}} allow you to tag directives in front of a normal
+{{slapd-ldap(5)}} directive. For example, the {{slapo-chain(5)}} overlay does
+this:
+
+{{"There are very few chain overlay specific directives; however, directives
+related to the instances of the ldap backend that may be implicitly instantiated
+by the overlay may assume a special meaning when used in conjunction with this
+overlay. They are described in slapd-ldap(5), and they also need to be prefixed
+by chain-."}}
+
+You may have also seen the {{slapd-ldap(5)}} backend used and described in the
+{{SECT: Push Based}} {{SECT: Replication}} section of the guide.
+
+It should therefore be obvious that the {{slapd-ldap(5)}} backend is extremely
+flexible and heavily used throughout the OpenLDAP Suite.
+
+The following is a very simple example, but already the power of the {{slapd-ldap(5)}}
+backend is seen by use of a {{uri list}}:
+
+> database ldap
+> suffix "dc=suretecsystems,dc=com"
+> rootdn "cn=slapd-ldap"
+> uri ldap://localhost/ ldap://remotehost ldap://remotehost2
+
+The URI list is space or comma-separated. Whenever the server that responds
+is not the first one in the list, the list is rearranged and the responsive
+server is moved to the head, so that it will be first contacted the next time
+a connection needs be created.
+
+This feature can be used to provide a form of load balancing when using
+{{SECT: Mirror mode replication}}.
+
+H3: Further Information
+
+{{slapd-ldap}}(5)
+
+H2: LDIF
+
+
+H3: Overview
+
+The LDIF backend to {{slapd}}(8) is a basic storage backend that stores
+entries in text files in LDIF format, and exploits the filesystem to create
+the tree structure of the database. It is intended as a cheap, low performance
+easy to use backend.
+
+When using the {{cn=config}} dynamic configuration database with persistent
+storage, the configuration data is stored using this backend. See {{slapd-config}}(5)
+for more information
+
+H3: back-ldif Configuration
+
+Like many other backends, the LDIF backend can be instantiated with very few
+configuration lines:
+
+> include ./schema/core.schema
+>
+> database ldif
+> directory ./ldif
+> suffix "dc=suretecsystems,dc=com"
+> rootdn "cn=LDIF,dc=suretecsystems,dc=com"
+> rootpw LDIF
+
+If we add the {{dcObject}} for {{dc=suretecsystems,dc=com}}, you can see how this
+is added behind the scenes on the file system:
+
+> dn: dc=suretecsystems,dc=com
+> objectClass: dcObject
+> objectClass: organization
+> dc: suretecsystems
+> o: Suretec Systems Ltd
+
+Now we add it to the directory:
+
+> ldapadd -x -H ldap://localhost:9011 -f suretec.ldif -D "cn=LDIF,dc=suretecsystems,dc=com" -w LDIF
+> adding new entry "dc=suretecsystems,dc=com"
+
+And inside {{F: ./ldif}} we have:
+
+> ls ./ldif
+> dc=suretecsystems,dc=com.ldif
+
+which again contains:
+
+> cat ldif/dc\=suretecsystems\,dc\=com.ldif
+>
+> dn: dc=suretecsystems
+> objectClass: dcObject
+> objectClass: organization
+> dc: suretecsystems
+> o: Suretec Systems Ltd.
+> structuralObjectClass: organization
+> entryUUID: 2134b714-e3a1-102c-9a15-f96ee263886d
+> creatorsName: cn=LDIF,dc=suretecsystems,dc=com
+> createTimestamp: 20080711142643Z
+> entryCSN: 20080711142643.661124Z#000000#000#000000
+> modifiersName: cn=LDIF,dc=suretecsystems,dc=com
+> modifyTimestamp: 20080711142643Z
+
+This is the complete format you would get when exporting your directory using
+{{F: slapcat}} etc.
+
+H3: Further Information
+
+{{slapd-ldif}}(5)
+
+H2: LMDB
+
+
+H3: Overview
+
+The {{mdb}} backend to {{slapd}}(8) is the recommended primary backend for a
+normal {{slapd}} database. It uses OpenLDAP's own
+Lightning Memory-Mapped Database ({{TERM:LMDB}})
+library to store data and replaces the BerkeleyDB backends used in older
+OpenLDAP releases.
+
+It supports indexing, it uses no caching, and requires no tuning to deliver
+maximum search performance. It is fully hierarchical and supports subtree
+renames in constant time.
+
+H3: back-mdb Configuration
+
+The {{mdb}} backend can be instantiated with very few configuration lines:
+
+> include ./schema/core.schema
+>
+> database mdb
+> directory ./mdb
+> suffix "dc=suretecsystems,dc=com"
+> rootdn "cn=mdb,dc=suretecsystems,dc=com"
+> rootpw mdb
+> maxsize 1073741824
+
+In addition to the usual parameters that a minimal configuration requires, the {{mdb}}
+backend requires a maximum size to be set. This should be the largest that
+the database is ever anticipated to grow (in bytes). The filesystem must also
+provide enough free space to accommodate this size.
+
+H3: Further Information
+
+{{slapd-mdb}}(5)
+
+H2: Metadirectory
+
+
+H3: Overview
+
+The meta backend to {{slapd}}(8) performs basic LDAP proxying with respect
+to a set of remote LDAP servers, called "targets". The information contained
+in these servers can be presented as belonging to a single Directory Information
+Tree ({{TERM:DIT}}).
+
+A basic knowledge of the functionality of the {{slapd-ldap}}(5) backend is
+recommended. This backend has been designed as an enhancement of the ldap
+backend. The two backends share many features (actually they also share portions
+ of code). While the ldap backend is intended to proxy operations directed
+ to a single server, the meta backend is mainly intended for proxying of
+ multiple servers and possibly naming context masquerading.
+
+These features, although useful in many scenarios, may result in excessive
+overhead for some applications, so its use should be carefully considered.
+
+
+H3: back-meta Configuration
+
+LATER
+
+H3: Further Information
+
+{{slapd-meta}}(5)
+
+H2: Monitor
+
+
+H3: Overview
+
+The monitor backend to {{slapd}}(8) is not an actual database; if enabled,
+it is automatically generated and dynamically maintained by slapd with
+information about the running status of the daemon.
+
+To inspect all monitor information, issue a subtree search with base {{cn=Monitor}},
+requesting that attributes "+" and "*" are returned. The monitor backend produces
+mostly operational attributes, and LDAP only returns operational attributes
+that are explicitly requested. Requesting attribute "+" is an extension which
+requests all operational attributes.
+
+See the {{SECT:Monitoring}} section.
+
+H3: back-monitor Configuration
+
+The monitor database can be instantiated only once, i.e. only one occurrence
+of "database monitor" can occur in the {{slapd.conf(5)}} file. Also the suffix
+is automatically set to {{"cn=Monitor"}}.
+
+You can however set a {{rootdn}} and {{rootpw}}. The following is all that is
+needed to instantiate a monitor backend:
+
+> include ./schema/core.schema
+>
+> database monitor
+> rootdn "cn=monitoring,cn=Monitor"
+> rootpw monitoring
+
+You can also apply Access Control to this database like any other database, for
+example:
+
+> access to dn.subtree="cn=Monitor"
+> by dn.exact="uid=Admin,dc=my,dc=org" write
+> by users read
+> by * none
+
+Note: The {{F: core.schema}} must be loaded for the monitor database to work.
+
+A small example of the data returned via {{ldapsearch}} would be:
+
+> ldapsearch -x -H ldap://localhost:9011 -b 'cn=Monitor'
+> # extended LDIF
+> #
+> # LDAPv3
+> # base <cn=Monitor> with scope subtree
+> # filter: (objectclass=*)
+> # requesting: ALL
+> #
+>
+> # Monitor
+> dn: cn=Monitor
+> objectClass: monitorServer
+> cn: Monitor
+> description: This subtree contains monitoring/managing objects.
+> description: This object contains information about this server.
+> description: Most of the information is held in operational attributes, which
+> must be explicitly requested.
+>
+> # Backends, Monitor
+> dn: cn=Backends,cn=Monitor
+> objectClass: monitorContainer
+> cn: Backends
+> description: This subsystem contains information about available backends.
+
+Please see the {{SECT: Monitoring}} section for complete examples of information
+available via this backend.
+
+H3: Further Information
+
+{{slapd-monitor}}(5)
+
+H2: Null
+
+
+H3: Overview
+
+The Null backend to {{slapd}}(8) is surely the most useful part of slapd:
+
+* Searches return success but no entries.
+* Compares return compareFalse.
+* Updates return success (unless readonly is on) but do nothing.
+* Binds other than as the rootdn fail unless the database option "bind on" is given.
+* The slapadd(8) and slapcat(8) tools are equally exciting.
+
+Inspired by the {{F:/dev/null}} device.
+
+H3: back-null Configuration
+
+This has to be one of the shortest configurations you'll ever do. In order to
+test this, your {{F: slapd.conf}} file would look like:
+
+> database null
+> suffix "cn=Nothing"
+> bind on
+
+{{bind on}} means:
+
+{{"Allow binds as any DN in this backend's suffix, with any password. The default is "off"."}}
+
+To test this backend with {{ldapsearch}}:
+
+> ldapsearch -x -H ldap://localhost:9011 -D "uid=none,cn=Nothing" -w testing -b 'cn=Nothing'
+> # extended LDIF
+> #
+> # LDAPv3
+> # base <cn=Nothing> with scope subtree
+> # filter: (objectclass=*)
+> # requesting: ALL
+> #
+>
+> # search result
+> search: 2
+> result: 0 Success
+>
+> # numResponses: 1
+
+
+H3: Further Information
+
+{{slapd-null}}(5)
+
+H2: Passwd
+
+
+H3: Overview
+
+The PASSWD backend to {{slapd}}(8) serves up the user account information
+listed in the system {{passwd}}(5) file (defaulting to {{F: /etc/passwd}}).
+
+This backend is provided for demonstration purposes only. The DN of each entry
+is "uid=<username>,<suffix>".
+
+H3: back-passwd Configuration
+
+The configuration using {{F: slapd.conf}} a slightly longer, but not much. For
+example:
+
+> include ./schema/core.schema
+>
+> database passwd
+> suffix "cn=passwd"
+
+Again, testing this with {{ldapsearch}} would result in something like:
+
+> ldapsearch -x -H ldap://localhost:9011 -b 'cn=passwd'
+> # extended LDIF
+> #
+> # LDAPv3
+> # base <cn=passwd> with scope subtree
+> # filter: (objectclass=*)
+> # requesting: ALL
+> #
+>
+> # passwd
+> dn: cn=passwd
+> cn: passwd
+> objectClass: organizationalUnit
+>
+> # root, passwd
+> dn: uid=root,cn=passwd
+> objectClass: person
+> objectClass: uidObject
+> uid: root
+> cn: root
+> sn: root
+> description: root
+
+
+H3: Further Information
+
+{{slapd-passwd}}(5)
+
+H2: Perl
+
+H3: Overview
+
+The Perl backend to {{slapd}}(8) works by embedding a {{perl}}(1) interpreter
+into {{slapd}}(8). Any perl database section of the configuration file
+{{slapd.conf}}(5) must then specify what Perl module to use. Slapd then creates
+a new Perl object that handles all the requests for that particular instance of the backend.
+
+H3: back-perl Configuration
+
+LATER
+
+H3: Further Information
+
+{{slapd-perl}}(5)
+
+H2: Relay
+
+
+H3: Overview
+
+The primary purpose of this {{slapd}}(8) backend is to map a naming context
+defined in a database running in the same {{slapd}}(8) instance into a
+virtual naming context, with attributeType and objectClass manipulation, if
+required. It requires the rwm overlay.
+
+This backend and the above mentioned overlay are experimental.
+
+H3: back-relay Configuration
+
+LATER
+
+H3: Further Information
+
+{{slapd-relay}}(5)
+
+H2: SQL
+
+
+H3: Overview
+
+The primary purpose of this {{slapd}}(8) backend is to PRESENT information
+stored in some RDBMS as an LDAP subtree without any programming (some SQL and
+maybe stored procedures can't be considered programming, anyway ;).
+
+That is, for example, when you (some ISP) have account information you use in
+an RDBMS, and want to use modern solutions that expect such information in LDAP
+(to authenticate users, make email lookups etc.). Or you want to synchronize or
+distribute information between different sites/applications that use RDBMSes
+and/or LDAP. Or whatever else...
+
+It is {{B:NOT}} designed as a general-purpose backend that uses RDBMS instead of
+LMDB (as the standard back-mdb backend does), though it can be used as such with
+several limitations. Please see {{SECT: LDAP vs RDBMS}} for discussion.
+
+The idea is to use some meta-information to translate LDAP queries to SQL queries,
+leaving relational schema untouched, so that old applications can continue using
+it without any modifications. This allows SQL and LDAP applications to interoperate
+without replication, and exchange data as needed.
+
+The SQL backend is designed to be tunable to virtually any relational schema without
+having to change source (through that meta-information mentioned). Also, it uses
+ODBC to connect to RDBMSes, and is highly configurable for SQL dialects RDBMSes
+may use, so it may be used for integration and distribution of data on different
+RDBMSes, OSes, hosts etc., in other words, in highly heterogeneous environments.
+
+This backend is experimental and deprecated.
+
+H3: back-sql Configuration
+
+This backend has to be one of the most abused and complex backends there is.
+Therefore, we will go through a simple, small example that comes with the
+OpenLDAP source and can be found in {{F: servers/slapd/back-sql/rdbms_depend/README}}
+
+For this example we will be using PostgreSQL.
+
+First, we add to {{F: /etc/odbc.ini}} a block of the form:
+
+> [example] <===
+> Description = Example for OpenLDAP's back-sql
+> Driver = PostgreSQL
+> Trace = No
+> Database = example <===
+> Servername = localhost
+> UserName = manager <===
+> Password = secret <===
+> Port = 5432
+> ;Protocol = 6.4
+> ReadOnly = No
+> RowVersioning = No
+> ShowSystemTables = No
+> ShowOidColumn = No
+> FakeOidIndex = No
+> ConnSettings =
+
+The relevant information for our test setup is highlighted with '<===' on the
+right above.
+
+Next, we add to {{F: /etc/odbcinst.ini}} a block of the form:
+
+> [PostgreSQL]
+> Description = ODBC for PostgreSQL
+> Driver = /usr/lib/libodbcpsql.so
+> Setup = /usr/lib/libodbcpsqlS.so
+> FileUsage = 1
+
+
+We will presume you know how to create a database and user in PostgreSQL and
+how to set a password. Also, we'll presume you can populate the 'example'
+database you've just created with the following files, as found in {{F: servers/slapd/back-sql/rdbms_depend/pgsql }}
+
+> backsql_create.sql, testdb_create.sql, testdb_data.sql, testdb_metadata.sql
+
+Lastly, run the test:
+
+> [root@localhost]# cd $SOURCES/tests
+> [root@localhost]# SLAPD_USE_SQL=pgsql ./run sql-test000
+
+Briefly, you should see something like (cut short for space):
+
+> Cleaning up test run directory leftover from previous run.
+> Running ./scripts/sql-test000-read...
+> running defines.sh
+> Starting slapd on TCP/IP port 9011...
+> Testing SQL backend read operations...
+> Waiting 5 seconds for slapd to start...
+> Testing correct bind... dn:cn=Mitya Kovalev,dc=example,dc=com
+> Testing incorrect bind (should fail)... ldap_bind: Invalid credentials (49)
+>
+> ......
+>
+> Filtering original ldif...
+> Comparing filter output...
+> >>>>> Test succeeded
+
+The test is basically readonly; this can be performed by all RDBMSes
+(listed above).
+
+There is another test, sql-test900-write, which is currently enabled
+only for PostgreSQL and IBM db2.
+
+Using {{F: sql-test000}}, files in {{F: servers/slapd/back-sql/rdbms_depend/pgsql/}}
+and the man page, you should be set.
+
+Note: This backend is experimental and deprecated.
+
+H3: Further Information
+
+{{slapd-sql}}(5) and {{F: servers/slapd/back-sql/rdbms_depend/README}}
diff --git a/doc/guide/admin/booktitle.sdf b/doc/guide/admin/booktitle.sdf
new file mode 100644
index 0000000..8d38008
--- /dev/null
+++ b/doc/guide/admin/booktitle.sdf
@@ -0,0 +1,34 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+#
+# Document: OpenLDAP Administrator's Guide
+# Master: master.sdf
+#
+
+!include "../preamble.sdf"
+
+!define DOC_TOC 3
+!define DOC_TYPE "Administrator's Guide"
+
+!macro build_html_cover
+!block inline
+<DIV align="Center">
+!endblock
+!default DOC_HTML_LOGO_BASE $var{'DOC_LOGO_BASE'}
+!import "../images/LDAPwww.gif"; base=$var{'DOC_HTML_LOGO_BASE'}
+P1[notoc; class='doc-title'] [[DOC_TITLE]]
+Sign[class='doc-modified'] [[DATE:DOC_MODIFIED]]
+!block inline
+</DIV>
+!endblock
+!endmacro
+
+!macro HTML_HEADER
+!endmacro
+
+!macro HTML_FOOTER
+!endmacro
+
+!build_title
+
diff --git a/doc/guide/admin/config.sdf b/doc/guide/admin/config.sdf
new file mode 100644
index 0000000..e21725d
--- /dev/null
+++ b/doc/guide/admin/config.sdf
@@ -0,0 +1,70 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+H1: The Big Picture - Configuration Choices
+
+This section gives a brief overview of various {{TERM:LDAP}} directory
+configurations, and how your Standalone LDAP Daemon {{slapd}}(8)
+fits in with the rest of the world.
+
+
+H2: Local Directory Service
+
+In this configuration, you run a {{slapd}}(8) instance which provides
+directory service for your local domain only. It does not interact
+with other directory servers in any way. This configuration is shown
+in Figure 3.1.
+
+!import "config_local.png"; align="center"; title="Local service via slapd(8) configuration"
+FT[align="Center"] Figure 3.1: Local service configuration.
+
+Use this configuration if you are just starting out (it's the one the
+quick-start guide makes for you) or if you want to provide a local
+service and are not interested in connecting to the rest of the world.
+It's easy to upgrade to another configuration later if you want.
+
+
+H2: Local Directory Service with Referrals
+
+In this configuration, you run a {{slapd}}(8) instance which provides
+directory service for your local domain and configure it to return
+referrals to other servers capable of handling requests. You may
+run this service (or services) yourself or use one provided to you.
+This configuration is shown in Figure 3.2.
+
+!import "config_ref.png"; align="center"; title="Local service with referrals"
+FT[align="Center"] Figure 3.2: Local service with referrals
+
+Use this configuration if you want to provide local service and
+participate in the Global Directory, or you want to delegate
+responsibility for {{subordinate}} entries to another server.
+
+
+H2: Replicated Directory Service
+
+slapd(8) includes support for {{LDAP Sync}}-based replication, called
+{{syncrepl}}, which may be used to maintain shadow copies of directory
+information on multiple directory servers. In its most basic
+configuration, the {{provider}} is a syncrepl provider and one or more
+{{consumer}} (or {{shadow}}) are syncrepl consumers. An example
+provider-consumer configuration is shown in figure 3.3. Multi-Provider
+configurations are also supported.
+
+!import "config_repl.png"; align="center"; title="Replicated Directory Services"
+FT[align="Center"] Figure 3.3: Replicated Directory Services
+
+This configuration can be used in conjunction with either of the
+first two configurations in situations where a single {{slapd}}(8)
+instance does not provide the required reliability or availability.
+
+H2: Distributed Local Directory Service
+
+In this configuration, the local service is partitioned into smaller
+services, each of which may be replicated, and {{glued}} together with
+{{superior}} and {{subordinate}} referrals.
+!if 0
+An example of this configuration is shown in Figure 3.4.
+
+!import "config_dist.gif"; align="center"; title="Distributed Local Directory Services"
+FT[align="Center"] Figure 3.4: Distributed Local Directory Services
+!endif
diff --git a/doc/guide/admin/config_dit.png b/doc/guide/admin/config_dit.png
new file mode 100644
index 0000000..fd51f29
--- /dev/null
+++ b/doc/guide/admin/config_dit.png
Binary files differ
diff --git a/doc/guide/admin/config_local.png b/doc/guide/admin/config_local.png
new file mode 100644
index 0000000..5337c7f
--- /dev/null
+++ b/doc/guide/admin/config_local.png
Binary files differ
diff --git a/doc/guide/admin/config_ref.png b/doc/guide/admin/config_ref.png
new file mode 100644
index 0000000..cca3dde
--- /dev/null
+++ b/doc/guide/admin/config_ref.png
Binary files differ
diff --git a/doc/guide/admin/config_repl.png b/doc/guide/admin/config_repl.png
new file mode 100644
index 0000000..9525279
--- /dev/null
+++ b/doc/guide/admin/config_repl.png
Binary files differ
diff --git a/doc/guide/admin/dbtools.sdf b/doc/guide/admin/dbtools.sdf
new file mode 100644
index 0000000..df29ead
--- /dev/null
+++ b/doc/guide/admin/dbtools.sdf
@@ -0,0 +1,382 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: Database Creation and Maintenance Tools
+
+This section tells you how to create a slapd database from scratch,
+and how to do trouble shooting if you run into problems. There are
+two ways to create a database. First, you can create the database
+on-line using {{TERM:LDAP}}. With this method, you simply start up slapd
+and add entries using the LDAP client of your choice. This method
+is fine for relatively small databases (a few hundred or thousand
+entries, depending on your requirements). This method works for
+database types which support updates.
+
+The second method of database creation is to do it off-line using
+special utilities provided with {{slapd}}(8). This method is best if you
+have many thousands of entries to create, which would take an
+unacceptably long time using the LDAP method, or if you want to
+ensure the database is not accessed while it is being created. Note
+that not all database types support these utilities.
+
+
+H2: Creating a database over LDAP
+
+With this method, you use the LDAP client of your choice (e.g.,
+the {{ldapadd}}(1)) to add entries, just like you would once the
+database is created. You should be sure to set the following
+options in the configuration file before starting {{slapd}}(8).
+
+> suffix <dn>
+
+As described in the {{SECT:General Database Directives}} section,
+this option defines which entries are to be held by this database.
+You should set this to the DN of the root of the subtree you are
+trying to create. For example:
+
+> suffix "dc=example,dc=com"
+
+You should be sure to specify a directory where the index files
+should be created:
+
+> directory <directory>
+
+For example:
+
+> directory /usr/local/var/openldap-data
+
+You need to create this directory with appropriate permissions such
+that slapd can write to it.
+
+You need to configure slapd so that you can connect to it as a
+directory user with permission to add entries. You can configure
+the directory to support a special {{super-user}} or {{root}} user
+just for this purpose. This is done through the following two
+options in the database definition:
+
+> rootdn <dn>
+> rootpw <passwd>
+
+For example:
+
+> rootdn "cn=Manager,dc=example,dc=com"
+> rootpw secret
+
+These options specify a DN and password that can be used to
+authenticate as the {{super-user}} entry of the database (i.e.,
+the entry allowed to do anything). The DN and password specified
+here will always work, regardless of whether the entry named actually
+exists or has the password given. This solves the chicken-and-egg
+problem of how to authenticate and add entries before any entries
+yet exist.
+
+Finally, you should make sure that the database definition contains
+the index definitions you want:
+
+> index {<attrlist> | default} [pres,eq,approx,sub,none]
+
+For example, to index the {{EX:cn}}, {{EX:sn}}, {{EX:uid}} and
+{{EX:objectclass}} attributes, the following {{EX:index}} directives
+could be used:
+
+> index cn,sn,uid pres,eq,approx,sub
+> index objectClass eq
+
+This would create presence, equality, approximate, and substring
+indices for the {{EX:cn}}, {{EX:sn}}, and {{EX:uid}} attributes and
+an equality index for the {{EX:objectClass}} attribute. Note that
+not all index types are available with all attribute types. See
+{{SECT:The slapd Configuration File}} section for more information
+on this option.
+
+Once you have configured things to your liking, start up slapd,
+connect with your LDAP client, and start adding entries. For
+example, to add an organization entry and an organizational role
+entry using the {{I:ldapadd}} tool, you could create an {{TERM:LDIF}}
+file called {{EX:entries.ldif}} with the contents:
+
+> # Organization for Example Corporation
+> dn: dc=example,dc=com
+> objectClass: dcObject
+> objectClass: organization
+> dc: example
+> o: Example Corporation
+> description: The Example Corporation
+>
+> # Organizational Role for Directory Manager
+> dn: cn=Manager,dc=example,dc=com
+> objectClass: organizationalRole
+> cn: Manager
+> description: Directory Manager
+
+and then use a command like this to actually create the entry:
+
+> ldapadd -f entries.ldif -x -D "cn=Manager,dc=example,dc=com" -w secret
+
+The above command assumes settings provided in the above examples.
+
+
+H2: Creating a database off-line
+
+The second method of database creation is to do it off-line, using
+the slapd database tools described below. This method is best if
+you have many thousands of entries to create, which would take an
+unacceptably long time to add using the LDAP method described above.
+These tools read the slapd configuration file and an input file
+containing a text representation of the entries to add. For database
+types which support the tools, they produce the database files
+directly (otherwise you must use the on-line method above). Also,
+the input file must be completely valid, as these tools do fewer
+consistency checks than the on-line method.
+
+Note: this Guide is not meant to provide exhaustive documentation
+on the software. The tool descriptions here only list a few of the
+available options for each command. Read the associated manpages for
+complete documentation on all of the available options.
+
+There are several important configuration options you will want to be
+sure and set in the config file database definition first:
+
+> suffix <dn>
+
+As described in the {{SECT:General Database Directives}} section,
+this option defines which entries are to be held by this database.
+You should set this to the DN of the root of the subtree you are
+trying to create. For example:
+
+> suffix "dc=example,dc=com"
+
+You should be sure to specify a directory where the index files
+should be created:
+
+> directory <directory>
+
+For example:
+
+> directory /usr/local/var/openldap-data
+
+Finally, you need to specify which indices you want to build. This
+is done by one or more index options.
+
+> index {<attrlist> | default} [pres,eq,approx,sub,none]
+
+For example:
+
+> index cn,sn,uid pres,eq,approx,sub
+> index objectClass eq
+
+This would create presence, equality, approximate, and substring
+indices for the {{EX:cn}}, {{EX:sn}}, and {{EX:uid}} attributes and
+an equality index for the {{EX:objectClass}} attribute. Note that
+not all index types are available with all attribute types. See
+{{SECT:The slapd Configuration File}} section for more information
+on this option.
+
+H3: The {{EX:slapadd}} program
+
+Once you've configured things to your liking, you create the primary
+database and associated indices by running the {{slapadd}}(8)
+program:
+
+> slapadd -l <inputfile> -f <slapdconfigfile>
+> [-d <debuglevel>] [-n <integer>|-b <suffix>]
+
+The arguments have the following meanings:
+
+> -l <inputfile>
+
+Specifies the {{TERM:LDIF}} input file containing the entries to
+add in text form (described below in the {{SECT:The LDIF text entry
+format}} section).
+
+> -f <slapdconfigfile>
+
+Specifies the slapd configuration file that tells where to create
+the indices, what indices to create, etc.
+
+> -F <slapdconfdirectory>
+
+Specifies a config directory. If both {{EX:-f}} and {{EX:-F}} are specified,
+the config file will be read and converted to config directory format and
+written to the specified directory. If neither option is specified, an attempt
+to read the default config directory will be made before trying to use the
+default config file. If a valid config directory exists then the default
+config file is ignored. If dryrun mode is also specified, no conversion will occur.
+
+> -d <debuglevel>
+
+Turn on debugging, as specified by {{EX:<debuglevel>}}. The debug
+levels are the same as for slapd. See the {{SECT:Command-Line
+Options}} section in {{SECT:Running slapd}}.
+
+> -n <databasenumber>
+
+An optional argument that specifies which database to modify. The
+first database listed in the configuration file is {{EX:1}}, the
+second {{EX:2}}, etc. By default, the first database in the
+configuration file is used. Should not be used in conjunction with
+{{EX:-b}}.
+
+> -b <suffix>
+
+An optional argument that specifies which database to modify. The
+provided suffix is matched against a database {{EX:suffix}} directive
+to determine the database number. Should not be used in conjunction
+with {{EX:-n}}.
+
+
+H3: The {{EX:slapindex}} program
+
+Sometimes it may be necessary to regenerate indices (such as after
+modifying {{slapd.conf}}(5)). This is possible using the {{slapindex}}(8)
+program. {{slapindex}} is invoked like this
+
+> slapindex -f <slapdconfigfile>
+> [-d <debuglevel>] [-n <databasenumber>|-b <suffix>] [attr...]
+
+Where the {{EX:-f}}, {{EX:-d}}, {{EX:-n}} and {{EX:-b}} options
+are the same as for the {{slapadd}}(1) program. If no specific
+attributes are listed, {{slapindex}} rebuilds all indices based
+upon the current database contents.
+
+
+H3: The {{EX:slapcat}} program
+
+The {{EX:slapcat}} program is used to dump the database to an
+{{TERM:LDIF}} file. This can be useful when you want to make a
+human-readable backup of your database or when you want to edit
+your database off-line. The program is invoked like this:
+
+> slapcat -l <filename> -f <slapdconfigfile>
+> [-d <debuglevel>] [-n <databasenumber>|-b <suffix>]
+
+where {{EX:-n}} or {{EX:-b}} is used to select the database in the
+{{slapd.conf}}(5) specified using {{EX:-f}}. The corresponding
+{{TERM:LDIF}} output is written to standard output or to the file
+specified using the {{EX:-l}} option.
+
+
+!if 0
+H3: The {{EX:ldif}} program
+
+The {{ldif}}(1) program is used to convert arbitrary data values
+to {{TERM:LDIF}} format. This can be useful when writing a program
+or script to create the LDIF file you will feed into the {{slapadd}}(8)
+or {{ldapadd}}(1) program, or when writing a SHELL backend.
+{{ldif}}(1) takes an attribute description as an argument and reads
+the attribute value(s) from standard input. It produces the LDIF
+formatted attribute line(s) on standard output. The usage is:
+
+> ldif [-b] <attrdesc>
+
+where {{EX:<attrdesc>}} is an attribute description. Without the
+{{EX-b}} option, the {{ldif}} program will consider each line of
+standard input to be a separate value of the attribute.
+
+> ldif description << EOF
+> leading space
+> # leading hash mark
+> EOF
+
+The {{EX:-b}} option can be used to force the {{ldif}} program to
+interpret its input as a single raw binary value. This option is
+useful when converting binary data such as a {{EX:jpegPhoto}} or
+{{EX:audio}} attribute. For example:
+
+> ldif -b jpegPhoto < photo.jpeg
+!endif
+
+
+H2: The LDIF text entry format
+
+The {{TERM[expand]LDIF}} (LDIF) is used to represent LDAP entries
+in a simple text format. This section provides a brief description
+of the LDIF entry format which complements {{ldif}}(5) and the
+technical specification {{REF:RFC2849}}.
+
+The basic form of an entry is:
+
+> # comment
+> dn: <distinguished name>
+> <attrdesc>: <attrvalue>
+> <attrdesc>: <attrvalue>
+>
+> ...
+
+Lines starting with a '{{EX:#}}' character are comments. An
+attribute description may be a simple attribute type like {{EX:cn}}
+or {{EX:objectClass}} or {{EX:1.2.3}} (an {{TERM:OID}} associated
+with an attribute type) or may include options such as {{EX:cn;lang_en_US}}
+or {{EX:userCertificate;binary}}.
+
+A line may be continued by starting the next line with a {{single}}
+space or tab character. For example:
+
+> dn: cn=Barbara J Jensen,dc=example,dc=
+> com
+> cn: Barbara J
+> Jensen
+
+is equivalent to:
+
+> dn: cn=Barbara J Jensen,dc=example,dc=com
+> cn: Barbara J Jensen
+
+Multiple attribute values are specified on separate lines. e.g.,
+
+> cn: Barbara J Jensen
+> cn: Babs Jensen
+
+If an {{EX:<attrvalue>}} contains non-printing characters or begins
+with a space, a colon ('{{EX::}}'), or a less than ('{{EX:<}}'),
+the {{EX:<attrdesc>}} is followed by a double colon and the base64
+encoding of the value. For example, the value "{{EX: begins with
+a space}}" would be encoded like this:
+
+> cn:: IGJlZ2lucyB3aXRoIGEgc3BhY2U=
+
+You can also specify a {{TERM:URL}} containing the attribute value.
+For example, the following specifies the {{EX:jpegPhoto}} value
+should be obtained from the file {{F:/path/to/file.jpeg}}.
+
+> jpegPhoto:< file:///path/to/file.jpeg
+
+Multiple entries within the same LDIF file are separated by blank
+lines. Here's an example of an LDIF file containing three entries.
+
+> # Barbara's Entry
+> dn: cn=Barbara J Jensen,dc=example,dc=com
+> cn: Barbara J Jensen
+> cn: Babs Jensen
+> objectClass: person
+> sn: Jensen
+>
+> # Bjorn's Entry
+> dn: cn=Bjorn J Jensen,dc=example,dc=com
+> cn: Bjorn J Jensen
+> cn: Bjorn Jensen
+> objectClass: person
+> sn: Jensen
+> # Base64 encoded JPEG photo
+> jpegPhoto:: /9j/4AAQSkZJRgABAAAAAQABAAD/2wBDABALD
+> A4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQ
+> ERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVG
+>
+> # Jennifer's Entry
+> dn: cn=Jennifer J Jensen,dc=example,dc=com
+> cn: Jennifer J Jensen
+> cn: Jennifer Jensen
+> objectClass: person
+> sn: Jensen
+> # JPEG photo from file
+> jpegPhoto:< file:///path/to/file.jpeg
+
+Notice that the {{EX:jpegPhoto}} in Bjorn's entry is base 64 encoded
+and the {{EX:jpegPhoto}} in Jennifer's entry is obtained from the
+location indicated by the URL.
+
+Note: Trailing spaces are not trimmed from values in an LDIF file.
+Nor are multiple internal spaces compressed. If you don't want them
+in your data, don't put them there.
+
diff --git a/doc/guide/admin/delta-syncrepl.png b/doc/guide/admin/delta-syncrepl.png
new file mode 100644
index 0000000..ca1da14
--- /dev/null
+++ b/doc/guide/admin/delta-syncrepl.png
Binary files differ
diff --git a/doc/guide/admin/dual_dc.png b/doc/guide/admin/dual_dc.png
new file mode 100644
index 0000000..367310f
--- /dev/null
+++ b/doc/guide/admin/dual_dc.png
Binary files differ
diff --git a/doc/guide/admin/glossary.sdf b/doc/guide/admin/glossary.sdf
new file mode 100644
index 0000000..bf04454
--- /dev/null
+++ b/doc/guide/admin/glossary.sdf
@@ -0,0 +1,16 @@
+# $OpenLDAP$
+# Copyright 2006-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+H1: Glossary
+
+H2: Terms
+!catalog terms ''; headings; columns="Term,Definition"
+
+H2: Related Organizations
+!catalog organisations ''; headings; columns="ORG:Name,Long,URL:Jump"
+
+H2: Related Products
+!catalog products ''; headings; columns="PRD:Name,URL:Jump"
+
+H2: References
+!catalog references ''; headings; columns="REF:Reference,Document,Status,URL:Jump"
diff --git a/doc/guide/admin/guide.book b/doc/guide/admin/guide.book
new file mode 100644
index 0000000..28e10c2
--- /dev/null
+++ b/doc/guide/admin/guide.book
@@ -0,0 +1,3 @@
+#HTMLDOC 1.8.27
+-t pdf14 --book --toclevels 3 --no-numbered --toctitle "Table of Contents" --title --titlefile "booktitle.html" --linkstyle plain --size Universal --left 1.00in --right 0.50in --top 0.50in --bottom 0.50in --header .t. --header1 ... --footer ..1 --nup 1 --tocheader .t. --tocfooter ..i --duplex --portrait --color --no-pscommands --no-xrxcomments --compression=1 --jpeg=0 --fontsize 11.0 --fontspacing 1.2 --headingfont Helvetica --bodyfont Times --headfootsize 11.0 --headfootfont Helvetica --charset iso-8859-1 --links --embedfonts --pagemode outline --pagelayout single --firstpage p1 --pageeffect none --pageduration 10 --effectduration 1.0 --no-encryption --permissions all --owner-password "" --user-password "" --browserwidth 680 --no-strict --no-overflow
+admin.html
diff --git a/doc/guide/admin/guide.html b/doc/guide/admin/guide.html
new file mode 100644
index 0000000..7e924c8
--- /dev/null
+++ b/doc/guide/admin/guide.html
@@ -0,0 +1,11454 @@
+<!doctype html public "-//W30//DTD W3 HTML 2.0//EN">
+
+<HTML>
+
+<!-- This file was generated using SDF 2.001 by
+ Ian Clatworthy (ianc@mincom.com). SDF is freely
+ available from http://www.mincom.com/mtr/sdf. -->
+
+<HEAD>
+<TITLE>OpenLDAP Software 2.5 Administrator's Guide</TITLE>
+</HEAD>
+<BODY>
+
+<DIV CLASS="header">
+<A HREF="https://www.OpenLDAP.org/">
+<P><IMG SRC="../images/LDAPlogo.gif" ALIGN="Left" BORDER=0></P>
+</A>
+<DIV CLASS="navigate">
+<P ALIGN="Center"><A HREF="https://www.openldap.org/">Home</A> | <A HREF="../index.html">Catalog</A></P>
+</DIV>
+<BR CLEAR="Left">
+</DIV>
+<DIV CLASS="title">
+<H1 CLASS="doc-title">OpenLDAP Software 2.5 Administrator's Guide</H1>
+<ADDRESS CLASS="doc-author">The OpenLDAP Project &lt;<A HREF="https://www.openldap.org/">https://www.openldap.org/</A>&gt;</ADDRESS>
+<ADDRESS CLASS="doc-modified">14 July 2022</ADDRESS>
+<BR CLEAR="All">
+</DIV>
+<DIV CLASS="contents">
+<HR>
+<H2>Table of Contents</H2>
+<UL>
+<A HREF="#Preface">Preface</A>
+<BR>
+<A HREF="#Introduction to OpenLDAP Directory Services">1. Introduction to OpenLDAP Directory Services</A><UL>
+<A HREF="#What is a directory service">1.1. What is a directory service?</A>
+<BR>
+<A HREF="#What is LDAP">1.2. What is LDAP?</A>
+<BR>
+<A HREF="#When should I use LDAP">1.3. When should I use LDAP?</A>
+<BR>
+<A HREF="#When should I not use LDAP">1.4. When should I not use LDAP?</A>
+<BR>
+<A HREF="#How does LDAP work">1.5. How does LDAP work?</A>
+<BR>
+<A HREF="#What about X.500">1.6. What about X.500?</A>
+<BR>
+<A HREF="#What is the difference between LDAPv2 and LDAPv3">1.7. What is the difference between LDAPv2 and LDAPv3?</A>
+<BR>
+<A HREF="#LDAP vs RDBMS">1.8. LDAP vs RDBMS</A>
+<BR>
+<A HREF="#What is slapd and what can it do">1.9. What is slapd and what can it do?</A>
+<BR>
+<A HREF="#What is lloadd and what can it do">1.10. What is lloadd and what can it do?</A></UL>
+<BR>
+<A HREF="#A Quick-Start Guide">2. A Quick-Start Guide</A>
+<BR>
+<A HREF="#The Big Picture - Configuration Choices">3. The Big Picture - Configuration Choices</A><UL>
+<A HREF="#Local Directory Service">3.1. Local Directory Service</A>
+<BR>
+<A HREF="#Local Directory Service with Referrals">3.2. Local Directory Service with Referrals</A>
+<BR>
+<A HREF="#Replicated Directory Service">3.3. Replicated Directory Service</A>
+<BR>
+<A HREF="#Distributed Local Directory Service">3.4. Distributed Local Directory Service</A></UL>
+<BR>
+<A HREF="#Building and Installing OpenLDAP Software">4. Building and Installing OpenLDAP Software</A><UL>
+<A HREF="#Obtaining and Extracting the Software">4.1. Obtaining and Extracting the Software</A>
+<BR>
+<A HREF="#Prerequisite software">4.2. Prerequisite software</A><UL>
+<A HREF="#{{TERM[expand]TLS}}">4.2.1. <TERM>Transport Layer Security</TERM></A>
+<BR>
+<A HREF="#{{TERM[expand]SASL}}">4.2.2. <TERM>Simple Authentication and Security Layer</TERM></A>
+<BR>
+<A HREF="#{{TERM[expand]Kerberos}}">4.2.3. <TERM>Kerberos Authentication Service</TERM></A>
+<BR>
+<A HREF="#Database Software">4.2.4. Database Software</A>
+<BR>
+<A HREF="#Threads">4.2.5. Threads</A>
+<BR>
+<A HREF="#TCP Wrappers">4.2.6. TCP Wrappers</A></UL>
+<BR>
+<A HREF="#Running configure">4.3. Running configure</A>
+<BR>
+<A HREF="#Building the Software">4.4. Building the Software</A>
+<BR>
+<A HREF="#Testing the Software">4.5. Testing the Software</A>
+<BR>
+<A HREF="#Installing the Software">4.6. Installing the Software</A></UL>
+<BR>
+<A HREF="#Configuring slapd">5. Configuring slapd</A><UL>
+<A HREF="#Configuration Layout">5.1. Configuration Layout</A>
+<BR>
+<A HREF="#Configuration Directives">5.2. Configuration Directives</A><UL>
+<A HREF="#cn=config">5.2.1. cn=config</A>
+<BR>
+<A HREF="#cn=module">5.2.2. cn=module</A>
+<BR>
+<A HREF="#cn=schema">5.2.3. cn=schema</A>
+<BR>
+<A HREF="#Backend-specific Directives">5.2.4. Backend-specific Directives</A>
+<BR>
+<A HREF="#Database-specific Directives">5.2.5. Database-specific Directives</A>
+<BR>
+<A HREF="#MDB Backend Directives">5.2.6. MDB Backend Directives</A>
+<BR>
+<A HREF="#MDB Database Directives">5.2.7. MDB Database Directives</A></UL>
+<BR>
+<A HREF="#Configuration Example">5.3. Configuration Example</A>
+<BR>
+<A HREF="#Converting old style {{slapd.conf}}(5) file to {{cn=config}} format">5.4. Converting old style <EM>slapd.conf</EM>(5) file to <EM>cn=config</EM> format</A>
+<BR>
+<A HREF="#Recovering from a broken configuration">5.5. Recovering from a broken configuration</A><UL>
+<A HREF="#Generate an ldif version of the configuration database and reload from that">5.5.1. Generate an ldif version of the configuration database and reload from that</A>
+<BR>
+<A HREF="#Modify config in-place">5.5.2. Modify config in-place</A>
+<BR>
+<A HREF="#Recover with plain back-ldif">5.5.3. Recover with plain back-ldif</A></UL></UL>
+<BR>
+<A HREF="#The slapd Configuration File">6. The slapd Configuration File</A><UL>
+<A HREF="#Configuration File Format">6.1. Configuration File Format</A>
+<BR>
+<A HREF="#Configuration File Directives">6.2. Configuration File Directives</A><UL>
+<A HREF="#Global Directives">6.2.1. Global Directives</A>
+<BR>
+<A HREF="#General Backend Directives">6.2.2. General Backend Directives</A>
+<BR>
+<A HREF="#General Database Directives">6.2.3. General Database Directives</A>
+<BR>
+<A HREF="#MDB Backend Directives">6.2.4. MDB Backend Directives</A>
+<BR>
+<A HREF="#MDB Database Directives">6.2.5. MDB Database Directives</A></UL>
+<BR>
+<A HREF="#Configuration File Example">6.3. Configuration File Example</A></UL>
+<BR>
+<A HREF="#Running slapd">7. Running slapd</A><UL>
+<A HREF="#Command-Line Options">7.1. Command-Line Options</A>
+<BR>
+<A HREF="#Starting slapd">7.2. Starting slapd</A>
+<BR>
+<A HREF="#Stopping slapd">7.3. Stopping slapd</A></UL>
+<BR>
+<A HREF="#Access Control">8. Access Control</A><UL>
+<A HREF="#Introduction">8.1. Introduction</A>
+<BR>
+<A HREF="#Access Control via Static Configuration">8.2. Access Control via Static Configuration</A><UL>
+<A HREF="#What to control access to">8.2.1. What to control access to</A>
+<BR>
+<A HREF="#Who to grant access to">8.2.2. Who to grant access to</A>
+<BR>
+<A HREF="#The access to grant">8.2.3. The access to grant</A>
+<BR>
+<A HREF="#Access Control Evaluation">8.2.4. Access Control Evaluation</A>
+<BR>
+<A HREF="#Access Control Examples">8.2.5. Access Control Examples</A></UL>
+<BR>
+<A HREF="#Access Control via Dynamic Configuration">8.3. Access Control via Dynamic Configuration</A><UL>
+<A HREF="#What to control access to">8.3.1. What to control access to</A>
+<BR>
+<A HREF="#Who to grant access to">8.3.2. Who to grant access to</A>
+<BR>
+<A HREF="#The access to grant">8.3.3. The access to grant</A>
+<BR>
+<A HREF="#Access Control Evaluation">8.3.4. Access Control Evaluation</A>
+<BR>
+<A HREF="#Access Control Examples">8.3.5. Access Control Examples</A>
+<BR>
+<A HREF="#Access Control Ordering">8.3.6. Access Control Ordering</A></UL>
+<BR>
+<A HREF="#Access Control Common Examples">8.4. Access Control Common Examples</A><UL>
+<A HREF="#Basic ACLs">8.4.1. Basic ACLs</A>
+<BR>
+<A HREF="#Matching Anonymous and Authenticated users">8.4.2. Matching Anonymous and Authenticated users</A>
+<BR>
+<A HREF="#Controlling rootdn access">8.4.3. Controlling rootdn access</A>
+<BR>
+<A HREF="#Managing access with Groups">8.4.4. Managing access with Groups</A>
+<BR>
+<A HREF="#Granting access to a subset of attributes">8.4.5. Granting access to a subset of attributes</A>
+<BR>
+<A HREF="#Allowing a user write to all entries below theirs">8.4.6. Allowing a user write to all entries below theirs</A>
+<BR>
+<A HREF="#Allowing entry creation">8.4.7. Allowing entry creation</A>
+<BR>
+<A HREF="#Tips for using regular expressions in Access Control">8.4.8. Tips for using regular expressions in Access Control</A>
+<BR>
+<A HREF="#Granting and Denying access based on security strength factors (ssf)">8.4.9. Granting and Denying access based on security strength factors (ssf)</A>
+<BR>
+<A HREF="#When things aren\'t working as expected">8.4.10. When things aren't working as expected</A></UL>
+<BR>
+<A HREF="#Sets - Granting rights based on relationships">8.5. Sets - Granting rights based on relationships</A><UL>
+<A HREF="#Groups of Groups">8.5.1. Groups of Groups</A>
+<BR>
+<A HREF="#Group ACLs without DN syntax">8.5.2. Group ACLs without DN syntax</A>
+<BR>
+<A HREF="#Following references">8.5.3. Following references</A></UL></UL>
+<BR>
+<A HREF="#Limits">9. Limits</A><UL>
+<A HREF="#Introduction">9.1. Introduction</A>
+<BR>
+<A HREF="#Soft and Hard limits">9.2. Soft and Hard limits</A>
+<BR>
+<A HREF="#Global Limits">9.3. Global Limits</A><UL>
+<A HREF="#Special Size Limits">9.3.1. Special Size Limits</A></UL>
+<BR>
+<A HREF="#Per-Database Limits">9.4. Per-Database Limits</A><UL>
+<A HREF="#Specify who the limits apply to">9.4.1. Specify who the limits apply to</A>
+<BR>
+<A HREF="#Specify time limits">9.4.2. Specify time limits</A>
+<BR>
+<A HREF="#Specifying size limits">9.4.3. Specifying size limits</A></UL>
+<BR>
+<A HREF="#Example Limit Configurations">9.5. Example Limit Configurations</A><UL>
+<A HREF="#Simple Global Limits">9.5.1. Simple Global Limits</A>
+<BR>
+<A HREF="#Global Hard and Soft Limits">9.5.2. Global Hard and Soft Limits</A>
+<BR>
+<A HREF="#Giving specific users larger limits">9.5.3. Giving specific users larger limits</A>
+<BR>
+<A HREF="#Limiting who can do paged searches">9.5.4. Limiting who can do paged searches</A></UL>
+<BR>
+<A HREF="#Glued/Subordinate database configurations">9.6. Glued/Subordinate database configurations</A>
+<BR>
+<A HREF="#Further Information">9.7. Further Information</A></UL>
+<BR>
+<A HREF="#Database Creation and Maintenance Tools">10. Database Creation and Maintenance Tools</A><UL>
+<A HREF="#Creating a database over LDAP">10.1. Creating a database over LDAP</A>
+<BR>
+<A HREF="#Creating a database off-line">10.2. Creating a database off-line</A><UL>
+<A HREF="#The {{EX:slapadd}} program">10.2.1. The <TT>slapadd</TT> program</A>
+<BR>
+<A HREF="#The {{EX:slapindex}} program">10.2.2. The <TT>slapindex</TT> program</A>
+<BR>
+<A HREF="#The {{EX:slapcat}} program">10.2.3. The <TT>slapcat</TT> program</A></UL>
+<BR>
+<A HREF="#The LDIF text entry format">10.3. The LDIF text entry format</A></UL>
+<BR>
+<A HREF="#Backends">11. Backends</A><UL>
+<A HREF="#LDAP">11.1. LDAP</A><UL>
+<A HREF="#Overview">11.1.1. Overview</A>
+<BR>
+<A HREF="#back-ldap Configuration">11.1.2. back-ldap Configuration</A>
+<BR>
+<A HREF="#Further Information">11.1.3. Further Information</A></UL>
+<BR>
+<A HREF="#LDIF">11.2. LDIF</A><UL>
+<A HREF="#Overview">11.2.1. Overview</A>
+<BR>
+<A HREF="#back-ldif Configuration">11.2.2. back-ldif Configuration</A>
+<BR>
+<A HREF="#Further Information">11.2.3. Further Information</A></UL>
+<BR>
+<A HREF="#LMDB">11.3. LMDB</A><UL>
+<A HREF="#Overview">11.3.1. Overview</A>
+<BR>
+<A HREF="#back-mdb Configuration">11.3.2. back-mdb Configuration</A>
+<BR>
+<A HREF="#Further Information">11.3.3. Further Information</A></UL>
+<BR>
+<A HREF="#Metadirectory">11.4. Metadirectory</A><UL>
+<A HREF="#Overview">11.4.1. Overview</A>
+<BR>
+<A HREF="#back-meta Configuration">11.4.2. back-meta Configuration</A>
+<BR>
+<A HREF="#Further Information">11.4.3. Further Information</A></UL>
+<BR>
+<A HREF="#Monitor">11.5. Monitor</A><UL>
+<A HREF="#Overview">11.5.1. Overview</A>
+<BR>
+<A HREF="#back-monitor Configuration">11.5.2. back-monitor Configuration</A>
+<BR>
+<A HREF="#Further Information">11.5.3. Further Information</A></UL>
+<BR>
+<A HREF="#Null">11.6. Null</A><UL>
+<A HREF="#Overview">11.6.1. Overview</A>
+<BR>
+<A HREF="#back-null Configuration">11.6.2. back-null Configuration</A>
+<BR>
+<A HREF="#Further Information">11.6.3. Further Information</A></UL>
+<BR>
+<A HREF="#Passwd">11.7. Passwd</A><UL>
+<A HREF="#Overview">11.7.1. Overview</A>
+<BR>
+<A HREF="#back-passwd Configuration">11.7.2. back-passwd Configuration</A>
+<BR>
+<A HREF="#Further Information">11.7.3. Further Information</A></UL>
+<BR>
+<A HREF="#Perl">11.8. Perl</A><UL>
+<A HREF="#Overview">11.8.1. Overview</A>
+<BR>
+<A HREF="#back-perl Configuration">11.8.2. back-perl Configuration</A>
+<BR>
+<A HREF="#Further Information">11.8.3. Further Information</A></UL>
+<BR>
+<A HREF="#Relay">11.9. Relay</A><UL>
+<A HREF="#Overview">11.9.1. Overview</A>
+<BR>
+<A HREF="#back-relay Configuration">11.9.2. back-relay Configuration</A>
+<BR>
+<A HREF="#Further Information">11.9.3. Further Information</A></UL>
+<BR>
+<A HREF="#SQL">11.10. SQL</A><UL>
+<A HREF="#Overview">11.10.1. Overview</A>
+<BR>
+<A HREF="#back-sql Configuration">11.10.2. back-sql Configuration</A>
+<BR>
+<A HREF="#Further Information">11.10.3. Further Information</A></UL></UL>
+<BR>
+<A HREF="#Overlays">12. Overlays</A><UL>
+<A HREF="#Access Logging">12.1. Access Logging</A><UL>
+<A HREF="#Overview">12.1.1. Overview</A>
+<BR>
+<A HREF="#Access Logging Configuration">12.1.2. Access Logging Configuration</A>
+<BR>
+<A HREF="#Further Information">12.1.3. Further Information</A></UL>
+<BR>
+<A HREF="#Audit Logging">12.2. Audit Logging</A><UL>
+<A HREF="#Overview">12.2.1. Overview</A>
+<BR>
+<A HREF="#Audit Logging Configuration">12.2.2. Audit Logging Configuration</A>
+<BR>
+<A HREF="#Further Information">12.2.3. Further Information</A></UL>
+<BR>
+<A HREF="#Chaining">12.3. Chaining</A><UL>
+<A HREF="#Overview">12.3.1. Overview</A>
+<BR>
+<A HREF="#Chaining Configuration">12.3.2. Chaining Configuration</A>
+<BR>
+<A HREF="#Handling Chaining Errors">12.3.3. Handling Chaining Errors</A>
+<BR>
+<A HREF="#Read-Back of Chained Modifications">12.3.4. Read-Back of Chained Modifications</A>
+<BR>
+<A HREF="#Further Information">12.3.5. Further Information</A></UL>
+<BR>
+<A HREF="#Constraints">12.4. Constraints</A><UL>
+<A HREF="#Overview">12.4.1. Overview</A>
+<BR>
+<A HREF="#Constraint Configuration">12.4.2. Constraint Configuration</A>
+<BR>
+<A HREF="#Further Information">12.4.3. Further Information</A></UL>
+<BR>
+<A HREF="#Dynamic Directory Services">12.5. Dynamic Directory Services</A><UL>
+<A HREF="#Overview">12.5.1. Overview</A>
+<BR>
+<A HREF="#Dynamic Directory Service Configuration">12.5.2. Dynamic Directory Service Configuration</A>
+<BR>
+<A HREF="#Further Information">12.5.3. Further Information</A></UL>
+<BR>
+<A HREF="#Dynamic Groups">12.6. Dynamic Groups</A><UL>
+<A HREF="#Overview">12.6.1. Overview</A>
+<BR>
+<A HREF="#Dynamic Group Configuration">12.6.2. Dynamic Group Configuration</A></UL>
+<BR>
+<A HREF="#Dynamic Lists">12.7. Dynamic Lists</A><UL>
+<A HREF="#Overview">12.7.1. Overview</A>
+<BR>
+<A HREF="#Dynamic List Configuration">12.7.2. Dynamic List Configuration</A>
+<BR>
+<A HREF="#Further Information">12.7.3. Further Information</A></UL>
+<BR>
+<A HREF="#Reverse Group Membership Maintenance">12.8. Reverse Group Membership Maintenance</A><UL>
+<A HREF="#Overview">12.8.1. Overview</A>
+<BR>
+<A HREF="#Member Of Configuration">12.8.2. Member Of Configuration</A>
+<BR>
+<A HREF="#Further Information">12.8.3. Further Information</A></UL>
+<BR>
+<A HREF="#The Proxy Cache Engine">12.9. The Proxy Cache Engine</A><UL>
+<A HREF="#Overview">12.9.1. Overview</A>
+<BR>
+<A HREF="#Proxy Cache Configuration">12.9.2. Proxy Cache Configuration</A>
+<BR>
+<A HREF="#Further Information">12.9.3. Further Information</A></UL>
+<BR>
+<A HREF="#Password Policies">12.10. Password Policies</A><UL>
+<A HREF="#Overview">12.10.1. Overview</A>
+<BR>
+<A HREF="#Password Policy Configuration">12.10.2. Password Policy Configuration</A>
+<BR>
+<A HREF="#Further Information">12.10.3. Further Information</A></UL>
+<BR>
+<A HREF="#Referential Integrity">12.11. Referential Integrity</A><UL>
+<A HREF="#Overview">12.11.1. Overview</A>
+<BR>
+<A HREF="#Referential Integrity Configuration">12.11.2. Referential Integrity Configuration</A>
+<BR>
+<A HREF="#Further Information">12.11.3. Further Information</A></UL>
+<BR>
+<A HREF="#Return Code">12.12. Return Code</A><UL>
+<A HREF="#Overview">12.12.1. Overview</A>
+<BR>
+<A HREF="#Return Code Configuration">12.12.2. Return Code Configuration</A>
+<BR>
+<A HREF="#Further Information">12.12.3. Further Information</A></UL>
+<BR>
+<A HREF="#Rewrite/Remap">12.13. Rewrite/Remap</A><UL>
+<A HREF="#Overview">12.13.1. Overview</A>
+<BR>
+<A HREF="#Rewrite/Remap Configuration">12.13.2. Rewrite/Remap Configuration</A>
+<BR>
+<A HREF="#Further Information">12.13.3. Further Information</A></UL>
+<BR>
+<A HREF="#Sync Provider">12.14. Sync Provider</A><UL>
+<A HREF="#Overview">12.14.1. Overview</A>
+<BR>
+<A HREF="#Sync Provider Configuration">12.14.2. Sync Provider Configuration</A>
+<BR>
+<A HREF="#Further Information">12.14.3. Further Information</A></UL>
+<BR>
+<A HREF="#Translucent Proxy">12.15. Translucent Proxy</A><UL>
+<A HREF="#Overview">12.15.1. Overview</A>
+<BR>
+<A HREF="#Translucent Proxy Configuration">12.15.2. Translucent Proxy Configuration</A>
+<BR>
+<A HREF="#Further Information">12.15.3. Further Information</A></UL>
+<BR>
+<A HREF="#Attribute Uniqueness">12.16. Attribute Uniqueness</A><UL>
+<A HREF="#Overview">12.16.1. Overview</A>
+<BR>
+<A HREF="#Attribute Uniqueness Configuration">12.16.2. Attribute Uniqueness Configuration</A>
+<BR>
+<A HREF="#Further Information">12.16.3. Further Information</A></UL>
+<BR>
+<A HREF="#Value Sorting">12.17. Value Sorting</A><UL>
+<A HREF="#Overview">12.17.1. Overview</A>
+<BR>
+<A HREF="#Value Sorting Configuration">12.17.2. Value Sorting Configuration</A>
+<BR>
+<A HREF="#Further Information">12.17.3. Further Information</A></UL>
+<BR>
+<A HREF="#Overlay Stacking">12.18. Overlay Stacking</A><UL>
+<A HREF="#Overview">12.18.1. Overview</A>
+<BR>
+<A HREF="#Example Scenarios">12.18.2. Example Scenarios</A></UL></UL>
+<BR>
+<A HREF="#Schema Specification">13. Schema Specification</A><UL>
+<A HREF="#Distributed Schema Files">13.1. Distributed Schema Files</A>
+<BR>
+<A HREF="#Extending Schema">13.2. Extending Schema</A><UL>
+<A HREF="#Object Identifiers">13.2.1. Object Identifiers</A>
+<BR>
+<A HREF="#Naming Elements">13.2.2. Naming Elements</A>
+<BR>
+<A HREF="#Local schema file">13.2.3. Local schema file</A>
+<BR>
+<A HREF="#Attribute Type Specification">13.2.4. Attribute Type Specification</A>
+<BR>
+<A HREF="#Object Class Specification">13.2.5. Object Class Specification</A>
+<BR>
+<A HREF="#OID Macros">13.2.6. OID Macros</A></UL></UL>
+<BR>
+<A HREF="#Security Considerations">14. Security Considerations</A><UL>
+<A HREF="#Network Security">14.1. Network Security</A><UL>
+<A HREF="#Selective Listening">14.1.1. Selective Listening</A>
+<BR>
+<A HREF="#IP Firewall">14.1.2. IP Firewall</A>
+<BR>
+<A HREF="#TCP Wrappers">14.1.3. TCP Wrappers</A></UL>
+<BR>
+<A HREF="#Data Integrity and Confidentiality Protection">14.2. Data Integrity and Confidentiality Protection</A><UL>
+<A HREF="#Security Strength Factors">14.2.1. Security Strength Factors</A></UL>
+<BR>
+<A HREF="#Authentication Methods">14.3. Authentication Methods</A><UL>
+<A HREF="#&quot;simple&quot; method">14.3.1. &quot;simple&quot; method</A>
+<BR>
+<A HREF="#SASL method">14.3.2. SASL method</A></UL>
+<BR>
+<A HREF="#Password Storage">14.4. Password Storage</A><UL>
+<A HREF="#SSHA password storage scheme">14.4.1. SSHA password storage scheme</A>
+<BR>
+<A HREF="#CRYPT password storage scheme">14.4.2. CRYPT password storage scheme</A>
+<BR>
+<A HREF="#MD5 password storage scheme">14.4.3. MD5 password storage scheme</A>
+<BR>
+<A HREF="#SMD5 password storage scheme">14.4.4. SMD5 password storage scheme</A>
+<BR>
+<A HREF="#SHA password storage scheme">14.4.5. SHA password storage scheme</A>
+<BR>
+<A HREF="#SASL password storage scheme">14.4.6. SASL password storage scheme</A></UL>
+<BR>
+<A HREF="#Pass-Through authentication">14.5. Pass-Through authentication</A><UL>
+<A HREF="#Configuring slapd to use an authentication provider">14.5.1. Configuring slapd to use an authentication provider</A>
+<BR>
+<A HREF="#Configuring saslauthd">14.5.2. Configuring saslauthd</A>
+<BR>
+<A HREF="#Testing pass-through authentication">14.5.3. Testing pass-through authentication</A></UL></UL>
+<BR>
+<A HREF="#Using SASL">15. Using SASL</A><UL>
+<A HREF="#SASL Security Considerations">15.1. SASL Security Considerations</A>
+<BR>
+<A HREF="#SASL Authentication">15.2. SASL Authentication</A><UL>
+<A HREF="#GSSAPI">15.2.1. GSSAPI</A>
+<BR>
+<A HREF="#KERBEROS_V4">15.2.2. KERBEROS_V4</A>
+<BR>
+<A HREF="#DIGEST-MD5">15.2.3. DIGEST-MD5</A>
+<BR>
+<A HREF="#EXTERNAL">15.2.4. EXTERNAL</A>
+<BR>
+<A HREF="#Mapping Authentication Identities">15.2.5. Mapping Authentication Identities</A>
+<BR>
+<A HREF="#Direct Mapping">15.2.6. Direct Mapping</A>
+<BR>
+<A HREF="#Search-based mappings">15.2.7. Search-based mappings</A></UL>
+<BR>
+<A HREF="#SASL Proxy Authorization">15.3. SASL Proxy Authorization</A><UL>
+<A HREF="#Uses of Proxy Authorization">15.3.1. Uses of Proxy Authorization</A>
+<BR>
+<A HREF="#SASL Authorization Identities">15.3.2. SASL Authorization Identities</A>
+<BR>
+<A HREF="#Proxy Authorization Rules">15.3.3. Proxy Authorization Rules</A></UL></UL>
+<BR>
+<A HREF="#Using TLS">16. Using TLS</A><UL>
+<A HREF="#TLS Certificates">16.1. TLS Certificates</A><UL>
+<A HREF="#Server Certificates">16.1.1. Server Certificates</A>
+<BR>
+<A HREF="#Client Certificates">16.1.2. Client Certificates</A></UL>
+<BR>
+<A HREF="#TLS Configuration">16.2. TLS Configuration</A><UL>
+<A HREF="#Server Configuration">16.2.1. Server Configuration</A>
+<BR>
+<A HREF="#Client Configuration">16.2.2. Client Configuration</A></UL></UL>
+<BR>
+<A HREF="#Constructing a Distributed Directory Service">17. Constructing a Distributed Directory Service</A><UL>
+<A HREF="#Subordinate Knowledge Information">17.1. Subordinate Knowledge Information</A>
+<BR>
+<A HREF="#Superior Knowledge Information">17.2. Superior Knowledge Information</A>
+<BR>
+<A HREF="#The ManageDsaIT Control">17.3. The ManageDsaIT Control</A></UL>
+<BR>
+<A HREF="#Replication">18. Replication</A><UL>
+<A HREF="#Replication Technology">18.1. Replication Technology</A><UL>
+<A HREF="#LDAP Sync Replication">18.1.1. LDAP Sync Replication</A></UL>
+<BR>
+<A HREF="#Deployment Alternatives">18.2. Deployment Alternatives</A><UL>
+<A HREF="#Delta-syncrepl replication">18.2.1. Delta-syncrepl replication</A>
+<BR>
+<A HREF="#N-Way Multi-Provider Replication">18.2.2. N-Way Multi-Provider Replication</A>
+<BR>
+<A HREF="#Mirror mode replication">18.2.3. Mirror mode replication</A>
+<BR>
+<A HREF="#Syncrepl Proxy Mode">18.2.4. Syncrepl Proxy Mode</A></UL>
+<BR>
+<A HREF="#Configuring the different replication types">18.3. Configuring the different replication types</A><UL>
+<A HREF="#Syncrepl">18.3.1. Syncrepl</A>
+<BR>
+<A HREF="#Delta-syncrepl">18.3.2. Delta-syncrepl</A>
+<BR>
+<A HREF="#N-Way Multi-Provider">18.3.3. N-Way Multi-Provider</A>
+<BR>
+<A HREF="#Mirror mode">18.3.4. Mirror mode</A>
+<BR>
+<A HREF="#Syncrepl Proxy">18.3.5. Syncrepl Proxy</A></UL></UL>
+<BR>
+<A HREF="#Maintenance">19. Maintenance</A><UL>
+<A HREF="#Directory Backups">19.1. Directory Backups</A>
+<BR>
+<A HREF="#Checkpointing">19.2. Checkpointing</A>
+<BR>
+<A HREF="#Migration">19.3. Migration</A></UL>
+<BR>
+<A HREF="#Monitoring">20. Monitoring</A><UL>
+<A HREF="#Monitor configuration via cn=config(5)">20.1. Monitor configuration via cn=config(5)</A>
+<BR>
+<A HREF="#Monitor configuration via slapd.conf(5)">20.2. Monitor configuration via slapd.conf(5)</A>
+<BR>
+<A HREF="#Accessing Monitoring Information">20.3. Accessing Monitoring Information</A>
+<BR>
+<A HREF="#Monitor Information">20.4. Monitor Information</A><UL>
+<A HREF="#Backends">20.4.1. Backends</A>
+<BR>
+<A HREF="#Connections">20.4.2. Connections</A>
+<BR>
+<A HREF="#Databases">20.4.3. Databases</A>
+<BR>
+<A HREF="#Listener">20.4.4. Listener</A>
+<BR>
+<A HREF="#Log">20.4.5. Log</A>
+<BR>
+<A HREF="#Operations">20.4.6. Operations</A>
+<BR>
+<A HREF="#Overlays">20.4.7. Overlays</A>
+<BR>
+<A HREF="#SASL">20.4.8. SASL</A>
+<BR>
+<A HREF="#Statistics">20.4.9. Statistics</A>
+<BR>
+<A HREF="#Threads">20.4.10. Threads</A>
+<BR>
+<A HREF="#Time">20.4.11. Time</A>
+<BR>
+<A HREF="#TLS">20.4.12. TLS</A>
+<BR>
+<A HREF="#Waiters">20.4.13. Waiters</A></UL></UL>
+<BR>
+<A HREF="#Load Balancing with lloadd">21. Load Balancing with lloadd</A><UL>
+<A HREF="#Overview">21.1. Overview</A>
+<BR>
+<A HREF="#When to use the OpenLDAP load balancer">21.2. When to use the OpenLDAP load balancer</A>
+<BR>
+<A HREF="#Runtime configurations">21.3. Runtime configurations</A>
+<BR>
+<A HREF="#Build Notes">21.4. Build Notes</A>
+<BR>
+<A HREF="#Sample Runtime">21.5. Sample Runtime</A>
+<BR>
+<A HREF="#Configuring load balancer">21.6. Configuring load balancer</A><UL>
+<A HREF="#Common configuration options">21.6.1. Common configuration options</A>
+<BR>
+<A HREF="#Sample backend config">21.6.2. Sample backend config</A></UL></UL>
+<BR>
+<A HREF="#Tuning">22. Tuning</A><UL>
+<A HREF="#Performance Factors">22.1. Performance Factors</A><UL>
+<A HREF="#Memory">22.1.1. Memory</A>
+<BR>
+<A HREF="#Disks">22.1.2. Disks</A>
+<BR>
+<A HREF="#Network Topology">22.1.3. Network Topology</A>
+<BR>
+<A HREF="#Directory Layout Design">22.1.4. Directory Layout Design</A>
+<BR>
+<A HREF="#Expected Usage">22.1.5. Expected Usage</A></UL>
+<BR>
+<A HREF="#Indexes">22.2. Indexes</A><UL>
+<A HREF="#Understanding how a search works">22.2.1. Understanding how a search works</A>
+<BR>
+<A HREF="#What to index">22.2.2. What to index</A>
+<BR>
+<A HREF="#Presence indexing">22.2.3. Presence indexing</A>
+<BR>
+<A HREF="#Equality indexing">22.2.4. Equality indexing</A>
+<BR>
+<A HREF="#Substring indexing">22.2.5. Substring indexing</A></UL>
+<BR>
+<A HREF="#Logging">22.3. Logging</A><UL>
+<A HREF="#What log level to use">22.3.1. What log level to use</A>
+<BR>
+<A HREF="#What to watch out for">22.3.2. What to watch out for</A>
+<BR>
+<A HREF="#Improving throughput">22.3.3. Improving throughput</A></UL>
+<BR>
+<A HREF="#{{slapd}}(8) Threads">22.4. <EM>slapd</EM>(8) Threads</A></UL>
+<BR>
+<A HREF="#Troubleshooting">23. Troubleshooting</A><UL>
+<A HREF="#User or Software errors">23.1. User or Software errors?</A>
+<BR>
+<A HREF="#Checklist">23.2. Checklist</A>
+<BR>
+<A HREF="#OpenLDAP Bugs">23.3. OpenLDAP Bugs</A>
+<BR>
+<A HREF="#3rd party software error">23.4. 3rd party software error</A>
+<BR>
+<A HREF="#How to contact the OpenLDAP Project">23.5. How to contact the OpenLDAP Project</A>
+<BR>
+<A HREF="#How to present your problem">23.6. How to present your problem</A>
+<BR>
+<A HREF="#Debugging {{slapd}}(8)">23.7. Debugging <EM>slapd</EM>(8)</A>
+<BR>
+<A HREF="#Commercial Support">23.8. Commercial Support</A></UL>
+<BR>
+<A HREF="#Changes Since Previous Release">A. Changes Since Previous Release</A><UL>
+<A HREF="#New Guide Sections">A.1. New Guide Sections</A>
+<BR>
+<A HREF="#New Features and Enhancements in 2.5">A.2. New Features and Enhancements in 2.5</A><UL>
+<A HREF="#Better {{B:cn=config}} functionality">A.2.1. Better <B>cn=config</B> functionality</A>
+<BR>
+<A HREF="#Better {{B:cn=schema}} functionality">A.2.2. Better <B>cn=schema</B> functionality</A>
+<BR>
+<A HREF="#More sophisticated Syncrepl configurations">A.2.3. More sophisticated Syncrepl configurations</A>
+<BR>
+<A HREF="#Replicating {{slapd}} Configuration (syncrepl and {{B:cn=config}})">A.2.4. Replicating <EM>slapd</EM> Configuration (syncrepl and <B>cn=config</B>)</A>
+<BR>
+<A HREF="#More extensive TLS configuration control">A.2.5. More extensive TLS configuration control</A>
+<BR>
+<A HREF="#Performance enhancements">A.2.6. Performance enhancements</A>
+<BR>
+<A HREF="#New overlays">A.2.7. New overlays</A>
+<BR>
+<A HREF="#New features in existing Overlays">A.2.8. New features in existing Overlays</A>
+<BR>
+<A HREF="#New features in slapd">A.2.9. New features in slapd</A>
+<BR>
+<A HREF="#New features in libldap">A.2.10. New features in libldap</A>
+<BR>
+<A HREF="#New clients, tools and tool enhancements">A.2.11. New clients, tools and tool enhancements</A>
+<BR>
+<A HREF="#New build options">A.2.12. New build options</A></UL>
+<BR>
+<A HREF="#Obsolete Features Removed From 2.5">A.3. Obsolete Features Removed From 2.5</A><UL>
+<A HREF="#back-bdb and back-hdb">A.3.1. back-bdb and back-hdb</A></UL></UL>
+<BR>
+<A HREF="#Upgrading from 2.4.x">B. Upgrading from 2.4.x</A><UL>
+<A HREF="#{{B:cn=config}} olc* attributes">B.1. <B>cn=config</B> olc* attributes</A>
+<BR>
+<A HREF="#ppolicy overlay">B.2. ppolicy overlay</A>
+<BR>
+<A HREF="#unique overlay">B.3. unique overlay</A>
+<BR>
+<A HREF="#ldap and meta backends">B.4. ldap and meta backends</A>
+<BR>
+<A HREF="#shell backend">B.5. shell backend</A>
+<BR>
+<A HREF="#perl and sql backends">B.6. perl and sql backends</A>
+<BR>
+<A HREF="#hdb and bdb backends">B.7. hdb and bdb backends</A>
+<BR>
+<A HREF="#mdb backend">B.8. mdb backend</A>
+<BR>
+<A HREF="#Client utility changes">B.9. Client utility changes</A></UL>
+<BR>
+<A HREF="#Common errors encountered when using OpenLDAP Software">C. Common errors encountered when using OpenLDAP Software</A><UL>
+<A HREF="#Common causes of LDAP errors">C.1. Common causes of LDAP errors</A><UL>
+<A HREF="#ldap_*: Can\'t contact LDAP server">C.1.1. ldap_*: Can't contact LDAP server</A>
+<BR>
+<A HREF="#ldap_*: No such object">C.1.2. ldap_*: No such object</A>
+<BR>
+<A HREF="#ldap_*: Can\'t chase referral">C.1.3. ldap_*: Can't chase referral</A>
+<BR>
+<A HREF="#ldap_*: server is unwilling to perform">C.1.4. ldap_*: server is unwilling to perform</A>
+<BR>
+<A HREF="#ldap_*: Insufficient access">C.1.5. ldap_*: Insufficient access</A>
+<BR>
+<A HREF="#ldap_*: Invalid DN syntax">C.1.6. ldap_*: Invalid DN syntax</A>
+<BR>
+<A HREF="#ldap_*: Referral hop limit exceeded">C.1.7. ldap_*: Referral hop limit exceeded</A>
+<BR>
+<A HREF="#ldap_*: operations error">C.1.8. ldap_*: operations error</A>
+<BR>
+<A HREF="#ldap_*: other error">C.1.9. ldap_*: other error</A>
+<BR>
+<A HREF="#ldap_add/modify: Invalid syntax">C.1.10. ldap_add/modify: Invalid syntax</A>
+<BR>
+<A HREF="#ldap_add/modify: Object class violation">C.1.11. ldap_add/modify: Object class violation</A>
+<BR>
+<A HREF="#ldap_add: No such object">C.1.12. ldap_add: No such object</A>
+<BR>
+<A HREF="#ldap add: invalid structural object class chain">C.1.13. ldap add: invalid structural object class chain</A>
+<BR>
+<A HREF="#ldap_add: no structuralObjectClass operational attribute">C.1.14. ldap_add: no structuralObjectClass operational attribute</A>
+<BR>
+<A HREF="#ldap_add/modify/rename: Naming violation">C.1.15. ldap_add/modify/rename: Naming violation</A>
+<BR>
+<A HREF="#ldap_add/delete/modify/rename: no global superior knowledge">C.1.16. ldap_add/delete/modify/rename: no global superior knowledge</A>
+<BR>
+<A HREF="#ldap_bind: Insufficient access">C.1.17. ldap_bind: Insufficient access</A>
+<BR>
+<A HREF="#ldap_bind: Invalid credentials">C.1.18. ldap_bind: Invalid credentials</A>
+<BR>
+<A HREF="#ldap_bind: Protocol error">C.1.19. ldap_bind: Protocol error</A>
+<BR>
+<A HREF="#ldap_modify: cannot modify object class">C.1.20. ldap_modify: cannot modify object class</A>
+<BR>
+<A HREF="#ldap_sasl_interactive_bind_s: ..">C.1.21. ldap_sasl_interactive_bind_s: ...</A>
+<BR>
+<A HREF="#ldap_sasl_interactive_bind_s: No such Object">C.1.22. ldap_sasl_interactive_bind_s: No such Object</A>
+<BR>
+<A HREF="#ldap_sasl_interactive_bind_s: No such attribute">C.1.23. ldap_sasl_interactive_bind_s: No such attribute</A>
+<BR>
+<A HREF="#ldap_sasl_interactive_bind_s: Unknown authentication method">C.1.24. ldap_sasl_interactive_bind_s: Unknown authentication method</A>
+<BR>
+<A HREF="#ldap_sasl_interactive_bind_s: Local error (82)">C.1.25. ldap_sasl_interactive_bind_s: Local error (82)</A>
+<BR>
+<A HREF="#ldap_search: Partial results and referral received">C.1.26. ldap_search: Partial results and referral received</A>
+<BR>
+<A HREF="#ldap_start_tls: Operations error">C.1.27. ldap_start_tls: Operations error</A></UL>
+<BR>
+<A HREF="#Other Errors">C.2. Other Errors</A><UL>
+<A HREF="#ber_get_next on fd X failed errno=34 (Numerical result out of range)">C.2.1. ber_get_next on fd X failed errno=34 (Numerical result out of range)</A>
+<BR>
+<A HREF="#ber_get_next on fd X failed errno=11 (Resource temporarily unavailable)">C.2.2. ber_get_next on fd X failed errno=11 (Resource temporarily unavailable)</A>
+<BR>
+<A HREF="#daemon: socket() failed errno=97 (Address family not supported)">C.2.3. daemon: socket() failed errno=97 (Address family not supported)</A>
+<BR>
+<A HREF="#GSSAPI: gss_acquire_cred: Miscellaneous failure; Permission denied;">C.2.4. GSSAPI: gss_acquire_cred: Miscellaneous failure; Permission denied;</A>
+<BR>
+<A HREF="#access from unknown denied">C.2.5. access from unknown denied</A>
+<BR>
+<A HREF="#ldap_read: want=# error=Resource temporarily unavailable">C.2.6. ldap_read: want=# error=Resource temporarily unavailable</A>
+<BR>
+<A HREF="#`make test\' fails">C.2.7. `make test' fails</A>
+<BR>
+<A HREF="#ldap_*: Internal (implementation specific) error (80) - additional info: entry index delete failed">C.2.8. ldap_*: Internal (implementation specific) error (80) - additional info: entry index delete failed</A>
+<BR>
+<A HREF="#ldap_sasl_interactive_bind_s: Can\'t contact LDAP server (-1)">C.2.9. ldap_sasl_interactive_bind_s: Can't contact LDAP server (-1)</A></UL></UL>
+<BR>
+<A HREF="#Recommended OpenLDAP Software Dependency Versions">D. Recommended OpenLDAP Software Dependency Versions</A><UL>
+<A HREF="#Dependency Versions">D.1. Dependency Versions</A></UL>
+<BR>
+<A HREF="#Real World OpenLDAP Deployments and Examples">E. Real World OpenLDAP Deployments and Examples</A>
+<BR>
+<A HREF="#OpenLDAP Software Contributions">F. OpenLDAP Software Contributions</A><UL>
+<A HREF="#Client APIs">F.1. Client APIs</A><UL>
+<A HREF="#ldapc++">F.1.1. ldapc++</A>
+<BR>
+<A HREF="#ldaptcl">F.1.2. ldaptcl</A></UL>
+<BR>
+<A HREF="#Overlays">F.2. Overlays</A><UL>
+<A HREF="#acl">F.2.1. acl</A>
+<BR>
+<A HREF="#addpartial">F.2.2. addpartial</A>
+<BR>
+<A HREF="#allop">F.2.3. allop</A>
+<BR>
+<A HREF="#autogroup">F.2.4. autogroup</A>
+<BR>
+<A HREF="#comp_match">F.2.5. comp_match</A>
+<BR>
+<A HREF="#denyop">F.2.6. denyop</A>
+<BR>
+<A HREF="#dsaschema">F.2.7. dsaschema</A>
+<BR>
+<A HREF="#lastmod">F.2.8. lastmod</A>
+<BR>
+<A HREF="#nops">F.2.9. nops</A>
+<BR>
+<A HREF="#nssov">F.2.10. nssov</A>
+<BR>
+<A HREF="#passwd">F.2.11. passwd</A>
+<BR>
+<A HREF="#proxyOld">F.2.12. proxyOld</A>
+<BR>
+<A HREF="#smbk5pwd">F.2.13. smbk5pwd</A>
+<BR>
+<A HREF="#trace">F.2.14. trace</A>
+<BR>
+<A HREF="#usn">F.2.15. usn</A></UL>
+<BR>
+<A HREF="#Tools">F.3. Tools</A><UL>
+<A HREF="#Statistic Logging">F.3.1. Statistic Logging</A></UL>
+<BR>
+<A HREF="#SLAPI Plugins">F.4. SLAPI Plugins</A><UL>
+<A HREF="#addrdnvalues">F.4.1. addrdnvalues</A></UL></UL>
+<BR>
+<A HREF="#Configuration File Examples">G. Configuration File Examples</A><UL>
+<A HREF="#slapd.conf">G.1. slapd.conf</A>
+<BR>
+<A HREF="#ldap.conf">G.2. ldap.conf</A>
+<BR>
+<A HREF="#a-n-other.conf">G.3. a-n-other.conf</A></UL>
+<BR>
+<A HREF="#LDAP Result Codes">H. LDAP Result Codes</A><UL>
+<A HREF="#Non-Error Result Codes">H.1. Non-Error Result Codes</A>
+<BR>
+<A HREF="#Result Codes">H.2. Result Codes</A>
+<BR>
+<A HREF="#success (0)">H.3. success (0)</A>
+<BR>
+<A HREF="#operationsError (1)">H.4. operationsError (1)</A>
+<BR>
+<A HREF="#protocolError (2)">H.5. protocolError (2)</A>
+<BR>
+<A HREF="#timeLimitExceeded (3)">H.6. timeLimitExceeded (3)</A>
+<BR>
+<A HREF="#sizeLimitExceeded (4)">H.7. sizeLimitExceeded (4)</A>
+<BR>
+<A HREF="#compareFalse (5)">H.8. compareFalse (5)</A>
+<BR>
+<A HREF="#compareTrue (6)">H.9. compareTrue (6)</A>
+<BR>
+<A HREF="#authMethodNotSupported (7)">H.10. authMethodNotSupported (7)</A>
+<BR>
+<A HREF="#strongerAuthRequired (8)">H.11. strongerAuthRequired (8)</A>
+<BR>
+<A HREF="#referral (10)">H.12. referral (10)</A>
+<BR>
+<A HREF="#adminLimitExceeded (11)">H.13. adminLimitExceeded (11)</A>
+<BR>
+<A HREF="#unavailableCriticalExtension (12)">H.14. unavailableCriticalExtension (12)</A>
+<BR>
+<A HREF="#confidentialityRequired (13)">H.15. confidentialityRequired (13)</A>
+<BR>
+<A HREF="#saslBindInProgress (14)">H.16. saslBindInProgress (14)</A>
+<BR>
+<A HREF="#noSuchAttribute (16)">H.17. noSuchAttribute (16)</A>
+<BR>
+<A HREF="#undefinedAttributeType (17)">H.18. undefinedAttributeType (17)</A>
+<BR>
+<A HREF="#inappropriateMatching (18)">H.19. inappropriateMatching (18)</A>
+<BR>
+<A HREF="#constraintViolation (19)">H.20. constraintViolation (19)</A>
+<BR>
+<A HREF="#attributeOrValueExists (20)">H.21. attributeOrValueExists (20)</A>
+<BR>
+<A HREF="#invalidAttributeSyntax (21)">H.22. invalidAttributeSyntax (21)</A>
+<BR>
+<A HREF="#noSuchObject (32)">H.23. noSuchObject (32)</A>
+<BR>
+<A HREF="#aliasProblem (33)">H.24. aliasProblem (33)</A>
+<BR>
+<A HREF="#invalidDNSyntax (34)">H.25. invalidDNSyntax (34)</A>
+<BR>
+<A HREF="#aliasDereferencingProblem (36)">H.26. aliasDereferencingProblem (36)</A>
+<BR>
+<A HREF="#inappropriateAuthentication (48)">H.27. inappropriateAuthentication (48)</A>
+<BR>
+<A HREF="#invalidCredentials (49)">H.28. invalidCredentials (49)</A>
+<BR>
+<A HREF="#insufficientAccessRights (50)">H.29. insufficientAccessRights (50)</A>
+<BR>
+<A HREF="#busy (51)">H.30. busy (51)</A>
+<BR>
+<A HREF="#unavailable (52)">H.31. unavailable (52)</A>
+<BR>
+<A HREF="#unwillingToPerform (53)">H.32. unwillingToPerform (53)</A>
+<BR>
+<A HREF="#loopDetect (54)">H.33. loopDetect (54)</A>
+<BR>
+<A HREF="#namingViolation (64)">H.34. namingViolation (64)</A>
+<BR>
+<A HREF="#objectClassViolation (65)">H.35. objectClassViolation (65)</A>
+<BR>
+<A HREF="#notAllowedOnNonLeaf (66)">H.36. notAllowedOnNonLeaf (66)</A>
+<BR>
+<A HREF="#notAllowedOnRDN (67)">H.37. notAllowedOnRDN (67)</A>
+<BR>
+<A HREF="#entryAlreadyExists (68)">H.38. entryAlreadyExists (68)</A>
+<BR>
+<A HREF="#objectClassModsProhibited (69)">H.39. objectClassModsProhibited (69)</A>
+<BR>
+<A HREF="#affectsMultipleDSAs (71)">H.40. affectsMultipleDSAs (71)</A>
+<BR>
+<A HREF="#other (80)">H.41. other (80)</A></UL>
+<BR>
+<A HREF="#Glossary">I. Glossary</A><UL>
+<A HREF="#Terms">I.1. Terms</A>
+<BR>
+<A HREF="#Related Organizations">I.2. Related Organizations</A>
+<BR>
+<A HREF="#Related Products">I.3. Related Products</A>
+<BR>
+<A HREF="#References">I.4. References</A></UL>
+<BR>
+<A HREF="#Generic configure Instructions">J. Generic configure Instructions</A>
+<BR>
+<A HREF="#OpenLDAP Software Copyright Notices">K. OpenLDAP Software Copyright Notices</A><UL>
+<A HREF="#OpenLDAP Copyright Notice">K.1. OpenLDAP Copyright Notice</A>
+<BR>
+<A HREF="#Additional Copyright Notices">K.2. Additional Copyright Notices</A>
+<BR>
+<A HREF="#University of Michigan Copyright Notice">K.3. University of Michigan Copyright Notice</A></UL>
+<BR>
+<A HREF="#OpenLDAP Public License">L. OpenLDAP Public License</A></UL>
+</DIV>
+<DIV CLASS="main">
+<P></P>
+<HR>
+<H1><A NAME="Preface">Preface</A></H1>
+<H2>Copyright</H2>
+<P>Copyright 1998-2013, The <A HREF="https://www.openldap.org/foundation/">OpenLDAP Foundation</A>, <EM>All Rights Reserved</EM>.</P>
+<P>Copyright 1992-1996, Regents of the <A HREF="https://www.umich.edu/">University of Michigan</A>, <EM>All Rights Reserved</EM>.</P>
+<P>This document is considered a part of OpenLDAP Software. This document is subject to terms of conditions set forth in <A HREF="#OpenLDAP Software Copyright Notices">OpenLDAP Software Copyright Notices</A> and the <A HREF="#OpenLDAP Public License">OpenLDAP Public License</A>. Complete copies of the notices and associated license can be found in Appendix K and L, respectively.</P>
+<P>Portions of OpenLDAP Software and this document may be copyright by other parties and/or subject to additional restrictions. Individual source files should be consulted for additional copyright notices.</P>
+<H2>Scope of this Document</H2>
+<P>This document provides a guide for installing OpenLDAP Software 2.5 (<A HREF="http://www.openldap.org/software/">http://www.openldap.org/software/</A>) on <TERM>UNIX</TERM> (and UNIX-like) systems. The document is aimed at experienced system administrators with basic understanding of <TERM>LDAP</TERM>-based directory services.</P>
+<P>This document is meant to be used in conjunction with other OpenLDAP information resources provided with the software package and on the project's site (<A HREF="http://www.OpenLDAP.org/">http://www.OpenLDAP.org/</A>) on the <TERM>World Wide Web</TERM>. The site makes available a number of resources.</P>
+<TABLE CLASS="columns" BORDER ALIGN='Center'>
+<CAPTION ALIGN=top>OpenLDAP Resources</CAPTION>
+<TR CLASS="heading">
+<TD>
+<STRONG>Resource</STRONG>
+</TD>
+<TD>
+<STRONG>URL</STRONG>
+</TD>
+</TR>
+<TR>
+<TD>
+Document Catalog
+</TD>
+<TD>
+<A HREF="http://www.OpenLDAP.org/doc/">http://www.OpenLDAP.org/doc/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+Frequently Asked Questions
+</TD>
+<TD>
+<A HREF="http://www.OpenLDAP.org/faq/">http://www.OpenLDAP.org/faq/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+Issue Tracking System
+</TD>
+<TD>
+<A HREF="http://www.OpenLDAP.org/its/">http://www.OpenLDAP.org/its/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+Mailing Lists
+</TD>
+<TD>
+<A HREF="http://www.OpenLDAP.org/lists/">http://www.OpenLDAP.org/lists/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+Manual Pages
+</TD>
+<TD>
+<A HREF="http://www.OpenLDAP.org/software/man.cgi">http://www.OpenLDAP.org/software/man.cgi</A>
+</TD>
+</TR>
+<TR>
+<TD>
+Software Pages
+</TD>
+<TD>
+<A HREF="http://www.OpenLDAP.org/software/">http://www.OpenLDAP.org/software/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+Support Pages
+</TD>
+<TD>
+<A HREF="http://www.OpenLDAP.org/support/">http://www.OpenLDAP.org/support/</A>
+</TD>
+</TR>
+</TABLE>
+
+<P>This document is not a complete reference for OpenLDAP software; the manual pages are the definitive documentation. For best results, you should use the manual pages that were installed on your system with your version of OpenLDAP software so that you're looking at documentation that matches the code. While the OpenLDAP web site also provides the manual pages for convenience, you can not assume that they correspond to the particular version you're running.</P>
+<H2>Acknowledgments</H2>
+<P>The <A HREF="https://www.openldap.org/project/">OpenLDAP Project</A> is comprised of a team of volunteers. This document would not be possible without their contribution of time and energy.</P>
+<P>The OpenLDAP Project would also like to thank the <A HREF="https://web.archive.org/web/20160302011357/http://www.umich.edu/~dirsvcs/ldap/ldap.html">University of Michigan LDAP Team</A> for building the foundation of LDAP software and information to which OpenLDAP Software is built upon. This document is based upon University of Michigan document: <A HREF="https://web.archive.org/web/20170809071245/http://www.umich.edu/~dirsvcs/ldap/doc/guides/slapd/guide.pdf">The SLAPD and SLURPD Administrators Guide</A>.</P>
+<H2>Amendments</H2>
+<P>Suggested enhancements and corrections to this document should be submitted using the <A HREF="https://www.openldap.org/">OpenLDAP</A> <TERM>Issue Tracking System</TERM> (<A HREF="http://www.openldap.org/its/">http://www.openldap.org/its/</A>).</P>
+<H2>About this document</H2>
+<P>This document was produced using the <TERM>Simple Document Format</TERM> (<TERM>SDF</TERM>) documentation system (<A HREF="http://search.cpan.org/src/IANC/sdf-2.001/doc/catalog.html">http://search.cpan.org/src/IANC/sdf-2.001/doc/catalog.html</A>) developed by <EM>Ian Clatworthy</EM>. Tools for SDF are available from <A HREF="https://www.cpan.org/">CPAN</A> (<A HREF="http://search.cpan.org/search?query=SDF&amp;mode=dist">http://search.cpan.org/search?query=SDF&amp;mode=dist</A>).</P>
+<P></P>
+<HR>
+<H1><A NAME="Introduction to OpenLDAP Directory Services">1. Introduction to OpenLDAP Directory Services</A></H1>
+<P>This document describes how to build, configure, and operate <A HREF="https://www.openldap.org/">OpenLDAP</A> Software to provide directory services. This includes details on how to configure and run the Standalone <TERM>LDAP</TERM> Daemon, <EM>slapd</EM>(8). It is intended for new and experienced administrators alike. This section provides a basic introduction to directory services and, in particular, the directory services provided by <EM>slapd</EM>(8). This introduction is only intended to provide enough information so one might get started learning about <TERM>LDAP</TERM>, <TERM>X.500</TERM>, and directory services.</P>
+<H2><A NAME="What is a directory service">1.1. What is a directory service?</A></H2>
+<P>A directory is a specialized database specifically designed for searching and browsing, in additional to supporting basic lookup and update functions.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>A directory is defined by some as merely a database optimized for read access. This definition, at best, is overly simplistic.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>Directories tend to contain descriptive, attribute-based information and support sophisticated filtering capabilities. Directories generally do not support complicated transaction or roll-back schemes found in database management systems designed for handling high-volume complex updates. Directory updates are typically simple all-or-nothing changes, if they are allowed at all. Directories are generally tuned to give quick response to high-volume lookup or search operations. They may have the ability to replicate information widely in order to increase availability and reliability, while reducing response time. When directory information is replicated, temporary inconsistencies between the consumers may be okay, as long as inconsistencies are resolved in a timely manner.</P>
+<P>There are many different ways to provide a directory service. Different methods allow different kinds of information to be stored in the directory, place different requirements on how that information can be referenced, queried and updated, how it is protected from unauthorized access, etc. Some directory services are <EM>local</EM>, providing service to a restricted context (e.g., the finger service on a single machine). Other services are global, providing service to a much broader context (e.g., the entire Internet). Global services are usually <EM>distributed</EM>, meaning that the data they contain is spread across many machines, all of which cooperate to provide the directory service. Typically a global service defines a uniform <EM>namespace</EM> which gives the same view of the data no matter where you are in relation to the data itself.</P>
+<P>A web directory, such as provided by the <EM>Curlie Project</EM> &lt;<A HREF="https://curlie.org">https://curlie.org</A>&gt;, is a good example of a directory service. These services catalog web pages and are specifically designed to support browsing and searching.</P>
+<P>While some consider the Internet <TERM>Domain Name System</TERM> (DNS) is an example of a globally distributed directory service, DNS is not browsable nor searchable. It is more properly described as a globally distributed <EM>lookup</EM> service.</P>
+<H2><A NAME="What is LDAP">1.2. What is LDAP?</A></H2>
+<P><TERM>LDAP</TERM> stands for <TERM>Lightweight Directory Access Protocol</TERM>. As the name suggests, it is a lightweight protocol for accessing directory services, specifically <TERM>X.500</TERM>-based directory services. LDAP runs over <TERM>TCP</TERM>/<TERM>IP</TERM> or other connection oriented transfer services. LDAP is an <A HREF="https://www.ietf.org/">IETF</A> Standard Track protocol and is specified in &quot;Lightweight Directory Access Protocol (LDAP) Technical Specification Road Map&quot; <A HREF="https://www.rfc-editor.org/rfc/rfc4510.txt">RFC4510</A>.</P>
+<P>This section gives an overview of LDAP from a user's perspective.</P>
+<P><EM>What kind of information can be stored in the directory?</EM> The LDAP information model is based on <EM>entries</EM>. An entry is a collection of attributes that has a globally-unique <TERM>Distinguished Name</TERM> (DN). The DN is used to refer to the entry unambiguously. Each of the entry's attributes has a <EM>type</EM> and one or more <EM>values</EM>. The types are typically mnemonic strings, like &quot;<TT>cn</TT>&quot; for common name, or &quot;<TT>mail</TT>&quot; for email address. The syntax of values depend on the attribute type. For example, a <TT>cn</TT> attribute might contain the value <TT>Babs Jensen</TT>. A <TT>mail</TT> attribute might contain the value &quot;<TT>babs@example.com</TT>&quot;. A <TT>jpegPhoto</TT> attribute would contain a photograph in the <TERM>JPEG</TERM> (binary) format.</P>
+<P><EM>How is the information arranged?</EM> In LDAP, directory entries are arranged in a hierarchical tree-like structure. Traditionally, this structure reflected the geographic and/or organizational boundaries. Entries representing countries appear at the top of the tree. Below them are entries representing states and national organizations. Below them might be entries representing organizational units, people, printers, documents, or just about anything else you can think of. Figure 1.1 shows an example LDAP directory tree using traditional naming.</P>
+<P><CENTER><IMG SRC="intro_tree.png" ALIGN="center"></CENTER></P>
+<P ALIGN="Center">Figure 1.1: LDAP directory tree (traditional naming)</P>
+<P>The tree may also be arranged based upon Internet domain names. This naming approach is becoming increasing popular as it allows for directory services to be located using the <EM>DNS</EM>. Figure 1.2 shows an example LDAP directory tree using domain-based naming.</P>
+<P><CENTER><IMG SRC="intro_dctree.png" ALIGN="center"></CENTER></P>
+<P ALIGN="Center">Figure 1.2: LDAP directory tree (Internet naming)</P>
+<P>In addition, LDAP allows you to control which attributes are required and allowed in an entry through the use of a special attribute called <TT>objectClass</TT>. The values of the <TT>objectClass</TT> attribute determine the <EM>schema</EM> rules the entry must obey.</P>
+<P><EM>How is the information referenced?</EM> An entry is referenced by its distinguished name, which is constructed by taking the name of the entry itself (called the <TERM>Relative Distinguished Name</TERM> or RDN) and concatenating the names of its ancestor entries. For example, the entry for Barbara Jensen in the Internet naming example above has an RDN of <TT>uid=babs</TT> and a DN of <TT>uid=babs,ou=People,dc=example,dc=com</TT>. The full DN format is described in <A HREF="https://www.rfc-editor.org/rfc/rfc4514.txt">RFC4514</A>, &quot;LDAP: String Representation of Distinguished Names.&quot;</P>
+<P><EM>How is the information accessed?</EM> LDAP defines operations for interrogating and updating the directory. Operations are provided for adding and deleting an entry from the directory, changing an existing entry, and changing the name of an entry. Most of the time, though, LDAP is used to search for information in the directory. The LDAP search operation allows some portion of the directory to be searched for entries that match some criteria specified by a search filter. Information can be requested from each entry that matches the criteria.</P>
+<P>For example, you might want to search the entire directory subtree at and below <TT>dc=example,dc=com</TT> for people with the name <TT>Barbara Jensen</TT>, retrieving the email address of each entry found. LDAP lets you do this easily. Or you might want to search the entries directly below the <TT>st=California,c=US</TT> entry for organizations with the string <TT>Acme</TT> in their name, and that have a fax number. LDAP lets you do this too. The next section describes in more detail what you can do with LDAP and how it might be useful to you.</P>
+<P><EM>How is the information protected from unauthorized access?</EM> Some directory services provide no protection, allowing anyone to see the information. LDAP provides a mechanism for a client to authenticate, or prove its identity to a directory server, paving the way for rich access control to protect the information the server contains. LDAP also supports data security (integrity and confidentiality) services.</P>
+<H2><A NAME="When should I use LDAP">1.3. When should I use LDAP?</A></H2>
+<P>This is a very good question. In general, you should use a Directory server when you require data to be centrally managed, stored and accessible via standards based methods.</P>
+<P>Some common examples found throughout the industry are, but not limited to:</P>
+<UL>
+<LI>Machine Authentication
+<LI>User Authentication
+<LI>User/System Groups
+<LI>Address book
+<LI>Organization Representation
+<LI>Asset Tracking
+<LI>Telephony Information Store
+<LI>User resource management
+<LI>E-mail address lookups
+<LI>Application Configuration store
+<LI>PBX Configuration store
+<LI>etc.....</UL>
+<P>There are various <A HREF="#Distributed Schema Files">Distributed Schema Files</A> that are standards based, but you can always create your own <A HREF="#Schema Specification">Schema Specification</A>.</P>
+<P>There are always new ways to use a Directory and apply LDAP principles to address certain problems, therefore there is no simple answer to this question.</P>
+<P>If in doubt, join the general LDAP forum for non-commercial discussions and information relating to LDAP at: <A HREF="http://www.umich.edu/~dirsvcs/ldap/mailinglist.html">http://www.umich.edu/~dirsvcs/ldap/mailinglist.html</A> and ask</P>
+<H2><A NAME="When should I not use LDAP">1.4. When should I not use LDAP?</A></H2>
+<P>When you start finding yourself bending the directory to do what you require, maybe a redesign is needed. Or if you only require one application to use and manipulate your data (for discussion of LDAP vs RDBMS, please read the <A HREF="#LDAP vs RDBMS">LDAP vs RDBMS</A> section).</P>
+<P>It will become obvious when LDAP is the right tool for the job.</P>
+<H2><A NAME="How does LDAP work">1.5. How does LDAP work?</A></H2>
+<P>LDAP utilizes a <EM>client-server model</EM>. One or more LDAP servers contain the data making up the directory information tree (<TERM>DIT</TERM>). The client connects to servers and asks it a question. The server responds with an answer and/or with a pointer to where the client can get additional information (typically, another LDAP server). No matter which LDAP server a client connects to, it sees the same view of the directory; a name presented to one LDAP server references the same entry it would at another LDAP server. This is an important feature of a global directory service.</P>
+<H2><A NAME="What about X.500">1.6. What about X.500?</A></H2>
+<P>Technically, <TERM>LDAP</TERM> is a directory access protocol to an <TERM>X.500</TERM> directory service, the <TERM>OSI</TERM> directory service. Initially, LDAP clients accessed gateways to the X.500 directory service. This gateway ran LDAP between the client and gateway and X.500's <TERM>Directory Access Protocol</TERM> (<TERM>DAP</TERM>) between the gateway and the X.500 server. DAP is a heavyweight protocol that operates over a full OSI protocol stack and requires a significant amount of computing resources. LDAP is designed to operate over <TERM>TCP</TERM>/<TERM>IP</TERM> and provides most of the functionality of DAP at a much lower cost.</P>
+<P>While LDAP is still used to access X.500 directory service via gateways, LDAP is now more commonly directly implemented in X.500 servers.</P>
+<P>The Standalone LDAP Daemon, or <EM>slapd</EM>(8), can be viewed as a <EM>lightweight</EM> X.500 directory server. That is, it does not implement the X.500's DAP nor does it support the complete X.500 models.</P>
+<P>If you are already running a X.500 DAP service and you want to continue to do so, you can probably stop reading this guide. This guide is all about running LDAP via <EM>slapd</EM>(8), without running X.500 DAP. If you are not running X.500 DAP, want to stop running X.500 DAP, or have no immediate plans to run X.500 DAP, read on.</P>
+<P>It is possible to replicate data from an LDAP directory server to a X.500 DAP <TERM>DSA</TERM>. This requires an LDAP/DAP gateway. OpenLDAP Software does not include such a gateway.</P>
+<H2><A NAME="What is the difference between LDAPv2 and LDAPv3">1.7. What is the difference between LDAPv2 and LDAPv3?</A></H2>
+<P>LDAPv3 was developed in the late 1990's to replace LDAPv2. LDAPv3 adds the following features to LDAP:</P>
+<UL>
+<LI>Strong authentication and data security services via <TERM>SASL</TERM>
+<LI>Certificate authentication and data security services via <TERM>TLS</TERM> (SSL)
+<LI>Internationalization through the use of Unicode
+<LI>Referrals and Continuations
+<LI>Schema Discovery
+<LI>Extensibility (controls, extended operations, and more)</UL>
+<P>LDAPv2 is historic (<A HREF="https://www.rfc-editor.org/rfc/rfc3494.txt">RFC3494</A>). As most <EM>so-called</EM> LDAPv2 implementations (including <EM>slapd</EM>(8)) do not conform to the LDAPv2 technical specification, interoperability amongst implementations claiming LDAPv2 support is limited. As LDAPv2 differs significantly from LDAPv3, deploying both LDAPv2 and LDAPv3 simultaneously is quite problematic. LDAPv2 should be avoided. LDAPv2 is disabled by default.</P>
+<H2><A NAME="LDAP vs RDBMS">1.8. LDAP vs RDBMS</A></H2>
+<P>This question is raised many times, in different forms. The most common, however, is: <EM>Why doesn't OpenLDAP use a relational database management system (RDBMS) instead of an embedded key/value store like LMDB?</EM> In general, expecting that the sophisticated algorithms implemented by commercial-grade RDBMS would make <EM>OpenLDAP</EM> be faster or somehow better and, at the same time, permitting sharing of data with other applications.</P>
+<P>The short answer is that use of an embedded database and custom indexing system allows OpenLDAP to provide greater performance and scalability without loss of reliability. OpenLDAP uses <TERM>LMDB</TERM> concurrent / transactional database software.</P>
+<P>Now for the long answer. We are all confronted all the time with the choice RDBMSes vs. directories. It is a hard choice and no simple answer exists.</P>
+<P>It is tempting to think that having a RDBMS backend to the directory solves all problems. However, it is a pig. This is because the data models are very different. Representing directory data with a relational database is going to require splitting data into multiple tables.</P>
+<P>Think for a moment about the person objectclass. Its definition requires attribute types objectclass, sn and cn and allows attribute types userPassword, telephoneNumber, seeAlso and description. All of these attributes are multivalued, so a normalization requires putting each attribute type in a separate table.</P>
+<P>Now you have to decide on appropriate keys for those tables. The primary key might be a combination of the DN, but this becomes rather inefficient on most database implementations.</P>
+<P>The big problem now is that accessing data from one entry requires seeking on different disk areas. On some applications this may be OK but in many applications performance suffers.</P>
+<P>The only attribute types that can be put in the main table entry are those that are mandatory and single-value. You may add also the optional single-valued attributes and set them to NULL or something if not present.</P>
+<P>But wait, the entry can have multiple objectclasses and they are organized in an inheritance hierarchy. An entry of objectclass organizationalPerson now has the attributes from person plus a few others and some formerly optional attribute types are now mandatory.</P>
+<P>What to do? Should we have different tables for the different objectclasses? This way the person would have an entry on the person table, another on organizationalPerson, etc. Or should we get rid of person and put everything on the second table?</P>
+<P>But what do we do with a filter like (cn=*) where cn is an attribute type that appears in many, many objectclasses. Should we search all possible tables for matching entries? Not very attractive.</P>
+<P>Once this point is reached, three approaches come to mind. One is to do full normalization so that each attribute type, no matter what, has its own separate table. The simplistic approach where the DN is part of the primary key is extremely wasteful, and calls for an approach where the entry has a unique numeric id that is used instead for the keys and a main table that maps DNs to ids. The approach, anyway, is very inefficient when several attribute types from one or more entries are requested. Such a database, though cumbersomely, can be managed from SQL applications.</P>
+<P>The second approach is to put the whole entry as a blob in a table shared by all entries regardless of the objectclass and have additional tables that act as indices for the first table. Index tables are not database indices, but are fully managed by the LDAP server-side implementation. However, the database becomes unusable from SQL. And, thus, a fully fledged database system provides little or no advantage. The full generality of the database is unneeded. Much better to use something light and fast, like <TERM>LMDB</TERM>.</P>
+<P>A completely different way to see this is to give up any hopes of implementing the directory data model. In this case, LDAP is used as an access protocol to data that provides only superficially the directory data model. For instance, it may be read only or, where updates are allowed, restrictions are applied, such as making single-value attribute types that would allow for multiple values. Or the impossibility to add new objectclasses to an existing entry or remove one of those present. The restrictions span the range from allowed restrictions (that might be elsewhere the result of access control) to outright violations of the data model. It can be, however, a method to provide LDAP access to preexisting data that is used by other applications. But in the understanding that we don't really have a &quot;directory&quot;.</P>
+<P>Existing commercial LDAP server implementations that use a relational database are either from the first kind or the third. I don't know of any implementation that uses a relational database to do inefficiently what LMDB does efficiently. For those who are interested in &quot;third way&quot; (exposing EXISTING data from RDBMS as LDAP tree, having some limitations compared to classic LDAP model, but making it possible to interoperate between LDAP and SQL applications):</P>
+<P>OpenLDAP includes back-sql - the backend that makes it possible. It uses ODBC + additional metainformation about translating LDAP queries to SQL queries in your RDBMS schema, providing different levels of access - from read-only to full access depending on RDBMS you use, and your schema.</P>
+<P>For more information on concept and limitations, see <EM>slapd-sql</EM>(5) man page, or the <A HREF="#Backends">Backends</A> section. There are also several examples for several RDBMSes in <TT>back-sql/rdbms_depend/*</TT> subdirectories.</P>
+<H2><A NAME="What is slapd and what can it do">1.9. What is slapd and what can it do?</A></H2>
+<P><EM>slapd</EM>(8) is an LDAP directory server that runs on many different platforms. You can use it to provide a directory service of your very own. Your directory can contain pretty much anything you want to put in it. You can connect it to the global LDAP directory service, or run a service all by yourself. Some of slapd's more interesting features and capabilities include:</P>
+<P><B>LDAPv3</B>: <EM>slapd</EM> implements version 3 of <TERM>Lightweight Directory Access Protocol</TERM>. <EM>slapd</EM> supports LDAP over both <TERM>IPv4</TERM> and <TERM>IPv6</TERM> and Unix <TERM>IPC</TERM>.</P>
+<P><B><TERM>Simple Authentication and Security Layer</TERM></B>: <EM>slapd</EM> supports strong authentication and data security (integrity and confidentiality) services through the use of SASL. <EM>slapd</EM>'s SASL implementation utilizes <A HREF="https://www.cyrusimap.org/sasl/">Cyrus SASL</A> software which supports a number of mechanisms including <TERM>DIGEST-MD5</TERM>, <TERM>EXTERNAL</TERM>, and <TERM>GSSAPI</TERM>.</P>
+<P><B><TERM>Transport Layer Security</TERM></B>: <EM>slapd</EM> supports certificate-based authentication and data security (integrity and confidentiality) services through the use of TLS (or SSL). <EM>slapd</EM>'s TLS implementation can utilize <A HREF="https://www.openssl.org/">OpenSSL</A> or <A HREF="https://gnutls.org/">GnuTLS</A>, software.</P>
+<P><B>Topology control</B>: <EM>slapd</EM> can be configured to restrict access at the socket layer based upon network topology information. This feature utilizes <EM>TCP wrappers</EM>.</P>
+<P><B>Access control</B>: <EM>slapd</EM> provides a rich and powerful access control facility, allowing you to control access to the information in your database(s). You can control access to entries based on LDAP authorization information, <TERM>IP</TERM> address, domain name and other criteria. <EM>slapd</EM> supports both <EM>static</EM> and <EM>dynamic</EM> access control information.</P>
+<P><B>Internationalization</B>: <EM>slapd</EM> supports Unicode and language tags.</P>
+<P><B>Choice of database backends</B>: <EM>slapd</EM> comes with a variety of different database backends you can choose from. They include <TERM>MDB</TERM>, a hierarchical high-performance transactional database backend; and PASSWD, a simple backend interface to the <EM>passwd</EM>(5) file. The MDB backend utilizes <TERM>LMDB</TERM>.</P>
+<P><B>Multiple database instances</B>: <EM>slapd</EM> can be configured to serve multiple databases at the same time. This means that a single <EM>slapd</EM> server can respond to requests for many logically different portions of the LDAP tree, using the same or different database backends.</P>
+<P><B>Generic modules API</B>: If you require even more customization, <EM>slapd</EM> lets you write your own modules easily. <EM>slapd</EM> consists of two distinct parts: a front end that handles protocol communication with LDAP clients; and modules which handle specific tasks such as database operations. Because these two pieces communicate via a well-defined <TERM>C</TERM> <TERM>API</TERM>, you can write your own customized modules which extend <EM>slapd</EM> in numerous ways. Also, a number of <EM>programmable database</EM> modules are provided. These allow you to expose external data sources to <EM>slapd</EM> using popular programming languages (<A HREF="https://www.perl.org/">Perl</A>, and <TERM>SQL</TERM>).</P>
+<P><B>Threads</B>: <EM>slapd</EM> is threaded for high performance. A single multi-threaded <EM>slapd</EM> process handles all incoming requests using a pool of threads. This reduces the amount of system overhead required while providing high performance.</P>
+<P><B>Replication</B>: <EM>slapd</EM> can be configured to maintain shadow copies of directory information. This <EM>single-provider/multiple-consumer</EM> replication scheme is vital in high-volume environments where a single <EM>slapd</EM> installation just doesn't provide the necessary availability or reliability. For extremely demanding environments where a single point of failure is not acceptable, <EM>multi-provider</EM> replication is also available. With <EM>multi-provider</EM> replication two or more nodes can accept write operations allowing for redundancy at the provider level.</P>
+<P><EM>slapd</EM> includes support for <EM>LDAP Sync</EM>-based replication.</P>
+<P><B>Proxy Cache</B>: <EM>slapd</EM> can be configured as a caching LDAP proxy service.</P>
+<P><B>Configuration</B>: <EM>slapd</EM> is highly configurable through a single configuration file which allows you to change just about everything you'd ever want to change. Configuration options have reasonable defaults, making your job much easier. Configuration can also be performed dynamically using LDAP itself, which greatly improves manageability.</P>
+<H2><A NAME="What is lloadd and what can it do">1.10. What is lloadd and what can it do?</A></H2>
+<P><EM>lloadd</EM>(8) is a daemon that provides an LDAPv3 load balancer service. It is responsible for distributing requests across a set of <EM>slapd</EM> instances.</P>
+<P>See the <A HREF="#Load Balancing with lloadd">Load Balancing with lloadd</A> chapter for information about how to configure and run <EM>lloadd</EM>(8).</P>
+<P>Alternatively, the load balancer can run as a module embedded inside of <EM>slapd</EM>. This is also described in the <A HREF="#Load Balancing with lloadd">Load Balancing with lloadd</A> chapter.</P>
+<P></P>
+<HR>
+<H1><A NAME="A Quick-Start Guide">2. A Quick-Start Guide</A></H1>
+<P>The following is a quick start guide to OpenLDAP Software 2.5, including the Standalone <TERM>LDAP</TERM> Daemon, <EM>slapd</EM>(8).</P>
+<P>It is meant to walk you through the basic steps needed to install and configure <A HREF="https://www.openldap.org/software/">OpenLDAP Software</A>. It should be used in conjunction with the other chapters of this document, manual pages, and other materials provided with the distribution (e.g. the <TT>INSTALL</TT> document) or on the <A HREF="https://www.openldap.org/">OpenLDAP</A> web site (<A HREF="http://www.OpenLDAP.org">http://www.OpenLDAP.org</A>), in particular the OpenLDAP Software <TERM>FAQ</TERM> (<A HREF="http://www.OpenLDAP.org/faq/?file=2">http://www.OpenLDAP.org/faq/?file=2</A>).</P>
+<P>If you intend to run OpenLDAP Software seriously, you should review all of this document before attempting to install the software.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>This quick start guide does not use strong authentication nor any integrity or confidential protection services. These services are described in other chapters of the OpenLDAP Administrator's Guide.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<UL>
+&nbsp;</UL><OL>
+<LI><B>Get the software</B>
+<BR>
+You can obtain a copy of the software by following the instructions on the OpenLDAP Software download page (<A HREF="http://www.openldap.org/software/download/">http://www.openldap.org/software/download/</A>). It is recommended that new users start with the latest <EM>release</EM>.
+<BR>
+&nbsp;
+<LI><B>Unpack the distribution</B>
+<BR>
+Pick a directory for the source to live under, change directory to there, and unpack the distribution using the following commands:<UL>
+<TT>gunzip -c openldap-VERSION.tgz | tar xvfB -</TT></UL>
+<BR>
+then relocate yourself into the distribution directory:<UL>
+<TT>cd openldap-VERSION</TT></UL>
+<BR>
+You'll have to replace <TT>VERSION</TT> with the version name of the release.
+<BR>
+&nbsp;
+<LI><B>Review documentation</B>
+<BR>
+You should now review the <TT>COPYRIGHT</TT>, <TT>LICENSE</TT>, <TT>README</TT> and <TT>INSTALL</TT> documents provided with the distribution. The <TT>COPYRIGHT</TT> and <TT>LICENSE</TT> provide information on acceptable use, copying, and limitation of warranty of OpenLDAP Software.
+<BR>
+&nbsp;
+<BR>
+You should also review other chapters of this document. In particular, the <A HREF="#Building and Installing OpenLDAP Software">Building and Installing OpenLDAP Software</A> chapter of this document provides detailed information on prerequisite software and installation procedures.
+<BR>
+&nbsp;
+<LI><B>Run <TT>configure</TT></B>
+<BR>
+You will need to run the provided <TT>configure</TT> script to <EM>configure</EM> the distribution for building on your system. The <TT>configure</TT> script accepts many command line options that enable or disable optional software features. Usually the defaults are okay, but you may want to change them. To get a complete list of options that <TT>configure</TT> accepts, use the <TT>--help</TT> option:<UL>
+<TT>./configure --help</TT></UL>
+<BR>
+However, given that you are using this guide, we'll assume you are brave enough to just let <TT>configure</TT> determine what's best:<UL>
+<TT>./configure</TT></UL>
+<BR>
+Assuming <TT>configure</TT> doesn't dislike your system, you can proceed with building the software. If <TT>configure</TT> did complain, well, you'll likely need to go to the Software FAQ <EM>Installation</EM> section (<A HREF="http://www.openldap.org/faq/?file=8">http://www.openldap.org/faq/?file=8</A>) and/or actually read the <A HREF="#Building and Installing OpenLDAP Software">Building and Installing OpenLDAP Software</A> chapter of this document.
+<BR>
+&nbsp;
+<LI><B>Build the software</B>.
+<BR>
+The next step is to build the software. This step has two parts, first we construct dependencies and then we compile the software:<UL>
+<TT>make depend</TT>
+<BR>
+<TT>make</TT></UL>
+<BR>
+Both makes should complete without error.
+<BR>
+&nbsp;
+<LI><B>Test the build</B>.
+<BR>
+To ensure a correct build, you should run the test suite (it only takes a few minutes):<UL>
+<TT>make test</TT></UL>
+<BR>
+Tests which apply to your configuration will run and they should pass. Some tests, such as the replication test, may be skipped.
+<BR>
+&nbsp;
+<LI><B>Install the software</B>.
+<BR>
+You are now ready to install the software; this usually requires <EM>super-user</EM> privileges:<UL>
+<TT>su root -c 'make install'</TT></UL>
+<BR>
+Everything should now be installed under <TT>/usr/local</TT> (or whatever installation prefix was used by <TT>configure</TT>).
+<BR>
+&nbsp;
+<LI><B>Edit the configuration file</B>.
+<BR>
+Use your favorite editor to edit the provided <EM>slapd.ldif</EM> example (usually installed as <TT>/usr/local/etc/openldap/slapd.ldif</TT>) to contain a MDB database definition of the form:<UL>
+<TT>dn: olcDatabase=mdb,cn=config</TT>
+<BR>
+<TT>objectClass: olcDatabaseConfig</TT>
+<BR>
+<TT>objectClass: olcMdbConfig</TT>
+<BR>
+<TT>olcDatabase: mdb</TT>
+<BR>
+<TT>OlcDbMaxSize: 1073741824</TT>
+<BR>
+<TT>olcSuffix: dc=&lt;MY-DOMAIN&gt;,dc=&lt;COM&gt;</TT>
+<BR>
+<TT>olcRootDN: cn=Manager,dc=&lt;MY-DOMAIN&gt;,dc=&lt;COM&gt;</TT>
+<BR>
+<TT>olcRootPW: secret</TT>
+<BR>
+<TT>olcDbDirectory: /usr/local/var/openldap-data</TT>
+<BR>
+<TT>olcDbIndex: objectClass eq</TT></UL>
+<BR>
+Be sure to replace <TT>&lt;MY-DOMAIN&gt;</TT> and <TT>&lt;COM&gt;</TT> with the appropriate domain components of your domain name. For example, for <TT>example.com</TT>, use:<UL>
+<TT>dn: olcDatabase=mdb,cn=config</TT>
+<BR>
+<TT>objectClass: olcDatabaseConfig</TT>
+<BR>
+<TT>objectClass: olcMdbConfig</TT>
+<BR>
+<TT>olcDatabase: mdb</TT>
+<BR>
+<TT>OlcDbMaxSize: 1073741824</TT>
+<BR>
+<TT>olcSuffix: dc=example,dc=com</TT>
+<BR>
+<TT>olcRootDN: cn=Manager,dc=example,dc=com</TT>
+<BR>
+<TT>olcRootPW: secret</TT>
+<BR>
+<TT>olcDbDirectory: /usr/local/var/openldap-data</TT>
+<BR>
+<TT>olcDbIndex: objectClass eq</TT></UL>
+<BR>
+If your domain contains additional components, such as <TT>eng.uni.edu.eu</TT>, use:<UL>
+<TT>dn: olcDatabase=mdb,cn=config</TT>
+<BR>
+<TT>objectClass: olcDatabaseConfig</TT>
+<BR>
+<TT>objectClass: olcMdbConfig</TT>
+<BR>
+<TT>olcDatabase: mdb</TT>
+<BR>
+<TT>OlcDbMaxSize: 1073741824</TT>
+<BR>
+<TT>olcSuffix: dc=eng,dc=uni,dc=edu,dc=eu</TT>
+<BR>
+<TT>olcRootDN: cn=Manager,dc=eng,dc=uni,dc=edu,dc=eu</TT>
+<BR>
+<TT>olcRootPW: secret</TT>
+<BR>
+<TT>olcDbDirectory: /usr/local/var/openldap-data</TT>
+<BR>
+<TT>olcDbIndex: objectClass eq</TT></UL>
+<BR>
+Details regarding configuring <EM>slapd</EM>(8) can be found in the <EM>slapd-config</EM>(5) manual page and the <A HREF="#Configuring slapd">Configuring slapd</A> chapter of this document. Note that the specified olcDbDirectory must exist prior to starting <EM>slapd</EM>(8).
+<BR>
+&nbsp;
+<LI><B>Import the configuration database</B>
+<BR>
+You are now ready to import your configuration database for use by <EM>slapd</EM>(8), by running the command:<UL>
+<TT> su root -c /usr/local/sbin/slapadd -n 0 -F /usr/local/etc/slapd.d -l /usr/local/etc/openldap/slapd.ldif</TT></UL>
+<BR>
+&nbsp;
+<LI><B>Start SLAPD</B>.
+<BR>
+You are now ready to start the Standalone LDAP Daemon, <EM>slapd</EM>(8), by running the command:<UL>
+<TT>su root -c /usr/local/libexec/slapd -F /usr/local/etc/slapd.d</TT></UL>
+<BR>
+To check to see if the server is running and configured correctly, you can run a search against it with <EM>ldapsearch</EM>(1). By default, <EM>ldapsearch</EM> is installed as <TT>/usr/local/bin/ldapsearch</TT>:<UL>
+<TT>ldapsearch -x -b '' -s base '(objectclass=*)' namingContexts</TT></UL>
+<BR>
+Note the use of single quotes around command parameters to prevent special characters from being interpreted by the shell. This should return:<UL>
+<TT>dn:</TT>
+<BR>
+<TT>namingContexts: dc=example,dc=com</TT></UL>
+<BR>
+Details regarding running <EM>slapd</EM>(8) can be found in the <EM>slapd</EM>(8) manual page and the <A HREF="#Running slapd">Running slapd</A> chapter of this document.
+<BR>
+&nbsp;
+<LI><B>Add initial entries to your directory</B>.
+<BR>
+You can use <EM>ldapadd</EM>(1) to add entries to your LDAP directory. <EM>ldapadd</EM> expects input in <TERM>LDIF</TERM> form. We'll do it in two steps:<OL>
+<LI>create an LDIF file
+<LI>run ldapadd</OL>
+<BR>
+Use your favorite editor and create an LDIF file that contains:<UL>
+<TT>dn: dc=&lt;MY-DOMAIN&gt;,dc=&lt;COM&gt;</TT>
+<BR>
+<TT>objectclass: dcObject</TT>
+<BR>
+<TT>objectclass: organization</TT>
+<BR>
+<TT>o: &lt;MY ORGANIZATION&gt;</TT>
+<BR>
+<TT>dc: &lt;MY-DOMAIN&gt;</TT>
+<BR>
+<TT></TT>
+<BR>
+<TT>dn: cn=Manager,dc=&lt;MY-DOMAIN&gt;,dc=&lt;COM&gt;</TT>
+<BR>
+<TT>objectclass: organizationalRole</TT>
+<BR>
+<TT>cn: Manager</TT></UL>
+<BR>
+Be sure to replace <TT>&lt;MY-DOMAIN&gt;</TT> and <TT>&lt;COM&gt;</TT> with the appropriate domain components of your domain name. <TT>&lt;MY ORGANIZATION&gt;</TT> should be replaced with the name of your organization. When you cut and paste, be sure to trim any leading and trailing whitespace from the example.<UL>
+<TT>dn: dc=example,dc=com</TT>
+<BR>
+<TT>objectclass: dcObject</TT>
+<BR>
+<TT>objectclass: organization</TT>
+<BR>
+<TT>o: Example Company</TT>
+<BR>
+<TT>dc: example</TT>
+<BR>
+<TT></TT>
+<BR>
+<TT>dn: cn=Manager,dc=example,dc=com</TT>
+<BR>
+<TT>objectclass: organizationalRole</TT>
+<BR>
+<TT>cn: Manager</TT></UL>
+<BR>
+Now, you may run <EM>ldapadd</EM>(1) to insert these entries into your directory.<UL>
+<TT>ldapadd -x -D &quot;cn=Manager,dc=&lt;MY-DOMAIN&gt;,dc=&lt;COM&gt;&quot; -W -f example.ldif</TT></UL>
+<BR>
+Be sure to replace <TT>&lt;MY-DOMAIN&gt;</TT> and <TT>&lt;COM&gt;</TT> with the appropriate domain components of your domain name. You will be prompted for the &quot;<TT>secret</TT>&quot; specified in <TT>slapd.conf</TT>. For example, for <TT>example.com</TT>, use:<UL>
+<TT>ldapadd -x -D &quot;cn=Manager,dc=example,dc=com&quot; -W -f example.ldif</TT></UL>
+<BR>
+where <TT>example.ldif</TT> is the file you created above.<UL>
+<TT> </TT></UL>
+<BR>
+Additional information regarding directory creation can be found in the <A HREF="#Database Creation and Maintenance Tools">Database Creation and Maintenance Tools</A> chapter of this document.
+<BR>
+&nbsp;
+<LI><B>See if it works</B>.
+<BR>
+Now we're ready to verify the added entries are in your directory. You can use any LDAP client to do this, but our example uses the <EM>ldapsearch</EM>(1) tool. Remember to replace <TT>dc=example,dc=com</TT> with the correct values for your site:<UL>
+<TT>ldapsearch -x -b 'dc=example,dc=com' '(objectclass=*)'</TT></UL>
+<BR>
+This command will search for and retrieve every entry in the database.</OL>
+<P>You are now ready to add more entries using <EM>ldapadd</EM>(1) or another LDAP client, experiment with various configuration options, backend arrangements, etc..</P>
+<P>Note that by default, the <EM>slapd</EM>(8) database grants <EM>read access to everybody</EM> excepting the <EM>super-user</EM> (as specified by the <TT>rootdn</TT> configuration directive). It is highly recommended that you establish controls to restrict access to authorized users. Access controls are discussed in the <A HREF="#Access Control">Access Control</A> chapter. You are also encouraged to read the <A HREF="#Security Considerations">Security Considerations</A>, <A HREF="#Using SASL">Using SASL</A> and <A HREF="#Using TLS">Using TLS</A> sections.</P>
+<P>The following chapters provide more detailed information on making, installing, and running <EM>slapd</EM>(8).</P>
+<P></P>
+<HR>
+<H1><A NAME="The Big Picture - Configuration Choices">3. The Big Picture - Configuration Choices</A></H1>
+<P>This section gives a brief overview of various <TERM>LDAP</TERM> directory configurations, and how your Standalone LDAP Daemon <EM>slapd</EM>(8) fits in with the rest of the world.</P>
+<H2><A NAME="Local Directory Service">3.1. Local Directory Service</A></H2>
+<P>In this configuration, you run a <EM>slapd</EM>(8) instance which provides directory service for your local domain only. It does not interact with other directory servers in any way. This configuration is shown in Figure 3.1.</P>
+<P><CENTER><IMG SRC="config_local.png" ALIGN="center"></CENTER></P>
+<P ALIGN="Center">Figure 3.1: Local service configuration.</P>
+<P>Use this configuration if you are just starting out (it's the one the quick-start guide makes for you) or if you want to provide a local service and are not interested in connecting to the rest of the world. It's easy to upgrade to another configuration later if you want.</P>
+<H2><A NAME="Local Directory Service with Referrals">3.2. Local Directory Service with Referrals</A></H2>
+<P>In this configuration, you run a <EM>slapd</EM>(8) instance which provides directory service for your local domain and configure it to return referrals to other servers capable of handling requests. You may run this service (or services) yourself or use one provided to you. This configuration is shown in Figure 3.2.</P>
+<P><CENTER><IMG SRC="config_ref.png" ALIGN="center"></CENTER></P>
+<P ALIGN="Center">Figure 3.2: Local service with referrals</P>
+<P>Use this configuration if you want to provide local service and participate in the Global Directory, or you want to delegate responsibility for <EM>subordinate</EM> entries to another server.</P>
+<H2><A NAME="Replicated Directory Service">3.3. Replicated Directory Service</A></H2>
+<P>slapd(8) includes support for <EM>LDAP Sync</EM>-based replication, called <EM>syncrepl</EM>, which may be used to maintain shadow copies of directory information on multiple directory servers. In its most basic configuration, the <EM>provider</EM> is a syncrepl provider and one or more <EM>consumer</EM> (or <EM>shadow</EM>) are syncrepl consumers. An example provider-consumer configuration is shown in figure 3.3. Multi-Provider configurations are also supported.</P>
+<P><CENTER><IMG SRC="config_repl.png" ALIGN="center"></CENTER></P>
+<P ALIGN="Center">Figure 3.3: Replicated Directory Services</P>
+<P>This configuration can be used in conjunction with either of the first two configurations in situations where a single <EM>slapd</EM>(8) instance does not provide the required reliability or availability.</P>
+<H2><A NAME="Distributed Local Directory Service">3.4. Distributed Local Directory Service</A></H2>
+<P>In this configuration, the local service is partitioned into smaller services, each of which may be replicated, and <EM>glued</EM> together with <EM>superior</EM> and <EM>subordinate</EM> referrals.</P>
+<P></P>
+<HR>
+<H1><A NAME="Building and Installing OpenLDAP Software">4. Building and Installing OpenLDAP Software</A></H1>
+<P>This chapter details how to build and install the <A HREF="https://www.openldap.org/">OpenLDAP</A> Software package including <EM>slapd</EM>(8), the Standalone <TERM>LDAP</TERM> Daemon. Building and installing OpenLDAP Software requires several steps: installing prerequisite software, configuring OpenLDAP Software itself, making, and finally installing. The following sections describe this process in detail.</P>
+<H2><A NAME="Obtaining and Extracting the Software">4.1. Obtaining and Extracting the Software</A></H2>
+<P>You can obtain OpenLDAP Software from the project's download page at <A HREF="http://www.openldap.org/software/download/">http://www.openldap.org/software/download/</A> or directly from the project's <TERM>FTP</TERM> service at <A HREF="ftp://ftp.openldap.org/pub/OpenLDAP/">ftp://ftp.openldap.org/pub/OpenLDAP/</A>.</P>
+<P>The project makes available two series of packages for <EM>general use</EM>. The project makes <EM>releases</EM> as new features and bug fixes come available. Though the project takes steps to improve stability of these releases, it is common for problems to arise only after <EM>release</EM>. The <EM>stable</EM> release is the latest <EM>release</EM> which has demonstrated stability through general use.</P>
+<P>Users of OpenLDAP Software can choose, depending on their desire for the <EM>latest features</EM> versus <EM>demonstrated stability</EM>, the most appropriate series to install.</P>
+<P>After downloading OpenLDAP Software, you need to extract the distribution from the compressed archive file and change your working directory to the top directory of the distribution:</P>
+<UL>
+<TT>gunzip -c openldap-VERSION.tgz | tar xf -</TT>
+<BR>
+<TT>cd openldap-VERSION</TT></UL>
+<P>You'll have to replace <TT>VERSION</TT> with the version name of the release.</P>
+<P>You should now review the <TT>COPYRIGHT</TT>, <TT>LICENSE</TT>, <TT>README</TT> and <TT>INSTALL</TT> documents provided with the distribution. The <TT>COPYRIGHT</TT> and <TT>LICENSE</TT> provide information on acceptable use, copying, and limitation of warranty of OpenLDAP Software. The <TT>README</TT> and <TT>INSTALL</TT> documents provide detailed information on prerequisite software and installation procedures.</P>
+<H2><A NAME="Prerequisite software">4.2. Prerequisite software</A></H2>
+<P>OpenLDAP Software relies upon a number of software packages distributed by third parties. Depending on the features you intend to use, you may have to download and install a number of additional software packages. This section details commonly needed third party software packages you might have to install. However, for an up-to-date prerequisite information, the <TT>README</TT> document should be consulted. Note that some of these third party packages may depend on additional software packages. Install each package per the installation instructions provided with it.</P>
+<H3><A NAME="{{TERM[expand]TLS}}">4.2.1. <TERM>Transport Layer Security</TERM></A></H3>
+<P>OpenLDAP clients and servers require installation of <A HREF="https://www.openssl.org/">OpenSSL</A> or <A HREF="https://gnutls.org/">GnuTLS</A> <TERM>TLS</TERM> libraries to provide <TERM>Transport Layer Security</TERM> services. Though some operating systems may provide these libraries as part of the base system or as an optional software component, OpenSSL and GnuTLS often require separate installation.</P>
+<P>OpenSSL is available from <A HREF="http://www.openssl.org/">http://www.openssl.org/</A>. GnuTLS is available from <A HREF="http://www.gnu.org/software/gnutls/">http://www.gnu.org/software/gnutls/</A>.</P>
+<P>OpenLDAP Software will not be fully LDAPv3 compliant unless OpenLDAP's <TT>configure</TT> detects a usable TLS library.</P>
+<H3><A NAME="{{TERM[expand]SASL}}">4.2.2. <TERM>Simple Authentication and Security Layer</TERM></A></H3>
+<P>OpenLDAP clients and servers require installation of <A HREF="https://www.cyrusimap.org/sasl/">Cyrus SASL</A> libraries to provide <TERM>Simple Authentication and Security Layer</TERM> services. Though some operating systems may provide this library as part of the base system or as an optional software component, Cyrus SASL often requires separate installation.</P>
+<P>Cyrus SASL is available from <A HREF="http://asg.web.cmu.edu/sasl/sasl-library.html">http://asg.web.cmu.edu/sasl/sasl-library.html</A>. Cyrus SASL will make use of OpenSSL and Kerberos/GSSAPI libraries if preinstalled.</P>
+<P>OpenLDAP Software will not be fully LDAPv3 compliant unless OpenLDAP's configure detects a usable Cyrus SASL installation.</P>
+<H3><A NAME="{{TERM[expand]Kerberos}}">4.2.3. <TERM>Kerberos Authentication Service</TERM></A></H3>
+<P>OpenLDAP clients and servers support <TERM>Kerberos</TERM> authentication services. In particular, OpenLDAP supports the Kerberos V <TERM>GSS-API</TERM> <TERM>SASL</TERM> authentication mechanism known as the <TERM>GSSAPI</TERM> mechanism. This feature requires, in addition to Cyrus SASL libraries, either <A HREF="https://github.com/heimdal/">Heimdal</A> or <A HREF="https://web.mit.edu/kerberos/">MIT Kerberos</A> V libraries.</P>
+<P>Heimdal Kerberos is available from <A HREF="https://github.com/heimdal/heimdal/">https://github.com/heimdal/heimdal/</A>. MIT Kerberos is available from <A HREF="http://web.mit.edu/kerberos/www/">http://web.mit.edu/kerberos/www/</A>.</P>
+<P>Use of strong authentication services, such as those provided by Kerberos, is highly recommended.</P>
+<H3><A NAME="Database Software">4.2.4. Database Software</A></H3>
+<P>OpenLDAP's <EM>slapd</EM>(8) <TERM>MDB</TERM> primary database backend uses the <TERM>LMDB</TERM> software included with the OpenLDAP source. There is no need to download any additional software to have <EM>MDB</EM> support.</P>
+<H3><A NAME="Threads">4.2.5. Threads</A></H3>
+<P>OpenLDAP is designed to take advantage of threads. OpenLDAP supports POSIX <EM>pthreads</EM>, NT threads and a number of other varieties. <TT>configure</TT> will complain if it cannot find a suitable thread subsystem. If this occurs, please consult the <TT>Software|Installation|Platform Hints</TT> section of the OpenLDAP FAQ <A HREF="http://www.openldap.org/faq/">http://www.openldap.org/faq/</A>.</P>
+<H3><A NAME="TCP Wrappers">4.2.6. TCP Wrappers</A></H3>
+<P><EM>slapd</EM>(8) supports TCP Wrappers (IP level access control filters) if preinstalled. Use of TCP Wrappers or other IP-level access filters (such as those provided by an IP-level firewall) is recommended for servers containing non-public information.</P>
+<H2><A NAME="Running configure">4.3. Running configure</A></H2>
+<P>Now you should probably run the <TT>configure</TT> script with the <TT>--help</TT> option. This will give you a list of options that you can change when building OpenLDAP. Many of the features of OpenLDAP can be enabled or disabled using this method.</P>
+<PRE>
+ ./configure --help
+</PRE>
+<P>The <TT>configure</TT> script also looks for certain variables on the command line and in the environment. These include:</P>
+<TABLE CLASS="columns" BORDER ALIGN='Center'>
+<CAPTION ALIGN=top>Table 4.1: Variables</CAPTION>
+<TR CLASS="heading">
+<TD>
+<STRONG>Variable</STRONG>
+</TD>
+<TD>
+<STRONG>Description</STRONG>
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>CC</TT>
+</TD>
+<TD>
+Specify alternative C Compiler
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>CFLAGS</TT>
+</TD>
+<TD>
+Specify additional compiler flags
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>CPPFLAGS</TT>
+</TD>
+<TD>
+Specify C Preprocessor flags
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>LDFLAGS</TT>
+</TD>
+<TD>
+Specify linker flags
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>LIBS</TT>
+</TD>
+<TD>
+Specify additional libraries
+</TD>
+</TR>
+</TABLE>
+
+<P>Now run the configure script with any desired configuration options or variables.</P>
+<PRE>
+ ./configure [options] [variable=value ...]
+</PRE>
+<P>As an example, let's assume that we want to install OpenLDAP with MDB backend and TCP Wrappers support. By default, MDB is enabled and TCP Wrappers is not. So, we just need to specify <TT>--enable-wrappers</TT> to include TCP Wrappers support:</P>
+<PRE>
+ ./configure --enable-wrappers
+</PRE>
+<P>However, this will fail to locate dependent software not installed in system directories. For example, if TCP Wrappers headers and libraries are installed in <TT>/usr/local/include</TT> and <TT>/usr/local/lib</TT> respectively, the <TT>configure</TT> script should typically be called as follows:</P>
+<PRE>
+ ./configure --enable-wrappers \
+ CPPFLAGS=&quot;-I/usr/local/include&quot; \
+ LDFLAGS=&quot;-L/usr/local/lib -Wl,-rpath,/usr/local/lib&quot;
+</PRE>
+<P>The <TT>configure</TT> script will normally auto-detect appropriate settings. If you have problems at this stage, consult any platform specific hints and check your <TT>configure</TT> options, if any.</P>
+<H2><A NAME="Building the Software">4.4. Building the Software</A></H2>
+<P>Once you have run the <TT>configure</TT> script the last line of output should be:</P>
+<PRE>
+ Please &quot;make depend&quot; to build dependencies
+</PRE>
+<P>If the last line of output does not match, <TT>configure</TT> has failed, and you will need to review its output to determine what went wrong. You should not proceed until <TT>configure</TT> completes successfully.</P>
+<P>To build dependencies, run:</P>
+<PRE>
+ make depend
+</PRE>
+<P>Now build the software, this step will actually compile OpenLDAP.</P>
+<PRE>
+ make
+</PRE>
+<P>You should examine the output of this command carefully to make sure everything is built correctly. Note that this command builds the LDAP libraries and associated clients as well as <EM>slapd</EM>(8).</P>
+<H2><A NAME="Testing the Software">4.5. Testing the Software</A></H2>
+<P>Once the software has been properly configured and successfully made, you should run the test suite to verify the build.</P>
+<PRE>
+ make test
+</PRE>
+<P>Tests which apply to your configuration will run and they should pass. Some tests, such as the replication test, may be skipped if not supported by your configuration.</P>
+<H2><A NAME="Installing the Software">4.6. Installing the Software</A></H2>
+<P>Once you have successfully tested the software, you are ready to install it. You will need to have write permission to the installation directories you specified when you ran configure. By default OpenLDAP Software is installed in <TT>/usr/local</TT>. If you changed this setting with the <TT>--prefix</TT> configure option, it will be installed in the location you provided.</P>
+<P>Typically, the installation requires <EM>super-user</EM> privileges. From the top level OpenLDAP source directory, type:</P>
+<PRE>
+ su root -c 'make install'
+</PRE>
+<P>and enter the appropriate password when requested.</P>
+<P>You should examine the output of this command carefully to make sure everything is installed correctly. You will find the configuration files for <EM>slapd</EM>(8) in <TT>/usr/local/etc/openldap</TT> by default. See the chapter <A HREF="#Configuring slapd">Configuring slapd</A> for additional information.</P>
+<P></P>
+<HR>
+<H1><A NAME="Configuring slapd">5. Configuring slapd</A></H1>
+<P>Once the software has been built and installed, you are ready to configure <EM>slapd</EM>(8) for use at your site.</P>
+<P>OpenLDAP 2.3 and later have transitioned to using a dynamic runtime configuration engine, <EM>slapd-config</EM>(5). <EM>slapd-config</EM>(5)</P>
+<UL>
+<LI>is fully LDAP-enabled
+<LI>is managed using the standard LDAP operations
+<LI>stores its configuration data in an <TERM>LDIF</TERM> database, generally in the <TT>/usr/local/etc/openldap/slapd.d</TT> directory.
+<LI>allows all of slapd's configuration options to be changed on the fly, generally without requiring a server restart for the changes to take effect.</UL>
+<P>This chapter describes the general format of the <EM>slapd-config</EM>(5) configuration system, followed by a detailed description of commonly used settings.</P>
+<P>The older style <EM>slapd.conf</EM>(5) file is still supported, but its use is deprecated and support for it will be withdrawn in a future OpenLDAP release. Configuring <EM>slapd</EM>(8) via <EM>slapd.conf</EM>(5) is described in the next chapter.</P>
+<P>Refer to <EM>slapd</EM>(8) for information on how to have slapd automatically convert from <EM>slapd.conf</EM>(5) to <EM>slapd-config</EM>(5).</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>Although the <EM>slapd-config</EM>(5) system stores its configuration as (text-based) LDIF files, you should <EM>never</EM> edit any of the LDIF files directly. Configuration changes should be performed via LDAP operations, e.g. <EM>ldapadd</EM>(1), <EM>ldapdelete</EM>(1), or <EM>ldapmodify</EM>(1). For offline modifications (when the server is not running), use <EM>slapadd</EM>(8) and <EM>slapmodify</EM>(8).
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>You will need to continue to use the older <EM>slapd.conf</EM>(5) configuration system if your OpenLDAP installation requires the use of one or more backends or overlays that have not been updated to use the <EM>slapd-config</EM>(5) system. As of OpenLDAP 2.4.33, all of the official backends have been updated. There may be additional contributed or experimental overlays that also have not been updated.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H2><A NAME="Configuration Layout">5.1. Configuration Layout</A></H2>
+<P>The slapd configuration is stored as a special LDAP directory with a predefined schema and DIT. There are specific objectClasses used to carry global configuration options, schema definitions, backend and database definitions, and assorted other items. A sample config tree is shown in Figure 5.1.</P>
+<P><CENTER><IMG SRC="config_dit.png" ALIGN="center"></CENTER></P>
+<P ALIGN="Center">Figure 5.1: Sample configuration tree.</P>
+<P>Other objects may be part of the configuration but were omitted from the illustration for clarity.</P>
+<P>The <EM>slapd-config</EM> configuration tree has a very specific structure. The root of the tree is named <TT>cn=config</TT> and contains global configuration settings. Additional settings are contained in separate child entries:</P>
+<UL>
+<LI>Dynamically loaded modules<UL>
+These may only be used if the <TT>--enable-modules</TT> option was used to configure the software.</UL>
+<LI>Schema definitions<UL>
+The <TT>cn=schema,cn=config</TT> entry contains the system schema (all the schema that is hard-coded in slapd).
+<BR>
+Child entries of <TT>cn=schema,cn=config</TT> contain user schema as loaded from config files or added at runtime.</UL>
+<LI>Backend-specific configuration
+<LI>Database-specific configuration<UL>
+Overlays are defined in children of the Database entry.
+<BR>
+Databases and Overlays may also have other miscellaneous children.</UL></UL>
+<P>The usual rules for LDIF files apply to the configuration information: Comment lines beginning with a '<TT>#</TT>' character are ignored. If a line begins with a single space, it is considered a continuation of the previous line (even if the previous line is a comment) and the single leading space is removed. Entries are separated by blank lines.</P>
+<P>The general layout of the config LDIF is as follows:</P>
+<PRE>
+ # global configuration settings
+ dn: cn=config
+ objectClass: olcGlobal
+ cn: config
+ &lt;global config settings&gt;
+
+ # schema definitions
+ dn: cn=schema,cn=config
+ objectClass: olcSchemaConfig
+ cn: schema
+ &lt;system schema&gt;
+
+ dn: cn={X}core,cn=schema,cn=config
+ objectClass: olcSchemaConfig
+ cn: {X}core
+ &lt;core schema&gt;
+
+ # additional user-specified schema
+ ...
+
+ # backend definitions
+ dn: olcBackend=&lt;typeA&gt;,cn=config
+ objectClass: olcBackendConfig
+ olcBackend: &lt;typeA&gt;
+ &lt;backend-specific settings&gt;
+
+ # database definitions
+ dn: olcDatabase={X}&lt;typeA&gt;,cn=config
+ objectClass: olcDatabaseConfig
+ olcDatabase: {X}&lt;typeA&gt;
+ &lt;database-specific settings&gt;
+
+ # subsequent definitions and settings
+ ...
+</PRE>
+<P>Some of the entries listed above have a numeric index <TT>&quot;{X}&quot;</TT> in their names. While most configuration settings have an inherent ordering dependency (i.e., one setting must take effect before a subsequent one may be set), LDAP databases are inherently unordered. The numeric index is used to enforce a consistent ordering in the configuration database, so that all ordering dependencies are preserved. In most cases the index does not have to be provided; it will be automatically generated based on the order in which entries are created.</P>
+<P>Configuration directives are specified as values of individual attributes. Most of the attributes and objectClasses used in the slapd configuration have a prefix of <TT>&quot;olc&quot;</TT> (OpenLDAP Configuration) in their names. Generally there is a one-to-one correspondence between the attributes and the old-style <TT>slapd.conf</TT> configuration keywords, using the keyword as the attribute name, with the &quot;olc&quot; prefix attached.</P>
+<P>A configuration directive may take arguments. If so, the arguments are separated by whitespace. If an argument contains whitespace, the argument should be enclosed in double quotes <TT>&quot;like this&quot;</TT>. In the descriptions that follow, arguments that should be replaced by actual text are shown in brackets <TT>&lt;&gt;</TT>.</P>
+<P>The distribution contains an example configuration file that will be installed in the <TT>/usr/local/etc/openldap</TT> directory. A number of files containing schema definitions (attribute types and object classes) are also provided in the <TT>/usr/local/etc/openldap/schema</TT> directory.</P>
+<H2><A NAME="Configuration Directives">5.2. Configuration Directives</A></H2>
+<P>This section details commonly used configuration directives. For a complete list, see the <EM>slapd-config</EM>(5) manual page. This section will treat the configuration directives in a top-down order, starting with the global directives in the <TT>cn=config</TT> entry. Each directive will be described along with its default value (if any) and an example of its use.</P>
+<H3><A NAME="cn=config">5.2.1. cn=config</A></H3>
+<P>Directives contained in this entry generally apply to the server as a whole. Most of them are system or connection oriented, not database related. This entry must have the <TT>olcGlobal</TT> objectClass.</P>
+<H4><A NAME="olcIdleTimeout: &lt;integer&gt;">5.2.1.1. olcIdleTimeout: &lt;integer&gt;</A></H4>
+<P>Specify the number of seconds to wait before forcibly closing an idle client connection. A value of 0, the default, disables this feature.</P>
+<H4><A NAME="olcLogLevel: &lt;level&gt;">5.2.1.2. olcLogLevel: &lt;level&gt;</A></H4>
+<P>This directive specifies the level at which log statements and operation statistics should be sent to syslog (currently logged to the <EM>syslogd</EM>(8) <TT>LOG_LOCAL4</TT> facility). You must have configured OpenLDAP <TT>--enable-debug</TT> (the default) for this to work, except for the two statistics levels, which are always enabled. Log levels may be specified as integers or by keyword. Multiple log levels may be used and the levels are additive. The possible values for &lt;level&gt; are:</P>
+<TABLE CLASS="columns" BORDER ALIGN='Center'>
+<CAPTION ALIGN=top>Table 5.1: Logging Levels</CAPTION>
+<TR CLASS="heading">
+<TD ALIGN='Right'>
+<STRONG>Level</STRONG>
+</TD>
+<TD ALIGN='Left'>
+<STRONG>Keyword</STRONG>
+</TD>
+<TD>
+<STRONG>Description</STRONG>
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+-1
+</TD>
+<TD ALIGN='Left'>
+any
+</TD>
+<TD>
+enable all debugging
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+0
+</TD>
+<TD ALIGN='Left'>
+&nbsp;
+</TD>
+<TD>
+no debugging
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+1
+</TD>
+<TD ALIGN='Left'>
+(0x1 trace)
+</TD>
+<TD>
+trace function calls
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+2
+</TD>
+<TD ALIGN='Left'>
+(0x2 packets)
+</TD>
+<TD>
+debug packet handling
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+4
+</TD>
+<TD ALIGN='Left'>
+(0x4 args)
+</TD>
+<TD>
+heavy trace debugging
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+8
+</TD>
+<TD ALIGN='Left'>
+(0x8 conns)
+</TD>
+<TD>
+connection management
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+16
+</TD>
+<TD ALIGN='Left'>
+(0x10 BER)
+</TD>
+<TD>
+print out packets sent and received
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+32
+</TD>
+<TD ALIGN='Left'>
+(0x20 filter)
+</TD>
+<TD>
+search filter processing
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+64
+</TD>
+<TD ALIGN='Left'>
+(0x40 config)
+</TD>
+<TD>
+configuration processing
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+128
+</TD>
+<TD ALIGN='Left'>
+(0x80 ACL)
+</TD>
+<TD>
+access control list processing
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+256
+</TD>
+<TD ALIGN='Left'>
+(0x100 stats)
+</TD>
+<TD>
+stats log connections/operations/results
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+512
+</TD>
+<TD ALIGN='Left'>
+(0x200 stats2)
+</TD>
+<TD>
+stats log entries sent
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+1024
+</TD>
+<TD ALIGN='Left'>
+(0x400 shell)
+</TD>
+<TD>
+print communication with shell backends
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+2048
+</TD>
+<TD ALIGN='Left'>
+(0x800 parse)
+</TD>
+<TD>
+print entry parsing debugging
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+16384
+</TD>
+<TD ALIGN='Left'>
+(0x4000 sync)
+</TD>
+<TD>
+syncrepl consumer processing
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+32768
+</TD>
+<TD ALIGN='Left'>
+(0x8000 none)
+</TD>
+<TD>
+only messages that get logged regardless of configured log level
+</TD>
+</TR>
+</TABLE>
+
+<P>The desired log level can be input as a single integer that combines the (ORed) desired levels, both in decimal or in hexadecimal notation, as a list of integers (that are ORed internally), or as a list of the names that are shown between brackets, such that</P>
+<PRE>
+ olcLogLevel 129
+ olcLogLevel 0x81
+ olcLogLevel 128 1
+ olcLogLevel 0x80 0x1
+ olcLogLevel acl trace
+</PRE>
+<P>are equivalent.</P>
+<P>Examples:</P>
+<PRE>
+ olcLogLevel -1
+</PRE>
+<P>This will enable all log levels.</P>
+<PRE>
+ olcLogLevel conns filter
+</PRE>
+<P>Just log the connection and search filter processing.</P>
+<PRE>
+ olcLogLevel none
+</PRE>
+<P>Log those messages that are logged regardless of the configured loglevel. This differs from setting the log level to 0, when no logging occurs. At least the <TT>None</TT> level is required to have high priority messages logged.</P>
+<P>Default:</P>
+<PRE>
+ olcLogLevel stats
+</PRE>
+<P>Basic stats logging is configured by default.</P>
+<H4><A NAME="olcReferral &lt;URI&gt;">5.2.1.3. olcReferral &lt;URI&gt;</A></H4>
+<P>This directive specifies the referral to pass back when slapd cannot find a local database to handle a request.</P>
+<P>Example:</P>
+<PRE>
+ olcReferral: ldap://root.openldap.org
+</PRE>
+<P>This will refer non-local queries to the global root LDAP server at the OpenLDAP Project. Smart LDAP clients can re-ask their query at that server, but note that most of these clients are only going to know how to handle simple LDAP URLs that contain a host part and optionally a distinguished name part.</P>
+<H4><A NAME="Sample Entry">5.2.1.4. Sample Entry</A></H4>
+<PRE>
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcIdleTimeout: 30
+olcLogLevel: Stats
+olcReferral: ldap://root.openldap.org
+</PRE>
+<H3><A NAME="cn=module">5.2.2. cn=module</A></H3>
+<P>If support for dynamically loaded modules was enabled when configuring slapd, <TT>cn=module</TT> entries may be used to specify sets of modules to load. Module entries must have the <TT>olcModuleList</TT> objectClass.</P>
+<H4><A NAME="olcModuleLoad: &lt;filename&gt;">5.2.2.1. olcModuleLoad: &lt;filename&gt;</A></H4>
+<P>Specify the name of a dynamically loadable module to load. The filename may be an absolute path name or a simple filename. Non-absolute names are searched for in the directories specified by the <TT>olcModulePath</TT> directive.</P>
+<H4><A NAME="olcModulePath: &lt;pathspec&gt;">5.2.2.2. olcModulePath: &lt;pathspec&gt;</A></H4>
+<P>Specify a list of directories to search for loadable modules. Typically the path is colon-separated but this depends on the operating system.</P>
+<H4><A NAME="Sample Entries">5.2.2.3. Sample Entries</A></H4>
+<PRE>
+dn: cn=module{0},cn=config
+objectClass: olcModuleList
+cn: module{0}
+olcModuleLoad: /usr/local/lib/smbk5pwd.la
+
+dn: cn=module{1},cn=config
+objectClass: olcModuleList
+cn: module{1}
+olcModulePath: /usr/local/lib:/usr/local/lib/slapd
+olcModuleLoad: accesslog.la
+olcModuleLoad: pcache.la
+</PRE>
+<H3><A NAME="cn=schema">5.2.3. cn=schema</A></H3>
+<P>The cn=schema entry holds all of the schema definitions that are hard-coded in slapd. As such, the values in this entry are generated by slapd so no schema values need to be provided in the config file. The entry must still be defined though, to serve as a base for the user-defined schema to add in underneath. Schema entries must have the <TT>olcSchemaConfig</TT> objectClass.</P>
+<H4><A NAME="olcAttributeTypes: &lt;{{REF:RFC4512}} Attribute Type Description&gt;"> </A>5.2.3.1. olcAttributeTypes: &lt;<A HREF="https://www.rfc-editor.org/rfc/rfc4512.txt">RFC4512</A> Attribute Type Description&gt;</H4>
+<P>This directive defines an attribute type. Please see the <A HREF="#Schema Specification">Schema Specification</A> chapter for information regarding how to use this directive.</P>
+<H4><A NAME="olcObjectClasses: &lt;{{REF:RFC4512}} Object Class Description&gt;"> </A>5.2.3.2. olcObjectClasses: &lt;<A HREF="https://www.rfc-editor.org/rfc/rfc4512.txt">RFC4512</A> Object Class Description&gt;</H4>
+<P>This directive defines an object class. Please see the <A HREF="#Schema Specification">Schema Specification</A> chapter for information regarding how to use this directive.</P>
+<H4><A NAME="Sample Entries">5.2.3.3. Sample Entries</A></H4>
+<PRE>
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+dn: cn=test,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: test
+olcAttributeTypes: ( 1.1.1
+ NAME 'testAttr'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+olcAttributeTypes: ( 1.1.2 NAME 'testTwo' EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 )
+olcObjectClasses: ( 1.1.3 NAME 'testObject'
+ MAY ( testAttr $ testTwo ) AUXILIARY )
+</PRE>
+<H3><A NAME="Backend-specific Directives">5.2.4. Backend-specific Directives</A></H3>
+<P>Backend directives apply to all database instances of the same type and, depending on the directive, may be overridden by database directives. Backend entries must have the <TT>olcBackendConfig</TT> objectClass.</P>
+<H4><A NAME="olcBackend: &lt;type&gt;">5.2.4.1. olcBackend: &lt;type&gt;</A></H4>
+<P>This directive names a backend-specific configuration entry. <TT>&lt;type&gt;</TT> should be one of the supported backend types listed in Table 5.2.</P>
+<TABLE CLASS="columns" BORDER ALIGN='Center'>
+<CAPTION ALIGN=top>Table 5.2: Database Backends</CAPTION>
+<TR CLASS="heading">
+<TD>
+<STRONG>Types</STRONG>
+</TD>
+<TD>
+<STRONG>Description</STRONG>
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>asyncmet</TT>
+</TD>
+<TD>
+a Asynchronous Metadirectory backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>config</TT>
+</TD>
+<TD>
+Slapd configuration backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>dnssrv</TT>
+</TD>
+<TD>
+DNS SRV backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>ldap</TT>
+</TD>
+<TD>
+Lightweight Directory Access Protocol (Proxy) backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>ldif</TT>
+</TD>
+<TD>
+Lightweight Data Interchange Format backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>mdb</TT>
+</TD>
+<TD>
+Memory-Mapped DB backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>meta</TT>
+</TD>
+<TD>
+Metadirectory backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>monitor</TT>
+</TD>
+<TD>
+Monitor backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>ndb</TT>
+</TD>
+<TD>
+MySQL NDB backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>null</TT>
+</TD>
+<TD>
+Null backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>passwd</TT>
+</TD>
+<TD>
+Provides read-only access to <EM>passwd</EM>(5)
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>perl</TT>
+</TD>
+<TD>
+Perl Programmable backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>relay</TT>
+</TD>
+<TD>
+Relay backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>sock</TT>
+</TD>
+<TD>
+Socket backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>sql</TT>
+</TD>
+<TD>
+SQL Programmable backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>wt</TT>
+</TD>
+<TD>
+WiredTiger backend
+</TD>
+</TR>
+</TABLE>
+
+<P>Example:</P>
+<PRE>
+ olcBackend: mdb
+</PRE>
+<P>This marks the beginning of a new <TERM>MDB</TERM> backend definition. At present, only back-mdb implements any options of this type, so this setting is not needed for any other backends.</P>
+<H4><A NAME="Sample Entry">5.2.4.2. Sample Entry</A></H4>
+<PRE>
+ dn: olcBackend=mdb,cn=config
+ objectClass: olcBackendConfig
+ olcBackend: mdb
+ olcBkMdbIdlExp: 16
+</PRE>
+<H3><A NAME="Database-specific Directives">5.2.5. Database-specific Directives</A></H3>
+<P>Directives in this section are supported by every type of database. Database entries must have the <TT>olcDatabaseConfig</TT> objectClass.</P>
+<H4><A NAME="olcDatabase: [{&lt;index&gt;}]&lt;type&gt;">5.2.5.1. olcDatabase: [{&lt;index&gt;}]&lt;type&gt;</A></H4>
+<P>This directive names a specific database instance. The numeric {&lt;index&gt;} may be provided to distinguish multiple databases of the same type. Usually the index can be omitted, and slapd will generate it automatically. <TT>&lt;type&gt;</TT> should be one of the supported backend types listed in Table 5.2 or the <TT>frontend</TT> type.</P>
+<P>The <TT>frontend</TT> is a special database that is used to hold database-level options that should be applied to all the other databases. Subsequent database definitions may also override some frontend settings.</P>
+<P>The <TT>config</TT> database is also special; both the <TT>config</TT> and the <TT>frontend</TT> databases are always created implicitly even if they are not explicitly configured, and they are created before any other databases.</P>
+<P>Example:</P>
+<PRE>
+ olcDatabase: mdb
+</PRE>
+<P>This marks the beginning of a new <TERM>MDB</TERM> database instance.</P>
+<H4><A NAME="olcAccess: to &lt;what&gt; [ by &lt;who&gt; [&lt;accesslevel&gt;] [&lt;control&gt;] ]+">5.2.5.2. olcAccess: to &lt;what&gt; [ by &lt;who&gt; [&lt;accesslevel&gt;] [&lt;control&gt;] ]+</A></H4>
+<P>This directive grants access (specified by &lt;accesslevel&gt;) to a set of entries and/or attributes (specified by &lt;what&gt;) by one or more requestors (specified by &lt;who&gt;). See the <A HREF="#Access Control">Access Control</A> section of this guide for basic usage.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>If no <TT>olcAccess</TT> directives are specified, the default access control policy, <TT>to * by * read</TT>, allows all users (both authenticated and anonymous) read access.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>Access controls defined in the frontend are appended to all other databases' controls.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H4><A NAME="olcReadonly { TRUE | FALSE }">5.2.5.3. olcReadonly { TRUE | FALSE }</A></H4>
+<P>This directive puts the database into &quot;read-only&quot; mode. Any attempts to modify the database will return an &quot;unwilling to perform&quot; error. If set on a consumer, modifications sent by syncrepl will still occur.</P>
+<P>Default:</P>
+<PRE>
+ olcReadonly: FALSE
+</PRE>
+<H4><A NAME="olcRootDN: &lt;DN&gt;">5.2.5.4. olcRootDN: &lt;DN&gt;</A></H4>
+<P>This directive specifies the DN that is not subject to access control or administrative limit restrictions for operations on this database. The DN need not refer to an entry in this database or even in the directory. The DN may refer to a SASL identity.</P>
+<P>Entry-based Example:</P>
+<PRE>
+ olcRootDN: cn=Manager,dc=example,dc=com
+</PRE>
+<P>SASL-based Example:</P>
+<PRE>
+ olcRootDN: uid=root,cn=example.com,cn=digest-md5,cn=auth
+</PRE>
+<P>See the <A HREF="#SASL Authentication">SASL Authentication</A> section for information on SASL authentication identities.</P>
+<H4><A NAME="olcRootPW: &lt;password&gt;">5.2.5.5. olcRootPW: &lt;password&gt;</A></H4>
+<P>This directive can be used to specify a password for the DN for the rootdn (when the rootdn is set to a DN within the database).</P>
+<P>Example:</P>
+<PRE>
+ olcRootPW: secret
+</PRE>
+<P>It is also permissible to provide a hash of the password in <A HREF="https://www.rfc-editor.org/rfc/rfc2307.txt">RFC2307</A> form. <EM>slappasswd</EM>(8) may be used to generate the password hash.</P>
+<P>Example:</P>
+<PRE>
+ olcRootPW: {SSHA}ZKKuqbEKJfKSXhUbHG3fG8MDn9j1v4QN
+</PRE>
+<P>The hash was generated using the command <TT>slappasswd -s secret</TT>.</P>
+<H4><A NAME="olcSizeLimit: &lt;integer&gt;">5.2.5.6. olcSizeLimit: &lt;integer&gt;</A></H4>
+<P>This directive specifies the maximum number of entries to return from a search operation.</P>
+<P>Default:</P>
+<PRE>
+ olcSizeLimit: 500
+</PRE>
+<P>See the <A HREF="#Limits">Limits</A> section of this guide and slapd-config(5) for more details.</P>
+<H4><A NAME="olcSuffix: &lt;dn suffix&gt;">5.2.5.7. olcSuffix: &lt;dn suffix&gt;</A></H4>
+<P>This directive specifies the DN suffix of queries that will be passed to this backend database. Multiple suffix lines can be given, and usually at least one is required for each database definition. (Some backend types, such as <TT>frontend</TT> and <TT>monitor</TT> use a hard-coded suffix which may not be overridden in the configuration.)</P>
+<P>Example:</P>
+<PRE>
+ olcSuffix: dc=example,dc=com
+</PRE>
+<P>Queries with a DN ending in &quot;dc=example,dc=com&quot; will be passed to this backend.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>When the backend to pass a query to is selected, slapd looks at the suffix value(s) in each database definition in the order in which they were configured. Thus, if one database suffix is a prefix of another, it must appear after it in the configuration.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H4><A NAME="olcSyncrepl">5.2.5.8. olcSyncrepl</A></H4>
+<PRE>
+ olcSyncrepl: rid=&lt;replica ID&gt;
+ provider=ldap[s]://&lt;hostname&gt;[:port]
+ [type=refreshOnly|refreshAndPersist]
+ [interval=dd:hh:mm:ss]
+ [retry=[&lt;retry interval&gt; &lt;# of retries&gt;]+]
+ searchbase=&lt;base DN&gt;
+ [filter=&lt;filter str&gt;]
+ [scope=sub|one|base]
+ [attrs=&lt;attr list&gt;]
+ [exattrs=&lt;attr list&gt;]
+ [attrsonly]
+ [sizelimit=&lt;limit&gt;]
+ [timelimit=&lt;limit&gt;]
+ [schemachecking=on|off]
+ [bindmethod=simple|sasl]
+ [binddn=&lt;DN&gt;]
+ [saslmech=&lt;mech&gt;]
+ [authcid=&lt;identity&gt;]
+ [authzid=&lt;identity&gt;]
+ [credentials=&lt;passwd&gt;]
+ [realm=&lt;realm&gt;]
+ [secprops=&lt;properties&gt;]
+ [starttls=yes|critical]
+ [tls_cert=&lt;file&gt;]
+ [tls_key=&lt;file&gt;]
+ [tls_cacert=&lt;file&gt;]
+ [tls_cacertdir=&lt;path&gt;]
+ [tls_reqcert=never|allow|try|demand]
+ [tls_cipher_suite=&lt;ciphers&gt;]
+ [tls_crlcheck=none|peer|all]
+ [logbase=&lt;base DN&gt;]
+ [logfilter=&lt;filter str&gt;]
+ [syncdata=default|accesslog|changelog]
+</PRE>
+<P>This directive specifies the current database as a consumer of the provider content by establishing the current <EM>slapd</EM>(8) as a replication consumer site running a syncrepl replication engine. The provider database is located at the provider site specified by the <TT>provider</TT> parameter. The consumer database is kept up-to-date with the provider content using the LDAP Content Synchronization protocol. See <A HREF="https://www.rfc-editor.org/rfc/rfc4533.txt">RFC4533</A> for more information on the protocol.</P>
+<P>The <TT>rid</TT> parameter is used for identification of the current <TT>syncrepl</TT> directive within the replication consumer server, where <TT>&lt;replica ID&gt;</TT> uniquely identifies the syncrepl specification described by the current <TT>syncrepl</TT> directive. <TT>&lt;replica ID&gt;</TT> is non-negative and is no more than three decimal digits in length.</P>
+<P>The <TT>provider</TT> parameter specifies the replication provider site containing the provider content as an LDAP URI. The <TT>provider</TT> parameter specifies a scheme, a host and optionally a port where the provider slapd instance can be found. Either a domain name or IP address may be used for &lt;hostname&gt;. Examples are <TT>ldap://provider.example.com:389</TT> or <TT>ldaps://192.168.1.1:636</TT>. If &lt;port&gt; is not given, the standard LDAP port number (389 or 636) is used. Note that the syncrepl uses a consumer-initiated protocol, and hence its specification is located on the consumer.</P>
+<P>The content of the syncrepl consumer is defined using a search specification as its result set. The consumer slapd will send search requests to the provider slapd according to the search specification. The search specification includes <TT>searchbase</TT>, <TT>scope</TT>, <TT>filter</TT>, <TT>attrs</TT>, <TT>exattrs</TT>, <TT>attrsonly</TT>, <TT>sizelimit</TT>, and <TT>timelimit</TT> parameters as in the normal search specification. The <TT>searchbase</TT> parameter has no default value and must always be specified. The <TT>scope</TT> defaults to <TT>sub</TT>, the <TT>filter</TT> defaults to <TT>(objectclass=*)</TT>, <TT>attrs</TT> defaults to <TT>&quot;*,+&quot;</TT> to replicate all user and operational attributes, and <TT>attrsonly</TT> is unset by default. Both <TT>sizelimit</TT> and <TT>timelimit</TT> default to &quot;unlimited&quot;, and only positive integers or &quot;unlimited&quot; may be specified. The <TT>exattrs</TT> option may also be used to specify attributes that should be omitted from incoming entries.</P>
+<P>The <TERM>LDAP Content Synchronization</TERM> protocol has two operation types: <TT>refreshOnly</TT> and <TT>refreshAndPersist</TT>. The operation type is specified by the <TT>type</TT> parameter. In the <TT>refreshOnly</TT> operation, the next synchronization search operation is periodically rescheduled at an interval time after each synchronization operation finishes. The interval is specified by the <TT>interval</TT> parameter. It is set to one day by default. In the <TT>refreshAndPersist</TT> operation, a synchronization search remains persistent in the provider <EM>slapd</EM> instance. Further updates to the provider will generate <TT>searchResultEntry</TT> to the consumer slapd as the search responses to the persistent synchronization search.</P>
+<P>If an error occurs during replication, the consumer will attempt to reconnect according to the retry parameter which is a list of the &lt;retry interval&gt; and &lt;# of retries&gt; pairs. For example, retry=&quot;60 10 300 3&quot; lets the consumer retry every 60 seconds for the first 10 times and then retry every 300 seconds for the next three times before stop retrying. + in &lt;# of retries&gt; means indefinite number of retries until success.</P>
+<P>The schema checking can be enforced at the LDAP Sync consumer site by turning on the <TT>schemachecking</TT> parameter. If it is turned on, every replicated entry will be checked for its schema as the entry is stored on the consumer. Every entry in the consumer should contain those attributes required by the schema definition. If it is turned off, entries will be stored without checking schema conformance. The default is off.</P>
+<P>The <TT>binddn</TT> parameter gives the DN to bind as for the syncrepl searches to the provider slapd. It should be a DN which has read access to the replication content in the provider database.</P>
+<P>The <TT>bindmethod</TT> is <TT>simple</TT> or <TT>sasl</TT>, depending on whether simple password-based authentication or <TERM>SASL</TERM> authentication is to be used when connecting to the provider <EM>slapd</EM> instance.</P>
+<P>Simple authentication should not be used unless adequate data integrity and confidentiality protections are in place (e.g. TLS or IPsec). Simple authentication requires specification of <TT>binddn</TT> and <TT>credentials</TT> parameters.</P>
+<P>SASL authentication is generally recommended. SASL authentication requires specification of a mechanism using the <TT>saslmech</TT> parameter. Depending on the mechanism, an authentication identity and/or credentials can be specified using <TT>authcid</TT> and <TT>credentials</TT>, respectively. The <TT>authzid</TT> parameter may be used to specify an authorization identity.</P>
+<P>The <TT>realm</TT> parameter specifies a realm which a certain mechanisms authenticate the identity within. The <TT>secprops</TT> parameter specifies Cyrus SASL security properties.</P>
+<P>The <TT>starttls</TT> parameter specifies use of the StartTLS extended operation to establish a TLS session before authenticating to the provider. If the <TT>critical</TT> argument is supplied, the session will be aborted if the StartTLS request fails. Otherwise the syncrepl session continues without TLS. The tls_reqcert setting defaults to <TT>&quot;demand&quot;</TT> and the other TLS settings default to the same as the main slapd TLS settings.</P>
+<P>Rather than replicating whole entries, the consumer can query logs of data modifications. This mode of operation is referred to as <EM>delta syncrepl</EM>. In addition to the above parameters, the <TT>logbase</TT> and <TT>logfilter</TT> parameters must be set appropriately for the log that will be used. The <TT>syncdata</TT> parameter must be set to either <TT>&quot;accesslog&quot;</TT> if the log conforms to the <EM>slapo-accesslog</EM>(5) log format, or <TT>&quot;changelog&quot;</TT> if the log conforms to the obsolete <EM>changelog</EM> format. If the <TT>syncdata</TT> parameter is omitted or set to <TT>&quot;default&quot;</TT> then the log parameters are ignored.</P>
+<P>The <EM>syncrepl</EM> replication mechanism is supported by the <EM>mdb</EM> backend.</P>
+<P>See the <A HREF="#LDAP Sync Replication">LDAP Sync Replication</A> chapter of this guide for more information on how to use this directive.</P>
+<H4><A NAME="olcTimeLimit: &lt;integer&gt;">5.2.5.9. olcTimeLimit: &lt;integer&gt;</A></H4>
+<P>This directive specifies the maximum number of seconds (in real time) slapd will spend answering a search request. If a request is not finished in this time, a result indicating an exceeded timelimit will be returned.</P>
+<P>Default:</P>
+<PRE>
+ olcTimeLimit: 3600
+</PRE>
+<P>See the <A HREF="#Limits">Limits</A> section of this guide and slapd-config(5) for more details.</P>
+<H4><A NAME="olcUpdateref: &lt;URL&gt;">5.2.5.10. olcUpdateref: &lt;URL&gt;</A></H4>
+<P>This directive is only applicable in a <EM>replica</EM> (or <EM>shadow</EM>) <EM>slapd</EM>(8) instance. It specifies the URL to return to clients which submit update requests upon the replica. If specified multiple times, each <TERM>URL</TERM> is provided.</P>
+<P>Example:</P>
+<PRE>
+ olcUpdateref: ldap://provider.example.net
+</PRE>
+<H4><A NAME="Sample Entries">5.2.5.11. Sample Entries</A></H4>
+<PRE>
+dn: olcDatabase=frontend,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcFrontendConfig
+olcDatabase: frontend
+olcReadOnly: FALSE
+
+dn: olcDatabase=config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: config
+olcRootDN: cn=Manager,dc=example,dc=com
+</PRE>
+<H3><A NAME="MDB Backend Directives">5.2.6. MDB Backend Directives</A></H3>
+<P>Directives in this category only apply to the <TERM>MDB</TERM> database backend. They will apply to all &quot;database mdb&quot; instances in the configuration. For a complete reference of MDB backend configuration directives, see <EM>slapd-mdb</EM>(5).</P>
+<H4><A NAME="olcBkMdbIdlExp &lt;exponent&gt;">5.2.6.1. olcBkMdbIdlExp &lt;exponent&gt;</A></H4>
+<P>Specify a power of 2 for the maximum size of an index slot. The default is 16, yielding a maximum slot size of 2^16 or 65536. The specified value must be in the range of 16-30.</P>
+<P>This setting helps with the case where certain search filters are slow to return results due to an index slot having collapsed to a range value. This occurs when the number of candidate entries that match the filter for the index slot exceed the configured slot size.</P>
+<P>If this setting is decreased on a server with existing <TERM>MDB</TERM> databases, each db will immediately need its indices to be rebuilt while slapd is offline with the &quot;slapindex -q -t&quot; command.</P>
+<P>If this setting is increased on a server with existing <TERM>MDB</TERM> databases, each db will need its indices rebuilt to take advantage of the change for indices that have already been converted to ranges.</P>
+<H3><A NAME="MDB Database Directives">5.2.7. MDB Database Directives</A></H3>
+<P>Directives in this category apply to the <TERM>MDB</TERM> database backend. They are used in an olcDatabase entry in addition to the generic database directives defined above. For a complete reference of MDB configuration directives, see <EM>slapd-mdb</EM>(5). In addition to the <TT>olcDatabaseConfig</TT> objectClass, MDB database entries must have the <TT>olcMdbConfig</TT> objectClass.</P>
+<H4><A NAME="olcDbDirectory: &lt;directory&gt;">5.2.7.1. olcDbDirectory: &lt;directory&gt;</A></H4>
+<P>This directive specifies the directory where the MDB files containing the database and associated indices live.</P>
+<P>Default:</P>
+<PRE>
+ olcDbDirectory: /usr/local/var/openldap-data
+</PRE>
+<H4><A NAME="olcDbCheckpoint: &lt;kbyte&gt; &lt;min&gt;">5.2.7.2. olcDbCheckpoint: &lt;kbyte&gt; &lt;min&gt;</A></H4>
+<P>This directive specifies the frequency for flushing the database disk buffers. This directive is only needed if the <EM>olcDbNoSync</EM> option is <TT>TRUE</TT>. The checkpoint will occur if either &lt;kbyte&gt; data has been written or &lt;min&gt; minutes have passed since the last checkpoint. Both arguments default to zero, in which case they are ignored. When the &lt;min&gt; argument is non-zero, an internal task will run every &lt;min&gt; minutes to perform the checkpoint. Note: currently the _kbyte_ setting is unimplemented.</P>
+<P>Example:</P>
+<PRE>
+ olcDbCheckpoint: 1024 10
+</PRE>
+<H4><A NAME="olcDbEnvFlags: {nosync,nometasync,writemap,mapasync,nordahead}">5.2.7.3. olcDbEnvFlags: {nosync,nometasync,writemap,mapasync,nordahead}</A></H4>
+<P>This option specifies flags for finer-grained control of the LMDB library's operation.</P>
+<UL>
+<LI><TT>nosync</TT>: This is exactly the same as the dbnosync directive.
+<LI><TT>nometasync</TT>: Flush the data on a commit, but skip the sync of the meta page. This mode is slightly faster than doing a full sync, but can potentially lose the last committed transaction if the operating system crashes. If both nometasync and nosync are set, the nosync flag takes precedence.
+<LI><TT>writemap</TT>: Use a writable memory map instead of just read-only. This speeds up write operations but makes the database vulnerable to corruption in case any bugs in slapd cause stray writes into the mmap region.
+<LI><TT>mapasync</TT>: When using a writable memory map and performing flushes on each commit, use an asynchronous flush instead of a synchronous flush (the default). This option has no effect if writemap has not been set. It also has no effect if nosync is set.
+<LI><TT>nordahead</TT>: Turn off file readahead. Usually the OS performs readahead on every read request. This usually boosts read performance but can be harmful to random access read performance if the system's memory is full and the DB is larger than RAM. This option is not implemented on Windows.</UL>
+<H4><A NAME="olcDbIndex: {&lt;attrlist&gt; | default} [pres,eq,approx,sub,none]">5.2.7.4. olcDbIndex: {&lt;attrlist&gt; | default} [pres,eq,approx,sub,none]</A></H4>
+<P>This directive specifies the indices to maintain for the given attribute. If only an <TT>&lt;attrlist&gt;</TT> is given, the default indices are maintained. The index keywords correspond to the common types of matches that may be used in an LDAP search filter.</P>
+<P>Example:</P>
+<PRE>
+ olcDbIndex: default pres,eq
+ olcDbIndex: uid
+ olcDbIndex: cn,sn pres,eq,sub
+ olcDbIndex: objectClass eq
+</PRE>
+<P>The first line sets the default set of indices to maintain to present and equality. The second line causes the default (pres,eq) set of indices to be maintained for the <TT>uid</TT> attribute type. The third line causes present, equality, and substring indices to be maintained for <TT>cn</TT> and <TT>sn</TT> attribute types. The fourth line causes an equality index for the <TT>objectClass</TT> attribute type.</P>
+<P>There is no index keyword for inequality matches. Generally these matches do not use an index. However, some attributes do support indexing for inequality matches, based on the equality index.</P>
+<P>A substring index can be more explicitly specified as <TT>subinitial</TT>, <TT>subany</TT>, or <TT>subfinal</TT>, corresponding to the three possible components of a substring match filter. A subinitial index only indexes substrings that appear at the beginning of an attribute value. A subfinal index only indexes substrings that appear at the end of an attribute value, while subany indexes substrings that occur anywhere in a value.</P>
+<P>Note that by default, setting an index for an attribute also affects every subtype of that attribute. E.g., setting an equality index on the <TT>name</TT> attribute causes <TT>cn</TT>, <TT>sn</TT>, and every other attribute that inherits from <TT>name</TT> to be indexed.</P>
+<P>By default, no indices are maintained. It is generally advised that minimally an equality index upon objectClass be maintained.</P>
+<PRE>
+ olcDbIndex: objectClass eq
+</PRE>
+<P>Additional indices should be configured corresponding to the most common searches that are used on the database. Presence indexing should not be configured for an attribute unless the attribute occurs very rarely in the database, and presence searches on the attribute occur very frequently during normal use of the directory. Most applications don't use presence searches, so usually presence indexing is not very useful.</P>
+<P>If this setting is changed while slapd is running, an internal task will be run to generate the changed index data. All server operations can continue as normal while the indexer does its work. If slapd is stopped before the index task completes, indexing will have to be manually completed using the slapindex tool.</P>
+<H4><A NAME="olcDbMaxEntrySize: &lt;bytes&gt;">5.2.7.5. olcDbMaxEntrySize: &lt;bytes&gt;</A></H4>
+<P>Specify the maximum size of an entry in bytes. Attempts to store an entry larger than this size will be rejected with the error LDAP_ADMINLIMIT_EXCEEDED. The default is 0, which is unlimited.</P>
+<H4><A NAME="olcDbMaxReaders: &lt;integer&gt;">5.2.7.6. olcDbMaxReaders: &lt;integer&gt;</A></H4>
+<P>This directive specifies the maximum number of threads that may have concurrent read access to the database. Tools such as slapcat count as a single thread, in addition to threads in any active slapd processes. The default is 126.</P>
+<H4><A NAME="olcDbMaxSize: &lt;bytes&gt;">5.2.7.7. olcDbMaxSize: &lt;bytes&gt;</A></H4>
+<P>This directive specifies the maximum size of the database in bytes. A memory map of this size is allocated at startup time and the database will not be allowed to grow beyond this size. The default is 10485760 bytes (10MB). This setting may be changed upward if the configured limit needs to be increased.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>It is important to set this to as large a value as possible, (relative to anticipated growth of the actual data over time) since growing the size later may not be practical when the system is under heavy load.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H4><A NAME="olcDbMode: { &lt;octal&gt; | &lt;symbolic&gt; }">5.2.7.8. olcDbMode: { &lt;octal&gt; | &lt;symbolic&gt; }</A></H4>
+<P>This directive specifies the file protection mode that newly created database index files should have. This can be in the form <TT>0600</TT> or <TT>-rw-------</TT></P>
+<P>Default:</P>
+<PRE>
+ olcDbMode: 0600
+</PRE>
+<H4><A NAME="olcDbMultival: { &lt;attrlist&gt; | default } &lt;integer&gt; hi,&lt;integer&gt; lo">5.2.7.9. olcDbMultival: { &lt;attrlist&gt; | default } &lt;integer&gt; hi,&lt;integer&gt; lo</A></H4>
+<P>Specify the number of values for which a multivalued attribute is stored in a separate table. Normally entries are stored as a single blob inside the database. When an entry gets very large or contains attributes with a very large number of values, modifications on that entry may get very slow. Splitting the large attributes out to a separate table can improve the performance of modification operations. The threshold is specified as a pair of integers. If the number of values exceeds the hi threshold the values will be split out. If a modification deletes enough values to bring an attribute below the lo threshold the values will be removed from the separate table and merged back into the main entry blob. The threshold can be set for a specific list of attributes, or the default can be configured for all other attributes. The default value for both hi and lo thresholds is UINT_MAX, which keeps all attributes in the main blob.</P>
+<P>In addition to increasing write performance of operations the use of multival can also decrease fragmentation of the primary <TERM>MDB</TERM> database.</P>
+<H4><A NAME="olcDbRtxnsize: &lt;entries&gt;">5.2.7.10. olcDbRtxnsize: &lt;entries&gt;</A></H4>
+<P>This directive specifies the maximum number of entries to process in a single read transaction when executing a large search. Long-lived read transactions prevent old database pages from being reused in write transactions, and so can cause significant growth of the database file when there is heavy write traffic. This setting causes the read transaction in large searches to be released and reacquired after the given number of entries has been read, to give writers the opportunity to reclaim old database pages. The default is 10000.</P>
+<H4><A NAME="olcDbSearchStack: &lt;integer&gt;">5.2.7.11. olcDbSearchStack: &lt;integer&gt;</A></H4>
+<P>Specify the depth of the stack used for search filter evaluation. Search filters are evaluated on a stack to accommodate nested <TT>AND</TT> / <TT>OR</TT> clauses. An individual stack is allocated for each server thread. The depth of the stack determines how complex a filter can be evaluated without requiring any additional memory allocation. Filters that are nested deeper than the search stack depth will cause a separate stack to be allocated for that particular search operation. These separate allocations can have a major negative impact on server performance, but specifying too much stack will also consume a great deal of memory. Each search uses 512K bytes per level on a 32-bit machine, or 1024K bytes per level on a 64-bit machine. The default stack depth is 16, thus 8MB or 16MB per thread is used on 32 and 64 bit machines, respectively. Also the 512KB size of a single stack slot is set by a compile-time constant which may be changed if needed; the code must be recompiled for the change to take effect.</P>
+<P>Default:</P>
+<PRE>
+ olcDbSearchStack: 16
+</PRE>
+<H4><A NAME="olcDbNosync: { TRUE | FALSE }">5.2.7.12. olcDbNosync: { TRUE | FALSE }</A></H4>
+<P>This directive causes on-disk database contents to not be immediately synchronized with in memory changes upon change. Setting this option to <TT>TRUE</TT> may improve performance at the expense of data integrity.</P>
+<H4><A NAME="Sample Entry">5.2.7.13. Sample Entry</A></H4>
+<PRE>
+dn: olcDatabase=mdb,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcMdbConfig
+olcDatabase: mdb
+olcSuffix: dc=example,dc=com
+olcDbDirectory: /usr/local/var/openldap-data
+olcDbIndex: objectClass eq
+</PRE>
+<H2><A NAME="Configuration Example">5.3. Configuration Example</A></H2>
+<P>The following is an example configuration, interspersed with explanatory text. It defines two databases to handle different parts of the <TERM>X.500</TERM> tree; both are <TERM>MDB</TERM> database instances. The line numbers shown are provided for reference only and are not included in the actual file. First, the global configuration section:</P>
+<PRE>
+ 1. # example config file - global configuration entry
+ 2. dn: cn=config
+ 3. objectClass: olcGlobal
+ 4. cn: config
+ 5. olcReferral: ldap://root.openldap.org
+ 6.
+</PRE>
+<P>Line 1 is a comment. Lines 2-4 identify this as the global configuration entry. The <TT>olcReferral:</TT> directive on line 5 means that queries not local to one of the databases defined below will be referred to the LDAP server running on the standard port (389) at the host <TT>root.openldap.org</TT>. Line 6 is a blank line, indicating the end of this entry.</P>
+<PRE>
+ 7. # internal schema
+ 8. dn: cn=schema,cn=config
+ 9. objectClass: olcSchemaConfig
+ 10. cn: schema
+ 11.
+</PRE>
+<P>Line 7 is a comment. Lines 8-10 identify this as the root of the schema subtree. The actual schema definitions in this entry are hardcoded into slapd so no additional attributes are specified here. Line 11 is a blank line, indicating the end of this entry.</P>
+<PRE>
+ 12. # include the core schema
+ 13. include: file:///usr/local/etc/openldap/schema/core.ldif
+ 14.
+</PRE>
+<P>Line 12 is a comment. Line 13 is an LDIF include directive which accesses the <EM>core</EM> schema definitions in LDIF format. Line 14 is a blank line.</P>
+<P>Next comes the database definitions. The first database is the special <TT>frontend</TT> database whose settings are applied globally to all the other databases.</P>
+<PRE>
+ 15. # global database parameters
+ 16. dn: olcDatabase=frontend,cn=config
+ 17. objectClass: olcDatabaseConfig
+ 18. olcDatabase: frontend
+ 19. olcAccess: to * by * read
+ 20.
+</PRE>
+<P>Line 15 is a comment. Lines 16-18 identify this entry as the global database entry. Line 19 is a global access control. It applies to all entries (after any applicable database-specific access controls). Line 20 is a blank line.</P>
+<P>The next entry defines the config backend.</P>
+<PRE>
+ 21. # set a rootpw for the config database so we can bind.
+ 22. # deny access to everyone else.
+ 23. dn: olcDatabase=config,cn=config
+ 24. objectClass: olcDatabaseConfig
+ 25. olcDatabase: config
+ 26. olcRootPW: {SSHA}XKYnrjvGT3wZFQrDD5040US592LxsdLy
+ 27. olcAccess: to * by * none
+ 28.
+</PRE>
+<P>Lines 21-22 are comments. Lines 23-25 identify this entry as the config database entry. Line 26 defines the <EM>super-user</EM> password for this database. (The DN defaults to <EM>&quot;cn=config&quot;</EM>.) Line 27 denies all access to this database, so only the super-user will be able to access it. (This is already the default access on the config database. It is just listed here for illustration, and to reiterate that unless a means to authenticate as the super-user is explicitly configured, the config database will be inaccessible.)</P>
+<P>Line 28 is a blank line.</P>
+<P>The next entry defines an MDB backend that will handle queries for things in the &quot;dc=example,dc=com&quot; portion of the tree. Indices are to be maintained for several attributes, and the <TT>userPassword</TT> attribute is to be protected from unauthorized access.</P>
+<PRE>
+ 29. # MDB definition for example.com
+ 30. dn: olcDatabase=mdb,cn=config
+ 31. objectClass: olcDatabaseConfig
+ 32. objectClass: olcMdbConfig
+ 33. olcDatabase: mdb
+ 34. olcSuffix: dc=example,dc=com
+ 35. olcDbDirectory: /usr/local/var/openldap-data
+ 36. olcRootDN: cn=Manager,dc=example,dc=com
+ 37. olcRootPW: secret
+ 38. olcDbIndex: uid pres,eq
+ 39. olcDbIndex: cn,sn pres,eq,approx,sub
+ 40. olcDbIndex: objectClass eq
+ 41. olcAccess: to attrs=userPassword
+ 42. by self write
+ 43. by anonymous auth
+ 44. by dn.base=&quot;cn=Admin,dc=example,dc=com&quot; write
+ 45. by * none
+ 46. olcAccess: to *
+ 47. by self write
+ 48. by dn.base=&quot;cn=Admin,dc=example,dc=com&quot; write
+ 49. by * read
+ 50.
+</PRE>
+<P>Line 29 is a comment. Lines 30-33 identify this entry as a MDB database configuration entry. Line 34 specifies the DN suffix for queries to pass to this database. Line 35 specifies the directory in which the database files will live.</P>
+<P>Lines 36 and 37 identify the database <EM>super-user</EM> entry and associated password. This entry is not subject to access control or size or time limit restrictions.</P>
+<P>Lines 38 through 40 indicate the indices to maintain for various attributes.</P>
+<P>Lines 41 through 49 specify access control for entries in this database. For all applicable entries, the <TT>userPassword</TT> attribute is writable by the entry itself and by the &quot;admin&quot; entry. It may be used for authentication/authorization purposes, but is otherwise not readable. All other attributes are writable by the entry and the &quot;admin&quot; entry, but may be read by all users (authenticated or not).</P>
+<P>Line 50 is a blank line, indicating the end of this entry.</P>
+<P>The next entry defines another MDB database. This one handles queries involving the <TT>dc=example,dc=net</TT> subtree but is managed by the same entity as the first database. Note that without line 60, the read access would be allowed due to the global access rule at line 19.</P>
+<PRE>
+ 51. # MDB definition for example.net
+ 52. dn: olcDatabase=mdb,cn=config
+ 53. objectClass: olcDatabaseConfig
+ 54. objectClass: olcMdbConfig
+ 55. olcDatabase: mdb
+ 56. olcSuffix: dc=example,dc=net
+ 57. olcDbDirectory: /usr/local/var/openldap-data-net
+ 58. olcRootDN: cn=Manager,dc=example,dc=com
+ 59. olcDbIndex: objectClass eq
+ 60. olcAccess: to * by users read
+</PRE>
+<H2><A NAME="Converting old style {{slapd.conf}}(5) file to {{cn=config}} format">5.4. Converting old style <EM>slapd.conf</EM>(5) file to <EM>cn=config</EM> format</A></H2>
+<P>Before converting to the <EM>cn=config</EM> format you should make sure that the config backend is properly configured in your existing config file. While the config backend is always present inside slapd, by default it is only accessible by its rootDN, and there are no default credentials assigned so unless you explicitly configure a means to authenticate to it, it will be unusable.</P>
+<P>If you do not already have a <TT>database config</TT> section, add something like this to the end of <TT>slapd.conf</TT></P>
+<PRE>
+ database config
+ rootpw VerySecret
+</PRE>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>Since the config backend can be used to load arbitrary code into the slapd process, it is extremely important to carefully guard whatever credentials are used to access it. Since simple passwords are vulnerable to password guessing attacks, it is usually better to omit the rootpw and only use SASL authentication for the config rootDN.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>An existing <EM>slapd.conf</EM>(5) file can be converted to the new format using <EM>slaptest</EM>(8) or any of the slap tools:</P>
+<PRE>
+ slaptest -f /usr/local/etc/openldap/slapd.conf -F /usr/local/etc/openldap/slapd.d
+</PRE>
+<P>Test that you can access entries under <TT>cn=config</TT> using the default <EM>rootdn</EM> and the <EM>rootpw</EM> configured above:</P>
+<PRE>
+ ldapsearch -x -D cn=config -w VerySecret -b cn=config
+</PRE>
+<P>You can then discard the old <EM>slapd.conf</EM>(5) file. Make sure to launch <EM>slapd</EM>(8) with the <EM>-F</EM> option to specify the configuration directory if you are not using the default directory path.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>When converting from the slapd.conf format to slapd.d format, any included files will also be integrated into the resulting configuration database.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H2><A NAME="Recovering from a broken configuration">5.5. Recovering from a broken configuration</A></H2>
+<P>If the server using <EM>cn=config</EM> does not start, either because the configuration does not represent the current version or because it has been corrupted, these actions are available, in the order of decreasing preference.</P>
+<P>Make sure you have made a backup of the &quot;broken&quot; version before you attempt any of these:</P>
+<H3><A NAME="Generate an ldif version of the configuration database and reload from that">5.5.1. Generate an ldif version of the configuration database and reload from that</A></H3>
+<P>Most of the time, the configuration can be parsed and a text version generated with <EM>slapcat</EM>(8):</P>
+<PRE>
+ slapcat -F /usr/local/etc/openldap/slapd.d -n0 -l extracted_config.ldif
+</PRE>
+<P>After you have backed up and removed the old configuration database contents, this output ldif can be hand-edited to adjust or remove the offending entries and imported again:</P>
+<PRE>
+ slapadd -F /usr/local/etc/openldap/slapd.d -l updated_config.ldif
+ slaptest -F /usr/local/etc/openldap/slapd.d
+</PRE>
+<H3><A NAME="Modify config in-place">5.5.2. Modify config in-place</A></H3>
+<P>If the configuration can be parsed and you know exactly what you need to do, you can use <EM>slapmodify</EM>(8) to effect the required changes directly:</P>
+<PRE>
+ slapmodify -F /usr/local/etc/openldap/slapd.d
+ dn: ..., cn=config
+ changetype: ...
+ ...
+</PRE>
+<H3><A NAME="Recover with plain back-ldif">5.5.3. Recover with plain back-ldif</A></H3>
+<P>If the configuration contains items that <EM>slapd</EM>(8) cannot process as a <EM>cn=config</EM> database at all, the last resort is to disable schema checking and operate on it as a regular back-ldif database. This might cease to work with future versions of OpenLDAP without notice, attempt this only when all of the above fail.</P>
+<P>First, create a directory to serve as the hosting DB and create the structure:</P>
+<PRE>
+ mkdir ./recovery ./recovery/cn=recovery
+ cp /usr/local/etc/openldap/slapd.d/cn=config.ldif ./recovery/cn=recovery
+ cp -r /usr/local/etc/openldap/slapd.d/cn=config ./recovery/cn=recovery
+</PRE>
+<P>Or, if you have already backed up your old configuration, you can symlink it into place:</P>
+<PRE>
+ mkdir ./recovery
+ ln -s /usr/local/etc/openldap/slapd.d ./recovery/cn=recovery
+</PRE>
+<P>Next, create a trivial <EM>slapd.conf</EM>(5) to access the new database:</P>
+<PRE>
+ database ldif
+ suffix cn=recovery
+ directory ./recovery/
+</PRE>
+<P>Note the change of suffix, <TT>cn=config</TT> is hardcoded to correspond to an active config database, so we have to home it one level deeper - at <TT>cn=config,cn=recovery</TT>.</P>
+<P>Now you can use <EM>slapmodify</EM>(8) to modify the database, it is most likely you will need to run with schema checking disabled:</P>
+<PRE>
+ slapmodify -f ./recovery.conf -s
+</PRE>
+<P>You can test the validity of your config with <EM>slaptest</EM>(8):</P>
+<PRE>
+ slaptest -F ./recovery/cn=recovery
+</PRE>
+<P>And generate a full ldif with <EM>slapcat</EM>(8):</P>
+<PRE>
+ slapcat -F ./recovery/cn=recovery -n0
+</PRE>
+<P></P>
+<HR>
+<H1><A NAME="The slapd Configuration File">6. The slapd Configuration File</A></H1>
+<P>This chapter describes configuring <EM>slapd</EM>(8) via the <EM>slapd.conf</EM>(5) configuration file. <EM>slapd.conf</EM>(5) has been deprecated and should only be used if your site requires one of the backends that hasn't yet been updated to work with the newer <EM>slapd-config</EM>(5) system. Configuring <EM>slapd</EM>(8) via <EM>slapd-config</EM>(5) is described in the previous chapter.</P>
+<P>The <EM>slapd.conf</EM>(5) file is normally installed in the <TT>/usr/local/etc/openldap</TT> directory. An alternate configuration file location can be specified via a command-line option to <EM>slapd</EM>(8).</P>
+<H2><A NAME="Configuration File Format">6.1. Configuration File Format</A></H2>
+<P>The <EM>slapd.conf</EM>(5) file consists of three types of configuration information: global, backend specific, and database specific. Global information is specified first, followed by information associated with a particular backend type, which is then followed by information associated with a particular database instance. Global directives can be overridden in backend and/or database directives, and backend directives can be overridden by database directives.</P>
+<P>Blank lines and comment lines beginning with a '<TT>#</TT>' character are ignored. If a line begins with whitespace, it is considered a continuation of the previous line (even if the previous line is a comment).</P>
+<P>The general format of slapd.conf is as follows:</P>
+<PRE>
+ # global configuration directives
+ &lt;global config directives&gt;
+
+ # backend definition
+ backend &lt;typeA&gt;
+ &lt;backend-specific directives&gt;
+
+ # first database definition &amp; config directives
+ database &lt;typeA&gt;
+ &lt;database-specific directives&gt;
+
+ # second database definition &amp; config directives
+ database &lt;typeB&gt;
+ &lt;database-specific directives&gt;
+
+ # second database definition &amp; config directives
+ database &lt;typeA&gt;
+ &lt;database-specific directives&gt;
+
+ # subsequent backend &amp; database definitions &amp; config directives
+ ...
+</PRE>
+<P>A configuration directive may take arguments. If so, they are separated by whitespace. If an argument contains whitespace, the argument should be enclosed in double quotes <TT>&quot;like this&quot;</TT>. If an argument contains a double quote or a backslash character `<TT>\</TT>', the character should be preceded by a backslash character `<TT>\</TT>'.</P>
+<P>The distribution contains an example configuration file that will be installed in the <TT>/usr/local/etc/openldap</TT> directory. A number of files containing schema definitions (attribute types and object classes) are also provided in the <TT>/usr/local/etc/openldap/schema</TT> directory.</P>
+<H2><A NAME="Configuration File Directives">6.2. Configuration File Directives</A></H2>
+<P>This section details commonly used configuration directives. For a complete list, see the <EM>slapd.conf</EM>(5) manual page. This section separates the configuration file directives into global, backend-specific and data-specific categories, describing each directive and its default value (if any), and giving an example of its use.</P>
+<H3><A NAME="Global Directives">6.2.1. Global Directives</A></H3>
+<P>Directives described in this section apply to all backends and databases unless specifically overridden in a backend or database definition. Arguments that should be replaced by actual text are shown in brackets <TT>&lt;&gt;</TT>.</P>
+<H4><A NAME="access to &lt;what&gt; [ by &lt;who&gt; [&lt;accesslevel&gt;] [&lt;control&gt;] ]+">6.2.1.1. access to &lt;what&gt; [ by &lt;who&gt; [&lt;accesslevel&gt;] [&lt;control&gt;] ]+</A></H4>
+<P>This directive grants access (specified by &lt;accesslevel&gt;) to a set of entries and/or attributes (specified by &lt;what&gt;) by one or more requestors (specified by &lt;who&gt;). See the <A HREF="#Access Control">Access Control</A> section of this guide for basic usage.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>If no <TT>access</TT> directives are specified, the default access control policy, <TT>access to * by * read</TT>, allows all both authenticated and anonymous users read access.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H4><A NAME="attributetype &lt;{{REF:RFC4512}} Attribute Type Description&gt;"> </A>6.2.1.2. attributetype &lt;<A HREF="https://www.rfc-editor.org/rfc/rfc4512.txt">RFC4512</A> Attribute Type Description&gt;</H4>
+<P>This directive defines an attribute type. Please see the <A HREF="#Schema Specification">Schema Specification</A> chapter for information regarding how to use this directive.</P>
+<H4><A NAME="idletimeout &lt;integer&gt;">6.2.1.3. idletimeout &lt;integer&gt;</A></H4>
+<P>Specify the number of seconds to wait before forcibly closing an idle client connection. An idletimeout of 0, the default, disables this feature.</P>
+<H4><A NAME="include &lt;filename&gt;">6.2.1.4. include &lt;filename&gt;</A></H4>
+<P>This directive specifies that slapd should read additional configuration information from the given file before continuing with the next line of the current file. The included file should follow the normal slapd config file format. The file is commonly used to include files containing schema specifications.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>You should be careful when using this directive - there is no small limit on the number of nested include directives, and no loop detection is done.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H4><A NAME="loglevel &lt;level&gt;">6.2.1.5. loglevel &lt;level&gt;</A></H4>
+<P>This directive specifies the level at which log statements and operation statistics should be sent to syslog (currently logged to the <EM>syslogd</EM>(8) <TT>LOG_LOCAL4</TT> facility). You must have configured OpenLDAP <TT>--enable-debug</TT> (the default) for this to work, except for the two statistics levels, which are always enabled. Log levels may be specified as integers or by keyword. Multiple log levels may be used and the levels are additive. The possible values for &lt;integer&gt; are:</P>
+<TABLE CLASS="columns" BORDER ALIGN='Center'>
+<CAPTION ALIGN=top>Table 6.1: Logging Levels</CAPTION>
+<TR CLASS="heading">
+<TD ALIGN='Right'>
+<STRONG>Level</STRONG>
+</TD>
+<TD ALIGN='Left'>
+<STRONG>Keyword</STRONG>
+</TD>
+<TD>
+<STRONG>Description</STRONG>
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+-1
+</TD>
+<TD ALIGN='Left'>
+any
+</TD>
+<TD>
+enable all debugging
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+0
+</TD>
+<TD ALIGN='Left'>
+&nbsp;
+</TD>
+<TD>
+no debugging
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+1
+</TD>
+<TD ALIGN='Left'>
+(0x1 trace)
+</TD>
+<TD>
+trace function calls
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+2
+</TD>
+<TD ALIGN='Left'>
+(0x2 packets)
+</TD>
+<TD>
+debug packet handling
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+4
+</TD>
+<TD ALIGN='Left'>
+(0x4 args)
+</TD>
+<TD>
+heavy trace debugging
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+8
+</TD>
+<TD ALIGN='Left'>
+(0x8 conns)
+</TD>
+<TD>
+connection management
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+16
+</TD>
+<TD ALIGN='Left'>
+(0x10 BER)
+</TD>
+<TD>
+print out packets sent and received
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+32
+</TD>
+<TD ALIGN='Left'>
+(0x20 filter)
+</TD>
+<TD>
+search filter processing
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+64
+</TD>
+<TD ALIGN='Left'>
+(0x40 config)
+</TD>
+<TD>
+configuration processing
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+128
+</TD>
+<TD ALIGN='Left'>
+(0x80 ACL)
+</TD>
+<TD>
+access control list processing
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+256
+</TD>
+<TD ALIGN='Left'>
+(0x100 stats)
+</TD>
+<TD>
+stats log connections/operations/results
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+512
+</TD>
+<TD ALIGN='Left'>
+(0x200 stats2)
+</TD>
+<TD>
+stats log entries sent
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+1024
+</TD>
+<TD ALIGN='Left'>
+(0x400 shell)
+</TD>
+<TD>
+print communication with shell backends
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+2048
+</TD>
+<TD ALIGN='Left'>
+(0x800 parse)
+</TD>
+<TD>
+print entry parsing debugging
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+16384
+</TD>
+<TD ALIGN='Left'>
+(0x4000 sync)
+</TD>
+<TD>
+syncrepl consumer processing
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+32768
+</TD>
+<TD ALIGN='Left'>
+(0x8000 none)
+</TD>
+<TD>
+only messages that get logged regardless of configured log level
+</TD>
+</TR>
+</TABLE>
+
+<P>The desired log level can be input as a single integer that combines the (ORed) desired levels, both in decimal or in hexadecimal notation, as a list of integers (that are ORed internally), or as a list of the names that are shown between brackets, such that</P>
+<PRE>
+ loglevel 129
+ loglevel 0x81
+ loglevel 128 1
+ loglevel 0x80 0x1
+ loglevel acl trace
+</PRE>
+<P>are equivalent.</P>
+<P>Examples:</P>
+<PRE>
+ loglevel -1
+</PRE>
+<P>This will enable all log levels.</P>
+<PRE>
+ loglevel conns filter
+</PRE>
+<P>Just log the connection and search filter processing.</P>
+<PRE>
+ loglevel none
+</PRE>
+<P>Log those messages that are logged regardless of the configured loglevel. This differs from setting the log level to 0, when no logging occurs. At least the <TT>None</TT> level is required to have high priority messages logged.</P>
+<P>Default:</P>
+<PRE>
+ loglevel stats
+</PRE>
+<P>Basic stats logging is configured by default.</P>
+<H4><A NAME="objectclass &lt;{{REF:RFC4512}} Object Class Description&gt;"> </A>6.2.1.6. objectclass &lt;<A HREF="https://www.rfc-editor.org/rfc/rfc4512.txt">RFC4512</A> Object Class Description&gt;</H4>
+<P>This directive defines an object class. Please see the <A HREF="#Schema Specification">Schema Specification</A> chapter for information regarding how to use this directive.</P>
+<H4><A NAME="referral &lt;URI&gt;">6.2.1.7. referral &lt;URI&gt;</A></H4>
+<P>This directive specifies the referral to pass back when slapd cannot find a local database to handle a request.</P>
+<P>Example:</P>
+<PRE>
+ referral ldap://root.openldap.org
+</PRE>
+<P>This will refer non-local queries to the global root LDAP server at the OpenLDAP Project. Smart LDAP clients can re-ask their query at that server, but note that most of these clients are only going to know how to handle simple LDAP URLs that contain a host part and optionally a distinguished name part.</P>
+<H4><A NAME="sizelimit &lt;integer&gt;">6.2.1.8. sizelimit &lt;integer&gt;</A></H4>
+<P>This directive specifies the maximum number of entries to return from a search operation.</P>
+<P>Default:</P>
+<PRE>
+ sizelimit 500
+</PRE>
+<P>See the <A HREF="#Limits">Limits</A> section of this guide and <EM>slapd.conf</EM>(5) for more details.</P>
+<H4><A NAME="timelimit &lt;integer&gt;">6.2.1.9. timelimit &lt;integer&gt;</A></H4>
+<P>This directive specifies the maximum number of seconds (in real time) slapd will spend answering a search request. If a request is not finished in this time, a result indicating an exceeded timelimit will be returned.</P>
+<P>Default:</P>
+<PRE>
+ timelimit 3600
+</PRE>
+<P>See the <A HREF="#Limits">Limits</A> section of this guide and <EM>slapd.conf</EM>(5) for more details.</P>
+<H3><A NAME="General Backend Directives">6.2.2. General Backend Directives</A></H3>
+<P>Directives in this section apply only to the backend in which they are defined. They are supported by every type of backend. Backend directives apply to all databases instances of the same type and, depending on the directive, may be overridden by database directives.</P>
+<H4><A NAME="backend &lt;type&gt;">6.2.2.1. backend &lt;type&gt;</A></H4>
+<P>This directive marks the beginning of a backend declaration. <TT>&lt;type&gt;</TT> should be one of the supported backend types listed in Table 6.2.</P>
+<TABLE CLASS="columns" BORDER ALIGN='Center'>
+<CAPTION ALIGN=top>Table 6.2: Database Backends</CAPTION>
+<TR CLASS="heading">
+<TD>
+<STRONG>Types</STRONG>
+</TD>
+<TD>
+<STRONG>Description</STRONG>
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>asyncmet</TT>
+</TD>
+<TD>
+a Asynchronous Metadirectory backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>config</TT>
+</TD>
+<TD>
+Slapd configuration backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>dnssrv</TT>
+</TD>
+<TD>
+DNS SRV backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>ldap</TT>
+</TD>
+<TD>
+Lightweight Directory Access Protocol (Proxy) backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>ldif</TT>
+</TD>
+<TD>
+Lightweight Data Interchange Format backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>mdb</TT>
+</TD>
+<TD>
+Memory-Mapped DB backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>meta</TT>
+</TD>
+<TD>
+Metadirectory backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>monitor</TT>
+</TD>
+<TD>
+Monitor backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>ndb</TT>
+</TD>
+<TD>
+MySQL NDB backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>null</TT>
+</TD>
+<TD>
+Null backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>passwd</TT>
+</TD>
+<TD>
+Provides read-only access to <EM>passwd</EM>(5)
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>perl</TT>
+</TD>
+<TD>
+Perl Programmable backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>relay</TT>
+</TD>
+<TD>
+Relay backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>sock</TT>
+</TD>
+<TD>
+Socket backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>sql</TT>
+</TD>
+<TD>
+SQL Programmable backend
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>wt</TT>
+</TD>
+<TD>
+WiredTiger backend
+</TD>
+</TR>
+</TABLE>
+
+<P>Example:</P>
+<PRE>
+ backend mdb
+ idlexp 16
+</PRE>
+<P>This marks the beginning of a new <TERM>MDB</TERM> backend definition. At present, only back-mdb implements any options of this type, so this setting is not needed for any other backends.</P>
+<H3><A NAME="General Database Directives">6.2.3. General Database Directives</A></H3>
+<P>Directives in this section apply only to the database in which they are defined. They are supported by every type of database.</P>
+<H4><A NAME="database &lt;type&gt;">6.2.3.1. database &lt;type&gt;</A></H4>
+<P>This directive marks the beginning of a database instance declaration. <TT>&lt;type&gt;</TT> should be one of the supported backend types listed in Table 6.2.</P>
+<P>Example:</P>
+<PRE>
+ database mdb
+</PRE>
+<P>This marks the beginning of a new <TERM>MDB</TERM> database instance declaration.</P>
+<H4><A NAME="limits &lt;selector&gt; &lt;limit&gt; [&lt;limit&gt; [...]]">6.2.3.2. limits &lt;selector&gt; &lt;limit&gt; [&lt;limit&gt; [...]]</A></H4>
+<P>Specify time and size limits based on the operation's initiator or base DN.</P>
+<P>See the <A HREF="#Limits">Limits</A> section of this guide and <EM>slapd.conf</EM>(5) for more details.</P>
+<H4><A NAME="readonly { on | off }">6.2.3.3. readonly { on | off }</A></H4>
+<P>This directive puts the database into &quot;read-only&quot; mode. Any attempts to modify the database will return an &quot;unwilling to perform&quot; error. If set on a consumer, modifications sent by syncrepl will still occur.</P>
+<P>Default:</P>
+<PRE>
+ readonly off
+</PRE>
+<H4><A NAME="rootdn &lt;DN&gt;">6.2.3.4. rootdn &lt;DN&gt;</A></H4>
+<P>This directive specifies the DN that is not subject to access control or administrative limit restrictions for operations on this database. The DN need not refer to an entry in this database or even in the directory. The DN may refer to a SASL identity.</P>
+<P>Entry-based Example:</P>
+<PRE>
+ rootdn &quot;cn=Manager,dc=example,dc=com&quot;
+</PRE>
+<P>SASL-based Example:</P>
+<PRE>
+ rootdn &quot;uid=root,cn=example.com,cn=digest-md5,cn=auth&quot;
+</PRE>
+<P>See the <A HREF="#SASL Authentication">SASL Authentication</A> section for information on SASL authentication identities.</P>
+<H4><A NAME="rootpw &lt;password&gt;">6.2.3.5. rootpw &lt;password&gt;</A></H4>
+<P>This directive can be used to specifies a password for the DN for the rootdn (when the rootdn is set to a DN within the database).</P>
+<P>Example:</P>
+<PRE>
+ rootpw secret
+</PRE>
+<P>It is also permissible to provide hash of the password in <A HREF="https://www.rfc-editor.org/rfc/rfc2307.txt">RFC2307</A> form. <EM>slappasswd</EM>(8) may be used to generate the password hash.</P>
+<P>Example:</P>
+<PRE>
+ rootpw {SSHA}ZKKuqbEKJfKSXhUbHG3fG8MDn9j1v4QN
+</PRE>
+<P>The hash was generated using the command <TT>slappasswd -s secret</TT>.</P>
+<H4><A NAME="suffix &lt;dn suffix&gt;">6.2.3.6. suffix &lt;dn suffix&gt;</A></H4>
+<P>This directive specifies the DN suffix of queries that will be passed to this backend database. Multiple suffix lines can be given, and at least one is required for each database definition.</P>
+<P>Example:</P>
+<PRE>
+ suffix &quot;dc=example,dc=com&quot;
+</PRE>
+<P>Queries with a DN ending in &quot;dc=example,dc=com&quot; will be passed to this backend.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>When the backend to pass a query to is selected, slapd looks at the suffix line(s) in each database definition in the order they appear in the file. Thus, if one database suffix is a prefix of another, it must appear after it in the config file.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H4><A NAME="syncrepl">6.2.3.7. syncrepl</A></H4>
+<PRE>
+ syncrepl rid=&lt;replica ID&gt;
+ provider=ldap[s]://&lt;hostname&gt;[:port]
+ searchbase=&lt;base DN&gt;
+ [type=refreshOnly|refreshAndPersist]
+ [interval=dd:hh:mm:ss]
+ [retry=[&lt;retry interval&gt; &lt;# of retries&gt;]+]
+ [filter=&lt;filter str&gt;]
+ [scope=sub|one|base]
+ [attrs=&lt;attr list&gt;]
+ [exattrs=&lt;attr list&gt;]
+ [attrsonly]
+ [sizelimit=&lt;limit&gt;]
+ [timelimit=&lt;limit&gt;]
+ [schemachecking=on|off]
+ [network-timeout=&lt;seconds&gt;]
+ [timeout=&lt;seconds&gt;]
+ [bindmethod=simple|sasl]
+ [binddn=&lt;DN&gt;]
+ [saslmech=&lt;mech&gt;]
+ [authcid=&lt;identity&gt;]
+ [authzid=&lt;identity&gt;]
+ [credentials=&lt;passwd&gt;]
+ [realm=&lt;realm&gt;]
+ [secprops=&lt;properties&gt;]
+ [keepalive=&lt;idle&gt;:&lt;probes&gt;:&lt;interval&gt;]
+ [starttls=yes|critical]
+ [tls_cert=&lt;file&gt;]
+ [tls_key=&lt;file&gt;]
+ [tls_cacert=&lt;file&gt;]
+ [tls_cacertdir=&lt;path&gt;]
+ [tls_reqcert=never|allow|try|demand]
+ [tls_cipher_suite=&lt;ciphers&gt;]
+ [tls_crlcheck=none|peer|all]
+ [tls_protocol_min=&lt;major&gt;[.&lt;minor&gt;]]
+ [suffixmassage=&lt;real DN&gt;]
+ [logbase=&lt;base DN&gt;]
+ [logfilter=&lt;filter str&gt;]
+ [syncdata=default|accesslog|changelog]
+</PRE>
+<P>This directive specifies the current database as a consumer of the provider content by establishing the current <EM>slapd</EM>(8) as a replication consumer site running a syncrepl replication engine. The provider database is located at the replication provider site specified by the <TT>provider</TT> parameter. The consumer database is kept up-to-date with the provider content using the LDAP Content Synchronization protocol. See <A HREF="https://www.rfc-editor.org/rfc/rfc4533.txt">RFC4533</A> for more information on the protocol.</P>
+<P>The <TT>rid</TT> parameter is used for identification of the current <TT>syncrepl</TT> directive within the replication consumer server, where <TT>&lt;replica ID&gt;</TT> uniquely identifies the syncrepl specification described by the current <TT>syncrepl</TT> directive. <TT>&lt;replica ID&gt;</TT> is non-negative and is no more than three decimal digits in length.</P>
+<P>The <TT>provider</TT> parameter specifies the replication provider site containing the provider content as an LDAP URI. The <TT>provider</TT> parameter specifies a scheme, a host and optionally a port where the provider slapd instance can be found. Either a domain name or IP address may be used for &lt;hostname&gt;. Examples are <TT>ldap://provider.example.com:389</TT> or <TT>ldaps://192.168.1.1:636</TT>. If &lt;port&gt; is not given, the standard LDAP port number (389 or 636) is used. Note that the syncrepl uses a consumer-initiated protocol, and hence its specification is located on the consumer.</P>
+<P>The content of the syncrepl consumer is defined using a search specification as its result set. The consumer slapd will send search requests to the provider slapd according to the search specification. The search specification includes <TT>searchbase</TT>, <TT>scope</TT>, <TT>filter</TT>, <TT>attrs</TT>, <TT>exattrs</TT>, <TT>attrsonly</TT>, <TT>sizelimit</TT>, and <TT>timelimit</TT> parameters as in the normal search specification. The <TT>searchbase</TT> parameter has no default value and must always be specified. The <TT>scope</TT> defaults to <TT>sub</TT>, the <TT>filter</TT> defaults to <TT>(objectclass=*)</TT>, <TT>attrs</TT> defaults to <TT>&quot;*,+&quot;</TT> to replicate all user and operational attributes, and <TT>attrsonly</TT> is unset by default. Both <TT>sizelimit</TT> and <TT>timelimit</TT> default to &quot;unlimited&quot;, and only positive integers or &quot;unlimited&quot; may be specified. The <TT>exattrs</TT> option may also be used to specify attributes that should be omitted from incoming entries.</P>
+<P>The <TERM>LDAP Content Synchronization</TERM> protocol has two operation types: <TT>refreshOnly</TT> and <TT>refreshAndPersist</TT>. The operation type is specified by the <TT>type</TT> parameter. In the <TT>refreshOnly</TT> operation, the next synchronization search operation is periodically rescheduled at an interval time after each synchronization operation finishes. The interval is specified by the <TT>interval</TT> parameter. It is set to one day by default. In the <TT>refreshAndPersist</TT> operation, a synchronization search remains persistent in the provider <EM>slapd</EM> instance. Further updates to the provider will generate <TT>searchResultEntry</TT> to the consumer slapd as the search responses to the persistent synchronization search.</P>
+<P>If an error occurs during replication, the consumer will attempt to reconnect according to the retry parameter which is a list of the &lt;retry interval&gt; and &lt;# of retries&gt; pairs. For example, retry=&quot;60 10 300 3&quot; lets the consumer retry every 60 seconds for the first 10 times and then retry every 300 seconds for the next three times before stop retrying. + in &lt;# of retries&gt; means indefinite number of retries until success.</P>
+<P>The schema checking can be enforced at the LDAP Sync consumer site by turning on the <TT>schemachecking</TT> parameter. If it is turned on, every replicated entry will be checked for its schema as the entry is stored on the consumer. Every entry in the consumer should contain those attributes required by the schema definition. If it is turned off, entries will be stored without checking schema conformance. The default is off.</P>
+<P>The <TT>network-timeout</TT> parameter sets how long the consumer will wait to establish a network connection to the provider. Once a connection is established, the <TT>timeout</TT> parameter determines how long the consumer will wait for the initial Bind request to complete. The defaults for these parameters come from <EM>ldap.conf</EM>(5).</P>
+<P>The <TT>binddn</TT> parameter gives the DN to bind as for the syncrepl searches to the provider slapd. It should be a DN which has read access to the replication content in the provider database.</P>
+<P>The <TT>bindmethod</TT> is <TT>simple</TT> or <TT>sasl</TT>, depending on whether simple password-based authentication or <TERM>SASL</TERM> authentication is to be used when connecting to the provider <EM>slapd</EM> instance.</P>
+<P>Simple authentication should not be used unless adequate data integrity and confidentiality protections are in place (e.g. TLS or IPsec). Simple authentication requires specification of <TT>binddn</TT> and <TT>credentials</TT> parameters.</P>
+<P>SASL authentication is generally recommended. SASL authentication requires specification of a mechanism using the <TT>saslmech</TT> parameter. Depending on the mechanism, an authentication identity and/or credentials can be specified using <TT>authcid</TT> and <TT>credentials</TT>, respectively. The <TT>authzid</TT> parameter may be used to specify an authorization identity.</P>
+<P>The <TT>realm</TT> parameter specifies a realm which a certain mechanisms authenticate the identity within. The <TT>secprops</TT> parameter specifies Cyrus SASL security properties.</P>
+<P>The <TT>keepalive</TT> parameter sets the values of idle, probes, and interval used to check whether a socket is alive; idle is the number of seconds a connection needs to remain idle before TCP starts sending keepalive probes; probes is the maximum number of keepalive probes TCP should send before dropping the connection; interval is interval in seconds between individual keepalive probes. Only some systems support the customization of these values; the keepalive parameter is ignored otherwise, and system-wide settings are used. For example, keepalive=&quot;240:10:30&quot; will send a keepalive probe 10 times, every 30 seconds, after 240 seconds of idle activity. If no response to the probes is received, the connection will be dropped.</P>
+<P>The <TT>starttls</TT> parameter specifies use of the StartTLS extended operation to establish a TLS session before authenticating to the provider. If the <TT>critical</TT> argument is supplied, the session will be aborted if the StartTLS request fails. Otherwise the syncrepl session continues without TLS. The tls_reqcert setting defaults to <TT>&quot;demand&quot;</TT> and the other TLS settings default to the same as the main slapd TLS settings.</P>
+<P>The <TT>suffixmassage</TT> parameter allows the consumer to pull entries from a remote directory whose DN suffix differs from the local directory. The portion of the remote entries' DNs that matches the searchbase will be replaced with the suffixmassage DN.</P>
+<P>Rather than replicating whole entries, the consumer can query logs of data modifications. This mode of operation is referred to as <EM>delta syncrepl</EM>. In addition to the above parameters, the <TT>logbase</TT> and <TT>logfilter</TT> parameters must be set appropriately for the log that will be used. The <TT>syncdata</TT> parameter must be set to either <TT>&quot;accesslog&quot;</TT> if the log conforms to the <EM>slapo-accesslog</EM>(5) log format, or <TT>&quot;changelog&quot;</TT> if the log conforms to the obsolete <EM>changelog</EM> format. If the <TT>syncdata</TT> parameter is omitted or set to <TT>&quot;default&quot;</TT> then the log parameters are ignored.</P>
+<P>The <EM>syncrepl</EM> replication mechanism is supported by the <EM>mdb</EM> backend.</P>
+<P>See the <A HREF="#LDAP Sync Replication">LDAP Sync Replication</A> chapter of this guide for more information on how to use this directive.</P>
+<H4><A NAME="updateref &lt;URL&gt;">6.2.3.8. updateref &lt;URL&gt;</A></H4>
+<P>This directive is only applicable in a <EM>replica</EM> (or <EM>shadow</EM>) <EM>slapd</EM>(8) instance. It specifies the URL to return to clients which submit update requests upon the replica. If specified multiple times, each <TERM>URL</TERM> is provided.</P>
+<P>Example:</P>
+<PRE>
+ updateref ldap://provider.example.net
+</PRE>
+<H3><A NAME="MDB Backend Directives">6.2.4. MDB Backend Directives</A></H3>
+<P>Directives in this category only apply to the <TERM>MDB</TERM> database backend. They will apply to all &quot;database mdb&quot; instances in the configuration. For a complete reference of MDB backend configuration directives, see <EM>slapd-mdb</EM>(5).</P>
+<H4><A NAME="idlexp &lt;exponent&gt;">6.2.4.1. idlexp &lt;exponent&gt;</A></H4>
+<P>Specify a power of 2 for the maximum size of an index slot. The default is 16, yielding a maximum slot size of 2^16 or 65536. The specified value must be in the range of 16-30.</P>
+<P>This setting helps with the case where certain search filters are slow to return results due to an index slot having collapsed to a range value. This occurs when the number of candidate entries that match the filter for the index slot exceed the configured slot size.</P>
+<P>If this setting is decreased on a server with existing <TERM>MDB</TERM> databases, each db will immediately need its indices to be rebuilt while slapd is offline with the &quot;slapindex -q -t&quot; command.</P>
+<P>If this setting is increased on a server with existing <TERM>MDB</TERM> databases, each db will need its indices rebuilt to take advantage of the change for indices that have already been converted to ranges.</P>
+<H3><A NAME="MDB Database Directives">6.2.5. MDB Database Directives</A></H3>
+<P>Directives in this category only apply to the <TERM>MDB</TERM> database backend. That is, they must follow a &quot;database mdb&quot; line and come before any subsequent &quot;backend&quot; or &quot;database&quot; lines. For a complete reference of MDB configuration directives, see <EM>slapd-mdb</EM>(5).</P>
+<H4><A NAME="directory &lt;directory&gt;">6.2.5.1. directory &lt;directory&gt;</A></H4>
+<P>This directive specifies the directory where the MDB files containing the database and associated indices live.</P>
+<P>Default:</P>
+<PRE>
+ directory /usr/local/var/openldap-data
+</PRE>
+<H4><A NAME="checkpoint &lt;kbyte&gt; &lt;min&gt;">6.2.5.2. checkpoint &lt;kbyte&gt; &lt;min&gt;</A></H4>
+<P>This directive specifies the frequency for flushing the database disk buffers. This directive is only needed if the <EM>dbnosync</EM> option is <TT>TRUE</TT>. The checkpoint will occur if either &lt;kbyte&gt; data has been written or &lt;min&gt; minutes have passed since the last checkpoint. Both arguments default to zero, in which case they are ignored. When the &lt;min&gt; argument is non-zero, an internal task will run every &lt;min&gt; minutes to perform the checkpoint. Note: currently the _kbyte_ setting is unimplemented.</P>
+<P>Example:</P>
+<PRE>
+ checkpoint: 1024 10
+</PRE>
+<H4><A NAME="dbnosync: { TRUE | FALSE }">6.2.5.3. dbnosync: { TRUE | FALSE }</A></H4>
+<P>This directive causes on-disk database contents to not be immediately synchronized with in memory changes upon change. Setting this option to <TT>TRUE</TT> may improve performance at the expense of data integrity.</P>
+<H4><A NAME="envflags: {nosync,nometasync,writemap,mapasync,nordahead}">6.2.5.4. envflags: {nosync,nometasync,writemap,mapasync,nordahead}</A></H4>
+<P>This option specifies flags for finer-grained control of the LMDB library's operation.</P>
+<UL>
+<LI><TT>nosync</TT>: This is exactly the same as the dbnosync directive.
+<LI><TT>nometasync</TT>: Flush the data on a commit, but skip the sync of the meta page. This mode is slightly faster than doing a full sync, but can potentially lose the last committed transaction if the operating system crashes. If both nometasync and nosync are set, the nosync flag takes precedence.
+<LI><TT>writemap</TT>: Use a writable memory map instead of just read-only. This speeds up write operations but makes the database vulnerable to corruption in case any bugs in slapd cause stray writes into the mmap region.
+<LI><TT>mapasync</TT>: When using a writable memory map and performing flushes on each commit, use an asynchronous flush instead of a synchronous flush (the default). This option has no effect if writemap has not been set. It also has no effect if nosync is set.
+<LI><TT>nordahead</TT>: Turn off file readahead. Usually the OS performs readahead on every read request. This usually boosts read performance but can be harmful to random access read performance if the system's memory is full and the DB is larger than RAM. This option is not implemented on Windows.</UL>
+<H4><A NAME="index: {&lt;attrlist&gt; | default} [pres,eq,approx,sub,none]">6.2.5.5. index: {&lt;attrlist&gt; | default} [pres,eq,approx,sub,none]</A></H4>
+<P>This directive specifies the indices to maintain for the given attribute. If only an <TT>&lt;attrlist&gt;</TT> is given, the default indices are maintained. The index keywords correspond to the common types of matches that may be used in an LDAP search filter.</P>
+<P>Example:</P>
+<PRE>
+ index: default pres,eq
+ index: uid
+ index: cn,sn pres,eq,sub
+ index: objectClass eq
+</PRE>
+<P>The first line sets the default set of indices to maintain to present and equality. The second line causes the default (pres,eq) set of indices to be maintained for the <TT>uid</TT> attribute type. The third line causes present, equality, and substring indices to be maintained for <TT>cn</TT> and <TT>sn</TT> attribute types. The fourth line causes an equality index for the <TT>objectClass</TT> attribute type.</P>
+<P>There is no index keyword for inequality matches. Generally these matches do not use an index. However, some attributes do support indexing for inequality matches, based on the equality index.</P>
+<P>A substring index can be more explicitly specified as <TT>subinitial</TT>, <TT>subany</TT>, or <TT>subfinal</TT>, corresponding to the three possible components of a substring match filter. A subinitial index only indexes substrings that appear at the beginning of an attribute value. A subfinal index only indexes substrings that appear at the end of an attribute value, while subany indexes substrings that occur anywhere in a value.</P>
+<P>Note that by default, setting an index for an attribute also affects every subtype of that attribute. E.g., setting an equality index on the <TT>name</TT> attribute causes <TT>cn</TT>, <TT>sn</TT>, and every other attribute that inherits from <TT>name</TT> to be indexed.</P>
+<P>By default, no indices are maintained. It is generally advised that minimally an equality index upon objectClass be maintained.</P>
+<PRE>
+ index: objectClass eq
+</PRE>
+<P>Additional indices should be configured corresponding to the most common searches that are used on the database. Presence indexing should not be configured for an attribute unless the attribute occurs very rarely in the database, and presence searches on the attribute occur very frequently during normal use of the directory. Most applications don't use presence searches, so usually presence indexing is not very useful.</P>
+<H4><A NAME="maxentrysize: &lt;bytes&gt;">6.2.5.6. maxentrysize: &lt;bytes&gt;</A></H4>
+<P>Specify the maximum size of an entry in bytes. Attempts to store an entry larger than this size will be rejected with the error LDAP_ADMINLIMIT_EXCEEDED. The default is 0, which is unlimited.</P>
+<H4><A NAME="maxreaders: &lt;integer&gt;">6.2.5.7. maxreaders: &lt;integer&gt;</A></H4>
+<P>This directive specifies the maximum number of threads that may have concurrent read access to the database. Tools such as slapcat count as a single thread, in addition to threads in any active slapd processes. The default is 126.</P>
+<H4><A NAME="maxsize: &lt;bytes&gt;">6.2.5.8. maxsize: &lt;bytes&gt;</A></H4>
+<P>This directive specifies the maximum size of the database in bytes. A memory map of this size is allocated at startup time and the database will not be allowed to grow beyond this size. The default is 10485760 bytes (10MB). This setting may be changed upward if the configured limit needs to be increased.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>It is important to set this to as large a value as possible, (relative to anticipated growth of the actual data over time) since growing the size later may not be practical when the system is under heavy load.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H4><A NAME="mode: { &lt;octal&gt; | &lt;symbolic&gt; }">6.2.5.9. mode: { &lt;octal&gt; | &lt;symbolic&gt; }</A></H4>
+<P>This directive specifies the file protection mode that newly created database index files should have. This can be in the form <TT>0600</TT> or <TT>-rw-------</TT></P>
+<P>Default:</P>
+<PRE>
+ mode: 0600
+</PRE>
+<H4><A NAME="multival: { &lt;attrlist&gt; | default } &lt;integer&gt; hi,&lt;integer&gt; lo">6.2.5.10. multival: { &lt;attrlist&gt; | default } &lt;integer&gt; hi,&lt;integer&gt; lo</A></H4>
+<P>Specify the number of values for which a multivalued attribute is stored in a separate table. Normally entries are stored as a single blob inside the database. When an entry gets very large or contains attributes with a very large number of values, modifications on that entry may get very slow. Splitting the large attributes out to a separate table can improve the performance of modification operations. The threshold is specified as a pair of integers. If the number of values exceeds the hi threshold the values will be split out. If a modification deletes enough values to bring an attribute below the lo threshold the values will be removed from the separate table and merged back into the main entry blob. The threshold can be set for a specific list of attributes, or the default can be configured for all other attributes. The default value for both hi and lo thresholds is UINT_MAX, which keeps all attributes in the main blob.</P>
+<P>In addition to increasing write performance of operations the use of multival can also decrease fragmentation of the primary <TERM>MDB</TERM> database.</P>
+<H4><A NAME="rtxnsize: &lt;entries&gt;">6.2.5.11. rtxnsize: &lt;entries&gt;</A></H4>
+<P>This directive specifies the maximum number of entries to process in a single read transaction when executing a large search. Long-lived read transactions prevent old database pages from being reused in write transactions, and so can cause significant growth of the database file when there is heavy write traffic. This setting causes the read transaction in large searches to be released and reacquired after the given number of entries has been read, to give writers the opportunity to reclaim old database pages. The default is 10000.</P>
+<H4><A NAME="searchstack: &lt;integer&gt;">6.2.5.12. searchstack: &lt;integer&gt;</A></H4>
+<P>Specify the depth of the stack used for search filter evaluation. Search filters are evaluated on a stack to accommodate nested <TT>AND</TT> / <TT>OR</TT> clauses. An individual stack is allocated for each server thread. The depth of the stack determines how complex a filter can be evaluated without requiring any additional memory allocation. Filters that are nested deeper than the search stack depth will cause a separate stack to be allocated for that particular search operation. These separate allocations can have a major negative impact on server performance, but specifying too much stack will also consume a great deal of memory. Each search uses 512K bytes per level on a 32-bit machine, or 1024K bytes per level on a 64-bit machine. The default stack depth is 16, thus 8MB or 16MB per thread is used on 32 and 64 bit machines, respectively. Also the 512KB size of a single stack slot is set by a compile-time constant which may be changed if needed; the code must be recompiled for the change to take effect.</P>
+<P>Default:</P>
+<PRE>
+ searchstack: 16
+</PRE>
+<H4><A NAME="Sample Entry">6.2.5.13. Sample Entry</A></H4>
+<PRE>
+database mdb
+suffix: &quot;dc=example,dc=com&quot;
+directory: /usr/local/var/openldap-data
+index: objectClass eq
+</PRE>
+<H2><A NAME="Configuration File Example">6.3. Configuration File Example</A></H2>
+<P>The following is an example configuration file, interspersed with explanatory text. It defines two databases to handle different parts of the <TERM>X.500</TERM> tree; both are <TERM>MDB</TERM> database instances. The line numbers shown are provided for reference only and are not included in the actual file. First, the global configuration section:</P>
+<PRE>
+ 1. # example config file - global configuration section
+ 2. include /usr/local/etc/schema/core.schema
+ 3. referral ldap://root.openldap.org
+ 4. access to * by * read
+</PRE>
+<P>Line 1 is a comment. Line 2 includes another config file which contains <EM>core</EM> schema definitions. The <TT>referral</TT> directive on line 3 means that queries not local to one of the databases defined below will be referred to the LDAP server running on the standard port (389) at the host <TT>root.openldap.org</TT>.</P>
+<P>Line 4 is a global access control. It applies to all entries (after any applicable database-specific access controls).</P>
+<P>The next section of the configuration file defines a MDB backend that will handle queries for things in the &quot;dc=example,dc=com&quot; portion of the tree. The database is to be replicated to two replica slapds, one on truelies, the other on judgmentday. Indices are to be maintained for several attributes, and the <TT>userPassword</TT> attribute is to be protected from unauthorized access.</P>
+<PRE>
+ 5. # MDB definition for the example.com
+ 6. database mdb
+ 7. suffix &quot;dc=example,dc=com&quot;
+ 8. directory /usr/local/var/openldap-data
+ 9. rootdn &quot;cn=Manager,dc=example,dc=com&quot;
+ 10. rootpw secret
+ 11. # indexed attribute definitions
+ 12. index uid pres,eq
+ 13. index cn,sn pres,eq,approx,sub
+ 14. index objectClass eq
+ 15. # database access control definitions
+ 16. access to attrs=userPassword
+ 17. by self write
+ 18. by anonymous auth
+ 19. by dn.base=&quot;cn=Admin,dc=example,dc=com&quot; write
+ 20. by * none
+ 21. access to *
+ 22. by self write
+ 23. by dn.base=&quot;cn=Admin,dc=example,dc=com&quot; write
+ 24. by * read
+</PRE>
+<P>Line 5 is a comment. The start of the database definition is marked by the database keyword on line 6. Line 7 specifies the DN suffix for queries to pass to this database. Line 8 specifies the directory in which the database files will live.</P>
+<P>Lines 9 and 10 identify the database <EM>super-user</EM> entry and associated password. This entry is not subject to access control or size or time limit restrictions.</P>
+<P>Lines 12 through 14 indicate the indices to maintain for various attributes.</P>
+<P>Lines 16 through 24 specify access control for entries in this database. For all applicable entries, the <TT>userPassword</TT> attribute is writable by the entry itself and by the &quot;admin&quot; entry. It may be used for authentication/authorization purposes, but is otherwise not readable. All other attributes are writable by the entry and the &quot;admin&quot; entry, but may be read by all users (authenticated or not).</P>
+<P>The next section of the example configuration file defines another MDB database. This one handles queries involving the <TT>dc=example,dc=net</TT> subtree but is managed by the same entity as the first database. Note that without line 39, the read access would be allowed due to the global access rule at line 4.</P>
+<PRE>
+ 33. # MDB definition for example.net
+ 34. database mdb
+ 35. suffix &quot;dc=example,dc=net&quot;
+ 36. directory /usr/local/var/openldap-data-net
+ 37. rootdn &quot;cn=Manager,dc=example,dc=com&quot;
+ 38. index objectClass eq
+ 39. access to * by users read
+</PRE>
+<P></P>
+<HR>
+<H1><A NAME="Running slapd">7. Running slapd</A></H1>
+<P><EM>slapd</EM>(8) is designed to be run as a standalone service. This allows the server to take advantage of caching, manage concurrency issues with underlying databases, and conserve system resources. Running from <EM>inetd</EM>(8) is <EM>NOT</EM> an option.</P>
+<H2><A NAME="Command-Line Options">7.1. Command-Line Options</A></H2>
+<P><EM>slapd</EM>(8) supports a number of command-line options as detailed in the manual page. This section details a few commonly used options.</P>
+<PRE>
+ -f &lt;filename&gt;
+</PRE>
+<P>This option specifies an alternate configuration file for slapd. The default is normally <TT>/usr/local/etc/openldap/slapd.conf</TT>.</P>
+<PRE>
+ -F &lt;slapd-config-directory&gt;
+</PRE>
+<P>Specifies the slapd configuration directory. The default is <TT>/usr/local/etc/openldap/slapd.d</TT>.</P>
+<P>If both <TT>-f</TT> and <TT>-F</TT> are specified, the config file will be read and converted to config directory format and written to the specified directory. If neither option is specified, slapd will attempt to read the default config directory before trying to use the default config file. If a valid config directory exists then the default config file is ignored. All of the slap tools that use the config options observe this same behavior.</P>
+<PRE>
+ -h &lt;URLs&gt;
+</PRE>
+<P>This option specifies alternative listener configurations. The default is <TT>ldap:///</TT> which implies <TERM>LDAP</TERM> over <TERM>TCP</TERM> on all interfaces on the default LDAP port 389. You can specify specific host-port pairs or other protocol schemes (such as <TT>ldaps://</TT> or <TT>ldapi://</TT>). slapd supports the HAProxy proxy protocol version 2, which allows a load balancer or proxy server to provide the remote client IP address to slapd to be used for access control or logging. Listeners configured using either <TT>pldap:///</TT> or <TT>pldaps:///</TT> URLS will only accept connections that include the necessary proxy protocol header. Connections to the ports used by these listeners should be restricted at the network level to only trusted load balancers or proxies to avoid spoofing of client IP addresses by third parties.</P>
+<TABLE CLASS="columns" BORDER>
+<TR CLASS="heading">
+<TD>
+<STRONG>URL</STRONG>
+</TD>
+<TD>
+<STRONG>Protocol</STRONG>
+</TD>
+<TD>
+<STRONG>Transport</STRONG>
+</TD>
+</TR>
+<TR>
+<TD>
+ldap:///
+</TD>
+<TD>
+LDAP
+</TD>
+<TD>
+TCP port 389
+</TD>
+</TR>
+<TR>
+<TD>
+pldap:///
+</TD>
+<TD>
+proxied LDAP
+</TD>
+<TD>
+TCP port 389
+</TD>
+</TR>
+<TR>
+<TD>
+ldaps:///
+</TD>
+<TD>
+LDAP over SSL
+</TD>
+<TD>
+TCP port 636
+</TD>
+</TR>
+<TR>
+<TD>
+pldaps:///
+</TD>
+<TD>
+proxied LDAP over SSL
+</TD>
+<TD>
+TCP port 636
+</TD>
+</TR>
+<TR>
+<TD>
+ldapi:///
+</TD>
+<TD>
+LDAP
+</TD>
+<TD>
+IPC (Unix-domain socket)
+</TD>
+</TR>
+</TABLE>
+
+<P>For example, <TT>-h &quot;ldaps:// ldap://127.0.0.1:666&quot;</TT> will create two listeners: one for the (non-standard) <TT>ldaps://</TT> scheme on all interfaces on the default <TT>ldaps://</TT> port 636, and one for the standard <TT>ldap://</TT> scheme on the <TT>localhost</TT> (<EM>loopback</EM>) interface on port 666. Hosts may be specified using using hostnames or <TERM>IPv4</TERM> or <TERM>IPv6</TERM> addresses. Port values must be numeric.</P>
+<P>For LDAP over IPC, the pathname of the Unix-domain socket can be encoded in the URL. Note that directory separators must be URL-encoded, like any other characters that are special to URLs. Thus the socket <TT>/usr/local/var/ldapi</TT> must be encoded as</P>
+<PRE>
+ ldapi://%2Fusr%2Flocal%2Fvar%2Fldapi
+</PRE>
+<P>ldapi: is described in detail in <EM>Using LDAP Over IPC Mechanisms</EM> [<A HREF="https://tools.ietf.org/html/draft-chu-ldap-ldapi-00">Chu-LDAPI</A>]</P>
+<P>Note that the ldapi:/// transport is not widely implemented: non-OpenLDAP clients may not be able to use it.</P>
+<PRE>
+ -n &lt;service-name&gt;
+</PRE>
+<P>This option specifies the service name used for logging and other purposes. The default service name is <TT>slapd</TT>.</P>
+<PRE>
+ -l &lt;syslog-local-user&gt;
+</PRE>
+<P>This option specifies the local user for the <EM>syslog</EM>(8) facility. Values can be <TT>LOCAL0</TT>, <TT>LOCAL1</TT>, <TT>LOCAL2</TT>, ..., and <TT>LOCAL7</TT>. The default is <TT>LOCAL4</TT>. This option may not be supported on all systems.</P>
+<PRE>
+ -u user -g group
+</PRE>
+<P>These options specify the user and group, respectively, to run as. <TT>user</TT> can be either a user name or uid. <TT>group</TT> can be either a group name or gid.</P>
+<PRE>
+ -r directory
+</PRE>
+<P>This option specifies a run-time directory. slapd will <EM>chroot</EM>(2) to this directory after opening listeners but before reading any configuration files or initializing any backends.</P>
+<UL>
+</UL>
+<PRE>
+ -d &lt;level&gt; | ?
+</PRE>
+<P>This option sets the slapd debug level to &lt;level&gt;. When level is a `?' character, the various debugging levels are printed and slapd exits, regardless of any other options you give it. Current debugging levels are</P>
+<TABLE CLASS="columns" BORDER ALIGN='Center'>
+<CAPTION ALIGN=top>Table 7.1: Debugging Levels</CAPTION>
+<TR CLASS="heading">
+<TD ALIGN='Right'>
+<STRONG>Level</STRONG>
+</TD>
+<TD ALIGN='Left'>
+<STRONG>Keyword</STRONG>
+</TD>
+<TD>
+<STRONG>Description</STRONG>
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+-1
+</TD>
+<TD ALIGN='Left'>
+any
+</TD>
+<TD>
+enable all debugging
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+0
+</TD>
+<TD ALIGN='Left'>
+&nbsp;
+</TD>
+<TD>
+no debugging
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+1
+</TD>
+<TD ALIGN='Left'>
+(0x1 trace)
+</TD>
+<TD>
+trace function calls
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+2
+</TD>
+<TD ALIGN='Left'>
+(0x2 packets)
+</TD>
+<TD>
+debug packet handling
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+4
+</TD>
+<TD ALIGN='Left'>
+(0x4 args)
+</TD>
+<TD>
+heavy trace debugging
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+8
+</TD>
+<TD ALIGN='Left'>
+(0x8 conns)
+</TD>
+<TD>
+connection management
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+16
+</TD>
+<TD ALIGN='Left'>
+(0x10 BER)
+</TD>
+<TD>
+print out packets sent and received
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+32
+</TD>
+<TD ALIGN='Left'>
+(0x20 filter)
+</TD>
+<TD>
+search filter processing
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+64
+</TD>
+<TD ALIGN='Left'>
+(0x40 config)
+</TD>
+<TD>
+configuration processing
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+128
+</TD>
+<TD ALIGN='Left'>
+(0x80 ACL)
+</TD>
+<TD>
+access control list processing
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+256
+</TD>
+<TD ALIGN='Left'>
+(0x100 stats)
+</TD>
+<TD>
+stats log connections/operations/results
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+512
+</TD>
+<TD ALIGN='Left'>
+(0x200 stats2)
+</TD>
+<TD>
+stats log entries sent
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+1024
+</TD>
+<TD ALIGN='Left'>
+(0x400 shell)
+</TD>
+<TD>
+print communication with shell backends
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+2048
+</TD>
+<TD ALIGN='Left'>
+(0x800 parse)
+</TD>
+<TD>
+print entry parsing debugging
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+16384
+</TD>
+<TD ALIGN='Left'>
+(0x4000 sync)
+</TD>
+<TD>
+syncrepl consumer processing
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Right'>
+32768
+</TD>
+<TD ALIGN='Left'>
+(0x8000 none)
+</TD>
+<TD>
+only messages that get logged whatever log level is set
+</TD>
+</TR>
+</TABLE>
+
+<P>You may enable multiple levels by specifying the debug option once for each desired level. Or, since debugging levels are additive, you can do the math yourself. That is, if you want to trace function calls and watch the config file being processed, you could set level to the sum of those two levels (in this case, <TT> -d 65</TT>). Or, you can let slapd do the math, (e.g. <TT> -d 1 -d 64</TT>). Consult <TT>&lt;ldap_log.h&gt;</TT> for more details.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>slapd must have been compiled with <TT>--enable-debug</TT>, which is the default, for any debugging information other than the stats and stats2 levels to be available as options.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H2><A NAME="Starting slapd">7.2. Starting slapd</A></H2>
+<P>In general, slapd is run like this:</P>
+<PRE>
+ /usr/local/libexec/slapd [&lt;option&gt;]*
+</PRE>
+<P>where <TT>/usr/local/libexec</TT> is determined by <TT>configure</TT> and &lt;option&gt; is one of the options described above (or in <EM>slapd</EM>(8)). Unless you have specified a debugging level (including level <TT>0</TT>), slapd will automatically fork and detach itself from its controlling terminal and run in the background.</P>
+<H2><A NAME="Stopping slapd">7.3. Stopping slapd</A></H2>
+<P>To kill off <EM>slapd</EM>(8) safely, you should give a command like this</P>
+<PRE>
+ kill -INT `cat /usr/local/var/slapd.pid`
+</PRE>
+<P>where <TT>/usr/local/var</TT> is determined by <TT>configure</TT>.</P>
+<P>Killing slapd by a more drastic method may cause information loss or database corruption.</P>
+<P></P>
+<HR>
+<H1><A NAME="Access Control">8. Access Control</A></H1>
+<H2><A NAME="Introduction">8.1. Introduction</A></H2>
+<P>As the directory gets populated with more and more data of varying sensitivity, controlling the kinds of access granted to the directory becomes more and more critical. For instance, the directory may contain data of a confidential nature that you may need to protect by contract or by law. Or, if using the directory to control access to other services, inappropriate access to the directory may create avenues of attack to your sites security that result in devastating damage to your assets.</P>
+<P>Access to your directory can be configured via two methods, the first using <A HREF="#The slapd Configuration File">The slapd Configuration File</A> and the second using the <EM>slapd-config</EM>(5) format (<A HREF="#Configuring slapd">Configuring slapd</A>).</P>
+<P>The default access control policy is allow read by all clients. Regardless of what access control policy is defined, the <EM>rootdn</EM> is always allowed full rights (i.e. auth, search, compare, read and write) on everything and anything.</P>
+<P>As a consequence, it's useless (and results in a performance penalty) to explicitly list the <EM>rootdn</EM> among the <EM>&lt;by&gt;</EM> clauses.</P>
+<P>The following sections will describe Access Control Lists in greater depth and follow with some examples and recommendations. See <EM>slapd.access</EM>(5) for complete details.</P>
+<H2><A NAME="Access Control via Static Configuration">8.2. Access Control via Static Configuration</A></H2>
+<P>Access to entries and attributes is controlled by the access configuration file directive. The general form of an access line is:</P>
+<PRE>
+ &lt;access directive&gt; ::= access to &lt;what&gt;
+ [by &lt;who&gt; [&lt;access&gt;] [&lt;control&gt;] ]+
+ &lt;what&gt; ::= * |
+ [dn[.&lt;basic-style&gt;]=&lt;regex&gt; | dn.&lt;scope-style&gt;=&lt;DN&gt;]
+ [filter=&lt;ldapfilter&gt;] [attrs=&lt;attrlist&gt;]
+ &lt;basic-style&gt; ::= regex | exact
+ &lt;scope-style&gt; ::= base | one | subtree | children
+ &lt;attrlist&gt; ::= &lt;attr&gt; [val[.&lt;basic-style&gt;]=&lt;regex&gt;] | &lt;attr&gt; , &lt;attrlist&gt;
+ &lt;attr&gt; ::= &lt;attrname&gt; | entry | children
+ &lt;who&gt; ::= * | [anonymous | users | self
+ | dn[.&lt;basic-style&gt;]=&lt;regex&gt; | dn.&lt;scope-style&gt;=&lt;DN&gt;]
+ [dnattr=&lt;attrname&gt;]
+ [group[/&lt;objectclass&gt;[/&lt;attrname&gt;][.&lt;basic-style&gt;]]=&lt;regex&gt;]
+ [peername[.&lt;basic-style&gt;]=&lt;regex&gt;]
+ [sockname[.&lt;basic-style&gt;]=&lt;regex&gt;]
+ [domain[.&lt;basic-style&gt;]=&lt;regex&gt;]
+ [sockurl[.&lt;basic-style&gt;]=&lt;regex&gt;]
+ [set=&lt;setspec&gt;]
+ [aci=&lt;attrname&gt;]
+ &lt;access&gt; ::= [self]{&lt;level&gt;|&lt;priv&gt;}
+ &lt;level&gt; ::= none | disclose | auth | compare | search | read | write | manage
+ &lt;priv&gt; ::= {=|+|-}{m|w|r|s|c|x|d|0}+
+ &lt;control&gt; ::= [stop | continue | break]
+</PRE>
+<P>where the &lt;what&gt; part selects the entries and/or attributes to which the access applies, the <TT>&lt;who&gt;</TT> part specifies which entities are granted access, and the <TT>&lt;access&gt;</TT> part specifies the access granted. Multiple <TT>&lt;who&gt; &lt;access&gt; &lt;control&gt;</TT> triplets are supported, allowing many entities to be granted different access to the same set of entries and attributes. Not all of these access control options are described here; for more details see the <EM>slapd.access</EM>(5) man page.</P>
+<H3><A NAME="What to control access to">8.2.1. What to control access to</A></H3>
+<P>The &lt;what&gt; part of an access specification determines the entries and attributes to which the access control applies. Entries are commonly selected in two ways: by DN and by filter. The following qualifiers select entries by DN:</P>
+<PRE>
+ to *
+ to dn[.&lt;basic-style&gt;]=&lt;regex&gt;
+ to dn.&lt;scope-style&gt;=&lt;DN&gt;
+</PRE>
+<P>The first form is used to select all entries. The second form may be used to select entries by matching a regular expression against the target entry's <EM>normalized DN</EM>. (The second form is not discussed further in this document.) The third form is used to select entries which are within the requested scope of DN. The &lt;DN&gt; is a string representation of the Distinguished Name, as described in <A HREF="https://www.rfc-editor.org/rfc/rfc4514.txt">RFC4514</A>.</P>
+<P>The scope can be either <TT>base</TT>, <TT>one</TT>, <TT>subtree</TT>, or <TT>children</TT>. Where <TT>base</TT> matches only the entry with provided DN, <TT>one</TT> matches the entries whose parent is the provided DN, <TT>subtree</TT> matches all entries in the subtree whose root is the provided DN, and <TT>children</TT> matches all entries under the DN (but not the entry named by the DN).</P>
+<P>For example, if the directory contained entries named:</P>
+<PRE>
+ 0: o=suffix
+ 1: cn=Manager,o=suffix
+ 2: ou=people,o=suffix
+ 3: uid=kdz,ou=people,o=suffix
+ 4: cn=addresses,uid=kdz,ou=people,o=suffix
+ 5: uid=hyc,ou=people,o=suffix
+</PRE>
+<P>Then:</P>
+<UL>
+<TT>dn.base=&quot;ou=people,o=suffix&quot;</TT> match 2;
+<BR>
+<TT>dn.one=&quot;ou=people,o=suffix&quot;</TT> match 3, and 5;
+<BR>
+<TT>dn.subtree=&quot;ou=people,o=suffix&quot;</TT> match 2, 3, 4, and 5; and
+<BR>
+<TT>dn.children=&quot;ou=people,o=suffix&quot;</TT> match 3, 4, and 5.</UL>
+<P>Entries may also be selected using a filter:</P>
+<PRE>
+ to filter=&lt;ldap filter&gt;
+</PRE>
+<P>where &lt;ldap filter&gt; is a string representation of an LDAP search filter, as described in <A HREF="https://www.rfc-editor.org/rfc/rfc4515.txt">RFC4515</A>. For example:</P>
+<PRE>
+ to filter=(objectClass=person)
+</PRE>
+<P>Note that entries may be selected by both DN and filter by including both qualifiers in the &lt;what&gt; clause.</P>
+<PRE>
+ to dn.one=&quot;ou=people,o=suffix&quot; filter=(objectClass=person)
+</PRE>
+<P>Attributes within an entry are selected by including a comma-separated list of attribute names in the &lt;what&gt; selector:</P>
+<PRE>
+ attrs=&lt;attribute list&gt;
+</PRE>
+<P>A specific value of an attribute is selected by using a single attribute name and also using a value selector:</P>
+<PRE>
+ attrs=&lt;attribute&gt; val[.&lt;style&gt;]=&lt;regex&gt;
+</PRE>
+<P>There are two special <EM>pseudo</EM> attributes <TT>entry</TT> and <TT>children</TT>. To read (and hence return) a target entry, the subject must have <TT>read</TT> access to the target's <EM>entry</EM> attribute. To perform a search, the subject must have <TT>search</TT> access to the search base's <EM>entry</EM> attribute. To add or delete an entry, the subject must have <TT>write</TT> access to the entry's <TT>entry</TT> attribute AND must have <TT>write</TT> access to the entry's parent's <TT>children</TT> attribute. To rename an entry, the subject must have <TT>write</TT> access to entry's <TT>entry</TT> attribute AND have <TT>write</TT> access to both the old parent's and new parent's <TT>children</TT> attributes. The complete examples at the end of this section should help clear things up.</P>
+<P>Lastly, there is a special entry selector <TT>&quot;*&quot;</TT> that is used to select any entry. It is used when no other <TT>&lt;what&gt;</TT> selector has been provided. It's equivalent to &quot;<TT>dn=.*</TT>&quot;</P>
+<H3><A NAME="Who to grant access to">8.2.2. Who to grant access to</A></H3>
+<P>The &lt;who&gt; part identifies the entity or entities being granted access. Note that access is granted to &quot;entities&quot; not &quot;entries.&quot; The following table summarizes entity specifiers:</P>
+<TABLE CLASS="columns" BORDER ALIGN='Center'>
+<CAPTION ALIGN=top>Table 6.3: Access Entity Specifiers</CAPTION>
+<TR CLASS="heading">
+<TD>
+<STRONG>Specifier</STRONG>
+</TD>
+<TD>
+<STRONG>Entities</STRONG>
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>*</TT>
+</TD>
+<TD>
+All, including anonymous and authenticated users
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>anonymous</TT>
+</TD>
+<TD>
+Anonymous (non-authenticated) users
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>users</TT>
+</TD>
+<TD>
+Authenticated users
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>self</TT>
+</TD>
+<TD>
+User associated with target entry
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>dn[.&lt;basic-style&gt;]=&lt;regex&gt;</TT>
+</TD>
+<TD>
+Users matching a regular expression
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>dn.&lt;scope-style&gt;=&lt;DN&gt;</TT>
+</TD>
+<TD>
+Users within scope of a DN
+</TD>
+</TR>
+</TABLE>
+
+<P>The DN specifier behaves much like &lt;what&gt; clause DN specifiers.</P>
+<P>Other control factors are also supported. For example, a <TT>&lt;who&gt;</TT> can be restricted by an entry listed in a DN-valued attribute in the entry to which the access applies:</P>
+<PRE>
+ dnattr=&lt;dn-valued attribute name&gt;
+</PRE>
+<P>The dnattr specification is used to give access to an entry whose DN is listed in an attribute of the entry (e.g., give access to a group entry to whoever is listed as the owner of the group entry).</P>
+<P>Some factors may not be appropriate in all environments (or any). For example, the domain factor relies on IP to domain name lookups. As these can easily be spoofed, the domain factor should be avoided.</P>
+<H3><A NAME="The access to grant">8.2.3. The access to grant</A></H3>
+<P>The kind of &lt;access&gt; granted can be one of the following:</P>
+<TABLE CLASS="columns" BORDER ALIGN='Center'>
+<CAPTION ALIGN=top>Table 6.4: Access Levels</CAPTION>
+<TR CLASS="heading">
+<TD ALIGN='Left'>
+<STRONG>Level</STRONG>
+</TD>
+<TD ALIGN='Right'>
+<STRONG>Privileges</STRONG>
+</TD>
+<TD ALIGN='Left'>
+<STRONG>Description</STRONG>
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>none =</TT>
+</TD>
+<TD ALIGN='Right'>
+<TT>0</TT>
+</TD>
+<TD ALIGN='Left'>
+no access
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>disclose =</TT>
+</TD>
+<TD ALIGN='Right'>
+<TT>d</TT>
+</TD>
+<TD ALIGN='Left'>
+needed for information disclosure on error
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>auth =</TT>
+</TD>
+<TD ALIGN='Right'>
+<TT>dx</TT>
+</TD>
+<TD ALIGN='Left'>
+needed to authenticate (bind)
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>compare =</TT>
+</TD>
+<TD ALIGN='Right'>
+<TT>cdx</TT>
+</TD>
+<TD ALIGN='Left'>
+needed to compare
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>search =</TT>
+</TD>
+<TD ALIGN='Right'>
+<TT>scdx</TT>
+</TD>
+<TD ALIGN='Left'>
+needed to apply search filters
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>read =</TT>
+</TD>
+<TD ALIGN='Right'>
+<TT>rscdx</TT>
+</TD>
+<TD ALIGN='Left'>
+needed to read search results
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>write =</TT>
+</TD>
+<TD ALIGN='Right'>
+<TT>wrscdx</TT>
+</TD>
+<TD ALIGN='Left'>
+needed to modify/rename
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>manage =</TT>
+</TD>
+<TD ALIGN='Right'>
+<TT>mwrscdx</TT>
+</TD>
+<TD ALIGN='Left'>
+needed to manage
+</TD>
+</TR>
+</TABLE>
+
+<P>Each level implies all lower levels of access. So, for example, granting someone <TT>write</TT> access to an entry also grants them <TT>read</TT>, <TT>search</TT>, <TT>compare</TT>, <TT>auth</TT> and <TT>disclose</TT> access. However, one may use the privileges specifier to grant specific permissions.</P>
+<H3><A NAME="Access Control Evaluation">8.2.4. Access Control Evaluation</A></H3>
+<P>When evaluating whether some requester should be given access to an entry and/or attribute, slapd compares the entry and/or attribute to the <TT>&lt;what&gt;</TT> selectors given in the configuration file. For each entry, access controls provided in the database which holds the entry (or the global access directives if not held in any database) apply first, followed by the global access directives. However, when dealing with an access list, because the global access list is effectively appended to each per-database list, if the resulting list is non-empty then the access list will end with an implicit <TT>access to * by * none</TT> directive. If there are no access directives applicable to a backend, then a default read is used.</P>
+<P>Within this priority, access directives are examined in the order in which they appear in the config file. Slapd stops with the first <TT>&lt;what&gt;</TT> selector that matches the entry and/or attribute. The corresponding access directive is the one slapd will use to evaluate access.</P>
+<P>Next, slapd compares the entity requesting access to the <TT>&lt;who&gt;</TT> selectors within the access directive selected above in the order in which they appear. It stops with the first <TT>&lt;who&gt;</TT> selector that matches the requester. This determines the access the entity requesting access has to the entry and/or attribute.</P>
+<P>Finally, slapd compares the access granted in the selected <TT>&lt;access&gt;</TT> clause to the access requested by the client. If it allows greater or equal access, access is granted. Otherwise, access is denied.</P>
+<P>The order of evaluation of access directives makes their placement in the configuration file important. If one access directive is more specific than another in terms of the entries it selects, it should appear first in the config file. Similarly, if one <TT>&lt;who&gt;</TT> selector is more specific than another it should come first in the access directive. The access control examples given below should help make this clear.</P>
+<H3><A NAME="Access Control Examples">8.2.5. Access Control Examples</A></H3>
+<P>The access control facility described above is quite powerful. This section shows some examples of its use for descriptive purposes.</P>
+<P>A simple example:</P>
+<PRE>
+ access to * by * read
+</PRE>
+<P>This access directive grants read access to everyone.</P>
+<PRE>
+ access to *
+ by self write
+ by anonymous auth
+ by * read
+</PRE>
+<P>This directive allows the user to modify their entry, allows anonymous to authenticate against these entries, and allows all others to read these entries. Note that only the first <TT>by &lt;who&gt;</TT> clause which matches applies. Hence, the anonymous users are granted <TT>auth</TT>, not <TT>read</TT>. The last clause could just as well have been &quot;<TT>by users read</TT>&quot;.</P>
+<P>It is often desirable to restrict operations based upon the level of protection in place. The following shows how security strength factors (SSF) can be used.</P>
+<PRE>
+ access to *
+ by ssf=128 self write
+ by ssf=64 anonymous auth
+ by ssf=64 users read
+</PRE>
+<P>This directive allows users to modify their own entries if security protections have of strength 128 or better have been established, allows authentication access to anonymous users, and read access when 64 or better security protections have been established. If client has not establish sufficient security protections, the implicit <TT>by * none</TT> clause would be applied.</P>
+<P>The following example shows the use of a style specifiers to select the entries by DN in two access directives where ordering is significant.</P>
+<PRE>
+ access to dn.children=&quot;dc=example,dc=com&quot;
+ by * search
+ access to dn.children=&quot;dc=com&quot;
+ by * read
+</PRE>
+<P>Read access is granted to entries under the <TT>dc=com</TT> subtree, except for those entries under the <TT>dc=example,dc=com</TT> subtree, to which search access is granted. No access is granted to <TT>dc=com</TT> as neither access directive matches this DN. If the order of these access directives was reversed, the trailing directive would never be reached, since all entries under <TT>dc=example,dc=com</TT> are also under <TT>dc=com</TT> entries.</P>
+<P>Also note that if no <TT>access to</TT> directive matches or no <TT>by &lt;who&gt;</TT> clause, <B>access is denied</B>. That is, every <TT>access to</TT> directive ends with an implicit <TT>by * none</TT> clause. When dealing with an access list, because the global access list is effectively appended to each per-database list, if the resulting list is non-empty then the access list will end with an implicit <TT>access to * by * none</TT> directive. If there are no access directives applicable to a backend, then a default read is used.</P>
+<P>The next example again shows the importance of ordering, both of the access directives and the <TT>by &lt;who&gt;</TT> clauses. It also shows the use of an attribute selector to grant access to a specific attribute and various <TT>&lt;who&gt;</TT> selectors.</P>
+<PRE>
+ access to dn.subtree=&quot;dc=example,dc=com&quot; attrs=homePhone
+ by self write
+ by dn.children=&quot;dc=example,dc=com&quot; search
+ by peername.regex=IP=10\..+ read
+ access to dn.subtree=&quot;dc=example,dc=com&quot;
+ by self write
+ by dn.children=&quot;dc=example,dc=com&quot; search
+ by anonymous auth
+</PRE>
+<P>This example applies to entries in the &quot;<TT>dc=example,dc=com</TT>&quot; subtree. To all attributes except <TT>homePhone</TT>, an entry can write to itself, entries under <TT>example.com</TT> entries can search by them, anybody else has no access (implicit <TT>by * none</TT>) excepting for authentication/authorization (which is always done anonymously). The <TT>homePhone</TT> attribute is writable by the entry, searchable by entries under <TT>example.com</TT>, readable by clients connecting from network 10, and otherwise not readable (implicit <TT>by * none</TT>). All other access is denied by the implicit <TT>access to * by * none</TT>.</P>
+<P>Sometimes it is useful to permit a particular DN to add or remove itself from an attribute. For example, if you would like to create a group and allow people to add and remove only their own DN from the member attribute, you could accomplish it with an access directive like this:</P>
+<PRE>
+ access to attrs=member,entry
+ by dnattr=member selfwrite
+</PRE>
+<P>The dnattr <TT>&lt;who&gt;</TT> selector says that the access applies to entries listed in the <TT>member</TT> attribute. The <TT>selfwrite</TT> access selector says that such members can only add or delete their own DN from the attribute, not other values. The addition of the entry attribute is required because access to the entry is required to access any of the entry's attributes.</P>
+<H2><A NAME="Access Control via Dynamic Configuration">8.3. Access Control via Dynamic Configuration</A></H2>
+<P>Access to slapd entries and attributes is controlled by the olcAccess attribute, whose values are a sequence of access directives. The general form of the olcAccess configuration is:</P>
+<PRE>
+ olcAccess: &lt;access directive&gt;
+ &lt;access directive&gt; ::= to &lt;what&gt;
+ [by &lt;who&gt; [&lt;access&gt;] [&lt;control&gt;] ]+
+ &lt;what&gt; ::= * |
+ [dn[.&lt;basic-style&gt;]=&lt;regex&gt; | dn.&lt;scope-style&gt;=&lt;DN&gt;]
+ [filter=&lt;ldapfilter&gt;] [attrs=&lt;attrlist&gt;]
+ &lt;basic-style&gt; ::= regex | exact
+ &lt;scope-style&gt; ::= base | one | subtree | children
+ &lt;attrlist&gt; ::= &lt;attr&gt; [val[.&lt;basic-style&gt;]=&lt;regex&gt;] | &lt;attr&gt; , &lt;attrlist&gt;
+ &lt;attr&gt; ::= &lt;attrname&gt; | entry | children
+ &lt;who&gt; ::= * | [anonymous | users | self
+ | dn[.&lt;basic-style&gt;]=&lt;regex&gt; | dn.&lt;scope-style&gt;=&lt;DN&gt;]
+ [dnattr=&lt;attrname&gt;]
+ [group[/&lt;objectclass&gt;[/&lt;attrname&gt;][.&lt;basic-style&gt;]]=&lt;regex&gt;]
+ [peername[.&lt;basic-style&gt;]=&lt;regex&gt;]
+ [sockname[.&lt;basic-style&gt;]=&lt;regex&gt;]
+ [domain[.&lt;basic-style&gt;]=&lt;regex&gt;]
+ [sockurl[.&lt;basic-style&gt;]=&lt;regex&gt;]
+ [set=&lt;setspec&gt;]
+ [aci=&lt;attrname&gt;]
+ &lt;access&gt; ::= [self]{&lt;level&gt;|&lt;priv&gt;}
+ &lt;level&gt; ::= none | disclose | auth | compare | search | read | write | manage
+ &lt;priv&gt; ::= {=|+|-}{m|w|r|s|c|x|d|0}+
+ &lt;control&gt; ::= [stop | continue | break]
+</PRE>
+<P>where the &lt;what&gt; part selects the entries and/or attributes to which the access applies, the <TT>&lt;who&gt;</TT> part specifies which entities are granted access, and the <TT>&lt;access&gt;</TT> part specifies the access granted. Multiple <TT>&lt;who&gt; &lt;access&gt; &lt;control&gt;</TT> triplets are supported, allowing many entities to be granted different access to the same set of entries and attributes. Not all of these access control options are described here; for more details see the <EM>slapd.access</EM>(5) man page.</P>
+<H3><A NAME="What to control access to">8.3.1. What to control access to</A></H3>
+<P>The &lt;what&gt; part of an access specification determines the entries and attributes to which the access control applies. Entries are commonly selected in two ways: by DN and by filter. The following qualifiers select entries by DN:</P>
+<PRE>
+ to *
+ to dn[.&lt;basic-style&gt;]=&lt;regex&gt;
+ to dn.&lt;scope-style&gt;=&lt;DN&gt;
+</PRE>
+<P>The first form is used to select all entries. The second form may be used to select entries by matching a regular expression against the target entry's <EM>normalized DN</EM>. (The second form is not discussed further in this document.) The third form is used to select entries which are within the requested scope of DN. The &lt;DN&gt; is a string representation of the Distinguished Name, as described in <A HREF="https://www.rfc-editor.org/rfc/rfc4514.txt">RFC4514</A>.</P>
+<P>The scope can be either <TT>base</TT>, <TT>one</TT>, <TT>subtree</TT>, or <TT>children</TT>. Where <TT>base</TT> matches only the entry with provided DN, <TT>one</TT> matches the entries whose parent is the provided DN, <TT>subtree</TT> matches all entries in the subtree whose root is the provided DN, and <TT>children</TT> matches all entries under the DN (but not the entry named by the DN).</P>
+<P>For example, if the directory contained entries named:</P>
+<PRE>
+ 0: o=suffix
+ 1: cn=Manager,o=suffix
+ 2: ou=people,o=suffix
+ 3: uid=kdz,ou=people,o=suffix
+ 4: cn=addresses,uid=kdz,ou=people,o=suffix
+ 5: uid=hyc,ou=people,o=suffix
+</PRE>
+<P>Then:</P>
+<UL>
+<TT>dn.base=&quot;ou=people,o=suffix&quot;</TT> match 2;
+<BR>
+<TT>dn.one=&quot;ou=people,o=suffix&quot;</TT> match 3, and 5;
+<BR>
+<TT>dn.subtree=&quot;ou=people,o=suffix&quot;</TT> match 2, 3, 4, and 5; and
+<BR>
+<TT>dn.children=&quot;ou=people,o=suffix&quot;</TT> match 3, 4, and 5.</UL>
+<P>Entries may also be selected using a filter:</P>
+<PRE>
+ to filter=&lt;ldap filter&gt;
+</PRE>
+<P>where &lt;ldap filter&gt; is a string representation of an LDAP search filter, as described in <A HREF="https://www.rfc-editor.org/rfc/rfc4515.txt">RFC4515</A>. For example:</P>
+<PRE>
+ to filter=(objectClass=person)
+</PRE>
+<P>Note that entries may be selected by both DN and filter by including both qualifiers in the &lt;what&gt; clause.</P>
+<PRE>
+ to dn.one=&quot;ou=people,o=suffix&quot; filter=(objectClass=person)
+</PRE>
+<P>Attributes within an entry are selected by including a comma-separated list of attribute names in the &lt;what&gt; selector:</P>
+<PRE>
+ attrs=&lt;attribute list&gt;
+</PRE>
+<P>A specific value of an attribute is selected by using a single attribute name and also using a value selector:</P>
+<PRE>
+ attrs=&lt;attribute&gt; val[.&lt;style&gt;]=&lt;regex&gt;
+</PRE>
+<P>There are two special <EM>pseudo</EM> attributes <TT>entry</TT> and <TT>children</TT>. To read (and hence return) a target entry, the subject must have <TT>read</TT> access to the target's <EM>entry</EM> attribute. To perform a search, the subject must have <TT>search</TT> access to the search base's <EM>entry</EM> attribute. To add or delete an entry, the subject must have <TT>write</TT> access to the entry's <TT>entry</TT> attribute AND must have <TT>write</TT> access to the entry's parent's <TT>children</TT> attribute. To rename an entry, the subject must have <TT>write</TT> access to entry's <TT>entry</TT> attribute AND have <TT>write</TT> access to both the old parent's and new parent's <TT>children</TT> attributes. The complete examples at the end of this section should help clear things up.</P>
+<P>Lastly, there is a special entry selector <TT>&quot;*&quot;</TT> that is used to select any entry. It is used when no other <TT>&lt;what&gt;</TT> selector has been provided. It's equivalent to &quot;<TT>dn=.*</TT>&quot;</P>
+<H3><A NAME="Who to grant access to">8.3.2. Who to grant access to</A></H3>
+<P>The &lt;who&gt; part identifies the entity or entities being granted access. Note that access is granted to &quot;entities&quot; not &quot;entries.&quot; The following table summarizes entity specifiers:</P>
+<TABLE CLASS="columns" BORDER ALIGN='Center'>
+<CAPTION ALIGN=top>Table 5.3: Access Entity Specifiers</CAPTION>
+<TR CLASS="heading">
+<TD>
+<STRONG>Specifier</STRONG>
+</TD>
+<TD>
+<STRONG>Entities</STRONG>
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>*</TT>
+</TD>
+<TD>
+All, including anonymous and authenticated users
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>anonymous</TT>
+</TD>
+<TD>
+Anonymous (non-authenticated) users
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>users</TT>
+</TD>
+<TD>
+Authenticated users
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>self</TT>
+</TD>
+<TD>
+User associated with target entry
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>dn[.&lt;basic-style&gt;]=&lt;regex&gt;</TT>
+</TD>
+<TD>
+Users matching a regular expression
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>dn.&lt;scope-style&gt;=&lt;DN&gt;</TT>
+</TD>
+<TD>
+Users within scope of a DN
+</TD>
+</TR>
+</TABLE>
+
+<P>The DN specifier behaves much like &lt;what&gt; clause DN specifiers.</P>
+<P>Other control factors are also supported. For example, a <TT>&lt;who&gt;</TT> can be restricted by an entry listed in a DN-valued attribute in the entry to which the access applies:</P>
+<PRE>
+ dnattr=&lt;dn-valued attribute name&gt;
+</PRE>
+<P>The dnattr specification is used to give access to an entry whose DN is listed in an attribute of the entry (e.g., give access to a group entry to whoever is listed as the owner of the group entry).</P>
+<P>Some factors may not be appropriate in all environments (or any). For example, the domain factor relies on IP to domain name lookups. As these can easily be spoofed, the domain factor should be avoided.</P>
+<H3><A NAME="The access to grant">8.3.3. The access to grant</A></H3>
+<P>The kind of &lt;access&gt; granted can be one of the following:</P>
+<TABLE CLASS="columns" BORDER ALIGN='Center'>
+<CAPTION ALIGN=top>Table 5.4: Access Levels</CAPTION>
+<TR CLASS="heading">
+<TD ALIGN='Left'>
+<STRONG>Level</STRONG>
+</TD>
+<TD ALIGN='Right'>
+<STRONG>Privileges</STRONG>
+</TD>
+<TD ALIGN='Left'>
+<STRONG>Description</STRONG>
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>none</TT>
+</TD>
+<TD ALIGN='Right'>
+<TT>=0</TT>
+</TD>
+<TD ALIGN='Left'>
+no access
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>disclose</TT>
+</TD>
+<TD ALIGN='Right'>
+<TT>=d</TT>
+</TD>
+<TD ALIGN='Left'>
+needed for information disclosure on error
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>auth</TT>
+</TD>
+<TD ALIGN='Right'>
+<TT>=dx</TT>
+</TD>
+<TD ALIGN='Left'>
+needed to authenticate (bind)
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>compare</TT>
+</TD>
+<TD ALIGN='Right'>
+<TT>=cdx</TT>
+</TD>
+<TD ALIGN='Left'>
+needed to compare
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>search</TT>
+</TD>
+<TD ALIGN='Right'>
+<TT>=scdx</TT>
+</TD>
+<TD ALIGN='Left'>
+needed to apply search filters
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>read</TT>
+</TD>
+<TD ALIGN='Right'>
+<TT>=rscdx</TT>
+</TD>
+<TD ALIGN='Left'>
+needed to read search results
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>write</TT>
+</TD>
+<TD ALIGN='Right'>
+<TT>=wrscdx</TT>
+</TD>
+<TD ALIGN='Left'>
+needed to modify/rename
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>manage</TT>
+</TD>
+<TD ALIGN='Right'>
+<TT>=mwrscdx</TT>
+</TD>
+<TD ALIGN='Left'>
+needed to manage
+</TD>
+</TR>
+</TABLE>
+
+<P>Each level implies all lower levels of access. So, for example, granting someone <TT>write</TT> access to an entry also grants them <TT>read</TT>, <TT>search</TT>, <TT>compare</TT>, <TT>auth</TT> and <TT>disclose</TT> access. However, one may use the privileges specifier to grant specific permissions.</P>
+<H3><A NAME="Access Control Evaluation">8.3.4. Access Control Evaluation</A></H3>
+<P>When evaluating whether some requester should be given access to an entry and/or attribute, slapd compares the entry and/or attribute to the <TT>&lt;what&gt;</TT> selectors given in the configuration. For each entry, access controls provided in the database which holds the entry (or the global access directives if not held in any database) apply first, followed by the global access directives (which are held in the <TT>frontend</TT> database definition). However, when dealing with an access list, because the global access list is effectively appended to each per-database list, if the resulting list is non-empty then the access list will end with an implicit <TT>access to * by * none</TT> directive. If there are no access directives applicable to a backend, then a default read is used.</P>
+<P>Within this priority, access directives are examined in the order in which they appear in the configuration attribute. Slapd stops with the first <TT>&lt;what&gt;</TT> selector that matches the entry and/or attribute. The corresponding access directive is the one slapd will use to evaluate access.</P>
+<P>Next, slapd compares the entity requesting access to the <TT>&lt;who&gt;</TT> selectors within the access directive selected above in the order in which they appear. It stops with the first <TT>&lt;who&gt;</TT> selector that matches the requester. This determines the access the entity requesting access has to the entry and/or attribute.</P>
+<P>Finally, slapd compares the access granted in the selected <TT>&lt;access&gt;</TT> clause to the access requested by the client. If it allows greater or equal access, access is granted. Otherwise, access is denied.</P>
+<P>The order of evaluation of access directives makes their placement in the configuration file important. If one access directive is more specific than another in terms of the entries it selects, it should appear first in the configuration. Similarly, if one <TT>&lt;who&gt;</TT> selector is more specific than another it should come first in the access directive. The access control examples given below should help make this clear.</P>
+<H3><A NAME="Access Control Examples">8.3.5. Access Control Examples</A></H3>
+<P>The access control facility described above is quite powerful. This section shows some examples of its use for descriptive purposes.</P>
+<P>A simple example:</P>
+<PRE>
+ olcAccess: to * by * read
+</PRE>
+<P>This access directive grants read access to everyone.</P>
+<PRE>
+ olcAccess: to *
+ by self write
+ by anonymous auth
+ by * read
+</PRE>
+<P>This directive allows the user to modify their entry, allows anonymous to authenticate against these entries, and allows all others to read these entries. Note that only the first <TT>by &lt;who&gt;</TT> clause which matches applies. Hence, the anonymous users are granted <TT>auth</TT>, not <TT>read</TT>. The last clause could just as well have been &quot;<TT>by users read</TT>&quot;.</P>
+<P>It is often desirable to restrict operations based upon the level of protection in place. The following shows how security strength factors (SSF) can be used.</P>
+<PRE>
+ olcAccess: to *
+ by ssf=128 self write
+ by ssf=64 anonymous auth
+ by ssf=64 users read
+</PRE>
+<P>This directive allows users to modify their own entries if security protections of strength 128 or better have been established, allows authentication access to anonymous users, and read access when strength 64 or better security protections have been established. If the client has not establish sufficient security protections, the implicit <TT>by * none</TT> clause would be applied.</P>
+<P>The following example shows the use of style specifiers to select the entries by DN in two access directives where ordering is significant.</P>
+<PRE>
+ olcAccess: to dn.children=&quot;dc=example,dc=com&quot;
+ by * search
+ olcAccess: to dn.children=&quot;dc=com&quot;
+ by * read
+</PRE>
+<P>Read access is granted to entries under the <TT>dc=com</TT> subtree, except for those entries under the <TT>dc=example,dc=com</TT> subtree, to which search access is granted. No access is granted to <TT>dc=com</TT> as neither access directive matches this DN. If the order of these access directives was reversed, the trailing directive would never be reached, since all entries under <TT>dc=example,dc=com</TT> are also under <TT>dc=com</TT> entries.</P>
+<P>Also note that if no <TT>olcAccess: to</TT> directive matches or no <TT>by &lt;who&gt;</TT> clause, <B>access is denied</B>. When dealing with an access list, because the global access list is effectively appended to each per-database list, if the resulting list is non-empty then the access list will end with an implicit <TT>access to * by * none</TT> directive. If there are no access directives applicable to a backend, then a default read is used.</P>
+<P>The next example again shows the importance of ordering, both of the access directives and the <TT>by &lt;who&gt;</TT> clauses. It also shows the use of an attribute selector to grant access to a specific attribute and various <TT>&lt;who&gt;</TT> selectors.</P>
+<PRE>
+ olcAccess: to dn.subtree=&quot;dc=example,dc=com&quot; attrs=homePhone
+ by self write
+ by dn.children=dc=example,dc=com&quot; search
+ by peername.regex=IP=10\..+ read
+ olcAccess: to dn.subtree=&quot;dc=example,dc=com&quot;
+ by self write
+ by dn.children=&quot;dc=example,dc=com&quot; search
+ by anonymous auth
+</PRE>
+<P>This example applies to entries in the &quot;<TT>dc=example,dc=com</TT>&quot; subtree. To all attributes except <TT>homePhone</TT>, an entry can write to itself, entries under <TT>example.com</TT> entries can search by them, anybody else has no access (implicit <TT>by * none</TT>) excepting for authentication/authorization (which is always done anonymously). The <TT>homePhone</TT> attribute is writable by the entry, searchable by entries under <TT>example.com</TT>, readable by clients connecting from network 10, and otherwise not readable (implicit <TT>by * none</TT>). All other access is denied by the implicit <TT>access to * by * none</TT>.</P>
+<P>Sometimes it is useful to permit a particular DN to add or remove itself from an attribute. For example, if you would like to create a group and allow people to add and remove only their own DN from the member attribute, you could accomplish it with an access directive like this:</P>
+<PRE>
+ olcAccess: to attrs=member,entry
+ by dnattr=member selfwrite
+</PRE>
+<P>The dnattr <TT>&lt;who&gt;</TT> selector says that the access applies to entries listed in the <TT>member</TT> attribute. The <TT>selfwrite</TT> access selector says that such members can only add or delete their own DN from the attribute, not other values. The addition of the entry attribute is required because access to the entry is required to access any of the entry's attributes.</P>
+<H3><A NAME="Access Control Ordering">8.3.6. Access Control Ordering</A></H3>
+<P>Since the ordering of <TT>olcAccess</TT> directives is essential to their proper evaluation, but LDAP attributes normally do not preserve the ordering of their values, OpenLDAP uses a custom schema extension to maintain a fixed ordering of these values. This ordering is maintained by prepending a <TT>&quot;{X}&quot;</TT> numeric index to each value, similarly to the approach used for ordering the configuration entries. These index tags are maintained automatically by slapd and do not need to be specified when originally defining the values. For example, when you create the settings</P>
+<PRE>
+ olcAccess: to attrs=member,entry
+ by dnattr=member selfwrite
+ olcAccess: to dn.children=&quot;dc=example,dc=com&quot;
+ by * search
+ olcAccess: to dn.children=&quot;dc=com&quot;
+ by * read
+</PRE>
+<P>when you read them back using slapcat or ldapsearch they will contain</P>
+<PRE>
+ olcAccess: {0}to attrs=member,entry
+ by dnattr=member selfwrite
+ olcAccess: {1}to dn.children=&quot;dc=example,dc=com&quot;
+ by * search
+ olcAccess: {2}to dn.children=&quot;dc=com&quot;
+ by * read
+</PRE>
+<P>The numeric index may be used to specify a particular value to change when using ldapmodify to edit the access rules. This index can be used instead of (or in addition to) the actual access value. Using this numeric index is very helpful when multiple access rules are being managed.</P>
+<P>For example, if we needed to change the second rule above to grant write access instead of search, we could try this LDIF:</P>
+<PRE>
+ changetype: modify
+ delete: olcAccess
+ olcAccess: to dn.children=&quot;dc=example,dc=com&quot; by * search
+ -
+ add: olcAccess
+ olcAccess: to dn.children=&quot;dc=example,dc=com&quot; by * write
+ -
+</PRE>
+<P>But this example <B>will not</B> guarantee that the existing values remain in their original order, so it will most likely yield a broken security configuration. Instead, the numeric index should be used:</P>
+<PRE>
+ changetype: modify
+ delete: olcAccess
+ olcAccess: {1}
+ -
+ add: olcAccess
+ olcAccess: {1}to dn.children=&quot;dc=example,dc=com&quot; by * write
+ -
+</PRE>
+<P>This example deletes whatever rule is in value #1 of the <TT>olcAccess</TT> attribute (regardless of its value) and adds a new value that is explicitly inserted as value #1. The result will be</P>
+<PRE>
+ olcAccess: {0}to attrs=member,entry
+ by dnattr=member selfwrite
+ olcAccess: {1}to dn.children=&quot;dc=example,dc=com&quot;
+ by * write
+ olcAccess: {2}to dn.children=&quot;dc=com&quot;
+ by * read
+</PRE>
+<P>which is exactly what was intended.</P>
+<H2><A NAME="Access Control Common Examples">8.4. Access Control Common Examples</A></H2>
+<H3><A NAME="Basic ACLs">8.4.1. Basic ACLs</A></H3>
+<P>Generally one should start with some basic ACLs such as:</P>
+<PRE>
+ access to attrs=userPassword
+ by self =xw
+ by anonymous auth
+ by * none
+
+
+ access to *
+ by self write
+ by users read
+ by * none
+</PRE>
+<P>The first ACL allows users to update (but not read) their passwords, anonymous users to authenticate against this attribute, and (implicitly) denying all access to others.</P>
+<P>The second ACL allows users full access to their entry, authenticated users read access to anything, and (implicitly) denying all access to others (in this case, anonymous users).</P>
+<H3><A NAME="Matching Anonymous and Authenticated users">8.4.2. Matching Anonymous and Authenticated users</A></H3>
+<P>An anonymous user has a empty DN. While the <EM>dn.exact=&quot;&quot;</EM> or <EM>dn.regex=&quot;^$&quot;</EM> could be used, <EM>slapd</EM>(8)) offers an anonymous shorthand which should be used instead.</P>
+<PRE>
+ access to *
+ by anonymous none
+ by * read
+</PRE>
+<P>denies all access to anonymous users while granting others read.</P>
+<P>Authenticated users have a subject DN. While <EM>dn.regex=&quot;.+&quot;</EM> will match any authenticated user, OpenLDAP provides the users short hand which should be used instead.</P>
+<PRE>
+ access to *
+ by users read
+ by * none
+</PRE>
+<P>This ACL grants read permissions to authenticated users while denying others (i.e.: anonymous users).</P>
+<H3><A NAME="Controlling rootdn access">8.4.3. Controlling rootdn access</A></H3>
+<P>You could specify the <EM>rootdn</EM> in <EM>slapd.conf</EM>(5) or <EM>slapd.d</EM> without specifying a <EM>rootpw</EM>. Then you have to add an actual directory entry with the same dn, e.g.:</P>
+<PRE>
+ dn: cn=Manager,o=MyOrganization
+ cn: Manager
+ sn: Manager
+ objectClass: person
+ objectClass: top
+ userPassword: {SSHA}someSSHAdata
+</PRE>
+<P>Then binding as the <EM>rootdn</EM> will require a regular bind to that DN, which in turn requires auth access to that entry's DN and <EM>userPassword</EM>, and this can be restricted via ACLs. E.g.:</P>
+<PRE>
+ access to dn.base=&quot;cn=Manager,o=MyOrganization&quot;
+ by peername.regex=127\.0\.0\.1 auth
+ by peername.regex=192\.168\.0\..* auth
+ by users none
+ by * none
+</PRE>
+<P>The ACLs above will only allow binding using rootdn from localhost and 192.168.0.0/24.</P>
+<H3><A NAME="Managing access with Groups">8.4.4. Managing access with Groups</A></H3>
+<P>There are a few ways to do this. One approach is illustrated here. Consider the following DIT layout:</P>
+<PRE>
+ +-dc=example,dc=com
+ +---cn=administrators,dc=example,dc=com
+ +---cn=fred blogs,dc=example,dc=com
+</PRE>
+<P>and the following group object (in LDIF format):</P>
+<PRE>
+ dn: cn=administrators,dc=example,dc=com
+ cn: administrators of this region
+ objectclass: groupOfNames (important for the group acl feature)
+ member: cn=fred blogs,dc=example,dc=com
+ member: cn=somebody else,dc=example,dc=com
+</PRE>
+<P>One can then grant access to the members of this this group by adding appropriate <EM>by group</EM> clause to an access directive in <EM>slapd.conf</EM>(5). For instance,</P>
+<PRE>
+ access to dn.children=&quot;dc=example,dc=com&quot;
+ by self write
+ by group.exact=&quot;cn=Administrators,dc=example,dc=com&quot; write
+ by * auth
+</PRE>
+<P>Like by <EM>dn</EM> clauses, one can also use <EM>expand</EM> to expand the group name based upon the regular expression matching of the target, that is, the to <EM>dn.regex</EM>). For instance,</P>
+<PRE>
+ access to dn.regex=&quot;(.+,)?ou=People,(dc=[^,]+,dc=[^,]+)$&quot;
+ attrs=children,entry,uid
+ by group.expand=&quot;cn=Managers,$2&quot; write
+ by users read
+ by * auth
+</PRE>
+<P>The above illustration assumed that the group members are to be found in the <EM>member</EM> attribute type of the <EM>groupOfNames</EM> object class. If you need to use a different group object and/or a different attribute type then use the following <EM>slapd.conf</EM>(5) (abbreviated) syntax:</P>
+<PRE>
+ access to &lt;what&gt;
+ by group/&lt;objectclass&gt;/&lt;attributename&gt;=&lt;DN&gt; &lt;access&gt;
+</PRE>
+<P>For example:</P>
+<PRE>
+ access to *
+ by group/organizationalRole/roleOccupant=&quot;cn=Administrator,dc=example,dc=com&quot; write
+</PRE>
+<P>In this case, we have an ObjectClass <EM>organizationalRole</EM> which contains the administrator DN's in the <EM>roleOccupant</EM> attribute. For instance:</P>
+<PRE>
+ dn: cn=Administrator,dc=example,dc=com
+ cn: Administrator
+ objectclass: organizationalRole
+ roleOccupant: cn=Jane Doe,dc=example,dc=com
+</PRE>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>the specified member attribute type MUST be of DN or <EM>NameAndOptionalUID</EM> syntax, and the specified object class SHOULD allow the attribute type.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>Dynamic Groups are also supported in Access Control. Please see <EM>slapo-dynlist</EM>(5) and the <A HREF="#Dynamic Lists">Dynamic Lists</A> overlay section.</P>
+<H3><A NAME="Granting access to a subset of attributes">8.4.5. Granting access to a subset of attributes</A></H3>
+<P>You can grant access to a set of attributes by specifying a list of attribute names in the ACL <EM>to</EM> clause. To be useful, you also need to grant access to the <EM>entry</EM> itself. Also note how <EM>children</EM> controls the ability to add, delete, and rename entries.</P>
+<PRE>
+ # mail: self may write, authenticated users may read
+ access to attrs=mail
+ by self write
+ by users read
+ by * none
+
+ # cn, sn: self my write, all may read
+ access to attrs=cn,sn
+ by self write
+ by * read
+
+ # immediate children: only self can add/delete entries under this entry
+ access to attrs=children
+ by self write
+
+ # entry itself: self may write, all may read
+ access to attrs=entry
+ by self write
+ by * read
+
+ # other attributes: self may write, others have no access
+ access to *
+ by self write
+ by * none
+</PRE>
+<P>ObjectClass names may also be specified in this list, which will affect all the attributes that are required and/or allowed by that <EM>objectClass</EM>. Actually, names in <EM>attrlist</EM> that are prefixed by <EM>@</EM> are directly treated as objectClass names. A name prefixed by <EM>!</EM> is also treated as an objectClass, but in this case the access rule affects the attributes that are not required nor allowed by that <EM>objectClass</EM>.</P>
+<H3><A NAME="Allowing a user write to all entries below theirs">8.4.6. Allowing a user write to all entries below theirs</A></H3>
+<P>For a setup where a user can write to its own record and to all of its children:</P>
+<PRE>
+ access to dn.regex=&quot;(.+,)?(uid=[^,]+,o=Company)$&quot;
+ by dn.exact,expand=&quot;$2&quot; write
+ by anonymous auth
+</PRE>
+<P>(Add more examples for above)</P>
+<H3><A NAME="Allowing entry creation">8.4.7. Allowing entry creation</A></H3>
+<P>Let's say, you have it like this:</P>
+<PRE>
+ o=&lt;basedn&gt;
+ ou=domains
+ associatedDomain=&lt;somedomain&gt;
+ ou=users
+ uid=&lt;someuserid&gt;
+ uid=&lt;someotheruserid&gt;
+ ou=addressbooks
+ uid=&lt;someuserid&gt;
+ cn=&lt;someone&gt;
+ cn=&lt;someoneelse&gt;
+</PRE>
+<P>and, for another domain &lt;someotherdomain&gt;:</P>
+<PRE>
+ o=&lt;basedn&gt;
+ ou=domains
+ associatedDomain=&lt;someotherdomain&gt;
+ ou=users
+ uid=&lt;someuserid&gt;
+ uid=&lt;someotheruserid&gt;
+ ou=addressbooks
+ uid=&lt;someotheruserid&gt;
+ cn=&lt;someone&gt;
+ cn=&lt;someoneelse&gt;
+</PRE>
+<P>then, if you wanted user <EM>uid=&lt;someuserid&gt;</EM> to <B>ONLY</B> create an entry for its own thing, you could write an ACL like this:</P>
+<PRE>
+ # this rule lets users of &quot;associatedDomain=&lt;matcheddomain&gt;&quot;
+ # write under &quot;ou=addressbook,associatedDomain=&lt;matcheddomain&gt;,ou=domains,o=&lt;basedn&gt;&quot;,
+ # i.e. a user can write ANY entry below its domain's address book;
+ # this permission is necessary, but not sufficient, the next
+ # will restrict this permission further
+
+
+ access to dn.regex=&quot;^ou=addressbook,associatedDomain=([^,]+),ou=domains,o=&lt;basedn&gt;$&quot; attrs=children
+ by dn.regex=&quot;^uid=([^,]+),ou=users,associatedDomain=$1,ou=domains,o=&lt;basedn&gt;$$&quot; write
+ by * none
+
+
+ # Note that above the &quot;by&quot; clause needs a &quot;regex&quot; style to make sure
+ # it expands to a DN that starts with a &quot;uid=&lt;someuserid&gt;&quot; pattern
+ # while substituting the associatedDomain submatch from the &quot;what&quot; clause.
+
+
+ # This rule lets a user with &quot;uid=&lt;matcheduid&gt;&quot; of &quot;&lt;associatedDomain=matcheddomain&gt;&quot;
+ # write (i.e. add, modify, delete) the entry whose DN is exactly
+ # &quot;uid=&lt;matcheduid&gt;,ou=addressbook,associatedDomain=&lt;matcheddomain&gt;,ou=domains,o=&lt;basedn&gt;&quot;
+ # and ANY entry as subtree of it
+
+
+ access to dn.regex=&quot;^(.+,)?uid=([^,]+),ou=addressbook,associatedDomain=([^,]+),ou=domains,o=&lt;basedn&gt;$&quot;
+ by dn.exact,expand=&quot;uid=$2,ou=users,associatedDomain=$3,ou=domains,o=&lt;basedn&gt;&quot; write
+ by * none
+
+
+ # Note that above the &quot;by&quot; clause uses the &quot;exact&quot; style with the &quot;expand&quot;
+ # modifier because now the whole pattern can be rebuilt by means of the
+ # submatches from the &quot;what&quot; clause, so a &quot;regex&quot; compilation and evaluation
+ # is no longer required.
+</PRE>
+<H3><A NAME="Tips for using regular expressions in Access Control">8.4.8. Tips for using regular expressions in Access Control</A></H3>
+<P>Always use <EM>dn.regex=&lt;pattern&gt;</EM> when you intend to use regular expression matching. <EM>dn=&lt;pattern&gt;</EM> alone defaults to <EM>dn.exact&lt;pattern&gt;</EM>.</P>
+<P>Use <EM>(.+)</EM> instead of <EM>(.*)</EM> when you want at least one char to be matched. <EM>(.*)</EM> matches the empty string as well.</P>
+<P>Don't use regular expressions for matches that can be done otherwise in a safer and cheaper manner. Examples:</P>
+<PRE>
+ dn.regex=&quot;.*dc=example,dc=com&quot;
+</PRE>
+<P>is unsafe and expensive:</P>
+<UL>
+<LI>unsafe because any string containing <EM>dc=example,dc=com </EM>will match, not only those that end with the desired pattern; use <EM>.*dc=example,dc=com$</EM> instead.
+<LI>unsafe also because it would allow any <EM>attributeType</EM> ending with <EM>dc</EM> as naming attribute for the first RDN in the string, e.g. a custom attributeType <EM>mydc</EM> would match as well. If you really need a regular expression that allows just <EM>dc=example,dc=com</EM> or any of its subtrees, use <EM>^(.+,)?dc=example,dc=com$</EM>, which means: anything to the left of dc=..., if any (the question mark after the pattern within brackets), must end with a comma;
+<LI>expensive because if you don't need submatches, you could use scoping styles, e.g.</UL>
+<PRE>
+ dn.subtree=&quot;dc=example,dc=com&quot;
+</PRE>
+<P>to include <EM>dc=example,dc=com</EM> in the matching patterns,</P>
+<PRE>
+ dn.children=&quot;dc=example,dc=com&quot;
+</PRE>
+<P>to exclude <EM>dc=example,dc=com</EM> from the matching patterns, or</P>
+<PRE>
+ dn.onelevel=&quot;dc=example,dc=com&quot;
+</PRE>
+<P>to allow exactly one sublevel matches only.</P>
+<P>Always use <EM>^</EM> and <EM>$</EM> in regexes, whenever appropriate, because <EM>ou=(.+),ou=(.+),ou=addressbooks,o=basedn</EM> will match <EM>something=bla,ou=xxx,ou=yyy,ou=addressbooks,o=basedn,ou=addressbooks,o=basedn,dc=some,dc=org</EM></P>
+<P>Always use <EM>([^,]+)</EM> to indicate exactly one RDN, because <EM>(.+)</EM> can include any number of RDNs; e.g. <EM>ou=(.+),dc=example,dc=com</EM> will match <EM>ou=My,o=Org,dc=example,dc=com</EM>, which might not be what you want.</P>
+<P>Never add the rootdn to the by clauses. ACLs are not even processed for operations performed with rootdn identity (otherwise there would be no reason to define a rootdn at all).</P>
+<P>Use shorthands. The user directive matches authenticated users and the anonymous directive matches anonymous users.</P>
+<P>Don't use the <EM>dn.regex</EM> form for &lt;by&gt; clauses if all you need is scoping and/or substring replacement; use scoping styles (e.g. <EM>exact</EM>, <EM>onelevel</EM>, <EM>children</EM> or <EM>subtree</EM>) and the style modifier expand to cause substring expansion.</P>
+<P>For instance,</P>
+<PRE>
+ access to dn.regex=&quot;.+,dc=([^,]+),dc=([^,]+)$&quot;
+ by dn.regex=&quot;^[^,],ou=Admin,dc=$1,dc=$2$$&quot; write
+</PRE>
+<P>although correct, can be safely and efficiently replaced by</P>
+<PRE>
+ access to dn.regex=&quot;.+,(dc=[^,]+,dc=[^,]+)$&quot;
+ by dn.onelevel,expand=&quot;ou=Admin,$1&quot; write
+</PRE>
+<P>where the regex in the <EM>&lt;what&gt;</EM> clause is more compact, and the one in the <EM>&lt;by&gt;</EM> clause is replaced by a much more efficient scoping style of onelevel with substring expansion.</P>
+<H3><A NAME="Granting and Denying access based on security strength factors (ssf)">8.4.9. Granting and Denying access based on security strength factors (ssf)</A></H3>
+<P>You can restrict access based on the security strength factor (SSF)</P>
+<PRE>
+ access to dn=&quot;cn=example,cn=edu&quot;
+ by * ssf=256 read
+</PRE>
+<P>0 (zero) implies no protection, 1 implies integrity protection only, 56 DES or other weak ciphers, 112 triple DES and similar ciphers, 128 RC4, Blowfish and other similar ciphers, 256 modern ciphers.</P>
+<P>Other possibilities:</P>
+<PRE>
+ transport_ssf=&lt;n&gt;
+ tls_ssf=&lt;n&gt;
+ sasl_ssf=&lt;n&gt;
+</PRE>
+<P>256 is recommended.</P>
+<P>See <EM>slapd.conf</EM>(5) for information on <EM>ssf</EM>.</P>
+<H3><A NAME="When things aren\'t working as expected">8.4.10. When things aren't working as expected</A></H3>
+<P>Consider this example:</P>
+<PRE>
+ access to *
+ by anonymous auth
+
+ access to *
+ by self write
+
+ access to *
+ by users read
+</PRE>
+<P>You may think this will allow any user to login, to read everything and change his own data if he is logged in. But in this example only the login works and an ldapsearch returns no data. The Problem is that SLAPD goes through its access config line by line and stops as soon as it finds a match in the part of the access rule.(here: <EM>to *</EM>)</P>
+<P>To get what we wanted the file has to read:</P>
+<PRE>
+ access to *
+ by anonymous auth
+ by self write
+ by users read
+</PRE>
+<P>The general rule is: &quot;special access rules first, generic access rules last&quot;</P>
+<P>See also <EM>slapd.access</EM>(5), loglevel 128 and <EM>slapacl</EM>(8) for debugging information.</P>
+<H2><A NAME="Sets - Granting rights based on relationships">8.5. Sets - Granting rights based on relationships</A></H2>
+<P>Sets are best illustrated via examples. The following sections will present a few set ACL examples in order to facilitate their understanding.</P>
+<P>(Sets in Access Controls FAQ Entry: <A HREF="http://www.openldap.org/faq/data/cache/1133.html">http://www.openldap.org/faq/data/cache/1133.html</A>)</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>Sets are considered experimental.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H3><A NAME="Groups of Groups">8.5.1. Groups of Groups</A></H3>
+<P>The OpenLDAP ACL for groups doesn't expand groups within groups, which are groups that have another group as a member. For example:</P>
+<PRE>
+ dn: cn=sudoadm,ou=group,dc=example,dc=com
+ cn: sudoadm
+ objectClass: groupOfNames
+ member: uid=john,ou=people,dc=example,dc=com
+ member: cn=accountadm,ou=group,dc=example,dc=com
+
+ dn: cn=accountadm,ou=group,dc=example,dc=com
+ cn: accountadm
+ objectClass: groupOfNames
+ member: uid=mary,ou=people,dc=example,dc=com
+</PRE>
+<P>If we use standard group ACLs with the above entries and allow members of the <TT>sudoadm</TT> group to write somewhere, <TT>mary</TT> won't be included:</P>
+<PRE>
+ access to dn.subtree=&quot;ou=sudoers,dc=example,dc=com&quot;
+ by group.exact=&quot;cn=sudoadm,ou=group,dc=example,dc=com&quot; write
+ by * read
+</PRE>
+<P>With sets we can make the ACL be recursive and consider group within groups. So for each member that is a group, it is further expanded:</P>
+<PRE>
+ access to dn.subtree=&quot;ou=sudoers,dc=example,dc=com&quot;
+ by set=&quot;[cn=sudoadm,ou=group,dc=example,dc=com]/member* &amp; user&quot; write
+ by * read
+</PRE>
+<P>This set ACL means: take the <TT>cn=sudoadm</TT> DN, check its <TT>member</TT> attribute(s) (where the &quot;<TT>*</TT>&quot; means recursively) and intersect the result with the authenticated user's DN. If the result is non-empty, the ACL is considered a match and write access is granted.</P>
+<P>The following drawing explains how this set is built:</P>
+<P><CENTER><IMG SRC="set-recursivegroup.png" ALIGN="center"></CENTER></P>
+<P ALIGN="Center">Figure X.Y: Populating a recursive group set</P>
+<P>First we get the <TT>uid=john</TT> DN. This entry doesn't have a <TT>member</TT> attribute, so the expansion stops here. Now we get to <TT>cn=accountadm</TT>. This one does have a <TT>member</TT> attribute, which is <TT>uid=mary</TT>. The <TT>uid=mary</TT> entry, however, doesn't have member, so we stop here again. The end comparison is:</P>
+<PRE>
+ {&quot;uid=john,ou=people,dc=example,dc=com&quot;,&quot;uid=mary,ou=people,dc=example,dc=com&quot;} &amp; user
+</PRE>
+<P>If the authenticated user's DN is any one of those two, write access is granted. So this set will include <TT>mary</TT> in the <TT>sudoadm</TT> group and she will be allowed the write access.</P>
+<H3><A NAME="Group ACLs without DN syntax">8.5.2. Group ACLs without DN syntax</A></H3>
+<P>The traditional group ACLs, and even the previous example about recursive groups, require that the members are specified as DNs instead of just usernames.</P>
+<P>With sets, however, it's also possible to use simple names in group ACLs, as this example will show.</P>
+<P>Let's say we want to allow members of the <TT>sudoadm</TT> group to write to the <TT>ou=sudoers</TT> branch of our tree. But our group definition now is using <TT>memberUid</TT> for the group members:</P>
+<PRE>
+ dn: cn=sudoadm,ou=group,dc=example,dc=com
+ cn: sudoadm
+ objectClass: posixGroup
+ gidNumber: 1000
+ memberUid: john
+</PRE>
+<P>With this type of group, we can't use group ACLs. But with a set ACL we can grant the desired access:</P>
+<PRE>
+ access to dn.subtree=&quot;ou=sudoers,dc=example,dc=com&quot;
+ by set=&quot;[cn=sudoadm,ou=group,dc=example,dc=com]/memberUid &amp; user/uid&quot; write
+ by * read
+</PRE>
+<P>We use a simple intersection where we compare the <TT>uid</TT> attribute of the connecting (and authenticated) user with the <TT>memberUid</TT> attributes of the group. If they match, the intersection is non-empty and the ACL will grant write access.</P>
+<P>This drawing illustrates this set when the connecting user is authenticated as <TT>uid=john,ou=people,dc=example,dc=com</TT>:</P>
+<P><CENTER><IMG SRC="set-memberUid.png" ALIGN="center"></CENTER></P>
+<P ALIGN="Center">Figure X.Y: Sets with <TT>memberUid</TT></P>
+<P>In this case, it's a match. If it were <TT>mary</TT> authenticating, however, she would be denied write access to <TT>ou=sudoers</TT> because her <TT>uid</TT> attribute is not listed in the group's <TT>memberUid</TT>.</P>
+<H3><A NAME="Following references">8.5.3. Following references</A></H3>
+<P>We will now show a quite powerful example of what can be done with sets. This example tends to make OpenLDAP administrators smile after they have understood it and its implications.</P>
+<P>Let's start with an user entry:</P>
+<PRE>
+ dn: uid=john,ou=people,dc=example,dc=com
+ uid: john
+ objectClass: inetOrgPerson
+ givenName: John
+ sn: Smith
+ cn: john
+ manager: uid=mary,ou=people,dc=example,dc=com
+</PRE>
+<P>Writing an ACL to allow the manager to update some attributes is quite simple using sets:</P>
+<PRE>
+ access to dn.exact=&quot;uid=john,ou=people,dc=example,dc=com&quot;
+ attrs=carLicense,homePhone,mobile,pager,telephoneNumber
+ by self write
+ by set=&quot;this/manager &amp; user&quot; write
+ by * read
+</PRE>
+<P>In that set, <TT>this</TT> expands to the entry being accessed, so that <TT>this/manager</TT> expands to <TT>uid=mary,ou=people,dc=example,dc=com</TT> when john's entry is accessed. If the manager herself is accessing John's entry, the ACL will match and write access to those attributes will be granted.</P>
+<P>So far, this same behavior can be obtained with the <TT>dnattr</TT> keyword. With sets, however, we can further enhance this ACL. Let's say we want to allow the secretary of the manager to also update these attributes. This is how we do it:</P>
+<PRE>
+ access to dn.exact=&quot;uid=john,ou=people,dc=example,dc=com&quot;
+ attrs=carLicense,homePhone,mobile,pager,telephoneNumber
+ by self write
+ by set=&quot;this/manager &amp; user&quot; write
+ by set=&quot;this/manager/secretary &amp; user&quot; write
+ by * read
+</PRE>
+<P>Now we need a picture to help explain what is happening here (entries shortened for clarity):</P>
+<P><CENTER><IMG SRC="set-following-references.png" ALIGN="center"></CENTER></P>
+<P ALIGN="Center">Figure X.Y: Sets jumping through entries</P>
+<P>In this example, Jane is the secretary of Mary, which is the manager of John. This whole relationship is defined with the <TT>manager</TT> and <TT>secretary</TT> attributes, which are both of the distinguishedName syntax (i.e., full DNs). So, when the <TT>uid=john</TT> entry is being accessed, the <TT>this/manager/secretary</TT> set becomes <TT>{&quot;uid=jane,ou=people,dc=example,dc=com&quot;</TT>} (follow the references in the picture):</P>
+<PRE>
+ this = [uid=john,ou=people,dc=example,dc=com]
+ this/manager = \
+ [uid=john,ou=people,dc=example,dc=com]/manager = uid=mary,ou=people,dc=example,dc=com
+ this/manager/secretary = \
+ [uid=mary,ou=people,dc=example,dc=com]/secretary = uid=jane,ou=people,dc=example,dc=com
+</PRE>
+<P>The end result is that when Jane accesses John's entry, she will be granted write access to the specified attributes. Better yet, this will happen to any entry she accesses which has Mary as the manager.</P>
+<P>This is all cool and nice, but perhaps gives too much power to secretaries. Maybe we need to further restrict it. For example, let's only allow executive secretaries to have this power:</P>
+<PRE>
+ access to dn.exact=&quot;uid=john,ou=people,dc=example,dc=com&quot;
+ attrs=carLicense,homePhone,mobile,pager,telephoneNumber
+ by self write
+ by set=&quot;this/manager &amp; user&quot; write
+ by set=&quot;this/manager/secretary &amp;
+ [cn=executive,ou=group,dc=example,dc=com]/member* &amp;
+ user&quot; write
+ by * read
+</PRE>
+<P>It's almost the same ACL as before, but we now also require that the connecting user be a member of the (possibly nested) <TT>cn=executive</TT> group.</P>
+<P></P>
+<HR>
+<H1><A NAME="Limits">9. Limits</A></H1>
+<H2><A NAME="Introduction">9.1. Introduction</A></H2>
+<P>It is usually desirable to limit the server resources that can be consumed by each LDAP client. OpenLDAP provides two sets of limits: a size limit, which can restrict the <EM>number</EM> of entries that a client can retrieve in a single operation, and a time limit which restricts the length of time that an operation may continue. Both types of limit can be given different values depending on who initiated the operation.</P>
+<H2><A NAME="Soft and Hard limits">9.2. Soft and Hard limits</A></H2>
+<P>The server administrator can specify both <EM>soft limits</EM> and <EM>hard limits</EM>. Soft limits can be thought of as being the default limit value. Hard limits cannot be exceeded by ordinary LDAP users.</P>
+<P>LDAP clients can specify their own size and time limits when issuing search operations. This feature has been present since the earliest version of X.500.</P>
+<P>If the client specifies a limit then the lower of the requested value and the <EM>hard limit</EM> will become the limit for the operation.</P>
+<P>If the client does not specify a limit then the server applies the <EM>soft limit</EM>.</P>
+<P>Soft and Hard limits are often referred to together as <EM>administrative limits</EM>. Thus, if an LDAP client requests a search that would return more results than the limits allow it will get an <EM>adminLimitExceeded</EM> error. Note that the server will usually return some results even if the limit has been exceeded: this feature is useful to clients that just want to check for the existence of some entries without needing to see them all.</P>
+<P>The <EM>rootdn</EM> is not subject to any limits.</P>
+<H2><A NAME="Global Limits">9.3. Global Limits</A></H2>
+<P>Limits specified in the global part of the server configuration act as defaults which are used if no database has more specific limits set.</P>
+<P>In a <EM>slapd.conf</EM>(5) configuration the keywords are <TT>sizelimit</TT> and <TT>timelimit</TT>. When using the <EM>slapd config</EM> backend, the corresponding attributes are <TT>olcSizeLimit</TT> and <TT>olcTimeLimit</TT>. The syntax of these values are the same in both cases.</P>
+<P>The simple form sets both soft and hard limits to the same value:</P>
+<PRE>
+ sizelimit {&lt;integer&gt;|unlimited}
+ timelimit {&lt;integer&gt;|unlimited}
+</PRE>
+<P>The default sizelimit is 500 entries and the default timelimit is 3600 seconds.</P>
+<P>An extended form allows soft and hard limits to be set separately:</P>
+<PRE>
+ sizelimit size[.{soft|hard}]=&lt;integer&gt; [...]
+ timelimit time[.{soft|hard}]=&lt;integer&gt; [...]
+</PRE>
+<P>Thus, to set a soft sizelimit of 10 entries and a hard limit of 75 entries:</P>
+<PRE>
+ sizelimit size.soft=10 size.hard=75
+</PRE>
+<H3><A NAME="Special Size Limits">9.3.1. Special Size Limits</A></H3>
+<P>There are other forms of size limits in addition to the soft and hard limits. Note that when using the simple <EM>sizelimit</EM> form, none of these special limits are changed.</P>
+<H4><A NAME="Unchecked Limits">9.3.1.1. Unchecked Limits</A></H4>
+<P>The <EM>unchecked</EM> keyword sets a limit on how many entries the server will examine after doing index lookups but before evaluating filter matches. If the set of candidates exceeds this limit, the search is aborted. The purpose is to avoid causing excessive workload on <EM>slapd</EM> if a filter uses attributes that are not properly indexed, and can be critical for very large directories.</P>
+<PRE>
+ sizelimit size.unchecked={&lt;integer&gt;|unlimited|disabled}
+</PRE>
+<P>The default is unlimited. The <EM>disabled</EM> setting prevents a search from being performed at all. This may be useful in the per-database limits described below, to disallow searches for a specific set of users.</P>
+<H4><A NAME="Paged Results Limits">9.3.1.2. Paged Results Limits</A></H4>
+<P>If the LDAP client adds the <EM>pagedResultsControl</EM> to the search operation, the hard size limit is used by default, because the request for a specific page size is considered an explicit request for a limitation on the number of entries to be returned. However, the size limit applies to the total count of entries returned within the search, and not to a single page.</P>
+<P>Additional size limits may be enforced for paged searches.</P>
+<P>The <TT>size.pr</TT> limit controls the maximum page size:</P>
+<PRE>
+ sizelimit size.pr={&lt;integer&gt;|noEstimate|unlimited}
+</PRE>
+<P><TT>&lt;integer&gt;</TT> is the maximum page size if no explicit size is set. <TT>noEstimate</TT> has no effect in the current implementation as the server does not return an estimate of the result size anyway. <TT>unlimited</TT> indicates that no limit is applied to the maximum page size.</P>
+<P>The <TT>size.prtotal</TT> limit controls the total number of entries that can be returned by a paged search. By default the limit is the same as the normal <TT>size.hard</TT> limit.</P>
+<PRE>
+ size.prtotal={&lt;integer&gt;|unlimited|disabled}
+</PRE>
+<P><TT>unlimited</TT> removes the limit on the number of entries that can be returned by a paged search. <TT>disabled</TT> can be used to selectively disable paged result searches.</P>
+<H2><A NAME="Per-Database Limits">9.4. Per-Database Limits</A></H2>
+<P>Each database can have its own set of limits that override the global ones. The syntax is more flexible, and it allows different limits to be applied to different entities. Note that an <EM>entity</EM> is different from an <EM>entry</EM>: the term <EM>entity</EM> is used here to indicate the ID of the person or process that has initiated the LDAP operation.</P>
+<P>In a <EM>slapd.conf</EM>(5) configuration the keyword is <TT>limits</TT>. When using the <EM>slapd config</EM> backend, the corresponding attribute is <TT>olcLimits</TT>. The syntax of the values is the same in both cases.</P>
+<PRE>
+ limits &lt;selector&gt; &lt;limit&gt; [&lt;limit&gt; [...]]
+</PRE>
+<P>The <EM>limits</EM> clause can be specified multiple times to apply different limits to different initiators. The server examines each clause in turn until it finds one that matches the operation's initiator or base DN. If no match is found, the global limits will be used.</P>
+<H3><A NAME="Specify who the limits apply to">9.4.1. Specify who the limits apply to</A></H3>
+<P>The <TT>&lt;selector&gt;</TT> part of the <EM>limits</EM> clause can take any of these values:</P>
+<TABLE CLASS="columns" BORDER ALIGN='Center'>
+<CAPTION ALIGN=top>Table 9.1: Limits Entity Specifiers</CAPTION>
+<TR CLASS="heading">
+<TD>
+<STRONG>Specifier</STRONG>
+</TD>
+<TD>
+<STRONG>Entities</STRONG>
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>*</TT>
+</TD>
+<TD>
+All, including anonymous and authenticated users
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>anonymous</TT>
+</TD>
+<TD>
+Anonymous (non-authenticated) users
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>users</TT>
+</TD>
+<TD>
+Authenticated users
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>dn[.&lt;type&gt;][.&lt;style&gt;]=&lt;pattern&gt;]</TT>
+</TD>
+<TD>
+Entry or entries within a scope that match &lt;pattern&gt;
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>group[/oc[/at]]=&lt;pattern&gt;</TT>
+</TD>
+<TD>
+Members of a group
+</TD>
+</TR>
+</TABLE>
+
+<P>Where</P>
+<P><EM>type</EM> can be one of self or this and</P>
+<P><EM>style</EM> can be one of exact, base, onelevel, subtree, children, regex, or anonymous</P>
+<P>More information can be found in the <EM>slapd.conf</EM>(5) or <EM>slapd-config</EM>(5) manual pages.</P>
+<H3><A NAME="Specify time limits">9.4.2. Specify time limits</A></H3>
+<P>The syntax for time limits is</P>
+<PRE>
+ time[.{soft|hard}]=&lt;integer&gt;
+</PRE>
+<P>where integer is the number of seconds slapd will spend answering a search request.</P>
+<P>If neither <EM>soft</EM> nor <EM>hard</EM> is specified, the value is used for both, e.g.:</P>
+<PRE>
+ limits anonymous time=27
+</PRE>
+<P>The value <EM>unlimited</EM> may be used to remove the hard time limit entirely, e.g.:</P>
+<PRE>
+ limits dn.exact=&quot;cn=anyuser,dc=example,dc=org&quot; time.hard=unlimited
+</PRE>
+<H3><A NAME="Specifying size limits">9.4.3. Specifying size limits</A></H3>
+<P>The syntax for size limit is</P>
+<PRE>
+ size[.{soft|hard}]=&lt;integer&gt;
+</PRE>
+<P>where <TT>&lt;integer&gt;</TT> is the maximum number of entries slapd will return when answering a search request.</P>
+<P>In addition to soft and hard limits, other limits are also available, with the same meanings described for the global limits configuration above.</P>
+<H2><A NAME="Example Limit Configurations">9.5. Example Limit Configurations</A></H2>
+<H3><A NAME="Simple Global Limits">9.5.1. Simple Global Limits</A></H3>
+<P>This simple global configuration fragment applies size and time limits to all searches by all users except <EM>rootdn</EM>. It limits searches to 50 results and sets an overall time limit of 10 seconds.</P>
+<PRE>
+ sizelimit 50
+ timelimit 10
+</PRE>
+<H3><A NAME="Global Hard and Soft Limits">9.5.2. Global Hard and Soft Limits</A></H3>
+<P>It is sometimes useful to limit the size of result sets but to allow clients to request a higher limit where needed. This can be achieved by setting separate hard and soft limits.</P>
+<PRE>
+ sizelimit size.soft=5 size.hard=100
+</PRE>
+<P>To prevent clients from doing very inefficient non-indexed searches, add the <EM>unchecked</EM> limit:</P>
+<PRE>
+ sizelimit size.soft=5 size.hard=100 size.unchecked=100
+</PRE>
+<H3><A NAME="Giving specific users larger limits">9.5.3. Giving specific users larger limits</A></H3>
+<P>Having set appropriate default limits in the global configuration, you may want to give certain users the ability to retrieve larger result sets. Here is a way to do that in the per-database configuration:</P>
+<PRE>
+ limits dn.exact=&quot;cn=anyuser,dc=example,dc=org&quot; size=100000
+ limits dn.exact=&quot;cn=personnel,dc=example,dc=org&quot; size=100000
+ limits dn.exact=&quot;cn=dirsync,dc=example,dc=org&quot; size=100000
+</PRE>
+<P>It is generally best to avoid mentioning specific users in the server configuration. A better way is to give the higher limits to a group:</P>
+<PRE>
+ limits group/groupOfNames/member=&quot;cn=bigwigs,dc=example,dc=org&quot; size=100000
+</PRE>
+<H3><A NAME="Limiting who can do paged searches">9.5.4. Limiting who can do paged searches</A></H3>
+<P>It may be required that certain applications need very large result sets that they retrieve using paged searches, but that you do not want ordinary LDAP users to use the pagedResults control. The <EM>pr</EM> and <EM>prtotal</EM> limits can help:</P>
+<PRE>
+ limits group/groupOfNames/member=&quot;cn=dirsync,dc=example,dc=org&quot; size.prtotal=unlimited
+ limits users size.soft=5 size.hard=100 size.prtotal=disabled
+ limits anonymous size.soft=2 size.hard=5 size.prtotal=disabled
+</PRE>
+<H2><A NAME="Glued/Subordinate database configurations">9.6. Glued/Subordinate database configurations</A></H2>
+<P>When using subordinate databases, it is necessary for any limits that are to be applied across the parent and its subordinates to be defined in both the parent and its subordinates. Otherwise the settings on the subordinate databases are not honored.</P>
+<H2><A NAME="Further Information">9.7. Further Information</A></H2>
+<P>For further information please see <EM>slapd.conf</EM>(5), <EM>ldapsearch</EM>(1) and <EM>slapd.access</EM>(5)</P>
+<P></P>
+<HR>
+<H1><A NAME="Database Creation and Maintenance Tools">10. Database Creation and Maintenance Tools</A></H1>
+<P>This section tells you how to create a slapd database from scratch, and how to do trouble shooting if you run into problems. There are two ways to create a database. First, you can create the database on-line using <TERM>LDAP</TERM>. With this method, you simply start up slapd and add entries using the LDAP client of your choice. This method is fine for relatively small databases (a few hundred or thousand entries, depending on your requirements). This method works for database types which support updates.</P>
+<P>The second method of database creation is to do it off-line using special utilities provided with <EM>slapd</EM>(8). This method is best if you have many thousands of entries to create, which would take an unacceptably long time using the LDAP method, or if you want to ensure the database is not accessed while it is being created. Note that not all database types support these utilities.</P>
+<H2><A NAME="Creating a database over LDAP">10.1. Creating a database over LDAP</A></H2>
+<P>With this method, you use the LDAP client of your choice (e.g., the <EM>ldapadd</EM>(1)) to add entries, just like you would once the database is created. You should be sure to set the following options in the configuration file before starting <EM>slapd</EM>(8).</P>
+<PRE>
+ suffix &lt;dn&gt;
+</PRE>
+<P>As described in the <A HREF="#General Database Directives">General Database Directives</A> section, this option defines which entries are to be held by this database. You should set this to the DN of the root of the subtree you are trying to create. For example:</P>
+<PRE>
+ suffix &quot;dc=example,dc=com&quot;
+</PRE>
+<P>You should be sure to specify a directory where the index files should be created:</P>
+<PRE>
+ directory &lt;directory&gt;
+</PRE>
+<P>For example:</P>
+<PRE>
+ directory /usr/local/var/openldap-data
+</PRE>
+<P>You need to create this directory with appropriate permissions such that slapd can write to it.</P>
+<P>You need to configure slapd so that you can connect to it as a directory user with permission to add entries. You can configure the directory to support a special <EM>super-user</EM> or <EM>root</EM> user just for this purpose. This is done through the following two options in the database definition:</P>
+<PRE>
+ rootdn &lt;dn&gt;
+ rootpw &lt;passwd&gt;
+</PRE>
+<P>For example:</P>
+<PRE>
+ rootdn &quot;cn=Manager,dc=example,dc=com&quot;
+ rootpw secret
+</PRE>
+<P>These options specify a DN and password that can be used to authenticate as the <EM>super-user</EM> entry of the database (i.e., the entry allowed to do anything). The DN and password specified here will always work, regardless of whether the entry named actually exists or has the password given. This solves the chicken-and-egg problem of how to authenticate and add entries before any entries yet exist.</P>
+<P>Finally, you should make sure that the database definition contains the index definitions you want:</P>
+<PRE>
+ index {&lt;attrlist&gt; | default} [pres,eq,approx,sub,none]
+</PRE>
+<P>For example, to index the <TT>cn</TT>, <TT>sn</TT>, <TT>uid</TT> and <TT>objectclass</TT> attributes, the following <TT>index</TT> directives could be used:</P>
+<PRE>
+ index cn,sn,uid pres,eq,approx,sub
+ index objectClass eq
+</PRE>
+<P>This would create presence, equality, approximate, and substring indices for the <TT>cn</TT>, <TT>sn</TT>, and <TT>uid</TT> attributes and an equality index for the <TT>objectClass</TT> attribute. Note that not all index types are available with all attribute types. See <A HREF="#The slapd Configuration File">The slapd Configuration File</A> section for more information on this option.</P>
+<P>Once you have configured things to your liking, start up slapd, connect with your LDAP client, and start adding entries. For example, to add an organization entry and an organizational role entry using the <I>ldapadd</I> tool, you could create an <TERM>LDIF</TERM> file called <TT>entries.ldif</TT> with the contents:</P>
+<PRE>
+ # Organization for Example Corporation
+ dn: dc=example,dc=com
+ objectClass: dcObject
+ objectClass: organization
+ dc: example
+ o: Example Corporation
+ description: The Example Corporation
+
+ # Organizational Role for Directory Manager
+ dn: cn=Manager,dc=example,dc=com
+ objectClass: organizationalRole
+ cn: Manager
+ description: Directory Manager
+</PRE>
+<P>and then use a command like this to actually create the entry:</P>
+<PRE>
+ ldapadd -f entries.ldif -x -D &quot;cn=Manager,dc=example,dc=com&quot; -w secret
+</PRE>
+<P>The above command assumes settings provided in the above examples.</P>
+<H2><A NAME="Creating a database off-line">10.2. Creating a database off-line</A></H2>
+<P>The second method of database creation is to do it off-line, using the slapd database tools described below. This method is best if you have many thousands of entries to create, which would take an unacceptably long time to add using the LDAP method described above. These tools read the slapd configuration file and an input file containing a text representation of the entries to add. For database types which support the tools, they produce the database files directly (otherwise you must use the on-line method above). Also, the input file must be completely valid, as these tools do fewer consistency checks than the on-line method.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>this Guide is not meant to provide exhaustive documentation on the software. The tool descriptions here only list a few of the available options for each command. Read the associated manpages for complete documentation on all of the available options.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>There are several important configuration options you will want to be sure and set in the config file database definition first:</P>
+<PRE>
+ suffix &lt;dn&gt;
+</PRE>
+<P>As described in the <A HREF="#General Database Directives">General Database Directives</A> section, this option defines which entries are to be held by this database. You should set this to the DN of the root of the subtree you are trying to create. For example:</P>
+<PRE>
+ suffix &quot;dc=example,dc=com&quot;
+</PRE>
+<P>You should be sure to specify a directory where the index files should be created:</P>
+<PRE>
+ directory &lt;directory&gt;
+</PRE>
+<P>For example:</P>
+<PRE>
+ directory /usr/local/var/openldap-data
+</PRE>
+<P>Finally, you need to specify which indices you want to build. This is done by one or more index options.</P>
+<PRE>
+ index {&lt;attrlist&gt; | default} [pres,eq,approx,sub,none]
+</PRE>
+<P>For example:</P>
+<PRE>
+ index cn,sn,uid pres,eq,approx,sub
+ index objectClass eq
+</PRE>
+<P>This would create presence, equality, approximate, and substring indices for the <TT>cn</TT>, <TT>sn</TT>, and <TT>uid</TT> attributes and an equality index for the <TT>objectClass</TT> attribute. Note that not all index types are available with all attribute types. See <A HREF="#The slapd Configuration File">The slapd Configuration File</A> section for more information on this option.</P>
+<H3><A NAME="The {{EX:slapadd}} program">10.2.1. The <TT>slapadd</TT> program</A></H3>
+<P>Once you've configured things to your liking, you create the primary database and associated indices by running the <EM>slapadd</EM>(8) program:</P>
+<PRE>
+ slapadd -l &lt;inputfile&gt; -f &lt;slapdconfigfile&gt;
+ [-d &lt;debuglevel&gt;] [-n &lt;integer&gt;|-b &lt;suffix&gt;]
+</PRE>
+<P>The arguments have the following meanings:</P>
+<PRE>
+ -l &lt;inputfile&gt;
+</PRE>
+<P>Specifies the <TERM>LDIF</TERM> input file containing the entries to add in text form (described below in the <A HREF="#The LDIF text entry format">The LDIF text entry format</A> section).</P>
+<PRE>
+ -f &lt;slapdconfigfile&gt;
+</PRE>
+<P>Specifies the slapd configuration file that tells where to create the indices, what indices to create, etc.</P>
+<PRE>
+ -F &lt;slapdconfdirectory&gt;
+</PRE>
+<P>Specifies a config directory. If both <TT>-f</TT> and <TT>-F</TT> are specified, the config file will be read and converted to config directory format and written to the specified directory. If neither option is specified, an attempt to read the default config directory will be made before trying to use the default config file. If a valid config directory exists then the default config file is ignored. If dryrun mode is also specified, no conversion will occur.</P>
+<PRE>
+ -d &lt;debuglevel&gt;
+</PRE>
+<P>Turn on debugging, as specified by <TT>&lt;debuglevel&gt;</TT>. The debug levels are the same as for slapd. See the <A HREF="#Command-Line Options">Command-Line Options</A> section in <A HREF="#Running slapd">Running slapd</A>.</P>
+<PRE>
+ -n &lt;databasenumber&gt;
+</PRE>
+<P>An optional argument that specifies which database to modify. The first database listed in the configuration file is <TT>1</TT>, the second <TT>2</TT>, etc. By default, the first database in the configuration file is used. Should not be used in conjunction with <TT>-b</TT>.</P>
+<PRE>
+ -b &lt;suffix&gt;
+</PRE>
+<P>An optional argument that specifies which database to modify. The provided suffix is matched against a database <TT>suffix</TT> directive to determine the database number. Should not be used in conjunction with <TT>-n</TT>.</P>
+<H3><A NAME="The {{EX:slapindex}} program">10.2.2. The <TT>slapindex</TT> program</A></H3>
+<P>Sometimes it may be necessary to regenerate indices (such as after modifying <EM>slapd.conf</EM>(5)). This is possible using the <EM>slapindex</EM>(8) program. <EM>slapindex</EM> is invoked like this</P>
+<PRE>
+ slapindex -f &lt;slapdconfigfile&gt;
+ [-d &lt;debuglevel&gt;] [-n &lt;databasenumber&gt;|-b &lt;suffix&gt;] [attr...]
+</PRE>
+<P>Where the <TT>-f</TT>, <TT>-d</TT>, <TT>-n</TT> and <TT>-b</TT> options are the same as for the <EM>slapadd</EM>(1) program. If no specific attributes are listed, <EM>slapindex</EM> rebuilds all indices based upon the current database contents.</P>
+<H3><A NAME="The {{EX:slapcat}} program">10.2.3. The <TT>slapcat</TT> program</A></H3>
+<P>The <TT>slapcat</TT> program is used to dump the database to an <TERM>LDIF</TERM> file. This can be useful when you want to make a human-readable backup of your database or when you want to edit your database off-line. The program is invoked like this:</P>
+<PRE>
+ slapcat -l &lt;filename&gt; -f &lt;slapdconfigfile&gt;
+ [-d &lt;debuglevel&gt;] [-n &lt;databasenumber&gt;|-b &lt;suffix&gt;]
+</PRE>
+<P>where <TT>-n</TT> or <TT>-b</TT> is used to select the database in the <EM>slapd.conf</EM>(5) specified using <TT>-f</TT>. The corresponding <TERM>LDIF</TERM> output is written to standard output or to the file specified using the <TT>-l</TT> option.</P>
+<H2><A NAME="The LDIF text entry format">10.3. The LDIF text entry format</A></H2>
+<P>The <TERM>LDAP Data Interchange Format</TERM> (LDIF) is used to represent LDAP entries in a simple text format. This section provides a brief description of the LDIF entry format which complements <EM>ldif</EM>(5) and the technical specification <A HREF="https://www.rfc-editor.org/rfc/rfc2849.txt">RFC2849</A>.</P>
+<P>The basic form of an entry is:</P>
+<PRE>
+ # comment
+ dn: &lt;distinguished name&gt;
+ &lt;attrdesc&gt;: &lt;attrvalue&gt;
+ &lt;attrdesc&gt;: &lt;attrvalue&gt;
+
+ ...
+</PRE>
+<P>Lines starting with a '<TT>#</TT>' character are comments. An attribute description may be a simple attribute type like <TT>cn</TT> or <TT>objectClass</TT> or <TT>1.2.3</TT> (an <TERM>OID</TERM> associated with an attribute type) or may include options such as <TT>cn;lang_en_US</TT> or <TT>userCertificate;binary</TT>.</P>
+<P>A line may be continued by starting the next line with a <EM>single</EM> space or tab character. For example:</P>
+<PRE>
+ dn: cn=Barbara J Jensen,dc=example,dc=
+ com
+ cn: Barbara J
+ Jensen
+</PRE>
+<P>is equivalent to:</P>
+<PRE>
+ dn: cn=Barbara J Jensen,dc=example,dc=com
+ cn: Barbara J Jensen
+</PRE>
+<P>Multiple attribute values are specified on separate lines. e.g.,</P>
+<PRE>
+ cn: Barbara J Jensen
+ cn: Babs Jensen
+</PRE>
+<P>If an <TT>&lt;attrvalue&gt;</TT> contains non-printing characters or begins with a space, a colon ('<TT>:</TT>'), or a less than ('<TT>&lt;</TT>'), the <TT>&lt;attrdesc&gt;</TT> is followed by a double colon and the base64 encoding of the value. For example, the value &quot;<TT> begins with a space</TT>&quot; would be encoded like this:</P>
+<PRE>
+ cn:: IGJlZ2lucyB3aXRoIGEgc3BhY2U=
+</PRE>
+<P>You can also specify a <TERM>URL</TERM> containing the attribute value. For example, the following specifies the <TT>jpegPhoto</TT> value should be obtained from the file <TT>/path/to/file.jpeg</TT>.</P>
+<PRE>
+ jpegPhoto:&lt; file:///path/to/file.jpeg
+</PRE>
+<P>Multiple entries within the same LDIF file are separated by blank lines. Here's an example of an LDIF file containing three entries.</P>
+<PRE>
+ # Barbara's Entry
+ dn: cn=Barbara J Jensen,dc=example,dc=com
+ cn: Barbara J Jensen
+ cn: Babs Jensen
+ objectClass: person
+ sn: Jensen
+
+ # Bjorn's Entry
+ dn: cn=Bjorn J Jensen,dc=example,dc=com
+ cn: Bjorn J Jensen
+ cn: Bjorn Jensen
+ objectClass: person
+ sn: Jensen
+ # Base64 encoded JPEG photo
+ jpegPhoto:: /9j/4AAQSkZJRgABAAAAAQABAAD/2wBDABALD
+ A4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQ
+ ERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVG
+
+ # Jennifer's Entry
+ dn: cn=Jennifer J Jensen,dc=example,dc=com
+ cn: Jennifer J Jensen
+ cn: Jennifer Jensen
+ objectClass: person
+ sn: Jensen
+ # JPEG photo from file
+ jpegPhoto:&lt; file:///path/to/file.jpeg
+</PRE>
+<P>Notice that the <TT>jpegPhoto</TT> in Bjorn's entry is base 64 encoded and the <TT>jpegPhoto</TT> in Jennifer's entry is obtained from the location indicated by the URL.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>Trailing spaces are not trimmed from values in an LDIF file. Nor are multiple internal spaces compressed. If you don't want them in your data, don't put them there.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P></P>
+<HR>
+<H1><A NAME="Backends">11. Backends</A></H1>
+<P>Backends do the actual work of storing or retrieving data in response to LDAP requests. Backends may be compiled statically into <EM>slapd</EM>, or when module support is enabled, they may be dynamically loaded.</P>
+<P>If your installation uses dynamic modules, you may need to add the relevant <EM>moduleload</EM> directives to the examples that follow. The name of the module for a backend is usually of the form:</P>
+<PRE>
+ back_&lt;backend name&gt;.la
+</PRE>
+<P>So for example, if you need to load the <EM>mdb</EM> backend, you would configure</P>
+<PRE>
+ moduleload back_mdb.la
+</PRE>
+<H2><A NAME="LDAP">11.1. LDAP</A></H2>
+<H3><A NAME="Overview">11.1.1. Overview</A></H3>
+<P>The LDAP backend to <EM>slapd</EM>(8) is not an actual database; instead it acts as a proxy to forward incoming requests to another LDAP server. While processing requests it will also chase referrals, so that referrals are fully processed instead of being returned to the <EM>slapd</EM> client.</P>
+<P>Sessions that explicitly <EM>Bind</EM> to the <EM>back-ldap</EM> database always create their own private connection to the remote LDAP server. Anonymous sessions will share a single anonymous connection to the remote server. For sessions bound through other mechanisms, all sessions with the same DN will share the same connection. This connection pooling strategy can enhance the proxy's efficiency by reducing the overhead of repeatedly making/breaking multiple connections.</P>
+<P>The ldap database can also act as an information service, i.e. the identity of locally authenticated clients is asserted to the remote server, possibly in some modified form. For this purpose, the proxy binds to the remote server with some administrative identity, and, if required, authorizes the asserted identity.</P>
+<P>It is heavily used by a lot of other <A HREF="#Backends">Backends</A> and <A HREF="#Overlays">Overlays</A>.</P>
+<H3><A NAME="back-ldap Configuration">11.1.2. back-ldap Configuration</A></H3>
+<P>As previously mentioned, <EM>slapd-ldap(5)</EM> is used behind the scenes by many other <A HREF="#Backends">Backends</A> and <A HREF="#Overlays">Overlays</A>. Some of them merely provide a few configuration directive themselves, but have available to the administrator the whole of the <EM>slapd-ldap(5)</EM> options.</P>
+<P>For example, the <A HREF="#Translucent Proxy">Translucent Proxy</A>, which retrieves entries from a remote LDAP server that can be partially overridden by the defined database, has only four specific <EM>translucent-</EM> directives, but can be configured using any of the normal <EM>slapd-ldap(5)</EM> options. See <EM>slapo-translucent(5)</EM> for details.</P>
+<P>Other <A HREF="#Overlays">Overlays</A> allow you to tag directives in front of a normal <EM>slapd-ldap(5)</EM> directive. For example, the <EM>slapo-chain(5)</EM> overlay does this:</P>
+<P><EM>&quot;There are very few chain overlay specific directives; however, directives related to the instances of the ldap backend that may be implicitly instantiated by the overlay may assume a special meaning when used in conjunction with this overlay. They are described in slapd-ldap(5), and they also need to be prefixed by chain-.&quot;</EM></P>
+<P>You may have also seen the <EM>slapd-ldap(5)</EM> backend used and described in the <A HREF="#Push Based">Push Based</A> <A HREF="#Replication">Replication</A> section of the guide.</P>
+<P>It should therefore be obvious that the <EM>slapd-ldap(5)</EM> backend is extremely flexible and heavily used throughout the OpenLDAP Suite.</P>
+<P>The following is a very simple example, but already the power of the <EM>slapd-ldap(5)</EM> backend is seen by use of a <EM>uri list</EM>:</P>
+<PRE>
+ database ldap
+ suffix &quot;dc=suretecsystems,dc=com&quot;
+ rootdn &quot;cn=slapd-ldap&quot;
+ uri ldap://localhost/ ldap://remotehost ldap://remotehost2
+</PRE>
+<P>The URI list is space or comma-separated. Whenever the server that responds is not the first one in the list, the list is rearranged and the responsive server is moved to the head, so that it will be first contacted the next time a connection needs be created.</P>
+<P>This feature can be used to provide a form of load balancing when using <A HREF="#Mirror mode replication">Mirror mode replication</A>.</P>
+<H3><A NAME="Further Information">11.1.3. Further Information</A></H3>
+<P><EM>slapd-ldap</EM>(5)</P>
+<H2><A NAME="LDIF">11.2. LDIF</A></H2>
+<H3><A NAME="Overview">11.2.1. Overview</A></H3>
+<P>The LDIF backend to <EM>slapd</EM>(8) is a basic storage backend that stores entries in text files in LDIF format, and exploits the filesystem to create the tree structure of the database. It is intended as a cheap, low performance easy to use backend.</P>
+<P>When using the <EM>cn=config</EM> dynamic configuration database with persistent storage, the configuration data is stored using this backend. See <EM>slapd-config</EM>(5) for more information</P>
+<H3><A NAME="back-ldif Configuration">11.2.2. back-ldif Configuration</A></H3>
+<P>Like many other backends, the LDIF backend can be instantiated with very few configuration lines:</P>
+<PRE>
+ include ./schema/core.schema
+
+ database ldif
+ directory ./ldif
+ suffix &quot;dc=suretecsystems,dc=com&quot;
+ rootdn &quot;cn=LDIF,dc=suretecsystems,dc=com&quot;
+ rootpw LDIF
+</PRE>
+<P>If we add the <EM>dcObject</EM> for <EM>dc=suretecsystems,dc=com</EM>, you can see how this is added behind the scenes on the file system:</P>
+<PRE>
+ dn: dc=suretecsystems,dc=com
+ objectClass: dcObject
+ objectClass: organization
+ dc: suretecsystems
+ o: Suretec Systems Ltd
+</PRE>
+<P>Now we add it to the directory:</P>
+<PRE>
+ ldapadd -x -H ldap://localhost:9011 -f suretec.ldif -D &quot;cn=LDIF,dc=suretecsystems,dc=com&quot; -w LDIF
+ adding new entry &quot;dc=suretecsystems,dc=com&quot;
+</PRE>
+<P>And inside <TT>./ldif</TT> we have:</P>
+<PRE>
+ ls ./ldif
+ dc=suretecsystems,dc=com.ldif
+</PRE>
+<P>which again contains:</P>
+<PRE>
+ cat ldif/dc\=suretecsystems\,dc\=com.ldif
+
+ dn: dc=suretecsystems
+ objectClass: dcObject
+ objectClass: organization
+ dc: suretecsystems
+ o: Suretec Systems Ltd.
+ structuralObjectClass: organization
+ entryUUID: 2134b714-e3a1-102c-9a15-f96ee263886d
+ creatorsName: cn=LDIF,dc=suretecsystems,dc=com
+ createTimestamp: 20080711142643Z
+ entryCSN: 20080711142643.661124Z#000000#000#000000
+ modifiersName: cn=LDIF,dc=suretecsystems,dc=com
+ modifyTimestamp: 20080711142643Z
+</PRE>
+<P>This is the complete format you would get when exporting your directory using <TT>slapcat</TT> etc.</P>
+<H3><A NAME="Further Information">11.2.3. Further Information</A></H3>
+<P><EM>slapd-ldif</EM>(5)</P>
+<H2><A NAME="LMDB">11.3. LMDB</A></H2>
+<H3><A NAME="Overview">11.3.1. Overview</A></H3>
+<P>The <EM>mdb</EM> backend to <EM>slapd</EM>(8) is the recommended primary backend for a normal <EM>slapd</EM> database. It uses OpenLDAP's own Lightning Memory-Mapped Database (<TERM>LMDB</TERM>) library to store data and replaces the BerkeleyDB backends used in older OpenLDAP releases.</P>
+<P>It supports indexing, it uses no caching, and requires no tuning to deliver maximum search performance. It is fully hierarchical and supports subtree renames in constant time.</P>
+<H3><A NAME="back-mdb Configuration">11.3.2. back-mdb Configuration</A></H3>
+<P>The <EM>mdb</EM> backend can be instantiated with very few configuration lines:</P>
+<PRE>
+ include ./schema/core.schema
+
+ database mdb
+ directory ./mdb
+ suffix &quot;dc=suretecsystems,dc=com&quot;
+ rootdn &quot;cn=mdb,dc=suretecsystems,dc=com&quot;
+ rootpw mdb
+ maxsize 1073741824
+</PRE>
+<P>In addition to the usual parameters that a minimal configuration requires, the <EM>mdb</EM> backend requires a maximum size to be set. This should be the largest that the database is ever anticipated to grow (in bytes). The filesystem must also provide enough free space to accommodate this size.</P>
+<H3><A NAME="Further Information">11.3.3. Further Information</A></H3>
+<P><EM>slapd-mdb</EM>(5)</P>
+<H2><A NAME="Metadirectory">11.4. Metadirectory</A></H2>
+<H3><A NAME="Overview">11.4.1. Overview</A></H3>
+<P>The meta backend to <EM>slapd</EM>(8) performs basic LDAP proxying with respect to a set of remote LDAP servers, called &quot;targets&quot;. The information contained in these servers can be presented as belonging to a single Directory Information Tree (<TERM>DIT</TERM>).</P>
+<P>A basic knowledge of the functionality of the <EM>slapd-ldap</EM>(5) backend is recommended. This backend has been designed as an enhancement of the ldap backend. The two backends share many features (actually they also share portions of code). While the ldap backend is intended to proxy operations directed to a single server, the meta backend is mainly intended for proxying of multiple servers and possibly naming context masquerading.</P>
+<P>These features, although useful in many scenarios, may result in excessive overhead for some applications, so its use should be carefully considered.</P>
+<H3><A NAME="back-meta Configuration">11.4.2. back-meta Configuration</A></H3>
+<P>LATER</P>
+<H3><A NAME="Further Information">11.4.3. Further Information</A></H3>
+<P><EM>slapd-meta</EM>(5)</P>
+<H2><A NAME="Monitor">11.5. Monitor</A></H2>
+<H3><A NAME="Overview">11.5.1. Overview</A></H3>
+<P>The monitor backend to <EM>slapd</EM>(8) is not an actual database; if enabled, it is automatically generated and dynamically maintained by slapd with information about the running status of the daemon.</P>
+<P>To inspect all monitor information, issue a subtree search with base <EM>cn=Monitor</EM>, requesting that attributes &quot;+&quot; and &quot;*&quot; are returned. The monitor backend produces mostly operational attributes, and LDAP only returns operational attributes that are explicitly requested. Requesting attribute &quot;+&quot; is an extension which requests all operational attributes.</P>
+<P>See the <A HREF="#Monitoring">Monitoring</A> section.</P>
+<H3><A NAME="back-monitor Configuration">11.5.2. back-monitor Configuration</A></H3>
+<P>The monitor database can be instantiated only once, i.e. only one occurrence of &quot;database monitor&quot; can occur in the <EM>slapd.conf(5)</EM> file. Also the suffix is automatically set to <EM>&quot;cn=Monitor&quot;</EM>.</P>
+<P>You can however set a <EM>rootdn</EM> and <EM>rootpw</EM>. The following is all that is needed to instantiate a monitor backend:</P>
+<PRE>
+ include ./schema/core.schema
+
+ database monitor
+ rootdn &quot;cn=monitoring,cn=Monitor&quot;
+ rootpw monitoring
+</PRE>
+<P>You can also apply Access Control to this database like any other database, for example:</P>
+<PRE>
+ access to dn.subtree=&quot;cn=Monitor&quot;
+ by dn.exact=&quot;uid=Admin,dc=my,dc=org&quot; write
+ by users read
+ by * none
+</PRE>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>The <TT>core.schema</TT> must be loaded for the monitor database to work.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>A small example of the data returned via <EM>ldapsearch</EM> would be:</P>
+<PRE>
+ ldapsearch -x -H ldap://localhost:9011 -b 'cn=Monitor'
+ # extended LDIF
+ #
+ # LDAPv3
+ # base &lt;cn=Monitor&gt; with scope subtree
+ # filter: (objectclass=*)
+ # requesting: ALL
+ #
+
+ # Monitor
+ dn: cn=Monitor
+ objectClass: monitorServer
+ cn: Monitor
+ description: This subtree contains monitoring/managing objects.
+ description: This object contains information about this server.
+ description: Most of the information is held in operational attributes, which
+ must be explicitly requested.
+
+ # Backends, Monitor
+ dn: cn=Backends,cn=Monitor
+ objectClass: monitorContainer
+ cn: Backends
+ description: This subsystem contains information about available backends.
+</PRE>
+<P>Please see the <A HREF="#Monitoring">Monitoring</A> section for complete examples of information available via this backend.</P>
+<H3><A NAME="Further Information">11.5.3. Further Information</A></H3>
+<P><EM>slapd-monitor</EM>(5)</P>
+<H2><A NAME="Null">11.6. Null</A></H2>
+<H3><A NAME="Overview">11.6.1. Overview</A></H3>
+<P>The Null backend to <EM>slapd</EM>(8) is surely the most useful part of slapd:</P>
+<UL>
+<LI>Searches return success but no entries.
+<LI>Compares return compareFalse.
+<LI>Updates return success (unless readonly is on) but do nothing.
+<LI>Binds other than as the rootdn fail unless the database option &quot;bind on&quot; is given.
+<LI>The slapadd(8) and slapcat(8) tools are equally exciting.</UL>
+<P>Inspired by the <TT>/dev/null</TT> device.</P>
+<H3><A NAME="back-null Configuration">11.6.2. back-null Configuration</A></H3>
+<P>This has to be one of the shortest configurations you'll ever do. In order to test this, your <TT>slapd.conf</TT> file would look like:</P>
+<PRE>
+ database null
+ suffix &quot;cn=Nothing&quot;
+ bind on
+</PRE>
+<P><EM>bind on</EM> means:</P>
+<P><EM>&quot;Allow binds as any DN in this backend's suffix, with any password. The default is &quot;off&quot;.&quot;</EM></P>
+<P>To test this backend with <EM>ldapsearch</EM>:</P>
+<PRE>
+ ldapsearch -x -H ldap://localhost:9011 -D &quot;uid=none,cn=Nothing&quot; -w testing -b 'cn=Nothing'
+ # extended LDIF
+ #
+ # LDAPv3
+ # base &lt;cn=Nothing&gt; with scope subtree
+ # filter: (objectclass=*)
+ # requesting: ALL
+ #
+
+ # search result
+ search: 2
+ result: 0 Success
+
+ # numResponses: 1
+</PRE>
+<H3><A NAME="Further Information">11.6.3. Further Information</A></H3>
+<P><EM>slapd-null</EM>(5)</P>
+<H2><A NAME="Passwd">11.7. Passwd</A></H2>
+<H3><A NAME="Overview">11.7.1. Overview</A></H3>
+<P>The PASSWD backend to <EM>slapd</EM>(8) serves up the user account information listed in the system <EM>passwd</EM>(5) file (defaulting to <TT>/etc/passwd</TT>).</P>
+<P>This backend is provided for demonstration purposes only. The DN of each entry is &quot;uid=&lt;username&gt;,&lt;suffix&gt;&quot;.</P>
+<H3><A NAME="back-passwd Configuration">11.7.2. back-passwd Configuration</A></H3>
+<P>The configuration using <TT>slapd.conf</TT> a slightly longer, but not much. For example:</P>
+<PRE>
+ include ./schema/core.schema
+
+ database passwd
+ suffix &quot;cn=passwd&quot;
+</PRE>
+<P>Again, testing this with <EM>ldapsearch</EM> would result in something like:</P>
+<PRE>
+ ldapsearch -x -H ldap://localhost:9011 -b 'cn=passwd'
+ # extended LDIF
+ #
+ # LDAPv3
+ # base &lt;cn=passwd&gt; with scope subtree
+ # filter: (objectclass=*)
+ # requesting: ALL
+ #
+
+ # passwd
+ dn: cn=passwd
+ cn: passwd
+ objectClass: organizationalUnit
+
+ # root, passwd
+ dn: uid=root,cn=passwd
+ objectClass: person
+ objectClass: uidObject
+ uid: root
+ cn: root
+ sn: root
+ description: root
+</PRE>
+<H3><A NAME="Further Information">11.7.3. Further Information</A></H3>
+<P><EM>slapd-passwd</EM>(5)</P>
+<H2><A NAME="Perl">11.8. Perl</A></H2>
+<H3><A NAME="Overview">11.8.1. Overview</A></H3>
+<P>The Perl backend to <EM>slapd</EM>(8) works by embedding a <EM>perl</EM>(1) interpreter into <EM>slapd</EM>(8). Any perl database section of the configuration file <EM>slapd.conf</EM>(5) must then specify what Perl module to use. Slapd then creates a new Perl object that handles all the requests for that particular instance of the backend.</P>
+<H3><A NAME="back-perl Configuration">11.8.2. back-perl Configuration</A></H3>
+<P>LATER</P>
+<H3><A NAME="Further Information">11.8.3. Further Information</A></H3>
+<P><EM>slapd-perl</EM>(5)</P>
+<H2><A NAME="Relay">11.9. Relay</A></H2>
+<H3><A NAME="Overview">11.9.1. Overview</A></H3>
+<P>The primary purpose of this <EM>slapd</EM>(8) backend is to map a naming context defined in a database running in the same <EM>slapd</EM>(8) instance into a virtual naming context, with attributeType and objectClass manipulation, if required. It requires the rwm overlay.</P>
+<P>This backend and the above mentioned overlay are experimental.</P>
+<H3><A NAME="back-relay Configuration">11.9.2. back-relay Configuration</A></H3>
+<P>LATER</P>
+<H3><A NAME="Further Information">11.9.3. Further Information</A></H3>
+<P><EM>slapd-relay</EM>(5)</P>
+<H2><A NAME="SQL">11.10. SQL</A></H2>
+<H3><A NAME="Overview">11.10.1. Overview</A></H3>
+<P>The primary purpose of this <EM>slapd</EM>(8) backend is to PRESENT information stored in some RDBMS as an LDAP subtree without any programming (some SQL and maybe stored procedures can't be considered programming, anyway ;).</P>
+<P>That is, for example, when you (some ISP) have account information you use in an RDBMS, and want to use modern solutions that expect such information in LDAP (to authenticate users, make email lookups etc.). Or you want to synchronize or distribute information between different sites/applications that use RDBMSes and/or LDAP. Or whatever else...</P>
+<P>It is <B>NOT</B> designed as a general-purpose backend that uses RDBMS instead of LMDB (as the standard back-mdb backend does), though it can be used as such with several limitations. Please see <A HREF="#LDAP vs RDBMS">LDAP vs RDBMS</A> for discussion.</P>
+<P>The idea is to use some meta-information to translate LDAP queries to SQL queries, leaving relational schema untouched, so that old applications can continue using it without any modifications. This allows SQL and LDAP applications to interoperate without replication, and exchange data as needed.</P>
+<P>The SQL backend is designed to be tunable to virtually any relational schema without having to change source (through that meta-information mentioned). Also, it uses ODBC to connect to RDBMSes, and is highly configurable for SQL dialects RDBMSes may use, so it may be used for integration and distribution of data on different RDBMSes, OSes, hosts etc., in other words, in highly heterogeneous environments.</P>
+<P>This backend is experimental and deprecated.</P>
+<H3><A NAME="back-sql Configuration">11.10.2. back-sql Configuration</A></H3>
+<P>This backend has to be one of the most abused and complex backends there is. Therefore, we will go through a simple, small example that comes with the OpenLDAP source and can be found in <TT>servers/slapd/back-sql/rdbms_depend/README</TT></P>
+<P>For this example we will be using PostgreSQL.</P>
+<P>First, we add to <TT>/etc/odbc.ini</TT> a block of the form:</P>
+<PRE>
+ [example] &lt;===
+ Description = Example for OpenLDAP's back-sql
+ Driver = PostgreSQL
+ Trace = No
+ Database = example &lt;===
+ Servername = localhost
+ UserName = manager &lt;===
+ Password = secret &lt;===
+ Port = 5432
+ ;Protocol = 6.4
+ ReadOnly = No
+ RowVersioning = No
+ ShowSystemTables = No
+ ShowOidColumn = No
+ FakeOidIndex = No
+ ConnSettings =
+</PRE>
+<P>The relevant information for our test setup is highlighted with '&lt;===' on the right above.</P>
+<P>Next, we add to <TT>/etc/odbcinst.ini</TT> a block of the form:</P>
+<PRE>
+ [PostgreSQL]
+ Description = ODBC for PostgreSQL
+ Driver = /usr/lib/libodbcpsql.so
+ Setup = /usr/lib/libodbcpsqlS.so
+ FileUsage = 1
+</PRE>
+<P>We will presume you know how to create a database and user in PostgreSQL and how to set a password. Also, we'll presume you can populate the 'example' database you've just created with the following files, as found in <TT>servers/slapd/back-sql/rdbms_depend/pgsql </TT></P>
+<PRE>
+ backsql_create.sql, testdb_create.sql, testdb_data.sql, testdb_metadata.sql
+</PRE>
+<P>Lastly, run the test:</P>
+<PRE>
+ [root@localhost]# cd $SOURCES/tests
+ [root@localhost]# SLAPD_USE_SQL=pgsql ./run sql-test000
+</PRE>
+<P>Briefly, you should see something like (cut short for space):</P>
+<PRE>
+ Cleaning up test run directory leftover from previous run.
+ Running ./scripts/sql-test000-read...
+ running defines.sh
+ Starting slapd on TCP/IP port 9011...
+ Testing SQL backend read operations...
+ Waiting 5 seconds for slapd to start...
+ Testing correct bind... dn:cn=Mitya Kovalev,dc=example,dc=com
+ Testing incorrect bind (should fail)... ldap_bind: Invalid credentials (49)
+
+ ......
+
+ Filtering original ldif...
+ Comparing filter output...
+ &gt;&gt;&gt;&gt;&gt; Test succeeded
+</PRE>
+<P>The test is basically readonly; this can be performed by all RDBMSes (listed above).</P>
+<P>There is another test, sql-test900-write, which is currently enabled only for PostgreSQL and IBM db2.</P>
+<P>Using <TT>sql-test000</TT>, files in <TT>servers/slapd/back-sql/rdbms_depend/pgsql/</TT> and the man page, you should be set.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>This backend is experimental and deprecated.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H3><A NAME="Further Information">11.10.3. Further Information</A></H3>
+<P><EM>slapd-sql</EM>(5) and <TT>servers/slapd/back-sql/rdbms_depend/README</TT></P>
+<P></P>
+<HR>
+<H1><A NAME="Overlays">12. Overlays</A></H1>
+<P>Overlays are software components that provide hooks to functions analogous to those provided by backends, which can be stacked on top of the backend calls and as callbacks on top of backend responses to alter their behavior.</P>
+<P>Overlays may be compiled statically into <EM>slapd</EM>, or when module support is enabled, they may be dynamically loaded. Most of the overlays are only allowed to be configured on individual databases.</P>
+<P>Some can be stacked on the <TT>frontend</TT> as well, for global use. This means that they can be executed after a request is parsed and validated, but right before the appropriate database is selected. The main purpose is to affect operations regardless of the database they will be handled by, and, in some cases, to influence the selection of the database by massaging the request DN.</P>
+<P>Essentially, overlays represent a means to:</P>
+<UL>
+<LI>customize the behavior of existing backends without changing the backend code and without requiring one to write a new custom backend with complete functionality
+<LI>write functionality of general usefulness that can be applied to different backend types</UL>
+<P>When using <EM>slapd.conf</EM>(5), overlays that are configured before any other databases are considered global, as mentioned above. In fact they are implicitly stacked on top of the <TT>frontend</TT> database. They can also be explicitly configured as such:</P>
+<PRE>
+ database frontend
+ overlay &lt;overlay name&gt;
+</PRE>
+<P>Overlays are usually documented by separate specific man pages in section 5; the naming convention is</P>
+<PRE>
+ slapo-&lt;overlay name&gt;
+</PRE>
+<P>All distributed core overlays have a man page. Feel free to contribute to any, if you think there is anything missing in describing the behavior of the component and the implications of all the related configuration directives.</P>
+<P>Official overlays are located in</P>
+<PRE>
+ servers/slapd/overlays/
+</PRE>
+<P>That directory also contains the file slapover.txt, which describes the rationale of the overlay implementation, and may serve as a guideline for the development of custom overlays.</P>
+<P>Contribware overlays are located in</P>
+<PRE>
+ contrib/slapd-modules/&lt;overlay name&gt;/
+</PRE>
+<P>along with other types of run-time loadable components; they are officially distributed, but not maintained by the project.</P>
+<P>All the current overlays in OpenLDAP are listed and described in detail in the following sections.</P>
+<H2><A NAME="Access Logging">12.1. Access Logging</A></H2>
+<H3><A NAME="Overview">12.1.1. Overview</A></H3>
+<P>This overlay can record accesses to a given backend database on another database.</P>
+<P>This allows all of the activity on a given database to be reviewed using arbitrary LDAP queries, instead of just logging to local flat text files. Configuration options are available for selecting a subset of operation types to log, and to automatically prune older log records from the logging database. Log records are stored with audit schema to assure their readability whether viewed as LDIF or in raw form.</P>
+<P>It is also used for <A HREF="#delta-syncrepl replication">delta-syncrepl replication</A></P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>An accesslog database is unique to a given provider. It should never be replicated.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H3><A NAME="Access Logging Configuration">12.1.2. Access Logging Configuration</A></H3>
+<P>The following is a basic example that implements Access Logging:</P>
+<PRE>
+ database mdb
+ suffix dc=example,dc=com
+ ...
+ overlay accesslog
+ logdb cn=log
+ logops writes reads
+ logold (objectclass=person)
+
+ database mdb
+ suffix cn=log
+ ...
+ index reqStart eq
+ access to *
+ by dn.base=&quot;cn=admin,dc=example,dc=com&quot; read
+</PRE>
+<P>The following is an example used for <A HREF="#delta-syncrepl replication">delta-syncrepl replication</A>:</P>
+<PRE>
+ database mdb
+ suffix cn=accesslog
+ directory /usr/local/var/openldap-accesslog
+ rootdn cn=accesslog
+ index default eq
+ index entryCSN,objectClass,reqEnd,reqResult,reqStart,reqDN
+</PRE>
+<P>Accesslog overlay definitions for the primary db</P>
+<PRE>
+ database mdb
+ suffix dc=example,dc=com
+ ...
+ overlay accesslog
+ logdb cn=accesslog
+ logops writes
+ logsuccess TRUE
+ # scan the accesslog DB every day, and purge entries older than 7 days
+ logpurge 07+00:00 01+00:00
+</PRE>
+<P>An example search result against <B>cn=accesslog</B> might look like:</P>
+<PRE>
+ [ghenry@suretec ghenry]# ldapsearch -x -b cn=accesslog
+ # extended LDIF
+ #
+ # LDAPv3
+ # base &lt;cn=accesslog&gt; with scope subtree
+ # filter: (objectclass=*)
+ # requesting: ALL
+ #
+
+ # accesslog
+ dn: cn=accesslog
+ objectClass: auditContainer
+ cn: accesslog
+
+ # 20080110163829.000004Z, accesslog
+ dn: reqStart=20080110163829.000004Z,cn=accesslog
+ objectClass: auditModify
+ reqStart: 20080110163829.000004Z
+ reqEnd: 20080110163829.000005Z
+ reqType: modify
+ reqSession: 196696
+ reqAuthzID: cn=admin,dc=suretecsystems,dc=com
+ reqDN: uid=suretec-46022f8$,ou=Users,dc=suretecsystems,dc=com
+ reqResult: 0
+ reqMod: sambaPwdCanChange:- ###CENSORED###
+ reqMod: sambaPwdCanChange:+ ###CENSORED###
+ reqMod: sambaNTPassword:- ###CENSORED###
+ reqMod: sambaNTPassword:+ ###CENSORED###
+ reqMod: sambaPwdLastSet:- ###CENSORED###
+ reqMod: sambaPwdLastSet:+ ###CENSORED###
+ reqMod: entryCSN:= 20080110163829.095157Z#000000#000#000000
+ reqMod: modifiersName:= cn=admin,dc=suretecsystems,dc=com
+ reqMod: modifyTimestamp:= 20080110163829Z
+
+ # search result
+ search: 2
+ result: 0 Success
+
+ # numResponses: 3
+ # numEntries: 2
+</PRE>
+<H3><A NAME="Further Information">12.1.3. Further Information</A></H3>
+<P><EM>slapo-accesslog(5)</EM> and the <A HREF="#delta-syncrepl replication">delta-syncrepl replication</A> section.</P>
+<H2><A NAME="Audit Logging">12.2. Audit Logging</A></H2>
+<P>The Audit Logging overlay can be used to record all changes on a given backend database to a specified log file.</P>
+<H3><A NAME="Overview">12.2.1. Overview</A></H3>
+<P>If the need arises whereby changes need to be logged as standard LDIF, then the auditlog overlay <B>slapo-auditlog (5)</B> can be used. Full examples are available in the man page <B>slapo-auditlog (5)</B></P>
+<H3><A NAME="Audit Logging Configuration">12.2.2. Audit Logging Configuration</A></H3>
+<P>If the directory is running vi <TT>slapd.d</TT>, then the following LDIF could be used to add the overlay to the overlay list in <B>cn=config</B> and set what file the <TERM>LDIF</TERM> gets logged to (adjust to suit)</P>
+<PRE>
+ dn: olcOverlay=auditlog,olcDatabase={1}mdb,cn=config
+ changetype: add
+ objectClass: olcOverlayConfig
+ objectClass: olcAuditLogConfig
+ olcOverlay: auditlog
+ olcAuditlogFile: /tmp/auditlog.ldif
+</PRE>
+<P>In this example for testing, we are logging changes to <TT>/tmp/auditlog.ldif</TT></P>
+<P>A typical <TERM>LDIF</TERM> file created by <B>slapo-auditlog(5)</B> would look like:</P>
+<PRE>
+ # add 1196797576 dc=suretecsystems,dc=com cn=admin,dc=suretecsystems,dc=com
+ dn: dc=suretecsystems,dc=com
+ changetype: add
+ objectClass: dcObject
+ objectClass: organization
+ dc: suretecsystems
+ o: Suretec Systems Ltd.
+ structuralObjectClass: organization
+ entryUUID: 1606f8f8-f06e-1029-8289-f0cc9d81e81a
+ creatorsName: cn=admin,dc=suretecsystems,dc=com
+ modifiersName: cn=admin,dc=suretecsystems,dc=com
+ createTimestamp: 20051123130912Z
+ modifyTimestamp: 20051123130912Z
+ entryCSN: 20051123130912.000000Z#000001#000#000000
+ auditContext: cn=accesslog
+ # end add 1196797576
+
+ # add 1196797577 dc=suretecsystems,dc=com cn=admin,dc=suretecsystems,dc=com
+ dn: ou=Groups,dc=suretecsystems,dc=com
+ changetype: add
+ objectClass: top
+ objectClass: organizationalUnit
+ ou: Groups
+ structuralObjectClass: organizationalUnit
+ entryUUID: 160aaa2a-f06e-1029-828a-f0cc9d81e81a
+ creatorsName: cn=admin,dc=suretecsystems,dc=com
+ modifiersName: cn=admin,dc=suretecsystems,dc=com
+ createTimestamp: 20051123130912Z
+ modifyTimestamp: 20051123130912Z
+ entryCSN: 20051123130912.000000Z#000002#000#000000
+ # end add 1196797577
+</PRE>
+<H3><A NAME="Further Information">12.2.3. Further Information</A></H3>
+<P><EM>slapo-auditlog(5)</EM></P>
+<H2><A NAME="Chaining">12.3. Chaining</A></H2>
+<H3><A NAME="Overview">12.3.1. Overview</A></H3>
+<P>The chain overlay provides basic chaining capability to the underlying database.</P>
+<P>What is chaining? It indicates the capability of a DSA to follow referrals on behalf of the client, so that distributed systems are viewed as a single virtual DSA by clients that are otherwise unable to &quot;chase&quot; (i.e. follow) referrals by themselves.</P>
+<P>The chain overlay is built on top of the ldap backend; it is compiled by default when <B>--enable-ldap</B>.</P>
+<H3><A NAME="Chaining Configuration">12.3.2. Chaining Configuration</A></H3>
+<P>In order to demonstrate how this overlay works, we shall discuss a typical scenario which might be one provider server and three Syncrepl replicas.</P>
+<P>On each replica, add this near the top of the <EM>slapd.conf</EM>(5) file (global), before any database definitions:</P>
+<PRE>
+ overlay chain
+ chain-uri &quot;ldap://ldapprovider.example.com&quot;
+ chain-idassert-bind bindmethod=&quot;simple&quot;
+ binddn=&quot;cn=Manager,dc=example,dc=com&quot;
+ credentials=&quot;&lt;secret&gt;&quot;
+ mode=&quot;self&quot;
+ chain-tls start
+ chain-return-error TRUE
+</PRE>
+<P>Add this below your <EM>syncrepl</EM> statement:</P>
+<PRE>
+ updateref &quot;ldap://ldapprovider.example.com/&quot;
+</PRE>
+<P>The <B>chain-tls</B> statement enables TLS from the replica to the ldap provider. The DITs are exactly the same between these machines, therefore whatever user bound to the replica will also exist on the provider. If that DN does not have update privileges on the provider, nothing will happen.</P>
+<P>You will need to restart the replica after these <EM>slapd.conf</EM> changes. Then, if you are using <EM>loglevel stats</EM> (256), you can monitor an <EM>ldapmodify</EM> on the replica and the provider. (If you're using <EM>cn=config</EM> no restart is required.)</P>
+<P>Now start an <EM>ldapmodify</EM> on the replica and watch the logs. You should expect something like:</P>
+<PRE>
+ Sep 6 09:27:25 replica1 slapd[29274]: conn=11 fd=31 ACCEPT from IP=143.199.102.216:45181 (IP=143.199.102.216:389)
+ Sep 6 09:27:25 replica1 slapd[29274]: conn=11 op=0 STARTTLS
+ Sep 6 09:27:25 replica1 slapd[29274]: conn=11 op=0 RESULT oid= err=0 text=
+ Sep 6 09:27:25 replica1 slapd[29274]: conn=11 fd=31 TLS established tls_ssf=256 ssf=256
+ Sep 6 09:27:28 replica1 slapd[29274]: conn=11 op=1 BIND dn=&quot;uid=user1,ou=people,dc=example,dc=com&quot; method=128
+ Sep 6 09:27:28 replica1 slapd[29274]: conn=11 op=1 BIND dn=&quot;uid=user1,ou=People,dc=example,dc=com&quot; mech=SIMPLE ssf=0
+ Sep 6 09:27:28 replica1 slapd[29274]: conn=11 op=1 RESULT tag=97 err=0 text=
+ Sep 6 09:27:28 replica1 slapd[29274]: conn=11 op=2 MOD dn=&quot;uid=user1,ou=People,dc=example,dc=com&quot;
+ Sep 6 09:27:28 replica1 slapd[29274]: conn=11 op=2 MOD attr=mail
+ Sep 6 09:27:28 replica1 slapd[29274]: conn=11 op=2 RESULT tag=103 err=0 text=
+ Sep 6 09:27:28 replica1 slapd[29274]: conn=11 op=3 UNBIND
+ Sep 6 09:27:28 replica1 slapd[29274]: conn=11 fd=31 closed
+ Sep 6 09:27:28 replica1 slapd[29274]: syncrepl_entry: LDAP_RES_SEARCH_ENTRY(LDAP_SYNC_MODIFY)
+ Sep 6 09:27:28 replica1 slapd[29274]: syncrepl_entry: be_search (0)
+ Sep 6 09:27:28 replica1 slapd[29274]: syncrepl_entry: uid=user1,ou=People,dc=example,dc=com
+ Sep 6 09:27:28 replica1 slapd[29274]: syncrepl_entry: be_modify (0)
+</PRE>
+<P>And on the provider you will see this:</P>
+<PRE>
+ Sep 6 09:23:57 ldapprovider slapd[2961]: conn=55902 op=3 PROXYAUTHZ dn=&quot;uid=user1,ou=people,dc=example,dc=com&quot;
+ Sep 6 09:23:57 ldapprovider slapd[2961]: conn=55902 op=3 MOD dn=&quot;uid=user1,ou=People,dc=example,dc=com&quot;
+ Sep 6 09:23:57 ldapprovider slapd[2961]: conn=55902 op=3 MOD attr=mail
+ Sep 6 09:23:57 ldapprovider slapd[2961]: conn=55902 op=3 RESULT tag=103 err=0 text=
+</PRE>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>You can clearly see the PROXYAUTHZ line on the provider, indicating the proper identity assertion for the update on the provider. Also note the replica immediately receiving the Syncrepl update from the provider.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H3><A NAME="Handling Chaining Errors">12.3.3. Handling Chaining Errors</A></H3>
+<P>By default, if chaining fails, the original referral is returned to the client under the assumption that the client might want to try and follow the referral.</P>
+<P>With the following directive however, if the chaining fails at the provider side, the actual error is returned to the client.</P>
+<PRE>
+ chain-return-error TRUE
+</PRE>
+<H3><A NAME="Read-Back of Chained Modifications">12.3.4. Read-Back of Chained Modifications</A></H3>
+<P>Occasionally, applications want to read back the data that they just wrote. If a modification requested to a shadow server was silently chained to its provider, an immediate read could result in receiving data not yet synchronized. In those cases, clients should use the <B>dontusecopy</B> control to ensure they are directed to the authoritative source for that piece of data.</P>
+<P>This control usually causes a referral to the actual source of the data to be returned. However, when the <EM>slapo-chain(5)</EM> overlay is used, it intercepts the referral being returned in response to the <B>dontusecopy</B> control, and tries to fetch the requested data.</P>
+<H3><A NAME="Further Information">12.3.5. Further Information</A></H3>
+<P><EM>slapo-chain(5)</EM></P>
+<H2><A NAME="Constraints">12.4. Constraints</A></H2>
+<H3><A NAME="Overview">12.4.1. Overview</A></H3>
+<P>This overlay enforces a regular expression constraint on all values of specified attributes during an LDAP modify request that contains add or modify commands. It is used to enforce a more rigorous syntax when the underlying attribute syntax is too general.</P>
+<H3><A NAME="Constraint Configuration">12.4.2. Constraint Configuration</A></H3>
+<P>Configuration via <EM>slapd.conf</EM>(5) would look like:</P>
+<PRE>
+ overlay constraint
+ constraint_attribute mail regex ^[[:alnum:]]+@mydomain.com$
+ constraint_attribute title uri
+ ldap:///dc=catalog,dc=example,dc=com?title?sub?(objectClass=titleCatalog)
+</PRE>
+<P>A specification like the above would reject any <EM>mail</EM> attribute which did not look like <EM>&lt;alphanumeric string&gt;@mydomain.com</EM>.</P>
+<P>It would also reject any title attribute whose values were not listed in the title attribute of any <EM>titleCatalog</EM> entries in the given scope.</P>
+<P>An example for use with <EM>cn=config</EM>:</P>
+<PRE>
+ dn: olcOverlay=constraint,olcDatabase={1}mdb,cn=config
+ changetype: add
+ objectClass: olcOverlayConfig
+ objectClass: olcConstraintConfig
+ olcOverlay: constraint
+ olcConstraintAttribute: mail regex ^[[:alnum:]]+@mydomain.com$
+ olcConstraintAttribute: title uri ldap:///dc=catalog,dc=example,dc=com?title?sub?(objectClass=titleCatalog)
+</PRE>
+<H3><A NAME="Further Information">12.4.3. Further Information</A></H3>
+<P><EM>slapo-constraint(5)</EM></P>
+<H2><A NAME="Dynamic Directory Services">12.5. Dynamic Directory Services</A></H2>
+<H3><A NAME="Overview">12.5.1. Overview</A></H3>
+<P>The <EM>dds</EM> overlay to <EM>slapd</EM>(8) implements dynamic objects as per <A HREF="https://www.rfc-editor.org/rfc/rfc2589.txt">RFC2589</A>. The name <EM>dds</EM> stands for Dynamic Directory Services. It allows to define dynamic objects, characterized by the <EM>dynamicObject</EM> objectClass.</P>
+<P>Dynamic objects have a limited lifetime, determined by a time-to-live (TTL) that can be refreshed by means of a specific refresh extended operation. This operation allows to set the Client Refresh Period (CRP), namely the period between refreshes that is required to preserve the dynamic object from expiration. The expiration time is computed by adding the requested TTL to the current time. When dynamic objects reach the end of their lifetime without being further refreshed, they are automatically <EM>deleted</EM>. There is no guarantee of immediate deletion, so clients should not count on it.</P>
+<H3><A NAME="Dynamic Directory Service Configuration">12.5.2. Dynamic Directory Service Configuration</A></H3>
+<P>A usage of dynamic objects might be to implement dynamic meetings; in this case, all the participants to the meeting are allowed to refresh the meeting object, but only the creator can delete it (otherwise it will be deleted when the TTL expires).</P>
+<P>If we add the overlay to an example database, specifying a Max TTL of 1 day, a min of 10 seconds, with a default TTL of 1 hour. We'll also specify an interval of 120 (less than 60s might be too small) seconds between expiration checks and a tolerance of 5 second (lifetime of a dynamic object will be <EM>entryTtl + tolerance</EM>).</P>
+<PRE>
+ overlay dds
+ dds-max-ttl 1d
+ dds-min-ttl 10s
+ dds-default-ttl 1h
+ dds-interval 120s
+ dds-tolerance 5s
+</PRE>
+<P>and add an index:</P>
+<PRE>
+ entryExpireTimestamp
+</PRE>
+<P>Creating a meeting is as simple as adding the following:</P>
+<PRE>
+ dn: cn=OpenLDAP Documentation Meeting,ou=Meetings,dc=example,dc=com
+ objectClass: groupOfNames
+ objectClass: dynamicObject
+ cn: OpenLDAP Documentation Meeting
+ member: uid=ghenry,ou=People,dc=example,dc=com
+ member: uid=hyc,ou=People,dc=example,dc=com
+</PRE>
+<H4><A NAME="Dynamic Directory Service ACLs">12.5.2.1. Dynamic Directory Service ACLs</A></H4>
+<P>Allow users to start a meeting and to join it; restrict refresh to the <EM>member</EM>; restrict delete to the creator:</P>
+<PRE>
+ access to attrs=userPassword
+ by self write
+ by * read
+
+ access to dn.base=&quot;ou=Meetings,dc=example,dc=com&quot;
+ attrs=children
+ by users write
+
+ access to dn.onelevel=&quot;ou=Meetings,dc=example,dc=com&quot;
+ attrs=entry
+ by dnattr=creatorsName write
+ by * read
+
+ access to dn.onelevel=&quot;ou=Meetings,dc=example,dc=com&quot;
+ attrs=participant
+ by dnattr=creatorsName write
+ by users selfwrite
+ by * read
+
+ access to dn.onelevel=&quot;ou=Meetings,dc=example,dc=com&quot;
+ attrs=entryTtl
+ by dnattr=member manage
+ by * read
+</PRE>
+<P>In simple terms, the user who created the <EM>OpenLDAP Documentation Meeting</EM> can add new attendees, refresh the meeting using (basically complete control):</P>
+<PRE>
+ ldapexop -x -H ldap://ldaphost &quot;refresh&quot; &quot;cn=OpenLDAP Documentation Meeting,ou=Meetings,dc=example,dc=com&quot; &quot;120&quot; -D &quot;uid=ghenry,ou=People,dc=example,dc=com&quot; -W
+</PRE>
+<P>Any user can join the meeting, but not add another attendee, but they can refresh the meeting. The ACLs above are quite straight forward to understand.</P>
+<H3><A NAME="Further Information">12.5.3. Further Information</A></H3>
+<P><EM>slapo-dds(5)</EM></P>
+<H2><A NAME="Dynamic Groups">12.6. Dynamic Groups</A></H2>
+<H3><A NAME="Overview">12.6.1. Overview</A></H3>
+<P>This overlay extends the Compare operation to detect members of a dynamic group. This overlay is now deprecated as all of its functions are available using the <A HREF="#Dynamic Lists">Dynamic Lists</A> overlay.</P>
+<H3><A NAME="Dynamic Group Configuration">12.6.2. Dynamic Group Configuration</A></H3>
+<H2><A NAME="Dynamic Lists">12.7. Dynamic Lists</A></H2>
+<H3><A NAME="Overview">12.7.1. Overview</A></H3>
+<P>This overlay allows expansion of dynamic groups and lists. Instead of having the group members or list attributes hard coded, this overlay allows us to define an LDAP search whose results will make up the group or list.</P>
+<H3><A NAME="Dynamic List Configuration">12.7.2. Dynamic List Configuration</A></H3>
+<P>This module can behave both as a dynamic list and dynamic group, depending on the configuration. The syntax is as follows:</P>
+<PRE>
+ overlay dynlist
+ dynlist-attrset &lt;group-oc&gt; &lt;URL-ad&gt; [member-ad]
+</PRE>
+<P>The parameters to the <TT>dynlist-attrset</TT> directive have the following meaning:</P>
+<UL>
+<LI><TT>&lt;group-oc&gt;</TT>: specifies which object class triggers the subsequent LDAP search. Whenever an entry with this object class is retrieved, the search is performed.
+<LI><TT>&lt;URL-ad&gt;</TT>: is the name of the attribute which holds the search URI. It has to be a subtype of <TT>labeledURI</TT>. The attributes and values present in the search result are added to the entry unless <TT>member-ad</TT> is used (see below).
+<LI><TT>member-ad</TT>: if present, changes the overlay behavior into a dynamic group. Instead of inserting the results of the search in the entry, the distinguished name of the results are added as values of this attribute.</UL>
+<P>Here is an example which will allow us to have an email alias which automatically expands to all user's emails according to our LDAP filter:</P>
+<P>In <EM>slapd.conf</EM>(5):</P>
+<PRE>
+ overlay dynlist
+ dynlist-attrset nisMailAlias labeledURI
+</PRE>
+<P>This means that whenever an entry which has the <TT>nisMailAlias</TT> object class is retrieved, the search specified in the <TT>labeledURI</TT> attribute is performed.</P>
+<P>Let's say we have this entry in our directory:</P>
+<PRE>
+ cn=all,ou=aliases,dc=example,dc=com
+ cn: all
+ objectClass: nisMailAlias
+ labeledURI: ldap:///ou=People,dc=example,dc=com?mail?one?(objectClass=inetOrgPerson)
+</PRE>
+<P>If this entry is retrieved, the search specified in <TT>labeledURI</TT> will be performed and the results will be added to the entry just as if they have always been there. In this case, the search filter selects all entries directly under <TT>ou=People</TT> that have the <TT>inetOrgPerson</TT> object class and retrieves the <TT>mail</TT> attribute, if it exists.</P>
+<P>This is what gets added to the entry when we have two users under <TT>ou=People</TT> that match the filter:</P>
+<P><CENTER><IMG SRC="allmail-en.png" ALIGN="center"></CENTER></P>
+<P ALIGN="Center">Figure X.Y: Dynamic List for all emails</P>
+<P>The configuration for a dynamic group is similar. Let's see an example which would automatically populate an <TT>allusers</TT> group with all the user accounts in the directory.</P>
+<P>In <TT>slapd.conf</TT>(5):</P>
+<PRE>
+ include /path/to/dyngroup.schema
+ ...
+ overlay dynlist
+ dynlist-attrset groupOfURLs labeledURI member
+</PRE>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>We must include the <TT>dyngroup.schema</TT> file that defines the <TT>groupOfURLs</TT> objectClass used in this example.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>Let's apply it to the following entry:</P>
+<PRE>
+ cn=allusers,ou=group,dc=example,dc=com
+ cn: all
+ objectClass: groupOfURLs
+ labeledURI: ldap:///ou=people,dc=example,dc=com??one?(objectClass=inetOrgPerson)
+</PRE>
+<P>The behavior is similar to the dynamic list configuration we had before: whenever an entry with the <TT>groupOfURLs</TT> object class is retrieved, the search specified in the <TT>labeledURI</TT> attribute is performed. But this time, only the distinguished names of the results are added, and as values of the <TT>member</TT> attribute.</P>
+<P>This is what we get:</P>
+<P><CENTER><IMG SRC="allusersgroup-en.png" ALIGN="center"></CENTER></P>
+<P ALIGN="Center">Figure X.Y: Dynamic Group for all users</P>
+<P>Note that a side effect of this scheme of dynamic groups is that the members need to be specified as full DNs. So, if you are planning in using this for <TT>posixGroup</TT>s, be sure to use RFC2307bis and some attribute which can hold distinguished names. The <TT>memberUid</TT> attribute used in the <TT>posixGroup</TT> object class can hold only names, not DNs, and is therefore not suitable for dynamic groups.</P>
+<H3><A NAME="Further Information">12.7.3. Further Information</A></H3>
+<P><EM>slapo-dynlist(5)</EM></P>
+<H2><A NAME="Reverse Group Membership Maintenance">12.8. Reverse Group Membership Maintenance</A></H2>
+<H3><A NAME="Overview">12.8.1. Overview</A></H3>
+<P>In some scenarios, it may be desirable for a client to be able to determine which groups an entry is a member of, without performing an additional search. Examples of this are applications using the <TERM>DIT</TERM> for access control based on group authorization.</P>
+<P>The <B>memberof</B> overlay updates an attribute (by default <B>memberOf</B>) whenever changes occur to the membership attribute (by default <B>member</B>) of entries of the objectclass (by default <B>groupOfNames</B>) configured to trigger updates.</P>
+<P>Thus, it provides maintenance of the list of groups an entry is a member of, when usual maintenance of groups is done by modifying the members on the group entry.</P>
+<H3><A NAME="Member Of Configuration">12.8.2. Member Of Configuration</A></H3>
+<P>The typical use of this overlay requires just enabling the overlay for a specific database. For example, with the following minimal slapd.conf:</P>
+<PRE>
+ include /usr/share/openldap/schema/core.schema
+ include /usr/share/openldap/schema/cosine.schema
+
+ authz-regexp &quot;gidNumber=0\\\+uidNumber=0,cn=peercred,cn=external,cn=auth&quot;
+ &quot;cn=Manager,dc=example,dc=com&quot;
+ database mdb
+ suffix &quot;dc=example,dc=com&quot;
+ rootdn &quot;cn=Manager,dc=example,dc=com&quot;
+ rootpw secret
+ directory /var/lib/ldap2.5
+ checkpoint 256 5
+ index objectClass eq
+ index uid eq,sub
+
+ overlay memberof
+</PRE>
+<P>adding the following ldif:</P>
+<PRE>
+ cat memberof.ldif
+ dn: dc=example,dc=com
+ objectclass: domain
+ dc: example
+
+ dn: ou=Group,dc=example,dc=com
+ objectclass: organizationalUnit
+ ou: Group
+
+ dn: ou=People,dc=example,dc=com
+ objectclass: organizationalUnit
+ ou: People
+
+ dn: uid=test1,ou=People,dc=example,dc=com
+ objectclass: account
+ uid: test1
+
+ dn: cn=testgroup,ou=Group,dc=example,dc=com
+ objectclass: groupOfNames
+ cn: testgroup
+ member: uid=test1,ou=People,dc=example,dc=com
+</PRE>
+<P>Results in the following output from a search on the test1 user:</P>
+<PRE>
+ # ldapsearch -LL -Y EXTERNAL -H ldapi:/// &quot;(uid=test1)&quot; -b dc=example,dc=com memberOf
+ SASL/EXTERNAL authentication started
+ SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
+ SASL SSF: 0
+ version: 1
+
+ dn: uid=test1,ou=People,dc=example,dc=com
+ memberOf: cn=testgroup,ou=Group,dc=example,dc=com
+</PRE>
+<P>Note that the <B>memberOf</B> attribute is an operational attribute, so it must be requested explicitly.</P>
+<H3><A NAME="Further Information">12.8.3. Further Information</A></H3>
+<P><EM>slapo-memberof(5)</EM></P>
+<H2><A NAME="The Proxy Cache Engine">12.9. The Proxy Cache Engine</A></H2>
+<P><TERM>LDAP</TERM> servers typically hold one or more subtrees of a <TERM>DIT</TERM>. Replica (or shadow) servers hold shadow copies of entries held by one or more provider servers. Changes are propagated from the provider server to replica servers using LDAP Sync replication. An LDAP cache is a special type of replica which holds entries corresponding to search filters instead of subtrees.</P>
+<H3><A NAME="Overview">12.9.1. Overview</A></H3>
+<P>The proxy cache extension of slapd is designed to improve the responsiveness of the ldap and meta backends. It handles a search request (query) by first determining whether it is contained in any cached search filter. Contained requests are answered from the proxy cache's local database. Other requests are passed on to the underlying ldap or meta backend and processed as usual.</P>
+<P>E.g. <TT>(shoesize&gt;=9)</TT> is contained in <TT>(shoesize&gt;=8)</TT> and <TT>(sn=Richardson)</TT> is contained in <TT>(sn=Richards*)</TT></P>
+<P>Correct matching rules and syntaxes are used while comparing assertions for query containment. To simplify the query containment problem, a list of cacheable &quot;templates&quot; (defined below) is specified at configuration time. A query is cached or answered only if it belongs to one of these templates. The entries corresponding to cached queries are stored in the proxy cache local database while its associated meta information (filter, scope, base, attributes) is stored in main memory.</P>
+<P>A template is a prototype for generating LDAP search requests. Templates are described by a prototype search filter and a list of attributes which are required in queries generated from the template. The representation for prototype filter is similar to <A HREF="https://www.rfc-editor.org/rfc/rfc4515.txt">RFC4515</A>, except that the assertion values are missing. Examples of prototype filters are: (sn=),(&amp;(sn=)(givenname=)) which are instantiated by search filters (sn=Doe) and (&amp;(sn=Doe)(givenname=John)) respectively.</P>
+<P>The cache replacement policy removes the least recently used (LRU) query and entries belonging to only that query. Queries are allowed a maximum time to live (TTL) in the cache thus providing weak consistency. A background task periodically checks the cache for expired queries and removes them.</P>
+<P>The Proxy Cache paper (<A HREF="http://www.openldap.org/pub/kapurva/proxycaching.pdf">http://www.openldap.org/pub/kapurva/proxycaching.pdf</A>) provides design and implementation details.</P>
+<H3><A NAME="Proxy Cache Configuration">12.9.2. Proxy Cache Configuration</A></H3>
+<P>The cache configuration specific directives described below must appear after a <TT>overlay pcache</TT> directive within a <TT>&quot;database meta&quot;</TT> or <TT>&quot;database ldap&quot;</TT> section of the server's <EM>slapd.conf</EM>(5) file.</P>
+<H4><A NAME="Setting cache parameters">12.9.2.1. Setting cache parameters</A></H4>
+<PRE>
+ pcache &lt;DB&gt; &lt;maxentries&gt; &lt;nattrsets&gt; &lt;entrylimit&gt; &lt;period&gt;
+</PRE>
+<P>This directive enables proxy caching and sets general cache parameters. The &lt;DB&gt; parameter specifies which underlying database is to be used to hold cached entries. It should be set to <TT>mdb</TT>. The &lt;maxentries&gt; parameter specifies the total number of entries which may be held in the cache. The &lt;nattrsets&gt; parameter specifies the total number of attribute sets (as specified by the <TT>pcacheAttrset</TT> directive) that may be defined. The &lt;entrylimit&gt; parameter specifies the maximum number of entries in a cacheable query. The &lt;period&gt; specifies the consistency check period (in seconds). In each period, queries with expired TTLs are removed.</P>
+<H4><A NAME="Defining attribute sets">12.9.2.2. Defining attribute sets</A></H4>
+<PRE>
+ pcacheAttrset &lt;index&gt; &lt;attrs...&gt;
+</PRE>
+<P>Used to associate a set of attributes to an index. Each attribute set is associated with an index number from 0 to &lt;numattrsets&gt;-1. These indices are used by the pcacheTemplate directive to define cacheable templates.</P>
+<H4><A NAME="Specifying cacheable templates">12.9.2.3. Specifying cacheable templates</A></H4>
+<PRE>
+ pcacheTemplate &lt;prototype_string&gt; &lt;attrset_index&gt; &lt;TTL&gt;
+</PRE>
+<P>Specifies a cacheable template and the &quot;time to live&quot; (in sec) &lt;TTL&gt; for queries belonging to the template. A template is described by its prototype filter string and set of required attributes identified by &lt;attrset_index&gt;.</P>
+<H4><A NAME="Example for slapd.conf">12.9.2.4. Example for slapd.conf</A></H4>
+<P>An example <EM>slapd.conf</EM>(5) database section for a caching server which proxies for the <TT>&quot;dc=example,dc=com&quot;</TT> subtree held at server <TT>ldap.example.com</TT>.</P>
+<PRE>
+ database ldap
+ suffix &quot;dc=example,dc=com&quot;
+ rootdn &quot;dc=example,dc=com&quot;
+ uri ldap://ldap.example.com/
+ overlay pcache
+ pcache mdb 100000 1 1000 100
+ pcacheAttrset 0 mail postaladdress telephonenumber
+ pcacheTemplate (sn=) 0 3600
+ pcacheTemplate (&amp;(sn=)(givenName=)) 0 3600
+ pcacheTemplate (&amp;(departmentNumber=)(secretary=*)) 0 3600
+
+ cachesize 20
+ directory ./testrun/db.2.a
+ index objectClass eq
+ index cn,sn,uid,mail pres,eq,sub
+</PRE>
+<H4><A NAME="Example for slapd-config">12.9.2.5. Example for slapd-config</A></H4>
+<P>The same example as a LDIF file for back-config for a caching server which proxies for the <TT>&quot;dc=example,dc=com&quot;</TT> subtree held at server <TT>ldap.example.com</TT>.</P>
+<PRE>
+ dn: olcDatabase={2}ldap,cn=config
+ objectClass: olcDatabaseConfig
+ objectClass: olcLDAPConfig
+ olcDatabase: {2}ldap
+ olcSuffix: dc=example,dc=com
+ olcRootDN: dc=example,dc=com
+ olcDbURI: &quot;ldap://ldap.example.com&quot;
+
+ dn: olcOverlay={0}pcache,olcDatabase={2}ldap,cn=config
+ objectClass: olcOverlayConfig
+ objectClass: olcPcacheConfig
+ olcOverlay: {0}pcache
+ olcPcache: mdb 100000 1 1000 100
+ olcPcacheAttrset: 0 mail postalAddress telephoneNumber
+ olcPcacheTemplate: &quot;(sn=)&quot; 0 3600 0 0 0
+ olcPcacheTemplate: &quot;(&amp;(sn=)(givenName=))&quot; 0 3600 0 0 0
+ olcPcacheTemplate: &quot;(&amp;(departmentNumber=)(secretary=))&quot; 0 3600
+
+ dn: olcDatabase={0}mdb,olcOverlay={0}pcache,olcDatabase={2}ldap,cn=config
+ objectClass: olcMdbConfig
+ objectClass: olcPcacheDatabase
+ olcDatabase: {0}mdb
+ olcDbDirectory: ./testrun/db.2.a
+ olcDbCacheSize: 20
+ olcDbIndex: objectClass eq
+ olcDbIndex: cn,sn,uid,mail pres,eq,sub
+</PRE>
+<H5><A NAME="Cacheable Queries">12.9.2.5.1. Cacheable Queries</A></H5>
+<P>A LDAP search query is cacheable when its filter matches one of the templates as defined in the &quot;pcacheTemplate&quot; statements and when it references only the attributes specified in the corresponding attribute set. In the example above the attribute set number 0 defines that only the attributes: <TT>mail postaladdress telephonenumber</TT> are cached for the following pcacheTemplates.</P>
+<H5><A NAME="Examples:">12.9.2.5.2. Examples:</A></H5>
+<PRE>
+ Filter: (&amp;(sn=Richard*)(givenName=jack))
+ Attrs: mail telephoneNumber
+</PRE>
+<P>is cacheable, because it matches the template <TT>(&amp;(sn=)(givenName=))</TT> and its attributes are contained in pcacheAttrset 0.</P>
+<PRE>
+ Filter: (&amp;(sn=Richard*)(telephoneNumber))
+ Attrs: givenName
+</PRE>
+<P>is not cacheable, because the filter does not match the template, nor is the attribute givenName stored in the cache</P>
+<PRE>
+ Filter: (|(sn=Richard*)(givenName=jack))
+ Attrs: mail telephoneNumber
+</PRE>
+<P>is not cacheable, because the filter does not match the template ( logical OR &quot;|&quot; condition instead of logical AND &quot;&amp;&quot; )</P>
+<H3><A NAME="Further Information">12.9.3. Further Information</A></H3>
+<P><EM>slapo-pcache(5)</EM></P>
+<H2><A NAME="Password Policies">12.10. Password Policies</A></H2>
+<H3><A NAME="Overview">12.10.1. Overview</A></H3>
+<P>This overlay follows the specifications contained in the draft RFC titled draft-behera-ldap-password-policy-09. While the draft itself is expired, it has been implemented in several directory servers, including slapd. Nonetheless, it is important to note that it is a draft, meaning that it is subject to change and is a work-in-progress.</P>
+<P>The key abilities of the password policy overlay are as follows:</P>
+<UL>
+<LI>Enforce a minimum length for new passwords
+<LI>Make sure passwords are not changed too frequently
+<LI>Cause passwords to expire, provide warnings before they need to be changed, and allow a fixed number of 'grace' logins to allow them to be changed after they have expired
+<LI>Maintain a history of passwords to prevent password re-use
+<LI>Prevent password guessing by locking a password for a specified period of time after repeated authentication failures
+<LI>Force a password to be changed at the next authentication
+<LI>Set an administrative lock on an account
+<LI>Support multiple password policies on a default or a per-object basis.
+<LI>Perform arbitrary quality checks using an external loadable module. This is a non-standard extension of the draft RFC.</UL>
+<H3><A NAME="Password Policy Configuration">12.10.2. Password Policy Configuration</A></H3>
+<P>Instantiate the module in the database where it will be used, after adding the new ppolicy schema and loading the ppolicy module. The following example shows the ppolicy module being added to the database that handles the naming context &quot;dc=example,dc=com&quot;. In this example we are also specifying the DN of a policy object to use if none other is specified in a user's object.</P>
+<PRE>
+ database mdb
+ suffix &quot;dc=example,dc=com&quot;
+ [...additional database configuration directives go here...]
+
+ overlay ppolicy
+ ppolicy_default &quot;cn=default,ou=policies,dc=example,dc=com&quot;
+</PRE>
+<P>Now we need a container for the policy objects. In our example the password policy objects are going to be placed in a section of the tree called &quot;ou=policies,dc=example,dc=com&quot;:</P>
+<PRE>
+ dn: ou=policies,dc=example,dc=com
+ objectClass: organizationalUnit
+ objectClass: top
+ ou: policies
+</PRE>
+<P>The default policy object that we are creating defines the following policies:</P>
+<UL>
+<LI>The user is allowed to change his own password. Note that the directory ACLs for this attribute can also affect this ability (pwdAllowUserChange: TRUE).
+<LI>The name of the password attribute is &quot;userPassword&quot; (pwdAttribute: userPassword). Note that this is the only value that is accepted by OpenLDAP for this attribute.
+<LI>The server will check the syntax of the password. If the server is unable to check the syntax (i.e., it was hashed or otherwise encoded by the client) it will return an error refusing the password (pwdCheckQuality: 2).
+<LI>When a client includes the Password Policy Request control with a bind request, the server will respond with a password expiration warning if it is going to expire in ten minutes or less (pwdExpireWarning: 600). The warnings themselves are returned in a Password Policy Response control.
+<LI>When the password for a DN has expired, the server will allow five additional &quot;grace&quot; logins (pwdGraceAuthNLimit: 5).
+<LI>The server will maintain a history of the last five passwords that were used for a DN (pwdInHistory: 5).
+<LI>The server will lock the account after the maximum number of failed bind attempts has been exceeded (pwdLockout: TRUE).
+<LI>When the server has locked an account, the server will keep it locked until an administrator unlocks it (pwdLockoutDuration: 0)
+<LI>The server will reset its failed bind count after a period of 30 seconds.
+<LI>Passwords will not expire (pwdMaxAge: 0).
+<LI>Passwords can be changed as often as desired (pwdMinAge: 0).
+<LI>Passwords must be at least 5 characters in length (pwdMinLength: 5).
+<LI>The password does not need to be changed at the first bind or when the administrator has reset the password (pwdMustChange: FALSE)
+<LI>The current password does not need to be included with password change requests (pwdSafeModify: FALSE)
+<LI>The server will only allow five failed binds in a row for a particular DN (pwdMaxFailure: 5).</UL>
+<P>The actual policy would be:</P>
+<PRE>
+ dn: cn=default,ou=policies,dc=example,dc=com
+ cn: default
+ objectClass: pwdPolicy
+ objectClass: namedPolicy
+ objectClass: top
+ pwdAllowUserChange: TRUE
+ pwdAttribute: userPassword
+ pwdCheckQuality: 2
+ pwdExpireWarning: 600
+ pwdFailureCountInterval: 30
+ pwdGraceAuthNLimit: 5
+ pwdInHistory: 5
+ pwdLockout: TRUE
+ pwdLockoutDuration: 0
+ pwdMaxAge: 0
+ pwdMaxFailure: 5
+ pwdMinAge: 0
+ pwdMinLength: 5
+ pwdMustChange: FALSE
+ pwdSafeModify: FALSE
+</PRE>
+<P>You can create additional policy objects as needed.</P>
+<P>The namedPolicy object class is present because the policy entry requires a structural object class.</P>
+<P>There are two ways password policy can be applied to individual objects:</P>
+<P>1. The pwdPolicySubentry in a user's object - If a user's object has a pwdPolicySubEntry attribute specifying the DN of a policy object, then the policy defined by that object is applied.</P>
+<P>2. Default password policy - If there is no specific pwdPolicySubentry set for an object, and the password policy module was configured with the DN of a default policy object and if that object exists, then the policy defined in that object is applied.</P>
+<P>Please see <EM>slapo-ppolicy(5)</EM> for a complete explanation of its features.</P>
+<P>A guiding philosophy for OpenLDAP and directory servers in general has been that they always hand back exactly what they were given, without modification. For example, if the cn attribute of an object was set to fOObaR, the server will return that exact string during a search. Values of attributes of a sensitive nature, such as userPassword, are often hashed to conceal their values. Since the userPassword values are used internally by the directory server to authenticate users, any hash algorithm that is applied to the value must be compatible with the directory server. Historically this problem has been solved by making the LDAP client application be able to hash the userPassword attribute value in a way that is compatible with the directory server, but this solution has the obvious drawback of requiring tight coupling between the LDAP client and server, and limits the choices of usable hashing algorithms to those that are accommodated by both. This is clearly a sub-optimal solution.</P>
+<P>In 2001 RFC 3062 became a standard that specified an LDAP extended operation for cases like this. Extended operations are not bound by the return-what-you-are-given philosophy and so are free to do things to attribute values that the add and modify operations cannot. The change password extended operation accepts a plaintext password and hashes it based on a specification that is contained in the server. This allows the server to be in control of the hashing algorithm which, in turn, ensures that any hashes applied to userPassword attribute values will not prevent users from being authenticated.</P>
+<P>The password policy module's ppolicy_hash_cleartext flag addresses this problem by intercepting LDAP modify operations that include the userPassword attribute and converting them to change password extended operations so they can be hashed according to the specification contained in slapd's configuration. When this flag is set, LDAP applications that modify the userPassword attribute can send the password in cleartext form to the server using a standard LDAP modify command and the server will hash the value according to the password-hash directive before storing it. It goes without saying that steps need to be taken to protect the cleartext password in transit, such as using SSL, TLS, or some other link encryption method.</P>
+<P>The following example shows the ppolicy module configured to hash cleartext passwords:</P>
+<PRE>
+ database mdb
+ suffix &quot;dc=example,dc=com&quot;
+ [...additional database configuration directives go here...]
+
+ overlay ppolicy
+ ppolicy_default &quot;cn=default,ou=policies,dc=example,dc=com&quot;
+ ppolicy_hash_cleartext
+</PRE>
+<H3><A NAME="Further Information">12.10.3. Further Information</A></H3>
+<P><EM>slapo-ppolicy(5)</EM></P>
+<H2><A NAME="Referential Integrity">12.11. Referential Integrity</A></H2>
+<H3><A NAME="Overview">12.11.1. Overview</A></H3>
+<P>This overlay can be used with a backend database such as slapd-mdb(5) to maintain the cohesiveness of a schema which utilizes reference attributes.</P>
+<P>Whenever a <EM>modrdn</EM> or <EM>delete</EM> is performed, that is, when an entry's DN is renamed or an entry is removed, the server will search the directory for references to this DN (in selected attributes: see below) and update them accordingly. If it was a <EM>delete</EM> operation, the reference is deleted. If it was a <EM>modrdn</EM> operation, then the reference is updated with the new DN.</P>
+<P>For example, a very common administration task is to maintain group membership lists, specially when users are removed from the directory. When an user account is deleted or renamed, all groups this user is a member of have to be updated. LDAP administrators usually have scripts for that. But we can use the <TT>refint</TT> overlay to automate this task. In this example, if the user is removed from the directory, the overlay will take care to remove the user from all the groups he/she was a member of. No more scripting for this.</P>
+<H3><A NAME="Referential Integrity Configuration">12.11.2. Referential Integrity Configuration</A></H3>
+<P>The configuration for this overlay is as follows:</P>
+<PRE>
+ overlay refint
+ refint_attributes &lt;attribute [attribute ...]&gt;
+ refint_nothing &lt;string&gt;
+</PRE>
+<UL>
+<LI><TT>refint_attributes</TT>: this parameter specifies a space separated list of attributes which will have the referential integrity maintained. When an entry is removed or has its DN renamed, the server will do an internal search for any of the <TT>refint_attributes</TT> that point to the affected DN and update them accordingly. IMPORTANT: the attributes listed here must have the <TT>distinguishedName</TT> syntax, that is, hold DNs as values.
+<LI><TT>refint_nothing</TT>: some times, while trying to maintain the referential integrity, the server has to remove the last attribute of its kind from an entry. This may be prohibited by the schema: for example, the <TT>groupOfNames</TT> object class requires at least one member. In these cases, the server will add the attribute value specified in <TT>refint_nothing</TT> to the entry.</UL>
+<P>To illustrate this overlay, we will use the group membership scenario.</P>
+<P>In <TT>slapd.conf</TT>:</P>
+<PRE>
+ overlay refint
+ refint_attributes member
+ refint_nothing &quot;cn=admin,dc=example,dc=com&quot;
+</PRE>
+<P>This configuration tells the overlay to maintain the referential integrity of the <TT>member</TT> attribute. This attribute is used in the <TT>groupOfNames</TT> object class which always needs a member, so we add the <TT>refint_nothing</TT> directive to fill in the group with a standard member should all the members vanish.</P>
+<P>If we have the following group membership, the refint overlay will automatically remove <TT>john</TT> from the group if his entry is removed from the directory:</P>
+<P><CENTER><IMG SRC="refint.png" ALIGN="center"></CENTER></P>
+<P ALIGN="Center">Figure X.Y: Maintaining referential integrity in groups</P>
+<P>Notice that if we rename (<TT>modrdn</TT>) the <TT>john</TT> entry to, say, <TT>jsmith</TT>, the refint overlay will also rename the reference in the <TT>member</TT> attribute, so the group membership stays correct.</P>
+<P>If we removed all users from the directory who are a member of this group, then the end result would be a single member in the group: <TT>cn=admin,dc=example,dc=com</TT>. This is the <TT>refint_nothing</TT> parameter kicking into action so that the schema is not violated.</P>
+<P>The <EM>rootdn</EM> must be set for the database as refint runs as the <EM>rootdn</EM> to gain access to make its updates. The <EM>rootpw</EM> does not need to be set.</P>
+<H3><A NAME="Further Information">12.11.3. Further Information</A></H3>
+<P><EM>slapo-refint(5)</EM></P>
+<H2><A NAME="Return Code">12.12. Return Code</A></H2>
+<H3><A NAME="Overview">12.12.1. Overview</A></H3>
+<P>This overlay is useful to test the behavior of clients when server-generated erroneous and/or unusual responses occur, for example; error codes, referrals, excessive response times and so on.</P>
+<P>This would be classed as a debugging tool whilst developing client software or additional Overlays.</P>
+<P>For detailed information, please see the <EM>slapo-retcode(5)</EM> man page.</P>
+<H3><A NAME="Return Code Configuration">12.12.2. Return Code Configuration</A></H3>
+<P>The retcode overlay utilizes the &quot;return code&quot; schema described in the man page. This schema is specifically designed for use with this overlay and is not intended to be used otherwise.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>The necessary schema is loaded automatically by the overlay.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>An example configuration might be:</P>
+<PRE>
+ overlay retcode
+ retcode-parent &quot;ou=RetCodes,dc=example,dc=com&quot;
+ include ./retcode.conf
+
+ retcode-item &quot;cn=Unsolicited&quot; 0x00 unsolicited=&quot;0&quot;
+ retcode-item &quot;cn=Notice of Disconnect&quot; 0x00 unsolicited=&quot;1.3.6.1.4.1.1466.20036&quot;
+ retcode-item &quot;cn=Pre-disconnect&quot; 0x34 flags=&quot;pre-disconnect&quot;
+ retcode-item &quot;cn=Post-disconnect&quot; 0x34 flags=&quot;post-disconnect&quot;
+</PRE>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG><EM>retcode.conf</EM> can be found in the openldap source at: <TT>tests/data/retcode.conf</TT>
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>An excerpt of a <TT>retcode.conf</TT> would be something like:</P>
+<PRE>
+ retcode-item &quot;cn=success&quot; 0x00
+
+ retcode-item &quot;cn=success w/ delay&quot; 0x00 sleeptime=2
+
+ retcode-item &quot;cn=operationsError&quot; 0x01
+ retcode-item &quot;cn=protocolError&quot; 0x02
+ retcode-item &quot;cn=timeLimitExceeded&quot; 0x03 op=search
+ retcode-item &quot;cn=sizeLimitExceeded&quot; 0x04 op=search
+ retcode-item &quot;cn=compareFalse&quot; 0x05 op=compare
+ retcode-item &quot;cn=compareTrue&quot; 0x06 op=compare
+ retcode-item &quot;cn=authMethodNotSupported&quot; 0x07
+ retcode-item &quot;cn=strongAuthNotSupported&quot; 0x07 text=&quot;same as authMethodNotSupported&quot;
+ retcode-item &quot;cn=strongAuthRequired&quot; 0x08
+ retcode-item &quot;cn=strongerAuthRequired&quot; 0x08 text=&quot;same as strongAuthRequired&quot;
+</PRE>
+<P>Please see <TT>tests/data/retcode.conf</TT> for a complete <TT>retcode.conf</TT></P>
+<H3><A NAME="Further Information">12.12.3. Further Information</A></H3>
+<P><EM>slapo-retcode(5)</EM></P>
+<H2><A NAME="Rewrite/Remap">12.13. Rewrite/Remap</A></H2>
+<H3><A NAME="Overview">12.13.1. Overview</A></H3>
+<P>It performs basic DN/data rewrite and objectClass/attributeType mapping. Its usage is mostly intended to provide virtual views of existing data either remotely, in conjunction with the proxy backend described in <EM>slapd-ldap(5)</EM>, or locally, in conjunction with the relay backend described in <EM>slapd-relay(5)</EM>.</P>
+<P>This overlay is extremely configurable and advanced, therefore recommended reading is the <EM>slapo-rwm(5)</EM> man page.</P>
+<H3><A NAME="Rewrite/Remap Configuration">12.13.2. Rewrite/Remap Configuration</A></H3>
+<H3><A NAME="Further Information">12.13.3. Further Information</A></H3>
+<P><EM>slapo-rwm(5)</EM></P>
+<H2><A NAME="Sync Provider">12.14. Sync Provider</A></H2>
+<H3><A NAME="Overview">12.14.1. Overview</A></H3>
+<P>This overlay implements the provider-side support for the LDAP Content Synchronization (<A HREF="https://www.rfc-editor.org/rfc/rfc4533.txt">RFC4533</A>) as well as syncrepl replication support, including persistent search functionality.</P>
+<H3><A NAME="Sync Provider Configuration">12.14.2. Sync Provider Configuration</A></H3>
+<P>There is very little configuration needed for this overlay, in fact for many situations merely loading the overlay will suffice.</P>
+<P>However, because the overlay creates a contextCSN attribute in the root entry of the database which is updated for every write operation performed against the database and only updated in memory, it is recommended to configure a checkpoint so that the contextCSN is written into the underlying database to minimize recovery time after an unclean shutdown:</P>
+<PRE>
+ overlay syncprov
+ syncprov-checkpoint 100 10
+</PRE>
+<P>For every 100 operations or 10 minutes, which ever is sooner, the contextCSN will be checkpointed.</P>
+<P>The four configuration directives available are <B>syncprov-checkpoint</B>, <B>syncprov-sessionlog</B>, <B>syncprov-nopresent</B> and <B>syncprov-reloadhint</B> which are covered in the man page discussing various other scenarios where this overlay can be used.</P>
+<H3><A NAME="Further Information">12.14.3. Further Information</A></H3>
+<P>The <EM>slapo-syncprov(5)</EM> man page and the <A HREF="#Configuring the different replication types">Configuring the different replication types</A> section</P>
+<H2><A NAME="Translucent Proxy">12.15. Translucent Proxy</A></H2>
+<H3><A NAME="Overview">12.15.1. Overview</A></H3>
+<P>This overlay can be used with a backend database such as <EM>slapd-mdb</EM>(5) to create a &quot;translucent proxy&quot;.</P>
+<P>Entries retrieved from a remote LDAP server may have some or all attributes overridden, or new attributes added, by entries in the local database before being presented to the client.</P>
+<P>A search operation is first populated with entries from the remote LDAP server, the attributes of which are then overridden with any attributes defined in the local database. Local overrides may be populated with the add, modify, and modrdn operations, the use of which is restricted to the root user of the translucent local database.</P>
+<P>A compare operation will perform a comparison with attributes defined in the local database record (if any) before any comparison is made with data in the remote database.</P>
+<H3><A NAME="Translucent Proxy Configuration">12.15.2. Translucent Proxy Configuration</A></H3>
+<P>There are various options available with this overlay, but for this example we will demonstrate adding new attributes to a remote entry and also searching against these newly added local attributes. For more information about overriding remote entries and search configuration, please see <EM>slapo-translucent(5)</EM></P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>The Translucent Proxy overlay will disable schema checking in the local database, so that an entry consisting of overlay attributes need not adhere to the complete schema.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>First we configure the overlay in the normal manner:</P>
+<PRE>
+ include /usr/local/etc/openldap/schema/core.schema
+ include /usr/local/etc/openldap/schema/cosine.schema
+ include /usr/local/etc/openldap/schema/nis.schema
+ include /usr/local/etc/openldap/schema/inetorgperson.schema
+
+ pidfile ./slapd.pid
+ argsfile ./slapd.args
+
+ database mdb
+ suffix &quot;dc=suretecsystems,dc=com&quot;
+ rootdn &quot;cn=trans,dc=suretecsystems,dc=com&quot;
+ rootpw secret
+ directory ./openldap-data
+
+ index objectClass eq
+
+ overlay translucent
+ translucent_local carLicense
+
+ uri ldap://192.168.X.X:389
+ lastmod off
+ acl-bind binddn=&quot;cn=admin,dc=suretecsystems,dc=com&quot; credentials=&quot;blahblah&quot;
+</PRE>
+<P>You will notice the overlay directive and a directive to say what attribute we want to be able to search against in the local database. We must also load the ldap backend which will connect to the remote directory server.</P>
+<P>Now we take an example LDAP group:</P>
+<PRE>
+ # itsupport, Groups, suretecsystems.com
+ dn: cn=itsupport,ou=Groups,dc=suretecsystems,dc=com
+ objectClass: posixGroup
+ objectClass: sambaGroupMapping
+ cn: itsupport
+ gidNumber: 1000
+ sambaSID: S-1-5-21-XXX
+ sambaGroupType: 2
+ displayName: itsupport
+ memberUid: ghenry
+ memberUid: joebloggs
+</PRE>
+<P>and create an LDIF file we can use to add our data to the local database, using some pretty strange choices of new attributes for demonstration purposes:</P>
+<PRE>
+ [ghenry@suretec test_configs]$ cat test-translucent-add.ldif
+ dn: cn=itsupport,ou=Groups,dc=suretecsystems,dc=com
+ businessCategory: frontend-override
+ carLicense: LIVID
+ employeeType: special
+ departmentNumber: 9999999
+ roomNumber: 41L-535
+</PRE>
+<P>Searching against the proxy gives:</P>
+<PRE>
+ [ghenry@suretec test_configs]$ ldapsearch -x -H ldap://127.0.0.1:9001 &quot;(cn=itsupport)&quot;
+ # itsupport, Groups, OxObjects, suretecsystems.com
+ dn: cn=itsupport,ou=Groups,ou=OxObjects,dc=suretecsystems,dc=com
+ objectClass: posixGroup
+ objectClass: sambaGroupMapping
+ cn: itsupport
+ gidNumber: 1003
+ SAMBASID: S-1-5-21-XXX
+ SAMBAGROUPTYPE: 2
+ displayName: itsupport
+ memberUid: ghenry
+ memberUid: joebloggs
+ roomNumber: 41L-535
+ departmentNumber: 9999999
+ employeeType: special
+ carLicense: LIVID
+ businessCategory: frontend-override
+</PRE>
+<P>Here we can see that the 5 new attributes are added to the remote entry before being returned to the our client.</P>
+<P>Because we have configured a local attribute to search against:</P>
+<PRE>
+ overlay translucent
+ translucent_local carLicense
+</PRE>
+<P>we can also search for that to return the completely fabricated entry:</P>
+<PRE>
+ ldapsearch -x -H ldap://127.0.0.1:9001 (carLicense=LIVID)
+</PRE>
+<P>This is an extremely useful feature because you can then extend a remote directory server locally and also search against the local entries.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>Because the translucent overlay does not perform any DN rewrites, the local and remote database instances must have the same suffix. Other configurations will probably fail with No Such Object and other errors
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H3><A NAME="Further Information">12.15.3. Further Information</A></H3>
+<P><EM>slapo-translucent(5)</EM></P>
+<H2><A NAME="Attribute Uniqueness">12.16. Attribute Uniqueness</A></H2>
+<H3><A NAME="Overview">12.16.1. Overview</A></H3>
+<P>This overlay can be used with a backend database such as <EM>slapd-mdb(5)</EM> to enforce the uniqueness of some or all attributes within a subtree.</P>
+<H3><A NAME="Attribute Uniqueness Configuration">12.16.2. Attribute Uniqueness Configuration</A></H3>
+<P>This overlay is only effective on new data from the point the overlay is enabled. To check uniqueness for existing data, you can export and import your data again via the LDAP Add operation, which will not be suitable for large amounts of data, unlike <B>slapcat</B>.</P>
+<P>For the following example, if uniqueness were enforced for the <B>mail</B> attribute, the subtree would be searched for any other records which also have a <B>mail</B> attribute containing the same value presented with an <B>add</B>, <B>modify</B> or <B>modrdn</B> operation which are unique within the configured scope. If any are found, the request is rejected.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>If no attributes are specified, for example <B>ldap:///??sub?</B>, then the URI applies to all non-operational attributes. However, the keyword <B>ignore</B> can be specified to exclude certain non-operational attributes.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>To search at the base dn of the current backend database ensuring uniqueness of the <B>mail</B> attribute, we simply add the following configuration:</P>
+<PRE>
+ overlay unique
+ unique_uri ldap:///?mail?sub?
+</PRE>
+<P>For an existing entry of:</P>
+<PRE>
+ dn: cn=gavin,dc=suretecsystems,dc=com
+ objectClass: top
+ objectClass: inetorgperson
+ cn: gavin
+ sn: henry
+ mail: ghenry@suretecsystems.com
+</PRE>
+<P>and we then try to add a new entry of:</P>
+<PRE>
+ dn: cn=robert,dc=suretecsystems,dc=com
+ objectClass: top
+ objectClass: inetorgperson
+ cn: robert
+ sn: jones
+ mail: ghenry@suretecsystems.com
+</PRE>
+<P>would result in an error like so:</P>
+<PRE>
+ adding new entry &quot;cn=robert,dc=example,dc=com&quot;
+ ldap_add: Constraint violation (19)
+ additional info: some attributes not unique
+</PRE>
+<P>The overlay can have multiple URIs specified within a domain, allowing complex selections of objects and also have multiple <B>unique_uri</B> statements or <B>olcUniqueURI</B> attributes which will create independent domains.</P>
+<P>For more information and details about the <B>strict</B> and <B>ignore</B> keywords, please see the <EM>slapo-unique(5)</EM> man page.</P>
+<H3><A NAME="Further Information">12.16.3. Further Information</A></H3>
+<P><EM>slapo-unique(5)</EM></P>
+<H2><A NAME="Value Sorting">12.17. Value Sorting</A></H2>
+<H3><A NAME="Overview">12.17.1. Overview</A></H3>
+<P>The Value Sorting overlay can be used with a backend database to sort the values of specific multi-valued attributes within a subtree. The sorting occurs whenever the attributes are returned in a search response.</P>
+<H3><A NAME="Value Sorting Configuration">12.17.2. Value Sorting Configuration</A></H3>
+<P>Sorting can be specified in ascending or descending order, using either numeric or alphanumeric sort methods. Additionally, a &quot;weighted&quot; sort can be specified, which uses a numeric weight prepended to the attribute values.</P>
+<P>The weighted sort is always performed in ascending order, but may be combined with the other methods for values that all have equal weights. The weight is specified by prepending an integer weight {&lt;weight&gt;} in front of each value of the attribute for which weighted sorting is desired. This weighting factor is stripped off and never returned in search results.</P>
+<P>Here are a few examples:</P>
+<PRE>
+ loglevel sync stats
+
+ database mdb
+ suffix &quot;dc=suretecsystems,dc=com&quot;
+ directory /usr/local/var/openldap-data
+
+ ......
+
+ overlay valsort
+ valsort-attr memberUid ou=Groups,dc=suretecsystems,dc=com alpha-ascend
+</PRE>
+<P>For example, ascend:</P>
+<PRE>
+ # sharedemail, Groups, suretecsystems.com
+ dn: cn=sharedemail,ou=Groups,dc=suretecsystems,dc=com
+ objectClass: posixGroup
+ objectClass: top
+ cn: sharedemail
+ gidNumber: 517
+ memberUid: admin
+ memberUid: dovecot
+ memberUid: laura
+ memberUid: suretec
+</PRE>
+<P>For weighted, we change our data to:</P>
+<PRE>
+ # sharedemail, Groups, suretecsystems.com
+ dn: cn=sharedemail,ou=Groups,dc=suretecsystems,dc=com
+ objectClass: posixGroup
+ objectClass: top
+ cn: sharedemail
+ gidNumber: 517
+ memberUid: {4}admin
+ memberUid: {2}dovecot
+ memberUid: {1}laura
+ memberUid: {3}suretec
+</PRE>
+<P>and change the config to:</P>
+<PRE>
+ overlay valsort
+ valsort-attr memberUid ou=Groups,dc=suretecsystems,dc=com weighted
+</PRE>
+<P>Searching now results in:</P>
+<PRE>
+ # sharedemail, Groups, OxObjects, suretecsystems.com
+ dn: cn=sharedemail,ou=Groups,ou=OxObjects,dc=suretecsystems,dc=com
+ objectClass: posixGroup
+ objectClass: top
+ cn: sharedemail
+ gidNumber: 517
+ memberUid: laura
+ memberUid: dovecot
+ memberUid: suretec
+ memberUid: admin
+</PRE>
+<H3><A NAME="Further Information">12.17.3. Further Information</A></H3>
+<P><EM>slapo-valsort(5)</EM></P>
+<H2><A NAME="Overlay Stacking">12.18. Overlay Stacking</A></H2>
+<H3><A NAME="Overview">12.18.1. Overview</A></H3>
+<P>Overlays can be stacked, which means that more than one overlay can be instantiated for each database, or for the <TT>frontend</TT>. As a consequence, each overlays function is called, if defined, when overlay execution is invoked. Multiple overlays are executed in reverse order (as a stack) with respect to their definition in slapd.conf (5), or with respect to their ordering in the config database, as documented in slapd-config (5).</P>
+<H3><A NAME="Example Scenarios">12.18.2. Example Scenarios</A></H3>
+<H4><A NAME="Samba">12.18.2.1. Samba</A></H4>
+<P></P>
+<HR>
+<H1><A NAME="Schema Specification">13. Schema Specification</A></H1>
+<P>This chapter describes how to extend the user schema used by <EM>slapd</EM>(8). The chapter assumes the reader is familiar with the <TERM>LDAP</TERM>/<TERM>X.500</TERM> information model.</P>
+<P>The first section, <A HREF="#Distributed Schema Files">Distributed Schema Files</A> details optional schema definitions provided in the distribution and where to obtain other definitions. The second section, <A HREF="#Extending Schema">Extending Schema</A>, details how to define new schema items.</P>
+<P>This chapter does not discuss how to extend system schema used by <EM>slapd</EM>(8) as this requires source code modification. System schema includes all operational attribute types or any object class which allows or requires an operational attribute (directly or indirectly).</P>
+<H2><A NAME="Distributed Schema Files">13.1. Distributed Schema Files</A></H2>
+<P>OpenLDAP Software is distributed with a set of schema specifications for your use. Each set is defined in a file suitable for inclusion (using the <TT>include</TT> directive) in your <EM>slapd.conf</EM>(5) file. These schema files are normally installed in the <TT>/usr/local/etc/openldap/schema</TT> directory.</P>
+<TABLE CLASS="columns" BORDER ALIGN='Center'>
+<CAPTION ALIGN=top>Table 8.1: Provided Schema Specifications</CAPTION>
+<TR CLASS="heading">
+<TD ALIGN='Left'>
+<STRONG>File</STRONG>
+</TD>
+<TD ALIGN='Right'>
+<STRONG>Description</STRONG>
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>core.schema</TT>
+</TD>
+<TD ALIGN='Right'>
+OpenLDAP <EM>core</EM> (required)
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>cosine.schema</TT>
+</TD>
+<TD ALIGN='Right'>
+Cosine and Internet X.500 (useful)
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>inetorgperson.schema</TT>
+</TD>
+<TD ALIGN='Right'>
+InetOrgPerson (useful)
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>misc.schema</TT>
+</TD>
+<TD ALIGN='Right'>
+Assorted (experimental)
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>nis.schema</TT>
+</TD>
+<TD ALIGN='Right'>
+Network Information Services (FYI)
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>openldap.schema</TT>
+</TD>
+<TD ALIGN='Right'>
+OpenLDAP Project (experimental)
+</TD>
+</TR>
+</TABLE>
+
+<P>To use any of these schema files, you only need to include the desired file in the global definitions portion of your <EM>slapd.conf</EM>(5) file. For example:</P>
+<PRE>
+ # include schema
+ include /usr/local/etc/openldap/schema/core.schema
+ include /usr/local/etc/openldap/schema/cosine.schema
+ include /usr/local/etc/openldap/schema/inetorgperson.schema
+</PRE>
+<P>Additional files may be available. Please consult the OpenLDAP <TERM>FAQ</TERM> (<A HREF="http://www.openldap.org/faq/">http://www.openldap.org/faq/</A>).</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>You should not modify any of the schema items defined in provided files.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H2><A NAME="Extending Schema">13.2. Extending Schema</A></H2>
+<P>Schema used by <EM>slapd</EM>(8) may be extended to support additional syntaxes, matching rules, attribute types, and object classes. This chapter details how to add user application attribute types and object classes using the syntaxes and matching rules already supported by slapd. slapd can also be extended to support additional syntaxes, matching rules and system schema, but this requires some programming and hence is not discussed here.</P>
+<P>There are five steps to defining new schema:</P>
+<OL>
+<LI>obtain Object Identifier
+<LI>choose a name prefix
+<LI>create local schema file
+<LI>define custom attribute types (if necessary)
+<LI>define custom object classes</OL>
+<H3><A NAME="Object Identifiers">13.2.1. Object Identifiers</A></H3>
+<P>Each schema element is identified by a globally unique <TERM>Object Identifier</TERM> (OID). OIDs are also used to identify other objects. They are commonly found in protocols described by <TERM>ASN.1</TERM>. In particular, they are heavily used by the <TERM>Simple Network Management Protocol</TERM> (SNMP). As OIDs are hierarchical, your organization can obtain one OID and branch it as needed. For example, if your organization were assigned OID <TT>1.1</TT>, you could branch the tree as follows:</P>
+<TABLE CLASS="columns" BORDER ALIGN='Center'>
+<CAPTION ALIGN=top>Table 8.2: Example OID hierarchy</CAPTION>
+<TR CLASS="heading">
+<TD ALIGN='Left'>
+<STRONG>OID</STRONG>
+</TD>
+<TD ALIGN='Right'>
+<STRONG>Assignment</STRONG>
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>1.1</TT>
+</TD>
+<TD ALIGN='Right'>
+Organization's OID
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>1.1.1</TT>
+</TD>
+<TD ALIGN='Right'>
+SNMP Elements
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>1.1.2</TT>
+</TD>
+<TD ALIGN='Right'>
+LDAP Elements
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>1.1.2.1</TT>
+</TD>
+<TD ALIGN='Right'>
+AttributeTypes
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>1.1.2.1.1</TT>
+</TD>
+<TD ALIGN='Right'>
+x-my-Attribute
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>1.1.2.2</TT>
+</TD>
+<TD ALIGN='Right'>
+ObjectClasses
+</TD>
+</TR>
+<TR>
+<TD ALIGN='Left'>
+<TT>1.1.2.2.1</TT>
+</TD>
+<TD ALIGN='Right'>
+x-my-ObjectClass
+</TD>
+</TR>
+</TABLE>
+
+<P>You are, of course, free to design a hierarchy suitable to your organizational needs under your organization's OID. No matter what hierarchy you choose, you should maintain a registry of assignments you make. This can be a simple flat file or something more sophisticated such as the <EM>OpenLDAP OID Registry</EM> (<A HREF="http://www.openldap.org/faq/index.cgi?file=197">http://www.openldap.org/faq/index.cgi?file=197</A>).</P>
+<P>For more information about Object Identifiers (and a listing service) see <A HREF="http://www.alvestrand.no/objectid/">http://www.alvestrand.no/objectid/</A>.</P>
+<UL>
+<EM>Under no circumstances should you hijack OID namespace!</EM></UL>
+<P>To obtain a registered OID at <EM>no cost</EM>, apply for a OID under the <A HREF="https://www.iana.org/">Internet Assigned Numbers Authority</A> (ORG:IANA) maintained <EM>Private Enterprise</EM> arc. Any private enterprise (organization) may request a <TERM>Private Enterprise Number</TERM> (PEN) to be assigned under this arc. Just fill out the IANA form at <A HREF="http://pen.iana.org/pen/PenApplication.page">http://pen.iana.org/pen/PenApplication.page</A> and your official PEN will be sent to you usually within a few days. Your base OID will be something like <TT>1.3.6.1.4.1.X</TT> where <TT>X</TT> is an integer.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>PENs obtained using this form may be used for any purpose including identifying LDAP schema elements.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>Alternatively, OID name space may be available from a national authority (e.g., <A HREF="https://www.ansi.org/">ANSI</A>, <A HREF="https://www.bsigroup.com/en-GB/">BSI</A>).</P>
+<H3><A NAME="Naming Elements">13.2.2. Naming Elements</A></H3>
+<P>In addition to assigning a unique object identifier to each schema element, you should provide at least one textual name for each element. Names should be registered with the <A HREF="https://www.iana.org/">IANA</A> or prefixed with &quot;x-&quot; to place in the &quot;private use&quot; name space.</P>
+<P>The name should be both descriptive and not likely to clash with names of other schema elements. In particular, any name you choose should not clash with present or future Standard Track names (this is assured if you registered names or use names beginning with &quot;x-&quot;).</P>
+<P>It is noted that you can obtain your own registered name prefix so as to avoid having to register your names individually. See <A HREF="https://www.rfc-editor.org/rfc/rfc4520.txt">RFC4520</A> for details.</P>
+<P>In the examples below, we have used a short prefix '<TT>x-my-</TT>'. Such a short prefix would only be suitable for a very large, global organization. In general, we recommend something like '<TT>x-de-Firm-</TT>' (German company) or '<TT>x-com-Example</TT>' (elements associated with organization associated with <TT>example.com</TT>).</P>
+<H3><A NAME="Local schema file">13.2.3. Local schema file</A></H3>
+<P>The <TT>objectclass</TT> and <TT>attributeTypes</TT> configuration file directives can be used to define schema rules on entries in the directory. It is customary to create a file to contain definitions of your custom schema items. We recommend you create a file <TT>local.schema</TT> in <TT>/usr/local/etc/openldap/schema/local.schema</TT> and then include this file in your <EM>slapd.conf</EM>(5) file immediately after other schema <TT>include</TT> directives.</P>
+<PRE>
+ # include schema
+ include /usr/local/etc/openldap/schema/core.schema
+ include /usr/local/etc/openldap/schema/cosine.schema
+ include /usr/local/etc/openldap/schema/inetorgperson.schema
+ # include local schema
+ include /usr/local/etc/openldap/schema/local.schema
+</PRE>
+<H3><A NAME="Attribute Type Specification">13.2.4. Attribute Type Specification</A></H3>
+<P>The <EM>attributetype</EM> directive is used to define a new attribute type. The directive uses the same Attribute Type Description (as defined in <A HREF="https://www.rfc-editor.org/rfc/rfc4512.txt">RFC4512</A>) used by the attributeTypes attribute found in the subschema subentry, e.g.:</P>
+<PRE>
+ attributetype &lt;<A HREF="https://www.rfc-editor.org/rfc/rfc4512.txt">RFC4512</A> Attribute Type Description&gt;
+</PRE>
+<P>where Attribute Type Description is defined by the following <TERM>ABNF</TERM>:</P>
+<PRE>
+ AttributeTypeDescription = &quot;(&quot; whsp
+ numericoid whsp ; AttributeType identifier
+ [ &quot;NAME&quot; qdescrs ] ; name used in AttributeType
+ [ &quot;DESC&quot; qdstring ] ; description
+ [ &quot;OBSOLETE&quot; whsp ]
+ [ &quot;SUP&quot; woid ] ; derived from this other
+ ; AttributeType
+ [ &quot;EQUALITY&quot; woid ; Matching Rule name
+ [ &quot;ORDERING&quot; woid ; Matching Rule name
+ [ &quot;SUBSTR&quot; woid ] ; Matching Rule name
+ [ &quot;SYNTAX&quot; whsp noidlen whsp ] ; Syntax OID
+ [ &quot;SINGLE-VALUE&quot; whsp ] ; default multi-valued
+ [ &quot;COLLECTIVE&quot; whsp ] ; default not collective
+ [ &quot;NO-USER-MODIFICATION&quot; whsp ]; default user modifiable
+ [ &quot;USAGE&quot; whsp AttributeUsage ]; default userApplications
+ whsp &quot;)&quot;
+
+ AttributeUsage =
+ &quot;userApplications&quot; /
+ &quot;directoryOperation&quot; /
+ &quot;distributedOperation&quot; / ; DSA-shared
+ &quot;dSAOperation&quot; ; DSA-specific, value depends on server
+
+</PRE>
+<P>where whsp is a space ('<TT> </TT>'), numericoid is a globally unique OID in dotted-decimal form (e.g. <TT>1.1.0</TT>), qdescrs is one or more names, woid is either the name or OID optionally followed by a length specifier (e.g <TT>{10</TT>}).</P>
+<P>For example, the attribute types <TT>name</TT> and <TT>cn</TT> are defined in <TT>core.schema</TT> as:</P>
+<PRE>
+ attributeType ( 2.5.4.41 NAME 'name'
+ DESC 'name(s) associated with the object'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )
+ attributeType ( 2.5.4.3 NAME ( 'cn' 'commonName' )
+ DESC 'common name(s) associated with the object'
+ SUP name )
+</PRE>
+<P>Notice that each defines the attribute's OID, provides a short name, and a brief description. Each name is an alias for the OID. <EM>slapd</EM>(8) returns the first listed name when returning results.</P>
+<P>The first attribute, <TT>name</TT>, holds values of <TT>directoryString</TT> (<TERM>UTF-8</TERM> encoded Unicode) syntax. The syntax is specified by OID (1.3.6.1.4.1.1466.115.121.1.15 identifies the directoryString syntax). A length recommendation of 32768 is specified. Servers should support values of this length, but may support longer values. The field does NOT specify a size constraint, so is ignored on servers (such as slapd) which don't impose such size limits. In addition, the equality and substring matching uses case ignore rules. Below are tables listing commonly used syntax and matching rules (<EM>slapd</EM>(8) supports these and many more).</P>
+<TABLE CLASS="columns" BORDER ALIGN='Center'>
+<CAPTION ALIGN=top>Table 8.3: Commonly Used Syntaxes</CAPTION>
+<TR CLASS="heading">
+<TD>
+<STRONG>Name</STRONG>
+</TD>
+<TD>
+<STRONG>OID</STRONG>
+</TD>
+<TD>
+<STRONG>Description</STRONG>
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>boolean</TT>
+</TD>
+<TD>
+<TT>1.3.6.1.4.1.1466.115.121.1.7</TT>
+</TD>
+<TD>
+boolean value
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>directoryString</TT>
+</TD>
+<TD>
+<TT>1.3.6.1.4.1.1466.115.121.1.15</TT>
+</TD>
+<TD>
+Unicode (UTF-8) string
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>distinguishedName</TT>
+</TD>
+<TD>
+<TT>1.3.6.1.4.1.1466.115.121.1.12</TT>
+</TD>
+<TD>
+LDAP <TERM>DN</TERM>
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>integer</TT>
+</TD>
+<TD>
+<TT>1.3.6.1.4.1.1466.115.121.1.27</TT>
+</TD>
+<TD>
+integer
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>numericString</TT>
+</TD>
+<TD>
+<TT>1.3.6.1.4.1.1466.115.121.1.36</TT>
+</TD>
+<TD>
+numeric string
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>OID</TT>
+</TD>
+<TD>
+<TT>1.3.6.1.4.1.1466.115.121.1.38</TT>
+</TD>
+<TD>
+object identifier
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>octetString</TT>
+</TD>
+<TD>
+<TT>1.3.6.1.4.1.1466.115.121.1.40</TT>
+</TD>
+<TD>
+arbitrary octets
+</TD>
+</TR>
+</TABLE>
+
+<PRE>
+
+</PRE>
+<TABLE CLASS="columns" BORDER ALIGN='Center'>
+<CAPTION ALIGN=top>Table 8.4: Commonly Used Matching Rules</CAPTION>
+<TR CLASS="heading">
+<TD>
+<STRONG>Name</STRONG>
+</TD>
+<TD>
+<STRONG>Type</STRONG>
+</TD>
+<TD>
+<STRONG>Description</STRONG>
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>booleanMatch</TT>
+</TD>
+<TD>
+equality
+</TD>
+<TD>
+boolean
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>caseIgnoreMatch</TT>
+</TD>
+<TD>
+equality
+</TD>
+<TD>
+case insensitive, space insensitive
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>caseIgnoreOrderingMatch</TT>
+</TD>
+<TD>
+ordering
+</TD>
+<TD>
+case insensitive, space insensitive
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>caseIgnoreSubstringsMatch</TT>
+</TD>
+<TD>
+substrings
+</TD>
+<TD>
+case insensitive, space insensitive
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>caseExactMatch</TT>
+</TD>
+<TD>
+equality
+</TD>
+<TD>
+case sensitive, space insensitive
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>caseExactOrderingMatch</TT>
+</TD>
+<TD>
+ordering
+</TD>
+<TD>
+case sensitive, space insensitive
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>caseExactSubstringsMatch</TT>
+</TD>
+<TD>
+substrings
+</TD>
+<TD>
+case sensitive, space insensitive
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>distinguishedNameMatch</TT>
+</TD>
+<TD>
+equality
+</TD>
+<TD>
+distinguished name
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>integerMatch</TT>
+</TD>
+<TD>
+equality
+</TD>
+<TD>
+integer
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>integerOrderingMatch</TT>
+</TD>
+<TD>
+ordering
+</TD>
+<TD>
+integer
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>numericStringMatch</TT>
+</TD>
+<TD>
+equality
+</TD>
+<TD>
+numerical
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>numericStringOrderingMatch</TT>
+</TD>
+<TD>
+ordering
+</TD>
+<TD>
+numerical
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>numericStringSubstringsMatch</TT>
+</TD>
+<TD>
+substrings
+</TD>
+<TD>
+numerical
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>octetStringMatch</TT>
+</TD>
+<TD>
+equality
+</TD>
+<TD>
+octet string
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>octetStringOrderingMatch</TT>
+</TD>
+<TD>
+ordering
+</TD>
+<TD>
+octet string
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>octetStringSubstringsMatch ordering</TT>
+</TD>
+<TD>
+octet st
+</TD>
+<TD>
+ring
+</TD>
+</TR>
+<TR>
+<TD>
+<TT>objectIdentiferMatch</TT>
+</TD>
+<TD>
+equality
+</TD>
+<TD>
+object identifier
+</TD>
+</TR>
+</TABLE>
+
+<P>The second attribute, <TT>cn</TT>, is a subtype of <TT>name</TT> hence it inherits the syntax, matching rules, and usage of <TT>name</TT>. <TT>commonName</TT> is an alternative name.</P>
+<P>Neither attribute is restricted to a single value. Both are meant for usage by user applications. Neither is obsolete nor collective.</P>
+<P>The following subsections provide a couple of examples.</P>
+<H4><A NAME="x-my-UniqueName">13.2.4.1. x-my-UniqueName</A></H4>
+<P>Many organizations maintain a single unique name for each user. Though one could use <TT>displayName</TT> (<A HREF="https://www.rfc-editor.org/rfc/rfc2798.txt">RFC2798</A>), this attribute is really meant to be controlled by the user, not the organization. We could just copy the definition of <TT>displayName</TT> from <TT>inetorgperson.schema</TT> and replace the OID, name, and description, e.g:</P>
+<PRE>
+ attributetype ( 1.1.2.1.1 NAME 'x-my-UniqueName'
+ DESC 'unique name with my organization'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ SINGLE-VALUE )
+</PRE>
+<P>However, if we want this name to be used in <TT>name</TT> assertions, e.g. <TT>(name=*Jane*)</TT>, the attribute could alternatively be defined as a subtype of <TT>name</TT>, e.g.:</P>
+<PRE>
+ attributetype ( 1.1.2.1.1 NAME 'x-my-UniqueName'
+ DESC 'unique name with my organization'
+ SUP name )
+</PRE>
+<H4><A NAME="x-my-Photo">13.2.4.2. x-my-Photo</A></H4>
+<P>Many organizations maintain a photo of each each user. A <TT>x-my-Photo</TT> attribute type could be defined to hold a photo. Of course, one could use just use <TT>jpegPhoto</TT> (<A HREF="https://www.rfc-editor.org/rfc/rfc2798.txt">RFC2798</A>) (or a subtype) to hold the photo. However, you can only do this if the photo is in <EM>JPEG File Interchange Format</EM>. Alternatively, an attribute type which uses the <EM>Octet String</EM> syntax can be defined, e.g.:</P>
+<PRE>
+ attributetype ( 1.1.2.1.2 NAME 'x-my-Photo'
+ DESC 'a photo (application defined format)'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.40
+ SINGLE-VALUE )
+</PRE>
+<P>In this case, the syntax doesn't specify the format of the photo. It's assumed (maybe incorrectly) that all applications accessing this attribute agree on the handling of values.</P>
+<P>If you wanted to support multiple photo formats, you could define a separate attribute type for each format, prefix the photo with some typing information, or describe the value using <TERM>ASN.1</TERM> and use the <TT>;binary</TT> transfer option.</P>
+<P>Another alternative is for the attribute to hold a <TERM>URI</TERM> pointing to the photo. You can model such an attribute after <TT>labeledURI</TT> (<A HREF="https://www.rfc-editor.org/rfc/rfc2079.txt">RFC2079</A>) or simply create a subtype, e.g.:</P>
+<PRE>
+ attributetype ( 1.1.2.1.3 NAME 'x-my-PhotoURI'
+ DESC 'URI and optional label referring to a photo'
+ SUP labeledURI )
+</PRE>
+<H3><A NAME="Object Class Specification">13.2.5. Object Class Specification</A></H3>
+<P>The <EM>objectclasses</EM> directive is used to define a new object class. The directive uses the same Object Class Description (as defined in <A HREF="https://www.rfc-editor.org/rfc/rfc4512.txt">RFC4512</A>) used by the objectClasses attribute found in the subschema subentry, e.g.:</P>
+<PRE>
+ objectclass &lt;<A HREF="https://www.rfc-editor.org/rfc/rfc4512.txt">RFC4512</A> Object Class Description&gt;
+</PRE>
+<P>where Object Class Description is defined by the following <TERM>ABNF</TERM>:</P>
+<PRE>
+ ObjectClassDescription = &quot;(&quot; whsp
+ numericoid whsp ; ObjectClass identifier
+ [ &quot;NAME&quot; qdescrs ]
+ [ &quot;DESC&quot; qdstring ]
+ [ &quot;OBSOLETE&quot; whsp ]
+ [ &quot;SUP&quot; oids ] ; Superior ObjectClasses
+ [ ( &quot;ABSTRACT&quot; / &quot;STRUCTURAL&quot; / &quot;AUXILIARY&quot; ) whsp ]
+ ; default structural
+ [ &quot;MUST&quot; oids ] ; AttributeTypes
+ [ &quot;MAY&quot; oids ] ; AttributeTypes
+ whsp &quot;)&quot;
+</PRE>
+<P>where whsp is a space ('<TT> </TT>'), numericoid is a globally unique OID in dotted-decimal form (e.g. <TT>1.1.0</TT>), qdescrs is one or more names, and oids is one or more names and/or OIDs.</P>
+<H4><A NAME="x-my-PhotoObject">13.2.5.1. x-my-PhotoObject</A></H4>
+<P>To define an <EM>auxiliary</EM> object class which allows x-my-Photo to be added to any existing entry.</P>
+<PRE>
+ objectclass ( 1.1.2.2.1 NAME 'x-my-PhotoObject'
+ DESC 'mixin x-my-Photo'
+ AUXILIARY
+ MAY x-my-Photo )
+</PRE>
+<H4><A NAME="x-my-Person">13.2.5.2. x-my-Person</A></H4>
+<P>If your organization would like have a private <EM>structural</EM> object class to instantiate users, you can subclass one of the existing person classes, such as <TT>inetOrgPerson</TT> (<A HREF="https://www.rfc-editor.org/rfc/rfc2798.txt">RFC2798</A>), and add any additional attributes which you desire.</P>
+<PRE>
+ objectclass ( 1.1.2.2.2 NAME 'x-my-Person'
+ DESC 'my person'
+ SUP inetOrgPerson
+ MUST ( x-my-UniqueName $ givenName )
+ MAY x-my-Photo )
+</PRE>
+<P>The object class inherits the required/allowed attribute types of <TT>inetOrgPerson</TT> but requires <TT>x-my-UniqueName</TT> and <TT>givenName</TT> and allows <TT>x-my-Photo</TT>.</P>
+<H3><A NAME="OID Macros">13.2.6. OID Macros</A></H3>
+<P>To ease the management and use of OIDs, <EM>slapd</EM>(8) supports <EM>Object Identifier</EM> macros. The <TT>objectIdentifier</TT> directive is used to equate a macro (name) with a OID. The OID may possibly be derived from a previously defined OID macro. The <EM>slapd.conf</EM>(5) syntax is:</P>
+<PRE>
+ objectIdentifier &lt;name&gt; { &lt;oid&gt; | &lt;name&gt;[:&lt;suffix&gt;] }
+</PRE>
+<P>The following demonstrates definition of a set of OID macros and their use in defining schema elements:</P>
+<PRE>
+ objectIdentifier myOID 1.1
+ objectIdentifier mySNMP myOID:1
+ objectIdentifier myLDAP myOID:2
+ objectIdentifier myAttributeType myLDAP:1
+ objectIdentifier myObjectClass myLDAP:2
+ attributetype ( myAttributeType:3 NAME 'x-my-PhotoURI'
+ DESC 'URI and optional label referring to a photo'
+ SUP labeledURI )
+ objectclass ( myObjectClass:1 NAME 'x-my-PhotoObject'
+ DESC 'mixin x-my-Photo'
+ AUXILIARY
+ MAY x-my-Photo )
+</PRE>
+<P></P>
+<HR>
+<H1><A NAME="Security Considerations">14. Security Considerations</A></H1>
+<P>OpenLDAP Software is designed to run in a wide variety of computing environments from tightly-controlled closed networks to the global Internet. Hence, OpenLDAP Software supports many different security mechanisms. This chapter describes these mechanisms and discusses security considerations for using OpenLDAP Software.</P>
+<H2><A NAME="Network Security">14.1. Network Security</A></H2>
+<H3><A NAME="Selective Listening">14.1.1. Selective Listening</A></H3>
+<P>By default, <EM>slapd</EM>(8) will listen on both the IPv4 and IPv6 &quot;any&quot; addresses. It is often desirable to have <EM>slapd</EM> listen on select address/port pairs. For example, listening only on the IPv4 address <TT>127.0.0.1</TT> will disallow remote access to the directory server. E.g.:</P>
+<PRE>
+ slapd -h ldap://127.0.0.1
+</PRE>
+<P>While the server can be configured to listen on a particular interface address, this doesn't necessarily restrict access to the server to only those networks accessible via that interface. To selective restrict remote access, it is recommend that an <A HREF="#IP Firewall">IP Firewall</A> be used to restrict access.</P>
+<P>See <A HREF="#Command-line Options">Command-line Options</A> and <EM>slapd</EM>(8) for more information.</P>
+<H3><A NAME="IP Firewall">14.1.2. IP Firewall</A></H3>
+<P><TERM>IP</TERM> firewall capabilities of the server system can be used to restrict access based upon the client's IP address and/or network interface used to communicate with the client.</P>
+<P>Generally, <EM>slapd</EM>(8) listens on port 389/tcp for <A HREF="ldap://">ldap://</A> sessions and port 636/tcp for <A HREF="ldaps://">ldaps://</A>) sessions. <EM>slapd</EM>(8) may be configured to listen on other ports.</P>
+<P>As specifics of how to configure IP firewall are dependent on the particular kind of IP firewall used, no examples are provided here. See the document associated with your IP firewall.</P>
+<H3><A NAME="TCP Wrappers">14.1.3. TCP Wrappers</A></H3>
+<P><EM>slapd</EM>(8) supports <TERM>TCP</TERM> Wrappers. TCP Wrappers provide a rule-based access control system for controlling TCP/IP access to the server. For example, the <EM>host_options</EM>(5) rule:</P>
+<PRE>
+ slapd: 10.0.0.0/255.0.0.0 127.0.0.1 : ALLOW
+ slapd: ALL : DENY
+</PRE>
+<P>allows only incoming connections from the private network <TT>10.0.0.0</TT> and localhost (<TT>127.0.0.1</TT>) to access the directory service.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>IP addresses are used as <EM>slapd</EM>(8) is not normally configured to perform reverse lookups.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>It is noted that TCP wrappers require the connection to be accepted. As significant processing is required just to deny a connection, it is generally advised that IP firewall protection be used instead of TCP wrappers.</P>
+<P>See <EM>hosts_access</EM>(5) for more information on TCP wrapper rules.</P>
+<H2><A NAME="Data Integrity and Confidentiality Protection">14.2. Data Integrity and Confidentiality Protection</A></H2>
+<P><TERM>Transport Layer Security</TERM> (TLS) can be used to provide data integrity and confidentiality protection. OpenLDAP supports negotiation of <TERM>TLS</TERM> (<TERM>SSL</TERM>) via both StartTLS and <A HREF="ldaps://">ldaps://</A>. See the <A HREF="#Using TLS">Using TLS</A> chapter for more information. StartTLS is the standard track mechanism.</P>
+<P>A number of <TERM>Simple Authentication and Security Layer</TERM> (SASL) mechanisms, such as <TERM>DIGEST-MD5</TERM> and <TERM>GSSAPI</TERM>, also provide data integrity and confidentiality protection. See the <A HREF="#Using SASL">Using SASL</A> chapter for more information.</P>
+<H3><A NAME="Security Strength Factors">14.2.1. Security Strength Factors</A></H3>
+<P>The server uses <TERM>Security Strength Factor</TERM>s (SSF) to indicate the relative strength of protection. A SSF of zero (0) indicates no protections are in place. A SSF of one (1) indicates integrity protection are in place. A SSF greater than one (&gt;1) roughly correlates to the effective encryption key length. For example, <TERM>DES</TERM> is 56, <TERM>3DES</TERM> is 112, and <TERM>AES</TERM> 128, 192, or 256.</P>
+<P>A number of administrative controls rely on SSFs associated with TLS and SASL protection in place on an LDAP session.</P>
+<P><TT>security</TT> controls disallow operations when appropriate protections are not in place. For example:</P>
+<PRE>
+ security ssf=1 update_ssf=112
+</PRE>
+<P>requires integrity protection for all operations and encryption protection, 3DES equivalent, for update operations (e.g. add, delete, modify, etc.). See <EM>slapd.conf</EM>(5) for details.</P>
+<P>For fine-grained control, SSFs may be used in access controls. See the <A HREF="#Access Control">Access Control</A> section for more information.</P>
+<H2><A NAME="Authentication Methods">14.3. Authentication Methods</A></H2>
+<H3><A NAME="&quot;simple&quot; method">14.3.1. &quot;simple&quot; method</A></H3>
+<P>The LDAP &quot;simple&quot; method has three modes of operation:</P>
+<UL>
+<LI>anonymous,
+<LI>unauthenticated, and
+<LI>user/password authenticated.</UL>
+<P>Anonymous access is requested by providing no name and no password to the &quot;simple&quot; bind operation. Unauthenticated access is requested by providing a name but no password. Authenticated access is requested by providing a valid name and password.</P>
+<P>An anonymous bind results in an <EM>anonymous</EM> authorization association. Anonymous bind mechanism is enabled by default, but can be disabled by specifying &quot;<TT>disallow bind_anon</TT>&quot; in <EM>slapd.conf</EM>(5).</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>Disabling the anonymous bind mechanism does not prevent anonymous access to the directory. To require authentication to access the directory, one should instead specify &quot;<TT>require authc</TT>&quot;.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>An unauthenticated bind also results in an <EM>anonymous</EM> authorization association. Unauthenticated bind mechanism is disabled by default, but can be enabled by specifying &quot;<TT>allow bind_anon_cred</TT>&quot; in <EM>slapd.conf</EM>(5). As a number of LDAP applications mistakenly generate unauthenticated bind request when authenticated access was intended (that is, they do not ensure a password was provided), this mechanism should generally remain disabled.</P>
+<P>A successful user/password authenticated bind results in a user authorization identity, the provided name, being associated with the session. User/password authenticated bind is enabled by default. However, as this mechanism itself offers no eavesdropping protection (e.g., the password is set in the clear), it is recommended that it be used only in tightly controlled systems or when the LDAP session is protected by other means (e.g., TLS, <TERM>IPsec</TERM>). Where the administrator relies on TLS to protect the password, it is recommended that unprotected authentication be disabled. This is done using the <TT>security</TT> directive's <TT>simple_bind</TT> option, which provides fine grain control over the level of confidential protection to require for <EM>simple</EM> user/password authentication. E.g., using <TT>security simple_bind=56</TT> would require <EM>simple</EM> binds to use encryption of DES equivalent or better.</P>
+<P>The user/password authenticated bind mechanism can be completely disabled by setting &quot;<TT>disallow bind_simple</TT>&quot;.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>An unsuccessful bind always results in the session having an <EM>anonymous</EM> authorization association.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H3><A NAME="SASL method">14.3.2. SASL method</A></H3>
+<P>The LDAP <TERM>SASL</TERM> method allows the use of any SASL authentication mechanism. The <A HREF="#Using SASL">Using SASL</A> section discusses the use of SASL.</P>
+<H2><A NAME="Password Storage">14.4. Password Storage</A></H2>
+<P>LDAP passwords are normally stored in the <EM>userPassword</EM> attribute. <A HREF="https://www.rfc-editor.org/rfc/rfc4519.txt">RFC4519</A> specifies that passwords are not stored in encrypted (or hashed) form. This allows a wide range of password-based authentication mechanisms, such as <TT>DIGEST-MD5</TT> to be used. This is also the most interoperable storage scheme.</P>
+<P>However, it may be desirable to store a hash of password instead. <EM>slapd</EM>(8) supports a variety of storage schemes for the administrator to choose from.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>Values of password attributes, regardless of storage scheme used, should be protected as if they were clear text. Hashed passwords are subject to <EM>dictionary attacks</EM> and <EM>brute-force attacks</EM>.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>The <EM>userPassword</EM> attribute is allowed to have more than one value, and it is possible for each value to be stored in a different form. During authentication, <EM>slapd</EM> will iterate through the values until it finds one that matches the offered password or until it runs out of values to inspect. The storage scheme is stored as a prefix on the value, so a hashed password using the Salted SHA1 (<TT>SSHA</TT>) scheme looks like:</P>
+<PRE>
+ userPassword: {SSHA}DkMTwBl+a/3DQTxCYEApdUtNXGgdUac3
+</PRE>
+<P>The advantage of hashed passwords is that an attacker which discovers the hash does not have direct access to the actual password. Unfortunately, as dictionary and brute force attacks are generally quite easy for attackers to successfully mount, this advantage is marginal at best (this is why all modern Unix systems use shadow password files).</P>
+<P>The disadvantages of hashed storage is that they are non-standard, may cause interoperability problem, and generally preclude the use of stronger than Simple (or SASL/PLAIN) password-based authentication mechanisms such as <TT>DIGEST-MD5</TT>.</P>
+<H3><A NAME="SSHA password storage scheme">14.4.1. SSHA password storage scheme</A></H3>
+<P>This is the salted version of the SHA scheme. It is believed to be the most secure password storage scheme supported by <EM>slapd</EM>.</P>
+<P>These values represent the same password:</P>
+<PRE>
+ userPassword: {SSHA}DkMTwBl+a/3DQTxCYEApdUtNXGgdUac3
+ userPassword: {SSHA}d0Q0626PSH9VUld7yWpR0k6BlpQmtczb
+</PRE>
+<H3><A NAME="CRYPT password storage scheme">14.4.2. CRYPT password storage scheme</A></H3>
+<P>This scheme uses the operating system's <EM>crypt(3)</EM> hash function. It normally produces the traditional Unix-style 13 character hash, but on systems with <TT>glibc2</TT> it can also generate the more secure 34-byte MD5 hash.</P>
+<PRE>
+ userPassword: {CRYPT}aUihad99hmev6
+ userPassword: {CRYPT}$1$czBJdDqS$TmkzUAb836oMxg/BmIwN.1
+</PRE>
+<P>The advantage of the CRYPT scheme is that passwords can be transferred to or from an existing Unix password file without having to know the cleartext form. Both forms of <EM>crypt</EM> include salt so they have some resistance to dictionary attacks.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>Since this scheme uses the operating system's <EM>crypt(3)</EM> hash function, it is therefore operating system specific.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H3><A NAME="MD5 password storage scheme">14.4.3. MD5 password storage scheme</A></H3>
+<P>This scheme simply takes the MD5 hash of the password and stores it in base64 encoded form:</P>
+<PRE>
+ userPassword: {MD5}Xr4ilOzQ4PCOq3aQ0qbuaQ==
+</PRE>
+<P>Although safer than cleartext storage, this is not a very secure scheme. The MD5 algorithm is fast, and because there is no salt the scheme is vulnerable to a dictionary attack.</P>
+<H3><A NAME="SMD5 password storage scheme">14.4.4. SMD5 password storage scheme</A></H3>
+<P>This improves on the basic MD5 scheme by adding salt (random data which means that there are many possible representations of a given plaintext password). For example, both of these values represent the same password:</P>
+<PRE>
+ userPassword: {SMD5}4QWGWZpj9GCmfuqEvm8HtZhZS6E=
+ userPassword: {SMD5}g2/J/7D5EO6+oPdklp5p8YtNFk4=
+</PRE>
+<H3><A NAME="SHA password storage scheme">14.4.5. SHA password storage scheme</A></H3>
+<P>Like the MD5 scheme, this simply feeds the password through an SHA hash process. SHA is thought to be more secure than MD5, but the lack of salt leaves the scheme exposed to dictionary attacks.</P>
+<PRE>
+ userPassword: {SHA}5en6G6MezRroT3XKqkdPOmY/BfQ=
+</PRE>
+<H3><A NAME="SASL password storage scheme">14.4.6. SASL password storage scheme</A></H3>
+<P>This is not really a password storage scheme at all. It uses the value of the <EM>userPassword</EM> attribute to delegate password verification to another process. See below for more information.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>This is not the same as using SASL to authenticate the LDAP session.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H2><A NAME="Pass-Through authentication">14.5. Pass-Through authentication</A></H2>
+<P>Since OpenLDAP 2.0 <EM>slapd</EM> has had the ability to delegate password verification to a separate process. This uses the <EM>sasl_checkpass(3)</EM> function so it can use any back-end server that Cyrus SASL supports for checking passwords. The choice is very wide, as one option is to use <EM>saslauthd(8)</EM> which in turn can use local files, Kerberos, an IMAP server, another LDAP server, or anything supported by the PAM mechanism.</P>
+<P>The server must be built with the <TT>--enable-spasswd</TT> configuration option to enable pass-through authentication.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>This is not the same as using a SASL mechanism to authenticate the LDAP session.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>Pass-Through authentication works only with plaintext passwords, as used in the &quot;simple bind&quot; and &quot;SASL PLAIN&quot; authentication mechanisms.</P>
+<P>Pass-Through authentication is selective: it only affects users whose <EM>userPassword</EM> attribute has a value marked with the &quot;{SASL}&quot; scheme. The format of the attribute is:</P>
+<PRE>
+ userPassword: {SASL}username@realm
+</PRE>
+<P>The <EM>username</EM> and <EM>realm</EM> are passed to the SASL authentication mechanism and are used to identify the account whose password is to be verified. This allows arbitrary mapping between entries in OpenLDAP and accounts known to the backend authentication service.</P>
+<P>It would be wise to use access control to prevent users from changing their passwords through LDAP where they have pass-through authentication enabled.</P>
+<H3><A NAME="Configuring slapd to use an authentication provider">14.5.1. Configuring slapd to use an authentication provider</A></H3>
+<P>Where an entry has a &quot;{SASL}&quot; password value, OpenLDAP delegates the whole process of validating that entry's password to Cyrus SASL. All the configuration is therefore done in SASL config files.</P>
+<P>The first file to be considered is confusingly named <EM>slapd.conf</EM> and is typically found in the SASL library directory, often <TT>/usr/lib/sasl2/slapd.conf</TT> This file governs the use of SASL when talking LDAP to <EM>slapd</EM> as well as the use of SASL backends for pass-through authentication. See <TT>options.html</TT> in the <A HREF="https://www.cyrusimap.org/sasl/">Cyrus SASL</A> docs for full details. Here is a simple example for a server that will use <EM>saslauthd</EM> to verify passwords:</P>
+<PRE>
+ mech_list: plain
+ pwcheck_method: saslauthd
+ saslauthd_path: /var/run/sasl2/mux
+</PRE>
+<H3><A NAME="Configuring saslauthd">14.5.2. Configuring saslauthd</A></H3>
+<P><EM>saslauthd</EM> is capable of using many different authentication services: see <EM>saslauthd(8)</EM> for details. A common requirement is to delegate some or all authentication to another LDAP server. Here is a sample <TT>saslauthd.conf</TT> that uses Microsoft Active Directory (AD):</P>
+<PRE>
+ ldap_servers: ldap://dc1.example.com/ ldap://dc2.example.com/
+
+ ldap_search_base: cn=Users,DC=ad,DC=example,DC=com
+ ldap_filter: (userPrincipalName=%u)
+
+ ldap_bind_dn: cn=saslauthd,cn=Users,DC=ad,DC=example,DC=com
+ ldap_password: secret
+</PRE>
+<P>In this case, <EM>saslauthd</EM> is run with the <TT>ldap</TT> authentication mechanism and is set to combine the SASL realm with the login name:</P>
+<PRE>
+ saslauthd -a ldap -r
+</PRE>
+<P>This means that the &quot;username@realm&quot; string from the <EM>userPassword</EM> attribute ends up being used to search AD for &quot;userPrincipalName=username@realm&quot; - the password is then verified by attempting to bind to AD using the entry found by the search and the password supplied by the LDAP client.</P>
+<H3><A NAME="Testing pass-through authentication">14.5.3. Testing pass-through authentication</A></H3>
+<P>It is usually best to start with the back-end authentication provider and work through <EM>saslauthd</EM> and <EM>slapd</EM> towards the LDAP client.</P>
+<P>In the AD example above, first check that the DN and password that <EM>saslauthd</EM> will use when it connects to AD are valid:</P>
+<PRE>
+ ldapsearch -x -H ldap://dc1.example.com/ \
+ -D cn=saslauthd,cn=Users,DC=ad,DC=example,DC=com \
+ -w secret \
+ -b '' \
+ -s base
+</PRE>
+<P>Next check that a sample AD user can be found:</P>
+<PRE>
+ ldapsearch -x -H ldap://dc1.example.com/ \
+ -D cn=saslauthd,cn=Users,DC=ad,DC=example,DC=com \
+ -w secret \
+ -b cn=Users,DC=ad,DC=example,DC=com \
+ &quot;(userPrincipalName=user@ad.example.com)&quot;
+</PRE>
+<P>Check that the user can bind to AD:</P>
+<PRE>
+ ldapsearch -x -H ldap://dc1.example.com/ \
+ -D cn=user,cn=Users,DC=ad,DC=example,DC=com \
+ -w userpassword \
+ -b cn=user,cn=Users,DC=ad,DC=example,DC=com \
+ -s base \
+ &quot;(objectclass=*)&quot;
+</PRE>
+<P>If all that works then <EM>saslauthd</EM> should be able to do the same:</P>
+<PRE>
+ testsaslauthd -u user@ad.example.com -p userpassword
+ testsaslauthd -u user@ad.example.com -p wrongpassword
+</PRE>
+<P>Now put the magic token into an entry in OpenLDAP:</P>
+<PRE>
+ userPassword: {SASL}user@ad.example.com
+</PRE>
+<P>It should now be possible to bind to OpenLDAP using the DN of that entry and the password of the AD user.</P>
+<P></P>
+<HR>
+<H1><A NAME="Using SASL">15. Using SASL</A></H1>
+<P>OpenLDAP clients and servers are capable of authenticating via the <TERM>Simple Authentication and Security Layer</TERM> (<TERM>SASL</TERM>) framework, which is detailed in <A HREF="https://www.rfc-editor.org/rfc/rfc4422.txt">RFC4422</A>. This chapter describes how to make use of SASL in OpenLDAP.</P>
+<P>There are several industry standard authentication mechanisms that can be used with SASL, including <TERM>GSSAPI</TERM> for <TERM>Kerberos</TERM> V, <TERM>DIGEST-MD5</TERM>, and <TERM>PLAIN</TERM> and <TERM>EXTERNAL</TERM> for use with <TERM>Transport Layer Security</TERM> (TLS).</P>
+<P>The standard client tools provided with OpenLDAP Software, such as <EM>ldapsearch</EM>(1) and <EM>ldapmodify</EM>(1), will by default attempt to authenticate the user to the <TERM>LDAP</TERM> directory server using SASL. Basic authentication service can be set up by the LDAP administrator with a few steps, allowing users to be authenticated to the slapd server as their LDAP entry. With a few extra steps, some users and services can be allowed to exploit SASL's proxy authorization feature, allowing them to authenticate themselves and then switch their identity to that of another user or service.</P>
+<P>This chapter assumes you have read <EM>Cyrus SASL for System Administrators</EM>, provided with the <A HREF="https://www.cyrusimap.org/sasl/">Cyrus SASL</A> package (in <TT>doc/sysadmin.html</TT>) and have a working Cyrus SASL installation. You should use the Cyrus SASL <TT>sample_client</TT> and <TT>sample_server</TT> to test your SASL installation before attempting to make use of it with OpenLDAP Software.</P>
+<P>Note that in the following text the term <EM>user</EM> is used to describe a person or application entity who is connecting to the LDAP server via an LDAP client, such as <EM>ldapsearch</EM>(1). That is, the term <EM>user</EM> not only applies to both an individual using an LDAP client, but to an application entity which issues LDAP client operations without direct user control. For example, an e-mail server which uses LDAP operations to access information held in an LDAP server is an application entity.</P>
+<H2><A NAME="SASL Security Considerations">15.1. SASL Security Considerations</A></H2>
+<P>SASL offers many different authentication mechanisms. This section briefly outlines security considerations.</P>
+<P>Some mechanisms, such as PLAIN and LOGIN, offer no greater security over LDAP <EM>simple</EM> authentication. Like LDAP <EM>simple</EM> authentication, such mechanisms should not be used unless you have adequate security protections in place. It is recommended that these mechanisms be used only in conjunction with <TERM>Transport Layer Security</TERM> (TLS). Use of PLAIN and LOGIN are not discussed further in this document.</P>
+<P>The DIGEST-MD5 mechanism is the mandatory-to-implement authentication mechanism for LDAPv3. Though DIGEST-MD5 is not a strong authentication mechanism in comparison with trusted third party authentication systems (such as <TERM>Kerberos</TERM> or public key systems), it does offer significant protections against a number of attacks. Unlike the <TERM>CRAM-MD5</TERM> mechanism, it prevents chosen plaintext attacks. DIGEST-MD5 is favored over the use of plaintext password mechanisms. The CRAM-MD5 mechanism is deprecated in favor of DIGEST-MD5. Use of <A HREF="#DIGEST-MD5">DIGEST-MD5</A> is discussed below.</P>
+<P>The GSSAPI mechanism utilizes <TERM>GSS-API</TERM> <TERM>Kerberos</TERM> V to provide secure authentication services. The KERBEROS_V4 mechanism is available for those using Kerberos IV. Kerberos is viewed as a secure, distributed authentication system suitable for both small and large enterprises. Use of <A HREF="#GSSAPI">GSSAPI</A> and <A HREF="#KERBEROS_V4">KERBEROS_V4</A> are discussed below.</P>
+<P>The EXTERNAL mechanism utilizes authentication services provided by lower level network services such as <TERM>Transport Layer Security</TERM> (<TERM>TLS</TERM>). When used in conjunction with <TERM>TLS</TERM> <TERM>X.509</TERM>-based public key technology, EXTERNAL offers strong authentication. TLS is discussed in the <A HREF="#Using TLS">Using TLS</A> chapter.</P>
+<P>EXTERNAL can also be used with the <TT>ldapi:///</TT> transport, as Unix-domain sockets can report the UID and GID of the client process.</P>
+<P>There are other strong authentication mechanisms to choose from, including <TERM>OTP</TERM> (one time passwords) and <TERM>SRP</TERM> (secure remote passwords). These mechanisms are not discussed in this document.</P>
+<H2><A NAME="SASL Authentication">15.2. SASL Authentication</A></H2>
+<P>Getting basic SASL authentication running involves a few steps. The first step configures your slapd server environment so that it can communicate with client programs using the security system in place at your site. This usually involves setting up a service key, a public key, or other form of secret. The second step concerns mapping authentication identities to LDAP <TERM>DN</TERM>'s, which depends on how entries are laid out in your directory. An explanation of the first step will be given in the next section using Kerberos V4 as an example mechanism. The steps necessary for your site's authentication mechanism will be similar, but a guide to every mechanism available under SASL is beyond the scope of this chapter. The second step is described in the section <A HREF="#Mapping Authentication Identities">Mapping Authentication Identities</A>.</P>
+<H3><A NAME="GSSAPI">15.2.1. GSSAPI</A></H3>
+<P>This section describes the use of the SASL GSSAPI mechanism and Kerberos V with OpenLDAP. It will be assumed that you have Kerberos V deployed, you are familiar with the operation of the system, and that your users are trained in its use. This section also assumes you have familiarized yourself with the use of the GSSAPI mechanism by reading <EM>Configuring GSSAPI and Cyrus SASL</EM> (provided with Cyrus SASL in the <TT>doc/gssapi</TT> file) and successfully experimented with the Cyrus provided <TT>sample_server</TT> and <TT>sample_client</TT> applications. General information about Kerberos is available at <A HREF="http://web.mit.edu/kerberos/www/">http://web.mit.edu/kerberos/www/</A>.</P>
+<P>To use the GSSAPI mechanism with <EM>slapd</EM>(8) one must create a service key with a principal for <EM>ldap</EM> service within the realm for the host on which the service runs. For example, if you run <EM>slapd</EM> on <TT>directory.example.com</TT> and your realm is <TT>EXAMPLE.COM</TT>, you need to create a service key with the principal:</P>
+<PRE>
+ ldap/directory.example.com@EXAMPLE.COM
+</PRE>
+<P>When <EM>slapd</EM>(8) runs, it must have access to this key. This is generally done by placing the key into a keytab file, <TT>/etc/krb5.keytab</TT>. See your Kerberos and Cyrus SASL documentation for information regarding keytab location settings.</P>
+<P>To use the GSSAPI mechanism to authenticate to the directory, the user obtains a Ticket Granting Ticket (TGT) prior to running the LDAP client. When using OpenLDAP client tools, the user may mandate use of the GSSAPI mechanism by specifying <TT>-Y GSSAPI</TT> as a command option.</P>
+<P>For the purposes of authentication and authorization, <EM>slapd</EM>(8) associates an authentication request DN of the form:</P>
+<PRE>
+ uid=&lt;primary[/instance][@realm]&gt;,cn=gssapi,cn=auth
+</PRE>
+<P>The realm is omitted by Cyrus SASL if it's equal to the default realm of the server in <TT>/etc/krb5.conf</TT>.</P>
+<P>Continuing our example, a user with the Kerberos principal <TT>kurt@EXAMPLE.COM</TT> would have the associated DN:</P>
+<PRE>
+ uid=kurt,cn=gssapi,cn=auth
+</PRE>
+<P>and the principal <TT>ursula/admin@FOREIGN.REALM</TT> would have the associated DN:</P>
+<PRE>
+ uid=ursula/admin@foreign.realm,cn=gssapi,cn=auth
+</PRE>
+<P>The authentication request DN can be used directly in ACLs and <TT>groupOfNames</TT> &quot;member&quot; attributes, since it is of legitimate LDAP DN format. Or alternatively, the authentication DN could be mapped before use. See the section <A HREF="#Mapping Authentication Identities">Mapping Authentication Identities</A> for details.</P>
+<P>If you configure the <EM>olcSaslRealm</EM> then it will be inserted as an extra component in the authorization DN, regardless of any Kerberos realms in use. For example, if you set olcSaslRealm to <TT>example.com</TT> then you will get:</P>
+<PRE>
+ uid=kurt,cn=example.com,cn=gssapi,cn=auth
+ uid=ursula/admin@foreign.realm,cn=example.com,cn=gssapi,cn=auth
+</PRE>
+<H3><A NAME="KERBEROS_V4">15.2.2. KERBEROS_V4</A></H3>
+<P>This section describes the use of the SASL KERBEROS_V4 mechanism with OpenLDAP. It will be assumed that you are familiar with the workings of the Kerberos IV security system, and that your site has Kerberos IV deployed. Your users should be familiar with authentication policy, how to receive credentials in a Kerberos ticket cache, and how to refresh expired credentials.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>KERBEROS_V4 and Kerberos IV are deprecated in favor of GSSAPI and Kerberos V.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>Client programs will need to be able to obtain a session key for use when connecting to your LDAP server. This allows the LDAP server to know the identity of the user, and allows the client to know it is connecting to a legitimate server. If encryption layers are to be used, the session key can also be used to help negotiate that option.</P>
+<P>The slapd server runs the service called &quot;<EM>ldap</EM>&quot;, and the server will require a srvtab file with a service key. SASL aware client programs will be obtaining an &quot;ldap&quot; service ticket with the user's ticket granting ticket (TGT), with the instance of the ticket matching the hostname of the OpenLDAP server. For example, if your realm is named <TT>EXAMPLE.COM</TT> and the slapd server is running on the host named <TT>directory.example.com</TT>, the <TT>/etc/srvtab</TT> file on the server will have a service key</P>
+<PRE>
+ ldap.directory@EXAMPLE.COM
+</PRE>
+<P>When an LDAP client is authenticating a user to the directory using the KERBEROS_IV mechanism, it will request a session key for that same principal, either from the ticket cache or by obtaining a new one from the Kerberos server. This will require the TGT to be available and valid in the cache as well. If it is not present or has expired, the client may print out the message:</P>
+<PRE>
+ ldap_sasl_interactive_bind_s: Local error
+</PRE>
+<P>When the service ticket is obtained, it will be passed to the LDAP server as proof of the user's identity. The server will extract the identity and realm out of the service ticket using SASL library calls, and convert them into an <EM>authentication request DN</EM> of the form</P>
+<PRE>
+ uid=&lt;username&gt;,cn=&lt;realm&gt;,cn=&lt;mechanism&gt;,cn=auth
+</PRE>
+<P>So in our above example, if the user's name were &quot;adamson&quot;, the authentication request DN would be:</P>
+<PRE>
+ uid=adamson,cn=example.com,cn=kerberos_v4,cn=auth
+</PRE>
+<P>This authentication request DN can be used directly ACLs or, alternatively, mapped prior to use. See the section <A HREF="#Mapping Authentication Identities">Mapping Authentication Identities</A> for details.</P>
+<H3><A NAME="DIGEST-MD5">15.2.3. DIGEST-MD5</A></H3>
+<P>This section describes the use of the SASL DIGEST-MD5 mechanism using secrets stored either in the directory itself or in Cyrus SASL's own database. DIGEST-MD5 relies on the client and the server sharing a &quot;secret&quot;, usually a password. The server generates a challenge and the client a response proving that it knows the shared secret. This is much more secure than simply sending the secret over the wire.</P>
+<P>Cyrus SASL supports several shared-secret mechanisms. To do this, it needs access to the plaintext password (unlike mechanisms which pass plaintext passwords over the wire, where the server can store a hashed version of the password).</P>
+<P>The server's copy of the shared-secret may be stored in Cyrus SASL's own <EM>sasldb</EM> database, in an external system accessed via <EM>saslauthd</EM>, or in LDAP database itself. In either case it is very important to apply file access controls and LDAP access controls to prevent exposure of the passwords. The configuration and commands discussed in this section assume the use of Cyrus SASL 2.1.</P>
+<P>To use secrets stored in <EM>sasldb</EM>, simply add users with the <EM>saslpasswd2</EM> command:</P>
+<PRE>
+ saslpasswd2 -c &lt;username&gt;
+</PRE>
+<P>The passwords for such users must be managed with the <EM>saslpasswd2</EM> command.</P>
+<P>To use secrets stored in the LDAP directory, place plaintext passwords in the <TT>userPassword</TT> attribute. It will be necessary to add an option to <TT>slapd.conf</TT> to make sure that passwords set using the LDAP Password Modify Operation are stored in plaintext:</P>
+<PRE>
+ password-hash {CLEARTEXT}
+</PRE>
+<P>Passwords stored in this way can be managed either with <EM>ldappasswd</EM>(1) or by simply modifying the <TT>userPassword</TT> attribute. Regardless of where the passwords are stored, a mapping will be needed from authentication request DN to user's DN.</P>
+<P>The DIGEST-MD5 mechanism produces authentication IDs of the form:</P>
+<PRE>
+ uid=&lt;username&gt;,cn=&lt;realm&gt;,cn=digest-md5,cn=auth
+</PRE>
+<P>If the default realm is used, the realm name is omitted from the ID, giving:</P>
+<PRE>
+ uid=&lt;username&gt;,cn=digest-md5,cn=auth
+</PRE>
+<P>See <A HREF="#Mapping Authentication Identities">Mapping Authentication Identities</A> below for information on optional mapping of identities.</P>
+<P>With suitable mappings in place, users can specify SASL IDs when performing LDAP operations, and the password stored in <EM>sasldb</EM> or in the directory itself will be used to verify the authentication. For example, the user identified by the directory entry:</P>
+<PRE>
+ dn: cn=Andrew Findlay+uid=u000997,dc=example,dc=com
+ objectclass: inetOrgPerson
+ objectclass: person
+ sn: Findlay
+ uid: u000997
+ userPassword: secret
+</PRE>
+<P>can issue commands of the form:</P>
+<PRE>
+ ldapsearch -Y DIGEST-MD5 -U u000997 ...
+</PRE>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>in each of the above cases, no authorization identity (e.g. <TT>-X</TT>) was provided. Unless you are attempting <A HREF="#SASL Proxy Authorization">SASL Proxy Authorization</A>, no authorization identity should be specified. The server will infer an authorization identity from authentication identity (as described below).
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H3><A NAME="EXTERNAL">15.2.4. EXTERNAL</A></H3>
+<P>The SASL EXTERNAL mechanism makes use of an authentication performed by a lower-level protocol: usually <TERM>TLS</TERM> or Unix <TERM>IPC</TERM></P>
+<P>Each transport protocol returns Authentication Identities in its own format:</P>
+<H4><A NAME="TLS Authentication Identity Format">15.2.4.1. TLS Authentication Identity Format</A></H4>
+<P>This is the Subject DN from the client-side certificate. Note that DNs are displayed differently by LDAP and by X.509, so a certificate issued to</P>
+<PRE>
+ C=gb, O=The Example Organisation, CN=A Person
+</PRE>
+<P>will produce an authentication identity of:</P>
+<PRE>
+ cn=A Person,o=The Example Organisation,c=gb
+</PRE>
+<P>Note that you must set a suitable value for TLSVerifyClient to make the server request the use of a client-side certificate. Without this, the SASL EXTERNAL mechanism will not be offered. Refer to the <A HREF="#Using TLS">Using TLS</A> chapter for details.</P>
+<H4><A NAME="IPC (ldapi:///) Identity Format">15.2.4.2. IPC (ldapi:///) Identity Format</A></H4>
+<P>This is formed from the Unix UID and GID of the client process:</P>
+<PRE>
+ gidNumber=&lt;number&gt;+uidNumber=&lt;number&gt;,cn=peercred,cn=external,cn=auth
+</PRE>
+<P>Thus, a client process running as <TT>root</TT> will be:</P>
+<PRE>
+ gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
+</PRE>
+<H3><A NAME="Mapping Authentication Identities">15.2.5. Mapping Authentication Identities</A></H3>
+<P>The authentication mechanism in the slapd server will use SASL library calls to obtain the authenticated user's &quot;username&quot;, based on whatever underlying authentication mechanism was used. This username is in the namespace of the authentication mechanism, and not in the normal LDAP namespace. As stated in the sections above, that username is reformatted into an authentication request DN of the form</P>
+<PRE>
+ uid=&lt;username&gt;,cn=&lt;realm&gt;,cn=&lt;mechanism&gt;,cn=auth
+</PRE>
+<P>or</P>
+<PRE>
+ uid=&lt;username&gt;,cn=&lt;mechanism&gt;,cn=auth
+</PRE>
+<P>depending on whether or not &lt;mechanism&gt; employs the concept of &quot;realms&quot;. Note also that the realm part will be omitted if the default realm was used in the authentication.</P>
+<P>The <EM>ldapwhoami</EM>(1) command may be used to determine the identity associated with the user. It is very useful for determining proper function of mappings.</P>
+<P>It is not intended that you should add LDAP entries of the above form to your LDAP database. Chances are you have an LDAP entry for each of the persons that will be authenticating to LDAP, laid out in your directory tree, and the tree does not start at cn=auth. But if your site has a clear mapping between the &quot;username&quot; and an LDAP entry for the person, you will be able to configure your LDAP server to automatically map a authentication request DN to the user's <EM>authentication DN</EM>.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>it is not required that the authentication request DN nor the user's authentication DN resulting from the mapping refer to an entry held in the directory. However, additional capabilities become available (see below).
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>The LDAP administrator will need to tell the slapd server how to map an authentication request DN to a user's authentication DN. This is done by adding one or more <TT>authz-regexp</TT> directives to the <EM>slapd.conf</EM>(5) file. This directive takes two arguments:</P>
+<PRE>
+ authz-regexp &lt;search pattern&gt; &lt;replacement pattern&gt;
+</PRE>
+<P>The authentication request DN is compared to the search pattern using the regular expression functions <EM>regcomp</EM>() and <EM>regexec</EM>(), and if it matches, it is rewritten as the replacement pattern. If there are multiple <TT>authz-regexp</TT> directives, only the first whose search pattern matches the authentication identity is used. The string that is output from the replacement pattern should be the authentication DN of the user or an LDAP URL. If replacement string produces a DN, the entry named by this DN need not be held by this server. If the replace string produces an LDAP URL, that LDAP URL must evaluate to one and only one entry held by this server.</P>
+<P>The search pattern can contain any of the regular expression characters listed in <EM>regexec</EM>(3C). The main characters of note are dot &quot;.&quot;, asterisk &quot;*&quot;, and the open and close parenthesis &quot;(&quot; and &quot;)&quot;. Essentially, the dot matches any character, the asterisk allows zero or more repeats of the immediately preceding character or pattern, and terms in parenthesis are remembered for the replacement pattern.</P>
+<P>The replacement pattern will produce either a DN or URL referring to the user. Anything from the authentication request DN that matched a string in parenthesis in the search pattern is stored in the variable &quot;$1&quot;. That variable &quot;$1&quot; can appear in the replacement pattern, and will be replaced by the string from the authentication request DN. If there were multiple sets of parentheses in the search pattern, the variables $2, $3, etc are used.</P>
+<H3><A NAME="Direct Mapping">15.2.6. Direct Mapping</A></H3>
+<P>Where possible, direct mapping of the authentication request DN to the user's DN is generally recommended. Aside from avoiding the expense of searching for the user's DN, it allows mapping to DNs which refer to entries not held by this server.</P>
+<P>Suppose the authentication request DN is written as:</P>
+<PRE>
+ uid=adamson,cn=example.com,cn=gssapi,cn=auth
+</PRE>
+<P>and the user's actual LDAP entry is:</P>
+<PRE>
+ uid=adamson,ou=people,dc=example,dc=com
+</PRE>
+<P>then the following <TT>authz-regexp</TT> directive in <EM>slapd.conf</EM>(5) would provide for direct mapping.</P>
+<PRE>
+ authz-regexp
+ uid=([^,]*),cn=example.com,cn=gssapi,cn=auth
+ uid=$1,ou=people,dc=example,dc=com
+</PRE>
+<P>An even more lenient rule could be written as</P>
+<PRE>
+ authz-regexp
+ uid=([^,]*),cn=[^,]*,cn=auth
+ uid=$1,ou=people,dc=example,dc=com
+</PRE>
+<P>Be careful about setting the search pattern too leniently, however, since it may mistakenly allow persons to become authenticated as a DN to which they should not have access. It is better to write several strict directives than one lenient directive which has security holes. If there is only one authentication mechanism in place at your site, and zero or one realms in use, you might be able to map between authentication identities and LDAP DN's with a single <TT>authz-regexp</TT> directive.</P>
+<P>Don't forget to allow for the case where the realm is omitted as well as the case with an explicitly specified realm. This may well require a separate <TT>authz-regexp</TT> directive for each case, with the explicit-realm entry being listed first.</P>
+<H3><A NAME="Search-based mappings">15.2.7. Search-based mappings</A></H3>
+<P>There are a number of cases where mapping to a LDAP URL may be appropriate. For instance, some sites may have person objects located in multiple areas of the LDAP tree, such as if there were an <TT>ou=accounting</TT> tree and an <TT>ou=engineering</TT> tree, with persons interspersed between them. Or, maybe the desired mapping must be based upon information in the user's information. Consider the need to map the above authentication request DN to user whose entry is as follows:</P>
+<PRE>
+ dn: cn=Mark Adamson,ou=People,dc=Example,dc=COM
+ objectclass: person
+ cn: Mark Adamson
+ uid: adamson
+</PRE>
+<P>The information in the authentication request DN is insufficient to allow the user's DN to be directly derived, instead the user's DN must be searched for. For these situations, a replacement pattern which produces a LDAP URL can be used in the <TT>authz-regexp</TT> directives. This URL will then be used to perform an internal search of the LDAP database to find the person's authentication DN.</P>
+<P>An LDAP URL, similar to other URL's, is of the form</P>
+<PRE>
+ ldap://&lt;host&gt;/&lt;base&gt;?&lt;attrs&gt;?&lt;scope&gt;?&lt;filter&gt;
+</PRE>
+<P>This contains all of the elements necessary to perform an LDAP search: the name of the server &lt;host&gt;, the LDAP DN search base &lt;base&gt;, the LDAP attributes to retrieve &lt;attrs&gt;, the search scope &lt;scope&gt; which is one of the three options &quot;base&quot;, &quot;one&quot;, or &quot;sub&quot;, and lastly an LDAP search filter &lt;filter&gt;. Since the search is for an LDAP DN within the current server, the &lt;host&gt; portion should be empty. The &lt;attrs&gt; field is also ignored since only the DN is of concern. These two elements are left in the format of the URL to maintain the clarity of what information goes where in the string.</P>
+<P>Suppose that the person in the example from above did in fact have an authentication username of &quot;adamson&quot; and that information was kept in the attribute &quot;uid&quot; in their LDAP entry. The <TT>authz-regexp</TT> directive might be written as</P>
+<PRE>
+ authz-regexp
+ uid=([^,]*),cn=example.com,cn=gssapi,cn=auth
+ ldap:///ou=people,dc=example,dc=com??one?(uid=$1)
+</PRE>
+<P>This will initiate an internal search of the LDAP database inside the slapd server. If the search returns exactly one entry, it is accepted as being the DN of the user. If there are more than one entries returned, or if there are zero entries returned, the authentication fails and the user's connection is left bound as the authentication request DN.</P>
+<P>The attributes that are used in the search filter &lt;filter&gt; in the URL should be indexed to allow faster searching. If they are not, the authentication step alone can take uncomfortably long periods, and users may assume the server is down.</P>
+<P>A more complex site might have several realms in use, each mapping to a different subtree in the directory. These can be handled with statements of the form:</P>
+<PRE>
+ # Match Engineering realm
+ authz-regexp
+ uid=([^,]*),cn=engineering.example.com,cn=digest-md5,cn=auth
+ ldap:///dc=eng,dc=example,dc=com??one?(&amp;(uid=$1)(objectClass=person))
+
+ # Match Accounting realm
+ authz-regexp
+ uid=([^,].*),cn=accounting.example.com,cn=digest-md5,cn=auth
+ ldap:///dc=accounting,dc=example,dc=com??one?(&amp;(uid=$1)(objectClass=person))
+
+ # Default realm is customers.example.com
+ authz-regexp
+ uid=([^,]*),cn=digest-md5,cn=auth
+ ldap:///dc=customers,dc=example,dc=com??one?(&amp;(uid=$1)(objectClass=person))
+</PRE>
+<P>Note that the explicitly-named realms are handled first, to avoid the realm name becoming part of the UID. Also note the use of scope and filters to limit matching to desirable entries.</P>
+<P>Note as well that <TT>authz-regexp</TT> internal search are subject to access controls. Specifically, the authentication identity must have <TT>auth</TT> access.</P>
+<P>See <EM>slapd.conf</EM>(5) for more detailed information.</P>
+<H2><A NAME="SASL Proxy Authorization">15.3. SASL Proxy Authorization</A></H2>
+<P>The SASL offers a feature known as <EM>proxy authorization</EM>, which allows an authenticated user to request that they act on the behalf of another user. This step occurs after the user has obtained an authentication DN, and involves sending an authorization identity to the server. The server will then make a decision on whether or not to allow the authorization to occur. If it is allowed, the user's LDAP connection is switched to have a binding DN derived from the authorization identity, and the LDAP session proceeds with the access of the new authorization DN.</P>
+<P>The decision to allow an authorization to proceed depends on the rules and policies of the site where LDAP is running, and thus cannot be made by SASL alone. The SASL library leaves it up to the server to make the decision. The LDAP administrator sets the guidelines of who can authorize to what identity by adding information into the LDAP database entries. By default, the authorization features are disabled, and must be explicitly configured by the LDAP administrator before use.</P>
+<H3><A NAME="Uses of Proxy Authorization">15.3.1. Uses of Proxy Authorization</A></H3>
+<P>This sort of service is useful when one entity needs to act on the behalf of many other users. For example, users may be directed to a web page to make changes to their personal information in their LDAP entry. The users authenticate to the web server to establish their identity, but the web server CGI cannot authenticate to the LDAP server as that user to make changes for them. Instead, the web server authenticates itself to the LDAP server as a service identity, say,</P>
+<PRE>
+ cn=WebUpdate,dc=example,dc=com
+</PRE>
+<P>and then it will SASL authorize to the DN of the user. Once so authorized, the CGI makes changes to the LDAP entry of the user, and as far as the slapd server can tell for its ACLs, it is the user themself on the other end of the connection. The user could have connected to the LDAP server directly and authenticated as themself, but that would require the user to have more knowledge of LDAP clients, knowledge which the web page provides in an easier format.</P>
+<P>Proxy authorization can also be used to limit access to an account that has greater access to the database. Such an account, perhaps even the root DN specified in <EM>slapd.conf</EM>(5), can have a strict list of people who can authorize to that DN. Changes to the LDAP database could then be only allowed by that DN, and in order to become that DN, users must first authenticate as one of the persons on the list. This allows for better auditing of who made changes to the LDAP database. If people were allowed to authenticate directly to the privileged account, possibly through the <TT>rootpw</TT> <EM>slapd.conf</EM>(5) directive or through a <TT>userPassword</TT> attribute, then auditing becomes more difficult.</P>
+<P>Note that after a successful proxy authorization, the original authentication DN of the LDAP connection is overwritten by the new DN from the authorization request. If a service program is able to authenticate itself as its own authentication DN and then authorize to other DN's, and it is planning on switching to several different identities during one LDAP session, it will need to authenticate itself each time before authorizing to another DN (or use a different proxy authorization mechanism). The slapd server does not keep record of the service program's ability to switch to other DN's. On authentication mechanisms like Kerberos this will not require multiple connections being made to the Kerberos server, since the user's TGT and &quot;ldap&quot; session key are valid for multiple uses for the several hours of the ticket lifetime.</P>
+<H3><A NAME="SASL Authorization Identities">15.3.2. SASL Authorization Identities</A></H3>
+<P>The SASL authorization identity is sent to the LDAP server via the <TT>-X</TT> switch for <EM>ldapsearch</EM>(1) and other tools, or in the <TT>*authzid</TT> parameter to the <EM>lutil_sasl_defaults</EM>() call. The identity can be in one of two forms, either</P>
+<PRE>
+ u:&lt;username&gt;
+</PRE>
+<P>or</P>
+<PRE>
+ dn:&lt;dn&gt;
+</PRE>
+<P>In the first form, the &lt;username&gt; is from the same namespace as the authentication identities above. It is the user's username as it is referred to by the underlying authentication mechanism. Authorization identities of this form are converted into a DN format by the same function that the authentication process used, producing an <EM>authorization request DN</EM> of the form</P>
+<PRE>
+ uid=&lt;username&gt;,cn=&lt;realm&gt;,cn=&lt;mechanism&gt;,cn=auth
+</PRE>
+<P>That authorization request DN is then run through the same <TT>authz-regexp</TT> process to convert it into a legitimate authorization DN from the database. If it cannot be converted due to a failed search from an LDAP URL, the authorization request fails with &quot;inappropriate access&quot;. Otherwise, the DN string is now a legitimate authorization DN ready to undergo approval.</P>
+<P>If the authorization identity was provided in the second form, with a <TT>&quot;dn:&quot;</TT> prefix, the string after the prefix is already in authorization DN form, ready to undergo approval.</P>
+<H3><A NAME="Proxy Authorization Rules">15.3.3. Proxy Authorization Rules</A></H3>
+<P>Once slapd has the authorization DN, the actual approval process begins. There are two attributes that the LDAP administrator can put into LDAP entries to allow authorization:</P>
+<PRE>
+ authzTo
+ authzFrom
+</PRE>
+<P>Both can be multivalued. The <TT>authzTo</TT> attribute is a source rule, and it is placed into the entry associated with the authentication DN to tell what authorization DNs the authenticated DN is allowed to assume. The second attribute is a destination rule, and it is placed into the entry associated with the requested authorization DN to tell which authenticated DNs may assume it.</P>
+<P>The choice of which authorization policy attribute to use is up to the administrator. Source rules are checked first in the person's authentication DN entry, and if none of the <TT>authzTo</TT> rules specify the authorization is permitted, the <TT>authzFrom</TT> rules in the authorization DN entry are then checked. If neither case specifies that the request be honored, the request is denied. Since the default behavior is to deny authorization requests, rules only specify that a request be allowed; there are no negative rules telling what authorizations to deny.</P>
+<P>The value(s) in the two attributes are of the same form as the output of the replacement pattern of a <TT>authz-regexp</TT> directive: either a DN or an LDAP URL. For example, if a <TT>authzTo</TT> value is a DN, that DN is one the authenticated user can authorize to. On the other hand, if the <TT>authzTo</TT> value is an LDAP URL, the URL is used as an internal search of the LDAP database, and the authenticated user can become ANY DN returned by the search. If an LDAP entry looked like:</P>
+<PRE>
+ dn: cn=WebUpdate,dc=example,dc=com
+ authzTo: ldap:///dc=example,dc=com??sub?(objectclass=person)
+</PRE>
+<P>then any user who authenticated as <TT>cn=WebUpdate,dc=example,dc=com</TT> could authorize to any other LDAP entry under the search base <TT>dc=example,dc=com</TT> which has an objectClass of <TT>Person</TT>.</P>
+<H4><A NAME="Notes on Proxy Authorization Rules">15.3.3.1. Notes on Proxy Authorization Rules</A></H4>
+<P>An LDAP URL in a <TT>authzTo</TT> or <TT>authzFrom</TT> attribute will return a set of DNs. Each DN returned will be checked. Searches which return a large set can cause the authorization process to take an uncomfortably long time. Also, searches should be performed on attributes that have been indexed by slapd.</P>
+<P>To help produce more sweeping rules for <TT>authzFrom</TT> and <TT>authzTo</TT>, the values of these attributes are allowed to be DNs with regular expression characters in them. This means a source rule like</P>
+<PRE>
+ authzTo: dn.regex:^uid=[^,]*,dc=example,dc=com$
+</PRE>
+<P>would allow that authenticated user to authorize to any DN that matches the regular expression pattern given. This regular expression comparison can be evaluated much faster than an LDAP search for <TT>(uid=*)</TT>.</P>
+<P>Also note that the values in an authorization rule must be one of the two forms: an LDAP URL or a DN (with or without regular expression characters). Anything that does not begin with &quot;<TT>ldap://</TT>&quot; is taken as a DN. It is not permissible to enter another authorization identity of the form &quot;<TT>u:&lt;username&gt;</TT>&quot; as an authorization rule.</P>
+<H4><A NAME="Policy Configuration">15.3.3.2. Policy Configuration</A></H4>
+<P>The decision of which type of rules to use, <TT>authzFrom</TT> or <TT>authzTo</TT>, will depend on the site's situation. For example, if the set of people who may become a given identity can easily be written as a search filter, then a single destination rule could be written. If the set of people is not easily defined by a search filter, and the set of people is small, it may be better to write a source rule in the entries of each of those people who should be allowed to perform the proxy authorization.</P>
+<P>By default, processing of proxy authorization rules is disabled. The <TT>authz-policy</TT> directive must be set in the <EM>slapd.conf</EM>(5) file to enable authorization. This directive can be set to <TT>none</TT> for no rules (the default), <TT>to</TT> for source rules, <TT>from</TT> for destination rules, or <TT>both</TT> for both source and destination rules.</P>
+<P>Source rules are extremely powerful. If ordinary users have access to write the <TT>authzTo</TT> attribute in their own entries, then they can write rules that would allow them to authorize as anyone else. As such, when using source rules, the <TT>authzTo</TT> attribute should be protected with an ACL that only allows privileged users to set its values.</P>
+<P></P>
+<HR>
+<H1><A NAME="Using TLS">16. Using TLS</A></H1>
+<P>OpenLDAP clients and servers are capable of using the <TERM>Transport Layer Security</TERM> (<TERM>TLS</TERM>) framework to provide integrity and confidentiality protections and to support LDAP authentication using the <TERM>SASL</TERM> <TERM>EXTERNAL</TERM> mechanism. TLS is defined in <A HREF="https://www.rfc-editor.org/rfc/rfc4346.txt">RFC4346</A>.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>For generating certificates, please reference <A HREF="http://www.openldap.org/faq/data/cache/185.html">http://www.openldap.org/faq/data/cache/185.html</A>
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H2><A NAME="TLS Certificates">16.1. TLS Certificates</A></H2>
+<P>TLS uses <TERM>X.509</TERM> certificates to carry client and server identities. All servers are required to have valid certificates, whereas client certificates are optional. Clients must have a valid certificate in order to authenticate via SASL EXTERNAL. For more information on creating and managing certificates, see the <A HREF="https://www.openssl.org/">OpenSSL</A> or <A HREF="https://gnutls.org/">GnuTLS</A> documentation, depending on which TLS implementation libraries you are using.</P>
+<H3><A NAME="Server Certificates">16.1.1. Server Certificates</A></H3>
+<P>The <TERM>DN</TERM> of a server certificate must use the <TT>CN</TT> attribute to name the server, and the <TT>CN</TT> must carry the server's fully qualified domain name. Additional alias names and wildcards may be present in the <TT>subjectAltName</TT> certificate extension. More details on server certificate names are in <A HREF="https://www.rfc-editor.org/rfc/rfc4513.txt">RFC4513</A>.</P>
+<H3><A NAME="Client Certificates">16.1.2. Client Certificates</A></H3>
+<P>The DN of a client certificate can be used directly as an authentication DN. Since X.509 is a part of the <TERM>X.500</TERM> standard and LDAP is also based on X.500, both use the same DN formats and generally the DN in a user's X.509 certificate should be identical to the DN of their LDAP entry. However, sometimes the DNs may not be exactly the same, and so the mapping facility described in <A HREF="#Mapping Authentication Identities">Mapping Authentication Identities</A> can be applied to these DNs as well.</P>
+<H2><A NAME="TLS Configuration">16.2. TLS Configuration</A></H2>
+<P>After obtaining the required certificates, a number of options must be configured on both the client and the server to enable TLS and make use of the certificates. At a minimum, the clients must be configured with the name of the file containing all of the <TERM>Certificate Authority</TERM> (CA) certificates it will trust. The server must be configured with the <TERM>CA</TERM> certificates and also its own server certificate and private key.</P>
+<P>Typically a single CA will have issued the server certificate and all of the trusted client certificates, so the server only needs to trust that one signing CA. However, a client may wish to connect to a variety of secure servers managed by different organizations, with server certificates generated by many different CAs. As such, a client is likely to need a list of many different trusted CAs in its configuration.</P>
+<H3><A NAME="Server Configuration">16.2.1. Server Configuration</A></H3>
+<P>The configuration directives for slapd belong in the global directives section of <EM>slapd.conf</EM>(5).</P>
+<H4><A NAME="TLSCACertificateFile &lt;filename&gt;">16.2.1.1. TLSCACertificateFile &lt;filename&gt;</A></H4>
+<P>This directive specifies the <TERM>PEM</TERM>-format file containing certificates for the CA's that slapd will trust. The certificate for the CA that signed the server certificate must be included among these certificates. If the signing CA was not a top-level (root) CA, certificates for the entire sequence of CA's from the signing CA to the top-level CA should be present. Multiple certificates are simply appended to the file; the order is not significant.</P>
+<H4><A NAME="TLSCACertificatePath &lt;path&gt;">16.2.1.2. TLSCACertificatePath &lt;path&gt;</A></H4>
+<P>This directive specifies the path of a directory that contains individual <TERM>CA</TERM> certificates in separate files. In addition, this directory must be specially managed using the OpenSSL <EM>rehash</EM> command. When using this feature, the OpenSSL library will attempt to locate certificate files based on a hash of their name and serial number. The OpenSSL <EM>rehash</EM> command is used to generate symbolic links with the hashed names that point to the actual certificate files. As such, this option can only be used with a filesystem that actually supports symbolic links. In general, it is simpler to use the <TT>TLSCACertificateFile</TT> directive instead.</P>
+<H4><A NAME="TLSCertificateFile &lt;filename&gt;">16.2.1.3. TLSCertificateFile &lt;filename&gt;</A></H4>
+<P>This directive specifies the file that contains the slapd server certificate. Certificates are generally public information and require no special protection.</P>
+<H4><A NAME="TLSCertificateKeyFile &lt;filename&gt;">16.2.1.4. TLSCertificateKeyFile &lt;filename&gt;</A></H4>
+<P>This directive specifies the file that contains the private key that matches the certificate stored in the <TT>TLSCertificateFile</TT> file. Private keys themselves are sensitive data and are usually password encrypted for protection. However, the current implementation doesn't support encrypted keys so the key must not be encrypted and the file itself must be protected carefully.</P>
+<H4><A NAME="TLSCipherSuite &lt;cipher-suite-spec&gt;">16.2.1.5. TLSCipherSuite &lt;cipher-suite-spec&gt;</A></H4>
+<P>This directive configures what ciphers will be accepted and the preference order. <TT>&lt;cipher-suite-spec&gt;</TT> should be a cipher specification for OpenSSL. You can use the command</P>
+<PRE>
+ openssl ciphers -v ALL
+</PRE>
+<P>to obtain a verbose list of available cipher specifications.</P>
+<P>Besides the individual cipher names, the specifiers <TT>HIGH</TT>, <TT>MEDIUM</TT>, <TT>LOW</TT>, <TT>EXPORT</TT>, and <TT>EXPORT40</TT> may be helpful, along with <TT>TLSv1</TT>, <TT>SSLv3</TT>, and <TT>SSLv2</TT>.</P>
+<P>To obtain the list of ciphers in GnuTLS use:</P>
+<PRE>
+ gnutls-cli -l
+</PRE>
+<H4><A NAME="TLSRandFile &lt;filename&gt;">16.2.1.6. TLSRandFile &lt;filename&gt;</A></H4>
+<P>This directive specifies the file to obtain random bits from when <TT>/dev/urandom</TT> is not available. If the system provides <TT>/dev/urandom</TT> then this option is not needed, otherwise a source of random data must be configured. Some systems (e.g. Linux) provide <TT>/dev/urandom</TT> by default, while others (e.g. Solaris) require the installation of a patch to provide it, and others may not support it at all. In the latter case, EGD or PRNGD should be installed, and this directive should specify the name of the EGD/PRNGD socket. The environment variable <TT>RANDFILE</TT> can also be used to specify the filename. Also, in the absence of these options, the <TT>.rnd</TT> file in the slapd user's home directory may be used if it exists. To use the <TT>.rnd</TT> file, just create the file and copy a few hundred bytes of arbitrary data into the file. The file is only used to provide a seed for the pseudo-random number generator, and it doesn't need very much data to work.</P>
+<P>This directive is ignored with GnuTLS.</P>
+<H4><A NAME="TLSDHParamFile &lt;filename&gt;">16.2.1.7. TLSDHParamFile &lt;filename&gt;</A></H4>
+<P>This directive specifies the file that contains parameters for Diffie-Hellman ephemeral key exchange. This is required in order to use DHE-based cipher suites, including all DSA-based suites (i.e. <TT>TLSCertificateKeyFile</TT> points to a DSA key), and RSA when the 'key encipherment' key usage is not specified in the certificate. Parameters can be generated using the following command</P>
+<PRE>
+ openssl dhparam [-dsaparam] -out &lt;filename&gt; &lt;numbits&gt; or
+ certtool --generate-dh-params --bits &lt;numbits&gt; --outfile &lt;filename&gt;
+</PRE>
+<H4><A NAME="TLSECName &lt;name&gt;">16.2.1.8. TLSECName &lt;name&gt;</A></H4>
+<P>This directive specifies the curve to use for Elliptic Curve Diffie-Hellman ephemeral key exchange. This option is only needed to use ECDHE-based cipher suites in OpenSSL. The names of supported curves may be shown using the following command</P>
+<PRE>
+ openssl ecparam -list_curves
+</PRE>
+<P>See the OpenSSL documentation for details. This directive is not used for GnuTLS. For GnuTLS the curves may be specified in the ciphersuite.</P>
+<H4><A NAME="TLSVerifyClient { never | allow | try | demand }">16.2.1.9. TLSVerifyClient { never | allow | try | demand }</A></H4>
+<P>This directive specifies what checks to perform on client certificates in an incoming TLS session, if any. This option is set to <TT>never</TT> by default, in which case the server never asks the client for a certificate. With a setting of <TT>allow</TT> the server will ask for a client certificate; if none is provided the session proceeds normally. If a certificate is provided but the server is unable to verify it, the certificate is ignored and the session proceeds normally, as if no certificate had been provided. With a setting of <TT>try</TT> the certificate is requested, and if none is provided, the session proceeds normally. If a certificate is provided and it cannot be verified, the session is immediately terminated. With a setting of <TT>demand</TT> the certificate is requested and a valid certificate must be provided, otherwise the session is immediately terminated.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>The server must request a client certificate in order to use the SASL EXTERNAL authentication mechanism with a TLS session. As such, a non-default <TT>TLSVerifyClient</TT> setting must be configured before SASL EXTERNAL authentication may be attempted, and the SASL EXTERNAL mechanism will only be offered to the client if a valid client certificate was received.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H3><A NAME="Client Configuration">16.2.2. Client Configuration</A></H3>
+<P>Most of the client configuration directives parallel the server directives. The names of the directives are different, and they go into <EM>ldap.conf</EM>(5) instead of <EM>slapd.conf</EM>(5), but their functionality is mostly the same. Also, while most of these options may be configured on a system-wide basis, they may all be overridden by individual users in their <EM>.ldaprc</EM> files.</P>
+<P>The LDAP Start TLS operation is used in LDAP to initiate TLS negotiation. All OpenLDAP command line tools support a <TT>-Z</TT> and <TT>-ZZ</TT> flag to indicate whether a Start TLS operation is to be issued. The latter flag indicates that the tool is to cease processing if TLS cannot be started while the former allows the command to continue.</P>
+<P>In LDAPv2 environments, TLS is normally started using the LDAP Secure URI scheme (<TT>ldaps://</TT>) instead of the normal LDAP URI scheme (<TT>ldap://</TT>). OpenLDAP command line tools allow either scheme to used with the <TT>-H</TT> flag and with the <TT>URI</TT> <EM>ldap.conf</EM>(5) option.</P>
+<H4><A NAME="TLS_CACERT &lt;filename&gt;">16.2.2.1. TLS_CACERT &lt;filename&gt;</A></H4>
+<P>This is equivalent to the server's <TT>TLSCACertificateFile</TT> option. As noted in the <A HREF="#TLS Configuration">TLS Configuration</A> section, a client typically may need to know about more CAs than a server, but otherwise the same considerations apply.</P>
+<H4><A NAME="TLS_CACERTDIR &lt;path&gt;">16.2.2.2. TLS_CACERTDIR &lt;path&gt;</A></H4>
+<P>This is equivalent to the server's <TT>TLSCACertificatePath</TT> option. The specified directory must be managed with the OpenSSL <EM>rehash</EM> command as well.</P>
+<H4><A NAME="TLS_CERT &lt;filename&gt;">16.2.2.3. TLS_CERT &lt;filename&gt;</A></H4>
+<P>This directive specifies the file that contains the client certificate. This is a user-only directive and can only be specified in a user's <EM>.ldaprc</EM> file.</P>
+<H4><A NAME="TLS_KEY &lt;filename&gt;">16.2.2.4. TLS_KEY &lt;filename&gt;</A></H4>
+<P>This directive specifies the file that contains the private key that matches the certificate stored in the <TT>TLS_CERT</TT> file. The same constraints mentioned for <TT>TLSCertificateKeyFile</TT> apply here. This is also a user-only directive.</P>
+<H4><A NAME="TLS_RANDFILE &lt;filename&gt;">16.2.2.5. TLS_RANDFILE &lt;filename&gt;</A></H4>
+<P>This directive is the same as the server's <TT>TLSRandFile</TT> option.</P>
+<H4><A NAME="TLS_REQCERT { never | allow | try | demand }">16.2.2.6. TLS_REQCERT { never | allow | try | demand }</A></H4>
+<P>This directive is equivalent to the server's <TT>TLSVerifyClient</TT> option. However, for clients the default value is <TT>demand</TT> and there generally is no good reason to change this setting.</P>
+<P></P>
+<HR>
+<H1><A NAME="Constructing a Distributed Directory Service">17. Constructing a Distributed Directory Service</A></H1>
+<P>For many sites, running one or more <EM>slapd</EM>(8) that hold an entire subtree of data is sufficient. But often it is desirable to have one <EM>slapd</EM> refer to other directory services for a certain part of the tree (which may or may not be running <EM>slapd</EM>).</P>
+<P><EM>slapd</EM> supports <EM>subordinate</EM> and <EM>superior</EM> knowledge information. Subordinate knowledge information is held in <TT>referral</TT> objects (<A HREF="https://www.rfc-editor.org/rfc/rfc3296.txt">RFC3296</A>).</P>
+<H2><A NAME="Subordinate Knowledge Information">17.1. Subordinate Knowledge Information</A></H2>
+<P>Subordinate knowledge information may be provided to delegate a subtree. Subordinate knowledge information is maintained in the directory as a special <EM>referral</EM> object at the delegate point. The referral object acts as a delegation point, gluing two services together. This mechanism allows for hierarchical directory services to be constructed.</P>
+<P>A referral object has a structural object class of <TT>referral</TT> and has the same <TERM>Distinguished Name</TERM> as the delegated subtree. Generally, the referral object will also provide the auxiliary object class <TT>extensibleObject</TT>. This allows the entry to contain appropriate <TERM>Relative Distinguished Name</TERM> values. This is best demonstrated by example.</P>
+<P>If the server <TT>a.example.net</TT> holds <TT>dc=example,dc=net</TT> and wished to delegate the subtree <TT>ou=subtree,dc=example,dc=net</TT> to another server <TT>b.example.net</TT>, the following named referral object would be added to <TT>a.example.net</TT>:</P>
+<PRE>
+ dn: dc=subtree,dc=example,dc=net
+ objectClass: referral
+ objectClass: extensibleObject
+ dc: subtree
+ ref: ldap://b.example.net/dc=subtree,dc=example,dc=net
+</PRE>
+<P>The server uses this information to generate referrals and search continuations to subordinate servers.</P>
+<P>For those familiar with <TERM>X.500</TERM>, a <EM>named referral</EM> object is similar to an X.500 knowledge reference held in a <EM>subr</EM> <TERM>DSE</TERM>.</P>
+<H2><A NAME="Superior Knowledge Information">17.2. Superior Knowledge Information</A></H2>
+<P>Superior knowledge information may be specified using the <TT>referral</TT> directive. The value is a list of <TERM>URI</TERM>s referring to superior directory services. For servers without immediate superiors, such as for <TT>a.example.net</TT> in the example above, the server can be configured to use a directory service with <EM>global knowledge</EM>, such as the <EM>OpenLDAP Root Service</EM> (<A HREF="http://www.openldap.org/faq/index.cgi?file=393">http://www.openldap.org/faq/index.cgi?file=393</A>).</P>
+<PRE>
+ referral ldap://root.openldap.org/
+</PRE>
+<P>However, as <TT>a.example.net</TT> is the <EM>immediate superior</EM> to <TT>b.example.net</TT>, <EM>b.example.net</EM> would be configured as follows:</P>
+<PRE>
+ referral ldap://a.example.net/
+</PRE>
+<P>The server uses this information to generate referrals for operations acting upon entries not within or subordinate to any of the naming contexts held by the server.</P>
+<P>For those familiar with <TERM>X.500</TERM>, this use of the <TT>ref</TT> attribute is similar to an X.500 knowledge reference held in a <EM>Supr</EM> <TERM>DSE</TERM>.</P>
+<H2><A NAME="The ManageDsaIT Control">17.3. The ManageDsaIT Control</A></H2>
+<P>Adding, modifying, and deleting referral objects is generally done using <EM>ldapmodify</EM>(1) or similar tools which support the ManageDsaIT control. The ManageDsaIT control informs the server that you intend to manage the referral object as a regular entry. This keeps the server from sending a referral result for requests which interrogate or update referral objects.</P>
+<P>The ManageDsaIT control should not be specified when managing regular entries.</P>
+<P>The <TT>-M</TT> option of <EM>ldapmodify</EM>(1) (and other tools) enables ManageDsaIT. For example:</P>
+<PRE>
+ ldapmodify -M -f referral.ldif -x -D &quot;cn=Manager,dc=example,dc=net&quot; -W
+</PRE>
+<P>or with <EM>ldapsearch</EM>(1):</P>
+<PRE>
+ ldapsearch -M -b &quot;dc=example,dc=net&quot; -x &quot;(objectclass=referral)&quot; '*' ref
+</PRE>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>the <TT>ref</TT> attribute is operational and must be explicitly requested when desired in search results.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>the use of referrals to construct a Distributed Directory Service is extremely clumsy and not well supported by common clients. If an existing installation has already been built using referrals, the use of the <EM>chain</EM> overlay to hide the referrals will greatly improve the usability of the Directory system. A better approach would be to use explicitly defined local and proxy databases in <EM>subordinate</EM> configurations to provide a seamless view of the Distributed Directory.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>LDAP operations, even subtree searches, normally access only one database. That can be changed by gluing databases together with the <B>subordinate</B>/<B>olcSubordinate</B> keyword. Please see <EM>slapd.conf</EM>(5) and <EM>slapd-config</EM>(5).
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P></P>
+<HR>
+<H1><A NAME="Replication">18. Replication</A></H1>
+<P>Replicated directories are a fundamental requirement for delivering a resilient enterprise deployment.</P>
+<P><A HREF="https://www.openldap.org/">OpenLDAP</A> has various configuration options for creating a replicated directory. In previous releases, replication was discussed in terms of a <EM>master</EM> server and some number of <EM>slave</EM> servers. A master accepted directory updates from other clients, and a slave only accepted updates from a (single) master. The replication structure was rigidly defined and any particular database could only fulfill a single role, either master or slave. Another historic term introduced with OpenLDAP 2.4 was multimaster.</P>
+<P>As OpenLDAP now supports a wide variety of replication topologies, these terms have been deprecated in favor of <EM>provider</EM>/<EM>multi-provider</EM> and <EM>consumer</EM>: A provider can accept external write operations and make them available for retrieval by consumers; consumers request replication updates from providers. Unlike the rigidly defined master/slave relationships, provider/consumer roles are quite fluid: replication updates received in a consumer can be further propagated by that consumer to other servers, so a consumer can also act simultaneously as a provider. Also, a consumer need not be an actual LDAP server; it may be just an LDAP client.</P>
+<P>The following sections will describe the replication technology and discuss the various replication options that are available.</P>
+<H2><A NAME="Replication Technology">18.1. Replication Technology</A></H2>
+<H3><A NAME="LDAP Sync Replication">18.1.1. LDAP Sync Replication</A></H3>
+<P>The <TERM>LDAP Sync</TERM> Replication engine, <TERM>syncrepl</TERM> for short, is a consumer-side replication engine that enables the consumer <TERM>LDAP</TERM> server to maintain a shadow copy of a <TERM>DIT</TERM> fragment. A syncrepl engine resides at the consumer and executes as one of the <EM>slapd</EM>(8) threads. It creates and maintains a replica by connecting to the replication provider to perform the initial DIT content load followed either by periodic content polling or by timely updates upon content changes.</P>
+<P>Syncrepl uses the LDAP Content Synchronization protocol (or LDAP Sync for short) as the consumer synchronization protocol. LDAP Sync provides a stateful replication which supports both pull-based and push-based synchronization and does not mandate the use of a history store. In pull-based replication the consumer periodically polls the provider for updates. In push-based replication the consumer listens for updates that are sent by the provider in realtime. Since the protocol does not require a history store, the provider does not need to maintain any log of updates it has received (Note that the syncrepl engine is extensible and additional replication protocols may be supported in the future.).</P>
+<P>Syncrepl keeps track of the status of the replication content by maintaining and exchanging synchronization cookies. Because the syncrepl consumer and provider maintain their content status, the consumer can poll the provider content to perform incremental synchronization by asking for the entries required to make the consumer up-to-date with the provider content. Syncrepl also enables convenient management of consumers by maintaining replication status. The consumer database can be constructed from a consumer-side or a provider-side backup at any synchronization status. Syncrepl can automatically resynchronize the consumer database to be up-to-date with the current provider content.</P>
+<P>Syncrepl supports both pull-based and push-based synchronization. In its basic refreshOnly synchronization mode, the provider uses pull-based synchronization where the consumer servers need not be tracked and no history information is maintained. The information required for the provider to process periodic polling requests is contained in the synchronization cookie of the request itself. To optimize the pull-based synchronization, syncrepl utilizes the present phase of the LDAP Sync protocol as well as its delete phase, instead of falling back on frequent full reloads. To further optimize the pull-based synchronization, the provider can maintain a per-scope session log as a history store. In its refreshAndPersist mode of synchronization, the provider uses a push-based synchronization. The provider keeps track of the consumer servers that have requested a persistent search and sends them necessary updates as the provider replication content gets modified.</P>
+<P>With syncrepl, a consumer can create a replication agreement without changing the provider's configurations and without restarting the provider server, if the consumer server has appropriate access privileges for the DIT fragment to be replicated. The consumer server can stop the replication also without the need for provider-side changes and restart.</P>
+<P>Syncrepl supports partial, sparse, and fractional replications. The shadow DIT fragment is defined by a general search criteria consisting of base, scope, filter, and attribute list. The consumer content is also subject to the access privileges of the bind identity of the syncrepl replication connection.</P>
+<H4><A NAME="The LDAP Content Synchronization Protocol">18.1.1.1. The LDAP Content Synchronization Protocol</A></H4>
+<P>The LDAP Sync protocol allows a client to maintain a synchronized copy of a DIT fragment. The LDAP Sync operation is defined as a set of controls and other protocol elements which extend the LDAP search operation. This section introduces the LDAP Content Sync protocol only briefly. For more information, refer to <A HREF="https://www.rfc-editor.org/rfc/rfc4533.txt">RFC4533</A>.</P>
+<P>The LDAP Sync protocol supports both polling and listening for changes by defining two respective synchronization operations: <EM>refreshOnly</EM> and <EM>refreshAndPersist</EM>. Polling is implemented by the <EM>refreshOnly</EM> operation. The consumer polls the provider using an LDAP Search request with an LDAP Sync control attached. The consumer copy is synchronized to the provider copy at the time of polling using the information returned in the search. The provider finishes the search operation by returning <EM>SearchResultDone</EM> at the end of the search operation as in the normal search. Listening is implemented by the <EM>refreshAndPersist</EM> operation. As the name implies, it begins with a search, like refreshOnly. Instead of finishing the search after returning all entries currently matching the search criteria, the synchronization search remains persistent in the provider. Subsequent updates to the synchronization content in the provider cause additional entry updates to be sent to the consumer.</P>
+<P>The <EM>refreshOnly</EM> operation and the refresh stage of the <EM>refreshAndPersist</EM> operation can be performed with a present phase or a delete phase.</P>
+<P>In the present phase, the provider sends the consumer the entries updated within the search scope since the last synchronization. The provider sends all requested attributes, be they changed or not, of the updated entries. For each unchanged entry which remains in the scope, the provider sends a present message consisting only of the name of the entry and the synchronization control representing state present. The present message does not contain any attributes of the entry. After the consumer receives all update and present entries, it can reliably determine the new consumer copy by adding the entries added to the provider, by replacing the entries modified at the provider, and by deleting entries in the consumer copy which have not been updated nor specified as being present at the provider.</P>
+<P>The transmission of the updated entries in the delete phase is the same as in the present phase. The provider sends all the requested attributes of the entries updated within the search scope since the last synchronization to the consumer. In the delete phase, however, the provider sends a delete message for each entry deleted from the search scope, instead of sending present messages. The delete message consists only of the name of the entry and the synchronization control representing state delete. The new consumer copy can be determined by adding, modifying, and removing entries according to the synchronization control attached to the <EM>SearchResultEntry</EM> message.</P>
+<P>In the case that the LDAP Sync provider maintains a history store and can determine which entries are scoped out of the consumer copy since the last synchronization time, the provider can use the delete phase. If the provider does not maintain any history store, cannot determine the scoped-out entries from the history store, or the history store does not cover the outdated synchronization state of the consumer, the provider should use the present phase. The use of the present phase is much more efficient than a full content reload in terms of the synchronization traffic. To reduce the synchronization traffic further, the LDAP Sync protocol also provides several optimizations such as the transmission of the normalized <TT>entryUUID</TT>s and the transmission of multiple <TT>entryUUIDs</TT> in a single <EM>syncIdSet</EM> message.</P>
+<P>At the end of the <EM>refreshOnly</EM> synchronization, the provider sends a synchronization cookie to the consumer as a state indicator of the consumer copy after the synchronization is completed. The consumer will present the received cookie when it requests the next incremental synchronization to the provider.</P>
+<P>When <EM>refreshAndPersist</EM> synchronization is used, the provider sends a synchronization cookie at the end of the refresh stage by sending a Sync Info message with refreshDone=TRUE. It also sends a synchronization cookie by attaching it to <EM>SearchResultEntry</EM> messages generated in the persist stage of the synchronization search. During the persist stage, the provider can also send a Sync Info message containing the synchronization cookie at any time the provider wants to update the consumer-side state indicator.</P>
+<P>In the LDAP Sync protocol, entries are uniquely identified by the <TT>entryUUID</TT> attribute value. It can function as a reliable identifier of the entry. The DN of the entry, on the other hand, can be changed over time and hence cannot be considered as the reliable identifier. The <TT>entryUUID</TT> is attached to each <EM>SearchResultEntry</EM> or <EM>SearchResultReference</EM> as a part of the synchronization control.</P>
+<H4><A NAME="Syncrepl Details">18.1.1.2. Syncrepl Details</A></H4>
+<P>The syncrepl engine utilizes both the <EM>refreshOnly</EM> and the <EM>refreshAndPersist</EM> operations of the LDAP Sync protocol. If a syncrepl specification is included in a database definition, <EM>slapd</EM>(8) launches a syncrepl engine as a <EM>slapd</EM>(8) thread and schedules its execution. If the <EM>refreshOnly</EM> operation is specified, the syncrepl engine will be rescheduled at the interval time after a synchronization operation is completed. If the <EM>refreshAndPersist</EM> operation is specified, the engine will remain active and process the persistent synchronization messages from the provider.</P>
+<P>The syncrepl engine utilizes both the present phase and the delete phase of the refresh synchronization. It is possible to configure a session log in the provider which stores the <TT>entryUUID</TT>s of a finite number of entries deleted from a database. Multiple consumers share the same session log. The syncrepl engine uses the delete phase if the session log is present and the state of the consumer server is recent enough that no session log entries are truncated after the last synchronization of the client. The syncrepl engine uses the present phase if no session log is configured for the replication content or if the consumer is too outdated to be covered by the session log. The current design of the session log store is memory based, so the information contained in the session log is not persistent over multiple provider invocations. It is not currently supported to access the session log store by using LDAP operations. It is also not currently supported to impose access control to the session log.</P>
+<P>As a further optimization, even in the case the synchronization search is not associated with any session log, no entries will be transmitted to the consumer server when there has been no update in the replication context.</P>
+<P>The syncrepl engine, which is a consumer-side replication engine, can work with any backends. The LDAP Sync provider can be configured as an overlay on any backend, but works best with the <EM>back-mdb</EM> backend.</P>
+<P>The LDAP Sync provider maintains a <TT>contextCSN</TT> for each database as the current synchronization state indicator of the provider content. It is the largest <TT>entryCSN</TT> in the provider context such that no transactions for an entry having smaller <TT>entryCSN</TT> value remains outstanding. The <TT>contextCSN</TT> could not just be set to the largest issued <TT>entryCSN</TT> because <TT>entryCSN</TT> is obtained before a transaction starts and transactions are not committed in the issue order.</P>
+<P>The provider stores the <TT>contextCSN</TT> of a context in the <TT>contextCSN</TT> attribute of the context suffix entry. The attribute is not written to the database after every update operation though; instead it is maintained primarily in memory. At database start time the provider reads the last saved <TT>contextCSN</TT> into memory and uses the in-memory copy exclusively thereafter. By default, changes to the <TT>contextCSN</TT> as a result of database updates will not be written to the database until the server is cleanly shut down. A checkpoint facility exists to cause the <TT>contextCSN</TT> to be written out more frequently if desired.</P>
+<P>Note that at startup time, if the provider is unable to read a <TT>contextCSN</TT> from the suffix entry, it will scan the entire database to determine the value, and this scan may take quite a long time on a large database. When a <TT>contextCSN</TT> value is read, the database will still be scanned for any <TT>entryCSN</TT> values greater than it, to make sure the <TT>contextCSN</TT> value truly reflects the greatest committed <TT>entryCSN</TT> in the database. On databases which support inequality indexing, setting an eq index on the <TT>entryCSN</TT> attribute and configuring <EM>contextCSN</EM> checkpoints will greatly speed up this scanning step.</P>
+<P>If no <TT>contextCSN</TT> can be determined by reading and scanning the database, a new value will be generated. Also, if scanning the database yielded a greater <TT>entryCSN</TT> than was previously recorded in the suffix entry's <TT>contextCSN</TT> attribute, a checkpoint will be immediately written with the new value.</P>
+<P>The consumer also stores its replication state, which is the provider's <TT>contextCSN</TT> received as a synchronization cookie, in the <TT>contextCSN</TT> attribute of the suffix entry. The replication state maintained by a consumer server is used as the synchronization state indicator when it performs subsequent incremental synchronization with the provider server. It is also used as a provider-side synchronization state indicator when it functions as a secondary provider server in a cascading replication configuration. Since the consumer and provider state information are maintained in the same location within their respective databases, any consumer can be promoted to a provider (and vice versa) without any special actions.</P>
+<P>Because a general search filter can be used in the syncrepl specification, some entries in the context may be omitted from the synchronization content. The syncrepl engine creates a glue entry to fill in the holes in the consumer context if any part of the consumer content is subordinate to the holes. The glue entries will not be returned in the search result unless <EM>ManageDsaIT</EM> control is provided.</P>
+<P>Also as a consequence of the search filter used in the syncrepl specification, it is possible for a modification to remove an entry from the replication scope even though the entry has not been deleted on the provider. Logically the entry must be deleted on the consumer but in <EM>refreshOnly</EM> mode the provider cannot detect and propagate this change without the use of the session log on the provider.</P>
+<P>For configuration, please see the <A HREF="#Syncrepl">Syncrepl</A> section.</P>
+<H2><A NAME="Deployment Alternatives">18.2. Deployment Alternatives</A></H2>
+<P>While the LDAP Sync specification only defines a narrow scope for replication, the OpenLDAP implementation is extremely flexible and supports a variety of operating modes to handle other scenarios not explicitly addressed in the spec.</P>
+<H3><A NAME="Delta-syncrepl replication">18.2.1. Delta-syncrepl replication</A></H3>
+<UL>
+<LI>Disadvantages of LDAP Sync replication:</UL>
+<P>LDAP Sync replication is an object-based replication mechanism. When any attribute value in a replicated object is changed on the provider, each consumer fetches and processes the complete changed object, including <B>both the changed and unchanged attribute values</B> during replication. One advantage of this approach is that when multiple changes occur to a single object, the precise sequence of those changes need not be preserved; only the final state of the entry is significant. But this approach may have drawbacks when the usage pattern involves single changes to multiple objects.</P>
+<P>For example, suppose you have a database consisting of 102,400 objects of 1 KB each. Further, suppose you routinely run a batch job to change the value of a single two-byte attribute value that appears in each of the 102,400 objects on the provider. Not counting LDAP and TCP/IP protocol overhead, each time you run this job each consumer will transfer and process <B>100 MB</B> of data to process <B>200KB of changes!</B></P>
+<P>99.98% of the data that is transmitted and processed in a case like this will be redundant, since it represents values that did not change. This is a waste of valuable transmission and processing bandwidth and can cause an unacceptable replication backlog to develop. While this situation is extreme, it serves to demonstrate a very real problem that is encountered in some LDAP deployments.</P>
+<UL>
+<LI>Where Delta-syncrepl comes in:</UL>
+<P>Delta-syncrepl, a changelog-based variant of syncrepl, is designed to address situations like the one described above. Delta-syncrepl works by maintaining a changelog of a selectable depth in a separate database on the provider. The replication consumer checks the changelog for the changes it needs and, as long as the changelog contains the needed changes, the consumer fetches the changes from the changelog and applies them to its database. If, however, a consumer is too far out of sync (or completely empty), conventional syncrepl is used to bring it up to date and replication then switches back to the delta-syncrepl mode.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>since the database state is stored in both the changelog DB and the main DB on the provider, it is important to backup/restore both the changelog DB and the main DB using slapcat/slapadd when restoring a DB or copying it to another machine.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>For configuration, please see the <A HREF="#Delta-syncrepl">Delta-syncrepl</A> section.</P>
+<H3><A NAME="N-Way Multi-Provider Replication">18.2.2. N-Way Multi-Provider Replication</A></H3>
+<P>Multi-Provider replication is a replication technique using Syncrepl to replicate data to multiple provider (&quot;Provider&quot;) Directory servers.</P>
+<H4><A NAME="Valid Arguments for Multi-Provider replication">18.2.2.1. Valid Arguments for Multi-Provider replication</A></H4>
+<UL>
+<LI>If any provider fails, other providers will continue to accept updates
+<LI>Avoids a single point of failure
+<LI>Providers can be located in several physical sites i.e. distributed across the network/globe.
+<LI>Good for Automatic failover/High Availability</UL>
+<H4><A NAME="Invalid Arguments for Multi-Provider replication">18.2.2.2. Invalid Arguments for Multi-Provider replication</A></H4>
+<P>(These are often claimed to be advantages of Multi-Provider replication but those claims are false):</P>
+<UL>
+<LI>It has <B>NOTHING</B> to do with load balancing
+<LI>Providers <B>must</B> propagate writes to <B>all</B> the other servers, which means the network traffic and write load spreads across all of the servers the same as for single-provider.
+<LI>Server utilization and performance are at best identical for Multi-Provider and Single-Provider replication; at worst Single-Provider is superior because indexing can be tuned differently to optimize for the different usage patterns between the provider and the consumers.</UL>
+<H4><A NAME="Arguments against Multi-Provider replication">18.2.2.3. Arguments against Multi-Provider replication</A></H4>
+<UL>
+<LI>Breaks the data consistency guarantees of the directory model
+<LI><A HREF="http://www.openldap.org/faq/data/cache/1240.html">http://www.openldap.org/faq/data/cache/1240.html</A>
+<LI>If connectivity with a provider is lost because of a network partition, then &quot;automatic failover&quot; can just compound the problem
+<LI>Typically, a particular machine cannot distinguish between losing contact with a peer because that peer crashed, or because the network link has failed
+<LI>If a network is partitioned and multiple clients start writing to each of the &quot;providers&quot; then reconciliation will be a pain; it may be best to simply deny writes to the clients that are partitioned from the single provider</UL>
+<P>For configuration, please see the <A HREF="#N-Way Multi-Provider">N-Way Multi-Provider</A> section below</P>
+<H3><A NAME="Mirror mode replication">18.2.3. Mirror mode replication</A></H3>
+<P>Mirror mode is a hybrid configuration that provides all of the consistency guarantees of single-provider replication, while also providing the high availability of multi-provider. In Mirror mode two providers are set up to replicate from each other (as a multi-provider configuration), but an external frontend is employed to direct all writes to only one of the two servers. The second provider will only be used for writes if the first provider crashes, at which point the frontend will switch to directing all writes to the second provider. When a crashed provider is repaired and restarted it will automatically catch up to any changes on the running provider and resync.</P>
+<H4><A NAME="Arguments for Mirror mode">18.2.3.1. Arguments for Mirror mode</A></H4>
+<UL>
+<LI>Provides a high-availability (HA) solution for directory writes (replicas handle reads)
+<LI>As long as one provider is operational, writes can safely be accepted
+<LI>Provider nodes replicate from each other, so they are always up to date and can be ready to take over (hot standby)
+<LI>Syncrepl also allows the provider nodes to re-synchronize after any downtime</UL>
+<H4><A NAME="Arguments against Mirror mode">18.2.3.2. Arguments against Mirror mode</A></H4>
+<UL>
+<LI>Mirror mode is not what is termed as a Multi-Provider solution. This is because writes have to go to just one of the mirror nodes at a time
+<LI>Mirror mode can be termed as Active-Active Hot-Standby, therefore an external server (slapd in proxy mode) or device (hardware load balancer) is needed to manage which provider is currently active
+<LI>Backups are managed slightly differently</UL>
+<P>For configuration, please see the <A HREF="#Mirror mode">Mirror mode</A> section below</P>
+<H3><A NAME="Syncrepl Proxy Mode">18.2.4. Syncrepl Proxy Mode</A></H3>
+<P>While the LDAP Sync protocol supports both pull- and push-based replication, the push mode (refreshAndPersist) must still be initiated from the consumer before the provider can begin pushing changes. In some network configurations, particularly where firewalls restrict the direction in which connections can be made, a provider-initiated push mode may be needed.</P>
+<P>This mode can be configured with the aid of the LDAP Backend (<A HREF="#Backends">Backends</A> and <EM>slapd-ldap(8)</EM>). Instead of running the syncrepl engine on the actual consumer, a slapd-ldap proxy is set up near (or collocated with) the provider that points to the consumer, and the syncrepl engine runs on the proxy.</P>
+<P>For configuration, please see the <A HREF="#Syncrepl Proxy">Syncrepl Proxy</A> section.</P>
+<H2><A NAME="Configuring the different replication types">18.3. Configuring the different replication types</A></H2>
+<H3><A NAME="Syncrepl">18.3.1. Syncrepl</A></H3>
+<H4><A NAME="Syncrepl configuration">18.3.1.1. Syncrepl configuration</A></H4>
+<P>Because syncrepl is a consumer-side replication engine, the syncrepl specification is defined in <EM>slapd.conf</EM>(5) of the consumer server, not in the provider server's configuration file. The initial loading of the consumer content can be performed either by starting the syncrepl engine with no synchronization cookie or by populating the consumer by loading an <TERM>LDIF</TERM> file dumped as a backup at the provider.</P>
+<P>When loading from a backup, it is not required to perform the initial loading from the up-to-date backup of the provider content. The syncrepl engine will automatically synchronize the initial consumer to the current provider content. As a result, it is not required to stop the provider server in order to avoid the replication inconsistency caused by the updates to the provider content during the content backup and loading process.</P>
+<P>When replicating a large scale directory, especially in a bandwidth constrained environment, it is advised to load the consumer from a backup instead of performing a full initial load using syncrepl.</P>
+<H4><A NAME="Set up the provider slapd">18.3.1.2. Set up the provider slapd</A></H4>
+<P>The provider is implemented as an overlay, so the overlay itself must first be configured in <EM>slapd.conf</EM>(5) before it can be used. The provider has two primary configuration directives and two secondary directives for when delta-syncrepl is being used. Because the LDAP Sync search is subject to access control, proper access control privileges should be set up for the replicated content.</P>
+<P>The two primary options to configure are the checkpoint and sessionlog behaviors.</P>
+<P>The <TT>contextCSN</TT> checkpoint is configured by the</P>
+<PRE>
+ syncprov-checkpoint &lt;ops&gt; &lt;minutes&gt;
+</PRE>
+<P>directive. Checkpoints are only tested after successful write operations. If <EM>&lt;ops&gt;</EM> operations or more than <EM>&lt;minutes&gt;</EM> time has passed since the last checkpoint, a new checkpoint is performed. Checkpointing is disabled by default.</P>
+<P>The session log is configured by the</P>
+<PRE>
+ syncprov-sessionlog &lt;ops&gt;
+</PRE>
+<P>directive, where <EM>&lt;ops&gt;</EM> is the maximum number of session log entries the session log can record. All write operations (except Adds) are recorded in the log.</P>
+<P>Note that using the session log requires searching on the <EM>entryUUID</EM> attribute. Setting an eq index on this attribute will greatly benefit the performance of the session log on the provider.</P>
+<P>The reloadhint option is configured by the</P>
+<PRE>
+ syncprov-reloadhint &lt;TRUE|FALSE&gt;
+</PRE>
+<P>directive. It must be set TRUE when using the accesslog overlay for delta-based syncrepl replication support. The default is FALSE.</P>
+<P>The nonpresent option is configured by the</P>
+<PRE>
+ syncprov-nopresent &lt;TRUE|FALSE&gt;
+</PRE>
+<P>directive. This value should only be set TRUE for a syncprov instance on top of a log database (such as one managed by the accesslog overlay). The default is FALSE.</P>
+<P>A more complete example of the <EM>slapd.conf</EM>(5) content is thus:</P>
+<PRE>
+ database mdb
+ maxsize 1073741824
+ suffix dc=Example,dc=com
+ rootdn dc=Example,dc=com
+ directory /var/ldap/db
+ index objectclass,entryCSN,entryUUID eq
+
+ overlay syncprov
+ syncprov-checkpoint 100 10
+ syncprov-sessionlog 100
+</PRE>
+<H4><A NAME="Set up the consumer slapd">18.3.1.3. Set up the consumer slapd</A></H4>
+<P>The syncrepl directive is specified in the database section of <EM>slapd.conf</EM>(5) for the consumer context. The syncrepl engine is backend independent and the directive can be defined with any database type.</P>
+<PRE>
+ database mdb
+ maxsize 1073741824
+ suffix dc=Example,dc=com
+ rootdn dc=Example,dc=com
+ directory /var/ldap/db
+ index objectclass,entryCSN,entryUUID eq
+
+ syncrepl rid=123
+ provider=ldap://provider.example.com:389
+ type=refreshOnly
+ interval=01:00:00:00
+ searchbase=&quot;dc=example,dc=com&quot;
+ filter=&quot;(objectClass=organizationalPerson)&quot;
+ scope=sub
+ attrs=&quot;cn,sn,ou,telephoneNumber,title,l&quot;
+ schemachecking=off
+ bindmethod=simple
+ binddn=&quot;cn=syncuser,dc=example,dc=com&quot;
+ credentials=secret
+</PRE>
+<P>In this example, the consumer will connect to the provider <EM>slapd</EM>(8) at port 389 of <A HREF="ldap://provider.example.com">ldap://provider.example.com</A> to perform a polling (<EM>refreshOnly</EM>) mode of synchronization once a day. It will bind as <TT>cn=syncuser,dc=example,dc=com</TT> using simple authentication with password &quot;secret&quot;. Note that the access control privilege of <TT>cn=syncuser,dc=example,dc=com</TT> should be set appropriately in the provider to retrieve the desired replication content. Also the search limits must be high enough on the provider to allow the syncuser to retrieve a complete copy of the requested content. The consumer uses the rootdn to write to its database so it always has full permissions to write all content.</P>
+<P>The synchronization search in the above example will search for the entries whose objectClass is organizationalPerson in the entire subtree rooted at <TT>dc=example,dc=com</TT>. The requested attributes are <TT>cn</TT>, <TT>sn</TT>, <TT>ou</TT>, <TT>telephoneNumber</TT>, <TT>title</TT>, and <TT>l</TT>. The schema checking is turned off, so that the consumer <EM>slapd</EM>(8) will not enforce entry schema checking when it processes updates from the provider <EM>slapd</EM>(8).</P>
+<P>For more detailed information on the syncrepl directive, see the <A HREF="#syncrepl">syncrepl</A> section of <A HREF="#The slapd Configuration File">The slapd Configuration File</A> chapter of this admin guide.</P>
+<H4><A NAME="Start the provider and the consumer slapd">18.3.1.4. Start the provider and the consumer slapd</A></H4>
+<P>The provider <EM>slapd</EM>(8) is not required to be restarted. <EM>contextCSN</EM> is automatically generated as needed: it might be originally contained in the <TERM>LDIF</TERM> file, generated by <EM>slapadd</EM> (8), generated upon changes in the context, or generated when the first LDAP Sync search arrives at the provider. If an LDIF file is being loaded which did not previously contain the <EM>contextCSN</EM>, the <EM>-w</EM> option should be used with <EM>slapadd</EM> (8) to cause it to be generated. This will allow the server to startup a little quicker the first time it runs.</P>
+<P>When starting a consumer <EM>slapd</EM>(8), it is possible to provide a synchronization cookie as the <EM>-c cookie</EM> command line option in order to start the synchronization from a specific state. The cookie is a comma separated list of name=value pairs. Currently supported syncrepl cookie fields are <EM>csn=&lt;csn&gt;</EM> and <EM>rid=&lt;rid&gt;</EM>. <EM>&lt;csn&gt;</EM> represents the current synchronization state of the consumer. <EM>&lt;rid&gt;</EM> identifies a consumer locally within the consumer server. It is used to relate the cookie to the syncrepl definition in <EM>slapd.conf</EM>(5) which has the matching <EM>&lt;rid&gt;</EM>. The <EM>&lt;rid&gt;</EM> must have no more than 3 decimal digits. The command line cookie overrides the synchronization cookie stored in the consumer database.</P>
+<H3><A NAME="Delta-syncrepl">18.3.2. Delta-syncrepl</A></H3>
+<H4><A NAME="Delta-syncrepl Provider configuration">18.3.2.1. Delta-syncrepl Provider configuration</A></H4>
+<P>Setting up delta-syncrepl requires configuration changes on both the provider and replica servers:</P>
+<PRE>
+ # Give the replicator DN unlimited read access. This ACL needs to be
+ # merged with other ACL statements, and/or moved within the scope
+ # of a database. The &quot;by * break&quot; portion causes evaluation of
+ # subsequent rules. See slapd.access(5) for details.
+ access to *
+ by dn.base=&quot;cn=replicator,dc=example,dc=com&quot; read
+ by * break
+
+ # Set the module path location
+ modulepath /opt/symas/lib/openldap
+
+ # Load the mdb backend
+ moduleload back_mdb.la
+
+ # Load the accesslog overlay
+ moduleload accesslog.la
+
+ #Load the syncprov overlay
+ moduleload syncprov.la
+
+ # Accesslog database definitions
+ database mdb
+ suffix cn=accesslog
+ directory /db/accesslog
+ rootdn cn=accesslog
+ index default eq
+ index entryCSN,objectClass,reqEnd,reqResult,reqStart,reqDN
+
+ overlay syncprov
+ syncprov-nopresent TRUE
+ syncprov-reloadhint TRUE
+
+ # Let the replicator DN have limitless searches
+ limits dn.exact=&quot;cn=replicator,dc=example,dc=com&quot; time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited
+
+ # Primary database definitions
+ database mdb
+ suffix &quot;dc=symas,dc=com&quot;
+ rootdn &quot;cn=manager,dc=symas,dc=com&quot;
+
+ ## Whatever other configuration options are desired
+
+ # syncprov specific indexing
+ index entryCSN eq
+ index entryUUID eq
+
+ # syncrepl Provider for primary db
+ overlay syncprov
+ syncprov-checkpoint 1000 60
+
+ # accesslog overlay definitions for primary db
+ overlay accesslog
+ logdb cn=accesslog
+ logops writes
+ logsuccess TRUE
+ # scan the accesslog DB every day, and purge entries older than 7 days
+ logpurge 07+00:00 01+00:00
+
+ # Let the replicator DN have limitless searches
+ limits dn.exact=&quot;cn=replicator,dc=example,dc=com&quot; time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited
+</PRE>
+<P>For more information, always consult the relevant man pages (<EM>slapo-accesslog</EM>(5) and <EM>slapd.conf</EM>(5))</P>
+<H4><A NAME="Delta-syncrepl Consumer configuration">18.3.2.2. Delta-syncrepl Consumer configuration</A></H4>
+<PRE>
+ # Replica database configuration
+ database mdb
+ suffix &quot;dc=symas,dc=com&quot;
+ rootdn &quot;cn=manager,dc=symas,dc=com&quot;
+
+ ## Whatever other configuration bits for the replica, like indexing
+ ## that you want
+
+ # syncrepl specific indices
+ index entryUUID eq
+
+ # syncrepl directives
+ syncrepl rid=0
+ provider=ldap://ldapprovider.example.com:389
+ bindmethod=simple
+ binddn=&quot;cn=replicator,dc=example,dc=com&quot;
+ credentials=secret
+ searchbase=&quot;dc=example,dc=com&quot;
+ logbase=&quot;cn=accesslog&quot;
+ logfilter=&quot;(&amp;(objectClass=auditWriteObject)(reqResult=0))&quot;
+ schemachecking=on
+ type=refreshAndPersist
+ retry=&quot;60 +&quot;
+ syncdata=accesslog
+
+ # Refer updates to the provider
+ updateref ldap://ldapprovider.example.com
+</PRE>
+<P>The above configuration assumes that you have a replicator identity defined in your database that can be used to bind to the provider.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>An accesslog database is unique to a given provider. It should never be replicated.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H3><A NAME="N-Way Multi-Provider">18.3.3. N-Way Multi-Provider</A></H3>
+<P>For the following example we will be using 3 Provider nodes. Keeping in line with <B>test050-syncrepl-multiprovider</B> of the OpenLDAP test suite, we will be configuring <EM>slapd(8)</EM> via <B>cn=config</B></P>
+<P>This sets up the config database:</P>
+<PRE>
+ dn: cn=config
+ objectClass: olcGlobal
+ cn: config
+ olcServerID: 1
+
+ dn: olcDatabase={0}config,cn=config
+ objectClass: olcDatabaseConfig
+ olcDatabase: {0}config
+ olcRootPW: secret
+</PRE>
+<P>Each server must have a unique server ID (<TT>SID</TT>), so second and third servers will have a different <TT>olcServerID</TT> obviously:</P>
+<PRE>
+ dn: cn=config
+ objectClass: olcGlobal
+ cn: config
+ olcServerID: 2
+
+ dn: olcDatabase={0}config,cn=config
+ objectClass: olcDatabaseConfig
+ olcDatabase: {0}config
+ olcRootPW: secret
+</PRE>
+<P>This sets up syncrepl as a provider (since these are all providers):</P>
+<PRE>
+ dn: cn=module,cn=config
+ objectClass: olcModuleList
+ cn: module
+ olcModulePath: /usr/local/libexec/openldap
+ olcModuleLoad: syncprov.la
+</PRE>
+<P>Now we setup the first Provider Node (replace $URI1, $URI2 and $URI3 etc. with your actual ldap urls):</P>
+<PRE>
+ dn: cn=config
+ changetype: modify
+ replace: olcServerID
+ olcServerID: 1 $URI1
+ olcServerID: 2 $URI2
+ olcServerID: 3 $URI3
+
+ dn: olcOverlay=syncprov,olcDatabase={0}config,cn=config
+ changetype: add
+ objectClass: olcOverlayConfig
+ objectClass: olcSyncProvConfig
+ olcOverlay: syncprov
+
+ dn: olcDatabase={0}config,cn=config
+ changetype: modify
+ add: olcSyncRepl
+ olcSyncRepl: rid=001 provider=$URI1 binddn=&quot;cn=config&quot; bindmethod=simple
+ credentials=secret searchbase=&quot;cn=config&quot; type=refreshAndPersist
+ retry=&quot;5 5 300 5&quot; timeout=1
+ olcSyncRepl: rid=002 provider=$URI2 binddn=&quot;cn=config&quot; bindmethod=simple
+ credentials=secret searchbase=&quot;cn=config&quot; type=refreshAndPersist
+ retry=&quot;5 5 300 5&quot; timeout=1
+ olcSyncRepl: rid=003 provider=$URI3 binddn=&quot;cn=config&quot; bindmethod=simple
+ credentials=secret searchbase=&quot;cn=config&quot; type=refreshAndPersist
+ retry=&quot;5 5 300 5&quot; timeout=1
+ -
+ add: olcMultiProvider
+ olcMultiProvider: TRUE
+</PRE>
+<P>Now start up the provider and a consumer/s, also add the above LDIF to the first consumer, second consumer etc. It will then replicate <B>cn=config</B>. You now have N-Way Multi-Provider on the config database.</P>
+<P>We still have to replicate the actual data, not just the config, so add to the provider (all active and configured consumers/providers will pull down this config, as they are all syncing). Also, replace all <EM>${</EM>} variables with whatever is applicable to your setup:</P>
+<PRE>
+ dn: olcDatabase={1}$BACKEND,cn=config
+ objectClass: olcDatabaseConfig
+ objectClass: olc${BACKEND}Config
+ olcDatabase: {1}$BACKEND
+ olcSuffix: $BASEDN
+ olcDbDirectory: ./db
+ olcRootDN: $MANAGERDN
+ olcRootPW: $PASSWD
+ olcLimits: dn.exact=&quot;$MANAGERDN&quot; time.soft=unlimited time.hard=unlimited
+ size.soft=unlimited size.hard=unlimited
+ olcSyncRepl: rid=004 provider=$URI1 binddn=&quot;$MANAGERDN&quot; bindmethod=simple
+ credentials=$PASSWD searchbase=&quot;$BASEDN&quot; type=refreshOnly
+ interval=00:00:00:10 retry=&quot;5 5 300 5&quot; timeout=1
+ olcSyncRepl: rid=005 provider=$URI2 binddn=&quot;$MANAGERDN&quot; bindmethod=simple
+ credentials=$PASSWD searchbase=&quot;$BASEDN&quot; type=refreshOnly
+ interval=00:00:00:10 retry=&quot;5 5 300 5&quot; timeout=1
+ olcSyncRepl: rid=006 provider=$URI3 binddn=&quot;$MANAGERDN&quot; bindmethod=simple
+ credentials=$PASSWD searchbase=&quot;$BASEDN&quot; type=refreshOnly
+ interval=00:00:00:10 retry=&quot;5 5 300 5&quot; timeout=1
+ olcMultiProvider: TRUE
+
+ dn: olcOverlay=syncprov,olcDatabase={1}${BACKEND},cn=config
+ changetype: add
+ objectClass: olcOverlayConfig
+ objectClass: olcSyncProvConfig
+ olcOverlay: syncprov
+</PRE>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>All of your servers' clocks must be tightly synchronized using e.g. NTP <A HREF="http://www.ntp.org/">http://www.ntp.org/</A>, atomic clock, or some other reliable time reference.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>As stated in <EM>slapd-config</EM>(5), URLs specified in <EM>olcSyncRepl</EM> directives are the URLs of the servers from which to replicate. These must exactly match the URLs <EM>slapd</EM> listens on (<EM>-h</EM> in <A HREF="#Command-Line Options">Command-Line Options</A>). Otherwise slapd may attempt to replicate from itself, causing a loop.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>The <EM>entryCSN</EM> and <EM>contextCSN</EM> attributes are used to track changes to an entry and naming context, respectively. The <TT>SID</TT> which must be unique for each replication provider is a component of these CSNs. If you're using <EM>slapadd</EM> to load a database and there are no entryCSNs already present in the input LDIF, <EM>slapadd</EM> will generate them with a <EM>SID</EM> of <TT>000</TT>. This is not a valid <EM>SID</EM> for multi-provider replication, and you should use the <TT>-S</TT> option of <EM>slapadd</EM> (8) to specify a valid <EM>SID</EM> for these generated CSNs. If there are existing entryCSNs in the input LDIF, <EM>slapadd</EM> will not change them.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H3><A NAME="Mirror mode">18.3.4. Mirror mode</A></H3>
+<P>Mirror mode configuration is actually very easy. If you have ever setup a normal slapd syncrepl provider, then the only change is the following two directives:</P>
+<PRE>
+ multiprovider on
+ serverID 1
+</PRE>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>You need to make sure that the <EM>serverID</EM> of each provider node is different and add it as a global configuration option.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H4><A NAME="Mirror Node Configuration">18.3.4.1. Mirror Node Configuration</A></H4>
+<P>The first step is to configure the syncrepl provider the same as in the <A HREF="#Set up the provider slapd">Set up the provider slapd</A> section.</P>
+<P>Here's a specific cut down example using <A HREF="#LDAP Sync Replication">LDAP Sync Replication</A> in <EM>refreshAndPersist</EM> mode:</P>
+<P>Mirror mode node 1:</P>
+<PRE>
+ # Global section
+ serverID 1
+ # database section
+
+ # syncrepl directive
+ syncrepl rid=001
+ provider=ldap://ldap-sid2.example.com
+ bindmethod=simple
+ binddn=&quot;cn=mirrormode,dc=example,dc=com&quot;
+ credentials=mirrormode
+ searchbase=&quot;dc=example,dc=com&quot;
+ schemachecking=on
+ type=refreshAndPersist
+ retry=&quot;60 +&quot;
+
+ multiprovider on
+</PRE>
+<P>Mirror mode node 2:</P>
+<PRE>
+ # Global section
+ serverID 2
+ # database section
+
+ # syncrepl directive
+ syncrepl rid=001
+ provider=ldap://ldap-sid1.example.com
+ bindmethod=simple
+ binddn=&quot;cn=mirrormode,dc=example,dc=com&quot;
+ credentials=mirrormode
+ searchbase=&quot;dc=example,dc=com&quot;
+ schemachecking=on
+ type=refreshAndPersist
+ retry=&quot;60 +&quot;
+
+ multiprovider on
+</PRE>
+<P>It's simple really; each Mirror mode node is setup <B>exactly</B> the same, except that the <EM>serverID</EM> is unique, and each consumer is pointed to the other server.</P>
+<H5><A NAME="Failover Configuration">18.3.4.1.1. Failover Configuration</A></H5>
+<P>There are generally 2 choices for this; 1. Hardware proxies/load-balancing or dedicated proxy software, 2. using a Back-LDAP proxy as a syncrepl provider</P>
+<P>A typical enterprise example might be:</P>
+<P><CENTER><IMG SRC="dual_dc.png" ALIGN="center"></CENTER></P>
+<P ALIGN="Center">Figure X.Y: Mirror mode in a Dual Data Center Configuration</P>
+<H5><A NAME="Normal Consumer Configuration">18.3.4.1.2. Normal Consumer Configuration</A></H5>
+<P>This is exactly the same as the <A HREF="#Set up the consumer slapd">Set up the consumer slapd</A> section. It can either setup in normal <A HREF="#syncrepl replication">syncrepl replication</A> mode, or in <A HREF="#delta-syncrepl replication">delta-syncrepl replication</A> mode.</P>
+<H4><A NAME="Mirror mode Summary">18.3.4.2. Mirror mode Summary</A></H4>
+<P>You will now have a directory architecture that provides all of the consistency guarantees of single-provider replication, while also providing the high availability of multi-provider replication.</P>
+<H3><A NAME="Syncrepl Proxy">18.3.5. Syncrepl Proxy</A></H3>
+<P><CENTER><IMG SRC="push-based-complete.png" ALIGN="center"></CENTER></P>
+<P ALIGN="Center">Figure X.Y: Replacing slurpd</P>
+<P>The following example is for a self-contained push-based replication solution:</P>
+<PRE>
+ #######################################################################
+ # Standard OpenLDAP Provider
+ #######################################################################
+
+ include /usr/local/etc/openldap/schema/core.schema
+ include /usr/local/etc/openldap/schema/cosine.schema
+ include /usr/local/etc/openldap/schema/nis.schema
+ include /usr/local/etc/openldap/schema/inetorgperson.schema
+
+ include /usr/local/etc/openldap/slapd.acl
+
+ modulepath /usr/local/libexec/openldap
+ moduleload back_mdb.la
+ moduleload syncprov.la
+ moduleload back_ldap.la
+
+ pidfile /usr/local/var/slapd.pid
+ argsfile /usr/local/var/slapd.args
+
+ loglevel sync stats
+
+ database mdb
+ suffix &quot;dc=suretecsystems,dc=com&quot;
+ directory /usr/local/var/openldap-data
+
+ checkpoint 1024 5
+
+ index objectClass eq
+ # rest of indexes
+ index default sub
+
+ rootdn &quot;cn=admin,dc=suretecsystems,dc=com&quot;
+ rootpw testing
+
+ # syncprov specific indexing
+ index entryCSN eq
+ index entryUUID eq
+
+ # syncrepl Provider for primary db
+ overlay syncprov
+ syncprov-checkpoint 1000 60
+
+ # Let the replicator DN have limitless searches
+ limits dn.exact=&quot;cn=replicator,dc=suretecsystems,dc=com&quot; time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited
+
+ database monitor
+
+ database config
+ rootpw testing
+
+ ##############################################################################
+ # Consumer Proxy that pulls in data via Syncrepl and pushes out via slapd-ldap
+ ##############################################################################
+
+ database ldap
+ # ignore conflicts with other databases, as we need to push out to same suffix
+ hidden on
+ suffix &quot;dc=suretecsystems,dc=com&quot;
+ rootdn &quot;cn=slapd-ldap&quot;
+ uri ldap://localhost:9012/
+
+ lastmod on
+
+ # We don't need any access to this DSA
+ restrict all
+
+ acl-bind bindmethod=simple
+ binddn=&quot;cn=replicator,dc=suretecsystems,dc=com&quot;
+ credentials=testing
+
+ syncrepl rid=001
+ provider=ldap://localhost:9011/
+ binddn=&quot;cn=replicator,dc=suretecsystems,dc=com&quot;
+ bindmethod=simple
+ credentials=testing
+ searchbase=&quot;dc=suretecsystems,dc=com&quot;
+ type=refreshAndPersist
+ retry=&quot;5 5 300 5&quot;
+
+ overlay syncprov
+</PRE>
+<P>A replica configuration for this type of setup could be:</P>
+<PRE>
+ #######################################################################
+ # Standard OpenLDAP Replica without Syncrepl
+ #######################################################################
+
+ include /usr/local/etc/openldap/schema/core.schema
+ include /usr/local/etc/openldap/schema/cosine.schema
+ include /usr/local/etc/openldap/schema/nis.schema
+ include /usr/local/etc/openldap/schema/inetorgperson.schema
+
+ include /usr/local/etc/openldap/slapd.acl
+
+ modulepath /usr/local/libexec/openldap
+ moduleload back_mdb.la
+ moduleload syncprov.la
+ moduleload back_ldap.la
+
+ pidfile /usr/local/var/slapd.pid
+ argsfile /usr/local/var/slapd.args
+
+ loglevel sync stats
+
+ database mdb
+ suffix &quot;dc=suretecsystems,dc=com&quot;
+ directory /usr/local/var/openldap-consumer/data
+
+ maxsize 85899345920
+ checkpoint 1024 5
+
+ index objectClass eq
+ # rest of indexes
+ index default sub
+
+ rootdn &quot;cn=admin,dc=suretecsystems,dc=com&quot;
+ rootpw testing
+
+ # Let the replicator DN have limitless searches
+ limits dn.exact=&quot;cn=replicator,dc=suretecsystems,dc=com&quot; time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited
+
+ updatedn &quot;cn=replicator,dc=suretecsystems,dc=com&quot;
+
+ # Refer updates to the provider
+ updateref ldap://localhost:9011
+
+ database monitor
+
+ database config
+ rootpw testing
+</PRE>
+<P>You can see we use the <EM>updatedn</EM> directive here and example ACLs (<TT>usr/local/etc/openldap/slapd.acl</TT>) for this could be:</P>
+<PRE>
+ # Give the replicator DN unlimited read access. This ACL may need to be
+ # merged with other ACL statements.
+
+ access to *
+ by dn.base=&quot;cn=replicator,dc=suretecsystems,dc=com&quot; write
+ by * break
+
+ access to dn.base=&quot;&quot;
+ by * read
+
+ access to dn.base=&quot;cn=Subschema&quot;
+ by * read
+
+ access to dn.subtree=&quot;cn=Monitor&quot;
+ by dn.exact=&quot;uid=admin,dc=suretecsystems,dc=com&quot; write
+ by users read
+ by * none
+
+ access to *
+ by self write
+ by * read
+</PRE>
+<P>In order to support more replicas, just add more <EM>database ldap</EM> sections and increment the <EM>syncrepl rid</EM> number accordingly.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>You must populate the Provider and Replica directories with the same data, unlike when using normal Syncrepl
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>If you do not have access to modify the provider directory configuration you can configure a standalone ldap proxy, which might look like:</P>
+<P><CENTER><IMG SRC="push-based-standalone.png" ALIGN="center"></CENTER></P>
+<P ALIGN="Center">Figure X.Y: Replacing slurpd with a standalone version</P>
+<P>The following configuration is an example of a standalone LDAP Proxy:</P>
+<PRE>
+ include /usr/local/etc/openldap/schema/core.schema
+ include /usr/local/etc/openldap/schema/cosine.schema
+ include /usr/local/etc/openldap/schema/nis.schema
+ include /usr/local/etc/openldap/schema/inetorgperson.schema
+
+ include /usr/local/etc/openldap/slapd.acl
+
+ modulepath /usr/local/libexec/openldap
+ moduleload syncprov.la
+ moduleload back_ldap.la
+
+ ##############################################################################
+ # Consumer Proxy that pulls in data via Syncrepl and pushes out via slapd-ldap
+ ##############################################################################
+
+ database ldap
+ # ignore conflicts with other databases, as we need to push out to same suffix
+ hidden on
+ suffix &quot;dc=suretecsystems,dc=com&quot;
+ rootdn &quot;cn=slapd-ldap&quot;
+ uri ldap://localhost:9012/
+
+ lastmod on
+
+ # We don't need any access to this DSA
+ restrict all
+
+ acl-bind bindmethod=simple
+ binddn=&quot;cn=replicator,dc=suretecsystems,dc=com&quot;
+ credentials=testing
+
+ syncrepl rid=001
+ provider=ldap://localhost:9011/
+ binddn=&quot;cn=replicator,dc=suretecsystems,dc=com&quot;
+ bindmethod=simple
+ credentials=testing
+ searchbase=&quot;dc=suretecsystems,dc=com&quot;
+ type=refreshAndPersist
+ retry=&quot;5 5 300 5&quot;
+
+ overlay syncprov
+</PRE>
+<P>As you can see, you can let your imagination go wild using Syncrepl and <EM>slapd-ldap(8)</EM> tailoring your replication to fit your specific network topology.</P>
+<P></P>
+<HR>
+<H1><A NAME="Maintenance">19. Maintenance</A></H1>
+<P>System Administration is all about maintenance, so it is only fair that we discuss how to correctly maintain an OpenLDAP deployment.</P>
+<H2><A NAME="Directory Backups">19.1. Directory Backups</A></H2>
+<P>Backup strategies largely depend on the amount of change in the database and how much of that change an administrator might be willing to lose in a catastrophic failure. There are two basic methods that can be used:</P>
+<P>1. Backup the LMDB database itself</P>
+<P>The LMDB database can be copied live using the mdb_copy command. If the database is a sparse file via the use of the &quot;writemap&quot; environment flag, the resulting copy will be the actual size of the database rather than a sparse copy.</P>
+<P>2. Periodically run slapcat and back up the LDIF file:</P>
+<P>Slapcat can be run while slapd is active. However, one runs the risk of an inconsistent database- not from the point of slapd, but from the point of the applications using LDAP. For example, if a provisioning application performed tasks that consisted of several LDAP operations, and the slapcat took place concurrently with those operations, then there might be inconsistencies in the LDAP database from the point of view of that provisioning application and applications that depended on it. One must, therefore, be convinced something like that won't happen. One way to do that would be to put the database in read-only mode while performing the slapcat. The other disadvantage of this approach is that the generated LDIF files can be rather large and the accumulation of the day's backups could add up to a substantial amount of space.</P>
+<P>You can use <EM>slapcat</EM>(8) to generate an LDIF file for each of your <EM>slapd</EM>(8) back-mdb databases.</P>
+<PRE>
+ slapcat -f slapd.conf -b &quot;dc=example,dc=com&quot;
+</PRE>
+<P>For back-mdb this command may be ran while slapd(8) is running.</P>
+<H2><A NAME="Checkpointing">19.2. Checkpointing</A></H2>
+<P>Setting a checkpoint is only necessary when back-mdb has the dbnosync flag set. Otherwise it has no effect. With back-mdb the kbyte option is not implemented, meaning it will only run a checkpoint based on the elapsed amount of minutes flag.</P>
+<H2><A NAME="Migration">19.3. Migration</A></H2>
+<P>The simplest steps needed to migrate between versions or upgrade, depending on your deployment type are:</P>
+<UL>
+&nbsp;</UL><OL>
+<LI><B>Stop the current server when convenient</B>
+<BR>
+&nbsp;
+<LI><B>slapcat the current data out</B>
+<BR>
+&nbsp;
+<LI><B>Clear out the current data directory (/usr/local/var/openldap-data/)</B>
+<BR>
+&nbsp;
+<LI><B>Perform the software upgrades</B>
+<BR>
+&nbsp;
+<LI><B>slapadd the exported data back into the directory</B>
+<BR>
+&nbsp;
+<LI><B>Start the server</B></OL>
+<P>Obviously this doesn't cater for any complicated deployments with <A HREF="#N-Way Multi-Provider">N-Way Multi-Provider</A>, but following the above sections and using either commercial support or community support should help. Also check the <A HREF="#Troubleshooting">Troubleshooting</A> section.</P>
+<P></P>
+<HR>
+<H1><A NAME="Monitoring">20. Monitoring</A></H1>
+<P><EM>slapd</EM>(8) supports an optional <TERM>LDAP</TERM> monitoring interface you can use to obtain information regarding the current state of your <EM>slapd</EM> instance. For instance, the interface allows you to determine how many clients are connected to the server currently. The monitoring information is provided by a specialized backend, the <EM>monitor</EM> backend. A manual page, <EM>slapd-monitor</EM>(5) is available.</P>
+<P>When the monitoring interface is enabled, LDAP clients may be used to access information provided by the <EM>monitor</EM> backend, subject to access and other controls.</P>
+<P>When enabled, the <EM>monitor</EM> backend dynamically generates and returns objects in response to search requests in the <EM>cn=Monitor</EM> subtree. Each object contains information about a particular aspect of the server. The information is held in a combination of user applications and operational attributes. This information can be accessed with <EM>ldapsearch(1)</EM>, with any general-purpose LDAP browser, or with specialized monitoring tools. The <A HREF="#Accessing Monitoring Information">Accessing Monitoring Information</A> section provides a brief tutorial on how to use <EM>ldapsearch</EM>(1) to access monitoring information, while the <A HREF="#Monitor information">Monitor information</A> section details monitoring information base and its organization.</P>
+<P>While support for the monitor backend is included in default builds of slapd(8), this support requires some configuration to become active. This may be done using either <TT>cn=config</TT> or <EM>slapd.conf</EM>(5). The former is discussed in the <A HREF="#Monitor configuration via cn=config">Monitor configuration via cn=config</A> section of this of this chapter. The latter is discussed in the <A HREF="#Monitor configuration via slapd.conf(5)">Monitor configuration via slapd.conf(5)</A> section of this chapter. These sections assume monitor backend is built into <EM>slapd</EM> (e.g., <TT>--enable-monitor=yes</TT>, the default). If the monitor backend was built as a module (e.g., <TT>--enable-monitor=mod</TT>, this module must loaded. Loading of modules is discussed in the <A HREF="#Configuring slapd">Configuring slapd</A> and <A HREF="#The slapd Configuration File">The slapd Configuration File</A> chapters.</P>
+<H2><A NAME="Monitor configuration via cn=config(5)">20.1. Monitor configuration via cn=config(5)</A></H2>
+<P>The <EM>monitor backend</EM> is statically built into slapd and can be instantiated via ldapadd.</P>
+<PRE>
+ dn: olcDatabase=monitor,cn=config
+ objectClass: olcDatabaseConfig
+ olcDatabase: monitor
+</PRE>
+<H2><A NAME="Monitor configuration via slapd.conf(5)">20.2. Monitor configuration via slapd.conf(5)</A></H2>
+<P>Configuration of the slapd.conf(5) to support LDAP monitoring is quite simple.</P>
+<P>First, ensure <EM>core.schema</EM> schema configuration file is included by your <EM>slapd.conf</EM>(5) file. The <EM>monitor</EM> backend requires it.</P>
+<P>Second, instantiate the <EM>monitor backend</EM> by adding a <EM>database monitor</EM> directive below your existing database sections. For instance:</P>
+<PRE>
+ database monitor
+</PRE>
+<P>Lastly, add additional global or database directives as needed.</P>
+<P>Like most other database backends, the monitor backend does honor slapd(8) access and other administrative controls. As some monitor information may be sensitive, it is generally recommend access to cn=monitor be restricted to directory administrators and their monitoring agents. Adding an <EM>access</EM> directive immediately below the <EM>database monitor</EM> directive is a clear and effective approach for controlling access. For instance, the addition of the following <EM>access</EM> directive immediately below the <EM>database monitor</EM> directive restricts access to monitoring information to the specified directory manager.</P>
+<PRE>
+ access to *
+ by dn.exact=&quot;cn=Manager,dc=example,dc=com
+ by * none
+</PRE>
+<P>More information on <EM>slapd</EM>(8) access controls, see <EM>The access Control Directive</EM> section of the <A HREF="#The slapd Configuration File">The slapd Configuration File</A> chapter and <EM>slapd.access</EM>(5).</P>
+<P>After restarting <EM>slapd</EM>(8), you are ready to start exploring the monitoring information provided in <TT>cn=config</TT> as discussed in the <A HREF="#Accessing Monitoring Information">Accessing Monitoring Information</A> section of this chapter.</P>
+<P>One can verify slapd(8) is properly configured to provide monitoring information by attempting to read the <TT>cn=monitor</TT> object. For instance, if the following <EM>ldapsearch</EM>(1) command returns the cn=monitor object (with, as requested, no attributes), it's working.</P>
+<PRE>
+ ldapsearch -x -D 'cn=Manager,dc=example,dc=com' -W \
+ -b 'cn=Monitor' -s base 1.1
+</PRE>
+<P>Note that unlike general purpose database backends, the database suffix is hardcoded. It's always <TT>cn=Monitor</TT>. So no <EM>suffix</EM> directive should be provided. Also note that general purpose database backends, the monitor backend cannot be instantiated multiple times. That is, there can only be one (or zero) occurrences of <TT>database monitor</TT> in the server's configuration.</P>
+<H2><A NAME="Accessing Monitoring Information">20.3. Accessing Monitoring Information</A></H2>
+<P>As previously discussed, when enabled, the <EM>monitor</EM> backend dynamically generates and returns objects in response to search requests in the <EM>cn=Monitor</EM> subtree. Each object contains information about a particular aspect of the server. The information is held in a combination of user applications and operational attributes. This information can be accessed with <EM>ldapsearch(1)</EM>, with any general-purpose LDAP browser, or with specialized monitoring tools.</P>
+<P>This section provides a provides a brief tutorial on how to use <EM>ldapsearch</EM>(1) to access monitoring information.</P>
+<P>To inspect any particular monitor object, one performs search operation on the object with a baseObject scope and a <TT>(objectClass=*)</TT> filter. As the monitoring information is contained in a combination of user applications and operational attributes, the return all user applications attributes (e.g., <TT>'*'</TT>) and all operational attributes (e.g., <TT>'+'</TT>) should be requested. For instance, to read the <TT>cn=Monitor</TT> object itself, the <EM>ldapsearch</EM>(1) command (modified to fit your configuration) can be used:</P>
+<PRE>
+ ldapsearch -x -D 'cn=Manager,dc=example,dc=com' -W \
+ -b 'cn=Monitor' -s base '(objectClass=*)' '*' '+'
+</PRE>
+<P>When run against your server, this should produce output similar to:</P>
+<PRE>
+ dn: cn=Monitor
+ objectClass: monitorServer
+ structuralObjectClass: monitorServer
+ cn: Monitor
+ creatorsName:
+ modifiersName:
+ createTimestamp: 20061208223558Z
+ modifyTimestamp: 20061208223558Z
+ description: This subtree contains monitoring/managing objects.
+ description: This object contains information about this server.
+ description: Most of the information is held in operational attributes, which
+ must be explicitly requested.
+ monitoredInfo: OpenLDAP: slapd 2.5 (Dec 7 2006 17:30:29)
+ entryDN: cn=Monitor
+ subschemaSubentry: cn=Subschema
+ hasSubordinates: TRUE
+</PRE>
+<P>To reduce the number of uninteresting attributes returned, one can be more selective when requesting which attributes are to be returned. For instance, one could request the return of all attributes allowed by the <EM>monitorServer</EM> object class (e.g., <TT>@objectClass</TT>) instead of all user and all operational attributes:</P>
+<PRE>
+ ldapsearch -x -D 'cn=Manager,dc=example,dc=com' -W \
+ -b 'cn=Monitor' -s base '(objectClass=*)' '@monitorServer'
+</PRE>
+<P>This limits the output as follows:</P>
+<PRE>
+ dn: cn=Monitor
+ objectClass: monitorServer
+ cn: Monitor
+ description: This subtree contains monitoring/managing objects.
+ description: This object contains information about this server.
+ description: Most of the information is held in operational attributes, which
+ must be explicitly requested.
+ monitoredInfo: OpenLDAP: slapd 2.X (Dec 7 2006 17:30:29)
+</PRE>
+<P>To return the names of all the monitoring objects, one performs a search of <TT>cn=Monitor</TT> with subtree scope and <TT>(objectClass=*)</TT> filter and requesting no attributes (e.g., <TT>1.1</TT>) be returned.</P>
+<PRE>
+ ldapsearch -x -D 'cn=Manager,dc=example,dc=com' -W -b 'cn=Monitor' -s sub 1.1
+</PRE>
+<P>If you run this command you will discover that there are many objects in the <EM>cn=Monitor</EM> subtree. The following section describes some of the commonly available monitoring objects.</P>
+<H2><A NAME="Monitor Information">20.4. Monitor Information</A></H2>
+<P>The <EM>monitor</EM> backend provides a wealth of information useful for monitoring the slapd(8) contained in set of monitor objects. Each object contains information about a particular aspect of the server, such as a backends, a connection, or a thread. Some objects serve as containers for other objects and used to construct a hierarchy of objects.</P>
+<P>In this hierarchy, the most superior object is {cn=Monitor}. While this object primarily serves as a container for other objects, most of which are containers, this object provides information about this server. In particular, it provides the slapd(8) version string. Example:</P>
+<PRE>
+ dn: cn=Monitor
+ monitoredInfo: OpenLDAP: slapd 2.X (Dec 7 2006 17:30:29)
+</PRE>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>Examples in this section (and its subsections) have been trimmed to show only key information.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H3><A NAME="Backends">20.4.1. Backends</A></H3>
+<P>The <TT>cn=Backends,cn=Monitor</TT> object provides a list of available backends. The list of available backends includes all builtin backends, as well as those backends loaded by modules. For example:</P>
+<PRE>
+ dn: cn=Backends,cn=Monitor
+ monitoredInfo: config
+ monitoredInfo: ldif
+ monitoredInfo: monitor
+ monitoredInfo: mdb
+</PRE>
+<P>This indicates the <EM>config</EM>, <EM>ldif</EM>, <EM>monitor</EM>, and <EM>mdb</EM> backends are available.</P>
+<P>The <TT>cn=Backends,cn=Monitor</TT> object is also a container for available backend objects. Each available backend object contains information about a particular backend. For example:</P>
+<PRE>
+ dn: cn=Backend 0,cn=Backends,cn=Monitor
+ monitoredInfo: config
+ monitorRuntimeConfig: TRUE
+ supportedControl: 2.16.840.1.113730.3.4.2
+ seeAlso: cn=Database 0,cn=Databases,cn=Monitor
+
+ dn: cn=Backend 1,cn=Backends,cn=Monitor
+ monitoredInfo: ldif
+ monitorRuntimeConfig: TRUE
+ supportedControl: 2.16.840.1.113730.3.4.2
+
+ dn: cn=Backend 2,cn=Backends,cn=Monitor
+ monitoredInfo: monitor
+ monitorRuntimeConfig: TRUE
+ supportedControl: 2.16.840.1.113730.3.4.2
+ seeAlso: cn=Database 2,cn=Databases,cn=Monitor
+
+ dn: cn=Backend 3,cn=Backends,cn=Monitor
+ monitoredInfo: mdb
+ monitorRuntimeConfig: TRUE
+ supportedControl: 1.3.6.1.1.12
+ supportedControl: 2.16.840.1.113730.3.4.2
+ supportedControl: 1.3.6.1.4.1.4203.666.5.2
+ supportedControl: 1.2.840.113556.1.4.319
+ supportedControl: 1.3.6.1.1.13.1
+ supportedControl: 1.3.6.1.1.13.2
+ supportedControl: 1.3.6.1.4.1.4203.1.10.1
+ supportedControl: 1.2.840.113556.1.4.1413
+ supportedControl: 1.3.6.1.4.1.4203.666.11.7.2
+</PRE>
+<P>For each of these objects, monitorInfo indicates which backend the information in the object is about. For instance, the <TT>cn=Backend 5,cn=Backends,cn=Monitor</TT> object contains (in the example) information about the <EM>mdb</EM> backend.</P>
+<TABLE CLASS="columns" BORDER>
+<TR CLASS="heading">
+<TD>
+<STRONG>Attribute</STRONG>
+</TD>
+<TD>
+<STRONG>Description</STRONG>
+</TD>
+</TR>
+<TR>
+<TD>
+monitoredInfo
+</TD>
+<TD>
+Name of backend
+</TD>
+</TR>
+<TR>
+<TD>
+supportedControl
+</TD>
+<TD>
+supported LDAP control extensions
+</TD>
+</TR>
+<TR>
+<TD>
+seeAlso
+</TD>
+<TD>
+Database objects of instances of this backend
+</TD>
+</TR>
+</TABLE>
+
+<H3><A NAME="Connections">20.4.2. Connections</A></H3>
+<P>The main entry is empty; it should contain some statistics on the number of connections.</P>
+<P>Dynamic child entries are created for each open connection, with stats on the activity on that connection (the format will be detailed later). There are two special child entries that show the number of total and current connections respectively.</P>
+<P>For example:</P>
+<P>Total Connections:</P>
+<PRE>
+ dn: cn=Total,cn=Connections,cn=Monitor
+ structuralObjectClass: monitorCounterObject
+ monitorCounter: 4
+ entryDN: cn=Total,cn=Connections,cn=Monitor
+ subschemaSubentry: cn=Subschema
+ hasSubordinates: FALSE
+</PRE>
+<P>Current Connections:</P>
+<PRE>
+ dn: cn=Current,cn=Connections,cn=Monitor
+ structuralObjectClass: monitorCounterObject
+ monitorCounter: 2
+ entryDN: cn=Current,cn=Connections,cn=Monitor
+ subschemaSubentry: cn=Subschema
+ hasSubordinates: FALSE
+</PRE>
+<H3><A NAME="Databases">20.4.3. Databases</A></H3>
+<P>The main entry contains the naming context of each configured database; the child entries contain, for each database, the type and the naming context.</P>
+<P>For example:</P>
+<PRE>
+ dn: cn=Database 2,cn=Databases,cn=Monitor
+ structuralObjectClass: monitoredObject
+ monitoredInfo: monitor
+ monitorIsShadow: FALSE
+ monitorContext: cn=Monitor
+ readOnly: FALSE
+ entryDN: cn=Database 2,cn=Databases,cn=Monitor
+ subschemaSubentry: cn=Subschema
+ hasSubordinates: FALSE
+</PRE>
+<H3><A NAME="Listener">20.4.4. Listener</A></H3>
+<P>It contains the description of the devices the server is currently listening on:</P>
+<PRE>
+ dn: cn=Listener 0,cn=Listeners,cn=Monitor
+ structuralObjectClass: monitoredObject
+ monitorConnectionLocalAddress: IP=0.0.0.0:389
+ entryDN: cn=Listener 0,cn=Listeners,cn=Monitor
+ subschemaSubentry: cn=Subschema
+ hasSubordinates: FALSE
+</PRE>
+<H3><A NAME="Log">20.4.5. Log</A></H3>
+<P>It contains the currently active log items. The <EM>Log</EM> subsystem allows user modify operations on the <EM>description</EM> attribute, whose values <EM>MUST</EM> be in the list of admittable log switches:</P>
+<PRE>
+ Trace
+ Packets
+ Args
+ Conns
+ BER
+ Filter
+ Config
+ ACL
+ Stats
+ Stats2
+ Shell
+ Parse
+ Sync
+</PRE>
+<P>These values can be added, replaced or deleted; they affect what messages are sent to the syslog device. Custom values could be added by custom modules.</P>
+<H3><A NAME="Operations">20.4.6. Operations</A></H3>
+<P>It shows some statistics on the operations performed by the server:</P>
+<PRE>
+ Initiated
+ Completed
+</PRE>
+<P>and for each operation type, i.e.:</P>
+<PRE>
+ Bind
+ Unbind
+ Add
+ Delete
+ Modrdn
+ Modify
+ Compare
+ Search
+ Abandon
+ Extended
+</PRE>
+<P>There are too many types to list example here, so please try for yourself using <A HREF="#Monitor search example">Monitor search example</A></P>
+<H3><A NAME="Overlays">20.4.7. Overlays</A></H3>
+<P>The main entry contains the type of overlays available at run-time; the child entries, for each overlay, contain the type of the overlay.</P>
+<P>It should also contain the modules that have been loaded if dynamic overlays are enabled:</P>
+<PRE>
+ # Overlays, Monitor
+ dn: cn=Overlays,cn=Monitor
+ structuralObjectClass: monitorContainer
+ monitoredInfo: syncprov
+ monitoredInfo: accesslog
+ monitoredInfo: glue
+ entryDN: cn=Overlays,cn=Monitor
+ subschemaSubentry: cn=Subschema
+ hasSubordinates: TRUE
+</PRE>
+<H3><A NAME="SASL">20.4.8. SASL</A></H3>
+<P>Currently empty.</P>
+<H3><A NAME="Statistics">20.4.9. Statistics</A></H3>
+<P>It shows some statistics on the data sent by the server:</P>
+<PRE>
+ Bytes
+ PDU
+ Entries
+ Referrals
+</PRE>
+<P>e.g.</P>
+<PRE>
+ # Entries, Statistics, Monitor
+ dn: cn=Entries,cn=Statistics,cn=Monitor
+ structuralObjectClass: monitorCounterObject
+ monitorCounter: 612248
+ entryDN: cn=Entries,cn=Statistics,cn=Monitor
+ subschemaSubentry: cn=Subschema
+ hasSubordinates: FALSE
+</PRE>
+<H3><A NAME="Threads">20.4.10. Threads</A></H3>
+<P>It contains the maximum number of threads enabled at startup and the current backload.</P>
+<P>e.g.</P>
+<PRE>
+ # Max, Threads, Monitor
+ dn: cn=Max,cn=Threads,cn=Monitor
+ structuralObjectClass: monitoredObject
+ monitoredInfo: 16
+ entryDN: cn=Max,cn=Threads,cn=Monitor
+ subschemaSubentry: cn=Subschema
+ hasSubordinates: FALSE
+</PRE>
+<H3><A NAME="Time">20.4.11. Time</A></H3>
+<P>It contains two child entries with the start time and the current time of the server.</P>
+<P>e.g.</P>
+<P>Start time:</P>
+<PRE>
+ dn: cn=Start,cn=Time,cn=Monitor
+ structuralObjectClass: monitoredObject
+ monitorTimestamp: 20061205124040Z
+ entryDN: cn=Start,cn=Time,cn=Monitor
+ subschemaSubentry: cn=Subschema
+ hasSubordinates: FALSE
+</PRE>
+<P>Current time:</P>
+<PRE>
+ dn: cn=Current,cn=Time,cn=Monitor
+ structuralObjectClass: monitoredObject
+ monitorTimestamp: 20061207120624Z
+ entryDN: cn=Current,cn=Time,cn=Monitor
+ subschemaSubentry: cn=Subschema
+ hasSubordinates: FALSE
+</PRE>
+<H3><A NAME="TLS">20.4.12. TLS</A></H3>
+<P>Currently empty.</P>
+<H3><A NAME="Waiters">20.4.13. Waiters</A></H3>
+<P>It contains the number of current read waiters.</P>
+<P>e.g.</P>
+<P>Read waiters:</P>
+<PRE>
+ dn: cn=Read,cn=Waiters,cn=Monitor
+ structuralObjectClass: monitorCounterObject
+ monitorCounter: 7
+ entryDN: cn=Read,cn=Waiters,cn=Monitor
+ subschemaSubentry: cn=Subschema
+ hasSubordinates: FALSE
+</PRE>
+<P>Write waiters:</P>
+<PRE>
+ dn: cn=Write,cn=Waiters,cn=Monitor
+ structuralObjectClass: monitorCounterObject
+ monitorCounter: 0
+ entryDN: cn=Write,cn=Waiters,cn=Monitor
+ subschemaSubentry: cn=Subschema
+ hasSubordinates: FALSE
+</PRE>
+<P>Add new monitored things here and discuss, referencing man pages and present examples</P>
+<P></P>
+<HR>
+<H1><A NAME="Load Balancing with lloadd">21. Load Balancing with lloadd</A></H1>
+<P>As covered in the <A HREF="#Replication">Replication</A> chapter, replication is a fundamental requirement for delivering a resilient enterprise deployment. As such there's a need for an LDAPv3 capable load balancer to spread the load between the various directory instances.</P>
+<P><EM>lloadd</EM>(8) provides the capability to distribute LDAP v3 requests between a set of running <EM>slapd</EM> instances. It can run as a standalone daemon <EM>lloadd</EM>, or as an embedded module running inside of <EM>slapd</EM>.</P>
+<H2><A NAME="Overview">21.1. Overview</A></H2>
+<P><EM>lloadd</EM>(8) was designed to handle LDAP loads. It is protocol-aware and can balance LDAP loads on a per-operation basis rather than on a per-connection basis.</P>
+<P><EM>lloadd</EM>(8) distributes the load across a set of slapd instances. The client connects to the load balancer instance which forwards the request to one of the servers and returns the response back to the client.</P>
+<H2><A NAME="When to use the OpenLDAP load balancer">21.2. When to use the OpenLDAP load balancer</A></H2>
+<P>In general, the OpenLDAP load balancer spreads the load across configured backend servers. It does not perform so-called intelligent routing. It does not understand semantics behind the operations being performed by the clients.</P>
+<P>More considerations:</P>
+<UL><UL>
+<LI>Servers are indistinguishable with respect to data contents. The exact same copy of data resides on every server.
+<LI>Clients do not require 'sticky' sessions.
+<LI>The sequence of operations isn't important. For example, read after update isn't required by the client.
+<LI>If your client can handle both connection pooling and load distribution then it's preferable to lloadd.
+<LI>Clients that require a consistent session (e.g. do writes), the best practice is to let them set up a direct session to one of the providers. The read-only clients are still free to use lloadd.
+<LI>2.6 release of lloadd will include sticky sessions (coherency).</UL></UL>
+<H2><A NAME="Runtime configurations">21.3. Runtime configurations</A></H2>
+<P>It deploys in one of two ways:</P>
+<OL>
+<LI>Standalone daemon: <EM>lloadd </EM>
+<LI>Loaded into the slapd daemon as a module: <EM>lloadd.la </EM></OL>
+<P>It is recommended to run with the balancer module embedded in slapd because dynamic configuration (cn=config) and the monitor backend are then available.</P>
+<P><B>Sample load balancer scenario:</B></P>
+<P><CENTER><IMG SRC="load-balancer-scenario.png" ALIGN="center"></CENTER></P>
+<P ALIGN="Center">Figure: Load balancer sample scenario</P>
+<OL>
+<LI>The LDAP client submits an LDAP operation to the load balancer daemon.
+<LI>The load balancer forwards the request to one of the backend instances in its pool of servers.
+<LI>The backend slapd server processes the request and returns the response to the load balancer instance.
+<LI>The load balancer returns the response to the client. The client's unaware that it's connecting to a load balancer instead of slapd.</OL>
+<H2><A NAME="Build Notes">21.4. Build Notes</A></H2>
+<P>To build the load balancer from source, follow the instructions in the <A HREF="#A Quick-Start Guide">A Quick-Start Guide</A> substituting the following commands:</P>
+<OL>
+<LI>To configure as standalone daemon:<UL>
+<TT>./configure --enable-balancer=yes</TT></UL>
+<LI>To configure as embedded module to slapd:<UL>
+<TT>./configure --enable-modules --enable-balancer=mod</TT></UL></OL>
+<H2><A NAME="Sample Runtime">21.5. Sample Runtime</A></H2>
+<OL>
+<LI>To run embedded as <EM>lloadd </EM> module:<UL>
+<TT> slapd [-h URLs] [-f lloadd-config-file] [-u user] [-g group]</TT></UL><UL>
+<LI>the startup is the same as starting the <EM>slapd </EM> daemon.</UL><UL>
+<LI>URLs is for slapd management. The load balancer's listener URLs set in the configuration file or node. (more later)</UL>
+<LI>To run as standalone daemon:<UL>
+<TT> lloadd [-h URLs] [-f lloadd-config-file] [-u user] [-g group]</TT></UL><UL>
+<LI>Other than a different daemon name, running standalone has the same options as starting <EM>slapd </EM>.</UL><UL>
+<LI>-h URLs specify the lloadd's interface directly, there is no management interface.</UL></OL>
+<P>For a complete list of options, checkout the man page <EM>lloadd.8 </EM></P>
+<H2><A NAME="Configuring load balancer">21.6. Configuring load balancer</A></H2>
+<H3><A NAME="Common configuration options">21.6.1. Common configuration options</A></H3>
+<P>Many of the same configuration options as slapd. For complete list, check the <EM>lloadd</EM>(5) man page.</P>
+<UL>
+&nbsp; <B>Edit the slapd.conf or cn=config configuration file</B>.</UL>
+<P>To configure your working <EM>lloadd</EM>(8) you need to make the following changes to your configuration file:</P>
+<OL>
+<LI>include <EM>core.schema </EM> (embedded only)
+<LI><EM>TLSShareSlapdCTX { on | off } </EM>
+<LI>Other common TLS slapd options
+<LI>Setup argsfile/pidfile
+<LI>Setup moduleload path (embedded mode only)
+<LI><EM>moduleload lloadd.la </EM>
+<LI>loglevel, threads, ACL's
+<LI><EM>backend lload </EM> begin lloadd specific backend configurations
+<LI><EM>listen ldap://:PORT </EM> Specify listen port for load balancer
+<LI><EM>feature proxyauthz </EM> Use the proxy authZ control to forward client's identity
+<LI><EM>io-threads INT </EM> specify the number of threads to use for the connection manager. The default is 1 and this is typically adequate for up to 16 CPU cores</OL>
+<H3><A NAME="Sample backend config">21.6.2. Sample backend config</A></H3>
+<P>Sample setup config for load balancer running in front of four slapd instances.</P>
+<PRE>
+backend lload
+
+# The Load Balancer manages its own sockets, so they have to be separate
+# from the ones slapd manages (as specified with the -h &quot;URLS&quot; option at
+# startup).
+listen ldap://:1389
+
+# Enable authorization tracking
+feature proxyauthz
+
+# Specify the number of threads to use for the connection manager. The default is 1 and this is typically adequate for up to 16 CPU cores.
+# The value should be set to a power of 2:
+io-threads 2
+
+# If TLS is configured above, use the same context for the Load Balancer
+# If using cn=config, this can be set to false and different settings
+# can be used for the Load Balancer
+TLSShareSlapdCTX true
+
+# Authentication and other options (timeouts) shared between backends.
+bindconf bindmethod=simple
+ binddn=dc=example,dc=com credentials=secret
+ network-timeout=5
+ tls_cacert=&quot;/usr/local/etc/openldap/ca.crt&quot;
+ tls_cert=&quot;/usr/local/etc/openldap/host.crt&quot;
+ tls_key=&quot;/usr/local/etc/openldap/host.pem&quot;
+
+
+# List the backends we should relay operations to, they all have to be
+# practically indistinguishable. Only TLS settings can be specified on
+# a per-backend basis.
+
+backend-server uri=ldap://ldaphost01 starttls=critical retry=5000
+ max-pending-ops=50 conn-max-pending=10
+ numconns=10 bindconns=5
+backend-server uri=ldap://ldaphost02 starttls=critical retry=5000
+ max-pending-ops=50 conn-max-pending=10
+ numconns=10 bindconns=5
+backend-server uri=ldap://ldaphost03 starttls=critical retry=5000
+ max-pending-ops=50 conn-max-pending=10
+ numconns=10 bindconns=5
+backend-server uri=ldap://ldaphost04 starttls=critical retry=5000
+ max-pending-ops=50 conn-max-pending=10
+ numconns=10 bindconns=5
+
+#######################################################################
+# Monitor database
+#######################################################################
+database monitor
+</PRE>
+<P></P>
+<HR>
+<H1><A NAME="Tuning">22. Tuning</A></H1>
+<P>This is perhaps one of the most important chapters in the guide, because if you have not tuned <EM>slapd</EM>(8) correctly or grasped how to design your directory and environment, you can expect very poor performance.</P>
+<P>Reading, understanding and experimenting using the instructions and information in the following sections, will enable you to fully understand how to tailor your directory server to your specific requirements.</P>
+<P>It should be noted that the following information has been collected over time from our community based FAQ. So obviously the benefit of this real world experience and advice should be of great value to the reader.</P>
+<H2><A NAME="Performance Factors">22.1. Performance Factors</A></H2>
+<P>Various factors can play a part in how your directory performs on your chosen hardware and environment. We will attempt to discuss these here.</P>
+<H3><A NAME="Memory">22.1.1. Memory</A></H3>
+<P>Scale your cache to use available memory and increase system memory if you can.</P>
+<H3><A NAME="Disks">22.1.2. Disks</A></H3>
+<P>Use fast filesystems, and conduct your own testing to see which filesystem types perform best with your workload. (On our own Linux testing, EXT2 and JFS tend to provide better write performance than everything else, including newer filesystems like EXT4, BTRFS, etc.)</P>
+<P>Use fast subsystems. Put each database on separate disks.</P>
+<H3><A NAME="Network Topology">22.1.3. Network Topology</A></H3>
+<P>http://www.openldap.org/faq/data/cache/363.html</P>
+<P>Drawing here.</P>
+<H3><A NAME="Directory Layout Design">22.1.4. Directory Layout Design</A></H3>
+<P>Reference to other sections and good/bad drawing here.</P>
+<H3><A NAME="Expected Usage">22.1.5. Expected Usage</A></H3>
+<P>Discussion.</P>
+<H2><A NAME="Indexes">22.2. Indexes</A></H2>
+<H3><A NAME="Understanding how a search works">22.2.1. Understanding how a search works</A></H3>
+<P>If you're searching on a filter that has been indexed, then the search reads the index and pulls exactly the entries that are referenced by the index. If the filter term has not been indexed, then the search must read every single entry in the target scope and test to see if each entry matches the filter. Obviously indexing can save a lot of work when it's used correctly.</P>
+<P>In back-mdb, indexes can only track a certain number of entries per key (by default that number is 2^16 = 65536). If more entries' values hash to this key, some/all of them will have to be represented by a range of candidates, making the index less useful over time as deletions cannot usually be tracked accurately.</P>
+<H3><A NAME="What to index">22.2.2. What to index</A></H3>
+<P>As a general rule, to make any use of indexes, you must set up an equality index on objectClass:</P>
+<PRE>
+ index objectClass eq
+</PRE>
+<P>Then you should create indices to match the actual filter terms used in search queries.</P>
+<PRE>
+ index cn,sn,givenname,mail eq
+</PRE>
+<P>Each attribute index can be tuned further by selecting the set of index types to generate. For example, substring and approximate search for organizations (o) may make little sense (and isn't like done very often). And searching for <EM>userPassword</EM> likely makes no sense what so ever.</P>
+<P>General rule: don't go overboard with indexes. Unused indexes must be maintained and hence can only slow things down.</P>
+<P>See <EM>slapd.conf</EM>(5) and <EM>slapdindex</EM>(8) for more information</P>
+<H3><A NAME="Presence indexing">22.2.3. Presence indexing</A></H3>
+<P>If your client application uses presence filters and if the target attribute exists on the majority of entries in your target scope, then all of those entries are going to be read anyway, because they are valid members of the result set. In a subtree where 100% of the entries are going to contain the same attributes, the presence index does absolutely NOTHING to benefit the search, because 100% of the entries match that presence filter. As an example, setting a presence index on objectClass provides no benefit since it is present on every entry.</P>
+<P>So the resource cost of generating the index is a complete waste of CPU time, disk, and memory. Don't do it unless you know that it will be used, and that the attribute in question occurs very infrequently in the target data.</P>
+<P>Almost no applications use presence filters in their search queries. Presence indexing is pointless when the target attribute exists on the majority of entries in the database. In most LDAP deployments, presence indexing should not be done, it's just wasted overhead.</P>
+<P>See the <EM>Logging</EM> section below on what to watch out for if you have a frequently searched for attribute that is unindexed.</P>
+<H3><A NAME="Equality indexing">22.2.4. Equality indexing</A></H3>
+<P>Similarly to presence indexes, equality indexes are most useful if the values searched for are uncommon. Most OpenLDAP indexes work by hashing the normalised value and using the hash as the key. Hashing behaviour depends on the matching rule syntax, some matching rules also implement indexers that help speed up inequality (lower than, ...) queries.</P>
+<P>Check the documentation and other parts of this guide if some indexes are mandatory - e.g. to enable replication, it is expected you index certain operational attributes, likewise if you rely on filters in ACL processing.</P>
+<P>Approximate indexes are usually identical to equality indexes unless a matching rule explicitly implements it. As of OpenLDAP 2.5, only directoryStringApproxMatch and IA5StringApproxMatch matchers and indexers are implemented, currently using soundex or metaphone, with metaphone being the default.</P>
+<H3><A NAME="Substring indexing">22.2.5. Substring indexing</A></H3>
+<P>Substring indexes work on splitting the value into short chunks and then indexing those in a similar way to how equality index does. The storage space needed to store all of this data is analogous to the amount of data being indexed, which makes the indexes extremely heavy-handed in most scenarios.</P>
+<H2><A NAME="Logging">22.3. Logging</A></H2>
+<H3><A NAME="What log level to use">22.3.1. What log level to use</A></H3>
+<P>The default of <EM>loglevel stats</EM> (256) is really the best bet. There's a corollary to this when problems *do* arise, don't try to trace them using syslog. Use the debug flag instead, and capture slapd's stderr output. syslog is too slow for debug tracing, and it's inherently lossy - it will throw away messages when it can't keep up. See <EM>slapd.conf</EM>(5) or <EM>slapd-config</EM>(5) for more information on how to configure the loglevel.</P>
+<P>Contrary to popular belief, <EM>loglevel 0</EM> is not ideal for production as you won't be able to track when problems first arise.</P>
+<H3><A NAME="What to watch out for">22.3.2. What to watch out for</A></H3>
+<P>The most common message you'll see that you should pay attention to is:</P>
+<PRE>
+ &quot;&lt;= mdb_equality_candidates: (foo) index_param failed (18)&quot;
+</PRE>
+<P>That means that some application tried to use an equality filter (<EM>foo=&lt;somevalue&gt;</EM>) and attribute <EM>foo</EM> does not have an equality index. If you see a lot of these messages, you should add the index. If you see one every month or so, it may be acceptable to ignore it.</P>
+<P>The default syslog level is stats (256) which logs the basic parameters of each request; it usually produces 1-3 lines of output. On Solaris and systems that only provide synchronous syslog, you may want to turn it off completely, but usually you want to leave it enabled so that you'll be able to see index messages whenever they arise. On Linux you can configure syslogd to run asynchronously, in which case the performance hit for moderate syslog traffic pretty much disappears.</P>
+<H3><A NAME="Improving throughput">22.3.3. Improving throughput</A></H3>
+<P>You can improve logging performance on some systems by configuring syslog not to sync the file system with every write (<EM>man syslogd/syslog.conf</EM>). In Linux, you can prepend the log file name with a &quot;-&quot; in <EM>syslog.conf</EM>. For example, if you are using the default LOCAL4 logging you could try:</P>
+<PRE>
+ # LDAP logs
+ LOCAL4.* -/var/log/ldap
+</PRE>
+<P>For syslog-ng, add or modify the following line in <EM>syslog-ng.conf</EM>:</P>
+<PRE>
+ options { sync(n); };
+</PRE>
+<P>where n is the number of lines which will be buffered before a write.</P>
+<H2><A NAME="{{slapd}}(8) Threads">22.4. <EM>slapd</EM>(8) Threads</A></H2>
+<P><EM>slapd</EM>(8) can process requests via a configurable number of threads, which in turn affects the in/out rate of connections.</P>
+<P>This value should generally be a function of the number of &quot;real&quot; cores on the system, for example on a server with 2 CPUs with one core each, set this to 8, or 4 threads per real core. This is a &quot;read&quot; maximized value. The more threads that are configured per core, the slower <EM>slapd</EM>(8) responds for &quot;read&quot; operations. On the flip side, it appears to handle write operations faster in a heavy write/low read scenario.</P>
+<P>The upper bound for good read performance appears to be 16 threads (which also happens to be the default setting).</P>
+<P></P>
+<HR>
+<H1><A NAME="Troubleshooting">23. Troubleshooting</A></H1>
+<P>If you're having trouble using OpenLDAP, get onto the OpenLDAP-Software mailing list, or:</P>
+<UL>
+<LI>Browse the list archives at <A HREF="http://www.openldap.org/lists/#archives">http://www.openldap.org/lists/#archives</A>
+<LI>Search the FAQ at <A HREF="http://www.openldap.org/faq/">http://www.openldap.org/faq/</A>
+<LI>Search the Issue Tracking System at <A HREF="http://www.openldap.org/its/">http://www.openldap.org/its/</A></UL>
+<P>Chances are the problem has been solved and explained in detail many times before.</P>
+<H2><A NAME="User or Software errors">23.1. User or Software errors?</A></H2>
+<P>More often than not, an error is caused by a configuration problem or a misunderstanding of what you are trying to implement and/or achieve.</P>
+<P>We will now attempt to discuss common user errors.</P>
+<H2><A NAME="Checklist">23.2. Checklist</A></H2>
+<P>The following checklist can help track down your problem. Please try to use if <B>before</B> posting to the list, or in the rare circumstances of reporting a bug.</P>
+<UL>
+&nbsp;</UL><OL>
+<LI><B>Use the <EM>slaptest</EM> tool to verify configurations before starting <EM>slapd</EM></B>
+<BR>
+&nbsp;
+<LI><B>Verify that <EM>slapd</EM> is listening to the specified port(s) (389 and 636, generally) before trying the <EM>ldapsearch</EM></B>
+<BR>
+&nbsp;
+<LI><B>Can you issue an <EM>ldapsearch</EM>?</B>
+<BR>
+&nbsp;
+<LI><B>If not, have you enabled complex ACLs without fully understanding them?</B>
+<BR>
+&nbsp;
+<LI><B>Do you have a system wide LDAP setting pointing to the wrong LDAP Directory?</B>
+<BR>
+&nbsp;
+<LI><B>Are you using TLS?</B>
+<BR>
+&nbsp;
+<LI><B>Have your certificates expired?</B></OL>
+<H2><A NAME="OpenLDAP Bugs">23.3. OpenLDAP Bugs</A></H2>
+<P>Sometimes you may encounter an actual OpenLDAP bug, in which case please visit our Issue Tracking system <A HREF="http://www.openldap.org/its/">http://www.openldap.org/its/</A> and report it. However, make sure it's not already a known bug or a common user problem.</P>
+<UL>
+<LI>bugs in historic versions of OpenLDAP will not be considered;
+<LI>bugs in released versions that are no longer present in the Git master branch, either because they have been fixed or because they no longer apply, will not be considered as well;
+<LI>bugs in distributions of OpenLDAP software that are not related to the software as provided by OpenLDAP will not be considered; in those cases please refer to the distributor.</UL>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>Our Issue Tracking system is <B>NOT</B> for OpenLDAP <B>Support</B>, please join our mailing Lists: <A HREF="http://www.openldap.org/lists/">http://www.openldap.org/lists/</A> for that.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>The information you should provide in your bug report is discussed in our FAQ-O-MATIC at <A HREF="http://www.openldap.org/faq/data/cache/59.html">http://www.openldap.org/faq/data/cache/59.html</A></P>
+<H2><A NAME="3rd party software error">23.4. 3rd party software error</A></H2>
+<P>The OpenLDAP Project only supports OpenLDAP software.</P>
+<P>You may however seek commercial support (<A HREF="http://www.openldap.org/support/">http://www.openldap.org/support/</A>) or join the general LDAP forum for non-commercial discussions and information relating to LDAP at: <A HREF="http://www.umich.edu/~dirsvcs/ldap/mailinglist.html">http://www.umich.edu/~dirsvcs/ldap/mailinglist.html</A></P>
+<H2><A NAME="How to contact the OpenLDAP Project">23.5. How to contact the OpenLDAP Project</A></H2>
+<UL>
+<LI>Mailing Lists: <A HREF="http://www.openldap.org/lists/">http://www.openldap.org/lists/</A>
+<LI>Project: <A HREF="http://www.openldap.org/project/">http://www.openldap.org/project/</A>
+<LI>Issue Tracking: <A HREF="http://www.openldap.org/its/">http://www.openldap.org/its/</A></UL>
+<H2><A NAME="How to present your problem">23.6. How to present your problem</A></H2>
+<H2><A NAME="Debugging {{slapd}}(8)">23.7. Debugging <EM>slapd</EM>(8)</A></H2>
+<P>After reading through the above sections and before e-mailing the OpenLDAP lists, you might want to try out some of the following to track down the cause of your problems:</P>
+<UL>
+<LI>A loglevel of stats (256) is generally a good first loglevel to use for getting information useful to list members on issues. This is the default loglevel if none is configured.
+<LI>Running <EM>slapd -d -1</EM> can often track down fairly simple issues, such as missing schemas and incorrect file permissions for the <EM>slapd</EM> user to things like certs
+<LI>Check your logs for errors, as discussed at <A HREF="http://www.openldap.org/faq/data/cache/358.html">http://www.openldap.org/faq/data/cache/358.html</A></UL>
+<H2><A NAME="Commercial Support">23.8. Commercial Support</A></H2>
+<P>The firms listed at <A HREF="http://www.openldap.org/support/">http://www.openldap.org/support/</A> offer technical support services catering to OpenLDAP community.</P>
+<P>The listing of any given firm should not be viewed as an endorsement or recommendation of any kind, nor as otherwise indicating there exists a business relationship or an affiliation between any listed firm and the OpenLDAP Foundation or the OpenLDAP Project or its contributors.</P>
+<P></P>
+<HR>
+<H1><A NAME="Changes Since Previous Release">A. Changes Since Previous Release</A></H1>
+<P>The following sections attempt to summarize the new features and changes in OpenLDAP software since the 2.4.x release and the OpenLDAP Admin Guide.</P>
+<H2><A NAME="New Guide Sections">A.1. New Guide Sections</A></H2>
+<P>In order to make the Admin Guide more thorough and cover the majority of questions asked on the OpenLDAP mailing lists and scenarios discussed there, we have added the following new sections:</P>
+<UL>
+<LI><A HREF="#When should I use LDAP">When should I use LDAP?</A>
+<LI><A HREF="#When should I not use LDAP">When should I not use LDAP?</A>
+<LI><A HREF="#LDAP vs RDBMS">LDAP vs RDBMS</A>
+<LI><A HREF="#Access Control">Access Control</A>
+<LI><A HREF="#Backends">Backends</A>
+<LI><A HREF="#Overlays">Overlays</A>
+<LI><A HREF="#Replication">Replication</A>
+<LI><A HREF="#Maintenance">Maintenance</A>
+<LI><A HREF="#Monitoring">Monitoring</A>
+<LI><A HREF="#Tuning">Tuning</A>
+<LI><A HREF="#Troubleshooting">Troubleshooting</A>
+<LI><A HREF="#Changes Since Previous Release">Changes Since Previous Release</A>
+<LI><A HREF="#Upgrading from 2.4.x">Upgrading from 2.4.x</A>
+<LI><A HREF="#Common errors encountered when using OpenLDAP Software">Common errors encountered when using OpenLDAP Software</A>
+<LI><A HREF="#Recommended OpenLDAP Software Dependency Versions">Recommended OpenLDAP Software Dependency Versions</A>
+<LI><A HREF="#Real World OpenLDAP Deployments and Examples">Real World OpenLDAP Deployments and Examples</A>
+<LI><A HREF="#OpenLDAP Software Contributions">OpenLDAP Software Contributions</A>
+<LI><A HREF="#Configuration File Examples">Configuration File Examples</A>
+<LI><A HREF="#LDAP Result Codes">LDAP Result Codes</A>
+<LI><A HREF="#Glossary">Glossary</A></UL>
+<P>Also, the table of contents is now 3 levels deep to ease navigation.</P>
+<H2><A NAME="New Features and Enhancements in 2.5">A.2. New Features and Enhancements in 2.5</A></H2>
+<H3><A NAME="Better {{B:cn=config}} functionality">A.2.1. Better <B>cn=config</B> functionality</A></H3>
+<H3><A NAME="Better {{B:cn=schema}} functionality">A.2.2. Better <B>cn=schema</B> functionality</A></H3>
+<H3><A NAME="More sophisticated Syncrepl configurations">A.2.3. More sophisticated Syncrepl configurations</A></H3>
+<H3><A NAME="Replicating {{slapd}} Configuration (syncrepl and {{B:cn=config}})">A.2.4. Replicating <EM>slapd</EM> Configuration (syncrepl and <B>cn=config</B>)</A></H3>
+<H3><A NAME="More extensive TLS configuration control">A.2.5. More extensive TLS configuration control</A></H3>
+<H3><A NAME="Performance enhancements">A.2.6. Performance enhancements</A></H3>
+<H3><A NAME="New overlays">A.2.7. New overlays</A></H3>
+<H3><A NAME="New features in existing Overlays">A.2.8. New features in existing Overlays</A></H3>
+<H3><A NAME="New features in slapd">A.2.9. New features in slapd</A></H3>
+<H3><A NAME="New features in libldap">A.2.10. New features in libldap</A></H3>
+<H3><A NAME="New clients, tools and tool enhancements">A.2.11. New clients, tools and tool enhancements</A></H3>
+<H3><A NAME="New build options">A.2.12. New build options</A></H3>
+<H2><A NAME="Obsolete Features Removed From 2.5">A.3. Obsolete Features Removed From 2.5</A></H2>
+<P>These features were strongly deprecated in 2.4 and removed in 2.5.</P>
+<H3><A NAME="back-bdb and back-hdb">A.3.1. back-bdb and back-hdb</A></H3>
+<P>back-bdb and back-hdb were significantly slower than back-mdb and required significant tuning of multiple parameters to maximize performance. back-mdb requires no tuning and provides all the functionality previously provided via back-bdb and back-hdb.</P>
+<P></P>
+<HR>
+<H1><A NAME="Upgrading from 2.4.x">B. Upgrading from 2.4.x</A></H1>
+<P>The following sections attempt to document the steps you will need to take in order to upgrade from the latest 2.4.x OpenLDAP version.</P>
+<P>The normal upgrade procedure, as discussed in the <A HREF="#Maintenance">Maintenance</A> section, should of course still be followed prior to doing any of this.</P>
+<H2><A NAME="{{B:cn=config}} olc* attributes">B.1. <B>cn=config</B> olc* attributes</A></H2>
+<P>The <EM>olcMirrorMode</EM> attribute has been renamed to <EM>olcMultiProvider</EM>. Existing configurations will continue to work with the old parameter name, but it is advised to update to the new name as a part of the upgrade process.</P>
+<H2><A NAME="ppolicy overlay">B.2. ppolicy overlay</A></H2>
+<P>The overlay now implements version 10 of the ppolicy draft in full. This includes the notion of a password administrator where applicable (as determined by having a <EM>manage</EM> permission to the <EM>userPassword</EM> attribute) and skips certain processing when there is no valid policy in effect or where the operation is initiated by a password administrator. Many attributes are now tagged with <EM>NO-USER-MODIFICATION</EM> in the schema, requiring the use of <EM>relax</EM> control to modify them.</P>
+<P>In OpenLDAP 2.4 the <EM>slapo-ppolicy</EM>(5) overlay relied on a separate schema file to be included for it to function. This schema is now implemented internally in the slapo-ppolicy module. When upgrading <EM>slapd.conf</EM>(5) deployments the include statement for the schema must be removed. For <EM>slapd-config</EM>(5) deployments, the config database must be exported via slapcat and the old ppolicy schema removed from the export. The resulting config database can then be imported.</P>
+<H2><A NAME="unique overlay">B.3. unique overlay</A></H2>
+<P>In OpenLDAP 2.4 it was possible to bypass <EM>slapo-unique</EM>(5) checks by using the manageDSAIT control as a part of the request. This is no longer possible. To achieve the same functionality the relax control must be used instead, and the binding identity must have manage permissions on the entry being modified.</P>
+<P>With OpenLDAP 2.5 a new keyword &quot;serialize&quot; has been added as a part of the unique_uri configuration parameter. This will cause all write operations requiring uniqueness to be serialized so as to avoid the scenario where multiple concurrent updates can prevent uniqueness from being enforced. See the <EM>slapo-unique</EM>(5) man page for further details.</P>
+<H2><A NAME="ldap and meta backends">B.4. ldap and meta backends</A></H2>
+<P>Several deprecated configuration directives for <EM>slapd-ldap</EM>(5) and <EM>slapd-meta</EM>(5) have been removed. Configurations using those directive must be updated to use supported directives prior to upgrade. See the <EM>slapd-ldap</EM>(5) and <EM>slapd-meta</EM>(5) man pages from OpenLDAP 2.4 for a list of deprecated directives.</P>
+<H2><A NAME="shell backend">B.5. shell backend</A></H2>
+<P>This deprecated backend has been removed from OpenLDAP 2.5. Configurations making use of this backend must remove it prior to upgrade. The <EM>slapd-sock</EM>(5) backend is recommended as an alternative.</P>
+<H2><A NAME="perl and sql backends">B.6. perl and sql backends</A></H2>
+<P>The <EM>slapd-perl</EM>(5) and <EM>slapd-sql</EM>(5) backends are now deprecated and no longer automatically enabled with the --enable-backends configure flag.</P>
+<H2><A NAME="hdb and bdb backends">B.7. hdb and bdb backends</A></H2>
+<P>The Berkeley DB based slapd-bdb and slapd-hdb backends have been removed from OpenLDAP 2.5. Deployments making use of these backends must migrate their configurations to use <EM>slapd-mdb</EM>(5) prior to upgrade.</P>
+<H2><A NAME="mdb backend">B.8. mdb backend</A></H2>
+<P>It is advised to determine if the new <EM>slapd-mdb</EM>(5) idlexp backend directive and/or the multival database directive should be added to the OpenLDAP 2.5 configuration as well as the existing global sortvals directive. Configuring any of these items requires that existing databases be reloaded for them to take full effect. This can be done separately from the overall upgrade from OpenLDAP 2.4 to OpenLDAP 2.5 if desired.</P>
+<H2><A NAME="Client utility changes">B.9. Client utility changes</A></H2>
+<P>The deprecated &quot;-h&quot; (host) and &quot;-p&quot; (port) options for the ldap client utilities have been removed. It is required to use a properly formatted LDAP URI with the &quot;-H&quot; option in OpenLDAP 2.5 and later.</P>
+<P></P>
+<HR>
+<H1><A NAME="Common errors encountered when using OpenLDAP Software">C. Common errors encountered when using OpenLDAP Software</A></H1>
+<P>The following sections attempt to summarize the most common causes of LDAP errors when using OpenLDAP</P>
+<H2><A NAME="Common causes of LDAP errors">C.1. Common causes of LDAP errors</A></H2>
+<H3><A NAME="ldap_*: Can\'t contact LDAP server">C.1.1. ldap_*: Can't contact LDAP server</A></H3>
+<P>The <B>Can't contact LDAP server</B> error is usually returned when the LDAP server cannot be contacted. This may occur for many reasons:</P>
+<UL>
+<LI>the LDAP server is not running; this can be checked by running, for example,</UL>
+<PRE>
+ telnet &lt;host&gt; &lt;port&gt;
+</PRE>
+<P>replacing <EM>&lt;host&gt;</EM> and <EM>&lt;port&gt;</EM> with the hostname and the port the server is supposed to listen on.</P>
+<UL>
+<LI>the client has not been instructed to contact a running server; with OpenLDAP command-line tools this is accomplished by providing the -H switch, whose argument is a valid LDAP url corresponding to the interface the server is supposed to be listening on.</UL>
+<H3><A NAME="ldap_*: No such object">C.1.2. ldap_*: No such object</A></H3>
+<P>The <B>no such object</B> error is generally returned when the target DN of the operation cannot be located. This section details reasons common to all operations. You should also look for answers specific to the operation (as indicated in the error message).</P>
+<P>The most common reason for this error is non-existence of the named object. First, check for typos.</P>
+<P>Also note that, by default, a new directory server holds no objects (except for a few system entries). So, if you are setting up a new directory server and get this message, it may simply be that you have yet to add the object you are trying to locate.</P>
+<P>The error commonly occurs because a DN was not specified and a default was not properly configured.</P>
+<P>If you have a suffix specified in slapd.conf eg.</P>
+<PRE>
+ suffix &quot;dc=example,dc=com&quot;
+</PRE>
+<P>You should use</P>
+<PRE>
+ ldapsearch -b 'dc=example,dc=com' '(cn=jane*)'
+</PRE>
+<P>to tell it where to start the search.</P>
+<P>The <TT>-b</TT> should be specified for all LDAP commands unless you have an <EM>ldap.conf</EM>(5) default configured.</P>
+<P>See <EM>ldapsearch</EM>(1), <EM>ldapmodify</EM>(1)</P>
+<P>Also, <EM>slapadd</EM>(8) and its ancillary programs are very strict about the syntax of the LDIF file.</P>
+<P>Some liberties in the LDIF file may result in an apparently successful creation of the database, but accessing some parts of it may be difficult.</P>
+<P>One known common error in database creation is putting a blank line before the first entry in the LDIF file. <B>There must be no leading blank lines in the LDIF file.</B></P>
+<P>It is generally recommended that <EM>ldapadd</EM>(1) be used instead of <EM>slapadd</EM>(8) when adding new entries your directory. <EM>slapadd</EM>(8) should be used to bulk load entries known to be valid.</P>
+<P>Another cause of this message is a referral ({SECT:Constructing a Distributed Directory Service}}) entry to an unpopulated directory.</P>
+<P>Either remove the referral, or add a single record with the referral base DN to the empty directory.</P>
+<P>This error may also occur when slapd is unable to access the contents of its database because of file permission problems. For instance, on a Red Hat Linux system, slapd runs as user 'ldap'. When slapadd is run as root to create a database from scratch, the contents of <TT>/var/lib/ldap</TT> are created with user and group root and with permission 600, making the contents inaccessible to the slapd server.</P>
+<H3><A NAME="ldap_*: Can\'t chase referral">C.1.3. ldap_*: Can't chase referral</A></H3>
+<P>This is caused by the line</P>
+<PRE>
+ referral ldap://root.openldap.org
+</PRE>
+<P>In <TT>slapd.conf</TT>, it was provided as an example for how to use referrals in the original file. However if your machine is not permanently connected to the Internet, it will fail to find the server, and hence produce an error message.</P>
+<P>To resolve, just place a # in front of line and restart slapd or point it to an available ldap server.</P>
+<P>See also: <EM>ldapadd</EM>(1), <EM>ldapmodify</EM>(1) and <EM>slapd.conf</EM>(5)</P>
+<H3><A NAME="ldap_*: server is unwilling to perform">C.1.4. ldap_*: server is unwilling to perform</A></H3>
+<P>slapd will return an unwilling to perform error if the backend holding the target entry does not support the given operation.</P>
+<P>The password backend is only willing to perform searches. It will return an unwilling to perform error for all other operations.</P>
+<H3><A NAME="ldap_*: Insufficient access">C.1.5. ldap_*: Insufficient access</A></H3>
+<P>This error occurs when server denies the operation due to insufficient access. This is usually caused by binding to a DN with insufficient privileges (or binding anonymously) to perform the operation.</P>
+<P>You can bind as the rootdn/rootpw specified in <EM>slapd.conf</EM>(5) to gain full access. Otherwise, you must bind to an entry which has been granted the appropriate rights through access controls.</P>
+<H3><A NAME="ldap_*: Invalid DN syntax">C.1.6. ldap_*: Invalid DN syntax</A></H3>
+<P>The target (or other) DN of the operation is invalid. This implies that either the string representation of the DN is not in the required form, one of the types in the attribute value assertions is not defined, or one of the values in the attribute value assertions does not conform to the appropriate syntax.</P>
+<H3><A NAME="ldap_*: Referral hop limit exceeded">C.1.7. ldap_*: Referral hop limit exceeded</A></H3>
+<P>This error generally occurs when the client chases a referral which refers itself back to a server it already contacted. The server responds as it did before and the client loops. This loop is detected when the hop limit is exceeded.</P>
+<P>This is most often caused through misconfiguration of the server's default referral. The default referral should not be itself:</P>
+<P>That is, on <A HREF="ldap://myldap/">ldap://myldap/</A> the default referral should not be <A HREF="ldap://myldap/">ldap://myldap/</A> (or any hostname/ip which is equivalent to myldap).</P>
+<H3><A NAME="ldap_*: operations error">C.1.8. ldap_*: operations error</A></H3>
+<P>In some versions of <EM>slapd</EM>(8), <EM>operationsError</EM> was returned instead of other.</P>
+<H3><A NAME="ldap_*: other error">C.1.9. ldap_*: other error</A></H3>
+<P>The other result code indicates an internal error has occurred. While the additional information provided with the result code might provide some hint as to the problem, often one will need to consult the server's log files.</P>
+<H3><A NAME="ldap_add/modify: Invalid syntax">C.1.10. ldap_add/modify: Invalid syntax</A></H3>
+<P>This error is reported when a value of an attribute does not conform to syntax restrictions. Additional information is commonly provided stating which value of which attribute was found to be invalid. Double check this value and other values (the server will only report the first error it finds).</P>
+<P>Common causes include:</P>
+<UL>
+<LI>extraneous whitespace (especially trailing whitespace)
+<LI>improperly encoded characters (LDAPv3 uses UTF-8 encoded Unicode)
+<LI>empty values (few syntaxes allow empty values)</UL>
+<P>For certain syntax, like OBJECT IDENTIFIER (OID), this error can indicate that the OID descriptor (a &quot;short name&quot;) provided is unrecognized. For instance, this error is returned if the <EM>objectClass</EM> value provided is unrecognized.</P>
+<H3><A NAME="ldap_add/modify: Object class violation">C.1.11. ldap_add/modify: Object class violation</A></H3>
+<P>This error is returned with the entry to be added or the entry as modified violates the object class schema rules. Normally additional information is returned the error detailing the violation. Some of these are detailed below.</P>
+<P>Violations related to the entry's attributes:</P>
+<PRE>
+ Attribute not allowed
+</PRE>
+<P>A provided attribute is not allowed by the entry's object class(es).</P>
+<PRE>
+ Missing required attribute
+</PRE>
+<P>An attribute required by the entry's object class(es) was not provided.</P>
+<P>Violations related to the entry's class(es):</P>
+<PRE>
+ Entry has no objectClass attribute
+</PRE>
+<P>The entry did not state which object classes it belonged to.</P>
+<PRE>
+ Unrecognized objectClass
+</PRE>
+<P>One (or more) of the listed objectClass values is not recognized.</P>
+<PRE>
+ No structural object class provided
+</PRE>
+<P>None of the listed objectClass values is structural.</P>
+<PRE>
+ Invalid structural object class chain
+</PRE>
+<P>Two or more structural objectClass values are not in same structural object class chain.</P>
+<PRE>
+ Structural object class modification
+</PRE>
+<P>Modify operation attempts to change the structural class of the entry.</P>
+<PRE>
+ Instantiation of abstract objectClass.
+</PRE>
+<P>An abstract class is not subordinate to any listed structural or auxiliary class.</P>
+<PRE>
+ Invalid structural object class
+</PRE>
+<P>Other structural object class problem.</P>
+<PRE>
+ No structuralObjectClass operational attribute
+</PRE>
+<P>This is commonly returned when a shadow server is provided an entry which does not contain the structuralObjectClass operational attribute.</P>
+<P>Note that the above error messages as well as the above answer assumes basic knowledge of LDAP/X.500 schema.</P>
+<H3><A NAME="ldap_add: No such object">C.1.12. ldap_add: No such object</A></H3>
+<P>The &quot;ldap_add: No such object&quot; error is commonly returned if parent of the entry being added does not exist. Add the parent entry first...</P>
+<P>For example, if you are adding &quot;cn=bob,dc=domain,dc=com&quot; and you get:</P>
+<PRE>
+ ldap_add: No such object
+</PRE>
+<P>The entry &quot;dc=domain,dc=com&quot; likely doesn't exist. You can use ldapsearch to see if does exist:</P>
+<PRE>
+ ldapsearch -b 'dc=domain,dc=com' -s base '(objectclass=*)'
+</PRE>
+<P>If it doesn't, add it. See <A HREF="#A Quick-Start Guide">A Quick-Start Guide</A> for assistance.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>if the entry being added is the same as database suffix, it's parent isn't required. i.e.: if your suffix is &quot;dc=domain,dc=com&quot;, &quot;dc=com&quot; doesn't need to exist to add &quot;dc=domain,dc=com&quot;.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>This error will also occur if you try to add any entry that the server is not configured to hold.</P>
+<P>For example, if your database suffix is &quot;dc=domain,dc=com&quot; and you attempt to add &quot;dc=domain2,dc=com&quot;, &quot;dc=com&quot;, &quot;dc=domain,dc=org&quot;, &quot;o=domain,c=us&quot;, or an other DN in the &quot;dc=domain,dc=com&quot; subtree, the server will return a &quot;No such object&quot; (or referral) error.</P>
+<P><EM>slapd</EM>(8) will generally return &quot;no global superior knowledge&quot; as additional information indicating its return noSuchObject instead of a referral as the server is not configured with knowledge of a global superior server.</P>
+<H3><A NAME="ldap add: invalid structural object class chain">C.1.13. ldap add: invalid structural object class chain</A></H3>
+<P>This particular error refers to the rule about STRUCTURAL objectclasses, which states that an object is of one STRUCTURAL class, the structural class of the object. The object is said to belong to this class, zero or more auxiliaries classes, and their super classes.</P>
+<P>While all of these classes are commonly listed in the objectClass attribute of the entry, one of these classes is the structural object class of the entry. Thus, it is OK for an objectClass attribute to contain inetOrgPerson, organizationalPerson, and person because they inherit one from another to form a single super class chain. That is, inetOrgPerson SUPs organizationPerson SUPs person. On the other hand, it is invalid for both inetOrgPerson and account to be listed in objectClass as inetOrgPerson and account are not part of the same super class chain (unless some other class is also listed with is a subclass of both).</P>
+<P>To resolve this problem, one must determine which class will better serve structural object class for the entry, adding this class to the objectClass attribute (if not already present), and remove any other structural class from the entry's objectClass attribute which is not a super class of the structural object class.</P>
+<P>Which object class is better depends on the particulars of the situation. One generally should consult the documentation for the applications one is using for help in making the determination.</P>
+<H3><A NAME="ldap_add: no structuralObjectClass operational attribute">C.1.14. ldap_add: no structuralObjectClass operational attribute</A></H3>
+<P>ldapadd(1) may error:</P>
+<PRE>
+ adding new entry &quot;uid=XXX,ou=People,o=campus,c=ru&quot;
+ ldap_add: Internal (implementation specific) error (80)
+ additional info: no structuralObjectClass operational attribute
+</PRE>
+<P>when slapd(8) cannot determine, based upon the contents of the objectClass attribute, what the structural class of the object should be.</P>
+<H3><A NAME="ldap_add/modify/rename: Naming violation">C.1.15. ldap_add/modify/rename: Naming violation</A></H3>
+<P>OpenLDAP's slapd checks for naming attributes and distinguished values consistency, according to RFC 4512.</P>
+<P>Naming attributes are those attributeTypes that appear in an entry's RDN; distinguished values are the values of the naming attributes that appear in an entry's RDN, e.g, in</P>
+<PRE>
+ cn=Someone+mail=someone@example.com,dc=example,dc=com
+</PRE>
+<P>the naming attributes are cn and mail, and the distinguished values are Someone and someone@example.com.</P>
+<P>OpenLDAP's slapd checks for consistency when:</P>
+<UL>
+<LI>adding an entry
+<LI>modifying an entry, if the values of the naming attributes are changed
+<LI>renaming an entry, if the RDN of the entry changes</UL>
+<P>Possible causes of error are:</P>
+<UL>
+<LI>the naming attributes are not present in the entry; for example:</UL>
+<PRE>
+ dn: dc=example,dc=com
+ objectClass: organization
+ o: Example
+ # note: &quot;dc: example&quot; is missing
+</PRE>
+<UL>
+<LI>the naming attributes are present in the entry, but in the attributeType definition they are marked as:<UL>
+<LI>collective
+<LI>operational
+<LI>obsolete</UL>
+<LI>the naming attributes are present in the entry, but the distinguished values are not; for example:</UL>
+<PRE>
+ dn: dc=example,dc=com
+ objectClass: domain
+ dc: foobar
+ # note: &quot;dc&quot; is present, but the value is not &quot;example&quot;
+</PRE>
+<UL>
+<LI>the naming attributes are present in the entry, with the distinguished values, but the naming attributes:<UL>
+<LI>do not have an equality field, so equality cannot be asserted
+<LI>the matching rule is not supported (yet)
+<LI>the matching rule is not appropriate</UL>
+<LI>the given distinguished values do not comply with their syntax
+<LI>other errors occurred during the validation/normalization/match process; this is a catchall: look at previous logs for details in case none of the above apply to your case.</UL>
+<P>In any case, make sure that the attributeType definition for the naming attributes contains an appropriate EQUALITY field; or that of the superior, if they are defined based on a superior attributeType (look at the SUP field). See RFC 4512 for details.</P>
+<H3><A NAME="ldap_add/delete/modify/rename: no global superior knowledge">C.1.16. ldap_add/delete/modify/rename: no global superior knowledge</A></H3>
+<P>If the target entry name places is not within any of the databases the server is configured to hold and the server has no knowledge of a global superior, the server will indicate it is unwilling to perform the operation and provide the text &quot;no global superior knowledge&quot; as additional text.</P>
+<P>Likely the entry name is incorrect, or the server is not properly configured to hold the named entry, or, in distributed directory environments, a default referral was not configured.</P>
+<H3><A NAME="ldap_bind: Insufficient access">C.1.17. ldap_bind: Insufficient access</A></H3>
+<P>Current versions of slapd(8) requires that clients have authentication permission to attribute types used for authentication purposes before accessing them to perform the bind operation. As all bind operations are done anonymously (regardless of previous bind success), the auth access must be granted to anonymous.</P>
+<P>In the example ACL below grants the following access:</P>
+<UL>
+<LI>to anonymous users:<UL>
+<LI>permission to authenticate using values of userPassword</UL>
+<LI>to authenticated users:<UL>
+<LI>permission to update (but not read) their userPassword
+<LI>permission to read any object excepting values of userPassword</UL></UL>
+<P>All other access is denied.</P>
+<PRE>
+ access to attr=userPassword
+ by self =w
+ by anonymous auth
+ access *
+ by self write
+ by users read
+</PRE>
+<H3><A NAME="ldap_bind: Invalid credentials">C.1.18. ldap_bind: Invalid credentials</A></H3>
+<P>The error usually occurs when the credentials (password) provided does not match the userPassword held in entry you are binding to.</P>
+<P>The error can also occur when the bind DN specified is not known to the server.</P>
+<P>Check both! In addition to the cases mentioned above you should check if the server denied access to userPassword on selected parts of the directory. In fact, slapd always returns &quot;Invalid credentials&quot; in case of failed bind, regardless of the failure reason, since other return codes could reveal the validity of the user's name.</P>
+<P>To debug access rules defined in slapd.conf, add &quot;ACL&quot; to log level.</P>
+<H3><A NAME="ldap_bind: Protocol error">C.1.19. ldap_bind: Protocol error</A></H3>
+<P>There error is generally occurs when the LDAP version requested by the client is not supported by the server.</P>
+<P>The OpenLDAP Software 2.x server, by default, only accepts version 3 LDAP Bind requests but can be configured to accept a version 2 LDAP Bind request.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>The 2.x server expects LDAPv3 [RFC4510] to be used when the client requests version 3 and expects a limited LDAPv3 variant (basically, LDAPv3 syntax and semantics in an LDAPv2 PDUs) to be used when version 2 is expected.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P>This variant is also sometimes referred to as LDAPv2+, but differs from the U-Mich LDAP variant in a number of ways.</P>
+<H3><A NAME="ldap_modify: cannot modify object class">C.1.20. ldap_modify: cannot modify object class</A></H3>
+<P>This message is commonly returned when attempting to modify the objectClass attribute in a manner inconsistent with the LDAP/X.500 information model. In particular, it commonly occurs when one tries to change the structure of the object from one class to another, for instance, trying to change an 'apple' into a 'pear' or a 'fruit' into a 'pear'.</P>
+<P>Such changes are disallowed by the slapd(8) in accordance with LDAP and X.500 restrictions.</P>
+<H3><A NAME="ldap_sasl_interactive_bind_s: ..">C.1.21. ldap_sasl_interactive_bind_s: ...</A></H3>
+<P>If you intended to bind using a DN and password and get an error from ldap_sasl_interactive_bind_s, you likely forgot to provide a '-x' option to the command. By default, SASL authentication is used. '-x' is necessary to select &quot;simple&quot; authentication.</P>
+<H3><A NAME="ldap_sasl_interactive_bind_s: No such Object">C.1.22. ldap_sasl_interactive_bind_s: No such Object</A></H3>
+<P>This indicates that LDAP SASL authentication function could not read the Root DSE. The error will occur when the server doesn't provide a root DSE. This may be due to access controls.</P>
+<H3><A NAME="ldap_sasl_interactive_bind_s: No such attribute">C.1.23. ldap_sasl_interactive_bind_s: No such attribute</A></H3>
+<P>This indicates that LDAP SASL authentication function could read the Root DSE but it contained no supportedSASLMechanism attribute.</P>
+<P>The supportedSASLmechanism attribute lists mechanisms currently available. The list may be empty because none of the supported mechanisms are currently available. For example, EXTERNAL is listed only if the client has established its identity by authenticating at a lower level (e.g. TLS).</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>the attribute may not be visible due to access controls
+<HR WIDTH="80%" ALIGN="Left"></P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>SASL bind is the default for all OpenLDAP tools, e.g. ldapsearch(1), ldapmodify(1). To force use of &quot;simple&quot; bind, use the &quot;-x&quot; option. Use of &quot;simple&quot; bind is not recommended unless one has adequate confidentiality protection in place (e.g. TLS/SSL, IPSEC).
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H3><A NAME="ldap_sasl_interactive_bind_s: Unknown authentication method">C.1.24. ldap_sasl_interactive_bind_s: Unknown authentication method</A></H3>
+<P>This indicates that none of the SASL authentication supported by the server are supported by the client, or that they are too weak or otherwise inappropriate for use by the client. Note that the default security options disallows the use of certain mechanisms such as ANONYMOUS and PLAIN (without TLS).</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>SASL bind is the default for all OpenLDAP tools. To force use of &quot;simple&quot; bind, use the &quot;-x&quot; option. Use of &quot;simple&quot; bind is not recommended unless one has adequate confidentiality protection in place (e.g. TLS/SSL, IPSEC).
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H3><A NAME="ldap_sasl_interactive_bind_s: Local error (82)">C.1.25. ldap_sasl_interactive_bind_s: Local error (82)</A></H3>
+<P>Apparently not having forward and reverse DNS entries for the LDAP server can result in this error.</P>
+<H3><A NAME="ldap_search: Partial results and referral received">C.1.26. ldap_search: Partial results and referral received</A></H3>
+<P>This error is returned with the server responses to an LDAPv2 search query with both results (zero or more matched entries) and references (referrals to other servers). See also: ldapsearch(1).</P>
+<P>If the updatedn on the replica does not exist, a referral will be returned. It may do this as well if the ACL needs tweaking.</P>
+<H3><A NAME="ldap_start_tls: Operations error">C.1.27. ldap_start_tls: Operations error</A></H3>
+<P>ldapsearch(1) and other tools will return</P>
+<PRE>
+ ldap_start_tls: Operations error (1)
+ additional info: TLS already started
+</PRE>
+<P>When the user (though command line options and/or ldap.conf(5)) has requested TLS (SSL) be started twice. For instance, when specifying both &quot;-H ldaps://server.do.main&quot; and &quot;-ZZ&quot;.</P>
+<H2><A NAME="Other Errors">C.2. Other Errors</A></H2>
+<H3><A NAME="ber_get_next on fd X failed errno=34 (Numerical result out of range)">C.2.1. ber_get_next on fd X failed errno=34 (Numerical result out of range)</A></H3>
+<P>This slapd error generally indicates that the client sent a message that exceeded an administrative limit. See sockbuf_max_incoming and sockbuf_max_incoming_auth configuration directives in slapd.conf(5).</P>
+<H3><A NAME="ber_get_next on fd X failed errno=11 (Resource temporarily unavailable)">C.2.2. ber_get_next on fd X failed errno=11 (Resource temporarily unavailable)</A></H3>
+<P>This message is not indicative of abnormal behavior or error. It simply means that expected data is not yet available from the resource, in this context, a network socket. slapd(8) will process the data once it does becomes available.</P>
+<H3><A NAME="daemon: socket() failed errno=97 (Address family not supported)">C.2.3. daemon: socket() failed errno=97 (Address family not supported)</A></H3>
+<P>This message indicates that the operating system does not support one of the (protocol) address families which slapd(8) was configured to support. Most commonly, this occurs when slapd(8) was configured to support IPv6 yet the operating system kernel wasn't. In such cases, the message can be ignored.</P>
+<H3><A NAME="GSSAPI: gss_acquire_cred: Miscellaneous failure; Permission denied;">C.2.4. GSSAPI: gss_acquire_cred: Miscellaneous failure; Permission denied;</A></H3>
+<P>This message means that slapd is not running as root and, thus, it cannot get its Kerberos 5 key from the keytab, usually file /etc/krb5.keytab.</P>
+<P>A keytab file is used to store keys that are to be used by services or daemons that are started at boot time. It is very important that these secrets are kept beyond reach of intruders.</P>
+<P>That's why the default keytab file is owned by root and protected from being read by others. Do not mess with these permissions, build a different keytab file for slapd instead, and make sure it is owned by the user that slapd runs as.</P>
+<P>To do this, start kadmin, and enter the following commands:</P>
+<PRE>
+ addprinc -randkey ldap/ldap.example.com@EXAMPLE.COM
+ ktadd -k /etc/openldap/ldap.keytab ldap/ldap.example.com@EXAMPLE.COM
+</PRE>
+<P>Then, on the shell, do:</P>
+<PRE>
+ chown ldap:ldap /etc/openldap/ldap.keytab
+ chmod 600 /etc/openldap/ldap.keytab
+</PRE>
+<P>Now you have to tell slapd (well, actually tell the gssapi library in Kerberos 5 that is invoked by Cyrus SASL) where to find the new keytab. You do this by setting the environment variable KRB5_KTNAME like this:</P>
+<PRE>
+ export KRB5_KTNAME=&quot;FILE:/etc/openldap/ldap.keytab&quot;
+</PRE>
+<P>Set that environment variable on the slapd start script (Red Hat users might find /etc/sysconfig/ldap a perfect place).</P>
+<P>This only works if you are using MIT kerberos. It doesn't work with Heimdal, for instance.</P>
+<P>In Heimdal there is a function gsskrb5_register_acceptor_identity() that sets the path of the keytab file you want to use. In Cyrus SASL 2 you can add</P>
+<PRE>
+ keytab: /path/to/file
+</PRE>
+<P>to your application's SASL config file to use this feature. This only works with Heimdal.</P>
+<H3><A NAME="access from unknown denied">C.2.5. access from unknown denied</A></H3>
+<P>This related to TCP wrappers. See hosts_access(5) for more information. in the log file: &quot;access from unknown denied&quot; This related to TCP wrappers. See hosts_access(5) for more information. for example: add the line &quot;slapd: .hosts.you.want.to.allow&quot; in /etc/hosts.allow to get rid of the error.</P>
+<H3><A NAME="ldap_read: want=# error=Resource temporarily unavailable">C.2.6. ldap_read: want=# error=Resource temporarily unavailable</A></H3>
+<P>This message occurs normally. It means that pending data is not yet available from the resource, a network socket. slapd(8) will process the data once it becomes available.</P>
+<H3><A NAME="`make test\' fails">C.2.7. `make test' fails</A></H3>
+<P>Some times, `make test' fails at the very first test with an obscure message like</P>
+<PRE>
+ make test
+ make[1]: Entering directory `/ldap_files/openldap-2.5.0/tests'
+ make[2]: Entering directory `/ldap_files/openldap-2.5.0/tests'
+ Initiating LDAP tests for MDB...
+ Cleaning up test run directory leftover from previous run.
+ Running ./scripts/all...
+ &gt;&gt;&gt;&gt;&gt; Executing all LDAP tests for mdb
+ &gt;&gt;&gt;&gt;&gt; Starting test000-rootdse ...
+ running defines.sh
+ Starting slapd on TCP/IP port 9011...
+ Using ldapsearch to retrieve the root DSE...
+ Waiting 5 seconds for slapd to start...
+ ./scripts/test000-rootdse: line 40: 10607 Segmentation fault $SLAPD -f $CONF1 -h $URI1 -d $LVL $TIMING &gt;$LOG1 2&gt;&amp;1
+ Waiting 5 seconds for slapd to start...
+ Waiting 5 seconds for slapd to start...
+ Waiting 5 seconds for slapd to start...
+ Waiting 5 seconds for slapd to start...
+ Waiting 5 seconds for slapd to start...
+ ./scripts/test000-rootdse: kill: (10607) - No such pid
+ ldap_sasl_bind_s: Can't contact LDAP server (-1)
+ &gt;&gt;&gt;&gt;&gt; Test failed
+ &gt;&gt;&gt;&gt;&gt; ./scripts/test000-rootdse failed (exit 1)
+ make[2]: *** [mdb-yes] Error 1
+ make[2]: Leaving directory `/ldap_files/openldap-2.5.0/tests'
+ make[1]: *** [test] Error 2
+ make[1]: Leaving directory `/ldap_files/openldap-2.5.0/tests'
+ make: *** [test] Error 2
+</PRE>
+<P>or so. Usually, the five lines</P>
+<P>Waiting 5 seconds for slapd to start...</P>
+<P>indicate that slapd didn't start at all.</P>
+<P>In tests/testrun/slapd.1.log there is a full log of what slapd wrote while trying to start. The log level can be increased by setting the environment variable SLAPD_DEBUG to the corresponding value; see loglevel in slapd.conf(5) for the meaning of log levels.</P>
+<P>A typical reason for this behavior is a runtime link problem, i.e. slapd cannot find some dynamic libraries it was linked against. Try running ldd(1) on slapd (for those architectures that support runtime linking).</P>
+<P>There might well be other reasons; the contents of the log file should help clarifying them.</P>
+<P>Tests that fire up multiple instances of slapd typically log to tests/testrun/slapd.&lt;n&gt;.log, with a distinct &lt;n&gt; for each instance of slapd; list tests/testrun/ for possible values of &lt;n&gt;.</P>
+<H3><A NAME="ldap_*: Internal (implementation specific) error (80) - additional info: entry index delete failed">C.2.8. ldap_*: Internal (implementation specific) error (80) - additional info: entry index delete failed</A></H3>
+<P>This seems to be related with wrong ownership of the MDB's dir (/var/lib/ldap) and files. The files must be owned by the user that slapd runs as.</P>
+<PRE>
+ chown -R ldap:ldap /var/lib/ldap
+</PRE>
+<P>fixes it in Debian</P>
+<H3><A NAME="ldap_sasl_interactive_bind_s: Can\'t contact LDAP server (-1)">C.2.9. ldap_sasl_interactive_bind_s: Can't contact LDAP server (-1)</A></H3>
+<P>Using SASL, when a client contacts LDAP server, the slapd service dies immediately and client gets an error :</P>
+<PRE>
+ SASL/GSSAPI authentication started ldap_sasl_interactive_bind_s: Can't contact LDAP server (-1)
+</PRE>
+<P>Then check the slapd service, it stopped.</P>
+<P></P>
+<HR>
+<H1><A NAME="Recommended OpenLDAP Software Dependency Versions">D. Recommended OpenLDAP Software Dependency Versions</A></H1>
+<P>This appendix details the recommended versions of the software that OpenLDAP depends on.</P>
+<P>Please read the <A HREF="#Prerequisite software">Prerequisite software</A> section for more information on the following software dependencies.</P>
+<H2><A NAME="Dependency Versions">D.1. Dependency Versions</A></H2>
+<TABLE CLASS="columns" BORDER ALIGN='Center'>
+<CAPTION ALIGN=top>Table 8.5: OpenLDAP Software Dependency Versions</CAPTION>
+<TR CLASS="heading">
+<TD>
+<STRONG>Feature</STRONG>
+</TD>
+<TD>
+<STRONG>Software</STRONG>
+</TD>
+<TD>
+<STRONG>Version</STRONG>
+</TD>
+</TR>
+<TR>
+<TD>
+&nbsp;<TERM>Transport Layer Security</TERM>:
+</TD>
+<TD>
+<TT>&nbsp;</TT>
+</TD>
+<TD>
+<TT>&nbsp;</TT>
+</TD>
+</TR>
+<TR>
+<TD>
+&nbsp;
+</TD>
+<TD>
+<TT>&nbsp;<A HREF="https://www.openssl.org/">OpenSSL</A></TT>
+</TD>
+<TD>
+<TT>1.1.1+</TT>
+</TD>
+</TR>
+<TR>
+<TD>
+&nbsp;
+</TD>
+<TD>
+<TT>&nbsp;<A HREF="https://gnutls.org/">GnuTLS</A></TT>
+</TD>
+<TD>
+<TT>3.6.0+</TT>
+</TD>
+</TR>
+<TR>
+<TD>
+&nbsp;<TERM>Simple Authentication and Security Layer</TERM>
+</TD>
+<TD>
+<TT>&nbsp;<A HREF="https://www.cyrusimap.org/sasl/">Cyrus SASL</A></TT>
+</TD>
+<TD>
+<TT>2.1.27+</TT>
+</TD>
+</TR>
+<TR>
+<TD>
+&nbsp;<TERM>LDAP Load Balancer</TERM>
+</TD>
+<TD>
+<TT>&nbsp;<A HREF="https://libevent.org/">libevent</A></TT>
+</TD>
+<TD>
+<TT>2.1+</TT>
+</TD>
+</TR>
+<TR>
+<TD>
+Threads:
+</TD>
+<TD>
+<TT>POSIX <EM>pthreads</EM></TT>
+</TD>
+<TD>
+<TT>Version</TT>
+</TD>
+</TR>
+</TABLE>
+
+<P></P>
+<HR>
+<H1><A NAME="Real World OpenLDAP Deployments and Examples">E. Real World OpenLDAP Deployments and Examples</A></H1>
+<P>Examples and discussions</P>
+<P></P>
+<HR>
+<H1><A NAME="OpenLDAP Software Contributions">F. OpenLDAP Software Contributions</A></H1>
+<P>The following sections attempt to summarize the various contributions in OpenLDAP software, as found in <TT>openldap_src/contrib</TT></P>
+<H2><A NAME="Client APIs">F.1. Client APIs</A></H2>
+<P>Intro and discuss</P>
+<H3><A NAME="ldapc++">F.1.1. ldapc++</A></H3>
+<P>Intro and discuss</P>
+<H3><A NAME="ldaptcl">F.1.2. ldaptcl</A></H3>
+<P>Intro and discuss</P>
+<H2><A NAME="Overlays">F.2. Overlays</A></H2>
+<H3><A NAME="acl">F.2.1. acl</A></H3>
+<P>Plugins that implement access rules. Currently only posixGroup, which implements access control based on posixGroup membership.</P>
+<H3><A NAME="addpartial">F.2.2. addpartial</A></H3>
+<P>Treat Add requests as Modify requests if the entry exists.</P>
+<H3><A NAME="allop">F.2.3. allop</A></H3>
+<P>Return operational attributes for root DSE even when not requested, since some clients expect this.</P>
+<H3><A NAME="autogroup">F.2.4. autogroup</A></H3>
+<P>Automated updates of group memberships.</P>
+<H3><A NAME="comp_match">F.2.5. comp_match</A></H3>
+<P>Component Matching rules (RFC 3687).</P>
+<H3><A NAME="denyop">F.2.6. denyop</A></H3>
+<P>Deny selected operations, returning <EM>unwillingToPerform</EM>.</P>
+<H3><A NAME="dsaschema">F.2.7. dsaschema</A></H3>
+<P>Permit loading DSA-specific schema, including operational attrs.</P>
+<H3><A NAME="lastmod">F.2.8. lastmod</A></H3>
+<P>Track the time of the last write operation to a database.</P>
+<H3><A NAME="nops">F.2.9. nops</A></H3>
+<P>Remove null operations, e.g. changing a value to same as before.</P>
+<H3><A NAME="nssov">F.2.10. nssov</A></H3>
+<P>Handle NSS lookup requests through a local Unix Domain socket.</P>
+<H3><A NAME="passwd">F.2.11. passwd</A></H3>
+<P>Support additional password mechanisms.</P>
+<H3><A NAME="proxyOld">F.2.12. proxyOld</A></H3>
+<P>Proxy Authorization compatibility with obsolete internet-draft.</P>
+<H3><A NAME="smbk5pwd">F.2.13. smbk5pwd</A></H3>
+<P>Make the PasswordModify Extended Operation update Kerberos keys and Samba password hashes as well as <EM>userPassword</EM>.</P>
+<H3><A NAME="trace">F.2.14. trace</A></H3>
+<P>Trace overlay invocation.</P>
+<H3><A NAME="usn">F.2.15. usn</A></H3>
+<P>Maintain <EM>usnCreated</EM> and <EM>usnChanged</EM> attrs similar to Microsoft AD.</P>
+<H2><A NAME="Tools">F.3. Tools</A></H2>
+<P>Intro and discuss</P>
+<H3><A NAME="Statistic Logging">F.3.1. Statistic Logging</A></H3>
+<P>statslog</P>
+<H2><A NAME="SLAPI Plugins">F.4. SLAPI Plugins</A></H2>
+<P>Intro and discuss</P>
+<H3><A NAME="addrdnvalues">F.4.1. addrdnvalues</A></H3>
+<P>More</P>
+<P></P>
+<HR>
+<H1><A NAME="Configuration File Examples">G. Configuration File Examples</A></H1>
+<H2><A NAME="slapd.conf">G.1. slapd.conf</A></H2>
+<H2><A NAME="ldap.conf">G.2. ldap.conf</A></H2>
+<H2><A NAME="a-n-other.conf">G.3. a-n-other.conf</A></H2>
+<P></P>
+<HR>
+<H1><A NAME="LDAP Result Codes">H. LDAP Result Codes</A></H1>
+<P>For the purposes of this guide, we have incorporated the standard LDAP result codes from <EM>Appendix A. LDAP Result Codes</EM> of <A HREF="https://www.rfc-editor.org/rfc/rfc4511.txt">RFC4511</A>, a copy of which can be found in <TT>doc/rfc</TT> of the OpenLDAP source code.</P>
+<P>We have expanded the description of each error in relation to the OpenLDAP toolsets. LDAP extensions may introduce extension-specific result codes, which are not part of RFC4511. OpenLDAP returns the result codes related to extensions it implements. Their meaning is documented in the extension they are related to.</P>
+<H2><A NAME="Non-Error Result Codes">H.1. Non-Error Result Codes</A></H2>
+<P>These result codes (called &quot;non-error&quot; result codes) do not indicate an error condition:</P>
+<PRE>
+ success (0),
+ compareFalse (5),
+ compareTrue (6),
+ referral (10), and
+ saslBindInProgress (14).
+</PRE>
+<P>The <EM>success</EM>, <EM>compareTrue</EM>, and <EM>compareFalse</EM> result codes indicate successful completion (and, hence, are referred to as &quot;successful&quot; result codes).</P>
+<P>The <EM>referral</EM> and <EM>saslBindInProgress</EM> result codes indicate the client needs to take additional action to complete the operation.</P>
+<H2><A NAME="Result Codes">H.2. Result Codes</A></H2>
+<P>Existing LDAP result codes are described as follows:</P>
+<H2><A NAME="success (0)">H.3. success (0)</A></H2>
+<P>Indicates the successful completion of an operation.</P>
+<P><HR WIDTH="80%" ALIGN="Left">
+<STRONG>Note: </STRONG>this code is not used with the Compare operation. See <A HREF="#compareFalse (5)">compareFalse (5)</A> and <A HREF="#compareTrue (6)">compareTrue (6)</A>.
+<HR WIDTH="80%" ALIGN="Left"></P>
+<H2><A NAME="operationsError (1)">H.4. operationsError (1)</A></H2>
+<P>Indicates that the operation is not properly sequenced with relation to other operations (of same or different type).</P>
+<P>For example, this code is returned if the client attempts to StartTLS (<A HREF="https://www.rfc-editor.org/rfc/rfc4511.txt">RFC4511</A> Section 4.14) while there are other uncompleted operations or if a TLS layer was already installed.</P>
+<H2><A NAME="protocolError (2)">H.5. protocolError (2)</A></H2>
+<P>Indicates the server received data that is not well-formed.</P>
+<P>For Bind operation only, this code is also used to indicate that the server does not support the requested protocol version.</P>
+<P>For Extended operations only, this code is also used to indicate that the server does not support (by design or configuration) the Extended operation associated with the <EM>requestName</EM>.</P>
+<P>For request operations specifying multiple controls, this may be used to indicate that the server cannot ignore the order of the controls as specified, or that the combination of the specified controls is invalid or unspecified.</P>
+<H2><A NAME="timeLimitExceeded (3)">H.6. timeLimitExceeded (3)</A></H2>
+<P>Indicates that the time limit specified by the client was exceeded before the operation could be completed.</P>
+<H2><A NAME="sizeLimitExceeded (4)">H.7. sizeLimitExceeded (4)</A></H2>
+<P>Indicates that the size limit specified by the client was exceeded before the operation could be completed.</P>
+<H2><A NAME="compareFalse (5)">H.8. compareFalse (5)</A></H2>
+<P>Indicates that the Compare operation has successfully completed and the assertion has evaluated to FALSE or Undefined.</P>
+<H2><A NAME="compareTrue (6)">H.9. compareTrue (6)</A></H2>
+<P>Indicates that the Compare operation has successfully completed and the assertion has evaluated to TRUE.</P>
+<H2><A NAME="authMethodNotSupported (7)">H.10. authMethodNotSupported (7)</A></H2>
+<P>Indicates that the authentication method or mechanism is not supported.</P>
+<H2><A NAME="strongerAuthRequired (8)">H.11. strongerAuthRequired (8)</A></H2>
+<P>Indicates the server requires strong(er) authentication in order to complete the operation.</P>
+<P>When used with the Notice of Disconnection operation, this code indicates that the server has detected that an established security association between the client and server has unexpectedly failed or been compromised.</P>
+<H2><A NAME="referral (10)">H.12. referral (10)</A></H2>
+<P>Indicates that a referral needs to be chased to complete the operation (see <A HREF="https://www.rfc-editor.org/rfc/rfc4511.txt">RFC4511</A> Section 4.1.10).</P>
+<H2><A NAME="adminLimitExceeded (11)">H.13. adminLimitExceeded (11)</A></H2>
+<P>Indicates that an administrative limit has been exceeded.</P>
+<H2><A NAME="unavailableCriticalExtension (12)">H.14. unavailableCriticalExtension (12)</A></H2>
+<P>Indicates a critical control is unrecognized (see <A HREF="https://www.rfc-editor.org/rfc/rfc4511.txt">RFC4511</A> Section 4.1.11).</P>
+<H2><A NAME="confidentialityRequired (13)">H.15. confidentialityRequired (13)</A></H2>
+<P>Indicates that data confidentiality protections are required.</P>
+<H2><A NAME="saslBindInProgress (14)">H.16. saslBindInProgress (14)</A></H2>
+<P>Indicates the server requires the client to send a new bind request, with the same SASL mechanism, to continue the authentication process (see <A HREF="https://www.rfc-editor.org/rfc/rfc4511.txt">RFC4511</A> Section 4.2).</P>
+<H2><A NAME="noSuchAttribute (16)">H.17. noSuchAttribute (16)</A></H2>
+<P>Indicates that the named entry does not contain the specified attribute or attribute value.</P>
+<H2><A NAME="undefinedAttributeType (17)">H.18. undefinedAttributeType (17)</A></H2>
+<P>Indicates that a request field contains an unrecognized attribute description.</P>
+<H2><A NAME="inappropriateMatching (18)">H.19. inappropriateMatching (18)</A></H2>
+<P>Indicates that an attempt was made (e.g., in an assertion) to use a matching rule not defined for the attribute type concerned.</P>
+<H2><A NAME="constraintViolation (19)">H.20. constraintViolation (19)</A></H2>
+<P>Indicates that the client supplied an attribute value that does not conform to the constraints placed upon it by the data model.</P>
+<P>For example, this code is returned when multiple values are supplied to an attribute that has a SINGLE-VALUE constraint.</P>
+<H2><A NAME="attributeOrValueExists (20)">H.21. attributeOrValueExists (20)</A></H2>
+<P>Indicates that the client supplied an attribute or value to be added to an entry, but the attribute or value already exists.</P>
+<H2><A NAME="invalidAttributeSyntax (21)">H.22. invalidAttributeSyntax (21)</A></H2>
+<P>Indicates that a purported attribute value does not conform to the syntax of the attribute.</P>
+<H2><A NAME="noSuchObject (32)">H.23. noSuchObject (32)</A></H2>
+<P>Indicates that the object does not exist in the DIT.</P>
+<H2><A NAME="aliasProblem (33)">H.24. aliasProblem (33)</A></H2>
+<P>Indicates that an alias problem has occurred. For example, the code may used to indicate an alias has been dereferenced that names no object.</P>
+<H2><A NAME="invalidDNSyntax (34)">H.25. invalidDNSyntax (34)</A></H2>
+<P>Indicates that an LDAPDN or RelativeLDAPDN field (e.g., search base, target entry, ModifyDN newrdn, etc.) of a request does not conform to the required syntax or contains attribute values that do not conform to the syntax of the attribute's type.</P>
+<H2><A NAME="aliasDereferencingProblem (36)">H.26. aliasDereferencingProblem (36)</A></H2>
+<P>Indicates that a problem occurred while dereferencing an alias. Typically, an alias was encountered in a situation where it was not allowed or where access was denied.</P>
+<H2><A NAME="inappropriateAuthentication (48)">H.27. inappropriateAuthentication (48)</A></H2>
+<P>Indicates the server requires the client that had attempted to bind anonymously or without supplying credentials to provide some form of credentials.</P>
+<H2><A NAME="invalidCredentials (49)">H.28. invalidCredentials (49)</A></H2>
+<P>Indicates that the provided credentials (e.g., the user's name and password) are invalid.</P>
+<H2><A NAME="insufficientAccessRights (50)">H.29. insufficientAccessRights (50)</A></H2>
+<P>Indicates that the client does not have sufficient access rights to perform the operation.</P>
+<H2><A NAME="busy (51)">H.30. busy (51)</A></H2>
+<P>Indicates that the server is too busy to service the operation.</P>
+<H2><A NAME="unavailable (52)">H.31. unavailable (52)</A></H2>
+<P>Indicates that the server is shutting down or a subsystem necessary to complete the operation is offline.</P>
+<H2><A NAME="unwillingToPerform (53)">H.32. unwillingToPerform (53)</A></H2>
+<P>Indicates that the server is unwilling to perform the operation.</P>
+<H2><A NAME="loopDetect (54)">H.33. loopDetect (54)</A></H2>
+<P>Indicates that the server has detected an internal loop (e.g., while dereferencing aliases or chaining an operation).</P>
+<H2><A NAME="namingViolation (64)">H.34. namingViolation (64)</A></H2>
+<P>Indicates that the entry's name violates naming restrictions.</P>
+<H2><A NAME="objectClassViolation (65)">H.35. objectClassViolation (65)</A></H2>
+<P>Indicates that the entry violates object class restrictions.</P>
+<H2><A NAME="notAllowedOnNonLeaf (66)">H.36. notAllowedOnNonLeaf (66)</A></H2>
+<P>Indicates that the operation is inappropriately acting upon a non-leaf entry.</P>
+<H2><A NAME="notAllowedOnRDN (67)">H.37. notAllowedOnRDN (67)</A></H2>
+<P>Indicates that the operation is inappropriately attempting to remove a value that forms the entry's relative distinguished name.</P>
+<H2><A NAME="entryAlreadyExists (68)">H.38. entryAlreadyExists (68)</A></H2>
+<P>Indicates that the request cannot be fulfilled (added, moved, or renamed) as the target entry already exists.</P>
+<H2><A NAME="objectClassModsProhibited (69)">H.39. objectClassModsProhibited (69)</A></H2>
+<P>Indicates that an attempt to modify the object class(es) of an entry's 'objectClass' attribute is prohibited.</P>
+<P>For example, this code is returned when a client attempts to modify the structural object class of an entry.</P>
+<H2><A NAME="affectsMultipleDSAs (71)">H.40. affectsMultipleDSAs (71)</A></H2>
+<P>Indicates that the operation cannot be performed as it would affect multiple servers (DSAs).</P>
+<H2><A NAME="other (80)">H.41. other (80)</A></H2>
+<P>Indicates the server has encountered an internal error.</P>
+<P></P>
+<HR>
+<H1><A NAME="Glossary">I. Glossary</A></H1>
+<H2><A NAME="Terms">I.1. Terms</A></H2>
+<TABLE CLASS="plain">
+<TR CLASS="heading">
+<TD>
+<STRONG>Term</STRONG>
+</TD>
+<TD>
+<STRONG>Definition</STRONG>
+</TD>
+</TR>
+<TR>
+<TD>
+3DES
+</TD>
+<TD>
+Triple DES
+</TD>
+</TR>
+<TR>
+<TD>
+ABNF
+</TD>
+<TD>
+Augmented Backus-Naur Form
+</TD>
+</TR>
+<TR>
+<TD>
+ACDF
+</TD>
+<TD>
+Access Control Decision Function
+</TD>
+</TR>
+<TR>
+<TD>
+ACE
+</TD>
+<TD>
+ASCII Compatible Encoding
+</TD>
+</TR>
+<TR>
+<TD>
+ASCII
+</TD>
+<TD>
+American Standard Code for Information Interchange
+</TD>
+</TR>
+<TR>
+<TD>
+ACID
+</TD>
+<TD>
+Atomicity, Consistency, Isolation, and Durability
+</TD>
+</TR>
+<TR>
+<TD>
+ACI
+</TD>
+<TD>
+Access Control Information
+</TD>
+</TR>
+<TR>
+<TD>
+ACL
+</TD>
+<TD>
+Access Control List
+</TD>
+</TR>
+<TR>
+<TD>
+AES
+</TD>
+<TD>
+Advance Encryption Standard
+</TD>
+</TR>
+<TR>
+<TD>
+ABI
+</TD>
+<TD>
+Application Binary Interface
+</TD>
+</TR>
+<TR>
+<TD>
+API
+</TD>
+<TD>
+Application Program Interface
+</TD>
+</TR>
+<TR>
+<TD>
+ASN.1
+</TD>
+<TD>
+Abstract Syntax Notation - One
+</TD>
+</TR>
+<TR>
+<TD>
+AVA
+</TD>
+<TD>
+Attribute Value Assertion
+</TD>
+</TR>
+<TR>
+<TD>
+AuthcDN
+</TD>
+<TD>
+Authentication DN
+</TD>
+</TR>
+<TR>
+<TD>
+AuthcId
+</TD>
+<TD>
+Authentication Identity
+</TD>
+</TR>
+<TR>
+<TD>
+AuthzDN
+</TD>
+<TD>
+Authorization DN
+</TD>
+</TR>
+<TR>
+<TD>
+AuthzId
+</TD>
+<TD>
+Authorization Identity
+</TD>
+</TR>
+<TR>
+<TD>
+BCP
+</TD>
+<TD>
+Best Current Practice
+</TD>
+</TR>
+<TR>
+<TD>
+BER
+</TD>
+<TD>
+Basic Encoding Rules
+</TD>
+</TR>
+<TR>
+<TD>
+BNF
+</TD>
+<TD>
+Backus-Naur Form
+</TD>
+</TR>
+<TR>
+<TD>
+C
+</TD>
+<TD>
+The C Programming Language
+</TD>
+</TR>
+<TR>
+<TD>
+CA
+</TD>
+<TD>
+Certificate Authority
+</TD>
+</TR>
+<TR>
+<TD>
+CER
+</TD>
+<TD>
+Canonical Encoding Rules
+</TD>
+</TR>
+<TR>
+<TD>
+CLDAP
+</TD>
+<TD>
+Connection-less LDAP
+</TD>
+</TR>
+<TR>
+<TD>
+CN
+</TD>
+<TD>
+Common Name
+</TD>
+</TR>
+<TR>
+<TD>
+CRAM-MD5
+</TD>
+<TD>
+SASL MD5 Challenge/Response Authentication Mechanism
+</TD>
+</TR>
+<TR>
+<TD>
+CRL
+</TD>
+<TD>
+Certificate Revocation List
+</TD>
+</TR>
+<TR>
+<TD>
+DAP
+</TD>
+<TD>
+Directory Access Protocol
+</TD>
+</TR>
+<TR>
+<TD>
+DC
+</TD>
+<TD>
+Domain Component
+</TD>
+</TR>
+<TR>
+<TD>
+DER
+</TD>
+<TD>
+Distinguished Encoding Rules
+</TD>
+</TR>
+<TR>
+<TD>
+DES
+</TD>
+<TD>
+Data Encryption Standard
+</TD>
+</TR>
+<TR>
+<TD>
+DIB
+</TD>
+<TD>
+Directory Information Base
+</TD>
+</TR>
+<TR>
+<TD>
+DIGEST-MD5
+</TD>
+<TD>
+SASL Digest MD5 Authentication Mechanism
+</TD>
+</TR>
+<TR>
+<TD>
+DISP
+</TD>
+<TD>
+Directory Information Shadowing Protocol
+</TD>
+</TR>
+<TR>
+<TD>
+DIT
+</TD>
+<TD>
+Directory Information Tree
+</TD>
+</TR>
+<TR>
+<TD>
+DNS
+</TD>
+<TD>
+Domain Name System
+</TD>
+</TR>
+<TR>
+<TD>
+DN
+</TD>
+<TD>
+Distinguished Name
+</TD>
+</TR>
+<TR>
+<TD>
+DOP
+</TD>
+<TD>
+Directory Operational Binding Management Protocol
+</TD>
+</TR>
+<TR>
+<TD>
+DSAIT
+</TD>
+<TD>
+DSA Information Tree
+</TD>
+</TR>
+<TR>
+<TD>
+DSA
+</TD>
+<TD>
+Directory System Agent
+</TD>
+</TR>
+<TR>
+<TD>
+DSE
+</TD>
+<TD>
+DSA-specific Entry
+</TD>
+</TR>
+<TR>
+<TD>
+DSP
+</TD>
+<TD>
+Directory System Protocol
+</TD>
+</TR>
+<TR>
+<TD>
+DS
+</TD>
+<TD>
+Draft Standard
+</TD>
+</TR>
+<TR>
+<TD>
+DUA
+</TD>
+<TD>
+Directory User Agent
+</TD>
+</TR>
+<TR>
+<TD>
+EXTERNAL
+</TD>
+<TD>
+SASL External Authentication Mechanism
+</TD>
+</TR>
+<TR>
+<TD>
+FAQ
+</TD>
+<TD>
+Frequently Asked Questions
+</TD>
+</TR>
+<TR>
+<TD>
+FTP
+</TD>
+<TD>
+File Transfer Protocol
+</TD>
+</TR>
+<TR>
+<TD>
+FYI
+</TD>
+<TD>
+For Your Information
+</TD>
+</TR>
+<TR>
+<TD>
+GSER
+</TD>
+<TD>
+Generic String Encoding Rules
+</TD>
+</TR>
+<TR>
+<TD>
+GSS-API
+</TD>
+<TD>
+Generic Security Service Application Program Interface
+</TD>
+</TR>
+<TR>
+<TD>
+GSSAPI
+</TD>
+<TD>
+SASL Kerberos V GSS-API Authentication Mechanism
+</TD>
+</TR>
+<TR>
+<TD>
+I-D
+</TD>
+<TD>
+Internet-Draft
+</TD>
+</TR>
+<TR>
+<TD>
+IA5
+</TD>
+<TD>
+International Alphabet 5
+</TD>
+</TR>
+<TR>
+<TD>
+IDNA
+</TD>
+<TD>
+Internationalized Domain Names in Applications
+</TD>
+</TR>
+<TR>
+<TD>
+IDN
+</TD>
+<TD>
+Internationalized Domain Name
+</TD>
+</TR>
+<TR>
+<TD>
+ID
+</TD>
+<TD>
+Identifier
+</TD>
+</TR>
+<TR>
+<TD>
+IDL
+</TD>
+<TD>
+Index Data Lookups
+</TD>
+</TR>
+<TR>
+<TD>
+IP
+</TD>
+<TD>
+Internet Protocol
+</TD>
+</TR>
+<TR>
+<TD>
+IPC
+</TD>
+<TD>
+Inter-process communication
+</TD>
+</TR>
+<TR>
+<TD>
+IPsec
+</TD>
+<TD>
+Internet Protocol Security
+</TD>
+</TR>
+<TR>
+<TD>
+IPv4
+</TD>
+<TD>
+Internet Protocol, version 4
+</TD>
+</TR>
+<TR>
+<TD>
+IPv6
+</TD>
+<TD>
+Internet Protocol, version 6
+</TD>
+</TR>
+<TR>
+<TD>
+ITS
+</TD>
+<TD>
+Issue Tracking System
+</TD>
+</TR>
+<TR>
+<TD>
+JPEG
+</TD>
+<TD>
+Joint Photographic Experts Group
+</TD>
+</TR>
+<TR>
+<TD>
+Kerberos
+</TD>
+<TD>
+Kerberos Authentication Service
+</TD>
+</TR>
+<TR>
+<TD>
+LBER
+</TD>
+<TD>
+Lightweight BER
+</TD>
+</TR>
+<TR>
+<TD>
+LDAP
+</TD>
+<TD>
+Lightweight Directory Access Protocol
+</TD>
+</TR>
+<TR>
+<TD>
+LDAP Sync
+</TD>
+<TD>
+LDAP Content Synchronization
+</TD>
+</TR>
+<TR>
+<TD>
+LDAPv3
+</TD>
+<TD>
+LDAP, version 3
+</TD>
+</TR>
+<TR>
+<TD>
+LDIF
+</TD>
+<TD>
+LDAP Data Interchange Format
+</TD>
+</TR>
+<TR>
+<TD>
+LMDB
+</TD>
+<TD>
+Lightning Memory-Mapped Database
+</TD>
+</TR>
+<TR>
+<TD>
+MD5
+</TD>
+<TD>
+Message Digest 5
+</TD>
+</TR>
+<TR>
+<TD>
+MDB
+</TD>
+<TD>
+Memory-Mapped Database (Backend)
+</TD>
+</TR>
+<TR>
+<TD>
+MIB
+</TD>
+<TD>
+Management Information Base
+</TD>
+</TR>
+<TR>
+<TD>
+MODDN
+</TD>
+<TD>
+Modify DN
+</TD>
+</TR>
+<TR>
+<TD>
+MODRDN
+</TD>
+<TD>
+Modify RDN
+</TD>
+</TR>
+<TR>
+<TD>
+NSSR
+</TD>
+<TD>
+Non-specific Subordinate Reference
+</TD>
+</TR>
+<TR>
+<TD>
+OID
+</TD>
+<TD>
+Object Identifier
+</TD>
+</TR>
+<TR>
+<TD>
+OSI
+</TD>
+<TD>
+Open Systems Interconnect
+</TD>
+</TR>
+<TR>
+<TD>
+OTP
+</TD>
+<TD>
+One Time Password
+</TD>
+</TR>
+<TR>
+<TD>
+PDU
+</TD>
+<TD>
+Protocol Data Unit
+</TD>
+</TR>
+<TR>
+<TD>
+PEM
+</TD>
+<TD>
+Privacy Enhanced eMail
+</TD>
+</TR>
+<TR>
+<TD>
+PEN
+</TD>
+<TD>
+Private Enterprise Number
+</TD>
+</TR>
+<TR>
+<TD>
+PKCS
+</TD>
+<TD>
+Public Key Cryptosystem
+</TD>
+</TR>
+<TR>
+<TD>
+PKI
+</TD>
+<TD>
+Public Key Infrastructure
+</TD>
+</TR>
+<TR>
+<TD>
+PKIX
+</TD>
+<TD>
+Public Key Infrastructure (X.509)
+</TD>
+</TR>
+<TR>
+<TD>
+PLAIN
+</TD>
+<TD>
+SASL Plaintext Password Authentication Mechanism
+</TD>
+</TR>
+<TR>
+<TD>
+POSIX
+</TD>
+<TD>
+Portable Operating System Interface
+</TD>
+</TR>
+<TR>
+<TD>
+PS
+</TD>
+<TD>
+Proposed Standard
+</TD>
+</TR>
+<TR>
+<TD>
+RDN
+</TD>
+<TD>
+Relative Distinguished Name
+</TD>
+</TR>
+<TR>
+<TD>
+RFC
+</TD>
+<TD>
+Request for Comments
+</TD>
+</TR>
+<TR>
+<TD>
+RPC
+</TD>
+<TD>
+Remote Procedure Call
+</TD>
+</TR>
+<TR>
+<TD>
+RXER
+</TD>
+<TD>
+Robust XML Encoding Rules
+</TD>
+</TR>
+<TR>
+<TD>
+SASL
+</TD>
+<TD>
+Simple Authentication and Security Layer
+</TD>
+</TR>
+<TR>
+<TD>
+SDF
+</TD>
+<TD>
+Simple Document Format
+</TD>
+</TR>
+<TR>
+<TD>
+SDSE
+</TD>
+<TD>
+Shadowed DSE
+</TD>
+</TR>
+<TR>
+<TD>
+SHA1
+</TD>
+<TD>
+Secure Hash Algorithm 1
+</TD>
+</TR>
+<TR>
+<TD>
+SLAPD
+</TD>
+<TD>
+Standalone LDAP Daemon
+</TD>
+</TR>
+<TR>
+<TD>
+SLURPD
+</TD>
+<TD>
+Standalone LDAP Update Replication Daemon
+</TD>
+</TR>
+<TR>
+<TD>
+SMTP
+</TD>
+<TD>
+Simple Mail Transfer Protocol
+</TD>
+</TR>
+<TR>
+<TD>
+SNMP
+</TD>
+<TD>
+Simple Network Management Protocol
+</TD>
+</TR>
+<TR>
+<TD>
+SQL
+</TD>
+<TD>
+Structured Query Language
+</TD>
+</TR>
+<TR>
+<TD>
+SRP
+</TD>
+<TD>
+Secure Remote Password
+</TD>
+</TR>
+<TR>
+<TD>
+SSF
+</TD>
+<TD>
+Security Strength Factor
+</TD>
+</TR>
+<TR>
+<TD>
+SSL
+</TD>
+<TD>
+Secure Socket Layer
+</TD>
+</TR>
+<TR>
+<TD>
+STD
+</TD>
+<TD>
+Internet Standard
+</TD>
+</TR>
+<TR>
+<TD>
+TCP
+</TD>
+<TD>
+Transmission Control Protocol
+</TD>
+</TR>
+<TR>
+<TD>
+TLS
+</TD>
+<TD>
+Transport Layer Security
+</TD>
+</TR>
+<TR>
+<TD>
+UCS
+</TD>
+<TD>
+Universal Multiple-Octet Coded Character Set
+</TD>
+</TR>
+<TR>
+<TD>
+UDP
+</TD>
+<TD>
+User Datagram Protocol
+</TD>
+</TR>
+<TR>
+<TD>
+UID
+</TD>
+<TD>
+User Identifier
+</TD>
+</TR>
+<TR>
+<TD>
+Unicode
+</TD>
+<TD>
+The Unicode Standard
+</TD>
+</TR>
+<TR>
+<TD>
+UNIX
+</TD>
+<TD>
+Unix
+</TD>
+</TR>
+<TR>
+<TD>
+URI
+</TD>
+<TD>
+Uniform Resource Identifier
+</TD>
+</TR>
+<TR>
+<TD>
+URL
+</TD>
+<TD>
+Uniform Resource Locator
+</TD>
+</TR>
+<TR>
+<TD>
+URN
+</TD>
+<TD>
+Uniform Resource Name
+</TD>
+</TR>
+<TR>
+<TD>
+UTF-8
+</TD>
+<TD>
+8-bit UCS/Unicode Transformation Format
+</TD>
+</TR>
+<TR>
+<TD>
+UTR
+</TD>
+<TD>
+Unicode Technical Report
+</TD>
+</TR>
+<TR>
+<TD>
+UUID
+</TD>
+<TD>
+Universally Unique Identifier
+</TD>
+</TR>
+<TR>
+<TD>
+WWW
+</TD>
+<TD>
+World Wide Web
+</TD>
+</TR>
+<TR>
+<TD>
+X.500
+</TD>
+<TD>
+X.500 Directory Services
+</TD>
+</TR>
+<TR>
+<TD>
+X.509
+</TD>
+<TD>
+X.509 Public Key and Attribute Certificate Frameworks
+</TD>
+</TR>
+<TR>
+<TD>
+XED
+</TD>
+<TD>
+XML Enabled Directory
+</TD>
+</TR>
+<TR>
+<TD>
+XER
+</TD>
+<TD>
+XML Encoding Rules
+</TD>
+</TR>
+<TR>
+<TD>
+XML
+</TD>
+<TD>
+Extensible Markup Language
+</TD>
+</TR>
+<TR>
+<TD>
+syncrepl
+</TD>
+<TD>
+LDAP Sync-based Replication
+</TD>
+</TR>
+<TR>
+<TD>
+lloadd
+</TD>
+<TD>
+LDAP Load Balancer
+</TD>
+</TR>
+</TABLE>
+
+<H2><A NAME="Related Organizations">I.2. Related Organizations</A></H2>
+<TABLE CLASS="plain">
+<TR CLASS="heading">
+<TD>
+<STRONG>Name</STRONG>
+</TD>
+<TD>
+<STRONG>Long</STRONG>
+</TD>
+<TD>
+<STRONG>Jump</STRONG>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.ansi.org/">ANSI</A>
+</TD>
+<TD>
+American National Standards Institute
+</TD>
+<TD>
+<A HREF="https://www.ansi.org/">https://www.ansi.org/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.bsigroup.com/en-GB/">BSI</A>
+</TD>
+<TD>
+British Standards Institute
+</TD>
+<TD>
+<A HREF="https://www.bsigroup.com/en-GB/">https://www.bsigroup.com/en-GB/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<ORG>COSINE</ORG>
+</TD>
+<TD>
+Co-operation and Open Systems Interconnection in Europe
+</TD>
+<TD>
+<JUMP>&nbsp;</JUMP>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.cpan.org/">CPAN</A>
+</TD>
+<TD>
+Comprehensive Perl Archive Network
+</TD>
+<TD>
+<A HREF="https://www.cpan.org/">https://www.cpan.org/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.cyrusimap.org/">Cyrus</A>
+</TD>
+<TD>
+Project Cyrus
+</TD>
+<TD>
+<A HREF="https://www.cyrusimap.org/">https://www.cyrusimap.org/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.fsf.org/">FSF</A>
+</TD>
+<TD>
+Free Software Foundation
+</TD>
+<TD>
+<A HREF="https://www.fsf.org/">https://www.fsf.org/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.gnu.org/">GNU</A>
+</TD>
+<TD>
+GNU Not Unix Project
+</TD>
+<TD>
+<A HREF="https://www.gnu.org/">https://www.gnu.org/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.iab.org/">IAB</A>
+</TD>
+<TD>
+Internet Architecture Board
+</TD>
+<TD>
+<A HREF="https://www.iab.org/">https://www.iab.org/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.iana.org/">IANA</A>
+</TD>
+<TD>
+Internet Assigned Numbers Authority
+</TD>
+<TD>
+<A HREF="https://www.iana.org/">https://www.iana.org/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.ieee.org">IEEE</A>
+</TD>
+<TD>
+Institute of Electrical and Electronics Engineers
+</TD>
+<TD>
+<A HREF="https://www.ieee.org">https://www.ieee.org</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.ietf.org/about/groups/iesg/">IESG</A>
+</TD>
+<TD>
+Internet Engineering Steering Group
+</TD>
+<TD>
+<A HREF="https://www.ietf.org/about/groups/iesg/">https://www.ietf.org/about/groups/iesg/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.ietf.org/">IETF</A>
+</TD>
+<TD>
+Internet Engineering Task Force
+</TD>
+<TD>
+<A HREF="https://www.ietf.org/">https://www.ietf.org/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://irtf.org/">IRTF</A>
+</TD>
+<TD>
+Internet Research Task Force
+</TD>
+<TD>
+<A HREF="https://irtf.org/">https://irtf.org/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.iso.org/">ISO</A>
+</TD>
+<TD>
+International Standards Organisation
+</TD>
+<TD>
+<A HREF="https://www.iso.org/">https://www.iso.org/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.internetsociety.org/">ISOC</A>
+</TD>
+<TD>
+Internet Society
+</TD>
+<TD>
+<A HREF="https://www.internetsociety.org/">https://www.internetsociety.org/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.itu.int/">ITU</A>
+</TD>
+<TD>
+International Telecommunication Union
+</TD>
+<TD>
+<A HREF="https://www.itu.int/">https://www.itu.int/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.openldap.org/foundation/">OLF</A>
+</TD>
+<TD>
+OpenLDAP Foundation
+</TD>
+<TD>
+<A HREF="https://www.openldap.org/foundation/">https://www.openldap.org/foundation/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.openldap.org/project/">OLP</A>
+</TD>
+<TD>
+OpenLDAP Project
+</TD>
+<TD>
+<A HREF="https://www.openldap.org/project/">https://www.openldap.org/project/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.openssl.org/">OpenSSL</A>
+</TD>
+<TD>
+OpenSSL Project
+</TD>
+<TD>
+<A HREF="https://www.openssl.org/">https://www.openssl.org/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/">RFC Editor</A>
+</TD>
+<TD>
+RFC Editor
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/">https://www.rfc-editor.org/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.oracle.com/">Oracle</A>
+</TD>
+<TD>
+Oracle Corporation
+</TD>
+<TD>
+<A HREF="https://www.oracle.com/">https://www.oracle.com/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.umich.edu/">UM</A>
+</TD>
+<TD>
+University of Michigan
+</TD>
+<TD>
+<A HREF="https://www.umich.edu/">https://www.umich.edu/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://web.archive.org/web/20160302011357/http://www.umich.edu/~dirsvcs/ldap/ldap.html">UMLDAP</A>
+</TD>
+<TD>
+University of Michigan LDAP Team
+</TD>
+<TD>
+<A HREF="https://web.archive.org/web/20160302011357/http://www.umich.edu/~dirsvcs/ldap/ldap.html">https://web.archive.org/web/20160302011357/http://www.umich.edu/~dirsvcs/ldap/ldap.html</A>
+</TD>
+</TR>
+</TABLE>
+
+<H2><A NAME="Related Products">I.3. Related Products</A></H2>
+<TABLE CLASS="plain">
+<TR CLASS="heading">
+<TD>
+<STRONG>Name</STRONG>
+</TD>
+<TD>
+<STRONG>Jump</STRONG>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://metacpan.org/release/sdf">SDF</A>
+</TD>
+<TD>
+<A HREF="https://metacpan.org/release/sdf">https://metacpan.org/release/sdf</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.cyrusimap.org/">Cyrus</A>
+</TD>
+<TD>
+<A HREF="https://www.cyrusimap.org/">https://www.cyrusimap.org/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.cyrusimap.org/sasl/">Cyrus SASL</A>
+</TD>
+<TD>
+<A HREF="https://www.cyrusimap.org/sasl/">https://www.cyrusimap.org/sasl/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://git-scm.com/">Git</A>
+</TD>
+<TD>
+<A HREF="https://git-scm.com/">https://git-scm.com/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.gnu.org/software/">GNU</A>
+</TD>
+<TD>
+<A HREF="https://www.gnu.org/software/">https://www.gnu.org/software/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://gnutls.org/">GnuTLS</A>
+</TD>
+<TD>
+<A HREF="https://gnutls.org/">https://gnutls.org/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://github.com/heimdal/">Heimdal</A>
+</TD>
+<TD>
+<A HREF="https://github.com/heimdal/">https://github.com/heimdal/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.openldap.org/jldap/">JLDAP</A>
+</TD>
+<TD>
+<A HREF="https://www.openldap.org/jldap/">https://www.openldap.org/jldap/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://libevent.org/">libevent</A>
+</TD>
+<TD>
+<A HREF="https://libevent.org/">https://libevent.org/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://web.mit.edu/kerberos/">MIT Kerberos</A>
+</TD>
+<TD>
+<A HREF="https://web.mit.edu/kerberos/">https://web.mit.edu/kerberos/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.openldap.org/">OpenLDAP</A>
+</TD>
+<TD>
+<A HREF="https://www.openldap.org/">https://www.openldap.org/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.openldap.org/faq/">OpenLDAP FAQ</A>
+</TD>
+<TD>
+<A HREF="https://www.openldap.org/faq/">https://www.openldap.org/faq/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://bugs.openldap.org/">OpenLDAP ITS</A>
+</TD>
+<TD>
+<A HREF="https://bugs.openldap.org/">https://bugs.openldap.org/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.openldap.org/software/">OpenLDAP Software</A>
+</TD>
+<TD>
+<A HREF="https://www.openldap.org/software/">https://www.openldap.org/software/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.openssl.org/">OpenSSL</A>
+</TD>
+<TD>
+<A HREF="https://www.openssl.org/">https://www.openssl.org/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.perl.org/">Perl</A>
+</TD>
+<TD>
+<A HREF="https://www.perl.org/">https://www.perl.org/</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://web.archive.org/web/20160302011357/http://www.umich.edu/~dirsvcs/ldap/ldap.html">UMLDAP</A>
+</TD>
+<TD>
+<A HREF="https://web.archive.org/web/20160302011357/http://www.umich.edu/~dirsvcs/ldap/ldap.html">https://web.archive.org/web/20160302011357/http://www.umich.edu/~dirsvcs/ldap/ldap.html</A>
+</TD>
+</TR>
+</TABLE>
+
+<H2><A NAME="References">I.4. References</A></H2>
+<TABLE CLASS="plain">
+<TR CLASS="heading">
+<TD>
+<STRONG>Reference</STRONG>
+</TD>
+<TD>
+<STRONG>Document</STRONG>
+</TD>
+<TD>
+<STRONG>Status</STRONG>
+</TD>
+<TD>
+<STRONG>Jump</STRONG>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://web.archive.org/web/20170809071245/http://www.umich.edu/~dirsvcs/ldap/doc/guides/slapd/guide.pdf">UM-GUIDE</A>
+</TD>
+<TD>
+The SLAPD and SLURPD Administrators Guide
+</TD>
+<TD>
+O
+</TD>
+<TD>
+<A HREF="https://web.archive.org/web/20170809071245/http://www.umich.edu/~dirsvcs/ldap/doc/guides/slapd/guide.pdf">https://web.archive.org/web/20170809071245/http://www.umich.edu/~dirsvcs/ldap/doc/guides/slapd/guide.pdf</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc2079.txt">RFC2079</A>
+</TD>
+<TD>
+Definition of an X.500 Attribute Type and an Object Class to Hold Uniform Resource Identifiers
+</TD>
+<TD>
+PS
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc2079.txt">https://www.rfc-editor.org/rfc/rfc2079.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc2296.txt">RFC2296</A>
+</TD>
+<TD>
+Use of Language Codes in LDAP
+</TD>
+<TD>
+PS
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc2296.txt">https://www.rfc-editor.org/rfc/rfc2296.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc2307.txt">RFC2307</A>
+</TD>
+<TD>
+An Approach for Using LDAP as a Network Information Service
+</TD>
+<TD>
+X
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc2307.txt">https://www.rfc-editor.org/rfc/rfc2307.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc2589.txt">RFC2589</A>
+</TD>
+<TD>
+Lightweight Directory Access Protocol (v3): Extensions for Dynamic Directory Services
+</TD>
+<TD>
+PS
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc2589.txt">https://www.rfc-editor.org/rfc/rfc2589.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc2798.txt">RFC2798</A>
+</TD>
+<TD>
+Definition of the inetOrgPerson LDAP Object Class
+</TD>
+<TD>
+I
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc2798.txt">https://www.rfc-editor.org/rfc/rfc2798.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc2831.txt">RFC2831</A>
+</TD>
+<TD>
+Using Digest Authentication as a SASL Mechanism
+</TD>
+<TD>
+PS
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc2831.txt">https://www.rfc-editor.org/rfc/rfc2831.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc2849.txt">RFC2849</A>
+</TD>
+<TD>
+The LDAP Data Interchange Format
+</TD>
+<TD>
+PS
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc2849.txt">https://www.rfc-editor.org/rfc/rfc2849.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc3088.txt">RFC3088</A>
+</TD>
+<TD>
+OpenLDAP Root Service
+</TD>
+<TD>
+X
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc3088.txt">https://www.rfc-editor.org/rfc/rfc3088.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc3296.txt">RFC3296</A>
+</TD>
+<TD>
+Named Subordinate References in LDAP
+</TD>
+<TD>
+PS
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc3296.txt">https://www.rfc-editor.org/rfc/rfc3296.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc3384.txt">RFC3384</A>
+</TD>
+<TD>
+Lightweight Directory Access Protocol (version 3) Replication Requirements
+</TD>
+<TD>
+I
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc3384.txt">https://www.rfc-editor.org/rfc/rfc3384.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc3494.txt">RFC3494</A>
+</TD>
+<TD>
+Lightweight Directory Access Protocol version 2 (LDAPv2) to Historic Status
+</TD>
+<TD>
+I
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc3494.txt">https://www.rfc-editor.org/rfc/rfc3494.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4013.txt">RFC4013</A>
+</TD>
+<TD>
+SASLprep: Stringprep Profile for User Names and Passwords
+</TD>
+<TD>
+PS
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4013.txt">https://www.rfc-editor.org/rfc/rfc4013.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4346.txt">RFC4346</A>
+</TD>
+<TD>
+The Transport Layer Security (TLS) Protocol, Version 1.1
+</TD>
+<TD>
+PS
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4346.txt">https://www.rfc-editor.org/rfc/rfc4346.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4422.txt">RFC4422</A>
+</TD>
+<TD>
+Simple Authentication and Security Layer (SASL)
+</TD>
+<TD>
+PS
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4422.txt">https://www.rfc-editor.org/rfc/rfc4422.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4510.txt">RFC4510</A>
+</TD>
+<TD>
+Lightweight Directory Access Protocol (LDAP): Technical Specification Roadmap
+</TD>
+<TD>
+PS
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4510.txt">https://www.rfc-editor.org/rfc/rfc4510.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4511.txt">RFC4511</A>
+</TD>
+<TD>
+Lightweight Directory Access Protocol (LDAP): The Protocol
+</TD>
+<TD>
+PS
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4511.txt">https://www.rfc-editor.org/rfc/rfc4511.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4512.txt">RFC4512</A>
+</TD>
+<TD>
+Lightweight Directory Access Protocol (LDAP): Directory Information Models
+</TD>
+<TD>
+PS
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4512.txt">https://www.rfc-editor.org/rfc/rfc4512.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4513.txt">RFC4513</A>
+</TD>
+<TD>
+Lightweight Directory Access Protocol (LDAP): Authentication Methods and Security Mechanisms
+</TD>
+<TD>
+PS
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4513.txt">https://www.rfc-editor.org/rfc/rfc4513.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4514.txt">RFC4514</A>
+</TD>
+<TD>
+Lightweight Directory Access Protocol (LDAP): String Representation of Distinguished Names
+</TD>
+<TD>
+PS
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4514.txt">https://www.rfc-editor.org/rfc/rfc4514.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4515.txt">RFC4515</A>
+</TD>
+<TD>
+Lightweight Directory Access Protocol (LDAP): String Representation of Search Filters
+</TD>
+<TD>
+PS
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4515.txt">https://www.rfc-editor.org/rfc/rfc4515.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4516.txt">RFC4516</A>
+</TD>
+<TD>
+Lightweight Directory Access Protocol (LDAP): Uniform Resource Locator
+</TD>
+<TD>
+PS
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4516.txt">https://www.rfc-editor.org/rfc/rfc4516.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4517.txt">RFC4517</A>
+</TD>
+<TD>
+Lightweight Directory Access Protocol (LDAP): Syntaxes and Matching Rules
+</TD>
+<TD>
+PS
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4517.txt">https://www.rfc-editor.org/rfc/rfc4517.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4518.txt">RFC4518</A>
+</TD>
+<TD>
+Lightweight Directory Access Protocol (LDAP): Internationalized String Preparation
+</TD>
+<TD>
+PS
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4518.txt">https://www.rfc-editor.org/rfc/rfc4518.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4519.txt">RFC4519</A>
+</TD>
+<TD>
+Lightweight Directory Access Protocol (LDAP): Schema for User Applications
+</TD>
+<TD>
+PS
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4519.txt">https://www.rfc-editor.org/rfc/rfc4519.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4520.txt">RFC4520</A>
+</TD>
+<TD>
+IANA Considerations for LDAP
+</TD>
+<TD>
+BCP
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4520.txt">https://www.rfc-editor.org/rfc/rfc4520.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4533.txt">RFC4533</A>
+</TD>
+<TD>
+The Lightweight Directory Access Protocol (LDAP) Content Synchronization Operation
+</TD>
+<TD>
+X
+</TD>
+<TD>
+<A HREF="https://www.rfc-editor.org/rfc/rfc4533.txt">https://www.rfc-editor.org/rfc/rfc4533.txt</A>
+</TD>
+</TR>
+<TR>
+<TD>
+<A HREF="https://tools.ietf.org/html/draft-chu-ldap-ldapi-00">Chu-LDAPI</A>
+</TD>
+<TD>
+Using LDAP Over IPC Mechanisms
+</TD>
+<TD>
+ID
+</TD>
+<TD>
+<A HREF="https://tools.ietf.org/html/draft-chu-ldap-ldapi-00">https://tools.ietf.org/html/draft-chu-ldap-ldapi-00</A>
+</TD>
+</TR>
+</TABLE>
+
+<P></P>
+<HR>
+<H1><A NAME="Generic configure Instructions">J. Generic configure Instructions</A></H1>
+<PRE>
+Basic Installation
+==================
+
+ These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+ The file `configure.in' is used to create `configure' by a program
+called `autoconf'. You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. You can give `configure'
+initial values for variables by setting them in the environment. Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+ CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+ env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory. After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+ By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=PATH' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on. Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+ CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+ If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+ Use and save the results of the tests in FILE instead of
+ `./config.cache'. Set FILE to `/dev/null' to disable caching, for
+ debugging `configure'.
+
+`--help'
+ Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`--version'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
+
+</PRE>
+<P></P>
+<HR>
+<H1><A NAME="OpenLDAP Software Copyright Notices">K. OpenLDAP Software Copyright Notices</A></H1>
+<H2><A NAME="OpenLDAP Copyright Notice">K.1. OpenLDAP Copyright Notice</A></H2>
+<P>Copyright 1998-2013 The OpenLDAP Foundation.<BR><EM>All rights reserved.</EM></P>
+<P>Redistribution and use in source and binary forms, with or without modification, are permitted <EM>only as authorized</EM> by the <A HREF="#OpenLDAP Public License">OpenLDAP Public License</A>.</P>
+<P>A copy of this license is available in file <TT>LICENSE</TT> in the top-level directory of the distribution or, alternatively, at &lt;<A HREF="http://www.OpenLDAP.org/license.html">http://www.OpenLDAP.org/license.html</A>&gt;.</P>
+<P>OpenLDAP is a registered trademark of the OpenLDAP Foundation.</P>
+<P>Individual files and/or contributed packages may be copyright by other parties and their use subject to additional restrictions.</P>
+<P>This work is derived from the University of Michigan LDAP v3.3 distribution. Information concerning this software is available at &lt;<A HREF="http://www.umich.edu/~dirsvcs/ldap/ldap.html">http://www.umich.edu/~dirsvcs/ldap/ldap.html</A>&gt;.</P>
+<P>This work also contains materials derived from public sources.</P>
+<P>Additional information about OpenLDAP software can be obtained at &lt;<A HREF="http://www.OpenLDAP.org/">http://www.OpenLDAP.org/</A>&gt;.</P>
+<H2><A NAME="Additional Copyright Notices">K.2. Additional Copyright Notices</A></H2>
+<P>Portions Copyright 1998-2013 Kurt D. Zeilenga.<BR>Portions Copyright 1998-2006 Net Boolean Incorporated.<BR>Portions Copyright 2001-2006 IBM Corporation.<BR><EM>All rights reserved.</EM></P>
+<P>Redistribution and use in source and binary forms, with or without modification, are permitted only as authorized by the <A HREF="#OpenLDAP Public License">OpenLDAP Public License</A>.</P>
+<P>Portions Copyright 1999-2008 Howard Y.H. Chu.<BR>Portions Copyright 1999-2008 Symas Corporation.<BR>Portions Copyright 1998-2003 Hallvard B. Furuseth.<BR>Portions Copyright 2007-2011 Gavin Henry.<BR>Portions Copyright 2007-2011 Suretec Systems Limited.<BR><EM>All rights reserved.</EM></P>
+<P>Redistribution and use in source and binary forms, with or without modification, are permitted provided that this notice is preserved. The names of the copyright holders may not be used to endorse or promote products derived from this software without their specific prior written permission. This software is provided ``as is'' without express or implied warranty.</P>
+<H2><A NAME="University of Michigan Copyright Notice">K.3. University of Michigan Copyright Notice</A></H2>
+<P>Portions Copyright 1992-1996 Regents of the University of Michigan.<BR><EM>All rights reserved.</EM></P>
+<P>Redistribution and use in source and binary forms are permitted provided that this notice is preserved and that due credit is given to the University of Michigan at Ann Arbor. The name of the University may not be used to endorse or promote products derived from this software without specific prior written permission. This software is provided ``as is'' without express or implied warranty.</P>
+<P></P>
+<HR>
+<H1><A NAME="OpenLDAP Public License">L. OpenLDAP Public License</A></H1>
+<PRE>
+The OpenLDAP Public License
+ Version 2.8, 17 August 2003
+
+Redistribution and use of this software and associated documentation
+(&quot;Software&quot;), with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Redistributions in source form must retain copyright statements
+ and notices,
+
+2. Redistributions in binary form must reproduce applicable copyright
+ statements and notices, this list of conditions, and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution, and
+
+3. Redistributions must contain a verbatim copy of this document.
+
+The OpenLDAP Foundation may revise this license from time to time.
+Each revision is distinguished by a version number. You may use
+this Software under terms of this license revision or under the
+terms of any subsequent revision of the license.
+
+THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS
+CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S)
+OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+The names of the authors and copyright holders must not be used in
+advertising or otherwise to promote the sale, use or other dealing
+in this Software without specific, written prior permission. Title
+to copyright in this Software shall at all times remain with copyright
+holders.
+
+OpenLDAP is a registered trademark of the OpenLDAP Foundation.
+
+Copyright 1999-2003 The OpenLDAP Foundation, Redwood City,
+California, USA. All Rights Reserved. Permission to copy and
+distribute verbatim copies of this document is granted.
+</PRE>
+</DIV>
+<DIV CLASS="footer">
+<HR>
+<DIV CLASS="navigate">
+<P ALIGN="Center"><A HREF="https://www.openldap.org/">Home</A> | <A HREF="../index.html">Catalog</A></P>
+</DIV>
+<P>
+<FONT COLOR="#808080" FACE="Arial,Verdana,Helvetica" SIZE="1"><B>
+________________<BR>
+<SMALL>&copy; Copyright 2011-2022, <A HREF="https://www.OpenLDAP.org/foundation/">OpenLDAP Foundation</A>, <A HREF="mailto:info@OpenLDAP.org">info@OpenLDAP.org</A></SMALL></B></FONT>
+
+</DIV>
+
+</BODY>
+</HTML>
diff --git a/doc/guide/admin/guide.sdf b/doc/guide/admin/guide.sdf
new file mode 100644
index 0000000..9fa4597
--- /dev/null
+++ b/doc/guide/admin/guide.sdf
@@ -0,0 +1,8 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+#
+# guide.sdf
+#
+
+!include "master.sdf"
diff --git a/doc/guide/admin/index.sdf b/doc/guide/admin/index.sdf
new file mode 100644
index 0000000..01d5613
--- /dev/null
+++ b/doc/guide/admin/index.sdf
@@ -0,0 +1,8 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+#
+# index.sdf
+#
+
+!include "master.sdf"
diff --git a/doc/guide/admin/install.sdf b/doc/guide/admin/install.sdf
new file mode 100644
index 0000000..a8e4f69
--- /dev/null
+++ b/doc/guide/admin/install.sdf
@@ -0,0 +1,245 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: Building and Installing OpenLDAP Software
+
+This chapter details how to build and install the {{PRD:OpenLDAP}}
+Software package including {{slapd}}(8), the Standalone {{TERM:LDAP}}
+Daemon. Building and installing OpenLDAP Software requires several
+steps: installing prerequisite software, configuring OpenLDAP
+Software itself, making, and finally installing. The following
+sections describe this process in detail.
+
+
+H2: Obtaining and Extracting the Software
+
+You can obtain OpenLDAP Software from the project's download
+page at {{URL: http://www.openldap.org/software/download/}} or
+directly from the project's {{TERM:FTP}} service at
+{{URL: ftp://ftp.openldap.org/pub/OpenLDAP/}}.
+
+The project makes available two series of packages for {{general
+use}}. The project makes {{releases}} as new features and bug fixes
+come available. Though the project takes steps to improve stability
+of these releases, it is common for problems to arise only after
+{{release}}. The {{stable}} release is the latest {{release}} which
+has demonstrated stability through general use.
+
+Users of OpenLDAP Software can choose, depending on their desire
+for the {{latest features}} versus {{demonstrated stability}}, the
+most appropriate series to install.
+
+After downloading OpenLDAP Software, you need to extract the
+distribution from the compressed archive file and change your working
+directory to the top directory of the distribution:
+
+.{{EX:gunzip -c openldap-VERSION.tgz | tar xf -}}
+.{{EX:cd openldap-VERSION}}
+
+You'll have to replace {{EX:VERSION}} with the version name of
+the release.
+
+You should now review the {{F:COPYRIGHT}}, {{F:LICENSE}}, {{F:README}}
+and {{F:INSTALL}} documents provided with the distribution. The
+{{F:COPYRIGHT}} and {{F:LICENSE}} provide information on acceptable
+use, copying, and limitation of warranty of OpenLDAP Software. The
+{{F:README}} and {{F:INSTALL}} documents provide detailed information
+on prerequisite software and installation procedures.
+
+
+H2: Prerequisite software
+
+OpenLDAP Software relies upon a number of software packages distributed
+by third parties. Depending on the features you intend to use, you
+may have to download and install a number of additional software
+packages. This section details commonly needed third party software
+packages you might have to install. However, for an up-to-date
+prerequisite information, the {{F:README}} document should be
+consulted. Note that some of these third party packages may depend
+on additional software packages. Install each package per the
+installation instructions provided with it.
+
+
+H3: {{TERM[expand]TLS}}
+
+OpenLDAP clients and servers require installation of {{PRD:OpenSSL}}
+ or {{PRD:GnuTLS}}
+{{TERM:TLS}} libraries to provide {{TERM[expand]TLS}} services. Though
+some operating systems may provide these libraries as part of the
+base system or as an optional software component, OpenSSL and GnuTLS
+often require separate installation.
+
+OpenSSL is available from {{URL: http://www.openssl.org/}}.
+GnuTLS is available from {{URL: http://www.gnu.org/software/gnutls/}}.
+
+OpenLDAP Software will not be fully LDAPv3 compliant unless OpenLDAP's
+{{EX:configure}} detects a usable TLS library.
+
+
+H3: {{TERM[expand]SASL}}
+
+OpenLDAP clients and servers require installation of {{PRD:Cyrus SASL}}
+libraries to provide {{TERM[expand]SASL}} services. Though
+some operating systems may provide this library as part of the
+base system or as an optional software component, Cyrus SASL
+often requires separate installation.
+
+Cyrus SASL is available from
+{{URL:http://asg.web.cmu.edu/sasl/sasl-library.html}}.
+Cyrus SASL will make use of OpenSSL and Kerberos/GSSAPI libraries
+if preinstalled.
+
+OpenLDAP Software will not be fully LDAPv3 compliant unless OpenLDAP's
+configure detects a usable Cyrus SASL installation.
+
+
+H3: {{TERM[expand]Kerberos}}
+
+OpenLDAP clients and servers support {{TERM:Kerberos}} authentication
+services. In particular, OpenLDAP supports the Kerberos V
+{{TERM:GSS-API}} {{TERM:SASL}} authentication mechanism known as
+the {{TERM:GSSAPI}} mechanism. This feature requires, in addition to
+Cyrus SASL libraries, either {{PRD:Heimdal}} or {{PRD:MIT Kerberos}}
+V libraries.
+
+Heimdal Kerberos is available from {{URL:https://github.com/heimdal/heimdal/}}.
+MIT Kerberos is available from {{URL:http://web.mit.edu/kerberos/www/}}.
+
+Use of strong authentication services, such as those provided by
+Kerberos, is highly recommended.
+
+
+
+H3: Database Software
+
+OpenLDAP's {{slapd}}(8) {{TERM:MDB}} primary database backend uses the {{TERM:LMDB}}
+software included with the OpenLDAP source. There is no need to download any
+additional software to have {{MDB}} support.
+
+
+
+H3: Threads
+
+OpenLDAP is designed to take advantage of threads. OpenLDAP
+supports POSIX {{pthreads}}, NT threads and a number of
+other varieties. {{EX:configure}} will complain if it cannot
+find a suitable thread subsystem. If this occurs, please
+consult the {{F:Software|Installation|Platform Hints}} section
+of the OpenLDAP FAQ {{URL: http://www.openldap.org/faq/}}.
+
+
+H3: TCP Wrappers
+
+{{slapd}}(8) supports TCP Wrappers (IP level access control filters)
+if preinstalled. Use of TCP Wrappers or other IP-level access
+filters (such as those provided by an IP-level firewall) is recommended
+for servers containing non-public information.
+
+
+H2: Running configure
+
+Now you should probably run the {{EX:configure}} script with the
+{{EX:--help}} option.
+This will give you a list of options that you can change when building
+OpenLDAP. Many of the features of OpenLDAP can be enabled or disabled
+using this method.
+!if 0
+Please see the appendix for a more detailed list of configure options,
+and their usage.
+!endif
+> ./configure --help
+
+The {{EX:configure}} script also looks for certain variables
+on the command line and in the environment. These include:
+
+!block table; align=Center; coltags="EX,N"; title="Table 4.1: Variables"
+Variable Description
+CC Specify alternative C Compiler
+CFLAGS Specify additional compiler flags
+CPPFLAGS Specify C Preprocessor flags
+LDFLAGS Specify linker flags
+LIBS Specify additional libraries
+!endblock
+
+Now run the configure script with any desired configuration options or
+variables.
+
+> ./configure [options] [variable=value ...]
+
+As an example, let's assume that we want to install OpenLDAP with
+MDB backend and TCP Wrappers support. By default, MDB
+is enabled and TCP Wrappers is not. So, we just need to specify
+{{EX:--enable-wrappers}} to include TCP Wrappers support:
+
+> ./configure --enable-wrappers
+
+However, this will fail to locate dependent software not
+installed in system directories. For example, if TCP Wrappers
+headers and libraries are installed in {{F:/usr/local/include}}
+and {{F:/usr/local/lib}} respectively, the {{EX:configure}}
+script should typically be called as follows:
+
+> ./configure --enable-wrappers \
+> CPPFLAGS="-I/usr/local/include" \
+> LDFLAGS="-L/usr/local/lib -Wl,-rpath,/usr/local/lib"
+
+The {{EX:configure}} script will normally auto-detect appropriate
+settings. If you have problems at this stage, consult any platform
+specific hints and check your {{EX:configure}} options, if any.
+
+
+H2: Building the Software
+
+Once you have run the {{EX:configure}} script the last line of output
+should be:
+> Please "make depend" to build dependencies
+
+If the last line of output does not match, {{EX:configure}} has failed,
+and you will need to review its output to determine what went wrong.
+You should not proceed until {{EX:configure}} completes successfully.
+
+To build dependencies, run:
+> make depend
+
+Now build the software, this step will actually compile OpenLDAP.
+> make
+
+You should examine the output of this command carefully to make sure
+everything is built correctly. Note that this command builds the LDAP
+libraries and associated clients as well as {{slapd}}(8).
+
+
+H2: Testing the Software
+
+Once the software has been properly configured and successfully
+made, you should run the test suite to verify the build.
+
+> make test
+
+Tests which apply to your configuration will run and they should pass.
+Some tests, such as the replication test, may be skipped if not supported
+by your configuration.
+
+
+H2: Installing the Software
+
+Once you have successfully tested the software, you are ready to
+install it. You will need to have write permission to the installation
+directories you specified when you ran configure. By default
+OpenLDAP Software is installed in {{F:/usr/local}}. If you changed
+this setting with the {{EX:--prefix}} configure option, it will be
+installed in the location you provided.
+
+Typically, the installation requires {{super-user}} privileges.
+From the top level OpenLDAP source directory, type:
+
+> su root -c 'make install'
+
+and enter the appropriate password when requested.
+
+You should examine the output of this command carefully to make sure
+everything is installed correctly. You will find the configuration files
+for {{slapd}}(8) in {{F:/usr/local/etc/openldap}} by default. See the
+chapter {{SECT:Configuring slapd}} for additional information.
+
diff --git a/doc/guide/admin/intro.sdf b/doc/guide/admin/intro.sdf
new file mode 100644
index 0000000..8417c1e
--- /dev/null
+++ b/doc/guide/admin/intro.sdf
@@ -0,0 +1,465 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+H1: Introduction to OpenLDAP Directory Services
+
+This document describes how to build, configure, and operate
+{{PRD:OpenLDAP}} Software to provide directory services. This
+includes details on how to configure and run the Standalone
+{{TERM:LDAP}} Daemon, {{slapd}}(8). It is intended for new and
+experienced administrators alike. This section provides a basic
+introduction to directory services and, in particular, the directory
+services provided by {{slapd}}(8). This introduction is only
+intended to provide enough information so one might get started
+learning about {{TERM:LDAP}}, {{TERM:X.500}}, and directory services.
+
+
+H2: What is a directory service?
+
+A directory is a specialized database specifically designed for
+searching and browsing, in additional to supporting basic lookup
+and update functions.
+
+Note: A directory is defined by some as merely a database optimized
+for read access. This definition, at best, is overly simplistic.
+
+Directories tend to contain descriptive, attribute-based information
+and support sophisticated filtering capabilities. Directories
+generally do not support complicated transaction or roll-back schemes
+found in database management systems designed for handling high-volume
+complex updates. Directory updates are typically simple all-or-nothing
+changes, if they are allowed at all. Directories are generally
+tuned to give quick response to high-volume lookup or search
+operations. They may have the ability to replicate information
+widely in order to increase availability and reliability, while
+reducing response time. When directory information is replicated,
+temporary inconsistencies between the consumers may be okay, as long
+as inconsistencies are resolved in a timely manner.
+
+There are many different ways to provide a directory service.
+Different methods allow different kinds of information to be stored
+in the directory, place different requirements on how that information
+can be referenced, queried and updated, how it is protected from
+unauthorized access, etc. Some directory services are {{local}},
+providing service to a restricted context (e.g., the finger service
+on a single machine). Other services are global, providing service
+to a much broader context (e.g., the entire Internet). Global
+services are usually {{distributed}}, meaning that the data they
+contain is spread across many machines, all of which cooperate to
+provide the directory service. Typically a global service defines
+a uniform {{namespace}} which gives the same view of the data no
+matter where you are in relation to the data itself.
+
+A web directory, such as provided by the {{Curlie Project}}
+<{{URL:https://curlie.org}}>, is a good example of a directory service.
+These services catalog web pages and are specifically designed to
+support browsing and searching.
+
+While some consider the Internet {{TERM[expand]DNS}} (DNS) is an
+example of a globally distributed directory service, DNS is not
+browsable nor searchable. It is more properly described as a
+globally distributed {{lookup}} service.
+
+
+H2: What is LDAP?
+
+{{TERM:LDAP}} stands for {{TERM[expand]LDAP}}. As the name suggests,
+it is a lightweight protocol for accessing directory services,
+specifically {{TERM:X.500}}-based directory services. LDAP runs
+over {{TERM:TCP}}/{{TERM:IP}} or other connection oriented transfer
+services. LDAP is an {{ORG:IETF}} Standard Track protocol and is
+specified in "Lightweight Directory Access Protocol (LDAP) Technical
+Specification Road Map" {{REF:RFC4510}}.
+
+This section gives an overview of LDAP from a user's perspective.
+
+{{What kind of information can be stored in the directory?}} The
+LDAP information model is based on {{entries}}. An entry is a
+collection of attributes that has a globally-unique {{TERM[expand]DN}}
+(DN). The DN is used to refer to the entry unambiguously. Each of
+the entry's attributes has a {{type}} and one or more {{values}}.
+The types are typically mnemonic strings, like "{{EX:cn}}" for
+common name, or "{{EX:mail}}" for email address. The syntax of
+values depend on the attribute type. For example, a {{EX:cn}}
+attribute might contain the value {{EX:Babs Jensen}}. A {{EX:mail}}
+attribute might contain the value "{{EX:babs@example.com}}". A
+{{EX:jpegPhoto}} attribute would contain a photograph in the
+{{TERM:JPEG}} (binary) format.
+
+{{How is the information arranged?}} In LDAP, directory entries
+are arranged in a hierarchical tree-like structure. Traditionally,
+this structure reflected the geographic and/or organizational
+boundaries. Entries representing countries appear at the top of
+the tree. Below them are entries representing states and national
+organizations. Below them might be entries representing organizational
+units, people, printers, documents, or just about anything else
+you can think of. Figure 1.1 shows an example LDAP directory tree
+using traditional naming.
+
+!import "intro_tree.png"; align="center"; \
+ title="LDAP directory tree (traditional naming)"
+FT[align="Center"] Figure 1.1: LDAP directory tree (traditional naming)
+
+The tree may also be arranged based upon Internet domain names.
+This naming approach is becoming increasing popular as it allows
+for directory services to be located using the {{DNS}}.
+Figure 1.2 shows an example LDAP directory tree using domain-based
+naming.
+
+!import "intro_dctree.png"; align="center"; \
+ title="LDAP directory tree (Internet naming)"
+FT[align="Center"] Figure 1.2: LDAP directory tree (Internet naming)
+
+In addition, LDAP allows you to control which attributes are required
+and allowed in an entry through the use of a special attribute
+called {{EX:objectClass}}. The values of the {{EX:objectClass}}
+attribute determine the {{schema}} rules the entry must obey.
+
+{{How is the information referenced?}} An entry is referenced by
+its distinguished name, which is constructed by taking the name of
+the entry itself (called the {{TERM[expand]RDN}} or RDN) and
+concatenating the names of its ancestor entries. For example, the
+entry for Barbara Jensen in the Internet naming example above has
+an RDN of {{EX:uid=babs}} and a DN of
+{{EX:uid=babs,ou=People,dc=example,dc=com}}. The full DN format is
+described in {{REF:RFC4514}}, "LDAP: String Representation of
+Distinguished Names."
+
+{{How is the information accessed?}} LDAP defines operations for
+interrogating and updating the directory. Operations are provided
+for adding and deleting an entry from the directory, changing an
+existing entry, and changing the name of an entry. Most of the
+time, though, LDAP is used to search for information in the directory.
+The LDAP search operation allows some portion of the directory to
+be searched for entries that match some criteria specified by a
+search filter. Information can be requested from each entry that
+matches the criteria.
+
+For example, you might want to search the entire directory subtree
+at and below {{EX:dc=example,dc=com}} for people with the name
+{{EX:Barbara Jensen}}, retrieving the email address of each entry
+found. LDAP lets you do this easily. Or you might want to search
+the entries directly below the {{EX:st=California,c=US}} entry for
+organizations with the string {{EX:Acme}} in their name, and that
+have a fax number. LDAP lets you do this too. The next section
+describes in more detail what you can do with LDAP and how it might
+be useful to you.
+
+{{How is the information protected from unauthorized access?}} Some
+directory services provide no protection, allowing anyone to see
+the information. LDAP provides a mechanism for a client to authenticate,
+or prove its identity to a directory server, paving the way for
+rich access control to protect the information the server contains.
+LDAP also supports data security (integrity and confidentiality)
+services.
+
+
+H2: When should I use LDAP?
+
+This is a very good question. In general, you should use a Directory
+server when you require data to be centrally managed, stored and accessible via
+standards based methods.
+
+Some common examples found throughout the industry are, but not limited to:
+
+* Machine Authentication
+* User Authentication
+* User/System Groups
+* Address book
+* Organization Representation
+* Asset Tracking
+* Telephony Information Store
+* User resource management
+* E-mail address lookups
+* Application Configuration store
+* PBX Configuration store
+* etc.....
+
+There are various {{SECT:Distributed Schema Files}} that are standards based, but
+you can always create your own {{SECT:Schema Specification}}.
+
+There are always new ways to use a Directory and apply LDAP principles to address
+certain problems, therefore there is no simple answer to this question.
+
+If in doubt, join the general LDAP forum for non-commercial discussions and
+information relating to LDAP at:
+{{URL:http://www.umich.edu/~dirsvcs/ldap/mailinglist.html}} and ask
+
+H2: When should I not use LDAP?
+
+When you start finding yourself bending the directory to do what you require,
+maybe a redesign is needed. Or if you only require one application to use and
+manipulate your data (for discussion of LDAP vs RDBMS, please read the
+{{SECT:LDAP vs RDBMS}} section).
+
+It will become obvious when LDAP is the right tool for the job.
+
+
+H2: How does LDAP work?
+
+LDAP utilizes a {{client-server model}}. One or more LDAP servers
+contain the data making up the directory information tree ({{TERM:DIT}}).
+The client connects to servers and asks it a question. The server
+responds with an answer and/or with a pointer to where the client
+can get additional information (typically, another LDAP server).
+No matter which LDAP server a client connects to, it sees the same
+view of the directory; a name presented to one LDAP server references
+the same entry it would at another LDAP server. This is an important
+feature of a global directory service.
+
+
+H2: What about X.500?
+
+Technically, {{TERM:LDAP}} is a directory access protocol to an
+{{TERM:X.500}} directory service, the {{TERM:OSI}} directory service.
+Initially, LDAP clients accessed gateways to the X.500 directory service.
+This gateway ran LDAP between the client and gateway and X.500's
+{{TERM[expand]DAP}} ({{TERM:DAP}}) between the gateway and the
+X.500 server. DAP is a heavyweight protocol that operates over a
+full OSI protocol stack and requires a significant amount of
+computing resources. LDAP is designed to operate over
+{{TERM:TCP}}/{{TERM:IP}} and provides most of the functionality of
+DAP at a much lower cost.
+
+While LDAP is still used to access X.500 directory service via
+gateways, LDAP is now more commonly directly implemented in X.500
+servers.
+
+The Standalone LDAP Daemon, or {{slapd}}(8), can be viewed as a
+{{lightweight}} X.500 directory server. That is, it does not
+implement the X.500's DAP nor does it support the complete X.500
+models.
+
+If you are already running a X.500 DAP service and you want to
+continue to do so, you can probably stop reading this guide. This
+guide is all about running LDAP via {{slapd}}(8), without running
+X.500 DAP. If you are not running X.500 DAP, want to stop running
+X.500 DAP, or have no immediate plans to run X.500 DAP, read on.
+
+It is possible to replicate data from an LDAP directory server to
+a X.500 DAP {{TERM:DSA}}. This requires an LDAP/DAP gateway.
+OpenLDAP Software does not include such a gateway.
+
+
+H2: What is the difference between LDAPv2 and LDAPv3?
+
+LDAPv3 was developed in the late 1990's to replace LDAPv2.
+LDAPv3 adds the following features to LDAP:
+
+ * Strong authentication and data security services via {{TERM:SASL}}
+ * Certificate authentication and data security services via {{TERM:TLS}} (SSL)
+ * Internationalization through the use of Unicode
+ * Referrals and Continuations
+ * Schema Discovery
+ * Extensibility (controls, extended operations, and more)
+
+LDAPv2 is historic ({{REF:RFC3494}}). As most {{so-called}} LDAPv2
+implementations (including {{slapd}}(8)) do not conform to the
+LDAPv2 technical specification, interoperability amongst
+implementations claiming LDAPv2 support is limited. As LDAPv2
+differs significantly from LDAPv3, deploying both LDAPv2 and LDAPv3
+simultaneously is quite problematic. LDAPv2 should be avoided.
+LDAPv2 is disabled by default.
+
+
+H2: LDAP vs RDBMS
+
+This question is raised many times, in different forms. The most common,
+however, is: {{Why doesn't OpenLDAP use a relational database management
+ system (RDBMS) instead of an embedded key/value store like LMDB?}} In
+general, expecting that the sophisticated algorithms implemented by
+commercial-grade RDBMS would make {{OpenLDAP}} be faster or somehow better
+and, at the same time, permitting sharing of data with other applications.
+
+The short answer is that use of an embedded database and custom indexing system
+allows OpenLDAP to provide greater performance and scalability without loss of
+reliability. OpenLDAP uses {{TERM:LMDB}} concurrent / transactional
+database software.
+
+Now for the long answer. We are all confronted all the time with the choice
+RDBMSes vs. directories. It is a hard choice and no simple answer exists.
+
+It is tempting to think that having a RDBMS backend to the directory solves all
+problems. However, it is a pig. This is because the data models are very
+different. Representing directory data with a relational database is going to
+require splitting data into multiple tables.
+
+Think for a moment about the person objectclass. Its definition requires
+attribute types objectclass, sn and cn and allows attribute types userPassword,
+telephoneNumber, seeAlso and description. All of these attributes are multivalued,
+so a normalization requires putting each attribute type in a separate table.
+
+Now you have to decide on appropriate keys for those tables. The primary key
+might be a combination of the DN, but this becomes rather inefficient on most
+database implementations.
+
+The big problem now is that accessing data from one entry requires seeking on
+different disk areas. On some applications this may be OK but in many
+applications performance suffers.
+
+The only attribute types that can be put in the main table entry are those that
+are mandatory and single-value. You may add also the optional single-valued
+attributes and set them to NULL or something if not present.
+
+But wait, the entry can have multiple objectclasses and they are organized in
+an inheritance hierarchy. An entry of objectclass organizationalPerson now has
+the attributes from person plus a few others and some formerly optional attribute
+types are now mandatory.
+
+What to do? Should we have different tables for the different objectclasses?
+This way the person would have an entry on the person table, another on
+organizationalPerson, etc. Or should we get rid of person and put everything on
+the second table?
+
+But what do we do with a filter like (cn=*) where cn is an attribute type that
+appears in many, many objectclasses. Should we search all possible tables for
+matching entries? Not very attractive.
+
+Once this point is reached, three approaches come to mind. One is to do full
+normalization so that each attribute type, no matter what, has its own separate
+table. The simplistic approach where the DN is part of the primary key is
+extremely wasteful, and calls for an approach where the entry has a unique
+numeric id that is used instead for the keys and a main table that maps DNs to
+ids. The approach, anyway, is very inefficient when several attribute types from
+one or more entries are requested. Such a database, though cumbersomely,
+can be managed from SQL applications.
+
+The second approach is to put the whole entry as a blob in a table shared by all
+entries regardless of the objectclass and have additional tables that act as
+indices for the first table. Index tables are not database indices, but are
+fully managed by the LDAP server-side implementation. However, the database
+becomes unusable from SQL. And, thus, a fully fledged database system provides
+little or no advantage. The full generality of the database is unneeded.
+Much better to use something light and fast, like {{TERM:LMDB}}.
+
+A completely different way to see this is to give up any hopes of implementing
+the directory data model. In this case, LDAP is used as an access protocol to
+data that provides only superficially the directory data model. For instance,
+it may be read only or, where updates are allowed, restrictions are applied,
+such as making single-value attribute types that would allow for multiple values.
+Or the impossibility to add new objectclasses to an existing entry or remove
+one of those present. The restrictions span the range from allowed restrictions
+(that might be elsewhere the result of access control) to outright violations of
+the data model. It can be, however, a method to provide LDAP access to preexisting
+data that is used by other applications. But in the understanding that we don't
+really have a "directory".
+
+Existing commercial LDAP server implementations that use a relational database
+are either from the first kind or the third. I don't know of any implementation
+that uses a relational database to do inefficiently what LMDB does efficiently.
+For those who are interested in "third way" (exposing EXISTING data from RDBMS
+as LDAP tree, having some limitations compared to classic LDAP model, but making
+it possible to interoperate between LDAP and SQL applications):
+
+OpenLDAP includes back-sql - the backend that makes it possible. It uses ODBC +
+additional metainformation about translating LDAP queries to SQL queries in your
+RDBMS schema, providing different levels of access - from read-only to full
+access depending on RDBMS you use, and your schema.
+
+For more information on concept and limitations, see {{slapd-sql}}(5) man page,
+or the {{SECT: Backends}} section. There are also several examples for several
+RDBMSes in {{F:back-sql/rdbms_depend/*}} subdirectories.
+
+
+H2: What is slapd and what can it do?
+
+{{slapd}}(8) is an LDAP directory server that runs on many different
+platforms. You can use it to provide a directory service of your
+very own. Your directory can contain pretty much anything you want
+to put in it. You can connect it to the global LDAP directory
+service, or run a service all by yourself. Some of slapd's more
+interesting features and capabilities include:
+
+{{B:LDAPv3}}: {{slapd}} implements version 3 of {{TERM[expand]LDAP}}.
+{{slapd}} supports LDAP over both {{TERM:IPv4}} and {{TERM:IPv6}}
+and Unix {{TERM:IPC}}.
+
+{{B:{{TERM[expand]SASL}}}}: {{slapd}} supports strong authentication
+and data security (integrity and confidentiality) services through
+the use of SASL. {{slapd}}'s SASL implementation utilizes {{PRD:Cyrus
+SASL}} software which supports a number of mechanisms including
+{{TERM:DIGEST-MD5}}, {{TERM:EXTERNAL}}, and {{TERM:GSSAPI}}.
+
+{{B:{{TERM[expand]TLS}}}}: {{slapd}} supports certificate-based
+authentication and data security (integrity and confidentiality)
+services through the use of TLS (or SSL). {{slapd}}'s TLS
+implementation can utilize {{PRD:OpenSSL}} or {{PRD:GnuTLS}},
+software.
+
+{{B:Topology control}}: {{slapd}} can be configured to restrict
+access at the socket layer based upon network topology information.
+This feature utilizes {{TCP wrappers}}.
+
+{{B:Access control}}: {{slapd}} provides a rich and powerful access
+control facility, allowing you to control access to the information
+in your database(s). You can control access to entries based on
+LDAP authorization information, {{TERM:IP}} address, domain name
+and other criteria. {{slapd}} supports both {{static}} and {{dynamic}}
+access control information.
+
+{{B:Internationalization}}: {{slapd}} supports Unicode and language
+tags.
+
+{{B:Choice of database backends}}: {{slapd}} comes with a variety
+of different database backends you can choose from. They include
+{{TERM:MDB}}, a hierarchical high-performance transactional database backend;
+and PASSWD, a simple backend interface to the {{passwd}}(5) file.
+The MDB backend utilizes {{TERM:LMDB}}.
+
+{{B:Multiple database instances}}: {{slapd}} can be configured to
+serve multiple databases at the same time. This means that a single
+{{slapd}} server can respond to requests for many logically different
+portions of the LDAP tree, using the same or different database
+backends.
+
+{{B:Generic modules API}}: If you require even more customization,
+{{slapd}} lets you write your own modules easily. {{slapd}} consists
+of two distinct parts: a front end that handles protocol communication
+with LDAP clients; and modules which handle specific tasks such as
+database operations. Because these two pieces communicate via a
+well-defined {{TERM:C}} {{TERM:API}}, you can write your own
+customized modules which extend {{slapd}} in numerous ways. Also,
+a number of {{programmable database}} modules are provided. These
+allow you to expose external data sources to {{slapd}} using popular
+programming languages ({{PRD:Perl}}, and {{TERM:SQL}}).
+
+{{B:Threads}}: {{slapd}} is threaded for high performance. A single
+multi-threaded {{slapd}} process handles all incoming requests using
+a pool of threads. This reduces the amount of system overhead
+required while providing high performance.
+
+{{B:Replication}}: {{slapd}} can be configured to maintain shadow
+copies of directory information. This {{single-provider/multiple-consumer}}
+replication scheme is vital in high-volume environments where a
+single {{slapd}} installation just doesn't provide the necessary availability
+or reliability. For extremely demanding environments where a
+single point of failure is not acceptable, {{multi-provider}} replication
+is also available. With {{multi-provider}} replication two or more nodes can
+accept write operations allowing for redundancy at the provider level.
+
+{{slapd}} includes support for {{LDAP Sync}}-based
+replication.
+
+{{B:Proxy Cache}}: {{slapd}} can be configured as a caching
+LDAP proxy service.
+
+{{B:Configuration}}: {{slapd}} is highly configurable through a
+single configuration file which allows you to change just about
+everything you'd ever want to change. Configuration options have
+reasonable defaults, making your job much easier. Configuration can
+also be performed dynamically using LDAP itself, which greatly
+improves manageability.
+
+H2: What is lloadd and what can it do?
+
+{{lloadd}}(8) is a daemon that provides an LDAPv3 load balancer service.
+It is responsible for distributing requests across a set of {{slapd}}
+instances.
+
+See the {{SECT:Load Balancing with lloadd}} chapter for information
+about how to configure and run {{lloadd}}(8).
+
+Alternatively, the load balancer can run as a module embedded inside of
+{{slapd}}. This is also described in the {{SECT:Load Balancing with lloadd}} chapter.
+
+
diff --git a/doc/guide/admin/intro_dctree.png b/doc/guide/admin/intro_dctree.png
new file mode 100644
index 0000000..099588c
--- /dev/null
+++ b/doc/guide/admin/intro_dctree.png
Binary files differ
diff --git a/doc/guide/admin/intro_tree.png b/doc/guide/admin/intro_tree.png
new file mode 100644
index 0000000..043b51e
--- /dev/null
+++ b/doc/guide/admin/intro_tree.png
Binary files differ
diff --git a/doc/guide/admin/ldap-sync-refreshandpersist.png b/doc/guide/admin/ldap-sync-refreshandpersist.png
new file mode 100644
index 0000000..f6a2232
--- /dev/null
+++ b/doc/guide/admin/ldap-sync-refreshandpersist.png
Binary files differ
diff --git a/doc/guide/admin/ldap-sync-refreshonly.png b/doc/guide/admin/ldap-sync-refreshonly.png
new file mode 100644
index 0000000..7f4a95e
--- /dev/null
+++ b/doc/guide/admin/ldap-sync-refreshonly.png
Binary files differ
diff --git a/doc/guide/admin/limits.sdf b/doc/guide/admin/limits.sdf
new file mode 100644
index 0000000..e202740
--- /dev/null
+++ b/doc/guide/admin/limits.sdf
@@ -0,0 +1,266 @@
+# $Id$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+# This contribution is derived from OpenLDAP Software.
+# All of the modifications to OpenLDAP Software represented in this contribution
+# were developed by Andrew Findlay <andrew.findlay@skills-1st.co.uk>.
+# I have not assigned rights and/or interest in this work to any party.
+#
+# Copyright 2008 Andrew Findlay
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted only as authorized by the OpenLDAP Public License.
+
+H1: Limits
+
+H2: Introduction
+
+It is usually desirable to limit the server resources that can be
+consumed by each LDAP client. OpenLDAP provides two sets of limits:
+a size limit, which can restrict the {{number}} of entries that a
+client can retrieve in a single operation, and a time limit
+which restricts the length of time that an operation may continue.
+Both types of limit can be given different values depending on who
+initiated the operation.
+
+H2: Soft and Hard limits
+
+The server administrator can specify both {{soft limits}} and
+{{hard limits}}. Soft limits can be thought of as being the
+default limit value. Hard limits cannot be exceeded by ordinary
+LDAP users.
+
+LDAP clients can specify their own
+size and time limits when issuing search operations.
+This feature has been present since the earliest version of X.500.
+
+If the client specifies a limit then the lower of the requested value
+and the {{hard limit}} will become the limit for the operation.
+
+If the client does not specify a limit then the server applies the
+{{soft limit}}.
+
+Soft and Hard limits are often referred to together as {{administrative
+limits}}. Thus, if an LDAP client requests a search that would return
+more results than the limits allow it will get an {{adminLimitExceeded}}
+error. Note that the server will usually return some results even if
+the limit has been exceeded: this feature is useful to clients that
+just want to check for the existence of some entries without needing
+to see them all.
+
+The {{rootdn}} is not subject to any limits.
+
+H2: Global Limits
+
+Limits specified in the global part of the server configuration act
+as defaults which are used if no database has more specific limits set.
+
+In a {{slapd.conf}}(5) configuration the keywords are {{EX:sizelimit}} and
+{{EX:timelimit}}. When using the {{slapd config}} backend, the corresponding
+attributes are {{EX:olcSizeLimit}} and {{EX:olcTimeLimit}}. The syntax of
+these values are the same in both cases.
+
+The simple form sets both soft and hard limits to the same value:
+
+> sizelimit {<integer>|unlimited}
+> timelimit {<integer>|unlimited}
+
+The default sizelimit is 500 entries and the default timelimit is
+3600 seconds.
+
+An extended form allows soft and hard limits to be set separately:
+
+> sizelimit size[.{soft|hard}]=<integer> [...]
+> timelimit time[.{soft|hard}]=<integer> [...]
+
+Thus, to set a soft sizelimit of 10 entries and a hard limit of 75 entries:
+
+E: sizelimit size.soft=10 size.hard=75
+
+H3: Special Size Limits
+
+There are other forms of size limits in addition to the soft and hard limits.
+Note that when using the simple {{sizelimit}} form, none of these special
+limits are changed.
+
+H4: Unchecked Limits
+
+The {{unchecked}} keyword sets a limit on how many entries the server
+will examine after doing index lookups but before evaluating filter
+matches. If the set of candidates exceeds this limit, the search is aborted.
+The purpose is to avoid causing excessive workload on {{slapd}}
+if a filter uses attributes that are not properly indexed, and can
+be critical for very large directories.
+
+> sizelimit size.unchecked={<integer>|unlimited|disabled}
+
+The default is unlimited. The {{disabled}} setting prevents a search
+from being performed at all. This may be useful in the per-database
+limits described below, to disallow searches for a specific set of users.
+
+H4: Paged Results Limits
+
+If the LDAP client adds the {{pagedResultsControl}} to the search operation,
+the hard size limit is used by default, because the request for a specific
+page size is considered an explicit request for a limitation on the number
+of entries to be returned. However, the size limit applies to the total
+count of entries returned within the search, and not to a single page.
+
+Additional size limits may be enforced for paged searches.
+
+The {{EX:size.pr}} limit controls the maximum page size:
+
+> sizelimit size.pr={<integer>|noEstimate|unlimited}
+
+{{EX:<integer>}} is the maximum page size if no explicit size is set.
+{{EX:noEstimate}} has no effect in the current implementation as the
+server does not return an estimate of the result size anyway.
+{{EX:unlimited}} indicates that no limit is applied to the maximum
+page size.
+
+The {{EX:size.prtotal}} limit controls the total number of entries
+that can be returned by a paged search. By default the limit is the
+same as the normal {{EX:size.hard}} limit.
+
+> size.prtotal={<integer>|unlimited|disabled}
+
+{{EX:unlimited}} removes the limit on the number of entries that can be
+returned by a paged search.
+{{EX:disabled}} can be used to selectively disable paged result searches.
+
+H2: Per-Database Limits
+
+Each database can have its own set of limits that override the global
+ones. The syntax is more flexible, and it allows different limits to
+be applied to different entities. Note that an {{entity}} is different from
+an {{entry}}: the term {{entity}} is used here to indicate the ID of the
+person or process that has initiated the LDAP operation.
+
+In a {{slapd.conf}}(5) configuration the keyword is {{EX:limits}}.
+When using the {{slapd config}} backend, the corresponding
+attribute is {{EX:olcLimits}}. The syntax of
+the values is the same in both cases.
+
+> limits <selector> <limit> [<limit> [...]]
+
+The {{limits}} clause can be specified multiple times to apply different
+limits to different initiators. The server examines each clause in turn
+until it finds one that matches the operation's initiator or base DN.
+If no match is found, the global limits will be used.
+
+H3: Specify who the limits apply to
+
+The {{EX:<selector>}} part of the {{limits}} clause can take any of these values:
+
+!block table; align=Center; coltags="EX,N"; \
+ title="Table 9.1: Limits Entity Specifiers"
+Specifier|Entities
+*|All, including anonymous and authenticated users
+anonymous|Anonymous (non-authenticated) users
+users|Authenticated users
+dn[.<type>][.<style>]=<pattern>]|Entry or entries within a scope that match <pattern>
+group[/oc[/at]]=<pattern>|Members of a group
+!endblock
+
+Where
+
+{{type}} can be one of self or this and
+
+{{style}} can be one of exact, base, onelevel, subtree, children, regex, or anonymous
+
+More information can be found in the {{slapd.conf}}(5) or {{slapd-config}}(5) manual
+pages.
+
+H3: Specify time limits
+
+The syntax for time limits is
+
+E: time[.{soft|hard}]=<integer>
+
+where integer is the number of seconds slapd will spend
+answering a search request.
+
+If neither {{soft}} nor {{hard}} is specified, the value is used for both,
+e.g.:
+
+E: limits anonymous time=27
+
+The value {{unlimited}} may be used to remove the hard time limit entirely,
+e.g.:
+
+E: limits dn.exact="cn=anyuser,dc=example,dc=org" time.hard=unlimited
+
+H3: Specifying size limits
+
+The syntax for size limit is
+
+E: size[.{soft|hard}]=<integer>
+
+where {{EX:<integer>}} is the maximum number of entries slapd will return
+when answering a search request.
+
+In addition to soft and hard limits, other limits are also available,
+with the same meanings described for the global limits configuration above.
+
+
+H2: Example Limit Configurations
+
+H3: Simple Global Limits
+
+This simple global configuration fragment applies size and time limits
+to all searches by all users except {{rootdn}}. It limits searches to
+50 results and sets an overall time limit of 10 seconds.
+
+E: sizelimit 50
+E: timelimit 10
+
+H3: Global Hard and Soft Limits
+
+It is sometimes useful to limit the size of result sets but to allow
+clients to request a higher limit where needed. This can be achieved
+by setting separate hard and soft limits.
+
+E: sizelimit size.soft=5 size.hard=100
+
+To prevent clients from doing very inefficient non-indexed searches,
+add the {{unchecked}} limit:
+
+E: sizelimit size.soft=5 size.hard=100 size.unchecked=100
+
+H3: Giving specific users larger limits
+
+Having set appropriate default limits in the global configuration,
+you may want to give certain users the ability to retrieve larger
+result sets. Here is a way to do that in the per-database configuration:
+
+E: limits dn.exact="cn=anyuser,dc=example,dc=org" size=100000
+E: limits dn.exact="cn=personnel,dc=example,dc=org" size=100000
+E: limits dn.exact="cn=dirsync,dc=example,dc=org" size=100000
+
+It is generally best to avoid mentioning specific users in the server
+configuration. A better way is to give the higher limits to a group:
+
+E: limits group/groupOfNames/member="cn=bigwigs,dc=example,dc=org" size=100000
+
+H3: Limiting who can do paged searches
+
+It may be required that certain applications need very large result sets that
+they retrieve using paged searches, but that you do not want ordinary
+LDAP users to use the pagedResults control. The {{pr}} and {{prtotal}}
+limits can help:
+
+E: limits group/groupOfNames/member="cn=dirsync,dc=example,dc=org" size.prtotal=unlimited
+E: limits users size.soft=5 size.hard=100 size.prtotal=disabled
+E: limits anonymous size.soft=2 size.hard=5 size.prtotal=disabled
+
+H2: Glued/Subordinate database configurations
+
+When using subordinate databases, it is necessary for any limits that
+are to be applied across the parent and its subordinates to be defined in both
+the parent and its subordinates. Otherwise the settings on the subordinate databases
+are not honored.
+
+H2: Further Information
+
+For further information please see {{slapd.conf}}(5), {{ldapsearch}}(1) and {{slapd.access}}(5)
+
diff --git a/doc/guide/admin/load-balancer-scenario.png b/doc/guide/admin/load-balancer-scenario.png
new file mode 100644
index 0000000..8774352
--- /dev/null
+++ b/doc/guide/admin/load-balancer-scenario.png
Binary files differ
diff --git a/doc/guide/admin/loadbalancer.sdf b/doc/guide/admin/loadbalancer.sdf
new file mode 100644
index 0000000..c14916d
--- /dev/null
+++ b/doc/guide/admin/loadbalancer.sdf
@@ -0,0 +1,169 @@
+# $OpenLDAP$
+# Copyright 2021-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+H1: Load Balancing with lloadd
+
+As covered in the {{SECT:Replication}} chapter, replication is a fundamental
+requirement for delivering a resilient enterprise deployment. As such
+there's a need for an LDAPv3 capable load balancer to spread the load between the
+various directory instances.
+
+{{lloadd}}(8) provides the capability to distribute LDAP v3 requests between a
+set of running {{slapd}} instances. It can run as a standalone daemon
+{{lloadd}}, or as an embedded module running inside of {{slapd}}.
+
+H2: Overview
+
+{{lloadd}}(8) was designed to handle LDAP loads.
+It is protocol-aware and can balance LDAP loads on a per-operation basis rather
+than on a per-connection basis.
+
+{{lloadd}}(8) distributes the load across a set of slapd instances. The client
+connects to the load balancer instance which forwards the request to one
+of the servers and returns the response back to the client.
+
+H2: When to use the OpenLDAP load balancer
+
+In general, the OpenLDAP load balancer spreads the load across configured backend servers. It does not perform
+so-called intelligent routing. It does not understand semantics behind the operations being performed by the clients.
+
+More considerations:
+
+ - Servers are indistinguishable with respect to data contents. The exact same copy of data resides on every server.
+ - Clients do not require 'sticky' sessions.
+ - The sequence of operations isn't important. For example, read after update isn't required by the client.
+ - If your client can handle both connection pooling and load distribution then it's preferable to lloadd.
+ - Clients that require a consistent session (e.g. do writes), the best practice is to let them set up a direct session to one of the providers. The read-only clients are still free to use lloadd.
+ - 2.6 release of lloadd will include sticky sessions (coherency).
+
+H2: Runtime configurations
+
+It deploys in one of two ways:
+
+^ Standalone daemon: {{ lloadd }}
++ Loaded into the slapd daemon as a module: {{ lloadd.la }}
+
+It is recommended to run with the balancer module embedded in slapd because dynamic configuration (cn=config) and the monitor backend are then available.
+
+{{B: Sample load balancer scenario:}}
+
+!import "load-balancer-scenario.png"; align="center"; title="Load Balancer Scenario"
+FT[align="Center"] Figure: Load balancer sample scenario
+
+^ The LDAP client submits an LDAP operation to
+the load balancer daemon.
+
++ The load balancer forwards the request to one of the backend instances in its pool of servers.
+
++ The backend slapd server processes the request and returns the response to
+the load balancer instance.
+
++ The load balancer returns the response to the client. The client's unaware that it's connecting to a load balancer instead of slapd.
+
+H2: Build Notes
+
+To build the load balancer from source, follow the instructions in the
+{{SECT: A Quick-Start Guide}} substituting the following commands:
+
+^ To configure as standalone daemon:
+
+..{{EX:./configure --enable-balancer=yes}}
+
++ To configure as embedded module to slapd:
+
+..{{EX:./configure --enable-modules --enable-balancer=mod}}
+
+H2: Sample Runtime
+
+^ To run embedded as {{ lloadd }} module:
+
+..{{EX: slapd [-h URLs] [-f lloadd-config-file] [-u user] [-g group]}}
+
+ - the startup is the same as starting the {{ slapd }} daemon.
+ - URLs is for slapd management. The load balancer's listener URLs set in the configuration file or node. (more later)
+
++ To run as standalone daemon:
+
+..{{EX: lloadd [-h URLs] [-f lloadd-config-file] [-u user] [-g group]}}
+
+ - Other than a different daemon name, running standalone has the same options as starting {{ slapd }}.
+ - -h URLs specify the lloadd's interface directly, there is no management interface.
+
+For a complete list of options, checkout the man page {{ lloadd.8 }}
+
+H2: Configuring load balancer
+
+H3: Common configuration options
+
+Many of the same configuration options as slapd. For complete list, check
+the {{lloadd}}(5) man page.
+
+.{{S: }}
+{{B:Edit the slapd.conf or cn=config configuration file}}.
+
+To configure your working {{lloadd}}(8) you need to make the following changes to your configuration file:
+ ^ include {{ core.schema }} (embedded only)
+ + {{ TLSShareSlapdCTX { on | off } }}
+ + Other common TLS slapd options
+ + Setup argsfile/pidfile
+ + Setup moduleload path (embedded mode only)
+ + {{ moduleload lloadd.la }}
+ + loglevel, threads, ACL's
+ + {{ backend lload }} begin lloadd specific backend configurations
+ + {{ listen ldap://:PORT }} Specify listen port for load balancer
+ + {{ feature proxyauthz }} Use the proxy authZ control to forward client's identity
+ + {{ io-threads INT }} specify the number of threads to use for the connection manager. The default is 1 and this is typically adequate for up to 16 CPU cores
+
+H3: Sample backend config
+
+Sample setup config for load balancer running in front of four slapd instances.
+
+>backend lload
+>
+># The Load Balancer manages its own sockets, so they have to be separate
+># from the ones slapd manages (as specified with the -h "URLS" option at
+># startup).
+>listen ldap://:1389
+>
+># Enable authorization tracking
+>feature proxyauthz
+>
+># Specify the number of threads to use for the connection manager. The default is 1 and this is typically adequate for up to 16 CPU cores.
+># The value should be set to a power of 2:
+>io-threads 2
+>
+># If TLS is configured above, use the same context for the Load Balancer
+># If using cn=config, this can be set to false and different settings
+># can be used for the Load Balancer
+>TLSShareSlapdCTX true
+>
+># Authentication and other options (timeouts) shared between backends.
+>bindconf bindmethod=simple
+> binddn=dc=example,dc=com credentials=secret
+> network-timeout=5
+> tls_cacert="/usr/local/etc/openldap/ca.crt"
+> tls_cert="/usr/local/etc/openldap/host.crt"
+> tls_key="/usr/local/etc/openldap/host.pem"
+>
+>
+># List the backends we should relay operations to, they all have to be
+># practically indistinguishable. Only TLS settings can be specified on
+># a per-backend basis.
+>
+>backend-server uri=ldap://ldaphost01 starttls=critical retry=5000
+> max-pending-ops=50 conn-max-pending=10
+> numconns=10 bindconns=5
+>backend-server uri=ldap://ldaphost02 starttls=critical retry=5000
+> max-pending-ops=50 conn-max-pending=10
+> numconns=10 bindconns=5
+>backend-server uri=ldap://ldaphost03 starttls=critical retry=5000
+> max-pending-ops=50 conn-max-pending=10
+> numconns=10 bindconns=5
+>backend-server uri=ldap://ldaphost04 starttls=critical retry=5000
+> max-pending-ops=50 conn-max-pending=10
+> numconns=10 bindconns=5
+>
+>#######################################################################
+># Monitor database
+>#######################################################################
+>database monitor
diff --git a/doc/guide/admin/maintenance.sdf b/doc/guide/admin/maintenance.sdf
new file mode 100644
index 0000000..62a5532
--- /dev/null
+++ b/doc/guide/admin/maintenance.sdf
@@ -0,0 +1,77 @@
+# $OpenLDAP$
+# Copyright 2007-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: Maintenance
+
+System Administration is all about maintenance, so it is only fair that we
+discuss how to correctly maintain an OpenLDAP deployment.
+
+
+H2: Directory Backups
+
+Backup strategies largely depend on the amount of change in the database
+and how much of that change an administrator might be willing to lose in a
+catastrophic failure. There are two basic methods that can be used:
+
+1. Backup the LMDB database itself
+
+The LMDB database can be copied live using the mdb_copy command. If the database
+is a sparse file via the use of the "writemap" environment flag, the resulting
+copy will be the actual size of the database rather than a sparse copy.
+
+2. Periodically run slapcat and back up the LDIF file:
+
+Slapcat can be run while slapd is active. However, one runs the risk of an
+inconsistent database- not from the point of slapd, but from the point of
+the applications using LDAP. For example, if a provisioning application
+performed tasks that consisted of several LDAP operations, and the slapcat
+took place concurrently with those operations, then there might be
+inconsistencies in the LDAP database from the point of view of that
+provisioning application and applications that depended on it. One must,
+therefore, be convinced something like that won't happen. One way to do that
+would be to put the database in read-only mode while performing the
+slapcat. The other disadvantage of this approach is that the generated LDIF
+files can be rather large and the accumulation of the day's backups could
+add up to a substantial amount of space.
+
+You can use {{slapcat}}(8) to generate an LDIF file for each of your {{slapd}}(8)
+back-mdb databases.
+
+> slapcat -f slapd.conf -b "dc=example,dc=com"
+
+For back-mdb this command may be ran while slapd(8) is running.
+
+
+H2: Checkpointing
+
+Setting a checkpoint is only necessary when back-mdb has the dbnosync flag set. Otherwise
+it has no effect. With back-mdb the kbyte option is not implemented, meaning it will only
+run a checkpoint based on the elapsed amount of minutes flag.
+
+H2: Migration
+
+The simplest steps needed to migrate between versions or upgrade, depending on your deployment
+type are:
+
+.{{S: }}
+^{{B: Stop the current server when convenient}}
+
+.{{S: }}
++{{B: slapcat the current data out}}
+
+.{{S: }}
++{{B: Clear out the current data directory (/usr/local/var/openldap-data/)}}
+
+.{{S: }}
++{{B: Perform the software upgrades}}
+
+.{{S: }}
++{{B: slapadd the exported data back into the directory}}
+
+.{{S: }}
++{{B: Start the server}}
+
+Obviously this doesn't cater for any complicated deployments with {{SECT: N-Way Multi-Provider}},
+but following the above sections and using either commercial support or community support should help. Also check the
+{{SECT: Troubleshooting}} section.
diff --git a/doc/guide/admin/master.sdf b/doc/guide/admin/master.sdf
new file mode 100644
index 0000000..48b8b06
--- /dev/null
+++ b/doc/guide/admin/master.sdf
@@ -0,0 +1,141 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+#
+# master file for the OpenLDAP Administrator's Guide
+#
+#
+# To generate guide for distribution:
+# sdf -2html guide.sdf
+# sdf -2txt guide.sdf
+# cp guide.{html,txt} $distribution/doc/guide
+#
+# To generate pages for web
+# sdf -2topics index.sdf
+#
+!include "../preamble.sdf"; plain
+
+# title information
+!include "title.sdf"
+PB:
+
+# Document copyright, publishing info, acknowledgements, preface
+!include "preface.sdf"; about
+PB:
+
+# Chapters
+!include "intro.sdf"; chapter
+PB:
+
+!include "quickstart.sdf"; chapter
+PB:
+
+!include "config.sdf"; chapter
+PB:
+
+!include "install.sdf"; chapter
+PB:
+
+!include "slapdconf2.sdf"; chapter
+PB:
+
+!include "slapdconfig.sdf"; chapter
+PB:
+
+!include "runningslapd.sdf"; chapter
+PB:
+
+!include "access-control.sdf"; chapter
+PB:
+
+!include "limits.sdf"; chapter
+PB:
+
+!include "dbtools.sdf"; chapter
+PB:
+
+!include "backends.sdf"; chapter
+PB:
+
+!include "overlays.sdf"; chapter
+PB:
+
+!include "schema.sdf"; chapter
+PB:
+
+!include "security.sdf"; chapter
+PB:
+
+!include "sasl.sdf"; chapter
+PB:
+
+!include "tls.sdf"; chapter
+PB:
+
+!include "referrals.sdf"; chapter
+PB:
+
+!include "replication.sdf"; chapter
+PB:
+
+!include "maintenance.sdf"; chapter
+PB:
+
+!include "monitoringslapd.sdf"; chapter
+PB:
+
+!include "loadbalancer.sdf"; chapter
+PB:
+
+!include "tuning.sdf"; chapter
+PB:
+
+!include "troubleshooting.sdf"; chapter
+PB:
+
+# Appendices
+!include "appendix-changes.sdf"; appendix
+PB:
+
+# Upgrade from 2.3.x
+!include "appendix-upgrading.sdf"; appendix
+PB:
+
+# Common Errors
+!include "appendix-common-errors.sdf"; appendix
+PB:
+
+# What versions we recommend
+!include "appendix-recommended-versions.sdf"; appendix
+PB:
+
+# Real Deployments
+!include "appendix-deployments.sdf"; appendix
+PB:
+
+# Contributions
+!include "appendix-contrib.sdf"; appendix
+PB:
+
+# Config file examples
+!include "appendix-configs.sdf"; appendix
+PB:
+
+# LDAP Result Codes
+!include "appendix-ldap-result-codes.sdf"; appendix
+PB:
+
+
+# Terms
+!include "glossary.sdf"; appendix
+PB:
+
+# Autoconf
+!include "../release/autoconf.sdf"; appendix
+PB:
+
+# Software Copyright/License
+!include "../release/copyright.sdf"; appendix
+PB:
+
+!include "../release/license.sdf"; appendix
diff --git a/doc/guide/admin/monitoringslapd.sdf b/doc/guide/admin/monitoringslapd.sdf
new file mode 100644
index 0000000..80b6137
--- /dev/null
+++ b/doc/guide/admin/monitoringslapd.sdf
@@ -0,0 +1,494 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+H1: Monitoring
+
+{{slapd}}(8) supports an optional {{TERM:LDAP}} monitoring interface
+you can use to obtain information regarding the current state of
+your {{slapd}} instance. For instance, the interface allows you
+to determine how many clients are connected to the server currently.
+The monitoring information is provided by a specialized backend,
+the {{monitor}} backend. A manual page, {{slapd-monitor}}(5) is
+available.
+
+When the monitoring interface is enabled, LDAP clients may be used
+to access information provided by the {{monitor}} backend, subject
+to access and other controls.
+
+When enabled, the {{monitor}} backend dynamically generates and
+returns objects in response to search requests in the {{cn=Monitor}}
+subtree. Each object contains information about a particular aspect
+of the server. The information is held in a combination of user
+applications and operational attributes. This information can be
+accessed with {{ldapsearch(1)}}, with any general-purpose LDAP browser,
+or with specialized monitoring tools. The {{SECT:Accessing Monitoring
+Information}} section provides a brief tutorial on how to use
+{{ldapsearch}}(1) to access monitoring information, while the
+{{SECT:Monitor information}} section details monitoring information
+base and its organization.
+
+While support for the monitor backend is included in default builds
+of slapd(8), this support requires some configuration to become
+active. This may be done using either {{EX:cn=config}} or
+{{slapd.conf}}(5). The former is discussed in the {{SECT:Monitor
+configuration via cn=config}} section of this of this chapter. The
+latter is discussed in the {{SECT:Monitor configuration via
+slapd.conf(5)}} section of this chapter. These sections assume
+monitor backend is built into {{slapd}} (e.g., {{EX:--enable-monitor=yes}},
+the default). If the monitor backend was built as a module (e.g.,
+{{EX:--enable-monitor=mod}}, this module must loaded. Loading of
+modules is discussed in the {{SECT:Configuring slapd}} and {{SECT:The
+slapd Configuration File}} chapters.
+
+
+H2: Monitor configuration via cn=config(5)
+
+The {{monitor backend}} is statically built into slapd and can be
+instantiated via ldapadd.
+
+> dn: olcDatabase=monitor,cn=config
+> objectClass: olcDatabaseConfig
+> olcDatabase: monitor
+
+H2: Monitor configuration via slapd.conf(5)
+
+Configuration of the slapd.conf(5) to support LDAP monitoring
+is quite simple.
+
+First, ensure {{core.schema}} schema configuration file is included
+by your {{slapd.conf}}(5) file. The {{monitor}} backend requires
+it.
+
+Second, instantiate the {{monitor backend}} by adding a
+{{database monitor}} directive below your existing database
+sections. For instance:
+
+> database monitor
+
+Lastly, add additional global or database directives as needed.
+
+Like most other database backends, the monitor backend does honor
+slapd(8) access and other administrative controls. As some monitor
+information may be sensitive, it is generally recommend access to
+cn=monitor be restricted to directory administrators and their
+monitoring agents. Adding an {{access}} directive immediately below
+the {{database monitor}} directive is a clear and effective approach
+for controlling access. For instance, the addition of the following
+{{access}} directive immediately below the {{database monitor}}
+directive restricts access to monitoring information to the specified
+directory manager.
+
+> access to *
+> by dn.exact="cn=Manager,dc=example,dc=com
+> by * none
+
+More information on {{slapd}}(8) access controls, see {{The access
+Control Directive}} section of the {{SECT:The slapd Configuration
+File}} chapter and {{slapd.access}}(5).
+
+After restarting {{slapd}}(8), you are ready to start exploring the
+monitoring information provided in {{EX:cn=config}} as discussed
+in the {{SECT:Accessing Monitoring Information}} section of this
+chapter.
+
+One can verify slapd(8) is properly configured to provide monitoring
+information by attempting to read the {{EX:cn=monitor}} object.
+For instance, if the following {{ldapsearch}}(1) command returns the
+cn=monitor object (with, as requested, no attributes), it's working.
+
+> ldapsearch -x -D 'cn=Manager,dc=example,dc=com' -W \
+> -b 'cn=Monitor' -s base 1.1
+
+Note that unlike general purpose database backends, the database
+suffix is hardcoded. It's always {{EX:cn=Monitor}}. So no {{suffix}}
+directive should be provided. Also note that general purpose
+database backends, the monitor backend cannot be instantiated
+multiple times. That is, there can only be one (or zero) occurrences
+of {{EX:database monitor}} in the server's configuration.
+
+
+H2: Accessing Monitoring Information
+
+As previously discussed, when enabled, the {{monitor}} backend
+dynamically generates and returns objects in response to search
+requests in the {{cn=Monitor}} subtree. Each object contains
+information about a particular aspect of the server. The information
+is held in a combination of user applications and operational
+attributes. This information can be accessed with {{ldapsearch(1)}},
+with any general-purpose LDAP browser, or with specialized monitoring
+tools.
+
+This section provides a provides a brief tutorial on how to use
+{{ldapsearch}}(1) to access monitoring information.
+
+To inspect any particular monitor object, one performs search
+operation on the object with a baseObject scope and a
+{{EX:(objectClass=*)}} filter. As the monitoring information is
+contained in a combination of user applications and operational
+attributes, the return all user applications attributes (e.g.,
+{{EX:'*'}}) and all operational attributes (e.g., {{EX:'+'}}) should
+be requested. For instance, to read the {{EX:cn=Monitor}} object
+itself, the {{ldapsearch}}(1) command (modified to fit your configuration)
+can be used:
+
+> ldapsearch -x -D 'cn=Manager,dc=example,dc=com' -W \
+> -b 'cn=Monitor' -s base '(objectClass=*)' '*' '+'
+
+When run against your server, this should produce output
+similar to:
+
+> dn: cn=Monitor
+> objectClass: monitorServer
+> structuralObjectClass: monitorServer
+> cn: Monitor
+> creatorsName:
+> modifiersName:
+> createTimestamp: 20061208223558Z
+> modifyTimestamp: 20061208223558Z
+> description: This subtree contains monitoring/managing objects.
+> description: This object contains information about this server.
+> description: Most of the information is held in operational attributes, which
+> must be explicitly requested.
+> monitoredInfo: OpenLDAP: slapd 2.5 (Dec 7 2006 17:30:29)
+> entryDN: cn=Monitor
+> subschemaSubentry: cn=Subschema
+> hasSubordinates: TRUE
+
+To reduce the number of uninteresting attributes returned, one
+can be more selective when requesting which attributes are to be
+returned. For instance, one could request the return of all
+attributes allowed by the {{monitorServer}} object class (e.g.,
+{{EX:@objectClass}}) instead of all user and all operational
+attributes:
+
+> ldapsearch -x -D 'cn=Manager,dc=example,dc=com' -W \
+> -b 'cn=Monitor' -s base '(objectClass=*)' '@monitorServer'
+
+This limits the output as follows:
+
+> dn: cn=Monitor
+> objectClass: monitorServer
+> cn: Monitor
+> description: This subtree contains monitoring/managing objects.
+> description: This object contains information about this server.
+> description: Most of the information is held in operational attributes, which
+> must be explicitly requested.
+> monitoredInfo: OpenLDAP: slapd 2.X (Dec 7 2006 17:30:29)
+
+To return the names of all the monitoring objects, one performs a
+search of {{EX:cn=Monitor}} with subtree scope and {{EX:(objectClass=*)}}
+filter and requesting no attributes (e.g., {{EX:1.1}}) be returned.
+
+> ldapsearch -x -D 'cn=Manager,dc=example,dc=com' -W -b 'cn=Monitor' -s sub 1.1
+
+If you run this command you will discover that there are many objects
+in the {{cn=Monitor}} subtree. The following section describes
+some of the commonly available monitoring objects.
+
+
+H2: Monitor Information
+
+The {{monitor}} backend provides a wealth of information useful
+for monitoring the slapd(8) contained in set of monitor objects.
+Each object contains information about a particular aspect of
+the server, such as a backends, a connection, or a thread.
+Some objects serve as containers for other objects and used
+to construct a hierarchy of objects.
+
+In this hierarchy, the most superior object is {cn=Monitor}.
+While this object primarily serves as a container for other
+objects, most of which are containers, this object provides
+information about this server. In particular, it provides the
+slapd(8) version string. Example:
+
+> dn: cn=Monitor
+> monitoredInfo: OpenLDAP: slapd 2.X (Dec 7 2006 17:30:29)
+
+Note: Examples in this section (and its subsections) have been
+trimmed to show only key information.
+
+
+H3: Backends
+
+The {{EX:cn=Backends,cn=Monitor}} object provides a list of available
+backends. The list of available backends includes all builtin backends,
+as well as those backends loaded by modules. For example:
+
+> dn: cn=Backends,cn=Monitor
+> monitoredInfo: config
+> monitoredInfo: ldif
+> monitoredInfo: monitor
+> monitoredInfo: mdb
+
+This indicates the {{config}}, {{ldif}}, {{monitor}},
+and {{mdb}} backends are available.
+
+The {{EX:cn=Backends,cn=Monitor}} object is also a container
+for available backend objects. Each available backend object
+contains information about a particular backend. For example:
+
+> dn: cn=Backend 0,cn=Backends,cn=Monitor
+> monitoredInfo: config
+> monitorRuntimeConfig: TRUE
+> supportedControl: 2.16.840.1.113730.3.4.2
+> seeAlso: cn=Database 0,cn=Databases,cn=Monitor
+>
+> dn: cn=Backend 1,cn=Backends,cn=Monitor
+> monitoredInfo: ldif
+> monitorRuntimeConfig: TRUE
+> supportedControl: 2.16.840.1.113730.3.4.2
+>
+> dn: cn=Backend 2,cn=Backends,cn=Monitor
+> monitoredInfo: monitor
+> monitorRuntimeConfig: TRUE
+> supportedControl: 2.16.840.1.113730.3.4.2
+> seeAlso: cn=Database 2,cn=Databases,cn=Monitor
+>
+> dn: cn=Backend 3,cn=Backends,cn=Monitor
+> monitoredInfo: mdb
+> monitorRuntimeConfig: TRUE
+> supportedControl: 1.3.6.1.1.12
+> supportedControl: 2.16.840.1.113730.3.4.2
+> supportedControl: 1.3.6.1.4.1.4203.666.5.2
+> supportedControl: 1.2.840.113556.1.4.319
+> supportedControl: 1.3.6.1.1.13.1
+> supportedControl: 1.3.6.1.1.13.2
+> supportedControl: 1.3.6.1.4.1.4203.1.10.1
+> supportedControl: 1.2.840.113556.1.4.1413
+> supportedControl: 1.3.6.1.4.1.4203.666.11.7.2
+
+For each of these objects, monitorInfo indicates which backend the
+information in the object is about. For instance, the {{EX:cn=Backend
+5,cn=Backends,cn=Monitor}} object contains (in the example) information
+about the {{mdb}} backend.
+
+!block table
+Attribute|Description
+monitoredInfo|Name of backend
+supportedControl|supported LDAP control extensions
+seeAlso|Database objects of instances of this backend
+!endblock
+
+H3: Connections
+
+The main entry is empty; it should contain some statistics on the number
+of connections.
+
+Dynamic child entries are created for each open connection, with stats on
+the activity on that connection (the format will be detailed later).
+There are two special child entries that show the number of total and
+current connections respectively.
+
+For example:
+
+Total Connections:
+
+> dn: cn=Total,cn=Connections,cn=Monitor
+> structuralObjectClass: monitorCounterObject
+> monitorCounter: 4
+> entryDN: cn=Total,cn=Connections,cn=Monitor
+> subschemaSubentry: cn=Subschema
+> hasSubordinates: FALSE
+
+Current Connections:
+
+> dn: cn=Current,cn=Connections,cn=Monitor
+> structuralObjectClass: monitorCounterObject
+> monitorCounter: 2
+> entryDN: cn=Current,cn=Connections,cn=Monitor
+> subschemaSubentry: cn=Subschema
+> hasSubordinates: FALSE
+
+
+H3: Databases
+
+The main entry contains the naming context of each configured database;
+the child entries contain, for each database, the type and the naming
+context.
+
+For example:
+
+> dn: cn=Database 2,cn=Databases,cn=Monitor
+> structuralObjectClass: monitoredObject
+> monitoredInfo: monitor
+> monitorIsShadow: FALSE
+> monitorContext: cn=Monitor
+> readOnly: FALSE
+> entryDN: cn=Database 2,cn=Databases,cn=Monitor
+> subschemaSubentry: cn=Subschema
+> hasSubordinates: FALSE
+
+H3: Listener
+
+It contains the description of the devices the server is currently
+listening on:
+
+> dn: cn=Listener 0,cn=Listeners,cn=Monitor
+> structuralObjectClass: monitoredObject
+> monitorConnectionLocalAddress: IP=0.0.0.0:389
+> entryDN: cn=Listener 0,cn=Listeners,cn=Monitor
+> subschemaSubentry: cn=Subschema
+> hasSubordinates: FALSE
+
+
+H3: Log
+
+It contains the currently active log items. The {{Log}} subsystem allows
+user modify operations on the {{description}} attribute, whose values {{MUST}}
+be in the list of admittable log switches:
+
+> Trace
+> Packets
+> Args
+> Conns
+> BER
+> Filter
+> Config
+> ACL
+> Stats
+> Stats2
+> Shell
+> Parse
+> Sync
+
+These values can be added, replaced or deleted; they affect what
+messages are sent to the syslog device.
+Custom values could be added by custom modules.
+
+H3: Operations
+
+It shows some statistics on the operations performed by the server:
+
+> Initiated
+> Completed
+
+and for each operation type, i.e.:
+
+> Bind
+> Unbind
+> Add
+> Delete
+> Modrdn
+> Modify
+> Compare
+> Search
+> Abandon
+> Extended
+
+There are too many types to list example here, so please try for yourself
+using {{SECT: Monitor search example}}
+
+H3: Overlays
+
+The main entry contains the type of overlays available at run-time;
+the child entries, for each overlay, contain the type of the overlay.
+
+It should also contain the modules that have been loaded if dynamic
+overlays are enabled:
+
+> # Overlays, Monitor
+> dn: cn=Overlays,cn=Monitor
+> structuralObjectClass: monitorContainer
+> monitoredInfo: syncprov
+> monitoredInfo: accesslog
+> monitoredInfo: glue
+> entryDN: cn=Overlays,cn=Monitor
+> subschemaSubentry: cn=Subschema
+> hasSubordinates: TRUE
+
+H3: SASL
+
+Currently empty.
+
+H3: Statistics
+
+It shows some statistics on the data sent by the server:
+
+> Bytes
+> PDU
+> Entries
+> Referrals
+
+e.g.
+
+> # Entries, Statistics, Monitor
+> dn: cn=Entries,cn=Statistics,cn=Monitor
+> structuralObjectClass: monitorCounterObject
+> monitorCounter: 612248
+> entryDN: cn=Entries,cn=Statistics,cn=Monitor
+> subschemaSubentry: cn=Subschema
+> hasSubordinates: FALSE
+
+H3: Threads
+
+It contains the maximum number of threads enabled at startup and the
+current backload.
+
+e.g.
+
+> # Max, Threads, Monitor
+> dn: cn=Max,cn=Threads,cn=Monitor
+> structuralObjectClass: monitoredObject
+> monitoredInfo: 16
+> entryDN: cn=Max,cn=Threads,cn=Monitor
+> subschemaSubentry: cn=Subschema
+> hasSubordinates: FALSE
+
+
+H3: Time
+
+It contains two child entries with the start time and the current time
+of the server.
+
+e.g.
+
+Start time:
+
+> dn: cn=Start,cn=Time,cn=Monitor
+> structuralObjectClass: monitoredObject
+> monitorTimestamp: 20061205124040Z
+> entryDN: cn=Start,cn=Time,cn=Monitor
+> subschemaSubentry: cn=Subschema
+> hasSubordinates: FALSE
+
+Current time:
+
+> dn: cn=Current,cn=Time,cn=Monitor
+> structuralObjectClass: monitoredObject
+> monitorTimestamp: 20061207120624Z
+> entryDN: cn=Current,cn=Time,cn=Monitor
+> subschemaSubentry: cn=Subschema
+> hasSubordinates: FALSE
+
+H3: TLS
+
+Currently empty.
+
+H3: Waiters
+
+It contains the number of current read waiters.
+
+e.g.
+
+Read waiters:
+
+> dn: cn=Read,cn=Waiters,cn=Monitor
+> structuralObjectClass: monitorCounterObject
+> monitorCounter: 7
+> entryDN: cn=Read,cn=Waiters,cn=Monitor
+> subschemaSubentry: cn=Subschema
+> hasSubordinates: FALSE
+
+Write waiters:
+
+> dn: cn=Write,cn=Waiters,cn=Monitor
+> structuralObjectClass: monitorCounterObject
+> monitorCounter: 0
+> entryDN: cn=Write,cn=Waiters,cn=Monitor
+> subschemaSubentry: cn=Subschema
+> hasSubordinates: FALSE
+
+Add new monitored things here and discuss, referencing man pages and present
+examples
+
+
diff --git a/doc/guide/admin/n-way-multi-provider.png b/doc/guide/admin/n-way-multi-provider.png
new file mode 100644
index 0000000..8eadf77
--- /dev/null
+++ b/doc/guide/admin/n-way-multi-provider.png
Binary files differ
diff --git a/doc/guide/admin/overlays.sdf b/doc/guide/admin/overlays.sdf
new file mode 100644
index 0000000..20e4b93
--- /dev/null
+++ b/doc/guide/admin/overlays.sdf
@@ -0,0 +1,1515 @@
+# $OpenLDAP$
+# Copyright 2007-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: Overlays
+
+Overlays are software components that provide hooks to functions analogous to
+those provided by backends, which can be stacked on top of the backend calls
+and as callbacks on top of backend responses to alter their behavior.
+
+Overlays may be compiled statically into {{slapd}}, or when module support
+is enabled, they may be dynamically loaded. Most of the overlays
+are only allowed to be configured on individual databases.
+
+Some can be stacked on the {{EX:frontend}} as well, for global use. This means that
+they can be executed after a request is parsed and validated, but right before the
+appropriate database is selected. The main purpose is to affect operations
+regardless of the database they will be handled by, and, in some cases,
+to influence the selection of the database by massaging the request DN.
+
+Essentially, overlays represent a means to:
+
+ * customize the behavior of existing backends without changing the backend
+ code and without requiring one to write a new custom backend with
+ complete functionality
+ * write functionality of general usefulness that can be applied to
+ different backend types
+
+When using {{slapd.conf}}(5), overlays that are configured before any other
+databases are considered global, as mentioned above. In fact they are implicitly
+stacked on top of the {{EX:frontend}} database. They can also be explicitly
+configured as such:
+
+> database frontend
+> overlay <overlay name>
+
+Overlays are usually documented by separate specific man pages in section 5;
+the naming convention is
+
+> slapo-<overlay name>
+
+All distributed core overlays have a man page. Feel free to contribute to any,
+if you think there is anything missing in describing the behavior of the component
+and the implications of all the related configuration directives.
+
+Official overlays are located in
+
+> servers/slapd/overlays/
+
+That directory also contains the file slapover.txt, which describes the
+rationale of the overlay implementation, and may serve as a guideline for the
+development of custom overlays.
+
+Contribware overlays are located in
+
+> contrib/slapd-modules/<overlay name>/
+
+along with other types of run-time loadable components; they are officially
+distributed, but not maintained by the project.
+
+All the current overlays in OpenLDAP are listed and described in detail in the
+following sections.
+
+
+H2: Access Logging
+
+
+H3: Overview
+
+This overlay can record accesses to a given backend database on another
+database.
+
+This allows all of the activity on a given database to be reviewed using arbitrary
+LDAP queries, instead of just logging to local flat text files. Configuration
+options are available for selecting a subset of operation types to log, and to
+automatically prune older log records from the logging database. Log records
+are stored with audit schema to assure their readability whether viewed as LDIF
+or in raw form.
+
+It is also used for {{SECT:delta-syncrepl replication}}
+
+Note: An accesslog database is unique to a given provider. It should
+never be replicated.
+
+H3: Access Logging Configuration
+
+The following is a basic example that implements Access Logging:
+
+> database mdb
+> suffix dc=example,dc=com
+> ...
+> overlay accesslog
+> logdb cn=log
+> logops writes reads
+> logold (objectclass=person)
+>
+> database mdb
+> suffix cn=log
+> ...
+> index reqStart eq
+> access to *
+> by dn.base="cn=admin,dc=example,dc=com" read
+
+The following is an example used for {{SECT:delta-syncrepl replication}}:
+
+> database mdb
+> suffix cn=accesslog
+> directory /usr/local/var/openldap-accesslog
+> rootdn cn=accesslog
+> index default eq
+> index entryCSN,objectClass,reqEnd,reqResult,reqStart,reqDN
+
+Accesslog overlay definitions for the primary db
+
+> database mdb
+> suffix dc=example,dc=com
+> ...
+> overlay accesslog
+> logdb cn=accesslog
+> logops writes
+> logsuccess TRUE
+> # scan the accesslog DB every day, and purge entries older than 7 days
+> logpurge 07+00:00 01+00:00
+
+An example search result against {{B:cn=accesslog}} might look like:
+
+> [ghenry@suretec ghenry]# ldapsearch -x -b cn=accesslog
+> # extended LDIF
+> #
+> # LDAPv3
+> # base <cn=accesslog> with scope subtree
+> # filter: (objectclass=*)
+> # requesting: ALL
+> #
+>
+> # accesslog
+> dn: cn=accesslog
+> objectClass: auditContainer
+> cn: accesslog
+>
+> # 20080110163829.000004Z, accesslog
+> dn: reqStart=20080110163829.000004Z,cn=accesslog
+> objectClass: auditModify
+> reqStart: 20080110163829.000004Z
+> reqEnd: 20080110163829.000005Z
+> reqType: modify
+> reqSession: 196696
+> reqAuthzID: cn=admin,dc=suretecsystems,dc=com
+> reqDN: uid=suretec-46022f8$,ou=Users,dc=suretecsystems,dc=com
+> reqResult: 0
+> reqMod: sambaPwdCanChange:- ###CENSORED###
+> reqMod: sambaPwdCanChange:+ ###CENSORED###
+> reqMod: sambaNTPassword:- ###CENSORED###
+> reqMod: sambaNTPassword:+ ###CENSORED###
+> reqMod: sambaPwdLastSet:- ###CENSORED###
+> reqMod: sambaPwdLastSet:+ ###CENSORED###
+> reqMod: entryCSN:= 20080110163829.095157Z#000000#000#000000
+> reqMod: modifiersName:= cn=admin,dc=suretecsystems,dc=com
+> reqMod: modifyTimestamp:= 20080110163829Z
+>
+> # search result
+> search: 2
+> result: 0 Success
+>
+> # numResponses: 3
+> # numEntries: 2
+
+
+H3: Further Information
+
+{{slapo-accesslog(5)}} and the {{SECT:delta-syncrepl replication}} section.
+
+
+H2: Audit Logging
+
+The Audit Logging overlay can be used to record all changes on a given backend database to a specified log file.
+
+H3: Overview
+
+If the need arises whereby changes need to be logged as standard LDIF, then the auditlog overlay {{B:slapo-auditlog (5)}}
+can be used. Full examples are available in the man page {{B:slapo-auditlog (5)}}
+
+H3: Audit Logging Configuration
+
+If the directory is running vi {{F:slapd.d}}, then the following LDIF could be used to add the overlay to the overlay list
+in {{B:cn=config}} and set what file the {{TERM:LDIF}} gets logged to (adjust to suit)
+
+> dn: olcOverlay=auditlog,olcDatabase={1}mdb,cn=config
+> changetype: add
+> objectClass: olcOverlayConfig
+> objectClass: olcAuditLogConfig
+> olcOverlay: auditlog
+> olcAuditlogFile: /tmp/auditlog.ldif
+
+
+In this example for testing, we are logging changes to {{F:/tmp/auditlog.ldif}}
+
+A typical {{TERM:LDIF}} file created by {{B:slapo-auditlog(5)}} would look like:
+
+> # add 1196797576 dc=suretecsystems,dc=com cn=admin,dc=suretecsystems,dc=com
+> dn: dc=suretecsystems,dc=com
+> changetype: add
+> objectClass: dcObject
+> objectClass: organization
+> dc: suretecsystems
+> o: Suretec Systems Ltd.
+> structuralObjectClass: organization
+> entryUUID: 1606f8f8-f06e-1029-8289-f0cc9d81e81a
+> creatorsName: cn=admin,dc=suretecsystems,dc=com
+> modifiersName: cn=admin,dc=suretecsystems,dc=com
+> createTimestamp: 20051123130912Z
+> modifyTimestamp: 20051123130912Z
+> entryCSN: 20051123130912.000000Z#000001#000#000000
+> auditContext: cn=accesslog
+> # end add 1196797576
+>
+> # add 1196797577 dc=suretecsystems,dc=com cn=admin,dc=suretecsystems,dc=com
+> dn: ou=Groups,dc=suretecsystems,dc=com
+> changetype: add
+> objectClass: top
+> objectClass: organizationalUnit
+> ou: Groups
+> structuralObjectClass: organizationalUnit
+> entryUUID: 160aaa2a-f06e-1029-828a-f0cc9d81e81a
+> creatorsName: cn=admin,dc=suretecsystems,dc=com
+> modifiersName: cn=admin,dc=suretecsystems,dc=com
+> createTimestamp: 20051123130912Z
+> modifyTimestamp: 20051123130912Z
+> entryCSN: 20051123130912.000000Z#000002#000#000000
+> # end add 1196797577
+
+
+H3: Further Information
+
+{{:slapo-auditlog(5)}}
+
+
+H2: Chaining
+
+
+H3: Overview
+
+The chain overlay provides basic chaining capability to the underlying
+database.
+
+What is chaining? It indicates the capability of a DSA to follow referrals on
+behalf of the client, so that distributed systems are viewed as a single
+virtual DSA by clients that are otherwise unable to "chase" (i.e. follow)
+referrals by themselves.
+
+The chain overlay is built on top of the ldap backend; it is compiled by
+default when {{B:--enable-ldap}}.
+
+
+H3: Chaining Configuration
+
+In order to demonstrate how this overlay works, we shall discuss a typical
+scenario which might be one provider server and three Syncrepl replicas.
+
+On each replica, add this near the top of the {{slapd.conf}}(5) file
+(global), before any database definitions:
+
+> overlay chain
+> chain-uri "ldap://ldapprovider.example.com"
+> chain-idassert-bind bindmethod="simple"
+> binddn="cn=Manager,dc=example,dc=com"
+> credentials="<secret>"
+> mode="self"
+> chain-tls start
+> chain-return-error TRUE
+
+Add this below your {{syncrepl}} statement:
+
+> updateref "ldap://ldapprovider.example.com/"
+
+The {{B:chain-tls}} statement enables TLS from the replica to the ldap provider.
+The DITs are exactly the same between these machines, therefore whatever user
+bound to the replica will also exist on the provider. If that DN does not have
+update privileges on the provider, nothing will happen.
+
+You will need to restart the replica after these {{slapd.conf}} changes.
+Then, if you are using {{loglevel stats}} (256), you can monitor an
+{{ldapmodify}} on the replica and the provider. (If you're using {{cn=config}}
+no restart is required.)
+
+Now start an {{ldapmodify}} on the replica and watch the logs. You should expect
+something like:
+
+> Sep 6 09:27:25 replica1 slapd[29274]: conn=11 fd=31 ACCEPT from IP=143.199.102.216:45181 (IP=143.199.102.216:389)
+> Sep 6 09:27:25 replica1 slapd[29274]: conn=11 op=0 STARTTLS
+> Sep 6 09:27:25 replica1 slapd[29274]: conn=11 op=0 RESULT oid= err=0 text=
+> Sep 6 09:27:25 replica1 slapd[29274]: conn=11 fd=31 TLS established tls_ssf=256 ssf=256
+> Sep 6 09:27:28 replica1 slapd[29274]: conn=11 op=1 BIND dn="uid=user1,ou=people,dc=example,dc=com" method=128
+> Sep 6 09:27:28 replica1 slapd[29274]: conn=11 op=1 BIND dn="uid=user1,ou=People,dc=example,dc=com" mech=SIMPLE ssf=0
+> Sep 6 09:27:28 replica1 slapd[29274]: conn=11 op=1 RESULT tag=97 err=0 text=
+> Sep 6 09:27:28 replica1 slapd[29274]: conn=11 op=2 MOD dn="uid=user1,ou=People,dc=example,dc=com"
+> Sep 6 09:27:28 replica1 slapd[29274]: conn=11 op=2 MOD attr=mail
+> Sep 6 09:27:28 replica1 slapd[29274]: conn=11 op=2 RESULT tag=103 err=0 text=
+> Sep 6 09:27:28 replica1 slapd[29274]: conn=11 op=3 UNBIND
+> Sep 6 09:27:28 replica1 slapd[29274]: conn=11 fd=31 closed
+> Sep 6 09:27:28 replica1 slapd[29274]: syncrepl_entry: LDAP_RES_SEARCH_ENTRY(LDAP_SYNC_MODIFY)
+> Sep 6 09:27:28 replica1 slapd[29274]: syncrepl_entry: be_search (0)
+> Sep 6 09:27:28 replica1 slapd[29274]: syncrepl_entry: uid=user1,ou=People,dc=example,dc=com
+> Sep 6 09:27:28 replica1 slapd[29274]: syncrepl_entry: be_modify (0)
+
+And on the provider you will see this:
+
+> Sep 6 09:23:57 ldapprovider slapd[2961]: conn=55902 op=3 PROXYAUTHZ dn="uid=user1,ou=people,dc=example,dc=com"
+> Sep 6 09:23:57 ldapprovider slapd[2961]: conn=55902 op=3 MOD dn="uid=user1,ou=People,dc=example,dc=com"
+> Sep 6 09:23:57 ldapprovider slapd[2961]: conn=55902 op=3 MOD attr=mail
+> Sep 6 09:23:57 ldapprovider slapd[2961]: conn=55902 op=3 RESULT tag=103 err=0 text=
+
+Note: You can clearly see the PROXYAUTHZ line on the provider, indicating the
+proper identity assertion for the update on the provider. Also note the replica
+immediately receiving the Syncrepl update from the provider.
+
+H3: Handling Chaining Errors
+
+By default, if chaining fails, the original referral is returned to the client
+under the assumption that the client might want to try and follow the referral.
+
+With the following directive however, if the chaining fails at the provider
+side, the actual error is returned to the client.
+
+> chain-return-error TRUE
+
+
+H3: Read-Back of Chained Modifications
+
+Occasionally, applications want to read back the data that they just wrote.
+If a modification requested to a shadow server was silently chained to its
+provider, an immediate read could result in receiving data not yet synchronized.
+In those cases, clients should use the {{B:dontusecopy}} control to ensure
+they are directed to the authoritative source for that piece of data.
+
+This control usually causes a referral to the actual source of the data
+to be returned. However, when the {{slapo-chain(5)}} overlay is used,
+it intercepts the referral being returned in response to the
+{{B:dontusecopy}} control, and tries to fetch the requested data.
+
+
+H3: Further Information
+
+{{:slapo-chain(5)}}
+
+
+H2: Constraints
+
+
+H3: Overview
+
+This overlay enforces a regular expression constraint on all values
+of specified attributes during an LDAP modify request that contains add or modify
+commands. It is used to enforce a more rigorous syntax when the underlying attribute
+syntax is too general.
+
+
+H3: Constraint Configuration
+
+Configuration via {{slapd.conf}}(5) would look like:
+
+> overlay constraint
+> constraint_attribute mail regex ^[[:alnum:]]+@mydomain.com$
+> constraint_attribute title uri
+> ldap:///dc=catalog,dc=example,dc=com?title?sub?(objectClass=titleCatalog)
+
+A specification like the above would reject any {{mail}} attribute which did not
+look like {{<alphanumeric string>@mydomain.com}}.
+
+It would also reject any title attribute whose values were not listed in the
+title attribute of any {{titleCatalog}} entries in the given scope.
+
+An example for use with {{cn=config}}:
+
+> dn: olcOverlay=constraint,olcDatabase={1}mdb,cn=config
+> changetype: add
+> objectClass: olcOverlayConfig
+> objectClass: olcConstraintConfig
+> olcOverlay: constraint
+> olcConstraintAttribute: mail regex ^[[:alnum:]]+@mydomain.com$
+> olcConstraintAttribute: title uri ldap:///dc=catalog,dc=example,dc=com?title?sub?(objectClass=titleCatalog)
+
+
+H3: Further Information
+
+{{:slapo-constraint(5)}}
+
+
+H2: Dynamic Directory Services
+
+
+H3: Overview
+
+The {{dds}} overlay to {{slapd}}(8) implements dynamic objects as per {{REF:RFC2589}}.
+The name {{dds}} stands for Dynamic Directory Services. It allows to define
+dynamic objects, characterized by the {{dynamicObject}} objectClass.
+
+Dynamic objects have a limited lifetime, determined by a time-to-live (TTL)
+that can be refreshed by means of a specific refresh extended operation. This
+operation allows to set the Client Refresh Period (CRP), namely the period
+between refreshes that is required to preserve the dynamic object from expiration.
+The expiration time is computed by adding the requested TTL to the current time.
+When dynamic objects reach the end of their lifetime without being further
+refreshed, they are automatically {{deleted}}. There is no guarantee of immediate
+deletion, so clients should not count on it.
+
+H3: Dynamic Directory Service Configuration
+
+A usage of dynamic objects might be to implement dynamic meetings; in this case,
+all the participants to the meeting are allowed to refresh the meeting object,
+but only the creator can delete it (otherwise it will be deleted when the TTL expires).
+
+If we add the overlay to an example database, specifying a Max TTL of 1 day, a
+min of 10 seconds, with a default TTL of 1 hour. We'll also specify an interval
+of 120 (less than 60s might be too small) seconds between expiration checks and a
+tolerance of 5 second (lifetime of a dynamic object will be {{entryTtl + tolerance}}).
+
+> overlay dds
+> dds-max-ttl 1d
+> dds-min-ttl 10s
+> dds-default-ttl 1h
+> dds-interval 120s
+> dds-tolerance 5s
+
+and add an index:
+
+> entryExpireTimestamp
+
+Creating a meeting is as simple as adding the following:
+
+> dn: cn=OpenLDAP Documentation Meeting,ou=Meetings,dc=example,dc=com
+> objectClass: groupOfNames
+> objectClass: dynamicObject
+> cn: OpenLDAP Documentation Meeting
+> member: uid=ghenry,ou=People,dc=example,dc=com
+> member: uid=hyc,ou=People,dc=example,dc=com
+
+H4: Dynamic Directory Service ACLs
+
+Allow users to start a meeting and to join it; restrict refresh to the {{member}};
+restrict delete to the creator:
+
+> access to attrs=userPassword
+> by self write
+> by * read
+>
+> access to dn.base="ou=Meetings,dc=example,dc=com"
+> attrs=children
+> by users write
+>
+> access to dn.onelevel="ou=Meetings,dc=example,dc=com"
+> attrs=entry
+> by dnattr=creatorsName write
+> by * read
+>
+> access to dn.onelevel="ou=Meetings,dc=example,dc=com"
+> attrs=participant
+> by dnattr=creatorsName write
+> by users selfwrite
+> by * read
+>
+> access to dn.onelevel="ou=Meetings,dc=example,dc=com"
+> attrs=entryTtl
+> by dnattr=member manage
+> by * read
+
+In simple terms, the user who created the {{OpenLDAP Documentation Meeting}} can add new attendees,
+refresh the meeting using (basically complete control):
+
+> ldapexop -x -H ldap://ldaphost "refresh" "cn=OpenLDAP Documentation Meeting,ou=Meetings,dc=example,dc=com" "120" -D "uid=ghenry,ou=People,dc=example,dc=com" -W
+
+Any user can join the meeting, but not add another attendee, but they can refresh the meeting. The ACLs above are quite straight forward to understand.
+
+
+H3: Further Information
+
+{{:slapo-dds(5)}}
+
+
+H2: Dynamic Groups
+
+
+H3: Overview
+
+This overlay extends the Compare operation to detect
+members of a dynamic group. This overlay is now deprecated
+as all of its functions are available using the
+{{SECT:Dynamic Lists}} overlay.
+
+
+H3: Dynamic Group Configuration
+
+
+H2: Dynamic Lists
+
+
+H3: Overview
+
+This overlay allows expansion of dynamic groups and lists. Instead of having the
+group members or list attributes hard coded, this overlay allows us to define
+an LDAP search whose results will make up the group or list.
+
+H3: Dynamic List Configuration
+
+This module can behave both as a dynamic list and dynamic group, depending on
+the configuration. The syntax is as follows:
+
+> overlay dynlist
+> dynlist-attrset <group-oc> <URL-ad> [member-ad]
+
+The parameters to the {{F:dynlist-attrset}} directive have the following meaning:
+* {{F:<group-oc>}}: specifies which object class triggers the subsequent LDAP search.
+Whenever an entry with this object class is retrieved, the search is performed.
+* {{F:<URL-ad>}}: is the name of the attribute which holds the search URI. It
+has to be a subtype of {{F:labeledURI}}. The attributes and values present in
+the search result are added to the entry unless {{F:member-ad}} is used (see
+below).
+* {{F:member-ad}}: if present, changes the overlay behavior into a dynamic group.
+Instead of inserting the results of the search in the entry, the distinguished name
+of the results are added as values of this attribute.
+
+Here is an example which will allow us to have an email alias which automatically
+expands to all user's emails according to our LDAP filter:
+
+In {{slapd.conf}}(5):
+
+> overlay dynlist
+> dynlist-attrset nisMailAlias labeledURI
+
+This means that whenever an entry which has the {{F:nisMailAlias}} object class is
+retrieved, the search specified in the {{F:labeledURI}} attribute is performed.
+
+Let's say we have this entry in our directory:
+
+> cn=all,ou=aliases,dc=example,dc=com
+> cn: all
+> objectClass: nisMailAlias
+> labeledURI: ldap:///ou=People,dc=example,dc=com?mail?one?(objectClass=inetOrgPerson)
+
+If this entry is retrieved, the search specified in {{F:labeledURI}} will be
+performed and the results will be added to the entry just as if they have always
+been there. In this case, the search filter selects all entries directly
+under {{F:ou=People}} that have the {{F:inetOrgPerson}} object class and retrieves
+the {{F:mail}} attribute, if it exists.
+
+This is what gets added to the entry when we have two users under {{F:ou=People}}
+that match the filter:
+!import "allmail-en.png"; align="center"; title="Dynamic list for email aliases"
+FT[align="Center"] Figure X.Y: Dynamic List for all emails
+
+The configuration for a dynamic group is similar. Let's see an example which would
+automatically populate an {{F:allusers}} group with all the user accounts in the
+directory.
+
+In {{F:slapd.conf}}(5):
+
+> include /path/to/dyngroup.schema
+> ...
+> overlay dynlist
+> dynlist-attrset groupOfURLs labeledURI member
+
+Note: We must include the {{F:dyngroup.schema}} file that defines the {{F:groupOfURLs}}
+objectClass used in this example.
+
+Let's apply it to the following entry:
+
+> cn=allusers,ou=group,dc=example,dc=com
+> cn: all
+> objectClass: groupOfURLs
+> labeledURI: ldap:///ou=people,dc=example,dc=com??one?(objectClass=inetOrgPerson)
+
+The behavior is similar to the dynamic list configuration we had before:
+whenever an entry with the {{F:groupOfURLs}} object class is retrieved, the
+search specified in the {{F:labeledURI}} attribute is performed. But this time,
+only the distinguished names of the results are added, and as values of the
+{{F:member}} attribute.
+
+This is what we get:
+!import "allusersgroup-en.png"; align="center"; title="Dynamic group for all users"
+FT[align="Center"] Figure X.Y: Dynamic Group for all users
+
+Note that a side effect of this scheme of dynamic groups is that the members
+need to be specified as full DNs. So, if you are planning in using this for
+{{F:posixGroup}}s, be sure to use RFC2307bis and some attribute which can hold
+distinguished names. The {{F:memberUid}} attribute used in the {{F:posixGroup}}
+object class can hold only names, not DNs, and is therefore not suitable for
+dynamic groups.
+
+
+H3: Further Information
+
+{{:slapo-dynlist(5)}}
+
+
+H2: Reverse Group Membership Maintenance
+
+H3: Overview
+
+In some scenarios, it may be desirable for a client to be able to determine
+which groups an entry is a member of, without performing an additional search.
+Examples of this are applications using the {{TERM:DIT}} for access control
+based on group authorization.
+
+The {{B:memberof}} overlay updates an attribute (by default {{B:memberOf}}) whenever
+changes occur to the membership attribute (by default {{B:member}}) of entries of the
+objectclass (by default {{B:groupOfNames}}) configured to trigger updates.
+
+Thus, it provides maintenance of the list of groups an entry is a member of,
+when usual maintenance of groups is done by modifying the members on the group
+entry.
+
+H3: Member Of Configuration
+
+The typical use of this overlay requires just enabling the overlay for a
+specific database. For example, with the following minimal slapd.conf:
+
+> include /usr/share/openldap/schema/core.schema
+> include /usr/share/openldap/schema/cosine.schema
+>
+> authz-regexp "gidNumber=0\\\+uidNumber=0,cn=peercred,cn=external,cn=auth"
+> "cn=Manager,dc=example,dc=com"
+> database mdb
+> suffix "dc=example,dc=com"
+> rootdn "cn=Manager,dc=example,dc=com"
+> rootpw secret
+> directory /var/lib/ldap2.5
+> checkpoint 256 5
+> index objectClass eq
+> index uid eq,sub
+>
+> overlay memberof
+
+adding the following ldif:
+
+> cat memberof.ldif
+> dn: dc=example,dc=com
+> objectclass: domain
+> dc: example
+>
+> dn: ou=Group,dc=example,dc=com
+> objectclass: organizationalUnit
+> ou: Group
+>
+> dn: ou=People,dc=example,dc=com
+> objectclass: organizationalUnit
+> ou: People
+>
+> dn: uid=test1,ou=People,dc=example,dc=com
+> objectclass: account
+> uid: test1
+>
+> dn: cn=testgroup,ou=Group,dc=example,dc=com
+> objectclass: groupOfNames
+> cn: testgroup
+> member: uid=test1,ou=People,dc=example,dc=com
+
+Results in the following output from a search on the test1 user:
+
+> # ldapsearch -LL -Y EXTERNAL -H ldapi:/// "(uid=test1)" -b dc=example,dc=com memberOf
+> SASL/EXTERNAL authentication started
+> SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
+> SASL SSF: 0
+> version: 1
+>
+> dn: uid=test1,ou=People,dc=example,dc=com
+> memberOf: cn=testgroup,ou=Group,dc=example,dc=com
+
+Note that the {{B:memberOf}} attribute is an operational attribute, so it must be
+requested explicitly.
+
+
+H3: Further Information
+
+{{:slapo-memberof(5)}}
+
+
+H2: The Proxy Cache Engine
+
+{{TERM:LDAP}} servers typically hold one or more subtrees of a
+{{TERM:DIT}}. Replica (or shadow) servers hold shadow copies of
+entries held by one or more provider servers. Changes are propagated
+from the provider server to replica servers using LDAP Sync
+replication. An LDAP cache is a special type of replica which holds
+entries corresponding to search filters instead of subtrees.
+
+H3: Overview
+
+The proxy cache extension of slapd is designed to improve the
+responsiveness of the ldap and meta backends. It handles a search
+request (query)
+by first determining whether it is contained in any cached search
+filter. Contained requests are answered from the proxy cache's local
+database. Other requests are passed on to the underlying ldap or
+meta backend and processed as usual.
+
+E.g. {{EX:(shoesize>=9)}} is contained in {{EX:(shoesize>=8)}} and
+{{EX:(sn=Richardson)}} is contained in {{EX:(sn=Richards*)}}
+
+Correct matching rules and syntaxes are used while comparing
+assertions for query containment. To simplify the query containment
+problem, a list of cacheable "templates" (defined below) is specified
+at configuration time. A query is cached or answered only if it
+belongs to one of these templates. The entries corresponding to
+cached queries are stored in the proxy cache local database while
+its associated meta information (filter, scope, base, attributes)
+is stored in main memory.
+
+A template is a prototype for generating LDAP search requests.
+Templates are described by a prototype search filter and a list of
+attributes which are required in queries generated from the template.
+The representation for prototype filter is similar to {{REF:RFC4515}},
+except that the assertion values are missing. Examples of prototype
+filters are: (sn=),(&(sn=)(givenname=)) which are instantiated by
+search filters (sn=Doe) and (&(sn=Doe)(givenname=John)) respectively.
+
+The cache replacement policy removes the least recently used (LRU)
+query and entries belonging to only that query. Queries are allowed
+a maximum time to live (TTL) in the cache thus providing weak
+consistency. A background task periodically checks the cache for
+expired queries and removes them.
+
+The Proxy Cache paper
+({{URL:http://www.openldap.org/pub/kapurva/proxycaching.pdf}}) provides
+design and implementation details.
+
+
+H3: Proxy Cache Configuration
+
+The cache configuration specific directives described below must
+appear after a {{EX:overlay pcache}} directive within a
+{{EX:"database meta"}} or {{EX:"database ldap"}} section of
+the server's {{slapd.conf}}(5) file.
+
+H4: Setting cache parameters
+
+> pcache <DB> <maxentries> <nattrsets> <entrylimit> <period>
+
+This directive enables proxy caching and sets general cache
+parameters. The <DB> parameter specifies which underlying database
+is to be used to hold cached entries. It should be set to
+{{EX:mdb}}. The <maxentries> parameter specifies the
+total number of entries which may be held in the cache. The
+<nattrsets> parameter specifies the total number of attribute sets
+(as specified by the {{EX:pcacheAttrset}} directive) that may be
+defined. The <entrylimit> parameter specifies the maximum number of
+entries in a cacheable query. The <period> specifies the consistency
+check period (in seconds). In each period, queries with expired
+TTLs are removed.
+
+H4: Defining attribute sets
+
+> pcacheAttrset <index> <attrs...>
+
+Used to associate a set of attributes to an index. Each attribute
+set is associated with an index number from 0 to <numattrsets>-1.
+These indices are used by the pcacheTemplate directive to define
+cacheable templates.
+
+H4: Specifying cacheable templates
+
+> pcacheTemplate <prototype_string> <attrset_index> <TTL>
+
+Specifies a cacheable template and the "time to live" (in sec) <TTL>
+for queries belonging to the template. A template is described by
+its prototype filter string and set of required attributes identified
+by <attrset_index>.
+
+
+H4: Example for slapd.conf
+
+An example {{slapd.conf}}(5) database section for a caching server
+which proxies for the {{EX:"dc=example,dc=com"}} subtree held
+at server {{EX:ldap.example.com}}.
+
+> database ldap
+> suffix "dc=example,dc=com"
+> rootdn "dc=example,dc=com"
+> uri ldap://ldap.example.com/
+> overlay pcache
+> pcache mdb 100000 1 1000 100
+> pcacheAttrset 0 mail postaladdress telephonenumber
+> pcacheTemplate (sn=) 0 3600
+> pcacheTemplate (&(sn=)(givenName=)) 0 3600
+> pcacheTemplate (&(departmentNumber=)(secretary=*)) 0 3600
+>
+> cachesize 20
+> directory ./testrun/db.2.a
+> index objectClass eq
+> index cn,sn,uid,mail pres,eq,sub
+
+H4: Example for slapd-config
+
+The same example as a LDIF file for back-config for a caching server
+which proxies for the {{EX:"dc=example,dc=com"}} subtree held
+at server {{EX:ldap.example.com}}.
+
+> dn: olcDatabase={2}ldap,cn=config
+> objectClass: olcDatabaseConfig
+> objectClass: olcLDAPConfig
+> olcDatabase: {2}ldap
+> olcSuffix: dc=example,dc=com
+> olcRootDN: dc=example,dc=com
+> olcDbURI: "ldap://ldap.example.com"
+>
+> dn: olcOverlay={0}pcache,olcDatabase={2}ldap,cn=config
+> objectClass: olcOverlayConfig
+> objectClass: olcPcacheConfig
+> olcOverlay: {0}pcache
+> olcPcache: mdb 100000 1 1000 100
+> olcPcacheAttrset: 0 mail postalAddress telephoneNumber
+> olcPcacheTemplate: "(sn=)" 0 3600 0 0 0
+> olcPcacheTemplate: "(&(sn=)(givenName=))" 0 3600 0 0 0
+> olcPcacheTemplate: "(&(departmentNumber=)(secretary=))" 0 3600
+>
+> dn: olcDatabase={0}mdb,olcOverlay={0}pcache,olcDatabase={2}ldap,cn=config
+> objectClass: olcMdbConfig
+> objectClass: olcPcacheDatabase
+> olcDatabase: {0}mdb
+> olcDbDirectory: ./testrun/db.2.a
+> olcDbCacheSize: 20
+> olcDbIndex: objectClass eq
+> olcDbIndex: cn,sn,uid,mail pres,eq,sub
+
+
+H5: Cacheable Queries
+
+A LDAP search query is cacheable when its filter matches one of the
+templates as defined in the "pcacheTemplate" statements and when it references
+only the attributes specified in the corresponding attribute set.
+In the example above the attribute set number 0 defines that only the
+attributes: {{EX:mail postaladdress telephonenumber}} are cached for the following
+pcacheTemplates.
+
+H5: Examples:
+
+> Filter: (&(sn=Richard*)(givenName=jack))
+> Attrs: mail telephoneNumber
+
+ is cacheable, because it matches the template {{EX:(&(sn=)(givenName=))}} and its
+ attributes are contained in pcacheAttrset 0.
+
+> Filter: (&(sn=Richard*)(telephoneNumber))
+> Attrs: givenName
+
+ is not cacheable, because the filter does not match the template,
+ nor is the attribute givenName stored in the cache
+
+> Filter: (|(sn=Richard*)(givenName=jack))
+> Attrs: mail telephoneNumber
+
+ is not cacheable, because the filter does not match the template ( logical
+ OR "|" condition instead of logical AND "&" )
+
+
+H3: Further Information
+
+{{:slapo-pcache(5)}}
+
+
+H2: Password Policies
+
+
+H3: Overview
+
+This overlay follows the specifications contained in the draft RFC titled
+draft-behera-ldap-password-policy-09. While the draft itself is expired, it has
+been implemented in several directory servers, including slapd. Nonetheless,
+it is important to note that it is a draft, meaning that it is subject to change
+and is a work-in-progress.
+
+The key abilities of the password policy overlay are as follows:
+
+* Enforce a minimum length for new passwords
+* Make sure passwords are not changed too frequently
+* Cause passwords to expire, provide warnings before they need to be changed, and allow a fixed number of 'grace' logins to allow them to be changed after they have expired
+* Maintain a history of passwords to prevent password re-use
+* Prevent password guessing by locking a password for a specified period of time after repeated authentication failures
+* Force a password to be changed at the next authentication
+* Set an administrative lock on an account
+* Support multiple password policies on a default or a per-object basis.
+* Perform arbitrary quality checks using an external loadable module. This is a non-standard extension of the draft RFC.
+
+
+H3: Password Policy Configuration
+
+Instantiate the module in the database where it will be used, after adding the
+new ppolicy schema and loading the ppolicy module. The following example shows
+the ppolicy module being added to the database that handles the naming
+context "dc=example,dc=com". In this example we are also specifying the DN of
+a policy object to use if none other is specified in a user's object.
+
+> database mdb
+> suffix "dc=example,dc=com"
+> [...additional database configuration directives go here...]
+>
+> overlay ppolicy
+> ppolicy_default "cn=default,ou=policies,dc=example,dc=com"
+
+
+Now we need a container for the policy objects. In our example the password
+policy objects are going to be placed in a section of the tree called
+"ou=policies,dc=example,dc=com":
+
+> dn: ou=policies,dc=example,dc=com
+> objectClass: organizationalUnit
+> objectClass: top
+> ou: policies
+
+
+The default policy object that we are creating defines the following policies:
+
+* The user is allowed to change his own password. Note that the directory ACLs for this attribute can also affect this ability (pwdAllowUserChange: TRUE).
+* The name of the password attribute is "userPassword" (pwdAttribute: userPassword). Note that this is the only value that is accepted by OpenLDAP for this attribute.
+* The server will check the syntax of the password. If the server is unable to check the syntax (i.e., it was hashed or otherwise encoded by the client) it will return an error refusing the password (pwdCheckQuality: 2).
+* When a client includes the Password Policy Request control with a bind request, the server will respond with a password expiration warning if it is going to expire in ten minutes or less (pwdExpireWarning: 600). The warnings themselves are returned in a Password Policy Response control.
+* When the password for a DN has expired, the server will allow five additional "grace" logins (pwdGraceAuthNLimit: 5).
+* The server will maintain a history of the last five passwords that were used for a DN (pwdInHistory: 5).
+* The server will lock the account after the maximum number of failed bind attempts has been exceeded (pwdLockout: TRUE).
+* When the server has locked an account, the server will keep it locked until an administrator unlocks it (pwdLockoutDuration: 0)
+* The server will reset its failed bind count after a period of 30 seconds.
+* Passwords will not expire (pwdMaxAge: 0).
+* Passwords can be changed as often as desired (pwdMinAge: 0).
+* Passwords must be at least 5 characters in length (pwdMinLength: 5).
+* The password does not need to be changed at the first bind or when the administrator has reset the password (pwdMustChange: FALSE)
+* The current password does not need to be included with password change requests (pwdSafeModify: FALSE)
+* The server will only allow five failed binds in a row for a particular DN (pwdMaxFailure: 5).
+
+
+The actual policy would be:
+
+> dn: cn=default,ou=policies,dc=example,dc=com
+> cn: default
+> objectClass: pwdPolicy
+> objectClass: namedPolicy
+> objectClass: top
+> pwdAllowUserChange: TRUE
+> pwdAttribute: userPassword
+> pwdCheckQuality: 2
+> pwdExpireWarning: 600
+> pwdFailureCountInterval: 30
+> pwdGraceAuthNLimit: 5
+> pwdInHistory: 5
+> pwdLockout: TRUE
+> pwdLockoutDuration: 0
+> pwdMaxAge: 0
+> pwdMaxFailure: 5
+> pwdMinAge: 0
+> pwdMinLength: 5
+> pwdMustChange: FALSE
+> pwdSafeModify: FALSE
+
+You can create additional policy objects as needed.
+
+The namedPolicy object class is present because the policy entry
+requires a structural object class.
+
+There are two ways password policy can be applied to individual objects:
+
+1. The pwdPolicySubentry in a user's object - If a user's object has a
+pwdPolicySubEntry attribute specifying the DN of a policy object, then
+the policy defined by that object is applied.
+
+2. Default password policy - If there is no specific pwdPolicySubentry set
+for an object, and the password policy module was configured with the DN of a
+default policy object and if that object exists, then the policy defined in
+that object is applied.
+
+Please see {{slapo-ppolicy(5)}} for a complete explanation of its features.
+
+A guiding philosophy for OpenLDAP and directory servers in general has been
+that they always hand back exactly what they were given, without
+modification. For example, if the cn attribute of an object was set to fOObaR,
+the server will return that exact string during a search. Values of attributes
+of a sensitive nature, such as userPassword, are often hashed to conceal their
+values. Since the userPassword values are used internally by the directory
+server to authenticate users, any hash algorithm that is applied to the value
+must be compatible with the directory server. Historically this problem has
+been solved by making the LDAP client application be able to hash the
+userPassword attribute value in a way that is compatible with the directory
+server, but this solution has the obvious drawback of requiring tight coupling
+between the LDAP client and server, and limits the choices of usable hashing
+algorithms to those that are accommodated by both. This is clearly a
+sub-optimal solution.
+
+In 2001 RFC 3062 became a standard that specified an LDAP extended operation
+for cases like this. Extended operations are not bound by the
+return-what-you-are-given philosophy and so are free to do things to attribute
+values that the add and modify operations cannot. The change password extended
+operation accepts a plaintext password and hashes it based on a specification
+that is contained in the server. This allows the server to be in control of
+the hashing algorithm which, in turn, ensures that any hashes applied to
+userPassword attribute values will not prevent users from being authenticated.
+
+The password policy module's ppolicy_hash_cleartext flag addresses this
+problem by intercepting LDAP modify operations that include the userPassword
+attribute and converting them to change password extended operations so they
+can be hashed according to the specification contained in slapd's
+configuration. When this flag is set, LDAP applications that modify the
+userPassword attribute can send the password in cleartext form to the server
+using a standard LDAP modify command and the server will hash the value
+according to the password-hash directive before storing it. It goes without
+saying that steps need to be taken to protect the cleartext password in
+transit, such as using SSL, TLS, or some other link encryption method.
+
+The following example shows the ppolicy module configured to hash cleartext
+passwords:
+
+> database mdb
+> suffix "dc=example,dc=com"
+> [...additional database configuration directives go here...]
+>
+> overlay ppolicy
+> ppolicy_default "cn=default,ou=policies,dc=example,dc=com"
+> ppolicy_hash_cleartext
+
+
+H3: Further Information
+
+{{:slapo-ppolicy(5)}}
+
+
+H2: Referential Integrity
+
+
+H3: Overview
+
+This overlay can be used with a backend database such as slapd-mdb(5)
+to maintain the cohesiveness of a schema which utilizes reference
+attributes.
+
+Whenever a {{modrdn}} or {{delete}} is performed, that is, when an entry's DN
+is renamed or an entry is removed, the server will search the directory for
+references to this DN (in selected attributes: see below) and update them
+accordingly. If it was a {{delete}} operation, the reference is deleted. If it
+was a {{modrdn}} operation, then the reference is updated with the new DN.
+
+For example, a very common administration task is to maintain group membership
+lists, specially when users are removed from the directory. When an
+user account is deleted or renamed, all groups this user is a member of have to be
+updated. LDAP administrators usually have scripts for that. But we can use the
+{{F:refint}} overlay to automate this task. In this example, if the user is
+removed from the directory, the overlay will take care to remove the user from
+all the groups he/she was a member of. No more scripting for this.
+
+H3: Referential Integrity Configuration
+
+The configuration for this overlay is as follows:
+
+> overlay refint
+> refint_attributes <attribute [attribute ...]>
+> refint_nothing <string>
+
+* {{F:refint_attributes}}: this parameter specifies a space separated list of
+attributes which will have the referential integrity maintained. When an entry is
+removed or has its DN renamed, the server will do an internal search for any of the
+{{F:refint_attributes}} that point to the affected DN and update them accordingly. IMPORTANT:
+the attributes listed here must have the {{F:distinguishedName}} syntax, that is,
+hold DNs as values.
+* {{F:refint_nothing}}: some times, while trying to maintain the referential
+integrity, the server has to remove the last attribute of its kind from an
+entry. This may be prohibited by the schema: for example, the
+{{F:groupOfNames}} object class requires at least one member. In these cases,
+the server will add the attribute value specified in {{F:refint_nothing}}
+to the entry.
+
+To illustrate this overlay, we will use the group membership scenario.
+
+In {{F:slapd.conf}}:
+
+> overlay refint
+> refint_attributes member
+> refint_nothing "cn=admin,dc=example,dc=com"
+
+This configuration tells the overlay to maintain the referential integrity of the {{F:member}}
+attribute. This attribute is used in the {{F:groupOfNames}} object class which always needs
+a member, so we add the {{F:refint_nothing}} directive to fill in the group with a standard
+member should all the members vanish.
+
+If we have the following group membership, the refint overlay will
+automatically remove {{F:john}} from the group if his entry is removed from the
+directory:
+
+!import "refint.png"; align="center"; title="Group membership"
+FT[align="Center"] Figure X.Y: Maintaining referential integrity in groups
+
+Notice that if we rename ({{F:modrdn}}) the {{F:john}} entry to, say, {{F:jsmith}}, the refint
+overlay will also rename the reference in the {{F:member}} attribute, so the group membership
+stays correct.
+
+If we removed all users from the directory who are a member of this group, then the end result
+would be a single member in the group: {{F:cn=admin,dc=example,dc=com}}. This is the
+{{F:refint_nothing}} parameter kicking into action so that the schema is not violated.
+
+The {{rootdn}} must be set for the database as refint runs as the {{rootdn}} to gain access to
+make its updates. The {{rootpw}} does not need to be set.
+
+H3: Further Information
+
+{{:slapo-refint(5)}}
+
+
+H2: Return Code
+
+
+H3: Overview
+
+This overlay is useful to test the behavior of clients when
+server-generated erroneous and/or unusual responses occur,
+for example; error codes, referrals, excessive response times and so on.
+
+This would be classed as a debugging tool whilst developing client software
+or additional Overlays.
+
+For detailed information, please see the {{slapo-retcode(5)}} man page.
+
+
+H3: Return Code Configuration
+
+The retcode overlay utilizes the "return code" schema described in the man page.
+This schema is specifically designed for use with this overlay and is not intended
+to be used otherwise.
+
+Note: The necessary schema is loaded automatically by the overlay.
+
+An example configuration might be:
+
+> overlay retcode
+> retcode-parent "ou=RetCodes,dc=example,dc=com"
+> include ./retcode.conf
+>
+> retcode-item "cn=Unsolicited" 0x00 unsolicited="0"
+> retcode-item "cn=Notice of Disconnect" 0x00 unsolicited="1.3.6.1.4.1.1466.20036"
+> retcode-item "cn=Pre-disconnect" 0x34 flags="pre-disconnect"
+> retcode-item "cn=Post-disconnect" 0x34 flags="post-disconnect"
+
+Note: {{retcode.conf}} can be found in the openldap source at: {{F:tests/data/retcode.conf}}
+
+An excerpt of a {{F:retcode.conf}} would be something like:
+
+> retcode-item "cn=success" 0x00
+>
+> retcode-item "cn=success w/ delay" 0x00 sleeptime=2
+>
+> retcode-item "cn=operationsError" 0x01
+> retcode-item "cn=protocolError" 0x02
+> retcode-item "cn=timeLimitExceeded" 0x03 op=search
+> retcode-item "cn=sizeLimitExceeded" 0x04 op=search
+> retcode-item "cn=compareFalse" 0x05 op=compare
+> retcode-item "cn=compareTrue" 0x06 op=compare
+> retcode-item "cn=authMethodNotSupported" 0x07
+> retcode-item "cn=strongAuthNotSupported" 0x07 text="same as authMethodNotSupported"
+> retcode-item "cn=strongAuthRequired" 0x08
+> retcode-item "cn=strongerAuthRequired" 0x08 text="same as strongAuthRequired"
+
+Please see {{F:tests/data/retcode.conf}} for a complete {{F:retcode.conf}}
+
+
+H3: Further Information
+
+{{:slapo-retcode(5)}}
+
+
+H2: Rewrite/Remap
+
+
+H3: Overview
+
+It performs basic DN/data rewrite and objectClass/attributeType mapping. Its
+usage is mostly intended to provide virtual views of existing data either
+remotely, in conjunction with the proxy backend described in {{slapd-ldap(5)}},
+or locally, in conjunction with the relay backend described in {{slapd-relay(5)}}.
+
+This overlay is extremely configurable and advanced, therefore recommended
+reading is the {{slapo-rwm(5)}} man page.
+
+
+H3: Rewrite/Remap Configuration
+
+
+H3: Further Information
+
+{{:slapo-rwm(5)}}
+
+
+H2: Sync Provider
+
+
+H3: Overview
+
+This overlay implements the provider-side support for the LDAP Content Synchronization
+({{REF:RFC4533}}) as well as syncrepl replication support, including persistent search functionality.
+
+H3: Sync Provider Configuration
+
+There is very little configuration needed for this overlay, in fact for many situations merely loading
+the overlay will suffice.
+
+However, because the overlay creates a contextCSN attribute in the root entry of the database which is
+updated for every write operation performed against the database and only updated in memory, it is
+recommended to configure a checkpoint so that the contextCSN is written into the underlying database to
+minimize recovery time after an unclean shutdown:
+
+> overlay syncprov
+> syncprov-checkpoint 100 10
+
+For every 100 operations or 10 minutes, which ever is sooner, the contextCSN will be checkpointed.
+
+The four configuration directives available are {{B:syncprov-checkpoint}}, {{B:syncprov-sessionlog}},
+{{B:syncprov-nopresent}} and {{B:syncprov-reloadhint}} which are covered in the man page discussing
+various other scenarios where this overlay can be used.
+
+H3: Further Information
+
+The {{:slapo-syncprov(5)}} man page and the {{SECT:Configuring the different replication types}} section
+
+
+H2: Translucent Proxy
+
+
+H3: Overview
+
+This overlay can be used with a backend database such as {{:slapd-mdb}}(5)
+to create a "translucent proxy".
+
+Entries retrieved from a remote LDAP server may have some or all attributes
+overridden, or new attributes added, by entries in the local database before
+being presented to the client.
+
+A search operation is first populated with entries from the remote LDAP server,
+the attributes of which are then overridden with any attributes defined in the
+local database. Local overrides may be populated with the add, modify, and
+modrdn operations, the use of which is restricted to the root user of the
+translucent local database.
+
+A compare operation will perform a comparison with attributes defined in the
+local database record (if any) before any comparison is made with data in the
+remote database.
+
+
+H3: Translucent Proxy Configuration
+
+There are various options available with this overlay, but for this example we
+will demonstrate adding new attributes to a remote entry and also searching
+against these newly added local attributes. For more information about overriding remote
+entries and search configuration, please see {{:slapo-translucent(5)}}
+
+Note: The Translucent Proxy overlay will disable schema checking in the local
+database, so that an entry consisting of overlay attributes need not adhere
+ to the complete schema.
+
+First we configure the overlay in the normal manner:
+
+> include /usr/local/etc/openldap/schema/core.schema
+> include /usr/local/etc/openldap/schema/cosine.schema
+> include /usr/local/etc/openldap/schema/nis.schema
+> include /usr/local/etc/openldap/schema/inetorgperson.schema
+>
+> pidfile ./slapd.pid
+> argsfile ./slapd.args
+>
+> database mdb
+> suffix "dc=suretecsystems,dc=com"
+> rootdn "cn=trans,dc=suretecsystems,dc=com"
+> rootpw secret
+> directory ./openldap-data
+>
+> index objectClass eq
+>
+> overlay translucent
+> translucent_local carLicense
+>
+> uri ldap://192.168.X.X:389
+> lastmod off
+> acl-bind binddn="cn=admin,dc=suretecsystems,dc=com" credentials="blahblah"
+
+You will notice the overlay directive and a directive to say what attribute we
+want to be able to search against in the local database. We must also load the
+ldap backend which will connect to the remote directory server.
+
+Now we take an example LDAP group:
+
+> # itsupport, Groups, suretecsystems.com
+> dn: cn=itsupport,ou=Groups,dc=suretecsystems,dc=com
+> objectClass: posixGroup
+> objectClass: sambaGroupMapping
+> cn: itsupport
+> gidNumber: 1000
+> sambaSID: S-1-5-21-XXX
+> sambaGroupType: 2
+> displayName: itsupport
+> memberUid: ghenry
+> memberUid: joebloggs
+
+and create an LDIF file we can use to add our data to the local database, using
+ some pretty strange choices of new attributes for demonstration purposes:
+
+> [ghenry@suretec test_configs]$ cat test-translucent-add.ldif
+> dn: cn=itsupport,ou=Groups,dc=suretecsystems,dc=com
+> businessCategory: frontend-override
+> carLicense: LIVID
+> employeeType: special
+> departmentNumber: 9999999
+> roomNumber: 41L-535
+
+Searching against the proxy gives:
+
+> [ghenry@suretec test_configs]$ ldapsearch -x -H ldap://127.0.0.1:9001 "(cn=itsupport)"
+> # itsupport, Groups, OxObjects, suretecsystems.com
+> dn: cn=itsupport,ou=Groups,ou=OxObjects,dc=suretecsystems,dc=com
+> objectClass: posixGroup
+> objectClass: sambaGroupMapping
+> cn: itsupport
+> gidNumber: 1003
+> SAMBASID: S-1-5-21-XXX
+> SAMBAGROUPTYPE: 2
+> displayName: itsupport
+> memberUid: ghenry
+> memberUid: joebloggs
+> roomNumber: 41L-535
+> departmentNumber: 9999999
+> employeeType: special
+> carLicense: LIVID
+> businessCategory: frontend-override
+
+Here we can see that the 5 new attributes are added to the remote entry before
+being returned to the our client.
+
+Because we have configured a local attribute to search against:
+
+> overlay translucent
+> translucent_local carLicense
+
+we can also search for that to return the completely fabricated entry:
+
+> ldapsearch -x -H ldap://127.0.0.1:9001 (carLicense=LIVID)
+
+This is an extremely useful feature because you can then extend a remote directory server
+locally and also search against the local entries.
+
+Note: Because the translucent overlay does not perform any DN rewrites, the local
+ and remote database instances must have the same suffix. Other configurations
+will probably fail with No Such Object and other errors
+
+H3: Further Information
+
+{{:slapo-translucent(5)}}
+
+
+H2: Attribute Uniqueness
+
+
+H3: Overview
+
+This overlay can be used with a backend database such as {{slapd-mdb(5)}}
+to enforce the uniqueness of some or all attributes within a subtree.
+
+
+H3: Attribute Uniqueness Configuration
+
+This overlay is only effective on new data from the point the overlay is enabled. To
+check uniqueness for existing data, you can export and import your data again via the
+LDAP Add operation, which will not be suitable for large amounts of data, unlike {{B:slapcat}}.
+
+For the following example, if uniqueness were enforced for the {{B:mail}} attribute,
+the subtree would be searched for any other records which also have a {{B:mail}} attribute
+containing the same value presented with an {{B:add}}, {{B:modify}} or {{B:modrdn}} operation
+which are unique within the configured scope. If any are found, the request is rejected.
+
+Note: If no attributes are specified, for example {{B:ldap:///??sub?}}, then the URI applies to all non-operational attributes. However,
+the keyword {{B:ignore}} can be specified to exclude certain non-operational attributes.
+
+To search at the base dn of the current backend database ensuring uniqueness of the {{B:mail}}
+attribute, we simply add the following configuration:
+
+> overlay unique
+> unique_uri ldap:///?mail?sub?
+
+For an existing entry of:
+
+> dn: cn=gavin,dc=suretecsystems,dc=com
+> objectClass: top
+> objectClass: inetorgperson
+> cn: gavin
+> sn: henry
+> mail: ghenry@suretecsystems.com
+
+and we then try to add a new entry of:
+
+> dn: cn=robert,dc=suretecsystems,dc=com
+> objectClass: top
+> objectClass: inetorgperson
+> cn: robert
+> sn: jones
+> mail: ghenry@suretecsystems.com
+
+would result in an error like so:
+
+> adding new entry "cn=robert,dc=example,dc=com"
+> ldap_add: Constraint violation (19)
+> additional info: some attributes not unique
+
+The overlay can have multiple URIs specified within a domain, allowing complex
+selections of objects and also have multiple {{B:unique_uri}} statements or
+{{B:olcUniqueURI}} attributes which will create independent domains.
+
+For more information and details about the {{B:strict}} and {{B:ignore}} keywords,
+please see the {{:slapo-unique(5)}} man page.
+
+H3: Further Information
+
+{{:slapo-unique(5)}}
+
+
+H2: Value Sorting
+
+
+H3: Overview
+
+The Value Sorting overlay can be used with a backend database to sort the
+values of specific multi-valued attributes within a subtree. The sorting occurs
+whenever the attributes are returned in a search response.
+
+H3: Value Sorting Configuration
+
+Sorting can be specified in ascending or descending order, using either numeric
+or alphanumeric sort methods. Additionally, a "weighted" sort can be specified,
+ which uses a numeric weight prepended to the attribute values.
+
+The weighted sort is always performed in ascending order, but may be combined
+with the other methods for values that all have equal weights. The weight is
+specified by prepending an integer weight {<weight>} in front of each value
+of the attribute for which weighted sorting is desired. This weighting factor
+is stripped off and never returned in search results.
+
+Here are a few examples:
+
+> loglevel sync stats
+>
+> database mdb
+> suffix "dc=suretecsystems,dc=com"
+> directory /usr/local/var/openldap-data
+>
+> ......
+>
+> overlay valsort
+> valsort-attr memberUid ou=Groups,dc=suretecsystems,dc=com alpha-ascend
+
+For example, ascend:
+
+> # sharedemail, Groups, suretecsystems.com
+> dn: cn=sharedemail,ou=Groups,dc=suretecsystems,dc=com
+> objectClass: posixGroup
+> objectClass: top
+> cn: sharedemail
+> gidNumber: 517
+> memberUid: admin
+> memberUid: dovecot
+> memberUid: laura
+> memberUid: suretec
+
+For weighted, we change our data to:
+
+> # sharedemail, Groups, suretecsystems.com
+> dn: cn=sharedemail,ou=Groups,dc=suretecsystems,dc=com
+> objectClass: posixGroup
+> objectClass: top
+> cn: sharedemail
+> gidNumber: 517
+> memberUid: {4}admin
+> memberUid: {2}dovecot
+> memberUid: {1}laura
+> memberUid: {3}suretec
+
+and change the config to:
+
+> overlay valsort
+> valsort-attr memberUid ou=Groups,dc=suretecsystems,dc=com weighted
+
+Searching now results in:
+
+> # sharedemail, Groups, OxObjects, suretecsystems.com
+> dn: cn=sharedemail,ou=Groups,ou=OxObjects,dc=suretecsystems,dc=com
+> objectClass: posixGroup
+> objectClass: top
+> cn: sharedemail
+> gidNumber: 517
+> memberUid: laura
+> memberUid: dovecot
+> memberUid: suretec
+> memberUid: admin
+
+
+H3: Further Information
+
+{{:slapo-valsort(5)}}
+
+
+H2: Overlay Stacking
+
+
+H3: Overview
+
+Overlays can be stacked, which means that more than one overlay
+can be instantiated for each database, or for the {{EX:frontend}}.
+As a consequence, each overlays function is called, if defined,
+when overlay execution is invoked.
+Multiple overlays are executed in reverse order (as a stack)
+with respect to their definition in slapd.conf (5), or with respect
+to their ordering in the config database, as documented in slapd-config (5).
+
+
+H3: Example Scenarios
+
+
+H4: Samba
diff --git a/doc/guide/admin/preface.sdf b/doc/guide/admin/preface.sdf
new file mode 100644
index 0000000..0cbc8c3
--- /dev/null
+++ b/doc/guide/admin/preface.sdf
@@ -0,0 +1,85 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+#
+
+P1: Preface
+
+
+# document's copyright
+P2[notoc] Copyright
+
+Copyright 1998-2013, The {{ORG[expand]OLF}}, {{All Rights Reserved}}.
+
+Copyright 1992-1996, Regents of the {{ORG[expand]UM}}, {{All Rights Reserved}}.
+
+This document is considered a part of OpenLDAP Software. This
+document is subject to terms of conditions set forth in {{SECT:OpenLDAP
+Software Copyright Notices}} and the {{SECT:OpenLDAP Public License}}.
+Complete copies of the notices and associated license can be found
+in Appendix K and L, respectively.
+
+Portions of OpenLDAP Software and this document may be copyright
+by other parties and/or subject to additional restrictions. Individual
+source files should be consulted for additional copyright notices.
+
+P2[notoc] Scope of this Document
+
+This document provides a guide for installing [[DOC_NAME]]
+({{URL:http://www.openldap.org/software/}}) on {{TERM:UNIX}} (and
+UNIX-like) systems. The document is aimed at experienced system
+administrators with basic understanding of {{TERM:LDAP}}-based
+directory services.
+
+This document is meant to be used in conjunction with other OpenLDAP
+information resources provided with the software package and on the
+project's site ({{URL:http://www.OpenLDAP.org/}}) on the
+{{TERM[expand]WWW}}. The site makes available a number of resources.
+
+!block table; align=Center; coltags="N,URL"; \
+ title="OpenLDAP Resources"
+Resource|URL
+Document Catalog|http://www.OpenLDAP.org/doc/
+Frequently Asked Questions|http://www.OpenLDAP.org/faq/
+Issue Tracking System|http://www.OpenLDAP.org/its/
+Mailing Lists|http://www.OpenLDAP.org/lists/
+Manual Pages|http://www.OpenLDAP.org/software/man.cgi
+Software Pages|http://www.OpenLDAP.org/software/
+Support Pages|http://www.OpenLDAP.org/support/
+!endblock
+
+This document is not a complete reference for OpenLDAP software; the
+manual pages are the definitive documentation. For best results,
+you should use the manual pages that were installed on your system
+with your version of OpenLDAP software so that you're looking at
+documentation that matches the code. While the OpenLDAP web site
+also provides the manual pages for convenience, you can not assume
+that they correspond to the particular version you're running.
+
+P2[notoc] Acknowledgments
+
+The {{ORG[expand]OLP}} is comprised of a team of volunteers. This
+document would not be possible without their contribution of time
+and energy.
+
+The OpenLDAP Project would also like to thank the {{ORG[expand]UMLDAP}}
+for building the foundation of LDAP software and information to
+which OpenLDAP Software is built upon. This document is based upon
+University of Michigan document: {{REF[expand]UM-GUIDE}}.
+
+
+P2[notoc] Amendments
+
+Suggested enhancements and corrections to this document should be
+submitted using the {{PRD:OpenLDAP}} {{TERM[expand]ITS}}
+({{URL: http://www.openldap.org/its/}}).
+
+
+P2[notoc] About this document
+
+This document was produced using the {{TERM[expand]SDF}} ({{TERM:SDF}})
+documentation system
+({{URL:http://search.cpan.org/src/IANC/sdf-2.001/doc/catalog.html}})
+developed by {{Ian Clatworthy}}. Tools for SDF are available from
+{{ORG:CPAN}} ({{URL:http://search.cpan.org/search?query=SDF&mode=dist}}).
+
diff --git a/doc/guide/admin/push-based-complete.png b/doc/guide/admin/push-based-complete.png
new file mode 100644
index 0000000..4a1b182
--- /dev/null
+++ b/doc/guide/admin/push-based-complete.png
Binary files differ
diff --git a/doc/guide/admin/push-based-standalone.png b/doc/guide/admin/push-based-standalone.png
new file mode 100644
index 0000000..0f8c997
--- /dev/null
+++ b/doc/guide/admin/push-based-standalone.png
Binary files differ
diff --git a/doc/guide/admin/quickstart.sdf b/doc/guide/admin/quickstart.sdf
new file mode 100644
index 0000000..047d8a9
--- /dev/null
+++ b/doc/guide/admin/quickstart.sdf
@@ -0,0 +1,300 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: A Quick-Start Guide
+
+The following is a quick start guide to [[DOC_NAME]],
+including the Standalone {{TERM:LDAP}} Daemon, {{slapd}}(8).
+
+It is meant to walk you through the basic steps needed to install
+and configure {{PRD:OpenLDAP Software}}. It should be used in
+conjunction with the other chapters of this document, manual pages,
+and other materials provided with the distribution (e.g. the
+{{F:INSTALL}} document) or on the {{PRD:OpenLDAP}} web site
+({{URL: http://www.OpenLDAP.org}}), in particular the OpenLDAP
+Software {{TERM:FAQ}} ({{URL: http://www.OpenLDAP.org/faq/?file=2}}).
+
+If you intend to run OpenLDAP Software seriously, you should review
+all of this document before attempting to install the software.
+
+Note: This quick start guide does not use strong authentication
+nor any integrity or confidential protection services. These
+services are described in other chapters of the
+OpenLDAP Administrator's Guide.
+
+
+.{{S: }}
+^{{B: Get the software}}
+
+. You can obtain a copy of the software by following the
+instructions on the OpenLDAP Software download page
+({{URL: http://www.openldap.org/software/download/}}). It is
+recommended that new users start with the latest {{release}}.
+
+
+.{{S: }}
++{{B: Unpack the distribution}}
+
+.Pick a directory for the source to live under, change
+directory to there, and unpack the distribution using the
+following commands:
+
+..{{EX:gunzip -c openldap-VERSION.tgz | tar xvfB -}}
+
+. then relocate yourself into the distribution directory:
+
+..{{EX:cd openldap-VERSION}}
+
+. You'll have to replace {{F:VERSION}} with the version
+name of the release.
+
+
+.{{S: }}
++{{B: Review documentation}}
+
+. You should now review the {{F:COPYRIGHT}}, {{F:LICENSE}},
+{{F:README}} and {{F:INSTALL}} documents provided with the distribution.
+The {{F:COPYRIGHT}} and {{F:LICENSE}} provide information on
+acceptable use, copying, and limitation of warranty of OpenLDAP
+Software.
+
+.{{S: }}
+. You should also review other chapters of this document.
+In particular, the {{SECT:Building and Installing OpenLDAP Software}}
+chapter of this document provides detailed information on prerequisite
+software and installation procedures.
+
+
+.{{S: }}
++{{B: Run {{EX:configure}}}}
+
+. You will need to run the provided {{EX:configure}} script to
+{{configure}} the distribution for building on your system. The
+{{EX:configure}} script accepts many command line options that enable or
+disable optional software features. Usually the defaults are okay,
+but you may want to change them. To get a complete list of options
+that {{EX:configure}} accepts, use the {{EX:--help}} option:
+
+..{{EX:./configure --help}}
+
+. However, given that you are using this guide, we'll assume you
+are brave enough to just let {{EX:configure}} determine
+what's best:
+
+..{{EX:./configure}}
+
+. Assuming {{EX:configure}} doesn't dislike your system, you can
+proceed with building the software. If {{EX:configure}} did
+complain, well, you'll likely need to go to the Software FAQ
+{{Installation}} section ({{URL:http://www.openldap.org/faq/?file=8}})
+and/or actually read the {{SECT:Building and Installing OpenLDAP Software}}
+chapter of this document.
+
+
+.{{S: }}
++{{B:Build the software}}.
+
+. The next step is to build the software. This step has two
+parts, first we construct dependencies and then we compile the
+software:
+
+..{{EX:make depend}}
+..{{EX:make}}
+
+
+. Both makes should complete without error.
+
+
+.{{S: }}
++{{B:Test the build}}.
+
+. To ensure a correct build, you should run the test suite
+(it only takes a few minutes):
+
+..{{EX:make test}}
+
+. Tests which apply to your configuration will run and they
+should pass. Some tests, such as the replication test, may
+be skipped.
+
+
+.{{S: }}
++{{B:Install the software}}.
+
+. You are now ready to install the software; this usually requires
+{{super-user}} privileges:
+
+..{{EX:su root -c 'make install'}}
+
+. Everything should now be installed under {{F:/usr/local}} (or
+whatever installation prefix was used by {{EX:configure}}).
+
+.{{S: }}
++{{B:Edit the configuration file}}.
+
+. Use your favorite editor to edit the provided {{slapd.ldif}}
+example (usually installed as {{F:/usr/local/etc/openldap/slapd.ldif}})
+to contain a MDB database definition of the form:
+
+..{{EX:dn: olcDatabase=mdb,cn=config}}
+..{{EX:objectClass: olcDatabaseConfig}}
+..{{EX:objectClass: olcMdbConfig}}
+..{{EX:olcDatabase: mdb}}
+..{{EX:OlcDbMaxSize: 1073741824}}
+..{{EX:olcSuffix: dc=<MY-DOMAIN>,dc=<COM>}}
+..{{EX:olcRootDN: cn=Manager,dc=<MY-DOMAIN>,dc=<COM>}}
+..{{EX:olcRootPW: secret}}
+..{{EX:olcDbDirectory: /usr/local/var/openldap-data}}
+..{{EX:olcDbIndex: objectClass eq}}
+
+. Be sure to replace {{EX:<MY-DOMAIN>}} and {{EX:<COM>}} with
+the appropriate domain components of your domain name. For
+example, for {{EX:example.com}}, use:
+
+..{{EX:dn: olcDatabase=mdb,cn=config}}
+..{{EX:objectClass: olcDatabaseConfig}}
+..{{EX:objectClass: olcMdbConfig}}
+..{{EX:olcDatabase: mdb}}
+..{{EX:OlcDbMaxSize: 1073741824}}
+..{{EX:olcSuffix: dc=example,dc=com}}
+..{{EX:olcRootDN: cn=Manager,dc=example,dc=com}}
+..{{EX:olcRootPW: secret}}
+..{{EX:olcDbDirectory: /usr/local/var/openldap-data}}
+..{{EX:olcDbIndex: objectClass eq}}
+
+.If your domain contains additional components, such as
+{{EX:eng.uni.edu.eu}}, use:
+
+..{{EX:dn: olcDatabase=mdb,cn=config}}
+..{{EX:objectClass: olcDatabaseConfig}}
+..{{EX:objectClass: olcMdbConfig}}
+..{{EX:olcDatabase: mdb}}
+..{{EX:OlcDbMaxSize: 1073741824}}
+..{{EX:olcSuffix: dc=eng,dc=uni,dc=edu,dc=eu}}
+..{{EX:olcRootDN: cn=Manager,dc=eng,dc=uni,dc=edu,dc=eu}}
+..{{EX:olcRootPW: secret}}
+..{{EX:olcDbDirectory: /usr/local/var/openldap-data}}
+..{{EX:olcDbIndex: objectClass eq}}
+
+. Details regarding configuring {{slapd}}(8) can be found
+in the {{slapd-config}}(5) manual page and the {{SECT:Configuring
+slapd}} chapter of this document. Note that the
+specified olcDbDirectory must exist prior to starting {{slapd}}(8).
+
+
+.{{S: }}
++{{B:Import the configuration database}}
+. You are now ready to import your configuration database for use by
+{{slapd}}(8), by running the command:
+
+..{{EX: su root -c /usr/local/sbin/slapadd -n 0 -F /usr/local/etc/slapd.d -l /usr/local/etc/openldap/slapd.ldif}}
+
+.{{S: }}
++{{B:Start SLAPD}}.
+
+. You are now ready to start the Standalone LDAP Daemon, {{slapd}}(8),
+by running the command:
+
+..{{EX:su root -c /usr/local/libexec/slapd -F /usr/local/etc/slapd.d}}
+
+
+. To check to see if the server is running and configured correctly,
+you can run a search against it with {{ldapsearch}}(1). By default,
+{{ldapsearch}} is installed as {{F:/usr/local/bin/ldapsearch}}:
+
+..{{EX:ldapsearch -x -b '' -s base '(objectclass=*)' namingContexts}}
+
+. Note the use of single quotes around command parameters to prevent
+special characters from being interpreted by the shell. This should return:
+
+..{{EX:dn:}}
+..{{EX:namingContexts: dc=example,dc=com}}
+
+. Details regarding running {{slapd}}(8) can be found
+in the {{slapd}}(8) manual page and the
+{{SECT:Running slapd}} chapter of this document.
+
+
+.{{S: }}
++{{B:Add initial entries to your directory}}.
+
+. You can use {{ldapadd}}(1) to add entries to your LDAP directory.
+{{ldapadd}} expects input in {{TERM:LDIF}} form. We'll do it in two
+steps:
+
+^^ create an LDIF file
+++ run ldapadd
+
+. Use your favorite editor and create an LDIF file that contains:
+
+..{{EX:dn: dc=<MY-DOMAIN>,dc=<COM>}}
+..{{EX:objectclass: dcObject}}
+..{{EX:objectclass: organization}}
+..{{EX:o: <MY ORGANIZATION>}}
+..{{EX:dc: <MY-DOMAIN>}}
+..{{EX:}}
+..{{EX:dn: cn=Manager,dc=<MY-DOMAIN>,dc=<COM>}}
+..{{EX:objectclass: organizationalRole}}
+..{{EX:cn: Manager}}
+
+. Be sure to replace {{EX:<MY-DOMAIN>}} and {{EX:<COM>}} with the
+appropriate domain components of your domain name. {{EX:<MY
+ORGANIZATION>}} should be replaced with the name of your organization.
+When you cut and paste, be sure to trim any leading and trailing
+whitespace from the example.
+
+..{{EX:dn: dc=example,dc=com}}
+..{{EX:objectclass: dcObject}}
+..{{EX:objectclass: organization}}
+..{{EX:o: Example Company}}
+..{{EX:dc: example}}
+..{{EX:}}
+..{{EX:dn: cn=Manager,dc=example,dc=com}}
+..{{EX:objectclass: organizationalRole}}
+..{{EX:cn: Manager}}
+
+. Now, you may run {{ldapadd}}(1) to insert these entries into
+your directory.
+
+..{{EX:ldapadd -x -D "cn=Manager,dc=<MY-DOMAIN>,dc=<COM>" -W -f example.ldif}}
+
+. Be sure to replace {{EX:<MY-DOMAIN>}} and {{EX:<COM>}} with the
+appropriate domain components of your domain name. You will be
+prompted for the "{{EX:secret}}" specified in {{F:slapd.conf}}.
+For example, for {{EX:example.com}}, use:
+
+..{{EX:ldapadd -x -D "cn=Manager,dc=example,dc=com" -W -f example.ldif}}
+
+. where {{F:example.ldif}} is the file you created above.
+..{{EX: }}
+. Additional information regarding directory creation can be found
+in the {{SECT:Database Creation and Maintenance Tools}} chapter of
+this document.
+
+.{{S: }}
++{{B:See if it works}}.
+
+. Now we're ready to verify the added entries are in your directory.
+You can use any LDAP client to do this, but our example uses the
+{{ldapsearch}}(1) tool. Remember to replace {{EX:dc=example,dc=com}}
+with the correct values for your site:
+
+..{{EX:ldapsearch -x -b 'dc=example,dc=com' '(objectclass=*)'}}
+
+. This command will search for and retrieve every entry in the database.
+
+You are now ready to add more entries using {{ldapadd}}(1) or
+another LDAP client, experiment with various configuration options,
+backend arrangements, etc..
+
+Note that by default, the {{slapd}}(8) database grants {{read access
+to everybody}} excepting the {{super-user}} (as specified by the
+{{EX:rootdn}} configuration directive). It is highly recommended
+that you establish controls to restrict access to authorized users.
+Access controls are discussed in the {{SECT:Access Control}} chapter.
+You are also encouraged to read the {{SECT:Security Considerations}},
+{{SECT:Using SASL}} and {{SECT:Using TLS}} sections.
+
+The following chapters provide more detailed information on making,
+installing, and running {{slapd}}(8).
diff --git a/doc/guide/admin/referrals.sdf b/doc/guide/admin/referrals.sdf
new file mode 100644
index 0000000..e00df79
--- /dev/null
+++ b/doc/guide/admin/referrals.sdf
@@ -0,0 +1,146 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: Constructing a Distributed Directory Service
+
+For many sites, running one or more {{slapd}}(8) that hold an
+entire subtree of data is sufficient. But often it is desirable
+to have one {{slapd}} refer to other directory services for a
+certain part of the tree (which may or may not be running {{slapd}}).
+
+!if 0
+{{slapd}} supports {{subordinate}}, {{immediate superior}},
+and {{superior}} knowledge information.
+!else
+{{slapd}} supports {{subordinate}} and {{superior}} knowledge information.
+Subordinate knowledge information is held in {{EX:referral}}
+objects ({{REF:RFC3296}}).
+!endif
+
+
+H2: Subordinate Knowledge Information
+
+Subordinate knowledge information may be provided to delegate
+a subtree.
+Subordinate knowledge information is maintained in the directory
+as a special {{referral}} object at the delegate point.
+The referral object acts as a delegation point, gluing two services
+together.
+This mechanism allows for hierarchical directory services to be
+constructed.
+
+A referral object has a structural object class of
+{{EX:referral}} and has the same {{TERM[expand]DN}} as the
+delegated subtree. Generally, the referral object will also
+provide the auxiliary object class {{EX:extensibleObject}}.
+This allows the entry to contain appropriate {{TERM[expand]RDN}}
+values. This is best demonstrated by example.
+
+If the server {{EX:a.example.net}} holds {{EX:dc=example,dc=net}}
+and wished to delegate the subtree {{EX:ou=subtree,dc=example,dc=net}}
+to another server {{EX:b.example.net}}, the following named referral
+object would be added to {{EX:a.example.net}}:
+
+> dn: dc=subtree,dc=example,dc=net
+> objectClass: referral
+> objectClass: extensibleObject
+> dc: subtree
+> ref: ldap://b.example.net/dc=subtree,dc=example,dc=net
+
+The server uses this information to generate referrals and
+search continuations to subordinate servers.
+
+For those familiar with {{TERM:X.500}}, a {{named referral}} object is
+similar to an X.500 knowledge reference held in a {{subr}}
+{{TERM:DSE}}.
+
+
+!if 0
+H2: Immediate Superior Knowledge Information
+
+Immediate superior knowledge information may be provided in the
+entry at the root of a delegated subtree. The knowledge information
+is contained with {{EX:ref}} operational attribute.
+
+Extending the example above, a {{ref}} attribute can be added
+to the entry {{EX:dc=subtree,dc=example,dc=net}} in server B indicating
+that A holds the immediate superior naming context.
+
+> dn: dc=subtree,dc=example,dc=net
+> changetype: modify
+> add: ref
+> ref: ldap://a.example.net/
+
+The server uses this information to generate referrals to
+management operations.
+
+For those familiar with {{TERM:X.500}}, this use of the {{EX:ref}}
+attribute is similar to an X.500 knowledge reference held in a
+{{immSupr}} {{TERM:DSE}}.
+!endif
+
+
+H2: Superior Knowledge Information
+
+Superior knowledge information may be specified using the {{EX:referral}}
+directive. The value is a list of {{TERM:URI}}s referring to
+superior directory services. For servers without immediate superiors,
+such as for {{EX:a.example.net}} in the example above, the server
+can be configured to use a directory service with {{global knowledge}},
+such as the {{OpenLDAP Root Service}}
+({{URL:http://www.openldap.org/faq/index.cgi?file=393}}).
+
+> referral ldap://root.openldap.org/
+
+However, as {{EX:a.example.net}} is the {{immediate superior}}
+to {{EX:b.example.net}}, {{b.example.net}} would be configured
+as follows:
+
+> referral ldap://a.example.net/
+
+The server uses this information to generate referrals for operations
+acting upon entries not within or subordinate to any of the naming
+contexts held by the server.
+
+For those familiar with {{TERM:X.500}}, this use of the {{EX:ref}}
+attribute is similar to an X.500 knowledge reference held in a
+{{Supr}} {{TERM:DSE}}.
+
+
+H2: The ManageDsaIT Control
+
+Adding, modifying, and deleting referral objects is generally done
+using {{ldapmodify}}(1) or similar tools which support the ManageDsaIT
+control. The ManageDsaIT control informs the server that you intend
+to manage the referral object as a regular entry. This keeps the
+server from sending a referral result for requests which interrogate
+or update referral objects.
+
+The ManageDsaIT control should not be specified when managing regular
+entries.
+
+The {{EX:-M}} option of {{ldapmodify}}(1) (and other tools) enables
+ManageDsaIT. For example:
+
+> ldapmodify -M -f referral.ldif -x -D "cn=Manager,dc=example,dc=net" -W
+
+or with {{ldapsearch}}(1):
+
+> ldapsearch -M -b "dc=example,dc=net" -x "(objectclass=referral)" '*' ref
+
+Note: the {{EX:ref}} attribute is operational and must be explicitly
+requested when desired in search results.
+
+Note: the use of referrals to construct a Distributed Directory Service is
+extremely clumsy and not well supported by common clients. If an existing
+installation has already been built using referrals, the use of the
+{{chain}} overlay to hide the referrals will greatly improve the usability
+of the Directory system. A better approach would be to use explicitly
+defined local and proxy databases in {{subordinate}} configurations to
+provide a seamless view of the Distributed Directory.
+
+Note: LDAP operations, even subtree searches, normally access only one
+database. That can be changed by gluing databases together with the
+{{B:subordinate}}/{{B:olcSubordinate}} keyword. Please see {{slapd.conf}}(5)
+and {{slapd-config}}(5).
diff --git a/doc/guide/admin/refint.png b/doc/guide/admin/refint.png
new file mode 100644
index 0000000..41843e7
--- /dev/null
+++ b/doc/guide/admin/refint.png
Binary files differ
diff --git a/doc/guide/admin/replication.sdf b/doc/guide/admin/replication.sdf
new file mode 100644
index 0000000..a23db1a
--- /dev/null
+++ b/doc/guide/admin/replication.sdf
@@ -0,0 +1,1148 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: Replication
+
+Replicated directories are a fundamental requirement for delivering a
+resilient enterprise deployment.
+
+{{PRD:OpenLDAP}} has various configuration options for creating a replicated
+directory. In previous releases, replication was discussed in terms of
+a {{master}} server and some number of {{slave}} servers. A master
+accepted directory updates from other clients, and a slave only
+accepted updates from a (single) master. The replication structure
+was rigidly defined and any particular database could only fulfill
+a single role, either master or slave. Another historic term introduced
+with OpenLDAP 2.4 was multimaster.
+
+As OpenLDAP now supports a wide variety of replication topologies, these
+terms have been deprecated in favor of {{provider}}/{{multi-provider}} and
+{{consumer}}: A provider can accept external write operations and make them
+available for retrieval by consumers; consumers request replication updates from
+providers. Unlike the rigidly defined master/slave relationships,
+provider/consumer roles are quite fluid: replication updates received in a
+consumer can be further propagated by that consumer to other servers, so a
+consumer can also act simultaneously as a provider. Also, a consumer need not
+be an actual LDAP server; it may be just an LDAP client.
+
+The following sections will describe the replication technology and
+discuss the various replication options that are available.
+
+H2: Replication Technology
+
+H3: LDAP Sync Replication
+
+The {{TERM:LDAP Sync}} Replication engine, {{TERM:syncrepl}} for
+short, is a consumer-side replication engine that enables the
+consumer {{TERM:LDAP}} server to maintain a shadow copy of a
+{{TERM:DIT}} fragment. A syncrepl engine resides at the consumer
+and executes as one of the {{slapd}}(8) threads. It creates and maintains a
+replica by connecting to the replication provider to perform
+the initial DIT content load followed either by periodic content
+polling or by timely updates upon content changes.
+
+Syncrepl uses the LDAP Content Synchronization protocol (or LDAP Sync for
+short) as the consumer synchronization protocol. LDAP Sync provides
+a stateful replication which supports both pull-based and push-based
+synchronization and does not mandate the use of a history store.
+In pull-based replication the consumer periodically
+polls the provider for updates. In push-based replication the consumer
+listens for updates that are sent by the provider in realtime. Since the
+protocol does not require a history store, the provider does not need to
+maintain any log of updates it has received (Note
+that the syncrepl engine is extensible and additional replication
+protocols may be supported in the future.).
+
+Syncrepl keeps track of the status of the replication content by
+maintaining and exchanging synchronization cookies. Because the
+syncrepl consumer and provider maintain their content status, the
+consumer can poll the provider content to perform incremental
+synchronization by asking for the entries required to make the
+consumer up-to-date with the provider content. Syncrepl
+also enables convenient management of consumers by maintaining replication
+status. The consumer database can be constructed from a consumer-side
+or a provider-side backup at any synchronization status. Syncrepl
+can automatically resynchronize the consumer database to be up-to-date
+with the current provider content.
+
+Syncrepl supports both pull-based and push-based synchronization.
+In its basic refreshOnly synchronization mode, the provider uses
+pull-based synchronization where the consumer servers need not be
+tracked and no history information is maintained. The information
+required for the provider to process periodic polling requests is
+contained in the synchronization cookie of the request itself. To
+optimize the pull-based synchronization, syncrepl utilizes the
+present phase of the LDAP Sync protocol as well as its delete phase,
+instead of falling back on frequent full reloads. To further optimize
+the pull-based synchronization, the provider can maintain a per-scope
+session log as a history store. In its refreshAndPersist mode of
+synchronization, the provider uses a push-based synchronization.
+The provider keeps track of the consumer servers that have requested
+a persistent search and sends them necessary updates as the provider
+replication content gets modified.
+
+With syncrepl, a consumer can create a replication agreement without
+changing the provider's configurations and without restarting the
+provider server, if the consumer server has appropriate access
+privileges for the DIT fragment to be replicated. The consumer
+server can stop the replication also without the need for provider-side
+changes and restart.
+
+Syncrepl supports partial, sparse, and fractional replications. The shadow
+DIT fragment is defined by a general search criteria consisting of
+base, scope, filter, and attribute list. The consumer content is
+also subject to the access privileges of the bind identity of the
+syncrepl replication connection.
+
+
+H4: The LDAP Content Synchronization Protocol
+
+The LDAP Sync protocol allows a client to maintain a synchronized
+copy of a DIT fragment. The LDAP Sync operation is defined as a set
+of controls and other protocol elements which extend the LDAP search
+operation. This section introduces the LDAP Content Sync protocol
+only briefly. For more information, refer to {{REF:RFC4533}}.
+
+The LDAP Sync protocol supports both polling and listening for changes
+by defining two respective synchronization operations:
+{{refreshOnly}} and {{refreshAndPersist}}. Polling is implemented
+by the {{refreshOnly}} operation. The consumer
+polls the provider using an LDAP Search request with an LDAP Sync
+control attached. The consumer copy is synchronized
+to the provider copy at the time of polling using the information
+returned in the search. The provider finishes the
+search operation by returning {{SearchResultDone}} at the end of
+the search operation as in the normal search. Listening is
+implemented by the {{refreshAndPersist}} operation. As the name
+implies, it begins with a search, like refreshOnly. Instead of
+finishing the search after returning all entries currently matching
+the search criteria, the synchronization search remains persistent
+in the provider. Subsequent updates to the synchronization content
+in the provider cause additional entry updates to be sent to the
+consumer.
+
+The {{refreshOnly}} operation and the refresh stage of the
+{{refreshAndPersist}} operation can be performed with a present
+phase or a delete phase.
+
+In the present phase, the provider sends the consumer the entries updated
+within the search scope since the last synchronization. The provider
+sends all requested attributes, be they changed or not, of the updated
+entries. For each unchanged entry which remains in the scope, the
+provider sends a present message consisting only of the name of the
+entry and the synchronization control representing state present.
+The present message does not contain any attributes of the entry.
+After the consumer receives all update and present entries, it can
+reliably determine the new consumer copy by adding the entries added
+to the provider, by replacing the entries modified at the provider, and
+by deleting entries in the consumer copy which have not been updated
+nor specified as being present at the provider.
+
+The transmission of the updated entries in the delete phase is the
+same as in the present phase. The provider sends all the requested
+attributes of the entries updated within the search scope since the
+last synchronization to the consumer. In the delete phase, however,
+the provider sends a delete message for each entry deleted from the
+search scope, instead of sending present messages. The delete
+message consists only of the name of the entry and the synchronization
+control representing state delete. The new consumer copy can be
+determined by adding, modifying, and removing entries according to
+the synchronization control attached to the {{SearchResultEntry}}
+message.
+
+In the case that the LDAP Sync provider maintains a history store and
+can determine which entries are scoped out of the consumer copy since
+the last synchronization time, the provider can use the delete phase.
+If the provider does not maintain any history store, cannot determine
+the scoped-out entries from the history store, or the history store
+does not cover the outdated synchronization state of the consumer,
+the provider should use the present phase. The use of the present
+phase is much more efficient than a full content reload in terms
+of the synchronization traffic. To reduce the synchronization
+traffic further, the LDAP Sync protocol also provides several
+optimizations such as the transmission of the normalized {{EX:entryUUID}}s
+and the transmission of multiple {{EX:entryUUIDs}} in a single
+{{syncIdSet}} message.
+
+At the end of the {{refreshOnly}} synchronization, the provider sends
+a synchronization cookie to the consumer as a state indicator of the
+consumer copy after the synchronization is completed. The consumer
+will present the received cookie when it requests the next incremental
+synchronization to the provider.
+
+When {{refreshAndPersist}} synchronization is used, the provider sends
+a synchronization cookie at the end of the refresh stage by sending
+a Sync Info message with refreshDone=TRUE. It also sends a
+synchronization cookie by attaching it to {{SearchResultEntry}}
+messages generated in the persist stage of the synchronization search. During
+the persist stage, the provider can also send a Sync Info message
+containing the synchronization cookie at any time the provider wants
+to update the consumer-side state indicator.
+
+In the LDAP Sync protocol, entries are uniquely identified by the
+{{EX:entryUUID}} attribute value. It can function as a reliable
+identifier of the entry. The DN of the entry, on the other hand,
+can be changed over time and hence cannot be considered as the
+reliable identifier. The {{EX:entryUUID}} is attached to each
+{{SearchResultEntry}} or {{SearchResultReference}} as a part of the
+synchronization control.
+
+H4: Syncrepl Details
+
+The syncrepl engine utilizes both the {{refreshOnly}} and the
+{{refreshAndPersist}} operations of the LDAP Sync protocol. If a
+syncrepl specification is included in a database definition,
+{{slapd}}(8) launches a syncrepl engine as a {{slapd}}(8) thread
+and schedules its execution. If the {{refreshOnly}} operation is
+specified, the syncrepl engine will be rescheduled at the interval
+time after a synchronization operation is completed. If the
+{{refreshAndPersist}} operation is specified, the engine will remain
+active and process the persistent synchronization messages from the
+provider.
+
+The syncrepl engine utilizes both the present phase and the delete
+phase of the refresh synchronization. It is possible to configure
+a session log in the provider which stores the
+{{EX:entryUUID}}s of a finite number of entries deleted from a
+database. Multiple consumers share the same session log. The syncrepl
+engine uses the delete phase if the session log is present and the state
+of the consumer server is recent enough that no session log entries are
+truncated after the last synchronization of the client. The syncrepl
+engine uses the present phase if no session log is configured for
+the replication content or if the consumer is too outdated
+to be covered by the session log. The current design of the session
+log store is memory based, so the information contained in the
+session log is not persistent over multiple provider invocations.
+It is not currently supported to access the session log store by
+using LDAP operations. It is also not currently supported to impose
+access control to the session log.
+
+As a further optimization, even in the case the synchronization
+search is not associated with any session log, no entries will be
+transmitted to the consumer server when there has been no update
+in the replication context.
+
+The syncrepl engine, which is a consumer-side replication engine,
+can work with any backends. The LDAP Sync provider can be configured
+as an overlay on any backend, but works best with the {{back-mdb}}
+backend.
+
+The LDAP Sync provider maintains a {{EX:contextCSN}} for each
+database as the current synchronization state indicator of the
+provider content. It is the largest {{EX:entryCSN}} in the provider
+context such that no transactions for an entry having smaller
+{{EX:entryCSN}} value remains outstanding. The {{EX:contextCSN}}
+could not just be set to the largest issued {{EX:entryCSN}} because
+{{EX:entryCSN}} is obtained before a transaction starts and
+transactions are not committed in the issue order.
+
+The provider stores the {{EX:contextCSN}} of a context in the
+{{EX:contextCSN}} attribute of the context suffix entry. The attribute
+is not written to the database after every update operation though;
+instead it is maintained primarily in memory. At database start
+time the provider reads the last saved {{EX:contextCSN}} into memory
+and uses the in-memory copy exclusively thereafter. By default,
+changes to the {{EX:contextCSN}} as a result of database updates
+will not be written to the database until the server is cleanly
+shut down. A checkpoint facility exists to cause the {{EX:contextCSN}} to
+be written out more frequently if desired.
+
+Note that at startup time, if the provider is unable to read a
+{{EX:contextCSN}} from the suffix entry, it will scan the entire
+database to determine the value, and this scan may take quite a
+long time on a large database. When a {{EX:contextCSN}} value is
+read, the database will still be scanned for any {{EX:entryCSN}}
+values greater than it, to make sure the {{EX:contextCSN}} value
+truly reflects the greatest committed {{EX:entryCSN}} in the database.
+On databases which support inequality indexing, setting an eq index
+on the {{EX:entryCSN}} attribute and configuring {{contextCSN}}
+checkpoints will greatly speed up this scanning step.
+
+If no {{EX:contextCSN}} can be determined by reading and scanning
+the database, a new value will be generated. Also, if scanning the
+database yielded a greater {{EX:entryCSN}} than was previously
+recorded in the suffix entry's {{EX:contextCSN}} attribute, a
+checkpoint will be immediately written with the new value.
+
+The consumer also stores its replication state, which is the provider's
+{{EX:contextCSN}} received as a synchronization cookie, in the
+{{EX:contextCSN}} attribute of the suffix entry. The replication state
+maintained by a consumer server is used as the synchronization state
+indicator when it performs subsequent incremental synchronization
+with the provider server. It is also used as a provider-side
+synchronization state indicator when it functions as a secondary
+provider server in a cascading replication configuration. Since
+the consumer and provider state information are maintained in the
+same location within their respective databases, any consumer can
+be promoted to a provider (and vice versa) without any special
+actions.
+
+Because a general search filter can be used in the syncrepl
+specification, some entries in the context may be omitted from the
+synchronization content. The syncrepl engine creates a glue entry
+to fill in the holes in the consumer context if any part of the
+consumer content is subordinate to the holes. The glue entries will
+not be returned in the search result unless {{ManageDsaIT}} control
+is provided.
+
+Also as a consequence of the search filter used in the syncrepl
+specification, it is possible for a modification to remove an entry
+from the replication scope even though the entry has not been deleted
+on the provider. Logically the entry must be deleted on the consumer
+but in {{refreshOnly}} mode the provider cannot detect and propagate
+this change without the use of the session log on the provider.
+
+For configuration, please see the {{SECT:Syncrepl}} section.
+
+
+H2: Deployment Alternatives
+
+While the LDAP Sync specification only defines a narrow scope for replication,
+the OpenLDAP implementation is extremely flexible and supports a variety of
+operating modes to handle other scenarios not explicitly addressed in the spec.
+
+
+H3: Delta-syncrepl replication
+
+* Disadvantages of LDAP Sync replication:
+
+LDAP Sync replication is an object-based replication mechanism.
+When any attribute value in a replicated object is changed on the provider,
+each consumer fetches and processes the complete changed object, including
+{{B:both the changed and unchanged attribute values}} during replication.
+One advantage of this approach is that when multiple changes occur to
+a single object, the precise sequence of those changes need not be preserved;
+only the final state of the entry is significant. But this approach
+may have drawbacks when the usage pattern involves single changes to
+multiple objects.
+
+For example, suppose you have a database consisting of 102,400 objects of 1 KB
+each. Further, suppose you routinely run a batch job to change the value of
+a single two-byte attribute value that appears in each of the 102,400 objects
+on the provider. Not counting LDAP and TCP/IP protocol overhead, each time you
+run this job each consumer will transfer and process {{B:100 MB}} of data to
+process {{B:200KB of changes!}}
+
+99.98% of the data that is transmitted and processed in a case like this will
+be redundant, since it represents values that did not change. This is a waste
+of valuable transmission and processing bandwidth and can cause an unacceptable
+replication backlog to develop. While this situation is extreme, it serves to
+demonstrate a very real problem that is encountered in some LDAP deployments.
+
+
+* Where Delta-syncrepl comes in:
+
+Delta-syncrepl, a changelog-based variant of syncrepl, is designed to address
+situations like the one described above. Delta-syncrepl works by maintaining a
+changelog of a selectable depth in a separate database on the provider. The replication consumer
+checks the changelog for the changes it needs and, as long as
+the changelog contains the needed changes, the consumer fetches the changes
+from the changelog and applies them to its database. If, however, a consumer
+is too far out of sync (or completely empty), conventional syncrepl is used to
+bring it up to date and replication then switches back to the delta-syncrepl
+mode.
+
+Note: since the database state is stored in both the changelog DB and the
+main DB on the provider, it is important to backup/restore both the changelog
+DB and the main DB using slapcat/slapadd when restoring a DB or copying
+it to another machine.
+
+For configuration, please see the {{SECT:Delta-syncrepl}} section.
+
+
+H3: N-Way Multi-Provider Replication
+
+Multi-Provider replication is a replication technique using Syncrepl to replicate
+data to multiple provider ("Provider") Directory servers.
+
+H4: Valid Arguments for Multi-Provider replication
+
+* If any provider fails, other providers will continue to accept updates
+* Avoids a single point of failure
+* Providers can be located in several physical sites i.e. distributed across
+the network/globe.
+* Good for Automatic failover/High Availability
+
+H4: Invalid Arguments for Multi-Provider replication
+
+(These are often claimed to be advantages of Multi-Provider replication but
+those claims are false):
+
+* It has {{B:NOTHING}} to do with load balancing
+* Providers {{B:must}} propagate writes to {{B:all}} the other servers, which
+means the network traffic and write load spreads across all
+of the servers the same as for single-provider.
+* Server utilization and performance are at best identical for
+Multi-Provider and Single-Provider replication; at worst Single-Provider is
+superior because indexing can be tuned differently to optimize for the
+different usage patterns between the provider and the consumers.
+
+H4: Arguments against Multi-Provider replication
+
+* Breaks the data consistency guarantees of the directory model
+* {{URL:http://www.openldap.org/faq/data/cache/1240.html}}
+* If connectivity with a provider is lost because of a network partition, then
+"automatic failover" can just compound the problem
+* Typically, a particular machine cannot distinguish between losing contact
+ with a peer because that peer crashed, or because the network link has failed
+* If a network is partitioned and multiple clients start writing to each of the
+"providers" then reconciliation will be a pain; it may be best to simply deny
+writes to the clients that are partitioned from the single provider
+
+
+For configuration, please see the {{SECT:N-Way Multi-Provider}} section below
+
+H3: Mirror mode replication
+
+Mirror mode is a hybrid configuration that provides all of the consistency
+guarantees of single-provider replication, while also providing the high
+availability of multi-provider. In Mirror mode two providers are set up to
+replicate from each other (as a multi-provider configuration), but an
+external frontend is employed to direct all writes to only one of
+the two servers. The second provider will only be used for writes if
+the first provider crashes, at which point the frontend will switch to
+directing all writes to the second provider. When a crashed provider is
+repaired and restarted it will automatically catch up to any changes
+on the running provider and resync.
+
+H4: Arguments for Mirror mode
+
+* Provides a high-availability (HA) solution for directory writes (replicas handle reads)
+* As long as one provider is operational, writes can safely be accepted
+* Provider nodes replicate from each other, so they are always up to date and
+can be ready to take over (hot standby)
+* Syncrepl also allows the provider nodes to re-synchronize after any downtime
+
+
+H4: Arguments against Mirror mode
+
+* Mirror mode is not what is termed as a Multi-Provider solution. This is because
+writes have to go to just one of the mirror nodes at a time
+* Mirror mode can be termed as Active-Active Hot-Standby, therefore an external
+server (slapd in proxy mode) or device (hardware load balancer)
+is needed to manage which provider is currently active
+* Backups are managed slightly differently
+
+For configuration, please see the {{SECT:Mirror mode}} section below
+
+
+H3: Syncrepl Proxy Mode
+
+While the LDAP Sync protocol supports both pull- and push-based replication,
+the push mode (refreshAndPersist) must still be initiated from the consumer
+before the provider can begin pushing changes. In some network configurations,
+particularly where firewalls restrict the direction in which connections
+can be made, a provider-initiated push mode may be needed.
+
+This mode can be configured with the aid of the LDAP Backend
+({{SECT: Backends}} and {{slapd-ldap(8)}}). Instead of running the
+syncrepl engine on the actual consumer, a slapd-ldap proxy is set up
+near (or collocated with) the provider that points to the consumer,
+and the syncrepl engine runs on the proxy.
+
+For configuration, please see the {{SECT:Syncrepl Proxy}} section.
+
+H2: Configuring the different replication types
+
+H3: Syncrepl
+
+H4: Syncrepl configuration
+
+Because syncrepl is a consumer-side replication engine, the syncrepl
+specification is defined in {{slapd.conf}}(5) of the consumer
+server, not in the provider server's configuration file. The initial
+loading of the consumer content can be performed either by starting
+the syncrepl engine with no synchronization cookie or by populating
+the consumer by loading an {{TERM:LDIF}} file dumped as a
+backup at the provider.
+
+When loading from a backup, it is not required to perform the initial
+loading from the up-to-date backup of the provider content. The
+syncrepl engine will automatically synchronize the initial consumer
+to the current provider content. As a result, it is not
+required to stop the provider server in order to avoid the replication
+inconsistency caused by the updates to the provider content during
+the content backup and loading process.
+
+When replicating a large scale directory, especially in a bandwidth
+constrained environment, it is advised to load the consumer
+from a backup instead of performing a full initial load using
+syncrepl.
+
+
+H4: Set up the provider slapd
+
+The provider is implemented as an overlay, so the overlay itself
+must first be configured in {{slapd.conf}}(5) before it can be
+used. The provider has two primary configuration directives and
+two secondary directives for when delta-syncrepl is being used.
+Because the LDAP Sync search is subject to access control, proper
+access control privileges should be set up for the replicated
+content.
+
+The two primary options to configure are the checkpoint and
+sessionlog behaviors.
+
+The {{EX:contextCSN}} checkpoint is configured by the
+
+> syncprov-checkpoint <ops> <minutes>
+
+directive. Checkpoints are only tested after successful write
+operations. If {{<ops>}} operations or more than {{<minutes>}}
+time has passed since the last checkpoint, a new checkpoint is
+performed. Checkpointing is disabled by default.
+
+The session log is configured by the
+
+> syncprov-sessionlog <ops>
+
+directive, where {{<ops>}} is the maximum number of session log
+entries the session log can record. All write operations (except Adds)
+are recorded in the log.
+
+Note that using the session log requires searching on the {{entryUUID}}
+attribute. Setting an eq index on this attribute will greatly benefit
+the performance of the session log on the provider.
+
+The reloadhint option is configured by the
+
+> syncprov-reloadhint <TRUE|FALSE>
+
+directive. It must be set TRUE when using the accesslog overlay for
+delta-based syncrepl replication support. The default is FALSE.
+
+The nonpresent option is configured by the
+
+> syncprov-nopresent <TRUE|FALSE>
+
+directive. This value should only be set TRUE for a syncprov instance
+on top of a log database (such as one managed by the accesslog overlay).
+The default is FALSE.
+
+A more complete example of the {{slapd.conf}}(5) content is thus:
+
+> database mdb
+> maxsize 1073741824
+> suffix dc=Example,dc=com
+> rootdn dc=Example,dc=com
+> directory /var/ldap/db
+> index objectclass,entryCSN,entryUUID eq
+>
+> overlay syncprov
+> syncprov-checkpoint 100 10
+> syncprov-sessionlog 100
+
+
+H4: Set up the consumer slapd
+
+The syncrepl directive is specified in the database section of
+{{slapd.conf}}(5) for the consumer context. The syncrepl engine
+is backend independent and the directive can be defined with any
+database type.
+
+> database mdb
+> maxsize 1073741824
+> suffix dc=Example,dc=com
+> rootdn dc=Example,dc=com
+> directory /var/ldap/db
+> index objectclass,entryCSN,entryUUID eq
+>
+> syncrepl rid=123
+> provider=ldap://provider.example.com:389
+> type=refreshOnly
+> interval=01:00:00:00
+> searchbase="dc=example,dc=com"
+> filter="(objectClass=organizationalPerson)"
+> scope=sub
+> attrs="cn,sn,ou,telephoneNumber,title,l"
+> schemachecking=off
+> bindmethod=simple
+> binddn="cn=syncuser,dc=example,dc=com"
+> credentials=secret
+
+In this example, the consumer will connect to the provider {{slapd}}(8)
+at port 389 of {{FILE:ldap://provider.example.com}} to perform a
+polling ({{refreshOnly}}) mode of synchronization once a day. It
+will bind as {{EX:cn=syncuser,dc=example,dc=com}} using simple
+authentication with password "secret". Note that the access control
+privilege of {{EX:cn=syncuser,dc=example,dc=com}} should be set
+appropriately in the provider to retrieve the desired replication
+content. Also the search limits must be high enough on the provider
+to allow the syncuser to retrieve a complete copy of the requested
+content. The consumer uses the rootdn to write to its database so
+it always has full permissions to write all content.
+
+The synchronization search in the above example will search for the
+entries whose objectClass is organizationalPerson in the entire
+subtree rooted at {{EX:dc=example,dc=com}}. The requested attributes
+are {{EX:cn}}, {{EX:sn}}, {{EX:ou}}, {{EX:telephoneNumber}},
+{{EX:title}}, and {{EX:l}}. The schema checking is turned off, so
+that the consumer {{slapd}}(8) will not enforce entry schema
+checking when it processes updates from the provider {{slapd}}(8).
+
+For more detailed information on the syncrepl directive, see the
+{{SECT:syncrepl}} section of {{SECT:The slapd Configuration File}}
+chapter of this admin guide.
+
+
+H4: Start the provider and the consumer slapd
+
+The provider {{slapd}}(8) is not required to be restarted.
+{{contextCSN}} is automatically generated as needed: it might be
+originally contained in the {{TERM:LDIF}} file, generated by
+{{slapadd}} (8), generated upon changes in the context, or generated
+when the first LDAP Sync search arrives at the provider. If an
+LDIF file is being loaded which did not previously contain the
+{{contextCSN}}, the {{-w}} option should be used with {{slapadd}}
+(8) to cause it to be generated. This will allow the server to
+startup a little quicker the first time it runs.
+
+When starting a consumer {{slapd}}(8), it is possible to provide
+a synchronization cookie as the {{-c cookie}} command line option
+in order to start the synchronization from a specific state. The
+cookie is a comma separated list of name=value pairs. Currently
+supported syncrepl cookie fields are {{csn=<csn>}} and {{rid=<rid>}}.
+{{<csn>}} represents the current synchronization state of the
+consumer. {{<rid>}} identifies a consumer locally
+within the consumer server. It is used to relate the cookie to the
+syncrepl definition in {{slapd.conf}}(5) which has the matching
+{{<rid>}}. The {{<rid>}} must have no more than 3 decimal
+digits. The command line cookie overrides the synchronization
+cookie stored in the consumer database.
+
+
+H3: Delta-syncrepl
+
+H4: Delta-syncrepl Provider configuration
+
+Setting up delta-syncrepl requires configuration changes on both the provider and
+replica servers:
+
+> # Give the replicator DN unlimited read access. This ACL needs to be
+> # merged with other ACL statements, and/or moved within the scope
+> # of a database. The "by * break" portion causes evaluation of
+> # subsequent rules. See slapd.access(5) for details.
+> access to *
+> by dn.base="cn=replicator,dc=example,dc=com" read
+> by * break
+>
+> # Set the module path location
+> modulepath /opt/symas/lib/openldap
+>
+> # Load the mdb backend
+> moduleload back_mdb.la
+>
+> # Load the accesslog overlay
+> moduleload accesslog.la
+>
+> #Load the syncprov overlay
+> moduleload syncprov.la
+>
+> # Accesslog database definitions
+> database mdb
+> suffix cn=accesslog
+> directory /db/accesslog
+> rootdn cn=accesslog
+> index default eq
+> index entryCSN,objectClass,reqEnd,reqResult,reqStart,reqDN
+>
+> overlay syncprov
+> syncprov-nopresent TRUE
+> syncprov-reloadhint TRUE
+>
+> # Let the replicator DN have limitless searches
+> limits dn.exact="cn=replicator,dc=example,dc=com" time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited
+>
+> # Primary database definitions
+> database mdb
+> suffix "dc=symas,dc=com"
+> rootdn "cn=manager,dc=symas,dc=com"
+>
+> ## Whatever other configuration options are desired
+>
+> # syncprov specific indexing
+> index entryCSN eq
+> index entryUUID eq
+>
+> # syncrepl Provider for primary db
+> overlay syncprov
+> syncprov-checkpoint 1000 60
+>
+> # accesslog overlay definitions for primary db
+> overlay accesslog
+> logdb cn=accesslog
+> logops writes
+> logsuccess TRUE
+> # scan the accesslog DB every day, and purge entries older than 7 days
+> logpurge 07+00:00 01+00:00
+>
+> # Let the replicator DN have limitless searches
+> limits dn.exact="cn=replicator,dc=example,dc=com" time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited
+
+For more information, always consult the relevant man pages ({{slapo-accesslog}}(5) and {{slapd.conf}}(5))
+
+
+H4: Delta-syncrepl Consumer configuration
+
+> # Replica database configuration
+> database mdb
+> suffix "dc=symas,dc=com"
+> rootdn "cn=manager,dc=symas,dc=com"
+>
+> ## Whatever other configuration bits for the replica, like indexing
+> ## that you want
+>
+> # syncrepl specific indices
+> index entryUUID eq
+>
+> # syncrepl directives
+> syncrepl rid=0
+> provider=ldap://ldapprovider.example.com:389
+> bindmethod=simple
+> binddn="cn=replicator,dc=example,dc=com"
+> credentials=secret
+> searchbase="dc=example,dc=com"
+> logbase="cn=accesslog"
+> logfilter="(&(objectClass=auditWriteObject)(reqResult=0))"
+> schemachecking=on
+> type=refreshAndPersist
+> retry="60 +"
+> syncdata=accesslog
+>
+> # Refer updates to the provider
+> updateref ldap://ldapprovider.example.com
+
+
+The above configuration assumes that you have a replicator identity defined
+in your database that can be used to bind to the provider.
+
+Note: An accesslog database is unique to a given provider. It should
+never be replicated.
+
+H3: N-Way Multi-Provider
+
+For the following example we will be using 3 Provider nodes. Keeping in line with
+{{B:test050-syncrepl-multiprovider}} of the OpenLDAP test suite, we will be configuring
+{{slapd(8)}} via {{B:cn=config}}
+
+This sets up the config database:
+
+> dn: cn=config
+> objectClass: olcGlobal
+> cn: config
+> olcServerID: 1
+>
+> dn: olcDatabase={0}config,cn=config
+> objectClass: olcDatabaseConfig
+> olcDatabase: {0}config
+> olcRootPW: secret
+
+Each server must have a unique server ID ({{EX:SID}}), so
+second and third servers will have a different {{EX:olcServerID}} obviously:
+
+> dn: cn=config
+> objectClass: olcGlobal
+> cn: config
+> olcServerID: 2
+>
+> dn: olcDatabase={0}config,cn=config
+> objectClass: olcDatabaseConfig
+> olcDatabase: {0}config
+> olcRootPW: secret
+
+This sets up syncrepl as a provider (since these are all providers):
+
+> dn: cn=module,cn=config
+> objectClass: olcModuleList
+> cn: module
+> olcModulePath: /usr/local/libexec/openldap
+> olcModuleLoad: syncprov.la
+
+Now we setup the first Provider Node (replace $URI1, $URI2 and $URI3 etc. with your actual ldap urls):
+
+> dn: cn=config
+> changetype: modify
+> replace: olcServerID
+> olcServerID: 1 $URI1
+> olcServerID: 2 $URI2
+> olcServerID: 3 $URI3
+>
+> dn: olcOverlay=syncprov,olcDatabase={0}config,cn=config
+> changetype: add
+> objectClass: olcOverlayConfig
+> objectClass: olcSyncProvConfig
+> olcOverlay: syncprov
+>
+> dn: olcDatabase={0}config,cn=config
+> changetype: modify
+> add: olcSyncRepl
+> olcSyncRepl: rid=001 provider=$URI1 binddn="cn=config" bindmethod=simple
+> credentials=secret searchbase="cn=config" type=refreshAndPersist
+> retry="5 5 300 5" timeout=1
+> olcSyncRepl: rid=002 provider=$URI2 binddn="cn=config" bindmethod=simple
+> credentials=secret searchbase="cn=config" type=refreshAndPersist
+> retry="5 5 300 5" timeout=1
+> olcSyncRepl: rid=003 provider=$URI3 binddn="cn=config" bindmethod=simple
+> credentials=secret searchbase="cn=config" type=refreshAndPersist
+> retry="5 5 300 5" timeout=1
+> -
+> add: olcMultiProvider
+> olcMultiProvider: TRUE
+
+Now start up the provider and a consumer/s, also add the above LDIF to the first consumer, second consumer etc. It will then replicate {{B:cn=config}}. You now have N-Way Multi-Provider on the config database.
+
+We still have to replicate the actual data, not just the config, so add to the provider (all active and configured consumers/providers will pull down this config, as they are all syncing). Also, replace all {{${}}} variables with whatever is applicable to your setup:
+
+> dn: olcDatabase={1}$BACKEND,cn=config
+> objectClass: olcDatabaseConfig
+> objectClass: olc${BACKEND}Config
+> olcDatabase: {1}$BACKEND
+> olcSuffix: $BASEDN
+> olcDbDirectory: ./db
+> olcRootDN: $MANAGERDN
+> olcRootPW: $PASSWD
+> olcLimits: dn.exact="$MANAGERDN" time.soft=unlimited time.hard=unlimited
+> size.soft=unlimited size.hard=unlimited
+> olcSyncRepl: rid=004 provider=$URI1 binddn="$MANAGERDN" bindmethod=simple
+> credentials=$PASSWD searchbase="$BASEDN" type=refreshOnly
+> interval=00:00:00:10 retry="5 5 300 5" timeout=1
+> olcSyncRepl: rid=005 provider=$URI2 binddn="$MANAGERDN" bindmethod=simple
+> credentials=$PASSWD searchbase="$BASEDN" type=refreshOnly
+> interval=00:00:00:10 retry="5 5 300 5" timeout=1
+> olcSyncRepl: rid=006 provider=$URI3 binddn="$MANAGERDN" bindmethod=simple
+> credentials=$PASSWD searchbase="$BASEDN" type=refreshOnly
+> interval=00:00:00:10 retry="5 5 300 5" timeout=1
+> olcMultiProvider: TRUE
+>
+> dn: olcOverlay=syncprov,olcDatabase={1}${BACKEND},cn=config
+> changetype: add
+> objectClass: olcOverlayConfig
+> objectClass: olcSyncProvConfig
+> olcOverlay: syncprov
+
+Note: All of your servers' clocks must be tightly synchronized using
+e.g. NTP {{http://www.ntp.org/}}, atomic clock, or some other reliable
+time reference.
+
+Note: As stated in {{slapd-config}}(5), URLs specified in {{olcSyncRepl}}
+directives are the URLs of the servers from which to replicate. These
+must exactly match the URLs {{slapd}} listens on ({{-h}} in {{SECT:Command-Line Options}}).
+Otherwise slapd may attempt to replicate from itself, causing a loop.
+
+Note: The {{entryCSN}} and {{contextCSN}} attributes are used
+to track changes to an entry and naming context, respectively. The
+{{EX:SID}} which must be unique for each replication provider is a
+component of these CSNs. If you're using {{slapadd}} to load a
+database and there are no entryCSNs already present in the input
+LDIF, {{slapadd}} will generate them with a {{SID}} of {{EX:000}}. This
+is not a valid {{SID}} for multi-provider replication, and you
+should use the {{EX:-S}} option of {{slapadd}} (8) to specify
+a valid {{SID}} for these generated CSNs. If there are existing
+entryCSNs in the input LDIF, {{slapadd}} will not change them.
+
+H3: Mirror mode
+
+Mirror mode configuration is actually very easy. If you have ever setup a normal
+slapd syncrepl provider, then the only change is the following two directives:
+
+> multiprovider on
+> serverID 1
+
+Note: You need to make sure that the {{serverID}} of each provider node is
+different and add it as a global configuration option.
+
+H4: Mirror Node Configuration
+
+The first step is to configure the syncrepl provider the same as in the
+{{SECT:Set up the provider slapd}} section.
+
+Here's a specific cut down example using {{SECT:LDAP Sync Replication}} in
+{{refreshAndPersist}} mode:
+
+Mirror mode node 1:
+
+> # Global section
+> serverID 1
+> # database section
+>
+> # syncrepl directive
+> syncrepl rid=001
+> provider=ldap://ldap-sid2.example.com
+> bindmethod=simple
+> binddn="cn=mirrormode,dc=example,dc=com"
+> credentials=mirrormode
+> searchbase="dc=example,dc=com"
+> schemachecking=on
+> type=refreshAndPersist
+> retry="60 +"
+>
+> multiprovider on
+
+Mirror mode node 2:
+
+> # Global section
+> serverID 2
+> # database section
+>
+> # syncrepl directive
+> syncrepl rid=001
+> provider=ldap://ldap-sid1.example.com
+> bindmethod=simple
+> binddn="cn=mirrormode,dc=example,dc=com"
+> credentials=mirrormode
+> searchbase="dc=example,dc=com"
+> schemachecking=on
+> type=refreshAndPersist
+> retry="60 +"
+>
+> multiprovider on
+
+It's simple really; each Mirror mode node is setup {{B:exactly}} the same, except
+that the {{serverID}} is unique, and each consumer is pointed to
+the other server.
+
+H5: Failover Configuration
+
+There are generally 2 choices for this; 1. Hardware proxies/load-balancing or
+dedicated proxy software, 2. using a Back-LDAP proxy as a syncrepl provider
+
+A typical enterprise example might be:
+
+!import "dual_dc.png"; align="center"; title="Mirror mode Enterprise Configuration"
+FT[align="Center"] Figure X.Y: Mirror mode in a Dual Data Center Configuration
+
+H5: Normal Consumer Configuration
+
+This is exactly the same as the {{SECT:Set up the consumer slapd}} section. It
+can either setup in normal {{SECT:syncrepl replication}} mode, or in
+{{SECT:delta-syncrepl replication}} mode.
+
+H4: Mirror mode Summary
+
+You will now have a directory architecture that provides all of the
+consistency guarantees of single-provider replication, while also providing the
+high availability of multi-provider replication.
+
+
+H3: Syncrepl Proxy
+
+!import "push-based-complete.png"; align="center"; title="Syncrepl Proxy Mode"
+FT[align="Center"] Figure X.Y: Replacing slurpd
+
+The following example is for a self-contained push-based replication solution:
+
+> #######################################################################
+> # Standard OpenLDAP Provider
+> #######################################################################
+>
+> include /usr/local/etc/openldap/schema/core.schema
+> include /usr/local/etc/openldap/schema/cosine.schema
+> include /usr/local/etc/openldap/schema/nis.schema
+> include /usr/local/etc/openldap/schema/inetorgperson.schema
+>
+> include /usr/local/etc/openldap/slapd.acl
+>
+> modulepath /usr/local/libexec/openldap
+> moduleload back_mdb.la
+> moduleload syncprov.la
+> moduleload back_ldap.la
+>
+> pidfile /usr/local/var/slapd.pid
+> argsfile /usr/local/var/slapd.args
+>
+> loglevel sync stats
+>
+> database mdb
+> suffix "dc=suretecsystems,dc=com"
+> directory /usr/local/var/openldap-data
+>
+> checkpoint 1024 5
+>
+> index objectClass eq
+> # rest of indexes
+> index default sub
+>
+> rootdn "cn=admin,dc=suretecsystems,dc=com"
+> rootpw testing
+>
+> # syncprov specific indexing
+> index entryCSN eq
+> index entryUUID eq
+>
+> # syncrepl Provider for primary db
+> overlay syncprov
+> syncprov-checkpoint 1000 60
+>
+> # Let the replicator DN have limitless searches
+> limits dn.exact="cn=replicator,dc=suretecsystems,dc=com" time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited
+>
+> database monitor
+>
+> database config
+> rootpw testing
+>
+> ##############################################################################
+> # Consumer Proxy that pulls in data via Syncrepl and pushes out via slapd-ldap
+> ##############################################################################
+>
+> database ldap
+> # ignore conflicts with other databases, as we need to push out to same suffix
+> hidden on
+> suffix "dc=suretecsystems,dc=com"
+> rootdn "cn=slapd-ldap"
+> uri ldap://localhost:9012/
+>
+> lastmod on
+>
+> # We don't need any access to this DSA
+> restrict all
+>
+> acl-bind bindmethod=simple
+> binddn="cn=replicator,dc=suretecsystems,dc=com"
+> credentials=testing
+>
+> syncrepl rid=001
+> provider=ldap://localhost:9011/
+> binddn="cn=replicator,dc=suretecsystems,dc=com"
+> bindmethod=simple
+> credentials=testing
+> searchbase="dc=suretecsystems,dc=com"
+> type=refreshAndPersist
+> retry="5 5 300 5"
+>
+> overlay syncprov
+
+A replica configuration for this type of setup could be:
+
+> #######################################################################
+> # Standard OpenLDAP Replica without Syncrepl
+> #######################################################################
+>
+> include /usr/local/etc/openldap/schema/core.schema
+> include /usr/local/etc/openldap/schema/cosine.schema
+> include /usr/local/etc/openldap/schema/nis.schema
+> include /usr/local/etc/openldap/schema/inetorgperson.schema
+>
+> include /usr/local/etc/openldap/slapd.acl
+>
+> modulepath /usr/local/libexec/openldap
+> moduleload back_mdb.la
+> moduleload syncprov.la
+> moduleload back_ldap.la
+>
+> pidfile /usr/local/var/slapd.pid
+> argsfile /usr/local/var/slapd.args
+>
+> loglevel sync stats
+>
+> database mdb
+> suffix "dc=suretecsystems,dc=com"
+> directory /usr/local/var/openldap-consumer/data
+>
+> maxsize 85899345920
+> checkpoint 1024 5
+>
+> index objectClass eq
+> # rest of indexes
+> index default sub
+>
+> rootdn "cn=admin,dc=suretecsystems,dc=com"
+> rootpw testing
+>
+> # Let the replicator DN have limitless searches
+> limits dn.exact="cn=replicator,dc=suretecsystems,dc=com" time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited
+>
+> updatedn "cn=replicator,dc=suretecsystems,dc=com"
+>
+> # Refer updates to the provider
+> updateref ldap://localhost:9011
+>
+> database monitor
+>
+> database config
+> rootpw testing
+
+You can see we use the {{updatedn}} directive here and example ACLs ({{F:usr/local/etc/openldap/slapd.acl}}) for this could be:
+
+> # Give the replicator DN unlimited read access. This ACL may need to be
+> # merged with other ACL statements.
+>
+> access to *
+> by dn.base="cn=replicator,dc=suretecsystems,dc=com" write
+> by * break
+>
+> access to dn.base=""
+> by * read
+>
+> access to dn.base="cn=Subschema"
+> by * read
+>
+> access to dn.subtree="cn=Monitor"
+> by dn.exact="uid=admin,dc=suretecsystems,dc=com" write
+> by users read
+> by * none
+>
+> access to *
+> by self write
+> by * read
+
+In order to support more replicas, just add more {{database ldap}} sections and
+increment the {{syncrepl rid}} number accordingly.
+
+Note: You must populate the Provider and Replica directories with the same data,
+unlike when using normal Syncrepl
+
+If you do not have access to modify the provider directory configuration you can
+configure a standalone ldap proxy, which might look like:
+
+!import "push-based-standalone.png"; align="center"; title="Syncrepl Standalone Proxy Mode"
+FT[align="Center"] Figure X.Y: Replacing slurpd with a standalone version
+
+The following configuration is an example of a standalone LDAP Proxy:
+
+> include /usr/local/etc/openldap/schema/core.schema
+> include /usr/local/etc/openldap/schema/cosine.schema
+> include /usr/local/etc/openldap/schema/nis.schema
+> include /usr/local/etc/openldap/schema/inetorgperson.schema
+>
+> include /usr/local/etc/openldap/slapd.acl
+>
+> modulepath /usr/local/libexec/openldap
+> moduleload syncprov.la
+> moduleload back_ldap.la
+>
+> ##############################################################################
+> # Consumer Proxy that pulls in data via Syncrepl and pushes out via slapd-ldap
+> ##############################################################################
+>
+> database ldap
+> # ignore conflicts with other databases, as we need to push out to same suffix
+> hidden on
+> suffix "dc=suretecsystems,dc=com"
+> rootdn "cn=slapd-ldap"
+> uri ldap://localhost:9012/
+>
+> lastmod on
+>
+> # We don't need any access to this DSA
+> restrict all
+>
+> acl-bind bindmethod=simple
+> binddn="cn=replicator,dc=suretecsystems,dc=com"
+> credentials=testing
+>
+> syncrepl rid=001
+> provider=ldap://localhost:9011/
+> binddn="cn=replicator,dc=suretecsystems,dc=com"
+> bindmethod=simple
+> credentials=testing
+> searchbase="dc=suretecsystems,dc=com"
+> type=refreshAndPersist
+> retry="5 5 300 5"
+>
+> overlay syncprov
+
+As you can see, you can let your imagination go wild using Syncrepl and
+{{slapd-ldap(8)}} tailoring your replication to fit your specific network
+topology.
diff --git a/doc/guide/admin/runningslapd.sdf b/doc/guide/admin/runningslapd.sdf
new file mode 100644
index 0000000..c7be0eb
--- /dev/null
+++ b/doc/guide/admin/runningslapd.sdf
@@ -0,0 +1,160 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+H1: Running slapd
+
+{{slapd}}(8) is designed to be run as a standalone service. This
+allows the server to take advantage of caching, manage concurrency
+issues with underlying databases, and conserve system resources.
+Running from {{inetd}}(8) is {{NOT}} an option.
+
+
+H2: Command-Line Options
+
+{{slapd}}(8) supports a number of command-line options as detailed
+in the manual page. This section details a few commonly used options.
+
+> -f <filename>
+
+This option specifies an alternate configuration file for slapd.
+The default is normally {{F:/usr/local/etc/openldap/slapd.conf}}.
+
+> -F <slapd-config-directory>
+
+Specifies the slapd configuration directory. The default is {{F:/usr/local/etc/openldap/slapd.d}}.
+
+If both {{EX:-f}} and {{EX:-F}} are specified, the config file will be read and converted
+to config directory format and written to the specified directory.
+If neither option is specified, slapd will attempt to read the default config
+directory before trying to use the default config file. If a valid config
+directory exists then the default config file is ignored. All of the slap tools
+that use the config options observe this same behavior.
+
+> -h <URLs>
+
+This option specifies alternative listener configurations. The
+default is {{EX:ldap:///}} which implies {{TERM:LDAP}} over
+{{TERM:TCP}} on all interfaces on the default LDAP port 389. You
+can specify specific host-port pairs or other protocol schemes (such
+as {{EX:ldaps://}} or {{EX:ldapi://}}). slapd supports the HAProxy
+proxy protocol version 2, which allows a load balancer or proxy
+server to provide the remote client IP address to slapd to be used
+for access control or logging. Listeners configured using either
+{{EX:pldap:///}} or {{EX:pldaps:///}} URLS will only accept
+connections that include the necessary proxy protocol header.
+Connections to the ports used by these listeners should be restricted
+at the network level to only trusted load balancers or proxies to
+avoid spoofing of client IP addresses by third parties.
+
+!block table
+URL Protocol Transport
+ldap:/// LDAP TCP port 389
+pldap:/// proxied LDAP TCP port 389
+ldaps:/// LDAP over SSL TCP port 636
+pldaps:/// proxied LDAP over SSL TCP port 636
+ldapi:/// LDAP IPC (Unix-domain socket)
+!endblock
+
+For example, {{EX:-h
+"ldaps:// ldap://127.0.0.1:666"}} will create two listeners: one
+for the (non-standard) {{EX:ldaps://}} scheme on all interfaces on
+the default {{EX:ldaps://}} port 636, and one for the standard
+{{EX:ldap://}} scheme on the {{EX:localhost}} ({{loopback}}) interface
+on port 666. Hosts may be specified using using hostnames or
+{{TERM:IPv4}} or {{TERM:IPv6}} addresses. Port values must be
+numeric.
+
+For LDAP over IPC, the pathname of the Unix-domain socket can be encoded
+in the URL. Note that directory separators must be
+URL-encoded, like any other characters that are special to URLs.
+Thus the socket {{EX:/usr/local/var/ldapi}} must be encoded as
+
+> ldapi://%2Fusr%2Flocal%2Fvar%2Fldapi
+
+ldapi: is described in detail in {{Using LDAP Over IPC Mechanisms}} [{{REF:Chu-LDAPI}}]
+
+Note that the ldapi:/// transport is not widely implemented: non-OpenLDAP clients
+may not be able to use it.
+
+> -n <service-name>
+
+This option specifies the service name used for logging and
+other purposes. The default service name is {{EX:slapd}}.
+
+> -l <syslog-local-user>
+
+This option specifies the local user for the {{syslog}}(8)
+facility. Values can be {{EX:LOCAL0}}, {{EX:LOCAL1}}, {{EX:LOCAL2}}, ...,
+and {{EX:LOCAL7}}. The default is {{EX:LOCAL4}}. This option
+may not be supported on all systems.
+
+> -u user -g group
+
+These options specify the user and group, respectively, to run
+as. {{EX:user}} can be either a user name or uid. {{EX:group}}
+can be either a group name or gid.
+
+> -r directory
+
+This option specifies a run-time directory. slapd will
+{{chroot}}(2) to this directory after opening listeners but
+before reading any configuration files or initializing
+any backends.
+.
+
+> -d <level> | ?
+
+This option sets the slapd debug level to <level>. When level is a
+`?' character, the various debugging levels are printed and slapd
+exits, regardless of any other options you give it. Current
+debugging levels are
+
+!block table; colaligns="RL"; align=Center; \
+ title="Table 7.1: Debugging Levels"
+Level Keyword Description
+-1 any enable all debugging
+0 no debugging
+1 (0x1 trace) trace function calls
+2 (0x2 packets) debug packet handling
+4 (0x4 args) heavy trace debugging
+8 (0x8 conns) connection management
+16 (0x10 BER) print out packets sent and received
+32 (0x20 filter) search filter processing
+64 (0x40 config) configuration processing
+128 (0x80 ACL) access control list processing
+256 (0x100 stats) stats log connections/operations/results
+512 (0x200 stats2) stats log entries sent
+1024 (0x400 shell) print communication with shell backends
+2048 (0x800 parse) print entry parsing debugging
+16384 (0x4000 sync) syncrepl consumer processing
+32768 (0x8000 none) only messages that get logged whatever log level is set
+!endblock
+
+You may enable multiple levels by specifying the debug option once for each desired level. Or, since debugging levels are additive, you can do the math yourself. That is, if you want to trace function calls and watch the config file being processed, you could set level to the sum of those two levels (in this case, {{EX: -d 65}}). Or, you can let slapd do the math, (e.g. {{EX: -d 1 -d 64}}). Consult {{F: <ldap_log.h>}} for more details.
+
+Note: slapd must have been compiled with {{EX:--enable-debug}}, which is the default,
+for any debugging information other than the stats and stats2 levels to be available as options.
+
+
+H2: Starting slapd
+
+In general, slapd is run like this:
+
+> /usr/local/libexec/slapd [<option>]*
+
+where {{F:/usr/local/libexec}} is determined by {{EX:configure}}
+and <option> is one of the options described above (or in {{slapd}}(8)).
+Unless you have specified a debugging level (including level {{EX:0}}),
+slapd will automatically fork and detach itself from its controlling
+terminal and run in the background.
+
+H2: Stopping slapd
+
+To kill off {{slapd}}(8) safely, you should give a command like this
+
+> kill -INT `cat /usr/local/var/slapd.pid`
+
+where {{F:/usr/local/var}} is determined by {{EX:configure}}.
+
+Killing slapd by a more drastic method may cause information loss or
+database corruption.
diff --git a/doc/guide/admin/sasl.sdf b/doc/guide/admin/sasl.sdf
new file mode 100644
index 0000000..77a7a4e
--- /dev/null
+++ b/doc/guide/admin/sasl.sdf
@@ -0,0 +1,741 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: Using SASL
+
+OpenLDAP clients and servers are capable of authenticating via the
+{{TERM[expand]SASL}} ({{TERM:SASL}}) framework, which is detailed
+in {{REF:RFC4422}}. This chapter describes how to make use of
+SASL in OpenLDAP.
+
+There are several industry standard authentication mechanisms that
+can be used with SASL, including {{TERM:GSSAPI}} for {{TERM:Kerberos}}
+V, {{TERM:DIGEST-MD5}}, and {{TERM:PLAIN}} and {{TERM:EXTERNAL}}
+for use with {{TERM[expand]TLS}} (TLS).
+
+The standard client tools provided with OpenLDAP Software, such as
+{{ldapsearch}}(1) and {{ldapmodify}}(1), will by default attempt
+to authenticate the user to the {{TERM:LDAP}} directory server using
+SASL. Basic authentication service can be set up by the LDAP
+administrator with a few steps, allowing users to be authenticated
+to the slapd server as their LDAP entry. With a few extra steps,
+some users and services can be allowed to exploit SASL's proxy
+authorization feature, allowing them to authenticate themselves and
+then switch their identity to that of another user or service.
+
+This chapter assumes you have read {{Cyrus SASL for System
+Administrators}}, provided with the {{PRD:Cyrus SASL}}
+package (in {{FILE:doc/sysadmin.html}}) and have a working Cyrus
+SASL installation. You should use the Cyrus SASL {{EX:sample_client}}
+and {{EX:sample_server}} to test your SASL installation before
+attempting to make use of it with OpenLDAP Software.
+
+Note that in the following text the term {{user}} is used to describe
+a person or application entity who is connecting to the LDAP server
+via an LDAP client, such as {{ldapsearch}}(1). That is, the term
+{{user}} not only applies to both an individual using an LDAP client,
+but to an application entity which issues LDAP client operations
+without direct user control. For example, an e-mail server which
+uses LDAP operations to access information held in an LDAP server
+is an application entity.
+
+
+H2: SASL Security Considerations
+
+SASL offers many different authentication mechanisms. This section
+briefly outlines security considerations.
+
+Some mechanisms, such as PLAIN and LOGIN, offer no greater security
+over LDAP {{simple}} authentication. Like LDAP {{simple}}
+authentication, such mechanisms should not be used unless you have
+adequate security protections in place. It is recommended that
+these mechanisms be used only in conjunction with {{TERM[expand]TLS}}
+(TLS). Use of PLAIN and LOGIN are not discussed further in this
+document.
+
+The DIGEST-MD5 mechanism is the mandatory-to-implement authentication
+mechanism for LDAPv3. Though DIGEST-MD5 is not a strong authentication
+mechanism in comparison with trusted third party authentication
+systems (such as {{TERM:Kerberos}} or public key systems), it does
+offer significant protections against a number of attacks. Unlike
+the {{TERM:CRAM-MD5}} mechanism, it prevents chosen plaintext
+attacks. DIGEST-MD5 is favored over the use of plaintext password
+mechanisms. The CRAM-MD5 mechanism is deprecated in favor of
+DIGEST-MD5. Use of {{SECT:DIGEST-MD5}} is discussed below.
+
+The GSSAPI mechanism utilizes {{TERM:GSS-API}} {{TERM:Kerberos}} V
+to provide secure authentication services. The KERBEROS_V4 mechanism
+is available for those using Kerberos IV. Kerberos is viewed as a
+secure, distributed authentication system suitable for both small
+and large enterprises. Use of {{SECT:GSSAPI}} and {{SECT:KERBEROS_V4}}
+are discussed below.
+
+The EXTERNAL mechanism utilizes authentication services provided
+by lower level network services such as {{TERM[expand]TLS}} ({{TERM:TLS}}). When
+used in conjunction with {{TERM:TLS}} {{TERM:X.509}}-based public
+key technology, EXTERNAL offers strong authentication.
+TLS is discussed in the {{SECT:Using TLS}} chapter.
+
+EXTERNAL can also be used with the {{EX:ldapi:///}} transport, as
+Unix-domain sockets can report the UID and GID of the client process.
+
+There are other strong authentication mechanisms to choose from,
+including {{TERM:OTP}} (one time passwords) and {{TERM:SRP}} (secure
+remote passwords). These mechanisms are not discussed in this
+document.
+
+
+H2: SASL Authentication
+
+Getting basic SASL authentication running involves a few steps.
+The first step configures your slapd server environment so that it
+can communicate with client programs using the security system in
+place at your site. This usually involves setting up a service key,
+a public key, or other form of secret. The second step concerns
+mapping authentication identities to LDAP {{TERM:DN}}'s, which
+depends on how entries are laid out in your directory. An explanation
+of the first step will be given in the next section using Kerberos
+V4 as an example mechanism. The steps necessary for your site's
+authentication mechanism will be similar, but a guide to every
+mechanism available under SASL is beyond the scope of this chapter.
+The second step is described in the section {{SECT:Mapping
+Authentication Identities}}.
+
+
+H3: GSSAPI
+
+This section describes the use of the SASL GSSAPI mechanism and
+Kerberos V with OpenLDAP. It will be assumed that you have Kerberos
+V deployed, you are familiar with the operation of the system, and
+that your users are trained in its use. This section also assumes
+you have familiarized yourself with the use of the GSSAPI mechanism
+by reading {{Configuring GSSAPI and Cyrus SASL}} (provided with
+Cyrus SASL in the {{FILE:doc/gssapi}} file) and successfully
+experimented with the Cyrus provided {{EX:sample_server}} and
+{{EX:sample_client}} applications. General information about
+Kerberos is available at {{URL:http://web.mit.edu/kerberos/www/}}.
+
+To use the GSSAPI mechanism with {{slapd}}(8) one must create a service
+key with a principal for {{ldap}} service within the realm for the host
+on which the service runs. For example, if you run {{slapd}} on
+{{EX:directory.example.com}} and your realm is {{EX:EXAMPLE.COM}},
+you need to create a service key with the principal:
+
+> ldap/directory.example.com@EXAMPLE.COM
+
+When {{slapd}}(8) runs, it must have access to this key. This is
+generally done by placing the key into a keytab file,
+{{FILE:/etc/krb5.keytab}}. See your Kerberos and Cyrus SASL
+documentation for information regarding keytab location settings.
+
+To use the GSSAPI mechanism to authenticate to the directory, the
+user obtains a Ticket Granting Ticket (TGT) prior to running the
+LDAP client. When using OpenLDAP client tools, the user may mandate
+use of the GSSAPI mechanism by specifying {{EX:-Y GSSAPI}} as a
+command option.
+
+For the purposes of authentication and authorization, {{slapd}}(8)
+associates an authentication request DN of the form:
+
+> uid=<primary[/instance][@realm]>,cn=gssapi,cn=auth
+
+The realm is omitted by Cyrus SASL if it's equal to the default realm of the
+server in {{FILE:/etc/krb5.conf}}.
+
+Continuing our example, a user with the Kerberos principal
+{{EX:kurt@EXAMPLE.COM}} would have the associated DN:
+
+> uid=kurt,cn=gssapi,cn=auth
+
+and the principal {{EX:ursula/admin@FOREIGN.REALM}} would have the
+associated DN:
+
+> uid=ursula/admin@foreign.realm,cn=gssapi,cn=auth
+
+
+The authentication request DN can be used directly in ACLs and
+{{EX:groupOfNames}} "member" attributes, since it is of legitimate
+LDAP DN format. Or alternatively, the authentication DN could be
+mapped before use. See the section {{SECT:Mapping Authentication
+Identities}} for details.
+
+If you configure the {{olcSaslRealm}} then it will be inserted as
+an extra component in the authorization DN, regardless of any
+Kerberos realms in use. For example, if you set olcSaslRealm to
+{{EX:example.com}} then you will get:
+
+> uid=kurt,cn=example.com,cn=gssapi,cn=auth
+> uid=ursula/admin@foreign.realm,cn=example.com,cn=gssapi,cn=auth
+
+H3: KERBEROS_V4
+
+This section describes the use of the SASL KERBEROS_V4 mechanism
+with OpenLDAP. It will be assumed that you are familiar with the
+workings of the Kerberos IV security system, and that your site has
+Kerberos IV deployed. Your users should be familiar with
+authentication policy, how to receive credentials in
+a Kerberos ticket cache, and how to refresh expired credentials.
+
+Note: KERBEROS_V4 and Kerberos IV are deprecated in favor of GSSAPI
+and Kerberos V.
+
+Client programs will need to be able to obtain a session key for
+use when connecting to your LDAP server. This allows the LDAP server
+to know the identity of the user, and allows the client to know it
+is connecting to a legitimate server. If encryption layers are to
+be used, the session key can also be used to help negotiate that
+option.
+
+The slapd server runs the service called "{{ldap}}", and the server
+will require a srvtab file with a service key. SASL aware client
+programs will be obtaining an "ldap" service ticket with the user's
+ticket granting ticket (TGT), with the instance of the ticket
+matching the hostname of the OpenLDAP server. For example, if your
+realm is named {{EX:EXAMPLE.COM}} and the slapd server is running
+on the host named {{EX:directory.example.com}}, the {{FILE:/etc/srvtab}}
+file on the server will have a service key
+
+> ldap.directory@EXAMPLE.COM
+
+When an LDAP client is authenticating a user to the directory using
+the KERBEROS_IV mechanism, it will request a session key for that
+same principal, either from the ticket cache or by obtaining a new
+one from the Kerberos server. This will require the TGT to be
+available and valid in the cache as well. If it is not present or
+has expired, the client may print out the message:
+
+> ldap_sasl_interactive_bind_s: Local error
+
+When the service ticket is obtained, it will be passed to the LDAP
+server as proof of the user's identity. The server will extract
+the identity and realm out of the service ticket using SASL
+library calls, and convert them into an {{authentication request
+DN}} of the form
+
+> uid=<username>,cn=<realm>,cn=<mechanism>,cn=auth
+
+So in our above example, if the user's name were "adamson", the
+authentication request DN would be:
+
+> uid=adamson,cn=example.com,cn=kerberos_v4,cn=auth
+
+This authentication request DN can be used directly ACLs or,
+alternatively, mapped prior to use. See the section {{SECT:Mapping
+Authentication Identities}} for details.
+
+
+H3: DIGEST-MD5
+
+This section describes the use of the SASL DIGEST-MD5 mechanism
+using secrets stored either in the directory itself or in Cyrus
+SASL's own database. DIGEST-MD5 relies on the client and the server
+sharing a "secret", usually a password. The server generates a
+challenge and the client a response proving that it knows the shared
+secret. This is much more secure than simply sending the secret
+over the wire.
+
+Cyrus SASL supports several shared-secret mechanisms. To do this,
+it needs access to the plaintext password (unlike mechanisms which
+pass plaintext passwords over the wire, where the server can store
+a hashed version of the password).
+
+The server's copy of the shared-secret may be stored in Cyrus SASL's
+own {{sasldb}} database, in an external system accessed via
+{{saslauthd}}, or in LDAP database itself. In either case it is
+very important to apply file access controls and LDAP access controls
+to prevent exposure of the passwords. The configuration and commands
+discussed in this section assume the use of Cyrus SASL 2.1.
+
+To use secrets stored in {{sasldb}}, simply add users with the
+{{saslpasswd2}} command:
+
+> saslpasswd2 -c <username>
+
+The passwords for such users must be managed with the {{saslpasswd2}}
+command.
+
+To use secrets stored in the LDAP directory, place plaintext passwords
+in the {{EX:userPassword}} attribute. It will be necessary to add
+an option to {{EX:slapd.conf}} to make sure that passwords set using
+the LDAP Password Modify Operation are stored in plaintext:
+
+> password-hash {CLEARTEXT}
+
+Passwords stored in this way can be managed either with {{ldappasswd}}(1)
+or by simply modifying the {{EX:userPassword}} attribute. Regardless of
+where the passwords are stored, a mapping will be needed from
+authentication request DN to user's DN.
+
+The DIGEST-MD5 mechanism produces authentication IDs of the form:
+
+> uid=<username>,cn=<realm>,cn=digest-md5,cn=auth
+
+If the default realm is used, the realm name is omitted from the ID,
+giving:
+
+> uid=<username>,cn=digest-md5,cn=auth
+
+See {{SECT: Mapping Authentication Identities}} below for information
+on optional mapping of identities.
+
+With suitable mappings in place, users can specify SASL IDs when
+performing LDAP operations, and the password stored in {{sasldb}} or in
+the directory itself will be used to verify the authentication.
+For example, the user identified by the directory entry:
+
+> dn: cn=Andrew Findlay+uid=u000997,dc=example,dc=com
+> objectclass: inetOrgPerson
+> objectclass: person
+> sn: Findlay
+> uid: u000997
+> userPassword: secret
+
+can issue commands of the form:
+
+> ldapsearch -Y DIGEST-MD5 -U u000997 ...
+
+Note: in each of the above cases, no authorization identity (e.g.
+{{EX:-X}}) was provided. Unless you are attempting {{SECT:SASL
+Proxy Authorization}}, no authorization identity should be specified.
+The server will infer an authorization identity from authentication
+identity (as described below).
+
+
+H3: EXTERNAL
+
+The SASL EXTERNAL mechanism makes use of an authentication performed
+by a lower-level protocol: usually {{TERM:TLS}} or Unix {{TERM:IPC}}
+
+Each transport protocol returns Authentication Identities in its own
+format:
+
+H4: TLS Authentication Identity Format
+
+This is the Subject DN from the client-side certificate.
+Note that DNs are displayed differently by LDAP and by X.509, so
+a certificate issued to
+> C=gb, O=The Example Organisation, CN=A Person
+
+will produce an authentication identity of:
+
+> cn=A Person,o=The Example Organisation,c=gb
+
+Note that you must set a suitable value for TLSVerifyClient to make the server
+request the use of a client-side certificate. Without this, the SASL EXTERNAL
+mechanism will not be offered.
+Refer to the {{SECT:Using TLS}} chapter for details.
+
+H4: IPC (ldapi:///) Identity Format
+
+This is formed from the Unix UID and GID of the client process:
+
+> gidNumber=<number>+uidNumber=<number>,cn=peercred,cn=external,cn=auth
+
+Thus, a client process running as {{EX:root}} will be:
+
+> gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
+
+
+H3: Mapping Authentication Identities
+
+The authentication mechanism in the slapd server will use SASL
+library calls to obtain the authenticated user's "username", based
+on whatever underlying authentication mechanism was used. This
+username is in the namespace of the authentication mechanism, and
+not in the normal LDAP namespace. As stated in the sections above,
+that username is reformatted into an authentication request DN of
+the form
+
+> uid=<username>,cn=<realm>,cn=<mechanism>,cn=auth
+
+or
+
+> uid=<username>,cn=<mechanism>,cn=auth
+
+depending on whether or not <mechanism> employs the concept of
+"realms". Note also that the realm part will be omitted if the
+default realm was used in the authentication.
+
+The {{ldapwhoami}}(1) command may be used to determine the identity
+associated with the user. It is very useful for determining proper
+function of mappings.
+
+It is not intended that you should add LDAP entries of the above
+form to your LDAP database. Chances are you have an LDAP entry for
+each of the persons that will be authenticating to LDAP, laid out
+in your directory tree, and the tree does not start at cn=auth.
+But if your site has a clear mapping between the "username" and an
+LDAP entry for the person, you will be able to configure your LDAP
+server to automatically map a authentication request DN to the
+user's {{authentication DN}}.
+
+Note: it is not required that the authentication request DN nor the
+user's authentication DN resulting from the mapping refer to an
+entry held in the directory. However, additional capabilities
+become available (see below).
+
+The LDAP administrator will need to tell the slapd server how to
+map an authentication request DN to a user's authentication DN.
+This is done by adding one or more {{EX:authz-regexp}} directives to
+the {{slapd.conf}}(5) file. This directive takes two arguments:
+
+> authz-regexp <search pattern> <replacement pattern>
+
+The authentication request DN is compared to the search pattern
+using the regular expression functions {{regcomp}}() and {{regexec}}(),
+and if it matches, it is rewritten as the replacement pattern. If
+there are multiple {{EX:authz-regexp}} directives, only the first
+whose search pattern matches the authentication identity is used.
+The string that is output from the replacement pattern should be
+the authentication DN of the user or an LDAP URL. If replacement
+string produces a DN, the entry named by this DN need not be held
+by this server. If the replace string produces an LDAP URL, that
+LDAP URL must evaluate to one and only one entry held by this server.
+
+The search pattern can contain any of the regular expression
+characters listed in {{regexec}}(3C). The main characters of note
+are dot ".", asterisk "*", and the open and close parenthesis "("
+and ")". Essentially, the dot matches any character, the asterisk
+allows zero or more repeats of the immediately preceding character
+or pattern, and terms in parenthesis are remembered for the replacement
+pattern.
+
+The replacement pattern will produce either a DN or URL referring
+to the user. Anything from the authentication request DN that
+matched a string in parenthesis in the search pattern is stored in
+the variable "$1". That variable "$1" can appear in the replacement
+pattern, and will be replaced by the string from the authentication
+request DN. If there were multiple sets of parentheses in the search
+pattern, the variables $2, $3, etc are used.
+
+H3: Direct Mapping
+
+Where possible, direct mapping of the authentication request DN to
+the user's DN is generally recommended. Aside from avoiding the
+expense of searching for the user's DN, it allows mapping to
+DNs which refer to entries not held by this server.
+
+Suppose the authentication request DN is written as:
+
+> uid=adamson,cn=example.com,cn=gssapi,cn=auth
+
+and the user's actual LDAP entry is:
+
+> uid=adamson,ou=people,dc=example,dc=com
+
+then the following {{EX:authz-regexp}} directive in {{slapd.conf}}(5)
+would provide for direct mapping.
+
+> authz-regexp
+> uid=([^,]*),cn=example.com,cn=gssapi,cn=auth
+> uid=$1,ou=people,dc=example,dc=com
+
+An even more lenient rule could be written as
+
+> authz-regexp
+> uid=([^,]*),cn=[^,]*,cn=auth
+> uid=$1,ou=people,dc=example,dc=com
+
+Be careful about setting the search pattern too leniently, however,
+since it may mistakenly allow persons to become authenticated as a
+DN to which they should not have access. It is better to write
+several strict directives than one lenient directive which has
+security holes. If there is only one authentication mechanism in
+place at your site, and zero or one realms in use, you might be
+able to map between authentication identities and LDAP DN's with a
+single {{EX:authz-regexp}} directive.
+
+Don't forget to allow for the case where the realm is omitted as
+well as the case with an explicitly specified realm. This may well
+require a separate {{EX:authz-regexp}} directive for each case, with
+the explicit-realm entry being listed first.
+
+H3: Search-based mappings
+
+There are a number of cases where mapping to a LDAP URL may be
+appropriate. For instance, some sites may have person objects
+located in multiple areas of the LDAP tree, such as if there were
+an {{EX:ou=accounting}} tree and an {{EX:ou=engineering}} tree,
+with persons interspersed between them. Or, maybe the desired
+mapping must be based upon information in the user's information.
+Consider the need to map the above authentication request DN to
+user whose entry is as follows:
+
+> dn: cn=Mark Adamson,ou=People,dc=Example,dc=COM
+> objectclass: person
+> cn: Mark Adamson
+> uid: adamson
+
+The information in the authentication request DN is insufficient
+to allow the user's DN to be directly derived, instead the user's
+DN must be searched for. For these situations, a replacement pattern
+which produces a LDAP URL can be used in the {{EX:authz-regexp}}
+directives. This URL will then be used to perform an internal
+search of the LDAP database to find the person's authentication DN.
+
+An LDAP URL, similar to other URL's, is of the form
+
+> ldap://<host>/<base>?<attrs>?<scope>?<filter>
+
+This contains all of the elements necessary to perform an LDAP
+search: the name of the server <host>, the LDAP DN search base
+<base>, the LDAP attributes to retrieve <attrs>, the search scope
+<scope> which is one of the three options "base", "one", or "sub",
+and lastly an LDAP search filter <filter>. Since the search is for
+an LDAP DN within the current server, the <host> portion should be
+empty. The <attrs> field is also ignored since only the DN is of
+concern. These two elements are left in the format of the URL to
+maintain the clarity of what information goes where in the string.
+
+Suppose that the person in the example from above did in fact have
+an authentication username of "adamson" and that information was
+kept in the attribute "uid" in their LDAP entry. The {{EX:authz-regexp}}
+directive might be written as
+
+> authz-regexp
+> uid=([^,]*),cn=example.com,cn=gssapi,cn=auth
+> ldap:///ou=people,dc=example,dc=com??one?(uid=$1)
+
+This will initiate an internal search of the LDAP database inside
+the slapd server. If the search returns exactly one entry, it is
+accepted as being the DN of the user. If there are more than one
+entries returned, or if there are zero entries returned, the
+authentication fails and the user's connection is left bound as the
+authentication request DN.
+
+The attributes that are used in the search filter <filter> in the
+URL should be indexed to allow faster searching. If they are not,
+the authentication step alone can take uncomfortably long periods,
+and users may assume the server is down.
+
+A more complex site might have several realms in use, each mapping
+to a different subtree in the directory. These can be handled with
+statements of the form:
+
+> # Match Engineering realm
+> authz-regexp
+> uid=([^,]*),cn=engineering.example.com,cn=digest-md5,cn=auth
+> ldap:///dc=eng,dc=example,dc=com??one?(&(uid=$1)(objectClass=person))
+>
+> # Match Accounting realm
+> authz-regexp
+> uid=([^,].*),cn=accounting.example.com,cn=digest-md5,cn=auth
+> ldap:///dc=accounting,dc=example,dc=com??one?(&(uid=$1)(objectClass=person))
+>
+> # Default realm is customers.example.com
+> authz-regexp
+> uid=([^,]*),cn=digest-md5,cn=auth
+> ldap:///dc=customers,dc=example,dc=com??one?(&(uid=$1)(objectClass=person))
+
+Note that the explicitly-named realms are handled first, to avoid
+the realm name becoming part of the UID. Also note the use of scope
+and filters to limit matching to desirable entries.
+
+Note as well that {{EX:authz-regexp}} internal search are subject
+to access controls. Specifically, the authentication identity
+must have {{EX:auth}} access.
+
+See {{slapd.conf}}(5) for more detailed information.
+
+
+H2: SASL Proxy Authorization
+
+The SASL offers a feature known as {{proxy authorization}}, which
+allows an authenticated user to request that they act on the behalf
+of another user. This step occurs after the user has obtained an
+authentication DN, and involves sending an authorization identity
+to the server. The server will then make a decision on whether or
+not to allow the authorization to occur. If it is allowed, the
+user's LDAP connection is switched to have a binding DN derived
+from the authorization identity, and the LDAP session proceeds with
+the access of the new authorization DN.
+
+The decision to allow an authorization to proceed depends on the
+rules and policies of the site where LDAP is running, and thus
+cannot be made by SASL alone. The SASL library leaves it up to the
+server to make the decision. The LDAP administrator sets the
+guidelines of who can authorize to what identity by adding information
+into the LDAP database entries. By default, the authorization
+features are disabled, and must be explicitly configured by the
+LDAP administrator before use.
+
+
+H3: Uses of Proxy Authorization
+
+This sort of service is useful when one entity needs to act on the
+behalf of many other users. For example, users may be directed to
+a web page to make changes to their personal information in their
+LDAP entry. The users authenticate to the web server to establish
+their identity, but the web server CGI cannot authenticate to the
+LDAP server as that user to make changes for them. Instead, the
+web server authenticates itself to the LDAP server as a service
+identity, say,
+
+> cn=WebUpdate,dc=example,dc=com
+
+and then it will SASL authorize to the DN of the user. Once so
+authorized, the CGI makes changes to the LDAP entry of the user,
+and as far as the slapd server can tell for its ACLs, it is the
+user themself on the other end of the connection. The user could
+have connected to the LDAP server directly and authenticated as
+themself, but that would require the user to have more knowledge
+of LDAP clients, knowledge which the web page provides in an easier
+format.
+
+Proxy authorization can also be used to limit access to an account
+that has greater access to the database. Such an account, perhaps
+even the root DN specified in {{slapd.conf}}(5), can have a strict
+list of people who can authorize to that DN. Changes to the LDAP
+database could then be only allowed by that DN, and in order to
+become that DN, users must first authenticate as one of the persons
+on the list. This allows for better auditing of who made changes
+to the LDAP database. If people were allowed to authenticate
+directly to the privileged account, possibly through the {{EX:rootpw}}
+{{slapd.conf}}(5) directive or through a {{EX:userPassword}}
+attribute, then auditing becomes more difficult.
+
+Note that after a successful proxy authorization, the original
+authentication DN of the LDAP connection is overwritten by the new
+DN from the authorization request. If a service program is able to
+authenticate itself as its own authentication DN and then authorize
+to other DN's, and it is planning on switching to several different
+identities during one LDAP session, it will need to authenticate
+itself each time before authorizing to another DN (or use a different
+proxy authorization mechanism). The slapd server does not keep
+record of the service program's ability to switch to other DN's.
+On authentication mechanisms like Kerberos this will not require
+multiple connections being made to the Kerberos server, since the
+user's TGT and "ldap" session key are valid for multiple uses for
+the several hours of the ticket lifetime.
+
+
+H3: SASL Authorization Identities
+
+The SASL authorization identity is sent to the LDAP server via the
+{{EX:-X}} switch for {{ldapsearch}}(1) and other tools, or in the
+{{EX:*authzid}} parameter to the {{lutil_sasl_defaults}}() call.
+The identity can be in one of two forms, either
+
+> u:<username>
+
+or
+
+> dn:<dn>
+
+In the first form, the <username> is from the same namespace as
+the authentication identities above. It is the user's username as
+it is referred to by the underlying authentication mechanism.
+Authorization identities of this form are converted into a DN format
+by the same function that the authentication process used, producing
+an {{authorization request DN}} of the form
+
+> uid=<username>,cn=<realm>,cn=<mechanism>,cn=auth
+
+That authorization request DN is then run through the same
+{{EX:authz-regexp}} process to convert it into a legitimate authorization
+DN from the database. If it cannot be converted due to a failed
+search from an LDAP URL, the authorization request fails with
+"inappropriate access". Otherwise, the DN string is now a legitimate
+authorization DN ready to undergo approval.
+
+If the authorization identity was provided in the second form, with
+a {{EX:"dn:"}} prefix, the string after the prefix is already in
+authorization DN form, ready to undergo approval.
+
+
+H3: Proxy Authorization Rules
+
+Once slapd has the authorization DN, the actual approval process
+begins. There are two attributes that the LDAP administrator can
+put into LDAP entries to allow authorization:
+
+> authzTo
+> authzFrom
+
+Both can be multivalued. The {{EX:authzTo}} attribute is a
+source rule, and it is placed into the entry associated with the
+authentication DN to tell what authorization DNs the authenticated
+DN is allowed to assume. The second attribute is a destination
+rule, and it is placed into the entry associated with the requested
+authorization DN to tell which authenticated DNs may assume it.
+
+The choice of which authorization policy attribute to use is up to
+the administrator. Source rules are checked first in the person's
+authentication DN entry, and if none of the {{EX:authzTo}} rules
+specify the authorization is permitted, the {{EX:authzFrom}}
+rules in the authorization DN entry are then checked. If neither
+case specifies that the request be honored, the request is denied.
+Since the default behavior is to deny authorization requests, rules
+only specify that a request be allowed; there are no negative rules
+telling what authorizations to deny.
+
+The value(s) in the two attributes are of the same form as the
+output of the replacement pattern of a {{EX:authz-regexp}} directive:
+either a DN or an LDAP URL. For example, if a {{EX:authzTo}}
+value is a DN, that DN is one the authenticated user can authorize
+to. On the other hand, if the {{EX:authzTo}} value is an LDAP
+URL, the URL is used as an internal search of the LDAP database,
+and the authenticated user can become ANY DN returned by the search.
+If an LDAP entry looked like:
+
+> dn: cn=WebUpdate,dc=example,dc=com
+> authzTo: ldap:///dc=example,dc=com??sub?(objectclass=person)
+
+then any user who authenticated as {{EX:cn=WebUpdate,dc=example,dc=com}}
+could authorize to any other LDAP entry under the search base
+{{EX:dc=example,dc=com}} which has an objectClass of {{EX:Person}}.
+
+
+H4: Notes on Proxy Authorization Rules
+
+An LDAP URL in a {{EX:authzTo}} or {{EX:authzFrom}} attribute
+will return a set of DNs. Each DN returned will be checked. Searches
+which return a large set can cause the authorization process to
+take an uncomfortably long time. Also, searches should be performed
+on attributes that have been indexed by slapd.
+
+To help produce more sweeping rules for {{EX:authzFrom}} and
+{{EX:authzTo}}, the values of these attributes are allowed to
+be DNs with regular expression characters in them. This means a
+source rule like
+
+> authzTo: dn.regex:^uid=[^,]*,dc=example,dc=com$
+
+would allow that authenticated user to authorize to any DN that
+matches the regular expression pattern given. This regular expression
+comparison can be evaluated much faster than an LDAP search for
+{{EX:(uid=*)}}.
+
+Also note that the values in an authorization rule must be one of
+the two forms: an LDAP URL or a DN (with or without regular expression
+characters). Anything that does not begin with "{{EX:ldap://}}" is
+taken as a DN. It is not permissible to enter another authorization
+identity of the form "{{EX:u:<username>}}" as an authorization rule.
+
+
+H4: Policy Configuration
+
+The decision of which type of rules to use, {{EX:authzFrom}}
+or {{EX:authzTo}}, will depend on the site's situation. For
+example, if the set of people who may become a given identity can
+easily be written as a search filter, then a single destination
+rule could be written. If the set of people is not easily defined
+by a search filter, and the set of people is small, it may be better
+to write a source rule in the entries of each of those people who
+should be allowed to perform the proxy authorization.
+
+By default, processing of proxy authorization rules is disabled.
+The {{EX:authz-policy}} directive must be set in the
+{{slapd.conf}}(5) file to enable authorization. This directive can
+be set to {{EX:none}} for no rules (the default), {{EX:to}} for
+source rules, {{EX:from}} for destination rules, or {{EX:both}} for
+both source and destination rules.
+
+Source rules are extremely powerful. If ordinary users have
+access to write the {{EX:authzTo}} attribute in their own
+entries, then they can write rules that would allow them to authorize
+as anyone else. As such, when using source rules, the
+{{EX:authzTo}} attribute should be protected with an ACL that
+only allows privileged users to set its values.
+
diff --git a/doc/guide/admin/schema.sdf b/doc/guide/admin/schema.sdf
new file mode 100644
index 0000000..d80d9cd
--- /dev/null
+++ b/doc/guide/admin/schema.sdf
@@ -0,0 +1,491 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: Schema Specification
+
+This chapter describes how to extend the user schema used by
+{{slapd}}(8). The chapter assumes the reader is familiar with the
+{{TERM:LDAP}}/{{TERM:X.500}} information model.
+
+The first section, {{SECT:Distributed Schema Files}} details optional
+schema definitions provided in the distribution and where to obtain
+other definitions.
+The second section, {{SECT:Extending Schema}}, details how to define
+new schema items.
+!if 0
+The third section, {{SECT:Transferring Schema}} details how you can
+export schema definitions from an LDAPv3 server and transform it
+to {{slapd.conf}}(5) format.
+!endif
+
+This chapter does not discuss how to extend system schema used by
+{{slapd}}(8) as this requires source code modification. System
+schema includes all operational attribute types or any object class
+which allows or requires an operational attribute (directly or
+indirectly).
+
+
+H2: Distributed Schema Files
+
+OpenLDAP Software is distributed with a set of schema specifications for
+your use. Each set is defined in a file suitable for inclusion
+(using the {{EX:include}} directive) in your {{slapd.conf}}(5)
+file. These schema files are normally installed in the
+{{F:/usr/local/etc/openldap/schema}} directory.
+
+!block table; colaligns="LR"; coltags="F,N"; align=Center; \
+ title="Table 8.1: Provided Schema Specifications"
+File Description
+core.schema OpenLDAP {{core}} (required)
+cosine.schema Cosine and Internet X.500 (useful)
+inetorgperson.schema InetOrgPerson (useful)
+misc.schema Assorted (experimental)
+nis.schema Network Information Services (FYI)
+openldap.schema OpenLDAP Project (experimental)
+!endblock
+
+To use any of these schema files, you only need to include the
+desired file in the global definitions portion of your
+{{slapd.conf}}(5) file. For example:
+
+> # include schema
+> include /usr/local/etc/openldap/schema/core.schema
+> include /usr/local/etc/openldap/schema/cosine.schema
+> include /usr/local/etc/openldap/schema/inetorgperson.schema
+
+Additional files may be available. Please consult the OpenLDAP
+{{TERM:FAQ}} ({{URL:http://www.openldap.org/faq/}}).
+
+Note: You should not modify any of the schema items defined
+in provided files.
+
+
+H2: Extending Schema
+
+Schema used by {{slapd}}(8) may be extended to support additional
+syntaxes, matching rules, attribute types, and object classes. This
+chapter details how to add user application attribute types and
+object classes using the syntaxes and matching rules already supported
+by slapd. slapd can also be extended to support additional syntaxes,
+matching rules and system schema, but this requires some programming
+and hence is not discussed here.
+
+There are five steps to defining new schema:
+^ obtain Object Identifier
++ choose a name prefix
++ create local schema file
++ define custom attribute types (if necessary)
++ define custom object classes
+
+
+H3: Object Identifiers
+
+Each schema element is identified by a globally unique {{TERM[expand]OID}}
+(OID). OIDs are also used to identify other objects. They are
+commonly found in protocols described by {{TERM:ASN.1}}. In
+particular, they are heavily used by the {{TERM[expand]SNMP}} (SNMP).
+As OIDs are hierarchical, your organization can obtain one OID and
+branch it as needed. For example, if your organization were assigned
+OID {{EX:1.1}}, you could branch the tree as follows:
+
+!block table; colaligns="LR"; coltags="EX,N"; align=Center; \
+ title="Table 8.2: Example OID hierarchy"
+OID Assignment
+1.1 Organization's OID
+1.1.1 SNMP Elements
+1.1.2 LDAP Elements
+1.1.2.1 AttributeTypes
+1.1.2.1.1 x-my-Attribute
+1.1.2.2 ObjectClasses
+1.1.2.2.1 x-my-ObjectClass
+!endblock
+
+You are, of course, free to design a hierarchy suitable to your
+organizational needs under your organization's OID. No matter what hierarchy you choose, you should maintain a registry of assignments you make. This can be a simple flat file or something more sophisticated such as the {{OpenLDAP OID Registry}} ({{URL:http://www.openldap.org/faq/index.cgi?file=197}}).
+
+For more information about Object Identifiers (and a listing service)
+see {{URL:http://www.alvestrand.no/objectid/}}.
+
+.{{Under no circumstances should you hijack OID namespace!}}
+
+To obtain a registered OID at {{no cost}}, apply for a OID
+under the {{ORG[expand]IANA}} (ORG:IANA) maintained {{Private Enterprise}} arc.
+Any private enterprise (organization) may request a {{TERM[expand]PEN}} (PEN) to be assigned under this arc. Just fill out the IANA form at {{URL: http://pen.iana.org/pen/PenApplication.page}} and your official PEN will be sent to you usually within a few days. Your base OID will be something like {{EX:1.3.6.1.4.1.X}} where {{EX:X}} is an integer.
+
+Note: PENs obtained using this form may be used for any purpose
+including identifying LDAP schema elements.
+
+Alternatively, OID name space may be available from a national
+authority (e.g., {{ORG:ANSI}}, {{ORG:BSI}}).
+
+
+H3: Naming Elements
+
+In addition to assigning a unique object identifier to each schema
+element, you should provide at least one textual name for each
+element. Names should be registered with the {{ORG:IANA}} or
+prefixed with "x-" to place in the "private use" name space.
+
+The name should be both descriptive and not likely to clash with
+names of other schema elements. In particular, any name you choose
+should not clash with present or future Standard Track names (this
+is assured if you registered names or use names beginning with "x-").
+
+It is noted that you can obtain your own registered name
+prefix so as to avoid having to register your names individually.
+See {{REF:RFC4520}} for details.
+
+In the examples below, we have used a short prefix '{{EX:x-my-}}'.
+Such a short prefix would only be suitable for a very large, global
+organization. In general, we recommend something like '{{EX:x-de-Firm-}}'
+(German company) or '{{EX:x-com-Example}}' (elements associated with
+organization associated with {{EX:example.com}}).
+
+
+H3: Local schema file
+
+The {{EX:objectclass}} and {{EX:attributeTypes}} configuration file
+directives can be used to define schema rules on entries in the
+directory. It is customary to create a file to contain definitions
+of your custom schema items. We recommend you create a file
+{{F:local.schema}} in {{F:/usr/local/etc/openldap/schema/local.schema}}
+and then include this file in your {{slapd.conf}}(5) file immediately
+after other schema {{EX:include}} directives.
+
+> # include schema
+> include /usr/local/etc/openldap/schema/core.schema
+> include /usr/local/etc/openldap/schema/cosine.schema
+> include /usr/local/etc/openldap/schema/inetorgperson.schema
+> # include local schema
+> include /usr/local/etc/openldap/schema/local.schema
+
+
+H3: Attribute Type Specification
+
+The {{attributetype}} directive is used to define a new attribute
+type. The directive uses the same Attribute Type Description
+(as defined in {{REF:RFC4512}}) used by the attributeTypes
+attribute found in the subschema subentry, e.g.:
+
+E: attributetype <{{REF:RFC4512}} Attribute Type Description>
+
+where Attribute Type Description is defined by the following
+{{TERM:ABNF}}:
+
+> AttributeTypeDescription = "(" whsp
+> numericoid whsp ; AttributeType identifier
+> [ "NAME" qdescrs ] ; name used in AttributeType
+> [ "DESC" qdstring ] ; description
+> [ "OBSOLETE" whsp ]
+> [ "SUP" woid ] ; derived from this other
+> ; AttributeType
+> [ "EQUALITY" woid ; Matching Rule name
+> [ "ORDERING" woid ; Matching Rule name
+> [ "SUBSTR" woid ] ; Matching Rule name
+> [ "SYNTAX" whsp noidlen whsp ] ; Syntax OID
+> [ "SINGLE-VALUE" whsp ] ; default multi-valued
+> [ "COLLECTIVE" whsp ] ; default not collective
+> [ "NO-USER-MODIFICATION" whsp ]; default user modifiable
+> [ "USAGE" whsp AttributeUsage ]; default userApplications
+> whsp ")"
+>
+> AttributeUsage =
+> "userApplications" /
+> "directoryOperation" /
+> "distributedOperation" / ; DSA-shared
+> "dSAOperation" ; DSA-specific, value depends on server
+>
+
+where whsp is a space ('{{EX: }}'), numericoid is a globally unique
+OID in dotted-decimal form (e.g. {{EX:1.1.0}}), qdescrs is one or
+more names, woid is either the name or OID optionally followed
+by a length specifier (e.g {{EX:{10}}}).
+
+For example, the attribute types {{EX:name}} and {{EX:cn}} are defined
+in {{F:core.schema}} as:
+
+> attributeType ( 2.5.4.41 NAME 'name'
+> DESC 'name(s) associated with the object'
+> EQUALITY caseIgnoreMatch
+> SUBSTR caseIgnoreSubstringsMatch
+> SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )
+> attributeType ( 2.5.4.3 NAME ( 'cn' 'commonName' )
+> DESC 'common name(s) associated with the object'
+> SUP name )
+
+Notice that each defines the attribute's OID, provides a short name,
+and a brief description. Each name is an alias for the OID.
+{{slapd}}(8) returns the first listed name when returning results.
+
+The first attribute, {{EX:name}}, holds values of {{EX:directoryString}}
+({{TERM:UTF-8}} encoded Unicode) syntax. The syntax is
+specified by OID (1.3.6.1.4.1.1466.115.121.1.15 identifies the
+directoryString syntax). A length recommendation of 32768 is
+specified. Servers should support values of this length, but may
+support longer values. The field does NOT specify a size constraint,
+so is ignored on servers (such as slapd) which don't impose such
+size limits. In addition, the equality and substring matching uses
+case ignore rules. Below are tables listing commonly used syntax
+and matching rules ({{slapd}}(8) supports these and many more).
+
+!block table; align=Center; coltags="EX,EX,N"; \
+ title="Table 8.3: Commonly Used Syntaxes"
+Name OID Description
+boolean 1.3.6.1.4.1.1466.115.121.1.7 boolean value
+directoryString 1.3.6.1.4.1.1466.115.121.1.15 Unicode (UTF-8) string
+distinguishedName 1.3.6.1.4.1.1466.115.121.1.12 LDAP {{TERM:DN}}
+integer 1.3.6.1.4.1.1466.115.121.1.27 integer
+numericString 1.3.6.1.4.1.1466.115.121.1.36 numeric string
+OID 1.3.6.1.4.1.1466.115.121.1.38 object identifier
+octetString 1.3.6.1.4.1.1466.115.121.1.40 arbitrary octets
+!endblock
+
+>
+
+!block table; align=Center; coltags="EX,N"; \
+ title="Table 8.4: Commonly Used Matching Rules"
+Name Type Description
+booleanMatch equality boolean
+caseIgnoreMatch equality case insensitive, space insensitive
+caseIgnoreOrderingMatch ordering case insensitive, space insensitive
+caseIgnoreSubstringsMatch substrings case insensitive, space insensitive
+caseExactMatch equality case sensitive, space insensitive
+caseExactOrderingMatch ordering case sensitive, space insensitive
+caseExactSubstringsMatch substrings case sensitive, space insensitive
+distinguishedNameMatch equality distinguished name
+integerMatch equality integer
+integerOrderingMatch ordering integer
+numericStringMatch equality numerical
+numericStringOrderingMatch ordering numerical
+numericStringSubstringsMatch substrings numerical
+octetStringMatch equality octet string
+octetStringOrderingMatch ordering octet string
+octetStringSubstringsMatch ordering octet string
+objectIdentiferMatch equality object identifier
+!endblock
+
+The second attribute, {{EX:cn}}, is a subtype of {{EX:name}} hence
+it inherits the syntax, matching rules, and usage of {{EX:name}}.
+{{EX:commonName}} is an alternative name.
+
+Neither attribute is restricted to a single value. Both are meant
+for usage by user applications. Neither is obsolete nor collective.
+
+The following subsections provide a couple of examples.
+
+
+H4: x-my-UniqueName
+
+Many organizations maintain a single unique name for each user.
+Though one could use {{EX:displayName}} ({{REF:RFC2798}}), this
+attribute is really meant to be controlled by the user, not the
+organization. We could just copy the definition of {{EX:displayName}}
+from {{F:inetorgperson.schema}} and replace the OID, name, and
+description, e.g:
+
+> attributetype ( 1.1.2.1.1 NAME 'x-my-UniqueName'
+> DESC 'unique name with my organization'
+> EQUALITY caseIgnoreMatch
+> SUBSTR caseIgnoreSubstringsMatch
+> SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+> SINGLE-VALUE )
+
+However, if we want this name to be used in {{EX:name}} assertions,
+e.g. {{EX:(name=*Jane*)}}, the attribute could alternatively be
+defined as a subtype of {{EX:name}}, e.g.:
+
+> attributetype ( 1.1.2.1.1 NAME 'x-my-UniqueName'
+> DESC 'unique name with my organization'
+> SUP name )
+
+
+H4: x-my-Photo
+
+Many organizations maintain a photo of each each user. A
+{{EX:x-my-Photo}} attribute type could be defined to hold a photo.
+Of course, one could use just use {{EX:jpegPhoto}} ({{REF:RFC2798}})
+(or a subtype) to hold the photo. However, you can only do
+this if the photo is in {{JPEG File Interchange Format}}.
+Alternatively, an attribute type which uses the {{Octet String}}
+syntax can be defined, e.g.:
+
+> attributetype ( 1.1.2.1.2 NAME 'x-my-Photo'
+> DESC 'a photo (application defined format)'
+> SYNTAX 1.3.6.1.4.1.1466.115.121.1.40
+> SINGLE-VALUE )
+
+In this case, the syntax doesn't specify the format of the photo.
+It's assumed (maybe incorrectly) that all applications accessing
+this attribute agree on the handling of values.
+
+If you wanted to support multiple photo formats, you could define
+a separate attribute type for each format, prefix the photo
+with some typing information, or describe the value using
+{{TERM:ASN.1}} and use the {{EX:;binary}} transfer option.
+
+Another alternative is for the attribute to hold a {{TERM:URI}}
+pointing to the photo. You can model such an attribute after
+{{EX:labeledURI}} ({{REF:RFC2079}}) or simply create a subtype,
+e.g.:
+
+> attributetype ( 1.1.2.1.3 NAME 'x-my-PhotoURI'
+> DESC 'URI and optional label referring to a photo'
+> SUP labeledURI )
+
+
+H3: Object Class Specification
+
+The {{objectclasses}} directive is used to define a new object
+class. The directive uses the same Object Class Description
+(as defined in {{REF:RFC4512}}) used by the objectClasses
+attribute found in the subschema subentry, e.g.:
+
+E: objectclass <{{REF:RFC4512}} Object Class Description>
+
+where Object Class Description is defined by the following
+{{TERM:ABNF}}:
+
+> ObjectClassDescription = "(" whsp
+> numericoid whsp ; ObjectClass identifier
+> [ "NAME" qdescrs ]
+> [ "DESC" qdstring ]
+> [ "OBSOLETE" whsp ]
+> [ "SUP" oids ] ; Superior ObjectClasses
+> [ ( "ABSTRACT" / "STRUCTURAL" / "AUXILIARY" ) whsp ]
+> ; default structural
+> [ "MUST" oids ] ; AttributeTypes
+> [ "MAY" oids ] ; AttributeTypes
+> whsp ")"
+
+where whsp is a space ('{{EX: }}'), numericoid is a globally unique
+OID in dotted-decimal form (e.g. {{EX:1.1.0}}), qdescrs is one or more
+names, and oids is one or more names and/or OIDs.
+
+
+H4: x-my-PhotoObject
+
+To define an {{auxiliary}} object class which allows
+x-my-Photo to be added to any existing entry.
+
+> objectclass ( 1.1.2.2.1 NAME 'x-my-PhotoObject'
+> DESC 'mixin x-my-Photo'
+> AUXILIARY
+> MAY x-my-Photo )
+
+
+H4: x-my-Person
+
+If your organization would like have a private {{structural}}
+object class to instantiate users, you can subclass one of
+the existing person classes, such as {{EX:inetOrgPerson}}
+({{REF:RFC2798}}), and add any additional attributes which
+you desire.
+
+> objectclass ( 1.1.2.2.2 NAME 'x-my-Person'
+> DESC 'my person'
+> SUP inetOrgPerson
+> MUST ( x-my-UniqueName $ givenName )
+> MAY x-my-Photo )
+
+The object class inherits the required/allowed attribute
+types of {{EX:inetOrgPerson}} but requires {{EX:x-my-UniqueName}}
+and {{EX:givenName}} and allows {{EX:x-my-Photo}}.
+
+!if 0
+H2: Transferring Schema
+
+Since the {{slapd.conf}}(5) schema directives use {{REF:RFC4512}}
+format values, you can extract schema elements published by any
+{{TERM:LDAPv3}} server and easily construct directives for use with
+{{slapd}}(8).
+
+LDAPv3 servers publish schema elements in special {{subschema}}
+entries (or subentries). While {{slapd}}(8) publishes a single
+subschema subentry normally named {{EX:cn=Subschema}}, this behavior
+cannot be expected from other servers. The subschema subentry
+controlling a particular entry can be obtained by examining the
+{{EX:subschemaSubentry}} attribute contained in the entry at the
+root of each administrative context. For example,
+
+> ldapsearch -LLL -x -b "dc=example,dc=com" -s base "(objectclass=*)" subschemaSubentry
+
+To obtain the schema from a subschema subentry, you can use
+ldapsearch(1) as follows (replace the search base as needed):
+
+> ldapsearch -LLL -x -b "cn=Subschema" -s base "(objectclass=subschema)" attributeTypes objectClasses
+
+where "cn=Subschema" is the value of subschemaSubentry returned in
+the prior search.
+
+This will return {{TERM:LDIF}} output containing many type/value
+pairs. The following is an abbreviated example:
+
+> dn: cn=Subschema
+> objectClasses: ( 1.1.2.2.2 NAME 'x-my-Person' DESC 'my person' SUP inet
+> OrgPerson MUST ( x-my-UniqueName $ givenName ) MAY x-my-Photo )
+> attributeTypes: ( 1.1.2.1.1 NAME 'x-my-UniqueName' DESC 'unique name wi
+> th my organization' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstrin
+> gsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
+> attributeTypes: ( 1.1.2.1.2 NAME 'x-my-Photo' DESC 'a photo (applicatio
+> n defined format)' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40
+
+Capture the output of the search in a file and then edit the file:
+
++ to contain only desired type/value pairs
+^ join LDIF continuation lines
+^ replace attribute type with directive name
+(e.g. {{EX:s/attributeTypes:/attributeType /}} and
+{{EX:s/objectClasses:/objectClass /}}).
+^ reorder lines so each element is defined before first use
+^ continue long directives over multiple lines
+
+For the three type/value pairs in our example, the edit should
+result in a file with contains of:
+
+> attributetype ( 1.1.2.1.1 NAME 'x-my-UniqueName'
+> DESC 'unique name with my organization'
+> EQUALITY caseIgnoreMatch
+> SUBSTR caseIgnoreSubstringsMatch
+> SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+> SINGLE-VALUE )
+> attributeType ( 1.1.2.1.2 NAME 'x-my-Photo'
+> DESC 'a photo (application defined format)'
+> SYNTAX 1.3.6.1.4.1.1466.115.121.1.40
+> objectClass ( 1.1.2.2.2 NAME 'x-my-Person'
+> DESC 'my person'
+> SUP inetOrgPerson
+> MUST ( x-my-UniqueName $ givenName )
+> MAY x-my-Photo )
+
+Save in an appropriately named file (e.g. {{F:local.schema}}).
+You may now include this file in your {{slapd.conf}}(5) file.
+!endif
+
+
+H3: OID Macros
+
+To ease the management and use of OIDs, {{slapd}}(8) supports
+{{Object Identifier}} macros. The {{EX:objectIdentifier}} directive
+is used to equate a macro (name) with a OID. The OID may possibly
+be derived from a previously defined OID macro. The {{slapd.conf}}(5)
+syntax is:
+
+E: objectIdentifier <name> { <oid> | <name>[:<suffix>] }
+
+The following demonstrates definition of a set of OID macros
+and their use in defining schema elements:
+
+> objectIdentifier myOID 1.1
+> objectIdentifier mySNMP myOID:1
+> objectIdentifier myLDAP myOID:2
+> objectIdentifier myAttributeType myLDAP:1
+> objectIdentifier myObjectClass myLDAP:2
+> attributetype ( myAttributeType:3 NAME 'x-my-PhotoURI'
+> DESC 'URI and optional label referring to a photo'
+> SUP labeledURI )
+> objectclass ( myObjectClass:1 NAME 'x-my-PhotoObject'
+> DESC 'mixin x-my-Photo'
+> AUXILIARY
+> MAY x-my-Photo )
+
diff --git a/doc/guide/admin/security.sdf b/doc/guide/admin/security.sdf
new file mode 100644
index 0000000..825f376
--- /dev/null
+++ b/doc/guide/admin/security.sdf
@@ -0,0 +1,398 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# Portions Copyright 2008 Andrew Findlay.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: Security Considerations
+
+OpenLDAP Software is designed to run in a wide variety of computing
+environments from tightly-controlled closed networks to the global
+Internet. Hence, OpenLDAP Software supports many different security
+mechanisms. This chapter describes these mechanisms and discusses
+security considerations for using OpenLDAP Software.
+
+H2: Network Security
+
+H3: Selective Listening
+
+By default, {{slapd}}(8) will listen on both the IPv4 and IPv6 "any"
+addresses. It is often desirable to have {{slapd}} listen on select
+address/port pairs. For example, listening only on the IPv4 address
+{{EX:127.0.0.1}} will disallow remote access to the directory server.
+E.g.:
+
+> slapd -h ldap://127.0.0.1
+
+While the server can be configured to listen on a particular interface
+address, this doesn't necessarily restrict access to the server to
+only those networks accessible via that interface. To selective
+restrict remote access, it is recommend that an {{SECT:IP Firewall}}
+be used to restrict access.
+
+See {{SECT:Command-line Options}} and {{slapd}}(8) for more
+information.
+
+
+H3: IP Firewall
+
+{{TERM:IP}} firewall capabilities of the server system can be used
+to restrict access based upon the client's IP address and/or network
+interface used to communicate with the client.
+
+Generally, {{slapd}}(8) listens on port 389/tcp for {{F:ldap://}}
+sessions and port 636/tcp for {{F:ldaps://}}) sessions. {{slapd}}(8)
+may be configured to listen on other ports.
+
+As specifics of how to configure IP firewall are dependent on the
+particular kind of IP firewall used, no examples are provided here.
+See the document associated with your IP firewall.
+
+
+H3: TCP Wrappers
+
+{{slapd}}(8) supports {{TERM:TCP}} Wrappers. TCP Wrappers provide
+a rule-based access control system for controlling TCP/IP access
+to the server. For example, the {{host_options}}(5) rule:
+
+> slapd: 10.0.0.0/255.0.0.0 127.0.0.1 : ALLOW
+> slapd: ALL : DENY
+
+allows only incoming connections from the private network {{F:10.0.0.0}}
+and localhost ({{F:127.0.0.1}}) to access the directory service.
+
+Note: IP addresses are used as {{slapd}}(8) is not normally
+configured to perform reverse lookups.
+
+It is noted that TCP wrappers require the connection to be accepted.
+As significant processing is required just to deny a connection,
+it is generally advised that IP firewall protection be used instead
+of TCP wrappers.
+
+See {{hosts_access}}(5) for more information on TCP wrapper rules.
+
+
+H2: Data Integrity and Confidentiality Protection
+
+{{TERM[expand]TLS}} (TLS) can be used to provide data integrity and
+confidentiality protection. OpenLDAP supports negotiation of
+{{TERM:TLS}} ({{TERM:SSL}}) via both StartTLS and {{F:ldaps://}}.
+See the {{SECT:Using TLS}} chapter for more information. StartTLS
+is the standard track mechanism.
+
+A number of {{TERM[expand]SASL}} (SASL) mechanisms, such as
+{{TERM:DIGEST-MD5}} and {{TERM:GSSAPI}}, also provide data integrity
+and confidentiality protection. See the {{SECT:Using SASL}} chapter
+for more information.
+
+
+H3: Security Strength Factors
+
+The server uses {{TERM[expand]SSF}}s (SSF) to indicate the relative
+strength of protection. A SSF of zero (0) indicates no protections
+are in place. A SSF of one (1) indicates integrity protection are
+in place. A SSF greater than one (>1) roughly correlates to the
+effective encryption key length. For example, {{TERM:DES}} is 56,
+{{TERM:3DES}} is 112, and {{TERM:AES}} 128, 192, or 256.
+
+A number of administrative controls rely on SSFs associated with
+TLS and SASL protection in place on an LDAP session.
+
+{{EX:security}} controls disallow operations when appropriate
+protections are not in place. For example:
+
+> security ssf=1 update_ssf=112
+
+requires integrity protection for all operations and encryption
+protection, 3DES equivalent, for update operations (e.g. add, delete,
+modify, etc.). See {{slapd.conf}}(5) for details.
+
+For fine-grained control, SSFs may be used in access controls.
+See the {{SECT:Access Control}} section for more information.
+
+
+H2: Authentication Methods
+
+H3: "simple" method
+
+The LDAP "simple" method has three modes of operation:
+
+* anonymous,
+* unauthenticated, and
+* user/password authenticated.
+
+Anonymous access is requested by providing no name and no password
+to the "simple" bind operation. Unauthenticated access is requested
+by providing a name but no password. Authenticated access is
+requested by providing a valid name and password.
+
+An anonymous bind results in an {{anonymous}} authorization
+association. Anonymous bind mechanism is enabled by default, but
+can be disabled by specifying "{{EX:disallow bind_anon}}" in
+{{slapd.conf}}(5).
+
+Note: Disabling the anonymous bind mechanism does not prevent
+anonymous access to the directory. To require authentication to
+access the directory, one should instead specify "{{EX:require authc}}".
+
+An unauthenticated bind also results in an {{anonymous}} authorization
+association. Unauthenticated bind mechanism is disabled by default,
+but can be enabled by specifying "{{EX:allow bind_anon_cred}}" in
+{{slapd.conf}}(5). As a number of LDAP applications mistakenly
+generate unauthenticated bind request when authenticated access was
+intended (that is, they do not ensure a password was provided),
+this mechanism should generally remain disabled.
+
+A successful user/password authenticated bind results in a user
+authorization identity, the provided name, being associated with
+the session. User/password authenticated bind is enabled by default.
+However, as this mechanism itself offers no eavesdropping protection
+(e.g., the password is set in the clear), it is recommended that
+it be used only in tightly controlled systems or when the LDAP
+session is protected by other means (e.g., TLS, {{TERM:IPsec}}).
+Where the administrator relies on TLS to protect the password, it
+is recommended that unprotected authentication be disabled. This
+is done using the {{EX:security}} directive's {{EX:simple_bind}}
+option, which provides fine grain control over the level of confidential
+protection to require for {{simple}} user/password authentication.
+E.g., using {{EX:security simple_bind=56}} would require {{simple}}
+binds to use encryption of DES equivalent or better.
+
+The user/password authenticated bind mechanism can be completely
+disabled by setting "{{EX:disallow bind_simple}}".
+
+Note: An unsuccessful bind always results in the session having
+an {{anonymous}} authorization association.
+
+
+H3: SASL method
+
+The LDAP {{TERM:SASL}} method allows the use of any SASL authentication
+mechanism. The {{SECT:Using SASL}} section discusses the use of SASL.
+
+H2: Password Storage
+
+LDAP passwords are normally stored in the {{userPassword}} attribute.
+{{REF:RFC4519}} specifies that passwords are not stored in encrypted
+(or hashed) form. This allows a wide range of password-based
+authentication mechanisms, such as {{EX:DIGEST-MD5}} to be used.
+This is also the most interoperable storage scheme.
+
+However, it may be desirable to store a hash of password instead.
+{{slapd}}(8) supports a variety of storage schemes for the administrator
+to choose from.
+
+Note: Values of password attributes, regardless of storage scheme
+used, should be protected as if they were clear text. Hashed
+passwords are subject to {{dictionary attacks}} and {{brute-force
+attacks}}.
+
+The {{userPassword}} attribute is allowed to have more than one value,
+and it is possible for each value to be stored in a different form.
+During authentication, {{slapd}} will iterate through the values
+until it finds one that matches the offered password or until it
+runs out of values to inspect. The storage scheme is stored as a prefix
+on the value, so a hashed password using the Salted SHA1 ({{EX:SSHA}})
+scheme looks like:
+
+> userPassword: {SSHA}DkMTwBl+a/3DQTxCYEApdUtNXGgdUac3
+
+The advantage of hashed passwords is that an attacker which
+discovers the hash does not have direct access to the actual password.
+Unfortunately, as dictionary and brute force attacks are generally
+quite easy for attackers to successfully mount, this advantage is
+marginal at best (this is why all modern Unix systems use shadow
+password files).
+
+The disadvantages of hashed storage is that they are non-standard, may
+cause interoperability problem, and generally preclude the use
+of stronger than Simple (or SASL/PLAIN) password-based authentication
+mechanisms such as {{EX:DIGEST-MD5}}.
+
+H3: SSHA password storage scheme
+
+This is the salted version of the SHA scheme. It is believed to be the
+most secure password storage scheme supported by {{slapd}}.
+
+These values represent the same password:
+
+> userPassword: {SSHA}DkMTwBl+a/3DQTxCYEApdUtNXGgdUac3
+> userPassword: {SSHA}d0Q0626PSH9VUld7yWpR0k6BlpQmtczb
+
+H3: CRYPT password storage scheme
+
+This scheme uses the operating system's {{crypt(3)}} hash function.
+It normally produces the traditional Unix-style 13 character hash, but
+on systems with {{EX:glibc2}} it can also generate the more secure
+34-byte MD5 hash.
+
+> userPassword: {CRYPT}aUihad99hmev6
+> userPassword: {CRYPT}$1$czBJdDqS$TmkzUAb836oMxg/BmIwN.1
+
+The advantage of the CRYPT scheme is that passwords can be
+transferred to or from an existing Unix password file without having
+to know the cleartext form. Both forms of {{crypt}} include salt so
+they have some resistance to dictionary attacks.
+
+Note: Since this scheme uses the operating system's {{crypt(3)}}
+hash function, it is therefore operating system specific.
+
+H3: MD5 password storage scheme
+
+This scheme simply takes the MD5 hash of the password and stores it in
+base64 encoded form:
+
+> userPassword: {MD5}Xr4ilOzQ4PCOq3aQ0qbuaQ==
+
+Although safer than cleartext storage, this is not a very secure
+scheme. The MD5 algorithm is fast, and because there is no salt the
+scheme is vulnerable to a dictionary attack.
+
+H3: SMD5 password storage scheme
+
+This improves on the basic MD5 scheme by adding salt (random data
+which means that there are many possible representations of a given
+plaintext password). For example, both of these values represent the
+same password:
+
+> userPassword: {SMD5}4QWGWZpj9GCmfuqEvm8HtZhZS6E=
+> userPassword: {SMD5}g2/J/7D5EO6+oPdklp5p8YtNFk4=
+
+H3: SHA password storage scheme
+
+Like the MD5 scheme, this simply feeds the password through an SHA
+hash process. SHA is thought to be more secure than MD5, but the lack
+of salt leaves the scheme exposed to dictionary attacks.
+
+> userPassword: {SHA}5en6G6MezRroT3XKqkdPOmY/BfQ=
+
+H3: SASL password storage scheme
+
+This is not really a password storage scheme at all. It uses the
+value of the {{userPassword}} attribute to delegate password
+verification to another process. See below for more information.
+
+Note: This is not the same as using SASL to authenticate the LDAP
+session.
+
+H2: Pass-Through authentication
+
+Since OpenLDAP 2.0 {{slapd}} has had the ability to delegate password
+verification to a separate process. This uses the {{sasl_checkpass(3)}}
+function so it can use any back-end server that Cyrus SASL supports for
+checking passwords. The choice is very wide, as one option is to use
+{{saslauthd(8)}} which in turn can use local files, Kerberos, an IMAP
+server, another LDAP server, or anything supported by the PAM mechanism.
+
+The server must be built with the {{EX:--enable-spasswd}}
+configuration option to enable pass-through authentication.
+
+Note: This is not the same as using a SASL mechanism to
+authenticate the LDAP session.
+
+Pass-Through authentication works only with plaintext passwords, as
+used in the "simple bind" and "SASL PLAIN" authentication mechanisms.
+
+Pass-Through authentication is selective: it only affects users whose
+{{userPassword}} attribute has a value marked with the "{SASL}"
+scheme. The format of the attribute is:
+
+> userPassword: {SASL}username@realm
+
+The {{username}} and {{realm}} are passed to the SASL authentication
+mechanism and are used to identify the account whose password is to be
+verified. This allows arbitrary mapping between entries in OpenLDAP
+and accounts known to the backend authentication service.
+
+It would be wise to use access control to prevent users from changing
+their passwords through LDAP where they have pass-through authentication
+enabled.
+
+
+H3: Configuring slapd to use an authentication provider
+
+Where an entry has a "{SASL}" password value, OpenLDAP delegates the
+whole process of validating that entry's password to Cyrus SASL. All
+the configuration is therefore done in SASL config files.
+
+The first
+file to be considered is confusingly named {{slapd.conf}} and is
+typically found in the SASL library directory, often
+{{EX:/usr/lib/sasl2/slapd.conf}} This file governs the use of SASL
+when talking LDAP to {{slapd}} as well as the use of SASL backends for
+pass-through authentication. See {{EX:options.html}} in the {{PRD:Cyrus SASL}}
+docs for full details. Here is a simple example for a server that will
+use {{saslauthd}} to verify passwords:
+
+> mech_list: plain
+> pwcheck_method: saslauthd
+> saslauthd_path: /var/run/sasl2/mux
+
+H3: Configuring saslauthd
+
+{{saslauthd}} is capable of using many different authentication
+services: see {{saslauthd(8)}} for details. A common requirement is to
+delegate some or all authentication to another LDAP server. Here is a
+sample {{EX:saslauthd.conf}} that uses Microsoft Active Directory (AD):
+
+> ldap_servers: ldap://dc1.example.com/ ldap://dc2.example.com/
+>
+> ldap_search_base: cn=Users,DC=ad,DC=example,DC=com
+> ldap_filter: (userPrincipalName=%u)
+>
+> ldap_bind_dn: cn=saslauthd,cn=Users,DC=ad,DC=example,DC=com
+> ldap_password: secret
+
+In this case, {{saslauthd}} is run with the {{EX:ldap}} authentication
+mechanism and is set to combine the SASL realm with the login name:
+
+> saslauthd -a ldap -r
+
+This means that the "username@realm" string from the {{userPassword}}
+attribute ends up being used to search AD for
+"userPrincipalName=username@realm" - the password is then verified by
+attempting to bind to AD using the entry found by the search and the
+password supplied by the LDAP client.
+
+H3: Testing pass-through authentication
+
+It is usually best to start with the back-end authentication provider
+and work through {{saslauthd}} and {{slapd}} towards the LDAP client.
+
+In the AD example above, first check that the DN and password that
+{{saslauthd}} will use when it connects to AD are valid:
+
+> ldapsearch -x -H ldap://dc1.example.com/ \
+> -D cn=saslauthd,cn=Users,DC=ad,DC=example,DC=com \
+> -w secret \
+> -b '' \
+> -s base
+
+Next check that a sample AD user can be found:
+
+> ldapsearch -x -H ldap://dc1.example.com/ \
+> -D cn=saslauthd,cn=Users,DC=ad,DC=example,DC=com \
+> -w secret \
+> -b cn=Users,DC=ad,DC=example,DC=com \
+> "(userPrincipalName=user@ad.example.com)"
+
+Check that the user can bind to AD:
+
+> ldapsearch -x -H ldap://dc1.example.com/ \
+> -D cn=user,cn=Users,DC=ad,DC=example,DC=com \
+> -w userpassword \
+> -b cn=user,cn=Users,DC=ad,DC=example,DC=com \
+> -s base \
+> "(objectclass=*)"
+
+If all that works then {{saslauthd}} should be able to do the same:
+
+> testsaslauthd -u user@ad.example.com -p userpassword
+> testsaslauthd -u user@ad.example.com -p wrongpassword
+
+Now put the magic token into an entry in OpenLDAP:
+
+> userPassword: {SASL}user@ad.example.com
+
+It should now be possible to bind to OpenLDAP using the DN of that
+entry and the password of the AD user.
+
diff --git a/doc/guide/admin/set-following-references.png b/doc/guide/admin/set-following-references.png
new file mode 100644
index 0000000..2e6ef93
--- /dev/null
+++ b/doc/guide/admin/set-following-references.png
Binary files differ
diff --git a/doc/guide/admin/set-memberUid.png b/doc/guide/admin/set-memberUid.png
new file mode 100644
index 0000000..0b8e037
--- /dev/null
+++ b/doc/guide/admin/set-memberUid.png
Binary files differ
diff --git a/doc/guide/admin/set-recursivegroup.png b/doc/guide/admin/set-recursivegroup.png
new file mode 100644
index 0000000..604a5db
--- /dev/null
+++ b/doc/guide/admin/set-recursivegroup.png
Binary files differ
diff --git a/doc/guide/admin/slapdconf2.sdf b/doc/guide/admin/slapdconf2.sdf
new file mode 100644
index 0000000..e2bc162
--- /dev/null
+++ b/doc/guide/admin/slapdconf2.sdf
@@ -0,0 +1,1264 @@
+# $OpenLDAP$
+# Copyright 2005-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: Configuring slapd
+
+Once the software has been built and installed, you are ready
+to configure {{slapd}}(8) for use at your site.
+
+OpenLDAP 2.3 and later have transitioned to using a dynamic runtime
+configuration engine, {{slapd-config}}(5). {{slapd-config}}(5)
+* is fully LDAP-enabled
+* is managed using the standard LDAP operations
+* stores its configuration data in an {{TERM:LDIF}} database, generally
+in the {{F:/usr/local/etc/openldap/slapd.d}} directory.
+* allows all of slapd's configuration options to be changed on the fly,
+generally without requiring a server restart for the changes
+to take effect.
+
+This chapter describes the general format of the {{slapd-config}}(5)
+configuration system, followed by a detailed description of commonly used
+settings.
+
+The older style {{slapd.conf}}(5) file is still supported, but its use
+is deprecated and support for it will be withdrawn in a future OpenLDAP
+release. Configuring {{slapd}}(8) via {{slapd.conf}}(5) is described in
+the next chapter.
+
+Refer to {{slapd}}(8) for information on how to have slapd automatically
+convert from {{slapd.conf}}(5) to {{slapd-config}}(5).
+
+
+Note: Although the {{slapd-config}}(5) system stores its configuration
+as (text-based) LDIF files, you should {{1:never}} edit any of
+the LDIF files directly. Configuration changes should be performed via LDAP
+operations, e.g. {{ldapadd}}(1), {{ldapdelete}}(1), or {{ldapmodify}}(1).
+For offline modifications (when the server is not running), use {{slapadd}}(8)
+and {{slapmodify}}(8).
+
+
+Note: You will need to continue to use the older {{slapd.conf}}(5)
+configuration system if your OpenLDAP installation requires the use of one
+or more backends or overlays that have not been updated to use the
+{{slapd-config}}(5) system. As of OpenLDAP 2.4.33, all of the official
+backends have been updated. There may be additional contributed or experimental
+overlays that also have not been updated.
+
+
+H2: Configuration Layout
+
+The slapd configuration is stored as a special LDAP directory with
+a predefined schema and DIT. There are specific objectClasses used to
+carry global configuration options, schema definitions, backend and
+database definitions, and assorted other items. A sample config tree
+is shown in Figure 5.1.
+
+!import "config_dit.png"; align="center"; title="Sample configuration tree"
+FT[align="Center"] Figure 5.1: Sample configuration tree.
+
+Other objects may be part of the configuration but were omitted from
+the illustration for clarity.
+
+The {{slapd-config}} configuration tree has a very specific structure. The
+root of the tree is named {{EX:cn=config}} and contains global configuration
+settings. Additional settings are contained in separate child entries:
+* Dynamically loaded modules
+.. These may only be used if the {{EX:--enable-modules}} option was
+used to configure the software.
+* Schema definitions
+.. The {{EX:cn=schema,cn=config}} entry contains the system schema (all
+the schema that is hard-coded in slapd).
+.. Child entries of {{EX:cn=schema,cn=config}} contain user schema as
+loaded from config files or added at runtime.
+* Backend-specific configuration
+* Database-specific configuration
+.. Overlays are defined in children of the Database entry.
+.. Databases and Overlays may also have other miscellaneous children.
+
+The usual rules for LDIF files apply to the configuration information:
+Comment lines beginning with a '{{EX:#}}' character
+are ignored. If a line begins with a single space, it is considered a
+continuation of the previous line (even if the previous line is a
+comment) and the single leading space is removed. Entries are separated by blank lines.
+
+The general layout of the config LDIF is as follows:
+
+> # global configuration settings
+> dn: cn=config
+> objectClass: olcGlobal
+> cn: config
+> <global config settings>
+>
+> # schema definitions
+> dn: cn=schema,cn=config
+> objectClass: olcSchemaConfig
+> cn: schema
+> <system schema>
+>
+> dn: cn={X}core,cn=schema,cn=config
+> objectClass: olcSchemaConfig
+> cn: {X}core
+> <core schema>
+>
+> # additional user-specified schema
+> ...
+>
+> # backend definitions
+> dn: olcBackend=<typeA>,cn=config
+> objectClass: olcBackendConfig
+> olcBackend: <typeA>
+> <backend-specific settings>
+>
+> # database definitions
+> dn: olcDatabase={X}<typeA>,cn=config
+> objectClass: olcDatabaseConfig
+> olcDatabase: {X}<typeA>
+> <database-specific settings>
+>
+> # subsequent definitions and settings
+> ...
+
+Some of the entries listed above have a numeric index {{EX:"{X}"}} in
+their names. While most configuration settings have an inherent ordering
+dependency (i.e., one setting must take effect before a subsequent one
+may be set), LDAP databases are inherently unordered. The numeric index
+is used to enforce a consistent ordering in the configuration database,
+so that all ordering dependencies are preserved. In most cases the index
+does not have to be provided; it will be automatically generated based
+on the order in which entries are created.
+
+Configuration directives are specified as values of individual
+attributes.
+Most of the attributes and objectClasses used in the slapd
+configuration have a prefix of {{EX:"olc"}} (OpenLDAP Configuration)
+in their names. Generally there is a one-to-one correspondence
+between the attributes and the old-style {{EX:slapd.conf}} configuration
+keywords, using the keyword as the attribute name, with the "olc"
+prefix attached.
+
+A configuration directive may take arguments. If so, the arguments are
+separated by whitespace. If an argument contains whitespace,
+the argument should be enclosed in double quotes {{EX:"like this"}}.
+In the descriptions that follow, arguments that should be replaced
+by actual text are shown in brackets {{EX:<>}}.
+
+The distribution contains an example configuration file that will
+be installed in the {{F: /usr/local/etc/openldap}} directory.
+A number of files containing schema definitions (attribute types
+and object classes) are also provided in the
+{{F: /usr/local/etc/openldap/schema}} directory.
+
+
+H2: Configuration Directives
+
+This section details commonly used configuration directives. For
+a complete list, see the {{slapd-config}}(5) manual page. This section
+will treat the configuration directives in a top-down order, starting
+with the global directives in the {{EX:cn=config}} entry. Each
+directive will be described along with its default value (if any) and
+an example of its use.
+
+
+H3: cn=config
+
+Directives contained in this entry generally apply to the server as a whole.
+Most of them are system or connection oriented, not database related. This
+entry must have the {{EX:olcGlobal}} objectClass.
+
+
+H4: olcIdleTimeout: <integer>
+
+Specify the number of seconds to wait before forcibly closing
+an idle client connection. A value of 0, the default,
+disables this feature.
+
+
+H4: olcLogLevel: <level>
+
+This directive specifies the level at which log statements
+and operation statistics should be sent to syslog (currently logged to
+the {{syslogd}}(8) {{EX:LOG_LOCAL4}} facility). You must have
+configured OpenLDAP {{EX:--enable-debug}} (the default) for this
+to work, except for the two statistics levels, which are always
+enabled. Log levels may be specified as integers or by keyword.
+Multiple log levels may be used and the levels are additive.
+The possible values for <level> are:
+
+!block table; colaligns="RL"; align=Center; \
+ title="Table 5.1: Logging Levels"
+Level Keyword Description
+-1 any enable all debugging
+0 no debugging
+1 (0x1 trace) trace function calls
+2 (0x2 packets) debug packet handling
+4 (0x4 args) heavy trace debugging
+8 (0x8 conns) connection management
+16 (0x10 BER) print out packets sent and received
+32 (0x20 filter) search filter processing
+64 (0x40 config) configuration processing
+128 (0x80 ACL) access control list processing
+256 (0x100 stats) stats log connections/operations/results
+512 (0x200 stats2) stats log entries sent
+1024 (0x400 shell) print communication with shell backends
+2048 (0x800 parse) print entry parsing debugging
+16384 (0x4000 sync) syncrepl consumer processing
+32768 (0x8000 none) only messages that get logged regardless of configured log level
+!endblock
+
+The desired log level can be input as a single integer that
+combines the (ORed) desired levels, both in decimal or in hexadecimal
+notation, as a list of integers (that are ORed internally), or as a list of the names that are shown between brackets, such that
+
+> olcLogLevel 129
+> olcLogLevel 0x81
+> olcLogLevel 128 1
+> olcLogLevel 0x80 0x1
+> olcLogLevel acl trace
+
+are equivalent.
+
+\Examples:
+
+E: olcLogLevel -1
+
+This will enable all log levels.
+
+E: olcLogLevel conns filter
+
+Just log the connection and search filter processing.
+
+E: olcLogLevel none
+
+Log those messages that are logged regardless of the configured loglevel. This
+differs from setting the log level to 0, when no logging occurs. At least the
+{{EX:None}} level is required to have high priority messages logged.
+
+\Default:
+
+E: olcLogLevel stats
+
+Basic stats logging is configured by default.
+
+H4: olcReferral <URI>
+
+This directive specifies the referral to pass back when slapd
+cannot find a local database to handle a request.
+
+\Example:
+
+> olcReferral: ldap://root.openldap.org
+
+This will refer non-local queries to the global root LDAP server
+at the OpenLDAP Project. Smart LDAP clients can re-ask their
+query at that server, but note that most of these clients are
+only going to know how to handle simple LDAP URLs that
+contain a host part and optionally a distinguished name part.
+
+
+H4: Sample Entry
+
+>dn: cn=config
+>objectClass: olcGlobal
+>cn: config
+>olcIdleTimeout: 30
+>olcLogLevel: Stats
+>olcReferral: ldap://root.openldap.org
+
+
+H3: cn=module
+
+If support for dynamically loaded modules was enabled when configuring
+slapd, {{EX:cn=module}} entries may be used to specify sets of modules to load.
+Module entries must have the {{EX:olcModuleList}} objectClass.
+
+
+H4: olcModuleLoad: <filename>
+
+Specify the name of a dynamically loadable module to load. The filename
+may be an absolute path name or a simple filename. Non-absolute names
+are searched for in the directories specified by the {{EX:olcModulePath}}
+directive.
+
+
+H4: olcModulePath: <pathspec>
+
+Specify a list of directories to search for loadable modules. Typically the
+path is colon-separated but this depends on the operating system.
+
+
+H4: Sample Entries
+
+>dn: cn=module{0},cn=config
+>objectClass: olcModuleList
+>cn: module{0}
+>olcModuleLoad: /usr/local/lib/smbk5pwd.la
+>
+>dn: cn=module{1},cn=config
+>objectClass: olcModuleList
+>cn: module{1}
+>olcModulePath: /usr/local/lib:/usr/local/lib/slapd
+>olcModuleLoad: accesslog.la
+>olcModuleLoad: pcache.la
+
+
+H3: cn=schema
+
+The cn=schema entry holds all of the schema definitions that are hard-coded
+in slapd. As such, the values in this entry are generated by slapd so no
+schema values need to be provided in the config file. The entry must still
+be defined though, to serve as a base for the user-defined schema to add
+in underneath. Schema entries must have the {{EX:olcSchemaConfig}}
+objectClass.
+
+
+H4: olcAttributeTypes: <{{REF:RFC4512}} Attribute Type Description>
+
+This directive defines an attribute type.
+Please see the {{SECT:Schema Specification}} chapter
+for information regarding how to use this directive.
+
+
+H4: olcObjectClasses: <{{REF:RFC4512}} Object Class Description>
+
+This directive defines an object class.
+Please see the {{SECT:Schema Specification}} chapter for
+information regarding how to use this directive.
+
+
+H4: Sample Entries
+
+>dn: cn=schema,cn=config
+>objectClass: olcSchemaConfig
+>cn: schema
+>
+>dn: cn=test,cn=schema,cn=config
+>objectClass: olcSchemaConfig
+>cn: test
+>olcAttributeTypes: ( 1.1.1
+> NAME 'testAttr'
+> EQUALITY integerMatch
+> SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+>olcAttributeTypes: ( 1.1.2 NAME 'testTwo' EQUALITY caseIgnoreMatch
+> SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 )
+>olcObjectClasses: ( 1.1.3 NAME 'testObject'
+> MAY ( testAttr $ testTwo ) AUXILIARY )
+
+
+H3: Backend-specific Directives
+
+Backend directives apply to all database instances of the
+same type and, depending on the directive, may be overridden
+by database directives. Backend entries must have the
+{{EX:olcBackendConfig}} objectClass.
+
+H4: olcBackend: <type>
+
+This directive names a backend-specific configuration entry.
+{{EX:<type>}} should be one of the
+supported backend types listed in Table 5.2.
+
+!block table; align=Center; coltags="EX,N"; \
+ title="Table 5.2: Database Backends"
+Types Description
+asyncmeta Asynchronous Metadirectory backend
+config Slapd configuration backend
+dnssrv DNS SRV backend
+ldap Lightweight Directory Access Protocol (Proxy) backend
+ldif Lightweight Data Interchange Format backend
+mdb Memory-Mapped DB backend
+meta Metadirectory backend
+monitor Monitor backend
+ndb MySQL NDB backend
+null Null backend
+passwd Provides read-only access to {{passwd}}(5)
+perl Perl Programmable backend
+relay Relay backend
+sock Socket backend
+sql SQL Programmable backend
+wt WiredTiger backend
+!endblock
+
+\Example:
+
+> olcBackend: mdb
+
+This marks the beginning of a new {{TERM:MDB}} backend
+definition. At present, only back-mdb implements any options
+of this type, so this setting is not needed for any other backends.
+
+
+H4: Sample Entry
+
+> dn: olcBackend=mdb,cn=config
+> objectClass: olcBackendConfig
+> olcBackend: mdb
+> olcBkMdbIdlExp: 16
+
+
+H3: Database-specific Directives
+
+Directives in this section are supported by every type of database.
+Database entries must have the {{EX:olcDatabaseConfig}} objectClass.
+
+H4: olcDatabase: [{<index>}]<type>
+
+This directive names a specific database instance. The numeric {<index>} may
+be provided to distinguish multiple databases of the same type. Usually the
+index can be omitted, and slapd will generate it automatically.
+{{EX:<type>}} should be one of the
+supported backend types listed in Table 5.2 or the {{EX:frontend}} type.
+
+The {{EX:frontend}} is a special database that is used to hold
+database-level options that should be applied to all the other
+databases. Subsequent database definitions may also override some
+frontend settings.
+
+The {{EX:config}} database is also special; both the {{EX:config}} and
+the {{EX:frontend}} databases are always created implicitly even if they
+are not explicitly configured, and they are created before any other
+databases.
+
+\Example:
+
+> olcDatabase: mdb
+
+This marks the beginning of a new {{TERM:MDB}} database instance.
+
+
+H4: olcAccess: to <what> [ by <who> [<accesslevel>] [<control>] ]+
+
+This directive grants access (specified by <accesslevel>) to a
+set of entries and/or attributes (specified by <what>) by one or
+more requestors (specified by <who>).
+See the {{SECT:Access Control}} section of this guide for basic usage.
+
+!if 0
+More detailed discussion of this directive can be found in the
+{{SECT:Advanced Access Control}} chapter.
+!endif
+
+Note: If no {{EX:olcAccess}} directives are specified, the default
+access control policy, {{EX:to * by * read}}, allows all
+users (both authenticated and anonymous) read access.
+
+Note: Access controls defined in the frontend are appended to all
+other databases' controls.
+
+
+H4: olcReadonly { TRUE | FALSE }
+
+This directive puts the database into "read-only" mode. Any
+attempts to modify the database will return an "unwilling to
+perform" error. If set on a consumer, modifications sent by
+syncrepl will still occur.
+
+\Default:
+
+> olcReadonly: FALSE
+
+
+H4: olcRootDN: <DN>
+
+This directive specifies the DN that is not subject to
+access control or administrative limit restrictions for
+operations on this database. The DN need not refer to
+an entry in this database or even in the directory. The
+DN may refer to a SASL identity.
+
+Entry-based Example:
+
+> olcRootDN: cn=Manager,dc=example,dc=com
+
+SASL-based Example:
+
+> olcRootDN: uid=root,cn=example.com,cn=digest-md5,cn=auth
+
+See the {{SECT:SASL Authentication}} section for information on
+SASL authentication identities.
+
+
+H4: olcRootPW: <password>
+
+This directive can be used to specify a password for the DN for
+the rootdn (when the rootdn is set to a DN within the database).
+
+\Example:
+
+> olcRootPW: secret
+
+It is also permissible to provide a hash of the password in
+{{REF:RFC2307}} form. {{slappasswd}}(8) may be used to generate
+the password hash.
+
+\Example:
+
+> olcRootPW: {SSHA}ZKKuqbEKJfKSXhUbHG3fG8MDn9j1v4QN
+
+The hash was generated using the command {{EX:slappasswd -s secret}}.
+
+
+H4: olcSizeLimit: <integer>
+
+This directive specifies the maximum number of entries to return
+from a search operation.
+
+\Default:
+
+> olcSizeLimit: 500
+
+See the {{SECT:Limits}} section of this guide and slapd-config(5)
+for more details.
+
+
+H4: olcSuffix: <dn suffix>
+
+This directive specifies the DN suffix of queries that will be
+passed to this backend database. Multiple suffix lines can be
+given, and usually at least one is required for each database
+definition. (Some backend types, such as {{EX:frontend}} and
+{{EX:monitor}} use a hard-coded suffix which may not be overridden
+in the configuration.)
+
+\Example:
+
+> olcSuffix: dc=example,dc=com
+
+Queries with a DN ending in "dc=example,dc=com"
+will be passed to this backend.
+
+Note: When the backend to pass a query to is selected, slapd
+looks at the suffix value(s) in each database definition in the
+order in which they were configured. Thus, if one database suffix is a
+prefix of another, it must appear after it in the configuration.
+
+
+H4: olcSyncrepl
+
+> olcSyncrepl: rid=<replica ID>
+> provider=ldap[s]://<hostname>[:port]
+> [type=refreshOnly|refreshAndPersist]
+> [interval=dd:hh:mm:ss]
+> [retry=[<retry interval> <# of retries>]+]
+> searchbase=<base DN>
+> [filter=<filter str>]
+> [scope=sub|one|base]
+> [attrs=<attr list>]
+> [exattrs=<attr list>]
+> [attrsonly]
+> [sizelimit=<limit>]
+> [timelimit=<limit>]
+> [schemachecking=on|off]
+> [bindmethod=simple|sasl]
+> [binddn=<DN>]
+> [saslmech=<mech>]
+> [authcid=<identity>]
+> [authzid=<identity>]
+> [credentials=<passwd>]
+> [realm=<realm>]
+> [secprops=<properties>]
+> [starttls=yes|critical]
+> [tls_cert=<file>]
+> [tls_key=<file>]
+> [tls_cacert=<file>]
+> [tls_cacertdir=<path>]
+> [tls_reqcert=never|allow|try|demand]
+> [tls_cipher_suite=<ciphers>]
+> [tls_crlcheck=none|peer|all]
+> [logbase=<base DN>]
+> [logfilter=<filter str>]
+> [syncdata=default|accesslog|changelog]
+
+
+This directive specifies the current database as a consumer of the
+provider content by establishing the current {{slapd}}(8) as a
+replication consumer site running a syncrepl replication engine.
+The provider database is located at the provider site
+specified by the {{EX:provider}} parameter. The consumer database is
+kept up-to-date with the provider content using the LDAP Content
+Synchronization protocol. See {{REF:RFC4533}}
+for more information on the protocol.
+
+The {{EX:rid}} parameter is used for identification of the current
+{{EX:syncrepl}} directive within the replication consumer server,
+where {{EX:<replica ID>}} uniquely identifies the syncrepl specification
+described by the current {{EX:syncrepl}} directive. {{EX:<replica ID>}}
+is non-negative and is no more than three decimal digits in length.
+
+The {{EX:provider}} parameter specifies the replication provider site
+containing the provider content as an LDAP URI. The {{EX:provider}}
+parameter specifies a scheme, a host and optionally a port where the
+provider slapd instance can be found. Either a domain name or IP
+address may be used for <hostname>. Examples are
+{{EX:ldap://provider.example.com:389}} or {{EX:ldaps://192.168.1.1:636}}.
+If <port> is not given, the standard LDAP port number (389 or 636) is used.
+Note that the syncrepl uses a consumer-initiated protocol, and hence its
+specification is located on the consumer.
+
+The content of the syncrepl consumer is defined using a search
+specification as its result set. The consumer slapd will
+send search requests to the provider slapd according to the search
+specification. The search specification includes {{EX:searchbase}},
+{{EX:scope}}, {{EX:filter}}, {{EX:attrs}}, {{EX:exattrs}}, {{EX:attrsonly}},
+{{EX:sizelimit}}, and {{EX:timelimit}} parameters as in the normal
+search specification. The {{EX:searchbase}} parameter has no
+default value and must always be specified. The {{EX:scope}} defaults
+to {{EX:sub}}, the {{EX:filter}} defaults to {{EX:(objectclass=*)}},
+{{EX:attrs}} defaults to {{EX:"*,+"}} to replicate all user and operational
+attributes, and {{EX:attrsonly}} is unset by default. Both {{EX:sizelimit}}
+and {{EX:timelimit}} default to "unlimited", and only positive integers
+or "unlimited" may be specified. The {{EX:exattrs}} option may also be used
+to specify attributes that should be omitted from incoming entries.
+
+The {{TERM[expand]LDAP Sync}} protocol has two operation
+types: {{EX:refreshOnly}} and {{EX:refreshAndPersist}}.
+The operation type is specified by the {{EX:type}} parameter.
+In the {{EX:refreshOnly}} operation, the next synchronization search operation
+is periodically rescheduled at an interval time after each
+synchronization operation finishes. The interval is specified
+by the {{EX:interval}} parameter. It is set to one day by default.
+In the {{EX:refreshAndPersist}} operation, a synchronization search
+remains persistent in the provider {{slapd}} instance. Further updates to the
+provider will generate {{EX:searchResultEntry}} to the consumer slapd
+as the search responses to the persistent synchronization search.
+
+If an error occurs during replication, the consumer will attempt to reconnect
+according to the retry parameter which is a list of the <retry interval>
+and <# of retries> pairs. For example, retry="60 10 300 3" lets the consumer
+retry every 60 seconds for the first 10 times and then retry every 300 seconds
+for the next three times before stop retrying. + in <# of retries> means
+indefinite number of retries until success.
+
+The schema checking can be enforced at the LDAP Sync consumer site
+by turning on the {{EX:schemachecking}} parameter.
+If it is turned on, every replicated entry will be checked for its
+schema as the entry is stored on the consumer.
+Every entry in the consumer should contain those attributes
+required by the schema definition.
+If it is turned off, entries will be stored without checking
+schema conformance. The default is off.
+
+The {{EX:binddn}} parameter gives the DN to bind as for the
+syncrepl searches to the provider slapd. It should be a DN
+which has read access to the replication content in the
+provider database.
+
+The {{EX:bindmethod}} is {{EX:simple}} or {{EX:sasl}},
+depending on whether simple password-based authentication or
+{{TERM:SASL}} authentication is to be used when connecting
+to the provider {{slapd}} instance.
+
+Simple authentication should not be used unless adequate data
+integrity and confidentiality protections are in place (e.g. TLS
+or IPsec). Simple authentication requires specification of {{EX:binddn}}
+and {{EX:credentials}} parameters.
+
+SASL authentication is generally recommended. SASL authentication
+requires specification of a mechanism using the {{EX:saslmech}} parameter.
+Depending on the mechanism, an authentication identity and/or
+credentials can be specified using {{EX:authcid}} and {{EX:credentials}},
+respectively. The {{EX:authzid}} parameter may be used to specify
+an authorization identity.
+
+The {{EX:realm}} parameter specifies a realm which a certain
+mechanisms authenticate the identity within. The {{EX:secprops}}
+parameter specifies Cyrus SASL security properties.
+
+The {{EX:starttls}} parameter specifies use of the StartTLS extended
+operation to establish a TLS session before authenticating to the provider.
+If the {{EX:critical}} argument is supplied, the session will be aborted
+if the StartTLS request fails. Otherwise the syncrepl session continues
+without TLS. The tls_reqcert setting defaults to {{EX:"demand"}} and the
+other TLS settings default to the same as the main slapd TLS settings.
+
+Rather than replicating whole entries, the consumer can query logs
+of data modifications. This mode of operation is referred to as
+{{delta syncrepl}}. In addition to the above parameters, the
+{{EX:logbase}} and {{EX:logfilter}} parameters must be set appropriately
+for the log that will be used. The {{EX:syncdata}} parameter must
+be set to either {{EX:"accesslog"}} if the log conforms to the
+{{slapo-accesslog}}(5) log format, or {{EX:"changelog"}} if the log
+conforms to the obsolete {{changelog}} format. If the {{EX:syncdata}}
+parameter is omitted or set to {{EX:"default"}} then the log
+parameters are ignored.
+
+The {{syncrepl}} replication mechanism is supported by the {{mdb}}
+backend.
+
+See the {{SECT:LDAP Sync Replication}} chapter of this guide for
+more information on how to use this directive.
+
+
+H4: olcTimeLimit: <integer>
+
+This directive specifies the maximum number of seconds (in real
+time) slapd will spend answering a search request. If a
+request is not finished in this time, a result indicating an
+exceeded timelimit will be returned.
+
+\Default:
+
+> olcTimeLimit: 3600
+
+See the {{SECT:Limits}} section of this guide and slapd-config(5)
+for more details.
+
+
+H4: olcUpdateref: <URL>
+
+This directive is only applicable in a {{replica}} (or {{shadow}})
+{{slapd}}(8) instance. It
+specifies the URL to return to clients which submit update
+requests upon the replica.
+If specified multiple times, each {{TERM:URL}} is provided.
+
+\Example:
+
+> olcUpdateref: ldap://provider.example.net
+
+
+H4: Sample Entries
+
+>dn: olcDatabase=frontend,cn=config
+>objectClass: olcDatabaseConfig
+>objectClass: olcFrontendConfig
+>olcDatabase: frontend
+>olcReadOnly: FALSE
+>
+>dn: olcDatabase=config,cn=config
+>objectClass: olcDatabaseConfig
+>olcDatabase: config
+>olcRootDN: cn=Manager,dc=example,dc=com
+
+
+H3: MDB Backend Directives
+
+Directives in this category only apply to the {{TERM:MDB}}
+database backend. They will apply to all "database mdb"
+instances in the configuration. For a complete reference
+of MDB backend configuration directives, see {{slapd-mdb}}(5).
+
+H4: olcBkMdbIdlExp <exponent>
+
+Specify a power of 2 for the maximum size of an index slot.
+The default is 16, yielding a maximum slot size of 2^16 or 65536.
+The specified value must be in the range of 16-30.
+
+This setting helps with the case where certain search filters are
+slow to return results due to an index slot having collapsed to a
+range value. This occurs when the number of candidate entries that
+match the filter for the index slot exceed the configured slot size.
+
+If this setting is decreased on a server with existing {{TERM:MDB}}
+databases, each db will immediately need its indices to be rebuilt
+while slapd is offline with the "slapindex -q -t" command.
+
+If this setting is increased on a server with existing {{TERM:MDB}}
+databases, each db will need its indices rebuilt to take advantage
+of the change for indices that have already been converted to ranges.
+
+
+H3: MDB Database Directives
+
+Directives in this category apply to the {{TERM:MDB}}
+database backend.
+They are used in an olcDatabase entry in addition to the generic
+database directives defined above. For a complete reference
+of MDB configuration directives, see {{slapd-mdb}}(5). In
+addition to the {{EX:olcDatabaseConfig}} objectClass, MDB
+database entries must have the {{EX:olcMdbConfig}} objectClass.
+
+
+H4: olcDbDirectory: <directory>
+
+This directive specifies the directory where the MDB files
+containing the database and associated indices live.
+
+\Default:
+
+> olcDbDirectory: /usr/local/var/openldap-data
+
+
+H4: olcDbCheckpoint: <kbyte> <min>
+
+This directive specifies the frequency for flushing the database disk
+buffers. This directive is only needed if the {{olcDbNoSync}} option is
+{{EX:TRUE}}.
+The checkpoint will occur if either <kbyte> data has been written or
+<min> minutes have passed since the last checkpoint. Both arguments default
+to zero, in which case they are ignored. When the <min> argument is
+non-zero, an internal task will run every <min> minutes to perform the
+checkpoint. Note: currently the _kbyte_ setting is unimplemented.
+
+\Example:
+
+> olcDbCheckpoint: 1024 10
+
+
+H4: olcDbEnvFlags: {nosync,nometasync,writemap,mapasync,nordahead}
+
+This option specifies flags for finer-grained control of the LMDB library's
+operation.
+
+* {{F:nosync}}: This is exactly the same as the dbnosync directive.
+
+* {{F:nometasync}}: Flush the data on a commit, but skip the sync of the meta
+page. This mode is slightly faster than doing a full sync, but can
+potentially lose the last committed transaction if the operating system
+crashes. If both nometasync and nosync are set, the nosync flag takes
+precedence.
+
+* {{F:writemap}}: Use a writable memory map instead of just read-only. This
+speeds up write operations but makes the database vulnerable to corruption in
+case any bugs in slapd cause stray writes into the mmap region.
+
+* {{F:mapasync}}: When using a writable memory map and performing flushes on
+each commit, use an asynchronous flush instead of a synchronous flush (the
+default). This option has no effect if writemap has not been set. It also has
+no effect if nosync is set.
+
+* {{F:nordahead}}: Turn off file readahead. Usually the OS performs readahead
+on every read request. This usually boosts read performance but can be
+harmful to random access read performance if the system's memory is full and
+the DB is larger than RAM. This option is not implemented on Windows.
+
+
+H4: olcDbIndex: {<attrlist> | default} [pres,eq,approx,sub,none]
+
+This directive specifies the indices to maintain for the given
+attribute. If only an {{EX:<attrlist>}} is given, the default
+indices are maintained. The index keywords correspond to the
+common types of matches that may be used in an LDAP search filter.
+
+\Example:
+
+> olcDbIndex: default pres,eq
+> olcDbIndex: uid
+> olcDbIndex: cn,sn pres,eq,sub
+> olcDbIndex: objectClass eq
+
+The first line sets the default set of indices to maintain to
+present and equality. The second line causes the default (pres,eq)
+set of indices to be maintained for the {{EX:uid}} attribute type.
+The third line causes present, equality, and substring indices to
+be maintained for {{EX:cn}} and {{EX:sn}} attribute types. The
+fourth line causes an equality index for the {{EX:objectClass}}
+attribute type.
+
+There is no index keyword for inequality matches. Generally these
+matches do not use an index. However, some attributes do support
+indexing for inequality matches, based on the equality index.
+
+A substring index can be more explicitly specified as {{EX:subinitial}},
+{{EX:subany}}, or {{EX:subfinal}}, corresponding to the three
+possible components
+of a substring match filter. A subinitial index only indexes
+substrings that appear at the beginning of an attribute value.
+A subfinal index only indexes substrings that appear at the end
+of an attribute value, while subany indexes substrings that occur
+anywhere in a value.
+
+Note that by default, setting an index for an attribute also
+affects every subtype of that attribute. E.g., setting an equality
+index on the {{EX:name}} attribute causes {{EX:cn}}, {{EX:sn}}, and every other
+attribute that inherits from {{EX:name}} to be indexed.
+
+By default, no indices are maintained. It is generally advised
+that minimally an equality index upon objectClass be maintained.
+
+> olcDbIndex: objectClass eq
+
+Additional indices should be configured corresponding to the
+most common searches that are used on the database.
+Presence indexing should not be configured for an attribute
+unless the attribute occurs very rarely in the database, and
+presence searches on the attribute occur very frequently during
+normal use of the directory. Most applications don't use presence
+searches, so usually presence indexing is not very useful.
+
+If this setting is changed while slapd is running, an internal task
+will be run to generate the changed index data. All server operations
+can continue as normal while the indexer does its work. If slapd is
+stopped before the index task completes, indexing will have to be
+manually completed using the slapindex tool.
+
+
+H4: olcDbMaxEntrySize: <bytes>
+
+Specify the maximum size of an entry in bytes. Attempts to store
+an entry larger than this size will be rejected with the error
+LDAP_ADMINLIMIT_EXCEEDED. The default is 0, which is unlimited.
+
+
+H4: olcDbMaxReaders: <integer>
+
+This directive specifies the maximum number of threads that may have
+concurrent read access to the database. Tools such as slapcat count as a
+single thread, in addition to threads in any active slapd processes. The
+default is 126.
+
+
+H4: olcDbMaxSize: <bytes>
+
+This directive specifies the maximum size of the database in bytes. A memory
+map of this size is allocated at startup time and the database will not be
+allowed to grow beyond this size. The default is 10485760 bytes (10MB). This
+setting may be changed upward if the configured limit needs to be increased.
+
+Note: It is important to set this to as large a value as possible, (relative
+to anticipated growth of the actual data over time) since growing the size
+later may not be practical when the system is under heavy load.
+
+
+H4: olcDbMode: { <octal> | <symbolic> }
+
+This directive specifies the file protection mode that newly
+created database index files should have. This can be in the form
+{{EX:0600}} or {{EX:-rw-------}}
+
+\Default:
+
+> olcDbMode: 0600
+
+
+H4: olcDbMultival: { <attrlist> | default } <integer> hi,<integer> lo
+
+Specify the number of values for which a multivalued attribute is
+stored in a separate table. Normally entries are stored as a single
+blob inside the database. When an entry gets very large or contains
+attributes with a very large number of values, modifications on that
+entry may get very slow. Splitting the large attributes out to a separate
+table can improve the performance of modification operations.
+The threshold is specified as a pair of integers. If the number of
+values exceeds the hi threshold the values will be split out. If
+a modification deletes enough values to bring an attribute below
+the lo threshold the values will be removed from the separate
+table and merged back into the main entry blob.
+The threshold can be set for a specific list of attributes, or
+the default can be configured for all other attributes.
+The default value for both hi and lo thresholds is UINT_MAX, which keeps
+all attributes in the main blob.
+
+In addition to increasing write performance of operations the use of
+multival can also decrease fragmentation of the primary {{TERM:MDB}} database.
+
+
+H4: olcDbRtxnsize: <entries>
+
+This directive specifies the maximum number of entries to process in a single
+read transaction when executing a large search. Long-lived read transactions
+prevent old database pages from being reused in write transactions, and so
+can cause significant growth of the database file when there is heavy write
+traffic. This setting causes the read transaction in large searches to be
+released and reacquired after the given number of entries has been read, to
+give writers the opportunity to reclaim old database pages. The default is
+10000.
+
+
+H4: olcDbSearchStack: <integer>
+
+Specify the depth of the stack used for search filter evaluation.
+Search filters are evaluated on a stack to accommodate nested {{EX:AND}} /
+{{EX:OR}} clauses. An individual stack is allocated for each server thread.
+The depth of the stack determines how complex a filter can be evaluated
+without requiring any additional memory allocation. Filters that are
+nested deeper than the search stack depth will cause a separate stack to
+be allocated for that particular search operation. These separate allocations
+can have a major negative impact on server performance, but specifying
+too much stack will also consume a great deal of memory. Each search
+uses 512K bytes per level on a 32-bit machine, or 1024K bytes per level
+on a 64-bit machine. The default stack depth is 16, thus 8MB or 16MB
+per thread is used on 32 and 64 bit machines, respectively. Also the
+512KB size of a single stack slot is set by a compile-time constant which
+may be changed if needed; the code must be recompiled for the change
+to take effect.
+
+\Default:
+
+> olcDbSearchStack: 16
+
+
+H4: olcDbNosync: { TRUE | FALSE }
+
+This directive causes on-disk database contents to not be immediately
+synchronized with in memory changes upon change. Setting this option
+to {{EX:TRUE}} may improve performance at the expense of data integrity.
+
+
+H4: Sample Entry
+
+>dn: olcDatabase=mdb,cn=config
+>objectClass: olcDatabaseConfig
+>objectClass: olcMdbConfig
+>olcDatabase: mdb
+>olcSuffix: dc=example,dc=com
+>olcDbDirectory: /usr/local/var/openldap-data
+>olcDbIndex: objectClass eq
+
+
+H2: Configuration Example
+
+The following is an example configuration, interspersed
+with explanatory text. It defines two databases to handle
+different parts of the {{TERM:X.500}} tree; both are {{TERM:MDB}}
+database instances. The line numbers shown are provided for
+reference only and are not included in the actual file. First, the
+global configuration section:
+
+E: 1. # example config file - global configuration entry
+E: 2. dn: cn=config
+E: 3. objectClass: olcGlobal
+E: 4. cn: config
+E: 5. olcReferral: ldap://root.openldap.org
+E: 6.
+
+Line 1 is a comment. Lines 2-4 identify this as the global
+configuration entry.
+The {{EX:olcReferral:}} directive on line 5
+means that queries not local to one of the databases defined
+below will be referred to the LDAP server running on the
+standard port (389) at the host {{EX:root.openldap.org}}.
+Line 6 is a blank line, indicating the end of this entry.
+
+E: 7. # internal schema
+E: 8. dn: cn=schema,cn=config
+E: 9. objectClass: olcSchemaConfig
+E: 10. cn: schema
+E: 11.
+
+Line 7 is a comment. Lines 8-10 identify this as the root of
+the schema subtree. The actual schema definitions in this entry
+are hardcoded into slapd so no additional attributes are specified here.
+Line 11 is a blank line, indicating the end of this entry.
+
+E: 12. # include the core schema
+E: 13. include: file:///usr/local/etc/openldap/schema/core.ldif
+E: 14.
+
+Line 12 is a comment. Line 13 is an LDIF include directive which
+accesses the {{core}} schema definitions in LDIF format. Line 14
+is a blank line.
+
+Next comes the database definitions. The first database is the
+special {{EX:frontend}} database whose settings are applied globally
+to all the other databases.
+
+E: 15. # global database parameters
+E: 16. dn: olcDatabase=frontend,cn=config
+E: 17. objectClass: olcDatabaseConfig
+E: 18. olcDatabase: frontend
+E: 19. olcAccess: to * by * read
+E: 20.
+
+Line 15 is a comment. Lines 16-18 identify this entry as the global
+database entry. Line 19 is a global access control. It applies to all
+entries (after any applicable database-specific access controls).
+Line 20 is a blank line.
+
+The next entry defines the config backend.
+
+E: 21. # set a rootpw for the config database so we can bind.
+E: 22. # deny access to everyone else.
+E: 23. dn: olcDatabase=config,cn=config
+E: 24. objectClass: olcDatabaseConfig
+E: 25. olcDatabase: config
+E: 26. olcRootPW: {SSHA}XKYnrjvGT3wZFQrDD5040US592LxsdLy
+E: 27. olcAccess: to * by * none
+E: 28.
+
+Lines 21-22 are comments. Lines 23-25 identify this entry as the config
+database entry. Line 26 defines the {{super-user}} password for this
+database. (The DN defaults to {{"cn=config"}}.) Line 27 denies all access
+to this database, so only the super-user will be able to access it. (This
+is already the default access on the config database. It is just listed
+here for illustration, and to reiterate that unless a means to authenticate
+as the super-user is explicitly configured, the config database will be
+inaccessible.)
+
+Line 28 is a blank line.
+
+The next entry defines an MDB backend that will handle queries for things
+in the "dc=example,dc=com" portion of the tree. Indices are to be maintained
+for several attributes, and the {{EX:userPassword}} attribute is to be
+protected from unauthorized access.
+
+E: 29. # MDB definition for example.com
+E: 30. dn: olcDatabase=mdb,cn=config
+E: 31. objectClass: olcDatabaseConfig
+E: 32. objectClass: olcMdbConfig
+E: 33. olcDatabase: mdb
+E: 34. olcSuffix: dc=example,dc=com
+E: 35. olcDbDirectory: /usr/local/var/openldap-data
+E: 36. olcRootDN: cn=Manager,dc=example,dc=com
+E: 37. olcRootPW: secret
+E: 38. olcDbIndex: uid pres,eq
+E: 39. olcDbIndex: cn,sn pres,eq,approx,sub
+E: 40. olcDbIndex: objectClass eq
+E: 41. olcAccess: to attrs=userPassword
+E: 42. by self write
+E: 43. by anonymous auth
+E: 44. by dn.base="cn=Admin,dc=example,dc=com" write
+E: 45. by * none
+E: 46. olcAccess: to *
+E: 47. by self write
+E: 48. by dn.base="cn=Admin,dc=example,dc=com" write
+E: 49. by * read
+E: 50.
+
+Line 29 is a comment. Lines 30-33 identify this entry as a MDB database
+configuration entry. Line 34 specifies the DN suffix
+for queries to pass to this database. Line 35 specifies the directory
+in which the database files will live.
+
+Lines 36 and 37 identify the database {{super-user}} entry and associated
+password. This entry is not subject to access control or size or
+time limit restrictions.
+
+Lines 38 through 40 indicate the indices to maintain for various
+attributes.
+
+Lines 41 through 49 specify access control for entries in this
+database. For all applicable entries, the {{EX:userPassword}} attribute is writable
+by the entry itself and by the "admin" entry. It may be used for
+authentication/authorization purposes, but is otherwise not readable.
+All other attributes are writable by the entry and the "admin"
+entry, but may be read by all users (authenticated or not).
+
+Line 50 is a blank line, indicating the end of this entry.
+
+The next entry defines another
+MDB database. This one handles queries involving the
+{{EX:dc=example,dc=net}} subtree but is managed by the same entity
+as the first database. Note that without line 60, the read access
+would be allowed due to the global access rule at line 19.
+
+E: 51. # MDB definition for example.net
+E: 52. dn: olcDatabase=mdb,cn=config
+E: 53. objectClass: olcDatabaseConfig
+E: 54. objectClass: olcMdbConfig
+E: 55. olcDatabase: mdb
+E: 56. olcSuffix: dc=example,dc=net
+E: 57. olcDbDirectory: /usr/local/var/openldap-data-net
+E: 58. olcRootDN: cn=Manager,dc=example,dc=com
+E: 59. olcDbIndex: objectClass eq
+E: 60. olcAccess: to * by users read
+
+
+H2: Converting old style {{slapd.conf}}(5) file to {{cn=config}} format
+
+Before converting to the {{cn=config}} format you should make sure that the
+config backend is properly configured in your existing config file. While
+the config backend is always present inside slapd, by default it is only
+accessible by its rootDN, and there are no default credentials assigned
+so unless you explicitly configure a means to authenticate to it, it will be
+unusable.
+
+If you do not already have a {{EX:database config}} section, add something
+like this to the end of {{EX:slapd.conf}}
+
+> database config
+> rootpw VerySecret
+
+Note: Since the config backend can be used to load arbitrary code into the
+slapd process, it is extremely important to carefully guard whatever
+credentials are used to access it. Since simple passwords are vulnerable to
+password guessing attacks, it is usually better to omit the rootpw and only
+use SASL authentication for the config rootDN.
+
+An existing {{slapd.conf}}(5) file can be converted to the new format using
+{{slaptest}}(8) or any of the slap tools:
+
+> slaptest -f /usr/local/etc/openldap/slapd.conf -F /usr/local/etc/openldap/slapd.d
+
+Test that you can access entries under {{EX:cn=config}} using the
+default {{rootdn}} and the {{rootpw}} configured above:
+
+> ldapsearch -x -D cn=config -w VerySecret -b cn=config
+
+You can then discard the old {{slapd.conf}}(5) file. Make sure to launch
+{{slapd}}(8) with the {{-F}} option to specify the configuration directory
+if you are not using the default directory path.
+
+Note: When converting from the slapd.conf format to slapd.d format, any
+included files will also be integrated into the resulting configuration
+database.
+
+
+H2: Recovering from a broken configuration
+
+If the server using {{cn=config}} does not start, either because the
+configuration does not represent the current version or because it has been
+corrupted, these actions are available, in the order of decreasing preference.
+
+Make sure you have made a backup of the "broken" version before you attempt any
+of these:
+
+
+H3: Generate an ldif version of the configuration database and reload from that
+
+Most of the time, the configuration can be parsed and a text version generated
+with {{slapcat}}(8):
+
+> slapcat -F /usr/local/etc/openldap/slapd.d -n0 -l extracted_config.ldif
+
+After you have backed up and removed the old configuration database contents,
+this output ldif can be hand-edited to adjust or remove the offending entries
+and imported again:
+
+> slapadd -F /usr/local/etc/openldap/slapd.d -l updated_config.ldif
+> slaptest -F /usr/local/etc/openldap/slapd.d
+
+
+H3: Modify config in-place
+
+If the configuration can be parsed and you know exactly what you need to do,
+you can use {{slapmodify}}(8) to effect the required changes directly:
+
+> slapmodify -F /usr/local/etc/openldap/slapd.d
+> dn: ..., cn=config
+> changetype: ...
+> ...
+
+
+H3: Recover with plain back-ldif
+
+If the configuration contains items that {{slapd}}(8) cannot process as a
+{{cn=config}} database at all, the last resort is to disable schema checking
+and operate on it as a regular back-ldif database. This might cease to work
+with future versions of OpenLDAP without notice, attempt this only when all of
+the above fail.
+
+First, create a directory to serve as the hosting DB and create the structure:
+
+> mkdir ./recovery ./recovery/cn=recovery
+> cp /usr/local/etc/openldap/slapd.d/cn=config.ldif ./recovery/cn=recovery
+> cp -r /usr/local/etc/openldap/slapd.d/cn=config ./recovery/cn=recovery
+
+Or, if you have already backed up your old configuration, you can symlink it
+into place:
+
+> mkdir ./recovery
+> ln -s /usr/local/etc/openldap/slapd.d ./recovery/cn=recovery
+
+Next, create a trivial {{slapd.conf}}(5) to access the new database:
+
+> database ldif
+> suffix cn=recovery
+> directory ./recovery/
+
+Note the change of suffix, {{EX:cn=config}} is hardcoded to correspond to an
+active config database, so we have to home it one level deeper - at
+{{EX:cn=config,cn=recovery}}.
+
+Now you can use {{slapmodify}}(8) to modify the database, it is most likely you
+will need to run with schema checking disabled:
+
+> slapmodify -f ./recovery.conf -s
+
+You can test the validity of your config with {{slaptest}}(8):
+
+> slaptest -F ./recovery/cn=recovery
+
+And generate a full ldif with {{slapcat}}(8):
+
+> slapcat -F ./recovery/cn=recovery -n0
diff --git a/doc/guide/admin/slapdconfig.sdf b/doc/guide/admin/slapdconfig.sdf
new file mode 100644
index 0000000..e9fc5d3
--- /dev/null
+++ b/doc/guide/admin/slapdconfig.sdf
@@ -0,0 +1,923 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: The slapd Configuration File
+
+This chapter describes configuring {{slapd}}(8) via the {{slapd.conf}}(5)
+configuration file. {{slapd.conf}}(5) has been deprecated and should
+only be used if your site requires one of the backends that hasn't yet
+been updated to work with the newer {{slapd-config}}(5) system. Configuring
+{{slapd}}(8) via {{slapd-config}}(5) is described in the previous chapter.
+
+The {{slapd.conf}}(5) file is normally installed in the
+{{EX:/usr/local/etc/openldap}} directory. An alternate configuration
+file location can be specified via a command-line option to {{slapd}}(8).
+
+
+H2: Configuration File Format
+
+The {{slapd.conf}}(5) file consists of three types of configuration
+information: global, backend specific, and database specific. Global
+information is specified first, followed by information associated
+with a particular backend type, which is then followed by information
+associated with a particular database instance. Global directives can
+be overridden in backend and/or database directives, and backend directives
+can be overridden by database directives.
+
+Blank lines and comment lines beginning with a '{{EX:#}}' character
+are ignored. If a line begins with whitespace, it is considered a
+continuation of the previous line (even if the previous line is a
+comment).
+
+The general format of slapd.conf is as follows:
+
+> # global configuration directives
+> <global config directives>
+>
+> # backend definition
+> backend <typeA>
+> <backend-specific directives>
+>
+> # first database definition & config directives
+> database <typeA>
+> <database-specific directives>
+>
+> # second database definition & config directives
+> database <typeB>
+> <database-specific directives>
+>
+> # second database definition & config directives
+> database <typeA>
+> <database-specific directives>
+>
+> # subsequent backend & database definitions & config directives
+> ...
+
+A configuration directive may take arguments. If so, they are
+separated by whitespace. If an argument contains whitespace,
+the argument should be enclosed in double quotes {{EX:"like this"}}. If
+an argument contains a double quote or a backslash character `{{EX:\}}',
+the character should be preceded by a backslash character `{{EX:\}}'.
+
+The distribution contains an example configuration file that will
+be installed in the {{F: /usr/local/etc/openldap}} directory.
+A number of files containing schema definitions (attribute types
+and object classes) are also provided in the
+{{F: /usr/local/etc/openldap/schema}} directory.
+
+
+H2: Configuration File Directives
+
+This section details commonly used configuration directives. For
+a complete list, see the {{slapd.conf}}(5) manual page. This section
+separates the configuration file directives into global,
+backend-specific and data-specific categories, describing each
+directive and its default value (if any), and giving an example of
+its use.
+
+
+
+H3: Global Directives
+
+Directives described in this section apply to all backends
+and databases unless specifically overridden in a backend or
+database definition. Arguments that should be replaced
+by actual text are shown in brackets {{EX:<>}}.
+
+
+H4: access to <what> [ by <who> [<accesslevel>] [<control>] ]+
+
+This directive grants access (specified by <accesslevel>) to a set
+of entries and/or attributes (specified by <what>) by one or more
+requestors (specified by <who>). See the {{SECT:Access Control}} section of
+this guide for basic usage.
+
+!if 0
+More details discussion of this directive can be found in the
+{{SECT:Advanced Access Control}} chapter.
+!endif
+
+Note: If no {{EX:access}} directives are specified, the default
+access control policy, {{EX:access to * by * read}}, allows all
+both authenticated and anonymous users read access.
+
+
+H4: attributetype <{{REF:RFC4512}} Attribute Type Description>
+
+This directive defines an attribute type.
+Please see the {{SECT:Schema Specification}} chapter
+for information regarding how to use this directive.
+
+H4: idletimeout <integer>
+
+Specify the number of seconds to wait before forcibly closing
+an idle client connection. An idletimeout of 0, the default,
+disables this feature.
+
+
+H4: include <filename>
+
+This directive specifies that slapd should read additional
+configuration information from the given file before continuing
+with the next line of the current file. The included file should
+follow the normal slapd config file format. The file is commonly
+used to include files containing schema specifications.
+
+Note: You should be careful when using this directive - there is
+no small limit on the number of nested include directives, and no
+loop detection is done.
+
+H4: loglevel <level>
+
+This directive specifies the level at which log statements
+and operation statistics should be sent to syslog (currently logged to
+the {{syslogd}}(8) {{EX:LOG_LOCAL4}} facility). You must have
+configured OpenLDAP {{EX:--enable-debug}} (the default) for this
+to work, except for the two statistics levels, which are always
+enabled. Log levels may be specified as integers or by keyword.
+Multiple log levels may be used and the levels are additive.
+The possible values for <integer> are:
+
+!block table; colaligns="RL"; align=Center; \
+ title="Table 6.1: Logging Levels"
+Level Keyword Description
+-1 any enable all debugging
+0 no debugging
+1 (0x1 trace) trace function calls
+2 (0x2 packets) debug packet handling
+4 (0x4 args) heavy trace debugging
+8 (0x8 conns) connection management
+16 (0x10 BER) print out packets sent and received
+32 (0x20 filter) search filter processing
+64 (0x40 config) configuration processing
+128 (0x80 ACL) access control list processing
+256 (0x100 stats) stats log connections/operations/results
+512 (0x200 stats2) stats log entries sent
+1024 (0x400 shell) print communication with shell backends
+2048 (0x800 parse) print entry parsing debugging
+16384 (0x4000 sync) syncrepl consumer processing
+32768 (0x8000 none) only messages that get logged regardless of configured log level
+!endblock
+
+The desired log level can be input as a single integer that
+combines the (ORed) desired levels, both in decimal or in hexadecimal
+notation, as a list of integers (that are ORed internally), or as a list of the names that are shown between brackets, such that
+
+> loglevel 129
+> loglevel 0x81
+> loglevel 128 1
+> loglevel 0x80 0x1
+> loglevel acl trace
+
+are equivalent.
+
+\Examples:
+
+E: loglevel -1
+
+This will enable all log levels.
+
+E: loglevel conns filter
+
+Just log the connection and search filter processing.
+
+E: loglevel none
+
+Log those messages that are logged regardless of the configured loglevel. This
+differs from setting the log level to 0, when no logging occurs. At least the
+{{EX:None}} level is required to have high priority messages logged.
+
+\Default:
+
+E: loglevel stats
+
+Basic stats logging is configured by default.
+
+H4: objectclass <{{REF:RFC4512}} Object Class Description>
+
+This directive defines an object class.
+Please see the {{SECT:Schema Specification}} chapter for
+information regarding how to use this directive.
+
+
+H4: referral <URI>
+
+This directive specifies the referral to pass back when slapd
+cannot find a local database to handle a request.
+
+\Example:
+
+> referral ldap://root.openldap.org
+
+This will refer non-local queries to the global root LDAP server
+at the OpenLDAP Project. Smart LDAP clients can re-ask their
+query at that server, but note that most of these clients are
+only going to know how to handle simple LDAP URLs that
+contain a host part and optionally a distinguished name part.
+
+
+H4: sizelimit <integer>
+
+This directive specifies the maximum number of entries to return
+from a search operation.
+
+\Default:
+
+> sizelimit 500
+
+See the {{SECT:Limits}} section of this guide and {{slapd.conf}}(5)
+for more details.
+
+H4: timelimit <integer>
+
+This directive specifies the maximum number of seconds (in real
+time) slapd will spend answering a search request. If a
+request is not finished in this time, a result indicating an
+exceeded timelimit will be returned.
+
+\Default:
+
+> timelimit 3600
+
+See the {{SECT:Limits}} section of this guide and {{slapd.conf}}(5)
+for more details.
+
+
+H3: General Backend Directives
+
+Directives in this section apply only to the backend in which
+they are defined. They are supported by every type of backend.
+Backend directives apply to all databases instances of the
+same type and, depending on the directive, may be overridden
+by database directives.
+
+H4: backend <type>
+
+This directive marks the beginning of a backend declaration.
+{{EX:<type>}} should be one of the
+supported backend types listed in Table 6.2.
+
+!block table; align=Center; coltags="EX,N"; \
+ title="Table 6.2: Database Backends"
+Types Description
+asyncmeta Asynchronous Metadirectory backend
+config Slapd configuration backend
+dnssrv DNS SRV backend
+ldap Lightweight Directory Access Protocol (Proxy) backend
+ldif Lightweight Data Interchange Format backend
+mdb Memory-Mapped DB backend
+meta Metadirectory backend
+monitor Monitor backend
+ndb MySQL NDB backend
+null Null backend
+passwd Provides read-only access to {{passwd}}(5)
+perl Perl Programmable backend
+relay Relay backend
+sock Socket backend
+sql SQL Programmable backend
+wt WiredTiger backend
+!endblock
+
+\Example:
+
+> backend mdb
+> idlexp 16
+
+This marks the beginning of a new {{TERM:MDB}} backend
+definition. At present, only back-mdb implements any options
+of this type, so this setting is not needed for any other backends.
+
+
+H3: General Database Directives
+
+Directives in this section apply only to the database in which
+they are defined. They are supported by every type of database.
+
+H4: database <type>
+
+This directive marks the beginning of a database instance
+declaration.
+{{EX:<type>}} should be one of the
+supported backend types listed in Table 6.2.
+
+\Example:
+
+> database mdb
+
+This marks the beginning of a new {{TERM:MDB}} database instance
+declaration.
+
+
+H4: limits <selector> <limit> [<limit> [...]]
+
+Specify time and size limits based on the operation's initiator or base
+DN.
+
+See the {{SECT:Limits}} section of this guide and {{slapd.conf}}(5)
+for more details.
+
+
+H4: readonly { on | off }
+
+This directive puts the database into "read-only" mode. Any
+attempts to modify the database will return an "unwilling to
+perform" error. If set on a consumer, modifications sent by
+syncrepl will still occur.
+
+\Default:
+
+> readonly off
+
+
+H4: rootdn <DN>
+
+This directive specifies the DN that is not subject to
+access control or administrative limit restrictions for
+operations on this database. The DN need not refer to
+an entry in this database or even in the directory. The
+DN may refer to a SASL identity.
+
+Entry-based Example:
+
+> rootdn "cn=Manager,dc=example,dc=com"
+
+SASL-based Example:
+
+> rootdn "uid=root,cn=example.com,cn=digest-md5,cn=auth"
+
+See the {{SECT:SASL Authentication}} section for information on
+SASL authentication identities.
+
+
+H4: rootpw <password>
+
+This directive can be used to specifies a password for the DN for
+the rootdn (when the rootdn is set to a DN within the database).
+
+\Example:
+
+> rootpw secret
+
+It is also permissible to provide hash of the password in {{REF:RFC2307}}
+form. {{slappasswd}}(8) may be used to generate the password hash.
+
+\Example:
+
+> rootpw {SSHA}ZKKuqbEKJfKSXhUbHG3fG8MDn9j1v4QN
+
+The hash was generated using the command {{EX:slappasswd -s secret}}.
+
+
+H4: suffix <dn suffix>
+
+This directive specifies the DN suffix of queries that will be
+passed to this backend database. Multiple suffix lines can be
+given, and at least one is required for each database
+definition.
+
+\Example:
+
+> suffix "dc=example,dc=com"
+
+Queries with a DN ending in "dc=example,dc=com"
+will be passed to this backend.
+
+Note: When the backend to pass a query to is selected, slapd
+looks at the suffix line(s) in each database definition in the
+order they appear in the file. Thus, if one database suffix is a
+prefix of another, it must appear after it in the config file.
+
+
+H4: syncrepl
+
+> syncrepl rid=<replica ID>
+> provider=ldap[s]://<hostname>[:port]
+> searchbase=<base DN>
+> [type=refreshOnly|refreshAndPersist]
+> [interval=dd:hh:mm:ss]
+> [retry=[<retry interval> <# of retries>]+]
+> [filter=<filter str>]
+> [scope=sub|one|base]
+> [attrs=<attr list>]
+> [exattrs=<attr list>]
+> [attrsonly]
+> [sizelimit=<limit>]
+> [timelimit=<limit>]
+> [schemachecking=on|off]
+> [network-timeout=<seconds>]
+> [timeout=<seconds>]
+> [bindmethod=simple|sasl]
+> [binddn=<DN>]
+> [saslmech=<mech>]
+> [authcid=<identity>]
+> [authzid=<identity>]
+> [credentials=<passwd>]
+> [realm=<realm>]
+> [secprops=<properties>]
+> [keepalive=<idle>:<probes>:<interval>]
+> [starttls=yes|critical]
+> [tls_cert=<file>]
+> [tls_key=<file>]
+> [tls_cacert=<file>]
+> [tls_cacertdir=<path>]
+> [tls_reqcert=never|allow|try|demand]
+> [tls_cipher_suite=<ciphers>]
+> [tls_crlcheck=none|peer|all]
+> [tls_protocol_min=<major>[.<minor>]]
+> [suffixmassage=<real DN>]
+> [logbase=<base DN>]
+> [logfilter=<filter str>]
+> [syncdata=default|accesslog|changelog]
+
+
+This directive specifies the current database as a consumer of the
+provider content by establishing the current {{slapd}}(8) as a
+replication consumer site running a syncrepl replication engine.
+The provider database is located at the replication provider site
+specified by the {{EX:provider}} parameter. The consumer database is
+kept up-to-date with the provider content using the LDAP Content
+Synchronization protocol. See {{REF:RFC4533}}
+for more information on the protocol.
+
+The {{EX:rid}} parameter is used for identification of the current
+{{EX:syncrepl}} directive within the replication consumer server,
+where {{EX:<replica ID>}} uniquely identifies the syncrepl specification
+described by the current {{EX:syncrepl}} directive. {{EX:<replica ID>}}
+is non-negative and is no more than three decimal digits in length.
+
+The {{EX:provider}} parameter specifies the replication provider site
+containing the provider content as an LDAP URI. The {{EX:provider}}
+parameter specifies a scheme, a host and optionally a port where the
+provider slapd instance can be found. Either a domain name or IP
+address may be used for <hostname>. Examples are
+{{EX:ldap://provider.example.com:389}} or {{EX:ldaps://192.168.1.1:636}}.
+If <port> is not given, the standard LDAP port number (389 or 636) is used.
+Note that the syncrepl uses a consumer-initiated protocol, and hence its
+specification is located on the consumer.
+
+The content of the syncrepl consumer is defined using a search
+specification as its result set. The consumer slapd will
+send search requests to the provider slapd according to the search
+specification. The search specification includes {{EX:searchbase}},
+{{EX:scope}}, {{EX:filter}}, {{EX:attrs}}, {{EX:exattrs}}, {{EX:attrsonly}},
+{{EX:sizelimit}}, and {{EX:timelimit}} parameters as in the normal
+search specification. The {{EX:searchbase}} parameter has no
+default value and must always be specified. The {{EX:scope}} defaults
+to {{EX:sub}}, the {{EX:filter}} defaults to {{EX:(objectclass=*)}},
+{{EX:attrs}} defaults to {{EX:"*,+"}} to replicate all user and operational
+attributes, and {{EX:attrsonly}} is unset by default. Both {{EX:sizelimit}}
+and {{EX:timelimit}} default to "unlimited", and only positive integers
+or "unlimited" may be specified. The {{EX:exattrs}} option may also be used
+to specify attributes that should be omitted from incoming entries.
+
+The {{TERM[expand]LDAP Sync}} protocol has two operation
+types: {{EX:refreshOnly}} and {{EX:refreshAndPersist}}.
+The operation type is specified by the {{EX:type}} parameter.
+In the {{EX:refreshOnly}} operation, the next synchronization search operation
+is periodically rescheduled at an interval time after each
+synchronization operation finishes. The interval is specified
+by the {{EX:interval}} parameter. It is set to one day by default.
+In the {{EX:refreshAndPersist}} operation, a synchronization search
+remains persistent in the provider {{slapd}} instance. Further updates to the
+provider will generate {{EX:searchResultEntry}} to the consumer slapd
+as the search responses to the persistent synchronization search.
+
+If an error occurs during replication, the consumer will attempt to reconnect
+according to the retry parameter which is a list of the <retry interval>
+and <# of retries> pairs. For example, retry="60 10 300 3" lets the consumer
+retry every 60 seconds for the first 10 times and then retry every 300 seconds
+for the next three times before stop retrying. + in <# of retries> means
+indefinite number of retries until success.
+
+The schema checking can be enforced at the LDAP Sync consumer site
+by turning on the {{EX:schemachecking}} parameter.
+If it is turned on, every replicated entry will be checked for its
+schema as the entry is stored on the consumer.
+Every entry in the consumer should contain those attributes
+required by the schema definition.
+If it is turned off, entries will be stored without checking
+schema conformance. The default is off.
+
+The {{EX:network-timeout}} parameter sets how long the consumer will
+wait to establish a network connection to the provider. Once a
+connection is established, the {{EX:timeout}} parameter determines how
+long the consumer will wait for the initial Bind request to complete. The
+defaults for these parameters come from {{ldap.conf}}(5).
+
+The {{EX:binddn}} parameter gives the DN to bind as for the
+syncrepl searches to the provider slapd. It should be a DN
+which has read access to the replication content in the
+provider database.
+
+The {{EX:bindmethod}} is {{EX:simple}} or {{EX:sasl}},
+depending on whether simple password-based authentication or
+{{TERM:SASL}} authentication is to be used when connecting
+to the provider {{slapd}} instance.
+
+Simple authentication should not be used unless adequate data
+integrity and confidentiality protections are in place (e.g. TLS
+or IPsec). Simple authentication requires specification of {{EX:binddn}}
+and {{EX:credentials}} parameters.
+
+SASL authentication is generally recommended. SASL authentication
+requires specification of a mechanism using the {{EX:saslmech}} parameter.
+Depending on the mechanism, an authentication identity and/or
+credentials can be specified using {{EX:authcid}} and {{EX:credentials}},
+respectively. The {{EX:authzid}} parameter may be used to specify
+an authorization identity.
+
+The {{EX:realm}} parameter specifies a realm which a certain
+mechanisms authenticate the identity within. The {{EX:secprops}}
+parameter specifies Cyrus SASL security properties.
+
+The {{EX:keepalive}} parameter sets the values of idle, probes, and interval
+used to check whether a socket is alive; idle is the number of seconds a
+connection needs to remain idle before TCP starts sending keepalive probes;
+probes is the maximum number of keepalive probes TCP should send before
+dropping the connection; interval is interval in seconds between individual
+keepalive probes. Only some systems support the customization of these
+values; the keepalive parameter is ignored otherwise, and system-wide
+settings are used. For example, keepalive="240:10:30" will send a keepalive
+probe 10 times, every 30 seconds, after 240 seconds of idle activity. If
+no response to the probes is received, the connection will be dropped.
+
+The {{EX:starttls}} parameter specifies use of the StartTLS extended
+operation to establish a TLS session before authenticating to the provider.
+If the {{EX:critical}} argument is supplied, the session will be aborted
+if the StartTLS request fails. Otherwise the syncrepl session continues
+without TLS. The tls_reqcert setting defaults to {{EX:"demand"}} and the
+other TLS settings default to the same as the main slapd TLS settings.
+
+The {{EX:suffixmassage}} parameter allows the consumer to pull entries
+from a remote directory whose DN suffix differs from the local directory.
+The portion of the remote entries' DNs that matches the searchbase will
+be replaced with the suffixmassage DN.
+
+Rather than replicating whole entries, the consumer can query logs
+of data modifications. This mode of operation is referred to as
+{{delta syncrepl}}. In addition to the above parameters, the
+{{EX:logbase}} and {{EX:logfilter}} parameters must be set appropriately
+for the log that will be used. The {{EX:syncdata}} parameter must
+be set to either {{EX:"accesslog"}} if the log conforms to the
+{{slapo-accesslog}}(5) log format, or {{EX:"changelog"}} if the log
+conforms to the obsolete {{changelog}} format. If the {{EX:syncdata}}
+parameter is omitted or set to {{EX:"default"}} then the log
+parameters are ignored.
+
+The {{syncrepl}} replication mechanism is supported by the {{mdb}} backend.
+
+See the {{SECT:LDAP Sync Replication}} chapter of this guide for
+more information on how to use this directive.
+
+
+H4: updateref <URL>
+
+This directive is only applicable in a {{replica}} (or {{shadow}})
+{{slapd}}(8) instance. It
+specifies the URL to return to clients which submit update
+requests upon the replica.
+If specified multiple times, each {{TERM:URL}} is provided.
+
+\Example:
+
+> updateref ldap://provider.example.net
+
+
+H3: MDB Backend Directives
+
+Directives in this category only apply to the {{TERM:MDB}}
+database backend. They will apply to all "database mdb"
+instances in the configuration. For a complete reference
+of MDB backend configuration directives, see {{slapd-mdb}}(5).
+
+H4: idlexp <exponent>
+
+Specify a power of 2 for the maximum size of an index slot.
+The default is 16, yielding a maximum slot size of 2^16 or 65536.
+The specified value must be in the range of 16-30.
+
+This setting helps with the case where certain search filters are
+slow to return results due to an index slot having collapsed to a
+range value. This occurs when the number of candidate entries that
+match the filter for the index slot exceed the configured slot size.
+
+If this setting is decreased on a server with existing {{TERM:MDB}}
+databases, each db will immediately need its indices to be rebuilt
+while slapd is offline with the "slapindex -q -t" command.
+
+If this setting is increased on a server with existing {{TERM:MDB}}
+databases, each db will need its indices rebuilt to take advantage
+of the change for indices that have already been converted to ranges.
+
+
+H3: MDB Database Directives
+
+Directives in this category only apply to the {{TERM:MDB}}
+database backend.
+That is, they must follow a "database mdb" line
+and come before any subsequent "backend" or "database" lines.
+For a complete reference of MDB configuration directives, see {{slapd-mdb}}(5).
+
+H4: directory <directory>
+
+This directive specifies the directory where the MDB files
+containing the database and associated indices live.
+
+\Default:
+
+> directory /usr/local/var/openldap-data
+
+H4: checkpoint <kbyte> <min>
+
+This directive specifies the frequency for flushing the database disk
+buffers. This directive is only needed if the {{dbnosync}} option is
+{{EX:TRUE}}.
+The checkpoint will occur if either <kbyte> data has been written or
+<min> minutes have passed since the last checkpoint. Both arguments default
+to zero, in which case they are ignored. When the <min> argument is
+non-zero, an internal task will run every <min> minutes to perform the
+checkpoint. Note: currently the _kbyte_ setting is unimplemented.
+
+\Example:
+
+> checkpoint: 1024 10
+
+H4: dbnosync: { TRUE | FALSE }
+
+This directive causes on-disk database contents to not be immediately
+synchronized with in memory changes upon change. Setting this option
+to {{EX:TRUE}} may improve performance at the expense of data integrity.
+
+
+H4: envflags: {nosync,nometasync,writemap,mapasync,nordahead}
+
+This option specifies flags for finer-grained control of the LMDB library's
+operation.
+
+* {{F:nosync}}: This is exactly the same as the dbnosync directive.
+
+* {{F:nometasync}}: Flush the data on a commit, but skip the sync of the meta
+page. This mode is slightly faster than doing a full sync, but can
+potentially lose the last committed transaction if the operating system
+crashes. If both nometasync and nosync are set, the nosync flag takes
+precedence.
+
+* {{F:writemap}}: Use a writable memory map instead of just read-only. This
+speeds up write operations but makes the database vulnerable to corruption in
+case any bugs in slapd cause stray writes into the mmap region.
+
+* {{F:mapasync}}: When using a writable memory map and performing flushes on
+each commit, use an asynchronous flush instead of a synchronous flush (the
+default). This option has no effect if writemap has not been set. It also has
+no effect if nosync is set.
+
+* {{F:nordahead}}: Turn off file readahead. Usually the OS performs readahead
+on every read request. This usually boosts read performance but can be
+harmful to random access read performance if the system's memory is full and
+the DB is larger than RAM. This option is not implemented on Windows.
+
+
+H4: index: {<attrlist> | default} [pres,eq,approx,sub,none]
+
+This directive specifies the indices to maintain for the given
+attribute. If only an {{EX:<attrlist>}} is given, the default
+indices are maintained. The index keywords correspond to the
+common types of matches that may be used in an LDAP search filter.
+
+\Example:
+
+> index: default pres,eq
+> index: uid
+> index: cn,sn pres,eq,sub
+> index: objectClass eq
+
+The first line sets the default set of indices to maintain to
+present and equality. The second line causes the default (pres,eq)
+set of indices to be maintained for the {{EX:uid}} attribute type.
+The third line causes present, equality, and substring indices to
+be maintained for {{EX:cn}} and {{EX:sn}} attribute types. The
+fourth line causes an equality index for the {{EX:objectClass}}
+attribute type.
+
+There is no index keyword for inequality matches. Generally these
+matches do not use an index. However, some attributes do support
+indexing for inequality matches, based on the equality index.
+
+A substring index can be more explicitly specified as {{EX:subinitial}},
+{{EX:subany}}, or {{EX:subfinal}}, corresponding to the three
+possible components
+of a substring match filter. A subinitial index only indexes
+substrings that appear at the beginning of an attribute value.
+A subfinal index only indexes substrings that appear at the end
+of an attribute value, while subany indexes substrings that occur
+anywhere in a value.
+
+Note that by default, setting an index for an attribute also
+affects every subtype of that attribute. E.g., setting an equality
+index on the {{EX:name}} attribute causes {{EX:cn}}, {{EX:sn}}, and every other
+attribute that inherits from {{EX:name}} to be indexed.
+
+By default, no indices are maintained. It is generally advised
+that minimally an equality index upon objectClass be maintained.
+
+> index: objectClass eq
+
+Additional indices should be configured corresponding to the
+most common searches that are used on the database.
+Presence indexing should not be configured for an attribute
+unless the attribute occurs very rarely in the database, and
+presence searches on the attribute occur very frequently during
+normal use of the directory. Most applications don't use presence
+searches, so usually presence indexing is not very useful.
+
+
+H4: maxentrysize: <bytes>
+
+Specify the maximum size of an entry in bytes. Attempts to store
+an entry larger than this size will be rejected with the error
+LDAP_ADMINLIMIT_EXCEEDED. The default is 0, which is unlimited.
+
+
+H4: maxreaders: <integer>
+
+This directive specifies the maximum number of threads that may have
+concurrent read access to the database. Tools such as slapcat count as a
+single thread, in addition to threads in any active slapd processes. The
+default is 126.
+
+
+H4: maxsize: <bytes>
+
+This directive specifies the maximum size of the database in bytes. A memory
+map of this size is allocated at startup time and the database will not be
+allowed to grow beyond this size. The default is 10485760 bytes (10MB). This
+setting may be changed upward if the configured limit needs to be increased.
+
+Note: It is important to set this to as large a value as possible, (relative
+to anticipated growth of the actual data over time) since growing the size
+later may not be practical when the system is under heavy load.
+
+
+H4: mode: { <octal> | <symbolic> }
+
+This directive specifies the file protection mode that newly
+created database index files should have. This can be in the form
+{{EX:0600}} or {{EX:-rw-------}}
+
+\Default:
+
+> mode: 0600
+
+
+H4: multival: { <attrlist> | default } <integer> hi,<integer> lo
+
+Specify the number of values for which a multivalued attribute is
+stored in a separate table. Normally entries are stored as a single
+blob inside the database. When an entry gets very large or contains
+attributes with a very large number of values, modifications on that
+entry may get very slow. Splitting the large attributes out to a separate
+table can improve the performance of modification operations.
+The threshold is specified as a pair of integers. If the number of
+values exceeds the hi threshold the values will be split out. If
+a modification deletes enough values to bring an attribute below
+the lo threshold the values will be removed from the separate
+table and merged back into the main entry blob.
+The threshold can be set for a specific list of attributes, or
+the default can be configured for all other attributes.
+The default value for both hi and lo thresholds is UINT_MAX, which keeps
+all attributes in the main blob.
+
+In addition to increasing write performance of operations the use of
+multival can also decrease fragmentation of the primary {{TERM:MDB}} database.
+
+
+H4: rtxnsize: <entries>
+
+This directive specifies the maximum number of entries to process in a single
+read transaction when executing a large search. Long-lived read transactions
+prevent old database pages from being reused in write transactions, and so
+can cause significant growth of the database file when there is heavy write
+traffic. This setting causes the read transaction in large searches to be
+released and reacquired after the given number of entries has been read, to
+give writers the opportunity to reclaim old database pages. The default is
+10000.
+
+
+H4: searchstack: <integer>
+
+Specify the depth of the stack used for search filter evaluation.
+Search filters are evaluated on a stack to accommodate nested {{EX:AND}} /
+{{EX:OR}} clauses. An individual stack is allocated for each server thread.
+The depth of the stack determines how complex a filter can be evaluated
+without requiring any additional memory allocation. Filters that are
+nested deeper than the search stack depth will cause a separate stack to
+be allocated for that particular search operation. These separate allocations
+can have a major negative impact on server performance, but specifying
+too much stack will also consume a great deal of memory. Each search
+uses 512K bytes per level on a 32-bit machine, or 1024K bytes per level
+on a 64-bit machine. The default stack depth is 16, thus 8MB or 16MB
+per thread is used on 32 and 64 bit machines, respectively. Also the
+512KB size of a single stack slot is set by a compile-time constant which
+may be changed if needed; the code must be recompiled for the change
+to take effect.
+
+\Default:
+
+> searchstack: 16
+
+
+H4: Sample Entry
+
+>database mdb
+>suffix: "dc=example,dc=com"
+>directory: /usr/local/var/openldap-data
+>index: objectClass eq
+
+
+H2: Configuration File Example
+
+The following is an example configuration file, interspersed
+with explanatory text. It defines two databases to handle
+different parts of the {{TERM:X.500}} tree; both are {{TERM:MDB}}
+database instances. The line numbers shown are provided for
+reference only and are not included in the actual file. First, the
+global configuration section:
+
+E: 1. # example config file - global configuration section
+E: 2. include /usr/local/etc/schema/core.schema
+E: 3. referral ldap://root.openldap.org
+E: 4. access to * by * read
+
+Line 1 is a comment. Line 2 includes another config file
+which contains {{core}} schema definitions.
+The {{EX:referral}} directive on line 3
+means that queries not local to one of the databases defined
+below will be referred to the LDAP server running on the
+standard port (389) at the host {{EX:root.openldap.org}}.
+
+Line 4 is a global access control. It applies to all
+entries (after any applicable database-specific access
+controls).
+
+The next section of the configuration file defines a MDB
+backend that will handle queries for things in the
+"dc=example,dc=com" portion of the tree. The
+database is to be replicated to two replica slapds, one on
+truelies, the other on judgmentday. Indices are to be
+maintained for several attributes, and the {{EX:userPassword}}
+attribute is to be protected from unauthorized access.
+
+E: 5. # MDB definition for the example.com
+E: 6. database mdb
+E: 7. suffix "dc=example,dc=com"
+E: 8. directory /usr/local/var/openldap-data
+E: 9. rootdn "cn=Manager,dc=example,dc=com"
+E: 10. rootpw secret
+E: 11. # indexed attribute definitions
+E: 12. index uid pres,eq
+E: 13. index cn,sn pres,eq,approx,sub
+E: 14. index objectClass eq
+E: 15. # database access control definitions
+E: 16. access to attrs=userPassword
+E: 17. by self write
+E: 18. by anonymous auth
+E: 19. by dn.base="cn=Admin,dc=example,dc=com" write
+E: 20. by * none
+E: 21. access to *
+E: 22. by self write
+E: 23. by dn.base="cn=Admin,dc=example,dc=com" write
+E: 24. by * read
+
+Line 5 is a comment. The start of the database definition is marked
+by the database keyword on line 6. Line 7 specifies the DN suffix
+for queries to pass to this database. Line 8 specifies the directory
+in which the database files will live.
+
+Lines 9 and 10 identify the database {{super-user}} entry and associated
+password. This entry is not subject to access control or size or
+time limit restrictions.
+
+Lines 12 through 14 indicate the indices to maintain for various
+attributes.
+
+Lines 16 through 24 specify access control for entries in this
+database. For all applicable entries, the {{EX:userPassword}} attribute is writable
+by the entry itself and by the "admin" entry. It may be used for
+authentication/authorization purposes, but is otherwise not readable.
+All other attributes are writable by the entry and the "admin"
+entry, but may be read by all users (authenticated or not).
+
+The next section of the example configuration file defines another
+MDB database. This one handles queries involving the
+{{EX:dc=example,dc=net}} subtree but is managed by the same entity
+as the first database. Note that without line 39, the read access
+would be allowed due to the global access rule at line 4.
+
+E: 33. # MDB definition for example.net
+E: 34. database mdb
+E: 35. suffix "dc=example,dc=net"
+E: 36. directory /usr/local/var/openldap-data-net
+E: 37. rootdn "cn=Manager,dc=example,dc=com"
+E: 38. index objectClass eq
+E: 39. access to * by users read
diff --git a/doc/guide/admin/title.sdf b/doc/guide/admin/title.sdf
new file mode 100644
index 0000000..fcfc0fa
--- /dev/null
+++ b/doc/guide/admin/title.sdf
@@ -0,0 +1,13 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+#
+# Document: OpenLDAP Administrator's Guide
+# Master: master.sdf
+#
+
+!define DOC_TOC 3
+!define DOC_TYPE "Administrator's Guide"
+
+!build_title
+
diff --git a/doc/guide/admin/tls.sdf b/doc/guide/admin/tls.sdf
new file mode 100644
index 0000000..61cf66e
--- /dev/null
+++ b/doc/guide/admin/tls.sdf
@@ -0,0 +1,256 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: Using TLS
+
+OpenLDAP clients and servers are capable of using the
+{{TERM[expand]TLS}} ({{TERM:TLS}}) framework to provide
+integrity and confidentiality protections and to support
+LDAP authentication using the {{TERM:SASL}} {{TERM:EXTERNAL}} mechanism.
+TLS is defined in {{REF:RFC4346}}.
+
+Note: For generating certificates, please reference {{URL:http://www.openldap.org/faq/data/cache/185.html}}
+
+H2: TLS Certificates
+
+TLS uses {{TERM:X.509}} certificates to carry client and server
+identities. All servers are required to have valid certificates,
+whereas client certificates are optional. Clients must have a
+valid certificate in order to authenticate via SASL EXTERNAL.
+For more information on creating and managing certificates,
+see the {{PRD:OpenSSL}} or {{PRD:GnuTLS}} documentation,
+depending on which TLS implementation libraries you are using.
+
+H3: Server Certificates
+
+The {{TERM:DN}} of a server certificate must use the {{EX:CN}}
+attribute to name the server, and the {{EX:CN}} must carry the
+server's fully qualified domain name. Additional alias names and
+wildcards may be present in the {{EX:subjectAltName}} certificate
+extension. More details on server certificate names are in
+{{REF:RFC4513}}.
+
+H3: Client Certificates
+
+The DN of a client certificate can be used directly as an
+authentication DN.
+Since X.509 is a part of the {{TERM:X.500}} standard and LDAP
+is also based on X.500, both use the same DN formats and
+generally the DN in a user's X.509 certificate should be
+identical to the DN of their LDAP entry. However, sometimes
+the DNs may not be exactly the same, and so the mapping
+facility described in
+{{SECT:Mapping Authentication Identities}}
+can be applied to these DNs as well.
+
+H2: TLS Configuration
+
+After obtaining the required certificates, a number of options must
+be configured on both the client and the server to enable TLS and
+make use of the certificates. At a minimum, the clients must be
+configured with the name of the file containing all of the
+{{TERM[expand]CA}} (CA) certificates it will trust. The server must
+be configured with the {{TERM:CA}} certificates and also its own
+server certificate and private key.
+
+Typically a single CA will have issued the server certificate
+and all of the trusted client certificates, so the server only
+needs to trust that one signing CA. However, a client may wish
+to connect to a variety of secure servers managed by different
+organizations, with server certificates generated by many
+different CAs. As such, a client is likely to need a list of
+many different trusted CAs in its configuration.
+
+H3: Server Configuration
+
+The configuration directives for slapd belong in the global directives
+section of {{slapd.conf}}(5).
+
+H4: TLSCACertificateFile <filename>
+
+This directive specifies the {{TERM:PEM}}-format file containing
+certificates for the CA's that slapd will trust. The certificate for
+the CA that signed the server certificate must be included among
+these certificates. If the signing CA was not a top-level (root) CA,
+certificates for the entire sequence of CA's from the signing CA to
+the top-level CA should be present. Multiple certificates are simply
+appended to the file; the order is not significant.
+
+H4: TLSCACertificatePath <path>
+
+This directive specifies the path of a directory that contains
+individual {{TERM:CA}} certificates in separate files. In addition,
+this directory must be specially managed using the OpenSSL {{rehash}}
+command. When using this feature, the OpenSSL library will attempt to
+locate certificate files based on a hash of their name and serial number.
+The OpenSSL {{rehash}} command is used to generate symbolic links with the
+hashed names that point to the actual certificate files. As such,
+this option can only be used with a filesystem that actually supports
+symbolic links. In general, it is simpler to use the
+{{EX:TLSCACertificateFile}} directive instead.
+
+H4: TLSCertificateFile <filename>
+
+This directive specifies the file that contains the slapd server
+certificate. Certificates are generally public information and
+require no special protection.
+
+H4: TLSCertificateKeyFile <filename>
+
+This directive specifies the file that contains the private key
+that matches the certificate stored in the {{EX:TLSCertificateFile}}
+file. Private keys themselves are sensitive data and are usually
+password encrypted for protection. However, the current implementation
+doesn't support encrypted keys so the key must not be encrypted
+and the file itself must be protected carefully.
+
+H4: TLSCipherSuite <cipher-suite-spec>
+
+This directive configures what ciphers will be accepted and the
+preference order. {{EX:<cipher-suite-spec>}} should be a cipher
+specification for OpenSSL. You can use the command
+
+> openssl ciphers -v ALL
+
+to obtain a verbose list of available cipher specifications.
+
+Besides the individual cipher names, the specifiers {{EX:HIGH}},
+{{EX:MEDIUM}}, {{EX:LOW}}, {{EX:EXPORT}}, and {{EX:EXPORT40}}
+may be helpful, along with {{EX:TLSv1}}, {{EX:SSLv3}},
+and {{EX:SSLv2}}.
+
+To obtain the list of ciphers in GnuTLS use:
+
+> gnutls-cli -l
+
+H4: TLSRandFile <filename>
+
+This directive specifies the file to obtain random bits from when
+{{FILE:/dev/urandom}} is not available. If the system provides
+{{FILE:/dev/urandom}} then this option is not needed, otherwise a
+source of random data must be configured. Some systems (e.g. Linux)
+provide {{FILE:/dev/urandom}} by default, while others (e.g. Solaris)
+require the installation of a patch to provide it, and others may
+not support it at all. In the latter case, EGD or PRNGD should be
+installed, and this directive should specify the name of the EGD/PRNGD
+socket. The environment variable {{EX:RANDFILE}} can also be used
+to specify the filename. Also, in the absence of these options, the
+{{EX:.rnd}} file in the slapd user's home directory may be used if
+it exists. To use the {{EX:.rnd}} file, just create the file and
+copy a few hundred bytes of arbitrary data into the file. The file
+is only used to provide a seed for the pseudo-random number generator,
+and it doesn't need very much data to work.
+
+This directive is ignored with GnuTLS.
+
+H4: TLSDHParamFile <filename>
+
+This directive specifies the file that contains parameters for
+Diffie-Hellman ephemeral key exchange. This is required in order
+to use DHE-based cipher suites, including all DSA-based suites (i.e.
+{{EX:TLSCertificateKeyFile}} points to a DSA key), and RSA when the 'key
+encipherment' key usage is not specified in the certificate. Parameters can be
+generated using the following command
+
+> openssl dhparam [-dsaparam] -out <filename> <numbits>
+or
+> certtool --generate-dh-params --bits <numbits> --outfile <filename>
+
+H4: TLSECName <name>
+
+This directive specifies the curve to use for Elliptic Curve
+Diffie-Hellman ephemeral key exchange. This option is only needed
+to use ECDHE-based cipher suites in OpenSSL. The names of supported
+curves may be shown using the following command
+
+> openssl ecparam -list_curves
+
+See the OpenSSL documentation for details.
+This directive is not used for GnuTLS.
+For GnuTLS the curves may be specified in the ciphersuite.
+
+H4: TLSVerifyClient { never | allow | try | demand }
+
+This directive specifies what checks to perform on client certificates
+in an incoming TLS session, if any. This option is set to {{EX:never}}
+by default, in which case the server never asks the client for a
+certificate. With a setting of {{EX:allow}} the server will ask
+for a client certificate; if none is provided the session proceeds
+normally. If a certificate is provided but the server is unable to
+verify it, the certificate is ignored and the session proceeds
+normally, as if no certificate had been provided. With a setting of
+{{EX:try}} the certificate is requested, and if none is provided,
+the session proceeds normally. If a certificate is provided and it
+cannot be verified, the session is immediately terminated. With a
+setting of {{EX:demand}} the certificate is requested and a valid
+certificate must be provided, otherwise the session is immediately
+terminated.
+
+Note: The server must request a client certificate in order to
+use the SASL EXTERNAL authentication mechanism with a TLS session.
+As such, a non-default {{EX:TLSVerifyClient}} setting must be configured
+before SASL EXTERNAL authentication may be attempted, and the
+SASL EXTERNAL mechanism will only be offered to the client if a valid
+client certificate was received.
+
+H3: Client Configuration
+
+Most of the client configuration directives parallel the server
+directives. The names of the directives are different, and they go
+into {{ldap.conf}}(5) instead of {{slapd.conf}}(5), but their
+functionality is mostly the same. Also, while most of these options may
+be configured on a system-wide basis, they may all be overridden by
+individual users in their {{.ldaprc}} files.
+
+The LDAP Start TLS operation is used in LDAP to initiate TLS
+negotiation. All OpenLDAP command line tools support a {{EX:-Z}}
+and {{EX:-ZZ}} flag to indicate whether a Start TLS operation is to
+be issued. The latter flag indicates that the tool is to cease
+processing if TLS cannot be started while the former allows the
+command to continue.
+
+In LDAPv2 environments, TLS is normally started using the LDAP
+Secure URI scheme ({{EX:ldaps://}}) instead of the normal LDAP URI
+scheme ({{EX:ldap://}}). OpenLDAP command line tools allow either
+scheme to used with the {{EX:-H}} flag and with the {{EX:URI}}
+{{ldap.conf}}(5) option.
+
+
+H4: TLS_CACERT <filename>
+
+This is equivalent to the server's {{EX:TLSCACertificateFile}} option. As
+noted in the {{SECT:TLS Configuration}} section, a client typically
+may need to know about more CAs than a server, but otherwise the
+same considerations apply.
+
+H4: TLS_CACERTDIR <path>
+
+This is equivalent to the server's {{EX:TLSCACertificatePath}} option. The
+specified directory must be managed with the OpenSSL {{rehash}}
+command as well.
+
+H4: TLS_CERT <filename>
+
+This directive specifies the file that contains the client certificate.
+This is a user-only directive and can only be specified in a user's
+{{.ldaprc}} file.
+
+H4: TLS_KEY <filename>
+
+This directive specifies the file that contains the private key
+that matches the certificate stored in the {{EX:TLS_CERT}}
+file. The same constraints mentioned for {{EX:TLSCertificateKeyFile}}
+apply here. This is also a user-only directive.
+
+H4: TLS_RANDFILE <filename>
+
+This directive is the same as the server's {{EX:TLSRandFile}}
+option.
+
+H4: TLS_REQCERT { never | allow | try | demand }
+
+This directive is equivalent to the server's {{EX:TLSVerifyClient}}
+option. However, for clients the default value is {{EX:demand}}
+and there generally is no good reason to change this setting.
+
diff --git a/doc/guide/admin/troubleshooting.sdf b/doc/guide/admin/troubleshooting.sdf
new file mode 100644
index 0000000..ba7e824
--- /dev/null
+++ b/doc/guide/admin/troubleshooting.sdf
@@ -0,0 +1,104 @@
+# $OpenLDAP$
+# Copyright 2007-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: Troubleshooting
+
+If you're having trouble using OpenLDAP, get onto the
+OpenLDAP-Software mailing list, or:
+
+* Browse the list archives at {{URL:http://www.openldap.org/lists/#archives}}
+* Search the FAQ at {{URL:http://www.openldap.org/faq/}}
+* Search the Issue Tracking System at {{URL:http://www.openldap.org/its/}}
+
+Chances are the problem has been solved and explained in detail many times before.
+
+H2: User or Software errors?
+
+More often than not, an error is caused by a configuration problem or a misunderstanding
+of what you are trying to implement and/or achieve.
+
+We will now attempt to discuss common user errors.
+
+H2: Checklist
+
+The following checklist can help track down your problem. Please try to use if {{B:before}}
+posting to the list, or in the rare circumstances of reporting a bug.
+
+.{{S: }}
+^{{B: Use the {{slaptest}} tool to verify configurations before starting {{slapd}}}}
+
+.{{S: }}
++{{B: Verify that {{slapd}} is listening to the specified port(s) (389 and 636, generally) before trying the {{ldapsearch}}}}
+
+.{{S: }}
++{{B: Can you issue an {{ldapsearch}}?}}
+
+.{{S: }}
++{{B: If not, have you enabled complex ACLs without fully understanding them?}}
+
+.{{S: }}
++{{B: Do you have a system wide LDAP setting pointing to the wrong LDAP Directory?}}
+
+.{{S: }}
++{{B: Are you using TLS?}}
+
+.{{S: }}
++{{B: Have your certificates expired?}}
+
+H2: OpenLDAP Bugs
+
+Sometimes you may encounter an actual OpenLDAP bug, in which case please visit
+our Issue Tracking system {{URL:http://www.openldap.org/its/}} and report it.
+However, make sure it's not already a known bug or a common user problem.
+
+* bugs in historic versions of OpenLDAP will not be considered;
+* bugs in released versions that are no longer present in the Git master branch,
+either because they have been fixed or because they no longer apply,
+will not be considered as well;
+* bugs in distributions of OpenLDAP software that are not related to the
+software as provided by OpenLDAP will not be considered; in those cases please
+refer to the distributor.
+
+Note: Our Issue Tracking system is {{B:NOT}} for OpenLDAP {{B:Support}}, please join our
+mailing Lists: {{URL:http://www.openldap.org/lists/}} for that.
+
+The information you should provide in your bug report is discussed in our FAQ-O-MATIC at
+{{URL:http://www.openldap.org/faq/data/cache/59.html}}
+
+H2: 3rd party software error
+
+The OpenLDAP Project only supports OpenLDAP software.
+
+You may however seek commercial support ({{URL:http://www.openldap.org/support/}}) or join
+the general LDAP forum for non-commercial discussions and information relating to LDAP at:
+{{URL:http://www.umich.edu/~dirsvcs/ldap/mailinglist.html}}
+
+
+H2: How to contact the OpenLDAP Project
+
+* Mailing Lists: {{URL:http://www.openldap.org/lists/}}
+* Project: {{URL: http://www.openldap.org/project/}}
+* Issue Tracking: {{URL:http://www.openldap.org/its/}}
+
+
+H2: How to present your problem
+
+
+H2: Debugging {{slapd}}(8)
+
+After reading through the above sections and before e-mailing the OpenLDAP lists, you
+might want to try out some of the following to track down the cause of your problems:
+
+* A loglevel of stats (256) is generally a good first loglevel to use for getting
+ information useful to list members on issues. This is the default loglevel if none is configured.
+* Running {{slapd -d -1}} can often track down fairly simple issues, such as
+ missing schemas and incorrect file permissions for the {{slapd}} user to things like certs
+* Check your logs for errors, as discussed at {{URL:http://www.openldap.org/faq/data/cache/358.html}}
+
+H2: Commercial Support
+
+The firms listed at {{URL:http://www.openldap.org/support/}} offer technical support services catering to OpenLDAP community.
+
+The listing of any given firm should not be viewed as an endorsement or recommendation of any kind, nor as otherwise indicating
+there exists a business relationship or an affiliation between any listed firm and the OpenLDAP Foundation or the OpenLDAP Project or its contributors.
diff --git a/doc/guide/admin/tuning.sdf b/doc/guide/admin/tuning.sdf
new file mode 100644
index 0000000..f00984d
--- /dev/null
+++ b/doc/guide/admin/tuning.sdf
@@ -0,0 +1,206 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: Tuning
+
+This is perhaps one of the most important chapters in the guide, because if
+you have not tuned {{slapd}}(8) correctly or grasped how to design your
+directory and environment, you can expect very poor performance.
+
+Reading, understanding and experimenting using the instructions and information
+in the following sections, will enable you to fully understand how to tailor
+your directory server to your specific requirements.
+
+It should be noted that the following information has been collected over time
+from our community based FAQ. So obviously the benefit of this real world experience
+and advice should be of great value to the reader.
+
+
+H2: Performance Factors
+
+Various factors can play a part in how your directory performs on your chosen
+hardware and environment. We will attempt to discuss these here.
+
+
+H3: Memory
+
+Scale your cache to use available memory and increase system memory if you can.
+
+
+H3: Disks
+
+Use fast filesystems, and conduct your own testing to see which filesystem
+types perform best with your workload. (On our own Linux testing, EXT2 and JFS
+tend to provide better write performance than everything else, including
+newer filesystems like EXT4, BTRFS, etc.)
+
+Use fast subsystems. Put each database on separate disks.
+
+H3: Network Topology
+
+http://www.openldap.org/faq/data/cache/363.html
+
+Drawing here.
+
+
+H3: Directory Layout Design
+
+Reference to other sections and good/bad drawing here.
+
+
+H3: Expected Usage
+
+Discussion.
+
+
+H2: Indexes
+
+H3: Understanding how a search works
+
+If you're searching on a filter that has been indexed, then the search reads
+the index and pulls exactly the entries that are referenced by the index.
+If the filter term has not been indexed, then the search must read every single
+ entry in the target scope and test to see if each entry matches the filter.
+Obviously indexing can save a lot of work when it's used correctly.
+
+In back-mdb, indexes can only track a certain number of entries per key (by
+default that number is 2^16 = 65536). If more entries' values hash to this
+key, some/all of them will have to be represented by a range of candidates,
+making the index less useful over time as deletions cannot usually be tracked
+accurately.
+
+H3: What to index
+
+As a general rule, to make any use of indexes, you must set up an equality
+index on objectClass:
+
+> index objectClass eq
+
+Then you should create indices to match the actual filter terms used in
+search queries.
+
+> index cn,sn,givenname,mail eq
+
+Each attribute index can be tuned further by selecting the set of index types to generate. For example, substring and approximate search for organizations (o) may make little sense (and isn't like done very often). And searching for {{userPassword}} likely makes no sense what so ever.
+
+General rule: don't go overboard with indexes. Unused indexes must be maintained and hence can only slow things down.
+
+See {{slapd.conf}}(5) and {{slapdindex}}(8) for more information
+
+
+H3: Presence indexing
+
+If your client application uses presence filters and if the
+target attribute exists on the majority of entries in your target scope, then
+all of those entries are going to be read anyway, because they are valid
+members of the result set. In a subtree where 100% of the
+entries are going to contain the same attributes, the presence index does
+absolutely NOTHING to benefit the search, because 100% of the entries match
+that presence filter. As an example, setting a presence index on objectClass
+provides no benefit since it is present on every entry.
+
+So the resource cost of generating the index is a
+complete waste of CPU time, disk, and memory. Don't do it unless you know
+that it will be used, and that the attribute in question occurs very
+infrequently in the target data.
+
+Almost no applications use presence filters in their search queries. Presence
+indexing is pointless when the target attribute exists on the majority of
+entries in the database. In most LDAP deployments, presence indexing should
+not be done, it's just wasted overhead.
+
+See the {{Logging}} section below on what to watch out for if you have a frequently searched
+for attribute that is unindexed.
+
+H3: Equality indexing
+
+Similarly to presence indexes, equality indexes are most useful if the
+values searched for are uncommon. Most OpenLDAP indexes work by hashing
+the normalised value and using the hash as the key. Hashing behaviour
+depends on the matching rule syntax, some matching rules also implement
+indexers that help speed up inequality (lower than, ...) queries.
+
+Check the documentation and other parts of this guide if some indexes are
+mandatory - e.g. to enable replication, it is expected you index certain
+operational attributes, likewise if you rely on filters in ACL processing.
+
+Approximate indexes are usually identical to equality indexes unless
+a matching rule explicitly implements it. As of OpenLDAP 2.5, only
+directoryStringApproxMatch and IA5StringApproxMatch matchers
+and indexers are implemented, currently using soundex or metaphone, with
+metaphone being the default.
+
+H3: Substring indexing
+
+Substring indexes work on splitting the value into short chunks and then
+indexing those in a similar way to how equality index does. The storage
+space needed to store all of this data is analogous to the amount of data
+being indexed, which makes the indexes extremely heavy-handed in most
+scenarios.
+
+
+H2: Logging
+
+H3: What log level to use
+
+The default of {{loglevel stats}} (256) is really the best bet. There's a corollary to
+this when problems *do* arise, don't try to trace them using syslog.
+Use the debug flag instead, and capture slapd's stderr output. syslog is too
+slow for debug tracing, and it's inherently lossy - it will throw away messages when it
+can't keep up. See {{slapd.conf}}(5) or {{slapd-config}}(5) for more information on
+how to configure the loglevel.
+
+Contrary to popular belief, {{loglevel 0}} is not ideal for production as you
+won't be able to track when problems first arise.
+
+H3: What to watch out for
+
+The most common message you'll see that you should pay attention to is:
+
+> "<= mdb_equality_candidates: (foo) index_param failed (18)"
+
+That means that some application tried to use an equality filter ({{foo=<somevalue>}})
+and attribute {{foo}} does not have an equality index. If you see a lot of these
+messages, you should add the index. If you see one every month or so, it may
+be acceptable to ignore it.
+
+The default syslog level is stats (256) which logs the basic parameters of each
+request; it usually produces 1-3 lines of output. On Solaris and systems that
+only provide synchronous syslog, you may want to turn it off completely, but
+usually you want to leave it enabled so that you'll be able to see index
+messages whenever they arise. On Linux you can configure syslogd to run
+asynchronously, in which case the performance hit for moderate syslog traffic
+pretty much disappears.
+
+H3: Improving throughput
+
+You can improve logging performance on some systems by configuring syslog not
+to sync the file system with every write ({{man syslogd/syslog.conf}}). In Linux,
+you can prepend the log file name with a "-" in {{syslog.conf}}. For example,
+if you are using the default LOCAL4 logging you could try:
+
+> # LDAP logs
+> LOCAL4.* -/var/log/ldap
+
+For syslog-ng, add or modify the following line in {{syslog-ng.conf}}:
+
+> options { sync(n); };
+
+where n is the number of lines which will be buffered before a write.
+
+
+H2: {{slapd}}(8) Threads
+
+{{slapd}}(8) can process requests via a configurable number of threads, which
+in turn affects the in/out rate of connections.
+
+This value should generally be a function of the number of "real" cores on
+the system, for example on a server with 2 CPUs with one core each, set this
+to 8, or 4 threads per real core. This is a "read" maximized value. The more
+threads that are configured per core, the slower {{slapd}}(8) responds for
+"read" operations. On the flip side, it appears to handle write operations
+faster in a heavy write/low read scenario.
+
+The upper bound for good read performance appears to be 16 threads (which
+also happens to be the default setting).
diff --git a/doc/guide/images/LDAPlogo.gif b/doc/guide/images/LDAPlogo.gif
new file mode 100644
index 0000000..31b58b3
--- /dev/null
+++ b/doc/guide/images/LDAPlogo.gif
Binary files differ
diff --git a/doc/guide/images/LDAPwww.gif b/doc/guide/images/LDAPwww.gif
new file mode 100644
index 0000000..875d8cf
--- /dev/null
+++ b/doc/guide/images/LDAPwww.gif
Binary files differ
diff --git a/doc/guide/images/src/README.fonts b/doc/guide/images/src/README.fonts
new file mode 100644
index 0000000..e8bf1c6
--- /dev/null
+++ b/doc/guide/images/src/README.fonts
@@ -0,0 +1,10 @@
+# $OpenLDAP$
+# Copyright 2007-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+#
+# README.fonts
+#
+
+In dia we use:
+
+sans Normal 1.00 #000000
diff --git a/doc/guide/images/src/allmail-en.svg b/doc/guide/images/src/allmail-en.svg
new file mode 100644
index 0000000..baefb54
--- /dev/null
+++ b/doc/guide/images/src/allmail-en.svg
@@ -0,0 +1,230 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="487.42709"
+ height="139.10474"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="/home/andreas/palestra"
+ sodipodi:docname="allmail-en.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:export-filename="/home/andreas/palestra/ppolicy.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible">
+ <path
+ id="path3186"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lstart"
+ style="overflow:visible">
+ <path
+ id="path3183"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(0.8,0,0,0.8,10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Send"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Send"
+ style="overflow:visible">
+ <path
+ id="path3198"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.2,0,0,-0.2,-1.2,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart"
+ style="overflow:visible">
+ <path
+ id="path3201"
+ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.97309,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="matrix(1.1,0,0,1.1,1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path8347"
+ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.97309,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2.351189"
+ inkscape:cx="115.68184"
+ inkscape:cy="40.808267"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ showguides="false"
+ inkscape:window-width="1272"
+ inkscape:window-height="724"
+ inkscape:window-x="0"
+ inkscape:window-y="24" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Camada 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-29.405584,-34.695505)">
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="116.88309"
+ y="48.033184"
+ id="text2170"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2"><tspan
+ sodipodi:role="line"
+ id="tspan2172"
+ x="116.88309"
+ y="48.033184">DN: cn=all,ou=aliases,dc=example,dc=com</tspan><tspan
+ sodipodi:role="line"
+ x="116.88309"
+ y="63.033184"
+ id="tspan2174">cn: all</tspan><tspan
+ sodipodi:role="line"
+ x="116.88309"
+ y="78.033184"
+ id="tspan5373">objectClass: <tspan
+ style="font-weight:bold"
+ id="tspan5377">nisMailAlias</tspan></tspan><tspan
+ sodipodi:role="line"
+ x="116.88309"
+ y="92.036435"
+ id="tspan3404"
+ style="font-size:11px;font-weight:bold">labeledURI: <tspan
+ style="font-weight:normal"
+ id="tspan5513">ldap:///ou=People,dc=example,dc=com?mail?</tspan></tspan><tspan
+ sodipodi:role="line"
+ x="116.88309"
+ y="105.78643"
+ id="tspan3413"
+ style="font-size:11px;font-weight:normal"> one?(objectClass=inetOrgPerson)</tspan><tspan
+ sodipodi:role="line"
+ x="116.88309"
+ y="120.53318"
+ id="tspan2180">mail: john@example.com</tspan><tspan
+ sodipodi:role="line"
+ x="116.88309"
+ y="135.53318"
+ id="tspan3411">mail: mary@example.com</tspan></text>
+ <rect
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.15456796px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="rect7321"
+ width="403.52588"
+ height="106.68739"
+ x="112.72952"
+ y="35.272789"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2" />
+ <rect
+ style="opacity:0.28915663;fill:#aa9ab2;fill-opacity:1;stroke:none;stroke-width:0.69669151;stroke-opacity:1"
+ id="rect7323"
+ width="168.38275"
+ height="31.950695"
+ x="113.71371"
+ y="107.91574"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="28.755194"
+ y="104.6917"
+ id="text4394"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2"><tspan
+ sodipodi:role="line"
+ x="28.755194"
+ y="104.6917"
+ id="tspan5371">search</tspan><tspan
+ sodipodi:role="line"
+ x="28.755194"
+ y="119.6917"
+ id="tspan2187">results</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Lend);stroke-opacity:1"
+ d="M 108.91035,92.832512 C 59.12768,98.112492 59.881964,116.96956 108.15606,124.51239"
+ id="path4400"
+ sodipodi:nodetypes="cc"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2" />
+ <path
+ style="opacity:1;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-end:url(#Arrow1Lend);stroke-miterlimit:4;stroke-dasharray:1, 1;stroke-dashoffset:0;stroke-opacity:1"
+ d="M 417.4481,61.482433 C 381.50911,141.44198 176.87108,164.40908 132.69882,105.71538"
+ id="path5515"
+ transform="translate(11.556417,34.695505)"
+ sodipodi:nodetypes="cc" />
+ <rect
+ inkscape:export-ydpi="136.2"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ y="81.541206"
+ x="417.55688"
+ height="12.386127"
+ width="24.625708"
+ id="rect6492"
+ style="opacity:0.28915663;fill:#aa9ab2;fill-opacity:1;stroke:none;stroke-width:0.69669151;stroke-opacity:1" />
+ </g>
+</svg>
diff --git a/doc/guide/images/src/allusersgroup-en.svg b/doc/guide/images/src/allusersgroup-en.svg
new file mode 100644
index 0000000..a50ee90
--- /dev/null
+++ b/doc/guide/images/src/allusersgroup-en.svg
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="419.4133"
+ height="107.84196"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.46+devel"
+ version="1.0"
+ sodipodi:docname="allusersgroup-en.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/admin/allusersgroup-en.png"
+ inkscape:export-xdpi="107.65753"
+ inkscape:export-ydpi="107.65753">
+ <defs
+ id="defs4">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 53.920979 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="419.4133 : 53.920979 : 1"
+ inkscape:persp3d-origin="209.70665 : 35.947319 : 1"
+ id="perspective30" />
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart"
+ style="overflow:visible">
+ <path
+ id="path3201"
+ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.97309,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="matrix(1.1,0,0,1.1,1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path8347"
+ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.97309,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2.3724279"
+ inkscape:cx="216.23418"
+ inkscape:cy="53.920976"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ showguides="true"
+ inkscape:window-width="1274"
+ inkscape:window-height="950"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:guide-bbox="true"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Camada 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-29.134918,-34.695504)">
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="116.88309"
+ y="48.033184"
+ id="text2170"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2"><tspan
+ sodipodi:role="line"
+ id="tspan2172"
+ x="116.88309"
+ y="48.033184">DN: cn=allusers,ou=group,dc=example,dc=com</tspan><tspan
+ sodipodi:role="line"
+ x="116.88309"
+ y="63.033184"
+ id="tspan2174">cn: all</tspan><tspan
+ sodipodi:role="line"
+ x="116.88309"
+ y="78.033184"
+ id="tspan5379">objectClass: <tspan
+ style="font-weight:bold"
+ id="tspan5396">groupOfURLs</tspan></tspan><tspan
+ sodipodi:role="line"
+ x="116.88309"
+ y="92.036435"
+ id="tspan3413"
+ style="font-size:11px;font-weight:bold">labeledURI<tspan
+ style="font-weight:normal"
+ id="tspan5410">: ldap:///ou=People,dc=example,dc=com??</tspan></tspan><tspan
+ sodipodi:role="line"
+ x="116.88309"
+ y="105.78643"
+ style="font-size:11px;font-weight:normal"
+ id="tspan5386"> one?(objectClass=inetOrgPerson)</tspan><tspan
+ sodipodi:role="line"
+ x="116.88309"
+ y="120.53318"
+ id="tspan2180"><tspan
+ style="font-weight:bold"
+ id="tspan5400">member</tspan>: uid=john,ou=people,dc=example,dc=com</tspan><tspan
+ sodipodi:role="line"
+ x="116.88309"
+ y="135.53318"
+ id="tspan3411"><tspan
+ style="font-weight:bold"
+ id="tspan5404">member</tspan>: uid=mary,ou=people,dc=example,dc=com</tspan></text>
+ <rect
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.05301607px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="rect7321"
+ width="335.34296"
+ height="106.78895"
+ x="112.67875"
+ y="35.222012"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2" />
+ <rect
+ style="opacity:0.28915663;fill:#aa9ab2;fill-opacity:1;stroke:none;stroke-width:0.69669151;stroke-opacity:1"
+ id="rect7323"
+ width="329.5397"
+ height="29.000132"
+ x="114.97823"
+ y="110.02328"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="28.484528"
+ y="99.799805"
+ id="text4394"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2"><tspan
+ sodipodi:role="line"
+ x="28.484528"
+ y="99.799805"
+ id="tspan5390">DNs of</tspan><tspan
+ sodipodi:role="line"
+ x="28.484528"
+ y="114.7998"
+ id="tspan2186">search</tspan><tspan
+ sodipodi:role="line"
+ x="28.484528"
+ y="129.7998"
+ id="tspan2188">results</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Lend);stroke-opacity:1"
+ d="M 108.91035,92.832512 C 59.12768,98.112492 59.881964,116.96956 108.15606,124.51239"
+ id="path4400"
+ sodipodi:nodetypes="cc"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2" />
+ </g>
+</svg>
diff --git a/doc/guide/images/src/config_dit.dia b/doc/guide/images/src/config_dit.dia
new file mode 100644
index 0000000..fc9d7c0
--- /dev/null
+++ b/doc/guide/images/src/config_dit.dia
Binary files differ
diff --git a/doc/guide/images/src/config_local.dia b/doc/guide/images/src/config_local.dia
new file mode 100644
index 0000000..a460368
--- /dev/null
+++ b/doc/guide/images/src/config_local.dia
Binary files differ
diff --git a/doc/guide/images/src/config_ref.dia b/doc/guide/images/src/config_ref.dia
new file mode 100644
index 0000000..7bcc733
--- /dev/null
+++ b/doc/guide/images/src/config_ref.dia
Binary files differ
diff --git a/doc/guide/images/src/config_repl.dia b/doc/guide/images/src/config_repl.dia
new file mode 100644
index 0000000..813168f
--- /dev/null
+++ b/doc/guide/images/src/config_repl.dia
Binary files differ
diff --git a/doc/guide/images/src/delta-syncrepl.dia b/doc/guide/images/src/delta-syncrepl.dia
new file mode 100644
index 0000000..3f159ea
--- /dev/null
+++ b/doc/guide/images/src/delta-syncrepl.dia
Binary files differ
diff --git a/doc/guide/images/src/delta-syncrepl.svg b/doc/guide/images/src/delta-syncrepl.svg
new file mode 100644
index 0000000..642bf8a
--- /dev/null
+++ b/doc/guide/images/src/delta-syncrepl.svg
@@ -0,0 +1,4856 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="524.40942"
+ id="svg7893"
+ inkscape:version="0.46"
+ sodipodi:docbase="/home/ghenry/Desktop"
+ sodipodi:docname="delta-syncrepl.svg"
+ sodipodi:version="0.32"
+ width="744.09448"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ version="1.0"
+ inkscape:export-filename="/home/ghenry/Desktop/delta-syncrepl.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <metadata
+ id="metadata2563">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:title>Firewall2</dc:title>
+ <dc:description />
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>wall</rdf:li>
+ <rdf:li>brick</rdf:li>
+ <rdf:li>computer</rdf:li>
+ <rdf:li>networksym</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ <dc:publisher>
+ <cc:Agent
+ rdf:about="http://www.openclipart.org/">
+ <dc:title>Open Clip Art Library</dc:title>
+ </cc:Agent>
+ </dc:publisher>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>HASH(0x89c79d4)</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:rights>
+ <cc:Agent>
+ <dc:title>HASH(0x89c79d4)</dc:title>
+ </cc:Agent>
+ </dc:rights>
+ <dc:date />
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <cc:license
+ rdf:resource="http://web.resource.org/cc/PublicDomain" />
+ <dc:language>en</dc:language>
+ </cc:Work>
+ <cc:License
+ rdf:about="http://web.resource.org/cc/PublicDomain">
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Reproduction" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Distribution" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs7895">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 372.04724 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="1052.3622 : 372.04724 : 1"
+ inkscape:persp3d-origin="526.18109 : 248.03149 : 1"
+ id="perspective6943" />
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible">
+ <path
+ id="path17680"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lstart"
+ style="overflow:visible">
+ <path
+ id="path17677"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(0.8,0,0,0.8,10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend"
+ style="overflow:visible">
+ <path
+ id="path17686"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mstart"
+ style="overflow:visible">
+ <path
+ id="path17683"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(0.4,0,0,0.4,4,0)" />
+ </marker>
+ <linearGradient
+ id="linearGradient6508">
+ <stop
+ id="stop6509"
+ offset="0.0000000"
+ style="stop-color:#ff0000;stop-opacity:1.0000000;" />
+ <stop
+ id="stop6511"
+ offset="0.64370060"
+ style="stop-color:#ffb900;stop-opacity:1.0000000;" />
+ <stop
+ id="stop6512"
+ offset="0.79038113"
+ style="stop-color:#ffff00;stop-opacity:0.84102565;" />
+ <stop
+ id="stop6510"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.21568628;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient13376">
+ <stop
+ style="stop-color:#d4d4d4;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop13377" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.49803922;"
+ offset="0.50000000"
+ id="stop13380" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="1.0000000"
+ id="stop13378" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient12744">
+ <stop
+ style="stop-color:#839da4;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop12745" />
+ <stop
+ style="stop-color:#496d77;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop12746" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient10810">
+ <stop
+ style="stop-color:#0e0000;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop10811" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1.0000000;"
+ offset="0.50000000"
+ id="stop10814" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop10812" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient11442">
+ <stop
+ style="stop-color:#6e6e6e;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop11443" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop11444" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient14160">
+ <stop
+ style="stop-color:#4af853;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop14161" />
+ <stop
+ style="stop-color:#68b96d;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop14162" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient14835">
+ <stop
+ style="stop-color:#bed1d0;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop14836" />
+ <stop
+ style="stop-color:#52727b;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop14837" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient29203">
+ <stop
+ style="stop-color:#d3d3d3;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop29205" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop29207" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient6658">
+ <stop
+ style="stop-color:#677883;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop6659" />
+ <stop
+ style="stop-color:#677883;stop-opacity:0.0000000;"
+ offset="1.0000000"
+ id="stop6660" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient41493">
+ <stop
+ style="stop-color:#181818;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop41495" />
+ <stop
+ style="stop-color:#5e5e5e;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop41497" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient12759">
+ <stop
+ style="stop-color:#b4b4b4;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop12761" />
+ <stop
+ style="stop-color:#d7d8de;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop12763" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient21825">
+ <stop
+ style="stop-color:#808080;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop21827" />
+ <stop
+ style="stop-color:#5e5e5e;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop21829" />
+ </linearGradient>
+ <radialGradient
+ xlink:href="#linearGradient13376"
+ r="31.620827"
+ id="radialGradient25527"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.776429,0,0,0.659114,-120.5524,673.5049)"
+ fy="254.35735"
+ fx="-19.038713"
+ cy="253.63734"
+ cx="-19.261518" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12759"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient25525"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,-145.458,730.6984)" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient21825"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient25403"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,-145.2247,712.702)" />
+ <linearGradient
+ y2="1977.8738"
+ y1="1924.0137"
+ xlink:href="#linearGradient41493"
+ x2="-35.763195"
+ x1="-39.828941"
+ id="linearGradient25401"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.672454,0,0,0.374188,-3.473342,95.2718)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient25353"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.270019,0,0,0.370779,-149.3489,792.5495)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient26976"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-90.06505,808.8095)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient26972"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.05831,0,0,0.803858,616.249,115.0105)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient26974"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.838868,0,0,0.530755,508.4408,137.664)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient26964"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.05831,0,0,0.803858,616.249,115.0105)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient26966"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.838868,0,0,0.530755,508.4408,137.664)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28284"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28286"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28288"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28290"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28274"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28276"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28278"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28280"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28264"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28266"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28268"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28270"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28254"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28256"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28258"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28260"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28244"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28246"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28248"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28250"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28234"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28236"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28238"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28240"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28224"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28226"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28228"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28230"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28214"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28216"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28218"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28220"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28208"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-125.9178,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28210"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="97.536598"
+ cy="113.726"
+ cx="97.536598" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28204"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-121.573,808.7592)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28206"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="100.67591"
+ cy="113.726"
+ cx="100.67591" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28200"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-116.9703,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28202"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="104.00187"
+ cy="113.726"
+ cx="104.00187" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28196"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-112.6254,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28198"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="107.14119"
+ cy="113.726"
+ cx="107.14119" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28192"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-108.4824,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28194"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="110.13468"
+ cy="113.726"
+ cx="110.13468" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28188"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-104.1375,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28190"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="113.27399"
+ cy="113.726"
+ cx="113.27399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28184"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-99.77797,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28186"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="116.42374"
+ cy="113.726"
+ cx="116.42374" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28180"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-95.43307,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28182"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="119.56305"
+ cy="113.726"
+ cx="119.56305" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28172"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28174"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28176"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28178"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28162"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28164"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28168"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28152"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28154"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28156"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28158"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28142"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28144"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28146"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28148"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28132"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28134"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28136"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28138"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28122"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28124"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28126"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28128"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28112"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28114"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28116"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28118"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28102"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28104"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28106"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28108"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28096"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-161.2375,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28098"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="71.480988"
+ cy="113.726"
+ cx="71.480988" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28092"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-156.8927,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28094"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="74.620308"
+ cy="113.726"
+ cx="74.620308" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28088"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-152.29,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28090"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="77.946259"
+ cy="113.726"
+ cx="77.946259" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28084"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-147.9451,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28086"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="81.085587"
+ cy="113.726"
+ cx="81.085587" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28080"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-143.8021,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28082"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="84.079071"
+ cy="113.726"
+ cx="84.079071" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28076"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-139.4573,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28078"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="87.218399"
+ cy="113.726"
+ cx="87.218399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28072"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-135.098,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28074"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="90.368126"
+ cy="113.726"
+ cx="90.368126" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28068"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-130.7531,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28070"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="93.507462"
+ cy="113.726"
+ cx="93.507462" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28060"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28062"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28064"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28066"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28050"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28052"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28054"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28056"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28040"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28042"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28044"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28046"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28030"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28032"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28034"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28036"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28020"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28022"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28024"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28026"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28010"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28012"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28014"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28016"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28000"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28002"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28004"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28006"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27990"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27992"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27994"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27996"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27984"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-197.2616,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27986"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="45.452175"
+ cy="113.726"
+ cx="45.452175" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27980"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-192.9168,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27982"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="48.591496"
+ cy="113.726"
+ cx="48.591496" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27976"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-188.3141,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27978"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="51.91745"
+ cy="113.726"
+ cx="51.91745" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27972"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-183.9692,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27974"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="55.05677"
+ cy="113.726"
+ cx="55.05677" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27968"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-179.8262,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27970"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="58.050255"
+ cy="113.726"
+ cx="58.050255" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27964"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-175.4813,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27966"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="61.189575"
+ cy="113.726"
+ cx="61.189575" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27960"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-171.122,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27962"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="64.339317"
+ cy="113.726"
+ cx="64.339317" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27956"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-166.7771,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27958"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="67.478638"
+ cy="113.726"
+ cx="67.478638" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27928"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27930"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27932"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27934"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27918"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27920"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27922"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27924"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27908"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27910"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27912"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27914"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27898"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27900"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27902"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27904"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27888"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27890"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27892"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27894"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27878"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27880"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27882"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27884"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27868"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27870"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27872"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27874"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27858"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27860"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27862"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27864"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27848"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27850"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27852"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27854"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27838"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27840"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27842"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27844"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27828"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27830"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27832"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27834"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27818"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27820"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27822"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27824"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27808"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27810"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27812"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27814"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27798"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27800"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27802"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27804"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27788"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27790"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27792"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27794"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27778"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27780"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27782"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27784"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27768"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27770"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27772"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27774"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27758"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27760"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27762"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27764"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27748"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27750"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27752"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27754"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27738"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27740"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27742"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27744"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27728"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27730"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27732"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27734"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27718"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27720"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27722"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27724"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27708"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27710"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27712"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27714"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27698"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27700"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27702"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27704"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28432"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-126.1386,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28434"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="97.536598"
+ cy="113.726"
+ cx="97.536598" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28428"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-121.7938,817.6604)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28430"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="100.67591"
+ cy="113.726"
+ cx="100.67591" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28424"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-117.1911,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28426"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="104.00187"
+ cy="113.726"
+ cx="104.00187" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28420"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-112.8462,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28422"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="107.14119"
+ cy="113.726"
+ cx="107.14119" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28416"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-108.7032,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28418"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="110.13468"
+ cy="113.726"
+ cx="110.13468" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28412"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-104.3583,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28414"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="113.27399"
+ cy="113.726"
+ cx="113.27399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28408"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-99.99876,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28410"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="116.42374"
+ cy="113.726"
+ cx="116.42374" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28404"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-95.65386,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28406"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="119.56305"
+ cy="113.726"
+ cx="119.56305" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28400"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-161.4583,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28402"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="71.480988"
+ cy="113.726"
+ cx="71.480988" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28396"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-157.1135,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28398"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="74.620308"
+ cy="113.726"
+ cx="74.620308" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28392"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-152.5108,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28394"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="77.946259"
+ cy="113.726"
+ cx="77.946259" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28388"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-148.1659,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28390"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="81.085587"
+ cy="113.726"
+ cx="81.085587" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28384"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-144.0229,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28386"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="84.079071"
+ cy="113.726"
+ cx="84.079071" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28380"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-139.6781,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28382"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="87.218399"
+ cy="113.726"
+ cx="87.218399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28376"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-135.3188,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28378"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="90.368126"
+ cy="113.726"
+ cx="90.368126" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28372"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-130.9739,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28374"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="93.507462"
+ cy="113.726"
+ cx="93.507462" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28368"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-197.4824,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28370"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="45.452175"
+ cy="113.726"
+ cx="45.452175" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28364"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-193.1376,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28366"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="48.591496"
+ cy="113.726"
+ cx="48.591496" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28360"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-188.5349,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28362"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="51.91745"
+ cy="113.726"
+ cx="51.91745" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28356"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-184.19,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28358"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="55.05677"
+ cy="113.726"
+ cx="55.05677" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28352"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-180.047,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28354"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="58.050255"
+ cy="113.726"
+ cx="58.050255" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28348"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-175.7021,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28350"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="61.189575"
+ cy="113.726"
+ cx="61.189575" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28344"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-171.3428,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28346"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="64.339317"
+ cy="113.726"
+ cx="64.339317" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28340"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-166.9979,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28342"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="67.478638"
+ cy="113.726"
+ cx="67.478638" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28438"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-90.25863,817.7848)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12759"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient36281"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,-149.897,802.9053)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36283"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36285"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36287"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36289"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36291"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36293"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36295"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36297"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36299"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36301"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36303"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36305"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36307"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36309"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36311"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36313"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36315"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36317"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36319"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36321"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36323"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36325"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36327"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36329"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36331"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.5348,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36333"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-185.7196,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36335"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-189.35,879.6484)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36337"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-180.274,879.6484)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36339"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-183.9043,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36341"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-182.0892,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient21825"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient36343"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,-149.6637,784.9089)" />
+ <linearGradient
+ y2="1977.8738"
+ y1="1924.0137"
+ xlink:href="#linearGradient41493"
+ x2="-35.763195"
+ x1="-39.828941"
+ id="linearGradient36345"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.672454,0,0,0.374188,-7.912301,167.4787)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36347"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.5296,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36349"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-185.7144,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36351"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-189.3448,881.7646)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36353"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-180.2688,881.7646)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36355"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-183.8991,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36357"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-182.084,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36359"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-198.4916,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36361"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-190.046,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36363"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.2306,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36365"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-192.8611,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36367"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-195.6763,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36369"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.74272,0,0,0.445632,-87.12747,420.4818)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36371"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36373"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36375"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.787283,0,0,0.475341,-91.66274,388.2275)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36377"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36379"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36381"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.270019,0,0,0.370779,-153.7879,864.7564)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12759"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient35867"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,-141.9847,635.4266)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35869"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35871"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35873"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35875"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35877"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35879"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35881"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35883"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35885"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35887"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35889"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35891"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35893"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35895"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35897"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35899"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35901"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35903"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35905"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35907"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35909"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35911"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35913"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35915"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35917"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35919"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35921"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35923"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35925"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35927"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35929"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35931"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35933"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35935"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35937"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35939"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35941"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35943"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35945"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35947"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35949"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35951"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35953"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35955"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35957"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35959"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35961"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35963"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35965"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-179.6225,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35967"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-168.7312,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35969"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-163.2856,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35971"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-161.4702,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35973"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-165.1007,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35975"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-177.8073,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35977"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-166.9159,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35979"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-181.4377,712.1697)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35981"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-170.5465,712.1697)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35983"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-172.3617,712.1697)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35985"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-175.992,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35987"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-174.1769,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient21825"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient35989"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,-141.7514,617.4302)" />
+ <linearGradient
+ y2="1977.8738"
+ y1="1924.0137"
+ xlink:href="#linearGradient41493"
+ x2="-35.763195"
+ x1="-39.828941"
+ id="linearGradient35991"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(2.672454,0.374188)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35993"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-179.6173,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35995"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-168.726,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35997"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-163.2804,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35999"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-161.465,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36001"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-165.0955,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36003"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-177.8021,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36005"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-166.9107,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36007"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-181.4325,714.2859)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36009"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-170.5413,714.2859)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36011"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-172.3565,714.2859)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36013"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-175.9868,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36015"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-174.1717,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36017"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-190.5793,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36019"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-182.1337,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36021"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-179.3183,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36023"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-184.9488,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36025"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.764,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36027"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.74272,0,0,0.445632,-79.21517,253.0031)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36029"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36031"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36033"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.787283,0,0,0.475341,-83.75044,220.7488)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36035"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36037"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36039"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.270019,0,0,0.370779,-145.8756,697.2777)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient13376"
+ r="31.620827"
+ id="radialGradient12151"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.20227,0,0,0.454077,6.691668,-148.3193)"
+ fy="254.35735"
+ fx="-19.038713"
+ cy="253.63734"
+ cx="-19.261518" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12744"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient12153"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,1.691668,-145.8193)" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient14835"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient12155"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,1.924904,-161.8157)" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12744"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient12157"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.350818,114.6621,-134.6472)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12159"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-59.65453)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12161"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-61.33423)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12163"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-63.01391)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12165"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.09869,-64.40064)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ </defs>
+ <sodipodi:namedview
+ bordercolor="#666666"
+ borderopacity="1.0"
+ id="base"
+ inkscape:current-layer="layer1"
+ inkscape:cx="391.40904"
+ inkscape:cy="253.29159"
+ inkscape:document-units="px"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:window-height="722"
+ inkscape:window-width="1014"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:zoom="1"
+ pagecolor="#ffffff"
+ width="1052.3622px"
+ height="744.09448px"
+ showgrid="false" />
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1">
+ <g
+ id="g12774"
+ transform="matrix(0.1881701,0,0,0.2844466,82.77219,152.33679)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path12776"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12778"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12780"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot12890"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial"
+ transform="translate(51.007531,-424.27533)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><flowRegion
+ id="flowRegion12892"><rect
+ id="rect12894"
+ width="156.14285"
+ height="34"
+ x="194.28572"
+ y="475.52304"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara6968">Delta-syncrepl</flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot27609"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial"
+ transform="translate(-33,210)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><flowRegion
+ id="flowRegion27611"><rect
+ id="rect27613"
+ width="134.05586"
+ height="26.345188"
+ x="96.974648"
+ y="113.75929"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara27617">Provider</flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot3120"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial"
+ transform="matrix(1,0,0,1.2037203,-16.30957,-194.07388)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><flowRegion
+ id="flowRegion3122"><rect
+ id="rect3124"
+ width="317.52289"
+ height="139.3987"
+ x="412.14224"
+ y="279.42432"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara4477">Delta-syncrepl is a changelog-based variant of syncrepl. It works by maintaining a changelog of a selectable depth on the provider. The replication consumer checks the changelog for the changes.</flowPara></flowRoot> <g
+ id="g7023"
+ transform="matrix(0.1267968,0,0,0.1710106,204.38313,147.27416)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path7025"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path7027"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path7029"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="253"
+ y="224.40942"
+ id="text7033"><tspan
+ sodipodi:role="line"
+ x="253"
+ y="224.40942"
+ id="tspan7037">cn=accesslog</tspan><tspan
+ sodipodi:role="line"
+ x="253"
+ y="239.40942"
+ id="tspan3575">database to hold</tspan><tspan
+ sodipodi:role="line"
+ x="253"
+ y="254.40942"
+ id="tspan4415">changes etc.</tspan><tspan
+ sodipodi:role="line"
+ x="253"
+ y="254.40942"
+ id="tspan4419" /><tspan
+ sodipodi:role="line"
+ x="253"
+ y="269.40942"
+ id="tspan4417" /><tspan
+ sodipodi:role="line"
+ x="253"
+ y="284.40942"
+ id="tspan3577" /><tspan
+ sodipodi:role="line"
+ x="253"
+ y="299.40942"
+ id="tspan3573" /></text>
+ <rect
+ style="fill:#0000ff;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:2, 1;stroke-dashoffset:0;opacity:0"
+ id="rect3579"
+ width="297"
+ height="168"
+ x="48"
+ y="128.40942" />
+ <rect
+ style="opacity:0;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:2, 1;stroke-dashoffset:0;fill:none"
+ id="rect4375"
+ width="305"
+ height="167"
+ x="55"
+ y="127.40942" />
+ <rect
+ style="opacity:0;fill:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:2, 1;stroke-dashoffset:0"
+ id="rect4379"
+ width="293"
+ height="167"
+ x="60"
+ y="123.40942" />
+ <rect
+ style="opacity:0;fill:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:2, 1;stroke-dashoffset:0;fill-opacity:1"
+ id="rect4381"
+ width="275"
+ height="161"
+ x="76"
+ y="143.40942" />
+ <rect
+ style="opacity:0;fill:#000000;fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:2, 1;stroke-dashoffset:0"
+ id="rect4383"
+ width="305"
+ height="172"
+ x="61"
+ y="127.40942" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="93.694336"
+ y="286.38306"
+ id="text4397"><tspan
+ sodipodi:role="line"
+ x="93.694336"
+ y="301.38306"
+ id="tspan4401">main database</tspan><tspan
+ sodipodi:role="line"
+ x="93.694336"
+ y="316.38306"
+ id="tspan4403" /><tspan
+ sodipodi:role="line"
+ x="93.694336"
+ y="331.38306"
+ id="tspan4405" /></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="316"
+ y="236.40942"
+ id="text4409"><tspan
+ sodipodi:role="line"
+ id="tspan4411"
+ x="316"
+ y="236.40942"></tspan><tspan
+ sodipodi:role="line"
+ id="tspan4413" /></text>
+ <rect
+ style="fill:#9087ff;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:1;fill-opacity:0;stroke-miterlimit:4;stroke-dasharray:3,1;stroke-dashoffset:0"
+ id="rect4421"
+ width="313"
+ height="184"
+ x="64"
+ y="133.40942" />
+ <g
+ id="g4423"
+ transform="matrix(0.1267968,0,0,0.1710106,337.38313,350.27416)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path4425"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path4427"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path4429"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="276.03223"
+ y="437.88306"
+ id="text4431"><tspan
+ sodipodi:role="line"
+ x="276.03223"
+ y="437.88306"
+ id="tspan4441"><tspan
+ style="font-weight:bold"
+ id="tspan5582">Consumer</tspan> which uses syncrepl and the </tspan><tspan
+ sodipodi:role="line"
+ x="276.03223"
+ y="452.88306"
+ id="tspan4439">&quot;syncdata=accesslog&quot; setting.</tspan><tspan
+ sodipodi:role="line"
+ x="276.03223"
+ y="467.88306"
+ id="tspan4469">Switches back to normal syncrepl if gets </tspan><tspan
+ sodipodi:role="line"
+ x="276.03223"
+ y="482.88306"
+ id="tspan4471">too far out of sync, then once caught up </tspan><tspan
+ sodipodi:role="line"
+ x="276.03223"
+ y="497.88306"
+ id="tspan4473">goes back to delta.</tspan><tspan
+ sodipodi:role="line"
+ x="276.03223"
+ y="512.88306"
+ id="tspan4435" /><tspan
+ sodipodi:role="line"
+ x="276.03223"
+ y="527.88306"
+ id="tspan4437" /></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.08729029px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 244.20659,325.76325 L 336.79341,392.05559"
+ id="path25655" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.48164538px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 168.40377,220.39252 L 208.59623,190.42632"
+ id="path5584" />
+ </g>
+</svg>
diff --git a/doc/guide/images/src/dual_dc.svg b/doc/guide/images/src/dual_dc.svg
new file mode 100755
index 0000000..ebd7a96
--- /dev/null
+++ b/doc/guide/images/src/dual_dc.svg
@@ -0,0 +1,6810 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="744.09448"
+ id="svg7893"
+ inkscape:version="0.45.1"
+ sodipodi:docbase="/home/ghenry/Desktop"
+ sodipodi:docname="dual_dc.svg"
+ sodipodi:version="0.32"
+ width="1052.3622"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ version="1.0"
+ inkscape:export-filename="/home/ghenry/Desktop/dual_dc.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <metadata
+ id="metadata2563">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:title>Firewall2</dc:title>
+ <dc:description />
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>wall</rdf:li>
+ <rdf:li>brick</rdf:li>
+ <rdf:li>computer</rdf:li>
+ <rdf:li>networksym</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ <dc:publisher>
+ <cc:Agent
+ rdf:about="http://www.openclipart.org/">
+ <dc:title>Open Clip Art Library</dc:title>
+ </cc:Agent>
+ </dc:publisher>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>HASH(0x89c79d4)</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:rights>
+ <cc:Agent>
+ <dc:title>HASH(0x89c79d4)</dc:title>
+ </cc:Agent>
+ </dc:rights>
+ <dc:date />
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <cc:license
+ rdf:resource="http://web.resource.org/cc/PublicDomain" />
+ <dc:language>en</dc:language>
+ </cc:Work>
+ <cc:License
+ rdf:about="http://web.resource.org/cc/PublicDomain">
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Reproduction" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Distribution" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs7895">
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Lend"
+ style="overflow:visible;">
+ <path
+ id="path17680"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
+ transform="scale(0.8) rotate(180) translate(12.5,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Lstart"
+ style="overflow:visible">
+ <path
+ id="path17677"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ transform="scale(0.8) translate(12.5,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Mend"
+ style="overflow:visible;">
+ <path
+ id="path17686"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none;"
+ transform="scale(0.4) rotate(180) translate(10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Mstart"
+ style="overflow:visible">
+ <path
+ id="path17683"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none"
+ transform="scale(0.4) translate(10,0)" />
+ </marker>
+ <linearGradient
+ id="linearGradient6508">
+ <stop
+ id="stop6509"
+ offset="0.0000000"
+ style="stop-color:#ff0000;stop-opacity:1.0000000;" />
+ <stop
+ id="stop6511"
+ offset="0.64370060"
+ style="stop-color:#ffb900;stop-opacity:1.0000000;" />
+ <stop
+ id="stop6512"
+ offset="0.79038113"
+ style="stop-color:#ffff00;stop-opacity:0.84102565;" />
+ <stop
+ id="stop6510"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.21568628;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient13376">
+ <stop
+ style="stop-color:#d4d4d4;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop13377" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.49803922;"
+ offset="0.50000000"
+ id="stop13380" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="1.0000000"
+ id="stop13378" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient12744">
+ <stop
+ style="stop-color:#839da4;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop12745" />
+ <stop
+ style="stop-color:#496d77;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop12746" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient10810">
+ <stop
+ style="stop-color:#0e0000;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop10811" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1.0000000;"
+ offset="0.50000000"
+ id="stop10814" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop10812" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient11442">
+ <stop
+ style="stop-color:#6e6e6e;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop11443" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop11444" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient14160">
+ <stop
+ style="stop-color:#4af853;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop14161" />
+ <stop
+ style="stop-color:#68b96d;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop14162" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient14835">
+ <stop
+ style="stop-color:#bed1d0;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop14836" />
+ <stop
+ style="stop-color:#52727b;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop14837" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient29203">
+ <stop
+ style="stop-color:#d3d3d3;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop29205" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop29207" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient6658">
+ <stop
+ style="stop-color:#677883;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop6659" />
+ <stop
+ style="stop-color:#677883;stop-opacity:0.0000000;"
+ offset="1.0000000"
+ id="stop6660" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient41493">
+ <stop
+ style="stop-color:#181818;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop41495" />
+ <stop
+ style="stop-color:#5e5e5e;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop41497" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient12759">
+ <stop
+ style="stop-color:#b4b4b4;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop12761" />
+ <stop
+ style="stop-color:#d7d8de;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop12763" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient21825">
+ <stop
+ style="stop-color:#808080;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop21827" />
+ <stop
+ style="stop-color:#5e5e5e;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop21829" />
+ </linearGradient>
+ <radialGradient
+ xlink:href="#linearGradient13376"
+ r="31.620827"
+ id="radialGradient25527"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.776429,0.000000,0.000000,0.659114,-120.5524,673.5049)"
+ fy="254.35735"
+ fx="-19.038713"
+ cy="253.63734"
+ cx="-19.261518" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12759"
+ x2="8.3977861"
+ x1="-35.945030"
+ id="linearGradient25525"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0.000000,0.000000,0.399114,-145.4580,730.6984)" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient21825"
+ x2="8.3977861"
+ x1="-35.945030"
+ id="linearGradient25403"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0.000000,0.000000,0.399114,-145.2247,712.7020)" />
+ <linearGradient
+ y2="1977.8738"
+ y1="1924.0137"
+ xlink:href="#linearGradient41493"
+ x2="-35.763195"
+ x1="-39.828941"
+ id="linearGradient25401"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.672454,0.000000,0.000000,0.374188,-3.473342,95.27180)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient25353"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.270019,0.000000,0.000000,0.370779,-149.3489,792.5495)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient26976"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-90.06505,808.8095)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient26972"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.058310,0.000000,0.000000,0.803858,616.2490,115.0105)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient26974"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.838868,0.000000,0.000000,0.530755,508.4408,137.6640)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient26964"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.058310,0.000000,0.000000,0.803858,616.2490,115.0105)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient26966"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.838868,0.000000,0.000000,0.530755,508.4408,137.6640)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28284"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28286"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28288"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.026341e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28290"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28274"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28276"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28278"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.026156e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28280"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28264"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28266"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28268"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.027076e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28270"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28254"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28256"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28258"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.026932e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28260"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28244"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28246"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28248"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.026947e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28250"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28234"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28236"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28238"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.026928e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28240"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28224"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28226"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28228"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.026495e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28230"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28214"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28216"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28218"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.026890e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28220"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28208"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-125.9178,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28210"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-286.0884,752.1340)"
+ fy="113.72600"
+ fx="97.536598"
+ cy="113.72600"
+ cx="97.536598" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28204"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-121.5730,808.7592)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28206"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-286.0884,752.1340)"
+ fy="113.72600"
+ fx="100.67591"
+ cy="113.72600"
+ cx="100.67591" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28200"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-116.9703,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28202"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-286.0884,752.1340)"
+ fy="113.72600"
+ fx="104.00187"
+ cy="113.72600"
+ cx="104.00187" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28196"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-112.6254,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28198"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-286.0884,752.1340)"
+ fy="113.72600"
+ fx="107.14119"
+ cy="113.72600"
+ cx="107.14119" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28192"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-108.4824,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28194"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-286.0884,752.1340)"
+ fy="113.72600"
+ fx="110.13468"
+ cy="113.72600"
+ cx="110.13468" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28188"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-104.1375,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28190"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-286.0884,752.1340)"
+ fy="113.72600"
+ fx="113.27399"
+ cy="113.72600"
+ cx="113.27399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28184"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-99.77797,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28186"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-286.0884,752.1340)"
+ fy="113.72600"
+ fx="116.42374"
+ cy="113.72600"
+ cx="116.42374" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28180"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-95.43307,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28182"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-286.0884,752.1340)"
+ fy="113.72600"
+ fx="119.56305"
+ cy="113.72600"
+ cx="119.56305" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28172"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28174"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28176"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.023496e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28178"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28162"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28164"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.023311e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28168"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28152"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28154"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28156"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.024231e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28158"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28142"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28144"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28146"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.024087e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28148"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28132"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28134"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28136"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.024102e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28138"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28122"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28124"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28126"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.024083e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28128"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28112"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28114"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28116"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.023650e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28118"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28102"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28104"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28106"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.024045e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28108"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28096"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-161.2375,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28098"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.3469,752.1340)"
+ fy="113.72600"
+ fx="71.480988"
+ cy="113.72600"
+ cx="71.480988" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28092"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-156.8927,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28094"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.3469,752.1340)"
+ fy="113.72600"
+ fx="74.620308"
+ cy="113.72600"
+ cx="74.620308" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28088"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-152.2900,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28090"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.3469,752.1340)"
+ fy="113.72600"
+ fx="77.946259"
+ cy="113.72600"
+ cx="77.946259" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28084"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-147.9451,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28086"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.3469,752.1340)"
+ fy="113.72600"
+ fx="81.085587"
+ cy="113.72600"
+ cx="81.085587" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28080"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-143.8021,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28082"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.3469,752.1340)"
+ fy="113.72600"
+ fx="84.079071"
+ cy="113.72600"
+ cx="84.079071" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28076"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-139.4573,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28078"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.3469,752.1340)"
+ fy="113.72600"
+ fx="87.218399"
+ cy="113.72600"
+ cx="87.218399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28072"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-135.0980,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28074"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.3469,752.1340)"
+ fy="113.72600"
+ fx="90.368126"
+ cy="113.72600"
+ cx="90.368126" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28068"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-130.7531,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28070"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.3469,752.1340)"
+ fy="113.72600"
+ fx="93.507462"
+ cy="113.72600"
+ cx="93.507462" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28060"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28062"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28064"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.026556e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28066"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28050"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28052"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28054"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.027091e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28056"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28040"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28042"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28044"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.026139e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28046"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28030"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28032"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28034"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.025995e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28036"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28020"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28022"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28024"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.026010e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28026"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28010"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28012"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28014"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.025991e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28016"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28000"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28002"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28004"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.025558e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28006"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27990"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27992"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27994"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.026529e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27996"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27984"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-197.2616,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27986"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.3469,752.1340)"
+ fy="113.72600"
+ fx="45.452175"
+ cy="113.72600"
+ cx="45.452175" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27980"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-192.9168,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27982"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.3469,752.1340)"
+ fy="113.72600"
+ fx="48.591496"
+ cy="113.72600"
+ cx="48.591496" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27976"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-188.3141,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27978"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.3469,752.1340)"
+ fy="113.72600"
+ fx="51.917450"
+ cy="113.72600"
+ cx="51.917450" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27972"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-183.9692,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27974"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.3469,752.1340)"
+ fy="113.72600"
+ fx="55.056770"
+ cy="113.72600"
+ cx="55.056770" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27968"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-179.8262,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27970"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.3469,752.1340)"
+ fy="113.72600"
+ fx="58.050255"
+ cy="113.72600"
+ cx="58.050255" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27964"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-175.4813,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27966"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.3469,752.1340)"
+ fy="113.72600"
+ fx="61.189575"
+ cy="113.72600"
+ cx="61.189575" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27960"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-171.1220,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27962"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.3469,752.1340)"
+ fy="113.72600"
+ fx="64.339317"
+ cy="113.72600"
+ cx="64.339317" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27956"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-166.7771,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27958"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.3469,752.1340)"
+ fy="113.72600"
+ fx="67.478638"
+ cy="113.72600"
+ cx="67.478638" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27928"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27930"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27932"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.034243e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27934"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27918"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27920"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27922"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.034058e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27924"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27908"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27910"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27912"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.034978e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27914"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27898"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27900"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27902"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.034834e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27904"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27888"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27890"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27892"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.034849e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27894"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27878"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27880"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27882"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.034830e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27884"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27868"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27870"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27872"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.034397e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27874"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27858"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27860"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27862"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.034792e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27864"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27848"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27850"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27852"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.031398e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27854"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27838"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27840"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27842"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.031213e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27844"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27828"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27830"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27832"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.032133e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27834"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27818"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27820"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27822"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.031989e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27824"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27808"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27810"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27812"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.032004e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27814"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27798"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27800"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27802"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.031985e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27804"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27788"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27790"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27792"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.031552e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27794"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27778"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27780"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27782"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.031947e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27784"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27768"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27770"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27772"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.034458e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27774"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27758"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27760"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27762"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.034993e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27764"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27748"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27750"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27752"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.034041e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27754"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27738"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27740"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27742"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.033897e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27744"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27728"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27730"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27732"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.033912e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27734"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27718"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27720"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27722"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.033893e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27724"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27708"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27710"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27712"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.033460e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27714"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27698"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27700"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27702"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.034431e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27704"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.146240"
+ fx="-75.268890"
+ cy="84.146240"
+ cx="-75.268890" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28432"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-126.1386,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28434"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-286.3092,761.0352)"
+ fy="113.72600"
+ fx="97.536598"
+ cy="113.72600"
+ cx="97.536598" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28428"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-121.7938,817.6604)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28430"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-286.3092,761.0352)"
+ fy="113.72600"
+ fx="100.67591"
+ cy="113.72600"
+ cx="100.67591" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28424"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-117.1911,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28426"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-286.3092,761.0352)"
+ fy="113.72600"
+ fx="104.00187"
+ cy="113.72600"
+ cx="104.00187" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28420"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-112.8462,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28422"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-286.3092,761.0352)"
+ fy="113.72600"
+ fx="107.14119"
+ cy="113.72600"
+ cx="107.14119" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28416"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-108.7032,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28418"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-286.3092,761.0352)"
+ fy="113.72600"
+ fx="110.13468"
+ cy="113.72600"
+ cx="110.13468" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28412"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-104.3583,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28414"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-286.3092,761.0352)"
+ fy="113.72600"
+ fx="113.27399"
+ cy="113.72600"
+ cx="113.27399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28408"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-99.99876,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28410"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-286.3092,761.0352)"
+ fy="113.72600"
+ fx="116.42374"
+ cy="113.72600"
+ cx="116.42374" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28404"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-95.65386,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28406"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-286.3092,761.0352)"
+ fy="113.72600"
+ fx="119.56305"
+ cy="113.72600"
+ cx="119.56305" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28400"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-161.4583,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28402"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.5677,761.0352)"
+ fy="113.72600"
+ fx="71.480988"
+ cy="113.72600"
+ cx="71.480988" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28396"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-157.1135,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28398"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.5677,761.0352)"
+ fy="113.72600"
+ fx="74.620308"
+ cy="113.72600"
+ cx="74.620308" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28392"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-152.5108,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28394"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.5677,761.0352)"
+ fy="113.72600"
+ fx="77.946259"
+ cy="113.72600"
+ cx="77.946259" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28388"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-148.1659,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28390"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.5677,761.0352)"
+ fy="113.72600"
+ fx="81.085587"
+ cy="113.72600"
+ cx="81.085587" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28384"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-144.0229,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28386"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.5677,761.0352)"
+ fy="113.72600"
+ fx="84.079071"
+ cy="113.72600"
+ cx="84.079071" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28380"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-139.6781,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28382"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.5677,761.0352)"
+ fy="113.72600"
+ fx="87.218399"
+ cy="113.72600"
+ cx="87.218399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28376"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-135.3188,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28378"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.5677,761.0352)"
+ fy="113.72600"
+ fx="90.368126"
+ cy="113.72600"
+ cx="90.368126" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28372"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-130.9739,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28374"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.5677,761.0352)"
+ fy="113.72600"
+ fx="93.507462"
+ cy="113.72600"
+ cx="93.507462" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28368"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-197.4824,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28370"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.5677,761.0352)"
+ fy="113.72600"
+ fx="45.452175"
+ cy="113.72600"
+ cx="45.452175" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28364"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-193.1376,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28366"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.5677,761.0352)"
+ fy="113.72600"
+ fx="48.591496"
+ cy="113.72600"
+ cx="48.591496" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28360"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-188.5349,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28362"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.5677,761.0352)"
+ fy="113.72600"
+ fx="51.917450"
+ cy="113.72600"
+ cx="51.917450" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28356"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-184.1900,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28358"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.5677,761.0352)"
+ fy="113.72600"
+ fx="55.056770"
+ cy="113.72600"
+ cx="55.056770" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28352"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-180.0470,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28354"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.5677,761.0352)"
+ fy="113.72600"
+ fx="58.050255"
+ cy="113.72600"
+ cx="58.050255" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28348"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-175.7021,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28350"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.5677,761.0352)"
+ fy="113.72600"
+ fx="61.189575"
+ cy="113.72600"
+ cx="61.189575" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28344"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-171.3428,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28346"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.5677,761.0352)"
+ fy="113.72600"
+ fx="64.339317"
+ cy="113.72600"
+ cx="64.339317" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28340"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-166.9979,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28342"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0.000000,0.000000,0.656868,-285.5677,761.0352)"
+ fy="113.72600"
+ fx="67.478638"
+ cy="113.72600"
+ cx="67.478638" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28438"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0.000000,0.000000,0.180797,-90.25863,817.7848)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12759"
+ x2="8.3977861"
+ x1="-35.945030"
+ id="linearGradient36281"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0.000000,0.000000,0.399114,-149.8970,802.9053)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36283"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36285"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.025564e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36287"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36289"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.025379e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36291"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36293"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.025424e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36295"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36297"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.025375e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36299"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36301"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.025318e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36303"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36305"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.025343e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36307"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36309"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.031109e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36311"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36313"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.030924e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36315"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36317"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.030969e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36319"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36321"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.030920e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36323"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36325"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.030863e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36327"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36329"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.030888e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36331"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-187.5348,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36333"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-185.7196,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36335"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-189.3500,879.6484)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36337"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-180.2740,879.6484)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36339"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-183.9043,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36341"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-182.0892,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient21825"
+ x2="8.3977861"
+ x1="-35.945030"
+ id="linearGradient36343"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0.000000,0.000000,0.399114,-149.6637,784.9089)" />
+ <linearGradient
+ y2="1977.8738"
+ y1="1924.0137"
+ xlink:href="#linearGradient41493"
+ x2="-35.763195"
+ x1="-39.828941"
+ id="linearGradient36345"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.672454,0.000000,0.000000,0.374188,-7.912301,167.4787)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36347"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-187.5296,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36349"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-185.7144,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36351"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-189.3448,881.7646)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36353"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-180.2688,881.7646)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36355"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-183.8991,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36357"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-182.0840,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36359"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-198.4916,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36361"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-190.0460,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36363"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-187.2306,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36365"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-192.8611,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36367"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-195.6763,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36369"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.742720,0.000000,0.000000,0.445632,-87.12747,420.4818)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36371"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36373"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.028536e-10)" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36375"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.787283,0.000000,0.000000,0.475341,-91.66274,388.2275)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36377"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36379"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.027980e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36381"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.270019,0.000000,0.000000,0.370779,-153.7879,864.7564)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12759"
+ x2="8.3977861"
+ x1="-35.945030"
+ id="linearGradient35867"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0.000000,0.000000,0.399114,-141.9847,635.4266)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35869"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35871"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.025486e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35873"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35875"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.025301e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35877"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35879"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.025346e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35881"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35883"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.025297e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35885"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35887"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.025240e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35889"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35891"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.025265e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35893"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35895"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.024517e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35897"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35899"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.024373e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35901"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35903"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.024388e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35905"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35907"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.024369e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35909"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35911"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.035110e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35913"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35915"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.024331e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35917"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35919"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.031174e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35921"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35923"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.030989e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35925"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35927"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.031034e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35929"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35931"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.030985e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35933"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35935"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.030928e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35937"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35939"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.030953e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35941"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35943"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.023950e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35945"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35947"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.023806e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35949"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35951"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.023821e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35953"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35955"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.023802e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35957"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35959"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.023764e-10)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35961"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35963"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.023379e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35965"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-179.6225,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35967"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-168.7312,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35969"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-163.2856,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35971"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-161.4702,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35973"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-165.1007,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35975"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-177.8073,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35977"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-166.9159,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35979"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-181.4377,712.1697)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35981"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-170.5465,712.1697)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35983"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-172.3617,712.1697)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35985"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-175.9920,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35987"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-174.1769,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient21825"
+ x2="8.3977861"
+ x1="-35.945030"
+ id="linearGradient35989"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0.000000,0.000000,0.399114,-141.7514,617.4302)" />
+ <linearGradient
+ y2="1977.8738"
+ y1="1924.0137"
+ xlink:href="#linearGradient41493"
+ x2="-35.763195"
+ x1="-39.828941"
+ id="linearGradient35991"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(2.672454,0.374188)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35993"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-179.6173,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35995"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-168.7260,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35997"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-163.2804,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35999"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-161.4650,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36001"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-165.0955,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36003"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-177.8021,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36005"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-166.9107,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36007"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-181.4325,714.2859)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36009"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-170.5413,714.2859)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36011"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-172.3565,714.2859)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36013"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-175.9868,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36015"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-174.1717,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36017"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-190.5793,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36019"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-182.1337,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36021"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-179.3183,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36023"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-184.9488,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36025"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0.000000,0.000000,0.194625,-187.7640,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36027"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.742720,0.000000,0.000000,0.445632,-79.21517,253.0031)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36029"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36031"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.028790e-10)" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36033"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.787283,0.000000,0.000000,0.475341,-83.75044,220.7488)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36035"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0.000000,0.000000,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36037"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0.000000,0.000000,0.916806,6.735873,-1.028234e-10)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36039"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.270019,0.000000,0.000000,0.370779,-145.8756,697.2777)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient13376"
+ r="31.620827"
+ id="radialGradient12151"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.20227,0,0,0.454077,6.691668,-148.3193)"
+ fy="254.35735"
+ fx="-19.038713"
+ cy="253.63734"
+ cx="-19.261518" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12744"
+ x2="8.3977861"
+ x1="-35.945030"
+ id="linearGradient12153"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,1.691668,-145.8193)" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient14835"
+ x2="8.3977861"
+ x1="-35.945030"
+ id="linearGradient12155"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,1.924904,-161.8157)" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12744"
+ x2="8.3977861"
+ x1="-35.945030"
+ id="linearGradient12157"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.350818,114.6621,-134.6472)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12159"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-59.65453)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12161"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-61.33423)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12163"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-63.01391)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12165"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.09869,-64.40064)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient13376"
+ id="radialGradient16585"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.20227,0,0,0.454077,6.691668,-148.3193)"
+ cx="-19.261518"
+ cy="253.63734"
+ fx="-19.038713"
+ fy="254.35735"
+ r="31.620827" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient12744"
+ id="linearGradient16587"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,1.691668,-145.8193)"
+ x1="-35.945030"
+ y1="233.36613"
+ x2="8.3977861"
+ y2="275.81308" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient14835"
+ id="linearGradient16589"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,1.924904,-161.8157)"
+ x1="-35.945030"
+ y1="233.36613"
+ x2="8.3977861"
+ y2="275.81308" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient12744"
+ id="linearGradient16591"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.350818,114.6621,-134.6472)"
+ x1="-35.945030"
+ y1="233.36613"
+ x2="8.3977861"
+ y2="275.81308" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient14160"
+ id="radialGradient16593"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-59.65453)"
+ cx="-66.099426"
+ cy="99.988457"
+ fx="-66.099426"
+ fy="99.988457"
+ r="2.0070677" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient14160"
+ id="radialGradient16595"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-61.33423)"
+ cx="-66.099426"
+ cy="99.988457"
+ fx="-66.099426"
+ fy="99.988457"
+ r="2.0070677" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient14160"
+ id="radialGradient16597"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-63.01391)"
+ cx="-66.099426"
+ cy="99.988457"
+ fx="-66.099426"
+ fy="99.988457"
+ r="2.0070677" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient14160"
+ id="radialGradient16599"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.09869,-64.40064)"
+ cx="-66.099426"
+ cy="99.988457"
+ fx="-66.099426"
+ fy="99.988457"
+ r="2.0070677" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient13376"
+ id="radialGradient3109"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.20227,0,0,0.454077,6.691668,-148.3193)"
+ cx="-19.261518"
+ cy="253.63734"
+ fx="-19.038713"
+ fy="254.35735"
+ r="31.620827" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient12744"
+ id="linearGradient3111"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,1.691668,-145.8193)"
+ x1="-35.945030"
+ y1="233.36613"
+ x2="8.3977861"
+ y2="275.81308" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient14835"
+ id="linearGradient3113"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,1.924904,-161.8157)"
+ x1="-35.945030"
+ y1="233.36613"
+ x2="8.3977861"
+ y2="275.81308" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient12744"
+ id="linearGradient3115"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.350818,114.6621,-134.6472)"
+ x1="-35.945030"
+ y1="233.36613"
+ x2="8.3977861"
+ y2="275.81308" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient14160"
+ id="radialGradient3117"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-59.65453)"
+ cx="-66.099426"
+ cy="99.988457"
+ fx="-66.099426"
+ fy="99.988457"
+ r="2.0070677" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient14160"
+ id="radialGradient3119"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-61.33423)"
+ cx="-66.099426"
+ cy="99.988457"
+ fx="-66.099426"
+ fy="99.988457"
+ r="2.0070677" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient14160"
+ id="radialGradient3121"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-63.01391)"
+ cx="-66.099426"
+ cy="99.988457"
+ fx="-66.099426"
+ fy="99.988457"
+ r="2.0070677" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient14160"
+ id="radialGradient3123"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.09869,-64.40064)"
+ cx="-66.099426"
+ cy="99.988457"
+ fx="-66.099426"
+ fy="99.988457"
+ r="2.0070677" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient13376"
+ id="radialGradient3239"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.20227,0,0,0.454077,6.691668,-148.3193)"
+ cx="-19.261518"
+ cy="253.63734"
+ fx="-19.038713"
+ fy="254.35735"
+ r="31.620827" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient12744"
+ id="linearGradient3241"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,1.691668,-145.8193)"
+ x1="-35.945030"
+ y1="233.36613"
+ x2="8.3977861"
+ y2="275.81308" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient14835"
+ id="linearGradient3243"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,1.924904,-161.8157)"
+ x1="-35.945030"
+ y1="233.36613"
+ x2="8.3977861"
+ y2="275.81308" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient12744"
+ id="linearGradient3245"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.350818,114.6621,-134.6472)"
+ x1="-35.945030"
+ y1="233.36613"
+ x2="8.3977861"
+ y2="275.81308" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient14160"
+ id="radialGradient3247"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-59.65453)"
+ cx="-66.099426"
+ cy="99.988457"
+ fx="-66.099426"
+ fy="99.988457"
+ r="2.0070677" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient14160"
+ id="radialGradient3249"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-61.33423)"
+ cx="-66.099426"
+ cy="99.988457"
+ fx="-66.099426"
+ fy="99.988457"
+ r="2.0070677" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient14160"
+ id="radialGradient3251"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-63.01391)"
+ cx="-66.099426"
+ cy="99.988457"
+ fx="-66.099426"
+ fy="99.988457"
+ r="2.0070677" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient14160"
+ id="radialGradient3253"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.09869,-64.40064)"
+ cx="-66.099426"
+ cy="99.988457"
+ fx="-66.099426"
+ fy="99.988457"
+ r="2.0070677" />
+ </defs>
+ <sodipodi:namedview
+ bordercolor="#666666"
+ borderopacity="1.0"
+ id="base"
+ inkscape:current-layer="layer1"
+ inkscape:cx="495.44191"
+ inkscape:cy="375.2641"
+ inkscape:document-units="px"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:window-height="953"
+ inkscape:window-width="1280"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:zoom="1"
+ pagecolor="#ffffff"
+ width="1052.3622px"
+ height="744.09448px" />
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1">
+ <g
+ id="g5278"
+ transform="matrix(0.1267968,0,0,0.1710106,100.15833,410.75325)">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path569"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z " />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path568"
+ d="M 495.64613 253.85521 A 234.95641 236.07524 0 1 1 25.733322,253.85521 A 234.95641 236.07524 0 1 1 495.64613 253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path566"
+ d="M 495.64613 253.85521 A 234.95641 236.07524 0 1 1 25.733322,253.85521 A 234.95641 236.07524 0 1 1 495.64613 253.85521 z" />
+ </g>
+ <g
+ id="g12726"
+ transform="matrix(1.2500391,0,0,1.184913,99.819149,206.94272)">
+ <g
+ transform="translate(152.9277,120.7469)"
+ id="g26712">
+ <path
+ style="fill:url(#radialGradient12151);fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ id="path12747"
+ d="M 24.972918,-30.66305 C 24.972918,-22.90055 -16.207082,-20.35055 -53.230207,-20.66305 C -90.878332,-20.66305 -92.370832,-26.65055 -92.370832,-34.41305 C -92.370832,-42.17555 -71.190832,-40.03805 -33.542707,-40.03805 C 4.105418,-40.03805 24.972918,-38.42555 24.972918,-30.66305 z " />
+ <path
+ style="fill:url(#linearGradient12153);fill-opacity:1;stroke:#677883;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ id="rect8945"
+ d="M -88.933334,-50.694302 L 23.566666,-50.694302 L 23.566666,-37.18304 L -88.933334,-37.18304 L -88.933334,-50.694302 z " />
+ <path
+ style="fill:url(#linearGradient12155);fill-opacity:1;stroke:#677883;stroke-width:0.49944988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ id="rect14210"
+ d="M -75.933161,-61.840606 L 36.319422,-61.840606 L 23.751782,-51.190703 L -88.500801,-51.190703 L -75.933161,-61.840606 z " />
+ <path
+ style="fill:url(#linearGradient12157);fill-opacity:1;stroke:#677883;stroke-width:0.46877259;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ id="rect14838"
+ d="M 23.981782,-50.846435 L 36.765614,-61.588097 L 36.752498,-48.186223 L 24.037025,-37.40884 L 23.981782,-50.846435 z " />
+ <rect
+ y="-40.584919"
+ x="-86.589584"
+ width="1.5625"
+ style="fill:url(#radialGradient12159);fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ rx="0"
+ id="rect14843"
+ height="0.78125" />
+ <rect
+ y="-42.264622"
+ x="-86.589584"
+ width="1.5625"
+ style="fill:url(#radialGradient12161);fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ rx="0"
+ id="rect14845"
+ height="0.78125" />
+ <rect
+ y="-43.944294"
+ x="-86.589584"
+ width="1.5625"
+ style="fill:url(#radialGradient12163);fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ rx="0"
+ id="rect14847"
+ height="0.78125" />
+ <rect
+ y="-45.33102"
+ x="-86.609116"
+ width="1.5625"
+ style="fill:url(#radialGradient12165);fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ rx="0"
+ id="rect14849"
+ height="0.78125" />
+ </g>
+ </g>
+ <g
+ id="g12774"
+ transform="matrix(0.1881701,0,0,0.2844466,210.77219,75.336794)">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path12776"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z " />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12778"
+ d="M 495.64613 253.85521 A 234.95641 236.07524 0 1 1 25.733322,253.85521 A 234.95641 236.07524 0 1 1 495.64613 253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12780"
+ d="M 495.64613 253.85521 A 234.95641 236.07524 0 1 1 25.733322,253.85521 A 234.95641 236.07524 0 1 1 495.64613 253.85521 z" />
+ </g>
+ <g
+ id="g12782"
+ transform="matrix(0.1881701,0,0,0.2844466,707.44948,77.500676)">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path12784"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z " />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12786"
+ d="M 495.64613 253.85521 A 234.95641 236.07524 0 1 1 25.733322,253.85521 A 234.95641 236.07524 0 1 1 495.64613 253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12788"
+ d="M 495.64613 253.85521 A 234.95641 236.07524 0 1 1 25.733322,253.85521 A 234.95641 236.07524 0 1 1 495.64613 253.85521 z" />
+ </g>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot12862"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial"
+ transform="translate(-143.44166,-193.94928)"><flowRegion
+ id="flowRegion12864"><rect
+ id="rect12866"
+ width="157.14285"
+ height="40"
+ x="194.28572"
+ y="475.52304"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara23709">Load Balancer</flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot12890"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr;text-anchor:start;font-family:Arial"
+ transform="translate(-12.992469,-434.27533)"><flowRegion
+ id="flowRegion12892"><rect
+ id="rect12894"
+ width="157.14285"
+ height="40"
+ x="194.28572"
+ y="475.52304"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara12898">Data Center A</flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot12900"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr;text-anchor:start;font-family:Arial"
+ transform="translate(486.55966,-436.47728)"><flowRegion
+ id="flowRegion12902"><rect
+ id="rect12904"
+ width="157.14285"
+ height="40"
+ x="194.28572"
+ y="475.52304"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara12908">Data Center B</flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot12976"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial"
+ transform="translate(667.90714,-192.88096)"><flowRegion
+ id="flowRegion12978"><rect
+ id="rect12980"
+ width="157.14285"
+ height="40"
+ x="194.28572"
+ y="475.52304"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara23707">Load Balancer</flowPara></flowRoot> <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:3,3;stroke-dashoffset:0"
+ d="M 519.21841,49.109532 L 519.21841,687.52594"
+ id="path14553" />
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot15524"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial"
+ transform="translate(-105.05587,-187.88838)"><flowRegion
+ id="flowRegion15526"><rect
+ id="rect15528"
+ width="129.29955"
+ height="26.263966"
+ x="137.38075"
+ y="681.46503"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara15542">Consumer Pool</flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot15534"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial"
+ transform="translate(781.35941,-209.327)"><flowRegion
+ id="flowRegion15536"><rect
+ id="rect15538"
+ width="129.29955"
+ height="26.263966"
+ x="137.38075"
+ y="681.46503"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara15544">Consumer Pool</flowPara></flowRoot> <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.71494228px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 254.55844,186.23712 L 254.55844,261.49474"
+ id="path16515" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.10873353px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 299.05951,113.93024 L 711.09302,176.21781"
+ id="path16519" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.03299749px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 301.04196,179.66157 L 715.17151,118.56769"
+ id="path16521" />
+ <g
+ id="g16529"
+ transform="matrix(1.2344541,0,0,1.166142,100.35599,485.83269)">
+ <g
+ transform="translate(152.9277,120.7469)"
+ id="g16531">
+ <path
+ style="fill:url(#radialGradient16585);fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ id="path16533"
+ d="M 24.972918,-30.66305 C 24.972918,-22.90055 -16.207082,-20.35055 -53.230207,-20.66305 C -90.878332,-20.66305 -92.370832,-26.65055 -92.370832,-34.41305 C -92.370832,-42.17555 -71.190832,-40.03805 -33.542707,-40.03805 C 4.105418,-40.03805 24.972918,-38.42555 24.972918,-30.66305 z " />
+ <path
+ style="fill:url(#linearGradient16587);fill-opacity:1;stroke:#677883;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ id="path16535"
+ d="M -88.933334,-50.694302 L 23.566666,-50.694302 L 23.566666,-37.18304 L -88.933334,-37.18304 L -88.933334,-50.694302 z " />
+ <path
+ style="fill:url(#linearGradient16589);fill-opacity:1;stroke:#677883;stroke-width:0.49944988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ id="path16537"
+ d="M -75.933161,-61.840606 L 36.319422,-61.840606 L 23.751782,-51.190703 L -88.500801,-51.190703 L -75.933161,-61.840606 z " />
+ <path
+ style="fill:url(#linearGradient16591);fill-opacity:1;stroke:#677883;stroke-width:0.46877259;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ id="path16539"
+ d="M 23.981782,-50.846435 L 36.765614,-61.588097 L 36.752498,-48.186223 L 24.037025,-37.40884 L 23.981782,-50.846435 z " />
+ <rect
+ y="-40.584919"
+ x="-86.589584"
+ width="1.5625"
+ style="fill:url(#radialGradient16593);fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ rx="0"
+ id="rect16541"
+ height="0.78125" />
+ <rect
+ y="-42.264622"
+ x="-86.589584"
+ width="1.5625"
+ style="fill:url(#radialGradient16595);fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ rx="0"
+ id="rect16543"
+ height="0.78125" />
+ <rect
+ y="-43.944294"
+ x="-86.589584"
+ width="1.5625"
+ style="fill:url(#radialGradient16597);fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ rx="0"
+ id="rect16545"
+ height="0.78125" />
+ <rect
+ y="-45.33102"
+ x="-86.609116"
+ width="1.5625"
+ style="fill:url(#radialGradient16599);fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ rx="0"
+ id="rect16547"
+ height="0.78125" />
+ </g>
+ </g>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot16549"
+ style="font-size:17.99999953px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr;text-anchor:start;font-family:Arial"
+ transform="matrix(0.7525038,0,0,0.6775389,64.79681,269.22814)"><flowRegion
+ id="flowRegion16551"><rect
+ id="rect16553"
+ width="157.14285"
+ height="40"
+ x="194.28572"
+ y="475.52304"
+ style="font-size:17.99999953px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara23713">Load Balancer</flowPara></flowRoot> <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.22766685px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 273.86518,195.76282 L 748.4092,262.07055"
+ id="path21761" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.10209382px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 273.80239,258.50247 L 744.43138,197.3106"
+ id="path21763" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.76721847px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 757.61441,187.27341 L 757.61441,274.60058"
+ id="path23705" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend)"
+ d="M 131.31983,412.76445 L 238.396,364.27713"
+ id="path23715" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.03078127px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 277.57943,366.85652 L 399.22276,412.20536"
+ id="path25655" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow1Lstart);marker-end:url(#Arrow1Lend)"
+ d="M 258.59905,410.74414 L 258.59905,356.19591"
+ id="path25659" />
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot27609"
+ style="font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;font-size:18px;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%"><flowRegion
+ id="flowRegion27611"><rect
+ id="rect27613"
+ width="105.05586"
+ height="34.345188"
+ x="96.974648"
+ y="113.75929"
+ style="font-family:Arial;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;font-size:18px;text-anchor:start;text-align:start;writing-mode:lr;line-height:125%" /></flowRegion><flowPara
+ id="flowPara27617">Mirror mode 1</flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot27619"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr;text-anchor:start;font-family:Arial"
+ transform="translate(750.17468,-1.4171474)"><flowRegion
+ id="flowRegion27621"><rect
+ id="rect27623"
+ width="105.05586"
+ height="34.345188"
+ x="96.974648"
+ y="113.75929"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara27627">Mirror mode 2</flowPara></flowRoot> <path
+ d="M 103.99577,648.52356 C 103.99577,648.52356 103.75488,628.78956 123.98963,634.70975 C 144.22438,640.62995 140.61103,650.49696 140.85192,650.49696 C 141.09281,650.49696 137.23858,638.37464 152.65553,636.11932 C 166.62714,637.52889 173.13117,640.62995 172.4085,651.06079 C 171.68583,661.49163 164.94091,664.02886 164.94091,664.02886 C 164.94091,664.02886 177.2263,665.43843 173.85384,686.58202 C 168.55426,701.24157 168.55426,699.832 156.26888,701.52349 C 150.72841,698.98626 150.48752,696.73094 150.48752,696.73094 C 150.48752,696.73094 160.60489,707.72561 145.42883,714.77347 C 128.32565,720.12985 126.15764,715.3373 118.93094,712.80007 C 111.94514,707.72561 113.39048,701.52349 113.39048,701.52349 C 113.39048,701.52349 117.7265,711.95432 103.03221,713.92773 C 88.337924,715.90113 82.797457,711.95432 80.147668,695.8852 C 79.424999,683.76287 92.433052,682.63522 92.433052,682.63522 C 92.433052,682.63522 84.483686,682.91713 82.315677,676.71501 C 80.147668,670.51289 78.702329,662.33737 85.206355,651.3427 C 97.25085,638.09272 105.44111,646.83207 103.99577,648.52356 z "
+ id="path1503"
+ sodipodi:nodetypes="cccccccccccccccccc"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.78178847;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g1556"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(5.9849059e-2,0,0,6.3887019e-2,92.207875,643.07667)">
+ <g
+ id="g770"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(107.0886,1.392441)">
+ <path
+ d="M 299.842,180.38 L 177.215,74.367 L 250.791,71.9936 L 385.285,162.183 C 385.285,162.183 353.639,191.456 299.842,180.38 z "
+ id="path743"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b3b2b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g761"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 176.424,74.367 L 178.006,238.924 L 300.633,364.715 L 301.424,181.962 L 176.424,74.367 z "
+ id="path744"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b3b2b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g754"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 302.216,181.171 C 302.216,181.171 328.323,184.335 348.102,180.38 C 375,170.095 386.076,163.766 386.076,163.766 L 386.867,342.563 C 386.867,342.563 371.835,353.639 353.64,360.759 C 323.576,367.088 300.633,364.715 300.633,364.715 L 302.216,181.171 z "
+ id="path742"
+ sodipodi:nodetypes="ccccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g749"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 315.665,205.696 C 315.665,205.696 378.956,192.247 378.956,193.038 C 378.956,193.829 378.165,218.354 378.165,218.354 C 378.165,218.354 316.456,231.804 315.665,231.804 C 314.874,231.804 314.874,207.278 315.665,205.696 z "
+ id="path745"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(-1.58226,0)" />
+ <path
+ d="M 323.576,246.835 L 370.253,237.342 L 370.253,241.298 L 323.576,251.582 L 323.576,246.835 z "
+ id="path746"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#000100;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 360.76,268.987 L 372.627,267.405 L 372.627,279.272 L 360.76,281.646 L 360.76,268.987 z "
+ id="path747"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#00b300;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 365.506,298.259 L 373.418,297.468 L 373.418,306.962 L 365.506,308.544 L 365.506,298.259 z "
+ id="path748"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+ </g>
+ <g
+ id="g818"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(0.868723,0,0,0.841809,-27.91207,15.52193)">
+ <g
+ id="g801"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 119.557,425.383 L 119.557,438.102 L 435.832,344.83 L 436.68,329.568 L 119.557,425.383 z "
+ id="path607"
+ sodipodi:nodetypes="ccccc"
+ style="font-size:12px;fill:#b3b3b3;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(-0.847921,19.50222)" />
+ <g
+ id="g798"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 166.193,347.374 L 119.557,426.231 L 436.68,329.568 L 345.104,298.194 L 166.193,347.374 z "
+ id="path606"
+ sodipodi:nodetypes="ccccc"
+ style="font-size:12px;fill:#cccccc;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(-0.847921,19.50222)" />
+ <path
+ d="M 139.059,335.503 C 139.059,335.503 127.188,354.157 132.275,369.42 C 136.515,388.075 150.082,392.314 153.474,392.314 C 156.866,392.314 377.177,334.884 377.177,334.884 C 377.177,334.884 383.26,322.784 386.652,303.282 C 379.869,279.54 367.15,276.996 352.735,276.996 C 328.993,263.43 353.584,276.996 353.584,276.996 L 139.059,335.503 z "
+ id="path605"
+ sodipodi:nodetypes="cccccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(0.708121,0,0,0.825311,71.30738,59.20586)" />
+ </g>
+ </g>
+ <g
+ id="g811"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 420.57,16.6844 C 420.57,17.5323 399.371,11.5968 377.325,9.90093 C 367.15,10.7489 66.1379,65.8638 63.5942,65.8638 C 67.8338,65.0159 54.267,65.8638 50.8753,73.4951 C 47.4836,81.1264 47.4836,343.134 47.4836,343.134 C 47.4836,343.134 47.4837,350.766 52.5712,356.701 C 60.2025,360.093 80.5526,360.941 72.9213,360.941 L 420.57,16.6844 z "
+ id="path602"
+ sodipodi:nodetypes="cccccccc"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g806"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 71.2332,108.009 L 418.873,40.4261 C 423.961,43.8178 429.049,41.2741 431.593,51.4492 C 434.137,61.6241 430.745,304.978 430.745,304.978 C 430.745,304.978 432.44,312.609 426.505,316.849 C 420.569,321.088 79.6699,416.904 79.6699,416.904 C 79.6699,416.904 69.4946,421.143 63.5593,412.664 C 55.0803,410.121 60.2025,124.37 60.2025,124.37 C 60.2025,124.37 61.0271,110.72 71.2332,108.009 z "
+ id="path600"
+ sodipodi:nodetypes="ccccccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(0.995445,0,0,0.910163,2.817975,-20.11005)" />
+ <g
+ id="g795"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 89.0318,98.0844 L 394.284,38.7303 C 394.284,38.7303 401.915,36.1865 407.851,45.5137 C 413.786,54.8408 412.09,59.0804 412.09,59.0804 L 412.09,234.6 C 411.242,243.927 412.938,240.535 407.851,250.711 C 397.675,254.102 105.142,333.807 105.142,333.807 C 105.142,333.807 105.142,335.503 94.9673,333.807 C 84.7922,332.111 81.4005,316.001 81.4005,315.153 C 81.4005,314.305 78.0088,115.891 78.0088,115.891 C 78.0088,115.891 79.7048,101.476 89.0318,98.0844 z "
+ id="path603"
+ sodipodi:nodetypes="ccccccccccc"
+ style="fill:url(#linearGradient614);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 368.846,266.821 L 398.523,258.342 L 398.523,271.909 C 398.523,271.909 368.846,279.54 368.846,280.388 C 368.846,281.236 368.846,266.821 368.846,266.821 z "
+ id="path604"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#00feb3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+ </g>
+ <g
+ id="g878"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(1.060794,0,0,0.913679,-36.3605,196.9337)">
+ <g
+ id="g875"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 35.6013,306.962 L 454.114,242.088 L 454.114,266.614 L 35.6012,332.279 L 35.6013,306.962 z "
+ id="path831"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b2b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(3.955698,-6.329117)" />
+ <path
+ d="M 39.557,300.633 C 41.9304,296.677 90.981,199.367 90.981,199.367 L 371.044,173.259 L 458.07,235.759 L 39.557,300.633 z "
+ id="path832"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g859"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <g
+ id="g844"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 331.487,184.335 C 331.487,184.335 332.279,193.038 332.279,193.829 C 332.279,194.62 366.298,234.177 366.298,234.177 L 424.051,224.683 L 423.26,218.354 L 422.468,218.354 L 331.487,184.335 z "
+ id="path841"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 332.279,184.336 L 369.462,180.38 L 424.051,218.354 L 370.253,225.475 L 332.279,184.336 z "
+ id="path840"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g847"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 297.468,195.411 C 297.468,195.411 297.468,203.323 297.468,204.114 C 297.468,204.905 318.038,241.297 318.829,241.297 C 319.62,241.297 353.639,236.551 353.639,236.551 L 352.057,227.057"
+ id="path843"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 298.26,195.411 C 299.051,195.411 322.785,193.038 322.785,193.038 L 352.848,227.057 L 321.994,231.804 L 298.26,195.411 z "
+ id="path842"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g850"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 90.1899,208.861 L 98.1013,195.411 L 272.943,178.006 L 282.437,189.082 L 90.1899,208.861 z "
+ id="path833"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 90.1899,209.652 L 89.3988,215.19 L 282.437,194.62 L 282.437,188.291 L 90.1899,209.652 z "
+ id="path834"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g853"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 90.981,215.981 L 287.184,194.62 L 309.336,229.43 L 71.9936,261.867 L 90.981,215.981 z "
+ id="path835"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 71.9937,261.076 L 71.2026,268.987 L 309.336,236.551 C 310.127,236.551 308.544,230.221 308.544,229.43 C 308.544,228.639 71.9937,263.449 71.9937,261.076 z "
+ id="path836"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g856"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 82.2785,274.525 L 301.424,242.089 L 306.171,250.791 L 79.9051,284.019 L 82.2785,274.525 z "
+ id="path838"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 79.1139,284.81 L 79.1139,291.93 L 306.171,255.538 L 305.38,250 L 79.1139,284.81 z "
+ id="path839"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+ </g>
+ <g
+ id="g1647"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(5.9849059e-2,0,0,6.3887019e-2,135.04094,644.20433)">
+ <g
+ id="g1648"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(107.0886,1.392441)">
+ <path
+ d="M 299.842,180.38 L 177.215,74.367 L 250.791,71.9936 L 385.285,162.183 C 385.285,162.183 353.639,191.456 299.842,180.38 z "
+ id="path1649"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b3b2b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g1650"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 176.424,74.367 L 178.006,238.924 L 300.633,364.715 L 301.424,181.962 L 176.424,74.367 z "
+ id="path1651"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b3b2b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g1652"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 302.216,181.171 C 302.216,181.171 328.323,184.335 348.102,180.38 C 375,170.095 386.076,163.766 386.076,163.766 L 386.867,342.563 C 386.867,342.563 371.835,353.639 353.64,360.759 C 323.576,367.088 300.633,364.715 300.633,364.715 L 302.216,181.171 z "
+ id="path1653"
+ sodipodi:nodetypes="ccccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g1654"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 315.665,205.696 C 315.665,205.696 378.956,192.247 378.956,193.038 C 378.956,193.829 378.165,218.354 378.165,218.354 C 378.165,218.354 316.456,231.804 315.665,231.804 C 314.874,231.804 314.874,207.278 315.665,205.696 z "
+ id="path1655"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(-1.58226,0)" />
+ <path
+ d="M 323.576,246.835 L 370.253,237.342 L 370.253,241.298 L 323.576,251.582 L 323.576,246.835 z "
+ id="path1656"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#000100;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 360.76,268.987 L 372.627,267.405 L 372.627,279.272 L 360.76,281.646 L 360.76,268.987 z "
+ id="path1657"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#00b300;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 365.506,298.259 L 373.418,297.468 L 373.418,306.962 L 365.506,308.544 L 365.506,298.259 z "
+ id="path1658"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+ </g>
+ <g
+ id="g1659"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(0.868723,0,0,0.841809,-27.91207,15.52193)">
+ <g
+ id="g1660"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 119.557,425.383 L 119.557,438.102 L 435.832,344.83 L 436.68,329.568 L 119.557,425.383 z "
+ id="path1661"
+ sodipodi:nodetypes="ccccc"
+ style="font-size:12px;fill:#b3b3b3;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(-0.847921,19.50222)" />
+ <g
+ id="g1662"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 166.193,347.374 L 119.557,426.231 L 436.68,329.568 L 345.104,298.194 L 166.193,347.374 z "
+ id="path1663"
+ sodipodi:nodetypes="ccccc"
+ style="font-size:12px;fill:#cccccc;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(-0.847921,19.50222)" />
+ <path
+ d="M 139.059,335.503 C 139.059,335.503 127.188,354.157 132.275,369.42 C 136.515,388.075 150.082,392.314 153.474,392.314 C 156.866,392.314 377.177,334.884 377.177,334.884 C 377.177,334.884 383.26,322.784 386.652,303.282 C 379.869,279.54 367.15,276.996 352.735,276.996 C 328.993,263.43 353.584,276.996 353.584,276.996 L 139.059,335.503 z "
+ id="path1664"
+ sodipodi:nodetypes="cccccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(0.708121,0,0,0.825311,71.30738,59.20586)" />
+ </g>
+ </g>
+ <g
+ id="g1665"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 420.57,16.6844 C 420.57,17.5323 399.371,11.5968 377.325,9.90093 C 367.15,10.7489 66.1379,65.8638 63.5942,65.8638 C 67.8338,65.0159 54.267,65.8638 50.8753,73.4951 C 47.4836,81.1264 47.4836,343.134 47.4836,343.134 C 47.4836,343.134 47.4837,350.766 52.5712,356.701 C 60.2025,360.093 80.5526,360.941 72.9213,360.941 L 420.57,16.6844 z "
+ id="path1666"
+ sodipodi:nodetypes="cccccccc"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g1667"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 71.2332,108.009 L 418.873,40.4261 C 423.961,43.8178 429.049,41.2741 431.593,51.4492 C 434.137,61.6241 430.745,304.978 430.745,304.978 C 430.745,304.978 432.44,312.609 426.505,316.849 C 420.569,321.088 79.6699,416.904 79.6699,416.904 C 79.6699,416.904 69.4946,421.143 63.5593,412.664 C 55.0803,410.121 60.2025,124.37 60.2025,124.37 C 60.2025,124.37 61.0271,110.72 71.2332,108.009 z "
+ id="path1668"
+ sodipodi:nodetypes="ccccccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(0.995445,0,0,0.910163,2.817975,-20.11005)" />
+ <g
+ id="g1669"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 89.0318,98.0844 L 394.284,38.7303 C 394.284,38.7303 401.915,36.1865 407.851,45.5137 C 413.786,54.8408 412.09,59.0804 412.09,59.0804 L 412.09,234.6 C 411.242,243.927 412.938,240.535 407.851,250.711 C 397.675,254.102 105.142,333.807 105.142,333.807 C 105.142,333.807 105.142,335.503 94.9673,333.807 C 84.7922,332.111 81.4005,316.001 81.4005,315.153 C 81.4005,314.305 78.0088,115.891 78.0088,115.891 C 78.0088,115.891 79.7048,101.476 89.0318,98.0844 z "
+ id="path1670"
+ sodipodi:nodetypes="ccccccccccc"
+ style="fill:url(#linearGradient614);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 368.846,266.821 L 398.523,258.342 L 398.523,271.909 C 398.523,271.909 368.846,279.54 368.846,280.388 C 368.846,281.236 368.846,266.821 368.846,266.821 z "
+ id="path1671"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#00feb3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+ </g>
+ <g
+ id="g1672"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(1.060794,0,0,0.913679,-36.3605,196.9337)">
+ <g
+ id="g1673"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 35.6013,306.962 L 454.114,242.088 L 454.114,266.614 L 35.6012,332.279 L 35.6013,306.962 z "
+ id="path1674"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b2b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(3.955698,-6.329117)" />
+ <path
+ d="M 39.557,300.633 C 41.9304,296.677 90.981,199.367 90.981,199.367 L 371.044,173.259 L 458.07,235.759 L 39.557,300.633 z "
+ id="path1675"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g1676"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <g
+ id="g1677"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 331.487,184.335 C 331.487,184.335 332.279,193.038 332.279,193.829 C 332.279,194.62 366.298,234.177 366.298,234.177 L 424.051,224.683 L 423.26,218.354 L 422.468,218.354 L 331.487,184.335 z "
+ id="path1678"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 332.279,184.336 L 369.462,180.38 L 424.051,218.354 L 370.253,225.475 L 332.279,184.336 z "
+ id="path1679"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g1680"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 297.468,195.411 C 297.468,195.411 297.468,203.323 297.468,204.114 C 297.468,204.905 318.038,241.297 318.829,241.297 C 319.62,241.297 353.639,236.551 353.639,236.551 L 352.057,227.057"
+ id="path1681"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 298.26,195.411 C 299.051,195.411 322.785,193.038 322.785,193.038 L 352.848,227.057 L 321.994,231.804 L 298.26,195.411 z "
+ id="path1682"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g1683"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 90.1899,208.861 L 98.1013,195.411 L 272.943,178.006 L 282.437,189.082 L 90.1899,208.861 z "
+ id="path1684"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 90.1899,209.652 L 89.3988,215.19 L 282.437,194.62 L 282.437,188.291 L 90.1899,209.652 z "
+ id="path1685"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g1686"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 90.981,215.981 L 287.184,194.62 L 309.336,229.43 L 71.9936,261.867 L 90.981,215.981 z "
+ id="path1687"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 71.9937,261.076 L 71.2026,268.987 L 309.336,236.551 C 310.127,236.551 308.544,230.221 308.544,229.43 C 308.544,228.639 71.9937,263.449 71.9937,261.076 z "
+ id="path1688"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g1689"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 82.2785,274.525 L 301.424,242.089 L 306.171,250.791 L 79.9051,284.019 L 82.2785,274.525 z "
+ id="path1690"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 79.1139,284.81 L 79.1139,291.93 L 306.171,255.538 L 305.38,250 L 79.1139,284.81 z "
+ id="path1691"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+ </g>
+ <g
+ id="g1692"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(5.9849059e-2,0,0,6.3887019e-2,112.49722,673.80536)">
+ <g
+ id="g1693"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(107.0886,1.392441)">
+ <path
+ d="M 299.842,180.38 L 177.215,74.367 L 250.791,71.9936 L 385.285,162.183 C 385.285,162.183 353.639,191.456 299.842,180.38 z "
+ id="path1694"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b3b2b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g1695"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 176.424,74.367 L 178.006,238.924 L 300.633,364.715 L 301.424,181.962 L 176.424,74.367 z "
+ id="path1696"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b3b2b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g1697"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 302.216,181.171 C 302.216,181.171 328.323,184.335 348.102,180.38 C 375,170.095 386.076,163.766 386.076,163.766 L 386.867,342.563 C 386.867,342.563 371.835,353.639 353.64,360.759 C 323.576,367.088 300.633,364.715 300.633,364.715 L 302.216,181.171 z "
+ id="path1698"
+ sodipodi:nodetypes="ccccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g1699"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 315.665,205.696 C 315.665,205.696 378.956,192.247 378.956,193.038 C 378.956,193.829 378.165,218.354 378.165,218.354 C 378.165,218.354 316.456,231.804 315.665,231.804 C 314.874,231.804 314.874,207.278 315.665,205.696 z "
+ id="path1700"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(-1.58226,0)" />
+ <path
+ d="M 323.576,246.835 L 370.253,237.342 L 370.253,241.298 L 323.576,251.582 L 323.576,246.835 z "
+ id="path1701"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#000100;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 360.76,268.987 L 372.627,267.405 L 372.627,279.272 L 360.76,281.646 L 360.76,268.987 z "
+ id="path1702"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#00b300;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 365.506,298.259 L 373.418,297.468 L 373.418,306.962 L 365.506,308.544 L 365.506,298.259 z "
+ id="path1703"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+ </g>
+ <g
+ id="g1704"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(0.868723,0,0,0.841809,-27.91207,15.52193)">
+ <g
+ id="g1705"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 119.557,425.383 L 119.557,438.102 L 435.832,344.83 L 436.68,329.568 L 119.557,425.383 z "
+ id="path1706"
+ sodipodi:nodetypes="ccccc"
+ style="font-size:12px;fill:#b3b3b3;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(-0.847921,19.50222)" />
+ <g
+ id="g1707"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 166.193,347.374 L 119.557,426.231 L 436.68,329.568 L 345.104,298.194 L 166.193,347.374 z "
+ id="path1708"
+ sodipodi:nodetypes="ccccc"
+ style="font-size:12px;fill:#cccccc;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(-0.847921,19.50222)" />
+ <path
+ d="M 139.059,335.503 C 139.059,335.503 127.188,354.157 132.275,369.42 C 136.515,388.075 150.082,392.314 153.474,392.314 C 156.866,392.314 377.177,334.884 377.177,334.884 C 377.177,334.884 383.26,322.784 386.652,303.282 C 379.869,279.54 367.15,276.996 352.735,276.996 C 328.993,263.43 353.584,276.996 353.584,276.996 L 139.059,335.503 z "
+ id="path1709"
+ sodipodi:nodetypes="cccccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(0.708121,0,0,0.825311,71.30738,59.20586)" />
+ </g>
+ </g>
+ <g
+ id="g1710"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 420.57,16.6844 C 420.57,17.5323 399.371,11.5968 377.325,9.90093 C 367.15,10.7489 66.1379,65.8638 63.5942,65.8638 C 67.8338,65.0159 54.267,65.8638 50.8753,73.4951 C 47.4836,81.1264 47.4836,343.134 47.4836,343.134 C 47.4836,343.134 47.4837,350.766 52.5712,356.701 C 60.2025,360.093 80.5526,360.941 72.9213,360.941 L 420.57,16.6844 z "
+ id="path1711"
+ sodipodi:nodetypes="cccccccc"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g1712"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 71.2332,108.009 L 418.873,40.4261 C 423.961,43.8178 429.049,41.2741 431.593,51.4492 C 434.137,61.6241 430.745,304.978 430.745,304.978 C 430.745,304.978 432.44,312.609 426.505,316.849 C 420.569,321.088 79.6699,416.904 79.6699,416.904 C 79.6699,416.904 69.4946,421.143 63.5593,412.664 C 55.0803,410.121 60.2025,124.37 60.2025,124.37 C 60.2025,124.37 61.0271,110.72 71.2332,108.009 z "
+ id="path1713"
+ sodipodi:nodetypes="ccccccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(0.995445,0,0,0.910163,2.817975,-20.11005)" />
+ <g
+ id="g1714"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 89.0318,98.0844 L 394.284,38.7303 C 394.284,38.7303 401.915,36.1865 407.851,45.5137 C 413.786,54.8408 412.09,59.0804 412.09,59.0804 L 412.09,234.6 C 411.242,243.927 412.938,240.535 407.851,250.711 C 397.675,254.102 105.142,333.807 105.142,333.807 C 105.142,333.807 105.142,335.503 94.9673,333.807 C 84.7922,332.111 81.4005,316.001 81.4005,315.153 C 81.4005,314.305 78.0088,115.891 78.0088,115.891 C 78.0088,115.891 79.7048,101.476 89.0318,98.0844 z "
+ id="path1715"
+ sodipodi:nodetypes="ccccccccccc"
+ style="fill:url(#linearGradient614);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 368.846,266.821 L 398.523,258.342 L 398.523,271.909 C 398.523,271.909 368.846,279.54 368.846,280.388 C 368.846,281.236 368.846,266.821 368.846,266.821 z "
+ id="path1716"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#00feb3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+ </g>
+ <g
+ id="g1717"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(1.060794,0,0,0.913679,-36.3605,196.9337)">
+ <g
+ id="g1718"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 35.6013,306.962 L 454.114,242.088 L 454.114,266.614 L 35.6012,332.279 L 35.6013,306.962 z "
+ id="path1719"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b2b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(3.955698,-6.329117)" />
+ <path
+ d="M 39.557,300.633 C 41.9304,296.677 90.981,199.367 90.981,199.367 L 371.044,173.259 L 458.07,235.759 L 39.557,300.633 z "
+ id="path1720"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g1721"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <g
+ id="g1722"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 331.487,184.335 C 331.487,184.335 332.279,193.038 332.279,193.829 C 332.279,194.62 366.298,234.177 366.298,234.177 L 424.051,224.683 L 423.26,218.354 L 422.468,218.354 L 331.487,184.335 z "
+ id="path1723"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 332.279,184.336 L 369.462,180.38 L 424.051,218.354 L 370.253,225.475 L 332.279,184.336 z "
+ id="path1724"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g1725"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 297.468,195.411 C 297.468,195.411 297.468,203.323 297.468,204.114 C 297.468,204.905 318.038,241.297 318.829,241.297 C 319.62,241.297 353.639,236.551 353.639,236.551 L 352.057,227.057"
+ id="path1726"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 298.26,195.411 C 299.051,195.411 322.785,193.038 322.785,193.038 L 352.848,227.057 L 321.994,231.804 L 298.26,195.411 z "
+ id="path1727"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g1728"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 90.1899,208.861 L 98.1013,195.411 L 272.943,178.006 L 282.437,189.082 L 90.1899,208.861 z "
+ id="path1729"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 90.1899,209.652 L 89.3988,215.19 L 282.437,194.62 L 282.437,188.291 L 90.1899,209.652 z "
+ id="path1730"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g1731"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 90.981,215.981 L 287.184,194.62 L 309.336,229.43 L 71.9936,261.867 L 90.981,215.981 z "
+ id="path1732"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 71.9937,261.076 L 71.2026,268.987 L 309.336,236.551 C 310.127,236.551 308.544,230.221 308.544,229.43 C 308.544,228.639 71.9937,263.449 71.9937,261.076 z "
+ id="path1733"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g1734"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 82.2785,274.525 L 301.424,242.089 L 306.171,250.791 L 79.9051,284.019 L 82.2785,274.525 z "
+ id="path1735"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 79.1139,284.81 L 79.1139,291.93 L 306.171,255.538 L 305.38,250 L 79.1139,284.81 z "
+ id="path1736"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+ </g>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.89171284px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 182.78346,673.43795 L 255.62274,616.76111"
+ id="path28400" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.90486449px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 745.21711,622.36338 L 838.70209,667.83568"
+ id="path28404" />
+ <path
+ d="M 879.79292,636.40173 C 879.79292,636.40173 879.55203,616.66773 899.78678,622.58792 C 920.02153,628.50812 916.40818,638.37513 916.64907,638.37513 C 916.88996,638.37513 913.03573,626.25281 928.45268,623.99749 C 942.42429,625.40706 948.92832,628.50812 948.20565,638.93896 C 947.48298,649.3698 940.73806,651.90703 940.73806,651.90703 C 940.73806,651.90703 953.02345,653.3166 949.65099,674.46019 C 944.35141,689.11974 944.35141,687.71017 932.06603,689.40166 C 926.52556,686.86443 926.28467,684.60911 926.28467,684.60911 C 926.28467,684.60911 936.40204,695.60378 921.22598,702.65164 C 904.1228,708.00802 901.95479,703.21547 894.72809,700.67824 C 887.74229,695.60378 889.18763,689.40166 889.18763,689.40166 C 889.18763,689.40166 893.52365,699.83249 878.82936,701.8059 C 864.13507,703.7793 858.59461,699.83249 855.94482,683.76337 C 855.22215,671.64104 868.2302,670.51339 868.2302,670.51339 C 868.2302,670.51339 860.28083,670.7953 858.11283,664.59318 C 855.94482,658.39106 854.49948,650.21554 861.0035,639.22087 C 873.048,625.97089 881.23826,634.71024 879.79292,636.40173 z "
+ id="path28406"
+ sodipodi:nodetypes="cccccccccccccccccc"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.78178847;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g28408"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(5.9849059e-2,0,0,6.3887019e-2,868.00502,630.95484)">
+ <g
+ id="g28410"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(107.0886,1.392441)">
+ <path
+ d="M 299.842,180.38 L 177.215,74.367 L 250.791,71.9936 L 385.285,162.183 C 385.285,162.183 353.639,191.456 299.842,180.38 z "
+ id="path28412"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b3b2b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g28414"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 176.424,74.367 L 178.006,238.924 L 300.633,364.715 L 301.424,181.962 L 176.424,74.367 z "
+ id="path28416"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b3b2b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g28418"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 302.216,181.171 C 302.216,181.171 328.323,184.335 348.102,180.38 C 375,170.095 386.076,163.766 386.076,163.766 L 386.867,342.563 C 386.867,342.563 371.835,353.639 353.64,360.759 C 323.576,367.088 300.633,364.715 300.633,364.715 L 302.216,181.171 z "
+ id="path28420"
+ sodipodi:nodetypes="ccccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g28422"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 315.665,205.696 C 315.665,205.696 378.956,192.247 378.956,193.038 C 378.956,193.829 378.165,218.354 378.165,218.354 C 378.165,218.354 316.456,231.804 315.665,231.804 C 314.874,231.804 314.874,207.278 315.665,205.696 z "
+ id="path28424"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(-1.58226,0)" />
+ <path
+ d="M 323.576,246.835 L 370.253,237.342 L 370.253,241.298 L 323.576,251.582 L 323.576,246.835 z "
+ id="path28426"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#000100;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 360.76,268.987 L 372.627,267.405 L 372.627,279.272 L 360.76,281.646 L 360.76,268.987 z "
+ id="path28428"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#00b300;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 365.506,298.259 L 373.418,297.468 L 373.418,306.962 L 365.506,308.544 L 365.506,298.259 z "
+ id="path28430"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+ </g>
+ <g
+ id="g28432"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(0.868723,0,0,0.841809,-27.91207,15.52193)">
+ <g
+ id="g28434"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 119.557,425.383 L 119.557,438.102 L 435.832,344.83 L 436.68,329.568 L 119.557,425.383 z "
+ id="path28436"
+ sodipodi:nodetypes="ccccc"
+ style="font-size:12px;fill:#b3b3b3;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(-0.847921,19.50222)" />
+ <g
+ id="g28438"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 166.193,347.374 L 119.557,426.231 L 436.68,329.568 L 345.104,298.194 L 166.193,347.374 z "
+ id="path28440"
+ sodipodi:nodetypes="ccccc"
+ style="font-size:12px;fill:#cccccc;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(-0.847921,19.50222)" />
+ <path
+ d="M 139.059,335.503 C 139.059,335.503 127.188,354.157 132.275,369.42 C 136.515,388.075 150.082,392.314 153.474,392.314 C 156.866,392.314 377.177,334.884 377.177,334.884 C 377.177,334.884 383.26,322.784 386.652,303.282 C 379.869,279.54 367.15,276.996 352.735,276.996 C 328.993,263.43 353.584,276.996 353.584,276.996 L 139.059,335.503 z "
+ id="path28442"
+ sodipodi:nodetypes="cccccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(0.708121,0,0,0.825311,71.30738,59.20586)" />
+ </g>
+ </g>
+ <g
+ id="g28444"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 420.57,16.6844 C 420.57,17.5323 399.371,11.5968 377.325,9.90093 C 367.15,10.7489 66.1379,65.8638 63.5942,65.8638 C 67.8338,65.0159 54.267,65.8638 50.8753,73.4951 C 47.4836,81.1264 47.4836,343.134 47.4836,343.134 C 47.4836,343.134 47.4837,350.766 52.5712,356.701 C 60.2025,360.093 80.5526,360.941 72.9213,360.941 L 420.57,16.6844 z "
+ id="path28446"
+ sodipodi:nodetypes="cccccccc"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g28448"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 71.2332,108.009 L 418.873,40.4261 C 423.961,43.8178 429.049,41.2741 431.593,51.4492 C 434.137,61.6241 430.745,304.978 430.745,304.978 C 430.745,304.978 432.44,312.609 426.505,316.849 C 420.569,321.088 79.6699,416.904 79.6699,416.904 C 79.6699,416.904 69.4946,421.143 63.5593,412.664 C 55.0803,410.121 60.2025,124.37 60.2025,124.37 C 60.2025,124.37 61.0271,110.72 71.2332,108.009 z "
+ id="path28450"
+ sodipodi:nodetypes="ccccccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(0.995445,0,0,0.910163,2.817975,-20.11005)" />
+ <g
+ id="g28452"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 89.0318,98.0844 L 394.284,38.7303 C 394.284,38.7303 401.915,36.1865 407.851,45.5137 C 413.786,54.8408 412.09,59.0804 412.09,59.0804 L 412.09,234.6 C 411.242,243.927 412.938,240.535 407.851,250.711 C 397.675,254.102 105.142,333.807 105.142,333.807 C 105.142,333.807 105.142,335.503 94.9673,333.807 C 84.7922,332.111 81.4005,316.001 81.4005,315.153 C 81.4005,314.305 78.0088,115.891 78.0088,115.891 C 78.0088,115.891 79.7048,101.476 89.0318,98.0844 z "
+ id="path28454"
+ sodipodi:nodetypes="ccccccccccc"
+ style="fill:url(#linearGradient614);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 368.846,266.821 L 398.523,258.342 L 398.523,271.909 C 398.523,271.909 368.846,279.54 368.846,280.388 C 368.846,281.236 368.846,266.821 368.846,266.821 z "
+ id="path28456"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#00feb3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+ </g>
+ <g
+ id="g28458"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(1.060794,0,0,0.913679,-36.3605,196.9337)">
+ <g
+ id="g28460"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 35.6013,306.962 L 454.114,242.088 L 454.114,266.614 L 35.6012,332.279 L 35.6013,306.962 z "
+ id="path28462"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b2b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(3.955698,-6.329117)" />
+ <path
+ d="M 39.557,300.633 C 41.9304,296.677 90.981,199.367 90.981,199.367 L 371.044,173.259 L 458.07,235.759 L 39.557,300.633 z "
+ id="path28464"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g28466"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <g
+ id="g28468"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 331.487,184.335 C 331.487,184.335 332.279,193.038 332.279,193.829 C 332.279,194.62 366.298,234.177 366.298,234.177 L 424.051,224.683 L 423.26,218.354 L 422.468,218.354 L 331.487,184.335 z "
+ id="path28470"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 332.279,184.336 L 369.462,180.38 L 424.051,218.354 L 370.253,225.475 L 332.279,184.336 z "
+ id="path28472"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g28474"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 297.468,195.411 C 297.468,195.411 297.468,203.323 297.468,204.114 C 297.468,204.905 318.038,241.297 318.829,241.297 C 319.62,241.297 353.639,236.551 353.639,236.551 L 352.057,227.057"
+ id="path28476"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 298.26,195.411 C 299.051,195.411 322.785,193.038 322.785,193.038 L 352.848,227.057 L 321.994,231.804 L 298.26,195.411 z "
+ id="path28478"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g28480"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 90.1899,208.861 L 98.1013,195.411 L 272.943,178.006 L 282.437,189.082 L 90.1899,208.861 z "
+ id="path28482"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 90.1899,209.652 L 89.3988,215.19 L 282.437,194.62 L 282.437,188.291 L 90.1899,209.652 z "
+ id="path28484"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g28486"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 90.981,215.981 L 287.184,194.62 L 309.336,229.43 L 71.9936,261.867 L 90.981,215.981 z "
+ id="path28488"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 71.9937,261.076 L 71.2026,268.987 L 309.336,236.551 C 310.127,236.551 308.544,230.221 308.544,229.43 C 308.544,228.639 71.9937,263.449 71.9937,261.076 z "
+ id="path28490"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g28492"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 82.2785,274.525 L 301.424,242.089 L 306.171,250.791 L 79.9051,284.019 L 82.2785,274.525 z "
+ id="path28494"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 79.1139,284.81 L 79.1139,291.93 L 306.171,255.538 L 305.38,250 L 79.1139,284.81 z "
+ id="path28496"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+ </g>
+ <g
+ id="g28498"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(5.9849059e-2,0,0,6.3887019e-2,910.83809,632.0825)">
+ <g
+ id="g28500"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(107.0886,1.392441)">
+ <path
+ d="M 299.842,180.38 L 177.215,74.367 L 250.791,71.9936 L 385.285,162.183 C 385.285,162.183 353.639,191.456 299.842,180.38 z "
+ id="path28502"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b3b2b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g28504"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 176.424,74.367 L 178.006,238.924 L 300.633,364.715 L 301.424,181.962 L 176.424,74.367 z "
+ id="path28506"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b3b2b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g28508"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 302.216,181.171 C 302.216,181.171 328.323,184.335 348.102,180.38 C 375,170.095 386.076,163.766 386.076,163.766 L 386.867,342.563 C 386.867,342.563 371.835,353.639 353.64,360.759 C 323.576,367.088 300.633,364.715 300.633,364.715 L 302.216,181.171 z "
+ id="path28510"
+ sodipodi:nodetypes="ccccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g28512"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 315.665,205.696 C 315.665,205.696 378.956,192.247 378.956,193.038 C 378.956,193.829 378.165,218.354 378.165,218.354 C 378.165,218.354 316.456,231.804 315.665,231.804 C 314.874,231.804 314.874,207.278 315.665,205.696 z "
+ id="path28514"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(-1.58226,0)" />
+ <path
+ d="M 323.576,246.835 L 370.253,237.342 L 370.253,241.298 L 323.576,251.582 L 323.576,246.835 z "
+ id="path28516"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#000100;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 360.76,268.987 L 372.627,267.405 L 372.627,279.272 L 360.76,281.646 L 360.76,268.987 z "
+ id="path28518"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#00b300;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 365.506,298.259 L 373.418,297.468 L 373.418,306.962 L 365.506,308.544 L 365.506,298.259 z "
+ id="path28520"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+ </g>
+ <g
+ id="g28522"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(0.868723,0,0,0.841809,-27.91207,15.52193)">
+ <g
+ id="g28524"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 119.557,425.383 L 119.557,438.102 L 435.832,344.83 L 436.68,329.568 L 119.557,425.383 z "
+ id="path28526"
+ sodipodi:nodetypes="ccccc"
+ style="font-size:12px;fill:#b3b3b3;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(-0.847921,19.50222)" />
+ <g
+ id="g28528"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 166.193,347.374 L 119.557,426.231 L 436.68,329.568 L 345.104,298.194 L 166.193,347.374 z "
+ id="path28530"
+ sodipodi:nodetypes="ccccc"
+ style="font-size:12px;fill:#cccccc;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(-0.847921,19.50222)" />
+ <path
+ d="M 139.059,335.503 C 139.059,335.503 127.188,354.157 132.275,369.42 C 136.515,388.075 150.082,392.314 153.474,392.314 C 156.866,392.314 377.177,334.884 377.177,334.884 C 377.177,334.884 383.26,322.784 386.652,303.282 C 379.869,279.54 367.15,276.996 352.735,276.996 C 328.993,263.43 353.584,276.996 353.584,276.996 L 139.059,335.503 z "
+ id="path28532"
+ sodipodi:nodetypes="cccccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(0.708121,0,0,0.825311,71.30738,59.20586)" />
+ </g>
+ </g>
+ <g
+ id="g28534"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 420.57,16.6844 C 420.57,17.5323 399.371,11.5968 377.325,9.90093 C 367.15,10.7489 66.1379,65.8638 63.5942,65.8638 C 67.8338,65.0159 54.267,65.8638 50.8753,73.4951 C 47.4836,81.1264 47.4836,343.134 47.4836,343.134 C 47.4836,343.134 47.4837,350.766 52.5712,356.701 C 60.2025,360.093 80.5526,360.941 72.9213,360.941 L 420.57,16.6844 z "
+ id="path28536"
+ sodipodi:nodetypes="cccccccc"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g28538"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 71.2332,108.009 L 418.873,40.4261 C 423.961,43.8178 429.049,41.2741 431.593,51.4492 C 434.137,61.6241 430.745,304.978 430.745,304.978 C 430.745,304.978 432.44,312.609 426.505,316.849 C 420.569,321.088 79.6699,416.904 79.6699,416.904 C 79.6699,416.904 69.4946,421.143 63.5593,412.664 C 55.0803,410.121 60.2025,124.37 60.2025,124.37 C 60.2025,124.37 61.0271,110.72 71.2332,108.009 z "
+ id="path28540"
+ sodipodi:nodetypes="ccccccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(0.995445,0,0,0.910163,2.817975,-20.11005)" />
+ <g
+ id="g28542"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 89.0318,98.0844 L 394.284,38.7303 C 394.284,38.7303 401.915,36.1865 407.851,45.5137 C 413.786,54.8408 412.09,59.0804 412.09,59.0804 L 412.09,234.6 C 411.242,243.927 412.938,240.535 407.851,250.711 C 397.675,254.102 105.142,333.807 105.142,333.807 C 105.142,333.807 105.142,335.503 94.9673,333.807 C 84.7922,332.111 81.4005,316.001 81.4005,315.153 C 81.4005,314.305 78.0088,115.891 78.0088,115.891 C 78.0088,115.891 79.7048,101.476 89.0318,98.0844 z "
+ id="path28544"
+ sodipodi:nodetypes="ccccccccccc"
+ style="fill:url(#linearGradient614);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 368.846,266.821 L 398.523,258.342 L 398.523,271.909 C 398.523,271.909 368.846,279.54 368.846,280.388 C 368.846,281.236 368.846,266.821 368.846,266.821 z "
+ id="path28546"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#00feb3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+ </g>
+ <g
+ id="g28548"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(1.060794,0,0,0.913679,-36.3605,196.9337)">
+ <g
+ id="g28550"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 35.6013,306.962 L 454.114,242.088 L 454.114,266.614 L 35.6012,332.279 L 35.6013,306.962 z "
+ id="path28552"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b2b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(3.955698,-6.329117)" />
+ <path
+ d="M 39.557,300.633 C 41.9304,296.677 90.981,199.367 90.981,199.367 L 371.044,173.259 L 458.07,235.759 L 39.557,300.633 z "
+ id="path28554"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g28556"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <g
+ id="g28558"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 331.487,184.335 C 331.487,184.335 332.279,193.038 332.279,193.829 C 332.279,194.62 366.298,234.177 366.298,234.177 L 424.051,224.683 L 423.26,218.354 L 422.468,218.354 L 331.487,184.335 z "
+ id="path28560"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 332.279,184.336 L 369.462,180.38 L 424.051,218.354 L 370.253,225.475 L 332.279,184.336 z "
+ id="path28562"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g28564"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 297.468,195.411 C 297.468,195.411 297.468,203.323 297.468,204.114 C 297.468,204.905 318.038,241.297 318.829,241.297 C 319.62,241.297 353.639,236.551 353.639,236.551 L 352.057,227.057"
+ id="path28566"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 298.26,195.411 C 299.051,195.411 322.785,193.038 322.785,193.038 L 352.848,227.057 L 321.994,231.804 L 298.26,195.411 z "
+ id="path28568"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g28570"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 90.1899,208.861 L 98.1013,195.411 L 272.943,178.006 L 282.437,189.082 L 90.1899,208.861 z "
+ id="path28572"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 90.1899,209.652 L 89.3988,215.19 L 282.437,194.62 L 282.437,188.291 L 90.1899,209.652 z "
+ id="path28574"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g28576"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 90.981,215.981 L 287.184,194.62 L 309.336,229.43 L 71.9936,261.867 L 90.981,215.981 z "
+ id="path28578"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 71.9937,261.076 L 71.2026,268.987 L 309.336,236.551 C 310.127,236.551 308.544,230.221 308.544,229.43 C 308.544,228.639 71.9937,263.449 71.9937,261.076 z "
+ id="path28580"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g28582"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 82.2785,274.525 L 301.424,242.089 L 306.171,250.791 L 79.9051,284.019 L 82.2785,274.525 z "
+ id="path28584"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 79.1139,284.81 L 79.1139,291.93 L 306.171,255.538 L 305.38,250 L 79.1139,284.81 z "
+ id="path28586"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+ </g>
+ <g
+ id="g28588"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(5.9849059e-2,0,0,6.3887019e-2,888.29437,661.68353)">
+ <g
+ id="g28590"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(107.0886,1.392441)">
+ <path
+ d="M 299.842,180.38 L 177.215,74.367 L 250.791,71.9936 L 385.285,162.183 C 385.285,162.183 353.639,191.456 299.842,180.38 z "
+ id="path28592"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b3b2b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g28594"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 176.424,74.367 L 178.006,238.924 L 300.633,364.715 L 301.424,181.962 L 176.424,74.367 z "
+ id="path28596"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b3b2b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g28598"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 302.216,181.171 C 302.216,181.171 328.323,184.335 348.102,180.38 C 375,170.095 386.076,163.766 386.076,163.766 L 386.867,342.563 C 386.867,342.563 371.835,353.639 353.64,360.759 C 323.576,367.088 300.633,364.715 300.633,364.715 L 302.216,181.171 z "
+ id="path28600"
+ sodipodi:nodetypes="ccccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:bevel;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g28602"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 315.665,205.696 C 315.665,205.696 378.956,192.247 378.956,193.038 C 378.956,193.829 378.165,218.354 378.165,218.354 C 378.165,218.354 316.456,231.804 315.665,231.804 C 314.874,231.804 314.874,207.278 315.665,205.696 z "
+ id="path28604"
+ style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(-1.58226,0)" />
+ <path
+ d="M 323.576,246.835 L 370.253,237.342 L 370.253,241.298 L 323.576,251.582 L 323.576,246.835 z "
+ id="path28606"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#000100;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 360.76,268.987 L 372.627,267.405 L 372.627,279.272 L 360.76,281.646 L 360.76,268.987 z "
+ id="path28608"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#00b300;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 365.506,298.259 L 373.418,297.468 L 373.418,306.962 L 365.506,308.544 L 365.506,298.259 z "
+ id="path28610"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+ </g>
+ <g
+ id="g28612"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(0.868723,0,0,0.841809,-27.91207,15.52193)">
+ <g
+ id="g28614"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 119.557,425.383 L 119.557,438.102 L 435.832,344.83 L 436.68,329.568 L 119.557,425.383 z "
+ id="path28616"
+ sodipodi:nodetypes="ccccc"
+ style="font-size:12px;fill:#b3b3b3;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(-0.847921,19.50222)" />
+ <g
+ id="g28618"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 166.193,347.374 L 119.557,426.231 L 436.68,329.568 L 345.104,298.194 L 166.193,347.374 z "
+ id="path28620"
+ sodipodi:nodetypes="ccccc"
+ style="font-size:12px;fill:#cccccc;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(-0.847921,19.50222)" />
+ <path
+ d="M 139.059,335.503 C 139.059,335.503 127.188,354.157 132.275,369.42 C 136.515,388.075 150.082,392.314 153.474,392.314 C 156.866,392.314 377.177,334.884 377.177,334.884 C 377.177,334.884 383.26,322.784 386.652,303.282 C 379.869,279.54 367.15,276.996 352.735,276.996 C 328.993,263.43 353.584,276.996 353.584,276.996 L 139.059,335.503 z "
+ id="path28622"
+ sodipodi:nodetypes="cccccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(0.708121,0,0,0.825311,71.30738,59.20586)" />
+ </g>
+ </g>
+ <g
+ id="g28624"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 420.57,16.6844 C 420.57,17.5323 399.371,11.5968 377.325,9.90093 C 367.15,10.7489 66.1379,65.8638 63.5942,65.8638 C 67.8338,65.0159 54.267,65.8638 50.8753,73.4951 C 47.4836,81.1264 47.4836,343.134 47.4836,343.134 C 47.4836,343.134 47.4837,350.766 52.5712,356.701 C 60.2025,360.093 80.5526,360.941 72.9213,360.941 L 420.57,16.6844 z "
+ id="path28626"
+ sodipodi:nodetypes="cccccccc"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <g
+ id="g28628"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 71.2332,108.009 L 418.873,40.4261 C 423.961,43.8178 429.049,41.2741 431.593,51.4492 C 434.137,61.6241 430.745,304.978 430.745,304.978 C 430.745,304.978 432.44,312.609 426.505,316.849 C 420.569,321.088 79.6699,416.904 79.6699,416.904 C 79.6699,416.904 69.4946,421.143 63.5593,412.664 C 55.0803,410.121 60.2025,124.37 60.2025,124.37 C 60.2025,124.37 61.0271,110.72 71.2332,108.009 z "
+ id="path28630"
+ sodipodi:nodetypes="ccccccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(0.995445,0,0,0.910163,2.817975,-20.11005)" />
+ <g
+ id="g28632"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 89.0318,98.0844 L 394.284,38.7303 C 394.284,38.7303 401.915,36.1865 407.851,45.5137 C 413.786,54.8408 412.09,59.0804 412.09,59.0804 L 412.09,234.6 C 411.242,243.927 412.938,240.535 407.851,250.711 C 397.675,254.102 105.142,333.807 105.142,333.807 C 105.142,333.807 105.142,335.503 94.9673,333.807 C 84.7922,332.111 81.4005,316.001 81.4005,315.153 C 81.4005,314.305 78.0088,115.891 78.0088,115.891 C 78.0088,115.891 79.7048,101.476 89.0318,98.0844 z "
+ id="path28634"
+ sodipodi:nodetypes="ccccccccccc"
+ style="fill:url(#linearGradient614);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 368.846,266.821 L 398.523,258.342 L 398.523,271.909 C 398.523,271.909 368.846,279.54 368.846,280.388 C 368.846,281.236 368.846,266.821 368.846,266.821 z "
+ id="path28636"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#00feb3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+ </g>
+ <g
+ id="g28638"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1"
+ transform="matrix(1.060794,0,0,0.913679,-36.3605,196.9337)">
+ <g
+ id="g28640"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 35.6013,306.962 L 454.114,242.088 L 454.114,266.614 L 35.6012,332.279 L 35.6013,306.962 z "
+ id="path28642"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#b2b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
+ transform="translate(3.955698,-6.329117)" />
+ <path
+ d="M 39.557,300.633 C 41.9304,296.677 90.981,199.367 90.981,199.367 L 371.044,173.259 L 458.07,235.759 L 39.557,300.633 z "
+ id="path28644"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g28646"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <g
+ id="g28648"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 331.487,184.335 C 331.487,184.335 332.279,193.038 332.279,193.829 C 332.279,194.62 366.298,234.177 366.298,234.177 L 424.051,224.683 L 423.26,218.354 L 422.468,218.354 L 331.487,184.335 z "
+ id="path28650"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 332.279,184.336 L 369.462,180.38 L 424.051,218.354 L 370.253,225.475 L 332.279,184.336 z "
+ id="path28652"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g28654"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 297.468,195.411 C 297.468,195.411 297.468,203.323 297.468,204.114 C 297.468,204.905 318.038,241.297 318.829,241.297 C 319.62,241.297 353.639,236.551 353.639,236.551 L 352.057,227.057"
+ id="path28656"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 298.26,195.411 C 299.051,195.411 322.785,193.038 322.785,193.038 L 352.848,227.057 L 321.994,231.804 L 298.26,195.411 z "
+ id="path28658"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g28660"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 90.1899,208.861 L 98.1013,195.411 L 272.943,178.006 L 282.437,189.082 L 90.1899,208.861 z "
+ id="path28662"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 90.1899,209.652 L 89.3988,215.19 L 282.437,194.62 L 282.437,188.291 L 90.1899,209.652 z "
+ id="path28664"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g28666"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 90.981,215.981 L 287.184,194.62 L 309.336,229.43 L 71.9936,261.867 L 90.981,215.981 z "
+ id="path28668"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 71.9937,261.076 L 71.2026,268.987 L 309.336,236.551 C 310.127,236.551 308.544,230.221 308.544,229.43 C 308.544,228.639 71.9937,263.449 71.9937,261.076 z "
+ id="path28670"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ id="g28672"
+ style="stroke:#000000;stroke-width:4.29749012;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ d="M 82.2785,274.525 L 301.424,242.089 L 306.171,250.791 L 79.9051,284.019 L 82.2785,274.525 z "
+ id="path28674"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ d="M 79.1139,284.81 L 79.1139,291.93 L 306.171,255.538 L 305.38,250 L 79.1139,284.81 z "
+ id="path28676"
+ sodipodi:nodetypes="ccccc"
+ style="fill:#e5e6e6;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.29749012;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ </g>
+ </g>
+ </g>
+ <g
+ id="g3073"
+ transform="matrix(0.1267968,0,0,0.1710106,229.00249,409.01498)">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path3075"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z " />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path3077"
+ d="M 495.64613 253.85521 A 234.95641 236.07524 0 1 1 25.733322,253.85521 A 234.95641 236.07524 0 1 1 495.64613 253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path3079"
+ d="M 495.64613 253.85521 A 234.95641 236.07524 0 1 1 25.733322,253.85521 A 234.95641 236.07524 0 1 1 495.64613 253.85521 z" />
+ </g>
+ <g
+ id="g3081"
+ transform="matrix(0.1267968,0,0,0.1710106,376.48475,404.97437)">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path3083"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z " />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path3085"
+ d="M 495.64613 253.85521 A 234.95641 236.07524 0 1 1 25.733322,253.85521 A 234.95641 236.07524 0 1 1 495.64613 253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path3087"
+ d="M 495.64613 253.85521 A 234.95641 236.07524 0 1 1 25.733322,253.85521 A 234.95641 236.07524 0 1 1 495.64613 253.85521 z" />
+ </g>
+ <g
+ id="g3089"
+ transform="matrix(1.2500391,0,0,1.184913,597.0199,213.65087)">
+ <g
+ transform="translate(152.9277,120.7469)"
+ id="g3091">
+ <path
+ style="fill:url(#radialGradient3109);fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ id="path3093"
+ d="M 24.972918,-30.66305 C 24.972918,-22.90055 -16.207082,-20.35055 -53.230207,-20.66305 C -90.878332,-20.66305 -92.370832,-26.65055 -92.370832,-34.41305 C -92.370832,-42.17555 -71.190832,-40.03805 -33.542707,-40.03805 C 4.105418,-40.03805 24.972918,-38.42555 24.972918,-30.66305 z " />
+ <path
+ style="fill:url(#linearGradient3111);fill-opacity:1;stroke:#677883;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ id="path3095"
+ d="M -88.933334,-50.694302 L 23.566666,-50.694302 L 23.566666,-37.18304 L -88.933334,-37.18304 L -88.933334,-50.694302 z " />
+ <path
+ style="fill:url(#linearGradient3113);fill-opacity:1;stroke:#677883;stroke-width:0.49944988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ id="path3097"
+ d="M -75.933161,-61.840606 L 36.319422,-61.840606 L 23.751782,-51.190703 L -88.500801,-51.190703 L -75.933161,-61.840606 z " />
+ <path
+ style="fill:url(#linearGradient3115);fill-opacity:1;stroke:#677883;stroke-width:0.46877259;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ id="path3099"
+ d="M 23.981782,-50.846435 L 36.765614,-61.588097 L 36.752498,-48.186223 L 24.037025,-37.40884 L 23.981782,-50.846435 z " />
+ <rect
+ y="-40.584919"
+ x="-86.589584"
+ width="1.5625"
+ style="fill:url(#radialGradient3117);fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ rx="0"
+ id="rect3101"
+ height="0.78125" />
+ <rect
+ y="-42.264622"
+ x="-86.589584"
+ width="1.5625"
+ style="fill:url(#radialGradient3119);fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ rx="0"
+ id="rect3103"
+ height="0.78125" />
+ <rect
+ y="-43.944294"
+ x="-86.589584"
+ width="1.5625"
+ style="fill:url(#radialGradient3121);fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ rx="0"
+ id="rect3105"
+ height="0.78125" />
+ <rect
+ y="-45.33102"
+ x="-86.609116"
+ width="1.5625"
+ style="fill:url(#radialGradient3123);fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ rx="0"
+ id="rect3107"
+ height="0.78125" />
+ </g>
+ </g>
+ <g
+ id="g3125"
+ transform="matrix(0.1267968,0,0,0.1710106,587.83432,404.25336)">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path3127"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z " />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path3129"
+ d="M 495.64613 253.85521 A 234.95641 236.07524 0 1 1 25.733322,253.85521 A 234.95641 236.07524 0 1 1 495.64613 253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path3131"
+ d="M 495.64613 253.85521 A 234.95641 236.07524 0 1 1 25.733322,253.85521 A 234.95641 236.07524 0 1 1 495.64613 253.85521 z" />
+ </g>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 618.99582,406.26456 L 726.07199,357.77724"
+ id="path3133" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.03078127px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 765.25542,360.35663 L 886.89875,405.70547"
+ id="path3135" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 746.27504,404.24425 L 746.27504,349.69602"
+ id="path3137" />
+ <g
+ id="g3139"
+ transform="matrix(0.1267968,0,0,0.1710106,716.67848,402.51509)">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path3141"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z " />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path3143"
+ d="M 495.64613 253.85521 A 234.95641 236.07524 0 1 1 25.733322,253.85521 A 234.95641 236.07524 0 1 1 495.64613 253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path3145"
+ d="M 495.64613 253.85521 A 234.95641 236.07524 0 1 1 25.733322,253.85521 A 234.95641 236.07524 0 1 1 495.64613 253.85521 z" />
+ </g>
+ <g
+ id="g3147"
+ transform="matrix(0.1267968,0,0,0.1710106,864.16074,398.47448)">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path3149"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z " />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path3151"
+ d="M 495.64613 253.85521 A 234.95641 236.07524 0 1 1 25.733322,253.85521 A 234.95641 236.07524 0 1 1 495.64613 253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path3153"
+ d="M 495.64613 253.85521 A 234.95641 236.07524 0 1 1 25.733322,253.85521 A 234.95641 236.07524 0 1 1 495.64613 253.85521 z" />
+ </g>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.06804883px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 132.72111,484.54548 L 239.79728,539.85635"
+ id="path3155" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.10092473px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 278.98071,536.91396 L 400.62404,485.18325"
+ id="path3157" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.06804883px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 260.00033,486.85011 L 260.00033,549.07483"
+ id="path3159" />
+ <g
+ id="g3205"
+ transform="matrix(1.2344541,0,0,1.166142,589.26929,486.12951)">
+ <g
+ transform="translate(152.9277,120.7469)"
+ id="g3207">
+ <path
+ style="fill:url(#radialGradient3239);fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ id="path3209"
+ d="M 24.972918,-30.66305 C 24.972918,-22.90055 -16.207082,-20.35055 -53.230207,-20.66305 C -90.878332,-20.66305 -92.370832,-26.65055 -92.370832,-34.41305 C -92.370832,-42.17555 -71.190832,-40.03805 -33.542707,-40.03805 C 4.105418,-40.03805 24.972918,-38.42555 24.972918,-30.66305 z " />
+ <path
+ style="fill:url(#linearGradient3241);fill-opacity:1;stroke:#677883;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ id="path3211"
+ d="M -88.933334,-50.694302 L 23.566666,-50.694302 L 23.566666,-37.18304 L -88.933334,-37.18304 L -88.933334,-50.694302 z " />
+ <path
+ style="fill:url(#linearGradient3243);fill-opacity:1;stroke:#677883;stroke-width:0.49944988;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ id="path3213"
+ d="M -75.933161,-61.840606 L 36.319422,-61.840606 L 23.751782,-51.190703 L -88.500801,-51.190703 L -75.933161,-61.840606 z " />
+ <path
+ style="fill:url(#linearGradient3245);fill-opacity:1;stroke:#677883;stroke-width:0.46877259;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ id="path3215"
+ d="M 23.981782,-50.846435 L 36.765614,-61.588097 L 36.752498,-48.186223 L 24.037025,-37.40884 L 23.981782,-50.846435 z " />
+ <rect
+ y="-40.584919"
+ x="-86.589584"
+ width="1.5625"
+ style="fill:url(#radialGradient3247);fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ rx="0"
+ id="rect3217"
+ height="0.78125" />
+ <rect
+ y="-42.264622"
+ x="-86.589584"
+ width="1.5625"
+ style="fill:url(#radialGradient3249);fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ rx="0"
+ id="rect3219"
+ height="0.78125" />
+ <rect
+ y="-43.944294"
+ x="-86.589584"
+ width="1.5625"
+ style="fill:url(#radialGradient3251);fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ rx="0"
+ id="rect3221"
+ height="0.78125" />
+ <rect
+ y="-45.33102"
+ x="-86.609116"
+ width="1.5625"
+ style="fill:url(#radialGradient3253);fill-opacity:1;stroke:none;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1"
+ rx="0"
+ id="rect3223"
+ height="0.78125" />
+ </g>
+ </g>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot3225"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial"
+ transform="matrix(0.7525038,0,0,0.6775389,553.71011,269.52496)"><flowRegion
+ id="flowRegion3227"><rect
+ id="rect3229"
+ width="157.14285"
+ height="40"
+ x="194.28572"
+ y="475.52304"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara3231">Load Balancer</flowPara></flowRoot> <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.06804883px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 621.63441,484.8423 L 728.71058,540.15317"
+ id="path3233" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.10092473px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 767.89401,537.21078 L 889.53734,485.48007"
+ id="path3235" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.06804883px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 748.91363,487.14693 L 748.91363,549.37165"
+ id="path3237" />
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot3255"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial"
+ transform="translate(-22.223356,-10.101525)"><flowRegion
+ id="flowRegion3257"><rect
+ id="rect3259"
+ width="151.52289"
+ height="32.324883"
+ x="44.446712"
+ y="364.27713"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara3263">Chaining Overlay</flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot3265"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial"
+ transform="matrix(1,0,0,1.2037203,31.345186,184.04024)"><flowRegion
+ id="flowRegion3267"><rect
+ id="rect3269"
+ width="208.52287"
+ height="72.93808"
+ x="412.14224"
+ y="279.42432"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara3096">50% of total writes </flowPara><flowPara
+ id="flowPara3098">(DC A + DC B) are </flowPara><flowPara
+ id="flowPara3100">always off-site</flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot3102"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans"
+ transform="translate(-8,4)"><flowRegion
+ id="flowRegion3104"><rect
+ id="rect3106"
+ width="70"
+ height="24"
+ x="196"
+ y="688.09448"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans" /></flowRegion><flowPara
+ id="flowPara3110">Clients</flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot3112"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans"
+ transform="translate(750.19434,-91.642006)"><flowRegion
+ id="flowRegion3114"><rect
+ id="rect3116"
+ width="70"
+ height="24"
+ x="196"
+ y="688.09448"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;font-family:Bitstream Vera Sans" /></flowRegion><flowPara
+ id="flowPara3118">Clients</flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot3120"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial"
+ transform="matrix(1,0,0,1.2037203,18.69043,-89.07388)"><flowRegion
+ id="flowRegion3122"><rect
+ id="rect3124"
+ width="208.52287"
+ height="72.93808"
+ x="412.14224"
+ y="279.42432"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara3132">Each LB points to</flowPara><flowPara
+ id="flowPara3134">the same Mirror mode</flowPara><flowPara
+ id="flowPara3136">Node at any time.</flowPara></flowRoot> </g>
+</svg>
diff --git a/doc/guide/images/src/intro_dctree.dia b/doc/guide/images/src/intro_dctree.dia
new file mode 100644
index 0000000..556671f
--- /dev/null
+++ b/doc/guide/images/src/intro_dctree.dia
Binary files differ
diff --git a/doc/guide/images/src/intro_tree.dia b/doc/guide/images/src/intro_tree.dia
new file mode 100644
index 0000000..86a8d1b
--- /dev/null
+++ b/doc/guide/images/src/intro_tree.dia
Binary files differ
diff --git a/doc/guide/images/src/ldap-sync-refreshandpersist.svg b/doc/guide/images/src/ldap-sync-refreshandpersist.svg
new file mode 100644
index 0000000..d5047ff
--- /dev/null
+++ b/doc/guide/images/src/ldap-sync-refreshandpersist.svg
@@ -0,0 +1,4853 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="524.40942"
+ id="svg7893"
+ inkscape:version="0.46"
+ sodipodi:docbase="/home/ghenry/Desktop"
+ sodipodi:docname="ldap-sync-refreshandpersist.svg"
+ sodipodi:version="0.32"
+ width="744.09448"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ version="1.0"
+ inkscape:export-filename="/home/ghenry/Desktop/ldap-sync-refreshandpersist.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <metadata
+ id="metadata2563">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:title>Firewall2</dc:title>
+ <dc:description />
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>wall</rdf:li>
+ <rdf:li>brick</rdf:li>
+ <rdf:li>computer</rdf:li>
+ <rdf:li>networksym</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ <dc:publisher>
+ <cc:Agent
+ rdf:about="http://www.openclipart.org/">
+ <dc:title>Open Clip Art Library</dc:title>
+ </cc:Agent>
+ </dc:publisher>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>HASH(0x89c79d4)</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:rights>
+ <cc:Agent>
+ <dc:title>HASH(0x89c79d4)</dc:title>
+ </cc:Agent>
+ </dc:rights>
+ <dc:date />
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <cc:license
+ rdf:resource="http://web.resource.org/cc/PublicDomain" />
+ <dc:language>en</dc:language>
+ </cc:Work>
+ <cc:License
+ rdf:about="http://web.resource.org/cc/PublicDomain">
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Reproduction" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Distribution" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs7895">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 372.04724 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="1052.3622 : 372.04724 : 1"
+ inkscape:persp3d-origin="526.18109 : 248.03149 : 1"
+ id="perspective6943" />
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible">
+ <path
+ id="path17680"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lstart"
+ style="overflow:visible">
+ <path
+ id="path17677"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(0.8,0,0,0.8,10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend"
+ style="overflow:visible">
+ <path
+ id="path17686"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mstart"
+ style="overflow:visible">
+ <path
+ id="path17683"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(0.4,0,0,0.4,4,0)" />
+ </marker>
+ <linearGradient
+ id="linearGradient6508">
+ <stop
+ id="stop6509"
+ offset="0.0000000"
+ style="stop-color:#ff0000;stop-opacity:1.0000000;" />
+ <stop
+ id="stop6511"
+ offset="0.64370060"
+ style="stop-color:#ffb900;stop-opacity:1.0000000;" />
+ <stop
+ id="stop6512"
+ offset="0.79038113"
+ style="stop-color:#ffff00;stop-opacity:0.84102565;" />
+ <stop
+ id="stop6510"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.21568628;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient13376">
+ <stop
+ style="stop-color:#d4d4d4;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop13377" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.49803922;"
+ offset="0.50000000"
+ id="stop13380" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="1.0000000"
+ id="stop13378" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient12744">
+ <stop
+ style="stop-color:#839da4;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop12745" />
+ <stop
+ style="stop-color:#496d77;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop12746" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient10810">
+ <stop
+ style="stop-color:#0e0000;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop10811" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1.0000000;"
+ offset="0.50000000"
+ id="stop10814" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop10812" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient11442">
+ <stop
+ style="stop-color:#6e6e6e;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop11443" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop11444" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient14160">
+ <stop
+ style="stop-color:#4af853;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop14161" />
+ <stop
+ style="stop-color:#68b96d;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop14162" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient14835">
+ <stop
+ style="stop-color:#bed1d0;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop14836" />
+ <stop
+ style="stop-color:#52727b;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop14837" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient29203">
+ <stop
+ style="stop-color:#d3d3d3;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop29205" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop29207" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient6658">
+ <stop
+ style="stop-color:#677883;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop6659" />
+ <stop
+ style="stop-color:#677883;stop-opacity:0.0000000;"
+ offset="1.0000000"
+ id="stop6660" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient41493">
+ <stop
+ style="stop-color:#181818;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop41495" />
+ <stop
+ style="stop-color:#5e5e5e;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop41497" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient12759">
+ <stop
+ style="stop-color:#b4b4b4;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop12761" />
+ <stop
+ style="stop-color:#d7d8de;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop12763" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient21825">
+ <stop
+ style="stop-color:#808080;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop21827" />
+ <stop
+ style="stop-color:#5e5e5e;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop21829" />
+ </linearGradient>
+ <radialGradient
+ xlink:href="#linearGradient13376"
+ r="31.620827"
+ id="radialGradient25527"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.776429,0,0,0.659114,-120.5524,673.5049)"
+ fy="254.35735"
+ fx="-19.038713"
+ cy="253.63734"
+ cx="-19.261518" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12759"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient25525"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,-145.458,730.6984)" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient21825"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient25403"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,-145.2247,712.702)" />
+ <linearGradient
+ y2="1977.8738"
+ y1="1924.0137"
+ xlink:href="#linearGradient41493"
+ x2="-35.763195"
+ x1="-39.828941"
+ id="linearGradient25401"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.672454,0,0,0.374188,-3.473342,95.2718)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient25353"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.270019,0,0,0.370779,-149.3489,792.5495)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient26976"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-90.06505,808.8095)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient26972"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.05831,0,0,0.803858,616.249,115.0105)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient26974"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.838868,0,0,0.530755,508.4408,137.664)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient26964"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.05831,0,0,0.803858,616.249,115.0105)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient26966"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.838868,0,0,0.530755,508.4408,137.664)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28284"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28286"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28288"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28290"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28274"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28276"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28278"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28280"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28264"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28266"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28268"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28270"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28254"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28256"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28258"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28260"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28244"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28246"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28248"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28250"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28234"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28236"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28238"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28240"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28224"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28226"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28228"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28230"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28214"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28216"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28218"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28220"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28208"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-125.9178,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28210"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="97.536598"
+ cy="113.726"
+ cx="97.536598" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28204"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-121.573,808.7592)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28206"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="100.67591"
+ cy="113.726"
+ cx="100.67591" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28200"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-116.9703,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28202"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="104.00187"
+ cy="113.726"
+ cx="104.00187" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28196"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-112.6254,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28198"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="107.14119"
+ cy="113.726"
+ cx="107.14119" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28192"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-108.4824,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28194"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="110.13468"
+ cy="113.726"
+ cx="110.13468" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28188"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-104.1375,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28190"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="113.27399"
+ cy="113.726"
+ cx="113.27399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28184"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-99.77797,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28186"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="116.42374"
+ cy="113.726"
+ cx="116.42374" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28180"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-95.43307,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28182"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="119.56305"
+ cy="113.726"
+ cx="119.56305" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28172"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28174"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28176"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28178"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28162"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28164"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28168"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28152"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28154"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28156"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28158"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28142"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28144"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28146"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28148"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28132"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28134"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28136"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28138"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28122"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28124"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28126"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28128"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28112"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28114"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28116"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28118"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28102"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28104"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28106"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28108"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28096"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-161.2375,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28098"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="71.480988"
+ cy="113.726"
+ cx="71.480988" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28092"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-156.8927,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28094"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="74.620308"
+ cy="113.726"
+ cx="74.620308" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28088"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-152.29,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28090"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="77.946259"
+ cy="113.726"
+ cx="77.946259" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28084"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-147.9451,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28086"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="81.085587"
+ cy="113.726"
+ cx="81.085587" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28080"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-143.8021,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28082"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="84.079071"
+ cy="113.726"
+ cx="84.079071" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28076"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-139.4573,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28078"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="87.218399"
+ cy="113.726"
+ cx="87.218399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28072"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-135.098,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28074"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="90.368126"
+ cy="113.726"
+ cx="90.368126" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28068"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-130.7531,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28070"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="93.507462"
+ cy="113.726"
+ cx="93.507462" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28060"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28062"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28064"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28066"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28050"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28052"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28054"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28056"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28040"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28042"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28044"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28046"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28030"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28032"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28034"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28036"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28020"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28022"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28024"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28026"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28010"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28012"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28014"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28016"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28000"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28002"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28004"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28006"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27990"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27992"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27994"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27996"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27984"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-197.2616,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27986"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="45.452175"
+ cy="113.726"
+ cx="45.452175" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27980"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-192.9168,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27982"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="48.591496"
+ cy="113.726"
+ cx="48.591496" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27976"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-188.3141,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27978"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="51.91745"
+ cy="113.726"
+ cx="51.91745" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27972"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-183.9692,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27974"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="55.05677"
+ cy="113.726"
+ cx="55.05677" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27968"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-179.8262,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27970"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="58.050255"
+ cy="113.726"
+ cx="58.050255" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27964"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-175.4813,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27966"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="61.189575"
+ cy="113.726"
+ cx="61.189575" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27960"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-171.122,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27962"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="64.339317"
+ cy="113.726"
+ cx="64.339317" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27956"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-166.7771,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27958"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="67.478638"
+ cy="113.726"
+ cx="67.478638" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27928"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27930"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27932"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27934"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27918"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27920"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27922"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27924"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27908"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27910"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27912"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27914"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27898"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27900"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27902"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27904"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27888"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27890"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27892"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27894"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27878"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27880"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27882"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27884"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27868"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27870"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27872"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27874"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27858"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27860"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27862"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27864"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27848"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27850"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27852"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27854"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27838"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27840"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27842"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27844"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27828"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27830"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27832"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27834"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27818"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27820"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27822"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27824"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27808"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27810"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27812"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27814"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27798"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27800"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27802"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27804"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27788"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27790"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27792"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27794"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27778"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27780"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27782"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27784"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27768"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27770"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27772"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27774"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27758"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27760"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27762"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27764"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27748"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27750"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27752"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27754"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27738"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27740"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27742"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27744"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27728"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27730"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27732"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27734"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27718"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27720"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27722"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27724"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27708"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27710"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27712"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27714"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27698"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27700"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27702"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27704"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28432"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-126.1386,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28434"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="97.536598"
+ cy="113.726"
+ cx="97.536598" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28428"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-121.7938,817.6604)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28430"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="100.67591"
+ cy="113.726"
+ cx="100.67591" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28424"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-117.1911,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28426"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="104.00187"
+ cy="113.726"
+ cx="104.00187" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28420"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-112.8462,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28422"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="107.14119"
+ cy="113.726"
+ cx="107.14119" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28416"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-108.7032,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28418"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="110.13468"
+ cy="113.726"
+ cx="110.13468" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28412"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-104.3583,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28414"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="113.27399"
+ cy="113.726"
+ cx="113.27399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28408"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-99.99876,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28410"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="116.42374"
+ cy="113.726"
+ cx="116.42374" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28404"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-95.65386,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28406"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="119.56305"
+ cy="113.726"
+ cx="119.56305" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28400"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-161.4583,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28402"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="71.480988"
+ cy="113.726"
+ cx="71.480988" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28396"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-157.1135,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28398"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="74.620308"
+ cy="113.726"
+ cx="74.620308" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28392"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-152.5108,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28394"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="77.946259"
+ cy="113.726"
+ cx="77.946259" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28388"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-148.1659,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28390"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="81.085587"
+ cy="113.726"
+ cx="81.085587" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28384"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-144.0229,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28386"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="84.079071"
+ cy="113.726"
+ cx="84.079071" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28380"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-139.6781,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28382"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="87.218399"
+ cy="113.726"
+ cx="87.218399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28376"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-135.3188,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28378"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="90.368126"
+ cy="113.726"
+ cx="90.368126" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28372"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-130.9739,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28374"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="93.507462"
+ cy="113.726"
+ cx="93.507462" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28368"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-197.4824,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28370"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="45.452175"
+ cy="113.726"
+ cx="45.452175" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28364"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-193.1376,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28366"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="48.591496"
+ cy="113.726"
+ cx="48.591496" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28360"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-188.5349,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28362"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="51.91745"
+ cy="113.726"
+ cx="51.91745" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28356"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-184.19,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28358"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="55.05677"
+ cy="113.726"
+ cx="55.05677" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28352"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-180.047,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28354"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="58.050255"
+ cy="113.726"
+ cx="58.050255" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28348"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-175.7021,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28350"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="61.189575"
+ cy="113.726"
+ cx="61.189575" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28344"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-171.3428,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28346"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="64.339317"
+ cy="113.726"
+ cx="64.339317" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28340"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-166.9979,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28342"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="67.478638"
+ cy="113.726"
+ cx="67.478638" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28438"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-90.25863,817.7848)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12759"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient36281"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,-149.897,802.9053)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36283"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36285"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36287"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36289"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36291"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36293"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36295"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36297"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36299"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36301"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36303"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36305"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36307"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36309"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36311"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36313"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36315"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36317"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36319"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36321"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36323"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36325"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36327"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36329"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36331"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.5348,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36333"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-185.7196,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36335"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-189.35,879.6484)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36337"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-180.274,879.6484)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36339"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-183.9043,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36341"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-182.0892,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient21825"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient36343"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,-149.6637,784.9089)" />
+ <linearGradient
+ y2="1977.8738"
+ y1="1924.0137"
+ xlink:href="#linearGradient41493"
+ x2="-35.763195"
+ x1="-39.828941"
+ id="linearGradient36345"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.672454,0,0,0.374188,-7.912301,167.4787)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36347"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.5296,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36349"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-185.7144,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36351"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-189.3448,881.7646)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36353"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-180.2688,881.7646)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36355"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-183.8991,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36357"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-182.084,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36359"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-198.4916,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36361"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-190.046,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36363"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.2306,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36365"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-192.8611,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36367"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-195.6763,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36369"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.74272,0,0,0.445632,-87.12747,420.4818)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36371"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36373"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36375"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.787283,0,0,0.475341,-91.66274,388.2275)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36377"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36379"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36381"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.270019,0,0,0.370779,-153.7879,864.7564)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12759"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient35867"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,-141.9847,635.4266)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35869"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35871"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35873"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35875"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35877"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35879"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35881"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35883"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35885"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35887"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35889"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35891"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35893"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35895"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35897"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35899"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35901"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35903"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35905"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35907"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35909"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35911"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35913"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35915"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35917"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35919"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35921"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35923"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35925"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35927"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35929"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35931"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35933"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35935"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35937"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35939"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35941"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35943"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35945"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35947"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35949"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35951"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35953"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35955"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35957"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35959"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35961"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35963"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35965"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-179.6225,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35967"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-168.7312,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35969"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-163.2856,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35971"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-161.4702,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35973"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-165.1007,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35975"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-177.8073,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35977"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-166.9159,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35979"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-181.4377,712.1697)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35981"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-170.5465,712.1697)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35983"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-172.3617,712.1697)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35985"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-175.992,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35987"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-174.1769,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient21825"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient35989"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,-141.7514,617.4302)" />
+ <linearGradient
+ y2="1977.8738"
+ y1="1924.0137"
+ xlink:href="#linearGradient41493"
+ x2="-35.763195"
+ x1="-39.828941"
+ id="linearGradient35991"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(2.672454,0.374188)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35993"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-179.6173,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35995"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-168.726,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35997"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-163.2804,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35999"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-161.465,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36001"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-165.0955,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36003"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-177.8021,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36005"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-166.9107,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36007"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-181.4325,714.2859)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36009"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-170.5413,714.2859)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36011"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-172.3565,714.2859)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36013"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-175.9868,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36015"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-174.1717,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36017"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-190.5793,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36019"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-182.1337,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36021"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-179.3183,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36023"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-184.9488,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36025"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.764,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36027"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.74272,0,0,0.445632,-79.21517,253.0031)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36029"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36031"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36033"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.787283,0,0,0.475341,-83.75044,220.7488)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36035"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36037"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36039"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.270019,0,0,0.370779,-145.8756,697.2777)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient13376"
+ r="31.620827"
+ id="radialGradient12151"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.20227,0,0,0.454077,6.691668,-148.3193)"
+ fy="254.35735"
+ fx="-19.038713"
+ cy="253.63734"
+ cx="-19.261518" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12744"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient12153"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,1.691668,-145.8193)" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient14835"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient12155"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,1.924904,-161.8157)" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12744"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient12157"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.350818,114.6621,-134.6472)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12159"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-59.65453)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12161"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-61.33423)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12163"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-63.01391)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12165"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.09869,-64.40064)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <inkscape:perspective
+ id="perspective3612"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ </defs>
+ <sodipodi:namedview
+ bordercolor="#666666"
+ borderopacity="1.0"
+ id="base"
+ inkscape:current-layer="layer1"
+ inkscape:cx="344.82324"
+ inkscape:cy="267.55258"
+ inkscape:document-units="px"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:window-height="768"
+ inkscape:window-width="1024"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:zoom="1"
+ pagecolor="#ffffff"
+ width="1052.3622px"
+ height="744.09448px"
+ showgrid="false" />
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1">
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot12890"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Arial"
+ transform="translate(-51.99247,-442.27533)"><flowRegion
+ id="flowRegion12892"><rect
+ id="rect12894"
+ width="445.14282"
+ height="64"
+ x="194.28572"
+ y="475.52304"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara3577">LDAP Content Synchronization Operation </flowPara><flowPara
+ id="flowPara3581">- refreshAndPersist</flowPara></flowRoot> <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.0861342px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 238,143.49926 L 238,444.31958"
+ id="path3597"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.08133781px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 464,147.49407 L 464,443.32477"
+ id="path3601"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow1Lend)"
+ d="M 244,149.40942 L 459,176.40942"
+ id="path3630" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow1Lend)"
+ d="M 455,184.40942 L 243,237.40942"
+ id="path3632" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 239.5,343.79723 L 454.5,370.79723"
+ id="path5496" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="19"
+ y="149.40942"
+ id="text5502"><tspan
+ sodipodi:role="line"
+ x="19"
+ y="149.40942"
+ id="tspan5518"
+ style="font-weight:bold">1.<tspan
+ style="font-weight:normal"
+ id="tspan5753"> Same as refreshOnly request,</tspan></tspan><tspan
+ sodipodi:role="line"
+ x="19"
+ y="164.40942"
+ style="font-weight:normal"
+ id="tspan5755">but refreshAndPersist mode</tspan><tspan
+ sodipodi:role="line"
+ x="19"
+ y="179.40942"
+ style="font-weight:normal"
+ id="tspan5757">set.</tspan><tspan
+ sodipodi:role="line"
+ x="19"
+ y="194.40942"
+ style="font-weight:bold"
+ id="tspan5751" /></text>
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="590"
+ y="113.40942"
+ id="text5506"><tspan
+ sodipodi:role="line"
+ id="tspan5508"
+ x="590"
+ y="113.40942">Server</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="92"
+ y="109.40942"
+ id="text5510"><tspan
+ sodipodi:role="line"
+ id="tspan5512"
+ x="92"
+ y="109.40942">Client</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="492"
+ y="155.40942"
+ id="text5520"><tspan
+ sodipodi:role="line"
+ x="492"
+ y="155.40942"
+ style="font-size:12px;font-weight:bold"
+ id="tspan5528">2a.<tspan
+ style="font-weight:normal"
+ id="tspan5789"> Same as refreshOnly mode.</tspan></tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="491.2998"
+ y="175.21997"
+ id="text5530"><tspan
+ sodipodi:role="line"
+ x="491.2998"
+ y="175.21997"
+ style="font-size:12px;font-weight:bold"
+ id="tspan5569">2b.<tspan
+ style="font-weight:normal"
+ id="tspan5805"> This time, send a Sync Info</tspan></tspan><tspan
+ sodipodi:role="line"
+ x="491.2998"
+ y="190.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5807">Message to client indicating refresh</tspan><tspan
+ sodipodi:role="line"
+ x="491.2998"
+ y="205.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5809">stage is done and then enters the </tspan><tspan
+ sodipodi:role="line"
+ x="491.2998"
+ y="220.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5811">persist stage</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="20.009766"
+ y="220.21997"
+ id="text5571"><tspan
+ sodipodi:role="line"
+ x="20.009766"
+ y="220.21997"
+ style="font-size:12px;font-weight:bold"
+ id="tspan5599">3. <tspan
+ style="font-weight:normal"
+ id="tspan5823">After receiving the message, </tspan></tspan><tspan
+ sodipodi:role="line"
+ x="20.009766"
+ y="235.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5817">the client will construct a </tspan><tspan
+ sodipodi:role="line"
+ x="20.009766"
+ y="250.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5819">synchronized copy as described</tspan><tspan
+ sodipodi:role="line"
+ x="20.009766"
+ y="265.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5821">in the refreshOnly mode.</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="490.00977"
+ y="266.21997"
+ id="text5603"><tspan
+ sodipodi:role="line"
+ x="490.00977"
+ y="266.21997"
+ style="font-size:12px;font-weight:bold"
+ id="tspan5635">4.<tspan
+ style="font-weight:normal"
+ id="tspan5629"> Server can now send change </tspan></tspan><tspan
+ sodipodi:role="line"
+ x="490.00977"
+ y="281.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5850">notifications based on original Sync</tspan><tspan
+ sodipodi:role="line"
+ x="490.00977"
+ y="296.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5852">Search Request</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="487.82422"
+ y="366.21997"
+ id="text5637"><tspan
+ sodipodi:role="line"
+ x="487.82422"
+ y="366.21997"
+ style="font-size:12px;font-weight:bold"
+ id="tspan5669">6.<tspan
+ style="font-weight:normal"
+ id="tspan5917"> Server may terminate Sync Operation.</tspan></tspan><tspan
+ sodipodi:role="line"
+ x="487.82422"
+ y="381.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5919">If it doesn't provide a cookie, a full</tspan><tspan
+ sodipodi:role="line"
+ x="487.82422"
+ y="396.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5921">refresh is needed by client.</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="19.681641"
+ y="303.21997"
+ id="text5675"><tspan
+ sodipodi:role="line"
+ x="19.681641"
+ y="303.21997"
+ style="font-size:12px;font-weight:bold"
+ id="tspan5685">5a.<tspan
+ style="font-weight:normal"
+ id="tspan5712"> For returned entries the </tspan></tspan><tspan
+ sodipodi:role="line"
+ x="19.681641"
+ y="318.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5854">SearchResultEntry will have the </tspan><tspan
+ sodipodi:role="line"
+ x="19.681641"
+ y="333.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5858">Sync State Control set to either;</tspan><tspan
+ sodipodi:role="line"
+ x="19.681641"
+ y="348.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5714">add, delete or modify</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 459,379.66689 L 247,432.66689"
+ id="path5691" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 244.5,244.79723 L 459.5,271.79723"
+ id="path5825" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 452,280.66689 L 240,333.66689"
+ id="path5831" />
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="18.818359"
+ y="369.21997"
+ id="text5869"><tspan
+ sodipodi:role="line"
+ x="18.818359"
+ y="369.21997"
+ style="font-size:12px;font-weight:bold"
+ id="tspan5895">5b.<tspan
+ style="font-weight:normal"
+ id="tspan5899"> Waits for server to send entries</tspan></tspan><tspan
+ sodipodi:role="line"
+ x="18.818359"
+ y="384.21997"
+ style="font-size:12px;font-weight:bold"
+ id="tspan5901" /></text>
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="17.103516"
+ y="437.88306"
+ id="text5923"><tspan
+ sodipodi:role="line"
+ x="17.103516"
+ y="437.88306"
+ style="font-size:12px;font-weight:bold"
+ id="tspan5925">7.<tspan
+ style="font-weight:normal"
+ id="tspan5931"> Client refreshes if disconnects </tspan></tspan><tspan
+ sodipodi:role="line"
+ x="17.103516"
+ y="452.88306"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5933">and provides last syncCookie if it</tspan><tspan
+ sodipodi:role="line"
+ x="17.103516"
+ y="467.88306"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5937">has one.</tspan><tspan
+ sodipodi:role="line"
+ x="17.103516"
+ y="482.88306"
+ style="font-size:12px;font-weight:bold"
+ id="tspan5929" /></text>
+ </g>
+</svg>
diff --git a/doc/guide/images/src/ldap-sync-refreshonly.svg b/doc/guide/images/src/ldap-sync-refreshonly.svg
new file mode 100644
index 0000000..efb68d0
--- /dev/null
+++ b/doc/guide/images/src/ldap-sync-refreshonly.svg
@@ -0,0 +1,4814 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="524.40942"
+ id="svg7893"
+ inkscape:version="0.46"
+ sodipodi:docbase="/home/ghenry/Desktop"
+ sodipodi:docname="ldap-sync-refreshonly.svg"
+ sodipodi:version="0.32"
+ width="744.09448"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ version="1.0"
+ inkscape:export-filename="/home/ghenry/Desktop/ldap-sync-refreshOnly.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <metadata
+ id="metadata2563">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:title>Firewall2</dc:title>
+ <dc:description />
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>wall</rdf:li>
+ <rdf:li>brick</rdf:li>
+ <rdf:li>computer</rdf:li>
+ <rdf:li>networksym</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ <dc:publisher>
+ <cc:Agent
+ rdf:about="http://www.openclipart.org/">
+ <dc:title>Open Clip Art Library</dc:title>
+ </cc:Agent>
+ </dc:publisher>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>HASH(0x89c79d4)</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:rights>
+ <cc:Agent>
+ <dc:title>HASH(0x89c79d4)</dc:title>
+ </cc:Agent>
+ </dc:rights>
+ <dc:date />
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <cc:license
+ rdf:resource="http://web.resource.org/cc/PublicDomain" />
+ <dc:language>en</dc:language>
+ </cc:Work>
+ <cc:License
+ rdf:about="http://web.resource.org/cc/PublicDomain">
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Reproduction" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Distribution" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs7895">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 372.04724 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="1052.3622 : 372.04724 : 1"
+ inkscape:persp3d-origin="526.18109 : 248.03149 : 1"
+ id="perspective6943" />
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible">
+ <path
+ id="path17680"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lstart"
+ style="overflow:visible">
+ <path
+ id="path17677"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(0.8,0,0,0.8,10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend"
+ style="overflow:visible">
+ <path
+ id="path17686"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mstart"
+ style="overflow:visible">
+ <path
+ id="path17683"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(0.4,0,0,0.4,4,0)" />
+ </marker>
+ <linearGradient
+ id="linearGradient6508">
+ <stop
+ id="stop6509"
+ offset="0.0000000"
+ style="stop-color:#ff0000;stop-opacity:1.0000000;" />
+ <stop
+ id="stop6511"
+ offset="0.64370060"
+ style="stop-color:#ffb900;stop-opacity:1.0000000;" />
+ <stop
+ id="stop6512"
+ offset="0.79038113"
+ style="stop-color:#ffff00;stop-opacity:0.84102565;" />
+ <stop
+ id="stop6510"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.21568628;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient13376">
+ <stop
+ style="stop-color:#d4d4d4;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop13377" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.49803922;"
+ offset="0.50000000"
+ id="stop13380" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="1.0000000"
+ id="stop13378" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient12744">
+ <stop
+ style="stop-color:#839da4;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop12745" />
+ <stop
+ style="stop-color:#496d77;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop12746" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient10810">
+ <stop
+ style="stop-color:#0e0000;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop10811" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1.0000000;"
+ offset="0.50000000"
+ id="stop10814" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop10812" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient11442">
+ <stop
+ style="stop-color:#6e6e6e;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop11443" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop11444" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient14160">
+ <stop
+ style="stop-color:#4af853;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop14161" />
+ <stop
+ style="stop-color:#68b96d;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop14162" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient14835">
+ <stop
+ style="stop-color:#bed1d0;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop14836" />
+ <stop
+ style="stop-color:#52727b;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop14837" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient29203">
+ <stop
+ style="stop-color:#d3d3d3;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop29205" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop29207" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient6658">
+ <stop
+ style="stop-color:#677883;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop6659" />
+ <stop
+ style="stop-color:#677883;stop-opacity:0.0000000;"
+ offset="1.0000000"
+ id="stop6660" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient41493">
+ <stop
+ style="stop-color:#181818;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop41495" />
+ <stop
+ style="stop-color:#5e5e5e;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop41497" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient12759">
+ <stop
+ style="stop-color:#b4b4b4;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop12761" />
+ <stop
+ style="stop-color:#d7d8de;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop12763" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient21825">
+ <stop
+ style="stop-color:#808080;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop21827" />
+ <stop
+ style="stop-color:#5e5e5e;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop21829" />
+ </linearGradient>
+ <radialGradient
+ xlink:href="#linearGradient13376"
+ r="31.620827"
+ id="radialGradient25527"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.776429,0,0,0.659114,-120.5524,673.5049)"
+ fy="254.35735"
+ fx="-19.038713"
+ cy="253.63734"
+ cx="-19.261518" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12759"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient25525"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,-145.458,730.6984)" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient21825"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient25403"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,-145.2247,712.702)" />
+ <linearGradient
+ y2="1977.8738"
+ y1="1924.0137"
+ xlink:href="#linearGradient41493"
+ x2="-35.763195"
+ x1="-39.828941"
+ id="linearGradient25401"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.672454,0,0,0.374188,-3.473342,95.2718)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient25353"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.270019,0,0,0.370779,-149.3489,792.5495)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient26976"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-90.06505,808.8095)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient26972"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.05831,0,0,0.803858,616.249,115.0105)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient26974"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.838868,0,0,0.530755,508.4408,137.664)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient26964"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.05831,0,0,0.803858,616.249,115.0105)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient26966"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.838868,0,0,0.530755,508.4408,137.664)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28284"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28286"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28288"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28290"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28274"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28276"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28278"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28280"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28264"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28266"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28268"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28270"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28254"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28256"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28258"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28260"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28244"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28246"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28248"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28250"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28234"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28236"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28238"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28240"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28224"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28226"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28228"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28230"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28214"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28216"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28218"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28220"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28208"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-125.9178,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28210"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="97.536598"
+ cy="113.726"
+ cx="97.536598" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28204"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-121.573,808.7592)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28206"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="100.67591"
+ cy="113.726"
+ cx="100.67591" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28200"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-116.9703,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28202"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="104.00187"
+ cy="113.726"
+ cx="104.00187" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28196"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-112.6254,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28198"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="107.14119"
+ cy="113.726"
+ cx="107.14119" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28192"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-108.4824,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28194"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="110.13468"
+ cy="113.726"
+ cx="110.13468" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28188"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-104.1375,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28190"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="113.27399"
+ cy="113.726"
+ cx="113.27399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28184"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-99.77797,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28186"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="116.42374"
+ cy="113.726"
+ cx="116.42374" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28180"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-95.43307,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28182"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="119.56305"
+ cy="113.726"
+ cx="119.56305" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28172"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28174"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28176"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28178"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28162"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28164"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28168"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28152"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28154"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28156"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28158"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28142"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28144"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28146"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28148"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28132"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28134"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28136"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28138"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28122"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28124"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28126"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28128"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28112"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28114"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28116"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28118"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28102"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28104"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28106"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28108"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28096"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-161.2375,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28098"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="71.480988"
+ cy="113.726"
+ cx="71.480988" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28092"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-156.8927,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28094"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="74.620308"
+ cy="113.726"
+ cx="74.620308" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28088"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-152.29,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28090"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="77.946259"
+ cy="113.726"
+ cx="77.946259" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28084"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-147.9451,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28086"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="81.085587"
+ cy="113.726"
+ cx="81.085587" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28080"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-143.8021,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28082"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="84.079071"
+ cy="113.726"
+ cx="84.079071" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28076"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-139.4573,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28078"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="87.218399"
+ cy="113.726"
+ cx="87.218399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28072"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-135.098,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28074"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="90.368126"
+ cy="113.726"
+ cx="90.368126" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28068"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-130.7531,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28070"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="93.507462"
+ cy="113.726"
+ cx="93.507462" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28060"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28062"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28064"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28066"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28050"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28052"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28054"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28056"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28040"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28042"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28044"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28046"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28030"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28032"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28034"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28036"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28020"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28022"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28024"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28026"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28010"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28012"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28014"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28016"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28000"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28002"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28004"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28006"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27990"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27992"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27994"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27996"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27984"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-197.2616,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27986"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="45.452175"
+ cy="113.726"
+ cx="45.452175" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27980"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-192.9168,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27982"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="48.591496"
+ cy="113.726"
+ cx="48.591496" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27976"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-188.3141,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27978"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="51.91745"
+ cy="113.726"
+ cx="51.91745" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27972"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-183.9692,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27974"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="55.05677"
+ cy="113.726"
+ cx="55.05677" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27968"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-179.8262,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27970"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="58.050255"
+ cy="113.726"
+ cx="58.050255" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27964"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-175.4813,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27966"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="61.189575"
+ cy="113.726"
+ cx="61.189575" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27960"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-171.122,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27962"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="64.339317"
+ cy="113.726"
+ cx="64.339317" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27956"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-166.7771,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27958"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="67.478638"
+ cy="113.726"
+ cx="67.478638" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27928"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27930"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27932"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27934"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27918"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27920"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27922"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27924"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27908"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27910"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27912"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27914"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27898"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27900"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27902"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27904"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27888"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27890"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27892"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27894"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27878"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27880"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27882"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27884"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27868"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27870"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27872"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27874"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27858"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27860"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27862"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27864"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27848"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27850"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27852"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27854"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27838"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27840"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27842"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27844"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27828"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27830"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27832"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27834"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27818"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27820"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27822"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27824"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27808"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27810"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27812"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27814"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27798"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27800"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27802"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27804"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27788"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27790"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27792"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27794"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27778"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27780"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27782"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27784"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27768"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27770"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27772"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27774"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27758"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27760"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27762"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27764"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27748"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27750"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27752"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27754"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27738"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27740"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27742"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27744"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27728"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27730"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27732"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27734"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27718"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27720"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27722"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27724"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27708"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27710"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27712"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27714"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27698"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27700"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27702"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27704"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28432"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-126.1386,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28434"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="97.536598"
+ cy="113.726"
+ cx="97.536598" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28428"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-121.7938,817.6604)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28430"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="100.67591"
+ cy="113.726"
+ cx="100.67591" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28424"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-117.1911,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28426"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="104.00187"
+ cy="113.726"
+ cx="104.00187" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28420"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-112.8462,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28422"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="107.14119"
+ cy="113.726"
+ cx="107.14119" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28416"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-108.7032,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28418"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="110.13468"
+ cy="113.726"
+ cx="110.13468" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28412"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-104.3583,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28414"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="113.27399"
+ cy="113.726"
+ cx="113.27399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28408"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-99.99876,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28410"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="116.42374"
+ cy="113.726"
+ cx="116.42374" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28404"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-95.65386,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28406"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="119.56305"
+ cy="113.726"
+ cx="119.56305" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28400"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-161.4583,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28402"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="71.480988"
+ cy="113.726"
+ cx="71.480988" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28396"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-157.1135,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28398"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="74.620308"
+ cy="113.726"
+ cx="74.620308" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28392"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-152.5108,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28394"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="77.946259"
+ cy="113.726"
+ cx="77.946259" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28388"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-148.1659,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28390"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="81.085587"
+ cy="113.726"
+ cx="81.085587" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28384"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-144.0229,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28386"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="84.079071"
+ cy="113.726"
+ cx="84.079071" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28380"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-139.6781,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28382"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="87.218399"
+ cy="113.726"
+ cx="87.218399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28376"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-135.3188,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28378"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="90.368126"
+ cy="113.726"
+ cx="90.368126" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28372"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-130.9739,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28374"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="93.507462"
+ cy="113.726"
+ cx="93.507462" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28368"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-197.4824,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28370"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="45.452175"
+ cy="113.726"
+ cx="45.452175" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28364"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-193.1376,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28366"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="48.591496"
+ cy="113.726"
+ cx="48.591496" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28360"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-188.5349,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28362"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="51.91745"
+ cy="113.726"
+ cx="51.91745" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28356"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-184.19,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28358"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="55.05677"
+ cy="113.726"
+ cx="55.05677" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28352"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-180.047,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28354"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="58.050255"
+ cy="113.726"
+ cx="58.050255" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28348"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-175.7021,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28350"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="61.189575"
+ cy="113.726"
+ cx="61.189575" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28344"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-171.3428,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28346"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="64.339317"
+ cy="113.726"
+ cx="64.339317" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28340"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-166.9979,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28342"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="67.478638"
+ cy="113.726"
+ cx="67.478638" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28438"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-90.25863,817.7848)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12759"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient36281"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,-149.897,802.9053)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36283"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36285"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36287"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36289"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36291"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36293"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36295"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36297"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36299"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36301"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36303"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36305"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36307"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36309"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36311"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36313"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36315"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36317"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36319"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36321"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36323"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36325"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36327"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36329"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36331"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.5348,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36333"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-185.7196,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36335"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-189.35,879.6484)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36337"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-180.274,879.6484)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36339"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-183.9043,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36341"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-182.0892,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient21825"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient36343"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,-149.6637,784.9089)" />
+ <linearGradient
+ y2="1977.8738"
+ y1="1924.0137"
+ xlink:href="#linearGradient41493"
+ x2="-35.763195"
+ x1="-39.828941"
+ id="linearGradient36345"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.672454,0,0,0.374188,-7.912301,167.4787)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36347"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.5296,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36349"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-185.7144,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36351"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-189.3448,881.7646)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36353"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-180.2688,881.7646)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36355"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-183.8991,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36357"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-182.084,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36359"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-198.4916,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36361"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-190.046,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36363"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.2306,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36365"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-192.8611,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36367"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-195.6763,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36369"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.74272,0,0,0.445632,-87.12747,420.4818)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36371"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36373"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36375"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.787283,0,0,0.475341,-91.66274,388.2275)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36377"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36379"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36381"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.270019,0,0,0.370779,-153.7879,864.7564)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12759"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient35867"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,-141.9847,635.4266)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35869"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35871"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35873"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35875"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35877"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35879"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35881"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35883"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35885"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35887"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35889"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35891"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35893"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35895"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35897"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35899"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35901"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35903"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35905"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35907"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35909"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35911"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35913"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35915"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35917"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35919"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35921"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35923"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35925"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35927"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35929"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35931"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35933"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35935"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35937"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35939"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35941"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35943"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35945"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35947"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35949"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35951"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35953"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35955"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35957"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35959"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35961"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35963"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35965"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-179.6225,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35967"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-168.7312,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35969"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-163.2856,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35971"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-161.4702,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35973"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-165.1007,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35975"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-177.8073,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35977"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-166.9159,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35979"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-181.4377,712.1697)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35981"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-170.5465,712.1697)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35983"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-172.3617,712.1697)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35985"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-175.992,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35987"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-174.1769,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient21825"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient35989"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,-141.7514,617.4302)" />
+ <linearGradient
+ y2="1977.8738"
+ y1="1924.0137"
+ xlink:href="#linearGradient41493"
+ x2="-35.763195"
+ x1="-39.828941"
+ id="linearGradient35991"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(2.672454,0.374188)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35993"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-179.6173,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35995"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-168.726,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35997"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-163.2804,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35999"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-161.465,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36001"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-165.0955,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36003"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-177.8021,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36005"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-166.9107,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36007"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-181.4325,714.2859)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36009"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-170.5413,714.2859)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36011"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-172.3565,714.2859)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36013"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-175.9868,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36015"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-174.1717,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36017"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-190.5793,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36019"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-182.1337,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36021"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-179.3183,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36023"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-184.9488,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36025"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.764,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36027"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.74272,0,0,0.445632,-79.21517,253.0031)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36029"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36031"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36033"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.787283,0,0,0.475341,-83.75044,220.7488)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36035"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36037"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36039"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.270019,0,0,0.370779,-145.8756,697.2777)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient13376"
+ r="31.620827"
+ id="radialGradient12151"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.20227,0,0,0.454077,6.691668,-148.3193)"
+ fy="254.35735"
+ fx="-19.038713"
+ cy="253.63734"
+ cx="-19.261518" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12744"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient12153"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,1.691668,-145.8193)" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient14835"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient12155"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,1.924904,-161.8157)" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12744"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient12157"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.350818,114.6621,-134.6472)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12159"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-59.65453)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12161"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-61.33423)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12163"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-63.01391)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12165"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.09869,-64.40064)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <inkscape:perspective
+ id="perspective3612"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ sodipodi:type="inkscape:persp3d" />
+ </defs>
+ <sodipodi:namedview
+ bordercolor="#666666"
+ borderopacity="1.0"
+ id="base"
+ inkscape:current-layer="layer1"
+ inkscape:cx="344.82324"
+ inkscape:cy="227.55258"
+ inkscape:document-units="px"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:window-height="722"
+ inkscape:window-width="1014"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:zoom="1"
+ pagecolor="#ffffff"
+ width="1052.3622px"
+ height="744.09448px"
+ showgrid="false" />
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1">
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot12890"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Arial"
+ transform="translate(-51.99247,-442.27533)"><flowRegion
+ id="flowRegion12892"><rect
+ id="rect12894"
+ width="445.14282"
+ height="64"
+ x="194.28572"
+ y="475.52304"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara3577">LDAP Content Synchronization Operation </flowPara><flowPara
+ id="flowPara3581">- refreshOnly </flowPara></flowRoot> <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 238,143.40942 L 238,398.40942"
+ id="path3597"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 464,147.40942 L 464,400.40942"
+ id="path3601"
+ inkscape:connector-type="polyline" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow1Lend)"
+ d="M 244,149.40942 L 459,176.40942"
+ id="path3630" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow1Lend)"
+ d="M 455,184.40942 L 243,237.40942"
+ id="path3632" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 245.5,303.79723 L 460.5,330.79723"
+ id="path5496" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 453,201.66689 L 241,254.66689"
+ id="path5498" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 455,218.66689 L 243,271.66689"
+ id="path5500" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="19"
+ y="149.40942"
+ id="text5502"><tspan
+ sodipodi:role="line"
+ id="tspan5504"
+ x="19"
+ y="149.40942"><tspan
+ style="font-weight:bold"
+ id="tspan5540">1.</tspan> Initial client copy Sync</tspan><tspan
+ sodipodi:role="line"
+ x="19"
+ y="164.40942"
+ id="tspan5514">request - search request</tspan><tspan
+ sodipodi:role="line"
+ x="19"
+ y="179.40942"
+ id="tspan5516">with Sync Request Control</tspan><tspan
+ sodipodi:role="line"
+ x="19"
+ y="194.40942"
+ id="tspan5518">with mode set to 'refreshOnly'</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="590"
+ y="113.40942"
+ id="text5506"><tspan
+ sodipodi:role="line"
+ id="tspan5508"
+ x="590"
+ y="113.40942">Server</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="92"
+ y="109.40942"
+ id="text5510"><tspan
+ sodipodi:role="line"
+ id="tspan5512"
+ x="92"
+ y="109.40942">Client</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="501"
+ y="154.40942"
+ id="text5520"><tspan
+ sodipodi:role="line"
+ id="tspan5522"
+ x="501"
+ y="154.40942"
+ style="font-size:12px"><tspan
+ style="font-weight:bold"
+ id="tspan5542">2a.</tspan> Returns content matching search </tspan><tspan
+ sodipodi:role="line"
+ x="501"
+ y="169.40942"
+ style="font-size:12px"
+ id="tspan5524">and with each entry provides a Sync</tspan><tspan
+ sodipodi:role="line"
+ x="501"
+ y="184.40942"
+ style="font-size:12px"
+ id="tspan5526">State Control which contains the </tspan><tspan
+ sodipodi:role="line"
+ x="501"
+ y="199.40942"
+ style="font-size:12px"
+ id="tspan5528">'entryUUID'</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="501.2998"
+ y="217.21997"
+ id="text5530"><tspan
+ sodipodi:role="line"
+ x="501.2998"
+ y="217.21997"
+ style="font-size:12px;font-weight:bold"
+ id="tspan5538">2b.<tspan
+ style="font-weight:normal"
+ id="tspan5561"> Follows with a SearchResultDone </tspan></tspan><tspan
+ sodipodi:role="line"
+ x="501.2998"
+ y="232.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5563">with a 'Sync Done Control' which</tspan><tspan
+ sodipodi:role="line"
+ x="501.2998"
+ y="247.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5567">provides the syncCookie - this cookie</tspan><tspan
+ sodipodi:role="line"
+ x="501.2998"
+ y="262.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5569">represents the session state.</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="16.009766"
+ y="271.21997"
+ id="text5571"><tspan
+ sodipodi:role="line"
+ x="16.009766"
+ y="271.21997"
+ style="font-size:12px;font-weight:bold"
+ id="tspan5581">3. <tspan
+ style="font-weight:normal"
+ id="tspan5597">Polls for updates providing the </tspan></tspan><tspan
+ sodipodi:role="line"
+ x="16.009766"
+ y="286.21997"
+ style="font-size:12px;font-weight:bold"
+ id="tspan5599"><tspan
+ style="font-weight:normal"
+ id="tspan5601">previously issued syncCookie </tspan></tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="498.00977"
+ y="330.21997"
+ id="text5603"><tspan
+ sodipodi:role="line"
+ x="498.00977"
+ y="330.21997"
+ style="font-size:12px;font-weight:bold"
+ id="tspan5613">4a.<tspan
+ style="font-weight:normal"
+ id="tspan5629"> Use present or delete phase?</tspan></tspan><tspan
+ sodipodi:role="line"
+ x="498.00977"
+ y="345.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5631">Both can be used, present brings </tspan><tspan
+ sodipodi:role="line"
+ x="498.00977"
+ y="360.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5633">client copy up to a point where delete</tspan><tspan
+ sodipodi:role="line"
+ x="498.00977"
+ y="375.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5635">can begin.</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="498.82422"
+ y="398.21997"
+ id="text5637"><tspan
+ sodipodi:role="line"
+ x="498.82422"
+ y="398.21997"
+ style="font-size:12px;font-weight:bold"
+ id="tspan5647">4b.<tspan
+ style="font-weight:normal"
+ id="tspan5663"> Server uses syncCookie as an </tspan></tspan><tspan
+ sodipodi:role="line"
+ x="498.82422"
+ y="413.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5665">indicator of what client got before and</tspan><tspan
+ sodipodi:role="line"
+ x="498.82422"
+ y="428.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5667">then sends copies of entries that have</tspan><tspan
+ sodipodi:role="line"
+ x="498.82422"
+ y="443.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5669">changed. <tspan
+ style="font-weight:bold"
+ id="tspan5671">All<tspan
+ style="font-weight:normal"
+ id="tspan5673"> attributes are sent.</tspan></tspan></tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="11.681641"
+ y="422.21997"
+ id="text5675"><tspan
+ sodipodi:role="line"
+ x="11.681641"
+ y="422.21997"
+ style="font-size:12px;font-weight:bold"
+ id="tspan5685">5.<tspan
+ style="font-weight:normal"
+ id="tspan5712"> Repeat using syncCookie, i.e.</tspan></tspan><tspan
+ sodipodi:role="line"
+ x="11.681641"
+ y="437.21997"
+ style="font-size:12px;font-weight:normal"
+ id="tspan5714">go back to step 3.</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 458,339.66689 L 246,392.66689"
+ id="path5691" />
+ </g>
+</svg>
diff --git a/doc/guide/images/src/mirrormode.dia b/doc/guide/images/src/mirrormode.dia
new file mode 100644
index 0000000..3f159ea
--- /dev/null
+++ b/doc/guide/images/src/mirrormode.dia
Binary files differ
diff --git a/doc/guide/images/src/n-way-multi-provider.dia b/doc/guide/images/src/n-way-multi-provider.dia
new file mode 100644
index 0000000..3f159ea
--- /dev/null
+++ b/doc/guide/images/src/n-way-multi-provider.dia
Binary files differ
diff --git a/doc/guide/images/src/n-way-multi-provider.svg b/doc/guide/images/src/n-way-multi-provider.svg
new file mode 100644
index 0000000..f0c3717
--- /dev/null
+++ b/doc/guide/images/src/n-way-multi-provider.svg
@@ -0,0 +1,5293 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="524.40942"
+ id="svg7893"
+ inkscape:version="0.46"
+ sodipodi:docbase="/home/ghenry/Desktop"
+ sodipodi:docname="n-way-multi-provider.svg"
+ sodipodi:version="0.32"
+ width="744.09448"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ version="1.0"
+ inkscape:export-filename="/home/ghenry/Desktop/n-way-multi-provider.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <metadata
+ id="metadata2563">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:title>Firewall2</dc:title>
+ <dc:description />
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>wall</rdf:li>
+ <rdf:li>brick</rdf:li>
+ <rdf:li>computer</rdf:li>
+ <rdf:li>networksym</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ <dc:publisher>
+ <cc:Agent
+ rdf:about="http://www.openclipart.org/">
+ <dc:title>Open Clip Art Library</dc:title>
+ </cc:Agent>
+ </dc:publisher>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>HASH(0x89c79d4)</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:rights>
+ <cc:Agent>
+ <dc:title>HASH(0x89c79d4)</dc:title>
+ </cc:Agent>
+ </dc:rights>
+ <dc:date />
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <cc:license
+ rdf:resource="http://web.resource.org/cc/PublicDomain" />
+ <dc:language>en</dc:language>
+ </cc:Work>
+ <cc:License
+ rdf:about="http://web.resource.org/cc/PublicDomain">
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Reproduction" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Distribution" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs7895">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 372.04724 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="1052.3622 : 372.04724 : 1"
+ inkscape:persp3d-origin="526.18109 : 248.03149 : 1"
+ id="perspective6943" />
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible">
+ <path
+ id="path17680"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lstart"
+ style="overflow:visible">
+ <path
+ id="path17677"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(0.8,0,0,0.8,10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend"
+ style="overflow:visible">
+ <path
+ id="path17686"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mstart"
+ style="overflow:visible">
+ <path
+ id="path17683"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(0.4,0,0,0.4,4,0)" />
+ </marker>
+ <linearGradient
+ id="linearGradient6508">
+ <stop
+ id="stop6509"
+ offset="0.0000000"
+ style="stop-color:#ff0000;stop-opacity:1.0000000;" />
+ <stop
+ id="stop6511"
+ offset="0.64370060"
+ style="stop-color:#ffb900;stop-opacity:1.0000000;" />
+ <stop
+ id="stop6512"
+ offset="0.79038113"
+ style="stop-color:#ffff00;stop-opacity:0.84102565;" />
+ <stop
+ id="stop6510"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.21568628;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient13376">
+ <stop
+ style="stop-color:#d4d4d4;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop13377" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.49803922;"
+ offset="0.50000000"
+ id="stop13380" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="1.0000000"
+ id="stop13378" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient12744">
+ <stop
+ style="stop-color:#839da4;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop12745" />
+ <stop
+ style="stop-color:#496d77;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop12746" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient10810">
+ <stop
+ style="stop-color:#0e0000;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop10811" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1.0000000;"
+ offset="0.50000000"
+ id="stop10814" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop10812" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient11442">
+ <stop
+ style="stop-color:#6e6e6e;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop11443" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop11444" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient14160">
+ <stop
+ style="stop-color:#4af853;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop14161" />
+ <stop
+ style="stop-color:#68b96d;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop14162" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient14835">
+ <stop
+ style="stop-color:#bed1d0;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop14836" />
+ <stop
+ style="stop-color:#52727b;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop14837" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient29203">
+ <stop
+ style="stop-color:#d3d3d3;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop29205" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop29207" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient6658">
+ <stop
+ style="stop-color:#677883;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop6659" />
+ <stop
+ style="stop-color:#677883;stop-opacity:0.0000000;"
+ offset="1.0000000"
+ id="stop6660" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient41493">
+ <stop
+ style="stop-color:#181818;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop41495" />
+ <stop
+ style="stop-color:#5e5e5e;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop41497" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient12759">
+ <stop
+ style="stop-color:#b4b4b4;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop12761" />
+ <stop
+ style="stop-color:#d7d8de;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop12763" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient21825">
+ <stop
+ style="stop-color:#808080;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop21827" />
+ <stop
+ style="stop-color:#5e5e5e;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop21829" />
+ </linearGradient>
+ <radialGradient
+ xlink:href="#linearGradient13376"
+ r="31.620827"
+ id="radialGradient25527"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.776429,0,0,0.659114,-120.5524,673.5049)"
+ fy="254.35735"
+ fx="-19.038713"
+ cy="253.63734"
+ cx="-19.261518" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12759"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient25525"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,-145.458,730.6984)" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient21825"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient25403"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,-145.2247,712.702)" />
+ <linearGradient
+ y2="1977.8738"
+ y1="1924.0137"
+ xlink:href="#linearGradient41493"
+ x2="-35.763195"
+ x1="-39.828941"
+ id="linearGradient25401"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.672454,0,0,0.374188,-3.473342,95.2718)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient25353"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.270019,0,0,0.370779,-149.3489,792.5495)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient26976"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-90.06505,808.8095)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient26972"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.05831,0,0,0.803858,616.249,115.0105)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient26974"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.838868,0,0,0.530755,508.4408,137.664)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient26964"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.05831,0,0,0.803858,616.249,115.0105)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient26966"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.838868,0,0,0.530755,508.4408,137.664)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28284"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28286"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28288"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28290"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28274"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28276"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28278"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28280"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28264"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28266"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28268"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28270"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28254"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28256"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28258"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28260"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28244"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28246"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28248"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28250"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28234"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28236"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28238"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28240"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28224"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28226"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28228"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28230"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28214"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28216"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28218"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28220"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28208"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-125.9178,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28210"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="97.536598"
+ cy="113.726"
+ cx="97.536598" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28204"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-121.573,808.7592)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28206"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="100.67591"
+ cy="113.726"
+ cx="100.67591" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28200"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-116.9703,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28202"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="104.00187"
+ cy="113.726"
+ cx="104.00187" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28196"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-112.6254,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28198"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="107.14119"
+ cy="113.726"
+ cx="107.14119" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28192"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-108.4824,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28194"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="110.13468"
+ cy="113.726"
+ cx="110.13468" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28188"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-104.1375,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28190"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="113.27399"
+ cy="113.726"
+ cx="113.27399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28184"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-99.77797,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28186"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="116.42374"
+ cy="113.726"
+ cx="116.42374" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28180"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-95.43307,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28182"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="119.56305"
+ cy="113.726"
+ cx="119.56305" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28172"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28174"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28176"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28178"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28162"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28164"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28168"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28152"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28154"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28156"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28158"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28142"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28144"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28146"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28148"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28132"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28134"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28136"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28138"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28122"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28124"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28126"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28128"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28112"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28114"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28116"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28118"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28102"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28104"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28106"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28108"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28096"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-161.2375,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28098"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="71.480988"
+ cy="113.726"
+ cx="71.480988" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28092"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-156.8927,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28094"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="74.620308"
+ cy="113.726"
+ cx="74.620308" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28088"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-152.29,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28090"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="77.946259"
+ cy="113.726"
+ cx="77.946259" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28084"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-147.9451,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28086"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="81.085587"
+ cy="113.726"
+ cx="81.085587" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28080"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-143.8021,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28082"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="84.079071"
+ cy="113.726"
+ cx="84.079071" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28076"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-139.4573,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28078"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="87.218399"
+ cy="113.726"
+ cx="87.218399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28072"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-135.098,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28074"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="90.368126"
+ cy="113.726"
+ cx="90.368126" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28068"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-130.7531,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28070"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="93.507462"
+ cy="113.726"
+ cx="93.507462" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28060"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28062"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28064"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28066"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28050"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28052"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28054"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28056"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28040"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28042"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28044"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28046"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28030"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28032"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28034"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28036"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28020"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28022"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28024"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28026"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28010"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28012"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28014"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28016"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28000"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28002"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28004"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28006"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27990"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27992"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27994"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27996"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27984"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-197.2616,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27986"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="45.452175"
+ cy="113.726"
+ cx="45.452175" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27980"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-192.9168,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27982"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="48.591496"
+ cy="113.726"
+ cx="48.591496" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27976"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-188.3141,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27978"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="51.91745"
+ cy="113.726"
+ cx="51.91745" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27972"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-183.9692,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27974"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="55.05677"
+ cy="113.726"
+ cx="55.05677" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27968"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-179.8262,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27970"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="58.050255"
+ cy="113.726"
+ cx="58.050255" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27964"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-175.4813,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27966"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="61.189575"
+ cy="113.726"
+ cx="61.189575" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27960"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-171.122,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27962"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="64.339317"
+ cy="113.726"
+ cx="64.339317" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27956"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-166.7771,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27958"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="67.478638"
+ cy="113.726"
+ cx="67.478638" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27928"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27930"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27932"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27934"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27918"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27920"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27922"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27924"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27908"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27910"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27912"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27914"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27898"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27900"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27902"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27904"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27888"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27890"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27892"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27894"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27878"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27880"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27882"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27884"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27868"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27870"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27872"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27874"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27858"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27860"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27862"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27864"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27848"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27850"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27852"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27854"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27838"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27840"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27842"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27844"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27828"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27830"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27832"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27834"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27818"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27820"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27822"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27824"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27808"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27810"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27812"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27814"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27798"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27800"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27802"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27804"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27788"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27790"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27792"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27794"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27778"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27780"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27782"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27784"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27768"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27770"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27772"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27774"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27758"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27760"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27762"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27764"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27748"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27750"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27752"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27754"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27738"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27740"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27742"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27744"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27728"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27730"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27732"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27734"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27718"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27720"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27722"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27724"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27708"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27710"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27712"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27714"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27698"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27700"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27702"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27704"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28432"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-126.1386,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28434"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="97.536598"
+ cy="113.726"
+ cx="97.536598" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28428"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-121.7938,817.6604)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28430"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="100.67591"
+ cy="113.726"
+ cx="100.67591" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28424"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-117.1911,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28426"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="104.00187"
+ cy="113.726"
+ cx="104.00187" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28420"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-112.8462,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28422"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="107.14119"
+ cy="113.726"
+ cx="107.14119" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28416"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-108.7032,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28418"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="110.13468"
+ cy="113.726"
+ cx="110.13468" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28412"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-104.3583,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28414"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="113.27399"
+ cy="113.726"
+ cx="113.27399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28408"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-99.99876,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28410"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="116.42374"
+ cy="113.726"
+ cx="116.42374" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28404"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-95.65386,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28406"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="119.56305"
+ cy="113.726"
+ cx="119.56305" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28400"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-161.4583,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28402"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="71.480988"
+ cy="113.726"
+ cx="71.480988" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28396"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-157.1135,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28398"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="74.620308"
+ cy="113.726"
+ cx="74.620308" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28392"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-152.5108,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28394"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="77.946259"
+ cy="113.726"
+ cx="77.946259" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28388"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-148.1659,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28390"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="81.085587"
+ cy="113.726"
+ cx="81.085587" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28384"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-144.0229,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28386"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="84.079071"
+ cy="113.726"
+ cx="84.079071" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28380"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-139.6781,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28382"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="87.218399"
+ cy="113.726"
+ cx="87.218399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28376"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-135.3188,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28378"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="90.368126"
+ cy="113.726"
+ cx="90.368126" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28372"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-130.9739,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28374"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="93.507462"
+ cy="113.726"
+ cx="93.507462" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28368"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-197.4824,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28370"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="45.452175"
+ cy="113.726"
+ cx="45.452175" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28364"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-193.1376,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28366"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="48.591496"
+ cy="113.726"
+ cx="48.591496" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28360"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-188.5349,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28362"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="51.91745"
+ cy="113.726"
+ cx="51.91745" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28356"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-184.19,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28358"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="55.05677"
+ cy="113.726"
+ cx="55.05677" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28352"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-180.047,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28354"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="58.050255"
+ cy="113.726"
+ cx="58.050255" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28348"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-175.7021,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28350"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="61.189575"
+ cy="113.726"
+ cx="61.189575" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28344"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-171.3428,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28346"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="64.339317"
+ cy="113.726"
+ cx="64.339317" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28340"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-166.9979,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28342"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="67.478638"
+ cy="113.726"
+ cx="67.478638" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28438"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-90.25863,817.7848)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12759"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient36281"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,-149.897,802.9053)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36283"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36285"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36287"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36289"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36291"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36293"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36295"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36297"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36299"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36301"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36303"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36305"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36307"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36309"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36311"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36313"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36315"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36317"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36319"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36321"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36323"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36325"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36327"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36329"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36331"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.5348,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36333"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-185.7196,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36335"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-189.35,879.6484)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36337"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-180.274,879.6484)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36339"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-183.9043,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36341"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-182.0892,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient21825"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient36343"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,-149.6637,784.9089)" />
+ <linearGradient
+ y2="1977.8738"
+ y1="1924.0137"
+ xlink:href="#linearGradient41493"
+ x2="-35.763195"
+ x1="-39.828941"
+ id="linearGradient36345"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.672454,0,0,0.374188,-7.912301,167.4787)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36347"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.5296,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36349"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-185.7144,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36351"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-189.3448,881.7646)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36353"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-180.2688,881.7646)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36355"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-183.8991,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36357"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-182.084,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36359"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-198.4916,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36361"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-190.046,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36363"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.2306,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36365"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-192.8611,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36367"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-195.6763,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36369"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.74272,0,0,0.445632,-87.12747,420.4818)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36371"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36373"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36375"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.787283,0,0,0.475341,-91.66274,388.2275)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36377"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36379"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36381"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.270019,0,0,0.370779,-153.7879,864.7564)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12759"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient35867"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,-141.9847,635.4266)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35869"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35871"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35873"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35875"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35877"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35879"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35881"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35883"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35885"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35887"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35889"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35891"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35893"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35895"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35897"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35899"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35901"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35903"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35905"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35907"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35909"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35911"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35913"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35915"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35917"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35919"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35921"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35923"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35925"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35927"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35929"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35931"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35933"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35935"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35937"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35939"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35941"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35943"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35945"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35947"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35949"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35951"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35953"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35955"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35957"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35959"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35961"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35963"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35965"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-179.6225,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35967"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-168.7312,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35969"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-163.2856,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35971"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-161.4702,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35973"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-165.1007,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35975"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-177.8073,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35977"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-166.9159,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35979"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-181.4377,712.1697)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35981"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-170.5465,712.1697)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35983"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-172.3617,712.1697)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35985"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-175.992,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35987"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-174.1769,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient21825"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient35989"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,-141.7514,617.4302)" />
+ <linearGradient
+ y2="1977.8738"
+ y1="1924.0137"
+ xlink:href="#linearGradient41493"
+ x2="-35.763195"
+ x1="-39.828941"
+ id="linearGradient35991"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(2.672454,0.374188)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35993"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-179.6173,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35995"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-168.726,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35997"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-163.2804,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35999"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-161.465,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36001"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-165.0955,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36003"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-177.8021,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36005"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-166.9107,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36007"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-181.4325,714.2859)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36009"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-170.5413,714.2859)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36011"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-172.3565,714.2859)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36013"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-175.9868,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36015"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-174.1717,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36017"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-190.5793,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36019"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-182.1337,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36021"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-179.3183,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36023"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-184.9488,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36025"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.764,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36027"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.74272,0,0,0.445632,-79.21517,253.0031)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36029"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36031"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36033"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.787283,0,0,0.475341,-83.75044,220.7488)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36035"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36037"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36039"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.270019,0,0,0.370779,-145.8756,697.2777)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient13376"
+ r="31.620827"
+ id="radialGradient12151"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.20227,0,0,0.454077,6.691668,-148.3193)"
+ fy="254.35735"
+ fx="-19.038713"
+ cy="253.63734"
+ cx="-19.261518" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12744"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient12153"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,1.691668,-145.8193)" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient14835"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient12155"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,1.924904,-161.8157)" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12744"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient12157"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.350818,114.6621,-134.6472)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12159"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-59.65453)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12161"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-61.33423)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12163"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-63.01391)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12165"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.09869,-64.40064)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ </defs>
+ <sodipodi:namedview
+ bordercolor="#666666"
+ borderopacity="1.0"
+ id="base"
+ inkscape:current-layer="layer1"
+ inkscape:cx="391.40904"
+ inkscape:cy="255.22111"
+ inkscape:document-units="px"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:window-height="722"
+ inkscape:window-width="1014"
+ inkscape:window-x="3"
+ inkscape:window-y="67"
+ inkscape:zoom="1"
+ pagecolor="#ffffff"
+ width="1052.3622px"
+ height="744.09448px"
+ showgrid="false" />
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1">
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot12890"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial"
+ transform="translate(51.007531,-424.27533)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><flowRegion
+ id="flowRegion12892"><rect
+ id="rect12894"
+ width="215.14285"
+ height="33"
+ x="194.28572"
+ y="475.52304"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara6968">N-Way Multi-Provider</flowPara></flowRoot> <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="316"
+ y="236.40942"
+ id="text4409"><tspan
+ sodipodi:role="line"
+ id="tspan4411"
+ x="316"
+ y="236.40942" /><tspan
+ sodipodi:role="line"
+ id="tspan4413" /></text>
+ <rect
+ style="opacity:0;fill:#0000ff;fill-rule:evenodd;stroke:#000000;stroke-width:0.73218948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.4643789, 0.73218945;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect3579"
+ width="178.64662"
+ height="149.73311"
+ x="47.800755"
+ y="127.86576" />
+ <rect
+ style="opacity:0;fill:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:2, 1;stroke-dashoffset:0"
+ id="rect4375"
+ width="183.45866"
+ height="148.84184"
+ x="52.01128"
+ y="126.97449" />
+ <rect
+ style="opacity:0;fill:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:2, 1;stroke-dashoffset:0"
+ id="rect4379"
+ width="176.24062"
+ height="148.84184"
+ x="55.018799"
+ y="123.40941" />
+ <rect
+ style="opacity:0;fill:#000000;fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:2, 1;stroke-dashoffset:0"
+ id="rect4381"
+ width="165.41354"
+ height="143.49423"
+ x="64.64286"
+ y="141.23479" />
+ <rect
+ style="opacity:0;fill:#000000;fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:2, 1;stroke-dashoffset:0"
+ id="rect4383"
+ width="183.45866"
+ height="153.29819"
+ x="55.620304"
+ y="126.97449" />
+ <rect
+ style="fill:#9087ff;fill-opacity:0;fill-rule:evenodd;stroke:#000000;stroke-width:1.17423046;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3.52269138, 1.17423046;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect4421"
+ width="298.58423"
+ height="265.9512"
+ x="68.433456"
+ y="127.24354" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.73100412px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 137.22159,211.39967 L 192.3649,161.08802"
+ id="path5584" />
+ <g
+ id="g5590"
+ transform="matrix(0.113185,0,0,0.2535183,188.60055,106.3998)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path5592"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path5594"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path5596"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <g
+ id="g5598"
+ transform="matrix(0.113185,0,0,0.2535183,289.84868,191.17904)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path5600"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path5602"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path5604"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.80629903px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 241.61342,161.08341 L 307.52195,212.29555"
+ id="path5626" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.64994711px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 244.26545,341.16464 L 294.25338,297.29009"
+ id="path5628" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.74283248px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 124.07011,296.83672 L 182.92991,345.50928"
+ id="path5630" />
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="151"
+ y="425.40942"
+ id="text12157"><tspan
+ sodipodi:role="line"
+ id="tspan12159"
+ x="151"
+ y="425.40942">Example of a</tspan><tspan
+ sodipodi:role="line"
+ x="151"
+ y="445.40942"
+ id="tspan12206">Normal topology</tspan></text>
+ <g
+ id="g12190"
+ transform="matrix(0.113185,0,0,0.2535183,185.77724,274.9451)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path12192"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12194"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12196"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <g
+ id="g12198"
+ transform="matrix(0.113185,0,0,0.2535183,87.77724,183.9451)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path12200"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12202"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12204"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <rect
+ style="opacity:0;fill:#0000ff;fill-rule:evenodd;stroke:#000000;stroke-width:0.73218948;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.4643789, 0.73218945;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect12248"
+ width="178.64662"
+ height="149.73311"
+ x="377.28104"
+ y="127.20171" />
+ <rect
+ style="opacity:0;fill:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:2, 1;stroke-dashoffset:0"
+ id="rect12250"
+ width="183.45866"
+ height="148.84184"
+ x="381.49155"
+ y="126.31043" />
+ <rect
+ style="opacity:0;fill:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:2, 1;stroke-dashoffset:0"
+ id="rect12252"
+ width="176.24062"
+ height="148.84184"
+ x="384.49905"
+ y="122.74535" />
+ <rect
+ style="opacity:0;fill:#000000;fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:2, 1;stroke-dashoffset:0"
+ id="rect12254"
+ width="165.41354"
+ height="143.49423"
+ x="394.12314"
+ y="140.57074" />
+ <rect
+ style="opacity:0;fill:#000000;fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:2, 1;stroke-dashoffset:0"
+ id="rect12256"
+ width="183.45866"
+ height="153.29819"
+ x="385.10059"
+ y="126.31043" />
+ <rect
+ style="fill:#9087ff;fill-opacity:0;fill-rule:evenodd;stroke:#000000;stroke-width:1.17423046;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3.52269138, 1.17423046;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect12258"
+ width="298.58423"
+ height="265.9512"
+ x="397.91373"
+ y="126.57948" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.38716727px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 444.79535,175.44697 L 472.2382,147.08805"
+ id="path12260" />
+ <g
+ id="g12262"
+ transform="matrix(5.632813e-2,0,0,0.1428994,470.36482,116.26221)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path12264"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z"
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12266"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12268"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <g
+ id="g12270"
+ transform="matrix(5.632813e-2,0,0,0.1428994,520.7524,164.04931)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path12272"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12274"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12276"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.42704627px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 496.74743,147.08545 L 529.54775,175.95195"
+ id="path12278" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.34423643px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 498.06725,248.59096 L 522.94446,223.86041"
+ id="path12280" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.39343202px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 438.25033,223.60486 L 467.54275,251.03988"
+ id="path12282" />
+ <text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="507.48026"
+ y="424.74536"
+ id="text12284"><tspan
+ sodipodi:role="line"
+ x="507.48026"
+ y="424.74536"
+ id="tspan12308">Example of a </tspan><tspan
+ sodipodi:role="line"
+ x="507.48026"
+ y="464.74536"
+ id="tspan2998">ComplexTopology</tspan></text>
+ <g
+ id="g12290"
+ transform="matrix(5.632813e-2,0,0,0.1428994,467.95976,208.26531)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path12292"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12294"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12296"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <g
+ id="g12298"
+ transform="matrix(5.632813e-2,0,0,0.1428994,420.18866,159.97179)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path12300"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12302"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12304"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <g
+ id="g12330"
+ transform="matrix(5.632813e-2,0,0,0.1428994,467.95454,296.20047)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path12332"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12334"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12336"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <g
+ id="g12338"
+ transform="matrix(5.632813e-2,0,0,0.1428994,593.95454,222.20047)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path12340"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12342"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12344"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <g
+ id="g12346"
+ transform="matrix(5.632813e-2,0,0,0.1428994,553.95454,299.20047)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path12348"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12350"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12352"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <g
+ id="g12354"
+ transform="matrix(5.632813e-2,0,0,0.1428994,637.95454,302.20047)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path12356"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12358"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12360"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.34423643px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 570.56139,304.7747 L 595.4386,280.04415"
+ id="path12362" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.39343202px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 621.35379,282.69191 L 650.64621,310.12693"
+ id="path12364" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.6021955px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 497.8011,335.40942 L 523.48612,335.40942 L 551.1989,335.40942"
+ id="path12376" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.6021955px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 584.3011,337.40942 L 609.98612,337.40942 L 637.6989,337.40942"
+ id="path13444" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.68973196px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 483,266.25429 L 483,289.66343 L 483,307.56455"
+ id="path13448" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="105"
+ y="253.40942"
+ id="text14516"><tspan
+ sodipodi:role="line"
+ id="tspan14518"
+ x="105"
+ y="253.40942">m1</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="206.34375"
+ y="177.78345"
+ id="text14520"><tspan
+ sodipodi:role="line"
+ x="206.34375"
+ y="177.78345"
+ id="tspan14564">m2</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="200.34375"
+ y="345.78345"
+ id="text14524"><tspan
+ sodipodi:role="line"
+ id="tspan14526"
+ x="200.34375"
+ y="345.78345">m4</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="309.34375"
+ y="263.78345"
+ id="text14528"><tspan
+ sodipodi:role="line"
+ id="tspan14530"
+ x="309.34375"
+ y="263.78345">m3</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:9.74170971px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="587.19482"
+ y="147.00529"
+ id="text14532"
+ transform="scale(0.72853,1.3726271)"><tspan
+ sodipodi:role="line"
+ id="tspan14534"
+ x="587.19482"
+ y="147.00529">m1</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:9.74170971px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="657.88513"
+ y="116.04277"
+ id="text14536"
+ transform="scale(0.72853,1.3726271)"><tspan
+ sodipodi:role="line"
+ id="tspan14538"
+ x="657.88513"
+ y="116.04277">m2</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:9.74170971px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="729.26178"
+ y="150.28368"
+ id="text14540"
+ transform="scale(0.72853,1.3726271)"><tspan
+ sodipodi:role="line"
+ id="tspan14542"
+ x="729.26178"
+ y="150.28368">m3</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:9.74170971px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="655.13983"
+ y="182.339"
+ id="text14544"
+ transform="scale(0.72853,1.3726271)"><tspan
+ sodipodi:role="line"
+ id="tspan14546"
+ x="655.13983"
+ y="182.339">m4</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:9.74170971px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="655.13983"
+ y="246.44963"
+ id="text14548"
+ transform="scale(0.72853,1.3726271)"><tspan
+ sodipodi:role="line"
+ id="tspan14550"
+ x="655.13983"
+ y="246.44963">m5</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:9.74170971px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="773.18579"
+ y="249.36375"
+ id="text14552"
+ transform="scale(0.72853,1.3726271)"><tspan
+ sodipodi:role="line"
+ id="tspan14554"
+ x="773.18579"
+ y="249.36375">m6</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:9.74170971px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="829.4635"
+ y="191.80989"
+ id="text14556"
+ transform="scale(0.72853,1.3726271)"><tspan
+ sodipodi:role="line"
+ id="tspan14558"
+ x="829.4635"
+ y="191.80989">m7</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:9.74170971px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="887.11389"
+ y="250.09229"
+ id="text14560"
+ transform="scale(0.72853,1.3726271)"><tspan
+ sodipodi:role="line"
+ id="tspan14562"
+ x="887.11389"
+ y="250.09229">m8</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.09121561px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 214,208.45503 L 214,296.36381"
+ id="path4225" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.01945496px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 141.00973,247.40942 L 289.99027,247.40942"
+ id="path7153" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.67594701px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 448.33798,195.40942 L 481.20271,195.40942 L 516.66202,195.40942"
+ id="path8754" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.72011989px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 483,173.76948 L 483,199.42807 L 483,219.04936"
+ id="path8756" />
+ </g>
+</svg>
diff --git a/doc/guide/images/src/push-based-complete.svg b/doc/guide/images/src/push-based-complete.svg
new file mode 100644
index 0000000..d784322
--- /dev/null
+++ b/doc/guide/images/src/push-based-complete.svg
@@ -0,0 +1,4754 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="524.40942"
+ id="svg7893"
+ inkscape:version="0.46"
+ sodipodi:docbase="/home/ghenry/Desktop"
+ sodipodi:docname="push-based-complete.svg"
+ sodipodi:version="0.32"
+ width="744.09448"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ version="1.0"
+ inkscape:export-filename="/home/ghenry/Desktop/dual_dc.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <metadata
+ id="metadata2563">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:title>Firewall2</dc:title>
+ <dc:description />
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>wall</rdf:li>
+ <rdf:li>brick</rdf:li>
+ <rdf:li>computer</rdf:li>
+ <rdf:li>networksym</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ <dc:publisher>
+ <cc:Agent
+ rdf:about="http://www.openclipart.org/">
+ <dc:title>Open Clip Art Library</dc:title>
+ </cc:Agent>
+ </dc:publisher>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>HASH(0x89c79d4)</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:rights>
+ <cc:Agent>
+ <dc:title>HASH(0x89c79d4)</dc:title>
+ </cc:Agent>
+ </dc:rights>
+ <dc:date />
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <cc:license
+ rdf:resource="http://web.resource.org/cc/PublicDomain" />
+ <dc:language>en</dc:language>
+ </cc:Work>
+ <cc:License
+ rdf:about="http://web.resource.org/cc/PublicDomain">
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Reproduction" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Distribution" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs7895">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 372.04724 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="1052.3622 : 372.04724 : 1"
+ inkscape:persp3d-origin="526.18109 : 248.03149 : 1"
+ id="perspective6943" />
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible">
+ <path
+ id="path17680"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lstart"
+ style="overflow:visible">
+ <path
+ id="path17677"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(0.8,0,0,0.8,10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend"
+ style="overflow:visible">
+ <path
+ id="path17686"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mstart"
+ style="overflow:visible">
+ <path
+ id="path17683"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(0.4,0,0,0.4,4,0)" />
+ </marker>
+ <linearGradient
+ id="linearGradient6508">
+ <stop
+ id="stop6509"
+ offset="0.0000000"
+ style="stop-color:#ff0000;stop-opacity:1.0000000;" />
+ <stop
+ id="stop6511"
+ offset="0.64370060"
+ style="stop-color:#ffb900;stop-opacity:1.0000000;" />
+ <stop
+ id="stop6512"
+ offset="0.79038113"
+ style="stop-color:#ffff00;stop-opacity:0.84102565;" />
+ <stop
+ id="stop6510"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.21568628;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient13376">
+ <stop
+ style="stop-color:#d4d4d4;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop13377" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.49803922;"
+ offset="0.50000000"
+ id="stop13380" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="1.0000000"
+ id="stop13378" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient12744">
+ <stop
+ style="stop-color:#839da4;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop12745" />
+ <stop
+ style="stop-color:#496d77;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop12746" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient10810">
+ <stop
+ style="stop-color:#0e0000;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop10811" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1.0000000;"
+ offset="0.50000000"
+ id="stop10814" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop10812" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient11442">
+ <stop
+ style="stop-color:#6e6e6e;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop11443" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop11444" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient14160">
+ <stop
+ style="stop-color:#4af853;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop14161" />
+ <stop
+ style="stop-color:#68b96d;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop14162" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient14835">
+ <stop
+ style="stop-color:#bed1d0;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop14836" />
+ <stop
+ style="stop-color:#52727b;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop14837" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient29203">
+ <stop
+ style="stop-color:#d3d3d3;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop29205" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop29207" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient6658">
+ <stop
+ style="stop-color:#677883;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop6659" />
+ <stop
+ style="stop-color:#677883;stop-opacity:0.0000000;"
+ offset="1.0000000"
+ id="stop6660" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient41493">
+ <stop
+ style="stop-color:#181818;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop41495" />
+ <stop
+ style="stop-color:#5e5e5e;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop41497" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient12759">
+ <stop
+ style="stop-color:#b4b4b4;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop12761" />
+ <stop
+ style="stop-color:#d7d8de;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop12763" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient21825">
+ <stop
+ style="stop-color:#808080;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop21827" />
+ <stop
+ style="stop-color:#5e5e5e;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop21829" />
+ </linearGradient>
+ <radialGradient
+ xlink:href="#linearGradient13376"
+ r="31.620827"
+ id="radialGradient25527"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.776429,0,0,0.659114,-120.5524,673.5049)"
+ fy="254.35735"
+ fx="-19.038713"
+ cy="253.63734"
+ cx="-19.261518" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12759"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient25525"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,-145.458,730.6984)" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient21825"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient25403"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,-145.2247,712.702)" />
+ <linearGradient
+ y2="1977.8738"
+ y1="1924.0137"
+ xlink:href="#linearGradient41493"
+ x2="-35.763195"
+ x1="-39.828941"
+ id="linearGradient25401"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.672454,0,0,0.374188,-3.473342,95.2718)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient25353"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.270019,0,0,0.370779,-149.3489,792.5495)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient26976"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-90.06505,808.8095)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient26972"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.05831,0,0,0.803858,616.249,115.0105)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient26974"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.838868,0,0,0.530755,508.4408,137.664)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient26964"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.05831,0,0,0.803858,616.249,115.0105)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient26966"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.838868,0,0,0.530755,508.4408,137.664)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28284"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28286"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28288"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28290"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28274"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28276"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28278"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28280"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28264"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28266"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28268"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28270"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28254"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28256"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28258"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28260"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28244"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28246"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28248"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28250"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28234"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28236"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28238"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28240"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28224"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28226"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28228"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28230"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28214"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28216"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28218"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28220"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28208"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-125.9178,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28210"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="97.536598"
+ cy="113.726"
+ cx="97.536598" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28204"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-121.573,808.7592)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28206"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="100.67591"
+ cy="113.726"
+ cx="100.67591" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28200"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-116.9703,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28202"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="104.00187"
+ cy="113.726"
+ cx="104.00187" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28196"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-112.6254,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28198"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="107.14119"
+ cy="113.726"
+ cx="107.14119" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28192"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-108.4824,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28194"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="110.13468"
+ cy="113.726"
+ cx="110.13468" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28188"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-104.1375,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28190"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="113.27399"
+ cy="113.726"
+ cx="113.27399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28184"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-99.77797,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28186"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="116.42374"
+ cy="113.726"
+ cx="116.42374" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28180"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-95.43307,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28182"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="119.56305"
+ cy="113.726"
+ cx="119.56305" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28172"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28174"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28176"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28178"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28162"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28164"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28168"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28152"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28154"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28156"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28158"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28142"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28144"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28146"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28148"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28132"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28134"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28136"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28138"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28122"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28124"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28126"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28128"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28112"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28114"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28116"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28118"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28102"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28104"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28106"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28108"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28096"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-161.2375,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28098"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="71.480988"
+ cy="113.726"
+ cx="71.480988" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28092"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-156.8927,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28094"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="74.620308"
+ cy="113.726"
+ cx="74.620308" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28088"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-152.29,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28090"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="77.946259"
+ cy="113.726"
+ cx="77.946259" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28084"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-147.9451,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28086"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="81.085587"
+ cy="113.726"
+ cx="81.085587" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28080"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-143.8021,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28082"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="84.079071"
+ cy="113.726"
+ cx="84.079071" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28076"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-139.4573,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28078"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="87.218399"
+ cy="113.726"
+ cx="87.218399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28072"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-135.098,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28074"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="90.368126"
+ cy="113.726"
+ cx="90.368126" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28068"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-130.7531,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28070"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="93.507462"
+ cy="113.726"
+ cx="93.507462" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28060"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28062"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28064"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28066"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28050"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28052"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28054"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28056"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28040"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28042"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28044"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28046"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28030"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28032"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28034"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28036"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28020"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28022"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28024"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28026"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28010"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28012"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28014"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28016"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28000"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28002"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28004"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28006"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27990"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27992"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27994"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27996"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27984"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-197.2616,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27986"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="45.452175"
+ cy="113.726"
+ cx="45.452175" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27980"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-192.9168,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27982"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="48.591496"
+ cy="113.726"
+ cx="48.591496" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27976"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-188.3141,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27978"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="51.91745"
+ cy="113.726"
+ cx="51.91745" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27972"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-183.9692,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27974"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="55.05677"
+ cy="113.726"
+ cx="55.05677" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27968"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-179.8262,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27970"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="58.050255"
+ cy="113.726"
+ cx="58.050255" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27964"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-175.4813,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27966"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="61.189575"
+ cy="113.726"
+ cx="61.189575" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27960"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-171.122,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27962"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="64.339317"
+ cy="113.726"
+ cx="64.339317" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27956"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-166.7771,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27958"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="67.478638"
+ cy="113.726"
+ cx="67.478638" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27928"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27930"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27932"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27934"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27918"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27920"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27922"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27924"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27908"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27910"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27912"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27914"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27898"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27900"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27902"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27904"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27888"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27890"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27892"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27894"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27878"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27880"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27882"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27884"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27868"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27870"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27872"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27874"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27858"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27860"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27862"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27864"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27848"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27850"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27852"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27854"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27838"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27840"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27842"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27844"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27828"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27830"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27832"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27834"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27818"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27820"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27822"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27824"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27808"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27810"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27812"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27814"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27798"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27800"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27802"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27804"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27788"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27790"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27792"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27794"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27778"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27780"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27782"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27784"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27768"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27770"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27772"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27774"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27758"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27760"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27762"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27764"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27748"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27750"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27752"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27754"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27738"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27740"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27742"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27744"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27728"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27730"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27732"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27734"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27718"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27720"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27722"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27724"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27708"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27710"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27712"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27714"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27698"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27700"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27702"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27704"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28432"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-126.1386,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28434"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="97.536598"
+ cy="113.726"
+ cx="97.536598" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28428"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-121.7938,817.6604)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28430"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="100.67591"
+ cy="113.726"
+ cx="100.67591" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28424"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-117.1911,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28426"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="104.00187"
+ cy="113.726"
+ cx="104.00187" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28420"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-112.8462,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28422"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="107.14119"
+ cy="113.726"
+ cx="107.14119" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28416"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-108.7032,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28418"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="110.13468"
+ cy="113.726"
+ cx="110.13468" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28412"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-104.3583,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28414"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="113.27399"
+ cy="113.726"
+ cx="113.27399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28408"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-99.99876,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28410"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="116.42374"
+ cy="113.726"
+ cx="116.42374" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28404"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-95.65386,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28406"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="119.56305"
+ cy="113.726"
+ cx="119.56305" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28400"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-161.4583,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28402"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="71.480988"
+ cy="113.726"
+ cx="71.480988" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28396"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-157.1135,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28398"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="74.620308"
+ cy="113.726"
+ cx="74.620308" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28392"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-152.5108,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28394"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="77.946259"
+ cy="113.726"
+ cx="77.946259" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28388"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-148.1659,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28390"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="81.085587"
+ cy="113.726"
+ cx="81.085587" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28384"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-144.0229,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28386"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="84.079071"
+ cy="113.726"
+ cx="84.079071" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28380"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-139.6781,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28382"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="87.218399"
+ cy="113.726"
+ cx="87.218399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28376"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-135.3188,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28378"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="90.368126"
+ cy="113.726"
+ cx="90.368126" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28372"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-130.9739,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28374"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="93.507462"
+ cy="113.726"
+ cx="93.507462" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28368"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-197.4824,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28370"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="45.452175"
+ cy="113.726"
+ cx="45.452175" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28364"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-193.1376,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28366"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="48.591496"
+ cy="113.726"
+ cx="48.591496" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28360"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-188.5349,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28362"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="51.91745"
+ cy="113.726"
+ cx="51.91745" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28356"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-184.19,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28358"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="55.05677"
+ cy="113.726"
+ cx="55.05677" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28352"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-180.047,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28354"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="58.050255"
+ cy="113.726"
+ cx="58.050255" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28348"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-175.7021,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28350"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="61.189575"
+ cy="113.726"
+ cx="61.189575" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28344"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-171.3428,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28346"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="64.339317"
+ cy="113.726"
+ cx="64.339317" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28340"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-166.9979,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28342"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="67.478638"
+ cy="113.726"
+ cx="67.478638" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28438"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-90.25863,817.7848)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12759"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient36281"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,-149.897,802.9053)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36283"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36285"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36287"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36289"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36291"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36293"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36295"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36297"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36299"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36301"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36303"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36305"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36307"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36309"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36311"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36313"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36315"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36317"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36319"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36321"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36323"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36325"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36327"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36329"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36331"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.5348,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36333"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-185.7196,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36335"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-189.35,879.6484)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36337"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-180.274,879.6484)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36339"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-183.9043,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36341"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-182.0892,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient21825"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient36343"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,-149.6637,784.9089)" />
+ <linearGradient
+ y2="1977.8738"
+ y1="1924.0137"
+ xlink:href="#linearGradient41493"
+ x2="-35.763195"
+ x1="-39.828941"
+ id="linearGradient36345"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.672454,0,0,0.374188,-7.912301,167.4787)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36347"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.5296,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36349"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-185.7144,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36351"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-189.3448,881.7646)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36353"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-180.2688,881.7646)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36355"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-183.8991,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36357"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-182.084,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36359"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-198.4916,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36361"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-190.046,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36363"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.2306,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36365"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-192.8611,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36367"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-195.6763,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36369"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.74272,0,0,0.445632,-87.12747,420.4818)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36371"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36373"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36375"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.787283,0,0,0.475341,-91.66274,388.2275)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36377"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36379"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36381"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.270019,0,0,0.370779,-153.7879,864.7564)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12759"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient35867"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,-141.9847,635.4266)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35869"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35871"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35873"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35875"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35877"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35879"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35881"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35883"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35885"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35887"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35889"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35891"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35893"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35895"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35897"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35899"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35901"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35903"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35905"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35907"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35909"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35911"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35913"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35915"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35917"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35919"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35921"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35923"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35925"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35927"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35929"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35931"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35933"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35935"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35937"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35939"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35941"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35943"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35945"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35947"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35949"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35951"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35953"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35955"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35957"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35959"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35961"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35963"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35965"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-179.6225,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35967"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-168.7312,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35969"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-163.2856,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35971"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-161.4702,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35973"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-165.1007,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35975"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-177.8073,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35977"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-166.9159,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35979"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-181.4377,712.1697)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35981"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-170.5465,712.1697)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35983"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-172.3617,712.1697)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35985"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-175.992,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35987"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-174.1769,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient21825"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient35989"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,-141.7514,617.4302)" />
+ <linearGradient
+ y2="1977.8738"
+ y1="1924.0137"
+ xlink:href="#linearGradient41493"
+ x2="-35.763195"
+ x1="-39.828941"
+ id="linearGradient35991"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(2.672454,0.374188)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35993"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-179.6173,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35995"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-168.726,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35997"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-163.2804,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35999"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-161.465,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36001"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-165.0955,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36003"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-177.8021,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36005"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-166.9107,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36007"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-181.4325,714.2859)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36009"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-170.5413,714.2859)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36011"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-172.3565,714.2859)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36013"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-175.9868,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36015"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-174.1717,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36017"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-190.5793,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36019"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-182.1337,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36021"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-179.3183,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36023"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-184.9488,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36025"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.764,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36027"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.74272,0,0,0.445632,-79.21517,253.0031)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36029"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36031"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36033"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.787283,0,0,0.475341,-83.75044,220.7488)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36035"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36037"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36039"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.270019,0,0,0.370779,-145.8756,697.2777)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient13376"
+ r="31.620827"
+ id="radialGradient12151"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.20227,0,0,0.454077,6.691668,-148.3193)"
+ fy="254.35735"
+ fx="-19.038713"
+ cy="253.63734"
+ cx="-19.261518" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12744"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient12153"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,1.691668,-145.8193)" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient14835"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient12155"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,1.924904,-161.8157)" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12744"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient12157"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.350818,114.6621,-134.6472)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12159"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-59.65453)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12161"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-61.33423)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12163"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-63.01391)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12165"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.09869,-64.40064)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ </defs>
+ <sodipodi:namedview
+ bordercolor="#666666"
+ borderopacity="1.0"
+ id="base"
+ inkscape:current-layer="layer1"
+ inkscape:cx="495.44191"
+ inkscape:cy="251.71914"
+ inkscape:document-units="px"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:window-height="953"
+ inkscape:window-width="1280"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:zoom="1"
+ pagecolor="#ffffff"
+ width="1052.3622px"
+ height="744.09448px"
+ showgrid="false" />
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1">
+ <g
+ id="g5278"
+ transform="matrix(0.1267968,0,0,0.1710106,135.15833,371.75325)">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path569"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path568"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path566"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <g
+ id="g12774"
+ transform="matrix(0.1881701,0,0,0.2844466,245.77219,174.33679)">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path12776"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12778"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12780"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot12890"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial"
+ transform="translate(12.007531,-421.27533)"><flowRegion
+ id="flowRegion12892"><rect
+ id="rect12894"
+ width="258.14285"
+ height="67"
+ x="194.28572"
+ y="475.52304"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara12898">Push Based Replication</flowPara><flowPara
+ id="flowPara6968">(replacing slurpd)</flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot15524"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial"
+ transform="translate(114.94413,-217.88838)"><flowRegion
+ id="flowRegion15526"><rect
+ id="rect15528"
+ width="129.29955"
+ height="26.263966"
+ x="137.38075"
+ y="681.46503"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara15542">Replicas</flowPara></flowRoot> <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 166.31983,373.76445 L 273.396,325.27713"
+ id="path23715" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.03078127px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 312.57943,327.85652 L 434.22276,373.20536"
+ id="path25655" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 293.59905,371.74414 L 293.59905,317.19591"
+ id="path25659" />
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot27609"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial"
+ transform="translate(90,50)"><flowRegion
+ id="flowRegion27611"><rect
+ id="rect27613"
+ width="134.05586"
+ height="26.345188"
+ x="96.974648"
+ y="113.75929"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara27617">Provider</flowPara></flowRoot> <g
+ id="g3073"
+ transform="matrix(0.1267968,0,0,0.1710106,264.00249,370.01498)">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path3075"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path3077"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path3079"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <g
+ id="g3081"
+ transform="matrix(0.1267968,0,0,0.1710106,411.48475,365.97437)">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path3083"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path3085"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path3087"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot3120"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial"
+ transform="matrix(1,0,0,1.2037203,-16.30957,-194.07388)"><flowRegion
+ id="flowRegion3122"><rect
+ id="rect3124"
+ width="210.52287"
+ height="143.55249"
+ x="412.14224"
+ y="279.42432"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara3136">Primary directory also contains back-ldap databases that replicate from the provider directory and push out changes to the replicas</flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot6975"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ transform="translate(18,-68)"><flowRegion
+ id="flowRegion6977"><rect
+ id="rect6979"
+ width="165"
+ height="63"
+ x="469"
+ y="437.09448" /></flowRegion><flowPara
+ id="flowPara6981">Replicas are readonly, but referrals can be handled by clients or using the chaining overlay. </flowPara></flowRoot> </g>
+</svg>
diff --git a/doc/guide/images/src/push-based-standalone.svg b/doc/guide/images/src/push-based-standalone.svg
new file mode 100644
index 0000000..0e4d783
--- /dev/null
+++ b/doc/guide/images/src/push-based-standalone.svg
@@ -0,0 +1,4844 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ height="524.40942"
+ id="svg7893"
+ inkscape:version="0.46"
+ sodipodi:docbase="/home/ghenry/Desktop"
+ sodipodi:docname="push-based-standalone.svg"
+ sodipodi:version="0.32"
+ width="744.09448"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ version="1.0"
+ inkscape:export-filename="/home/ghenry/Desktop/dual_dc.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <metadata
+ id="metadata2563">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:title>Firewall2</dc:title>
+ <dc:description />
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>wall</rdf:li>
+ <rdf:li>brick</rdf:li>
+ <rdf:li>computer</rdf:li>
+ <rdf:li>networksym</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ <dc:publisher>
+ <cc:Agent
+ rdf:about="http://www.openclipart.org/">
+ <dc:title>Open Clip Art Library</dc:title>
+ </cc:Agent>
+ </dc:publisher>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>HASH(0x89c79d4)</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:rights>
+ <cc:Agent>
+ <dc:title>HASH(0x89c79d4)</dc:title>
+ </cc:Agent>
+ </dc:rights>
+ <dc:date />
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <cc:license
+ rdf:resource="http://web.resource.org/cc/PublicDomain" />
+ <dc:language>en</dc:language>
+ </cc:Work>
+ <cc:License
+ rdf:about="http://web.resource.org/cc/PublicDomain">
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Reproduction" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/Distribution" />
+ <cc:permits
+ rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs7895">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 372.04724 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="1052.3622 : 372.04724 : 1"
+ inkscape:persp3d-origin="526.18109 : 248.03149 : 1"
+ id="perspective6943" />
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible">
+ <path
+ id="path17680"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lstart"
+ style="overflow:visible">
+ <path
+ id="path17677"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(0.8,0,0,0.8,10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend"
+ style="overflow:visible">
+ <path
+ id="path17686"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mstart"
+ style="overflow:visible">
+ <path
+ id="path17683"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(0.4,0,0,0.4,4,0)" />
+ </marker>
+ <linearGradient
+ id="linearGradient6508">
+ <stop
+ id="stop6509"
+ offset="0.0000000"
+ style="stop-color:#ff0000;stop-opacity:1.0000000;" />
+ <stop
+ id="stop6511"
+ offset="0.64370060"
+ style="stop-color:#ffb900;stop-opacity:1.0000000;" />
+ <stop
+ id="stop6512"
+ offset="0.79038113"
+ style="stop-color:#ffff00;stop-opacity:0.84102565;" />
+ <stop
+ id="stop6510"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.21568628;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient13376">
+ <stop
+ style="stop-color:#d4d4d4;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop13377" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.49803922;"
+ offset="0.50000000"
+ id="stop13380" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.0000000;"
+ offset="1.0000000"
+ id="stop13378" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient12744">
+ <stop
+ style="stop-color:#839da4;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop12745" />
+ <stop
+ style="stop-color:#496d77;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop12746" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient10810">
+ <stop
+ style="stop-color:#0e0000;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop10811" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1.0000000;"
+ offset="0.50000000"
+ id="stop10814" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop10812" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient11442">
+ <stop
+ style="stop-color:#6e6e6e;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop11443" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop11444" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient14160">
+ <stop
+ style="stop-color:#4af853;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop14161" />
+ <stop
+ style="stop-color:#68b96d;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop14162" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient14835">
+ <stop
+ style="stop-color:#bed1d0;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop14836" />
+ <stop
+ style="stop-color:#52727b;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop14837" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient29203">
+ <stop
+ style="stop-color:#d3d3d3;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop29205" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop29207" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient6658">
+ <stop
+ style="stop-color:#677883;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop6659" />
+ <stop
+ style="stop-color:#677883;stop-opacity:0.0000000;"
+ offset="1.0000000"
+ id="stop6660" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient41493">
+ <stop
+ style="stop-color:#181818;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop41495" />
+ <stop
+ style="stop-color:#5e5e5e;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop41497" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient12759">
+ <stop
+ style="stop-color:#b4b4b4;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop12761" />
+ <stop
+ style="stop-color:#d7d8de;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop12763" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient21825">
+ <stop
+ style="stop-color:#808080;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop21827" />
+ <stop
+ style="stop-color:#5e5e5e;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop21829" />
+ </linearGradient>
+ <radialGradient
+ xlink:href="#linearGradient13376"
+ r="31.620827"
+ id="radialGradient25527"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.776429,0,0,0.659114,-120.5524,673.5049)"
+ fy="254.35735"
+ fx="-19.038713"
+ cy="253.63734"
+ cx="-19.261518" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12759"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient25525"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,-145.458,730.6984)" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient21825"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient25403"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,-145.2247,712.702)" />
+ <linearGradient
+ y2="1977.8738"
+ y1="1924.0137"
+ xlink:href="#linearGradient41493"
+ x2="-35.763195"
+ x1="-39.828941"
+ id="linearGradient25401"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.672454,0,0,0.374188,-3.473342,95.2718)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient25353"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.270019,0,0,0.370779,-149.3489,792.5495)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient26976"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-90.06505,808.8095)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient26972"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.05831,0,0,0.803858,616.249,115.0105)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient26974"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.838868,0,0,0.530755,508.4408,137.664)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient26964"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.05831,0,0,0.803858,616.249,115.0105)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient26966"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.838868,0,0,0.530755,508.4408,137.664)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28284"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28286"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28288"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28290"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28274"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28276"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28278"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28280"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28264"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28266"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28268"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28270"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28254"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28256"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28258"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28260"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28244"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28246"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28248"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28250"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28234"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28236"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28238"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28240"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28224"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28226"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28228"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28230"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28214"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28216"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28218"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28220"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28208"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-125.9178,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28210"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="97.536598"
+ cy="113.726"
+ cx="97.536598" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28204"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-121.573,808.7592)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28206"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="100.67591"
+ cy="113.726"
+ cx="100.67591" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28200"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-116.9703,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28202"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="104.00187"
+ cy="113.726"
+ cx="104.00187" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28196"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-112.6254,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28198"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="107.14119"
+ cy="113.726"
+ cx="107.14119" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28192"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-108.4824,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28194"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="110.13468"
+ cy="113.726"
+ cx="110.13468" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28188"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-104.1375,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28190"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="113.27399"
+ cy="113.726"
+ cx="113.27399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28184"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-99.77797,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28186"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="116.42374"
+ cy="113.726"
+ cx="116.42374" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28180"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-95.43307,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28182"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.0884,752.134)"
+ fy="113.726"
+ fx="119.56305"
+ cy="113.726"
+ cx="119.56305" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28172"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28174"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28176"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28178"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28162"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28164"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28166"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28168"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28152"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28154"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28156"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28158"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28142"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28144"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28146"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28148"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28132"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28134"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28136"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28138"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28122"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28124"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28126"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28128"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28112"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28114"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28116"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28118"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28102"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28104"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28106"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28108"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28096"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-161.2375,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28098"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="71.480988"
+ cy="113.726"
+ cx="71.480988" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28092"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-156.8927,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28094"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="74.620308"
+ cy="113.726"
+ cx="74.620308" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28088"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-152.29,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28090"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="77.946259"
+ cy="113.726"
+ cx="77.946259" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28084"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-147.9451,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28086"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="81.085587"
+ cy="113.726"
+ cx="81.085587" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28080"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-143.8021,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28082"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="84.079071"
+ cy="113.726"
+ cx="84.079071" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28076"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-139.4573,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28078"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="87.218399"
+ cy="113.726"
+ cx="87.218399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28072"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-135.098,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28074"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="90.368126"
+ cy="113.726"
+ cx="90.368126" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28068"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-130.7531,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28070"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="93.507462"
+ cy="113.726"
+ cx="93.507462" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28060"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28062"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28064"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28066"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28050"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28052"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28054"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28056"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28040"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28042"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28044"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28046"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28030"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28032"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28034"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28036"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28020"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28022"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28024"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28026"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28010"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28012"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28014"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28016"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient28000"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28002"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient28004"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient28006"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27990"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27992"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27994"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27996"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27984"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-197.2616,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27986"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="45.452175"
+ cy="113.726"
+ cx="45.452175" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27980"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-192.9168,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27982"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="48.591496"
+ cy="113.726"
+ cx="48.591496" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27976"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-188.3141,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27978"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="51.91745"
+ cy="113.726"
+ cx="51.91745" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27972"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-183.9692,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27974"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="55.05677"
+ cy="113.726"
+ cx="55.05677" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27968"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-179.8262,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27970"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="58.050255"
+ cy="113.726"
+ cx="58.050255" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27964"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-175.4813,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27966"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="61.189575"
+ cy="113.726"
+ cx="61.189575" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27960"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-171.122,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27962"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="64.339317"
+ cy="113.726"
+ cx="64.339317" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient27956"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-166.7771,808.7593)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient27958"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.3469,752.134)"
+ fy="113.726"
+ fx="67.478638"
+ cy="113.726"
+ cx="67.478638" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27928"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27930"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27932"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27934"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27918"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27920"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27922"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27924"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27908"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27910"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27912"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27914"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27898"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27900"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27902"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27904"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27888"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27890"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27892"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27894"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27878"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27880"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27882"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27884"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27868"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27870"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27872"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27874"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27858"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27860"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27862"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27864"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27848"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27850"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27852"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27854"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27838"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27840"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27842"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27844"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27828"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27830"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27832"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27834"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27818"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27820"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27822"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27824"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27808"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27810"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27812"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27814"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27798"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27800"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27802"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27804"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27788"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27790"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27792"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27794"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27778"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27780"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27782"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27784"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27768"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27770"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27772"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27774"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27758"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27760"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27762"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27764"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27748"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27750"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27752"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27754"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27738"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27740"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27742"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27744"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27728"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27730"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27732"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27734"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27718"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27720"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27722"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27724"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27708"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27710"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27712"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27714"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient27698"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27700"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient27702"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="2.8901422"
+ id="radialGradient27704"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.112677,0.898733)"
+ fy="84.14624"
+ fx="-75.26889"
+ cy="84.14624"
+ cx="-75.26889" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28432"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-126.1386,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28434"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="97.536598"
+ cy="113.726"
+ cx="97.536598" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28428"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-121.7938,817.6604)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28430"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="100.67591"
+ cy="113.726"
+ cx="100.67591" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28424"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-117.1911,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28426"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="104.00187"
+ cy="113.726"
+ cx="104.00187" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28420"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-112.8462,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28422"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="107.14119"
+ cy="113.726"
+ cx="107.14119" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28416"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-108.7032,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28418"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="110.13468"
+ cy="113.726"
+ cx="110.13468" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28412"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-104.3583,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28414"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="113.27399"
+ cy="113.726"
+ cx="113.27399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28408"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-99.99876,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28410"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="116.42374"
+ cy="113.726"
+ cx="116.42374" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28404"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-95.65386,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28406"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-286.3092,761.0352)"
+ fy="113.726"
+ fx="119.56305"
+ cy="113.726"
+ cx="119.56305" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28400"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-161.4583,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28402"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="71.480988"
+ cy="113.726"
+ cx="71.480988" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28396"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-157.1135,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28398"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="74.620308"
+ cy="113.726"
+ cx="74.620308" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28392"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-152.5108,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28394"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="77.946259"
+ cy="113.726"
+ cx="77.946259" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28388"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-148.1659,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28390"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="81.085587"
+ cy="113.726"
+ cx="81.085587" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28384"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-144.0229,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28386"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="84.079071"
+ cy="113.726"
+ cx="84.079071" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28380"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-139.6781,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28382"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="87.218399"
+ cy="113.726"
+ cx="87.218399" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28376"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-135.3188,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28378"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="90.368126"
+ cy="113.726"
+ cx="90.368126" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28372"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-130.9739,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28374"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="93.507462"
+ cy="113.726"
+ cx="93.507462" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28368"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-197.4824,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28370"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="45.452175"
+ cy="113.726"
+ cx="45.452175" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28364"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-193.1376,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28366"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="48.591496"
+ cy="113.726"
+ cx="48.591496" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28360"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-188.5349,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28362"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="51.91745"
+ cy="113.726"
+ cx="51.91745" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28356"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-184.19,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28358"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="55.05677"
+ cy="113.726"
+ cx="55.05677" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28352"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-180.047,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28354"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="58.050255"
+ cy="113.726"
+ cx="58.050255" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28348"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-175.7021,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28350"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="61.189575"
+ cy="113.726"
+ cx="61.189575" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28344"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-171.3428,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28346"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="64.339317"
+ cy="113.726"
+ cx="64.339317" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28340"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-166.9979,817.6605)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient6658"
+ r="0.55242717"
+ id="radialGradient28342"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.384013,0,0,0.656868,-285.5677,761.0352)"
+ fy="113.726"
+ fx="67.478638"
+ cy="113.726"
+ cx="67.478638" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient28438"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.380937,0,0,0.180797,-90.25863,817.7848)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12759"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient36281"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,-149.897,802.9053)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36283"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36285"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36287"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36289"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36291"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36293"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36295"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36297"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36299"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36301"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36303"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36305"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36307"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36309"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36311"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36313"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36315"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36317"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36319"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36321"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36323"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36325"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36327"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36329"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36331"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.5348,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36333"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-185.7196,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36335"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-189.35,879.6484)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36337"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-180.274,879.6484)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36339"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-183.9043,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36341"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-182.0892,879.6483)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient21825"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient36343"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,-149.6637,784.9089)" />
+ <linearGradient
+ y2="1977.8738"
+ y1="1924.0137"
+ xlink:href="#linearGradient41493"
+ x2="-35.763195"
+ x1="-39.828941"
+ id="linearGradient36345"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.672454,0,0,0.374188,-7.912301,167.4787)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36347"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.5296,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36349"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-185.7144,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36351"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-189.3448,881.7646)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36353"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-180.2688,881.7646)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36355"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-183.8991,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36357"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-182.084,881.7645)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36359"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-198.4916,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36361"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-190.046,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36363"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.2306,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36365"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-192.8611,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36367"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-195.6763,883.5145)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36369"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.74272,0,0,0.445632,-87.12747,420.4818)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36371"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36373"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36375"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.787283,0,0,0.475341,-91.66274,388.2275)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36377"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36379"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36381"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.270019,0,0,0.370779,-153.7879,864.7564)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12759"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient35867"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,-141.9847,635.4266)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35869"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35871"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35873"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35875"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35877"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35879"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35881"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35883"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35885"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35887"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35889"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35891"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35893"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35895"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35897"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35899"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35901"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35903"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35905"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35907"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35909"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35911"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35913"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35915"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35917"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35919"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35921"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35923"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35925"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35927"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35929"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35931"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35933"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35935"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35937"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35939"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35941"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35943"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35945"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35947"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35949"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35951"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35953"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35955"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35957"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35959"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient35961"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient35963"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35965"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-179.6225,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35967"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-168.7312,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35969"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-163.2856,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35971"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-161.4702,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35973"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-165.1007,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35975"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-177.8073,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35977"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-166.9159,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35979"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-181.4377,712.1697)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35981"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-170.5465,712.1697)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35983"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-172.3617,712.1697)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35985"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-175.992,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35987"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-174.1769,712.1696)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient21825"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient35989"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,-141.7514,617.4302)" />
+ <linearGradient
+ y2="1977.8738"
+ y1="1924.0137"
+ xlink:href="#linearGradient41493"
+ x2="-35.763195"
+ x1="-39.828941"
+ id="linearGradient35991"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(2.672454,0.374188)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35993"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-179.6173,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35995"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-168.726,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35997"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-163.2804,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient35999"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-161.465,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36001"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-165.0955,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36003"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-177.8021,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36005"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-166.9107,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36007"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-181.4325,714.2859)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36009"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-170.5413,714.2859)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36011"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-172.3565,714.2859)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36013"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-175.9868,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36015"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-174.1717,714.2858)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36017"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-190.5793,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36019"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-182.1337,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36021"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-179.3183,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36023"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-184.9488,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36025"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-187.764,716.0358)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36027"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.74272,0,0,0.445632,-79.21517,253.0031)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36029"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36031"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <linearGradient
+ y2="1085.6781"
+ y1="1085.6781"
+ xlink:href="#linearGradient12759"
+ x2="-116.40664"
+ x1="-128.30727"
+ id="linearGradient36033"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.787283,0,0,0.475341,-83.75044,220.7488)" />
+ <linearGradient
+ y2="84.271248"
+ y1="80.490494"
+ xlink:href="#linearGradient10810"
+ x2="-152.33473"
+ x1="-156.03067"
+ id="linearGradient36035"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.731264,0,0,1.388554,183.0968,-38.74554)" />
+ <linearGradient
+ y2="80.317116"
+ y1="83.947449"
+ xlink:href="#linearGradient11442"
+ x2="-63.953007"
+ x1="-64.000694"
+ id="linearGradient36037"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.372284,0,0,0.916806,6.735873,0)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient36039"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.270019,0,0,0.370779,-145.8756,697.2777)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient13376"
+ r="31.620827"
+ id="radialGradient12151"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.20227,0,0,0.454077,6.691668,-148.3193)"
+ fy="254.35735"
+ fx="-19.038713"
+ cy="253.63734"
+ cx="-19.261518" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12744"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient12153"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.399114,1.691668,-145.8193)" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient14835"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient12155"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.500039,0,0,0.399114,1.924904,-161.8157)" />
+ <linearGradient
+ y2="275.81308"
+ y1="233.36613"
+ xlink:href="#linearGradient12744"
+ x2="8.3977861"
+ x1="-35.94503"
+ id="linearGradient12157"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(2.505549,0,0,0.350818,114.6621,-134.6472)" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12159"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-59.65453)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12161"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-61.33423)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12163"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.07916,-63.01391)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ <radialGradient
+ xlink:href="#linearGradient14160"
+ r="2.0070677"
+ id="radialGradient12165"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.389249,0,0,0.194625,-60.09869,-64.40064)"
+ fy="99.988457"
+ fx="-66.099426"
+ cy="99.988457"
+ cx="-66.099426" />
+ </defs>
+ <sodipodi:namedview
+ bordercolor="#666666"
+ borderopacity="1.0"
+ id="base"
+ inkscape:current-layer="layer1"
+ inkscape:cx="495.44191"
+ inkscape:cy="251.71914"
+ inkscape:document-units="px"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:window-height="953"
+ inkscape:window-width="1280"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ inkscape:zoom="1"
+ pagecolor="#ffffff"
+ width="1052.3622px"
+ height="744.09448px"
+ showgrid="false" />
+ <g
+ id="layer1"
+ inkscape:groupmode="layer"
+ inkscape:label="Layer 1">
+ <g
+ id="g5278"
+ transform="matrix(0.1267968,0,0,0.1710106,135.15833,371.75325)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path569"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path568"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path566"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <g
+ id="g12774"
+ transform="matrix(0.1881701,0,0,0.2844466,82.77219,152.33679)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path12776"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12778"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path12780"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot12890"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial"
+ transform="translate(12.007531,-421.27533)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><flowRegion
+ id="flowRegion12892"><rect
+ id="rect12894"
+ width="258.14285"
+ height="67"
+ x="194.28572"
+ y="475.52304"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara12898">Push Based Replication</flowPara><flowPara
+ id="flowPara6968">(replacing slurpd)</flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot15524"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial"
+ transform="translate(114.94413,-217.88838)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><flowRegion
+ id="flowRegion15526"><rect
+ id="rect15528"
+ width="129.29955"
+ height="26.263966"
+ x="137.38075"
+ y="681.46503"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara15542">Replicas</flowPara></flowRoot> <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 166.31983,373.76445 L 273.396,325.27713"
+ id="path23715"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.03078127px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 312.57943,327.85652 L 434.22276,373.20536"
+ id="path25655"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 293.59905,371.74414 L 293.59905,317.19591"
+ id="path25659"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot27609"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial"
+ transform="translate(-57,29)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><flowRegion
+ id="flowRegion27611"><rect
+ id="rect27613"
+ width="134.05586"
+ height="26.345188"
+ x="96.974648"
+ y="113.75929"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara27617">Provider</flowPara></flowRoot> <g
+ id="g3073"
+ transform="matrix(0.1267968,0,0,0.1710106,264.00249,370.01498)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path3075"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path3077"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path3079"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <g
+ id="g3081"
+ transform="matrix(0.1267968,0,0,0.1710106,411.48475,365.97437)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path3083"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path3085"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path3087"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot3120"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial"
+ transform="matrix(1,0,0,1.2037203,-16.30957,-194.07388)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><flowRegion
+ id="flowRegion3122"><rect
+ id="rect3124"
+ width="317.52289"
+ height="139.3987"
+ x="412.14224"
+ y="279.42432"
+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial" /></flowRegion><flowPara
+ id="flowPara3136">Primary directory is a standard OpenLDAP provider, ldap proxy using Syncrepl pulls in changes from the provider and pushes out to replicas. Useful if you don't have access to original provider.</flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot6975"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ transform="translate(18,-68)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90"><flowRegion
+ id="flowRegion6977"><rect
+ id="rect6979"
+ width="165"
+ height="63"
+ x="469"
+ y="437.09448" /></flowRegion><flowPara
+ id="flowPara6981">Replicas are readonly, but referrals can be handled by clients or using the chaining overlay. </flowPara></flowRoot> <g
+ id="g7023"
+ transform="matrix(0.1267968,0,0,0.1710106,261.38313,237.27416)"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90">
+ <path
+ transform="matrix(0,-0.604122,1.608296,0,-165.2214,394.5863)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.75"
+ sodipodi:nodetypes="ccccccc"
+ id="path7025"
+ d="M 426.278,378.046 L 79.4376,379.165 C 79.4376,379.165 -4.63207,356.939 -1.27547,255.125 C -13.892,169.635 56.2895,146.476 60.7648,142 C 65.2402,137.525 427.397,137.496 427.397,137.496 L 427.397,137.496 L 426.278,378.046 z" />
+ <path
+ transform="matrix(0,-0.174045,0.823205,0,39.47672,182.772)"
+ style="font-size:12px;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:9.76546001"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path7027"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ <path
+ transform="matrix(0,-0.169729,0.807961,0,43.34661,181.9851)"
+ style="font-size:12px;fill:#cccccc;fill-opacity:1;fill-rule:evenodd;stroke-width:3.75"
+ sodipodi:type="arc"
+ sodipodi:ry="236.07524"
+ sodipodi:rx="234.95641"
+ sodipodi:cy="253.85521"
+ sodipodi:cx="260.68973"
+ id="path7029"
+ d="M 495.64613,253.85521 A 234.95641,236.07524 0 1 1 25.733322,253.85521 A 234.95641,236.07524 0 1 1 495.64613,253.85521 z" />
+ </g>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.89670002px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow1Lstart);marker-mid:none;marker-end:url(#Arrow1Lend);stroke-opacity:1"
+ d="M 170.1113,236.57545 L 255.8887,285.24339"
+ id="path7031"
+ inkscape:export-filename="/anything/src/openldap/ldap/doc/guide/images/src/push-based-complete.png"
+ inkscape:export-xdpi="90"
+ inkscape:export-ydpi="90" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="253"
+ y="224.40942"
+ id="text7033"><tspan
+ sodipodi:role="line"
+ id="tspan7035"
+ x="253"
+ y="224.40942">Standalone</tspan><tspan
+ sodipodi:role="line"
+ x="253"
+ y="239.40942"
+ id="tspan7037">LDAP Proxy</tspan></text>
+ </g>
+</svg>
diff --git a/doc/guide/images/src/refint.svg b/doc/guide/images/src/refint.svg
new file mode 100644
index 0000000..5a118f7
--- /dev/null
+++ b/doc/guide/images/src/refint.svg
@@ -0,0 +1,199 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="412.04193"
+ height="172.80376"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="/home/andreas/palestra"
+ sodipodi:docname="refint.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path8347"
+ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.97309,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.386"
+ inkscape:cx="381.27532"
+ inkscape:cy="98.970161"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ showguides="false"
+ inkscape:window-width="1278"
+ inkscape:window-height="724"
+ inkscape:window-x="0"
+ inkscape:window-y="25" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Camada 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-29.66815,-34.695504)">
+ <g
+ id="g7325"
+ transform="translate(-297.25829,101.81818)">
+ <g
+ transform="translate(233.76623,-28.71069)"
+ id="g7289">
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="124.81962"
+ y="80.50071"
+ id="text2160"><tspan
+ sodipodi:role="line"
+ id="tspan2162"
+ x="124.81962"
+ y="80.50071"
+ style="font-weight:normal">DN: uid=john,ou=people,dc=example,dc=com</tspan><tspan
+ sodipodi:role="line"
+ x="124.81962"
+ y="95.50071"
+ id="tspan2164">uid: john</tspan><tspan
+ sodipodi:role="line"
+ x="124.81962"
+ y="110.50071"
+ id="tspan2166">mail: john@example.com</tspan><tspan
+ sodipodi:role="line"
+ x="124.81962"
+ y="125.50071"
+ id="tspan2168">(...)</tspan></text>
+ <rect
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.79348463px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="rect2182"
+ width="298.90784"
+ height="68.027596"
+ x="121.10886"
+ y="65.967438" />
+ <rect
+ style="opacity:0.28915663;fill:#aa9ab2;fill-opacity:1;stroke:none;stroke-width:0.69669151;stroke-opacity:1"
+ id="rect3159"
+ width="298.28311"
+ height="17.619322"
+ x="121.06046"
+ y="66.674454" />
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="358.58585"
+ y="51.79002"
+ id="text7300"><tspan
+ sodipodi:role="line"
+ id="tspan7302"
+ x="358.58585"
+ y="51.79002"
+ style="font-weight:normal">DN: uid=john,ou=people,dc=example,dc=com</tspan><tspan
+ sodipodi:role="line"
+ x="358.58585"
+ y="66.79002"
+ id="tspan7304">uid: john</tspan><tspan
+ sodipodi:role="line"
+ x="358.58585"
+ y="81.79002"
+ id="tspan7306">mail: john@example.com</tspan><tspan
+ sodipodi:role="line"
+ x="358.58585"
+ y="96.79002"
+ id="tspan7308">(...)</tspan></text>
+ <rect
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.79348463px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="rect7310"
+ width="298.90784"
+ height="68.027596"
+ x="354.87509"
+ y="37.256748" />
+ <rect
+ style="opacity:0.28915663;fill:#aa9ab2;fill-opacity:1;stroke:none;stroke-width:0.69669151;stroke-opacity:1"
+ id="rect7312"
+ width="298.28311"
+ height="17.619322"
+ x="354.82669"
+ y="37.963764" />
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="116.88309"
+ y="48.033184"
+ id="text2170"><tspan
+ sodipodi:role="line"
+ id="tspan2172"
+ x="116.88309"
+ y="48.033184">DN: cn=tech,ou=group,dc=example,dc=com</tspan><tspan
+ sodipodi:role="line"
+ x="116.88309"
+ y="63.033184"
+ id="tspan2174">cn: tech</tspan><tspan
+ sodipodi:role="line"
+ x="116.88309"
+ y="78.033184"
+ id="tspan2176"
+ style="font-weight:normal">member: uid=john,ou=people,dc=example,dc=com</tspan><tspan
+ sodipodi:role="line"
+ x="116.88309"
+ y="93.033184"
+ id="tspan2178">member: uid=mary,ou=people,dc=example,dc=com</tspan><tspan
+ sodipodi:role="line"
+ x="116.88309"
+ y="108.03318"
+ id="tspan2180">(...)</tspan></text>
+ <rect
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.8948347px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="rect7321"
+ width="328.38803"
+ height="78.748756"
+ x="112.59964"
+ y="35.142921" />
+ <rect
+ style="opacity:0.28915663;fill:#aa9ab2;fill-opacity:1;stroke:none;stroke-width:0.69669151;stroke-opacity:1"
+ id="rect7323"
+ width="329.30765"
+ height="17.619322"
+ x="112.40244"
+ y="65.054672" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Lend);stroke-opacity:1"
+ d="M 54.112554,146.87878 C -1.5132611,141.59779 49.816091,72.233662 106.06061,73.285704"
+ id="path7352"
+ sodipodi:nodetypes="cc" />
+ </g>
+</svg>
diff --git a/doc/guide/images/src/set-following-references.svg b/doc/guide/images/src/set-following-references.svg
new file mode 100644
index 0000000..6b15705
--- /dev/null
+++ b/doc/guide/images/src/set-following-references.svg
@@ -0,0 +1,272 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="383.93671"
+ height="174.87033"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="/home/andreas/cvs/openldap-guide/images/src"
+ sodipodi:docname="set-managersecretary.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:export-filename="/home/andreas/palestra/managersecretary.png"
+ inkscape:export-xdpi="187.53"
+ inkscape:export-ydpi="187.53">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible">
+ <path
+ id="path3186"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lstart"
+ style="overflow:visible">
+ <path
+ id="path3183"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(0.8,0,0,0.8,10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Send"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Send"
+ style="overflow:visible">
+ <path
+ id="path3198"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.2,0,0,-0.2,-1.2,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart"
+ style="overflow:visible">
+ <path
+ id="path3201"
+ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.97309,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="matrix(1.1,0,0,1.1,1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path8347"
+ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.97309,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="3.1307244"
+ inkscape:cx="191.96835"
+ inkscape:cy="87.435165"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ showguides="false"
+ inkscape:window-width="1280"
+ inkscape:window-height="953"
+ inkscape:window-x="0"
+ inkscape:window-y="24"
+ width="1052.3622px"
+ height="744.09449px" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Camada 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-241.56641,-98.789978)">
+ <g
+ id="g3270"
+ transform="translate(0,-9.9371414e-6)">
+ <text
+ inkscape:export-ydpi="136.2"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ id="text2170"
+ y="112.12766"
+ x="267.92389"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="112.12766"
+ x="267.92389"
+ id="tspan2172"
+ sodipodi:role="line">DN: uid=john,ou=people,dc=example,dc=com</tspan><tspan
+ id="tspan2174"
+ y="127.12766"
+ x="267.92389"
+ sodipodi:role="line">uid: john</tspan><tspan
+ id="tspan5373"
+ y="142.12766"
+ x="267.92389"
+ sodipodi:role="line">manager: uid=mary,ou=people,dc=example,dc=com</tspan><tspan
+ id="tspan3411"
+ y="157.12766"
+ x="267.92389"
+ sodipodi:role="line" /></text>
+ <rect
+ inkscape:export-ydpi="136.2"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ y="99.161621"
+ x="263.56467"
+ height="53.761242"
+ width="331.86697"
+ id="rect7321"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.74326539px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <rect
+ inkscape:export-ydpi="136.2"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ y="130.60817"
+ x="265.52121"
+ height="17.286547"
+ width="327.07599"
+ id="rect7323"
+ style="opacity:0.28915663;fill:#aa9ab2;fill-opacity:1;stroke:none;stroke-width:0.69669151;stroke-opacity:1" />
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="299.92389"
+ y="250.12769"
+ id="text2235"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2"><tspan
+ sodipodi:role="line"
+ id="tspan2237"
+ x="299.92389"
+ y="250.12769">DN: uid=jane,ou=people,dc=example,dc=com</tspan><tspan
+ sodipodi:role="line"
+ x="299.92389"
+ y="265.12769"
+ id="tspan2239">uid: jane</tspan><tspan
+ sodipodi:role="line"
+ x="299.92389"
+ y="280.12769"
+ id="tspan2241" /><tspan
+ sodipodi:role="line"
+ x="299.92389"
+ y="295.12769"
+ id="tspan2243" /></text>
+ <rect
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.60843331px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="rect2245"
+ width="329.70166"
+ height="36.261875"
+ x="295.49725"
+ y="237.09422"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2" />
+ <g
+ id="g3279"
+ transform="translate(0,-1.3751839e-5)">
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="283.92386"
+ y="181.12766"
+ id="text2223"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2"><tspan
+ sodipodi:role="line"
+ id="tspan2225"
+ x="283.92386"
+ y="181.12766">DN: uid=mary,ou=people,dc=example,dc=com</tspan><tspan
+ sodipodi:role="line"
+ x="283.92386"
+ y="196.12766"
+ id="tspan2227">uid: mary</tspan><tspan
+ sodipodi:role="line"
+ x="283.92386"
+ y="211.12766"
+ id="tspan2229">secretary: uid=jane,ou=people,dc=example,dc=com</tspan><tspan
+ sodipodi:role="line"
+ x="283.92386"
+ y="226.12766"
+ id="tspan2231" /></text>
+ <rect
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.74326539px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="rect2233"
+ width="331.86697"
+ height="53.761246"
+ x="279.56464"
+ y="168.16162"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2" />
+ <rect
+ style="opacity:0.28915663;fill:#aa9ab2;fill-opacity:1;stroke:none;stroke-width:0.69669151;stroke-opacity:1"
+ id="rect2247"
+ width="327.07599"
+ height="17.286547"
+ x="281.52118"
+ y="197.60815"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2" />
+ </g>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Lend);stroke-opacity:1"
+ d="M 147.97396,105.42967 C 100.43828,122.29717 161.77464,141.46478 161.77464,141.46478"
+ id="path2275"
+ transform="translate(112.15223,34.695502)"
+ sodipodi:nodetypes="cc" />
+ <path
+ sodipodi:nodetypes="cc"
+ id="path3248"
+ d="M 276.12619,208.12517 C 228.59051,224.99267 289.92687,244.16028 289.92687,244.16028"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Lend);stroke-opacity:1" />
+ </g>
+</svg>
diff --git a/doc/guide/images/src/set-memberUid.svg b/doc/guide/images/src/set-memberUid.svg
new file mode 100644
index 0000000..52e958b
--- /dev/null
+++ b/doc/guide/images/src/set-memberUid.svg
@@ -0,0 +1,272 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="746.3288"
+ height="127.80122"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.45.1"
+ version="1.0"
+ sodipodi:docbase="/home/andreas/cvs/openldap-guide/images/src"
+ sodipodi:docname="set-memberUid.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:export-filename="/home/andreas/set-recursivegroup.png"
+ inkscape:export-xdpi="70.18"
+ inkscape:export-ydpi="70.18">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible">
+ <path
+ id="path3186"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lstart"
+ style="overflow:visible">
+ <path
+ id="path3183"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(0.8,0,0,0.8,10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Send"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Send"
+ style="overflow:visible">
+ <path
+ id="path3198"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.2,0,0,-0.2,-1.2,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart"
+ style="overflow:visible">
+ <path
+ id="path3201"
+ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.97309,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="matrix(1.1,0,0,1.1,1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path8347"
+ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.97309,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.6105502"
+ inkscape:cx="373.1644"
+ inkscape:cy="63.900612"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ showguides="false"
+ inkscape:window-width="1280"
+ inkscape:window-height="953"
+ inkscape:window-x="0"
+ inkscape:window-y="24"
+ width="1052.3622px"
+ height="744.09449px" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Camada 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-164.76663,-192.97633)">
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="177.73021"
+ y="206.31401"
+ id="text2170"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2"><tspan
+ sodipodi:role="line"
+ id="tspan2172"
+ x="177.73021"
+ y="206.31401">DN: cn=sudoadm,ou=group,dc=example,dc=com</tspan><tspan
+ sodipodi:role="line"
+ x="177.73021"
+ y="221.31401"
+ id="tspan2174">cn: sudoadm</tspan><tspan
+ sodipodi:role="line"
+ x="177.73021"
+ y="236.31401"
+ id="tspan5373">objectClass: posixGroup</tspan><tspan
+ sodipodi:role="line"
+ x="177.73021"
+ y="251.31401"
+ id="tspan2336">gidNumber: 1000</tspan><tspan
+ sodipodi:role="line"
+ x="177.73021"
+ y="266.31401"
+ id="tspan3411">memberUid: john</tspan></text>
+ <rect
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.98517001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="rect7321"
+ width="316.56842"
+ height="99.014832"
+ x="173.49196"
+ y="193.46892"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2" />
+ <rect
+ inkscape:export-ydpi="136.2"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ y="255.51881"
+ x="175.66292"
+ height="16.666452"
+ width="107.33646"
+ id="rect5582"
+ style="opacity:0.28915663;fill:#aa9ab2;fill-opacity:1;stroke:none;stroke-width:0.69669151;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-start:url(#Arrow2Lstart);marker-end:url(#Arrow2Lend);stroke-opacity:1"
+ d="M 288.18971,264.67045 C 388.9562,262.34006 478.83987,220.53502 612.19092,219.08835"
+ id="path7687"
+ sodipodi:nodetypes="cc"
+ inkscape:export-filename="/home/andreas/set-recursivegroup.png"
+ inkscape:export-xdpi="80.970001"
+ inkscape:export-ydpi="80.970001" />
+ <g
+ id="g3381"
+ transform="translate(86,0)">
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="534.08191"
+ y="208.5367"
+ id="text3318"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2"><tspan
+ sodipodi:role="line"
+ id="tspan3320"
+ x="534.08191"
+ y="208.5367">DN: uid=john,ou=people,dc=example,dc=com</tspan><tspan
+ sodipodi:role="line"
+ x="534.08191"
+ y="223.5367"
+ id="tspan3322">uid: john</tspan><tspan
+ sodipodi:role="line"
+ x="534.08191"
+ y="238.5367"
+ id="tspan3324">objectClass: person</tspan><tspan
+ sodipodi:role="line"
+ x="534.08191"
+ y="253.5367"
+ id="tspan3326">cn: john</tspan><tspan
+ id="tspan3334"
+ sodipodi:role="line"
+ x="534.08191"
+ y="268.5367">givenName: John</tspan><tspan
+ sodipodi:role="line"
+ x="534.08191"
+ y="283.5367"
+ id="tspan3330">sn: Smith</tspan></text>
+ <rect
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.94494522px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="rect3336"
+ width="294.23233"
+ height="98.00956"
+ x="530.39062"
+ y="194.49431"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2" />
+ <rect
+ inkscape:export-ydpi="136.2"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ y="211.30989"
+ x="533.61841"
+ height="16.666452"
+ width="57.336445"
+ id="rect2372"
+ style="opacity:0.28915663;fill:#aa9ab2;fill-opacity:1;stroke:none;stroke-width:0.69669151;stroke-opacity:1" />
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:italic;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="164.28616"
+ y="318.28146"
+ id="text3369"><tspan
+ sodipodi:role="line"
+ id="tspan3371"
+ x="164.28616"
+ y="318.28146">[cn=sudoadm,ou=group,dc=example,dc=com]/memberUid</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:italic;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="646.18683"
+ y="318.97287"
+ id="text3373"><tspan
+ sodipodi:role="line"
+ id="tspan3375"
+ x="646.18683"
+ y="318.97287">user/uid</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:italic;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="581.57733"
+ y="319.33908"
+ id="text3377"><tspan
+ sodipodi:role="line"
+ id="tspan3379"
+ x="581.57733"
+ y="319.33908">&amp;</tspan></text>
+ </g>
+</svg>
diff --git a/doc/guide/images/src/set-recursivegroup.svg b/doc/guide/images/src/set-recursivegroup.svg
new file mode 100644
index 0000000..8f2a65b
--- /dev/null
+++ b/doc/guide/images/src/set-recursivegroup.svg
@@ -0,0 +1,505 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="833.63007"
+ height="212.5425"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.47pre4 r22446"
+ version="1.0"
+ sodipodi:docname="set-recursivegroup.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:export-filename="/home/andreas/set-recursivegroup.png"
+ inkscape:export-xdpi="70.18"
+ inkscape:export-ydpi="70.18">
+ <defs
+ id="defs4">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 106.27125 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="833.63007 : 106.27125 : 1"
+ inkscape:persp3d-origin="416.81503 : 70.847499 : 1"
+ id="perspective3053" />
+ <marker
+ inkscape:stockid="Arrow1Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lend"
+ style="overflow:visible">
+ <path
+ id="path3186"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.8,0,0,-0.8,-10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Lstart"
+ style="overflow:visible">
+ <path
+ id="path3183"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(0.8,0,0,0.8,10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Send"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Send"
+ style="overflow:visible">
+ <path
+ id="path3198"
+ d="M 0,0 L 5,-5 L -12.5,0 L 5,5 L 0,0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
+ transform="matrix(-0.2,0,0,-0.2,-1.2,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lstart"
+ style="overflow:visible">
+ <path
+ id="path3201"
+ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.97309,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="matrix(1.1,0,0,1.1,1.1,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible">
+ <path
+ id="path8347"
+ style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.97309,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2.9689479"
+ inkscape:cx="232.40369"
+ inkscape:cy="118.87263"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ showguides="false"
+ inkscape:window-width="1655"
+ inkscape:window-height="1001"
+ inkscape:window-x="0"
+ inkscape:window-y="25"
+ width="1052.3622px"
+ height="744.09449px"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Camada 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-39.91817,-73.881854)">
+ <g
+ id="g3462"
+ transform="translate(30.553822,-0.6080081)"
+ inkscape:export-filename="/home/andreas/set-recursivegroup.png"
+ inkscape:export-xdpi="80.970001"
+ inkscape:export-ydpi="80.970001">
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="523.97247"
+ y="89.280624"
+ id="text3318"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2"><tspan
+ sodipodi:role="line"
+ id="tspan3320"
+ x="523.97247"
+ y="89.280624">DN: <tspan
+ style="font-weight:bold"
+ id="tspan7581">uid=john,ou=people,dc=example,dc=com</tspan></tspan><tspan
+ sodipodi:role="line"
+ x="523.97247"
+ y="104.28062"
+ id="tspan3322">uid: john</tspan><tspan
+ sodipodi:role="line"
+ x="523.97247"
+ y="119.28062"
+ id="tspan3324">objectClass: person</tspan><tspan
+ sodipodi:role="line"
+ x="523.97247"
+ y="134.28062"
+ id="tspan3326">cn: john</tspan><tspan
+ id="tspan3334"
+ sodipodi:role="line"
+ x="523.97247"
+ y="149.28062">givenName: John</tspan><tspan
+ sodipodi:role="line"
+ x="523.97247"
+ y="164.28062"
+ id="tspan3330">sn: Smith</tspan></text>
+ <rect
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.97567958px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="rect3336"
+ width="318.06735"
+ height="96.658691"
+ x="520.29657"
+ y="75.253609"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2" />
+ </g>
+ <g
+ id="g3474"
+ transform="translate(30.276908,4.0242246)"
+ inkscape:export-filename="/home/andreas/set-recursivegroup.png"
+ inkscape:export-xdpi="80.970001"
+ inkscape:export-ydpi="80.970001">
+ <g
+ id="g7676">
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="523.97247"
+ y="199.28062"
+ id="text3416"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2"><tspan
+ sodipodi:role="line"
+ id="tspan3418"
+ x="523.97247"
+ y="199.28062">DN: <tspan
+ id="tspan7674"
+ style="font-weight:bold">uid=mary,ou=people,dc=example,dc=com</tspan></tspan><tspan
+ sodipodi:role="line"
+ x="523.97247"
+ y="214.28062"
+ id="tspan3420">uid: mary</tspan><tspan
+ sodipodi:role="line"
+ x="523.97247"
+ y="229.28062"
+ id="tspan3422">objectClass: person</tspan><tspan
+ sodipodi:role="line"
+ x="523.97247"
+ y="244.28062"
+ id="tspan3424">cn: mary</tspan><tspan
+ id="tspan3426"
+ sodipodi:role="line"
+ x="523.97247"
+ y="259.28062">givenName: Mary</tspan><tspan
+ sodipodi:role="line"
+ x="523.97247"
+ y="274.28062"
+ id="tspan3432">sn: Smith</tspan></text>
+ <rect
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.98239046px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ id="rect3460"
+ width="322.48019"
+ height="96.651978"
+ x="520.29993"
+ y="185.25696"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2" />
+ </g>
+ </g>
+ <g
+ id="g7550"
+ transform="translate(-109.4887,-12.321663)"
+ inkscape:export-filename="/home/andreas/set-recursivegroup.png"
+ inkscape:export-xdpi="80.970001"
+ inkscape:export-ydpi="80.970001">
+ <g
+ id="g7614"
+ transform="translate(-103.41823,-0.8839165)">
+ <text
+ inkscape:export-ydpi="136.2"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ id="text3350"
+ y="216.91795"
+ x="258.37482"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="216.91795"
+ x="258.37482"
+ id="tspan3352"
+ sodipodi:role="line">DN: cn=accountadm,ou=group,dc=example,dc=com</tspan><tspan
+ id="tspan3354"
+ y="231.91795"
+ x="258.37482"
+ sodipodi:role="line">cn: accountadm</tspan><tspan
+ id="tspan3356"
+ y="246.91795"
+ x="258.37482"
+ sodipodi:role="line">objectClass: groupOfNames</tspan><tspan
+ id="tspan3360"
+ y="261.91795"
+ x="258.37482"
+ sodipodi:role="line">member: <tspan
+ id="tspan7612"
+ style="font-weight:bold">uid=mary,ou=people,dc=example,dc=com</tspan></tspan><tspan
+ id="tspan3362"
+ y="276.91795"
+ x="258.37482"
+ sodipodi:role="line" /></text>
+ <rect
+ inkscape:export-ydpi="136.2"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ y="203.48654"
+ x="254.13257"
+ height="83.046989"
+ width="371.37915"
+ id="rect3402"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.97723264px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <rect
+ inkscape:export-ydpi="136.2"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ y="249.90959"
+ x="256.3075"
+ height="16.297295"
+ width="351.43427"
+ id="rect5542"
+ style="opacity:0.28915663;fill:#aa9ab2;fill-opacity:1;stroke:none;stroke-width:0.69669151;stroke-opacity:1" />
+ </g>
+ </g>
+ <g
+ id="g7662"
+ transform="translate(-217.44346,0.8839165)"
+ inkscape:export-filename="/home/andreas/set-recursivegroup.png"
+ inkscape:export-xdpi="80.970001"
+ inkscape:export-ydpi="80.970001">
+ <text
+ inkscape:export-ydpi="136.2"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ id="text2170"
+ y="86.335617"
+ x="262.09247"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ y="86.335617"
+ x="262.09247"
+ id="tspan2172"
+ sodipodi:role="line">DN: <tspan
+ style="font-weight:bold"
+ id="tspan7595">cn=sudoadm,ou=group,dc=example,dc=com</tspan></tspan><tspan
+ id="tspan2174"
+ y="101.33562"
+ x="262.09247"
+ sodipodi:role="line">cn: sudoadm</tspan><tspan
+ id="tspan5373"
+ y="116.33562"
+ x="262.09247"
+ sodipodi:role="line">objectClass: groupOfNames</tspan><tspan
+ id="tspan3295"
+ y="131.33562"
+ x="262.09247"
+ sodipodi:role="line">member: uid=john,ou=people,dc=example,dc=com</tspan><tspan
+ id="tspan3297"
+ y="146.33562"
+ x="262.09247"
+ sodipodi:role="line">member: cn=accountadm,ou=group,dc=example,dc=com</tspan><tspan
+ id="tspan3411"
+ y="161.33562"
+ x="262.09247"
+ sodipodi:role="line" /></text>
+ <rect
+ inkscape:export-ydpi="136.2"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ y="73.485397"
+ x="257.84909"
+ height="83.049301"
+ width="369.61365"
+ id="rect7321"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.97492063px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <rect
+ style="opacity:0.28915663;fill:#aa9ab2;fill-opacity:1;stroke:none"
+ id="rect5582"
+ width="360.11356"
+ height="31.950695"
+ x="260.02518"
+ y="120.25619"
+ inkscape:export-filename="/home/andreas/palestra/allmail.png"
+ inkscape:export-xdpi="136.2"
+ inkscape:export-ydpi="136.2" />
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:italic;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="364.2525"
+ y="224.56728"
+ id="text7528"
+ inkscape:export-filename="/home/andreas/set-recursivegroup.png"
+ inkscape:export-xdpi="80.970001"
+ inkscape:export-ydpi="80.970001"><tspan
+ sodipodi:role="line"
+ id="tspan7530"
+ x="364.2525"
+ y="224.56728">yes!</tspan></text>
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Lend);stroke-opacity:1"
+ d="M 365.29385,128.78999 C 466.06034,130.87918 457.22118,89.335108 547.38066,84.915525"
+ id="path7687"
+ sodipodi:nodetypes="cc"
+ inkscape:export-filename="/home/andreas/set-recursivegroup.png"
+ inkscape:export-xdpi="80.970001"
+ inkscape:export-ydpi="80.970001" />
+ <path
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Lend);stroke-opacity:1"
+ d="m 404.35364,144.89531 c 26.30535,21.71639 24.5822,55.81327 -30.22062,56.69719"
+ id="path7689"
+ sodipodi:nodetypes="cc"
+ inkscape:export-filename="/home/andreas/set-recursivegroup.png"
+ inkscape:export-xdpi="80.970001"
+ inkscape:export-ydpi="80.970001" />
+ <path
+ sodipodi:nodetypes="cc"
+ id="path7691"
+ d="M 396.23093,243.6739 C 484.62258,241.34352 479.3191,199.79944 547.38066,198.91553"
+ style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker-end:url(#Arrow2Lend);stroke-opacity:1"
+ inkscape:export-filename="/home/andreas/set-recursivegroup.png"
+ inkscape:export-xdpi="80.970001"
+ inkscape:export-ydpi="80.970001" />
+ <text
+ id="text9637"
+ y="232.54912"
+ x="473.47699"
+ style="font-size:12px;font-style:italic;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"
+ inkscape:export-filename="/home/andreas/set-recursivegroup.png"
+ inkscape:export-xdpi="80.970001"
+ inkscape:export-ydpi="80.970001"><tspan
+ y="232.54912"
+ x="473.47699"
+ id="tspan9639"
+ sodipodi:role="line">more<tspan
+ id="tspan9641"
+ style="font-weight:bold" /></tspan><tspan
+ y="247.54912"
+ x="473.47699"
+ sodipodi:role="line"
+ id="tspan9643"><tspan
+ style="font-weight:bold"
+ id="tspan9645">member</tspan>?</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:italic;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="473.47699"
+ y="112.54912"
+ id="text9647"
+ inkscape:export-filename="/home/andreas/set-recursivegroup.png"
+ inkscape:export-xdpi="80.970001"
+ inkscape:export-ydpi="80.970001"><tspan
+ sodipodi:role="line"
+ id="tspan9649"
+ x="473.47699"
+ y="112.54912">more<tspan
+ style="font-weight:bold"
+ id="tspan9651" /></tspan><tspan
+ id="tspan9653"
+ sodipodi:role="line"
+ x="473.47699"
+ y="127.54912"><tspan
+ id="tspan9655"
+ style="font-weight:bold">member</tspan>?</tspan></text>
+ <text
+ id="text10626"
+ y="173.85262"
+ x="431.01266"
+ style="font-size:12px;font-style:italic;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"
+ inkscape:export-filename="/home/andreas/set-recursivegroup.png"
+ inkscape:export-xdpi="80.970001"
+ inkscape:export-ydpi="80.970001"><tspan
+ y="173.85262"
+ x="431.01266"
+ id="tspan10628"
+ sodipodi:role="line">more<tspan
+ id="tspan10630"
+ style="font-weight:bold" /></tspan><tspan
+ y="188.85262"
+ x="431.01266"
+ sodipodi:role="line"
+ id="tspan10632"><tspan
+ style="font-weight:bold"
+ id="tspan10634">member</tspan>?</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:italic;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="742.7262"
+ y="130.87918"
+ id="text10640"
+ inkscape:export-filename="/home/andreas/set-recursivegroup.png"
+ inkscape:export-xdpi="80.970001"
+ inkscape:export-ydpi="80.970001"><tspan
+ sodipodi:role="line"
+ id="tspan10642"
+ x="742.7262"
+ y="130.87918">no <tspan
+ style="font-weight:bold"
+ id="tspan10648">member</tspan></tspan><tspan
+ sodipodi:role="line"
+ x="742.7262"
+ y="145.87918"
+ id="tspan10644">here!</tspan></text>
+ <text
+ id="text10650"
+ y="244.87918"
+ x="742.7262"
+ style="font-size:12px;font-style:italic;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ xml:space="preserve"
+ inkscape:export-filename="/home/andreas/set-recursivegroup.png"
+ inkscape:export-xdpi="80.970001"
+ inkscape:export-ydpi="80.970001"><tspan
+ y="244.87918"
+ x="742.7262"
+ id="tspan10652"
+ sodipodi:role="line">no <tspan
+ id="tspan10654"
+ style="font-weight:bold">member</tspan></tspan><tspan
+ id="tspan10656"
+ y="259.87918"
+ x="742.7262"
+ sodipodi:role="line">here!</tspan></text>
+ </g>
+</svg>
diff --git a/doc/guide/images/src/syncrepl-firewalls.dia b/doc/guide/images/src/syncrepl-firewalls.dia
new file mode 100644
index 0000000..3f159ea
--- /dev/null
+++ b/doc/guide/images/src/syncrepl-firewalls.dia
Binary files differ
diff --git a/doc/guide/images/src/syncrepl-pull.dia b/doc/guide/images/src/syncrepl-pull.dia
new file mode 100644
index 0000000..3f159ea
--- /dev/null
+++ b/doc/guide/images/src/syncrepl-pull.dia
Binary files differ
diff --git a/doc/guide/images/src/syncrepl-push.dia b/doc/guide/images/src/syncrepl-push.dia
new file mode 100644
index 0000000..3f159ea
--- /dev/null
+++ b/doc/guide/images/src/syncrepl-push.dia
Binary files differ
diff --git a/doc/guide/images/src/syncrepl.dia b/doc/guide/images/src/syncrepl.dia
new file mode 100644
index 0000000..3f159ea
--- /dev/null
+++ b/doc/guide/images/src/syncrepl.dia
Binary files differ
diff --git a/doc/guide/plain.sdf b/doc/guide/plain.sdf
new file mode 100644
index 0000000..26382fa
--- /dev/null
+++ b/doc/guide/plain.sdf
@@ -0,0 +1,20 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+# template for plain documents
+!macro HTML_PRE_SECTION
+!endmacro
+!macro HTML_POST_SECTION
+!endmacro
+
+!macro HTML_HEADER
+!endmacro
+!macro HTML_FOOTER
+{{INLINE:<FONT COLOR="#808080" FACE="Arial,Verdana,Helvetica" SIZE="1">}}
+{{INLINE:<B>________________<BR><SMALL>}}
+[[c]] Copyright 2011-2022,
+{{INLINE:<A HREF="/foundation/">OpenLDAP Foundation</A>}},
+{{EMAIL: info@OpenLDAP.org}}
+{{INLINE:</SMALL><BR></B></FONT>}}
+!endmacro
diff --git a/doc/guide/preamble.sdf b/doc/guide/preamble.sdf
new file mode 100644
index 0000000..4ffe2a1
--- /dev/null
+++ b/doc/guide/preamble.sdf
@@ -0,0 +1,312 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+#
+# Preamble for all OpenLDAP SDF documents
+#
+
+!default VERSION 2.5
+
+#
+# Paths are relative to the main subdirectories
+#
+
+!define DOC_AUTHOR "The OpenLDAP Project <{{URL:https://www.openldap.org/}}>"
+!define DOC_NAME "OpenLDAP Software 2.5"
+!define DOC_TYPE "Guide"
+
+!define DOC_LOGO "../images/LDAPlogo.gif"
+!define DOC_LOGO_BASE "../images/"
+!define DOC_HTML_LOGO ""
+
+!define DOC_LOF_TITLE "Figures"
+!define DOC_LOF
+
+!define HTML_URL_HOME "https://www.openldap.org/"
+!define HTML_URL_CATALOG "../index.html"
+
+!macro HTML_HEADER
+# !block inline
+#<FONT FACE="Arial,Verdana,Helvetica">
+# !endblock
+ !if DOC_LOGO
+ !block inline
+<A HREF="https://www.OpenLDAP.org/">
+ !endblock
+ !import DOC_LOGO; align="Left"; border="0"; base=$var{'DOC_LOGO_BASE'}
+ !block inline
+</A>
+ !endblock
+ !HTML_NAVIGATE
+ !clear "Left"
+ !else
+ !HTML_NAVIGATE
+ !endif
+!endmacro
+
+!macro HTML_FOOTER
+ !HTML_PRE_SECTION
+ !HTML_NAVIGATE
+# !block inline; expand
+#</FONT>
+# !endblock
+ !block inline; expand
+<P>
+<FONT COLOR="#808080" FACE="Arial,Verdana,Helvetica" SIZE="1"><B>
+________________<BR>
+<SMALL>&copy; Copyright 2011-2022, <A HREF="https://www.OpenLDAP.org/foundation/">OpenLDAP Foundation</A>, <A HREF="mailto:info@OpenLDAP.org">info@OpenLDAP.org</A></SMALL></B></FONT>
+
+ !endblock
+!endmacro
+
+!macro HTML_TOPIC_HEADER
+# !block inline; expand
+#<FONT FACE="Arial,Verdana,Helvetica">
+# !endblock
+ !define DOC_TOPIC_LOGO $var{'DOC_LOGO'}
+ !if DOC_TOPIC_LOGO
+ !default DOC_TOPIC_LOGO_BASE $var{'DOC_LOGO_BASE'}
+ !block inline
+<A HREF="https://www.OpenLDAP.org/">
+ !endblock
+ !import DOC_TOPIC_LOGO; align="Left"; border="0"; base=$var{'DOC_TOPIC_LOGO_BASE'}
+ !block inline
+</A>
+ !endblock
+ !HTML_TOPIC_NAVIGATE
+ !clear "Left"
+ !else
+ !HTML_TOPIC_NAVIGATE
+ !endif
+!endmacro
+
+!macro HTML_TOPIC_FOOTER
+ !HTML_PRE_SECTION
+ !HTML_TOPIC_NAVIGATE
+# !block inline; expand
+#</FONT>
+# !endblock
+ !block inline; expand
+<P>
+<FONT COLOR="#808080" FACE="Arial,Verdana,Helvetica" SIZE="1"><B>
+________________<BR>
+<SMALL>&copy; Copyright 2011-2022, <A HREF="https://www.OpenLDAP.org/foundation/">OpenLDAP Foundation</A>, <A HREF="mailto:info@OpenLDAP.org">info@OpenLDAP.org</A></SMALL></B></FONT>
+
+ !endblock
+!endmacro
+
+
+# OpenLDAP related organization
+!block organisations; data; sort='Name'
+Name|Long|Jump
+ANSI|American National Standards Institute|https://www.ansi.org/
+BSI|British Standards Institute|https://www.bsigroup.com/en-GB/
+COSINE|Co-operation and Open Systems Interconnection in Europe
+CPAN|Comprehensive Perl Archive Network|https://www.cpan.org/
+Cyrus|Project Cyrus|https://www.cyrusimap.org/
+FSF|Free Software Foundation|https://www.fsf.org/
+GNU|GNU Not Unix Project|https://www.gnu.org/
+IAB|Internet Architecture Board|https://www.iab.org/
+IANA|Internet Assigned Numbers Authority|https://www.iana.org/
+IEEE|Institute of Electrical and Electronics Engineers|https://www.ieee.org
+IESG|Internet Engineering Steering Group|https://www.ietf.org/about/groups/iesg/
+IETF|Internet Engineering Task Force|https://www.ietf.org/
+IRTF|Internet Research Task Force|https://irtf.org/
+ISO|International Standards Organisation|https://www.iso.org/
+ISOC|Internet Society|https://www.internetsociety.org/
+ITU|International Telecommunication Union|https://www.itu.int/
+OLF|OpenLDAP Foundation|https://www.openldap.org/foundation/
+OLP|OpenLDAP Project|https://www.openldap.org/project/
+OpenSSL|OpenSSL Project|https://www.openssl.org/
+RFC Editor|RFC Editor|https://www.rfc-editor.org/
+Oracle|Oracle Corporation|https://www.oracle.com/
+UM|University of Michigan|https://www.umich.edu/
+UMLDAP|University of Michigan LDAP Team|https://web.archive.org/web/20160302011357/http://www.umich.edu/~dirsvcs/ldap/ldap.html
+!endblock
+
+!block products; data; sort='Name'
+Name|Jump
+Cyrus|https://www.cyrusimap.org/
+Cyrus SASL|https://www.cyrusimap.org/sasl/
+Git|https://git-scm.com/
+GNU|https://www.gnu.org/software/
+GnuTLS|https://gnutls.org/
+Heimdal|https://github.com/heimdal/
+JLDAP|https://www.openldap.org/jldap/
+libevent|https://libevent.org/
+MIT Kerberos|https://web.mit.edu/kerberos/
+OpenLDAP|https://www.openldap.org/
+OpenLDAP FAQ|https://www.openldap.org/faq/
+OpenLDAP ITS|https://bugs.openldap.org/
+OpenLDAP Software|https://www.openldap.org/software/
+OpenSSL|https://www.openssl.org/
+Perl|https://www.perl.org/
+SDF|https://metacpan.org/release/sdf
+UMLDAP|https://web.archive.org/web/20160302011357/http://www.umich.edu/~dirsvcs/ldap/ldap.html
+!endblock
+
+# Internet and X.500 terms
+!block terms; data; sort='Term'
+Term|Definition
+3DES|Triple DES
+ABNF|Augmented Backus-Naur Form
+ACDF|Access Control Decision Function
+ACE|ASCII Compatible Encoding
+ASCII|American Standard Code for Information Interchange
+ACID|Atomicity, Consistency, Isolation, and Durability
+ACI|Access Control Information
+ACL|Access Control List
+AES|Advance Encryption Standard
+ABI|Application Binary Interface
+API|Application Program Interface
+ASN.1|Abstract Syntax Notation - One
+AVA|Attribute Value Assertion
+AuthcDN|Authentication DN
+AuthcId|Authentication Identity
+AuthzDN|Authorization DN
+AuthzId|Authorization Identity
+BCP|Best Current Practice
+BER|Basic Encoding Rules
+BNF|Backus-Naur Form
+C|The C Programming Language
+CA|Certificate Authority
+CER|Canonical Encoding Rules
+CLDAP|Connection-less LDAP
+CN|Common Name
+CRAM-MD5|SASL MD5 Challenge/Response Authentication Mechanism
+CRL|Certificate Revocation List
+DAP|Directory Access Protocol
+DC|Domain Component
+DER|Distinguished Encoding Rules
+DES|Data Encryption Standard
+DIB|Directory Information Base
+DIGEST-MD5|SASL Digest MD5 Authentication Mechanism
+DISP|Directory Information Shadowing Protocol
+DIT|Directory Information Tree
+DNS|Domain Name System
+DN|Distinguished Name
+DOP|Directory Operational Binding Management Protocol
+DSAIT|DSA Information Tree
+DSA|Directory System Agent
+DSE|DSA-specific Entry
+DSP|Directory System Protocol
+DS|Draft Standard
+DUA|Directory User Agent
+EXTERNAL|SASL External Authentication Mechanism
+FAQ|Frequently Asked Questions
+FTP|File Transfer Protocol
+FYI|For Your Information
+GSER|Generic String Encoding Rules
+GSS-API|Generic Security Service Application Program Interface
+GSSAPI|SASL Kerberos V GSS-API Authentication Mechanism
+I-D|Internet-Draft
+IA5|International Alphabet 5
+IDNA|Internationalized Domain Names in Applications
+IDN|Internationalized Domain Name
+ID|Identification
+ID|Identifier
+IDL|Index Data Lookups
+IP|Internet Protocol
+IPC|Inter-process communication
+IPsec|Internet Protocol Security
+IPv4|Internet Protocol, version 4
+IPv6|Internet Protocol, version 6
+ITS|Issue Tracking System
+JPEG|Joint Photographic Experts Group
+Kerberos|Kerberos Authentication Service
+LBER|Lightweight BER
+LDAP|Lightweight Directory Access Protocol
+LDAP Sync|LDAP Content Synchronization
+LDAPv3|LDAP, version 3
+LDIF|LDAP Data Interchange Format
+LMDB|Lightning Memory-Mapped Database
+MD5|Message Digest 5
+MDB|Memory-Mapped Database (Backend)
+MIB|Management Information Base
+MODDN|Modify DN
+MODRDN|Modify RDN
+NSSR|Non-specific Subordinate Reference
+OID|Object Identifier
+OSI|Open Systems Interconnect
+OTP|One Time Password
+PDU|Protocol Data Unit
+PEM|Privacy Enhanced eMail
+PEN|Private Enterprise Number
+PKCS|Public Key Cryptosystem
+PKI|Public Key Infrastructure
+PKIX|Public Key Infrastructure (X.509)
+PLAIN|SASL Plaintext Password Authentication Mechanism
+POSIX|Portable Operating System Interface
+PS|Proposed Standard
+RDN|Relative Distinguished Name
+RFC|Request for Comments
+RPC|Remote Procedure Call
+RXER|Robust XML Encoding Rules
+SASL|Simple Authentication and Security Layer
+SDF|Simple Document Format
+SDSE|Shadowed DSE
+SHA1|Secure Hash Algorithm 1
+SLAPD|Standalone LDAP Daemon
+SLURPD|Standalone LDAP Update Replication Daemon
+SMTP|Simple Mail Transfer Protocol
+SNMP|Simple Network Management Protocol
+SQL|Structured Query Language
+SRP|Secure Remote Password
+SSF|Security Strength Factor
+SSL|Secure Socket Layer
+STD|Internet Standard
+TCP|Transmission Control Protocol
+TLS|Transport Layer Security
+UCS|Universal Multiple-Octet Coded Character Set
+UDP|User Datagram Protocol
+UID|User Identifier
+Unicode|The Unicode Standard
+UNIX|Unix
+URI|Uniform Resource Identifier
+URL|Uniform Resource Locator
+URN|Uniform Resource Name
+UTF-8|8-bit UCS/Unicode Transformation Format
+UTR|Unicode Technical Report
+UUID|Universally Unique Identifier
+WWW|World Wide Web
+X.500|X.500 Directory Services
+X.509|X.509 Public Key and Attribute Certificate Frameworks
+XED|XML Enabled Directory
+XER|XML Encoding Rules
+XML|Extensible Markup Language
+syncrepl|LDAP Sync-based Replication
+lloadd|LDAP Load Balancer
+!endblock
+
+!block references; data; sort=Reference; style=grid
+Reference|Status|Document|Jump
+UM-GUIDE|O|The SLAPD and SLURPD Administrators Guide|https://web.archive.org/web/20170809071245/http://www.umich.edu/~dirsvcs/ldap/doc/guides/slapd/guide.pdf
+RFC2079|PS|Definition of an X.500 Attribute Type and an Object Class to Hold Uniform Resource Identifiers|https://www.rfc-editor.org/rfc/rfc2079.txt
+RFC2296|PS|Use of Language Codes in LDAP|https://www.rfc-editor.org/rfc/rfc2296.txt
+RFC2307|X|An Approach for Using LDAP as a Network Information Service|https://www.rfc-editor.org/rfc/rfc2307.txt
+RFC2589|PS|Lightweight Directory Access Protocol (v3): Extensions for Dynamic Directory Services|https://www.rfc-editor.org/rfc/rfc2589.txt
+RFC2798|I|Definition of the inetOrgPerson LDAP Object Class|https://www.rfc-editor.org/rfc/rfc2798.txt
+RFC2831|PS|Using Digest Authentication as a SASL Mechanism|https://www.rfc-editor.org/rfc/rfc2831.txt
+RFC2849|PS|The LDAP Data Interchange Format|https://www.rfc-editor.org/rfc/rfc2849.txt
+RFC3088|X|OpenLDAP Root Service|https://www.rfc-editor.org/rfc/rfc3088.txt
+RFC3296|PS|Named Subordinate References in LDAP|https://www.rfc-editor.org/rfc/rfc3296.txt
+RFC3384|I|Lightweight Directory Access Protocol (version 3) Replication Requirements|https://www.rfc-editor.org/rfc/rfc3384.txt
+RFC3494|I|Lightweight Directory Access Protocol version 2 (LDAPv2) to Historic Status|https://www.rfc-editor.org/rfc/rfc3494.txt
+RFC4013|PS|SASLprep: Stringprep Profile for User Names and Passwords|https://www.rfc-editor.org/rfc/rfc4013.txt
+RFC4346|PS|The Transport Layer Security (TLS) Protocol, Version 1.1|https://www.rfc-editor.org/rfc/rfc4346.txt
+RFC4422|PS|Simple Authentication and Security Layer (SASL)|https://www.rfc-editor.org/rfc/rfc4422.txt
+RFC4510|PS|Lightweight Directory Access Protocol (LDAP): Technical Specification Roadmap|https://www.rfc-editor.org/rfc/rfc4510.txt
+RFC4511|PS|Lightweight Directory Access Protocol (LDAP): The Protocol|https://www.rfc-editor.org/rfc/rfc4511.txt
+RFC4512|PS|Lightweight Directory Access Protocol (LDAP): Directory Information Models|https://www.rfc-editor.org/rfc/rfc4512.txt
+RFC4513|PS|Lightweight Directory Access Protocol (LDAP): Authentication Methods and Security Mechanisms|https://www.rfc-editor.org/rfc/rfc4513.txt
+RFC4514|PS|Lightweight Directory Access Protocol (LDAP): String Representation of Distinguished Names|https://www.rfc-editor.org/rfc/rfc4514.txt
+RFC4515|PS|Lightweight Directory Access Protocol (LDAP): String Representation of Search Filters|https://www.rfc-editor.org/rfc/rfc4515.txt
+RFC4516|PS|Lightweight Directory Access Protocol (LDAP): Uniform Resource Locator|https://www.rfc-editor.org/rfc/rfc4516.txt
+RFC4517|PS|Lightweight Directory Access Protocol (LDAP): Syntaxes and Matching Rules|https://www.rfc-editor.org/rfc/rfc4517.txt
+RFC4518|PS|Lightweight Directory Access Protocol (LDAP): Internationalized String Preparation|https://www.rfc-editor.org/rfc/rfc4518.txt
+RFC4519|PS|Lightweight Directory Access Protocol (LDAP): Schema for User Applications|https://www.rfc-editor.org/rfc/rfc4519.txt
+RFC4520|BCP|IANA Considerations for LDAP|https://www.rfc-editor.org/rfc/rfc4520.txt
+RFC4533|X|The Lightweight Directory Access Protocol (LDAP) Content Synchronization Operation|https://www.rfc-editor.org/rfc/rfc4533.txt
+Chu-LDAPI|ID|Using LDAP Over IPC Mechanisms|https://tools.ietf.org/html/draft-chu-ldap-ldapi-00
+!endblock
diff --git a/doc/guide/release/autoconf-install.txt b/doc/guide/release/autoconf-install.txt
new file mode 100644
index 0000000..50dbe43
--- /dev/null
+++ b/doc/guide/release/autoconf-install.txt
@@ -0,0 +1,183 @@
+Basic Installation
+==================
+
+ These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+ The file `configure.in' is used to create `configure' by a program
+called `autoconf'. You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. You can give `configure'
+initial values for variables by setting them in the environment. Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+ CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+ env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory. After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+ By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=PATH' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on. Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+ CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+ If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+ Use and save the results of the tests in FILE instead of
+ `./config.cache'. Set FILE to `/dev/null' to disable caching, for
+ debugging `configure'.
+
+`--help'
+ Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`--version'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
+
diff --git a/doc/guide/release/autoconf.sdf b/doc/guide/release/autoconf.sdf
new file mode 100644
index 0000000..008c288
--- /dev/null
+++ b/doc/guide/release/autoconf.sdf
@@ -0,0 +1,16 @@
+# $OpenLDAP$
+#
+# Generic Autoconf INSTALL
+#
+
+H1: Generic configure Instructions
+
+#!block inline
+#<FONT FACE="Courier">
+#!endblock
+
+!include "../release/autoconf-install.txt" ; verbatim
+
+#!block inline
+#</FONT>
+#!endblock
diff --git a/doc/guide/release/copyright-plain.sdf b/doc/guide/release/copyright-plain.sdf
new file mode 100644
index 0000000..f5e838e
--- /dev/null
+++ b/doc/guide/release/copyright-plain.sdf
@@ -0,0 +1,10 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+#
+# Plain Copyright for Software Distribution
+#
+!define HTML_TITLE "OpenLDAP Copyright Notices"
+!include "../plain.sdf"
+!include "copyright.sdf"; plain
diff --git a/doc/guide/release/copyright.sdf b/doc/guide/release/copyright.sdf
new file mode 100644
index 0000000..90ff8a4
--- /dev/null
+++ b/doc/guide/release/copyright.sdf
@@ -0,0 +1,89 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+# This file should match ../../../COPYRIGHT (except in formatting)
+
+!if OPT_PP_HTML
+!define copyright '[[c]] Copyright'
+!else
+!define copyright 'Copyright'
+!endif
+
+H1: OpenLDAP Software Copyright Notices
+
+H2: OpenLDAP Copyright Notice
+
+!block nofill
+[[copyright]] 1998-2013 The OpenLDAP Foundation.
+{{All rights reserved.}}
+!endblock
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted {{only as authorized}} by the {{SECT:OpenLDAP
+Public License}}.
+
+A copy of this license is available in file {{F:LICENSE}} in the
+top-level directory of the distribution or, alternatively, at
+<{{URL:http://www.OpenLDAP.org/license.html}}>.
+
+OpenLDAP is a registered trademark of the OpenLDAP Foundation.
+
+Individual files and/or contributed packages may be copyright by
+other parties and their use subject to additional restrictions.
+
+This work is derived from the University of Michigan LDAP v3.3
+distribution. Information concerning this software is available
+at <{{URL:http://www.umich.edu/~dirsvcs/ldap/ldap.html}}>.
+
+This work also contains materials derived from public sources.
+
+Additional information about OpenLDAP software can be obtained at
+<{{URL:http://www.OpenLDAP.org/}}>.
+
+
+H2: Additional Copyright Notices
+
+!block nofill
+Portions [[copyright]] 1998-2013 Kurt D. Zeilenga.
+Portions [[copyright]] 1998-2006 Net Boolean Incorporated.
+Portions [[copyright]] 2001-2006 IBM Corporation.
+{{All rights reserved.}}
+!endblock
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the {{SECT:OpenLDAP
+Public License}}.
+
+
+!block nofill
+Portions [[copyright]] 1999-2008 Howard Y.H. Chu.
+Portions [[copyright]] 1999-2008 Symas Corporation.
+Portions [[copyright]] 1998-2003 Hallvard B. Furuseth.
+Portions [[copyright]] 2007-2011 Gavin Henry.
+Portions [[copyright]] 2007-2011 Suretec Systems Limited.
+{{All rights reserved.}}
+!endblock
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that this notice is preserved.
+The names of the copyright holders may not be used to endorse or
+promote products derived from this software without their specific
+prior written permission. This software is provided ``as is''
+without express or implied warranty.
+
+
+H2: University of Michigan Copyright Notice
+
+!block nofill
+Portions [[copyright]] 1992-1996 Regents of the University of Michigan.
+{{All rights reserved.}}
+!endblock
+
+Redistribution and use in source and binary forms are permitted
+provided that this notice is preserved and that due credit is given
+to the University of Michigan at Ann Arbor. The name of the University
+may not be used to endorse or promote products derived from this
+software without specific prior written permission. This software
+is provided ``as is'' without express or implied warranty.
+
diff --git a/doc/guide/release/install.sdf b/doc/guide/release/install.sdf
new file mode 100644
index 0000000..5825fcc
--- /dev/null
+++ b/doc/guide/release/install.sdf
@@ -0,0 +1,100 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+P1: Making and Installing the OpenLDAP Distribution
+
+This file provides brief instructions on how to build and install
+OpenLDAP on UNIX (and UNIX-{{like}}) systems. More detailed
+information and instructions can be found in
+{{The OpenLDAP Administrator's Guide}}
+(available from {{URL:http://www.openldap.org/doc/}}).
+
+It is recommended that you read, or at least skim through, ALL of
+the instructions in this file before attempting to build the
+software.
+
+P2: Making and Installing the OpenLDAP Distribution
+
+^ Unpack the distribution and change directory:
+
+E: % tar xfz openldap-VERSION.tgz
+E: % cd openldap-VERSION
+
+.(replacing {{EX:VERSION}} with the appropriate version string). If
+you are reading this file, you probably have already done this!
+
+
++ Type:
+
+E: % ./configure --help
+
+.to list available configuration options.
+
+.The {{EX:configure}} script also looks for compiler/linker options
+on the command line and in the environment. These include:
+
+> Variable Description Example
+> CC C compiler gcc
+> CFLAGS C flags -O -g
+> CPPFLAGS cpp flags -I/path/include -D__FOO__=42
+> LDFLAGS ld flags -L/usr/local/lib
+> LIBS libraries -llib
+> PATH command path /usr/local/bin:/usr/bin:/bin
+
+!if $var{'OPT_TARGET'} eq 'txt'
+.See doc/install/configure for generic configure documentation.
+!endif
+
++ Configure the build system:
+
+E: % ./configure [options] [var=value ...]
+
+.If all goes well, the {{EX:configure}} script will automatically detect
+the appropriate settings. If the {{EX:configure}} script fails, you should
+read the {{FILE:config.log}} file that it generated to see what it was trying
+to do and exactly what failed. You may need to specify additional
+options and/or variables besides those listed above to
+obtain desired results, depending on your operating system.
+
++ Build dependencies:
+
+E: % make depend
+
++ Build the system:
+
+E: % make
+
+.If all goes well, the system will build as configured. If not,
+return to step 3 after reviewing the configuration settings.
+
++ Test the standalone system:
+
+.This step requires the standalone LDAP server, {{slapd}}(8),
+with {{MDB}} support.
+
+E: % make test
+
+.If all goes well, the system has been built as configured. If not,
+return to step 2 after reviewing your configuration settings.
+
++ Install the software. You may need to become the
+{{super-user}} (e.g. {{EX:root}}) to do this (depending on where you
+are installing things):
+
+E: % su root -c 'make install'
+
++ That's it. Enjoy!
+
+See the {{OpenLDAP Administrator's Guide}} and the manual pages
+for the individual applications for configuration and use information.
+You may also want to edit the configuration files used by the
+various components. These configuration files are located in
+the OpenLDAP configuration directory (normally
+{{FILE:/usr/local/etc/openldap}}).
+
+> ldap.conf client defaults
+> slapd.conf Standalone LDAP daemon
+> lload.conf LDAP Load Balancer daemon
+> schema/*.schema Schema Definitions
+
diff --git a/doc/guide/release/license-plain.sdf b/doc/guide/release/license-plain.sdf
new file mode 100644
index 0000000..c84a006
--- /dev/null
+++ b/doc/guide/release/license-plain.sdf
@@ -0,0 +1,10 @@
+# $OpenLDAP$
+# Copyright 1999-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+#
+# Plain Copyright for Software Distribution
+#
+!define HTML_TITLE "OpenLDAP Public License"
+!include "../plain.sdf"
+!include "license.sdf"; plain
diff --git a/doc/guide/release/license.sdf b/doc/guide/release/license.sdf
new file mode 100644
index 0000000..939fcf8
--- /dev/null
+++ b/doc/guide/release/license.sdf
@@ -0,0 +1,15 @@
+# $OpenLDAP$
+# Copyright 2000-2022 The OpenLDAP Foundation, All Rights Reserved.
+# COPYING RESTRICTIONS APPLY, see COPYRIGHT.
+
+H1: OpenLDAP Public License
+
+#!block inline
+#<FONT FACE="Courier">
+#!endblock
+
+!include "../../../LICENSE" ; verbatim
+
+#!block inline
+#</FONT>
+#!endblock
diff --git a/doc/install/configure b/doc/install/configure
new file mode 100644
index 0000000..c94362a
--- /dev/null
+++ b/doc/install/configure
@@ -0,0 +1,187 @@
+The following is a verbatim copy of the of Autoconf 2.12 generic
+INSTALL document.
+
+
+Basic Installation
+==================
+
+ These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+ The file `configure.in' is used to create `configure' by a program
+called `autoconf'. You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. You can give `configure'
+initial values for variables by setting them in the environment. Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+ CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+ env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory. After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+ By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=PATH' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on. Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+ CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+ If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+ Use and save the results of the tests in FILE instead of
+ `./config.cache'. Set FILE to `/dev/null' to disable caching, for
+ debugging `configure'.
+
+`--help'
+ Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`--version'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
+
diff --git a/doc/man/Makefile.in b/doc/man/Makefile.in
new file mode 100644
index 0000000..f6024b3
--- /dev/null
+++ b/doc/man/Makefile.in
@@ -0,0 +1,16 @@
+# man Makefile.in for OpenLDAP
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SUBDIRS= man1 man3 man5 man8
diff --git a/doc/man/Project b/doc/man/Project
new file mode 100644
index 0000000..ed7cd85
--- /dev/null
+++ b/doc/man/Project
@@ -0,0 +1,5 @@
+.\" Shared Project Acknowledgement Text
+.B "OpenLDAP Software"
+is developed and maintained by The OpenLDAP Project <http://www.openldap.org/>.
+.B "OpenLDAP Software"
+is derived from the University of Michigan LDAP 3.3 Release.
diff --git a/doc/man/man1/Makefile.in b/doc/man/man1/Makefile.in
new file mode 100644
index 0000000..c051765
--- /dev/null
+++ b/doc/man/man1/Makefile.in
@@ -0,0 +1,16 @@
+# man1 Makefile.in for OpenLDAP
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+MANSECT=1
diff --git a/doc/man/man1/ldapcompare.1 b/doc/man/man1/ldapcompare.1
new file mode 100644
index 0000000..b15b0c4
--- /dev/null
+++ b/doc/man/man1/ldapcompare.1
@@ -0,0 +1,241 @@
+.TH LDAPCOMPARE 1 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldapcompare \- LDAP compare tool
+.SH SYNOPSIS
+.B ldapcompare
+[\c
+.BR \-V [ V ]]
+[\c
+.BI \-d \ debuglevel\fR]
+[\c
+.BR \-n ]
+[\c
+.BR \-v ]
+[\c
+.BR \-z ]
+[\c
+.BR \-M [ M ]]
+[\c
+.BR \-x ]
+[\c
+.BI \-D \ binddn\fR]
+[\c
+.BR \-W ]
+[\c
+.BI \-w \ passwd\fR]
+[\c
+.BI \-y \ passwdfile\fR]
+[\c
+.BI \-H \ ldapuri\fR]
+[\c
+.BR \-P \ { 2 \||\| 3 }]
+[\c
+.BR \-e \ [ ! ] \fIext\fP [ =\fIextparam\fP ]]
+[\c
+.BR \-E \ [ ! ] \fIext\fP [ =\fIextparam\fP ]]
+[\c
+.BI \-o \ opt \fR[= optparam \fR]]
+[\c
+.BI \-O \ security-properties\fR]
+[\c
+.BR \-I ]
+[\c
+.BR \-Q ]
+[\c
+.BR \-N ]
+[\c
+.BI \-U \ authcid\fR]
+[\c
+.BI \-R \ realm\fR]
+[\c
+.BI \-X \ authzid\fR]
+[\c
+.BI \-Y \ mech\fR]
+[\c
+.BR \-Z [ Z ]]
+.IR DN
+{\c
+.BI attr: value
+|
+.BI attr:: b64value\fR}
+.SH DESCRIPTION
+.I ldapcompare
+is a shell-accessible interface to the
+.BR ldap_compare_ext (3)
+library call.
+.LP
+.B ldapcompare
+opens a connection to an LDAP server, binds, and performs a compare
+using specified parameters. The \fIDN\fP should be a distinguished
+name in the directory. \fIAttr\fP should be a known attribute. If
+followed by one colon, the assertion \fIvalue\fP should be provided
+as a string. If followed by two colons, the base64 encoding of the
+value is provided. The result code of the compare is provided as
+the exit code and, unless ran with \fB\-z\fP, the program prints
+TRUE, FALSE, or UNDEFINED on standard output.
+.LP
+.SH OPTIONS
+.TP
+.BR \-V [ V ]
+Print version info.
+If \fB\-VV\fP is given, only the version information is printed.
+.TP
+.BI \-d \ debuglevel
+Set the LDAP debugging level to \fIdebuglevel\fP.
+.B ldapcompare
+must be compiled with LDAP_DEBUG defined for this option to have any effect.
+.TP
+.B \-n
+Show what would be done, but don't actually perform the compare. Useful for
+debugging in conjunction with \fB\-v\fP.
+.TP
+.B \-v
+Run in verbose mode, with many diagnostics written to standard output.
+.TP
+.B \-z
+Run in quiet mode, no output is written. You must check the return
+status. Useful in shell scripts.
+.TP
+.BR \-M [ M ]
+Enable manage DSA IT control.
+.B \-MM
+makes control critical.
+.TP
+.B \-x
+Use simple authentication instead of SASL.
+.TP
+.BI \-D \ binddn
+Use the Distinguished Name \fIbinddn\fP to bind to the LDAP directory.
+For SASL binds, the server is expected to ignore this value.
+.TP
+.B \-W
+Prompt for simple authentication.
+This is used instead of specifying the password on the command line.
+.TP
+.BI \-w \ passwd
+Use \fIpasswd\fP as the password for simple authentication.
+.TP
+.BI \-y \ passwdfile
+Use complete contents of \fIpasswdfile\fP as the password for
+simple authentication.
+Note that \fIcomplete\fP means that any leading or trailing whitespaces,
+including newlines, will be considered part of the password and,
+unlike other software, they will not be stripped.
+As a consequence, passwords stored in files by commands like
+.BR echo (1)
+will not behave as expected, since
+.BR echo (1)
+by default appends a trailing newline to the echoed string.
+The recommended portable way to store a cleartext password in a file
+for use with this option is to use
+.BR slappasswd (8)
+with \fI{CLEARTEXT}\fP as hash and the option \fB\-n\fP.
+.TP
+.BI \-H \ ldapuri
+Specify URI(s) referring to the ldap server(s); only the protocol/host/port
+fields are allowed; a list of URI, separated by whitespace or commas
+is expected.
+.TP
+.BR \-P \ { 2 \||\| 3 }
+Specify the LDAP protocol version to use.
+.TP
+.BR \-e \ [ ! ] \fIext\fP [ =\fIextparam\fP ]
+.TP
+.BR \-E \ [ ! ] \fIext\fP [ =\fIextparam\fP ]
+
+Specify general extensions with \fB\-e\fP and compare extensions with \fB\-E\fP.
+\'\fB!\fP\' indicates criticality.
+
+General extensions:
+.nf
+ [!]assert=<filter> (an RFC 4515 Filter)
+ !authzid=<authzid> ("dn:<dn>" or "u:<user>")
+ [!]bauthzid (RFC 3829 authzid control)
+ [!]chaining[=<resolve>[/<cont>]]
+ [!]manageDSAit
+ [!]noop
+ ppolicy
+ [!]postread[=<attrs>] (a comma-separated attribute list)
+ [!]preread[=<attrs>] (a comma-separated attribute list)
+ [!]relax
+ sessiontracking[=<username>]
+ abandon,cancel,ignore (SIGINT sends abandon/cancel,
+ or ignores response; if critical, doesn't wait for SIGINT.
+ not really controls)
+.fi
+
+Compare extensions:
+.nf
+ !dontUseCopy
+.fi
+.TP
+.BI \-o \ opt \fR[= optparam \fR]
+
+Specify any
+.BR ldap.conf (5)
+option or one of the following:
+.nf
+ nettimeout=<timeout> (in seconds, or "none" or "max")
+ ldif_wrap=<width> (in columns, or "no" for no wrapping)
+.fi
+
+.TP
+.BI \-O \ security-properties
+Specify SASL security properties.
+.TP
+.B \-I
+Enable SASL Interactive mode. Always prompt. Default is to prompt
+only as needed.
+.TP
+.B \-Q
+Enable SASL Quiet mode. Never prompt.
+.TP
+.B \-N
+Do not use reverse DNS to canonicalize SASL host name.
+.TP
+.BI \-U \ authcid
+Specify the authentication ID for SASL bind. The form of the ID
+depends on the actual SASL mechanism used.
+.TP
+.BI \-R \ realm
+Specify the realm of authentication ID for SASL bind. The form of the realm
+depends on the actual SASL mechanism used.
+.TP
+.BI \-X \ authzid
+Specify the requested authorization ID for SASL bind.
+.I authzid
+must be one of the following formats:
+.BI dn: "<distinguished name>"
+or
+.BI u: <username>
+.TP
+.BI \-Y \ mech
+Specify the SASL mechanism to be used for authentication. If it's not
+specified, the program will choose the best mechanism the server knows.
+.TP
+.BR \-Z [ Z ]
+Issue StartTLS (Transport Layer Security) extended operation. If you use
+\fB\-ZZ\fP, the command will require the operation to be successful.
+.SH EXAMPLES
+.nf
+ ldapcompare "uid=babs,dc=example,dc=com" sn:Jensen
+ ldapcompare "uid=babs,dc=example,dc=com" sn::SmVuc2Vu
+.fi
+are all equivalent.
+.SH LIMITATIONS
+Requiring the value be passed on the command line is limiting
+and introduces some security concerns. The command should support
+a mechanism to specify the location (file name or URL) to read
+the value from.
+.SH "SEE ALSO"
+.BR ldap.conf (5),
+.BR ldif (5),
+.BR ldap (3),
+.BR ldap_compare_ext (3)
+.SH AUTHOR
+The OpenLDAP Project <http://www.openldap.org/>
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man1/ldapdelete.1 b/doc/man/man1/ldapdelete.1
new file mode 100644
index 0000000..e12cc56
--- /dev/null
+++ b/doc/man/man1/ldapdelete.1
@@ -0,0 +1,252 @@
+.TH LDAPDELETE 1 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldapdelete \- LDAP delete entry tool
+.SH SYNOPSIS
+.B ldapdelete
+[\c
+.BR \-V [ V ]]
+[\c
+.BI \-d \ debuglevel\fR]
+[\c
+.BR \-n ]
+[\c
+.BR \-v ]
+[\c
+.BR \-c ]
+[\c
+.BI \-f \ file\fR]
+[\c
+.BR \-r ]
+[\c
+.BI \-z \ sizelimit\fR]
+[\c
+.BR \-M [ M ]]
+[\c
+.BR \-x ]
+[\c
+.BI \-D \ binddn\fR]
+[\c
+.BR \-W ]
+[\c
+.BI \-w \ passwd\fR]
+[\c
+.BI \-y \ passwdfile\fR]
+[\c
+.BI \-H \ ldapuri\fR]
+[\c
+.BR \-P \ { 2 \||\| 3 }]
+[\c
+.BR \-e \ [ ! ] \fIext\fP [ =\fIextparam\fP ]]
+[\c
+.BR \-E \ [ ! ] \fIext\fP [ =\fIextparam\fP ]]
+[\c
+.BI \-o \ opt \fR[= optparam \fR]]
+[\c
+.BI \-O \ security-properties\fR]
+[\c
+.BR \-I ]
+[\c
+.BR \-Q ]
+[\c
+.BR \-N ]
+[\c
+.BI \-U \ authcid\fR]
+[\c
+.BI \-R \ realm\fR]
+[\c
+.BI \-X \ authzid\fR]
+[\c
+.BI \-Y \ mech\fR]
+[\c
+.BR \-Z [ Z ]]
+[\c
+.IR DN \ [ ... ]]
+.SH DESCRIPTION
+.I ldapdelete
+is a shell-accessible interface to the
+.BR ldap_delete_ext (3)
+library call.
+.LP
+.B ldapdelete
+opens a connection to an LDAP server, binds, and deletes one or more
+entries. If one or more \fIDN\fP arguments are provided, entries with
+those Distinguished Names are deleted. Each \fIDN\fP should be provided
+using the LDAPv3 string representation as defined in RFC 4514.
+If no \fIDN\fP arguments
+are provided, a list of DNs is read from standard input (or from
+\fIfile\fP if the \fB\-f\fP flag is used).
+.SH OPTIONS
+.TP
+.BR \-V [ V ]
+Print version info.
+If \fB\-VV\fP is given, only the version information is printed.
+.TP
+.BI \-d \ debuglevel
+Set the LDAP debugging level to \fIdebuglevel\fP.
+.B ldapdelete
+must be compiled with LDAP_DEBUG defined for this option to have any effect.
+.TP
+.B \-n
+Show what would be done, but don't actually delete entries. Useful for
+debugging in conjunction with \fB\-v\fP.
+.TP
+.B \-v
+Use verbose mode, with many diagnostics written to standard output.
+.TP
+.B \-c
+Continuous operation mode. Errors are reported, but
+.B ldapdelete
+will continue with deletions. The default is to exit after
+reporting an error.
+.TP
+.BI \-f \ file
+Read a series of DNs from \fIfile\fP, one per line, performing an
+LDAP delete for each.
+.TP
+.B \-r
+Do a recursive delete. If the DN specified isn't a leaf, its
+children, and all their children are deleted down the tree. No
+verification is done, so if you add this switch, ldapdelete will
+happily delete large portions of your tree. Use with care.
+.TP
+.BI \-z \ sizelimit
+Use \fIsizelimit\fP when searching for children DN to delete,
+to circumvent any server-side size limit. Only useful in conjunction
+with \fB\-r\fP.
+.TP
+.BR \-M [ M ]
+Enable manage DSA IT control.
+.B \-MM
+makes control critical.
+.TP
+.B \-x
+Use simple authentication instead of SASL.
+.TP
+.BI \-D \ binddn
+Use the Distinguished Name \fIbinddn\fP to bind to the LDAP directory.
+For SASL binds, the server is expected to ignore this value.
+.TP
+.B \-W
+Prompt for simple authentication.
+This is used instead of specifying the password on the command line.
+.TP
+.BI \-w \ passwd
+Use \fIpasswd\fP as the password for simple authentication.
+.TP
+.BI \-y \ passwdfile
+Use complete contents of \fIpasswdfile\fP as the password for
+simple authentication.
+.TP
+.BI \-H \ ldapuri
+Specify URI(s) referring to the ldap server(s); only the protocol/host/port
+fields are allowed; a list of URI, separated by whitespace or commas
+is expected.
+.TP
+.BR \-P \ { 2 \||\| 3 }
+Specify the LDAP protocol version to use.
+.TP
+.BR \-e \ [ ! ] \fIext\fP [ =\fIextparam\fP ]
+.TP
+.BR \-E \ [ ! ] \fIext\fP [ =\fIextparam\fP ]
+
+Specify general extensions with \fB\-e\fP and delete extensions with \fB\-E\fP.
+\'\fB!\fP\' indicates criticality.
+
+General extensions:
+.nf
+ [!]assert=<filter> (an RFC 4515 Filter)
+ !authzid=<authzid> ("dn:<dn>" or "u:<user>")
+ [!]bauthzid (RFC 3829 authzid control)
+ [!]chaining[=<resolve>[/<cont>]]
+ [!]manageDSAit
+ [!]noop
+ ppolicy
+ [!]postread[=<attrs>] (a comma-separated attribute list)
+ [!]preread[=<attrs>] (a comma-separated attribute list)
+ [!]relax
+ sessiontracking[=<username>]
+ abandon,cancel,ignore (SIGINT sends abandon/cancel,
+ or ignores response; if critical, doesn't wait for SIGINT.
+ not really controls)
+.fi
+
+Delete extensions:
+.nf
+ (none)
+.fi
+.TP
+.BI \-o \ opt \fR[= optparam \fR]
+
+Specify any
+.BR ldap.conf (5)
+option or one of the following:
+.nf
+ nettimeout=<timeout> (in seconds, or "none" or "max")
+ ldif_wrap=<width> (in columns, or "no" for no wrapping)
+.fi
+
+.TP
+.BI \-O \ security-properties
+Specify SASL security properties.
+.TP
+.B \-I
+Enable SASL Interactive mode. Always prompt. Default is to prompt
+only as needed.
+.TP
+.B \-Q
+Enable SASL Quiet mode. Never prompt.
+.TP
+.B \-N
+Do not use reverse DNS to canonicalize SASL host name.
+.TP
+.BI \-U \ authcid
+Specify the authentication ID for SASL bind. The form of the identity depends on the
+actual SASL mechanism used.
+.TP
+.BI \-R \ realm
+Specify the realm of authentication ID for SASL bind. The form of the realm
+depends on the actual SASL mechanism used.
+.TP
+.BI \-X \ authzid
+Specify the requested authorization ID for SASL bind.
+.I authzid
+must be one of the following formats:
+.BI dn: "<distinguished name>"
+or
+.BI u: <username>
+.TP
+.BI \-Y \ mech
+Specify the SASL mechanism to be used for authentication. If it's not
+specified, the program will choose the best mechanism the server knows.
+.TP
+.BR \-Z [ Z ]
+Issue StartTLS (Transport Layer Security) extended operation. If you use
+\fB\-ZZ\fP, the command will require the operation to be successful.
+.SH EXAMPLE
+The following command:
+.LP
+.nf
+ ldapdelete "cn=Delete Me,dc=example,dc=com"
+.fi
+.LP
+will attempt to delete the entry named "cn=Delete Me,dc=example,dc=com".
+Of course it would probably be necessary to supply authentication
+credentials.
+.SH DIAGNOSTICS
+Exit status is 0 if no errors occur. Errors result in a non-zero exit
+status and a diagnostic message being written to standard error.
+.SH "SEE ALSO"
+.BR ldap.conf (5),
+.BR ldapadd (1),
+.BR ldapmodify (1),
+.BR ldapmodrdn (1),
+.BR ldapsearch (1),
+.BR ldap (3),
+.BR ldap_delete_ext (3)
+.SH AUTHOR
+The OpenLDAP Project <http://www.openldap.org/>
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man1/ldapexop.1 b/doc/man/man1/ldapexop.1
new file mode 100644
index 0000000..2040c3e
--- /dev/null
+++ b/doc/man/man1/ldapexop.1
@@ -0,0 +1,242 @@
+.\" $OpenLDAP$
+.\" This contribution is derived from OpenLDAP Software.
+.\" All of the modifications to OpenLDAP Software represented in this
+.\" contribution were developed by Peter Marschall <peter@adpm.de>.
+.\" I have not assigned rights and/or interest in this work to any party.
+.\"
+.\" Copyright 2009 Peter Marschall
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted only as authorized by the OpenLDAP Public License.
+.\"
+.\" A copy of this license is available in file LICENSE in the
+.\" top-level directory of the distribution or, alternatively, at
+.\" http://www.OpenLDAP.org/license.html.
+
+.TH LDAPEXOP 1
+
+.SH NAME
+ldapexop \- issue LDAP extended operations
+
+.SH SYNOPSIS
+ldapexop
+[\c
+.BR \-V [ V ]]
+[\c
+.BI \-d \ debuglevel\fR]
+[\c
+.BR \-n ]
+[\c
+.BR \-v ]
+[\c
+.BI \-f \ file\fR]
+[\c
+.BR \-x ]
+[\c
+.BI \-D \ binddn\fR]
+[\c
+.BR \-W ]
+[\c
+.BI \-w \ passwd\fR]
+[\c
+.BI \-y \ passwdfile\fR]
+[\c
+.BI \-H \ URI\fR]
+[\c
+.BR \-e \ [ ! ] \fIext\fP [ =\fIextparam\fP ]]
+[\c
+.BI \-o \ opt \fR[= optparam \fR]]
+[\c
+.BI \-O \ security-properties\fR]
+[\c
+.BR \-I ]
+[\c
+.BR \-Q ]
+[\c
+.BR \-N ]
+[\c
+.BI \-U \ authcid\fR]
+[\c
+.BI \-R \ realm\fR]
+[\c
+.BI \-X \ authzid\fR]
+[\c
+.BI \-Y \ mech\fR]
+[\c
+.BR \-Z [ Z ]]
+{\c
+.I oid
+|
+.BI oid: data
+|
+.BI oid:: b64data
+|
+.B whoami
+|
+.BI cancel \ cancel-id
+|
+.BI refresh \ DN \ \fR[\fIttl\fR]}
+
+.SH DESCRIPTION
+ldapexop issues the LDAP extended operation specified by \fBoid\fP
+or one of the special keywords \fBwhoami\fP, \fBcancel\fP, or \fBrefresh\fP.
+
+Additional data for the extended operation can be passed to the server using
+\fIdata\fP or base-64 encoded as \fIb64data\fP in the case of \fBoid\fP,
+or using the additional parameters in the case of the specially named extended
+operations above.
+
+Please note that ldapexop behaves differently for the same extended operation
+when it was given as an OID or as a specially named operation:
+
+Calling ldapexop with the OID of the \fBwhoami\fP (RFC 4532) extended operation
+.nf
+
+ ldapexop [<options>] 1.3.6.1.4.1.4203.1.11.3
+
+.fi
+yields
+.nf
+
+ # extended operation response
+ data:: <base64 encoded response data>
+
+.fi
+while calling it with the keyword \fBwhoami\fP
+.nf
+
+ ldapexop [<options>] whoami
+
+.fi
+results in
+.nf
+
+ dn:<client's identity>
+
+.fi
+
+
+.SH OPTIONS
+.TP
+.BI \-V [ V ]
+Print version info.
+If\fB\-VV\fP is given, only the version information is printed.
+.TP
+.BI \-d \ debuglevel
+Set the LDAP debugging level to \fIdebuglevel\fP.
+.TP
+.BI \-n
+Show what would be done but don't actually do it.
+Useful for debugging in conjunction with \fB\-v\fP.
+.TP
+.BI \-v
+Run in verbose mode, with many diagnostics written to standard output.
+.TP
+.BI \-f \ file
+Read operations from \fIfile\fP.
+.TP
+.BI \-x
+Use simple authentication instead of SASL.
+.TP
+.BI \-D \ binddn
+Use the Distinguished Name \fIbinddn\fP to bind to the LDAP directory.
+.TP
+.BI \-W
+Prompt for simple authentication.
+This is used instead of specifying the password on the command line.
+.TP
+.BI \-w \ passwd
+Use \fIpasswd\fP as the password for simple authentication.
+.TP
+.BI \-y \ passwdfile
+Use complete contents of \fIpasswdfile\fP as the password for
+simple authentication.
+.TP
+.BI \-H \ URI
+Specify URI(s) referring to the ldap server(s); only the protocol/host/port
+fields are allowed; a list of URI, separated by whitespace or commas
+is expected.
+.TP
+.BR \-e \ [ ! ] \fIext\fP [ =\fIextparam\fP ]
+Specify general extensions. \'!\' indicates criticality.
+.nf
+ [!]assert=<filter> (an RFC 4515 Filter)
+ !authzid=<authzid> ("dn:<dn>" or "u:<user>")
+ [!]bauthzid (RFC 3829 authzid control)
+ [!]chaining[=<resolve>[/<cont>]]
+ [!]manageDSAit
+ [!]noop
+ ppolicy
+ [!]postread[=<attrs>] (a comma-separated attribute list)
+ [!]preread[=<attrs>] (a comma-separated attribute list)
+ [!]relax
+ sessiontracking[=<username>]
+ abandon,cancel,ignore (SIGINT sends abandon/cancel,
+ or ignores response; if critical, doesn't wait for SIGINT.
+ not really controls)
+.fi
+.TP
+.BI \-o \ opt \fR[= optparam \fR]
+
+Specify any
+.BR ldap.conf (5)
+option or one of the following:
+.nf
+ nettimeout=<timeout> (in seconds, or "none" or "max")
+ ldif_wrap=<width> (in columns, or "no" for no wrapping)
+.fi
+
+.TP
+.BI \-O \ security-properties
+Specify SASL security properties.
+.TP
+.BI \-I
+Enable SASL Interactive mode. Always prompt. Default is to prompt
+only as needed.
+.TP
+.BI \-Q
+Enable SASL Quiet mode. Never prompt.
+.TP
+.B \-N
+Do not use reverse DNS to canonicalize SASL host name.
+.TP
+.BI \-U \ authcid
+Specify the authentication ID for SASL bind. The form of the ID
+depends on the actual SASL mechanism used.
+.TP
+.BI \-R \ realm
+Specify the realm of authentication ID for SASL bind. The form of the realm
+depends on the actual SASL mechanism used.
+.TP
+.BI \-X \ authzid
+Specify the requested authorization ID for SASL bind.
+.I authzid
+must be one of the following formats:
+.BI dn: "<distinguished name>"
+or
+.BI u: <username>
+.TP
+.BI \-Y \ mech
+Specify the SASL mechanism to be used for authentication.
+Without this option, the program will choose the best mechanism the server knows.
+.TP
+.BR \-Z [ Z ]
+Issue StartTLS (Transport Layer Security) extended operation.
+Giving it twice (\fB\-ZZ\fP) will require the operation to be successful.
+
+.SH DIAGNOSTICS
+Exit status is zero if no errors occur.
+Errors result in a non-zero exit status and
+a diagnostic message being written to standard error.
+
+.SH "SEE ALSO"
+.BR ldap_extended_operation_s (3)
+
+.SH AUTHOR
+This manual page was written by Peter Marschall
+based on \fBldapexop\fP's usage message and a few tests
+with \fBldapexop\fP.
+Do not expect it to be complete or absolutely correct.
+
+.SH ACKNOWLEDGEMENTS
+.so ../Project
+
diff --git a/doc/man/man1/ldapmodify.1 b/doc/man/man1/ldapmodify.1
new file mode 100644
index 0000000..1104e9f
--- /dev/null
+++ b/doc/man/man1/ldapmodify.1
@@ -0,0 +1,390 @@
+.TH LDAPMODIFY 1 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldapmodify, ldapadd \- LDAP modify entry and LDAP add entry tools
+.SH SYNOPSIS
+.B ldapmodify
+[\c
+.BR \-V [ V ]]
+[\c
+.BI \-d \ debuglevel\fR]
+[\c
+.BR \-n ]
+[\c
+.BR \-v ]
+[\c
+.BR \-a ]
+[\c
+.BR \-c ]
+[\c
+.BI \-f \ file\fR]
+[\c
+.BI \-S \ file\fR]
+[\c
+.BR \-M [ M ]]
+[\c
+.BR \-x ]
+[\c
+.BI \-D \ binddn\fR]
+[\c
+.BR \-W ]
+[\c
+.BI \-w \ passwd\fR]
+[\c
+.BI \-y \ passwdfile\fR]
+[\c
+.BI \-H \ ldapuri\fR]
+[\c
+.BR \-P \ { 2 \||\| 3 }]
+[\c
+.BR \-e \ [ ! ] \fIext\fP [ =\fIextparam\fP ]]
+[\c
+.BR \-E \ [ ! ] \fIext\fP [ =\fIextparam\fP ]]
+[\c
+.BI \-o \ opt \fR[= optparam \fR]]
+[\c
+.BI \-O \ security-properties\fR]
+[\c
+.BR \-I ]
+[\c
+.BR \-Q ]
+[\c
+.BR \-N ]
+[\c
+.BI \-U \ authcid\fR]
+[\c
+.BI \-R \ realm\fR]
+[\c
+.BI \-X \ authzid\fR]
+[\c
+.BI \-Y \ mech\fR]
+[\c
+.BR \-Z [ Z ]]
+.LP
+.B ldapadd
+[\c
+.BR \-V [ V ]]
+[\c
+.BI \-d \ debuglevel\fR]
+[\c
+.BR \-n ]
+[\c
+.BR \-v ]
+[\c
+.BR \-c ]
+[\c
+.BI \-f \ file\fR]
+[\c
+.BI \-S \ file\fR]
+[\c
+.BR \-M [ M ]]
+[\c
+.BR \-x ]
+[\c
+.BI \-D \ binddn\fR]
+[\c
+.BR \-W ]
+[\c
+.BI \-w \ passwd\fR]
+[\c
+.BI \-y \ passwdfile\fR]
+[\c
+.BI \-H \ ldapuri\fR]
+[\c
+.BR \-P \ { 2 \||\| 3 }]
+[\c
+.BR \-e \ [ ! ] \fIext\fP [ =\fIextparam\fP ]]
+[\c
+.BR \-E \ [ ! ] \fIext\fP [ =\fIextparam\fP ]]
+[\c
+.BI \-o \ opt \fR[= optparam \fR]]
+[\c
+.BI \-O \ security-properties\fR]
+[\c
+.BR \-I ]
+[\c
+.BR \-Q ]
+[\c
+.BR \-N ]
+[\c
+.BI \-U \ authcid\fR]
+[\c
+.BI \-R \ realm\fR]
+[\c
+.BI \-X \ authzid\fR]
+[\c
+.BI \-Y \ mech\fR]
+[\c
+.BR \-Z [ Z ]]
+.SH DESCRIPTION
+.B ldapmodify
+is a shell-accessible interface to the
+.BR ldap_add_ext (3),
+.BR ldap_modify_ext (3),
+.BR ldap_delete_ext (3)
+and
+.BR ldap_rename (3).
+library calls.
+.B ldapadd
+is implemented as a hard link to the ldapmodify tool. When invoked as
+.B ldapadd
+the \fB\-a\fP (add new entry) flag is turned on automatically.
+.LP
+.B ldapmodify
+opens a connection to an LDAP server, binds, and modifies or adds entries.
+The entry information is read from standard input or from \fIfile\fP through
+the use of the \fB\-f\fP option.
+.SH OPTIONS
+.TP
+.BR \-V [ V ]
+Print version info.
+If \fB\-VV\fP is given, only the version information is printed.
+.TP
+.BI \-d \ debuglevel
+Set the LDAP debugging level to \fIdebuglevel\fP.
+.B ldapmodify
+must be compiled with LDAP_DEBUG defined for this option to have any effect.
+.TP
+.B \-n
+Show what would be done, but don't actually modify entries. Useful for
+debugging in conjunction with \fB\-v\fP.
+.TP
+.B \-v
+Use verbose mode, with many diagnostics written to standard output.
+.TP
+.B \-a
+Add new entries. The default for
+.B ldapmodify
+is to modify existing entries. If invoked as
+.BR ldapadd ,
+this flag is always set.
+.TP
+.B \-c
+Continuous operation mode. Errors are reported, but
+.B ldapmodify
+will continue with modifications. The default is to exit after
+reporting an error.
+.TP
+.BI \-f \ file
+Read the entry modification information from \fIfile\fP instead of from
+standard input.
+.TP
+.BI \-S \ file
+Add or change records which were skipped due to an error are written to \fIfile\fP
+and the error message returned by the server is added as a comment. Most useful in
+conjunction with \fB\-c\fP.
+.TP
+.BR \-M [ M ]
+Enable manage DSA IT control.
+.B \-MM
+makes control critical.
+.TP
+.B \-x
+Use simple authentication instead of SASL.
+.TP
+.BI \-D \ binddn
+Use the Distinguished Name \fIbinddn\fP to bind to the LDAP directory.
+For SASL binds, the server is expected to ignore this value.
+.TP
+.B \-W
+Prompt for simple authentication.
+This is used instead of specifying the password on the command line.
+.TP
+.BI \-w \ passwd
+Use \fIpasswd\fP as the password for simple authentication.
+.TP
+.BI \-y \ passwdfile
+Use complete contents of \fIpasswdfile\fP as the password for
+simple authentication.
+.TP
+.BI \-H \ ldapuri
+Specify URI(s) referring to the ldap server(s); only the protocol/host/port
+fields are allowed; a list of URI, separated by whitespace or commas
+is expected.
+.TP
+.BR \-P \ { 2 \||\| 3 }
+Specify the LDAP protocol version to use.
+.TP
+.BR \-e \ [ ! ] \fIext\fP [ =\fIextparam\fP ]
+.TP
+.BR \-E \ [ ! ] \fIext\fP [ =\fIextparam\fP ]
+
+Specify general extensions with \fB\-e\fP and modify extensions with \fB\-E\fP.
+\'\fB!\fP\' indicates criticality.
+
+General extensions:
+.nf
+ [!]assert=<filter> (an RFC 4515 Filter)
+ !authzid=<authzid> ("dn:<dn>" or "u:<user>")
+ [!]bauthzid (RFC 3829 authzid control)
+ [!]chaining[=<resolve>[/<cont>]]
+ [!]manageDSAit
+ [!]noop
+ ppolicy
+ [!]postread[=<attrs>] (a comma-separated attribute list)
+ [!]preread[=<attrs>] (a comma-separated attribute list)
+ [!]relax
+ sessiontracking[=<username>]
+ abandon,cancel,ignore (SIGINT sends abandon/cancel,
+ or ignores response; if critical, doesn't wait for SIGINT.
+ not really controls)
+.fi
+
+Modify extensions:
+.nf
+ [!]txn[=abort|commit]
+.fi
+.TP
+.BI \-o \ opt \fR[= optparam \fR]]
+
+Specify any
+.BR ldap.conf (5)
+option or one of the following:
+.nf
+ nettimeout=<timeout> (in seconds, or "none" or "max")
+ ldif_wrap=<width> (in columns, or "no" for no wrapping)
+.fi
+
+.TP
+.BI \-O \ security-properties
+Specify SASL security properties.
+.TP
+.B \-I
+Enable SASL Interactive mode. Always prompt. Default is to prompt
+only as needed.
+.TP
+.B \-Q
+Enable SASL Quiet mode. Never prompt.
+.TP
+.B \-N
+Do not use reverse DNS to canonicalize SASL host name.
+.TP
+.BI \-U \ authcid
+Specify the authentication ID for SASL bind. The form of the ID
+depends on the actual SASL mechanism used.
+.TP
+.BI \-R \ realm
+Specify the realm of authentication ID for SASL bind. The form of the realm
+depends on the actual SASL mechanism used.
+.TP
+.BI \-X \ authzid
+Specify the requested authorization ID for SASL bind.
+.I authzid
+must be one of the following formats:
+.BI dn: "<distinguished name>"
+or
+.BI u: <username>
+.TP
+.BI \-Y \ mech
+Specify the SASL mechanism to be used for authentication. If it's not
+specified, the program will choose the best mechanism the server knows.
+.TP
+.BR \-Z [ Z ]
+Issue StartTLS (Transport Layer Security) extended operation. If you use
+.B \-ZZ\c
+, the command will require the operation to be successful.
+.SH INPUT FORMAT
+The contents of \fIfile\fP (or standard input if no \fB\-f\fP flag is given on
+the command line) must conform to the format defined in
+.BR ldif (5)
+(LDIF as defined in RFC 2849).
+.SH EXAMPLES
+Assuming that the file
+.B /tmp/entrymods
+exists and has the contents:
+.LP
+.nf
+ dn: cn=Modify Me,dc=example,dc=com
+ changetype: modify
+ replace: mail
+ mail: modme@example.com
+ \-
+ add: title
+ title: Grand Poobah
+ \-
+ add: jpegPhoto
+ jpegPhoto:< file:///tmp/modme.jpeg
+ \-
+ delete: description
+ \-
+.fi
+.LP
+the command:
+.LP
+.nf
+ ldapmodify \-f /tmp/entrymods
+.fi
+.LP
+will replace the contents of the "Modify Me" entry's
+.I mail
+attribute with the value "modme@example.com", add a
+.I title
+of "Grand Poobah", and the contents of the file "/tmp/modme.jpeg"
+as a
+.IR jpegPhoto ,
+and completely remove the
+.I description
+attribute.
+.LP
+Assuming that the file
+.B /tmp/newentry
+exists and has the contents:
+.LP
+.nf
+ dn: cn=Barbara Jensen,dc=example,dc=com
+ objectClass: person
+ cn: Barbara Jensen
+ cn: Babs Jensen
+ sn: Jensen
+ title: the world's most famous mythical manager
+ mail: bjensen@example.com
+ uid: bjensen
+.fi
+.LP
+the command:
+.LP
+.nf
+ ldapadd \-f /tmp/newentry
+.fi
+.LP
+will add a new entry for Babs Jensen, using the values from the
+file
+.B /tmp/newentry.
+.LP
+Assuming that the file
+.B /tmp/entrymods
+exists and has the contents:
+.LP
+.nf
+ dn: cn=Barbara Jensen,dc=example,dc=com
+ changetype: delete
+.fi
+.LP
+the command:
+.LP
+.nf
+ ldapmodify \-f /tmp/entrymods
+.fi
+.LP
+will remove Babs Jensen's entry.
+.SH DIAGNOSTICS
+Exit status is zero if no errors occur. Errors result in a non-zero
+exit status and a diagnostic message being written to standard error.
+.SH "SEE ALSO"
+.BR ldapadd (1),
+.BR ldapdelete (1),
+.BR ldapmodrdn (1),
+.BR ldapsearch (1),
+.BR ldap.conf (5),
+.BR ldap (3),
+.BR ldap_add_ext (3),
+.BR ldap_delete_ext (3),
+.BR ldap_modify_ext (3),
+.BR ldap_modrdn_ext (3),
+.BR ldif (5).
+.SH AUTHOR
+The OpenLDAP Project <http://www.openldap.org/>
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man1/ldapmodify.1.links b/doc/man/man1/ldapmodify.1.links
new file mode 100644
index 0000000..eb4fb76
--- /dev/null
+++ b/doc/man/man1/ldapmodify.1.links
@@ -0,0 +1 @@
+ldapadd.1
diff --git a/doc/man/man1/ldapmodrdn.1 b/doc/man/man1/ldapmodrdn.1
new file mode 100644
index 0000000..777c539
--- /dev/null
+++ b/doc/man/man1/ldapmodrdn.1
@@ -0,0 +1,268 @@
+.TH LDAPMODRDN 1 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldapmodrdn \- LDAP rename entry tool
+.SH SYNOPSIS
+.B ldapmodrdn
+[\c
+.BR \-V [ V ]]
+[\c
+.BI \-d \ debuglevel\fR]
+[\c
+.BR \-n ]
+[\c
+.BR \-v ]
+[\c
+.BR \-r ]
+[\c
+.BI \-s \ newsup\fR]
+[\c
+.BR \-c ]
+[\c
+.BI \-f \ file\fR]
+[\c
+.BR \-M [ M ]]
+[\c
+.BR \-x ]
+[\c
+.BI \-D \ binddn\fR]
+[\c
+.BR \-W ]
+[\c
+.BI \-w \ passwd\fR]
+[\c
+.BI \-y \ passwdfile\fR]
+[\c
+.BI \-H \ ldapuri\fR]
+[\c
+.BR \-P \ { 2 \||\| 3 }]
+[\c
+.BR \-e \ [ ! ] \fIext\fP [ =\fIextparam\fP ]]
+[\c
+.BR \-E \ [ ! ] \fIext\fP [ =\fIextparam\fP ]]
+[\c
+.BI \-o \ opt \fR[= optparam \fR]]
+[\c
+.BI \-O \ security-properties\fR]
+[\c
+.BR \-I ]
+[\c
+.BR \-Q ]
+[\c
+.BR \-N ]
+[\c
+.BI \-U \ authcid\fR]
+[\c
+.BI \-R \ realm\fR]
+[\c
+.BI \-X \ authzid\fR]
+[\c
+.BI \-Y \ mech\fR]
+[\c
+.BR \-Z [ Z ]]
+[\c
+.I dn rdn\fR]
+.SH DESCRIPTION
+.B ldapmodrdn
+is a shell-accessible interface to the
+.BR ldap_rename (3)
+library call.
+.LP
+.B ldapmodrdn
+opens a connection to an LDAP server, binds, and modifies the RDN of entries.
+The entry information is read from standard input, from \fIfile\fP through
+the use of the
+.RI \- f
+option, or from the command-line pair \fIdn\fP and
+\fIrdn\fP.
+.SH OPTIONS
+.TP
+.BR \-V [ V ]
+Print version info.
+If \fB\-VV\fP is given, only the version information is printed.
+.TP
+.BI \-d \ debuglevel
+Set the LDAP debugging level to \fIdebuglevel\fP.
+.B ldapmodrdn
+must be compiled with LDAP_DEBUG defined for this option to have any effect.
+.TP
+.B \-n
+Show what would be done, but don't actually change entries. Useful for
+debugging in conjunction with \fB\-v\fP.
+.TP
+.B \-v
+Use verbose mode, with many diagnostics written to standard output.
+.TP
+.B \-r
+Remove old RDN values from the entry. Default is to keep old values.
+.TP
+.BI \-s \ newsup
+Specify a new superior entry. (I.e., move the target entry and make it a
+child of the new superior.) This option is not supported in LDAPv2.
+.TP
+.B \-c
+Continuous operation mode. Errors are reported, but ldapmodrdn
+will continue with modifications. The default is to exit after
+reporting an error.
+.TP
+.BI \-f \ file
+Read the entry modification information from \fIfile\fP instead of from
+standard input or the command-line.
+.TP
+.BR \-M [ M ]
+Enable manage DSA IT control.
+.B \-MM
+makes control critical.
+.TP
+.B \-x
+Use simple authentication instead of SASL.
+.TP
+.BI \-D \ binddn
+Use the Distinguished Name \fIbinddn\fP to bind to the LDAP directory.
+For SASL binds, the server is expected to ignore this value.
+.TP
+.B \-W
+Prompt for simple authentication.
+This is used instead of specifying the password on the command line.
+.TP
+.BI \-w \ passwd
+Use \fIpasswd\fP as the password for simple authentication.
+.TP
+.BI \-y \ passwdfile
+Use complete contents of \fIpasswdfile\fP as the password for
+simple authentication.
+.TP
+.BI \-H \ ldapuri
+Specify URI(s) referring to the ldap server(s); only the protocol/host/port
+fields are allowed; a list of URI, separated by whitespace or commas
+is expected.
+.TP
+.BR \-P \ { 2 \||\| 3 }
+Specify the LDAP protocol version to use.
+.TP
+.BR \-e \ [ ! ] \fIext\fP [ =\fIextparam\fP ]
+.TP
+.BR \-E \ [ ! ] \fIext\fP [ =\fIextparam\fP ]
+
+Specify general extensions with \fB\-e\fP and modrdn extensions with \fB\-E\fP.
+\'\fB!\fP\' indicates criticality.
+
+General extensions:
+.nf
+ [!]assert=<filter> (an RFC 4515 Filter)
+ !authzid=<authzid> ("dn:<dn>" or "u:<user>")
+ [!]bauthzid (RFC 3829 authzid control)
+ [!]chaining[=<resolve>[/<cont>]]
+ [!]manageDSAit
+ [!]noop
+ ppolicy
+ [!]postread[=<attrs>] (a comma-separated attribute list)
+ [!]preread[=<attrs>] (a comma-separated attribute list)
+ [!]relax
+ sessiontracking[=<username>]
+ abandon,cancel,ignore (SIGINT sends abandon/cancel,
+ or ignores response; if critical, doesn't wait for SIGINT.
+ not really controls)
+.fi
+
+Modrdn extensions:
+.nf
+ (none)
+.fi
+.TP
+.BI \-o \ opt \fR[= optparam \fR]
+
+Specify any
+.BR ldap.conf (5)
+option or one of the following:
+.nf
+ nettimeout=<timeout> (in seconds, or "none" or "max")
+ ldif_wrap=<width> (in columns, or "no" for no wrapping)
+.fi
+
+.TP
+.BI \-O \ security-properties
+Specify SASL security properties.
+.TP
+.B \-I
+Enable SASL Interactive mode. Always prompt. Default is to prompt
+only as needed.
+.TP
+.B \-Q
+Enable SASL Quiet mode. Never prompt.
+.TP
+.B \-N
+Do not use reverse DNS to canonicalize SASL host name.
+.TP
+.BI \-U \ authcid
+Specify the authentication ID for SASL bind. The form of the ID
+depends on the actual SASL mechanism used.
+.TP
+.BI \-R \ realm
+Specify the realm of authentication ID for SASL bind. The form of the realm
+depends on the actual SASL mechanism used.
+.TP
+.BI \-X \ authzid
+Specify the requested authorization ID for SASL bind.
+.I authzid
+must be one of the following formats:
+.BI dn: "<distinguished name>"
+or
+.BI u: <username>
+.TP
+.BI \-Y \ mech
+Specify the SASL mechanism to be used for authentication. If it's not
+specified, the program will choose the best mechanism the server knows.
+.TP
+.BR \-Z [ Z ]
+Issue StartTLS (Transport Layer Security) extended operation. If you use
+\fB\-ZZ\fP, the command will require the operation to be successful.
+.SH INPUT FORMAT
+If the command-line arguments \fIdn\fP and \fIrdn\fP are given, \fIrdn\fP
+will replace the RDN of the entry specified by the DN, \fIdn\fP.
+.LP
+Otherwise, the contents of \fIfile\fP (or standard input if
+no \fB\-f\fP flag is given) should consist of one or more entries.
+.LP
+.nf
+ Distinguished Name (DN)
+ Relative Distinguished Name (RDN)
+.fi
+.LP
+One or more blank lines may be used to separate each DN/RDN pair.
+.SH EXAMPLE
+Assuming that the file
+.B /tmp/entrymods
+exists and has the contents:
+.LP
+.nf
+ cn=Modify Me,dc=example,dc=com
+ cn=The New Me
+.fi
+.LP
+the command:
+.LP
+.nf
+ ldapmodrdn \-r \-f /tmp/entrymods
+.fi
+.LP
+will change the RDN of the "Modify Me" entry from "Modify Me" to
+"The New Me" and the old cn, "Modify Me" will be removed.
+.LP
+.SH DIAGNOSTICS
+Exit status is 0 if no errors occur. Errors result in a non-zero exit
+status and a diagnostic message being written to standard error.
+.SH "SEE ALSO"
+.BR ldapadd (1),
+.BR ldapdelete (1),
+.BR ldapmodify (1),
+.BR ldapsearch (1),
+.BR ldap.conf (5),
+.BR ldap (3),
+.BR ldap_rename (3)
+.SH AUTHOR
+The OpenLDAP Project <http://www.openldap.org/>
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man1/ldappasswd.1 b/doc/man/man1/ldappasswd.1
new file mode 100644
index 0000000..d1aea0c
--- /dev/null
+++ b/doc/man/man1/ldappasswd.1
@@ -0,0 +1,231 @@
+.TH LDAPPASSWD 1 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldappasswd \- change the password of an LDAP entry
+.SH SYNOPSIS
+.B ldappasswd
+[\c
+.BR \-V [ V ]]
+[\c
+.BI \-d \ debuglevel\fR]
+[\c
+.BR \-n ]
+[\c
+.BR \-v ]
+[\c
+.BR \-A ]
+[\c
+.BI \-a \ oldPasswd\fR]
+[\c
+.BI \-t \ oldpasswdfile\fR]
+[\c
+.BR \-S ]
+[\c
+.BI \-s \ newPasswd\fR]
+[\c
+.BI \-T \ newpasswdfile\fR]
+[\c
+.BR \-x ]
+[\c
+.BI \-D \ binddn\fR]
+[\c
+.BR \-W ]
+[\c
+.BI \-w \ passwd\fR]
+[\c
+.BI \-y \ passwdfile\fR]
+[\c
+.BI \-H \ ldapuri\fR]
+[\c
+.BR \-e \ [ ! ] \fIext\fP [ =\fIextparam\fP ]]
+[\c
+.BR \-E \ [ ! ] \fIext\fP [ =\fIextparam\fP ]]
+[\c
+.BI \-o \ opt \fR[= optparam \fR]]
+[\c
+.BI \-O \ security-properties\fR]
+[\c
+.BR \-I ]
+[\c
+.BR \-Q ]
+[\c
+.BR \-N ]
+[\c
+.BI \-U \ authcid\fR]
+[\c
+.BI \-R \ realm\fR]
+[\c
+.BI \-X \ authzid\fR]
+[\c
+.BI \-Y \ mech\fR]
+[\c
+.BR \-Z [ Z ]]
+[\c
+.IR user ]
+.SH DESCRIPTION
+.B ldappasswd
+is a tool to set the password of an LDAP user.
+.B ldappasswd
+uses the LDAPv3 Password Modify (RFC 3062) extended operation.
+.LP
+.B ldappasswd
+sets the password of associated with the user [or an optionally
+specified
+.IR user ].
+If the new
+password is not specified on the command line and the user
+doesn't enable prompting, the server will be asked to generate
+a password for the user.
+.LP
+.B ldappasswd
+is neither designed nor intended to be a replacement for
+.BR passwd (1)
+and should not be installed as such.
+.SH OPTIONS
+.TP
+.BR \-V [ V ]
+Print version info.
+If \fB\-VV\fP is given, only the version information is printed.
+.TP
+.BI \-d \ debuglevel
+Set the LDAP debugging level to \fIdebuglevel\fP.
+.B ldappasswd
+must be compiled with LDAP_DEBUG defined for this option to have any effect.
+.TP
+.B \-n
+Do not set password. (Can be useful when used in conjunction with
+\fB\-v\fP or \fB\-d\fP)
+.TP
+.B \-v
+Increase the verbosity of output. Can be specified multiple times.
+.TP
+.BI \-A
+Prompt for old password.
+This is used instead of specifying the password on the command line.
+.TP
+.BI \-a \ oldPasswd
+Set the old password to \fIoldPasswd\fP.
+.TP
+.BI \-t \ oldPasswdFile
+Set the old password to the contents of \fIoldPasswdFile\fP.
+.TP
+.BI \-S
+Prompt for new password.
+This is used instead of specifying the password on the command line.
+.TP
+.BI \-s \ newPasswd
+Set the new password to \fInewPasswd\fP.
+.TP
+.BI \-T \ newPasswdFile
+Set the new password to the contents of \fInewPasswdFile\fP.
+.TP
+.B \-x
+Use simple authentication instead of SASL.
+.TP
+.BI \-D \ binddn
+Use the Distinguished Name \fIbinddn\fP to bind to the LDAP directory.
+For SASL binds, the server is expected to ignore this value.
+.TP
+.BI \-W
+Prompt for bind password.
+This is used instead of specifying the password on the command line.
+.TP
+.BI \-w \ passwd
+Use \fIpasswd\fP as the password to bind with.
+.TP
+.BI \-y \ passwdfile
+Use complete contents of \fIpasswdfile\fP as the password for
+simple authentication.
+.TP
+.BI \-H \ ldapuri
+Specify URI(s) referring to the ldap server(s); only the protocol/host/port
+fields are allowed; a list of URI, separated by whitespace or commas
+is expected.
+.TP
+.BR \-e \ [ ! ] \fIext\fP [ =\fIextparam\fP ]
+.TP
+.BR \-E \ [ ! ] \fIext\fP [ =\fIextparam\fP ]
+
+Specify general extensions with \fB\-e\fP and passwd modify extensions with \fB\-E\fP.
+\'\fB!\fP\' indicates criticality.
+
+General extensions:
+.nf
+ [!]assert=<filter> (an RFC 4515 Filter)
+ !authzid=<authzid> ("dn:<dn>" or "u:<user>")
+ [!]bauthzid (RFC 3829 authzid control)
+ [!]chaining[=<resolve>[/<cont>]]
+ [!]manageDSAit
+ [!]noop
+ ppolicy
+ [!]postread[=<attrs>] (a comma-separated attribute list)
+ [!]preread[=<attrs>] (a comma-separated attribute list)
+ [!]relax
+ sessiontracking[=<username>]
+ abandon,cancel,ignore (SIGINT sends abandon/cancel,
+ or ignores response; if critical, doesn't wait for SIGINT.
+ not really controls)
+.fi
+
+Passwd Modify extensions:
+.nf
+ (none)
+.fi
+.TP
+.BI \-o \ opt \fR[= optparam \fR]]
+
+Specify any
+.BR ldap.conf (5)
+option or one of the following:
+.nf
+ nettimeout=<timeout> (in seconds, or "none" or "max")
+ ldif_wrap=<width> (in columns, or "no" for no wrapping)
+.fi
+
+.TP
+.BI \-O \ security-properties
+Specify SASL security properties.
+.TP
+.B \-I
+Enable SASL Interactive mode. Always prompt. Default is to prompt
+only as needed.
+.TP
+.B \-Q
+Enable SASL Quiet mode. Never prompt.
+.TP
+.B \-N
+Do not use reverse DNS to canonicalize SASL host name.
+.TP
+.BI \-U \ authcid
+Specify the authentication ID for SASL bind. The form of the ID
+depends on the actual SASL mechanism used.
+.TP
+.BI \-R \ realm
+Specify the realm of authentication ID for SASL bind. The form of the realm
+depends on the actual SASL mechanism used.
+.TP
+.BI \-X \ authzid
+Specify the requested authorization ID for SASL bind.
+.I authzid
+must be one of the following formats:
+.BI dn: "<distinguished name>"
+or
+.BI u: <username>\fP.
+.TP
+.BI \-Y \ mech
+Specify the SASL mechanism to be used for authentication. If it's not
+specified, the program will choose the best mechanism the server knows.
+.TP
+.BR \-Z [ Z ]
+Issue StartTLS (Transport Layer Security) extended operation. If you use
+\fB\-ZZ\fP, the command will require the operation to be successful
+.SH SEE ALSO
+.BR ldap_sasl_bind (3),
+.BR ldap_extended_operation (3),
+.BR ldap_start_tls_s (3)
+.SH AUTHOR
+The OpenLDAP Project <http://www.openldap.org/>
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man1/ldapsearch.1 b/doc/man/man1/ldapsearch.1
new file mode 100644
index 0000000..2aec7c5
--- /dev/null
+++ b/doc/man/man1/ldapsearch.1
@@ -0,0 +1,495 @@
+.TH LDAPSEARCH 1 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldapsearch \- LDAP search tool
+.SH SYNOPSIS
+.B ldapsearch
+[\c
+.BR \-V [ V ]]
+[\c
+.BI \-d \ debuglevel\fR]
+[\c
+.BR \-n ]
+[\c
+.BR \-v ]
+[\c
+.BR \-c ]
+[\c
+.BR \-u ]
+[\c
+.BR \-t [ t ]]
+[\c
+.BI \-T \ path\fR]
+[\c
+.BI \-F \ prefix\fR]
+[\c
+.BR \-A ]
+[\c
+.BR \-L [ L [ L ]]]
+[\c
+.BI \-S \ attribute\fR]
+[\c
+.BI \-b \ searchbase\fR]
+[\c
+.BR \-s \ { base \||\| one \||\| sub \||\| children }]
+[\c
+.BR \-a \ { never \||\| always \||\| search \||\| find }]
+[\c
+.BI \-l \ timelimit\fR]
+[\c
+.BI \-z \ sizelimit\fR]
+[\c
+.BI \-f \ file\fR]
+[\c
+.BR \-M [ M ]]
+[\c
+.BR \-x ]
+[\c
+.BI \-D \ binddn\fR]
+[\c
+.BR \-W ]
+[\c
+.BI \-w \ passwd\fR]
+[\c
+.BI \-y \ passwdfile\fR]
+[\c
+.BI \-H \ ldapuri\fR]
+[\c
+.BR \-P \ { 2 \||\| 3 }]
+[\c
+.BR \-e \ [ ! ] \fIext\fP [ =\fIextparam\fP ]]
+[\c
+.BR \-E \ [ ! ] \fIext\fP [ =\fIextparam\fP ]]
+[\c
+.BI \-o \ opt \fR[= optparam \fR]]
+[\c
+.BI \-O \ security-properties\fR]
+[\c
+.BR \-I ]
+[\c
+.BR \-Q ]
+[\c
+.BR \-N ]
+[\c
+.BI \-U \ authcid\fR]
+[\c
+.BI \-R \ realm\fR]
+[\c
+.BI \-X \ authzid\fR]
+[\c
+.BI \-Y \ mech\fR]
+[\c
+.BR \-Z [ Z ]]
+.I filter
+[\c
+.IR attrs... ]
+.SH DESCRIPTION
+.I ldapsearch
+is a shell-accessible interface to the
+.BR ldap_search_ext (3)
+library call.
+.LP
+.B ldapsearch
+opens a connection to an LDAP server, binds, and performs a search
+using specified parameters. The \fIfilter\fP should conform to
+the string representation for search filters as defined in RFC 4515.
+If not provided, the default filter, \fB(objectClass=*)\fP, is used.
+.LP
+If
+.B ldapsearch
+finds one or more entries, the attributes specified by
+\fIattrs\fP are returned. If \fB*\fP is listed, all user attributes are
+returned. If \fB+\fP is listed, all operational attributes are returned.
+If no \fIattrs\fP are listed, all user attributes are returned. If only
+1.1 is listed, no attributes will be returned.
+.LP
+The search results are displayed using an extended version of LDIF.
+Option \fI\-L\fP controls the format of the output.
+.SH OPTIONS
+.TP
+.BR \-V [ V ]
+Print version info.
+If \fB\-VV\fP is given, exit after providing version info. Otherwise proceed
+with the specified search
+.TP
+.BI \-d \ debuglevel
+Set the LDAP debugging level to \fIdebuglevel\fP.
+.B ldapsearch
+must be compiled with LDAP_DEBUG defined for this option to have any effect.
+.TP
+.B \-n
+Show what would be done, but don't actually perform the search. Useful for
+debugging in conjunction with \fB\-v\fP.
+.TP
+.B \-v
+Run in verbose mode, with many diagnostics written to standard output.
+.TP
+.B \-c
+Continuous operation mode. Errors are reported, but ldapsearch will continue
+with searches. The default is to exit after reporting an error. Only useful
+in conjunction with \fB\-f\fP.
+.TP
+.B \-u
+Include the User Friendly Name form of the Distinguished Name (DN)
+in the output.
+.TP
+.BR \-t [ t ]
+A single \fB\-t\fP writes retrieved non-printable values to a set of temporary
+files. This is useful for dealing with values containing non-character
+data such as jpegPhoto or audio. A second \fB\-t\fP writes all retrieved values to
+files.
+.TP
+.BI \-T \ path
+Write temporary files to directory specified by \fIpath\fP (default:
+\fB/var/tmp/\fP)
+.TP
+.BI \-F \ prefix
+URL prefix for temporary files. Default is \fBfile://\fIpath\fP where
+\fIpath\fP is \fB/var/tmp/\fP or specified with \fB\-T\fP.
+.TP
+.B \-A
+Retrieve attributes only (no values). This is useful when you just want to
+see if an attribute is present in an entry and are not interested in the
+specific values.
+.TP
+.B \-L
+Search results are display in LDAP Data Interchange Format detailed in
+.BR ldif (5).
+A single \fB\-L\fP restricts the output to LDIFv1.
+ A second \fB\-L\fP disables comments.
+A third \fB\-L\fP disables printing of the LDIF version.
+The default is to use an extended version of LDIF.
+.TP
+.BI \-S \ attribute
+Sort the entries returned based on \fIattribute\fP. The default is not
+to sort entries returned. If \fIattribute\fP is a zero-length string (""),
+the entries are sorted by the components of their Distinguished Name. See
+.BR ldap_sort (3)
+for more details. Note that
+.B ldapsearch
+normally prints out entries as it receives them. The use of the \fB\-S\fP
+option defeats this behavior, causing all entries to be retrieved,
+then sorted, then printed.
+.TP
+.BI \-b \ searchbase
+Use \fIsearchbase\fP as the starting point for the search instead of
+the default.
+.TP
+.BR \-s \ { base \||\| one \||\| sub \||\| children }
+Specify the scope of the search to be one of
+.BR base ,
+.BR one ,
+.BR sub ,
+or
+.B children
+to specify a base object, one-level, subtree, or children search.
+The default is
+.BR sub .
+Note:
+.I children
+scope requires LDAPv3 subordinate feature extension.
+.TP
+.BR \-a \ { never \||\| always \||\| search \||\| find }
+Specify how aliases dereferencing is done. Should be one of
+.BR never ,
+.BR always ,
+.BR search ,
+or
+.B find
+to specify that aliases are never dereferenced, always dereferenced,
+dereferenced when searching, or dereferenced only when locating the
+base object for the search. The default is to never dereference aliases.
+.TP
+.BI \-l \ timelimit
+wait at most \fItimelimit\fP seconds for a search to complete.
+A timelimit of
+.I 0
+(zero) or
+.I none
+means no limit.
+A timelimit of
+.I max
+means the maximum integer allowable by the protocol.
+A server may impose a maximal timelimit which only
+the root user may override.
+.TP
+.BI \-z \ sizelimit
+retrieve at most \fIsizelimit\fP entries for a search.
+A sizelimit of
+.I 0
+(zero) or
+.I none
+means no limit.
+A sizelimit of
+.I max
+means the maximum integer allowable by the protocol.
+A server may impose a maximal sizelimit which only
+the root user may override.
+.TP
+.BI \-f \ file
+Read a series of lines from \fIfile\fP, performing one LDAP search for
+each line. In this case, the \fIfilter\fP given on the command line
+is treated as a pattern where the first and only occurrence of \fB%s\fP
+is replaced with a line from \fIfile\fP. Any other occurrence of the
+the \fB%\fP character in the pattern will be regarded as an error.
+Where it is desired that the search filter include a \fB%\fP character,
+the character should be encoded as \fB\\25\fP (see RFC 4515).
+If \fIfile\fP is a single
+\fB\-\fP character, then the lines are read from standard input.
+.B ldapsearch
+will exit when the first non-successful search result is returned,
+unless \fB\-c\fP is used.
+.TP
+.BR \-M [ M ]
+Enable manage DSA IT control.
+.B \-MM
+makes control critical.
+.TP
+.B \-x
+Use simple authentication instead of SASL.
+.TP
+.BI \-D \ binddn
+Use the Distinguished Name \fIbinddn\fP to bind to the LDAP directory.
+For SASL binds, the server is expected to ignore this value.
+.TP
+.B \-W
+Prompt for simple authentication.
+This is used instead of specifying the password on the command line.
+.TP
+.BI \-w \ passwd
+Use \fIpasswd\fP as the password for simple authentication.
+.TP
+.BI \-y \ passwdfile
+Use complete contents of \fIpasswdfile\fP as the password for
+simple authentication.
+.TP
+.BI \-H \ ldapuri
+Specify URI(s) referring to the ldap server(s);
+a list of URI, separated by whitespace or commas is expected;
+only the protocol/host/port fields are allowed.
+As an exception, if no host/port is specified, but a DN is,
+the DN is used to look up the corresponding host(s) using the
+DNS SRV records, according to RFC 2782. The DN must be a non-empty
+sequence of AVAs whose attribute type is "dc" (domain component),
+and must be escaped according to RFC 2396.
+.TP
+.BR \-P \ { 2 \||\| 3 }
+Specify the LDAP protocol version to use.
+.TP
+.BR \-e \ [ ! ] \fIext\fP [ =\fIextparam\fP ]
+.TP
+.BR \-E \ [ ! ] \fIext\fP [ =\fIextparam\fP ]
+
+Specify general extensions with \fB\-e\fP and search extensions with \fB\-E\fP.
+\'\fB!\fP\' indicates criticality.
+
+General extensions:
+.nf
+ [!]assert=<filter> (an RFC 4515 Filter)
+ !authzid=<authzid> ("dn:<dn>" or "u:<user>")
+ [!]bauthzid (RFC 3829 authzid control)
+ [!]chaining[=<resolve>[/<cont>]]
+ [!]manageDSAit
+ [!]noop
+ ppolicy
+ [!]postread[=<attrs>] (a comma-separated attribute list)
+ [!]preread[=<attrs>] (a comma-separated attribute list)
+ [!]relax
+ sessiontracking[=<username>]
+ abandon,cancel,ignore (SIGINT sends abandon/cancel,
+ or ignores response; if critical, doesn't wait for SIGINT.
+ not really controls)
+.fi
+
+Search extensions:
+.nf
+ !dontUseCopy
+ [!]domainScope (domain scope)
+ [!]mv=<filter> (matched values filter)
+ [!]pr=<size>[/prompt|noprompt] (paged results/prompt)
+ [!]sss=[\-]<attr[:OID]>[/[\-]<attr[:OID]>...] (server side sorting)
+ [!]subentries[=true|false] (subentries)
+ [!]sync=ro[/<cookie>] (LDAP Sync refreshOnly)
+ rp[/<cookie>][/<slimit>] (LDAP Sync refreshAndPersist)
+ [!]vlv=<before>/<after>(/<offset>/<count>|:<value>) (virtual list view)
+ [!]deref=derefAttr:attr[,attr[...]][;derefAttr:attr[,attr[...]]]
+ [!]<oid>[=:<value>|::<b64value>]
+.fi
+.TP
+.BI \-o \ opt \fR[= optparam \fR]
+
+Specify any
+.BR ldap.conf (5)
+option or one of the following:
+.nf
+ nettimeout=<timeout> (in seconds, or "none" or "max")
+ ldif_wrap=<width> (in columns, or "no" for no wrapping)
+.fi
+
+.TP
+.BI \-O \ security-properties
+Specify SASL security properties.
+.TP
+.B \-I
+Enable SASL Interactive mode. Always prompt. Default is to prompt
+only as needed.
+.TP
+.B \-Q
+Enable SASL Quiet mode. Never prompt.
+.TP
+.B \-N
+Do not use reverse DNS to canonicalize SASL host name.
+.TP
+.BI \-U \ authcid
+Specify the authentication ID for SASL bind. The form of the ID
+depends on the actual SASL mechanism used.
+.TP
+.BI \-R \ realm
+Specify the realm of authentication ID for SASL bind. The form of the realm
+depends on the actual SASL mechanism used.
+.TP
+.BI \-X \ authzid
+Specify the requested authorization ID for SASL bind.
+.I authzid
+must be one of the following formats:
+.BI dn: "<distinguished name>"
+or
+.BI u: <username>
+.TP
+.BI \-Y \ mech
+Specify the SASL mechanism to be used for authentication. If it's not
+specified, the program will choose the best mechanism the server knows.
+.TP
+.BR \-Z [ Z ]
+Issue StartTLS (Transport Layer Security) extended operation. If you use
+\fB\-ZZ\fP, the command will require the operation to be successful.
+.SH OUTPUT FORMAT
+If one or more entries are found, each entry is written to standard
+output in LDAP Data Interchange Format or
+.BR ldif (5):
+.LP
+.nf
+ version: 1
+
+ # bjensen, example, net
+ dn: uid=bjensen,dc=example,dc=net
+ objectClass: person
+ objectClass: dcObject
+ uid: bjensen
+ cn: Barbara Jensen
+ sn: Jensen
+ ...
+.fi
+.LP
+If the \fB\-t\fP option is used, the URI of a temporary file
+is used in place of the actual value. If the \fB\-A\fP option
+is given, only the "attributename" part is written.
+.SH EXAMPLE
+The following command:
+.LP
+.nf
+ ldapsearch \-LLL "(sn=smith)" cn sn telephoneNumber
+.fi
+.LP
+will perform a subtree search (using the default search base and
+other parameters defined in
+.BR ldap.conf (5))
+for entries with a surname (sn) of smith. The common name (cn), surname
+(sn) and telephoneNumber values will be retrieved and printed to
+standard output.
+The output might look something like this if two entries are found:
+.LP
+.nf
+ dn: uid=jts,dc=example,dc=com
+ cn: John Smith
+ cn: John T. Smith
+ sn: Smith
+ sn;lang\-en: Smith
+ sn;lang\-de: Schmidt
+ telephoneNumber: 1 555 123\-4567
+
+ dn: uid=sss,dc=example,dc=com
+ cn: Steve Smith
+ cn: Steve S. Smith
+ sn: Smith
+ sn;lang\-en: Smith
+ sn;lang\-de: Schmidt
+ telephoneNumber: 1 555 765\-4321
+.fi
+.LP
+The command:
+.LP
+.nf
+ ldapsearch \-LLL \-u \-t "(uid=xyz)" jpegPhoto audio
+.fi
+.LP
+will perform a subtree search using the default search base for entries
+with user id of "xyz". The user friendly form of the entry's DN will be
+output after the line that contains the DN itself, and the jpegPhoto
+and audio values will be retrieved and written to temporary files. The
+output might look like this if one entry with one value for each of the
+requested attributes is found:
+.LP
+.nf
+ dn: uid=xyz,dc=example,dc=com
+ ufn: xyz, example, com
+ audio:< file:///tmp/ldapsearch\-audio\-a19924
+ jpegPhoto:< file:///tmp/ldapsearch\-jpegPhoto\-a19924
+.fi
+.LP
+This command:
+.LP
+.nf
+ ldapsearch \-LLL \-s one \-b "c=US" "(o=University*)" o description
+.fi
+.LP
+will perform a one-level search at the c=US level for all entries
+whose organization name (o) begins with \fBUniversity\fP.
+The organization name and description attribute values will be retrieved
+and printed to standard output, resulting in output similar to this:
+.LP
+.nf
+ dn: o=University of Alaska Fairbanks,c=US
+ o: University of Alaska Fairbanks
+ description: Naturally Inspiring
+ description: leaf node only
+
+ dn: o=University of Colorado at Boulder,c=US
+ o: University of Colorado at Boulder
+ description: No personnel information
+ description: Institution of education and research
+
+ dn: o=University of Colorado at Denver,c=US
+ o: University of Colorado at Denver
+ o: UCD
+ o: CU/Denver
+ o: CU\-Denver
+ description: Institute for Higher Learning and Research
+
+ dn: o=University of Florida,c=US
+ o: University of Florida
+ o: UFl
+ description: Warper of young minds
+
+ ...
+.fi
+.SH DIAGNOSTICS
+Exit status is zero if no errors occur.
+Errors result in a non-zero exit status and
+a diagnostic message being written to standard error.
+.SH "SEE ALSO"
+.BR ldapadd (1),
+.BR ldapdelete (1),
+.BR ldapmodify (1),
+.BR ldapmodrdn (1),
+.BR ldap.conf (5),
+.BR ldif (5),
+.BR ldap (3),
+.BR ldap_search_ext (3),
+.BR ldap_sort (3)
+.SH AUTHOR
+The OpenLDAP Project <http://www.openldap.org/>
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man1/ldapurl.1 b/doc/man/man1/ldapurl.1
new file mode 100644
index 0000000..7e38270
--- /dev/null
+++ b/doc/man/man1/ldapurl.1
@@ -0,0 +1,168 @@
+.TH LDAPURL 1 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 2008-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldapurl \- LDAP URL formatting tool
+.SH SYNOPSIS
+.B ldapurl
+[\c
+.BR \-a \ attrs\fR]
+[\c
+.BI \-b \ searchbase\fR]
+[\c
+.BR \-e \ [ ! ] \fIext\fP [ =\fIextparam\fP ]]
+[\c
+.BR \-E \ [ ! ] \fIext\fP [ =\fIextparam\fP ]]
+[\c
+.BI \-f \ filter\fR]
+[\c
+.BI \-H \ ldapuri\fR]
+[\c
+.BI \-h \ ldaphost\fR]
+[\c
+.BI \-p \ ldapport\fR]
+[\c
+.BR \-s \ { base \||\| one \||\| sub \||\| children }]
+[\c
+.BI \-S \ scheme\fR]
+.SH DESCRIPTION
+.I ldapurl
+is a command that allows one to either compose or decompose LDAP URIs.
+.LP
+When invoked with the \fB\-H\fP option,
+.B ldapurl
+extracts the components of the \fIldapuri\fP option argument,
+unescaping hex-escaped chars as required.
+It basically acts as a frontend to the
+.BR ldap_url_parse (3)
+call.
+Otherwise, it builds an LDAP URI based on the components
+passed with the appropriate options, performing the inverse operation.
+Option \fB\-H\fP is incompatible with options
+.BR \-a ,
+.BR \-b ,
+.BR \-E ,
+.BR \-f ,
+.BR \-H ,
+.BR \-h ,
+.BR \-p ,
+.BR \-S ,
+and
+.BR \-s .
+.SH OPTIONS
+.TP
+.TP
+.BI \-a \ attrs
+Set a comma-separated list of attribute selectors.
+.TP
+.BI \-b \ searchbase
+Set the \fIsearchbase\fP.
+.TP
+.BR \-e \ [ ! ] \fIext\fP [ =\fIextparam\fP ]
+
+Specify general extensions with \fB\-e\fP
+\'\fB!\fP\' indicates criticality.
+
+General extensions:
+.nf
+ [!]assert=<filter> (an RFC 4515 Filter)
+ !authzid=<authzid> ("dn:<dn>" or "u:<user>")
+ [!]bauthzid (RFC 3829 authzid control)
+ [!]chaining[=<resolve>[/<cont>]]
+ [!]manageDSAit
+ [!]noop
+ ppolicy
+ [!]postread[=<attrs>] (a comma-separated attribute list)
+ [!]preread[=<attrs>] (a comma-separated attribute list)
+ [!]relax
+ sessiontracking[=<username>]
+ abandon,cancel,ignore (SIGINT sends abandon/cancel,
+ or ignores response; if critical, doesn't wait for SIGINT.
+ not really controls)
+.fi
+
+.TP
+.BR \-E \ [ ! ] \fIext\fP [ =\fIextparam\fP ]
+Set URL extensions; incompatible with
+.BR \-H .
+.TP
+.BI \-f \ filter
+Set the URL filter. No particular check on conformity with RFC 4515
+LDAP filters is performed, but the value is hex-escaped as required.
+.TP
+.BI \-H \ ldapuri
+Specify URI to be exploded.
+.TP
+.BI \-h \ ldaphost
+Set the host.
+.TP
+.BI \-p \ ldapport
+Set the TCP port.
+.TP
+.BI \-S \ scheme
+Set the URL scheme. Defaults for other fields, like \fIldapport\fP,
+may depend on the value of \fIscheme\fP.
+.TP
+.BR \-s \ { base \||\| one \||\| sub \||\| children }
+Specify the scope of the search to be one of
+.BR base ,
+.BR one ,
+.BR sub ,
+or
+.B children
+to specify a base object, one-level, subtree, or children search.
+The default is
+.BR sub .
+Note:
+.B children
+scope requires LDAPv3 subordinate feature extension.
+
+.SH OUTPUT FORMAT
+If the \fB\-H\fP option is used, the \fIldapuri\fP supplied
+is exploded in its components, which are printed to standard output
+in an LDIF-like form.
+.LP
+Otherwise, the URI built using the values passed with the other options
+is printed to standard output.
+.SH EXAMPLE
+The following command:
+.LP
+.nf
+ ldapurl \-h ldap.example.com \-b dc=example,dc=com \-s sub \-f "(cn=Some One)"
+.fi
+.LP
+returns
+.LP
+.nf
+ ldap://ldap.example.com:389/dc=example,dc=com??sub?(cn=Some%20One)
+.fi
+.LP
+The command:
+.LP
+.nf
+ ldapurl \-H ldap://ldap.example.com:389/dc=example,dc=com??sub?(cn=Some%20One)
+.fi
+.LP
+returns
+.LP
+.nf
+ scheme: ldap
+ host: ldap.example.com
+ port: 389
+ dn: dc=example,dc=com
+ scope: sub
+ filter: (cn=Some One)
+.fi
+.LP
+.SH DIAGNOSTICS
+Exit status is zero if no errors occur.
+Errors result in a non-zero exit status and
+a diagnostic message being written to standard error.
+.SH "SEE ALSO"
+.BR ldap (3),
+.BR ldap_url_parse (3),
+.SH AUTHOR
+The OpenLDAP Project <http://www.openldap.org/>
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man1/ldapvc.1 b/doc/man/man1/ldapvc.1
new file mode 100644
index 0000000..4733080
--- /dev/null
+++ b/doc/man/man1/ldapvc.1
@@ -0,0 +1,213 @@
+.TH LDAPVC 1 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldapvc \- LDAP verify credentials tool
+.SH SYNOPSIS
+.B ldapvc
+[\c
+.BR \-V [ V ]]
+[\c
+.BI \-d \ debuglevel\fR]
+[\c
+.BR \-a ]
+[\c
+.BR \-b ]
+[\c
+.BR \-n ]
+[\c
+.BR \-v ]
+[\c
+.BR \-x ]
+[\c
+.BI \-D \ binddn\fR]
+[\c
+.BR \-W ]
+[\c
+.BI \-w \ passwd\fR]
+[\c
+.BI \-y \ passwdfile\fR]
+[\c
+.BI \-H \ ldapuri\fR]
+[\c
+.BR \-e \ [ ! ] \fIext\fP [ =\fIextparam\fP ]]
+[\c
+.BR \-E \ [ ! ] \fIext\fP [ =\fIextparam\fP ]]
+[\c
+.BI \-o \ opt \fR[= optparam \fR]]
+[\c
+.BI \-O \ security-properties\fR]
+[\c
+.BR \-I ]
+[\c
+.BR \-Q ]
+[\c
+.BR \-N ]
+[\c
+.BI \-U \ authcid\fR]
+[\c
+.BI \-R \ realm\fR]
+[\c
+.BI \-X \ authzid\fR]
+[\c
+.BI \-Y \ mech\fR]
+[\c
+.BR \-Z [ Z ]]
+\c
+.I Distinguished Name \
+\c
+.I [Credentials]
+.SH DESCRIPTION
+.I ldapvc
+implements the LDAP "Verify Credentials" extended operation.
+.LP
+.B Verify Credentials
+operation behaves like LDAP Bind but has no impact upon the underlying LDAP session.
+.SH OPTIONS
+.TP
+.BR \-V [ V ]
+Print version info.
+If \fB\-VV\fP is given, only the version information is printed.
+.TP
+.BI \-d \ debuglevel
+Set the LDAP debugging level to \fIdebuglevel\fP.
+.B ldapvc
+must be compiled with LDAP_DEBUG defined for this option to have any effect.
+.TP
+.B \-a
+Print the authzID resulting from a successful verification of credentials.
+.TP
+.B \-b
+Print the results from the ppolicy control after verification of credentials.
+.TP
+.B \-n
+Show what would be done, but don't actually perform the operation.
+Useful for
+debugging in conjunction with \fB\-v\fP.
+.TP
+.B \-v
+Run in verbose mode, with many diagnostics written to standard output.
+.TP
+.B \-x
+Use simple authentication instead of SASL.
+.TP
+.BI \-D \ binddn
+Use the Distinguished Name \fIbinddn\fP to bind to the LDAP directory.
+For SASL binds, the server is expected to ignore this value.
+.TP
+.B \-W
+Prompt for simple authentication.
+This is used instead of specifying the password on the command line.
+.TP
+.BI \-w \ passwd
+Use \fIpasswd\fP as the password for simple authentication.
+.TP
+.BI \-y \ passwdfile
+Use complete contents of \fIpasswdfile\fP as the password for
+simple authentication.
+.TP
+.BI \-H \ ldapuri
+Specify URI(s) referring to the ldap server(s); only the protocol/host/port
+fields are allowed; a list of URI, separated by whitespace or commas
+is expected.
+.TP
+.BR \-e \ [ ! ] \fIext\fP [ =\fIextparam\fP ]
+.TP
+.BR \-E \ [ ! ] \fIext\fP [ =\fIextparam\fP ]
+
+Specify general extensions with \fB\-e\fP and Verify Credentials extensions with \fB\-E\fP.
+\'\fB!\fP\' indicates criticality.
+
+General extensions:
+.nf
+ [!]assert=<filter> (an RFC 4515 Filter)
+ [!]bauthzid (RFC 3829 authzid control)
+ [!]chaining[=<resolve>[/<cont>]]
+ [!]manageDSAit
+ [!]noop
+ ppolicy
+ [!]postread[=<attrs>] (a comma-separated attribute list)
+ [!]preread[=<attrs>] (a comma-separated attribute list)
+ [!]relax
+ sessiontracking[=<username>]
+ abandon,cancel,ignore (SIGINT sends abandon/cancel,
+ or ignores response; if critical, doesn't wait for SIGINT.
+ not really controls)
+.fi
+.sp
+Verify Credentials extensions:
+.sp
+The following options set SASL params on the Verify Credentials request:
+.nf
+ authcid=<authcid> (SASL Authentication Identity "dn:<dn>" or "u:<user>")
+ authzid=<authzid> (SASL Authorization Identity "dn:<dn>" or "u:<user>")
+ mech=<mech> (SASL mechanism default e.g. Simple)
+ realm=<realm> (SASL Realm, defaults to none)
+ sasl=a[utomatic]|i[nteractive]|q[uiet] (SASL mode defaults to automatic if any other -E option provided, otherwise none)
+ secprops=<secprops> (SASL Security Properties)
+.fi
+.TP
+.BI \-o \ opt \fR[= optparam \fR]
+
+Specify any
+.BR ldap.conf (5)
+option or one of the following:
+.nf
+ nettimeout=<timeout> (in seconds, or "none" or "max")
+ ldif_wrap=<width> (in columns, or "no" for no wrapping)
+.fi
+
+.B -o
+option that can be passed here, check
+.BR ldap.conf (5)
+for details.
+.TP
+.BI \-O \ security-properties
+Specify SASL security properties.
+.TP
+.B \-I
+Enable SASL Interactive mode. Always prompt. Default is to prompt
+only as needed.
+.TP
+.B \-Q
+Enable SASL Quiet mode. Never prompt.
+.TP
+.B \-N
+Do not use reverse DNS to canonicalize SASL host name.
+.TP
+.BI \-U \ authcid
+Specify the authentication ID for SASL bind. The form of the ID
+depends on the actual SASL mechanism used.
+.TP
+.BI \-R \ realm
+Specify the realm of authentication ID for SASL bind. The form of the realm
+depends on the actual SASL mechanism used.
+.TP
+.BI \-X \ authzid
+Specify the requested authorization ID for SASL bind.
+.I authzid
+must be one of the following formats:
+.BI dn: "<distinguished name>"
+or
+.BI u: <username>
+.TP
+.BI \-Y \ mech
+Specify the SASL mechanism to be used for authentication. If it's not
+specified, the program will choose the best mechanism the server knows.
+.TP
+.BR \-Z [ Z ]
+Issue StartTLS (Transport Layer Security) extended operation. If you use
+\fB\-ZZ\fP, the command will require the operation to be successful.
+.SH EXAMPLE
+.nf
+ ldapvc \-x "uid=Alice,ou=People,dc=example,dc=com"
+.fi
+.SH "SEE ALSO"
+.BR ldap.conf (5),
+.BR ldap (3),
+.BR ldap_extended_operation (3)
+.SH AUTHOR
+The OpenLDAP Project <http://www.openldap.org/>
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man1/ldapwhoami.1 b/doc/man/man1/ldapwhoami.1
new file mode 100644
index 0000000..49b1187
--- /dev/null
+++ b/doc/man/man1/ldapwhoami.1
@@ -0,0 +1,194 @@
+.TH LDAPWHOAMI 1 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldapwhoami \- LDAP who am i? tool
+.SH SYNOPSIS
+.B ldapwhoami
+[\c
+.BR \-V [ V ]]
+[\c
+.BI \-d \ debuglevel\fR]
+[\c
+.BR \-n ]
+[\c
+.BR \-v ]
+[\c
+.BR \-x ]
+[\c
+.BI \-D \ binddn\fR]
+[\c
+.BR \-W ]
+[\c
+.BI \-w \ passwd\fR]
+[\c
+.BI \-y \ passwdfile\fR]
+[\c
+.BI \-H \ ldapuri\fR]
+[\c
+.BR \-e \ [ ! ] \fIext\fP [ =\fIextparam\fP ]]
+[\c
+.BR \-E \ [ ! ] \fIext\fP [ =\fIextparam\fP ]]
+[\c
+.BI \-o \ opt \fR[= optparam \fR]]
+[\c
+.BI \-O \ security-properties\fR]
+[\c
+.BR \-I ]
+[\c
+.BR \-Q ]
+[\c
+.BR \-N ]
+[\c
+.BI \-U \ authcid\fR]
+[\c
+.BI \-R \ realm\fR]
+[\c
+.BI \-X \ authzid\fR]
+[\c
+.BI \-Y \ mech\fR]
+[\c
+.BR \-Z [ Z ]]
+.SH DESCRIPTION
+.I ldapwhoami
+implements the LDAP "Who Am I?" extended operation.
+.LP
+.B ldapwhoami
+opens a connection to an LDAP server, binds, and performs a whoami
+operation.
+.SH OPTIONS
+.TP
+.BR \-V [ V ]
+Print version info.
+If \fB\-VV\fP is given, only the version information is printed.
+.TP
+.BI \-d \ debuglevel
+Set the LDAP debugging level to \fIdebuglevel\fP.
+.B ldapwhoami
+must be compiled with LDAP_DEBUG defined for this option to have any effect.
+.TP
+.B \-n
+Show what would be done, but don't actually perform the whoami operation.
+Useful for
+debugging in conjunction with \fB\-v\fP.
+.TP
+.B \-v
+Run in verbose mode, with many diagnostics written to standard output.
+.TP
+.B \-x
+Use simple authentication instead of SASL.
+.TP
+.BI \-D \ binddn
+Use the Distinguished Name \fIbinddn\fP to bind to the LDAP directory.
+For SASL binds, the server is expected to ignore this value.
+.TP
+.B \-W
+Prompt for simple authentication.
+This is used instead of specifying the password on the command line.
+.TP
+.BI \-w \ passwd
+Use \fIpasswd\fP as the password for simple authentication.
+.TP
+.BI \-y \ passwdfile
+Use complete contents of \fIpasswdfile\fP as the password for
+simple authentication.
+.TP
+.BI \-H \ ldapuri
+Specify URI(s) referring to the ldap server(s); only the protocol/host/port
+fields are allowed; a list of URI, separated by whitespace or commas
+is expected.
+.TP
+.BR \-e \ [ ! ] \fIext\fP [ =\fIextparam\fP ]
+.TP
+.BR \-E \ [ ! ] \fIext\fP [ =\fIextparam\fP ]
+
+Specify general extensions with \fB\-e\fP and whoami extensions with \fB\-E\fP.
+\'\fB!\fP\' indicates criticality.
+
+General extensions:
+.nf
+ [!]assert=<filter> (an RFC 4515 Filter)
+ !authzid=<authzid> ("dn:<dn>" or "u:<user>")
+ [!]bauthzid (RFC 3829 authzid control)
+ [!]chaining[=<resolve>[/<cont>]]
+ [!]manageDSAit
+ [!]noop
+ ppolicy
+ [!]postread[=<attrs>] (a comma-separated attribute list)
+ [!]preread[=<attrs>] (a comma-separated attribute list)
+ [!]relax
+ sessiontracking[=<username>]
+ abandon,cancel,ignore (SIGINT sends abandon/cancel,
+ or ignores response; if critical, doesn't wait for SIGINT.
+ not really controls)
+.fi
+
+WhoAmI extensions:
+.nf
+ (none)
+.fi
+.TP
+.BI \-o \ opt \fR[= optparam \fR]
+
+Specify any
+.BR ldap.conf (5)
+option or one of the following:
+.nf
+ nettimeout=<timeout> (in seconds, or "none" or "max")
+ ldif_wrap=<width> (in columns, or "no" for no wrapping)
+.fi
+
+.B -o
+option that can be passed here, check
+.BR ldap.conf (5)
+for details.
+.TP
+.BI \-O \ security-properties
+Specify SASL security properties.
+.TP
+.B \-I
+Enable SASL Interactive mode. Always prompt. Default is to prompt
+only as needed.
+.TP
+.B \-Q
+Enable SASL Quiet mode. Never prompt.
+.TP
+.B \-N
+Do not use reverse DNS to canonicalize SASL host name.
+.TP
+.BI \-U \ authcid
+Specify the authentication ID for SASL bind. The form of the ID
+depends on the actual SASL mechanism used.
+.TP
+.BI \-R \ realm
+Specify the realm of authentication ID for SASL bind. The form of the realm
+depends on the actual SASL mechanism used.
+.TP
+.BI \-X \ authzid
+Specify the requested authorization ID for SASL bind.
+.I authzid
+must be one of the following formats:
+.BI dn: "<distinguished name>"
+or
+.BI u: <username>
+.TP
+.BI \-Y \ mech
+Specify the SASL mechanism to be used for authentication. If it's not
+specified, the program will choose the best mechanism the server knows.
+.TP
+.BR \-Z [ Z ]
+Issue StartTLS (Transport Layer Security) extended operation. If you use
+\fB\-ZZ\fP, the command will require the operation to be successful.
+.SH EXAMPLE
+.nf
+ ldapwhoami \-x \-D "cn=Manager,dc=example,dc=com" \-W
+.fi
+.SH "SEE ALSO"
+.BR ldap.conf (5),
+.BR ldap (3),
+.BR ldap_extended_operation (3)
+.SH AUTHOR
+The OpenLDAP Project <http://www.openldap.org/>
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/Deprecated b/doc/man/man3/Deprecated
new file mode 100644
index 0000000..3b7f696
--- /dev/null
+++ b/doc/man/man3/Deprecated
@@ -0,0 +1,7 @@
+Deprecated interfaces generally remain in the library. The macro
+LDAP_DEPRECATED can be defined to a non-zero value
+(e.g., -DLDAP_DEPRECATED=1) when compiling program designed to use
+deprecated interfaces. It is recommended that developers writing new
+programs, or updating old programs, avoid use of deprecated interfaces.
+Over time, it is expected that documentation (and, eventually, support) for
+deprecated interfaces to be eliminated.
diff --git a/doc/man/man3/Makefile.in b/doc/man/man3/Makefile.in
new file mode 100644
index 0000000..0a43c6e
--- /dev/null
+++ b/doc/man/man3/Makefile.in
@@ -0,0 +1,16 @@
+# man3 Makefile.in for OpenLDAP
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+MANSECT=3
diff --git a/doc/man/man3/lber-decode.3 b/doc/man/man3/lber-decode.3
new file mode 100644
index 0000000..97d4932
--- /dev/null
+++ b/doc/man/man3/lber-decode.3
@@ -0,0 +1,357 @@
+.TH LBER_DECODE 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ber_get_next, ber_skip_tag, ber_peek_tag, ber_scanf, ber_get_int, ber_get_enum, ber_get_stringb, ber_get_stringa, ber_get_stringal, ber_get_stringbv, ber_get_null, ber_get_boolean, ber_get_bitstring, ber_first_element, ber_next_element \- OpenLDAP LBER simplified Basic Encoding Rules library routines for decoding
+.SH LIBRARY
+OpenLDAP LBER (liblber, \-llber)
+.SH SYNOPSIS
+.B #include <lber.h>
+.LP
+.BI "ber_tag_t ber_get_next(Sockbuf *" sb ", ber_len_t *" len ", BerElement *" ber ");"
+.LP
+.BI "ber_tag_t ber_skip_tag(BerElement *" ber ", ber_len_t *" len ");"
+.LP
+.BI "ber_tag_t ber_peek_tag(BerElement *" ber ", ber_len_t *" len ");"
+.LP
+.BI "ber_tag_t ber_scanf(BerElement *" ber ", const char *" fmt ", ...);"
+.LP
+.BI "ber_tag_t ber_get_int(BerElement *" ber ", ber_int_t *" num ");"
+.LP
+.BI "ber_tag_t ber_get_enum(BerElement *" ber ", ber_int_t *" num ");"
+.LP
+.BI "ber_tag_t ber_get_stringb(BerElement *" ber ", char *" buf ", ber_len_t *" len ");"
+.LP
+.BI "ber_tag_t ber_get_stringa(BerElement *" ber ", char **" buf ");"
+.LP
+.BI "ber_tag_t ber_get_stringal(BerElement *" ber ", struct berval **" bv ");"
+.LP
+.BI "ber_tag_t ber_get_stringbv(BerElement *" ber ", struct berval *" bv ", int " alloc ");"
+.LP
+.BI "ber_tag_t ber_get_null(BerElement *" ber ");"
+.LP
+.BI "ber_tag_t ber_get_boolean(BerElement *" ber ", ber_int_t *" bool ");"
+.LP
+.BI "ber_tag_t ber_get_bitstringa(BerElement *" ber ", char **" buf ", ber_len_t *" blen ");"
+.LP
+.BI "ber_tag_t ber_first_element(BerElement *" ber ", ber_len_t *" len ", char **" cookie ");"
+.LP
+.BI "ber_tag_t ber_next_element(BerElement *" ber ", ber_len_t *" len ", const char *" cookie ");"
+.SH DESCRIPTION
+.LP
+These routines provide a subroutine interface to a simplified
+implementation of the Basic Encoding Rules of ASN.1. The version
+of BER these routines support is the one defined for the LDAP
+protocol. The encoding rules are the same as BER, except that
+only definite form lengths are used, and bitstrings and octet strings
+are always encoded in primitive form. This man page
+describes the decoding routines in the lber library. See
+.BR lber-encode (3)
+for details on the corresponding encoding routines.
+Consult
+.BR lber-types (3)
+for information about types, allocators, and deallocators.
+.LP
+Normally, the only routines that need to be called by an application
+are
+.BR ber_get_next ()
+to get the next BER element and
+.BR ber_scanf ()
+to do the actual decoding. In some cases,
+.BR ber_peek_tag ()
+may also need to be called in normal usage. The other routines are
+provided for those applications that need more control than
+.BR ber_scanf ()
+provides. In
+general, these routines return the tag of the element decoded, or
+LBER_ERROR if an error occurred.
+.LP
+The
+.BR ber_get_next ()
+routine is used to read the next BER element from the given Sockbuf,
+\fIsb\fP. It strips off and returns the leading tag, strips off and
+returns the length of the entire element in \fIlen\fP, and sets up
+\fIber\fP for subsequent calls to
+.BR ber_scanf ()
+et al to decode the element. See
+.BR lber-sockbuf (3)
+for details of the Sockbuf implementation of the \fIsb\fP parameter.
+.LP
+The
+.BR ber_scanf ()
+routine is used to decode a BER element in much the same way that
+.BR scanf (3)
+works. It reads from \fIber\fP, a pointer to a BerElement
+such as returned by
+.BR ber_get_next (),
+interprets the bytes according to the format string \fIfmt\fP, and stores the
+results in its additional arguments. The format string contains
+conversion specifications which are used to direct the interpretation
+of the BER element. The format string can contain the following
+characters.
+.RS
+.LP
+.TP 3
+.B a
+Octet string. A char ** should be supplied. Memory is allocated,
+filled with the contents of the octet string, null-terminated, and
+returned in the parameter. The caller should free the returned
+string using
+.BR ber_memfree ().
+.TP
+.B A
+Octet string. A variant of "\fBa\fP". A char ** should be supplied.
+Memory is allocated, filled with the contents of the octet string,
+null-terminated, and returned in the parameter, unless a zero-length
+string would result; in that case, the arg is set to NULL.
+The caller should free the returned string using
+.BR ber_memfree ().
+.TP
+.B s
+Octet string. A char * buffer should be supplied, followed by a pointer to a
+ber_len_t initialized to the size of the buffer. Upon return, the
+null-terminated octet string is put into the buffer, and the
+ber_len_t is set to the actual size of the octet string.
+.TP
+.B O
+Octet string. A struct ber_val ** should be supplied, which upon
+return points to a dynamically allocated struct berval
+containing the octet string and its length.
+The caller should free the returned structure using
+.BR ber_bvfree ().
+.TP
+.B o
+Octet string. A struct ber_val * should be supplied, which upon
+return contains the dynamically allocated
+octet string and its length. The caller should free the returned octet
+string using
+.BR ber_memfree ().
+.TP
+.B m
+Octet string. A struct ber_val * should be supplied, which upon return
+contains the octet string and its length. The string resides in memory
+assigned to the BerElement, and must not be freed by the caller.
+.TP
+.B b
+Boolean. A pointer to a ber_int_t should be supplied.
+.TP
+.B e
+Enumeration. A pointer to a ber_int_t should be supplied.
+.TP
+.B i
+Integer. A pointer to a ber_int_t should be supplied.
+.TP
+.B B
+Bitstring. A char ** should be supplied which will point to the
+dynamically allocated
+bits, followed by a ber_len_t *, which will point to the length
+(in bits) of the bitstring returned.
+.TP
+.B n
+Null. No parameter is required. The element is simply skipped if
+it is recognized.
+.TP
+.B v
+Sequence of octet strings. A char *** should be supplied, which upon
+return points to a dynamically allocated null-terminated array of char *'s
+containing the octet strings. NULL is returned if the sequence is empty.
+The caller should free the returned array and octet strings using
+.BR ber_memvfree ().
+.TP
+.B V
+Sequence of octet strings with lengths.
+A struct berval *** should be supplied, which upon
+return points to a dynamically allocated null-terminated array of
+struct berval *'s
+containing the octet strings and their lengths.
+NULL is returned if the sequence is empty.
+The caller should free the returned structures using
+.BR ber_bvecfree ().
+.TP
+.B W
+Sequence of octet strings with lengths.
+A BerVarray * should be supplied, which upon
+return points to a dynamically allocated array of
+struct berval's
+containing the octet strings and their lengths. The array is terminated
+by a struct berval with a NULL bv_val string pointer.
+NULL is returned if the sequence is empty.
+The caller should free the returned structures using
+.BR ber_bvarray_free ().
+.TP
+.B M
+Sequence of octet strings with lengths. This is a generalized form
+of the previous three formats.
+A void ** (ptr) should be supplied, followed by a ber_len_t * (len)
+and a ber_len_t (off).
+Upon return (ptr) will point to a dynamically allocated array
+whose elements are all of size (*len). A struct berval will be filled
+starting at offset (off) in each element. The strings in each struct
+berval reside in memory assigned to the BerElement and must not be
+freed by the caller. The array is terminated by a struct berval
+with a NULL bv_val string pointer. NULL is returned if the sequence
+is empty. The number of elements in the array is also stored
+in (*len) on return. The caller should free the returned array using
+.BR ber_memfree ().
+.TP
+.B l
+Length of the next element. A pointer to a ber_len_t should be supplied.
+.TP
+.B t
+Tag of the next element. A pointer to a ber_tag_t should be supplied.
+.TP
+.B T
+Skip element and return its tag. A pointer to a ber_tag_t should be supplied.
+.TP
+.B x
+Skip element. The next element is skipped.
+.TP
+.B {
+Begin sequence. No parameter is required. The initial sequence tag
+and length are skipped.
+.TP
+.B }
+End sequence. No parameter is required and no action is taken.
+.TP
+.B [
+Begin set. No parameter is required. The initial set tag
+and length are skipped.
+.TP
+.B ]
+End set. No parameter is required and no action is taken.
+.RE
+.LP
+The
+.BR ber_get_int ()
+routine tries to interpret the next element as an integer,
+returning the result in \fInum\fP. The tag of whatever it finds is returned
+on success, LBER_ERROR (\-1) on failure.
+.LP
+The
+.BR ber_get_stringb ()
+routine is used to read an octet string into a
+preallocated buffer. The \fIlen\fP parameter should be initialized to
+the size of the buffer, and will contain the length of the octet string
+read upon return. The buffer should be big enough to take the octet
+string value plus a terminating NULL byte.
+.LP
+The
+.BR ber_get_stringa ()
+routine is used to dynamically allocate space into
+which an octet string is read.
+The caller should free the returned string using
+.BR ber_memfree().
+.LP
+The
+.BR ber_get_stringal ()
+routine is used to dynamically allocate space
+into which an octet string and its length are read. It takes a
+struct berval **, and returns the result in this parameter.
+The caller should free the returned structure using
+.BR ber_bvfree().
+.LP
+The
+.BR ber_get_stringbv ()
+routine is used to read an octet string and its length into the
+provided struct berval *. If the \fIalloc\fP parameter is zero, the string
+will reside in memory assigned to the BerElement, and must not be freed
+by the caller. If the \fIalloc\fP parameter is non-zero, the string will be
+copied into dynamically allocated space which should be returned using
+.BR ber_memfree ().
+.LP
+The
+.BR ber_get_null ()
+routine is used to read a NULL element. It returns
+the tag of the element it skips over.
+.LP
+The
+.BR ber_get_boolean ()
+routine is used to read a boolean value. It is called the same way that
+.BR ber_get_int ()
+is called.
+.LP
+The
+.BR ber_get_enum ()
+routine is used to read a enumeration value. It is called the same way that
+.BR ber_get_int ()
+is called.
+.LP
+The
+.BR ber_get_bitstringa ()
+routine is used to read a bitstring value. It
+takes a char ** which will hold the dynamically allocated bits, followed by an
+ber_len_t *, which will point to the length (in bits) of the bitstring returned.
+The caller should free the returned string using
+.BR ber_memfree ().
+.LP
+The
+.BR ber_first_element ()
+routine is used to return the tag and length
+of the first element in a set or sequence. It also returns in \fIcookie\fP
+a magic cookie parameter that should be passed to subsequent calls to
+ber_next_element(), which returns similar information.
+.SH EXAMPLES
+Assume the variable \fIber\fP contains a lightweight BER encoding of
+the following ASN.1 object:
+.LP
+.nf
+ AlmostASearchRequest := SEQUENCE {
+ baseObject DistinguishedName,
+ scope ENUMERATED {
+ baseObject (0),
+ singleLevel (1),
+ wholeSubtree (2)
+ },
+ derefAliases ENUMERATED {
+ neverDerefaliases (0),
+ derefInSearching (1),
+ derefFindingBaseObj (2),
+ alwaysDerefAliases (3)
+ },
+ sizelimit INTEGER (0 .. 65535),
+ timelimit INTEGER (0 .. 65535),
+ attrsOnly BOOLEAN,
+ attributes SEQUENCE OF AttributeType
+ }
+.fi
+.LP
+The element can be decoded using
+.BR ber_scanf ()
+as follows.
+.LP
+.nf
+ ber_int_t scope, deref, size, time, attrsonly;
+ char *dn, **attrs;
+ ber_tag_t tag;
+
+ tag = ber_scanf( ber, "{aeeiib{v}}",
+ &dn, &scope, &deref,
+ &size, &time, &attrsonly, &attrs );
+
+ if( tag == LBER_ERROR ) {
+ /* error */
+ } else {
+ /* success */
+ }
+
+ ber_memfree( dn );
+ ber_memvfree( attrs );
+.fi
+.SH ERRORS
+If an error occurs during decoding, generally these routines return
+LBER_ERROR ((ber_tag_t)\-1).
+.LP
+.SH NOTES
+.LP
+The return values for all of these functions are declared in the
+.B <lber.h>
+header file. Some routines may dynamically allocate memory
+which must be freed by the caller using supplied deallocation routines.
+.SH SEE ALSO
+.BR lber-encode (3),
+.BR lber-memory (3),
+.BR lber-sockbuf (3),
+.BR lber-types (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/lber-decode.3.links b/doc/man/man3/lber-decode.3.links
new file mode 100644
index 0000000..3ec9328
--- /dev/null
+++ b/doc/man/man3/lber-decode.3.links
@@ -0,0 +1,13 @@
+ber_get_next.3
+ber_skip_tag.3
+ber_peek_tag.3
+ber_scanf.3
+ber_get_int.3
+ber_get_stringa.3
+ber_get_stringb.3
+ber_get_null.3
+ber_get_enum.3
+ber_get_boolean.3
+ber_get_bitstring.3
+ber_first_element.3
+ber_next_element.3
diff --git a/doc/man/man3/lber-encode.3 b/doc/man/man3/lber-encode.3
new file mode 100644
index 0000000..0d2e44d
--- /dev/null
+++ b/doc/man/man3/lber-encode.3
@@ -0,0 +1,288 @@
+.TH LBER_ENCODE 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ber_alloc_t, ber_flush, ber_flush2, ber_printf, ber_put_int, ber_put_enum, ber_put_ostring, ber_put_string, ber_put_null, ber_put_boolean, ber_put_bitstring, ber_start_seq, ber_start_set, ber_put_seq, ber_put_set \- OpenLDAP LBER simplified Basic Encoding Rules library routines for encoding
+.SH LIBRARY
+OpenLDAP LBER (liblber, \-llber)
+.SH SYNOPSIS
+.B #include <lber.h>
+.LP
+.BI "BerElement *ber_alloc_t(int " options ");"
+.LP
+.BI "int ber_flush(Sockbuf *" sb ", BerElement *" ber ", int " freeit ");"
+.LP
+.BI "int ber_flush2(Sockbuf *" sb ", BerElement *" ber ", int " freeit ");"
+.LP
+.BI "int ber_printf(BerElement *" ber ", const char *" fmt ", ...);"
+.LP
+.BI "int ber_put_int(BerElement *" ber ", ber_int_t " num ", ber_tag_t " tag ");"
+.LP
+.BI "int ber_put_enum(BerElement *" ber ", ber_int_t " num ", ber_tag_t " tag ");"
+.LP
+.BI "int ber_put_ostring(BerElement *" ber ", const char *" str ", ber_len_t " len ", ber_tag_t " tag ");"
+.LP
+.BI "int ber_put_string(BerElement *" ber ", const char *" str ", ber_tag_t " tag ");"
+.LP
+.BI "int ber_put_null(BerElement *" ber ", ber_tag_t " tag ");"
+.LP
+.BI "int ber_put_boolean(BerElement *" ber ", ber_int_t " bool ", ber_tag_t " tag ");"
+.LP
+.BI "int ber_put_bitstring(BerElement *" ber ", const char *" str ", ber_len_t " blen ", ber_tag_t " tag ");"
+.LP
+.BI "int ber_start_seq(BerElement *" ber ", ber_tag_t " tag ");"
+.LP
+.BI "int ber_start_set(BerElement *" ber ", ber_tag_t " tag ");"
+.LP
+.BI "int ber_put_seq(BerElement *" ber ");"
+.LP
+.BI "int ber_put_set(BerElement *" ber ");"
+.SH DESCRIPTION
+.LP
+These routines provide a subroutine interface to a simplified
+implementation of the Basic Encoding Rules of ASN.1. The version
+of BER these routines support is the one defined for the LDAP
+protocol. The encoding rules are the same as BER, except that
+only definite form lengths are used, and bitstrings and octet strings
+are always encoded in primitive form. This
+man page describes the encoding routines in the lber library. See
+.BR lber-decode (3)
+for details on the corresponding decoding routines. Consult
+.BR lber-types (3)
+for information about types, allocators, and deallocators.
+.LP
+Normally, the only routines that need to be called by an application
+are
+.BR ber_alloc_t ()
+to allocate a BER element for encoding,
+.BR ber_printf ()
+to do the actual encoding, and
+.BR ber_flush2 ()
+to actually write the element. The other routines are provided for those
+applications that need more control than
+.BR ber_printf ()
+provides. In
+general, these routines return the length of the element encoded, or
+\-1 if an error occurred.
+.LP
+The
+.BR ber_alloc_t ()
+routine is used to allocate a new BER element. It
+should be called with an argument of LBER_USE_DER.
+.LP
+The
+.BR ber_flush2 ()
+routine is used to actually write the element to a socket
+(or file) descriptor, once it has been fully encoded (using
+.BR ber_printf ()
+and friends). See
+.BR lber-sockbuf (3)
+for more details on the Sockbuf implementation of the \fIsb\fP parameter.
+If the \fIfreeit\fP parameter is non-zero, the supplied \fIber\fP will
+be freed.
+If \fILBER_FLUSH_FREE_ON_SUCCESS\fP is used, the \fIber\fP is only freed
+when successfully flushed, otherwise it is left intact;
+if \fILBER_FLUSH_FREE_ON_ERROR\fP is used, the \fIber\fP is only freed
+when an error occurs, otherwise it is left intact;
+if \fILBER_FLUSH_FREE_ALWAYS\fP is used, the \fIber\fP is freed anyway.
+This function differs from the original
+.BR ber_flush (3)
+function, whose behavior corresponds to that indicated
+for \fILBER_FLUSH_FREE_ON_SUCCESS\fP.
+Note that in the future, the behavior of
+.BR ber_flush (3)
+with \fIfreeit\fP non-zero might change into that of
+.BR ber_flush2 (3)
+with \fIfreeit\fP set to \fILBER_FLUSH_FREE_ALWAYS\fP.
+.LP
+The
+.BR ber_printf ()
+routine is used to encode a BER element in much the same way that
+.BR sprintf (3)
+works. One important difference, though, is
+that some state information is kept with the \fIber\fP parameter so
+that multiple calls can be made to
+.BR ber_printf ()
+to append things to the end of the BER element.
+.BR Ber_printf ()
+writes to \fIber\fP, a pointer to a BerElement such as returned by
+.BR ber_alloc_t ().
+It interprets and
+formats its arguments according to the format string \fIfmt\fP.
+The format string can contain the following characters:
+.RS
+.LP
+.TP 3
+.B b
+Boolean. An ber_int_t parameter should be supplied. A boolean element
+is output.
+.TP
+.B e
+Enumeration. An ber_int_t parameter should be supplied. An
+enumeration element is output.
+.TP
+.B i
+Integer. An ber_int_t parameter should be supplied. An integer element
+is output.
+.TP
+.B B
+Bitstring. A char * pointer to the start of the bitstring is supplied,
+followed by the number of bits in the bitstring. A bitstring element
+is output.
+.TP
+.B n
+Null. No parameter is required. A null element is output.
+.TP
+.B o
+Octet string. A char * is supplied, followed by the length of the
+string pointed to. An octet string element is output.
+.TP
+.B O
+Octet string. A struct berval * is supplied.
+An octet string element is output.
+.TP
+.B s
+Octet string. A null-terminated string is supplied. An octet string
+element is output, not including the trailing NULL octet.
+.TP
+.B t
+Tag. A ber_tag_t specifying the tag to give the next element
+is provided. This works across calls.
+.TP
+.B v
+Several octet strings. A null-terminated array of char *'s is
+supplied. Note that a construct like '{v}' is required to get
+an actual SEQUENCE OF octet strings.
+.TP
+.B V
+Several octet strings. A null-terminated array of struct berval *'s
+is supplied. Note that a construct like '{V}' is required to get
+an actual SEQUENCE OF octet strings.
+.TP
+.B W
+Several octet strings. An array of struct berval's is supplied. The
+array is terminated by a struct berval with a NULL bv_val.
+Note that a construct like '{W}' is required to get
+an actual SEQUENCE OF octet strings.
+.TP
+.B {
+Begin sequence. No parameter is required.
+.TP
+.B }
+End sequence. No parameter is required.
+.TP
+.B [
+Begin set. No parameter is required.
+.TP
+.B ]
+End set. No parameter is required.
+.RE
+.LP
+The
+.BR ber_put_int ()
+routine writes the integer element \fInum\fP to the BER element \fIber\fP.
+.LP
+The
+.BR ber_put_enum ()
+routine writes the enumeration element \fInum\fP to the BER element \fIber\fP.
+.LP
+The
+.BR ber_put_boolean ()
+routine writes the boolean value given by \fIbool\fP to the BER element.
+.LP
+The
+.BR ber_put_bitstring ()
+routine writes \fIblen\fP bits starting
+at \fIstr\fP as a bitstring value to the given BER element. Note
+that \fIblen\fP is the length \fIin bits\fP of the bitstring.
+.LP
+The
+.BR ber_put_ostring ()
+routine writes \fIlen\fP bytes starting at
+\fIstr\fP to the BER element as an octet string.
+.LP
+The
+.BR ber_put_string ()
+routine writes the null-terminated string (minus
+the terminating '\0') to the BER element as an octet string.
+.LP
+The
+.BR ber_put_null ()
+routine writes a NULL element to the BER element.
+.LP
+The
+.BR ber_start_seq ()
+routine is used to start a sequence in the BER element. The
+.BR ber_start_set ()
+routine works similarly.
+The end of the sequence or set is marked by the nearest matching call to
+.BR ber_put_seq ()
+or
+.BR ber_put_set (),
+respectively.
+.SH EXAMPLES
+Assuming the following variable declarations, and that the variables
+have been assigned appropriately, an lber encoding of
+the following ASN.1 object:
+.LP
+.nf
+ AlmostASearchRequest := SEQUENCE {
+ baseObject DistinguishedName,
+ scope ENUMERATED {
+ baseObject (0),
+ singleLevel (1),
+ wholeSubtree (2)
+ },
+ derefAliases ENUMERATED {
+ neverDerefaliases (0),
+ derefInSearching (1),
+ derefFindingBaseObj (2),
+ alwaysDerefAliases (3)
+ },
+ sizelimit INTEGER (0 .. 65535),
+ timelimit INTEGER (0 .. 65535),
+ attrsOnly BOOLEAN,
+ attributes SEQUENCE OF AttributeType
+ }
+.fi
+.LP
+can be achieved like so:
+.LP
+.nf
+ int rc;
+ ber_int_t scope, ali, size, time, attrsonly;
+ char *dn, **attrs;
+ BerElement *ber;
+
+ /* ... fill in values ... */
+
+ ber = ber_alloc_t( LBER_USE_DER );
+
+ if ( ber == NULL ) {
+ /* error */
+ }
+
+ rc = ber_printf( ber, "{siiiib{v}}", dn, scope, ali,
+ size, time, attrsonly, attrs );
+
+ if( rc == \-1 ) {
+ /* error */
+ } else {
+ /* success */
+ }
+.fi
+.SH ERRORS
+If an error occurs during encoding, generally these routines return \-1.
+.LP
+.SH NOTES
+.LP
+The return values for all of these functions are declared in the
+<lber.h> header file.
+.SH SEE ALSO
+.BR lber-decode (3),
+.BR lber-memory (3),
+.BR lber-sockbuf (3),
+.BR lber-types (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/lber-encode.3.links b/doc/man/man3/lber-encode.3.links
new file mode 100644
index 0000000..54cd0e9
--- /dev/null
+++ b/doc/man/man3/lber-encode.3.links
@@ -0,0 +1,11 @@
+ber_alloc_t.3
+ber_flush.3
+ber_printf.3
+ber_put_int.3
+ber_put_ostring.3
+ber_put_string.3
+ber_put_null.3
+ber_put_enum.3
+ber_start_set.3
+ber_put_seq.3
+ber_put_set.3
diff --git a/doc/man/man3/lber-memory.3 b/doc/man/man3/lber-memory.3
new file mode 100644
index 0000000..70679b5
--- /dev/null
+++ b/doc/man/man3/lber-memory.3
@@ -0,0 +1,49 @@
+.TH LBER_MEMORY 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ber_memalloc, ber_memcalloc, ber_memrealloc, ber_memfree, ber_memvfree \- OpenLDAP LBER memory allocators
+.SH LIBRARY
+OpenLDAP LBER (liblber, \-llber)
+.SH SYNOPSIS
+.B #include <lber.h>
+.LP
+.BI "void *ber_memalloc(ber_len_t " bytes ");"
+.LP
+.BI "void *ber_memcalloc(ber_len_t " nelems ", ber_len_t " bytes ");"
+.LP
+.BI "void *ber_memrealloc(void *" ptr ", ber_len_t " bytes ");"
+.LP
+.BI "void ber_memfree(void *" ptr ");"
+.LP
+.BI "void ber_memvfree(void **" vec ");"
+.SH DESCRIPTION
+.LP
+These routines are used to allocate/deallocate memory used/returned
+by the Lightweight BER library as required by
+.BR lber-encode (3)
+and
+.BR lber-decode (3).
+.BR ber_memalloc (),
+.BR ber_memcalloc (),
+.BR ber_memrealloc (),
+and
+.BR ber_memfree ()
+are used exactly like the standard
+.BR malloc (3),
+.BR calloc (3),
+.BR realloc (3),
+and
+.BR free (3)
+routines, respectively. The
+.BR ber_memvfree ()
+routine is used to free a dynamically allocated array of pointers to
+arbitrary dynamically allocated objects.
+.SH SEE ALSO
+.BR lber-decode (3),
+.BR lber-encode (3),
+.BR lber-types (3)
+.LP
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/lber-sockbuf.3 b/doc/man/man3/lber-sockbuf.3
new file mode 100644
index 0000000..383ccda
--- /dev/null
+++ b/doc/man/man3/lber-sockbuf.3
@@ -0,0 +1,199 @@
+.TH LBER_SOCKBUF 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ber_sockbuf_alloc, ber_sockbuf_free, ber_sockbuf_ctrl, ber_sockbuf_add_io, ber_sockbuf_remove_io, Sockbuf_IO \- OpenLDAP LBER I/O infrastructure
+.SH LIBRARY
+OpenLDAP LBER (liblber, \-llber)
+.SH SYNOPSIS
+.B #include <lber.h>
+.LP
+.B Sockbuf *ber_sockbuf_alloc( void );
+.LP
+.BI "void ber_sockbuf_free(Sockbuf *" sb ");"
+.LP
+.BI "int ber_sockbuf_ctrl(Sockbuf *" sb ", int " opt ", void *" arg ");"
+.LP
+.BI "int ber_sockbuf_add_io(Sockbuf *" sb ", Sockbuf_IO *" sbio ", int " layer ", void *" arg ");"
+.LP
+.BI "int ber_sockbuf_remove_io(Sockbuf *" sb ", Sockbuf_IO *" sbio ", int " layer ");"
+.LP
+.nf
+.B typedef struct sockbuf_io_desc {
+.BI "int " sbiod_level ";"
+.BI "Sockbuf *" sbiod_sb ";"
+.BI "Sockbuf_IO *" sbiod_io ";"
+.BI "void *" sbiod_pvt ";"
+.BI "struct sockbuf_io_desc *" sbiod_next ";"
+.B } Sockbuf_IO_Desc;
+.LP
+.B typedef struct sockbuf_io {
+.BI "int (*" sbi_setup ")(Sockbuf_IO_Desc *" sbiod ", void *" arg ");"
+.BI "int (*" sbi_remove ")(Sockbuf_IO_Desc *" sbiod ");"
+.BI "int (*" sbi_ctrl ")(Sockbuf_IO_Desc *" sbiod ", int " opt ", void *" arg ");"
+.BI "ber_slen_t (*" sbi_read ")(Sockbuf_IO_Desc *" sbiod ", void *" buf ", ber_len_t " len ");"
+.BI "ber_slen_t (*" sbi_write ")(Sockbuf_IO_Desc *" sbiod ", void *" buf ", ber_len_t " len ");"
+.BI "int (*" sbi_close ")(Sockbuf_IO_Desc *" sbiod ");"
+.B } Sockbuf_IO;
+
+.SH DESCRIPTION
+.LP
+These routines are used to manage the low level I/O operations performed
+by the Lightweight BER library. They are called implicitly by the other
+libraries and usually do not need to be called directly from applications.
+The I/O framework is modularized and new transport layers can be supported
+by appropriately defining a
+.B Sockbuf_IO
+structure and installing it onto an existing
+.BR Sockbuf .
+.B Sockbuf
+structures are allocated and freed by
+.BR ber_sockbuf_alloc ()
+and
+.BR ber_sockbuf_free (),
+respectively. The
+.BR ber_sockbuf_ctrl ()
+function is used to get and set options related to a
+.B Sockbuf
+or to a specific I/O layer of the
+.BR Sockbuf .
+The
+.BR ber_sockbuf_add_io ()
+and
+.BR ber_sockbuf_remove_io ()
+functions are used to add and remove specific I/O layers on a
+.BR Sockbuf .
+
+Options for
+.BR ber_sockbuf_ctrl ()
+include:
+.TP
+.B LBER_SB_OPT_HAS_IO
+Takes a
+.B Sockbuf_IO *
+argument and returns 1 if the given handler is installed
+on the
+.BR Sockbuf ,
+otherwise returns 0.
+.TP
+.B LBER_SB_OPT_GET_FD
+Retrieves the file descriptor associated to the
+.BR Sockbuf ;
+.B arg
+must be a
+.BR "ber_socket_t *" .
+The return value will be 1 if a valid descriptor was present, \-1 otherwise.
+.TP
+.B LBER_SB_OPT_SET_FD
+Sets the file descriptor of the
+.B Sockbuf
+to the descriptor pointed to by
+.BR arg ;
+.B arg
+must be a
+.BR "ber_socket_t *" .
+The return value will always be 1.
+.TP
+.B LBER_SB_OPT_SET_NONBLOCK
+Toggles the non-blocking state of the file descriptor associated to
+the
+.BR Sockbuf .
+.B arg
+should be NULL to disable and non-NULL to enable the non-blocking state.
+The return value will be 1 for success, \-1 otherwise.
+.TP
+.B LBER_SB_OPT_DRAIN
+Flush (read and discard) all available input on the
+.BR Sockbuf .
+The return value will be 1.
+.TP
+.B LBER_SB_OPT_NEEDS_READ
+Returns non-zero if input is waiting to be read.
+.TP
+.B LBER_SB_OPT_NEEDS_WRITE
+Returns non-zero if the
+.B Sockbuf
+is ready to be written.
+.TP
+.B LBER_SB_OPT_GET_MAX_INCOMING
+Returns the maximum allowed size of an incoming message;
+.B arg
+must be a
+.BR "ber_len_t *" .
+The return value will be 1.
+.TP
+.B LBER_SB_OPT_SET_MAX_INCOMING
+Sets the maximum allowed size of an incoming message;
+.B arg
+must be a
+.BR "ber_len_t *" .
+The return value will be 1.
+
+.LP
+Options not in this list will be passed down to each
+.B Sockbuf_IO
+handler in turn until one of them processes it. If the option is not handled
+.BR ber_sockbuf_ctrl ()
+will return 0.
+
+.LP
+Multiple
+.B Sockbuf_IO
+handlers can be stacked in multiple layers to provide various functionality.
+Currently defined layers include
+.TP
+.B LBER_SBIOD_LEVEL_PROVIDER
+the lowest layer, talking directly to a network
+.TP
+.B LBER_SBIOD_LEVEL_TRANSPORT
+an intermediate layer
+.TP
+.B LBER_SBIOD_LEVEL_APPLICATION
+a higher layer
+.LP
+Currently defined
+.B Sockbuf_IO
+handlers in liblber include
+.TP
+.B ber_sockbuf_io_tcp
+The default stream-oriented provider
+.TP
+.B ber_sockbuf_io_fd
+A stream-oriented provider for local IPC sockets
+.TP
+.B ber_sockbuf_io_dgram
+A datagram-oriented provider. This handler is only present if the liblber
+library was built with LDAP_CONNECTIONLESS defined.
+.TP
+.B ber_sockbuf_io_readahead
+A buffering layer, usually used with a datagram provider to hide the
+datagram semantics from upper layers.
+.TP
+.B ber_sockbuf_io_debug
+A generic handler that outputs hex dumps of all traffic. This handler
+may be inserted multiple times at arbitrary layers to show the flow
+of data between other handlers.
+.LP
+Additional handlers may be present in libldap if support for them was
+enabled:
+.TP
+.B ldap_pvt_sockbuf_io_sasl
+An application layer handler for SASL encoding/decoding.
+.TP
+.B sb_tls_sbio
+A transport layer handler for SSL/TLS encoding/decoding. Note that this
+handler is private to the library and is not exposed in the API.
+.LP
+The provided handlers are all instantiated implicitly by libldap, and
+applications generally will not need to directly manipulate them.
+
+.SH SEE ALSO
+.BR lber-decode (3),
+.BR lber-encode (3),
+.BR lber-types (3),
+.BR ldap_get_option (3)
+
+.LP
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/lber-types.3 b/doc/man/man3/lber-types.3
new file mode 100644
index 0000000..29cfc2c
--- /dev/null
+++ b/doc/man/man3/lber-types.3
@@ -0,0 +1,188 @@
+.TH LBER_TYPES 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ber_int_t, ber_uint_t, ber_len_t, ber_slen_t, ber_tag_t, struct berval, BerValue, BerVarray, BerElement, ber_bvfree, ber_bvecfree, ber_bvecadd, ber_bvarray_free, ber_bvarray_add, ber_bvdup, ber_dupbv, ber_bvstr, ber_bvstrdup, ber_str2bv, ber_alloc_t, ber_init, ber_init2, ber_free \- OpenLDAP LBER types and allocation functions
+.SH LIBRARY
+OpenLDAP LBER (liblber, \-llber)
+.SH SYNOPSIS
+.B #include <lber.h>
+.LP
+.nf
+.ft B
+typedef impl_tag_t ber_tag_t;
+typedef impl_int_t ber_int_t;
+typedef impl_uint_t ber_uint_t;
+typedef impl_len_t ber_len_t;
+typedef impl_slen_t ber_slen_t;
+
+typedef struct berval {
+ ber_len_t bv_len;
+ char *bv_val;
+} BerValue, *BerVarray;
+
+typedef struct berelement BerElement;
+.ft
+.fi
+.LP
+.BI "void ber_bvfree(struct berval *" bv ");"
+.LP
+.BI "void ber_bvecfree(struct berval **" bvec ");"
+.LP
+.BI "void ber_bvecadd(struct berval ***" bvec ", struct berval *" bv ");"
+.LP
+.BI "void ber_bvarray_free(struct berval *" bvarray ");"
+.LP
+.BI "void ber_bvarray_add(BerVarray *" bvarray ", BerValue *" bv ");"
+.LP
+.BI "struct berval *ber_bvdup(const struct berval *" bv ");"
+.LP
+.BI "struct berval *ber_dupbv(const struct berval *" dst ", struct berval *" src ");"
+.LP
+.BI "struct berval *ber_bvstr(const char *" str ");"
+.LP
+.BI "struct berval *ber_bvstrdup(const char *" str ");"
+.LP
+.BI "struct berval *ber_str2bv(const char *" str ", ber_len_t " len ", int " dup ", struct berval *" bv ");"
+.LP
+.BI "BerElement *ber_alloc_t(int " options ");"
+.LP
+.BI "BerElement *ber_init(struct berval *" bv ");"
+.LP
+.BI "void ber_init2(BerElement *" ber ", struct berval *" bv ", int " options ");"
+.LP
+.BI "void ber_free(BerElement *" ber ", int " freebuf ");"
+.SH DESCRIPTION
+.LP
+The following are the basic types and structures defined for use
+with the Lightweight BER library.
+.LP
+.B ber_int_t
+is a signed integer of at least 32 bits. It is commonly equivalent to
+.BR int .
+.B ber_uint_t
+is the unsigned variant of
+.BR ber_int_t .
+.LP
+.B ber_len_t
+is an unsigned integer of at least 32 bits used to represent a length.
+It is commonly equivalent to a
+.BR size_t .
+.B ber_slen_t
+is the signed variant to
+.BR ber_len_t .
+.LP
+.B ber_tag_t
+is an unsigned integer of at least 32 bits used to represent a
+BER tag. It is commonly equivalent to a
+.BR unsigned\ long .
+.LP
+The actual definitions of the integral impl_TYPE_t types are platform
+specific.
+.LP
+.BR BerValue ,
+commonly used as
+.BR struct\ berval ,
+is used to hold an arbitrary sequence of octets.
+.B bv_val
+points to
+.B bv_len
+octets.
+.B bv_val
+is not necessarily terminated by a NULL (zero) octet.
+.BR ber_bvfree ()
+frees a BerValue, pointed to by \fIbv\fP, returned from this API. If \fIbv\fP
+is NULL, the routine does nothing.
+.LP
+.BR ber_bvecfree ()
+frees an array of BerValues (and the array), pointed to by \fIbvec\fP,
+returned from this API. If \fIbvec\fP is NULL, the routine does nothing.
+.BR ber_bvecadd ()
+appends the \fIbv\fP pointer to the \fIbvec\fP array. Space for the array
+is allocated as needed. The end of the array is marked by a NULL pointer.
+.LP
+.BR ber_bvarray_free ()
+frees an array of BerValues (and the array), pointed to by \fIbvarray\fP,
+returned from this API. If \fIbvarray\fP is NULL, the routine does nothing.
+.BR ber_bvarray_add ()
+appends the contents of the BerValue pointed to by \fIbv\fP to the
+\fIbvarray\fP array. Space for the new element is allocated as needed.
+The end of the array is marked by a BerValue with a NULL bv_val field.
+.LP
+.BR ber_bvdup ()
+returns a copy of a BerValue. The routine returns NULL upon error
+(e.g. out of memory). The caller should use
+.BR ber_bvfree ()
+to deallocate the resulting BerValue.
+.BR ber_dupbv ()
+copies a BerValue from \fIsrc\fP to \fIdst\fP. If \fIdst\fP is NULL a
+new BerValue will be allocated to hold the copy. The routine returns NULL
+upon error, otherwise it returns a pointer to the copy. If \fIdst\fP is
+NULL the caller should use
+.BR ber_bvfree ()
+to deallocate the resulting BerValue, otherwise
+.BR ber_memfree ()
+should be used to deallocate the \fIdst->bv_val\fP. (The
+.BR ber_bvdup ()
+function is internally implemented as ber_dupbv(NULL, bv).
+.BR ber_bvdup ()
+is provided only for compatibility with an expired draft of the LDAP C API;
+.BR ber_dupbv ()
+is the preferred interface.)
+.LP
+.BR ber_bvstr ()
+returns a BerValue containing the string pointed to by \fIstr\fP.
+.BR ber_bvstrdup ()
+returns a BerValue containing a copy of the string pointed to by \fIstr\fP.
+.BR ber_str2bv ()
+returns a BerValue containing the string pointed to by \fIstr\fP, whose
+length may be optionally specified in \fIlen\fP. If \fIdup\fP is non-zero,
+the BerValue will contain a copy of \fIstr\fP. If \fIlen\fP is zero, the
+number of bytes to copy will be determined by
+.BR strlen (3),
+otherwise \fIlen\fP bytes will be copied. If \fIbv\fP is non-NULL, the result
+will be stored in the given BerValue, otherwise a new BerValue will be
+allocated to store the result. NOTE: Both
+.BR ber_bvstr ()
+and
+.BR ber_bvstrdup ()
+are implemented as macros using
+.BR ber_str2bv ()
+in this version of the library.
+.LP
+.B BerElement
+is an opaque structure used to maintain state information used in
+encoding and decoding.
+.BR ber_alloc_t ()
+is used to create an empty BerElement structure. If
+.B LBER_USE_DER
+is specified for the
+.I options
+parameter then data lengths for data written to the BerElement will be
+encoded in the minimal number of octets required, otherwise they will
+always be written as four byte values.
+.BR ber_init ()
+creates a BerElement structure that is initialized with a copy of the
+data in its
+.I bv
+parameter.
+.BR ber_init2 ()
+initializes an existing BerElement
+.I ber
+using the data in the
+.I bv
+parameter. The data is referenced directly, not copied. The
+.I options
+parameter is the same as for
+.BR ber_alloc_t ().
+.BR ber_free ()
+frees a BerElement pointed to by \fIber\fP. If \fIber\fP is NULL, the routine
+does nothing. If \fIfreebuf\fP is zero, the internal buffer is not freed.
+.SH SEE ALSO
+.BR lber-encode (3),
+.BR lber-decode (3),
+.BR lber-memory (3)
+.LP
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/lber-types.3.links b/doc/man/man3/lber-types.3.links
new file mode 100644
index 0000000..89f90fb
--- /dev/null
+++ b/doc/man/man3/lber-types.3.links
@@ -0,0 +1,11 @@
+ber_bvarray_add.3
+ber_bvarray_free.3
+ber_bvdup.3
+ber_bvecadd.3
+ber_bvecfree.3
+ber_bvfree.3
+ber_bvstr.3
+ber_bvstrdup.3
+ber_dupbv.3
+ber_free.3
+ber_str2bv.3
diff --git a/doc/man/man3/ldap.3 b/doc/man/man3/ldap.3
new file mode 100644
index 0000000..25fa0f0
--- /dev/null
+++ b/doc/man/man3/ldap.3
@@ -0,0 +1,278 @@
+.TH LDAP 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap \- OpenLDAP Lightweight Directory Access Protocol API
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.ft
+.fi
+.SH DESCRIPTION
+.LP
+The Lightweight Directory Access Protocol (LDAP) (RFC 4510) provides
+access to X.500 directory services. These services may be stand\-alone
+or part of a distributed directory service. This client API supports
+LDAP over TCP (RFC 4511), LDAP over TLS/SSL, and LDAP over IPC (UNIX
+domain sockets). This API supports SASL (RFC 4513) and Start TLS
+(RFC 4513) as well as a number of protocol extensions. This API is
+loosely based upon IETF/LDAPEXT C LDAP API draft specification, a (orphaned)
+work in progress.
+.LP
+The OpenLDAP Software package includes a stand\-alone server in
+.BR slapd (8),
+various LDAP clients, and an LDAP client library used to provide
+programmatic access to the LDAP protocol. This man page gives an
+overview of the LDAP library routines.
+.LP
+Both synchronous and asynchronous APIs are provided. Also included are
+various routines to parse the results returned from these routines.
+These routines are found in the \-lldap library.
+.LP
+The basic interaction is as follows. A session handle is
+created using
+.BR ldap_initialize (3)
+and set the protocol version to 3 by calling
+.BR ldap_set_option (3).
+The underlying session is established first operation is
+issued. This would generally be a Start TLS or Bind operation,
+or a Search operation to read attributes of the Root DSE.
+A Start TLS operation is performed by calling
+.BR ldap_start_tls_s (3).
+A LDAP bind operation is performed by calling
+.BR ldap_sasl_bind (3)
+or one of its friends.
+A Search operation is performed by calling ldap_search_ext_s(3)
+or one of its friends.
+
+Subsequently, additional operations are performed
+by calling one of the synchronous or asynchronous routines (e.g.,
+.BR ldap_compare_ext_s (3)
+or
+.BR ldap_compare_ext (3)
+followed by
+.BR ldap_result (3)).
+Results returned from these routines are interpreted by calling the
+LDAP parsing routines such as
+.BR ldap_parse_result (3).
+The LDAP association and underlying connection is terminated by calling
+.BR ldap_unbind_ext (3).
+Errors can be interpreted by calling
+.BR ldap_err2string (3).
+.SH LDAP versions
+This library supports version 3 of the Lightweight Directory Access
+Protocol (LDAPv3) as defined in RFC 4510. It also supports a variant
+of version 2 of LDAP as defined by U-Mich LDAP and, to some degree,
+RFC 1777. Version 2 (all variants) are considered obsolete.
+Version 3 should be used instead.
+.LP
+For backwards compatibility reasons, the library defaults to version 2.
+Hence, all new applications (and all actively maintained applications)
+should use
+.BR ldap_set_option (3)
+to select version 3. The library manual pages assume version 3
+has been selected.
+.SH INPUT and OUTPUT PARAMETERS
+All character string input/output is expected to be/is UTF-8
+encoded Unicode (version 3.2).
+.LP
+Distinguished names (DN) (and relative distinguished names (RDN) to
+be passed to the LDAP routines should conform to RFC 4514 UTF-8
+string representation.
+.LP
+Search filters to be passed to the search routines are to be
+constructed by hand and should conform to RFC 4515 UTF-8
+string representation.
+.LP
+LDAP URLs to be passed to routines are expected to conform
+to RFC 4516 format. The
+.BR ldap_url (3)
+routines can be used to work with LDAP URLs.
+.LP
+LDAP controls to be passed to routines can be manipulated using the
+.BR ldap_controls (3)
+routines.
+.SH DISPLAYING RESULTS
+Results obtained from the search routines can be output by hand,
+by calling
+.BR ldap_first_entry (3)
+and
+.BR ldap_next_entry (3)
+to step through
+the entries returned,
+.BR ldap_first_attribute (3)
+and
+.BR ldap_next_attribute (3)
+to step through an entry's attributes, and
+.BR ldap_get_values (3)
+to retrieve a given attribute's values. Attribute values
+may or may not be displayable.
+.SH UTILITY ROUTINES
+Also provided are various utility routines. The
+.BR ldap_sort (3)
+routines are used to sort the entries and values returned via
+the ldap search routines.
+.SH DEPRECATED INTERFACES
+A number of interfaces are now considered deprecated. For instance,
+ldap_add(3) is deprecated in favor of ldap_add_ext(3).
+.so Deprecated
+.SH BER LIBRARY
+Also included in the distribution is a set of lightweight Basic
+Encoding Rules routines. These routines are used by the LDAP library
+routines to encode and decode LDAP protocol elements using the
+(slightly simplified) Basic Encoding Rules defined by LDAP. They are
+not normally used directly by an LDAP application program except
+in the handling of controls and extended operations. The
+routines provide a printf and scanf\-like interface, as well as
+lower\-level access. These routines are discussed in
+.BR lber\-decode (3),
+.BR lber\-encode (3),
+.BR lber\-memory (3),
+and
+.BR lber\-types (3).
+.SH INDEX
+.TP 20
+.SM ldap_initialize(3)
+initialize the LDAP library without opening a connection to a server
+.TP
+.SM ldap_result(3)
+wait for the result from an asynchronous operation
+.TP
+.SM ldap_abandon_ext(3)
+abandon (abort) an asynchronous operation
+.TP
+.SM ldap_add_ext(3)
+asynchronously add an entry
+.TP
+.SM ldap_add_ext_s(3)
+synchronously add an entry
+.TP
+.SM ldap_sasl_bind(3)
+asynchronously bind to the directory
+.TP
+.SM ldap_sasl_bind_s(3)
+synchronously bind to the directory
+.TP
+.SM ldap_unbind_ext(3)
+synchronously unbind from the LDAP server and close the connection
+.TP
+.SM ldap_unbind(3) and ldap_unbind_s(3) are
+equivalent to
+.BR ldap_unbind_ext (3)
+.TP
+.SM ldap_memfree(3)
+dispose of memory allocated by LDAP routines.
+.TP
+.SM ldap_compare_ext(3)
+asynchronously compare to a directory entry
+.TP
+.SM ldap_compare_ext_s(3)
+synchronously compare to a directory entry
+.TP
+.SM ldap_delete_ext(3)
+asynchronously delete an entry
+.TP
+.SM ldap_delete_ext_s(3)
+synchronously delete an entry
+.TP
+.SM ld_errno(3)
+LDAP error indication
+.TP
+.SM ldap_errlist(3)
+list of LDAP errors and their meanings
+.TP
+.SM ldap_err2string(3)
+convert LDAP error indication to a string
+.TP
+.SM ldap_extended_operation(3)
+asynchronously perform an arbitrary extended operation
+.TP
+.SM ldap_extended_operation_s(3)
+synchronously perform an arbitrary extended operation
+.TP
+.SM ldap_first_attribute(3)
+return first attribute name in an entry
+.TP
+.SM ldap_next_attribute(3)
+return next attribute name in an entry
+.TP
+.SM ldap_first_entry(3)
+return first entry in a chain of search results
+.TP
+.SM ldap_next_entry(3)
+return next entry in a chain of search results
+.TP
+.SM ldap_count_entries(3)
+return number of entries in a search result
+.TP
+.SM ldap_get_dn(3)
+extract the DN from an entry
+.TP
+.SM ldap_get_values_len(3)
+return an attribute's values with lengths
+.TP
+.SM ldap_value_free_len(3)
+free memory allocated by ldap_get_values_len(3)
+.TP
+.SM ldap_count_values_len(3)
+return number of values
+.TP
+.SM ldap_modify_ext(3)
+asynchronously modify an entry
+.TP
+.SM ldap_modify_ext_s(3)
+synchronously modify an entry
+.TP
+.SM ldap_mods_free(3)
+free array of pointers to mod structures used by ldap_modify_ext(3)
+.TP
+.SM ldap_rename(3)
+asynchronously rename an entry
+.TP
+.SM ldap_rename_s(3)
+synchronously rename an entry
+.TP
+.SM ldap_msgfree(3)
+free results allocated by ldap_result(3)
+.TP
+.SM ldap_msgtype(3)
+return the message type of a message from ldap_result(3)
+.TP
+.SM ldap_msgid(3)
+return the message id of a message from ldap_result(3)
+.TP
+.SM ldap_search_ext(3)
+asynchronously search the directory
+.TP
+.SM ldap_search_ext_s(3)
+synchronously search the directory
+.TP
+.SM ldap_is_ldap_url(3)
+check a URL string to see if it is an LDAP URL
+.TP
+.SM ldap_url_parse(3)
+break up an LDAP URL string into its components
+.TP
+.SM ldap_sort_entries(3)
+sort a list of search results
+.TP
+.SM ldap_sort_values(3)
+sort a list of attribute values
+.TP
+.SM ldap_sort_strcasecmp(3)
+case insensitive string comparison
+.SH SEE ALSO
+.BR ldap.conf (5),
+.BR slapd (8),
+.BR draft-ietf-ldapext-ldap-c-api-xx.txt \ <http://www.ietf.org>
+.SH ACKNOWLEDGEMENTS
+.so ../Project
+.LP
+These API manual pages are loosely based upon descriptions provided
+in the IETF/LDAPEXT C LDAP API Internet Draft, a (orphaned) work
+in progress.
+
diff --git a/doc/man/man3/ldap_abandon.3 b/doc/man/man3/ldap_abandon.3
new file mode 100644
index 0000000..7beb37a
--- /dev/null
+++ b/doc/man/man3/ldap_abandon.3
@@ -0,0 +1,69 @@
+.TH LDAP_ABANDON 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_abandon_ext \- Abandon an LDAP operation in progress
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.B
+#include <ldap.h>
+.LP
+.ft B
+int ldap_abandon_ext(
+.RS
+.ft B
+LDAP *\fIld\fB,
+Bint \fImsgid\fB,
+LDAPControl **\fIsctrls\fB,
+LDAPControl **\fIcctrls\fB );
+.RE
+.fi
+.SH DESCRIPTION
+The
+.B ldap_abandon_ext()
+routine is used to send a LDAP Abandon request for an
+operation in progress. The \fImsgid\fP passed should be the
+message id of an outstanding LDAP operation, such as returned by
+.BR ldap_search_ext (3).
+.LP
+.BR ldap_abandon_ext ()
+checks to see if the result of the operation has already come in. If it
+has, it deletes it from the queue of pending messages. If not,
+it sends an LDAP abandon request to the LDAP server.
+.LP
+The caller can expect that the result of an abandoned operation
+will not be returned from a future call to
+.BR ldap_result (3).
+.LP
+.B ldap_abandon_ext()
+allows server and client controls to be passed in via the
+.I sctrls
+and
+.I cctrls
+parameters, respectively.
+.LP
+.B ldap_abandon_ext()
+returns a code indicating success or, in the case of failure, the
+nature of the failure. See
+.BR ldap_error (3)
+for details.
+.SH DEPRECATED INTERFACES
+The
+.B ldap_abandon()
+routine is deprecated in favor of the
+.B ldap_abandon_ext()
+routine.
+.LP
+.so Deprecated
+
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_error (3),
+.BR ldap_result (3),
+.BR ldap_search_ext (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
+
diff --git a/doc/man/man3/ldap_abandon.3.links b/doc/man/man3/ldap_abandon.3.links
new file mode 100644
index 0000000..3b7bc3f
--- /dev/null
+++ b/doc/man/man3/ldap_abandon.3.links
@@ -0,0 +1 @@
+ldap_abandon_ext.3
diff --git a/doc/man/man3/ldap_add.3 b/doc/man/man3/ldap_add.3
new file mode 100644
index 0000000..9fdc695
--- /dev/null
+++ b/doc/man/man3/ldap_add.3
@@ -0,0 +1,81 @@
+.TH LDAP_ADD 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_add_ext, ldap_add_ext_s \- Perform an LDAP add operation
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+.nf
+int ldap_add_ext(
+.RS
+.ft B
+LDAP *\fIld,
+const char *\fIdn\fB,
+LDAPMod **\fIattrs\fB,
+LDAPControl **\fIsctrls\fB,
+LDAPControl **\fIcctrls\fB,
+int *\fImsgidp\fB );
+.RE
+.LP
+.ft B
+.nf
+int ldap_add_ext_s(
+.RS
+LDAP *\fIld\fB,
+const char *\fIdn\fB,
+LDAPMod **\fIattrs\fB,
+LDAPControl *\fIsctrls\fB,
+LDAPControl *\fIcctrls\fB );
+.RE
+.fi
+.SH DESCRIPTION
+The
+.B ldap_add_ext_s()
+routine is used to perform an LDAP add operation.
+It takes \fIdn\fP, the DN of the entry to add, and \fIattrs\fP, a
+null-terminated array of the entry's attributes. The LDAPMod structure
+is used to represent attributes, with the \fImod_type\fP and
+\fImod_values\fP fields being used as described under
+.BR ldap_modify_ext (3),
+and the \fIldap_op\fP field being used only if you need to specify
+the LDAP_MOD_BVALUES option. Otherwise, it should be set to zero.
+.LP
+Note that all entries except that
+specified by the last component in the given DN must already exist.
+.B ldap_add_ext_s()
+returns an code indicating success or, in the case of failure,
+indicating the nature of failure of the operation. See
+.BR ldap_error (3)
+for more details.
+.LP
+The
+.B ldap_add_ext()
+routine works just like
+.BR ldap_add_ext_s() ,
+but it is asynchronous. It returns the message id of the request it
+initiated. The result of this operation can be obtained by calling
+.BR ldap_result (3).
+.SH DEPRECATED INTERFACES
+The
+.BR ldap_add ()
+and
+.BR ldap_add_s ()
+routines are deprecated in favor of the
+.BR ldap_add_ext ()
+and
+.BR ldap_add_ext_s ()
+routines, respectively.
+.LP
+.so Deprecated
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_error (3),
+.BR ldap_modify (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_add.3.links b/doc/man/man3/ldap_add.3.links
new file mode 100644
index 0000000..8114ef1
--- /dev/null
+++ b/doc/man/man3/ldap_add.3.links
@@ -0,0 +1,3 @@
+ldap_add_s.3
+ldap_add_ext.3
+ldap_add_ext_s.3
diff --git a/doc/man/man3/ldap_bind.3 b/doc/man/man3/ldap_bind.3
new file mode 100644
index 0000000..7b9e2de
--- /dev/null
+++ b/doc/man/man3/ldap_bind.3
@@ -0,0 +1,334 @@
+.TH LDAP_BIND 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_bind, ldap_bind_s, ldap_simple_bind, ldap_simple_bind_s, ldap_sasl_bind, ldap_sasl_bind_s, ldap_sasl_interactive_bind_s, ldap_parse_sasl_bind_result, ldap_unbind, ldap_unbind_s, ldap_unbind_ext, ldap_unbind_ext_s, ldap_set_rebind_proc \- LDAP bind routines
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.B #include <ldap.h>
+.LP
+.BI "int ldap_bind(LDAP *" ld ", const char *" who ", const char *" cred ","
+.RS
+.BI "int " method ");"
+.RE
+.LP
+.BI "int ldap_bind_s(LDAP *" ld ", const char *" who ", const char *" cred ","
+.RS
+.BI "int " method ");"
+.RE
+.LP
+.BI "int ldap_simple_bind(LDAP *" ld ", const char *" who ", const char *" passwd ");"
+.LP
+.BI "int ldap_simple_bind_s(LDAP *" ld ", const char *" who ", const char *" passwd ");"
+.LP
+.BI "int ldap_sasl_bind(LDAP *" ld ", const char *" dn ", const char *" mechanism ","
+.RS
+.BI "struct berval *" cred ", LDAPControl *" sctrls "[],"
+.BI "LDAPControl *" cctrls "[], int *" msgidp ");"
+.RE
+.LP
+.BI "int ldap_sasl_bind_s(LDAP *" ld ", const char *" dn ", const char *" mechanism ","
+.RS
+.BI "struct berval *" cred ", LDAPControl *" sctrls "[],"
+.BI "LDAPControl *" cctrls "[], struct berval **" servercredp ");"
+.RE
+.LP
+.BI "int ldap_parse_sasl_bind_result(LDAP *" ld ", LDAPMessage *" res ","
+.RS
+.BI "struct berval **" servercredp ", int " freeit ");"
+.RE
+.LP
+.BI "int ldap_sasl_interactive_bind_s(LDAP *" ld ", const char *" dn ","
+.RS
+.BI "const char *" mechs ","
+.BI "LDAPControl *" sctrls "[], LDAPControl *" cctrls "[],"
+.BI "unsigned " flags ", LDAP_SASL_INTERACT_PROC *" interact ","
+.BI "void *" defaults ");"
+.RE
+.LP
+.BI "int ldap_sasl_interactive_bind(LDAP *" ld ", const char *" dn ","
+.RS
+.BI "const char *" mechs ","
+.BI "LDAPControl *" sctrls "[], LDAPControl *" cctrls "[],"
+.BI "unsigned " flags ", LDAP_SASL_INTERACT_PROC *" interact ","
+.BI "void *" defaults ", LDAPMessage *" result ","
+.BI "const char **" rmechp ", int *" msgidp ");"
+.RE
+.LP
+.BI "int (LDAP_SASL_INTERACT_PROC)(LDAP *" ld ", unsigned " flags ", void *" defaults ", void *" sasl_interact ");"
+.LP
+.BI "int ldap_unbind(LDAP *" ld ");"
+.LP
+.BI "int ldap_unbind_s(LDAP *" ld ");"
+.LP
+.BI "int ldap_unbind_ext(LDAP *" ld ", LDAPControl *" sctrls "[],"
+.RS
+.BI "LDAPControl *" cctrls "[]);"
+.RE
+.LP
+.BI "int ldap_unbind_ext_s(LDAP *" ld ", LDAPControl *" sctrls "[],"
+.RS
+.BI "LDAPControl *" cctrls "[]);"
+.RE
+.LP
+.BI "int ldap_set_rebind_proc (LDAP *" ld ", LDAP_REBIND_PROC *" ldap_proc ", void *" params ");"
+.LP
+.BI "int (LDAP_REBIND_PROC)(LDAP *" ld ", LDAP_CONST char *" url ", ber_tag_t " request ", ber_int_t " msgid ", void *" params ");"
+.SH DESCRIPTION
+.LP
+These routines provide various interfaces to the LDAP bind operation.
+After an association with an LDAP server is made using
+.BR ldap_init (3),
+an LDAP bind operation should be performed before other operations are
+attempted over the connection. An LDAP bind is required when using
+Version 2 of the LDAP protocol; it is optional for Version 3 but is
+usually needed due to security considerations.
+.LP
+There are three types of bind calls, ones providing simple authentication,
+ones providing SASL authentication, and general routines capable of doing
+either simple or SASL authentication.
+.LP
+.B SASL
+(Simple Authentication and Security Layer)
+can negotiate one of many different kinds of authentication.
+Both synchronous and asynchronous versions of each variant of the bind
+call are provided. All routines
+take \fIld\fP as their first parameter, as returned from
+.BR ldap_init (3).
+.SH SIMPLE AUTHENTICATION
+The simplest form of the bind call is
+.BR ldap_simple_bind_s() .
+It takes the DN to bind as in \fIwho\fP, and the userPassword associated
+with the entry in \fIpasswd\fP. It returns an LDAP error indication
+(see
+.BR ldap_error (3)).
+The
+.B ldap_simple_bind()
+call is asynchronous,
+taking the same parameters but only initiating the bind operation and
+returning the message id of the request it sent. The result of the
+operation can be obtained by a subsequent call to
+.BR ldap_result (3).
+The
+.B ldap_sasl_bind_s()
+and asynchronous
+.B ldap_sasl_bind()
+functions can also be used to make a simple bind by using
+LDAP_SASL_SIMPLE as the SASL mechanism.
+.SH GENERAL AUTHENTICATION
+The
+.B ldap_bind()
+and
+.B ldap_bind_s()
+routines can be used when the
+authentication method to use needs to be selected at runtime. They
+both take an extra \fImethod\fP parameter selecting the authentication
+method to use. It should be set to LDAP_AUTH_SIMPLE
+to select simple authentication.
+.B ldap_bind()
+returns the message id of the request it initiates.
+.B ldap_bind_s()
+returns an LDAP error indication.
+.SH SASL AUTHENTICATION
+For SASL binds the server always ignores any provided DN, so the
+.I dn
+parameter should always be NULL.
+.BR ldap_sasl_bind_s ()
+sends a single SASL bind request with the given SASL
+.I mechanism
+and credentials in the
+.I cred
+parameter. The format of the credentials depends on the particular
+SASL mechanism in use. For mechanisms that provide mutual authentication
+the server's credentials will be returned in the
+.I servercredp
+parameter.
+The routine returns an LDAP error indication (see
+.BR ldap_error (3)).
+The
+.BR ldap_sasl_bind ()
+call is asynchronous, taking the same parameters but only sending the
+request and returning the message id of the request it sent. The result of
+the operation can be obtained by a subsequent
+call to
+.BR ldap_result (3).
+The result must be additionally parsed by
+.BR ldap_parse_sasl_bind_result ()
+to obtain any server credentials sent from the server.
+.LP
+Many SASL mechanisms require multiple message exchanges to perform a
+complete authentication. Applications should generally use
+.BR ldap_sasl_interactive_bind_s ()
+rather than calling the basic
+.BR ldap_sasl_bind ()
+functions directly. The
+.I mechs
+parameter should contain a space-separated list of candidate mechanisms
+to use. If this parameter is NULL or empty the library will query
+the supportedSASLMechanisms attribute from the server's rootDSE
+for the list of SASL mechanisms the server supports. The
+.I flags
+parameter controls the interaction used to retrieve any necessary
+SASL authentication parameters and should be one of:
+.TP
+LDAP_SASL_AUTOMATIC
+use defaults if available, prompt otherwise
+.TP
+LDAP_SASL_INTERACTIVE
+always prompt
+.TP
+LDAP_SASL_QUIET
+never prompt
+.LP
+The
+.I interact
+function uses the provided
+.I defaults
+to handle requests from the SASL library for particular authentication
+parameters. There is no defined format for the
+.I defaults
+information;
+it is up to the caller to use whatever format is appropriate for the
+supplied
+.I interact
+function.
+The
+.I sasl_interact
+parameter comes from the underlying SASL library. When used with Cyrus SASL
+this is an array of
+.B sasl_interact_t
+structures. The Cyrus SASL library will prompt for a variety of inputs,
+including:
+.TP
+SASL_CB_GETREALM
+the realm for the authentication attempt
+.TP
+SASL_CB_AUTHNAME
+the username to authenticate
+.TP
+SASL_CB_PASS
+the password for the provided username
+.TP
+SASL_CB_USER
+the username to use for proxy authorization
+.TP
+SASL_CB_NOECHOPROMPT
+generic prompt for input with input echoing disabled
+.TP
+SASL_CB_ECHOPROMPT
+generic prompt for input with input echoing enabled
+.TP
+SASL_CB_LIST_END
+indicates the end of the array of prompts
+.LP
+See the Cyrus SASL documentation for more details.
+.LP
+Applications which need to manage connections asynchronously may use
+.BR ldap_sasl_interactive_bind ()
+instead of the synchronous version.
+A valid mechs parameter must be supplied, otherwise the library will
+be forced to query the server for a list of supported mechanisms,
+and this query will be performed synchronously.
+The other parameters are the same as
+for the synchronous function, with three additional parameters.
+The actual SASL mechanism that was used, and the message ID for use
+with
+.BR ldap_result ()
+will be returned in rmechp and msgidp, respectively.
+The value in rmechp must not be modified by the caller and must be
+passed back on each subsequent call. The message obtained from
+.BR ldap_result ()
+must be passed in the result parameter.
+This parameter must be NULL when initiating a new Bind. The caller
+must free the result message after each call using
+.BR ldap_msgfree ().
+The
+.BR ldap_sasl_interactive_bind ()
+function returns an LDAP result code. If the code is
+LDAP_SASL_BIND_IN_PROGRESS then the Bind is not complete yet, and
+this function must be called again with the next result from the server.
+.SH REBINDING
+.LP
+The
+.B ldap_set_rebind_proc
+function() sets the process to use for binding when an operation returns a
+referral. This function is used when an application needs to bind to another server
+in order to follow a referral or search continuation reference.
+.LP
+The function takes \fIld\fP, the \fIrebind\fP function, and the \fIparams\fP,
+the arbitrary data like state information which the client might need to properly rebind.
+The LDAP_OPT_REFERRALS option in the \fIld\fP must be set to ON for the libraries
+to use the rebind function. Use the
+.BR ldap_set_option
+function to set the value.
+.LP
+The rebind function parameters are as follows:
+.LP
+The \fIld\fP parameter must be used by the application when binding to the
+referred server if the application wants the libraries to follow the referral.
+.LP
+The \fIurl\fP parameter points to the URL referral string received from the LDAP server.
+The LDAP application can use the
+.BR ldap_url_parse (3)
+function to parse the string into its components.
+.LP
+The \fIrequest\fP parameter specifies the type of request that generated the referral.
+.LP
+The \fImsgid\fP parameter specifies the message ID of the request generating the referral.
+.LP
+The \fIparams\fP parameter is the same value as passed originally to the
+.BR ldap_set_rebind_proc ()
+function.
+.LP
+The LDAP libraries set all the parameters when they call the rebind function. The application
+should not attempt to free either the ld or the url structures in the rebind function.
+.LP
+The application must supply to the rebind function the required authentication information such as,
+user name, password, and certificates. The rebind function must use a synchronous bind method.
+.SH UNBINDING
+The
+.B ldap_unbind()
+call is used to unbind from the directory,
+terminate the current association, and free the resources contained
+in the \fIld\fP structure. Once it is called, the connection to
+the LDAP server is closed, and the \fIld\fP structure is invalid.
+The
+.B ldap_unbind_s()
+call is just another name for
+.BR ldap_unbind() ;
+both of these calls are synchronous in nature.
+.LP
+The
+.B ldap_unbind_ext()
+and
+.B ldap_unbind_ext_s()
+allows the operations to specify controls.
+.SH ERRORS
+Asynchronous routines will return \-1 in case of error, setting the
+\fIld_errno\fP parameter of the \fIld\fP structure. Synchronous
+routines return whatever \fIld_errno\fP is set to. See
+.BR ldap_error (3)
+for more information.
+.SH NOTES
+If an anonymous bind is sufficient for the application, the rebind process
+need not be provided. The LDAP libraries with the LDAP_OPT_REFERRALS option
+set to ON (default value) will automatically follow referrals using an anonymous bind.
+.LP
+If the application needs stronger authentication than an anonymous bind,
+you need to provide a rebind process for that authentication method.
+The bind method must be synchronous.
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_error (3),
+.BR ldap_open (3),
+.BR ldap_set_option (3),
+.BR ldap_url_parse (3)
+.B RFC 4422
+(http://www.rfc-editor.org),
+.B Cyrus SASL
+(http://asg.web.cmu.edu/sasl/)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_bind.3.links b/doc/man/man3/ldap_bind.3.links
new file mode 100644
index 0000000..ffaedd5
--- /dev/null
+++ b/doc/man/man3/ldap_bind.3.links
@@ -0,0 +1,10 @@
+ldap_bind_s.3
+ldap_simple_bind.3
+ldap_simple_bind_s.3
+ldap_sasl_bind.3
+ldap_sasl_bind_s.3
+ldap_unbind.3
+ldap_unbind_ext.3
+ldap_unbind_s.3
+ldap_unbind_ext_s.3
+ldap_set_rebind_proc.3
diff --git a/doc/man/man3/ldap_compare.3 b/doc/man/man3/ldap_compare.3
new file mode 100644
index 0000000..86b3bdd
--- /dev/null
+++ b/doc/man/man3/ldap_compare.3
@@ -0,0 +1,79 @@
+.TH LDAP_COMPARE 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_compare, ldap_compare_s, ldap_compare_ext, ldap_compare_ext_s \- Perform an LDAP compare operation.
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+int ldap_compare_ext(
+.RS
+.ft B
+LDAP *\fIld\fB,
+char *\fIdn\fB,
+char *\fIattr\fB,
+const struct berval *\fIbvalue\fB,
+LDAPControl **\fIserverctrls\fB,
+LDAPControl **\fIclientctrls\fB,
+int *\fImsgidp\fB );
+.RE
+.LP
+.ft B
+int ldap_compare_ext_s(
+.RS
+.ft B
+LDAP *\fIld\fB,
+char *\fIdn\fB,
+char *\fIattr\fB,
+const struct berval *\fIbvalue\fB,
+LDAPControl **\fIserverctrls\fB,
+LDAPControl **\fIclientctrls\fB );
+.RE
+.SH DESCRIPTION
+The
+.B ldap_compare_ext_s()
+routine is used to perform an LDAP compare operation synchronously.
+It takes \fIdn\fP, the DN of the entry upon which to perform the
+compare, and \fIattr\fP and \fIvalue\fP, the attribute description and
+value to compare to those found in the entry. It returns a code, which
+will be LDAP_COMPARE_TRUE if the entry contains the attribute value and
+LDAP_COMPARE_FALSE if it does not. Otherwise, an error code is
+returned that indicates the nature of the problem. See
+.BR ldap (3)
+for details.
+.LP
+The
+.B ldap_compare_ext()
+routine is used to perform an LDAP compare operation
+asynchronously. It takes the same parameters as
+.BR ldap_compare_ext_s() ,
+but provides the message id of the request it initiated in the
+integer pointed to \fImsgidp\fP. The result of
+the compare can be obtained by a subsequent call to
+.BR ldap_result (3).
+.LP
+Both routines allow server and client controls to be specified to
+extend the compare request.
+.SH DEPRECATED INTERFACES
+The routines
+.BR ldap_compare ()
+and
+.BR ldap_compare_s ()
+are deprecated in favor of
+.BR ldap_compare_ext ()
+and
+.BR ldap_compare_ext_s (),
+respectively.
+.LP
+.so Deprecated
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_error (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_compare.3.links b/doc/man/man3/ldap_compare.3.links
new file mode 100644
index 0000000..66821cc
--- /dev/null
+++ b/doc/man/man3/ldap_compare.3.links
@@ -0,0 +1,3 @@
+ldap_compare_s.3
+ldap_compare_ext.3
+ldap_compare_ext_s.3
diff --git a/doc/man/man3/ldap_controls.3 b/doc/man/man3/ldap_controls.3
new file mode 100644
index 0000000..292bb0e
--- /dev/null
+++ b/doc/man/man3/ldap_controls.3
@@ -0,0 +1,84 @@
+.TH LDAP_CONTROLS 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_control_create, ldap_control_find, ldap_control_dup,
+ldap_controls_dup, ldap_control_free, ldap_controls_free
+\- LDAP control manipulation routines
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.B #include <ldap.h>
+.LP
+.BI "int ldap_control_create(const char *" oid ", int " iscritical ", struct berval *" value ", int " dupval ", LDAPControl **" ctrlp ");"
+.LP
+.BI "LDAPControl *ldap_control_find( const char *" oid ", LDAPControl **" ctrls ", LDAPControl ***" nextctrlp ");"
+.LP
+.BI "LDAPControl *ldap_control_dup(LDAPControl *" ctrl ");"
+.LP
+.BI "LDAPControl **ldap_controls_dup(LDAPControl **" ctrls ");"
+.LP
+.BI "void ldap_control_free(LDAPControl *" ctrl ");"
+.LP
+.BI "void ldap_controls_free(LDAPControl **" ctrls ");"
+.SH DESCRIPTION
+These routines are used to manipulate structures used for LDAP controls.
+
+.BR ldap_control_create ()
+creates a control with the specified
+.I OID
+using the contents of the
+.I value
+parameter for the control value, if any. The content of
+.I value
+is duplicated if
+.I dupval
+is non-zero. The
+.I iscritical
+parameter must be non-zero for a critical control. The created control
+is returned in the
+.I ctrlp
+parameter. The routine returns
+.B LDAP_SUCCESS
+on success or some other error code on failure.
+The content of
+.IR value ,
+for supported control types, can be prepared using helpers provided
+by this implementation of libldap, usually in the form
+.BR "ldap_create_<control name>_control_value" ().
+Otherwise, it can be BER-encoded using the functionalities of liblber.
+
+.BR ldap_control_find ()
+searches the NULL-terminated
+.I ctrls
+array for a control whose OID matches the
+.I oid
+parameter. The routine returns a pointer to the control if found,
+NULL otherwise.
+If the parameter
+.I nextctrlp
+is not NULL, on return it will point to the next control
+in the array, and can be passed to the
+.BR ldap_control_find ()
+routine for subsequent calls, to find further occurrences of the same
+control type.
+The use of this function is discouraged; the recommended way of handling
+controls in responses consists in going through the array of controls,
+dealing with each of them in the returned order, since it could matter.
+
+.BR ldap_control_dup ()
+duplicates an individual control structure, and
+.BR ldap_controls_dup ()
+duplicates a NULL-terminated array of controls.
+
+.BR ldap_control_free ()
+frees an individual control structure, and
+.BR ldap_controls_free ()
+frees a NULL-terminated array of controls.
+
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_error (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_controls.3.links b/doc/man/man3/ldap_controls.3.links
new file mode 100644
index 0000000..6c5248f
--- /dev/null
+++ b/doc/man/man3/ldap_controls.3.links
@@ -0,0 +1,6 @@
+ldap_control_create.3
+ldap_control_find.3
+ldap_control_dup.3
+ldap_controls_dup.3
+ldap_control_free.3
+ldap_controls_free.3
diff --git a/doc/man/man3/ldap_delete.3 b/doc/man/man3/ldap_delete.3
new file mode 100644
index 0000000..5086a6d
--- /dev/null
+++ b/doc/man/man3/ldap_delete.3
@@ -0,0 +1,89 @@
+.TH LDAP_DELETE 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_delete, ldap_delete_s, ldap_delete_ext, ldap_delete_ext_s \- Perform an LDAP delete operation.
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+int ldap_delete_s(ld, dn)
+.ft
+LDAP *ld;
+char *dn;
+.LP
+.ft B
+int ldap_delete(ld, dn)
+.ft
+LDAP *ld;
+char *dn;
+.LP
+.ft B
+int ldap_delete_ext(ld, dn, serverctrls, clientctrls, msgidp)
+.ft
+LDAP *ld;
+char *dn;
+LDAPControl **serverctrls, **clientctrls;
+int *msgidp;
+.LP
+.ft B
+int ldap_delete_ext_s(ld, dn, serverctrls, clientctrls)
+.ft
+LDAP *ld;
+char *dn;
+LDAPControl **serverctrls, **clientctrls;
+.SH DESCRIPTION
+The
+.B ldap_delete_s()
+routine is used to perform an LDAP delete operation
+synchronously. It takes \fIdn\fP, the DN of the entry to be deleted.
+It returns an LDAP error code, indicating the success or failure of the
+operation.
+.LP
+The
+.B ldap_delete()
+routine is used to perform an LDAP delete operation
+asynchronously. It takes the same parameters as
+.BR ldap_delete_s(),
+but returns the message id of the request it initiated. The result of
+the delete can be obtained by a subsequent call to
+.BR ldap_result (3).
+.LP
+The
+.B ldap_delete_ext()
+routine allows server and client controls to be
+specified to extend the delete request. This routine is asynchronous like
+ldap_delete(), but its return value is an LDAP error code. It stores the
+message id of the request in the integer pointed to by msgidp.
+.LP
+The
+.B ldap_delete_ext_s()
+routine is the synchronous version of
+.BR ldap_delete_ext().
+It also returns an LDAP error code indicating success
+or failure of the operation.
+.SH ERRORS
+.B ldap_delete_s()
+returns an LDAP error code which can be interpreted
+by calling one of
+.BR ldap_perror (3)
+and friends.
+.B ldap_delete()
+returns \-1 if something went wrong initiating the request. It returns the
+non-negative message id of the request if things went ok.
+.LP
+.B ldap_delete_ext()
+and
+.B ldap_delete_ext_s()
+return some Non-zero value if
+something went wrong initiating the request, else return 0.
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_error (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_delete.3.links b/doc/man/man3/ldap_delete.3.links
new file mode 100644
index 0000000..d4eac2f
--- /dev/null
+++ b/doc/man/man3/ldap_delete.3.links
@@ -0,0 +1,3 @@
+ldap_delete_s.3
+ldap_delete_ext.3
+ldap_delete_ext_s.3
diff --git a/doc/man/man3/ldap_dup.3 b/doc/man/man3/ldap_dup.3
new file mode 100644
index 0000000..945ca54
--- /dev/null
+++ b/doc/man/man3/ldap_dup.3
@@ -0,0 +1,125 @@
+.TH LDAP_OPEN 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_dup, ldap_destroy, \- Duplicate and destroy LDAP session handles
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+LDAP *ldap_dup(
+.RS
+.ft B
+LDAP *\fIold\fB );
+.RE
+.LP
+.ft B
+int ldap_destroy(
+.RS
+.ft B
+LDAP *\fIold\fB );
+.RE
+.SH DESCRIPTION
+.LP
+.B ldap_dup()
+duplicates an existing LDAP
+.RB ( "LDAP *" )
+session handle.
+The new session handle may be used concurrently with the
+original session handle.
+In a threaded environment, different threads may execute concurrent
+requests on the same connection/session without fear of contamination.
+Each session handle manages its own private error results.
+.LP
+.B ldap_destroy()
+destroys an existing session handle.
+.LP
+The
+.B ldap_dup()
+and
+.B ldap_destroy()
+functions are used in conjunction with a "thread safe" version
+of
+.B libldap
+to enable operation thread safe API calls, so that a single session
+may be simultaneously used across multiple threads with consistent
+error handling.
+.LP
+When a session is created through the use of one of the session creation
+functions including
+.BR ldap_open (3),
+.BR ldap_init (3),
+.BR ldap_initialize (3)
+or
+.BR ldap_init_fd (3)
+an
+.B "LDAP *"
+session handle is returned to the application.
+The session handle may be shared amongst threads, however the
+error codes are unique to a session handle.
+Multiple threads performing different operations using the same
+session handle will result in inconsistent error codes and
+return values.
+.LP
+To prevent this confusion,
+.B ldap_dup()
+is used duplicate an existing session handle so that multiple threads
+can share the session, and maintain consistent error information
+and results.
+.LP
+The message queues for a session are shared between sibling session handles.
+Results of operations on a sibling session handles are accessible
+to all the sibling session handles.
+Applications desiring results associated with a specific operation
+should provide the appropriate msgid to
+.BR ldap_result() .
+Applications should avoid calling
+.B ldap_result()
+with
+.B LDAP_RES_ANY
+as that may "steal" and return results in the calling thread
+that another operation in a different thread, using a
+different session handle, may require to complete.
+.LP
+When
+.B ldap_unbind()
+is called on a session handle with siblings, all the
+siblings become invalid.
+.LP
+Siblings must be destroyed using
+.BR ldap_destroy() .
+Session handle resources associated with the original
+.RB ( "LDAP *" )
+will be freed when the last session handle is destroyed or when
+.B ldap_unbind()
+is called, if no other session handles currently exist.
+.SH ERRORS
+If an error occurs,
+.B ldap_dup()
+will return NULL and
+.I errno
+should be set appropriately.
+.B ldap_destroy()
+will directly return the LDAP code associated to the error (or
+.I LDAP_SUCCESS
+in case of success);
+.I errno
+should be set as well whenever appropriate.
+.SH SEE ALSO
+.BR ldap_open (3),
+.BR ldap_init (3),
+.BR ldap_initialize (3),
+.BR ldap_init_fd (3),
+.BR errno (3)
+.SH ACKNOWLEDGEMENTS
+This work is based on the previously proposed
+.B LDAP C API Concurrency Extensions
+draft
+.BR ( draft-zeilenga-ldap-c-api-concurrency-00.txt )
+effort.
+.so ../Project
diff --git a/doc/man/man3/ldap_dup.3.links b/doc/man/man3/ldap_dup.3.links
new file mode 100644
index 0000000..1d77f93
--- /dev/null
+++ b/doc/man/man3/ldap_dup.3.links
@@ -0,0 +1 @@
+ldap_destroy.3
diff --git a/doc/man/man3/ldap_error.3 b/doc/man/man3/ldap_error.3
new file mode 100644
index 0000000..bbe0b5d
--- /dev/null
+++ b/doc/man/man3/ldap_error.3
@@ -0,0 +1,224 @@
+.TH LDAP_ERROR 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_perror, ld_errno, ldap_result2error, ldap_errlist, ldap_err2string \- LDAP protocol error handling routines
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+char *ldap_err2string( int \fIerr\fB );
+.SH DESCRIPTION
+The
+.B ldap_err2string()
+routine provides short description of the various codes returned by
+routines in this library. The returned string is a pointer to a
+static area that should not be modified.
+
+These codes are either negative,
+indicating an API error code; positive, indicating an LDAP resultCode
+other than \'success' (0), or - zero, indicating both successful use
+of the API and the LDAP resultCode \'success' (0).
+
+The code associated with an LDAP session is accessible using
+.BR ldap_get_option (3)
+and
+.BR ldap_set_option (3)
+with the
+.B LDAP_OPT_RESULT_CODE
+option (previously called
+.BR LDAP_OPT_ERROR_NUMBER ).
+
+.SH PROTOCOL RESULT CODES
+
+This section provides a partial list of protocol codes recognized
+by the library. As LDAP is extensible, additional values may be
+returned. A complete listing of \fIregistered\fP LDAP result codes
+can be obtained from the \fIInternet Assigned Numbers Authority\fP
+<http://www.iana.org>.
+
+.LP
+.TP 20
+.SM LDAP_SUCCESS
+The request was successful.
+.TP
+.SM LDAP_OPERATIONS_ERROR
+An operations error occurred.
+.TP
+.SM LDAP_PROTOCOL_ERROR
+A protocol violation was detected.
+.TP
+.SM LDAP_TIMELIMIT_EXCEEDED
+An LDAP time limit was exceeded.
+.TP
+.SM LDAP_SIZELIMIT_EXCEEDED
+An LDAP size limit was exceeded.
+.TP
+.SM LDAP_COMPARE_FALSE
+A compare operation returned false.
+.TP
+.SM LDAP_COMPARE_TRUE
+A compare operation returned true.
+.TP
+.SM LDAP_STRONG_AUTH_NOT_SUPPORTED
+The LDAP server does not support strong authentication.
+.TP
+.SM LDAP_STRONG_AUTH_REQUIRED
+Strong authentication is required for the operation.
+.TP
+.SM LDAP_PARTIAL_RESULTS
+Partial results only returned.
+.TP
+.SM LDAP_NO_SUCH_ATTRIBUTE
+The attribute type specified does not exist in the entry.
+.TP
+.SM LDAP_UNDEFINED_TYPE
+The attribute type specified is invalid.
+.TP
+.SM LDAP_INAPPROPRIATE_MATCHING
+Filter type not supported for the specified attribute.
+.TP
+.SM LDAP_CONSTRAINT_VIOLATION
+An attribute value specified violates some constraint (e.g., a postalAddress
+has too many lines, or a line that is too long).
+.TP
+.SM LDAP_TYPE_OR_VALUE_EXISTS
+An attribute type or attribute value specified already exists in the entry.
+.TP
+.SM LDAP_INVALID_SYNTAX
+An invalid attribute value was specified.
+.TP
+.SM LDAP_NO_SUCH_OBJECT
+The specified object does not exist in The Directory.
+.TP
+.SM LDAP_ALIAS_PROBLEM
+An alias in The Directory points to a nonexistent entry.
+.TP
+.SM LDAP_INVALID_DN_SYNTAX
+A syntactically invalid DN was specified.
+.TP
+.SM LDAP_IS_LEAF
+The object specified is a leaf.
+.TP
+.SM LDAP_ALIAS_DEREF_PROBLEM
+A problem was encountered when dereferencing an alias.
+.TP
+.SM LDAP_INAPPROPRIATE_AUTH
+Inappropriate authentication was specified (e.g., LDAP_AUTH_SIMPLE was
+specified and the entry does not have a userPassword attribute).
+.TP
+.SM LDAP_INVALID_CREDENTIALS
+Invalid credentials were presented (e.g., the wrong password).
+.TP
+.SM LDAP_INSUFFICIENT_ACCESS
+The user has insufficient access to perform the operation.
+.TP
+.SM LDAP_BUSY
+The DSA is busy.
+.TP
+.SM LDAP_UNAVAILABLE
+The DSA is unavailable.
+.TP
+.SM LDAP_UNWILLING_TO_PERFORM
+The DSA is unwilling to perform the operation.
+.TP
+.SM LDAP_LOOP_DETECT
+A loop was detected.
+.TP
+.SM LDAP_NAMING_VIOLATION
+A naming violation occurred.
+.TP
+.SM LDAP_OBJECT_CLASS_VIOLATION
+An object class violation occurred (e.g., a "must" attribute was missing
+from the entry).
+.TP
+.SM LDAP_NOT_ALLOWED_ON_NONLEAF
+The operation is not allowed on a nonleaf object.
+.TP
+.SM LDAP_NOT_ALLOWED_ON_RDN
+The operation is not allowed on an RDN.
+.TP
+.SM LDAP_ALREADY_EXISTS
+The entry already exists.
+.TP
+.SM LDAP_NO_OBJECT_CLASS_MODS
+Object class modifications are not allowed.
+.TP
+.SM LDAP_OTHER
+An unknown error occurred.
+
+.SH API ERROR CODES
+
+This section provides a complete list of API error codes recognized
+by the library. Note that LDAP_SUCCESS indicates success of an
+API call in addition to representing the return of the LDAP
+\'success' resultCode.
+
+
+.LP
+.TP 20
+.SM LDAP_SERVER_DOWN
+The LDAP library can't contact the LDAP server.
+.TP
+.SM LDAP_LOCAL_ERROR
+Some local error occurred. This is usually a failed dynamic memory allocation.
+.TP
+.SM LDAP_ENCODING_ERROR
+An error was encountered encoding parameters to send to the LDAP server.
+.TP
+.SM LDAP_DECODING_ERROR
+An error was encountered decoding a result from the LDAP server.
+.TP
+.SM LDAP_TIMEOUT
+A timelimit was exceeded while waiting for a result.
+.TP
+.SM LDAP_AUTH_UNKNOWN
+The authentication method specified to ldap_bind() is not known.
+.TP
+.SM LDAP_FILTER_ERROR
+An invalid filter was supplied to ldap_search() (e.g., unbalanced
+parentheses).
+.TP
+.SM LDAP_PARAM_ERROR
+An ldap routine was called with a bad parameter.
+.TP
+.SM LDAP_NO_MEMORY
+An memory allocation (e.g., malloc(3) or other dynamic memory
+allocator) call failed in an ldap library routine.
+.TP
+.SM LDAP_USER_CANCELED
+Indicates the user cancelled the operation.
+.TP
+.SM LDAP_CONNECT_ERROR
+Indicates a connection problem.
+.TP
+.SM LDAP_NOT_SUPPORTED
+Indicates the routine was called in a manner not supported by the library.
+.TP
+.SM LDAP_CONTROL_NOT_FOUND
+Indicates the control provided is unknown to the client library.
+.TP
+.SM LDAP_NO_RESULTS_RETURNED
+Indicates no results returned.
+.TP
+.SM LDAP_MORE_RESULTS_TO_RETURN
+Indicates more results could be returned.
+.TP
+.SM LDAP_CLIENT_LOOP
+Indicates the library has detected a loop in its processing.
+.TP
+.SM LDAP_REFERRAL_LIMIT_EXCEEDED
+Indicates the referral limit has been exceeded.
+
+.SH DEPRECATED
+.so Deprecated
+
+.SH SEE ALSO
+.BR ldap (3),
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_error.3.links b/doc/man/man3/ldap_error.3.links
new file mode 100644
index 0000000..841370d
--- /dev/null
+++ b/doc/man/man3/ldap_error.3.links
@@ -0,0 +1,5 @@
+ldap_perror.3
+ld_errno.3
+ldap_result2error.3
+ldap_errlist.3
+ldap_err2string.3
diff --git a/doc/man/man3/ldap_extended_operation.3 b/doc/man/man3/ldap_extended_operation.3
new file mode 100644
index 0000000..02ec882
--- /dev/null
+++ b/doc/man/man3/ldap_extended_operation.3
@@ -0,0 +1,75 @@
+.TH LDAP_EXTENDED_OPERATION 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_extended_operation, ldap_extended_operation_s \- Extends the LDAP operations to the LDAP server.
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+int ldap_extended_operation(
+.RS
+.ft B
+LDAP *\fIld\fB,
+const char *\fIrequestoid\fB,
+const struct berval *\fIrequestdata\fB,
+LDAPControl **\fIsctrls\fB,
+LDAPControl **\fIcctrls\fB,
+int *\fImsgidp\fB );
+.RE
+.LP
+.ft B
+int ldap_extended_operation_s(
+.RS
+.ft B
+LDAP *\fIld\fB,
+const char *\fIrequestoid\fB,
+const struct berval *\fIrequestdata\fB,
+LDAPControl **\fIsctrls\fB,
+LDAPControl **\fIcctrls\fB,
+char **\fIretoidp\fB,
+struct berval **\fIretdatap\fB );
+.RE
+.SH DESCRIPTION
+The
+.B ldap_extended_operation_s()
+routine is used to synchronously perform an LDAP extended operation.
+It takes \fIrequestoid\fP, which points to a dotted-decimal OID string
+identifying the extended operation to perform. \fIrequestdata\fP is the
+data required for the request, \fIsctrls\fP is an array of LDAPControl
+structures to use with this extended operation, \fIcctrls\fP is an array
+of LDAPControl structures that list the client controls to use with
+this extended operation.
+.LP
+The output parameter \fIretoidp\fP points to a dotted-decimal OID
+string returned by the LDAP server. The memory used by the string
+should be freed with the
+.BR ldap_memfree (3)
+function.
+The output parameter \fIretdatap\fP points to a pointer to a berval
+structure that contains the returned data. If no data is returned
+by the server, the pointer is set this to NULL. The memory used by
+this structure should be freed with the
+.BR ber_bvfree (3)
+function.
+.LP
+The
+.B ldap_extended_operation()
+works just like
+.BR ldap_extended_operation_s() ,
+but the operation is asynchronous. It provides the message id of
+the request it initiated in the integer pointed to be \fImsgidp\fP.
+The result of this operation can be obtained by calling
+.BR ldap_result(3).
+.SH SEE ALSO
+.BR ber_bvfree (3),
+.BR ldap_memfree (3),
+.BR ldap_parse_extended_result (3),
+.BR ldap_result (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_extended_operation.3.links b/doc/man/man3/ldap_extended_operation.3.links
new file mode 100644
index 0000000..1c5dc67
--- /dev/null
+++ b/doc/man/man3/ldap_extended_operation.3.links
@@ -0,0 +1,2 @@
+ldap_extended_operation_s.3
+
diff --git a/doc/man/man3/ldap_first_attribute.3 b/doc/man/man3/ldap_first_attribute.3
new file mode 100644
index 0000000..47e8b0c
--- /dev/null
+++ b/doc/man/man3/ldap_first_attribute.3
@@ -0,0 +1,97 @@
+.TH LDAP_FIRST_ATTRIBUTE 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_first_attribute, ldap_next_attribute \- step through LDAP entry attributes
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+char *ldap_first_attribute(
+ LDAP *ld, LDAPMessage *entry, BerElement **berptr )
+.LP
+.ft B
+char *ldap_next_attribute(
+ LDAP *ld, LDAPMessage *entry, BerElement *ber )
+.LP
+.ft B
+int ldap_get_attribute_ber(
+ LDAP *ld, LDAPMessage *entry, BerElement *ber,
+ BerValue *attr, BerVarray *vals )
+.SH DESCRIPTION
+The
+.BR ldap_first_attribute() ,
+.B ldap_next_attribute()
+and
+.B ldap_get_attribute_ber()
+routines are used
+to step through the attributes in an LDAP entry.
+.B ldap_first_attribute()
+takes an \fIentry\fP as returned by
+.BR ldap_first_entry (3)
+or
+.BR ldap_next_entry (3)
+and returns a pointer to character string
+containing the first attribute description in the entry.
+.B ldap_next_attribute()
+returns the next attribute description in the entry.
+.LP
+It also returns, in \fIberptr\fP, a pointer to a BerElement it has
+allocated to keep track of its current position. This pointer should
+be passed to subsequent calls to
+.B ldap_next_attribute()
+and is used
+to effectively step through the entry's attributes. The caller is
+solely responsible for freeing the BerElement pointed to by \fIberptr\fP
+when it is no longer needed by calling
+.BR ber_free (3).
+When calling
+.BR ber_free (3)
+in this instance, be sure the second argument is 0.
+.LP
+The attribute names returned are suitable for inclusion in a call
+to
+.BR ldap_get_values (3)
+to retrieve the attribute's values.
+.LP
+The
+.B ldap_get_attribute_ber()
+routine allows one to iterate over all attributes in-place, without
+allocating memory to hold text for the attribute name or its values,
+if requested. The use case is similar to
+.B ldap_next_attribute()
+except that the attribute name is returned into \fIattr\fP and, if
+\fIvals\fP is non-NULL, the list of values is stored there. Both point
+into the LDAP message and remain valid only while the entry is valid.
+The caller is still responsible for freeing \fIvals\fP with
+.BR ldap_memfree (3),
+if used.
+.SH ERRORS
+If an error occurs, NULL is returned and the ld_errno field in the
+\fIld\fP parameter is set to indicate the error. See
+.BR ldap_error (3)
+for a description of possible error codes.
+.SH NOTES
+The
+.B ldap_first_attribute()
+and
+.B ldap_next_attribute()
+return dynamically allocated memory that must be freed by the caller via
+.BR ldap_memfree (3).
+For
+.BR ldap_get_attribute_ber() ,
+only the actual \fIvals\fP pointer needs to be freed with
+.BR ldap_memfree (3),
+other data is accounted for as part of \fIber\fP.
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_first_entry (3),
+.BR ldap_get_values (3),
+.BR ldap_error (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_first_attribute.3.links b/doc/man/man3/ldap_first_attribute.3.links
new file mode 100644
index 0000000..ce3981c
--- /dev/null
+++ b/doc/man/man3/ldap_first_attribute.3.links
@@ -0,0 +1,2 @@
+ldap_next_attribute.3
+ldap_get_attribute_ber.3
diff --git a/doc/man/man3/ldap_first_entry.3 b/doc/man/man3/ldap_first_entry.3
new file mode 100644
index 0000000..b0eadd0
--- /dev/null
+++ b/doc/man/man3/ldap_first_entry.3
@@ -0,0 +1,80 @@
+.TH LDAP_FIRST_ENTRY 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_first_entry, ldap_next_entry, ldap_count_entries \- LDAP result entry parsing and counting routines
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+int ldap_count_entries( LDAP *ld, LDAPMessage *result )
+.LP
+.ft B
+LDAPMessage *ldap_first_entry( LDAP *ld, LDAPMessage *result )
+.LP
+.ft B
+LDAPMessage *ldap_next_entry( LDAP *ld, LDAPMessage *entry )
+.SH DESCRIPTION
+.LP
+These routines are used to parse results received from
+.BR ldap_result (3)
+or the synchronous LDAP search operation routines
+.BR ldap_search_s (3)
+and
+.BR ldap_search_st (3).
+.LP
+The
+.B ldap_first_entry()
+routine is used to retrieve the first entry in a chain
+of search results. It takes the \fIresult\fP as returned by a call to
+.BR ldap_result (3)
+or
+.BR ldap_search_s (3)
+or
+.BR ldap_search_st (3)
+and returns a pointer to the first entry in the result.
+.LP
+This pointer should be supplied on a subsequent call to
+.B ldap_next_entry()
+to get the next entry, the result of which should be
+supplied to the next call to
+.BR ldap_next_entry() ,
+etc.
+.B ldap_next_entry()
+will return NULL when there are no more entries. The entries returned
+from these calls are used in calls to the routines described in
+.BR ldap_get_dn (3),
+.BR ldap_first_attribute (3),
+.BR ldap_get_values (3),
+etc.
+.LP
+A count of the number of entries in the search result can be obtained
+by calling
+.BR ldap_count_entries() .
+.SH ERRORS
+If an error occurs in
+.B ldap_first_entry()
+or
+.BR ldap_next_entry() ,
+NULL is returned and the ld_errno field in the \fIld\fP parameter
+is set to indicate the error. If an error occurs in
+.BR ldap_count_entries() ,
+-1 is returned, and
+.B ld_errno
+is set appropriately. See
+.BR ldap_error (3)
+for a description of possible error codes.
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_result (3),
+.BR ldap_search (3),
+.BR ldap_first_attribute (3),
+.BR ldap_get_values (3),
+.BR ldap_get_dn (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_first_entry.3.links b/doc/man/man3/ldap_first_entry.3.links
new file mode 100644
index 0000000..781590b
--- /dev/null
+++ b/doc/man/man3/ldap_first_entry.3.links
@@ -0,0 +1,2 @@
+ldap_next_entry.3
+ldap_count_entries.3
diff --git a/doc/man/man3/ldap_first_message.3 b/doc/man/man3/ldap_first_message.3
new file mode 100644
index 0000000..4d62359
--- /dev/null
+++ b/doc/man/man3/ldap_first_message.3
@@ -0,0 +1,82 @@
+.TH LDAP_FIRST_MESSAGE 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_first_message, ldap_next_message, ldap_count_messages \- Stepping through messages in a result chain
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+int ldap_count_messages( LDAP *ld, LDAPMessage *result )
+.LP
+.ft B
+LDAPMessage *ldap_first_message( LDAP *ld, LDAPMessage *result )
+.LP
+.ft B
+LDAPMessage *ldap_next_message( LDAP *ld, LDAPMessage *message )
+.SH DESCRIPTION
+.LP
+These routines are used to step through the messages in a result chain
+received from
+.BR ldap_result (3) .
+For search operations, the result chain can contain referral, entry
+and result messages. The
+.BR ldap_msgtype (3)
+function can be used to distinguish between the different message types.
+.LP
+The
+.B ldap_first_message()
+routine is used to retrieve the first message in a result chain.
+It takes the \fIresult\fP as returned by a call to
+.BR ldap_result (3) ,
+.BR ldap_search_s (3)
+or
+.BR ldap_search_st (3)
+and returns a pointer to the first message in the result chain.
+.LP
+This pointer should be supplied on a subsequent call to
+.B ldap_next_message()
+to get the next message, the result of which should be
+supplied to the next call to
+.BR ldap_next_message() ,
+etc.
+.B ldap_next_message()
+will return NULL when there are no more messages.
+.LP
+These functions are useful when using routines like
+.BR ldap_parse_result (3)
+that only operate on the first result in the chain.
+.LP
+A count of the number of messages in the result chain can be obtained
+by calling
+.BR ldap_count_messages() .
+It can also be used to count the number of remaining messages in a chain
+if called with a message, entry or reference returned by
+.B ldap_first_message() ,
+.B ldap_next_message() ,
+.BR ldap_first_entry (3) ,
+.BR ldap_next_entry (3) ,
+.BR ldap_first_reference (3) ,
+.BR ldap_next_reference (3) .
+.SH ERRORS
+If an error occurs in
+.B ldap_first_message()
+or
+.BR ldap_next_message() ,
+NULL is returned. If an error occurs in
+.BR ldap_count_messages() ,
+-1 is returned.
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_search (3),
+.BR ldap_result (3),
+.BR ldap_parse_result (3),
+.BR ldap_first_entry (3),
+.BR ldap_first_reference (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_first_message.3.links b/doc/man/man3/ldap_first_message.3.links
new file mode 100644
index 0000000..420c04f
--- /dev/null
+++ b/doc/man/man3/ldap_first_message.3.links
@@ -0,0 +1,2 @@
+ldap_next_message.3
+ldap_count_messages.3
diff --git a/doc/man/man3/ldap_first_reference.3 b/doc/man/man3/ldap_first_reference.3
new file mode 100644
index 0000000..2bcba1a
--- /dev/null
+++ b/doc/man/man3/ldap_first_reference.3
@@ -0,0 +1,71 @@
+.TH LDAP_FIRST_REFERENCE 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_first_reference, ldap_next_reference, ldap_count_references \- Stepping through continuation references in a result chain
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+int ldap_count_references( LDAP *ld, LDAPMessage *result )
+.LP
+.ft B
+LDAPMessage *ldap_first_reference( LDAP *ld, LDAPMessage *result )
+.LP
+.ft B
+LDAPMessage *ldap_next_reference( LDAP *ld, LDAPMessage *reference )
+.SH DESCRIPTION
+.LP
+These routines are used to step through the continuation references in a
+result chain received from
+.BR ldap_result (3)
+or the synchronous LDAP search operation routines.
+.LP
+The
+.B ldap_first_reference()
+routine is used to retrieve the first reference message in a
+result chain. It takes the \fIresult\fP as returned by a call to
+.BR ldap_result (3) ,
+.BR ldap_search_s (3)
+or
+.BR ldap_search_st (3)
+and returns a pointer to the first reference message in the
+result chain.
+.LP
+This pointer should be supplied on a subsequent call to
+.B ldap_next_reference()
+to get the next reference message, the result of which should be
+supplied to the next call to
+.BR ldap_next_reference() ,
+etc.
+.B ldap_next_reference()
+will return NULL when there are no more reference messages.
+The reference messages returned from these calls are used by
+.BR ldap_parse_reference (3)
+to extract referrals and controls.
+.LP
+A count of the number of reference messages in the search result can be
+obtained by calling
+.BR ldap_count_references() .
+It can also be used to count the number of reference messages remaining
+in a result chain.
+.SH ERRORS
+If an error occurs in
+.B ldap_first_reference()
+or
+.BR ldap_next_reference() ,
+NULL is returned. If an error occurs in
+.BR ldap_count_references() ,
+-1 is returned.
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_result (3),
+.BR ldap_search (3),
+.BR ldap_parse_reference (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_first_reference.3.links b/doc/man/man3/ldap_first_reference.3.links
new file mode 100644
index 0000000..a747bbb
--- /dev/null
+++ b/doc/man/man3/ldap_first_reference.3.links
@@ -0,0 +1,2 @@
+ldap_next_reference.3
+ldap_count_references.3
diff --git a/doc/man/man3/ldap_get_dn.3 b/doc/man/man3/ldap_get_dn.3
new file mode 100644
index 0000000..6e052a3
--- /dev/null
+++ b/doc/man/man3/ldap_get_dn.3
@@ -0,0 +1,246 @@
+.TH LDAP_GET_DN 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_get_dn, ldap_explode_dn, ldap_explode_rdn, ldap_dn2ufn \- LDAP DN handling routines
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+char *ldap_get_dn( LDAP *ld, LDAPMessage *entry )
+.LP
+.ft B
+int ldap_str2dn( const char *str, LDAPDN *dn, unsigned flags )
+.LP
+.ft B
+void ldap_dnfree( LDAPDN dn )
+.LP
+.ft B
+int ldap_dn2str( LDAPDN dn, char **str, unsigned flags )
+.LP
+.ft B
+char **ldap_explode_dn( const char *dn, int notypes )
+.LP
+.ft B
+char **ldap_explode_rdn( const char *rdn, int notypes )
+.LP
+.ft B
+char *ldap_dn2ufn( const char * dn )
+.LP
+.ft B
+char *ldap_dn2dcedn( const char * dn )
+.LP
+.ft B
+char *ldap_dcedn2dn( const char * dn )
+.LP
+.ft B
+char *ldap_dn2ad_canonical( const char * dn )
+.SH DESCRIPTION
+These routines allow LDAP entry names (Distinguished Names, or DNs)
+to be obtained, parsed, converted to a user-friendly form, and tested.
+A DN has the form described in
+RFC 4414 "Lightweight Directory Access Protocol (LDAP):
+String Representation of Distinguished Names".
+.LP
+The
+.B ldap_get_dn()
+routine takes an \fIentry\fP as returned by
+.BR ldap_first_entry (3)
+or
+.BR ldap_next_entry (3)
+and returns a copy of
+the entry's DN. Space for the DN will be obtained dynamically
+and should be freed by the caller using
+.BR ldap_memfree (3).
+.LP
+.B ldap_str2dn()
+parses a string representation of a distinguished name contained in
+.B str
+into its components,
+which are stored in
+.B dn
+as
+.B ldap_ava
+structures, arranged in
+.B LDAPAVA,
+.B LDAPRDN,
+and
+.B LDAPDN
+terms. Space for
+.B dn
+will be obtained dynamically and should be freed by the caller using
+.BR ldap_dnfree (3).
+The
+.B LDAPDN
+is defined as:
+.nf
+.ft B
+
+typedef struct ldap_ava {
+ struct berval la_attr;
+ struct berval la_value;
+ unsigned la_flags;
+} LDAPAVA;
+
+typedef LDAPAVA** LDAPRDN;
+typedef LDAPRDN* LDAPDN;
+
+.ft
+.fi
+The attribute types and the attribute values are not normalized.
+The
+.B la_flags
+can be either
+.B LDAP_AVA_STRING
+or
+.B LDAP_AVA_BINARY,
+the latter meaning that the value is BER/DER encoded and thus must
+be represented as, quoting from RFC 4514, " ... an
+octothorpe character ('#' ASCII 35) followed by the hexadecimal
+representation of each of the bytes of the BER encoding of the X.500
+AttributeValue."
+The
+.B flags
+parameter to
+.B ldap_str2dn()
+can be
+.LP
+.nf
+ LDAP_DN_FORMAT_LDAPV3
+ LDAP_DN_FORMAT_LDAPV2
+ LDAP_DN_FORMAT_DCE
+
+.fi
+which defines what DN syntax is expected (according to RFC 4514,
+RFC 1779 and DCE, respectively).
+The format can be \fIOR\fPed to the flags
+.LP
+.nf
+ LDAP_DN_P_NO_SPACES
+ LDAP_DN_P_NO_SPACE_AFTER_RDN
+ ...
+ LDAP_DN_PEDANTIC
+
+.fi
+The latter is a shortcut for all the previous limitations.
+.LP
+.B LDAP_DN_P_NO_SPACES
+does not allow extra spaces in the dn; the default is to silently
+eliminate spaces around AVA separators ('='), RDN component separators
+('+' for LDAPv3/LDAPv2 or ',' for DCE) and RDN separators
+(',' LDAPv3/LDAPv2 or '/' for DCE).
+.LP
+.B LDAP_DN_P_NO_SPACE_AFTER_RDN
+does not allow a single space after RDN separators.
+.LP
+.B ldap_dn2str()
+performs the inverse operation, yielding in
+.B str
+a string representation of
+.B dn.
+It allows the same values for
+.B flags
+as
+.B ldap_str2dn(),
+plus
+.LP
+.nf
+ LDAP_DN_FORMAT_UFN
+ LDAP_DN_FORMAT_AD_CANONICAL
+
+.fi
+for user-friendly naming (RFC 1781) and AD canonical.
+.LP
+The following routines are viewed as deprecated in favor of
+.B ldap_str2dn()
+and
+.BR ldap_dn2str().
+They are provided to support legacy applications.
+.LP
+The
+.B ldap_explode_dn()
+routine takes a DN as returned by
+.B ldap_get_dn()
+and breaks it up into its component parts. Each part is known as a
+Relative Distinguished Name, or RDN.
+.B ldap_explode_dn()
+returns a
+NULL-terminated array, each component of which contains an RDN from the
+DN. The \fInotypes\fP parameter is used to request that only the RDN
+values be returned, not their types. For example, the DN "cn=Bob,
+c=US" would return as either { "cn=Bob", "c=US", NULL } or { "Bob",
+"US", NULL }, depending on whether notypes was 0 or 1, respectively.
+Assertion values in RDN strings may included escaped characters.
+The result can be freed by calling
+.BR ldap_value_free (3).
+.LP
+Similarly, the
+.B ldap_explode_rdn()
+routine takes an RDN as returned by
+.B ldap_explode_dn(dn,0)
+and breaks it up into its "type=value" component parts (or just "value",
+if the \fInotypes\fP parameter is set). Note the value is not
+unescaped. The result can be freed by calling
+.BR ldap_value_free (3).
+.LP
+.B ldap_dn2ufn()
+is used to turn a DN as returned by
+.BR ldap_get_dn (3)
+into a more user-friendly form, stripping off all type names. See
+"Using the Directory to Achieve User Friendly Naming" (RFC 1781)
+for more details on the UFN format. Due to the ambiguous nature
+of the format, it is generally only used for display purposes.
+The space for the UFN returned is obtained dynamically and the user
+is responsible for freeing it via a call to
+.BR ldap_memfree (3).
+.LP
+.B ldap_dn2dcedn()
+is used to turn a DN as returned by
+.BR ldap_get_dn (3)
+into a DCE-style DN, e.g. a string with most-significant to least
+significant rdns separated by slashes ('/'); rdn components
+are separated by commas (',').
+Only printable chars (e.g. LDAPv2 printable string) are allowed,
+at least in this implementation.
+.B ldap_dcedn2dn()
+performs the opposite operation.
+.B ldap_dn2ad_canonical()
+turns a DN into a AD canonical name, which is basically a DCE dn
+with attribute types omitted.
+The trailing domain, if present, is turned in a DNS-like domain.
+The space for the returned value is obtained dynamically and the user
+is responsible for freeing it via a call to
+.BR ldap_memfree (3).
+.SH ERRORS
+If an error occurs in
+.BR ldap_get_dn() ,
+NULL is returned and the
+.B ld_errno
+field in the \fIld\fP parameter is set to indicate the error. See
+.BR ldap_error (3)
+for a description of possible error codes.
+.BR ldap_explode_dn() ,
+.BR ldap_explode_rdn() ,
+.B ldap_dn2ufn(),
+.B ldap_dn2dcedn(),
+.B ldap_dcedn2dn(),
+and
+.B ldap_dn2ad_canonical()
+will return NULL with
+.BR errno (3)
+set appropriately in case of trouble.
+.SH NOTES
+These routines dynamically allocate memory that the caller must free.
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_error (3),
+.BR ldap_first_entry (3),
+.BR ldap_memfree (3),
+.BR ldap_value_free (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_get_dn.3.links b/doc/man/man3/ldap_get_dn.3.links
new file mode 100644
index 0000000..4c71aa5
--- /dev/null
+++ b/doc/man/man3/ldap_get_dn.3.links
@@ -0,0 +1,9 @@
+ldap_explode_dn.3
+ldap_explode_rdn.3
+ldap_dn2ufn.3
+ldap_str2dn.3
+ldap_dnfree.3
+ldap_dn2str.3
+ldap_dn2dcedn.3
+ldap_dcedn2dn.3
+ldap_dn2ad_canonical.3
diff --git a/doc/man/man3/ldap_get_option.3 b/doc/man/man3/ldap_get_option.3
new file mode 100644
index 0000000..b98ad60
--- /dev/null
+++ b/doc/man/man3/ldap_get_option.3
@@ -0,0 +1,932 @@
+.TH LDAP_GET_OPTION 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_get_option, ldap_set_option \- LDAP option handling routines
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.B #include <ldap.h>
+.LP
+.BI "int ldap_get_option(LDAP *" ld ", int " option ", void *" outvalue ");"
+.LP
+.BI "int ldap_set_option(LDAP *" ld ", int " option ", const void *" invalue ");"
+.SH DESCRIPTION
+.LP
+These routines provide access to options stored either in a LDAP handle
+or as global options, where applicable.
+They make use of a neutral interface, where the type of the value
+either retrieved by
+.BR ldap_get_option (3)
+or set by
+.BR ldap_set_option (3)
+is cast to
+.BR "void *" .
+The actual type is determined based on the value of the
+.B option
+argument.
+Global options are set/retrieved by passing a NULL LDAP handle. LDAP handles
+inherit their default settings from the global options in effect at the time
+the handle is created.
+.TP
+.B LDAP_OPT_API_FEATURE_INFO
+Fills-in a
+.BR "LDAPAPIFeatureInfo" ;
+.BR outvalue
+must be a
+.BR "LDAPAPIFeatureInfo *" ,
+pointing to an already allocated struct.
+The
+.B ldapaif_info_version
+field of the struct must be initialized to
+.B LDAP_FEATURE_INFO_VERSION
+before making the call. The
+.B ldapaif_name
+field must be set to the name of a feature to query.
+This is a read-only option.
+.TP
+.B LDAP_OPT_API_INFO
+Fills-in a
+.BR "LDAPAPIInfo" ;
+.BR outvalue
+must be a
+.BR "LDAPAPIInfo *" ,
+pointing to an already allocated struct. The
+.B ldapai_info_version
+field of the struct must be initialized to
+.B LDAP_API_INFO_VERSION
+before making the call.
+If the version passed in does not match the current library
+version, the expected version number will be stored in the
+struct and the call will fail.
+The caller is responsible for freeing the elements of the
+.B ldapai_extensions
+array and the array itself using
+.BR ldap_memfree (3).
+The caller must also free the
+.BR ldapi_vendor_name .
+This is a read-only option.
+.TP
+.B LDAP_OPT_CLIENT_CONTROLS
+Sets/gets the client-side controls to be used for all operations.
+This is now deprecated as modern LDAP C API provides replacements
+for all main operations which accepts client-side controls as
+explicit arguments; see for example
+.BR ldap_search_ext (3),
+.BR ldap_add_ext (3),
+.BR ldap_modify_ext (3)
+and so on.
+.BR outvalue
+must be
+.BR "LDAPControl ***" ,
+and the caller is responsible of freeing the returned controls, if any,
+by calling
+.BR ldap_controls_free (3),
+while
+.BR invalue
+must be
+.BR "LDAPControl *const *" ;
+the library duplicates the controls passed via
+.BR invalue .
+.TP
+.B LDAP_OPT_CONNECT_ASYNC
+Sets/gets the status of the asynchronous connect flag.
+.BR invalue
+should either be
+.BR LDAP_OPT_OFF
+or
+.BR LDAP_OPT_ON ;
+.BR outvalue
+must be
+.BR "int *" .
+When set, the library will call
+.BR connect (2)
+and return, without waiting for response.
+This leaves the handle in a connecting state.
+Subsequent calls to library routines will poll for completion
+of the connect before performing further operations.
+As a consequence, library calls that need to establish a connection
+with a DSA do not block even for the network timeout
+(option
+.BR LDAP_OPT_NETWORK_TIMEOUT ).
+This option is OpenLDAP specific.
+.TP
+.B LDAP_OPT_CONNECT_CB
+This option allows to set a connect callback.
+.B invalue
+must be a
+.BR "const struct ldap_conncb *" .
+Callbacks are executed in last in-first served order.
+Handle-specific callbacks are executed first, followed by global ones.
+Right before freeing the callback structure, the
+.B lc_del
+callback handler is passed a
+.B NULL
+.BR Sockbuf .
+Calling
+.BR ldap_get_option (3)
+for this option removes the callback whose pointer matches
+.BR outvalue .
+This option is OpenLDAP specific.
+.TP
+.B LDAP_OPT_DEBUG_LEVEL
+Sets/gets the debug level of the client library.
+.BR invalue
+must be a
+.BR "const int *" ;
+.BR outvalue
+must be a
+.BR "int *" .
+Valid debug levels are
+.BR LDAP_DEBUG_ANY ,
+.BR LDAP_DEBUG_ARGS ,
+.BR LDAP_DEBUG_BER ,
+.BR LDAP_DEBUG_CONNS ,
+.BR LDAP_DEBUG_NONE ,
+.BR LDAP_DEBUG_PACKETS ,
+.BR LDAP_DEBUG_PARSE ,
+and
+.BR LDAP_DEBUG_TRACE .
+This option is OpenLDAP specific.
+.TP
+.B LDAP_OPT_DEFBASE
+Sets/gets a string containing the DN to be used as default base
+for search operations.
+.BR outvalue
+must be a
+.BR "char **" ,
+and the caller is responsible of freeing the returned string by calling
+.BR ldap_memfree (3),
+while
+.BR invalue
+must be a
+.BR "const char *" ;
+the library duplicates the corresponding string.
+This option is OpenLDAP specific.
+.TP
+.B LDAP_OPT_DEREF
+Sets/gets the value that defines when alias dereferencing must occur.
+.BR invalue
+must be
+.BR "const int *" ;
+.BR outvalue
+must be
+.BR "int *" .
+They cannot be NULL.
+The value of
+.BR *invalue
+should be one of
+.BR LDAP_DEREF_NEVER
+(the default),
+.BR LDAP_DEREF_SEARCHING ,
+.BR LDAP_DEREF_FINDING ,
+or
+.BR LDAP_DEREF_ALWAYS .
+Note that this has ever been the only means to determine alias dereferencing
+within search operations.
+.TP
+.B LDAP_OPT_DESC
+Returns the file descriptor associated to the socket buffer
+of the LDAP handle passed in as
+.BR ld ;
+.BR outvalue
+must be a
+.BR "int *" .
+This is a read-only, handle-specific option.
+.TP
+.B LDAP_OPT_DIAGNOSTIC_MESSAGE
+Sets/gets a string containing the error string associated to the LDAP handle.
+This option was formerly known as
+.BR LDAP_OPT_ERROR_STRING .
+.BR outvalue
+must be a
+.BR "char **" ,
+and the caller is responsible of freeing the returned string by calling
+.BR ldap_memfree (3),
+while
+.BR invalue
+must be a
+.BR "char *" ;
+the library duplicates the corresponding string.
+.TP
+.B LDAP_OPT_HOST_NAME
+Sets/gets a space-separated list of hosts to be contacted by the library
+when trying to establish a connection.
+This is now deprecated in favor of
+.BR LDAP_OPT_URI .
+.BR outvalue
+must be a
+.BR "char **" ,
+and the caller is responsible of freeing the resulting string by calling
+.BR ldap_memfree (3),
+while
+.BR invalue
+must be a
+.BR "const char *" ;
+the library duplicates the corresponding string.
+.TP
+.B LDAP_OPT_MATCHED_DN
+Sets/gets a string containing the matched DN associated to the LDAP handle.
+.BR outvalue
+must be a
+.BR "char **" ,
+and the caller is responsible of freeing the returned string by calling
+.BR ldap_memfree (3),
+while
+.BR invalue
+must be a
+.BR "const char *" ;
+the library duplicates the corresponding string.
+.TP
+.B LDAP_OPT_NETWORK_TIMEOUT
+Sets/gets the network timeout value after which
+.BR poll (2)/ select (2)
+following a
+.BR connect (2)
+returns in case of no activity.
+.B outvalue
+must be a
+.BR "struct timeval **"
+(the caller has to free
+.BR *outvalue
+using
+.BR ldap_memfree (3)),
+and
+.B invalue
+must be a
+.BR "const struct timeval *" .
+They cannot be NULL. Using a struct with seconds set to \-1 results
+in an infinite timeout, which is the default.
+This option is OpenLDAP specific.
+.TP
+.B LDAP_OPT_PROTOCOL_VERSION
+Sets/gets the protocol version.
+.BR outvalue
+and
+.BR invalue
+must be
+.BR "int *" .
+.TP
+.B LDAP_OPT_REFERRAL_URLS
+Sets/gets an array containing the referral URIs associated to the LDAP handle.
+.BR outvalue
+must be a
+.BR "char ***" ,
+and the caller is responsible of freeing the returned string by calling
+.BR ldap_memvfree (3),
+while
+.BR invalue
+must be a NULL-terminated
+.BR "char *const *" ;
+the library duplicates the corresponding string.
+This option is OpenLDAP specific.
+.TP
+.B LDAP_OPT_REFERRALS
+Determines whether the library should implicitly chase referrals or not.
+.BR invalue
+must be
+.BR "const int *" ;
+its value should either be
+.BR LDAP_OPT_OFF
+or
+.BR LDAP_OPT_ON .
+.BR outvalue
+must be
+.BR "int *" .
+.\".TP
+.\".B LDAP_OPT_REFHOPLIMIT
+.\"This option is OpenLDAP specific.
+.\"It is not currently implemented.
+.TP
+.B LDAP_OPT_RESTART
+Determines whether the library should implicitly restart connections (FIXME).
+.BR invalue
+must be
+.BR "const int *" ;
+its value should either be
+.BR LDAP_OPT_OFF
+or
+.BR LDAP_OPT_ON .
+.BR outvalue
+must be
+.BR "int *" .
+.TP
+.B LDAP_OPT_RESULT_CODE
+Sets/gets the LDAP result code associated to the handle.
+This option was formerly known as
+.BR LDAP_OPT_ERROR_NUMBER .
+.BR invalue
+must be a
+.BR "const int *" .
+.BR outvalue
+must be a
+.BR "int *" .
+.TP
+.B LDAP_OPT_SERVER_CONTROLS
+Sets/gets the server-side controls to be used for all operations.
+This is now deprecated as modern LDAP C API provides replacements
+for all main operations which accepts server-side controls as
+explicit arguments; see for example
+.BR ldap_search_ext (3),
+.BR ldap_add_ext (3),
+.BR ldap_modify_ext (3)
+and so on.
+.BR outvalue
+must be
+.BR "LDAPControl ***" ,
+and the caller is responsible of freeing the returned controls, if any,
+by calling
+.BR ldap_controls_free (3),
+while
+.BR invalue
+must be
+.BR "LDAPControl *const *" ;
+the library duplicates the controls passed via
+.BR invalue .
+.TP
+.B LDAP_OPT_SESSION_REFCNT
+Returns the reference count associated with the LDAP handle passed in as
+.BR ld ;
+.BR outvalue
+must be a
+.BR "int *" .
+This is a read-only, handle-specific option.
+This option is OpenLDAP specific.
+.TP
+.B LDAP_OPT_SIZELIMIT
+Sets/gets the value that defines the maximum number of entries
+to be returned by a search operation.
+.BR invalue
+must be
+.BR "const int *" ,
+while
+.BR outvalue
+must be
+.BR "int *" ;
+They cannot be NULL.
+.TP
+.B LDAP_OPT_SOCKBUF
+Returns a pointer to the socket buffer of the LDAP handle passed in as
+.BR ld ;
+.BR outvalue
+must be a
+.BR "Sockbuf **" .
+This is a read-only, handle-specific option.
+This option is OpenLDAP specific.
+.TP
+.B LDAP_OPT_SOCKET_BIND_ADDRESSES
+Sets/gets a space-separated list of IP Addresses used as binding interface
+to remote server when trying to establish a connection. Only one valid IPv4
+address and/or one valid IPv6 address are allowed in the list.
+.BR outvalue
+must be a
+.BR "char **",
+and the caller is responsible of freeing the returned string by calling
+.BR ldap_memfree (3),
+while
+.BR invalue
+must be a
+.BR "const char *" ;
+the library duplicates the corresponding string.
+.TP
+.B LDAP_OPT_TIMELIMIT
+Sets/gets the value that defines the time limit after which
+a search operation should be terminated by the server.
+.BR invalue
+must be
+.BR "const int *" ,
+while
+.BR outvalue
+must be
+.BR "int *" ,
+and they cannot be NULL.
+.TP
+.B LDAP_OPT_TIMEOUT
+Sets/gets a timeout value for the synchronous API calls.
+.B outvalue
+must be a
+.BR "struct timeval **"
+(the caller has to free
+.BR *outvalue
+using
+.BR ldap_memfree (3)),
+and
+.B invalue
+must be a
+.BR "struct timeval *" ,
+and they cannot be NULL. Using a struct with seconds set to \-1 results
+in an infinite timeout, which is the default.
+This option is OpenLDAP specific.
+.TP
+.B LDAP_OPT_URI
+Sets/gets a comma- or space-separated list of URIs to be contacted by the library
+when trying to establish a connection.
+.BR outvalue
+must be a
+.BR "char **" ,
+and the caller is responsible of freeing the resulting string by calling
+.BR ldap_memfree (3),
+while
+.BR invalue
+must be a
+.BR "const char *" ;
+the library parses the string into a list of
+.BR LDAPURLDesc
+structures, so the invocation of
+.BR ldap_set_option (3)
+may fail if URL parsing fails.
+URIs may only contain the
+.BR schema ,
+the
+.BR host ,
+and the
+.BR port
+fields.
+This option is OpenLDAP specific.
+.TP
+.B LDAP_OPT_KEEPCONN
+Instructs
+.BR ldap_result (3)
+to keep the connection open on read error or if Notice of Disconnection is received. In these cases, the connection should be closed by the caller.
+This option is OpenLDAP specific.
+.TP
+.B LDAP_OPT_TCP_USER_TIMEOUT
+Allows to configure TCP_USER_TIMEOUT in milliseconds on the connection, overriding the operating system setting.
+This option is OpenLDAP specific and supported only on Linux 2.6.37 or higher.
+.B invalue
+must be a
+.BR "const unsigned int *" ;
+.BR outvalue
+must be an
+.BR "unsigned int *" .
+
+.SH SASL OPTIONS
+The SASL options are OpenLDAP specific and unless otherwise noted, require an LDAP handle to be passed.
+.TP
+.B LDAP_OPT_X_SASL_AUTHCID
+Gets the SASL authentication identity;
+.BR outvalue
+must be a
+.BR "char **" ,
+its content needs to be freed by the caller using
+.BR ldap_memfree (3).
+.TP
+.B LDAP_OPT_X_SASL_AUTHZID
+Gets the SASL authorization identity;
+.BR outvalue
+must be a
+.BR "char **" ,
+its content needs to be freed by the caller using
+.BR ldap_memfree (3).
+.TP
+.B LDAP_OPT_X_SASL_MAXBUFSIZE
+Gets/sets SASL maximum buffer size;
+.BR invalue
+must be
+.BR "const ber_len_t *" ,
+while
+.BR outvalue
+must be
+.BR "ber_len_t *" .
+See also
+.BR LDAP_OPT_X_SASL_SECPROPS .
+.TP
+.B LDAP_OPT_X_SASL_MECH
+Gets the SASL mechanism;
+.BR outvalue
+must be a
+.BR "char **" ,
+its content needs to be freed by the caller using
+.BR ldap_memfree (3).
+.TP
+.B LDAP_OPT_X_SASL_MECHLIST
+Gets the list of the available mechanisms,
+in form of a NULL-terminated array of strings;
+.BR outvalue
+must be
+.BR "char ***" .
+The caller must not free or otherwise muck with it. This option can be used globally.
+.TP
+.B LDAP_OPT_X_SASL_NOCANON
+Sets/gets the NOCANON flag.
+When unset, the hostname is canonicalized.
+.BR invalue
+must be
+.BR "const int *" ;
+its value should either be
+.BR LDAP_OPT_OFF
+or
+.BR LDAP_OPT_ON .
+.BR outvalue
+must be
+.BR "int *" .
+.TP
+.B LDAP_OPT_X_SASL_REALM
+Gets the SASL realm;
+.BR outvalue
+must be a
+.BR "char **" ,
+its content needs to be freed by the caller using
+.BR ldap_memfree (3).
+.TP
+.B LDAP_OPT_X_SASL_SECPROPS
+Sets the SASL secprops;
+.BR invalue
+must be a
+.BR "char *" ,
+containing a comma-separated list of properties.
+Legal values are:
+.BR none ,
+.BR nodict ,
+.BR noplain ,
+.BR noactive ,
+.BR passcred ,
+.BR forwardsec ,
+.BR noanonymous ,
+.BR minssf=<minssf> ,
+.BR maxssf=<maxssf> ,
+.BR maxbufsize=<maxbufsize> .
+.TP
+.B LDAP_OPT_X_SASL_SSF
+Gets the SASL SSF;
+.BR outvalue
+must be a
+.BR "ber_len_t *" .
+.TP
+.B LDAP_OPT_X_SASL_SSF_EXTERNAL
+Sets the SASL SSF value related to an authentication
+performed using an EXTERNAL mechanism;
+.BR invalue
+must be a
+.BR "const ber_len_t *" .
+.TP
+.B LDAP_OPT_X_SASL_SSF_MAX
+Gets/sets SASL maximum SSF;
+.BR invalue
+must be
+.BR "const ber_len_t *" ,
+while
+.BR outvalue
+must be
+.BR "ber_len_t *" .
+See also
+.BR LDAP_OPT_X_SASL_SECPROPS .
+.TP
+.B LDAP_OPT_X_SASL_SSF_MIN
+Gets/sets SASL minimum SSF;
+.BR invalue
+must be
+.BR "const ber_len_t *" ,
+while
+.BR outvalue
+must be
+.BR "ber_len_t *" .
+See also
+.BR LDAP_OPT_X_SASL_SECPROPS .
+.TP
+.B LDAP_OPT_X_SASL_USERNAME
+Gets the SASL username;
+.BR outvalue
+must be a
+.BR "char **" .
+Its content needs to be freed by the caller using
+.BR ldap_memfree (3).
+.TP
+.B LDAP_OPT_X_SASL_CBINDING
+Sets/gets the channel-binding type to use in SASL,
+one of
+.BR LDAP_OPT_X_SASL_CBINDING_NONE
+(the default),
+.BR LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE
+the "tls-unique" type from RFC 5929.
+.BR LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT
+the "tls-server-end-point" from RFC 5929, compatible with Windows.
+.BR invalue
+must be
+.BR "const int *" ;
+.BR outvalue
+must be
+.BR "int *" .
+.SH TCP OPTIONS
+The TCP options are OpenLDAP specific.
+Mainly intended for use with Linux, they may not be portable.
+.TP
+.B LDAP_OPT_X_KEEPALIVE_IDLE
+Sets/gets the number of seconds a connection needs to remain idle
+before TCP starts sending keepalive probes.
+.BR invalue
+must be
+.BR "const int *" ;
+.BR outvalue
+must be
+.BR "int *" .
+.TP
+.B LDAP_OPT_X_KEEPALIVE_PROBES
+Sets/gets the maximum number of keepalive probes TCP should send
+before dropping the connection.
+.BR invalue
+must be
+.BR "const int *" ;
+.BR outvalue
+must be
+.BR "int *" .
+.TP
+.B LDAP_OPT_X_KEEPALIVE_INTERVAL
+Sets/gets the interval in seconds between individual keepalive probes.
+.BR invalue
+must be
+.BR "const int *" ;
+.BR outvalue
+must be
+.BR "int *" .
+.SH TLS OPTIONS
+The TLS options are OpenLDAP specific.
+.\".TP
+.\".B LDAP_OPT_X_TLS
+.\"Sets/gets the TLS mode.
+.TP
+.B LDAP_OPT_X_TLS_CACERTDIR
+Sets/gets the path of the directory containing CA certificates.
+.BR invalue
+must be
+.BR "const char *" ;
+.BR outvalue
+must be
+.BR "char **" ,
+and its contents need to be freed by the caller using
+.BR ldap_memfree (3).
+.TP
+.B LDAP_OPT_X_TLS_CACERTFILE
+Sets/gets the full-path of the CA certificate file.
+.BR invalue
+must be
+.BR "const char *" ;
+.BR outvalue
+must be
+.BR "char **" ,
+and its contents need to be freed by the caller using
+.BR ldap_memfree (3).
+.TP
+.B LDAP_OPT_X_TLS_CERTFILE
+Sets/gets the full-path of the certificate file.
+.BR invalue
+must be
+.BR "const char *" ;
+.BR outvalue
+must be
+.BR "char **" ,
+and its contents need to be freed by the caller using
+.BR ldap_memfree (3).
+.TP
+.B LDAP_OPT_X_TLS_CIPHER
+Gets the cipher being used on an established TLS session.
+.BR outvalue
+must be
+.BR "char **" ,
+and its contents need to be freed by the caller using
+.BR ldap_memfree (3).
+.TP
+.B LDAP_OPT_X_TLS_CIPHER_SUITE
+Sets/gets the allowed cipher suite.
+.BR invalue
+must be
+.BR "const char *" ;
+.BR outvalue
+must be
+.BR "char **" ,
+and its contents need to be freed by the caller using
+.BR ldap_memfree (3).
+.TP
+.B LDAP_OPT_X_TLS_CONNECT_ARG
+Sets/gets the connection callback argument.
+.BR invalue
+must be
+.BR "const void *" ;
+.BR outvalue
+must be
+.BR "void **" .
+.TP
+.B LDAP_OPT_X_TLS_CONNECT_CB
+Sets/gets the connection callback handle.
+.BR invalue
+must be
+.BR "const LDAP_TLS_CONNECT_CB *" ;
+.BR outvalue
+must be
+.BR "LDAP_TLS_CONNECT_CB **" .
+.TP
+.B LDAP_OPT_X_TLS_CRLCHECK
+Sets/gets the CRL evaluation strategy, one of
+.BR LDAP_OPT_X_TLS_CRL_NONE ,
+.BR LDAP_OPT_X_TLS_CRL_PEER ,
+or
+.BR LDAP_OPT_X_TLS_CRL_ALL .
+.BR invalue
+must be
+.BR "const int *" ;
+.BR outvalue
+must be
+.BR "int *" .
+Requires OpenSSL.
+.TP
+.B LDAP_OPT_X_TLS_CRLFILE
+Sets/gets the full-path of the CRL file.
+.BR invalue
+must be
+.BR "const char *" ;
+.BR outvalue
+must be
+.BR "char **" ,
+and its contents need to be freed by the caller using
+.BR ldap_memfree (3).
+This option is only valid for GnuTLS.
+.TP
+.B LDAP_OPT_X_TLS_CTX
+Sets/gets the TLS library context. New TLS sessions will inherit their
+default settings from this library context.
+.BR invalue
+must be
+.BR "const void *" ;
+.BR outvalue
+must be
+.BR "void **" .
+When using the OpenSSL library this is an SSL_CTX*. When using other
+crypto libraries this is a pointer to an OpenLDAP private structure.
+Applications generally should not use this option or attempt to
+manipulate this structure.
+.TP
+.B LDAP_OPT_X_TLS_DHFILE
+Gets/sets the full-path of the file containing the parameters
+for Diffie-Hellman ephemeral key exchange.
+.BR invalue
+must be
+.BR "const char *" ;
+.BR outvalue
+must be
+.BR "char **" ,
+and its contents need to be freed by the caller using
+.BR ldap_memfree (3).
+.TP
+.B LDAP_OPT_X_TLS_ECNAME
+Gets/sets the name of the curve(s) used for
+elliptic curve key exchanges.
+.BR invalue
+must be
+.BR "const char *" ;
+.BR outvalue
+must be
+.BR "char **" ,
+and its contents need to be freed by the caller using
+.BR ldap_memfree (3).
+Ignored by GnuTLS. In GnuTLS a curve may be selected
+in the cipher suite specification.
+.TP
+.B LDAP_OPT_X_TLS_KEYFILE
+Sets/gets the full-path of the certificate key file.
+.BR invalue
+must be
+.BR "const char *" ;
+.BR outvalue
+must be
+.BR "char **" ,
+and its contents need to be freed by the caller using
+.BR ldap_memfree (3).
+.TP
+.B LDAP_OPT_X_TLS_NEWCTX
+Instructs the library to create a new TLS library context.
+.BR invalue
+must be
+.BR "const int *" .
+A non-zero value pointed to by
+.BR invalue
+tells the library to create a context for a server.
+.TP
+.B LDAP_OPT_X_TLS_PEERCERT
+Gets the peer's certificate in DER format from an established TLS session.
+.BR outvalue
+must be
+.BR "struct berval *" ,
+and the data it returns needs to be freed by the caller using
+.BR ldap_memfree (3).
+.TP
+.B LDAP_OPT_X_TLS_PROTOCOL_MAX
+Sets/gets the maximum protocol version.
+.BR invalue
+must be
+.BR "const int *" ;
+.BR outvalue
+must be
+.BR "int *" .
+.TP
+.B LDAP_OPT_X_TLS_PROTOCOL_MIN
+Sets/gets the minimum protocol version.
+.BR invalue
+must be
+.BR "const int *" ;
+.BR outvalue
+must be
+.BR "int *" .
+.TP
+.B LDAP_OPT_X_TLS_RANDOM_FILE
+Sets/gets the random file when
+.B /dev/random
+and
+.B /dev/urandom
+are not available.
+.BR invalue
+must be
+.BR "const char *" ;
+.BR outvalue
+must be
+.BR "char **" ,
+and its contents need to be freed by the caller using
+.BR ldap_memfree (3).
+Ignored by GnuTLS older than version 2.2.
+.TP
+.B LDAP_OPT_X_TLS_REQUIRE_CERT
+Sets/gets the peer certificate checking strategy,
+one of
+.BR LDAP_OPT_X_TLS_NEVER ,
+.BR LDAP_OPT_X_TLS_HARD ,
+.BR LDAP_OPT_X_TLS_DEMAND ,
+.BR LDAP_OPT_X_TLS_ALLOW ,
+.BR LDAP_OPT_X_TLS_TRY .
+.TP
+.B LDAP_OPT_X_TLS_REQUIRE_SAN
+Sets/gets the peer certificate subjectAlternativeName checking strategy,
+one of
+.BR LDAP_OPT_X_TLS_NEVER ,
+.BR LDAP_OPT_X_TLS_HARD ,
+.BR LDAP_OPT_X_TLS_DEMAND ,
+.BR LDAP_OPT_X_TLS_ALLOW ,
+.BR LDAP_OPT_X_TLS_TRY .
+.TP
+.B LDAP_OPT_X_TLS_SSL_CTX
+Gets the TLS session context associated with this handle.
+.BR outvalue
+must be
+.BR "void **" .
+When using the OpenSSL library this is an SSL*. When using other
+crypto libraries this is a pointer to an OpenLDAP private structure.
+Applications generally should not use this option.
+.TP
+.B LDAP_OPT_X_TLS_VERSION
+Gets the TLS version being used on an established TLS session.
+.BR outvalue
+must be
+.BR "char **" ,
+and its contents need to be freed by the caller using
+.BR ldap_memfree (3).
+.TP
+.B LDAP_OPT_X_TLS_PEERKEY_HASH
+Sets the (public) key that the application expects the peer to be using.
+.B invalue
+must be
+.BR "const char *"
+containing the base64 encoding of the expected peer's key or in the format
+.B "<hashalg>:<peerkey hash base64 encoded>"
+where as a TLS session is established, the library will hash the peer's key
+with the provided hash algorithm and compare it with value provided and will
+only allow the session to continue if they match. This happens regardless of
+certificate checking strategy. The list of supported
+.B hashalg
+values depends on the crypto library used, check its documentation to get
+a list.
+.SH ERRORS
+On success, the functions return
+.BR LDAP_OPT_SUCCESS ,
+while they may return
+.B LDAP_OPT_ERROR
+to indicate a generic option handling error.
+Occasionally, more specific errors can be returned, like
+.B LDAP_NO_MEMORY
+to indicate a failure in memory allocation.
+.SH NOTES
+The LDAP libraries with the
+.B LDAP_OPT_REFERRALS
+option set to
+.B LDAP_OPT_ON
+(default value) automatically follow referrals using an anonymous bind.
+Application developers are encouraged to either implement consistent
+referral chasing features, or explicitly disable referral chasing
+by setting that option to
+.BR LDAP_OPT_OFF .
+.P
+The protocol version used by the library defaults to LDAPv2 (now historic),
+which corresponds to the
+.B LDAP_VERSION2
+macro.
+Application developers are encouraged to explicitly set
+.B LDAP_OPT_PROTOCOL_VERSION
+to LDAPv3, using the
+.B LDAP_VERSION3
+macro, or to allow users to select the protocol version.
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_error (3),
+.B RFC 4422
+(http://www.rfc-editor.org),
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_get_option.3.links b/doc/man/man3/ldap_get_option.3.links
new file mode 100644
index 0000000..9105ef0
--- /dev/null
+++ b/doc/man/man3/ldap_get_option.3.links
@@ -0,0 +1 @@
+ldap_set_option.3
diff --git a/doc/man/man3/ldap_get_values.3 b/doc/man/man3/ldap_get_values.3
new file mode 100644
index 0000000..a557c53
--- /dev/null
+++ b/doc/man/man3/ldap_get_values.3
@@ -0,0 +1,102 @@
+.TH LDAP_GET_VALUES 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_get_values, ldap_get_values_len, ldap_count_values \- LDAP attribute value handling routines
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+
+.LP
+.ft B
+char **ldap_get_values(ld, entry, attr)
+.ft
+LDAP *ld;
+LDAPMessage *entry;
+char *attr;
+.LP
+.ft B
+struct berval **ldap_get_values_len(ld, entry, attr)
+.ft
+LDAP *ld;
+LDAPMessage *entry;
+char *attr;
+.LP
+.ft B
+int ldap_count_values(vals)
+.ft
+char **vals;
+.LP
+.ft B
+int ldap_count_values_len(vals)
+.ft
+struct berval **vals;
+.LP
+.ft B
+void ldap_value_free(vals)
+.ft
+char **vals;
+.LP
+.ft B
+void ldap_value_free_len(vals)
+.ft
+struct berval **vals;
+.SH DESCRIPTION
+These routines are used to retrieve and manipulate attribute values
+from an LDAP entry as returned by
+.BR ldap_first_entry (3)
+or
+.BR ldap_next_entry (3).
+.B ldap_get_values()
+takes the \fIentry\fP and the attribute \fIattr\fP
+whose values are desired and returns a NULL-terminated array of the
+attribute's values. \fIattr\fP may be an attribute type as returned
+from
+.BR ldap_first_attribute (3)
+or
+.BR ldap_next_attribute (3),
+or if the attribute type is known it can simply be given.
+.LP
+The number of values in the array can be counted by calling
+.BR ldap_count_values() .
+The array of values returned can be freed by calling
+.BR ldap_value_free() .
+.LP
+If the attribute values are binary in nature, and thus not suitable
+to be returned as an array of char *'s, the
+.B ldap_get_values_len()
+routine can be used instead. It takes the same parameters as
+.BR ldap_get_values() ,
+but returns a NULL-terminated array of pointers
+to berval structures, each containing the length of and a pointer
+to a value.
+.LP
+The number of values in the array can be counted by calling
+.BR ldap_count_values_len() .
+The array of values returned can be freed by calling
+.BR ldap_value_free_len() .
+.SH ERRORS
+If an error occurs in
+.B ldap_get_values()
+or
+.BR ldap_get_values_len() ,
+NULL is returned and the
+.B ld_errno
+field in the \fIld\fP parameter is set to
+indicate the error. See
+.BR ldap_error (3)
+for a description of possible error codes.
+.SH NOTES
+These routines dynamically allocate memory which the caller must free
+using the supplied routines.
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_first_entry (3),
+.BR ldap_first_attribute (3),
+.BR ldap_error (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_get_values.3.links b/doc/man/man3/ldap_get_values.3.links
new file mode 100644
index 0000000..ac2b454
--- /dev/null
+++ b/doc/man/man3/ldap_get_values.3.links
@@ -0,0 +1,5 @@
+ldap_get_values_len.3
+ldap_value_free.3
+ldap_value_free_len.3
+ldap_count_values.3
+ldap_count_values_len.3
diff --git a/doc/man/man3/ldap_memory.3 b/doc/man/man3/ldap_memory.3
new file mode 100644
index 0000000..b3b6bb0
--- /dev/null
+++ b/doc/man/man3/ldap_memory.3
@@ -0,0 +1,50 @@
+.TH LDAP_MEMORY 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_memfree, ldap_memvfree, ldap_memalloc, ldap_memcalloc, ldap_memrealloc, ldap_strdup \- LDAP memory allocation routines
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.B #include <ldap.h>
+.LP
+.BI "void ldap_memfree(void *" p ");"
+.LP
+.BI "void ldap_memvfree(void **" v ");"
+.LP
+.BI "void *ldap_memalloc(ber_len_t " s ");"
+.LP
+.BI "void *ldap_memcalloc(ber_len_t " n ", ber_len_t " s ");"
+.LP
+.BI "void *ldap_memrealloc(void *" p ", ber_len_t " s ");"
+.LP
+.BI "char *ldap_strdup(LDAP_CONST char *" p ");"
+.SH DESCRIPTION
+These routines are used to allocate/deallocate memory used/returned
+by the LDAP library.
+.BR ldap_memalloc (),
+.BR ldap_memcalloc (),
+.BR ldap_memrealloc (),
+and
+.BR ldap_memfree ()
+are used exactly like the standard
+.BR malloc (3),
+.BR calloc (3),
+.BR realloc (3),
+and
+.BR free (3)
+routines, respectively.
+The
+.BR ldap_memvfree ()
+routine is used to free a dynamically allocated array of pointers to
+arbitrary dynamically allocated objects.
+The
+.BR ldap_strdup ()
+routine is used exactly like the standard
+.BR strdup (3)
+routine.
+.SH SEE ALSO
+.BR ldap (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_memory.3.links b/doc/man/man3/ldap_memory.3.links
new file mode 100644
index 0000000..9351ff1
--- /dev/null
+++ b/doc/man/man3/ldap_memory.3.links
@@ -0,0 +1,6 @@
+ldap_memfree.3
+ldap_memvfree.3
+ldap_memalloc.3
+ldap_memcalloc.3
+ldap_memrealloc.3
+ldap_strdup.3
diff --git a/doc/man/man3/ldap_modify.3 b/doc/man/man3/ldap_modify.3
new file mode 100644
index 0000000..9ce3d74
--- /dev/null
+++ b/doc/man/man3/ldap_modify.3
@@ -0,0 +1,134 @@
+.TH LDAP_MODIFY 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_modify_ext, ldap_modify_ext_s \- Perform an LDAP modify operation
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+int ldap_modify_ext(
+.RS
+.ft B
+LDAP *\fIld\fB,
+char *\fIdn\fB,
+LDAPMod *\fImods[]\fB,
+LDAPControl **\fIsctrls\fB,
+LDAPControl **\fIcctrls\fB,
+int *\fImsgidp\fB );
+.RE
+.LP
+.nf
+.ft B
+int ldap_modify_ext_s(
+.RS
+.ft B
+LDAP *\fIld\fB,
+char *\fIdn\fB,
+LDAPMod *\fImods[]\fB,
+LDAPControl **\fIsctrls\fB,
+LDAPControl **\fIcctrls\fB );
+.RE
+.LP
+.nf
+.ft B
+void ldap_mods_free(
+.RS
+.ft B
+LDAPMod **\fImods\fB,
+int \fIfreemods\fB );
+.RE
+.SH DESCRIPTION
+The routine
+.B ldap_modify_ext_s()
+is used to perform an LDAP modify operation.
+\fIdn\fP is the DN of the entry to modify, and \fImods\fP is a
+null-terminated array of modifications to make to the entry. Each element
+of the \fImods\fP array is a pointer to an LDAPMod structure, which is
+defined below.
+.LP
+.nf
+ typedef struct ldapmod {
+ int mod_op;
+ char *mod_type;
+ union {
+ char **modv_strvals;
+ struct berval **modv_bvals;
+ } mod_vals;
+ } LDAPMod;
+ #define mod_values mod_vals.modv_strvals
+ #define mod_bvalues mod_vals.modv_bvals
+.ft
+.fi
+.LP
+The \fImod_op\fP field is used to specify the type of modification to
+perform and should be one of LDAP_MOD_ADD, LDAP_MOD_DELETE, or
+LDAP_MOD_REPLACE. The \fImod_type\fP and \fImod_values\fP fields
+specify the attribute type to modify and a null-terminated array of
+values to add, delete, or replace respectively.
+.LP
+If you need to specify a non-string value (e.g., to add a
+photo or audio attribute value), you should set \fImod_op\fP to the
+logical OR of the operation as above (e.g., LDAP_MOD_REPLACE)
+and the constant LDAP_MOD_BVALUES. In this case, \fImod_bvalues\fP
+should be used instead of \fImod_values\fP, and it should point to
+a null-terminated array of struct bervals, as defined in <lber.h>.
+.LP
+For LDAP_MOD_ADD modifications, the given values are added to the
+entry, creating the attribute if necessary. For LDAP_MOD_DELETE
+modifications, the given values are deleted from the entry, removing
+the attribute if no values remain. If the entire attribute is to be deleted,
+the \fImod_values\fP field should be set to NULL. For LDAP_MOD_REPLACE
+modifications, the attribute will have the listed values after the
+modification, having been created if necessary. All modifications are
+performed in the order in which they are listed.
+.LP
+.B ldap_mods_free()
+can be used to free each element of a NULL-terminated
+array of mod structures. If \fIfreemods\fP is non-zero, the
+\fImods\fP pointer itself is freed as well.
+.LP
+.B ldap_modify_ext_s()
+returns a code indicating success or, in the case of failure,
+indicating the nature of the failure. See
+.BR ldap_error (3)
+for details
+.LP
+The
+.B ldap_modify_ext()
+operation works the same way as
+.BR ldap_modify_ext_s() ,
+except that it is asynchronous. The integer that \fImsgidp\fP points
+to is set to the message id of the modify request. The result of
+the operation can be obtained by calling
+.BR ldap_result (3).
+.LP
+Both
+.B ldap_modify_ext()
+and
+.B ldap_modify_ext_s()
+allows server and client controls to be passed in
+via the sctrls and cctrls parameters, respectively.
+.SH DEPRECATED INTERFACES
+The
+.B ldap_modify()
+and
+.B ldap_modify_s()
+routines are deprecated in favor of the
+.B ldap_modify_ext()
+and
+.B ldap_modify_ext_s()
+routines, respectively.
+.LP
+.so Deprecated
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_error (3),
+.SH ACKNOWLEDGEMENTS
+.so ../Project
+
diff --git a/doc/man/man3/ldap_modify.3.links b/doc/man/man3/ldap_modify.3.links
new file mode 100644
index 0000000..81c6f2a
--- /dev/null
+++ b/doc/man/man3/ldap_modify.3.links
@@ -0,0 +1,4 @@
+ldap_modify_s.3
+ldap_modify_ext.3
+ldap_modify_ext_s.3
+ldap_mods_free.3
diff --git a/doc/man/man3/ldap_modrdn.3 b/doc/man/man3/ldap_modrdn.3
new file mode 100644
index 0000000..3b2e77a
--- /dev/null
+++ b/doc/man/man3/ldap_modrdn.3
@@ -0,0 +1,81 @@
+.TH LDAP_MODRDN 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_modrdn, ldap_modrdn_s, ldap_modrdn2, ldap_modrdn2_s \- Perform an LDAP modify RDN operation
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+int ldap_modrdn(ld, dn, newrdn)
+.ft
+LDAP \(**ld;
+char \(**dn, \(**newrdn;
+.LP
+.ft B
+.LP
+.ft B
+int ldap_modrdn_s(ld, dn, newrdn)
+.ft
+LDAP \(**ld;
+char \(**dn, \(**newrdn;
+.LP
+.ft B
+int ldap_modrdn2(ld, dn, newrdn, deleteoldrdn)
+.ft
+LDAP \(**ld;
+char \(**dn, \(**newrdn;
+int deleteoldrdn;
+.LP
+.ft B
+int ldap_modrdn2_s(ld, dn, newrdn, deleteoldrdn)
+.ft
+LDAP \(**ld;
+char \(**dn, \(**newrdn;
+int deleteoldrdn;
+.SH DESCRIPTION
+The
+.B ldap_modrdn()
+and
+.B ldap_modrdn_s()
+routines perform an LDAP modify
+RDN operation. They both take \fIdn\fP, the DN of the entry whose
+RDN is to be changed, and \fInewrdn\fP, the new RDN to give the entry.
+The old RDN of the entry is never kept as an attribute of the entry.
+.B ldap_modrdn()
+is asynchronous, returning the message id of the operation
+it initiates.
+.B ldap_modrdn_s()
+is synchronous, returning the LDAP error
+code indicating the success or failure of the operation. Use of
+these routines is deprecated. Use the versions described below
+instead.
+.LP
+The
+.B ldap_modrdn2()
+and
+.B ldap_modrdn2_s()
+routines also perform an LDAP
+modify RDN operation, taking the same parameters as above. In addition,
+they both take the \fIdeleteoldrdn\fP parameter which is used as a boolean
+value to indicate whether the old RDN values should be deleted from
+the entry or not.
+.SH ERRORS
+The synchronous (_s) versions of these routines return an LDAP error
+code, either LDAP_SUCCESS or an error if there was trouble.
+The asynchronous versions return \-1 in case
+of trouble, setting the
+.B ld_errno
+field of \fIld\fP. See
+.BR ldap_error (3)
+for more details.
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_error (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_modrdn.3.links b/doc/man/man3/ldap_modrdn.3.links
new file mode 100644
index 0000000..86063e2
--- /dev/null
+++ b/doc/man/man3/ldap_modrdn.3.links
@@ -0,0 +1,3 @@
+ldap_modrdn_s.3
+ldap_modrdn2.3
+ldap_modrdn2_s.3
diff --git a/doc/man/man3/ldap_open.3 b/doc/man/man3/ldap_open.3
new file mode 100644
index 0000000..994032c
--- /dev/null
+++ b/doc/man/man3/ldap_open.3
@@ -0,0 +1,236 @@
+.TH LDAP_OPEN 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_init, ldap_initialize, ldap_open \- Initialize the LDAP library and open a connection to an LDAP server
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+LDAP *ldap_open(host, port)
+.ft
+char *host;
+int port;
+.LP
+.ft B
+LDAP *ldap_init(host, port)
+.ft
+char *host;
+int port;
+.LP
+.ft B
+int ldap_initialize(ldp, uri)
+.ft
+LDAP **ldp;
+char *uri;
+.LP
+.ft B
+int ldap_connect(ldp)
+.ft
+LDAP *ldp;
+.LP
+.ft B
+int ldap_set_urllist_proc(ld, proc, params)
+.ft
+LDAP *ld;
+LDAP_URLLIST_PROC *proc;
+void *params;
+.LP
+.ft B
+int (LDAP_URLLIST_PROC)(ld, urllist, url, params);
+.ft
+LDAP *ld;
+LDAPURLDesc **urllist;
+LDAPURLDesc **url;
+void *params;
+.LP
+.ft B
+#include <openldap.h>
+.LP
+.ft B
+int ldap_init_fd(fd, proto, uri, ldp)
+.ft
+ber_socket_t fd;
+int proto;
+char *uri;
+LDAP **ldp;
+.SH DESCRIPTION
+.LP
+.B ldap_open()
+opens a connection to an LDAP server and allocates an LDAP
+structure which is used to identify
+the connection and to maintain per-connection information.
+.B ldap_init()
+allocates an LDAP structure but does not open an initial connection.
+.B ldap_initialize()
+allocates an LDAP structure but does not open an initial connection.
+.B ldap_init_fd()
+allocates an LDAP structure using an existing connection on the
+provided socket.
+One
+of these routines must be called before any operations are attempted.
+.LP
+.B ldap_open()
+takes \fIhost\fP, the hostname on which the LDAP server is
+running, and \fIport\fP, the port number to which to connect. If the default
+IANA-assigned port of 389 is desired, LDAP_PORT should be specified for
+\fIport\fP. The \fIhost\fP parameter may contain a blank-separated list
+of hosts to try to connect to, and each host may optionally by of the form
+\fIhost:port\fP. If present, the \fI:port\fP overrides the \fIport\fP
+parameter to
+.BR ldap_open() .
+Upon successfully making a connection to an
+LDAP server,
+.B ldap_open()
+returns a pointer to an opaque LDAP structure, which should be passed
+to subsequent calls to
+.BR ldap_bind() ,
+.BR ldap_search() ,
+etc. Certain fields in the LDAP structure can be set to indicate size limit,
+time limit, and how aliases are handled during operations; read and write access
+to those fields must occur by calling
+.BR ldap_get_option (3)
+and
+.BR ldap_set_option (3)
+respectively, whenever possible.
+.LP
+.B
+ldap_init()
+acts just like
+.BR ldap_open() ,
+but does not open a connection
+to the LDAP server. The actual connection open will occur when the
+first operation is attempted.
+.LP
+.B ldap_initialize()
+acts like
+.BR ldap_init() ,
+but it returns an integer indicating either success or the failure reason,
+and it allows to specify details for the connection in the schema portion
+of the URI.
+The
+.I uri
+parameter may be a comma- or whitespace-separated list of URIs
+containing only the
+.IR schema ,
+the
+.IR host ,
+and the
+.I port
+fields.
+Apart from
+.BR ldap ,
+other (non-standard) recognized values of the
+.I schema
+field are
+.B ldaps
+(LDAP over TLS),
+.B ldapi
+(LDAP over IPC),
+and
+.B cldap
+(connectionless LDAP).
+If other fields are present, the behavior is undefined.
+.LP
+At this time,
+.B ldap_open()
+and
+.B ldap_init()
+are deprecated in favor of
+.BR ldap_initialize() ,
+essentially because the latter allows to specify a schema in the URI
+and it explicitly returns an error code.
+.LP
+.B ldap_connect()
+causes a handle created by
+.B ldap_initialize()
+to connect to the server. This is useful in situations where a file
+descriptor is required before a request is performed.
+.LP
+.B ldap_init_fd()
+allows an LDAP structure to be initialized using an already-opened
+connection. The
+.I proto
+parameter should be one of LDAP_PROTO_TCP, LDAP_PROTO_UDP,
+or LDAP_PROTO_IPC
+for a connection using TCP, UDP, or IPC, respectively. The value
+LDAP_PROTO_EXT
+may also be specified if user-supplied sockbuf handlers are going to
+be used. Note that support for UDP is not implemented unless libldap
+was built with LDAP_CONNECTIONLESS defined.
+The
+.I uri
+parameter may optionally be provided for informational purposes.
+.LP
+.B ldap_set_urllist_proc()
+allows to set a function
+.I proc
+of type
+.I LDAP_URLLIST_PROC
+that is called when a successful connection can be established.
+This function receives the list of URIs parsed from the
+.I uri
+string originally passed to
+.BR ldap_initialize() ,
+and the one that successfully connected.
+The function may manipulate the URI list; the typical use consists
+in moving the successful URI to the head of the list,
+so that subsequent attempts to connect to one of the URIs using the same LDAP handle
+will try it first.
+If
+.I ld
+is null,
+.I proc
+is set as a global parameter that is inherited by all handlers
+within the process that are created after the call to
+.BR ldap_set_urllist_proc() .
+By default, no
+.I LDAP_URLLIST_PROC
+is set.
+In a multithreaded environment,
+.B ldap_set_urllist_proc()
+must be called before any concurrent operation using the LDAP handle is started.
+
+Note: the first call into the LDAP library also initializes the global
+options for the library. As such the first call should be single-threaded
+or otherwise protected to insure that only one call is active. It is
+recommended that
+.BR ldap_get_option ()
+or
+.BR ldap_set_option ()
+be used in the program's main thread before any additional threads are created.
+See
+.BR ldap_get_option (3).
+
+.SH ERRORS
+If an error occurs,
+.B ldap_open()
+and
+.B ldap_init()
+will return NULL and
+.I errno
+should be set appropriately.
+.B ldap_initialize()
+and
+.B ldap_init_fd()
+will directly return the LDAP code associated to the error (or
+.I LDAP_SUCCESS
+in case of success);
+.I errno
+should be set as well whenever appropriate.
+.B ldap_set_urllist_proc()
+returns LDAP_OPT_ERROR on error, and LDAP_OPT_SUCCESS on success.
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_bind (3),
+.BR ldap_get_option (3),
+.BR ldap_set_option (3),
+.BR lber-sockbuf (3),
+.BR errno (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_open.3.links b/doc/man/man3/ldap_open.3.links
new file mode 100644
index 0000000..aa34ab7
--- /dev/null
+++ b/doc/man/man3/ldap_open.3.links
@@ -0,0 +1,4 @@
+ldap_init.3
+ldap_initialize.3
+ldap_set_urllist_proc.3
+ldap_init_fd.3
diff --git a/doc/man/man3/ldap_parse_reference.3 b/doc/man/man3/ldap_parse_reference.3
new file mode 100644
index 0000000..21fd733
--- /dev/null
+++ b/doc/man/man3/ldap_parse_reference.3
@@ -0,0 +1,61 @@
+.TH LDAP_PARSE_REFERENCE 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_parse_reference \- Extract referrals and controls from a reference message
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+int ldap_parse_reference( LDAP *ld, LDAPMessage *reference,
+ char ***referralsp, LDAPControl ***serverctrlsp,
+ int freeit )
+.SH DESCRIPTION
+.LP
+The
+.B ldap_parse_reference()
+routine is used to extract referrals and controls from a reference message.
+The \fIreference\fP parameter is a reference message as returned by a
+call to
+.BR ldap_first_reference (3) ,
+.BR ldap_next_reference (3) ,
+.BR ldap_first_message (3) ,
+.BR ldap_next_message (3) ,
+or
+.BR ldap_result (3) .
+.LP
+The \fIreferralsp\fP parameter will be filled in with an allocated array of
+character strings. The strings are copies of the referrals contained in
+the parsed message. The array should be freed by calling
+.BR ldap_value_free (3) .
+If \fIreferralsp\fP is NULL, no referrals are returned.
+If no referrals were returned, \fI*referralsp\fP is set to NULL.
+.LP
+The \fIserverctrlsp\fP parameter will be filled in with an allocated array of
+controls copied from the parsed message. The array should be freed by calling
+.BR ldap_controls_free (3).
+If \fIserverctrlsp\fP is NULL, no controls are returned.
+If no controls were returned, \fI*serverctrlsp\fP is set to NULL.
+.LP
+The \fIfreeit\fP parameter determines whether the parsed message is
+freed or not after the extraction. Any non-zero value will make it
+free the message. The
+.BR ldap_msgfree (3)
+routine can also be used to free the message later.
+.SH ERRORS
+Upon success LDAP_SUCCESS is returned. Otherwise the values of the
+\fIreferralsp\fP and \fIserverctrlsp\fP parameters are undefined.
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_first_reference (3),
+.BR ldap_first_message (3),
+.BR ldap_result (3),
+.BR ldap_get_values (3),
+.BR ldap_controls_free (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_parse_result.3 b/doc/man/man3/ldap_parse_result.3
new file mode 100644
index 0000000..82c7710
--- /dev/null
+++ b/doc/man/man3/ldap_parse_result.3
@@ -0,0 +1,114 @@
+.TH LDAP_PARSE_RESULT 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_parse_result \- Parsing results
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+int ldap_parse_result( LDAP *ld, LDAPMessage *result,
+ int *errcodep, char **matcheddnp, char **errmsgp,
+ char ***referralsp, LDAPControl ***serverctrlsp,
+ int freeit )
+.LP
+.ft B
+int ldap_parse_sasl_bind_result( LDAP *ld, LDAPMessage *result,
+ struct berval **servercredp, int freeit )
+.LP
+.ft B
+int ldap_parse_extended_result( LDAP *ld, LDAPMessage *result,
+ char **retoidp, struct berval **retdatap, int freeit )
+.LP
+.ft B
+int ldap_parse_intermediate( LDAP *ld, LDAPMessage *result,
+ char **retoidp, struct berval **retdatap,
+ LDAPControl ***serverctrlsp, int freeit )
+.SH DESCRIPTION
+.LP
+These routines are used to extract information from a result message.
+They will operate on the first result message in a chain of search
+results (skipping past other message types). They take the \fIresult\fP
+as returned by a call to
+.BR ldap_result (3),
+.BR ldap_search_s (3)
+or
+.BR ldap_search_st (3).
+In addition to
+.BR ldap_parse_result() ,
+the routines
+.B ldap_parse_sasl_bind_result()
+and
+.B ldap_parse_extended_result()
+are used to get all the result information from SASL bind and extended
+operations. To extract information from intermediate responses,
+.B ldap_parse_intermediate()
+can be used.
+.LP
+The \fIerrcodep\fP parameter will be filled in with the result code from
+the result message.
+.LP
+The server might supply a matched DN string in the message indicating
+how much of a name in a request was recognized. The \fImatcheddnp\fP
+parameter will be filled in with this string if supplied, else it will
+be NULL. If a string is returned, it should be freed using
+.BR ldap_memfree (3).
+.LP
+The \fIerrmsgp\fP parameter will be filled in with the error message
+field from the parsed message. This string should be freed using
+.BR ldap_memfree (3).
+.LP
+The \fIreferralsp\fP parameter will be filled in with an allocated array of
+referral strings from the parsed message. This array should be freed using
+.BR ldap_memvfree (3).
+If no referrals were returned, \fI*referralsp\fP is set to NULL.
+.LP
+The \fIserverctrlsp\fP parameter will be filled in with an allocated array of
+controls copied from the parsed message. The array should be freed using
+.BR ldap_controls_free (3).
+If no controls were returned, \fI*serverctrlsp\fP is set to NULL.
+.LP
+The \fIfreeit\fP parameter determines whether the parsed message is
+freed or not after the extraction. Any non-zero value will make it
+free the message. The
+.BR ldap_msgfree (3)
+routine can also be used to free the message later.
+.LP
+For SASL bind results, the \fIservercredp\fP parameter will be filled in
+with an allocated berval structure containing the credentials from the
+server if present. The structure should be freed using
+.BR ber_bvfree (3).
+.LP
+For extended results and intermediate responses, the \fIretoidp\fP parameter will be filled in
+with the dotted-OID text representation of the name of the extended
+operation response. The string should be freed using
+.BR ldap_memfree (3).
+If no OID was returned, \fI*retoidp\fP is set to NULL.
+.LP
+For extended results and intermediate responses, the \fIretdatap\fP parameter will be filled in
+with a pointer to a berval structure containing the data from the
+extended operation response. The structure should be freed using
+.BR ber_bvfree (3).
+If no data were returned, \fI*retdatap\fP is set to NULL.
+.LP
+For all the above result parameters, NULL values can be used in calls
+in order to ignore certain fields.
+.SH ERRORS
+Upon success LDAP_SUCCESS is returned. Otherwise the values of the
+result parameters are undefined.
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_result (3),
+.BR ldap_search (3),
+.BR ldap_memfree (3),
+.BR ldap_memvfree (3),
+.BR ldap_get_values (3),
+.BR ldap_controls_free (3),
+.BR lber-types (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_parse_result.3.links b/doc/man/man3/ldap_parse_result.3.links
new file mode 100644
index 0000000..e2f4755
--- /dev/null
+++ b/doc/man/man3/ldap_parse_result.3.links
@@ -0,0 +1,3 @@
+ldap_parse_sasl_bind_result.3
+ldap_parse_extended_result.3
+ldap_parse_intermediate.3
diff --git a/doc/man/man3/ldap_parse_sort_control.3 b/doc/man/man3/ldap_parse_sort_control.3
new file mode 100644
index 0000000..56bf021
--- /dev/null
+++ b/doc/man/man3/ldap_parse_sort_control.3
@@ -0,0 +1,40 @@
+.TH LDAP_PARSE_SORT-CONTROL 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_parse_sort_control \- Decode the information returned from a search operation that used a server-side sort control
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+int ldap_parse_sort_control(ld, ctrls, returnCode, attribute)
+.ft
+LDAP *ld;
+LDAPControl **ctrls;
+unsigned long *returnCode;
+char **attribute;
+.SH DESCRIPTION
+This function is used to parse the results returned in a search operation
+that uses a server-side sort control.
+.LP
+It takes a null terminated array of LDAPControl structures usually obtained
+by a call to the
+.BR ldap_parse_result
+function. A returncode which points to the sort control result code,and an array
+of LDAPControl structures that list the client controls to use with the search.
+The function also takes an out parameter \fIattribute\fP and if the sort operation
+fails, the server may return a string that indicates the first attribute in the
+sortKey list that caused the failure. If this parameter is NULL, no string is
+returned. If a string is returned, the memory should be freed by calling the
+ldap_memfree function.
+.SH NOTES
+.SH SEE ALSO
+.BR ldap_result (3),
+.BR ldap_controls_free (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_parse_vlv_control.3 b/doc/man/man3/ldap_parse_vlv_control.3
new file mode 100644
index 0000000..be9efae
--- /dev/null
+++ b/doc/man/man3/ldap_parse_vlv_control.3
@@ -0,0 +1,49 @@
+.TH LDAP_PARSE_VLV_CONTROL 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_parse_vlv_control \- Decode the information returned from a search operation that used a VLV (virtual list view) control
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+int ldap_parse_vlv_control( ld, ctrlp, target_posp, list_countp, contextp, errcodep )
+.ft
+LDAP *ld;
+LDAPControl **ctrlp;
+unsigned long *target_posp, *list_countp;
+struct berval **contextp;
+int *errcodep;
+.SH DESCRIPTION
+The
+.B ldap_parse_vlv_control
+is used to decode the information returned from a search operation that used a
+VLV (virtual list view)control. It takes a null terminated array of LDAPControl
+structures, usually obtained by a call to the
+.BR ldap_parse_result function,
+a \fItarget_pos\fP which points to the list index of the target entry. If
+this parameter is NULL, the target position is not returned. The index returned
+is an approximation of the position of the target entry. It is
+not guaranteed to be exact. The parameter \fIlist_countp\fP points to
+the server's estimate of the size of the list. If this parameter is NULL, the
+size is not returned. \fIcontextp\fP is a pointer to the address of a berval
+structure that contains a server-generated context identifier if server returns
+one. If server does not return a context identifier, the server returns a NULL
+in this parameter. If this parameter is set to NULL, the context identifier is
+not returned. You should use this returned context in the next call to
+create a VLV control. When the berval structure is no longer needed, you should
+free the memory by calling the \fIber_bvfree function.e\fP
+\fIerrcodep\fP is an output parameter, which points to the result code returned
+by the server. If this parameter is NULL, the result code is not returned.
+.LP
+See
+ldap.h for a list of possible return codes.
+.SH SEE ALSO
+.BR ldap_search (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_rename.3 b/doc/man/man3/ldap_rename.3
new file mode 100644
index 0000000..497be46
--- /dev/null
+++ b/doc/man/man3/ldap_rename.3
@@ -0,0 +1,66 @@
+.TH LDAP_RENAME 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_rename, ldap_rename_s \- Renames the specified entry.
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+int ldap_rename( ld, dn, newrdn, newparent, deleteoldrdn, sctrls[], cctrls[], msgidp );
+.ft
+LDAP *ld;
+const char *dn, *newrdn, *newparent;
+int deleteoldrdn;
+LDAPControl *sctrls[], *cctrls[];
+int *msgidp);
+.LP
+.ft B
+int ldap_rename_s( ld, dn, newrdn, newparent, deleteoldrdn, sctrls[], cctrls[] );
+.ft
+LDAP *ld;
+const char *dn, *newrdn, *newparent;
+int deleteoldrdn;
+LDAPControl *sctrls[], *cctrls[];
+.SH DESCRIPTION
+These routines are used to perform a LDAP rename operation.
+The function changes the leaf component of an entry's distinguished
+name and optionally moves the entry to a new parent container. The
+.B ldap_rename_s
+performs a rename operation synchronously.
+The method takes \fIdn\fP, which points to the distinguished name of
+the entry whose attribute is being compared, \fInewparent\fP,the distinguished
+name of the entry's new parent. If this parameter is NULL, only the RDN is changed.
+The root DN is specified by passing a zero length string, "".
+\fIdeleteoldrdn\fP specifies whether the old RDN should be retained or deleted.
+Zero indicates that the old RDN should be retained. If you choose this option,
+the attribute will contain both names (the old and the new).
+Non-zero indicates that the old RDN should be deleted.
+\fIserverctrls\fP points to an array of LDAPControl structures that list the
+client controls to use with this extended operation. Use NULL to specify
+no client controls. \fIclientctrls\fP points to an array of LDAPControl
+structures that list the client controls to use with the search.
+.LP
+.B ldap_rename
+works just like
+.B ldap_rename_s,
+but the operation is asynchronous. It returns the message id of the request
+it initiated. The result of this operation can be obtained by calling
+.BR ldap_result(3).
+.SH ERRORS
+.B ldap_rename()
+returns \-1 in case of error initiating the request, and
+will set the \fIld_errno\fP field in the \fIld\fP parameter to
+indicate the error.
+.BR ldap_rename_s()
+returns the LDAP error code resulting from the rename operation.
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_modify (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_rename.3.links b/doc/man/man3/ldap_rename.3.links
new file mode 100644
index 0000000..3281906
--- /dev/null
+++ b/doc/man/man3/ldap_rename.3.links
@@ -0,0 +1 @@
+ldap_rename_s.3
diff --git a/doc/man/man3/ldap_result.3 b/doc/man/man3/ldap_result.3
new file mode 100644
index 0000000..27f0805
--- /dev/null
+++ b/doc/man/man3/ldap_result.3
@@ -0,0 +1,136 @@
+.TH LDAP_RESULT 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_result \- Wait for the result of an LDAP operation
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+int ldap_result( LDAP *ld, int msgid, int all,
+ struct timeval *timeout, LDAPMessage **result );
+
+int ldap_msgfree( LDAPMessage *msg );
+
+int ldap_msgtype( LDAPMessage *msg );
+
+int ldap_msgid( LDAPMessage *msg );
+.ft
+.SH DESCRIPTION
+The
+.B ldap_result()
+routine is used to wait for and return the result of
+an operation previously initiated by one of the LDAP asynchronous
+operation routines (e.g.,
+.BR ldap_search_ext (3),
+.BR ldap_modify_ext (3),
+etc.). Those routines all return \-1 in case of error, and an
+invocation identifier upon successful initiation of the operation. The
+invocation identifier is picked by the library and is guaranteed to be
+unique across the LDAP session. It can be used to request the result
+of a specific operation from
+.B ldap_result()
+through the \fImsgid\fP parameter.
+.LP
+The
+.B ldap_result()
+routine will block or not, depending upon the setting
+of the \fItimeout\fP parameter.
+If timeout is not a NULL pointer, it specifies a maximum
+interval to wait for the selection to complete. If timeout
+is a NULL pointer, the LDAP_OPT_TIMEOUT value set by
+.BR ldap_set_option (3)
+is used. With the default setting,
+the select blocks indefinitely. To
+effect a poll, the timeout argument should be a non-NULL
+pointer, pointing to a zero-valued timeval structure.
+To obtain the behavior of the default setting, bypassing any value set by
+.BR ldap_set_option (3),
+set to -1 the \fItv_sec\fP field of the \fItimeout\fP parameter.
+See
+.BR select (2)
+for further details.
+.LP
+If the result of a specific operation is required, \fImsgid\fP should
+be set to the invocation identifier returned when the operation was
+initiated, otherwise LDAP_RES_ANY or LDAP_RES_UNSOLICITED should be
+supplied to wait for any or unsolicited response.
+.LP
+The \fIall\fP parameter, if non-zero, causes
+.B ldap_result()
+to return all responses with msgid, otherwise only the
+next response is returned. This is commonly used to obtain all
+the responses of a search operation.
+.LP
+A search response is made up of zero or
+more search entries, zero or more search references, and zero or
+more extended partial responses followed by a search result. If
+\fIall\fP is set to 0, search entries will be returned one at a
+time as they come in, via separate calls to
+.BR ldap_result() .
+If it's set to 1, the search
+response will only be returned in its entirety, i.e., after all entries,
+all references, all extended partial responses, and the final search
+result have been received.
+.SH RETURN VALUE
+Upon success, the type of the result received is returned and the
+\fIresult\fP parameter will contain the result of the operation;
+otherwise, the \fIresult\fP parameter is undefined. This
+result should be passed to the LDAP parsing routines,
+.BR ldap_first_message (3)
+and friends, for interpretation.
+.LP
+The possible result types returned are:
+.LP
+.nf
+ LDAP_RES_BIND (0x61)
+ LDAP_RES_SEARCH_ENTRY (0x64)
+ LDAP_RES_SEARCH_REFERENCE (0x73)
+ LDAP_RES_SEARCH_RESULT (0x65)
+ LDAP_RES_MODIFY (0x67)
+ LDAP_RES_ADD (0x69)
+ LDAP_RES_DELETE (0x6b)
+ LDAP_RES_MODDN (0x6d)
+ LDAP_RES_COMPARE (0x6f)
+ LDAP_RES_EXTENDED (0x78)
+ LDAP_RES_INTERMEDIATE (0x79)
+.fi
+.LP
+The
+.B ldap_msgfree()
+routine is used to free the memory allocated for
+result(s) by
+.B ldap_result()
+or
+.BR ldap_search_ext_s (3)
+and friends.
+It takes a pointer to the result or result chain to be freed and returns
+the type of the last message in the chain.
+If the parameter is NULL, the function does nothing and returns zero.
+.LP
+The
+.B ldap_msgtype()
+routine returns the type of a message.
+.LP
+The
+.B ldap_msgid()
+routine returns the message id of a message.
+.SH ERRORS
+.B ldap_result()
+returns \-1 if something bad happens, and zero if the
+timeout specified was exceeded.
+.B ldap_msgtype()
+and
+.B ldap_msgid()
+return \-1 on error.
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_first_message (3),
+.BR select (2)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_result.3.links b/doc/man/man3/ldap_result.3.links
new file mode 100644
index 0000000..1394c6c
--- /dev/null
+++ b/doc/man/man3/ldap_result.3.links
@@ -0,0 +1,3 @@
+ldap_msgfree.3
+ldap_msgtype.3
+ldap_msgid.3
diff --git a/doc/man/man3/ldap_schema.3 b/doc/man/man3/ldap_schema.3
new file mode 100644
index 0000000..1cae152
--- /dev/null
+++ b/doc/man/man3/ldap_schema.3
@@ -0,0 +1,320 @@
+.TH LDAP_SCHEMA 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 2000-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_str2syntax, ldap_syntax2str, ldap_syntax2name, ldap_syntax_free, ldap_str2matchingrule, ldap_matchingrule2str, ldap_matchingrule2name, ldap_matchingrule_free, ldap_str2attributetype, ldap_attributetype2str, ldap_attributetype2name, ldap_attributetype_free, ldap_str2objectclass, ldap_objectclass2str, ldap_objectclass2name, ldap_objectclass_free, ldap_scherr2str \- Schema definition handling routines
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+#include <ldap_schema.h>
+.LP
+.ft B
+LDAPSyntax * ldap_str2syntax(s, code, errp, flags)
+.ft
+const char * s;
+int * code;
+const char ** errp;
+const int flags;
+.LP
+.ft B
+char * ldap_syntax2str(syn)
+.ft
+const LDAPSyntax * syn;
+.LP
+.ft B
+const char * ldap_syntax2name(syn)
+.ft
+LDAPSyntax * syn;
+.LP
+.ft B
+ldap_syntax_free(syn)
+.ft
+LDAPSyntax * syn;
+.LP
+.ft B
+LDAPMatchingRule * ldap_str2matchingrule(s, code, errp, flags)
+.ft
+const char * s;
+int * code;
+const char ** errp;
+const int flags;
+.LP
+.ft B
+char * ldap_matchingrule2str(mr);
+.ft
+const LDAPMatchingRule * mr;
+.LP
+.ft B
+const char * ldap_matchingrule2name(mr)
+.ft
+LDAPMatchingRule * mr;
+.LP
+.ft B
+ldap_matchingrule_free(mr)
+.ft
+LDAPMatchingRule * mr;
+.LP
+.ft B
+LDAPAttributeType * ldap_str2attributetype(s, code, errp, flags)
+.ft
+const char * s;
+int * code;
+const char ** errp;
+const int flags;
+.LP
+.ft B
+char * ldap_attributetype2str(at)
+.ft
+const LDAPAttributeType * at;
+.LP
+.ft B
+const char * ldap_attributetype2name(at)
+.ft
+LDAPAttributeType * at;
+.LP
+.ft B
+ldap_attributetype_free(at)
+.ft
+LDAPAttributeType * at;
+.LP
+.ft B
+LDAPObjectClass * ldap_str2objectclass(s, code, errp, flags)
+.ft
+const char * s;
+int * code;
+const char ** errp;
+const int flags;
+.LP
+.ft B
+char * ldap_objectclass2str(oc)
+.ft
+const LDAPObjectClass * oc;
+.LP
+.ft B
+const char * ldap_objectclass2name(oc)
+.ft
+LDAPObjectClass * oc;
+.LP
+.ft B
+ldap_objectclass_free(oc)
+.ft
+LDAPObjectClass * oc;
+.LP
+.ft B
+char * ldap_scherr2str(code)
+.ft
+int code;
+.SH DESCRIPTION
+These routines are used to parse schema definitions in the syntax
+defined in RFC 4512 into structs and handle these structs. These
+routines handle four kinds of definitions: syntaxes, matching rules,
+attribute types and object classes. For each definition kind, four
+routines are provided.
+.LP
+.B ldap_str2xxx()
+takes a definition in RFC 4512 format in argument
+.IR s
+as a NUL-terminated string and returns, if possible, a pointer to a
+newly allocated struct of the appropriate kind. The caller is
+responsible for freeing the struct by calling
+.B ldap_xxx_free()
+when not needed any longer. The routine returns NULL if some problem
+happened. In this case, the integer pointed at by argument
+.IR code
+will receive an error code (see below the description of
+.B ldap_scherr2str()
+for an explanation of the values) and a pointer to a NUL-terminated
+string will be placed where requested by argument
+.IR errp
+, indicating where in argument
+.IR s
+the error happened, so it must not be freed by the caller. Argument
+.IR flags
+is a bit mask of parsing options controlling the relaxation of the
+syntax recognized. The following values are defined:
+.TP
+.B LDAP_SCHEMA_ALLOW_NONE
+strict parsing according to RFC 4512.
+.TP
+.B LDAP_SCHEMA_ALLOW_NO_OID
+permit definitions that do not contain an initial OID.
+.TP
+.B LDAP_SCHEMA_ALLOW_QUOTED
+permit quotes around some items that should not have them.
+.TP
+.B LDAP_SCHEMA_ALLOW_DESCR
+permit a
+.B descr
+instead of a numeric OID in places where the syntax expect the latter.
+.TP
+.B LDAP_SCHEMA_ALLOW_DESCR_PREFIX
+permit that the initial numeric OID contains a prefix in
+.B descr
+format.
+.TP
+.B LDAP_SCHEMA_ALLOW_ALL
+be very liberal, include all options.
+.LP
+The structures returned are as follows:
+.sp
+.RS
+.nf
+.ne 7
+.ta 8n 16n 32n
+typedef struct ldap_schema_extension_item {
+ char *lsei_name; /* Extension name */
+ char **lsei_values; /* Extension values */
+} LDAPSchemaExtensionItem;
+
+typedef struct ldap_syntax {
+ char *syn_oid; /* OID */
+ char **syn_names; /* Names */
+ char *syn_desc; /* Description */
+ LDAPSchemaExtensionItem **syn_extensions; /* Extension */
+} LDAPSyntax;
+
+typedef struct ldap_matchingrule {
+ char *mr_oid; /* OID */
+ char **mr_names; /* Names */
+ char *mr_desc; /* Description */
+ int mr_obsolete; /* Is obsolete? */
+ char *mr_syntax_oid; /* Syntax of asserted values */
+ LDAPSchemaExtensionItem **mr_extensions; /* Extensions */
+} LDAPMatchingRule;
+
+typedef struct ldap_attributetype {
+ char *at_oid; /* OID */
+ char **at_names; /* Names */
+ char *at_desc; /* Description */
+ int at_obsolete; /* Is obsolete? */
+ char *at_sup_oid; /* OID of superior type */
+ char *at_equality_oid; /* OID of equality matching rule */
+ char *at_ordering_oid; /* OID of ordering matching rule */
+ char *at_substr_oid; /* OID of substrings matching rule */
+ char *at_syntax_oid; /* OID of syntax of values */
+ int at_syntax_len; /* Suggested minimum maximum length */
+ int at_single_value; /* Is single-valued? */
+ int at_collective; /* Is collective? */
+ int at_no_user_mod; /* Are changes forbidden through LDAP? */
+ int at_usage; /* Usage, see below */
+ LDAPSchemaExtensionItem **at_extensions; /* Extensions */
+} LDAPAttributeType;
+
+typedef struct ldap_objectclass {
+ char *oc_oid; /* OID */
+ char **oc_names; /* Names */
+ char *oc_desc; /* Description */
+ int oc_obsolete; /* Is obsolete? */
+ char **oc_sup_oids; /* OIDs of superior classes */
+ int oc_kind; /* Kind, see below */
+ char **oc_at_oids_must; /* OIDs of required attribute types */
+ char **oc_at_oids_may; /* OIDs of optional attribute types */
+ LDAPSchemaExtensionItem **oc_extensions; /* Extensions */
+} LDAPObjectClass;
+.ta
+.fi
+.RE
+.PP
+Some integer fields (those described with a question mark) have a
+truth value, for these fields the possible values are:
+.TP
+.B LDAP_SCHEMA_NO
+The answer to the question is no.
+.TP
+.B LDAP_SCHEMA_YES
+The answer to the question is yes.
+.LP
+For attribute types, the following usages are possible:
+.TP
+.B LDAP_SCHEMA_USER_APPLICATIONS
+the attribute type is non-operational.
+.TP
+.B LDAP_SCHEMA_DIRECTORY_OPERATION
+the attribute type is operational and is pertinent to the directory
+itself, i.e. it has the same value on all servers that provide the
+entry containing this attribute type.
+.TP
+.B LDAP_SCHEMA_DISTRIBUTED_OPERATION
+the attribute type is operational and is pertinent to replication,
+shadowing or other distributed directory aspect. TBC.
+.TP
+.B LDAP_SCHEMA_DSA_OPERATION
+the attribute type is operational and is pertinent to the directory
+server itself, i.e. it may have different values for the same entry
+when retrieved from different servers that provide the entry.
+.LP
+Object classes can be of three kinds:
+.TP
+.B LDAP_SCHEMA_ABSTRACT
+the object class is abstract, i.e. there cannot be entries of this
+class alone.
+.TP
+.B LDAP_SCHEMA_STRUCTURAL
+the object class is structural, i.e. it describes the main role of the
+entry. On some servers, once the entry is created the set of
+structural object classes assigned cannot be changed: none of those
+present can be removed and none other can be added.
+.TP
+.B LDAP_SCHEMA_AUXILIARY
+the object class is auxiliary, i.e. it is intended to go with other,
+structural, object classes. These can be added or removed at any time
+if attribute types are added or removed at the same time as needed by
+the set of object classes resulting from the operation.
+.LP
+Routines
+.B ldap_xxx2name()
+return a canonical name for the definition.
+.LP
+Routines
+.B ldap_xxx2str()
+return a string representation in the format described by RFC 4512 of
+the struct passed in the argument. The string is a newly allocated
+string that must be freed by the caller. These routines may return
+NULL if no memory can be allocated for the string.
+.LP
+.B ldap_scherr2str()
+returns a NUL-terminated string with a text description of the error
+found. This is a pointer to a static area, so it must not be freed by
+the caller. The argument
+.IR code
+comes from one of the parsing routines and can adopt the following
+values:
+.TP
+.B LDAP_SCHERR_OUTOFMEM
+Out of memory.
+.TP
+.B LDAP_SCHERR_UNEXPTOKEN
+Unexpected token.
+.TP
+.B LDAP_SCHERR_NOLEFTPAREN
+Missing opening parenthesis.
+.TP
+.B LDAP_SCHERR_NORIGHTPAREN
+Missing closing parenthesis.
+.TP
+.B LDAP_SCHERR_NODIGIT
+Expecting digit.
+.TP
+.B LDAP_SCHERR_BADNAME
+Expecting a name.
+.TP
+.B LDAP_SCHERR_BADDESC
+Bad description.
+.TP
+.B LDAP_SCHERR_BADSUP
+Bad superiors.
+.TP
+.B LDAP_SCHERR_DUPOPT
+Duplicate option.
+.TP
+.B LDAP_SCHERR_EMPTY
+Unexpected end of data.
+
+.SH SEE ALSO
+.BR ldap (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_schema.3.links b/doc/man/man3/ldap_schema.3.links
new file mode 100644
index 0000000..05e1675
--- /dev/null
+++ b/doc/man/man3/ldap_schema.3.links
@@ -0,0 +1,17 @@
+ldap_str2syntax.3
+ldap_syntax2str.3
+ldap_syntax2name.3
+ldap_syntax_free.3
+ldap_str2matchingrule.3
+ldap_matchingrule2str.3
+ldap_matchingrule2name.3
+ldap_matchingrule_free.3
+ldap_str2attributetype.3
+ldap_attributetype2str.3
+ldap_attributetype2name.3
+ldap_attributetype_free.3
+ldap_str2objectclass.3
+ldap_objectclass2str.3
+ldap_objectclass2name.3
+ldap_objectclass_free.3
+ldap_scherr2str.3
diff --git a/doc/man/man3/ldap_search.3 b/doc/man/man3/ldap_search.3
new file mode 100644
index 0000000..dc58b6d
--- /dev/null
+++ b/doc/man/man3/ldap_search.3
@@ -0,0 +1,144 @@
+.TH LDAP_SEARCH 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_search, ldap_search_s, ldap_search_st, ldap_search_ext, ldap_search_ext_s \- Perform an LDAP search operation
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <sys/types.h>
+#include <ldap.h>
+.LP
+.ft B
+int ldap_search_ext(
+.RS
+LDAP *\fIld\fB,
+char *\fIbase\fB,
+int \fIscope\fB,
+char *\fIfilter\fB,
+char *\fIattrs\fB[],
+int \fIattrsonly\fB,
+LDAPControl **\fIserverctrls\fB,
+LDAPControl **\fIclientctrls\fB,
+struct timeval *\fItimeout\fB,
+int \fIsizelimit\fB,
+int *\fImsgidp\fB );
+.RE
+.LP
+.ft B
+int ldap_search_ext_s(
+.RS
+LDAP *\fIld\fB,
+char *\fIbase\fB,
+int \fIscope\fB,
+char *\fIfilter\fB,
+char *\fIattrs\fB[],
+int \fIattrsonly\fB,
+LDAPControl **\fIserverctrls\fB,
+LDAPControl **\fIclientctrls\fB,
+struct timeval *\fItimeout\fB,
+int \fIsizelimit\fB,
+LDAPMessage **\fIres\fB );
+.RE
+.SH DESCRIPTION
+These routines are used to perform LDAP search operations.
+The
+.B ldap_search_ext_s()
+routine
+does the search synchronously (i.e., not
+returning until the operation completes), providing a pointer
+to the resulting LDAP messages at the location pointed to by
+the \fIres\fP parameter.
+.LP
+The
+.B ldap_search_ext()
+routine is the asynchronous version, initiating the search and returning
+the message id of the operation it initiated in the integer
+pointed to by the \fImsgidp\fP parameter.
+.LP
+The \fIbase\fP parameter is the DN of the entry at which to start the search.
+.LP
+The \fIscope\fP parameter is the scope of the search and should be one
+of LDAP_SCOPE_BASE, to search the object itself, LDAP_SCOPE_ONELEVEL,
+to search the object's immediate children, LDAP_SCOPE_SUBTREE, to
+search the object and all its descendants, or LDAP_SCOPE_CHILDREN,
+to search all of the descendants. Note that the latter requires
+the server support the LDAP Subordinates Search Scope extension.
+.LP
+The \fIfilter\fP is a string representation of the filter to
+apply in the search. The string should conform to the format
+specified in RFC 4515 as extended by RFC 4526. For instance,
+"(cn=Jane Doe)". Note that use of the extension requires the
+server to support the LDAP Absolute True/False Filter extension.
+NULL may be specified to indicate the library should send the
+filter (objectClass=*).
+.LP
+The \fIattrs\fP parameter is a null-terminated array of attribute
+descriptions to return from matching entries.
+If NULL is specified, the return of all user attributes is requested.
+The description "*" (LDAP_ALL_USER_ATTRIBUTES) may be used to request
+all user attributes to be returned.
+The description "+"(LDAP_ALL_OPERATIONAL_ATTRIBUTES) may be used to
+request all operational attributes to be returned. Note that this
+requires the server to support the LDAP All Operational Attribute
+extension.
+To request no attributes, the description "1.1" (LDAP_NO_ATTRS)
+should be listed by itself.
+.LP
+The \fIattrsonly\fP parameter should be set to a non-zero value
+if only attribute descriptions are wanted. It should be set to zero (0)
+if both attributes descriptions and attribute values are wanted.
+.LP
+The \fIserverctrls\fP and \fIclientctrls\fP parameters may be used
+to specify server and client controls, respectively.
+.LP
+The
+.B ldap_search_ext_s()
+routine is the synchronous version of
+.BR ldap_search_ext().
+.LP
+It also returns a code indicating success or, in the
+case of failure, indicating the nature of the failure
+of the operation. See
+.BR ldap_error (3)
+for details.
+.SH NOTES
+Note that both read
+and list functionality are subsumed by these routines,
+by using a filter like "(objectclass=*)" and a scope of LDAP_SCOPE_BASE (to
+emulate read) or LDAP_SCOPE_ONELEVEL (to emulate list).
+.LP
+These routines may dynamically allocate memory. The caller is
+responsible for freeing such memory using supplied deallocation
+routines. Return values are contained in <ldap.h>.
+.LP
+Note that \fIres\fR parameter of
+.B ldap_search_ext_s()
+and
+.B ldap_search_s()
+should be freed with
+.B ldap_msgfree()
+regardless of return value of these functions.
+.SH DEPRECATED INTERFACES
+The
+.B ldap_search()
+routine is deprecated in favor of the
+.B ldap_search_ext()
+routine. The
+.B ldap_search_s()
+and
+.B ldap_search_st()
+routines are deprecated in favor of the
+.B ldap_search_ext_s()
+routine.
+.LP
+.so Deprecated
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_result (3),
+.BR ldap_error (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_search.3.links b/doc/man/man3/ldap_search.3.links
new file mode 100644
index 0000000..d85bf81
--- /dev/null
+++ b/doc/man/man3/ldap_search.3.links
@@ -0,0 +1,4 @@
+ldap_search_s.3
+ldap_search_st.3
+ldap_search_ext.3
+ldap_search_ext_s.3
diff --git a/doc/man/man3/ldap_sort.3 b/doc/man/man3/ldap_sort.3
new file mode 100644
index 0000000..75fe54c
--- /dev/null
+++ b/doc/man/man3/ldap_sort.3
@@ -0,0 +1,21 @@
+.TH LDAP_SORT 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_sort_entries, ldap_sort_values, ldap_sort_strcasecmp \- LDAP sorting routines (deprecated)
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH DESCRIPTION
+The
+.BR ldap_sort_entries (),
+.BR ldap_sort_values (),
+and
+.BR ldap_sort_strcasecmp ()
+are deprecated.
+.LP
+.so Deprecated
+.SH SEE ALSO
+.BR ldap (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_sort.3.links b/doc/man/man3/ldap_sort.3.links
new file mode 100644
index 0000000..7a4be53
--- /dev/null
+++ b/doc/man/man3/ldap_sort.3.links
@@ -0,0 +1,3 @@
+ldap_sort_entries.3
+ldap_sort_values.3
+ldap_sort_strcasecmp.3
diff --git a/doc/man/man3/ldap_sync.3 b/doc/man/man3/ldap_sync.3
new file mode 100644
index 0000000..8fb77f5
--- /dev/null
+++ b/doc/man/man3/ldap_sync.3
@@ -0,0 +1,326 @@
+.TH LDAP_SYNC 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 2006-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_sync_init, ldap_sync_init_refresh_only, ldap_sync_init_refresh_and_persist, ldap_sync_poll \- LDAP sync routines
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.B #include <ldap.h>
+.LP
+.BI "int ldap_sync_init(ldap_sync_t *" ls ", int " mode ");"
+.LP
+.BI "int ldap_sync_init_refresh_only(ldap_sync_t *" ls ");"
+.LP
+.BI "int ldap_sync_init_refresh_and_persist(ldap_sync_t *" ls ");"
+.LP
+.BI "int ldap_sync_poll(ldap_sync_t *" ls ");"
+.LP
+.BI "ldap_sync_t * ldap_sync_initialize(ldap_sync_t *" ls ");"
+.LP
+.BI "void ldap_sync_destroy(ldap_sync_t *" ls ", int " freeit ");"
+.LP
+.BI "typedef int (*" ldap_sync_search_entry_f ")(ldap_sync_t *" ls ","
+.RS
+.BI "LDAPMessage *" msg ", struct berval *" entryUUID ","
+.BI "ldap_sync_refresh_t " phase ");"
+.RE
+.LP
+.BI "typedef int (*" ldap_sync_search_reference_f ")(ldap_sync_t *" ls ","
+.RS
+.BI "LDAPMessage *" msg ");"
+.RE
+.LP
+.BI "typedef int (*" ldap_sync_intermediate_f ")(ldap_sync_t *" ls ","
+.RS
+.BI "LDAPMessage *" msg ", BerVarray " syncUUIDs ","
+.BI "ldap_sync_refresh_t " phase ");"
+.RE
+.LP
+.BI "typedef int (*" ldap_sync_search_result_f ")(ldap_sync_t *" ls ","
+.RS
+.BI "LDAPMessage *" msg ", int " refreshDeletes ");"
+.RE
+.SH DESCRIPTION
+.LP
+These routines provide an interface to the LDAP Content Synchronization
+operation (RFC 4533).
+They require an
+.BR ldap_sync_t
+structure to be set up with parameters required for various phases
+of the operation; this includes setting some handlers for special events.
+All handlers take a pointer to the \fBldap_sync_t\fP structure as the first
+argument, and a pointer to the \fBLDAPMessage\fP structure as received
+from the server by the client library, plus, occasionally, other specific
+arguments.
+
+The members of the \fBldap_sync_t\fP structure are:
+.TP
+.BI "char *" ls_base
+The search base; by default, the
+.B BASE
+option in
+.BR ldap.conf (5).
+.TP
+.BI "int " ls_scope
+The search scope (one of
+.BR LDAP_SCOPE_BASE ,
+.BR LDAP_SCOPE_ONELEVEL ,
+.BR LDAP_SCOPE_SUBORDINATE
+or
+.BR LDAP_SCOPE_SUBTREE ;
+see
+.B ldap.h
+for details).
+.TP
+.BI "char *" ls_filter
+The filter (RFC 4515); by default,
+.BR (objectClass=*) .
+.TP
+.BI "char **" ls_attrs
+The requested attributes; by default
+.BR NULL ,
+indicating all user attributes.
+.TP
+.BI "int " ls_timelimit
+The requested time limit (in seconds); by default
+.BR 0 ,
+to indicate no limit.
+.TP
+.BI "int " ls_sizelimit
+The requested size limit (in entries); by default
+.BR 0 ,
+to indicate no limit.
+.TP
+.BI "int " ls_timeout
+The desired timeout during polling with
+.BR ldap_sync_poll (3).
+A value of
+.BR \-1
+means that polling is blocking, so
+.BR ldap_sync_poll (3)
+will not return until a message is received; a value of
+.BR 0
+means that polling returns immediately, no matter if any response
+is available or not; a positive value represents the timeout the
+.BR ldap_sync_poll (3)
+function will wait for response before returning, unless a message
+is received; in that case,
+.BR ldap_sync_poll (3)
+returns as soon as the message is available.
+.TP
+.BI "ldap_sync_search_entry_f " ls_search_entry
+A function that is called whenever an entry is returned.
+The
+.BR msg
+argument is the
+.BR LDAPMessage
+that contains the searchResultEntry; it can be parsed using the regular
+client API routines, like
+.BR ldap_get_dn (3),
+.BR ldap_first_attribute (3),
+and so on.
+The
+.BR entryUUID
+argument contains the entryUUID of the entry.
+The
+.BR phase
+argument indicates the type of operation: one of
+.BR LDAP_SYNC_CAPI_PRESENT ,
+.BR LDAP_SYNC_CAPI_ADD ,
+.BR LDAP_SYNC_CAPI_MODIFY ,
+.BR LDAP_SYNC_CAPI_DELETE ;
+in case of
+.BR LDAP_SYNC_CAPI_PRESENT
+or
+.BR LDAP_SYNC_CAPI_DELETE ,
+only the DN is contained in the
+.IR LDAPMessage ;
+in case of
+.BR LDAP_SYNC_CAPI_MODIFY ,
+the whole entry is contained in the
+.IR LDAPMessage ,
+and the application is responsible of determining the differences
+between the new view of the entry provided by the caller and the data
+already known.
+.TP
+.BI "ldap_sync_search_reference_f " ls_search_reference
+A function that is called whenever a search reference is returned.
+The
+.BR msg
+argument is the
+.BR LDAPMessage
+that contains the searchResultReference; it can be parsed using
+the regular client API routines, like
+.BR ldap_parse_reference (3).
+.TP
+.BI "ldap_sync_intermediate_f " ls_intermediate
+A function that is called whenever something relevant occurs during
+the refresh phase of the search, which is marked by
+an \fIintermediateResponse\fP message type.
+The
+.BR msg
+argument is the
+.BR LDAPMessage
+that contains the intermediate response; it can be parsed using
+the regular client API routines, like
+.BR ldap_parse_intermediate (3).
+The
+.BR syncUUIDs
+argument contains an array of UUIDs of the entries that depends
+on the value of the
+.BR phase
+argument.
+In case of
+.BR LDAP_SYNC_CAPI_PRESENTS ,
+the "present" phase is being entered;
+this means that the following sequence of results will consist
+in entries in "present" sync state.
+In case of
+.BR LDAP_SYNC_CAPI_DELETES ,
+the "deletes" phase is being entered;
+this means that the following sequence of results will consist
+in entries in "delete" sync state.
+In case of
+.BR LDAP_SYNC_CAPI_PRESENTS_IDSET ,
+the message contains a set of UUIDs of entries that are present;
+it replaces a "presents" phase.
+In case of
+.BR LDAP_SYNC_CAPI_DELETES_IDSET ,
+the message contains a set of UUIDs of entries that have been deleted;
+it replaces a "deletes" phase.
+In case of
+.BR LDAP_SYNC_CAPI_DONE,
+a "presents" phase with "refreshDone" set to "TRUE" has been returned
+to indicate that the refresh phase of refreshAndPersist is over, and
+the client should start polling.
+Except for the
+.BR LDAP_SYNC_CAPI_PRESENTS_IDSET
+and
+.BR LDAP_SYNC_CAPI_DELETES_IDSET
+cases,
+.BR syncUUIDs
+is NULL.
+.BR
+.TP
+.BI "ldap_sync_search_result_f " ls_search_result
+A function that is called whenever a searchResultDone is returned.
+In refreshAndPersist this can only occur when the server decides
+that the search must be interrupted.
+The
+.BR msg
+argument is the
+.BR LDAPMessage
+that contains the response; it can be parsed using
+the regular client API routines, like
+.BR ldap_parse_result (3).
+The
+.BR refreshDeletes
+argument is not relevant in this case; it should always be \-1.
+.TP
+.BI "void *" ls_private
+A pointer to private data. The client may register here
+a pointer to data the handlers above may need.
+.TP
+.BI "LDAP *" ls_ld
+A pointer to a LDAP structure that is used to connect to the server.
+It is the responsibility of the client to initialize the structure
+and to provide appropriate authentication and security in place.
+
+.SH "GENERAL USE"
+A
+.B ldap_sync_t
+structure is initialized by calling
+.BR ldap_sync_initialize(3).
+This simply clears out the contents of an already existing
+.B ldap_sync_t
+structure, and sets appropriate values for some members.
+After that, the caller is responsible for setting up the
+connection (member
+.BR ls_ld ),
+eventually setting up transport security (TLS),
+for binding and any other initialization.
+The caller must also fill all the documented search-related fields
+of the
+.B ldap_sync_t
+structure.
+
+At the end of a session, the structure can be cleaned up by calling
+.BR ldap_sync_destroy (3),
+which takes care of freeing all data assuming it was allocated by
+.BR ldap_mem* (3)
+routines.
+Otherwise, the caller should take care of destroying and zeroing out
+the documented search-related fields, and call
+.BR ldap_sync_destroy (3)
+to free undocumented members set by the API.
+
+.SH "REFRESH ONLY"
+The
+.BR refreshOnly
+functionality is obtained by periodically calling
+.BR ldap_sync_init (3)
+with mode set to
+.BR LDAP_SYNC_REFRESH_ONLY ,
+or, which is equivalent, by directly calling
+.BR ldap_sync_init_refresh_only (3).
+The state of the search, and the consistency of the search parameters,
+is preserved across calls by passing the
+.B ldap_sync_t
+structure as left by the previous call.
+
+.SH "REFRESH AND PERSIST"
+The
+.BR refreshAndPersist
+functionality is obtained by calling
+.BR ldap_sync_init (3)
+with mode set to
+.BR LDAP_SYNC_REFRESH_AND_PERSIST ,
+or, which is equivalent, by directly calling
+.BR ldap_sync_init_refresh_and_persist (3)
+and, after a successful return, by repeatedly polling with
+.BR ldap_sync_poll (3)
+according to the desired pattern.
+
+A client may insert a call to
+.BR ldap_sync_poll (3)
+into an external loop to check if any modification was returned;
+in this case, it might be appropriate to set
+.BR ls_timeout
+to 0, or to set it to a finite, small value.
+Otherwise, if the client's main purpose consists in waiting for
+responses, a timeout of \-1 is most suitable, so that the function
+only returns after some data has been received and handled.
+
+.SH ERRORS
+All routines return any LDAP error resulting from a lower-level error
+in the API calls they are based on, or LDAP_SUCCESS in case of success.
+.BR ldap_sync_poll (3)
+may return
+.BR LDAP_SYNC_REFRESH_REQUIRED
+if a full refresh is requested by the server.
+In this case, it is appropriate to call
+.BR ldap_sync_init (3)
+again, passing the same
+.B ldap_sync_t
+structure as resulted from any previous call.
+.SH NOTES
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_search_ext (3),
+.BR ldap_result (3) ;
+.B RFC 4533
+(http://www.rfc-editor.org),
+.SH AUTHOR
+Designed and implemented by Pierangelo Masarati, based on RFC 4533
+and loosely inspired by syncrepl code in
+.BR slapd (8).
+.SH ACKNOWLEDGEMENTS
+Initially developed by
+.BR "SysNet s.n.c."
+.B OpenLDAP
+is developed and maintained by The OpenLDAP Project (http://www.openldap.org/).
+.B OpenLDAP
+is derived from University of Michigan LDAP 3.3 Release.
diff --git a/doc/man/man3/ldap_tls.3 b/doc/man/man3/ldap_tls.3
new file mode 100644
index 0000000..4170d42
--- /dev/null
+++ b/doc/man/man3/ldap_tls.3
@@ -0,0 +1,41 @@
+.TH LDAP_TLS 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_start_tls, ldap_start_tls_s, ldap_tls_inplace, ldap_install_tls \- LDAP TLS initialization routines
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.B #include <ldap.h>
+.LP
+.BI "int ldap_start_tls(LDAP *" ld ");"
+.LP
+.BI "int ldap_start_tls_s(LDAP *" ld ", LDAPControl **" serverctrls ", LDAPControl **" clientctrls ");"
+.LP
+.BI "int ldap_tls_inplace(LDAP *" ld ");"
+.LP
+.BI "int ldap_install_tls(LDAP *" ld ");"
+.SH DESCRIPTION
+These routines are used to initiate TLS processing on an LDAP session.
+.BR ldap_start_tls_s ()
+sends a StartTLS request to a server, waits for the reply, and then installs
+TLS handlers on the session if the request succeeded. The routine returns
+.B LDAP_SUCCESS
+if everything succeeded, otherwise it returns an LDAP error code.
+.BR ldap_start_tls ()
+sends a StartTLS request to a server and does nothing else. It returns
+.B LDAP_SUCCESS
+if the request was sent successfully.
+.BR ldap_tls_inplace ()
+returns 1 if TLS handlers have been installed on the specified session, 0
+otherwise.
+.BR ldap_install_tls ()
+installs the TLS handlers on the given session. It returns
+.B LDAP_LOCAL_ERROR
+if TLS is already installed.
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldap_error (3)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man3/ldap_tls.3.links b/doc/man/man3/ldap_tls.3.links
new file mode 100644
index 0000000..d03e2bf
--- /dev/null
+++ b/doc/man/man3/ldap_tls.3.links
@@ -0,0 +1,4 @@
+ldap_start_tls.3
+ldap_start_tls_s.3
+ldap_tls_inplace.3
+ldap_install_tls.3
diff --git a/doc/man/man3/ldap_url.3 b/doc/man/man3/ldap_url.3
new file mode 100644
index 0000000..ec7f343
--- /dev/null
+++ b/doc/man/man3/ldap_url.3
@@ -0,0 +1,83 @@
+.TH LDAP_URL 3 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap_is_ldap_url, ldap_url_parse, ldap_free_urldesc \- LDAP Uniform Resource Locator routines
+.SH LIBRARY
+OpenLDAP LDAP (libldap, \-lldap)
+.SH SYNOPSIS
+.nf
+.ft B
+#include <ldap.h>
+.LP
+.ft B
+int ldap_is_ldap_url( const char *url )
+.LP
+.ft B
+int ldap_url_parse( const char *url, LDAPURLDesc **ludpp )
+.LP
+typedef struct ldap_url_desc {
+ char * lud_scheme; /* URI scheme */
+ char * lud_host; /* LDAP host to contact */
+ int lud_port; /* port on host */
+ char * lud_dn; /* base for search */
+ char ** lud_attrs; /* list of attributes */
+ int lud_scope; /* a LDAP_SCOPE_... value */
+ char * lud_filter; /* LDAP search filter */
+ char ** lud_exts; /* LDAP extensions */
+ int lud_crit_exts; /* true if any extension is critical */
+ /* may contain additional fields for internal use */
+} LDAPURLDesc;
+.LP
+.ft B
+void ldap_free_urldesc( LDAPURLDesc *ludp );
+.SH DESCRIPTION
+These routines support the use of LDAP URLs (Uniform Resource Locators)
+as detailed in RFC 4516. LDAP URLs look like this:
+.nf
+
+ \fBldap://\fP\fIhostport\fP\fB/\fP\fIdn\fP[\fB?\fP\fIattrs\fP[\fB?\fP\fIscope\fP[\fB?\fP\fIfilter\fP[\fB?\fP\fIexts\fP]]]]
+
+where:
+ \fIhostport\fP is a host name with an optional ":portnumber"
+ \fIdn\fP is the search base
+ \fIattrs\fP is a comma separated list of attributes to request
+ \fIscope\fP is one of these three strings:
+ base one sub (default=base)
+ \fIfilter\fP is filter
+ \fIexts\fP are recognized set of LDAP and/or API extensions.
+
+Example:
+ ldap://ldap.example.net/dc=example,dc=net?cn,sn?sub?(cn=*)
+
+.fi
+.LP
+URLs that are wrapped in angle-brackets and/or preceded by "URL:" are also
+tolerated. Alternative LDAP schemes such as ldaps:// and ldapi:// may be
+parsed using the below routines as well.
+.LP
+.B ldap_is_ldap_url()
+returns a non-zero value if \fIurl\fP looks like an LDAP URL (as
+opposed to some other kind of URL). It can be used as a quick check
+for an LDAP URL; the
+.B ldap_url_parse()
+routine should be used if a more thorough check is needed.
+.LP
+.B ldap_url_parse()
+breaks down an LDAP URL passed in \fIurl\fP into its component pieces.
+If successful, zero is returned, an LDAP URL description is
+allocated, filled in, and \fIludpp\fP is set to point to it. If an
+error occurs, a non-zero URL error code is returned.
+.LP
+.B ldap_free_urldesc()
+should be called to free an LDAP URL description that was obtained from
+a call to
+.B ldap_url_parse().
+.SH SEE ALSO
+.nf
+.BR ldap (3)
+.BR "RFC 4516" " <http://www.rfc-editor.org/rfc/rfc4516.txt>"
+.SH ACKNOWLEDGEMENTS
+.fi
+.so ../Project
diff --git a/doc/man/man3/ldap_url.3.links b/doc/man/man3/ldap_url.3.links
new file mode 100644
index 0000000..90fe023
--- /dev/null
+++ b/doc/man/man3/ldap_url.3.links
@@ -0,0 +1,3 @@
+ldap_is_ldap_url.3
+ldap_url_parse.3
+ldap_free_urldesc.3
diff --git a/doc/man/man5/Makefile.in b/doc/man/man5/Makefile.in
new file mode 100644
index 0000000..edfb106
--- /dev/null
+++ b/doc/man/man5/Makefile.in
@@ -0,0 +1,16 @@
+# man5 Makefile.in for OpenLDAP
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+MANSECT=5
diff --git a/doc/man/man5/ldap.conf.5 b/doc/man/man5/ldap.conf.5
new file mode 100644
index 0000000..df357ab
--- /dev/null
+++ b/doc/man/man5/ldap.conf.5
@@ -0,0 +1,529 @@
+.TH LDAP.CONF 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldap.conf, .ldaprc \- LDAP configuration file/environment variables
+.SH SYNOPSIS
+ETCDIR/ldap.conf, ldaprc, .ldaprc, $LDAP<option-name>
+.SH DESCRIPTION
+If the environment variable \fBLDAPNOINIT\fP is defined, all
+defaulting is disabled.
+.LP
+The
+.I ldap.conf
+configuration file is used to set system-wide defaults to be applied when
+running
+.I ldap
+clients.
+.LP
+Users may create an optional configuration file,
+.I ldaprc
+or
+.IR .ldaprc ,
+in their home directory which will be used to override the system-wide
+defaults file.
+The file
+.I ldaprc
+in the current working directory is also used.
+.LP
+.LP
+Additional configuration files can be specified using
+the \fBLDAPCONF\fP and \fBLDAPRC\fP environment variables.
+\fBLDAPCONF\fP may be set to the path of a configuration file. This
+path can be absolute or relative to the current working directory.
+The \fBLDAPRC\fP, if defined, should be the basename of a file
+in the current working directory or in the user's home directory.
+.LP
+Environmental variables may also be used to augment the file based defaults.
+The name of the variable is the option name with an added prefix of \fBLDAP\fP.
+For example, to define \fBBASE\fP via the environment, set the variable
+\fBLDAPBASE\fP to the desired value.
+.LP
+Some options are user-only. Such options are ignored if present
+in the
+.I ldap.conf
+(or file specified by
+.BR LDAPCONF ).
+.LP
+Thus the following files and variables are read, in order:
+.nf
+ variable $LDAPNOINIT, and if that is not set:
+ system file ETCDIR/ldap.conf,
+ user files $HOME/ldaprc, $HOME/.ldaprc, ./ldaprc,
+ system file $LDAPCONF,
+ user files $HOME/$LDAPRC, $HOME/.$LDAPRC, ./$LDAPRC,
+ variables $LDAP<uppercase option name>.
+.fi
+Settings late in the list override earlier ones.
+.SH SYNTAX
+The configuration options are case-insensitive;
+their value, on a case by case basis, may be case-sensitive.
+.LP
+Blank lines are ignored.
+.br
+Lines beginning with a hash mark (`#') are comments, and ignored.
+.LP
+Valid lines are made of an option's name (a sequence of non-blanks,
+conventionally written in uppercase, although not required),
+followed by a value.
+The value starts with the first non-blank character after
+the option's name, and terminates at the end of the line,
+or at the last sequence of blanks before the end of the line.
+The tokenization of the value, if any, is delegated to the handler(s)
+for that option, if any. Quoting values that contain blanks
+may be incorrect, as the quotes would become part of the value.
+For example,
+
+.nf
+ # Wrong - erroneous quotes:
+ URI "ldap:// ldaps://"
+
+ # Right - space-separated list of URIs, without quotes:
+ URI ldap:// ldaps://
+
+ # Right - DN syntax needs quoting for Example, Inc:
+ BASE ou=IT staff,o="Example, Inc",c=US
+ # or:
+ BASE ou=IT staff,o=Example\\2C Inc,c=US
+
+ # Wrong - comment on same line as option:
+ DEREF never # Never follow aliases
+.fi
+.LP
+A line cannot be longer than LINE_MAX, which should be more than 2000 bytes
+on all platforms.
+There is no mechanism to split a long line on multiple lines, either for
+beautification or to overcome the above limit.
+.SH OPTIONS
+The different configuration options are:
+.TP
+.B URI <ldap[si]://[name[:port]] ...>
+Specifies the URI(s) of an LDAP server(s) to which the
+.I LDAP
+library should connect. The URI scheme may be any of
+.BR ldap ,
+.B ldaps
+or
+.BR ldapi ,
+which refer to LDAP over TCP, LDAP over SSL (TLS) and LDAP
+over IPC (UNIX domain sockets), respectively.
+Each server's name can be specified as a
+domain-style name or an IP address literal. Optionally, the
+server's name can followed by a ':' and the port number the LDAP
+server is listening on. If no port number is provided, the default
+port for the scheme is used (389 for ldap://, 636 for ldaps://).
+For LDAP over IPC,
+.B name
+is the name of the socket, and no
+.B port
+is required, nor allowed; note that directory separators must be
+URL-encoded, like any other characters that are special to URLs;
+so the socket
+
+ /usr/local/var/ldapi
+
+must be specified as
+
+ ldapi://%2Fusr%2Flocal%2Fvar%2Fldapi
+
+A space separated list of URIs may be provided.
+.TP
+.B BASE <base>
+Specifies the default base DN to use when performing ldap operations.
+The base must be specified as a Distinguished Name in LDAP format.
+.TP
+.B BINDDN <dn>
+Specifies the default bind DN to use when performing ldap operations.
+The bind DN must be specified as a Distinguished Name in LDAP format.
+.B This is a user-only option.
+.TP
+.B DEREF <when>
+Specifies how alias dereferencing is done when performing a search. The
+.B <when>
+can be specified as one of the following keywords:
+.RS
+.TP
+.B never
+Aliases are never dereferenced. This is the default.
+.TP
+.B searching
+Aliases are dereferenced in subordinates of the base object, but
+not in locating the base object of the search.
+.TP
+.B finding
+Aliases are only dereferenced when locating the base object of the search.
+.TP
+.B always
+Aliases are dereferenced both in searching and in locating the base object
+of the search.
+.RE
+.TP
+.TP
+.B HOST <name[:port] ...>
+Specifies the name(s) of an LDAP server(s) to which the
+.I LDAP
+library should connect. Each server's name can be specified as a
+domain-style name or an IP address and optionally followed by a ':' and
+the port number the ldap server is listening on. A space separated
+list of hosts may be provided.
+.B HOST
+is deprecated in favor of
+.BR URI .
+.TP
+.B KEEPALIVE_IDLE
+Sets/gets the number of seconds a connection needs to remain idle
+before TCP starts sending keepalive probes. Linux only.
+.TP
+.B KEEPALIVE_PROBES
+Sets/gets the maximum number of keepalive probes TCP should send
+before dropping the connection. Linux only.
+.TP
+.B KEEPALIVE_INTERVAL
+Sets/gets the interval in seconds between individual keepalive probes.
+Linux only.
+.TP
+.B NETWORK_TIMEOUT <integer>
+Specifies the timeout (in seconds) after which the poll(2)/select(2)
+following a connect(2) returns in case of no activity.
+.TP
+.B PORT <port>
+Specifies the default port used when connecting to LDAP servers(s).
+The port may be specified as a number.
+.B PORT
+is deprecated in favor of
+.BR URI.
+.TP
+.B REFERRALS <on/true/yes/off/false/no>
+Specifies if the client should automatically follow referrals returned
+by LDAP servers.
+The default is on.
+Note that the command line tools
+.BR ldapsearch (1)
+&co always override this option.
+.\" This should only be allowed via ldap_set_option(3)
+.\".TP
+.\".B RESTART <on/true/yes/off/false/no>
+.\"Determines whether the library should implicitly restart connections (FIXME).
+.TP
+.B SIZELIMIT <integer>
+Specifies a size limit (number of entries) to use when performing searches.
+The number should be a non-negative integer. \fISIZELIMIT\fP of zero (0)
+specifies a request for unlimited search size. Please note that the server
+may still apply any server-side limit on the amount of entries that can be
+returned by a search operation.
+.TP
+.B SOCKET_BIND_ADDRESSES <IP>
+Specifies the source bind IP to be used for connecting to target LDAP server.
+Multiple IP addresses must be space separated. Only one valid IPv4
+address and/or one valid IPv6 address are allowed in the list.
+.TP
+.B TIMELIMIT <integer>
+Specifies a time limit (in seconds) to use when performing searches.
+The number should be a non-negative integer. \fITIMELIMIT\fP of zero (0)
+specifies unlimited search time to be used. Please note that the server
+may still apply any server-side limit on the duration of a search operation.
+.TP
+.B VERSION {2|3}
+Specifies what version of the LDAP protocol should be used.
+.TP
+.B TIMEOUT <integer>
+Specifies a timeout (in seconds) after which calls to synchronous LDAP
+APIs will abort if no response is received. Also used for any
+.BR ldap_result (3)
+calls where a NULL timeout parameter is supplied.
+.SH SASL OPTIONS
+If OpenLDAP is built with Simple Authentication and Security Layer support,
+there are more options you can specify.
+.TP
+.B SASL_MECH <mechanism>
+Specifies the SASL mechanism to use.
+.TP
+.B SASL_REALM <realm>
+Specifies the SASL realm.
+.TP
+.B SASL_AUTHCID <authcid>
+Specifies the authentication identity.
+.B This is a user-only option.
+.TP
+.B SASL_AUTHZID <authcid>
+Specifies the proxy authorization identity.
+.B This is a user-only option.
+.TP
+.B SASL_SECPROPS <properties>
+Specifies Cyrus SASL security properties. The
+.B <properties>
+can be specified as a comma-separated list of the following:
+.RS
+.TP
+.B none
+(without any other properties) causes the properties
+defaults ("noanonymous,noplain") to be cleared.
+.TP
+.B noplain
+disables mechanisms susceptible to simple passive attacks.
+.TP
+.B noactive
+disables mechanisms susceptible to active attacks.
+.TP
+.B nodict
+disables mechanisms susceptible to passive dictionary attacks.
+.TP
+.B noanonymous
+disables mechanisms which support anonymous login.
+.TP
+.B forwardsec
+requires forward secrecy between sessions.
+.TP
+.B passcred
+requires mechanisms which pass client credentials (and allows
+mechanisms which can pass credentials to do so).
+.TP
+.B minssf=<factor>
+specifies the minimum acceptable
+.I security strength factor
+as an integer approximate to effective key length used for
+encryption. 0 (zero) implies no protection, 1 implies integrity
+protection only, 128 allows RC4, Blowfish and other similar ciphers,
+256 will require modern ciphers. The default is 0.
+.TP
+.B maxssf=<factor>
+specifies the maximum acceptable
+.I security strength factor
+as an integer (see
+.B minssf
+description). The default is
+.BR INT_MAX .
+.TP
+.B maxbufsize=<factor>
+specifies the maximum security layer receive buffer
+size allowed. 0 disables security layers. The default is 65536.
+.RE
+.TP
+.B SASL_NOCANON <on/true/yes/off/false/no>
+Do not perform reverse DNS lookups to canonicalize SASL host names. The default is off.
+.TP
+.B SASL_CBINDING <none/tls-unique/tls-endpoint>
+The channel-binding type to use, see also LDAP_OPT_X_SASL_CBINDING. The default is none.
+.SH GSSAPI OPTIONS
+If OpenLDAP is built with Generic Security Services Application Programming Interface support,
+there are more options you can specify.
+.TP
+.B GSSAPI_SIGN <on/true/yes/off/false/no>
+Specifies if GSSAPI signing (GSS_C_INTEG_FLAG) should be used.
+The default is off.
+.TP
+.B GSSAPI_ENCRYPT <on/true/yes/off/false/no>
+Specifies if GSSAPI encryption (GSS_C_INTEG_FLAG and GSS_C_CONF_FLAG)
+should be used. The default is off.
+.TP
+.B GSSAPI_ALLOW_REMOTE_PRINCIPAL <on/true/yes/off/false/no>
+Specifies if GSSAPI based authentication should try to form the
+target principal name out of the ldapServiceName or dnsHostName
+attribute of the targets RootDSE entry. The default is off.
+.SH TLS OPTIONS
+If OpenLDAP is built with Transport Layer Security support, there
+are more options you can specify. These options are used when an
+.B ldaps:// URI
+is selected (by default or otherwise) or when the application
+negotiates TLS by issuing the LDAP StartTLS operation.
+.TP
+.B TLS_CACERT <filename>
+Specifies the file that contains certificates for all of the Certificate
+Authorities the client will recognize.
+.TP
+.B TLS_CACERTDIR <path>
+Specifies the path of a directory that contains Certificate Authority
+certificates in separate individual files. The
+.B TLS_CACERT
+is always used before
+.B TLS_CACERTDIR.
+.TP
+.B TLS_CERT <filename>
+Specifies the file that contains the client certificate.
+.B This is a user-only option.
+.TP
+.B TLS_ECNAME <name>
+Specify the name of the curve(s) to use for Elliptic curve Diffie-Hellman
+ephemeral key exchange. This option is only used for OpenSSL.
+This option is not used with GnuTLS; the curves may be
+chosen in the GnuTLS ciphersuite specification.
+.TP
+.B TLS_KEY <filename>
+Specifies the file that contains the private key that matches the certificate
+stored in the
+.B TLS_CERT
+file. Currently, the private key must not be protected with a password, so
+it is of critical importance that the key file is protected carefully.
+.B This is a user-only option.
+.TP
+.B TLS_CIPHER_SUITE <cipher-suite-spec>
+Specifies acceptable cipher suite and preference order.
+<cipher-suite-spec> should be a cipher specification for
+the TLS library in use (OpenSSL or GnuTLS).
+Example:
+.RS
+.RS
+.TP
+.I OpenSSL:
+TLS_CIPHER_SUITE HIGH:MEDIUM:+SSLv2
+.TP
+.I GnuTLS:
+TLS_CIPHER_SUITE SECURE256:!AES-128-CBC
+.RE
+
+To check what ciphers a given spec selects in OpenSSL, use:
+
+.nf
+ openssl ciphers \-v <cipher-suite-spec>
+.fi
+
+With GnuTLS the available specs can be found in the manual page of
+.BR gnutls\-cli (1)
+(see the description of the
+option
+.BR \-\-priority ).
+
+In older versions of GnuTLS, where gnutls\-cli does not support the option
+\-\-priority, you can obtain the \(em more limited \(em list of ciphers by calling:
+
+.nf
+ gnutls\-cli \-l
+.fi
+.RE
+.TP
+.B TLS_PROTOCOL_MIN <major>[.<minor>]
+Specifies minimum SSL/TLS protocol version that will be negotiated.
+If the server doesn't support at least that version,
+the SSL handshake will fail.
+To require TLS 1.x or higher, set this option to 3.(x+1),
+e.g.,
+
+.nf
+ TLS_PROTOCOL_MIN 3.2
+.fi
+
+would require TLS 1.1.
+Specifying a minimum that is higher than that supported by the
+OpenLDAP implementation will result in it requiring the
+highest level that it does support.
+This parameter is ignored with GnuTLS.
+.TP
+.B TLS_RANDFILE <filename>
+Specifies the file to obtain random bits from when /dev/[u]random is
+not available. Generally set to the name of the EGD/PRNGD socket.
+The environment variable RANDFILE can also be used to specify the filename.
+This parameter is ignored with GnuTLS.
+.TP
+.B TLS_REQCERT <level>
+Specifies what checks to perform on server certificates in a TLS session.
+The
+.B <level>
+can be specified as one of the following keywords:
+.RS
+.TP
+.B never
+The client will not request or check any server certificate.
+.TP
+.B allow
+The server certificate is requested. If a bad certificate is provided, it will
+be ignored and the session proceeds normally.
+.TP
+.B try
+The server certificate is requested. If a bad certificate is provided,
+the session is immediately terminated.
+.TP
+.B demand | hard
+These keywords are equivalent and the same as
+.BR try .
+This is the default setting.
+.RE
+.TP
+.B TLS_REQSAN <level>
+Specifies what checks to perform on the subjectAlternativeName
+(SAN) extensions in a server certificate when validating the certificate
+name against the specified hostname of the server. The
+.B <level>
+can be specified as one of the following keywords:
+.RS
+.TP
+.B never
+The client will not check any SAN in the certificate.
+.TP
+.B allow
+The SAN is checked against the specified hostname. If a SAN is
+present but none match the specified hostname, the SANs are ignored
+and the usual check against the certificate DN is used.
+This is the default setting.
+.TP
+.B try
+The SAN is checked against the specified hostname. If no SAN is present
+in the server certificate, the usual check against the certificate DN
+is used. If a SAN is present but doesn't match the specified hostname,
+the session is immediately terminated. This setting may be preferred
+when a mix of certs with and without SANs are in use.
+.TP
+.B demand | hard
+These keywords are equivalent. The SAN is checked against the specified
+hostname. If no SAN is present in the server certificate, or no SANs
+match, the session is immediately terminated. This setting should be
+used when only certificates with SANs are in use.
+.RE
+.TP
+.B TLS_CRLCHECK <level>
+Specifies if the Certificate Revocation List (CRL) of the CA should be
+used to verify if the server certificates have not been revoked. This
+requires
+.B TLS_CACERTDIR
+parameter to be set. This parameter is ignored with GnuTLS.
+.B <level>
+can be specified as one of the following keywords:
+.RS
+.TP
+.B none
+No CRL checks are performed
+.TP
+.B peer
+Check the CRL of the peer certificate
+.TP
+.B all
+Check the CRL for a whole certificate chain
+.RE
+.TP
+.B TLS_CRLFILE <filename>
+Specifies the file containing a Certificate Revocation List to be used
+to verify if the server certificates have not been revoked. This
+parameter is only supported with GnuTLS.
+.SH "ENVIRONMENT VARIABLES"
+.TP
+LDAPNOINIT
+disable all defaulting
+.TP
+LDAPCONF
+path of a configuration file
+.TP
+LDAPRC
+basename of ldaprc file in $HOME or $CWD
+.TP
+LDAP<option-name>
+Set <option-name> as from ldap.conf
+.SH FILES
+.TP
+.I ETCDIR/ldap.conf
+system-wide ldap configuration file
+.TP
+.I $HOME/ldaprc, $HOME/.ldaprc
+user ldap configuration file
+.TP
+.I $CWD/ldaprc
+local ldap configuration file
+.SH "SEE ALSO"
+.BR ldap (3),
+.BR ldap_set_option (3),
+.BR ldap_result (3),
+.BR openssl (1),
+.BR sasl (3)
+.SH AUTHOR
+Kurt Zeilenga, The OpenLDAP Project
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man5/ldif.5 b/doc/man/man5/ldif.5
new file mode 100644
index 0000000..d3fa232
--- /dev/null
+++ b/doc/man/man5/ldif.5
@@ -0,0 +1,277 @@
+.TH LDIF 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+ldif \- LDAP Data Interchange Format
+.SH DESCRIPTION
+The LDAP Data Interchange Format (LDIF) is used to represent LDAP
+entries and change records in text form. LDAP tools, such as
+.BR ldapadd (1)
+and
+.BR ldapsearch (1),
+read and write LDIF entry
+records.
+.BR ldapmodify (1)
+reads LDIF change records.
+.LP
+This manual page provides a basic description of LDIF. A
+formal specification of LDIF is published in RFC 2849.
+.SH ENTRY RECORDS
+.LP
+LDIF entry records are used to represent directory entries. The basic
+form of an entry record is:
+.LP
+.nf
+.ft tt
+ dn: <distinguished name>
+ <attrdesc>: <attrvalue>
+ <attrdesc>: <attrvalue>
+ <attrdesc>:: <base64-encoded-value>
+ <attrdesc>:< <URL>
+ ...
+.ft
+.fi
+.LP
+The value may be specified as UTF-8 text or as base64 encoded data,
+or a URI may be provided to the location of the attribute value.
+.LP
+A line may be continued by starting the next line with a single space
+or tab, e.g.,
+.LP
+.nf
+.ft tt
+ dn: cn=Barbara J Jensen,dc=exam
+ ple,dc=com
+.ft
+.fi
+.LP
+Lines beginning with a sharp sign ('#') are ignored.
+.LP
+Multiple attribute values are specified on separate lines, e.g.,
+.LP
+.nf
+.ft tt
+ cn: Barbara J Jensen
+ cn: Babs Jensen
+.ft
+.fi
+.LP
+If an value contains a non-printing character, or begins
+with a space or a colon ':', the <attrtype> is followed by a
+double colon and the value is encoded in base 64 notation. e.g.,
+the value " begins with a space" would be encoded like this:
+.LP
+.nf
+.ft tt
+ cn:: IGJlZ2lucyB3aXRoIGEgc3BhY2U=
+.ft
+.fi
+.LP
+If the attribute value is located in a file, the <attrtype> is
+followed by a ':<' and a file: URI. e.g., the value contained
+in the file /tmp/value would be listed like this:
+.LP
+.nf
+.ft tt
+ cn:< file:///tmp/value
+.ft
+.fi
+Other URI schemes (ftp,http) may be supported as well.
+.LP
+Multiple entries within the same LDIF file are separated by blank
+lines.
+.SH ENTRY RECORD EXAMPLE
+Here is an example of an LDIF file containing three entries.
+.LP
+.nf
+.ft tt
+ dn: cn=Barbara J Jensen,dc=example,dc=com
+ cn: Barbara J Jensen
+ cn: Babs Jensen
+ objectclass: person
+ description:< file:///tmp/babs
+ sn: Jensen
+
+ dn: cn=Bjorn J Jensen,dc=example,dc=com
+ cn: Bjorn J Jensen
+ cn: Bjorn Jensen
+ objectclass: person
+ sn: Jensen
+
+ dn: cn=Jennifer J Jensen,dc=example,dc=com
+ cn: Jennifer J Jensen
+ cn: Jennifer Jensen
+ objectclass: person
+ sn: Jensen
+ jpegPhoto:: /9j/4AAQSkZJRgABAAAAAQABAAD/2wBDABALD
+ A4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQ
+ ERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVG
+ ...
+.ft
+.fi
+.LP
+Note that the description in Barbara Jensen's entry is
+read from file:///tmp/babs and the jpegPhoto in Jennifer
+Jensen's entry is encoded using base 64.
+.SH CHANGE RECORDS
+LDIF change records are used to represent directory change requests.
+Each change record starts with line indicating the distinguished
+name of the entry being changed:
+.LP
+.nf
+ dn: <distinguishedname>
+.fi
+.LP
+.nf
+ changetype: <[modify|add|delete|modrdn]>
+.fi
+.LP
+Finally, the change information itself is given, the format of which
+depends on what kind of change was specified above. For a \fIchangetype\fP
+of \fImodify\fP, the format is one or more of the following:
+.LP
+.nf
+ add: <attributetype>
+ <attrdesc>: <value1>
+ <attrdesc>: <value2>
+ ...
+ \-
+.fi
+.LP
+Or, for a replace modification:
+.LP
+.nf
+ replace: <attributetype>
+ <attrdesc>: <value1>
+ <attrdesc>: <value2>
+ ...
+ \-
+.fi
+.LP
+If no \fIattributetype\fP lines are given to replace,
+the entire attribute is to be deleted (if present).
+.LP
+Or, for a delete modification:
+.LP
+.nf
+ delete: <attributetype>
+ <attrdesc>: <value1>
+ <attrdesc>: <value2>
+ ...
+ \-
+.fi
+.LP
+If no \fIattributetype\fP lines are given to delete,
+the entire attribute is to be deleted.
+.LP
+For a \fIchangetype\fP of \fIadd\fP, the format is:
+.LP
+.nf
+ <attrdesc1>: <value1>
+ <attrdesc1>: <value2>
+ ...
+ <attrdescN>: <value1>
+ <attrdescN>: <value2>
+.fi
+.LP
+For a \fIchangetype\fP of \fImodrdn\fP or \fImoddn\fP,
+the format is:
+.LP
+.nf
+ newrdn: <newrdn>
+ deleteoldrdn: 0 | 1
+ newsuperior: <DN>
+.fi
+.LP
+where a value of 1 for deleteoldrdn means to delete the values
+forming the old rdn from the entry, and a value of 0 means to
+leave the values as non-distinguished attributes in the entry.
+The newsuperior line is optional and, if present, specifies the
+new superior to move the entry to.
+.LP
+For a \fIchangetype\fP of \fIdelete\fP, no additional information
+is needed in the record.
+.LP
+Note that attribute values may be presented using base64 or in
+files as described for entry records. Lines in change records
+may be continued in the manner described for entry records as
+well.
+.SH CHANGE RECORD EXAMPLE
+The following sample LDIF file contains a change record
+of each type of change.
+.LP
+.nf
+ dn: cn=Babs Jensen,dc=example,dc=com
+ changetype: add
+ objectclass: person
+ objectclass: extensibleObject
+ cn: babs
+ cn: babs jensen
+ sn: jensen
+
+ dn: cn=Babs Jensen,dc=example,dc=com
+ changetype: modify
+ add: givenName
+ givenName: Barbara
+ givenName: babs
+ \-
+ replace: description
+ description: the fabulous babs
+ \-
+ delete: sn
+ sn: jensen
+ \-
+
+ dn: cn=Babs Jensen,dc=example,dc=com
+ changetype: modrdn
+ newrdn: cn=Barbara J Jensen
+ deleteoldrdn: 0
+ newsuperior: ou=People,dc=example,dc=com
+
+ dn: cn=Barbara J Jensen,ou=People,dc=example,dc=com
+ changetype: delete
+.fi
+
+.SH INCLUDE STATEMENT
+The LDIF parser has been extended to support an
+.B include
+statement for referencing other LDIF files. The
+.B include
+statement must be separated from other records by a blank line.
+The referenced file is specified using a file: URI and all of its
+contents are incorporated as if they were part of the original
+LDIF file. As above, other URI schemes may be supported. For example:
+.LP
+.nf
+ dn: dc=example,dc=com
+ objectclass: domain
+ dc: example
+
+ include: file:///tmp/example.com.ldif
+
+ dn: dc=example,dc=org
+ objectclass: domain
+ dc: example
+.fi
+This feature is not part of the LDIF specification in RFC 2849 but
+is expected to appear in a future revision of this spec. It is supported
+by the
+.BR ldapadd (1),
+.BR ldapmodify (1),
+and
+.BR slapadd (8)
+commands.
+
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldapsearch (1),
+.BR ldapadd (1),
+.BR ldapmodify (1),
+.BR slapadd (8),
+.BR slapcat (8),
+.BR slapd\-ldif (5).
+.LP
+"LDAP Data Interchange Format," Good, G., RFC 2849.
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man5/lloadd.conf.5 b/doc/man/man5/lloadd.conf.5
new file mode 100644
index 0000000..53f50ba
--- /dev/null
+++ b/doc/man/man5/lloadd.conf.5
@@ -0,0 +1,848 @@
+.TH LLOADD.CONF 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+lloadd.conf \- configuration file for lloadd, the stand-alone LDAP daemon
+.SH SYNOPSIS
+ETCDIR/lloadd.conf
+.SH DESCRIPTION
+The file
+.B ETCDIR/lloadd.conf
+contains configuration information for the
+.BR lloadd (8) daemon.
+.LP
+The
+.B lloadd.conf
+file consists of a series of global configuration options that apply to
+.B lloadd
+as a whole (including all backends), followed by zero or more
+backend definitions that contain information specific how a backend
+instance should be contacted.
+The configuration options are case-insensitive;
+their value, on a case by case basis, may be case-sensitive.
+.LP
+The general format of
+.B lloadd.conf
+is as follows:
+.LP
+.nf
+ # comment - these options apply to the server as a whole
+ <global configuration options>
+ # first backend definition
+ backend-server <backend 1 definition>
+ # subsequent backend definitions
+ ...
+.fi
+.LP
+As many backend servers may be configured as desired.
+.LP
+If a line begins with white space, it is considered a continuation
+of the previous line. No physical line should be over 2000 bytes
+long.
+.LP
+Blank lines and comment lines beginning with
+a `#' character are ignored. Note: continuation lines are unwrapped
+before comment processing is applied.
+.LP
+Arguments on configuration lines are separated by white space. If an
+argument contains white space, the argument should be enclosed in
+double quotes. If an argument contains a double quote (`"') or a
+backslash character (`\\'), the character should be preceded by a
+backslash character.
+.LP
+The specific configuration options available are discussed below in the
+Global Configuration Options and General Backend Options.
+Refer to the "OpenLDAP Administrator's Guide" for more
+details on the lloadd configuration file.
+
+.SH SLAPD INTEGRATION
+Note that when
+.B lloadd
+is configured as a
+.B slapd
+module, any option that shares the same name as an option in
+.BR slapd.conf (5),
+the
+.B slapd
+interpretation wins and the
+.B lloadd
+option mentioned is unavailable through
+.BR slapd.conf (5)
+directly, instead, it would have to be configured via a dedicated attribute in
+cn=config. In particular, unless the
+.B TLSShareSlapdCTX
+option is set,
+.B lloadd
+keeps its own TLS context which cannot be configured except
+through the dynamic configuration.
+
+An additional option is available when running as a
+.B slapd
+module:
+.TP
+.B listen "<listen URIs>"
+The URIs the Load Balancer module should listen on. Must not overlap with the
+ones that
+.B slapd
+uses for its own listening sockets. The related
+.B cn=config
+attribute is
+.B olcBkLloadListen
+with each URI provided as a separate value. No changes to this attribute made
+after the server has started up will take effect until it is restarted.
+
+.SH GLOBAL CONFIGURATION OPTIONS
+Options described in this section apply to all backends. Arguments that should
+be replaced by actual text are shown in brackets <>.
+.TP
+.B argsfile <filename>
+The (absolute) name of a file that will hold the
+.B lloadd
+server's command line (program name and options).
+.TP
+.B concurrency <integer>
+Specify a desired level of concurrency. Provided to the underlying
+thread system as a hint. The default is not to provide any hint.
+.\" .TP
+.\" .B gentlehup { on | off }
+.\" A SIGHUP signal will only cause a 'gentle' shutdown-attempt:
+.\" .B Lloadd
+.\" will stop listening for new connections, but will not close the
+.\" connections to the current clients. Future write operations return
+.\" unwilling-to-perform, though. Lloadd terminates when all clients
+.\" have closed their connections (if they ever do), or - as before -
+.\" if it receives a SIGTERM signal. This can be useful if you wish to
+.\" terminate the server and start a new
+.\" .B lloadd
+.\" server
+.\" .B with another database,
+.\" without disrupting the currently active clients.
+.\" The default is off. You may wish to use
+.\" .B idletimeout
+.\" along with this option.
+.\" .TP
+.\" .B idletimeout <integer>
+.\" Specify the number of seconds to wait before forcibly closing
+.\" an idle client connection. A idletimeout of 0 disables this
+.\" feature. The default is 0. You may also want to set the
+.\" .B iotimeout
+.\" option.
+.TP
+.B feature <feature> [...]
+Switch additional features supported by the LDAP Load Balancer on.
+Supported features are:
+.RS
+.RS
+.PD 0
+.TP
+.B proxyauthz
+when proxying an operation, pass the client's authorized identity using
+the proxy authorization control (RFC 4370). No control is added to the
+operation if initiated by a client whose bound identity matches the identity
+configured in
+.B bindconf
+(no normalisation of the DN is attempted).
+
+If SASL binds are issued by clients and this feature is enabled, backend
+servers need to support LDAP Who Am I? extended operation for the Load Balancer
+to detect the correct authorization identity.
+.\" .TP
+.\" .B vc
+.\" when receiving a bind operation from a client, pass it onto a backend
+.\" as a verify credentials external operation request. With this enabled,
+.\" the
+.\" .BR backend 's
+.\" .B bindconns
+.\" option has no effect as there is no need to maintain dedicated bind
+.\" connections anymore.
+.PD
+.RE
+.RE
+.TP
+.B include <filename>
+Read additional configuration information from the given file before
+continuing with the next line of the current file.
+.TP
+.B io-threads <integer>
+Specify the number of threads to use for the connection manager.
+The default is 1 and this is typically adequate for up to 16 CPU cores.
+The value should be set to a power of 2.
+
+If modified after server starts up, a change to this option will not take
+effect until the server has been restarted.
+.TP
+.B logfile <filename>
+Specify a file for recording lloadd debug messages. By default these messages
+only go to stderr, are not recorded anywhere else, and are unrelated to
+messages exposed by the
+.B loglevel
+configuration parameter. Specifying a logfile copies messages to both stderr
+and the logfile.
+.TP
+.B loglevel <integer> [...]
+Specify the level at which debugging statements and operation
+statistics should be syslogged (currently logged to the
+.BR syslogd (8)
+LOG_LOCAL4 facility).
+They must be considered subsystems rather than increasingly verbose
+log levels.
+Some messages with higher priority are logged regardless
+of the configured loglevel as soon as any logging is configured.
+Log levels are additive, and available levels are:
+.RS
+.RS
+.PD 0
+.TP
+.B 1
+.B (0x1 trace)
+trace function calls
+.TP
+.B 2
+.B (0x2 packets)
+debug packet handling
+.TP
+.B 4
+.B (0x4 args)
+heavy trace debugging (function args)
+.TP
+.B 8
+.B (0x8 conns)
+connection management
+.TP
+.B 16
+.B (0x10 BER)
+print out packets sent and received
+.\" .TP
+.\" .B 32
+.\" .B (0x20 filter)
+.\" search filter processing
+.TP
+.B 64
+.B (0x40 config)
+configuration file processing
+.\" .TP
+.\" .B 128
+.\" .B (0x80 ACL)
+.\" access control list processing
+.TP
+.B 256
+.B (0x100 stats)
+connections, LDAP operations, results (recommended)
+.TP
+.B 512
+.B (0x200 stats2)
+stats log entries sent
+.\" .TP
+.\" .B 1024
+.\" .B (0x400 shell)
+.\" print communication with shell backends
+.\" .TP
+.\" .B 2048
+.\" .B (0x800 parse)
+.\" entry parsing
+\".TP
+\".B 4096
+\".B (0x1000 cache)
+\"caching (unused)
+\".TP
+\".B 8192
+\".B (0x2000 index)
+\"data indexing (unused)
+.\" .TP
+.\" .B 16384
+.\" .B (0x4000 sync)
+.\" LDAPSync replication
+.TP
+.B 32768
+.B (0x8000 none)
+only messages that get logged whatever log level is set
+.PD
+.RE
+The desired log level can be input as a single integer that combines
+the (ORed) desired levels, both in decimal or in hexadecimal notation,
+as a list of integers (that are ORed internally),
+or as a list of the names that are shown between parentheses, such that
+.LP
+.nf
+ loglevel 513
+ loglevel 0x201
+ loglevel 512 1
+ loglevel 0x200 0x1
+ loglevel stats trace
+.fi
+.LP
+are equivalent.
+The keyword
+.B any
+can be used as a shortcut to enable logging at all levels (equivalent to \-1).
+The keyword
+.BR none ,
+or the equivalent integer representation, causes those messages
+that are logged regardless of the configured loglevel to be logged.
+In fact, if loglevel is set to 0, no logging occurs,
+so at least the
+.B none
+level is required to have high priority messages logged.
+
+The loglevel defaults to \fBstats\fP.
+This level should usually also be included when using other loglevels, to
+help analyze the logs.
+.RE
+.TP
+.B pidfile <filename>
+The (absolute) name of a file that will hold the
+.B lloadd
+server's process ID (see
+.BR getpid (2)).
+.TP
+.B sockbuf_max_incoming_client <integer>
+Specify the maximum LDAP PDU size accepted coming from clients.
+The default is 262143.
+.TP
+.B sockbuf_max_incoming_upstream <integer>
+Specify the maximum LDAP PDU size accepted coming from upstream
+connections.
+The default is 4194303.
+.TP
+.B tcp-buffer [listener=<URL>] [{read|write}=]<size>
+Specify the size of the TCP buffer.
+A global value for both read and write TCP buffers related to any listener
+is defined, unless the listener is explicitly specified,
+or either the read or write qualifiers are used.
+See
+.BR tcp (7)
+for details.
+Note that some OS-es implement automatic TCP buffer tuning.
+.TP
+.B threads <integer>
+Specify the maximum size of the primary thread pool.
+The default is 16; the minimum value is 2.
+.TP
+.B threadqueues <integer>
+Specify the number of work queues to use for the primary thread pool.
+The default is 1 and this is typically adequate for up to 8 CPU cores.
+The value should not exceed the number of CPUs in the system.
+.TP
+.B max_pdus_per_cycle <integer>
+If set to 0, PDUs are handled by the I/O threads directly, otherwise
+a task is queued to be picked up by the thread pool. This task will
+process PDUs from the connection until there is no more data to be
+read or this limit is reached when the I/O thread can pick it up again.
+Very high values have a potential to cause some connections to be
+starved in a very high-bandwidth environment. The default is 1000.
+.TP
+.B client_max_pending <integer>
+Will cause the load balancer to limit the number unfinished operations for each
+client connection. The default is 0, unlimited.
+.TP
+.B iotimeout <integer>
+Specify the number of milliseconds to wait before forcibly closing
+a connection with an outstanding write. This allows faster recovery from
+various network hang conditions. An iotimeout of 0 disables this feature.
+The default is 10000.
+
+.SH TLS OPTIONS
+If
+.B lloadd
+is built with support for Transport Layer Security, there are more options
+you can specify.
+
+.TP
+.B TLSShareSlapdCTX { on | off }
+If set to no (the default),
+.B lloadd
+will use its own TLS context (needs to be configured via
+.B cn=config
+unless
+.B lloadd
+is run as a standalone daemon). If enabled, the options for
+.B slapd
+apply instead, since the
+.BR slapd 's
+TLS context is used then.
+
+.LP
+
+The following options are available only when compiled as a standalone daemon.
+When compiled as a
+.BR slapd (8)
+module, the cn=config equivalents need to be used if a separate TLS context for
+the module is needed, otherwise use the
+.B TLSShareSlapdCTX
+option.
+
+.TP
+.B TLSCipherSuite <cipher-suite-spec>
+Permits configuring what ciphers will be accepted and the preference order.
+<cipher-suite-spec> should be a cipher specification for the TLS library
+in use (OpenSSL, GnuTLS, or Mozilla NSS).
+Example:
+.RS
+.RS
+.TP
+.I OpenSSL:
+TLSCipherSuite HIGH:MEDIUM:+SSLv2
+.TP
+.I GnuTLS:
+TLSCiphersuite SECURE256:!AES-128-CBC
+.RE
+
+To check what ciphers a given spec selects in OpenSSL, use:
+
+.nf
+ openssl ciphers \-v <cipher-suite-spec>
+.fi
+
+With GnuTLS the available specs can be found in the manual page of
+.BR gnutls\-cli (1)
+(see the description of the
+option
+.BR \-\-priority ).
+
+In older versions of GnuTLS, where gnutls\-cli does not support the option
+\-\-priority, you can obtain the \(em more limited \(em list of ciphers by calling:
+
+.nf
+ gnutls\-cli \-l
+.fi
+
+When using Mozilla NSS, the OpenSSL cipher suite specifications are used and
+translated into the format used internally by Mozilla NSS. There isn't an easy
+way to list the cipher suites from the command line. The authoritative list
+is in the source code for Mozilla NSS in the file sslinfo.c in the structure
+.nf
+ static const SSLCipherSuiteInfo suiteInfo[]
+.fi
+.RE
+.TP
+.B TLSCACertificateFile <filename>
+Specifies the file that contains certificates for all of the Certificate
+Authorities that
+.B lloadd
+will recognize. The certificate for
+the CA that signed the server certificate must be included among
+these certificates. If the signing CA was not a top-level (root) CA,
+certificates for the entire sequence of CA's from the signing CA to
+the top-level CA should be present. Multiple certificates are simply
+appended to the file; the order is not significant.
+.TP
+.B TLSCACertificatePath <path>
+Specifies the path of a directory that contains Certificate Authority
+certificates in separate individual files. Usually only one of this
+or the TLSCACertificateFile is used. This directive is not supported
+when using GnuTLS.
+
+When using Mozilla NSS, <path> may contain a Mozilla NSS cert/key
+database. If <path> contains a Mozilla NSS cert/key database and
+CA cert files, OpenLDAP will use the cert/key database and will
+ignore the CA cert files.
+.TP
+.B TLSCertificateFile <filename>
+Specifies the file that contains the
+.B lloadd
+server certificate.
+
+When using Mozilla NSS, if using a cert/key database (specified with
+TLSCACertificatePath), TLSCertificateFile specifies
+the name of the certificate to use:
+.nf
+ TLSCertificateFile Server-Cert
+.fi
+If using a token other than the internal built in token, specify the
+token name first, followed by a colon:
+.nf
+ TLSCertificateFile my hardware device:Server-Cert
+.fi
+Use certutil \-L to list the certificates by name:
+.nf
+ certutil \-d /path/to/certdbdir \-L
+.fi
+.TP
+.B TLSCertificateKeyFile <filename>
+Specifies the file that contains the
+.B lloadd
+server private key that matches the certificate stored in the
+.B TLSCertificateFile
+file. Currently, the private key must not be protected with a password, so
+it is of critical importance that it is protected carefully.
+
+When using Mozilla NSS, TLSCertificateKeyFile specifies the name of
+a file that contains the password for the key for the certificate specified with
+TLSCertificateFile. The modutil command can be used to turn off password
+protection for the cert/key database. For example, if TLSCACertificatePath
+specifies /etc/openldap/certdb as the location of the cert/key database, use
+modutil to change the password to the empty string:
+.nf
+ modutil \-dbdir /etc/openldap/certdb \-changepw 'NSS Certificate DB'
+.fi
+You must have the old password, if any. Ignore the WARNING about the running
+browser. Press 'Enter' for the new password.
+.TP
+.B TLSDHParamFile <filename>
+This directive specifies the file that contains parameters for Diffie-Hellman
+ephemeral key exchange. This is required in order to use a DSA certificate on
+the server, or an RSA certificate missing the "key encipherment" key usage.
+Note that setting this option may also enable
+Anonymous Diffie-Hellman key exchanges in certain non-default cipher suites.
+Anonymous key exchanges should generally be avoided since they provide no
+actual client or server authentication and provide no protection against
+man-in-the-middle attacks.
+You should append "!ADH" to your cipher suites to ensure that these suites
+are not used.
+When using Mozilla NSS these parameters are always generated randomly
+so this directive is ignored.
+.TP
+.B TLSECName <name>
+Specify the name of a curve to use for Elliptic curve Diffie-Hellman
+ephemeral key exchange. This is required to enable ECDHE algorithms in
+OpenSSL. This option is not used with GnuTLS; the curves may be
+chosen in the GnuTLS ciphersuite specification. This option is also
+ignored for Mozilla NSS.
+.TP
+.B TLSProtocolMin <major>[.<minor>]
+Specifies minimum SSL/TLS protocol version that will be negotiated.
+If the server doesn't support at least that version,
+the SSL handshake will fail.
+To require TLS 1.x or higher, set this option to 3.(x+1),
+e.g.,
+
+.nf
+ TLSProtocolMin 3.2
+.fi
+
+would require TLS 1.1.
+Specifying a minimum that is higher than that supported by the
+OpenLDAP implementation will result in it requiring the
+highest level that it does support.
+This directive is ignored with GnuTLS.
+.TP
+.B TLSRandFile <filename>
+Specifies the file to obtain random bits from when /dev/[u]random
+is not available. Generally set to the name of the EGD/PRNGD socket.
+The environment variable RANDFILE can also be used to specify the filename.
+This directive is ignored with GnuTLS and Mozilla NSS.
+.TP
+.B TLSVerifyClient <level>
+Specifies what checks to perform on client certificates in an
+incoming TLS session, if any.
+The
+.B <level>
+can be specified as one of the following keywords:
+.RS
+.TP
+.B never
+This is the default.
+.B lloadd
+will not ask the client for a certificate.
+.TP
+.B allow
+The client certificate is requested. If no certificate is provided,
+the session proceeds normally. If a bad certificate is provided,
+it will be ignored and the session proceeds normally.
+.TP
+.B try
+The client certificate is requested. If no certificate is provided,
+the session proceeds normally. If a bad certificate is provided,
+the session is immediately terminated.
+.TP
+.B demand | hard | true
+These keywords are all equivalent, for compatibility reasons.
+The client certificate is requested. If no certificate is provided,
+or a bad certificate is provided, the session is immediately terminated.
+.TP
+.B TLSCRLCheck <level>
+Specifies if the Certificate Revocation List (CRL) of the CA should be
+used to verify if the client certificates have not been revoked. This
+requires
+.B TLSCACertificatePath
+parameter to be set. This directive is ignored with GnuTLS and Mozilla NSS.
+.B <level>
+can be specified as one of the following keywords:
+.RS
+.TP
+.B none
+No CRL checks are performed
+.TP
+.B peer
+Check the CRL of the peer certificate
+.TP
+.B all
+Check the CRL for a whole certificate chain
+.RE
+.TP
+.B TLSCRLFile <filename>
+Specifies a file containing a Certificate Revocation List to be used
+for verifying that certificates have not been revoked. This directive is
+only valid when using GnuTLS and Mozilla NSS.
+
+.SH BACKEND CONFIGURATION
+Options in this section describe how the
+.B lloadd
+connects and authenticates to the backend servers.
+
+It is assumed all backend servers serve the same data. On startup, the
+configured connections are set up and those not dedicated to handle bind
+requests are authenticated with the backend using the information in the
+.B bindconf
+option. The authentication configuration is shared between them.
+.TP
+.B bindconf
+.B [bindmethod=simple|sasl]
+.B [binddn=<dn>]
+.B [saslmech=<mech>]
+.B [authcid=<identity>]
+.B [authzid=<identity>]
+.B [credentials=<passwd>]
+.B [realm=<realm>]
+.B [secprops=<properties>]
+.B [timeout=<seconds>]
+.B [network\-timeout=<seconds>]
+.B [tcp\-user\-timeout=<milliseconds>]
+
+Specifies the bind credentials
+.B lloadd
+uses when setting up its regular connections to all backends.
+
+A
+.B bindmethod
+of
+.B simple
+requires the options
+.B binddn
+and
+.B credentials
+and should only be used when adequate security services
+(e.g. TLS or IPSEC) are in place.
+.B REMEMBER: simple bind credentials must be in cleartext!
+A
+.B bindmethod
+of
+.B sasl
+requires the option
+.B saslmech.
+Depending on the mechanism, an authentication identity and/or
+credentials can be specified using
+.B authcid
+and
+.B credentials.
+The
+.B authzid
+parameter may be used to specify an authorization identity.
+Specific security properties (as with the
+.B sasl\-secprops
+keyword above) for a SASL bind can be set with the
+.B secprops
+option. A non default SASL realm can be set with the
+.B realm
+option.
+
+The
+.B timeout
+parameter indicates how long an operation can be pending a response (result,
+search entry, ...) from the server in seconds. Due to how timeouts are
+detected, the timeout might not be detected and handled up to
+.B timeout
+seconds after it happens.
+
+The
+.B network\-timeout
+parameter sets how long the consumer will wait to establish a
+network connection to the provider. Once a connection is
+established, the
+.B timeout
+parameter determines how long the consumer will wait for the initial
+Bind request to complete.
+
+Timeout set to 0 means no timeout is in effect and by default, no timeouts are
+in effect.
+
+The
+.B tcp\-user\-timeout
+parameter, if non-zero, corresponds to the
+.B TCP_USER_TIMEOUT
+set on the upstream connections, overriding the operating system setting.
+Only some systems support the customization of this parameter, it is
+ignored otherwise and system-wide settings are used.
+
+.SH BACKEND OPTIONS
+
+.TP
+.B backend-server
+.B uri=ldap[s]://<hostname>[:port]
+.B [retry=<retry interval in ms>]
+.B [keepalive=<idle>:<probes>:<interval>]
+.B [starttls=yes|critical]
+.B [tls_cert=<file>]
+.B [tls_key=<file>]
+.B [tls_cacert=<file>]
+.B [tls_cacertdir=<path>]
+.B [tls_reqcert=never|allow|try|demand]
+.B [tls_cipher_suite=<ciphers>]
+.B [tls_crlcheck=none|peer|all]
+.B [tls_protocol_min=<major>[.<minor>]]
+.B [numconns=<conns>]
+.B [bindconns=<conns>]
+.B [max-pending-ops=<ops>]
+.B [conn-max-pending=<ops>]
+
+Marks the beginning of a backend definition.
+
+.B uri
+specifies the backend as an LDAP URI. If <port> is not given, the standard
+LDAP port number (389 or 636) is used.
+
+Lloadd will attempt to maintain
+.B numconns
+active connections and
+.\" unless the
+.\" .B vc
+.\" feature is enabled,
+also
+.B bindconns
+active connections dedicated to handling client bind requests.
+
+If an error occurs on a working connection, a new connection attempt is
+made immediately, if one happens on establishing a new connection to this
+backend, lloadd will wait before a new reconnect attempt is made
+according to the
+.B retry
+parameter (default is 5 seconds).
+
+Operations will be distributed across the backend's connections
+.RB ( upstreams ).
+
+The parameter
+.B conn-max-pending
+unless set to
+.B 0
+(the default), will limit the number unfinished operations per upstream
+connection. Similarly,
+.B max-pending-ops
+will limit the total number or unfinished operations across all backend's
+connections,
+.BR 0 ,
+the default, means no limit will be imposed for this backend.
+
+The
+.B keepalive
+parameter sets the values of \fIidle\fP, \fIprobes\fP, and \fIinterval\fP
+used to check whether a socket is alive;
+.I idle
+is the number of seconds a connection needs to remain idle before TCP
+starts sending keepalive probes;
+.I probes
+is the maximum number of keepalive probes TCP should send before dropping
+the connection;
+.I interval
+is interval in seconds between individual keepalive probes.
+Only some systems support the customization of these values;
+the
+.B keepalive
+parameter is ignored otherwise, and system-wide settings are used.
+
+The
+.B starttls
+parameter specifies use of the StartTLS extended operation
+to establish a TLS session before Binding to the provider. If the
+.B critical
+argument is supplied, the session will be aborted if the StartTLS request
+fails. Otherwise the syncrepl session continues without TLS. The
+tls_reqcert setting defaults to "demand" and the other TLS settings
+default to the same as the main slapd TLS settings.
+
+.\" .TP
+.\" .B readonly on | off
+.\" This option puts the backend into "read-only" mode. Only read
+.\" operations (i.e. bind, search, compare) will be directed towards this
+.\" backend. By default, readonly is off.
+.\" .TP
+.\" .B restrict <oplist>
+.\" Specify a whitespace separated list of operations that are restricted.
+.\" If defined inside a database specification, restrictions apply only
+.\" to that database, otherwise they are global.
+.\" Operations can be any of
+.\" .BR add ,
+.\" .BR bind ,
+.\" .BR compare ,
+.\" .BR delete ,
+.\" .BR extended[=<OID>] ,
+.\" .BR modify ,
+.\" .BR rename ,
+.\" .BR search ,
+.\" or the special pseudo-operations
+.\" .B read
+.\" and
+.\" .BR write ,
+.\" which respectively summarize read and write operations.
+.\" The use of
+.\" .I restrict write
+.\" is equivalent to
+.\" .I readonly on
+.\" (see above).
+.\" The
+.\" .B extended
+.\" keyword allows one to indicate the OID of the specific operation
+.\" to be restricted.
+
+.SH EXAMPLES
+.LP
+Here is a short example of a configuration file:
+.LP
+.RS
+.nf
+argsfile LOCALSTATEDIR/run/lloadd.args
+pidfile LOCALSTATEDIR/run/lloadd.pid
+
+bindconf
+ bindmethod=simple
+ binddn=cn=test
+ credentials=pass
+
+backend-server
+ uri=ldap://ldap1.example.com
+ numconns=3
+ bindconns=2
+ retry=5000
+ max-pending-ops=5
+ conn-max-pending=3
+
+backend-server
+ uri=ldap://ldap2.example.com
+ numconns=3
+ bindconns=2
+ retry=5000
+ max-pending-ops=5
+ conn-max-pending=3
+.fi
+.RE
+.LP
+"OpenLDAP Administrator's Guide" contains a longer annotated
+example of a configuration file.
+The original ETCDIR/lloadd.conf is another example.
+
+.SH LIMITATIONS
+Support for proxying SASL Binds is limited to the
+.B EXTERNAL
+mechanism (and only to extract the DN of a client TLS cerificate if used during
+the last renegotiation) and mechanisms that rely neither on connection metadata
+(as Kerberos does) nor establish a SASL integrity/confidentialiy layer (again,
+some Kerberos mechanisms,
+.B DIGEST-MD5
+can negotiate this).
+
+.SH FILES
+.TP
+ETCDIR/lloadd.conf
+default lloadd configuration file
+.SH SEE ALSO
+.BR ldap (3),
+.BR gnutls\-cli (1),
+.BR slapd.conf (5),
+.BR tcp (7),
+.BR lloadd (8),
+.BR slapd (8).
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man5/slapd-asyncmeta.5 b/doc/man/man5/slapd-asyncmeta.5
new file mode 100644
index 0000000..743d3ef
--- /dev/null
+++ b/doc/man/man5/slapd-asyncmeta.5
@@ -0,0 +1,532 @@
+.TH SLAPD-ASYNCMETA 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2016-2022 The OpenLDAP Foundation.
+.\" Portions Copyright 2016 Symas Corporation.
+.\" Copying restrictions apply. See the COPYRIGHT file.
+.\" $OpenLDAP$
+.\"
+
+.SH NAME
+slapd\-asyncmeta \- asynchronous metadirectory backend to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B asyncmeta
+backend to
+.BR slapd (8)
+performs basic LDAP proxying with respect to a set of remote LDAP
+servers, called "targets".
+The information contained in these servers can be presented as
+belonging to a single Directory Information Tree (DIT).
+
+.LP
+A good knowledge of the functionality of the
+.BR slapd\-meta(5)
+backend is recommended. This backend has been designed as
+an asynchronous version of the
+.B meta
+backend. Unlike
+.B meta
+, the operation handling threads are no longer pending
+on the response from the remote server, thus decreasing the
+number of threads necessary to handle the same load. While
+.B asyncmeta
+maintains the functionality of
+.B meta
+and has a largely similar codebase,
+some changes in operation and some new configuration directives have been
+added. Some configuration options, such as
+.B conn\-pool\-max ,
+.B conn\-ttl ,
+.B single\-conn ,
+and
+.B use\-temporary\-conn
+have been removed, as they are no longer relevant.
+.LP
+.B New connection handling:
+.LP
+
+Unlike
+.B meta,
+which caches bound connections, the
+.B asyncmeta
+works with a configured maximum number of connections per target.
+For each request redirected to a target, a different connection is selected.
+Each connection has a queue, to which the request is added before it is sent to the
+remote server, and is removed after the last response for that request is received.
+ For each new request, the connection with the smallest number of pending requests
+is selected, or using round\-robin if the numbers are equal.
+.LP
+.B Overlays:
+.LP
+Due to implementation specifics, there is no guarantee that any of the existing OpenLDAP overlays will work with
+.B asyncmeta
+backend.
+
+.SH EXAMPLES
+Refer to
+.B slapd\-meta(5)
+for configuration examples.
+
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the ASYNCMETA backend database.
+That is, they must follow a "database asyncmeta" line and come before any
+subsequent "backend" or "database" lines.
+Other database options are described in the
+.BR slapd.conf (5)
+manual page.
+
+.SH SPECIAL CONFIGURATION DIRECTIVES
+Target configuration starts with the "uri" directive.
+All the configuration directives that are not specific to targets
+should be defined first for clarity, including those that are common
+to all backends.
+They are:
+
+.TP
+.B default\-target none
+This directive forces the backend to reject all those operations
+that must resolve to a single target in case none or multiple
+targets are selected.
+They include: add, delete, modify, modrdn; compare is not included, as
+well as bind since, as they don't alter entries, in case of multiple
+matches an attempt is made to perform the operation on any candidate
+target, with the constraint that at most one must succeed.
+This directive can also be used when processing targets to mark a
+specific target as default.
+
+.TP
+.B dncache\-ttl {DISABLED|forever|<ttl>}
+This directive sets the time-to-live of the DN cache.
+This caches the target that holds a given DN to speed up target
+selection in case multiple targets would result from an uncached
+search; forever means cache never expires; disabled means no DN
+caching; otherwise a valid ( > 0 ) ttl is required, in the format
+illustrated for the
+.B idle\-timeout
+directive.
+
+.TP
+.B onerr {CONTINUE|report|stop}
+This directive allows one to select the behavior in case an error is returned
+by one target during a search.
+The default, \fBcontinue\fP, consists in continuing the operation,
+trying to return as much data as possible.
+If the value is set to \fBstop\fP, the search is terminated as soon
+as an error is returned by one target, and the error is immediately
+propagated to the client.
+If the value is set to \fBreport\fP, the search is continued to the end
+but, in case at least one target returned an error code, the first
+non-success error code is returned.
+
+.TP
+.B max\-timeout\-ops <number>
+Specify the number of consecutive timed out requests,
+after which the connection will be considered faulty and dropped.
+
+.TP
+.B max\-pending\-ops <number>
+The maximum number of pending requests stored in a connection's queue.
+The default is 128. When this number is exceeded,
+.B LDAP_BUSY
+will be returned to the client.
+
+.TP
+.B max\-target\-conns <number>
+The maximum number of connections per target. Unlike
+.B slapd\-meta(5),
+no new connections will be created
+once this number is reached. The default value is 255.
+
+.TP
+.B norefs <NO|yes>
+If
+.BR yes ,
+do not return search reference responses.
+By default, they are returned unless request is LDAPv2.
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive.
+
+.TP
+.B noundeffilter <NO|yes>
+If
+.BR yes ,
+return success instead of searching if a filter is undefined or contains
+undefined portions.
+By default, the search is propagated after replacing undefined portions
+with
+.BR (!(objectClass=*)) ,
+which corresponds to the empty result set.
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive.
+
+.TP
+.B protocol\-version {0,2,3}
+This directive indicates what protocol version must be used to contact
+the remote server.
+If set to 0 (the default), the proxy uses the same protocol version
+used by the client, otherwise the requested protocol is used.
+The proxy returns \fIunwillingToPerform\fP if an operation that is
+incompatible with the requested protocol is attempted.
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive.
+
+.TP
+.B pseudoroot\-bind\-defer {YES|no}
+This directive, when set to
+.BR yes ,
+causes the authentication to the remote servers with the pseudo-root
+identity (the identity defined in each
+.B idassert-bind
+directive) to be deferred until actually needed by subsequent operations.
+Otherwise, all binds as the rootdn are propagated to the targets.
+
+.TP
+.B quarantine <interval>,<num>[;<interval>,<num>[...]]
+Turns on quarantine of URIs that returned
+.IR LDAP_UNAVAILABLE ,
+so that an attempt to reconnect only occurs at given intervals instead
+of any time a client requests an operation.
+The pattern is: retry only after at least
+.I interval
+seconds elapsed since last attempt, for exactly
+.I num
+times; then use the next pattern.
+If
+.I num
+for the last pattern is "\fB+\fP", it retries forever; otherwise,
+no more retries occur.
+This directive must appear before any target specification;
+it affects all targets with the same pattern.
+
+.TP
+.B rebind\-as\-user {NO|yes}
+If this option is given, the client's bind credentials are remembered
+for rebinds, when trying to re-establish a broken connection,
+or when chasing a referral, if
+.B chase\-referrals
+is set to
+.IR yes .
+
+.TP
+.B session\-tracking\-request {NO|yes}
+Adds session tracking control for all requests.
+The client's IP and hostname, and the identity associated to each request,
+if known, are sent to the remote server for informational purposes.
+This directive is incompatible with setting \fIprotocol\-version\fP to 2.
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive.
+
+.SH TARGET SPECIFICATION
+Target specification starts with a "uri" directive:
+
+.TP
+.B uri <protocol>://[<host>]/<naming context> [...]
+Identical to
+.B meta.
+See
+.B slapd\-meta(5)
+for details.
+
+.TP
+.B acl\-authcDN "<administrative DN for access control purposes>"
+DN which is used to query the target server for acl checking,
+as in the LDAP backend; it is supposed to have read access
+on the target server to attributes used on the proxy for acl checking.
+There is no risk of giving away such values; they are only used to
+check permissions.
+.B The acl\-authcDN identity is by no means implicitly used by the proxy
+.B when the client connects anonymously.
+
+.TP
+.B acl\-passwd <password>
+Password used with the
+.B acl\-authcDN
+above.
+
+.TP
+.B bind\-timeout <microseconds>
+This directive defines the timeout, in microseconds, used when polling
+for response after an asynchronous bind connection. See
+.B slapd\-meta(5)
+for details.
+
+.TP
+.B chase\-referrals {YES|no}
+enable/disable automatic referral chasing, which is delegated to the
+underlying libldap, with rebinding eventually performed if the
+\fBrebind\-as\-user\fP directive is used. The default is to chase referrals.
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive.
+
+.TP
+.B client\-pr {accept-unsolicited|DISABLE|<size>}
+This feature allows one to use RFC 2696 Paged Results control when performing
+search operations with a specific target,
+irrespective of the client's request. See
+.B slapd\-meta(5)
+for details.
+
+.TP
+.B default\-target [<target>]
+The "default\-target" directive can also be used during target specification.
+With no arguments it marks the current target as the default.
+The optional number marks target <target> as the default one, starting
+from 1.
+Target <target> must be defined.
+
+.TP
+.B filter <pattern>
+This directive allows specifying a
+.BR regex (5)
+pattern to indicate what search filter terms are actually served by a target.
+
+In a search request, if the search filter matches the \fIpattern\fP
+the target is considered while fulfilling the request; otherwise
+the target is ignored. There may be multiple occurrences of
+the
+.B filter
+directive for each target.
+
+.TP
+.B idassert\-authzFrom <authz-regexp>
+if defined, selects what
+.I local
+identities are authorized to exploit the identity assertion feature.
+The string
+.B <authz-regexp>
+follows the rules defined for the
+.I authzFrom
+attribute.
+See
+.BR slapd.conf (5),
+section related to
+.BR authz\-policy ,
+for details on the syntax of this field.
+
+.HP
+.hy 0
+.B idassert\-bind
+.B bindmethod=none|simple|sasl [binddn=<simple DN>] [credentials=<simple password>]
+.B [saslmech=<SASL mech>] [secprops=<properties>] [realm=<realm>]
+.B [authcId=<authentication ID>] [authzId=<authorization ID>]
+.B [authz={native|proxyauthz}] [mode=<mode>] [flags=<flags>]
+.B [starttls=no|yes|critical]
+.B [tls_cert=<file>]
+.B [tls_key=<file>]
+.B [tls_cacert=<file>]
+.B [tls_cacertdir=<path>]
+.B [tls_reqcert=never|allow|try|demand]
+.B [tls_reqsan=never|allow|try|demand]
+.B [tls_cipher_suite=<ciphers>]
+.B [tls_ecname=<names>]
+.B [tls_protocol_min=<major>[.<minor>]]
+.B [tls_crlcheck=none|peer|all]
+Allows one to define the parameters of the authentication method that is
+internally used by the proxy to authorize connections that are
+authenticated by other databases. See
+.B slapd\-meta(5)
+for details.
+
+.TP
+.B idle\-timeout <time>
+This directive causes a a persistent connection to be dropped after
+it has been idle for the specified time. The connection will be re-created
+the next time it is selected for use. A connection is considered idle if no
+attempts have been made by the backend to use it to send a request to
+the backend server. If there are still pending requests in
+its queue, the connection will be dropped after the last
+request one has either received a result or has timed out.
+
+[<d>d][<h>h][<m>m][<s>[s]]
+
+where <d>, <h>, <m> and <s> are respectively treated as days, hours,
+minutes and seconds.
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive.
+
+.TP
+.B keepalive <idle>:<probes>:<interval>
+The
+.B keepalive
+parameter sets the values of \fIidle\fP, \fIprobes\fP, and \fIinterval\fP
+used to check whether a socket is alive;
+.I idle
+is the number of seconds a connection needs to remain idle before TCP
+starts sending keepalive probes;
+.I probes
+is the maximum number of keepalive probes TCP should send before dropping
+the connection;
+.I interval
+is interval in seconds between individual keepalive probes.
+Only some systems support the customization of these values;
+the
+.B keepalive
+parameter is ignored otherwise, and system-wide settings are used.
+
+.TP
+.B tcp\-user\-timeout <milliseconds>
+If non-zero, corresponds to the
+.B TCP_USER_TIMEOUT
+set on the target connections, overriding the operating system setting.
+Only some systems support the customization of this parameter, it is
+ignored otherwise and system-wide settings are used.
+
+.TP
+.B map "{attribute|objectclass} [<local name>|*] {<foreign name>|*}"
+This maps object classes and attributes as in the LDAP backend.
+See
+.BR slapd\-ldap (5).
+
+.TP
+.B network\-timeout <time>
+Sets the network timeout value after which
+.BR poll (2)/ select (2)
+following a
+.BR connect (2)
+returns in case of no activity while sending an operation to the remote target.
+The value is in milliseconds, and it can be specified as for
+.BR idle\-timeout .
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive.
+
+.TP
+.B nretries {forever|never|<nretries>}
+This directive defines how many times forwarding an operation should be retried
+in case of temporary failure in contacting a target. The number of retries
+is per operation, so if a bind to the target is necessary first, the remaining
+number is decremented. If defined
+before any target specification, it applies to all targets (by default,
+.BR 3
+times);
+the global value can be overridden by redefinitions inside each target
+specification.
+
+.TP
+.B rewrite* ...
+The rewrite options are identical to the
+.B meta
+backend. See the
+.B REWRITING
+section of
+.B slapd\-meta(5).
+
+.TP
+.B subtree\-{exclude|include} "<rule>"
+This directive allows one to indicate what subtrees are actually served
+by a target. See
+.B slapd\-meta(5)
+for details.
+
+.TP
+.B suffixmassage "<local suffix>" "<remote suffix>"
+.B slapd\-asyncmeta
+does not support the rewrite engine used by
+the LDAP and META backends.
+.B suffixmassage
+can be used to perform DN suffix rewriting, the same way as the obsoleted suffixmassage directive
+previously used by the LDAP backend.
+
+.TP
+.B t\-f\-support {NO|yes|discover}
+enable if the remote server supports absolute filters
+(see \fIRFC 4526\fP for details).
+If set to
+.BR discover ,
+support is detected by reading the remote server's root DSE.
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive.
+
+.TP
+.B timeout [<op>=]<val> [...]
+This directive allows one to set per-operation timeouts.
+Operations can be
+
+\fB<op> ::= bind, add, delete, modrdn, modify, compare, search\fP
+
+By default, the timeout for all operations is 2 seconds.
+
+See
+.B slapd\-meta(5)
+for details.
+
+.TP
+.B tls {none|[try\-]start|[try\-]propagate|ldaps}
+B [starttls=no]
+.B [tls_cert=<file>]
+.B [tls_key=<file>]
+.B [tls_cacert=<file>]
+.B [tls_cacertdir=<path>]
+.B [tls_reqcert=never|allow|try|demand]
+.B [tls_reqsan=never|allow|try|demand]
+.B [tls_cipher_suite=<ciphers>]
+.B [tls_ecname=<names>]
+.B [tls_crlcheck=none|peer|all]
+.RS
+Specify TLS settings regular connections.
+
+If the first parameter is not "none" then this configures the TLS
+settings to be used for regular connections.
+The StartTLS extended operation will be used when establishing the
+connection unless the URI directive protocol scheme is \fBldaps://\fP.
+In that case this keyword may only be set to "ldaps" and the StartTLS
+operation will not be used.
+
+With \fBpropagate\fP, the proxy issues the StartTLS operation only if
+the original connection has a TLS layer set up.
+The \fBtry\-\fP prefix instructs the proxy to continue operations
+if the StartTLS operation failed; its use is \fBnot\fP recommended.
+
+The TLS settings default to the same as the main slapd TLS settings,
+except for
+.B tls_reqcert
+which defaults to "demand",
+.B tls_reqsan
+which defaults to "allow", and
+.B starttls
+which is overshadowed by the first keyword and thus ignored.
+
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive.
+.RE
+
+.SH SCENARIOS
+See
+.B slapd\-meta(5)
+for configuration scenarios.
+
+.SH ACLs
+ACL behavior is identical to meta. See
+.B slapd\-meta(5).
+
+.SH ACCESS CONTROL
+The
+.B asyncmeta
+backend does not honor all ACL semantics as described in
+.BR slapd.access (5).
+In general, access checking is delegated to the remote server(s).
+Only
+.B read (=r)
+access to the
+.B entry
+pseudo-attribute and to the other attribute values of the entries
+returned by the
+.B search
+operation is honored, which is performed by the frontend.
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-ldap (5),
+.BR slapd\-meta (5),
+.BR slapo\-pcache (5),
+.BR slapd (8),
+.BR regex (7),
+.BR re_format (7).
+.SH AUTHOR
+Nadezhda Ivanova, based on back-meta by Pierangelo Masarati.
diff --git a/doc/man/man5/slapd-config.5 b/doc/man/man5/slapd-config.5
new file mode 100644
index 0000000..d402340
--- /dev/null
+++ b/doc/man/man5/slapd-config.5
@@ -0,0 +1,2274 @@
+.TH SLAPD-CONFIG 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd\-config \- configuration backend to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.d
+.SH DESCRIPTION
+The
+.B config
+backend manages all of the configuration information for the
+.BR slapd (8)
+daemon. This configuration information is also used by the SLAPD tools
+.BR slapacl (8),
+.BR slapadd (8),
+.BR slapauth (8),
+.BR slapcat (8),
+.BR slapdn (8),
+.BR slapindex (8),
+.BR slapmodify (8),
+and
+.BR slaptest (8).
+.LP
+The
+.B config
+backend is backward compatible with the older
+.BR slapd.conf (5)
+file but provides the ability to change the configuration dynamically
+at runtime. If slapd is run with only a
+.B slapd.conf
+file dynamic changes will be allowed but they will not persist across
+a server restart. Dynamic changes are only saved when slapd is running
+from a
+.B slapd.d
+configuration directory.
+.LP
+
+Unlike other backends, there can only be one instance of the
+.B config
+backend, and most of its structure is predefined. The root of the
+database is hardcoded to
+.B "cn=config"
+and this root entry contains
+global settings for slapd. Multiple child entries underneath the
+root entry are used to carry various other settings:
+.RS
+.TP
+.B cn=Module
+dynamically loaded modules
+.TP
+.B cn=Schema
+schema definitions
+.TP
+.B olcBackend=xxx
+backend-specific settings
+.TP
+.B olcDatabase=xxx
+database-specific settings
+.RE
+
+The
+.B cn=Module
+entries will only appear in configurations where slapd
+was built with support for dynamically loaded modules. There can be
+multiple entries, one for each configured module path. Within each
+entry there will be values recorded for each module loaded on a
+given path. These entries have no children.
+
+The
+.B cn=Schema
+entry contains all of the hardcoded schema elements.
+The children of this entry contain all user-defined schema elements.
+In schema that were loaded from include files, the child entry will
+be named after the include file from which the schema was loaded.
+Typically the first child in this subtree will be
+.BR cn=core,cn=schema,cn=config .
+
+.B olcBackend
+entries are for storing settings specific to a single
+backend type (and thus global to all database instances of that type).
+At present, only back-mdb implements any options of this type, so this
+setting is not needed for any other backends.
+
+.B olcDatabase
+entries store settings specific to a single database
+instance. These entries may have
+.B olcOverlay
+child entries corresponding
+to any overlays configured on the database. The olcDatabase and
+olcOverlay entries may also have miscellaneous child entries for
+other settings as needed. There are two special database entries
+that are predefined \- one is an entry for the config database itself,
+and the other is for the "frontend" database. Settings in the
+frontend database are inherited by the other databases, unless
+they are explicitly overridden in a specific database.
+.LP
+The specific configuration options available are discussed below in the
+Global Configuration Options, General Backend Options, and General Database
+Options. Options are set by defining LDAP attributes with specific values.
+In general the names of the LDAP attributes are the same as the corresponding
+.B slapd.conf
+keyword, with an "olc" prefix added on.
+
+The parser for many of these attributes is the same as used for parsing
+the slapd.conf keywords. As such, slapd.conf keywords that allow multiple
+items to be specified on one line, separated by whitespace, will allow
+multiple items to be specified in one attribute value. However, when
+reading the attribute via LDAP, the items will be returned as individual
+attribute values.
+
+Backend-specific options are discussed in the
+.B slapd\-<backend>(5)
+manual pages. Refer to the "OpenLDAP Administrator's Guide" for more
+details on configuring slapd.
+.SH GLOBAL CONFIGURATION OPTIONS
+Options described in this section apply to the server as a whole.
+Arguments that should be replaced by
+actual text are shown in brackets <>.
+
+These options may only be specified in the
+.B cn=config
+entry. This entry must have an objectClass of
+.BR olcGlobal .
+
+.TP
+.B olcAllows: <features>
+Specify a set of features to allow (default none).
+.B bind_v2
+allows acceptance of LDAPv2 bind requests. Note that
+.BR slapd (8)
+does not truly implement LDAPv2 (RFC 1777), now Historic (RFC 3494).
+.B bind_anon_cred
+allows anonymous bind when credentials are not empty (e.g.
+when DN is empty).
+.B bind_anon_dn
+allows unauthenticated (anonymous) bind when DN is not empty.
+.B update_anon
+allows unauthenticated (anonymous) update operations to be processed
+(subject to access controls and other administrative limits).
+.B proxy_authz_anon
+allows unauthenticated (anonymous) proxy authorization control to be processed
+(subject to access controls, authorization and other administrative limits).
+.TP
+.B olcArgsFile: <filename>
+The (absolute) name of a file that will hold the
+.B slapd
+server's command line (program name and options).
+.TP
+.B olcAttributeOptions: <option-name>...
+Define tagging attribute options or option tag/range prefixes.
+Options must not end with `\-', prefixes must end with `\-'.
+The `lang\-' prefix is predefined.
+If you use the
+.B olcAttributeOptions
+directive, `lang\-' will no longer be defined and you must specify it
+explicitly if you want it defined.
+
+An attribute description with a tagging option is a subtype of that
+attribute description without the option.
+Except for that, options defined this way have no special semantics.
+Prefixes defined this way work like the `lang\-' options:
+They define a prefix for tagging options starting with the prefix.
+That is, if you define the prefix `x\-foo\-', you can use the option
+`x\-foo\-bar'.
+Furthermore, in a search or compare, a prefix or range name (with
+a trailing `\-') matches all options starting with that name, as well
+as the option with the range name sans the trailing `\-'.
+That is, `x\-foo\-bar\-' matches `x\-foo\-bar' and `x\-foo\-bar\-baz'.
+
+RFC 4520 reserves options beginning with `x\-' for private experiments.
+Other options should be registered with IANA, see RFC 4520 section 3.5.
+OpenLDAP also has the `binary' option built in, but this is a transfer
+option, not a tagging option.
+.TP
+.B olcAuthIDRewrite: <rewrite\-rule>
+Used by the authentication framework to convert simple user names
+to an LDAP DN used for authorization purposes.
+Its purpose is analogous to that of
+.BR olcAuthzRegexp
+(see below).
+The
+.B rewrite\-rule
+is a set of rules analogous to those described in
+.BR slapo\-rwm (5)
+for data rewriting (after stripping the \fIrwm\-\fP prefix).
+.B olcAuthIDRewrite
+and
+.B olcAuthzRegexp
+should not be intermixed.
+.TP
+.B olcAuthzPolicy: <policy>
+Used to specify which rules to use for Proxy Authorization. Proxy
+authorization allows a client to authenticate to the server using one
+user's credentials, but specify a different identity to use for authorization
+and access control purposes. It essentially allows user A to login as user
+B, using user A's password.
+The
+.B none
+flag disables proxy authorization. This is the default setting.
+The
+.B from
+flag will use rules in the
+.I authzFrom
+attribute of the authorization DN.
+The
+.B to
+flag will use rules in the
+.I authzTo
+attribute of the authentication DN.
+The
+.B any
+flag, an alias for the deprecated value of
+.BR both ,
+will allow any of the above, whatever succeeds first (checked in
+.BR to ,
+.B from
+sequence.
+The
+.B all
+flag requires both authorizations to succeed.
+.LP
+.RS
+The rules are mechanisms to specify which identities are allowed
+to perform proxy authorization.
+The
+.I authzFrom
+attribute in an entry specifies which other users
+are allowed to proxy login to this entry. The
+.I authzTo
+attribute in
+an entry specifies which other users this user can authorize as. Use of
+.I authzTo
+rules can be easily
+abused if users are allowed to write arbitrary values to this attribute.
+In general the
+.I authzTo
+attribute must be protected with ACLs such that
+only privileged users can modify it.
+The value of
+.I authzFrom
+and
+.I authzTo
+describes an
+.B identity
+or a set of identities; it can take five forms:
+.RS
+.TP
+.B ldap:///<base>??[<scope>]?<filter>
+.RE
+.RS
+.B dn[.<dnstyle>]:<pattern>
+.RE
+.RS
+.B u[.<mech>[<realm>]]:<pattern>
+.RE
+.RS
+.B group[/objectClass[/attributeType]]:<pattern>
+.RE
+.RS
+.B <pattern>
+.RE
+.RS
+
+.B <dnstyle>:={exact|onelevel|children|subtree|regex}
+
+.RE
+The first form is a valid LDAP
+.B URI
+where the
+.IR <host>:<port> ,
+the
+.I <attrs>
+and the
+.I <extensions>
+portions must be absent, so that the search occurs locally on either
+.I authzFrom
+or
+.IR authzTo .
+
+.LP
+The second form is a
+.BR DN ,
+with the optional style modifiers
+.IR exact ,
+.IR onelevel ,
+.IR children ,
+and
+.I subtree
+for exact, onelevel, children and subtree matches, which cause
+.I <pattern>
+to be normalized according to the DN normalization rules, or the special
+.I regex
+style, which causes the
+.I <pattern>
+to be treated as a POSIX (''extended'') regular expression, as
+discussed in
+.BR regex (7)
+and/or
+.BR re_format (7).
+A pattern of
+.I *
+means any non-anonymous DN.
+
+.LP
+The third form is a SASL
+.BR id ,
+with the optional fields
+.I <mech>
+and
+.I <realm>
+that allow to specify a SASL
+.BR mechanism ,
+and eventually a SASL
+.BR realm ,
+for those mechanisms that support one.
+The need to allow the specification of a mechanism is still debated,
+and users are strongly discouraged to rely on this possibility.
+
+.LP
+The fourth form is a group specification.
+It consists of the keyword
+.BR group ,
+optionally followed by the specification of the group
+.B objectClass
+and
+.BR attributeType .
+The
+.B objectClass
+defaults to
+.IR groupOfNames .
+The
+.B attributeType
+defaults to
+.IR member .
+The group with DN
+.B <pattern>
+is searched with base scope, filtered on the specified
+.BR objectClass .
+The values of the resulting
+.B attributeType
+are searched for the asserted DN.
+
+.LP
+The fifth form is provided for backwards compatibility. If no identity
+type is provided, i.e. only
+.B <pattern>
+is present, an
+.I exact DN
+is assumed; as a consequence,
+.B <pattern>
+is subjected to DN normalization.
+
+.LP
+Since the interpretation of
+.I authzFrom
+and
+.I authzTo
+can impact security, users are strongly encouraged
+to explicitly set the type of identity specification that is being used.
+A subset of these rules can be used as third arg in the
+.B olcAuthzRegexp
+statement (see below); significantly, the
+.IR URI ,
+provided it results in exactly one entry,
+and the
+.I dn.exact:<dn>
+forms.
+.RE
+.TP
+.B olcAuthzRegexp: <match> <replace>
+Used by the authentication framework to convert simple user names,
+such as provided by SASL subsystem, or extracted from certificates
+in case of cert-based SASL EXTERNAL, or provided within the RFC 4370
+"proxied authorization" control, to an LDAP DN used for
+authorization purposes. Note that the resulting DN need not refer
+to an existing entry to be considered valid. When an authorization
+request is received from the SASL subsystem, the SASL
+.BR USERNAME ,
+.BR REALM ,
+and
+.B MECHANISM
+are taken, when available, and combined into a name of the form
+.RS
+.RS
+.TP
+.B UID=<username>[[,CN=<realm>],CN=<mechanism>],CN=auth
+
+.RE
+This name is then compared against the
+.B match
+POSIX (''extended'') regular expression, and if the match is successful,
+the name is replaced with the
+.B replace
+string. If there are wildcard strings in the
+.B match
+regular expression that are enclosed in parenthesis, e.g.
+.RS
+.TP
+.B UID=([^,]*),CN=.*
+
+.RE
+then the portion of the name that matched the wildcard will be stored
+in the numbered placeholder variable $1. If there are other wildcard strings
+in parenthesis, the matching strings will be in $2, $3, etc. up to $9. The
+placeholders can then be used in the
+.B replace
+string, e.g.
+.RS
+.TP
+.B UID=$1,OU=Accounts,DC=example,DC=com
+
+.RE
+The replaced name can be either a DN, i.e. a string prefixed by "dn:",
+or an LDAP URI.
+If the latter, the server will use the URI to search its own database(s)
+and, if the search returns exactly one entry, the name is
+replaced by the DN of that entry. The LDAP URI must have no
+hostport, attrs, or extensions components, but the filter is mandatory,
+e.g.
+.RS
+.TP
+.B ldap:///OU=Accounts,DC=example,DC=com??one?(UID=$1)
+
+.RE
+The protocol portion of the URI must be strictly
+.BR ldap .
+Note that this search is subject to access controls. Specifically,
+the authentication identity must have "auth" access in the subject.
+
+Multiple
+.B olcAuthzRegexp
+values can be specified to allow for multiple matching
+and replacement patterns. The matching patterns are checked in the order they
+appear in the attribute, stopping at the first successful match.
+
+.\".B Caution:
+.\"Because the plus sign + is a character recognized by the regular expression engine,
+.\"and it will appear in names that include a REALM, be careful to escape the
+.\"plus sign with a backslash \\+ to remove the character's special meaning.
+.RE
+.TP
+.B olcConcurrency: <integer>
+Specify a desired level of concurrency. Provided to the underlying
+thread system as a hint. The default is not to provide any hint. This setting
+is only meaningful on some platforms where there is not a one to one
+correspondence between user threads and kernel threads.
+.TP
+.B olcConnMaxPending: <integer>
+Specify the maximum number of pending requests for an anonymous session.
+If requests are submitted faster than the server can process them, they
+will be queued up to this limit. If the limit is exceeded, the session
+is closed. The default is 100.
+.TP
+.B olcConnMaxPendingAuth: <integer>
+Specify the maximum number of pending requests for an authenticated session.
+The default is 1000.
+.TP
+.B olcDisallows: <features>
+Specify a set of features to disallow (default none).
+.B bind_anon
+disables acceptance of anonymous bind requests. Note that this setting
+does not prohibit anonymous directory access (See "require authc").
+.B bind_simple
+disables simple (bind) authentication.
+.B tls_2_anon
+disables forcing session to anonymous status (see also
+.BR tls_authc )
+upon StartTLS operation receipt.
+.B tls_authc
+disallows the StartTLS operation if authenticated (see also
+.BR tls_2_anon ).
+.B proxy_authz_non_critical
+disables acceptance of the proxied authorization control (RFC4370)
+with criticality set to FALSE.
+.B dontusecopy_non_critical
+disables acceptance of the dontUseCopy control (a work in progress)
+with criticality set to FALSE.
+.TP
+.B olcGentleHUP: { TRUE | FALSE }
+A SIGHUP signal will only cause a 'gentle' shutdown-attempt:
+.B Slapd
+will stop listening for new connections, but will not close the
+connections to the current clients. Future write operations return
+unwilling-to-perform, though. Slapd terminates when all clients
+have closed their connections (if they ever do), or \- as before \-
+if it receives a SIGTERM signal. This can be useful if you wish to
+terminate the server and start a new
+.B slapd
+server
+.B with another database,
+without disrupting the currently active clients.
+The default is FALSE. You may wish to use
+.B olcIdleTimeout
+along with this option.
+.TP
+.B olcIdleTimeout: <integer>
+Specify the number of seconds to wait before forcibly closing
+an idle client connection. A setting of 0 disables this
+feature. The default is 0. You may also want to set the
+.B olcWriteTimeout
+option.
+.TP
+.B olcIndexHash64: { on | off }
+Use a 64 bit hash for indexing. The default is to use 32 bit hashes.
+These hashes are used for equality and substring indexing. The 64 bit
+version may be needed to avoid index collisions when the number of
+indexed values exceeds ~64 million. (Note that substring indexing
+generates multiple index values per actual attribute value.)
+Indices generated with 32 bit hashes are incompatible with the 64 bit
+version, and vice versa. Any existing databases must be fully reloaded
+when changing this setting. This directive is only supported on 64 bit CPUs.
+.TP
+.B olcIndexIntLen: <integer>
+Specify the key length for ordered integer indices. The most significant
+bytes of the binary integer will be used for index keys. The default
+value is 4, which provides exact indexing for 31 bit values.
+A floating point representation is used to index too large values.
+.TP
+.B olcIndexSubstrIfMaxlen: <integer>
+Specify the maximum length for subinitial and subfinal indices. Only
+this many characters of an attribute value will be processed by the
+indexing functions; any excess characters are ignored. The default is 4.
+.TP
+.B olcIndexSubstrIfMinlen: <integer>
+Specify the minimum length for subinitial and subfinal indices. An
+attribute value must have at least this many characters in order to be
+processed by the indexing functions. The default is 2.
+.TP
+.B olcIndexSubstrAnyLen: <integer>
+Specify the length used for subany indices. An attribute value must have
+at least this many characters in order to be processed. Attribute values
+longer than this length will be processed in segments of this length. The
+default is 4. The subany index will also be used in subinitial and
+subfinal index lookups when the filter string is longer than the
+.I olcIndexSubstrIfMaxlen
+value.
+.TP
+.B olcIndexSubstrAnyStep: <integer>
+Specify the steps used in subany index lookups. This value sets the offset
+for the segments of a filter string that are processed for a subany index
+lookup. The default is 2. For example, with the default values, a search
+using this filter "cn=*abcdefgh*" would generate index lookups for
+"abcd", "cdef", and "efgh".
+
+.LP
+Note: Indexing support depends on the particular backend in use. Also,
+changing these settings will generally require deleting any indices that
+depend on these parameters and recreating them with
+.BR slapindex (8).
+
+.TP
+.B olcListenerThreads: <integer>
+Specify the number of threads to use for the connection manager.
+The default is 1 and this is typically adequate for up to 16 CPU cores.
+The value should be set to a power of 2.
+.TP
+.B olcLocalSSF: <SSF>
+Specifies the Security Strength Factor (SSF) to be given local LDAP sessions,
+such as those to the ldapi:// listener. For a description of SSF values,
+see
+.BR olcSaslSecProps 's
+.B minssf
+option description. The default is 71.
+.TP
+.B olcLogFile: <filename>
+Specify a file for recording slapd debug messages. By default these messages
+only go to stderr, are not recorded anywhere else, and are unrelated to
+messages exposed by the
+.B loglevel
+configuration parameter. Specifying a logfile copies messages to both stderr
+and the logfile.
+.TP
+.B olcLogLevel: <integer> [...]
+Specify the level at which debugging statements and operation
+statistics should be syslogged (currently logged to the
+.BR syslogd (8)
+LOG_LOCAL4 facility).
+They must be considered subsystems rather than increasingly verbose
+log levels.
+Some messages with higher priority are logged regardless
+of the configured loglevel as soon as any logging is configured.
+Log levels are additive, and available levels are:
+.RS
+.RS
+.PD 0
+.TP
+.B 1
+.B (0x1 trace)
+trace function calls
+.TP
+.B 2
+.B (0x2 packets)
+debug packet handling
+.TP
+.B 4
+.B (0x4 args)
+heavy trace debugging (function args)
+.TP
+.B 8
+.B (0x8 conns)
+connection management
+.TP
+.B 16
+.B (0x10 BER)
+print out packets sent and received
+.TP
+.B 32
+.B (0x20 filter)
+search filter processing
+.TP
+.B 64
+.B (0x40 config)
+configuration file processing
+.TP
+.B 128
+.B (0x80 ACL)
+access control list processing
+.TP
+.B 256
+.B (0x100 stats)
+connections, LDAP operations, results (recommended)
+.TP
+.B 512
+.B (0x200 stats2)
+stats2 log entries sent
+.TP
+.B 1024
+.B (0x400 shell)
+print communication with shell backends
+.TP
+.B 2048
+.B (0x800 parse)
+entry parsing
+\".TP
+\".B 4096
+\".B (0x1000 cache)
+\"caching (unused)
+\".TP
+\".B 8192
+\".B (0x2000 index)
+\"data indexing (unused)
+.TP
+.B 16384
+.B (0x4000 sync)
+LDAPSync replication
+.TP
+.B 32768
+.B (0x8000 none)
+only messages that get logged whatever log level is set
+.PD
+.RE
+The desired log level can be input as a single integer that combines
+the (ORed) desired levels, both in decimal or in hexadecimal notation,
+as a list of integers (that are ORed internally),
+or as a list of the names that are shown between parenthesis, such that
+.LP
+.nf
+ olcLogLevel: 129
+ olcLogLevel: 0x81
+ olcLogLevel: 128 1
+ olcLogLevel: 0x80 0x1
+ olcLogLevel: acl trace
+.fi
+.LP
+are equivalent.
+The keyword
+.B any
+can be used as a shortcut to enable logging at all levels (equivalent to \-1).
+The keyword
+.BR none ,
+or the equivalent integer representation, causes those messages
+that are logged regardless of the configured olcLogLevel to be logged.
+In fact, if no olcLogLevel (or a 0 level) is defined, no logging occurs,
+so at least the
+.B none
+level is required to have high priority messages logged.
+
+Note that the
+.BR packets ,
+.BR BER ,
+and
+.B parse
+levels are only available as debug output on stderr, and are not
+sent to syslog.
+
+This setting defaults to \fBstats\fP.
+This level should usually also be included when using other loglevels, to
+help analyze the logs.
+.RE
+.TP
+.B olcMaxFilterDepth: <integer>
+Specify the maximum depth of nested filters in search requests.
+The default is 1000.
+.TP
+.B olcPasswordCryptSaltFormat: <format>
+Specify the format of the salt passed to
+.BR crypt (3)
+when generating {CRYPT} passwords (see
+.BR olcPasswordHash )
+during processing of LDAP Password Modify Extended Operations (RFC 3062).
+
+This string needs to be in
+.BR sprintf (3)
+format and may include one (and only one) %s conversion.
+This conversion will be substituted with a string of random
+characters from [A\-Za\-z0\-9./]. For example, "%.2s"
+provides a two character salt and "$1$%.8s" tells some
+versions of crypt(3) to use an MD5 algorithm and provides
+8 random characters of salt. The default is "%s", which
+provides 31 characters of salt.
+.TP
+.B olcPidFile: <filename>
+The (absolute) name of a file that will hold the
+.B slapd
+server's process ID (see
+.BR getpid (2)).
+.TP
+.B olcPluginLogFile: <filename>
+The ( absolute ) name of a file that will contain log
+messages from
+.B SLAPI
+plugins. See
+.BR slapd.plugin (5)
+for details.
+.TP
+.B olcReferral: <url>
+Specify the referral to pass back when
+.BR slapd (8)
+cannot find a local database to handle a request.
+If multiple values are specified, each url is provided.
+.TP
+.B olcReverseLookup: TRUE | FALSE
+Enable/disable client name unverified reverse lookup (default is
+.BR FALSE
+if compiled with \-\-enable\-rlookups).
+.TP
+.B olcRootDSE: <file>
+Specify the name of an LDIF(5) file containing user defined attributes
+for the root DSE. These attributes are returned in addition to the
+attributes normally produced by slapd.
+
+The root DSE is an entry with information about the server and its
+capabilities, in operational attributes.
+It has the empty DN, and can be read with e.g.:
+.ti +4
+ldapsearch \-x \-b "" \-s base "+"
+.br
+See RFC 4512 section 5.1 for details.
+.TP
+.B olcSaslAuxprops: <plugin> [...]
+Specify which auxprop plugins to use for authentication lookups. The
+default is empty, which just uses slapd's internal support. Usually
+no other auxprop plugins are needed.
+.TP
+.B olcSaslAuxpropsDontUseCopy: <attr> [...]
+Specify which attribute(s) should be subject to the don't use copy control. This
+is necessary for some SASL mechanisms such as OTP to work in a replicated
+environment. The attribute "cmusaslsecretOTP" is the default value.
+.TP
+.B olcSaslAuxpropsDontUseCopyIgnore TRUE | FALSE
+Used to disable replication of the attribute(s) defined by
+olcSaslAuxpropsDontUseCopy and instead use a local value for the attribute. This
+allows the SASL mechanism to continue to work if the provider is offline. This can
+cause replication inconsistency. Defaults to FALSE.
+.TP
+.B olcSaslHost: <fqdn>
+Used to specify the fully qualified domain name used for SASL processing.
+.TP
+.B olcSaslRealm: <realm>
+Specify SASL realm. Default is empty.
+.TP
+.B olcSaslCbinding: none | tls-unique | tls-endpoint
+Specify the channel-binding type, see also LDAP_OPT_X_SASL_CBINDING.
+Default is none.
+.TP
+.B olcSaslSecProps: <properties>
+Used to specify Cyrus SASL security properties.
+The
+.B none
+flag (without any other properties) causes the flag properties
+default, "noanonymous,noplain", to be cleared.
+The
+.B noplain
+flag disables mechanisms susceptible to simple passive attacks.
+The
+.B noactive
+flag disables mechanisms susceptible to active attacks.
+The
+.B nodict
+flag disables mechanisms susceptible to passive dictionary attacks.
+The
+.B noanonymous
+flag disables mechanisms which support anonymous login.
+The
+.B forwardsec
+flag require forward secrecy between sessions.
+The
+.B passcred
+require mechanisms which pass client credentials (and allow
+mechanisms which can pass credentials to do so).
+The
+.B minssf=<factor>
+property specifies the minimum acceptable
+.I security strength factor
+as an integer approximate to effective key length used for
+encryption. 0 (zero) implies no protection, 1 implies integrity
+protection only, 128 allows RC4, Blowfish and other similar ciphers,
+256 will require modern ciphers. The default is 0.
+The
+.B maxssf=<factor>
+property specifies the maximum acceptable
+.I security strength factor
+as an integer (see minssf description). The default is INT_MAX.
+The
+.B maxbufsize=<size>
+property specifies the maximum security layer receive buffer
+size allowed. 0 disables security layers. The default is 65536.
+.TP
+.B olcServerID: <integer> [<URL>]
+Specify an integer ID from 0 to 4095 for this server. The ID may also be
+specified as a hexadecimal ID by prefixing the value with "0x".
+Non-zero IDs are required when using multi-provider replication and each
+provider must have a unique non-zero ID. Note that this requirement also
+applies to separate providers contributing to a glued set of databases.
+If the URL is provided, this directive may be specified
+multiple times, providing a complete list of participating servers
+and their IDs. The fully qualified hostname of each server should be
+used in the supplied URLs. The IDs are used in the "replica id" field
+of all CSNs generated by the specified server. The default value is zero, which
+is only valid for single provider replication.
+Example:
+.LP
+.nf
+ olcServerID: 1 ldap://ldap1.example.com
+ olcServerID: 2 ldap://ldap2.example.com
+.fi
+.TP
+.B olcSockbufMaxIncoming: <integer>
+Specify the maximum incoming LDAP PDU size for anonymous sessions.
+The default is 262143.
+.TP
+.B olcSockbufMaxIncomingAuth: <integer>
+Specify the maximum incoming LDAP PDU size for authenticated sessions.
+The default is 4194303.
+.TP
+.B olcTCPBuffer [listener=<URL>] [{read|write}=]<size>
+Specify the size of the TCP buffer.
+A global value for both read and write TCP buffers related to any listener
+is defined, unless the listener is explicitly specified,
+or either the read or write qualifiers are used.
+See
+.BR tcp (7)
+for details.
+Note that some OS-es implement automatic TCP buffer tuning.
+.TP
+.B olcThreads: <integer>
+Specify the maximum size of the primary thread pool.
+The default is 16; the minimum value is 2.
+.TP
+.B olcThreadQueues: <integer>
+Specify the number of work queues to use for the primary thread pool.
+The default is 1 and this is typically adequate for up to 8 CPU cores.
+The value should not exceed the number of CPUs in the system.
+.TP
+.B olcToolThreads: <integer>
+Specify the maximum number of threads to use in tool mode.
+This should not be greater than the number of CPUs in the system.
+The default is 1.
+.TP
+.B olcWriteTimeout: <integer>
+Specify the number of seconds to wait before forcibly closing
+a connection with an outstanding write. This allows recovery from
+various network hang conditions. A setting of 0 disables this
+feature. The default is 0.
+.SH TLS OPTIONS
+If
+.B slapd
+is built with support for Transport Layer Security, there are more options
+you can specify.
+.TP
+.B olcTLSCipherSuite: <cipher-suite-spec>
+Permits configuring what ciphers will be accepted and the preference order.
+<cipher-suite-spec> should be a cipher specification for the TLS library
+in use (OpenSSL or GnuTLS).
+Example:
+.RS
+.RS
+.TP
+.I OpenSSL:
+olcTLSCipherSuite: HIGH:MEDIUM:+SSLv2
+.TP
+.I GnuTLS:
+olcTLSCiphersuite: SECURE256:!AES-128-CBC
+.RE
+
+To check what ciphers a given spec selects in OpenSSL, use:
+
+.nf
+ openssl ciphers \-v <cipher-suite-spec>
+.fi
+
+With GnuTLS the available specs can be found in the manual page of
+.BR gnutls\-cli (1)
+(see the description of the
+option
+.BR \-\-priority ).
+
+In older versions of GnuTLS, where gnutls\-cli does not support the option
+\-\-priority, you can obtain the \(em more limited \(em list of ciphers by calling:
+
+.nf
+ gnutls\-cli \-l
+.fi
+.RE
+.TP
+.B olcTLSCACertificateFile: <filename>
+Specifies the file that contains certificates for all of the Certificate
+Authorities that
+.B slapd
+will recognize. The certificate for
+the CA that signed the server certificate must be included among
+these certificates. If the signing CA was not a top-level (root) CA,
+certificates for the entire sequence of CA's from the signing CA to
+the top-level CA should be present. Multiple certificates are simply
+appended to the file; the order is not significant.
+.TP
+.B olcTLSCACertificatePath: <path>
+Specifies the path of a directory that contains Certificate Authority
+certificates in separate individual files. Usually only one of this
+or the olcTLSCACertificateFile is defined. If both are specified, both
+locations will be used.
+.TP
+.B olcTLSCertificateFile: <filename>
+Specifies the file that contains the
+.B slapd
+server certificate.
+
+When using OpenSSL that file may also contain any number of intermediate
+certificates after the server certificate.
+.TP
+.B olcTLSCertificateKeyFile: <filename>
+Specifies the file that contains the
+.B slapd
+server private key that matches the certificate stored in the
+.B olcTLSCertificateFile
+file. If the private key is protected with a password, the password must
+be manually typed in when slapd starts. Usually the private key is not
+protected with a password, to allow slapd to start without manual
+intervention, so
+it is of critical importance that the file is protected carefully.
+.TP
+.B olcTLSDHParamFile: <filename>
+This directive specifies the file that contains parameters for Diffie-Hellman
+ephemeral key exchange. This is required in order to use a DSA certificate on
+the server, or an RSA certificate missing the "key encipherment" key usage.
+Note that setting this option may also enable
+Anonymous Diffie-Hellman key exchanges in certain non-default cipher suites.
+Anonymous key exchanges should generally be avoided since they provide no
+actual client or server authentication and provide no protection against
+man-in-the-middle attacks.
+You should append "!ADH" to your cipher suites to ensure that these suites
+are not used.
+.TP
+.B olcTLSECName: <name>
+Specify the name of the curve(s) to use for Elliptic curve Diffie-Hellman
+ephemeral key exchange. This option is only used for OpenSSL.
+This option is not used with GnuTLS; the curves may be
+chosen in the GnuTLS ciphersuite specification.
+.TP
+.B olcTLSProtocolMin: <major>[.<minor>]
+Specifies minimum SSL/TLS protocol version that will be negotiated.
+If the server doesn't support at least that version,
+the SSL handshake will fail.
+To require TLS 1.x or higher, set this option to 3.(x+1),
+e.g.,
+
+.nf
+ olcTLSProtocolMin: 3.2
+.fi
+
+would require TLS 1.1.
+Specifying a minimum that is higher than that supported by the
+OpenLDAP implementation will result in it requiring the
+highest level that it does support.
+This directive is ignored with GnuTLS.
+.TP
+.B olcTLSRandFile: <filename>
+Specifies the file to obtain random bits from when /dev/[u]random
+is not available. Generally set to the name of the EGD/PRNGD socket.
+The environment variable RANDFILE can also be used to specify the filename.
+This directive is ignored with GnuTLS.
+.TP
+.B olcTLSVerifyClient: <level>
+Specifies what checks to perform on client certificates in an
+incoming TLS session, if any.
+The
+.B <level>
+can be specified as one of the following keywords:
+.RS
+.TP
+.B never
+This is the default.
+.B slapd
+will not ask the client for a certificate.
+.TP
+.B allow
+The client certificate is requested. If no certificate is provided,
+the session proceeds normally. If a bad certificate is provided,
+it will be ignored and the session proceeds normally.
+.TP
+.B try
+The client certificate is requested. If no certificate is provided,
+the session proceeds normally. If a bad certificate is provided,
+the session is immediately terminated.
+.TP
+.B demand | hard | true
+These keywords are all equivalent, for compatibility reasons.
+The client certificate is requested. If no certificate is provided,
+or a bad certificate is provided, the session is immediately terminated.
+
+Note that a valid client certificate is required in order to use the
+SASL EXTERNAL authentication mechanism with a TLS session. As such,
+a non-default
+.B olcTLSVerifyClient
+setting must be chosen to enable SASL EXTERNAL authentication.
+.RE
+.TP
+.B olcTLSCRLCheck: <level>
+Specifies if the Certificate Revocation List (CRL) of the CA should be
+used to verify if the client certificates have not been revoked. This
+requires
+.B olcTLSCACertificatePath
+parameter to be set. This parameter is ignored with GnuTLS.
+.B <level>
+can be specified as one of the following keywords:
+.RS
+.TP
+.B none
+No CRL checks are performed
+.TP
+.B peer
+Check the CRL of the peer certificate
+.TP
+.B all
+Check the CRL for a whole certificate chain
+.RE
+.TP
+.B olcTLSCRLFile: <filename>
+Specifies a file containing a Certificate Revocation List to be used
+for verifying that certificates have not been revoked. This parameter is
+only valid when using GnuTLS.
+.SH DYNAMIC MODULE OPTIONS
+If
+.B slapd
+is compiled with \-\-enable\-modules then the module-related entries will
+be available. These entries are named
+.B cn=module{x},cn=config
+and
+must have the olcModuleList objectClass. One entry should be created
+per
+.B olcModulePath.
+Normally the config engine generates the "{x}" index in the RDN
+automatically, so it can be omitted when initially loading these entries.
+.TP
+.B olcModuleLoad: <filename> [<arguments>...]
+Specify the name of a dynamically loadable module to load and any
+additional arguments if supported by the module. The filename
+may be an absolute path name or a simple filename. Non-absolute names
+are searched for in the directories specified by the
+.B olcModulePath
+option.
+.TP
+.B olcModulePath: <pathspec>
+Specify a list of directories to search for loadable modules. Typically
+the path is colon-separated but this depends on the operating system.
+The default is MODULEDIR, which is where the standard OpenLDAP install
+will place its modules.
+.SH SCHEMA OPTIONS
+Schema definitions are created as entries in the
+.B cn=schema,cn=config
+subtree. These entries must have the olcSchemaConfig objectClass.
+As noted above, the actual
+.B cn=schema,cn=config
+entry is predefined and any values specified for it are ignored.
+
+.HP
+.hy 0
+.B olcAttributetypes: "(\ <oid>\
+ [NAME\ <name>]\
+ [DESC\ <description>]\
+ [OBSOLETE]\
+ [SUP\ <oid>]\
+ [EQUALITY\ <oid>]\
+ [ORDERING\ <oid>]\
+ [SUBSTR\ <oid>]\
+ [SYNTAX\ <oidlen>]\
+ [SINGLE\-VALUE]\
+ [COLLECTIVE]\
+ [NO\-USER\-MODIFICATION]\
+ [USAGE\ <attributeUsage>]\ )"
+.RS
+Specify an attribute type using the LDAPv3 syntax defined in RFC 4512.
+The slapd parser extends the RFC 4512 definition by allowing string
+forms as well as numeric OIDs to be used for the attribute OID and
+attribute syntax OID.
+(See the
+.B olcObjectIdentifier
+description.)
+.RE
+
+.HP
+.hy 0
+.B olcDitContentRules: "(\ <oid>\
+ [NAME\ <name>]\
+ [DESC\ <description>]\
+ [OBSOLETE]\
+ [AUX\ <oids>]\
+ [MUST\ <oids>]\
+ [MAY\ <oids>]\
+ [NOT\ <oids>]\ )"
+.RS
+Specify an DIT Content Rule using the LDAPv3 syntax defined in RFC 4512.
+The slapd parser extends the RFC 4512 definition by allowing string
+forms as well as numeric OIDs to be used for the attribute OID and
+attribute syntax OID.
+(See the
+.B olcObjectIdentifier
+description.)
+.RE
+
+.HP
+.hy 0
+.B olcLdapSyntaxes "(\ <oid>\
+ [DESC\ <description>]\
+ [X\-SUBST <substitute-syntax>]\ )"
+.RS
+Specify an LDAP syntax using the LDAPv3 syntax defined in RFC 4512.
+The slapd parser extends the RFC 4512 definition by allowing string
+forms as well as numeric OIDs to be used for the syntax OID.
+(See the
+.B objectidentifier
+description.)
+The slapd parser also honors the
+.B X\-SUBST
+extension (an OpenLDAP-specific extension), which allows one to use the
+.B olcLdapSyntaxes
+attribute to define a non-implemented syntax along with another syntax,
+the extension value
+.IR substitute-syntax ,
+as its temporary replacement.
+The
+.I substitute-syntax
+must be defined.
+This allows one to define attribute types that make use of non-implemented syntaxes
+using the correct syntax OID.
+Unless
+.B X\-SUBST
+is used, this configuration statement would result in an error,
+since no handlers would be associated to the resulting syntax structure.
+.RE
+
+.HP
+.hy 0
+.B olcObjectClasses: "(\ <oid>\
+ [NAME\ <name>]\
+ [DESC\ <description>]\
+ [OBSOLETE]\
+ [SUP\ <oids>]\
+ [{ ABSTRACT | STRUCTURAL | AUXILIARY }]\
+ [MUST\ <oids>] [MAY\ <oids>] )"
+.RS
+Specify an objectclass using the LDAPv3 syntax defined in RFC 4512.
+The slapd parser extends the RFC 4512 definition by allowing string
+forms as well as numeric OIDs to be used for the object class OID.
+(See the
+.B
+olcObjectIdentifier
+description.) Object classes are "STRUCTURAL" by default.
+.RE
+.TP
+.B olcObjectIdentifier: <name> "{ <oid> | <name>[:<suffix>] }"
+Define a string name that equates to the given OID. The string can be used
+in place of the numeric OID in objectclass and attribute definitions. The
+name can also be used with a suffix of the form ":xx" in which case the
+value "oid.xx" will be used.
+
+.SH GENERAL BACKEND OPTIONS
+Options in these entries only apply to the configuration of a single
+type of backend. All backends may support this class of options, but
+currently only back-mdb does.
+The entry must be named
+.B olcBackend=<databasetype>,cn=config
+and must have the olcBackendConfig objectClass.
+<databasetype>
+should be one of
+.BR asyncmeta ,
+.BR config ,
+.BR dnssrv ,
+.BR ldap ,
+.BR ldif ,
+.BR mdb ,
+.BR meta ,
+.BR monitor ,
+.BR ndb ,
+.BR null ,
+.BR passwd ,
+.BR perl ,
+.BR relay ,
+.BR sock ,
+.BR sql ,
+or
+.BR wt .
+At present, only back-mdb implements any options of this type, so this
+entry should not be used for any other backends.
+
+.SH DATABASE OPTIONS
+Database options are set in entries named
+.B olcDatabase={x}<databasetype>,cn=config
+and must have the olcDatabaseConfig objectClass. Normally the config
+engine generates the "{x}" index in the RDN automatically, so it
+can be omitted when initially loading these entries.
+
+The special frontend database is always numbered "{\-1}" and the config
+database is always numbered "{0}".
+
+.SH GLOBAL DATABASE OPTIONS
+Options in this section may be set in the special "frontend" database
+and inherited in all the other databases. These options may be altered
+by further settings in each specific database. The frontend entry must
+be named
+.B olcDatabase=frontend,cn=config
+and must have the olcFrontendConfig objectClass.
+.TP
+.B olcAccess: to <what> "[ by <who> <access> <control> ]+"
+Grant access (specified by <access>) to a set of entries and/or
+attributes (specified by <what>) by one or more requestors (specified
+by <who>).
+If no access controls are present, the default policy
+allows anyone and everyone to read anything but restricts
+updates to rootdn. (e.g., "olcAccess: to * by * read").
+See
+.BR slapd.access (5)
+and the "OpenLDAP Administrator's Guide" for details.
+
+Access controls set in the frontend are appended to any access
+controls set on the specific databases.
+The rootdn of a database can always read and write EVERYTHING
+in that database.
+
+Extra special care must be taken with the access controls on the
+config database. Unlike other databases, the default policy for the
+config database is to only allow access to the rootdn. Regular users
+should not have read access, and write access should be granted very
+carefully to privileged administrators.
+
+.TP
+.B olcDefaultSearchBase: <dn>
+Specify a default search base to use when client submits a
+non-base search request with an empty base DN.
+Base scoped search requests with an empty base DN are not affected.
+This setting is only allowed in the frontend entry.
+.TP
+.B olcExtraAttrs: <attr>
+Lists what attributes need to be added to search requests.
+Local storage backends return the entire entry to the frontend.
+The frontend takes care of only returning the requested attributes
+that are allowed by ACLs.
+However, features like access checking and so may need specific
+attributes that are not automatically returned by remote storage
+backends, like proxy backends and so on.
+.B <attr>
+is an attribute that is needed for internal purposes
+and thus always needs to be collected, even when not explicitly
+requested by clients.
+This attribute is multi-valued.
+.TP
+.B olcPasswordHash: <hash> [<hash>...]
+This option configures one or more hashes to be used in generation of user
+passwords stored in the userPassword attribute during processing of
+LDAP Password Modify Extended Operations (RFC 3062).
+The <hash> must be one of
+.BR {SSHA} ,
+.BR {SHA} ,
+.BR {SMD5} ,
+.BR {MD5} ,
+.BR {CRYPT} ,
+and
+.BR {CLEARTEXT} .
+The default is
+.BR {SSHA} .
+
+.B {SHA}
+and
+.B {SSHA}
+use the SHA-1 algorithm (FIPS 160-1), the latter with a seed.
+
+.B {MD5}
+and
+.B {SMD5}
+use the MD5 algorithm (RFC 1321), the latter with a seed.
+
+.B {CRYPT}
+uses the
+.BR crypt (3).
+
+.B {CLEARTEXT}
+indicates that the new password should be
+added to userPassword as clear text.
+
+Note that this option does not alter the normal user applications
+handling of userPassword during LDAP Add, Modify, or other LDAP operations.
+This setting is only allowed in the frontend entry.
+.TP
+.B olcReadOnly: TRUE | FALSE
+This option puts the database into "read-only" mode. Any attempts to
+modify the database will return an "unwilling to perform" error. By
+default, olcReadOnly is FALSE. Note that when this option is set
+TRUE on the frontend, it cannot be reset without restarting the
+server, since further writes to the config database will be rejected.
+.TP
+.B olcRequires: <conditions>
+Specify a set of conditions to require (default none).
+The directive may be specified globally and/or per-database;
+databases inherit global conditions, so per-database specifications
+are additive.
+.B bind
+requires bind operation prior to directory operations.
+.B LDAPv3
+requires session to be using LDAP version 3.
+.B authc
+requires authentication prior to directory operations.
+.B SASL
+requires SASL authentication prior to directory operations.
+.B strong
+requires strong authentication prior to directory operations.
+The strong keyword allows protected "simple" authentication
+as well as SASL authentication.
+.B none
+may be used to require no conditions (useful to clear out globally
+set conditions within a particular database); it must occur first
+in the list of conditions.
+.TP
+.B olcRestrict: <oplist>
+Specify a list of operations that are restricted.
+Restrictions on a specific database override any frontend setting.
+Operations can be any of
+.BR add ,
+.BR bind ,
+.BR compare ,
+.BR delete ,
+.BR extended[=<OID>] ,
+.BR modify ,
+.BR rename ,
+.BR search ,
+or the special pseudo-operations
+.B read
+and
+.BR write ,
+which respectively summarize read and write operations.
+The use of
+.I restrict write
+is equivalent to
+.I olcReadOnly: TRUE
+(see above).
+The
+.B extended
+keyword allows one to indicate the OID of the specific operation
+to be restricted.
+.TP
+.B olcSchemaDN: <dn>
+Specify the distinguished name for the subschema subentry that
+controls the entries on this server. The default is "cn=Subschema".
+.TP
+.B olcSecurity: <factors>
+Specify a set of security strength factors (separated by white space)
+to require (see
+.BR olcSaslSecprops 's
+.B minssf
+option for a description of security strength factors).
+The directive may be specified globally and/or per-database.
+.B ssf=<n>
+specifies the overall security strength factor.
+.B transport=<n>
+specifies the transport security strength factor.
+.B tls=<n>
+specifies the TLS security strength factor.
+.B sasl=<n>
+specifies the SASL security strength factor.
+.B update_ssf=<n>
+specifies the overall security strength factor to require for
+directory updates.
+.B update_transport=<n>
+specifies the transport security strength factor to require for
+directory updates.
+.B update_tls=<n>
+specifies the TLS security strength factor to require for
+directory updates.
+.B update_sasl=<n>
+specifies the SASL security strength factor to require for
+directory updates.
+.B simple_bind=<n>
+specifies the security strength factor required for
+.I simple
+username/password authentication.
+Note that the
+.B transport
+factor is measure of security provided by the underlying transport,
+e.g. ldapi:// (and eventually IPSEC). It is not normally used.
+.TP
+.B olcSizeLimit: {<integer>|unlimited}
+.TP
+.B olcSizeLimit: size[.{soft|hard}]=<integer> [...]
+Specify the maximum number of entries to return from a search operation.
+The default size limit is 500.
+Use
+.B unlimited
+to specify no limits.
+The second format allows a fine grain setting of the size limits.
+If no special qualifiers are specified, both soft and hard limits are set.
+Extra args can be added in the same value.
+Additional qualifiers are available; see
+.BR olcLimits
+for an explanation of all of the different flags.
+.TP
+.B olcSortVals: <attr> [...]
+Specify a list of multi-valued attributes whose values will always
+be maintained in sorted order. Using this option will allow Modify,
+Compare, and filter evaluations on these attributes to be performed
+more efficiently. The resulting sort order depends on the
+attributes' syntax and matching rules and may not correspond to
+lexical order or any other recognizable order.
+This setting is only allowed in the frontend entry.
+.TP
+.B olcTimeLimit: {<integer>|unlimited}
+.TP
+.B olcTimeLimit: time[.{soft|hard}]=<integer> [...]
+Specify the maximum number of seconds (in real time)
+.B slapd
+will spend answering a search request. The default time limit is 3600.
+Use
+.B unlimited
+to specify no limits.
+The second format allows a fine grain setting of the time limits.
+Extra args can be added in the same value. See
+.BR olcLimits
+for an explanation of the different flags.
+
+.SH GENERAL DATABASE OPTIONS
+Options in this section only apply to the specific database for
+which they are defined. They are supported by every
+type of backend. All of the Global Database Options may also be
+used here.
+.TP
+.B olcAddContentAcl: TRUE | FALSE
+Controls whether Add operations will perform ACL checks on
+the content of the entry being added. This check is off
+by default. See the
+.BR slapd.access (5)
+manual page for more details on ACL requirements for
+Add operations.
+.TP
+.B olcHidden: TRUE | FALSE
+Controls whether the database will be used to answer
+queries. A database that is hidden will never be
+selected to answer any queries, and any suffix configured
+on the database will be ignored in checks for conflicts
+with other databases. By default, olcHidden is FALSE.
+.TP
+.B olcLastMod: TRUE | FALSE
+Controls whether
+.B slapd
+will automatically maintain the
+modifiersName, modifyTimestamp, creatorsName, and
+createTimestamp attributes for entries. It also controls
+the entryCSN and entryUUID attributes, which are needed
+by the syncrepl provider. By default, olcLastMod is TRUE.
+.TP
+.B olcLastBind: TRUE | FALSE
+Controls whether
+.B slapd
+will automatically maintain the pwdLastSuccess attribute for
+entries. By default, olcLastBind is FALSE.
+.TP
+.B olcLimits: <selector> <limit> [<limit> [...]]
+Specify time and size limits based on the operation's initiator or
+base DN.
+The argument
+.B <selector>
+can be any of
+.RS
+.RS
+.TP
+anonymous | users | [<dnspec>=]<pattern> | group[/oc[/at]]=<pattern>
+
+.RE
+with
+.RS
+.TP
+<dnspec> ::= dn[.<type>][.<style>]
+.TP
+<type> ::= self | this
+.TP
+<style> ::= exact | base | onelevel | subtree | children | regex | anonymous
+
+.RE
+DN type
+.B self
+is the default and means the bound user, while
+.B this
+means the base DN of the operation.
+The term
+.B anonymous
+matches all unauthenticated clients.
+The term
+.B users
+matches all authenticated clients;
+otherwise an
+.B exact
+dn pattern is assumed unless otherwise specified by qualifying
+the (optional) key string
+.B dn
+with
+.B exact
+or
+.B base
+(which are synonyms), to require an exact match; with
+.BR onelevel ,
+to require exactly one level of depth match; with
+.BR subtree ,
+to allow any level of depth match, including the exact match; with
+.BR children ,
+to allow any level of depth match, not including the exact match;
+.BR regex
+explicitly requires the (default) match based on POSIX (''extended'')
+regular expression pattern.
+Finally,
+.B anonymous
+matches unbound operations; the
+.B pattern
+field is ignored.
+The same behavior is obtained by using the
+.B anonymous
+form of the
+.B <selector>
+clause.
+The term
+.BR group ,
+with the optional objectClass
+.B oc
+and attributeType
+.B at
+fields, followed by
+.BR pattern ,
+sets the limits for any DN listed in the values of the
+.B at
+attribute (default
+.BR member )
+of the
+.B oc
+group objectClass (default
+.BR groupOfNames )
+whose DN exactly matches
+.BR pattern .
+
+The currently supported limits are
+.B size
+and
+.BR time .
+
+The syntax for time limits is
+.BR time[.{soft|hard}]=<integer> ,
+where
+.I integer
+is the number of seconds slapd will spend answering a search request.
+If no time limit is explicitly requested by the client, the
+.BR soft
+limit is used; if the requested time limit exceeds the
+.BR hard
+.\"limit, an
+.\".I "Administrative limit exceeded"
+.\"error is returned.
+limit, the value of the limit is used instead.
+If the
+.BR hard
+limit is set to the keyword
+.IR soft ,
+the soft limit is used in either case; if it is set to the keyword
+.IR unlimited ,
+no hard limit is enforced.
+Explicit requests for time limits smaller or equal to the
+.BR hard
+limit are honored.
+If no limit specifier is set, the value is assigned to the
+.BR soft
+limit, and the
+.BR hard
+limit is set to
+.IR soft ,
+to preserve the original behavior.
+
+The syntax for size limits is
+.BR size[.{soft|hard|unchecked}]=<integer> ,
+where
+.I integer
+is the maximum number of entries slapd will return answering a search
+request.
+If no size limit is explicitly requested by the client, the
+.BR soft
+limit is used; if the requested size limit exceeds the
+.BR hard
+.\"limit, an
+.\".I "Administrative limit exceeded"
+.\"error is returned.
+limit, the value of the limit is used instead.
+If the
+.BR hard
+limit is set to the keyword
+.IR soft ,
+the soft limit is used in either case; if it is set to the keyword
+.IR unlimited ,
+no hard limit is enforced.
+Explicit requests for size limits smaller or equal to the
+.BR hard
+limit are honored.
+The
+.BR unchecked
+specifier sets a limit on the number of candidates a search request is allowed
+to examine.
+The rationale behind it is that searches for non-properly indexed
+attributes may result in large sets of candidates, which must be
+examined by
+.BR slapd (8)
+to determine whether they match the search filter or not.
+The
+.B unchecked
+limit provides a means to drop such operations before they are even
+started.
+If the selected candidates exceed the
+.BR unchecked
+limit, the search will abort with
+.IR "Unwilling to perform" .
+If it is set to the keyword
+.IR unlimited ,
+no limit is applied (the default).
+If it is set to
+.IR disabled ,
+the search is not even performed; this can be used to disallow searches
+for a specific set of users.
+If no limit specifier is set, the value is assigned to the
+.BR soft
+limit, and the
+.BR hard
+limit is set to
+.IR soft ,
+to preserve the original behavior.
+
+In case of no match, the global limits are used.
+The default values are the same as for
+.B olcSizeLimit
+and
+.BR olcTimeLimit ;
+no limit is set on
+.BR unchecked .
+
+If
+.B pagedResults
+control is requested, the
+.B hard
+size limit is used by default, because the request of a specific page size
+is considered an explicit request for a limitation on the number
+of entries to be returned.
+However, the size limit applies to the total count of entries returned within
+the search, and not to a single page.
+Additional size limits may be enforced; the syntax is
+.BR size.pr={<integer>|noEstimate|unlimited} ,
+where
+.I integer
+is the max page size if no explicit limit is set; the keyword
+.I noEstimate
+inhibits the server from returning an estimate of the total number
+of entries that might be returned
+(note: the current implementation does not return any estimate).
+The keyword
+.I unlimited
+indicates that no limit is applied to the pagedResults control page size.
+The syntax
+.B size.prtotal={<integer>|hard|unlimited|disabled}
+allows one to set a limit on the total number of entries that the pagedResults
+control will return.
+By default it is set to the
+.B hard
+limit which will use the size.hard value.
+When set,
+.I integer
+is the max number of entries that the whole search with pagedResults control
+can return.
+Use
+.I unlimited
+to allow unlimited number of entries to be returned, e.g. to allow
+the use of the pagedResults control as a means to circumvent size
+limitations on regular searches; the keyword
+.I disabled
+disables the control, i.e. no paged results can be returned.
+Note that the total number of entries returned when the pagedResults control
+is requested cannot exceed the
+.B hard
+size limit of regular searches unless extended by the
+.B prtotal
+switch.
+
+The \fBolcLimits\fP statement is typically used to let an unlimited
+number of entries be returned by searches performed
+with the identity used by the consumer for synchronization purposes
+by means of the RFC 4533 LDAP Content Synchronization protocol
+(see \fBolcSyncrepl\fP for details).
+
+When using subordinate databases, it is necessary for any limits that
+are to be applied across the parent and its subordinates to be defined in
+both the parent and its subordinates. Otherwise the settings on the
+subordinate databases are not honored.
+.RE
+.TP
+.B olcMaxDerefDepth: <depth>
+Specifies the maximum number of aliases to dereference when trying to
+resolve an entry, used to avoid infinite alias loops. The default is 15.
+.TP
+.B olcMultiProvider: TRUE | FALSE
+This option puts a consumer database into Multi-Provider mode. Update
+operations will be accepted from any user, not just the updatedn. The
+database must already be configured as a syncrepl consumer
+before this keyword may be set. This mode also requires a
+.B olcServerID
+(see above) to be configured.
+By default, this setting is FALSE.
+.TP
+.B olcMonitoring: TRUE | FALSE
+This option enables database-specific monitoring in the entry related
+to the current database in the "cn=Databases,cn=Monitor" subtree
+of the monitor database, if the monitor database is enabled.
+Currently, only the MDB database provides database-specific monitoring.
+If monitoring is supported by the backend it defaults to TRUE, otherwise
+FALSE.
+.TP
+.B olcPlugin: <plugin_type> <lib_path> <init_function> [<arguments>]
+Configure a SLAPI plugin. See the
+.BR slapd.plugin (5)
+manpage for more details.
+.TP
+.B olcRootDN: <dn>
+Specify the distinguished name that is not subject to access control
+or administrative limit restrictions for operations on this database.
+This DN may or may not be associated with an entry. An empty root
+DN (the default) specifies no root access is to be granted. It is
+recommended that the rootdn only be specified when needed (such as
+when initially populating a database). If the rootdn is within
+a namingContext (suffix) of the database, a simple bind password
+may also be provided using the
+.B olcRootPW
+directive. Many optional features, including syncrepl, require the
+rootdn to be defined for the database.
+The
+.B olcRootDN
+of the
+.B cn=config
+database defaults to
+.B cn=config
+itself.
+.TP
+.B olcRootPW: <password>
+Specify a password (or hash of the password) for the rootdn. The
+password can only be set if the rootdn is within the namingContext
+(suffix) of the database.
+This option accepts all RFC 2307 userPassword formats known to
+the server (see
+.B olcPasswordHash
+description) as well as cleartext.
+.BR slappasswd (8)
+may be used to generate a hash of a password. Cleartext
+and \fB{CRYPT}\fP passwords are not recommended. If empty
+(the default), authentication of the root DN is by other means
+(e.g. SASL). Use of SASL is encouraged.
+.TP
+.B olcSubordinate: [TRUE | FALSE | advertise]
+Specify that the current backend database is a subordinate of another
+backend database. A subordinate database may have only one suffix. This
+option may be used to glue multiple databases into a single namingContext.
+If the suffix of the current database is within the namingContext of a
+superior database, searches against the superior database will be
+propagated to the subordinate as well. All of the databases
+associated with a single namingContext should have identical rootdns.
+Behavior of other LDAP operations is unaffected by this setting. In
+particular, it is not possible to use moddn to move an entry from
+one subordinate to another subordinate within the namingContext.
+
+If the optional \fBadvertise\fP flag is supplied, the naming context of
+this database is advertised in the root DSE. The default is to hide this
+database context, so that only the superior context is visible.
+
+If the slap tools
+.BR slapcat (8),
+.BR slapadd (8),
+.BR slapmodify (8),
+or
+.BR slapindex (8)
+are used on the superior database, any glued subordinates that support
+these tools are opened as well.
+
+Databases that are glued together should usually be configured with the
+same indices (assuming they support indexing), even for attributes that
+only exist in some of these databases. In general, all of the glued
+databases should be configured as similarly as possible, since the intent
+is to provide the appearance of a single directory.
+
+Note that the subordinate functionality is implemented internally
+by the \fIglue\fP overlay and as such its behavior will interact with other
+overlays in use. By default, the glue overlay is automatically configured as
+the last overlay on the superior database. Its position on the database
+can be explicitly configured by setting an \fBoverlay glue\fP directive
+at the desired position. This explicit configuration is necessary e.g.
+when using the \fIsyncprov\fP overlay, which needs to follow \fIglue\fP
+in order to work over all of the glued databases. E.g.
+.RS
+.nf
+ dn: olcDatabase={1}mdb,cn=config
+ olcSuffix: dc=example,dc=com
+ ...
+
+ dn: olcOverlay={0}glue,olcDatabase={1}mdb,cn=config
+ ...
+
+ dn: olcOverlay={1}syncprov,olcDatabase={1}mdb,cn=config
+ ...
+.fi
+.RE
+See the Overlays section below for more details.
+.TP
+.B olcSuffix: <dn suffix>
+Specify the DN suffix of queries that will be passed to this
+backend database. Multiple suffix lines can be given and at least one is
+required for each database definition.
+
+If the suffix of one database is "inside" that of another, the database
+with the inner suffix must come first in the configuration file.
+You may also want to glue such databases together with the
+.B olcSubordinate
+attribute.
+.TP
+.B olcSyncUseSubentry: TRUE | FALSE
+Store the syncrepl contextCSN in a subentry instead of the context entry
+of the database. The subentry's RDN will be "cn=ldapsync". The default is
+FALSE, meaning the contextCSN is stored in the context entry.
+.HP
+.hy 0
+.B olcSyncrepl: rid=<replica ID>
+.B provider=ldap[s]://<hostname>[:port]
+.B searchbase=<base DN>
+.B [type=refreshOnly|refreshAndPersist]
+.B [interval=dd:hh:mm:ss]
+.B [retry=[<retry interval> <# of retries>]+]
+.B [filter=<filter str>]
+.B [scope=sub|one|base|subord]
+.B [attrs=<attr list>]
+.B [exattrs=<attr list>]
+.B [attrsonly]
+.B [sizelimit=<limit>]
+.B [timelimit=<limit>]
+.B [schemachecking=on|off]
+.B [network\-timeout=<seconds>]
+.B [timeout=<seconds>]
+.B [tcp\-user\-timeout=<milliseconds>]
+.B [bindmethod=simple|sasl]
+.B [binddn=<dn>]
+.B [saslmech=<mech>]
+.B [authcid=<identity>]
+.B [authzid=<identity>]
+.B [credentials=<passwd>]
+.B [realm=<realm>]
+.B [secprops=<properties>]
+.B [keepalive=<idle>:<probes>:<interval>]
+.B [starttls=yes|critical]
+.B [tls_cert=<file>]
+.B [tls_key=<file>]
+.B [tls_cacert=<file>]
+.B [tls_cacertdir=<path>]
+.B [tls_reqcert=never|allow|try|demand]
+.B [tls_reqsan=never|allow|try|demand]
+.B [tls_cipher_suite=<ciphers>]
+.B [tls_ecname=<names>]
+.B [tls_crlcheck=none|peer|all]
+.B [tls_protocol_min=<major>[.<minor>]]
+.B [suffixmassage=<real DN>]
+.B [logbase=<base DN>]
+.B [logfilter=<filter str>]
+.B [syncdata=default|accesslog|changelog]
+.B [lazycommit]
+.RS
+Specify the current database as a consumer which is kept up-to-date with the
+provider content by establishing the current
+.BR slapd (8)
+as a replication consumer site running a
+.B syncrepl
+replication engine.
+The consumer content is kept synchronized to the provider content using
+the LDAP Content Synchronization protocol. Refer to the
+"OpenLDAP Administrator's Guide" for detailed information on
+setting up a replicated
+.B slapd
+directory service using the
+.B syncrepl
+replication engine.
+
+.B rid
+identifies the current
+.B syncrepl
+directive within the replication consumer site.
+It is a non-negative integer not greater than 999 (limited
+to three decimal digits).
+
+.B provider
+specifies the replication provider site containing the provider content
+as an LDAP URI. If <port> is not given, the standard LDAP port number
+(389 or 636) is used.
+
+The content of the
+.B syncrepl
+consumer is defined using a search
+specification as its result set. The consumer
+.B slapd
+will send search requests to the provider
+.B slapd
+according to the search specification. The search specification includes
+.BR searchbase ", " scope ", " filter ", " attrs ", " attrsonly ", " sizelimit ", "
+and
+.B timelimit
+parameters as in the normal search specification. The
+.B exattrs
+option may also be used to specify attributes that should be omitted
+from incoming entries.
+The \fBscope\fP defaults to \fBsub\fP, the \fBfilter\fP defaults to
+\fB(objectclass=*)\fP, and there is no default \fBsearchbase\fP. The
+\fBattrs\fP list defaults to \fB"*,+"\fP to return all user and operational
+attributes, and \fBattrsonly\fP and \fBexattrs\fP are unset by default.
+The \fBsizelimit\fP and \fBtimelimit\fP only
+accept "unlimited" and positive integers, and both default to "unlimited".
+The \fBsizelimit\fP and \fBtimelimit\fP parameters define
+a consumer requested limitation on the number of entries that can be returned
+by the LDAP Content Synchronization operation; these should be left unchanged
+from the default otherwise replication may never succeed.
+Note, however, that any provider-side limits for the replication identity
+will be enforced by the provider regardless of the limits requested
+by the LDAP Content Synchronization operation, much like for any other
+search operation.
+
+The LDAP Content Synchronization protocol has two operation types.
+In the
+.B refreshOnly
+operation, the next synchronization search operation
+is periodically rescheduled at an interval time (specified by
+.B interval
+parameter; 1 day by default)
+after each synchronization operation finishes.
+In the
+.B refreshAndPersist
+operation, a synchronization search remains persistent in the provider slapd.
+Further updates to the provider will generate
+.B searchResultEntry
+to the consumer slapd as the search responses to the persistent
+synchronization search. If the initial search fails due to an error, the
+next synchronization search operation is periodically rescheduled at an
+interval time (specified by
+.B interval
+parameter; 1 day by default)
+
+If an error occurs during replication, the consumer will attempt to
+reconnect according to the
+.B retry
+parameter which is a list of the <retry interval> and <# of retries> pairs.
+For example, retry="60 10 300 3" lets the consumer retry every 60 seconds
+for the first 10 times and then retry every 300 seconds for the next 3
+times before stop retrying. The `+' in <# of retries> means indefinite
+number of retries until success.
+If no
+.B retry
+is specified, by default syncrepl retries every hour forever.
+
+The schema checking can be enforced at the LDAP Sync
+consumer site by turning on the
+.B schemachecking
+parameter. The default is \fBoff\fP.
+Schema checking \fBon\fP means that replicated entries must have
+a structural objectClass, must obey to objectClass requirements
+in terms of required/allowed attributes, and that naming attributes
+and distinguished values must be present.
+As a consequence, schema checking should be \fBoff\fP when partial
+replication is used.
+
+The
+.B network\-timeout
+parameter sets how long the consumer will wait to establish a
+network connection to the provider. Once a connection is
+established, the
+.B timeout
+parameter determines how long the consumer will wait for the initial
+Bind request to complete. The defaults for these parameters come
+from
+.BR ldap.conf (5).
+The
+.B tcp\-user\-timeout
+parameter, if non-zero, corresponds to the
+.B TCP_USER_TIMEOUT
+set on the target connections, overriding the operating system setting.
+Only some systems support the customization of this parameter, it is
+ignored otherwise and system-wide settings are used.
+
+A
+.B bindmethod
+of
+.B simple
+requires the options
+.B binddn
+and
+.B credentials
+and should only be used when adequate security services
+(e.g. TLS or IPSEC) are in place.
+.B REMEMBER: simple bind credentials must be in cleartext!
+A
+.B bindmethod
+of
+.B sasl
+requires the option
+.B saslmech.
+Depending on the mechanism, an authentication identity and/or
+credentials can be specified using
+.B authcid
+and
+.B credentials.
+The
+.B authzid
+parameter may be used to specify an authorization identity.
+Specific security properties (as with the
+.B sasl\-secprops
+keyword above) for a SASL bind can be set with the
+.B secprops
+option. A non default SASL realm can be set with the
+.B realm
+option.
+The identity used for synchronization by the consumer should be allowed
+to receive an unlimited number of entries in response to a search request.
+The provider, other than allowing authentication of the syncrepl identity,
+should grant that identity appropriate access privileges to the data
+that is being replicated (\fBaccess\fP directive), and appropriate time
+and size limits.
+This can be accomplished by either allowing unlimited \fBsizelimit\fP
+and \fBtimelimit\fP, or by setting an appropriate \fBlimits\fP statement
+in the consumer's configuration (see \fBsizelimit\fP and \fBlimits\fP
+for details).
+
+The
+.B keepalive
+parameter sets the values of \fIidle\fP, \fIprobes\fP, and \fIinterval\fP
+used to check whether a socket is alive;
+.I idle
+is the number of seconds a connection needs to remain idle before TCP
+starts sending keepalive probes;
+.I probes
+is the maximum number of keepalive probes TCP should send before dropping
+the connection;
+.I interval
+is interval in seconds between individual keepalive probes.
+Only some systems support the customization of these values;
+the
+.B keepalive
+parameter is ignored otherwise, and system-wide settings are used.
+
+The
+.B starttls
+parameter specifies use of the StartTLS extended operation
+to establish a TLS session before Binding to the provider. If the
+.B critical
+argument is supplied, the session will be aborted if the StartTLS request
+fails. Otherwise the syncrepl session continues without TLS. The
+.B tls_reqcert
+setting defaults to "demand", the
+.B tls_reqsan
+setting defaults to "allow", and the other TLS settings
+default to the same as the main slapd TLS settings.
+
+The
+.B suffixmassage
+parameter allows the consumer to pull entries from a remote directory
+whose DN suffix differs from the local directory. The portion of the
+remote entries' DNs that matches the \fIsearchbase\fP will be replaced
+with the suffixmassage DN.
+
+Rather than replicating whole entries, the consumer can query logs of
+data modifications. This mode of operation is referred to as \fIdelta
+syncrepl\fP. In addition to the above parameters, the
+.B logbase
+and
+.B logfilter
+parameters must be set appropriately for the log that will be used. The
+.B syncdata
+parameter must be set to either "accesslog" if the log conforms to the
+.BR slapo\-accesslog (5)
+log format, or "changelog" if the log conforms
+to the obsolete \fIchangelog\fP format. If the
+.B syncdata
+parameter is omitted or set to "default" then the log parameters are
+ignored.
+
+The
+.B lazycommit
+parameter tells the underlying database that it can store changes without
+performing a full flush after each change. This may improve performance
+for the consumer, while sacrificing safety or durability.
+.RE
+.TP
+.B olcUpdateDN: <dn>
+This option is only applicable in a replica
+database.
+It specifies the DN permitted to update (subject to access controls)
+the replica. It is only needed in certain push-mode
+replication scenarios. Generally, this DN
+.I should not
+be the same as the
+.B rootdn
+used at the provider.
+.TP
+.B olcUpdateRef: <url>
+Specify the referral to pass back when
+.BR slapd (8)
+is asked to modify a replicated local database.
+If multiple values are specified, each url is provided.
+
+.SH DATABASE-SPECIFIC OPTIONS
+Each database may allow specific configuration options; they are
+documented separately in the backends' manual pages. See the
+.BR slapd.backends (5)
+manual page for an overview of available backends.
+.SH OVERLAYS
+An overlay is a piece of
+code that intercepts database operations in order to extend or change
+them. Overlays are pushed onto
+a stack over the database, and so they will execute in the reverse
+of the order in which they were configured and the database itself
+will receive control last of all.
+
+Overlays must be configured as child entries of a specific database. The
+entry's RDN must be of the form
+.B olcOverlay={x}<overlaytype>
+and the entry must have the olcOverlayConfig objectClass. Normally the
+config engine generates the "{x}" index in the RDN automatically, so
+it can be omitted when initially loading these entries.
+
+See the
+.BR slapd.overlays (5)
+manual page for an overview of available overlays.
+.SH EXAMPLES
+.LP
+Here is a short example of a configuration in LDIF suitable for use with
+.BR slapadd (8)
+:
+.LP
+.RS
+.nf
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcPidFile: LOCALSTATEDIR/run/slapd.pid
+olcAttributeOptions: x\-hidden lang\-
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file://SYSCONFDIR/schema/core.ldif
+
+dn: olcDatabase=frontend,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcFrontendConfig
+olcDatabase: frontend
+# Subtypes of "name" (e.g. "cn" and "ou") with the
+# option ";x\-hidden" can be searched for/compared,
+# but are not shown. See \fBslapd.access\fP(5).
+olcAccess: to attrs=name;x\-hidden by * =cs
+# Protect passwords. See \fBslapd.access\fP(5).
+olcAccess: to attrs=userPassword by * auth
+# Read access to other attributes and entries.
+olcAccess: to * by * read
+
+# set a rootpw for the config database so we can bind.
+# deny access to everyone else.
+dn: olcDatabase=config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: config
+olcRootPW: {SSHA}XKYnrjvGT3wZFQrDD5040US592LxsdLy
+olcAccess: to * by * none
+
+dn: olcDatabase=mdb,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcMdbConfig
+olcDatabase: mdb
+olcSuffix: "dc=our\-domain,dc=com"
+# The database directory MUST exist prior to
+# running slapd AND should only be accessible
+# by the slapd/tools. Mode 0700 recommended.
+olcDbDirectory: LOCALSTATEDIR/openldap\-data
+# Indices to maintain
+olcDbIndex: objectClass eq
+olcDbIndex: cn,sn,mail pres,eq,approx,sub
+
+# We serve small clients that do not handle referrals,
+# so handle remote lookups on their behalf.
+dn: olcDatabase=ldap,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcLdapConfig
+olcDatabase: ldap
+olcSuffix: ""
+olcDbUri: ldap://ldap.some\-server.com/
+.fi
+.RE
+.LP
+Assuming the above data was saved in a file named "config.ldif" and the
+ETCDIR/slapd.d directory has been created, this command will initialize
+the configuration:
+.RS
+.nf
+slapadd \-F ETCDIR/slapd.d \-n 0 \-l config.ldif
+.fi
+.RE
+
+.LP
+"OpenLDAP Administrator's Guide" contains a longer annotated
+example of a slapd configuration.
+
+Alternatively, an existing slapd.conf file can be converted to the new
+format using slapd or any of the slap tools:
+.RS
+.nf
+slaptest \-f ETCDIR/slapd.conf \-F ETCDIR/slapd.d
+.fi
+.RE
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.TP
+ETCDIR/slapd.d
+default slapd configuration directory
+.SH SEE ALSO
+.BR ldap (3),
+.BR ldif (5),
+.BR gnutls\-cli (1),
+.BR slapd.access (5),
+.BR slapd.backends (5),
+.BR slapd.conf (5),
+.BR slapd.overlays (5),
+.BR slapd.plugin (5),
+.BR slapd (8),
+.BR slapacl (8),
+.BR slapadd (8),
+.BR slapauth (8),
+.BR slapcat (8),
+.BR slapdn (8),
+.BR slapindex (8),
+.BR slapmodify (8),
+.BR slappasswd (8),
+.BR slaptest (8).
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man5/slapd-dnssrv.5 b/doc/man/man5/slapd-dnssrv.5
new file mode 100644
index 0000000..f29c620
--- /dev/null
+++ b/doc/man/man5/slapd-dnssrv.5
@@ -0,0 +1,49 @@
+.TH SLAPD-DNSSRV 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd\-dnssrv \- DNS SRV referral backend to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The DNSSRV backend to
+.BR slapd (8)
+serves up referrals based upon SRV resource records held in
+the Domain Name System.
+.LP
+This backend is experimental.
+.SH CONFIGURATION
+The DNSSRV backend has no backend nor database specific options.
+It is configured simply by "database dnssrv" followed a suffix
+directive, e.g. suffix "".
+.SH ACCESS CONTROL
+The
+.B dnssrv
+backend does not honor all ACL semantics as described in
+.BR slapd.access (5).
+In fact, this backend only implements the
+.B search
+operation when the
+.B manageDSAit
+control (RFC 3296) is used, otherwise for every operation a referral,
+whenever appropriate, or an error is returned.
+Currently, there is no means to condition the returning of the referral
+by means of ACLs; no access control is implemented, except for
+.B read (=r)
+access to the returned entries, which is actually provided by the frontend.
+Note, however, that the information returned by this backend is collected
+through the DNS, so it is public by definition.
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.br
+.SH SEE ALSO
+\fB"OpenLDAP Root Service - An experimental LDAP referral
+service"\fR [RFC 3088],
+.br
+\fB"OpenLDAP LDAP Root Service"\fR <http://www.openldap.org/faq/?file=393)>,
+.br
+.BR slapd.conf (5),
+.BR slapd (8)
diff --git a/doc/man/man5/slapd-ldap.5 b/doc/man/man5/slapd-ldap.5
new file mode 100644
index 0000000..ffcbe81
--- /dev/null
+++ b/doc/man/man5/slapd-ldap.5
@@ -0,0 +1,700 @@
+.TH SLAPD-LDAP 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd\-ldap \- LDAP backend to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The LDAP backend to
+.BR slapd (8)
+is not an actual database; instead it acts as a proxy to forward incoming
+requests to another LDAP server. While processing requests it will also
+chase referrals, so that referrals are fully processed instead of being
+returned to the slapd client.
+
+Sessions that explicitly Bind to the back-ldap database always create their
+own private connection to the remote LDAP server. Anonymous sessions will
+share a single anonymous connection to the remote server. For sessions bound
+through other mechanisms, all sessions with the same DN will share the
+same connection. This connection pooling strategy can enhance the proxy's
+efficiency by reducing the overhead of repeatedly making/breaking multiple
+connections.
+
+The ldap database can also act as an information service, i.e. the identity
+of locally authenticated clients is asserted to the remote server, possibly
+in some modified form.
+For this purpose, the proxy binds to the remote server with some
+administrative identity, and, if required, authorizes the asserted identity.
+See the
+.IR idassert\- *
+rules below.
+The administrative identity of the proxy, on the remote server, must be
+allowed to authorize by means of appropriate
+.B authzTo
+rules; see
+.BR slapd.conf (5)
+for details.
+
+The proxy instance of
+.BR slapd (8)
+must contain schema information for the attributes and objectClasses
+used in filters, request DNs and request-related data in general.
+It should also contain schema information for the data returned
+by the proxied server.
+It is the responsibility of the proxy administrator to keep the schema
+of the proxy lined up with that of the proxied server.
+
+.LP
+Note: When looping back to the same instance of
+.BR slapd (8),
+each connection requires a new thread; as a consequence, the
+.BR slapd (8)
+\fBthreads\fP parameter may need some tuning. In those cases,
+one may consider using
+.BR slapd\-relay (5)
+instead, which performs the relayed operation
+internally and thus reuses the same connection.
+
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the LDAP backend database.
+That is, they must follow a "database ldap" line and come before any
+subsequent "backend" or "database" lines.
+Other database options are described in the
+.BR slapd.conf (5)
+manual page.
+
+.LP
+Note: In early versions of back-ldap it was recommended to always set
+.LP
+.RS
+.nf
+lastmod off
+.fi
+.RE
+.LP
+for
+.B ldap
+and
+.B meta
+databases.
+This was required because operational attributes related to entry creation
+and modification should not be proxied, as they could be mistakenly written
+to the target server(s), generating an error.
+The current implementation automatically sets lastmod to \fBoff\fP,
+so its use is redundant and should be omitted.
+
+.TP
+.B uri <ldapurl>
+LDAP server to use. Multiple URIs can be set in a single
+.B ldapurl
+argument, resulting in the underlying library automatically
+calling the first server of the list that responds, e.g.
+
+\fBuri "ldap://host/ ldap://backup\-host/"\fP
+
+The URI list is space- or comma-separated.
+Whenever the server that responds is not the first one in the list,
+the list is rearranged and the responsive server is moved to the head,
+so that it will be first contacted the next time a connection
+needs to be created.
+.HP
+.hy 0
+.B acl\-bind
+.B bindmethod=simple|sasl [binddn=<simple DN>] [credentials=<simple password>]
+.B [saslmech=<SASL mech>] [secprops=<properties>] [realm=<realm>]
+.B [authcId=<authentication ID>] [authzId=<authorization ID>]
+.B [starttls=no|yes|critical]
+.B [tls_cert=<file>]
+.B [tls_key=<file>]
+.B [tls_cacert=<file>]
+.B [tls_cacertdir=<path>]
+.B [tls_reqcert=never|allow|try|demand]
+.B [tls_reqsan=never|allow|try|demand]
+.B [tls_cipher_suite=<ciphers>]
+.B [tls_ecname=<names>]
+.B [tls_protocol_min=<major>[.<minor>]]
+.B [tls_crlcheck=none|peer|all]
+.RS
+Allows one to define the parameters of the authentication method that is
+internally used by the proxy to collect info related to access control,
+and whenever an operation occurs with the identity of the rootdn
+of the LDAP proxy database.
+The identity defined by this directive, according to the properties
+associated to the authentication method, is supposed to have read access
+on the target server to attributes used on the proxy for ACL checking.
+
+There is no risk of giving away such values; they are only used to
+check permissions.
+The default is to use
+.BR simple
+bind, with empty \fIbinddn\fP and \fIcredentials\fP,
+which means that the related operations will be performed anonymously.
+If not set, and if \fBidassert\-bind\fP is defined, this latter identity
+is used instead. See \fBidassert\-bind\fP for details.
+
+The connection between the proxy database and the remote server
+associated to this identity is cached regardless of the lifespan
+of the client-proxy connection that first established it.
+
+.B This identity is not implicitly used by the proxy
+.B when the client connects anonymously.
+The
+.B idassert\-bind
+feature, instead, in some cases can be crafted to implement that behavior,
+which is \fIintrinsically unsafe and should be used with extreme care\fP.
+
+The TLS settings default to the same as the main slapd TLS settings,
+except for
+.B tls_reqcert
+which defaults to "demand", and
+.B tls_reqsan
+which defaults to "allow".
+.RE
+
+.TP
+.B cancel {ABANDON|ignore|exop[\-discover]}
+Defines how to handle operation cancellation.
+By default,
+.B abandon
+is invoked, so the operation is abandoned immediately.
+If set to
+.BR ignore ,
+no action is taken and any further response is ignored; this may result
+in further response messages to be queued for that connection, so it is
+recommended that long lasting connections are timed out either by
+.I idle\-timeout
+or
+.IR conn\-ttl ,
+so that resources eventually get released.
+If set to
+.BR exop ,
+a
+.I cancel
+operation (RFC 3909) is issued, resulting in the cancellation
+of the current operation; the
+.I cancel
+operation waits for remote server response, so its use
+may not be recommended.
+If set to
+.BR exop\-discover ,
+support of the
+.I cancel
+extended operation is detected by reading the remote server's root DSE.
+
+.TP
+.B chase\-referrals {YES|no}
+enable/disable automatic referral chasing, which is delegated to the
+underlying libldap, with rebinding eventually performed if the
+\fBrebind\-as\-user\fP directive is used. The default is to chase referrals.
+
+.TP
+.B conn\-pool\-max <int>
+This directive defines the maximum size of the privileged connections pool.
+
+.TP
+.B conn\-ttl <time>
+This directive causes a cached connection to be dropped and recreated
+after a given ttl, regardless of being idle or not.
+
+.TP
+.B idassert\-authzFrom <authz-regexp>
+if defined, selects what
+.I local
+identities are authorized to exploit the identity assertion feature.
+The string
+.B <authz-regexp>
+mostly follows the rules defined for the
+.I authzFrom
+attribute.
+See
+.BR slapd.conf (5),
+section related to
+.BR authz\-policy ,
+for details on the syntax of this field. This parameter differs from
+the documented behavior in relation to the meaning of *, which in this
+case allows anonymous rather than denies.
+
+.HP
+.hy 0
+.B idassert\-bind
+.B bindmethod=none|simple|sasl [binddn=<simple DN>] [credentials=<simple password>]
+.B [saslmech=<SASL mech>] [secprops=<properties>] [realm=<realm>]
+.B [authcId=<authentication ID>] [authzId=<authorization ID>]
+.B [authz={native|proxyauthz}] [mode=<mode>] [flags=<flags>]
+.B [starttls=no|yes|critical]
+.B [tls_cert=<file>]
+.B [tls_key=<file>]
+.B [tls_cacert=<file>]
+.B [tls_cacertdir=<path>]
+.B [tls_reqcert=never|allow|try|demand]
+.B [tls_reqsan=never|allow|try|demand]
+.B [tls_cipher_suite=<ciphers>]
+.B [tls_ecname=<names>]
+.B [tls_protocol_min=<version>]
+.B [tls_crlcheck=none|peer|all]
+.RS
+Allows one to define the parameters of the authentication method that is
+internally used by the proxy to authorize connections that are
+authenticated by other databases.
+Direct binds are always proxied without any idassert handling.
+
+The identity defined by this directive, according to the properties
+associated to the authentication method, is supposed to have auth access
+on the target server to attributes used on the proxy for authentication
+and authorization, and to be allowed to authorize the users.
+This requires to have
+.B proxyAuthz
+privileges on a wide set of DNs, e.g.
+.BR authzTo=dn.subtree:"" ,
+and the remote server to have
+.B authz\-policy
+set to
+.B to
+or
+.BR both .
+See
+.BR slapd.conf (5)
+for details on these statements and for remarks and drawbacks about
+their usage.
+The supported bindmethods are
+
+\fBnone|simple|sasl\fP
+
+where
+.B none
+is the default, i.e. no \fIidentity assertion\fP is performed.
+
+The
+.B authz
+parameter is used to instruct the SASL bind to exploit
+.B native
+SASL authorization, if available; since connections are cached,
+this should only be used when authorizing with a fixed identity
+(e.g. by means of the
+.B authzDN
+or
+.B authzID
+parameters).
+Otherwise, the default
+.B proxyauthz
+is used, i.e. the proxyAuthz control (Proxied Authorization, RFC 4370)
+is added to all operations.
+
+The supported modes are:
+
+\fB<mode> := {legacy|anonymous|none|self}\fP
+
+If
+.B <mode>
+is not present, and
+.B authzId
+is given, the proxy always authorizes that identity.
+.B <authorization ID>
+can be
+
+\fBu:<user>\fP
+
+\fB[dn:]<DN>\fP
+
+The former is supposed to be expanded by the remote server according
+to the authz rules; see
+.BR slapd.conf (5)
+for details.
+In the latter case, whether or not the
+.B dn:
+prefix is present, the string must pass DN validation and normalization.
+
+The default mode is
+.BR legacy ,
+which implies that the proxy will either perform a simple bind as the
+.I authcDN
+or a SASL bind as the
+.I authcID
+and assert the client's identity when it is not anonymous.
+The other modes imply that the proxy will always either perform a simple bind
+as the
+.IR authcDN
+or a SASL bind as the
+.IR authcID ,
+unless restricted by
+.BR idassert\-authzFrom
+rules (see below), in which case the operation will fail;
+eventually, it will assert some other identity according to
+.BR <mode> .
+Other identity assertion modes are
+.BR anonymous
+and
+.BR self ,
+which respectively mean that the
+.I empty
+or the
+.IR client 's
+identity
+will be asserted;
+.BR none ,
+which means that no proxyAuthz control will be used, so the
+.I authcDN
+or the
+.I authcID
+identity will be asserted.
+For all modes that require the use of the
+.I proxyAuthz
+control, on the remote server the proxy identity must have appropriate
+.I authzTo
+permissions, or the asserted identities must have appropriate
+.I authzFrom
+permissions. Note, however, that the ID assertion feature is mostly
+useful when the asserted identities do not exist on the remote server.
+
+Flags can be
+
+\fBoverride,[non\-]prescriptive,proxy\-authz\-[non\-]critical,dn\-{authzid|whoami}\fP
+
+When the
+.B override
+flag is used, identity assertion takes place even when the database
+is authorizing for the identity of the client, i.e. after binding
+with the provided identity, and thus authenticating it, the proxy
+performs the identity assertion using the configured identity and
+authentication method.
+
+When the
+.B prescriptive
+flag is used (the default), operations fail with
+\fIinappropriateAuthentication\fP
+for those identities whose assertion is not allowed by the
+.B idassert\-authzFrom
+patterns.
+If the
+.B non\-prescriptive
+flag is used, operations are performed anonymously for those identities
+whose assertion is not allowed by the
+.B idassert\-authzFrom
+patterns.
+
+When the
+.B proxy\-authz\-non\-critical
+flag is used (the default), the proxyAuthz control is not marked as critical,
+in violation of RFC 4370. Use of
+.B proxy\-authz\-critical
+is recommended.
+
+When the
+.B dn\-authzid
+flag is used, RFC 3829 LDAP Authorization Identity Controls
+is used to retrieve the identity associated to the SASL identity;
+when the
+.B dn\-whoami
+flag is used, RFC 4532 LDAP Who am I? Operation is performed
+after the bind for the same purpose.
+
+The TLS settings default to the same as the main slapd TLS settings,
+except for
+.B tls_reqcert
+which defaults to "demand", and
+.B tls_reqsan
+which defaults to "allow".
+
+The identity associated to this directive is also used for privileged
+operations whenever \fBidassert\-bind\fP is defined and \fBacl\-bind\fP
+is not. See \fBacl\-bind\fP for details.
+
+.TP
+.B idassert-passthru <authz-regexp>
+if defined, selects what
+.I local
+identities bypass the identity assertion feature.
+Those identities need to be known by the remote host.
+The string
+.B <authz-regexp>
+follows the rules defined for the
+.I authzFrom
+attribute.
+See
+.BR slapd.conf (5),
+section related to
+.BR authz\-policy ,
+for details on the syntax of this field.
+
+.TP
+.B idle\-timeout <time>
+This directive causes a cached connection to be dropped an recreated
+after it has been idle for the specified time.
+
+.TP
+.B keepalive <idle>:<probes>:<interval>
+The
+.B keepalive
+parameter sets the values of \fIidle\fP, \fIprobes\fP, and \fIinterval\fP
+used to check whether a socket is alive;
+.I idle
+is the number of seconds a connection needs to remain idle before TCP
+starts sending keepalive probes;
+.I probes
+is the maximum number of keepalive probes TCP should send before dropping
+the connection;
+.I interval
+is interval in seconds between individual keepalive probes.
+Only some systems support the customization of these values;
+the
+.B keepalive
+parameter is ignored otherwise, and system-wide settings are used.
+
+.TP
+.B tcp\-user\-timeout <milliseconds>
+If non-zero, corresponds to the
+.B TCP_USER_TIMEOUT
+set on the target connections, overriding the operating system setting.
+Only some systems support the customization of this parameter, it is
+ignored otherwise and system-wide settings are used.
+
+.TP
+.B network\-timeout <time>
+Sets the network timeout value after which
+.BR poll (2)/ select (2)
+following a
+.BR connect (2)
+returns in case of no activity.
+The value is in seconds, and it can be specified as for
+.BR idle\-timeout .
+
+.TP
+.B norefs <NO|yes>
+If
+.BR yes ,
+do not return search reference responses.
+By default, they are returned unless request is LDAPv2.
+
+.TP
+.B omit-unknown-schema <NO|yes>
+If
+.BR yes ,
+do not return objectClasses or attributes that are not known to the local server.
+The default is to return all schema elements.
+
+.TP
+.B noundeffilter <NO|yes>
+If
+.BR yes ,
+return success instead of searching if a filter is undefined or contains
+undefined portions.
+By default, the search is propagated after replacing undefined portions
+with
+.BR (!(objectClass=*)) ,
+which corresponds to the empty result set.
+
+.TP
+.B onerr {CONTINUE|stop}
+This directive allows one to select the behavior in case an error is returned
+by the remote server during a search.
+The default, \fBcontinue\fP, consists in returning success.
+If the value is set to \fBstop\fP, the error is returned to the client.
+
+.TP
+.B protocol\-version {0,2,3}
+This directive indicates what protocol version must be used to contact
+the remote server.
+If set to 0 (the default), the proxy uses the same protocol version
+used by the client, otherwise the requested protocol is used.
+The proxy returns \fIunwillingToPerform\fP if an operation that is
+incompatible with the requested protocol is attempted.
+
+.TP
+.B proxy\-whoami {NO|yes}
+Turns on proxying of the WhoAmI extended operation. If this option is
+given, back-ldap will replace slapd's original WhoAmI routine with its
+own. On slapd sessions that were authenticated by back-ldap, the WhoAmI
+request will be forwarded to the remote LDAP server. Other sessions will
+be handled by the local slapd, as before. This option is mainly useful
+in conjunction with Proxy Authorization.
+
+.TP
+.B quarantine <interval>,<num>[;<interval>,<num>[...]]
+Turns on quarantine of URIs that returned
+.IR LDAP_UNAVAILABLE ,
+so that an attempt to reconnect only occurs at given intervals instead
+of any time a client requests an operation.
+The pattern is: retry only after at least
+.I interval
+seconds elapsed since last attempt, for exactly
+.I num
+times; then use the next pattern.
+If
+.I num
+for the last pattern is "\fB+\fP", it retries forever; otherwise,
+no more retries occur.
+The process can be restarted by resetting the \fIolcDbQuarantine\fP
+attribute of the database entry in the configuration backend.
+
+.TP
+.B rebind\-as\-user {NO|yes}
+If this option is given, the client's bind credentials are remembered
+for rebinds, when trying to re-establish a broken connection,
+or when chasing a referral, if
+.B chase\-referrals
+is set to
+.IR yes .
+
+.TP
+.B session\-tracking\-request {NO|yes}
+Adds session tracking control for all requests.
+The client's IP and hostname, and the identity associated to each request,
+if known, are sent to the remote server for informational purposes.
+This directive is incompatible with setting \fIprotocol\-version\fP to 2.
+
+.TP
+.B single\-conn {NO|yes}
+Discards current cached connection when the client rebinds.
+
+.TP
+.B t\-f\-support {NO|yes|discover}
+enable if the remote server supports absolute filters
+(see \fIRFC 4526\fP for details).
+If set to
+.BR discover ,
+support is detected by reading the remote server's root DSE.
+
+.TP
+.B timeout [<op>=]<val> [...]
+This directive allows one to set per-operation timeouts.
+Operations can be
+
+\fB<op> ::= bind, add, delete, modrdn, modify, compare, search\fP
+
+The overall duration of the \fBsearch\fP operation is controlled either
+by the \fBtimelimit\fP parameter or by server-side enforced
+time limits (see \fBtimelimit\fP and \fBlimits\fP in
+.BR slapd.conf (5)
+for details).
+This \fBtimeout\fP parameter controls how long the target can be
+irresponsive before the operation is aborted.
+Timeout is meaningless for the remaining operations,
+\fBunbind\fP and \fBabandon\fP, which do not imply any response,
+while it is not yet implemented in currently supported \fBextended\fP
+operations.
+If no operation is specified, the timeout \fBval\fP affects all
+supported operations.
+
+Note: if the timelimit is exceeded, the operation is cancelled
+(according to the \fBcancel\fP directive);
+the protocol does not provide any means to rollback operations,
+so the client will not be notified about the result of the operation,
+which may eventually succeeded or not.
+In case the timeout is exceeded during a bind operation, the connection
+is destroyed, according to RFC4511.
+
+Note: in some cases, this backend may issue binds prior
+to other operations (e.g. to bind anonymously or with some prescribed
+identity according to the \fBidassert\-bind\fP directive).
+In this case, the timeout of the operation that resulted in the bind
+is used.
+
+.HP
+.hy 0
+.B tls {none|[try\-]start|[try\-]propagate|ldaps}
+.B [starttls=no]
+.B [tls_cert=<file>]
+.B [tls_key=<file>]
+.B [tls_cacert=<file>]
+.B [tls_cacertdir=<path>]
+.B [tls_reqcert=never|allow|try|demand]
+.B [tls_reqsan=never|allow|try|demand]
+.B [tls_cipher_suite=<ciphers>]
+.B [tls_ecname=<names>]
+.B [tls_crlcheck=none|peer|all]
+.RS
+Specify TLS settings for regular connections.
+
+If the first parameter is not "none" then this configures the TLS
+settings to be used for regular connections.
+The StartTLS extended operation will be used when establishing the
+connection unless the URI directive protocol scheme is \fBldaps://\fP.
+In that case this keyword may only be set to "ldaps" and the StartTLS
+operation will not be used.
+
+With \fBpropagate\fP, the proxy issues the StartTLS operation only if
+the original connection has a TLS layer set up.
+The \fBtry\-\fP prefix instructs the proxy to continue operations
+if the StartTLS operation failed; its use is \fBnot\fP recommended.
+
+The TLS settings default to the same as the main slapd TLS settings,
+except for
+.B tls_reqcert
+which defaults to "demand",
+.B tls_reqsan
+which defaults to "allow", and
+.B starttls
+which is overshadowed by the first keyword and thus ignored.
+.RE
+
+.TP
+.B use\-temporary\-conn {NO|yes}
+when set to
+.BR yes ,
+create a temporary connection whenever competing with other threads
+for a shared one; otherwise, wait until the shared connection is available.
+
+.SH ACCESS CONTROL
+The
+.B ldap
+backend does not honor all ACL semantics as described in
+.BR slapd.access (5).
+In general, access checking is delegated to the remote server(s).
+Only
+.B read (=r)
+access to the
+.B entry
+pseudo-attribute and to the other attribute values of the entries
+returned by the
+.B search
+operation is honored, which is performed by the frontend.
+
+.SH OVERLAYS
+The LDAP backend provides basic proxying functionalities to many overlays.
+The
+.B chain
+overlay, described in
+.BR slapo\-chain (5),
+and the
+.B translucent
+overlay, described in
+.BR slapo\-translucent (5),
+deserve a special mention.
+
+Conversely, there are many overlays that are best used in conjunction
+with the LDAP backend.
+The
+.B proxycache
+overlay allows caching of LDAP search requests (queries)
+in a local database.
+See
+.BR slapo\-pcache (5)
+for details.
+The
+.B rwm
+overlay provides DN rewrite and attribute/objectClass mapping
+capabilities to the underlying database.
+See
+.BR slapo\-rwm (5)
+for details.
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd\-meta (5),
+.BR slapo\-chain (5),
+.BR slapo\-pcache (5),
+.BR slapo\-rwm (5),
+.BR slapo\-translucent (5),
+.BR slapd (8),
+.BR ldap (3).
+.SH AUTHOR
+Howard Chu, with enhancements by Pierangelo Masarati
diff --git a/doc/man/man5/slapd-ldif.5 b/doc/man/man5/slapd-ldif.5
new file mode 100644
index 0000000..3209fc4
--- /dev/null
+++ b/doc/man/man5/slapd-ldif.5
@@ -0,0 +1,54 @@
+.TH SLAPD-LDIF 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd\-ldif \- LDIF backend to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The LDIF backend to
+.BR slapd (8)
+is a basic storage backend that stores entries in text files in LDIF format,
+and exploits the filesystem to create the tree structure of the database.
+It is intended as a cheap, low performance easy to use backend, and it is
+exploited by higher-level internal structures to provide a permanent
+storage.
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the LDIF backend database.
+That is, they must follow a "database ldif" line and come before
+any subsequent "backend" or "database" lines.
+Other database options are described in the
+.BR slapd.conf (5)
+manual page.
+.TP
+.B directory <dir>
+Specify the directory where the database tree starts. The directory
+must exist and grant appropriate permissions (rwx) to the identity slapd
+is running with.
+.SH ACCESS CONTROL
+The
+.B LDIF
+backend does not honor any of the access control semantics described in
+.BR slapd.access (5).
+Only
+.B read (=r)
+access to the
+.B entry
+pseudo-attribute and to the other attribute values of the entries
+returned by the
+.B search
+operation is honored, which is performed by the frontend.
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd (8),
+.BR ldif (5).
+.SH AUTHOR
+Eric Stokes
diff --git a/doc/man/man5/slapd-mdb.5 b/doc/man/man5/slapd-mdb.5
new file mode 100644
index 0000000..a6bb77c
--- /dev/null
+++ b/doc/man/man5/slapd-mdb.5
@@ -0,0 +1,241 @@
+.TH SLAPD-MDB 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2011-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd\-mdb \- Memory-Mapped DB backend to slapd
+.SH SYNOPSIS
+.B ETCDIR/slapd.conf
+.SH DESCRIPTION
+The \fBmdb\fP backend to
+.BR slapd (8)
+uses OpenLDAP's Lightning Memory-Mapped DB (LMDB) library to store data.
+It relies completely on the underlying operating system for memory
+management and does no caching of its own. It is the recommended
+primary database backend.
+.LP
+The \fBmdb\fP backend uses a hierarchical database layout which
+supports subtree renames.
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the \fBmdb\fP backend.
+That is, they must follow a "backend mdb" line and
+come before any subsequent "backend" or "database" lines.
+.TP
+.BI idlexp \ <exp>
+Specify a power of 2 for the maximum size of an index slot.
+The default is 16, yielding a maximum slot size of 2^16 or 65536.
+Once set, this option applies to every \fBmdb\fP database instance.
+The specified value must be in the range of 16-30.
+.LP
+
+These
+.B slapd.conf
+options apply to the \fBmdb\fP backend database.
+That is, they must follow a "database mdb" line and
+come before any subsequent "backend" or "database" lines.
+Other database options are described in the
+.BR slapd.conf (5)
+manual page.
+.TP
+.BI checkpoint \ <kbyte>\ <min>
+Specify the frequency for flushing the database disk buffers.
+This setting is only needed if the \fBdbnosync\fP option is used.
+The checkpoint will occur if either \fI<kbyte>\fP data has been written or
+\fI<min>\fP minutes have passed since the last checkpoint.
+Both arguments default to zero, in which case they are ignored. When
+the \fI<min>\fP argument is non-zero, an internal task will run every
+\fI<min>\fP minutes to perform the checkpoint.
+Note: currently the \fI<kbyte>\fP setting is unimplemented.
+.TP
+.B dbnosync
+Specify that on-disk database contents should not be immediately
+synchronized with in memory changes.
+Enabling this option may improve performance at the expense of data
+security. In particular, if the operating system crashes before changes are
+flushed, some number of transactions may be lost.
+By default, a full data flush/sync is performed when each
+transaction is committed.
+.TP
+.BI directory \ <directory>
+Specify the directory where the LMDB files containing this database and
+associated indexes live.
+A separate directory must be specified for each database.
+The default is
+.BR LOCALSTATEDIR/openldap\-data .
+.TP
+\fBenvflags \fR{\fBnosync\fR,\fBnometasync\fR,\fBwritemap\fR,\fBmapasync\fR,\fBnordahead\fR}
+Specify flags for finer-grained control of the LMDB library's operation.
+.RS
+.TP
+.B nosync
+This is exactly the same as the
+.I dbnosync
+directive.
+.RE
+.RS
+.TP
+.B nometasync
+Flush the data on a commit, but skip the sync of the meta page. This mode is
+slightly faster than doing a full sync, but can potentially lose the last
+committed transaction if the operating system crashes. If both
+.I nometasync
+and
+.I nosync
+are set, the
+.I nosync
+flag takes precedence.
+.RE
+.RS
+.TP
+.B writemap
+Use a writable memory map instead of just read-only. This speeds up write operations
+but makes the database vulnerable to corruption in case any bugs in slapd
+cause stray writes into the mmap region.
+.RE
+.RS
+.TP
+.B mapasync
+When using a writable memory map and performing flushes on each commit, use an
+asynchronous flush instead of a synchronous flush (the default). This option
+has no effect if
+.I writemap
+has not been set. It also has no effect if
+.I nosync
+is set.
+.RE
+.RS
+.TP
+.B nordahead
+Turn off file readahead. Usually the OS performs readahead on every read
+request. This usually boosts read performance but can be harmful to
+random access read performance if the system's memory is full and the DB
+is larger than RAM. This option is not implemented on Windows.
+.RE
+
+.TP
+\fBindex \fR{\fI<attrlist>\fR|\fBdefault\fR} [\fBpres\fR,\fBeq\fR,\fBapprox\fR,\fBsub\fR,\fI<special>\fR]
+Specify the indexes to maintain for the given attribute (or
+list of attributes).
+Some attributes only support a subset of indexes.
+If only an \fI<attr>\fP is given, the indices specified for \fBdefault\fR
+are maintained.
+Note that setting a default does not imply that all attributes will be
+indexed. Also, for best performance, an
+.B eq
+index should always be configured for the
+.B objectClass
+attribute.
+
+A number of special index parameters may be specified.
+The index type
+.B sub
+can be decomposed into
+.BR subinitial ,
+.BR subany ,\ and
+.B subfinal
+indices.
+The special type
+.B nolang
+may be specified to disallow use of this index by language subtypes.
+The special type
+.B nosubtypes
+may be specified to disallow use of this index by named subtypes.
+Note: changing \fBindex\fP settings in
+.BR slapd.conf (5)
+requires rebuilding indices, see
+.BR slapindex (8);
+changing \fBindex\fP settings
+dynamically by LDAPModifying "cn=config" automatically causes rebuilding
+of the indices online in a background task.
+.TP
+.BI maxentrysize \ <bytes>
+Specify the maximum size of an entry in bytes. Attempts to store
+an entry larger than this size will be rejected with the error
+LDAP_ADMINLIMIT_EXCEEDED. The default is 0, which is unlimited.
+.TP
+.BI maxreaders \ <integer>
+Specify the maximum number of threads that may have concurrent read access
+to the database. Tools such as slapcat count as a single thread,
+in addition to threads in any active slapd processes. The
+default is 126.
+.TP
+.BI maxsize \ <bytes>
+Specify the maximum size of the database in bytes. A memory map of this
+size is allocated at startup time and the database will not be allowed
+to grow beyond this size. The default is 10485760 bytes. This setting
+may be changed upward if the configured limit needs to be increased.
+
+Note: It is important to set this to as large a value as possible,
+(relative to anticipated growth of the actual data over time) since
+growing the size later may not be practical when the system is under
+heavy load.
+.TP
+.BI mode \ <integer>
+Specify the file protection mode that newly created database
+files should have.
+The default is 0600.
+.TP
+\fBmultival \fR{\fI<attrlist>\fR|\fBdefault\fR} \fI<integer hi>\fR,\fI<integer lo>
+Specify the number of values for which a multivalued attribute is
+stored in a separate table. Normally entries are stored as a single
+blob inside the database. When an entry gets very large or contains
+attributes with a very large number of values, modifications on that
+entry may get very slow. Splitting the large attributes out to a separate
+table can improve the performance of modification operations.
+The threshold is specified as a pair of integers. If the number of
+values exceeds the hi threshold the values will be split out. If
+a modification deletes enough values to bring an attribute below
+the lo threshold the values will be removed from the separate
+table and merged back into the main entry blob.
+The threshold can be set for a specific list of attributes, or
+the default can be configured for all other attributes.
+The default value for both hi and lo thresholds is UINT_MAX, which keeps
+all attributes in the main blob.
+.TP
+.BI rtxnsize \ <entries>
+Specify the maximum number of entries to process in a single read
+transaction when executing a large search. Long-lived read transactions
+prevent old database pages from being reused in write transactions, and
+so can cause significant growth of the database file when there is
+heavy write traffic. This setting causes the read transaction in
+large searches to be released and reacquired after the given number
+of entries has been read, to give writers the opportunity to
+reclaim old database pages. The default is 10000.
+.TP
+.BI searchstack \ <depth>
+Specify the depth of the stack used for search filter evaluation.
+Search filters are evaluated on a stack to accommodate nested AND / OR
+clauses. An individual stack is assigned to each server thread.
+The depth of the stack determines how complex a filter can be
+evaluated without requiring any additional memory allocation. Filters that
+are nested deeper than the search stack depth will cause a separate
+stack to be allocated for that particular search operation. These
+allocations can have a major negative impact on server performance,
+but specifying too much stack will also consume a great deal of memory.
+Each search stack uses 512K bytes per level. The default stack depth
+is 16, thus 8MB per thread is used.
+.SH ACCESS CONTROL
+The
+.B mdb
+backend honors access control semantics as indicated in
+.BR slapd.access (5).
+.SH FILES
+.TP
+.B ETCDIR/slapd.conf
+default
+.B slapd
+configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd (8),
+.BR slapadd (8),
+.BR slapcat (8),
+.BR slapindex (8),
+.BR slapmodify (8),
+OpenLDAP LMDB documentation.
+.SH ACKNOWLEDGEMENTS
+.so ../Project
+Written by Howard Chu.
diff --git a/doc/man/man5/slapd-meta.5 b/doc/man/man5/slapd-meta.5
new file mode 100644
index 0000000..2134ff6
--- /dev/null
+++ b/doc/man/man5/slapd-meta.5
@@ -0,0 +1,1378 @@
+.TH SLAPD-META 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation, All Rights Reserved.
+.\" Copying restrictions apply. See the COPYRIGHT file.
+.\" Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+.\" $OpenLDAP$
+.\"
+.\" Portions of this document should probably be moved to slapd-ldap(5)
+.\" and maybe manual pages for librewrite.
+.\"
+.SH NAME
+slapd\-meta \- metadirectory backend to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B meta
+backend to
+.BR slapd (8)
+performs basic LDAP proxying with respect to a set of remote LDAP
+servers, called "targets".
+The information contained in these servers can be presented as
+belonging to a single Directory Information Tree (DIT).
+.LP
+A basic knowledge of the functionality of the
+.BR slapd\-ldap (5)
+backend is recommended.
+This backend has been designed as an enhancement of the ldap backend.
+The two backends share many features (actually they also share
+portions of code).
+While the
+.B ldap
+backend is intended to proxy operations directed to a single server, the
+.B meta
+backend is mainly intended for proxying of multiple servers and possibly
+naming context masquerading.
+These features, although useful in many scenarios, may result in
+excessive overhead for some applications, so its use should be
+carefully considered.
+In the examples section, some typical scenarios will be discussed.
+
+The proxy instance of
+.BR slapd (8)
+must contain schema information for the attributes and objectClasses
+used in filters, request DN and request-related data in general.
+It should also contain schema information for the data returned
+by the proxied server.
+It is the responsibility of the proxy administrator to keep the schema
+of the proxy lined up with that of the proxied server.
+
+.LP
+Note: When looping back to the same instance of \fBslapd\fP(8),
+each connection requires a new thread; as a consequence, the \fBslapd\fP(8)
+\fBthreads\fP parameter may need some tuning. In those cases, unless the
+multiple target feature is required, one may consider using \fBslapd\-relay\fP(5) instead,
+which performs the relayed operation internally and thus reuses
+the same connection.
+
+.SH EXAMPLES
+There are examples in various places in this document, as well as in the
+slapd/back-meta/data/ directory in the OpenLDAP source tree.
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the META backend database.
+That is, they must follow a "database meta" line and come before any
+subsequent "backend" or "database" lines.
+Other database options are described in the
+.BR slapd.conf (5)
+manual page.
+.LP
+Note: In early versions of back-ldap and back-meta it was recommended to always set
+.LP
+.RS
+.nf
+lastmod off
+.fi
+.RE
+.LP
+for
+.B ldap
+and
+.B meta
+databases.
+This was required because operational attributes related to entry creation
+and modification should not be proxied, as they could be mistakenly written
+to the target server(s), generating an error.
+The current implementation automatically sets lastmod to \fBoff\fP,
+so its use is redundant and should be omitted.
+
+.SH SPECIAL CONFIGURATION DIRECTIVES
+Target configuration starts with the "uri" directive.
+All the configuration directives that are not specific to targets
+should be defined first for clarity, including those that are common
+to all backends.
+They are:
+
+.TP
+.B conn\-pool\-max <int>
+This directive defines the maximum size of the privileged connections pool.
+
+.TP
+.B conn\-ttl <time>
+This directive causes a cached connection to be dropped an recreated
+after a given ttl, regardless of being idle or not.
+
+.TP
+.B default\-target none
+This directive forces the backend to reject all those operations
+that must resolve to a single target in case none or multiple
+targets are selected.
+They include: add, delete, modify, modrdn; compare is not included, as
+well as bind since, as they don't alter entries, in case of multiple
+matches an attempt is made to perform the operation on any candidate
+target, with the constraint that at most one must succeed.
+This directive can also be used when processing targets to mark a
+specific target as default.
+
+.TP
+.B dncache\-ttl {DISABLED|forever|<ttl>}
+This directive sets the time-to-live of the DN cache.
+This caches the target that holds a given DN to speed up target
+selection in case multiple targets would result from an uncached
+search; forever means cache never expires; disabled means no DN
+caching; otherwise a valid ( > 0 ) ttl is required, in the format
+illustrated for the
+.B idle\-timeout
+directive.
+
+.TP
+.B onerr {CONTINUE|report|stop}
+This directive allows one to select the behavior in case an error is returned
+by one target during a search.
+The default, \fBcontinue\fP, consists in continuing the operation,
+trying to return as much data as possible.
+If the value is set to \fBstop\fP, the search is terminated as soon
+as an error is returned by one target, and the error is immediately
+propagated to the client.
+If the value is set to \fBreport\fP, the search is continued to the end
+but, in case at least one target returned an error code, the first
+non-success error code is returned.
+
+.TP
+.B norefs <NO|yes>
+If
+.BR yes ,
+do not return search reference responses.
+By default, they are returned unless request is LDAPv2.
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive.
+
+.TP
+.B noundeffilter <NO|yes>
+If
+.BR yes ,
+return success instead of searching if a filter is undefined or contains
+undefined portions.
+By default, the search is propagated after replacing undefined portions
+with
+.BR (!(objectClass=*)) ,
+which corresponds to the empty result set.
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive.
+
+.TP
+.B protocol\-version {0,2,3}
+This directive indicates what protocol version must be used to contact
+the remote server.
+If set to 0 (the default), the proxy uses the same protocol version
+used by the client, otherwise the requested protocol is used.
+The proxy returns \fIunwillingToPerform\fP if an operation that is
+incompatible with the requested protocol is attempted.
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive.
+
+.TP
+.B pseudoroot\-bind\-defer {YES|no}
+This directive, when set to
+.BR yes ,
+causes the authentication to the remote servers with the pseudo-root
+identity (the identity defined in each
+.B idassert\-bind
+directive) to be deferred until actually needed by subsequent operations.
+Otherwise, all binds as the rootdn are propagated to the targets.
+
+.TP
+.B quarantine <interval>,<num>[;<interval>,<num>[...]]
+Turns on quarantine of URIs that returned
+.IR LDAP_UNAVAILABLE ,
+so that an attempt to reconnect only occurs at given intervals instead
+of any time a client requests an operation.
+The pattern is: retry only after at least
+.I interval
+seconds elapsed since last attempt, for exactly
+.I num
+times; then use the next pattern.
+If
+.I num
+for the last pattern is "\fB+\fP", it retries forever; otherwise,
+no more retries occur.
+This directive must appear before any target specification;
+it affects all targets with the same pattern.
+
+.TP
+.B rebind\-as\-user {NO|yes}
+If this option is given, the client's bind credentials are remembered
+for rebinds, when trying to re-establish a broken connection,
+or when chasing a referral, if
+.B chase\-referrals
+is set to
+.IR yes .
+
+.TP
+.B session\-tracking\-request {NO|yes}
+Adds session tracking control for all requests.
+The client's IP and hostname, and the identity associated to each request,
+if known, are sent to the remote server for informational purposes.
+This directive is incompatible with setting \fIprotocol\-version\fP to 2.
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive.
+
+.TP
+.B single\-conn {NO|yes}
+Discards current cached connection when the client rebinds.
+
+.TP
+.B use\-temporary\-conn {NO|yes}
+when set to
+.BR yes ,
+create a temporary connection whenever competing with other threads
+for a shared one; otherwise, wait until the shared connection is available.
+
+.SH TARGET SPECIFICATION
+Target specification starts with a "uri" directive:
+
+.TP
+.B uri <protocol>://[<host>]/<naming context> [...]
+The <protocol> part can be anything
+.BR ldap_initialize (3)
+accepts ({ldap|ldaps|ldapi} and variants); the <host> may be
+omitted, defaulting to whatever is set in
+.BR ldap.conf (5).
+The <naming context> part is \fImandatory\fP for the first URI,
+but it \fImust be omitted\fP for subsequent ones, if any.
+The naming context part must be within the naming context defined for the backend,
+e.g.:
+.LP
+.RS
+.nf
+suffix "\fBdc=foo,dc=com\fP"
+uri "ldap://x.foo.com/dc=x,\fBdc=foo,dc=com\fP"
+.fi
+
+.RE
+.RS
+The <naming context> part doesn't need to be unique across the targets;
+it may also match one of the values of the "suffix" directive.
+Multiple URIs may be defined in a single URI statement.
+The additional URIs must be separate arguments and must not have any
+<naming context> part. This causes the underlying library
+to contact the first server of the list that responds.
+For example, if \fIl1.foo.com\fP and \fIl2.foo.com\fP are shadows
+of the same server, the directive
+.LP
+.nf
+suffix "\fBdc=foo,dc=com\fP"
+uri "ldap://l1.foo.com/\fBdc=foo,dc=com\fP" "ldap://l2.foo.com/"
+.fi
+
+.RE
+.RS
+causes \fIl2.foo.com\fP to be contacted whenever \fIl1.foo.com\fP
+does not respond.
+In that case, the URI list is internally rearranged, by moving unavailable
+URIs to the end, so that further connection attempts occur with respect to
+the last URI that succeeded.
+.RE
+
+.TP
+.B acl\-authcDN "<administrative DN for access control purposes>"
+DN which is used to query the target server for acl checking,
+as in the LDAP backend; it is supposed to have read access
+on the target server to attributes used on the proxy for acl checking.
+There is no risk of giving away such values; they are only used to
+check permissions.
+.B The acl\-authcDN identity is by no means implicitly used by the proxy
+.B when the client connects anonymously.
+
+.TP
+.B acl\-passwd <password>
+Password used with the
+.B acl\-authcDN
+above.
+
+.TP
+.B bind\-timeout <microseconds>
+This directive defines the timeout, in microseconds, used when polling
+for response after an asynchronous bind connection. The initial call
+to ldap_result(3) is performed with a trade-off timeout of 100000 us;
+if that results in a timeout exceeded, subsequent calls use the value
+provided with
+.BR bind\-timeout .
+The default value is used also for subsequent calls if
+.B bind\-timeout
+is not specified.
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive.
+
+.TP
+.B chase\-referrals {YES|no}
+enable/disable automatic referral chasing, which is delegated to the
+underlying libldap, with rebinding eventually performed if the
+\fBrebind\-as\-user\fP directive is used. The default is to chase referrals.
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive.
+
+.TP
+.B client\-pr {accept-unsolicited|DISABLE|<size>}
+This feature allows one to use RFC 2696 Paged Results control when performing
+search operations with a specific target,
+irrespective of the client's request.
+When set to a numeric value, Paged Results control is always
+used with \fIsize\fP as the page size.
+When set to \fIaccept\-unsolicited\fP, unsolicited Paged Results
+control responses are accepted and honored
+for compatibility with broken remote DSAs.
+The client is not exposed to paged results handling
+between
+.BR slapd\-meta (5)
+and the remote servers.
+By default (disabled), Paged Results control is not used
+and responses are not accepted.
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive.
+
+.TP
+.B default\-target [<target>]
+The "default\-target" directive can also be used during target specification.
+With no arguments it marks the current target as the default.
+The optional number marks target <target> as the default one, starting
+from 1.
+Target <target> must be defined.
+
+.TP
+.B filter <pattern>
+This directive allows specifying a
+.BR regex (5)
+pattern to indicate what search filter terms are actually served by a target.
+
+In a search request, if the search filter matches the \fIpattern\fP
+the target is considered while fulfilling the request; otherwise
+the target is ignored. There may be multiple occurrences of
+the
+.B filter
+directive for each target.
+
+.TP
+.B idassert\-authzFrom <authz-regexp>
+if defined, selects what
+.I local
+identities are authorized to exploit the identity assertion feature.
+The string
+.B <authz\-regexp>
+follows the rules defined for the
+.I authzFrom
+attribute.
+See
+.BR slapd.conf (5),
+section related to
+.BR authz\-policy ,
+for details on the syntax of this field.
+
+.HP
+.hy 0
+.B idassert\-bind
+.B bindmethod=none|simple|sasl [binddn=<simple DN>] [credentials=<simple password>]
+.B [saslmech=<SASL mech>] [secprops=<properties>] [realm=<realm>]
+.B [authcId=<authentication ID>] [authzId=<authorization ID>]
+.B [authz={native|proxyauthz}] [mode=<mode>] [flags=<flags>]
+.B [starttls=no|yes|critical]
+.B [tls_cert=<file>]
+.B [tls_key=<file>]
+.B [tls_cacert=<file>]
+.B [tls_cacertdir=<path>]
+.B [tls_reqcert=never|allow|try|demand]
+.B [tls_reqsan=never|allow|try|demand]
+.B [tls_cipher_suite=<ciphers>]
+.B [tls_ecname=<ciphers>]
+.B [tls_protocol_min=<major>[.<minor>]]
+.B [tls_crlcheck=none|peer|all]
+.RS
+Allows one to define the parameters of the authentication method that is
+internally used by the proxy to authorize connections that are
+authenticated by other databases.
+The identity defined by this directive, according to the properties
+associated to the authentication method, is supposed to have auth access
+on the target server to attributes used on the proxy for authentication
+and authorization, and to be allowed to authorize the users.
+This requires to have
+.B proxyAuthz
+privileges on a wide set of DNs, e.g.
+.BR authzTo=dn.subtree:"" ,
+and the remote server to have
+.B authz\-policy
+set to
+.B to
+or
+.BR both .
+See
+.BR slapd.conf (5)
+for details on these statements and for remarks and drawbacks about
+their usage.
+The supported bindmethods are
+
+\fBnone|simple|sasl\fP
+
+where
+.B none
+is the default, i.e. no \fIidentity assertion\fP is performed.
+
+The
+.B authz
+parameter is used to instruct the SASL bind to exploit
+.B native
+SASL authorization, if available; since connections are cached,
+this should only be used when authorizing with a fixed identity
+(e.g. by means of the
+.B authzDN
+or
+.B authzID
+parameters).
+Otherwise, the default
+.B proxyauthz
+is used, i.e. the proxyAuthz control (Proxied Authorization, RFC 4370)
+is added to all operations.
+
+The supported modes are:
+
+\fB<mode> := {legacy|anonymous|none|self}\fP
+
+If
+.B <mode>
+is not present, and
+.B authzId
+is given, the proxy always authorizes that identity.
+.B <authorization ID>
+can be
+
+\fBu:<user>\fP
+
+\fB[dn:]<DN>\fP
+
+The former is supposed to be expanded by the remote server according
+to the authz rules; see
+.BR slapd.conf (5)
+for details.
+In the latter case, whether or not the
+.B dn:
+prefix is present, the string must pass DN validation and normalization.
+
+The default mode is
+.BR legacy ,
+which implies that the proxy will either perform a simple bind as the
+.I authcDN
+or a SASL bind as the
+.I authcID
+and assert the client's identity when it is not anonymous.
+Direct binds are always proxied.
+The other modes imply that the proxy will always either perform a simple bind
+as the
+.IR authcDN
+or a SASL bind as the
+.IR authcID ,
+unless restricted by
+.BR idassert\-authzFrom
+rules (see below), in which case the operation will fail;
+eventually, it will assert some other identity according to
+.BR <mode> .
+Other identity assertion modes are
+.BR anonymous
+and
+.BR self ,
+which respectively mean that the
+.I empty
+or the
+.IR client 's
+identity
+will be asserted;
+.BR none ,
+which means that no proxyAuthz control will be used, so the
+.I authcDN
+or the
+.I authcID
+identity will be asserted.
+For all modes that require the use of the
+.I proxyAuthz
+control, on the remote server the proxy identity must have appropriate
+.I authzTo
+permissions, or the asserted identities must have appropriate
+.I authzFrom
+permissions. Note, however, that the ID assertion feature is mostly
+useful when the asserted identities do not exist on the remote server.
+When
+.I bindmethod
+is
+.BR SASL ,
+the
+.I authcDN
+must be specified in addition to the
+.IR authcID ,
+although it is not used within the authentication process.
+
+Flags can be
+
+\fBoverride,[non\-]prescriptive,proxy\-authz\-[non\-]critical\fP
+
+When the
+.B override
+flag is used, identity assertion takes place even when the database
+is authorizing for the identity of the client, i.e. after binding
+with the provided identity, and thus authenticating it, the proxy
+performs the identity assertion using the configured identity and
+authentication method.
+
+When the
+.B prescriptive
+flag is used (the default), operations fail with
+\fIinappropriateAuthentication\fP
+for those identities whose assertion is not allowed by the
+.B idassert\-authzFrom
+patterns.
+If the
+.B non\-prescriptive
+flag is used, operations are performed anonymously for those identities
+whose assertion is not allowed by the
+.B idassert\-authzFrom
+patterns.
+
+When the
+.B proxy\-authz\-non\-critical
+flag is used (the default), the proxyAuthz control is not marked as critical,
+in violation of RFC 4370. Use of
+.B proxy\-authz\-critical
+is recommended.
+
+The TLS settings default to the same as the main slapd TLS settings,
+except for
+.B tls_reqcert
+which defaults to "demand", and
+.B tls_reqsan
+which defaults to "allow"..
+
+The identity associated to this directive is also used for privileged
+operations whenever \fBidassert\-bind\fP is defined and \fBacl\-bind\fP
+is not. See \fBacl\-bind\fP for details.
+.RE
+
+.TP
+.B idle\-timeout <time>
+This directive causes a cached connection to be dropped an recreated
+after it has been idle for the specified time.
+The value can be specified as
+
+[<d>d][<h>h][<m>m][<s>[s]]
+
+where <d>, <h>, <m> and <s> are respectively treated as days, hours,
+minutes and seconds.
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive.
+
+.TP
+.B keepalive <idle>:<probes>:<interval>
+The
+.B keepalive
+parameter sets the values of \fIidle\fP, \fIprobes\fP, and \fIinterval\fP
+used to check whether a socket is alive;
+.I idle
+is the number of seconds a connection needs to remain idle before TCP
+starts sending keepalive probes;
+.I probes
+is the maximum number of keepalive probes TCP should send before dropping
+the connection;
+.I interval
+is interval in seconds between individual keepalive probes.
+Only some systems support the customization of these values;
+the
+.B keepalive
+parameter is ignored otherwise, and system-wide settings are used.
+
+.TP
+.B tcp\-user\-timeout <milliseconds>
+If non-zero, corresponds to the
+.B TCP_USER_TIMEOUT
+set on the target connections, overriding the operating system setting.
+Only some systems support the customization of this parameter, it is
+ignored otherwise and system-wide settings are used.
+
+.TP
+.B map "{attribute|objectclass} [<local name>|*] {<foreign name>|*}"
+This maps object classes and attributes as in the LDAP backend.
+See
+.BR slapd\-ldap (5).
+
+.TP
+.B network\-timeout <time>
+Sets the network timeout value after which
+.BR poll (2)/ select (2)
+following a
+.BR connect (2)
+returns in case of no activity.
+The value is in seconds, and it can be specified as for
+.BR idle\-timeout .
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive.
+
+.TP
+.B nretries {forever|never|<nretries>}
+This directive defines how many times a bind should be retried
+in case of temporary failure in contacting a target. If defined
+before any target specification, it applies to all targets (by default,
+.BR 3
+times);
+the global value can be overridden by redefinitions inside each target
+specification.
+
+.TP
+.B rewrite* ...
+The rewrite options are described in the "REWRITING" section.
+
+.TP
+.B subtree\-{exclude|include} "<rule>"
+This directive allows one to indicate what subtrees are actually served
+by a target.
+The syntax of the supported rules is
+
+\fB<rule>: [dn[.<style>]:]<pattern>\fP
+
+\fB<style>: subtree|children|regex\fP
+
+When \fB<style>\fP is either \fBsubtree\fP or \fBchildren\fP
+the \fB<pattern>\fP is a DN that must be within the naming context
+served by the target.
+When \fB<style>\fP is \fBregex\fP the \fB<pattern>\fP is a
+.BR regex (5)
+pattern.
+If the \fBdn.<style>:\fP prefix is omitted, \fBdn.subtree:\fP
+is implicitly assumed for backward compatibility.
+
+In the
+.B subtree\-exclude
+form if the \fIrequest DN\fP matches at least one rule,
+the target is not considered while fulfilling the request;
+otherwise, the target is considered based on the value of the \fIrequest DN\fP.
+When the request is a search, also the \fIscope\fP is considered.
+
+In the
+.B subtree\-include
+form if the \fIrequest DN\fP matches at least one rule,
+the target is considered while fulfilling the request;
+otherwise the target is ignored.
+
+.LP
+.RS
+.nf
+ | match | exclude |
+ +---------+---------+-------------------+
+ | T | T | not candidate |
+ | F | T | continue checking |
+ +---------+---------+-------------------+
+ | T | F | candidate |
+ | F | F | not candidate |
+ +---------+---------+-------------------+
+.fi
+
+.RE
+.RS
+There may be multiple occurrences of the
+.B subtree\-exclude
+or
+.B subtree\-include
+directive for each of the targets, but they are mutually exclusive.
+.RE
+
+.TP
+.B suffixmassage "<virtual naming context>" "<real naming context>"
+All the directives starting with "rewrite" refer to the rewrite engine
+that has been added to slapd.
+The "suffixmassage" directive was introduced in the LDAP backend to
+allow suffix massaging while proxying.
+It has been obsoleted by the rewriting tools.
+However, both for backward compatibility and for ease of configuration
+when simple suffix massage is required, it has been preserved.
+It wraps the basic rewriting instructions that perform suffix
+massaging. See the "REWRITING" section for a detailed list
+of the rewrite rules it implies.
+
+.TP
+.B t\-f\-support {NO|yes|discover}
+enable if the remote server supports absolute filters
+(see \fIRFC 4526\fP for details).
+If set to
+.BR discover ,
+support is detected by reading the remote server's root DSE.
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive.
+
+.TP
+.B timeout [<op>=]<val> [...]
+This directive allows one to set per-operation timeouts.
+Operations can be
+
+\fB<op> ::= bind, add, delete, modrdn, modify, compare, search\fP
+
+The overall duration of the \fBsearch\fP operation is controlled either
+by the \fBtimelimit\fP parameter or by server-side enforced
+time limits (see \fBtimelimit\fP and \fBlimits\fP in
+.BR slapd.conf (5)
+for details).
+This \fBtimeout\fP parameter controls how long the target can be
+irresponsive before the operation is aborted.
+Timeout is meaningless for the remaining operations,
+\fBunbind\fP and \fBabandon\fP, which do not imply any response,
+while it is not yet implemented in currently supported \fBextended\fP
+operations.
+If no operation is specified, the timeout \fBval\fP affects all
+supported operations.
+If specified before any target definition, it affects all targets
+unless overridden by per-target directives.
+
+Note: if the timeout is exceeded, the operation is cancelled
+(according to the \fBcancel\fP directive);
+the protocol does not provide any means to rollback operations,
+so the client will not be notified about the result of the operation,
+which may eventually succeeded or not.
+In case the timeout is exceeded during a bind operation, the connection
+is destroyed, according to RFC4511.
+
+.TP
+.B tls {none|[try\-]start|[try\-]propagate|ldaps}
+.B [starttls=no]
+.B [tls_cert=<file>]
+.B [tls_key=<file>]
+.B [tls_cacert=<file>]
+.B [tls_cacertdir=<path>]
+.B [tls_reqcert=never|allow|try|demand]
+.B [tls_reqsan=never|allow|try|demand]
+.B [tls_cipher_suite=<ciphers>]
+.B [tls_ecname=<names>]
+.B [tls_crlcheck=none|peer|all]
+.RS
+Specify TLS settings regular connections.
+
+If the first parameter is not "none" then this configures the TLS
+settings to be used for regular connections.
+The StartTLS extended operation will be used when establishing the
+connection unless the URI directive protocol scheme is \fBldaps://\fP.
+In that case this keyword may only be set to "ldaps" and the StartTLS
+operation will not be used.
+
+With \fBpropagate\fP, the proxy issues the StartTLS operation only if
+the original connection has a TLS layer set up.
+The \fBtry\-\fP prefix instructs the proxy to continue operations
+if the StartTLS operation failed; its use is \fBnot\fP recommended.
+
+The TLS settings default to the same as the main slapd TLS settings,
+except for
+.B tls_reqcert
+which defaults to "demand",
+.B tls_reqsan
+which defaults to "allow", and
+.B starttls
+which is overshadowed by the first keyword and thus ignored.
+
+If set before any target specification, it affects all targets, unless
+overridden by any per-target directive.
+.RE
+
+.SH SCENARIOS
+A powerful (and in some sense dangerous) rewrite engine has been added
+to both the LDAP and Meta backends.
+While the former can gain limited beneficial effects from rewriting
+stuff, the latter can become an amazingly powerful tool.
+.LP
+Consider a couple of scenarios first.
+.LP
+1) Two directory servers share two levels of naming context;
+say "dc=a,dc=foo,dc=com" and "dc=b,dc=foo,dc=com".
+Then, an unambiguous Meta database can be configured as:
+.LP
+.RS
+.nf
+database meta
+suffix "\fBdc=foo,dc=com\fP"
+uri "ldap://a.foo.com/dc=a,\fBdc=foo,dc=com\fP"
+uri "ldap://b.foo.com/dc=b,\fBdc=foo,dc=com\fP"
+.fi
+.RE
+.LP
+Operations directed to a specific target can be easily resolved
+because there are no ambiguities.
+The only operation that may resolve to multiple targets is a search
+with base "dc=foo,dc=com" and scope at least "one", which results in
+spawning two searches to the targets.
+.LP
+2a) Two directory servers don't share any portion of naming context,
+but they'd present as a single DIT
+[Caveat: uniqueness of (massaged) entries among the two servers is
+assumed; integrity checks risk to incur in excessive overhead and have
+not been implemented].
+Say we have "dc=bar,dc=org" and "o=Foo,c=US",
+and we'd like them to appear as branches of "dc=foo,dc=com", say
+"dc=a,dc=foo,dc=com" and "dc=b,dc=foo,dc=com".
+Then we need to configure our Meta backend as:
+.LP
+.RS
+.nf
+database meta
+suffix "dc=foo,dc=com"
+
+uri "ldap://a.bar.com/\fBdc=a,dc=foo,dc=com\fP"
+suffixmassage "\fBdc=a,dc=foo,dc=com\fP" "dc=bar,dc=org"
+
+uri "ldap://b.foo.com/\fBdc=b,dc=foo,dc=com\fP"
+suffixmassage "\fBdc=b,dc=foo,dc=com\fP" "o=Foo,c=US"
+.fi
+.RE
+.LP
+Again, operations can be resolved without ambiguity, although
+some rewriting is required.
+Notice that the virtual naming context of each target is a branch of
+the database's naming context; it is rewritten back and forth when
+operations are performed towards the target servers.
+What "back and forth" means will be clarified later.
+.LP
+When a search with base "dc=foo,dc=com" is attempted, if the
+scope is "base" it fails with "no such object"; in fact, the
+common root of the two targets (prior to massaging) does not
+exist.
+If the scope is "one", both targets are contacted with the base
+replaced by each target's base; the scope is derated to "base".
+In general, a scope "one" search is honored, and the scope is derated,
+only when the incoming base is at most one level lower of a target's
+naming context (prior to massaging).
+.LP
+Finally, if the scope is "sub" the incoming base is replaced
+by each target's unmassaged naming context, and the scope
+is not altered.
+.LP
+2b) Consider the above reported scenario with the two servers
+sharing the same naming context:
+.LP
+.RS
+.nf
+database meta
+suffix "\fBdc=foo,dc=com\fP"
+
+uri "ldap://a.bar.com/\fBdc=foo,dc=com\fP"
+suffixmassage "\fBdc=foo,dc=com\fP" "dc=bar,dc=org"
+
+uri "ldap://b.foo.com/\fBdc=foo,dc=com\fP"
+suffixmassage "\fBdc=foo,dc=com\fP" "o=Foo,c=US"
+.fi
+.RE
+.LP
+All the previous considerations hold, except that now there is
+no way to unambiguously resolve a DN.
+In this case, all the operations that require an unambiguous target
+selection will fail unless the DN is already cached or a default
+target has been set.
+Practical configurations may result as a combination of all the
+above scenarios.
+.SH ACLs
+Note on ACLs: at present you may add whatever ACL rule you desire
+to the Meta (and LDAP) backends.
+However, the meaning of an ACL on a proxy may require some
+considerations.
+Two philosophies may be considered:
+.LP
+a) the remote server dictates the permissions; the proxy simply passes
+back what it gets from the remote server.
+.LP
+b) the remote server unveils "everything"; the proxy is responsible
+for protecting data from unauthorized access.
+.LP
+Of course the latter sounds unreasonable, but it is not.
+It is possible to imagine scenarios in which a remote host discloses
+data that can be considered "public" inside an intranet, and a proxy
+that connects it to the internet may impose additional constraints.
+To this purpose, the proxy should be able to comply with all the ACL
+matching criteria that the server supports.
+This has been achieved with regard to all the criteria supported by
+slapd except a special subtle case (please file an ITS if you can
+find other exceptions: <http://www.openldap.org/its/>).
+The rule
+.LP
+.RS
+.nf
+access to dn="<dn>" attrs=<attr>
+ by dnattr=<dnattr> read
+ by * none
+.fi
+.RE
+.LP
+cannot be matched iff the attribute that is being requested, <attr>,
+is NOT <dnattr>, and the attribute that determines membership,
+<dnattr>, has not been requested (e.g. in a search)
+.LP
+In fact this ACL is resolved by slapd using the portion of entry it
+retrieved from the remote server without requiring any further
+intervention of the backend, so, if the <dnattr> attribute has not
+been fetched, the match cannot be assessed because the attribute is
+not present, not because no value matches the requirement!
+.LP
+Note on ACLs and attribute mapping: ACLs are applied to the mapped
+attributes; for instance, if the attribute locally known as "foo" is
+mapped to "bar" on a remote server, then local ACLs apply to attribute
+"foo" and are totally unaware of its remote name.
+The remote server will check permissions for "bar", and the local
+server will possibly enforce additional restrictions to "foo".
+.\"
+.\" If this section is moved, also update the reference in
+.\" libraries/librewrite/RATIONALE.
+.\"
+.SH REWRITING
+A string is rewritten according to a set of rules, called a `rewrite
+context'.
+The rules are based on POSIX (''extended'') regular expressions (regex)
+with substring matching; basic variable substitution and map resolution
+of substrings is allowed by specific mechanisms detailed in the following.
+The behavior of pattern matching/substitution can be altered by a set
+of flags.
+.LP
+The underlying concept is to build a lightweight rewrite module
+for the slapd server (initially dedicated to the LDAP backend).
+.SH Passes
+An incoming string is matched against a set of rules.
+Rules are made of a regex match pattern, a substitution pattern
+and a set of actions, described by a set of flags.
+In case of match a string rewriting is performed according to the
+substitution pattern that allows one to refer to substrings matched in the
+incoming string.
+The actions, if any, are finally performed.
+The substitution pattern allows map resolution of substrings.
+A map is a generic object that maps a substitution pattern to a value.
+The flags are divided in "Pattern matching Flags" and "Action Flags";
+the former alter the regex match pattern behavior while the latter
+alter the action that is taken after substitution.
+.SH "Pattern Matching Flags"
+.TP
+.B `C'
+honors case in matching (default is case insensitive)
+.TP
+.B `R'
+use POSIX ''basic'' regular expressions (default is ''extended'')
+.TP
+.B `M{n}'
+allow no more than
+.B n
+recursive passes for a specific rule; does not alter the max total count
+of passes, so it can only enforce a stricter limit for a specific rule.
+.SH "Action Flags"
+.TP
+.B `:'
+apply the rule once only (default is recursive)
+.TP
+.B `@'
+stop applying rules in case of match; the current rule is still applied
+recursively; combine with `:' to apply the current rule only once
+and then stop.
+.TP
+.B `#'
+stop current operation if the rule matches, and issue an `unwilling to
+perform' error.
+.TP
+.B `G{n}'
+jump
+.B n
+rules back and forth (watch for loops!).
+Note that `G{1}' is implicit in every rule.
+.TP
+.B `I'
+ignores errors in rule; this means, in case of error, e.g. issued by a
+map, the error is treated as a missed match.
+The `unwilling to perform' is not overridden.
+.TP
+.B `U{n}'
+uses
+.B
+n
+as return code if the rule matches; the flag does not alter the recursive
+behavior of the rule, so, to have it performed only once, it must be used
+in combination with `:', e.g.
+.B `:U{16}'
+returns the value `16' after exactly one execution of the rule, if the
+pattern matches.
+As a consequence, its behavior is equivalent to `@', with the return
+code set to
+.BR n ;
+or, in other words, `@' is equivalent to `U{0}'.
+By convention, the freely available codes are above 16 included;
+the others are reserved.
+.LP
+The ordering of the flags can be significant.
+For instance: `IG{2}' means ignore errors and jump two lines ahead
+both in case of match and in case of error, while `G{2}I' means ignore
+errors, but jump two lines ahead only in case of match.
+.LP
+More flags (mainly Action Flags) will be added as needed.
+.SH "Pattern matching:"
+See
+.BR regex (7)
+and/or
+.BR re_format (7).
+.SH "Substitution Pattern Syntax:"
+Everything starting with `%' requires substitution;
+.LP
+the only obvious exception is `%%', which is left as is;
+.LP
+the basic substitution is `%d', where `d' is a digit;
+0 means the whole string, while 1-9 is a submatch;
+.LP
+a `%' followed by a `{' invokes an advanced substitution.
+The pattern is:
+.LP
+.RS
+`%' `{' [ <op> ] <name> `(' <substitution> `)' `}'
+.RE
+.LP
+where <name> must be a legal name for the map, i.e.
+.LP
+.RS
+.nf
+<name> ::= [a-z][a-z0-9]* (case insensitive)
+<op> ::= `>' `|' `&' `&&' `*' `**' `$'
+.fi
+.RE
+.LP
+and <substitution> must be a legal substitution
+pattern, with no limits on the nesting level.
+.LP
+The operators are:
+.TP
+.B >
+sub context invocation; <name> must be a legal, already defined
+rewrite context name
+.TP
+.B |
+external command invocation; <name> must refer to a legal, already
+defined command name (NOT IMPL.)
+.TP
+.B &
+variable assignment; <name> defines a variable in the running
+operation structure which can be dereferenced later; operator
+.B &
+assigns a variable in the rewrite context scope; operator
+.B &&
+assigns a variable that scopes the entire session, e.g. its value
+can be dereferenced later by other rewrite contexts
+.TP
+.B *
+variable dereferencing; <name> must refer to a variable that is
+defined and assigned for the running operation; operator
+.B *
+dereferences a variable scoping the rewrite context; operator
+.B **
+dereferences a variable scoping the whole session, e.g. the value
+is passed across rewrite contexts
+.TP
+.B $
+parameter dereferencing; <name> must refer to an existing parameter;
+the idea is to make some run-time parameters set by the system
+available to the rewrite engine, as the client host name, the bind DN
+if any, constant parameters initialized at config time, and so on;
+no parameter is currently set by either
+.B back\-ldap
+or
+.BR back\-meta ,
+but constant parameters can be defined in the configuration file
+by using the
+.B rewriteParam
+directive.
+.LP
+Substitution escaping has been delegated to the `%' symbol,
+which is used instead of `\e' in string substitution patterns
+because `\e' is already escaped by slapd's low level parsing routines;
+as a consequence, regex escaping requires two `\e' symbols,
+e.g. `\fB.*\e.foo\e.bar\fP' must be written as `\fB.*\e\e.foo\e\e.bar\fP'.
+.\"
+.\" The symbol can be altered at will by redefining the related macro in
+.\" "rewrite-int.h".
+.\"
+.SH "Rewrite context:"
+A rewrite context is a set of rules which are applied in sequence.
+The basic idea is to have an application initialize a rewrite
+engine (think of Apache's mod_rewrite ...) with a set of rewrite
+contexts; when string rewriting is required, one invokes the
+appropriate rewrite context with the input string and obtains the
+newly rewritten one if no errors occur.
+.LP
+Each basic server operation is associated to a rewrite context;
+they are divided in two main groups: client \-> server and
+server \-> client rewriting.
+.LP
+client \-> server:
+.LP
+.RS
+.nf
+(default) if defined and no specific context
+ is available
+bindDN bind
+searchBase search
+searchFilter search
+searchFilterAttrDN search
+compareDN compare
+compareAttrDN compare AVA
+addDN add
+addAttrDN add AVA
+modifyDN modify
+modifyAttrDN modify AVA
+modrDN modrdn
+newSuperiorDN modrdn
+deleteDN delete
+exopPasswdDN password modify extended operation DN if proxy
+.fi
+.RE
+.LP
+server \-> client:
+.LP
+.RS
+.nf
+searchResult search (only if defined; no default;
+ acts on DN and DN-syntax attributes
+ of search results)
+searchAttrDN search AVA
+matchedDN all ops (only if applicable)
+.fi
+.RE
+.LP
+.SH "Basic configuration syntax"
+.TP
+.B rewriteEngine { on | off }
+If `on', the requested rewriting is performed; if `off', no
+rewriting takes place (an easy way to stop rewriting without
+altering too much the configuration file).
+.TP
+.B rewriteContext <context name> "[ alias <aliased context name> ]"
+<Context name> is the name that identifies the context, i.e. the name
+used by the application to refer to the set of rules it contains.
+It is used also to reference sub contexts in string rewriting.
+A context may alias another one.
+In this case the alias context contains no rule, and any reference to
+it will result in accessing the aliased one.
+.TP
+.B rewriteRule "<regex match pattern>" "<substitution pattern>" "[ <flags> ]"
+Determines how a string can be rewritten if a pattern is matched.
+Examples are reported below.
+.SH "Additional configuration syntax:"
+.TP
+.B rewriteMap "<map type>" "<map name>" "[ <map attrs> ]"
+Allows one to define a map that transforms substring rewriting into
+something else.
+The map is referenced inside the substitution pattern of a rule.
+.TP
+.B rewriteParam <param name> <param value>
+Sets a value with global scope, that can be dereferenced by the
+command `%{$paramName}'.
+.TP
+.B rewriteMaxPasses <number of passes> [<number of passes per rule>]
+Sets the maximum number of total rewriting passes that can be
+performed in a single rewrite operation (to avoid loops).
+A safe default is set to 100; note that reaching this limit is still
+treated as a success; recursive invocation of rules is simply
+interrupted.
+The count applies to the rewriting operation as a whole, not
+to any single rule; an optional per-rule limit can be set.
+This limit is overridden by setting specific per-rule limits
+with the `M{n}' flag.
+.SH "Configuration examples:"
+.nf
+# set to `off' to disable rewriting
+rewriteEngine on
+
+# the rules the "suffixmassage" directive implies
+rewriteEngine on
+# all dataflow from client to server referring to DNs
+rewriteContext default
+rewriteRule "(.*)<virtualnamingcontext>$" "%1<realnamingcontext>" ":"
+# empty filter rule
+rewriteContext searchFilter
+# all dataflow from server to client
+rewriteContext searchResult
+rewriteRule "(.*)<realnamingcontext>$" "%1<virtualnamingcontext>" ":"
+rewriteContext searchAttrDN alias searchResult
+rewriteContext matchedDN alias searchResult
+
+# Everything defined here goes into the `default' context.
+# This rule changes the naming context of anything sent
+# to `dc=home,dc=net' to `dc=OpenLDAP, dc=org'
+
+rewriteRule "(.*)dc=home,[ ]?dc=net"
+ "%1dc=OpenLDAP, dc=org" ":"
+
+# since a pretty/normalized DN does not include spaces
+# after rdn separators, e.g. `,', this rule suffices:
+
+rewriteRule "(.*)dc=home,dc=net"
+ "%1dc=OpenLDAP,dc=org" ":"
+
+# Start a new context (ends input of the previous one).
+# This rule adds blanks between DN parts if not present.
+rewriteContext addBlanks
+rewriteRule "(.*),([^ ].*)" "%1, %2"
+
+# This one eats blanks
+rewriteContext eatBlanks
+rewriteRule "(.*),[ ](.*)" "%1,%2"
+
+# Here control goes back to the default rewrite
+# context; rules are appended to the existing ones.
+# anything that gets here is piped into rule `addBlanks'
+rewriteContext default
+rewriteRule ".*" "%{>addBlanks(%0)}" ":"
+
+.\" # Anything with `uid=username' is looked up in
+.\" # /etc/passwd for gecos (I know it's nearly useless,
+.\" # but it is there just as a guideline to implementing
+.\" # custom maps).
+.\" # Note the `I' flag that leaves `uid=username' in place
+.\" # if `username' does not have a valid account, and the
+.\" # `:' that forces the rule to be processed exactly once.
+.\" rewriteContext uid2Gecos
+.\" rewriteRule "(.*)uid=([a-z0-9]+),(.+)"
+.\" "%1cn=%2{xpasswd},%3" "I:"
+.\"
+.\" # Finally, in a bind, if one uses a `uid=username' DN,
+.\" # it is rewritten in `cn=name surname' if possible.
+.\" rewriteContext bindDN
+.\" rewriteRule ".*" "%{>addBlanks(%{>uid2Gecos(%0)})}" ":"
+.\"
+# Rewrite the search base according to `default' rules.
+rewriteContext searchBase alias default
+
+# Search results with OpenLDAP DN are rewritten back with
+# `dc=home,dc=net' naming context, with spaces eaten.
+rewriteContext searchResult
+rewriteRule "(.*[^ ]?)[ ]?dc=OpenLDAP,[ ]?dc=org"
+ "%{>eatBlanks(%1)}dc=home,dc=net" ":"
+
+# Bind with email instead of full DN: we first need
+# an ldap map that turns attributes into a DN (the
+# argument used when invoking the map is appended to
+# the URI and acts as the filter portion)
+rewriteMap ldap attr2dn "ldap://host/dc=my,dc=org?dn?sub"
+
+# Then we need to detect DN made up of a single email,
+# e.g. `mail=someone@example.com'; note that the rule
+# in case of match stops rewriting; in case of error,
+# it is ignored. In case we are mapping virtual
+# to real naming contexts, we also need to rewrite
+# regular DNs, because the definition of a bindDn
+# rewrite context overrides the default definition.
+rewriteContext bindDN
+rewriteRule "^mail=[^,]+@[^,]+$" "%{attr2dn(%0)}" ":@I"
+
+# This is a rather sophisticated example. It massages a
+# search filter in case who performs the search has
+# administrative privileges. First we need to keep
+# track of the bind DN of the incoming request, which is
+# stored in a variable called `binddn' with session scope,
+# and left in place to allow regular binding:
+rewriteContext bindDN
+rewriteRule ".+" "%{&&binddn(%0)}%0" ":"
+
+# A search filter containing `uid=' is rewritten only
+# if an appropriate DN is bound.
+# To do this, in the first rule the bound DN is
+# dereferenced, while the filter is decomposed in a
+# prefix, in the value of the `uid=<arg>' AVA, and
+# in a suffix. A tag `<>' is appended to the DN.
+# If the DN refers to an entry in the `ou=admin' subtree,
+# the filter is rewritten OR-ing the `uid=<arg>' with
+# `cn=<arg>'; otherwise it is left as is. This could be
+# useful, for instance, to allow apache's auth_ldap-1.4
+# module to authenticate users with both `uid' and
+# `cn', but only if the request comes from a possible
+# `cn=Web auth,ou=admin,dc=home,dc=net' user.
+rewriteContext searchFilter
+rewriteRule "(.*\e\e()uid=([a-z0-9_]+)(\e\e).*)"
+ "%{**binddn}<>%{&prefix(%1)}%{&arg(%2)}%{&suffix(%3)}"
+ ":I"
+rewriteRule "[^,]+,ou=admin,dc=home,dc=net"
+ "%{*prefix}|(uid=%{*arg})(cn=%{*arg})%{*suffix}" ":@I"
+rewriteRule ".*<>" "%{*prefix}uid=%{*arg}%{*suffix}" ":"
+
+# This example shows how to strip unwanted DN-valued
+# attribute values from a search result; the first rule
+# matches DN values below "ou=People,dc=example,dc=com";
+# in case of match the rewriting exits successfully.
+# The second rule matches everything else and causes
+# the value to be rejected.
+rewriteContext searchResult
+rewriteRule ".*,ou=People,dc=example,dc=com" "%0" ":@"
+rewriteRule ".*" "" "#"
+.fi
+.SH "LDAP Proxy resolution (a possible evolution of slapd\-ldap(5)):"
+In case the rewritten DN is an LDAP URI, the operation is initiated
+towards the host[:port] indicated in the uri, if it does not refer
+to the local server.
+E.g.:
+.LP
+.nf
+ rewriteRule '^cn=root,.*' '%0' 'G{3}'
+ rewriteRule '^cn=[a-l].*' 'ldap://ldap1.my.org/%0' ':@'
+ rewriteRule '^cn=[m-z].*' 'ldap://ldap2.my.org/%0' ':@'
+ rewriteRule '.*' 'ldap://ldap3.my.org/%0' ':@'
+.fi
+.LP
+(Rule 1 is simply there to illustrate the `G{n}' action; it could have
+been written:
+.LP
+.nf
+ rewriteRule '^cn=root,.*' 'ldap://ldap3.my.org/%0' ':@'
+.fi
+.LP
+with the advantage of saving one rewrite pass ...)
+
+.SH ACCESS CONTROL
+The
+.B meta
+backend does not honor all ACL semantics as described in
+.BR slapd.access (5).
+In general, access checking is delegated to the remote server(s).
+Only
+.B read (=r)
+access to the
+.B entry
+pseudo-attribute and to the other attribute values of the entries
+returned by the
+.B search
+operation is honored, which is performed by the frontend.
+
+.SH PROXY CACHE OVERLAY
+The proxy cache overlay
+allows caching of LDAP search requests (queries) in a local database.
+See
+.BR slapo\-pcache (5)
+for details.
+
+.SH DEPRECATED STATEMENTS
+The following statements have been deprecated and should no longer be used.
+
+.TP
+.B pseudorootdn "<substitute DN in case of rootdn bind>"
+Use
+.B idassert\-bind
+instead.
+
+.TP
+.B pseudorootpw "<substitute password in case of rootdn bind>"
+Use
+.B idassert\-bind
+instead.
+
+
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-asyncmeta (5),
+.BR slapd\-ldap (5),
+.BR slapo\-pcache (5),
+.BR slapd (8),
+.BR regex (7),
+.BR re_format (7).
+.SH AUTHOR
+Pierangelo Masarati, based on back-ldap by Howard Chu
diff --git a/doc/man/man5/slapd-monitor.5 b/doc/man/man5/slapd-monitor.5
new file mode 100644
index 0000000..84a85ba
--- /dev/null
+++ b/doc/man/man5/slapd-monitor.5
@@ -0,0 +1,126 @@
+.TH SLAPD-MONITOR 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd\-monitor \- Monitor backend to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B monitor
+backend to
+.BR slapd (8)
+is not an actual database; if enabled, it is automatically generated
+and dynamically maintained by
+.B slapd
+with information about the running status of the daemon.
+.LP
+To inspect all monitor information, issue a subtree search with base
+cn=Monitor, requesting that attributes "+" and "*" are returned.
+The monitor backend produces mostly operational attributes, and LDAP
+only returns operational attributes that are explicitly requested.
+Requesting attribute "+" is an extension which requests all operational
+attributes.
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the
+.B monitor
+backend database.
+That is, they must follow a "database monitor" line and come before any
+subsequent "backend" or "database" lines.
+.LP
+As opposed to most databases, the
+.B monitor
+database can be instantiated only once, i.e. only one occurrence
+of "database monitor" can occur in the
+.BR slapd.conf (5)
+file.
+Moreover, the suffix of the database cannot be explicitly set by means
+of the
+.B suffix
+directive.
+The suffix is automatically set
+to "\fIcn=Monitor\fP".
+.LP
+The
+.B monitor
+database honors the
+.B rootdn
+and the
+.B rootpw
+directives, and the usual ACL directives, e.g. the
+.B access
+directive.
+.\".LP
+.\"The following directives can be used:
+.\".TP
+.\".BI l \ <locality>
+.\"The additional argument \fI<locality>\fP,
+.\"a string, is added to the "\fIcn=Monitor\fP" entry as value of the
+.\".B l
+.\"attribute (Note: this may be subjected to changes).
+.LP
+Other database options are described in the
+.BR slapd.conf (5)
+manual page.
+.SH USAGE
+The usage is:
+.TP
+1) enable the \fBmonitor\fP backend at configure:
+.LP
+.RS
+.nf
+configure \-\-enable\-monitor
+.fi
+.RE
+.TP
+2) activate the \fBmonitor\fP database in the \fBslapd.conf\fP(5) file:
+.LP
+.RS
+.nf
+database monitor
+.fi
+.RE
+.TP
+3) add ACLs as detailed in \fBslapd.access\fP(5) to control access to the database, e.g.:
+.LP
+.RS
+.nf
+access to dn.subtree="cn=Monitor"
+ by dn.exact="uid=Admin,dc=my,dc=org" write
+ by users read
+ by * none
+.fi
+.RE
+.TP
+4) ensure that the \fBcore.schema\fP file is loaded.
+The
+.B monitor
+backend relies on some standard track attributeTypes
+that must be already defined when the backend is started.
+.SH ACCESS CONTROL
+The
+.B monitor
+backend honors access control semantics as indicated in
+.BR slapd.access (5),
+including the
+.B disclose
+access privilege, on all currently implemented operations.
+.SH KNOWN LIMITATIONS
+The
+.B monitor
+backend does not honor size/time limits in search operations.
+.SH FILES
+.TP
+.B ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd.access (5),
+.BR slapd (8),
+.BR ldap (3).
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man5/slapd-ndb.5 b/doc/man/man5/slapd-ndb.5
new file mode 100644
index 0000000..ead0651
--- /dev/null
+++ b/doc/man/man5/slapd-ndb.5
@@ -0,0 +1,127 @@
+.TH SLAPD-NDB 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2008-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd\-ndb \- MySQL NDB backend to slapd
+.SH SYNOPSIS
+.B ETCDIR/slapd.conf
+.SH DESCRIPTION
+The \fBndb\fP backend to
+.BR slapd (8)
+uses the MySQL Cluster package to store data, through its NDB API.
+It provides fault tolerance with extreme scalability, along with
+a degree of SQL compatibility.
+.LP
+This backend is designed to store LDAP information using tables that
+are also visible from SQL. It uses a higher level SQL API for creating
+these tables, while using the low level NDB API for storing and
+retrieving the data within these tables. The NDB Cluster engine
+allows data to be partitioned across multiple data nodes, and this
+backend allows multiple slapd instances to operate against a given
+database concurrently.
+.LP
+The general approach is to use distinct tables for each LDAP object class.
+Entries comprised of multiple object classes will have their data
+spread across multiple tables. The data tables use a 64 bit entryID
+as their primary key. The DIT hierarchy is maintained in a separate
+table, which maps DNs to entryIDs.
+.LP
+This backend is experimental. While intended to be a general-purpose
+backend, it is currently missing a number of common LDAP features.
+See the \fBTODO\fP file in the source directory for details.
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the \fBndb\fP backend database.
+That is, they must follow a "database ndb" line and
+come before any subsequent "backend" or "database" lines.
+Other database options are described in the
+.BR slapd.conf (5)
+manual page.
+
+.SH DATA SOURCE CONFIGURATION
+
+.TP
+.B dbhost <hostname>
+The name or IP address of the host running the MySQL server. The default
+is "localhost". On Unix systems, the connection to a local server is made
+using a Unix Domain socket, whose path is specified using the
+.B dbsocket
+directive.
+.TP
+.B dbuser <username>
+The MySQL login ID to use when connecting to the MySQL server. The chosen
+user must have sufficient privileges to manipulate the SQL tables in the
+target database.
+.TP
+.B dbpasswd <password>
+The password for the \fBdbuser\fP.
+.TP
+.B dbname <database name>
+The name of the MySQL database to use.
+.TP
+.B dbport <port>
+The port number to use for the TCP connection to the MySQL server.
+.TP
+.B dbsocket <path>
+The socket to be used for connecting to a local MySQL server.
+.TP
+.B dbflag <integer>
+Client flags for the MySQL session. See the MySQL documentation for details.
+.TP
+.B dbconnect <connectstring>
+The name or IP address of the host running the cluster manager. The default
+is "localhost".
+.TP
+.B dbconnections <integer>
+The number of cluster connections to establish. Using up to 4 may improve
+performance under heavier load. The default is 1.
+
+.SH SCHEMA CONFIGURATION
+.TP
+.B attrlen <attribute> <length>
+Specify the column length to use for a particular attribute. LDAP attributes are
+stored in individual columns of the SQL tables. The maximum column lengths for
+each column must be specified when creating these tables. If a length constraint
+was specified in the attribute's LDAP schema definition, that value will be used
+by default. If the schema didn't specify a constraint, the default is 128 bytes.
+Currently the maximum is 1024.
+.TP
+.B index <attr[,attr...]>
+Specify a list of attributes for which indexing should be maintained.
+Currently there is no support for substring indexing; a single index structure
+provides presence, equality, and inequality indexing for the specified attributes.
+.TP
+.B attrset <set> <attrs>
+Specify a list of attributes to be treated as an attribute set. This directive
+creates a table named \fIset\fP which will contain all of the listed attributes.
+Ordinarily an attribute resides in a table named by an object class that uses
+the attribute. However, attributes are only allowed to appear in a single table.
+For attributes that are derived from an inherited object class definition,
+the attribute will only be stored in the superior class's table.
+Attribute sets should be defined for any attributes that are used in multiple
+unrelated object classes, i.e., classes that are not connected by a simple
+inheritance chain.
+.SH ACCESS CONTROL
+The
+.B ndb
+backend honors most access control semantics as indicated in
+.BR slapd.access (5).
+.SH FILES
+.TP
+.B ETCDIR/slapd.conf
+default
+.B slapd
+configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd (8),
+.BR slapadd (8),
+.BR slapcat (8),
+.BR slapindex (8),
+.BR slapmodify (8),
+MySQL Cluster documentation.
+.SH AUTHOR
+Howard Chu, with assistance from Johan Andersson et al @ MySQL.
diff --git a/doc/man/man5/slapd-null.5 b/doc/man/man5/slapd-null.5
new file mode 100644
index 0000000..f091ed6
--- /dev/null
+++ b/doc/man/man5/slapd-null.5
@@ -0,0 +1,72 @@
+.TH SLAPD-NULL 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2002-2022 The OpenLDAP Foundation. All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd\-null \- Null backend to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The Null backend to
+.BR slapd (8)
+is surely the most useful part of
+.BR slapd :
+.br
+- Searches return success but no entries.
+.br
+- Compares return compareFalse.
+.br
+- Updates return success (unless readonly is on) but do nothing.
+.br
+- Binds other than as the rootdn fail unless the database option "bind
+on" is given.
+.br
+- The
+.BR slapadd (8)
+and
+.BR slapcat (8)
+tools are equally exciting.
+.br
+Inspired by the /dev/null device.
+.SH CONFIGURATION
+This
+.B slapd.conf
+option applies to the NULL backend database.
+That is, it must follow a "database null" line and come before
+any subsequent "database" lines.
+Other database options are described in the
+.BR slapd.conf (5)
+manual page.
+.TP
+.B bind <on/off>
+Allow binds as any DN in this backend's suffix, with any password.
+The default is "off".
+.TP
+.B dosearch <on/off>
+If enabled, a single entry will be returned on all search requests.
+The entry's DN will be the same as the database suffix.
+The default is "off".
+.SH EXAMPLE
+Here is a possible slapd.conf extract using the Null backend:
+.LP
+.RS
+.nf
+database null
+suffix "cn=Nothing"
+bind on
+.fi
+.RE
+.SH ACCESS CONTROL
+The
+.B null
+backend does not honor any of the access control semantics described in
+.BR slapd.access (5).
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd (8),
+.BR slapadd (8),
+.BR slapcat (8).
diff --git a/doc/man/man5/slapd-passwd.5 b/doc/man/man5/slapd-passwd.5
new file mode 100644
index 0000000..6b51333
--- /dev/null
+++ b/doc/man/man5/slapd-passwd.5
@@ -0,0 +1,56 @@
+.TH SLAPD-PASSWD 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd\-passwd \- /etc/passwd backend to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The PASSWD backend to
+.BR slapd (8)
+serves up the user account information listed in the system
+.BR passwd (5)
+file. This backend is provided for demonstration purposes only.
+The DN of each entry is "uid=<username>,<suffix>".
+Note that non-base searches scan the entire passwd file, and
+are best suited for hosts with small passwd files.
+.SH CONFIGURATION
+This
+.B slapd.conf
+option applies to the PASSWD backend database.
+That is, it must follow a "database passwd" line and come before any
+subsequent "backend" or "database" lines.
+Other database options are described in the
+.BR slapd.conf (5)
+manual page.
+.TP
+.B file <filename>
+Specifies an alternate passwd file to use.
+The default is
+.BR /etc/passwd .
+.SH ACCESS CONTROL
+The
+.B passwd
+backend does not honor any of the access control semantics described in
+.BR slapd.access (5).
+Only
+.B read (=r)
+access to the
+.B entry
+pseudo-attribute and to the other attribute values of the entries
+returned by the
+.B search
+operation is honored, which is performed by the frontend.
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.TP
+/etc/passwd
+user account information
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd (8),
+.BR passwd (5).
diff --git a/doc/man/man5/slapd-perl.5 b/doc/man/man5/slapd-perl.5
new file mode 100644
index 0000000..f0fddd5
--- /dev/null
+++ b/doc/man/man5/slapd-perl.5
@@ -0,0 +1,199 @@
+.TH SLAPD-PERL 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.SH NAME
+slapd\-perl \- Perl backend to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The Perl backend to
+.BR slapd (8)
+works by embedding a
+.BR perl (1)
+interpreter into
+.BR slapd (8).
+Any perl database section of the configuration file
+.BR slapd.conf (5)
+must then specify what Perl module to use.
+.B Slapd
+then creates a new Perl object that handles all the requests for that
+particular instance of the backend.
+.LP
+You will need to create a method for each one of the
+following actions:
+.LP
+.nf
+ * new # creates a new object,
+ * search # performs the ldap search,
+ * compare # does a compare,
+ * modify # modifies an entry,
+ * add # adds an entry to backend,
+ * modrdn # modifies an entry's rdn,
+ * delete # deletes an ldap entry,
+ * config # module-specific config directives,
+ * init # called after backend is initialized.
+.fi
+.LP
+Unless otherwise specified, the methods return the result code
+which will be returned to the client. Unimplemented actions
+can just return unwillingToPerform (53).
+.TP
+.B new
+This method is called when the configuration file encounters a
+.B perlmod
+line.
+The module in that line is then effectively `use'd into the perl
+interpreter, then the \fBnew\fR method is called to create a new
+object.
+Note that multiple instances of that object may be instantiated, as
+with any perl object.
+.\" .LP
+The
+.B new
+method receives the class name as argument.
+.TP
+.B search
+This method is called when a search request comes from a client.
+It arguments are as follows:
+.nf
+ * object reference
+ * base DN
+ * scope
+ * alias dereferencing policy
+ * size limit
+ * time limit
+ * filter string
+ * attributes only flag (1 for yes)
+ * list of attributes to return (may be empty)
+.fi
+.LP
+Return value: (resultcode, ldif-entry, ldif-entry, ...)
+.TP
+.B compare
+This method is called when a compare request comes from a client.
+Its arguments are as follows.
+.nf
+ * object reference
+ * dn
+ * attribute assertion string
+.fi
+.LP
+.TP
+.B modify
+This method is called when a modify request comes from a client.
+Its arguments are as follows.
+.nf
+ * object reference
+ * dn
+ * a list formatted as follows
+ ({ "ADD" | "DELETE" | "REPLACE" },
+ attributetype, value...)...
+.fi
+.LP
+.TP
+.B add
+This method is called when a add request comes from a client.
+Its arguments are as follows.
+.nf
+ * object reference
+ * entry in string format
+.fi
+.LP
+.TP
+.B modrdn
+This method is called when a modrdn request comes from a client.
+Its arguments are as follows.
+.nf
+ * object reference
+ * dn
+ * new rdn
+ * delete old dn flag (1 means yes)
+.fi
+.LP
+.TP
+.B delete
+This method is called when a delete request comes from a client.
+Its arguments are as follows.
+.nf
+ * object reference
+ * dn
+.fi
+.LP
+.TP
+.B config
+This method is called once for each perlModuleConfig line in the
+.BR slapd.conf (5)
+configuration file.
+Its arguments are as follows.
+.nf
+ * object reference
+ * array of arguments on line
+.fi
+.LP
+Return value: nonzero if this is not a valid option.
+.TP
+.B init
+This method is called after backend is initialized.
+Its argument is as follows.
+.nf
+ * object reference
+.fi
+.LP
+Return value: nonzero if initialization failed.
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the PERL backend database.
+That is, they must follow a "database perl" line and come before any
+subsequent "backend" or "database" lines.
+Other database options are described in the
+.BR slapd.conf (5)
+manual page.
+.TP
+.B perlModulePath /path/to/libs
+Add the path to the @INC variable.
+.TP
+.B perlModule ModName
+`Use' the module name ModName from ModName.pm
+.TP
+.B filterSearchResults
+Search results are candidates that need to be filtered (with the
+filter in the search request), rather than search results to be
+returned directly to the client.
+.TP
+.B perlModuleConfig <arguments>
+Invoke the module's config method with the given arguments.
+.SH EXAMPLE
+There is an example Perl module `SampleLDAP' in the slapd/back\-perl/
+directory in the OpenLDAP source tree.
+.SH ACCESS CONTROL
+The
+.B perl
+backend does not honor any of the access control semantics described in
+.BR slapd.access (5);
+all access control is delegated to the underlying PERL scripting.
+Only
+.B read (=r)
+access to the
+.B entry
+pseudo-attribute and to the other attribute values of the entries
+returned by the
+.B search
+operation is honored, which is performed by the frontend.
+.SH WARNING
+The interface of this backend to the perl module MAY change.
+Any suggestions would greatly be appreciated.
+
+Note: in previous versions, any unrecognized lines in the slapd.conf
+file were passed to the perl module's config method. This behavior is
+deprecated (but still allowed for backward compatibility), and the
+perlModuleConfig directive should instead be used to invoke the
+module's config method. This compatibility feature will be removed at
+some future date.
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd (8),
+.BR perl (1).
diff --git a/doc/man/man5/slapd-relay.5 b/doc/man/man5/slapd-relay.5
new file mode 100644
index 0000000..057d3d4
--- /dev/null
+++ b/doc/man/man5/slapd-relay.5
@@ -0,0 +1,207 @@
+.TH SLAPD-RELAY 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd\-relay \- relay backend to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The primary purpose of this
+.BR slapd (8)
+backend is to map a naming context defined in a database
+running in the same
+.BR slapd (8)
+instance into a virtual naming context, with attributeType
+and objectClass manipulation, if required.
+It requires the
+.BR slapo\-rwm (5)
+overlay.
+.LP
+This backend and the above mentioned overlay are experimental.
+.SH CONFIGURATION
+The following
+.B slapd.conf
+directives apply to the relay backend database.
+That is, they must follow a "database relay" line and come before any
+subsequent "backend" or "database" lines.
+Other database options are described in the
+.BR slapd.conf (5)
+manual page; only the
+.B suffix
+directive is allowed by the
+.I relay
+backend.
+.TP
+.B relay <real naming context>
+The naming context of the database that is presented
+under a virtual naming context.
+The presence of this directive implies that one specific database,
+i.e. the one serving the
+.BR "real naming context" ,
+will be presented under a virtual naming context.
+
+.SH MASSAGING
+The
+.B relay
+database does not automatically rewrite the naming context
+of requests and responses.
+For this purpose, the
+.BR slapo\-rwm (5)
+overlay must be explicitly instantiated, and configured
+as appropriate.
+Usually, the
+.B rwm\-suffixmassage
+directive suffices if only naming context rewriting is required.
+
+.SH ACCESS RULES
+One important issue is that access rules are based on the identity
+that issued the operation.
+After massaging from the virtual to the real naming context, the
+frontend sees the operation as performed by the identity in the
+real naming context.
+Moreover, since
+.B back\-relay
+bypasses the real database frontend operations by short-circuiting
+operations through the internal backend API, the original database
+access rules do not apply but in selected cases, i.e. when the
+backend itself applies access control.
+As a consequence, the instances of the relay database must provide
+own access rules that are consistent with those of the original
+database, possibly adding further specific restrictions.
+So, access rules in the
+.B relay
+database must refer to identities in the real naming context.
+Examples are reported in the EXAMPLES section.
+
+.SH SCENARIOS
+.LP
+If no
+.B relay
+directive is given, the
+.I relay
+database does not refer to any specific database, but the most
+appropriate one is looked-up after rewriting the request DN
+for the operation that is being handled.
+.LP
+This allows one to write carefully crafted rewrite rules that
+cause some of the requests to be directed to one database, and
+some to another; e.g., authentication can be mapped to one
+database, and searches to another, or different target databases
+can be selected based on the DN of the request, and so.
+.LP
+Another possibility is to map the same operation to different
+databases based on details of the virtual naming context,
+e.g. groups on one database and persons on another.
+.LP
+.SH EXAMPLES
+To implement a plain virtual naming context mapping
+that refers to a single database, use
+.LP
+.nf
+ database relay
+ suffix "dc=virtual,dc=naming,dc=context"
+ relay "dc=real,dc=naming,dc=context"
+ overlay rwm
+ rwm\-suffixmassage "dc=real,dc=naming,dc=context"
+.fi
+.LP
+To implement a plain virtual naming context mapping
+that looks up the real naming context for each operation, use
+.LP
+.nf
+ database relay
+ suffix "dc=virtual,dc=naming,dc=context"
+ overlay rwm
+ rwm\-suffixmassage "dc=real,dc=naming,dc=context"
+.fi
+.LP
+This is useful, for instance, to relay different databases that
+share the terminal portion of the naming context (the one that
+is rewritten).
+.LP
+To implement the old-fashioned suffixalias, e.g. mapping
+the virtual to the real naming context, but not the results
+back from the real to the virtual naming context, use
+.LP
+.nf
+ database relay
+ suffix "dc=virtual,dc=naming,dc=context"
+ relay "dc=real,dc=naming,dc=context"
+ overlay rwm
+ rwm\-rewriteEngine on
+ rwm\-rewriteContext default
+ rwm\-rewriteRule "dc=virtual,dc=naming,dc=context"
+ "dc=real,dc=naming,dc=context" ":@"
+ rwm\-rewriteContext searchFilter
+ rwm\-rewriteContext searchEntryDN
+ rwm\-rewriteContext searchAttrDN
+ rwm\-rewriteContext matchedDN
+.fi
+.LP
+Note that the
+.BR slapo\-rwm (5)
+overlay is instantiated, but the rewrite rules are written explicitly,
+rather than automatically as with the
+.B rwm\-suffixmassage
+statement, to map all the virtual to real naming context data flow,
+but none of the real to virtual.
+.LP
+Access rules:
+.LP
+.nf
+ database mdb
+ suffix "dc=example,dc=com"
+ # skip...
+ access to dn.subtree="dc=example,dc=com"
+ by dn.exact="cn=Supervisor,dc=example,dc=com" write
+ by * read
+
+ database relay
+ suffix "o=Example,c=US"
+ relay "dc=example,dc=com"
+ overlay rwm
+ rwm\-suffixmassage "dc=example,dc=com"
+ # skip ...
+ access to dn.subtree="o=Example,c=US"
+ by dn.exact="cn=Supervisor,dc=example,dc=com" write
+ by dn.exact="cn=Relay Supervisor,dc=example,dc=com" write
+ by * read
+.fi
+.LP
+Note that, in both databases, the identities (the
+.B <who>
+clause) are in the
+.BR "real naming context" ,
+i.e.
+.BR "`dc=example,dc=com'" ,
+while the targets (the
+.B <what>
+clause) are in the
+.B real
+and in the
+.BR "virtual naming context" ,
+respectively.
+.SH ACCESS CONTROL
+The
+.B relay
+backend does not honor any of the access control semantics described in
+.BR slapd.access (5);
+all access control is delegated to the relayed database(s).
+Only
+.B read (=r)
+access to the
+.B entry
+pseudo-attribute and to the other attribute values of the entries
+returned by the
+.B search
+operation is honored, which is performed by the frontend.
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapo\-rwm (5),
+.BR slapd (8).
diff --git a/doc/man/man5/slapd-sock.5 b/doc/man/man5/slapd-sock.5
new file mode 100644
index 0000000..f68ea52
--- /dev/null
+++ b/doc/man/man5/slapd-sock.5
@@ -0,0 +1,329 @@
+.TH SLAPD-SOCK 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2007-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd\-sock \- Socket backend/overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The Socket backend to
+.BR slapd (8)
+uses an external program to handle queries, similarly to
+.BR slapd\-shell (5).
+However, in this case the external program listens on a Unix domain socket.
+This makes it possible to have a pool of processes, which persist between
+requests. This allows multithreaded operation and a higher level of
+efficiency. The external program must have been started independently;
+.BR slapd (8)
+itself will not start it.
+
+This module may also be used as an overlay on top of some other database.
+Use as an overlay allows external actions to be triggered in response to
+operations on the main database.
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the SOCK backend database.
+That is, they must follow a "database sock" line and come before any
+subsequent "backend" or "database" lines.
+Other database options are described in the
+.BR slapd.conf (5)
+manual page.
+
+Alternatively, to use this module as an overlay, these directives must
+follow an "overlay sock" line within an existing database definition.
+.TP
+.B extensions [ binddn | peername | ssf | connid ]*
+Enables the sending of additional meta-attributes with each request.
+.nf
+binddn: <bound DN>
+peername: IP=<address>:<port>
+ssf: <SSF value>
+connid: <connection ID>
+.fi
+.TP
+.B socketpath <pathname>
+Gives the path to a Unix domain socket to which the commands will
+be sent and from which replies are received.
+
+When used as an overlay, these additional directives are defined:
+.TP
+.B sockops [ bind | unbind | search | compare | modify | modrdn | add | delete | extended ]*
+Specify which request types to send to the external program. The default is
+empty (no requests are sent).
+.TP
+.B sockresps [ result | search ]*
+Specify which response types to send to the external program. "result"
+sends just the results of an operation. "search" sends all entries that
+the database returned for a search request. The default is empty
+(no responses are sent).
+.TP
+.B sockdnpat <regexp>
+Specify DN patterns for which the overlay will act. Only operations on
+DNs matching the specified regular expression will be processed. The default
+is empty (all DNs are processed).
+
+.SH PROTOCOL
+The protocol is essentially the same as
+.BR slapd\-shell (5)
+with the addition of a newline to terminate the command parameters. The
+following commands are sent:
+.RS
+.nf
+ADD
+msgid: <message id>
+<repeat { "suffix:" <database suffix DN> }>
+<entry in LDIF format>
+<blank line>
+.fi
+.RE
+.PP
+.RS
+.nf
+BIND
+msgid: <message id>
+<repeat { "suffix:" <database suffix DN> }>
+dn: <DN>
+method: <method number>
+credlen: <length of <credentials>>
+cred: <credentials>
+<blank line>
+.fi
+.RE
+.PP
+.RS
+.nf
+COMPARE
+msgid: <message id>
+<repeat { "suffix:" <database suffix DN> }>
+dn: <DN>
+<attribute>: <value>
+<blank line>
+.fi
+.RE
+.PP
+.RS
+.nf
+DELETE
+msgid: <message id>
+<repeat { "suffix:" <database suffix DN> }>
+dn: <DN>
+<blank line>
+.fi
+.RE
+.PP
+.RS
+.nf
+EXTENDED
+msgid: <message id>
+<repeat { "suffix:" <database suffix DN> }>
+oid: <OID>
+value: <base64-value>
+<blank line>
+.fi
+.RE
+.PP
+.RS
+.nf
+MODIFY
+msgid: <message id>
+<repeat { "suffix:" <database suffix DN> }>
+dn: <DN>
+<repeat {
+ <"add"/"delete"/"replace">: <attribute>
+ <repeat { <attribute>: <value> }>
+ \-
+}>
+<blank line>
+.fi
+.RE
+.PP
+.RS
+.nf
+MODRDN
+msgid: <message id>
+<repeat { "suffix:" <database suffix DN> }>
+dn: <DN>
+newrdn: <new RDN>
+deleteoldrdn: <0 or 1>
+<if new superior is specified: "newSuperior: <DN>">
+<blank line>
+.fi
+.RE
+.PP
+.RS
+.nf
+SEARCH
+msgid: <message id>
+<repeat { "suffix:" <database suffix DN> }>
+base: <base DN>
+scope: <0-2, see ldap.h>
+deref: <0-3, see ldap.h>
+sizelimit: <size limit>
+timelimit: <time limit>
+filter: <filter>
+attrsonly: <0 or 1>
+attrs: <"all" or space-separated attribute list>
+<blank line>
+.fi
+.RE
+.PP
+.RS
+.nf
+UNBIND
+msgid: <message id>
+<repeat { "suffix:" <database suffix DN> }>
+<blank line>
+.fi
+.RE
+.LP
+The commands - except \fBunbind\fP - should output:
+.RS
+.nf
+RESULT
+code: <integer>
+matched: <matched DN>
+info: <text>
+.fi
+.RE
+where only RESULT is mandatory, and then close the socket.
+The \fBsearch\fP RESULT should be preceded by the entries in LDIF
+format, each entry followed by a blank line.
+Lines starting with `#' or `DEBUG:' are ignored.
+
+When used as an overlay, the external program should return a
+CONTINUE response if request processing should continue normally, or
+a regular RESULT response if the external program wishes to bypass the
+underlying database.
+
+If the overlay is configured to send response messages to the external
+program, they will appear as an extended RESULT message or as an
+ENTRY message, defined below. The RESULT message is similar to
+the one above, but also includes the msgid and any configured
+extensions:
+.RS
+.nf
+RESULT
+msgid: <message id>
+code: <integer>
+matched: <matched DN>
+info: <text>
+<blank line>
+.fi
+.RE
+
+Typically both the msgid and the connid will be needed to match
+a result message to a request. The ENTRY message has the form
+.RS
+.nf
+ENTRY
+msgid: <message id>
+<entry in LDIF format>
+<blank line>
+.fi
+.RE
+
+.SH KNOWN LIMITATIONS
+The
+.B sock
+backend does not process extended operation results from an external program.
+
+.SH ACCESS CONTROL
+The
+.B sock
+backend does not honor all ACL semantics as described in
+.BR slapd.access (5).
+In general, access to objects is checked by using a dummy object
+that contains only the DN, so access rules that rely on the contents
+of the object are not honored.
+In detail:
+.LP
+The
+.B add
+operation does not require
+.B write (=w)
+access to the
+.B children
+pseudo-attribute of the parent entry.
+.LP
+The
+.B bind
+operation requires
+.B auth (=x)
+access to the
+.B entry
+pseudo-attribute of the entry whose identity is being assessed;
+.B auth (=x)
+access to the credentials is not checked, but rather delegated
+to the underlying program.
+.LP
+The
+.B compare
+operation requires
+.B compare (=c)
+access to the
+.B entry
+pseudo-attribute
+of the object whose value is being asserted;
+.B compare (=c)
+access to the attribute whose value is being asserted is not checked.
+.LP
+The
+.B delete
+operation does not require
+.B write (=w)
+access to the
+.B children
+pseudo-attribute of the parent entry.
+.LP
+The
+.B modify
+operation requires
+.B write (=w)
+access to the
+.B entry
+pseudo-attribute;
+.B write (=w)
+access to the specific attributes that are modified is not checked.
+.LP
+The
+.B modrdn
+operation does not require
+.B write (=w)
+access to the
+.B children
+pseudo-attribute of the parent entry, nor to that of the new parent,
+if different;
+.B write (=w)
+access to the distinguished values of the naming attributes
+is not checked.
+.LP
+The
+.B search
+operation does not require
+.B search (=s)
+access to the
+.B entry
+pseudo_attribute of the searchBase;
+.B search (=s)
+access to the attributes and values used in the filter is not checked.
+.LP
+The
+.B extended
+operation does not require any access special rights.
+The external program has to implement any sort of access control.
+
+.SH EXAMPLE
+There is an example script in the slapd/back\-sock/ directory
+in the OpenLDAP source tree.
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd (8).
+.SH AUTHOR
+Brian Candler, with enhancements by Howard Chu
diff --git a/doc/man/man5/slapd-sock.5.links b/doc/man/man5/slapd-sock.5.links
new file mode 100644
index 0000000..b5f4e45
--- /dev/null
+++ b/doc/man/man5/slapd-sock.5.links
@@ -0,0 +1 @@
+slapo-sock.5
diff --git a/doc/man/man5/slapd-sql.5 b/doc/man/man5/slapd-sql.5
new file mode 100644
index 0000000..8e1f40b
--- /dev/null
+++ b/doc/man/man5/slapd-sql.5
@@ -0,0 +1,699 @@
+.TH SLAPD-SQL 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" $OpenLDAP$
+.SH NAME
+slapd\-sql \- SQL backend to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The primary purpose of this
+.BR slapd (8)
+backend is to PRESENT information stored in some RDBMS as an LDAP subtree
+without any programming (some SQL and maybe stored procedures can't be
+considered programming, anyway ;).
+.LP
+That is, for example, when you (some ISP) have account information you
+use in an RDBMS, and want to use modern solutions that expect such
+information in LDAP (to authenticate users, make email lookups etc.).
+Or you want to synchronize or distribute information between different
+sites/applications that use RDBMSes and/or LDAP.
+Or whatever else...
+.LP
+It is NOT designed as a general-purpose backend that uses RDBMS instead
+of LMDB (as the standard MDB backend does), though it can be
+used as such with several limitations.
+You can take a look at
+.B http://www.openldap.org/faq/index.cgi?file=378
+(OpenLDAP FAQ\-O\-Matic/General LDAP FAQ/Directories vs. conventional
+databases) to find out more on this point.
+.LP
+The idea (detailed below) is to use some meta-information to translate
+LDAP queries to SQL queries, leaving relational schema untouched, so
+that old applications can continue using it without any
+modifications.
+This allows SQL and LDAP applications to inter-operate without
+replication, and exchange data as needed.
+.LP
+The SQL backend is designed to be tunable to virtually any relational
+schema without having to change source (through that meta-information
+mentioned).
+Also, it uses ODBC to connect to RDBMSes, and is highly configurable
+for SQL dialects RDBMSes may use, so it may be used for integration
+and distribution of data on different RDBMSes, OSes, hosts etc., in
+other words, in highly heterogeneous environment.
+.LP
+This backend is \fIexperimental\fP.
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the SQL backend database, which means that
+they must follow a "database sql" line and come before any
+subsequent "backend" or "database" lines.
+Other database options not specific to this backend are described
+in the
+.BR slapd.conf (5)
+manual page.
+.SH DATA SOURCE CONFIGURATION
+
+.TP
+.B dbname <datasource name>
+The name of the ODBC datasource to use.
+.LP
+.B dbhost <hostname>
+.br
+.B dbpasswd <password>
+.br
+.B dbuser <username>
+.RS
+The three above options are generally unneeded, because this information
+is taken from the datasource specified by the
+.B dbname
+directive.
+They allow to override datasource settings.
+Also, several RDBMS' drivers tend to require explicit passing of user/password,
+even if those are given in datasource (Note:
+.B dbhost
+is currently ignored).
+.RE
+.SH SCOPING CONFIGURATION
+These options specify SQL query templates for scoping searches.
+
+.TP
+.B subtree_cond <SQL expression>
+Specifies a where-clause template used to form a subtree search condition
+(dn="(.+,)?<dn>$").
+It may differ from one SQL dialect to another (see samples).
+By default, it is constructed based on the knowledge about
+how to normalize DN values (e.g.
+\fB"<upper_func>(ldap_entries.dn) LIKE CONCAT('%',?)"\fP);
+see \fBupper_func\fP, \fBupper_needs_cast\fP, \fBconcat_pattern\fP
+and \fBstrcast_func\fP in "HELPER CONFIGURATION" for details.
+
+.TP
+.B children_cond <SQL expression>
+Specifies a where-clause template used to form a children search condition
+(dn=".+,<dn>$").
+It may differ from one SQL dialect to another (see samples).
+By default, it is constructed based on the knowledge about
+how to normalize DN values (e.g.
+\fB"<upper_func>(ldap_entries.dn) LIKE CONCAT('%,',?)"\fP);
+see \fBupper_func\fP, \fBupper_needs_cast\fP, \fBconcat_pattern\fP
+and \fBstrcast_func\fP in "HELPER CONFIGURATION" for details.
+
+.TP
+.B use_subtree_shortcut { YES | no }
+Do not use the subtree condition when the searchBase is the database
+suffix, and the scope is subtree; rather collect all entries.
+
+.RE
+.SH STATEMENT CONFIGURATION
+These options specify SQL query templates for loading schema mapping
+meta-information, adding and deleting entries to ldap_entries, etc.
+All these and subtree_cond should have the given default values.
+For the current value it is recommended to look at the sources,
+or in the log output when slapd starts with "\-d 5" or greater.
+Note that the parameter number and order must not be changed.
+
+.TP
+.B oc_query <SQL expression>
+The query that is used to collect the objectClass mapping data
+from table \fIldap_oc_mappings\fP; see "METAINFORMATION USED" for details.
+The default is
+\fB"SELECT id, name, keytbl, keycol, create_proc, delete_proc, expect_return
+FROM ldap_oc_mappings"\fP.
+
+.TP
+.B at_query <SQL expression>
+The query that is used to collect the attributeType mapping data
+from table \fIldap_attr_mappings\fP; see "METAINFORMATION USED" for details.
+The default is
+\fB"SELECT name, sel_expr, from_tbls, join_where, add_proc, delete_proc,
+param_order, expect_return FROM ldap_attr_mappings WHERE oc_map_id=?"\fP.
+
+.TP
+.B id_query <SQL expression>
+The query that is used to map a DN to an entry
+in table \fIldap_entries\fP; see "METAINFORMATION USED" for details.
+The default is
+\fB"SELECT id,keyval,oc_map_id,dn FROM ldap_entries WHERE <DN match expr>"\fP,
+where \fB<DN match expr>\fP is constructed based on the knowledge about
+how to normalize DN values (e.g. \fB"dn=?"\fP if no means to uppercase
+strings are available; typically, \fB"<upper_func>(dn)=?"\fP is used);
+see \fBupper_func\fP, \fBupper_needs_cast\fP, \fBconcat_pattern\fP
+and \fBstrcast_func\fP in "HELPER CONFIGURATION" for details.
+
+.TP
+.B insentry_stmt <SQL expression>
+The statement that is used to insert a new entry
+in table \fIldap_entries\fP; see "METAINFORMATION USED" for details.
+The default is
+\fB"INSERT INTO ldap_entries (dn, oc_map_id, parent, keyval) VALUES
+(?, ?, ?, ?)"\fP.
+
+.TP
+.B delentry_stmt <SQL expression>
+The statement that is used to delete an existing entry
+from table \fIldap_entries\fP; see "METAINFORMATION USED" for details.
+The default is
+\fB"DELETE FROM ldap_entries WHERE id=?"\fP.
+
+.TP
+.B delobjclasses_stmt <SQL expression>
+The statement that is used to delete an existing entry's ID
+from table \fIldap_objclasses\fP; see "METAINFORMATION USED" for details.
+The default is
+\fB"DELETE FROM ldap_entry_objclasses WHERE entry_id=?"\fP.
+
+.RE
+.SH HELPER CONFIGURATION
+These statements are used to modify the default behavior of the backend
+according to issues of the dialect of the RDBMS.
+The first options essentially refer to string and DN normalization
+when building filters.
+LDAP normalization is more than upper- (or lower-)casing everything;
+however, as a reasonable trade-off, for case-sensitive RDBMSes the backend
+can be instructed to uppercase strings and DNs by providing
+the \fBupper_func\fP directive.
+Some RDBMSes, to use functions on arbitrary data types, e.g. string
+constants, requires a cast, which is triggered
+by the \fBupper_needs_cast\fP directive.
+If required, a string cast function can be provided as well,
+by using the \fBstrcast_func\fP directive.
+Finally, a custom string concatenation pattern may be required;
+it is provided by the \fBconcat_pattern\fP directive.
+
+.TP
+.B upper_func <SQL function name>
+Specifies the name of a function that converts a given value to uppercase.
+This is used for case insensitive matching when the RDBMS is case sensitive.
+It may differ from one SQL dialect to another (e.g. \fBUCASE\fP, \fBUPPER\fP
+or whatever; see samples). By default, none is used, i.e. strings are not
+uppercased, so matches may be case sensitive.
+
+.TP
+.B upper_needs_cast { NO | yes }
+Set this directive to
+.B yes
+if
+.B upper_func
+needs an explicit cast when applied to literal strings.
+A cast in the form
+.B CAST (<arg> AS VARCHAR(<max DN length>))
+is used, where
+.B <max DN length>
+is builtin in back-sql; see macro
+.B BACKSQL_MAX_DN_LEN
+(currently 255; note that slapd's builtin limit, in macro
+.BR SLAP_LDAPDN_MAXLEN ,
+is set to 8192).
+This is \fIexperimental\fP and may change in future releases.
+
+.TP
+.B strcast_func <SQL function name>
+Specifies the name of a function that converts a given value to a string
+for appropriate ordering. This is used in "SELECT DISTINCT" statements
+for strongly typed RDBMSes with little implicit casting (like PostgreSQL),
+when a literal string is specified.
+This is \fIexperimental\fP and may change in future releases.
+
+.TP
+.B concat_pattern <pattern>
+This statement defines the
+.B pattern
+that is used to concatenate strings. The
+.B pattern
+MUST contain two question marks, '?', that will be replaced
+by the two strings that must be concatenated. The default value is
+.BR "CONCAT(?,?)";
+a form that is known to be highly portable (IBM db2, PostgreSQL) is
+.BR "?||?",
+but an explicit cast may be required when operating on literal strings:
+.BR "CAST(?||? AS VARCHAR(<length>))".
+On some RDBMSes (IBM db2, MSSQL) the form
+.B "?+?"
+is known to work as well.
+Carefully check the documentation of your RDBMS or stay with the examples
+for supported ones.
+This is \fIexperimental\fP and may change in future releases.
+
+.TP
+.B aliasing_keyword <string>
+Define the aliasing keyword. Some RDBMSes use the word "\fIAS\fP"
+(the default), others don't use any.
+
+.TP
+.B aliasing_quote <string>
+Define the quoting char of the aliasing keyword. Some RDBMSes
+don't require any (the default), others may require single
+or double quotes.
+
+.TP
+.B has_ldapinfo_dn_ru { NO | yes }
+Explicitly inform the backend whether the dn_ru column
+(DN in reverse uppercased form) is present in table \fIldap_entries\fP.
+Overrides automatic check (this is required, for instance,
+by PostgreSQL/unixODBC).
+This is \fIexperimental\fP and may change in future releases.
+
+.TP
+.B fail_if_no_mapping { NO | yes }
+When set to
+.B yes
+it forces \fIattribute\fP write operations to fail if no appropriate
+mapping between LDAP attributes and SQL data is available.
+The default behavior is to ignore those changes that cannot be mapped.
+It has no impact on objectClass mapping, i.e. if the
+.I structuralObjectClass
+of an entry cannot be mapped to SQL by looking up its name
+in ldap_oc_mappings, an
+.I add
+operation will fail regardless of the
+.B fail_if_no_mapping
+switch; see section "METAINFORMATION USED" for details.
+This is \fIexperimental\fP and may change in future releases.
+
+.TP
+.B allow_orphans { NO | yes }
+When set to
+.B yes
+orphaned entries (i.e. without the parent entry in the database)
+can be added. This option should be used with care, possibly
+in conjunction with some special rule on the RDBMS side that
+dynamically creates the missing parent.
+
+.TP
+.B baseObject [ <filename> ]
+Instructs the database to create and manage an in-memory baseObject
+entry instead of looking for one in the RDBMS.
+If the (optional)
+.B <filename>
+argument is given, the entry is read from that file in
+.BR LDIF (5)
+format; otherwise, an entry with objectClass \fBextensibleObject\fP
+is created based on the contents of the RDN of the \fIbaseObject\fP.
+This is particularly useful when \fIldap_entries\fP
+information is stored in a view rather than in a table, and
+.B union
+is not supported for views, so that the view can only specify
+one rule to compute the entry structure for one objectClass.
+This topic is discussed further in section "METAINFORMATION USED".
+This is \fIexperimental\fP and may change in future releases.
+
+.TP
+.B create_needs_select { NO | yes }
+Instructs the database whether or not entry creation
+in table \fIldap_entries\fP needs a subsequent select to collect
+the automatically assigned ID, instead of being returned
+by a stored procedure.
+
+.LP
+.B fetch_attrs <attrlist>
+.br
+.B fetch_all_attrs { NO | yes }
+.RS
+The first statement allows one to provide a list of attributes that
+must always be fetched in addition to those requested by any specific
+operation, because they are required for the proper usage of the
+backend. For instance, all attributes used in ACLs should be listed
+here. The second statement is a shortcut to require all attributes
+to be always loaded. Note that the dynamically generated attributes,
+e.g. \fIhasSubordinates\fP, \fIentryDN\fP and other implementation
+dependent attributes are \fBNOT\fP generated at this point, for
+consistency with the rest of slapd. This may change in the future.
+.RE
+
+.TP
+.B check_schema { YES | no }
+Instructs the database to check schema adherence of entries after
+modifications, and structural objectClass chain when entries are built.
+By default it is set to
+.BR yes .
+
+.TP
+.B sqllayer <name> [...]
+Loads the layer \fB<name>\fP onto a stack of helpers that are used
+to map DNs from LDAP to SQL representation and vice-versa.
+Subsequent args are passed to the layer configuration routine.
+This is \fIhighly experimental\fP and should be used with extreme care.
+The API of the layers is not frozen yet, so it is unpublished.
+
+.TP
+.B autocommit { NO | yes }
+Activates autocommit; by default, it is off.
+
+.SH METAINFORMATION USED
+.LP
+Almost everything mentioned later is illustrated in examples located
+in the
+.B servers/slapd/back\-sql/rdbms_depend/
+directory in the OpenLDAP source tree, and contains scripts for
+generating sample database for Oracle, MS SQL Server, mySQL and more
+(including PostgreSQL and IBM db2).
+.LP
+The first thing that one must arrange is what set of LDAP
+object classes can present your RDBMS information.
+.LP
+The easiest way is to create an objectClass for each entity you had in
+ER-diagram when designing your relational schema.
+Any relational schema, no matter how normalized it is, was designed
+after some model of your application's domain (for instance, accounts,
+services etc. in ISP), and is used in terms of its entities, not just
+tables of normalized schema.
+It means that for every attribute of every such instance there is an
+effective SQL query that loads its values.
+.LP
+Also you might want your object classes to conform to some of the standard
+schemas like inetOrgPerson etc.
+.LP
+Nevertheless, when you think it out, we must define a way to translate
+LDAP operation requests to (a series of) SQL queries.
+Let us deal with the SEARCH operation.
+.LP
+Example:
+Let's suppose that we store information about persons working in our
+organization in two tables:
+.LP
+.nf
+ PERSONS PHONES
+ ---------- -------------
+ id integer id integer
+ first_name varchar pers_id integer references persons(id)
+ last_name varchar phone
+ middle_name varchar
+ ...
+.fi
+.LP
+(PHONES contains telephone numbers associated with persons).
+A person can have several numbers, then PHONES contains several
+records with corresponding pers_id, or no numbers (and no records in
+PHONES with such pers_id).
+An LDAP objectclass to present such information could look like this:
+.LP
+.nf
+ person
+ -------
+ MUST cn
+ MAY telephoneNumber $ firstName $ lastName
+ ...
+.fi
+.LP
+To fetch all values for cn attribute given person ID, we construct the
+query:
+.LP
+.nf
+ SELECT CONCAT(persons.first_name,' ',persons.last_name)
+ AS cn FROM persons WHERE persons.id=?
+.fi
+.LP
+for telephoneNumber we can use:
+.LP
+.nf
+ SELECT phones.phone AS telephoneNumber FROM persons,phones
+ WHERE persons.id=phones.pers_id AND persons.id=?
+.fi
+.LP
+If we wanted to service LDAP requests with filters like
+(telephoneNumber=123*), we would construct something like:
+.LP
+.nf
+ SELECT ... FROM persons,phones
+ WHERE persons.id=phones.pers_id
+ AND persons.id=?
+ AND phones.phone like '%1%2%3%'
+.fi
+.LP
+(note how the telephoneNumber match is expanded in multiple wildcards
+to account for interspersed ininfluential chars like spaces, dashes
+and so; this occurs by design because telephoneNumber is defined after
+a specially recognized syntax).
+So, if we had information about what tables contain values for each
+attribute, how to join these tables and arrange these values, we could
+try to automatically generate such statements, and translate search
+filters to SQL WHERE clauses.
+.LP
+To store such information, we add three more tables to our schema
+and fill it with data (see samples):
+.LP
+.nf
+ ldap_oc_mappings (some columns are not listed for clarity)
+ ---------------
+ id=1
+ name="person"
+ keytbl="persons"
+ keycol="id"
+.fi
+.LP
+This table defines a mapping between objectclass (its name held in the
+"name" column), and a table that holds the primary key for corresponding
+entities.
+For instance, in our example, the person entity, which we are trying
+to present as "person" objectclass, resides in two tables (persons and
+phones), and is identified by the persons.id column (that we will call
+the primary key for this entity).
+Keytbl and keycol thus contain "persons" (name of the table), and "id"
+(name of the column).
+.LP
+.nf
+ ldap_attr_mappings (some columns are not listed for clarity)
+ -----------
+ id=1
+ oc_map_id=1
+ name="cn"
+ sel_expr="CONCAT(persons.first_name,' ',persons.last_name)"
+ from_tbls="persons"
+ join_where=NULL
+ ************
+ id=<n>
+ oc_map_id=1
+ name="telephoneNumber"
+ sel_expr="phones.phone"
+ from_tbls="persons,phones"
+ join_where="phones.pers_id=persons.id"
+.fi
+.LP
+This table defines mappings between LDAP attributes and SQL queries
+that load their values.
+Note that, unlike LDAP schema, these are not
+.B attribute types
+- the attribute "cn" for "person" objectclass can
+have its values in different tables than "cn" for some other objectclass,
+so attribute mappings depend on objectclass mappings (unlike attribute
+types in LDAP schema, which are indifferent to objectclasses).
+Thus, we have oc_map_id column with link to oc_mappings table.
+.LP
+Now we cut the SQL query that loads values for a given attribute into 3 parts.
+First goes into sel_expr column - this is the expression we had
+between SELECT and FROM keywords, which defines WHAT to load.
+Next is table list - text between FROM and WHERE keywords.
+It may contain aliases for convenience (see examples).
+The last is part of the where clause, which (if it exists at all) expresses the
+condition for joining the table containing values with the table
+containing the primary key (foreign key equality and such).
+If values are in the same table as the primary key, then this column is
+left NULL (as for cn attribute above).
+.LP
+Having this information in parts, we are able to not only construct
+queries that load attribute values by id of entry (for this we could
+store SQL query as a whole), but to construct queries that load id's
+of objects that correspond to a given search filter (or at least part of
+it).
+See below for examples.
+.LP
+.nf
+ ldap_entries
+ ------------
+ id=1
+ dn=<dn you choose>
+ oc_map_id=...
+ parent=<parent record id>
+ keyval=<value of primary key>
+.fi
+.LP
+This table defines mappings between DNs of entries in your LDAP tree,
+and values of primary keys for corresponding relational data.
+It has recursive structure (parent column references id column of the
+same table), which allows you to add any tree structure(s) to your
+flat relational data.
+Having id of objectclass mapping, we can determine table and column
+for primary key, and keyval stores value of it, thus defining the exact
+tuple corresponding to the LDAP entry with this DN.
+.LP
+Note that such design (see exact SQL table creation query) implies one
+important constraint - the key must be an integer.
+But all that I know about well-designed schemas makes me think that it's
+not very narrow ;) If anyone needs support for different types for
+keys - he may want to write a patch, and submit it to OpenLDAP ITS,
+then I'll include it.
+.LP
+Also, several users complained that they don't really need very
+structured trees, and they don't want to update one more table every
+time they add or delete an instance in the relational schema.
+Those people can use a view instead of a real table for ldap_entries, something
+like this (by Robin Elfrink):
+.LP
+.nf
+ CREATE VIEW ldap_entries (id, dn, oc_map_id, parent, keyval)
+ AS
+ SELECT 0, UPPER('o=MyCompany,c=NL'),
+ 3, 0, 'baseObject' FROM unixusers WHERE userid='root'
+ UNION
+ SELECT (1000000000+userid),
+ UPPER(CONCAT(CONCAT('cn=',gecos),',o=MyCompany,c=NL')),
+ 1, 0, userid FROM unixusers
+ UNION
+ SELECT (2000000000+groupnummer),
+ UPPER(CONCAT(CONCAT('cn=',groupname),',o=MyCompany,c=NL')),
+ 2, 0, groupnummer FROM groups;
+.fi
+
+.LP
+If your RDBMS does not support
+.B unions
+in views, only one objectClass can be mapped in
+.BR ldap_entries ,
+and the baseObject cannot be created; in this case, see the
+.B baseObject
+directive for a possible workaround.
+
+.LP
+.SH TYPICAL SQL BACKEND OPERATION
+Having meta-information loaded, the SQL backend uses these tables to
+determine a set of primary keys of candidates (depending on search
+scope and filter).
+It tries to do it for each objectclass registered in ldap_objclasses.
+.LP
+Example:
+for our query with filter (telephoneNumber=123*) we would get the following
+query generated (which loads candidate IDs)
+.LP
+.nf
+ SELECT ldap_entries.id,persons.id, 'person' AS objectClass,
+ ldap_entries.dn AS dn
+ FROM ldap_entries,persons,phones
+ WHERE persons.id=ldap_entries.keyval
+ AND ldap_entries.objclass=?
+ AND ldap_entries.parent=?
+ AND phones.pers_id=persons.id
+ AND (phones.phone LIKE '%1%2%3%')
+.fi
+.LP
+(for ONELEVEL search)
+or "... AND dn=?" (for BASE search)
+or "... AND dn LIKE '%?'" (for SUBTREE)
+.LP
+Then, for each candidate, we load the requested attributes using
+per-attribute queries like
+.LP
+.nf
+ SELECT phones.phone AS telephoneNumber
+ FROM persons,phones
+ WHERE persons.id=? AND phones.pers_id=persons.id
+.fi
+.LP
+Then, we use test_filter() from the frontend API to test the entry for a full
+LDAP search filter match (since we cannot effectively make sense of
+SYNTAX of corresponding LDAP schema attribute, we translate the filter
+into the most relaxed SQL condition to filter candidates), and send it to
+the user.
+.LP
+ADD, DELETE, MODIFY and MODRDN operations are also performed on per-attribute
+meta-information (add_proc etc.).
+In those fields one can specify an SQL statement or stored procedure
+call which can add, or delete given values of a given attribute, using
+the given entry keyval (see examples -- mostly PostgreSQL, ORACLE and MSSQL
+- since as of this writing there are no stored procs in MySQL).
+.LP
+We just add more columns to ldap_oc_mappings and ldap_attr_mappings, holding
+statements to execute (like create_proc, add_proc, del_proc etc.), and
+flags governing the order of parameters passed to those statements.
+Please see samples to find out what are the parameters passed, and other
+information on this matter - they are self-explanatory for those familiar
+with the concepts expressed above.
+.LP
+.SH COMMON TECHNIQUES
+First of all, let's recall that among other major differences to the
+complete LDAP data model, the above illustrated concept does not directly
+support such features as multiple objectclasses per entry, and referrals.
+Fortunately, they are easy to adopt in this scheme.
+The SQL backend requires that one more table is added to the schema:
+ldap_entry_objectclasses(entry_id,oc_name).
+.LP
+That table contains any number of objectclass names that corresponding
+entries will possess, in addition to that mentioned in mapping.
+The SQL backend automatically adds attribute mapping for the "objectclass"
+attribute to each objectclass mapping that loads values from this table.
+So, you may, for instance, have a mapping for inetOrgPerson, and use it
+for queries for "person" objectclass...
+.LP
+Referrals used to be implemented in a loose manner by adding an extra
+table that allowed any entry to host a "ref" attribute, along with
+a "referral" extra objectClass in table ldap_entry_objclasses.
+In the current implementation, referrals are treated like any other
+user-defined schema, since "referral" is a structural objectclass.
+The suggested practice is to define a "referral" entry in ldap_oc_mappings,
+holding a naming attribute, e.g. "ou" or "cn", a "ref" attribute,
+containing the url; in case multiple referrals per entry are needed,
+a separate table for urls can be created, where urls are mapped
+to the respective entries.
+The use of the naming attribute usually requires to add
+an "extensibleObject" value to ldap_entry_objclasses.
+
+.LP
+.SH CAVEATS
+As previously stated, this backend should not be considered
+a replacement of other data storage backends, but rather a gateway
+to existing RDBMS storages that need to be published in LDAP form.
+.LP
+The \fBhasSubordinates\fP operational attribute is honored by back-sql
+in search results and in compare operations; it is partially honored
+also in filtering. Owing to design limitations, a (brain-dead?) filter
+of the form
+\fB(!(hasSubordinates=TRUE))\fP
+will give no results instead of returning all the leaf entries, because
+it actually expands into \fB... AND NOT (1=1)\fP.
+If you need to find all the leaf entries, please use
+\fB(hasSubordinates=FALSE)\fP
+instead.
+.LP
+A directoryString value of the form "__First___Last_"
+(where underscores mean spaces, ASCII 0x20 char) corresponds
+to its prettified counterpart "First_Last"; this is not currently
+honored by back-sql if non-prettified data is written via RDBMS;
+when non-prettified data is written through back-sql, the prettified
+values are actually used instead.
+
+.LP
+.SH BUGS
+When the
+.B ldap_entry_objclasses
+table is empty, filters on the
+.B objectClass
+attribute erroneously result in no candidates.
+A workaround consists in adding at least one row to that table,
+no matter if valid or not.
+
+.LP
+.SH PROXY CACHE OVERLAY
+The proxy cache overlay
+allows caching of LDAP search requests (queries) in a local database.
+See
+.BR slapo\-pcache (5)
+for details.
+.SH EXAMPLES
+There are example SQL modules in the slapd/back\-sql/rdbms_depend/
+directory in the OpenLDAP source tree.
+.SH ACCESS CONTROL
+The
+.B sql
+backend honors access control semantics as indicated in
+.BR slapd.access (5)
+(including the
+.B disclose
+access privilege when enabled at compile time).
+.SH FILES
+
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd (8).
diff --git a/doc/man/man5/slapd-wt.5 b/doc/man/man5/slapd-wt.5
new file mode 100644
index 0000000..e83301a
--- /dev/null
+++ b/doc/man/man5/slapd-wt.5
@@ -0,0 +1,97 @@
+.TH SLAPD-WT 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2011-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd\-wt \- WiredTiger backend to slapd
+.SH SYNOPSIS
+.B ETCDIR/slapd.conf
+.SH DESCRIPTION
+The \fBwt\fP backend to
+.BR slapd (8)
+uses WiredTiger database library to store data.
+.LP
+The \fBwt\fP backend is experimental module that have potential high
+write performance and high concurrency performance.
+This backend have not some basic feature yet. Please backup data using
+slapcat before update the module.
+
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the \fBwt\fP backend database.
+That is, they must follow a "database wt" line and
+come before any subsequent "backend" or "database" lines.
+Other database options are described in the
+.BR slapd.conf (5)
+manual page.
+.TP
+.BI directory \ <directory>
+Specify WiredTiger home directory that containing this database and
+associated indexes live.
+A separate directory must be specified for each database.
+The default is
+.BR LOCALSTATEDIR/openldap\-data .
+.TP
+.BI idlcache \ <boolean>
+Use the in-memory idlcache. The default is true.
+.TP
+\fBindex \fR{\fI<attrlist>\fR|\fBdefault\fR} [\fBpres\fR,\fBeq\fR,\fBapprox\fR,\fBsub\fR,\fI<special>\fR]
+Specify the indexes to maintain for the given attribute (or
+list of attributes).
+Some attributes only support a subset of indexes.
+If only an \fI<attr>\fP is given, the indices specified for \fBdefault\fR
+are maintained.
+Note that setting a default does not imply that all attributes will be
+indexed. Also, for best performance, an
+.B eq
+index should always be configured for the
+.B objectClass
+attribute.
+.TP
+.BI mode \ <integer>
+back-wt does not support mode option. use umask instead.
+.TP
+\fBwtconfig \fR{\fBcreate\fR,\fBcache_size=512M\fR,\fBasync=(enabled)\fR}
+Specify configuration for wiredtiger, This parameter is pass to
+.BR wiredtiger_open (3).
+.RS
+.TP
+.B create
+create the database if it does not exist.
+.RE
+.RS
+.TP
+.B cache_size
+maximum heap memory to allocate for the cache.
+.RE
+.RS
+.TP
+.B async
+asynchronous operations configuration options. disabled by default.
+.RE
+.RS
+
+.SH ACCESS CONTROL
+The
+.B wt
+backend honors access control semantics as indicated in
+.BR slapd.access (5).
+.SH FILES
+.TP
+.B ETCDIR/slapd.conf
+default
+.B slapd
+configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd (8),
+.BR slapadd (8),
+.BR slapcat (8),
+.BR slapindex (8),
+.BR slapmodify (8),
+WiredTiger documentation.
+.SH ACKNOWLEDGEMENTS
+.so ../Project
+Written by HAMANO Tsukasa <hamano@osstech.co.jp>.
diff --git a/doc/man/man5/slapd.access.5 b/doc/man/man5/slapd.access.5
new file mode 100644
index 0000000..171a73b
--- /dev/null
+++ b/doc/man/man5/slapd.access.5
@@ -0,0 +1,1205 @@
+.TH SLAPD.ACCESS 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd.access \- access configuration for slapd, the stand-alone LDAP daemon
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.BR slapd.conf (5)
+file contains configuration information for the
+.BR slapd (8)
+daemon. This configuration file is also used by the SLAPD tools
+.BR slapacl (8),
+.BR slapadd (8),
+.BR slapauth (8),
+.BR slapcat (8),
+.BR slapdn (8),
+.BR slapindex (8),
+.BR slapmodify (8),
+and
+.BR slaptest (8).
+.LP
+The
+.B slapd.conf
+file consists of a series of global configuration options that apply to
+.B slapd
+as a whole (including all backends), followed by zero or more database
+backend definitions that contain information specific to a backend
+instance.
+.LP
+The general format of
+.B slapd.conf
+is as follows:
+.LP
+.nf
+ # comment - these options apply to every database
+ <global configuration options>
+ # first database definition & configuration options
+ database <backend 1 type>
+ <configuration options specific to backend 1>
+ # subsequent database definitions & configuration options
+ ...
+.fi
+.LP
+Both the global configuration and each backend-specific section can
+contain access information. Backend-specific access control
+directives are used for those entries that belong to the backend,
+according to their naming context. In case no access control
+directives are defined for a backend or those which are defined are
+not applicable, the directives from the global configuration section
+are then used.
+.LP
+If no access controls are present, the default policy
+allows anyone and everyone to read anything but restricts
+updates to rootdn. (e.g., "access to * by * read").
+.LP
+When dealing with an access list, because the global access list is
+effectively appended to each per-database list, if the resulting
+list is non-empty then the access list will end with an implicit
+.B access to * by * none
+directive. If there are no access directives applicable to a backend,
+then a default read is used.
+.LP
+.B Be warned: the rootdn can always read and write EVERYTHING!
+.LP
+For entries not held in any backend (such as a root DSE), the
+global directives are used.
+.LP
+Arguments that should be replaced by actual text are shown in
+brackets <>.
+.SH THE ACCESS DIRECTIVE
+The structure of the access control directives is
+.TP
+.B access to <what> "[ by <who> [ <access> ] [ <control> ] ]+"
+Grant access (specified by
+.BR <access> )
+to a set of entries and/or attributes (specified by
+.BR <what> )
+by one or more requestors (specified by
+.BR <who> ).
+
+.LP
+Lists of access directives are evaluated in the order they appear
+in \fIslapd.conf\fP.
+When a
+.B <what>
+clause matches the datum whose access is being evaluated, its
+.B <who>
+clause list is checked.
+When a
+.B <who>
+clause matches the accessor's properties, its
+.B <access>
+and
+.B <control>
+clauses are evaluated.
+
+.LP
+Access control checking stops at the first match of the
+.B <what>
+and
+.B <who>
+clause, unless otherwise dictated by the
+.B <control>
+clause.
+Each
+.B <who>
+clause list is implicitly terminated by a
+.LP
+.nf
+ by * none stop
+.fi
+.LP
+.B <control>
+clause. This implicit
+.B <control>
+stops access directive evaluation with no more access privileges
+granted to anyone else.
+To stop access directive evaluation only when both
+.B <who>
+and
+.B <what>
+match, add an explicit
+.LP
+.nf
+ by * break
+.fi
+.LP
+to the end of the
+.B <who>
+clause list.
+
+.LP
+Each
+.B <what>
+clause list is implicitly terminated by a
+.LP
+.nf
+ access to *
+ by * none
+.fi
+.LP
+clause that results in granting no access privileges to an otherwise
+unspecified datum.
+.SH THE <WHAT> FIELD
+The field
+.BR <what>
+specifies the entity the access control directive applies to.
+It can have the forms
+.LP
+.nf
+ dn[.<dnstyle>]=<dnpattern>
+ filter=<ldapfilter>
+ attrs=<attrlist>[ val[/matchingRule][.<attrstyle>]=<attrval>]
+.fi
+.LP
+with
+.LP
+.nf
+ <dnstyle>={{exact|base(object)}|regex
+ |one(level)|sub(tree)|children}
+ <attrlist>={<attr>|[{!|@}]<objectClass>}[,<attrlist>]
+ <attrstyle>={{exact|base(object)}|regex
+ |one(level)|sub(tree)|children}
+.fi
+.LP
+The statement
+.B dn=<dnpattern>
+selects the entries based on their naming context.
+The
+.B <dnpattern>
+is a string representation of the entry's DN.
+The wildcard
+.B *
+stands for all the entries, and it is implied if no
+.B dn
+form is given.
+.LP
+The
+.B <dnstyle>
+is optional; however, it is recommended to specify it to avoid ambiguities.
+.B Base
+(synonym of
+.BR baseObject ),
+the default,
+or
+.B exact
+(an alias of
+.BR base )
+indicates the entry whose DN is equal to the
+.BR <dnpattern> ;
+.B one
+(synonym of
+.BR onelevel )
+indicates all the entries immediately below the
+.BR <dnpattern> ,
+.B sub
+(synonym of
+.BR subtree )
+indicates all entries in the subtree at the
+.BR <dnpattern> ,
+.B children
+indicates all the entries below (subordinate to) the
+.BR <dnpattern> .
+.LP
+If the
+.B <dnstyle>
+qualifier is
+.BR regex ,
+then
+.B <dnpattern>
+is a POSIX (''extended'') regular expression pattern,
+as detailed in
+.BR regex (7)
+and/or
+.BR re_format (7),
+matching a normalized string representation of the entry's DN.
+The regex form of the pattern does not (yet) support UTF-8.
+.LP
+The statement
+.B filter=<ldapfilter>
+selects the entries based on a valid LDAP filter as described in RFC 4515.
+A filter of
+.B (objectClass=*)
+is implied if no
+.B filter
+form is given.
+.LP
+The statement
+.B attrs=<attrlist>
+selects the attributes the access control rule applies to.
+It is a comma-separated list of attribute types, plus the special names
+.BR entry ,
+indicating access to the entry itself, and
+.BR children ,
+indicating access to the entry's children. ObjectClass names may also
+be specified in this list, which will affect all the attributes that
+are required and/or allowed by that objectClass.
+Actually, names in
+.B <attrlist>
+that are prefixed by
+.B @
+are directly treated as objectClass names. A name prefixed by
+.B !
+is also treated as an objectClass, but in this case the access rule
+affects the attributes that are not required nor allowed
+by that objectClass.
+If no
+.B attrs
+form is given,
+.B attrs=@extensibleObject
+is implied, i.e. all attributes are addressed.
+.LP
+Using the form
+.B attrs=<attr> val[/matchingRule][.<attrstyle>]=<attrval>
+specifies access to a particular value of a single attribute.
+In this case, only a single attribute type may be given. The
+.B <attrstyle>
+.B exact
+(the default) uses the attribute's equality matching rule to compare the
+value, unless a different (and compatible) matching rule is specified. If the
+.B <attrstyle>
+is
+.BR regex ,
+the provided value is used as a POSIX (''extended'') regular
+expression pattern. If the attribute has DN syntax, the
+.B <attrstyle>
+can be any of
+.BR base ,
+.BR onelevel ,
+.B subtree
+or
+.BR children ,
+resulting in base, onelevel, subtree or children match, respectively.
+.LP
+The dn, filter, and attrs statements are additive; they can be used in sequence
+to select entities the access rule applies to based on naming context,
+value and attribute type simultaneously.
+Submatches resulting from
+.B regex
+matching can be dereferenced in the
+.B <who>
+field using the syntax
+.IR ${v<n>} ,
+where
+.I <n>
+is the submatch number.
+The default syntax,
+.IR $<n> ,
+is actually an alias for
+.IR ${d<n>} ,
+that corresponds to dereferencing submatches from the
+.B dnpattern
+portion of the
+.B <what>
+field.
+.SH THE <WHO> FIELD
+The field
+.B <who>
+indicates whom the access rules apply to.
+Multiple
+.B <who>
+statements can appear in an access control statement, indicating the
+different access privileges to the same resource that apply to different
+accessee.
+It can have the forms
+.LP
+.nf
+ *
+ anonymous
+ users
+ self[.<selfstyle>]
+
+ dn[.<dnstyle>[,<modifier>]]=<DN>
+ dnattr=<attrname>
+
+ realanonymous
+ realusers
+ realself[.<selfstyle>]
+
+ realdn[.<dnstyle>[,<modifier>]]=<DN>
+ realdnattr=<attrname>
+
+ group[/<objectclass>[/<attrname>]]
+ [.<groupstyle>]=<group>
+ peername[.<peernamestyle>]=<peername>
+ sockname[.<style>]=<sockname>
+ domain[.<domainstyle>[,<modifier>]]=<domain>
+ sockurl[.<style>]=<sockurl>
+ set[.<setstyle>]=<pattern>
+
+ ssf=<n>
+ transport_ssf=<n>
+ tls_ssf=<n>
+ sasl_ssf=<n>
+
+ dynacl/<name>[/<options>][.<dynstyle>][=<pattern>]
+.fi
+.LP
+with
+.LP
+.nf
+ <style>={exact|regex|expand}
+ <selfstyle>={level{<n>}}
+ <dnstyle>={{exact|base(object)}|regex
+ |one(level)|sub(tree)|children|level{<n>}}
+ <groupstyle>={exact|expand}
+ <peernamestyle>={<style>|ip|ipv6|path}
+ <domainstyle>={exact|regex|sub(tree)}
+ <setstyle>={exact|expand}
+ <modifier>={expand}
+ <name>=aci <pattern>=<attrname>]
+.fi
+.LP
+They may be specified in combination.
+.LP
+.nf
+.fi
+.LP
+The wildcard
+.B *
+refers to everybody.
+.LP
+The keywords prefixed by
+.B real
+act as their counterparts without prefix; the checking respectively occurs
+with the \fIauthentication\fP DN and the \fIauthorization\fP DN.
+.LP
+The keyword
+.B anonymous
+means access is granted to unauthenticated clients; it is mostly used
+to limit access to authentication resources (e.g. the
+.B userPassword
+attribute) to unauthenticated clients for authentication purposes.
+.LP
+The keyword
+.B users
+means access is granted to authenticated clients.
+.LP
+The keyword
+.B self
+means access to an entry is allowed to the entry itself (e.g. the entry
+being accessed and the requesting entry must be the same).
+It allows the
+.B level{<n>}
+style, where \fI<n>\fP indicates what ancestor of the DN
+is to be used in matches.
+A positive value indicates that the <n>-th ancestor of the user's DN
+is to be considered; a negative value indicates that the <n>-th ancestor
+of the target is to be considered.
+For example, a "\fIby self.level{1} ...\fP" clause would match
+when the object "\fIdc=example,dc=com\fP" is accessed
+by "\fIcn=User,dc=example,dc=com\fP".
+A "\fIby self.level{-1} ...\fP" clause would match when the same user
+accesses the object "\fIou=Address Book,cn=User,dc=example,dc=com\fP".
+.LP
+The statement
+.B dn=<DN>
+means that access is granted to the matching DN.
+The optional style qualifier
+.B dnstyle
+allows the same choices of the dn form of the
+.B <what>
+field. In addition, the
+.B regex
+style can exploit substring substitution of submatches in the
+.B <what>
+dn.regex clause by using the form
+.BR $<digit> ,
+with
+.B digit
+ranging from 0 to 9 (where 0 matches the entire string),
+or the form
+.BR ${<digit>+} ,
+for submatches higher than 9.
+Substring substitution from attribute value can
+be done in
+using the form
+.BR ${v<digit>+} .
+Since the dollar character is used to indicate a substring replacement,
+the dollar character that is used to indicate match up to the end of
+the string must be escaped by a second dollar character, e.g.
+.LP
+.nf
+ access to dn.regex="^(.+,)?uid=([^,]+),dc=[^,]+,dc=com$"
+ by dn.regex="^uid=$2,dc=[^,]+,dc=com$$" write
+.fi
+.LP
+The style qualifier
+allows an optional
+.BR modifier .
+At present, the only type allowed is
+.BR expand ,
+which causes substring substitution of submatches to take place
+even if
+.B dnstyle
+is not
+.BR regex .
+Note that the
+.B regex
+dnstyle in the above example may be of use only if the
+.B <by>
+clause needs to be a regex; otherwise, if the
+value of the second (from the right)
+.B dc=
+portion of the DN in the above example were fixed, the form
+.LP
+.nf
+ access to dn.regex="^(.+,)?uid=([^,]+),dc=example,dc=com$"
+ by dn.exact,expand="uid=$2,dc=example,dc=com" write
+.fi
+.LP
+could be used; if it had to match the value in the
+.B <what>
+clause, the form
+.LP
+.nf
+ access to dn.regex="^(.+,)?uid=([^,]+),dc=([^,]+),dc=com$"
+ by dn.exact,expand="uid=$2,dc=$3,dc=com" write
+.fi
+.LP
+could be used.
+.LP
+Forms of the
+.B <what>
+clause other than regex may provide submatches as well.
+The
+.BR base(object) ,
+the
+.BR sub(tree) ,
+the
+.BR one(level) ,
+and the
+.BR children
+forms provide
+.B $0
+as the match of the entire string.
+The
+.BR sub(tree) ,
+the
+.BR one(level) ,
+and the
+.BR children
+forms also provide
+.B $1
+as the match of the rightmost part of the DN as defined in the
+.B <what>
+clause.
+This may be useful, for instance, to provide access to all the
+ancestors of a user by defining
+.LP
+.nf
+ access to dn.subtree="dc=com"
+ by dn.subtree,expand="$1" read
+.fi
+.LP
+which means that only access to entries that appear in the DN of the
+.B <by>
+clause is allowed.
+.LP
+The
+.BR level{<n>}
+form is an extension and a generalization of the
+.BR onelevel
+form, which matches all DNs whose <n>-th ancestor is the pattern.
+So, \fIlevel{1}\fP is equivalent to \fIonelevel\fP,
+and \fIlevel{0}\fP is equivalent to \fIbase\fP.
+.LP
+It is perfectly useless to give any access privileges to a DN
+that exactly matches the
+.B rootdn
+of the database the ACLs apply to, because it implicitly
+possesses write privileges for the entire tree of that database.
+Actually, access control is bypassed for the
+.BR rootdn ,
+to solve the intrinsic chicken-and-egg problem.
+.LP
+The statement
+.B dnattr=<attrname>
+means that access is granted to requests whose DN is listed in the
+entry being accessed under the
+.B <attrname>
+attribute.
+.LP
+The statement
+.B group=<group>
+means that access is granted to requests whose DN is listed
+in the group entry whose DN is given by
+.BR <group> .
+The optional parameters
+.B <objectclass>
+and
+.B <attrname>
+define the objectClass and the member attributeType of the group entry.
+The defaults are
+.B groupOfNames
+and
+.BR member ,
+respectively.
+The optional style qualifier
+.B <style>
+can be
+.BR expand ,
+which means that
+.B <group>
+will be expanded as a replacement string (but not as a regular expression)
+according to
+.BR regex (7)
+and/or
+.BR re_format (7),
+and
+.BR exact ,
+which means that exact match will be used.
+If the style of the DN portion of the
+.B <what>
+clause is regex, the submatches are made available according to
+.BR regex (7)
+and/or
+.BR re_format (7);
+other styles provide limited submatches as discussed above about
+the DN form of the
+.B <by>
+clause.
+.LP
+For static groups, the specified attributeType must have
+.B DistinguishedName
+or
+.B NameAndOptionalUID
+syntax. For dynamic groups the attributeType must
+be a subtype of the
+.B labeledURI
+attributeType. Only LDAP URIs of the form
+.B ldap:///<base>??<scope>?<filter>
+will be evaluated in a dynamic group, by searching the local server only.
+.LP
+The statements
+.BR peername=<peername> ,
+.BR sockname=<sockname> ,
+.BR domain=<domain> ,
+and
+.BR sockurl=<sockurl>
+mean that the contacting host IP (in the form
+.BR "IP=<ip>:<port>"
+for IPv4, or
+.BR "IP=[<ipv6>]:<port>"
+for IPv6)
+or the contacting host named pipe file name (in the form
+.B "PATH=<path>"
+if connecting through a named pipe) for
+.BR peername ,
+the named pipe file name for
+.BR sockname ,
+the contacting host name for
+.BR domain ,
+and the contacting URL for
+.BR sockurl
+are compared against
+.B pattern
+to determine access.
+The same
+.B style
+rules for pattern match described for the
+.B group
+case apply, plus the
+.B regex
+style, which implies submatch
+.B expand
+and regex match of the corresponding connection parameters.
+The
+.B exact
+style of the
+.BR <peername>
+clause (the default) implies a case-exact match on the client's
+.BR IP ,
+including the
+.B "IP="
+prefix and the trailing
+.BR ":<port>" ,
+or the client's
+.BR path ,
+including the
+.B "PATH="
+prefix if connecting through a named pipe.
+The special
+.B ip
+style interprets the pattern as
+.BR <peername>=<ip>[%<mask>][{<n>}] ,
+where
+.B <ip>
+and
+.B <mask>
+are dotted digit representations of the IP and the mask, while
+.BR <n> ,
+delimited by curly brackets, is an optional port.
+The same applies to IPv6 addresses when the special
+.B ipv6
+style is used.
+When checking access privileges, the IP portion of the
+.BR peername
+is extracted, eliminating the
+.B "IP="
+prefix and the
+.B ":<port>"
+part, and it is compared against the
+.B <ip>
+portion of the pattern after masking with
+.BR <mask> :
+\fI((peername & <mask>) == <ip>)\fP.
+As an example,
+.B peername.ip=127.0.0.1
+and
+.B peername.ipv6=::1
+allow connections only from localhost,
+.B peername.ip=192.168.1.0%255.255.255.0
+allows connections from any IP in the 192.168.1 class C domain, and
+.B peername.ip=192.168.1.16%255.255.255.240{9009}
+allows connections from any IP in the 192.168.1.[16-31] range
+of the same domain, only if port 9009 is used.
+The special
+.B path
+style eliminates the
+.B "PATH="
+prefix from the
+.B peername
+when connecting through a named pipe, and performs an exact match
+on the given pattern.
+The
+.BR <domain>
+clause also allows the
+.B subtree
+style, which succeeds when a fully qualified name exactly matches the
+.BR domain
+pattern, or its trailing part, after a
+.BR dot ,
+exactly matches the
+.BR domain
+pattern.
+The
+.B expand
+style is allowed, implying an
+.B exact
+match with submatch expansion; the use of
+.B expand
+as a style modifier is considered more appropriate.
+As an example,
+.B domain.subtree=example.com
+will match www.example.com, but will not match www.anotherexample.com.
+The
+.B domain
+of the contacting host is determined by performing a DNS reverse lookup.
+As this lookup can easily be spoofed, use of the
+.B domain
+statement is strongly discouraged. By default, reverse lookups are disabled.
+The optional
+.B domainstyle
+qualifier of the
+.B <domain>
+clause allows a
+.B modifier
+option; the only value currently supported is
+.BR expand ,
+which causes substring substitution of submatches to take place even if
+the
+.B domainstyle
+is not
+.BR regex ,
+much like the analogous usage in
+.B <dn>
+clause.
+.LP
+The statement
+.B set=<pattern>
+is undocumented yet.
+.LP
+The statement
+.B dynacl/<name>[/<options>][.<dynstyle>][=<pattern>]
+means that access checking is delegated to the admin-defined method
+indicated by
+.BR <name> ,
+which can be registered at run-time by means of the
+.B moduleload
+statement.
+The fields
+.BR <options> ,
+.B <dynstyle>
+and
+.B <pattern>
+are optional, and are directly passed to the registered parsing routine.
+Dynacl is experimental; it must be enabled at compile time.
+.LP
+The statement
+.B dynacl/aci[=<attrname>]
+means that the access control is determined by the values in the
+.B attrname
+of the entry itself.
+The optional
+.B <attrname>
+indicates what attributeType holds the ACI information in the entry.
+By default, the
+.B OpenLDAPaci
+operational attribute is used.
+ACIs are experimental; they must be enabled at compile time.
+.LP
+The statements
+.BR ssf=<n> ,
+.BR transport_ssf=<n> ,
+.BR tls_ssf=<n> ,
+and
+.BR sasl_ssf=<n>
+set the minimum required Security Strength Factor (ssf) needed
+to grant access. The value should be positive integer.
+.SH THE <ACCESS> FIELD
+The optional field
+.B <access> ::= [[real]self]{<level>|<priv>}
+determines the access level or the specific access privileges the
+.B who
+field will have.
+Its component are defined as
+.LP
+.nf
+ <level> ::= none|disclose|auth|compare|search|read|{write|add|delete}|manage
+ <priv> ::= {=|+|\-}{0|d|x|c|s|r|{w|a|z}|m}+
+.fi
+.LP
+The modifier
+.B self
+allows special operations like having a certain access level or privilege
+only in case the operation involves the name of the user that's requesting
+the access.
+It implies the user that requests access is authorized.
+The modifier
+.B realself
+refers to the authenticated DN as opposed to the authorized DN of the
+.B self
+modifier.
+An example is the
+.B selfwrite
+access to the member attribute of a group, which allows one to add/delete
+its own DN from the member list of a group, while being not allowed
+to affect other members.
+.LP
+The
+.B level
+access model relies on an incremental interpretation of the access
+privileges.
+The possible levels are
+.BR none ,
+.BR disclose ,
+.BR auth ,
+.BR compare ,
+.BR search ,
+.BR read ,
+.BR write ,
+and
+.BR manage .
+Each access level implies all the preceding ones, thus
+.B manage
+grants all access including administrative access. This access
+allows some modifications which would otherwise be prohibited by the
+LDAP data model or the directory schema, e.g. changing the
+structural objectclass of an entry, or modifying an operational
+attribute that is defined as not user modifiable.
+The
+.BR write
+access is actually the combination of
+.BR add
+and
+.BR delete ,
+which respectively restrict the write privilege to add or delete
+the specified
+.BR <what> .
+
+.LP
+The
+.B none
+access level disallows all access including disclosure on error.
+.LP
+The
+.B disclose
+access level allows disclosure of information on error.
+.LP
+The
+.B auth
+access level means that one is allowed access to an attribute to perform
+authentication/authorization operations (e.g.
+.BR bind )
+with no other access.
+This is useful to grant unauthenticated clients the least possible
+access level to critical resources, like passwords.
+.LP
+The
+.B priv
+access model relies on the explicit setting of access privileges
+for each clause.
+The
+.B =
+sign resets previously defined accesses; as a consequence, the final
+access privileges will be only those defined by the clause.
+The
+.B +
+and
+.B \-
+signs add/remove access privileges to the existing ones.
+The privileges are
+.B m
+for manage,
+.B w
+for write,
+.B a
+for add,
+.B z
+for delete,
+.B r
+for read,
+.B s
+for search,
+.B c
+for compare,
+.B x
+for authentication, and
+.B d
+for disclose.
+More than one of the above privileges can be added in one statement.
+.B 0
+indicates no privileges and is used only by itself (e.g., +0).
+Note that
+.B +az
+is equivalent to
+.BR +w .
+.LP
+If no access is given, it defaults to
+.BR +0 .
+.SH THE <CONTROL> FIELD
+The optional field
+.B <control>
+controls the flow of access rule application.
+It can have the forms
+.LP
+.nf
+ stop
+ continue
+ break
+.fi
+.LP
+where
+.BR stop ,
+the default, means access checking stops in case of match.
+The other two forms are used to keep on processing access clauses.
+In detail, the
+.B continue
+form allows for other
+.B <who>
+clauses in the same
+.B <access>
+clause to be considered, so that they may result in incrementally altering
+the privileges, while the
+.B break
+form allows for other
+.B <access>
+clauses that match the same target to be processed.
+Consider the (silly) example
+.LP
+.nf
+ access to dn.subtree="dc=example,dc=com" attrs=cn
+ by * =cs break
+
+ access to dn.subtree="ou=People,dc=example,dc=com"
+ by * +r
+.fi
+.LP
+which allows search and compare privileges to everybody under
+the "dc=example,dc=com" tree, with the second rule allowing
+also read in the "ou=People" subtree,
+or the (even more silly) example
+.LP
+.nf
+ access to dn.subtree="dc=example,dc=com" attrs=cn
+ by * =cs continue
+ by users +r
+.fi
+.LP
+which grants everybody search and compare privileges, and adds read
+privileges to authenticated clients.
+.LP
+One useful application is to easily grant write privileges to an
+.B updatedn
+that is different from the
+.BR rootdn .
+In this case, since the
+.B updatedn
+needs write access to (almost) all data, one can use
+.LP
+.nf
+ access to *
+ by dn.exact="cn=The Update DN,dc=example,dc=com" write
+ by * break
+.fi
+.LP
+as the first access rule.
+As a consequence, unless the operation is performed with the
+.B updatedn
+identity, control is passed straight to the subsequent rules.
+
+.SH OPERATION REQUIREMENTS
+Operations require different privileges on different portions of entries.
+The following summary applies to primary MDB database backend. Requirements
+for other backends may (and often do) differ.
+
+.LP
+The
+.B add
+operation requires
+.B add (=a)
+privileges on the pseudo-attribute
+.B entry
+of the entry being added, and
+.B add (=a)
+privileges on the pseudo-attribute
+.B children
+of the entry's parent.
+When adding the suffix entry of a database,
+.B add
+access to
+.B children
+of the empty DN ("") is required. Also if
+Add content ACL checking has been configured on
+the database (see the
+.BR slapd.conf (5)
+or
+.BR slapd\-config (5)
+manual page),
+.B add (=a)
+will be required on all of the attributes being added.
+
+.LP
+The
+.B bind
+operation, when credentials are stored in the directory, requires
+.B auth (=x)
+privileges on the attribute the credentials are stored in (usually
+.BR userPassword ).
+
+.LP
+The
+.B compare
+operation requires
+.B compare (=c)
+privileges on the attribute that is being compared.
+
+.LP
+The
+.B delete
+operation requires
+.B delete (=z)
+privileges on the pseudo-attribute
+.B entry
+of the entry being deleted, and
+.B delete (=d)
+privileges on the
+.B children
+pseudo-attribute of the entry's parent.
+
+.LP
+The
+.B modify
+operation requires
+.B write (=w)
+privileges on the attributes being modified.
+In detail,
+.B add (=a)
+is required to add new values,
+.B delete (=z)
+is required to delete existing values,
+and both
+.B delete
+and
+.BR "add (=az)" ,
+or
+.BR "write (=w)" ,
+are required to replace existing values.
+
+.LP
+The
+.B modrdn
+operation requires
+.B write (=w)
+privileges on the pseudo-attribute
+.B entry
+of the entry whose relative DN is being modified,
+.B delete (=z)
+privileges on the pseudo-attribute
+.B children
+of the old entry's parents,
+.B add (=a)
+privileges on the pseudo-attribute
+.B children
+of the new entry's parents, and
+.B add (=a)
+privileges on the attributes that are present in the new relative DN.
+.B Delete (=z)
+privileges are also required on the attributes that are present
+in the old relative DN if
+.B deleteoldrdn
+is set to 1.
+
+.LP
+The
+.B search
+operation, requires
+.B search (=s)
+privileges on the
+.B entry
+pseudo-attribute of the searchBase
+(NOTE: this was introduced with OpenLDAP 2.4).
+Then, for each entry, it requires
+.B search (=s)
+privileges on the attributes that are defined in the filter.
+The resulting entries are finally tested for
+.B read (=r)
+privileges on the pseudo-attribute
+.B entry
+(for read access to the entry itself)
+and for
+.B read (=r)
+access on each value of each attribute that is requested.
+Also, for each
+.B referral
+object used in generating continuation references, the operation requires
+.B read (=r)
+access on the pseudo-attribute
+.B entry
+(for read access to the referral object itself),
+as well as
+.B read (=r)
+access to the attribute holding the referral information
+(generally the
+.B ref
+attribute).
+
+.LP
+Some internal operations and some
+.B controls
+require specific access privileges.
+The
+.B authzID
+mapping and the
+.B proxyAuthz
+control require
+.B auth (=x)
+privileges on all the attributes that are present in the search filter
+of the URI regexp maps (the right-hand side of the
+.B authz-regexp
+directives).
+.B Auth (=x)
+privileges are also required on the
+.B authzTo
+attribute of the authorizing identity and/or on the
+.B authzFrom
+attribute of the authorized identity.
+In general, when an internal lookup is performed for authentication
+or authorization purposes, search-specific privileges (see the access
+requirements for the search operation illustrated above) are relaxed to
+.BR auth .
+
+.LP
+Access control to search entries is checked by the frontend,
+so it is fully honored by all backends; for all other operations
+and for the discovery phase of the search operation,
+full ACL semantics is only supported by the primary backends, i.e.
+.BR back\-mdb (5).
+
+Some other backend, like
+.BR back\-sql (5),
+may fully support them; others may only support a portion of the
+described semantics, or even differ in some aspects.
+The relevant details are described in the backend-specific man pages.
+
+.SH CAVEATS
+It is strongly recommended to explicitly use the most appropriate
+.B <dnstyle>
+in
+.B <what>
+and
+.B <who>
+clauses, to avoid possible incorrect specifications of the access rules
+as well as for performance (avoid unnecessary regex matching when an exact
+match suffices) reasons.
+.LP
+An administrator might create a rule of the form:
+.LP
+.nf
+ access to dn.regex="dc=example,dc=com"
+ by ...
+.fi
+.LP
+expecting it to match all entries in the subtree "dc=example,dc=com".
+However, this rule actually matches any DN which contains anywhere
+the substring "dc=example,dc=com". That is, the rule matches both
+"uid=joe,dc=example,dc=com" and "dc=example,dc=com,uid=joe".
+.LP
+To match the desired subtree, the rule would be more precisely
+written:
+.LP
+.nf
+ access to dn.regex="^(.+,)?dc=example,dc=com$"
+ by ...
+.fi
+.LP
+For performance reasons, it would be better to use the subtree style.
+.LP
+.nf
+ access to dn.subtree="dc=example,dc=com"
+ by ...
+.fi
+.LP
+When writing submatch rules, it may be convenient to avoid unnecessary
+.B regex
+.B <dnstyle>
+use; for instance, to allow access to the subtree of the user
+that matches the
+.B <what>
+clause, one could use
+.LP
+.nf
+ access to dn.regex="^(.+,)?uid=([^,]+),dc=example,dc=com$"
+ by dn.regex="^uid=$2,dc=example,dc=com$$" write
+ by ...
+.fi
+.LP
+However, since all that is required in the
+.B <by>
+clause is substring expansion, a more efficient solution is
+.LP
+.nf
+ access to dn.regex="^(.+,)?uid=([^,]+),dc=example,dc=com$"
+ by dn.exact,expand="uid=$2,dc=example,dc=com" write
+ by ...
+.fi
+.LP
+In fact, while a
+.B <dnstyle>
+of
+.B regex
+implies substring expansion,
+.BR exact ,
+as well as all the other DN specific
+.B <dnstyle>
+values, does not, so it must be explicitly requested.
+.LP
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd (8),
+.BR slapd\-* (5),
+.BR slapacl (8),
+.BR regex (7),
+.BR re_format (7)
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man5/slapd.backends.5 b/doc/man/man5/slapd.backends.5
new file mode 100644
index 0000000..641dbe4
--- /dev/null
+++ b/doc/man/man5/slapd.backends.5
@@ -0,0 +1,140 @@
+.TH SLAPD.BACKENDS 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2006-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd.backends \- backends for slapd, the stand-alone LDAP daemon
+.SH DESCRIPTION
+The
+.BR slapd (8)
+daemon can use a variety of different backends for serving LDAP requests.
+Backends may be compiled statically into slapd, or when module support
+is enabled, they may be dynamically loaded. Multiple instances of a
+backend can be configured, to serve separate databases from the same
+slapd server.
+
+
+Configuration options for each backend are documented separately in the
+corresponding
+.BR slapd\-<backend> (5)
+manual pages.
+.TP
+.B asyncmeta
+This backend performs basic LDAP proxying with respect to a set of
+remote LDAP servers. It is an enhancement of the
+.B ldap
+backend that operates asynchronously, to prevent tying up slapd threads
+while waiting for operations to complete.
+.TP
+.B config
+This backend is used to manage the configuration of slapd at run-time.
+Unlike other backends, only a single instance of the
+.B config
+backend may be defined. It also instantiates itself automatically,
+so it is always present even if not explicitly defined in the
+.BR slapd.conf (5)
+file.
+.TP
+.B dnssrv
+This backend is experimental.
+It serves up referrals based upon SRV resource records held in the
+Domain Name System.
+.TP
+.B ldap
+This backend acts as a proxy to forward incoming requests to another
+LDAP server.
+.TP
+.B ldif
+This database uses the filesystem to build the tree structure
+of the database, using plain ascii files to store data.
+Its usage should be limited to very simple databases, where performance
+is not a requirement. This backend also supports subtree renames.
+.TP
+.B mdb
+This is the recommended primary backend.
+This backend uses OpenLDAP's own MDB transactional database
+library. This backend also supports subtree renames.
+.TP
+.B meta
+This backend performs basic LDAP proxying with respect to a set of
+remote LDAP servers. It is an enhancement of the
+.B ldap
+backend.
+.TP
+.B monitor
+This backend provides information about the running status of the slapd
+daemon. Only a single instance of the
+.B monitor
+backend may be defined.
+.TP
+.B ndb
+This backend is experimental, incomplete, and deprecated.
+It uses the transactional database interface of the MySQL Cluster Engine
+(NDB) to store data. Note that Oracle, which now owns MySQL, has withdrawn
+support for NDB and this backend is unlikely to be developed any further.
+.TP
+.B null
+Operations in this backend succeed but do nothing.
+.TP
+.B passwd
+This backend is provided for demonstration purposes only.
+It serves up user account information from the system
+.BR passwd (5)
+file.
+.TP
+.B perl
+This backend embeds a
+.BR perl (1)
+interpreter into slapd.
+It runs Perl subroutines to implement LDAP operations.
+This backend is deprecated.
+.TP
+.B relay
+This backend is experimental.
+It redirects LDAP operations to another database
+in the same server, based on the naming context of the request.
+Its use requires the
+.B rwm
+overlay (see
+.BR slapo\-rwm (5)
+for details) to rewrite the naming context of the request.
+It is primarily intended to implement virtual views on databases
+that actually store data.
+.TP
+.B sql
+This backend is experimental and deprecated.
+It services LDAP requests from an SQL database.
+.TP
+.B wiredtiger
+This backend is experimental.
+It services LDAP requests from a wiredtiger database.
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.TP
+ETCDIR/slapd.d
+default slapd configuration directory
+.SH SEE ALSO
+.BR ldap (3),
+.BR slapd\-asyncmeta (5),
+.BR slapd\-config (5),
+.BR slapd\-dnssrv (5),
+.BR slapd\-ldap (5),
+.BR slapd\-ldif (5),
+.BR slapd\-mdb (5),
+.BR slapd\-meta (5),
+.BR slapd\-monitor (5),
+.BR slapd\-ndb (5),
+.BR slapd\-null (5),
+.BR slapd\-passwd (5),
+.BR slapd\-perl (5),
+.BR slapd\-relay (5),
+.BR slapd\-sql (5),
+.BR slapd\-wt (5),
+.BR slapd.conf (5),
+.BR slapd.overlays (5),
+.BR slapd (8).
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man5/slapd.conf.5 b/doc/man/man5/slapd.conf.5
new file mode 100644
index 0000000..6ec19c5
--- /dev/null
+++ b/doc/man/man5/slapd.conf.5
@@ -0,0 +1,2140 @@
+.TH SLAPD.CONF 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd.conf \- configuration file for slapd, the stand-alone LDAP daemon
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The file
+.B ETCDIR/slapd.conf
+contains configuration information for the
+.BR slapd (8)
+daemon. This configuration file is also used by the SLAPD tools
+.BR slapacl (8),
+.BR slapadd (8),
+.BR slapauth (8),
+.BR slapcat (8),
+.BR slapdn (8),
+.BR slapindex (8),
+.BR slapmodify (8),
+and
+.BR slaptest (8).
+.LP
+The
+.B slapd.conf
+file consists of a series of global configuration options that apply to
+.B slapd
+as a whole (including all backends), followed by zero or more database
+backend definitions that contain information specific to a backend
+instance.
+The configuration options are case-insensitive;
+their value, on a case by case basis, may be case-sensitive.
+.LP
+The general format of
+.B slapd.conf
+is as follows:
+.LP
+.nf
+ # comment - these options apply to every database
+ <global configuration options>
+ # first database definition & configuration options
+ database <backend 1 type>
+ <configuration options specific to backend 1>
+ # subsequent database definitions & configuration options
+ ...
+.fi
+.LP
+As many backend-specific sections as desired may be included. Global
+options can be overridden in a backend (for options that appear more
+than once, the last appearance in the
+.B slapd.conf
+file is used).
+.LP
+If a line begins with white space, it is considered a continuation
+of the previous line. No physical line should be over 2000 bytes
+long.
+.LP
+Blank lines and comment lines beginning with
+a `#' character are ignored. Note: continuation lines are unwrapped
+before comment processing is applied.
+.LP
+Arguments on configuration lines are separated by white space. If an
+argument contains white space, the argument should be enclosed in
+double quotes. If an argument contains a double quote (`"') or a
+backslash character (`\\'), the character should be preceded by a
+backslash character.
+.LP
+The specific configuration options available are discussed below in the
+Global Configuration Options, General Backend Options, and General Database
+Options. Backend-specific options are discussed in the
+.B slapd\-<backend>(5)
+manual pages. Refer to the "OpenLDAP Administrator's Guide" for more
+details on the slapd configuration file.
+.SH GLOBAL CONFIGURATION OPTIONS
+Options described in this section apply to all backends, unless specifically
+overridden in a backend definition. Arguments that should be replaced by
+actual text are shown in brackets <>.
+.TP
+.B access to <what> "[ by <who> <access> <control> ]+"
+Grant access (specified by <access>) to a set of entries and/or
+attributes (specified by <what>) by one or more requestors (specified
+by <who>).
+If no access controls are present, the default policy
+allows anyone and everyone to read anything but restricts
+updates to rootdn. (e.g., "access to * by * read").
+The rootdn can always read and write EVERYTHING!
+See
+.BR slapd.access (5)
+and the "OpenLDAP's Administrator's Guide" for details.
+.TP
+.B allow <features>
+Specify a set of features (separated by white space) to
+allow (default none).
+.B bind_v2
+allows acceptance of LDAPv2 bind requests. Note that
+.BR slapd (8)
+does not truly implement LDAPv2 (RFC 1777), now Historic (RFC 3494).
+.B bind_anon_cred
+allows anonymous bind when credentials are not empty (e.g.
+when DN is empty).
+.B bind_anon_dn
+allows unauthenticated (anonymous) bind when DN is not empty.
+.B update_anon
+allows unauthenticated (anonymous) update operations to be processed
+(subject to access controls and other administrative limits).
+.B proxy_authz_anon
+allows unauthenticated (anonymous) proxy authorization control to be processed
+(subject to access controls, authorization and other administrative limits).
+.TP
+.B argsfile <filename>
+The (absolute) name of a file that will hold the
+.B slapd
+server's command line (program name and options).
+.TP
+.B attributeoptions [option-name]...
+Define tagging attribute options or option tag/range prefixes.
+Options must not end with `\-', prefixes must end with `\-'.
+The `lang\-' prefix is predefined.
+If you use the
+.B attributeoptions
+directive, `lang\-' will no longer be defined and you must specify it
+explicitly if you want it defined.
+
+An attribute description with a tagging option is a subtype of that
+attribute description without the option.
+Except for that, options defined this way have no special semantics.
+Prefixes defined this way work like the `lang\-' options:
+They define a prefix for tagging options starting with the prefix.
+That is, if you define the prefix `x\-foo\-', you can use the option
+`x\-foo\-bar'.
+Furthermore, in a search or compare, a prefix or range name (with
+a trailing `\-') matches all options starting with that name, as well
+as the option with the range name sans the trailing `\-'.
+That is, `x\-foo\-bar\-' matches `x\-foo\-bar' and `x\-foo\-bar\-baz'.
+
+RFC 4520 reserves options beginning with `x\-' for private experiments.
+Other options should be registered with IANA, see RFC 4520 section 3.5.
+OpenLDAP also has the `binary' option built in, but this is a transfer
+option, not a tagging option.
+.HP
+.hy 0
+.B attributetype "(\ <oid>\
+ [NAME\ <name>]\
+ [DESC\ <description>]\
+ [OBSOLETE]\
+ [SUP\ <oid>]\
+ [EQUALITY\ <oid>]\
+ [ORDERING\ <oid>]\
+ [SUBSTR\ <oid>]\
+ [SYNTAX\ <oidlen>]\
+ [SINGLE\-VALUE]\
+ [COLLECTIVE]\
+ [NO\-USER\-MODIFICATION]\
+ [USAGE\ <attributeUsage>]\ )"
+.RS
+Specify an attribute type using the LDAPv3 syntax defined in RFC 4512.
+The slapd parser extends the RFC 4512 definition by allowing string
+forms as well as numeric OIDs to be used for the attribute OID and
+attribute syntax OID.
+(See the
+.B objectidentifier
+description.)
+.RE
+.TP
+.B authid\-rewrite<cmd> <args>
+Used by the authentication framework to convert simple user names
+to an LDAP DN used for authorization purposes.
+Its purpose is analogous to that of
+.BR authz-regexp
+(see below).
+The prefix \fIauthid\-\fP is followed by a set of rules analogous
+to those described in
+.BR slapo\-rwm (5)
+for data rewriting (replace the \fIrwm\-\fP prefix with \fIauthid\-\fP).
+.B authid\-rewrite<cmd>
+and
+.B authz\-regexp
+rules should not be intermixed.
+.TP
+.B authz\-policy <policy>
+Used to specify which rules to use for Proxy Authorization. Proxy
+authorization allows a client to authenticate to the server using one
+user's credentials, but specify a different identity to use for authorization
+and access control purposes. It essentially allows user A to login as user
+B, using user A's password.
+The
+.B none
+flag disables proxy authorization. This is the default setting.
+The
+.B from
+flag will use rules in the
+.I authzFrom
+attribute of the authorization DN.
+The
+.B to
+flag will use rules in the
+.I authzTo
+attribute of the authentication DN.
+The
+.B any
+flag, an alias for the deprecated value of
+.BR both ,
+will allow any of the above, whatever succeeds first (checked in
+.BR to ,
+.B from
+sequence.
+The
+.B all
+flag requires both authorizations to succeed.
+.LP
+.RS
+The rules are mechanisms to specify which identities are allowed
+to perform proxy authorization.
+The
+.I authzFrom
+attribute in an entry specifies which other users
+are allowed to proxy login to this entry. The
+.I authzTo
+attribute in
+an entry specifies which other users this user can authorize as. Use of
+.I authzTo
+rules can be easily
+abused if users are allowed to write arbitrary values to this attribute.
+In general the
+.I authzTo
+attribute must be protected with ACLs such that
+only privileged users can modify it.
+The value of
+.I authzFrom
+and
+.I authzTo
+describes an
+.B identity
+or a set of identities; it can take five forms:
+.RS
+.TP
+.B ldap:///<base>??[<scope>]?<filter>
+.RE
+.RS
+.B dn[.<dnstyle>]:<pattern>
+.RE
+.RS
+.B u[.<mech>[/<realm>]]:<pattern>
+.RE
+.RS
+.B group[/objectClass[/attributeType]]:<pattern>
+.RE
+.RS
+.B <pattern>
+.RE
+.RS
+
+.B <dnstyle>:={exact|onelevel|children|subtree|regex}
+
+.RE
+The first form is a valid LDAP
+.B URI
+where the
+.IR <host>:<port> ,
+the
+.I <attrs>
+and the
+.I <extensions>
+portions must be absent, so that the search occurs locally on either
+.I authzFrom
+or
+.IR authzTo .
+
+.LP
+The second form is a
+.BR DN .
+The optional
+.B dnstyle
+modifiers
+.IR exact ,
+.IR onelevel ,
+.IR children ,
+and
+.I subtree
+provide exact, onelevel, children and subtree matches, which cause
+.I <pattern>
+to be normalized according to the DN normalization rules.
+The special
+.B dnstyle
+modifier
+.I regex
+causes the
+.I <pattern>
+to be treated as a POSIX (''extended'') regular expression, as
+discussed in
+.BR regex (7)
+and/or
+.BR re_format (7).
+A pattern of
+.I *
+means any non-anonymous DN.
+
+.LP
+The third form is a SASL
+.BR id .
+The optional fields
+.I <mech>
+and
+.I <realm>
+allow specification of a SASL
+.BR mechanism ,
+and eventually a SASL
+.BR realm ,
+for those mechanisms that support one.
+The need to allow the specification of a mechanism is still debated,
+and users are strongly discouraged to rely on this possibility.
+
+.LP
+The fourth form is a group specification.
+It consists of the keyword
+.BR group ,
+optionally followed by the specification of the group
+.B objectClass
+and
+.BR attributeType .
+The
+.B objectClass
+defaults to
+.IR groupOfNames .
+The
+.B attributeType
+defaults to
+.IR member .
+The group with DN
+.B <pattern>
+is searched with base scope, filtered on the specified
+.BR objectClass .
+The values of the resulting
+.B attributeType
+are searched for the asserted DN.
+
+.LP
+The fifth form is provided for backwards compatibility. If no identity
+type is provided, i.e. only
+.B <pattern>
+is present, an
+.I exact DN
+is assumed; as a consequence,
+.B <pattern>
+is subjected to DN normalization.
+
+.LP
+Since the interpretation of
+.I authzFrom
+and
+.I authzTo
+can impact security, users are strongly encouraged
+to explicitly set the type of identity specification that is being used.
+A subset of these rules can be used as third arg in the
+.B authz\-regexp
+statement (see below); significantly, the
+.IR URI ,
+provided it results in exactly one entry,
+and the
+.I dn.exact:<dn>
+forms.
+.RE
+.TP
+.B authz\-regexp <match> <replace>
+Used by the authentication framework to convert simple user names,
+such as provided by SASL subsystem, or extracted from certificates
+in case of cert-based SASL EXTERNAL, or provided within the RFC 4370
+"proxied authorization" control, to an LDAP DN used for
+authorization purposes. Note that the resulting DN need not refer
+to an existing entry to be considered valid. When an authorization
+request is received from the SASL subsystem, the SASL
+.BR USERNAME ,
+.BR REALM ,
+and
+.B MECHANISM
+are taken, when available, and combined into a name of the form
+.RS
+.RS
+.TP
+.B UID=<username>[[,CN=<realm>],CN=<mechanism>],CN=auth
+
+.RE
+This name is then compared against the
+.B match
+POSIX (''extended'') regular expression, and if the match is successful,
+the name is replaced with the
+.B replace
+string. If there are wildcard strings in the
+.B match
+regular expression that are enclosed in parenthesis, e.g.
+.RS
+.TP
+.B UID=([^,]*),CN=.*
+
+.RE
+then the portion of the name that matched the wildcard will be stored
+in the numbered placeholder variable $1. If there are other wildcard strings
+in parenthesis, the matching strings will be in $2, $3, etc. up to $9. The
+placeholders can then be used in the
+.B replace
+string, e.g.
+.RS
+.TP
+.B UID=$1,OU=Accounts,DC=example,DC=com
+
+.RE
+The replaced name can be either a DN, i.e. a string prefixed by "dn:",
+or an LDAP URI.
+If the latter, the server will use the URI to search its own database(s)
+and, if the search returns exactly one entry, the name is
+replaced by the DN of that entry. The LDAP URI must have no
+hostport, attrs, or extensions components, but the filter is mandatory,
+e.g.
+.RS
+.TP
+.B ldap:///OU=Accounts,DC=example,DC=com??one?(UID=$1)
+
+.RE
+The protocol portion of the URI must be strictly
+.BR ldap .
+Note that this search is subject to access controls. Specifically,
+the authentication identity must have "auth" access in the subject.
+
+Multiple
+.B authz\-regexp
+options can be given in the configuration file to allow for multiple matching
+and replacement patterns. The matching patterns are checked in the order they
+appear in the file, stopping at the first successful match.
+
+.\".B Caution:
+.\"Because the plus sign + is a character recognized by the regular expression engine,
+.\"and it will appear in names that include a REALM, be careful to escape the
+.\"plus sign with a backslash \\+ to remove the character's special meaning.
+.RE
+.TP
+.B concurrency <integer>
+Specify a desired level of concurrency. Provided to the underlying
+thread system as a hint. The default is not to provide any hint. This setting
+is only meaningful on some platforms where there is not a one to one
+correspondence between user threads and kernel threads.
+.TP
+.B conn_max_pending <integer>
+Specify the maximum number of pending requests for an anonymous session.
+If requests are submitted faster than the server can process them, they
+will be queued up to this limit. If the limit is exceeded, the session
+is closed. The default is 100.
+.TP
+.B conn_max_pending_auth <integer>
+Specify the maximum number of pending requests for an authenticated session.
+The default is 1000.
+.TP
+.B defaultsearchbase <dn>
+Specify a default search base to use when client submits a
+non-base search request with an empty base DN.
+Base scoped search requests with an empty base DN are not affected.
+.TP
+.B disallow <features>
+Specify a set of features (separated by white space) to
+disallow (default none).
+.B bind_anon
+disables acceptance of anonymous bind requests. Note that this setting
+does not prohibit anonymous directory access (See "require authc").
+.B bind_simple
+disables simple (bind) authentication.
+.B tls_2_anon
+disables forcing session to anonymous status (see also
+.BR tls_authc )
+upon StartTLS operation receipt.
+.B tls_authc
+disallows the StartTLS operation if authenticated (see also
+.BR tls_2_anon ).
+.B proxy_authz_non_critical
+disables acceptance of the proxied authorization control (RFC4370)
+with criticality set to FALSE.
+.B dontusecopy_non_critical
+disables acceptance of the dontUseCopy control (a work in progress)
+with criticality set to FALSE.
+.HP
+.hy 0
+.B ditcontentrule "(\ <oid>\
+ [NAME\ <name>]\
+ [DESC\ <description>]\
+ [OBSOLETE]\
+ [AUX\ <oids>]\
+ [MUST\ <oids>]\
+ [MAY\ <oids>]\
+ [NOT\ <oids>]\ )"
+.RS
+Specify an DIT Content Rule using the LDAPv3 syntax defined in RFC 4512.
+The slapd parser extends the RFC 4512 definition by allowing string
+forms as well as numeric OIDs to be used for the attribute OID and
+attribute syntax OID.
+(See the
+.B objectidentifier
+description.)
+.RE
+.TP
+.B gentlehup { on | off }
+A SIGHUP signal will only cause a 'gentle' shutdown-attempt:
+.B Slapd
+will stop listening for new connections, but will not close the
+connections to the current clients. Future write operations return
+unwilling-to-perform, though. Slapd terminates when all clients
+have closed their connections (if they ever do), or \- as before \-
+if it receives a SIGTERM signal. This can be useful if you wish to
+terminate the server and start a new
+.B slapd
+server
+.B with another database,
+without disrupting the currently active clients.
+The default is off. You may wish to use
+.B idletimeout
+along with this option.
+.TP
+.B idletimeout <integer>
+Specify the number of seconds to wait before forcibly closing
+an idle client connection. A setting of 0 disables this
+feature. The default is 0. You may also want to set the
+.B writetimeout
+option.
+.TP
+.B include <filename>
+Read additional configuration information from the given file before
+continuing with the next line of the current file.
+.TP
+.B index_hash64 { on | off }
+Use a 64 bit hash for indexing. The default is to use 32 bit hashes.
+These hashes are used for equality and substring indexing. The 64 bit
+version may be needed to avoid index collisions when the number of
+indexed values exceeds ~64 million. (Note that substring indexing
+generates multiple index values per actual attribute value.)
+Indices generated with 32 bit hashes are incompatible with the 64 bit
+version, and vice versa. Any existing databases must be fully reloaded
+when changing this setting. This directive is only supported on 64 bit CPUs.
+.TP
+.B index_intlen <integer>
+Specify the key length for ordered integer indices. The most significant
+bytes of the binary integer will be used for index keys. The default
+value is 4, which provides exact indexing for 31 bit values.
+A floating point representation is used to index too large values.
+.TP
+.B index_substr_if_maxlen <integer>
+Specify the maximum length for subinitial and subfinal indices. Only
+this many characters of an attribute value will be processed by the
+indexing functions; any excess characters are ignored. The default is 4.
+.TP
+.B index_substr_if_minlen <integer>
+Specify the minimum length for subinitial and subfinal indices. An
+attribute value must have at least this many characters in order to be
+processed by the indexing functions. The default is 2.
+.TP
+.B index_substr_any_len <integer>
+Specify the length used for subany indices. An attribute value must have
+at least this many characters in order to be processed. Attribute values
+longer than this length will be processed in segments of this length. The
+default is 4. The subany index will also be used in subinitial and
+subfinal index lookups when the filter string is longer than the
+.I index_substr_if_maxlen
+value.
+.TP
+.B index_substr_any_step <integer>
+Specify the steps used in subany index lookups. This value sets the offset
+for the segments of a filter string that are processed for a subany index
+lookup. The default is 2. For example, with the default values, a search
+using this filter "cn=*abcdefgh*" would generate index lookups for
+"abcd", "cdef", and "efgh".
+
+.LP
+Note: Indexing support depends on the particular backend in use. Also,
+changing these settings will generally require deleting any indices that
+depend on these parameters and recreating them with
+.BR slapindex (8).
+
+.HP
+.hy 0
+.B ldapsyntax "(\ <oid>\
+ [DESC\ <description>]\
+ [X\-SUBST <substitute-syntax>]\ )"
+.RS
+Specify an LDAP syntax using the LDAPv3 syntax defined in RFC 4512.
+The slapd parser extends the RFC 4512 definition by allowing string
+forms as well as numeric OIDs to be used for the syntax OID.
+(See the
+.B objectidentifier
+description.)
+The slapd parser also honors the
+.B X\-SUBST
+extension (an OpenLDAP-specific extension), which allows one to use the
+.B ldapsyntax
+statement to define a non-implemented syntax along with another syntax,
+the extension value
+.IR substitute-syntax ,
+as its temporary replacement.
+The
+.I substitute-syntax
+must be defined.
+This allows one to define attribute types that make use of non-implemented syntaxes
+using the correct syntax OID.
+Unless
+.B X\-SUBST
+is used, this configuration statement would result in an error,
+since no handlers would be associated to the resulting syntax structure.
+.RE
+
+.TP
+.B listener-threads <integer>
+Specify the number of threads to use for the connection manager.
+The default is 1 and this is typically adequate for up to 16 CPU cores.
+The value should be set to a power of 2.
+.TP
+.B localSSF <SSF>
+Specifies the Security Strength Factor (SSF) to be given local LDAP sessions,
+such as those to the ldapi:// listener. For a description of SSF values,
+see
+.BR sasl-secprops 's
+.B minssf
+option description. The default is 71.
+.TP
+.B logfile <filename>
+Specify a file for recording slapd debug messages. By default these messages
+only go to stderr, are not recorded anywhere else, and are unrelated to
+messages exposed by the
+.B loglevel
+configuration parameter. Specifying a logfile copies messages to both stderr
+and the logfile.
+.TP
+.B loglevel <integer> [...]
+Specify the level at which debugging statements and operation
+statistics should be syslogged (currently logged to the
+.BR syslogd (8)
+LOG_LOCAL4 facility).
+They must be considered subsystems rather than increasingly verbose
+log levels.
+Some messages with higher priority are logged regardless
+of the configured loglevel as soon as any logging is configured.
+Log levels are additive, and available levels are:
+.RS
+.RS
+.PD 0
+.TP
+.B 1
+.B (0x1 trace)
+trace function calls
+.TP
+.B 2
+.B (0x2 packets)
+debug packet handling
+.TP
+.B 4
+.B (0x4 args)
+heavy trace debugging (function args)
+.TP
+.B 8
+.B (0x8 conns)
+connection management
+.TP
+.B 16
+.B (0x10 BER)
+print out packets sent and received
+.TP
+.B 32
+.B (0x20 filter)
+search filter processing
+.TP
+.B 64
+.B (0x40 config)
+configuration file processing
+.TP
+.B 128
+.B (0x80 ACL)
+access control list processing
+.TP
+.B 256
+.B (0x100 stats)
+connections, LDAP operations, results (recommended)
+.TP
+.B 512
+.B (0x200 stats2)
+stats2 log entries sent
+.TP
+.B 1024
+.B (0x400 shell)
+print communication with shell backends
+.TP
+.B 2048
+.B (0x800 parse)
+entry parsing
+\".TP
+\".B 4096
+\".B (0x1000 cache)
+\"caching (unused)
+\".TP
+\".B 8192
+\".B (0x2000 index)
+\"data indexing (unused)
+.TP
+.B 16384
+.B (0x4000 sync)
+LDAPSync replication
+.TP
+.B 32768
+.B (0x8000 none)
+only messages that get logged whatever log level is set
+.PD
+.RE
+The desired log level can be input as a single integer that combines
+the (ORed) desired levels, both in decimal or in hexadecimal notation,
+as a list of integers (that are ORed internally),
+or as a list of the names that are shown between parentheses, such that
+.LP
+.nf
+ loglevel 129
+ loglevel 0x81
+ loglevel 128 1
+ loglevel 0x80 0x1
+ loglevel acl trace
+.fi
+.LP
+are equivalent.
+The keyword
+.B any
+can be used as a shortcut to enable logging at all levels (equivalent to \-1).
+The keyword
+.BR none ,
+or the equivalent integer representation, causes those messages
+that are logged regardless of the configured loglevel to be logged.
+In fact, if loglevel is set to 0, no logging occurs,
+so at least the
+.B none
+level is required to have high priority messages logged.
+
+Note that the
+.BR packets ,
+.BR BER ,
+and
+.B parse
+levels are only available as debug output on stderr, and are not
+sent to syslog.
+
+The loglevel defaults to \fBstats\fP.
+This level should usually also be included when using other loglevels, to
+help analyze the logs.
+.RE
+.TP
+.B maxfilterdepth <integer>
+Specify the maximum depth of nested filters in search requests.
+The default is 1000.
+.TP
+.B moduleload <filename> [<arguments>...]
+Specify the name of a dynamically loadable module to load and any
+additional arguments if supported by the module. The filename
+may be an absolute path name or a simple filename. Non-absolute names
+are searched for in the directories specified by the
+.B modulepath
+option. This option and the
+.B modulepath
+option are only usable if slapd was compiled with \-\-enable\-modules.
+.TP
+.B modulepath <pathspec>
+Specify a list of directories to search for loadable modules. Typically
+the path is colon-separated but this depends on the operating system.
+The default is MODULEDIR, which is where the standard OpenLDAP install
+will place its modules.
+.HP
+.hy 0
+.B objectclass "(\ <oid>\
+ [NAME\ <name>]\
+ [DESC\ <description>]\
+ [OBSOLETE]\
+ [SUP\ <oids>]\
+ [{ ABSTRACT | STRUCTURAL | AUXILIARY }]\
+ [MUST\ <oids>] [MAY\ <oids>] )"
+.RS
+Specify an objectclass using the LDAPv3 syntax defined in RFC 4512.
+The slapd parser extends the RFC 4512 definition by allowing string
+forms as well as numeric OIDs to be used for the object class OID.
+(See the
+.B
+objectidentifier
+description.) Object classes are "STRUCTURAL" by default.
+.RE
+.TP
+.B objectidentifier <name> "{ <oid> | <name>[:<suffix>] }"
+Define a string name that equates to the given OID. The string can be used
+in place of the numeric OID in objectclass and attribute definitions. The
+name can also be used with a suffix of the form ":xx" in which case the
+value "oid.xx" will be used.
+.TP
+.B password\-hash <hash> [<hash>...]
+This option configures one or more hashes to be used in generation of user
+passwords stored in the userPassword attribute during processing of
+LDAP Password Modify Extended Operations (RFC 3062).
+The <hash> must be one of
+.BR {SSHA} ,
+.BR {SHA} ,
+.BR {SMD5} ,
+.BR {MD5} ,
+.BR {CRYPT} ,
+and
+.BR {CLEARTEXT} .
+The default is
+.BR {SSHA} .
+
+.B {SHA}
+and
+.B {SSHA}
+use the SHA-1 algorithm (FIPS 160-1), the latter with a seed.
+
+.B {MD5}
+and
+.B {SMD5}
+use the MD5 algorithm (RFC 1321), the latter with a seed.
+
+.B {CRYPT}
+uses the
+.BR crypt (3).
+
+.B {CLEARTEXT}
+indicates that the new password should be
+added to userPassword as clear text.
+
+Note that this option does not alter the normal user applications
+handling of userPassword during LDAP Add, Modify, or other LDAP operations.
+.TP
+.B password\-crypt\-salt\-format <format>
+Specify the format of the salt passed to
+.BR crypt (3)
+when generating {CRYPT} passwords (see
+.BR password\-hash )
+during processing of LDAP Password Modify Extended Operations (RFC 3062).
+
+This string needs to be in
+.BR sprintf (3)
+format and may include one (and only one) %s conversion.
+This conversion will be substituted with a string of random
+characters from [A\-Za\-z0\-9./]. For example, "%.2s"
+provides a two character salt and "$1$%.8s" tells some
+versions of crypt(3) to use an MD5 algorithm and provides
+8 random characters of salt. The default is "%s", which
+provides 31 characters of salt.
+.TP
+.B pidfile <filename>
+The (absolute) name of a file that will hold the
+.B slapd
+server's process ID (see
+.BR getpid (2)).
+.TP
+.B pluginlog: <filename>
+The ( absolute ) name of a file that will contain log
+messages from
+.B SLAPI
+plugins. See
+.BR slapd.plugin (5)
+for details.
+.TP
+.B referral <url>
+Specify the referral to pass back when
+.BR slapd (8)
+cannot find a local database to handle a request.
+If specified multiple times, each url is provided.
+.TP
+.B require <conditions>
+Specify a set of conditions (separated by white space) to
+require (default none).
+The directive may be specified globally and/or per-database;
+databases inherit global conditions, so per-database specifications
+are additive.
+.B bind
+requires bind operation prior to directory operations.
+.B LDAPv3
+requires session to be using LDAP version 3.
+.B authc
+requires authentication prior to directory operations.
+.B SASL
+requires SASL authentication prior to directory operations.
+.B strong
+requires strong authentication prior to directory operations.
+The strong keyword allows protected "simple" authentication
+as well as SASL authentication.
+.B none
+may be used to require no conditions (useful to clear out globally
+set conditions within a particular database); it must occur first
+in the list of conditions.
+.TP
+.B reverse\-lookup on | off
+Enable/disable client name unverified reverse lookup (default is
+.BR off
+if compiled with \-\-enable\-rlookups).
+.TP
+.B rootDSE <file>
+Specify the name of an LDIF(5) file containing user defined attributes
+for the root DSE. These attributes are returned in addition to the
+attributes normally produced by slapd.
+
+The root DSE is an entry with information about the server and its
+capabilities, in operational attributes.
+It has the empty DN, and can be read with e.g.:
+.ti +4
+ldapsearch \-x \-b "" \-s base "+"
+.br
+See RFC 4512 section 5.1 for details.
+.TP
+.B sasl\-auxprops <plugin> [...]
+Specify which auxprop plugins to use for authentication lookups. The
+default is empty, which just uses slapd's internal support. Usually
+no other auxprop plugins are needed.
+.TP
+.B sasl\-auxprops\-dontusecopy <attr> [...]
+Specify which attribute(s) should be subject to the don't use copy control. This
+is necessary for some SASL mechanisms such as OTP to work in a replicated
+environment. The attribute "cmusaslsecretOTP" is the default value.
+.TP
+.B sasl\-auxprops\-dontusecopy\-ignore on | off
+Used to disable replication of the attribute(s) defined by
+sasl-auxprops-dontusecopy and instead use a local value for the attribute. This
+allows the SASL mechanism to continue to work if the provider is offline. This can
+cause replication inconsistency. Defaults to off.
+.TP
+.B sasl\-host <fqdn>
+Used to specify the fully qualified domain name used for SASL processing.
+.TP
+.B sasl\-realm <realm>
+Specify SASL realm. Default is empty.
+.TP
+.B sasl\-cbinding none | tls-unique | tls-endpoint
+Specify the channel-binding type, see also LDAP_OPT_X_SASL_CBINDING.
+Default is none.
+.TP
+.B sasl\-secprops <properties>
+Used to specify Cyrus SASL security properties.
+The
+.B none
+flag (without any other properties) causes the flag properties
+default, "noanonymous,noplain", to be cleared.
+The
+.B noplain
+flag disables mechanisms susceptible to simple passive attacks.
+The
+.B noactive
+flag disables mechanisms susceptible to active attacks.
+The
+.B nodict
+flag disables mechanisms susceptible to passive dictionary attacks.
+The
+.B noanonymous
+flag disables mechanisms which support anonymous login.
+The
+.B forwardsec
+flag require forward secrecy between sessions.
+The
+.B passcred
+require mechanisms which pass client credentials (and allow
+mechanisms which can pass credentials to do so).
+The
+.B minssf=<factor>
+property specifies the minimum acceptable
+.I security strength factor
+as an integer approximate to effective key length used for
+encryption. 0 (zero) implies no protection, 1 implies integrity
+protection only, 128 allows RC4, Blowfish and other similar ciphers,
+256 will require modern ciphers. The default is 0.
+The
+.B maxssf=<factor>
+property specifies the maximum acceptable
+.I security strength factor
+as an integer (see minssf description). The default is INT_MAX.
+The
+.B maxbufsize=<size>
+property specifies the maximum security layer receive buffer
+size allowed. 0 disables security layers. The default is 65536.
+.TP
+.B schemadn <dn>
+Specify the distinguished name for the subschema subentry that
+controls the entries on this server. The default is "cn=Subschema".
+.TP
+.B security <factors>
+Specify a set of security strength factors (separated by white space)
+to require (see
+.BR sasl\-secprops 's
+.B minssf
+option for a description of security strength factors).
+The directive may be specified globally and/or per-database.
+.B ssf=<n>
+specifies the overall security strength factor.
+.B transport=<n>
+specifies the transport security strength factor.
+.B tls=<n>
+specifies the TLS security strength factor.
+.B sasl=<n>
+specifies the SASL security strength factor.
+.B update_ssf=<n>
+specifies the overall security strength factor to require for
+directory updates.
+.B update_transport=<n>
+specifies the transport security strength factor to require for
+directory updates.
+.B update_tls=<n>
+specifies the TLS security strength factor to require for
+directory updates.
+.B update_sasl=<n>
+specifies the SASL security strength factor to require for
+directory updates.
+.B simple_bind=<n>
+specifies the security strength factor required for
+.I simple
+username/password authentication.
+Note that the
+.B transport
+factor is measure of security provided by the underlying transport,
+e.g. ldapi:// (and eventually IPSEC). It is not normally used.
+.TP
+.B serverID <integer> [<URL>]
+Specify an integer ID from 0 to 4095 for this server. The ID may also be
+specified as a hexadecimal ID by prefixing the value with "0x".
+Non-zero IDs are required when using multi-provider replication and each
+provider must have a unique non-zero ID. Note that this requirement also
+applies to separate providers contributing to a glued set of databases.
+If the URL is provided, this directive may be specified
+multiple times, providing a complete list of participating servers
+and their IDs. The fully qualified hostname of each server should be
+used in the supplied URLs. The IDs are used in the "replica id" field
+of all CSNs generated by the specified server. The default value is zero, which
+is only valid for single provider replication.
+Example:
+.LP
+.nf
+ serverID 1 ldap://ldap1.example.com
+ serverID 2 ldap://ldap2.example.com
+.fi
+.TP
+.B sizelimit {<integer>|unlimited}
+.TP
+.B sizelimit size[.{soft|hard}]=<integer> [...]
+Specify the maximum number of entries to return from a search operation.
+The default size limit is 500.
+Use
+.B unlimited
+to specify no limits.
+The second format allows a fine grain setting of the size limits.
+If no special qualifiers are specified, both soft and hard limits are set.
+Extra args can be added on the same line.
+Additional qualifiers are available; see
+.BR limits
+for an explanation of all of the different flags.
+.TP
+.B sockbuf_max_incoming <integer>
+Specify the maximum incoming LDAP PDU size for anonymous sessions.
+The default is 262143.
+.TP
+.B sockbuf_max_incoming_auth <integer>
+Specify the maximum incoming LDAP PDU size for authenticated sessions.
+The default is 4194303.
+.TP
+.B sortvals <attr> [...]
+Specify a list of multi-valued attributes whose values will always
+be maintained in sorted order. Using this option will allow Modify,
+Compare, and filter evaluations on these attributes to be performed
+more efficiently. The resulting sort order depends on the
+attributes' syntax and matching rules and may not correspond to
+lexical order or any other recognizable order.
+.TP
+.B tcp-buffer [listener=<URL>] [{read|write}=]<size>
+Specify the size of the TCP buffer.
+A global value for both read and write TCP buffers related to any listener
+is defined, unless the listener is explicitly specified,
+or either the read or write qualifiers are used.
+See
+.BR tcp (7)
+for details.
+Note that some OS-es implement automatic TCP buffer tuning.
+.TP
+.B threads <integer>
+Specify the maximum size of the primary thread pool.
+The default is 16; the minimum value is 2.
+.TP
+.B threadqueues <integer>
+Specify the number of work queues to use for the primary thread pool.
+The default is 1 and this is typically adequate for up to 8 CPU cores.
+The value should not exceed the number of CPUs in the system.
+.TP
+.B timelimit {<integer>|unlimited}
+.TP
+.B timelimit time[.{soft|hard}]=<integer> [...]
+Specify the maximum number of seconds (in real time)
+.B slapd
+will spend answering a search request. The default time limit is 3600.
+Use
+.B unlimited
+to specify no limits.
+The second format allows a fine grain setting of the time limits.
+Extra args can be added on the same line. See
+.BR limits
+for an explanation of the different flags.
+.TP
+.B tool\-threads <integer>
+Specify the maximum number of threads to use in tool mode.
+This should not be greater than the number of CPUs in the system.
+The default is 1.
+.TP
+.B writetimeout <integer>
+Specify the number of seconds to wait before forcibly closing
+a connection with an outstanding write. This allows recovery from
+various network hang conditions. A writetimeout of 0 disables this
+feature. The default is 0.
+.SH TLS OPTIONS
+If
+.B slapd
+is built with support for Transport Layer Security, there are more options
+you can specify.
+.TP
+.B TLSCipherSuite <cipher-suite-spec>
+Permits configuring what ciphers will be accepted and the preference order.
+<cipher-suite-spec> should be a cipher specification for the TLS library
+in use (OpenSSL or GnuTLS).
+Example:
+.RS
+.RS
+.TP
+.I OpenSSL:
+TLSCipherSuite HIGH:MEDIUM:+SSLv2
+.TP
+.I GnuTLS:
+TLSCiphersuite SECURE256:!AES-128-CBC
+.RE
+
+To check what ciphers a given spec selects in OpenSSL, use:
+
+.nf
+ openssl ciphers \-v <cipher-suite-spec>
+.fi
+
+With GnuTLS the available specs can be found in the manual page of
+.BR gnutls\-cli (1)
+(see the description of the
+option
+.BR \-\-priority ).
+
+In older versions of GnuTLS, where gnutls\-cli does not support the option
+\-\-priority, you can obtain the \(em more limited \(em list of ciphers by calling:
+
+.nf
+ gnutls\-cli \-l
+.fi
+.RE
+.TP
+.B TLSCACertificateFile <filename>
+Specifies the file that contains certificates for all of the Certificate
+Authorities that
+.B slapd
+will recognize. The certificate for
+the CA that signed the server certificate must(GnuTLS)/may(OpenSSL) be included among
+these certificates. If the signing CA was not a top-level (root) CA,
+certificates for the entire sequence of CA's from the signing CA to
+the top-level CA should be present. Multiple certificates are simply
+appended to the file; the order is not significant.
+.TP
+.B TLSCACertificatePath <path>
+Specifies the path of a directory that contains Certificate Authority
+certificates in separate individual files. Usually only one of this
+or the TLSCACertificateFile is used. If both are specified, both
+locations will be used.
+.TP
+.B TLSCertificateFile <filename>
+Specifies the file that contains the
+.B slapd
+server certificate.
+
+When using OpenSSL that file may also contain any number of intermediate
+certificates after the server certificate.
+.TP
+.B TLSCertificateKeyFile <filename>
+Specifies the file that contains the
+.B slapd
+server private key that matches the certificate stored in the
+.B TLSCertificateFile
+file. Currently, the private key must not be protected with a password, so
+it is of critical importance that it is protected carefully.
+.TP
+.B TLSDHParamFile <filename>
+This directive specifies the file that contains parameters for Diffie-Hellman
+ephemeral key exchange. This is required in order to use a DSA certificate on
+the server, or an RSA certificate missing the "key encipherment" key usage.
+Note that setting this option may also enable
+Anonymous Diffie-Hellman key exchanges in certain non-default cipher suites.
+Anonymous key exchanges should generally be avoided since they provide no
+actual client or server authentication and provide no protection against
+man-in-the-middle attacks.
+You should append "!ADH" to your cipher suites to ensure that these suites
+are not used.
+.TP
+.B TLSECName <name>
+Specify the name of the curve(s) to use for Elliptic curve Diffie-Hellman
+ephemeral key exchange. This option is only used for OpenSSL.
+This option is not used with GnuTLS; the curves may be
+chosen in the GnuTLS ciphersuite specification.
+.TP
+.B TLSProtocolMin <major>[.<minor>]
+Specifies minimum SSL/TLS protocol version that will be negotiated.
+If the server doesn't support at least that version,
+the SSL handshake will fail.
+To require TLS 1.x or higher, set this option to 3.(x+1),
+e.g.,
+
+.nf
+ TLSProtocolMin 3.2
+.fi
+
+would require TLS 1.1.
+Specifying a minimum that is higher than that supported by the
+OpenLDAP implementation will result in it requiring the
+highest level that it does support.
+This directive is ignored with GnuTLS.
+.TP
+.B TLSRandFile <filename>
+Specifies the file to obtain random bits from when /dev/[u]random
+is not available. Generally set to the name of the EGD/PRNGD socket.
+The environment variable RANDFILE can also be used to specify the filename.
+This directive is ignored with GnuTLS.
+.TP
+.B TLSVerifyClient <level>
+Specifies what checks to perform on client certificates in an
+incoming TLS session, if any.
+The
+.B <level>
+can be specified as one of the following keywords:
+.RS
+.TP
+.B never
+This is the default.
+.B slapd
+will not ask the client for a certificate.
+.TP
+.B allow
+The client certificate is requested. If no certificate is provided,
+the session proceeds normally. If a bad certificate is provided,
+it will be ignored and the session proceeds normally.
+.TP
+.B try
+The client certificate is requested. If no certificate is provided,
+the session proceeds normally. If a bad certificate is provided,
+the session is immediately terminated.
+.TP
+.B demand | hard | true
+These keywords are all equivalent, for compatibility reasons.
+The client certificate is requested. If no certificate is provided,
+or a bad certificate is provided, the session is immediately terminated.
+
+Note that a valid client certificate is required in order to use the
+SASL EXTERNAL authentication mechanism with a TLS session. As such,
+a non-default
+.B TLSVerifyClient
+setting must be chosen to enable SASL EXTERNAL authentication.
+.RE
+.TP
+.B TLSCRLCheck <level>
+Specifies if the Certificate Revocation List (CRL) of the CA should be
+used to verify if the client certificates have not been revoked. This
+requires
+.B TLSCACertificatePath
+parameter to be set. This directive is ignored with GnuTLS.
+.B <level>
+can be specified as one of the following keywords:
+.RS
+.TP
+.B none
+No CRL checks are performed
+.TP
+.B peer
+Check the CRL of the peer certificate
+.TP
+.B all
+Check the CRL for a whole certificate chain
+.RE
+.TP
+.B TLSCRLFile <filename>
+Specifies a file containing a Certificate Revocation List to be used
+for verifying that certificates have not been revoked. This directive is
+only valid when using GnuTLS.
+.SH GENERAL BACKEND OPTIONS
+Options in this section only apply to the configuration file section
+of all instances of the specified backend. All backends may support
+this class of options, but currently only back-mdb does.
+.TP
+.B backend <databasetype>
+Mark the beginning of a backend definition. <databasetype>
+should be one of
+.BR asyncmeta ,
+.BR config ,
+.BR dnssrv ,
+.BR ldap ,
+.BR ldif ,
+.BR mdb ,
+.BR meta ,
+.BR monitor ,
+.BR ndb ,
+.BR null ,
+.BR passwd ,
+.BR perl ,
+.BR relay ,
+.BR sock ,
+.BR sql ,
+or
+.BR wt .
+At present, only back-mdb implements any options of this type, so this
+setting is not needed for any other backends.
+
+.SH GENERAL DATABASE OPTIONS
+Options in this section only apply to the configuration file section
+for the database in which they are defined. They are supported by every
+type of backend. Note that the
+.B database
+and at least one
+.B suffix
+option are mandatory for each database.
+.TP
+.B database <databasetype>
+Mark the beginning of a new database instance definition. <databasetype>
+should be one of
+.BR asyncmeta ,
+.BR config ,
+.BR dnssrv ,
+.BR ldap ,
+.BR ldif ,
+.BR mdb ,
+.BR meta ,
+.BR monitor ,
+.BR ndb ,
+.BR null ,
+.BR passwd ,
+.BR perl ,
+.BR relay ,
+.BR sock ,
+.BR sql ,
+or
+.BR wt ,
+depending on which backend will serve the database.
+
+LDAP operations, even subtree searches, normally access only one
+database.
+That can be changed by gluing databases together with the
+.B subordinate
+keyword.
+Access controls and some overlays can also involve multiple databases.
+.TP
+.B add_content_acl on | off
+Controls whether Add operations will perform ACL checks on
+the content of the entry being added. This check is off
+by default. See the
+.BR slapd.access (5)
+manual page for more details on ACL requirements for
+Add operations.
+.TP
+.B extra_attrs <attrlist>
+Lists what attributes need to be added to search requests.
+Local storage backends return the entire entry to the frontend.
+The frontend takes care of only returning the requested attributes
+that are allowed by ACLs.
+However, features like access checking and so may need specific
+attributes that are not automatically returned by remote storage
+backends, like proxy backends and so on.
+.B <attrlist>
+is a list of attributes that are needed for internal purposes
+and thus always need to be collected, even when not explicitly
+requested by clients.
+.TP
+.B hidden on | off
+Controls whether the database will be used to answer
+queries. A database that is hidden will never be
+selected to answer any queries, and any suffix configured
+on the database will be ignored in checks for conflicts
+with other databases. By default, hidden is off.
+.TP
+.B lastmod on | off
+Controls whether
+.B slapd
+will automatically maintain the
+modifiersName, modifyTimestamp, creatorsName, and
+createTimestamp attributes for entries. It also controls
+the entryCSN and entryUUID attributes, which are needed
+by the syncrepl provider. By default, lastmod is on.
+.TP
+.B lastbind on | off
+Controls whether
+.B slapd
+will automatically maintain the pwdLastSuccess attribute for
+entries. By default, lastbind is off.
+.TP
+.B limits <selector> <limit> [<limit> [...]]
+Specify time and size limits based on the operation's initiator or
+base DN.
+The argument
+.B <selector>
+can be any of
+.RS
+.RS
+.TP
+anonymous | users | [<dnspec>=]<pattern> | group[/oc[/at]]=<pattern>
+
+.RE
+with
+.RS
+.TP
+<dnspec> ::= dn[.<type>][.<style>]
+.TP
+<type> ::= self | this
+.TP
+<style> ::= exact | base | onelevel | subtree | children | regex | anonymous
+
+.RE
+DN type
+.B self
+is the default and means the bound user, while
+.B this
+means the base DN of the operation.
+The term
+.B anonymous
+matches all unauthenticated clients.
+The term
+.B users
+matches all authenticated clients;
+otherwise an
+.B exact
+dn pattern is assumed unless otherwise specified by qualifying
+the (optional) key string
+.B dn
+with
+.B exact
+or
+.B base
+(which are synonyms), to require an exact match; with
+.BR onelevel ,
+to require exactly one level of depth match; with
+.BR subtree ,
+to allow any level of depth match, including the exact match; with
+.BR children ,
+to allow any level of depth match, not including the exact match;
+.BR regex
+explicitly requires the (default) match based on POSIX (''extended'')
+regular expression pattern.
+Finally,
+.B anonymous
+matches unbound operations; the
+.B pattern
+field is ignored.
+The same behavior is obtained by using the
+.B anonymous
+form of the
+.B <selector>
+clause.
+The term
+.BR group ,
+with the optional objectClass
+.B oc
+and attributeType
+.B at
+fields, followed by
+.BR pattern ,
+sets the limits for any DN listed in the values of the
+.B at
+attribute (default
+.BR member )
+of the
+.B oc
+group objectClass (default
+.BR groupOfNames )
+whose DN exactly matches
+.BR pattern .
+
+The currently supported limits are
+.B size
+and
+.BR time .
+
+The syntax for time limits is
+.BR time[.{soft|hard}]=<integer> ,
+where
+.I integer
+is the number of seconds slapd will spend answering a search request.
+If no time limit is explicitly requested by the client, the
+.BR soft
+limit is used; if the requested time limit exceeds the
+.BR hard
+.\"limit, an
+.\".I "Administrative limit exceeded"
+.\"error is returned.
+limit, the value of the limit is used instead.
+If the
+.BR hard
+limit is set to the keyword
+.IR soft ,
+the soft limit is used in either case; if it is set to the keyword
+.IR unlimited ,
+no hard limit is enforced.
+Explicit requests for time limits smaller or equal to the
+.BR hard
+limit are honored.
+If no limit specifier is set, the value is assigned to the
+.BR soft
+limit, and the
+.BR hard
+limit is set to
+.IR soft ,
+to preserve the original behavior.
+
+The syntax for size limits is
+.BR size[.{soft|hard|unchecked}]=<integer> ,
+where
+.I integer
+is the maximum number of entries slapd will return answering a search
+request.
+If no size limit is explicitly requested by the client, the
+.BR soft
+limit is used; if the requested size limit exceeds the
+.BR hard
+.\"limit, an
+.\".I "Administrative limit exceeded"
+.\"error is returned.
+limit, the value of the limit is used instead.
+If the
+.BR hard
+limit is set to the keyword
+.IR soft ,
+the soft limit is used in either case; if it is set to the keyword
+.IR unlimited ,
+no hard limit is enforced.
+Explicit requests for size limits smaller or equal to the
+.BR hard
+limit are honored.
+The
+.BR unchecked
+specifier sets a limit on the number of candidates a search request is allowed
+to examine.
+The rationale behind it is that searches for non-properly indexed
+attributes may result in large sets of candidates, which must be
+examined by
+.BR slapd (8)
+to determine whether they match the search filter or not.
+The
+.B unchecked
+limit provides a means to drop such operations before they are even
+started.
+If the selected candidates exceed the
+.BR unchecked
+limit, the search will abort with
+.IR "Unwilling to perform" .
+If it is set to the keyword
+.IR unlimited ,
+no limit is applied (the default).
+If it is set to
+.IR disabled ,
+the search is not even performed; this can be used to disallow searches
+for a specific set of users.
+If no limit specifier is set, the value is assigned to the
+.BR soft
+limit, and the
+.BR hard
+limit is set to
+.IR soft ,
+to preserve the original behavior.
+
+In case of no match, the global limits are used.
+The default values are the same as for
+.B sizelimit
+and
+.BR timelimit ;
+no limit is set on
+.BR unchecked .
+
+If
+.B pagedResults
+control is requested, the
+.B hard
+size limit is used by default, because the request of a specific page size
+is considered an explicit request for a limitation on the number
+of entries to be returned.
+However, the size limit applies to the total count of entries returned within
+the search, and not to a single page.
+Additional size limits may be enforced; the syntax is
+.BR size.pr={<integer>|noEstimate|unlimited} ,
+where
+.I integer
+is the max page size if no explicit limit is set; the keyword
+.I noEstimate
+inhibits the server from returning an estimate of the total number
+of entries that might be returned
+(note: the current implementation does not return any estimate).
+The keyword
+.I unlimited
+indicates that no limit is applied to the pagedResults control page size.
+The syntax
+.B size.prtotal={<integer>|hard|unlimited|disabled}
+allows one to set a limit on the total number of entries that the pagedResults
+control will return.
+By default it is set to the
+.B hard
+limit which will use the size.hard value.
+When set,
+.I integer
+is the max number of entries that the whole search with pagedResults control
+can return.
+Use
+.I unlimited
+to allow unlimited number of entries to be returned, e.g. to allow
+the use of the pagedResults control as a means to circumvent size
+limitations on regular searches; the keyword
+.I disabled
+disables the control, i.e. no paged results can be returned.
+Note that the total number of entries returned when the pagedResults control
+is requested cannot exceed the
+.B hard
+size limit of regular searches unless extended by the
+.B prtotal
+switch.
+
+The \fBlimits\fP statement is typically used to let an unlimited
+number of entries be returned by searches performed
+with the identity used by the consumer for synchronization purposes
+by means of the RFC 4533 LDAP Content Synchronization protocol
+(see \fBsyncrepl\fP for details).
+
+When using subordinate databases, it is necessary for any limits that
+are to be applied across the parent and its subordinates to be defined in
+both the parent and its subordinates. Otherwise the settings on the
+subordinate databases are not honored.
+.RE
+.TP
+.B maxderefdepth <depth>
+Specifies the maximum number of aliases to dereference when trying to
+resolve an entry, used to avoid infinite alias loops. The default is 15.
+.TP
+.B multiprovider on | off
+This option puts a consumer database into Multi-Provider mode. Update
+operations will be accepted from any user, not just the updatedn. The
+database must already be configured as a syncrepl consumer
+before this keyword may be set. This mode also requires a
+.B serverID
+(see above) to be configured.
+By default, multiprovider is off.
+.TP
+.B monitoring on | off
+This option enables database-specific monitoring in the entry related
+to the current database in the "cn=Databases,cn=Monitor" subtree
+of the monitor database, if the monitor database is enabled.
+Currently, only the MDB database provides database-specific monitoring.
+If monitoring is supported by the backend it defaults to on, otherwise
+off.
+.TP
+.B overlay <overlay-name>
+Add the specified overlay to this database. An overlay is a piece of
+code that intercepts database operations in order to extend or change
+them. Overlays are pushed onto
+a stack over the database, and so they will execute in the reverse
+of the order in which they were configured and the database itself
+will receive control last of all. See the
+.BR slapd.overlays (5)
+manual page for an overview of the available overlays.
+Note that all of the database's
+regular settings should be configured before any overlay settings.
+.TP
+.B readonly on | off
+This option puts the database into "read-only" mode. Any attempts to
+modify the database will return an "unwilling to perform" error. By
+default, readonly is off.
+.TP
+.B restrict <oplist>
+Specify a whitespace separated list of operations that are restricted.
+If defined inside a database specification, restrictions apply only
+to that database, otherwise they are global.
+Operations can be any of
+.BR add ,
+.BR bind ,
+.BR compare ,
+.BR delete ,
+.BR extended[=<OID>] ,
+.BR modify ,
+.BR rename ,
+.BR search ,
+or the special pseudo-operations
+.B read
+and
+.BR write ,
+which respectively summarize read and write operations.
+The use of
+.I restrict write
+is equivalent to
+.I readonly on
+(see above).
+The
+.B extended
+keyword allows one to indicate the OID of the specific operation
+to be restricted.
+.TP
+.B rootdn <dn>
+Specify the distinguished name that is not subject to access control
+or administrative limit restrictions for operations on this database.
+This DN may or may not be associated with an entry. An empty root
+DN (the default) specifies no root access is to be granted. It is
+recommended that the rootdn only be specified when needed (such as
+when initially populating a database). If the rootdn is within
+a namingContext (suffix) of the database, a simple bind password
+may also be provided using the
+.B rootpw
+directive. Many optional features, including syncrepl, require the
+rootdn to be defined for the database.
+.TP
+.B rootpw <password>
+Specify a password (or hash of the password) for the rootdn. The
+password can only be set if the rootdn is within the namingContext
+(suffix) of the database.
+This option accepts all RFC 2307 userPassword formats known to
+the server (see
+.B password\-hash
+description) as well as cleartext.
+.BR slappasswd (8)
+may be used to generate a hash of a password. Cleartext
+and \fB{CRYPT}\fP passwords are not recommended. If empty
+(the default), authentication of the root DN is by other means
+(e.g. SASL). Use of SASL is encouraged.
+.TP
+.B suffix <dn suffix>
+Specify the DN suffix of queries that will be passed to this
+backend database. Multiple suffix lines can be given and at least one is
+required for each database definition.
+
+If the suffix of one database is "inside" that of another, the database
+with the inner suffix must come first in the configuration file.
+You may also want to glue such databases together with the
+.B subordinate
+keyword.
+.TP
+.B subordinate [advertise]
+Specify that the current backend database is a subordinate of another
+backend database. A subordinate database may have only one suffix. This
+option may be used to glue multiple databases into a single namingContext.
+If the suffix of the current database is within the namingContext of a
+superior database, searches against the superior database will be
+propagated to the subordinate as well. All of the databases
+associated with a single namingContext should have identical rootdns.
+Behavior of other LDAP operations is unaffected by this setting. In
+particular, it is not possible to use moddn to move an entry from
+one subordinate to another subordinate within the namingContext.
+
+If the optional \fBadvertise\fP flag is supplied, the naming context of
+this database is advertised in the root DSE. The default is to hide this
+database context, so that only the superior context is visible.
+
+If the slap tools
+.BR slapcat (8),
+.BR slapadd (8),
+.BR slapmodify (8),
+or
+.BR slapindex (8)
+are used on the superior database, any glued subordinates that support
+these tools are opened as well.
+
+Databases that are glued together should usually be configured with the
+same indices (assuming they support indexing), even for attributes that
+only exist in some of these databases. In general, all of the glued
+databases should be configured as similarly as possible, since the intent
+is to provide the appearance of a single directory.
+
+Note that the \fIsubordinate\fP functionality is implemented internally
+by the \fIglue\fP overlay and as such its behavior will interact with other
+overlays in use. By default, the glue overlay is automatically configured as
+the last overlay on the superior backend. Its position on the backend
+can be explicitly configured by setting an \fBoverlay glue\fP directive
+at the desired position. This explicit configuration is necessary e.g.
+when using the \fIsyncprov\fP overlay, which needs to follow \fIglue\fP
+in order to work over all of the glued databases. E.g.
+.RS
+.nf
+ database mdb
+ suffix dc=example,dc=com
+ ...
+ overlay glue
+ overlay syncprov
+.fi
+.RE
+.TP
+.B sync_use_subentry
+Store the syncrepl contextCSN in a subentry instead of the context entry
+of the database. The subentry's RDN will be "cn=ldapsync". By default
+the contextCSN is stored in the context entry.
+.HP
+.hy 0
+.B syncrepl rid=<replica ID>
+.B provider=ldap[s]://<hostname>[:port]
+.B searchbase=<base DN>
+.B [type=refreshOnly|refreshAndPersist]
+.B [interval=dd:hh:mm:ss]
+.B [retry=[<retry interval> <# of retries>]+]
+.B [filter=<filter str>]
+.B [scope=sub|one|base|subord]
+.B [attrs=<attr list>]
+.B [exattrs=<attr list>]
+.B [attrsonly]
+.B [sizelimit=<limit>]
+.B [timelimit=<limit>]
+.B [schemachecking=on|off]
+.B [network\-timeout=<seconds>]
+.B [timeout=<seconds>]
+.B [tcp\-user\-timeout=<milliseconds>]
+.B [bindmethod=simple|sasl]
+.B [binddn=<dn>]
+.B [saslmech=<mech>]
+.B [authcid=<identity>]
+.B [authzid=<identity>]
+.B [credentials=<passwd>]
+.B [realm=<realm>]
+.B [secprops=<properties>]
+.B [keepalive=<idle>:<probes>:<interval>]
+.B [starttls=yes|critical]
+.B [tls_cert=<file>]
+.B [tls_key=<file>]
+.B [tls_cacert=<file>]
+.B [tls_cacertdir=<path>]
+.B [tls_reqcert=never|allow|try|demand]
+.B [tls_reqsan=never|allow|try|demand]
+.B [tls_cipher_suite=<ciphers>]
+.B [tls_ecname=<names>]
+.B [tls_crlcheck=none|peer|all]
+.B [tls_protocol_min=<major>[.<minor>]]
+.B [suffixmassage=<real DN>]
+.B [logbase=<base DN>]
+.B [logfilter=<filter str>]
+.B [syncdata=default|accesslog|changelog]
+.B [lazycommit]
+.RS
+Specify the current database as a consumer which is kept up-to-date with the
+provider content by establishing the current
+.BR slapd (8)
+as a replication consumer site running a
+.B syncrepl
+replication engine.
+The consumer content is kept synchronized to the provider content using
+the LDAP Content Synchronization protocol. Refer to the
+"OpenLDAP Administrator's Guide" for detailed information on
+setting up a replicated
+.B slapd
+directory service using the
+.B syncrepl
+replication engine.
+
+.B rid
+identifies the current
+.B syncrepl
+directive within the replication consumer site.
+It is a non-negative integer not greater than 999 (limited
+to three decimal digits).
+
+.B provider
+specifies the replication provider site containing the provider content
+as an LDAP URI. If <port> is not given, the standard LDAP port number
+(389 or 636) is used.
+
+The content of the
+.B syncrepl
+consumer is defined using a search
+specification as its result set. The consumer
+.B slapd
+will send search requests to the provider
+.B slapd
+according to the search specification. The search specification includes
+.BR searchbase ", " scope ", " filter ", " attrs ", " attrsonly ", " sizelimit ", "
+and
+.B timelimit
+parameters as in the normal search specification. The
+.B exattrs
+option may also be used to specify attributes that should be omitted
+from incoming entries.
+The \fBscope\fP defaults to \fBsub\fP, the \fBfilter\fP defaults to
+\fB(objectclass=*)\fP, and there is no default \fBsearchbase\fP. The
+\fBattrs\fP list defaults to \fB"*,+"\fP to return all user and operational
+attributes, and \fBattrsonly\fP and \fBexattrs\fP are unset by default.
+The \fBsizelimit\fP and \fBtimelimit\fP only
+accept "unlimited" and positive integers, and both default to "unlimited".
+The \fBsizelimit\fP and \fBtimelimit\fP parameters define
+a consumer requested limitation on the number of entries that can be returned
+by the LDAP Content Synchronization operation; these should be left unchanged
+from the default otherwise replication may never succeed.
+Note, however, that any provider-side limits for the replication identity
+will be enforced by the provider regardless of the limits requested
+by the LDAP Content Synchronization operation, much like for any other
+search operation.
+
+The LDAP Content Synchronization protocol has two operation types.
+In the
+.B refreshOnly
+operation, the next synchronization search operation
+is periodically rescheduled at an interval time (specified by
+.B interval
+parameter; 1 day by default)
+after each synchronization operation finishes.
+In the
+.B refreshAndPersist
+operation, a synchronization search remains persistent in the provider slapd.
+Further updates to the provider will generate
+.B searchResultEntry
+to the consumer slapd as the search responses to the persistent
+synchronization search. If the initial search fails due to an error, the
+next synchronization search operation is periodically rescheduled at an
+interval time (specified by
+.B interval
+parameter; 1 day by default)
+
+If an error occurs during replication, the consumer will attempt to
+reconnect according to the
+.B retry
+parameter which is a list of the <retry interval> and <# of retries> pairs.
+For example, retry="60 10 300 3" lets the consumer retry every 60 seconds
+for the first 10 times and then retry every 300 seconds for the next 3
+times before stop retrying. The `+' in <# of retries> means indefinite
+number of retries until success.
+If no
+.B retry
+is specified, by default syncrepl retries every hour forever.
+
+The schema checking can be enforced at the LDAP Sync
+consumer site by turning on the
+.B schemachecking
+parameter. The default is \fBoff\fP.
+Schema checking \fBon\fP means that replicated entries must have
+a structural objectClass, must obey to objectClass requirements
+in terms of required/allowed attributes, and that naming attributes
+and distinguished values must be present.
+As a consequence, schema checking should be \fBoff\fP when partial
+replication is used.
+
+The
+.B network\-timeout
+parameter sets how long the consumer will wait to establish a
+network connection to the provider. Once a connection is
+established, the
+.B timeout
+parameter determines how long the consumer will wait for the initial
+Bind request to complete. The defaults for these parameters come
+from
+.BR ldap.conf (5).
+The
+.B tcp\-user\-timeout
+parameter, if non-zero, corresponds to the
+.B TCP_USER_TIMEOUT
+set on the target connections, overriding the operating system setting.
+Only some systems support the customization of this parameter, it is
+ignored otherwise and system-wide settings are used.
+
+A
+.B bindmethod
+of
+.B simple
+requires the options
+.B binddn
+and
+.B credentials
+and should only be used when adequate security services
+(e.g. TLS or IPSEC) are in place.
+.B REMEMBER: simple bind credentials must be in cleartext!
+A
+.B bindmethod
+of
+.B sasl
+requires the option
+.B saslmech.
+Depending on the mechanism, an authentication identity and/or
+credentials can be specified using
+.B authcid
+and
+.B credentials.
+The
+.B authzid
+parameter may be used to specify an authorization identity.
+Specific security properties (as with the
+.B sasl\-secprops
+keyword above) for a SASL bind can be set with the
+.B secprops
+option. A non default SASL realm can be set with the
+.B realm
+option.
+The identity used for synchronization by the consumer should be allowed
+to receive an unlimited number of entries in response to a search request.
+The provider, other than allowing authentication of the syncrepl identity,
+should grant that identity appropriate access privileges to the data
+that is being replicated (\fBaccess\fP directive), and appropriate time
+and size limits.
+This can be accomplished by either allowing unlimited \fBsizelimit\fP
+and \fBtimelimit\fP, or by setting an appropriate \fBlimits\fP statement
+in the consumer's configuration (see \fBsizelimit\fP and \fBlimits\fP
+for details).
+
+The
+.B keepalive
+parameter sets the values of \fIidle\fP, \fIprobes\fP, and \fIinterval\fP
+used to check whether a socket is alive;
+.I idle
+is the number of seconds a connection needs to remain idle before TCP
+starts sending keepalive probes;
+.I probes
+is the maximum number of keepalive probes TCP should send before dropping
+the connection;
+.I interval
+is interval in seconds between individual keepalive probes.
+Only some systems support the customization of these values;
+the
+.B keepalive
+parameter is ignored otherwise, and system-wide settings are used.
+
+The
+.B starttls
+parameter specifies use of the StartTLS extended operation
+to establish a TLS session before Binding to the provider. If the
+.B critical
+argument is supplied, the session will be aborted if the StartTLS request
+fails. Otherwise the syncrepl session continues without TLS. The
+.B tls_reqcert
+setting defaults to "demand", the
+.B tls_reqsan
+setting defaults to "allow", and the other TLS settings
+default to the same as the main slapd TLS settings.
+
+The
+.B suffixmassage
+parameter allows the consumer to pull entries from a remote directory
+whose DN suffix differs from the local directory. The portion of the
+remote entries' DNs that matches the \fIsearchbase\fP will be replaced
+with the suffixmassage DN.
+
+Rather than replicating whole entries, the consumer can query logs of
+data modifications. This mode of operation is referred to as \fIdelta
+syncrepl\fP. In addition to the above parameters, the
+.B logbase
+and
+.B logfilter
+parameters must be set appropriately for the log that will be used. The
+.B syncdata
+parameter must be set to either "accesslog" if the log conforms to the
+.BR slapo\-accesslog (5)
+log format, or "changelog" if the log conforms
+to the obsolete \fIchangelog\fP format. If the
+.B syncdata
+parameter is omitted or set to "default" then the log parameters are
+ignored.
+
+The
+.B lazycommit
+parameter tells the underlying database that it can store changes without
+performing a full flush after each change. This may improve performance
+for the consumer, while sacrificing safety or durability.
+.RE
+.TP
+.B updatedn <dn>
+This option is only applicable in a replica
+database.
+It specifies the DN permitted to update (subject to access controls)
+the replica. It is only needed in certain push-mode
+replication scenarios. Generally, this DN
+.I should not
+be the same as the
+.B rootdn
+used at the provider.
+.TP
+.B updateref <url>
+Specify the referral to pass back when
+.BR slapd (8)
+is asked to modify a replicated local database.
+If specified multiple times, each url is provided.
+
+.SH DATABASE-SPECIFIC OPTIONS
+Each database may allow specific configuration options; they are
+documented separately in the backends' manual pages. See the
+.BR slapd.backends (5)
+manual page for an overview of available backends.
+.SH EXAMPLES
+.LP
+Here is a short example of a configuration file:
+.LP
+.RS
+.nf
+include SYSCONFDIR/schema/core.schema
+pidfile LOCALSTATEDIR/run/slapd.pid
+
+# Subtypes of "name" (e.g. "cn" and "ou") with the
+# option ";x\-hidden" can be searched for/compared,
+# but are not shown. See \fBslapd.access\fP(5).
+attributeoptions x\-hidden lang\-
+access to attrs=name;x\-hidden by * =cs
+
+# Protect passwords. See \fBslapd.access\fP(5).
+access to attrs=userPassword by * auth
+# Read access to other attributes and entries.
+access to * by * read
+
+database mdb
+suffix "dc=our\-domain,dc=com"
+# The database directory MUST exist prior to
+# running slapd AND should only be accessible
+# by the slapd/tools. Mode 0700 recommended.
+directory LOCALSTATEDIR/openldap\-data
+# Indices to maintain
+index objectClass eq
+index cn,sn,mail pres,eq,approx,sub
+
+# We serve small clients that do not handle referrals,
+# so handle remote lookups on their behalf.
+database ldap
+suffix ""
+uri ldap://ldap.some\-server.com/
+lastmod off
+.fi
+.RE
+.LP
+"OpenLDAP Administrator's Guide" contains a longer annotated
+example of a configuration file.
+The original ETCDIR/slapd.conf is another example.
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR ldap (3),
+.BR gnutls\-cli (1),
+.BR slapd\-config (5),
+.BR slapd.access (5),
+.BR slapd.backends (5),
+.BR slapd.overlays (5),
+.BR slapd.plugin (5),
+.BR slapd (8),
+.BR slapacl (8),
+.BR slapadd (8),
+.BR slapauth (8),
+.BR slapcat (8),
+.BR slapdn (8),
+.BR slapindex (8),
+.BR slapmodify (8),
+.BR slappasswd (8),
+.BR slaptest (8).
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man5/slapd.overlays.5 b/doc/man/man5/slapd.overlays.5
new file mode 100644
index 0000000..307a28a
--- /dev/null
+++ b/doc/man/man5/slapd.overlays.5
@@ -0,0 +1,204 @@
+.TH SLAPD.OVERLAYS 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2006-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd.overlays \- overlays for slapd, the stand-alone LDAP daemon
+.SH DESCRIPTION
+The
+.BR slapd (8)
+daemon can use a variety of different overlays to alter or extend
+the normal behavior of a database backend.
+Overlays may be compiled statically into slapd, or when module support
+is enabled, they may be dynamically loaded. Most of the overlays
+are only allowed to be configured on individual databases, but some
+may also be configured globally.
+
+Configuration options for each overlay are documented separately in the
+corresponding
+.BR slapo\-<overlay> (5)
+manual pages.
+.TP
+.B accesslog
+Access Logging.
+This overlay can record accesses to a given backend database on another
+database.
+.TP
+.B auditlog
+Audit Logging.
+This overlay records changes on a given backend database to an LDIF log
+file.
+By default it is not built.
+.TP
+.B autoca
+Automatic Certificate Authority overlay.
+This overlay can generate X.509 certificate/key pairs for
+entries in the directory if slapd is linked to OpenSSL.
+By default it is not built.
+.TP
+.B chain
+Chaining.
+This overlay allows automatic referral chasing when a referral would
+have been returned, either when configured by the server or when
+requested by the client.
+.TP
+.B collect
+Collective Attributes.
+This overlay implements RFC 3671 collective attributes; these
+attributes share common values over all the members of the collection
+as inherited from an ancestor entry.
+.TP
+.B constraint
+Constraint.
+This overlay enforces a regular expression constraint on all values
+of specified attributes. It is used to enforce a more rigorous
+syntax when the underlying attribute syntax is too general.
+.TP
+.B dds
+Dynamic Directory Services.
+This overlay supports dynamic objects, which have a limited life after
+which they expire and are automatically deleted.
+.TP
+.B deref
+Dereference Control.
+This overlay implements the draft Dereference control. The overlay can be
+used with any backend or globally for all backends.
+.TP
+.B dyngroup
+Dynamic Group.
+This is a demo overlay which extends the Compare operation to detect
+members of a dynamic group.
+It has no effect on any other operations.
+.TP
+.B dynlist
+Dynamic List.
+This overlay allows expansion of dynamic groups and more.
+.TP
+.B homedir
+Home Directory Provisioning.
+This overlay manages creation/deletion of home directories for LDAP-based
+Unix accounts.
+.TP
+.B memberof
+MemberOf.
+This overlay maintains automatic reverse group membership values,
+typically stored in an attribute called memberOf. This overlay
+is deprecated and should be replaced with dynlist.
+.TP
+.B otp
+OATH One-Time Password module.
+This module allows time-based one-time password, AKA "authenticator-style",
+and HMAC-based one-time password authentication to be used in conjunction
+with a standard LDAP password for two factor authentication.
+.TP
+.B pbind
+Proxybind.
+This overlay forwards simple bind requests on a local database to a
+remote LDAP server.
+.TP
+.B pcache
+Proxycache.
+This overlay allows caching of LDAP search requests in a local database.
+It is most often used with the
+.BR slapd\-ldap (5)
+or
+.BR slapd\-meta (5)
+backends.
+.TP
+.B ppolicy
+Password Policy.
+This overlay provides a variety of password control mechanisms,
+e.g. password aging, password reuse and duplication control, mandatory
+password resets, etc.
+.TP
+.B refint
+Referential Integrity.
+This overlay can be used with a backend database such as
+.BR slapd\-mdb (5)
+to maintain the cohesiveness of a schema which utilizes reference
+attributes.
+.TP
+.B remoteauth
+Remote Authentication.
+This overlay delegates authentication requests to remote directories.
+.TP
+.B retcode
+Return Code.
+This overlay is useful to test the behavior of clients when
+server-generated erroneous and/or unusual responses occur.
+.TP
+.B rwm
+Rewrite/remap.
+This overlay is experimental.
+It performs basic DN/data rewrite and
+objectClass/attributeType mapping.
+.TP
+.B sssvlv
+Server Side Sorting and Virtual List Views.
+This overlay implements the RFC2891 server-side sorting control and
+virtual list view controls, and replaces the RFC2696 paged-results
+implementation to ensure it works with the sorting technique.
+.TP
+.B syncprov
+Syncrepl Provider.
+This overlay implements the provider-side support for
+.B syncrepl
+replication, including persistent search functionality.
+.TP
+.B translucent
+Translucent Proxy.
+This overlay can be used with a backend database such as
+.BR slapd\-mdb (5)
+to create a "translucent proxy".
+Content of entries retrieved from a remote LDAP server can be partially
+overridden by the database.
+.TP
+.B unique
+Attribute Uniqueness.
+This overlay can be used with a backend database such as
+.BR slapd\-mdb (5)
+to enforce the uniqueness of some or all attributes within a subtree.
+.TP
+.B valsort
+Value Sorting.
+This overlay can be used to enforce a specific order for the values
+of an attribute when it is returned in a search.
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.TP
+ETCDIR/slapd.d
+default slapd configuration directory
+.SH SEE ALSO
+.BR ldap (3),
+.BR slapo\-accesslog (5),
+.BR slapo\-auditlog (5),
+.BR slapo\-autoca (5),
+.BR slapo\-chain (5),
+.BR slapo\-collect (5),
+.BR slapo\-constraint (5),
+.BR slapo\-dds (5),
+.BR slapo\-deref (5),
+.BR slapo\-dyngroup (5),
+.BR slapo\-dynlist (5),
+.BR slapo\-memberof (5),
+.BR slapo\-pbind (5),
+.BR slapo\-pcache (5),
+.BR slapo\-ppolicy (5),
+.BR slapo\-refint (5),
+.BR slapo\-remoteauth (5),
+.BR slapo\-retcode (5),
+.BR slapo\-rwm (5),
+.BR slapo\-sssvlv (5),
+.BR slapo\-syncprov (5),
+.BR slapo\-translucent (5),
+.BR slapo\-unique (5).
+.BR slapo\-valsort (5).
+.BR slapd\-config (5),
+.BR slapd.conf (5),
+.BR slapd.backends (5),
+.BR slapd (8).
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man5/slapd.plugin.5 b/doc/man/man5/slapd.plugin.5
new file mode 100644
index 0000000..145ff87
--- /dev/null
+++ b/doc/man/man5/slapd.plugin.5
@@ -0,0 +1,124 @@
+.TH SLAPD.PLUGIN 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2002-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+slapd.plugin \- plugin configuration for slapd, the stand-alone LDAP daemon
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.BR slapd.conf (5)
+file contains configuration information for the
+.BR slapd (8)
+daemon. This configuration file is also used by the SLAPD tools
+.BR slapadd (8),
+.BR slapcat (8),
+.BR slapmodify (8),
+and
+.BR slapindex (8).
+.LP
+The
+.B slapd.conf
+file consists of a series of global configuration options that apply to
+.B slapd
+as a whole (including all backends), followed by zero or more database
+backend definitions that contain information specific to a backend
+instance.
+.LP
+The general format of
+.B slapd.conf
+is as follows:
+.LP
+.nf
+ # comment - these options apply to every database
+ <global configuration options>
+ # first database definition & configuration options
+ database <backend 1 type>
+ <configuration options specific to backend 1>
+ # subsequent database definitions & configuration options
+ ...
+.fi
+.LP
+If slapd is compiled with \fI\-\-enable\-slapi\fP, support for plugins
+according to \fINetscape's Directory Server Plug-Ins\fP.
+Version 4 of the API is currently implemented, with some extensions
+from version 5.
+.LP
+Both global and database specific data may contain plugin information.
+Plugins associated with a specific database are called before global
+plugins.
+This manpage details the
+.BR slapd (8)
+configuration statements that affect the loading of SLAPI \fIplugins\fP.
+.LP
+Arguments that should be replaced by actual text are shown in brackets <>.
+.LP
+The structure of the plugin directives is
+.TP
+.B plugin "<type> <lib_path> <init_function> [<arguments>]"
+Load a plugin of the specified type for the current database.
+.LP
+The
+.BR <type>
+can be one of
+.BR preoperation ,
+that is executed before processing the operation for the specified
+database,
+.BR postoperation ,
+that is executed after the operation for the specified database
+has been processed,
+.BR extendedop ,
+that is used when executing an extended operation, or
+.BR object .
+The latter is used for miscellaneous types such as ACL, computed
+attribute and search filter rewriter plugins.
+.LP
+The
+.BR <libpath>
+argument specifies the path to the plugin loadable object; if a relative
+path is given, the object is looked for according to the underlying
+dynamic loading package (libtool's ltdl is used).
+.LP
+The
+.BR <init_function>
+argument specifies what symbol must be called when the plugin is first
+loaded.
+This function should register the functions provided by the plugin
+for the desired operations. It should be noted that it is this
+init function, not the plugin type specified as the first argument,
+that determines when and for what operations the plugin will be invoked.
+The optional
+.BR <arguments>
+list is passed to the init function.
+.TP
+.B pluginlog <file>
+Specify an alternative path for the plugin log file (default is
+LOCALSTATEDIR/errors).
+.TP
+.B modulepath <pathspec>
+This statement sets the module load path for dynamically loadable
+backends, as described in
+.BR slapd.conf (5);
+however, since both the dynamically loadable backends
+and the SLAPI plugins use the same underlying library (libtool's ltdl)
+its value also affects the plugin search path.
+In general the search path is made of colon-separated paths; usually
+the user-defined path is searched first; then the value of the
+\fILTDL_LIBRARY_PATH\fP environment variable, if defined, is used;
+finally, the system-specific dynamic load path is attempted (e.g. on
+Linux the value of the environment variable \fILD_LIBRARY_PATH\fP).
+Please carefully read the documentation of ltdl because its behavior
+is very platform dependent.
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.TP
+LOCALSTATEDIR/errors
+default plugin log file
+.SH SEE ALSO
+.BR slapd (8),
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man5/slapo-accesslog.5 b/doc/man/man5/slapo-accesslog.5
new file mode 100644
index 0000000..a21f7d2
--- /dev/null
+++ b/doc/man/man5/slapo-accesslog.5
@@ -0,0 +1,514 @@
+.TH SLAPO-ACCESSLOG 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2005-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-accesslog \- Access Logging overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The Access Logging overlay can be used to record all accesses to a given
+backend database on another database. This allows all of the activity on
+a given database to be reviewed using arbitrary LDAP queries, instead of
+just logging to local flat text files. Configuration options are available
+for selecting a subset of operation types to log, and to automatically
+prune older log records from the logging database. Log records are stored
+with audit schema (see below) to assure their readability whether viewed
+as LDIF or in raw form.
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the Access Logging overlay.
+They should appear after the
+.B overlay
+directive.
+.TP
+.B logdb <suffix>
+Specify the suffix of a database to be used for storing the log records.
+The specified database must be defined elsewhere in the configuration and
+must support an ordered return of results such as
+.BR slapd\-mdb (5)
+The access controls
+on the log database should prevent general access. The suffix entry
+of the log database will be created automatically by this overlay. The log
+entries will be generated as the immediate children of the suffix entry.
+.TP
+.B logops <operations>
+Specify which types of operations to log. The valid operation types are
+abandon, add, bind, compare, delete, extended, modify, modrdn, search,
+and unbind. Aliases for common sets of operations are also available:
+.RS
+.TP
+.B writes
+add, delete, modify, modrdn
+.TP
+.B reads
+compare, search
+.TP
+.B session
+abandon, bind, unbind
+.TP
+.B all
+all operations
+.RE
+.TP
+.B logbase <operations> <baseDN>
+Specify a set of operations that will only be logged if they occur under
+a specific subtree of the database. The operation types are as above for
+the
+.B logops
+setting, and delimited by a '|' character.
+.TP
+.B logold <filter>
+Specify a filter for matching against Deleted and Modified entries. If
+the entry matches the filter, the old contents of the entry will be
+logged along with the current request.
+.TP
+.B logoldattr <attr> ...
+Specify a list of attributes whose old contents are always logged in
+Modify and ModRDN requests that match any of the filters configured in
+.BR logold .
+Usually only the contents of attributes that were
+actually modified will be logged; by default no old attributes are logged
+for ModRDN requests.
+.TP
+.B logpurge <age> <interval>
+Specify the maximum age for log entries to be retained in the database,
+and how often to scan the database for old entries. Both the
+.B age
+and
+.B interval
+are specified as a time span in days, hours, minutes, and seconds. The
+time format is [ddd+]hh:mm[:ss] i.e., the days and seconds components are
+optional but hours and minutes are required. Except for days, which can
+be up to 5 digits, each numeric field must be exactly two digits. For example
+.RS
+.RS
+.PD 0
+.TP
+logpurge 2+00:00 1+00:00
+.RE
+.PD
+would specify that the log database should be scanned every day for old
+entries, and entries older than two days should be deleted. When using a
+log database that supports ordered indexing on generalizedTime attributes,
+specifying an eq index on the
+.B reqStart
+attribute will greatly benefit the performance of the purge operation.
+.RE
+.TP
+.B logsuccess TRUE | FALSE
+If set to TRUE then log records will only be generated for successful
+requests, i.e., requests that produce a result code of 0 (LDAP_SUCCESS).
+If FALSE, log records are generated for all requests whether they
+succeed or not. The default is FALSE.
+
+.SH EXAMPLES
+.LP
+.nf
+ database mdb
+ suffix dc=example,dc=com
+ \...
+ overlay accesslog
+ logdb cn=log
+ logops writes reads
+ logbase search|compare ou=testing,dc=example,dc=com
+ logold (objectclass=person)
+
+ database mdb
+ suffix cn=log
+ \...
+ index reqStart eq
+ access to *
+ by dn.base="cn=admin,dc=example,dc=com" read
+.fi
+
+.SH SCHEMA
+The
+.B accesslog
+overlay utilizes the "audit" schema described herein.
+This schema is specifically designed for
+.B accesslog
+auditing and is not intended to be used otherwise. It is also
+noted that the schema described here is
+.I a work in
+.IR progress ,
+and hence subject to change without notice.
+The schema is loaded automatically by the overlay.
+
+The schema includes a number of object classes and associated
+attribute types as described below.
+
+The root entry of the underlying accesslog database makes use
+of the
+.B auditContainer
+class which is as follows:
+.LP
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.5.2.0
+ NAME 'auditContainer'
+ DESC 'AuditLog container'
+ SUP top STRUCTURAL
+ MAY ( cn $ reqStart $ reqEnd ) )
+.RE
+.P
+
+There is
+a basic
+.B auditObject
+class from which two additional classes,
+.B auditReadObject
+and
+.B auditWriteObject
+are derived. Object classes for each type of LDAP operation are further
+derived from these classes. This object class hierarchy is designed to
+allow flexible yet efficient searches of the log based on either a specific
+operation type's class, or on more general classifications. The definition
+of the
+.B auditObject
+class is as follows:
+.LP
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.5.2.1
+ NAME 'auditObject'
+ DESC 'OpenLDAP request auditing'
+ SUP top STRUCTURAL
+ MUST ( reqStart $ reqType $ reqSession )
+ MAY ( reqDN $ reqAuthzID $ reqControls $ reqRespControls $
+ reqEnd $ reqResult $ reqMessage $ reqReferral $ reqEntryUUID ) )
+.RE
+.P
+Note that all of the OIDs used in the logging schema currently reside
+under the OpenLDAP Experimental branch. It is anticipated that they
+will migrate to a Standard branch in the future.
+
+An overview of the attributes follows:
+.B reqStart
+and
+.B reqEnd
+provide the start and end time of the operation, respectively. They use
+generalizedTime syntax. The
+.B reqStart
+attribute is also used as the RDN for each log entry.
+
+The
+.B reqType
+attribute is a simple string containing the type of operation
+being logged, e.g.
+.BR add ,
+.BR delete ,
+.BR search ,
+etc. For extended operations, the type also includes the OID of the
+extended operation, e.g.
+.B extended(1.1.1.1)
+
+The
+.B reqSession
+attribute is an implementation-specific identifier that is common to
+all the operations associated with the same LDAP session. Currently this
+is slapd's internal connection ID, stored in decimal.
+
+The
+.B reqDN
+attribute is the distinguishedName of the target of the operation. E.g., for
+a Bind request, this is the Bind DN. For an Add request, this is the DN
+of the entry being added. For a Search request, this is the base DN of
+the search.
+
+The
+.B reqAuthzID
+attribute is the distinguishedName of the user that performed the operation.
+This will usually be the same name as was established at the start of a
+session by a Bind request (if any) but may be altered in various
+circumstances.
+
+The
+.B reqControls
+and
+.B reqRespControls
+attributes carry any controls sent by the client on the request and returned
+by the server in the response, respectively. The attribute values are just
+uninterpreted octet strings.
+
+The
+.B reqResult
+attribute is the numeric LDAP result code of the operation, indicating
+either success or a particular LDAP error code. An error code may be
+accompanied by a text error message which will be recorded in the
+.B reqMessage
+attribute.
+
+The
+.B reqReferral
+attribute carries any referrals that were returned with the result of the
+request.
+
+The
+.B reqEntryUUID
+attribute records the entryUUID attribute of the entry operated on, for an Add
+request, this is the entryUUID of the newly created entry.
+
+Operation-specific classes are defined with additional attributes to carry
+all of the relevant parameters associated with the operation:
+
+.LP
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.5.2.4
+ NAME 'auditAbandon'
+ DESC 'Abandon operation'
+ SUP auditObject STRUCTURAL
+ MUST reqId )
+.RE
+.P
+For the
+.B Abandon
+operation the
+.B reqId
+attribute contains the message ID of the request that was abandoned.
+
+.LP
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.5.2.5
+ NAME 'auditAdd'
+ DESC 'Add operation'
+ SUP auditWriteObject STRUCTURAL
+ MUST reqMod )
+.RE
+.P
+The
+.B Add
+class inherits from the
+.B auditWriteObject
+class. The Add and Modify classes are very similar. The
+.B reqMod
+attribute carries all of the attributes of the original entry being added.
+(Or in the case of a Modify operation, all of the modifications being
+performed.) The values are formatted as
+.RS
+.PD 0
+.TP
+attribute:<+|\-|=|#> [ value]
+.RE
+.RE
+.PD
+Where '+' indicates an Add of a value, '\-' for Delete, '=' for Replace,
+and '#' for Increment. In an Add operation, all of the reqMod values will
+have the '+' designator.
+.P
+.LP
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.5.2.6
+ NAME 'auditBind'
+ DESC 'Bind operation'
+ SUP auditObject STRUCTURAL
+ MUST ( reqVersion $ reqMethod ) )
+.RE
+.P
+The
+.B Bind
+class includes the
+.B reqVersion
+attribute which contains the LDAP protocol version specified in the Bind
+as well as the
+.B reqMethod
+attribute which contains the Bind Method used in the Bind. This will be
+the string
+.B SIMPLE
+for LDAP Simple Binds or
+.B SASL(<mech>)
+for SASL Binds.
+Note that unless configured as a global overlay, only Simple Binds using
+DNs that reside in the current database will be logged.
+
+.LP
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.5.2.7
+ NAME 'auditCompare'
+ DESC 'Compare operation'
+ SUP auditObject STRUCTURAL
+ MUST reqAssertion )
+.RE
+.P
+For the
+.B Compare
+operation the
+.B reqAssertion
+attribute carries the Attribute Value Assertion used in the compare request.
+
+.LP
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.5.2.8
+ NAME 'auditDelete'
+ DESC 'Delete operation'
+ SUP auditWriteObject STRUCTURAL
+ MAY reqOld )
+.RE
+.P
+The
+.B Delete
+operation needs no further parameters. However, the
+.B reqOld
+attribute may optionally be used to record the contents of the entry prior
+to its deletion. The values are formatted as
+.RS
+.PD 0
+.TP
+attribute: value
+.RE
+.PD
+The
+.B reqOld
+attribute is only populated if the entry being deleted matches the
+configured
+.B logold
+filter.
+
+.LP
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.5.2.9
+ NAME 'auditModify'
+ DESC 'Modify operation'
+ SUP auditWriteObject STRUCTURAL
+ MAY ( reqOld $ reqMod ) )
+.RE
+.P
+The
+.B Modify
+operation contains a description of modifications in the
+.B reqMod
+attribute, which was already described above in the Add operation. It may
+optionally contain the previous contents of any modified attributes in the
+.B reqOld
+attribute, using the same format as described above for the Delete operation.
+The
+.B reqOld
+attribute is only populated if the entry being modified matches the
+configured
+.B logold
+filter.
+
+.LP
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.5.2.10
+ NAME 'auditModRDN'
+ DESC 'ModRDN operation'
+ SUP auditWriteObject STRUCTURAL
+ MUST ( reqNewRDN $ reqDeleteOldRDN )
+ MAY ( reqNewSuperior $ reqMod $ reqOld ) )
+.RE
+.P
+The
+.B ModRDN
+class uses the
+.B reqNewRDN
+attribute to carry the new RDN of the request.
+The
+.B reqDeleteOldRDN
+attribute is a Boolean value showing
+.B TRUE
+if the old RDN was deleted from the entry, or
+.B FALSE
+if the old RDN was preserved.
+The
+.B reqNewSuperior
+attribute carries the DN of the new parent entry if the request specified
+the new parent.
+The
+.B reqOld
+attribute is only populated if the entry being modified matches the
+configured
+.B logold
+filter and contains attributes in the
+.B logoldattr
+list.
+
+.LP
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.5.2.11
+ NAME 'auditSearch'
+ DESC 'Search operation'
+ SUP auditReadObject STRUCTURAL
+ MUST ( reqScope $ reqDerefAliases $ reqAttrsOnly )
+ MAY ( reqFilter $ reqAttr $ reqEntries $ reqSizeLimit $
+ reqTimeLimit ) )
+.RE
+.P
+For the
+.B Search
+class the
+.B reqScope
+attribute contains the scope of the original search request, using the
+values specified for the LDAP URL format. I.e.
+.BR base ,
+.BR one ,
+.BR sub ,
+or
+.BR subord .
+The
+.B reqDerefAliases
+attribute is one of
+.BR never ,
+.BR finding ,
+.BR searching ,
+or
+.BR always ,
+denoting how aliases will be processed during the search.
+The
+.B reqAttrsOnly
+attribute is a Boolean value showing
+.B TRUE
+if only attribute names were requested, or
+.B FALSE
+if attributes and their values were requested.
+The
+.B reqFilter
+attribute carries the filter used in the search request.
+The
+.B reqAttr
+attribute lists the requested attributes if specific attributes were
+requested.
+The
+.B reqEntries
+attribute is the integer count of how many entries were returned by
+this search request.
+The
+.B reqSizeLimit
+and
+.B reqTimeLimit
+attributes indicate what limits were requested on the search operation.
+
+.LP
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.5.2.12
+ NAME 'auditExtended'
+ DESC 'Extended operation'
+ SUP auditObject STRUCTURAL
+ MAY reqData )
+.RE
+.P
+The
+.B Extended
+class represents an LDAP Extended Operation. As noted above, the actual OID of
+the operation is included in the
+.B reqType
+attribute of the parent class. If any optional data was provided with the
+request, it will be contained in the
+.B reqData
+attribute as an uninterpreted octet string.
+
+.SH NOTES
+The Access Log implemented by this overlay may be used for a variety of
+other tasks, e.g. as a ChangeLog for a replication mechanism, as well
+as for security/audit logging purposes.
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5).
+
+.SH ACKNOWLEDGEMENTS
+.P
+This module was written in 2005 by Howard Chu of Symas Corporation.
diff --git a/doc/man/man5/slapo-auditlog.5 b/doc/man/man5/slapo-auditlog.5
new file mode 100644
index 0000000..6aeca87
--- /dev/null
+++ b/doc/man/man5/slapo-auditlog.5
@@ -0,0 +1,98 @@
+.TH SLAPO-AUDITLOG 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2005-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-auditlog \- Audit Logging overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.TP
+ETCDIR/slapd.d
+.SH DESCRIPTION
+The Audit Logging overlay can be used to record all changes on a given
+backend database to a specified log file. Changes are logged as standard
+LDIF, with an additional comment header providing six fields of
+information about the change. A second comment header is added at the end
+of the operation to note the termination of the change.
+.LP
+For Add and Modify operations the identity comes from the modifiersName
+associated with the operation. This is usually the same as the requestor's
+identity, but may be set by other overlays to reflect other values.
+.SH CONFIGURATION
+This
+.B slapd.conf
+option applies to the Audit Logging overlay.
+It should appear after the
+.B overlay
+directive.
+.TP
+.B auditlog <filename>
+Specify the fully qualified path for the log file.
+.TP
+.B olcAuditlogFile <filename>
+For use with
+.B cn=config
+.SH COMMENT FIELD INFORMATION
+The first field is the operation type.
+.br
+The second field is the timestamp of the operation in seconds since epoch.
+.br
+The third field is the suffix of the database.
+.br
+The fourth field is the recorded modifiersName.
+.br
+The fifth field is the originating IP address and port.
+.br
+The sixth field is the connection number. A connection number of -1
+indicates an internal slapd operation.
+.SH EXAMPLE
+The following LDIF could be used to add this overlay to
+.B cn=config
+(adjust to suit)
+.LP
+.RS
+.nf
+dn: olcOverlay=auditlog,olcDatabase={1}mdb,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectClass: olcAuditLogConfig
+olcOverlay: auditlog
+olcAuditlogFile: /tmp/auditlog.ldif
+.fi
+.RE
+.LP
+.LP
+.SH EXAMPLE CHANGELOG
+.LP
+.RS
+.nf
+# modify 1614223245 dc=example,dc=com cn=admin,dc=example,dc=com IP=[::1]:47270 conn=1002
+dn: uid=joepublic,ou=people,dc=example,dc=com
+changetype: modify
+replace: displayName
+displayName: Joe Public
+-
+replace: entryCSN
+entryCSN: 20210225032045.045229Z#000000#001#000000
+-
+replace: modifiersName
+modifiersName: cn=admin,dc=example,dc=com
+-
+replace: modifyTimestamp
+modifyTimestamp: 20210225032045Z
+-
+# end modify 1614223245
+
+.fi
+.RE
+.LP
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.TP
+ETCDIR/slapd.d
+default slapd configuration directory
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config(5).
diff --git a/doc/man/man5/slapo-autoca.5 b/doc/man/man5/slapo-autoca.5
new file mode 100644
index 0000000..8e77cc8
--- /dev/null
+++ b/doc/man/man5/slapo-autoca.5
@@ -0,0 +1,120 @@
+.TH SLAPO-AUTOCA 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2009-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copyright 2009-2018 Howard Chu All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-autoca \- Automatic Certificate Authority overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The Automatic CA overlay generates X.509 certificate/key pairs for
+entries in the directory. The DN of a generated certificate is
+identical to the DN of the entry containing it. On startup it
+looks for a CA certificate and key in the suffix entry of the
+database which it will use to sign all subsequently generated
+certificates. A new CA certificate and key will be generated
+and stored in the suffix entry if none already exists. The CA
+certificate is stored in the cACertificate;binary attribute of
+the suffix entry, and the private key is stored in the
+cAPrivateKey;binary attribute of the suffix entry. These
+attributes may be overwritten if some other CA certificate/key
+pair is desired for use.
+.LP
+Certificates for users and servers are generated on demand using
+a Search request returning only the userCertificate;binary and
+userPrivateKey;binary attributes. Any Search for anything besides
+exactly these two attributes is ignored by the overlay. Note that
+these values are stored in ASN.1 DER form in the directory so the
+";binary" attribute option is mandatory.
+.LP
+Entries that do not belong to selected objectClasses will be
+ignored by the overlay. By default, entries of objectClass
+.B person
+will be treated as users, and entries of objectClass
+.B ipHost
+will be treated as servers. There are slight differences in the
+set of X.509V3 certificate extensions added to the certificate
+between users and servers.
+.LP
+The CA's private key is stored in a
+.B cAPrivateKey
+attribute, and user and server private keys are stored in the
+.B userPrivateKey
+attribute. The private key values are encoded in PKCS#8 format.
+It is essential that access to these attributes be
+properly secured with ACLs. Both of these attributes inherit
+from the
+.B pKCS8PrivateKey
+attribute, so it is sufficient to use a single ACL rule like
+
+.nf
+ access to attrs=pKCS8PrivateKey by self ssf=128 write
+.fi
+
+at the beginning of the rules.
+.LP
+Currently there is no automated management for expiration or revocation.
+Obsolete certificates and keys must be manually removed by deleting
+an entry's userCertificate and userPrivateKey attributes.
+
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the Automatic CA overlay.
+They should appear after the
+.B overlay
+directive.
+.TP
+.B userClass <objectClass>
+Specify the objectClass to be treated as user entries.
+.TP
+.B serverClass <objectClass>
+Specify the objectClass to be treated as server entries.
+.TP
+.B userKeybits <integer>
+Specify the size of the private key to use for user certificates.
+The default is 2048 and the minimum is 512.
+.TP
+.B serverKeybits <integer>
+Specify the size of the private key to use for server certificates.
+The default is 2048 and the minimum is 512.
+.TP
+.B caKeybits <integer>
+Specify the size of the private key to use for the CA certificate.
+The default is 2048 and the minimum is 512.
+.TP
+.B userDays <integer>
+Specify the duration for a user certificate's validity.
+The default is 365, 1 year.
+.TP
+.B serverDays <integer>
+Specify the duration for a server certificate's validity.
+The default is 1826, 5 years.
+.TP
+.B caDays <integer>
+Specify the duration for the CA certificate's validity.
+The default is 3652, 10 years.
+.TP
+.B localDN <DN>
+Specify the DN of an entry that represents this server. Requests
+to generate a certificate/key pair for this DN will also install
+the certificate and key into slapd's TLS settings in cn=config
+for immediate use.
+
+.SH EXAMPLES
+.nf
+ database mdb
+ ...
+ overlay autoca
+ caKeybits 4096
+.fi
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5).
+.SH AUTHOR
+Howard Chu
diff --git a/doc/man/man5/slapo-chain.5 b/doc/man/man5/slapo-chain.5
new file mode 100644
index 0000000..eaaa2b2
--- /dev/null
+++ b/doc/man/man5/slapo-chain.5
@@ -0,0 +1,152 @@
+.TH SLAPO-CHAIN 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation, All Rights Reserved.
+.\" Copying restrictions apply. See the COPYRIGHT file.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-chain \- chain overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B chain
+overlay to
+.BR slapd (8)
+allows automatic referral chasing.
+Any time a referral is returned (except for bind operations),
+it is chased by using an instance of the ldap backend.
+If operations are performed with an identity (i.e. after a bind),
+that identity can be asserted while chasing the referrals
+by means of the \fIidentity assertion\fP feature of back-ldap
+(see
+.BR slapd\-ldap (5)
+for details), which is essentially based on the
+.B proxied authorization
+control [RFC 4370].
+Referral chasing can be controlled by the client by issuing the
+\fBchaining\fP control
+(see \fIdraft-sermersheim-ldap-chaining\fP for details.)
+
+.LP
+The config directives that are specific to the
+.B chain
+overlay are prefixed by
+.BR chain\- ,
+to avoid potential conflicts with directives specific to the underlying
+database or to other stacked overlays.
+
+.LP
+There are very few chain overlay specific directives; however, directives
+related to the instances of the \fIldap\fP backend that may be implicitly
+instantiated by the overlay may assume a special meaning when used
+in conjunction with this overlay. They are described in
+.BR slapd\-ldap (5),
+and they also need to be prefixed by
+.BR chain\- .
+
+Note: this overlay is built into the \fIldap\fP backend; it is not
+a separate module.
+
+.TP
+.B overlay chain
+This directive adds the chain overlay to the current backend.
+The chain overlay may be used with any backend, but it is mainly
+intended for use with local storage backends that may return referrals.
+It is useless in conjunction with the \fIslapd\-ldap\fP and \fIslapd\-meta\fP
+backends because they already exploit the libldap specific referral chase
+feature.
+[Note: this may change in the future, as the \fBldap\fP(5) and
+\fBmeta\fP(5) backends might no longer chase referrals on their own.]
+.TP
+.B chain\-cache\-uri {FALSE|true}
+This directive instructs the \fIchain\fP overlay to cache
+connections to URIs parsed out of referrals that are not predefined,
+to be reused for later chaining.
+These URIs inherit the properties configured for the underlying
+\fBslapd\-ldap\fP(5) before any occurrence of the \fBchain\-uri\fP
+directive; basically, they are chained anonymously.
+.TP
+.B chain\-chaining [resolve=<r>] [continuation=<c>] [critical]
+This directive enables the \fIchaining\fP control
+(see \fIdraft-sermersheim-ldap-chaining\fP for details)
+with the desired resolve and continuation behaviors and criticality.
+The \fBresolve\fP parameter refers to the behavior while discovering
+a resource, namely when accessing the object indicated by the request DN;
+the \fBcontinuation\fP parameter refers to the behavior while handling
+intermediate responses, which is mostly significant for the search
+operation, but may affect extended operations that return intermediate
+responses.
+The values \fBr\fP and \fBc\fP can be any of
+.BR chainingPreferred ,
+.BR chainingRequired ,
+.BR referralsPreferred ,
+.BR referralsRequired .
+If the \fBcritical\fP flag affects the control criticality if provided.
+[This control is experimental and its support may change in the future.]
+.TP
+.B chain\-max\-depth <n>
+In case a referral is returned during referral chasing, further chasing
+occurs at most \fB<n>\fP levels deep. Set to \fB1\fP (the default)
+to disable further referral chasing.
+.TP
+.B chain\-return\-error {FALSE|true}
+In case referral chasing fails, the real error is returned instead
+of the original referral. In case multiple referral URIs are present,
+only the first error is returned. This behavior may not be always
+appropriate nor desirable, since failures in referral chasing might be
+better resolved by the client (e.g. when caused by distributed
+authentication issues).
+.TP
+.B chain\-uri <ldapuri>
+This directive instantiates a new underlying \fIldap\fP database
+and instructs it about which URI to contact to chase referrals.
+As opposed to what stated in \fBslapd\-ldap\fP(5), only one URI
+can appear after this directive; all subsequent \fBslapd\-ldap\fP(5)
+directives prefixed by \fBchain\-\fP refer to this specific instance
+of a remote server.
+.LP
+
+Directives for configuring the underlying ldap database may also
+be required, as shown in this example:
+.LP
+.RS
+.nf
+overlay chain
+chain\-rebind\-as\-user FALSE
+
+chain\-uri "ldap://ldap1.example.com"
+chain\-rebind\-as\-user TRUE
+chain\-idassert\-bind bindmethod="simple"
+ binddn="cn=Auth,dc=example,dc=com"
+ credentials="secret"
+ mode="self"
+
+chain\-uri "ldap://ldap2.example.com"
+chain\-idassert\-bind bindmethod="simple"
+ binddn="cn=Auth,dc=example,dc=com"
+ credentials="secret"
+ mode="none"
+
+.fi
+.RE
+.LP
+Any valid directives for the ldap database may be used; see
+.BR slapd\-ldap (5)
+for details.
+Multiple occurrences of the \fBchain\-uri\fP directive may appear,
+to define multiple "trusted" URIs where operations with
+\fIidentity assertion\fP are chained.
+All URIs not listed in the configuration are chained anonymously.
+All \fBslapd\-ldap\fP(5) directives appearing before the first
+occurrence of \fBchain\-uri\fP are inherited by all URIs,
+unless specifically overridden inside each URI configuration.
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd\-ldap (5),
+.BR slapd (8).
+.SH AUTHOR
+Originally implemented by Howard Chu; extended by Pierangelo Masarati.
diff --git a/doc/man/man5/slapo-collect.5 b/doc/man/man5/slapo-collect.5
new file mode 100644
index 0000000..443118a
--- /dev/null
+++ b/doc/man/man5/slapo-collect.5
@@ -0,0 +1,52 @@
+.TH SLAPO-COLLECT 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2003-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-collect \- Collective attributes overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The collect overlay is used to provide a relatively coarse
+implementation of RFC 3671 collective attributes.
+In X.500, a collective attribute is "a user attribute whose
+values are the same for each member of an entry collection".
+
+Collective attributes are added to entries returned by a search operation
+when the entry is within the scope of the related ancestor.
+Collective attributes can only be modified when the modification affects
+the related ancestor.
+
+.SH CONFIGURATION
+This
+.B slapd.conf
+option applies to the collect overlay.
+It should appear after the
+.B overlay
+directive.
+.TP
+.B collectinfo <DN> <attrlist>
+Specify the
+.B DN
+of the ancestor entry and the set of related collective attributes, where
+.B attrlist
+is a comma-separated list of attributes.
+The
+.B DN
+should be within the naming context of the database.
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+The
+.BR slapo\-collect (5)
+overlay supports dynamic configuration via
+.BR back-config .
+.SH ACKNOWLEDGEMENTS
+This module was written in 2003 by Howard Chu.
+This man page was written in 2008 by Pierangelo Masarati.
+.so ../Project
diff --git a/doc/man/man5/slapo-constraint.5 b/doc/man/man5/slapo-constraint.5
new file mode 100644
index 0000000..240f713
--- /dev/null
+++ b/doc/man/man5/slapo-constraint.5
@@ -0,0 +1,155 @@
+.TH SLAPO-CONSTRAINT 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2005-2006 Hewlett-Packard Company
+.\" Copyright 2006-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-constraint \- Attribute Constraint Overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The constraint overlay is used to ensure that attribute values match
+some constraints beyond basic LDAP syntax. Attributes can
+have multiple constraints placed upon them, and all must be satisfied
+when modifying an attribute value under constraint.
+.LP
+This overlay is intended to be used to force syntactic regularity upon
+certain string represented data which have well known canonical forms,
+like telephone numbers, post codes, FQDNs, etc.
+.LP
+It constrains only LDAP \fIadd\fP, \fImodify\fP and \fIrename\fP commands
+and only seeks to control the \fIadd\fP and \fIreplace\fP values
+of \fImodify\fP and \fIrename\fP requests.
+.LP
+No constraints are applied for operations performed with the
+.I relax
+control set.
+.SH CONFIGURATION
+This
+.B slapd.conf
+option applies to the constraint overlay.
+It should appear after the
+.B overlay
+directive.
+.TP
+.B constraint_attribute <attribute_name>[,...] <type> <value> [<extra> [...]]
+Specifies the constraint which should apply to the comma-separated
+attribute list named as the first parameter.
+Six types of constraint are currently supported -
+.BR regex ,
+.BR negregex ,
+.BR size ,
+.BR count ,
+.BR uri ,
+and
+.BR set .
+
+The parameter following the
+.B regex
+or
+.B negregex
+type is a Unix style regular expression (See
+.BR regex (7)
+). The parameter following the
+.B uri
+type is an LDAP URI. The URI will be evaluated using an internal search.
+It must not include a hostname, and it must include a list of attributes
+to evaluate.
+
+The parameter following the
+.B set
+type is a string that is interpreted according to the syntax in use
+for ACL sets. This allows one to construct constraints based on the contents
+of the entry.
+
+The
+.B size
+type can be used to enforce a limit on an attribute length, and the
+.B count
+type limits the number of values of an attribute.
+
+Extra parameters can occur in any order after those described above.
+.RS
+.TP
+.B <extra> : restrict=<uri>
+.RE
+
+.RS
+This extra parameter allows one to restrict the application of the corresponding
+constraint only to entries that match the
+.IR base ,
+.I scope
+and
+.I filter
+portions of the LDAP URI.
+The
+.IR base ,
+if present, must be within the naming context of the database.
+The
+.I scope
+is only used when the
+.I base
+is present; it defaults to
+.BR base .
+The other parameters of the URI are not allowed.
+.RE
+
+.LP
+Any attempt to add or modify an attribute named as part of the
+constraint overlay specification which does not fit the
+constraint listed will fail with a
+LDAP_CONSTRAINT_VIOLATION error.
+.SH EXAMPLES
+.LP
+.RS
+.nf
+overlay constraint
+constraint_attribute jpegPhoto size 131072
+constraint_attribute userPassword count 3
+constraint_attribute mail regex ^[[:alnum:]]+@mydomain.com$
+constraint_attribute mail negregex ^[[:alnum:]]+@notallowed.com$
+constraint_attribute title uri
+ ldap:///dc=catalog,dc=example,dc=com?title?sub?(objectClass=titleCatalog)
+constraint_attribute cn,sn,givenName set
+ "(this/givenName + [ ] + this/sn) & this/cn"
+ restrict="ldap:///ou=People,dc=example,dc=com??sub?(objectClass=inetOrgPerson)"
+.fi
+
+.RE
+A specification like the above would reject any
+.B mail
+attribute which did not look like
+.BR "<alphanumeric string>@mydomain.com"
+or that looks like
+.BR "<alphanumeric string>@notallowed.com" .
+It would also reject any
+.B title
+attribute whose values were not listed in the
+.B title
+attribute of any
+.B titleCatalog
+entries in the given scope. (Note that the
+"dc=catalog,dc=example,dc=com" subtree ought to reside
+in a separate database, otherwise the initial set of
+titleCatalog entries could not be populated while the
+constraint is in effect.)
+Finally, it requires the values of the attribute
+.B cn
+to be constructed by pairing values of the attributes
+.B sn
+and
+.BR givenName ,
+separated by a space, but only for entries derived from the objectClass
+.BR inetOrgPerson .
+.RE
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.SH ACKNOWLEDGEMENTS
+This module was written in 2005 by Neil Dunbar of Hewlett-Packard and subsequently
+extended by Howard Chu and Emmanuel Dreyfus.
+.so ../Project
diff --git a/doc/man/man5/slapo-dds.5 b/doc/man/man5/slapo-dds.5
new file mode 100644
index 0000000..36218c8
--- /dev/null
+++ b/doc/man/man5/slapo-dds.5
@@ -0,0 +1,271 @@
+.TH SLAPO-DDS 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2005-2022 The OpenLDAP Foundation, All Rights Reserved.
+.\" Copying restrictions apply. See the COPYRIGHT file.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-dds \- Dynamic Directory Services overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B dds
+overlay to
+.BR slapd (8)
+implements dynamic objects as per RFC 2589.
+The name
+.B dds
+stands for
+Dynamic Directory Services.
+It allows one to define dynamic objects, characterized by the
+.B dynamicObject
+objectClass.
+
+Dynamic objects have a limited lifetime, determined by a time-to-live
+(TTL) that can be refreshed by means of a specific
+.B refresh
+extended operation.
+This operation allows one to set the Client Refresh Period (CRP),
+namely the period between refreshes that is required to preserve the
+dynamic object from expiration.
+The expiration time is computed by adding the requested TTL to the
+current time.
+When dynamic objects reach the end of their lifetime without being
+further refreshed, they are automatically deleted.
+There is no guarantee of immediate deletion, so clients should not count
+on it.
+
+Dynamic objects can have subordinates, provided these also are dynamic
+objects.
+RFC 2589 does not specify what the behavior of a dynamic directory
+service should be when a dynamic object with (dynamic) subordinates
+expires.
+In this implementation, the lifetime of dynamic objects with subordinates
+is prolonged until all the dynamic subordinates expire.
+
+
+This
+.BR slapd.conf (5)
+directive adds the
+.B dds
+overlay to the current database:
+
+.TP
+.B overlay dds
+
+.LP
+The database must have a
+.B rootdn
+specified, otherwise, the
+.B dds
+overlay will not be able to delete expired objects. The
+.B dds
+overlay may be used with any backend that implements the
+.BR add ,
+.BR modify ,
+.BR search ,
+and
+.BR delete
+operations.
+Since its use may result in many internal entry lookups, adds
+and deletes, it should be best used in conjunction with backends
+that have reasonably good write performances.
+
+.LP
+The config directives that are specific to the
+.B dds
+overlay are prefixed by
+.BR dds\- ,
+to avoid potential conflicts with directives specific to the underlying
+database or to other stacked overlays.
+
+.TP
+.B dds\-max\-ttl <time>
+Specifies the max TTL value.
+This is also the default TTL newly created
+dynamic objects receive, unless
+.B dds\-default\-ttl
+is set.
+When the client with a refresh extended operation requests a TTL higher
+than it, sizeLimitExceeded is returned.
+This value must be between 86400 (1 day, the default) and 31557600
+(1 year plus 6 hours, as per RFC 2589).
+
+.TP
+.B dds\-min\-ttl <time>
+Specifies the min TTL value; clients requesting a lower TTL by means
+of the refresh extended operation actually obtain this value as CRP.
+If set to 0 (the default), no lower limit is set.
+
+.TP
+.B dds\-default\-ttl <time>
+Specifies the default TTL value that newly created dynamic objects get.
+If set to 0 (the default), the
+.B dds\-max\-ttl
+is used.
+
+.TP
+.B dds\-interval <time>
+Specifies the interval between expiration checks; defaults to 1 hour.
+
+.TP
+.B dds\-tolerance <time>
+Specifies an extra time that is added to the timer that actually wakes up
+the thread that will delete an expired dynamic object.
+So the nominal lifetime of the entry is that specified in the
+.B entryTtl
+attribute, but its lifetime will actually be
+.BR "entryTtl + tolerance" .
+Note that there is no guarantee that the lifetime of a dynamic object
+will be
+.I exactly
+the requested TTL; due to implementation details, it may be longer, which
+is allowed by RFC 2589.
+By default, tolerance is 0.
+
+.TP
+.B dds\-max\-dynamicObjects <num>
+Specifies the maximum number of dynamic objects that can simultaneously exist
+within a naming context.
+This allows one to limit the amount of resources (mostly in terms of
+run-queue size) that are used by dynamic objects.
+By default, no limit is set.
+
+.TP
+.B dds\-state {TRUE|false}
+Specifies if the Dynamic Directory Services feature is enabled or not.
+By default it is; however, a proxy does not need to keep track of dynamic
+objects itself, it only needs to inform the frontend that support for
+dynamic objects is available.
+
+.SH ACCESS CONTROL
+The
+.B dds
+overlay restricts the refresh operation by requiring
+.B manage
+access to the
+.B entryTtl
+attribute (see
+.BR slapd.access (5)
+for details about the
+.B manage
+access privilege).
+Since the
+.B entryTtl
+is an operational, NO-USER-MODIFICATION attribute, no direct write access
+to it is possible.
+So the
+.B dds
+overlay turns refresh extended operation into an internal modification to
+the value of the
+.B entryTtl
+attribute with the
+.B relax
+control set.
+
+RFC 2589 recommends that anonymous clients should not be allowed to refresh
+a dynamic object.
+This can be implemented by appropriately crafting access control to obtain
+the desired effect.
+
+Example: restrict refresh to authenticated clients
+
+.RS
+.nf
+access to attrs=entryTtl
+ by users manage
+ by * read
+
+.fi
+.RE
+Example: restrict refresh to the creator of the dynamic object
+
+.RS
+.nf
+access to attrs=entryTtl
+ by dnattr=creatorsName manage
+ by * read
+
+.fi
+.RE
+Another suggested usage of dynamic objects is to implement dynamic meetings;
+in this case, all the participants to the meeting are allowed to refresh
+the meeting object, but only the creator can delete it (otherwise it will
+be deleted when the TTL expires)
+
+Example: assuming \fIparticipant\fP is a valid DN-valued attribute,
+allow users to start a meeting and to join it; restrict refresh
+to the participants; restrict delete to the creator
+
+.RS
+.nf
+access to dn.base="cn=Meetings"
+ attrs=children
+ by users write
+
+access to dn.onelevel="cn=Meetings"
+ attrs=entry
+ by dnattr=creatorsName write
+ by * read
+
+access to dn.onelevel="cn=Meetings"
+ attrs=participant
+ by dnattr=creatorsName write
+ by users selfwrite
+ by * read
+
+access to dn.onelevel="cn=Meetings"
+ attrs=entryTtl
+ by dnattr=participant manage
+ by * read
+
+.fi
+.RE
+
+.SH REPLICATION
+This implementation of RFC 2589 provides a restricted interpretation of how
+dynamic objects replicate. Only the provider takes care of handling dynamic
+object expiration, while consumers simply see the dynamic object as a plain
+object.
+
+When replicating these objects, one needs to explicitly exclude the
+.B dynamicObject
+class and the
+.B entryTtl
+attribute.
+This implementation of RFC 2589 introduces a new operational attribute,
+.BR entryExpireTimestamp ,
+that contains the expiration timestamp. This must be excluded from
+replication as well.
+
+The quick and dirty solution is to set
+.B schemacheck=off
+in the syncrepl configuration
+and, optionally, exclude the operational attributes from replication, using
+
+.RS
+.nf
+syncrepl ...
+ exattrs=entryTtl,entryExpireTimestamp
+.fi
+.RE
+
+In any case the overlay must be either statically built in or run-time loaded
+by the consumer, so that it is aware of the
+.B entryExpireTimestamp
+operational attribute; however, it must not be configured in the shadow
+database.
+Currently, there is no means to remove the
+.B dynamicObject
+class from the entry; this may be seen as a feature, since it allows one to see
+the dynamic properties of the object.
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd (8).
+.SH AUTHOR
+Implemented by Pierangelo Masarati.
diff --git a/doc/man/man5/slapo-deref.5 b/doc/man/man5/slapo-deref.5
new file mode 100644
index 0000000..abd2dfe
--- /dev/null
+++ b/doc/man/man5/slapo-deref.5
@@ -0,0 +1,80 @@
+.TH SLAPO-DEREF 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2008-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-deref \- Dereference Control overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.TP
+ETCDIR/slapd.d
+.SH DESCRIPTION
+This overlay implements the draft Dereference control. The overlay can be
+used with any backend or globally for all backends.
+
+.SH EXAMPLES
+.nf
+ database mdb
+ ...
+ overlay deref
+.fi
+
+Given these entries:
+.nf
+ dn: cn=Howard Chu,ou=people,dc=example,dc=org
+ objectClass: inetOrgPerson
+ cn: Howard Chu
+ sn: Chu
+ uid: hyc
+
+ dn: cn=Pierangelo Masarati,ou=people,dc=example,dc=org
+ objectClass: inetOrgPerson
+ cn: Pierangelo Masarati
+ sn: Masarati
+ uid: ando
+
+ dn: cn=Test Group,ou=groups,dc=example,dc=org
+ objectClass: groupOfNames
+ cn: Test Group
+ member: cn=Howard Chu,ou=people,dc=example,dc=org
+ member: cn=Pierangelo Masarati,ou=people,dc=example,dc=org
+.fi
+
+A search could be performed with a Dereference request control value
+specified as
+
+.nf
+ { member, uid }
+.fi
+
+I.e.,
+.nf
+ ldapsearch -x -b dc=example,dc=org -E 'deref=member:uid'
+.fi
+
+and the "cn=Test Group" entry would be returned with the response
+control value
+.nf
+ { { member, cn=Howard Chu,ou=people,dc=example,dc=org,
+ { { uid, [hyc] } } },
+ { member, cn=Pierangelo Masarati,ou=people,dc=example,dc=org,
+ { { uid, [ando] } } } }
+.fi
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.TP
+ETCDIR/slapd.d
+default slapd configuration directory
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5).
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+IETF LDAP Dereference Control proposal by P. Masarati, H. Chu,
+in IETF document "draft-masarati-ldap-deref-00.txt".
+.SH AUTHOR
+Pierangelo Masarati
diff --git a/doc/man/man5/slapo-dyngroup.5 b/doc/man/man5/slapo-dyngroup.5
new file mode 100644
index 0000000..bdb4dc5
--- /dev/null
+++ b/doc/man/man5/slapo-dyngroup.5
@@ -0,0 +1,58 @@
+.TH SLAPO-DYNGROUP 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2005-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-dyngroup \- Dynamic Group overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The Dynamic Group overlay allows clients to use LDAP Compare operations
+to test the membership of a dynamic group the same way they would check
+against a static group. Compare operations targeting a group's static
+member attribute will be intercepted and tested against the configured
+dynamic group's URL attribute.
+.LP
+Note that this intercept only happens if the actual
+Compare operation does not return a LDAP_COMPARE_TRUE result. So if a
+group has both static and dynamic members, the static member list will
+be checked first.
+.SH CONFIGURATION
+This
+.B slapd.conf
+option applies to the Dynamic Group overlay.
+It should appear after the
+.B overlay
+directive.
+.TP
+.B attrpair <memberAttr> <URLattr>
+Specify the attributes to be compared. A compare operation on the
+.I memberAttr
+will cause the
+.I URLattr
+to be evaluated for the result.
+.SH EXAMPLES
+.nf
+ database mdb
+ ...
+ overlay dyngroup
+ attrpair member memberURL
+.fi
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH BACKWARD COMPATIBILITY
+The dyngroup overlay has been reworked with the 2.5 release to use
+a consistent namespace as with other overlays. As a side-effect the
+following cn=config parameters are deprecated and will be removed in
+a future release:
+.B olcDGAttrPair
+is replaced with olcDynGroupAttrPair
+.B olcDGConfig
+is replaced with olcDynGroupConfig
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5).
+.SH AUTHOR
+Howard Chu
diff --git a/doc/man/man5/slapo-dynlist.5 b/doc/man/man5/slapo-dynlist.5
new file mode 100644
index 0000000..db00312
--- /dev/null
+++ b/doc/man/man5/slapo-dynlist.5
@@ -0,0 +1,275 @@
+.TH SLAPO-DYNLIST 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation, All Rights Reserved.
+.\" Copying restrictions apply. See the COPYRIGHT file.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-dynlist \- Dynamic List overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B dynlist
+overlay to
+.BR slapd (8)
+allows expansion of dynamic groups and more.
+Any time an entry with a specific objectClass (defined in the overlay configuration) is being returned,
+the LDAP URI-valued occurrences of a specific attribute (also defined in the overlay configuration) are
+expanded into the corresponding entries, and the values
+of the attributes listed in the URI are added to the original
+entry.
+No recursion is allowed, to avoid potential infinite loops.
+
+The resulting entry must comply with the LDAP data model, so constraints
+are enforced.
+For example, if a \fISINGLE\-VALUE\fP attribute is listed,
+only the first value found during the list expansion appears in the final entry.
+All dynamic behavior is disabled when the \fImanageDSAit\fP
+control (RFC 3296) is used.
+In that case, the contents of the dynamic group entry is returned;
+namely, the URLs are returned instead of being expanded.
+
+.SH CONFIGURATION
+The config directives that are specific to the
+.B dynlist
+overlay must be prefixed by
+.BR dynlist\- ,
+to avoid potential conflicts with directives specific to the underlying
+database or to other stacked overlays.
+
+.TP
+.B overlay dynlist
+This directive adds the dynlist overlay to the current database,
+or to the frontend, if used before any database instantiation; see
+.BR slapd.conf (5)
+for details.
+
+.LP
+This
+.B slapd.conf
+configuration option is defined for the dynlist overlay. It may have multiple
+occurrences, and it must appear after the
+.B overlay
+directive.
+.TP
+.B dynlist\-attrset <group-oc> [<URI>] <URL-ad> [[<mapped-ad>:]<member-ad>[+<memberOf-ad[@<static-oc>[*]] ...]
+The value
+.B group\-oc
+is the name of the objectClass that triggers the dynamic expansion of the
+data.
+
+The optional
+.B URI
+restricts expansion only to entries matching the \fIDN\fP,
+the \fIscope\fP and the \fIfilter\fP portions of the URI.
+
+The value
+.B URL-ad
+is the name of the attributeDescription that contains the URI that is
+expanded by the overlay; if none is present, no expansion occurs.
+If the intersection of the attributes requested by the search operation
+(or the asserted attribute for compares) and the attributes listed
+in the URI is empty, no expansion occurs for that specific URI.
+It must be a subtype of \fIlabeledURI\fP.
+
+The value
+.B member-ad
+is optional; if present, the overlay behaves as a dynamic group: this
+attribute will list the DN of the entries resulting from the internal search.
+In this case, the \fIattrs\fP portion of the URIs in the
+.B URL-ad
+attribute must be absent, and the \fIDN\fPs
+of all the entries resulting from the expansion of the URIs are listed
+as values of this attribute.
+Compares that assert the value of the
+.B member-ad
+attribute of entries with
+.B group-oc
+objectClass apply as if the DN of the entries resulting from the expansion
+of the URI were present in the
+.B group-oc
+entry as values of the
+.B member-ad
+attribute.
+If the optional
+.B memberOf-ad
+attribute is also specified, then it will be populated with the DNs of the
+dynamic groups that an entry is a member of.
+If the optional
+.B static-oc
+objectClass is also specified, then the memberOf attribute will also be
+populated with the DNs of the static groups that an entry is a member of.
+If the optional
+.B *
+character is also specified, then the member and memberOf values will be
+populated recursively, for nested groups. Note that currently nesting is
+only supported for Search operations, not Compares.
+
+Alternatively,
+.B mapped-ad
+can be used to remap attributes obtained through expansion.
+.B member-ad
+attributes are not filled by expanded DN, but are remapped as
+.B mapped-ad
+attributes. Multiple mapping statements can be used. The
+.B memberOf-ad
+option is not used in this case.
+
+.LP
+The dynlist overlay may be used with any backend, but it is mainly
+intended for use with local storage backends.
+In case the URI expansion is very resource-intensive and occurs frequently
+with well-defined patterns, one should consider adding a proxycache
+later on in the overlay stack.
+
+.SH AUTHORIZATION
+By default the expansions are performed using the identity of the current
+LDAP user.
+This identity may be overridden by setting the
+.B dgIdentity
+attribute in the group's entry to the DN of another LDAP user.
+In that case the dgIdentity will be used when expanding the URIs in the object.
+Setting the dgIdentity to a zero-length string will cause the expansions
+to be performed anonymously.
+Note that the dgIdentity attribute is defined in the
+.B dyngroup
+schema, and this schema must be loaded before the dgIdentity
+authorization feature may be used.
+If the
+.B dgAuthz
+attribute is also present in the group's entry, its values are used
+to determine what identities are authorized to use the
+.B dgIdentity
+to expand the group.
+Values of the
+.B dgAuthz
+attribute must conform to the (experimental) \fIOpenLDAP authz\fP syntax.
+When using dynamic memberOf in search filters, search access to the
+.B entryDN
+pseudo-attribute is required.
+
+.SH EXAMPLE
+This example collects all the email addresses of a database into a single
+entry; first of all, make sure that slapd.conf contains the directives:
+
+.LP
+.nf
+ include /path/to/dyngroup.schema
+ # ...
+
+ database <database>
+ # ...
+
+ overlay dynlist
+ dynlist\-attrset groupOfURLs memberURL
+.fi
+.LP
+and that slapd loads dynlist.la, if compiled as a run-time module;
+then add to the database an entry like
+.LP
+.nf
+ dn: cn=Dynamic List,ou=Groups,dc=example,dc=com
+ objectClass: groupOfURLs
+ cn: Dynamic List
+ memberURL: ldap:///ou=People,dc=example,dc=com?mail?sub?(objectClass=person)
+.fi
+
+If no <attrs> are provided in the URI, all (non-operational) attributes are
+collected.
+
+This example implements the dynamic group feature on the
+.B member
+attribute:
+
+.LP
+.nf
+ include /path/to/dyngroup.schema
+ # ...
+
+ database <database>
+ # ...
+
+ overlay dynlist
+ dynlist\-attrset groupOfURLs memberURL member
+.fi
+.LP
+
+A dynamic group with dgIdentity authorization could be created with an
+entry like
+.LP
+.nf
+ dn: cn=Dynamic Group,ou=Groups,dc=example,dc=com
+ objectClass: groupOfURLs
+ objectClass: dgIdentityAux
+ cn: Dynamic Group
+ memberURL: ldap:///ou=People,dc=example,dc=com??sub?(objectClass=person)
+ dgIdentity: cn=Group Proxy,ou=Services,dc=example,dc=com
+.fi
+
+
+This example extends the dynamic group feature to add a dynamic
+.B dgMemberOf
+attribute to all the members of a dynamic group:
+.LP
+.nf
+ include /path/to/dyngroup.schema
+ # ...
+
+ database <database>
+ # ...
+
+ overlay dynlist
+ dynlist\-attrset groupOfURLs memberURL member+dgMemberOf
+.fi
+.LP
+
+
+This example extends the dynamic memberOf feature to add the
+.B memberOf
+attribute to all the members of both static and dynamic groups:
+.LP
+.nf
+ include /path/to/dyngroup.schema
+ # ...
+
+ database <database>
+ # ...
+
+ overlay dynlist
+ dynlist\-attrset groupOfURLs memberURL member+memberOf@groupOfNames
+.fi
+.LP
+This dynamic memberOf feature can fully replace the functionality of the
+.BR slapo\-memberof (5)
+overlay.
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH BACKWARD COMPATIBILITY
+The dynlist overlay has been reworked with the 2.5 release to use
+a consistent namespace as with other overlays. As a side-effect the
+following cn=config parameters are deprecated and will be removed in
+a future release:
+.B olcDlAttrSet
+is replaced with olcDynListAttrSet
+.B olcDynamicList
+is replaced with olcDynListConfig
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd (8).
+The
+.BR slapo\-dynlist (5)
+overlay supports dynamic configuration via
+.BR back-config .
+
+.SH BUGS
+Filtering on dynamic groups may return incomplete results if the
+search operation uses the \fIpagedResults\fP control.
+
+.SH ACKNOWLEDGEMENTS
+.P
+This module was written in 2004 by Pierangelo Masarati for SysNet s.n.c.
+.P
+Attribute remapping was contributed in 2008 by Emmanuel Dreyfus.
diff --git a/doc/man/man5/slapo-homedir.5 b/doc/man/man5/slapo-homedir.5
new file mode 100644
index 0000000..5cd4ee8
--- /dev/null
+++ b/doc/man/man5/slapo-homedir.5
@@ -0,0 +1,130 @@
+.TH SLAPO-HOMEDIR 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation, All Rights Reserved.
+.\" Copying restrictions apply. See the COPYRIGHT file.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-homedir \- Home directory provisioning overlay
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B homedir
+overlay causes
+.BR slapd (8)
+to notice changes involving RFC-2307bis style user-objects and make
+appropriate changes to the local filesystem. This can be performed
+on both master and replica systems, so it is possible to perform
+remote home directory provisioning.
+.SH CONFIGURATION
+Both slapd.conf and back-config style configuration is supported.
+.TP
+.B overlay homedir
+This directive adds the homedir overlay to the current database,
+or to the frontend, if used before any database instantiation; see
+.BR slapd.conf (5)
+for details.
+.TP
+.B homedir\-skeleton\-path <pathname>
+.TP
+.B olcSkeletonPath: pathname
+These options set the path to the skeleton account directory.
+(Generally, /etc/skel) Files in this directory will be copied into
+newly created home directories. Copying is recursive and handles
+symlinks and fifos, but will skip most specials.
+.TP
+.B homedir\-min\-uidnumber <user id number>
+.TP
+.B olcMinimumUidNumber: number
+These options configure the minimum userid to use in any home
+directory attempt. This is a basic safety measure to prevent
+accidentally using system accounts. See REPLICATION for more flexible
+options for selecting accounts.
+.TP
+.B homedir\-regexp <regexp> <path>
+.TP
+.B olcHomedirRegexp: regexp path
+These options configure a set of regular expressions to use for
+matching and optionally remapping incoming
+.B homeDirectory
+attribute values to pathnames on the local filesystem. $number
+expansion is supported to access values captured in parentheses.
+
+For example, to accept any directory starting with \/home and use it
+verbatim on the local filesystem:
+
+.B homedir-regexp ^(/home/[\-_/a\-z0\-9]+)$ $1
+
+To match the same set of directories, but create them instead under
+\/export\/home, as is popular on Solaris NFS servers:
+
+.B homedir-regexp ^(/home/[\-_/a\-z0\-9]+)$ /export$1
+.TP
+.B homedir\-delete\-style style
+.TP
+.B olcHomedirDeleteStyle: style
+These options configure how deletes of posixAccount entries or their
+attributes are handled; valid styles are
+.B IGNORE,
+which does nothing, and
+.B DELETE,
+which immediately performs a recursive delete on the home directory,
+and
+.B ARCHIVE,
+which archives the home directory contents in a TAR file for later
+examination. The default is IGNORE. Use with caution. ARCHIVE
+requires homedir-archive-path to be set, or it functions similar to
+IGNORE.
+.TP
+.B homedir\-archive\-path <pathname>
+.TP
+.B olcArchivePath: pathname
+These options specify the destination path for TAR files created by
+the ARCHIVE delete style.
+.SH REPLICATION
+The homedir overlay can operate on either master or replica systems
+with no changes. See
+.BR slapd.conf (5)
+or
+.BR slapd\-config (5)
+for more information on configure syncrepl.
+
+Partial replication (e.g. with filters) is especially useful for
+providing different provisioning options to different sets of users.
+.SH BUGS
+DELETE, MOD, and MODRDN operations that remove the unix attributes
+when delete style is set to DELETE will recursively delete the (regex
+modified) home directory from the disk. Please be careful when
+deleting or changing values.
+
+MOD and MODRDN will correctly respond to homeDirectory changes and
+perform a non-destructive rename() operation on the filesystem, but
+this does not correctly retry with a recursive copy when moving
+between filesystems.
+
+The recursive copy/delete/chown/tar functions are not aware of ACLs,
+extended attributes, forks, sparse files, or hard links. Block and
+character device archival is non-portable, but should not be an issue
+in home directories, hopefully.
+
+Copying and archiving may not support files larger than 2GiB on some
+architectures. Bare POSIX UStar archives cannot support internal
+files larger than 8GiB. The current tar generator does not attempt to
+resolve uid/gid into symbolic names.
+
+No attempt is made to try to mkdir() the parent directories needed for
+a given home directory or archive path.
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.TP
+/etc/skel (or similar)
+source of new homedir files.
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd (8),
+RFC-2307, RFC-2307bis.
+.SH ACKNOWLEDGEMENTS
+.P
+This module was written in 2009 by Emily Backes for Symas Corporation.
diff --git a/doc/man/man5/slapo-memberof.5 b/doc/man/man5/slapo-memberof.5
new file mode 100644
index 0000000..45bf1b1
--- /dev/null
+++ b/doc/man/man5/slapo-memberof.5
@@ -0,0 +1,145 @@
+.TH SLAPO-MEMBEROF 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation, All Rights Reserved.
+.\" Copying restrictions apply. See the COPYRIGHT file.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-memberof \- Reverse Group Membership overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B memberof
+overlay to
+.BR slapd (8)
+allows automatic reverse group membership maintenance.
+Any time a group entry is modified, its members are modified as appropriate
+in order to keep a DN-valued "is member of" attribute updated with the DN
+of the group.
+.LP
+Note that this overlay is deprecated and support will be dropped in future
+OpenLDAP releases. Installations should use the \fBdynlist\fP
+overlay instead. Using this overlay in a replicated environment is especially
+discouraged.
+
+.SH CONFIGURATION
+The config directives that are specific to the
+.B memberof
+overlay must be prefixed by
+.BR memberof\- ,
+to avoid potential conflicts with directives specific to the underlying
+database or to other stacked overlays.
+
+.TP
+.B overlay memberof
+This directive adds the memberof overlay to the current database; see
+.BR slapd.conf (5)
+for details.
+
+.LP
+The following
+.B slapd.conf
+configuration options are defined for the memberof overlay.
+
+.TP
+.BI memberof\-group\-oc \ <group-oc>
+The value
+.I <group-oc>
+is the name of the objectClass that triggers the reverse group membership
+update.
+It defaults to \fIgroupOfNames\fP.
+
+.TP
+.BI memberof\-member\-ad \ <member-ad>
+The value
+.I <member-ad>
+is the name of the attribute that contains the names of the members
+in the group objects; it must be DN-valued.
+It defaults to \fImember\fP.
+
+.TP
+.BI memberof\-memberof\-ad \ <memberof-ad>
+The value
+.I <memberof-ad>
+is the name of the attribute that contains the names of the groups
+an entry is member of; it must be DN-valued. Its contents are
+automatically updated by the overlay.
+It defaults to \fImemberOf\fP.
+
+.TP
+.BI memberof\-dn \ <dn>
+The value
+.I <dn>
+contains the DN that is used as \fImodifiersName\fP for internal
+modifications performed to update the reverse group membership.
+It defaults to the \fIrootdn\fP of the underlying database.
+
+.TP
+.BI "memberof\-dangling {" ignore ", " drop ", " error "}"
+This option determines the behavior of the overlay when, during
+a modification, it encounters dangling references.
+The default is
+.IR ignore ,
+which may leave dangling references.
+Other options are
+.IR drop ,
+which discards those modifications that would result in dangling
+references, and
+.IR error ,
+which causes modifications that would result in dangling references
+to fail.
+
+.TP
+.BI memberof\-dangling\-error \ <error-code>
+If
+.BR memberof\-dangling
+is set to
+.IR error ,
+this configuration parameter can be used to modify the response code
+returned in case of violation. It defaults to "constraint violation",
+but other implementations are known to return "no such object" instead.
+
+.TP
+.BI "memberof\-refint {" true "|" FALSE "}"
+This option determines whether the overlay will try to preserve
+referential integrity or not.
+If set to
+.IR TRUE ,
+when an entry containing values of the "is member of" attribute is modified,
+the corresponding groups are modified as well.
+
+.LP
+The memberof overlay may be used with any backend that provides full
+read-write functionality, but it is mainly intended for use
+with local storage backends. The maintenance operations it performs
+are internal to the server on which the overlay is configured and
+are never replicated. Consumer servers should be configured with their
+own instances of the memberOf overlay if it is desired to maintain
+these memberOf attributes on the consumers. Note that slapo-memberOf
+is not compatible with syncrepl based replication, and should not be
+used in a replicated environment. An alternative is to use slapo-dynlist
+to emulate slapo-memberOf behavior.
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH BACKWARD COMPATIBILITY
+The memberof overlay has been reworked with the 2.5 release to use
+a consistent namespace as with other overlays. As a side-effect the
+following cn=config parameters are deprecated and will be removed in
+a future release:
+.B olcMemberOf
+is replaced with olcMemberOfConfig
+.SH SEE ALSO
+.BR slapo-dynlist (5),
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd (8).
+The
+.BR slapo\-memberof (5)
+overlay supports dynamic configuration via
+.BR back-config .
+.SH ACKNOWLEDGEMENTS
+.P
+This module was written in 2005 by Pierangelo Masarati for SysNet s.n.c.
+
diff --git a/doc/man/man5/slapo-otp.5 b/doc/man/man5/slapo-otp.5
new file mode 100644
index 0000000..7ff89c3
--- /dev/null
+++ b/doc/man/man5/slapo-otp.5
@@ -0,0 +1,138 @@
+.TH SLAPO_OTP 5 "2018/6/29" "SLAPO-OTP"
+.\" Copyright 2015-2022 The OpenLDAP Foundation.
+.\" Portions Copyright 2015 by Howard Chu, Symas Corp. All rights reserved.
+.\" Portions Copyright 2018 by Ondřej Kuzník, Symas Corp. All rights reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+slapo-otp \- OATH One-Time Password module
+.SH SYNOPSIS
+.B moduleload
+.I otp.la
+.SH DESCRIPTION
+The
+.B otp
+module allows time-based one-time password, AKA "authenticator-style", and
+HMAC-based one-time password authentication to be used in conjunction with
+a standard LDAP password for two-factor authentication.
+
+With this module, users would use their password, followed with the one-time
+password in the password prompt to authenticate.
+
+The password needed for a user to authenticate is calculated based on a counter
+(current time in case of TOTP) and a key that is referenced in the user's LDAP
+entry. Since the password is based on the time or number of uses, it changes
+periodically. Once used, it cannot be used again so keyloggers and
+shoulder-surfers are thwarted. A mobile phone application, such as the Google
+Authenticator or YubiKey (a
+.BR prover ),
+can be used to calculate the user's current one-time password, which is
+expressed as a (usually six-digit) number.
+
+Alternatively, the value can be calculated by some other application with
+access to the user's key and delivered to the user through SMS or some other
+channel. When prompted to authenticate, the user merely appends the code
+provided by the prover at the end of their password when authenticating.
+
+This implementation complies with
+.B RFC 4226 HOTP HMAC-Based One Time Passwords
+and
+.B RFC 6238 TOTP Time-based One Time Passwords
+and includes support for the SHA-1, SHA-256, and SHA-512 HMAC
+algorithms.
+
+The HMAC key used in the OTP computation is stored in the oathOTPToken entry referenced in
+the user's LDAP entry and the parameters are stored in the oathOTPParams LDAP
+entry referenced in the token.
+
+.SH CONFIGURATION
+Once the module is configured on the database, it will intercept LDAP simple
+binds for users whose LDAP entry has any of the
+.B oathOTPUser
+derived objectlasses attached to it. The attributes linking the user and the
+shared secret are:
+
+.RS
+.TP
+.B oathTOTPToken: <dn>
+Mandatory for
+.BR oathTOTPUser ,
+indicates that the named entry is designated to hold the time-based one-time
+password shared secret and the last password used.
+.TP
+.B oathHOTPToken: <dn>
+Mandatory for
+.BR oathHOTPUser ,
+indicates that the named entry is designated to hold the one-time password
+shared secret and the last password used.
+.TP
+.B oathTOTPParams: <dn>
+Mandatory for
+.BR oathTOTPToken ,
+indicates that the named entry is designated to hold the parameters to generate
+time-based one-time password shared secret: its length and algorithm to use as
+well as the length of each time step and the grace period.
+.TP
+.B oathHOTPParams: <dn>
+Mandatory for
+.BR oathHOTPToken ,
+indicates that the named entry is designated to hold the parameters to generate
+one-time password shared secret: its length and algorithm to use as well as the
+permitted number of passwords to skip.
+.RE
+
+The following parts of the OATH-LDAP schema are implemented.
+
+General attributes:
+
+.RS
+.TP
+.B oathSecret: <data>
+The shared secret is stored here as raw bytes.
+.TP
+.B oathOTPLength: <length>
+The password length, usually 6.
+.TP
+.B oathHMACAlgorithm: <OID>
+The OID of the hash algorithm to use as defined in RFC 8018.
+Supported algorithms include SHA1, SHA224, SHA256, SHA384 and SHA512.
+.RE
+
+The HOTP attributes:
+
+.RS
+.TP
+.B oathHOTPLookAhead: <number>
+The number of successive HOTP tokens that can be skipped.
+.TP
+.B oathHOTPCounter: <number>
+The order of the last HOTP token successfully redeemed by the user.
+.RE
+
+The TOTP attributes:
+
+.RS
+.TP
+.B oathTOTPTimeStepPeriod: <seconds>
+The length of the time-step period for TOTP calculation.
+.TP
+.B oathTOTPLastTimeStep: <number>
+The order of the last TOTP token successfully redeemed by the user.
+.TP
+.B oathTOTPTimeStepWindow: <number>
+The number of time periods around the current time to try when checking the
+password provided by the user.
+.TP
+.B oathTOTPTimeStepDrift: <number>
+If the client didn't provide the correct token but it still fit with
+oathTOTPTimeStepWindow above, this attribute records the current offset to
+provide for slow clock drift of the client device.
+.RE
+
+.SH "SEE ALSO"
+.BR slapd\-config (5).
+
+.SH ACKNOWLEDGEMENT
+This work was developed by Ondřej Kuzník and Howard Chu of Symas Corporation
+for inclusion in OpenLDAP Software.
+
+This work reuses the OATH-LDAP schema developed by Michael Ströder.
diff --git a/doc/man/man5/slapo-pbind.5 b/doc/man/man5/slapo-pbind.5
new file mode 100644
index 0000000..4a3c58f
--- /dev/null
+++ b/doc/man/man5/slapo-pbind.5
@@ -0,0 +1,61 @@
+.TH SLAPO-PBIND 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2010-2022 The OpenLDAP Foundation, All Rights Reserved.
+.\" Copying restrictions apply. See the COPYRIGHT file.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-pbind \- proxy bind overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B pbind
+overlay to
+.BR slapd (8)
+forwards Simple Binds on a local database to a remote
+LDAP server instead of processing them locally. The remote
+connection is managed using an instance of the ldap backend.
+
+.LP
+The
+.B pbind
+overlay uses a subset of the \fIldap\fP backend's config directives. They
+are described in more detail in
+.BR slapd\-ldap (5).
+
+Note: this overlay is built into the \fIldap\fP backend; it is not a
+separate module.
+
+.TP
+.B overlay pbind
+This directive adds the proxy bind overlay to the current backend.
+The proxy bind overlay may be used with any backend, but it is mainly
+intended for use with local storage backends.
+
+.TP
+.B uri <ldapurl>
+LDAP server to use.
+
+.TP
+.B tls <TLS parameters>
+Specify the use of TLS.
+
+.TP
+.B network\-timeout <time>
+Set the network timeout.
+
+.TP
+.B quarantine <quarantine parameters>
+Turns on quarantine of URIs that returned
+.IR LDAP_UNAVAILABLE .
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd\-ldap (5),
+.BR slapd (8).
+.SH AUTHOR
+Howard Chu
diff --git a/doc/man/man5/slapo-pcache.5 b/doc/man/man5/slapo-pcache.5
new file mode 100644
index 0000000..1425897
--- /dev/null
+++ b/doc/man/man5/slapo-pcache.5
@@ -0,0 +1,327 @@
+.TH SLAPO-PCACHE 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation, All Rights Reserved.
+.\" Copying restrictions apply. See the COPYRIGHT file.
+.\" Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+.\" $OpenLDAP$
+.SH NAME
+slapo\-pcache \- proxy cache overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B pcache
+overlay to
+.BR slapd (8)
+allows caching of LDAP search requests (queries) in a local database.
+For an incoming query, the
+proxy cache determines its corresponding \fBtemplate\fP. If the template
+was specified as cacheable using the \fBpcacheTemplate\fP directive
+and the request is contained in a cached request, it is answered from
+the proxy cache.
+Otherwise, the search is performed as usual and cacheable search results
+are saved in the cache for use in future queries.
+.LP
+
+A template is defined by a filter string and an index identifying a set of
+attributes. The \fBtemplate string\fP for a query can be obtained by
+removing assertion values from the RFC 4515 representation of its search
+filter. A query belongs to a template if its template string and set of
+projected attributes correspond to a cacheable template.
+Examples of template strings are \fB(mail=)\fP, \fB(|(sn=)(cn=))\fP,
+\fB(&(sn=)(givenName=))\fP.
+
+.LP
+The config directives that are specific to the
+.B pcache
+overlay can be prefixed by
+.BR pcache\- ,
+to avoid conflicts with directives specific to the underlying database
+or to other stacked overlays. This may be particularly useful for those
+directives that refer to the backend used for local storage.
+The following cache specific directives can be used to configure the proxy
+cache:
+.TP
+.B overlay pcache
+This directive adds the proxy cache overlay to the current backend. The
+proxy cache overlay may be used with any backend but is intended for use
+with the
+.BR ldap ,
+.BR meta ,
+and
+.BR sql
+backends. Please note that the underlying backend must have a configured
+.BR rootdn.
+.TP
+.B pcache <database> <max_entries> <numattrsets> <entry_limit> <cc_period>
+The directive enables proxy caching in the current backend and sets general
+cache parameters. A <database> backend will be used internally to maintain
+the cached entries. The chosen database will need to be configured as well,
+as shown below. Cache replacement is invoked when the cache size grows to
+<max_entries> entries and continues till the cache size drops below this size.
+<numattrsets> should be equal to the number of following \fBpcacheAttrset\fP
+directives. Queries are cached only if they correspond to a cacheable template
+(specified by the \fBpcacheTemplate\fP directive) and the number of entries
+returned is less than <entry_limit>. Consistency check is performed every
+<cc_period> duration (specified in secs). In each cycle queries with expired
+"time to live(\fBTTL\fP)" are removed. A sample cache configuration is:
+.LP
+.RS
+pcache \fBmdb 10000 1 50 100\fP
+.RE
+
+.TP
+.B pcacheAttrset <index> <attrs...>
+Used to associate a set of attributes <attrs..> with an <index>. Each attribute
+set is associated with an integer from 0 to <numattrsets>\-1. These indices are
+used by the \fBpcacheTemplate\fP directive to define cacheable templates.
+A set of attributes cannot be empty. A set of attributes can contain the
+special attributes "*" (all user attributes), "+" (all operational attributes)
+or both; in the latter case, any other attribute is redundant and should
+be avoided for clarity. A set of attributes can contain "1.1" as the only
+attribute; in this case, only the presence of the entries is cached.
+Attributes prefixed by "undef:" need not be present in the schema.
+
+.TP
+.B pcacheMaxQueries <queries>
+Specify the maximum number of queries to cache. The default is 10000.
+
+.TP
+.B pcacheValidate { TRUE | FALSE }
+Check whether the results of a query being cached can actually be returned
+from the cache by the proxy DSA. When enabled, the entries being returned
+while caching the results of a query are checked to ensure consistency
+with the schema known to the proxy DSA. In case of failure, the query
+is not cached. By default, the check is off.
+
+.TP
+.B pcacheOffline { TRUE | FALSE }
+Set the cache to offline mode. While offline, the consistency checker
+will be stopped and no expirations will occur. This allows the cache
+contents to be used indefinitely while the proxy is cut off from network
+access to the remote DSA. The default is FALSE, i.e. consistency
+checks and expirations will be performed.
+
+.TP
+.B pcachePersist { TRUE | FALSE }
+Specify whether the cached queries should be saved across restarts
+of the caching proxy, to provide hot startup of the cache. Only non-expired
+queries are reloaded. The default is FALSE.
+
+.BR CAVEAT :
+of course, the configuration of the proxy cache must not change
+across restarts; the pcache overlay does not perform any consistency
+checks in this sense.
+In detail, this option should be disabled unless the existing
+.B pcacheAttrset
+and
+.B pcacheTemplate
+directives are not changed neither in order nor in contents.
+If new sets and templates are added, or if other details of the pcache
+overlay configuration changed, this feature should not be affected.
+
+.TP
+.B pcacheTemplate <template_string> <attrset_index> <ttl> [<negttl> [<limitttl> [<ttr>]]]
+Specifies a cacheable template and "time to live" <ttl> of queries
+belonging to the template. An optional <negttl> can be used to specify
+that negative results (i.e., queries that returned zero entries)
+should also be cached for the specified amount of time. Negative
+results are not cached by default (<negttl> set to 0).
+An optional <limitttl> can be used to specify that results
+hitting a sizelimit should also be cached for the specified amount of time.
+Results hitting a sizelimit are not cached by default (<limitttl> set to 0).
+An optional <ttr> "time to refresh" can be used to specify that cached
+entries should be automatically refreshed after a certain time. Entries
+will only be refreshed while they have not expired, so the <ttl> should
+be larger than the <ttr> for this option to be useful. Entries are not
+refreshed by default (<ttr> set to 0).
+
+.TP
+.B pcacheBind <filter_template> <attrset_index> <ttr> <scope> <base>
+Specifies a template for caching Simple Bind credentials based on an
+already defined \fBpcacheTemplate\fP. The <filter_template> is similar
+to a <template_string> except that it may have some values present. Its
+purpose is to allow the overlay to generate filters similar to what other
+applications do when they do a Search immediately before a Bind. E.g.,
+if a client like nss_ldap is configured to search for a user with the
+filter "(&(objectClass=posixAccount)(uid=<username>))" then the corresponding
+template "(&(objectClass=posixAccount)(uid=))" should be used here. When
+converted to a regular template e.g. "(&(objectClass=)(uid=))" this
+template and the <attrset_index> must match an already defined
+\fBpcacheTemplate\fP clause. The "time to refresh" <ttr> determines the
+time interval after which the cached credentials may be refreshed. The
+first Bind request that occurs after that time will trigger the refresh
+attempt. Refreshes are not performed when the overlay is Offline. There
+is no "time to live" parameter for the Bind credentials; the credentials
+will expire according to the \fBpcacheTemplate\fP ttl. The <scope> and
+<base> should match the search scope and base used by the authentication
+clients. The cached credentials are not stored in cleartext, they are
+hashed using the default password hash.
+By default Bind caching is not enabled.
+
+.TP
+.B pcachePosition { head | tail }
+Specifies whether the response callback should be placed at the
+.B tail
+(the default) or at the
+.B head
+(actually, wherever the stacking sequence would make it appear)
+of the callback list. This affects how the overlay interacts with other
+overlays, since the proxycache overlay should be executed as early
+as possible (and thus configured as late as possible), to get
+a chance to return the cached results; however, if executed early
+at response, it would cache entries that may be later "massaged"
+by other databases and thus returned \fIafter\fP massaging the first
+time, and \fIbefore\fP massaging when cached.
+
+.TP
+There are some constraints:
+
+all values must be positive;
+
+.B <entry_limit>
+must be less than or equal to
+.BR <max_entries> ;
+
+.B <numattrsets>
+attribute sets SHOULD be defined by using the directive
+.BR pcacheAttrset ;
+
+all attribute sets SHOULD be referenced by (at least) one
+.B pcacheTemplate
+directive;
+
+.LP
+The following adds a template with filter string \fB(&(sn=)(givenName=))\fP
+and attributes mail, postaladdress, telephonenumber and a TTL of 1 hour.
+.LP
+.RS
+.nf
+pcacheAttrset \fB0 mail postaladdress telephonenumber\fP
+pcacheTemplate \fB(&(sn=)(givenName=)) 0 3600\fP
+.fi
+.RE
+
+.LP
+Directives for configuring the underlying database must also be given, as
+shown here:
+.LP
+.RS
+.nf
+directory /var/tmp/cache
+cachesize 100
+.fi
+.RE
+.LP
+Any valid directives for the chosen database type may be used. Indexing
+should be used as appropriate for the queries being handled. In addition,
+an equality index on the \fBpcacheQueryid\fP attribute should be configured, to
+assist in the removal of expired query data.
+.SH BACKWARD COMPATIBILITY
+The configuration keywords have been renamed and the older form is
+deprecated. These older keywords are still recognized but may disappear
+in future releases.
+
+.TP
+.B proxycache
+use pcache
+
+.TP
+.B proxyattrset
+use pcacheAttrset
+
+.TP
+.B proxycachequeries
+use pcacheMaxQueries
+
+.TP
+.B proxycheckcacheability
+use pcacheValidate
+
+.TP
+.B proxysavequeries
+use pcachePersist
+
+.TP
+.B proxytemplate
+use pcacheTemplate
+
+.TP
+.B response-callback
+use pcachePosition
+
+.SH CAVEATS
+Caching data is prone to inconsistencies because updates on the remote server
+will not be reflected in the response of the cache at least (and at most)
+for the duration of the
+.B pcacheTemplate
+.BR TTL .
+These inconsistencies can be minimized by careful use of the TTR.
+
+The proxy cache overlay requires a full result set of data to properly
+function. Therefore it will strip out the paged results control if it is
+requested by the client.
+
+The remote server should expose the
+.B objectClass
+attribute because the underlying database that actually caches the entries
+may need it for optimal local processing of the queries.
+
+The proxy server should contain all the schema information required for caching.
+Significantly, it needs the schema of attributes used in the query templates.
+If the objectClass attribute is used in a query template, it needs the definition
+of the objectClasses of the entries it is supposed to cache.
+It is the responsibility of the proxy administrator to keep the proxy schema
+lined up with that of the proxied server.
+
+Another potential (and subtle) inconsistency may occur when data is retrieved
+with different identities and specific per-identity access control
+is enforced by the remote server.
+If data was retrieved with an identity that collected only partial results
+because of access rules enforcement on the remote server, other users
+with different access privileges on the remote server will get different
+results from the remote server and from the cache.
+If those users have higher access privileges on the remote server, they will
+get from the cache only a subset of the results they would get directly
+from the remote server; but if they have lower access privileges, they will
+get from the cache a superset of the results they would get directly
+from the remote server.
+Either occurrence may or may not be acceptable, based on the security policy
+of the cache and of the remote server.
+It is important to note that in this case the proxy is violating the security
+of the remote server by disclosing to an identity data that was collected
+by another identity.
+For this reason, it is suggested that, when using
+.BR back-ldap ,
+proxy caching be used in conjunction with the
+.I identity assertion
+feature of
+.BR slapd\-ldap (5)
+(see the
+.B idassert\-bind
+and the
+.B idassert\-authz
+statements), so that remote server interrogation occurs with a vanilla identity
+that has some relatively high
+.B search
+and
+.B read
+access privileges, and the "real" access control is delegated to the proxy's ACLs.
+Beware that since only the cached fraction of the real datum is available
+to the cache, it may not be possible to enforce the same access rules that
+are defined on the remote server.
+When security is a concern, cached proxy access must be carefully tailored.
+.SH FILES
+
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd\-ldap (5),
+.BR slapd\-meta (5),
+.BR slapd\-sql (5),
+.BR slapd (8).
+.SH AUTHOR
+Originally implemented by Apurva Kumar as an extension to back-meta;
+turned into an overlay by Howard Chu.
diff --git a/doc/man/man5/slapo-ppolicy.5 b/doc/man/man5/slapo-ppolicy.5
new file mode 100644
index 0000000..8d12ea2
--- /dev/null
+++ b/doc/man/man5/slapo-ppolicy.5
@@ -0,0 +1,1060 @@
+.TH SLAPO_PPOLICY 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2004-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-ppolicy \- Password Policy overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+.LP
+The
+.B ppolicy
+overlay
+is an implementation of the most recent IETF Password
+Policy proposal for LDAP. When instantiated, it intercepts,
+decodes and applies specific password policy controls to overall
+use of a backend database, changes to user password fields, etc.
+.P
+The overlay provides a variety of password control mechanisms. They
+include password aging -- both minimum and maximum ages, password
+reuse and duplication control, account time-outs, mandatory password
+resets, acceptable password content, and even grace logins.
+Different groups of users may be associated with different password
+policies, and there is no limit to the number of password policies
+that may be created.
+.P
+Note that some of the policies do not take effect when the operation
+is performed with the
+.B rootdn
+identity; all the operations, when performed with any other identity,
+may be subjected to constraints, like access control. This overlay
+requires a rootdn to be configured on the database.
+.P
+During password update, an identity with
+.B manage
+access to the userPassword attribute is considered a password
+administrator where relevant to the IETF Password Policy proposal.
+.P
+Note that the IETF Password Policy proposal for LDAP makes sense
+when considering a single-valued password attribute, while
+the userPassword attribute allows multiple values. This implementation
+enforces a single value for the userPassword attribute, despite
+its specification.
+.P
+In addition to supporting the IETF Password Policy, this module
+supports the SunDS Account Usability control (1.3.6.1.4.1.42.2.27.9.5.8)
+on search requests and can send the Netscape Password validity controls
+when configured to do so.
+
+.SH CONFIGURATION
+These
+.B slapd.conf
+configuration options apply to the ppolicy overlay. They should appear
+after the
+.B overlay
+directive.
+.TP
+.B ppolicy_default <policyDN>
+Specify the DN of the pwdPolicy object to use when no specific policy is
+set on a given user's entry. If there is no specific policy for an entry
+and no default is given, then no policies will be enforced.
+.TP
+.B ppolicy_forward_updates
+Specify that policy state changes that result from Bind operations (such
+as recording failures, lockout, etc.) on a consumer should be forwarded
+to a provider instead of being written directly into the consumer's local
+database. This setting is only useful on a replication consumer, and
+also requires the
+.B updateref
+setting and
+.B chain
+overlay to be appropriately configured.
+.TP
+.B ppolicy_hash_cleartext
+Specify that cleartext passwords present in Add and Modify requests should
+be hashed before being stored in the database. This violates the X.500/LDAP
+information model, but may be needed to compensate for LDAP clients that
+don't use the Password Modify extended operation to manage passwords. It
+is recommended that when this option is used that compare, search, and
+read access be denied to all directory users.
+.TP
+.B ppolicy_use_lockout
+A client will always receive an LDAP
+.B InvalidCredentials
+response when
+Binding to a locked account. By default, when a Password Policy control
+was provided on the Bind request, a Password Policy response will be
+included with no special error code set. This option changes the
+Password Policy response to include the
+.B AccountLocked
+error code. Note
+that sending the
+.B AccountLocked
+error code provides useful information
+to an attacker; sites that are sensitive to security issues should not
+enable this option.
+.TP
+.B ppolicy_send_netscape_controls
+If set, ppolicy will send the password policy expired (2.16.840.1.113730.3.4.4)
+and password policy expiring (2.16.840.1.113730.3.4.5) controls when
+appropriate. The controls are not sent for bind requests where the Password
+policy control has already been requested. Default is not to send the controls.
+
+.SH OBJECT CLASS
+The
+.B ppolicy
+overlay depends on the
+.B pwdPolicy
+object class. The definition of that class is as follows:
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.2.1
+ NAME 'pwdPolicy'
+ AUXILIARY
+ SUP top
+ MUST ( pwdAttribute )
+ MAY (
+ pwdMinAge $ pwdMaxAge $ pwdInHistory $
+ pwdCheckQuality $ pwdMinLength $ pwdMaxLength $
+ pwdExpireWarning $ pwdGraceAuthnLimit $
+ pwdGraceExpiry $ pwdLockout $ pwdLockoutDuration $
+ pwdMaxFailure $ pwdFailureCountInterval $
+ pwdMustChange $ pwdAllowUserChange $
+ pwdSafeModify $ pwdMaxRecordedFailure $
+ pwdMinDelay $ pwdMaxDelay $ pwdMaxIdle ) )
+.RE
+
+The
+.B pwdPolicy
+class is not structural, and so entries using it require another,
+structural, object class. The
+.B namedPolicy
+object class is a good choice.
+.B namedPolicy
+requires a
+.B cn
+attribute, suitable as the policy entry's rDN.
+
+This implementation also provides an additional
+.B pwdPolicyChecker
+objectclass, used for password quality checking (see below).
+.LP
+.RS 4
+( 1.3.6.1.4.1.4754.2.99.1
+ NAME 'pwdPolicyChecker'
+ AUXILIARY
+ SUP top
+ MAY ( pwdCheckModule $ pwdCheckModuleArg ) )
+.RE
+.P
+Every account that should be subject to password policy control should
+have a
+.B
+pwdPolicySubentry
+attribute containing the DN of a valid
+.B pwdPolicy
+entry, or they can simply use the configured default.
+In this way different users may be managed according to
+different policies.
+
+.SH OBJECT CLASS ATTRIBUTES
+.P
+Each one of the sections below details the meaning and use of a particular
+attribute of this
+.B pwdPolicy
+object class.
+.P
+
+.B pwdAttribute
+.P
+This attribute contains the name of the attribute to which the password
+policy is applied. For example, the password policy may be applied
+to the
+.B userPassword
+attribute.
+.P
+Note: in this implementation, the only
+value accepted for
+.B pwdAttribute
+is
+.IR " userPassword ".
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.1
+ NAME 'pwdAttribute'
+ EQUALITY objectIdentifierMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
+.RE
+
+.B pwdMinAge
+.P
+This attribute contains the number of seconds that must elapse
+between modifications allowed to the password. If this attribute
+is not present, zero seconds is assumed (i.e. the password may be
+modified whenever and however often is desired).
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.2
+ NAME 'pwdMinAge'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE\-VALUE )
+.RE
+
+.B pwdMaxAge
+.P
+This attribute contains the number of seconds after which a modified
+password will expire. If this attribute is not present, or if its
+value is zero (0), then passwords will not expire.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.3
+ NAME 'pwdMaxAge'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE\-VALUE )
+.RE
+
+.B pwdInHistory
+.P
+This attribute is used to specify the maximum number of used
+passwords that will be stored in the
+.B pwdHistory
+attribute. If the
+.B pwdInHistory
+attribute is not present, or if its value is
+zero (0), used passwords will not be stored in
+.B pwdHistory
+and thus any previously-used password may be reused.
+No history checking occurs if the password is being modified by the
+.BR rootdn ,
+although the password is saved in the history.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.4
+ NAME 'pwdInHistory'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE\-VALUE )
+.RE
+
+.B pwdCheckQuality
+.P
+This attribute indicates if and how password syntax will be checked
+while a password is being modified or added. If this attribute is
+not present, or its value is zero (0), no syntax checking will be
+done. If its value is one (1), the server will check the syntax,
+and if the server is unable to check the syntax,
+whether due to a client-side hashed password or some other reason,
+it will be
+accepted. If its value is two (2), the server will check the syntax,
+and if the server is unable to check the syntax it will return an
+error refusing the password.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.5
+ NAME 'pwdCheckQuality'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE\-VALUE )
+.RE
+
+.B pwdMinLength
+.P
+When syntax checking is enabled
+(see also the
+.B pwdCheckQuality
+attribute), this attribute contains the minimum
+length in bytes that will be accepted in a password. If this
+attribute is not present, minimum password length is not
+enforced. If the server is unable to check the length of the password,
+whether due to a client-side hashed password or some other reason,
+the server will, depending on the
+value of
+.BR pwdCheckQuality ,
+either accept the password
+without checking it (if
+.B pwdCheckQuality
+is zero (0) or one (1)) or refuse it (if
+.B pwdCheckQuality
+is two (2)). If the number of characters should be enforced with regards
+to a particular encoding, the use of an appropriate pwdCheckModule is
+required.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.6
+ NAME 'pwdMinLength'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE\-VALUE )
+.RE
+
+.B pwdMaxLength
+.P
+When syntax checking is enabled
+(see also the
+.B pwdCheckQuality
+attribute), this attribute contains the maximum
+length in bytes that will be accepted in a password. If this
+attribute is not present, maximum password length is not
+enforced. If the server is unable to check the length of the password,
+whether due to a client-side hashed password or some other reason,
+the server will, depending on the
+value of
+.BR pwdCheckQuality ,
+either accept the password
+without checking it (if
+.B pwdCheckQuality
+is zero (0) or one (1)) or refuse it (if
+.B pwdCheckQuality
+is two (2)). If the number of characters should be enforced with regards
+to a particular encoding, the use of an appropriate pwdCheckModule is
+required.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.31
+ NAME 'pwdMaxLength'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE\-VALUE )
+.RE
+
+.B pwdExpireWarning
+.P
+This attribute contains the maximum number of seconds before a
+password is due to expire that expiration warning messages will be
+returned to a user who is authenticating to the directory.
+If this attribute is not
+present, or if the value is zero (0), no warnings will be sent.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.7
+ NAME 'pwdExpireWarning'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE\-VALUE )
+.RE
+
+.B pwdGraceAuthnLimit
+.P
+This attribute contains the number of times that an expired password
+may be used to authenticate a user to the directory. If this
+attribute is not present or if its value is zero (0), users with
+expired passwords will not be allowed to authenticate to the
+directory.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.8
+ NAME 'pwdGraceAuthnLimit'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE\-VALUE )
+.RE
+
+.B pwdGraceExpiry
+.P
+This attribute specifies the number of seconds the grace
+authentications are valid. If this attribute is not present or if
+the value is zero (0), there is no time limit on the grace
+authentications.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.30
+ NAME 'pwdGraceExpiry'
+ EQUALITY integerMatch
+ ORDERING integerOrderingMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE\-VALUE )
+.RE
+
+.B pwdLockout
+.P
+This attribute specifies the action that should be taken
+by the directory when a user has made a number of failed attempts
+to authenticate to the directory. If
+.B pwdLockout
+is set (its value is "TRUE"), the user will not be allowed to
+attempt to authenticate to the directory after there have been a
+specified number of consecutive failed bind attempts. The maximum
+number of consecutive failed bind attempts allowed is specified by
+the
+.B pwdMaxFailure
+attribute. If
+.B pwdLockout
+is not present, or if its value is "FALSE", the password may be
+used to authenticate no matter how many consecutive failed bind
+attempts have been made.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.9
+ NAME 'pwdLockout'
+ EQUALITY booleanMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+ SINGLE\-VALUE )
+.RE
+
+.B pwdLockoutDuration
+.P
+This attribute contains the number of seconds during
+which the password cannot be used to authenticate the
+user to the directory due to too many consecutive failed
+bind attempts.
+(See also
+.B pwdLockout
+and
+.BR pwdMaxFailure .)
+If
+.B pwdLockoutDuration
+is not present, or if its value is zero (0), the password
+cannot be used to authenticate the user to the directory
+again until it is reset by an administrator.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.10
+ NAME 'pwdLockoutDuration'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE\-VALUE )
+.RE
+
+.B pwdMaxFailure
+.P
+This attribute contains the number of consecutive failed bind
+attempts after which the password may not be used to authenticate
+a user to the directory.
+If
+.B pwdMaxFailure
+is not present, or its value is zero (0), then a user will
+be allowed to continue to attempt to authenticate to
+the directory, no matter how many consecutive failed
+bind attempts have occurred with that user's DN.
+(See also
+.B pwdLockout
+and
+.BR pwdLockoutDuration .)
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.11
+ NAME 'pwdMaxFailure'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE\-VALUE )
+.RE
+
+.B pwdMaxRecordedFailure
+.P
+This attribute contains the maximum number of failed bind
+attempts to store in a user's entry.
+If
+.B pwdMaxRecordedFailure
+is not present, or its value is zero (0), then it defaults
+to the value of
+.BR pwdMaxFailure .
+If that value is also 0, the default is 5.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.32
+ NAME 'pwdMaxRecordedFailure'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE\-VALUE )
+.RE
+
+.B pwdFailureCountInterval
+.P
+This attribute contains the number of seconds after which old
+consecutive failed bind attempts are purged from the failure counter,
+even though no successful authentication has occurred.
+If
+.B pwdFailureCountInterval
+is not present, or its value is zero (0), the failure
+counter will only be reset by a successful authentication.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.12
+ NAME 'pwdFailureCountInterval'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE\-VALUE )
+.RE
+
+.B pwdMustChange
+.P
+This attribute specifies whether users must change their passwords
+when they first bind to the directory after a password is set or
+reset by the administrator, or not. If
+.B pwdMustChange
+has a value of "TRUE", users must change their passwords when they
+first bind to the directory after a password is set or reset by
+the administrator. If
+.B pwdMustChange
+is not present, or its value is "FALSE",
+users are not required to change their password upon binding after
+the administrator sets or resets the password.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.13
+ NAME 'pwdMustChange'
+ EQUALITY booleanMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+ SINGLE\-VALUE )
+.RE
+
+.B pwdAllowUserChange
+.P
+This attribute specifies whether users are allowed to change their own
+passwords or not. If
+.B pwdAllowUserChange
+is set to "TRUE", or if the attribute is not present, users will be
+allowed to change their own passwords. If its value is "FALSE",
+users will not be allowed to change their own passwords.
+.LP
+Note: this implies that when
+.B pwdAllowUserChange
+is set to "TRUE",
+users will still be able to change the password of another user,
+subjected to access control.
+This restriction only applies to modifications of ones's own password.
+It should also be noted that
+.B pwdAllowUserChange
+was defined in the specification to provide rough access control
+to the password attribute in implementations that do not allow fine-grain
+access control.
+Since OpenLDAP provides fine-grain access control, the use of this attribute
+is discouraged; ACLs should be used instead
+(see
+.BR slapd.access (5)
+for details).
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.14
+ NAME 'pwdAllowUserChange'
+ EQUALITY booleanMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+ SINGLE\-VALUE )
+.RE
+
+.B pwdSafeModify
+.P
+This attribute denotes whether the user's existing password must be sent
+along with their new password when changing a password. If
+.B pwdSafeModify
+is set to "TRUE", the existing password must be sent
+along with the new password. If the attribute is not present, or
+its value is "FALSE", the existing password need not be sent
+along with the new password.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.15
+ NAME 'pwdSafeModify'
+ EQUALITY booleanMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+ SINGLE\-VALUE )
+.RE
+
+.B pwdMinDelay
+.P
+This attribute specifies the number of seconds to delay responding to
+the first failed authentication attempt. If this attribute is not
+set or is zero (0), no delays will be used.
+.B pwdMaxDelay
+must also be specified if
+.B pwdMinDelay
+is set.
+
+Note that this implementation uses a variable lockout instead of
+delaying the bind response.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.24
+ NAME 'pwdMinDelay'
+ EQUALITY integerMatch
+ ORDERING integerOrderingMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE\-VALUE )
+.RE
+
+.B pwdMaxDelay
+.P
+This attribute specifies the maximum number of seconds to delay when
+responding to a failed authentication attempt. The time specified in
+.B pwdMinDelay
+is used as the starting time and is then doubled on each failure until
+the delay time is greater than or equal to
+.B pwdMaxDelay
+(or a successful authentication occurs, which resets the failure
+counter).
+.B pwdMinDelay
+must also be specified if
+.B pwdMaxDelay
+is set.
+
+Note that this implementation uses a variable lockout instead of
+delaying the bind response.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.25
+ NAME 'pwdMaxDelay'
+ EQUALITY integerMatch
+ ORDERING integerOrderingMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE\-VALUE )
+.RE
+
+.B pwdMaxIdle
+.P
+This attribute specifies the number of seconds an account may remain
+unused before it becomes locked. If this attribute is not set or is
+zero (0), no check is performed. For this to be enforced,
+.B lastbind
+functionality needs to be enabled on the database, that is
+.B olcLastBind
+is set to
+.BR TRUE .
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.26
+ NAME 'pwdMaxIdle'
+ EQUALITY integerMatch
+ ORDERING integerOrderingMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE\-VALUE )
+.RE
+
+.BR pwdCheckModule / pwdCheckModuleArg
+.P
+This attribute names a user-defined loadable module that must
+instantiate the check_password() function. This function
+will be called to further check a new password if
+.B pwdCheckQuality
+is set to one (1) or two (2),
+after all of the built-in password compliance checks have
+been passed. This function will be called according to this
+function prototype:
+.RS 4
+int
+.I check_password
+(char *pPasswd, char **ppErrStr, Entry *pEntry, struct berval *pArg);
+.RE
+The
+.B pPasswd
+parameter contains the clear-text user password, the
+.B ppErrStr
+parameter contains a double pointer that allows the function
+to return human-readable details about any error it encounters.
+
+The
+.B pEntry
+parameter is optional, if non-NULL, carries a pointer to the
+entry whose password is being checked.
+
+The optional
+.B pArg
+parameter points to a
+.B struct berval
+containing the value of
+.B pwdCheckModuleArg
+in the effective password policy, if set, otherwise NULL.
+
+If
+.B ppErrStr
+is NULL, then
+.I funcName
+must NOT attempt to use it/them.
+A return value of LDAP_SUCCESS from the called
+function indicates that the password is ok, any other value
+indicates that the password is unacceptable. If the password is
+unacceptable, the server will return an error to the client, and
+.B ppErrStr
+may be used to return a human-readable textual explanation of the
+error. The error string must be dynamically allocated as it will
+be free()'d by slapd.
+.LP
+.RS 4
+( 1.3.6.1.4.1.4754.1.99.1
+ NAME 'pwdCheckModule'
+ EQUALITY caseExactIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE\-VALUE )
+
+( 1.3.6.1.4.1.4754.1.99.2
+ NAME 'pwdCheckModuleArg'
+ EQUALITY octetStringMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.40
+ DESC 'Argument to pass to check_password() function'
+ SINGLE\-VALUE )
+.RE
+.P
+Note:
+The user-defined loadable module named by
+.B pwdCheckModule
+must be in
+.B slapd's
+standard executable search PATH.
+.P
+Note:
+.B pwdCheckModule
+is a non-standard extension to the LDAP password
+policy proposal.
+
+.SH OPERATIONAL ATTRIBUTES
+.P
+The operational attributes used by the
+.B ppolicy
+module are stored in the user's entry. Most of these attributes
+are not intended to be changed directly by users; they are there
+to track user activity. They have been detailed here so that
+administrators and users can both understand the workings of
+the
+.B ppolicy
+module.
+
+.P
+Note that the current IETF Password Policy proposal does not define
+how these operational attributes are expected to behave in a
+replication environment. In general, authentication attempts on
+a replica server only affect the copy of the operational attributes
+on that replica and will not affect any attributes for
+a user's entry on the provider. Operational attribute changes
+resulting from authentication attempts on a provider
+will usually replicate to the replicas (and also overwrite
+any changes that originated on the replica).
+These behaviors are not guaranteed and are subject to change
+when a formal specification emerges.
+
+.B userPassword
+.P
+The
+.B userPassword
+attribute is not strictly part of the
+.B ppolicy
+module. It is, however, the attribute that is tracked and controlled
+by the module. Please refer to the standard OpenLDAP schema for
+its definition.
+
+.B pwdPolicySubentry
+.P
+This attribute refers directly to the
+.B pwdPolicy
+subentry that is to be used for this particular directory user.
+If
+.B pwdPolicySubentry
+exists, it must contain the DN of a valid
+.B pwdPolicy
+object. If it does not exist, the
+.B ppolicy
+module will enforce the default password policy rules on the
+user associated with this authenticating DN. If there is no
+default, or the referenced subentry does not exist, then no
+policy rules will be enforced.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.23
+ NAME 'pwdPolicySubentry'
+ DESC 'The pwdPolicy subentry in effect for
+ this object'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ SINGLE\-VALUE
+ USAGE directoryOperation)
+.RE
+
+.B pwdChangedTime
+.P
+This attribute denotes the last time that the entry's password was
+changed. This value is used by the password expiration policy to
+determine whether the password is too old to be allowed to be used
+for user authentication. If
+.B pwdChangedTime
+does not exist, the user's password will not expire.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.16
+ NAME 'pwdChangedTime'
+ DESC 'The time the password was last changed'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
+ EQUALITY generalizedTimeMatch
+ ORDERING generalizedTimeOrderingMatch
+ SINGLE\-VALUE
+ NO\-USER\-MODIFICATION
+ USAGE directoryOperation)
+.RE
+
+.B pwdAccountLockedTime
+.P
+This attribute contains the time that the user's account was locked.
+If the account has been locked, the password may no longer be used to
+authenticate the user to the directory. If
+.B pwdAccountLockedTime
+is set to 000001010000Z, the user's account has been permanently locked
+and may only be unlocked by an administrator. Note that account locking
+only takes effect when the
+.B pwdLockout
+password policy attribute is set to "TRUE".
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.17
+ NAME 'pwdAccountLockedTime'
+ DESC 'The time an user account was locked'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
+ EQUALITY generalizedTimeMatch
+ ORDERING generalizedTimeOrderingMatch
+ SINGLE\-VALUE
+ USAGE directoryOperation)
+.RE
+
+.B pwdFailureTime
+.P
+This attribute contains the timestamps of each of the consecutive
+authentication failures made upon attempted authentication to this
+DN (i.e. account). If too many timestamps accumulate here (refer to
+the
+.B pwdMaxFailure
+password policy attribute for details),
+and the
+.B pwdLockout
+password policy attribute is set to "TRUE", the
+account may be locked.
+(Please also refer to the
+.B pwdLockout
+password policy attribute.)
+Excess timestamps beyond those allowed by
+.B pwdMaxFailure
+or
+.B pwdMaxRecordedFailure
+may also be purged. If a successful authentication is made to this
+DN (i.e. to this user account), then
+.B pwdFailureTime
+will be cleansed of entries.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.19
+ NAME 'pwdFailureTime'
+ DESC 'The timestamps of the last consecutive
+ authentication failures'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
+ EQUALITY generalizedTimeMatch
+ ORDERING generalizedTimeOrderingMatch
+ NO\-USER\-MODIFICATION
+ USAGE directoryOperation )
+.RE
+
+.B pwdHistory
+.P
+This attribute contains the history of previously used passwords
+for this DN (i.e. for this user account).
+The values of this attribute are stored in string format as follows:
+
+.RS 4
+
+pwdHistory=
+.RS 4
+time "#" syntaxOID "#" length "#" data
+.RE
+
+time=
+.RS 4
+GeneralizedTime as specified in section 3.3.13 of [RFC4517]
+.RE
+
+.P
+syntaxOID = numericoid
+.RS 4
+This is the string representation of the dotted-decimal OID that
+defines the syntax used to store the password. numericoid is
+described in section 1.4 of [RFC4512].
+.RE
+
+length = NumericString
+.RS 4
+The number of octets in the data. NumericString is described in
+section 3.3.23 of [RFC4517].
+.RE
+
+data =
+.RS 4
+Octets representing the password in the format specified by syntaxOID.
+.RE
+
+.RE
+
+This format allows the server to store and transmit a history of
+passwords that have been used. In order for equality matching
+on the values in this attribute to function properly, the time
+field is in GMT format.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.20
+ NAME 'pwdHistory'
+ DESC 'The history of user passwords'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.40
+ EQUALITY octetStringMatch
+ NO\-USER\-MODIFICATION
+ USAGE directoryOperation)
+.RE
+
+.B pwdGraceUseTime
+
+This attribute contains the list of timestamps of logins made after
+the user password in the DN has expired. These post-expiration
+logins are known as "\fIgrace logins\fP".
+If too many
+.I grace logins
+have been used (please refer to the
+.B pwdGraceAuthnLimit
+password policy attribute), then the DN will no longer be allowed
+to be used to authenticate the user to the directory until the
+administrator changes the DN's
+.B userPassword
+attribute.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.21
+ NAME 'pwdGraceUseTime'
+ DESC 'The timestamps of the grace login once the password has expired'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
+ EQUALITY generalizedTimeMatch
+ NO\-USER\-MODIFICATION
+ USAGE directoryOperation)
+.RE
+
+.B pwdReset
+.P
+This attribute indicates whether the user's password has been reset
+by the administrator and thus must be changed upon first use of this
+DN for authentication to the directory. If
+.B pwdReset
+is set to "TRUE", then the password was reset and the user must change
+it upon first authentication. If the attribute does not exist, or
+is set to "FALSE", the user need not change their password due to
+administrative reset.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.22
+ NAME 'pwdReset'
+ DESC 'The indication that the password has
+ been reset'
+ EQUALITY booleanMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+ SINGLE\-VALUE
+ USAGE directoryOperation)
+.RE
+
+.B pwdStartTime
+
+This attribute specifies the time the entry's password becomes valid
+for authentication. Authentication attempts made before this time
+will fail. If this attribute does not exist, then no restriction
+applies.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.27
+ NAME 'pwdStartTime'
+ DESC 'The time the password becomes enabled'
+ EQUALITY generalizedTimeMatch
+ ORDERING generalizedTimeOrderingMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
+ SINGLE\-VALUE
+ USAGE directoryOperation )
+.RE
+
+.B pwdEndTime
+
+This attribute specifies the time the entry's password becomes
+invalid for authentication. Authentication attempts made after this
+time will fail, regardless of expiration or grace settings. If this
+attribute does not exist, then this restriction does not apply.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.28
+ NAME 'pwdEndTime'
+ DESC 'The time the password becomes disabled'
+ EQUALITY generalizedTimeMatch
+ ORDERING generalizedTimeOrderingMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
+ SINGLE\-VALUE
+ USAGE directoryOperation )
+.RE
+
+Note that pwdStartTime may be set to a time greater than or equal to
+pwdEndTime; this simply disables the account.
+
+.B pwdAccountTmpLockoutEnd
+.P
+This attribute that the user's password has been locked out temporarily
+according to the
+.B pwdMinDelay
+policy option and when the lockout ends.
+.LP
+.RS 4
+( 1.3.6.1.4.1.42.2.27.8.1.33
+ NAME 'pwdAccountTmpLockoutEnd'
+ DESC 'Temporary lockout end'
+ EQUALITY generalizedTimeMatch
+ ORDERING generalizedTimeOrderingMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.24
+ SINGLE\-VALUE
+ NO\-USER\-MODIFICATION
+ USAGE directoryOperation )
+.RE
+
+.SH SUNDS ACCOUNT USABILITY CONTROL
+.LP
+If the SunDS Account Usability control is used with a search request, the
+overlay will attach validity information to each entry provided all of the
+following are met:
+.IP \[bu] 2
+There is a password policy that applies to the entry
+.IP \[bu]
+The user has
+.B compare
+access to the entry's password attribute.
+.IP \[bu]
+The configured password attribute is present in the entry
+
+.SH EXAMPLES
+.LP
+.RS
+.nf
+database mdb
+suffix dc=example,dc=com
+\|...
+overlay ppolicy
+ppolicy_default "cn=Standard,ou=Policies,dc=example,dc=com"
+.fi
+.RE
+
+.SH SEE ALSO
+.BR ldap (3),
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapo\-chain (5).
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+IETF LDAP password policy proposal by P. Behera, L. Poitou and J.
+Sermersheim: documented in IETF document
+"draft-behera-ldap-password-policy-10.txt".
+
+.SH BUGS
+The LDAP Password Policy specification is not yet an approved standard,
+and it is still evolving. This code will continue to be in flux until the
+specification is finalized.
+
+.SH ACKNOWLEDGEMENTS
+.P
+This module was written in 2004 by Howard Chu of Symas Corporation
+with significant input from Neil Dunbar and Kartik Subbarao of Hewlett-Packard.
+.P
+This manual page borrows heavily and shamelessly from the specification
+upon which the password policy module it describes is based. This
+source is the
+IETF LDAP password policy proposal by P. Behera, L.
+Poitou and J. Sermersheim.
+The proposal is fully documented in
+the
+IETF document named draft-behera-ldap-password-policy-10.txt,
+written in August of 2009.
+.P
+.so ../Project
diff --git a/doc/man/man5/slapo-refint.5 b/doc/man/man5/slapo-refint.5
new file mode 100644
index 0000000..98c24e7
--- /dev/null
+++ b/doc/man/man5/slapo-refint.5
@@ -0,0 +1,78 @@
+.TH SLAPO-REFINT 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2004-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-refint \- Referential Integrity overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The Referential Integrity overlay can be used with a backend database such as
+.BR slapd\-mdb (5)
+to maintain the cohesiveness of a schema which utilizes reference attributes.
+.LP
+Integrity is maintained by updating database records which contain the named
+attributes to match the results of a
+.B modrdn
+or
+.B delete
+operation. For example, if the integrity attribute were configured as
+.BR manager ,
+deletion of the record "uid=robert,ou=people,dc=example,dc=com" would trigger a
+search for all other records which have a
+.B manager
+attribute containing that DN. Entries matching that search would have their
+.B manager
+attribute removed.
+Or, renaming the same record into "uid=george,ou=people,dc=example,dc=com"
+would trigger a search for all other records which have a
+.B manager
+attribute containing that DN.
+Entries matching that search would have their
+.B manager
+attribute deleted and replaced by the new DN.
+.LP
+.B rootdn
+must be set for the database. refint runs as the rootdn
+to gain access to make its updates.
+.B rootpw
+is not needed.
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the Referential Integrity overlay.
+They should appear after the
+.B overlay
+directive.
+.TP
+.B refint_attributes <attribute> [...]
+Specify one or more attributes for which integrity will be maintained
+as described above.
+.TP
+.B refint_nothing <string>
+Specify an arbitrary value to be used as a placeholder when the last value
+would otherwise be deleted from an attribute. This can be useful in cases
+where the schema requires the existence of an attribute for which referential
+integrity is enforced. The attempted deletion of a required attribute will
+otherwise result in an Object Class Violation, causing the request to fail.
+The string must be a valid DN.
+.TP
+.B refint_modifiersname <DN>
+Specify the DN to be used as the modifiersName of the internal modifications
+performed by the overlay.
+It defaults to "\fIcn=Referential Integrity Overlay\fP".
+.LP
+Modifications performed by this overlay are not propagated during
+replication. This overlay must be configured identically on
+replication consumers in order to maintain full synchronization
+with the provider.
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5).
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man5/slapo-remoteauth.5 b/doc/man/man5/slapo-remoteauth.5
new file mode 100644
index 0000000..4d12587
--- /dev/null
+++ b/doc/man/man5/slapo-remoteauth.5
@@ -0,0 +1,160 @@
+.TH SLAPO-REMOTEAUTH 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation, All Rights Reserved.
+.\" Copying restrictions apply. See the COPYRIGHT file.
+.\" $OpenLDAP$
+.SH NAME
+slapo-remoteauth \- Delegate authentication requests to remote directories, e.g. Active Directory
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B remoteauth
+overlay to
+.BR slapd (8)
+provides passthrough authentication to remote directory servers, e.g.
+Active Directory, for LDAP simple bind operations. The local LDAP entry
+referenced in the bind operation is mapped to its counterpart in the remote
+directory. An LDAP bind operation is performed against the remote directory
+and results are returned based on those of the remote operation.
+.LP
+A slapd server configured with the
+.B remoteauth
+overlay handles an authentication request based on the presence of
+.B userPassword
+in the local entry. If the
+.B userPassword
+is present, authentication is performed locally, otherwise the
+.B remoteauth
+overlay performs the authentication request to the configured remote directory
+server.
+.LP
+
+.SH CONFIGURATION
+
+The following options can be applied to the
+.B remoteauth
+overlay within the slapd.conf file. All options should follow the
+.B overlay remoteauth
+directive.
+
+.TP
+.B overlay remoteauth
+This directive adds the
+.B remoteauth
+overlay to the current database, see
+.BR slapd.conf (5)
+for details.
+
+.TP
+.B remoteauth_dn_attribute <dnattr>
+Attribute in the local entry that is used to store the bind DN to a remote
+directory server.
+
+.TP
+.B remoteauth_mapping <domain> <hostname|LDAP URI|file:///path/to/list_of_hostnames>
+For a non-Windows deployment, a domain can be considered as a collection of
+one or more hosts to which slapd server authentcates against on behalf of
+authenticating users.
+For a given domain name, the mapping specifies the target server(s),
+e.g., Active Directory domain controller(s), to connect to via LDAP.
+The second argument can be given either as a hostname, an LDAP URI, or a file
+containing a list of hostnames/URIs, one per line. The hostnames are tried in
+sequence until the connection succeeds.
+
+This option can be provided more than once to provide mapping information for
+different domains. For example:
+
+.nf
+ remoteauth_mapping americas file:///path/to/americas.domain.hosts
+ remoteauth_mapping asiapacific file:///path/to/asiapacific.domain.hosts
+ remoteauth_mapping emea emeadc1.emea.example.com
+.fi
+
+.TP
+.B remoteauth_domain_attribute <attr>
+Attribute in the local entry that specifies the domain name, any text after
+"\\" or ":" is ignored.
+
+.TP
+.B remoteauth_default_domain <default domain>
+Default domain.
+
+
+.TP
+.B remoteauth_default_realm <server>
+Fallback server to connect to for domains not specified in
+.BR remoteauth_mapping .
+
+.TP
+.B remoteauth_retry_count <num>
+Number of connection retries attempted. Default is 3.
+
+.TP
+.B remoteauth_store <on|off>
+Whether to store the password in the local entry on successful bind. Default is
+off.
+
+.HP
+.hy 0
+.B remoteauth_tls
+.B [starttls=yes]
+.B [tls_cert=<file>]
+.B [tls_key=<file>]
+.B [tls_cacert=<file>]
+.B [tls_cacertdir=<path>]
+.B [tls_reqcert=never|allow|try|demand]
+.B [tls_reqsan=never|allow|try|demand]
+.B [tls_cipher_suite=<ciphers>]
+.B [tls_ecname=<names>]
+.B [tls_crlcheck=none|peer|all]
+.RS
+Remoteauth specific TLS configuration, see
+.BR slapd.conf (5)
+for more details on each of the parameters and defaults.
+.RE
+
+.TP
+.B remoteauth_tls_peerkey_hash <hostname> <hashname>:<base64 of public key hash>
+Mapping between remote server hostnames and their public key hashes. Only one
+mapping per hostname is supported and if any pins are specified, all hosts
+need to be pinned. If set, pinning is in effect regardless of whether or not
+certificate name validation is enabled by
+.BR tls_reqcert .
+
+.SH EXAMPLE
+A typical example configuration of
+.B remoteauth
+overlay for AD is shown below (as a
+.BR slapd.conf (5)
+snippet):
+
+.LP
+.nf
+ database <database>
+ #...
+
+ overlay remoteauth
+ remoteauth_dn_attribute seeAlso
+ remoteauth_domain_attribute associatedDomain
+ remoteauth_default_realm americas.example.com
+
+ remoteauth_mapping americas file:///home/ldap/etc/remoteauth.americas
+ remoteauth_mapping emea emeadc1.emea.example.com
+
+ remoteauth_tls starttls=yes tls_reqcert=demand tls_cacert=/home/ldap/etc/example-ca.pem
+ remoteauth_tls_peerkey_hash ldap.americas.tld sha256:Bxv3MkLoDm6gt/iDfeGNdNNqa5TTpPDdIwvZM/cIgeo=
+.fi
+
+Where seeAlso contains the AD bind DN for the user, associatedDomain contains the
+Windows Domain Id in the form of <NT-domain-name>:<NT-username> in which
+anything following, including ":", is ignored.
+
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd (8).
+
+.SH Copyrights
+Copyright 2004-2022 The OpenLDAP Foundation.
+Portions Copyright 2004-2017 Howard Chu, Symas Corporation.
+Portions Copyright 2017-2021 Ondřej Kuzník, Symas Corporation.
+Portions Copyright 2004 Hewlett-Packard Company
diff --git a/doc/man/man5/slapo-retcode.5 b/doc/man/man5/slapo-retcode.5
new file mode 100644
index 0000000..ab63801
--- /dev/null
+++ b/doc/man/man5/slapo-retcode.5
@@ -0,0 +1,257 @@
+.TH SLAPO-RETCODE 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation, All Rights Reserved.
+.\" Copying restrictions apply. See the COPYRIGHT file.
+.\" Copyright 2001, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+.\" $OpenLDAP$
+.SH NAME
+slapo\-retcode \- return code overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B retcode
+overlay to
+.BR slapd (8)
+is useful to test the behavior of clients when server-generated erroneous
+and/or unusual responses occur, e.g. error codes, referrals,
+excessive response times and so on.
+
+The error responses are generated according to different strategies.
+.LP
+In the first case, all operations targeted at a specific configurable
+subtree cause the object related to the request DN to be looked up
+and checked for return code data: a response code, plus an optional
+textual message, an optional configurable delay, an optional matched DN
+field, and, when the response code is "referral", a (list of) referral(s).
+.LP
+Well-known response codes from standard track documents are provided
+in \fBretcode.conf\fP, which can be included after instantiating
+the overlay.
+.LP
+In the second case, objects of classes inherited from
+the \fBerrAbsObject\fP, like \fBerrObject\fP or \fBerrAuxObject\fP,
+when returned as intermediate responses of a search request, are changed
+into the response dictated by their content.
+.LP
+A third mode causes objects to be looked up from the underlying database
+to discover if their class inherits from \fBerrABsObject\fP;
+in that case, their content is used to compute the corresponding response.
+.LP
+The behavior is disabled by using the \fBmanageDSAit\fP control (RFC 3296);
+in that case, the resulting object, either present in the directory
+or dynamically generated by the overlay, or contained in the request,
+is handled as usual.
+.LP
+The config directives that are specific to the
+.B retcode
+overlay must be prefixed by
+.BR retcode\- ,
+to avoid conflicts with directives specific to the underlying database
+or to other stacked overlays. The following specific directives
+can be used to configure the retcode overlay:
+.TP
+.B retcode\-parent <DN>
+This directive defines the parent DN where dynamically generated
+entries reside.
+If not defined, the suffix of the database is used.
+.HP
+.hy 0
+.B retcode\-item <RDN> <errCode> [op=<oplist>] [text=<message>]
+.B [ref=<referral>] [sleeptime=<sec>] [matched=<DN>]
+.B [unsolicited=<OID>[:<data>]] [flags=[\{pre|post\}\-]disconnect[,...]]
+.RS
+A dynamically generated entry, located below \fBretcode\-parent\fP.
+The \fBerrCode\fP is the number of the response code;
+it can be in any format supported by
+.BR strtol (3).
+The optional \fBoplist\fP is a list of operations that cause
+response code generation; if absent, all operations are affected.
+The \fBmatched\fP field is the matched DN that is returned
+along with the error, while the \fBtext\fP field is an optional
+diagnostics message.
+The \fBref\fP field is only allowed for the \fBreferral\fP
+response code.
+The \fBsleeptime\fP field causes
+.BR slapd (8)
+to sleep the specified number of seconds before proceeding
+with the operation.
+The \fBunsolicited\fP field can be used to cause the return
+of an RFC 4511 unsolicited response message; if \fBOID\fP
+is not "0", an extended response is generated, with the optional
+\fBdata\fP appended.
+If \fBflags\fP contains \fBdisconnect\fP, or \fBpre\-disconnect\fP,
+.BR slapd (8)
+disconnects abruptly, without notice; \fBpost\-disconnect\fP
+causes disconnection right after sending response as appropriate.
+.RE
+.TP
+.B retcode\-indir
+Enables exploitation of in-directory stored errAbsObject.
+May result in a lot of unnecessary overhead.
+.TP
+.B retcode\-sleep [\-]<n>
+Defines a sleep time in seconds that is spent before actually handling
+any operation.
+If negative, a random time between 0 and the absolute value of the argument
+is used.
+
+.SH SCHEMA
+The
+.B retcode
+overlay utilizes the "return code" schema described herein.
+This schema is specifically designed for use with this
+overlay and is not intended to be used otherwise.
+It is also noted that the schema described here is
+.I a work in
+.IR progress ,
+and hence subject to change without notice.
+The schema is loaded automatically by the overlay.
+
+The schema includes a number of object classes and associated
+attribute types as described below.
+
+.LP
+The error code:
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.4.1.1
+ NAME ( 'errCode' )
+ DESC 'LDAP error code'
+ EQUALITY integerMatch
+ ORDERING integerOrderingMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE\-VALUE )
+.RE
+.LP
+The operations that trigger the response code:
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.4.1.2
+ NAME ( 'errOp' )
+ DESC 'Operations the errObject applies to'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+.RE
+.LP
+The text message:
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.4.1.3
+ NAME ( 'errText' )
+ DESC 'LDAP error textual description'
+ EQUALITY caseIgnoreMatch
+ SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ SINGLE\-VALUE )
+.RE
+.LP
+The sleep time before the response is actually returned to the client:
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.4.1.4
+ NAME ( 'errSleepTime' )
+ DESC 'Time to wait before returning the error'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ SINGLE\-VALUE )
+.RE
+.LP
+The matched DN returned to the client:
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.4.1.5
+ NAME ( 'errMatchedDN' )
+ DESC 'Value to be returned as matched DN'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ SINGLE\-VALUE )
+.RE
+.LP
+The OID to be returned as extended response OID
+in RFC 4511 unsolicited responses
+("0" generates a regular response with msgid set to 0):
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.4.1.6
+ NAME ( 'errUnsolicitedOID' )
+ DESC 'OID to be returned within unsolicited response'
+ EQUALITY objectIdentifierMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+ SINGLE\-VALUE )
+.RE
+.LP
+The octet string to be returned as extended response data
+in RFC 4511 unsolicited response:
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.4.1.7
+ NAME ( 'errUnsolicitedData' )
+ DESC 'Data to be returned within unsolicited response'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.40
+ SINGLE\-VALUE )
+.RE
+.LP
+If TRUE,
+.BR slapd (8)
+disconnects abruptly without notice; if FALSE, it disconnects
+after sending response as appropriate:
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.4.1.8
+ NAME ( 'errDisconnect' )
+ DESC 'Disconnect without notice'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+ SINGLE\-VALUE )
+.RE
+.LP
+The abstract class that triggers the overlay:
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.4.3.0
+ NAME ( 'errAbsObject' )
+ SUP top ABSTRACT
+ MUST ( errCode )
+ MAY ( cn $ description $ errOp $ errText $ errSleepTime
+ $ errMatchedDN ) )
+.RE
+.LP
+The standalone structural objectclass for specifically created data:
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.4.3.1
+ NAME ( 'errObject' )
+ SUP errAbsObject STRUCTURAL )
+.RE
+.LP
+The auxiliary objectclass to alter the behavior of existing objects:
+.RS 4
+( 1.3.6.1.4.1.4203.666.11.4.3.2
+ NAME ( 'errAuxObject' )
+ SUP errAbsObject AUXILIARY )
+.RE
+
+.SH EXAMPLE
+.LP
+.RS
+.nf
+overlay retcode
+retcode\-parent "ou=RetCodes,dc=example,dc=com"
+
+# retcode.conf is found in tests/data/ of the source tree
+include ./retcode.conf
+
+# Wait 10 seconds, then return success (0x00)
+retcode\-item "cn=Success after 10 seconds" 0x00 sleeptime=10
+# Wait 10 seconds, then return timelimitExceeded (0x03)
+retcode\-item "cn=Timelimit after 10 seconds" 0x03 sleeptime=10
+.fi
+.RE
+.LP
+.LP
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd (8).
+The
+.BR slapo\-retcode (5)
+overlay supports dynamic configuration via
+.BR back-config .
+.SH ACKNOWLEDGEMENTS
+.P
+This module was written in 2005 by Pierangelo Masarati for SysNet s.n.c.
diff --git a/doc/man/man5/slapo-rwm.5 b/doc/man/man5/slapo-rwm.5
new file mode 100644
index 0000000..69912d6
--- /dev/null
+++ b/doc/man/man5/slapo-rwm.5
@@ -0,0 +1,669 @@
+.TH SLAPO-RWM 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation, All Rights Reserved.
+.\" Copying restrictions apply. See the COPYRIGHT file.
+.\" Copyright 2004, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+.\" $OpenLDAP$
+.\"
+.\" Portions of this document should probably be moved to slapd-ldap(5)
+.\" and maybe manual pages for librewrite.
+.\"
+.SH NAME
+slapo\-rwm \- rewrite/remap overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B rwm
+overlay to
+.BR slapd (8)
+performs basic DN/data rewrite and objectClass/attributeType mapping.
+Its usage is mostly intended to provide virtual views of existing data
+either remotely, in conjunction with the proxy backend described in
+.BR slapd\-ldap (5),
+or locally, in conjunction with the relay backend described in
+.BR slapd\-relay (5).
+.LP
+This overlay is experimental.
+.SH MAPPING
+An important feature of the
+.B rwm
+overlay is the capability to map objectClasses and attributeTypes
+from the local set (or a subset of it) to a foreign set, and vice versa.
+This is accomplished by means of the
+.B rwm\-map
+directive.
+.TP
+.B rwm\-map "{attribute | objectclass} [<local name> | *] {<foreign name> | *}"
+Map attributeTypes and objectClasses from the foreign server to
+different values on the local slapd.
+The reason is that some attributes might not be part of the local
+slapd's schema, some attribute names might be different but serve the
+same purpose, etc.
+If local or foreign name is `*', the name is preserved.
+If local name is omitted, the foreign name is removed.
+Unmapped names are preserved if both local and foreign name are `*',
+and removed if local name is omitted and foreign name is `*'.
+.LP
+The local
+.I objectClasses
+and
+.I attributeTypes
+must be defined in the local schema; the foreign ones do not have to,
+but users are encouraged to explicitly define the remote attributeTypes
+and the objectClasses they intend to map. All in all, when remapping
+a remote server via back-ldap (\fBslapd\-ldap\fP(5))
+or back-meta (\fBslapd\-meta\fP(5))
+their definition can be easily obtained by querying the \fIsubschemaSubentry\fP
+of the remote server; the problem should not exist when remapping a local
+database.
+Note, however, that the decision whether to rewrite or not attributeTypes
+with
+.IR "distinguishedName syntax" ,
+requires the knowledge of the attributeType syntax.
+See the REWRITING section for details.
+.LP
+Note that when mapping DN-valued attributes from local to remote,
+first the DN is rewritten, and then the attributeType is mapped;
+while mapping from remote to local, first the attributeType is mapped,
+and then the DN is rewritten.
+As such, it is important that the local attributeType is appropriately
+defined as using the distinguishedName syntax.
+Also, note that there are DN-related syntaxes (i.e. compound types with
+a portion that is DN-valued), like nameAndOptionalUID,
+whose values are currently not rewritten.
+.LP
+If the foreign type of an attribute mapping is not defined on the local
+server, it might be desirable to have the attribute values normalized after
+the mapping process. Not normalizing the values can lead to wrong results,
+when the
+.B rwm
+overlay is used together with e.g. the
+.B pcache
+overlay. This normalization can be enabled by means of the
+.B rwm\-normalize\-mapped\-attrs
+directive.
+.TP
+.B rwm\-normalize\-mapped\-attrs {yes|no}
+Set this to "yes", if the
+.B rwm
+overlay should try to normalize the values of attributes that are mapped from
+an attribute type that is unknown to the local server. The default value of
+this setting is "no".
+.TP
+.B rwm-drop-unrequested-attrs {yes|no}
+Set this to "yes", if the
+.B rwm
+overlay should drop attributes that are not explicitly requested
+by a search operation.
+When this is set to "no", the
+.B rwm
+overlay will leave all attributes in place, so that subsequent modules
+can further manipulate them.
+In any case, unrequested attributes will be omitted from search results
+by the frontend, when the search entry response package is encoded.
+The default value of this setting is "yes".
+.SH SUFFIX MASSAGING
+A basic feature of the
+.B rwm
+overlay is the capability to perform suffix massaging between a virtual
+and a real naming context by means of the
+.B rwm\-suffixmassage
+directive.
+This, in conjunction with proxy backends,
+.BR slapd\-ldap (5)
+and
+.BR slapd\-meta (5),
+or with the relay backend,
+.BR slapd\-relay (5),
+allows one to create virtual views of databases.
+A distinguishing feature of this overlay is that, when instantiated
+before any database, it can modify the DN of requests
+.I before
+database selection.
+For this reason, rules that rewrite the empty DN ("")
+or the subschemaSubentry DN (usually "cn=subschema"),
+would prevent clients from reading the root DSE or the DSA's schema.
+.TP
+.B rwm\-suffixmassage "[<virtual naming context>]" "<real naming context>"
+Shortcut to implement naming context rewriting; the trailing part
+of the DN is rewritten from the virtual to the real naming context
+in the bindDN, searchDN, searchFilterAttrDN, compareDN, compareAttrDN,
+addDN, addAttrDN, modifyDN, modifyAttrDN, modrDN, newSuperiorDN,
+deleteDN, exopPasswdDN, and from the real to the virtual naming context
+in the searchEntryDN, searchAttrDN and matchedDN rewrite contexts.
+By default no rewriting occurs for the searchFilter
+and for the referralAttrDN and referralDN rewrite contexts.
+If no \fI<virtual naming context>\fP is given, the first suffix of the
+database is used; this requires the
+.B rwm\-suffixmassage
+directive be defined \fIafter\fP the database
+.B suffix
+directive.
+The
+.B rwm\-suffixmassage
+directive automatically sets the
+.B rwm\-rewriteEngine
+to
+.BR ON .
+.LP
+See the REWRITING section for details.
+.SH REWRITING
+A string is rewritten according to a set of rules, called a `rewrite
+context'.
+The rules are based on POSIX (''extended'') regular expressions with
+substring matching; basic variable substitution and map resolution
+of substrings is allowed by specific mechanisms detailed in the following.
+The behavior of pattern matching/substitution can be altered by a set
+of flags.
+.LP
+.RS
+.nf
+<rewrite context> ::= <rewrite rule> [...]
+<rewrite rule> ::= <pattern> <action> [<flags>]
+.fi
+.RE
+.LP
+The underlying concept is to build a lightweight rewrite module
+for the slapd server (initially dedicated to the LDAP backend):
+.LP
+.SH Passes
+An incoming string is matched against a set of
+.IR rewriteRules .
+Rules are made of a
+.IR "regex match pattern" ,
+a
+.I "substitution pattern"
+and a set of actions, described by a set of
+.IR "optional flags" .
+In case of match, string rewriting is performed according to the
+substitution pattern that allows one to refer to substrings matched in the
+incoming string.
+The actions, if any, are finally performed.
+Each rule is executed recursively, unless altered by specific action
+flags; see "Action Flags" for details.
+A default limit on the recursion level is set, and can be altered
+by the
+.B rwm\-rewriteMaxPasses
+directive, as detailed in the "Additional Configuration Syntax" section.
+The substitution pattern allows map resolution of substrings.
+A map is a generic object that maps a substitution pattern to a value.
+The flags are divided in "Pattern Matching Flags" and "Action Flags";
+the former alter the regex match pattern behavior, while the latter
+alter the actions that are taken after substitution.
+.SH "Pattern Matching Flags"
+.TP
+.B `C'
+honors case in matching (default is case insensitive)
+.TP
+.B `R'
+use POSIX ''basic'' regular expressions (default is ''extended'')
+.TP
+.B `M{n}'
+allow no more than
+.B n
+recursive passes for a specific rule; does not alter the max total count
+of passes, so it can only enforce a stricter limit for a specific rule.
+.SH "Action Flags"
+.TP
+.B `:'
+apply the rule once only (default is recursive)
+.TP
+.B `@'
+stop applying rules in case of match; the current rule is still applied
+recursively; combine with `:' to apply the current rule only once
+and then stop.
+.TP
+.B `#'
+stop current operation if the rule matches, and issue an `unwilling to
+perform' error.
+.TP
+.B `G{n}'
+jump
+.B n
+rules back and forth (watch for loops!).
+Note that `G{1}' is implicit in every rule.
+.TP
+.B `I'
+ignores errors in rule; this means, in case of error, e.g. issued by a
+map, the error is treated as a missed match.
+The `unwilling to perform' is not overridden.
+.TP
+.B `U{n}'
+uses
+.B
+n
+as return code if the rule matches; the flag does not alter the recursive
+behavior of the rule, so, to have it performed only once, it must be used
+in combination with `:', e.g.
+.B `:U{32}'
+returns the value `32' (indicating noSuchObject) after exactly
+one execution of the rule, if the pattern matches.
+As a consequence, its behavior is equivalent to `@', with the return
+code set to
+.BR n ;
+or, in other words, `@' is equivalent to `U{0}'.
+Positive errors are allowed, indicating the related LDAP error codes
+as specified in \fIdraft-ietf-ldapbis-protocol\fP.
+.LP
+The ordering of the flags can be significant.
+For instance: `IG{2}' means ignore errors and jump two lines ahead
+both in case of match and in case of error, while `G{2}I' means ignore
+errors, but jump two lines ahead only in case of match.
+.LP
+More flags (mainly Action Flags) will be added as needed.
+.SH "Pattern Matching"
+See
+.BR regex (7)
+and/or
+.BR re_format (7).
+.SH "Substitution Pattern Syntax"
+Everything starting with `$' requires substitution;
+.LP
+the only obvious exception is `$$', which is turned into a single `$';
+.LP
+the basic substitution is `$<d>', where `<d>' is a digit;
+0 means the whole string, while 1-9 is a submatch, as discussed in
+.BR regex (7)
+and/or
+.BR re_format (7).
+.LP
+a `$' followed by a `{' invokes an advanced substitution.
+The pattern is:
+.LP
+.RS
+`$' `{' [ <operator> ] <name> `(' <substitution> `)' `}'
+.RE
+.LP
+where <name> must be a legal name for the map, i.e.
+.LP
+.RS
+.nf
+<name> ::= [a-z][a-z0-9]* (case insensitive)
+<operator> ::= `>' `|' `&' `&&' `*' `**' `$'
+.fi
+.RE
+.LP
+and <substitution> must be a legal substitution
+pattern, with no limits on the nesting level.
+.LP
+The operators are:
+.TP
+.B >
+sub-context invocation; <name> must be a legal, already defined
+rewrite context name
+.TP
+.B |
+external command invocation; <name> must refer to a legal, already
+defined command name (NOT IMPLEMENTED YET)
+.TP
+.B &
+variable assignment; <name> defines a variable in the running
+operation structure which can be dereferenced later; operator
+.B &
+assigns a variable in the rewrite context scope; operator
+.B &&
+assigns a variable that scopes the entire session, e.g. its value
+can be dereferenced later by other rewrite contexts
+.TP
+.B *
+variable dereferencing; <name> must refer to a variable that is
+defined and assigned for the running operation; operator
+.B *
+dereferences a variable scoping the rewrite context; operator
+.B **
+dereferences a variable scoping the whole session, e.g. the value
+is passed across rewrite contexts
+.TP
+.B $
+parameter dereferencing; <name> must refer to an existing parameter;
+the idea is to make some run-time parameters set by the system
+available to the rewrite engine, as the client host name, the bind DN
+if any, constant parameters initialized at config time, and so on;
+no parameter is currently set by either
+.B back\-ldap
+or
+.BR back\-meta ,
+but constant parameters can be defined in the configuration file
+by using the
+.B rewriteParam
+directive.
+.LP
+Substitution escaping has been delegated to the `$' symbol,
+which is used instead of `\e' in string substitution patterns
+because `\e' is already escaped by slapd's low level parsing routines;
+as a consequence, regex escaping requires
+two `\e' symbols, e.g. `\fB.*\e.foo\e.bar\fP' must
+be written as `\fB.*\e\e.foo\e\e.bar\fP'.
+.\"
+.\" The symbol can be altered at will by redefining the related macro in
+.\" "rewrite-int.h".
+.\"
+.SH "Rewrite Context"
+A rewrite context is a set of rules which are applied in sequence.
+The basic idea is to have an application initialize a rewrite
+engine (think of Apache's mod_rewrite ...) with a set of rewrite
+contexts; when string rewriting is required, one invokes the
+appropriate rewrite context with the input string and obtains the
+newly rewritten one if no errors occur.
+.LP
+Each basic server operation is associated to a rewrite context;
+they are divided in two main groups: client \-> server and
+server \-> client rewriting.
+.LP
+client \-> server:
+.LP
+.RS
+.nf
+(default) if defined and no specific context
+ is available
+bindDN bind
+searchDN search
+searchFilter search
+searchFilterAttrDN search
+compareDN compare
+compareAttrDN compare AVA
+addDN add
+addAttrDN add AVA (DN portion of "ref" excluded)
+modifyDN modify
+modifyAttrDN modify AVA (DN portion of "ref" excluded)
+referralAttrDN add/modify DN portion of referrals
+ (default to none)
+renameDN modrdn (the old DN)
+newSuperiorDN modrdn (the new parent DN, if any)
+newRDN modrdn (the new relative DN)
+deleteDN delete
+exopPasswdDN password modify extended operation DN
+.fi
+.RE
+.LP
+server \-> client:
+.LP
+.RS
+.nf
+searchEntryDN search (only if defined; no default;
+ acts on DN of search entries)
+searchAttrDN search AVA (only if defined; defaults
+ to searchEntryDN; acts on DN-syntax
+ attributes of search results)
+matchedDN all ops (only if applicable; defaults
+ to searchEntryDN)
+referralDN all ops (only if applicable; defaults
+ to none)
+.fi
+.RE
+.LP
+.SH "Basic Configuration Syntax"
+All rewrite/remap directives start with the prefix
+.BR rwm\-
+.TP
+.B rwm\-rewriteEngine { on | off }
+If `on', the requested rewriting is performed; if `off', no
+rewriting takes place (an easy way to stop rewriting without
+altering too much the configuration file).
+.TP
+.B rwm\-rewriteContext <context name> "[ alias <aliased context name> ]"
+<Context name> is the name that identifies the context, i.e. the name
+used by the application to refer to the set of rules it contains.
+It is used also to reference sub contexts in string rewriting.
+A context may alias another one.
+In this case the alias context contains no rule, and any reference to
+it will result in accessing the aliased one.
+.TP
+.B rwm\-rewriteRule "<regex match pattern>" "<substitution pattern>" "[ <flags> ]"
+Determines how a string can be rewritten if a pattern is matched.
+Examples are reported below.
+.SH "Additional Configuration Syntax"
+.TP
+.B rwm\-rewriteMap "<map type>" "<map name>" "[ <map attrs> ]"
+Allows one to define a map that transforms substring rewriting into
+something else.
+The map is referenced inside the substitution pattern of a rule.
+.TP
+.B rwm\-rewriteParam <param name> <param value>
+Sets a value with global scope, that can be dereferenced by the
+command `${$paramName}'.
+.TP
+.B rwm\-rewriteMaxPasses <number of passes> [<number of passes per rule>]
+Sets the maximum number of total rewriting passes that can be
+performed in a single rewrite operation (to avoid loops).
+A safe default is set to 100; note that reaching this limit is still
+treated as a success; recursive invocation of rules is simply
+interrupted.
+The count applies to the rewriting operation as a whole, not
+to any single rule; an optional per-rule limit can be set.
+This limit is overridden by setting specific per-rule limits
+with the `M{n}' flag.
+
+.SH "MAPS"
+Currently, few maps are builtin but additional map types may be
+registered at runtime.
+
+Supported maps are:
+.TP
+.B LDAP <URI> [bindwhen=<when>] [version=<version>] [binddn=<DN>] [credentials=<cred>]
+The
+.B LDAP
+map expands a value by performing a simple LDAP search.
+Its configuration is based on a mandatory URI, whose
+.B attrs
+portion must contain exactly one attribute
+(use
+.B entryDN
+to fetch the DN of an entry).
+If a multi-valued attribute is used, only the first value is considered.
+
+The parameter
+.B bindwhen
+determines when the connection is established.
+It can take the values
+.BR now ,
+.BR later ,
+and
+.BR everytime ,
+respectively indicating that the connection should be created at startup,
+when required, or any time it is used.
+In the former two cases, the connection is cached, while in the latter
+a fresh new one is used all times. This is the default.
+
+The parameters
+.B binddn
+and
+.B credentials
+represent the DN and the password that is used to perform an authenticated
+simple bind before performing the search operation; if not given,
+an anonymous connection is used.
+
+The parameter
+.B version
+can be 2 or 3 to indicate the protocol version that must be used.
+The default is 3.
+
+.TP
+.B slapd <URI>
+The
+.B slapd
+map expands a value by performing an internal LDAP search.
+Its configuration is based on a mandatory URI, which must begin with
+.B "ldap:///"
+(i.e., it must be an LDAP URI and it must not specify a host).
+As with the
+LDAP map, the
+.B attrs
+portion must contain exactly one attribute, and if
+a multi-valued attribute is used, only the first value is considered.
+
+.SH "REWRITE CONFIGURATION EXAMPLES"
+.nf
+# set to `off' to disable rewriting
+rwm\-rewriteEngine on
+
+# the rules the "suffixmassage" directive implies
+rwm\-rewriteEngine on
+# all dataflow from client to server referring to DNs
+rwm\-rewriteContext default
+rwm\-rewriteRule "(.+,)?<virtualnamingcontext>$" "$1<realnamingcontext>" ":"
+# empty filter rule
+rwm\-rewriteContext searchFilter
+# all dataflow from server to client
+rwm\-rewriteContext searchEntryDN
+rwm\-rewriteRule "(.+,)?<realnamingcontext>$" "$1<virtualnamingcontext>" ":"
+rwm\-rewriteContext searchAttrDN alias searchEntryDN
+rwm\-rewriteContext matchedDN alias searchEntryDN
+# misc empty rules
+rwm\-rewriteContext referralAttrDN
+rwm\-rewriteContext referralDN
+
+# Everything defined here goes into the `default' context.
+# This rule changes the naming context of anything sent
+# to `dc=home,dc=net' to `dc=OpenLDAP, dc=org'
+
+rwm\-rewriteRule "(.+,)?dc=home,[ ]?dc=net$"
+ "$1dc=OpenLDAP, dc=org" ":"
+
+# since a pretty/normalized DN does not include spaces
+# after rdn separators, e.g. `,', this rule suffices:
+
+rwm\-rewriteRule "(.+,)?dc=home,dc=net$"
+ "$1dc=OpenLDAP,dc=org" ":"
+
+# Start a new context (ends input of the previous one).
+# This rule adds blanks between DN parts if not present.
+rwm\-rewriteContext addBlanks
+rwm\-rewriteRule "(.*),([^ ].*)" "$1, $2"
+
+# This one eats blanks
+rwm\-rewriteContext eatBlanks
+rwm\-rewriteRule "(.*), (.*)" "$1,$2"
+
+# Here control goes back to the default rewrite
+# context; rules are appended to the existing ones.
+# anything that gets here is piped into rule `addBlanks'
+rwm\-rewriteContext default
+rwm\-rewriteRule ".*" "${>addBlanks($0)}" ":"
+
+.\" # Anything with `uid=username' is looked up in
+.\" # /etc/passwd for gecos (I know it's nearly useless,
+.\" # but it is there just as a guideline to implementing
+.\" # custom maps).
+.\" # Note the `I' flag that leaves `uid=username' in place
+.\" # if `username' does not have a valid account, and the
+.\" # `:' that forces the rule to be processed exactly once.
+.\" rwm\-rewriteContext uid2Gecos
+.\" rwm\-rewriteRule "(.*)uid=([a\-z0\-9]+),(.+)"
+.\" "$1cn=$2{xpasswd},$3" "I:"
+.\"
+.\" # Finally, in a bind, if one uses a `uid=username' DN,
+.\" # it is rewritten in `cn=name surname' if possible.
+.\" rwm\-rewriteContext bindDN
+.\" rwm\-rewriteRule ".*" "${>addBlanks(${>uid2Gecos($0)})}" ":"
+.\"
+# Rewrite the search base according to `default' rules.
+rwm\-rewriteContext searchDN alias default
+
+# Search results with OpenLDAP DN are rewritten back with
+# `dc=home,dc=net' naming context, with spaces eaten.
+rwm\-rewriteContext searchEntryDN
+rwm\-rewriteRule "(.*[^ ],)?[ ]?dc=OpenLDAP,[ ]?dc=org$"
+ "${>eatBlanks($1)}dc=home,dc=net" ":"
+
+# Bind with email instead of full DN: we first need
+# an ldap map that turns attributes into a DN (the
+# argument used when invoking the map is appended to
+# the URI and acts as the filter portion)
+rwm\-rewriteMap ldap attr2dn "ldap://host/dc=my,dc=org?dn?sub"
+
+# Then we need to detect DN made up of a single email,
+# e.g. `mail=someone@example.com'; note that the rule
+# in case of match stops rewriting; in case of error,
+# it is ignored. In case we are mapping virtual
+# to real naming contexts, we also need to rewrite
+# regular DNs, because the definition of a bindDN
+# rewrite context overrides the default definition.
+rwm\-rewriteContext bindDN
+rwm\-rewriteRule "^mail=[^,]+@[^,]+$" "${attr2dn($0)}" ":@I"
+
+# This is a rather sophisticated example. It massages a
+# search filter in case who performs the search has
+# administrative privileges. First we need to keep
+# track of the bind DN of the incoming request, which is
+# stored in a variable called `binddn' with session scope,
+# and left in place to allow regular binding:
+rwm\-rewriteContext bindDN
+rwm\-rewriteRule ".+" "${&&binddn($0)}$0" ":"
+
+# A search filter containing `uid=' is rewritten only
+# if an appropriate DN is bound.
+# To do this, in the first rule the bound DN is
+# dereferenced, while the filter is decomposed in a
+# prefix, in the value of the `uid=<arg>' AVA, and
+# in a suffix. A tag `<>' is appended to the DN.
+# If the DN refers to an entry in the `ou=admin' subtree,
+# the filter is rewritten OR-ing the `uid=<arg>' with
+# `cn=<arg>'; otherwise it is left as is. This could be
+# useful, for instance, to allow apache's auth_ldap-1.4
+# module to authenticate users with both `uid' and
+# `cn', but only if the request comes from a possible
+# `cn=Web auth,ou=admin,dc=home,dc=net' user.
+rwm\-rewriteContext searchFilter
+rwm\-rewriteRule "(.*\e\e()uid=([a\-z0\-9_]+)(\e\e).*)"
+ "${**binddn}<>${&prefix($1)}${&arg($2)}${&suffix($3)}"
+ ":I"
+rwm\-rewriteRule "^[^,]+,ou=admin,dc=home,dc=net$"
+ "${*prefix}|(uid=${*arg})(cn=${*arg})${*suffix}" ":@I"
+rwm\-rewriteRule ".*<>$" "${*prefix}uid=${*arg}${*suffix}" ":"
+
+# This example shows how to strip unwanted DN-valued
+# attribute values from a search result; the first rule
+# matches DN values below "ou=People,dc=example,dc=com";
+# in case of match the rewriting exits successfully.
+# The second rule matches everything else and causes
+# the value to be rejected.
+rwm\-rewriteContext searchEntryDN
+rwm\-rewriteRule ".+,ou=People,dc=example,dc=com$" "$0" ":@"
+rwm\-rewriteRule ".*" "" "#"
+.fi
+.SH "MAPPING EXAMPLES"
+The following directives map the object class `groupOfNames' to
+the object class `groupOfUniqueNames' and the attribute type
+`member' to the attribute type `uniqueMember':
+.LP
+.RS
+.nf
+map objectclass groupOfNames groupOfUniqueNames
+map attribute uniqueMember member
+.fi
+.RE
+.LP
+This presents a limited attribute set from the foreign
+server:
+.LP
+.RS
+.nf
+map attribute cn *
+map attribute sn *
+map attribute manager *
+map attribute description *
+map attribute *
+.fi
+.RE
+.LP
+These lines map cn, sn, manager, and description to themselves, and
+any other attribute gets "removed" from the object before it is sent
+to the client (or sent up to the LDAP server). This is obviously a
+simplistic example, but you get the point.
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd\-ldap (5),
+.BR slapd\-meta (5),
+.BR slapd\-relay (5),
+.BR slapd (8),
+.BR regex (7),
+.BR re_format (7).
+.SH AUTHOR
+Pierangelo Masarati; based on back-ldap rewrite/remap features
+by Howard Chu, Pierangelo Masarati.
diff --git a/doc/man/man5/slapo-sssvlv.5 b/doc/man/man5/slapo-sssvlv.5
new file mode 100644
index 0000000..42a39a7
--- /dev/null
+++ b/doc/man/man5/slapo-sssvlv.5
@@ -0,0 +1,57 @@
+.TH SLAPO-SSSVLV 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2009-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copyright 2009 Symas Corporation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-sssvlv \- Server Side Sorting and Virtual List View overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+This overlay implements the LDAP Server Side Sorting (RFC2891) control
+as well as the Virtual List View control. It also replaces the default
+implementation of the LDAP PagedResults (RFC2696) control, to ensure
+that it works with Sorting. The overlay can be used with any backend
+or globally for all backends.
+
+Since a complete result set must be generated in memory before sorting can
+be performed, processing sort requests can have a large impact on the
+server's memory use. As such, any connection is limited to having only
+a limited number of sort requests active at a time. Additional limits may
+be configured as described below.
+
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the SSSVLV overlay.
+They should appear after the
+.B overlay
+directive.
+.TP
+.B sssvlv\-max <num>
+Set the maximum number of concurrent sort requests allowed across all
+connections. The default is one half of the number of server threads.
+.TP
+.B sssvlv\-maxkeys <num>
+Set the maximum number of keys allowed in a sort request. The default is 5.
+.TP
+.B sssvlv\-maxperconn <num>
+Set the maximum number of concurrent paged search requests per connection. The default is 5. The number of concurrent requests remains limited by
+.B sssvlv-max.
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.TP
+ETCDIR/slapd.d
+default slapd configuration directory
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5).
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+IETF LDAP Virtual List View proposal by D. Boreham, J. Sermersheim,
+and A. Kashi in IETF document "draft-ietf-ldapext-ldapv3-vlv-09.txt".
+.SH AUTHOR
+Howard Chu
diff --git a/doc/man/man5/slapo-syncprov.5 b/doc/man/man5/slapo-syncprov.5
new file mode 100644
index 0000000..3c6e6b8
--- /dev/null
+++ b/doc/man/man5/slapo-syncprov.5
@@ -0,0 +1,81 @@
+.TH SLAPO-SYNCPROV 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2004-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-syncprov \- Sync Provider overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The Sync Provider overlay implements the provider-side support for the
+LDAP Content Synchronization (RFC4533) as well as syncrepl replication
+support. The overlay
+can be used with any backend that maintains entryCSN and entryUUID
+attributes for its entries. It also creates a contextCSN attribute in
+the root entry of the database.
+
+The contextCSN is updated for every write operation performed against the
+database. To reduce database contention, the contextCSN is only updated in
+memory. The value is written to the database on server shutdown and read into
+memory on startup, and maintained in memory thereafter. Checkpoints may be
+configured to write the contextCSN into the underlying database to minimize
+recovery time after an unclean shutdown.
+
+On databases that support inequality indexing, it is highly recommended to set an
+eq index on the entryCSN attribute when using this overlay.
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the Sync Provider overlay.
+They should appear after the
+.B overlay
+directive.
+.TP
+.B syncprov\-checkpoint <ops> <minutes>
+After a write operation has succeeded, write the contextCSN to the underlying
+database if
+.B <ops>
+write operations or more than
+.B <minutes>
+time have passed
+since the last checkpoint. Checkpointing is disabled by default.
+.TP
+.B syncprov\-sessionlog <ops>
+Configures an in-memory session log for recording information about write
+operations made on the database. The
+.B <ops>
+specifies the number of operations that are recorded in the log. All write
+operations (except Adds) are recorded in the log.
+When using the session log, it is helpful to set an eq index on the
+entryUUID attribute in the underlying database.
+.TP
+.B syncprov\-sessionlog\-source <dn>
+Should not be set when syncprov-sessionlog is set and vice versa.
+
+When accesslog for this database is configured and is logging at this suffix,
+it can be used as the session log source instead of the in-memory session log
+mentioned above. This log has the advantage of not starting afresh every time
+the server is restarted.
+.TP
+.B syncprov\-nopresent TRUE | FALSE
+Specify that the Present phase of refreshing should be skipped. This value
+should only be set TRUE for a syncprov instance on top of a log database
+(such as one managed by the accesslog overlay).
+The default is FALSE.
+.TP
+.B syncprov\-reloadhint TRUE | FALSE
+Specify that the overlay should honor the reloadHint flag in the Sync
+Control. It must be set TRUE when using the accesslog overlay for
+delta-based syncrepl replication support.
+The default is FALSE.
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapo\-accesslog (5).
+OpenLDAP Administrator's Guide.
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man5/slapo-translucent.5 b/doc/man/man5/slapo-translucent.5
new file mode 100644
index 0000000..f7dadf2
--- /dev/null
+++ b/doc/man/man5/slapo-translucent.5
@@ -0,0 +1,133 @@
+.TH SLAPO-TRANSLUCENT 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2004-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-translucent \- Translucent Proxy overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The Translucent Proxy overlay can be used with a backend database such as
+.BR slapd\-mdb (5)
+to create a "translucent proxy". Entries retrieved from a remote LDAP
+server may have some or all attributes overridden, or new attributes
+added, by entries in the local database before being presented to the
+client.
+.LP
+A
+.BR search
+operation is first populated with entries from the remote LDAP server, the
+attributes of which are then overridden with any attributes defined in the
+local database. Local overrides may be populated with the
+.BR add ,
+.B modify ,
+and
+.B modrdn
+operations, the use of which is restricted to the root user.
+.LP
+A
+.BR compare
+operation will perform a comparison with attributes defined in the local
+database record (if any) before any comparison is made with data in the
+remote database.
+.SH CONFIGURATION
+The Translucent Proxy overlay uses a proxied database,
+typically a (set of) remote LDAP server(s), which is configured with the options shown in
+.BR slapd\-ldap (5),
+.BR slapd\-meta (5)
+or similar.
+These
+.B slapd.conf
+options are specific to the Translucent Proxy overlay; they must appear
+after the
+.B overlay
+directive that instantiates the
+.B translucent
+overlay.
+.TP
+.B translucent_strict
+By default, attempts to delete attributes in either the local or remote
+databases will be silently ignored. The
+.B translucent_strict
+directive causes these modifications to fail with a Constraint Violation.
+.TP
+.B translucent_no_glue
+This configuration option disables the automatic creation of "glue" records
+for an
+.B add
+or
+.B modrdn
+operation, such that all parents of an entry added to the local database
+must be created by hand. Glue records are always created for a
+.B modify
+operation.
+.TP
+.B translucent_local <attr[,attr...]>
+Specify a list of attributes that should be searched for in the local database
+when used in a search filter. By default, search filters are only handled by
+the remote database. With this directive, search filters will be split into a
+local and remote portion, and local attributes will be searched locally.
+.TP
+.B translucent_remote <attr[,attr...]>
+Specify a list of attributes that should be searched for in the remote database
+when used in a search filter. This directive complements the
+.B translucent_local
+directive. Attributes may be specified as both local and remote if desired.
+.LP
+If neither
+.B translucent_local
+nor
+.B translucent_remote
+are specified, the default behavior is to search the remote database with the
+complete search filter. If only
+.B translucent_local
+is specified, searches will only be run on the local database. Likewise, if only
+.B translucent_remote
+is specified, searches will only be run on the remote database. In any case, both
+the local and remote entries corresponding to a search result will be merged
+before being returned to the client.
+
+.TP
+.B translucent_bind_local
+Enable looking for locally stored credentials for simple bind when binding
+to the remote database fails. Disabled by default.
+
+.TP
+.B translucent_pwmod_local
+Enable RFC 3062 Password Modification extended operation on locally stored
+credentials. The operation only applies to entries that exist in the remote
+database. Disabled by default.
+
+.SH ACCESS CONTROL
+Access control is delegated to either the remote DSA(s) or to the local database
+backend for
+.B auth
+and
+.B write
+operations.
+It is delegated to the remote DSA(s) and to the frontend for
+.B read
+operations.
+Local access rules involving data returned by the remote DSA(s) should be designed
+with care. In fact, entries are returned by the remote DSA(s) only based on the
+remote fraction of the data, based on the identity the operation is performed as.
+As a consequence, local rules might only be allowed to see a portion
+of the remote data.
+
+.SH CAVEATS
+.LP
+The Translucent Proxy overlay will disable schema checking in the local database,
+so that an entry consisting of overlay attributes need not adhere to the
+complete schema.
+.LP
+Because the translucent overlay does not perform any DN rewrites, the local
+and remote database instances must have the same suffix. Other configurations
+will probably fail with No Such Object and other errors.
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd\-ldap (5).
diff --git a/doc/man/man5/slapo-unique.5 b/doc/man/man5/slapo-unique.5
new file mode 100644
index 0000000..720c35a
--- /dev/null
+++ b/doc/man/man5/slapo-unique.5
@@ -0,0 +1,187 @@
+.TH SLAPO-UNIQUE 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2004-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-unique \- Attribute Uniqueness overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The Attribute Uniqueness overlay can be used with a backend database such as
+.BR slapd\-mdb (5)
+to enforce the uniqueness of some or all attributes within a
+scope. This subtree defaults to all objects within the subtree of the
+database for which the Uniqueness overlay is configured.
+.LP
+Uniqueness is enforced by searching the subtree to ensure that the values of
+all attributes presented with an
+.BR add ,
+.B modify
+or
+.B modrdn
+operation are unique within the scope.
+For example, if uniqueness were enforced for the
+.B uid
+attribute, the subtree would be searched for any other records which also
+have a
+.B uid
+attribute containing the same value. If any are found, the request is
+rejected.
+.LP
+The search is performed using the rootdn of the database, to avoid issues
+with ACLs preventing the overlay from seeing all of the relevant data. As
+such, the database must have a rootdn configured.
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the Attribute Uniqueness overlay.
+They should appear after the
+.B overlay
+directive.
+.TP
+.B unique_uri <[strict ][ignore ][serialize ]URI[[ URI...]...]>
+Configure the base, attributes, scope, and filter for uniqueness
+checking. Multiple URIs may be specified within a domain,
+allowing complex selections of objects. Multiple
+.B unique_uri
+statements or
+.B olcUniqueURI
+attribute values will create independent domains, each with their own
+independent lists of URIs and ignore/strict settings.
+
+Keywords
+.BR strict ,
+.BR ignore ,
+and
+.B serialize
+have to be enclosed in quotes (") together with the URI.
+
+The LDAP URI syntax is a subset of
+.B RFC-4516,
+and takes the form:
+
+ldap:///[base dn]?[attributes...]?scope[?filter]
+
+The
+.B base dn
+defaults to that of the back-end database.
+Specified base dns must be within the subtree of the back-end database.
+
+If no
+.B attributes
+are specified, the URI applies to all non-operational attributes.
+
+The
+.B scope
+component is effectively mandatory, because LDAP URIs default to
+.B base
+scope, which is not valid for uniqueness, because groups of one object
+are always unique. Scopes of
+.B sub
+(for subtree) and
+.B one
+for one-level are valid.
+
+The
+.B filter
+component causes the domain to apply uniqueness constraints only to
+matching objects. e.g.
+.B ldap:///?cn?sub?(sn=e*)
+would require unique
+.B cn
+attributes for all objects in the subtree of the back-end database whose
+.B sn
+starts with an e.
+
+It is possible to assert uniqueness upon all non-operational
+attributes except those listed by prepending the keyword
+.B ignore
+If not configured, all non-operational (e.g., system) attributes must be
+unique. Note that the
+.B attributes
+list of an
+.B ignore
+URI should generally contain the
+.BR objectClass ,
+.BR dc ,
+.B ou
+and
+.B o
+attributes, as these will generally not be unique, nor are they operational
+attributes.
+
+It is possible to set strict checking for the uniqueness domain by
+prepending the keyword
+.B strict.
+By default, uniqueness is not enforced
+for null values. Enabling
+.B strict
+mode extends the concept of uniqueness to include null values, such
+that only one attribute within a subtree will be allowed to have a
+null value. Strictness applies to all URIs within a uniqueness
+domain, but some domains may be strict while others are not.
+
+It is possible to enforce strict serialization of modifications by
+prepending the keyword
+.B serialize.
+By default, no serialization is performed, so multiple modifications
+occurring nearly simultaneously may see incomplete uniqueness results.
+Using
+.B serialize
+will force individual write operations to fully complete before allowing
+any others to proceed, to ensure that each operation's uniqueness checks
+are consistent.
+.LP
+It is not possible to set both URIs and legacy slapo\-unique configuration
+parameters simultaneously. In general, the legacy configuration options
+control pieces of a single unfiltered subtree domain.
+.TP
+.B unique_base <basedn>
+This legacy configuration parameter should be converted to the
+.B base dn
+component of the above
+.B unique_uri
+style of parameter.
+.TP
+.B unique_ignore <attribute...>
+This legacy configuration parameter should be converted to a
+.B unique_uri
+parameter with
+.B ignore
+keyword as described above.
+.TP
+.B unique_attributes <attribute...>
+This legacy configuration parameter should be converted to a
+.B unique_uri
+parameter, as described above.
+.TP
+.B unique_strict <attribute...>
+This legacy configuration parameter should be converted to a
+.B strict
+keyword prepended to a
+.B unique_uri
+parameter, as described above.
+.SH CAVEATS
+.LP
+.B unique_uri
+cannot be used with the old-style of configuration, and vice versa.
+.B unique_uri
+can implement everything the older system can do, however.
+.LP
+Typical attributes for the
+.B ignore ldap:///...
+URIs are intentionally not hardcoded into the overlay to allow for
+maximum flexibility in meeting site-specific requirements.
+.LP
+Replication and operations with the
+.B relax
+control are allowed to bypass this enforcement. It is therefore important that
+all servers accepting writes have this overlay configured in order to maintain
+uniqueness in a replicated DIT.
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5).
diff --git a/doc/man/man5/slapo-valsort.5 b/doc/man/man5/slapo-valsort.5
new file mode 100644
index 0000000..97f8db4
--- /dev/null
+++ b/doc/man/man5/slapo-valsort.5
@@ -0,0 +1,97 @@
+.TH SLAPO-VALSORT 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2005-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-valsort \- Value Sorting overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The Value Sorting overlay can be used with a backend database to sort the
+values of specific multi-valued attributes within a subtree. The sorting
+occurs whenever the attributes are returned in a search response.
+.LP
+Sorting can be specified in ascending or descending order, using either
+numeric or alphanumeric sort methods. Additionally, a "weighted" sort can
+be specified, which uses a numeric weight prepended to the attribute values.
+The weighted sort is always performed in ascending order, but may be combined
+with the other methods for values that all have equal weights. The weight
+is specified by prepending an integer weight {<\fIweight\fP>}
+in front of each value of the attribute for which weighted sorting is
+desired. This weighting factor is stripped off and not returned in search
+results unless the valsort control is specified (1.3.6.1.4.1.4203.666.5.14).
+
+The valsort control requires a value consisting of a Sequence that contains
+a boolean flag. The weighting factor is only returned if the boolean value is TRUE. In
+.BR lber-encode (3)
+format, the required value must conform to "{b}" syntax.
+
+.SH CONFIGURATION
+These
+.I slapd.conf
+options apply to the Value Sorting overlay.
+They should appear after the
+.B overlay
+directive.
+.TP
+valsort\-attr <\fIattribute\fP> <\fIbaseDN\fP> (<\fIsort-method\fP> | weighted [<\fIsort-method\fP>])
+Configure a sorting method for the specified
+.I attribute
+in the subtree rooted at
+.IR baseDN .
+The
+.I sort-method
+may be one of
+.BR alpha\-ascend ,
+.BR alpha\-descend ,
+.BR numeric\-ascend ,
+or
+.BR numeric\-descend .
+If the special
+.B weighted
+method is specified, a secondary
+.I sort-method
+may also be specified. It is an
+error to specify an alphanumeric
+.I sort-method
+for an attribute with Integer
+or NumericString syntax, and it is an error to specify a numeric
+.I sort-method
+for an attribute with a syntax other than Integer or NumericString.
+.SH EXAMPLES
+.LP
+.nf
+ database mdb
+ suffix dc=example,dc=com
+ ...
+ overlay valsort
+ valsort\-attr member ou=groups,dc=example,dc=com alpha\-ascend
+.fi
+
+To invoke
+.BR ldapsearch (1)
+with the valsort control, the control value must be set appropriately.
+The following octets represent the desired "{b}" encoding:
+.LP
+.nf
+ 0x30 0x03 0x01 0x01 0xff
+.fi
+
+The control can be sent from the command-line using the base64
+encoding of the value:
+.LP
+.nf
+ ldapsearch \-E 1.3.6.1.4.1.4203.666.5.14=::MAMBAf8=
+.fi
+
+.SH FILES
+.TP
+\fIETCDIR/slapd.conf\fP
+default \fBslapd\fP configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd\-config (5).
+.SH ACKNOWLEDGEMENTS
+.P
+This module was written in 2005 by Howard Chu of Symas Corporation. The
+work was sponsored by Stanford University.
diff --git a/doc/man/man5/slappw-argon2.5 b/doc/man/man5/slappw-argon2.5
new file mode 100644
index 0000000..eaeab2b
--- /dev/null
+++ b/doc/man/man5/slappw-argon2.5
@@ -0,0 +1,131 @@
+.TH SLAPPW-ARGON2 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2020-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slappw\-argon2 \- Argon2 password module to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.RS
+.LP
+.B moduleload argon2
+.RI [ <parameters> ]
+.RE
+.SH DESCRIPTION
+.LP
+The
+.B argon2
+module to
+.BR slapd (8)
+provides support for the use of the key derivation function Argon2,
+that was selected as the winner of the Password Hashing Competition in July 2015,
+in hashed passwords in OpenLDAP.
+.LP
+It does so by providing the additional password scheme
+.B {ARGON2}
+for use in slapd.
+
+.SH CONFIGURATION
+The
+.B argon2
+module does not need any configuration,
+but it can be configured by giving the following parameters:
+.TP
+.BI m= <memory>
+Set memory usage to
+.I <memory>
+kiB.
+.TP
+.BI p= <parallelism>
+Set parallelism to
+.I <parallelism>
+threads. Currently supported only when linked with
+.BR libargon2 .
+.TP
+.BI t= <iterations>
+Set the number of iterations to
+.IR <iterations> .
+.LP
+These replace defaults when preparing hashes for new passwords where possible.
+.LP
+After loading the module, the password scheme
+.B {ARGON2}
+will be recognised in values of the
+.I userPassword
+attribute.
+.LP
+You can then instruct OpenLDAP to use this scheme when processing
+the LDAPv3 Password Modify (RFC 3062) extended operations by using the
+.BR password-hash
+option in
+.BR slapd.conf (5):
+.RS
+.LP
+.B password\-hash {ARGON2}
+.RE
+.LP
+
+.SS NOTES
+If you want to use the scheme described here with
+.BR slappasswd (8),
+remember to load the module using its command line options.
+The relevant option/value is:
+.RS
+.LP
+.B \-o
+.BR module\-load = argon2
+.LP
+.RE
+Or if non-default parameters are required:
+.RS
+.LP
+.B \-o
+.BR module\-load =" argon2
+.RB [ <param> ...]"
+.LP
+.RE
+Depending on
+.BR argon2 's
+location, you may also need:
+.RS
+.LP
+.B \-o
+.BR module\-path = \fIpathspec\fP
+.RE
+
+.SH EXAMPLES
+Both userPassword LDAP attributes below encode the password
+.RI ' secret '
+using different salts:
+.EX
+.LP
+userPassword: {ARGON2}$argon2i$v=19$m=4096,t=3,p=1$c2FsdHNhbHQ$DKlexoEJUoZTmkAAC3SaMWk30El9/RvVhlqGo6afIng
+.LP
+userPassword: {ARGON2}$argon2i$v=19$m=4096,t=3,p=1$c2FsdHNhbHRzYWx0$qOCkx9nMeFlaGOO4DUmPDgrlUbgMMuO9T1+vQCFuyzw
+.EE
+
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR ldappasswd (1),
+.BR slappasswd (8),
+.BR ldap (3),
+.LP
+.UR http://www.OpenLDAP.org/doc/
+"OpenLDAP Administrator's Guide"
+.UE
+.LP
+
+.SH ACKNOWLEDGEMENTS
+This manual page has been written by Peter Marschall based on the
+module's README file written by
+.MT simon@levermann.de
+Simon Levermann
+.ME .
+.LP
+.B OpenLDAP
+is developed and maintained by
+.UR http://www.openldap.org/
+The OpenLDAP Project
+.UE .
+.B OpenLDAP
+is derived from University of Michigan LDAP 3.3 Release.
diff --git a/doc/man/man8/Makefile.in b/doc/man/man8/Makefile.in
new file mode 100644
index 0000000..30f21e0
--- /dev/null
+++ b/doc/man/man8/Makefile.in
@@ -0,0 +1,16 @@
+# man8 Makefile.in for OpenLDAP
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+MANSECT=8
diff --git a/doc/man/man8/lloadd.8 b/doc/man/man8/lloadd.8
new file mode 100644
index 0000000..d999d5b
--- /dev/null
+++ b/doc/man/man8/lloadd.8
@@ -0,0 +1,312 @@
+.TH LLOADD 8C "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2017-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+lloadd \- LDAP Load Balancer Daemon
+.SH SYNOPSIS
+.B LIBEXECDIR/lloadd
+[\c
+.BR \-4 | \-6 ]
+[\c
+.BI \-d \ debug-level\fR]
+[\c
+.BI \-f \ lloadd-config-file\fR]
+[\c
+.BI \-h \ URLs\fR]
+[\c
+.BI \-n \ service-name\fR]
+[\c
+.BI \-s \ syslog-level\fR]
+[\c
+.BI \-l \ syslog-local-user\fR]
+[\c
+.BI \-o \ option\fR[ = value\fR]]
+[\c
+.BI \-r \ directory\fR]
+[\c
+.BI \-u \ user\fR]
+[\c
+.BI \-g \ group\fR]
+.SH DESCRIPTION
+.LP
+.B Lloadd
+is the stand-alone LDAP daemon. It listens for LDAP connections on
+any number of ports (default \fB389\fP), forwarding the LDAP operations
+it receives over these connections to be handled by the configured
+backends.
+.B lloadd
+is typically invoked at boot time, usually out of
+.BR /etc/rc.local .
+Upon startup,
+.B lloadd
+normally forks and disassociates itself from the invoking tty.
+If configured in the config file, the
+.B lloadd
+process will print its process ID (see
+.BR getpid (2))
+to a
+.B .pid
+file, as well as the command line options during invocation to an
+.B .args
+file (see
+.BR lloadd.conf (5)).
+If the
+.B \-d
+flag is given, even with a zero argument,
+.B lloadd
+will not fork and disassociate from the invoking tty.
+.LP
+See the "OpenLDAP Administrator's Guide" for more details on
+.BR lloadd .
+.SH OPTIONS
+.TP
+.B \-4
+Listen on IPv4 addresses only.
+.TP
+.B \-6
+Listen on IPv6 addresses only.
+.TP
+.BI \-d \ debug-level
+Turn on debugging as defined by
+.IR debug-level .
+If this option is specified, even with a zero argument,
+.B lloadd
+will not fork or disassociate from the invoking terminal. Some general
+operation and status messages are printed for any value of \fIdebug-level\fP.
+\fIdebug-level\fP is taken as a bit string, with each bit corresponding to a
+different kind of debugging information. See <ldap_log.h> for details.
+Comma-separated arrays of friendly names can be specified to select
+debugging output of the corresponding debugging information.
+All the names recognized by the \fIloglevel\fP directive
+described in \fBlloadd.conf\fP(5) are supported.
+If \fIdebug-level\fP is \fB?\fP, a list of installed debug-levels is printed,
+and lloadd exits.
+
+Remember that if you turn on packet logging, packets containing bind passwords
+will be output, so if you redirect the log to a logfile, that file should
+be read-protected.
+.TP
+.BI \-s \ syslog-level
+This option tells
+.B lloadd
+at what debug-level debugging statements should be logged to the
+.BR syslog (8)
+facility.
+The value \fIsyslog-level\fP can be set to any value or combination
+allowed by the \fB\-d\fP switch.
+Lloadd logs all messages selected by \fIsyslog-level\fP
+at the
+.BR syslog (3)
+severity debug-level \fBDEBUG\fP,
+on the unit specified with \fB\-l\fP.
+.TP
+.BI \-n \ service-name
+Specifies the service name for logging and other purposes. Defaults
+to basename of argv[0], i.e.: "lloadd".
+.TP
+.BI \-l \ syslog-local-user
+Selects the local user of the
+.BR syslog (8)
+facility. Value can be
+.BR LOCAL0 ,
+through
+.BR LOCAL7 ,
+as well as
+.B USER
+and
+.BR DAEMON .
+The default is
+.BR LOCAL4 .
+However, this option is only permitted on systems that support
+local users with the
+.BR syslog (8)
+facility.
+Logging to syslog(8) occurs at the "DEBUG" severity debug-level.
+.TP
+.BI \-f \ lloadd-config-file
+Specifies the lloadd configuration file. The default is
+.BR ETCDIR/lloadd.conf .
+.TP
+.BI \-h \ URLlist
+.B lloadd
+will by default serve
+.B ldap:///
+(LDAP over TCP on all interfaces on default LDAP port). That is,
+it will bind using INADDR_ANY and port \fB389\fP.
+The
+.B \-h
+option may be used to specify LDAP (and other scheme) URLs to serve.
+For example, if lloadd is given
+.BR "\-h \(dqldap://127.0.0.1:9009/ ldaps:/// ldapi:///\(dq" ,
+it will listen on 127.0.0.1:9009 for LDAP, 0.0.0.0:636 for LDAP over TLS,
+and LDAP over IPC (Unix domain sockets). Host 0.0.0.0 represents
+INADDR_ANY (any interface).
+A space separated list of URLs is expected. The URLs should be of the LDAP,
+PLDAP, LDAPS, PLDAPS, or LDAPI schemes, and generally without a DN or other
+optional parameters (excepting as discussed below). Support for the latter
+three schemes depends on selected configuration options. Hosts may be specified
+by name or IPv4 and IPv6 address formats. Ports, if specified, must be
+numeric. The default ldap:// port is \fB389\fP and the default ldaps:// port
+is \fB636\fP, same for the proxy enabled variants.
+
+The PLDAP and PLDAPS URL schemes provide support for the HAProxy proxy protocol
+version 2, which allows a load balancer or proxy server to provide the remote
+client IP address to slapd to be used for access control or logging. Ports
+configured for PLDAP or PLDAPS will only accept connections that include the
+necessary proxy protocol header. Connections to these ports should be
+restricted at the network level to only trusted load balancers or proxies to
+avoid spoofing of client IP addresses by third parties.
+
+At the moment, the load balancer does not act on the recorded address in any
+way.
+
+For LDAP over IPC,
+.B name
+is the name of the socket, and no
+.B port
+is required, nor allowed; note that directory separators must be
+URL-encoded, like any other characters that are special to URLs;
+so the socket
+
+ /usr/local/var/ldapi
+
+must be specified as
+
+ ldapi://%2Fusr%2Flocal%2Fvar%2Fldapi
+
+The default location for the IPC socket is LOCALSTATEDIR/run/ldapi
+.TP
+.BI \-r \ directory
+Specifies a directory to become the root directory. lloadd will
+change the current working directory to this directory and
+then
+.BR chroot (2)
+to this directory. This is done after opening listeners but before
+reading any configuration file or initializing any backend. When
+used as a security mechanism, it should be used in conjunction with
+.B \-u
+and
+.B \-g
+options.
+.TP
+.BI \-u \ user
+.B lloadd
+will run lloadd with the specified user name or id, and that user's
+supplementary group access list as set with initgroups(3). The group ID
+is also changed to this user's gid, unless the \fB\-g\fP option is used to
+override. Note when used with
+.BR \-r ,
+lloadd will use the user database in the change root environment.
+.TP
+.BI \-g \ group
+.B lloadd
+will run with the specified group name or id. Note when used with
+.BR \-r ,
+lloadd will use the group database in the change root environment.
+.TP
+.BI \-o \ option\fR[ = value\fR]
+This option provides a generic means to specify options without the need to reserve
+a separate letter for them.
+
+It supports the following options:
+.RS
+.TP
+.BR slp= { on \||\| off \||\| \fIslp-attrs\fP }
+When SLP support is compiled into lloadd, disable it (\fBoff\fP),
+ enable it by registering at SLP DAs without specific SLP attributes (\fBon\fP),
+or with specific SLP attributes
+.I slp-attrs
+that must be an SLP attribute list definition according to the SLP standard.
+
+For example, \fB"slp=(tree=production),(server-type=OpenLDAP),(server\-version=2.4.15)"\fP
+registers at SLP DAs with the three SLP attributes tree, server-type and server-version
+that have the values given above.
+This allows one to specifically query the SLP DAs for LDAP servers holding the
+.I production
+tree in case multiple trees are available.
+.RE
+
+.SH RELATION TO SLAPD(8)
+.B Lloadd
+can be compiled as a
+.B slapd
+loadable module. In that case, it can be loaded as such:
+.LP
+.nf
+.ft tt
+ moduleload path/to/lloadd.la
+ backend lload
+ listen "listening URLs"
+.ft
+.fi
+
+This enables
+.B lloadd
+to provide additional features through the host slapd process like access to
+run-time statistics in
+.B cn=monitor
+and dynamic configuration from
+.BR cn=config .
+
+The listening sockets specified will be under direct control of
+.B lloadd
+and need to be different from the sockets slapd is configured to listen on.
+Clients connecting to these are completely separate from regular LDAP clients
+connecting to the usual
+.B slapd
+sockets -
+.B lloadd
+clients have no access to slapd databases, similarly,
+.B slapd
+client traffic does not propagate to the
+.B lloadd
+backend servers in any way.
+
+.SH EXAMPLES
+To start
+.I lloadd
+and have it fork and detach from the terminal and start load-balancing
+the LDAP servers defined in the default config file, just type:
+.LP
+.nf
+.ft tt
+ LIBEXECDIR/lloadd
+.ft
+.fi
+.LP
+To start
+.B lloadd
+with an alternate configuration file, and turn
+on voluminous debugging which will be printed on standard error, type:
+.LP
+.nf
+.ft tt
+ LIBEXECDIR/lloadd \-f /var/tmp/lloadd.conf \-d 255
+.ft
+.fi
+.LP
+To start
+.B lloadd
+as a module inside a slapd process listening on ldap://:1389 and ldaps://,
+put the following in your slapd.conf (or its equivalent in cn=config):
+.LP
+.nf
+.ft tt
+ moduleload lloadd.la
+ backend lload
+ listen "ldap://:1389 ldaps://"
+.ft
+.fi
+.SH "SEE ALSO"
+.BR ldap (3),
+.BR lloadd.conf (5),
+.BR slapd-config (5),
+.BR slapd-monitor (5),
+.BR slapd (8).
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.SH BUGS
+See http://www.openldap.org/its/
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man8/slapacl.8 b/doc/man/man8/slapacl.8
new file mode 100644
index 0000000..c283f11
--- /dev/null
+++ b/doc/man/man8/slapacl.8
@@ -0,0 +1,205 @@
+.TH SLAPACL 8C "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2004-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapacl \- Check access to a list of attributes.
+.SH SYNOPSIS
+.B SBINDIR/slapacl
+.BI \-b \ DN
+[\c
+.BI \-d \ debug-level\fR]
+[\c
+.BI \-D \ authcDN\ \fR|
+.BI \-U \ authcID\fR]
+[\c
+.BI \-f \ slapd.conf\fR]
+[\c
+.BI \-F \ confdir\fR]
+[\c
+.BI \-o \ option\fR[ = value\fR]]
+[\c
+.BR \-u ]
+[\c
+.BR \-v ]
+[\c
+.BI \-X \ authzID\ \fR|
+.BI "\-o \ authzDN=" DN\fR]
+[\c
+.IR attr [\fB/\fI access ][\fB:\fI value ]]\fR\ [...]
+.LP
+.SH DESCRIPTION
+.LP
+.B slapacl
+is used to check the behavior of
+.BR slapd (8)
+by verifying access to directory data according to the access control list
+directives defined in its configuration.
+.
+It opens the
+.BR slapd.conf (5)
+configuration file or the
+.BR slapd\-config (5)
+backend, reads in the
+.BR access / olcAccess
+directives, and then parses the
+.B attr
+list given on the command-line; if none is given, access to the
+.B entry
+pseudo-attribute is tested.
+.LP
+.SH OPTIONS
+.TP
+.BI \-b \ DN
+specify the
+.I DN
+which access is requested to; the corresponding entry is fetched
+from the database, and thus it must exist.
+The
+.I DN
+is also used to determine what rules apply; thus, it must be
+in the naming context of a configured database. By default, the first
+database that supports the requested operation is used. See also
+.BR \-u .
+
+.TP
+.BI \-d \ debug-level
+enable debugging messages as defined by the specified
+.IR debug-level ;
+see
+.BR slapd (8)
+for details.
+.TP
+.BI \-D \ authcDN
+specify a DN to be used as identity through the test session
+when selecting appropriate
+.B <by>
+clauses in access lists.
+.TP
+.BI \-f \ slapd.conf
+specify an alternative
+.BR slapd.conf (5)
+file.
+.TP
+.BI \-F \ confdir
+specify a config directory.
+If both
+.B \-f
+and
+.B \-F
+are specified, the config file will be read and converted to
+config directory format and written to the specified directory.
+If neither option is specified, an attempt to read the
+default config directory will be made before trying to use the default
+config file. If a valid config directory exists then the
+default config file is ignored.
+.TP
+.BI \-o \ option\fR[ = value\fR]
+Specify an
+.I option
+with a(n optional)
+.IR value .
+Possible generic options/values are:
+.LP
+.nf
+ syslog=<subsystems> (see `\-s' in slapd(8))
+ syslog\-level=<level> (see `\-S' in slapd(8))
+ syslog\-user=<user> (see `\-l' in slapd(8))
+
+.fi
+.RS
+Possible options/values specific to
+.B slapacl
+are:
+.RE
+.nf
+
+ authzDN
+ domain
+ peername
+ sasl_ssf
+ sockname
+ sockurl
+ ssf
+ tls_ssf
+ transport_ssf
+
+.fi
+.RS
+See the related fields in
+.BR slapd.access (5)
+for details.
+.RE
+.TP
+.BI \-u
+do not fetch the entry from the database.
+In this case, if the entry does not exist, a fake entry with the
+.I DN
+given with the
+.B \-b
+option is used, with no attributes.
+As a consequence, those rules that depend on the contents
+of the target object will not behave as with the real object.
+The
+.I DN
+given with the
+.B \-b
+option is still used to select what rules apply; thus, it must be
+in the naming context of a configured database.
+See also
+.BR \-b .
+.TP
+.BI \-U \ authcID
+specify an ID to be mapped to a
+.B DN
+as by means of
+.B authz\-regexp
+or
+.B authz\-rewrite
+rules (see
+.BR slapd.conf (5)
+for details); mutually exclusive with
+.BR \-D .
+.TP
+.B \-v
+enable verbose mode.
+.TP
+.BI \-X \ authzID
+specify an authorization ID to be mapped to a
+.B DN
+as by means of
+.B authz\-regexp
+or
+.B authz\-rewrite
+rules (see
+.BR slapd.conf (5)
+for details); mutually exclusive with \fB\-o\fP \fBauthzDN=\fIDN\fR.
+.SH EXAMPLES
+The command
+.LP
+.nf
+.ft tt
+ SBINDIR/slapacl \-f ETCDIR/slapd.conf \-v \\
+ \-U bjorn \-b "o=University of Michigan,c=US" \\
+ "o/read:University of Michigan"
+
+.ft
+.fi
+tests whether the user
+.I bjorn
+can access the attribute
+.I o
+of the entry
+.I o=University of Michigan,c=US
+at
+.I read
+level.
+.SH "SEE ALSO"
+.BR ldap (3),
+.BR slapd (8),
+.BR slaptest (8),
+.BR slapauth (8)
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man8/slapadd.8 b/doc/man/man8/slapadd.8
new file mode 100644
index 0000000..d31d440
--- /dev/null
+++ b/doc/man/man8/slapadd.8
@@ -0,0 +1,218 @@
+.TH SLAPADD 8C "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapadd \- Add entries to a SLAPD database
+.SH SYNOPSIS
+.B SBINDIR/slapadd
+[\c
+.BI \-b \ suffix\fR]
+[\c
+.BR \-c ]
+[\c
+.BI \-d \ debug-level\fR]
+[\c
+.BI \-f \ slapd.conf\fR]
+[\c
+.BI \-F \ confdir\fR]
+[\c
+.BR \-g ]
+[\c
+.BI \-j \ lineno\fR]
+[\c
+.BI \-l \ ldif-file\fR]
+[\c
+.BI \-n \ dbnum\fR]
+[\c
+.BI \-o \ option\fR[ = value\fR]]
+[\c
+.BR \-q ]
+[\c
+.BR \-s ]
+[\c
+.BI \-S \ SID\fR]
+[\c
+.BR \-u ]
+[\c
+.BR \-v ]
+[\c
+.BR \-w ]
+.SH DESCRIPTION
+.LP
+.B Slapadd
+is used to add entries specified in LDAP Directory Interchange Format
+(LDIF) to a
+.BR slapd (8)
+database.
+It opens the given database determined by the database number or
+suffix and adds entries corresponding to the provided LDIF to
+the database.
+Databases configured as
+.B subordinate
+of this one are also updated, unless \fB\-g\fP is specified.
+The LDIF input is read from standard input or the specified file.
+
+All files eventually created by
+.BR slapadd
+will belong to the identity
+.BR slapadd
+is run as, so make sure you either run
+.BR slapadd
+with the same identity
+.BR slapd (8)
+will be run as (see option
+.B \-u
+in
+.BR slapd (8)),
+or change file ownership before running
+.BR slapd (8).
+
+Note: slapadd will also perform the relevant indexing whilst adding the database if
+any are configured. For specific details, please see
+.BR slapindex (8).
+.SH OPTIONS
+.TP
+.BI \-b \ suffix
+Use the specified \fIsuffix\fR to determine which database to
+add entries to. By default, the first database that supports the requested
+operation is used. The \fB\-b\fP cannot be used in conjunction with the
+.B \-n
+option.
+.TP
+.B \-c
+enable continue (ignore errors) mode.
+.TP
+.BI \-d \ debug-level
+enable debugging messages as defined by the specified
+.IR debug-level ;
+see
+.BR slapd (8)
+for details.
+.TP
+.BI \-f \ slapd.conf
+specify an alternative
+.BR slapd.conf (5)
+file.
+.TP
+.BI \-F \ confdir
+specify a config directory.
+If both
+.B \-f
+and
+.B \-F
+are specified, the config file will be read and converted to
+config directory format and written to the specified directory.
+If neither option is specified, an attempt to read the
+default config directory will be made before trying to use the default
+config file. If a valid config directory exists then the
+default config file is ignored. If dry-run mode is also specified,
+no conversion will occur.
+.TP
+.B \-g
+disable subordinate gluing. Only the specified database will be
+processed, and not its glued subordinates (if any).
+.TP
+.BI \-j \ lineno
+Jump to the specified line number in the LDIF file before processing
+any entries. This allows a load that was aborted due to errors in the
+input LDIF to be resumed after the errors are corrected.
+.TP
+.BI \-l \ ldif-file
+Read LDIF from the specified file instead of standard input.
+.TP
+.BI \-n \ dbnum
+Add entries to the \fIdbnum\fR-th database listed in the
+configuration file. The
+.B \-n
+cannot be used in conjunction with the
+.B \-b
+option.
+To populate the config database
+.BR slapd\-config (5),
+use
+.B \-n 0
+as it is always the first database. It must physically exist
+on the filesystem prior to this, however.
+.TP
+.BI \-o \ option\fR[ = value\fR]
+Specify an
+.I option
+with a(n optional)
+.IR value .
+Possible generic options/values are:
+.LP
+.nf
+ syslog=<subsystems> (see `\-s' in slapd(8))
+ syslog\-level=<level> (see `\-S' in slapd(8))
+ syslog\-user=<user> (see `\-l' in slapd(8))
+
+ schema-check={yes|no}
+ value-check={yes|no}
+
+.in
+The \fIschema\-check\fR option toggles schema checking (default on);
+the \fIvalue\-check\fR option toggles value checking (default off).
+The latter is incompatible with \fB-q\fR.
+.TP
+.B \-q
+enable quick (fewer integrity checks) mode. Does fewer consistency checks
+on the input data, and no consistency checks when writing the database.
+Improves the load time but if any errors or interruptions occur the resulting
+database will be unusable.
+.TP
+.B \-s
+disable schema checking. This option is intended to be used when loading
+databases containing special objects, such as fractional objects on a
+partial consumer. Loading normal objects which do not conform to
+schema may result in unexpected and ill behavior.
+.TP
+.BI \-S \ SID
+Server ID to use in generated entryCSN. Also used for contextCSN
+if \fB\-w\fP is set as well. Defaults to \fB0\fP.
+.TP
+.B \-u
+enable dry-run (don't write to backend) mode.
+.TP
+.B \-v
+enable verbose mode.
+.TP
+.BI \-w
+write syncrepl context information.
+After all entries are added, the contextCSN
+will be updated with the greatest CSN in the database.
+.SH LIMITATIONS
+Your
+.BR slapd (8)
+should not be running
+when you do this to ensure consistency of the database.
+.LP
+.B slapadd
+may not provide naming or schema checks. It is advisable to
+use
+.BR ldapadd (1)
+when adding new entries into an existing directory.
+.SH EXAMPLES
+To import the entries specified in file
+.B ldif
+into your
+.BR slapd (8)
+database give the command:
+.LP
+.nf
+.ft tt
+ SBINDIR/slapadd \-l ldif
+.ft
+.fi
+.SH "SEE ALSO"
+.BR ldap (3),
+.BR ldif (5),
+.BR slapcat (8),
+.BR slapindex (8),
+.BR slapmodify (8),
+.BR ldapadd (1),
+.BR slapd (8)
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man8/slapauth.8 b/doc/man/man8/slapauth.8
new file mode 100644
index 0000000..17e529e
--- /dev/null
+++ b/doc/man/man8/slapauth.8
@@ -0,0 +1,152 @@
+.TH SLAPAUTH 8C "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2004-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapauth \- Check a list of string-represented IDs for LDAP authc/authz
+.SH SYNOPSIS
+.B SBINDIR/slapauth
+[\c
+.BI \-d \ debug-level\fR]
+[\c
+.BI \-f \ slapd.conf\fR]
+[\c
+.BI \-F \ confdir\fR]
+[\c
+.BI \-M \ mech\fR]
+[\c
+.BI \-o \ option\fR[ = value\fR]]
+[\c
+.BI \-R \ realm\fR]
+[\c
+.BI \-U \ authcID\fR]
+[\c
+.BR \-v ]
+[\c
+.BI \-X \ authzID\fR]
+.IR ID \ [ ... ]
+.LP
+.SH DESCRIPTION
+.LP
+.B Slapauth
+is used to check the behavior of the slapd in mapping identities
+for authentication and authorization purposes, as specified in
+.BR slapd.conf (5).
+It opens the
+.BR slapd.conf (5)
+configuration file or the
+.BR slapd\-config (5)
+backend, reads in the
+.BR authz\-policy / olcAuthzPolicy
+and
+.BR authz\-regexp / olcAuthzRegexp
+directives, and then parses the
+.I ID
+list given on the command-line.
+.LP
+.SH OPTIONS
+.TP
+.BI \-d \ debug-level
+enable debugging messages as defined by the specified
+.IR debug-level ;
+see
+.BR slapd (8)
+for details.
+.TP
+.BI \-f \ slapd.conf
+specify an alternative
+.BR slapd.conf (5)
+file.
+.TP
+.BI \-F \ confdir
+specify a config directory.
+If both
+.B \-f
+and
+.B \-F
+are specified, the config file will be read and converted to
+config directory format and written to the specified directory.
+If neither option is specified, an attempt to read the
+default config directory will be made before trying to use the default
+config file. If a valid config directory exists then the
+default config file is ignored.
+.TP
+.BI \-M \ mech
+specify a mechanism.
+.TP
+.BI \-o \ option\fR[ = value\fR]
+Specify an
+.I option
+with a(n optional)
+.IR value .
+Possible generic options/values are:
+.LP
+.nf
+ syslog=<subsystems> (see `\-s' in slapd(8))
+ syslog\-level=<level> (see `\-S' in slapd(8))
+ syslog\-user=<user> (see `\-l' in slapd(8))
+
+.fi
+.TP
+.BI \-R \ realm
+specify a realm.
+.TP
+.BI \-U \ authcID
+specify an ID to be used as
+.I authcID
+throughout the test session.
+If present, and if no
+.I authzID
+is given, the IDs in the ID list are treated as
+.IR authzID .
+.TP
+.BI \-X \ authzID
+specify an ID to be used as
+.I authzID
+throughout the test session.
+If present, and if no
+.I authcID
+is given, the IDs in the ID list are treated as
+.IR authcID .
+If both
+.I authcID
+and
+.I authzID
+are given via command line switch, the ID list cannot be present.
+.TP
+.B \-v
+enable verbose mode.
+.SH EXAMPLES
+The command
+.LP
+.nf
+.ft tt
+ SBINDIR/slapauth \-f /ETCDIR/slapd.conf \-v \\
+ \-U bjorn \-X u:bjensen
+
+.ft
+.fi
+tests whether the user
+.I bjorn
+can assume the identity of the user
+.I bjensen
+provided the directives
+.LP
+.nf
+.ft tt
+ authz\-policy from
+ authz\-regexp "^uid=([^,]+).*,cn=auth$"
+ "ldap:///dc=example,dc=net??sub?uid=$1"
+
+.ft
+.fi
+are defined in
+.BR slapd.conf (5).
+.SH "SEE ALSO"
+.BR ldap (3),
+.BR slapd (8),
+.BR slaptest (8)
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man8/slapcat.8 b/doc/man/man8/slapcat.8
new file mode 100644
index 0000000..c836a04
--- /dev/null
+++ b/doc/man/man8/slapcat.8
@@ -0,0 +1,203 @@
+.TH SLAPCAT 8C "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapcat \- SLAPD database to LDIF utility
+.SH SYNOPSIS
+.B SBINDIR/slapcat
+[\c
+.BI \-a filter\fR]
+[\c
+.BI \-b suffix\fR]
+[\c
+.BR \-c ]
+[\c
+.BI \-d debug-level\fR]
+[\c
+.BI \-f slapd.conf\fR]
+[\c
+.BI \-F confdir\fR]
+[\c
+.BR \-g ]
+[\c
+.BI \-H URI\fR]
+[\c
+.BI \-l ldif-file\fR]
+[\c
+.BI \-n dbnum\fR]
+[\c
+.BI \-o option\fR[ = value\fR]]
+[\c
+.BI \-s subtree-dn\fR]
+[\c
+.BR \-v ]
+.LP
+.SH DESCRIPTION
+.LP
+.B Slapcat
+is used to generate an LDAP Directory Interchange Format
+(LDIF) output based upon the contents of a
+.BR slapd (8)
+database.
+It opens the given database determined by the database number or
+suffix and writes the corresponding LDIF to standard output or
+the specified file.
+Databases configured as
+.B subordinate
+of this one are also output, unless \fB\-g\fP is specified.
+.LP
+The entry records are presented in database order, not superior first
+order. The entry records will include all (user and operational)
+attributes stored in the database. The entry records will not include
+dynamically generated attributes (such as subschemaSubentry).
+.LP
+The output of slapcat is intended to be used as input to
+.BR slapadd (8).
+The output of slapcat cannot generally be used as input to
+.BR ldapadd (1)
+or other LDAP clients without first editing the output.
+This editing would normally include reordering the records
+into superior first order and removing no-user-modification
+operational attributes.
+.SH OPTIONS
+.TP
+.BI \-a \ filter
+Only dump entries matching the asserted filter.
+For example
+
+slapcat \-a \\
+ "(!(entryDN:dnSubtreeMatch:=ou=People,dc=example,dc=com))"
+
+will dump all but the "ou=People,dc=example,dc=com" subtree
+of the "dc=example,dc=com" database.
+Deprecated; use \fB-H\fP \fIldap:///???(filter)\fP instead.
+.TP
+.BI \-b \ suffix
+Use the specified \fIsuffix\fR to determine which database to
+generate output for. By default, the first database that supports the requested
+operation is used. The \fB\-b\fP cannot be used in conjunction with the
+.B \-n
+option.
+.TP
+.B \-c
+Enable continue (ignore errors) mode.
+Multiple occurrences of
+.B \-c
+make
+.BR slapcat (8)
+try harder.
+.TP
+.BI \-d \ debug-level
+Enable debugging messages as defined by the specified
+.IR debug-level ;
+see
+.BR slapd (8)
+for details.
+.TP
+.BI \-f \ slapd.conf
+Specify an alternative
+.BR slapd.conf (5)
+file.
+.TP
+.BI \-F \ confdir
+specify a config directory.
+If both
+.B \-f
+and
+.B \-F
+are specified, the config file will be read and converted to
+config directory format and written to the specified directory.
+If neither option is specified, an attempt to read the
+default config directory will be made before trying to use the default
+config file. If a valid config directory exists then the
+default config file is ignored.
+.TP
+.B \-g
+disable subordinate gluing. Only the specified database will be
+processed, and not its glued subordinates (if any).
+.TP
+.B \-H \ URI
+use dn, scope and filter from URI to only handle matching entries.
+.TP
+.BI \-l \ ldif-file
+Write LDIF to specified file instead of standard output.
+.TP
+.BI \-n \ dbnum
+Generate output for the \fIdbnum\fR-th database listed in the
+configuration file. The config database
+.BR slapd\-config (5),
+is always the first database, so use
+.B \-n 0
+to select it.
+
+The
+.B \-n
+cannot be used in conjunction with the
+.B \-b
+option.
+.TP
+.BI \-o \ option\fR[ = value\fR]
+Specify an
+.I option
+with a(n optional)
+.IR value .
+Possible generic options/values are:
+.LP
+.nf
+ syslog=<subsystems> (see `\-s' in slapd(8))
+ syslog\-level=<level> (see `\-S' in slapd(8))
+ syslog\-user=<user> (see `\-l' in slapd(8))
+
+ ldif_wrap={no|<n>}
+
+.in
+\fIn\fP is the number of columns allowed for the LDIF output
+(\fIn\fP equal to \fI0\fP uses the default, corresponding to 78).
+The minimum is 2, leaving space for one character and one
+continuation character.
+Use \fIno\fP for no wrap.
+.TP
+.BI \-s \ subtree-dn
+Only dump entries in the subtree specified by this DN.
+Implies \fB\-b\fP \fIsubtree-dn\fP if no
+.B \-b
+or
+.B \-n
+option is given.
+Deprecated; use \fB-H\fP \fIldap:///subtree-dn\fP instead.
+.TP
+.B \-v
+Enable verbose mode.
+.SH LIMITATIONS
+For some backend types, your
+.BR slapd (8)
+should not be running (at least, not in read-write
+mode) when you do this to ensure consistency of the database. It is
+always safe to run
+.B slapcat
+with the
+.BR slapd\-mdb (5),
+and
+.BR slapd\-null (5)
+backends.
+.SH EXAMPLES
+To make a text backup of your SLAPD database and put it in a file called
+.BR ldif ,
+give the command:
+.LP
+.nf
+.ft tt
+ SBINDIR/slapcat \-l ldif
+.ft
+.fi
+.SH "SEE ALSO"
+.BR ldap (3),
+.BR ldif (5),
+.BR slapadd (8),
+.BR ldapadd (1),
+.BR slapd (8)
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man8/slapd.8 b/doc/man/man8/slapd.8
new file mode 100644
index 0000000..a93fcbc
--- /dev/null
+++ b/doc/man/man8/slapd.8
@@ -0,0 +1,377 @@
+.TH SLAPD 8C "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd \- Stand-alone LDAP Daemon
+.SH SYNOPSIS
+.B LIBEXECDIR/slapd
+[\c
+.BR \-V [ V [ V ]]
+[\c
+.BR \-4 | \-6 ]
+[\c
+.BR \-T \ { acl \||\| a [ dd ]\||\| auth \||\| c [ at ]\||\|
+.BR d [ n ]\||\| i [ ndex ]\||\| p [ asswd ]\||\| s [ chema ]\||\| t [ est ]}]
+[\c
+.BI \-d \ debug-level\fR]
+[\c
+.BI \-f \ slapd-config-file\fR]
+[\c
+.BI \-F \ slapd-config-directory\fR]
+[\c
+.BI \-h \ URLs\fR]
+[\c
+.BI \-n \ service-name\fR]
+[\c
+.BI \-s \ syslog-level\fR]
+[\c
+.BI \-l \ syslog-local-user\fR]
+[\c
+.BI \-o \ option\fR[ = value\fR]]
+[\c
+.BI \-r \ directory\fR]
+[\c
+.BI \-u \ user\fR]
+[\c
+.BI \-g \ group\fR]
+[\c
+.BI \-c \ cookie\fR]
+.SH DESCRIPTION
+.LP
+.B Slapd
+is the stand-alone LDAP daemon. It listens for LDAP connections on
+any number of ports (default \fB389\fP), responding
+to the LDAP operations it receives over these connections.
+.B slapd
+is typically invoked at boot time, usually out of
+.BR /etc/rc.local .
+Upon startup,
+.B slapd
+normally forks and disassociates itself from the invoking tty.
+If configured in the config file (or config directory),
+the
+.B slapd
+process will print its process ID (see
+.BR getpid (2))
+to a
+.B .pid
+file, as well as the command line options during invocation to an
+.B .args
+file (see
+.BR slapd.conf (5)).
+If the
+.B \-d
+flag is given, even with a zero argument,
+.B slapd
+will not fork and disassociate from the invoking tty.
+.LP
+See the "OpenLDAP Administrator's Guide" for more details on
+.BR slapd .
+.SH OPTIONS
+.TP
+.BR \-V [ V [ V ]]
+Print version info and proceed with startup.
+If \fB\-VV\fP is given, exit after providing version info. If \fB\-VVV\fP is
+given, additionally provide information on static overlays and backends.
+.TP
+.B \-4
+Listen on IPv4 addresses only.
+.TP
+.B \-6
+Listen on IPv6 addresses only.
+.TP
+.BI \-T \ tool
+Run in Tool mode. The \fItool\fP argument selects whether to run as
+.IR slapadd ,
+.IR slapcat ,
+.IR slapdn ,
+.IR slapindex ,
+.IR slappasswd ,
+.IR slapschema ,
+or
+.I slaptest
+(\fIslapacl\fP and \fIslapauth\fP need the entire \fBacl\fP and \fBauth\fP
+option value to be spelled out, as \fBa\fP is reserved to
+.IR slapadd ).
+This option should be the first option specified when it is used;
+any remaining options will be interpreted by the corresponding
+slap tool program, according to the respective man pages.
+Note that these tool programs will usually be symbolic links to
+.BR slapd .
+This option is provided for situations where symbolic links
+are not provided or not usable.
+.TP
+.BI \-d \ debug-level
+Turn on debugging as defined by
+.IR debug-level .
+If this option is specified, even with a zero argument,
+.B slapd
+will not fork or disassociate from the invoking terminal. Some general
+operation and status messages are printed for any value of \fIdebug-level\fP.
+\fIdebug-level\fP is taken as a bit string, with each bit corresponding to a
+different kind of debugging information. See <ldap_log.h> for details.
+Comma-separated arrays of friendly names can be specified to select
+debugging output of the corresponding debugging information.
+All the names recognized by the \fIloglevel\fP directive
+described in \fBslapd.conf\fP(5) are supported.
+If \fIdebug-level\fP is \fB?\fP, a list of installed debug-levels is printed,
+and slapd exits.
+
+Remember that if you turn on packet logging, packets containing bind passwords
+will be output, so if you redirect the log to a logfile, that file should
+be read-protected.
+.TP
+.BI \-s \ syslog-level
+This option tells
+.B slapd
+at what debug-level debugging statements should be logged to the
+.BR syslog (8)
+facility.
+The value \fIsyslog-level\fP can be set to any value or combination
+allowed by the \fB\-d\fP switch.
+Slapd logs all messages selected by \fIsyslog-level\fP
+at the
+.BR syslog (3)
+severity debug-level \fBDEBUG\fP,
+on the unit specified with \fB\-l\fP.
+.TP
+.BI \-n \ service-name
+Specifies the service name for logging and other purposes. Defaults
+to basename of argv[0], i.e.: "slapd".
+.TP
+.BI \-l \ syslog-local-user
+Selects the local user of the
+.BR syslog (8)
+facility. Value can be
+.BR LOCAL0 ,
+through
+.BR LOCAL7 ,
+as well as
+.B USER
+and
+.BR DAEMON .
+The default is
+.BR LOCAL4 .
+However, this option is only permitted on systems that support
+local users with the
+.BR syslog (8)
+facility.
+Logging to syslog(8) occurs at the "DEBUG" severity debug-level.
+.TP
+.BI \-f \ slapd-config-file
+Specifies the slapd configuration file. The default is
+.BR ETCDIR/slapd.conf .
+.TP
+.BI \-F \ slapd-config-directory
+Specifies the slapd configuration directory. The default is
+.BR ETCDIR/slapd.d .
+If both
+.B \-f
+and
+.B \-F
+are specified, the config file will be read and converted to
+config directory format and written to the specified directory.
+If neither option is specified, slapd will attempt to read the
+default config directory before trying to use the default
+config file. If a valid config directory exists then the
+default config file is ignored. All of the slap tools that
+use the config options observe this same behavior.
+.TP
+.BI \-h \ URLlist
+.B slapd
+will by default serve
+.B ldap:///
+(LDAP over TCP on all interfaces on default LDAP port). That is,
+it will bind using INADDR_ANY and port \fB389\fP.
+The
+.B \-h
+option may be used to specify LDAP (and other scheme) URLs to serve.
+For example, if slapd is given
+.BR "\-h \(dqldap://127.0.0.1:9009/ ldaps:/// ldapi:///\(dq" ,
+it will listen on 127.0.0.1:9009 for LDAP, 0.0.0.0:636 for LDAP over TLS,
+and LDAP over IPC (Unix domain sockets). Host 0.0.0.0 represents
+INADDR_ANY (any interface).
+A space separated list of URLs is expected. The URLs should be of the LDAP,
+PLDAP, LDAPS, PLDAPS, or LDAPI schemes, and generally without a DN or other
+optional parameters (excepting as discussed below). Support for the latter
+three schemes depends on selected configuration options. Hosts may be specified
+by name or IPv4 and IPv6 address formats. Ports, if specified, must be
+numeric. The default ldap:// port is \fB389\fP and the default ldaps:// port
+is \fB636\fP, same for the proxy enabled variants.
+
+The PLDAP and PLDAPS URL schemes provide support for the HAProxy proxy protocol
+version 2, which allows a load balancer or proxy server to provide the remote
+client IP address to slapd to be used for access control or logging. Ports
+configured for PLDAP or PLDAPS will only accept connections that include the
+necessary proxy protocol header. Connections to these ports should be
+restricted at the network level to only trusted load balancers or proxies to
+avoid spoofing of client IP addresses by third parties.
+
+For LDAP over IPC,
+.B name
+is the name of the socket, and no
+.B port
+is required, nor allowed; note that directory separators must be
+URL-encoded, like any other characters that are special to URLs;
+so the socket
+
+ /usr/local/var/ldapi
+
+must be specified as
+
+ ldapi://%2Fusr%2Flocal%2Fvar%2Fldapi
+
+The default location for the IPC socket is LOCALSTATEDIR/run/ldapi
+
+The listener permissions are indicated by
+"x\-mod=\-rwxrwxrwx", "x\-mod=0777" or "x\-mod=777", where any
+of the "rwx" can be "\-" to suppress the related permission, while any
+of the "7" can be any legal octal digit, according to chmod(1).
+The listeners can take advantage of the "x\-mod"
+extension to apply rough limitations to operations, e.g. allow read operations
+("r", which applies to search and compare), write operations ("w",
+which applies to add, delete, modify and modrdn), and execute operations
+("x", which means bind is required).
+"User" permissions apply to authenticated users, while "other" apply
+to anonymous users; "group" permissions are ignored.
+For example, "ldap:///????x\-mod=\-rw\-\-\-\-\-\-\-" means that read and write is only allowed
+for authenticated connections, and bind is required for all operations.
+This feature is experimental, and requires to be manually enabled
+at configure time.
+.TP
+.BI \-r \ directory
+Specifies a directory to become the root directory. slapd will
+change the current working directory to this directory and
+then
+.BR chroot (2)
+to this directory. This is done after opening listeners but before
+reading any configuration file or initializing any backend. When
+used as a security mechanism, it should be used in conjunction with
+.B \-u
+and
+.B \-g
+options.
+.TP
+.BI \-u \ user
+.B slapd
+will run slapd with the specified user name or id, and that user's
+supplementary group access list as set with initgroups(3). The group ID
+is also changed to this user's gid, unless the \fB\-g\fP option is used to
+override. Note when used with
+.BR \-r ,
+slapd will use the user database in the change root environment.
+
+Note that on some systems, running as a non-privileged user will prevent
+passwd back-ends from accessing the encrypted passwords. Note also that
+any shell back-ends will run as the specified non-privileged user.
+.TP
+.BI \-g \ group
+.B slapd
+will run with the specified group name or id. Note when used with
+.BR \-r ,
+slapd will use the group database in the change root environment.
+.TP
+.BI \-c \ cookie
+This option provides a cookie for the syncrepl replication consumer.
+The cookie is a comma separated list of \fIname=value\fP pairs.
+Currently supported syncrepl cookie fields are
+.BR rid ,
+.BR sid ,
+and
+.BR csn .
+.B rid
+identifies a replication thread within the consumer server
+and is used to find the syncrepl specification in
+.BR slapd.conf (5)
+or
+.BR slapd\-config (5)
+having the matching replication identifier in its definition. The
+.B rid
+must be provided in order for any other specified values to be used.
+.B sid
+is the server id in a multi-provider configuration.
+.B csn
+is the commit sequence number received by a previous synchronization
+and represents the state of the consumer content which the
+syncrepl engine will synchronize to the current provider content.
+In case of \fImulti-provider\fP replication agreement,
+multiple
+.B csn
+values, semicolon separated, can appear.
+Use only the
+.B rid
+part to force a full reload.
+.TP
+.BI \-o \ option\fR[ = value\fR]
+This option provides a generic means to specify options without the need to reserve
+a separate letter for them.
+
+It supports the following options:
+.RS
+.TP
+.BR slp= { on \||\| off \||\| \fIslp-attrs\fP }
+When SLP support is compiled into slapd, disable it (\fBoff\fP),
+ enable it by registering at SLP DAs without specific SLP attributes (\fBon\fP),
+or with specific SLP attributes
+.I slp-attrs
+that must be an SLP attribute list definition according to the SLP standard.
+
+For example, \fB"slp=(tree=production),(server-type=OpenLDAP),(server\-version=2.4.15)"\fP
+registers at SLP DAs with the three SLP attributes tree, server-type and server-version
+that have the values given above.
+This allows one to specifically query the SLP DAs for LDAP servers holding the
+.I production
+tree in case multiple trees are available.
+.RE
+.SH EXAMPLES
+To start
+.I slapd
+and have it fork and detach from the terminal and start serving
+the LDAP databases defined in the default config file, just type:
+.LP
+.nf
+.ft tt
+ LIBEXECDIR/slapd
+.ft
+.fi
+.LP
+To start
+.B slapd
+with an alternate configuration file, and turn
+on voluminous debugging which will be printed on standard error, type:
+.LP
+.nf
+.ft tt
+ LIBEXECDIR/slapd \-f /var/tmp/slapd.conf \-d 255
+.ft
+.fi
+.LP
+To test whether the configuration file is correct or not, type:
+.LP
+.nf
+.ft tt
+ LIBEXECDIR/slapd \-Tt
+.ft
+.fi
+.LP
+.SH "SEE ALSO"
+.BR ldap (3),
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapd.access (5),
+.BR slapacl (8),
+.BR slapadd (8),
+.BR slapauth (8),
+.BR slapcat (8),
+.BR slapdn (8),
+.BR slapindex (8),
+.BR slappasswd (8),
+.BR slapschema (8),
+.BR slaptest (8).
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.SH BUGS
+See http://www.openldap.org/its/
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man8/slapdn.8 b/doc/man/man8/slapdn.8
new file mode 100644
index 0000000..424bf83
--- /dev/null
+++ b/doc/man/man8/slapdn.8
@@ -0,0 +1,108 @@
+.TH SLAPDN 8C "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2004-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapdn \- Check a list of string-represented LDAP DNs based on schema syntax
+.SH SYNOPSIS
+.B SBINDIR/slapdn
+[\c
+.BI \-d \ debug-level\fR]
+[\c
+.BI \-f \ slapd.conf\fR]
+[\c
+.BI \-F \ confdir\fR]
+[\c
+.BR \-N | \-P ]
+[\c
+.BI \-o \ option\fR[ = value\fR]]
+[\c
+.BR \-v ]
+.IR DN \ [...]
+.LP
+.SH DESCRIPTION
+.LP
+.B Slapdn
+is used to check the conformance of a DN based on the schema
+defined in
+.BR slapd (8)
+and that loaded via
+.BR slapd.conf (5).
+It opens the
+.BR slapd.conf (5)
+configuration file or the slapd\-config (5) backend, reads in the schema definitions, and then
+parses the
+.I DN
+list given on the command-line.
+.LP
+.SH OPTIONS
+.TP
+.BI \-d \ debug-level
+enable debugging messages as defined by the specified
+.IR debug-level ;
+see
+.BR slapd (8)
+for details.
+.TP
+.BI \-f \ slapd.conf
+specify an alternative
+.BR slapd.conf (5)
+file.
+.TP
+.BI \-F \ confdir
+specify a config directory.
+If both
+.B \-f
+and
+.B \-F
+are specified, the config file will be read and converted to
+config directory format and written to the specified directory.
+If neither option is specified, an attempt to read the
+default config directory will be made before trying to use the default
+config file. If a valid config directory exists then the
+default config file is ignored.
+.TP
+.BI \-N
+only output a normalized form of the \fIDN\fP, suitable to be used
+in a normalization tool; incompatible with
+.BR \-P .
+.TP
+.BI \-o \ option\fR[ = value\fR]
+Specify an
+.I option
+with a(n optional)
+.IR value .
+Possible generic options/values are:
+.LP
+.nf
+ syslog=<subsystems> (see `\-s' in slapd(8))
+ syslog\-level=<level> (see `\-S' in slapd(8))
+ syslog\-user=<user> (see `\-l' in slapd(8))
+
+.fi
+.TP
+.BI \-P
+only output a prettified form of the \fIDN\fP, suitable to be used
+in a check and beautification tool; incompatible with
+.BR \-N .
+.TP
+.B \-v
+enable verbose mode.
+.SH EXAMPLES
+To check a
+.B DN
+give the command:
+.LP
+.nf
+.ft tt
+ SBINDIR/slapdn \-f /ETCDIR/slapd.conf \-v DN
+.ft
+.fi
+.SH "SEE ALSO"
+.BR ldap (3),
+.BR slapd (8),
+.BR slaptest (8)
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man8/slapindex.8 b/doc/man/man8/slapindex.8
new file mode 100644
index 0000000..9cadb64
--- /dev/null
+++ b/doc/man/man8/slapindex.8
@@ -0,0 +1,178 @@
+.TH SLAPINDEX 8C "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapindex \- Reindex entries in a SLAPD database
+.SH SYNOPSIS
+.B SBINDIR/slapindex
+[\c
+.BI \-b \ suffix\fR]
+[\c
+.BR \-c ]
+[\c
+.BI \-d \ debug-level\fR]
+[\c
+.BI \-f \ slapd.conf\fR]
+[\c
+.BI \-F \ confdir\fR]
+[\c
+.BR \-g ]
+[\c
+.BI \-n \ dbnum\fR]
+[\c
+.BI \-o \ option\fR[ = value\fR]]
+[\c
+.BR \-q ]
+[\c
+.BR \-t ]
+[\c
+.BR \-v ]
+[\c
+.IR attr [ ... ]]
+.B
+.LP
+.SH DESCRIPTION
+.LP
+.B Slapindex
+is used to regenerate
+.BR slapd (8)
+indices based upon the current contents of a database.
+It opens the given database determined by the database number or
+suffix and updates the indices for all values of all attributes
+of all entries. If a list of specific attributes is provided
+on the command line, only the indices for those attributes will
+be regenerated.
+Databases configured as
+.B subordinate
+of this one are also re-indexed, unless \fB\-g\fP is specified.
+
+All files eventually created by
+.BR slapindex
+will belong to the identity
+.BR slapindex
+is run as, so make sure you either run
+.BR slapindex
+with the same identity
+.BR slapd (8)
+will be run as (see option
+.B \-u
+in
+.BR slapd (8)),
+or change file ownership before running
+.BR slapd (8).
+.SH OPTIONS
+.TP
+.BI \-b \ suffix
+Use the specified \fIsuffix\fR to determine which database to
+generate output for. By default, the first database that supports the requested
+operation is used. The \fB\-b\fP cannot be used in conjunction with the
+.B \-n
+option.
+.TP
+.B \-c
+enable continue (ignore errors) mode.
+.TP
+.BI \-d \ debug-level
+enable debugging messages as defined by the specified
+.IR debug-level ;
+see
+.BR slapd (8)
+for details.
+.TP
+.BI \-f \ slapd.conf
+specify an alternative
+.BR slapd.conf (5)
+file.
+.TP
+.BI \-F \ confdir
+specify a config directory.
+If both
+.B \-f
+and
+.B \-F
+are specified, the config file will be read and converted to
+config directory format and written to the specified directory.
+If neither option is specified, an attempt to read the
+default config directory will be made before trying to use the default
+config file. If a valid config directory exists then the
+default config file is ignored.
+.TP
+.B \-g
+disable subordinate gluing. Only the specified database will be
+processed, and not its glued subordinates (if any).
+.TP
+.BI \-n \ dbnum
+Generate output for the \fIdbnum\fR-th database listed in the
+configuration file. The config database
+.BR slapd\-config (5),
+is always the first database, so use
+.B \-n 0
+
+The
+.B \-n
+cannot be used in conjunction with the
+.B \-b
+option.
+.TP
+.BI \-o \ option\fR[ = value\fR]
+Specify an
+.I option
+with a(n optional)
+.IR value .
+Possible generic options/values are:
+.LP
+.nf
+ syslog=<subsystems> (see `\-s' in slapd(8))
+ syslog\-level=<level> (see `\-S' in slapd(8))
+ syslog\-user=<user> (see `\-l' in slapd(8))
+
+.fi
+.TP
+.B \-q
+enable quick (fewer integrity checks) mode. Performs no consistency checks
+when writing the database. Improves indexing time,
+.B however
+the database will most likely be unusable if any errors or
+interruptions occur.
+.TP
+.B \-t
+enable truncate mode. Truncates (empties) an index database before indexing
+any entries. May only be used with back-mdb.
+.TP
+.B \-v
+enable verbose mode.
+.SH LIMITATIONS
+Your
+.BR slapd (8)
+should not be running (at least, not in read-write
+mode) when you do this to ensure consistency of the database.
+.LP
+This command provides ample opportunity for the user to obtain
+and drink their favorite beverage.
+.SH EXAMPLES
+To reindex your SLAPD database, give the command:
+.LP
+.nf
+.ft tt
+ SBINDIR/slapindex
+.ft
+.fi
+To regenerate the index for only a specific attribute, e.g. "uid",
+give the command:
+.LP
+.nf
+.ft tt
+ SBINDIR/slapindex uid
+.ft
+.fi
+.SH "SEE ALSO"
+.BR ldap (3),
+.BR ldif (5),
+.BR slapadd (8),
+.BR ldapadd (1),
+.BR slapd (8)
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man8/slapmodify.8 b/doc/man/man8/slapmodify.8
new file mode 100644
index 0000000..98069be
--- /dev/null
+++ b/doc/man/man8/slapmodify.8
@@ -0,0 +1,222 @@
+.TH SLAPMODIFY 8C "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapmodify \- Modify entries in a SLAPD database
+.SH SYNOPSIS
+.B SBINDIR/slapmodify
+[\c
+.BI \-b \ suffix\fR]
+[\c
+.BR \-c ]
+[\c
+.BI \-d \ debug-level\fR]
+[\c
+.BI \-f \ slapd.conf\fR]
+[\c
+.BI \-F \ confdir\fR]
+[\c
+.BR \-g ]
+[\c
+.BI \-j \ lineno\fR]
+[\c
+.BI \-l \ ldif-file\fR]
+[\c
+.BI \-n \ dbnum\fR]
+[\c
+.BI \-o \ option\fR[ = value\fR]]
+[\c
+.BR \-q ]
+[\c
+.BR \-s ]
+[\c
+.BI \-S \ SID\fR]
+[\c
+.BR \-u ]
+[\c
+.BR \-v ]
+[\c
+.BR \-w ]
+.SH DESCRIPTION
+.LP
+.B Slapmodify
+is used to apply modifications specified in LDAP Directory Interchange Format
+(LDIF) to a
+.BR slapd (8)
+database.
+It opens the given database determined by the database number or
+suffix and performs modifications corresponding to the provided LDIF to
+the database.
+Databases configured as
+.B subordinate
+of this one are also updated, unless \fB\-g\fP is specified.
+The LDIF input is read from standard input or the specified file.
+
+All files eventually created by
+.BR slapmodify
+will belong to the identity
+.BR slapmodify
+is run as, so make sure you either run
+.BR slapmodify
+with the same identity
+.BR slapd (8)
+will be run as (see option
+.B \-u
+in
+.BR slapd (8)),
+or change file ownership before running
+.BR slapd (8).
+
+Note: slapmodify will also perform the relevant indexing whilst modifying the database if
+any are configured. For specific details, please see
+.BR slapindex (8).
+.SH OPTIONS
+.TP
+.BI \-b \ suffix
+Use the specified \fIsuffix\fR to determine which database to
+add entries to. The \fB\-b\fP cannot be used in conjunction
+with the
+.B \-n
+option.
+.TP
+.B \-c
+enable continue (ignore errors) mode.
+.TP
+.BI \-d \ debug-level
+enable debugging messages as defined by the specified
+.IR debug-level ;
+see
+.BR slapd (8)
+for details.
+.TP
+.BI \-f \ slapd.conf
+specify an alternative
+.BR slapd.conf (5)
+file.
+.TP
+.BI \-F \ confdir
+specify a config directory.
+If both
+.B \-f
+and
+.B \-F
+are specified, the config file will be read and converted to
+config directory format and written to the specified directory.
+If neither option is specified, an attempt to read the
+default config directory will be made before trying to use the default
+config file. If a valid config directory exists then the
+default config file is ignored. If dry-run mode is also specified,
+no conversion will occur.
+.TP
+.B \-g
+disable subordinate gluing. Only the specified database will be
+processed, and not its glued subordinates (if any).
+.TP
+.BI \-j \ lineno
+Jump to the specified line number in the LDIF file before processing
+any entries. This allows a modification that was aborted due to errors in the
+input LDIF to be resumed after the errors are corrected.
+.TP
+.BI \-l \ ldif-file
+Read LDIF from the specified file instead of standard input.
+.TP
+.BI \-n \ dbnum
+Perform changes on the \fIdbnum\fR-th database listed in the
+configuration file. The
+.B \-n
+cannot be used in conjunction with the
+.B \-b
+option.
+To manipulate the config database
+.BR slapd\-config (5),
+use
+.B \-n 0
+as it is always the first database. It must physically exist
+on the filesystem prior to this, however.
+.TP
+.BI \-o \ option\fR[ = value\fR]
+Specify an
+.I option
+with a(n optional)
+.IR value .
+Possible generic options/values are:
+.LP
+.nf
+ syslog=<subsystems> (see `\-s' in slapd(8))
+ syslog\-level=<level> (see `\-S' in slapd(8))
+ syslog\-user=<user> (see `\-l' in slapd(8))
+
+ schema-check={yes|no}
+ value-check={yes|no}
+
+.in
+The \fIschema\-check\fR option toggles schema checking (default on);
+the \fIvalue\-check\fR option toggles value checking (default off).
+The latter is incompatible with \fB-q\fR.
+.TP
+.B \-q
+enable quick (fewer integrity checks) mode. Does fewer consistency checks
+on the input data, and no consistency checks when writing the database.
+Improves the run time but if any errors or interruptions occur the resulting
+database will be unusable.
+.TP
+.B \-s
+disable schema checking. This option is intended to be used when
+manipulating databases containing special objects, such as fractional
+objects on a partial replica. Creating normal objects which do not
+conform to schema may result in unexpected and ill behavior.
+.TP
+.BI \-S \ SID
+Server ID to use in generated entryCSN. Also used for contextCSN
+if \fB\-w\fP is set as well. Defaults to \fB0\fP.
+.TP
+.B \-u
+enable dry-run (don't write to backend) mode.
+.TP
+.B \-v
+enable verbose mode.
+.TP
+.BI \-w
+write syncrepl context information.
+After all entries are added, the contextCSN
+will be updated with the greatest CSN in the database.
+.SH LIMITATIONS
+Your
+.BR slapd (8)
+should not be running
+when you do this to ensure consistency of the database.
+.LP
+Not all backends support all types of modification, \fImodrdn\fR
+changetype in particular is not implemented for any of the current
+backends.
+.LP
+.B slapmodify
+may not provide naming or schema checks. It is advisable to
+use
+.BR ldapmodify (1)
+when possible.
+.SH EXAMPLES
+To make modifications specified in file
+.B ldif
+into your
+.BR slapd (8)
+database give the command:
+.LP
+.nf
+.ft tt
+ SBINDIR/slapmodify \-l ldif
+.ft
+.fi
+.SH "SEE ALSO"
+.BR ldap (3),
+.BR ldif (5),
+.BR slapcat (8),
+.BR slapadd (8),
+.BR slapindex (8),
+.BR ldapmodify (1),
+.BR slapd (8)
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man8/slappasswd.8 b/doc/man/man8/slappasswd.8
new file mode 100644
index 0000000..7bca21d
--- /dev/null
+++ b/doc/man/man8/slappasswd.8
@@ -0,0 +1,203 @@
+.TH SLAPPASSWD 8C "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slappasswd \- OpenLDAP password utility
+.SH SYNOPSIS
+.B SBINDIR/slappasswd
+[\c
+.BR \-v ]
+[\c
+.BR \-u ]
+[\c
+.BR \-g \||\| \-s \ \fIsecret\fR \||\| \fB\-T \ \fIfile\fR]
+[\c
+.BI \-h \ hash\fR]
+[\c
+.BI \-c \ salt-format\fR]
+[\c
+.BR \-n ]
+[\c
+.BI \-o \ option\fR[ = value\fR]]
+.LP
+.SH DESCRIPTION
+.LP
+.B Slappasswd
+is used to generate an userPassword value
+suitable for use with
+.BR ldapmodify (1),
+.BR slapd.conf (5)
+.I rootpw
+configuration directive or the
+.BR slapd\-config (5)
+.I olcRootPW
+configuration directive.
+.
+.SH OPTIONS
+.TP
+.B \-v
+enable verbose mode.
+.TP
+.B \-u
+Generate RFC 2307 userPassword values (the default). Future
+versions of this program may generate alternative syntaxes
+by default. This option is provided for forward compatibility.
+.TP
+.BI \-s \ secret
+The secret to hash.
+If this,
+.B \-g
+and
+.B \-T
+are absent, the user will be prompted for the secret to hash.
+.BR \-s ,
+.B \-g
+and
+.B \-T
+are mutually exclusive flags.
+.TP
+.BI \-g
+Generate the secret.
+If this,
+.B \-s
+and
+.B \-T
+are absent, the user will be prompted for the secret to hash.
+.BR \-s ,
+.B \-g
+and
+.B \-T
+are mutually exclusive flags.
+If this is present,
+.I {CLEARTEXT}
+is used as scheme.
+.B \-g
+and
+.B \-h
+are mutually exclusive flags.
+.TP
+.BI \-T \ "file"
+Hash the contents of the file.
+If this,
+.B \-g
+and
+.B \-s
+are absent, the user will be prompted for the secret to hash.
+.BR \-s ,
+.B \-g
+and
+.B \-T
+and mutually exclusive flags.
+.TP
+.BI \-h \ "scheme"
+If \fB\-h\fP is specified, one of the following RFC 2307 schemes may
+be specified:
+.BR {CRYPT} ,
+.BR {MD5} ,
+.BR {SMD5} ,
+.BR {SSHA} ", and"
+.BR {SHA} .
+The default is
+.BR {SSHA} .
+
+Note that scheme names may need to be protected, due to
+.B {
+and
+.BR } ,
+from expansion by the user's command interpreter.
+
+.B {SHA}
+and
+.B {SSHA}
+use the SHA-1 algorithm (FIPS 160-1), the latter with a seed.
+
+.B {MD5}
+and
+.B {SMD5}
+use the MD5 algorithm (RFC 1321), the latter with a seed.
+
+.B {CRYPT}
+uses the
+.BR crypt (3).
+
+.B {CLEARTEXT}
+indicates that the new password should be added to userPassword as
+clear text.
+Unless
+.I {CLEARTEXT}
+is used, this flag is incompatible with option
+.BR \-g .
+.TP
+.BI \-c \ crypt-salt-format
+Specify the format of the salt passed to
+.BR crypt (3)
+when generating {CRYPT} passwords.
+This string needs to be in
+.BR sprintf (3)
+format and may include one (and only one)
+.B %s
+conversion.
+This conversion will be substituted with a string of random
+characters from [A\-Za\-z0\-9./]. For example,
+.RB ' %.2s '
+provides a two character salt and
+.RB ' $1$%.8s '
+tells some
+versions of
+.BR crypt (3)
+to use an MD5 algorithm and provides
+8 random characters of salt.
+The default is
+.RB ' %s ' ,
+which provides 31 characters of salt.
+.TP
+.BI \-n
+Omit the trailing newline; useful to pipe the credentials
+into a command.
+.TP
+.BI \-o \ option\fR[ = value\fR]
+Specify an
+.I option
+with a(n optional)
+.IR value .
+Possible generic options/values are:
+.LP
+.nf
+ module\-path=<pathspec> (see `\fBmodulepath\fP' in slapd.conf(5))
+ module\-load="<filename> [<arguments>...]" (see `\fBmoduleload\fP' in slapd.conf(5))
+
+.in
+You can load a dynamically loadable password hash module by
+using this option.
+.SH LIMITATIONS
+The practice of storing hashed passwords in userPassword violates
+Standard Track (RFC 4519) schema specifications and may hinder
+interoperability. A new attribute type, authPassword, to hold
+hashed passwords has been defined (RFC 3112), but is not yet
+implemented in
+.BR slapd (8).
+.LP
+It should also be noted that the behavior of
+.BR crypt (3)
+is platform specific.
+.SH "SECURITY CONSIDERATIONS"
+Use of hashed passwords does not protect passwords during
+protocol transfer. TLS or other eavesdropping protections
+should be in-place before using LDAP simple bind.
+.LP
+The hashed password values should be protected as if they
+were clear text passwords.
+.SH "SEE ALSO"
+.BR ldappasswd (1),
+.BR ldapmodify (1),
+.BR slapd (8),
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.B RFC 2307\fP,
+.B RFC 4519\fP,
+.B RFC 3112
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man8/slapschema.8 b/doc/man/man8/slapschema.8
new file mode 100644
index 0000000..8b91f8a
--- /dev/null
+++ b/doc/man/man8/slapschema.8
@@ -0,0 +1,193 @@
+.TH SLAPSCHEMA 8C "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1998-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapschema \- SLAPD in-database schema checking utility
+.SH SYNOPSIS
+.B SBINDIR/slapschema
+[\c
+.BI \-a filter\fR]
+[\c
+.BI \-b suffix\fR]
+[\c
+.BR \-c ]
+[\c
+.BI \-d debug-level\fR]
+[\c
+.BI \-f slapd.conf\fR]
+[\c
+.BI \-F confdir\fR]
+[\c
+.BR \-g ]
+[\c
+.BI \-H URI\fR]
+[\c
+.BI \-l error-file\fR]
+[\c
+.BI \-n dbnum\fR]
+[\c
+.BI \-o option\fR[ = value\fR]]
+[\c
+.BI \-s subtree-dn\fR]
+[\c
+.BR \-v ]
+.LP
+.SH DESCRIPTION
+.LP
+.B Slapschema
+is used to check schema compliance of the contents of a
+.BR slapd (8)
+database.
+It opens the given database determined by the database number or
+suffix and checks the compliance of its contents with the corresponding
+schema. Errors are written to standard output or the specified file.
+Databases configured as
+.B subordinate
+of this one are also output, unless \fB\-g\fP is specified.
+.LP
+Administrators may need to modify existing schema items, including
+adding new required attributes to objectClasses,
+removing existing required or allowed attributes from objectClasses,
+entirely removing objectClasses,
+or any other change that may result in making perfectly valid entries
+no longer compliant with the modified schema.
+The execution of the
+.B slapschema
+tool after modifying the schema can point out
+inconsistencies that would otherwise surface only when
+inconsistent entries need to be modified.
+
+.LP
+The entry records are checked in database order, not superior first
+order. The entry records will be checked considering all
+(user and operational) attributes stored in the database.
+Dynamically generated attributes (such as subschemaSubentry)
+will not be considered.
+.SH OPTIONS
+.TP
+.BI \-a \ filter
+Only check entries matching the asserted filter.
+For example
+
+slapschema \-a \\
+ "(!(entryDN:dnSubtreeMatch:=ou=People,dc=example,dc=com))"
+
+will check all but the "ou=People,dc=example,dc=com" subtree
+of the "dc=example,dc=com" database.
+Deprecated; use \fB-H\fP \fIldap:///???(filter)\fP instead.
+.TP
+.BI \-b \ suffix
+Use the specified \fIsuffix\fR to determine which database to
+check. By default, the first database that supports the requested operation is
+used. The \fB\-b\fP cannot be used in conjunction with the
+.B \-n
+option.
+.TP
+.B \-c
+Enable continue (ignore errors) mode.
+.TP
+.BI \-d \ debug-level
+Enable debugging messages as defined by the specified
+.IR debug-level ;
+see
+.BR slapd (8)
+for details.
+.TP
+.BI \-f \ slapd.conf
+Specify an alternative
+.BR slapd.conf (5)
+file.
+.TP
+.BI \-F \ confdir
+specify a config directory.
+If both
+.B \-f
+and
+.B \-F
+are specified, the config file will be read and converted to
+config directory format and written to the specified directory.
+If neither option is specified, an attempt to read the
+default config directory will be made before trying to use the default
+config file. If a valid config directory exists then the
+default config file is ignored.
+.TP
+.B \-g
+disable subordinate gluing. Only the specified database will be
+processed, and not its glued subordinates (if any).
+.TP
+.B \-H \ URI
+use dn, scope and filter from URI to only handle matching entries.
+.TP
+.BI \-l \ error-file
+Write errors to specified file instead of standard output.
+.TP
+.BI \-n \ dbnum
+Check the \fIdbnum\fR\-th database listed in the
+configuration file. The config database
+.BR slapd\-config (5),
+is always the first database, so use
+.B \-n 0
+
+The
+.B \-n
+cannot be used in conjunction with the
+.B \-b
+option.
+.TP
+.BI \-o \ option\fR[ = value\fR]
+Specify an
+.I option
+with a(n optional)
+.IR value .
+Possible generic options/values are:
+.LP
+.nf
+ syslog=<subsystems> (see `\-s' in slapd(8))
+ syslog\-level=<level> (see `\-S' in slapd(8))
+ syslog\-user=<user> (see `\-l' in slapd(8))
+
+.fi
+.TP
+.BI \-s \ subtree-dn
+Only check entries in the subtree specified by this DN.
+Implies \fB\-b\fP \fIsubtree-dn\fP if no
+.B \-b
+nor
+.B \-n
+option is given.
+Deprecated; use \fB-H\fP \fIldap:///subtree-dn\fP instead.
+.TP
+.B \-v
+Enable verbose mode.
+.SH LIMITATIONS
+For some backend types, your
+.BR slapd (8)
+should not be running (at least, not in read-write
+mode) when you do this to ensure consistency of the database. It is
+always safe to run
+.B slapschema
+with the
+.BR slapd\-mdb (5),
+and
+.BR slapd\-null (5)
+backends.
+.SH EXAMPLES
+To check the schema compliance of your SLAPD database after modifications
+to the schema, and put any error in a file called
+.BR errors.ldif ,
+give the command:
+.LP
+.nf
+.ft tt
+ SBINDIR/slapschema \-l errors.ldif
+.ft
+.fi
+.SH "SEE ALSO"
+.BR ldap (3),
+.BR ldif (5),
+.BR slapd (8)
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/doc/man/man8/slaptest.8 b/doc/man/man8/slaptest.8
new file mode 100644
index 0000000..9effa9f
--- /dev/null
+++ b/doc/man/man8/slaptest.8
@@ -0,0 +1,117 @@
+.TH SLAPTEST 8C "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2004-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slaptest \- Check the suitability of the OpenLDAP slapd configuration
+.SH SYNOPSIS
+.B SBINDIR/slaptest
+[\c
+.BI \-d \ debug-level\fR]
+[\c
+.BI \-f \ slapd.conf\fR]
+[\c
+.BI \-F \ confdir\fR]
+[\c
+.BI \-n dbnum\fR]
+[\c
+.BI \-o \ option\fR[ = value\fR]]
+[\c
+.BR \-Q ]
+[\c
+.BR \-u ]
+[\c
+.BR \-v ]
+.LP
+.SH DESCRIPTION
+.LP
+.B Slaptest
+is used to check the conformance of the
+.BR slapd (8)
+configuration.
+It opens the
+.BR slapd.conf (5)
+configuration file or the
+.BR slapd\-config (5)
+backend, and parses it according to the general and the backend-specific
+rules, checking its sanity.
+.LP
+.SH OPTIONS
+.TP
+.BI \-d \ debug-level
+enable debugging messages as defined by the specified
+.IR debug-level ;
+see
+.BR slapd (8)
+for details.
+.TP
+.BI \-f \ slapd.conf
+specify an alternative
+.BR slapd.conf (5)
+file.
+.TP
+.BI \-F \ confdir
+specify a config directory.
+If both
+.B \-f
+and
+.B \-F
+are specified, the config file will be read and converted to
+config directory format and written to the specified directory.
+If neither option is specified, slaptest will attempt to read the
+default config directory before trying to use the default
+config file. If a valid config directory exists then the
+default config file is ignored. If dry-run mode is also specified,
+no conversion will occur.
+.TP
+.BI \-n \ dbnum
+Just open and test the \fIdbnum\fR-th database listed in the
+configuration file.
+To only test the config database
+.BR slapd\-config (5),
+use
+.B \-n 0
+as it is always the first database.
+.TP
+.BI \-o \ option\fR[ = value\fR]
+Specify an
+.I option
+with a(n optional)
+.IR value .
+Possible generic options/values are:
+.LP
+.nf
+ syslog=<subsystems> (see `\-s' in slapd(8))
+ syslog\-level=<level> (see `\-S' in slapd(8))
+ syslog\-user=<user> (see `\-l' in slapd(8))
+
+.fi
+.TP
+.BI \-Q
+Be extremely quiet: only the exit code indicates success (0) or not
+(any other value).
+.TP
+.B \-u
+enable dry-run mode (i.e. don't fail if databases cannot be opened,
+but config is fine).
+.TP
+.B \-v
+enable verbose mode.
+.SH EXAMPLES
+To check a
+.BR slapd.conf (5)
+give the command:
+.LP
+.nf
+.ft tt
+ SBINDIR/slaptest \-f /ETCDIR/slapd.conf \-v
+.ft
+.fi
+.SH "SEE ALSO"
+.BR ldap (3),
+.BR slapd (8),
+.BR slapdn (8)
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.SH ACKNOWLEDGEMENTS
+.so ../Project
diff --git a/include/Makefile.in b/include/Makefile.in
new file mode 100644
index 0000000..debe1b0
--- /dev/null
+++ b/include/Makefile.in
@@ -0,0 +1,85 @@
+# include Makefile.in for OpenLDAP
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+all-local: ldap_config.h FORCE
+
+install-local: FORCE
+ -$(MKDIR) $(DESTDIR)$(includedir)
+ for header in $(srcdir)/lber.h lber_types.h \
+ $(srcdir)/ldap.h $(srcdir)/ldap_cdefs.h \
+ $(srcdir)/ldap_schema.h $(srcdir)/ldap_utf8.h \
+ $(srcdir)/slapi-plugin.h ldap_features.h \
+ $(srcdir)/ldif.h $(srcdir)/openldap.h ; \
+ do \
+ $(INSTALL) $(INSTALLFLAGS) -m 644 $$header $(DESTDIR)$(includedir); \
+ done
+
+clean-local: FORCE
+ $(RM) ldap_config.h
+
+veryclean-local: clean-local FORCE
+ $(RM) portable.h lber_types.h ldap_features.h
+
+depend-local: ldap_config.h FORCE
+
+LDAP_CONFIG=$(srcdir)/ldap_config.hin
+
+ldap_config.h: $(LDAP_CONFIG) Makefile
+ @$(RM) $@
+ @echo "Making $@"
+ @echo "/* Generated from $(LDAP_CONFIG) on `date` */" > $@; \
+ if test $(PLAT) = NT; then \
+ sysconfdir=`cygpath -w $(sysconfdir) | \
+ $(SED) -e 's/\\\\/\\\\\\\\\\\\\\\\/g'`; \
+ datadir=`cygpath -w $(datadir) | \
+ $(SED) -e 's/\\\\/\\\\\\\\\\\\\\\\/g'`; \
+ bindir=`cygpath -w $(bindir) | \
+ $(SED) -e 's/\\\\/\\\\\\\\\\\\\\\\/g'`; \
+ sbindir=`cygpath -w $(sbindir) | \
+ $(SED) -e 's/\\\\/\\\\\\\\\\\\\\\\/g'`; \
+ libexecdir=`cygpath -w $(libexecdir) | \
+ $(SED) -e 's/\\\\/\\\\\\\\\\\\\\\\/g'`; \
+ moduledir=`cygpath -w $(moduledir) | \
+ $(SED) -e 's/\\\\/\\\\\\\\\\\\\\\\/g'`; \
+ localstatedir=`cygpath -w $(localstatedir) | \
+ $(SED) -e 's/\\\\/\\\\\\\\\\\\\\\\/g'`; \
+ else \
+ sysconfdir=$(sysconfdir); \
+ datadir=$(datadir); \
+ bindir=$(bindir); \
+ sbindir=$(sbindir); \
+ libexecdir=$(libexecdir); \
+ moduledir=$(moduledir); \
+ localstatedir=$(localstatedir); \
+ localedir=$(localedir); \
+ fi; \
+ $(SED) \
+ -e "s;%SYSCONFDIR%;$$sysconfdir;" \
+ -e "s;%DATADIR%;$$datadir;" \
+ -e "s;%BINDIR%;$$bindir;" \
+ -e "s;%SBINDIR%;$$sbindir;" \
+ -e "s;%LIBEXECDIR%;$$libexecdir;" \
+ -e "s;%MODULEDIR%;$$moduledir;" \
+ -e "s;%RUNDIR%;$$localstatedir;" \
+ -e "s;%LOCALEDIR%;$$localedir;" \
+ $(LDAP_CONFIG) >> $@; \
+ $(CHMOD) 444 $@
+
+all-common: all-local
+install-common: all-common install-local
+clean-common: clean-local
+veryclean-common: veryclean-local
+depend-common: depend-local
+
diff --git a/include/ac/alloca.h b/include/ac/alloca.h
new file mode 100644
index 0000000..0192dca
--- /dev/null
+++ b/include/ac/alloca.h
@@ -0,0 +1,43 @@
+/* Generic alloca.h */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _AC_ALLOCA_H
+#define _AC_ALLOCA_H
+
+/*
+ * use of alloca is disallowed as it is machine dependent
+ */
+#error "alloca() not supported, use malloc()"
+
+/* AIX requires this to be the first thing in the file. */
+#ifdef __GNUC__
+# define alloca __builtin_alloca
+#else
+# ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+# else
+# ifdef _AIX
+#pragma alloca
+# else
+# ifndef alloca /* predefined by HP cc +Olibcalls */
+extern char *(alloca)();
+# endif
+# endif
+# endif
+#endif
+
+
+#endif /* _AC_ALLOCA_H */
diff --git a/include/ac/assert.h b/include/ac/assert.h
new file mode 100644
index 0000000..dbb2295
--- /dev/null
+++ b/include/ac/assert.h
@@ -0,0 +1,57 @@
+/* Generic assert.h */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _AC_ASSERT_H
+#define _AC_ASSERT_H
+
+#undef assert
+
+#ifdef LDAP_DEBUG
+
+#if defined( HAVE_ASSERT_H ) || defined( STDC_HEADERS )
+
+#undef NDEBUG
+#include <assert.h>
+
+#else /* !(HAVE_ASSERT_H || STDC_HEADERS) */
+
+#define LDAP_NEED_ASSERT 1
+
+/*
+ * no assert()... must be a very old compiler.
+ * create a replacement and hope it works
+ */
+
+LBER_F (void) ber_pvt_assert LDAP_P(( const char *file, int line,
+ const char *test ));
+
+/* Can't use LDAP_STRING(test), that'd expand to "test" */
+#if defined(__STDC__) || defined(__cplusplus)
+#define assert(test) \
+ ((test) ? (void)0 : ber_pvt_assert( __FILE__, __LINE__, #test ) )
+#else
+#define assert(test) \
+ ((test) ? (void)0 : ber_pvt_assert( __FILE__, __LINE__, "test" ) )
+#endif
+
+#endif /* (HAVE_ASSERT_H || STDC_HEADERS) */
+
+#else /* !LDAP_DEBUG */
+/* no asserts */
+#define assert(test) ((void)0)
+#endif /* LDAP_DEBUG */
+
+#endif /* _AC_ASSERT_H */
diff --git a/include/ac/bytes.h b/include/ac/bytes.h
new file mode 100644
index 0000000..f8a6314
--- /dev/null
+++ b/include/ac/bytes.h
@@ -0,0 +1,78 @@
+/* Generic bytes.h */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _AC_BYTES_H
+#define _AC_BYTES_H
+
+/* cross compilers should define both AC_INT{2,4}_TYPE in CPPFLAGS */
+
+#if !defined( AC_INT4_TYPE )
+ /* use autoconf defines to provide sized typedefs */
+# if SIZEOF_LONG == 4
+# define AC_INT4_TYPE long
+# elif SIZEOF_INT == 4
+# define AC_INT4_TYPE int
+# elif SIZEOF_SHORT == 4
+# define AC_INT4_TYPE short
+# else
+# error "AC_INT4_TYPE?"
+# endif
+#endif
+
+typedef AC_INT4_TYPE ac_int4;
+typedef signed AC_INT4_TYPE ac_sint4;
+typedef unsigned AC_INT4_TYPE ac_uint4;
+
+#if !defined( AC_INT2_TYPE )
+# if SIZEOF_SHORT == 2
+# define AC_INT2_TYPE short
+# elif SIZEOF_INT == 2
+# define AC_INT2_TYPE int
+# elif SIZEOF_LONG == 2
+# define AC_INT2_TYPE long
+# else
+# error "AC_INT2_TYPE?"
+# endif
+#endif
+
+#if defined( AC_INT2_TYPE )
+typedef AC_INT2_TYPE ac_int2;
+typedef signed AC_INT2_TYPE ac_sint2;
+typedef unsigned AC_INT2_TYPE ac_uint2;
+#endif
+
+#ifndef BYTE_ORDER
+/* cross compilers should define BYTE_ORDER in CPPFLAGS */
+
+/*
+ * Definitions for byte order, according to byte significance from low
+ * address to high.
+ */
+#define LITTLE_ENDIAN 1234 /* LSB first: i386, vax */
+#define BIG_ENDIAN 4321 /* MSB first: 68000, ibm, net */
+#define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long */
+
+/* assume autoconf's AC_C_BIGENDIAN has been ran */
+/* if it hasn't, we assume (maybe falsely) the order is LITTLE ENDIAN */
+# ifdef WORDS_BIGENDIAN
+# define BYTE_ORDER BIG_ENDIAN
+# else
+# define BYTE_ORDER LITTLE_ENDIAN
+# endif
+
+#endif /* BYTE_ORDER */
+
+#endif /* _AC_BYTES_H */
diff --git a/include/ac/crypt.h b/include/ac/crypt.h
new file mode 100644
index 0000000..da52c1c
--- /dev/null
+++ b/include/ac/crypt.h
@@ -0,0 +1,29 @@
+/* Generic crypt.h */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _AC_CRYPT_H
+#define _AC_CRYPT_H
+
+#include <ac/unistd.h>
+
+/* crypt() may be defined in a separate include file */
+#ifdef HAVE_CRYPT_H
+# include <crypt.h>
+#else
+ extern char *(crypt)();
+#endif
+
+#endif /* _AC_CRYPT_H */
diff --git a/include/ac/ctype.h b/include/ac/ctype.h
new file mode 100644
index 0000000..e385f3a
--- /dev/null
+++ b/include/ac/ctype.h
@@ -0,0 +1,33 @@
+/* Generic ctype.h */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _AC_CTYPE_H
+#define _AC_CTYPE_H
+
+#include <ctype.h>
+
+#undef TOUPPER
+#undef TOLOWER
+
+#ifdef C_UPPER_LOWER
+# define TOUPPER(c) (islower(c) ? toupper(c) : (c))
+# define TOLOWER(c) (isupper(c) ? tolower(c) : (c))
+#else
+# define TOUPPER(c) toupper(c)
+# define TOLOWER(c) tolower(c)
+#endif
+
+#endif /* _AC_CTYPE_H */
diff --git a/include/ac/dirent.h b/include/ac/dirent.h
new file mode 100644
index 0000000..93df7b6
--- /dev/null
+++ b/include/ac/dirent.h
@@ -0,0 +1,54 @@
+/* Generic dirent.h */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _AC_DIRENT_H
+#define _AC_DIRENT_H
+
+#ifdef HAVE_DIRENT_H
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#elif defined(_MSC_VER)
+#include <windows.h>
+#ifndef MAX_PATH
+#define MAX_PATH 260
+#endif
+struct dirent {
+ char *d_name;
+};
+typedef struct DIR {
+ HANDLE dir;
+ struct dirent data;
+ int first;
+ char buf[MAX_PATH+1];
+} DIR;
+DIR *opendir(const char *name);
+struct dirent *readdir(DIR *dir);
+int closedir(DIR *dir);
+#else
+# define dirent direct
+# define NAMLEN(dirent) (dirent)->d_namlen
+# ifdef HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# ifdef HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# ifdef HAVE_NDIR_H
+# include <ndir.h>
+# endif
+#endif
+
+#endif /* _AC_DIRENT_H */
diff --git a/include/ac/errno.h b/include/ac/errno.h
new file mode 100644
index 0000000..8a7f32c
--- /dev/null
+++ b/include/ac/errno.h
@@ -0,0 +1,32 @@
+/* Generic errno.h */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _AC_ERRNO_H
+#define _AC_ERRNO_H
+
+#if defined( HAVE_ERRNO_H )
+# include <errno.h>
+#elif defined( HAVE_SYS_ERRNO_H )
+# include <sys/errno.h>
+#endif
+
+#if defined( HAVE_SYS_ERRLIST ) && defined( DECL_SYS_ERRLIST )
+ /* have sys_errlist but need declaration */
+ LDAP_LIBC_V(int) sys_nerr;
+ LDAP_LIBC_V(char) *sys_errlist[];
+#endif
+
+#endif /* _AC_ERRNO_H */
diff --git a/include/ac/fdset.h b/include/ac/fdset.h
new file mode 100644
index 0000000..620850b
--- /dev/null
+++ b/include/ac/fdset.h
@@ -0,0 +1,42 @@
+/* redefine FD_SET */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * This header is to be included by portable.h to ensure
+ * tweaking of FD_SETSIZE is done early enough to be effective.
+ */
+
+#ifndef _AC_FDSET_H
+#define _AC_FDSET_H
+
+#if !defined( OPENLDAP_FD_SETSIZE ) && !defined( FD_SETSIZE )
+# define OPENLDAP_FD_SETSIZE 4096
+#endif
+
+#ifdef OPENLDAP_FD_SETSIZE
+ /* assume installer desires to enlarge fd_set */
+# ifdef HAVE_BITS_TYPES_H
+# include <bits/types.h>
+# endif
+# ifdef __FD_SETSIZE
+# undef __FD_SETSIZE
+# define __FD_SETSIZE OPENLDAP_FD_SETSIZE
+# else
+# define FD_SETSIZE OPENLDAP_FD_SETSIZE
+# endif
+#endif
+
+#endif /* _AC_FDSET_H */
diff --git a/include/ac/localize.h b/include/ac/localize.h
new file mode 100644
index 0000000..9e19fe6
--- /dev/null
+++ b/include/ac/localize.h
@@ -0,0 +1,44 @@
+/* localize.h (i18n/l10n) */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _AC_LOCALIZE_H
+#define _AC_LOCALIZE_H
+
+#ifdef LDAP_LOCALIZE
+
+# include <locale.h>
+# include <libintl.h>
+
+ /* enable i18n/l10n */
+# define gettext_noop(s) s
+# define _(s) gettext(s)
+# define N_(s) gettext_noop(s)
+# define ldap_pvt_setlocale(c,l) ((void) setlocale(c, l))
+# define ldap_pvt_textdomain(d) ((void) textdomain(d))
+# define ldap_pvt_bindtextdomain(p,d) ((void) bindtextdomain(p, d))
+
+#else
+
+ /* disable i18n/l10n */
+# define _(s) s
+# define N_(s) s
+# define ldap_pvt_setlocale(c,l) ((void) 0)
+# define ldap_pvt_textdomain(d) ((void) 0)
+# define ldap_pvt_bindtextdomain(p,d) ((void) 0)
+
+#endif
+
+#endif /* _AC_LOCALIZE_H */
diff --git a/include/ac/param.h b/include/ac/param.h
new file mode 100644
index 0000000..a3f5d67
--- /dev/null
+++ b/include/ac/param.h
@@ -0,0 +1,39 @@
+/* Generic param.h */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _AC_PARAM_H
+#define _AC_PARAM_H
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+/* MAXPATHLEN should come from <unistd.h> */
+#include <ac/unistd.h>
+
+#ifndef MAXPATHLEN
+# if defined(PATH_MAX)
+# define MAXPATHLEN PATH_MAX
+
+# elif defined(_MAX_PATH)
+# define MAXPATHLEN _MAX_PATH
+
+# else
+# define MAXPATHLEN 4096
+# endif
+#endif
+
+#endif /* _AC_PARAM_H */
diff --git a/include/ac/regex.h b/include/ac/regex.h
new file mode 100644
index 0000000..ed1ddd4
--- /dev/null
+++ b/include/ac/regex.h
@@ -0,0 +1,39 @@
+/* Generic Regex */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _AC_REGEX_H_
+#define _AC_REGEX_H_
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifndef HAVE_REGEX_H
+/* NO POSIX REGEX!!
+ * You'll need to install a POSIX compatible REGEX library.
+ * Either Henry Spencer's or GNU regex will do.
+ */
+#error "No POSIX REGEX available."
+
+#elif HAVE_GNUREGEX_H
+ /* system has GNU gnuregex.h */
+# include <gnuregex.h>
+#else
+ /* have regex.h, assume it's POSIX compliant */
+# include <regex.h>
+#endif /* regex.h */
+
+#endif /* _AC_REGEX_H_ */
diff --git a/include/ac/signal.h b/include/ac/signal.h
new file mode 100644
index 0000000..1c7293b
--- /dev/null
+++ b/include/ac/signal.h
@@ -0,0 +1,80 @@
+/* Generic signal.h */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _AC_SIGNAL_H
+#define _AC_SIGNAL_H
+
+#include <signal.h>
+
+#undef SIGNAL
+
+#if defined( HAVE_SIGACTION )
+#define SIGNAL lutil_sigaction
+typedef void (*lutil_sig_t)(int);
+LDAP_LUTIL_F(lutil_sig_t) lutil_sigaction( int sig, lutil_sig_t func );
+#define SIGNAL_REINSTALL(sig,act) (void)0
+#elif defined( HAVE_SIGSET )
+#define SIGNAL sigset
+#define SIGNAL_REINSTALL sigset
+#else
+#define SIGNAL signal
+#define SIGNAL_REINSTALL signal
+#endif
+
+#if !defined( LDAP_SIGUSR1 ) || !defined( LDAP_SIGUSR2 )
+#undef LDAP_SIGUSR1
+#undef LDAP_SIGUSR2
+
+# if defined(WINNT) || defined(_WINNT) || defined(_WIN32)
+# define LDAP_SIGUSR1 SIGILL
+# define LDAP_SIGUSR2 SIGTERM
+
+# elif !defined(HAVE_LINUX_THREADS)
+# define LDAP_SIGUSR1 SIGUSR1
+# define LDAP_SIGUSR2 SIGUSR2
+
+# else
+ /*
+ * Some versions of LinuxThreads unfortunately uses the only
+ * two signals reserved for user applications. This forces
+ * OpenLDAP to use other signals reserved for other uses.
+ */
+
+# if defined( SIGSTKFLT )
+# define LDAP_SIGUSR1 SIGSTKFLT
+# elif defined ( SIGSYS )
+# define LDAP_SIGUSR1 SIGSYS
+# endif
+
+# if defined( SIGUNUSED )
+# define LDAP_SIGUSR2 SIGUNUSED
+# elif defined ( SIGINFO )
+# define LDAP_SIGUSR2 SIGINFO
+# elif defined ( SIGEMT )
+# define LDAP_SIGUSR2 SIGEMT
+# endif
+# endif
+#endif
+
+#ifndef LDAP_SIGCHLD
+#ifdef SIGCHLD
+#define LDAP_SIGCHLD SIGCHLD
+#elif SIGCLD
+#define LDAP_SIGCHLD SIGCLD
+#endif
+#endif
+
+#endif /* _AC_SIGNAL_H */
diff --git a/include/ac/socket.h b/include/ac/socket.h
new file mode 100644
index 0000000..3899013
--- /dev/null
+++ b/include/ac/socket.h
@@ -0,0 +1,266 @@
+/* Generic socket.h */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _AC_SOCKET_H_
+#define _AC_SOCKET_H_
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#elif defined(HAVE_SYS_POLL_H)
+#include <sys/poll.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#include <netinet/in.h>
+
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+
+#include <netdb.h>
+
+#ifdef HAVE_RESOLV_H
+#include <resolv.h>
+#endif
+
+#endif /* HAVE_SYS_SOCKET_H */
+
+#ifdef HAVE_WINSOCK2
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#elif HAVE_WINSOCK
+#include <winsock.h>
+#endif
+
+#ifdef HAVE_PCNFS
+#include <tklib.h>
+#endif /* HAVE_PCNFS */
+
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK (0x7f000001UL)
+#endif
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+#undef sock_errno
+#undef sock_errstr
+#define sock_errno() errno
+#define sock_errstr(e, b, l) AC_STRERROR_R(e, b, l)
+#define sock_errset(e) ((void) (errno = (e)))
+
+#ifdef HAVE_WINSOCK
+# define tcp_read( s, buf, len ) recv( s, buf, len, 0 )
+# define tcp_write( s, buf, len ) send( s, buf, len, 0 )
+# define ioctl( s, c, a ) ioctlsocket( (s), (c), (a) )
+# define ioctl_t u_long
+# define AC_SOCKET_INVALID ((unsigned int) -1)
+
+# ifdef SD_BOTH
+# define tcp_close( s ) (shutdown( s, SD_BOTH ), closesocket( s ))
+# else
+# define tcp_close( s ) closesocket( s )
+# endif
+
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#define EINPROGRESS WSAEINPROGRESS
+#define ETIMEDOUT WSAETIMEDOUT
+
+#undef sock_errno
+#undef sock_errstr
+#undef sock_errset
+#define sock_errno() WSAGetLastError()
+#define sock_errstr(e, b, l) ber_pvt_wsa_err2string(e)
+#define sock_errset(e) WSASetLastError(e)
+
+LBER_F( char * ) ber_pvt_wsa_err2string LDAP_P((int));
+
+#elif MACOS
+# define tcp_close( s ) tcpclose( s )
+# define tcp_read( s, buf, len ) tcpread( s, buf, len )
+# define tcp_write( s, buf, len ) tcpwrite( s, buf, len )
+
+#elif DOS
+# ifdef PCNFS
+# define tcp_close( s ) close( s )
+# define tcp_read( s, buf, len ) recv( s, buf, len, 0 )
+# define tcp_write( s, buf, len ) send( s, buf, len, 0 )
+# endif /* PCNFS */
+# ifdef NCSA
+# define tcp_close( s ) do { netclose( s ); netshut() } while(0)
+# define tcp_read( s, buf, len ) nread( s, buf, len )
+# define tcp_write( s, buf, len ) netwrite( s, buf, len )
+# endif /* NCSA */
+
+#elif defined(HAVE_CLOSESOCKET)
+# define tcp_close( s ) closesocket( s )
+
+# ifdef __BEOS__
+# define tcp_read( s, buf, len ) recv( s, buf, len, 0 )
+# define tcp_write( s, buf, len ) send( s, buf, len, 0 )
+# endif
+
+#else
+# define tcp_read( s, buf, len) read( s, buf, len )
+# define tcp_write( s, buf, len) write( s, buf, len )
+
+# ifdef SHUT_RDWR
+# define tcp_close( s ) (shutdown( s, SHUT_RDWR ), close( s ))
+# else
+# define tcp_close( s ) close( s )
+# endif
+
+#ifdef HAVE_PIPE
+/*
+ * Only use pipe() on systems where file and socket descriptors
+ * are interchangeable
+ */
+# define USE_PIPE HAVE_PIPE
+#endif
+
+#endif /* MACOS */
+
+#ifndef ioctl_t
+# define ioctl_t int
+#endif
+
+#ifndef AC_SOCKET_INVALID
+# define AC_SOCKET_INVALID (-1)
+#endif
+#ifndef AC_SOCKET_ERROR
+# define AC_SOCKET_ERROR (-1)
+#endif
+
+#if !defined( HAVE_INET_ATON ) && !defined( inet_aton )
+# define inet_aton ldap_pvt_inet_aton
+struct in_addr;
+LDAP_F (int) ldap_pvt_inet_aton LDAP_P(( const char *, struct in_addr * ));
+#endif
+
+#if defined(__WIN32) && defined(_ALPHA)
+/* NT on Alpha is hosed. */
+# define AC_HTONL( l ) \
+ ((((l)&0xffU)<<24) + (((l)&0xff00U)<<8) + \
+ (((l)&0xff0000U)>>8) + (((l)&0xff000000U)>>24))
+# define AC_NTOHL(l) AC_HTONL(l)
+
+#else
+# define AC_HTONL( l ) htonl( l )
+# define AC_NTOHL( l ) ntohl( l )
+#endif
+
+/* htons()/ntohs() may be broken much like htonl()/ntohl() */
+#define AC_HTONS( s ) htons( s )
+#define AC_NTOHS( s ) ntohs( s )
+
+#ifdef LDAP_PF_LOCAL
+# if !defined( AF_LOCAL ) && defined( AF_UNIX )
+# define AF_LOCAL AF_UNIX
+# endif
+# if !defined( PF_LOCAL ) && defined( PF_UNIX )
+# define PF_LOCAL PF_UNIX
+# endif
+#endif
+
+#ifndef INET_ADDRSTRLEN
+# define INET_ADDRSTRLEN 16
+#endif
+#ifndef INET6_ADDRSTRLEN
+# define INET6_ADDRSTRLEN 46
+#endif
+
+#if defined( HAVE_GETADDRINFO ) || defined( HAVE_GETNAMEINFO )
+# ifdef HAVE_GAI_STRERROR
+# define AC_GAI_STRERROR(x) (gai_strerror((x)))
+# else
+# define AC_GAI_STRERROR(x) (ldap_pvt_gai_strerror((x)))
+ LDAP_F (char *) ldap_pvt_gai_strerror( int );
+# endif
+#endif
+
+#if defined(LDAP_PF_LOCAL) && \
+ !defined(HAVE_GETPEEREID) && \
+ !defined(HAVE_GETPEERUCRED) && \
+ !defined(SO_PEERCRED) && !defined(LOCAL_PEERCRED) && \
+ defined(HAVE_SENDMSG) && (defined(HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTSLEN) || \
+ defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL))
+# define LDAP_PF_LOCAL_SENDMSG 1
+#endif
+
+#ifdef HAVE_GETPEEREID
+#define LUTIL_GETPEEREID( s, uid, gid, bv ) getpeereid( s, uid, gid )
+#elif defined(LDAP_PF_LOCAL_SENDMSG)
+struct berval;
+LDAP_LUTIL_F( int ) lutil_getpeereid( int s, uid_t *, gid_t *, struct berval *bv );
+#define LUTIL_GETPEEREID( s, uid, gid, bv ) lutil_getpeereid( s, uid, gid, bv )
+#else
+LDAP_LUTIL_F( int ) lutil_getpeereid( int s, uid_t *, gid_t * );
+#define LUTIL_GETPEEREID( s, uid, gid, bv ) lutil_getpeereid( s, uid, gid )
+#endif
+
+typedef union Sockaddr {
+ struct sockaddr sa_addr;
+ struct sockaddr_in sa_in_addr;
+#ifdef LDAP_PF_INET6
+ struct sockaddr_storage sa_storage;
+ struct sockaddr_in6 sa_in6_addr;
+#endif
+#ifdef LDAP_PF_LOCAL
+ struct sockaddr_un sa_un_addr;
+#endif
+} Sockaddr;
+
+/* DNS RFC defines max host name as 255. New systems seem to use 1024 */
+#ifndef NI_MAXHOST
+#define NI_MAXHOST 256
+#endif
+
+#ifdef HAVE_POLL
+# ifndef INFTIM
+# define INFTIM (-1)
+# endif
+#undef POLL_OTHER
+#define POLL_OTHER (POLLERR|POLLHUP)
+#undef POLL_READ
+#define POLL_READ (POLLIN|POLLPRI|POLL_OTHER)
+#undef POLL_WRITE
+#define POLL_WRITE (POLLOUT|POLL_OTHER)
+#endif
+
+#endif /* _AC_SOCKET_H_ */
diff --git a/include/ac/stdarg.h b/include/ac/stdarg.h
new file mode 100644
index 0000000..7ba2973
--- /dev/null
+++ b/include/ac/stdarg.h
@@ -0,0 +1,28 @@
+/* Generic stdarg.h */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _AC_STDARG_H
+#define _AC_STDARG_H 1
+
+/* require STDC variable argument support */
+
+#include <stdarg.h>
+
+#ifndef HAVE_STDARG
+# define HAVE_STDARG 1
+#endif
+
+#endif /* _AC_STDARG_H */
diff --git a/include/ac/stdlib.h b/include/ac/stdlib.h
new file mode 100644
index 0000000..63e3e70
--- /dev/null
+++ b/include/ac/stdlib.h
@@ -0,0 +1,48 @@
+/* Generic stdlib.h */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _AC_STDLIB_H
+#define _AC_STDLIB_H
+
+#if defined( HAVE_CSRIMALLOC )
+#include <stdio.h>
+#define MALLOC_TRACE
+#include <libmalloc.h>
+#endif
+
+#include <stdlib.h>
+
+/* Ignore malloc.h if we have STDC_HEADERS */
+#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
+# include <malloc.h>
+#endif
+
+#ifndef EXIT_SUCCESS
+# define EXIT_SUCCESS 0
+# define EXIT_FAILURE 1
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#if defined(LINE_MAX)
+# define AC_LINE_MAX LINE_MAX
+#else
+# define AC_LINE_MAX 2048 /* POSIX MIN */
+#endif
+
+#endif /* _AC_STDLIB_H */
diff --git a/include/ac/string.h b/include/ac/string.h
new file mode 100644
index 0000000..c4c1354
--- /dev/null
+++ b/include/ac/string.h
@@ -0,0 +1,118 @@
+/* Generic string.h */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _AC_STRING_H
+#define _AC_STRING_H
+
+#ifdef STDC_HEADERS
+# include <string.h>
+
+#else
+# ifdef HAVE_STRING_H
+# include <string.h>
+# endif
+# if defined(HAVE_STRINGS_H) && (!defined(HAVE_STRING_H) || defined(BOTH_STRINGS_H))
+# include <strings.h>
+# endif
+
+# ifdef HAVE_MEMORY_H
+# include <memory.h>
+# endif
+
+# ifndef HAVE_STRRCHR
+# undef strchr
+# define strchr index
+# undef strrchr
+# define strrchr rindex
+# endif
+
+# ifndef HAVE_MEMCPY
+# undef memcpy
+# define memcpy(d, s, n) ((void) bcopy ((s), (d), (n)))
+# undef memmove
+# define memmove(d, s, n) ((void) bcopy ((s), (d), (n)))
+# endif
+#endif
+
+/* use ldap_pvt_strtok instead of strtok or strtok_r! */
+LDAP_F(char *) ldap_pvt_strtok LDAP_P(( char *str,
+ const char *delim, char **pos ));
+
+#ifndef HAVE_STRDUP
+ /* strdup() is missing, declare our own version */
+# undef strdup
+# define strdup(s) ber_strdup(s)
+#elif !defined(_WIN32)
+ /* some systems fail to declare strdup */
+ /* Windows does not require this declaration */
+ LDAP_LIBC_F(char *) (strdup)();
+#endif
+
+/*
+ * some systems fail to declare strcasecmp() and strncasecmp()
+ * we need them declared so we can obtain pointers to them
+ */
+
+/* we don't want these declared for Windows or Mingw */
+#ifndef _WIN32
+int (strcasecmp)();
+int (strncasecmp)();
+#endif
+
+#ifndef SAFEMEMCPY
+# if defined( HAVE_MEMMOVE )
+# define SAFEMEMCPY( d, s, n ) memmove((d), (s), (n))
+# elif defined( HAVE_BCOPY )
+# define SAFEMEMCPY( d, s, n ) bcopy((s), (d), (n))
+# else
+ /* nothing left but memcpy() */
+# define SAFEMEMCPY( d, s, n ) memcpy((d), (s), (n))
+# endif
+#endif
+
+#define AC_MEMCPY( d, s, n ) (SAFEMEMCPY((d),(s),(n)))
+#define AC_FMEMCPY( d, s, n ) do { \
+ if((n) == 1) *((char*)(d)) = *((char*)(s)); \
+ else AC_MEMCPY( (d), (s), (n) ); \
+ } while(0)
+
+#ifdef NEED_MEMCMP_REPLACEMENT
+ int (lutil_memcmp)(const void *b1, const void *b2, size_t len);
+#define memcmp lutil_memcmp
+#endif
+
+void *(lutil_memrchr)(const void *b, int c, size_t n);
+/* GNU extension (glibc >= 2.1.91), only declared when defined(_GNU_SOURCE) */
+#if defined(HAVE_MEMRCHR) && defined(_GNU_SOURCE)
+#define lutil_memrchr(b, c, n) memrchr(b, c, n)
+#endif /* ! HAVE_MEMRCHR */
+
+#define STRLENOF(s) (sizeof(s)-1)
+
+#if defined( HAVE_NONPOSIX_STRERROR_R )
+# define AC_STRERROR_R(e,b,l) (strerror_r((e), (b), (l)))
+#elif defined( HAVE_STRERROR_R )
+# define AC_STRERROR_R(e,b,l) (strerror_r((e), (b), (l)) == 0 ? (b) : "Unknown error")
+#elif defined( HAVE_SYS_ERRLIST )
+# define AC_STRERROR_R(e,b,l) ((e) > -1 && (e) < sys_nerr \
+ ? sys_errlist[(e)] : "Unknown error" )
+#elif defined( HAVE_STRERROR )
+# define AC_STRERROR_R(e,b,l) (strerror(e)) /* NOTE: may be NULL */
+#else
+# define AC_STRERROR_R(e,b,l) ("Unknown error")
+#endif
+
+#endif /* _AC_STRING_H */
diff --git a/include/ac/sysexits.h b/include/ac/sysexits.h
new file mode 100644
index 0000000..f723224
--- /dev/null
+++ b/include/ac/sysexits.h
@@ -0,0 +1,26 @@
+/* Generic sysexits */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _AC_SYSEXITS_H_
+#define _AC_SYSEXITS_H_
+
+#ifdef HAVE_SYSEXITS_H
+# include <sysexits.h>
+#else
+# include <sysexits-compat.h>
+#endif
+
+#endif /* _AC_SYSEXITS_H_ */
diff --git a/include/ac/syslog.h b/include/ac/syslog.h
new file mode 100644
index 0000000..90028ae
--- /dev/null
+++ b/include/ac/syslog.h
@@ -0,0 +1,38 @@
+/* Generic syslog.h */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _AC_SYSLOG_H_
+#define _AC_SYSLOG_H_
+
+#if defined( HAVE_SYSLOG_H )
+#include <syslog.h>
+#elif defined ( HAVE_SYS_SYSLOG_H )
+#include <sys/syslog.h>
+#endif
+
+#if defined( LOG_NDELAY ) && defined( LOG_NOWAIT )
+# define OPENLOG_OPTIONS ( LOG_PID | LOG_NDELAY | LOG_NOWAIT )
+#elif defined( LOG_NDELAY )
+# define OPENLOG_OPTIONS ( LOG_PID | LOG_NDELAY )
+#elif defined( LOG_NOWAIT )
+# define OPENLOG_OPTIONS ( LOG_PID | LOG_NOWAIT )
+#elif defined( LOG_PID )
+# define OPENLOG_OPTIONS ( LOG_PID )
+#else
+# define OPENLOG_OPTIONS ( 0 )
+#endif
+
+#endif /* _AC_SYSLOG_H_ */
diff --git a/include/ac/termios.h b/include/ac/termios.h
new file mode 100644
index 0000000..427b1ca
--- /dev/null
+++ b/include/ac/termios.h
@@ -0,0 +1,50 @@
+/* Generic termios.h */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _AC_TERMIOS_H
+#define _AC_TERMIOS_H
+
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+
+#ifdef GCWINSZ_IN_SYS_IOCTL
+#include <sys/ioctl.h>
+#endif
+
+#define TERMIO_TYPE struct termios
+#define TERMFLAG_TYPE tcflag_t
+#define GETATTR( fd, tiop ) tcgetattr((fd), (tiop))
+#define SETATTR( fd, tiop ) tcsetattr((fd), TCSANOW /* 0 */, (tiop))
+#define GETFLAGS( tio ) ((tio).c_lflag)
+#define SETFLAGS( tio, flags ) ((tio).c_lflag = (flags))
+
+#elif defined( HAVE_SGTTY_H )
+#include <sgtty.h>
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#define TERMIO_TYPE struct sgttyb
+#define TERMFLAG_TYPE int
+#define GETATTR( fd, tiop ) ioctl((fd), TIOCGETP, (caddr_t)(tiop))
+#define SETATTR( fd, tiop ) ioctl((fd), TIOCSETP, (caddr_t)(tiop))
+#define GETFLAGS( tio ) ((tio).sg_flags)
+#define SETFLAGS( tio, flags ) ((tio).sg_flags = (flags))
+
+#endif /* HAVE_SGTTY_H */
+
+#endif /* _AC_TERMIOS_H */
diff --git a/include/ac/time.h b/include/ac/time.h
new file mode 100644
index 0000000..f36b940
--- /dev/null
+++ b/include/ac/time.h
@@ -0,0 +1,39 @@
+/* Generic time.h */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _AC_TIME_H
+#define _AC_TIME_H
+
+#ifdef TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#elif defined(HAVE_SYS_TIME_H)
+# include <sys/time.h>
+# ifdef HAVE_SYS_TIMEB_H
+# include <sys/timeb.h>
+# endif
+#else
+# include <time.h>
+#endif
+
+#if defined(_WIN32) && !defined(HAVE_CLOCK_GETTIME)
+ struct timespec {
+ time_t tv_sec;
+ int tv_nsec;
+ };
+#endif
+
+#endif /* _AC_TIME_H */
diff --git a/include/ac/unistd.h b/include/ac/unistd.h
new file mode 100644
index 0000000..d9c4529
--- /dev/null
+++ b/include/ac/unistd.h
@@ -0,0 +1,72 @@
+/* Generic unistd.h */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _AC_UNISTD_H
+#define _AC_UNISTD_H
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_PROCESS_H
+# include <process.h>
+#endif
+
+/* note: callers of crypt(3) should include <ac/crypt.h> */
+
+#if defined(HAVE_GETPASSPHRASE)
+LDAP_LIBC_F(char*)(getpassphrase)();
+
+#else
+#define getpassphrase(p) lutil_getpass(p)
+LDAP_LUTIL_F(char*)(lutil_getpass) LDAP_P((const char *getpass));
+#endif
+
+/* getopt() defines may be in separate include file */
+#ifdef HAVE_GETOPT_H
+# include <getopt.h>
+
+#elif !defined(HAVE_GETOPT)
+ /* no getopt, assume we need getopt-compat.h */
+# include <getopt-compat.h>
+
+#else
+ /* assume we need to declare these externs */
+ LDAP_LIBC_V (char *) optarg;
+ LDAP_LIBC_V (int) optind, opterr, optopt;
+#endif
+
+/* use lutil file locking */
+#define ldap_lockf(x) lutil_lockf(x)
+#define ldap_unlockf(x) lutil_unlockf(x)
+#include <lutil_lockf.h>
+
+/*
+ * Windows: although sleep() will be resolved by both MSVC and Mingw GCC
+ * linkers, the function is not declared in header files. This is
+ * because Windows' version of the function is called _sleep(), and it
+ * is declared in stdlib.h
+ */
+
+#ifdef _WIN32
+#define sleep _sleep
+#endif
+
+#endif /* _AC_UNISTD_H */
diff --git a/include/ac/wait.h b/include/ac/wait.h
new file mode 100644
index 0000000..4837934
--- /dev/null
+++ b/include/ac/wait.h
@@ -0,0 +1,56 @@
+/* Generic wait.h */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _AC_WAIT_H
+#define _AC_WAIT_H
+
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+#define LDAP_HI(s) (((s) >> 8) & 0377)
+#define LDAP_LO(s) ((s) & 0377)
+
+/* These should work on non-POSIX UNIX platforms,
+ all bets on off on non-POSIX non-UNIX platforms... */
+#ifndef WIFEXITED
+# define WIFEXITED(s) (LDAP_LO(s) == 0)
+#endif
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(s) LDAP_HI(s)
+#endif
+#ifndef WIFSIGNALED
+# define WIFSIGNALED(s) (LDAP_LO(s) > 0 && LDAP_HI(s) == 0)
+#endif
+#ifndef WTERMSIG
+# define WTERMSIG(s) (LDAP_LO(s) & 0177)
+#endif
+#ifndef WIFSTOPPED
+# define WIFSTOPPED(s) (LDAP_LO(s) == 0177 && LDAP_HI(s) != 0)
+#endif
+#ifndef WSTOPSIG
+# define WSTOPSIG(s) LDAP_HI(s)
+#endif
+
+#ifdef WCONTINUED
+# define WAIT_FLAGS ( WNOHANG | WUNTRACED | WCONTINUED )
+#else
+# define WAIT_FLAGS ( WNOHANG | WUNTRACED )
+#endif
+
+#endif /* _AC_WAIT_H */
diff --git a/include/getopt-compat.h b/include/getopt-compat.h
new file mode 100644
index 0000000..3bbc1e4
--- /dev/null
+++ b/include/getopt-compat.h
@@ -0,0 +1,40 @@
+/* getopt-compat.h -- getopt(3) compatibility header */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * getopt(3) declarations
+ */
+#ifndef _GETOPT_COMPAT_H
+#define _GETOPT_COMPAT_H
+
+#include <ldap_cdefs.h>
+
+LDAP_BEGIN_DECL
+
+/* change symbols to avoid clashing */
+#define optarg lutil_optarg
+#define optind lutil_optind
+#define opterr lutil_opterr
+#define optopt lutil_optopt
+#define getopt lutil_getopt
+
+LDAP_LUTIL_V (char *) optarg;
+LDAP_LUTIL_V (int) optind, opterr, optopt;
+LDAP_LUTIL_F (int) getopt LDAP_P(( int, char * const [], const char *));
+
+LDAP_END_DECL
+
+#endif /* _GETOPT_COMPAT_H */
diff --git a/include/lber.h b/include/lber.h
new file mode 100644
index 0000000..d474eb0
--- /dev/null
+++ b/include/lber.h
@@ -0,0 +1,691 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#ifndef _LBER_H
+#define _LBER_H
+
+#include <lber_types.h>
+#include <string.h>
+
+LDAP_BEGIN_DECL
+
+/*
+ * ber_tag_t represents the identifier octets at the beginning of BER
+ * elements. OpenLDAP treats them as mere big-endian unsigned integers.
+ *
+ * Actually the BER identifier octets look like this:
+ *
+ * Bits of 1st octet:
+ * ______
+ * 8 7 | CLASS
+ * 0 0 = UNIVERSAL
+ * 0 1 = APPLICATION
+ * 1 0 = CONTEXT-SPECIFIC
+ * 1 1 = PRIVATE
+ * _____
+ * | 6 | DATA-TYPE
+ * 0 = PRIMITIVE
+ * 1 = CONSTRUCTED
+ * ___________
+ * | 5 ... 1 | TAG-NUMBER
+ *
+ * For ASN.1 tag numbers >= 0x1F, TAG-NUMBER above is 0x1F and the next
+ * BER octets contain the actual ASN.1 tag number: Big-endian, base
+ * 128, 8.bit = 1 in all but the last octet, minimum number of octets.
+ */
+
+/* BER classes and mask (in 1st identifier octet) */
+#define LBER_CLASS_UNIVERSAL ((ber_tag_t) 0x00U)
+#define LBER_CLASS_APPLICATION ((ber_tag_t) 0x40U)
+#define LBER_CLASS_CONTEXT ((ber_tag_t) 0x80U)
+#define LBER_CLASS_PRIVATE ((ber_tag_t) 0xc0U)
+#define LBER_CLASS_MASK ((ber_tag_t) 0xc0U)
+
+/* BER encoding type and mask (in 1st identifier octet) */
+#define LBER_PRIMITIVE ((ber_tag_t) 0x00U)
+#define LBER_CONSTRUCTED ((ber_tag_t) 0x20U)
+#define LBER_ENCODING_MASK ((ber_tag_t) 0x20U)
+
+#define LBER_BIG_TAG_MASK ((ber_tag_t) 0x1fU)
+#define LBER_MORE_TAG_MASK ((ber_tag_t) 0x80U)
+
+/*
+ * LBER_ERROR and LBER_DEFAULT are values that can never appear
+ * as valid BER tags, so it is safe to use them to report errors.
+ * Valid tags have (tag & (ber_tag_t) 0xFF) != 0xFF.
+ */
+#define LBER_ERROR ((ber_tag_t) -1)
+#define LBER_DEFAULT ((ber_tag_t) -1)
+
+/* general BER types we know about */
+#define LBER_BOOLEAN ((ber_tag_t) 0x01UL)
+#define LBER_INTEGER ((ber_tag_t) 0x02UL)
+#define LBER_BITSTRING ((ber_tag_t) 0x03UL)
+#define LBER_OCTETSTRING ((ber_tag_t) 0x04UL)
+#define LBER_NULL ((ber_tag_t) 0x05UL)
+#define LBER_ENUMERATED ((ber_tag_t) 0x0aUL)
+#define LBER_SEQUENCE ((ber_tag_t) 0x30UL) /* constructed */
+#define LBER_SET ((ber_tag_t) 0x31UL) /* constructed */
+
+/* LBER BerElement options */
+#define LBER_USE_DER 0x01
+
+/* get/set options for BerElement */
+#define LBER_OPT_BER_OPTIONS 0x01
+#define LBER_OPT_BER_DEBUG 0x02
+#define LBER_OPT_BER_REMAINING_BYTES 0x03
+#define LBER_OPT_BER_TOTAL_BYTES 0x04
+#define LBER_OPT_BER_BYTES_TO_WRITE 0x05
+#define LBER_OPT_BER_MEMCTX 0x06
+
+#define LBER_OPT_DEBUG_LEVEL LBER_OPT_BER_DEBUG
+#define LBER_OPT_REMAINING_BYTES LBER_OPT_BER_REMAINING_BYTES
+#define LBER_OPT_TOTAL_BYTES LBER_OPT_BER_TOTAL_BYTES
+#define LBER_OPT_BYTES_TO_WRITE LBER_OPT_BER_BYTES_TO_WRITE
+
+#define LBER_OPT_LOG_PRINT_FN 0x8001
+#define LBER_OPT_MEMORY_FNS 0x8002
+#define LBER_OPT_ERROR_FN 0x8003
+#define LBER_OPT_LOG_PRINT_FILE 0x8004
+
+/* get/set Memory Debug options */
+#define LBER_OPT_MEMORY_INUSE 0x8005 /* for memory debugging */
+#define LBER_OPT_LOG_PROC 0x8006 /* for external logging function */
+
+typedef int* (*BER_ERRNO_FN) LDAP_P(( void ));
+
+typedef void (*BER_LOG_PRINT_FN) LDAP_P(( LDAP_CONST char *buf ));
+
+typedef void* (BER_MEMALLOC_FN) LDAP_P(( ber_len_t size, void *ctx ));
+typedef void* (BER_MEMCALLOC_FN) LDAP_P(( ber_len_t n, ber_len_t size, void *ctx ));
+typedef void* (BER_MEMREALLOC_FN) LDAP_P(( void *p, ber_len_t size, void *ctx ));
+typedef void (BER_MEMFREE_FN) LDAP_P(( void *p, void *ctx ));
+
+typedef struct lber_memory_fns {
+ BER_MEMALLOC_FN *bmf_malloc;
+ BER_MEMCALLOC_FN *bmf_calloc;
+ BER_MEMREALLOC_FN *bmf_realloc;
+ BER_MEMFREE_FN *bmf_free;
+} BerMemoryFunctions;
+
+/* LBER Sockbuf_IO options */
+#define LBER_SB_OPT_GET_FD 1
+#define LBER_SB_OPT_SET_FD 2
+#define LBER_SB_OPT_HAS_IO 3
+#define LBER_SB_OPT_SET_NONBLOCK 4
+#define LBER_SB_OPT_GET_SSL 7
+#define LBER_SB_OPT_DATA_READY 8
+#define LBER_SB_OPT_SET_READAHEAD 9
+#define LBER_SB_OPT_DRAIN 10
+#define LBER_SB_OPT_NEEDS_READ 11
+#define LBER_SB_OPT_NEEDS_WRITE 12
+#define LBER_SB_OPT_GET_MAX_INCOMING 13
+#define LBER_SB_OPT_SET_MAX_INCOMING 14
+
+/* Only meaningful ifdef LDAP_PF_LOCAL_SENDMSG */
+#define LBER_SB_OPT_UNGET_BUF 15
+
+/* Largest option used by the library */
+#define LBER_SB_OPT_OPT_MAX 15
+
+/* LBER IO operations stacking levels */
+#define LBER_SBIOD_LEVEL_PROVIDER 10
+#define LBER_SBIOD_LEVEL_TRANSPORT 20
+#define LBER_SBIOD_LEVEL_APPLICATION 30
+
+/* get/set options for Sockbuf */
+#define LBER_OPT_SOCKBUF_DESC 0x1000
+#define LBER_OPT_SOCKBUF_OPTIONS 0x1001
+#define LBER_OPT_SOCKBUF_DEBUG 0x1002
+
+/* on/off values */
+LBER_V( char ) ber_pvt_opt_on;
+#define LBER_OPT_ON ((void *) &ber_pvt_opt_on)
+#define LBER_OPT_OFF ((void *) 0)
+
+#define LBER_OPT_SUCCESS (0)
+#define LBER_OPT_ERROR (-1)
+
+typedef struct berelement BerElement;
+typedef struct sockbuf Sockbuf;
+
+typedef struct sockbuf_io Sockbuf_IO;
+
+/* Structure for LBER IO operation descriptor */
+typedef struct sockbuf_io_desc {
+ int sbiod_level;
+ Sockbuf *sbiod_sb;
+ Sockbuf_IO *sbiod_io;
+ void *sbiod_pvt;
+ struct sockbuf_io_desc *sbiod_next;
+} Sockbuf_IO_Desc;
+
+/* Structure for LBER IO operation functions */
+struct sockbuf_io {
+ int (*sbi_setup)( Sockbuf_IO_Desc *sbiod, void *arg );
+ int (*sbi_remove)( Sockbuf_IO_Desc *sbiod );
+ int (*sbi_ctrl)( Sockbuf_IO_Desc *sbiod, int opt, void *arg);
+
+ ber_slen_t (*sbi_read)( Sockbuf_IO_Desc *sbiod, void *buf,
+ ber_len_t len );
+ ber_slen_t (*sbi_write)( Sockbuf_IO_Desc *sbiod, void *buf,
+ ber_len_t len );
+
+ int (*sbi_close)( Sockbuf_IO_Desc *sbiod );
+};
+
+/* Helper macros for LBER IO functions */
+#define LBER_SBIOD_READ_NEXT( sbiod, buf, len ) \
+ ( (sbiod)->sbiod_next->sbiod_io->sbi_read( (sbiod)->sbiod_next, \
+ buf, len ) )
+#define LBER_SBIOD_WRITE_NEXT( sbiod, buf, len ) \
+ ( (sbiod)->sbiod_next->sbiod_io->sbi_write( (sbiod)->sbiod_next, \
+ buf, len ) )
+#define LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg ) \
+ ( (sbiod)->sbiod_next ? \
+ ( (sbiod)->sbiod_next->sbiod_io->sbi_ctrl( \
+ (sbiod)->sbiod_next, opt, arg ) ) : 0 )
+
+/* structure for returning a sequence of octet strings + length */
+typedef struct berval {
+ ber_len_t bv_len;
+ char *bv_val;
+} BerValue;
+
+typedef BerValue *BerVarray; /* To distinguish from a single bv */
+
+/* this should be moved to lber-int.h */
+
+/*
+ * in bprint.c:
+ */
+LBER_F( void )
+ber_error_print LDAP_P((
+ LDAP_CONST char *data ));
+
+LBER_F( void )
+ber_bprint LDAP_P((
+ LDAP_CONST char *data, ber_len_t len ));
+
+LBER_F( void )
+ber_dump LDAP_P((
+ BerElement *ber, int inout ));
+
+/*
+ * in decode.c:
+ */
+typedef int (*BERDecodeCallback) LDAP_P((
+ BerElement *ber,
+ void *data,
+ int mode ));
+
+LBER_F( ber_tag_t )
+ber_get_tag LDAP_P((
+ BerElement *ber ));
+
+LBER_F( ber_tag_t )
+ber_skip_tag LDAP_P((
+ BerElement *ber,
+ ber_len_t *len ));
+
+LBER_F( ber_tag_t )
+ber_peek_tag LDAP_P((
+ BerElement *ber,
+ ber_len_t *len ));
+
+LBER_F( ber_tag_t )
+ber_skip_raw LDAP_P((
+ BerElement *ber,
+ struct berval *bv ));
+
+LBER_F( ber_tag_t )
+ber_skip_element LDAP_P((
+ BerElement *ber,
+ struct berval *bv ));
+
+LBER_F( ber_tag_t )
+ber_peek_element LDAP_P((
+ LDAP_CONST BerElement *ber,
+ struct berval *bv ));
+
+LBER_F( ber_tag_t )
+ber_get_int LDAP_P((
+ BerElement *ber,
+ ber_int_t *num ));
+
+LBER_F( ber_tag_t )
+ber_get_enum LDAP_P((
+ BerElement *ber,
+ ber_int_t *num ));
+
+LBER_F( int )
+ber_decode_int LDAP_P((
+ const struct berval *bv,
+ ber_int_t *num ));
+
+LBER_F( ber_tag_t )
+ber_get_stringb LDAP_P((
+ BerElement *ber,
+ char *buf,
+ ber_len_t *len ));
+
+#define LBER_BV_ALLOC 0x01 /* allocate/copy result, otherwise in-place */
+#define LBER_BV_NOTERM 0x02 /* omit NUL-terminator if parsing in-place */
+#define LBER_BV_STRING 0x04 /* fail if berval contains embedded \0 */
+/* LBER_BV_STRING currently accepts a terminating \0 in the berval, because
+ * Active Directory sends that in at least the diagonsticMessage field.
+ */
+
+LBER_F( ber_tag_t )
+ber_get_stringbv LDAP_P((
+ BerElement *ber,
+ struct berval *bv,
+ int options ));
+
+LBER_F( ber_tag_t )
+ber_get_stringa LDAP_P((
+ BerElement *ber,
+ char **buf ));
+
+LBER_F( ber_tag_t )
+ber_get_stringal LDAP_P((
+ BerElement *ber,
+ struct berval **bv ));
+
+LBER_F( ber_tag_t )
+ber_get_bitstringa LDAP_P((
+ BerElement *ber,
+ char **buf,
+ ber_len_t *len ));
+
+LBER_F( ber_tag_t )
+ber_get_null LDAP_P((
+ BerElement *ber ));
+
+LBER_F( ber_tag_t )
+ber_get_boolean LDAP_P((
+ BerElement *ber,
+ ber_int_t *boolval ));
+
+LBER_F( ber_tag_t )
+ber_first_element LDAP_P((
+ BerElement *ber,
+ ber_len_t *len,
+ char **last ));
+
+LBER_F( ber_tag_t )
+ber_next_element LDAP_P((
+ BerElement *ber,
+ ber_len_t *len,
+ LDAP_CONST char *last ));
+
+LBER_F( ber_tag_t )
+ber_scanf LDAP_P((
+ BerElement *ber,
+ LDAP_CONST char *fmt,
+ ... ));
+
+LBER_F( int )
+ber_decode_oid LDAP_P((
+ struct berval *in,
+ struct berval *out ));
+
+/*
+ * in encode.c
+ */
+LBER_F( int )
+ber_encode_oid LDAP_P((
+ struct berval *in,
+ struct berval *out ));
+
+typedef int (*BEREncodeCallback) LDAP_P((
+ BerElement *ber,
+ void *data ));
+
+LBER_F( int )
+ber_put_enum LDAP_P((
+ BerElement *ber,
+ ber_int_t num,
+ ber_tag_t tag ));
+
+LBER_F( int )
+ber_put_int LDAP_P((
+ BerElement *ber,
+ ber_int_t num,
+ ber_tag_t tag ));
+
+LBER_F( int )
+ber_put_ostring LDAP_P((
+ BerElement *ber,
+ LDAP_CONST char *str,
+ ber_len_t len,
+ ber_tag_t tag ));
+
+LBER_F( int )
+ber_put_berval LDAP_P((
+ BerElement *ber,
+ struct berval *bv,
+ ber_tag_t tag ));
+
+LBER_F( int )
+ber_put_string LDAP_P((
+ BerElement *ber,
+ LDAP_CONST char *str,
+ ber_tag_t tag ));
+
+LBER_F( int )
+ber_put_bitstring LDAP_P((
+ BerElement *ber,
+ LDAP_CONST char *str,
+ ber_len_t bitlen,
+ ber_tag_t tag ));
+
+LBER_F( int )
+ber_put_null LDAP_P((
+ BerElement *ber,
+ ber_tag_t tag ));
+
+LBER_F( int )
+ber_put_boolean LDAP_P((
+ BerElement *ber,
+ ber_int_t boolval,
+ ber_tag_t tag ));
+
+LBER_F( int )
+ber_start_seq LDAP_P((
+ BerElement *ber,
+ ber_tag_t tag ));
+
+LBER_F( int )
+ber_start_set LDAP_P((
+ BerElement *ber,
+ ber_tag_t tag ));
+
+LBER_F( int )
+ber_put_seq LDAP_P((
+ BerElement *ber ));
+
+LBER_F( int )
+ber_put_set LDAP_P((
+ BerElement *ber ));
+
+LBER_F( int )
+ber_printf LDAP_P((
+ BerElement *ber,
+ LDAP_CONST char *fmt,
+ ... ));
+
+
+/*
+ * in io.c:
+ */
+
+LBER_F( ber_slen_t )
+ber_skip_data LDAP_P((
+ BerElement *ber,
+ ber_len_t len ));
+
+LBER_F( ber_slen_t )
+ber_read LDAP_P((
+ BerElement *ber,
+ char *buf,
+ ber_len_t len ));
+
+LBER_F( ber_slen_t )
+ber_write LDAP_P((
+ BerElement *ber,
+ LDAP_CONST char *buf,
+ ber_len_t len,
+ int zero )); /* nonzero is unsupported from OpenLDAP 2.4.18 */
+
+LBER_F( void )
+ber_free LDAP_P((
+ BerElement *ber,
+ int freebuf ));
+
+LBER_F( void )
+ber_free_buf LDAP_P(( BerElement *ber ));
+
+LBER_F( int )
+ber_flush2 LDAP_P((
+ Sockbuf *sb,
+ BerElement *ber,
+ int freeit ));
+#define LBER_FLUSH_FREE_NEVER (0x0) /* traditional behavior */
+#define LBER_FLUSH_FREE_ON_SUCCESS (0x1) /* traditional behavior */
+#define LBER_FLUSH_FREE_ON_ERROR (0x2)
+#define LBER_FLUSH_FREE_ALWAYS (LBER_FLUSH_FREE_ON_SUCCESS|LBER_FLUSH_FREE_ON_ERROR)
+
+LBER_F( int )
+ber_flush LDAP_P((
+ Sockbuf *sb,
+ BerElement *ber,
+ int freeit )); /* DEPRECATED */
+
+LBER_F( BerElement * )
+ber_alloc LDAP_P(( void )); /* DEPRECATED */
+
+LBER_F( BerElement * )
+der_alloc LDAP_P(( void )); /* DEPRECATED */
+
+LBER_F( BerElement * )
+ber_alloc_t LDAP_P((
+ int beroptions ));
+
+LBER_F( BerElement * )
+ber_dup LDAP_P((
+ BerElement *ber ));
+
+LBER_F( ber_tag_t )
+ber_get_next LDAP_P((
+ Sockbuf *sb,
+ ber_len_t *len,
+ BerElement *ber ));
+
+LBER_F( void )
+ber_init2 LDAP_P((
+ BerElement *ber,
+ struct berval *bv,
+ int options ));
+
+LBER_F( void )
+ber_init_w_nullc LDAP_P(( /* DEPRECATED */
+ BerElement *ber,
+ int options ));
+
+LBER_F( void )
+ber_reset LDAP_P((
+ BerElement *ber,
+ int was_writing ));
+
+LBER_F( BerElement * )
+ber_init LDAP_P((
+ struct berval *bv ));
+
+LBER_F( int )
+ber_flatten LDAP_P((
+ BerElement *ber,
+ struct berval **bvPtr ));
+
+LBER_F( int )
+ber_flatten2 LDAP_P((
+ BerElement *ber,
+ struct berval *bv,
+ int alloc ));
+
+LBER_F( int )
+ber_remaining LDAP_P((
+ BerElement *ber ));
+
+/*
+ * LBER ber accessor functions
+ */
+
+LBER_F( int )
+ber_get_option LDAP_P((
+ void *item,
+ int option,
+ void *outvalue));
+
+LBER_F( int )
+ber_set_option LDAP_P((
+ void *item,
+ int option,
+ LDAP_CONST void *invalue));
+
+/*
+ * LBER sockbuf.c
+ */
+
+LBER_F( Sockbuf * )
+ber_sockbuf_alloc LDAP_P((
+ void ));
+
+LBER_F( void )
+ber_sockbuf_free LDAP_P((
+ Sockbuf *sb ));
+
+LBER_F( int )
+ber_sockbuf_add_io LDAP_P((
+ Sockbuf *sb,
+ Sockbuf_IO *sbio,
+ int layer,
+ void *arg ));
+
+LBER_F( int )
+ber_sockbuf_remove_io LDAP_P((
+ Sockbuf *sb,
+ Sockbuf_IO *sbio,
+ int layer ));
+
+LBER_F( int )
+ber_sockbuf_ctrl LDAP_P((
+ Sockbuf *sb,
+ int opt,
+ void *arg ));
+
+LBER_V( Sockbuf_IO ) ber_sockbuf_io_tcp;
+LBER_V( Sockbuf_IO ) ber_sockbuf_io_readahead;
+LBER_V( Sockbuf_IO ) ber_sockbuf_io_fd;
+LBER_V( Sockbuf_IO ) ber_sockbuf_io_debug;
+LBER_V( Sockbuf_IO ) ber_sockbuf_io_udp;
+
+/*
+ * LBER memory.c
+ */
+LBER_F( void * )
+ber_memalloc LDAP_P((
+ ber_len_t s ));
+
+LBER_F( void * )
+ber_memrealloc LDAP_P((
+ void* p,
+ ber_len_t s ));
+
+LBER_F( void * )
+ber_memcalloc LDAP_P((
+ ber_len_t n,
+ ber_len_t s ));
+
+LBER_F( void )
+ber_memfree LDAP_P((
+ void* p ));
+
+LBER_F( void )
+ber_memvfree LDAP_P((
+ void** vector ));
+
+LBER_F( void )
+ber_bvfree LDAP_P((
+ struct berval *bv ));
+
+LBER_F( void )
+ber_bvecfree LDAP_P((
+ struct berval **bv ));
+
+LBER_F( int )
+ber_bvecadd LDAP_P((
+ struct berval ***bvec,
+ struct berval *bv ));
+
+LBER_F( struct berval * )
+ber_dupbv LDAP_P((
+ struct berval *dst, struct berval *src ));
+
+LBER_F( struct berval * )
+ber_bvdup LDAP_P((
+ struct berval *src ));
+
+LBER_F( struct berval * )
+ber_mem2bv LDAP_P((
+ LDAP_CONST char *, ber_len_t len, int duplicate, struct berval *bv));
+
+LBER_F( struct berval * )
+ber_str2bv LDAP_P((
+ LDAP_CONST char *, ber_len_t len, int duplicate, struct berval *bv));
+
+#define ber_bvstr(a) ((ber_str2bv)((a), 0, 0, NULL))
+#define ber_bvstrdup(a) ((ber_str2bv)((a), 0, 1, NULL))
+
+LBER_F( char * )
+ber_strdup LDAP_P((
+ LDAP_CONST char * ));
+
+LBER_F( ber_len_t )
+ber_strnlen LDAP_P((
+ LDAP_CONST char *s, ber_len_t len ));
+
+LBER_F( char * )
+ber_strndup LDAP_P((
+ LDAP_CONST char *s, ber_len_t l ));
+
+LBER_F( struct berval * )
+ber_bvreplace LDAP_P((
+ struct berval *dst, LDAP_CONST struct berval *src ));
+
+LBER_F( void )
+ber_bvarray_free LDAP_P(( BerVarray p ));
+
+LBER_F( int )
+ber_bvarray_add LDAP_P(( BerVarray *p, BerValue *bv ));
+
+#define ber_bvcmp(v1,v2) \
+ ((v1)->bv_len < (v2)->bv_len \
+ ? -1 : ((v1)->bv_len > (v2)->bv_len \
+ ? 1 : memcmp((v1)->bv_val, (v2)->bv_val, (v1)->bv_len) ))
+
+/*
+ * error.c
+ */
+LBER_F( int * ) ber_errno_addr LDAP_P((void));
+#define ber_errno (*(ber_errno_addr)())
+
+#define LBER_ERROR_NONE 0
+#define LBER_ERROR_PARAM 0x1
+#define LBER_ERROR_MEMORY 0x2
+
+LDAP_END_DECL
+
+#endif /* _LBER_H */
diff --git a/include/lber_pvt.h b/include/lber_pvt.h
new file mode 100644
index 0000000..474c291
--- /dev/null
+++ b/include/lber_pvt.h
@@ -0,0 +1,223 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * lber_pvt.h - Header for ber_pvt_ functions.
+ * These are meant to be internal to OpenLDAP Software.
+ */
+
+#ifndef _LBER_PVT_H
+#define _LBER_PVT_H 1
+
+#include <lber.h>
+
+LDAP_BEGIN_DECL
+
+/* for allocating aligned buffers (on the stack) */
+#define LBER_ALIGNED_BUFFER(uname,size) \
+ union uname { \
+ char buffer[size]; \
+ /* force alignment */ \
+ int ialign; \
+ long lalign; \
+ float falign; \
+ double dalign; \
+ char* palign; \
+ }
+
+#define LBER_ELEMENT_SIZEOF (256) /* must be >= sizeof(BerElement) */
+typedef LBER_ALIGNED_BUFFER(lber_berelement_u,LBER_ELEMENT_SIZEOF)
+ BerElementBuffer;
+
+typedef struct sockbuf_buf {
+ ber_len_t buf_size;
+ ber_len_t buf_ptr;
+ ber_len_t buf_end;
+ char *buf_base;
+} Sockbuf_Buf;
+
+/*
+ * bprint.c
+ */
+LBER_V( BER_LOG_PRINT_FN ) ber_pvt_log_print;
+
+LBER_F( int )
+ber_pvt_log_printf LDAP_P((
+ int errlvl,
+ int loglvl,
+ const char *fmt,
+ ... )) LDAP_GCCATTR((format(printf, 3, 4)));
+
+/*
+ * sockbuf.c
+ */
+LBER_F( ber_slen_t )
+ber_pvt_sb_do_write LDAP_P(( Sockbuf_IO_Desc *sbiod, Sockbuf_Buf *buf_out ));
+
+LBER_F( void )
+ber_pvt_sb_buf_init LDAP_P(( Sockbuf_Buf *buf ));
+
+LBER_F( void )
+ber_pvt_sb_buf_destroy LDAP_P(( Sockbuf_Buf *buf ));
+
+LBER_F( int )
+ber_pvt_sb_grow_buffer LDAP_P(( Sockbuf_Buf *buf, ber_len_t minsize ));
+
+LBER_F( ber_len_t )
+ber_pvt_sb_copy_out LDAP_P(( Sockbuf_Buf *sbb, char *buf, ber_len_t len ));
+
+LBER_F( int )
+ber_pvt_socket_set_nonblock LDAP_P(( ber_socket_t sd, int nb ));
+
+/*
+ * memory.c
+ */
+LBER_F( void * )
+ber_memalloc_x LDAP_P((
+ ber_len_t s, void *ctx));
+
+LBER_F( void * )
+ber_memrealloc_x LDAP_P((
+ void* p,
+ ber_len_t s, void *ctx ));
+
+LBER_F( void * )
+ber_memcalloc_x LDAP_P((
+ ber_len_t n,
+ ber_len_t s, void *ctx ));
+
+LBER_F( void )
+ber_memfree_x LDAP_P((
+ void* p, void *ctx ));
+
+LBER_F( void )
+ber_memvfree_x LDAP_P((
+ void** vector, void *ctx ));
+
+LBER_F( void )
+ber_bvfree_x LDAP_P((
+ struct berval *bv, void *ctx ));
+
+LBER_F( void )
+ber_bvecfree_x LDAP_P((
+ struct berval **bv, void *ctx ));
+
+LBER_F( int )
+ber_bvecadd_x LDAP_P((
+ struct berval ***bvec,
+ struct berval *bv, void *ctx ));
+
+LBER_F( struct berval * )
+ber_dupbv_x LDAP_P((
+ struct berval *dst, struct berval *src, void *ctx ));
+
+LBER_F( struct berval * )
+ber_str2bv_x LDAP_P((
+ LDAP_CONST char *, ber_len_t len, int dup, struct berval *bv, void *ctx));
+
+LBER_F( struct berval * )
+ber_mem2bv_x LDAP_P((
+ LDAP_CONST char *, ber_len_t len, int dup, struct berval *bv, void *ctx));
+
+LBER_F( char * )
+ber_strdup_x LDAP_P((
+ LDAP_CONST char *, void *ctx ));
+
+LBER_F( struct berval * )
+ber_bvreplace_x LDAP_P((
+ struct berval *dst, LDAP_CONST struct berval *src, void *ctx ));
+
+LBER_F( void )
+ber_bvarray_free_x LDAP_P(( BerVarray p, void *ctx ));
+
+LBER_F( int )
+ber_bvarray_add_x LDAP_P(( BerVarray *p, BerValue *bv, void *ctx ));
+
+LBER_F( int )
+ber_bvarray_dup_x LDAP_P(( BerVarray *dst, BerVarray src, void *ctx ));
+
+#if 0
+#define ber_bvstrcmp(v1,v2) \
+ ((v1)->bv_len < (v2)->bv_len \
+ ? -1 : ((v1)->bv_len > (v2)->bv_len \
+ ? 1 : strncmp((v1)->bv_val, (v2)->bv_val, (v1)->bv_len) ))
+#else
+ /* avoid strncmp() */
+#define ber_bvstrcmp(v1,v2) ber_bvcmp((v1),(v2))
+#endif
+
+#define ber_bvstrcasecmp(v1,v2) \
+ ((v1)->bv_len < (v2)->bv_len \
+ ? -1 : ((v1)->bv_len > (v2)->bv_len \
+ ? 1 : strncasecmp((v1)->bv_val, (v2)->bv_val, (v1)->bv_len) ))
+
+#define ber_bvccmp(v1,c) \
+ ( (v1)->bv_len == 1 && (v1)->bv_val[0] == (c) )
+
+#define ber_strccmp(s,c) \
+ ( (s)[0] == (c) && (s)[1] == '\0' )
+
+#define ber_bvchr(bv,c) \
+ ((char *) memchr( (bv)->bv_val, (c), (bv)->bv_len ))
+
+#define ber_bvrchr(bv,c) \
+ ((char *) lutil_memrchr( (bv)->bv_val, (c), (bv)->bv_len ))
+
+#define ber_bvchr_post(dst,bv,c) \
+ do { \
+ (dst)->bv_val = memchr( (bv)->bv_val, (c), (bv)->bv_len ); \
+ (dst)->bv_len = (dst)->bv_val ? (bv)->bv_len - ((dst)->bv_val - (bv)->bv_val) : 0; \
+ } while (0)
+
+#define ber_bvchr_pre(dst,bv,c) \
+ do { \
+ (dst)->bv_val = memchr( (bv)->bv_val, (c), (bv)->bv_len ); \
+ (dst)->bv_len = (dst)->bv_val ? ((dst)->bv_val - (bv)->bv_val) : (bv)->bv_len; \
+ (dst)->bv_val = (bv)->bv_val; \
+ } while (0)
+
+#define ber_bvrchr_post(dst,bv,c) \
+ do { \
+ (dst)->bv_val = lutil_memrchr( (bv)->bv_val, (c), (bv)->bv_len ); \
+ (dst)->bv_len = (dst)->bv_val ? (bv)->bv_len - ((dst)->bv_val - (bv)->bv_val) : 0; \
+ } while (0)
+
+#define ber_bvrchr_pre(dst,bv,c) \
+ do { \
+ (dst)->bv_val = lutil_memrchr( (bv)->bv_val, (c), (bv)->bv_len ); \
+ (dst)->bv_len = (dst)->bv_val ? ((dst)->bv_val - (bv)->bv_val) : (bv)->bv_len; \
+ (dst)->bv_val = (bv)->bv_val; \
+ } while (0)
+
+#define BER_STRLENOF(s) (sizeof(s)-1)
+#define BER_BVC(s) { BER_STRLENOF(s), (char *)(s) }
+#define BER_BVNULL { 0L, NULL }
+#define BER_BVZERO(bv) \
+ do { \
+ (bv)->bv_len = 0; \
+ (bv)->bv_val = NULL; \
+ } while (0)
+#define BER_BVSTR(bv,s) \
+ do { \
+ (bv)->bv_len = BER_STRLENOF(s); \
+ (bv)->bv_val = (s); \
+ } while (0)
+#define BER_BVISNULL(bv) ((bv)->bv_val == NULL)
+#define BER_BVISEMPTY(bv) ((bv)->bv_len == 0)
+
+LDAP_END_DECL
+
+#endif
+
diff --git a/include/lber_types.hin b/include/lber_types.hin
new file mode 100644
index 0000000..a557068
--- /dev/null
+++ b/include/lber_types.hin
@@ -0,0 +1,62 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * LBER types
+ */
+
+#ifndef _LBER_TYPES_H
+#define _LBER_TYPES_H
+
+#include <ldap_cdefs.h>
+
+LDAP_BEGIN_DECL
+
+/* LBER boolean, enum, integers (32 bits or larger) */
+#undef LBER_INT_T
+
+/* LBER tags (32 bits or larger) */
+#undef LBER_TAG_T
+
+/* LBER socket descriptor */
+#undef LBER_SOCKET_T
+
+/* LBER lengths (32 bits or larger) */
+#undef LBER_LEN_T
+
+/* ------------------------------------------------------------ */
+
+/* booleans, enumerations, and integers */
+typedef LBER_INT_T ber_int_t;
+
+/* signed and unsigned versions */
+typedef signed LBER_INT_T ber_sint_t;
+typedef unsigned LBER_INT_T ber_uint_t;
+
+/* tags */
+typedef unsigned LBER_TAG_T ber_tag_t;
+
+/* "socket" descriptors */
+typedef LBER_SOCKET_T ber_socket_t;
+
+/* lengths */
+typedef unsigned LBER_LEN_T ber_len_t;
+
+/* signed lengths */
+typedef signed LBER_LEN_T ber_slen_t;
+
+LDAP_END_DECL
+
+#endif /* _LBER_TYPES_H */
diff --git a/include/ldap.h b/include/ldap.h
new file mode 100644
index 0000000..f0cce01
--- /dev/null
+++ b/include/ldap.h
@@ -0,0 +1,2817 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#ifndef _LDAP_H
+#define _LDAP_H
+
+/* pull in lber */
+#include <lber.h>
+
+/* include version and API feature defines */
+#include <ldap_features.h>
+
+LDAP_BEGIN_DECL
+
+#define LDAP_VERSION1 1
+#define LDAP_VERSION2 2
+#define LDAP_VERSION3 3
+
+#define LDAP_VERSION_MIN LDAP_VERSION2
+#define LDAP_VERSION LDAP_VERSION2
+#define LDAP_VERSION_MAX LDAP_VERSION3
+
+/*
+ * We use 3000+n here because it is above 1823 (for RFC 1823),
+ * above 2000+rev of IETF LDAPEXT draft (now quite dated),
+ * yet below allocations for new RFCs (just in case there is
+ * someday an RFC produced).
+ */
+#define LDAP_API_VERSION 3001
+#define LDAP_VENDOR_NAME "OpenLDAP"
+
+/* OpenLDAP API Features */
+#define LDAP_API_FEATURE_X_OPENLDAP LDAP_VENDOR_VERSION
+
+#if defined( LDAP_API_FEATURE_X_OPENLDAP_REENTRANT )
+# define LDAP_API_FEATURE_THREAD_SAFE 1
+#endif
+#if defined( LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE )
+# define LDAP_API_FEATURE_SESSION_THREAD_SAFE 1
+# define LDAP_API_FEATURE_OPERATION_THREAD_SAFE 1
+#endif
+
+
+#define LDAP_PORT 389 /* ldap:/// default LDAP port */
+#define LDAPS_PORT 636 /* ldaps:/// default LDAP over TLS port */
+
+#define LDAP_ROOT_DSE ""
+#define LDAP_NO_ATTRS "1.1"
+#define LDAP_ALL_USER_ATTRIBUTES "*"
+#define LDAP_ALL_OPERATIONAL_ATTRIBUTES "+" /* RFC 3673 */
+
+/* RFC 4511: maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) -- */
+#define LDAP_MAXINT (2147483647)
+
+/*
+ * LDAP_OPTions
+ * 0x0000 - 0x0fff reserved for api options
+ * 0x1000 - 0x3fff reserved for api extended options
+ * 0x4000 - 0x7fff reserved for private and experimental options
+ */
+
+#define LDAP_OPT_API_INFO 0x0000
+#define LDAP_OPT_DESC 0x0001 /* historic */
+#define LDAP_OPT_DEREF 0x0002
+#define LDAP_OPT_SIZELIMIT 0x0003
+#define LDAP_OPT_TIMELIMIT 0x0004
+/* 0x05 - 0x07 not defined */
+#define LDAP_OPT_REFERRALS 0x0008
+#define LDAP_OPT_RESTART 0x0009
+/* 0x0a - 0x10 not defined */
+#define LDAP_OPT_PROTOCOL_VERSION 0x0011
+#define LDAP_OPT_SERVER_CONTROLS 0x0012
+#define LDAP_OPT_CLIENT_CONTROLS 0x0013
+/* 0x14 not defined */
+#define LDAP_OPT_API_FEATURE_INFO 0x0015
+/* 0x16 - 0x2f not defined */
+#define LDAP_OPT_HOST_NAME 0x0030
+#define LDAP_OPT_RESULT_CODE 0x0031
+#define LDAP_OPT_ERROR_NUMBER LDAP_OPT_RESULT_CODE
+#define LDAP_OPT_DIAGNOSTIC_MESSAGE 0x0032
+#define LDAP_OPT_ERROR_STRING LDAP_OPT_DIAGNOSTIC_MESSAGE
+#define LDAP_OPT_MATCHED_DN 0x0033
+/* 0x0034 - 0x3fff not defined */
+/* 0x0091 used by Microsoft for LDAP_OPT_AUTO_RECONNECT */
+#define LDAP_OPT_SSPI_FLAGS 0x0092
+/* 0x0093 used by Microsoft for LDAP_OPT_SSL_INFO */
+/* 0x0094 used by Microsoft for LDAP_OPT_REF_DEREF_CONN_PER_MSG */
+#define LDAP_OPT_SIGN 0x0095
+#define LDAP_OPT_ENCRYPT 0x0096
+#define LDAP_OPT_SASL_METHOD 0x0097
+/* 0x0098 used by Microsoft for LDAP_OPT_AREC_EXCLUSIVE */
+#define LDAP_OPT_SECURITY_CONTEXT 0x0099
+/* 0x009A used by Microsoft for LDAP_OPT_ROOTDSE_CACHE */
+/* 0x009B - 0x3fff not defined */
+
+/* API Extensions */
+#define LDAP_OPT_API_EXTENSION_BASE 0x4000 /* API extensions */
+
+/* private and experimental options */
+/* OpenLDAP specific options */
+#define LDAP_OPT_DEBUG_LEVEL 0x5001 /* debug level */
+#define LDAP_OPT_TIMEOUT 0x5002 /* default timeout */
+#define LDAP_OPT_REFHOPLIMIT 0x5003 /* ref hop limit */
+#define LDAP_OPT_NETWORK_TIMEOUT 0x5005 /* socket level timeout */
+#define LDAP_OPT_URI 0x5006
+#define LDAP_OPT_REFERRAL_URLS 0x5007 /* Referral URLs */
+#define LDAP_OPT_SOCKBUF 0x5008 /* sockbuf */
+#define LDAP_OPT_DEFBASE 0x5009 /* searchbase */
+#define LDAP_OPT_CONNECT_ASYNC 0x5010 /* create connections asynchronously */
+#define LDAP_OPT_CONNECT_CB 0x5011 /* connection callbacks */
+#define LDAP_OPT_SESSION_REFCNT 0x5012 /* session reference count */
+#define LDAP_OPT_KEEPCONN 0x5013 /* keep the connection on read error or NoD */
+#define LDAP_OPT_SOCKET_BIND_ADDRESSES 0x5014 /* user configured bind IPs */
+#define LDAP_OPT_TCP_USER_TIMEOUT 0x5015 /* set TCP_USER_TIMEOUT if the OS supports it, ignored otherwise */
+
+/* OpenLDAP TLS options */
+#define LDAP_OPT_X_TLS 0x6000
+#define LDAP_OPT_X_TLS_CTX 0x6001 /* OpenSSL CTX* */
+#define LDAP_OPT_X_TLS_CACERTFILE 0x6002
+#define LDAP_OPT_X_TLS_CACERTDIR 0x6003
+#define LDAP_OPT_X_TLS_CERTFILE 0x6004
+#define LDAP_OPT_X_TLS_KEYFILE 0x6005
+#define LDAP_OPT_X_TLS_REQUIRE_CERT 0x6006
+#define LDAP_OPT_X_TLS_PROTOCOL_MIN 0x6007
+#define LDAP_OPT_X_TLS_CIPHER_SUITE 0x6008
+#define LDAP_OPT_X_TLS_RANDOM_FILE 0x6009
+#define LDAP_OPT_X_TLS_SSL_CTX 0x600a /* OpenSSL SSL* */
+#define LDAP_OPT_X_TLS_CRLCHECK 0x600b
+#define LDAP_OPT_X_TLS_CONNECT_CB 0x600c
+#define LDAP_OPT_X_TLS_CONNECT_ARG 0x600d
+#define LDAP_OPT_X_TLS_DHFILE 0x600e
+#define LDAP_OPT_X_TLS_NEWCTX 0x600f
+#define LDAP_OPT_X_TLS_CRLFILE 0x6010 /* GNUtls only */
+#define LDAP_OPT_X_TLS_PACKAGE 0x6011
+#define LDAP_OPT_X_TLS_ECNAME 0x6012
+#define LDAP_OPT_X_TLS_VERSION 0x6013 /* read-only */
+#define LDAP_OPT_X_TLS_CIPHER 0x6014 /* read-only */
+#define LDAP_OPT_X_TLS_PEERCERT 0x6015 /* read-only */
+#define LDAP_OPT_X_TLS_CACERT 0x6016
+#define LDAP_OPT_X_TLS_CERT 0x6017
+#define LDAP_OPT_X_TLS_KEY 0x6018
+#define LDAP_OPT_X_TLS_PEERKEY_HASH 0x6019
+#define LDAP_OPT_X_TLS_REQUIRE_SAN 0x601a
+#define LDAP_OPT_X_TLS_PROTOCOL_MAX 0x601b
+
+#define LDAP_OPT_X_TLS_NEVER 0
+#define LDAP_OPT_X_TLS_HARD 1
+#define LDAP_OPT_X_TLS_DEMAND 2
+#define LDAP_OPT_X_TLS_ALLOW 3
+#define LDAP_OPT_X_TLS_TRY 4
+
+#define LDAP_OPT_X_TLS_CRL_NONE 0
+#define LDAP_OPT_X_TLS_CRL_PEER 1
+#define LDAP_OPT_X_TLS_CRL_ALL 2
+
+/* for LDAP_OPT_X_TLS_PROTOCOL_MIN/MAX */
+#define LDAP_OPT_X_TLS_PROTOCOL(maj,min) (((maj) << 8) + (min))
+#define LDAP_OPT_X_TLS_PROTOCOL_SSL2 (2 << 8)
+#define LDAP_OPT_X_TLS_PROTOCOL_SSL3 (3 << 8)
+#define LDAP_OPT_X_TLS_PROTOCOL_TLS1_0 ((3 << 8) + 1)
+#define LDAP_OPT_X_TLS_PROTOCOL_TLS1_1 ((3 << 8) + 2)
+#define LDAP_OPT_X_TLS_PROTOCOL_TLS1_2 ((3 << 8) + 3)
+#define LDAP_OPT_X_TLS_PROTOCOL_TLS1_3 ((3 << 8) + 4)
+
+#define LDAP_OPT_X_SASL_CBINDING_NONE 0
+#define LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE 1
+#define LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT 2
+
+/* OpenLDAP SASL options */
+#define LDAP_OPT_X_SASL_MECH 0x6100
+#define LDAP_OPT_X_SASL_REALM 0x6101
+#define LDAP_OPT_X_SASL_AUTHCID 0x6102
+#define LDAP_OPT_X_SASL_AUTHZID 0x6103
+#define LDAP_OPT_X_SASL_SSF 0x6104 /* read-only */
+#define LDAP_OPT_X_SASL_SSF_EXTERNAL 0x6105 /* write-only */
+#define LDAP_OPT_X_SASL_SECPROPS 0x6106 /* write-only */
+#define LDAP_OPT_X_SASL_SSF_MIN 0x6107
+#define LDAP_OPT_X_SASL_SSF_MAX 0x6108
+#define LDAP_OPT_X_SASL_MAXBUFSIZE 0x6109
+#define LDAP_OPT_X_SASL_MECHLIST 0x610a /* read-only */
+#define LDAP_OPT_X_SASL_NOCANON 0x610b
+#define LDAP_OPT_X_SASL_USERNAME 0x610c /* read-only */
+#define LDAP_OPT_X_SASL_GSS_CREDS 0x610d
+#define LDAP_OPT_X_SASL_CBINDING 0x610e
+
+/*
+ * OpenLDAP per connection tcp-keepalive settings
+ * (Linux only, ignored where unsupported)
+ */
+#define LDAP_OPT_X_KEEPALIVE_IDLE 0x6300
+#define LDAP_OPT_X_KEEPALIVE_PROBES 0x6301
+#define LDAP_OPT_X_KEEPALIVE_INTERVAL 0x6302
+
+/* Private API Extensions -- reserved for application use */
+#define LDAP_OPT_PRIVATE_EXTENSION_BASE 0x7000 /* Private API inclusive */
+
+/*
+ * ldap_get_option() and ldap_set_option() return values.
+ * As later versions may return other values indicating
+ * failure, current applications should only compare returned
+ * value against LDAP_OPT_SUCCESS.
+ */
+#define LDAP_OPT_SUCCESS 0
+#define LDAP_OPT_ERROR (-1)
+
+/* option on/off values */
+#define LDAP_OPT_ON ((void *) &ber_pvt_opt_on)
+#define LDAP_OPT_OFF ((void *) 0)
+
+typedef struct ldapapiinfo {
+ int ldapai_info_version; /* version of LDAPAPIInfo */
+#define LDAP_API_INFO_VERSION (1)
+ int ldapai_api_version; /* revision of API supported */
+ int ldapai_protocol_version; /* highest LDAP version supported */
+ char **ldapai_extensions; /* names of API extensions */
+ char *ldapai_vendor_name; /* name of supplier */
+ int ldapai_vendor_version; /* supplier-specific version * 100 */
+} LDAPAPIInfo;
+
+typedef struct ldap_apifeature_info {
+ int ldapaif_info_version; /* version of LDAPAPIFeatureInfo */
+#define LDAP_FEATURE_INFO_VERSION (1) /* apifeature_info struct version */
+ char* ldapaif_name; /* LDAP_API_FEATURE_* (less prefix) */
+ int ldapaif_version; /* value of LDAP_API_FEATURE_... */
+} LDAPAPIFeatureInfo;
+
+/*
+ * LDAP Control structure
+ */
+typedef struct ldapcontrol {
+ char * ldctl_oid; /* numericoid of control */
+ struct berval ldctl_value; /* encoded value of control */
+ char ldctl_iscritical; /* criticality */
+} LDAPControl;
+
+/* LDAP Controls */
+/* standard track controls */
+#define LDAP_CONTROL_MANAGEDSAIT "2.16.840.1.113730.3.4.2" /* RFC 3296 */
+#define LDAP_CONTROL_PROXY_AUTHZ "2.16.840.1.113730.3.4.18" /* RFC 4370 */
+#define LDAP_CONTROL_SUBENTRIES "1.3.6.1.4.1.4203.1.10.1" /* RFC 3672 */
+
+#define LDAP_CONTROL_VALUESRETURNFILTER "1.2.826.0.1.3344810.2.3"/* RFC 3876 */
+
+#define LDAP_CONTROL_ASSERT "1.3.6.1.1.12" /* RFC 4528 */
+#define LDAP_CONTROL_PRE_READ "1.3.6.1.1.13.1" /* RFC 4527 */
+#define LDAP_CONTROL_POST_READ "1.3.6.1.1.13.2" /* RFC 4527 */
+
+#define LDAP_CONTROL_SORTREQUEST "1.2.840.113556.1.4.473" /* RFC 2891 */
+#define LDAP_CONTROL_SORTRESPONSE "1.2.840.113556.1.4.474" /* RFC 2891 */
+
+/* non-standard track controls */
+#define LDAP_CONTROL_PAGEDRESULTS "1.2.840.113556.1.4.319" /* RFC 2696 */
+
+#define LDAP_CONTROL_AUTHZID_REQUEST "2.16.840.1.113730.3.4.16" /* RFC 3829 */
+#define LDAP_CONTROL_AUTHZID_RESPONSE "2.16.840.1.113730.3.4.15" /* RFC 3829 */
+
+/* LDAP Content Synchronization Operation -- RFC 4533 */
+#define LDAP_SYNC_OID "1.3.6.1.4.1.4203.1.9.1"
+#define LDAP_CONTROL_SYNC LDAP_SYNC_OID ".1"
+#define LDAP_CONTROL_SYNC_STATE LDAP_SYNC_OID ".2"
+#define LDAP_CONTROL_SYNC_DONE LDAP_SYNC_OID ".3"
+#define LDAP_SYNC_INFO LDAP_SYNC_OID ".4"
+
+#define LDAP_SYNC_NONE 0x00
+#define LDAP_SYNC_REFRESH_ONLY 0x01
+#define LDAP_SYNC_RESERVED 0x02
+#define LDAP_SYNC_REFRESH_AND_PERSIST 0x03
+
+#define LDAP_SYNC_REFRESH_PRESENTS 0
+#define LDAP_SYNC_REFRESH_DELETES 1
+
+#define LDAP_TAG_SYNC_NEW_COOKIE ((ber_tag_t) 0x80U)
+#define LDAP_TAG_SYNC_REFRESH_DELETE ((ber_tag_t) 0xa1U)
+#define LDAP_TAG_SYNC_REFRESH_PRESENT ((ber_tag_t) 0xa2U)
+#define LDAP_TAG_SYNC_ID_SET ((ber_tag_t) 0xa3U)
+
+#define LDAP_TAG_SYNC_COOKIE ((ber_tag_t) 0x04U)
+#define LDAP_TAG_REFRESHDELETES ((ber_tag_t) 0x01U)
+#define LDAP_TAG_REFRESHDONE ((ber_tag_t) 0x01U)
+#define LDAP_TAG_RELOAD_HINT ((ber_tag_t) 0x01U)
+
+#define LDAP_SYNC_PRESENT 0
+#define LDAP_SYNC_ADD 1
+#define LDAP_SYNC_MODIFY 2
+#define LDAP_SYNC_DELETE 3
+#define LDAP_SYNC_NEW_COOKIE 4
+
+/* LDAP Don't Use Copy Control (RFC 6171) */
+#define LDAP_CONTROL_DONTUSECOPY "1.3.6.1.1.22"
+
+/* Password policy Controls *//* work in progress */
+/* ITS#3458: released; disabled by default */
+#define LDAP_CONTROL_PASSWORDPOLICYREQUEST "1.3.6.1.4.1.42.2.27.8.5.1"
+#define LDAP_CONTROL_PASSWORDPOLICYRESPONSE "1.3.6.1.4.1.42.2.27.8.5.1"
+
+/* various works in progress */
+#define LDAP_CONTROL_NOOP "1.3.6.1.4.1.4203.666.5.2"
+#define LDAP_CONTROL_NO_SUBORDINATES "1.3.6.1.4.1.4203.666.5.11"
+#define LDAP_CONTROL_RELAX "1.3.6.1.4.1.4203.666.5.12"
+#define LDAP_CONTROL_MANAGEDIT LDAP_CONTROL_RELAX
+#define LDAP_CONTROL_SLURP "1.3.6.1.4.1.4203.666.5.13"
+#define LDAP_CONTROL_VALSORT "1.3.6.1.4.1.4203.666.5.14"
+#define LDAP_CONTROL_X_DEREF "1.3.6.1.4.1.4203.666.5.16"
+#define LDAP_CONTROL_X_WHATFAILED "1.3.6.1.4.1.4203.666.5.17"
+
+/* LDAP Chaining Behavior Control *//* work in progress */
+/* <draft-sermersheim-ldap-chaining>;
+ * see also LDAP_NO_REFERRALS_FOUND, LDAP_CANNOT_CHAIN */
+#define LDAP_CONTROL_X_CHAINING_BEHAVIOR "1.3.6.1.4.1.4203.666.11.3"
+
+#define LDAP_CHAINING_PREFERRED 0
+#define LDAP_CHAINING_REQUIRED 1
+#define LDAP_REFERRALS_PREFERRED 2
+#define LDAP_REFERRALS_REQUIRED 3
+
+/* MS Active Directory controls (for compatibility) */
+#define LDAP_CONTROL_X_LAZY_COMMIT "1.2.840.113556.1.4.619"
+#define LDAP_CONTROL_X_INCREMENTAL_VALUES "1.2.840.113556.1.4.802"
+#define LDAP_CONTROL_X_DOMAIN_SCOPE "1.2.840.113556.1.4.1339"
+#define LDAP_CONTROL_X_PERMISSIVE_MODIFY "1.2.840.113556.1.4.1413"
+#define LDAP_CONTROL_X_SEARCH_OPTIONS "1.2.840.113556.1.4.1340"
+#define LDAP_SEARCH_FLAG_DOMAIN_SCOPE 1 /* do not generate referrals */
+#define LDAP_SEARCH_FLAG_PHANTOM_ROOT 2 /* search all subordinate NCs */
+#define LDAP_CONTROL_X_TREE_DELETE "1.2.840.113556.1.4.805"
+
+/* MS Active Directory controls - not implemented in slapd(8) */
+#define LDAP_CONTROL_X_SERVER_NOTIFICATION "1.2.840.113556.1.4.528"
+#define LDAP_CONTROL_X_EXTENDED_DN "1.2.840.113556.1.4.529"
+#define LDAP_CONTROL_X_SHOW_DELETED "1.2.840.113556.1.4.417"
+#define LDAP_CONTROL_X_DIRSYNC "1.2.840.113556.1.4.841"
+
+#define LDAP_CONTROL_X_DIRSYNC_OBJECT_SECURITY 0x00000001
+#define LDAP_CONTROL_X_DIRSYNC_ANCESTORS_FIRST 0x00000800
+#define LDAP_CONTROL_X_DIRSYNC_PUBLIC_DATA_ONLY 0x00002000
+#define LDAP_CONTROL_X_DIRSYNC_INCREMENTAL_VALUES 0x80000000
+
+
+/* <draft-wahl-ldap-session> */
+#define LDAP_CONTROL_X_SESSION_TRACKING "1.3.6.1.4.1.21008.108.63.1"
+#define LDAP_CONTROL_X_SESSION_TRACKING_RADIUS_ACCT_SESSION_ID \
+ LDAP_CONTROL_X_SESSION_TRACKING ".1"
+#define LDAP_CONTROL_X_SESSION_TRACKING_RADIUS_ACCT_MULTI_SESSION_ID \
+ LDAP_CONTROL_X_SESSION_TRACKING ".2"
+#define LDAP_CONTROL_X_SESSION_TRACKING_USERNAME \
+ LDAP_CONTROL_X_SESSION_TRACKING ".3"
+/* various expired works */
+
+/* LDAP Duplicated Entry Control Extension *//* not implemented in slapd(8) */
+#define LDAP_CONTROL_DUPENT_REQUEST "2.16.840.1.113719.1.27.101.1"
+#define LDAP_CONTROL_DUPENT_RESPONSE "2.16.840.1.113719.1.27.101.2"
+#define LDAP_CONTROL_DUPENT_ENTRY "2.16.840.1.113719.1.27.101.3"
+#define LDAP_CONTROL_DUPENT LDAP_CONTROL_DUPENT_REQUEST
+
+/* LDAP Persistent Search Control *//* not implemented in slapd(8) */
+#define LDAP_CONTROL_PERSIST_REQUEST "2.16.840.1.113730.3.4.3"
+#define LDAP_CONTROL_PERSIST_ENTRY_CHANGE_NOTICE "2.16.840.1.113730.3.4.7"
+#define LDAP_CONTROL_PERSIST_ENTRY_CHANGE_ADD 0x1
+#define LDAP_CONTROL_PERSIST_ENTRY_CHANGE_DELETE 0x2
+#define LDAP_CONTROL_PERSIST_ENTRY_CHANGE_MODIFY 0x4
+#define LDAP_CONTROL_PERSIST_ENTRY_CHANGE_RENAME 0x8
+
+/* LDAP VLV */
+#define LDAP_CONTROL_VLVREQUEST "2.16.840.1.113730.3.4.9"
+#define LDAP_CONTROL_VLVRESPONSE "2.16.840.1.113730.3.4.10"
+
+/* Sun's analogue to ppolicy */
+#define LDAP_CONTROL_X_ACCOUNT_USABILITY "1.3.6.1.4.1.42.2.27.9.5.8"
+
+#define LDAP_TAG_X_ACCOUNT_USABILITY_AVAILABLE ((ber_tag_t) 0x80U) /* primitive + 0 */
+#define LDAP_TAG_X_ACCOUNT_USABILITY_NOT_AVAILABLE ((ber_tag_t) 0xA1U) /* constructed + 1 */
+
+#define LDAP_TAG_X_ACCOUNT_USABILITY_INACTIVE ((ber_tag_t) 0x80U) /* primitive + 0 */
+#define LDAP_TAG_X_ACCOUNT_USABILITY_RESET ((ber_tag_t) 0x81U) /* primitive + 1 */
+#define LDAP_TAG_X_ACCOUNT_USABILITY_EXPIRED ((ber_tag_t) 0x82U) /* primitive + 2 */
+#define LDAP_TAG_X_ACCOUNT_USABILITY_REMAINING_GRACE ((ber_tag_t) 0x83U) /* primitive + 3 */
+#define LDAP_TAG_X_ACCOUNT_USABILITY_UNTIL_UNLOCK ((ber_tag_t) 0x84U) /* primitive + 4 */
+
+/* Netscape Password policy response controls */
+/* <draft-vchu-ldap-pwd-policy> */
+#define LDAP_CONTROL_X_PASSWORD_EXPIRED "2.16.840.1.113730.3.4.4"
+#define LDAP_CONTROL_X_PASSWORD_EXPIRING "2.16.840.1.113730.3.4.5"
+
+/* LDAP Unsolicited Notifications */
+#define LDAP_NOTICE_OF_DISCONNECTION "1.3.6.1.4.1.1466.20036" /* RFC 4511 */
+#define LDAP_NOTICE_DISCONNECT LDAP_NOTICE_OF_DISCONNECTION
+
+/* LDAP Extended Operations */
+#define LDAP_EXOP_START_TLS "1.3.6.1.4.1.1466.20037" /* RFC 4511 */
+
+#define LDAP_EXOP_MODIFY_PASSWD "1.3.6.1.4.1.4203.1.11.1" /* RFC 3062 */
+#define LDAP_TAG_EXOP_MODIFY_PASSWD_ID ((ber_tag_t) 0x80U)
+#define LDAP_TAG_EXOP_MODIFY_PASSWD_OLD ((ber_tag_t) 0x81U)
+#define LDAP_TAG_EXOP_MODIFY_PASSWD_NEW ((ber_tag_t) 0x82U)
+#define LDAP_TAG_EXOP_MODIFY_PASSWD_GEN ((ber_tag_t) 0x80U)
+
+#define LDAP_EXOP_CANCEL "1.3.6.1.1.8" /* RFC 3909 */
+#define LDAP_EXOP_X_CANCEL LDAP_EXOP_CANCEL
+
+#define LDAP_EXOP_REFRESH "1.3.6.1.4.1.1466.101.119.1" /* RFC 2589 */
+#define LDAP_TAG_EXOP_REFRESH_REQ_DN ((ber_tag_t) 0x80U)
+#define LDAP_TAG_EXOP_REFRESH_REQ_TTL ((ber_tag_t) 0x81U)
+#define LDAP_TAG_EXOP_REFRESH_RES_TTL ((ber_tag_t) 0x81U)
+
+#define LDAP_EXOP_VERIFY_CREDENTIALS "1.3.6.1.4.1.4203.666.6.5"
+#define LDAP_EXOP_X_VERIFY_CREDENTIALS LDAP_EXOP_VERIFY_CREDENTIALS
+
+#define LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE ((ber_tag_t) 0x80U)
+#define LDAP_TAG_EXOP_VERIFY_CREDENTIALS_SCREDS ((ber_tag_t) 0x81U)
+#define LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS ((ber_tag_t) 0xa2U) /* context specific + constructed + 2 */
+
+#define LDAP_EXOP_WHO_AM_I "1.3.6.1.4.1.4203.1.11.3" /* RFC 4532 */
+#define LDAP_EXOP_X_WHO_AM_I LDAP_EXOP_WHO_AM_I
+
+/* various works in progress */
+#define LDAP_EXOP_TURN "1.3.6.1.1.19" /* RFC 4531 */
+#define LDAP_EXOP_X_TURN LDAP_EXOP_TURN
+
+/* LDAP Distributed Procedures <draft-sermersheim-ldap-distproc> */
+/* a work in progress */
+#define LDAP_X_DISTPROC_BASE "1.3.6.1.4.1.4203.666.11.6"
+#define LDAP_EXOP_X_CHAINEDREQUEST LDAP_X_DISTPROC_BASE ".1"
+#define LDAP_FEATURE_X_CANCHAINOPS LDAP_X_DISTPROC_BASE ".2"
+#define LDAP_CONTROL_X_RETURNCONTREF LDAP_X_DISTPROC_BASE ".3"
+#define LDAP_URLEXT_X_LOCALREFOID LDAP_X_DISTPROC_BASE ".4"
+#define LDAP_URLEXT_X_REFTYPEOID LDAP_X_DISTPROC_BASE ".5"
+#define LDAP_URLEXT_X_SEARCHEDSUBTREEOID \
+ LDAP_X_DISTPROC_BASE ".6"
+#define LDAP_URLEXT_X_FAILEDNAMEOID LDAP_X_DISTPROC_BASE ".7"
+#define LDAP_URLEXT_X_LOCALREF "x-localReference"
+#define LDAP_URLEXT_X_REFTYPE "x-referenceType"
+#define LDAP_URLEXT_X_SEARCHEDSUBTREE "x-searchedSubtree"
+#define LDAP_URLEXT_X_FAILEDNAME "x-failedName"
+
+#define LDAP_TXN "1.3.6.1.1.21" /* RFC 5805 */
+#define LDAP_EXOP_TXN_START LDAP_TXN ".1"
+#define LDAP_CONTROL_TXN_SPEC LDAP_TXN ".2"
+#define LDAP_EXOP_TXN_END LDAP_TXN ".3"
+#define LDAP_EXOP_TXN_ABORTED_NOTICE LDAP_TXN ".4"
+
+/* LDAP Features */
+#define LDAP_FEATURE_ALL_OP_ATTRS "1.3.6.1.4.1.4203.1.5.1" /* RFC 3673 */
+#define LDAP_FEATURE_OBJECTCLASS_ATTRS \
+ "1.3.6.1.4.1.4203.1.5.2" /* @objectClass - new number to be assigned */
+#define LDAP_FEATURE_ABSOLUTE_FILTERS "1.3.6.1.4.1.4203.1.5.3" /* (&) (|) */
+#define LDAP_FEATURE_LANGUAGE_TAG_OPTIONS "1.3.6.1.4.1.4203.1.5.4"
+#define LDAP_FEATURE_LANGUAGE_RANGE_OPTIONS "1.3.6.1.4.1.4203.1.5.5"
+#define LDAP_FEATURE_MODIFY_INCREMENT "1.3.6.1.1.14"
+
+/* LDAP Experimental (works in progress) Features */
+#define LDAP_FEATURE_SUBORDINATE_SCOPE \
+ "1.3.6.1.4.1.4203.666.8.1" /* "children" */
+#define LDAP_FEATURE_CHILDREN_SCOPE LDAP_FEATURE_SUBORDINATE_SCOPE
+
+/*
+ * specific LDAP instantiations of BER types we know about
+ */
+
+/* Overview of LBER tag construction
+ *
+ * Bits
+ * ______
+ * 8 7 | CLASS
+ * 0 0 = UNIVERSAL
+ * 0 1 = APPLICATION
+ * 1 0 = CONTEXT-SPECIFIC
+ * 1 1 = PRIVATE
+ * _____
+ * | 6 | DATA-TYPE
+ * 0 = PRIMITIVE
+ * 1 = CONSTRUCTED
+ * ___________
+ * | 5 ... 1 | TAG-NUMBER
+ */
+
+/* general stuff */
+#define LDAP_TAG_MESSAGE ((ber_tag_t) 0x30U) /* constructed + 16 */
+#define LDAP_TAG_MSGID ((ber_tag_t) 0x02U) /* integer */
+
+#define LDAP_TAG_LDAPDN ((ber_tag_t) 0x04U) /* octet string */
+#define LDAP_TAG_LDAPCRED ((ber_tag_t) 0x04U) /* octet string */
+
+#define LDAP_TAG_CONTROLS ((ber_tag_t) 0xa0U) /* context specific + constructed + 0 */
+#define LDAP_TAG_REFERRAL ((ber_tag_t) 0xa3U) /* context specific + constructed + 3 */
+
+#define LDAP_TAG_NEWSUPERIOR ((ber_tag_t) 0x80U) /* context-specific + primitive + 0 */
+
+#define LDAP_TAG_EXOP_REQ_OID ((ber_tag_t) 0x80U) /* context specific + primitive */
+#define LDAP_TAG_EXOP_REQ_VALUE ((ber_tag_t) 0x81U) /* context specific + primitive */
+#define LDAP_TAG_EXOP_RES_OID ((ber_tag_t) 0x8aU) /* context specific + primitive */
+#define LDAP_TAG_EXOP_RES_VALUE ((ber_tag_t) 0x8bU) /* context specific + primitive */
+
+#define LDAP_TAG_IM_RES_OID ((ber_tag_t) 0x80U) /* context specific + primitive */
+#define LDAP_TAG_IM_RES_VALUE ((ber_tag_t) 0x81U) /* context specific + primitive */
+
+#define LDAP_TAG_SASL_RES_CREDS ((ber_tag_t) 0x87U) /* context specific + primitive */
+
+/* LDAP Request Messages */
+#define LDAP_REQ_BIND ((ber_tag_t) 0x60U) /* application + constructed */
+#define LDAP_REQ_UNBIND ((ber_tag_t) 0x42U) /* application + primitive */
+#define LDAP_REQ_SEARCH ((ber_tag_t) 0x63U) /* application + constructed */
+#define LDAP_REQ_MODIFY ((ber_tag_t) 0x66U) /* application + constructed */
+#define LDAP_REQ_ADD ((ber_tag_t) 0x68U) /* application + constructed */
+#define LDAP_REQ_DELETE ((ber_tag_t) 0x4aU) /* application + primitive */
+#define LDAP_REQ_MODDN ((ber_tag_t) 0x6cU) /* application + constructed */
+#define LDAP_REQ_MODRDN LDAP_REQ_MODDN
+#define LDAP_REQ_RENAME LDAP_REQ_MODDN
+#define LDAP_REQ_COMPARE ((ber_tag_t) 0x6eU) /* application + constructed */
+#define LDAP_REQ_ABANDON ((ber_tag_t) 0x50U) /* application + primitive */
+#define LDAP_REQ_EXTENDED ((ber_tag_t) 0x77U) /* application + constructed */
+
+/* LDAP Response Messages */
+#define LDAP_RES_BIND ((ber_tag_t) 0x61U) /* application + constructed */
+#define LDAP_RES_SEARCH_ENTRY ((ber_tag_t) 0x64U) /* application + constructed */
+#define LDAP_RES_SEARCH_REFERENCE ((ber_tag_t) 0x73U) /* V3: application + constructed */
+#define LDAP_RES_SEARCH_RESULT ((ber_tag_t) 0x65U) /* application + constructed */
+#define LDAP_RES_MODIFY ((ber_tag_t) 0x67U) /* application + constructed */
+#define LDAP_RES_ADD ((ber_tag_t) 0x69U) /* application + constructed */
+#define LDAP_RES_DELETE ((ber_tag_t) 0x6bU) /* application + constructed */
+#define LDAP_RES_MODDN ((ber_tag_t) 0x6dU) /* application + constructed */
+#define LDAP_RES_MODRDN LDAP_RES_MODDN /* application + constructed */
+#define LDAP_RES_RENAME LDAP_RES_MODDN /* application + constructed */
+#define LDAP_RES_COMPARE ((ber_tag_t) 0x6fU) /* application + constructed */
+#define LDAP_RES_EXTENDED ((ber_tag_t) 0x78U) /* V3: application + constructed */
+#define LDAP_RES_INTERMEDIATE ((ber_tag_t) 0x79U) /* V3+: application + constructed */
+
+#define LDAP_RES_ANY (-1)
+#define LDAP_RES_UNSOLICITED (0)
+
+
+/* sasl methods */
+#define LDAP_SASL_SIMPLE ((char*)0)
+#define LDAP_SASL_NULL ("")
+
+
+/* authentication methods available */
+#define LDAP_AUTH_NONE ((ber_tag_t) 0x00U) /* no authentication */
+#define LDAP_AUTH_SIMPLE ((ber_tag_t) 0x80U) /* context specific + primitive */
+#define LDAP_AUTH_SASL ((ber_tag_t) 0xa3U) /* context specific + constructed */
+#define LDAP_AUTH_KRBV4 ((ber_tag_t) 0xffU) /* means do both of the following */
+#define LDAP_AUTH_KRBV41 ((ber_tag_t) 0x81U) /* context specific + primitive */
+#define LDAP_AUTH_KRBV42 ((ber_tag_t) 0x82U) /* context specific + primitive */
+
+/* used by the Windows API but not used on the wire */
+#define LDAP_AUTH_NEGOTIATE ((ber_tag_t) 0x04FFU)
+
+/* filter types */
+#define LDAP_FILTER_AND ((ber_tag_t) 0xa0U) /* context specific + constructed */
+#define LDAP_FILTER_OR ((ber_tag_t) 0xa1U) /* context specific + constructed */
+#define LDAP_FILTER_NOT ((ber_tag_t) 0xa2U) /* context specific + constructed */
+#define LDAP_FILTER_EQUALITY ((ber_tag_t) 0xa3U) /* context specific + constructed */
+#define LDAP_FILTER_SUBSTRINGS ((ber_tag_t) 0xa4U) /* context specific + constructed */
+#define LDAP_FILTER_GE ((ber_tag_t) 0xa5U) /* context specific + constructed */
+#define LDAP_FILTER_LE ((ber_tag_t) 0xa6U) /* context specific + constructed */
+#define LDAP_FILTER_PRESENT ((ber_tag_t) 0x87U) /* context specific + primitive */
+#define LDAP_FILTER_APPROX ((ber_tag_t) 0xa8U) /* context specific + constructed */
+#define LDAP_FILTER_EXT ((ber_tag_t) 0xa9U) /* context specific + constructed */
+
+/* extended filter component types */
+#define LDAP_FILTER_EXT_OID ((ber_tag_t) 0x81U) /* context specific */
+#define LDAP_FILTER_EXT_TYPE ((ber_tag_t) 0x82U) /* context specific */
+#define LDAP_FILTER_EXT_VALUE ((ber_tag_t) 0x83U) /* context specific */
+#define LDAP_FILTER_EXT_DNATTRS ((ber_tag_t) 0x84U) /* context specific */
+
+/* substring filter component types */
+#define LDAP_SUBSTRING_INITIAL ((ber_tag_t) 0x80U) /* context specific */
+#define LDAP_SUBSTRING_ANY ((ber_tag_t) 0x81U) /* context specific */
+#define LDAP_SUBSTRING_FINAL ((ber_tag_t) 0x82U) /* context specific */
+
+/* search scopes */
+#define LDAP_SCOPE_BASE ((ber_int_t) 0x0000)
+#define LDAP_SCOPE_BASEOBJECT LDAP_SCOPE_BASE
+#define LDAP_SCOPE_ONELEVEL ((ber_int_t) 0x0001)
+#define LDAP_SCOPE_ONE LDAP_SCOPE_ONELEVEL
+#define LDAP_SCOPE_SUBTREE ((ber_int_t) 0x0002)
+#define LDAP_SCOPE_SUB LDAP_SCOPE_SUBTREE
+#define LDAP_SCOPE_SUBORDINATE ((ber_int_t) 0x0003) /* OpenLDAP extension */
+#define LDAP_SCOPE_CHILDREN LDAP_SCOPE_SUBORDINATE
+#define LDAP_SCOPE_DEFAULT ((ber_int_t) -1) /* OpenLDAP extension */
+
+/* substring filter component types */
+#define LDAP_SUBSTRING_INITIAL ((ber_tag_t) 0x80U) /* context specific */
+#define LDAP_SUBSTRING_ANY ((ber_tag_t) 0x81U) /* context specific */
+#define LDAP_SUBSTRING_FINAL ((ber_tag_t) 0x82U) /* context specific */
+
+/*
+ * LDAP Result Codes
+ */
+#define LDAP_SUCCESS 0x00
+
+#define LDAP_RANGE(n,x,y) (((x) <= (n)) && ((n) <= (y)))
+
+#define LDAP_OPERATIONS_ERROR 0x01
+#define LDAP_PROTOCOL_ERROR 0x02
+#define LDAP_TIMELIMIT_EXCEEDED 0x03
+#define LDAP_SIZELIMIT_EXCEEDED 0x04
+#define LDAP_COMPARE_FALSE 0x05
+#define LDAP_COMPARE_TRUE 0x06
+#define LDAP_AUTH_METHOD_NOT_SUPPORTED 0x07
+#define LDAP_STRONG_AUTH_NOT_SUPPORTED LDAP_AUTH_METHOD_NOT_SUPPORTED
+#define LDAP_STRONG_AUTH_REQUIRED 0x08
+#define LDAP_STRONGER_AUTH_REQUIRED LDAP_STRONG_AUTH_REQUIRED
+#define LDAP_PARTIAL_RESULTS 0x09 /* LDAPv2+ (not LDAPv3) */
+
+#define LDAP_REFERRAL 0x0a /* LDAPv3 */
+#define LDAP_ADMINLIMIT_EXCEEDED 0x0b /* LDAPv3 */
+#define LDAP_UNAVAILABLE_CRITICAL_EXTENSION 0x0c /* LDAPv3 */
+#define LDAP_CONFIDENTIALITY_REQUIRED 0x0d /* LDAPv3 */
+#define LDAP_SASL_BIND_IN_PROGRESS 0x0e /* LDAPv3 */
+
+#define LDAP_ATTR_ERROR(n) LDAP_RANGE((n),0x10,0x15) /* 16-21 */
+
+#define LDAP_NO_SUCH_ATTRIBUTE 0x10
+#define LDAP_UNDEFINED_TYPE 0x11
+#define LDAP_INAPPROPRIATE_MATCHING 0x12
+#define LDAP_CONSTRAINT_VIOLATION 0x13
+#define LDAP_TYPE_OR_VALUE_EXISTS 0x14
+#define LDAP_INVALID_SYNTAX 0x15
+
+#define LDAP_NAME_ERROR(n) LDAP_RANGE((n),0x20,0x24) /* 32-34,36 */
+
+#define LDAP_NO_SUCH_OBJECT 0x20
+#define LDAP_ALIAS_PROBLEM 0x21
+#define LDAP_INVALID_DN_SYNTAX 0x22
+#define LDAP_IS_LEAF 0x23 /* not LDAPv3 */
+#define LDAP_ALIAS_DEREF_PROBLEM 0x24
+
+#define LDAP_SECURITY_ERROR(n) LDAP_RANGE((n),0x2F,0x32) /* 47-50 */
+
+#define LDAP_X_PROXY_AUTHZ_FAILURE 0x2F /* LDAPv3 proxy authorization */
+#define LDAP_INAPPROPRIATE_AUTH 0x30
+#define LDAP_INVALID_CREDENTIALS 0x31
+#define LDAP_INSUFFICIENT_ACCESS 0x32
+
+#define LDAP_SERVICE_ERROR(n) LDAP_RANGE((n),0x33,0x36) /* 51-54 */
+
+#define LDAP_BUSY 0x33
+#define LDAP_UNAVAILABLE 0x34
+#define LDAP_UNWILLING_TO_PERFORM 0x35
+#define LDAP_LOOP_DETECT 0x36
+
+#define LDAP_UPDATE_ERROR(n) LDAP_RANGE((n),0x40,0x47) /* 64-69,71 */
+
+#define LDAP_NAMING_VIOLATION 0x40
+#define LDAP_OBJECT_CLASS_VIOLATION 0x41
+#define LDAP_NOT_ALLOWED_ON_NONLEAF 0x42
+#define LDAP_NOT_ALLOWED_ON_RDN 0x43
+#define LDAP_ALREADY_EXISTS 0x44
+#define LDAP_NO_OBJECT_CLASS_MODS 0x45
+#define LDAP_RESULTS_TOO_LARGE 0x46 /* CLDAP */
+#define LDAP_AFFECTS_MULTIPLE_DSAS 0x47
+
+#define LDAP_VLV_ERROR 0x4C
+
+#define LDAP_OTHER 0x50
+
+/* LCUP operation codes (113-117) - not implemented */
+#define LDAP_CUP_RESOURCES_EXHAUSTED 0x71
+#define LDAP_CUP_SECURITY_VIOLATION 0x72
+#define LDAP_CUP_INVALID_DATA 0x73
+#define LDAP_CUP_UNSUPPORTED_SCHEME 0x74
+#define LDAP_CUP_RELOAD_REQUIRED 0x75
+
+/* Cancel operation codes (118-121) */
+#define LDAP_CANCELLED 0x76
+#define LDAP_NO_SUCH_OPERATION 0x77
+#define LDAP_TOO_LATE 0x78
+#define LDAP_CANNOT_CANCEL 0x79
+
+/* Assertion control (122) */
+#define LDAP_ASSERTION_FAILED 0x7A
+
+/* Proxied Authorization Denied (123) */
+#define LDAP_PROXIED_AUTHORIZATION_DENIED 0x7B
+
+/* Experimental result codes */
+#define LDAP_E_ERROR(n) LDAP_RANGE((n),0x1000,0x3FFF)
+
+/* LDAP Sync (4096) */
+#define LDAP_SYNC_REFRESH_REQUIRED 0x1000
+
+
+/* Private Use result codes */
+#define LDAP_X_ERROR(n) LDAP_RANGE((n),0x4000,0xFFFF)
+
+#define LDAP_X_SYNC_REFRESH_REQUIRED 0x4100 /* defunct */
+#define LDAP_X_ASSERTION_FAILED 0x410f /* defunct */
+
+/* for the LDAP No-Op control */
+#define LDAP_X_NO_OPERATION 0x410e
+
+/* for the Chaining Behavior control (consecutive result codes requested;
+ * see <draft-sermersheim-ldap-chaining> ) */
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+#define LDAP_X_NO_REFERRALS_FOUND 0x4110
+#define LDAP_X_CANNOT_CHAIN 0x4111
+#endif
+
+/* for Distributed Procedures (see <draft-sermersheim-ldap-distproc>) */
+#ifdef LDAP_X_DISTPROC_BASE
+#define LDAP_X_INVALIDREFERENCE 0x4112
+#endif
+
+#define LDAP_TXN_SPECIFY_OKAY 0x4120
+#define LDAP_TXN_ID_INVALID 0x4121
+
+/* API Error Codes
+ *
+ * Based on draft-ietf-ldap-c-api-xx
+ * but with new negative code values
+ */
+#define LDAP_API_ERROR(n) ((n)<0)
+#define LDAP_API_RESULT(n) ((n)<=0)
+
+#define LDAP_SERVER_DOWN (-1)
+#define LDAP_LOCAL_ERROR (-2)
+#define LDAP_ENCODING_ERROR (-3)
+#define LDAP_DECODING_ERROR (-4)
+#define LDAP_TIMEOUT (-5)
+#define LDAP_AUTH_UNKNOWN (-6)
+#define LDAP_FILTER_ERROR (-7)
+#define LDAP_USER_CANCELLED (-8)
+#define LDAP_PARAM_ERROR (-9)
+#define LDAP_NO_MEMORY (-10)
+#define LDAP_CONNECT_ERROR (-11)
+#define LDAP_NOT_SUPPORTED (-12)
+#define LDAP_CONTROL_NOT_FOUND (-13)
+#define LDAP_NO_RESULTS_RETURNED (-14)
+#define LDAP_MORE_RESULTS_TO_RETURN (-15) /* Obsolete */
+#define LDAP_CLIENT_LOOP (-16)
+#define LDAP_REFERRAL_LIMIT_EXCEEDED (-17)
+#define LDAP_X_CONNECTING (-18)
+
+
+/*
+ * This structure represents both ldap messages and ldap responses.
+ * These are really the same, except in the case of search responses,
+ * where a response has multiple messages.
+ */
+
+typedef struct ldapmsg LDAPMessage;
+
+/* for modifications */
+typedef struct ldapmod {
+ int mod_op;
+
+#define LDAP_MOD_OP (0x0007)
+#define LDAP_MOD_ADD (0x0000)
+#define LDAP_MOD_DELETE (0x0001)
+#define LDAP_MOD_REPLACE (0x0002)
+#define LDAP_MOD_INCREMENT (0x0003) /* OpenLDAP extension */
+#define LDAP_MOD_BVALUES (0x0080)
+/* IMPORTANT: do not use code 0x1000 (or above),
+ * it is used internally by the backends!
+ * (see ldap/servers/slapd/slap.h)
+ */
+
+ char *mod_type;
+ union mod_vals_u {
+ char **modv_strvals;
+ struct berval **modv_bvals;
+ } mod_vals;
+#define mod_values mod_vals.modv_strvals
+#define mod_bvalues mod_vals.modv_bvals
+} LDAPMod;
+
+/*
+ * structure representing an ldap session which can
+ * encompass connections to multiple servers (in the
+ * face of referrals).
+ */
+typedef struct ldap LDAP;
+
+#define LDAP_DEREF_NEVER 0x00
+#define LDAP_DEREF_SEARCHING 0x01
+#define LDAP_DEREF_FINDING 0x02
+#define LDAP_DEREF_ALWAYS 0x03
+
+#define LDAP_NO_LIMIT 0
+
+/* how many messages to retrieve results for */
+#define LDAP_MSG_ONE 0x00
+#define LDAP_MSG_ALL 0x01
+#define LDAP_MSG_RECEIVED 0x02
+
+/*
+ * types for ldap URL handling
+ */
+typedef struct ldap_url_desc {
+ struct ldap_url_desc *lud_next;
+ char *lud_scheme;
+ char *lud_host;
+ int lud_port;
+ char *lud_dn;
+ char **lud_attrs;
+ int lud_scope;
+ char *lud_filter;
+ char **lud_exts;
+ int lud_crit_exts;
+} LDAPURLDesc;
+
+#define LDAP_URL_SUCCESS 0x00 /* Success */
+#define LDAP_URL_ERR_MEM 0x01 /* can't allocate memory space */
+#define LDAP_URL_ERR_PARAM 0x02 /* parameter is bad */
+
+#define LDAP_URL_ERR_BADSCHEME 0x03 /* URL doesn't begin with "ldap[si]://" */
+#define LDAP_URL_ERR_BADENCLOSURE 0x04 /* URL is missing trailing ">" */
+#define LDAP_URL_ERR_BADURL 0x05 /* URL is bad */
+#define LDAP_URL_ERR_BADHOST 0x06 /* host port is bad */
+#define LDAP_URL_ERR_BADATTRS 0x07 /* bad (or missing) attributes */
+#define LDAP_URL_ERR_BADSCOPE 0x08 /* scope string is invalid (or missing) */
+#define LDAP_URL_ERR_BADFILTER 0x09 /* bad or missing filter */
+#define LDAP_URL_ERR_BADEXTS 0x0a /* bad or missing extensions */
+
+/*
+ * LDAP sync (RFC4533) API
+ */
+
+typedef struct ldap_sync_t ldap_sync_t;
+
+typedef enum {
+ /* these are private - the client should never see them */
+ LDAP_SYNC_CAPI_NONE = -1,
+
+ LDAP_SYNC_CAPI_PHASE_FLAG = 0x10U,
+ LDAP_SYNC_CAPI_IDSET_FLAG = 0x20U,
+ LDAP_SYNC_CAPI_DONE_FLAG = 0x40U,
+
+ /* these are passed to ls_search_entry() */
+ LDAP_SYNC_CAPI_PRESENT = LDAP_SYNC_PRESENT,
+ LDAP_SYNC_CAPI_ADD = LDAP_SYNC_ADD,
+ LDAP_SYNC_CAPI_MODIFY = LDAP_SYNC_MODIFY,
+ LDAP_SYNC_CAPI_DELETE = LDAP_SYNC_DELETE,
+
+ /* these are passed to ls_intermediate() */
+ LDAP_SYNC_CAPI_PRESENTS = ( LDAP_SYNC_CAPI_PHASE_FLAG | LDAP_SYNC_CAPI_PRESENT ),
+ LDAP_SYNC_CAPI_DELETES = ( LDAP_SYNC_CAPI_PHASE_FLAG | LDAP_SYNC_CAPI_DELETE ),
+
+ LDAP_SYNC_CAPI_PRESENTS_IDSET = ( LDAP_SYNC_CAPI_PRESENTS | LDAP_SYNC_CAPI_IDSET_FLAG ),
+ LDAP_SYNC_CAPI_DELETES_IDSET = ( LDAP_SYNC_CAPI_DELETES | LDAP_SYNC_CAPI_IDSET_FLAG ),
+
+ LDAP_SYNC_CAPI_DONE = ( LDAP_SYNC_CAPI_DONE_FLAG | LDAP_SYNC_CAPI_PRESENTS )
+} ldap_sync_refresh_t;
+
+/*
+ * Called when an entry is returned by ldap_result().
+ * If phase is LDAP_SYNC_CAPI_ADD or LDAP_SYNC_CAPI_MODIFY,
+ * the entry has been either added or modified, and thus
+ * the complete view of the entry should be in the LDAPMessage.
+ * If phase is LDAP_SYNC_CAPI_PRESENT or LDAP_SYNC_CAPI_DELETE,
+ * only the DN should be in the LDAPMessage.
+ */
+typedef int (*ldap_sync_search_entry_f) LDAP_P((
+ ldap_sync_t *ls,
+ LDAPMessage *msg,
+ struct berval *entryUUID,
+ ldap_sync_refresh_t phase ));
+
+/*
+ * Called when a reference is returned; the client should know
+ * what to do with it.
+ */
+typedef int (*ldap_sync_search_reference_f) LDAP_P((
+ ldap_sync_t *ls,
+ LDAPMessage *msg ));
+
+/*
+ * Called when specific intermediate/final messages are returned.
+ * If phase is LDAP_SYNC_CAPI_PRESENTS or LDAP_SYNC_CAPI_DELETES,
+ * a "presents" or "deletes" phase begins.
+ * If phase is LDAP_SYNC_CAPI_DONE, a special "presents" phase
+ * with refreshDone set to "TRUE" has been returned, to indicate
+ * that the refresh phase of a refreshAndPersist is complete.
+ * In the above cases, syncUUIDs is NULL.
+ *
+ * If phase is LDAP_SYNC_CAPI_PRESENTS_IDSET or
+ * LDAP_SYNC_CAPI_DELETES_IDSET, syncUUIDs is an array of UUIDs
+ * that are either present or have been deleted.
+ */
+typedef int (*ldap_sync_intermediate_f) LDAP_P((
+ ldap_sync_t *ls,
+ LDAPMessage *msg,
+ BerVarray syncUUIDs,
+ ldap_sync_refresh_t phase ));
+
+/*
+ * Called when a searchResultDone is returned. In refreshAndPersist,
+ * this can only occur if the search for any reason is being terminated
+ * by the server.
+ */
+typedef int (*ldap_sync_search_result_f) LDAP_P((
+ ldap_sync_t *ls,
+ LDAPMessage *msg,
+ int refreshDeletes ));
+
+/*
+ * This structure contains all information about the persistent search;
+ * the caller is responsible for connecting, setting version, binding, tls...
+ */
+struct ldap_sync_t {
+ /* conf search params */
+ char *ls_base;
+ int ls_scope;
+ char *ls_filter;
+ char **ls_attrs;
+ int ls_timelimit;
+ int ls_sizelimit;
+
+ /* poll timeout */
+ int ls_timeout;
+
+ /* helpers - add as appropriate */
+ ldap_sync_search_entry_f ls_search_entry;
+ ldap_sync_search_reference_f ls_search_reference;
+ ldap_sync_intermediate_f ls_intermediate;
+ ldap_sync_search_result_f ls_search_result;
+
+ /* set by the caller as appropriate */
+ void *ls_private;
+
+ /* conn stuff */
+ LDAP *ls_ld;
+
+ /* --- the parameters below are private - do not modify --- */
+
+ /* FIXME: make the structure opaque, and provide an interface
+ * to modify the public values? */
+
+ /* result stuff */
+ int ls_msgid;
+
+ /* sync stuff */
+ /* needed by refreshOnly */
+ int ls_reloadHint;
+
+ /* opaque - need to pass between sessions, updated by the API */
+ struct berval ls_cookie;
+
+ /* state variable - do not modify */
+ ldap_sync_refresh_t ls_refreshPhase;
+};
+
+/*
+ * End of LDAP sync (RFC4533) API
+ */
+
+/*
+ * Connection callbacks...
+ */
+struct ldap_conncb;
+struct sockaddr;
+
+/* Called after a connection is established */
+typedef int (ldap_conn_add_f) LDAP_P(( LDAP *ld, Sockbuf *sb, LDAPURLDesc *srv, struct sockaddr *addr,
+ struct ldap_conncb *ctx ));
+/* Called before a connection is closed */
+typedef void (ldap_conn_del_f) LDAP_P(( LDAP *ld, Sockbuf *sb, struct ldap_conncb *ctx ));
+
+/* Callbacks are pushed on a stack. Last one pushed is first one executed. The
+ * delete callback is called with a NULL Sockbuf just before freeing the LDAP handle.
+ */
+typedef struct ldap_conncb {
+ ldap_conn_add_f *lc_add;
+ ldap_conn_del_f *lc_del;
+ void *lc_arg;
+} ldap_conncb;
+
+/*
+ * The API draft spec says we should declare (or cause to be declared)
+ * 'struct timeval'. We don't. See IETF LDAPext discussions.
+ */
+struct timeval;
+
+/*
+ * in options.c:
+ */
+LDAP_F( int )
+ldap_get_option LDAP_P((
+ LDAP *ld,
+ int option,
+ void *outvalue));
+
+LDAP_F( int )
+ldap_set_option LDAP_P((
+ LDAP *ld,
+ int option,
+ LDAP_CONST void *invalue));
+
+/* V3 REBIND Function Callback Prototype */
+typedef int (LDAP_REBIND_PROC) LDAP_P((
+ LDAP *ld, LDAP_CONST char *url,
+ ber_tag_t request, ber_int_t msgid,
+ void *params ));
+
+LDAP_F( int )
+ldap_set_rebind_proc LDAP_P((
+ LDAP *ld,
+ LDAP_REBIND_PROC *rebind_proc,
+ void *params ));
+
+/* V3 referral selection Function Callback Prototype */
+typedef int (LDAP_NEXTREF_PROC) LDAP_P((
+ LDAP *ld, char ***refsp, int *cntp,
+ void *params ));
+
+LDAP_F( int )
+ldap_set_nextref_proc LDAP_P((
+ LDAP *ld,
+ LDAP_NEXTREF_PROC *nextref_proc,
+ void *params ));
+
+/* V3 URLLIST Function Callback Prototype */
+typedef int (LDAP_URLLIST_PROC) LDAP_P((
+ LDAP *ld,
+ LDAPURLDesc **urllist,
+ LDAPURLDesc **url,
+ void *params ));
+
+LDAP_F( int )
+ldap_set_urllist_proc LDAP_P((
+ LDAP *ld,
+ LDAP_URLLIST_PROC *urllist_proc,
+ void *params ));
+
+/*
+ * in controls.c:
+ */
+#if LDAP_DEPRECATED
+LDAP_F( int )
+ldap_create_control LDAP_P(( /* deprecated, use ldap_control_create */
+ LDAP_CONST char *requestOID,
+ BerElement *ber,
+ int iscritical,
+ LDAPControl **ctrlp ));
+
+LDAP_F( LDAPControl * )
+ldap_find_control LDAP_P(( /* deprecated, use ldap_control_find */
+ LDAP_CONST char *oid,
+ LDAPControl **ctrls ));
+#endif
+
+LDAP_F( int )
+ldap_control_create LDAP_P((
+ LDAP_CONST char *requestOID,
+ int iscritical,
+ struct berval *value,
+ int dupval,
+ LDAPControl **ctrlp ));
+
+LDAP_F( LDAPControl * )
+ldap_control_find LDAP_P((
+ LDAP_CONST char *oid,
+ LDAPControl **ctrls,
+ LDAPControl ***nextctrlp ));
+
+LDAP_F( void )
+ldap_control_free LDAP_P((
+ LDAPControl *ctrl ));
+
+LDAP_F( void )
+ldap_controls_free LDAP_P((
+ LDAPControl **ctrls ));
+
+LDAP_F( LDAPControl ** )
+ldap_controls_dup LDAP_P((
+ LDAPControl *LDAP_CONST *controls ));
+
+LDAP_F( LDAPControl * )
+ldap_control_dup LDAP_P((
+ LDAP_CONST LDAPControl *c ));
+
+/*
+ * in dnssrv.c:
+ */
+LDAP_F( int )
+ldap_domain2dn LDAP_P((
+ LDAP_CONST char* domain,
+ char** dn ));
+
+LDAP_F( int )
+ldap_dn2domain LDAP_P((
+ LDAP_CONST char* dn,
+ char** domain ));
+
+LDAP_F( int )
+ldap_domain2hostlist LDAP_P((
+ LDAP_CONST char *domain,
+ char** hostlist ));
+
+/*
+ * in extended.c:
+ */
+LDAP_F( int )
+ldap_extended_operation LDAP_P((
+ LDAP *ld,
+ LDAP_CONST char *reqoid,
+ struct berval *reqdata,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls,
+ int *msgidp ));
+
+LDAP_F( int )
+ldap_extended_operation_s LDAP_P((
+ LDAP *ld,
+ LDAP_CONST char *reqoid,
+ struct berval *reqdata,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls,
+ char **retoidp,
+ struct berval **retdatap ));
+
+LDAP_F( int )
+ldap_parse_extended_result LDAP_P((
+ LDAP *ld,
+ LDAPMessage *res,
+ char **retoidp,
+ struct berval **retdatap,
+ int freeit ));
+
+LDAP_F( int )
+ldap_parse_intermediate LDAP_P((
+ LDAP *ld,
+ LDAPMessage *res,
+ char **retoidp,
+ struct berval **retdatap,
+ LDAPControl ***serverctrls,
+ int freeit ));
+
+
+/*
+ * in abandon.c:
+ */
+LDAP_F( int )
+ldap_abandon_ext LDAP_P((
+ LDAP *ld,
+ int msgid,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls ));
+
+#if LDAP_DEPRECATED
+LDAP_F( int )
+ldap_abandon LDAP_P(( /* deprecated, use ldap_abandon_ext */
+ LDAP *ld,
+ int msgid ));
+#endif
+
+/*
+ * in add.c:
+ */
+LDAP_F( int )
+ldap_add_ext LDAP_P((
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPMod **attrs,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls,
+ int *msgidp ));
+
+LDAP_F( int )
+ldap_add_ext_s LDAP_P((
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPMod **attrs,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls ));
+
+#if LDAP_DEPRECATED
+LDAP_F( int )
+ldap_add LDAP_P(( /* deprecated, use ldap_add_ext */
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPMod **attrs ));
+
+LDAP_F( int )
+ldap_add_s LDAP_P(( /* deprecated, use ldap_add_ext_s */
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPMod **attrs ));
+#endif
+
+
+/*
+ * in sasl.c:
+ */
+LDAP_F( int )
+ldap_sasl_bind LDAP_P((
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *mechanism,
+ struct berval *cred,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls,
+ int *msgidp ));
+
+/* Interaction flags (should be passed about in a control)
+ * Automatic (default): use defaults, prompt otherwise
+ * Interactive: prompt always
+ * Quiet: never prompt
+ */
+#define LDAP_SASL_AUTOMATIC 0U
+#define LDAP_SASL_INTERACTIVE 1U
+#define LDAP_SASL_QUIET 2U
+
+/*
+ * V3 SASL Interaction Function Callback Prototype
+ * when using Cyrus SASL, interact is pointer to sasl_interact_t
+ * should likely passed in a control (and provided controls)
+ */
+typedef int (LDAP_SASL_INTERACT_PROC) LDAP_P((
+ LDAP *ld, unsigned flags, void* defaults, void *interact ));
+
+LDAP_F( int )
+ldap_sasl_interactive_bind LDAP_P((
+ LDAP *ld,
+ LDAP_CONST char *dn, /* usually NULL */
+ LDAP_CONST char *saslMechanism,
+ LDAPControl **serverControls,
+ LDAPControl **clientControls,
+
+ /* should be client controls */
+ unsigned flags,
+ LDAP_SASL_INTERACT_PROC *proc,
+ void *defaults,
+
+ /* as obtained from ldap_result() */
+ LDAPMessage *result,
+
+ /* returned during bind processing */
+ const char **rmech,
+ int *msgid ));
+
+LDAP_F( int )
+ldap_sasl_interactive_bind_s LDAP_P((
+ LDAP *ld,
+ LDAP_CONST char *dn, /* usually NULL */
+ LDAP_CONST char *saslMechanism,
+ LDAPControl **serverControls,
+ LDAPControl **clientControls,
+
+ /* should be client controls */
+ unsigned flags,
+ LDAP_SASL_INTERACT_PROC *proc,
+ void *defaults ));
+
+LDAP_F( int )
+ldap_sasl_bind_s LDAP_P((
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *mechanism,
+ struct berval *cred,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls,
+ struct berval **servercredp ));
+
+LDAP_F( int )
+ldap_parse_sasl_bind_result LDAP_P((
+ LDAP *ld,
+ LDAPMessage *res,
+ struct berval **servercredp,
+ int freeit ));
+
+#if LDAP_DEPRECATED
+/*
+ * in bind.c:
+ * (deprecated)
+ */
+LDAP_F( int )
+ldap_bind LDAP_P(( /* deprecated, use ldap_sasl_bind */
+ LDAP *ld,
+ LDAP_CONST char *who,
+ LDAP_CONST char *passwd,
+ int authmethod ));
+
+LDAP_F( int )
+ldap_bind_s LDAP_P(( /* deprecated, use ldap_sasl_bind_s */
+ LDAP *ld,
+ LDAP_CONST char *who,
+ LDAP_CONST char *cred,
+ int authmethod ));
+
+/*
+ * in sbind.c:
+ */
+LDAP_F( int )
+ldap_simple_bind LDAP_P(( /* deprecated, use ldap_sasl_bind */
+ LDAP *ld,
+ LDAP_CONST char *who,
+ LDAP_CONST char *passwd ));
+
+LDAP_F( int )
+ldap_simple_bind_s LDAP_P(( /* deprecated, use ldap_sasl_bind_s */
+ LDAP *ld,
+ LDAP_CONST char *who,
+ LDAP_CONST char *passwd ));
+
+#endif
+
+
+/*
+ * in compare.c:
+ */
+LDAP_F( int )
+ldap_compare_ext LDAP_P((
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *attr,
+ struct berval *bvalue,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls,
+ int *msgidp ));
+
+LDAP_F( int )
+ldap_compare_ext_s LDAP_P((
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *attr,
+ struct berval *bvalue,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls ));
+
+#if LDAP_DEPRECATED
+LDAP_F( int )
+ldap_compare LDAP_P(( /* deprecated, use ldap_compare_ext */
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *attr,
+ LDAP_CONST char *value ));
+
+LDAP_F( int )
+ldap_compare_s LDAP_P(( /* deprecated, use ldap_compare_ext_s */
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *attr,
+ LDAP_CONST char *value ));
+#endif
+
+
+/*
+ * in delete.c:
+ */
+LDAP_F( int )
+ldap_delete_ext LDAP_P((
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls,
+ int *msgidp ));
+
+LDAP_F( int )
+ldap_delete_ext_s LDAP_P((
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls ));
+
+#if LDAP_DEPRECATED
+LDAP_F( int )
+ldap_delete LDAP_P(( /* deprecated, use ldap_delete_ext */
+ LDAP *ld,
+ LDAP_CONST char *dn ));
+
+LDAP_F( int )
+ldap_delete_s LDAP_P(( /* deprecated, use ldap_delete_ext_s */
+ LDAP *ld,
+ LDAP_CONST char *dn ));
+#endif
+
+
+/*
+ * in error.c:
+ */
+LDAP_F( int )
+ldap_parse_result LDAP_P((
+ LDAP *ld,
+ LDAPMessage *res,
+ int *errcodep,
+ char **matcheddnp,
+ char **diagmsgp,
+ char ***referralsp,
+ LDAPControl ***serverctrls,
+ int freeit ));
+
+LDAP_F( char * )
+ldap_err2string LDAP_P((
+ int err ));
+
+#if LDAP_DEPRECATED
+LDAP_F( int )
+ldap_result2error LDAP_P(( /* deprecated, use ldap_parse_result */
+ LDAP *ld,
+ LDAPMessage *r,
+ int freeit ));
+
+LDAP_F( void )
+ldap_perror LDAP_P(( /* deprecated, use ldap_err2string */
+ LDAP *ld,
+ LDAP_CONST char *s ));
+#endif
+
+
+/*
+ * in modify.c:
+ */
+LDAP_F( int )
+ldap_modify_ext LDAP_P((
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPMod **mods,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls,
+ int *msgidp ));
+
+LDAP_F( int )
+ldap_modify_ext_s LDAP_P((
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPMod **mods,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls ));
+
+#if LDAP_DEPRECATED
+LDAP_F( int )
+ldap_modify LDAP_P(( /* deprecated, use ldap_modify_ext */
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPMod **mods ));
+
+LDAP_F( int )
+ldap_modify_s LDAP_P(( /* deprecated, use ldap_modify_ext_s */
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPMod **mods ));
+#endif
+
+
+/*
+ * in modrdn.c:
+ */
+LDAP_F( int )
+ldap_rename LDAP_P((
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *newrdn,
+ LDAP_CONST char *newSuperior,
+ int deleteoldrdn,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp ));
+
+LDAP_F( int )
+ldap_rename_s LDAP_P((
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *newrdn,
+ LDAP_CONST char *newSuperior,
+ int deleteoldrdn,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls ));
+
+#if LDAP_DEPRECATED
+LDAP_F( int )
+ldap_rename2 LDAP_P(( /* deprecated, use ldap_rename */
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *newrdn,
+ LDAP_CONST char *newSuperior,
+ int deleteoldrdn ));
+
+LDAP_F( int )
+ldap_rename2_s LDAP_P(( /* deprecated, use ldap_rename_s */
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *newrdn,
+ LDAP_CONST char *newSuperior,
+ int deleteoldrdn ));
+
+LDAP_F( int )
+ldap_modrdn LDAP_P(( /* deprecated, use ldap_rename */
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *newrdn ));
+
+LDAP_F( int )
+ldap_modrdn_s LDAP_P(( /* deprecated, use ldap_rename_s */
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *newrdn ));
+
+LDAP_F( int )
+ldap_modrdn2 LDAP_P(( /* deprecated, use ldap_rename */
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *newrdn,
+ int deleteoldrdn ));
+
+LDAP_F( int )
+ldap_modrdn2_s LDAP_P(( /* deprecated, use ldap_rename_s */
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *newrdn,
+ int deleteoldrdn));
+#endif
+
+
+/*
+ * in open.c:
+ */
+#if LDAP_DEPRECATED
+LDAP_F( LDAP * )
+ldap_init LDAP_P(( /* deprecated, use ldap_create or ldap_initialize */
+ LDAP_CONST char *host,
+ int port ));
+
+LDAP_F( LDAP * )
+ldap_open LDAP_P(( /* deprecated, use ldap_create or ldap_initialize */
+ LDAP_CONST char *host,
+ int port ));
+#endif
+
+LDAP_F( int )
+ldap_create LDAP_P((
+ LDAP **ldp ));
+
+LDAP_F( int )
+ldap_initialize LDAP_P((
+ LDAP **ldp,
+ LDAP_CONST char *url ));
+
+LDAP_F( LDAP * )
+ldap_dup LDAP_P((
+ LDAP *old ));
+
+LDAP_F( int )
+ldap_connect( LDAP *ld );
+
+/*
+ * in tls.c
+ */
+
+LDAP_F( int )
+ldap_tls_inplace LDAP_P((
+ LDAP *ld ));
+
+LDAP_F( int )
+ldap_start_tls LDAP_P((
+ LDAP *ld,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls,
+ int *msgidp ));
+
+LDAP_F( int )
+ldap_install_tls LDAP_P((
+ LDAP *ld ));
+
+LDAP_F( int )
+ldap_start_tls_s LDAP_P((
+ LDAP *ld,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls ));
+
+/*
+ * in messages.c:
+ */
+LDAP_F( LDAPMessage * )
+ldap_first_message LDAP_P((
+ LDAP *ld,
+ LDAPMessage *chain ));
+
+LDAP_F( LDAPMessage * )
+ldap_next_message LDAP_P((
+ LDAP *ld,
+ LDAPMessage *msg ));
+
+LDAP_F( int )
+ldap_count_messages LDAP_P((
+ LDAP *ld,
+ LDAPMessage *chain ));
+
+/*
+ * in references.c:
+ */
+LDAP_F( LDAPMessage * )
+ldap_first_reference LDAP_P((
+ LDAP *ld,
+ LDAPMessage *chain ));
+
+LDAP_F( LDAPMessage * )
+ldap_next_reference LDAP_P((
+ LDAP *ld,
+ LDAPMessage *ref ));
+
+LDAP_F( int )
+ldap_count_references LDAP_P((
+ LDAP *ld,
+ LDAPMessage *chain ));
+
+LDAP_F( int )
+ldap_parse_reference LDAP_P((
+ LDAP *ld,
+ LDAPMessage *ref,
+ char ***referralsp,
+ LDAPControl ***serverctrls,
+ int freeit));
+
+
+/*
+ * in getentry.c:
+ */
+LDAP_F( LDAPMessage * )
+ldap_first_entry LDAP_P((
+ LDAP *ld,
+ LDAPMessage *chain ));
+
+LDAP_F( LDAPMessage * )
+ldap_next_entry LDAP_P((
+ LDAP *ld,
+ LDAPMessage *entry ));
+
+LDAP_F( int )
+ldap_count_entries LDAP_P((
+ LDAP *ld,
+ LDAPMessage *chain ));
+
+LDAP_F( int )
+ldap_get_entry_controls LDAP_P((
+ LDAP *ld,
+ LDAPMessage *entry,
+ LDAPControl ***serverctrls));
+
+
+/*
+ * in addentry.c
+ */
+LDAP_F( LDAPMessage * )
+ldap_delete_result_entry LDAP_P((
+ LDAPMessage **list,
+ LDAPMessage *e ));
+
+LDAP_F( void )
+ldap_add_result_entry LDAP_P((
+ LDAPMessage **list,
+ LDAPMessage *e ));
+
+
+/*
+ * in getdn.c
+ */
+LDAP_F( char * )
+ldap_get_dn LDAP_P((
+ LDAP *ld,
+ LDAPMessage *entry ));
+
+typedef struct ldap_ava {
+ struct berval la_attr;
+ struct berval la_value;
+ unsigned la_flags;
+#define LDAP_AVA_NULL 0x0000U
+#define LDAP_AVA_STRING 0x0001U
+#define LDAP_AVA_BINARY 0x0002U
+#define LDAP_AVA_NONPRINTABLE 0x0004U
+#define LDAP_AVA_FREE_ATTR 0x0010U
+#define LDAP_AVA_FREE_VALUE 0x0020U
+
+ void *la_private;
+} LDAPAVA;
+
+typedef LDAPAVA** LDAPRDN;
+typedef LDAPRDN* LDAPDN;
+
+/* DN formats */
+#define LDAP_DN_FORMAT_LDAP 0x0000U
+#define LDAP_DN_FORMAT_LDAPV3 0x0010U
+#define LDAP_DN_FORMAT_LDAPV2 0x0020U
+#define LDAP_DN_FORMAT_DCE 0x0030U
+#define LDAP_DN_FORMAT_UFN 0x0040U /* dn2str only */
+#define LDAP_DN_FORMAT_AD_CANONICAL 0x0050U /* dn2str only */
+#define LDAP_DN_FORMAT_LBER 0x00F0U /* for testing only */
+#define LDAP_DN_FORMAT_MASK 0x00F0U
+
+/* DN flags */
+#define LDAP_DN_PRETTY 0x0100U
+#define LDAP_DN_SKIP 0x0200U
+#define LDAP_DN_P_NOLEADTRAILSPACES 0x1000U
+#define LDAP_DN_P_NOSPACEAFTERRDN 0x2000U
+#define LDAP_DN_PEDANTIC 0xF000U
+
+LDAP_F( void ) ldap_rdnfree LDAP_P(( LDAPRDN rdn ));
+LDAP_F( void ) ldap_dnfree LDAP_P(( LDAPDN dn ));
+
+LDAP_F( int )
+ldap_bv2dn LDAP_P((
+ struct berval *bv,
+ LDAPDN *dn,
+ unsigned flags ));
+
+LDAP_F( int )
+ldap_str2dn LDAP_P((
+ LDAP_CONST char *str,
+ LDAPDN *dn,
+ unsigned flags ));
+
+LDAP_F( int )
+ldap_dn2bv LDAP_P((
+ LDAPDN dn,
+ struct berval *bv,
+ unsigned flags ));
+
+LDAP_F( int )
+ldap_dn2str LDAP_P((
+ LDAPDN dn,
+ char **str,
+ unsigned flags ));
+
+LDAP_F( int )
+ldap_bv2rdn LDAP_P((
+ struct berval *bv,
+ LDAPRDN *rdn,
+ char **next,
+ unsigned flags ));
+
+LDAP_F( int )
+ldap_str2rdn LDAP_P((
+ LDAP_CONST char *str,
+ LDAPRDN *rdn,
+ char **next,
+ unsigned flags ));
+
+LDAP_F( int )
+ldap_rdn2bv LDAP_P((
+ LDAPRDN rdn,
+ struct berval *bv,
+ unsigned flags ));
+
+LDAP_F( int )
+ldap_rdn2str LDAP_P((
+ LDAPRDN rdn,
+ char **str,
+ unsigned flags ));
+
+LDAP_F( int )
+ldap_dn_normalize LDAP_P((
+ LDAP_CONST char *in, unsigned iflags,
+ char **out, unsigned oflags ));
+
+LDAP_F( char * )
+ldap_dn2ufn LDAP_P(( /* deprecated, use ldap_str2dn/dn2str */
+ LDAP_CONST char *dn ));
+
+LDAP_F( char ** )
+ldap_explode_dn LDAP_P(( /* deprecated, ldap_str2dn */
+ LDAP_CONST char *dn,
+ int notypes ));
+
+LDAP_F( char ** )
+ldap_explode_rdn LDAP_P(( /* deprecated, ldap_str2rdn */
+ LDAP_CONST char *rdn,
+ int notypes ));
+
+typedef int LDAPDN_rewrite_func
+ LDAP_P(( LDAPDN dn, unsigned flags, void *ctx ));
+
+LDAP_F( int )
+ldap_X509dn2bv LDAP_P(( void *x509_name, struct berval *dn,
+ LDAPDN_rewrite_func *func, unsigned flags ));
+
+LDAP_F( char * )
+ldap_dn2dcedn LDAP_P(( /* deprecated, ldap_str2dn/dn2str */
+ LDAP_CONST char *dn ));
+
+LDAP_F( char * )
+ldap_dcedn2dn LDAP_P(( /* deprecated, ldap_str2dn/dn2str */
+ LDAP_CONST char *dce ));
+
+LDAP_F( char * )
+ldap_dn2ad_canonical LDAP_P(( /* deprecated, ldap_str2dn/dn2str */
+ LDAP_CONST char *dn ));
+
+LDAP_F( int )
+ldap_get_dn_ber LDAP_P((
+ LDAP *ld, LDAPMessage *e, BerElement **berout, struct berval *dn ));
+
+LDAP_F( int )
+ldap_get_attribute_ber LDAP_P((
+ LDAP *ld, LDAPMessage *e, BerElement *ber, struct berval *attr,
+ struct berval **vals ));
+
+/*
+ * in getattr.c
+ */
+LDAP_F( char * )
+ldap_first_attribute LDAP_P((
+ LDAP *ld,
+ LDAPMessage *entry,
+ BerElement **ber ));
+
+LDAP_F( char * )
+ldap_next_attribute LDAP_P((
+ LDAP *ld,
+ LDAPMessage *entry,
+ BerElement *ber ));
+
+
+/*
+ * in getvalues.c
+ */
+LDAP_F( struct berval ** )
+ldap_get_values_len LDAP_P((
+ LDAP *ld,
+ LDAPMessage *entry,
+ LDAP_CONST char *target ));
+
+LDAP_F( int )
+ldap_count_values_len LDAP_P((
+ struct berval **vals ));
+
+LDAP_F( void )
+ldap_value_free_len LDAP_P((
+ struct berval **vals ));
+
+#if LDAP_DEPRECATED
+LDAP_F( char ** )
+ldap_get_values LDAP_P(( /* deprecated, use ldap_get_values_len */
+ LDAP *ld,
+ LDAPMessage *entry,
+ LDAP_CONST char *target ));
+
+LDAP_F( int )
+ldap_count_values LDAP_P(( /* deprecated, use ldap_count_values_len */
+ char **vals ));
+
+LDAP_F( void )
+ldap_value_free LDAP_P(( /* deprecated, use ldap_value_free_len */
+ char **vals ));
+#endif
+
+/*
+ * in result.c:
+ */
+LDAP_F( int )
+ldap_result LDAP_P((
+ LDAP *ld,
+ int msgid,
+ int all,
+ struct timeval *timeout,
+ LDAPMessage **result ));
+
+LDAP_F( int )
+ldap_msgtype LDAP_P((
+ LDAPMessage *lm ));
+
+LDAP_F( int )
+ldap_msgid LDAP_P((
+ LDAPMessage *lm ));
+
+LDAP_F( int )
+ldap_msgfree LDAP_P((
+ LDAPMessage *lm ));
+
+LDAP_F( int )
+ldap_msgdelete LDAP_P((
+ LDAP *ld,
+ int msgid ));
+
+
+/*
+ * in search.c:
+ */
+LDAP_F( int )
+ldap_bv2escaped_filter_value LDAP_P((
+ struct berval *in,
+ struct berval *out ));
+
+LDAP_F( int )
+ldap_search_ext LDAP_P((
+ LDAP *ld,
+ LDAP_CONST char *base,
+ int scope,
+ LDAP_CONST char *filter,
+ char **attrs,
+ int attrsonly,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls,
+ struct timeval *timeout,
+ int sizelimit,
+ int *msgidp ));
+
+LDAP_F( int )
+ldap_search_ext_s LDAP_P((
+ LDAP *ld,
+ LDAP_CONST char *base,
+ int scope,
+ LDAP_CONST char *filter,
+ char **attrs,
+ int attrsonly,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls,
+ struct timeval *timeout,
+ int sizelimit,
+ LDAPMessage **res ));
+
+#if LDAP_DEPRECATED
+LDAP_F( int )
+ldap_search LDAP_P(( /* deprecated, use ldap_search_ext */
+ LDAP *ld,
+ LDAP_CONST char *base,
+ int scope,
+ LDAP_CONST char *filter,
+ char **attrs,
+ int attrsonly ));
+
+LDAP_F( int )
+ldap_search_s LDAP_P(( /* deprecated, use ldap_search_ext_s */
+ LDAP *ld,
+ LDAP_CONST char *base,
+ int scope,
+ LDAP_CONST char *filter,
+ char **attrs,
+ int attrsonly,
+ LDAPMessage **res ));
+
+LDAP_F( int )
+ldap_search_st LDAP_P(( /* deprecated, use ldap_search_ext_s */
+ LDAP *ld,
+ LDAP_CONST char *base,
+ int scope,
+ LDAP_CONST char *filter,
+ char **attrs,
+ int attrsonly,
+ struct timeval *timeout,
+ LDAPMessage **res ));
+#endif
+
+/*
+ * in unbind.c
+ */
+LDAP_F( int )
+ldap_unbind_ext LDAP_P((
+ LDAP *ld,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls));
+
+LDAP_F( int )
+ldap_unbind_ext_s LDAP_P((
+ LDAP *ld,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls));
+
+LDAP_F( int )
+ldap_destroy LDAP_P((
+ LDAP *ld));
+
+#if LDAP_DEPRECATED
+LDAP_F( int )
+ldap_unbind LDAP_P(( /* deprecated, use ldap_unbind_ext */
+ LDAP *ld ));
+
+LDAP_F( int )
+ldap_unbind_s LDAP_P(( /* deprecated, use ldap_unbind_ext_s */
+ LDAP *ld ));
+#endif
+
+/*
+ * in filter.c
+ */
+LDAP_F( int )
+ldap_put_vrFilter LDAP_P((
+ BerElement *ber,
+ const char *vrf ));
+
+/*
+ * in free.c
+ */
+
+LDAP_F( void * )
+ldap_memalloc LDAP_P((
+ ber_len_t s ));
+
+LDAP_F( void * )
+ldap_memrealloc LDAP_P((
+ void* p,
+ ber_len_t s ));
+
+LDAP_F( void * )
+ldap_memcalloc LDAP_P((
+ ber_len_t n,
+ ber_len_t s ));
+
+LDAP_F( void )
+ldap_memfree LDAP_P((
+ void* p ));
+
+LDAP_F( void )
+ldap_memvfree LDAP_P((
+ void** v ));
+
+LDAP_F( char * )
+ldap_strdup LDAP_P((
+ LDAP_CONST char * ));
+
+LDAP_F( void )
+ldap_mods_free LDAP_P((
+ LDAPMod **mods,
+ int freemods ));
+
+
+#if LDAP_DEPRECATED
+/*
+ * in sort.c (deprecated, use custom code instead)
+ */
+typedef int (LDAP_SORT_AD_CMP_PROC) LDAP_P(( /* deprecated */
+ LDAP_CONST char *left,
+ LDAP_CONST char *right ));
+
+typedef int (LDAP_SORT_AV_CMP_PROC) LDAP_P(( /* deprecated */
+ LDAP_CONST void *left,
+ LDAP_CONST void *right ));
+
+LDAP_F( int ) /* deprecated */
+ldap_sort_entries LDAP_P(( LDAP *ld,
+ LDAPMessage **chain,
+ LDAP_CONST char *attr,
+ LDAP_SORT_AD_CMP_PROC *cmp ));
+
+LDAP_F( int ) /* deprecated */
+ldap_sort_values LDAP_P((
+ LDAP *ld,
+ char **vals,
+ LDAP_SORT_AV_CMP_PROC *cmp ));
+
+LDAP_F( int ) /* deprecated */
+ldap_sort_strcasecmp LDAP_P((
+ LDAP_CONST void *a,
+ LDAP_CONST void *b ));
+#endif
+
+/*
+ * in url.c
+ */
+LDAP_F( int )
+ldap_is_ldap_url LDAP_P((
+ LDAP_CONST char *url ));
+
+LDAP_F( int )
+ldap_is_ldaps_url LDAP_P((
+ LDAP_CONST char *url ));
+
+LDAP_F( int )
+ldap_is_ldapi_url LDAP_P((
+ LDAP_CONST char *url ));
+
+#ifdef LDAP_CONNECTIONLESS
+LDAP_F( int )
+ldap_is_ldapc_url LDAP_P((
+ LDAP_CONST char *url ));
+#endif
+
+LDAP_F( int )
+ldap_url_parse LDAP_P((
+ LDAP_CONST char *url,
+ LDAPURLDesc **ludpp ));
+
+LDAP_F( char * )
+ldap_url_desc2str LDAP_P((
+ LDAPURLDesc *ludp ));
+
+LDAP_F( void )
+ldap_free_urldesc LDAP_P((
+ LDAPURLDesc *ludp ));
+
+
+/*
+ * LDAP Cancel Extended Operation <draft-zeilenga-ldap-cancel-xx.txt>
+ * in cancel.c
+ */
+#define LDAP_API_FEATURE_CANCEL 1000
+
+LDAP_F( int )
+ldap_cancel LDAP_P(( LDAP *ld,
+ int cancelid,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp ));
+
+LDAP_F( int )
+ldap_cancel_s LDAP_P(( LDAP *ld,
+ int cancelid,
+ LDAPControl **sctrl,
+ LDAPControl **cctrl ));
+
+/*
+ * LDAP Turn Extended Operation <draft-zeilenga-ldap-turn-xx.txt>
+ * in turn.c
+ */
+#define LDAP_API_FEATURE_TURN 1000
+
+LDAP_F( int )
+ldap_turn LDAP_P(( LDAP *ld,
+ int mutual,
+ LDAP_CONST char* identifier,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp ));
+
+LDAP_F( int )
+ldap_turn_s LDAP_P(( LDAP *ld,
+ int mutual,
+ LDAP_CONST char* identifier,
+ LDAPControl **sctrl,
+ LDAPControl **cctrl ));
+
+/*
+ * LDAP Paged Results
+ * in pagectrl.c
+ */
+#define LDAP_API_FEATURE_PAGED_RESULTS 2000
+
+LDAP_F( int )
+ldap_create_page_control_value LDAP_P((
+ LDAP *ld,
+ ber_int_t pagesize,
+ struct berval *cookie,
+ struct berval *value ));
+
+LDAP_F( int )
+ldap_create_page_control LDAP_P((
+ LDAP *ld,
+ ber_int_t pagesize,
+ struct berval *cookie,
+ int iscritical,
+ LDAPControl **ctrlp ));
+
+#if LDAP_DEPRECATED
+LDAP_F( int )
+ldap_parse_page_control LDAP_P((
+ /* deprecated, use ldap_parse_pageresponse_control */
+ LDAP *ld,
+ LDAPControl **ctrls,
+ ber_int_t *count,
+ struct berval **cookie ));
+#endif
+
+LDAP_F( int )
+ldap_parse_pageresponse_control LDAP_P((
+ LDAP *ld,
+ LDAPControl *ctrl,
+ ber_int_t *count,
+ struct berval *cookie ));
+
+/*
+ * LDAP Server Side Sort
+ * in sortctrl.c
+ */
+#define LDAP_API_FEATURE_SERVER_SIDE_SORT 2000
+
+/* structure for a sort-key */
+typedef struct ldapsortkey {
+ char *attributeType;
+ char *orderingRule;
+ int reverseOrder;
+} LDAPSortKey;
+
+LDAP_F( int )
+ldap_create_sort_keylist LDAP_P((
+ LDAPSortKey ***sortKeyList,
+ char *keyString ));
+
+LDAP_F( void )
+ldap_free_sort_keylist LDAP_P((
+ LDAPSortKey **sortkeylist ));
+
+LDAP_F( int )
+ldap_create_sort_control_value LDAP_P((
+ LDAP *ld,
+ LDAPSortKey **keyList,
+ struct berval *value ));
+
+LDAP_F( int )
+ldap_create_sort_control LDAP_P((
+ LDAP *ld,
+ LDAPSortKey **keyList,
+ int iscritical,
+ LDAPControl **ctrlp ));
+
+LDAP_F( int )
+ldap_parse_sortresponse_control LDAP_P((
+ LDAP *ld,
+ LDAPControl *ctrl,
+ ber_int_t *result,
+ char **attribute ));
+
+/*
+ * LDAP Virtual List View
+ * in vlvctrl.c
+ */
+#define LDAP_API_FEATURE_VIRTUAL_LIST_VIEW 2000
+
+/* structure for virtual list */
+typedef struct ldapvlvinfo {
+ ber_int_t ldvlv_version;
+ ber_int_t ldvlv_before_count;
+ ber_int_t ldvlv_after_count;
+ ber_int_t ldvlv_offset;
+ ber_int_t ldvlv_count;
+ struct berval * ldvlv_attrvalue;
+ struct berval * ldvlv_context;
+ void * ldvlv_extradata;
+} LDAPVLVInfo;
+
+LDAP_F( int )
+ldap_create_vlv_control_value LDAP_P((
+ LDAP *ld,
+ LDAPVLVInfo *ldvlistp,
+ struct berval *value));
+
+LDAP_F( int )
+ldap_create_vlv_control LDAP_P((
+ LDAP *ld,
+ LDAPVLVInfo *ldvlistp,
+ LDAPControl **ctrlp ));
+
+LDAP_F( int )
+ldap_parse_vlvresponse_control LDAP_P((
+ LDAP *ld,
+ LDAPControl *ctrls,
+ ber_int_t *target_posp,
+ ber_int_t *list_countp,
+ struct berval **contextp,
+ int *errcodep ));
+
+/*
+ * LDAP Verify Credentials
+ */
+#define LDAP_API_FEATURE_VERIFY_CREDENTIALS 1000
+
+LDAP_F( int )
+ldap_verify_credentials LDAP_P((
+ LDAP *ld,
+ struct berval *cookie,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *mechanism,
+ struct berval *cred,
+ LDAPControl **ctrls,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls,
+ int *msgidp ));
+
+LDAP_F( int )
+ldap_verify_credentials_s LDAP_P((
+ LDAP *ld,
+ struct berval *cookie,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *mechanism,
+ struct berval *cred,
+ LDAPControl **vcictrls,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls,
+ int *code,
+ char **diagmsgp,
+ struct berval **scookie,
+ struct berval **servercredp,
+ LDAPControl ***vcoctrls));
+
+
+LDAP_F( int )
+ldap_parse_verify_credentials LDAP_P((
+ LDAP *ld,
+ LDAPMessage *res,
+ int *code,
+ char **diagmsgp,
+ struct berval **cookie,
+ struct berval **servercredp,
+ LDAPControl ***vcctrls));
+
+/* not yet implemented */
+/* #define LDAP_API_FEATURE_VERIFY_CREDENTIALS_INTERACTIVE 1000 */
+#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS_INTERACTIVE
+LDAP_F( int )
+ldap_verify_credentials_interactive LDAP_P((
+ LDAP *ld,
+ LDAP_CONST char *dn, /* usually NULL */
+ LDAP_CONST char *saslMechanism,
+ LDAPControl **vcControls,
+ LDAPControl **serverControls,
+ LDAPControl **clientControls,
+
+ /* should be client controls */
+ unsigned flags,
+ LDAP_SASL_INTERACT_PROC *proc,
+ void *defaults,
+ void *context,
+
+ /* as obtained from ldap_result() */
+ LDAPMessage *result,
+
+ /* returned during bind processing */
+ const char **rmech,
+ int *msgid ));
+#endif
+
+/*
+ * LDAP Who Am I?
+ * in whoami.c
+ */
+#define LDAP_API_FEATURE_WHOAMI 1000
+
+LDAP_F( int )
+ldap_parse_whoami LDAP_P((
+ LDAP *ld,
+ LDAPMessage *res,
+ struct berval **authzid ));
+
+LDAP_F( int )
+ldap_whoami LDAP_P(( LDAP *ld,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp ));
+
+LDAP_F( int )
+ldap_whoami_s LDAP_P((
+ LDAP *ld,
+ struct berval **authzid,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls ));
+
+/*
+ * LDAP Password Modify
+ * in passwd.c
+ */
+#define LDAP_API_FEATURE_PASSWD_MODIFY 1000
+
+LDAP_F( int )
+ldap_parse_passwd LDAP_P((
+ LDAP *ld,
+ LDAPMessage *res,
+ struct berval *newpasswd ));
+
+LDAP_F( int )
+ldap_passwd LDAP_P(( LDAP *ld,
+ struct berval *user,
+ struct berval *oldpw,
+ struct berval *newpw,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp ));
+
+LDAP_F( int )
+ldap_passwd_s LDAP_P((
+ LDAP *ld,
+ struct berval *user,
+ struct berval *oldpw,
+ struct berval *newpw,
+ struct berval *newpasswd,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls ));
+
+#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
+/*
+ * LDAP Password Policy controls
+ * in ppolicy.c
+ */
+#define LDAP_API_FEATURE_PASSWORD_POLICY 1000
+
+typedef enum passpolicyerror_enum {
+ PP_passwordExpired = 0,
+ PP_accountLocked = 1,
+ PP_changeAfterReset = 2,
+ PP_passwordModNotAllowed = 3,
+ PP_mustSupplyOldPassword = 4,
+ PP_insufficientPasswordQuality = 5,
+ PP_passwordTooShort = 6,
+ PP_passwordTooYoung = 7,
+ PP_passwordInHistory = 8,
+ PP_passwordTooLong = 9,
+ PP_noError = 65535
+} LDAPPasswordPolicyError;
+
+LDAP_F( int )
+ldap_create_passwordpolicy_control LDAP_P((
+ LDAP *ld,
+ LDAPControl **ctrlp ));
+
+LDAP_F( int )
+ldap_parse_passwordpolicy_control LDAP_P((
+ LDAP *ld,
+ LDAPControl *ctrl,
+ ber_int_t *expirep,
+ ber_int_t *gracep,
+ LDAPPasswordPolicyError *errorp ));
+
+LDAP_F( const char * )
+ldap_passwordpolicy_err2txt LDAP_P(( LDAPPasswordPolicyError ));
+#endif /* LDAP_CONTROL_PASSWORDPOLICYREQUEST */
+
+LDAP_F( int )
+ldap_parse_password_expiring_control LDAP_P((
+ LDAP *ld,
+ LDAPControl *ctrl,
+ long *secondsp ));
+
+/*
+ * LDAP Dynamic Directory Services Refresh -- RFC 2589
+ * in dds.c
+ */
+#define LDAP_API_FEATURE_REFRESH 1000
+
+LDAP_F( int )
+ldap_parse_refresh LDAP_P((
+ LDAP *ld,
+ LDAPMessage *res,
+ ber_int_t *newttl ));
+
+LDAP_F( int )
+ldap_refresh LDAP_P(( LDAP *ld,
+ struct berval *dn,
+ ber_int_t ttl,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp ));
+
+LDAP_F( int )
+ldap_refresh_s LDAP_P((
+ LDAP *ld,
+ struct berval *dn,
+ ber_int_t ttl,
+ ber_int_t *newttl,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls ));
+
+/*
+ * LDAP Transactions
+ */
+LDAP_F( int )
+ldap_txn_start LDAP_P(( LDAP *ld,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp ));
+
+LDAP_F( int )
+ldap_txn_start_s LDAP_P(( LDAP *ld,
+ LDAPControl **sctrl,
+ LDAPControl **cctrl,
+ struct berval **rettxnid ));
+
+LDAP_F( int )
+ldap_txn_end LDAP_P(( LDAP *ld,
+ int commit,
+ struct berval *txnid,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp ));
+
+LDAP_F( int )
+ldap_txn_end_s LDAP_P(( LDAP *ld,
+ int commit,
+ struct berval *txnid,
+ LDAPControl **sctrl,
+ LDAPControl **cctrl,
+ int *retidp ));
+
+/*
+ * in ldap_sync.c
+ */
+
+/*
+ * initialize the persistent search structure
+ */
+LDAP_F( ldap_sync_t * )
+ldap_sync_initialize LDAP_P((
+ ldap_sync_t *ls ));
+
+/*
+ * destroy the persistent search structure
+ */
+LDAP_F( void )
+ldap_sync_destroy LDAP_P((
+ ldap_sync_t *ls,
+ int freeit ));
+
+/*
+ * initialize a refreshOnly sync
+ */
+LDAP_F( int )
+ldap_sync_init LDAP_P((
+ ldap_sync_t *ls,
+ int mode ));
+
+/*
+ * initialize a refreshOnly sync
+ */
+LDAP_F( int )
+ldap_sync_init_refresh_only LDAP_P((
+ ldap_sync_t *ls ));
+
+/*
+ * initialize a refreshAndPersist sync
+ */
+LDAP_F( int )
+ldap_sync_init_refresh_and_persist LDAP_P((
+ ldap_sync_t *ls ));
+
+/*
+ * poll for new responses
+ */
+LDAP_F( int )
+ldap_sync_poll LDAP_P((
+ ldap_sync_t *ls ));
+
+#ifdef LDAP_CONTROL_X_SESSION_TRACKING
+
+/*
+ * in stctrl.c
+ */
+LDAP_F( int )
+ldap_create_session_tracking_value LDAP_P((
+ LDAP *ld,
+ char *sessionSourceIp,
+ char *sessionSourceName,
+ char *formatOID,
+ struct berval *sessionTrackingIdentifier,
+ struct berval *value ));
+
+LDAP_F( int )
+ldap_create_session_tracking_control LDAP_P((
+ LDAP *ld,
+ char *sessionSourceIp,
+ char *sessionSourceName,
+ char *formatOID,
+ struct berval *sessionTrackingIdentifier,
+ LDAPControl **ctrlp ));
+
+LDAP_F( int )
+ldap_parse_session_tracking_control LDAP_P((
+ LDAP *ld,
+ LDAPControl *ctrl,
+ struct berval *ip,
+ struct berval *name,
+ struct berval *oid,
+ struct berval *id ));
+
+#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
+
+/*
+ * in msctrl.c
+ */
+#ifdef LDAP_CONTROL_X_DIRSYNC
+LDAP_F( int )
+ldap_create_dirsync_value LDAP_P((
+ LDAP *ld,
+ int flags,
+ int maxAttrCount,
+ struct berval *cookie,
+ struct berval *value ));
+
+LDAP_F( int )
+ldap_create_dirsync_control LDAP_P((
+ LDAP *ld,
+ int flags,
+ int maxAttrCount,
+ struct berval *cookie,
+ LDAPControl **ctrlp ));
+
+LDAP_F( int )
+ldap_parse_dirsync_control LDAP_P((
+ LDAP *ld,
+ LDAPControl *ctrl,
+ int *continueFlag,
+ struct berval *cookie ));
+#endif /* LDAP_CONTROL_X_DIRSYNC */
+
+#ifdef LDAP_CONTROL_X_EXTENDED_DN
+LDAP_F( int )
+ldap_create_extended_dn_value LDAP_P((
+ LDAP *ld,
+ int flag,
+ struct berval *value ));
+
+LDAP_F( int )
+ldap_create_extended_dn_control LDAP_P((
+ LDAP *ld,
+ int flag,
+ LDAPControl **ctrlp ));
+#endif /* LDAP_CONTROL_X_EXTENDED_DN */
+
+#ifdef LDAP_CONTROL_X_SHOW_DELETED
+LDAP_F( int )
+ldap_create_show_deleted_control LDAP_P((
+ LDAP *ld,
+ LDAPControl **ctrlp ));
+#endif /* LDAP_CONTROL_X_SHOW_DELETED */
+
+#ifdef LDAP_CONTROL_X_SERVER_NOTIFICATION
+LDAP_F( int )
+ldap_create_server_notification_control LDAP_P((
+ LDAP *ld,
+ LDAPControl **ctrlp ));
+#endif /* LDAP_CONTROL_X_SERVER_NOTIFICATION */
+
+/*
+ * in assertion.c
+ */
+LDAP_F (int)
+ldap_create_assertion_control_value LDAP_P((
+ LDAP *ld,
+ char *assertion,
+ struct berval *value ));
+
+LDAP_F( int )
+ldap_create_assertion_control LDAP_P((
+ LDAP *ld,
+ char *filter,
+ int iscritical,
+ LDAPControl **ctrlp ));
+
+/*
+ * in deref.c
+ */
+
+typedef struct LDAPDerefSpec {
+ char *derefAttr;
+ char **attributes;
+} LDAPDerefSpec;
+
+typedef struct LDAPDerefVal {
+ char *type;
+ BerVarray vals;
+ struct LDAPDerefVal *next;
+} LDAPDerefVal;
+
+typedef struct LDAPDerefRes {
+ char *derefAttr;
+ struct berval derefVal;
+ LDAPDerefVal *attrVals;
+ struct LDAPDerefRes *next;
+} LDAPDerefRes;
+
+LDAP_F( int )
+ldap_create_deref_control_value LDAP_P((
+ LDAP *ld,
+ LDAPDerefSpec *ds,
+ struct berval *value ));
+
+LDAP_F( int )
+ldap_create_deref_control LDAP_P((
+ LDAP *ld,
+ LDAPDerefSpec *ds,
+ int iscritical,
+ LDAPControl **ctrlp ));
+
+LDAP_F( void )
+ldap_derefresponse_free LDAP_P((
+ LDAPDerefRes *dr ));
+
+LDAP_F( int )
+ldap_parse_derefresponse_control LDAP_P((
+ LDAP *ld,
+ LDAPControl *ctrl,
+ LDAPDerefRes **drp ));
+
+LDAP_F( int )
+ldap_parse_deref_control LDAP_P((
+ LDAP *ld,
+ LDAPControl **ctrls,
+ LDAPDerefRes **drp ));
+
+/*
+ * in psearch.c
+ */
+
+LDAP_F( int )
+ldap_create_persistentsearch_control_value LDAP_P((
+ LDAP *ld,
+ int changetypes,
+ int changesonly,
+ int return_echg_ctls,
+ struct berval *value ));
+
+LDAP_F( int )
+ldap_create_persistentsearch_control LDAP_P((
+ LDAP *ld,
+ int changetypes,
+ int changesonly,
+ int return_echg_ctls,
+ int isCritical,
+ LDAPControl **ctrlp ));
+
+LDAP_F( int )
+ldap_parse_entrychange_control LDAP_P((
+ LDAP *ld,
+ LDAPControl *ctrl,
+ int *chgtypep,
+ struct berval *prevdnp,
+ int *chgnumpresentp,
+ long *chgnump ));
+
+/* in account_usability.c */
+
+LDAP_F( int )
+ldap_create_accountusability_control LDAP_P((
+ LDAP *ld,
+ LDAPControl **ctrlp ));
+
+typedef struct LDAPAccountUsabilityMoreInfo {
+ ber_int_t inactive;
+ ber_int_t reset;
+ ber_int_t expired;
+ ber_int_t remaining_grace;
+ ber_int_t seconds_before_unlock;
+} LDAPAccountUsabilityMoreInfo;
+
+typedef union LDAPAccountUsability {
+ ber_int_t seconds_remaining;
+ LDAPAccountUsabilityMoreInfo more_info;
+} LDAPAccountUsability;
+
+LDAP_F( int )
+ldap_parse_accountusability_control LDAP_P((
+ LDAP *ld,
+ LDAPControl *ctrl,
+ int *availablep,
+ LDAPAccountUsability *usabilityp ));
+
+
+/*
+ * high level LDIF to LDAP structure support
+ */
+#define LDIF_DEFAULT_ADD 0x01 /* if changetype missing, assume LDAP_ADD */
+#define LDIF_ENTRIES_ONLY 0x02 /* ignore changetypes other than add */
+#define LDIF_NO_CONTROLS 0x04 /* ignore control specifications */
+#define LDIF_MODS_ONLY 0x08 /* no changetypes, assume LDAP_MODIFY */
+#define LDIF_NO_DN 0x10 /* dn is not present */
+
+typedef struct ldifrecord {
+ ber_tag_t lr_op; /* type of operation - LDAP_REQ_MODIFY, LDAP_REQ_ADD, etc. */
+ struct berval lr_dn; /* DN of operation */
+ LDAPControl **lr_ctrls; /* controls specified for operation */
+ /* some ops such as LDAP_REQ_DELETE require only a DN */
+ /* other ops require different data - the ldif_ops union
+ is used to specify the data for each type of operation */
+ union ldif_ops_u {
+ LDAPMod **lr_mods; /* list of mods for LDAP_REQ_MODIFY, LDAP_REQ_ADD */
+#define lrop_mods ldif_ops.lr_mods
+ struct ldif_op_rename_s {
+ struct berval lr_newrdn; /* LDAP_REQ_MODDN, LDAP_REQ_MODRDN, LDAP_REQ_RENAME */
+#define lrop_newrdn ldif_ops.ldif_op_rename.lr_newrdn
+ struct berval lr_newsuperior; /* LDAP_REQ_MODDN, LDAP_REQ_MODRDN, LDAP_REQ_RENAME */
+#define lrop_newsup ldif_ops.ldif_op_rename.lr_newsuperior
+ int lr_deleteoldrdn; /* LDAP_REQ_MODDN, LDAP_REQ_MODRDN, LDAP_REQ_RENAME */
+#define lrop_delold ldif_ops.ldif_op_rename.lr_deleteoldrdn
+ } ldif_op_rename; /* rename/moddn/modrdn */
+ /* the following are for future support */
+ struct ldif_op_ext_s {
+ struct berval lr_extop_oid; /* LDAP_REQ_EXTENDED */
+#define lrop_extop_oid ldif_ops.ldif_op_ext.lr_extop_oid
+ struct berval lr_extop_data; /* LDAP_REQ_EXTENDED */
+#define lrop_extop_data ldif_ops.ldif_op_ext.lr_extop_data
+ } ldif_op_ext; /* extended operation */
+ struct ldif_op_cmp_s {
+ struct berval lr_cmp_attr; /* LDAP_REQ_COMPARE */
+#define lrop_cmp_attr ldif_ops.ldif_op_cmp.lr_cmp_attr
+ struct berval lr_cmp_bvalue; /* LDAP_REQ_COMPARE */
+#define lrop_cmp_bval ldif_ops.ldif_op_cmp.lr_cmp_bvalue
+ } ldif_op_cmp; /* compare operation */
+ } ldif_ops;
+ /* PRIVATE STUFF - DO NOT TOUCH */
+ /* for efficiency, the implementation allocates memory */
+ /* in large blobs, and makes the above fields point to */
+ /* locations inside those blobs - one consequence is that */
+ /* you cannot simply free the above allocated fields, nor */
+ /* assign them to be owned by another memory context which */
+ /* might free them (unless providing your own mem ctx) */
+ /* we use the fields below to keep track of those blobs */
+ /* so we that we can free them later */
+ void *lr_ctx; /* the memory context or NULL */
+ int lr_lines;
+ LDAPMod *lr_lm;
+ unsigned char *lr_mops;
+ char *lr_freeval;
+ struct berval *lr_vals;
+ struct berval *lr_btype;
+} LDIFRecord;
+
+/* free internal fields - does not free the LDIFRecord */
+LDAP_F( void )
+ldap_ldif_record_done LDAP_P((
+ LDIFRecord *lr ));
+
+LDAP_F( int )
+ldap_parse_ldif_record LDAP_P((
+ struct berval *rbuf,
+ unsigned long linenum,
+ LDIFRecord *lr,
+ const char *errstr,
+ unsigned int flags ));
+
+LDAP_END_DECL
+#endif /* _LDAP_H */
diff --git a/include/ldap_avl.h b/include/ldap_avl.h
new file mode 100644
index 0000000..4bb1c39
--- /dev/null
+++ b/include/ldap_avl.h
@@ -0,0 +1,165 @@
+/* ldap_avl.h - avl tree definitions */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1993 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+
+#ifndef _AVL
+#define _AVL
+
+#include <ldap_cdefs.h>
+
+/*
+ * this structure represents a generic avl tree node.
+ */
+
+LDAP_BEGIN_DECL
+
+typedef struct avlnode Avlnode;
+
+struct avlnode {
+ void* avl_data;
+ struct avlnode *avl_link[2];
+ char avl_bits[2];
+ signed char avl_bf;
+};
+
+#define avl_left avl_link[0]
+#define avl_right avl_link[1]
+#define avl_lbit avl_bits[0]
+#define avl_rbit avl_bits[1]
+
+typedef struct tavlnode TAvlnode;
+
+struct tavlnode {
+ void* avl_data;
+ struct tavlnode *avl_link[2];
+ char avl_bits[2];
+ signed char avl_bf;
+};
+
+#ifdef AVL_INTERNAL
+
+/* balance factor values */
+#define LH (-1)
+#define EH 0
+#define RH 1
+
+#define avl_bf2str(bf) ((bf) == -1 ? "LH" : (bf) == 0 ? "EH" : (bf) == 1 ? "RH" : "(unknown)" )
+
+/* thread bits */
+#define AVL_CHILD 0
+#define AVL_THREAD 1
+
+/* avl routines */
+#define ldap_avl_getone(x) ((x) == 0 ? 0 : (x)->avl_data)
+#define ldap_avl_onenode(x) ((x) == 0 || ((x)->avl_left == 0 && (x)->avl_right == 0))
+
+#endif /* AVL_INTERNALS */
+
+#define ldap_avl_child(x,dir) ((x)->avl_bits[dir]) == AVL_CHILD ? \
+ (x)->avl_link[dir] : NULL
+#define ldap_avl_lchild(x) ldap_avl_child(x,0)
+#define ldap_avl_rchild(x) ldap_avl_child(x,1)
+
+typedef int (*AVL_APPLY) LDAP_P((void *, void*));
+typedef int (*AVL_CMP) LDAP_P((const void*, const void*));
+typedef int (*AVL_DUP) LDAP_P((void*, void*));
+typedef void (*AVL_FREE) LDAP_P((void*));
+
+LDAP_AVL_F( int )
+ldap_avl_free LDAP_P(( Avlnode *root, AVL_FREE dfree ));
+
+LDAP_AVL_F( int )
+ldap_avl_insert LDAP_P((Avlnode **, void*, AVL_CMP, AVL_DUP));
+
+LDAP_AVL_F( void* )
+ldap_avl_delete LDAP_P((Avlnode **, void*, AVL_CMP));
+
+LDAP_AVL_F( void* )
+ldap_avl_find LDAP_P((Avlnode *, const void*, AVL_CMP));
+
+LDAP_AVL_F( Avlnode* )
+ldap_avl_find2 LDAP_P((Avlnode *, const void*, AVL_CMP));
+
+LDAP_AVL_F( void* )
+ldap_avl_find_lin LDAP_P((Avlnode *, const void*, AVL_CMP));
+
+#ifdef AVL_NONREENTRANT
+LDAP_AVL_F( void* )
+ldap_avl_getfirst LDAP_P((Avlnode *));
+
+LDAP_AVL_F( void* )
+ldap_avl_getnext LDAP_P((void));
+#endif
+
+LDAP_AVL_F( int )
+ldap_avl_dup_error LDAP_P((void*, void*));
+
+LDAP_AVL_F( int )
+ldap_avl_dup_ok LDAP_P((void*, void*));
+
+LDAP_AVL_F( int )
+ldap_avl_apply LDAP_P((Avlnode *, AVL_APPLY, void*, int, int));
+
+LDAP_AVL_F( int )
+ldap_avl_prefixapply LDAP_P((Avlnode *, void*, AVL_CMP, void*, AVL_CMP, void*, int));
+
+LDAP_AVL_F( int )
+ldap_tavl_free LDAP_P(( TAvlnode *root, AVL_FREE dfree ));
+
+LDAP_AVL_F( int )
+ldap_tavl_insert LDAP_P((TAvlnode **, void*, AVL_CMP, AVL_DUP));
+
+LDAP_AVL_F( void* )
+ldap_tavl_delete LDAP_P((TAvlnode **, void*, AVL_CMP));
+
+LDAP_AVL_F( void* )
+ldap_tavl_find LDAP_P((TAvlnode *, const void*, AVL_CMP));
+
+LDAP_AVL_F( TAvlnode* )
+ldap_tavl_find2 LDAP_P((TAvlnode *, const void*, AVL_CMP));
+
+LDAP_AVL_F( TAvlnode* )
+ldap_tavl_find3 LDAP_P((TAvlnode *, const void*, AVL_CMP, int *ret));
+
+#define TAVL_DIR_LEFT 0
+#define TAVL_DIR_RIGHT 1
+
+LDAP_AVL_F( TAvlnode* )
+ldap_tavl_end LDAP_P((TAvlnode *, int direction));
+
+LDAP_AVL_F( TAvlnode* )
+ldap_tavl_next LDAP_P((TAvlnode *, int direction));
+
+/* apply traversal types */
+#define AVL_PREORDER 1
+#define AVL_INORDER 2
+#define AVL_POSTORDER 3
+/* what apply returns if it ran out of nodes */
+#define AVL_NOMORE (-6)
+
+LDAP_END_DECL
+
+#endif /* _AVL */
diff --git a/include/ldap_cdefs.h b/include/ldap_cdefs.h
new file mode 100644
index 0000000..2f5fa7f
--- /dev/null
+++ b/include/ldap_cdefs.h
@@ -0,0 +1,248 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* LDAP C Defines */
+
+#ifndef _LDAP_CDEFS_H
+#define _LDAP_CDEFS_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+# define LDAP_BEGIN_DECL extern "C" {
+# define LDAP_END_DECL }
+#else
+# define LDAP_BEGIN_DECL /* begin declarations */
+# define LDAP_END_DECL /* end declarations */
+#endif
+
+#if !defined(LDAP_NO_PROTOTYPES) && ( defined(LDAP_NEEDS_PROTOTYPES) || \
+ defined(__STDC__) || defined(__cplusplus) || defined(c_plusplus) )
+
+ /* ANSI C or C++ */
+# define LDAP_P(protos) protos
+# define LDAP_CONCAT1(x,y) x ## y
+# define LDAP_CONCAT(x,y) LDAP_CONCAT1(x,y)
+# define LDAP_STRING(x) #x /* stringify without expanding x */
+# define LDAP_XSTRING(x) LDAP_STRING(x) /* expand x, then stringify */
+
+#ifndef LDAP_CONST
+# define LDAP_CONST const
+#endif
+
+#else /* no prototypes */
+
+ /* traditional C */
+# define LDAP_P(protos) ()
+# define LDAP_CONCAT(x,y) x/**/y
+# define LDAP_STRING(x) "x"
+
+#ifndef LDAP_CONST
+# define LDAP_CONST /* no const */
+#endif
+
+#endif /* no prototypes */
+
+#if (__GNUC__) * 1000 + (__GNUC_MINOR__) >= 2006
+# define LDAP_GCCATTR(attrs) __attribute__(attrs)
+#else
+# define LDAP_GCCATTR(attrs)
+#endif
+
+/*
+ * Support for Windows DLLs.
+ *
+ * When external source code includes header files for dynamic libraries,
+ * the external source code is "importing" DLL symbols into its resulting
+ * object code. On Windows, symbols imported from DLLs must be explicitly
+ * indicated in header files with the __declspec(dllimport) directive.
+ * This is not totally necessary for functions because the compiler
+ * (gcc or MSVC) will generate stubs when this directive is absent.
+ * However, this is required for imported variables.
+ *
+ * The LDAP libraries, i.e. liblber and libldap, can be built as
+ * static or shared, based on configuration. Just about all other source
+ * code in OpenLDAP use these libraries. If the LDAP libraries
+ * are configured as shared, 'configure' defines the LDAP_LIBS_DYNAMIC
+ * macro. When other source files include LDAP library headers, the
+ * LDAP library symbols will automatically be marked as imported. When
+ * the actual LDAP libraries are being built, the symbols will not
+ * be marked as imported because the LBER_LIBRARY or LDAP_LIBRARY macros
+ * will be respectively defined.
+ *
+ * Any project outside of OpenLDAP with source code wanting to use
+ * LDAP dynamic libraries should explicitly define LDAP_LIBS_DYNAMIC.
+ * This will ensure that external source code appropriately marks symbols
+ * that will be imported.
+ *
+ * The slapd executable, itself, can be used as a dynamic library.
+ * For example, if a backend module is compiled as shared, it will
+ * import symbols from slapd. When this happens, the slapd symbols
+ * must be marked as imported in header files that the backend module
+ * includes. Remember that slapd links with various static libraries.
+ * If the LDAP libraries were configured as static, their object
+ * code is also part of the monolithic slapd executable. Thus, when
+ * a backend module imports symbols from slapd, it imports symbols from
+ * all of the static libraries in slapd as well. Thus, the SLAP_IMPORT
+ * macro, when defined, will appropriately mark symbols as imported.
+ * This macro should be used by shared backend modules as well as any
+ * other external source code that imports symbols from the slapd
+ * executable as if it were a DLL.
+ *
+ * Note that we don't actually have to worry about using the
+ * __declspec(dllexport) directive anywhere. This is because both
+ * MSVC and Mingw provide alternate (more effective) methods for exporting
+ * symbols out of binaries, i.e. the use of a DEF file.
+ *
+ * NOTE ABOUT BACKENDS: Backends can be configured as static or dynamic.
+ * When a backend is configured as dynamic, slapd will load the backend
+ * explicitly and populate function pointer structures by calling
+ * the backend's well-known initialization function. Because of this
+ * procedure, slapd never implicitly imports symbols from dynamic backends.
+ * This makes it unnecessary to tag various backend functions with the
+ * __declspec(dllimport) directive. This is because neither slapd nor
+ * any other external binary should ever be implicitly loading a backend
+ * dynamic module.
+ *
+ * Backends are supposed to be self-contained. However, it appears that
+ * back-meta DOES implicitly import symbols from back-ldap. This means
+ * that the __declspec(dllimport) directive should be marked on back-ldap
+ * functions (in its header files) if and only if we're compiling for
+ * windows AND back-ldap has been configured as dynamic AND back-meta
+ * is the client of back-ldap. When client is slapd, there is no effect
+ * since slapd does not implicitly import symbols.
+ *
+ * TODO(?): Currently, back-meta nor back-ldap is supported for Mingw32.
+ * Thus, there's no need to worry about this right now. This is something that
+ * may or may not have to be addressed in the future.
+ */
+
+/* LBER library */
+#if defined(_WIN32) && \
+ ((defined(LDAP_LIBS_DYNAMIC) && !defined(LBER_LIBRARY)) || \
+ (!defined(LDAP_LIBS_DYNAMIC) && defined(SLAPD_IMPORT)))
+# define LBER_F(type) extern __declspec(dllimport) type
+# define LBER_V(type) extern __declspec(dllimport) type
+#else
+# define LBER_F(type) extern type
+# define LBER_V(type) extern type
+#endif
+
+/* LDAP library */
+#if defined(_WIN32) && \
+ ((defined(LDAP_LIBS_DYNAMIC) && !defined(LDAP_LIBRARY)) || \
+ (!defined(LDAP_LIBS_DYNAMIC) && defined(SLAPD_IMPORT)))
+# define LDAP_F(type) extern __declspec(dllimport) type
+# define LDAP_V(type) extern __declspec(dllimport) type
+#else
+# define LDAP_F(type) extern type
+# define LDAP_V(type) extern type
+#endif
+
+/* AVL library */
+#if defined(_WIN32) && defined(SLAPD_IMPORT)
+# define LDAP_AVL_F(type) extern __declspec(dllimport) type
+# define LDAP_AVL_V(type) extern __declspec(dllimport) type
+#else
+# define LDAP_AVL_F(type) extern type
+# define LDAP_AVL_V(type) extern type
+#endif
+
+/* LDIF library */
+#if defined(_WIN32) && defined(SLAPD_IMPORT)
+# define LDAP_LDIF_F(type) extern __declspec(dllimport) type
+# define LDAP_LDIF_V(type) extern __declspec(dllimport) type
+#else
+# define LDAP_LDIF_F(type) extern type
+# define LDAP_LDIF_V(type) extern type
+#endif
+
+/* LUNICODE library */
+#if defined(_WIN32) && defined(SLAPD_IMPORT)
+# define LDAP_LUNICODE_F(type) extern __declspec(dllimport) type
+# define LDAP_LUNICODE_V(type) extern __declspec(dllimport) type
+#else
+# define LDAP_LUNICODE_F(type) extern type
+# define LDAP_LUNICODE_V(type) extern type
+#endif
+
+/* LUTIL library */
+#if defined(_WIN32) && defined(SLAPD_IMPORT)
+# define LDAP_LUTIL_F(type) extern __declspec(dllimport) type
+# define LDAP_LUTIL_V(type) extern __declspec(dllimport) type
+#else
+# define LDAP_LUTIL_F(type) extern type
+# define LDAP_LUTIL_V(type) extern type
+#endif
+
+/* REWRITE library */
+#if defined(_WIN32) && defined(SLAPD_IMPORT)
+# define LDAP_REWRITE_F(type) extern __declspec(dllimport) type
+# define LDAP_REWRITE_V(type) extern __declspec(dllimport) type
+#else
+# define LDAP_REWRITE_F(type) extern type
+# define LDAP_REWRITE_V(type) extern type
+#endif
+
+/* SLAPD (as a dynamic library exporting symbols) */
+#if defined(_WIN32) && defined(SLAPD_IMPORT)
+# define LDAP_SLAPD_F(type) extern __declspec(dllimport) type
+# define LDAP_SLAPD_V(type) extern __declspec(dllimport) type
+#else
+# define LDAP_SLAPD_F(type) extern type
+# define LDAP_SLAPD_V(type) extern type
+#endif
+
+/* SLAPD (as a dynamic library exporting symbols) */
+#if defined(_WIN32) && defined(SLAPD_IMPORT)
+# define LDAP_SLAPI_F(type) extern __declspec(dllimport) type
+# define LDAP_SLAPI_V(type) extern __declspec(dllimport) type
+#else
+# define LDAP_SLAPI_F(type) extern type
+# define LDAP_SLAPI_V(type) extern type
+#endif
+
+/* SLAPD (as a dynamic library exporting symbols) */
+#if defined(_WIN32) && defined(SLAPD_IMPORT)
+# define SLAPI_F(type) extern __declspec(dllimport) type
+# define SLAPI_V(type) extern __declspec(dllimport) type
+#else
+# define SLAPI_F(type) extern type
+# define SLAPI_V(type) extern type
+#endif
+
+/*
+ * C library. Mingw32 links with the dynamic C run-time library by default,
+ * so the explicit definition of CSTATIC will keep dllimport from
+ * being defined, if desired.
+ *
+ * MSVC defines the _DLL macro when the compiler is invoked with /MD or /MDd,
+ * which means the resulting object code will be linked with the dynamic
+ * C run-time library.
+ *
+ * Technically, it shouldn't be necessary to redefine any functions that
+ * the headers for the C library should already contain. Nevertheless, this
+ * is here as a safe-guard.
+ *
+ * TODO: Determine if these macros ever get expanded for Windows. If not,
+ * the declspec expansion can probably be removed.
+ */
+#if (defined(__MINGW32__) && !defined(CSTATIC)) || \
+ (defined(_MSC_VER) && defined(_DLL))
+# define LDAP_LIBC_F(type) extern __declspec(dllimport) type
+# define LDAP_LIBC_V(type) extern __declspec(dllimport) type
+#else
+# define LDAP_LIBC_F(type) extern type
+# define LDAP_LIBC_V(type) extern type
+#endif
+
+#endif /* _LDAP_CDEFS_H */
diff --git a/include/ldap_config.hin b/include/ldap_config.hin
new file mode 100644
index 0000000..8146995
--- /dev/null
+++ b/include/ldap_config.hin
@@ -0,0 +1,73 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * This file works in conjunction with OpenLDAP configure system.
+ * If you do no like the values below, adjust your configure options.
+ */
+
+#ifndef _LDAP_CONFIG_H
+#define _LDAP_CONFIG_H
+
+/* directory separator */
+#ifndef LDAP_DIRSEP
+#ifndef _WIN32
+#define LDAP_DIRSEP "/"
+#else
+#define LDAP_DIRSEP "\\"
+#endif
+#endif
+
+/* directory for temporary files */
+#if defined(_WIN32)
+# define LDAP_TMPDIR "C:\\." /* we don't have much of a choice */
+#elif defined( _P_tmpdir )
+# define LDAP_TMPDIR _P_tmpdir
+#elif defined( P_tmpdir )
+# define LDAP_TMPDIR P_tmpdir
+#elif defined( _PATH_TMPDIR )
+# define LDAP_TMPDIR _PATH_TMPDIR
+#else
+# define LDAP_TMPDIR LDAP_DIRSEP "tmp"
+#endif
+
+/* directories */
+#ifndef LDAP_BINDIR
+#define LDAP_BINDIR "%BINDIR%"
+#endif
+#ifndef LDAP_SBINDIR
+#define LDAP_SBINDIR "%SBINDIR%"
+#endif
+#ifndef LDAP_DATADIR
+#define LDAP_DATADIR "%DATADIR%"
+#endif
+#ifndef LDAP_SYSCONFDIR
+#define LDAP_SYSCONFDIR "%SYSCONFDIR%"
+#endif
+#ifndef LDAP_LIBEXECDIR
+#define LDAP_LIBEXECDIR "%LIBEXECDIR%"
+#endif
+#ifndef LDAP_MODULEDIR
+#define LDAP_MODULEDIR "%MODULEDIR%"
+#endif
+#ifndef LDAP_RUNDIR
+#define LDAP_RUNDIR "%RUNDIR%"
+#endif
+#ifndef LDAP_LOCALEDIR
+#define LDAP_LOCALEDIR "%LOCALEDIR%"
+#endif
+
+
+#endif /* _LDAP_CONFIG_H */
diff --git a/include/ldap_defaults.h b/include/ldap_defaults.h
new file mode 100644
index 0000000..834c001
--- /dev/null
+++ b/include/ldap_defaults.h
@@ -0,0 +1,71 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1994 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+/*
+ * This file controls defaults for OpenLDAP package.
+ * You probably do not need to edit the defaults provided by this file.
+ */
+
+#ifndef _LDAP_DEFAULTS_H
+#define _LDAP_DEFAULTS_H
+
+
+#include <ldap_config.h>
+
+#define LDAP_CONF_FILE LDAP_SYSCONFDIR LDAP_DIRSEP "ldap.conf"
+#define LDAP_USERRC_FILE "ldaprc"
+#define LDAP_ENV_PREFIX "LDAP"
+
+/* default ldapi:// socket */
+#define LDAPI_SOCK LDAP_RUNDIR LDAP_DIRSEP "run" LDAP_DIRSEP "ldapi"
+
+/*
+ * SLAPD DEFINITIONS
+ */
+ /* location of the default slapd config file */
+#define SLAPD_DEFAULT_CONFIGFILE LDAP_SYSCONFDIR LDAP_DIRSEP "slapd.conf"
+#define SLAPD_DEFAULT_CONFIGDIR LDAP_SYSCONFDIR LDAP_DIRSEP "slapd.d"
+#define SLAPD_DEFAULT_DB_DIR LDAP_RUNDIR LDAP_DIRSEP "openldap-data"
+#define SLAPD_DEFAULT_DB_MODE 0600
+#define SLAPD_DEFAULT_UCDATA LDAP_DATADIR LDAP_DIRSEP "ucdata"
+ /* default max deref depth for aliases */
+#define SLAPD_DEFAULT_MAXDEREFDEPTH 15
+ /* default sizelimit on number of entries from a search */
+#define SLAPD_DEFAULT_SIZELIMIT 500
+ /* default timelimit to spend on a search */
+#define SLAPD_DEFAULT_TIMELIMIT 3600
+
+/* the following DNs must be normalized! */
+ /* dn of the default subschema subentry */
+#define SLAPD_SCHEMA_DN "cn=Subschema"
+ /* dn of the default "monitor" subentry */
+#define SLAPD_MONITOR_DN "cn=Monitor"
+
+/*
+ * LLOADD DEFINITIONS
+ */
+#define LLOADD_DEFAULT_CONFIGFILE LDAP_SYSCONFDIR LDAP_DIRSEP "lloadd.conf"
+
+#endif /* _LDAP_CONFIG_H */
diff --git a/include/ldap_features.hin b/include/ldap_features.hin
new file mode 100644
index 0000000..91ca099
--- /dev/null
+++ b/include/ldap_features.hin
@@ -0,0 +1,55 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * LDAP Features
+ */
+
+#ifndef _LDAP_FEATURES_H
+#define _LDAP_FEATURES_H 1
+
+/* OpenLDAP API version macros */
+#undef LDAP_VENDOR_VERSION
+#undef LDAP_VENDOR_VERSION_MAJOR
+#undef LDAP_VENDOR_VERSION_MINOR
+#undef LDAP_VENDOR_VERSION_PATCH
+
+/*
+** WORK IN PROGRESS!
+**
+** OpenLDAP reentrancy/thread-safeness should be dynamically
+** checked using ldap_get_option().
+**
+** If built with thread support, the -lldap implementation is:
+** LDAP_API_FEATURE_THREAD_SAFE (basic thread safety)
+** LDAP_API_FEATURE_SESSION_THREAD_SAFE
+** LDAP_API_FEATURE_OPERATION_THREAD_SAFE
+**
+** The preprocessor flag LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE
+** can be used to determine if -lldap is thread safe at compile
+** time.
+**
+*/
+
+/* is -lldap reentrant or not */
+#undef LDAP_API_FEATURE_X_OPENLDAP_REENTRANT
+
+/* is -lldap thread safe or not */
+#undef LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE
+
+/* LDAP v2 Referrals */
+#undef LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS
+
+#endif /* LDAP_FEATURES */
diff --git a/include/ldap_int_thread.h b/include/ldap_int_thread.h
new file mode 100644
index 0000000..2c82d68
--- /dev/null
+++ b/include/ldap_int_thread.h
@@ -0,0 +1,290 @@
+/* ldap_int_thread.h - ldap internal thread wrappers header file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+
+LDAP_BEGIN_DECL
+
+/* Can be done twice. See libldap/ldap_thr_debug.h. */
+LDAP_F(int) ldap_int_thread_initialize LDAP_P(( void ));
+LDAP_F(int) ldap_int_thread_destroy LDAP_P(( void ));
+
+LDAP_END_DECL
+
+#ifndef _LDAP_INT_THREAD_H
+#define _LDAP_INT_THREAD_H
+
+#if defined( HAVE_PTHREADS )
+/**********************************
+ * *
+ * definitions for POSIX Threads *
+ * *
+ **********************************/
+
+#include <pthread.h>
+#ifdef HAVE_SCHED_H
+#include <sched.h>
+#endif
+
+LDAP_BEGIN_DECL
+
+typedef pthread_t ldap_int_thread_t;
+typedef pthread_mutex_t ldap_int_thread_mutex_t;
+typedef pthread_cond_t ldap_int_thread_cond_t;
+typedef pthread_key_t ldap_int_thread_key_t;
+
+#define ldap_int_thread_equal(a, b) pthread_equal((a), (b))
+
+#if defined( _POSIX_REENTRANT_FUNCTIONS ) || \
+ defined( _POSIX_THREAD_SAFE_FUNCTIONS ) || \
+ defined( _POSIX_THREADSAFE_FUNCTIONS )
+#define HAVE_REENTRANT_FUNCTIONS 1
+#endif
+
+#if defined( HAVE_PTHREAD_GETCONCURRENCY ) || \
+ defined( HAVE_THR_GETCONCURRENCY )
+#define LDAP_THREAD_HAVE_GETCONCURRENCY 1
+#endif
+
+#if defined( HAVE_PTHREAD_SETCONCURRENCY ) || \
+ defined( HAVE_THR_SETCONCURRENCY )
+#define LDAP_THREAD_HAVE_SETCONCURRENCY 1
+#endif
+
+#if defined( HAVE_PTHREAD_RWLOCK_DESTROY )
+#define LDAP_THREAD_HAVE_RDWR 1
+typedef pthread_rwlock_t ldap_int_thread_rdwr_t;
+#endif
+
+#ifndef LDAP_INT_MUTEX_NULL
+#define LDAP_INT_MUTEX_NULL PTHREAD_MUTEX_INITIALIZER
+#define LDAP_INT_MUTEX_FIRSTCREATE(m) ((void) 0)
+#endif
+
+LDAP_END_DECL
+
+#elif defined( HAVE_GNU_PTH )
+/***********************************
+ * *
+ * thread definitions for GNU Pth *
+ * *
+ ***********************************/
+
+#define PTH_SYSCALL_SOFT 1
+#include <pth.h>
+
+LDAP_BEGIN_DECL
+
+typedef pth_t ldap_int_thread_t;
+typedef pth_mutex_t ldap_int_thread_mutex_t;
+typedef pth_cond_t ldap_int_thread_cond_t;
+typedef pth_key_t ldap_int_thread_key_t;
+
+#if 0
+#define LDAP_THREAD_HAVE_RDWR 1
+typedef pth_rwlock_t ldap_int_thread_rdwr_t;
+#endif
+
+#ifndef LDAP_INT_MUTEX_NULL
+#define LDAP_INT_MUTEX_NULL PTH_MUTEX_INIT
+#define LDAP_INT_MUTEX_FIRSTCREATE(m) ((void) 0)
+#endif
+
+LDAP_END_DECL
+
+#elif defined( HAVE_THR )
+/********************************************
+ * *
+ * thread definitions for Solaris LWP (THR) *
+ * *
+ ********************************************/
+
+#include <thread.h>
+#include <synch.h>
+
+LDAP_BEGIN_DECL
+
+typedef thread_t ldap_int_thread_t;
+typedef mutex_t ldap_int_thread_mutex_t;
+typedef cond_t ldap_int_thread_cond_t;
+typedef thread_key_t ldap_int_thread_key_t;
+
+#define HAVE_REENTRANT_FUNCTIONS 1
+
+#ifdef HAVE_THR_GETCONCURRENCY
+#define LDAP_THREAD_HAVE_GETCONCURRENCY 1
+#endif
+#ifdef HAVE_THR_SETCONCURRENCY
+#define LDAP_THREAD_HAVE_SETCONCURRENCY 1
+#endif
+
+#ifndef LDAP_INT_MUTEX_NULL
+#define LDAP_INT_MUTEX_NULL DEFAULTMUTEX
+#define LDAP_INT_MUTEX_FIRSTCREATE(m) ((void) 0)
+#endif
+
+#elif defined(HAVE_NT_THREADS)
+/*************************************
+ * *
+ * thread definitions for NT threads *
+ * *
+ *************************************/
+
+#include <process.h>
+#include <windows.h>
+
+LDAP_BEGIN_DECL
+
+typedef unsigned long ldap_int_thread_t;
+typedef HANDLE ldap_int_thread_mutex_t;
+typedef HANDLE ldap_int_thread_cond_t;
+typedef DWORD ldap_int_thread_key_t;
+
+LDAP_F( int )
+ldap_int_mutex_firstcreate LDAP_P(( ldap_int_thread_mutex_t *mutex ));
+
+#ifndef LDAP_INT_MUTEX_NULL
+#define LDAP_INT_MUTEX_NULL ((HANDLE)0)
+#define LDAP_INT_MUTEX_FIRSTCREATE(m) \
+ ldap_int_mutex_firstcreate(&(m))
+#endif
+
+LDAP_END_DECL
+
+#else
+/***********************************
+ * *
+ * thread definitions for no *
+ * underlying library support *
+ * *
+ ***********************************/
+
+#ifndef NO_THREADS
+#define NO_THREADS 1
+#endif
+
+LDAP_BEGIN_DECL
+
+typedef int ldap_int_thread_t;
+typedef int ldap_int_thread_mutex_t;
+typedef int ldap_int_thread_cond_t;
+typedef int ldap_int_thread_key_t;
+
+#define LDAP_THREAD_HAVE_TPOOL 1
+typedef int ldap_int_thread_pool_t;
+
+#ifndef LDAP_INT_MUTEX_NULL
+#define LDAP_INT_MUTEX_NULL 0
+#define LDAP_INT_MUTEX_FIRSTCREATE(m) ((void) 0)
+#endif
+
+LDAP_END_DECL
+
+#endif /* no threads support */
+
+
+LDAP_BEGIN_DECL
+
+#ifndef ldap_int_thread_equal
+#define ldap_int_thread_equal(a, b) ((a) == (b))
+#endif
+
+#ifndef LDAP_THREAD_HAVE_RDWR
+typedef struct ldap_int_thread_rdwr_s * ldap_int_thread_rdwr_t;
+#endif
+
+LDAP_F(int) ldap_int_thread_pool_startup ( void );
+LDAP_F(int) ldap_int_thread_pool_shutdown ( void );
+
+#ifndef LDAP_THREAD_HAVE_TPOOL
+typedef struct ldap_int_thread_pool_s * ldap_int_thread_pool_t;
+#endif
+LDAP_END_DECL
+
+
+#if defined(LDAP_THREAD_DEBUG) && !((LDAP_THREAD_DEBUG +0) & 2U)
+#define LDAP_THREAD_DEBUG_WRAP 1
+#endif
+
+#ifdef LDAP_THREAD_DEBUG_WRAP
+/**************************************
+ * *
+ * definitions for type-wrapped debug *
+ * *
+ **************************************/
+
+LDAP_BEGIN_DECL
+
+#ifndef LDAP_UINTPTR_T /* May be configured in CPPFLAGS */
+#define LDAP_UINTPTR_T unsigned long
+#endif
+
+typedef enum {
+ ldap_debug_magic = -(int) (((unsigned)-1)/19)
+} ldap_debug_magic_t;
+
+typedef enum {
+ /* Could fill in "locked" etc here later */
+ ldap_debug_state_inited = (int) (((unsigned)-1)/11),
+ ldap_debug_state_destroyed
+} ldap_debug_state_t;
+
+typedef struct {
+ /* Enclosed in magic numbers in the hope of catching overwrites */
+ ldap_debug_magic_t magic; /* bit pattern to recognize usages */
+ LDAP_UINTPTR_T self; /* ~(LDAP_UINTPTR_T)&(this struct) */
+ union ldap_debug_mem_u { /* Dummy memory reference */
+ unsigned char *ptr;
+ LDAP_UINTPTR_T num;
+ } mem;
+ ldap_debug_state_t state; /* doubles as another magic number */
+} ldap_debug_usage_info_t;
+
+typedef struct {
+ ldap_int_thread_mutex_t wrapped;
+ ldap_debug_usage_info_t usage;
+ ldap_int_thread_t owner;
+} ldap_debug_thread_mutex_t;
+
+#define LDAP_DEBUG_MUTEX_NULL {LDAP_INT_MUTEX_NULL, {0,0,{0},0} /*,owner*/}
+#define LDAP_DEBUG_MUTEX_FIRSTCREATE(m) \
+ ((void) ((m).usage.state || ldap_pvt_thread_mutex_init(&(m))))
+
+typedef struct {
+ ldap_int_thread_cond_t wrapped;
+ ldap_debug_usage_info_t usage;
+} ldap_debug_thread_cond_t;
+
+typedef struct {
+ ldap_int_thread_rdwr_t wrapped;
+ ldap_debug_usage_info_t usage;
+} ldap_debug_thread_rdwr_t;
+
+#ifndef NDEBUG
+#define LDAP_INT_THREAD_ASSERT_MUTEX_OWNER(mutex) \
+ ldap_debug_thread_assert_mutex_owner( \
+ __FILE__, __LINE__, "owns(" #mutex ")", mutex )
+LDAP_F(void) ldap_debug_thread_assert_mutex_owner LDAP_P((
+ LDAP_CONST char *file,
+ int line,
+ LDAP_CONST char *msg,
+ ldap_debug_thread_mutex_t *mutex ));
+#endif /* NDEBUG */
+
+LDAP_END_DECL
+
+#endif /* LDAP_THREAD_DEBUG_WRAP */
+
+#endif /* _LDAP_INT_THREAD_H */
diff --git a/include/ldap_log.h b/include/ldap_log.h
new file mode 100644
index 0000000..8b8b0b6
--- /dev/null
+++ b/include/ldap_log.h
@@ -0,0 +1,211 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#ifndef LDAP_LOG_H
+#define LDAP_LOG_H
+
+#include <stdio.h>
+#include <ldap_cdefs.h>
+
+LDAP_BEGIN_DECL
+
+/*
+ * debug reporting levels.
+ *
+ * They start with the syslog levels, and
+ * go down in importance. The normal
+ * debugging levels begin with LDAP_LEVEL_ENTRY
+ *
+ */
+
+/*
+ * The "OLD_DEBUG" means that all logging occurs at LOG_DEBUG
+ */
+
+#ifdef OLD_DEBUG
+/* original behavior: all logging occurs at the same severity level */
+#if defined(LDAP_DEBUG) && defined(LDAP_SYSLOG)
+#define LDAP_LEVEL_EMERG ldap_syslog_level
+#define LDAP_LEVEL_ALERT ldap_syslog_level
+#define LDAP_LEVEL_CRIT ldap_syslog_level
+#define LDAP_LEVEL_ERR ldap_syslog_level
+#define LDAP_LEVEL_WARNING ldap_syslog_level
+#define LDAP_LEVEL_NOTICE ldap_syslog_level
+#define LDAP_LEVEL_INFO ldap_syslog_level
+#define LDAP_LEVEL_DEBUG ldap_syslog_level
+#else /* !LDAP_DEBUG || !LDAP_SYSLOG */
+#define LDAP_LEVEL_EMERG (7)
+#define LDAP_LEVEL_ALERT (7)
+#define LDAP_LEVEL_CRIT (7)
+#define LDAP_LEVEL_ERR (7)
+#define LDAP_LEVEL_WARNING (7)
+#define LDAP_LEVEL_NOTICE (7)
+#define LDAP_LEVEL_INFO (7)
+#define LDAP_LEVEL_DEBUG (7)
+#endif /* !LDAP_DEBUG || !LDAP_SYSLOG */
+
+#else /* ! OLD_DEBUG */
+/* map syslog onto LDAP severity levels */
+#ifdef LOG_DEBUG
+#define LDAP_LEVEL_EMERG LOG_EMERG
+#define LDAP_LEVEL_ALERT LOG_ALERT
+#define LDAP_LEVEL_CRIT LOG_CRIT
+#define LDAP_LEVEL_ERR LOG_ERR
+#define LDAP_LEVEL_WARNING LOG_WARNING
+#define LDAP_LEVEL_NOTICE LOG_NOTICE
+#define LDAP_LEVEL_INFO LOG_INFO
+#define LDAP_LEVEL_DEBUG LOG_DEBUG
+#else /* ! LOG_DEBUG */
+#define LDAP_LEVEL_EMERG (0)
+#define LDAP_LEVEL_ALERT (1)
+#define LDAP_LEVEL_CRIT (2)
+#define LDAP_LEVEL_ERR (3)
+#define LDAP_LEVEL_WARNING (4)
+#define LDAP_LEVEL_NOTICE (5)
+#define LDAP_LEVEL_INFO (6)
+#define LDAP_LEVEL_DEBUG (7)
+#endif /* ! LOG_DEBUG */
+#endif /* ! OLD_DEBUG */
+#if 0
+/* in case we need to reuse the unused bits of severity */
+#define LDAP_LEVEL_MASK(s) ((s) & 0x7)
+#else
+#define LDAP_LEVEL_MASK(s) (s)
+#endif
+
+/* (yet) unused */
+#define LDAP_LEVEL_ENTRY (0x08) /* log function entry points */
+#define LDAP_LEVEL_ARGS (0x10) /* log function call parameters */
+#define LDAP_LEVEL_RESULTS (0x20) /* Log function results */
+#define LDAP_LEVEL_DETAIL1 (0x40) /* log level 1 function operational details */
+#define LDAP_LEVEL_DETAIL2 (0x80) /* Log level 2 function operational details */
+/* end of (yet) unused */
+
+/* original subsystem selection mechanism */
+#define LDAP_DEBUG_TRACE 0x0001
+#define LDAP_DEBUG_PACKETS 0x0002
+#define LDAP_DEBUG_ARGS 0x0004
+#define LDAP_DEBUG_CONNS 0x0008
+#define LDAP_DEBUG_BER 0x0010
+#define LDAP_DEBUG_FILTER 0x0020
+#define LDAP_DEBUG_CONFIG 0x0040
+#define LDAP_DEBUG_ACL 0x0080
+#define LDAP_DEBUG_STATS 0x0100
+#define LDAP_DEBUG_STATS2 0x0200
+#define LDAP_DEBUG_SHELL 0x0400
+#define LDAP_DEBUG_PARSE 0x0800
+#if 0 /* no longer used (nor supported) */
+#define LDAP_DEBUG_CACHE 0x1000
+#define LDAP_DEBUG_INDEX 0x2000
+#endif
+#define LDAP_DEBUG_SYNC 0x4000
+
+#define LDAP_DEBUG_NONE 0x8000
+#define LDAP_DEBUG_ANY (-1)
+
+/* debugging stuff */
+#ifdef LDAP_DEBUG
+ /*
+ * This is a bogus extern declaration for the compiler. No need to ensure
+ * a 'proper' dllimport.
+ */
+#ifndef ldap_debug
+extern int ldap_debug;
+#endif /* !ldap_debug */
+
+#ifdef LDAP_SYSLOG
+extern int ldap_syslog;
+extern int ldap_syslog_level;
+
+#ifdef HAVE_EBCDIC
+#define syslog eb_syslog
+extern void eb_syslog(int pri, const char *fmt, ...);
+#endif /* HAVE_EBCDIC */
+
+#endif /* LDAP_SYSLOG */
+#endif /* LDAP_DEBUG */
+
+/* we keep libldap working with preprocessors that can't do variadic macros */
+#ifndef LDAP_INT_DEBUG
+/* this doesn't below as part of ldap.h */
+#ifdef LDAP_DEBUG
+#ifdef LDAP_SYSLOG
+
+#define LogTest(level) ( ( ldap_debug | ldap_syslog ) & (level) )
+#define Log(level, severity, ...) \
+ do { \
+ if ( ldap_debug & (level) ) \
+ lutil_debug( ldap_debug, (level), __VA_ARGS__ ); \
+ if ( ldap_syslog & (level) ) \
+ syslog( LDAP_LEVEL_MASK((severity)), __VA_ARGS__ ); \
+ } while ( 0 )
+
+#else /* ! LDAP_SYSLOG */
+
+#define LogTest(level) ( ldap_debug & (level) )
+#define Log(level, severity, ...) \
+ do { \
+ if ( ldap_debug & (level) ) \
+ lutil_debug( ldap_debug, (level), __VA_ARGS__ ); \
+ } while ( 0 )
+
+#endif /* ! LDAP_SYSLOG */
+#else /* ! LDAP_DEBUG */
+
+/* TODO: in case LDAP_DEBUG is undefined, make sure logs with appropriate
+ * severity gets thru anyway */
+#define LogTest(level) ( 0 )
+#define Log(level, severity, ...) ((void) 0)
+
+#endif /* ! LDAP_DEBUG */
+
+#define Debug(level, ...) \
+ Log((level), ldap_syslog_level, __VA_ARGS__ )
+#endif /* ! LDAP_INT_DEBUG */
+
+/* Actually now in liblber/debug.c */
+LDAP_LUTIL_F(int) lutil_debug_file LDAP_P(( FILE *file ));
+
+LDAP_LUTIL_F(void) lutil_debug LDAP_P((
+ int debug, int level,
+ const char* fmt, ... )) LDAP_GCCATTR((format(printf, 3, 4)));
+
+#ifdef LDAP_DEFINE_LDAP_DEBUG
+/* This struct matches the head of ldapoptions in <ldap-int.h> */
+struct ldapoptions_prefix {
+ short ldo_valid;
+ int ldo_debug;
+};
+#define ldap_debug \
+ (*(int *) ((char *)&ldap_int_global_options \
+ + offsetof(struct ldapoptions_prefix, ldo_debug)))
+
+struct ldapoptions;
+LDAP_V ( struct ldapoptions ) ldap_int_global_options;
+#endif /* LDAP_DEFINE_LDAP_DEBUG */
+
+LDAP_END_DECL
+
+#endif /* LDAP_LOG_H */
diff --git a/include/ldap_pvt.h b/include/ldap_pvt.h
new file mode 100644
index 0000000..ba6ec15
--- /dev/null
+++ b/include/ldap_pvt.h
@@ -0,0 +1,588 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* ldap-pvt.h - Header for ldap_pvt_ functions.
+ * These are meant to be internal to OpenLDAP Software.
+ */
+
+#ifndef _LDAP_PVT_H
+#define _LDAP_PVT_H 1
+
+#include <openldap.h> /* get public interfaces */
+#include <lber.h> /* get ber_slen_t */
+#include <lber_pvt.h> /* get Sockbuf_Buf */
+
+LDAP_BEGIN_DECL
+
+LDAP_F ( int )
+ldap_pvt_url_scheme2proto LDAP_P((
+ const char * ));
+LDAP_F ( int )
+ldap_pvt_url_scheme2tls LDAP_P((
+ const char * ));
+LDAP_F ( int )
+ldap_pvt_url_scheme2proxied LDAP_P((
+ const char * ));
+
+LDAP_F ( int )
+ldap_pvt_url_scheme_port LDAP_P((
+ const char *, int ));
+
+struct ldap_url_desc; /* avoid pulling in <ldap.h> */
+
+#define LDAP_PVT_URL_PARSE_NONE (0x00U)
+#define LDAP_PVT_URL_PARSE_NOEMPTY_HOST (0x01U)
+#define LDAP_PVT_URL_PARSE_DEF_PORT (0x02U)
+#define LDAP_PVT_URL_PARSE_NOEMPTY_DN (0x04U)
+#define LDAP_PVT_URL_PARSE_NODEF_SCOPE (0x08U)
+#define LDAP_PVT_URL_PARSE_HISTORIC (LDAP_PVT_URL_PARSE_NODEF_SCOPE | \
+ LDAP_PVT_URL_PARSE_NOEMPTY_HOST | \
+ LDAP_PVT_URL_PARSE_DEF_PORT)
+
+LDAP_F( int )
+ldap_url_parse_ext LDAP_P((
+ LDAP_CONST char *url,
+ struct ldap_url_desc **ludpp,
+ unsigned flags ));
+
+LDAP_F (int) ldap_url_parselist LDAP_P(( /* deprecated, use ldap_url_parselist_ext() */
+ struct ldap_url_desc **ludlist,
+ const char *url ));
+
+LDAP_F (int) ldap_url_parselist_ext LDAP_P((
+ struct ldap_url_desc **ludlist,
+ const char *url,
+ const char *sep,
+ unsigned flags ));
+
+LDAP_F (char *) ldap_url_list2urls LDAP_P((
+ struct ldap_url_desc *ludlist ));
+
+LDAP_F (void) ldap_free_urllist LDAP_P((
+ struct ldap_url_desc *ludlist ));
+
+LDAP_F (int) ldap_pvt_scope2bv LDAP_P ((
+ int scope, struct berval *bv ));
+
+LDAP_F (LDAP_CONST char *) ldap_pvt_scope2str LDAP_P ((
+ int scope ));
+
+LDAP_F (int) ldap_pvt_bv2scope LDAP_P ((
+ struct berval *bv ));
+
+LDAP_F (int) ldap_pvt_str2scope LDAP_P ((
+ LDAP_CONST char * ));
+
+LDAP_F( char * )
+ldap_pvt_ctime LDAP_P((
+ const time_t *tp,
+ char *buf ));
+
+# if defined( HAVE_GMTIME_R )
+# define USE_GMTIME_R
+# define ldap_pvt_gmtime(timep, result) gmtime_r((timep), (result))
+# else
+LDAP_F( struct tm * )
+ldap_pvt_gmtime LDAP_P((
+ LDAP_CONST time_t *timep,
+ struct tm *result ));
+#endif
+
+# if defined( HAVE_LOCALTIME_R )
+# define USE_LOCALTIME_R
+# define ldap_pvt_localtime(timep, result) localtime_r((timep), (result))
+# else
+LDAP_F( struct tm * )
+ldap_pvt_localtime LDAP_P((
+ LDAP_CONST time_t *timep,
+ struct tm *result ));
+# endif
+
+#if defined( USE_GMTIME_R ) && defined( USE_LOCALTIME_R )
+# define ldap_pvt_gmtime_lock() (0)
+# define ldap_pvt_gmtime_unlock() (0)
+#else
+LDAP_F( int )
+ldap_pvt_gmtime_lock LDAP_P(( void ));
+
+LDAP_F( int )
+ldap_pvt_gmtime_unlock LDAP_P(( void ));
+#endif /* USE_GMTIME_R && USE_LOCALTIME_R */
+
+/* Get current time as a structured time */
+struct lutil_tm;
+LDAP_F( void )
+ldap_pvt_gettime LDAP_P(( struct lutil_tm * ));
+
+#ifdef _WIN32
+#define gettimeofday(tv,tz) ldap_pvt_gettimeofday(tv,tz)
+struct timeval;
+LDAP_F( int )
+ldap_pvt_gettimeofday LDAP_P(( struct timeval *tv, void *unused ));
+#ifndef CLOCK_REALTIME
+#define CLOCK_REALTIME 0
+#endif
+#define clock_gettime(clkid,tv) ldap_pvt_clock_gettime(clkid,tv)
+struct timespec;
+LDAP_F( int )
+ldap_pvt_clock_gettime LDAP_P(( int clkid, struct timespec *tv ));
+#endif
+
+/* use this macro to allocate buffer for ldap_pvt_csnstr */
+#define LDAP_PVT_CSNSTR_BUFSIZE 64
+LDAP_F( size_t )
+ldap_pvt_csnstr( char *buf, size_t len, unsigned int replica, unsigned int mod );
+
+LDAP_F( char *) ldap_pvt_get_fqdn LDAP_P(( char * ));
+
+struct hostent; /* avoid pulling in <netdb.h> */
+
+LDAP_F( int )
+ldap_pvt_gethostbyname_a LDAP_P((
+ const char *name,
+ struct hostent *resbuf,
+ char **buf,
+ struct hostent **result,
+ int *herrno_ptr ));
+
+LDAP_F( int )
+ldap_pvt_gethostbyaddr_a LDAP_P((
+ const char *addr,
+ int len,
+ int type,
+ struct hostent *resbuf,
+ char **buf,
+ struct hostent **result,
+ int *herrno_ptr ));
+
+struct sockaddr;
+
+LDAP_F( int )
+ldap_pvt_get_hname LDAP_P((
+ const struct sockaddr * sa,
+ int salen,
+ char *name,
+ int namelen,
+ char **herr ));
+
+#ifdef LDAP_PF_LOCAL
+#define LDAP_IPADDRLEN (MAXPATHLEN + sizeof("PATH="))
+#elif defined(LDAP_PF_INET6)
+#define LDAP_IPADDRLEN sizeof("IP=[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535")
+#else
+#define LDAP_IPADDRLEN sizeof("IP=255.255.255.255:65336")
+#endif
+
+union Sockaddr;
+
+LDAP_F (void)
+ldap_pvt_sockaddrstr LDAP_P((
+ union Sockaddr *sa,
+ struct berval * ));
+
+
+/* charray.c */
+
+LDAP_F( int )
+ldap_charray_add LDAP_P((
+ char ***a,
+ const char *s ));
+
+LDAP_F( int )
+ldap_charray_merge LDAP_P((
+ char ***a,
+ char **s ));
+
+LDAP_F( void )
+ldap_charray_free LDAP_P(( char **a ));
+
+LDAP_F( int )
+ldap_charray_inlist LDAP_P((
+ char **a,
+ const char *s ));
+
+LDAP_F( char ** )
+ldap_charray_dup LDAP_P(( char **a ));
+
+LDAP_F( char ** )
+ldap_str2charray LDAP_P((
+ const char *str,
+ const char *brkstr ));
+
+LDAP_F( char * )
+ldap_charray2str LDAP_P((
+ char **array, const char* sep ));
+
+/* getdn.c */
+
+#ifdef LDAP_AVA_NULL /* in ldap.h */
+LDAP_F( void ) ldap_rdnfree_x LDAP_P(( LDAPRDN rdn, void *ctx ));
+LDAP_F( void ) ldap_dnfree_x LDAP_P(( LDAPDN dn, void *ctx ));
+
+LDAP_F( int ) ldap_bv2dn_x LDAP_P((
+ struct berval *bv, LDAPDN *dn, unsigned flags, void *ctx ));
+LDAP_F( int ) ldap_dn2bv_x LDAP_P((
+ LDAPDN dn, struct berval *bv, unsigned flags, void *ctx ));
+LDAP_F( int ) ldap_bv2rdn_x LDAP_P((
+ struct berval *, LDAPRDN *, char **, unsigned flags, void *ctx ));
+LDAP_F( int ) ldap_rdn2bv_x LDAP_P((
+ LDAPRDN rdn, struct berval *bv, unsigned flags, void *ctx ));
+#endif /* LDAP_AVA_NULL */
+
+/* url.c */
+LDAP_F (void) ldap_pvt_hex_unescape LDAP_P(( char *s ));
+
+/*
+ * these macros assume 'x' is an ASCII x
+ * and assume the "C" locale
+ */
+#define LDAP_ASCII(c) (!((c) & 0x80))
+#define LDAP_SPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
+#define LDAP_DIGIT(c) ((c) >= '0' && (c) <= '9')
+#define LDAP_LOWER(c) ((c) >= 'a' && (c) <= 'z')
+#define LDAP_UPPER(c) ((c) >= 'A' && (c) <= 'Z')
+#define LDAP_ALPHA(c) (LDAP_LOWER(c) || LDAP_UPPER(c))
+#define LDAP_ALNUM(c) (LDAP_ALPHA(c) || LDAP_DIGIT(c))
+
+#define LDAP_LDH(c) (LDAP_ALNUM(c) || (c) == '-')
+
+#define LDAP_HEXLOWER(c) ((c) >= 'a' && (c) <= 'f')
+#define LDAP_HEXUPPER(c) ((c) >= 'A' && (c) <= 'F')
+#define LDAP_HEX(c) (LDAP_DIGIT(c) || \
+ LDAP_HEXLOWER(c) || LDAP_HEXUPPER(c))
+
+/* controls.c */
+struct ldapcontrol;
+LDAP_F (int)
+ldap_pvt_put_control LDAP_P((
+ const struct ldapcontrol *c,
+ BerElement *ber ));
+LDAP_F (int) ldap_pvt_get_controls LDAP_P((
+ BerElement *be,
+ struct ldapcontrol ***ctrlsp));
+
+#ifdef HAVE_CYRUS_SASL
+/* cyrus.c */
+struct sasl_security_properties; /* avoid pulling in <sasl.h> */
+LDAP_F (int) ldap_pvt_sasl_secprops LDAP_P((
+ const char *in,
+ struct sasl_security_properties *secprops ));
+LDAP_F (void) ldap_pvt_sasl_secprops_unparse LDAP_P((
+ struct sasl_security_properties *secprops,
+ struct berval *out ));
+
+LDAP_F (void *) ldap_pvt_sasl_mutex_new LDAP_P((void));
+LDAP_F (int) ldap_pvt_sasl_mutex_lock LDAP_P((void *mutex));
+LDAP_F (int) ldap_pvt_sasl_mutex_unlock LDAP_P((void *mutex));
+LDAP_F (void) ldap_pvt_sasl_mutex_dispose LDAP_P((void *mutex));
+
+LDAP_F (int) ldap_pvt_sasl_cbinding_parse LDAP_P(( const char *arg ));
+LDAP_F (void *) ldap_pvt_sasl_cbinding LDAP_P(( void *ssl, int type,
+ int is_server ));
+#endif /* HAVE_CYRUS_SASL */
+
+struct sockbuf; /* avoid pulling in <lber.h> */
+LDAP_F (int) ldap_pvt_sasl_install LDAP_P(( struct sockbuf *, void * ));
+LDAP_F (void) ldap_pvt_sasl_remove LDAP_P(( struct sockbuf * ));
+
+/*
+ * SASL encryption support for LBER Sockbufs
+ */
+
+struct sb_sasl_generic_data;
+
+struct sb_sasl_generic_ops {
+ void (*init)(struct sb_sasl_generic_data *p,
+ ber_len_t *min_send,
+ ber_len_t *max_send,
+ ber_len_t *max_recv);
+ ber_int_t (*encode)(struct sb_sasl_generic_data *p,
+ unsigned char *buf,
+ ber_len_t len,
+ Sockbuf_Buf *dst);
+ ber_int_t (*decode)(struct sb_sasl_generic_data *p,
+ const Sockbuf_Buf *src,
+ Sockbuf_Buf *dst);
+ void (*reset_buf)(struct sb_sasl_generic_data *p,
+ Sockbuf_Buf *buf);
+ void (*fini)(struct sb_sasl_generic_data *p);
+};
+
+struct sb_sasl_generic_install {
+ const struct sb_sasl_generic_ops *ops;
+ void *ops_private;
+};
+
+struct sb_sasl_generic_data {
+ const struct sb_sasl_generic_ops *ops;
+ void *ops_private;
+ Sockbuf_IO_Desc *sbiod;
+ ber_len_t min_send;
+ ber_len_t max_send;
+ ber_len_t max_recv;
+ Sockbuf_Buf sec_buf_in;
+ Sockbuf_Buf buf_in;
+ Sockbuf_Buf buf_out;
+ unsigned int flags;
+#define LDAP_PVT_SASL_PARTIAL_WRITE 1
+};
+
+#ifndef LDAP_PVT_SASL_LOCAL_SSF
+#define LDAP_PVT_SASL_LOCAL_SSF 71 /* SSF for Unix Domain Sockets */
+#endif /* ! LDAP_PVT_SASL_LOCAL_SSF */
+
+struct ldap;
+struct ldapmsg;
+struct ldifrecord;
+
+/* abandon */
+LDAP_F ( int ) ldap_pvt_discard LDAP_P((
+ struct ldap *ld, ber_int_t msgid ));
+
+/* init.c */
+LDAP_F( int )
+ldap_pvt_conf_option LDAP_P((
+ char *cmd, char *opt, int userconf ));
+
+/* ldifutil.c */
+LDAP_F( int )
+ldap_parse_ldif_record_x LDAP_P((
+ struct berval *rbuf,
+ unsigned long linenum,
+ struct ldifrecord *lr,
+ const char *errstr,
+ unsigned int flags,
+ void *ctx ));
+
+/* messages.c */
+LDAP_F( BerElement * )
+ldap_get_message_ber LDAP_P((
+ struct ldapmsg * ));
+
+/* open */
+LDAP_F (int) ldap_open_internal_connection LDAP_P((
+ struct ldap **ldp, ber_socket_t *fdp ));
+
+/* sasl.c */
+LDAP_F (int) ldap_pvt_sasl_generic_install LDAP_P(( Sockbuf *sb,
+ struct sb_sasl_generic_install *install_arg ));
+LDAP_F (void) ldap_pvt_sasl_generic_remove LDAP_P(( Sockbuf *sb ));
+
+/* search.c */
+LDAP_F( int ) ldap_pvt_put_filter LDAP_P((
+ BerElement *ber,
+ const char *str ));
+
+LDAP_F( char * )
+ldap_pvt_find_wildcard LDAP_P(( const char *s ));
+
+LDAP_F( ber_slen_t )
+ldap_pvt_filter_value_unescape LDAP_P(( char *filter ));
+
+LDAP_F( ber_len_t )
+ldap_bv2escaped_filter_value_len LDAP_P(( struct berval *in ));
+
+LDAP_F( int )
+ldap_bv2escaped_filter_value_x LDAP_P(( struct berval *in, struct berval *out,
+ int inplace, void *ctx ));
+
+LDAP_F (int) ldap_pvt_search LDAP_P((
+ struct ldap *ld,
+ LDAP_CONST char *base,
+ int scope,
+ LDAP_CONST char *filter,
+ char **attrs,
+ int attrsonly,
+ struct ldapcontrol **sctrls,
+ struct ldapcontrol **cctrls,
+ struct timeval *timeout,
+ int sizelimit,
+ int deref,
+ int *msgidp ));
+
+LDAP_F(int) ldap_pvt_search_s LDAP_P((
+ struct ldap *ld,
+ LDAP_CONST char *base,
+ int scope,
+ LDAP_CONST char *filter,
+ char **attrs,
+ int attrsonly,
+ struct ldapcontrol **sctrls,
+ struct ldapcontrol **cctrls,
+ struct timeval *timeout,
+ int sizelimit,
+ int deref,
+ struct ldapmsg **res ));
+
+/* string.c */
+LDAP_F( char * )
+ldap_pvt_str2upper LDAP_P(( char *str ));
+
+LDAP_F( char * )
+ldap_pvt_str2lower LDAP_P(( char *str ));
+
+LDAP_F( struct berval * )
+ldap_pvt_str2upperbv LDAP_P(( char *str, struct berval *bv ));
+
+LDAP_F( struct berval * )
+ldap_pvt_str2lowerbv LDAP_P(( char *str, struct berval *bv ));
+
+/* tls.c */
+LDAP_F (int) ldap_pvt_tls_config LDAP_P(( struct ldap *ld,
+ int option, const char *arg ));
+LDAP_F (int) ldap_pvt_tls_get_option LDAP_P(( struct ldap *ld,
+ int option, void *arg ));
+LDAP_F (int) ldap_pvt_tls_set_option LDAP_P(( struct ldap *ld,
+ int option, void *arg ));
+
+LDAP_F (void) ldap_pvt_tls_destroy LDAP_P(( void ));
+LDAP_F (int) ldap_pvt_tls_init LDAP_P(( int do_threads ));
+LDAP_F (int) ldap_pvt_tls_init_def_ctx LDAP_P(( int is_server ));
+LDAP_F (int) ldap_pvt_tls_accept LDAP_P(( Sockbuf *sb, void *ctx_arg ));
+LDAP_F (int) ldap_pvt_tls_connect LDAP_P(( struct ldap *ld, Sockbuf *sb, const char *host ));
+LDAP_F (int) ldap_pvt_tls_inplace LDAP_P(( Sockbuf *sb ));
+LDAP_F (void *) ldap_pvt_tls_sb_ctx LDAP_P(( Sockbuf *sb ));
+LDAP_F (void) ldap_pvt_tls_ctx_free LDAP_P(( void * ));
+
+typedef int LDAPDN_rewrite_dummy LDAP_P (( void *dn, unsigned flags ));
+
+typedef int (LDAP_TLS_CONNECT_CB) LDAP_P (( struct ldap *ld, void *ssl,
+ void *ctx, void *arg ));
+
+LDAP_F (int) ldap_pvt_tls_get_my_dn LDAP_P(( void *ctx, struct berval *dn,
+ LDAPDN_rewrite_dummy *func, unsigned flags ));
+LDAP_F (int) ldap_pvt_tls_get_peer_dn LDAP_P(( void *ctx, struct berval *dn,
+ LDAPDN_rewrite_dummy *func, unsigned flags ));
+LDAP_F (int) ldap_pvt_tls_get_strength LDAP_P(( void *ctx ));
+LDAP_F (int) ldap_pvt_tls_get_unique LDAP_P(( void *ctx, struct berval *buf, int is_server ));
+LDAP_F (int) ldap_pvt_tls_get_endpoint LDAP_P(( void *ctx, struct berval *buf, int is_server ));
+LDAP_F (const char *) ldap_pvt_tls_get_version LDAP_P(( void *ctx ));
+LDAP_F (const char *) ldap_pvt_tls_get_cipher LDAP_P(( void *ctx ));
+
+LDAP_END_DECL
+
+/*
+ * Multiple precision stuff
+ *
+ * May use OpenSSL's BIGNUM if built with TLS,
+ * or GNU's multiple precision library. But if
+ * long long is available, that's big enough
+ * and much more efficient.
+ *
+ * If none is available, unsigned long data is used.
+ */
+
+LDAP_BEGIN_DECL
+
+#ifdef USE_MP_BIGNUM
+/*
+ * Use OpenSSL's BIGNUM
+ */
+#include <openssl/crypto.h>
+#include <openssl/bn.h>
+
+typedef BIGNUM* ldap_pvt_mp_t;
+#define LDAP_PVT_MP_INIT (NULL)
+
+#define ldap_pvt_mp_init(mp) \
+ do { (mp) = BN_new(); } while (0)
+
+/* FIXME: we rely on mpr being initialized */
+#define ldap_pvt_mp_init_set(mpr,mpv) \
+ do { ldap_pvt_mp_init((mpr)); BN_add((mpr), (mpr), (mpv)); } while (0)
+
+#define ldap_pvt_mp_add(mpr,mpv) \
+ BN_add((mpr), (mpr), (mpv))
+
+#define ldap_pvt_mp_add_ulong(mp,v) \
+ BN_add_word((mp), (v))
+
+#define ldap_pvt_mp_clear(mp) \
+ do { BN_free((mp)); (mp) = 0; } while (0)
+
+#elif defined(USE_MP_GMP)
+/*
+ * Use GNU's multiple precision library
+ */
+#include <gmp.h>
+
+typedef mpz_t ldap_pvt_mp_t;
+#define LDAP_PVT_MP_INIT { 0 }
+
+#define ldap_pvt_mp_init(mp) \
+ mpz_init((mp))
+
+#define ldap_pvt_mp_init_set(mpr,mpv) \
+ mpz_init_set((mpr), (mpv))
+
+#define ldap_pvt_mp_add(mpr,mpv) \
+ mpz_add((mpr), (mpr), (mpv))
+
+#define ldap_pvt_mp_add_ulong(mp,v) \
+ mpz_add_ui((mp), (mp), (v))
+
+#define ldap_pvt_mp_clear(mp) \
+ mpz_clear((mp))
+
+#else
+/*
+ * Use unsigned long long
+ */
+
+#ifdef USE_MP_LONG_LONG
+typedef unsigned long long ldap_pvt_mp_t;
+#define LDAP_PVT_MP_INIT (0LL)
+#elif defined(USE_MP_LONG)
+typedef unsigned long ldap_pvt_mp_t;
+#define LDAP_PVT_MP_INIT (0L)
+#elif defined(HAVE_LONG_LONG)
+typedef unsigned long long ldap_pvt_mp_t;
+#define LDAP_PVT_MP_INIT (0LL)
+#else
+typedef unsigned long ldap_pvt_mp_t;
+#define LDAP_PVT_MP_INIT (0L)
+#endif
+
+#define ldap_pvt_mp_init(mp) \
+ do { (mp) = 0; } while (0)
+
+#define ldap_pvt_mp_init_set(mpr,mpv) \
+ do { (mpr) = (mpv); } while (0)
+
+#define ldap_pvt_mp_add(mpr,mpv) \
+ do { (mpr) += (mpv); } while (0)
+
+#define ldap_pvt_mp_add_ulong(mp,v) \
+ do { (mp) += (v); } while (0)
+
+#define ldap_pvt_mp_clear(mp) \
+ do { (mp) = 0; } while (0)
+
+#endif /* MP */
+
+#include "ldap_pvt_uc.h"
+
+LDAP_END_DECL
+
+LDAP_BEGIN_DECL
+
+#include <limits.h> /* get CHAR_BIT */
+
+/* Buffer space for sign, decimal digits and \0. Note: log10(2) < 146/485. */
+#define LDAP_PVT_INTTYPE_CHARS(type) (((sizeof(type)*CHAR_BIT-1)*146)/485 + 3)
+
+LDAP_END_DECL
+
+#endif /* _LDAP_PVT_H */
diff --git a/include/ldap_pvt_thread.h b/include/ldap_pvt_thread.h
new file mode 100644
index 0000000..037d6e9
--- /dev/null
+++ b/include/ldap_pvt_thread.h
@@ -0,0 +1,342 @@
+/* ldap_pvt_thread.h - ldap threads header file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _LDAP_PVT_THREAD_H
+#define _LDAP_PVT_THREAD_H /* libldap/ldap_thr_debug.h #undefines this */
+
+#include "ldap_cdefs.h"
+#include "ldap_int_thread.h"
+
+LDAP_BEGIN_DECL
+
+#ifndef LDAP_PVT_THREAD_H_DONE
+typedef ldap_int_thread_t ldap_pvt_thread_t;
+#ifdef LDAP_THREAD_DEBUG_WRAP
+typedef ldap_debug_thread_mutex_t ldap_pvt_thread_mutex_t;
+typedef ldap_debug_thread_cond_t ldap_pvt_thread_cond_t;
+typedef ldap_debug_thread_rdwr_t ldap_pvt_thread_rdwr_t;
+#define LDAP_PVT_MUTEX_FIRSTCREATE LDAP_DEBUG_MUTEX_FIRSTCREATE
+#define LDAP_PVT_MUTEX_NULL LDAP_DEBUG_MUTEX_NULL
+#else
+typedef ldap_int_thread_mutex_t ldap_pvt_thread_mutex_t;
+typedef ldap_int_thread_cond_t ldap_pvt_thread_cond_t;
+typedef ldap_int_thread_rdwr_t ldap_pvt_thread_rdwr_t;
+#define LDAP_PVT_MUTEX_FIRSTCREATE LDAP_INT_MUTEX_FIRSTCREATE
+#define LDAP_PVT_MUTEX_NULL LDAP_INT_MUTEX_NULL
+#endif
+typedef ldap_int_thread_key_t ldap_pvt_thread_key_t;
+#endif /* !LDAP_PVT_THREAD_H_DONE */
+
+#define ldap_pvt_thread_equal ldap_int_thread_equal
+
+LDAP_F( int )
+ldap_pvt_thread_initialize LDAP_P(( void ));
+
+LDAP_F( int )
+ldap_pvt_thread_destroy LDAP_P(( void ));
+
+LDAP_F( unsigned int )
+ldap_pvt_thread_sleep LDAP_P(( unsigned int s ));
+
+LDAP_F( int )
+ldap_pvt_thread_get_concurrency LDAP_P(( void ));
+
+LDAP_F( int )
+ldap_pvt_thread_set_concurrency LDAP_P(( int ));
+
+#define LDAP_PVT_THREAD_CREATE_JOINABLE 0
+#define LDAP_PVT_THREAD_CREATE_DETACHED 1
+
+#ifndef LDAP_PVT_THREAD_H_DONE
+#define LDAP_PVT_THREAD_SET_STACK_SIZE
+/* The size may be explicitly #defined to zero to disable it. */
+#if defined( LDAP_PVT_THREAD_STACK_SIZE ) && LDAP_PVT_THREAD_STACK_SIZE == 0
+# undef LDAP_PVT_THREAD_SET_STACK_SIZE
+#elif !defined( LDAP_PVT_THREAD_STACK_SIZE )
+ /* LARGE stack. Will be twice as large on 64 bit machine. */
+# define LDAP_PVT_THREAD_STACK_SIZE ( 1 * 1024 * 1024 * sizeof(void *) )
+#endif
+#endif /* !LDAP_PVT_THREAD_H_DONE */
+
+LDAP_F( int )
+ldap_pvt_thread_create LDAP_P((
+ ldap_pvt_thread_t * thread,
+ int detach,
+ void *(*start_routine)( void * ),
+ void *arg));
+
+LDAP_F( void )
+ldap_pvt_thread_exit LDAP_P(( void *retval ));
+
+LDAP_F( int )
+ldap_pvt_thread_join LDAP_P(( ldap_pvt_thread_t thread, void **status ));
+
+LDAP_F( int )
+ldap_pvt_thread_kill LDAP_P(( ldap_pvt_thread_t thread, int signo ));
+
+LDAP_F( int )
+ldap_pvt_thread_yield LDAP_P(( void ));
+
+LDAP_F( int )
+ldap_pvt_thread_cond_init LDAP_P(( ldap_pvt_thread_cond_t *cond ));
+
+LDAP_F( int )
+ldap_pvt_thread_cond_destroy LDAP_P(( ldap_pvt_thread_cond_t *cond ));
+
+LDAP_F( int )
+ldap_pvt_thread_cond_signal LDAP_P(( ldap_pvt_thread_cond_t *cond ));
+
+LDAP_F( int )
+ldap_pvt_thread_cond_broadcast LDAP_P(( ldap_pvt_thread_cond_t *cond ));
+
+LDAP_F( int )
+ldap_pvt_thread_cond_wait LDAP_P((
+ ldap_pvt_thread_cond_t *cond,
+ ldap_pvt_thread_mutex_t *mutex ));
+
+LDAP_F( int )
+ldap_pvt_thread_mutex_init LDAP_P(( ldap_pvt_thread_mutex_t *mutex ));
+
+LDAP_F( int )
+ldap_pvt_thread_mutex_recursive_init LDAP_P(( ldap_pvt_thread_mutex_t *mutex ));
+
+LDAP_F( int )
+ldap_pvt_thread_mutex_destroy LDAP_P(( ldap_pvt_thread_mutex_t *mutex ));
+
+LDAP_F( int )
+ldap_pvt_thread_mutex_lock LDAP_P(( ldap_pvt_thread_mutex_t *mutex ));
+
+LDAP_F( int )
+ldap_pvt_thread_mutex_trylock LDAP_P(( ldap_pvt_thread_mutex_t *mutex ));
+
+LDAP_F( int )
+ldap_pvt_thread_mutex_unlock LDAP_P(( ldap_pvt_thread_mutex_t *mutex ));
+
+LDAP_F( ldap_pvt_thread_t )
+ldap_pvt_thread_self LDAP_P(( void ));
+
+#ifdef LDAP_INT_THREAD_ASSERT_MUTEX_OWNER
+#define LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER LDAP_INT_THREAD_ASSERT_MUTEX_OWNER
+#else
+#define LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER(mutex) ((void) 0)
+#endif
+
+LDAP_F( int )
+ldap_pvt_thread_rdwr_init LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp));
+
+LDAP_F( int )
+ldap_pvt_thread_rdwr_destroy LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp));
+
+LDAP_F( int )
+ldap_pvt_thread_rdwr_rlock LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp));
+
+LDAP_F( int )
+ldap_pvt_thread_rdwr_rtrylock LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp));
+
+LDAP_F( int )
+ldap_pvt_thread_rdwr_runlock LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp));
+
+LDAP_F( int )
+ldap_pvt_thread_rdwr_wlock LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp));
+
+LDAP_F( int )
+ldap_pvt_thread_rdwr_wtrylock LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp));
+
+LDAP_F( int )
+ldap_pvt_thread_rdwr_wunlock LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp));
+
+LDAP_F( int )
+ldap_pvt_thread_key_create LDAP_P((ldap_pvt_thread_key_t *keyp));
+
+LDAP_F( int )
+ldap_pvt_thread_key_destroy LDAP_P((ldap_pvt_thread_key_t key));
+
+LDAP_F( int )
+ldap_pvt_thread_key_setdata LDAP_P((ldap_pvt_thread_key_t key, void *data));
+
+LDAP_F( int )
+ldap_pvt_thread_key_getdata LDAP_P((ldap_pvt_thread_key_t key, void **data));
+
+#ifdef LDAP_DEBUG
+LDAP_F( int )
+ldap_pvt_thread_rdwr_readers LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp));
+
+LDAP_F( int )
+ldap_pvt_thread_rdwr_writers LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp));
+
+LDAP_F( int )
+ldap_pvt_thread_rdwr_active LDAP_P((ldap_pvt_thread_rdwr_t *rdwrp));
+#endif /* LDAP_DEBUG */
+
+#define LDAP_PVT_THREAD_EINVAL EINVAL
+#define LDAP_PVT_THREAD_EBUSY EINVAL
+
+#ifndef LDAP_PVT_THREAD_H_DONE
+typedef ldap_int_thread_pool_t ldap_pvt_thread_pool_t;
+
+typedef void * (ldap_pvt_thread_start_t) LDAP_P((void *ctx, void *arg));
+typedef int (ldap_pvt_thread_walk_t) LDAP_P((ldap_pvt_thread_start_t *start, void *start_arg, void *arg));
+typedef void (ldap_pvt_thread_pool_keyfree_t) LDAP_P((void *key, void *data));
+#endif /* !LDAP_PVT_THREAD_H_DONE */
+
+LDAP_F( int )
+ldap_pvt_thread_pool_init LDAP_P((
+ ldap_pvt_thread_pool_t *pool_out,
+ int max_threads,
+ int max_pending ));
+
+LDAP_F( int )
+ldap_pvt_thread_pool_init_q LDAP_P((
+ ldap_pvt_thread_pool_t *pool_out,
+ int max_threads,
+ int max_pending,
+ int num_qs ));
+
+LDAP_F( int )
+ldap_pvt_thread_pool_submit LDAP_P((
+ ldap_pvt_thread_pool_t *pool,
+ ldap_pvt_thread_start_t *start,
+ void *arg ));
+
+LDAP_F( int )
+ldap_pvt_thread_pool_submit2 LDAP_P((
+ ldap_pvt_thread_pool_t *pool,
+ ldap_pvt_thread_start_t *start,
+ void *arg,
+ void **cookie ));
+
+LDAP_F( int )
+ldap_pvt_thread_pool_retract LDAP_P((
+ void *cookie ));
+
+LDAP_F( int )
+ldap_pvt_thread_pool_walk LDAP_P((
+ ldap_pvt_thread_pool_t *pool,
+ ldap_pvt_thread_start_t *start,
+ ldap_pvt_thread_walk_t *cb,
+ void *arg ));
+
+LDAP_F( int )
+ldap_pvt_thread_pool_maxthreads LDAP_P((
+ ldap_pvt_thread_pool_t *pool,
+ int max_threads ));
+
+LDAP_F( int )
+ldap_pvt_thread_pool_queues LDAP_P((
+ ldap_pvt_thread_pool_t *pool,
+ int numqs ));
+
+#ifndef LDAP_PVT_THREAD_H_DONE
+typedef enum {
+ LDAP_PVT_THREAD_POOL_PARAM_UNKNOWN = -1,
+ LDAP_PVT_THREAD_POOL_PARAM_MAX,
+ LDAP_PVT_THREAD_POOL_PARAM_MAX_PENDING,
+ LDAP_PVT_THREAD_POOL_PARAM_OPEN,
+ LDAP_PVT_THREAD_POOL_PARAM_STARTING,
+ LDAP_PVT_THREAD_POOL_PARAM_ACTIVE,
+ LDAP_PVT_THREAD_POOL_PARAM_PAUSING,
+ LDAP_PVT_THREAD_POOL_PARAM_PENDING,
+ LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD,
+ LDAP_PVT_THREAD_POOL_PARAM_ACTIVE_MAX,
+ LDAP_PVT_THREAD_POOL_PARAM_PENDING_MAX,
+ LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD_MAX,
+ LDAP_PVT_THREAD_POOL_PARAM_STATE
+} ldap_pvt_thread_pool_param_t;
+#endif /* !LDAP_PVT_THREAD_H_DONE */
+
+LDAP_F( int )
+ldap_pvt_thread_pool_query LDAP_P((
+ ldap_pvt_thread_pool_t *pool,
+ ldap_pvt_thread_pool_param_t param, void *value ));
+
+LDAP_F( int )
+ldap_pvt_thread_pool_pausing LDAP_P((
+ ldap_pvt_thread_pool_t *pool ));
+
+LDAP_F( int )
+ldap_pvt_thread_pool_backload LDAP_P((
+ ldap_pvt_thread_pool_t *pool ));
+
+LDAP_F( void )
+ldap_pvt_thread_pool_idle LDAP_P((
+ ldap_pvt_thread_pool_t *pool ));
+
+LDAP_F( void )
+ldap_pvt_thread_pool_unidle LDAP_P((
+ ldap_pvt_thread_pool_t *pool ));
+
+LDAP_F( int )
+ldap_pvt_thread_pool_pausecheck LDAP_P((
+ ldap_pvt_thread_pool_t *pool ));
+
+LDAP_F( int )
+ldap_pvt_thread_pool_pausecheck_native LDAP_P((
+ ldap_pvt_thread_pool_t *pool ));
+
+LDAP_F( int )
+ldap_pvt_thread_pool_pause LDAP_P((
+ ldap_pvt_thread_pool_t *pool ));
+
+LDAP_F( int )
+ldap_pvt_thread_pool_resume LDAP_P((
+ ldap_pvt_thread_pool_t *pool ));
+
+LDAP_F( int )
+ldap_pvt_thread_pool_destroy LDAP_P((
+ ldap_pvt_thread_pool_t *pool,
+ int run_pending ));
+
+LDAP_F( int )
+ldap_pvt_thread_pool_close LDAP_P((
+ ldap_pvt_thread_pool_t *pool,
+ int run_pending ));
+
+LDAP_F( int )
+ldap_pvt_thread_pool_free LDAP_P((
+ ldap_pvt_thread_pool_t *pool ));
+
+LDAP_F( int )
+ldap_pvt_thread_pool_getkey LDAP_P((
+ void *ctx,
+ void *key,
+ void **data,
+ ldap_pvt_thread_pool_keyfree_t **kfree ));
+
+LDAP_F( int )
+ldap_pvt_thread_pool_setkey LDAP_P((
+ void *ctx,
+ void *key,
+ void *data,
+ ldap_pvt_thread_pool_keyfree_t *kfree,
+ void **olddatap,
+ ldap_pvt_thread_pool_keyfree_t **oldkfreep ));
+
+LDAP_F( void )
+ldap_pvt_thread_pool_purgekey LDAP_P(( void *key ));
+
+LDAP_F( void *)
+ldap_pvt_thread_pool_context LDAP_P(( void ));
+
+LDAP_F( void )
+ldap_pvt_thread_pool_context_reset LDAP_P(( void *key ));
+
+LDAP_F( ldap_pvt_thread_t )
+ldap_pvt_thread_pool_tid LDAP_P(( void *ctx ));
+
+LDAP_END_DECL
+
+#define LDAP_PVT_THREAD_H_DONE
+#endif /* _LDAP_PVT_THREAD_H */
diff --git a/include/ldap_pvt_uc.h b/include/ldap_pvt_uc.h
new file mode 100644
index 0000000..1f2a50d
--- /dev/null
+++ b/include/ldap_pvt_uc.h
@@ -0,0 +1,163 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * ldap_pvt_uc.h - Header for Unicode functions.
+ * These are meant to be used by the OpenLDAP distribution only.
+ * These should be named ldap_pvt_....()
+ */
+
+#ifndef _LDAP_PVT_UC_H
+#define _LDAP_PVT_UC_H 1
+
+#include <lber.h> /* get ber_slen_t */
+
+#include <ac/bytes.h>
+#include "../libraries/liblunicode/ucdata/ucdata.h"
+
+LDAP_BEGIN_DECL
+
+/*
+ * UTF-8 (in utf-8.c)
+ */
+
+/* UCDATA uses UCS-2 passed in a 4 byte unsigned int */
+typedef ac_uint4 ldap_unicode_t;
+
+/* Convert a string with csize octets per character to UTF-8 */
+LDAP_F( int ) ldap_ucs_to_utf8s LDAP_P((
+ struct berval *ucs, int csize, struct berval *utf8s ));
+
+
+/* returns the number of bytes in the UTF-8 string */
+LDAP_F (ber_len_t) ldap_utf8_bytes( const char * );
+/* returns the number of UTF-8 characters in the string */
+LDAP_F (ber_len_t) ldap_utf8_chars( const char * );
+/* returns the length (in bytes) of the UTF-8 character */
+LDAP_F (int) ldap_utf8_offset( const char * );
+/* returns the length (in bytes) indicated by the UTF-8 character */
+LDAP_F (int) ldap_utf8_charlen( const char * );
+
+/* returns the length (in bytes) indicated by the UTF-8 character
+ * also checks that shortest possible encoding was used
+ */
+LDAP_F (int) ldap_utf8_charlen2( const char * );
+
+/* copies a UTF-8 character and returning number of bytes copied */
+LDAP_F (int) ldap_utf8_copy( char *, const char *);
+
+/* returns pointer of next UTF-8 character in string */
+LDAP_F (char*) ldap_utf8_next( const char * );
+/* returns pointer of previous UTF-8 character in string */
+LDAP_F (char*) ldap_utf8_prev( const char * );
+
+/* primitive ctype routines -- not aware of non-ascii characters */
+LDAP_F (int) ldap_utf8_isascii( const char * );
+LDAP_F (int) ldap_utf8_isalpha( const char * );
+LDAP_F (int) ldap_utf8_isalnum( const char * );
+LDAP_F (int) ldap_utf8_isdigit( const char * );
+LDAP_F (int) ldap_utf8_isxdigit( const char * );
+LDAP_F (int) ldap_utf8_isspace( const char * );
+
+/* span characters not in set, return bytes spanned */
+LDAP_F (ber_len_t) ldap_utf8_strcspn( const char* str, const char *set);
+/* span characters in set, return bytes spanned */
+LDAP_F (ber_len_t) ldap_utf8_strspn( const char* str, const char *set);
+/* return first occurrence of character in string */
+LDAP_F (char *) ldap_utf8_strchr( const char* str, const char *chr);
+/* return first character of set in string */
+LDAP_F (char *) ldap_utf8_strpbrk( const char* str, const char *set);
+/* reentrant tokenizer */
+LDAP_F (char*) ldap_utf8_strtok( char* sp, const char* sep, char **last);
+
+/* Optimizations */
+LDAP_V (const char) ldap_utf8_lentab[128];
+LDAP_V (const char) ldap_utf8_mintab[32];
+
+#define LDAP_UTF8_ISASCII(p) ( !(*(const unsigned char *)(p) & 0x80 ) )
+#define LDAP_UTF8_CHARLEN(p) ( LDAP_UTF8_ISASCII(p) \
+ ? 1 : ldap_utf8_lentab[*(const unsigned char *)(p) ^ 0x80] )
+
+/* This is like CHARLEN but additionally validates to make sure
+ * the char used the shortest possible encoding.
+ * 'l' is used to temporarily hold the result of CHARLEN.
+ */
+#define LDAP_UTF8_CHARLEN2(p, l) ( ( ( l = LDAP_UTF8_CHARLEN( p )) < 3 || \
+ ( ldap_utf8_mintab[*(const unsigned char *)(p) & 0x1f] & (p)[1] ) ) ? \
+ l : 0 )
+
+#define LDAP_UTF8_OFFSET(p) ( LDAP_UTF8_ISASCII(p) \
+ ? 1 : ldap_utf8_offset((p)) )
+
+#define LDAP_UTF8_COPY(d,s) ( LDAP_UTF8_ISASCII(s) \
+ ? (*(d) = *(s), 1) : ldap_utf8_copy((d),(s)) )
+
+#define LDAP_UTF8_NEXT(p) ( LDAP_UTF8_ISASCII(p) \
+ ? (char *)(p)+1 : ldap_utf8_next((p)) )
+
+#define LDAP_UTF8_INCR(p) ((p) = LDAP_UTF8_NEXT(p))
+
+/* For symmetry */
+#define LDAP_UTF8_PREV(p) (ldap_utf8_prev((p)))
+#define LDAP_UTF8_DECR(p) ((p)=LDAP_UTF8_PREV((p)))
+
+
+/* these probably should be renamed */
+LDAP_LUNICODE_F(int) ucstrncmp(
+ const ldap_unicode_t *,
+ const ldap_unicode_t *,
+ ber_len_t );
+
+LDAP_LUNICODE_F(int) ucstrncasecmp(
+ const ldap_unicode_t *,
+ const ldap_unicode_t *,
+ ber_len_t );
+
+LDAP_LUNICODE_F(ldap_unicode_t *) ucstrnchr(
+ const ldap_unicode_t *,
+ ber_len_t,
+ ldap_unicode_t );
+
+LDAP_LUNICODE_F(ldap_unicode_t *) ucstrncasechr(
+ const ldap_unicode_t *,
+ ber_len_t,
+ ldap_unicode_t );
+
+LDAP_LUNICODE_F(void) ucstr2upper(
+ ldap_unicode_t *,
+ ber_len_t );
+
+#define LDAP_UTF8_NOCASEFOLD 0x0U
+#define LDAP_UTF8_CASEFOLD 0x1U
+#define LDAP_UTF8_ARG1NFC 0x2U
+#define LDAP_UTF8_ARG2NFC 0x4U
+#define LDAP_UTF8_APPROX 0x8U
+
+LDAP_LUNICODE_F(struct berval *) UTF8bvnormalize(
+ struct berval *,
+ struct berval *,
+ unsigned,
+ void *memctx );
+
+LDAP_LUNICODE_F(int) UTF8bvnormcmp(
+ struct berval *,
+ struct berval *,
+ unsigned,
+ void *memctx );
+
+LDAP_END_DECL
+
+#endif
+
diff --git a/include/ldap_queue.h b/include/ldap_queue.h
new file mode 100644
index 0000000..6d32370
--- /dev/null
+++ b/include/ldap_queue.h
@@ -0,0 +1,593 @@
+/* ldap_queue.h -- queue macros */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ * $FreeBSD: src/sys/sys/queue.h,v 1.32.2.5 2001/09/30 21:12:54 luigi Exp $
+ *
+ * See also: ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work is derived from FreeBSD queue.h work. Adapted for use in
+ * OpenLDAP Software by Kurt D. Zeilenga.
+ */
+
+#ifndef _LDAP_QUEUE_H_
+#define _LDAP_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists, tail queues, and circular queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection. Also, it is possible to rotate the queue,
+ * rejoining the ends and splitting it so that a given element becomes the
+ * new head or tail.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ * All macros are prefixed with LDAP_.
+ *
+ * SLIST_ LIST_ STAILQ_ TAILQ_ CIRCLEQ_
+ * _HEAD + + + + +
+ * _ENTRY + + + + +
+ * _INIT + + + + +
+ * _ENTRY_INIT + + + + +
+ * _EMPTY + + + + +
+ * _FIRST + + + + +
+ * _NEXT + + + + +
+ * _PREV - - - + +
+ * _LAST - - + + +
+ * _FOREACH + + + + +
+ * _FOREACH_REVERSE - - - + +
+ * _INSERT_HEAD + + + + +
+ * _INSERT_BEFORE - + - + +
+ * _INSERT_AFTER + + + + +
+ * _INSERT_TAIL - - + + +
+ * _REMOVE_HEAD + - + - -
+ * _REMOVE + + + + +
+ *
+ */
+
+/*
+ * Singly-linked List definitions.
+ */
+#define LDAP_SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define LDAP_SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LDAP_SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+#define LDAP_SLIST_ENTRY_INITIALIZER(entry) \
+ { NULL }
+
+/*
+ * Singly-linked List functions.
+ */
+#define LDAP_SLIST_EMPTY(head) ((head)->slh_first == NULL)
+
+#define LDAP_SLIST_FIRST(head) ((head)->slh_first)
+
+#define LDAP_SLIST_FOREACH(var, head, field) \
+ for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next)
+
+#define LDAP_SLIST_INIT(head) { \
+ (head)->slh_first = NULL; \
+}
+
+#define LDAP_SLIST_ENTRY_INIT(var, field) { \
+ (var)->field.sle_next = NULL; \
+}
+
+#define LDAP_SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (0)
+
+#define LDAP_SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (0)
+
+#define LDAP_SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define LDAP_SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (0)
+
+#define LDAP_SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ LDAP_SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = (head)->slh_first; \
+ while( curelm->field.sle_next != (elm) ) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ } \
+} while (0)
+
+/*
+ * Singly-linked Tail queue definitions.
+ */
+#define LDAP_STAILQ_HEAD(name, type) \
+struct name { \
+ struct type *stqh_first;/* first element */ \
+ struct type **stqh_last;/* addr of last next element */ \
+}
+
+#define LDAP_STAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).stqh_first }
+
+#define LDAP_STAILQ_ENTRY(type) \
+struct { \
+ struct type *stqe_next; /* next element */ \
+}
+
+#define LDAP_STAILQ_ENTRY_INITIALIZER(entry) \
+ { NULL }
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define LDAP_STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
+
+#define LDAP_STAILQ_INIT(head) do { \
+ (head)->stqh_first = NULL; \
+ (head)->stqh_last = &(head)->stqh_first; \
+} while (0)
+
+#define LDAP_STAILQ_ENTRY_INIT(var, field) { \
+ (var)->field.stqe_next = NULL; \
+}
+
+#define LDAP_STAILQ_FIRST(head) ((head)->stqh_first)
+
+#define LDAP_STAILQ_LAST(head, type, field) \
+ (LDAP_STAILQ_EMPTY(head) ? \
+ NULL : \
+ ((struct type *) \
+ ((char *)((head)->stqh_last) - offsetof(struct type, field))))
+
+#define LDAP_STAILQ_FOREACH(var, head, field) \
+ for((var) = (head)->stqh_first; (var); (var) = (var)->field.stqe_next)
+
+#define LDAP_STAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+ (head)->stqh_first = (elm); \
+} while (0)
+
+#define LDAP_STAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.stqe_next = NULL; \
+ *(head)->stqh_last = (elm); \
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+} while (0)
+
+#define LDAP_STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
+ if (((elm)->field.stqe_next = (tqelm)->field.stqe_next) == NULL)\
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+ (tqelm)->field.stqe_next = (elm); \
+} while (0)
+
+#define LDAP_STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+
+#define LDAP_STAILQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->stqh_first = \
+ (head)->stqh_first->field.stqe_next) == NULL) \
+ (head)->stqh_last = &(head)->stqh_first; \
+} while (0)
+
+#define LDAP_STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do { \
+ if (((head)->stqh_first = (elm)->field.stqe_next) == NULL) \
+ (head)->stqh_last = &(head)->stqh_first; \
+} while (0)
+
+#define LDAP_STAILQ_REMOVE(head, elm, type, field) do { \
+ if ((head)->stqh_first == (elm)) { \
+ LDAP_STAILQ_REMOVE_HEAD(head, field); \
+ } \
+ else { \
+ struct type *curelm = (head)->stqh_first; \
+ while( curelm->field.stqe_next != (elm) ) \
+ curelm = curelm->field.stqe_next; \
+ if((curelm->field.stqe_next = \
+ curelm->field.stqe_next->field.stqe_next) == NULL) \
+ (head)->stqh_last = &(curelm)->field.stqe_next; \
+ } \
+} while (0)
+
+/*
+ * List definitions.
+ */
+#define LDAP_LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LDAP_LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LDAP_LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+#define LDAP_LIST_ENTRY_INITIALIZER(entry) \
+ { NULL, NULL }
+
+/*
+ * List functions.
+ */
+
+#define LDAP_LIST_EMPTY(head) ((head)->lh_first == NULL)
+
+#define LDAP_LIST_FIRST(head) ((head)->lh_first)
+
+#define LDAP_LIST_FOREACH(var, head, field) \
+ for((var) = (head)->lh_first; (var); (var) = (var)->field.le_next)
+
+#define LDAP_LIST_INIT(head) do { \
+ (head)->lh_first = NULL; \
+} while (0)
+
+#define LDAP_LIST_ENTRY_INIT(var, field) do { \
+ (var)->field.le_next = NULL; \
+ (var)->field.le_prev = NULL; \
+} while (0)
+
+#define LDAP_LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (0)
+
+#define LDAP_LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (0)
+
+#define LDAP_LIST_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (0)
+
+#define LDAP_LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LDAP_LIST_REMOVE(elm, field) do { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+} while (0)
+
+/*
+ * Tail queue definitions.
+ */
+#define LDAP_TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define LDAP_TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define LDAP_TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+#define LDAP_TAILQ_ENTRY_INITIALIZER(entry) \
+ { NULL, NULL }
+
+/*
+ * Tail queue functions.
+ */
+#define LDAP_TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+
+#define LDAP_TAILQ_FOREACH(var, head, field) \
+ for (var = LDAP_TAILQ_FIRST(head); var; var = LDAP_TAILQ_NEXT(var, field))
+
+#define LDAP_TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = LDAP_TAILQ_LAST((head), headname); \
+ (var); \
+ (var) = LDAP_TAILQ_PREV((var), headname, field))
+
+#define LDAP_TAILQ_FIRST(head) ((head)->tqh_first)
+
+#define LDAP_TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define LDAP_TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define LDAP_TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define LDAP_TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (0)
+
+#define LDAP_TAILQ_ENTRY_INIT(var, field) do { \
+ (var)->field.tqe_next = NULL; \
+ (var)->field.tqe_prev = NULL; \
+} while (0)
+
+#define LDAP_TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (0)
+
+#define LDAP_TAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (0)
+
+#define LDAP_TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (0)
+
+#define LDAP_TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (0)
+
+#define LDAP_TAILQ_REMOVE(head, elm, field) do { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define LDAP_CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define LDAP_CIRCLEQ_HEAD_INITIALIZER(head) \
+ { (void *)&(head), (void *)&(head) }
+
+#define LDAP_CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue functions.
+ */
+#define LDAP_CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head))
+
+#define LDAP_CIRCLEQ_FIRST(head) ((head)->cqh_first)
+
+#define LDAP_CIRCLEQ_FOREACH(var, head, field) \
+ for((var) = (head)->cqh_first; \
+ (var) != (void *)(head); \
+ (var) = (var)->field.cqe_next)
+
+#define LDAP_CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for((var) = (head)->cqh_last; \
+ (var) != (void *)(head); \
+ (var) = (var)->field.cqe_prev)
+
+#define LDAP_CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = (void *)(head); \
+ (head)->cqh_last = (void *)(head); \
+} while (0)
+
+#define LDAP_CIRCLEQ_ENTRY_INIT(var, field) do { \
+ (var)->field.cqe_next = NULL; \
+ (var)->field.cqe_prev = NULL; \
+} while (0)
+
+#define LDAP_CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == (void *)(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (0)
+
+#define LDAP_CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == (void *)(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (0)
+
+#define LDAP_CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = (void *)(head); \
+ if ((head)->cqh_last == (void *)(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (0)
+
+#define LDAP_CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.cqe_next = (void *)(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == (void *)(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (0)
+
+#define LDAP_CIRCLEQ_LAST(head) ((head)->cqh_last)
+
+#define LDAP_CIRCLEQ_NEXT(elm,field) ((elm)->field.cqe_next)
+
+#define LDAP_CIRCLEQ_PREV(elm,field) ((elm)->field.cqe_prev)
+
+#define LDAP_CIRCLEQ_REMOVE(head, elm, field) do { \
+ if ((elm)->field.cqe_next == (void *)(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == (void *)(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+} while (0)
+
+#define LDAP_CIRCLEQ_LOOP_NEXT(head, elm, field) \
+ (((elm)->field.cqe_next == (void *)(head)) \
+ ? ((head)->cqh_first) \
+ : ((elm)->field.cqe_next))
+
+#define LDAP_CIRCLEQ_LOOP_PREV(head, elm, field) \
+ (((elm)->field.cqe_prev == (void *)(head)) \
+ ? ((head)->cqh_last) \
+ : ((elm)->field.cqe_prev))
+
+#define LDAP_CIRCLEQ_MAKE_HEAD(head, elm, field) do { \
+ if ((elm)->field.cqe_prev != (void *)(head)) { \
+ (head)->cqh_first->field.cqe_prev = (head)->cqh_last; \
+ (head)->cqh_last->field.cqe_next = (head)->cqh_first; \
+ (head)->cqh_first = elm; \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ (elm)->field.cqe_prev->field.cqe_next = (void *)(head); \
+ (elm)->field.cqe_prev = (void *)(head); \
+ } \
+} while (0)
+
+#define LDAP_CIRCLEQ_MAKE_TAIL(head, elm, field) do { \
+ if ((elm)->field.cqe_next != (void *)(head)) { \
+ (head)->cqh_first->field.cqe_prev = (head)->cqh_last; \
+ (head)->cqh_last->field.cqe_next = (head)->cqh_first; \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ (head)->cqh_last = elm; \
+ (elm)->field.cqe_next->field.cqe_prev = (void *)(head); \
+ (elm)->field.cqe_next = (void *)(head); \
+ } \
+} while (0)
+
+#endif /* !_LDAP_QUEUE_H_ */
diff --git a/include/ldap_rq.h b/include/ldap_rq.h
new file mode 100644
index 0000000..1050550
--- /dev/null
+++ b/include/ldap_rq.h
@@ -0,0 +1,102 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef LDAP_RQ_H
+#define LDAP_RQ_H 1
+
+#include <ldap_cdefs.h>
+
+LDAP_BEGIN_DECL
+
+typedef struct re_s {
+ struct timeval next_sched;
+ struct timeval interval;
+ LDAP_STAILQ_ENTRY(re_s) tnext; /* it includes running */
+ LDAP_STAILQ_ENTRY(re_s) rnext;
+ ldap_pvt_thread_start_t *routine;
+ void *arg;
+ char *tname;
+ char *tspec;
+ void *pool_cookie;
+} re_t;
+
+typedef struct runqueue_s {
+ LDAP_STAILQ_HEAD(l, re_s) task_list;
+ LDAP_STAILQ_HEAD(rl, re_s) run_list;
+ ldap_pvt_thread_mutex_t rq_mutex;
+} runqueue_t;
+
+LDAP_F( struct re_s* )
+ldap_pvt_runqueue_insert(
+ struct runqueue_s* rq,
+ time_t interval,
+ ldap_pvt_thread_start_t* routine,
+ void *arg,
+ char *tname,
+ char *tspec
+);
+
+LDAP_F( struct re_s* )
+ldap_pvt_runqueue_find(
+ struct runqueue_s* rq,
+ ldap_pvt_thread_start_t* routine,
+ void *arg
+);
+
+LDAP_F( void )
+ldap_pvt_runqueue_remove(
+ struct runqueue_s* rq,
+ struct re_s* entry
+);
+
+LDAP_F( struct re_s* )
+ldap_pvt_runqueue_next_sched(
+ struct runqueue_s* rq,
+ struct timeval* next_run
+);
+
+LDAP_F( void )
+ldap_pvt_runqueue_runtask(
+ struct runqueue_s* rq,
+ struct re_s* entry
+);
+
+LDAP_F( void )
+ldap_pvt_runqueue_stoptask(
+ struct runqueue_s* rq,
+ struct re_s* entry
+);
+
+LDAP_F( int )
+ldap_pvt_runqueue_isrunning(
+ struct runqueue_s* rq,
+ struct re_s* entry
+);
+
+LDAP_F( void )
+ldap_pvt_runqueue_resched(
+ struct runqueue_s* rq,
+ struct re_s* entry,
+ int defer
+);
+
+LDAP_F( int )
+ldap_pvt_runqueue_persistent_backload(
+ struct runqueue_s* rq
+);
+
+LDAP_END_DECL
+
+#endif
diff --git a/include/ldap_schema.h b/include/ldap_schema.h
new file mode 100644
index 0000000..9632180
--- /dev/null
+++ b/include/ldap_schema.h
@@ -0,0 +1,360 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* ldap-schema.h - Header for basic schema handling functions that can be
+ * used by both clients and servers.
+ * these routines should be renamed ldap_x_...
+ */
+
+#ifndef _LDAP_SCHEMA_H
+#define _LDAP_SCHEMA_H 1
+
+#include <ldap_cdefs.h>
+
+LDAP_BEGIN_DECL
+
+/* Codes for parsing errors */
+
+#define LDAP_SCHERR_OUTOFMEM 1
+#define LDAP_SCHERR_UNEXPTOKEN 2
+#define LDAP_SCHERR_NOLEFTPAREN 3
+#define LDAP_SCHERR_NORIGHTPAREN 4
+#define LDAP_SCHERR_NODIGIT 5
+#define LDAP_SCHERR_BADNAME 6
+#define LDAP_SCHERR_BADDESC 7
+#define LDAP_SCHERR_BADSUP 8
+#define LDAP_SCHERR_DUPOPT 9
+#define LDAP_SCHERR_EMPTY 10
+#define LDAP_SCHERR_MISSING 11
+#define LDAP_SCHERR_OUT_OF_ORDER 12
+
+typedef struct ldap_schema_extension_item {
+ char *lsei_name;
+ char **lsei_values;
+} LDAPSchemaExtensionItem;
+
+typedef struct ldap_syntax {
+ char *syn_oid; /* REQUIRED */
+ char **syn_names; /* OPTIONAL */
+ char *syn_desc; /* OPTIONAL */
+ LDAPSchemaExtensionItem **syn_extensions; /* OPTIONAL */
+} LDAPSyntax;
+
+typedef struct ldap_matchingrule {
+ char *mr_oid; /* REQUIRED */
+ char **mr_names; /* OPTIONAL */
+ char *mr_desc; /* OPTIONAL */
+ int mr_obsolete; /* OPTIONAL */
+ char *mr_syntax_oid; /* REQUIRED */
+ LDAPSchemaExtensionItem **mr_extensions; /* OPTIONAL */
+} LDAPMatchingRule;
+
+typedef struct ldap_matchingruleuse {
+ char *mru_oid; /* REQUIRED */
+ char **mru_names; /* OPTIONAL */
+ char *mru_desc; /* OPTIONAL */
+ int mru_obsolete; /* OPTIONAL */
+ char **mru_applies_oids; /* REQUIRED */
+ LDAPSchemaExtensionItem **mru_extensions; /* OPTIONAL */
+} LDAPMatchingRuleUse;
+
+typedef struct ldap_attributetype {
+ char *at_oid; /* REQUIRED */
+ char **at_names; /* OPTIONAL */
+ char *at_desc; /* OPTIONAL */
+ int at_obsolete; /* 0=no, 1=yes */
+ char *at_sup_oid; /* OPTIONAL */
+ char *at_equality_oid; /* OPTIONAL */
+ char *at_ordering_oid; /* OPTIONAL */
+ char *at_substr_oid; /* OPTIONAL */
+ char *at_syntax_oid; /* OPTIONAL */
+ int at_syntax_len; /* OPTIONAL */
+ int at_single_value; /* 0=no, 1=yes */
+ int at_collective; /* 0=no, 1=yes */
+ int at_no_user_mod; /* 0=no, 1=yes */
+ int at_usage; /* 0=userApplications, 1=directoryOperation,
+ 2=distributedOperation, 3=dSAOperation */
+ LDAPSchemaExtensionItem **at_extensions; /* OPTIONAL */
+} LDAPAttributeType;
+
+typedef struct ldap_objectclass {
+ char *oc_oid; /* REQUIRED */
+ char **oc_names; /* OPTIONAL */
+ char *oc_desc; /* OPTIONAL */
+ int oc_obsolete; /* 0=no, 1=yes */
+ char **oc_sup_oids; /* OPTIONAL */
+ int oc_kind; /* 0=ABSTRACT, 1=STRUCTURAL, 2=AUXILIARY */
+ char **oc_at_oids_must; /* OPTIONAL */
+ char **oc_at_oids_may; /* OPTIONAL */
+ LDAPSchemaExtensionItem **oc_extensions; /* OPTIONAL */
+} LDAPObjectClass;
+
+typedef struct ldap_contentrule {
+ char *cr_oid; /* REQUIRED */
+ char **cr_names; /* OPTIONAL */
+ char *cr_desc; /* OPTIONAL */
+ char **cr_sup_oids; /* OPTIONAL */
+ int cr_obsolete; /* 0=no, 1=yes */
+ char **cr_oc_oids_aux; /* OPTIONAL */
+ char **cr_at_oids_must; /* OPTIONAL */
+ char **cr_at_oids_may; /* OPTIONAL */
+ char **cr_at_oids_not; /* OPTIONAL */
+ LDAPSchemaExtensionItem **cr_extensions; /* OPTIONAL */
+} LDAPContentRule;
+
+typedef struct ldap_nameform {
+ char *nf_oid; /* REQUIRED */
+ char **nf_names; /* OPTIONAL */
+ char *nf_desc; /* OPTIONAL */
+ int nf_obsolete; /* 0=no, 1=yes */
+ char *nf_objectclass; /* REQUIRED */
+ char **nf_at_oids_must; /* REQUIRED */
+ char **nf_at_oids_may; /* OPTIONAL */
+ LDAPSchemaExtensionItem **nf_extensions; /* OPTIONAL */
+} LDAPNameForm;
+
+typedef struct ldap_structurerule {
+ int sr_ruleid; /* REQUIRED */
+ char **sr_names; /* OPTIONAL */
+ char *sr_desc; /* OPTIONAL */
+ int sr_obsolete; /* 0=no, 1=yes */
+ char *sr_nameform; /* REQUIRED */
+ int sr_nsup_ruleids;/* number of sr_sup_ruleids */
+ int *sr_sup_ruleids;/* OPTIONAL */
+ LDAPSchemaExtensionItem **sr_extensions; /* OPTIONAL */
+} LDAPStructureRule;
+
+/*
+ * Misc macros
+ */
+#define LDAP_SCHEMA_NO 0
+#define LDAP_SCHEMA_YES 1
+
+#define LDAP_SCHEMA_USER_APPLICATIONS 0
+#define LDAP_SCHEMA_DIRECTORY_OPERATION 1
+#define LDAP_SCHEMA_DISTRIBUTED_OPERATION 2
+#define LDAP_SCHEMA_DSA_OPERATION 3
+
+#define LDAP_SCHEMA_ABSTRACT 0
+#define LDAP_SCHEMA_STRUCTURAL 1
+#define LDAP_SCHEMA_AUXILIARY 2
+
+
+/*
+ * Flags that control how liberal the parsing routines are.
+ */
+#define LDAP_SCHEMA_ALLOW_NONE 0x00U /* Strict parsing */
+#define LDAP_SCHEMA_ALLOW_NO_OID 0x01U /* Allow missing oid */
+#define LDAP_SCHEMA_ALLOW_QUOTED 0x02U /* Allow bogus extra quotes */
+#define LDAP_SCHEMA_ALLOW_DESCR 0x04U /* Allow descr instead of OID */
+#define LDAP_SCHEMA_ALLOW_DESCR_PREFIX 0x08U /* Allow descr as OID prefix */
+#define LDAP_SCHEMA_ALLOW_OID_MACRO 0x10U /* Allow OID macros in slapd */
+#define LDAP_SCHEMA_ALLOW_OUT_OF_ORDER_FIELDS 0x20U /* Allow fields in most any order */
+#define LDAP_SCHEMA_ALLOW_ALL 0x3fU /* Be very liberal in parsing */
+#define LDAP_SCHEMA_SKIP 0x80U /* Don't malloc any result */
+
+
+LDAP_F( LDAP_CONST char * )
+ldap_syntax2name LDAP_P((
+ LDAPSyntax * syn ));
+
+LDAP_F( LDAP_CONST char * )
+ldap_matchingrule2name LDAP_P((
+ LDAPMatchingRule * mr ));
+
+LDAP_F( LDAP_CONST char * )
+ldap_matchingruleuse2name LDAP_P((
+ LDAPMatchingRuleUse * mru ));
+
+LDAP_F( LDAP_CONST char * )
+ldap_attributetype2name LDAP_P((
+ LDAPAttributeType * at ));
+
+LDAP_F( LDAP_CONST char * )
+ldap_objectclass2name LDAP_P((
+ LDAPObjectClass * oc ));
+
+LDAP_F( LDAP_CONST char * )
+ldap_contentrule2name LDAP_P((
+ LDAPContentRule * cr ));
+
+LDAP_F( LDAP_CONST char * )
+ldap_nameform2name LDAP_P((
+ LDAPNameForm * nf ));
+
+LDAP_F( LDAP_CONST char * )
+ldap_structurerule2name LDAP_P((
+ LDAPStructureRule * sr ));
+
+LDAP_F( void )
+ldap_syntax_free LDAP_P((
+ LDAPSyntax * syn ));
+
+LDAP_F( void )
+ldap_matchingrule_free LDAP_P((
+ LDAPMatchingRule * mr ));
+
+LDAP_F( void )
+ldap_matchingruleuse_free LDAP_P((
+ LDAPMatchingRuleUse * mr ));
+
+LDAP_F( void )
+ldap_attributetype_free LDAP_P((
+ LDAPAttributeType * at ));
+
+LDAP_F( void )
+ldap_objectclass_free LDAP_P((
+ LDAPObjectClass * oc ));
+
+LDAP_F( void )
+ldap_contentrule_free LDAP_P((
+ LDAPContentRule * cr ));
+
+LDAP_F( void )
+ldap_nameform_free LDAP_P((
+ LDAPNameForm * nf ));
+
+LDAP_F( void )
+ldap_structurerule_free LDAP_P((
+ LDAPStructureRule * sr ));
+
+LDAP_F( LDAPStructureRule * )
+ldap_str2structurerule LDAP_P((
+ LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags ));
+
+LDAP_F( LDAPNameForm * )
+ldap_str2nameform LDAP_P((
+ LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags ));
+
+LDAP_F( LDAPContentRule * )
+ldap_str2contentrule LDAP_P((
+ LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags ));
+
+LDAP_F( LDAPObjectClass * )
+ldap_str2objectclass LDAP_P((
+ LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags ));
+
+LDAP_F( LDAPAttributeType * )
+ldap_str2attributetype LDAP_P((
+ LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags ));
+
+LDAP_F( LDAPSyntax * )
+ldap_str2syntax LDAP_P((
+ LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags ));
+
+LDAP_F( LDAPMatchingRule * )
+ldap_str2matchingrule LDAP_P((
+ LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags ));
+
+LDAP_F( LDAPMatchingRuleUse * )
+ldap_str2matchingruleuse LDAP_P((
+ LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags ));
+
+LDAP_F( char * )
+ldap_structurerule2str LDAP_P((
+ LDAPStructureRule * sr ));
+
+LDAP_F( struct berval * )
+ldap_structurerule2bv LDAP_P((
+ LDAPStructureRule * sr, struct berval *bv ));
+
+LDAP_F( char * )
+ldap_nameform2str LDAP_P((
+ LDAPNameForm * nf ));
+
+LDAP_F( struct berval * )
+ldap_nameform2bv LDAP_P((
+ LDAPNameForm * nf, struct berval *bv ));
+
+LDAP_F( char * )
+ldap_contentrule2str LDAP_P((
+ LDAPContentRule * cr ));
+
+LDAP_F( struct berval * )
+ldap_contentrule2bv LDAP_P((
+ LDAPContentRule * cr, struct berval *bv ));
+
+LDAP_F( char * )
+ldap_objectclass2str LDAP_P((
+ LDAPObjectClass * oc ));
+
+LDAP_F( struct berval * )
+ldap_objectclass2bv LDAP_P((
+ LDAPObjectClass * oc, struct berval *bv ));
+
+LDAP_F( char * )
+ldap_attributetype2str LDAP_P((
+ LDAPAttributeType * at ));
+
+LDAP_F( struct berval * )
+ldap_attributetype2bv LDAP_P((
+ LDAPAttributeType * at, struct berval *bv ));
+
+LDAP_F( char * )
+ldap_syntax2str LDAP_P((
+ LDAPSyntax * syn ));
+
+LDAP_F( struct berval * )
+ldap_syntax2bv LDAP_P((
+ LDAPSyntax * syn, struct berval *bv ));
+
+LDAP_F( char * )
+ldap_matchingrule2str LDAP_P((
+ LDAPMatchingRule * mr ));
+
+LDAP_F( struct berval * )
+ldap_matchingrule2bv LDAP_P((
+ LDAPMatchingRule * mr, struct berval *bv ));
+
+LDAP_F( char * )
+ldap_matchingruleuse2str LDAP_P((
+ LDAPMatchingRuleUse * mru ));
+
+LDAP_F( struct berval * )
+ldap_matchingruleuse2bv LDAP_P((
+ LDAPMatchingRuleUse * mru, struct berval *bv ));
+
+LDAP_F( char * )
+ldap_scherr2str LDAP_P((
+ int code )) LDAP_GCCATTR((const));
+
+LDAP_END_DECL
+
+#endif
+
diff --git a/include/ldap_utf8.h b/include/ldap_utf8.h
new file mode 100644
index 0000000..663043a
--- /dev/null
+++ b/include/ldap_utf8.h
@@ -0,0 +1,106 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* This notice applies to changes, created by or for Novell, Inc.,
+ * to preexisting works for which notices appear elsewhere in this file.
+ *
+ * Copyright (C) 2000 Novell, Inc. All Rights Reserved.
+ *
+ * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
+ * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
+ * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
+ * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
+ * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
+ * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
+ * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
+ * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
+ */
+/* Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
+ * can be found in the file "build/LICENSE-2.0.1" in this distribution
+ * of OpenLDAP Software.
+ */
+
+#ifndef _LDAP_UTF8_H
+#define _LDAP_UTF8_H
+
+#include <lber_types.h> /* get ber_*_t */
+
+/*
+ * UTF-8 Utility Routines
+ */
+
+LDAP_BEGIN_DECL
+
+#define LDAP_UCS4_INVALID (0x80000000U)
+typedef ber_int_t ldap_ucs4_t;
+
+
+/* LDAP_MAX_UTF8_LEN is 3 or 6 depending on size of wchar_t */
+#define LDAP_MAX_UTF8_LEN ( sizeof(wchar_t) * 3/2 )
+
+/* Unicode conversion routines */
+LDAP_F( ldap_ucs4_t ) ldap_x_utf8_to_ucs4( LDAP_CONST char * p );
+LDAP_F( int ) ldap_x_ucs4_to_utf8( ldap_ucs4_t c, char *buf );
+
+
+/*
+ * Wide Char / UTF-8 Conversion Routines
+ */
+
+/* UTF-8 character to Wide Char */
+LDAP_F(int) ldap_x_utf8_to_wc LDAP_P((
+ wchar_t *wchar, LDAP_CONST char *utf8char ));
+
+/* UTF-8 string to Wide Char string */
+LDAP_F(int) ldap_x_utf8s_to_wcs LDAP_P((
+ wchar_t *wcstr, LDAP_CONST char *utf8str, size_t count ));
+
+/* Wide Char to UTF-8 character */
+LDAP_F(int) ldap_x_wc_to_utf8 LDAP_P((
+ char *utf8char, wchar_t wchar, size_t count ));
+
+/* Wide Char string to UTF-8 string */
+LDAP_F(int) ldap_x_wcs_to_utf8s LDAP_P((
+ char *utf8str, LDAP_CONST wchar_t *wcstr, size_t count ));
+
+/*
+ * MultiByte Char / UTF-8 Conversion Routines
+ */
+
+/* UTF-8 character to MultiByte character */
+LDAP_F(int) ldap_x_utf8_to_mb LDAP_P((
+ char *mbchar, LDAP_CONST char *utf8char,
+ int (*ldap_f_wctomb)( char *mbchar, wchar_t wchar )));
+
+/* UTF-8 string to MultiByte string */
+LDAP_F(int) ldap_x_utf8s_to_mbs LDAP_P((
+ char *mbstr, LDAP_CONST char *utf8str, size_t count,
+ size_t (*ldap_f_wcstombs)( char *mbstr,
+ LDAP_CONST wchar_t *wcstr, size_t count) ));
+
+/* MultiByte character to UTF-8 character */
+LDAP_F(int) ldap_x_mb_to_utf8 LDAP_P((
+ char *utf8char, LDAP_CONST char *mbchar, size_t mbsize,
+ int (*ldap_f_mbtowc)( wchar_t *wchar,
+ LDAP_CONST char *mbchar, size_t count) ));
+
+/* MultiByte string to UTF-8 string */
+LDAP_F(int) ldap_x_mbs_to_utf8s LDAP_P((
+ char *utf8str, LDAP_CONST char *mbstr, size_t count,
+ size_t (*ldap_f_mbstowcs)( wchar_t *wcstr,
+ LDAP_CONST char *mbstr, size_t count) ));
+
+LDAP_END_DECL
+
+#endif /* _LDAP_UTF8_H */
diff --git a/include/ldif.h b/include/ldif.h
new file mode 100644
index 0000000..62cacdc
--- /dev/null
+++ b/include/ldif.h
@@ -0,0 +1,171 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1996 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#ifndef _LDIF_H
+#define _LDIF_H
+
+#include <ldap_cdefs.h>
+
+LDAP_BEGIN_DECL
+
+/* This is NOT a bogus extern declaration (unlike ldap_debug) */
+LDAP_LDIF_V (int) ldif_debug;
+
+#define LDIF_LINE_WIDTH 78 /* default maximum length of LDIF lines */
+#define LDIF_LINE_WIDTH_MAX ((ber_len_t)-1) /* maximum length of LDIF lines */
+#define LDIF_LINE_WIDTH_WRAP(wrap) ((wrap) == 0 ? LDIF_LINE_WIDTH : (wrap))
+
+/*
+ * Macro to calculate maximum number of bytes that the base64 equivalent
+ * of an item that is "len" bytes long will take up. Base64 encoding
+ * uses one byte for every six bits in the value plus up to two pad bytes.
+ */
+#define LDIF_BASE64_LEN(len) (((len) * 4 / 3 ) + 3)
+
+/*
+ * Macro to calculate maximum size that an LDIF-encoded type (length
+ * tlen) and value (length vlen) will take up: room for type + ":: " +
+ * first newline + base64 value + continued lines. Each continued line
+ * needs room for a newline and a leading space character.
+ */
+#define LDIF_SIZE_NEEDED(nlen,vlen) LDIF_SIZE_NEEDED_WRAP(nlen, vlen, 0)
+
+#define LDIF_SIZE_NEEDED_WRAP(nlen,vlen,wrap) \
+ ((nlen) + 4 + LDIF_BASE64_LEN(vlen) \
+ + ((wrap) == 0 ? ((LDIF_BASE64_LEN(vlen) + (nlen) + 3) / ( LDIF_LINE_WIDTH-1 ) * 2 ) : \
+ ((wrap) == LDIF_LINE_WIDTH_MAX ? 0 : ((LDIF_BASE64_LEN(vlen) + (nlen) + 3) / (wrap-1) * 2 ))))
+
+LDAP_LDIF_F( int )
+ldif_parse_line LDAP_P((
+ LDAP_CONST char *line,
+ char **name,
+ char **value,
+ ber_len_t *vlen ));
+
+LDAP_LDIF_F( int )
+ldif_parse_line2 LDAP_P((
+ char *line,
+ struct berval *type,
+ struct berval *value,
+ int *freeval ));
+
+LDAP_LDIF_F( FILE * )
+ldif_open_url LDAP_P(( LDAP_CONST char *urlstr ));
+
+LDAP_LDIF_F( int )
+ldif_fetch_url LDAP_P((
+ LDAP_CONST char *line,
+ char **value,
+ ber_len_t *vlen ));
+
+LDAP_LDIF_F( char * )
+ldif_getline LDAP_P(( char **next ));
+
+LDAP_LDIF_F( int )
+ldif_countlines LDAP_P(( LDAP_CONST char *line ));
+
+/* ldif_ropen, rclose, read_record - just for reading LDIF files,
+ * no special open/close needed to write LDIF files.
+ */
+typedef struct LDIFFP {
+ FILE *fp;
+ struct LDIFFP *prev;
+} LDIFFP;
+
+LDAP_LDIF_F( LDIFFP * )
+ldif_open LDAP_P(( LDAP_CONST char *file, LDAP_CONST char *mode ));
+
+/* ldif_open equivalent that opens ldif stream in memory rather than from file */
+LDAP_LDIF_F( LDIFFP * )
+ldif_open_mem LDAP_P(( char *ldif, size_t size, LDAP_CONST char *mode ));
+
+LDAP_LDIF_F( void )
+ldif_close LDAP_P(( LDIFFP * ));
+
+LDAP_LDIF_F( int )
+ldif_read_record LDAP_P((
+ LDIFFP *fp,
+ unsigned long *lineno,
+ char **bufp,
+ int *buflen ));
+
+LDAP_LDIF_F( int )
+ldif_must_b64_encode_register LDAP_P((
+ LDAP_CONST char *name,
+ LDAP_CONST char *oid ));
+
+LDAP_LDIF_F( void )
+ldif_must_b64_encode_release LDAP_P(( void ));
+
+#define LDIF_PUT_NOVALUE 0x0000 /* no value */
+#define LDIF_PUT_VALUE 0x0001 /* value w/ auto detection */
+#define LDIF_PUT_TEXT 0x0002 /* assume text */
+#define LDIF_PUT_BINARY 0x0004 /* assume binary (convert to base64) */
+#define LDIF_PUT_B64 0x0008 /* pre-converted base64 value */
+
+#define LDIF_PUT_COMMENT 0x0010 /* comment */
+#define LDIF_PUT_URL 0x0020 /* url */
+#define LDIF_PUT_SEP 0x0040 /* separator */
+
+LDAP_LDIF_F( void )
+ldif_sput LDAP_P((
+ char **out,
+ int type,
+ LDAP_CONST char *name,
+ LDAP_CONST char *val,
+ ber_len_t vlen ));
+
+LDAP_LDIF_F( void )
+ldif_sput_wrap LDAP_P((
+ char **out,
+ int type,
+ LDAP_CONST char *name,
+ LDAP_CONST char *val,
+ ber_len_t vlen,
+ ber_len_t wrap ));
+
+LDAP_LDIF_F( char * )
+ldif_put LDAP_P((
+ int type,
+ LDAP_CONST char *name,
+ LDAP_CONST char *val,
+ ber_len_t vlen ));
+
+LDAP_LDIF_F( char * )
+ldif_put_wrap LDAP_P((
+ int type,
+ LDAP_CONST char *name,
+ LDAP_CONST char *val,
+ ber_len_t vlen,
+ ber_len_t wrap ));
+
+LDAP_LDIF_F( int )
+ldif_is_not_printable LDAP_P((
+ LDAP_CONST char *val,
+ ber_len_t vlen ));
+
+LDAP_END_DECL
+
+#endif /* _LDIF_H */
diff --git a/include/lutil.h b/include/lutil.h
new file mode 100644
index 0000000..fe0e6de
--- /dev/null
+++ b/include/lutil.h
@@ -0,0 +1,375 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _LUTIL_H
+#define _LUTIL_H 1
+
+#include <ldap_cdefs.h>
+#include <lber_types.h>
+#include <ac/socket.h>
+
+#ifdef HAVE_TCPD
+# include <tcpd.h>
+# define LUTIL_STRING_UNKNOWN STRING_UNKNOWN
+#else /* ! TCP Wrappers */
+# define LUTIL_STRING_UNKNOWN "unknown"
+#endif /* ! TCP Wrappers */
+
+/*
+ * Include file for LDAP utility routine
+ */
+
+LDAP_BEGIN_DECL
+
+/* n octets encode into ceiling(n/3) * 4 bytes */
+/* Avoid floating point math through extra padding */
+
+#define LUTIL_BASE64_ENCODE_LEN(n) (((n)+2)/3 * 4)
+#define LUTIL_BASE64_DECODE_LEN(n) ((n)/4*3)
+
+/* ISC Base64 Routines */
+/* base64.c */
+
+LDAP_LUTIL_F( int )
+lutil_b64_ntop LDAP_P((
+ unsigned char const *,
+ size_t,
+ char *,
+ size_t));
+
+LDAP_LUTIL_F( int )
+lutil_b64_pton LDAP_P((
+ char const *,
+ unsigned char *,
+ size_t));
+
+/* detach.c */
+LDAP_LUTIL_F( int )
+lutil_detach LDAP_P((
+ int debug,
+ int do_close));
+
+/* entropy.c */
+LDAP_LUTIL_F( int )
+lutil_entropy LDAP_P((
+ unsigned char *buf,
+ ber_len_t nbytes ));
+
+/* passfile.c */
+struct berval; /* avoid pulling in lber.h */
+
+LDAP_LUTIL_F( int )
+lutil_get_filed_password LDAP_P((
+ const char *filename,
+ struct berval * ));
+
+/* passwd.c */
+struct lutil_pw_scheme;
+
+#define LUTIL_PASSWD_OK (0)
+#define LUTIL_PASSWD_ERR (-1)
+
+typedef int (LUTIL_PASSWD_CHK_FUNC)(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text );
+
+typedef int (LUTIL_PASSWD_HASH_FUNC) (
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text );
+
+LDAP_LUTIL_F( int )
+lutil_passwd_add LDAP_P((
+ struct berval *scheme,
+ LUTIL_PASSWD_CHK_FUNC *chk_fn,
+ LUTIL_PASSWD_HASH_FUNC *hash_fn ));
+
+LDAP_LUTIL_F( void )
+lutil_passwd_init LDAP_P(( void ));
+
+LDAP_LUTIL_F( void )
+lutil_passwd_destroy LDAP_P(( void ));
+
+LDAP_LUTIL_F( int )
+lutil_authpasswd LDAP_P((
+ const struct berval *passwd, /* stored password */
+ const struct berval *cred, /* user supplied value */
+ const char **methods ));
+
+LDAP_LUTIL_F( int )
+lutil_authpasswd_hash LDAP_P((
+ const struct berval *cred,
+ struct berval **passwd, /* password to store */
+ struct berval **salt, /* salt to store */
+ const char *method ));
+
+#ifdef SLAPD_CRYPT
+typedef int (lutil_cryptfunc) LDAP_P((
+ const char *key,
+ const char *salt,
+ char **hash ));
+LDAP_LUTIL_V (lutil_cryptfunc *) lutil_cryptptr;
+#endif
+
+LDAP_LUTIL_F( int )
+lutil_passwd LDAP_P((
+ const struct berval *passwd, /* stored password */
+ const struct berval *cred, /* user supplied value */
+ const char **methods,
+ const char **text )); /* error message */
+
+LDAP_LUTIL_F( int )
+lutil_passwd_generate LDAP_P(( struct berval *pw, ber_len_t ));
+
+LDAP_LUTIL_F( int )
+lutil_passwd_hash LDAP_P((
+ const struct berval *passwd,
+ const char *method,
+ struct berval *hash,
+ const char **text ));
+
+LDAP_LUTIL_F( int )
+lutil_passwd_scheme LDAP_P((
+ const char *scheme ));
+
+LDAP_LUTIL_F( int )
+lutil_salt_format LDAP_P((
+ const char *format ));
+
+LDAP_LUTIL_F( int )
+lutil_passwd_string64 LDAP_P((
+ const struct berval *sc,
+ const struct berval *hash,
+ struct berval *b64,
+ const struct berval *salt ));
+
+/* utils.c */
+LDAP_LUTIL_F( char* )
+lutil_progname LDAP_P((
+ const char* name,
+ int argc,
+ char *argv[] ));
+
+typedef struct lutil_tm {
+ int tm_sec; /* seconds 0-60 (1 leap second) */
+ int tm_min; /* minutes 0-59 */
+ int tm_hour; /* hours 0-23 */
+ int tm_mday; /* day 1-31 */
+ int tm_mon; /* month 0-11 */
+ int tm_year; /* year - 1900 */
+ int tm_nsec; /* nanoseconds */
+ int tm_usub; /* submicro */
+} lutil_tm;
+
+typedef struct lutil_timet {
+ unsigned int tt_sec; /* seconds since epoch, 0000 or 1970 */
+ int tt_gsec; /* seconds since epoch, high 7 bits, maybe sign-flipped */
+ /* sign flipped to sort properly as unsigned ints */
+ unsigned int tt_nsec; /* nanoseconds */
+} lutil_timet;
+
+/* Parse a timestamp string into a structure */
+LDAP_LUTIL_F( int )
+lutil_parsetime LDAP_P((
+ char *atm, struct lutil_tm * ));
+
+/* Convert structured time to time in seconds since 1970 (Unix epoch) */
+LDAP_LUTIL_F( int )
+lutil_tm2time LDAP_P((
+ struct lutil_tm *, struct lutil_timet * ));
+
+/* Convert structured time to time in seconds since 0000 (Proleptic Gregorian) */
+LDAP_LUTIL_F( int )
+lutil_tm2gtime LDAP_P((
+ struct lutil_tm *, struct lutil_timet * ));
+
+#ifdef _WIN32
+LDAP_LUTIL_F( void )
+lutil_slashpath LDAP_P(( char* path ));
+#define LUTIL_SLASHPATH(p) lutil_slashpath(p)
+#else
+#define LUTIL_SLASHPATH(p)
+#endif
+
+LDAP_LUTIL_F( char* )
+lutil_strcopy LDAP_P(( char *dst, const char *src ));
+
+LDAP_LUTIL_F( char* )
+lutil_strncopy LDAP_P(( char *dst, const char *src, size_t n ));
+
+LDAP_LUTIL_F( char* )
+lutil_memcopy LDAP_P(( char *dst, const char *src, size_t n ));
+
+#define lutil_strbvcopy(a, bv) lutil_memcopy((a),(bv)->bv_val,(bv)->bv_len)
+
+struct tm;
+
+/* use this macro to statically allocate buffer for lutil_gentime */
+#define LDAP_LUTIL_GENTIME_BUFSIZE 22
+#define lutil_gentime(s,m,t) lutil_localtime((s),(m),(t),0)
+LDAP_LUTIL_F( size_t )
+lutil_localtime LDAP_P(( char *s, size_t smax, const struct tm *tm,
+ long delta ));
+
+#ifndef HAVE_MKSTEMP
+LDAP_LUTIL_F( int )
+mkstemp LDAP_P (( char * template ));
+#endif
+
+/* sockpair.c */
+LDAP_LUTIL_F( int )
+lutil_pair( ber_socket_t sd[2] );
+
+/* uuid.c */
+/* use this macro to allocate buffer for lutil_uuidstr */
+#define LDAP_LUTIL_UUIDSTR_BUFSIZE 40
+LDAP_LUTIL_F( size_t )
+lutil_uuidstr( char *buf, size_t len );
+
+LDAP_LUTIL_F( int )
+lutil_uuidstr_from_normalized(
+ char *uuid,
+ size_t uuidlen,
+ char *buf,
+ size_t buflen );
+
+/*
+ * Sometimes not all declarations in a header file are needed.
+ * An indicator to this is whether or not the symbol's type has
+ * been defined. Thus, we don't need to include a symbol if
+ * its type has not been defined through another header file.
+ */
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+LDAP_LUTIL_V (int) is_NT_Service;
+
+#ifdef _LDAP_PVT_THREAD_H
+LDAP_LUTIL_V (ldap_pvt_thread_cond_t) started_event;
+#endif /* _LDAP_PVT_THREAD_H */
+
+/* macros are different between Windows and Mingw */
+#if defined(_WINSVC_H) || defined(_WINSVC_)
+LDAP_LUTIL_V (SERVICE_STATUS) lutil_ServiceStatus;
+LDAP_LUTIL_V (SERVICE_STATUS_HANDLE) hlutil_ServiceStatus;
+#endif /* _WINSVC_H */
+
+LDAP_LUTIL_F (void)
+lutil_CommenceStartupProcessing( char *serverName, void (*stopper)(int)) ;
+
+LDAP_LUTIL_F (void)
+lutil_ReportShutdownComplete( void );
+
+LDAP_LUTIL_F (void *)
+lutil_getRegParam( char *svc, char *value );
+
+LDAP_LUTIL_F (int)
+lutil_srv_install( char* service, char * displayName, char* filename,
+ int auto_start );
+LDAP_LUTIL_F (int)
+lutil_srv_remove ( char* service, char* filename );
+
+#endif /* HAVE_NT_SERVICE_MANAGER */
+
+#ifdef HAVE_NT_EVENT_LOG
+LDAP_LUTIL_F (void)
+lutil_LogStartedEvent( char *svc, int slap_debug, char *configfile, char *urls );
+
+LDAP_LUTIL_F (void)
+lutil_LogStoppedEvent( char *svc );
+#endif
+
+#ifdef HAVE_EBCDIC
+/* Generally this has only been used to put '\n' to stdout. We need to
+ * make sure it is output in EBCDIC.
+ */
+#undef putchar
+#undef putc
+#define putchar(c) putc((c), stdout)
+#define putc(c,fp) do { char x=(c); __atoe_l(&x,1); putc(x,fp); } while(0)
+#endif
+
+LDAP_LUTIL_F (int)
+lutil_atoix( int *v, const char *s, int x );
+
+LDAP_LUTIL_F (int)
+lutil_atoux( unsigned *v, const char *s, int x );
+
+LDAP_LUTIL_F (int)
+lutil_atolx( long *v, const char *s, int x );
+
+LDAP_LUTIL_F (int)
+lutil_atoulx( unsigned long *v, const char *s, int x );
+
+#define lutil_atoi(v, s) lutil_atoix((v), (s), 10)
+#define lutil_atou(v, s) lutil_atoux((v), (s), 10)
+#define lutil_atol(v, s) lutil_atolx((v), (s), 10)
+#define lutil_atoul(v, s) lutil_atoulx((v), (s), 10)
+
+#ifdef HAVE_LONG_LONG
+#if defined(HAVE_STRTOLL) || defined(HAVE_STRTOQ)
+LDAP_LUTIL_F (int)
+lutil_atollx( long long *v, const char *s, int x );
+#define lutil_atoll(v, s) lutil_atollx((v), (s), 10)
+#endif /* HAVE_STRTOLL || HAVE_STRTOQ */
+
+#if defined(HAVE_STRTOULL) || defined(HAVE_STRTOUQ)
+LDAP_LUTIL_F (int)
+lutil_atoullx( unsigned long long *v, const char *s, int x );
+#define lutil_atoull(v, s) lutil_atoullx((v), (s), 10)
+#endif /* HAVE_STRTOULL || HAVE_STRTOUQ */
+#endif /* HAVE_LONG_LONG */
+
+LDAP_LUTIL_F (int)
+lutil_str2bin( struct berval *in, struct berval *out, void *ctx );
+
+/* Parse and unparse time intervals */
+LDAP_LUTIL_F (int)
+lutil_parse_time( const char *in, unsigned long *tp );
+
+LDAP_LUTIL_F (int)
+lutil_unparse_time( char *buf, size_t buflen, unsigned long t );
+
+#ifdef timerdiv
+#define lutil_timerdiv timerdiv
+#else /* ! timerdiv */
+/* works inplace (x == t) */
+#define lutil_timerdiv(t,d,x) \
+ do { \
+ time_t s = (t)->tv_sec; \
+ assert( d > 0 ); \
+ (x)->tv_sec = s / d; \
+ (x)->tv_usec = ( (t)->tv_usec + 1000000 * ( s % d ) ) / d; \
+ } while ( 0 )
+#endif /* ! timerdiv */
+
+#ifdef timermul
+#define lutil_timermul timermul
+#else /* ! timermul */
+/* works inplace (x == t) */
+#define lutil_timermul(t,m,x) \
+ do { \
+ time_t u = (t)->tv_usec * m; \
+ assert( m > 0 ); \
+ (x)->tv_sec = (t)->tv_sec * m + u / 1000000; \
+ (x)->tv_usec = u % 1000000; \
+ } while ( 0 );
+#endif /* ! timermul */
+
+LDAP_END_DECL
+
+#endif /* _LUTIL_H */
diff --git a/include/lutil_hash.h b/include/lutil_hash.h
new file mode 100644
index 0000000..2b1a744
--- /dev/null
+++ b/include/lutil_hash.h
@@ -0,0 +1,78 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _LUTIL_HASH_H_
+#define _LUTIL_HASH_H_
+
+#include <lber_types.h>
+
+LDAP_BEGIN_DECL
+
+#define LUTIL_HASH_BYTES 4
+
+#ifdef HAVE_LONG_LONG
+
+typedef union lutil_HASHContext {
+ ber_uint_t hash;
+ unsigned long long hash64;
+} lutil_HASH_CTX;
+
+#else /* !HAVE_LONG_LONG */
+
+typedef struct lutil_HASHContext {
+ ber_uint_t hash;
+} lutil_HASH_CTX;
+
+#endif /* HAVE_LONG_LONG */
+
+LDAP_LUTIL_F( void )
+lutil_HASHInit LDAP_P((
+ lutil_HASH_CTX *context));
+
+LDAP_LUTIL_F( void )
+lutil_HASHUpdate LDAP_P((
+ lutil_HASH_CTX *context,
+ unsigned char const *buf,
+ ber_len_t len));
+
+LDAP_LUTIL_F( void )
+lutil_HASHFinal LDAP_P((
+ unsigned char digest[LUTIL_HASH_BYTES],
+ lutil_HASH_CTX *context));
+
+#ifdef HAVE_LONG_LONG
+
+#define LUTIL_HASH64_BYTES 8
+
+LDAP_LUTIL_F( void )
+lutil_HASH64Init LDAP_P((
+ lutil_HASH_CTX *context));
+
+LDAP_LUTIL_F( void )
+lutil_HASH64Update LDAP_P((
+ lutil_HASH_CTX *context,
+ unsigned char const *buf,
+ ber_len_t len));
+
+LDAP_LUTIL_F( void )
+lutil_HASH64Final LDAP_P((
+ unsigned char digest[LUTIL_HASH64_BYTES],
+ lutil_HASH_CTX *context));
+
+#endif /* HAVE_LONG_LONG */
+
+LDAP_END_DECL
+
+#endif /* _LUTIL_HASH_H_ */
diff --git a/include/lutil_ldap.h b/include/lutil_ldap.h
new file mode 100644
index 0000000..179c7b8
--- /dev/null
+++ b/include/lutil_ldap.h
@@ -0,0 +1,47 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _LUTIL_LDAP_H
+#define _LUTIL_LDAP_H 1
+
+#include <ldap_cdefs.h>
+#include <lber_types.h>
+
+/*
+ * Include file for lutil LDAP routines
+ */
+
+LDAP_BEGIN_DECL
+
+LDAP_LUTIL_F( void )
+lutil_sasl_freedefs LDAP_P((
+ void *defaults ));
+
+LDAP_LUTIL_F( void * )
+lutil_sasl_defaults LDAP_P((
+ LDAP *ld,
+ char *mech,
+ char *realm,
+ char *authcid,
+ char *passwd,
+ char *authzid ));
+
+LDAP_LUTIL_F( int )
+lutil_sasl_interact LDAP_P((
+ LDAP *ld, unsigned flags, void *defaults, void *p ));
+
+LDAP_END_DECL
+
+#endif /* _LUTIL_LDAP_H */
diff --git a/include/lutil_lockf.h b/include/lutil_lockf.h
new file mode 100644
index 0000000..b24bde8
--- /dev/null
+++ b/include/lutil_lockf.h
@@ -0,0 +1,34 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* File locking methods
+ *
+ * lutil_lockf() will block until an exclusive lock is acquired.
+ */
+
+#ifndef _LUTIL_LOCKF_H_
+#define _LUTIL_LOCKF_H_
+
+LDAP_BEGIN_DECL
+
+LDAP_LUTIL_F( int )
+lutil_lockf LDAP_P(( int fd ));
+
+LDAP_LUTIL_F( int )
+lutil_unlockf LDAP_P(( int fd ));
+
+LDAP_END_DECL
+
+#endif /* _LUTIL_LOCKF_H_ */
diff --git a/include/lutil_md5.h b/include/lutil_md5.h
new file mode 100644
index 0000000..8c1c112
--- /dev/null
+++ b/include/lutil_md5.h
@@ -0,0 +1,64 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _LUTIL_MD5_H_
+#define _LUTIL_MD5_H_
+
+#include <lber_types.h>
+
+LDAP_BEGIN_DECL
+
+/* Unlike previous versions of this code, ber_int_t need not be exactly
+ 32 bits, merely 32 bits or more. Choosing a data type which is 32
+ bits instead of 64 is not important; speed is considerably more
+ important. ANSI guarantees that "unsigned long" will be big enough,
+ and always using it seems to have few disadvantages. */
+
+#define LUTIL_MD5_BYTES 16
+
+struct lutil_MD5Context {
+ ber_uint_t buf[4];
+ ber_uint_t bits[2];
+ unsigned char in[64];
+};
+
+LDAP_LUTIL_F( void )
+lutil_MD5Init LDAP_P((
+ struct lutil_MD5Context *context));
+
+LDAP_LUTIL_F( void )
+lutil_MD5Update LDAP_P((
+ struct lutil_MD5Context *context,
+ unsigned char const *buf,
+ ber_len_t len));
+
+LDAP_LUTIL_F( void )
+lutil_MD5Final LDAP_P((
+ unsigned char digest[16],
+ struct lutil_MD5Context *context));
+
+LDAP_LUTIL_F( void )
+lutil_MD5Transform LDAP_P((
+ ber_uint_t buf[4],
+ const unsigned char in[64]));
+
+/*
+ * This is needed to make RSAREF happy on some MS-DOS compilers.
+ */
+typedef struct lutil_MD5Context lutil_MD5_CTX;
+
+LDAP_END_DECL
+
+#endif /* _LUTIL_MD5_H_ */
diff --git a/include/lutil_meter.h b/include/lutil_meter.h
new file mode 100644
index 0000000..66105fa
--- /dev/null
+++ b/include/lutil_meter.h
@@ -0,0 +1,70 @@
+/* lutil_meter.h - progress meters */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright (c) 2009 by Emily Backes, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Emily Backes for inclusion
+ * in OpenLDAP software.
+ */
+
+#ifndef _LUTIL_METER_H
+#define _LUTIL_METER_H
+
+#include "portable.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <ac/stdlib.h>
+#include <ac/time.h>
+
+typedef struct {
+ int (*display_open) (void **datap);
+ int (*display_update) (void **datap, double frac, time_t remaining_time, time_t elapsed, double byte_rate);
+ int (*display_close) (void **datap);
+} lutil_meter_display_t;
+
+typedef struct {
+ int (*estimator_open) (void **datap);
+ int (*estimator_update) (void **datap, double start, double frac, time_t *remaining_time);
+ int (*estimator_close) (void **datap);
+} lutil_meter_estimator_t;
+
+typedef struct {
+ const lutil_meter_display_t *display;
+ void * display_data;
+ const lutil_meter_estimator_t *estimator;
+ void * estimator_data;
+ double start_time;
+ double last_update;
+ size_t goal_value;
+ size_t last_position;
+} lutil_meter_t;
+
+extern const lutil_meter_display_t lutil_meter_text_display;
+extern const lutil_meter_estimator_t lutil_meter_linear_estimator;
+
+extern int lutil_meter_open (
+ lutil_meter_t *lutil_meter,
+ const lutil_meter_display_t *display,
+ const lutil_meter_estimator_t *estimator,
+ size_t goal_value);
+extern int lutil_meter_update (
+ lutil_meter_t *lutil_meter,
+ size_t position,
+ int force);
+extern int lutil_meter_close (lutil_meter_t *lutil_meter);
+
+#endif /* _LUTIL_METER_H */
diff --git a/include/lutil_sha1.h b/include/lutil_sha1.h
new file mode 100644
index 0000000..87ea86c
--- /dev/null
+++ b/include/lutil_sha1.h
@@ -0,0 +1,77 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* This version is based on:
+ * $OpenBSD: sha1.h,v 1.8 1997/07/15 01:54:23 millert Exp $ */
+
+#ifndef _LUTIL_SHA1_H_
+#define _LUTIL_SHA1_H_
+
+#include <ldap_cdefs.h>
+#include <ac/bytes.h>
+
+#ifdef AC_INT4_TYPE
+
+LDAP_BEGIN_DECL
+
+
+/*
+ * SHA-1 in C
+ * By Steve Reid <steve@edmweb.com>
+ */
+#define LUTIL_SHA1_BYTES 20
+
+/* This code assumes char are 8-bits and uint32 are 32-bits */
+typedef ac_uint4 uint32;
+
+typedef struct {
+ uint32 state[5];
+ uint32 count[2];
+ unsigned char buffer[64];
+} lutil_SHA1_CTX;
+
+LDAP_LUTIL_F( void )
+lutil_SHA1Transform
+ LDAP_P((uint32 state[5], const unsigned char buffer[64]));
+
+LDAP_LUTIL_F( void )
+lutil_SHA1Init
+ LDAP_P((lutil_SHA1_CTX *context));
+
+LDAP_LUTIL_F( void )
+lutil_SHA1Update
+ LDAP_P((lutil_SHA1_CTX *context, const unsigned char *data, uint32 len));
+
+LDAP_LUTIL_F( void )
+lutil_SHA1Final
+ LDAP_P((unsigned char digest[20], lutil_SHA1_CTX *context));
+
+LDAP_LUTIL_F( char * )
+lutil_SHA1End
+ LDAP_P((lutil_SHA1_CTX *, char *));
+
+LDAP_LUTIL_F( char * )
+lutil_SHA1File
+ LDAP_P((char *, char *));
+
+LDAP_LUTIL_F( char * )
+lutil_SHA1Data
+ LDAP_P((const unsigned char *, size_t, char *));
+
+LDAP_END_DECL
+
+#endif /* AC_INT4_TYPE */
+
+#endif /* _LUTIL_SHA1_H_ */
diff --git a/include/openldap.h b/include/openldap.h
new file mode 100644
index 0000000..70a39c6
--- /dev/null
+++ b/include/openldap.h
@@ -0,0 +1,39 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2019-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* openldap.h - Header for openldap specific interfaces. */
+
+#ifndef _OPENLDAP_H
+#define _OPENLDAP_H 1
+
+#include <ldap.h>
+
+LDAP_BEGIN_DECL
+
+#define LDAP_PROTO_TCP 1 /* ldap:// */
+#define LDAP_PROTO_UDP 2 /* reserved */
+#define LDAP_PROTO_IPC 3 /* ldapi:// */
+#define LDAP_PROTO_EXT 4 /* user-defined socket/sockbuf */
+
+LDAP_F( int )
+ldap_init_fd LDAP_P((
+ ber_socket_t fd,
+ int proto,
+ LDAP_CONST char *url,
+ LDAP **ldp ));
+
+LDAP_END_DECL
+
+#endif /* _OPENLDAP_H */
diff --git a/include/portable.hin b/include/portable.hin
new file mode 100644
index 0000000..c4c1b1f
--- /dev/null
+++ b/include/portable.hin
@@ -0,0 +1,1195 @@
+/* include/portable.hin. Generated from configure.ac by autoheader. */
+
+
+/* begin of portable.h.pre */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _LDAP_PORTABLE_H
+#define _LDAP_PORTABLE_H
+
+/* define this if needed to get reentrant functions */
+#ifndef REENTRANT
+#undef REENTRANT
+#endif
+#ifndef _REENTRANT
+#undef _REENTRANT
+#endif
+
+/* define this if needed to get threadsafe functions */
+#ifndef THREADSAFE
+#undef THREADSAFE
+#endif
+#ifndef _THREADSAFE
+#undef _THREADSAFE
+#endif
+#ifndef THREAD_SAFE
+#undef THREAD_SAFE
+#endif
+#ifndef _THREAD_SAFE
+#undef _THREAD_SAFE
+#endif
+
+#ifndef _SGI_MP_SOURCE
+#undef _SGI_MP_SOURCE
+#endif
+
+/* end of portable.h.pre */
+
+
+/* Define if building universal (internal helper macro) */
+#undef AC_APPLE_UNIVERSAL_BUILD
+
+/* define to use both <string.h> and <strings.h> */
+#undef BOTH_STRINGS_H
+
+/* define if cross compiling */
+#undef CROSS_COMPILING
+
+/* set to the number of arguments ctime_r() expects */
+#undef CTIME_R_NARGS
+
+/* define if toupper() requires islower() */
+#undef C_UPPER_LOWER
+
+/* define if sys_errlist is not declared in stdio.h or errno.h */
+#undef DECL_SYS_ERRLIST
+
+/* define to enable slapi library */
+#undef ENABLE_SLAPI
+
+/* defined to be the EXE extension */
+#undef EXEEXT
+
+/* set to the number of arguments gethostbyaddr_r() expects */
+#undef GETHOSTBYADDR_R_NARGS
+
+/* set to the number of arguments gethostbyname_r() expects */
+#undef GETHOSTBYNAME_R_NARGS
+
+/* Define to 1 if `TIOCGWINSZ' requires <sys/ioctl.h>. */
+#undef GWINSZ_IN_SYS_IOCTL
+
+/* define if you have AIX security lib */
+#undef HAVE_AIX_SECURITY
+
+/* Define to 1 if you have the <argon2.h> header file. */
+#undef HAVE_ARGON2_H
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#undef HAVE_ARPA_INET_H
+
+/* Define to 1 if you have the <arpa/nameser.h> header file. */
+#undef HAVE_ARPA_NAMESER_H
+
+/* Define to 1 if you have the <assert.h> header file. */
+#undef HAVE_ASSERT_H
+
+/* Define to 1 if you have the `bcopy' function. */
+#undef HAVE_BCOPY
+
+/* Define to 1 if you have the <bits/types.h> header file. */
+#undef HAVE_BITS_TYPES_H
+
+/* Define to 1 if you have the `chroot' function. */
+#undef HAVE_CHROOT
+
+/* Define to 1 if you have the `clock_gettime' function. */
+#undef HAVE_CLOCK_GETTIME
+
+/* Define to 1 if you have the `closesocket' function. */
+#undef HAVE_CLOSESOCKET
+
+/* Define to 1 if you have the <conio.h> header file. */
+#undef HAVE_CONIO_H
+
+/* define if crypt(3) is available */
+#undef HAVE_CRYPT
+
+/* Define to 1 if you have the <crypt.h> header file. */
+#undef HAVE_CRYPT_H
+
+/* define if crypt_r() is also available */
+#undef HAVE_CRYPT_R
+
+/* Define to 1 if you have the `ctime_r' function. */
+#undef HAVE_CTIME_R
+
+/* define if you have Cyrus SASL */
+#undef HAVE_CYRUS_SASL
+
+/* define if your system supports /dev/poll */
+#undef HAVE_DEVPOLL
+
+/* Define to 1 if you have the <direct.h> header file. */
+#undef HAVE_DIRECT_H
+
+/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
+ */
+#undef HAVE_DIRENT_H
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
+#undef HAVE_DOPRNT
+
+/* define if system uses EBCDIC instead of ASCII */
+#undef HAVE_EBCDIC
+
+/* Define to 1 if you have the `endgrent' function. */
+#undef HAVE_ENDGRENT
+
+/* Define to 1 if you have the `endpwent' function. */
+#undef HAVE_ENDPWENT
+
+/* define if your system supports epoll */
+#undef HAVE_EPOLL
+
+/* Define to 1 if you have the <errno.h> header file. */
+#undef HAVE_ERRNO_H
+
+/* Define to 1 if you have the `fcntl' function. */
+#undef HAVE_FCNTL
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* define if you actually have FreeBSD fetch(3) */
+#undef HAVE_FETCH
+
+/* Define to 1 if you have the <filio.h> header file. */
+#undef HAVE_FILIO_H
+
+/* Define to 1 if you have the `flock' function. */
+#undef HAVE_FLOCK
+
+/* Define to 1 if you have the `fmemopen' function. */
+#undef HAVE_FMEMOPEN
+
+/* Define to 1 if you have the `fstat' function. */
+#undef HAVE_FSTAT
+
+/* Define to 1 if you have the `gai_strerror' function. */
+#undef HAVE_GAI_STRERROR
+
+/* Define to 1 if you have the `getaddrinfo' function. */
+#undef HAVE_GETADDRINFO
+
+/* Define to 1 if you have the `getdtablesize' function. */
+#undef HAVE_GETDTABLESIZE
+
+/* Define to 1 if you have the `geteuid' function. */
+#undef HAVE_GETEUID
+
+/* Define to 1 if you have the `getgrgid' function. */
+#undef HAVE_GETGRGID
+
+/* Define to 1 if you have the `gethostbyaddr_r' function. */
+#undef HAVE_GETHOSTBYADDR_R
+
+/* Define to 1 if you have the `gethostbyname_r' function. */
+#undef HAVE_GETHOSTBYNAME_R
+
+/* Define to 1 if you have the `gethostname' function. */
+#undef HAVE_GETHOSTNAME
+
+/* Define to 1 if you have the `getnameinfo' function. */
+#undef HAVE_GETNAMEINFO
+
+/* Define to 1 if you have the `getopt' function. */
+#undef HAVE_GETOPT
+
+/* Define to 1 if you have the <getopt.h> header file. */
+#undef HAVE_GETOPT_H
+
+/* Define to 1 if you have the `getpassphrase' function. */
+#undef HAVE_GETPASSPHRASE
+
+/* Define to 1 if you have the `getpeereid' function. */
+#undef HAVE_GETPEEREID
+
+/* Define to 1 if you have the `getpeerucred' function. */
+#undef HAVE_GETPEERUCRED
+
+/* Define to 1 if you have the `getpwnam' function. */
+#undef HAVE_GETPWNAM
+
+/* Define to 1 if you have the `getpwuid' function. */
+#undef HAVE_GETPWUID
+
+/* Define to 1 if you have the `getspnam' function. */
+#undef HAVE_GETSPNAM
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#undef HAVE_GETTIMEOFDAY
+
+/* Define to 1 if you have the <gmp.h> header file. */
+#undef HAVE_GMP_H
+
+/* Define to 1 if you have the `gmtime_r' function. */
+#undef HAVE_GMTIME_R
+
+/* define if you have GNUtls */
+#undef HAVE_GNUTLS
+
+/* Define to 1 if you have the <gnutls/gnutls.h> header file. */
+#undef HAVE_GNUTLS_GNUTLS_H
+
+/* if you have GNU Pth */
+#undef HAVE_GNU_PTH
+
+/* Define to 1 if you have the <grp.h> header file. */
+#undef HAVE_GRP_H
+
+/* Define to 1 if you have the `hstrerror' function. */
+#undef HAVE_HSTRERROR
+
+/* define to you inet_aton(3) is available */
+#undef HAVE_INET_ATON
+
+/* Define to 1 if you have the `inet_ntoa_b' function. */
+#undef HAVE_INET_NTOA_B
+
+/* Define to 1 if you have the `inet_ntop' function. */
+#undef HAVE_INET_NTOP
+
+/* Define to 1 if you have the `initgroups' function. */
+#undef HAVE_INITGROUPS
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the `ioctl' function. */
+#undef HAVE_IOCTL
+
+/* Define to 1 if you have the <io.h> header file. */
+#undef HAVE_IO_H
+
+/* define if your system supports kqueue */
+#undef HAVE_KQUEUE
+
+/* define if you have libargon2 */
+#undef HAVE_LIBARGON2
+
+/* define if you have -levent */
+#undef HAVE_LIBEVENT
+
+/* Define to 1 if you have the `gen' library (-lgen). */
+#undef HAVE_LIBGEN
+
+/* Define to 1 if you have the `gmp' library (-lgmp). */
+#undef HAVE_LIBGMP
+
+/* Define to 1 if you have the `inet' library (-linet). */
+#undef HAVE_LIBINET
+
+/* define if you have libtool -ltdl */
+#undef HAVE_LIBLTDL
+
+/* Define to 1 if you have the `net' library (-lnet). */
+#undef HAVE_LIBNET
+
+/* Define to 1 if you have the `nsl' library (-lnsl). */
+#undef HAVE_LIBNSL
+
+/* Define to 1 if you have the `nsl_s' library (-lnsl_s). */
+#undef HAVE_LIBNSL_S
+
+/* Define to 1 if you have the `socket' library (-lsocket). */
+#undef HAVE_LIBSOCKET
+
+/* define if you have libsodium */
+#undef HAVE_LIBSODIUM
+
+/* Define to 1 if you have the <libutil.h> header file. */
+#undef HAVE_LIBUTIL_H
+
+/* Define to 1 if you have the `V3' library (-lV3). */
+#undef HAVE_LIBV3
+
+/* Define to 1 if you have the <limits.h> header file. */
+#undef HAVE_LIMITS_H
+
+/* if you have LinuxThreads */
+#undef HAVE_LINUX_THREADS
+
+/* Define to 1 if you have the <locale.h> header file. */
+#undef HAVE_LOCALE_H
+
+/* Define to 1 if you have the `localtime_r' function. */
+#undef HAVE_LOCALTIME_R
+
+/* Define to 1 if you have the `lockf' function. */
+#undef HAVE_LOCKF
+
+/* Define to 1 if the system has the type `long long'. */
+#undef HAVE_LONG_LONG
+
+/* Define to 1 if you have the <ltdl.h> header file. */
+#undef HAVE_LTDL_H
+
+/* Define to 1 if you have the <malloc.h> header file. */
+#undef HAVE_MALLOC_H
+
+/* Define to 1 if you have the `memcpy' function. */
+#undef HAVE_MEMCPY
+
+/* Define to 1 if you have the `memmove' function. */
+#undef HAVE_MEMMOVE
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the `memrchr' function. */
+#undef HAVE_MEMRCHR
+
+/* Define to 1 if you have the `mkstemp' function. */
+#undef HAVE_MKSTEMP
+
+/* Define to 1 if you have the `mktemp' function. */
+#undef HAVE_MKTEMP
+
+/* define this if you have mkversion */
+#undef HAVE_MKVERSION
+
+/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
+#undef HAVE_NDIR_H
+
+/* Define to 1 if you have the <netinet/tcp.h> header file. */
+#undef HAVE_NETINET_TCP_H
+
+/* define if strerror_r returns char* instead of int */
+#undef HAVE_NONPOSIX_STRERROR_R
+
+/* if you have NT Event Log */
+#undef HAVE_NT_EVENT_LOG
+
+/* if you have NT Service Manager */
+#undef HAVE_NT_SERVICE_MANAGER
+
+/* if you have NT Threads */
+#undef HAVE_NT_THREADS
+
+/* define if you have OpenSSL */
+#undef HAVE_OPENSSL
+
+/* Define to 1 if you have the <openssl/bn.h> header file. */
+#undef HAVE_OPENSSL_BN_H
+
+/* Define to 1 if you have the <openssl/crypto.h> header file. */
+#undef HAVE_OPENSSL_CRYPTO_H
+
+/* Define to 1 if you have the <openssl/ssl.h> header file. */
+#undef HAVE_OPENSSL_SSL_H
+
+/* Define to 1 if you have the `pipe' function. */
+#undef HAVE_PIPE
+
+/* Define to 1 if you have the `poll' function. */
+#undef HAVE_POLL
+
+/* Define to 1 if you have the <poll.h> header file. */
+#undef HAVE_POLL_H
+
+/* Define to 1 if you have the <process.h> header file. */
+#undef HAVE_PROCESS_H
+
+/* Define to 1 if you have the <psap.h> header file. */
+#undef HAVE_PSAP_H
+
+/* define to pthreads API spec revision */
+#undef HAVE_PTHREADS
+
+/* define if you have pthread_detach function */
+#undef HAVE_PTHREAD_DETACH
+
+/* Define to 1 if you have the `pthread_getconcurrency' function. */
+#undef HAVE_PTHREAD_GETCONCURRENCY
+
+/* Define to 1 if you have the <pthread.h> header file. */
+#undef HAVE_PTHREAD_H
+
+/* Define to 1 if you have the `pthread_kill' function. */
+#undef HAVE_PTHREAD_KILL
+
+/* Define to 1 if you have the `pthread_kill_other_threads_np' function. */
+#undef HAVE_PTHREAD_KILL_OTHER_THREADS_NP
+
+/* define if you have pthread_rwlock_destroy function */
+#undef HAVE_PTHREAD_RWLOCK_DESTROY
+
+/* Define to 1 if you have the `pthread_setconcurrency' function. */
+#undef HAVE_PTHREAD_SETCONCURRENCY
+
+/* Define to 1 if you have the `pthread_yield' function. */
+#undef HAVE_PTHREAD_YIELD
+
+/* Define to 1 if you have the <pth.h> header file. */
+#undef HAVE_PTH_H
+
+/* Define to 1 if the system has the type `ptrdiff_t'. */
+#undef HAVE_PTRDIFF_T
+
+/* Define to 1 if you have the <pwd.h> header file. */
+#undef HAVE_PWD_H
+
+/* Define to 1 if you have the `read' function. */
+#undef HAVE_READ
+
+/* Define to 1 if you have the `recv' function. */
+#undef HAVE_RECV
+
+/* Define to 1 if you have the `recvfrom' function. */
+#undef HAVE_RECVFROM
+
+/* Define to 1 if you have the <regex.h> header file. */
+#undef HAVE_REGEX_H
+
+/* Define to 1 if you have the <resolv.h> header file. */
+#undef HAVE_RESOLV_H
+
+/* define if you have res_query() */
+#undef HAVE_RES_QUERY
+
+/* Define to 1 if you have the <sasl.h> header file. */
+#undef HAVE_SASL_H
+
+/* Define to 1 if you have the <sasl/sasl.h> header file. */
+#undef HAVE_SASL_SASL_H
+
+/* define if your SASL library has sasl_version() */
+#undef HAVE_SASL_VERSION
+
+/* Define to 1 if you have the <sched.h> header file. */
+#undef HAVE_SCHED_H
+
+/* Define to 1 if you have the `sched_yield' function. */
+#undef HAVE_SCHED_YIELD
+
+/* Define to 1 if you have the `send' function. */
+#undef HAVE_SEND
+
+/* Define to 1 if you have the `sendmsg' function. */
+#undef HAVE_SENDMSG
+
+/* Define to 1 if you have the `sendto' function. */
+#undef HAVE_SENDTO
+
+/* Define to 1 if you have the `setegid' function. */
+#undef HAVE_SETEGID
+
+/* Define to 1 if you have the `seteuid' function. */
+#undef HAVE_SETEUID
+
+/* Define to 1 if you have the `setgid' function. */
+#undef HAVE_SETGID
+
+/* Define to 1 if you have the `setpwfile' function. */
+#undef HAVE_SETPWFILE
+
+/* Define to 1 if you have the `setsid' function. */
+#undef HAVE_SETSID
+
+/* Define to 1 if you have the `setuid' function. */
+#undef HAVE_SETUID
+
+/* Define to 1 if you have the <sgtty.h> header file. */
+#undef HAVE_SGTTY_H
+
+/* Define to 1 if you have the <shadow.h> header file. */
+#undef HAVE_SHADOW_H
+
+/* Define to 1 if you have the `sigaction' function. */
+#undef HAVE_SIGACTION
+
+/* Define to 1 if you have the `signal' function. */
+#undef HAVE_SIGNAL
+
+/* Define to 1 if you have the `sigset' function. */
+#undef HAVE_SIGSET
+
+/* define if you have -lslp */
+#undef HAVE_SLP
+
+/* Define to 1 if you have the <slp.h> header file. */
+#undef HAVE_SLP_H
+
+/* Define to 1 if you have the `snprintf' function. */
+#undef HAVE_SNPRINTF
+
+/* Define to 1 if you have the <sodium.h> header file. */
+#undef HAVE_SODIUM_H
+
+/* if you have spawnlp() */
+#undef HAVE_SPAWNLP
+
+/* Define to 1 if you have the <sqlext.h> header file. */
+#undef HAVE_SQLEXT_H
+
+/* Define to 1 if you have the <sql.h> header file. */
+#undef HAVE_SQL_H
+
+/* Define to 1 if you have the <stddef.h> header file. */
+#undef HAVE_STDDEF_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `strdup' function. */
+#undef HAVE_STRDUP
+
+/* Define to 1 if you have the `strerror' function. */
+#undef HAVE_STRERROR
+
+/* Define to 1 if you have the `strerror_r' function. */
+#undef HAVE_STRERROR_R
+
+/* Define to 1 if you have the `strftime' function. */
+#undef HAVE_STRFTIME
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strpbrk' function. */
+#undef HAVE_STRPBRK
+
+/* Define to 1 if you have the `strrchr' function. */
+#undef HAVE_STRRCHR
+
+/* Define to 1 if you have the `strsep' function. */
+#undef HAVE_STRSEP
+
+/* Define to 1 if you have the `strspn' function. */
+#undef HAVE_STRSPN
+
+/* Define to 1 if you have the `strstr' function. */
+#undef HAVE_STRSTR
+
+/* Define to 1 if you have the `strtol' function. */
+#undef HAVE_STRTOL
+
+/* Define to 1 if you have the `strtoll' function. */
+#undef HAVE_STRTOLL
+
+/* Define to 1 if you have the `strtoq' function. */
+#undef HAVE_STRTOQ
+
+/* Define to 1 if you have the `strtoul' function. */
+#undef HAVE_STRTOUL
+
+/* Define to 1 if you have the `strtoull' function. */
+#undef HAVE_STRTOULL
+
+/* Define to 1 if you have the `strtouq' function. */
+#undef HAVE_STRTOUQ
+
+/* Define to 1 if `msg_accrightslen' is a member of `struct msghdr'. */
+#undef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTSLEN
+
+/* Define to 1 if `msg_control' is a member of `struct msghdr'. */
+#undef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+
+/* Define to 1 if `pw_gecos' is a member of `struct passwd'. */
+#undef HAVE_STRUCT_PASSWD_PW_GECOS
+
+/* Define to 1 if `pw_passwd' is a member of `struct passwd'. */
+#undef HAVE_STRUCT_PASSWD_PW_PASSWD
+
+/* Define to 1 if `st_blksize' is a member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_BLKSIZE
+
+/* Define to 1 if `st_fstype' is a member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_FSTYPE
+
+/* define to 1 if st_fstype is char * */
+#undef HAVE_STRUCT_STAT_ST_FSTYPE_CHAR
+
+/* define to 1 if st_fstype is int */
+#undef HAVE_STRUCT_STAT_ST_FSTYPE_INT
+
+/* Define to 1 if `st_vfstype' is a member of `struct stat'. */
+#undef HAVE_STRUCT_STAT_ST_VFSTYPE
+
+/* Define to 1 if you have the <synch.h> header file. */
+#undef HAVE_SYNCH_H
+
+/* Define to 1 if you have the `sysconf' function. */
+#undef HAVE_SYSCONF
+
+/* Define to 1 if you have the <sysexits.h> header file. */
+#undef HAVE_SYSEXITS_H
+
+/* Define to 1 if you have the <syslog.h> header file. */
+#undef HAVE_SYSLOG_H
+
+/* define if you have systemd */
+#undef HAVE_SYSTEMD
+
+/* Define to 1 if you have the <systemd/sd-daemon.h> header file. */
+#undef HAVE_SYSTEMD_SD_DAEMON_H
+
+/* Define to 1 if you have the <sys/devpoll.h> header file. */
+#undef HAVE_SYS_DEVPOLL_H
+
+/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
+ */
+#undef HAVE_SYS_DIR_H
+
+/* Define to 1 if you have the <sys/epoll.h> header file. */
+#undef HAVE_SYS_EPOLL_H
+
+/* define if you actually have sys_errlist in your libs */
+#undef HAVE_SYS_ERRLIST
+
+/* Define to 1 if you have the <sys/errno.h> header file. */
+#undef HAVE_SYS_ERRNO_H
+
+/* Define to 1 if you have the <sys/event.h> header file. */
+#undef HAVE_SYS_EVENT_H
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#undef HAVE_SYS_FILE_H
+
+/* Define to 1 if you have the <sys/filio.h> header file. */
+#undef HAVE_SYS_FILIO_H
+
+/* Define to 1 if you have the <sys/fstyp.h> header file. */
+#undef HAVE_SYS_FSTYP_H
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#undef HAVE_SYS_IOCTL_H
+
+/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
+ */
+#undef HAVE_SYS_NDIR_H
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+#undef HAVE_SYS_POLL_H
+
+/* Define to 1 if you have the <sys/privgrp.h> header file. */
+#undef HAVE_SYS_PRIVGRP_H
+
+/* Define to 1 if you have the <sys/resource.h> header file. */
+#undef HAVE_SYS_RESOURCE_H
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#undef HAVE_SYS_SELECT_H
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#undef HAVE_SYS_SOCKET_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/syslog.h> header file. */
+#undef HAVE_SYS_SYSLOG_H
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#undef HAVE_SYS_TIME_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <sys/ucred.h> header file. */
+#undef HAVE_SYS_UCRED_H
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+#undef HAVE_SYS_UIO_H
+
+/* Define to 1 if you have the <sys/un.h> header file. */
+#undef HAVE_SYS_UN_H
+
+/* Define to 1 if you have the <sys/uuid.h> header file. */
+#undef HAVE_SYS_UUID_H
+
+/* Define to 1 if you have the <sys/vmount.h> header file. */
+#undef HAVE_SYS_VMOUNT_H
+
+/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
+#undef HAVE_SYS_WAIT_H
+
+/* define if you have -lwrap */
+#undef HAVE_TCPD
+
+/* Define to 1 if you have the <tcpd.h> header file. */
+#undef HAVE_TCPD_H
+
+/* Define to 1 if you have the <termios.h> header file. */
+#undef HAVE_TERMIOS_H
+
+/* if you have Solaris LWP (thr) package */
+#undef HAVE_THR
+
+/* Define to 1 if you have the <thread.h> header file. */
+#undef HAVE_THREAD_H
+
+/* Define to 1 if you have the `thr_getconcurrency' function. */
+#undef HAVE_THR_GETCONCURRENCY
+
+/* Define to 1 if you have the `thr_setconcurrency' function. */
+#undef HAVE_THR_SETCONCURRENCY
+
+/* Define to 1 if you have the `thr_yield' function. */
+#undef HAVE_THR_YIELD
+
+/* define if you have TLS */
+#undef HAVE_TLS
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the <utime.h> header file. */
+#undef HAVE_UTIME_H
+
+/* define if you have uuid_generate() */
+#undef HAVE_UUID_GENERATE
+
+/* define if you have uuid_to_str() */
+#undef HAVE_UUID_TO_STR
+
+/* Define to 1 if you have the <uuid/uuid.h> header file. */
+#undef HAVE_UUID_UUID_H
+
+/* Define to 1 if you have the `vprintf' function. */
+#undef HAVE_VPRINTF
+
+/* Define to 1 if you have the `vsnprintf' function. */
+#undef HAVE_VSNPRINTF
+
+/* Define to 1 if you have the `wait4' function. */
+#undef HAVE_WAIT4
+
+/* Define to 1 if you have the `waitpid' function. */
+#undef HAVE_WAITPID
+
+/* define if you have winsock */
+#undef HAVE_WINSOCK
+
+/* define if you have winsock2 */
+#undef HAVE_WINSOCK2
+
+/* Define to 1 if you have the <winsock2.h> header file. */
+#undef HAVE_WINSOCK2_H
+
+/* Define to 1 if you have the <winsock.h> header file. */
+#undef HAVE_WINSOCK_H
+
+/* Define to 1 if you have the `write' function. */
+#undef HAVE_WRITE
+
+/* define if select implicitly yields */
+#undef HAVE_YIELDING_SELECT
+
+/* Define to 1 if you have the `_vsnprintf' function. */
+#undef HAVE__VSNPRINTF
+
+/* define to 32-bit or greater integer type */
+#undef LBER_INT_T
+
+/* define to large integer type */
+#undef LBER_LEN_T
+
+/* define to socket descriptor type */
+#undef LBER_SOCKET_T
+
+/* define to large integer type */
+#undef LBER_TAG_T
+
+/* define to 1 if library is reentrant */
+#undef LDAP_API_FEATURE_X_OPENLDAP_REENTRANT
+
+/* define to 1 if library is thread safe */
+#undef LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE
+
+/* define to LDAP VENDOR VERSION */
+#undef LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS
+
+/* define this to add debugging code */
+#undef LDAP_DEBUG
+
+/* define if LDAP libs are dynamic */
+#undef LDAP_LIBS_DYNAMIC
+
+/* define to support PF_INET6 */
+#undef LDAP_PF_INET6
+
+/* define to support PF_LOCAL */
+#undef LDAP_PF_LOCAL
+
+/* define this to add SLAPI code */
+#undef LDAP_SLAPI
+
+/* define this to add syslog code */
+#undef LDAP_SYSLOG
+
+/* Version */
+#undef LDAP_VENDOR_VERSION
+
+/* Major */
+#undef LDAP_VENDOR_VERSION_MAJOR
+
+/* Minor */
+#undef LDAP_VENDOR_VERSION_MINOR
+
+/* Patch */
+#undef LDAP_VENDOR_VERSION_PATCH
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#undef LT_OBJDIR
+
+/* define if memcmp is not 8-bit clean or is otherwise broken */
+#undef NEED_MEMCMP_REPLACEMENT
+
+/* define if you have (or want) no threads */
+#undef NO_THREADS
+
+/* define to use the original debug style */
+#undef OLD_DEBUG
+
+/* Package */
+#undef OPENLDAP_PACKAGE
+
+/* Version */
+#undef OPENLDAP_VERSION
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* define if sched_yield yields the entire process */
+#undef REPLACE_BROKEN_YIELD
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#undef RETSIGTYPE
+
+/* Define to the type of arg 1 for `select'. */
+#undef SELECT_TYPE_ARG1
+
+/* Define to the type of args 2, 3 and 4 for `select'. */
+#undef SELECT_TYPE_ARG234
+
+/* Define to the type of arg 5 for `select'. */
+#undef SELECT_TYPE_ARG5
+
+/* The size of `int', as computed by sizeof. */
+#undef SIZEOF_INT
+
+/* The size of `long', as computed by sizeof. */
+#undef SIZEOF_LONG
+
+/* The size of `long long', as computed by sizeof. */
+#undef SIZEOF_LONG_LONG
+
+/* The size of `short', as computed by sizeof. */
+#undef SIZEOF_SHORT
+
+/* The size of `wchar_t', as computed by sizeof. */
+#undef SIZEOF_WCHAR_T
+
+/* define to support per-object ACIs */
+#undef SLAPD_ACI_ENABLED
+
+/* define to support LDAP Async Metadirectory backend */
+#undef SLAPD_ASYNCMETA
+
+/* define to support cleartext passwords */
+#undef SLAPD_CLEARTEXT
+
+/* define to support crypt(3) passwords */
+#undef SLAPD_CRYPT
+
+/* define to support DNS SRV backend */
+#undef SLAPD_DNSSRV
+
+/* define to support LDAP backend */
+#undef SLAPD_LDAP
+
+/* define to support MDB backend */
+#undef SLAPD_MDB
+
+/* define to support LDAP Metadirectory backend */
+#undef SLAPD_META
+
+/* define to support modules */
+#undef SLAPD_MODULES
+
+/* dynamically linked module */
+#undef SLAPD_MOD_DYNAMIC
+
+/* statically linked module */
+#undef SLAPD_MOD_STATIC
+
+/* define to support NDB backend */
+#undef SLAPD_NDB
+
+/* define to support NULL backend */
+#undef SLAPD_NULL
+
+/* define for In-Directory Access Logging overlay */
+#undef SLAPD_OVER_ACCESSLOG
+
+/* define for Audit Logging overlay */
+#undef SLAPD_OVER_AUDITLOG
+
+/* define for Automatic Certificate Authority overlay */
+#undef SLAPD_OVER_AUTOCA
+
+/* define for Collect overlay */
+#undef SLAPD_OVER_COLLECT
+
+/* define for Attribute Constraint overlay */
+#undef SLAPD_OVER_CONSTRAINT
+
+/* define for Dynamic Directory Services overlay */
+#undef SLAPD_OVER_DDS
+
+/* define for Dynamic Directory Services overlay */
+#undef SLAPD_OVER_DEREF
+
+/* define for Dynamic Group overlay */
+#undef SLAPD_OVER_DYNGROUP
+
+/* define for Dynamic List overlay */
+#undef SLAPD_OVER_DYNLIST
+
+/* define for Home Directory Management overlay */
+#undef SLAPD_OVER_HOMEDIR
+
+/* define for Reverse Group Membership overlay */
+#undef SLAPD_OVER_MEMBEROF
+
+/* define for OTP 2-factor Authentication overlay */
+#undef SLAPD_OVER_OTP
+
+/* define for Password Policy overlay */
+#undef SLAPD_OVER_PPOLICY
+
+/* define for Proxy Cache overlay */
+#undef SLAPD_OVER_PROXYCACHE
+
+/* define for Referential Integrity overlay */
+#undef SLAPD_OVER_REFINT
+
+/* define for Deferred Authentication overlay */
+#undef SLAPD_OVER_REMOTEAUTH
+
+/* define for Return Code overlay */
+#undef SLAPD_OVER_RETCODE
+
+/* define for Rewrite/Remap overlay */
+#undef SLAPD_OVER_RWM
+
+/* define for Sequential Modify overlay */
+#undef SLAPD_OVER_SEQMOD
+
+/* define for ServerSideSort/VLV overlay */
+#undef SLAPD_OVER_SSSVLV
+
+/* define for Syncrepl Provider overlay */
+#undef SLAPD_OVER_SYNCPROV
+
+/* define for Translucent Proxy overlay */
+#undef SLAPD_OVER_TRANSLUCENT
+
+/* define for Attribute Uniqueness overlay */
+#undef SLAPD_OVER_UNIQUE
+
+/* define for Value Sorting overlay */
+#undef SLAPD_OVER_VALSORT
+
+/* define to support PASSWD backend */
+#undef SLAPD_PASSWD
+
+/* define to support PERL backend */
+#undef SLAPD_PERL
+
+/* define for Argon2 Password hashing module */
+#undef SLAPD_PWMOD_PW_ARGON2
+
+/* define to support relay backend */
+#undef SLAPD_RELAY
+
+/* define to support reverse lookups */
+#undef SLAPD_RLOOKUPS
+
+/* define to support SOCK backend */
+#undef SLAPD_SOCK
+
+/* define to support SASL passwords */
+#undef SLAPD_SPASSWD
+
+/* define to support SQL backend */
+#undef SLAPD_SQL
+
+/* define to support WiredTiger backend */
+#undef SLAPD_WT
+
+/* define to support run-time loadable ACL */
+#undef SLAP_DYNACL
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#undef TIME_WITH_SYS_TIME
+
+/* Define to 1 if your <sys/time.h> declares `struct tm'. */
+#undef TM_IN_SYS_TIME
+
+/* set to urandom device */
+#undef URANDOM_DEVICE
+
+/* define to use OpenSSL BIGNUM for MP */
+#undef USE_MP_BIGNUM
+
+/* define to use GMP for MP */
+#undef USE_MP_GMP
+
+/* define to use 'long' for MP */
+#undef USE_MP_LONG
+
+/* define to use 'long long' for MP */
+#undef USE_MP_LONG_LONG
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+# define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+# undef WORDS_BIGENDIAN
+# endif
+#endif
+
+/* Define to the type of arg 3 for `accept'. */
+#undef ber_socklen_t
+
+/* Define to `char *' if <sys/types.h> does not define. */
+#undef caddr_t
+
+/* Define to empty if `const' does not conform to ANSI C. */
+#undef const
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef gid_t
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef mode_t
+
+/* Define to `long' if <sys/types.h> does not define. */
+#undef off_t
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef pid_t
+
+/* Define to `int' if <signal.h> does not define. */
+#undef sig_atomic_t
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+#undef size_t
+
+/* define to snprintf routine */
+#undef snprintf
+
+/* Define like ber_socklen_t if <sys/socket.h> does not define. */
+#undef socklen_t
+
+/* Define to `signed int' if <sys/types.h> does not define. */
+#undef ssize_t
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef uid_t
+
+/* define as empty if volatile is not supported */
+#undef volatile
+
+/* define to snprintf routine */
+#undef vsnprintf
+
+
+/* begin of portable.h.post */
+
+#ifdef _WIN32
+ /* don't suck in all of the win32 api */
+# define WIN32_LEAN_AND_MEAN 1
+#endif
+
+#ifndef LDAP_NEEDS_PROTOTYPES
+/* force LDAP_P to always include prototypes */
+#define LDAP_NEEDS_PROTOTYPES 1
+#endif
+
+#ifndef LDAP_REL_ENG
+#if (LDAP_VENDOR_VERSION == 000000) && !defined(LDAP_DEVEL)
+#define LDAP_DEVEL
+#endif
+#if defined(LDAP_DEVEL) && !defined(LDAP_TEST)
+#define LDAP_TEST
+#endif
+#endif
+
+#ifdef HAVE_STDDEF_H
+# include <stddef.h>
+#endif
+
+#ifdef HAVE_EBCDIC
+/* ASCII/EBCDIC converting replacements for stdio funcs
+ * vsnprintf and snprintf are used too, but they are already
+ * checked by the configure script
+ */
+#define fputs ber_pvt_fputs
+#define fgets ber_pvt_fgets
+#define printf ber_pvt_printf
+#define fprintf ber_pvt_fprintf
+#define vfprintf ber_pvt_vfprintf
+#define vsprintf ber_pvt_vsprintf
+#endif
+
+#include "ac/fdset.h"
+
+#include "ldap_cdefs.h"
+#include "ldap_features.h"
+
+#include "ac/assert.h"
+#include "ac/localize.h"
+
+#endif /* _LDAP_PORTABLE_H */
+/* end of portable.h.post */
+
diff --git a/include/rewrite.h b/include/rewrite.h
new file mode 100644
index 0000000..52d43be
--- /dev/null
+++ b/include/rewrite.h
@@ -0,0 +1,298 @@
+/* $OpenLDAP$
+ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#ifndef REWRITE_H
+#define REWRITE_H
+
+/*
+ * Default rewrite context
+ */
+#define REWRITE_DEFAULT_CONTEXT "default"
+
+/*
+ * Rewrite engine states
+ */
+#define REWRITE_OFF 0x0000
+#define REWRITE_ON 0x0001
+#define REWRITE_DEFAULT REWRITE_OFF
+
+/*
+ * Rewrite internal status returns
+ */
+#define REWRITE_SUCCESS LDAP_SUCCESS
+#define REWRITE_ERR LDAP_OTHER
+
+/*
+ * Rewrite modes (input values for rewrite_info_init); determine the
+ * behavior in case a null or non existent context is required:
+ *
+ * REWRITE_MODE_ERR error
+ * REWRITE_MODE_OK no error but no rewrite
+ * REWRITE_MODE_COPY_INPUT a copy of the input is returned
+ * REWRITE_MODE_USE_DEFAULT the default context is used.
+ */
+#define REWRITE_MODE_ERR 0x0010
+#define REWRITE_MODE_OK 0x0011
+#define REWRITE_MODE_COPY_INPUT 0x0012
+#define REWRITE_MODE_USE_DEFAULT 0x0013
+
+/*
+ * Rewrite status returns
+ *
+ * REWRITE_REGEXEC_OK success (result may be empty in case
+ * of no match)
+ * REWRITE_REGEXEC_ERR error (internal error,
+ * misconfiguration, map not working ...)
+ * REWRITE_REGEXEC_STOP internal use; never returned
+ * REWRITE_REGEXEC_UNWILLING the server should issue an 'unwilling
+ * to perform' error
+ */
+#define REWRITE_REGEXEC_OK (0)
+#define REWRITE_REGEXEC_ERR (-1)
+#define REWRITE_REGEXEC_STOP (-2)
+#define REWRITE_REGEXEC_UNWILLING (-3)
+#define REWRITE_REGEXEC_USER (1) /* and above: LDAP errors */
+
+/*
+ * Rewrite variable flags
+ * REWRITE_VAR_INSERT insert mode (default) when adding
+ * a variable; if not set during value
+ * update, the variable is not inserted
+ * if not present
+ * REWRITE_VAR_UPDATE update mode (default) when updating
+ * a variable; if not set during insert,
+ * the value is not updated if the
+ * variable already exists
+ * REWRITE_VAR_COPY_NAME copy the variable name; if not set,
+ * the name is not copied; be sure the
+ * referenced string is available for
+ * the entire life scope of the variable.
+ * REWRITE_VAR_COPY_VALUE copy the variable value; if not set,
+ * the value is not copied; be sure the
+ * referenced string is available for
+ * the entire life scope of the variable.
+ */
+#define REWRITE_VAR_NONE 0x0000
+#define REWRITE_VAR_INSERT 0x0001
+#define REWRITE_VAR_UPDATE 0x0002
+#define REWRITE_VAR_COPY_NAME 0x0004
+#define REWRITE_VAR_COPY_VALUE 0x0008
+
+/*
+ * Rewrite info
+ */
+struct rewrite_info;
+
+struct berval; /* avoid include */
+
+LDAP_BEGIN_DECL
+
+/*
+ * Inits the info
+ */
+LDAP_REWRITE_F (struct rewrite_info *)
+rewrite_info_init(
+ int mode
+);
+
+/*
+ * Cleans up the info structure
+ */
+LDAP_REWRITE_F (int)
+rewrite_info_delete(
+ struct rewrite_info **info
+);
+
+
+/*
+ * Parses a config line and takes actions to fit content in rewrite structure;
+ * lines handled are of the form:
+ *
+ * rewriteEngine {on|off}
+ * rewriteMaxPasses numPasses
+ * rewriteContext contextName [alias aliasedRewriteContex]
+ * rewriteRule pattern substPattern [ruleFlags]
+ * rewriteMap mapType mapName [mapArgs]
+ * rewriteParam paramName paramValue
+ */
+LDAP_REWRITE_F (int)
+rewrite_parse(
+ struct rewrite_info *info,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv
+);
+
+/*
+ * process a config file that was already opened. Uses rewrite_parse.
+ */
+LDAP_REWRITE_F (int)
+rewrite_read(
+ FILE *fin,
+ struct rewrite_info *info
+);
+
+/*
+ * Rewrites a string according to context.
+ * If the engine is off, OK is returned, but the return string will be NULL.
+ * In case of 'unwilling to perform', UNWILLING is returned, and the
+ * return string will also be null. The same in case of error.
+ * Otherwise, OK is returned, and result will hold a newly allocated string
+ * with the rewriting.
+ *
+ * What to do in case of non-existing rewrite context is still an issue.
+ * Four possibilities:
+ * - error,
+ * - ok with NULL result,
+ * - ok with copy of string as result,
+ * - use the default rewrite context.
+ */
+LDAP_REWRITE_F (int)
+rewrite(
+ struct rewrite_info *info,
+ const char *rewriteContext,
+ const char *string,
+ char **result
+);
+
+/*
+ * Same as above; the cookie relates the rewrite to a session
+ */
+LDAP_REWRITE_F (int)
+rewrite_session(
+ struct rewrite_info *info,
+ const char *rewriteContext,
+ const char *string,
+ const void *cookie,
+ char **result
+);
+
+/*
+ * Inits a session
+ */
+LDAP_REWRITE_F (struct rewrite_session *)
+rewrite_session_init(
+ struct rewrite_info *info,
+ const void *cookie
+);
+
+/*
+ * Defines and inits a variable with session scope
+ */
+LDAP_REWRITE_F (int)
+rewrite_session_var_set_f(
+ struct rewrite_info *info,
+ const void *cookie,
+ const char *name,
+ const char *value,
+ int flags
+);
+
+#define rewrite_session_var_set(info, cookie, name, value) \
+ rewrite_session_var_set_f((info), (cookie), (name), (value), \
+ REWRITE_VAR_INSERT|REWRITE_VAR_UPDATE|REWRITE_VAR_COPY_NAME|REWRITE_VAR_COPY_VALUE)
+
+/*
+ * Deletes a session
+ */
+LDAP_REWRITE_F (int)
+rewrite_session_delete(
+ struct rewrite_info *info,
+ const void *cookie
+);
+
+
+/*
+ * Params
+ */
+
+/*
+ * Defines and inits a variable with global scope
+ */
+LDAP_REWRITE_F (int)
+rewrite_param_set(
+ struct rewrite_info *info,
+ const char *name,
+ const char *value
+);
+
+/*
+ * Gets a var with global scope
+ */
+LDAP_REWRITE_F (int)
+rewrite_param_get(
+ struct rewrite_info *info,
+ const char *name,
+ struct berval *value
+);
+
+/*
+ * Destroys the parameter tree
+ */
+LDAP_REWRITE_F (int)
+rewrite_param_destroy(
+ struct rewrite_info *info
+);
+
+/*
+ * Mapping implementations
+ */
+
+struct rewrite_mapper;
+
+typedef void * (rewrite_mapper_config)(
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv );
+
+typedef int (rewrite_mapper_apply)(
+ void *ctx,
+ const char *arg,
+ struct berval *retval );
+
+typedef int (rewrite_mapper_destroy)(
+ void *ctx );
+
+typedef struct rewrite_mapper {
+ char *rm_name;
+ rewrite_mapper_config *rm_config;
+ rewrite_mapper_apply *rm_apply;
+ rewrite_mapper_destroy *rm_destroy;
+} rewrite_mapper;
+
+/* For dynamic loading and unloading of mappers */
+LDAP_REWRITE_F (int)
+rewrite_mapper_register(
+ const rewrite_mapper *map );
+
+LDAP_REWRITE_F (int)
+rewrite_mapper_unregister(
+ const rewrite_mapper *map );
+
+LDAP_REWRITE_F (const rewrite_mapper *)
+rewrite_mapper_find(
+ const char *name );
+
+LDAP_END_DECL
+
+#endif /* REWRITE_H */
diff --git a/include/slapi-plugin.h b/include/slapi-plugin.h
new file mode 100644
index 0000000..33f007b
--- /dev/null
+++ b/include/slapi-plugin.h
@@ -0,0 +1,905 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1997,2002,2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * This header is used in development of SLAPI plugins for
+ * OpenLDAP slapd(8) and other directory servers supporting
+ * this interface. Your portability mileage may vary.
+ */
+
+#ifndef _SLAPI_PLUGIN_H
+#define _SLAPI_PLUGIN_H
+
+#include <ldap.h>
+
+typedef struct slapi_pblock Slapi_PBlock;
+typedef struct slapi_entry Slapi_Entry;
+typedef struct slapi_attr Slapi_Attr;
+typedef struct slapi_value Slapi_Value;
+typedef struct slapi_valueset Slapi_ValueSet;
+typedef struct slapi_filter Slapi_Filter;
+typedef struct BackendDB Slapi_Backend;
+typedef struct Operation Slapi_Operation;
+typedef struct Connection Slapi_Connection;
+typedef struct slapi_dn Slapi_DN;
+typedef struct slapi_rdn Slapi_RDN;
+typedef struct slapi_mod Slapi_Mod;
+typedef struct slapi_mods Slapi_Mods;
+typedef struct slapi_componentid Slapi_ComponentId;
+
+#define SLAPI_ATTR_UNIQUEID "entryUUID"
+#define SLAPI_ATTR_OBJECTCLASS "objectClass"
+
+/* pblock routines */
+int slapi_pblock_get( Slapi_PBlock *pb, int arg, void *value );
+int slapi_pblock_set( Slapi_PBlock *pb, int arg, void *value );
+Slapi_PBlock *slapi_pblock_new( void );
+void slapi_pblock_destroy( Slapi_PBlock *pb );
+
+/* entry/attr/dn routines */
+Slapi_Entry *slapi_str2entry( char *s, int flags );
+#define SLAPI_STR2ENTRY_REMOVEDUPVALS 1
+#define SLAPI_STR2ENTRY_ADDRDNVALS 2
+#define SLAPI_STR2ENTRY_BIGENTRY 4
+#define SLAPI_STR2ENTRY_TOMBSTONE_CHECK 8
+#define SLAPI_STR2ENTRY_IGNORE_STATE 16
+#define SLAPI_STR2ENTRY_INCLUDE_VERSION_STR 32
+#define SLAPI_STR2ENTRY_EXPAND_OBJECTCLASSES 64
+#define SLAPI_STR2ENTRY_NOT_WELL_FORMED_LDIF 128
+char *slapi_entry2str( Slapi_Entry *e, int *len );
+char *slapi_entry_get_dn( Slapi_Entry *e );
+int slapi_x_entry_get_id( Slapi_Entry *e );
+void slapi_entry_set_dn( Slapi_Entry *e, char *dn );
+Slapi_Entry *slapi_entry_dup( Slapi_Entry *e );
+int slapi_entry_attr_delete( Slapi_Entry *e, char *type );
+Slapi_Entry *slapi_entry_alloc();
+void slapi_entry_free( Slapi_Entry *e );
+int slapi_entry_attr_merge( Slapi_Entry *e, char *type, struct berval **vals );
+int slapi_entry_attr_find( Slapi_Entry *e, char *type, Slapi_Attr **attr );
+char *slapi_entry_attr_get_charptr( const Slapi_Entry *e, const char *type );
+int slapi_entry_attr_get_int( const Slapi_Entry *e, const char *type );
+long slapi_entry_attr_get_long( const Slapi_Entry *e, const char *type );
+unsigned int slapi_entry_attr_get_uint( const Slapi_Entry *e, const char *type );
+unsigned long slapi_entry_attr_get_ulong( const Slapi_Entry *e, const char *type );
+int slapi_attr_get_values( Slapi_Attr *attr, struct berval ***vals );
+char *slapi_dn_normalize( char *dn );
+char *slapi_dn_normalize_case( char *dn );
+int slapi_dn_issuffix( char *dn, char *suffix );
+char *slapi_dn_beparent( Slapi_PBlock *pb, const char *dn );
+int slapi_dn_isbesuffix( Slapi_PBlock *pb, char *dn );
+char *slapi_dn_parent( const char *dn );
+int slapi_dn_isparent( const char *parentdn, const char *childdn );
+char *slapi_dn_ignore_case( char *dn );
+int slapi_rdn2typeval( char *rdn, char **type, struct berval *bv );
+char *slapi_dn_plus_rdn(const char *dn, const char *rdn);
+
+/* DS 5.x SLAPI */
+int slapi_access_allowed( Slapi_PBlock *pb, Slapi_Entry *e, char *attr, struct berval *val, int access );
+int slapi_acl_check_mods( Slapi_PBlock *pb, Slapi_Entry *e, LDAPMod **mods, char **errbuf );
+Slapi_Attr *slapi_attr_new( void );
+Slapi_Attr *slapi_attr_init( Slapi_Attr *a, const char *type );
+void slapi_attr_free( Slapi_Attr **a );
+Slapi_Attr *slapi_attr_dup( const Slapi_Attr *attr );
+int slapi_attr_add_value( Slapi_Attr *a, const Slapi_Value *v );
+int slapi_attr_type2plugin( const char *type, void **pi );
+int slapi_attr_get_type( const Slapi_Attr *attr, char **type );
+int slapi_attr_get_oid_copy( const Slapi_Attr *attr, char **oidp );
+int slapi_attr_get_flags( const Slapi_Attr *attr, unsigned long *flags );
+int slapi_attr_flag_is_set( const Slapi_Attr *attr, unsigned long flag );
+int slapi_attr_value_cmp( const Slapi_Attr *attr, const struct berval *v1, const struct berval *v2 );
+int slapi_attr_value_find( const Slapi_Attr *a, struct berval *v );
+#define SLAPI_TYPE_CMP_EXACT 0
+#define SLAPI_TYPE_CMP_BASE 1
+#define SLAPI_TYPE_CMP_SUBTYPE 2
+int slapi_attr_type_cmp( const char *t1, const char *t2, int opt );
+int slapi_attr_types_equivalent( const char *t1, const char *t2 );
+int slapi_attr_first_value( Slapi_Attr *a, Slapi_Value **v );
+int slapi_attr_next_value( Slapi_Attr *a, int hint, Slapi_Value **v );
+int slapi_attr_get_numvalues( const Slapi_Attr *a, int *numValues );
+int slapi_attr_get_valueset( const Slapi_Attr *a, Slapi_ValueSet **vs );
+int slapi_attr_get_bervals_copy( Slapi_Attr *a, struct berval ***vals );
+int slapi_entry_attr_hasvalue( Slapi_Entry *e, const char *type, const char *value );
+int slapi_entry_attr_merge_sv( Slapi_Entry *e, const char *type, Slapi_Value **vals );
+void slapi_entry_attr_set_charptr(Slapi_Entry* e, const char *type, const char *value);
+void slapi_entry_attr_set_int( Slapi_Entry* e, const char *type, int l);
+void slapi_entry_attr_set_uint( Slapi_Entry* e, const char *type, unsigned int l);
+void slapi_entry_attr_set_long(Slapi_Entry* e, const char *type, long l);
+void slapi_entry_attr_set_ulong(Slapi_Entry* e, const char *type, unsigned long l);
+int slapi_entry_has_children(const Slapi_Entry *e);
+size_t slapi_entry_size(Slapi_Entry *e);
+int slapi_is_rootdse( const char *dn );
+int slapi_entry_attr_merge_sv( Slapi_Entry *e, const char *type, Slapi_Value **vals );
+int slapi_entry_add_values_sv( Slapi_Entry *e, const char *type, Slapi_Value **vals );
+int slapi_entry_add_valueset(Slapi_Entry *e, const char *type, Slapi_ValueSet *vs);
+int slapi_entry_delete_values_sv( Slapi_Entry *e, const char *type, Slapi_Value **vals );
+int slapi_entry_merge_values_sv( Slapi_Entry *e, const char *type, Slapi_Value **vals );
+int slapi_entry_attr_replace_sv( Slapi_Entry *e, const char *type, Slapi_Value **vals );
+int slapi_entry_add_value(Slapi_Entry *e, const char *type, const Slapi_Value *value);
+int slapi_entry_add_string(Slapi_Entry *e, const char *type, const char *value);
+int slapi_entry_delete_string(Slapi_Entry *e, const char *type, const char *value);
+int slapi_entry_first_attr( const Slapi_Entry *e, Slapi_Attr **attr );
+int slapi_entry_next_attr( const Slapi_Entry *e, Slapi_Attr *prevattr, Slapi_Attr **attr );
+const char *slapi_entry_get_uniqueid( const Slapi_Entry *e );
+void slapi_entry_set_uniqueid( Slapi_Entry *e, char *uniqueid );
+int slapi_entry_schema_check( Slapi_PBlock *pb, Slapi_Entry *e );
+int slapi_entry_rdn_values_present( const Slapi_Entry *e );
+int slapi_entry_add_rdn_values( Slapi_Entry *e );
+char *slapi_attr_syntax_normalize( const char *s );
+
+Slapi_Value *slapi_value_new( void );
+Slapi_Value *slapi_value_new_berval(const struct berval *bval);
+Slapi_Value *slapi_value_new_value(const Slapi_Value *v);
+Slapi_Value *slapi_value_new_string(const char *s);
+Slapi_Value *slapi_value_init(Slapi_Value *v);
+Slapi_Value *slapi_value_init_berval(Slapi_Value *v, struct berval *bval);
+Slapi_Value *slapi_value_init_string(Slapi_Value *v, const char *s);
+Slapi_Value *slapi_value_dup(const Slapi_Value *v);
+void slapi_value_free(Slapi_Value **value);
+const struct berval *slapi_value_get_berval( const Slapi_Value *value );
+Slapi_Value *slapi_value_set_berval( Slapi_Value *value, const struct berval *bval );
+Slapi_Value *slapi_value_set_value( Slapi_Value *value, const Slapi_Value *vfrom);
+Slapi_Value *slapi_value_set( Slapi_Value *value, void *val, unsigned long len);
+int slapi_value_set_string(Slapi_Value *value, const char *strVal);
+int slapi_value_set_int(Slapi_Value *value, int intVal);
+const char*slapi_value_get_string(const Slapi_Value *value);
+int slapi_value_get_int(const Slapi_Value *value);
+unsigned int slapi_value_get_uint(const Slapi_Value *value);
+long slapi_value_get_long(const Slapi_Value *value);
+unsigned long slapi_value_get_ulong(const Slapi_Value *value);
+size_t slapi_value_get_length(const Slapi_Value *value);
+int slapi_value_compare(const Slapi_Attr *a, const Slapi_Value *v1, const Slapi_Value *v2);
+
+Slapi_ValueSet *slapi_valueset_new( void );
+void slapi_valueset_free(Slapi_ValueSet *vs);
+void slapi_valueset_init(Slapi_ValueSet *vs);
+void slapi_valueset_done(Slapi_ValueSet *vs);
+void slapi_valueset_add_value(Slapi_ValueSet *vs, const Slapi_Value *addval);
+int slapi_valueset_first_value( Slapi_ValueSet *vs, Slapi_Value **v );
+int slapi_valueset_next_value( Slapi_ValueSet *vs, int index, Slapi_Value **v);
+int slapi_valueset_count( const Slapi_ValueSet *vs);
+void slapi_valueset_set_valueset(Slapi_ValueSet *vs1, const Slapi_ValueSet *vs2);
+
+/* DNs */
+Slapi_DN *slapi_sdn_new( void );
+Slapi_DN *slapi_sdn_new_dn_byval( const char *dn );
+Slapi_DN *slapi_sdn_new_ndn_byval( const char *ndn );
+Slapi_DN *slapi_sdn_new_dn_byref( const char *dn );
+Slapi_DN *slapi_sdn_new_ndn_byref( const char *ndn );
+Slapi_DN *slapi_sdn_new_dn_passin( const char *dn );
+Slapi_DN *slapi_sdn_set_dn_byval( Slapi_DN *sdn, const char *dn );
+Slapi_DN *slapi_sdn_set_dn_byref( Slapi_DN *sdn, const char *dn );
+Slapi_DN *slapi_sdn_set_dn_passin( Slapi_DN *sdn, const char *dn );
+Slapi_DN *slapi_sdn_set_ndn_byval( Slapi_DN *sdn, const char *ndn );
+Slapi_DN *slapi_sdn_set_ndn_byref( Slapi_DN *sdn, const char *ndn );
+void slapi_sdn_done( Slapi_DN *sdn );
+void slapi_sdn_free( Slapi_DN **sdn );
+const char * slapi_sdn_get_dn( const Slapi_DN *sdn );
+const char * slapi_sdn_get_ndn( const Slapi_DN *sdn );
+void slapi_sdn_get_parent( const Slapi_DN *sdn,Slapi_DN *sdn_parent );
+void slapi_sdn_get_backend_parent( const Slapi_DN *sdn, Slapi_DN *sdn_parent, const Slapi_Backend *backend );
+Slapi_DN * slapi_sdn_dup( const Slapi_DN *sdn );
+void slapi_sdn_copy( const Slapi_DN *from, Slapi_DN *to );
+int slapi_sdn_compare( const Slapi_DN *sdn1, const Slapi_DN *sdn2 );
+int slapi_sdn_isempty( const Slapi_DN *sdn );
+int slapi_sdn_issuffix(const Slapi_DN *sdn, const Slapi_DN *suffixsdn );
+int slapi_sdn_isparent( const Slapi_DN *parent, const Slapi_DN *child );
+int slapi_sdn_isgrandparent( const Slapi_DN *parent, const Slapi_DN *child );
+int slapi_sdn_get_ndn_len( const Slapi_DN *sdn );
+int slapi_sdn_scope_test( const Slapi_DN *dn, const Slapi_DN *base, int scope );
+void slapi_sdn_get_rdn( const Slapi_DN *sdn,Slapi_RDN *rdn );
+Slapi_DN *slapi_sdn_set_rdn( Slapi_DN *sdn, const Slapi_RDN *rdn );
+Slapi_DN *slapi_sdn_set_parent( Slapi_DN *sdn, const Slapi_DN *parentdn );
+int slapi_sdn_is_rdn_component( const Slapi_DN *rdn, const Slapi_Attr *a, const Slapi_Value *v );
+char * slapi_moddn_get_newdn( Slapi_DN *dn_olddn, char *newrdn, char *newsuperiordn );
+
+/* RDNs */
+Slapi_RDN *slapi_rdn_new( void );
+Slapi_RDN *slapi_rdn_new_dn( const char *dn );
+Slapi_RDN *slapi_rdn_new_sdn( const Slapi_DN *sdn );
+Slapi_RDN *slapi_rdn_new_rdn( const Slapi_RDN *fromrdn );
+void slapi_rdn_init( Slapi_RDN *rdn );
+void slapi_rdn_init_dn( Slapi_RDN *rdn, const char *dn );
+void slapi_rdn_init_sdn( Slapi_RDN *rdn, const Slapi_DN *sdn );
+void slapi_rdn_init_rdn( Slapi_RDN *rdn, const Slapi_RDN *fromrdn );
+void slapi_rdn_set_dn( Slapi_RDN *rdn, const char *dn );
+void slapi_rdn_set_sdn( Slapi_RDN *rdn, const Slapi_DN *sdn );
+void slapi_rdn_set_rdn( Slapi_RDN *rdn, const Slapi_RDN *fromrdn );
+void slapi_rdn_free( Slapi_RDN **rdn );
+void slapi_rdn_done( Slapi_RDN *rdn );
+int slapi_rdn_get_first( Slapi_RDN *rdn, char **type, char **value );
+int slapi_rdn_get_next( Slapi_RDN *rdn, int index, char **type, char **value );
+int slapi_rdn_get_index( Slapi_RDN *rdn, const char *type, const char *value, size_t length );
+int slapi_rdn_get_index_attr( Slapi_RDN *rdn, const char *type, char **value );
+int slapi_rdn_contains( Slapi_RDN *rdn, const char *type, const char *value,size_t length );
+int slapi_rdn_contains_attr( Slapi_RDN *rdn, const char *type, char **value );
+int slapi_rdn_add( Slapi_RDN *rdn, const char *type, const char *value );
+int slapi_rdn_remove_index( Slapi_RDN *rdn, int atindex );
+int slapi_rdn_remove( Slapi_RDN *rdn, const char *type, const char *value, size_t length );
+int slapi_rdn_remove_attr( Slapi_RDN *rdn, const char *type );
+int slapi_rdn_isempty( const Slapi_RDN *rdn );
+int slapi_rdn_get_num_components( Slapi_RDN *rdn );
+int slapi_rdn_compare( Slapi_RDN *rdn1, Slapi_RDN *rdn2 );
+const char *slapi_rdn_get_rdn( const Slapi_RDN *rdn );
+const char *slapi_rdn_get_nrdn( const Slapi_RDN *rdn );
+Slapi_DN *slapi_sdn_add_rdn( Slapi_DN *sdn, const Slapi_RDN *rdn );
+
+/* locks and synchronization */
+typedef struct slapi_mutex Slapi_Mutex;
+typedef struct slapi_condvar Slapi_CondVar;
+Slapi_Mutex *slapi_new_mutex( void );
+void slapi_destroy_mutex( Slapi_Mutex *mutex );
+void slapi_lock_mutex( Slapi_Mutex *mutex );
+int slapi_unlock_mutex( Slapi_Mutex *mutex );
+Slapi_CondVar *slapi_new_condvar( Slapi_Mutex *mutex );
+void slapi_destroy_condvar( Slapi_CondVar *cvar );
+int slapi_wait_condvar( Slapi_CondVar *cvar, struct timeval *timeout );
+int slapi_notify_condvar( Slapi_CondVar *cvar, int notify_all );
+
+/* thread-safe LDAP connections */
+LDAP *slapi_ldap_init( char *ldaphost, int ldapport, int secure, int shared );
+void slapi_ldap_unbind( LDAP *ld );
+
+char *slapi_ch_malloc( unsigned long size );
+void slapi_ch_free( void **ptr );
+void slapi_ch_free_string( char **ptr );
+char *slapi_ch_calloc( unsigned long nelem, unsigned long size );
+char *slapi_ch_realloc( char *block, unsigned long size );
+char *slapi_ch_strdup( const char *s );
+void slapi_ch_array_free( char **arrayp );
+struct berval *slapi_ch_bvdup(const struct berval *v);
+struct berval **slapi_ch_bvecdup(const struct berval **v);
+
+/* LDAP V3 routines */
+int slapi_control_present( LDAPControl **controls, char *oid,
+ struct berval **val, int *iscritical);
+void slapi_register_supported_control(char *controloid,
+ unsigned long controlops);
+#define SLAPI_OPERATION_BIND 0x00000001L
+#define SLAPI_OPERATION_UNBIND 0x00000002L
+#define SLAPI_OPERATION_SEARCH 0x00000004L
+#define SLAPI_OPERATION_MODIFY 0x00000008L
+#define SLAPI_OPERATION_ADD 0x00000010L
+#define SLAPI_OPERATION_DELETE 0x00000020L
+#define SLAPI_OPERATION_MODDN 0x00000040L
+#define SLAPI_OPERATION_MODRDN SLAPI_OPERATION_MODDN
+#define SLAPI_OPERATION_COMPARE 0x00000080L
+#define SLAPI_OPERATION_ABANDON 0x00000100L
+#define SLAPI_OPERATION_EXTENDED 0x00000200L
+#define SLAPI_OPERATION_ANY 0xFFFFFFFFL
+#define SLAPI_OPERATION_NONE 0x00000000L
+int slapi_get_supported_controls(char ***ctrloidsp, unsigned long **ctrlopsp);
+LDAPControl *slapi_dup_control(LDAPControl *ctrl);
+void slapi_register_supported_saslmechanism(char *mechanism);
+char **slapi_get_supported_saslmechanisms();
+char **slapi_get_supported_extended_ops(void);
+
+/* operation */
+int slapi_op_abandoned( Slapi_PBlock *pb );
+unsigned long slapi_op_get_type(Slapi_Operation * op);
+void slapi_operation_set_flag(Slapi_Operation *op, unsigned long flag);
+void slapi_operation_clear_flag(Slapi_Operation *op, unsigned long flag);
+int slapi_operation_is_flag_set(Slapi_Operation *op, unsigned long flag);
+char *slapi_op_type_to_string(unsigned long type);
+
+/* send ldap result back */
+void slapi_send_ldap_result( Slapi_PBlock *pb, int err, char *matched,
+ char *text, int nentries, struct berval **urls );
+int slapi_send_ldap_search_entry( Slapi_PBlock *pb, Slapi_Entry *e,
+ LDAPControl **ectrls, char **attrs, int attrsonly );
+int slapi_send_ldap_search_reference( Slapi_PBlock *pb, Slapi_Entry *e,
+ struct berval **urls, LDAPControl **ectrls, struct berval **v2refs );
+
+/* filter routines */
+Slapi_Filter *slapi_str2filter( char *str );
+Slapi_Filter *slapi_filter_dup( Slapi_Filter *f );
+void slapi_filter_free( Slapi_Filter *f, int recurse );
+int slapi_filter_get_choice( Slapi_Filter *f);
+int slapi_filter_get_ava( Slapi_Filter *f, char **type, struct berval **bval );
+Slapi_Filter *slapi_filter_list_first( Slapi_Filter *f );
+Slapi_Filter *slapi_filter_list_next( Slapi_Filter *f, Slapi_Filter *fprev );
+int slapi_filter_get_attribute_type( Slapi_Filter *f, char **type );
+int slapi_x_filter_set_attribute_type( Slapi_Filter *f, const char *type );
+int slapi_filter_get_subfilt( Slapi_Filter *f, char **type, char **initial,
+ char ***any, char **final );
+Slapi_Filter *slapi_filter_join( int ftype, Slapi_Filter *f1, Slapi_Filter *f2);
+int slapi_x_filter_append( int choice, Slapi_Filter **pContainingFilter,
+ Slapi_Filter **pNextFilter, Slapi_Filter *filterToAppend );
+int slapi_filter_test( Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Filter *f,
+ int verify_access );
+int slapi_filter_test_simple( Slapi_Entry *e, Slapi_Filter *f );
+typedef int (*FILTER_APPLY_FN)( Slapi_Filter *f, void *arg );
+int slapi_filter_apply( Slapi_Filter *f, FILTER_APPLY_FN fn, void *arg, int *error_code );
+#define SLAPI_FILTER_SCAN_STOP -1 /* set by callback */
+#define SLAPI_FILTER_SCAN_ERROR -2 /* set by callback */
+#define SLAPI_FILTER_SCAN_NOMORE 0 /* set by callback */
+#define SLAPI_FILTER_SCAN_CONTINUE 1 /* set by callback */
+#define SLAPI_FILTER_UNKNOWN_FILTER_TYPE 2 /* set by slapi_filter_apply() */
+
+/* internal add/delete/search/modify routines */
+Slapi_PBlock *slapi_search_internal( char *base, int scope, char *filter,
+ LDAPControl **controls, char **attrs, int attrsonly );
+Slapi_PBlock *slapi_modify_internal( char *dn, LDAPMod **mods,
+ LDAPControl **controls, int log_change );
+Slapi_PBlock *slapi_add_internal( char * dn, LDAPMod **attrs,
+ LDAPControl **controls, int log_changes );
+Slapi_PBlock *slapi_add_entry_internal( Slapi_Entry * e,
+ LDAPControl **controls, int log_change );
+Slapi_PBlock *slapi_delete_internal( char * dn, LDAPControl **controls,
+ int log_change );
+Slapi_PBlock *slapi_modrdn_internal( char * olddn, char * newrdn,
+ int deloldrdn, LDAPControl **controls,
+ int log_change );
+Slapi_PBlock *slapi_rename_internal( const char * olddn, const char *newrdn,
+ const char *newsuperior, int deloldrdn,
+ LDAPControl **controls, int log_change );
+void slapi_free_search_results_internal(Slapi_PBlock *pb);
+
+/* new internal add/delete/search/modify routines */
+typedef void (*plugin_result_callback)( int rc, void *callback_data );
+typedef int (*plugin_referral_entry_callback)( char * referral,
+ void *callback_data );
+typedef int (*plugin_search_entry_callback)( Slapi_Entry *e,
+ void *callback_data );
+void slapi_free_search_results_internal( Slapi_PBlock *pb );
+
+#define SLAPI_OP_FLAG_NEVER_CHAIN 0x0800
+
+int slapi_search_internal_pb( Slapi_PBlock *pb );
+int slapi_search_internal_callback_pb( Slapi_PBlock *pb, void *callback_data,
+ plugin_result_callback prc, plugin_search_entry_callback psec,
+ plugin_referral_entry_callback prec );
+int slapi_add_internal_pb( Slapi_PBlock *pb );
+int slapi_modify_internal_pb( Slapi_PBlock *pb );
+int slapi_modrdn_internal_pb( Slapi_PBlock *pb );
+int slapi_delete_internal_pb( Slapi_PBlock *pb );
+
+int slapi_seq_internal_callback_pb(Slapi_PBlock *pb, void *callback_data,
+ plugin_result_callback res_callback,
+ plugin_search_entry_callback srch_callback,
+ plugin_referral_entry_callback ref_callback);
+
+void slapi_search_internal_set_pb( Slapi_PBlock *pb, const char *base,
+ int scope, const char *filter, char **attrs, int attrsonly,
+ LDAPControl **controls, const char *uniqueid,
+ Slapi_ComponentId *plugin_identity, int operation_flags );
+void slapi_add_entry_internal_set_pb( Slapi_PBlock *pb, Slapi_Entry *e,
+ LDAPControl **controls, Slapi_ComponentId *plugin_identity,
+ int operation_flags );
+int slapi_add_internal_set_pb( Slapi_PBlock *pb, const char *dn,
+ LDAPMod **attrs, LDAPControl **controls,
+ Slapi_ComponentId *plugin_identity, int operation_flags );
+void slapi_modify_internal_set_pb( Slapi_PBlock *pb, const char *dn,
+ LDAPMod **mods, LDAPControl **controls, const char *uniqueid,
+ Slapi_ComponentId *plugin_identity, int operation_flags );
+void slapi_rename_internal_set_pb( Slapi_PBlock *pb, const char *olddn,
+ const char *newrdn, const char *newsuperior, int deloldrdn,
+ LDAPControl **controls, const char *uniqueid,
+ Slapi_ComponentId *plugin_identity, int operation_flags );
+void slapi_delete_internal_set_pb( Slapi_PBlock *pb, const char *dn,
+ LDAPControl **controls, const char *uniqueid,
+ Slapi_ComponentId *plugin_identity, int operation_flags );
+void slapi_seq_internal_set_pb( Slapi_PBlock *pb, char *ibase, int type,
+ char *attrname, char *val, char **attrs, int attrsonly,
+ LDAPControl **controls, Slapi_ComponentId *plugin_identity,
+ int operation_flags );
+
+/* connection related routines */
+int slapi_is_connection_ssl(Slapi_PBlock *pPB, int *isSSL);
+int slapi_get_client_port(Slapi_PBlock *pPB, int *fromPort);
+int slapi_get_client_ip(Slapi_PBlock *pb, char **clientIP);
+void slapi_free_client_ip(char **clientIP);
+
+/* computed attributes */
+typedef struct _computed_attr_context computed_attr_context;
+typedef int (*slapi_compute_output_t)(computed_attr_context *c, Slapi_Attr *a, Slapi_Entry *e);
+typedef int (*slapi_compute_callback_t)(computed_attr_context *c, char *type, Slapi_Entry *e, slapi_compute_output_t outputfn);
+typedef int (*slapi_search_rewrite_callback_t)(Slapi_PBlock *pb);
+int slapi_compute_add_evaluator(slapi_compute_callback_t function);
+int slapi_compute_add_search_rewriter(slapi_search_rewrite_callback_t function);
+int compute_rewrite_search_filter(Slapi_PBlock *pb);
+int compute_evaluator(computed_attr_context *c, char *type, Slapi_Entry *e, slapi_compute_output_t outputfn);
+int slapi_x_compute_get_pblock(computed_attr_context *c, Slapi_PBlock **pb);
+
+/* backend routines */
+void slapi_be_set_readonly( Slapi_Backend *be, int readonly );
+int slapi_be_get_readonly( Slapi_Backend *be );
+const char *slapi_x_be_get_updatedn( Slapi_Backend *be );
+Slapi_Backend *slapi_be_select( const Slapi_DN *sdn );
+
+/* ACL plugins; only SLAPI_PLUGIN_ACL_ALLOW_ACCESS supported now */
+typedef int (*slapi_acl_callback_t)(Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ const char *attr,
+ struct berval *berval,
+ int access,
+ void *state);
+
+/* object extensions */
+typedef void *(*slapi_extension_constructor_fnptr)(void *object, void *parent);
+
+typedef void (*slapi_extension_destructor_fnptr)(void *extension,
+ void *object, void *parent);
+
+int slapi_register_object_extension( const char *pluginname,
+ const char *objectname, slapi_extension_constructor_fnptr constructor,
+ slapi_extension_destructor_fnptr destructor, int *objecttype,
+ int *extensionhandle);
+
+#define SLAPI_EXT_CONNECTION "Connection"
+#define SLAPI_EXT_OPERATION "Operation"
+#define SLAPI_EXT_ENTRY "Entry"
+#define SLAPI_EXT_MTNODE "Mapping Tree Node"
+
+void *slapi_get_object_extension(int objecttype, void *object,
+ int extensionhandle);
+void slapi_set_object_extension(int objecttype, void *object,
+ int extensionhandle, void *extension);
+
+int slapi_x_backend_get_flags( const Slapi_Backend *be, unsigned long *flags );
+
+/* parameters currently supported */
+
+/*
+ * Attribute flags returned by slapi_attr_get_flags()
+ */
+#define SLAPI_ATTR_FLAG_SINGLE 0x0001
+#define SLAPI_ATTR_FLAG_OPATTR 0x0002
+#define SLAPI_ATTR_FLAG_READONLY 0x0004
+#define SLAPI_ATTR_FLAG_STD_ATTR SLAPI_ATTR_FLAG_READONLY
+#define SLAPI_ATTR_FLAG_OBSOLETE 0x0040
+#define SLAPI_ATTR_FLAG_COLLECTIVE 0x0080
+#define SLAPI_ATTR_FLAG_NOUSERMOD 0x0100
+
+/*
+ * Backend flags returned by slapi_x_backend_get_flags()
+ */
+#define SLAPI_BACKEND_FLAG_NOLASTMOD 0x0001U
+#define SLAPI_BACKEND_FLAG_NO_SCHEMA_CHECK 0x0002U
+#define SLAPI_BACKEND_FLAG_GLUE_INSTANCE 0x0010U /* a glue backend */
+#define SLAPI_BACKEND_FLAG_GLUE_SUBORDINATE 0x0020U /* child of a glue hierarchy */
+#define SLAPI_BACKEND_FLAG_GLUE_LINKED 0x0040U /* child is connected to parent */
+#define SLAPI_BACKEND_FLAG_OVERLAY 0x0080U /* this db struct is an overlay */
+#define SLAPI_BACKEND_FLAG_GLOBAL_OVERLAY 0x0100U /* this db struct is a global overlay */
+#define SLAPI_BACKEND_FLAG_SHADOW 0x8000U /* a shadow */
+#define SLAPI_BACKEND_FLAG_SYNC_SHADOW 0x1000U /* a sync shadow */
+#define SLAPI_BACKEND_FLAG_SLURP_SHADOW 0x2000U /* a slurp shadow */
+
+/*
+ * ACL levels
+ */
+#define SLAPI_ACL_COMPARE 0x01
+#define SLAPI_ACL_SEARCH 0x02
+#define SLAPI_ACL_READ 0x04
+#define SLAPI_ACL_WRITE 0x08
+#define SLAPI_ACL_DELETE 0x10
+#define SLAPI_ACL_ADD 0x20
+#define SLAPI_ACL_SELF 0x40
+#define SLAPI_ACL_PROXY 0x80
+#define SLAPI_ACL_ALL 0x7f
+
+/* plugin types supported */
+
+#define SLAPI_PLUGIN_DATABASE 1
+#define SLAPI_PLUGIN_EXTENDEDOP 2
+#define SLAPI_PLUGIN_PREOPERATION 3
+#define SLAPI_PLUGIN_POSTOPERATION 4
+#define SLAPI_PLUGIN_MATCHINGRULE 5
+#define SLAPI_PLUGIN_SYNTAX 6
+#define SLAPI_PLUGIN_AUDIT 7
+
+/* misc params */
+
+#define SLAPI_BACKEND 130
+#define SLAPI_CONNECTION 131
+#define SLAPI_OPERATION 132
+#define SLAPI_REQUESTOR_ISROOT 133
+#define SLAPI_BE_MONITORDN 134
+#define SLAPI_BE_TYPE 135
+#define SLAPI_BE_READONLY 136
+#define SLAPI_BE_LASTMOD 137
+#define SLAPI_CONN_ID 139
+
+/* operation params */
+#define SLAPI_OPINITIATED_TIME 140
+#define SLAPI_REQUESTOR_DN 141
+#define SLAPI_IS_REPLICATED_OPERATION 142
+#define SLAPI_REQUESTOR_ISUPDATEDN SLAPI_IS_REPLICATED_OPERATION
+
+/* connection structure params*/
+#define SLAPI_CONN_DN 143
+#define SLAPI_CONN_AUTHTYPE 144
+#define SLAPI_CONN_CLIENTIP 145
+#define SLAPI_CONN_SERVERIP 146
+/* OpenLDAP extensions */
+#define SLAPI_X_CONN_CLIENTPATH 1300
+#define SLAPI_X_CONN_SERVERPATH 1301
+#define SLAPI_X_CONN_IS_UDP 1302
+#define SLAPI_X_CONN_SSF 1303
+#define SLAPI_X_CONN_SASL_CONTEXT 1304
+#define SLAPI_X_OPERATION_DELETE_GLUE_PARENT 1305
+#define SLAPI_X_RELAX 1306
+#define SLAPI_X_MANAGEDIT SLAPI_X_RELAX
+#define SLAPI_X_OPERATION_NO_SCHEMA_CHECK 1307
+#define SLAPI_X_ADD_STRUCTURAL_CLASS 1308
+#define SLAPI_X_OPERATION_NO_SUBORDINATE_GLUE 1309
+
+/* Authentication types */
+#define SLAPD_AUTH_NONE "none"
+#define SLAPD_AUTH_SIMPLE "simple"
+#define SLAPD_AUTH_SSL "SSL"
+#define SLAPD_AUTH_SASL "SASL "
+
+/* plugin configuration parmams */
+#define SLAPI_PLUGIN 3
+#define SLAPI_PLUGIN_PRIVATE 4
+#define SLAPI_PLUGIN_TYPE 5
+#define SLAPI_PLUGIN_ARGV 6
+#define SLAPI_PLUGIN_ARGC 7
+#define SLAPI_PLUGIN_VERSION 8
+#define SLAPI_PLUGIN_OPRETURN 9
+#define SLAPI_PLUGIN_OBJECT 10
+#define SLAPI_PLUGIN_DESTROY_FN 11
+#define SLAPI_PLUGIN_DESCRIPTION 12
+#define SLAPI_PLUGIN_IDENTITY 13
+
+/* internal operations params */
+#define SLAPI_PLUGIN_INTOP_RESULT 15
+#define SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES 16
+#define SLAPI_PLUGIN_INTOP_SEARCH_REFERRALS 17
+
+/* transaction arguments */
+#define SLAPI_PARENT_TXN 190
+#define SLAPI_TXN 191
+
+/* function pointer params for backends */
+#define SLAPI_PLUGIN_DB_BIND_FN 200
+#define SLAPI_PLUGIN_DB_UNBIND_FN 201
+#define SLAPI_PLUGIN_DB_SEARCH_FN 202
+#define SLAPI_PLUGIN_DB_COMPARE_FN 203
+#define SLAPI_PLUGIN_DB_MODIFY_FN 204
+#define SLAPI_PLUGIN_DB_MODRDN_FN 205
+#define SLAPI_PLUGIN_DB_ADD_FN 206
+#define SLAPI_PLUGIN_DB_DELETE_FN 207
+#define SLAPI_PLUGIN_DB_ABANDON_FN 208
+#define SLAPI_PLUGIN_DB_CONFIG_FN 209
+#define SLAPI_PLUGIN_CLOSE_FN 210
+#define SLAPI_PLUGIN_DB_FLUSH_FN 211
+#define SLAPI_PLUGIN_START_FN 212
+#define SLAPI_PLUGIN_DB_SEQ_FN 213
+#define SLAPI_PLUGIN_DB_ENTRY_FN 214
+#define SLAPI_PLUGIN_DB_REFERRAL_FN 215
+#define SLAPI_PLUGIN_DB_RESULT_FN 216
+#define SLAPI_PLUGIN_DB_LDIF2DB_FN 217
+#define SLAPI_PLUGIN_DB_DB2LDIF_FN 218
+#define SLAPI_PLUGIN_DB_BEGIN_FN 219
+#define SLAPI_PLUGIN_DB_COMMIT_FN 220
+#define SLAPI_PLUGIN_DB_ABORT_FN 221
+#define SLAPI_PLUGIN_DB_ARCHIVE2DB_FN 222
+#define SLAPI_PLUGIN_DB_DB2ARCHIVE_FN 223
+#define SLAPI_PLUGIN_DB_NEXT_SEARCH_ENTRY_FN 224
+#define SLAPI_PLUGIN_DB_FREE_RESULT_SET_FN 225
+#define SLAPI_PLUGIN_DB_SIZE_FN 226
+#define SLAPI_PLUGIN_DB_TEST_FN 227
+
+
+/* functions pointers for LDAP V3 extended ops */
+#define SLAPI_PLUGIN_EXT_OP_FN 300
+#define SLAPI_PLUGIN_EXT_OP_OIDLIST 301
+
+/* preoperation */
+#define SLAPI_PLUGIN_PRE_BIND_FN 401
+#define SLAPI_PLUGIN_PRE_UNBIND_FN 402
+#define SLAPI_PLUGIN_PRE_SEARCH_FN 403
+#define SLAPI_PLUGIN_PRE_COMPARE_FN 404
+#define SLAPI_PLUGIN_PRE_MODIFY_FN 405
+#define SLAPI_PLUGIN_PRE_MODRDN_FN 406
+#define SLAPI_PLUGIN_PRE_ADD_FN 407
+#define SLAPI_PLUGIN_PRE_DELETE_FN 408
+#define SLAPI_PLUGIN_PRE_ABANDON_FN 409
+#define SLAPI_PLUGIN_PRE_ENTRY_FN 410
+#define SLAPI_PLUGIN_PRE_REFERRAL_FN 411
+#define SLAPI_PLUGIN_PRE_RESULT_FN 412
+
+/* internal preoperation */
+#define SLAPI_PLUGIN_INTERNAL_PRE_ADD_FN 420
+#define SLAPI_PLUGIN_INTERNAL_PRE_MODIFY_FN 421
+#define SLAPI_PLUGIN_INTERNAL_PRE_MODRDN_FN 422
+#define SLAPI_PLUGIN_INTERNAL_PRE_DELETE_FN 423
+
+/* backend preoperation */
+#define SLAPI_PLUGIN_BE_PRE_ADD_FN 450
+#define SLAPI_PLUGIN_BE_PRE_MODIFY_FN 451
+#define SLAPI_PLUGIN_BE_PRE_MODRDN_FN 452
+#define SLAPI_PLUGIN_BE_PRE_DELETE_FN 453
+
+/* postoperation */
+#define SLAPI_PLUGIN_POST_BIND_FN 501
+#define SLAPI_PLUGIN_POST_UNBIND_FN 502
+#define SLAPI_PLUGIN_POST_SEARCH_FN 503
+#define SLAPI_PLUGIN_POST_COMPARE_FN 504
+#define SLAPI_PLUGIN_POST_MODIFY_FN 505
+#define SLAPI_PLUGIN_POST_MODRDN_FN 506
+#define SLAPI_PLUGIN_POST_ADD_FN 507
+#define SLAPI_PLUGIN_POST_DELETE_FN 508
+#define SLAPI_PLUGIN_POST_ABANDON_FN 509
+#define SLAPI_PLUGIN_POST_ENTRY_FN 510
+#define SLAPI_PLUGIN_POST_REFERRAL_FN 511
+#define SLAPI_PLUGIN_POST_RESULT_FN 512
+
+/* internal postoperation */
+#define SLAPI_PLUGIN_INTERNAL_POST_ADD_FN 520
+#define SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN 521
+#define SLAPI_PLUGIN_INTERNAL_POST_MODRDN_FN 522
+#define SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN 523
+
+/* backend postoperation */
+#define SLAPI_PLUGIN_BE_POST_ADD_FN 550
+#define SLAPI_PLUGIN_BE_POST_MODIFY_FN 551
+#define SLAPI_PLUGIN_BE_POST_MODRDN_FN 552
+#define SLAPI_PLUGIN_BE_POST_DELETE_FN 553
+
+#define SLAPI_OPERATION_TYPE 590
+#define SLAPI_OPERATION_MSGID 591
+
+#define SLAPI_PLUGIN_MR_FILTER_CREATE_FN 600
+#define SLAPI_PLUGIN_MR_INDEXER_CREATE_FN 601
+#define SLAPI_PLUGIN_MR_FILTER_MATCH_FN 602
+#define SLAPI_PLUGIN_MR_FILTER_INDEX_FN 603
+#define SLAPI_PLUGIN_MR_FILTER_RESET_FN 604
+#define SLAPI_PLUGIN_MR_INDEX_FN 605
+#define SLAPI_PLUGIN_MR_OID 610
+#define SLAPI_PLUGIN_MR_TYPE 611
+#define SLAPI_PLUGIN_MR_VALUE 612
+#define SLAPI_PLUGIN_MR_VALUES 613
+#define SLAPI_PLUGIN_MR_KEYS 614
+#define SLAPI_PLUGIN_MR_FILTER_REUSABLE 615
+#define SLAPI_PLUGIN_MR_QUERY_OPERATOR 616
+#define SLAPI_PLUGIN_MR_USAGE 617
+
+#define SLAPI_MATCHINGRULE_NAME 1
+#define SLAPI_MATCHINGRULE_OID 2
+#define SLAPI_MATCHINGRULE_DESC 3
+#define SLAPI_MATCHINGRULE_SYNTAX 4
+#define SLAPI_MATCHINGRULE_OBSOLETE 5
+
+#define SLAPI_OP_LESS 1
+#define SLAPI_OP_LESS_OR_EQUAL 2
+#define SLAPI_OP_EQUAL 3
+#define SLAPI_OP_GREATER_OR_EQUAL 4
+#define SLAPI_OP_GREATER 5
+#define SLAPI_OP_SUBSTRING 6
+
+#define SLAPI_PLUGIN_MR_USAGE_INDEX 0
+#define SLAPI_PLUGIN_MR_USAGE_SORT 1
+
+#define SLAPI_PLUGIN_SYNTAX_FILTER_AVA 700
+#define SLAPI_PLUGIN_SYNTAX_FILTER_SUB 701
+#define SLAPI_PLUGIN_SYNTAX_VALUES2KEYS 702
+#define SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_AVA 703
+#define SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_SUB 704
+#define SLAPI_PLUGIN_SYNTAX_NAMES 705
+#define SLAPI_PLUGIN_SYNTAX_OID 706
+#define SLAPI_PLUGIN_SYNTAX_FLAGS 707
+#define SLAPI_PLUGIN_SYNTAX_COMPARE 708
+
+#define SLAPI_PLUGIN_SYNTAX_FLAG_ORKEYS 1
+#define SLAPI_PLUGIN_SYNTAX_FLAG_ORDERING 2
+
+#define SLAPI_PLUGIN_ACL_INIT 730
+#define SLAPI_PLUGIN_ACL_SYNTAX_CHECK 731
+#define SLAPI_PLUGIN_ACL_ALLOW_ACCESS 732
+#define SLAPI_PLUGIN_ACL_MODS_ALLOWED 733
+#define SLAPI_PLUGIN_ACL_MODS_UPDATE 734
+
+#define SLAPI_OPERATION_AUTHTYPE 741
+#define SLAPI_OPERATION_ID 742
+#define SLAPI_CONN_CERT 743
+#define SLAPI_CONN_AUTHMETHOD 746
+#define SLAPI_IS_INTERNAL_OPERATION 748
+
+#define SLAPI_RESULT_CODE 881
+#define SLAPI_RESULT_TEXT 882
+#define SLAPI_RESULT_MATCHED 883
+
+/* managedsait control */
+#define SLAPI_MANAGEDSAIT 1000
+
+/* audit plugin defines */
+#define SLAPI_PLUGIN_AUDIT_DATA 1100
+#define SLAPI_PLUGIN_AUDIT_FN 1101
+
+/* backend_group extension */
+#define SLAPI_X_PLUGIN_PRE_GROUP_FN 1202
+#define SLAPI_X_PLUGIN_POST_GROUP_FN 1203
+
+#define SLAPI_X_GROUP_ENTRY 1250 /* group entry */
+#define SLAPI_X_GROUP_ATTRIBUTE 1251 /* member attribute */
+#define SLAPI_X_GROUP_OPERATION_DN 1252 /* asserted value */
+#define SLAPI_X_GROUP_TARGET_ENTRY 1253 /* target entry */
+
+/* internal preoperation extensions */
+#define SLAPI_PLUGIN_INTERNAL_PRE_BIND_FN 1260
+#define SLAPI_PLUGIN_INTERNAL_PRE_UNBIND_FN 1261
+#define SLAPI_PLUGIN_INTERNAL_PRE_SEARCH_FN 1262
+#define SLAPI_PLUGIN_INTERNAL_PRE_COMPARE_FN 1263
+#define SLAPI_PLUGIN_INTERNAL_PRE_ABANDON_FN 1264
+
+/* internal postoperation extensions */
+#define SLAPI_PLUGIN_INTERNAL_POST_BIND_FN 1270
+#define SLAPI_PLUGIN_INTERNAL_POST_UNBIND_FN 1271
+#define SLAPI_PLUGIN_INTERNAL_POST_SEARCH_FN 1272
+#define SLAPI_PLUGIN_INTERNAL_POST_COMPARE_FN 1273
+#define SLAPI_PLUGIN_INTERNAL_POST_ABANDON_FN 1274
+
+/* config stuff */
+#define SLAPI_CONFIG_FILENAME 40
+#define SLAPI_CONFIG_LINENO 41
+#define SLAPI_CONFIG_ARGC 42
+#define SLAPI_CONFIG_ARGV 43
+
+/* operational params */
+#define SLAPI_TARGET_ADDRESS 48
+#define SLAPI_TARGET_UNIQUEID 49
+#define SLAPI_TARGET_DN 50
+
+/* server LDAPv3 controls */
+#define SLAPI_REQCONTROLS 51
+#define SLAPI_RESCONTROLS 55
+#define SLAPI_ADD_RESCONTROL 56
+#define SLAPI_CONTROLS_ARG 58
+
+/* add params */
+#define SLAPI_ADD_TARGET SLAPI_TARGET_DN
+#define SLAPI_ADD_ENTRY 60
+#define SLAPI_ADD_EXISTING_DN_ENTRY 61
+#define SLAPI_ADD_PARENT_ENTRY 62
+#define SLAPI_ADD_PARENT_UNIQUEID 63
+#define SLAPI_ADD_EXISTING_UNIQUEID_ENTRY 64
+
+/* bind params */
+#define SLAPI_BIND_TARGET SLAPI_TARGET_DN
+#define SLAPI_BIND_METHOD 70
+#define SLAPI_BIND_CREDENTIALS 71
+#define SLAPI_BIND_SASLMECHANISM 72
+#define SLAPI_BIND_RET_SASLCREDS 73
+
+/* compare params */
+#define SLAPI_COMPARE_TARGET SLAPI_TARGET_DN
+#define SLAPI_COMPARE_TYPE 80
+#define SLAPI_COMPARE_VALUE 81
+
+/* delete params */
+#define SLAPI_DELETE_TARGET SLAPI_TARGET_DN
+#define SLAPI_DELETE_EXISTING_ENTRY SLAPI_ADD_EXISTING_DN_ENTRY
+
+/* modify params */
+#define SLAPI_MODIFY_TARGET SLAPI_TARGET_DN
+#define SLAPI_MODIFY_MODS 90
+#define SLAPI_MODIFY_EXISTING_ENTRY SLAPI_ADD_EXISTING_DN_ENTRY
+
+/* modrdn params */
+#define SLAPI_MODRDN_TARGET SLAPI_TARGET_DN
+#define SLAPI_MODRDN_NEWRDN 100
+#define SLAPI_MODRDN_DELOLDRDN 101
+#define SLAPI_MODRDN_NEWSUPERIOR 102 /* v3 only */
+#define SLAPI_MODRDN_EXISTING_ENTRY SLAPI_ADD_EXISTING_DN_ENTRY
+#define SLAPI_MODRDN_PARENT_ENTRY 104
+#define SLAPI_MODRDN_NEWPARENT_ENTRY 105
+#define SLAPI_MODRDN_TARGET_ENTRY 106
+#define SLAPI_MODRDN_NEWSUPERIOR_ADDRESS 107
+
+/* search params */
+#define SLAPI_SEARCH_TARGET SLAPI_TARGET_DN
+#define SLAPI_SEARCH_SCOPE 110
+#define SLAPI_SEARCH_DEREF 111
+#define SLAPI_SEARCH_SIZELIMIT 112
+#define SLAPI_SEARCH_TIMELIMIT 113
+#define SLAPI_SEARCH_FILTER 114
+#define SLAPI_SEARCH_STRFILTER 115
+#define SLAPI_SEARCH_ATTRS 116
+#define SLAPI_SEARCH_ATTRSONLY 117
+
+/* abandon params */
+#define SLAPI_ABANDON_MSGID 120
+
+/* extended operation params */
+#define SLAPI_EXT_OP_REQ_OID 160
+#define SLAPI_EXT_OP_REQ_VALUE 161
+
+/* extended operation return codes */
+#define SLAPI_EXT_OP_RET_OID 162
+#define SLAPI_EXT_OP_RET_VALUE 163
+
+#define SLAPI_PLUGIN_EXTENDED_SENT_RESULT -1
+
+#define SLAPI_FAIL_DISKFULL -2
+#define SLAPI_FAIL_GENERAL -1
+#define SLAPI_PLUGIN_EXTENDED_NOT_HANDLED -2
+#define SLAPI_BIND_SUCCESS 0
+#define SLAPI_BIND_FAIL 2
+#define SLAPI_BIND_ANONYMOUS 3
+
+/* Search result params */
+#define SLAPI_SEARCH_RESULT_SET 193
+#define SLAPI_SEARCH_RESULT_ENTRY 194
+#define SLAPI_NENTRIES 195
+#define SLAPI_SEARCH_REFERRALS 196
+
+/* filter types */
+#ifndef LDAP_FILTER_AND
+#define LDAP_FILTER_AND 0xa0L
+#endif
+#ifndef LDAP_FILTER_OR
+#define LDAP_FILTER_OR 0xa1L
+#endif
+#ifndef LDAP_FILTER_NOT
+#define LDAP_FILTER_NOT 0xa2L
+#endif
+#ifndef LDAP_FILTER_EQUALITY
+#define LDAP_FILTER_EQUALITY 0xa3L
+#endif
+#ifndef LDAP_FILTER_SUBSTRINGS
+#define LDAP_FILTER_SUBSTRINGS 0xa4L
+#endif
+#ifndef LDAP_FILTER_GE
+#define LDAP_FILTER_GE 0xa5L
+#endif
+#ifndef LDAP_FILTER_LE
+#define LDAP_FILTER_LE 0xa6L
+#endif
+#ifndef LDAP_FILTER_PRESENT
+#define LDAP_FILTER_PRESENT 0x87L
+#endif
+#ifndef LDAP_FILTER_APPROX
+#define LDAP_FILTER_APPROX 0xa8L
+#endif
+#ifndef LDAP_FILTER_EXT_MATCH
+#define LDAP_FILTER_EXT_MATCH 0xa9L
+#endif
+
+int slapi_log_error( int severity, char *subsystem, char *fmt, ... );
+#define SLAPI_LOG_FATAL 0
+#define SLAPI_LOG_TRACE 1
+#define SLAPI_LOG_PACKETS 2
+#define SLAPI_LOG_ARGS 3
+#define SLAPI_LOG_CONNS 4
+#define SLAPI_LOG_BER 5
+#define SLAPI_LOG_FILTER 6
+#define SLAPI_LOG_CONFIG 7
+#define SLAPI_LOG_ACL 8
+#define SLAPI_LOG_SHELL 9
+#define SLAPI_LOG_PARSE 10
+#define SLAPI_LOG_HOUSE 11
+#define SLAPI_LOG_REPL 12
+#define SLAPI_LOG_CACHE 13
+#define SLAPI_LOG_PLUGIN 14
+#define SLAPI_LOG_TIMING 15
+
+#define SLAPI_PLUGIN_DESCRIPTION 12
+typedef struct slapi_plugindesc {
+ char *spd_id;
+ char *spd_vendor;
+ char *spd_version;
+ char *spd_description;
+} Slapi_PluginDesc;
+
+#define SLAPI_PLUGIN_VERSION_01 "01"
+#define SLAPI_PLUGIN_VERSION_02 "02"
+#define SLAPI_PLUGIN_VERSION_03 "03"
+#define SLAPI_PLUGIN_CURRENT_VERSION SLAPI_PLUGIN_VERSION_03
+
+#endif /* _SLAPI_PLUGIN_H */
+
diff --git a/include/sysexits-compat.h b/include/sysexits-compat.h
new file mode 100644
index 0000000..8dedb7f
--- /dev/null
+++ b/include/sysexits-compat.h
@@ -0,0 +1,115 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * @(#)sysexits.h 4.5 (Berkeley) 7/6/88
+ */
+
+/*
+** SYSEXITS.H -- Exit status codes for system programs.
+**
+** This include file attempts to categorize possible error
+** exit statuses for system programs, notably delivermail
+** and the Berkeley network.
+**
+** Error numbers begin at EX__BASE to reduce the possibility of
+** clashing with other exit statuses that random programs may
+** already return. The meaning of the codes is approximately
+** as follows:
+**
+** EX_USAGE -- The command was used incorrectly, e.g., with
+** the wrong number of arguments, a bad flag, a bad
+** syntax in a parameter, or whatever.
+** EX_DATAERR -- The input data was incorrect in some way.
+** This should only be used for user's data & not
+** system files.
+** EX_NOINPUT -- An input file (not a system file) did not
+** exist or was not readable. This could also include
+** errors like "No message" to a mailer (if it cared
+** to catch it).
+** EX_NOUSER -- The user specified did not exist. This might
+** be used for mail addresses or remote logins.
+** EX_NOHOST -- The host specified did not exist. This is used
+** in mail addresses or network requests.
+** EX_UNAVAILABLE -- A service is unavailable. This can occur
+** if a support program or file does not exist. This
+** can also be used as a catchall message when something
+** you wanted to do doesn't work, but you don't know
+** why.
+** EX_SOFTWARE -- An internal software error has been detected.
+** This should be limited to non-operating system related
+** errors as possible.
+** EX_OSERR -- An operating system error has been detected.
+** This is intended to be used for such things as "cannot
+** fork", "cannot create pipe", or the like. It includes
+** things like getuid returning a user that does not
+** exist in the passwd file.
+** EX_OSFILE -- Some system file (e.g., /etc/passwd, /etc/utmp,
+** etc.) does not exist, cannot be opened, or has some
+** sort of error (e.g., syntax error).
+** EX_CANTCREAT -- A (user specified) output file cannot be
+** created.
+** EX_IOERR -- An error occurred while doing I/O on some file.
+** EX_TEMPFAIL -- temporary failure, indicating something that
+** is not really an error. In sendmail, this means
+** that a mailer (e.g.) could not create a connection,
+** and the request should be reattempted later.
+** EX_PROTOCOL -- the remote system returned something that
+** was "not possible" during a protocol exchange.
+** EX_NOPERM -- You did not have sufficient permission to
+** perform the operation. This is not intended for
+** file system problems, which should use NOINPUT or
+** CANTCREAT, but rather for higher level permissions.
+** For example, kre uses this to restrict who students
+** can send mail to.
+**
+** Maintained by Eric Allman (eric@berkeley, ucbvax!eric) --
+** please mail changes to me.
+**
+** @(#)sysexits.h 4.5 7/6/88
+*/
+
+# define EX_OK 0 /* successful termination */
+
+# define EX__BASE 64 /* base value for error messages */
+
+# define EX_USAGE 64 /* command line usage error */
+# define EX_DATAERR 65 /* data format error */
+# define EX_NOINPUT 66 /* cannot open input */
+# define EX_NOUSER 67 /* addressee unknown */
+# define EX_NOHOST 68 /* host name unknown */
+# define EX_UNAVAILABLE 69 /* service unavailable */
+# define EX_SOFTWARE 70 /* internal software error */
+# define EX_OSERR 71 /* system error (e.g., can't fork) */
+# define EX_OSFILE 72 /* critical OS file missing */
+# define EX_CANTCREAT 73 /* can't create (user) output file */
+# define EX_IOERR 74 /* input/output error */
+# define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */
+# define EX_PROTOCOL 76 /* remote error in protocol */
+# define EX_NOPERM 77 /* permission denied */
+# define EX_CONFIG 78 /* configuration error */
diff --git a/libraries/Makefile.in b/libraries/Makefile.in
new file mode 100644
index 0000000..b3a9127
--- /dev/null
+++ b/libraries/Makefile.in
@@ -0,0 +1,30 @@
+# Libraries Makefile for OpenLDAP
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SUBDIRS= \
+ liblutil \
+ liblber \
+ liblunicode \
+ libldap \
+ librewrite
+
+PKGCONFIG_DIR=$(DESTDIR)$(libdir)/pkgconfig
+PKGCONFIG_SRCDIRS=liblber libldap
+
+install-local:
+ @-$(MKDIR) $(PKGCONFIG_DIR)
+ @for i in $(PKGCONFIG_SRCDIRS); do \
+ $(INSTALL_DATA) $$i/*.pc $(PKGCONFIG_DIR); \
+ done
diff --git a/libraries/liblber/Makefile.in b/libraries/liblber/Makefile.in
new file mode 100644
index 0000000..a843449
--- /dev/null
+++ b/libraries/liblber/Makefile.in
@@ -0,0 +1,56 @@
+# LIBLBER
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+LIBRARY = liblber.la
+
+NT_SRCS = nt_err.c
+NT_OBJS = nt_err.lo
+
+UNIX_SRCS = stdio.c
+UNIX_OBJS = stdio.lo
+
+LIB_DEFS = -DLBER_LIBRARY
+
+SRCS= assert.c decode.c encode.c io.c bprint.c debug.c \
+ memory.c options.c sockbuf.c $(@PLAT@_SRCS)
+OBJS= assert.lo decode.lo encode.lo io.lo bprint.lo debug.lo \
+ memory.lo options.lo sockbuf.lo $(@PLAT@_OBJS)
+XSRCS= version.c
+
+PROGRAMS= dtest etest idtest
+
+LDAP_INCDIR= ../../include
+LDAP_LIBDIR= ../../libraries
+
+XLIBS = $(LIBRARY) $(LDAP_LIBLUTIL_A)
+XXLIBS =
+NT_LINK_LIBS = $(AC_LIBS)
+UNIX_LINK_LIBS = $(AC_LIBS)
+ifneq (,$(OL_VERSIONED_SYMBOLS))
+ SYMBOL_VERSION_FLAGS=$(OL_VERSIONED_SYMBOLS)$(LDAP_LIBDIR)/liblber/liblber.vers
+endif
+
+dtest: $(XLIBS) dtest.o
+ $(LTLINK) -o $@ dtest.o $(LIBS)
+etest: $(XLIBS) etest.o
+ $(LTLINK) -o $@ etest.o $(LIBS)
+idtest: $(XLIBS) idtest.o
+ $(LTLINK) -o $@ idtest.o $(LIBS)
+
+install-local: FORCE
+ -$(MKDIR) $(DESTDIR)$(libdir)
+ $(LTINSTALL) $(INSTALLFLAGS) -m 644 $(LIBRARY) $(DESTDIR)$(libdir)
+ $(LTFINISH) $(DESTDIR)$(libdir)
+
diff --git a/libraries/liblber/assert.c b/libraries/liblber/assert.c
new file mode 100644
index 0000000..bcaa9af
--- /dev/null
+++ b/libraries/liblber/assert.c
@@ -0,0 +1,40 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#ifdef LDAP_NEED_ASSERT
+
+#include <stdio.h>
+
+/*
+ * helper for our private assert() macro
+ *
+ * note: if assert() doesn't exist, like abort() or raise() won't either.
+ * could use kill() but that might be problematic. I'll just ignore this
+ * issue for now.
+ */
+
+void
+ber_pvt_assert( const char *file, int line, const char *test )
+{
+ fprintf(stderr,
+ _("Assertion failed: %s, file %s, line %d\n"),
+ test, file, line);
+
+ abort();
+}
+
+#endif
diff --git a/libraries/liblber/bprint.c b/libraries/liblber/bprint.c
new file mode 100644
index 0000000..a82e138
--- /dev/null
+++ b/libraries/liblber/bprint.c
@@ -0,0 +1,296 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/*
+ * Copyright (c) 1991 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by the University of Michigan
+ * (as part of U-MICH LDAP).
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/stdarg.h>
+#include <ac/string.h>
+
+#include "lber-int.h"
+
+#define ber_log_check(errlvl, loglvl) ((errlvl) & (loglvl))
+
+BER_LOG_FN ber_int_log_proc = NULL;
+
+/*
+ * We don't just set ber_pvt_err_file to stderr here, because in NT,
+ * stderr is a symbol imported from a DLL. As such, the compiler
+ * doesn't recognize the symbol as having a constant address. Thus
+ * we set ber_pvt_err_file to stderr later, when it first gets
+ * referenced.
+ */
+FILE *ber_pvt_err_file = NULL;
+
+/*
+ * ber errno
+ */
+BER_ERRNO_FN ber_int_errno_fn = NULL;
+
+int * ber_errno_addr(void)
+{
+ static int ber_int_errno = LBER_ERROR_NONE;
+
+ if( ber_int_errno_fn ) {
+ return (*ber_int_errno_fn)();
+ }
+
+ return &ber_int_errno;
+}
+
+/*
+ * Print stuff
+ */
+void ber_error_print( LDAP_CONST char *data )
+{
+ assert( data != NULL );
+
+ if (!ber_pvt_err_file) ber_pvt_err_file = stderr;
+
+ fputs( data, ber_pvt_err_file );
+
+ /* Print to both streams */
+ if (ber_pvt_err_file != stderr) {
+ fputs( data, stderr );
+ fflush( stderr );
+ }
+
+ fflush( ber_pvt_err_file );
+}
+
+BER_LOG_PRINT_FN ber_pvt_log_print = ber_error_print;
+
+/*
+ * lber log
+ */
+
+int ber_pvt_log_output(
+ const char *subsystem,
+ int level,
+ const char *fmt,
+ ... )
+{
+ char buf[1024];
+ va_list vl;
+ va_start( vl, fmt );
+
+ if ( ber_int_log_proc != NULL ) {
+ ber_int_log_proc( ber_pvt_err_file, subsystem, level, fmt, vl );
+
+ } else {
+ int level;
+ ber_get_option( NULL, LBER_OPT_BER_DEBUG, &level );
+ buf[sizeof(buf) - 1] = '\0';
+ vsnprintf( buf, sizeof(buf)-1, fmt, vl );
+ if ( ber_log_check( LDAP_DEBUG_BER, level ) ) {
+ (*ber_pvt_log_print)( buf );
+ }
+ }
+
+ va_end(vl);
+ return 1;
+}
+
+int ber_pvt_log_printf( int errlvl, int loglvl, const char *fmt, ... )
+{
+ char buf[1024];
+ va_list ap;
+
+ assert( fmt != NULL );
+
+ if ( !ber_log_check( errlvl, loglvl )) {
+ return 0;
+ }
+
+ va_start( ap, fmt );
+
+ buf[sizeof(buf) - 1] = '\0';
+ vsnprintf( buf, sizeof(buf)-1, fmt, ap );
+
+ va_end(ap);
+
+ (*ber_pvt_log_print)( buf );
+ return 1;
+}
+
+#if 0
+static int ber_log_puts(int errlvl, int loglvl, char *buf)
+{
+ assert( buf != NULL );
+
+ if ( !ber_log_check( errlvl, loglvl )) {
+ return 0;
+ }
+
+ (*ber_pvt_log_print)( buf );
+ return 1;
+}
+#endif
+
+/*
+ * Print arbitrary stuff, for debugging.
+ */
+
+int
+ber_log_bprint(int errlvl,
+ int loglvl,
+ const char *data,
+ ber_len_t len )
+{
+ assert( data != NULL );
+
+ if ( !ber_log_check( errlvl, loglvl )) {
+ return 0;
+ }
+
+ ber_bprint(data, len);
+ return 1;
+}
+
+void
+ber_bprint(
+ LDAP_CONST char *data,
+ ber_len_t len )
+{
+ static const char hexdig[] = "0123456789abcdef";
+#define BP_OFFSET 9
+#define BP_GRAPH 60
+#define BP_LEN 80
+ char line[BP_LEN];
+ ber_len_t i;
+
+ assert( data != NULL );
+
+ /* in case len is zero */
+ line[0] = '\n';
+ line[1] = '\0';
+
+ for ( i = 0 ; i < len ; i++ ) {
+ int n = i % 16;
+ unsigned off;
+
+ if( !n ) {
+ if( i ) (*ber_pvt_log_print)( line );
+ memset( line, ' ', sizeof(line)-2 );
+ line[sizeof(line)-2] = '\n';
+ line[sizeof(line)-1] = '\0';
+
+ off = i % 0x0ffffU;
+
+ line[2] = hexdig[0x0f & (off >> 12)];
+ line[3] = hexdig[0x0f & (off >> 8)];
+ line[4] = hexdig[0x0f & (off >> 4)];
+ line[5] = hexdig[0x0f & off];
+ line[6] = ':';
+ }
+
+ off = BP_OFFSET + n*3 + ((n >= 8)?1:0);
+ line[off] = hexdig[0x0f & ( data[i] >> 4 )];
+ line[off+1] = hexdig[0x0f & data[i]];
+
+ off = BP_GRAPH + n + ((n >= 8)?1:0);
+
+ if ( isprint( (unsigned char) data[i] )) {
+ line[BP_GRAPH + n] = data[i];
+ } else {
+ line[BP_GRAPH + n] = '.';
+ }
+ }
+
+ (*ber_pvt_log_print)( line );
+}
+
+
+int
+ber_log_dump(
+ int errlvl,
+ int loglvl,
+ BerElement *ber,
+ int inout )
+{
+ assert( ber != NULL );
+ assert( LBER_VALID( ber ) );
+
+ if ( !ber_log_check( errlvl, loglvl )) {
+ return 0;
+ }
+
+ ber_dump(ber, inout);
+ return 1;
+}
+
+void
+ber_dump(
+ BerElement *ber,
+ int inout )
+{
+ char buf[132];
+ ber_len_t len;
+
+ assert( ber != NULL );
+ assert( LBER_VALID( ber ) );
+
+ if ( inout == 1 ) {
+ len = ber_pvt_ber_remaining(ber);
+ } else {
+ len = ber_pvt_ber_write(ber);
+ }
+
+ sprintf( buf, "ber_dump: buf=%p ptr=%p end=%p len=%ld\n",
+ ber->ber_buf,
+ ber->ber_ptr,
+ ber->ber_end,
+ (long) len );
+
+ (void) (*ber_pvt_log_print)( buf );
+
+ ber_bprint( ber->ber_ptr, len );
+}
+
+typedef struct seqorset Seqorset;
+
+/* Exists for binary compatibility with OpenLDAP 2.4.17-- */
+int
+ber_log_sos_dump(
+ int errlvl,
+ int loglvl,
+ Seqorset *sos )
+{
+ return 0;
+}
+
+/* Exists for binary compatibility with OpenLDAP 2.4.17-- */
+void
+ber_sos_dump(
+ Seqorset *sos )
+{
+}
diff --git a/libraries/liblber/debug.c b/libraries/liblber/debug.c
new file mode 100644
index 0000000..1744e58
--- /dev/null
+++ b/libraries/liblber/debug.c
@@ -0,0 +1,73 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdarg.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/ctype.h>
+
+#ifdef LDAP_SYSLOG
+#include <ac/syslog.h>
+#endif
+
+#include "ldap_log.h"
+#include "ldap_defaults.h"
+#include "lber.h"
+#include "ldap_pvt.h"
+
+int lutil_debug_file( FILE *file )
+{
+ ber_set_option( NULL, LBER_OPT_LOG_PRINT_FILE, file );
+
+ return 0;
+}
+
+void (lutil_debug)( int debug, int level, const char *fmt, ... )
+{
+ char buffer[4096];
+ va_list vl;
+
+ if ( !(level & debug ) ) return;
+
+ va_start( vl, fmt );
+ vsnprintf( buffer, sizeof(buffer), fmt, vl );
+ va_end( vl );
+ ber_pvt_log_print( buffer );
+}
+
+#if defined(HAVE_EBCDIC) && defined(LDAP_SYSLOG)
+#undef syslog
+void eb_syslog( int pri, const char *fmt, ... )
+{
+ char buffer[4096];
+ va_list vl;
+
+ va_start( vl, fmt );
+ vsnprintf( buffer, sizeof(buffer), fmt, vl );
+ buffer[sizeof(buffer)-1] = '\0';
+
+ /* The syslog function appears to only work with pure EBCDIC */
+ __atoe(buffer);
+#pragma convlit(suspend)
+ syslog( pri, "%s", buffer );
+#pragma convlit(resume)
+ va_end( vl );
+}
+#endif
diff --git a/libraries/liblber/decode.c b/libraries/liblber/decode.c
new file mode 100644
index 0000000..48696e0
--- /dev/null
+++ b/libraries/liblber/decode.c
@@ -0,0 +1,1026 @@
+/* decode.c - ber input decoding routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by the University of Michigan
+ * (as part of U-MICH LDAP).
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/stdarg.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "lber-int.h"
+
+
+/* out->bv_len should be the buffer size on input */
+int
+ber_decode_oid( BerValue *in, BerValue *out )
+{
+ const unsigned char *der;
+ unsigned long val;
+ unsigned val1;
+ ber_len_t i;
+ char *ptr;
+
+ assert( in != NULL );
+ assert( out != NULL );
+
+ /* need 4 chars/inbyte + \0 for input={7f 7f 7f...} */
+ if ( !out->bv_val || (out->bv_len+3)/4 <= in->bv_len )
+ return -1;
+
+ ptr = NULL;
+ der = (unsigned char *) in->bv_val;
+ val = 0;
+ for ( i=0; i < in->bv_len; i++ ) {
+ val |= der[i] & 0x7f;
+ if ( !( der[i] & 0x80 )) {
+ if ( ptr == NULL ) {
+ /* Initial "x.y": val=x*40+y, x<=2, y<40 if x<2 */
+ ptr = out->bv_val;
+ val1 = (val < 80 ? val/40 : 2);
+ val -= val1*40;
+ ptr += sprintf( ptr, "%u", val1 );
+ }
+ ptr += sprintf( ptr, ".%lu", val );
+ val = 0;
+ } else if ( val - 1UL < LBER_OID_COMPONENT_MAX >> 7 ) {
+ val <<= 7;
+ } else {
+ /* val would overflow, or is 0 from invalid initial 0x80 octet */
+ return -1;
+ }
+ }
+ if ( ptr == NULL || val != 0 )
+ return -1;
+
+ out->bv_len = ptr - out->bv_val;
+ return 0;
+}
+
+/* Return tag, with *bv = rest of element (starting at length octets) */
+static ber_tag_t
+ber_tag_and_rest( const BerElement *ber, struct berval *bv )
+{
+ ber_tag_t tag;
+ ptrdiff_t rest;
+ unsigned char *ptr;
+
+ assert( ber != NULL );
+ assert( LBER_VALID( ber ) );
+
+ ptr = (unsigned char *) ber->ber_ptr;
+ rest = (unsigned char *) ber->ber_end - ptr;
+ if ( rest <= 0 ) {
+ goto fail;
+ }
+
+ tag = ber->ber_tag;
+ if ( (char *) ptr == ber->ber_buf ) {
+ tag = *ptr;
+ }
+ ptr++;
+ rest--;
+ if ( (tag & LBER_BIG_TAG_MASK) != LBER_BIG_TAG_MASK ) {
+ goto done;
+ }
+
+ do {
+ if ( rest <= 0 ) {
+ break;
+ }
+ tag <<= 8;
+ tag |= *ptr++ & 0xffU;
+ rest--;
+
+ if ( ! (tag & LBER_MORE_TAG_MASK) ) {
+ goto done;
+ }
+ } while ( tag <= (ber_tag_t)-1 / 256 );
+
+ fail:
+ /* Error or unsupported tag size */
+ tag = LBER_DEFAULT;
+
+ done:
+ bv->bv_len = rest;
+ bv->bv_val = (char *) ptr;
+ return tag;
+}
+
+/* Return the tag - LBER_DEFAULT returned means trouble */
+ber_tag_t
+ber_get_tag( BerElement *ber )
+{
+ struct berval bv;
+ ber_tag_t tag = ber_tag_and_rest( ber, &bv );
+
+ ber->ber_ptr = bv.bv_val;
+ return tag;
+}
+
+/* Return next element's tag and point *bv at its contents in-place */
+ber_tag_t
+ber_peek_element( const BerElement *ber, struct berval *bv )
+{
+ ber_tag_t tag;
+ ber_len_t len, rest;
+ unsigned i;
+ unsigned char *ptr;
+
+ assert( bv != NULL );
+
+ /*
+ * Any ber element looks like this: tag length contents.
+ * Assuming everything's ok, we return the tag, and point
+ * bv at the contents.
+ *
+ * Assumptions:
+ * 1) definite lengths
+ * 2) primitive encodings used whenever possible
+ */
+
+ len = 0;
+
+ /*
+ * First, we read the tag.
+ */
+ tag = ber_tag_and_rest( ber, bv );
+
+ rest = bv->bv_len;
+ ptr = (unsigned char *) bv->bv_val;
+ if ( tag == LBER_DEFAULT || rest == 0 ) {
+ goto fail;
+ }
+
+ /*
+ * Next, read the length. The first octet determines the length
+ * of the length. If bit 8 is 0, the length is the short form,
+ * otherwise if the octet != 0x80 it's the long form, otherwise
+ * the ber element has the unsupported indefinite-length format.
+ * Lengths that do not fit in a ber_len_t are not accepted.
+ */
+
+ len = *ptr++;
+ rest--;
+
+ if ( len & 0x80U ) {
+ len &= 0x7fU;
+ if ( len - 1U > sizeof(ber_len_t) - 1U || rest < len ) {
+ /* Indefinite-length/too long length/not enough data */
+ goto fail;
+ }
+
+ rest -= len;
+ i = len;
+ for( len = *ptr++ & 0xffU; --i; len |= *ptr++ & 0xffU ) {
+ len <<= 8;
+ }
+ }
+
+ /* BER element should have enough data left */
+ if( len > rest ) {
+ fail:
+ tag = LBER_DEFAULT;
+ }
+
+ bv->bv_len = len;
+ bv->bv_val = (char *) ptr;
+ return tag;
+}
+
+/* Move past next element, point *bv at it in-place, and return its tag.
+ * The caller may \0-terminate *bv, as next octet is saved in ber->ber_tag.
+ * Similar to ber_get_stringbv(ber, bv, LBER_BV_NOTERM) except on error.
+ */
+ber_tag_t
+ber_skip_element( BerElement *ber, struct berval *bv )
+{
+ ber_tag_t tag = ber_peek_element( ber, bv );
+
+ if ( tag != LBER_DEFAULT ) {
+ ber->ber_ptr = bv->bv_val + bv->bv_len;
+ ber->ber_tag = *(unsigned char *) ber->ber_ptr;
+ }
+
+ return tag;
+}
+
+/* Move past next element, point *bv at the complete element in-place, and
+ * return its tag. The caller may \0-terminate *bv, as next octet is saved in
+ * ber->ber_tag. Similar to ber_skip_element(ber, bv) except the tag+length
+ * header is also included in *bv.
+ */
+ber_tag_t
+ber_skip_raw( BerElement *ber, struct berval *bv )
+{
+ char *val = ber->ber_ptr;
+ ber_tag_t tag = ber_skip_element( ber, bv );
+
+ if ( tag != LBER_DEFAULT ) {
+ bv->bv_len += bv->bv_val - val;
+ bv->bv_val = val;
+ }
+
+ return tag;
+}
+
+ber_tag_t
+ber_peek_tag(
+ BerElement *ber,
+ ber_len_t *len )
+{
+ struct berval bv;
+ ber_tag_t tag = ber_peek_element( ber, &bv );
+
+ *len = bv.bv_len;
+ return tag;
+}
+
+ber_tag_t
+ber_skip_tag( BerElement *ber, ber_len_t *lenp )
+{
+ struct berval bv;
+ ber_tag_t tag = ber_peek_element( ber, &bv );
+
+ ber->ber_ptr = bv.bv_val;
+ ber->ber_tag = *(unsigned char *) ber->ber_ptr;
+
+ *lenp = bv.bv_len;
+ return tag;
+}
+
+ber_tag_t
+ber_get_int(
+ BerElement *ber,
+ ber_int_t *num )
+{
+ struct berval bv;
+ ber_tag_t tag = ber_skip_element( ber, &bv );
+
+ if ( tag == LBER_DEFAULT ) {
+ return tag;
+ }
+
+ return ber_decode_int( &bv, num ) ? LBER_DEFAULT : tag;
+}
+
+int
+ber_decode_int( const struct berval *bv, ber_int_t *num )
+{
+ ber_len_t len = bv->bv_len;
+ if ( len > sizeof(ber_int_t) )
+ return -1;
+
+ assert( num != NULL );
+
+ /* parse two's complement integer */
+ if( len ) {
+ unsigned char *buf = (unsigned char *) bv->bv_val;
+ ber_len_t i;
+ ber_int_t netnum = buf[0] & 0xff;
+
+ /* sign extend */
+ netnum = (netnum ^ 0x80) - 0x80;
+
+ /* shift in the bytes */
+ for( i = 1; i < len; i++ ) {
+ netnum = (netnum << 8 ) | buf[i];
+ }
+
+ *num = netnum;
+
+ } else {
+ *num = 0;
+ }
+
+ return 0;
+}
+
+ber_tag_t
+ber_get_enum(
+ BerElement *ber,
+ ber_int_t *num )
+{
+ return ber_get_int( ber, num );
+}
+
+ber_tag_t
+ber_get_stringb(
+ BerElement *ber,
+ char *buf,
+ ber_len_t *len )
+{
+ struct berval bv;
+ ber_tag_t tag;
+
+ if ( (tag = ber_skip_element( ber, &bv )) == LBER_DEFAULT ) {
+ return LBER_DEFAULT;
+ }
+
+ /* must fit within allocated space with termination */
+ if ( bv.bv_len >= *len ) {
+ return LBER_DEFAULT;
+ }
+
+ memcpy( buf, bv.bv_val, bv.bv_len );
+ buf[bv.bv_len] = '\0';
+
+ *len = bv.bv_len;
+ return tag;
+}
+
+/* Definitions for get_string vector
+ *
+ * ChArray, BvArray, and BvVec are self-explanatory.
+ * BvOff is a struct berval embedded in an array of larger structures
+ * of siz bytes at off bytes from the beginning of the struct.
+ */
+enum bgbvc { ChArray, BvArray, BvVec, BvOff };
+
+/* Use this single cookie for state, to keep actual
+ * stack use to the absolute minimum.
+ */
+typedef struct bgbvr {
+ const enum bgbvc choice;
+ const int option; /* (ALLOC unless BvOff) | (STRING if ChArray) */
+ ber_len_t siz; /* input array element size, output count */
+ ber_len_t off; /* BvOff offset to the struct berval */
+ void *result;
+} bgbvr;
+
+static ber_tag_t
+ber_get_stringbvl( BerElement *ber, bgbvr *b )
+{
+ int i = 0, n;
+ ber_tag_t tag;
+ ber_len_t tot_size = 0, siz = b->siz;
+ char *last, *orig;
+ struct berval bv, *bvp = NULL;
+ union stringbvl_u {
+ char **ca; /* ChArray */
+ BerVarray ba; /* BvArray */
+ struct berval **bv; /* BvVec */
+ char *bo; /* BvOff */
+ } res;
+
+ tag = ber_skip_tag( ber, &bv.bv_len );
+
+ if ( tag != LBER_DEFAULT ) {
+ tag = 0;
+ orig = ber->ber_ptr;
+ last = orig + bv.bv_len;
+
+ for ( ; ber->ber_ptr < last; i++, tot_size += siz ) {
+ if ( ber_skip_element( ber, &bv ) == LBER_DEFAULT )
+ break;
+ }
+ if ( ber->ber_ptr != last ) {
+ i = 0;
+ tag = LBER_DEFAULT;
+ }
+
+ ber->ber_ptr = orig;
+ ber->ber_tag = *(unsigned char *) orig;
+ }
+
+ b->siz = i;
+ if ( i == 0 ) {
+ return tag;
+ }
+
+ /* Allocate and NULL-terminate the result vector */
+ b->result = ber_memalloc_x( tot_size + siz, ber->ber_memctx );
+ if ( b->result == NULL ) {
+ return LBER_DEFAULT;
+ }
+ switch (b->choice) {
+ case ChArray:
+ res.ca = b->result;
+ res.ca[i] = NULL;
+ break;
+ case BvArray:
+ res.ba = b->result;
+ res.ba[i].bv_val = NULL;
+ break;
+ case BvVec:
+ res.bv = b->result;
+ res.bv[i] = NULL;
+ break;
+ case BvOff:
+ res.bo = (char *) b->result + b->off;
+ ((struct berval *) (res.bo + tot_size))->bv_val = NULL;
+ tot_size = 0;
+ break;
+ }
+
+ n = 0;
+ do {
+ tag = ber_get_stringbv( ber, &bv, b->option );
+ if ( tag == LBER_DEFAULT ) {
+ goto failed;
+ }
+
+ /* store my result */
+ switch (b->choice) {
+ case ChArray:
+ res.ca[n] = bv.bv_val;
+ break;
+ case BvArray:
+ res.ba[n] = bv;
+ break;
+ case BvVec:
+ bvp = ber_memalloc_x( sizeof( struct berval ),
+ ber->ber_memctx );
+ if ( !bvp ) {
+ ber_memfree_x( bv.bv_val, ber->ber_memctx );
+ goto failed;
+ }
+ res.bv[n] = bvp;
+ *bvp = bv;
+ break;
+ case BvOff:
+ *(struct berval *)(res.bo + tot_size) = bv;
+ tot_size += siz;
+ break;
+ }
+ } while (++n < i);
+ return tag;
+
+failed:
+ if (b->choice != BvOff) { /* BvOff does not have LBER_BV_ALLOC set */
+ while (--n >= 0) {
+ switch(b->choice) {
+ case ChArray:
+ ber_memfree_x(res.ca[n], ber->ber_memctx);
+ break;
+ case BvArray:
+ ber_memfree_x(res.ba[n].bv_val, ber->ber_memctx);
+ break;
+ case BvVec:
+ ber_memfree_x(res.bv[n]->bv_val, ber->ber_memctx);
+ ber_memfree_x(res.bv[n], ber->ber_memctx);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ ber_memfree_x(b->result, ber->ber_memctx);
+ b->result = NULL;
+ return LBER_DEFAULT;
+}
+
+ber_tag_t
+ber_get_stringbv( BerElement *ber, struct berval *bv, int option )
+{
+ ber_tag_t tag;
+ char *data;
+
+ tag = ber_skip_element( ber, bv );
+ if ( tag == LBER_DEFAULT ||
+ (( option & LBER_BV_STRING ) &&
+ bv->bv_len && memchr( bv->bv_val, 0, bv->bv_len - 1 )))
+ {
+ bv->bv_val = NULL;
+ return LBER_DEFAULT;
+ }
+
+ data = bv->bv_val;
+ if ( option & LBER_BV_ALLOC ) {
+ bv->bv_val = (char *) ber_memalloc_x( bv->bv_len + 1,
+ ber->ber_memctx );
+ if ( bv->bv_val == NULL ) {
+ return LBER_DEFAULT;
+ }
+
+ if ( bv->bv_len != 0 ) {
+ memcpy( bv->bv_val, data, bv->bv_len );
+ }
+ data = bv->bv_val;
+ }
+ if ( !( option & LBER_BV_NOTERM ))
+ data[bv->bv_len] = '\0';
+
+ return tag;
+}
+
+ber_tag_t
+ber_get_stringbv_null( BerElement *ber, struct berval *bv, int option )
+{
+ ber_tag_t tag;
+ char *data;
+
+ tag = ber_skip_element( ber, bv );
+ if ( tag == LBER_DEFAULT || bv->bv_len == 0 ) {
+ bv->bv_val = NULL;
+ return tag;
+ }
+
+ if (( option & LBER_BV_STRING ) &&
+ memchr( bv->bv_val, 0, bv->bv_len - 1 ))
+ {
+ bv->bv_val = NULL;
+ return LBER_DEFAULT;
+ }
+
+ data = bv->bv_val;
+ if ( option & LBER_BV_ALLOC ) {
+ bv->bv_val = (char *) ber_memalloc_x( bv->bv_len + 1,
+ ber->ber_memctx );
+ if ( bv->bv_val == NULL ) {
+ return LBER_DEFAULT;
+ }
+
+ memcpy( bv->bv_val, data, bv->bv_len );
+ data = bv->bv_val;
+ }
+ if ( !( option & LBER_BV_NOTERM ))
+ data[bv->bv_len] = '\0';
+
+ return tag;
+}
+
+ber_tag_t
+ber_get_stringa( BerElement *ber, char **buf )
+{
+ BerValue bv;
+ ber_tag_t tag;
+
+ assert( buf != NULL );
+
+ tag = ber_get_stringbv( ber, &bv, LBER_BV_ALLOC | LBER_BV_STRING );
+ *buf = bv.bv_val;
+
+ return tag;
+}
+
+ber_tag_t
+ber_get_stringa_null( BerElement *ber, char **buf )
+{
+ BerValue bv;
+ ber_tag_t tag;
+
+ assert( buf != NULL );
+
+ tag = ber_get_stringbv_null( ber, &bv, LBER_BV_ALLOC | LBER_BV_STRING );
+ *buf = bv.bv_val;
+
+ return tag;
+}
+
+ber_tag_t
+ber_get_stringal( BerElement *ber, struct berval **bv )
+{
+ ber_tag_t tag;
+
+ assert( ber != NULL );
+ assert( bv != NULL );
+
+ *bv = (struct berval *) ber_memalloc_x( sizeof(struct berval),
+ ber->ber_memctx );
+ if ( *bv == NULL ) {
+ return LBER_DEFAULT;
+ }
+
+ tag = ber_get_stringbv( ber, *bv, LBER_BV_ALLOC );
+ if ( tag == LBER_DEFAULT ) {
+ ber_memfree_x( *bv, ber->ber_memctx );
+ *bv = NULL;
+ }
+ return tag;
+}
+
+ber_tag_t
+ber_get_bitstringa(
+ BerElement *ber,
+ char **buf,
+ ber_len_t *blen )
+{
+ ber_tag_t tag;
+ struct berval data;
+ unsigned char unusedbits;
+
+ assert( buf != NULL );
+ assert( blen != NULL );
+
+ if ( (tag = ber_skip_element( ber, &data )) == LBER_DEFAULT ) {
+ goto fail;
+ }
+
+ if ( --data.bv_len > (ber_len_t)-1 / 8 ) {
+ goto fail;
+ }
+ unusedbits = *(unsigned char *) data.bv_val++;
+ if ( unusedbits > 7 ) {
+ goto fail;
+ }
+
+ if ( memchr( data.bv_val, 0, data.bv_len )) {
+ goto fail;
+ }
+
+ *buf = (char *) ber_memalloc_x( data.bv_len, ber->ber_memctx );
+ if ( *buf == NULL ) {
+ return LBER_DEFAULT;
+ }
+ memcpy( *buf, data.bv_val, data.bv_len );
+
+ *blen = data.bv_len * 8 - unusedbits;
+ return tag;
+
+ fail:
+ *buf = NULL;
+ return LBER_DEFAULT;
+}
+
+ber_tag_t
+ber_get_null( BerElement *ber )
+{
+ ber_len_t len;
+ ber_tag_t tag = ber_skip_tag( ber, &len );
+
+ return( len == 0 ? tag : LBER_DEFAULT );
+}
+
+ber_tag_t
+ber_get_boolean(
+ BerElement *ber,
+ ber_int_t *boolval )
+{
+ return ber_get_int( ber, boolval );
+}
+
+ber_tag_t
+ber_first_element(
+ BerElement *ber,
+ ber_len_t *len,
+ char **last )
+{
+ assert( last != NULL );
+
+ /* skip the sequence header, use the len to mark where to stop */
+ if ( ber_skip_tag( ber, len ) == LBER_DEFAULT ) {
+ *last = NULL;
+ return LBER_DEFAULT;
+ }
+
+ *last = ber->ber_ptr + *len;
+
+ if ( *len == 0 ) {
+ return LBER_DEFAULT;
+ }
+
+ return ber_peek_tag( ber, len );
+}
+
+ber_tag_t
+ber_next_element(
+ BerElement *ber,
+ ber_len_t *len,
+ LDAP_CONST char *last )
+{
+ assert( ber != NULL );
+ assert( last != NULL );
+ assert( LBER_VALID( ber ) );
+
+ if ( ber->ber_ptr >= last ) {
+ return LBER_DEFAULT;
+ }
+
+ return ber_peek_tag( ber, len );
+}
+
+/* VARARGS */
+ber_tag_t
+ber_scanf ( BerElement *ber,
+ LDAP_CONST char *fmt,
+ ... )
+{
+ va_list ap;
+ LDAP_CONST char *fmt_reset;
+ char *s, **ss, ***sss;
+ struct berval data, *bval, **bvp, ***bvpp;
+ ber_int_t *i;
+ ber_len_t *l;
+ ber_tag_t *t;
+ ber_tag_t rc;
+ ber_len_t len;
+
+ va_start( ap, fmt );
+
+ assert( ber != NULL );
+ assert( fmt != NULL );
+ assert( LBER_VALID( ber ) );
+
+ fmt_reset = fmt;
+
+ if ( ber->ber_debug & (LDAP_DEBUG_TRACE|LDAP_DEBUG_BER)) {
+ ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug,
+ "ber_scanf fmt (%s) ber:\n", fmt );
+ ber_log_dump( LDAP_DEBUG_BER, ber->ber_debug, ber, 1 );
+ }
+
+ for ( rc = 0; *fmt && rc != LBER_DEFAULT; fmt++ ) {
+ /* When this is modified, remember to update
+ * the error-cleanup code below accordingly. */
+ switch ( *fmt ) {
+ case '!': { /* Hook */
+ BERDecodeCallback *f;
+ void *p;
+
+ f = va_arg( ap, BERDecodeCallback * );
+ p = va_arg( ap, void * );
+
+ rc = (*f)( ber, p, 0 );
+ } break;
+
+ case 'a': /* octet string - allocate storage as needed */
+ ss = va_arg( ap, char ** );
+ rc = ber_get_stringa( ber, ss );
+ break;
+
+ case 'A': /* octet string - allocate storage as needed,
+ * but return NULL if len == 0 */
+ ss = va_arg( ap, char ** );
+ rc = ber_get_stringa_null( ber, ss );
+ break;
+
+ case 'b': /* boolean */
+ i = va_arg( ap, ber_int_t * );
+ rc = ber_get_boolean( ber, i );
+ break;
+
+ case 'B': /* bit string - allocate storage as needed */
+ ss = va_arg( ap, char ** );
+ l = va_arg( ap, ber_len_t * ); /* for length, in bits */
+ rc = ber_get_bitstringa( ber, ss, l );
+ break;
+
+ case 'e': /* enumerated */
+ case 'i': /* integer */
+ i = va_arg( ap, ber_int_t * );
+ rc = ber_get_int( ber, i );
+ break;
+
+ case 'l': /* length of next item */
+ l = va_arg( ap, ber_len_t * );
+ rc = ber_peek_tag( ber, l );
+ break;
+
+ case 'm': /* octet string in berval, in-place */
+ bval = va_arg( ap, struct berval * );
+ rc = ber_get_stringbv( ber, bval, 0 );
+ break;
+
+ case 'M': /* bvoffarray - must include address of
+ * a record len, and record offset.
+ * number of records will be returned thru
+ * len ptr on finish. parsed in-place.
+ */
+ {
+ bgbvr cookie = { BvOff, 0 };
+ bvp = va_arg( ap, struct berval ** );
+ l = va_arg( ap, ber_len_t * );
+ cookie.siz = *l;
+ cookie.off = va_arg( ap, ber_len_t );
+ rc = ber_get_stringbvl( ber, &cookie );
+ *bvp = cookie.result;
+ *l = cookie.siz;
+ break;
+ }
+
+ case 'n': /* null */
+ rc = ber_get_null( ber );
+ break;
+
+ case 'o': /* octet string in a supplied berval */
+ bval = va_arg( ap, struct berval * );
+ rc = ber_get_stringbv( ber, bval, LBER_BV_ALLOC );
+ break;
+
+ case 'O': /* octet string - allocate & include length */
+ bvp = va_arg( ap, struct berval ** );
+ rc = ber_get_stringal( ber, bvp );
+ break;
+
+ case 's': /* octet string - in a buffer */
+ s = va_arg( ap, char * );
+ l = va_arg( ap, ber_len_t * );
+ rc = ber_get_stringb( ber, s, l );
+ break;
+
+ case 't': /* tag of next item */
+ t = va_arg( ap, ber_tag_t * );
+ *t = rc = ber_peek_tag( ber, &len );
+ break;
+
+ case 'T': /* skip tag of next item */
+ t = va_arg( ap, ber_tag_t * );
+ *t = rc = ber_skip_tag( ber, &len );
+ break;
+
+ case 'v': /* sequence of strings */
+ {
+ bgbvr cookie = {
+ ChArray, LBER_BV_ALLOC | LBER_BV_STRING, sizeof( char * )
+ };
+ rc = ber_get_stringbvl( ber, &cookie );
+ *(va_arg( ap, char *** )) = cookie.result;
+ break;
+ }
+
+ case 'V': /* sequence of strings + lengths */
+ {
+ bgbvr cookie = {
+ BvVec, LBER_BV_ALLOC, sizeof( struct berval * )
+ };
+ rc = ber_get_stringbvl( ber, &cookie );
+ *(va_arg( ap, struct berval *** )) = cookie.result;
+ break;
+ }
+
+ case 'W': /* bvarray */
+ {
+ bgbvr cookie = {
+ BvArray, LBER_BV_ALLOC, sizeof( struct berval )
+ };
+ rc = ber_get_stringbvl( ber, &cookie );
+ *(va_arg( ap, struct berval ** )) = cookie.result;
+ break;
+ }
+
+ case 'x': /* skip the next element - whatever it is */
+ rc = ber_skip_element( ber, &data );
+ break;
+
+ case '{': /* begin sequence */
+ case '[': /* begin set */
+ switch ( fmt[1] ) {
+ case 'v': case 'V': case 'W': case 'M':
+ break;
+ default:
+ rc = ber_skip_tag( ber, &len );
+ break;
+ }
+ break;
+
+ case '}': /* end sequence */
+ case ']': /* end set */
+ break;
+
+ default:
+ if( ber->ber_debug ) {
+ ber_log_printf( LDAP_DEBUG_ANY, ber->ber_debug,
+ "ber_scanf: unknown fmt %c\n", *fmt );
+ }
+ rc = LBER_DEFAULT;
+ break;
+ }
+ }
+
+ va_end( ap );
+
+ if ( rc == LBER_DEFAULT ) {
+ /*
+ * Error. Reclaim malloced memory that was given to the caller.
+ * Set allocated pointers to NULL, "data length" outvalues to 0.
+ */
+ va_start( ap, fmt );
+
+ for ( ; fmt_reset < fmt; fmt_reset++ ) {
+ switch ( *fmt_reset ) {
+ case '!': { /* Hook */
+ BERDecodeCallback *f;
+ void *p;
+
+ f = va_arg( ap, BERDecodeCallback * );
+ p = va_arg( ap, void * );
+
+ (void) (*f)( ber, p, 1 );
+ } break;
+
+ case 'a': /* octet string - allocate storage as needed */
+ case 'A':
+ ss = va_arg( ap, char ** );
+ ber_memfree_x( *ss, ber->ber_memctx );
+ *ss = NULL;
+ break;
+
+ case 'b': /* boolean */
+ case 'e': /* enumerated */
+ case 'i': /* integer */
+ (void) va_arg( ap, ber_int_t * );
+ break;
+
+ case 'l': /* length of next item */
+ *(va_arg( ap, ber_len_t * )) = 0;
+ break;
+
+ case 'm': /* berval in-place */
+ bval = va_arg( ap, struct berval * );
+ BER_BVZERO( bval );
+ break;
+
+ case 'M': /* BVoff array in-place */
+ bvp = va_arg( ap, struct berval ** );
+ ber_memfree_x( *bvp, ber->ber_memctx );
+ *bvp = NULL;
+ *(va_arg( ap, ber_len_t * )) = 0;
+ (void) va_arg( ap, ber_len_t );
+ break;
+
+ case 'o': /* octet string in a supplied berval */
+ bval = va_arg( ap, struct berval * );
+ ber_memfree_x( bval->bv_val, ber->ber_memctx );
+ BER_BVZERO( bval );
+ break;
+
+ case 'O': /* octet string - allocate & include length */
+ bvp = va_arg( ap, struct berval ** );
+ ber_bvfree_x( *bvp, ber->ber_memctx );
+ *bvp = NULL;
+ break;
+
+ case 's': /* octet string - in a buffer */
+ (void) va_arg( ap, char * );
+ *(va_arg( ap, ber_len_t * )) = 0;
+ break;
+
+ case 't': /* tag of next item */
+ case 'T': /* skip tag of next item */
+ (void) va_arg( ap, ber_tag_t * );
+ break;
+
+ case 'B': /* bit string - allocate storage as needed */
+ ss = va_arg( ap, char ** );
+ ber_memfree_x( *ss, ber->ber_memctx );
+ *ss = NULL;
+ *(va_arg( ap, ber_len_t * )) = 0; /* for length, in bits */
+ break;
+
+ case 'v': /* sequence of strings */
+ sss = va_arg( ap, char *** );
+ ber_memvfree_x( (void **) *sss, ber->ber_memctx );
+ *sss = NULL;
+ break;
+
+ case 'V': /* sequence of strings + lengths */
+ bvpp = va_arg( ap, struct berval *** );
+ ber_bvecfree_x( *bvpp, ber->ber_memctx );
+ *bvpp = NULL;
+ break;
+
+ case 'W': /* BerVarray */
+ bvp = va_arg( ap, struct berval ** );
+ ber_bvarray_free_x( *bvp, ber->ber_memctx );
+ *bvp = NULL;
+ break;
+
+ case 'n': /* null */
+ case 'x': /* skip the next element - whatever it is */
+ case '{': /* begin sequence */
+ case '[': /* begin set */
+ case '}': /* end sequence */
+ case ']': /* end set */
+ break;
+
+ default:
+ /* format should be good */
+ assert( 0 );
+ }
+ }
+
+ va_end( ap );
+ }
+
+ return rc;
+}
diff --git a/libraries/liblber/dtest.c b/libraries/liblber/dtest.c
new file mode 100644
index 0000000..4785254
--- /dev/null
+++ b/libraries/liblber/dtest.c
@@ -0,0 +1,121 @@
+/* dtest.c - lber decoding test program */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by the University of Michigan
+ * (as part of U-MICH LDAP).
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+#include <ac/errno.h>
+
+#ifdef HAVE_CONSOLE_H
+#include <console.h>
+#endif
+
+#include <lber.h>
+
+static void usage( const char *name )
+{
+ fprintf( stderr, "usage: %s fmt\n", name );
+}
+
+int
+main( int argc, char **argv )
+{
+ char *s;
+
+ ber_tag_t tag;
+ ber_len_t len;
+
+ BerElement *ber;
+ Sockbuf *sb;
+ int fd;
+
+ /* enable debugging */
+ int ival = -1;
+ ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &ival );
+
+ if ( argc < 2 ) {
+ usage( argv[0] );
+ return( EXIT_FAILURE );
+ }
+
+#ifdef HAVE_CONSOLE_H
+ ccommand( &argv );
+ cshow( stdout );
+#endif
+
+ sb = ber_sockbuf_alloc();
+ fd = fileno( stdin );
+ ber_sockbuf_add_io( sb, &ber_sockbuf_io_fd, LBER_SBIOD_LEVEL_PROVIDER,
+ (void *)&fd );
+
+ ber = ber_alloc_t(LBER_USE_DER);
+ if( ber == NULL ) {
+ perror( "ber_alloc_t" );
+ return( EXIT_FAILURE );
+ }
+
+ for (;;) {
+ tag = ber_get_next( sb, &len, ber);
+ if( tag != LBER_ERROR ) break;
+
+ if( errno == EWOULDBLOCK ) continue;
+ if( errno == EAGAIN ) continue;
+
+ perror( "ber_get_next" );
+ return( EXIT_FAILURE );
+ }
+
+ printf("decode: message tag 0x%lx and length %ld\n",
+ (unsigned long) tag, (long) len );
+
+ for( s = argv[1]; *s; s++ ) {
+ char buf[128];
+ char fmt[2];
+ fmt[0] = *s;
+ fmt[1] = '\0';
+
+ printf("decode: format %s\n", fmt );
+ len = sizeof(buf);
+ tag = ber_scanf( ber, fmt, &buf[0], &len );
+
+ if( tag == LBER_ERROR ) {
+ perror( "ber_scanf" );
+ return( EXIT_FAILURE );
+ }
+ }
+
+ ber_sockbuf_free( sb );
+ return( EXIT_SUCCESS );
+}
diff --git a/libraries/liblber/encode.c b/libraries/liblber/encode.c
new file mode 100644
index 0000000..30d28eb
--- /dev/null
+++ b/libraries/liblber/encode.c
@@ -0,0 +1,651 @@
+/* encode.c - ber output encoding routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by the University of Michigan
+ * (as part of U-MICH LDAP).
+ */
+
+#include "portable.h"
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/stdarg.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "lber-int.h"
+
+
+#define OCTET_SIZE(type) ((ber_len_t) (sizeof(type)*CHAR_BIT + 7) / 8)
+#define TAGBUF_SIZE OCTET_SIZE(ber_tag_t)
+#define LENBUF_SIZE (1 + OCTET_SIZE(ber_len_t))
+#define HEADER_SIZE (TAGBUF_SIZE + LENBUF_SIZE)
+
+/*
+ * BER element size constrains:
+ *
+ * - We traditionally support a length of max 0xffffffff. However
+ * some functions return an int length so that is their max.
+ * MAXINT_BERSIZE is the max for those functions.
+ *
+ * - MAXINT_BERSIZE must fit in MAXINT_BERSIZE_OCTETS octets.
+ *
+ * - sizeof(ber_elem_size_t) is normally MAXINT_BERSIZE_OCTETS:
+ * Big enough for MAXINT_BERSIZE, but not more. (Larger wastes
+ * space in the working encoding and DER encoding of a sequence
+ * or set. Smaller further limits sizes near a sequence/set.)
+ *
+ * ber_len_t is mostly unrelated to this. Which may be for the best,
+ * since it is also used for lengths of data that are never encoded.
+ */
+#define MAXINT_BERSIZE \
+ (INT_MAX>0xffffffffUL ? (ber_len_t) 0xffffffffUL : INT_MAX-HEADER_SIZE)
+#define MAXINT_BERSIZE_OCTETS 4
+typedef ber_uint_t ber_elem_size_t; /* normally 32 bits */
+
+
+/* Prepend tag to ptr, which points to the end of a tag buffer */
+static unsigned char *
+ber_prepend_tag( unsigned char *ptr, ber_tag_t tag )
+{
+ do {
+ *--ptr = (unsigned char) tag & 0xffU;
+ } while ( (tag >>= 8) != 0 );
+
+ return ptr;
+}
+
+/* Prepend ber length to ptr, which points to the end of a length buffer */
+static unsigned char *
+ber_prepend_len( unsigned char *ptr, ber_len_t len )
+{
+ /*
+ * short len if it's less than 128 - one byte giving the len,
+ * with bit 8 0.
+ * long len otherwise - one byte with bit 8 set, giving the
+ * length of the length, followed by the length itself.
+ */
+
+ *--ptr = (unsigned char) len & 0xffU;
+
+ if ( len >= 0x80 ) {
+ unsigned char *endptr = ptr--;
+
+ while ( (len >>= 8) != 0 ) {
+ *ptr-- = (unsigned char) len & 0xffU;
+ }
+ *ptr = (unsigned char) (endptr - ptr) + 0x80U;
+ }
+
+ return ptr;
+}
+
+/* out->bv_len should be the buffer size on input */
+int
+ber_encode_oid( BerValue *in, BerValue *out )
+{
+ unsigned char *der;
+ unsigned long val1, val;
+ int i, j, len;
+ char *ptr, *end, *inend;
+
+ assert( in != NULL );
+ assert( out != NULL );
+
+ if ( !out->bv_val || out->bv_len < in->bv_len/2 )
+ return -1;
+
+ der = (unsigned char *) out->bv_val;
+ ptr = in->bv_val;
+ inend = ptr + in->bv_len;
+
+ /* OIDs start with <0-1>.<0-39> or 2.<any>, DER-encoded 40*val1+val2 */
+ if ( !isdigit( (unsigned char) *ptr )) return -1;
+ val1 = strtoul( ptr, &end, 10 );
+ if ( end == ptr || val1 > 2 ) return -1;
+ if ( *end++ != '.' || !isdigit( (unsigned char) *end )) return -1;
+ val = strtoul( end, &ptr, 10 );
+ if ( ptr == end ) return -1;
+ if ( val > (val1 < 2 ? 39 : LBER_OID_COMPONENT_MAX - 80) ) return -1;
+ val += val1 * 40;
+
+ for (;;) {
+ if ( ptr > inend ) return -1;
+
+ /* Write the OID component little-endian, then reverse it */
+ len = 0;
+ do {
+ der[len++] = (val & 0xff) | 0x80;
+ } while ( (val >>= 7) != 0 );
+ der[0] &= 0x7f;
+ for ( i = 0, j = len; i < --j; i++ ) {
+ unsigned char tmp = der[i];
+ der[i] = der[j];
+ der[j] = tmp;
+ }
+ der += len;
+
+ if ( ptr == inend )
+ break;
+
+ if ( *ptr++ != '.' ) return -1;
+ if ( !isdigit( (unsigned char) *ptr )) return -1;
+ val = strtoul( ptr, &end, 10 );
+ if ( end == ptr || val > LBER_OID_COMPONENT_MAX ) return -1;
+ ptr = end;
+ }
+
+ out->bv_len = (char *)der - out->bv_val;
+ return 0;
+}
+
+static int
+ber_put_int_or_enum(
+ BerElement *ber,
+ ber_int_t num,
+ ber_tag_t tag )
+{
+ ber_uint_t unum;
+ unsigned char sign, data[TAGBUF_SIZE+1 + OCTET_SIZE(ber_int_t)], *ptr;
+
+ sign = 0;
+ unum = num; /* Bit fiddling should be done with unsigned values */
+ if ( num < 0 ) {
+ sign = 0xffU;
+ unum = ~unum;
+ }
+ for ( ptr = &data[sizeof(data) - 1] ;; unum >>= 8 ) {
+ *ptr-- = (sign ^ (unsigned char) unum) & 0xffU;
+ if ( unum < 0x80 ) /* top bit at *ptr is sign bit */
+ break;
+ }
+
+ *ptr = (unsigned char) (&data[sizeof(data) - 1] - ptr); /* length */
+ ptr = ber_prepend_tag( ptr, tag );
+
+ return ber_write( ber, (char *) ptr, &data[sizeof(data)] - ptr, 0 );
+}
+
+int
+ber_put_enum(
+ BerElement *ber,
+ ber_int_t num,
+ ber_tag_t tag )
+{
+ if ( tag == LBER_DEFAULT ) {
+ tag = LBER_ENUMERATED;
+ }
+
+ return ber_put_int_or_enum( ber, num, tag );
+}
+
+int
+ber_put_int(
+ BerElement *ber,
+ ber_int_t num,
+ ber_tag_t tag )
+{
+ if ( tag == LBER_DEFAULT ) {
+ tag = LBER_INTEGER;
+ }
+
+ return ber_put_int_or_enum( ber, num, tag );
+}
+
+int
+ber_put_ostring(
+ BerElement *ber,
+ LDAP_CONST char *str,
+ ber_len_t len,
+ ber_tag_t tag )
+{
+ int rc;
+ unsigned char header[HEADER_SIZE], *ptr;
+
+ if ( tag == LBER_DEFAULT ) {
+ tag = LBER_OCTETSTRING;
+ }
+
+ if ( len > MAXINT_BERSIZE ) {
+ return -1;
+ }
+
+ ptr = ber_prepend_len( &header[sizeof(header)], len );
+ ptr = ber_prepend_tag( ptr, tag );
+
+ rc = ber_write( ber, (char *) ptr, &header[sizeof(header)] - ptr, 0 );
+ if ( rc >= 0 && ber_write( ber, str, len, 0 ) >= 0 ) {
+ /* length(tag + length + contents) */
+ return rc + (int) len;
+ }
+
+ return -1;
+}
+
+int
+ber_put_berval(
+ BerElement *ber,
+ struct berval *bv,
+ ber_tag_t tag )
+{
+ if( bv == NULL || bv->bv_len == 0 ) {
+ return ber_put_ostring( ber, "", (ber_len_t) 0, tag );
+ }
+
+ return ber_put_ostring( ber, bv->bv_val, bv->bv_len, tag );
+}
+
+int
+ber_put_string(
+ BerElement *ber,
+ LDAP_CONST char *str,
+ ber_tag_t tag )
+{
+ assert( str != NULL );
+
+ return ber_put_ostring( ber, str, strlen( str ), tag );
+}
+
+int
+ber_put_bitstring(
+ BerElement *ber,
+ LDAP_CONST char *str,
+ ber_len_t blen /* in bits */,
+ ber_tag_t tag )
+{
+ int rc;
+ ber_len_t len;
+ unsigned char unusedbits, header[HEADER_SIZE + 1], *ptr;
+
+ if ( tag == LBER_DEFAULT ) {
+ tag = LBER_BITSTRING;
+ }
+
+ unusedbits = (unsigned char) -blen & 7;
+ len = blen / 8 + (unusedbits != 0); /* (blen+7)/8 without overflow */
+ if ( len >= MAXINT_BERSIZE ) {
+ return -1;
+ }
+
+ header[sizeof(header) - 1] = unusedbits;
+ ptr = ber_prepend_len( &header[sizeof(header) - 1], len + 1 );
+ ptr = ber_prepend_tag( ptr, tag );
+
+ rc = ber_write( ber, (char *) ptr, &header[sizeof(header)] - ptr, 0 );
+ if ( rc >= 0 && ber_write( ber, str, len, 0 ) >= 0 ) {
+ /* length(tag + length + unused bit count + bitstring) */
+ return rc + (int) len;
+ }
+
+ return -1;
+}
+
+int
+ber_put_null( BerElement *ber, ber_tag_t tag )
+{
+ unsigned char data[TAGBUF_SIZE + 1], *ptr;
+
+ if ( tag == LBER_DEFAULT ) {
+ tag = LBER_NULL;
+ }
+
+ data[sizeof(data) - 1] = 0; /* length */
+ ptr = ber_prepend_tag( &data[sizeof(data) - 1], tag );
+
+ return ber_write( ber, (char *) ptr, &data[sizeof(data)] - ptr, 0 );
+}
+
+int
+ber_put_boolean(
+ BerElement *ber,
+ ber_int_t boolval,
+ ber_tag_t tag )
+{
+ unsigned char data[TAGBUF_SIZE + 2], *ptr;
+
+ if ( tag == LBER_DEFAULT )
+ tag = LBER_BOOLEAN;
+
+ data[sizeof(data) - 1] = boolval ? 0xff : 0;
+ data[sizeof(data) - 2] = 1; /* length */
+ ptr = ber_prepend_tag( &data[sizeof(data) - 2], tag );
+
+ return ber_write( ber, (char *) ptr, &data[sizeof(data)] - ptr, 0 );
+}
+
+
+/* Max number of length octets in a sequence or set, normally 5 */
+#define SOS_LENLEN (1 + (sizeof(ber_elem_size_t) > MAXINT_BERSIZE_OCTETS ? \
+ (ber_len_t) sizeof(ber_elem_size_t) : MAXINT_BERSIZE_OCTETS))
+
+/* Header of incomplete sequence or set */
+typedef struct seqorset_header {
+ char xtagbuf[TAGBUF_SIZE + 1]; /* room for tag + len(tag or len) */
+ union {
+ ber_elem_size_t offset; /* enclosing sequence/set */
+ char padding[SOS_LENLEN-1]; /* for final length encoding */
+ } next_sos;
+# define SOS_TAG_END(header) ((unsigned char *) &(header).next_sos - 1)
+} Seqorset_header;
+
+/* Start a sequence or set */
+static int
+ber_start_seqorset(
+ BerElement *ber,
+ ber_tag_t tag )
+{
+ /*
+ * Write the tag and SOS_LENLEN octets reserved for length, to ber.
+ * For now, length octets = (tag length, previous ber_sos_inner).
+ *
+ * Update ber_sos_inner and the write-cursor ber_sos_ptr. ber_ptr
+ * will not move until the outermost sequence or set is complete.
+ */
+
+ Seqorset_header header;
+ unsigned char *headptr;
+ ber_len_t taglen, headlen;
+ char *dest, **p;
+
+ assert( ber != NULL );
+ assert( LBER_VALID( ber ) );
+
+ if ( ber->ber_sos_ptr == NULL ) { /* outermost sequence/set? */
+ header.next_sos.offset = 0;
+ p = &ber->ber_ptr;
+ } else {
+ if ( (ber_len_t) -1 > (ber_elem_size_t) -1 ) {
+ if ( ber->ber_sos_inner > (ber_elem_size_t) -1 )
+ return -1;
+ }
+ header.next_sos.offset = ber->ber_sos_inner;
+ p = &ber->ber_sos_ptr;
+ }
+ headptr = ber_prepend_tag( SOS_TAG_END(header), tag );
+ *SOS_TAG_END(header) = taglen = SOS_TAG_END(header) - headptr;
+ headlen = taglen + SOS_LENLEN;
+
+ /* As ber_write(,headptr,headlen,) except update ber_sos_ptr, not *p */
+ if ( headlen > (ber_len_t) (ber->ber_end - *p) ) {
+ if ( ber_realloc( ber, headlen ) != 0 )
+ return -1;
+ }
+ dest = *p;
+ AC_MEMCPY( dest, headptr, headlen );
+ ber->ber_sos_ptr = dest + headlen;
+
+ ber->ber_sos_inner = dest + taglen - ber->ber_buf;
+
+ /*
+ * Do not return taglen + SOS_LENLEN here - then ber_put_seqorset()
+ * should return lenlen - SOS_LENLEN + len, which can be < 0.
+ */
+ return 0;
+}
+
+int
+ber_start_seq( BerElement *ber, ber_tag_t tag )
+{
+ if ( tag == LBER_DEFAULT ) {
+ tag = LBER_SEQUENCE;
+ }
+
+ return ber_start_seqorset( ber, tag );
+}
+
+int
+ber_start_set( BerElement *ber, ber_tag_t tag )
+{
+ if ( tag == LBER_DEFAULT ) {
+ tag = LBER_SET;
+ }
+
+ return ber_start_seqorset( ber, tag );
+}
+
+/* End a sequence or set */
+static int
+ber_put_seqorset( BerElement *ber )
+{
+ Seqorset_header header;
+ unsigned char *lenptr; /* length octets in the sequence/set */
+ ber_len_t len; /* length(contents) */
+ ber_len_t xlen; /* len + length(length) */
+
+ assert( ber != NULL );
+ assert( LBER_VALID( ber ) );
+
+ if ( ber->ber_sos_ptr == NULL ) return -1;
+
+ lenptr = (unsigned char *) ber->ber_buf + ber->ber_sos_inner;
+ xlen = ber->ber_sos_ptr - (char *) lenptr;
+ if ( xlen > MAXINT_BERSIZE + SOS_LENLEN ) {
+ return -1;
+ }
+
+ /* Extract sequence/set information from length octets */
+ memcpy( SOS_TAG_END(header), lenptr, SOS_LENLEN );
+
+ /* Store length, and close gap of leftover reserved length octets */
+ len = xlen - SOS_LENLEN;
+ if ( !(ber->ber_options & LBER_USE_DER) ) {
+ int i;
+ lenptr[0] = SOS_LENLEN - 1 + 0x80; /* length(length)-1 */
+ for( i = SOS_LENLEN; --i > 0; len >>= 8 ) {
+ lenptr[i] = len & 0xffU;
+ }
+ } else {
+ unsigned char *p = ber_prepend_len( lenptr + SOS_LENLEN, len );
+ ber_len_t unused = p - lenptr;
+ if ( unused != 0 ) {
+ /* length(length) < the reserved SOS_LENLEN bytes */
+ xlen -= unused;
+ AC_MEMCPY( lenptr, p, xlen );
+ ber->ber_sos_ptr = (char *) lenptr + xlen;
+ }
+ }
+
+ ber->ber_sos_inner = header.next_sos.offset;
+ if ( header.next_sos.offset == 0 ) { /* outermost sequence/set? */
+ /* The ber_ptr is at the set/seq start - move it to the end */
+ ber->ber_ptr = ber->ber_sos_ptr;
+ ber->ber_sos_ptr = NULL;
+ }
+
+ return xlen + *SOS_TAG_END(header); /* lenlen + len + taglen */
+}
+
+int
+ber_put_seq( BerElement *ber )
+{
+ return ber_put_seqorset( ber );
+}
+
+int
+ber_put_set( BerElement *ber )
+{
+ return ber_put_seqorset( ber );
+}
+
+/* N tag */
+static ber_tag_t lber_int_null = 0;
+
+/* VARARGS */
+int
+ber_printf( BerElement *ber, LDAP_CONST char *fmt, ... )
+{
+ va_list ap;
+ char *s, **ss;
+ struct berval *bv, **bvp;
+ int rc;
+ ber_int_t i;
+ ber_len_t len;
+
+ assert( ber != NULL );
+ assert( fmt != NULL );
+ assert( LBER_VALID( ber ) );
+
+ va_start( ap, fmt );
+
+ for ( rc = 0; *fmt && rc != -1; fmt++ ) {
+ switch ( *fmt ) {
+ case '!': { /* hook */
+ BEREncodeCallback *f;
+ void *p;
+
+ ber->ber_usertag = 0;
+
+ f = va_arg( ap, BEREncodeCallback * );
+ p = va_arg( ap, void * );
+ rc = (*f)( ber, p );
+
+ if ( ber->ber_usertag ) {
+ goto next;
+ }
+ } break;
+
+ case 'b': /* boolean */
+ i = va_arg( ap, ber_int_t );
+ rc = ber_put_boolean( ber, i, ber->ber_tag );
+ break;
+
+ case 'i': /* int */
+ i = va_arg( ap, ber_int_t );
+ rc = ber_put_int( ber, i, ber->ber_tag );
+ break;
+
+ case 'e': /* enumeration */
+ i = va_arg( ap, ber_int_t );
+ rc = ber_put_enum( ber, i, ber->ber_tag );
+ break;
+
+ case 'n': /* null */
+ rc = ber_put_null( ber, ber->ber_tag );
+ break;
+
+ case 'N': /* Debug NULL */
+ rc = 0;
+ if( lber_int_null != 0 ) {
+ /* Insert NULL to ensure peer ignores unknown tags */
+ rc = ber_put_null( ber, lber_int_null );
+ }
+ break;
+
+ case 'o': /* octet string (non-null terminated) */
+ s = va_arg( ap, char * );
+ len = va_arg( ap, ber_len_t );
+ rc = ber_put_ostring( ber, s, len, ber->ber_tag );
+ break;
+
+ case 'O': /* berval octet string */
+ bv = va_arg( ap, struct berval * );
+ if( bv == NULL ) break;
+ rc = ber_put_berval( ber, bv, ber->ber_tag );
+ break;
+
+ case 's': /* string */
+ s = va_arg( ap, char * );
+ rc = ber_put_string( ber, s, ber->ber_tag );
+ break;
+
+ case 'B': /* bit string */
+ case 'X': /* bit string (deprecated) */
+ s = va_arg( ap, char * );
+ len = va_arg( ap, ber_len_t ); /* in bits */
+ rc = ber_put_bitstring( ber, s, len, ber->ber_tag );
+ break;
+
+ case 't': /* tag for the next element */
+ ber->ber_tag = va_arg( ap, ber_tag_t );
+ goto next;
+
+ case 'v': /* vector of strings */
+ if ( (ss = va_arg( ap, char ** )) == NULL )
+ break;
+ for ( i = 0; ss[i] != NULL; i++ ) {
+ if ( (rc = ber_put_string( ber, ss[i],
+ ber->ber_tag )) == -1 )
+ break;
+ }
+ break;
+
+ case 'V': /* sequences of strings + lengths */
+ if ( (bvp = va_arg( ap, struct berval ** )) == NULL )
+ break;
+ for ( i = 0; bvp[i] != NULL; i++ ) {
+ if ( (rc = ber_put_berval( ber, bvp[i],
+ ber->ber_tag )) == -1 )
+ break;
+ }
+ break;
+
+ case 'W': /* BerVarray */
+ if ( (bv = va_arg( ap, BerVarray )) == NULL )
+ break;
+ for ( i = 0; bv[i].bv_val != NULL; i++ ) {
+ if ( (rc = ber_put_berval( ber, &bv[i],
+ ber->ber_tag )) == -1 )
+ break;
+ }
+ break;
+
+ case '{': /* begin sequence */
+ rc = ber_start_seq( ber, ber->ber_tag );
+ break;
+
+ case '}': /* end sequence */
+ rc = ber_put_seqorset( ber );
+ break;
+
+ case '[': /* begin set */
+ rc = ber_start_set( ber, ber->ber_tag );
+ break;
+
+ case ']': /* end set */
+ rc = ber_put_seqorset( ber );
+ break;
+
+ default:
+ if( ber->ber_debug ) {
+ ber_log_printf( LDAP_DEBUG_ANY, ber->ber_debug,
+ "ber_printf: unknown fmt %c\n", *fmt );
+ }
+ rc = -1;
+ break;
+ }
+
+ ber->ber_tag = LBER_DEFAULT;
+ next:;
+ }
+
+ va_end( ap );
+
+ return rc;
+}
diff --git a/libraries/liblber/etest.c b/libraries/liblber/etest.c
new file mode 100644
index 0000000..3f60878
--- /dev/null
+++ b/libraries/liblber/etest.c
@@ -0,0 +1,181 @@
+/* etest.c - lber encoding test program */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by the University of Michigan
+ * (as part of U-MICH LDAP).
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#ifdef HAVE_CONSOLE_H
+#include <console.h>
+#endif /* HAVE_CONSOLE_H */
+
+#include "lber.h"
+
+static void usage( const char *name )
+{
+ fprintf( stderr, "usage: %s fmtstring\n", name );
+}
+
+static char* getbuf( void ) {
+ char *p;
+ static char buf[1024];
+
+ if ( fgets( buf, sizeof(buf), stdin ) == NULL ) return NULL;
+
+ if ( (p = strchr( buf, '\n' )) != NULL ) *p = '\0';
+
+ return buf;
+}
+
+int
+main( int argc, char **argv )
+{
+ char *s;
+ int tag;
+
+ int fd, rc;
+ BerElement *ber;
+ Sockbuf *sb;
+
+ /* enable debugging */
+ int ival = -1;
+ ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &ival );
+
+ if ( argc < 2 ) {
+ usage( argv[0] );
+ return( EXIT_FAILURE );
+ }
+
+#ifdef HAVE_CONSOLE_H
+ ccommand( &argv );
+ cshow( stdout );
+
+ if (( fd = open( "lber-test", O_WRONLY|O_CREAT|O_TRUNC|O_BINARY ))
+ < 0 ) {
+ perror( "open" );
+ return( EXIT_FAILURE );
+ }
+
+#else
+ fd = fileno(stdout);
+#endif
+
+ sb = ber_sockbuf_alloc();
+ ber_sockbuf_add_io( sb, &ber_sockbuf_io_fd, LBER_SBIOD_LEVEL_PROVIDER,
+ (void *)&fd );
+
+ if( sb == NULL ) {
+ perror( "ber_sockbuf_alloc_fd" );
+ return( EXIT_FAILURE );
+ }
+
+ if ( (ber = ber_alloc_t( LBER_USE_DER )) == NULL ) {
+ perror( "ber_alloc" );
+ return( EXIT_FAILURE );
+ }
+
+ fprintf(stderr, "encode: start\n" );
+ if( ber_printf( ber, "{" /*}*/ ) ) {
+ perror( "ber_printf {" /*}*/ );
+ return( EXIT_FAILURE );
+ }
+
+ for ( s = argv[1]; *s; s++ ) {
+ char *buf;
+ char fmt[2];
+
+ fmt[0] = *s;
+ fmt[1] = '\0';
+
+ fprintf(stderr, "encode: %s\n", fmt );
+ switch ( *s ) {
+ case 'i': /* int */
+ case 'b': /* boolean */
+ case 'e': /* enumeration */
+ buf = getbuf();
+ rc = ber_printf( ber, fmt, atoi(buf) );
+ break;
+
+ case 'n': /* null */
+ case '{': /* begin sequence */
+ case '}': /* end sequence */
+ case '[': /* begin set */
+ case ']': /* end set */
+ rc = ber_printf( ber, fmt );
+ break;
+
+ case 'o': /* octet string (non-null terminated) */
+ case 'B': /* bit string */
+ buf = getbuf();
+ rc = ber_printf( ber, fmt, buf, strlen(buf) );
+ break;
+
+ case 's': /* string */
+ buf = getbuf();
+ rc = ber_printf( ber, fmt, buf );
+ break;
+ case 't': /* tag for the next element */
+ buf = getbuf();
+ tag = atoi(buf);
+ rc = ber_printf( ber, fmt, tag );
+ break;
+
+ default:
+ fprintf( stderr, "encode: unknown fmt %c\n", *fmt );
+ rc = -1;
+ break;
+ }
+
+ if( rc == -1 ) {
+ perror( "ber_printf" );
+ return( EXIT_FAILURE );
+ }
+ }
+
+ fprintf(stderr, "encode: end\n" );
+ if( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
+ perror( /*{*/ "ber_printf }" );
+ return( EXIT_FAILURE );
+ }
+
+ if ( ber_flush2( sb, ber, LBER_FLUSH_FREE_ALWAYS ) == -1 ) {
+ perror( "ber_flush2" );
+ return( EXIT_FAILURE );
+ }
+
+ ber_sockbuf_free( sb );
+ return( EXIT_SUCCESS );
+}
diff --git a/libraries/liblber/idtest.c b/libraries/liblber/idtest.c
new file mode 100644
index 0000000..f4e7ac2
--- /dev/null
+++ b/libraries/liblber/idtest.c
@@ -0,0 +1,87 @@
+/* idtest.c - ber decoding test program using isode libraries */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by the University of Michigan
+ * (as part of U-MICH LDAP).
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#ifdef HAVE_PSAP_H
+#include <psap.h>
+#include <quipu/attr.h>
+#endif
+
+int
+main( int argc, char **argv )
+{
+#ifdef HAVE_PSAP_H
+ PE pe;
+ PS psin, psout, pserr;
+
+ /* read the pe from standard in */
+ if ( (psin = ps_alloc( std_open )) == NULLPS ) {
+ perror( "ps_alloc" );
+ exit( EXIT_FAILURE );
+ }
+ if ( std_setup( psin, stdin ) == NOTOK ) {
+ perror( "std_setup" );
+ exit( EXIT_FAILURE );
+ }
+ /* write the pe to standard out */
+ if ( (psout = ps_alloc( std_open )) == NULLPS ) {
+ perror( "ps_alloc" );
+ exit( EXIT_FAILURE );
+ }
+ if ( std_setup( psout, stdout ) == NOTOK ) {
+ perror( "std_setup" );
+ exit( EXIT_FAILURE );
+ }
+ /* pretty print it to standard error */
+ if ( (pserr = ps_alloc( std_open )) == NULLPS ) {
+ perror( "ps_alloc" );
+ exit( EXIT_FAILURE );
+ }
+ if ( std_setup( pserr, stderr ) == NOTOK ) {
+ perror( "std_setup" );
+ exit( EXIT_FAILURE );
+ }
+
+ while ( (pe = ps2pe( psin )) != NULLPE ) {
+ pe2pl( pserr, pe );
+ pe2ps( psout, pe );
+ }
+
+ exit( EXIT_SUCCESS );
+#else
+ fprintf(stderr, "requires ISODE X.500 distribution.\n");
+ return( EXIT_FAILURE );
+#endif
+}
diff --git a/libraries/liblber/io.c b/libraries/liblber/io.c
new file mode 100644
index 0000000..ea5aaa6
--- /dev/null
+++ b/libraries/liblber/io.c
@@ -0,0 +1,725 @@
+/* io.c - ber general i/o routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by the University of Michigan
+ * (as part of U-MICH LDAP).
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+
+#include "lber-int.h"
+#include "ldap_log.h"
+
+ber_slen_t
+ber_skip_data(
+ BerElement *ber,
+ ber_len_t len )
+{
+ ber_len_t actuallen, nleft;
+
+ assert( ber != NULL );
+ assert( LBER_VALID( ber ) );
+
+ nleft = ber_pvt_ber_remaining( ber );
+ actuallen = nleft < len ? nleft : len;
+ ber->ber_ptr += actuallen;
+ ber->ber_tag = *(unsigned char *)ber->ber_ptr;
+
+ return( (ber_slen_t) actuallen );
+}
+
+/*
+ * Read from the ber buffer. The caller must maintain ber->ber_tag.
+ * Do not use to read whole tags. See ber_get_tag() and ber_skip_data().
+ */
+ber_slen_t
+ber_read(
+ BerElement *ber,
+ char *buf,
+ ber_len_t len )
+{
+ ber_len_t actuallen, nleft;
+
+ assert( ber != NULL );
+ assert( buf != NULL );
+ assert( LBER_VALID( ber ) );
+
+ nleft = ber_pvt_ber_remaining( ber );
+ actuallen = nleft < len ? nleft : len;
+
+ AC_MEMCPY( buf, ber->ber_ptr, actuallen );
+
+ ber->ber_ptr += actuallen;
+
+ return( (ber_slen_t) actuallen );
+}
+
+/*
+ * Write to the ber buffer.
+ * Note that ber_start_seqorset/ber_put_seqorset() bypass ber_write().
+ */
+ber_slen_t
+ber_write(
+ BerElement *ber,
+ LDAP_CONST char *buf,
+ ber_len_t len,
+ int zero ) /* nonzero is unsupported from OpenLDAP 2.4.18 */
+{
+ char **p;
+
+ assert( ber != NULL );
+ assert( buf != NULL );
+ assert( LBER_VALID( ber ) );
+
+ if ( zero != 0 ) {
+ ber_log_printf( LDAP_DEBUG_ANY, ber->ber_debug, "%s",
+ "ber_write: nonzero 4th argument not supported\n" );
+ return( -1 );
+ }
+
+ p = ber->ber_sos_ptr == NULL ? &ber->ber_ptr : &ber->ber_sos_ptr;
+ if ( len > (ber_len_t) (ber->ber_end - *p) ) {
+ if ( ber_realloc( ber, len ) != 0 ) return( -1 );
+ }
+ AC_MEMCPY( *p, buf, len );
+ *p += len;
+
+ return( (ber_slen_t) len );
+}
+
+/* Resize the ber buffer */
+int
+ber_realloc( BerElement *ber, ber_len_t len )
+{
+ ber_len_t total, offset, sos_offset, rw_offset;
+ char *buf;
+
+ assert( ber != NULL );
+ assert( LBER_VALID( ber ) );
+
+ /* leave room for ber_flatten() to \0-terminate ber_buf */
+ if ( ++len == 0 ) {
+ return( -1 );
+ }
+
+ total = ber_pvt_ber_total( ber );
+
+#define LBER_EXBUFSIZ 4060 /* a few words less than 2^N for binary buddy */
+#if defined( LBER_EXBUFSIZ ) && LBER_EXBUFSIZ > 0
+# ifndef notdef
+ /* don't realloc by small amounts */
+ total += len < LBER_EXBUFSIZ ? LBER_EXBUFSIZ : len;
+# else
+ { /* not sure what value this adds. reduce fragmentation? */
+ ber_len_t have = (total + (LBER_EXBUFSIZE - 1)) / LBER_EXBUFSIZ;
+ ber_len_t need = (len + (LBER_EXBUFSIZ - 1)) / LBER_EXBUFSIZ;
+ total = ( have + need ) * LBER_EXBUFSIZ;
+ }
+# endif
+#else
+ total += len; /* realloc just what's needed */
+#endif
+
+ if ( total < len || total > (ber_len_t)-1 / 2 /* max ber_slen_t */ ) {
+ return( -1 );
+ }
+
+ buf = ber->ber_buf;
+ offset = ber->ber_ptr - buf;
+ sos_offset = ber->ber_sos_ptr ? ber->ber_sos_ptr - buf : 0;
+ /* if ber_sos_ptr != NULL, it is > ber_buf so that sos_offset > 0 */
+ rw_offset = ber->ber_rwptr ? ber->ber_rwptr - buf : 0;
+
+ buf = (char *) ber_memrealloc_x( buf, total, ber->ber_memctx );
+ if ( buf == NULL ) {
+ return( -1 );
+ }
+
+ ber->ber_buf = buf;
+ ber->ber_end = buf + total;
+ ber->ber_ptr = buf + offset;
+ if ( sos_offset )
+ ber->ber_sos_ptr = buf + sos_offset;
+ if ( ber->ber_rwptr )
+ ber->ber_rwptr = buf + rw_offset;
+
+ return( 0 );
+}
+
+void
+ber_free_buf( BerElement *ber )
+{
+ assert( LBER_VALID( ber ) );
+
+ if ( ber->ber_buf) ber_memfree_x( ber->ber_buf, ber->ber_memctx );
+
+ ber->ber_buf = NULL;
+ ber->ber_sos_ptr = NULL;
+ ber->ber_valid = LBER_UNINITIALIZED;
+}
+
+void
+ber_free( BerElement *ber, int freebuf )
+{
+ if( ber == NULL ) {
+ LDAP_MEMORY_DEBUG_ASSERT( ber != NULL );
+ return;
+ }
+
+ if( freebuf ) ber_free_buf( ber );
+
+ ber_memfree_x( (char *) ber, ber->ber_memctx );
+}
+
+int
+ber_flush( Sockbuf *sb, BerElement *ber, int freeit )
+{
+ return ber_flush2( sb, ber,
+ freeit ? LBER_FLUSH_FREE_ON_SUCCESS
+ : LBER_FLUSH_FREE_NEVER );
+}
+
+int
+ber_flush2( Sockbuf *sb, BerElement *ber, int freeit )
+{
+ ber_len_t towrite;
+ ber_slen_t rc;
+
+ assert( sb != NULL );
+ assert( ber != NULL );
+ assert( SOCKBUF_VALID( sb ) );
+ assert( LBER_VALID( ber ) );
+
+ if ( ber->ber_rwptr == NULL ) {
+ ber->ber_rwptr = ber->ber_buf;
+ }
+ towrite = ber->ber_ptr - ber->ber_rwptr;
+
+ if ( sb->sb_debug ) {
+ ber_log_printf( LDAP_DEBUG_TRACE, sb->sb_debug,
+ "ber_flush2: %ld bytes to sd %ld%s\n",
+ towrite, (long) sb->sb_fd,
+ ber->ber_rwptr != ber->ber_buf ? " (re-flush)" : "" );
+ ber_log_bprint( LDAP_DEBUG_BER, sb->sb_debug,
+ ber->ber_rwptr, towrite );
+ }
+
+ while ( towrite > 0 ) {
+#ifdef LBER_TRICKLE
+ sleep(1);
+ rc = ber_int_sb_write( sb, ber->ber_rwptr, 1 );
+#else
+ rc = ber_int_sb_write( sb, ber->ber_rwptr, towrite );
+#endif
+ if ( rc <= 0 ) {
+ if ( freeit & LBER_FLUSH_FREE_ON_ERROR ) ber_free( ber, 1 );
+ return -1;
+ }
+ towrite -= rc;
+ ber->ber_rwptr += rc;
+ }
+
+ if ( freeit & LBER_FLUSH_FREE_ON_SUCCESS ) ber_free( ber, 1 );
+
+ return 0;
+}
+
+BerElement *
+ber_alloc_t( int options )
+{
+ BerElement *ber;
+
+ ber = (BerElement *) LBER_CALLOC( 1, sizeof(BerElement) );
+
+ if ( ber == NULL ) {
+ return NULL;
+ }
+
+ ber->ber_valid = LBER_VALID_BERELEMENT;
+ ber->ber_tag = LBER_DEFAULT;
+ ber->ber_options = options;
+ ber->ber_debug = ber_int_debug;
+
+ assert( LBER_VALID( ber ) );
+ return ber;
+}
+
+BerElement *
+ber_alloc( void ) /* deprecated */
+{
+ return ber_alloc_t( 0 );
+}
+
+BerElement *
+der_alloc( void ) /* deprecated */
+{
+ return ber_alloc_t( LBER_USE_DER );
+}
+
+BerElement *
+ber_dup( BerElement *ber )
+{
+ BerElement *new;
+
+ assert( ber != NULL );
+ assert( LBER_VALID( ber ) );
+
+ if ( (new = ber_alloc_t( ber->ber_options )) == NULL ) {
+ return NULL;
+ }
+
+ *new = *ber;
+
+ assert( LBER_VALID( new ) );
+ return( new );
+}
+
+
+void
+ber_init2( BerElement *ber, struct berval *bv, int options )
+{
+ assert( ber != NULL );
+
+ (void) memset( (char *)ber, '\0', sizeof( BerElement ));
+ ber->ber_valid = LBER_VALID_BERELEMENT;
+ ber->ber_tag = LBER_DEFAULT;
+ ber->ber_options = (char) options;
+ ber->ber_debug = ber_int_debug;
+
+ if ( bv != NULL ) {
+ ber->ber_buf = bv->bv_val;
+ ber->ber_ptr = ber->ber_buf;
+ ber->ber_end = ber->ber_buf + bv->bv_len;
+ }
+
+ assert( LBER_VALID( ber ) );
+}
+
+/* OLD U-Mich ber_init() */
+void
+ber_init_w_nullc( BerElement *ber, int options )
+{
+ ber_init2( ber, NULL, options );
+}
+
+/* New C-API ber_init() */
+/* This function constructs a BerElement containing a copy
+** of the data in the bv argument.
+*/
+BerElement *
+ber_init( struct berval *bv )
+{
+ BerElement *ber;
+
+ assert( bv != NULL );
+
+ if ( bv == NULL ) {
+ return NULL;
+ }
+
+ ber = ber_alloc_t( 0 );
+
+ if( ber == NULL ) {
+ /* allocation failed */
+ return NULL;
+ }
+
+ /* copy the data */
+ if ( ((ber_len_t) ber_write ( ber, bv->bv_val, bv->bv_len, 0 ))
+ != bv->bv_len )
+ {
+ /* write failed, so free and return NULL */
+ ber_free( ber, 1 );
+ return NULL;
+ }
+
+ ber_reset( ber, 1 ); /* reset the pointer to the start of the buffer */
+ return ber;
+}
+
+/* New C-API ber_flatten routine */
+/* This routine allocates a struct berval whose contents are a BER
+** encoding taken from the ber argument. The bvPtr pointer points to
+** the returned berval.
+**
+** ber_flatten2 is the same, but uses a struct berval passed by
+** the caller. If alloc is 0 the returned bv uses the ber buf directly.
+*/
+int ber_flatten2(
+ BerElement *ber,
+ struct berval *bv,
+ int alloc )
+{
+ assert( bv != NULL );
+
+ if ( bv == NULL ) {
+ return -1;
+ }
+
+ if ( ber == NULL ) {
+ /* ber is null, create an empty berval */
+ bv->bv_val = NULL;
+ bv->bv_len = 0;
+
+ } else if ( ber->ber_sos_ptr != NULL ) {
+ /* unmatched "{" and "}" */
+ return -1;
+
+ } else {
+ /* copy the berval */
+ ber_len_t len = ber_pvt_ber_write( ber );
+
+ if ( alloc ) {
+ bv->bv_val = (char *) ber_memalloc_x( len + 1, ber->ber_memctx );
+ if ( bv->bv_val == NULL ) {
+ return -1;
+ }
+ AC_MEMCPY( bv->bv_val, ber->ber_buf, len );
+ bv->bv_val[len] = '\0';
+ } else if ( ber->ber_buf != NULL ) {
+ bv->bv_val = ber->ber_buf;
+ bv->bv_val[len] = '\0';
+ } else {
+ bv->bv_val = "";
+ }
+ bv->bv_len = len;
+ }
+ return 0;
+}
+
+int ber_flatten(
+ BerElement *ber,
+ struct berval **bvPtr)
+{
+ struct berval *bv;
+ int rc;
+
+ assert( bvPtr != NULL );
+
+ if(bvPtr == NULL) {
+ return -1;
+ }
+
+ bv = ber_memalloc_x( sizeof(struct berval), ber->ber_memctx );
+ if ( bv == NULL ) {
+ return -1;
+ }
+ rc = ber_flatten2(ber, bv, 1);
+ if (rc == -1) {
+ ber_memfree_x(bv, ber->ber_memctx);
+ } else {
+ *bvPtr = bv;
+ }
+ return rc;
+}
+
+void
+ber_reset( BerElement *ber, int was_writing )
+{
+ assert( ber != NULL );
+ assert( LBER_VALID( ber ) );
+
+ if ( was_writing ) {
+ ber->ber_end = ber->ber_ptr;
+ ber->ber_ptr = ber->ber_buf;
+
+ } else {
+ ber->ber_ptr = ber->ber_end;
+ }
+
+ ber->ber_rwptr = NULL;
+}
+
+/*
+ * A rewrite of ber_get_next that can safely be called multiple times
+ * for the same packet. It will simply continue where it stopped until
+ * a full packet is read.
+ */
+
+#define LENSIZE 4
+
+ber_tag_t
+ber_get_next(
+ Sockbuf *sb,
+ ber_len_t *len,
+ BerElement *ber )
+{
+ assert( sb != NULL );
+ assert( len != NULL );
+ assert( ber != NULL );
+ assert( SOCKBUF_VALID( sb ) );
+ assert( LBER_VALID( ber ) );
+
+ if ( ber->ber_debug & LDAP_DEBUG_TRACE ) {
+ ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug,
+ "ber_get_next\n" );
+ }
+
+ /*
+ * Any ber element looks like this: tag length contents.
+ * Assuming everything's ok, we return the tag byte (we
+ * can assume a single byte), return the length in len,
+ * and the rest of the undecoded element in buf.
+ *
+ * Assumptions:
+ * 1) small tags (less than 128)
+ * 2) definite lengths
+ * 3) primitive encodings used whenever possible
+ *
+ * The code also handles multi-byte tags. The first few bytes
+ * of the message are read to check for multi-byte tags and
+ * lengths. These bytes are temporarily stored in the ber_tag,
+ * ber_len, and ber_usertag fields of the berelement until
+ * tag/len parsing is complete. After this parsing, any leftover
+ * bytes and the rest of the message are copied into the ber_buf.
+ *
+ * We expect tag and len to be at most 32 bits wide.
+ */
+
+ if (ber->ber_rwptr == NULL) {
+ assert( ber->ber_buf == NULL );
+ ber->ber_rwptr = (char *) &ber->ber_len-1;
+ ber->ber_ptr = ber->ber_rwptr;
+ ber->ber_tag = 0;
+ }
+
+ while (ber->ber_rwptr > (char *)&ber->ber_tag && ber->ber_rwptr <
+ (char *)&ber->ber_len + LENSIZE*2) {
+ ber_slen_t sblen;
+ char buf[sizeof(ber->ber_len)-1];
+ ber_len_t tlen = 0;
+
+ /* The tag & len can be at most 9 bytes; we try to read up to 8 here */
+ sock_errset(0);
+ sblen=((char *)&ber->ber_len + LENSIZE*2 - 1)-ber->ber_rwptr;
+ /* Trying to read the last len byte of a 9 byte tag+len */
+ if (sblen<1)
+ sblen = 1;
+ sblen=ber_int_sb_read( sb, ber->ber_rwptr, sblen );
+ if (sblen<=0) return LBER_DEFAULT;
+ ber->ber_rwptr += sblen;
+
+ /* We got at least one byte, try to parse the tag. */
+ if (ber->ber_ptr == (char *)&ber->ber_len-1) {
+ ber_tag_t tag;
+ unsigned char *p = (unsigned char *)ber->ber_ptr;
+ tag = *p++;
+ if ((tag & LBER_BIG_TAG_MASK) == LBER_BIG_TAG_MASK) {
+ ber_len_t i;
+ for (i=1; (char *)p<ber->ber_rwptr; i++) {
+ tag <<= 8;
+ tag |= *p++;
+ if (!(tag & LBER_MORE_TAG_MASK))
+ break;
+ /* Is the tag too big? */
+ if (i == sizeof(ber_tag_t)-1) {
+ sock_errset(ERANGE);
+ return LBER_DEFAULT;
+ }
+ }
+ /* Did we run out of bytes? */
+ if ((char *)p == ber->ber_rwptr) {
+ sock_errset(EWOULDBLOCK);
+ return LBER_DEFAULT;
+ }
+ }
+ ber->ber_tag = tag;
+ ber->ber_ptr = (char *)p;
+ }
+
+ if ( ber->ber_ptr == ber->ber_rwptr ) {
+ sock_errset(EWOULDBLOCK);
+ return LBER_DEFAULT;
+ }
+
+ /* Now look for the length */
+ if (*ber->ber_ptr & 0x80) { /* multi-byte */
+ int i;
+ unsigned char *p = (unsigned char *)ber->ber_ptr;
+ int llen = *p++ & 0x7f;
+ if (llen > LENSIZE) {
+ sock_errset(ERANGE);
+ return LBER_DEFAULT;
+ }
+ /* Not enough bytes? */
+ if (ber->ber_rwptr - (char *)p < llen) {
+ sock_errset(EWOULDBLOCK);
+ return LBER_DEFAULT;
+ }
+ for (i=0; i<llen; i++) {
+ tlen <<=8;
+ tlen |= *p++;
+ }
+ ber->ber_ptr = (char *)p;
+ } else {
+ tlen = *(unsigned char *)ber->ber_ptr++;
+ }
+
+ /* Are there leftover data bytes inside ber->ber_len? */
+ if (ber->ber_ptr < (char *)&ber->ber_usertag) {
+ if (ber->ber_rwptr < (char *)&ber->ber_usertag) {
+ sblen = ber->ber_rwptr - ber->ber_ptr;
+ } else {
+ sblen = (char *)&ber->ber_usertag - ber->ber_ptr;
+ }
+ AC_MEMCPY(buf, ber->ber_ptr, sblen);
+ ber->ber_ptr += sblen;
+ } else {
+ sblen = 0;
+ }
+ ber->ber_len = tlen;
+
+ /* now fill the buffer. */
+
+ /* make sure length is reasonable */
+ if ( ber->ber_len == 0 ) {
+ sock_errset(ERANGE);
+ return LBER_DEFAULT;
+ }
+
+ if ( sb->sb_max_incoming && ber->ber_len > sb->sb_max_incoming ) {
+ ber_log_printf( LDAP_DEBUG_CONNS, ber->ber_debug,
+ "ber_get_next: sockbuf_max_incoming exceeded "
+ "(%ld > %ld)\n", ber->ber_len, sb->sb_max_incoming );
+ sock_errset(ERANGE);
+ return LBER_DEFAULT;
+ }
+
+ if (ber->ber_buf==NULL) {
+ ber_len_t l = ber->ber_rwptr - ber->ber_ptr;
+ /* ber->ber_ptr is always <= ber->ber->ber_rwptr.
+ * make sure ber->ber_len agrees with what we've
+ * already read.
+ */
+ if ( ber->ber_len < sblen + l ) {
+ sock_errset(ERANGE);
+ return LBER_DEFAULT;
+ }
+ ber->ber_buf = (char *) ber_memalloc_x( ber->ber_len + 1, ber->ber_memctx );
+ if (ber->ber_buf==NULL) {
+ return LBER_DEFAULT;
+ }
+ ber->ber_end = ber->ber_buf + ber->ber_len;
+ if (sblen) {
+ AC_MEMCPY(ber->ber_buf, buf, sblen);
+ }
+ if (l > 0) {
+ AC_MEMCPY(ber->ber_buf + sblen, ber->ber_ptr, l);
+ sblen += l;
+ }
+ *ber->ber_end = '\0';
+ ber->ber_ptr = ber->ber_buf;
+ ber->ber_usertag = 0;
+ if ((ber_len_t)sblen == ber->ber_len) {
+ goto done;
+ }
+ ber->ber_rwptr = ber->ber_buf + sblen;
+ }
+ }
+
+ if ((ber->ber_rwptr>=ber->ber_buf) && (ber->ber_rwptr<ber->ber_end)) {
+ ber_slen_t res;
+ ber_slen_t to_go;
+
+ to_go = ber->ber_end - ber->ber_rwptr;
+ /* unsigned/signed overflow */
+ if (to_go<0) return LBER_DEFAULT;
+
+ sock_errset(0);
+ res = ber_int_sb_read( sb, ber->ber_rwptr, to_go );
+ if (res<=0) return LBER_DEFAULT;
+ ber->ber_rwptr+=res;
+
+ if (res<to_go) {
+ sock_errset(EWOULDBLOCK);
+ return LBER_DEFAULT;
+ }
+done:
+ ber->ber_rwptr = NULL;
+ *len = ber->ber_len;
+ if ( ber->ber_debug ) {
+ ber_log_printf( LDAP_DEBUG_TRACE, ber->ber_debug,
+ "ber_get_next: tag 0x%lx len %ld contents:\n",
+ ber->ber_tag, ber->ber_len );
+ ber_log_dump( LDAP_DEBUG_BER, ber->ber_debug, ber, 1 );
+ }
+ return (ber->ber_tag);
+ }
+
+ /* invalid input */
+ return LBER_DEFAULT;
+}
+
+char *
+ber_start( BerElement* ber )
+{
+ return ber->ber_buf;
+}
+
+int
+ber_len( BerElement* ber )
+{
+ return ( ber->ber_end - ber->ber_buf );
+}
+
+int
+ber_ptrlen( BerElement* ber )
+{
+ return ( ber->ber_ptr - ber->ber_buf );
+}
+
+void
+ber_rewind ( BerElement * ber )
+{
+ ber->ber_rwptr = NULL;
+ ber->ber_sos_ptr = NULL;
+ ber->ber_end = ber->ber_ptr;
+ ber->ber_ptr = ber->ber_buf;
+#if 0 /* TODO: Should we add this? */
+ ber->ber_tag = LBER_DEFAULT;
+ ber->ber_usertag = 0;
+#endif
+}
+
+int
+ber_remaining( BerElement * ber )
+{
+ return ber_pvt_ber_remaining( ber );
+}
diff --git a/libraries/liblber/lber-int.h b/libraries/liblber/lber-int.h
new file mode 100644
index 0000000..14a1106
--- /dev/null
+++ b/libraries/liblber/lber-int.h
@@ -0,0 +1,225 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#ifndef _LBER_INT_H
+#define _LBER_INT_H
+
+#include "lber.h"
+#define LDAP_INT_DEBUG
+#include "ldap_log.h"
+#include "lber_pvt.h"
+#include "ldap_queue.h"
+
+LDAP_BEGIN_DECL
+
+typedef void (*BER_LOG_FN)(FILE *file,
+ const char *subsys, int level, const char *fmt, ... );
+
+LBER_V (BER_ERRNO_FN) ber_int_errno_fn;
+
+#ifdef LDAP_MEMORY_TRACE
+# ifndef LDAP_MEMORY_DEBUG
+# define LDAP_MEMORY_DEBUG 1
+# endif
+#endif
+
+#ifdef LDAP_MEMORY_DEBUG
+LBER_V (long) ber_int_meminuse;
+#endif
+#if defined(LDAP_MEMORY_DEBUG) && ((LDAP_MEMORY_DEBUG +0) & 2)
+# define LDAP_MEMORY_DEBUG_ASSERT assert
+#else
+# define LDAP_MEMORY_DEBUG_ASSERT(expr) ((void) 0)
+#endif
+
+struct lber_options {
+ short lbo_valid;
+ unsigned short lbo_options;
+ int lbo_debug;
+};
+
+LBER_F( int ) ber_pvt_log_output(
+ const char *subsystem,
+ int level,
+ const char *fmt, ... );
+
+#define LBER_UNINITIALIZED 0x0
+#define LBER_INITIALIZED 0x1
+#define LBER_VALID_BERELEMENT 0x2
+#define LBER_VALID_SOCKBUF 0x3
+
+LBER_V (struct lber_options) ber_int_options;
+#define ber_int_debug ber_int_options.lbo_debug
+
+/* Data encoded in ASN.1 BER format */
+struct berelement {
+ struct lber_options ber_opts;
+#define ber_valid ber_opts.lbo_valid
+#define ber_options ber_opts.lbo_options
+#define ber_debug ber_opts.lbo_debug
+
+ /*
+ * The members below, when not NULL/LBER_DEFAULT/etc, are:
+ * ber_buf Data buffer. Other pointers normally point into it.
+ * ber_rwptr Read/write cursor for Sockbuf I/O.
+ * ber_memctx Context passed to ber_memalloc() & co.
+ * When decoding data (reading it from the BerElement):
+ * ber_end End of BER data.
+ * ber_ptr Read cursor, except for 1st octet of tags.
+ * ber_tag 1st octet of next tag, saved from *ber_ptr when
+ * ber_ptr may be pointing at a tag and is >ber_buf.
+ * The octet *ber_ptr itself may get overwritten with
+ * a \0, to terminate the preceding element.
+ * When encoding data (writing it to the BerElement):
+ * ber_end End of allocated buffer - 1 (allowing a final \0).
+ * ber_ptr Last complete BER element (normally write cursor).
+ * ber_sos_ptr NULL or write cursor for incomplete sequence or set.
+ * ber_sos_inner offset(seq/set length octets) if ber_sos_ptr!=NULL.
+ * ber_tag Default tag for next ber_printf() element.
+ * ber_usertag Boolean set by ber_printf "!" if it sets ber_tag.
+ * ber_len Reused for ber_sos_inner.
+ * When output to a Sockbuf:
+ * ber_ptr End of encoded data to write.
+ * When input from a Sockbuf:
+ * See ber_get_next().
+ */
+
+ /* Do not change the order of these 3 fields! see ber_get_next */
+ ber_tag_t ber_tag;
+ ber_len_t ber_len;
+ ber_tag_t ber_usertag;
+
+ char *ber_buf;
+ char *ber_ptr;
+ char *ber_end;
+
+ char *ber_sos_ptr;
+# define ber_sos_inner ber_len /* reused for binary compat */
+
+ char *ber_rwptr;
+ void *ber_memctx;
+};
+#define LBER_VALID(ber) ((ber)->ber_valid==LBER_VALID_BERELEMENT)
+
+#define ber_pvt_ber_remaining(ber) ((ber)->ber_end - (ber)->ber_ptr)
+#define ber_pvt_ber_total(ber) ((ber)->ber_end - (ber)->ber_buf)
+#define ber_pvt_ber_write(ber) ((ber)->ber_ptr - (ber)->ber_buf)
+
+struct sockbuf {
+ struct lber_options sb_opts;
+ Sockbuf_IO_Desc *sb_iod; /* I/O functions */
+#define sb_valid sb_opts.lbo_valid
+#define sb_options sb_opts.lbo_options
+#define sb_debug sb_opts.lbo_debug
+ ber_socket_t sb_fd;
+ ber_len_t sb_max_incoming;
+ unsigned int sb_trans_needs_read:1;
+ unsigned int sb_trans_needs_write:1;
+#ifdef LDAP_PF_LOCAL_SENDMSG
+ char sb_ungetlen;
+ char sb_ungetbuf[8];
+#endif
+};
+
+#define SOCKBUF_VALID( sb ) ( (sb)->sb_valid == LBER_VALID_SOCKBUF )
+
+
+/*
+ * decode.c, encode.c
+ */
+
+/* Simplest OID max-DER-component to implement in both decode and encode */
+#define LBER_OID_COMPONENT_MAX ((unsigned long)-1 - 128)
+
+
+/*
+ * io.c
+ */
+LBER_F( int )
+ber_realloc LDAP_P((
+ BerElement *ber,
+ ber_len_t len ));
+
+LBER_F (char *) ber_start LDAP_P(( BerElement * ));
+LBER_F (int) ber_len LDAP_P(( BerElement * ));
+LBER_F (int) ber_ptrlen LDAP_P(( BerElement * ));
+LBER_F (void) ber_rewind LDAP_P(( BerElement * ));
+
+/*
+ * bprint.c
+ */
+#define ber_log_printf ber_pvt_log_printf
+
+LBER_F( int )
+ber_log_bprint LDAP_P((
+ int errlvl,
+ int loglvl,
+ const char *data,
+ ber_len_t len ));
+
+LBER_F( int )
+ber_log_dump LDAP_P((
+ int errlvl,
+ int loglvl,
+ BerElement *ber,
+ int inout ));
+
+LBER_V (BER_LOG_FN) ber_int_log_proc;
+LBER_V (FILE *) ber_pvt_err_file;
+
+/* memory.c */
+ /* simple macros to realloc for now */
+LBER_V (BerMemoryFunctions *) ber_int_memory_fns;
+LBER_F (char *) ber_strndup( LDAP_CONST char *, ber_len_t );
+LBER_F (char *) ber_strndup_x( LDAP_CONST char *, ber_len_t, void *ctx );
+
+#define LBER_MALLOC(s) ber_memalloc((s))
+#define LBER_CALLOC(n,s) ber_memcalloc((n),(s))
+#define LBER_REALLOC(p,s) ber_memrealloc((p),(s))
+#define LBER_FREE(p) ber_memfree((p))
+#define LBER_VFREE(v) ber_memvfree((void**)(v))
+#define LBER_STRDUP(s) ber_strdup((s))
+#define LBER_STRNDUP(s,l) ber_strndup((s),(l))
+
+/* sockbuf.c */
+
+LBER_F( int )
+ber_int_sb_init LDAP_P(( Sockbuf *sb ));
+
+LBER_F( int )
+ber_int_sb_close LDAP_P(( Sockbuf *sb ));
+
+LBER_F( int )
+ber_int_sb_destroy LDAP_P(( Sockbuf *sb ));
+
+LBER_F( ber_slen_t )
+ber_int_sb_read LDAP_P(( Sockbuf *sb, void *buf, ber_len_t len ));
+
+LBER_F( ber_slen_t )
+ber_int_sb_write LDAP_P(( Sockbuf *sb, void *buf, ber_len_t len ));
+
+LDAP_END_DECL
+
+#endif /* _LBER_INT_H */
diff --git a/libraries/liblber/lber.pc.in b/libraries/liblber/lber.pc.in
new file mode 100644
index 0000000..772feb0
--- /dev/null
+++ b/libraries/liblber/lber.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+includedir=@includedir@
+libdir=@libdir@
+
+Name: lber (@PACKAGE@)
+Description: OpenLDAP Lightweight ASN.1 Basic Encoding Rules library
+URL: https://www.openldap.org
+Version: @VERSION@
+Cflags: -I${includedir}
+Libs: -L${libdir} -llber
+Libs.private: @LIBS@
diff --git a/libraries/liblber/liblber.vers.in b/libraries/liblber/liblber.vers.in
new file mode 100644
index 0000000..104b860
--- /dev/null
+++ b/libraries/liblber/liblber.vers.in
@@ -0,0 +1,17 @@
+HIDDEN
+{
+ local:
+ __*;
+ _rest*;
+ _save*;
+};
+
+OPENLDAP_@OPENLDAP_LIBRELEASE@
+{
+ global:
+ ber_*;
+ der_alloc*;
+ lutil_*;
+ local: *;
+};
+
diff --git a/libraries/liblber/memory.c b/libraries/liblber/memory.c
new file mode 100644
index 0000000..c508338
--- /dev/null
+++ b/libraries/liblber/memory.c
@@ -0,0 +1,825 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <ac/stdlib.h>
+#include <ac/string.h>
+
+#include "lber-int.h"
+
+#ifdef LDAP_MEMORY_TRACE
+#include <stdio.h>
+#endif
+
+#ifdef LDAP_MEMORY_DEBUG
+/*
+ * LDAP_MEMORY_DEBUG should only be enabled for the purposes of
+ * debugging memory management within OpenLDAP libraries and slapd.
+ *
+ * It should only be enabled by an experienced developer as it causes
+ * the inclusion of numerous assert()'s, many of which may be triggered
+ * by a perfectly valid program. If LDAP_MEMORY_DEBUG & 2 is true,
+ * that includes asserts known to break both slapd and current clients.
+ *
+ * The code behind this macro is subject to change as needed to
+ * support this testing.
+ */
+
+struct ber_mem_hdr {
+ ber_int_t bm_top; /* Pattern to detect buf overrun from prev buffer */
+ ber_int_t bm_length; /* Length of user allocated area */
+#ifdef LDAP_MEMORY_TRACE
+ ber_int_t bm_sequence; /* Allocation sequence number */
+#endif
+ union bmu_align_u { /* Force alignment, pattern to detect back clobber */
+ ber_len_t bmu_len_t;
+ ber_tag_t bmu_tag_t;
+ ber_int_t bmu_int_t;
+
+ size_t bmu_size_t;
+ void * bmu_voidp;
+ double bmu_double;
+ long bmu_long;
+ long (*bmu_funcp)( double );
+ unsigned char bmu_char[4];
+ } ber_align;
+#define bm_junk ber_align.bmu_len_t
+#define bm_data ber_align.bmu_char[1]
+#define bm_char ber_align.bmu_char
+};
+
+/* Pattern at top of allocated space */
+#define LBER_MEM_JUNK ((ber_int_t) 0xdeaddada)
+
+static const struct ber_mem_hdr ber_int_mem_hdr = { LBER_MEM_JUNK };
+
+/* Note sequence and ber_int_meminuse are counters, but are not
+ * thread safe. If you want to use these values for multithreaded applications,
+ * you must put mutexes around them, otherwise they will have incorrect values.
+ * When debugging, if you sort the debug output, the sequence number will
+ * put allocations/frees together. It is then a simple matter to write a script
+ * to find any allocations that don't have a buffer free function.
+ */
+long ber_int_meminuse = 0;
+#ifdef LDAP_MEMORY_TRACE
+static ber_int_t sequence = 0;
+#endif
+
+/* Pattern placed just before user data */
+static unsigned char toppattern[4] = { 0xde, 0xad, 0xba, 0xde };
+/* Pattern placed just after user data */
+static unsigned char endpattern[4] = { 0xd1, 0xed, 0xde, 0xca };
+
+#define mbu_len sizeof(ber_int_mem_hdr.ber_align)
+
+/* Test if pattern placed just before user data is good */
+#define testdatatop(val) ( \
+ *(val->bm_char+mbu_len-4)==toppattern[0] && \
+ *(val->bm_char+mbu_len-3)==toppattern[1] && \
+ *(val->bm_char+mbu_len-2)==toppattern[2] && \
+ *(val->bm_char+mbu_len-1)==toppattern[3] )
+
+/* Place pattern just before user data */
+#define setdatatop(val) *(val->bm_char+mbu_len-4)=toppattern[0]; \
+ *(val->bm_char+mbu_len-3)=toppattern[1]; \
+ *(val->bm_char+mbu_len-2)=toppattern[2]; \
+ *(val->bm_char+mbu_len-1)=toppattern[3];
+
+/* Test if pattern placed just after user data is good */
+#define testend(val) ( *((unsigned char *)val+0)==endpattern[0] && \
+ *((unsigned char *)val+1)==endpattern[1] && \
+ *((unsigned char *)val+2)==endpattern[2] && \
+ *((unsigned char *)val+3)==endpattern[3] )
+
+/* Place pattern just after user data */
+#define setend(val) *((unsigned char *)val+0)=endpattern[0]; \
+ *((unsigned char *)val+1)=endpattern[1]; \
+ *((unsigned char *)val+2)=endpattern[2]; \
+ *((unsigned char *)val+3)=endpattern[3];
+
+#define BER_MEM_BADADDR ((void *) &ber_int_mem_hdr.bm_data)
+#define BER_MEM_VALID(p) do { \
+ assert( (p) != BER_MEM_BADADDR ); \
+ assert( (p) != (void *) &ber_int_mem_hdr ); \
+ } while(0)
+
+#else
+#define BER_MEM_VALID(p) /* no-op */
+#endif
+
+BerMemoryFunctions *ber_int_memory_fns = NULL;
+
+void
+ber_memfree_x( void *p, void *ctx )
+{
+ if( p == NULL ) {
+ return;
+ }
+
+ BER_MEM_VALID( p );
+
+ if( ber_int_memory_fns == NULL || ctx == NULL ) {
+#ifdef LDAP_MEMORY_DEBUG
+ struct ber_mem_hdr *mh = (struct ber_mem_hdr *)
+ ((char *)p - sizeof(struct ber_mem_hdr));
+ assert( mh->bm_top == LBER_MEM_JUNK);
+ assert( testdatatop( mh));
+ assert( testend( (char *)&mh[1] + mh->bm_length) );
+ ber_int_meminuse -= mh->bm_length;
+
+#ifdef LDAP_MEMORY_TRACE
+ fprintf(stderr, "0x%08lx 0x%08lx -f- %ld ber_memfree %ld\n",
+ (long)mh->bm_sequence, (long)mh, (long)mh->bm_length,
+ ber_int_meminuse);
+#endif
+ /* Fill the free space with poison */
+ memset( mh, 0xff, mh->bm_length + sizeof(struct ber_mem_hdr) + sizeof(ber_int_t));
+ free( mh );
+#else
+ free( p );
+#endif
+ return;
+ }
+
+ assert( ber_int_memory_fns->bmf_free != 0 );
+
+ (*ber_int_memory_fns->bmf_free)( p, ctx );
+}
+
+void
+ber_memfree( void *p )
+{
+ ber_memfree_x(p, NULL);
+}
+
+void
+ber_memvfree_x( void **vec, void *ctx )
+{
+ int i;
+
+ if( vec == NULL ) {
+ return;
+ }
+
+ BER_MEM_VALID( vec );
+
+ for ( i = 0; vec[i] != NULL; i++ ) {
+ ber_memfree_x( vec[i], ctx );
+ }
+
+ ber_memfree_x( vec, ctx );
+}
+
+void
+ber_memvfree( void **vec )
+{
+ ber_memvfree_x( vec, NULL );
+}
+
+void *
+ber_memalloc_x( ber_len_t s, void *ctx )
+{
+ void *new;
+
+ if( s == 0 ) {
+ LDAP_MEMORY_DEBUG_ASSERT( s != 0 );
+ return NULL;
+ }
+
+ if( ber_int_memory_fns == NULL || ctx == NULL ) {
+#ifdef LDAP_MEMORY_DEBUG
+ new = malloc(s + sizeof(struct ber_mem_hdr) + sizeof( ber_int_t));
+ if( new )
+ {
+ struct ber_mem_hdr *mh = new;
+ mh->bm_top = LBER_MEM_JUNK;
+ mh->bm_length = s;
+ setdatatop( mh);
+ setend( (char *)&mh[1] + mh->bm_length );
+
+ ber_int_meminuse += mh->bm_length; /* Count mem inuse */
+
+#ifdef LDAP_MEMORY_TRACE
+ mh->bm_sequence = sequence++;
+ fprintf(stderr, "0x%08lx 0x%08lx -a- %ld ber_memalloc %ld\n",
+ (long)mh->bm_sequence, (long)mh, (long)mh->bm_length,
+ ber_int_meminuse);
+#endif
+ /* poison new memory */
+ memset( (char *)&mh[1], 0xff, s);
+
+ BER_MEM_VALID( &mh[1] );
+ new = &mh[1];
+ }
+#else
+ new = malloc( s );
+#endif
+ } else {
+ new = (*ber_int_memory_fns->bmf_malloc)( s, ctx );
+ }
+
+ if( new == NULL ) {
+ ber_errno = LBER_ERROR_MEMORY;
+ }
+
+ return new;
+}
+
+void *
+ber_memalloc( ber_len_t s )
+{
+ return ber_memalloc_x( s, NULL );
+}
+
+void *
+ber_memcalloc_x( ber_len_t n, ber_len_t s, void *ctx )
+{
+ void *new;
+
+ if( n == 0 || s == 0 ) {
+ LDAP_MEMORY_DEBUG_ASSERT( n != 0 && s != 0);
+ return NULL;
+ }
+
+ if( ber_int_memory_fns == NULL || ctx == NULL ) {
+#ifdef LDAP_MEMORY_DEBUG
+ new = n < (-sizeof(struct ber_mem_hdr) - sizeof(ber_int_t)) / s
+ ? calloc(1, n*s + sizeof(struct ber_mem_hdr) + sizeof(ber_int_t))
+ : NULL;
+ if( new )
+ {
+ struct ber_mem_hdr *mh = new;
+
+ mh->bm_top = LBER_MEM_JUNK;
+ mh->bm_length = n*s;
+ setdatatop( mh);
+ setend( (char *)&mh[1] + mh->bm_length );
+
+ ber_int_meminuse += mh->bm_length;
+
+#ifdef LDAP_MEMORY_TRACE
+ mh->bm_sequence = sequence++;
+ fprintf(stderr, "0x%08lx 0x%08lx -a- %ld ber_memcalloc %ld\n",
+ (long)mh->bm_sequence, (long)mh, (long)mh->bm_length,
+ ber_int_meminuse);
+#endif
+ BER_MEM_VALID( &mh[1] );
+ new = &mh[1];
+ }
+#else
+ new = calloc( n, s );
+#endif
+
+ } else {
+ new = (*ber_int_memory_fns->bmf_calloc)( n, s, ctx );
+ }
+
+ if( new == NULL ) {
+ ber_errno = LBER_ERROR_MEMORY;
+ }
+
+ return new;
+}
+
+void *
+ber_memcalloc( ber_len_t n, ber_len_t s )
+{
+ return ber_memcalloc_x( n, s, NULL );
+}
+
+void *
+ber_memrealloc_x( void* p, ber_len_t s, void *ctx )
+{
+ void *new = NULL;
+
+ /* realloc(NULL,s) -> malloc(s) */
+ if( p == NULL ) {
+ return ber_memalloc_x( s, ctx );
+ }
+
+ /* realloc(p,0) -> free(p) */
+ if( s == 0 ) {
+ ber_memfree_x( p, ctx );
+ return NULL;
+ }
+
+ BER_MEM_VALID( p );
+
+ if( ber_int_memory_fns == NULL || ctx == NULL ) {
+#ifdef LDAP_MEMORY_DEBUG
+ ber_int_t oldlen;
+ struct ber_mem_hdr *mh = (struct ber_mem_hdr *)
+ ((char *)p - sizeof(struct ber_mem_hdr));
+ assert( mh->bm_top == LBER_MEM_JUNK);
+ assert( testdatatop( mh));
+ assert( testend( (char *)&mh[1] + mh->bm_length) );
+ oldlen = mh->bm_length;
+
+ p = realloc( mh, s + sizeof(struct ber_mem_hdr) + sizeof(ber_int_t) );
+ if( p == NULL ) {
+ ber_errno = LBER_ERROR_MEMORY;
+ return NULL;
+ }
+
+ mh = p;
+ mh->bm_length = s;
+ setend( (char *)&mh[1] + mh->bm_length );
+ if( s > oldlen ) {
+ /* poison any new memory */
+ memset( (char *)&mh[1] + oldlen, 0xff, s - oldlen);
+ }
+
+ assert( mh->bm_top == LBER_MEM_JUNK);
+ assert( testdatatop( mh));
+
+ ber_int_meminuse += s - oldlen;
+#ifdef LDAP_MEMORY_TRACE
+ fprintf(stderr, "0x%08lx 0x%08lx -a- %ld ber_memrealloc %ld\n",
+ (long)mh->bm_sequence, (long)mh, (long)mh->bm_length,
+ ber_int_meminuse);
+#endif
+ BER_MEM_VALID( &mh[1] );
+ return &mh[1];
+#else
+ new = realloc( p, s );
+#endif
+ } else {
+ new = (*ber_int_memory_fns->bmf_realloc)( p, s, ctx );
+ }
+
+ if( new == NULL ) {
+ ber_errno = LBER_ERROR_MEMORY;
+ }
+
+ return new;
+}
+
+void *
+ber_memrealloc( void* p, ber_len_t s )
+{
+ return ber_memrealloc_x( p, s, NULL );
+}
+
+void
+ber_bvfree_x( struct berval *bv, void *ctx )
+{
+ if( bv == NULL ) {
+ return;
+ }
+
+ BER_MEM_VALID( bv );
+
+ if ( bv->bv_val != NULL ) {
+ ber_memfree_x( bv->bv_val, ctx );
+ }
+
+ ber_memfree_x( (char *) bv, ctx );
+}
+
+void
+ber_bvfree( struct berval *bv )
+{
+ ber_bvfree_x( bv, NULL );
+}
+
+void
+ber_bvecfree_x( struct berval **bv, void *ctx )
+{
+ int i;
+
+ if( bv == NULL ) {
+ return;
+ }
+
+ BER_MEM_VALID( bv );
+
+ /* count elements */
+ for ( i = 0; bv[i] != NULL; i++ ) ;
+
+ /* free in reverse order */
+ for ( i--; i >= 0; i-- ) {
+ ber_bvfree_x( bv[i], ctx );
+ }
+
+ ber_memfree_x( (char *) bv, ctx );
+}
+
+void
+ber_bvecfree( struct berval **bv )
+{
+ ber_bvecfree_x( bv, NULL );
+}
+
+int
+ber_bvecadd_x( struct berval ***bvec, struct berval *bv, void *ctx )
+{
+ ber_len_t i;
+ struct berval **new;
+
+ if( *bvec == NULL ) {
+ if( bv == NULL ) {
+ /* nothing to add */
+ return 0;
+ }
+
+ *bvec = ber_memalloc_x( 2 * sizeof(struct berval *), ctx );
+
+ if( *bvec == NULL ) {
+ return -1;
+ }
+
+ (*bvec)[0] = bv;
+ (*bvec)[1] = NULL;
+
+ return 1;
+ }
+
+ BER_MEM_VALID( bvec );
+
+ /* count entries */
+ for ( i = 0; (*bvec)[i] != NULL; i++ ) {
+ /* EMPTY */;
+ }
+
+ if( bv == NULL ) {
+ return i;
+ }
+
+ new = ber_memrealloc_x( *bvec, (i+2) * sizeof(struct berval *), ctx);
+
+ if( new == NULL ) {
+ return -1;
+ }
+
+ *bvec = new;
+
+ (*bvec)[i++] = bv;
+ (*bvec)[i] = NULL;
+
+ return i;
+}
+
+int
+ber_bvecadd( struct berval ***bvec, struct berval *bv )
+{
+ return ber_bvecadd_x( bvec, bv, NULL );
+}
+
+struct berval *
+ber_dupbv_x(
+ struct berval *dst, struct berval *src, void *ctx )
+{
+ struct berval *new, tmp;
+
+ if( src == NULL ) {
+ ber_errno = LBER_ERROR_PARAM;
+ return NULL;
+ }
+
+ if ( dst ) {
+ new = &tmp;
+ } else {
+ if(( new = ber_memalloc_x( sizeof(struct berval), ctx )) == NULL ) {
+ return NULL;
+ }
+ }
+
+ if ( src->bv_val == NULL ) {
+ new->bv_val = NULL;
+ new->bv_len = 0;
+ } else {
+
+ if(( new->bv_val = ber_memalloc_x( src->bv_len + 1, ctx )) == NULL ) {
+ if ( !dst )
+ ber_memfree_x( new, ctx );
+ return NULL;
+ }
+
+ AC_MEMCPY( new->bv_val, src->bv_val, src->bv_len );
+ new->bv_val[src->bv_len] = '\0';
+ new->bv_len = src->bv_len;
+ }
+
+ if ( dst ) {
+ *dst = *new;
+ new = dst;
+ }
+
+ return new;
+}
+
+struct berval *
+ber_dupbv(
+ struct berval *dst, struct berval *src )
+{
+ return ber_dupbv_x( dst, src, NULL );
+}
+
+struct berval *
+ber_bvdup(
+ struct berval *src )
+{
+ return ber_dupbv_x( NULL, src, NULL );
+}
+
+struct berval *
+ber_str2bv_x(
+ LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv,
+ void *ctx)
+{
+ struct berval *new;
+
+ if( s == NULL ) {
+ ber_errno = LBER_ERROR_PARAM;
+ return NULL;
+ }
+
+ if( bv ) {
+ new = bv;
+ } else {
+ if(( new = ber_memalloc_x( sizeof(struct berval), ctx )) == NULL ) {
+ return NULL;
+ }
+ }
+
+ new->bv_len = len ? len : strlen( s );
+ if ( dup ) {
+ if ( (new->bv_val = ber_memalloc_x( new->bv_len+1, ctx )) == NULL ) {
+ if ( !bv )
+ ber_memfree_x( new, ctx );
+ return NULL;
+ }
+
+ AC_MEMCPY( new->bv_val, s, new->bv_len );
+ new->bv_val[new->bv_len] = '\0';
+ } else {
+ new->bv_val = (char *) s;
+ }
+
+ return( new );
+}
+
+struct berval *
+ber_str2bv(
+ LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv)
+{
+ return ber_str2bv_x( s, len, dup, bv, NULL );
+}
+
+struct berval *
+ber_mem2bv_x(
+ LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv,
+ void *ctx)
+{
+ struct berval *new;
+
+ if( s == NULL ) {
+ ber_errno = LBER_ERROR_PARAM;
+ return NULL;
+ }
+
+ if( bv ) {
+ new = bv;
+ } else {
+ if(( new = ber_memalloc_x( sizeof(struct berval), ctx )) == NULL ) {
+ return NULL;
+ }
+ }
+
+ new->bv_len = len;
+ if ( dup ) {
+ if ( (new->bv_val = ber_memalloc_x( new->bv_len+1, ctx )) == NULL ) {
+ if ( !bv ) {
+ ber_memfree_x( new, ctx );
+ }
+ return NULL;
+ }
+
+ AC_MEMCPY( new->bv_val, s, new->bv_len );
+ new->bv_val[new->bv_len] = '\0';
+ } else {
+ new->bv_val = (char *) s;
+ }
+
+ return( new );
+}
+
+struct berval *
+ber_mem2bv(
+ LDAP_CONST char *s, ber_len_t len, int dup, struct berval *bv)
+{
+ return ber_mem2bv_x( s, len, dup, bv, NULL );
+}
+
+char *
+ber_strdup_x( LDAP_CONST char *s, void *ctx )
+{
+ char *p;
+ size_t len;
+
+#ifdef LDAP_MEMORY_DEBUG
+ assert(s != NULL); /* bv damn better point to something */
+#endif
+
+ if( s == NULL ) {
+ ber_errno = LBER_ERROR_PARAM;
+ return NULL;
+ }
+
+ len = strlen( s ) + 1;
+ if ( (p = ber_memalloc_x( len, ctx )) != NULL ) {
+ AC_MEMCPY( p, s, len );
+ }
+
+ return p;
+}
+
+char *
+ber_strdup( LDAP_CONST char *s )
+{
+ return ber_strdup_x( s, NULL );
+}
+
+ber_len_t
+ber_strnlen( LDAP_CONST char *s, ber_len_t len )
+{
+ ber_len_t l;
+
+ for ( l = 0; l < len && s[l] != '\0'; l++ ) ;
+
+ return l;
+}
+
+char *
+ber_strndup_x( LDAP_CONST char *s, ber_len_t l, void *ctx )
+{
+ char *p;
+ size_t len;
+
+#ifdef LDAP_MEMORY_DEBUG
+ assert(s != NULL); /* bv damn better point to something */
+#endif
+
+ if( s == NULL ) {
+ ber_errno = LBER_ERROR_PARAM;
+ return NULL;
+ }
+
+ len = ber_strnlen( s, l );
+ if ( (p = ber_memalloc_x( len + 1, ctx )) != NULL ) {
+ AC_MEMCPY( p, s, len );
+ p[len] = '\0';
+ }
+
+ return p;
+}
+
+char *
+ber_strndup( LDAP_CONST char *s, ber_len_t l )
+{
+ return ber_strndup_x( s, l, NULL );
+}
+
+/*
+ * dst is resized as required by src and the value of src is copied into dst
+ * dst->bv_val must be NULL (and dst->bv_len must be 0), or it must be
+ * alloc'ed with the context ctx
+ */
+struct berval *
+ber_bvreplace_x( struct berval *dst, LDAP_CONST struct berval *src, void *ctx )
+{
+ assert( dst != NULL );
+ assert( !BER_BVISNULL( src ) );
+
+ if ( BER_BVISNULL( dst ) || dst->bv_len < src->bv_len ) {
+ dst->bv_val = ber_memrealloc_x( dst->bv_val, src->bv_len + 1, ctx );
+ }
+
+ AC_MEMCPY( dst->bv_val, src->bv_val, src->bv_len + 1 );
+ dst->bv_len = src->bv_len;
+
+ return dst;
+}
+
+struct berval *
+ber_bvreplace( struct berval *dst, LDAP_CONST struct berval *src )
+{
+ return ber_bvreplace_x( dst, src, NULL );
+}
+
+void
+ber_bvarray_free_x( BerVarray a, void *ctx )
+{
+ int i;
+
+ if (a) {
+ BER_MEM_VALID( a );
+
+ /* count elements */
+ for (i=0; a[i].bv_val; i++) ;
+
+ /* free in reverse order */
+ for (i--; i>=0; i--) {
+ ber_memfree_x(a[i].bv_val, ctx);
+ }
+
+ ber_memfree_x(a, ctx);
+ }
+}
+
+void
+ber_bvarray_free( BerVarray a )
+{
+ ber_bvarray_free_x(a, NULL);
+}
+
+int
+ber_bvarray_dup_x( BerVarray *dst, BerVarray src, void *ctx )
+{
+ int i, j;
+ BerVarray new;
+
+ if ( !src ) {
+ *dst = NULL;
+ return 0;
+ }
+
+ for (i=0; !BER_BVISNULL( &src[i] ); i++) ;
+ new = ber_memalloc_x(( i+1 ) * sizeof(BerValue), ctx );
+ if ( !new )
+ return -1;
+ for (j=0; j<i; j++) {
+ ber_dupbv_x( &new[j], &src[j], ctx );
+ if ( BER_BVISNULL( &new[j] )) {
+ ber_bvarray_free_x( new, ctx );
+ return -1;
+ }
+ }
+ BER_BVZERO( &new[j] );
+ *dst = new;
+ return 0;
+}
+
+int
+ber_bvarray_add_x( BerVarray *a, BerValue *bv, void *ctx )
+{
+ int n;
+
+ if ( *a == NULL ) {
+ if (bv == NULL) {
+ return 0;
+ }
+ n = 0;
+
+ *a = (BerValue *) ber_memalloc_x( 2 * sizeof(BerValue), ctx );
+ if ( *a == NULL ) {
+ return -1;
+ }
+
+ } else {
+ BerVarray atmp;
+ BER_MEM_VALID( a );
+
+ for ( n = 0; *a != NULL && (*a)[n].bv_val != NULL; n++ ) {
+ ; /* just count them */
+ }
+
+ if (bv == NULL) {
+ return n;
+ }
+
+ atmp = (BerValue *) ber_memrealloc_x( (char *) *a,
+ (n + 2) * sizeof(BerValue), ctx );
+
+ if( atmp == NULL ) {
+ return -1;
+ }
+
+ *a = atmp;
+ }
+
+ (*a)[n++] = *bv;
+ (*a)[n].bv_val = NULL;
+ (*a)[n].bv_len = 0;
+
+ return n;
+}
+
+int
+ber_bvarray_add( BerVarray *a, BerValue *bv )
+{
+ return ber_bvarray_add_x( a, bv, NULL );
+}
diff --git a/libraries/liblber/nt_err.c b/libraries/liblber/nt_err.c
new file mode 100644
index 0000000..5425356
--- /dev/null
+++ b/libraries/liblber/nt_err.c
@@ -0,0 +1,96 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#ifdef HAVE_WINSOCK2
+#include <winsock2.h>
+#elif defined(HAVE_WINSOCK)
+#include <winsock.h>
+#endif /* HAVE_WINSOCK(2) */
+
+#define LBER_RETSTR( x ) case x: return #x;
+
+char *ber_pvt_wsa_err2string( int err )
+{
+ switch( err ) {
+ LBER_RETSTR( WSAEINTR )
+ LBER_RETSTR( WSAEBADF )
+ LBER_RETSTR( WSAEACCES )
+ LBER_RETSTR( WSAEFAULT )
+ LBER_RETSTR( WSAEINVAL )
+ LBER_RETSTR( WSAEMFILE )
+ LBER_RETSTR( WSAEWOULDBLOCK )
+ LBER_RETSTR( WSAEINPROGRESS )
+ LBER_RETSTR( WSAEALREADY )
+ LBER_RETSTR( WSAENOTSOCK )
+ LBER_RETSTR( WSAEDESTADDRREQ )
+ LBER_RETSTR( WSAEMSGSIZE )
+ LBER_RETSTR( WSAEPROTOTYPE )
+ LBER_RETSTR( WSAENOPROTOOPT )
+ LBER_RETSTR( WSAEPROTONOSUPPORT )
+ LBER_RETSTR( WSAESOCKTNOSUPPORT )
+ LBER_RETSTR( WSAEOPNOTSUPP )
+ LBER_RETSTR( WSAEPFNOSUPPORT )
+ LBER_RETSTR( WSAEAFNOSUPPORT )
+ LBER_RETSTR( WSAEADDRINUSE )
+ LBER_RETSTR( WSAEADDRNOTAVAIL )
+ LBER_RETSTR( WSAENETDOWN )
+ LBER_RETSTR( WSAENETUNREACH )
+ LBER_RETSTR( WSAENETRESET )
+ LBER_RETSTR( WSAECONNABORTED )
+ LBER_RETSTR( WSAECONNRESET )
+ LBER_RETSTR( WSAENOBUFS )
+ LBER_RETSTR( WSAEISCONN )
+ LBER_RETSTR( WSAENOTCONN )
+ LBER_RETSTR( WSAESHUTDOWN )
+ LBER_RETSTR( WSAETOOMANYREFS )
+ LBER_RETSTR( WSAETIMEDOUT )
+ LBER_RETSTR( WSAECONNREFUSED )
+ LBER_RETSTR( WSAELOOP )
+ LBER_RETSTR( WSAENAMETOOLONG )
+ LBER_RETSTR( WSAEHOSTDOWN )
+ LBER_RETSTR( WSAEHOSTUNREACH )
+ LBER_RETSTR( WSAENOTEMPTY )
+ LBER_RETSTR( WSAEPROCLIM )
+ LBER_RETSTR( WSAEUSERS )
+ LBER_RETSTR( WSAEDQUOT )
+ LBER_RETSTR( WSAESTALE )
+ LBER_RETSTR( WSAEREMOTE )
+ LBER_RETSTR( WSASYSNOTREADY )
+ LBER_RETSTR( WSAVERNOTSUPPORTED )
+ LBER_RETSTR( WSANOTINITIALISED )
+ LBER_RETSTR( WSAEDISCON )
+
+#ifdef HAVE_WINSOCK2
+ LBER_RETSTR( WSAENOMORE )
+ LBER_RETSTR( WSAECANCELLED )
+ LBER_RETSTR( WSAEINVALIDPROCTABLE )
+ LBER_RETSTR( WSAEINVALIDPROVIDER )
+ LBER_RETSTR( WSASYSCALLFAILURE )
+ LBER_RETSTR( WSASERVICE_NOT_FOUND )
+ LBER_RETSTR( WSATYPE_NOT_FOUND )
+ LBER_RETSTR( WSA_E_NO_MORE )
+ LBER_RETSTR( WSA_E_CANCELLED )
+ LBER_RETSTR( WSAEREFUSED )
+#endif /* HAVE_WINSOCK2 */
+
+ LBER_RETSTR( WSAHOST_NOT_FOUND )
+ LBER_RETSTR( WSATRY_AGAIN )
+ LBER_RETSTR( WSANO_RECOVERY )
+ LBER_RETSTR( WSANO_DATA )
+ }
+ return "unknown WSA error";
+}
diff --git a/libraries/liblber/options.c b/libraries/liblber/options.c
new file mode 100644
index 0000000..a31c8ec
--- /dev/null
+++ b/libraries/liblber/options.c
@@ -0,0 +1,237 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/stdarg.h>
+#include "lber-int.h"
+
+char ber_pvt_opt_on; /* used to get a non-NULL address for *_OPT_ON */
+
+struct lber_options ber_int_options = {
+ LBER_UNINITIALIZED, 0, 0 };
+
+static BerMemoryFunctions ber_int_memory_fns_datum;
+
+int
+ber_get_option(
+ void *item,
+ int option,
+ void *outvalue)
+{
+ const BerElement *ber;
+ const Sockbuf *sb;
+
+ if(outvalue == NULL) {
+ /* no place to get to */
+ ber_errno = LBER_ERROR_PARAM;
+ return LBER_OPT_ERROR;
+ }
+
+ if(item == NULL) {
+ switch ( option ) {
+ case LBER_OPT_BER_DEBUG:
+ * (int *) outvalue = ber_int_debug;
+ return LBER_OPT_SUCCESS;
+
+ case LBER_OPT_MEMORY_INUSE:
+ /* The memory inuse is a global variable on kernel implementations.
+ * This means that memory debug is shared by all LDAP processes
+ * so for this variable to have much meaning, only one LDAP process
+ * should be running and memory inuse should be initialized to zero
+ * using the lber_set_option() function during startup.
+ * The counter is not accurate for multithreaded ldap applications.
+ */
+#ifdef LDAP_MEMORY_DEBUG
+ * (int *) outvalue = ber_int_meminuse;
+ return LBER_OPT_SUCCESS;
+#else
+ return LBER_OPT_ERROR;
+#endif
+
+ case LBER_OPT_LOG_PRINT_FILE:
+ *((FILE**)outvalue) = (FILE*)ber_pvt_err_file;
+ return LBER_OPT_SUCCESS;
+
+ case LBER_OPT_LOG_PRINT_FN:
+ *(BER_LOG_PRINT_FN *)outvalue = ber_pvt_log_print;
+ return LBER_OPT_SUCCESS;
+ }
+
+ ber_errno = LBER_ERROR_PARAM;
+ return LBER_OPT_ERROR;
+ }
+
+ ber = item;
+ sb = item;
+
+ switch(option) {
+ case LBER_OPT_BER_OPTIONS:
+ assert( LBER_VALID( ber ) );
+ * (int *) outvalue = ber->ber_options;
+ return LBER_OPT_SUCCESS;
+
+ case LBER_OPT_BER_DEBUG:
+ assert( LBER_VALID( ber ) );
+ * (int *) outvalue = ber->ber_debug;
+ return LBER_OPT_SUCCESS;
+
+ case LBER_OPT_BER_REMAINING_BYTES:
+ assert( LBER_VALID( ber ) );
+ *((ber_len_t *) outvalue) = ber_pvt_ber_remaining(ber);
+ return LBER_OPT_SUCCESS;
+
+ case LBER_OPT_BER_TOTAL_BYTES:
+ assert( LBER_VALID( ber ) );
+ *((ber_len_t *) outvalue) = ber_pvt_ber_total(ber);
+ return LBER_OPT_SUCCESS;
+
+ case LBER_OPT_BER_BYTES_TO_WRITE:
+ assert( LBER_VALID( ber ) );
+ *((ber_len_t *) outvalue) = ber_pvt_ber_write(ber);
+ return LBER_OPT_SUCCESS;
+
+ case LBER_OPT_BER_MEMCTX:
+ assert( LBER_VALID( ber ) );
+ *((void **) outvalue) = ber->ber_memctx;
+ return LBER_OPT_SUCCESS;
+
+ default:
+ /* bad param */
+ ber_errno = LBER_ERROR_PARAM;
+ break;
+ }
+
+ return LBER_OPT_ERROR;
+}
+
+int
+ber_set_option(
+ void *item,
+ int option,
+ LDAP_CONST void *invalue)
+{
+ BerElement *ber;
+ Sockbuf *sb;
+
+ if(invalue == NULL) {
+ /* no place to set from */
+ ber_errno = LBER_ERROR_PARAM;
+ return LBER_OPT_ERROR;
+ }
+
+ if(item == NULL) {
+ switch ( option ) {
+ case LBER_OPT_BER_DEBUG:
+ ber_int_debug = * (const int *) invalue;
+ return LBER_OPT_SUCCESS;
+
+ case LBER_OPT_LOG_PRINT_FN:
+ ber_pvt_log_print = (BER_LOG_PRINT_FN) invalue;
+ return LBER_OPT_SUCCESS;
+
+ case LBER_OPT_LOG_PRINT_FILE:
+ ber_pvt_err_file = (void *) invalue;
+ return LBER_OPT_SUCCESS;
+
+ case LBER_OPT_MEMORY_INUSE:
+ /* The memory inuse is a global variable on kernel implementations.
+ * This means that memory debug is shared by all LDAP processes
+ * so for this variable to have much meaning, only one LDAP process
+ * should be running and memory inuse should be initialized to zero
+ * using the lber_set_option() function during startup.
+ * The counter is not accurate for multithreaded applications.
+ */
+#ifdef LDAP_MEMORY_DEBUG
+ ber_int_meminuse = * (int *) invalue;
+ return LBER_OPT_SUCCESS;
+#else
+ return LBER_OPT_ERROR;
+#endif
+ case LBER_OPT_MEMORY_FNS:
+ if ( ber_int_memory_fns == NULL )
+ {
+ const BerMemoryFunctions *f =
+ (const BerMemoryFunctions *) invalue;
+ /* make sure all functions are provided */
+ if(!( f->bmf_malloc && f->bmf_calloc
+ && f->bmf_realloc && f->bmf_free ))
+ {
+ ber_errno = LBER_ERROR_PARAM;
+ return LBER_OPT_ERROR;
+ }
+
+ ber_int_memory_fns = &ber_int_memory_fns_datum;
+
+ AC_MEMCPY(ber_int_memory_fns, f,
+ sizeof(BerMemoryFunctions));
+
+ return LBER_OPT_SUCCESS;
+ }
+ break;
+
+ case LBER_OPT_LOG_PROC:
+ ber_int_log_proc = (BER_LOG_FN)invalue;
+ return LBER_OPT_SUCCESS;
+ }
+
+ ber_errno = LBER_ERROR_PARAM;
+ return LBER_OPT_ERROR;
+ }
+
+ ber = item;
+ sb = item;
+
+ switch(option) {
+ case LBER_OPT_BER_OPTIONS:
+ assert( LBER_VALID( ber ) );
+ ber->ber_options = * (const int *) invalue;
+ return LBER_OPT_SUCCESS;
+
+ case LBER_OPT_BER_DEBUG:
+ assert( LBER_VALID( ber ) );
+ ber->ber_debug = * (const int *) invalue;
+ return LBER_OPT_SUCCESS;
+
+ case LBER_OPT_BER_REMAINING_BYTES:
+ assert( LBER_VALID( ber ) );
+ ber->ber_end = &ber->ber_ptr[* (const ber_len_t *) invalue];
+ return LBER_OPT_SUCCESS;
+
+ case LBER_OPT_BER_TOTAL_BYTES:
+ assert( LBER_VALID( ber ) );
+ ber->ber_end = &ber->ber_buf[* (const ber_len_t *) invalue];
+ return LBER_OPT_SUCCESS;
+
+ case LBER_OPT_BER_BYTES_TO_WRITE:
+ assert( LBER_VALID( ber ) );
+ ber->ber_ptr = &ber->ber_buf[* (const ber_len_t *) invalue];
+ return LBER_OPT_SUCCESS;
+
+ case LBER_OPT_BER_MEMCTX:
+ assert( LBER_VALID( ber ) );
+ ber->ber_memctx = *(void **)invalue;
+ return LBER_OPT_SUCCESS;
+
+ default:
+ /* bad param */
+ ber_errno = LBER_ERROR_PARAM;
+ break;
+ }
+
+ return LBER_OPT_ERROR;
+}
diff --git a/libraries/liblber/sockbuf.c b/libraries/liblber/sockbuf.c
new file mode 100644
index 0000000..7bd3228
--- /dev/null
+++ b/libraries/liblber/sockbuf.c
@@ -0,0 +1,988 @@
+/* sockbuf.c - i/o routines with support for adding i/o layers. */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif /* HAVE_IO_H */
+
+#if defined( HAVE_FCNTL_H )
+#include <fcntl.h>
+#endif
+
+#if defined( HAVE_SYS_FILIO_H )
+#include <sys/filio.h>
+#elif defined( HAVE_SYS_IOCTL_H )
+#include <sys/ioctl.h>
+#endif
+
+#include "lber-int.h"
+
+#ifndef LBER_MIN_BUFF_SIZE
+#define LBER_MIN_BUFF_SIZE 4096
+#endif
+#ifndef LBER_MAX_BUFF_SIZE
+#define LBER_MAX_BUFF_SIZE (65536*256)
+#endif
+#ifndef LBER_DEFAULT_READAHEAD
+#define LBER_DEFAULT_READAHEAD 16384
+#endif
+
+Sockbuf *
+ber_sockbuf_alloc( void )
+{
+ Sockbuf *sb;
+
+ sb = LBER_CALLOC( 1, sizeof( Sockbuf ) );
+
+ if( sb == NULL ) return NULL;
+
+ ber_int_sb_init( sb );
+ return sb;
+}
+
+void
+ber_sockbuf_free( Sockbuf *sb )
+{
+ assert( sb != NULL );
+ assert( SOCKBUF_VALID( sb ) );
+
+ ber_int_sb_close( sb );
+ ber_int_sb_destroy( sb );
+ LBER_FREE( sb );
+}
+
+/* Return values: -1: error, 0: no operation performed or the answer is false,
+ * 1: successful operation or the answer is true
+ */
+int
+ber_sockbuf_ctrl( Sockbuf *sb, int opt, void *arg )
+{
+ Sockbuf_IO_Desc *p;
+ int ret = 0;
+
+ assert( sb != NULL );
+ assert( SOCKBUF_VALID( sb ) );
+
+ switch ( opt ) {
+ case LBER_SB_OPT_HAS_IO:
+ p = sb->sb_iod;
+ while ( p && p->sbiod_io != (Sockbuf_IO *)arg ) {
+ p = p->sbiod_next;
+ }
+
+ if ( p ) {
+ ret = 1;
+ }
+ break;
+
+ case LBER_SB_OPT_GET_FD:
+ if ( arg != NULL ) {
+ *((ber_socket_t *)arg) = sb->sb_fd;
+ }
+ ret = ( sb->sb_fd == AC_SOCKET_INVALID ? -1 : 1);
+ break;
+
+ case LBER_SB_OPT_SET_FD:
+ sb->sb_fd = *((ber_socket_t *)arg);
+ ret = 1;
+ break;
+
+ case LBER_SB_OPT_SET_NONBLOCK:
+ ret = ber_pvt_socket_set_nonblock( sb->sb_fd, arg != NULL)
+ ? -1 : 1;
+ break;
+
+ case LBER_SB_OPT_DRAIN: {
+ /* Drain the data source to enable possible errors (e.g.
+ * TLS) to be propagated to the upper layers
+ */
+ char buf[LBER_MIN_BUFF_SIZE];
+
+ do {
+ ret = ber_int_sb_read( sb, buf, sizeof( buf ) );
+ } while ( ret == sizeof( buf ) );
+
+ ret = 1;
+ } break;
+
+ case LBER_SB_OPT_NEEDS_READ:
+ ret = ( sb->sb_trans_needs_read ? 1 : 0 );
+ break;
+
+ case LBER_SB_OPT_NEEDS_WRITE:
+ ret = ( sb->sb_trans_needs_write ? 1 : 0 );
+ break;
+
+ case LBER_SB_OPT_GET_MAX_INCOMING:
+ if ( arg != NULL ) {
+ *((ber_len_t *)arg) = sb->sb_max_incoming;
+ }
+ ret = 1;
+ break;
+
+ case LBER_SB_OPT_SET_MAX_INCOMING:
+ sb->sb_max_incoming = *((ber_len_t *)arg);
+ ret = 1;
+ break;
+
+ case LBER_SB_OPT_UNGET_BUF:
+#ifdef LDAP_PF_LOCAL_SENDMSG
+ sb->sb_ungetlen = ((struct berval *)arg)->bv_len;
+ if ( sb->sb_ungetlen <= sizeof( sb->sb_ungetbuf )) {
+ AC_MEMCPY( sb->sb_ungetbuf, ((struct berval *)arg)->bv_val,
+ sb->sb_ungetlen );
+ ret = 1;
+ } else {
+ sb->sb_ungetlen = 0;
+ ret = -1;
+ }
+#endif
+ break;
+
+ default:
+ ret = sb->sb_iod->sbiod_io->sbi_ctrl( sb->sb_iod, opt, arg );
+ break;
+ }
+
+ return ret;
+}
+
+int
+ber_sockbuf_add_io( Sockbuf *sb, Sockbuf_IO *sbio, int layer, void *arg )
+{
+ Sockbuf_IO_Desc *d, *p, **q;
+
+ assert( sb != NULL );
+ assert( SOCKBUF_VALID( sb ) );
+
+ if ( sbio == NULL ) {
+ return -1;
+ }
+
+ q = &sb->sb_iod;
+ p = *q;
+ while ( p && p->sbiod_level > layer ) {
+ q = &p->sbiod_next;
+ p = *q;
+ }
+
+ d = LBER_MALLOC( sizeof( *d ) );
+ if ( d == NULL ) {
+ return -1;
+ }
+
+ d->sbiod_level = layer;
+ d->sbiod_sb = sb;
+ d->sbiod_io = sbio;
+ memset( &d->sbiod_pvt, '\0', sizeof( d->sbiod_pvt ) );
+ d->sbiod_next = p;
+ *q = d;
+
+ if ( sbio->sbi_setup != NULL && ( sbio->sbi_setup( d, arg ) < 0 ) ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+ber_sockbuf_remove_io( Sockbuf *sb, Sockbuf_IO *sbio, int layer )
+{
+ Sockbuf_IO_Desc *p, **q;
+
+ assert( sb != NULL );
+ assert( SOCKBUF_VALID( sb ) );
+
+ if ( sb->sb_iod == NULL ) {
+ return -1;
+ }
+
+ q = &sb->sb_iod;
+ while ( *q != NULL ) {
+ p = *q;
+ if ( layer == p->sbiod_level && p->sbiod_io == sbio ) {
+ if ( p->sbiod_io->sbi_remove != NULL &&
+ p->sbiod_io->sbi_remove( p ) < 0 )
+ {
+ return -1;
+ }
+ *q = p->sbiod_next;
+ LBER_FREE( p );
+ break;
+ }
+ q = &p->sbiod_next;
+ }
+
+ return 0;
+}
+
+void
+ber_pvt_sb_buf_init( Sockbuf_Buf *buf )
+{
+ buf->buf_base = NULL;
+ buf->buf_ptr = 0;
+ buf->buf_end = 0;
+ buf->buf_size = 0;
+}
+
+void
+ber_pvt_sb_buf_destroy( Sockbuf_Buf *buf )
+{
+ assert( buf != NULL);
+
+ if (buf->buf_base) {
+ LBER_FREE( buf->buf_base );
+ }
+ ber_pvt_sb_buf_init( buf );
+}
+
+int
+ber_pvt_sb_grow_buffer( Sockbuf_Buf *buf, ber_len_t minsize )
+{
+ ber_len_t pw;
+ char *p;
+
+ assert( buf != NULL );
+
+ for ( pw = LBER_MIN_BUFF_SIZE; pw < minsize; pw <<= 1 ) {
+ if (pw > LBER_MAX_BUFF_SIZE) return -1;
+ }
+
+ if ( buf->buf_size < pw ) {
+ p = LBER_REALLOC( buf->buf_base, pw );
+ if ( p == NULL ) return -1;
+ buf->buf_base = p;
+ buf->buf_size = pw;
+ }
+ return 0;
+}
+
+ber_len_t
+ber_pvt_sb_copy_out( Sockbuf_Buf *sbb, char *buf, ber_len_t len )
+{
+ ber_len_t max;
+
+ assert( buf != NULL );
+ assert( sbb != NULL );
+#if 0
+ assert( sbb->buf_size > 0 );
+#endif
+
+ max = sbb->buf_end - sbb->buf_ptr;
+ max = ( max < len) ? max : len;
+ if ( max ) {
+ AC_MEMCPY( buf, sbb->buf_base + sbb->buf_ptr, max );
+ sbb->buf_ptr += max;
+ if ( sbb->buf_ptr >= sbb->buf_end ) {
+ sbb->buf_ptr = sbb->buf_end = 0;
+ }
+ }
+ return max;
+}
+
+ber_slen_t
+ber_pvt_sb_do_write( Sockbuf_IO_Desc *sbiod, Sockbuf_Buf *buf_out )
+{
+ ber_len_t to_go;
+ ber_slen_t ret;
+
+ assert( sbiod != NULL );
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+ to_go = buf_out->buf_end - buf_out->buf_ptr;
+ assert( to_go > 0 );
+
+ for(;;) {
+ ret = LBER_SBIOD_WRITE_NEXT( sbiod, buf_out->buf_base +
+ buf_out->buf_ptr, to_go );
+#ifdef EINTR
+ if ((ret<0) && (errno==EINTR)) continue;
+#endif
+ break;
+ }
+
+ if ( ret <= 0 ) return ret;
+
+ buf_out->buf_ptr += ret;
+ if (buf_out->buf_ptr == buf_out->buf_end) {
+ buf_out->buf_end = buf_out->buf_ptr = 0;
+ }
+
+ return ret;
+}
+
+int
+ber_pvt_socket_set_nonblock( ber_socket_t sd, int nb )
+{
+#ifdef HAVE_FCNTL
+ int flags = fcntl( sd, F_GETFL);
+ if( nb ) {
+ flags |= O_NONBLOCK;
+ } else {
+ flags &= ~O_NONBLOCK;
+ }
+ return fcntl( sd, F_SETFL, flags );
+
+#elif defined( FIONBIO )
+ ioctl_t status = nb ? 1 : 0;
+ return ioctl( sd, FIONBIO, &status );
+#endif
+}
+
+int
+ber_int_sb_init( Sockbuf *sb )
+{
+ assert( sb != NULL);
+
+ sb->sb_valid=LBER_VALID_SOCKBUF;
+ sb->sb_options = 0;
+ sb->sb_debug = ber_int_debug;
+ sb->sb_fd = AC_SOCKET_INVALID;
+ sb->sb_iod = NULL;
+ sb->sb_trans_needs_read = 0;
+ sb->sb_trans_needs_write = 0;
+
+ assert( SOCKBUF_VALID( sb ) );
+ return 0;
+}
+
+int
+ber_int_sb_close( Sockbuf *sb )
+{
+ Sockbuf_IO_Desc *p;
+
+ assert( sb != NULL);
+
+ p = sb->sb_iod;
+ while ( p ) {
+ if ( p->sbiod_io->sbi_close && p->sbiod_io->sbi_close( p ) < 0 ) {
+ return -1;
+ }
+ p = p->sbiod_next;
+ }
+
+ sb->sb_fd = AC_SOCKET_INVALID;
+
+ return 0;
+}
+
+int
+ber_int_sb_destroy( Sockbuf *sb )
+{
+ Sockbuf_IO_Desc *p;
+
+ assert( sb != NULL);
+ assert( SOCKBUF_VALID( sb ) );
+
+ while ( sb->sb_iod ) {
+ p = sb->sb_iod->sbiod_next;
+ ber_sockbuf_remove_io( sb, sb->sb_iod->sbiod_io,
+ sb->sb_iod->sbiod_level );
+ sb->sb_iod = p;
+ }
+
+ return ber_int_sb_init( sb );
+}
+
+ber_slen_t
+ber_int_sb_read( Sockbuf *sb, void *buf, ber_len_t len )
+{
+ ber_slen_t ret;
+
+ assert( buf != NULL );
+ assert( sb != NULL);
+ assert( sb->sb_iod != NULL );
+ assert( SOCKBUF_VALID( sb ) );
+
+ for (;;) {
+ ret = sb->sb_iod->sbiod_io->sbi_read( sb->sb_iod, buf, len );
+
+#ifdef EINTR
+ if ( ( ret < 0 ) && ( errno == EINTR ) ) continue;
+#endif
+ break;
+ }
+
+ return ret;
+}
+
+ber_slen_t
+ber_int_sb_write( Sockbuf *sb, void *buf, ber_len_t len )
+{
+ ber_slen_t ret;
+
+ assert( buf != NULL );
+ assert( sb != NULL);
+ assert( sb->sb_iod != NULL );
+ assert( SOCKBUF_VALID( sb ) );
+
+ for (;;) {
+ ret = sb->sb_iod->sbiod_io->sbi_write( sb->sb_iod, buf, len );
+
+#ifdef EINTR
+ if ( ( ret < 0 ) && ( errno == EINTR ) ) continue;
+#endif
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Support for TCP
+ */
+
+static ber_slen_t
+sb_stream_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
+{
+ assert( sbiod != NULL);
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+#if defined(MACOS)
+/*
+ * MacTCP/OpenTransport
+ */
+ return tcpread( sbiod->sbiod_sb->sb_fd, 0, (unsigned char *)buf,
+ len, NULL );
+
+#elif defined( HAVE_PCNFS ) || \
+ defined( HAVE_WINSOCK ) || defined ( __BEOS__ )
+/*
+ * PCNFS (under DOS)
+ */
+/*
+ * Windows Socket API (under DOS/Windows 3.x)
+ */
+/*
+ * 32-bit Windows Socket API (under Windows NT or Windows 95)
+ */
+ return recv( sbiod->sbiod_sb->sb_fd, buf, len, 0 );
+
+#elif defined( HAVE_NCSA )
+/*
+ * NCSA Telnet TCP/IP stack (under DOS)
+ */
+ return nread( sbiod->sbiod_sb->sb_fd, buf, len );
+
+#else
+ return read( sbiod->sbiod_sb->sb_fd, buf, len );
+#endif
+}
+
+static ber_slen_t
+sb_stream_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
+{
+ assert( sbiod != NULL);
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+#if defined(MACOS)
+/*
+ * MacTCP/OpenTransport
+ */
+#define MAX_WRITE 65535
+ return tcpwrite( sbiod->sbiod_sb->sb_fd, (unsigned char *)buf,
+ (len<MAX_WRITE) ? len : MAX_WRITE );
+
+#elif defined( HAVE_PCNFS) \
+ || defined( HAVE_WINSOCK) || defined ( __BEOS__ )
+/*
+ * PCNFS (under DOS)
+ */
+/*
+ * Windows Socket API (under DOS/Windows 3.x)
+ */
+/*
+ * 32-bit Windows Socket API (under Windows NT or Windows 95)
+ */
+ return send( sbiod->sbiod_sb->sb_fd, buf, len, 0 );
+
+#elif defined(HAVE_NCSA)
+ return netwrite( sbiod->sbiod_sb->sb_fd, buf, len );
+
+#elif defined(VMS)
+/*
+ * VMS -- each write must be 64K or smaller
+ */
+#define MAX_WRITE 65535
+ return write( sbiod->sbiod_sb->sb_fd, buf,
+ (len<MAX_WRITE) ? len : MAX_WRITE);
+#else
+ return write( sbiod->sbiod_sb->sb_fd, buf, len );
+#endif
+}
+
+static int
+sb_stream_close( Sockbuf_IO_Desc *sbiod )
+{
+ assert( sbiod != NULL );
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+ if ( sbiod->sbiod_sb->sb_fd != AC_SOCKET_INVALID )
+ tcp_close( sbiod->sbiod_sb->sb_fd );
+ return 0;
+}
+
+/* The argument is a pointer to the socket descriptor */
+static int
+sb_stream_setup( Sockbuf_IO_Desc *sbiod, void *arg ) {
+ assert( sbiod != NULL );
+
+ if ( arg != NULL ) {
+ sbiod->sbiod_sb->sb_fd = *((int *)arg);
+ }
+ return 0;
+}
+
+static int
+sb_stream_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) {
+ /* This is an end IO descriptor */
+ return 0;
+}
+
+Sockbuf_IO ber_sockbuf_io_tcp = {
+ sb_stream_setup, /* sbi_setup */
+ NULL, /* sbi_remove */
+ sb_stream_ctrl, /* sbi_ctrl */
+ sb_stream_read, /* sbi_read */
+ sb_stream_write, /* sbi_write */
+ sb_stream_close /* sbi_close */
+};
+
+
+/*
+ * Support for readahead (UDP needs it)
+ */
+
+static int
+sb_rdahead_setup( Sockbuf_IO_Desc *sbiod, void *arg )
+{
+ Sockbuf_Buf *p;
+
+ assert( sbiod != NULL );
+
+ p = LBER_MALLOC( sizeof( *p ) );
+ if ( p == NULL ) return -1;
+
+ ber_pvt_sb_buf_init( p );
+
+ if ( arg == NULL ) {
+ ber_pvt_sb_grow_buffer( p, LBER_DEFAULT_READAHEAD );
+ } else {
+ ber_pvt_sb_grow_buffer( p, *((int *)arg) );
+ }
+
+ sbiod->sbiod_pvt = p;
+ return 0;
+}
+
+static int
+sb_rdahead_remove( Sockbuf_IO_Desc *sbiod )
+{
+ Sockbuf_Buf *p;
+
+ assert( sbiod != NULL );
+
+ p = (Sockbuf_Buf *)sbiod->sbiod_pvt;
+
+ if ( p->buf_ptr != p->buf_end ) return -1;
+
+ ber_pvt_sb_buf_destroy( (Sockbuf_Buf *)(sbiod->sbiod_pvt) );
+ LBER_FREE( sbiod->sbiod_pvt );
+ sbiod->sbiod_pvt = NULL;
+
+ return 0;
+}
+
+static ber_slen_t
+sb_rdahead_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
+{
+ Sockbuf_Buf *p;
+ ber_slen_t bufptr = 0, ret, max;
+
+ assert( sbiod != NULL );
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+ assert( sbiod->sbiod_next != NULL );
+
+ p = (Sockbuf_Buf *)sbiod->sbiod_pvt;
+
+ assert( p->buf_size > 0 );
+
+ /* Are there anything left in the buffer? */
+ ret = ber_pvt_sb_copy_out( p, buf, len );
+ bufptr += ret;
+ len -= ret;
+
+ if ( len == 0 ) return bufptr;
+
+ max = p->buf_size - p->buf_end;
+ ret = 0;
+ while ( max > 0 ) {
+ ret = LBER_SBIOD_READ_NEXT( sbiod, p->buf_base + p->buf_end,
+ max );
+#ifdef EINTR
+ if ( ( ret < 0 ) && ( errno == EINTR ) ) continue;
+#endif
+ break;
+ }
+
+ if ( ret < 0 ) {
+ return ( bufptr ? bufptr : ret );
+ }
+
+ p->buf_end += ret;
+ bufptr += ber_pvt_sb_copy_out( p, (char *) buf + bufptr, len );
+ return bufptr;
+}
+
+static ber_slen_t
+sb_rdahead_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
+{
+ assert( sbiod != NULL );
+ assert( sbiod->sbiod_next != NULL );
+
+ return LBER_SBIOD_WRITE_NEXT( sbiod, buf, len );
+}
+
+static int
+sb_rdahead_close( Sockbuf_IO_Desc *sbiod )
+{
+ assert( sbiod != NULL );
+
+ /* Just erase the buffer */
+ ber_pvt_sb_buf_destroy((Sockbuf_Buf *)sbiod->sbiod_pvt);
+ return 0;
+}
+
+static int
+sb_rdahead_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
+{
+ Sockbuf_Buf *p;
+
+ p = (Sockbuf_Buf *)sbiod->sbiod_pvt;
+
+ if ( opt == LBER_SB_OPT_DATA_READY ) {
+ if ( p->buf_ptr != p->buf_end ) {
+ return 1;
+ }
+
+ } else if ( opt == LBER_SB_OPT_SET_READAHEAD ) {
+ if ( p->buf_size >= *((ber_len_t *)arg) ) {
+ return 0;
+ }
+ return ( ber_pvt_sb_grow_buffer( p, *((int *)arg) ) ?
+ -1 : 1 );
+ }
+
+ return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg );
+}
+
+Sockbuf_IO ber_sockbuf_io_readahead = {
+ sb_rdahead_setup, /* sbi_setup */
+ sb_rdahead_remove, /* sbi_remove */
+ sb_rdahead_ctrl, /* sbi_ctrl */
+ sb_rdahead_read, /* sbi_read */
+ sb_rdahead_write, /* sbi_write */
+ sb_rdahead_close /* sbi_close */
+};
+
+/*
+ * Support for simple file IO
+ */
+
+static ber_slen_t
+sb_fd_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
+{
+ assert( sbiod != NULL);
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+#ifdef LDAP_PF_LOCAL_SENDMSG
+ if ( sbiod->sbiod_sb->sb_ungetlen ) {
+ ber_len_t blen = sbiod->sbiod_sb->sb_ungetlen;
+ if ( blen > len )
+ blen = len;
+ AC_MEMCPY( buf, sbiod->sbiod_sb->sb_ungetbuf, blen );
+ buf = (char *) buf + blen;
+ len -= blen;
+ sbiod->sbiod_sb->sb_ungetlen -= blen;
+ if ( sbiod->sbiod_sb->sb_ungetlen ) {
+ AC_MEMCPY( sbiod->sbiod_sb->sb_ungetbuf,
+ sbiod->sbiod_sb->sb_ungetbuf+blen,
+ sbiod->sbiod_sb->sb_ungetlen );
+ }
+ if ( len == 0 )
+ return blen;
+ }
+#endif
+ return read( sbiod->sbiod_sb->sb_fd, buf, len );
+}
+
+static ber_slen_t
+sb_fd_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
+{
+ assert( sbiod != NULL);
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+ return write( sbiod->sbiod_sb->sb_fd, buf, len );
+}
+
+static int
+sb_fd_close( Sockbuf_IO_Desc *sbiod )
+{
+ assert( sbiod != NULL );
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+ if ( sbiod->sbiod_sb->sb_fd != AC_SOCKET_INVALID )
+ close( sbiod->sbiod_sb->sb_fd );
+ return 0;
+}
+
+/* The argument is a pointer to the file descriptor */
+static int
+sb_fd_setup( Sockbuf_IO_Desc *sbiod, void *arg ) {
+ assert( sbiod != NULL );
+
+ if ( arg != NULL )
+ sbiod->sbiod_sb->sb_fd = *((int *)arg);
+ return 0;
+}
+
+static int
+sb_fd_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) {
+ /* This is an end IO descriptor */
+ return 0;
+}
+
+Sockbuf_IO ber_sockbuf_io_fd = {
+ sb_fd_setup, /* sbi_setup */
+ NULL, /* sbi_remove */
+ sb_fd_ctrl, /* sbi_ctrl */
+ sb_fd_read, /* sbi_read */
+ sb_fd_write, /* sbi_write */
+ sb_fd_close /* sbi_close */
+};
+
+/*
+ * Debugging layer
+ */
+
+static int
+sb_debug_setup( Sockbuf_IO_Desc *sbiod, void *arg )
+{
+ assert( sbiod != NULL );
+
+ if ( arg == NULL ) arg = "sockbuf_";
+
+ sbiod->sbiod_pvt = LBER_MALLOC( strlen( arg ) + 1 );
+ if ( sbiod->sbiod_pvt == NULL ) return -1;
+
+ strcpy( (char *)sbiod->sbiod_pvt, (char *)arg );
+ return 0;
+}
+
+static int
+sb_debug_remove( Sockbuf_IO_Desc *sbiod )
+{
+ assert( sbiod != NULL );
+ assert( sbiod->sbiod_pvt != NULL );
+
+ LBER_FREE( sbiod->sbiod_pvt );
+ sbiod->sbiod_pvt = NULL;
+ return 0;
+}
+
+static int
+sb_debug_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
+{
+ return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg );
+}
+
+static ber_slen_t
+sb_debug_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
+{
+ ber_slen_t ret;
+ char ebuf[128];
+
+ ret = LBER_SBIOD_READ_NEXT( sbiod, buf, len );
+ if (sbiod->sbiod_sb->sb_debug & LDAP_DEBUG_PACKETS) {
+ int err = sock_errno();
+ if ( ret < 0 ) {
+ ber_log_printf( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug,
+ "%sread: want=%ld error=%s\n", (char *)sbiod->sbiod_pvt,
+ (long)len, AC_STRERROR_R( err, ebuf, sizeof ebuf ) );
+ } else {
+ ber_log_printf( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug,
+ "%sread: want=%ld, got=%ld\n", (char *)sbiod->sbiod_pvt,
+ (long)len, (long)ret );
+ ber_log_bprint( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug,
+ (const char *)buf, ret );
+ }
+ sock_errset(err);
+ }
+ return ret;
+}
+
+static ber_slen_t
+sb_debug_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
+{
+ ber_slen_t ret;
+ char ebuf[128];
+
+ ret = LBER_SBIOD_WRITE_NEXT( sbiod, buf, len );
+ if (sbiod->sbiod_sb->sb_debug & LDAP_DEBUG_PACKETS) {
+ int err = sock_errno();
+ if ( ret < 0 ) {
+ ber_log_printf( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug,
+ "%swrite: want=%ld error=%s\n",
+ (char *)sbiod->sbiod_pvt, (long)len,
+ AC_STRERROR_R( err, ebuf, sizeof ebuf ) );
+ } else {
+ ber_log_printf( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug,
+ "%swrite: want=%ld, written=%ld\n",
+ (char *)sbiod->sbiod_pvt, (long)len, (long)ret );
+ ber_log_bprint( LDAP_DEBUG_PACKETS, sbiod->sbiod_sb->sb_debug,
+ (const char *)buf, ret );
+ }
+ sock_errset(err);
+ }
+
+ return ret;
+}
+
+Sockbuf_IO ber_sockbuf_io_debug = {
+ sb_debug_setup, /* sbi_setup */
+ sb_debug_remove, /* sbi_remove */
+ sb_debug_ctrl, /* sbi_ctrl */
+ sb_debug_read, /* sbi_read */
+ sb_debug_write, /* sbi_write */
+ NULL /* sbi_close */
+};
+
+#ifdef LDAP_CONNECTIONLESS
+
+/*
+ * Support for UDP (CLDAP)
+ *
+ * All I/O at this level must be atomic. For ease of use, the sb_readahead
+ * must be used above this module. All data reads and writes are prefixed
+ * with a sockaddr_storage containing the address of the remote entity. Upper levels
+ * must read and write this sockaddr_storage before doing the usual ber_printf/scanf
+ * operations on LDAP messages.
+ */
+
+static int
+sb_dgram_setup( Sockbuf_IO_Desc *sbiod, void *arg )
+{
+ assert( sbiod != NULL);
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+ if ( arg != NULL ) sbiod->sbiod_sb->sb_fd = *((int *)arg);
+ return 0;
+}
+
+static ber_slen_t
+sb_dgram_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
+{
+ ber_slen_t rc;
+ ber_socklen_t addrlen;
+ struct sockaddr *src;
+
+ assert( sbiod != NULL );
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+ assert( buf != NULL );
+
+ addrlen = sizeof( struct sockaddr_storage );
+ src = buf;
+ buf = (char *) buf + addrlen;
+ len -= addrlen;
+ rc = recvfrom( sbiod->sbiod_sb->sb_fd, buf, len, 0, src, &addrlen );
+
+ return rc > 0 ? rc+sizeof(struct sockaddr_storage) : rc;
+}
+
+static ber_slen_t
+sb_dgram_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len )
+{
+ ber_slen_t rc;
+ struct sockaddr *dst;
+ socklen_t dstsize;
+
+ assert( sbiod != NULL );
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+ assert( buf != NULL );
+
+ dst = buf;
+ buf = (char *) buf + sizeof( struct sockaddr_storage );
+ len -= sizeof( struct sockaddr_storage );
+ dstsize = dst->sa_family == AF_INET ? sizeof( struct sockaddr_in )
+#ifdef LDAP_PF_INET6
+ : dst->sa_family == AF_INET6 ? sizeof( struct sockaddr_in6 )
+#endif
+ : sizeof( struct sockaddr_storage );
+ rc = sendto( sbiod->sbiod_sb->sb_fd, buf, len, 0, dst, dstsize );
+
+ if ( rc < 0 ) return -1;
+
+ /* fake error if write was not atomic */
+ if (rc < len) {
+# ifdef EMSGSIZE
+ errno = EMSGSIZE;
+# endif
+ return -1;
+ }
+ rc = len + sizeof(struct sockaddr_storage);
+ return rc;
+}
+
+static int
+sb_dgram_close( Sockbuf_IO_Desc *sbiod )
+{
+ assert( sbiod != NULL );
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+ if ( sbiod->sbiod_sb->sb_fd != AC_SOCKET_INVALID )
+ tcp_close( sbiod->sbiod_sb->sb_fd );
+ return 0;
+}
+
+static int
+sb_dgram_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
+{
+ /* This is an end IO descriptor */
+ return 0;
+}
+
+Sockbuf_IO ber_sockbuf_io_udp =
+{
+ sb_dgram_setup, /* sbi_setup */
+ NULL, /* sbi_remove */
+ sb_dgram_ctrl, /* sbi_ctrl */
+ sb_dgram_read, /* sbi_read */
+ sb_dgram_write, /* sbi_write */
+ sb_dgram_close /* sbi_close */
+};
+
+#endif /* LDAP_CONNECTIONLESS */
diff --git a/libraries/liblber/stdio.c b/libraries/liblber/stdio.c
new file mode 100644
index 0000000..afca4b1
--- /dev/null
+++ b/libraries/liblber/stdio.c
@@ -0,0 +1,243 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdarg.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <lutil.h>
+
+#if !defined(HAVE_VSNPRINTF) && !defined(HAVE_EBCDIC)
+/* Write at most n characters to the buffer in str, return the
+ * number of chars written or -1 if the buffer would have been
+ * overflowed.
+ *
+ * This is portable to any POSIX-compliant system. We use pipe()
+ * to create a valid file descriptor, and then fdopen() it to get
+ * a valid FILE pointer. The user's buffer and size are assigned
+ * to the FILE pointer using setvbuf. Then we close the read side
+ * of the pipe to invalidate the descriptor.
+ *
+ * If the write arguments all fit into size n, the write will
+ * return successfully. If the write is too large, the stdio
+ * buffer will need to be flushed to the underlying file descriptor.
+ * The flush will fail because it is attempting to write to a
+ * broken pipe, and the write will be terminated.
+ * -- hyc, 2002-07-19
+ */
+/* This emulation uses vfprintf; on OS/390 we're also emulating
+ * that function so it's more efficient just to have a separate
+ * version of vsnprintf there.
+ */
+#include <ac/signal.h>
+int ber_pvt_vsnprintf( char *str, size_t n, const char *fmt, va_list ap )
+{
+ int fds[2], res;
+ FILE *f;
+ RETSIGTYPE (*sig)();
+
+ if (pipe( fds )) return -1;
+
+ f = fdopen( fds[1], "w" );
+ if ( !f ) {
+ close( fds[1] );
+ close( fds[0] );
+ return -1;
+ }
+ setvbuf( f, str, _IOFBF, n );
+ sig = signal( SIGPIPE, SIG_IGN );
+ close( fds[0] );
+
+ res = vfprintf( f, fmt, ap );
+
+ fclose( f );
+ signal( SIGPIPE, sig );
+ if ( res > 0 && res < n ) {
+ res = vsprintf( str, fmt, ap );
+ }
+ return res;
+}
+#endif
+
+#ifndef HAVE_SNPRINTF
+int ber_pvt_snprintf( char *str, size_t n, const char *fmt, ... )
+{
+ va_list ap;
+ int res;
+
+ va_start( ap, fmt );
+ res = vsnprintf( str, n, fmt, ap );
+ va_end( ap );
+ return res;
+}
+#endif /* !HAVE_SNPRINTF */
+
+#ifdef HAVE_EBCDIC
+/* stdio replacements with ASCII/EBCDIC translation for OS/390.
+ * The OS/390 port depends on the CONVLIT compiler option being
+ * used to force character and string literals to be compiled in
+ * ISO8859-1, and the __LIBASCII cpp symbol to be defined to use the
+ * OS/390 ASCII-compatibility library. This library only supplies
+ * an ASCII version of sprintf, so other needed functions are
+ * provided here.
+ *
+ * All of the internal character manipulation is done in ASCII,
+ * but file I/O is EBCDIC, so we catch any stdio reading/writing
+ * of files here and do the translations.
+ */
+
+#undef fputs
+#undef fgets
+
+char *ber_pvt_fgets( char *s, int n, FILE *fp )
+{
+ s = (char *)fgets( s, n, fp );
+ if ( s ) __etoa( s );
+ return s;
+}
+
+int ber_pvt_fputs( const char *str, FILE *fp )
+{
+ char buf[8192];
+
+ strncpy( buf, str, sizeof(buf) );
+ __atoe( buf );
+ return fputs( buf, fp );
+}
+
+/* The __LIBASCII doesn't include a working vsprintf, so we make do
+ * using just sprintf. This is a very simplistic parser that looks for
+ * format strings and uses sprintf to process them one at a time.
+ * Literal text is just copied straight to the destination.
+ * The result is appended to the destination string. The parser
+ * recognizes field-width specifiers and the 'l' qualifier; it
+ * may need to be extended to recognize other qualifiers but so
+ * far this seems to be enough.
+ */
+int ber_pvt_vsnprintf( char *str, size_t n, const char *fmt, va_list ap )
+{
+ char *ptr, *pct, *s2, *f2, *end;
+ char fm2[64];
+ int len, rem;
+
+ ptr = (char *)fmt;
+ s2 = str;
+ fm2[0] = '%';
+ if (n) {
+ end = str + n;
+ } else {
+ end = NULL;
+ }
+
+ for (pct = strchr(ptr, '%'); pct; pct = strchr(ptr, '%')) {
+ len = pct-ptr;
+ if (end) {
+ rem = end-s2;
+ if (rem < 1) return -1;
+ if (rem < len) len = rem;
+ }
+ s2 = lutil_strncopy( s2, ptr, len );
+ /* Did we cheat the length above? If so, bail out */
+ if (len < pct-ptr) return -1;
+ for (pct++, f2 = fm2+1; isdigit(*pct);) *f2++ = *pct++;
+ if (*pct == 'l') *f2++ = *pct++;
+ if (*pct == '%') {
+ *s2++ = '%';
+ } else {
+ *f2++ = *pct;
+ *f2 = '\0';
+ if (*pct == 's') {
+ char *ss = va_arg(ap, char *);
+ /* Attempt to limit sprintf output. This
+ * may be thrown off if field widths were
+ * specified for this string.
+ *
+ * If it looks like the string is too
+ * long for the remaining buffer, bypass
+ * sprintf and just copy what fits, then
+ * quit.
+ */
+ if (end && strlen(ss) > (rem=end-s2)) {
+ strncpy(s2, ss, rem);
+ return -1;
+ } else {
+ s2 += sprintf(s2, fm2, ss);
+ }
+ } else {
+ s2 += sprintf(s2, fm2, va_arg(ap, int));
+ }
+ }
+ ptr = pct + 1;
+ }
+ if (end) {
+ rem = end-s2;
+ if (rem > 0) {
+ len = strlen(ptr);
+ s2 = lutil_strncopy( s2, ptr, rem );
+ rem -= len;
+ }
+ if (rem < 0) return -1;
+ } else {
+ s2 = lutil_strcopy( s2, ptr );
+ }
+ return s2 - str;
+}
+
+int ber_pvt_vsprintf( char *str, const char *fmt, va_list ap )
+{
+ return vsnprintf( str, 0, fmt, ap );
+}
+
+/* The fixed buffer size here is a problem, we don't know how
+ * to flush the buffer and keep printing if the msg is too big.
+ * Hopefully we never try to write something bigger than this
+ * in a log msg...
+ */
+int ber_pvt_vfprintf( FILE *fp, const char *fmt, va_list ap )
+{
+ char buf[8192];
+ int res;
+
+ vsnprintf( buf, sizeof(buf), fmt, ap );
+ __atoe( buf );
+ res = fputs( buf, fp );
+ if (res == EOF) res = -1;
+ return res;
+}
+
+int ber_pvt_printf( const char *fmt, ... )
+{
+ va_list ap;
+ int res;
+
+ va_start( ap, fmt );
+ res = ber_pvt_vfprintf( stdout, fmt, ap );
+ va_end( ap );
+ return res;
+}
+
+int ber_pvt_fprintf( FILE *fp, const char *fmt, ... )
+{
+ va_list ap;
+ int res;
+
+ va_start( ap, fmt );
+ res = ber_pvt_vfprintf( fp, fmt, ap );
+ va_end( ap );
+ return res;
+}
+#endif
diff --git a/libraries/libldap/Makefile.in b/libraries/libldap/Makefile.in
new file mode 100644
index 0000000..ad533af
--- /dev/null
+++ b/libraries/libldap/Makefile.in
@@ -0,0 +1,98 @@
+# Makefile.in for LDAP -lldap
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+LIBRARY = libldap.la
+
+PROGRAMS = apitest dntest ftest ltest urltest testavl
+
+SRCS = bind.c open.c result.c error.c compare.c search.c \
+ controls.c messages.c references.c extended.c cyrus.c \
+ modify.c add.c modrdn.c delete.c abandon.c \
+ sasl.c sbind.c unbind.c cancel.c \
+ filter.c free.c sort.c passwd.c whoami.c vc.c \
+ getdn.c getentry.c getattr.c getvalues.c addentry.c \
+ request.c os-ip.c url.c pagectrl.c sortctrl.c vlvctrl.c \
+ init.c options.c print.c string.c util-int.c schema.c \
+ charray.c os-local.c dnssrv.c utf-8.c utf-8-conv.c \
+ tls2.c tls_o.c tls_g.c \
+ turn.c ppolicy.c dds.c txn.c ldap_sync.c stctrl.c \
+ assertion.c deref.c ldifutil.c ldif.c fetch.c lbase64.c \
+ msctrl.c psearchctrl.c threads.c rdwr.c tpool.c rq.c \
+ thr_posix.c thr_thr.c thr_nt.c thr_pth.c thr_debug.c \
+ account_usability.c avl.c tavl.c testavl.c
+
+OBJS = bind.lo open.lo result.lo error.lo compare.lo search.lo \
+ controls.lo messages.lo references.lo extended.lo cyrus.lo \
+ modify.lo add.lo modrdn.lo delete.lo abandon.lo \
+ sasl.lo sbind.lo unbind.lo cancel.lo \
+ filter.lo free.lo sort.lo passwd.lo whoami.lo vc.lo \
+ getdn.lo getentry.lo getattr.lo getvalues.lo addentry.lo \
+ request.lo os-ip.lo url.lo pagectrl.lo sortctrl.lo vlvctrl.lo \
+ init.lo options.lo print.lo string.lo util-int.lo schema.lo \
+ charray.lo os-local.lo dnssrv.lo utf-8.lo utf-8-conv.lo \
+ tls2.lo tls_o.lo tls_g.lo \
+ turn.lo ppolicy.lo dds.lo txn.lo ldap_sync.lo stctrl.lo \
+ assertion.lo deref.lo ldifutil.lo ldif.lo fetch.lo lbase64.lo \
+ msctrl.lo psearchctrl.lo threads.lo rdwr.lo tpool.lo rq.lo \
+ thr_posix.lo thr_thr.lo thr_nt.lo thr_pth.lo thr_debug.lo \
+ account_usability.lo avl.lo tavl.lo
+
+LDAP_INCDIR= ../../include
+LDAP_LIBDIR= ../../libraries
+
+LIB_DEFS = -DLDAP_LIBRARY
+
+XLIBS = $(LIBRARY) $(LDAP_LIBLBER_LA) $(LDAP_LIBLUTIL_A)
+XXLIBS = $(SECURITY_LIBS) $(LUTIL_LIBS)
+NT_LINK_LIBS = $(LDAP_LIBLBER_LA) $(AC_LIBS) $(SECURITY_LIBS)
+UNIX_LINK_LIBS = $(LDAP_LIBLBER_LA) $(AC_LIBS) $(SECURITY_LIBS) $(LTHREAD_LIBS)
+ifneq (,$(OL_VERSIONED_SYMBOLS))
+ SYMBOL_VERSION_FLAGS=$(OL_VERSIONED_SYMBOLS)$(LDAP_LIBDIR)/libldap/libldap.vers
+endif
+
+apitest: $(XLIBS) apitest.o
+ $(LTLINK) -o $@ apitest.o $(LIBS)
+dntest: $(XLIBS) dntest.o
+ $(LTLINK) -o $@ dntest.o $(LIBS)
+ftest: $(XLIBS) ftest.o
+ $(LTLINK) -o $@ ftest.o $(LIBS)
+ltest: $(XLIBS) test.o
+ $(LTLINK) -o $@ test.o $(LIBS)
+testavl: $(XLIBS) testavl.o
+ $(LTLINK) -o $@ testavl.o $(LIBS)
+testtavl: $(XLIBS) testtavl.o
+ $(LTLINK) -o $@ testtavl.o $(LIBS)
+urltest: $(XLIBS) urltest.o
+ $(LTLINK) -o $@ urltest.o $(LIBS)
+
+
+CFFILES=ldap.conf
+
+install-local: $(CFFILES) FORCE
+ -$(MKDIR) $(DESTDIR)$(libdir)
+ $(LTINSTALL) $(INSTALLFLAGS) -m 644 $(LIBRARY) $(DESTDIR)$(libdir)
+ $(LTFINISH) $(DESTDIR)$(libdir)
+ -$(MKDIR) $(DESTDIR)$(sysconfdir)
+ @for i in $(CFFILES); do \
+ if test ! -f $(DESTDIR)$(sysconfdir)/$$i; then \
+ echo "installing $$i in $(sysconfdir)"; \
+ echo "$(INSTALL) $(INSTALLFLAGS) -m 644 $(srcdir)/$$i $(DESTDIR)$(sysconfdir)/$$i"; \
+ $(INSTALL) $(INSTALLFLAGS) -m 644 $(srcdir)/$$i $(DESTDIR)$(sysconfdir)/$$i; \
+ else \
+ echo "PRESERVING EXISTING CONFIGURATION FILE $(sysconfdir)/$$i" ; \
+ fi; \
+ $(INSTALL) $(INSTALLFLAGS) -m 644 $(srcdir)/$$i $(DESTDIR)$(sysconfdir)/$$i.default; \
+ done
+
diff --git a/libraries/libldap/abandon.c b/libraries/libldap/abandon.c
new file mode 100644
index 0000000..6161cc9
--- /dev/null
+++ b/libraries/libldap/abandon.c
@@ -0,0 +1,458 @@
+/* abandon.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/*
+ * An abandon request looks like this:
+ * AbandonRequest ::= [APPLICATION 16] MessageID
+ * and has no response. (Source: RFC 4511)
+ */
+#include "lutil.h"
+
+static int
+do_abandon(
+ LDAP *ld,
+ ber_int_t origid,
+ LDAPRequest *lr,
+ LDAPControl **sctrls,
+ int sendabandon );
+
+/*
+ * ldap_abandon_ext - perform an ldap extended abandon operation.
+ *
+ * Parameters:
+ * ld LDAP descriptor
+ * msgid The message id of the operation to abandon
+ * scntrls Server Controls
+ * ccntrls Client Controls
+ *
+ * ldap_abandon_ext returns a LDAP error code.
+ * (LDAP_SUCCESS if everything went ok)
+ *
+ * Example:
+ * ldap_abandon_ext( ld, msgid, scntrls, ccntrls );
+ */
+int
+ldap_abandon_ext(
+ LDAP *ld,
+ int msgid,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ int rc;
+
+ Debug1( LDAP_DEBUG_TRACE, "ldap_abandon_ext %d\n", msgid );
+
+ /* check client controls */
+ LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+
+ rc = ldap_int_client_controls( ld, cctrls );
+ if ( rc == LDAP_SUCCESS ) {
+ rc = do_abandon( ld, msgid, NULL, sctrls, 1 );
+ }
+
+ LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
+
+ return rc;
+}
+
+
+/*
+ * ldap_abandon - perform an ldap abandon operation. Parameters:
+ *
+ * ld LDAP descriptor
+ * msgid The message id of the operation to abandon
+ *
+ * ldap_abandon returns 0 if everything went ok, -1 otherwise.
+ *
+ * Example:
+ * ldap_abandon( ld, msgid );
+ */
+int
+ldap_abandon( LDAP *ld, int msgid )
+{
+ Debug1( LDAP_DEBUG_TRACE, "ldap_abandon %d\n", msgid );
+ return ldap_abandon_ext( ld, msgid, NULL, NULL ) == LDAP_SUCCESS
+ ? 0 : -1;
+}
+
+
+int
+ldap_pvt_discard(
+ LDAP *ld,
+ ber_int_t msgid )
+{
+ int rc;
+
+ LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+ rc = do_abandon( ld, msgid, NULL, NULL, 0 );
+ LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
+ return rc;
+}
+
+static int
+do_abandon(
+ LDAP *ld,
+ ber_int_t origid,
+ LDAPRequest *lr,
+ LDAPControl **sctrls,
+ int sendabandon )
+{
+ BerElement *ber;
+ int i, err;
+ ber_int_t msgid = origid;
+ Sockbuf *sb;
+ LDAPRequest needle = {0};
+
+ needle.lr_msgid = origid;
+
+ if ( lr != NULL ) {
+ msgid = lr->lr_msgid;
+ Debug2( LDAP_DEBUG_TRACE, "do_abandon origid %d, msgid %d\n",
+ origid, msgid );
+ } else if ( (lr = ldap_tavl_find( ld->ld_requests, &needle, ldap_req_cmp )) != NULL ) {
+ Debug2( LDAP_DEBUG_TRACE, "do_abandon origid %d, msgid %d\n",
+ origid, msgid );
+ if ( lr->lr_parent != NULL ) {
+ /* don't let caller abandon child requests! */
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return( LDAP_PARAM_ERROR );
+ }
+ msgid = lr->lr_msgid;
+ }
+
+ if ( lr != NULL ) {
+ LDAPRequest **childp = &lr->lr_child;
+
+ needle.lr_msgid = lr->lr_msgid;
+
+ if ( lr->lr_status != LDAP_REQST_INPROGRESS ) {
+ /* no need to send abandon message */
+ sendabandon = 0;
+ }
+
+ while ( *childp ) {
+ /* Abandon children */
+ LDAPRequest *child = *childp;
+
+ (void)do_abandon( ld, lr->lr_origid, child, sctrls, sendabandon );
+ if ( *childp == child ) {
+ childp = &child->lr_refnext;
+ }
+ }
+ }
+
+ /* ldap_msgdelete locks the res_mutex. Give up the req_mutex
+ * while we're in there.
+ */
+ LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
+ err = ldap_msgdelete( ld, msgid );
+ LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+ if ( err == 0 ) {
+ ld->ld_errno = LDAP_SUCCESS;
+ return LDAP_SUCCESS;
+ }
+
+ /* fetch again the request that we are abandoning */
+ if ( lr != NULL ) {
+ lr = ldap_tavl_find( ld->ld_requests, &needle, ldap_req_cmp );
+ }
+
+ err = 0;
+ if ( sendabandon ) {
+ if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, NULL ) == -1 ) {
+ /* not connected */
+ err = -1;
+ ld->ld_errno = LDAP_SERVER_DOWN;
+
+ } else if ( ( ber = ldap_alloc_ber_with_options( ld ) ) == NULL ) {
+ /* BER element allocation failed */
+ err = -1;
+ ld->ld_errno = LDAP_NO_MEMORY;
+
+ } else {
+ /*
+ * We already have the mutex in LDAP_R_COMPILE, so
+ * don't try to get it again.
+ * LDAP_NEXT_MSGID(ld, i);
+ */
+
+ LDAP_NEXT_MSGID(ld, i);
+#ifdef LDAP_CONNECTIONLESS
+ if ( LDAP_IS_UDP(ld) ) {
+ struct sockaddr_storage sa = {0};
+ /* dummy, filled with ldo_peer in request.c */
+ err = ber_write( ber, (char *) &sa, sizeof(sa), 0 );
+ }
+ if ( LDAP_IS_UDP(ld) && ld->ld_options.ldo_version ==
+ LDAP_VERSION2 )
+ {
+ char *dn;
+ LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
+ dn = ld->ld_options.ldo_cldapdn;
+ if (!dn) dn = "";
+ err = ber_printf( ber, "{isti", /* '}' */
+ i, dn,
+ LDAP_REQ_ABANDON, msgid );
+ LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
+ } else
+#endif
+ {
+ /* create a message to send */
+ err = ber_printf( ber, "{iti", /* '}' */
+ i,
+ LDAP_REQ_ABANDON, msgid );
+ }
+
+ if ( err == -1 ) {
+ /* encoding error */
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+
+ } else {
+ /* Put Server Controls */
+ if ( ldap_int_put_controls( ld, sctrls, ber )
+ != LDAP_SUCCESS )
+ {
+ err = -1;
+
+ } else {
+ /* close '{' */
+ err = ber_printf( ber, /*{*/ "N}" );
+
+ if ( err == -1 ) {
+ /* encoding error */
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ }
+ }
+ }
+
+ if ( err == -1 ) {
+ ber_free( ber, 1 );
+
+ } else {
+ /* send the message */
+ if ( lr != NULL ) {
+ assert( lr->lr_conn != NULL );
+ sb = lr->lr_conn->lconn_sb;
+ } else {
+ sb = ld->ld_sb;
+ }
+
+ if ( ber_flush2( sb, ber, LBER_FLUSH_FREE_ALWAYS ) != 0 ) {
+ ld->ld_errno = LDAP_SERVER_DOWN;
+ err = -1;
+ } else {
+ err = 0;
+ }
+ }
+ }
+ }
+
+ if ( lr != NULL ) {
+ LDAPConn *lc;
+ int freeconn = 0;
+ if ( sendabandon || lr->lr_status == LDAP_REQST_WRITING ) {
+ freeconn = 1;
+ lc = lr->lr_conn;
+ }
+ if ( origid == msgid ) {
+ ldap_free_request( ld, lr );
+
+ } else {
+ lr->lr_abandoned = 1;
+ }
+
+ if ( freeconn ) {
+ /* release ld_req_mutex while grabbing ld_conn_mutex to
+ * prevent deadlock.
+ */
+ LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ ldap_free_connection( ld, lc, 0, 1 );
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+ }
+ }
+
+ LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
+
+ /* use bisection */
+ i = 0;
+ if ( ld->ld_nabandoned == 0 ||
+ ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &i ) == 0 )
+ {
+ ldap_int_bisect_insert( &ld->ld_abandoned, &ld->ld_nabandoned, msgid, i );
+ }
+
+ if ( err != -1 ) {
+ ld->ld_errno = LDAP_SUCCESS;
+ }
+
+ LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
+ return( ld->ld_errno );
+}
+
+/*
+ * ldap_int_bisect_find
+ *
+ * args:
+ * v: array of length n (in)
+ * n: length of array v (in)
+ * id: value to look for (in)
+ * idxp: pointer to location of value/insert point
+ *
+ * return:
+ * 0: not found
+ * 1: found
+ * -1: error
+ */
+int
+ldap_int_bisect_find( ber_int_t *v, ber_len_t n, ber_int_t id, int *idxp )
+{
+ int begin,
+ end,
+ rc = 0;
+
+ assert( id >= 0 );
+
+ begin = 0;
+ end = n - 1;
+
+ if ( n <= 0 || id < v[ begin ] ) {
+ *idxp = 0;
+
+ } else if ( id > v[ end ] ) {
+ *idxp = n;
+
+ } else {
+ int pos;
+ ber_int_t curid;
+
+ do {
+ pos = (begin + end)/2;
+ curid = v[ pos ];
+
+ if ( id < curid ) {
+ end = pos - 1;
+
+ } else if ( id > curid ) {
+ begin = ++pos;
+
+ } else {
+ /* already abandoned? */
+ rc = 1;
+ break;
+ }
+ } while ( end >= begin );
+
+ *idxp = pos;
+ }
+
+ return rc;
+}
+
+/*
+ * ldap_int_bisect_insert
+ *
+ * args:
+ * vp: pointer to array of length *np (in/out)
+ * np: pointer to length of array *vp (in/out)
+ * id: value to insert (in)
+ * idx: location of insert point (as computed by ldap_int_bisect_find())
+ *
+ * return:
+ * 0: inserted
+ * -1: error
+ */
+int
+ldap_int_bisect_insert( ber_int_t **vp, ber_len_t *np, int id, int idx )
+{
+ ber_int_t *v;
+ ber_len_t n;
+ int i;
+
+ assert( vp != NULL );
+ assert( np != NULL );
+ assert( idx >= 0 );
+ assert( (unsigned) idx <= *np );
+
+ n = *np;
+
+ v = ber_memrealloc( *vp, sizeof( ber_int_t ) * ( n + 1 ) );
+ if ( v == NULL ) {
+ return -1;
+ }
+ *vp = v;
+
+ for ( i = n; i > idx; i-- ) {
+ v[ i ] = v[ i - 1 ];
+ }
+ v[ idx ] = id;
+ ++(*np);
+
+ return 0;
+}
+
+/*
+ * ldap_int_bisect_delete
+ *
+ * args:
+ * vp: pointer to array of length *np (in/out)
+ * np: pointer to length of array *vp (in/out)
+ * id: value to delete (in)
+ * idx: location of value to delete (as computed by ldap_int_bisect_find())
+ *
+ * return:
+ * 0: deleted
+ */
+int
+ldap_int_bisect_delete( ber_int_t **vp, ber_len_t *np, int id, int idx )
+{
+ ber_int_t *v;
+ ber_len_t i, n;
+
+ assert( vp != NULL );
+ assert( np != NULL );
+ assert( idx >= 0 );
+ assert( (unsigned) idx < *np );
+
+ v = *vp;
+
+ assert( v[ idx ] == id );
+
+ --(*np);
+ n = *np;
+
+ for ( i = idx; i < n; i++ ) {
+ v[ i ] = v[ i + 1 ];
+ }
+
+ return 0;
+}
diff --git a/libraries/libldap/account_usability.c b/libraries/libldap/account_usability.c
new file mode 100644
index 0000000..9cac6e6
--- /dev/null
+++ b/libraries/libldap/account_usability.c
@@ -0,0 +1,128 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2004 Hewlett-Packard Company.
+ * Portions Copyright 2004 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Howard Chu for inclusion in
+ * OpenLDAP Software, based on prior work by Neil Dunbar (HP).
+ * This work was sponsored by the Hewlett-Packard Company.
+ */
+
+#include "portable.h"
+
+#include "ldap-int.h"
+
+#ifdef LDAP_CONTROL_X_ACCOUNT_USABILITY
+
+int
+ldap_create_accountusability_control( LDAP *ld,
+ LDAPControl **ctrlp )
+{
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( ctrlp != NULL );
+
+ ld->ld_errno = ldap_control_create( LDAP_CONTROL_X_ACCOUNT_USABILITY,
+ 0, NULL, 0, ctrlp );
+
+ return ld->ld_errno;
+}
+
+int
+ldap_parse_accountusability_control(
+ LDAP *ld,
+ LDAPControl *ctrl,
+ int *availablep,
+ LDAPAccountUsability *usabilityp )
+{
+ BerElement *ber;
+ int available = 0;
+ ber_tag_t tag;
+ ber_len_t berLen;
+ char *last;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( ctrl != NULL );
+
+ if ( !ctrl->ldctl_value.bv_val ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+ }
+
+ /* Create a BerElement from the berval returned in the control. */
+ ber = ber_init(&ctrl->ldctl_value);
+
+ if (ber == NULL) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return(ld->ld_errno);
+ }
+
+ tag = ber_peek_tag( ber, &berLen );
+
+ if ( tag == LDAP_TAG_X_ACCOUNT_USABILITY_AVAILABLE ) {
+ available = 1;
+
+ if ( usabilityp != NULL ) {
+ if (ber_get_int( ber, &usabilityp->seconds_remaining ) == LBER_DEFAULT) goto exit;
+ }
+ } else if ( tag == LDAP_TAG_X_ACCOUNT_USABILITY_NOT_AVAILABLE ) {
+ available = 0;
+ LDAPAccountUsabilityMoreInfo more_info = { 0, 0, 0, -1, -1 };
+
+ ber_skip_tag( ber, &berLen );
+ while ( (tag = ber_peek_tag( ber, &berLen )) != LBER_DEFAULT ) {
+ switch (tag) {
+ case LDAP_TAG_X_ACCOUNT_USABILITY_INACTIVE:
+ if (ber_get_boolean( ber, &more_info.inactive ) == LBER_DEFAULT) goto exit;
+ break;
+ case LDAP_TAG_X_ACCOUNT_USABILITY_RESET:
+ if (ber_get_boolean( ber, &more_info.reset ) == LBER_DEFAULT) goto exit;
+ break;
+ case LDAP_TAG_X_ACCOUNT_USABILITY_EXPIRED:
+ if (ber_get_boolean( ber, &more_info.expired ) == LBER_DEFAULT) goto exit;
+ break;
+ case LDAP_TAG_X_ACCOUNT_USABILITY_REMAINING_GRACE:
+ if (ber_get_int( ber, &more_info.remaining_grace ) == LBER_DEFAULT) goto exit;
+ break;
+ case LDAP_TAG_X_ACCOUNT_USABILITY_UNTIL_UNLOCK:
+ if (ber_get_int( ber, &more_info.seconds_before_unlock ) == LBER_DEFAULT) goto exit;
+ break;
+ default:
+ goto exit;
+ }
+ }
+ if ( usabilityp != NULL ) {
+ usabilityp->more_info = more_info;
+ }
+ } else {
+ goto exit;
+ }
+ if ( availablep != NULL ) {
+ *availablep = available;
+ }
+
+ ber_free(ber, 1);
+
+ ld->ld_errno = LDAP_SUCCESS;
+ return(ld->ld_errno);
+
+ exit:
+ ber_free(ber, 1);
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+}
+
+#endif /* LDAP_CONTROL_X_ACCOUNT_USABILITY */
diff --git a/libraries/libldap/add.c b/libraries/libldap/add.c
new file mode 100644
index 0000000..7f63fa2
--- /dev/null
+++ b/libraries/libldap/add.c
@@ -0,0 +1,263 @@
+/* add.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/* An LDAP Add Request/Response looks like this:
+ * AddRequest ::= [APPLICATION 8] SEQUENCE {
+ * entry LDAPDN,
+ * attributes AttributeList }
+ *
+ * AttributeList ::= SEQUENCE OF attribute Attribute
+ *
+ * Attribute ::= PartialAttribute(WITH COMPONENTS {
+ * ...,
+ * vals (SIZE(1..MAX))})
+ *
+ * PartialAttribute ::= SEQUENCE {
+ * type AttributeDescription,
+ * vals SET OF value AttributeValue }
+ *
+ * AttributeDescription ::= LDAPString
+ * -- Constrained to <attributedescription> [RFC4512]
+ *
+ * AttributeValue ::= OCTET STRING
+ *
+ * AddResponse ::= [APPLICATION 9] LDAPResult
+ * (Source: RFC 4511)
+ */
+
+/*
+ * ldap_add - initiate an ldap add operation. Parameters:
+ *
+ * ld LDAP descriptor
+ * dn DN of the entry to add
+ * mods List of attributes for the entry. This is a null-
+ * terminated array of pointers to LDAPMod structures.
+ * only the type and values in the structures need be
+ * filled in.
+ *
+ * Example:
+ * LDAPMod *attrs[] = {
+ * { 0, "cn", { "babs jensen", "babs", 0 } },
+ * { 0, "sn", { "jensen", 0 } },
+ * { 0, "objectClass", { "person", 0 } },
+ * 0
+ * }
+ * msgid = ldap_add( ld, dn, attrs );
+ */
+int
+ldap_add( LDAP *ld, LDAP_CONST char *dn, LDAPMod **attrs )
+{
+ int rc;
+ int msgid;
+
+ rc = ldap_add_ext( ld, dn, attrs, NULL, NULL, &msgid );
+
+ if ( rc != LDAP_SUCCESS )
+ return -1;
+
+ return msgid;
+}
+
+
+BerElement *
+ldap_build_add_req(
+ LDAP *ld,
+ const char *dn,
+ LDAPMod **attrs,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp )
+{
+ BerElement *ber;
+ int i, rc;
+
+ /* create a message to send */
+ if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
+ return( NULL );
+ }
+
+ LDAP_NEXT_MSGID(ld, *msgidp);
+ rc = ber_printf( ber, "{it{s{", /* '}}}' */
+ *msgidp, LDAP_REQ_ADD, dn );
+
+ if ( rc == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ /* allow attrs to be NULL ("touch"; should fail...) */
+ if ( attrs ) {
+ /* for each attribute in the entry... */
+ for ( i = 0; attrs[i] != NULL; i++ ) {
+ if ( ( attrs[i]->mod_op & LDAP_MOD_BVALUES) != 0 ) {
+ int j;
+
+ if ( attrs[i]->mod_bvalues == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ for ( j = 0; attrs[i]->mod_bvalues[ j ] != NULL; j++ ) {
+ if ( attrs[i]->mod_bvalues[ j ]->bv_val == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+ }
+
+ rc = ber_printf( ber, "{s[V]N}", attrs[i]->mod_type,
+ attrs[i]->mod_bvalues );
+
+ } else {
+ if ( attrs[i]->mod_values == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ rc = ber_printf( ber, "{s[v]N}", attrs[i]->mod_type,
+ attrs[i]->mod_values );
+ }
+ if ( rc == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+ }
+ }
+
+ if ( ber_printf( ber, /*{{*/ "N}N}" ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ /* Put Server Controls */
+ if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ return( ber );
+}
+
+/*
+ * ldap_add_ext - initiate an ldap extended add operation. Parameters:
+ *
+ * ld LDAP descriptor
+ * dn DN of the entry to add
+ * mods List of attributes for the entry. This is a null-
+ * terminated array of pointers to LDAPMod structures.
+ * only the type and values in the structures need be
+ * filled in.
+ * sctrl Server Controls
+ * cctrl Client Controls
+ * msgidp Message ID pointer
+ *
+ * Example:
+ * LDAPMod *attrs[] = {
+ * { 0, "cn", { "babs jensen", "babs", 0 } },
+ * { 0, "sn", { "jensen", 0 } },
+ * { 0, "objectClass", { "person", 0 } },
+ * 0
+ * }
+ * rc = ldap_add_ext( ld, dn, attrs, NULL, NULL, &msgid );
+ */
+int
+ldap_add_ext(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPMod **attrs,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ BerElement *ber;
+ int rc;
+ ber_int_t id;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_add_ext\n" );
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( dn != NULL );
+ assert( msgidp != NULL );
+
+ /* check client controls */
+ rc = ldap_int_client_controls( ld, cctrls );
+ if( rc != LDAP_SUCCESS ) return rc;
+
+ ber = ldap_build_add_req( ld, dn, attrs, sctrls, cctrls, &id );
+ if( !ber )
+ return ld->ld_errno;
+
+ /* send the message */
+ *msgidp = ldap_send_initial_request( ld, LDAP_REQ_ADD, dn, ber, id );
+
+ if(*msgidp < 0)
+ return ld->ld_errno;
+
+ return LDAP_SUCCESS;
+}
+
+int
+ldap_add_ext_s(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPMod **attrs,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ int msgid, rc;
+ LDAPMessage *res;
+
+ rc = ldap_add_ext( ld, dn, attrs, sctrls, cctrls, &msgid );
+
+ if ( rc != LDAP_SUCCESS )
+ return( rc );
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res )
+ return( ld->ld_errno );
+
+ return( ldap_result2error( ld, res, 1 ) );
+}
+
+int
+ldap_add_s( LDAP *ld, LDAP_CONST char *dn, LDAPMod **attrs )
+{
+ return ldap_add_ext_s( ld, dn, attrs, NULL, NULL );
+}
+
diff --git a/libraries/libldap/addentry.c b/libraries/libldap/addentry.c
new file mode 100644
index 0000000..14a8879
--- /dev/null
+++ b/libraries/libldap/addentry.c
@@ -0,0 +1,72 @@
+/* addentry.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+LDAPMessage *
+ldap_delete_result_entry( LDAPMessage **list, LDAPMessage *e )
+{
+ LDAPMessage *tmp, *prev = NULL;
+
+ assert( list != NULL );
+ assert( e != NULL );
+
+ for ( tmp = *list; tmp != NULL && tmp != e; tmp = tmp->lm_chain )
+ prev = tmp;
+
+ if ( tmp == NULL )
+ return( NULL );
+
+ if ( prev == NULL ) {
+ if ( tmp->lm_chain )
+ tmp->lm_chain->lm_chain_tail = (*list)->lm_chain_tail;
+ *list = tmp->lm_chain;
+ } else {
+ prev->lm_chain = tmp->lm_chain;
+ if ( prev->lm_chain == NULL )
+ (*list)->lm_chain_tail = prev;
+ }
+ tmp->lm_chain = NULL;
+
+ return( tmp );
+}
+
+void
+ldap_add_result_entry( LDAPMessage **list, LDAPMessage *e )
+{
+ assert( list != NULL );
+ assert( e != NULL );
+
+ e->lm_chain = *list;
+ if ( *list )
+ e->lm_chain_tail = (*list)->lm_chain_tail;
+ else
+ e->lm_chain_tail = e;
+ *list = e;
+}
diff --git a/libraries/libldap/apitest.c b/libraries/libldap/apitest.c
new file mode 100644
index 0000000..a731087
--- /dev/null
+++ b/libraries/libldap/apitest.c
@@ -0,0 +1,241 @@
+/* apitest.c -- OpenLDAP API Test Program */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1998-2003 Kurt D. Zeilenga.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This program was originally developed by Kurt D. Zeilenga for inclusion in
+ * OpenLDAP Software.
+ */
+#include "portable.h"
+
+#include <ac/stdlib.h>
+
+#include <stdio.h>
+
+#include <ldap.h>
+
+int
+main(int argc, char **argv)
+{
+ LDAPAPIInfo api;
+ int ival;
+ char *sval;
+
+ printf("Compile time API Information\n");
+
+#ifdef LDAP_API_INFO_VERSION
+ api.ldapai_info_version = LDAP_API_INFO_VERSION;
+ printf(" API Info version: %d\n", (int) api.ldapai_info_version);
+#else
+ api.ldapai_info_version = 1;
+ printf(" API Info version: unknown\n");
+#endif
+
+#ifdef LDAP_FEATURE_INFO_VERSION
+ printf(" Feature Info version: %d\n", (int) LDAP_FEATURE_INFO_VERSION);
+#else
+ printf(" Feature Info version: unknown\n");
+ api.ldapai_info_version = 1;
+#endif
+
+#ifdef LDAP_API_VERSION
+ printf(" API version: %d\n", (int) LDAP_API_VERSION);
+#else
+ printf(" API version: unknown\n");
+#endif
+
+#ifdef LDAP_VERSION
+ printf(" Protocol Version: %d\n", (int) LDAP_VERSION);
+#else
+ printf(" Protocol Version: unknown\n");
+#endif
+#ifdef LDAP_VERSION_MIN
+ printf(" Protocol Min: %d\n", (int) LDAP_VERSION_MIN);
+#else
+ printf(" Protocol Min: unknown\n");
+#endif
+#ifdef LDAP_VERSION_MAX
+ printf(" Protocol Max: %d\n", (int) LDAP_VERSION_MAX);
+#else
+ printf(" Protocol Max: unknown\n");
+#endif
+#ifdef LDAP_VENDOR_NAME
+ printf(" Vendor Name: %s\n", LDAP_VENDOR_NAME);
+#else
+ printf(" Vendor Name: unknown\n");
+#endif
+#ifdef LDAP_VENDOR_VERSION
+ printf(" Vendor Version: %d\n", (int) LDAP_VENDOR_VERSION);
+#else
+ printf(" Vendor Version: unknown\n");
+#endif
+
+ if(ldap_get_option(NULL, LDAP_OPT_API_INFO, &api) != LDAP_SUCCESS) {
+ fprintf(stderr, "%s: ldap_get_option(API_INFO) failed\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ printf("\nExecution time API Information\n");
+ printf(" API Info version: %d\n", api.ldapai_info_version);
+
+ if (api.ldapai_info_version != LDAP_API_INFO_VERSION) {
+ printf(" API INFO version mismatch: got %d, expected %d\n",
+ api.ldapai_info_version, LDAP_API_INFO_VERSION);
+ return EXIT_FAILURE;
+ }
+
+ printf(" API Version: %d\n", api.ldapai_api_version);
+ printf(" Protocol Max: %d\n", api.ldapai_protocol_version);
+
+ if(api.ldapai_extensions == NULL) {
+ printf(" Extensions: none\n");
+
+ } else {
+ int i;
+ for(i=0; api.ldapai_extensions[i] != NULL; i++) /* empty */;
+ printf(" Extensions: %d\n", i);
+ for(i=0; api.ldapai_extensions[i] != NULL; i++) {
+#ifdef LDAP_OPT_API_FEATURE_INFO
+ LDAPAPIFeatureInfo fi;
+ fi.ldapaif_info_version = LDAP_FEATURE_INFO_VERSION;
+ fi.ldapaif_name = api.ldapai_extensions[i];
+ fi.ldapaif_version = 0;
+
+ if( ldap_get_option(NULL, LDAP_OPT_API_FEATURE_INFO, &fi) == LDAP_SUCCESS ) {
+ if(fi.ldapaif_info_version != LDAP_FEATURE_INFO_VERSION) {
+ printf(" %s feature info mismatch: got %d, expected %d\n",
+ api.ldapai_extensions[i],
+ LDAP_FEATURE_INFO_VERSION,
+ fi.ldapaif_info_version);
+
+ } else {
+ printf(" %s: version %d\n",
+ fi.ldapaif_name,
+ fi.ldapaif_version);
+ }
+
+ } else {
+ printf(" %s (NO FEATURE INFO)\n",
+ api.ldapai_extensions[i]);
+ }
+
+#else
+ printf(" %s\n",
+ api.ldapai_extensions[i]);
+#endif
+
+ ldap_memfree(api.ldapai_extensions[i]);
+ }
+ ldap_memfree(api.ldapai_extensions);
+ }
+
+ printf(" Vendor Name: %s\n", api.ldapai_vendor_name);
+ ldap_memfree(api.ldapai_vendor_name);
+
+ printf(" Vendor Version: %d\n", api.ldapai_vendor_version);
+
+ printf("\nExecution time Default Options\n");
+
+ if(ldap_get_option(NULL, LDAP_OPT_DEREF, &ival) != LDAP_SUCCESS) {
+ fprintf(stderr, "%s: ldap_get_option(api) failed\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ printf(" DEREF: %d\n", ival);
+
+ if(ldap_get_option(NULL, LDAP_OPT_SIZELIMIT, &ival) != LDAP_SUCCESS) {
+ fprintf(stderr, "%s: ldap_get_option(sizelimit) failed\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ printf(" SIZELIMIT: %d\n", ival);
+
+ if(ldap_get_option(NULL, LDAP_OPT_TIMELIMIT, &ival) != LDAP_SUCCESS) {
+ fprintf(stderr, "%s: ldap_get_option(timelimit) failed\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ printf(" TIMELIMIT: %d\n", ival);
+
+ if(ldap_get_option(NULL, LDAP_OPT_REFERRALS, &ival) != LDAP_SUCCESS) {
+ fprintf(stderr, "%s: ldap_get_option(referrals) failed\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ printf(" REFERRALS: %s\n", ival ? "on" : "off");
+
+ if(ldap_get_option(NULL, LDAP_OPT_RESTART, &ival) != LDAP_SUCCESS) {
+ fprintf(stderr, "%s: ldap_get_option(restart) failed\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ printf(" RESTART: %s\n", ival ? "on" : "off");
+
+ if(ldap_get_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &ival) != LDAP_SUCCESS) {
+ fprintf(stderr, "%s: ldap_get_option(protocol version) failed\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ printf(" PROTOCOL VERSION: %d\n", ival);
+
+ if(ldap_get_option(NULL, LDAP_OPT_HOST_NAME, &sval) != LDAP_SUCCESS) {
+ fprintf(stderr, "%s: ldap_get_option(host name) failed\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ if( sval != NULL ) {
+ printf(" HOST NAME: %s\n", sval);
+ ldap_memfree(sval);
+ } else {
+ puts(" HOST NAME: <not set>");
+ }
+
+#if 0
+ /* API tests */
+ { /* bindless unbind */
+ LDAP *ld;
+ int rc;
+
+ ld = ldap_init( "localhost", 389 );
+ if( ld == NULL ) {
+ perror("ldap_init");
+ return EXIT_FAILURE;
+ }
+
+ rc = ldap_unbind( ld );
+ if( rc != LDAP_SUCCESS ) {
+ perror("ldap_unbind");
+ return EXIT_FAILURE;
+ }
+ }
+ { /* bindless unbind */
+ LDAP *ld;
+ int rc;
+
+ ld = ldap_init( "localhost", 389 );
+ if( ld == NULL ) {
+ perror("ldap_init");
+ return EXIT_FAILURE;
+ }
+
+ rc = ldap_abandon_ext( ld, 0, NULL, NULL );
+ if( rc != LDAP_SERVER_DOWN ) {
+ ldap_perror( ld, "ldap_abandon");
+ return EXIT_FAILURE;
+ }
+
+ rc = ldap_unbind( ld );
+ if( rc != LDAP_SUCCESS ) {
+ perror("ldap_unbind");
+ return EXIT_FAILURE;
+ }
+ }
+#endif
+
+ return EXIT_SUCCESS;
+}
diff --git a/libraries/libldap/assertion.c b/libraries/libldap/assertion.c
new file mode 100644
index 0000000..97ddb61
--- /dev/null
+++ b/libraries/libldap/assertion.c
@@ -0,0 +1,100 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+int
+ldap_create_assertion_control_value(
+ LDAP *ld,
+ char *assertion,
+ struct berval *value )
+{
+ BerElement *ber = NULL;
+ int err;
+
+ ld->ld_errno = LDAP_SUCCESS;
+
+ if ( assertion == NULL || assertion[ 0 ] == '\0' ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ if ( value == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ BER_BVZERO( value );
+
+ ber = ldap_alloc_ber_with_options( ld );
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ err = ldap_pvt_put_filter( ber, assertion );
+ if ( err < 0 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+
+ err = ber_flatten2( ber, value, 1 );
+ if ( err < 0 ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ goto done;
+ }
+
+done:;
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ return ld->ld_errno;
+}
+
+int
+ldap_create_assertion_control(
+ LDAP *ld,
+ char *assertion,
+ int iscritical,
+ LDAPControl **ctrlp )
+{
+ struct berval value;
+
+ if ( ctrlp == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ ld->ld_errno = ldap_create_assertion_control_value( ld,
+ assertion, &value );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ ld->ld_errno = ldap_control_create( LDAP_CONTROL_ASSERT,
+ iscritical, &value, 0, ctrlp );
+ if ( ld->ld_errno != LDAP_SUCCESS ) {
+ LDAP_FREE( value.bv_val );
+ }
+ }
+
+ return ld->ld_errno;
+}
+
diff --git a/libraries/libldap/avl.c b/libraries/libldap/avl.c
new file mode 100644
index 0000000..fd22c7f
--- /dev/null
+++ b/libraries/libldap/avl.c
@@ -0,0 +1,671 @@
+/* avl.c - routines to implement an avl tree */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1993 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by the University of Michigan
+ * (as part of U-MICH LDAP). Additional significant contributors
+ * include:
+ * Howard Y. Chu
+ * Hallvard B. Furuseth
+ * Kurt D. Zeilenga
+ */
+
+#include "portable.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#ifdef CSRIMALLOC
+#define ber_memalloc malloc
+#define ber_memrealloc realloc
+#define ber_memfree free
+#else
+#include "lber.h"
+#endif
+
+#define AVL_INTERNAL
+#include "ldap_avl.h"
+
+/* Maximum tree depth this host's address space could support */
+#define MAX_TREE_DEPTH (sizeof(void *) * CHAR_BIT)
+
+static const int avl_bfs[] = {LH, RH};
+
+/*
+ * ldap_avl_insert -- insert a node containing data data into the avl tree
+ * with root root. fcmp is a function to call to compare the data portion
+ * of two nodes. it should take two arguments and return <, >, or == 0,
+ * depending on whether its first argument is <, >, or == its second
+ * argument (like strcmp, e.g.). fdup is a function to call when a duplicate
+ * node is inserted. it should return 0, or -1 and its return value
+ * will be the return value from ldap_avl_insert in the case of a duplicate node.
+ * the function will be called with the original node's data as its first
+ * argument and with the incoming duplicate node's data as its second
+ * argument. this could be used, for example, to keep a count with each
+ * node.
+ *
+ * NOTE: this routine may malloc memory
+ */
+int
+ldap_avl_insert( Avlnode ** root, void *data, AVL_CMP fcmp, AVL_DUP fdup )
+{
+ Avlnode *t, *p, *s, *q, *r;
+ int a, cmp, ncmp;
+
+ if ( *root == NULL ) {
+ if (( r = (Avlnode *) ber_memalloc( sizeof( Avlnode ))) == NULL ) {
+ return( -1 );
+ }
+ r->avl_link[0] = r->avl_link[1] = NULL;
+ r->avl_data = data;
+ r->avl_bits[0] = r->avl_bits[1] = AVL_CHILD;
+ r->avl_bf = EH;
+ *root = r;
+
+ return( 0 );
+ }
+
+ t = NULL;
+ s = p = *root;
+
+ /* find insertion point */
+ while (1) {
+ cmp = fcmp( data, p->avl_data );
+ if ( cmp == 0 )
+ return (*fdup)( p->avl_data, data );
+
+ cmp = (cmp > 0);
+ q = p->avl_link[cmp];
+ if (q == NULL) {
+ /* insert */
+ if (( q = (Avlnode *) ber_memalloc( sizeof( Avlnode ))) == NULL ) {
+ return( -1 );
+ }
+ q->avl_link[0] = q->avl_link[1] = NULL;
+ q->avl_data = data;
+ q->avl_bits[0] = q->avl_bits[1] = AVL_CHILD;
+ q->avl_bf = EH;
+
+ p->avl_link[cmp] = q;
+ break;
+ } else if ( q->avl_bf ) {
+ t = p;
+ s = q;
+ }
+ p = q;
+ }
+
+ /* adjust balance factors */
+ cmp = fcmp( data, s->avl_data ) > 0;
+ r = p = s->avl_link[cmp];
+ a = avl_bfs[cmp];
+
+ while ( p != q ) {
+ cmp = fcmp( data, p->avl_data ) > 0;
+ p->avl_bf = avl_bfs[cmp];
+ p = p->avl_link[cmp];
+ }
+
+ /* checks and balances */
+
+ if ( s->avl_bf == EH ) {
+ s->avl_bf = a;
+ return 0;
+ } else if ( s->avl_bf == -a ) {
+ s->avl_bf = EH;
+ return 0;
+ } else if ( s->avl_bf == a ) {
+ cmp = (a > 0);
+ ncmp = !cmp;
+ if ( r->avl_bf == a ) {
+ /* single rotation */
+ p = r;
+ s->avl_link[cmp] = r->avl_link[ncmp];
+ r->avl_link[ncmp] = s;
+ s->avl_bf = 0;
+ r->avl_bf = 0;
+ } else if ( r->avl_bf == -a ) {
+ /* double rotation */
+ p = r->avl_link[ncmp];
+ r->avl_link[ncmp] = p->avl_link[cmp];
+ p->avl_link[cmp] = r;
+ s->avl_link[cmp] = p->avl_link[ncmp];
+ p->avl_link[ncmp] = s;
+
+ if ( p->avl_bf == a ) {
+ s->avl_bf = -a;
+ r->avl_bf = 0;
+ } else if ( p->avl_bf == -a ) {
+ s->avl_bf = 0;
+ r->avl_bf = a;
+ } else {
+ s->avl_bf = 0;
+ r->avl_bf = 0;
+ }
+ p->avl_bf = 0;
+ }
+ /* Update parent */
+ if ( t == NULL )
+ *root = p;
+ else if ( s == t->avl_right )
+ t->avl_right = p;
+ else
+ t->avl_left = p;
+ }
+
+ return 0;
+}
+
+void*
+ldap_avl_delete( Avlnode **root, void* data, AVL_CMP fcmp )
+{
+ Avlnode *p, *q, *r, *top;
+ int side, side_bf, shorter, nside;
+
+ /* parent stack */
+ Avlnode *pptr[MAX_TREE_DEPTH];
+ unsigned char pdir[MAX_TREE_DEPTH];
+ int depth = 0;
+
+ if ( *root == NULL )
+ return NULL;
+
+ p = *root;
+
+ while (1) {
+ side = fcmp( data, p->avl_data );
+ if ( !side )
+ break;
+ side = ( side > 0 );
+ pdir[depth] = side;
+ pptr[depth++] = p;
+
+ p = p->avl_link[side];
+ if ( p == NULL )
+ return p;
+ }
+ data = p->avl_data;
+
+ /* If this node has two children, swap so we are deleting a node with
+ * at most one child.
+ */
+ if ( p->avl_link[0] && p->avl_link[1] ) {
+
+ /* find the immediate predecessor <q> */
+ q = p->avl_link[0];
+ side = depth;
+ pdir[depth++] = 0;
+ while (q->avl_link[1]) {
+ pdir[depth] = 1;
+ pptr[depth++] = q;
+ q = q->avl_link[1];
+ }
+ /* swap links */
+ r = p->avl_link[0];
+ p->avl_link[0] = q->avl_link[0];
+ q->avl_link[0] = r;
+
+ q->avl_link[1] = p->avl_link[1];
+ p->avl_link[1] = NULL;
+
+ q->avl_bf = p->avl_bf;
+
+ /* fix stack positions: old parent of p points to q */
+ pptr[side] = q;
+ if ( side ) {
+ r = pptr[side-1];
+ r->avl_link[pdir[side-1]] = q;
+ } else {
+ *root = q;
+ }
+ /* new parent of p points to p */
+ if ( depth-side > 1 ) {
+ r = pptr[depth-1];
+ r->avl_link[1] = p;
+ } else {
+ q->avl_link[0] = p;
+ }
+ }
+
+ /* now <p> has at most one child, get it */
+ q = p->avl_link[0] ? p->avl_link[0] : p->avl_link[1];
+
+ ber_memfree( p );
+
+ if ( !depth ) {
+ *root = q;
+ return data;
+ }
+
+ /* set the child into p's parent */
+ depth--;
+ p = pptr[depth];
+ side = pdir[depth];
+ p->avl_link[side] = q;
+
+ top = NULL;
+ shorter = 1;
+
+ while ( shorter ) {
+ p = pptr[depth];
+ side = pdir[depth];
+ nside = !side;
+ side_bf = avl_bfs[side];
+
+ /* case 1: height unchanged */
+ if ( p->avl_bf == EH ) {
+ /* Tree is now heavier on opposite side */
+ p->avl_bf = avl_bfs[nside];
+ shorter = 0;
+
+ } else if ( p->avl_bf == side_bf ) {
+ /* case 2: taller subtree shortened, height reduced */
+ p->avl_bf = EH;
+ } else {
+ /* case 3: shorter subtree shortened */
+ if ( depth )
+ top = pptr[depth-1]; /* p->parent; */
+ else
+ top = NULL;
+ /* set <q> to the taller of the two subtrees of <p> */
+ q = p->avl_link[nside];
+ if ( q->avl_bf == EH ) {
+ /* case 3a: height unchanged, single rotate */
+ p->avl_link[nside] = q->avl_link[side];
+ q->avl_link[side] = p;
+ shorter = 0;
+ q->avl_bf = side_bf;
+ p->avl_bf = (- side_bf);
+
+ } else if ( q->avl_bf == p->avl_bf ) {
+ /* case 3b: height reduced, single rotate */
+ p->avl_link[nside] = q->avl_link[side];
+ q->avl_link[side] = p;
+ shorter = 1;
+ q->avl_bf = EH;
+ p->avl_bf = EH;
+
+ } else {
+ /* case 3c: height reduced, balance factors opposite */
+ r = q->avl_link[side];
+ q->avl_link[side] = r->avl_link[nside];
+ r->avl_link[nside] = q;
+
+ p->avl_link[nside] = r->avl_link[side];
+ r->avl_link[side] = p;
+
+ if ( r->avl_bf == side_bf ) {
+ q->avl_bf = (- side_bf);
+ p->avl_bf = EH;
+ } else if ( r->avl_bf == (- side_bf)) {
+ q->avl_bf = EH;
+ p->avl_bf = side_bf;
+ } else {
+ q->avl_bf = EH;
+ p->avl_bf = EH;
+ }
+ r->avl_bf = EH;
+ q = r;
+ }
+ /* a rotation has caused <q> (or <r> in case 3c) to become
+ * the root. let <p>'s former parent know this.
+ */
+ if ( top == NULL ) {
+ *root = q;
+ } else if (top->avl_link[0] == p) {
+ top->avl_link[0] = q;
+ } else {
+ top->avl_link[1] = q;
+ }
+ /* end case 3 */
+ p = q;
+ }
+ if ( !depth )
+ break;
+ depth--;
+ } /* end while(shorter) */
+
+ return data;
+}
+
+static int
+avl_inapply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag )
+{
+ if ( root == 0 )
+ return( AVL_NOMORE );
+
+ if ( root->avl_left != 0 )
+ if ( avl_inapply( root->avl_left, fn, arg, stopflag )
+ == stopflag )
+ return( stopflag );
+
+ if ( (*fn)( root->avl_data, arg ) == stopflag )
+ return( stopflag );
+
+ if ( root->avl_right == 0 )
+ return( AVL_NOMORE );
+ else
+ return( avl_inapply( root->avl_right, fn, arg, stopflag ) );
+}
+
+static int
+avl_postapply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag )
+{
+ if ( root == 0 )
+ return( AVL_NOMORE );
+
+ if ( root->avl_left != 0 )
+ if ( avl_postapply( root->avl_left, fn, arg, stopflag )
+ == stopflag )
+ return( stopflag );
+
+ if ( root->avl_right != 0 )
+ if ( avl_postapply( root->avl_right, fn, arg, stopflag )
+ == stopflag )
+ return( stopflag );
+
+ return( (*fn)( root->avl_data, arg ) );
+}
+
+static int
+avl_preapply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag )
+{
+ if ( root == 0 )
+ return( AVL_NOMORE );
+
+ if ( (*fn)( root->avl_data, arg ) == stopflag )
+ return( stopflag );
+
+ if ( root->avl_left != 0 )
+ if ( avl_preapply( root->avl_left, fn, arg, stopflag )
+ == stopflag )
+ return( stopflag );
+
+ if ( root->avl_right == 0 )
+ return( AVL_NOMORE );
+ else
+ return( avl_preapply( root->avl_right, fn, arg, stopflag ) );
+}
+
+/*
+ * ldap_avl_apply -- avl tree root is traversed, function fn is called with
+ * arguments arg and the data portion of each node. if fn returns stopflag,
+ * the traversal is cut short, otherwise it continues. Do not use -6 as
+ * a stopflag, as this is what is used to indicate the traversal ran out
+ * of nodes.
+ */
+
+int
+ldap_avl_apply( Avlnode *root, AVL_APPLY fn, void* arg, int stopflag, int type )
+{
+ switch ( type ) {
+ case AVL_INORDER:
+ return( avl_inapply( root, fn, arg, stopflag ) );
+ case AVL_PREORDER:
+ return( avl_preapply( root, fn, arg, stopflag ) );
+ case AVL_POSTORDER:
+ return( avl_postapply( root, fn, arg, stopflag ) );
+ default:
+ fprintf( stderr, "Invalid traversal type %d\n", type );
+ return( -1 );
+ }
+
+ /* NOTREACHED */
+}
+
+/*
+ * ldap_avl_prefixapply - traverse avl tree root, applying function fprefix
+ * to any nodes that match. fcmp is called with data as its first arg
+ * and the current node's data as its second arg. it should return
+ * 0 if they match, < 0 if data is less, and > 0 if data is greater.
+ * the idea is to efficiently find all nodes that are prefixes of
+ * some key... Like ldap_avl_apply, this routine also takes a stopflag
+ * and will return prematurely if fmatch returns this value. Otherwise,
+ * AVL_NOMORE is returned.
+ */
+
+int
+ldap_avl_prefixapply(
+ Avlnode *root,
+ void* data,
+ AVL_CMP fmatch,
+ void* marg,
+ AVL_CMP fcmp,
+ void* carg,
+ int stopflag
+)
+{
+ int cmp;
+
+ if ( root == 0 )
+ return( AVL_NOMORE );
+
+ cmp = (*fcmp)( data, root->avl_data /* , carg */);
+ if ( cmp == 0 ) {
+ if ( (*fmatch)( root->avl_data, marg ) == stopflag )
+ return( stopflag );
+
+ if ( root->avl_left != 0 )
+ if ( ldap_avl_prefixapply( root->avl_left, data, fmatch,
+ marg, fcmp, carg, stopflag ) == stopflag )
+ return( stopflag );
+
+ if ( root->avl_right != 0 )
+ return( ldap_avl_prefixapply( root->avl_right, data, fmatch,
+ marg, fcmp, carg, stopflag ) );
+ else
+ return( AVL_NOMORE );
+
+ } else if ( cmp < 0 ) {
+ if ( root->avl_left != 0 )
+ return( ldap_avl_prefixapply( root->avl_left, data, fmatch,
+ marg, fcmp, carg, stopflag ) );
+ } else {
+ if ( root->avl_right != 0 )
+ return( ldap_avl_prefixapply( root->avl_right, data, fmatch,
+ marg, fcmp, carg, stopflag ) );
+ }
+
+ return( AVL_NOMORE );
+}
+
+/*
+ * ldap_avl_free -- traverse avltree root, freeing the memory it is using.
+ * the dfree() is called to free the data portion of each node. The
+ * number of items actually freed is returned.
+ */
+
+int
+ldap_avl_free( Avlnode *root, AVL_FREE dfree )
+{
+ int nleft, nright;
+
+ if ( root == 0 )
+ return( 0 );
+
+ nleft = nright = 0;
+ if ( root->avl_left != 0 )
+ nleft = ldap_avl_free( root->avl_left, dfree );
+
+ if ( root->avl_right != 0 )
+ nright = ldap_avl_free( root->avl_right, dfree );
+
+ if ( dfree )
+ (*dfree)( root->avl_data );
+ ber_memfree( root );
+
+ return( nleft + nright + 1 );
+}
+
+/*
+ * ldap_avl_find -- search avltree root for a node with data data. the function
+ * cmp is used to compare things. it is called with data as its first arg
+ * and the current node data as its second. it should return 0 if they match,
+ * < 0 if arg1 is less than arg2 and > 0 if arg1 is greater than arg2.
+ */
+
+Avlnode *
+ldap_avl_find2( Avlnode *root, const void *data, AVL_CMP fcmp )
+{
+ int cmp;
+
+ while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) {
+ cmp = cmp > 0;
+ root = root->avl_link[cmp];
+ }
+ return root;
+}
+
+void*
+ldap_avl_find( Avlnode *root, const void* data, AVL_CMP fcmp )
+{
+ int cmp;
+
+ while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) {
+ cmp = cmp > 0;
+ root = root->avl_link[cmp];
+ }
+
+ return( root ? root->avl_data : 0 );
+}
+
+/*
+ * ldap_avl_find_lin -- search avltree root linearly for a node with data data.
+ * the function cmp is used to compare things. it is called with data as its
+ * first arg and the current node data as its second. it should return 0 if
+ * they match, non-zero otherwise.
+ */
+
+void*
+ldap_avl_find_lin( Avlnode *root, const void* data, AVL_CMP fcmp )
+{
+ void* res;
+
+ if ( root == 0 )
+ return( NULL );
+
+ if ( (*fcmp)( data, root->avl_data ) == 0 )
+ return( root->avl_data );
+
+ if ( root->avl_left != 0 )
+ if ( (res = ldap_avl_find_lin( root->avl_left, data, fcmp ))
+ != NULL )
+ return( res );
+
+ if ( root->avl_right == 0 )
+ return( NULL );
+ else
+ return( ldap_avl_find_lin( root->avl_right, data, fcmp ) );
+}
+
+/* NON-REENTRANT INTERFACE */
+
+static void* *avl_list;
+static int avl_maxlist;
+static int ldap_avl_nextlist;
+
+#define AVL_GRABSIZE 100
+
+/* ARGSUSED */
+static int
+avl_buildlist( void* data, void* arg )
+{
+ static int slots;
+
+ if ( avl_list == (void* *) 0 ) {
+ avl_list = (void* *) ber_memalloc(AVL_GRABSIZE * sizeof(void*));
+ slots = AVL_GRABSIZE;
+ avl_maxlist = 0;
+ } else if ( avl_maxlist == slots ) {
+ slots += AVL_GRABSIZE;
+ avl_list = (void* *) ber_memrealloc( (char *) avl_list,
+ (unsigned) slots * sizeof(void*));
+ }
+
+ avl_list[ avl_maxlist++ ] = data;
+
+ return( 0 );
+}
+
+/*
+ * ldap_avl_getfirst() and ldap_avl_getnext() are provided as alternate tree
+ * traversal methods, to be used when a single function cannot be
+ * provided to be called with every node in the tree. ldap_avl_getfirst()
+ * traverses the tree and builds a linear list of all the nodes,
+ * returning the first node. ldap_avl_getnext() returns the next thing
+ * on the list built by ldap_avl_getfirst(). This means that ldap_avl_getfirst()
+ * can take a while, and that the tree should not be messed with while
+ * being traversed in this way, and that multiple traversals (even of
+ * different trees) cannot be active at once.
+ */
+
+void*
+ldap_avl_getfirst( Avlnode *root )
+{
+ if ( avl_list ) {
+ ber_memfree( (char *) avl_list);
+ avl_list = (void* *) 0;
+ }
+ avl_maxlist = 0;
+ ldap_avl_nextlist = 0;
+
+ if ( root == 0 )
+ return( 0 );
+
+ (void) ldap_avl_apply( root, avl_buildlist, (void*) 0, -1, AVL_INORDER );
+
+ return( avl_list[ ldap_avl_nextlist++ ] );
+}
+
+void*
+ldap_avl_getnext( void )
+{
+ if ( avl_list == 0 )
+ return( 0 );
+
+ if ( ldap_avl_nextlist == avl_maxlist ) {
+ ber_memfree( (void*) avl_list);
+ avl_list = (void* *) 0;
+ return( 0 );
+ }
+
+ return( avl_list[ ldap_avl_nextlist++ ] );
+}
+
+/* end non-reentrant code */
+
+
+int
+ldap_avl_dup_error( void* left, void* right )
+{
+ return( -1 );
+}
+
+int
+ldap_avl_dup_ok( void* left, void* right )
+{
+ return( 0 );
+}
diff --git a/libraries/libldap/bind.c b/libraries/libldap/bind.c
new file mode 100644
index 0000000..ea6fe5c
--- /dev/null
+++ b/libraries/libldap/bind.c
@@ -0,0 +1,117 @@
+/* bind.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_log.h"
+
+/*
+ * BindRequest ::= SEQUENCE {
+ * version INTEGER,
+ * name DistinguishedName, -- who
+ * authentication CHOICE {
+ * simple [0] OCTET STRING -- passwd
+ * krbv42ldap [1] OCTET STRING -- OBSOLETE
+ * krbv42dsa [2] OCTET STRING -- OBSOLETE
+ * sasl [3] SaslCredentials -- LDAPv3
+ * }
+ * }
+ *
+ * BindResponse ::= SEQUENCE {
+ * COMPONENTS OF LDAPResult,
+ * serverSaslCreds OCTET STRING OPTIONAL -- LDAPv3
+ * }
+ *
+ * (Source: RFC 2251)
+ */
+
+/*
+ * ldap_bind - bind to the ldap server (and X.500). The dn and password
+ * of the entry to which to bind are supplied, along with the authentication
+ * method to use. The msgid of the bind request is returned on success,
+ * -1 if there's trouble. ldap_result() should be called to find out the
+ * outcome of the bind request.
+ *
+ * Example:
+ * ldap_bind( ld, "cn=manager, o=university of michigan, c=us", "secret",
+ * LDAP_AUTH_SIMPLE )
+ */
+
+int
+ldap_bind( LDAP *ld, LDAP_CONST char *dn, LDAP_CONST char *passwd, int authmethod )
+{
+ Debug0( LDAP_DEBUG_TRACE, "ldap_bind\n" );
+
+ switch ( authmethod ) {
+ case LDAP_AUTH_SIMPLE:
+ return( ldap_simple_bind( ld, dn, passwd ) );
+
+ case LDAP_AUTH_SASL:
+ /* user must use ldap_sasl_bind */
+ /* FALL-THRU */
+
+ default:
+ ld->ld_errno = LDAP_AUTH_UNKNOWN;
+ return( -1 );
+ }
+}
+
+/*
+ * ldap_bind_s - bind to the ldap server (and X.500). The dn and password
+ * of the entry to which to bind are supplied, along with the authentication
+ * method to use. This routine just calls whichever bind routine is
+ * appropriate and returns the result of the bind (e.g. LDAP_SUCCESS or
+ * some other error indication).
+ *
+ * Examples:
+ * ldap_bind_s( ld, "cn=manager, o=university of michigan, c=us",
+ * "secret", LDAP_AUTH_SIMPLE )
+ * ldap_bind_s( ld, "cn=manager, o=university of michigan, c=us",
+ * NULL, LDAP_AUTH_KRBV4 )
+ */
+int
+ldap_bind_s(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *passwd,
+ int authmethod )
+{
+ Debug0( LDAP_DEBUG_TRACE, "ldap_bind_s\n" );
+
+ switch ( authmethod ) {
+ case LDAP_AUTH_SIMPLE:
+ return( ldap_simple_bind_s( ld, dn, passwd ) );
+
+ case LDAP_AUTH_SASL:
+ /* user must use ldap_sasl_bind */
+ /* FALL-THRU */
+
+ default:
+ return( ld->ld_errno = LDAP_AUTH_UNKNOWN );
+ }
+}
diff --git a/libraries/libldap/cancel.c b/libraries/libldap/cancel.c
new file mode 100644
index 0000000..cfb4ba1
--- /dev/null
+++ b/libraries/libldap/cancel.c
@@ -0,0 +1,76 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This program was originally developed by Kurt D. Zeilenga for inclusion
+ * in OpenLDAP Software.
+ */
+
+/*
+ * LDAPv3 Cancel Operation Request
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_log.h"
+
+int
+ldap_cancel(
+ LDAP *ld,
+ int cancelid,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ BerElement *cancelidber = NULL;
+ struct berval cancelidvalp = { 0, NULL };
+ int rc;
+
+ cancelidber = ber_alloc_t( LBER_USE_DER );
+ ber_printf( cancelidber, "{i}", cancelid );
+ ber_flatten2( cancelidber, &cancelidvalp, 0 );
+ rc = ldap_extended_operation( ld, LDAP_EXOP_CANCEL,
+ &cancelidvalp, sctrls, cctrls, msgidp );
+ ber_free( cancelidber, 1 );
+ return rc;
+}
+
+int
+ldap_cancel_s(
+ LDAP *ld,
+ int cancelid,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ BerElement *cancelidber = NULL;
+ struct berval cancelidvalp = { 0, NULL };
+ int rc;
+
+ cancelidber = ber_alloc_t( LBER_USE_DER );
+ ber_printf( cancelidber, "{i}", cancelid );
+ ber_flatten2( cancelidber, &cancelidvalp, 0 );
+ rc = ldap_extended_operation_s( ld, LDAP_EXOP_CANCEL,
+ &cancelidvalp, sctrls, cctrls, NULL, NULL );
+ ber_free( cancelidber, 1 );
+ return rc;
+}
+
diff --git a/libraries/libldap/charray.c b/libraries/libldap/charray.c
new file mode 100644
index 0000000..856843f
--- /dev/null
+++ b/libraries/libldap/charray.c
@@ -0,0 +1,275 @@
+/* charray.c - routines for dealing with char * arrays */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "ldap-int.h"
+
+int
+ldap_charray_add(
+ char ***a,
+ const char *s
+)
+{
+ int n;
+
+ if ( *a == NULL ) {
+ *a = (char **) LDAP_MALLOC( 2 * sizeof(char *) );
+ n = 0;
+
+ if( *a == NULL ) {
+ return -1;
+ }
+
+ } else {
+ char **new;
+
+ for ( n = 0; *a != NULL && (*a)[n] != NULL; n++ ) {
+ ; /* NULL */
+ }
+
+ new = (char **) LDAP_REALLOC( (char *) *a,
+ (n + 2) * sizeof(char *) );
+
+ if( new == NULL ) {
+ /* caller is required to call ldap_charray_free(*a) */
+ return -1;
+ }
+
+ *a = new;
+ }
+
+ (*a)[n] = LDAP_STRDUP(s);
+
+ if( (*a)[n] == NULL ) {
+ return 1;
+ }
+
+ (*a)[++n] = NULL;
+
+ return 0;
+}
+
+int
+ldap_charray_merge(
+ char ***a,
+ char **s
+)
+{
+ int i, n, nn;
+ char **aa;
+
+ for ( n = 0; *a != NULL && (*a)[n] != NULL; n++ ) {
+ ; /* NULL */
+ }
+ for ( nn = 0; s[nn] != NULL; nn++ ) {
+ ; /* NULL */
+ }
+
+ aa = (char **) LDAP_REALLOC( (char *) *a, (n + nn + 1) * sizeof(char *) );
+
+ if( aa == NULL ) {
+ return -1;
+ }
+
+ *a = aa;
+
+ for ( i = 0; i < nn; i++ ) {
+ (*a)[n + i] = LDAP_STRDUP(s[i]);
+
+ if( (*a)[n + i] == NULL ) {
+ for( --i ; i >= 0 ; i-- ) {
+ LDAP_FREE( (*a)[n + i] );
+ (*a)[n + i] = NULL;
+ }
+ return -1;
+ }
+ }
+
+ (*a)[n + nn] = NULL;
+ return 0;
+}
+
+void
+ldap_charray_free( char **a )
+{
+ char **p;
+
+ if ( a == NULL ) {
+ return;
+ }
+
+ for ( p = a; *p != NULL; p++ ) {
+ if ( *p != NULL ) {
+ LDAP_FREE( *p );
+ }
+ }
+
+ LDAP_FREE( (char *) a );
+}
+
+int
+ldap_charray_inlist(
+ char **a,
+ const char *s
+)
+{
+ int i;
+
+ if( a == NULL ) return 0;
+
+ for ( i=0; a[i] != NULL; i++ ) {
+ if ( strcasecmp( s, a[i] ) == 0 ) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+char **
+ldap_charray_dup( char **a )
+{
+ int i;
+ char **new;
+
+ for ( i = 0; a[i] != NULL; i++ )
+ ; /* NULL */
+
+ new = (char **) LDAP_MALLOC( (i + 1) * sizeof(char *) );
+
+ if( new == NULL ) {
+ return NULL;
+ }
+
+ for ( i = 0; a[i] != NULL; i++ ) {
+ new[i] = LDAP_STRDUP( a[i] );
+
+ if( new[i] == NULL ) {
+ for( --i ; i >= 0 ; i-- ) {
+ LDAP_FREE( new[i] );
+ }
+ LDAP_FREE( new );
+ return NULL;
+ }
+ }
+ new[i] = NULL;
+
+ return( new );
+}
+
+char **
+ldap_str2charray( const char *str_in, const char *brkstr )
+{
+ char **res;
+ char *str, *s;
+ char *lasts;
+ int i;
+
+ /* protect the input string from strtok */
+ str = LDAP_STRDUP( str_in );
+ if( str == NULL ) {
+ return NULL;
+ }
+
+ i = 1;
+ for ( s = str; ; LDAP_UTF8_INCR(s) ) {
+ s = ldap_utf8_strpbrk( s, brkstr );
+ if ( !s ) break;
+ i++;
+ }
+
+ res = (char **) LDAP_MALLOC( (i + 1) * sizeof(char *) );
+
+ if( res == NULL ) {
+ LDAP_FREE( str );
+ return NULL;
+ }
+
+ i = 0;
+
+ for ( s = ldap_utf8_strtok( str, brkstr, &lasts );
+ s != NULL;
+ s = ldap_utf8_strtok( NULL, brkstr, &lasts ) )
+ {
+ res[i] = LDAP_STRDUP( s );
+
+ if(res[i] == NULL) {
+ for( --i ; i >= 0 ; i-- ) {
+ LDAP_FREE( res[i] );
+ }
+ LDAP_FREE( res );
+ LDAP_FREE( str );
+ return NULL;
+ }
+
+ i++;
+ }
+
+ res[i] = NULL;
+
+ LDAP_FREE( str );
+ return( res );
+}
+
+char * ldap_charray2str( char **a, const char *sep )
+{
+ char *s, **v, *p;
+ int len;
+ int slen;
+
+ if( sep == NULL ) sep = " ";
+
+ slen = strlen( sep );
+ len = 0;
+
+ for ( v = a; *v != NULL; v++ ) {
+ len += strlen( *v ) + slen;
+ }
+
+ if ( len == 0 ) {
+ return NULL;
+ }
+
+ /* trim extra sep len */
+ len -= slen;
+
+ s = LDAP_MALLOC ( len + 1 );
+
+ if ( s == NULL ) {
+ return NULL;
+ }
+
+ p = s;
+ for ( v = a; *v != NULL; v++ ) {
+ if ( v != a ) {
+ strncpy( p, sep, slen );
+ p += slen;
+ }
+
+ len = strlen( *v );
+ strncpy( p, *v, len );
+ p += len;
+ }
+
+ *p = '\0';
+ return s;
+}
diff --git a/libraries/libldap/compare.c b/libraries/libldap/compare.c
new file mode 100644
index 0000000..86285c6
--- /dev/null
+++ b/libraries/libldap/compare.c
@@ -0,0 +1,197 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_log.h"
+
+/* The compare request looks like this:
+ * CompareRequest ::= SEQUENCE {
+ * entry DistinguishedName,
+ * ava SEQUENCE {
+ * type AttributeType,
+ * value AttributeValue
+ * }
+ * }
+ */
+
+BerElement *
+ldap_build_compare_req(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *attr,
+ struct berval *bvalue,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ BerElement *ber;
+ int rc;
+
+ /* create a message to send */
+ if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
+ return( NULL );
+ }
+
+ LDAP_NEXT_MSGID(ld, *msgidp);
+ rc = ber_printf( ber, "{it{s{sON}N}", /* '}' */
+ *msgidp,
+ LDAP_REQ_COMPARE, dn, attr, bvalue );
+ if ( rc == -1 )
+ {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ /* Put Server Controls */
+ if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ if( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ return( ber );
+}
+
+/*
+ * ldap_compare_ext - perform an ldap extended compare operation. The dn
+ * of the entry to compare to and the attribute and value to compare (in
+ * attr and value) are supplied. The msgid of the response is returned.
+ *
+ * Example:
+ * struct berval bvalue = { "secret", sizeof("secret")-1 };
+ * rc = ldap_compare( ld, "c=us@cn=bob",
+ * "userPassword", &bvalue,
+ * sctrl, cctrl, &msgid )
+ */
+int
+ldap_compare_ext(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *attr,
+ struct berval *bvalue,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ int rc;
+ BerElement *ber;
+ ber_int_t id;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_compare\n" );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( dn != NULL );
+ assert( attr != NULL );
+ assert( msgidp != NULL );
+
+ /* check client controls */
+ rc = ldap_int_client_controls( ld, cctrls );
+ if( rc != LDAP_SUCCESS ) return rc;
+
+ ber = ldap_build_compare_req(
+ ld, dn, attr, bvalue, sctrls, cctrls, &id );
+ if( !ber )
+ return ld->ld_errno;
+
+ /* send the message */
+ *msgidp = ldap_send_initial_request( ld, LDAP_REQ_COMPARE, dn, ber, id );
+ return ( *msgidp < 0 ? ld->ld_errno : LDAP_SUCCESS );
+}
+
+/*
+ * ldap_compare_ext - perform an ldap extended compare operation. The dn
+ * of the entry to compare to and the attribute and value to compare (in
+ * attr and value) are supplied. The msgid of the response is returned.
+ *
+ * Example:
+ * msgid = ldap_compare( ld, "c=us@cn=bob", "userPassword", "secret" )
+ */
+int
+ldap_compare(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *attr,
+ LDAP_CONST char *value )
+{
+ int msgid;
+ struct berval bvalue;
+
+ assert( value != NULL );
+
+ bvalue.bv_val = (char *) value;
+ bvalue.bv_len = (value == NULL) ? 0 : strlen( value );
+
+ return ldap_compare_ext( ld, dn, attr, &bvalue, NULL, NULL, &msgid ) == LDAP_SUCCESS
+ ? msgid : -1;
+}
+
+int
+ldap_compare_ext_s(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *attr,
+ struct berval *bvalue,
+ LDAPControl **sctrl,
+ LDAPControl **cctrl )
+{
+ int rc;
+ int msgid;
+ LDAPMessage *res;
+
+ rc = ldap_compare_ext( ld, dn, attr, bvalue, sctrl, cctrl, &msgid );
+
+ if ( rc != LDAP_SUCCESS )
+ return( rc );
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res )
+ return( ld->ld_errno );
+
+ return( ldap_result2error( ld, res, 1 ) );
+}
+
+int
+ldap_compare_s(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *attr,
+ LDAP_CONST char *value )
+{
+ struct berval bvalue;
+
+ assert( value != NULL );
+
+ bvalue.bv_val = (char *) value;
+ bvalue.bv_len = (value == NULL) ? 0 : strlen( value );
+
+ return ldap_compare_ext_s( ld, dn, attr, &bvalue, NULL, NULL );
+}
diff --git a/libraries/libldap/controls.c b/libraries/libldap/controls.c
new file mode 100644
index 0000000..125707e
--- /dev/null
+++ b/libraries/libldap/controls.c
@@ -0,0 +1,552 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* This notice applies to changes, created by or for Novell, Inc.,
+ * to preexisting works for which notices appear elsewhere in this file.
+ *
+ * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
+ *
+ * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
+ * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
+ * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
+ * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
+ * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
+ * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
+ * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
+ * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
+ *---
+ * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
+ * can be found in the file "build/LICENSE-2.0.1" in this distribution
+ * of OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <ac/stdlib.h>
+
+#include <ac/time.h>
+#include <ac/string.h>
+
+#include "ldap-int.h"
+
+/* LDAPv3 Controls (RFC 4511)
+ *
+ * Controls ::= SEQUENCE OF control Control
+ *
+ * Control ::= SEQUENCE {
+ * controlType LDAPOID,
+ * criticality BOOLEAN DEFAULT FALSE,
+ * controlValue OCTET STRING OPTIONAL
+ * }
+ */
+
+int
+ldap_pvt_put_control(
+ const LDAPControl *c,
+ BerElement *ber )
+{
+ if ( ber_printf( ber, "{s" /*}*/, c->ldctl_oid ) == -1 ) {
+ return LDAP_ENCODING_ERROR;
+ }
+
+ if ( c->ldctl_iscritical /* only if true */
+ && ( ber_printf( ber, "b",
+ (ber_int_t) c->ldctl_iscritical ) == -1 ) )
+ {
+ return LDAP_ENCODING_ERROR;
+ }
+
+ if ( !BER_BVISNULL( &c->ldctl_value ) /* only if we have a value */
+ && ( ber_printf( ber, "O", &c->ldctl_value ) == -1 ) )
+ {
+ return LDAP_ENCODING_ERROR;
+ }
+
+ if ( ber_printf( ber, /*{*/"N}" ) == -1 ) {
+ return LDAP_ENCODING_ERROR;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+
+/*
+ * ldap_int_put_controls
+ */
+
+int
+ldap_int_put_controls(
+ LDAP *ld,
+ LDAPControl *const *ctrls,
+ BerElement *ber )
+{
+ LDAPControl *const *c;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( ber != NULL );
+
+ if( ctrls == NULL ) {
+ /* use default server controls */
+ ctrls = ld->ld_sctrls;
+ }
+
+ if( ctrls == NULL || *ctrls == NULL ) {
+ return LDAP_SUCCESS;
+ }
+
+ if ( ld->ld_version < LDAP_VERSION3 ) {
+ /* LDAPv2 doesn't support controls,
+ * error if any control is critical
+ */
+ for( c = ctrls ; *c != NULL; c++ ) {
+ if( (*c)->ldctl_iscritical ) {
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ return ld->ld_errno;
+ }
+ }
+
+ return LDAP_SUCCESS;
+ }
+
+ /* Controls are encoded as a sequence of sequences */
+ if( ber_printf( ber, "t{"/*}*/, LDAP_TAG_CONTROLS ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ return ld->ld_errno;
+ }
+
+ for( c = ctrls ; *c != NULL; c++ ) {
+ ld->ld_errno = ldap_pvt_put_control( *c, ber );
+ if ( ld->ld_errno != LDAP_SUCCESS ) {
+ return ld->ld_errno;
+ }
+ }
+
+
+ if( ber_printf( ber, /*{*/ "}" ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ return ld->ld_errno;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int ldap_pvt_get_controls(
+ BerElement *ber,
+ LDAPControl ***ctrls )
+{
+ int nctrls;
+ ber_tag_t tag;
+ ber_len_t len;
+ char *opaque;
+
+ assert( ber != NULL );
+
+ if( ctrls == NULL ) {
+ return LDAP_SUCCESS;
+ }
+ *ctrls = NULL;
+
+ len = ber_pvt_ber_remaining( ber );
+
+ if( len == 0) {
+ /* no controls */
+ return LDAP_SUCCESS;
+ }
+
+ if(( tag = ber_peek_tag( ber, &len )) != LDAP_TAG_CONTROLS ) {
+ if( tag == LBER_ERROR ) {
+ /* decoding error */
+ return LDAP_DECODING_ERROR;
+ }
+
+ /* ignore unexpected input */
+ return LDAP_SUCCESS;
+ }
+
+ /* set through each element */
+ nctrls = 0;
+ *ctrls = LDAP_MALLOC( 1 * sizeof(LDAPControl *) );
+
+ if( *ctrls == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+
+ *ctrls[nctrls] = NULL;
+
+ for( tag = ber_first_element( ber, &len, &opaque );
+ tag != LBER_ERROR;
+ tag = ber_next_element( ber, &len, opaque ) )
+ {
+ LDAPControl *tctrl;
+ LDAPControl **tctrls;
+
+ tctrl = LDAP_CALLOC( 1, sizeof(LDAPControl) );
+
+ /* allocate pointer space for current controls (nctrls)
+ * + this control + extra NULL
+ */
+ tctrls = (tctrl == NULL) ? NULL :
+ LDAP_REALLOC(*ctrls, (nctrls+2) * sizeof(LDAPControl *));
+
+ if( tctrls == NULL ) {
+ /* one of the above allocation failed */
+
+ if( tctrl != NULL ) {
+ LDAP_FREE( tctrl );
+ }
+
+ ldap_controls_free(*ctrls);
+ *ctrls = NULL;
+
+ return LDAP_NO_MEMORY;
+ }
+
+
+ tctrls[nctrls++] = tctrl;
+ tctrls[nctrls] = NULL;
+
+ tag = ber_scanf( ber, "{a" /*}*/, &tctrl->ldctl_oid );
+
+ if( tag == LBER_ERROR ) {
+ *ctrls = NULL;
+ ldap_controls_free( tctrls );
+ return LDAP_DECODING_ERROR;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+
+ if( tag == LBER_BOOLEAN ) {
+ ber_int_t crit;
+ tag = ber_scanf( ber, "b", &crit );
+ tctrl->ldctl_iscritical = crit ? (char) 0 : (char) ~0;
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if( tag == LBER_OCTETSTRING ) {
+ tag = ber_scanf( ber, "o", &tctrl->ldctl_value );
+ } else {
+ BER_BVZERO( &tctrl->ldctl_value );
+ }
+
+ *ctrls = tctrls;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Free a LDAPControl
+ */
+void
+ldap_control_free( LDAPControl *c )
+{
+ LDAP_MEMORY_DEBUG_ASSERT( c != NULL );
+
+ if ( c != NULL ) {
+ if( c->ldctl_oid != NULL) {
+ LDAP_FREE( c->ldctl_oid );
+ }
+
+ if( c->ldctl_value.bv_val != NULL ) {
+ LDAP_FREE( c->ldctl_value.bv_val );
+ }
+
+ LDAP_FREE( c );
+ }
+}
+
+/*
+ * Free an array of LDAPControl's
+ */
+void
+ldap_controls_free( LDAPControl **controls )
+{
+ LDAP_MEMORY_DEBUG_ASSERT( controls != NULL );
+
+ if ( controls != NULL ) {
+ int i;
+
+ for( i=0; controls[i] != NULL; i++) {
+ ldap_control_free( controls[i] );
+ }
+
+ LDAP_FREE( controls );
+ }
+}
+
+/*
+ * Duplicate an array of LDAPControl
+ */
+LDAPControl **
+ldap_controls_dup( LDAPControl *const *controls )
+{
+ LDAPControl **new;
+ int i;
+
+ if ( controls == NULL ) {
+ return NULL;
+ }
+
+ /* count the controls */
+ for(i=0; controls[i] != NULL; i++) /* empty */ ;
+
+ if( i < 1 ) {
+ /* no controls to duplicate */
+ return NULL;
+ }
+
+ new = (LDAPControl **) LDAP_MALLOC( (i+1) * sizeof(LDAPControl *) );
+
+ if( new == NULL ) {
+ /* memory allocation failure */
+ return NULL;
+ }
+
+ /* duplicate the controls */
+ for(i=0; controls[i] != NULL; i++) {
+ new[i] = ldap_control_dup( controls[i] );
+
+ if( new[i] == NULL ) {
+ ldap_controls_free( new );
+ return NULL;
+ }
+ }
+
+ new[i] = NULL;
+
+ return new;
+}
+
+/*
+ * Duplicate a LDAPControl
+ */
+LDAPControl *
+ldap_control_dup( const LDAPControl *c )
+{
+ LDAPControl *new;
+
+ if ( c == NULL || c->ldctl_oid == NULL ) {
+ return NULL;
+ }
+
+ new = (LDAPControl *) LDAP_MALLOC( sizeof(LDAPControl) );
+
+ if( new == NULL ) {
+ return NULL;
+ }
+
+ new->ldctl_oid = LDAP_STRDUP( c->ldctl_oid );
+
+ if(new->ldctl_oid == NULL) {
+ LDAP_FREE( new );
+ return NULL;
+ }
+
+ if( c->ldctl_value.bv_val != NULL ) {
+ new->ldctl_value.bv_val =
+ (char *) LDAP_MALLOC( c->ldctl_value.bv_len + 1 );
+
+ if(new->ldctl_value.bv_val == NULL) {
+ if(new->ldctl_oid != NULL) {
+ LDAP_FREE( new->ldctl_oid );
+ }
+ LDAP_FREE( new );
+ return NULL;
+ }
+
+ new->ldctl_value.bv_len = c->ldctl_value.bv_len;
+
+ AC_MEMCPY( new->ldctl_value.bv_val, c->ldctl_value.bv_val,
+ c->ldctl_value.bv_len );
+
+ new->ldctl_value.bv_val[new->ldctl_value.bv_len] = '\0';
+
+ } else {
+ new->ldctl_value.bv_len = 0;
+ new->ldctl_value.bv_val = NULL;
+ }
+
+ new->ldctl_iscritical = c->ldctl_iscritical;
+ return new;
+}
+
+/*
+ * Find a LDAPControl - deprecated
+ */
+LDAPControl *
+ldap_find_control(
+ LDAP_CONST char *oid,
+ LDAPControl **ctrls )
+{
+ if( ctrls == NULL || *ctrls == NULL ) {
+ return NULL;
+ }
+
+ for( ; *ctrls != NULL; ctrls++ ) {
+ if( strcmp( (*ctrls)->ldctl_oid, oid ) == 0 ) {
+ return *ctrls;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Find a LDAPControl
+ */
+LDAPControl *
+ldap_control_find(
+ LDAP_CONST char *oid,
+ LDAPControl **ctrls,
+ LDAPControl ***nextctrlp )
+{
+ if ( oid == NULL || ctrls == NULL || *ctrls == NULL ) {
+ return NULL;
+ }
+
+ for( ; *ctrls != NULL; ctrls++ ) {
+ if( strcmp( (*ctrls)->ldctl_oid, oid ) == 0 ) {
+ if ( nextctrlp != NULL ) {
+ *nextctrlp = ctrls + 1;
+ }
+
+ return *ctrls;
+ }
+ }
+
+ if ( nextctrlp != NULL ) {
+ *nextctrlp = NULL;
+ }
+
+ return NULL;
+}
+
+/*
+ * Create a LDAPControl, optionally from ber - deprecated
+ */
+int
+ldap_create_control(
+ LDAP_CONST char *requestOID,
+ BerElement *ber,
+ int iscritical,
+ LDAPControl **ctrlp )
+{
+ LDAPControl *ctrl;
+
+ assert( requestOID != NULL );
+ assert( ctrlp != NULL );
+
+ ctrl = (LDAPControl *) LDAP_MALLOC( sizeof(LDAPControl) );
+ if ( ctrl == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+
+ BER_BVZERO(&ctrl->ldctl_value);
+ if ( ber && ( ber_flatten2( ber, &ctrl->ldctl_value, 1 ) == -1 )) {
+ LDAP_FREE( ctrl );
+ return LDAP_NO_MEMORY;
+ }
+
+ ctrl->ldctl_oid = LDAP_STRDUP( requestOID );
+ ctrl->ldctl_iscritical = iscritical;
+
+ if ( requestOID != NULL && ctrl->ldctl_oid == NULL ) {
+ ldap_control_free( ctrl );
+ return LDAP_NO_MEMORY;
+ }
+
+ *ctrlp = ctrl;
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Create a LDAPControl, optionally from value
+ */
+int
+ldap_control_create(
+ LDAP_CONST char *requestOID,
+ int iscritical,
+ struct berval *value,
+ int dupval,
+ LDAPControl **ctrlp )
+{
+ LDAPControl *ctrl;
+
+ assert( requestOID != NULL );
+ assert( ctrlp != NULL );
+
+ ctrl = (LDAPControl *) LDAP_CALLOC( sizeof(LDAPControl), 1 );
+ if ( ctrl == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+
+ ctrl->ldctl_iscritical = iscritical;
+ if ( requestOID != NULL ) {
+ ctrl->ldctl_oid = LDAP_STRDUP( requestOID );
+ if ( ctrl->ldctl_oid == NULL ) {
+ ldap_control_free( ctrl );
+ return LDAP_NO_MEMORY;
+ }
+ }
+
+ if ( value && !BER_BVISNULL( value ) ) {
+ if ( dupval ) {
+ ber_dupbv( &ctrl->ldctl_value, value );
+ if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
+ ldap_control_free( ctrl );
+ return LDAP_NO_MEMORY;
+ }
+
+ } else {
+ ctrl->ldctl_value = *value;
+ }
+ }
+
+ *ctrlp = ctrl;
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * check for critical client controls and bitch if present
+ * if we ever support critical controls, we'll have to
+ * find a means for maintaining per API call control
+ * information.
+ */
+int ldap_int_client_controls( LDAP *ld, LDAPControl **ctrls )
+{
+ LDAPControl *const *c;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ if( ctrls == NULL ) {
+ /* use default client controls */
+ ctrls = ld->ld_cctrls;
+ }
+
+ if( ctrls == NULL || *ctrls == NULL ) {
+ return LDAP_SUCCESS;
+ }
+
+ for( c = ctrls ; *c != NULL; c++ ) {
+ if( (*c)->ldctl_iscritical ) {
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ return ld->ld_errno;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
diff --git a/libraries/libldap/cyrus.c b/libraries/libldap/cyrus.c
new file mode 100644
index 0000000..cc925d3
--- /dev/null
+++ b/libraries/libldap/cyrus.c
@@ -0,0 +1,1335 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include "ldap-int.h"
+
+#ifdef HAVE_CYRUS_SASL
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/errno.h>
+#include <ac/ctype.h>
+#include <ac/unistd.h>
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#ifndef INT_MAX
+#define INT_MAX 2147483647 /* 32 bit signed max */
+#endif
+
+#if !defined(HOST_NAME_MAX) && defined(_POSIX_HOST_NAME_MAX)
+#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
+#endif
+
+#ifdef HAVE_SASL_SASL_H
+#include <sasl/sasl.h>
+#else
+#include <sasl.h>
+#endif
+
+#if SASL_VERSION_MAJOR >= 2
+#define SASL_CONST const
+#else
+#define SASL_CONST
+#endif
+
+/*
+* Various Cyrus SASL related stuff.
+*/
+
+static const sasl_callback_t client_callbacks[] = {
+#ifdef SASL_CB_GETREALM
+ { SASL_CB_GETREALM, NULL, NULL },
+#endif
+ { SASL_CB_USER, NULL, NULL },
+ { SASL_CB_AUTHNAME, NULL, NULL },
+ { SASL_CB_PASS, NULL, NULL },
+ { SASL_CB_ECHOPROMPT, NULL, NULL },
+ { SASL_CB_NOECHOPROMPT, NULL, NULL },
+ { SASL_CB_LIST_END, NULL, NULL }
+};
+
+/*
+ * ldap_int_initialize is responsible for calling this only once.
+ */
+int ldap_int_sasl_init( void )
+{
+#ifdef HAVE_SASL_VERSION
+ /* stringify the version number, sasl.h doesn't do it for us */
+#define VSTR0(maj, min, pat) #maj "." #min "." #pat
+#define VSTR(maj, min, pat) VSTR0(maj, min, pat)
+#define SASL_VERSION_STRING VSTR(SASL_VERSION_MAJOR, SASL_VERSION_MINOR, \
+ SASL_VERSION_STEP)
+ { int rc;
+ sasl_version( NULL, &rc );
+ if ( ((rc >> 16) != ((SASL_VERSION_MAJOR << 8)|SASL_VERSION_MINOR)) ||
+ (rc & 0xffff) < SASL_VERSION_STEP) {
+ char version[sizeof("xxx.xxx.xxxxx")];
+ sprintf( version, "%u.%d.%d", (unsigned)rc >> 24, (rc >> 16) & 0xff,
+ rc & 0xffff );
+
+ Debug1( LDAP_DEBUG_ANY,
+ "ldap_int_sasl_init: SASL library version mismatch:"
+ " expected " SASL_VERSION_STRING ","
+ " got %s\n", version );
+ return -1;
+ }
+ }
+#endif
+
+/* SASL 2 takes care of its own memory completely internally */
+#if SASL_VERSION_MAJOR < 2 && !defined(CSRIMALLOC)
+ sasl_set_alloc(
+ ber_memalloc,
+ ber_memcalloc,
+ ber_memrealloc,
+ ber_memfree );
+#endif /* CSRIMALLOC */
+
+#ifdef LDAP_R_COMPILE
+ sasl_set_mutex(
+ ldap_pvt_sasl_mutex_new,
+ ldap_pvt_sasl_mutex_lock,
+ ldap_pvt_sasl_mutex_unlock,
+ ldap_pvt_sasl_mutex_dispose );
+#endif
+
+ if ( sasl_client_init( NULL ) == SASL_OK ) {
+ return 0;
+ }
+
+#if SASL_VERSION_MAJOR < 2
+ /* A no-op to make sure we link with Cyrus 1.5 */
+ sasl_client_auth( NULL, NULL, NULL, 0, NULL, NULL );
+#endif
+ return -1;
+}
+
+static void
+sb_sasl_cyrus_init(
+ struct sb_sasl_generic_data *p,
+ ber_len_t *min_send,
+ ber_len_t *max_send,
+ ber_len_t *max_recv)
+{
+ sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private;
+ ber_len_t maxbuf;
+
+ sasl_getprop( sasl_context, SASL_MAXOUTBUF,
+ (SASL_CONST void **)(char *) &maxbuf );
+
+ *min_send = SASL_MIN_BUFF_SIZE;
+ *max_send = maxbuf;
+ *max_recv = SASL_MAX_BUFF_SIZE;
+}
+
+static ber_int_t
+sb_sasl_cyrus_encode(
+ struct sb_sasl_generic_data *p,
+ unsigned char *buf,
+ ber_len_t len,
+ Sockbuf_Buf *dst)
+{
+ sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private;
+ ber_int_t ret;
+ unsigned tmpsize = dst->buf_size;
+
+ ret = sasl_encode( sasl_context, (char *)buf, len,
+ (SASL_CONST char **)&dst->buf_base,
+ &tmpsize );
+
+ dst->buf_size = tmpsize;
+ dst->buf_end = dst->buf_size;
+
+ if ( ret != SASL_OK ) {
+ ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
+ "sb_sasl_cyrus_encode: failed to encode packet: %s\n",
+ sasl_errstring( ret, NULL, NULL ) );
+ return -1;
+ }
+
+ return 0;
+}
+
+static ber_int_t
+sb_sasl_cyrus_decode(
+ struct sb_sasl_generic_data *p,
+ const Sockbuf_Buf *src,
+ Sockbuf_Buf *dst)
+{
+ sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private;
+ ber_int_t ret;
+ unsigned tmpsize = dst->buf_size;
+
+ ret = sasl_decode( sasl_context,
+ src->buf_base, src->buf_end,
+ (SASL_CONST char **)&dst->buf_base,
+ (unsigned *)&tmpsize );
+
+
+ dst->buf_size = tmpsize;
+ dst->buf_end = dst->buf_size;
+
+ if ( ret != SASL_OK ) {
+ ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
+ "sb_sasl_cyrus_decode: failed to decode packet: %s\n",
+ sasl_errstring( ret, NULL, NULL ) );
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+sb_sasl_cyrus_reset_buf(
+ struct sb_sasl_generic_data *p,
+ Sockbuf_Buf *buf)
+{
+#if SASL_VERSION_MAJOR >= 2
+ ber_pvt_sb_buf_init( buf );
+#else
+ ber_pvt_sb_buf_destroy( buf );
+#endif
+}
+
+static void
+sb_sasl_cyrus_fini(
+ struct sb_sasl_generic_data *p)
+{
+#if SASL_VERSION_MAJOR >= 2
+ /*
+ * SASLv2 encode/decode buffers are managed by
+ * libsasl2. Ensure they are not freed by liblber.
+ */
+ p->buf_in.buf_base = NULL;
+ p->buf_out.buf_base = NULL;
+#endif
+}
+
+static const struct sb_sasl_generic_ops sb_sasl_cyrus_ops = {
+ sb_sasl_cyrus_init,
+ sb_sasl_cyrus_encode,
+ sb_sasl_cyrus_decode,
+ sb_sasl_cyrus_reset_buf,
+ sb_sasl_cyrus_fini
+ };
+
+int ldap_pvt_sasl_install( Sockbuf *sb, void *ctx_arg )
+{
+ struct sb_sasl_generic_install install_arg;
+
+ install_arg.ops = &sb_sasl_cyrus_ops;
+ install_arg.ops_private = ctx_arg;
+
+ return ldap_pvt_sasl_generic_install( sb, &install_arg );
+}
+
+void ldap_pvt_sasl_remove( Sockbuf *sb )
+{
+ ldap_pvt_sasl_generic_remove( sb );
+}
+
+static int
+sasl_err2ldap( int saslerr )
+{
+ int rc;
+
+ /* map SASL errors to LDAP API errors returned by:
+ * sasl_client_new()
+ * SASL_OK, SASL_NOMECH, SASL_NOMEM
+ * sasl_client_start()
+ * SASL_OK, SASL_NOMECH, SASL_NOMEM, SASL_INTERACT
+ * sasl_client_step()
+ * SASL_OK, SASL_INTERACT, SASL_BADPROT, SASL_BADSERV
+ */
+
+ switch (saslerr) {
+ case SASL_CONTINUE:
+ rc = LDAP_MORE_RESULTS_TO_RETURN;
+ break;
+ case SASL_INTERACT:
+ rc = LDAP_LOCAL_ERROR;
+ break;
+ case SASL_OK:
+ rc = LDAP_SUCCESS;
+ break;
+ case SASL_NOMEM:
+ rc = LDAP_NO_MEMORY;
+ break;
+ case SASL_NOMECH:
+ rc = LDAP_AUTH_UNKNOWN;
+ break;
+ case SASL_BADPROT:
+ rc = LDAP_DECODING_ERROR;
+ break;
+ case SASL_BADSERV:
+ rc = LDAP_AUTH_UNKNOWN;
+ break;
+
+ /* other codes */
+ case SASL_BADAUTH:
+ rc = LDAP_AUTH_UNKNOWN;
+ break;
+ case SASL_NOAUTHZ:
+ rc = LDAP_PARAM_ERROR;
+ break;
+ case SASL_FAIL:
+ rc = LDAP_LOCAL_ERROR;
+ break;
+ case SASL_TOOWEAK:
+ case SASL_ENCRYPT:
+ rc = LDAP_AUTH_UNKNOWN;
+ break;
+ default:
+ rc = LDAP_LOCAL_ERROR;
+ break;
+ }
+
+ assert( rc == LDAP_SUCCESS || LDAP_API_ERROR( rc ) );
+ return rc;
+}
+
+int
+ldap_int_sasl_open(
+ LDAP *ld,
+ LDAPConn *lc,
+ const char * host )
+{
+ int rc;
+ sasl_conn_t *ctx;
+
+ assert( lc->lconn_sasl_authctx == NULL );
+
+ if ( host == NULL ) {
+ ld->ld_errno = LDAP_LOCAL_ERROR;
+ return ld->ld_errno;
+ }
+
+#if SASL_VERSION_MAJOR >= 2
+ rc = sasl_client_new( "ldap", host, NULL, NULL,
+ client_callbacks, 0, &ctx );
+#else
+ rc = sasl_client_new( "ldap", host, client_callbacks,
+ SASL_SECURITY_LAYER, &ctx );
+#endif
+
+ if ( rc != SASL_OK ) {
+ ld->ld_errno = sasl_err2ldap( rc );
+ return ld->ld_errno;
+ }
+
+ Debug1( LDAP_DEBUG_TRACE, "ldap_int_sasl_open: host=%s\n",
+ host );
+
+ lc->lconn_sasl_authctx = ctx;
+
+ return LDAP_SUCCESS;
+}
+
+int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc )
+{
+ sasl_conn_t *ctx = lc->lconn_sasl_authctx;
+
+ if( ctx != NULL ) {
+ sasl_dispose( &ctx );
+ if ( lc->lconn_sasl_sockctx &&
+ lc->lconn_sasl_authctx != lc->lconn_sasl_sockctx ) {
+ ctx = lc->lconn_sasl_sockctx;
+ sasl_dispose( &ctx );
+ }
+ lc->lconn_sasl_sockctx = NULL;
+ lc->lconn_sasl_authctx = NULL;
+ }
+ if( lc->lconn_sasl_cbind ) {
+ ldap_memfree( lc->lconn_sasl_cbind );
+ lc->lconn_sasl_cbind = NULL;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int ldap_pvt_sasl_cbinding_parse( const char *arg )
+{
+ int i = -1;
+
+ if ( strcasecmp(arg, "none") == 0 )
+ i = LDAP_OPT_X_SASL_CBINDING_NONE;
+ else if ( strcasecmp(arg, "tls-unique") == 0 )
+ i = LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE;
+ else if ( strcasecmp(arg, "tls-endpoint") == 0 )
+ i = LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT;
+
+ return i;
+}
+
+void *ldap_pvt_sasl_cbinding( void *ssl, int type, int is_server )
+{
+#if defined(SASL_CHANNEL_BINDING) && defined(HAVE_TLS)
+ char unique_prefix[] = "tls-unique:";
+ char endpoint_prefix[] = "tls-server-end-point:";
+ char cbinding[ 64 ];
+ struct berval cbv = { 64, cbinding };
+ void *cb_data; /* used since cb->data is const* */
+ sasl_channel_binding_t *cb;
+ char *prefix;
+ int plen;
+
+ switch (type) {
+ case LDAP_OPT_X_SASL_CBINDING_NONE:
+ return NULL;
+ case LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE:
+ if ( !ldap_pvt_tls_get_unique( ssl, &cbv, is_server ))
+ return NULL;
+ prefix = unique_prefix;
+ plen = sizeof(unique_prefix) -1;
+ break;
+ case LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT:
+ if ( !ldap_pvt_tls_get_endpoint( ssl, &cbv, is_server ))
+ return NULL;
+ prefix = endpoint_prefix;
+ plen = sizeof(endpoint_prefix) -1;
+ break;
+ default:
+ return NULL;
+ }
+
+ cb = ldap_memalloc( sizeof(*cb) + plen + cbv.bv_len );
+ cb->len = plen + cbv.bv_len;
+ cb->data = cb_data = cb+1;
+ memcpy( cb_data, prefix, plen );
+ memcpy( cb_data + plen, cbv.bv_val, cbv.bv_len );
+ cb->name = "ldap";
+ cb->critical = 0;
+
+ return cb;
+#else
+ return NULL;
+#endif
+}
+
+int
+ldap_int_sasl_bind(
+ LDAP *ld,
+ const char *dn,
+ const char *mechs,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ unsigned flags,
+ LDAP_SASL_INTERACT_PROC *interact,
+ void *defaults,
+ LDAPMessage *result,
+ const char **rmech,
+ int *msgid )
+{
+ const char *mech;
+ sasl_ssf_t *ssf;
+ sasl_conn_t *ctx;
+ sasl_interact_t *prompts = NULL;
+ struct berval ccred = BER_BVNULL;
+ int saslrc, rc;
+ unsigned credlen;
+#if !defined(_WIN32)
+ char my_hostname[HOST_NAME_MAX + 1];
+#endif
+ int free_saslhost = 0;
+
+ Debug1( LDAP_DEBUG_TRACE, "ldap_int_sasl_bind: %s\n",
+ mechs ? mechs : "<null>" );
+
+ /* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */
+ if (ld->ld_version < LDAP_VERSION3) {
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ return ld->ld_errno;
+ }
+
+ /* Starting a Bind */
+ if ( !result ) {
+ const char *pmech = NULL;
+ sasl_conn_t *oldctx;
+ ber_socket_t sd;
+ void *ssl;
+
+ rc = 0;
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd );
+
+ if ( sd == AC_SOCKET_INVALID || !ld->ld_defconn ) {
+ /* not connected yet */
+
+ rc = ldap_open_defconn( ld );
+
+ if ( rc == 0 ) {
+ ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb,
+ LBER_SB_OPT_GET_FD, &sd );
+
+ if( sd == AC_SOCKET_INVALID ) {
+ ld->ld_errno = LDAP_LOCAL_ERROR;
+ rc = ld->ld_errno;
+ }
+ }
+ }
+ if ( rc == 0 && ld->ld_defconn &&
+ ld->ld_defconn->lconn_status == LDAP_CONNST_CONNECTING ) {
+ rc = ldap_int_check_async_open( ld, sd );
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ if( rc != 0 ) return ld->ld_errno;
+
+ oldctx = ld->ld_defconn->lconn_sasl_authctx;
+
+ /* If we already have an authentication context, clear it out */
+ if( oldctx ) {
+ if ( oldctx != ld->ld_defconn->lconn_sasl_sockctx ) {
+ sasl_dispose( &oldctx );
+ }
+ ld->ld_defconn->lconn_sasl_authctx = NULL;
+ }
+
+ {
+ char *saslhost;
+ int nocanon = (int)LDAP_BOOL_GET( &ld->ld_options,
+ LDAP_BOOL_SASL_NOCANON );
+
+ /* If we don't need to canonicalize just use the host
+ * from the LDAP URI.
+ * Always use the result of gethostname() for LDAPI.
+ * Skip for Windows which doesn't support LDAPI.
+ */
+#if !defined(_WIN32)
+ if (ld->ld_defconn->lconn_server->lud_scheme != NULL &&
+ strcmp("ldapi", ld->ld_defconn->lconn_server->lud_scheme) == 0) {
+ rc = gethostname(my_hostname, HOST_NAME_MAX + 1);
+ if (rc == 0) {
+ saslhost = my_hostname;
+ } else {
+ saslhost = "localhost";
+ }
+ } else
+#endif
+ if ( nocanon )
+ saslhost = ld->ld_defconn->lconn_server->lud_host;
+ else {
+ saslhost = ldap_host_connected_to( ld->ld_defconn->lconn_sb,
+ "localhost" );
+ free_saslhost = 1;
+ }
+ rc = ldap_int_sasl_open( ld, ld->ld_defconn, saslhost );
+ if ( free_saslhost )
+ LDAP_FREE( saslhost );
+ }
+
+ if ( rc != LDAP_SUCCESS ) return rc;
+
+ ctx = ld->ld_defconn->lconn_sasl_authctx;
+
+#ifdef HAVE_TLS
+ /* Check for TLS */
+ ssl = ldap_pvt_tls_sb_ctx( ld->ld_defconn->lconn_sb );
+ if ( ssl ) {
+ struct berval authid = BER_BVNULL;
+ ber_len_t fac;
+
+ fac = ldap_pvt_tls_get_strength( ssl );
+ /* failure is OK, we just can't use SASL EXTERNAL */
+ (void) ldap_pvt_tls_get_my_dn( ssl, &authid, NULL, 0 );
+
+ (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid.bv_val, fac );
+ LDAP_FREE( authid.bv_val );
+#ifdef SASL_CHANNEL_BINDING /* 2.1.25+ */
+ if ( ld->ld_defconn->lconn_sasl_cbind == NULL ) {
+ void *cb;
+ cb = ldap_pvt_sasl_cbinding( ssl,
+ ld->ld_options.ldo_sasl_cbinding,
+ 0 );
+ if ( cb != NULL ) {
+ sasl_setprop( ld->ld_defconn->lconn_sasl_authctx,
+ SASL_CHANNEL_BINDING, cb );
+ ld->ld_defconn->lconn_sasl_cbind = cb;
+ }
+ }
+#endif
+ }
+#endif
+
+#if !defined(_WIN32)
+ /* Check for local */
+ if ( ldap_pvt_url_scheme2proto(
+ ld->ld_defconn->lconn_server->lud_scheme ) == LDAP_PROTO_IPC )
+ {
+ char authid[sizeof("gidNumber=4294967295+uidNumber=4294967295,"
+ "cn=peercred,cn=external,cn=auth")];
+ sprintf( authid, "gidNumber=%u+uidNumber=%u,"
+ "cn=peercred,cn=external,cn=auth",
+ getegid(), geteuid() );
+ (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid,
+ LDAP_PVT_SASL_LOCAL_SSF );
+ }
+#endif
+
+ /* (re)set security properties */
+ sasl_setprop( ctx, SASL_SEC_PROPS,
+ &ld->ld_options.ldo_sasl_secprops );
+
+ mech = NULL;
+
+ do {
+ saslrc = sasl_client_start( ctx,
+ mechs,
+#if SASL_VERSION_MAJOR < 2
+ NULL,
+#endif
+ &prompts,
+ (SASL_CONST char **)&ccred.bv_val,
+ &credlen,
+ &mech );
+
+ if( pmech == NULL && mech != NULL ) {
+ pmech = mech;
+ *rmech = mech;
+
+ if( flags != LDAP_SASL_QUIET ) {
+ fprintf(stderr,
+ "SASL/%s authentication started\n",
+ pmech );
+ }
+ }
+
+ if( saslrc == SASL_INTERACT ) {
+ int res;
+ if( !interact ) break;
+ res = (interact)( ld, flags, defaults, prompts );
+
+ if( res != LDAP_SUCCESS ) break;
+ }
+ } while ( saslrc == SASL_INTERACT );
+ rc = LDAP_SASL_BIND_IN_PROGRESS;
+
+ } else {
+ /* continuing an in-progress Bind */
+ struct berval *scred = NULL;
+
+ ctx = ld->ld_defconn->lconn_sasl_authctx;
+
+ rc = ldap_parse_sasl_bind_result( ld, result, &scred, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ if ( scred )
+ ber_bvfree( scred );
+ goto done;
+ }
+
+ rc = ldap_result2error( ld, result, 0 );
+ if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
+ if( scred ) {
+ /* and server provided us with data? */
+ Debug2( LDAP_DEBUG_TRACE,
+ "ldap_int_sasl_bind: rc=%d len=%ld\n",
+ rc, scred ? (long) scred->bv_len : -1L );
+ ber_bvfree( scred );
+ scred = NULL;
+ }
+ goto done;
+ }
+
+ mech = *rmech;
+ if ( rc == LDAP_SUCCESS && mech == NULL ) {
+ if ( scred )
+ ber_bvfree( scred );
+ goto success;
+ }
+
+ do {
+ if( ! scred ) {
+ /* no data! */
+ Debug0( LDAP_DEBUG_TRACE,
+ "ldap_int_sasl_bind: no data in step!\n" );
+ }
+
+ saslrc = sasl_client_step( ctx,
+ (scred == NULL) ? NULL : scred->bv_val,
+ (scred == NULL) ? 0 : scred->bv_len,
+ &prompts,
+ (SASL_CONST char **)&ccred.bv_val,
+ &credlen );
+
+ Debug1( LDAP_DEBUG_TRACE, "sasl_client_step: %d\n",
+ saslrc );
+
+ if( saslrc == SASL_INTERACT ) {
+ int res;
+ if( !interact ) break;
+ res = (interact)( ld, flags, defaults, prompts );
+ if( res != LDAP_SUCCESS ) break;
+ }
+ } while ( saslrc == SASL_INTERACT );
+
+ ber_bvfree( scred );
+ }
+
+ if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
+ rc = ld->ld_errno = sasl_err2ldap( saslrc );
+#if SASL_VERSION_MAJOR >= 2
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) );
+#endif
+ goto done;
+ }
+
+ if ( saslrc == SASL_OK )
+ *rmech = NULL;
+
+ ccred.bv_len = credlen;
+
+ if ( rc == LDAP_SASL_BIND_IN_PROGRESS ) {
+ rc = ldap_sasl_bind( ld, dn, mech, &ccred, sctrls, cctrls, msgid );
+
+ if ( ccred.bv_val != NULL ) {
+#if SASL_VERSION_MAJOR < 2
+ LDAP_FREE( ccred.bv_val );
+#endif
+ ccred.bv_val = NULL;
+ }
+ if ( rc == LDAP_SUCCESS )
+ rc = LDAP_SASL_BIND_IN_PROGRESS;
+ goto done;
+ }
+
+success:
+ /* Conversation was completed successfully by now */
+ if( flags != LDAP_SASL_QUIET ) {
+ char *data;
+ saslrc = sasl_getprop( ctx, SASL_USERNAME,
+ (SASL_CONST void **)(char *) &data );
+ if( saslrc == SASL_OK && data && *data ) {
+ fprintf( stderr, "SASL username: %s\n", data );
+ }
+
+#if SASL_VERSION_MAJOR < 2
+ saslrc = sasl_getprop( ctx, SASL_REALM,
+ (SASL_CONST void **) &data );
+ if( saslrc == SASL_OK && data && *data ) {
+ fprintf( stderr, "SASL realm: %s\n", data );
+ }
+#endif
+ }
+
+ ssf = NULL;
+ saslrc = sasl_getprop( ctx, SASL_SSF, (SASL_CONST void **)(char *) &ssf );
+ if( saslrc == SASL_OK ) {
+ if( flags != LDAP_SASL_QUIET ) {
+ fprintf( stderr, "SASL SSF: %lu\n",
+ (unsigned long) *ssf );
+ }
+
+ if( ssf && *ssf ) {
+ if ( ld->ld_defconn->lconn_sasl_sockctx ) {
+ sasl_conn_t *oldctx = ld->ld_defconn->lconn_sasl_sockctx;
+ sasl_dispose( &oldctx );
+ ldap_pvt_sasl_remove( ld->ld_defconn->lconn_sb );
+ }
+ ldap_pvt_sasl_install( ld->ld_defconn->lconn_sb, ctx );
+ ld->ld_defconn->lconn_sasl_sockctx = ctx;
+
+ if( flags != LDAP_SASL_QUIET ) {
+ fprintf( stderr, "SASL data security layer installed.\n" );
+ }
+ }
+ }
+ ld->ld_defconn->lconn_sasl_authctx = ctx;
+
+done:
+ return rc;
+}
+
+int
+ldap_int_sasl_external(
+ LDAP *ld,
+ LDAPConn *conn,
+ const char * authid,
+ ber_len_t ssf )
+{
+ int sc;
+ sasl_conn_t *ctx;
+#if SASL_VERSION_MAJOR < 2
+ sasl_external_properties_t extprops;
+#else
+ sasl_ssf_t sasl_ssf = ssf;
+#endif
+
+ ctx = conn->lconn_sasl_authctx;
+
+ if ( ctx == NULL ) {
+ return LDAP_LOCAL_ERROR;
+ }
+
+#if SASL_VERSION_MAJOR >= 2
+ sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf );
+ if ( sc == SASL_OK )
+ sc = sasl_setprop( ctx, SASL_AUTH_EXTERNAL, authid );
+#else
+ memset( &extprops, '\0', sizeof(extprops) );
+ extprops.ssf = ssf;
+ extprops.auth_id = (char *) authid;
+
+ sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL,
+ (void *) &extprops );
+#endif
+
+ if ( sc != SASL_OK ) {
+ return LDAP_LOCAL_ERROR;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+
+#define GOT_MINSSF 1
+#define GOT_MAXSSF 2
+#define GOT_MAXBUF 4
+
+static struct {
+ struct berval key;
+ int sflag;
+ int ival;
+ int idef;
+} sprops[] = {
+ { BER_BVC("none"), 0, 0, 0 },
+ { BER_BVC("nodict"), SASL_SEC_NODICTIONARY, 0, 0 },
+ { BER_BVC("noplain"), SASL_SEC_NOPLAINTEXT, 0, 0 },
+ { BER_BVC("noactive"), SASL_SEC_NOACTIVE, 0, 0 },
+ { BER_BVC("passcred"), SASL_SEC_PASS_CREDENTIALS, 0, 0 },
+ { BER_BVC("forwardsec"), SASL_SEC_FORWARD_SECRECY, 0, 0 },
+ { BER_BVC("noanonymous"), SASL_SEC_NOANONYMOUS, 0, 0 },
+ { BER_BVC("minssf="), 0, GOT_MINSSF, 0 },
+ { BER_BVC("maxssf="), 0, GOT_MAXSSF, INT_MAX },
+ { BER_BVC("maxbufsize="), 0, GOT_MAXBUF, 65536 },
+ { BER_BVNULL, 0, 0, 0 }
+};
+
+void ldap_pvt_sasl_secprops_unparse(
+ sasl_security_properties_t *secprops,
+ struct berval *out )
+{
+ int i, l = 0;
+ int comma;
+ char *ptr;
+
+ if ( secprops == NULL || out == NULL ) {
+ return;
+ }
+
+ comma = 0;
+ for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) {
+ if ( sprops[i].ival ) {
+ int v = 0;
+
+ switch( sprops[i].ival ) {
+ case GOT_MINSSF: v = secprops->min_ssf; break;
+ case GOT_MAXSSF: v = secprops->max_ssf; break;
+ case GOT_MAXBUF: v = secprops->maxbufsize; break;
+ }
+ /* It is the default, ignore it */
+ if ( v == sprops[i].idef ) continue;
+
+ l += sprops[i].key.bv_len + 24;
+ } else if ( sprops[i].sflag ) {
+ if ( sprops[i].sflag & secprops->security_flags ) {
+ l += sprops[i].key.bv_len;
+ }
+ } else if ( secprops->security_flags == 0 ) {
+ l += sprops[i].key.bv_len;
+ }
+ if ( comma ) l++;
+ comma = 1;
+ }
+ l++;
+
+ out->bv_val = LDAP_MALLOC( l );
+ if ( out->bv_val == NULL ) {
+ out->bv_len = 0;
+ return;
+ }
+
+ ptr = out->bv_val;
+ comma = 0;
+ for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) {
+ if ( sprops[i].ival ) {
+ int v = 0;
+
+ switch( sprops[i].ival ) {
+ case GOT_MINSSF: v = secprops->min_ssf; break;
+ case GOT_MAXSSF: v = secprops->max_ssf; break;
+ case GOT_MAXBUF: v = secprops->maxbufsize; break;
+ }
+ /* It is the default, ignore it */
+ if ( v == sprops[i].idef ) continue;
+
+ if ( comma ) *ptr++ = ',';
+ ptr += sprintf(ptr, "%s%d", sprops[i].key.bv_val, v );
+ comma = 1;
+ } else if ( sprops[i].sflag ) {
+ if ( sprops[i].sflag & secprops->security_flags ) {
+ if ( comma ) *ptr++ = ',';
+ ptr += sprintf(ptr, "%s", sprops[i].key.bv_val );
+ comma = 1;
+ }
+ } else if ( secprops->security_flags == 0 ) {
+ if ( comma ) *ptr++ = ',';
+ ptr += sprintf(ptr, "%s", sprops[i].key.bv_val );
+ comma = 1;
+ }
+ }
+ out->bv_len = ptr - out->bv_val;
+}
+
+int ldap_pvt_sasl_secprops(
+ const char *in,
+ sasl_security_properties_t *secprops )
+{
+ unsigned i, j, l;
+ char **props;
+ unsigned sflags = 0;
+ int got_sflags = 0;
+ sasl_ssf_t max_ssf = 0;
+ int got_max_ssf = 0;
+ sasl_ssf_t min_ssf = 0;
+ int got_min_ssf = 0;
+ unsigned maxbufsize = 0;
+ int got_maxbufsize = 0;
+
+ if( secprops == NULL ) {
+ return LDAP_PARAM_ERROR;
+ }
+ props = ldap_str2charray( in, "," );
+ if( props == NULL ) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ for( i=0; props[i]; i++ ) {
+ l = strlen( props[i] );
+ for ( j=0; !BER_BVISNULL( &sprops[j].key ); j++ ) {
+ if ( l < sprops[j].key.bv_len ) continue;
+ if ( strncasecmp( props[i], sprops[j].key.bv_val,
+ sprops[j].key.bv_len )) continue;
+ if ( sprops[j].ival ) {
+ unsigned v;
+ char *next = NULL;
+ if ( !isdigit( (unsigned char)props[i][sprops[j].key.bv_len] ))
+ continue;
+ v = strtoul( &props[i][sprops[j].key.bv_len], &next, 10 );
+ if ( next == &props[i][sprops[j].key.bv_len] || next[0] != '\0' ) continue;
+ switch( sprops[j].ival ) {
+ case GOT_MINSSF:
+ min_ssf = v; got_min_ssf++; break;
+ case GOT_MAXSSF:
+ max_ssf = v; got_max_ssf++; break;
+ case GOT_MAXBUF:
+ maxbufsize = v; got_maxbufsize++; break;
+ }
+ } else {
+ if ( props[i][sprops[j].key.bv_len] ) continue;
+ if ( sprops[j].sflag )
+ sflags |= sprops[j].sflag;
+ else
+ sflags = 0;
+ got_sflags++;
+ }
+ break;
+ }
+ if ( BER_BVISNULL( &sprops[j].key )) {
+ ldap_charray_free( props );
+ return LDAP_NOT_SUPPORTED;
+ }
+ }
+
+ if(got_sflags) {
+ secprops->security_flags = sflags;
+ }
+ if(got_min_ssf) {
+ secprops->min_ssf = min_ssf;
+ }
+ if(got_max_ssf) {
+ secprops->max_ssf = max_ssf;
+ }
+ if(got_maxbufsize) {
+ secprops->maxbufsize = maxbufsize;
+ }
+
+ ldap_charray_free( props );
+ return LDAP_SUCCESS;
+}
+
+int
+ldap_int_sasl_config( struct ldapoptions *lo, int option, const char *arg )
+{
+ int rc, i;
+
+ switch( option ) {
+ case LDAP_OPT_X_SASL_SECPROPS:
+ rc = ldap_pvt_sasl_secprops( arg, &lo->ldo_sasl_secprops );
+ if( rc == LDAP_SUCCESS ) return 0;
+ break;
+ case LDAP_OPT_X_SASL_CBINDING:
+ i = ldap_pvt_sasl_cbinding_parse( arg );
+ if ( i >= 0 ) {
+ lo->ldo_sasl_cbinding = i;
+ return 0;
+ }
+ break;
+ }
+
+ return -1;
+}
+
+int
+ldap_int_sasl_get_option( LDAP *ld, int option, void *arg )
+{
+ if ( option == LDAP_OPT_X_SASL_MECHLIST ) {
+ *(char ***)arg = (char **)sasl_global_listmech();
+ return 0;
+ }
+
+ if ( ld == NULL )
+ return -1;
+
+ switch ( option ) {
+ case LDAP_OPT_X_SASL_MECH: {
+ *(char **)arg = ld->ld_options.ldo_def_sasl_mech
+ ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_mech ) : NULL;
+ } break;
+ case LDAP_OPT_X_SASL_REALM: {
+ *(char **)arg = ld->ld_options.ldo_def_sasl_realm
+ ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_realm ) : NULL;
+ } break;
+ case LDAP_OPT_X_SASL_AUTHCID: {
+ *(char **)arg = ld->ld_options.ldo_def_sasl_authcid
+ ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_authcid ) : NULL;
+ } break;
+ case LDAP_OPT_X_SASL_AUTHZID: {
+ *(char **)arg = ld->ld_options.ldo_def_sasl_authzid
+ ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_authzid ) : NULL;
+ } break;
+
+ case LDAP_OPT_X_SASL_SSF: {
+ int sc;
+ sasl_ssf_t *ssf;
+ sasl_conn_t *ctx;
+
+ if( ld->ld_defconn == NULL ) {
+ return -1;
+ }
+
+ ctx = ld->ld_defconn->lconn_sasl_sockctx;
+
+ if ( ctx == NULL ) {
+ return -1;
+ }
+
+ sc = sasl_getprop( ctx, SASL_SSF,
+ (SASL_CONST void **)(char *) &ssf );
+
+ if ( sc != SASL_OK ) {
+ return -1;
+ }
+
+ *(ber_len_t *)arg = *ssf;
+ } break;
+
+ case LDAP_OPT_X_SASL_SSF_EXTERNAL:
+ /* this option is write only */
+ return -1;
+
+ case LDAP_OPT_X_SASL_SSF_MIN:
+ *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.min_ssf;
+ break;
+ case LDAP_OPT_X_SASL_SSF_MAX:
+ *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.max_ssf;
+ break;
+ case LDAP_OPT_X_SASL_MAXBUFSIZE:
+ *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.maxbufsize;
+ break;
+ case LDAP_OPT_X_SASL_NOCANON:
+ *(int *)arg = (int) LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_SASL_NOCANON );
+ break;
+
+ case LDAP_OPT_X_SASL_USERNAME: {
+ int sc;
+ char *username;
+ sasl_conn_t *ctx;
+
+ if( ld->ld_defconn == NULL ) {
+ return -1;
+ }
+
+ ctx = ld->ld_defconn->lconn_sasl_authctx;
+
+ if ( ctx == NULL ) {
+ return -1;
+ }
+
+ sc = sasl_getprop( ctx, SASL_USERNAME,
+ (SASL_CONST void **)(char **) &username );
+
+ if ( sc != SASL_OK ) {
+ return -1;
+ }
+
+ *(char **)arg = username ? LDAP_STRDUP( username ) : NULL;
+ } break;
+
+ case LDAP_OPT_X_SASL_SECPROPS:
+ /* this option is write only */
+ return -1;
+
+ case LDAP_OPT_X_SASL_CBINDING:
+ *(int *)arg = ld->ld_options.ldo_sasl_cbinding;
+ break;
+
+#ifdef SASL_GSS_CREDS
+ case LDAP_OPT_X_SASL_GSS_CREDS: {
+ sasl_conn_t *ctx;
+ int sc;
+
+ if ( ld->ld_defconn == NULL )
+ return -1;
+
+ ctx = ld->ld_defconn->lconn_sasl_authctx;
+ if ( ctx == NULL )
+ return -1;
+
+ sc = sasl_getprop( ctx, SASL_GSS_CREDS, arg );
+ if ( sc != SASL_OK )
+ return -1;
+ }
+ break;
+#endif
+
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+int
+ldap_int_sasl_set_option( LDAP *ld, int option, void *arg )
+{
+ if ( ld == NULL )
+ return -1;
+
+ if ( arg == NULL && option != LDAP_OPT_X_SASL_NOCANON )
+ return -1;
+
+ switch ( option ) {
+ case LDAP_OPT_X_SASL_SSF:
+ case LDAP_OPT_X_SASL_USERNAME:
+ /* This option is read-only */
+ return -1;
+
+ case LDAP_OPT_X_SASL_SSF_EXTERNAL: {
+ int sc;
+#if SASL_VERSION_MAJOR < 2
+ sasl_external_properties_t extprops;
+#else
+ sasl_ssf_t sasl_ssf;
+#endif
+ sasl_conn_t *ctx;
+
+ if( ld->ld_defconn == NULL ) {
+ return -1;
+ }
+
+ ctx = ld->ld_defconn->lconn_sasl_authctx;
+
+ if ( ctx == NULL ) {
+ return -1;
+ }
+
+#if SASL_VERSION_MAJOR >= 2
+ sasl_ssf = * (ber_len_t *)arg;
+ sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf);
+#else
+ memset(&extprops, 0L, sizeof(extprops));
+
+ extprops.ssf = * (ber_len_t *) arg;
+
+ sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL,
+ (void *) &extprops );
+#endif
+
+ if ( sc != SASL_OK ) {
+ return -1;
+ }
+ } break;
+
+ case LDAP_OPT_X_SASL_SSF_MIN:
+ ld->ld_options.ldo_sasl_secprops.min_ssf = *(ber_len_t *)arg;
+ break;
+ case LDAP_OPT_X_SASL_SSF_MAX:
+ ld->ld_options.ldo_sasl_secprops.max_ssf = *(ber_len_t *)arg;
+ break;
+ case LDAP_OPT_X_SASL_MAXBUFSIZE:
+ ld->ld_options.ldo_sasl_secprops.maxbufsize = *(ber_len_t *)arg;
+ break;
+ case LDAP_OPT_X_SASL_NOCANON:
+ if ( arg == LDAP_OPT_OFF ) {
+ LDAP_BOOL_CLR(&ld->ld_options, LDAP_BOOL_SASL_NOCANON );
+ } else {
+ LDAP_BOOL_SET(&ld->ld_options, LDAP_BOOL_SASL_NOCANON );
+ }
+ break;
+
+ case LDAP_OPT_X_SASL_SECPROPS: {
+ int sc;
+ sc = ldap_pvt_sasl_secprops( (char *) arg,
+ &ld->ld_options.ldo_sasl_secprops );
+
+ return sc == LDAP_SUCCESS ? 0 : -1;
+ }
+
+ case LDAP_OPT_X_SASL_CBINDING:
+ if ( !arg ) return -1;
+ switch( *(int *) arg ) {
+ case LDAP_OPT_X_SASL_CBINDING_NONE:
+ case LDAP_OPT_X_SASL_CBINDING_TLS_UNIQUE:
+ case LDAP_OPT_X_SASL_CBINDING_TLS_ENDPOINT:
+ ld->ld_options.ldo_sasl_cbinding = *(int *) arg;
+ return 0;
+ }
+ return -1;
+
+#ifdef SASL_GSS_CREDS
+ case LDAP_OPT_X_SASL_GSS_CREDS: {
+ sasl_conn_t *ctx;
+ int sc;
+
+ if ( ld->ld_defconn == NULL )
+ return -1;
+
+ ctx = ld->ld_defconn->lconn_sasl_authctx;
+ if ( ctx == NULL )
+ return -1;
+
+ sc = sasl_setprop( ctx, SASL_GSS_CREDS, arg );
+ if ( sc != SASL_OK )
+ return -1;
+ }
+ break;
+#endif
+
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+#ifdef LDAP_R_COMPILE
+#define LDAP_DEBUG_R_SASL
+void *ldap_pvt_sasl_mutex_new(void)
+{
+ ldap_pvt_thread_mutex_t *mutex;
+
+ mutex = (ldap_pvt_thread_mutex_t *) LDAP_CALLOC( 1,
+ sizeof(ldap_pvt_thread_mutex_t) );
+
+ if ( ldap_pvt_thread_mutex_init( mutex ) == 0 ) {
+ return mutex;
+ }
+ LDAP_FREE( mutex );
+#ifndef LDAP_DEBUG_R_SASL
+ assert( 0 );
+#endif /* !LDAP_DEBUG_R_SASL */
+ return NULL;
+}
+
+int ldap_pvt_sasl_mutex_lock(void *mutex)
+{
+#ifdef LDAP_DEBUG_R_SASL
+ if ( mutex == NULL ) {
+ return SASL_OK;
+ }
+#else /* !LDAP_DEBUG_R_SASL */
+ assert( mutex != NULL );
+#endif /* !LDAP_DEBUG_R_SASL */
+ return ldap_pvt_thread_mutex_lock( (ldap_pvt_thread_mutex_t *)mutex )
+ ? SASL_FAIL : SASL_OK;
+}
+
+int ldap_pvt_sasl_mutex_unlock(void *mutex)
+{
+#ifdef LDAP_DEBUG_R_SASL
+ if ( mutex == NULL ) {
+ return SASL_OK;
+ }
+#else /* !LDAP_DEBUG_R_SASL */
+ assert( mutex != NULL );
+#endif /* !LDAP_DEBUG_R_SASL */
+ return ldap_pvt_thread_mutex_unlock( (ldap_pvt_thread_mutex_t *)mutex )
+ ? SASL_FAIL : SASL_OK;
+}
+
+void ldap_pvt_sasl_mutex_dispose(void *mutex)
+{
+#ifdef LDAP_DEBUG_R_SASL
+ if ( mutex == NULL ) {
+ return;
+ }
+#else /* !LDAP_DEBUG_R_SASL */
+ assert( mutex != NULL );
+#endif /* !LDAP_DEBUG_R_SASL */
+ (void) ldap_pvt_thread_mutex_destroy( (ldap_pvt_thread_mutex_t *)mutex );
+ LDAP_FREE( mutex );
+}
+#endif
+
+#else
+int ldap_int_sasl_init( void )
+{ return LDAP_SUCCESS; }
+
+int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc )
+{ return LDAP_SUCCESS; }
+
+int
+ldap_int_sasl_bind(
+ LDAP *ld,
+ const char *dn,
+ const char *mechs,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ unsigned flags,
+ LDAP_SASL_INTERACT_PROC *interact,
+ void *defaults,
+ LDAPMessage *result,
+ const char **rmech,
+ int *msgid )
+{ return LDAP_NOT_SUPPORTED; }
+
+int
+ldap_int_sasl_external(
+ LDAP *ld,
+ LDAPConn *conn,
+ const char * authid,
+ ber_len_t ssf )
+{ return LDAP_SUCCESS; }
+
+#endif /* HAVE_CYRUS_SASL */
diff --git a/libraries/libldap/dds.c b/libraries/libldap/dds.c
new file mode 100644
index 0000000..7244d05
--- /dev/null
+++ b/libraries/libldap/dds.c
@@ -0,0 +1,156 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2005-2006 SysNet s.n.c.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+int
+ldap_parse_refresh( LDAP *ld, LDAPMessage *res, ber_int_t *newttl )
+{
+ int rc;
+ struct berval *retdata = NULL;
+ ber_tag_t tag;
+ BerElement *ber;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( res != NULL );
+ assert( newttl != NULL );
+
+ *newttl = 0;
+
+ rc = ldap_parse_extended_result( ld, res, NULL, &retdata, 0 );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( ld->ld_errno != LDAP_SUCCESS ) {
+ return ld->ld_errno;
+ }
+
+ if ( retdata == NULL ) {
+ rc = ld->ld_errno = LDAP_DECODING_ERROR;
+ return rc;
+ }
+
+ ber = ber_init( retdata );
+ if ( ber == NULL ) {
+ rc = ld->ld_errno = LDAP_NO_MEMORY;
+ goto done;
+ }
+
+ /* check the tag */
+ tag = ber_scanf( ber, "{i}", newttl );
+ ber_free( ber, 1 );
+
+ if ( tag != LDAP_TAG_EXOP_REFRESH_RES_TTL ) {
+ *newttl = 0;
+ rc = ld->ld_errno = LDAP_DECODING_ERROR;
+ }
+
+done:;
+ if ( retdata ) {
+ ber_bvfree( retdata );
+ }
+
+ return rc;
+}
+
+int
+ldap_refresh(
+ LDAP *ld,
+ struct berval *dn,
+ ber_int_t ttl,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ struct berval bv = { 0, NULL };
+ BerElement *ber = NULL;
+ int rc;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( dn != NULL );
+ assert( msgidp != NULL );
+
+ *msgidp = -1;
+
+ ber = ber_alloc_t( LBER_USE_DER );
+
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ ber_printf( ber, "{tOtiN}",
+ LDAP_TAG_EXOP_REFRESH_REQ_DN, dn,
+ LDAP_TAG_EXOP_REFRESH_REQ_TTL, ttl );
+
+ rc = ber_flatten2( ber, &bv, 0 );
+
+ if ( rc < 0 ) {
+ rc = ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+
+ rc = ldap_extended_operation( ld, LDAP_EXOP_REFRESH, &bv,
+ sctrls, cctrls, msgidp );
+
+done:;
+ ber_free( ber, 1 );
+
+ return rc;
+}
+
+int
+ldap_refresh_s(
+ LDAP *ld,
+ struct berval *dn,
+ ber_int_t ttl,
+ ber_int_t *newttl,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ int rc;
+ int msgid;
+ LDAPMessage *res;
+
+ rc = ldap_refresh( ld, dn, ttl, sctrls, cctrls, &msgid );
+ if ( rc != LDAP_SUCCESS ) return rc;
+
+ rc = ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *)NULL, &res );
+ if( rc == -1 || !res ) return ld->ld_errno;
+
+ rc = ldap_parse_refresh( ld, res, newttl );
+ if( rc != LDAP_SUCCESS ) {
+ ldap_msgfree( res );
+ return rc;
+ }
+
+ return ldap_result2error( ld, res, 1 );
+}
+
diff --git a/libraries/libldap/delete.c b/libraries/libldap/delete.c
new file mode 100644
index 0000000..b086f97
--- /dev/null
+++ b/libraries/libldap/delete.c
@@ -0,0 +1,174 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/*
+ * A delete request looks like this:
+ * DelRequest ::= DistinguishedName,
+ */
+
+BerElement *
+ldap_build_delete_req(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ BerElement *ber;
+ int rc;
+
+ /* create a message to send */
+ if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
+ return( NULL );
+ }
+
+ LDAP_NEXT_MSGID( ld, *msgidp );
+ rc = ber_printf( ber, "{its", /* '}' */
+ *msgidp, LDAP_REQ_DELETE, dn );
+ if ( rc == -1 )
+ {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ /* Put Server Controls */
+ if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ return( ber );
+}
+
+/*
+ * ldap_delete_ext - initiate an ldap extended delete operation. Parameters:
+ *
+ * ld LDAP descriptor
+ * dn DN of the object to delete
+ * sctrls Server Controls
+ * cctrls Client Controls
+ * msgidp Message Id Pointer
+ *
+ * Example:
+ * rc = ldap_delete( ld, dn, sctrls, cctrls, msgidp );
+ */
+int
+ldap_delete_ext(
+ LDAP *ld,
+ LDAP_CONST char* dn,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ int rc;
+ BerElement *ber;
+ ber_int_t id;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_delete_ext\n" );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( dn != NULL );
+ assert( msgidp != NULL );
+
+ /* check client controls */
+ rc = ldap_int_client_controls( ld, cctrls );
+ if( rc != LDAP_SUCCESS ) return rc;
+
+ ber = ldap_build_delete_req( ld, dn, sctrls, cctrls, &id );
+ if( !ber )
+ return ld->ld_errno;
+
+ /* send the message */
+ *msgidp = ldap_send_initial_request( ld, LDAP_REQ_DELETE, dn, ber, id );
+
+ if(*msgidp < 0)
+ return ld->ld_errno;
+
+ return LDAP_SUCCESS;
+}
+
+int
+ldap_delete_ext_s(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ int msgid;
+ int rc;
+ LDAPMessage *res;
+
+ rc = ldap_delete_ext( ld, dn, sctrls, cctrls, &msgid );
+
+ if( rc != LDAP_SUCCESS )
+ return( ld->ld_errno );
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res )
+ return( ld->ld_errno );
+
+ return( ldap_result2error( ld, res, 1 ) );
+}
+/*
+ * ldap_delete - initiate an ldap (and X.500) delete operation. Parameters:
+ *
+ * ld LDAP descriptor
+ * dn DN of the object to delete
+ *
+ * Example:
+ * msgid = ldap_delete( ld, dn );
+ */
+int
+ldap_delete( LDAP *ld, LDAP_CONST char *dn )
+{
+ int msgid;
+
+ /*
+ * A delete request looks like this:
+ * DelRequest ::= DistinguishedName,
+ */
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_delete\n" );
+
+ return ldap_delete_ext( ld, dn, NULL, NULL, &msgid ) == LDAP_SUCCESS
+ ? msgid : -1 ;
+}
+
+
+int
+ldap_delete_s( LDAP *ld, LDAP_CONST char *dn )
+{
+ return ldap_delete_ext_s( ld, dn, NULL, NULL );
+}
diff --git a/libraries/libldap/deref.c b/libraries/libldap/deref.c
new file mode 100644
index 0000000..f187a9f
--- /dev/null
+++ b/libraries/libldap/deref.c
@@ -0,0 +1,289 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati
+ * for inclusion in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+int
+ldap_create_deref_control_value(
+ LDAP *ld,
+ LDAPDerefSpec *ds,
+ struct berval *value )
+{
+ BerElement *ber = NULL;
+ ber_tag_t tag;
+ int i;
+
+ if ( ld == NULL || value == NULL || ds == NULL )
+ {
+ if ( ld )
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return LDAP_PARAM_ERROR;
+ }
+
+ assert( LDAP_VALID( ld ) );
+
+ value->bv_val = NULL;
+ value->bv_len = 0;
+ ld->ld_errno = LDAP_SUCCESS;
+
+ ber = ldap_alloc_ber_with_options( ld );
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ tag = ber_printf( ber, "{" /*}*/ );
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+
+ for ( i = 0; ds[i].derefAttr != NULL; i++ ) {
+ int j;
+
+ tag = ber_printf( ber, "{s{" /*}}*/ , ds[i].derefAttr );
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+
+ for ( j = 0; ds[i].attributes[j] != NULL; j++ ) {
+ tag = ber_printf( ber, "s", ds[i].attributes[ j ] );
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+ }
+
+ tag = ber_printf( ber, /*{{*/ "}N}" );
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+ }
+
+ tag = ber_printf( ber, /*{*/ "}" );
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+
+ if ( ber_flatten2( ber, value, 1 ) == -1 ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ }
+
+done:;
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ return ld->ld_errno;
+}
+
+int
+ldap_create_deref_control(
+ LDAP *ld,
+ LDAPDerefSpec *ds,
+ int iscritical,
+ LDAPControl **ctrlp )
+{
+ struct berval value;
+
+ if ( ctrlp == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ ld->ld_errno = ldap_create_deref_control_value( ld, ds, &value );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ ld->ld_errno = ldap_control_create( LDAP_CONTROL_X_DEREF,
+ iscritical, &value, 0, ctrlp );
+ if ( ld->ld_errno != LDAP_SUCCESS ) {
+ LDAP_FREE( value.bv_val );
+ }
+ }
+
+ return ld->ld_errno;
+}
+
+void
+ldap_derefresponse_free( LDAPDerefRes *dr )
+{
+ for ( ; dr; ) {
+ LDAPDerefRes *drnext = dr->next;
+ LDAPDerefVal *dv;
+
+ LDAP_FREE( dr->derefAttr );
+ LDAP_FREE( dr->derefVal.bv_val );
+
+ for ( dv = dr->attrVals; dv; ) {
+ LDAPDerefVal *dvnext = dv->next;
+ LDAP_FREE( dv->type );
+ ber_bvarray_free( dv->vals );
+ LDAP_FREE( dv );
+ dv = dvnext;
+ }
+
+ LDAP_FREE( dr );
+
+ dr = drnext;
+ }
+}
+
+int
+ldap_parse_derefresponse_control(
+ LDAP *ld,
+ LDAPControl *ctrl,
+ LDAPDerefRes **drp2 )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len;
+ char *last;
+ LDAPDerefRes *drhead = NULL, **drp;
+
+ if ( ld == NULL || ctrl == NULL || drp2 == NULL ) {
+ if ( ld )
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return LDAP_PARAM_ERROR;
+ }
+
+ /* Set up a BerElement from the berval returned in the control. */
+ ber_init2( ber, &ctrl->ldctl_value, 0 );
+
+ /* Extract the count and cookie from the control. */
+ drp = &drhead;
+ for ( tag = ber_first_element( ber, &len, &last );
+ tag != LBER_DEFAULT;
+ tag = ber_next_element( ber, &len, last ) )
+ {
+ LDAPDerefRes *dr;
+ LDAPDerefVal **dvp;
+ char *last2;
+
+ dr = LDAP_CALLOC( 1, sizeof(LDAPDerefRes) );
+ if ( dr == NULL ) {
+ ldap_derefresponse_free( drhead );
+ *drp2 = NULL;
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+ dvp = &dr->attrVals;
+
+ tag = ber_scanf( ber, "{ao", &dr->derefAttr, &dr->derefVal );
+ if ( tag == LBER_ERROR ) {
+ goto done;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == (LBER_CONSTRUCTED|LBER_CLASS_CONTEXT) ) {
+ for ( tag = ber_first_element( ber, &len, &last2 );
+ tag != LBER_DEFAULT;
+ tag = ber_next_element( ber, &len, last2 ) )
+ {
+ LDAPDerefVal *dv;
+
+ dv = LDAP_CALLOC( 1, sizeof(LDAPDerefVal) );
+ if ( dv == NULL ) {
+ ldap_derefresponse_free( drhead );
+ LDAP_FREE( dr );
+ *drp2 = NULL;
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ tag = ber_scanf( ber, "{a[W]}", &dv->type, &dv->vals );
+ if ( tag == LBER_ERROR ) {
+ goto done;
+ }
+
+ *dvp = dv;
+ dvp = &dv->next;
+ }
+ }
+
+ tag = ber_scanf( ber, "}" );
+ if ( tag == LBER_ERROR ) {
+ goto done;
+ }
+
+ *drp = dr;
+ drp = &dr->next;
+ }
+
+ tag = 0;
+
+done:;
+ if ( tag == LBER_ERROR ) {
+ if ( drhead != NULL ) {
+ ldap_derefresponse_free( drhead );
+ }
+
+ *drp2 = NULL;
+ ld->ld_errno = LDAP_DECODING_ERROR;
+
+ } else {
+ *drp2 = drhead;
+ ld->ld_errno = LDAP_SUCCESS;
+ }
+
+ return ld->ld_errno;
+}
+
+int
+ldap_parse_deref_control(
+ LDAP *ld,
+ LDAPControl **ctrls,
+ LDAPDerefRes **drp )
+{
+ LDAPControl *c;
+
+ if ( drp == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ *drp = NULL;
+
+ if ( ctrls == NULL ) {
+ ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
+ return ld->ld_errno;
+ }
+
+ c = ldap_control_find( LDAP_CONTROL_X_DEREF, ctrls, NULL );
+ if ( c == NULL ) {
+ /* No deref control was found. */
+ ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
+ return ld->ld_errno;
+ }
+
+ ld->ld_errno = ldap_parse_derefresponse_control( ld, c, drp );
+
+ return ld->ld_errno;
+}
+
diff --git a/libraries/libldap/dnssrv.c b/libraries/libldap/dnssrv.c
new file mode 100644
index 0000000..433c37f
--- /dev/null
+++ b/libraries/libldap/dnssrv.c
@@ -0,0 +1,422 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * locate LDAP servers using DNS SRV records.
+ * Location code based on MIT Kerberos KDC location code.
+ */
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/param.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+#ifdef HAVE_RESOLV_H
+#include <resolv.h>
+#endif
+
+int ldap_dn2domain(
+ LDAP_CONST char *dn_in,
+ char **domainp)
+{
+ int i, j;
+ char *ndomain;
+ LDAPDN dn = NULL;
+ LDAPRDN rdn = NULL;
+ LDAPAVA *ava = NULL;
+ struct berval domain = BER_BVNULL;
+ static const struct berval DC = BER_BVC("DC");
+ static const struct berval DCOID = BER_BVC("0.9.2342.19200300.100.1.25");
+
+ assert( dn_in != NULL );
+ assert( domainp != NULL );
+
+ *domainp = NULL;
+
+ if ( ldap_str2dn( dn_in, &dn, LDAP_DN_FORMAT_LDAP ) != LDAP_SUCCESS ) {
+ return -2;
+ }
+
+ if( dn ) for( i=0; dn[i] != NULL; i++ ) {
+ rdn = dn[i];
+
+ for( j=0; rdn[j] != NULL; j++ ) {
+ ava = rdn[j];
+
+ if( rdn[j+1] == NULL &&
+ (ava->la_flags & LDAP_AVA_STRING) &&
+ ava->la_value.bv_len &&
+ ( ber_bvstrcasecmp( &ava->la_attr, &DC ) == 0
+ || ber_bvcmp( &ava->la_attr, &DCOID ) == 0 ) )
+ {
+ if( domain.bv_len == 0 ) {
+ ndomain = LDAP_REALLOC( domain.bv_val,
+ ava->la_value.bv_len + 1);
+
+ if( ndomain == NULL ) {
+ goto return_error;
+ }
+
+ domain.bv_val = ndomain;
+
+ AC_MEMCPY( domain.bv_val, ava->la_value.bv_val,
+ ava->la_value.bv_len );
+
+ domain.bv_len = ava->la_value.bv_len;
+ domain.bv_val[domain.bv_len] = '\0';
+
+ } else {
+ ndomain = LDAP_REALLOC( domain.bv_val,
+ ava->la_value.bv_len + sizeof(".") + domain.bv_len );
+
+ if( ndomain == NULL ) {
+ goto return_error;
+ }
+
+ domain.bv_val = ndomain;
+ domain.bv_val[domain.bv_len++] = '.';
+ AC_MEMCPY( &domain.bv_val[domain.bv_len],
+ ava->la_value.bv_val, ava->la_value.bv_len );
+ domain.bv_len += ava->la_value.bv_len;
+ domain.bv_val[domain.bv_len] = '\0';
+ }
+ } else {
+ domain.bv_len = 0;
+ }
+ }
+ }
+
+
+ if( domain.bv_len == 0 && domain.bv_val != NULL ) {
+ LDAP_FREE( domain.bv_val );
+ domain.bv_val = NULL;
+ }
+
+ ldap_dnfree( dn );
+ *domainp = domain.bv_val;
+ return 0;
+
+return_error:
+ ldap_dnfree( dn );
+ LDAP_FREE( domain.bv_val );
+ return -1;
+}
+
+int ldap_domain2dn(
+ LDAP_CONST char *domain_in,
+ char **dnp)
+{
+ char *domain, *s, *tok_r, *dn, *dntmp;
+ size_t loc;
+
+ assert( domain_in != NULL );
+ assert( dnp != NULL );
+
+ domain = LDAP_STRDUP(domain_in);
+ if (domain == NULL) {
+ return LDAP_NO_MEMORY;
+ }
+ dn = NULL;
+ loc = 0;
+
+ for (s = ldap_pvt_strtok(domain, ".", &tok_r);
+ s != NULL;
+ s = ldap_pvt_strtok(NULL, ".", &tok_r))
+ {
+ size_t len = strlen(s);
+
+ dntmp = (char *) LDAP_REALLOC(dn, loc + sizeof(",dc=") + len );
+ if (dntmp == NULL) {
+ if (dn != NULL)
+ LDAP_FREE(dn);
+ LDAP_FREE(domain);
+ return LDAP_NO_MEMORY;
+ }
+
+ dn = dntmp;
+
+ if (loc > 0) {
+ /* not first time. */
+ strcpy(dn + loc, ",");
+ loc++;
+ }
+ strcpy(dn + loc, "dc=");
+ loc += sizeof("dc=")-1;
+
+ strcpy(dn + loc, s);
+ loc += len;
+ }
+
+ LDAP_FREE(domain);
+ *dnp = dn;
+ return LDAP_SUCCESS;
+}
+
+#ifdef HAVE_RES_QUERY
+#define DNSBUFSIZ (64*1024)
+#define MAXHOST 254 /* RFC 1034, max length is 253 chars */
+typedef struct srv_record {
+ u_short priority;
+ u_short weight;
+ u_short port;
+ char hostname[MAXHOST];
+} srv_record;
+
+/* Linear Congruential Generator - we don't need
+ * high quality randomness, and we don't want to
+ * interfere with anyone else's use of srand().
+ *
+ * The PRNG here cycles thru 941,955 numbers.
+ */
+static float srv_seed;
+
+static void srv_srand(int seed) {
+ srv_seed = (float)seed / (float)RAND_MAX;
+}
+
+static float srv_rand() {
+ float val = 9821.0 * srv_seed + .211327;
+ srv_seed = val - (int)val;
+ return srv_seed;
+}
+
+static int srv_cmp(const void *aa, const void *bb){
+ srv_record *a=(srv_record *)aa;
+ srv_record *b=(srv_record *)bb;
+ int i = a->priority - b->priority;
+ if (i) return i;
+ return b->weight - a->weight;
+}
+
+static void srv_shuffle(srv_record *a, int n) {
+ int i, j, total = 0, r, p;
+
+ for (i=0; i<n; i++)
+ total += a[i].weight;
+
+ /* Do a shuffle per RFC2782 Page 4 */
+ for (p=n; p>1; a++, p--) {
+ if (!total) {
+ /* all remaining weights are zero,
+ do a straight Fisher-Yates shuffle */
+ j = srv_rand() * p;
+ } else {
+ r = srv_rand() * total;
+ for (j=0; j<p; j++) {
+ r -= a[j].weight;
+ if (r < 0) {
+ total -= a[j].weight;
+ break;
+ }
+ }
+ }
+ if (j && j<p) {
+ srv_record t = a[0];
+ a[0] = a[j];
+ a[j] = t;
+ }
+ }
+}
+#endif /* HAVE_RES_QUERY */
+
+/*
+ * Lookup and return LDAP servers for domain (using the DNS
+ * SRV record _ldap._tcp.domain).
+ */
+int ldap_domain2hostlist(
+ LDAP_CONST char *domain,
+ char **list )
+{
+#ifdef HAVE_RES_QUERY
+ char *request;
+ char *hostlist = NULL;
+ srv_record *hostent_head=NULL;
+ int i, j;
+ int rc, len, cur = 0;
+ unsigned char reply[DNSBUFSIZ];
+ int hostent_count=0;
+
+ assert( domain != NULL );
+ assert( list != NULL );
+ if( *domain == '\0' ) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ request = LDAP_MALLOC(strlen(domain) + sizeof("_ldap._tcp."));
+ if (request == NULL) {
+ return LDAP_NO_MEMORY;
+ }
+ sprintf(request, "_ldap._tcp.%s", domain);
+
+ LDAP_MUTEX_LOCK(&ldap_int_resolv_mutex);
+
+ rc = LDAP_UNAVAILABLE;
+#ifdef NS_HFIXEDSZ
+ /* Bind 8/9 interface */
+ len = res_query(request, ns_c_in, ns_t_srv, reply, sizeof(reply));
+# ifndef T_SRV
+# define T_SRV ns_t_srv
+# endif
+#else
+ /* Bind 4 interface */
+# ifndef T_SRV
+# define T_SRV 33
+# endif
+
+ len = res_query(request, C_IN, T_SRV, reply, sizeof(reply));
+#endif
+ if (len >= 0) {
+ unsigned char *p;
+ char host[DNSBUFSIZ];
+ int status;
+ u_short port, priority, weight;
+
+ /* Parse out query */
+ p = reply;
+
+#ifdef NS_HFIXEDSZ
+ /* Bind 8/9 interface */
+ p += NS_HFIXEDSZ;
+#elif defined(HFIXEDSZ)
+ /* Bind 4 interface w/ HFIXEDSZ */
+ p += HFIXEDSZ;
+#else
+ /* Bind 4 interface w/o HFIXEDSZ */
+ p += sizeof(HEADER);
+#endif
+
+ status = dn_expand(reply, reply + len, p, host, sizeof(host));
+ if (status < 0) {
+ goto out;
+ }
+ p += status;
+ p += 4;
+
+ while (p < reply + len) {
+ int type, class, ttl, size;
+ status = dn_expand(reply, reply + len, p, host, sizeof(host));
+ if (status < 0) {
+ goto out;
+ }
+ p += status;
+ type = (p[0] << 8) | p[1];
+ p += 2;
+ class = (p[0] << 8) | p[1];
+ p += 2;
+ ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+ p += 4;
+ size = (p[0] << 8) | p[1];
+ p += 2;
+ if (type == T_SRV) {
+ status = dn_expand(reply, reply + len, p + 6, host, sizeof(host));
+ if (status < 0) {
+ goto out;
+ }
+
+ /* Get priority weight and port */
+ priority = (p[0] << 8) | p[1];
+ weight = (p[2] << 8) | p[3];
+ port = (p[4] << 8) | p[5];
+
+ if ( port == 0 || host[ 0 ] == '\0' ) {
+ goto add_size;
+ }
+
+ hostent_head = (srv_record *) LDAP_REALLOC(hostent_head, (hostent_count+1)*(sizeof(srv_record)));
+ if(hostent_head==NULL){
+ rc=LDAP_NO_MEMORY;
+ goto out;
+ }
+ hostent_head[hostent_count].priority=priority;
+ hostent_head[hostent_count].weight=weight;
+ hostent_head[hostent_count].port=port;
+ strncpy(hostent_head[hostent_count].hostname, host, MAXHOST-1);
+ hostent_head[hostent_count].hostname[MAXHOST-1] = '\0';
+ hostent_count++;
+ }
+add_size:;
+ p += size;
+ }
+ if (!hostent_head) goto out;
+ qsort(hostent_head, hostent_count, sizeof(srv_record), srv_cmp);
+
+ if (!srv_seed)
+ srv_srand(time(0L));
+
+ /* shuffle records of same priority */
+ j = 0;
+ priority = hostent_head[0].priority;
+ for (i=1; i<hostent_count; i++) {
+ if (hostent_head[i].priority != priority) {
+ priority = hostent_head[i].priority;
+ if (i-j > 1)
+ srv_shuffle(hostent_head+j, i-j);
+ j = i;
+ }
+ }
+ if (i-j > 1)
+ srv_shuffle(hostent_head+j, i-j);
+
+ for(i=0; i<hostent_count; i++){
+ int buflen;
+ buflen = strlen(hostent_head[i].hostname) + STRLENOF(":65535 ");
+ hostlist = (char *) LDAP_REALLOC(hostlist, cur+buflen+1);
+ if (hostlist == NULL) {
+ rc = LDAP_NO_MEMORY;
+ goto out;
+ }
+ if(cur>0){
+ hostlist[cur++]=' ';
+ }
+ cur += sprintf(&hostlist[cur], "%s:%hu", hostent_head[i].hostname, hostent_head[i].port);
+ }
+ }
+
+ if (hostlist == NULL) {
+ /* No LDAP servers found in DNS. */
+ rc = LDAP_UNAVAILABLE;
+ goto out;
+ }
+
+ rc = LDAP_SUCCESS;
+ *list = hostlist;
+
+ out:
+ LDAP_MUTEX_UNLOCK(&ldap_int_resolv_mutex);
+
+ if (request != NULL) {
+ LDAP_FREE(request);
+ }
+ if (hostent_head != NULL) {
+ LDAP_FREE(hostent_head);
+ }
+ if (rc != LDAP_SUCCESS && hostlist != NULL) {
+ LDAP_FREE(hostlist);
+ }
+ return rc;
+#else
+ return LDAP_NOT_SUPPORTED;
+#endif /* HAVE_RES_QUERY */
+}
diff --git a/libraries/libldap/dntest.c b/libraries/libldap/dntest.c
new file mode 100644
index 0000000..b5d4e89
--- /dev/null
+++ b/libraries/libldap/dntest.c
@@ -0,0 +1,296 @@
+/* dntest.c -- OpenLDAP DN API Test Program */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This program was initially developed by Pierangelo Masarati <ando@OpenLDAP.org>
+ * for inclusion in OpenLDAP Software.
+ */
+
+/*
+ * This program is designed to test the ldap_str2dn/ldap_dn2str
+ * functions
+ */
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include <ldap.h>
+
+#include "ldap-int.h"
+
+#include "ldif.h"
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "ldap_defaults.h"
+
+int
+main( int argc, char *argv[] )
+{
+ int rc, i, debug = 0, f2 = 0;
+ unsigned flags[ 2 ] = { 0U, 0 };
+ char *strin, *str = NULL, buf[ 1024 ];
+ LDAPDN dn, dn2 = NULL;
+
+ while ( 1 ) {
+ int opt = getopt( argc, argv, "d:" );
+
+ if ( opt == EOF ) {
+ break;
+ }
+
+ switch ( opt ) {
+ case 'd':
+ debug = atoi( optarg );
+ break;
+ }
+ }
+
+ optind--;
+ argc -= optind;
+ argv += optind;
+
+ if ( argc < 2 ) {
+ fprintf( stderr, "usage: dntest <dn> [flags-in[,...]] [flags-out[,...]]\n\n" );
+ fprintf( stderr, "\tflags-in: V3,V2,DCE,<flags>\n" );
+ fprintf( stderr, "\tflags-out: V3,V2,UFN,DCE,AD,<flags>\n\n" );
+ fprintf( stderr, "\t<flags>: PRETTY,PEDANTIC,NOSPACES,NOONESPACE\n\n" );
+ return( 0 );
+ }
+
+ if ( ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &debug ) != LBER_OPT_SUCCESS ) {
+ fprintf( stderr, "Could not set LBER_OPT_DEBUG_LEVEL %d\n", debug );
+ }
+ if ( ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &debug ) != LDAP_OPT_SUCCESS ) {
+ fprintf( stderr, "Could not set LDAP_OPT_DEBUG_LEVEL %d\n", debug );
+ }
+
+ if ( strcmp( argv[ 1 ], "-" ) == 0 ) {
+ size_t len = fgets( buf, sizeof( buf ), stdin ) ? strlen( buf ) : 0;
+
+ if ( len == 0 || buf[ --len ] == '\n' ) {
+ buf[ len ] = '\0';
+ }
+ strin = buf;
+ } else {
+ strin = argv[ 1 ];
+ }
+
+ if ( argc >= 3 ) {
+ for ( i = 0; i < argc - 2; i++ ) {
+ char *s, *e;
+ for ( s = argv[ 2 + i ]; s; s = e ) {
+ e = strchr( s, ',' );
+ if ( e != NULL ) {
+ e[ 0 ] = '\0';
+ e++;
+ }
+
+ if ( !strcasecmp( s, "V3" ) ) {
+ flags[ i ] |= LDAP_DN_FORMAT_LDAPV3;
+ } else if ( !strcasecmp( s, "V2" ) ) {
+ flags[ i ] |= LDAP_DN_FORMAT_LDAPV2;
+ } else if ( !strcasecmp( s, "DCE" ) ) {
+ flags[ i ] |= LDAP_DN_FORMAT_DCE;
+ } else if ( !strcasecmp( s, "UFN" ) ) {
+ flags[ i ] |= LDAP_DN_FORMAT_UFN;
+ } else if ( !strcasecmp( s, "AD" ) ) {
+ flags[ i ] |= LDAP_DN_FORMAT_AD_CANONICAL;
+ } else if ( !strcasecmp( s, "PRETTY" ) ) {
+ flags[ i ] |= LDAP_DN_PRETTY;
+ } else if ( !strcasecmp( s, "PEDANTIC" ) ) {
+ flags[ i ] |= LDAP_DN_PEDANTIC;
+ } else if ( !strcasecmp( s, "NOSPACES" ) ) {
+ flags[ i ] |= LDAP_DN_P_NOLEADTRAILSPACES;
+ } else if ( !strcasecmp( s, "NOONESPACE" ) ) {
+ flags[ i ] |= LDAP_DN_P_NOSPACEAFTERRDN;
+ }
+ }
+ }
+ }
+
+ if ( flags[ 1 ] == 0 )
+ flags[ 1 ] = LDAP_DN_FORMAT_LDAPV3;
+
+ f2 = 1;
+
+ rc = ldap_str2dn( strin, &dn, flags[ 0 ] );
+
+ if ( rc == LDAP_SUCCESS ) {
+ int i;
+ if ( dn ) {
+ for ( i = 0; dn[ i ]; i++ ) {
+ LDAPRDN rdn = dn[ i ];
+ char *rstr = NULL;
+
+ if ( ldap_rdn2str( rdn, &rstr, flags[ f2 ] ) ) {
+ fprintf( stdout, "\tldap_rdn2str() failed\n" );
+ continue;
+ }
+
+ fprintf( stdout, "\tldap_rdn2str() = \"%s\"\n", rstr );
+ ldap_memfree( rstr );
+ }
+ } else {
+ fprintf( stdout, "\tempty DN\n" );
+ }
+ }
+
+ str = NULL;
+ if ( rc == LDAP_SUCCESS &&
+ ldap_dn2str( dn, &str, flags[ f2 ] ) == LDAP_SUCCESS )
+ {
+ char **values, *tmp, *tmp2, *str2 = NULL;
+ int n;
+
+ fprintf( stdout, "\nldap_dn2str(ldap_str2dn(\"%s\"))\n"
+ "\t= \"%s\"\n", strin, str );
+
+ switch ( flags[ f2 ] & LDAP_DN_FORMAT_MASK ) {
+ case LDAP_DN_FORMAT_UFN:
+ case LDAP_DN_FORMAT_AD_CANONICAL:
+ return( 0 );
+
+ case LDAP_DN_FORMAT_LDAPV3:
+ case LDAP_DN_FORMAT_LDAPV2:
+ n = ldap_dn2domain( strin, &tmp );
+ if ( n ) {
+ fprintf( stdout, "\nldap_dn2domain(\"%s\") FAILED\n", strin );
+ } else {
+ fprintf( stdout, "\nldap_dn2domain(\"%s\")\n"
+ "\t= \"%s\"\n", strin, tmp ? tmp : "" );
+ }
+ ldap_memfree( tmp );
+
+ tmp = ldap_dn2ufn( strin );
+ fprintf( stdout, "\nldap_dn2ufn(\"%s\")\n"
+ "\t= \"%s\"\n", strin, tmp ? tmp : "" );
+ ldap_memfree( tmp );
+
+ tmp = ldap_dn2dcedn( strin );
+ fprintf( stdout, "\nldap_dn2dcedn(\"%s\")\n"
+ "\t= \"%s\"\n", strin, tmp ? tmp : "" );
+ tmp2 = ldap_dcedn2dn( tmp );
+ fprintf( stdout, "\nldap_dcedn2dn(\"%s\")\n"
+ "\t= \"%s\"\n",
+ tmp ? tmp : "", tmp2 ? tmp2 : "" );
+ ldap_memfree( tmp );
+ ldap_memfree( tmp2 );
+
+ tmp = ldap_dn2ad_canonical( strin );
+ fprintf( stdout, "\nldap_dn2ad_canonical(\"%s\")\n"
+ "\t= \"%s\"\n", strin, tmp ? tmp : "" );
+ ldap_memfree( tmp );
+
+ fprintf( stdout, "\nldap_explode_dn(\"%s\"):\n", str );
+ values = ldap_explode_dn( str, 0 );
+ for ( n = 0; values && values[ n ]; n++ ) {
+ char **vv;
+ int nn;
+
+ fprintf( stdout, "\t\"%s\"\n", values[ n ] );
+
+ fprintf( stdout, "\tldap_explode_rdn(\"%s\")\n",
+ values[ n ] );
+ vv = ldap_explode_rdn( values[ n ], 0 );
+ for ( nn = 0; vv && vv[ nn ]; nn++ ) {
+ fprintf( stdout, "\t\t'%s'\n",
+ vv[ nn ] );
+ }
+ LDAP_VFREE( vv );
+
+ fprintf( stdout, "\tldap_explode_rdn(\"%s\")"
+ " (no types)\n", values[ n ] );
+ vv = ldap_explode_rdn( values[ n ], 1 );
+ for ( nn = 0; vv && vv[ nn ]; nn++ ) {
+ fprintf( stdout, "\t\t\t\"%s\"\n",
+ vv[ nn ] );
+ }
+ LDAP_VFREE( vv );
+
+ }
+ LDAP_VFREE( values );
+
+ fprintf( stdout, "\nldap_explode_dn(\"%s\")"
+ " (no types):\n", str );
+ values = ldap_explode_dn( str, 1 );
+ for ( n = 0; values && values[ n ]; n++ ) {
+ fprintf( stdout, "\t\"%s\"\n", values[ n ] );
+ }
+ LDAP_VFREE( values );
+
+ break;
+ }
+
+ dn2 = NULL;
+ rc = ldap_str2dn( str, &dn2, flags[ f2 ] );
+ str2 = NULL;
+ if ( rc == LDAP_SUCCESS &&
+ ldap_dn2str( dn2, &str2, flags[ f2 ] )
+ == LDAP_SUCCESS ) {
+ int iRDN;
+
+ fprintf( stdout, "\n\"%s\"\n\t == \"%s\" ? %s\n",
+ str, str2,
+ strcmp( str, str2 ) == 0 ? "yes" : "no" );
+
+ if( dn != NULL && dn2 == NULL ) {
+ fprintf( stdout, "dn mismatch\n" );
+ } else if (( dn != NULL ) && (dn2 != NULL))
+ for ( iRDN = 0; dn[ iRDN ] && dn2[ iRDN ]; iRDN++ )
+ {
+ LDAPRDN r = dn[ iRDN ];
+ LDAPRDN r2 = dn2[ iRDN ];
+ int iAVA;
+
+ for ( iAVA = 0; r[ iAVA ] && r2[ iAVA ]; iAVA++ ) {
+ LDAPAVA *a = r[ iAVA ];
+ LDAPAVA *a2 = r2[ iAVA ];
+
+ if ( a->la_attr.bv_len != a2->la_attr.bv_len ) {
+ fprintf( stdout, "ava(%d), rdn(%d) attr len mismatch (%ld->%ld)\n",
+ iAVA + 1, iRDN + 1,
+ a->la_attr.bv_len, a2->la_attr.bv_len );
+ } else if ( memcmp( a->la_attr.bv_val, a2->la_attr.bv_val, a->la_attr.bv_len ) ) {
+ fprintf( stdout, "ava(%d), rdn(%d) attr mismatch\n",
+ iAVA + 1, iRDN + 1 );
+ } else if ( a->la_flags != a2->la_flags ) {
+ fprintf( stdout, "ava(%d), rdn(%d) flag mismatch (%x->%x)\n",
+ iAVA + 1, iRDN + 1, a->la_flags, a2->la_flags );
+ } else if ( a->la_value.bv_len != a2->la_value.bv_len ) {
+ fprintf( stdout, "ava(%d), rdn(%d) value len mismatch (%ld->%ld)\n",
+ iAVA + 1, iRDN + 1,
+ a->la_value.bv_len, a2->la_value.bv_len );
+ } else if ( memcmp( a->la_value.bv_val, a2->la_value.bv_val, a->la_value.bv_len ) ) {
+ fprintf( stdout, "ava(%d), rdn(%d) value mismatch\n",
+ iAVA + 1, iRDN + 1 );
+ }
+ }
+ }
+
+ ldap_dnfree( dn2 );
+ ldap_memfree( str2 );
+ }
+ ldap_memfree( str );
+ }
+ ldap_dnfree( dn );
+
+ /* note: dn is not freed */
+
+ return( 0 );
+}
diff --git a/libraries/libldap/error.c b/libraries/libldap/error.c
new file mode 100644
index 0000000..444a024
--- /dev/null
+++ b/libraries/libldap/error.c
@@ -0,0 +1,395 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+void ldap_int_error_init( void ) {
+}
+
+char *
+ldap_err2string( int err )
+{
+ char *m;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_err2string\n" );
+
+ switch ( err ) {
+# define C(code, message) case code: m = message; break
+
+ /* LDAPv3 (RFC 4511) codes */
+ C(LDAP_SUCCESS, N_("Success"));
+ C(LDAP_OPERATIONS_ERROR, N_("Operations error"));
+ C(LDAP_PROTOCOL_ERROR, N_("Protocol error"));
+ C(LDAP_TIMELIMIT_EXCEEDED, N_("Time limit exceeded"));
+ C(LDAP_SIZELIMIT_EXCEEDED, N_("Size limit exceeded"));
+ C(LDAP_COMPARE_FALSE, N_("Compare False"));
+ C(LDAP_COMPARE_TRUE, N_("Compare True"));
+ C(LDAP_STRONG_AUTH_NOT_SUPPORTED,N_("Authentication method not supported"));
+ C(LDAP_STRONG_AUTH_REQUIRED, N_("Strong(er) authentication required"));
+
+ C(LDAP_REFERRAL, N_("Referral"));
+ C(LDAP_ADMINLIMIT_EXCEEDED, N_("Administrative limit exceeded"));
+ C(LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
+ N_("Critical extension is unavailable"));
+ C(LDAP_CONFIDENTIALITY_REQUIRED,N_("Confidentiality required"));
+ C(LDAP_SASL_BIND_IN_PROGRESS, N_("SASL bind in progress"));
+
+ C(LDAP_NO_SUCH_ATTRIBUTE, N_("No such attribute"));
+ C(LDAP_UNDEFINED_TYPE, N_("Undefined attribute type"));
+ C(LDAP_INAPPROPRIATE_MATCHING, N_("Inappropriate matching"));
+ C(LDAP_CONSTRAINT_VIOLATION, N_("Constraint violation"));
+ C(LDAP_TYPE_OR_VALUE_EXISTS, N_("Type or value exists"));
+ C(LDAP_INVALID_SYNTAX, N_("Invalid syntax"));
+
+ C(LDAP_NO_SUCH_OBJECT, N_("No such object"));
+ C(LDAP_ALIAS_PROBLEM, N_("Alias problem"));
+ C(LDAP_INVALID_DN_SYNTAX, N_("Invalid DN syntax"));
+
+ C(LDAP_ALIAS_DEREF_PROBLEM, N_("Alias dereferencing problem"));
+
+ C(LDAP_INAPPROPRIATE_AUTH, N_("Inappropriate authentication"));
+ C(LDAP_INVALID_CREDENTIALS, N_("Invalid credentials"));
+ C(LDAP_INSUFFICIENT_ACCESS, N_("Insufficient access"));
+ C(LDAP_BUSY, N_("Server is busy"));
+ C(LDAP_UNAVAILABLE, N_("Server is unavailable"));
+ C(LDAP_UNWILLING_TO_PERFORM, N_("Server is unwilling to perform"));
+ C(LDAP_LOOP_DETECT, N_("Loop detected"));
+
+ C(LDAP_NAMING_VIOLATION, N_("Naming violation"));
+ C(LDAP_OBJECT_CLASS_VIOLATION, N_("Object class violation"));
+ C(LDAP_NOT_ALLOWED_ON_NONLEAF, N_("Operation not allowed on non-leaf"));
+ C(LDAP_NOT_ALLOWED_ON_RDN, N_("Operation not allowed on RDN"));
+ C(LDAP_ALREADY_EXISTS, N_("Already exists"));
+ C(LDAP_NO_OBJECT_CLASS_MODS, N_("Cannot modify object class"));
+
+ C(LDAP_AFFECTS_MULTIPLE_DSAS, N_("Operation affects multiple DSAs"));
+
+ /* Virtual List View draft */
+ C(LDAP_VLV_ERROR, N_("Virtual List View error"));
+
+ C(LDAP_OTHER, N_("Other (e.g., implementation specific) error"));
+
+ /* LDAPv2 (RFC 1777) codes */
+ C(LDAP_PARTIAL_RESULTS, N_("Partial results and referral received"));
+ C(LDAP_IS_LEAF, N_("Entry is a leaf"));
+
+ /* Connection-less LDAP (CLDAP - RFC 1798) code */
+ C(LDAP_RESULTS_TOO_LARGE, N_("Results too large"));
+
+ /* Cancel Operation (RFC 3909) codes */
+ C(LDAP_CANCELLED, N_("Cancelled"));
+ C(LDAP_NO_SUCH_OPERATION, N_("No Operation to Cancel"));
+ C(LDAP_TOO_LATE, N_("Too Late to Cancel"));
+ C(LDAP_CANNOT_CANCEL, N_("Cannot Cancel"));
+
+ /* Assert Control (RFC 4528 and old internet-draft) codes */
+ C(LDAP_ASSERTION_FAILED, N_("Assertion Failed"));
+ C(LDAP_X_ASSERTION_FAILED, N_("Assertion Failed (X)"));
+
+ /* Proxied Authorization Control (RFC 4370 and I-D) codes */
+ C(LDAP_PROXIED_AUTHORIZATION_DENIED, N_("Proxied Authorization Denied"));
+ C(LDAP_X_PROXY_AUTHZ_FAILURE, N_("Proxy Authorization Failure (X)"));
+
+ /* Content Sync Operation (RFC 4533 and I-D) codes */
+ C(LDAP_SYNC_REFRESH_REQUIRED, N_("Content Sync Refresh Required"));
+ C(LDAP_X_SYNC_REFRESH_REQUIRED, N_("Content Sync Refresh Required (X)"));
+
+ /* No-Op Control (draft-zeilenga-ldap-noop) code */
+ C(LDAP_X_NO_OPERATION, N_("No Operation (X)"));
+
+ /* Client Update Protocol (RFC 3928) codes */
+ C(LDAP_CUP_RESOURCES_EXHAUSTED, N_("LCUP Resources Exhausted"));
+ C(LDAP_CUP_SECURITY_VIOLATION, N_("LCUP Security Violation"));
+ C(LDAP_CUP_INVALID_DATA, N_("LCUP Invalid Data"));
+ C(LDAP_CUP_UNSUPPORTED_SCHEME, N_("LCUP Unsupported Scheme"));
+ C(LDAP_CUP_RELOAD_REQUIRED, N_("LCUP Reload Required"));
+
+ C(LDAP_TXN_SPECIFY_OKAY, N_("TXN specify okay"));
+ C(LDAP_TXN_ID_INVALID, N_("TXN ID is invalid"));
+
+ /* API codes - renumbered since draft-ietf-ldapext-ldap-c-api */
+ C(LDAP_SERVER_DOWN, N_("Can't contact LDAP server"));
+ C(LDAP_LOCAL_ERROR, N_("Local error"));
+ C(LDAP_ENCODING_ERROR, N_("Encoding error"));
+ C(LDAP_DECODING_ERROR, N_("Decoding error"));
+ C(LDAP_TIMEOUT, N_("Timed out"));
+ C(LDAP_AUTH_UNKNOWN, N_("Unknown authentication method"));
+ C(LDAP_FILTER_ERROR, N_("Bad search filter"));
+ C(LDAP_USER_CANCELLED, N_("User cancelled operation"));
+ C(LDAP_PARAM_ERROR, N_("Bad parameter to an ldap routine"));
+ C(LDAP_NO_MEMORY, N_("Out of memory"));
+ C(LDAP_CONNECT_ERROR, N_("Connect error"));
+ C(LDAP_NOT_SUPPORTED, N_("Not Supported"));
+ C(LDAP_CONTROL_NOT_FOUND, N_("Control not found"));
+ C(LDAP_NO_RESULTS_RETURNED, N_("No results returned"));
+ C(LDAP_MORE_RESULTS_TO_RETURN, N_("More results to return"));
+ C(LDAP_CLIENT_LOOP, N_("Client Loop"));
+ C(LDAP_REFERRAL_LIMIT_EXCEEDED, N_("Referral Limit Exceeded"));
+ C(LDAP_X_CONNECTING, N_("Connecting (X)"));
+# undef C
+
+ default:
+ m = (LDAP_API_ERROR(err) ? N_("Unknown API error")
+ : LDAP_E_ERROR(err) ? N_("Unknown (extension) error")
+ : LDAP_X_ERROR(err) ? N_("Unknown (private extension) error")
+ : N_("Unknown error"));
+ break;
+ }
+
+ return _(m);
+}
+
+/* deprecated */
+void
+ldap_perror( LDAP *ld, LDAP_CONST char *str )
+{
+ int i;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( str != NULL );
+
+ fprintf( stderr, "%s: %s (%d)\n",
+ str ? str : "ldap_perror",
+ ldap_err2string( ld->ld_errno ),
+ ld->ld_errno );
+
+ if ( ld->ld_matched != NULL && ld->ld_matched[0] != '\0' ) {
+ fprintf( stderr, _("\tmatched DN: %s\n"), ld->ld_matched );
+ }
+
+ if ( ld->ld_error != NULL && ld->ld_error[0] != '\0' ) {
+ fprintf( stderr, _("\tadditional info: %s\n"), ld->ld_error );
+ }
+
+ if ( ld->ld_referrals != NULL && ld->ld_referrals[0] != NULL) {
+ fprintf( stderr, _("\treferrals:\n") );
+ for (i=0; ld->ld_referrals[i]; i++) {
+ fprintf( stderr, _("\t\t%s\n"), ld->ld_referrals[i] );
+ }
+ }
+
+ fflush( stderr );
+}
+
+/* deprecated */
+int
+ldap_result2error( LDAP *ld, LDAPMessage *r, int freeit )
+{
+ int rc, err;
+
+ rc = ldap_parse_result( ld, r, &err,
+ NULL, NULL, NULL, NULL, freeit );
+
+ return err != LDAP_SUCCESS ? err : rc;
+}
+
+/*
+ * Parse LDAPResult Messages:
+ *
+ * LDAPResult ::= SEQUENCE {
+ * resultCode ENUMERATED,
+ * matchedDN LDAPDN,
+ * errorMessage LDAPString,
+ * referral [3] Referral OPTIONAL }
+ *
+ * including Bind results:
+ *
+ * BindResponse ::= [APPLICATION 1] SEQUENCE {
+ * COMPONENTS OF LDAPResult,
+ * serverSaslCreds [7] OCTET STRING OPTIONAL }
+ *
+ * and ExtendedOp results:
+ *
+ * ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
+ * COMPONENTS OF LDAPResult,
+ * responseName [10] LDAPOID OPTIONAL,
+ * response [11] OCTET STRING OPTIONAL }
+ *
+ */
+int
+ldap_parse_result(
+ LDAP *ld,
+ LDAPMessage *r,
+ int *errcodep,
+ char **matcheddnp,
+ char **errmsgp,
+ char ***referralsp,
+ LDAPControl ***serverctrls,
+ int freeit )
+{
+ LDAPMessage *lm;
+ ber_int_t errcode = LDAP_SUCCESS;
+
+ ber_tag_t tag;
+ BerElement *ber;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_parse_result\n" );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( r != NULL );
+
+ if(errcodep != NULL) *errcodep = LDAP_SUCCESS;
+ if(matcheddnp != NULL) *matcheddnp = NULL;
+ if(errmsgp != NULL) *errmsgp = NULL;
+ if(referralsp != NULL) *referralsp = NULL;
+ if(serverctrls != NULL) *serverctrls = NULL;
+
+ LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
+ /* Find the result, last msg in chain... */
+ lm = r->lm_chain_tail;
+ /* FIXME: either this is not possible (assert?)
+ * or it should be handled */
+ if ( lm != NULL ) {
+ switch ( lm->lm_msgtype ) {
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_REFERENCE:
+ case LDAP_RES_INTERMEDIATE:
+ lm = NULL;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if( lm == NULL ) {
+ errcode = ld->ld_errno = LDAP_NO_RESULTS_RETURNED;
+ LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
+ goto done;
+ }
+
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ ld->ld_error = NULL;
+ }
+ if ( ld->ld_matched ) {
+ LDAP_FREE( ld->ld_matched );
+ ld->ld_matched = NULL;
+ }
+ if ( ld->ld_referrals ) {
+ LDAP_VFREE( ld->ld_referrals );
+ ld->ld_referrals = NULL;
+ }
+
+ /* parse results */
+
+ ber = ber_dup( lm->lm_ber );
+
+ if ( ld->ld_version < LDAP_VERSION2 ) {
+ tag = ber_scanf( ber, "{iA}",
+ &ld->ld_errno, &ld->ld_error );
+
+ } else {
+ ber_len_t len;
+
+ tag = ber_scanf( ber, "{iAA" /*}*/,
+ &ld->ld_errno, &ld->ld_matched, &ld->ld_error );
+
+ if( tag != LBER_ERROR ) {
+ /* peek for referrals */
+ if( ber_peek_tag(ber, &len) == LDAP_TAG_REFERRAL ) {
+ tag = ber_scanf( ber, "v", &ld->ld_referrals );
+ }
+ }
+
+ /* need to clean out misc items */
+ if( tag != LBER_ERROR ) {
+ if( lm->lm_msgtype == LDAP_RES_BIND ) {
+ /* look for sasl result credentials */
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SASL_RES_CREDS ) {
+ /* skip 'em */
+ tag = ber_scanf( ber, "x" );
+ }
+
+ } else if( lm->lm_msgtype == LDAP_RES_EXTENDED ) {
+ /* look for exop result oid or value */
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_EXOP_RES_OID ) {
+ /* skip 'em */
+ tag = ber_scanf( ber, "x" );
+ }
+
+ if ( tag != LBER_ERROR &&
+ ber_peek_tag( ber, &len ) == LDAP_TAG_EXOP_RES_VALUE )
+ {
+ /* skip 'em */
+ tag = ber_scanf( ber, "x" );
+ }
+ }
+ }
+
+ if( tag != LBER_ERROR ) {
+ int rc = ldap_pvt_get_controls( ber, serverctrls );
+
+ if( rc != LDAP_SUCCESS ) {
+ tag = LBER_ERROR;
+ }
+ }
+
+ if( tag != LBER_ERROR ) {
+ tag = ber_scanf( ber, /*{*/"}" );
+ }
+ }
+
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = errcode = LDAP_DECODING_ERROR;
+ }
+
+ if( ber != NULL ) {
+ ber_free( ber, 0 );
+ }
+
+ /* return */
+ if( errcodep != NULL ) {
+ *errcodep = ld->ld_errno;
+ }
+ if ( errcode == LDAP_SUCCESS ) {
+ if( matcheddnp != NULL ) {
+ if ( ld->ld_matched )
+ {
+ *matcheddnp = LDAP_STRDUP( ld->ld_matched );
+ }
+ }
+ if( errmsgp != NULL ) {
+ if ( ld->ld_error )
+ {
+ *errmsgp = LDAP_STRDUP( ld->ld_error );
+ }
+ }
+
+ if( referralsp != NULL) {
+ *referralsp = ldap_value_dup( ld->ld_referrals );
+ }
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
+
+done:
+ if ( freeit ) {
+ ldap_msgfree( r );
+ }
+
+ return errcode;
+}
diff --git a/libraries/libldap/extended.c b/libraries/libldap/extended.c
new file mode 100644
index 0000000..b492fcd
--- /dev/null
+++ b/libraries/libldap/extended.c
@@ -0,0 +1,419 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_log.h"
+
+BerElement *
+ldap_build_extended_req(
+ LDAP *ld,
+ LDAP_CONST char *reqoid,
+ struct berval *reqdata,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp )
+{
+ BerElement *ber;
+ int rc;
+
+ /* create a message to send */
+ if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
+ return( NULL );
+ }
+
+ LDAP_NEXT_MSGID( ld, *msgidp );
+ if ( reqdata != NULL ) {
+ rc = ber_printf( ber, "{it{tstON}", /* '}' */
+ *msgidp, LDAP_REQ_EXTENDED,
+ LDAP_TAG_EXOP_REQ_OID, reqoid,
+ LDAP_TAG_EXOP_REQ_VALUE, reqdata );
+
+ } else {
+ rc = ber_printf( ber, "{it{tsN}", /* '}' */
+ *msgidp, LDAP_REQ_EXTENDED,
+ LDAP_TAG_EXOP_REQ_OID, reqoid );
+ }
+
+ if( rc == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ /* Put Server Controls */
+ if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ return( ber );
+}
+
+/*
+ * LDAPv3 Extended Operation Request
+ * ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
+ * requestName [0] LDAPOID,
+ * requestValue [1] OCTET STRING OPTIONAL
+ * }
+ *
+ * LDAPv3 Extended Operation Response
+ * ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
+ * COMPONENTS OF LDAPResult,
+ * responseName [10] LDAPOID OPTIONAL,
+ * response [11] OCTET STRING OPTIONAL
+ * }
+ *
+ * (Source RFC 4511)
+ */
+
+int
+ldap_extended_operation(
+ LDAP *ld,
+ LDAP_CONST char *reqoid,
+ struct berval *reqdata,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ BerElement *ber;
+ ber_int_t id;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_extended_operation\n" );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( reqoid != NULL && *reqoid != '\0' );
+ assert( msgidp != NULL );
+
+ /* must be version 3 (or greater) */
+ if ( ld->ld_version < LDAP_VERSION3 ) {
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ return( ld->ld_errno );
+ }
+
+ ber = ldap_build_extended_req( ld, reqoid, reqdata,
+ sctrls, cctrls, &id );
+ if ( !ber )
+ return( ld->ld_errno );
+
+ /* send the message */
+ *msgidp = ldap_send_initial_request( ld, LDAP_REQ_EXTENDED, NULL, ber, id );
+
+ return( *msgidp < 0 ? ld->ld_errno : LDAP_SUCCESS );
+}
+
+int
+ldap_extended_operation_s(
+ LDAP *ld,
+ LDAP_CONST char *reqoid,
+ struct berval *reqdata,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ char **retoidp,
+ struct berval **retdatap )
+{
+ int rc;
+ int msgid;
+ LDAPMessage *res;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_extended_operation_s\n" );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( reqoid != NULL && *reqoid != '\0' );
+
+ rc = ldap_extended_operation( ld, reqoid, reqdata,
+ sctrls, cctrls, &msgid );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return( rc );
+ }
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) {
+ return( ld->ld_errno );
+ }
+
+ if ( retoidp != NULL ) *retoidp = NULL;
+ if ( retdatap != NULL ) *retdatap = NULL;
+
+ rc = ldap_parse_extended_result( ld, res, retoidp, retdatap, 0 );
+
+ if( rc != LDAP_SUCCESS ) {
+ ldap_msgfree( res );
+ return rc;
+ }
+
+ return( ldap_result2error( ld, res, 1 ) );
+}
+
+/* Parse an extended result */
+int
+ldap_parse_extended_result (
+ LDAP *ld,
+ LDAPMessage *res,
+ char **retoidp,
+ struct berval **retdatap,
+ int freeit )
+{
+ BerElement *ber;
+ ber_tag_t rc;
+ ber_tag_t tag;
+ ber_len_t len;
+ struct berval *resdata;
+ ber_int_t errcode;
+ char *resoid;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( res != NULL );
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_parse_extended_result\n" );
+
+ if( ld->ld_version < LDAP_VERSION3 ) {
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ return ld->ld_errno;
+ }
+
+ if( res->lm_msgtype != LDAP_RES_EXTENDED ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ if( retoidp != NULL ) *retoidp = NULL;
+ if( retdatap != NULL ) *retdatap = NULL;
+
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ ld->ld_error = NULL;
+ }
+
+ if ( ld->ld_matched ) {
+ LDAP_FREE( ld->ld_matched );
+ ld->ld_matched = NULL;
+ }
+
+ ber = ber_dup( res->lm_ber );
+
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ rc = ber_scanf( ber, "{eAA" /*}*/, &errcode,
+ &ld->ld_matched, &ld->ld_error );
+
+ if( rc == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 0 );
+ return ld->ld_errno;
+ }
+
+ resoid = NULL;
+ resdata = NULL;
+
+ tag = ber_peek_tag( ber, &len );
+
+ if( tag == LDAP_TAG_REFERRAL ) {
+ /* skip over referral */
+ if( ber_scanf( ber, "x" ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 0 );
+ return ld->ld_errno;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if( tag == LDAP_TAG_EXOP_RES_OID ) {
+ /* we have a resoid */
+ if( ber_scanf( ber, "a", &resoid ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 0 );
+ return ld->ld_errno;
+ }
+
+ assert( resoid[ 0 ] != '\0' );
+
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if( tag == LDAP_TAG_EXOP_RES_VALUE ) {
+ /* we have a resdata */
+ if( ber_scanf( ber, "O", &resdata ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 0 );
+ if( resoid != NULL ) LDAP_FREE( resoid );
+ return ld->ld_errno;
+ }
+ }
+
+ ber_free( ber, 0 );
+
+ if( retoidp != NULL ) {
+ *retoidp = resoid;
+ } else {
+ LDAP_FREE( resoid );
+ }
+
+ if( retdatap != NULL ) {
+ *retdatap = resdata;
+ } else {
+ ber_bvfree( resdata );
+ }
+
+ ld->ld_errno = errcode;
+
+ if( freeit ) {
+ ldap_msgfree( res );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+
+/* Parse an extended partial */
+int
+ldap_parse_intermediate (
+ LDAP *ld,
+ LDAPMessage *res,
+ char **retoidp,
+ struct berval **retdatap,
+ LDAPControl ***serverctrls,
+ int freeit )
+{
+ BerElement *ber;
+ ber_tag_t tag;
+ ber_len_t len;
+ struct berval *resdata;
+ char *resoid;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( res != NULL );
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_parse_intermediate\n" );
+
+ if( ld->ld_version < LDAP_VERSION3 ) {
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ return ld->ld_errno;
+ }
+
+ if( res->lm_msgtype != LDAP_RES_INTERMEDIATE ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ if( retoidp != NULL ) *retoidp = NULL;
+ if( retdatap != NULL ) *retdatap = NULL;
+ if( serverctrls != NULL ) *serverctrls = NULL;
+
+ ber = ber_dup( res->lm_ber );
+
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ tag = ber_scanf( ber, "{" /*}*/ );
+
+ if( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 0 );
+ return ld->ld_errno;
+ }
+
+ resoid = NULL;
+ resdata = NULL;
+
+ tag = ber_peek_tag( ber, &len );
+
+ /*
+ * NOTE: accept intermediate and extended response tag values
+ * as older versions of slapd(8) incorrectly used extended
+ * response tags.
+ * Should be removed when 2.2 is moved to Historic.
+ */
+ if( tag == LDAP_TAG_IM_RES_OID || tag == LDAP_TAG_EXOP_RES_OID ) {
+ /* we have a resoid */
+ if( ber_scanf( ber, "a", &resoid ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 0 );
+ return ld->ld_errno;
+ }
+
+ assert( resoid[ 0 ] != '\0' );
+
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if( tag == LDAP_TAG_IM_RES_VALUE || tag == LDAP_TAG_EXOP_RES_VALUE ) {
+ /* we have a resdata */
+ if( ber_scanf( ber, "O", &resdata ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 0 );
+ if( resoid != NULL ) LDAP_FREE( resoid );
+ return ld->ld_errno;
+ }
+ }
+
+ if ( serverctrls == NULL ) {
+ ld->ld_errno = LDAP_SUCCESS;
+ goto free_and_return;
+ }
+
+ if ( ber_scanf( ber, /*{*/ "}" ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ goto free_and_return;
+ }
+
+ ld->ld_errno = ldap_pvt_get_controls( ber, serverctrls );
+
+free_and_return:
+ ber_free( ber, 0 );
+
+ if( retoidp != NULL ) {
+ *retoidp = resoid;
+ } else {
+ LDAP_FREE( resoid );
+ }
+
+ if( retdatap != NULL ) {
+ *retdatap = resdata;
+ } else {
+ ber_bvfree( resdata );
+ }
+
+ if( freeit ) {
+ ldap_msgfree( res );
+ }
+
+ return ld->ld_errno;
+}
+
diff --git a/libraries/libldap/fetch.c b/libraries/libldap/fetch.c
new file mode 100644
index 0000000..9e426dc
--- /dev/null
+++ b/libraries/libldap/fetch.c
@@ -0,0 +1,146 @@
+/* fetch.c - routines for fetching data at URLs */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Kurt D. Zeilenga.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* This work was initially developed by Kurt D. Zeilenga for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/time.h>
+
+#ifdef HAVE_FETCH
+#include <fetch.h>
+#endif
+
+#include "lber_pvt.h"
+#include "ldap_pvt.h"
+#include "ldap_config.h"
+#include "ldif.h"
+
+FILE *
+ldif_open_url(
+ LDAP_CONST char *urlstr )
+{
+ FILE *url;
+
+ if( strncasecmp( "file:", urlstr, sizeof("file:")-1 ) == 0 ) {
+ char *p;
+ urlstr += sizeof("file:")-1;
+
+ /* we don't check for LDAP_DIRSEP since URLs should contain '/' */
+ if ( urlstr[0] == '/' && urlstr[1] == '/' ) {
+ urlstr += 2;
+ /* path must be absolute if authority is present
+ * technically, file://hostname/path is also legal but we don't
+ * accept a non-empty hostname
+ */
+ if ( urlstr[0] != '/' ) {
+#ifdef _WIN32
+ /* An absolute path in improper file://C:/foo/bar format */
+ if ( urlstr[1] != ':' )
+#endif
+ return NULL;
+ }
+#ifdef _WIN32
+ /* An absolute path in proper file:///C:/foo/bar format */
+ if ( urlstr[2] == ':' )
+ urlstr++;
+#endif
+ }
+
+ p = ber_strdup( urlstr );
+
+ /* But we should convert to LDAP_DIRSEP before use */
+ if ( LDAP_DIRSEP[0] != '/' ) {
+ char *s = p;
+ while (( s = strchr( s, '/' )))
+ *s++ = LDAP_DIRSEP[0];
+ }
+
+ ldap_pvt_hex_unescape( p );
+
+ url = fopen( p, "rb" );
+
+ ber_memfree( p );
+ } else {
+#ifdef HAVE_FETCH
+ url = fetchGetURL( (char*) urlstr, "" );
+#else
+ url = NULL;
+#endif
+ }
+ return url;
+}
+
+int
+ldif_fetch_url(
+ LDAP_CONST char *urlstr,
+ char **valuep,
+ ber_len_t *vlenp )
+{
+ FILE *url;
+ char buffer[1024];
+ char *p = NULL;
+ size_t total;
+ size_t bytes;
+
+ *valuep = NULL;
+ *vlenp = 0;
+
+ url = ldif_open_url( urlstr );
+
+ if( url == NULL ) {
+ return -1;
+ }
+
+ total = 0;
+
+ while( (bytes = fread( buffer, 1, sizeof(buffer), url )) != 0 ) {
+ char *newp = ber_memrealloc( p, total + bytes + 1 );
+ if( newp == NULL ) {
+ ber_memfree( p );
+ fclose( url );
+ return -1;
+ }
+ p = newp;
+ AC_MEMCPY( &p[total], buffer, bytes );
+ total += bytes;
+ }
+
+ fclose( url );
+
+ if( total == 0 ) {
+ char *newp = ber_memrealloc( p, 1 );
+ if( newp == NULL ) {
+ ber_memfree( p );
+ return -1;
+ }
+ p = newp;
+ }
+
+ p[total] = '\0';
+ *valuep = p;
+ *vlenp = total;
+
+ return 0;
+}
diff --git a/libraries/libldap/filter.c b/libraries/libldap/filter.c
new file mode 100644
index 0000000..064b2ea
--- /dev/null
+++ b/libraries/libldap/filter.c
@@ -0,0 +1,1115 @@
+/* search.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+static int put_simple_vrFilter LDAP_P((
+ BerElement *ber,
+ char *str ));
+
+static int put_vrFilter_list LDAP_P((
+ BerElement *ber,
+ char *str ));
+
+static char *put_complex_filter LDAP_P((
+ BerElement *ber,
+ char *str,
+ ber_tag_t tag,
+ int not ));
+
+static int put_simple_filter LDAP_P((
+ BerElement *ber,
+ char *str ));
+
+static int put_substring_filter LDAP_P((
+ BerElement *ber,
+ char *type,
+ char *str,
+ char *nextstar ));
+
+static int put_filter_list LDAP_P((
+ BerElement *ber,
+ char *str,
+ ber_tag_t tag ));
+
+static int ldap_is_oid ( const char *str )
+{
+ int i;
+
+ if( LDAP_ALPHA( str[0] )) {
+ for( i=1; str[i]; i++ ) {
+ if( !LDAP_LDH( str[i] )) {
+ return 0;
+ }
+ }
+ return 1;
+
+ } else if LDAP_DIGIT( str[0] ) {
+ int dot=0;
+ for( i=1; str[i]; i++ ) {
+ if( LDAP_DIGIT( str[i] )) {
+ dot=0;
+
+ } else if ( str[i] == '.' ) {
+ if( ++dot > 1 ) return 0;
+
+ } else {
+ return 0;
+ }
+ }
+ return !dot;
+ }
+
+ return 0;
+}
+
+static int ldap_is_desc ( const char *str )
+{
+ int i;
+
+ if( LDAP_ALPHA( str[0] )) {
+ for( i=1; str[i]; i++ ) {
+ if( str[i] == ';' ) {
+ str = &str[i+1];
+ goto options;
+ }
+
+ if( !LDAP_LDH( str[i] )) {
+ return 0;
+ }
+ }
+ return 1;
+
+ } else if LDAP_DIGIT( str[0] ) {
+ int dot=0;
+ for( i=1; str[i]; i++ ) {
+ if( str[i] == ';' ) {
+ if( dot ) return 0;
+ str = &str[i+1];
+ goto options;
+ }
+
+ if( LDAP_DIGIT( str[i] )) {
+ dot=0;
+
+ } else if ( str[i] == '.' ) {
+ if( ++dot > 1 ) return 0;
+
+ } else {
+ return 0;
+ }
+ }
+ return !dot;
+ }
+
+ return 0;
+
+options:
+ if( !LDAP_LDH( str[0] )) {
+ return 0;
+ }
+ for( i=1; str[i]; i++ ) {
+ if( str[i] == ';' ) {
+ str = &str[i+1];
+ goto options;
+ }
+ if( !LDAP_LDH( str[i] )) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static char *
+find_right_paren( char *s )
+{
+ int balance, escape;
+
+ balance = 1;
+ escape = 0;
+ while ( *s && balance ) {
+ if ( !escape ) {
+ if ( *s == '(' ) {
+ balance++;
+ } else if ( *s == ')' ) {
+ balance--;
+ }
+ }
+
+ escape = ( *s == '\\' && !escape );
+
+ if ( balance ) s++;
+ }
+
+ return *s ? s : NULL;
+}
+
+static int hex2value( int c )
+{
+ if( c >= '0' && c <= '9' ) {
+ return c - '0';
+ }
+
+ if( c >= 'A' && c <= 'F' ) {
+ return c + (10 - (int) 'A');
+ }
+
+ if( c >= 'a' && c <= 'f' ) {
+ return c + (10 - (int) 'a');
+ }
+
+ return -1;
+}
+
+char *
+ldap_pvt_find_wildcard( const char *s )
+{
+ for( ; *s; s++ ) {
+ switch( *s ) {
+ case '*': /* found wildcard */
+ return (char *) s;
+
+ case '(':
+ case ')':
+ return NULL;
+
+ case '\\':
+ if( s[1] == '\0' ) return NULL;
+
+ if( LDAP_HEX( s[1] ) && LDAP_HEX( s[2] ) ) {
+ s+=2;
+
+ } else switch( s[1] ) {
+ default:
+ return NULL;
+
+ /* allow RFC 1960 escapes */
+ case '*':
+ case '(':
+ case ')':
+ case '\\':
+ s++;
+ }
+ }
+ }
+
+ return (char *) s;
+}
+
+/* unescape filter value */
+/* support both LDAP v2 and v3 escapes */
+/* output can include nul characters! */
+ber_slen_t
+ldap_pvt_filter_value_unescape( char *fval )
+{
+ ber_slen_t r, v;
+ int v1, v2;
+
+ for( r=v=0; fval[v] != '\0'; v++ ) {
+ switch( fval[v] ) {
+ case '(':
+ case ')':
+ case '*':
+ return -1;
+
+ case '\\':
+ /* escape */
+ v++;
+
+ if ( fval[v] == '\0' ) {
+ /* escape at end of string */
+ return -1;
+ }
+
+ if (( v1 = hex2value( fval[v] )) >= 0 ) {
+ /* LDAPv3 escape */
+ if (( v2 = hex2value( fval[v+1] )) < 0 ) {
+ /* must be two digit code */
+ return -1;
+ }
+
+ fval[r++] = v1 * 16 + v2;
+ v++;
+
+ } else {
+ /* LDAPv2 escape */
+ switch( fval[v] ) {
+ case '(':
+ case ')':
+ case '*':
+ case '\\':
+ fval[r++] = fval[v];
+ break;
+ default:
+ /* illegal escape */
+ return -1;
+ }
+ }
+ break;
+
+ default:
+ fval[r++] = fval[v];
+ }
+ }
+
+ fval[r] = '\0';
+ return r;
+}
+
+static char *
+put_complex_filter( BerElement *ber, char *str, ber_tag_t tag, int not )
+{
+ char *next;
+
+ /*
+ * We have (x(filter)...) with str sitting on
+ * the x. We have to find the paren matching
+ * the one before the x and put the intervening
+ * filters by calling put_filter_list().
+ */
+
+ /* put explicit tag */
+ if ( ber_printf( ber, "t{" /*"}"*/, tag ) == -1 ) {
+ return NULL;
+ }
+
+ str++;
+ if ( (next = find_right_paren( str )) == NULL ) {
+ return NULL;
+ }
+
+ *next = '\0';
+ if ( put_filter_list( ber, str, tag ) == -1 ) {
+ return NULL;
+ }
+
+ /* close the '(' */
+ *next++ = ')';
+
+ /* flush explicit tagged thang */
+ if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) {
+ return NULL;
+ }
+
+ return next;
+}
+
+int
+ldap_pvt_put_filter( BerElement *ber, const char *str_in )
+{
+ int rc;
+ char *freeme;
+ char *str;
+ char *next;
+ int parens, balance, escape;
+
+ /*
+ * A Filter looks like this (RFC 4511 as extended by RFC 4526):
+ * Filter ::= CHOICE {
+ * and [0] SET SIZE (0..MAX) OF filter Filter,
+ * or [1] SET SIZE (0..MAX) OF filter Filter,
+ * not [2] Filter,
+ * equalityMatch [3] AttributeValueAssertion,
+ * substrings [4] SubstringFilter,
+ * greaterOrEqual [5] AttributeValueAssertion,
+ * lessOrEqual [6] AttributeValueAssertion,
+ * present [7] AttributeDescription,
+ * approxMatch [8] AttributeValueAssertion,
+ * extensibleMatch [9] MatchingRuleAssertion,
+ * ... }
+ *
+ * SubstringFilter ::= SEQUENCE {
+ * type AttributeDescription,
+ * substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE {
+ * initial [0] AssertionValue, -- only once
+ * any [1] AssertionValue,
+ * final [2] AssertionValue -- only once
+ * }
+ * }
+ *
+ * MatchingRuleAssertion ::= SEQUENCE {
+ * matchingRule [1] MatchingRuleId OPTIONAL,
+ * type [2] AttributeDescription OPTIONAL,
+ * matchValue [3] AssertionValue,
+ * dnAttributes [4] BOOLEAN DEFAULT FALSE }
+ *
+ * Note: tags in a CHOICE are always explicit
+ */
+
+ Debug1( LDAP_DEBUG_TRACE, "put_filter: \"%s\"\n", str_in );
+
+ freeme = LDAP_STRDUP( str_in );
+ if( freeme == NULL ) return LDAP_NO_MEMORY;
+ str = freeme;
+
+ parens = 0;
+ while ( *str ) {
+ switch ( *str ) {
+ case '(': /*')'*/
+ str++;
+ parens++;
+
+ /* skip spaces */
+ while( LDAP_SPACE( *str ) ) str++;
+
+ switch ( *str ) {
+ case '&':
+ Debug0( LDAP_DEBUG_TRACE, "put_filter: AND\n" );
+
+ str = put_complex_filter( ber, str,
+ LDAP_FILTER_AND, 0 );
+ if( str == NULL ) {
+ rc = -1;
+ goto done;
+ }
+
+ parens--;
+ break;
+
+ case '|':
+ Debug0( LDAP_DEBUG_TRACE, "put_filter: OR\n" );
+
+ str = put_complex_filter( ber, str,
+ LDAP_FILTER_OR, 0 );
+ if( str == NULL ) {
+ rc = -1;
+ goto done;
+ }
+
+ parens--;
+ break;
+
+ case '!':
+ Debug0( LDAP_DEBUG_TRACE, "put_filter: NOT\n" );
+
+ str = put_complex_filter( ber, str,
+ LDAP_FILTER_NOT, 0 );
+ if( str == NULL ) {
+ rc = -1;
+ goto done;
+ }
+
+ parens--;
+ break;
+
+ case '(':
+ rc = -1;
+ goto done;
+
+ default:
+ Debug0( LDAP_DEBUG_TRACE, "put_filter: simple\n" );
+
+ balance = 1;
+ escape = 0;
+ next = str;
+
+ while ( *next && balance ) {
+ if ( escape == 0 ) {
+ if ( *next == '(' ) {
+ balance++;
+ } else if ( *next == ')' ) {
+ balance--;
+ }
+ }
+
+ if ( *next == '\\' && ! escape ) {
+ escape = 1;
+ } else {
+ escape = 0;
+ }
+
+ if ( balance ) next++;
+ }
+
+ if ( balance != 0 ) {
+ rc = -1;
+ goto done;
+ }
+
+ *next = '\0';
+
+ if ( put_simple_filter( ber, str ) == -1 ) {
+ rc = -1;
+ goto done;
+ }
+
+ *next++ = /*'('*/ ')';
+
+ str = next;
+ parens--;
+ break;
+ }
+ break;
+
+ case /*'('*/ ')':
+ Debug0( LDAP_DEBUG_TRACE, "put_filter: end\n" );
+ if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) {
+ rc = -1;
+ goto done;
+ }
+ str++;
+ parens--;
+ break;
+
+ case ' ':
+ str++;
+ break;
+
+ default: /* assume it's a simple type=value filter */
+ Debug0( LDAP_DEBUG_TRACE, "put_filter: default\n" );
+ next = strchr( str, '\0' );
+ if ( put_simple_filter( ber, str ) == -1 ) {
+ rc = -1;
+ goto done;
+ }
+ str = next;
+ break;
+ }
+ if ( !parens )
+ break;
+ }
+
+ rc = ( parens || *str ) ? -1 : 0;
+
+done:
+ LDAP_FREE( freeme );
+ return rc;
+}
+
+/*
+ * Put a list of filters like this "(filter1)(filter2)..."
+ */
+
+static int
+put_filter_list( BerElement *ber, char *str, ber_tag_t tag )
+{
+ char *next = NULL;
+ char save;
+
+ Debug1( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n",
+ str );
+
+ while ( *str ) {
+ while ( *str && LDAP_SPACE( (unsigned char) *str ) ) {
+ str++;
+ }
+ if ( *str == '\0' ) break;
+
+ if ( (next = find_right_paren( str + 1 )) == NULL ) {
+ return -1;
+ }
+ save = *++next;
+
+ /* now we have "(filter)" with str pointing to it */
+ *next = '\0';
+ if ( ldap_pvt_put_filter( ber, str ) == -1 ) return -1;
+ *next = save;
+ str = next;
+
+ if( tag == LDAP_FILTER_NOT ) break;
+ }
+
+ if( tag == LDAP_FILTER_NOT && ( next == NULL || *str )) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+put_simple_filter(
+ BerElement *ber,
+ char *str )
+{
+ char *s;
+ char *value;
+ ber_tag_t ftype;
+ int rc = -1;
+
+ Debug1( LDAP_DEBUG_TRACE, "put_simple_filter: \"%s\"\n",
+ str );
+
+ str = LDAP_STRDUP( str );
+ if( str == NULL ) return -1;
+
+ if ( (s = strchr( str, '=' )) == NULL ) {
+ goto done;
+ }
+
+ value = s + 1;
+ *s-- = '\0';
+
+ switch ( *s ) {
+ case '<':
+ ftype = LDAP_FILTER_LE;
+ *s = '\0';
+ break;
+
+ case '>':
+ ftype = LDAP_FILTER_GE;
+ *s = '\0';
+ break;
+
+ case '~':
+ ftype = LDAP_FILTER_APPROX;
+ *s = '\0';
+ break;
+
+ case ':':
+ /* RFC 4515 extensible filters are off the form:
+ * type [:dn] [:rule] := value
+ * or [:dn]:rule := value
+ */
+ ftype = LDAP_FILTER_EXT;
+ *s = '\0';
+
+ {
+ char *dn = strchr( str, ':' );
+ char *rule = NULL;
+
+ if( dn != NULL ) {
+ *dn++ = '\0';
+ rule = strchr( dn, ':' );
+
+ if( rule == NULL ) {
+ /* one colon */
+ if ( strcasecmp(dn, "dn") == 0 ) {
+ /* must have attribute */
+ if( !ldap_is_desc( str ) ) {
+ goto done;
+ }
+
+ rule = "";
+
+ } else {
+ rule = dn;
+ dn = NULL;
+ }
+
+ } else {
+ /* two colons */
+ *rule++ = '\0';
+
+ if ( strcasecmp(dn, "dn") != 0 ) {
+ /* must have "dn" */
+ goto done;
+ }
+ }
+
+ }
+
+ if ( *str == '\0' && ( !rule || *rule == '\0' ) ) {
+ /* must have either type or rule */
+ goto done;
+ }
+
+ if ( *str != '\0' && !ldap_is_desc( str ) ) {
+ goto done;
+ }
+
+ if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) {
+ goto done;
+ }
+
+ rc = ber_printf( ber, "t{" /*"}"*/, ftype );
+
+ if( rc != -1 && rule && *rule != '\0' ) {
+ rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
+ }
+
+ if( rc != -1 && *str != '\0' ) {
+ rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
+ }
+
+ if( rc != -1 ) {
+ ber_slen_t len = ldap_pvt_filter_value_unescape( value );
+
+ if( len >= 0 ) {
+ rc = ber_printf( ber, "to",
+ LDAP_FILTER_EXT_VALUE, value, len );
+ } else {
+ rc = -1;
+ }
+ }
+
+ if( rc != -1 && dn ) {
+ rc = ber_printf( ber, "tb",
+ LDAP_FILTER_EXT_DNATTRS, (ber_int_t) 1 );
+ }
+
+ if( rc != -1 ) {
+ rc = ber_printf( ber, /*"{"*/ "N}" );
+ }
+ }
+ goto done;
+
+ default:
+ if( !ldap_is_desc( str ) ) {
+ goto done;
+
+ } else {
+ char *nextstar = ldap_pvt_find_wildcard( value );
+
+ if ( nextstar == NULL ) {
+ goto done;
+
+ } else if ( *nextstar == '\0' ) {
+ ftype = LDAP_FILTER_EQUALITY;
+
+ } else if ( strcmp( value, "*" ) == 0 ) {
+ ftype = LDAP_FILTER_PRESENT;
+
+ } else {
+ rc = put_substring_filter( ber, str, value, nextstar );
+ goto done;
+ }
+ } break;
+ }
+
+ if( !ldap_is_desc( str ) ) goto done;
+
+ if ( ftype == LDAP_FILTER_PRESENT ) {
+ rc = ber_printf( ber, "ts", ftype, str );
+
+ } else {
+ ber_slen_t len = ldap_pvt_filter_value_unescape( value );
+
+ if( len >= 0 ) {
+ rc = ber_printf( ber, "t{soN}",
+ ftype, str, value, len );
+ }
+ }
+
+done:
+ if( rc != -1 ) rc = 0;
+ LDAP_FREE( str );
+ return rc;
+}
+
+static int
+put_substring_filter( BerElement *ber, char *type, char *val, char *nextstar )
+{
+ int gotstar = 0;
+ ber_tag_t ftype = LDAP_FILTER_SUBSTRINGS;
+
+ Debug2( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n",
+ type, val );
+
+ if ( ber_printf( ber, "t{s{" /*"}}"*/, ftype, type ) == -1 ) {
+ return -1;
+ }
+
+ for( ; *val; val=nextstar ) {
+ if ( gotstar )
+ nextstar = ldap_pvt_find_wildcard( val );
+
+ if ( nextstar == NULL ) {
+ return -1;
+ }
+
+ if ( *nextstar == '\0' ) {
+ ftype = LDAP_SUBSTRING_FINAL;
+ } else {
+ *nextstar++ = '\0';
+ if ( gotstar++ == 0 ) {
+ ftype = LDAP_SUBSTRING_INITIAL;
+ } else {
+ ftype = LDAP_SUBSTRING_ANY;
+ }
+ }
+
+ if ( *val != '\0' || ftype == LDAP_SUBSTRING_ANY ) {
+ ber_slen_t len = ldap_pvt_filter_value_unescape( val );
+
+ if ( len <= 0 ) {
+ return -1;
+ }
+
+ if ( ber_printf( ber, "to", ftype, val, len ) == -1 ) {
+ return -1;
+ }
+ }
+ }
+
+ if ( ber_printf( ber, /*"{{"*/ "N}N}" ) == -1 ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+put_vrFilter( BerElement *ber, const char *str_in )
+{
+ int rc;
+ char *freeme;
+ char *str;
+ char *next;
+ int parens, balance, escape;
+
+ /*
+ * A ValuesReturnFilter looks like this:
+ *
+ * ValuesReturnFilter ::= SEQUENCE OF SimpleFilterItem
+ * SimpleFilterItem ::= CHOICE {
+ * equalityMatch [3] AttributeValueAssertion,
+ * substrings [4] SubstringFilter,
+ * greaterOrEqual [5] AttributeValueAssertion,
+ * lessOrEqual [6] AttributeValueAssertion,
+ * present [7] AttributeType,
+ * approxMatch [8] AttributeValueAssertion,
+ * extensibleMatch [9] SimpleMatchingAssertion -- LDAPv3
+ * }
+ *
+ * SubstringFilter ::= SEQUENCE {
+ * type AttributeType,
+ * SEQUENCE OF CHOICE {
+ * initial [0] IA5String,
+ * any [1] IA5String,
+ * final [2] IA5String
+ * }
+ * }
+ *
+ * SimpleMatchingAssertion ::= SEQUENCE { -- LDAPv3
+ * matchingRule [1] MatchingRuleId OPTIONAL,
+ * type [2] AttributeDescription OPTIONAL,
+ * matchValue [3] AssertionValue }
+ *
+ * (Source: RFC 3876)
+ */
+
+ Debug1( LDAP_DEBUG_TRACE, "put_vrFilter: \"%s\"\n", str_in );
+
+ freeme = LDAP_STRDUP( str_in );
+ if( freeme == NULL ) return LDAP_NO_MEMORY;
+ str = freeme;
+
+ parens = 0;
+ while ( *str ) {
+ switch ( *str ) {
+ case '(': /*')'*/
+ str++;
+ parens++;
+
+ /* skip spaces */
+ while( LDAP_SPACE( *str ) ) str++;
+
+ switch ( *str ) {
+ case '(':
+ if ( (next = find_right_paren( str )) == NULL ) {
+ rc = -1;
+ goto done;
+ }
+
+ *next = '\0';
+
+ if ( put_vrFilter_list( ber, str ) == -1 ) {
+ rc = -1;
+ goto done;
+ }
+
+ /* close the '(' */
+ *next++ = ')';
+
+ str = next;
+
+ parens--;
+ break;
+
+
+ default:
+ Debug0( LDAP_DEBUG_TRACE, "put_vrFilter: simple\n" );
+
+ balance = 1;
+ escape = 0;
+ next = str;
+
+ while ( *next && balance ) {
+ if ( escape == 0 ) {
+ if ( *next == '(' ) {
+ balance++;
+ } else if ( *next == ')' ) {
+ balance--;
+ }
+ }
+
+ if ( *next == '\\' && ! escape ) {
+ escape = 1;
+ } else {
+ escape = 0;
+ }
+
+ if ( balance ) next++;
+ }
+
+ if ( balance != 0 ) {
+ rc = -1;
+ goto done;
+ }
+
+ *next = '\0';
+
+ if ( put_simple_vrFilter( ber, str ) == -1 ) {
+ rc = -1;
+ goto done;
+ }
+
+ *next++ = /*'('*/ ')';
+
+ str = next;
+ parens--;
+ break;
+ }
+ break;
+
+ case /*'('*/ ')':
+ Debug0( LDAP_DEBUG_TRACE, "put_vrFilter: end\n" );
+ if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) {
+ rc = -1;
+ goto done;
+ }
+ str++;
+ parens--;
+ break;
+
+ case ' ':
+ str++;
+ break;
+
+ default: /* assume it's a simple type=value filter */
+ Debug0( LDAP_DEBUG_TRACE, "put_vrFilter: default\n" );
+ next = strchr( str, '\0' );
+ if ( put_simple_vrFilter( ber, str ) == -1 ) {
+ rc = -1;
+ goto done;
+ }
+ str = next;
+ break;
+ }
+ }
+
+ rc = parens ? -1 : 0;
+
+done:
+ LDAP_FREE( freeme );
+ return rc;
+}
+
+int
+ldap_put_vrFilter( BerElement *ber, const char *str_in )
+{
+ int rc =0;
+
+ if ( ber_printf( ber, "{" /*"}"*/ ) == -1 ) {
+ return -1;
+ }
+
+ rc = put_vrFilter( ber, str_in );
+
+ if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) {
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static int
+put_vrFilter_list( BerElement *ber, char *str )
+{
+ char *next = NULL;
+ char save;
+
+ Debug1( LDAP_DEBUG_TRACE, "put_vrFilter_list \"%s\"\n",
+ str );
+
+ while ( *str ) {
+ while ( *str && LDAP_SPACE( (unsigned char) *str ) ) {
+ str++;
+ }
+ if ( *str == '\0' ) break;
+
+ if ( (next = find_right_paren( str + 1 )) == NULL ) {
+ return -1;
+ }
+ save = *++next;
+
+ /* now we have "(filter)" with str pointing to it */
+ *next = '\0';
+ if ( put_vrFilter( ber, str ) == -1 ) return -1;
+ *next = save;
+ str = next;
+ }
+
+ return 0;
+}
+
+static int
+put_simple_vrFilter(
+ BerElement *ber,
+ char *str )
+{
+ char *s;
+ char *value;
+ ber_tag_t ftype;
+ int rc = -1;
+
+ Debug1( LDAP_DEBUG_TRACE, "put_simple_vrFilter: \"%s\"\n",
+ str );
+
+ str = LDAP_STRDUP( str );
+ if( str == NULL ) return -1;
+
+ if ( (s = strchr( str, '=' )) == NULL ) {
+ goto done;
+ }
+
+ value = s + 1;
+ *s-- = '\0';
+
+ switch ( *s ) {
+ case '<':
+ ftype = LDAP_FILTER_LE;
+ *s = '\0';
+ break;
+
+ case '>':
+ ftype = LDAP_FILTER_GE;
+ *s = '\0';
+ break;
+
+ case '~':
+ ftype = LDAP_FILTER_APPROX;
+ *s = '\0';
+ break;
+
+ case ':':
+ /* According to ValuesReturnFilter control definition
+ * extensible filters are off the form:
+ * type [:rule] := value
+ * or :rule := value
+ */
+ ftype = LDAP_FILTER_EXT;
+ *s = '\0';
+
+ {
+ char *rule = strchr( str, ':' );
+
+ if( rule == NULL ) {
+ /* must have attribute */
+ if( !ldap_is_desc( str ) ) {
+ goto done;
+ }
+ rule = "";
+ } else {
+ *rule++ = '\0';
+ }
+
+ if ( *str == '\0' && ( !rule || *rule == '\0' ) ) {
+ /* must have either type or rule */
+ goto done;
+ }
+
+ if ( *str != '\0' && !ldap_is_desc( str ) ) {
+ goto done;
+ }
+
+ if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) {
+ goto done;
+ }
+
+ rc = ber_printf( ber, "t{" /*"}"*/, ftype );
+
+ if( rc != -1 && rule && *rule != '\0' ) {
+ rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
+ }
+
+ if( rc != -1 && *str != '\0' ) {
+ rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
+ }
+
+ if( rc != -1 ) {
+ ber_slen_t len = ldap_pvt_filter_value_unescape( value );
+
+ if( len >= 0 ) {
+ rc = ber_printf( ber, "to",
+ LDAP_FILTER_EXT_VALUE, value, len );
+ } else {
+ rc = -1;
+ }
+ }
+
+ if( rc != -1 ) {
+ rc = ber_printf( ber, /*"{"*/ "N}" );
+ }
+ }
+ goto done;
+
+ default:
+ if( !ldap_is_desc( str ) ) {
+ goto done;
+
+ } else {
+ char *nextstar = ldap_pvt_find_wildcard( value );
+
+ if ( nextstar == NULL ) {
+ goto done;
+
+ } else if ( *nextstar == '\0' ) {
+ ftype = LDAP_FILTER_EQUALITY;
+
+ } else if ( strcmp( value, "*" ) == 0 ) {
+ ftype = LDAP_FILTER_PRESENT;
+
+ } else {
+ rc = put_substring_filter( ber, str, value, nextstar );
+ goto done;
+ }
+ } break;
+ }
+
+ if( !ldap_is_desc( str ) ) goto done;
+
+ if ( ftype == LDAP_FILTER_PRESENT ) {
+ rc = ber_printf( ber, "ts", ftype, str );
+
+ } else {
+ ber_slen_t len = ldap_pvt_filter_value_unescape( value );
+
+ if( len >= 0 ) {
+ rc = ber_printf( ber, "t{soN}",
+ ftype, str, value, len );
+ }
+ }
+
+done:
+ if( rc != -1 ) rc = 0;
+ LDAP_FREE( str );
+ return rc;
+}
+
diff --git a/libraries/libldap/free.c b/libraries/libldap/free.c
new file mode 100644
index 0000000..4d09eee
--- /dev/null
+++ b/libraries/libldap/free.c
@@ -0,0 +1,107 @@
+/* free.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1994 The Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+/*
+ * free.c - some free routines are included here to avoid having to
+ * link in lots of extra code when not using certain features
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/*
+ * C-API deallocator
+ */
+void
+ldap_memfree( void *p )
+{
+ LDAP_FREE( p );
+}
+
+void
+ldap_memvfree( void **v )
+{
+ LDAP_VFREE( v );
+}
+
+void *
+ldap_memalloc( ber_len_t s )
+{
+ return LDAP_MALLOC( s );
+}
+
+void *
+ldap_memcalloc( ber_len_t n, ber_len_t s )
+{
+ return LDAP_CALLOC( n, s );
+}
+
+void *
+ldap_memrealloc( void* p, ber_len_t s )
+{
+ return LDAP_REALLOC( p, s );
+}
+
+char *
+ldap_strdup( LDAP_CONST char *p )
+{
+ return LDAP_STRDUP( p );
+}
+
+/*
+ * free a null-terminated array of pointers to mod structures. the
+ * structures are freed, not the array itself, unless the freemods
+ * flag is set.
+ */
+
+void
+ldap_mods_free( LDAPMod **mods, int freemods )
+{
+ int i;
+
+ if ( mods == NULL )
+ return;
+
+ for ( i = 0; mods[i] != NULL; i++ ) {
+ if ( mods[i]->mod_op & LDAP_MOD_BVALUES ) {
+ if( mods[i]->mod_bvalues != NULL )
+ ber_bvecfree( mods[i]->mod_bvalues );
+
+ } else if( mods[i]->mod_values != NULL ) {
+ LDAP_VFREE( mods[i]->mod_values );
+ }
+
+ if ( mods[i]->mod_type != NULL ) {
+ LDAP_FREE( mods[i]->mod_type );
+ }
+
+ LDAP_FREE( (char *) mods[i] );
+ }
+
+ if ( freemods ) {
+ LDAP_FREE( (char *) mods );
+ }
+}
diff --git a/libraries/libldap/ftest.c b/libraries/libldap/ftest.c
new file mode 100644
index 0000000..445e89f
--- /dev/null
+++ b/libraries/libldap/ftest.c
@@ -0,0 +1,119 @@
+/* ftest.c -- OpenLDAP Filter API Test */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include <stdio.h>
+
+#include <ldap.h>
+
+#include "ldap_pvt.h"
+#include "lber_pvt.h"
+
+#include "ldif.h"
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "ldap_defaults.h"
+
+static int filter2ber( char *filter );
+
+int usage()
+{
+ fprintf( stderr, "usage:\n"
+ " ftest [-d n] filter\n"
+ " filter - RFC 4515 string representation of an "
+ "LDAP search filter\n" );
+ return EXIT_FAILURE;
+}
+
+int
+main( int argc, char *argv[] )
+{
+ int c;
+ int debug=0;
+
+ while( (c = getopt( argc, argv, "d:" )) != EOF ) {
+ switch ( c ) {
+ case 'd':
+ debug = atoi( optarg );
+ break;
+ default:
+ fprintf( stderr, "ftest: unrecognized option -%c\n",
+ optopt );
+ return usage();
+ }
+ }
+
+ if ( debug ) {
+ if ( ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &debug )
+ != LBER_OPT_SUCCESS )
+ {
+ fprintf( stderr, "Could not set LBER_OPT_DEBUG_LEVEL %d\n",
+ debug );
+ }
+ if ( ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &debug )
+ != LDAP_OPT_SUCCESS )
+ {
+ fprintf( stderr, "Could not set LDAP_OPT_DEBUG_LEVEL %d\n",
+ debug );
+ }
+ }
+
+ if ( argc - optind != 1 ) {
+ return usage();
+ }
+
+ return filter2ber( strdup( argv[optind] ) );
+}
+
+static int filter2ber( char *filter )
+{
+ int rc;
+ struct berval bv = BER_BVNULL;
+ BerElement *ber;
+
+ printf( "Filter: %s\n", filter );
+
+ ber = ber_alloc_t( LBER_USE_DER );
+ if( ber == NULL ) {
+ perror( "ber_alloc_t" );
+ return EXIT_FAILURE;
+ }
+
+ rc = ldap_pvt_put_filter( ber, filter );
+ if( rc < 0 ) {
+ fprintf( stderr, "Filter error!\n");
+ return EXIT_FAILURE;
+ }
+
+ rc = ber_flatten2( ber, &bv, 0 );
+ if( rc < 0 ) {
+ perror( "ber_flatten2" );
+ return EXIT_FAILURE;
+ }
+
+ printf( "BER encoding (len=%ld):\n", (long) bv.bv_len );
+ ber_bprint( bv.bv_val, bv.bv_len );
+
+ ber_free( ber, 1 );
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/libraries/libldap/getattr.c b/libraries/libldap/getattr.c
new file mode 100644
index 0000000..07398df
--- /dev/null
+++ b/libraries/libldap/getattr.c
@@ -0,0 +1,157 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+char *
+ldap_first_attribute( LDAP *ld, LDAPMessage *entry, BerElement **berout )
+{
+ int rc;
+ ber_tag_t tag;
+ ber_len_t len = 0;
+ char *attr;
+ BerElement *ber;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_first_attribute\n" );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( entry != NULL );
+ assert( berout != NULL );
+
+ *berout = NULL;
+
+ ber = ldap_alloc_ber_with_options( ld );
+ if( ber == NULL ) {
+ return NULL;
+ }
+
+ *ber = *entry->lm_ber;
+
+ /*
+ * Skip past the sequence, dn, sequence of sequence leaving
+ * us at the first attribute.
+ */
+
+ tag = ber_scanf( ber, "{xl{" /*}}*/, &len );
+ if( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 0 );
+ return NULL;
+ }
+
+ /* set the length to avoid overrun */
+ rc = ber_set_option( ber, LBER_OPT_REMAINING_BYTES, &len );
+ if( rc != LBER_OPT_SUCCESS ) {
+ ld->ld_errno = LDAP_LOCAL_ERROR;
+ ber_free( ber, 0 );
+ return NULL;
+ }
+
+ if ( ber_pvt_ber_remaining( ber ) == 0 ) {
+ assert( len == 0 );
+ ber_free( ber, 0 );
+ return NULL;
+ }
+ assert( len != 0 );
+
+ /* snatch the first attribute */
+ tag = ber_scanf( ber, "{ax}", &attr );
+ if( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 0 );
+ return NULL;
+ }
+
+ *berout = ber;
+ return attr;
+}
+
+/* ARGSUSED */
+char *
+ldap_next_attribute( LDAP *ld, LDAPMessage *entry, BerElement *ber )
+{
+ ber_tag_t tag;
+ char *attr;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_next_attribute\n" );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( entry != NULL );
+ assert( ber != NULL );
+
+ if ( ber_pvt_ber_remaining( ber ) == 0 ) {
+ return NULL;
+ }
+
+ /* skip sequence, snarf attribute type, skip values */
+ tag = ber_scanf( ber, "{ax}", &attr );
+ if( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return NULL;
+ }
+
+ return attr;
+}
+
+/* Fetch attribute type and optionally fetch values. The type
+ * and values are referenced in-place from the BerElement, they are
+ * not dup'd into malloc'd memory.
+ */
+/* ARGSUSED */
+int
+ldap_get_attribute_ber( LDAP *ld, LDAPMessage *entry, BerElement *ber,
+ BerValue *attr, BerVarray *vals )
+{
+ ber_tag_t tag;
+ int rc = LDAP_SUCCESS;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_get_attribute_ber\n" );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( entry != NULL );
+ assert( ber != NULL );
+ assert( attr != NULL );
+
+ attr->bv_val = NULL;
+ attr->bv_len = 0;
+
+ if ( ber_pvt_ber_remaining( ber ) ) {
+ ber_len_t siz = sizeof( BerValue );
+
+ /* skip sequence, snarf attribute type */
+ tag = ber_scanf( ber, vals ? "{mM}" : "{mx}", attr, vals,
+ &siz, (ber_len_t)0 );
+ if( tag == LBER_ERROR ) {
+ rc = ld->ld_errno = LDAP_DECODING_ERROR;
+ }
+ }
+
+ return rc;
+}
diff --git a/libraries/libldap/getdn.c b/libraries/libldap/getdn.c
new file mode 100644
index 0000000..6170596
--- /dev/null
+++ b/libraries/libldap/getdn.c
@@ -0,0 +1,3334 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1994 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_schema.h"
+#include "ldif.h"
+
+/* extension to UFN that turns trailing "dc=value" rdns in DNS style,
+ * e.g. "ou=People,dc=openldap,dc=org" => "People, openldap.org" */
+#define DC_IN_UFN
+
+/* parsing/printing routines */
+static int str2strval( const char *str, ber_len_t stoplen, struct berval *val,
+ const char **next, unsigned flags, int *retFlags, void *ctx );
+static int DCE2strval( const char *str, struct berval *val,
+ const char **next, unsigned flags, void *ctx );
+static int IA52strval( const char *str, struct berval *val,
+ const char **next, unsigned flags, void *ctx );
+static int quotedIA52strval( const char *str, struct berval *val,
+ const char **next, unsigned flags, void *ctx );
+static int hexstr2binval( const char *str, struct berval *val,
+ const char **next, unsigned flags, void *ctx );
+static int hexstr2bin( const char *str, char *c );
+static int byte2hexpair( const char *val, char *pair );
+static int binval2hexstr( struct berval *val, char *str );
+static int strval2strlen( struct berval *val, unsigned flags,
+ ber_len_t *len );
+static int strval2str( struct berval *val, char *str, unsigned flags,
+ ber_len_t *len );
+static int strval2IA5strlen( struct berval *val, unsigned flags,
+ ber_len_t *len );
+static int strval2IA5str( struct berval *val, char *str, unsigned flags,
+ ber_len_t *len );
+static int strval2DCEstrlen( struct berval *val, unsigned flags,
+ ber_len_t *len );
+static int strval2DCEstr( struct berval *val, char *str, unsigned flags,
+ ber_len_t *len );
+static int strval2ADstrlen( struct berval *val, unsigned flags,
+ ber_len_t *len );
+static int strval2ADstr( struct berval *val, char *str, unsigned flags,
+ ber_len_t *len );
+static int dn2domain( LDAPDN dn, struct berval *bv, int pos, int *iRDN );
+
+/* AVA helpers */
+static LDAPAVA * ldapava_new(
+ const struct berval *attr, const struct berval *val, unsigned flags, void *ctx );
+
+/* Higher level helpers */
+static int rdn2strlen( LDAPRDN rdn, unsigned flags, ber_len_t *len,
+ int ( *s2l )( struct berval *, unsigned, ber_len_t * ) );
+static int rdn2str( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len,
+ int ( *s2s )( struct berval *, char *, unsigned, ber_len_t * ));
+static int rdn2UFNstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len );
+static int rdn2UFNstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len );
+static int rdn2DCEstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len );
+static int rdn2DCEstr( LDAPRDN rdn, char *str, unsigned flag, ber_len_t *len, int first );
+static int rdn2ADstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len );
+static int rdn2ADstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, int first );
+
+/*
+ * RFC 1823 ldap_get_dn
+ */
+char *
+ldap_get_dn( LDAP *ld, LDAPMessage *entry )
+{
+ char *dn;
+ BerElement tmp;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_get_dn\n" );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID(ld) );
+ assert( entry != NULL );
+
+ tmp = *entry->lm_ber; /* struct copy */
+ if ( ber_scanf( &tmp, "{a" /*}*/, &dn ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return( NULL );
+ }
+
+ return( dn );
+}
+
+int
+ldap_get_dn_ber( LDAP *ld, LDAPMessage *entry, BerElement **berout,
+ BerValue *dn )
+{
+ BerElement tmp, *ber;
+ ber_len_t len = 0;
+ int rc = LDAP_SUCCESS;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_get_dn_ber\n" );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID(ld) );
+ assert( entry != NULL );
+ assert( dn != NULL );
+
+ dn->bv_val = NULL;
+ dn->bv_len = 0;
+
+ if ( berout ) {
+ *berout = NULL;
+ ber = ldap_alloc_ber_with_options( ld );
+ if( ber == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+ *berout = ber;
+ } else {
+ ber = &tmp;
+ }
+
+ *ber = *entry->lm_ber; /* struct copy */
+ if ( ber_scanf( ber, "{ml{" /*}*/, dn, &len ) == LBER_ERROR ) {
+ rc = ld->ld_errno = LDAP_DECODING_ERROR;
+ }
+ if ( rc == LDAP_SUCCESS ) {
+ /* set the length to avoid overrun */
+ rc = ber_set_option( ber, LBER_OPT_REMAINING_BYTES, &len );
+ if( rc != LBER_OPT_SUCCESS ) {
+ rc = ld->ld_errno = LDAP_LOCAL_ERROR;
+ }
+ }
+ if ( rc != LDAP_SUCCESS && berout ) {
+ ber_free( ber, 0 );
+ *berout = NULL;
+ }
+ return rc;
+}
+
+/*
+ * RFC 1823 ldap_dn2ufn
+ */
+char *
+ldap_dn2ufn( LDAP_CONST char *dn )
+{
+ char *out = NULL;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_dn2ufn\n" );
+
+ ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP,
+ &out, LDAP_DN_FORMAT_UFN );
+
+ return( out );
+}
+
+/*
+ * RFC 1823 ldap_explode_dn
+ */
+char **
+ldap_explode_dn( LDAP_CONST char *dn, int notypes )
+{
+ LDAPDN tmpDN;
+ char **values = NULL;
+ int iRDN;
+ unsigned flag = notypes ? LDAP_DN_FORMAT_UFN : LDAP_DN_FORMAT_LDAPV3;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_explode_dn\n" );
+
+ if ( ldap_str2dn( dn, &tmpDN, LDAP_DN_FORMAT_LDAP )
+ != LDAP_SUCCESS ) {
+ return NULL;
+ }
+
+ if( tmpDN == NULL ) {
+ values = LDAP_MALLOC( sizeof( char * ) );
+ if( values == NULL ) return NULL;
+
+ values[0] = NULL;
+ return values;
+ }
+
+ for ( iRDN = 0; tmpDN[ iRDN ]; iRDN++ );
+
+ values = LDAP_MALLOC( sizeof( char * ) * ( 1 + iRDN ) );
+ if ( values == NULL ) {
+ ldap_dnfree( tmpDN );
+ return NULL;
+ }
+
+ for ( iRDN = 0; tmpDN[ iRDN ]; iRDN++ ) {
+ ldap_rdn2str( tmpDN[ iRDN ], &values[ iRDN ], flag );
+ }
+ ldap_dnfree( tmpDN );
+ values[ iRDN ] = NULL;
+
+ return values;
+}
+
+char **
+ldap_explode_rdn( LDAP_CONST char *rdn, int notypes )
+{
+ LDAPRDN tmpRDN;
+ char **values = NULL;
+ const char *p;
+ int iAVA;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_explode_rdn\n" );
+
+ /*
+ * we only parse the first rdn
+ * FIXME: we prefer efficiency over checking if the _ENTIRE_
+ * dn can be parsed
+ */
+ if ( ldap_str2rdn( rdn, &tmpRDN, (char **) &p, LDAP_DN_FORMAT_LDAP )
+ != LDAP_SUCCESS ) {
+ return( NULL );
+ }
+
+ for ( iAVA = 0; tmpRDN[ iAVA ]; iAVA++ ) ;
+ values = LDAP_MALLOC( sizeof( char * ) * ( 1 + iAVA ) );
+ if ( values == NULL ) {
+ ldap_rdnfree( tmpRDN );
+ return( NULL );
+ }
+
+ for ( iAVA = 0; tmpRDN[ iAVA ]; iAVA++ ) {
+ ber_len_t l = 0, vl, al = 0;
+ char *str;
+ LDAPAVA *ava = tmpRDN[ iAVA ];
+
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ vl = 1 + 2 * ava->la_value.bv_len;
+
+ } else {
+ if ( strval2strlen( &ava->la_value,
+ ava->la_flags, &vl ) ) {
+ goto error_return;
+ }
+ }
+
+ if ( !notypes ) {
+ al = ava->la_attr.bv_len;
+ l = vl + ava->la_attr.bv_len + 1;
+
+ str = LDAP_MALLOC( l + 1 );
+ if ( str == NULL ) {
+ goto error_return;
+ }
+ AC_MEMCPY( str, ava->la_attr.bv_val,
+ ava->la_attr.bv_len );
+ str[ al++ ] = '=';
+
+ } else {
+ l = vl;
+ str = LDAP_MALLOC( l + 1 );
+ if ( str == NULL ) {
+ goto error_return;
+ }
+ }
+
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ str[ al++ ] = '#';
+ if ( binval2hexstr( &ava->la_value, &str[ al ] ) ) {
+ goto error_return;
+ }
+
+ } else {
+ if ( strval2str( &ava->la_value, &str[ al ],
+ ava->la_flags, &vl ) ) {
+ goto error_return;
+ }
+ }
+
+ str[ l ] = '\0';
+ values[ iAVA ] = str;
+ }
+ values[ iAVA ] = NULL;
+
+ ldap_rdnfree( tmpRDN );
+
+ return( values );
+
+error_return:;
+ LBER_VFREE( values );
+ ldap_rdnfree( tmpRDN );
+ return( NULL );
+}
+
+char *
+ldap_dn2dcedn( LDAP_CONST char *dn )
+{
+ char *out = NULL;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_dn2dcedn\n" );
+
+ ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP,
+ &out, LDAP_DN_FORMAT_DCE );
+
+ return( out );
+}
+
+char *
+ldap_dcedn2dn( LDAP_CONST char *dce )
+{
+ char *out = NULL;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_dcedn2dn\n" );
+
+ ( void )ldap_dn_normalize( dce, LDAP_DN_FORMAT_DCE, &out, LDAP_DN_FORMAT_LDAPV3 );
+
+ return( out );
+}
+
+char *
+ldap_dn2ad_canonical( LDAP_CONST char *dn )
+{
+ char *out = NULL;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_dn2ad_canonical\n" );
+
+ ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP,
+ &out, LDAP_DN_FORMAT_AD_CANONICAL );
+
+ return( out );
+}
+
+/*
+ * function that changes the string representation of dnin
+ * from ( fin & LDAP_DN_FORMAT_MASK ) to ( fout & LDAP_DN_FORMAT_MASK )
+ *
+ * fin can be one of:
+ * LDAP_DN_FORMAT_LDAP (RFC 4514 liberal, plus some RFC 1779)
+ * LDAP_DN_FORMAT_LDAPV3 (RFC 4514)
+ * LDAP_DN_FORMAT_LDAPV2 (RFC 1779)
+ * LDAP_DN_FORMAT_DCE (?)
+ *
+ * fout can be any of the above except
+ * LDAP_DN_FORMAT_LDAP
+ * plus:
+ * LDAP_DN_FORMAT_UFN (RFC 1781, partial and with extensions)
+ * LDAP_DN_FORMAT_AD_CANONICAL (?)
+ */
+int
+ldap_dn_normalize( LDAP_CONST char *dnin,
+ unsigned fin, char **dnout, unsigned fout )
+{
+ int rc;
+ LDAPDN tmpDN = NULL;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_dn_normalize\n" );
+
+ assert( dnout != NULL );
+
+ *dnout = NULL;
+
+ if ( dnin == NULL ) {
+ return( LDAP_SUCCESS );
+ }
+
+ rc = ldap_str2dn( dnin , &tmpDN, fin );
+ if ( rc != LDAP_SUCCESS ) {
+ return( rc );
+ }
+
+ rc = ldap_dn2str( tmpDN, dnout, fout );
+
+ ldap_dnfree( tmpDN );
+
+ return( rc );
+}
+
+/* States */
+#define B4AVA 0x0000
+
+/* #define B4ATTRTYPE 0x0001 */
+#define B4OIDATTRTYPE 0x0002
+#define B4STRINGATTRTYPE 0x0003
+
+#define B4AVAEQUALS 0x0100
+#define B4AVASEP 0x0200
+#define B4RDNSEP 0x0300
+#define GOTAVA 0x0400
+
+#define B4ATTRVALUE 0x0010
+#define B4STRINGVALUE 0x0020
+#define B4IA5VALUEQUOTED 0x0030
+#define B4IA5VALUE 0x0040
+#define B4BINARYVALUE 0x0050
+
+/*
+ * Helpers (mostly from slap.h)
+ * c is assumed to Unicode in an ASCII compatible format (UTF-8)
+ * Macros assume "C" Locale (ASCII)
+ */
+#define LDAP_DN_ASCII_SPACE(c) \
+ ( (c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r' )
+#define LDAP_DN_ASCII_LOWER(c) LDAP_LOWER(c)
+#define LDAP_DN_ASCII_UPPER(c) LDAP_UPPER(c)
+#define LDAP_DN_ASCII_ALPHA(c) LDAP_ALPHA(c)
+
+#define LDAP_DN_ASCII_DIGIT(c) LDAP_DIGIT(c)
+#define LDAP_DN_ASCII_LCASE_HEXALPHA(c) LDAP_HEXLOWER(c)
+#define LDAP_DN_ASCII_UCASE_HEXALPHA(c) LDAP_HEXUPPER(c)
+#define LDAP_DN_ASCII_HEXDIGIT(c) LDAP_HEX(c)
+#define LDAP_DN_ASCII_ALNUM(c) LDAP_ALNUM(c)
+#define LDAP_DN_ASCII_PRINTABLE(c) ( (c) >= ' ' && (c) <= '~' )
+
+/* attribute type */
+#define LDAP_DN_OID_LEADCHAR(c) LDAP_DIGIT(c)
+#define LDAP_DN_DESC_LEADCHAR(c) LDAP_ALPHA(c)
+#define LDAP_DN_DESC_CHAR(c) LDAP_LDH(c)
+#define LDAP_DN_LANG_SEP(c) ( (c) == ';' )
+#define LDAP_DN_ATTRDESC_CHAR(c) \
+ ( LDAP_DN_DESC_CHAR(c) || LDAP_DN_LANG_SEP(c) )
+
+/* special symbols */
+#define LDAP_DN_AVA_EQUALS(c) ( (c) == '=' )
+#define LDAP_DN_AVA_SEP(c) ( (c) == '+' )
+#define LDAP_DN_RDN_SEP(c) ( (c) == ',' )
+#define LDAP_DN_RDN_SEP_V2(c) ( LDAP_DN_RDN_SEP(c) || (c) == ';' )
+#define LDAP_DN_OCTOTHORPE(c) ( (c) == '#' )
+#define LDAP_DN_QUOTES(c) ( (c) == '\"' )
+#define LDAP_DN_ESCAPE(c) ( (c) == '\\' )
+#define LDAP_DN_VALUE_END(c) \
+ ( LDAP_DN_RDN_SEP(c) || LDAP_DN_AVA_SEP(c) )
+
+/* NOTE: according to RFC 4514, '=' can be escaped and treated as special,
+ * i.e. escaped both as "\<hexpair>" and * as "\=", but it is treated as
+ * a regular char, i.e. it can also appear as '='.
+ *
+ * As such, in 2.2 we used to allow reading unescaped '=', but we always
+ * produced escaped '\3D'; this changes since 2.3, if compatibility issues
+ * do not arise
+ */
+#define LDAP_DN_NE(c) \
+ ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_SEP(c) \
+ || LDAP_DN_QUOTES(c) \
+ || (c) == '<' || (c) == '>' )
+#define LDAP_DN_MAYESCAPE(c) \
+ ( LDAP_DN_ESCAPE(c) || LDAP_DN_NE(c) \
+ || LDAP_DN_AVA_EQUALS(c) \
+ || LDAP_DN_ASCII_SPACE(c) || LDAP_DN_OCTOTHORPE(c) )
+#define LDAP_DN_SHOULDESCAPE(c) ( LDAP_DN_AVA_EQUALS(c) )
+
+#define LDAP_DN_NEEDESCAPE(c) \
+ ( LDAP_DN_ESCAPE(c) || LDAP_DN_NE(c) )
+#define LDAP_DN_NEEDESCAPE_LEAD(c) LDAP_DN_MAYESCAPE(c)
+#define LDAP_DN_NEEDESCAPE_TRAIL(c) \
+ ( LDAP_DN_ASCII_SPACE(c) || LDAP_DN_NEEDESCAPE(c) )
+#define LDAP_DN_WILLESCAPE_CHAR(c) \
+ ( LDAP_DN_RDN_SEP(c) || LDAP_DN_AVA_SEP(c) || LDAP_DN_ESCAPE(c) )
+#define LDAP_DN_IS_PRETTY(f) ( (f) & LDAP_DN_PRETTY )
+#define LDAP_DN_WILLESCAPE_HEX(f, c) \
+ ( ( !LDAP_DN_IS_PRETTY( f ) ) && LDAP_DN_WILLESCAPE_CHAR(c) )
+
+/* LDAPv2 */
+#define LDAP_DN_VALUE_END_V2(c) \
+ ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_SEP(c) )
+/* RFC 1779 */
+#define LDAP_DN_V2_SPECIAL(c) \
+ ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_EQUALS(c) \
+ || LDAP_DN_AVA_SEP(c) || (c) == '<' || (c) == '>' \
+ || LDAP_DN_OCTOTHORPE(c) )
+#define LDAP_DN_V2_PAIR(c) \
+ ( LDAP_DN_V2_SPECIAL(c) || LDAP_DN_ESCAPE(c) || LDAP_DN_QUOTES(c) )
+
+/*
+ * DCE (mostly from Luke Howard and IBM implementation for AIX)
+ *
+ * From: "Application Development Guide - Directory Services" (FIXME: add link?)
+ * Here escapes and valid chars for GDS are considered; as soon as more
+ * specific info is found, the macros will be updated.
+ *
+ * Chars: 'a'-'z', 'A'-'Z', '0'-'9',
+ * '.', ':', ',', ''', '+', '-', '=', '(', ')', '?', '/', ' '.
+ *
+ * Metachars: '/', ',', '=', '\'.
+ *
+ * the '\' is used to escape other metachars.
+ *
+ * Assertion: '='
+ * RDN separator: '/'
+ * AVA separator: ','
+ *
+ * Attribute types must start with alphabetic chars and can contain
+ * alphabetic chars and digits (FIXME: no '-'?). OIDs are allowed.
+ */
+#define LDAP_DN_RDN_SEP_DCE(c) ( (c) == '/' )
+#define LDAP_DN_AVA_SEP_DCE(c) ( (c) == ',' )
+#define LDAP_DN_ESCAPE_DCE(c) ( LDAP_DN_ESCAPE(c) )
+#define LDAP_DN_VALUE_END_DCE(c) \
+ ( LDAP_DN_RDN_SEP_DCE(c) || LDAP_DN_AVA_SEP_DCE(c) )
+#define LDAP_DN_NEEDESCAPE_DCE(c) \
+ ( LDAP_DN_VALUE_END_DCE(c) || LDAP_DN_AVA_EQUALS(c) )
+
+/* AD Canonical */
+#define LDAP_DN_RDN_SEP_AD(c) ( (c) == '/' )
+#define LDAP_DN_ESCAPE_AD(c) ( LDAP_DN_ESCAPE(c) )
+#define LDAP_DN_AVA_SEP_AD(c) ( (c) == ',' ) /* assume same as DCE */
+#define LDAP_DN_VALUE_END_AD(c) \
+ ( LDAP_DN_RDN_SEP_AD(c) || LDAP_DN_AVA_SEP_AD(c) )
+#define LDAP_DN_NEEDESCAPE_AD(c) \
+ ( LDAP_DN_VALUE_END_AD(c) || LDAP_DN_AVA_EQUALS(c) )
+
+/* generics */
+#define LDAP_DN_HEXPAIR(s) \
+ ( LDAP_DN_ASCII_HEXDIGIT((s)[0]) && LDAP_DN_ASCII_HEXDIGIT((s)[1]) )
+/* better look at the AttributeDescription? */
+
+/* FIXME: no composite rdn or non-"dc" types, right?
+ * (what about "dc" in OID form?) */
+/* FIXME: we do not allow binary values in domain, right? */
+/* NOTE: use this macro only when ABSOLUTELY SURE rdn IS VALID! */
+/* NOTE: don't use strcasecmp() as it is locale specific! */
+#define LDAP_DC_ATTR "dc"
+#define LDAP_DC_ATTRU "DC"
+#define LDAP_DN_IS_RDN_DC( r ) \
+ ( (r) && (r)[0] && !(r)[1] \
+ && ((r)[0]->la_flags & LDAP_AVA_STRING) \
+ && ((r)[0]->la_attr.bv_len == 2) \
+ && (((r)[0]->la_attr.bv_val[0] == LDAP_DC_ATTR[0]) \
+ || ((r)[0]->la_attr.bv_val[0] == LDAP_DC_ATTRU[0])) \
+ && (((r)[0]->la_attr.bv_val[1] == LDAP_DC_ATTR[1]) \
+ || ((r)[0]->la_attr.bv_val[1] == LDAP_DC_ATTRU[1])))
+
+/* Composite rules */
+#define LDAP_DN_ALLOW_ONE_SPACE(f) \
+ ( LDAP_DN_LDAPV2(f) \
+ || !( (f) & LDAP_DN_P_NOSPACEAFTERRDN ) )
+#define LDAP_DN_ALLOW_SPACES(f) \
+ ( LDAP_DN_LDAPV2(f) \
+ || !( (f) & ( LDAP_DN_P_NOLEADTRAILSPACES | LDAP_DN_P_NOSPACEAFTERRDN ) ) )
+#define LDAP_DN_LDAP(f) \
+ ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAP )
+#define LDAP_DN_LDAPV3(f) \
+ ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAPV3 )
+#define LDAP_DN_LDAPV2(f) \
+ ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAPV2 )
+#define LDAP_DN_DCE(f) \
+ ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_DCE )
+#define LDAP_DN_UFN(f) \
+ ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_UFN )
+#define LDAP_DN_ADC(f) \
+ ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_AD_CANONICAL )
+#define LDAP_DN_FORMAT(f) ( (f) & LDAP_DN_FORMAT_MASK )
+
+/*
+ * LDAPAVA helpers (will become part of the API for operations
+ * on structural representations of DNs).
+ */
+static LDAPAVA *
+ldapava_new( const struct berval *attr, const struct berval *val,
+ unsigned flags, void *ctx )
+{
+ LDAPAVA *ava;
+
+ assert( attr != NULL );
+ assert( val != NULL );
+
+ ava = LDAP_MALLOCX( sizeof( LDAPAVA ) + attr->bv_len + 1, ctx );
+
+ if ( ava ) {
+ ava->la_attr.bv_len = attr->bv_len;
+ ava->la_attr.bv_val = (char *)(ava+1);
+ AC_MEMCPY( ava->la_attr.bv_val, attr->bv_val, attr->bv_len );
+ ava->la_attr.bv_val[attr->bv_len] = '\0';
+
+ ava->la_value = *val;
+ ava->la_flags = flags | LDAP_AVA_FREE_VALUE;
+
+ ava->la_private = NULL;
+ }
+
+ return( ava );
+}
+
+static void
+ldapava_free( LDAPAVA *ava, void *ctx )
+{
+ assert( ava != NULL );
+
+#if 0
+ /* ava's private must be freed by caller
+ * (at present let's skip this check because la_private
+ * basically holds static data) */
+ assert( ava->la_private == NULL );
+#endif
+
+ if (ava->la_flags & LDAP_AVA_FREE_VALUE)
+ LDAP_FREEX( ava->la_value.bv_val, ctx );
+
+ LDAP_FREEX( ava, ctx );
+}
+
+void
+ldap_rdnfree( LDAPRDN rdn )
+{
+ ldap_rdnfree_x( rdn, NULL );
+}
+
+void
+ldap_rdnfree_x( LDAPRDN rdn, void *ctx )
+{
+ int iAVA;
+
+ if ( rdn == NULL ) {
+ return;
+ }
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ ldapava_free( rdn[ iAVA ], ctx );
+ }
+
+ LDAP_FREEX( rdn, ctx );
+}
+
+void
+ldap_dnfree( LDAPDN dn )
+{
+ ldap_dnfree_x( dn, NULL );
+}
+
+void
+ldap_dnfree_x( LDAPDN dn, void *ctx )
+{
+ int iRDN;
+
+ if ( dn == NULL ) {
+ return;
+ }
+
+ for ( iRDN = 0; dn[ iRDN ]; iRDN++ ) {
+ ldap_rdnfree_x( dn[ iRDN ], ctx );
+ }
+
+ LDAP_FREEX( dn, ctx );
+}
+
+/*
+ * Converts a string representation of a DN (in LDAPv3, LDAPv2 or DCE)
+ * into a structural representation of the DN, by separating attribute
+ * types and values encoded in the more appropriate form, which is
+ * string or OID for attribute types and binary form of the BER encoded
+ * value or Unicode string. Formats different from LDAPv3 are parsed
+ * according to their own rules and turned into the more appropriate
+ * form according to LDAPv3.
+ *
+ * NOTE: I realize the code is getting spaghettish; it is rather
+ * experimental and will hopefully turn into something more simple
+ * and readable as soon as it works as expected.
+ */
+
+/*
+ * Default sizes of AVA and RDN static working arrays; if required
+ * the are dynamically resized. The values can be tuned in case
+ * of special requirements (e.g. very deep DN trees or high number
+ * of AVAs per RDN).
+ */
+#define TMP_AVA_SLOTS 8
+#define TMP_RDN_SLOTS 32
+
+int
+ldap_str2dn( LDAP_CONST char *str, LDAPDN *dn, unsigned flags )
+{
+ struct berval bv;
+
+ assert( str != NULL );
+
+ bv.bv_len = strlen( str );
+ bv.bv_val = (char *) str;
+
+ return ldap_bv2dn_x( &bv, dn, flags, NULL );
+}
+
+int
+ldap_bv2dn( struct berval *bv, LDAPDN *dn, unsigned flags )
+{
+ return ldap_bv2dn_x( bv, dn, flags, NULL );
+}
+
+int
+ldap_bv2dn_x( struct berval *bvin, LDAPDN *dn, unsigned flags, void *ctx )
+{
+ const char *p;
+ int rc = LDAP_DECODING_ERROR;
+ int nrdns = 0;
+
+ LDAPDN newDN = NULL;
+ LDAPRDN newRDN = NULL, tmpDN_[TMP_RDN_SLOTS], *tmpDN = tmpDN_;
+ int num_slots = TMP_RDN_SLOTS;
+ char *str, *end;
+ struct berval bvtmp, *bv = &bvtmp;
+
+ assert( bvin != NULL );
+ assert( bvin->bv_val != NULL );
+ assert( dn != NULL );
+
+ *bv = *bvin;
+ str = bv->bv_val;
+ end = str + bv->bv_len;
+
+ Debug2( LDAP_DEBUG_ARGS, "=> ldap_bv2dn(%s,%u)\n", str, flags );
+
+ *dn = NULL;
+
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAP:
+ case LDAP_DN_FORMAT_LDAPV3:
+ case LDAP_DN_FORMAT_DCE:
+ break;
+
+ /* allow DN enclosed in brackets */
+ case LDAP_DN_FORMAT_LDAPV2:
+ if ( str[0] == '<' ) {
+ if ( bv->bv_len < 2 || end[ -1 ] != '>' ) {
+ rc = LDAP_DECODING_ERROR;
+ goto parsing_error;
+ }
+ bv->bv_val++;
+ bv->bv_len -= 2;
+ str++;
+ end--;
+ }
+ break;
+
+ /* unsupported in str2dn */
+ case LDAP_DN_FORMAT_UFN:
+ case LDAP_DN_FORMAT_AD_CANONICAL:
+ return LDAP_PARAM_ERROR;
+
+ case LDAP_DN_FORMAT_LBER:
+ default:
+ return LDAP_PARAM_ERROR;
+ }
+
+ if ( bv->bv_len == 0 ) {
+ return LDAP_SUCCESS;
+ }
+
+ if( memchr( bv->bv_val, '\0', bv->bv_len ) != NULL ) {
+ /* value must have embedded NULs */
+ return LDAP_DECODING_ERROR;
+ }
+
+ p = str;
+ if ( LDAP_DN_DCE( flags ) ) {
+
+ /*
+ * (from Luke Howard: thnx) A RDN separator is required
+ * at the beginning of an (absolute) DN.
+ */
+ if ( !LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) {
+ goto parsing_error;
+ }
+ p++;
+
+ /*
+ * actually we do not want to accept by default the DCE form,
+ * we do not want to auto-detect it
+ */
+#if 0
+ } else if ( LDAP_DN_LDAP( flags ) ) {
+ /*
+ * if dn starts with '/' let's make it a DCE dn
+ */
+ if ( LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) {
+ flags |= LDAP_DN_FORMAT_DCE;
+ p++;
+ }
+#endif
+ }
+
+ for ( ; p < end; p++ ) {
+ int err;
+ struct berval tmpbv;
+ tmpbv.bv_len = bv->bv_len - ( p - str );
+ tmpbv.bv_val = (char *)p;
+
+ err = ldap_bv2rdn_x( &tmpbv, &newRDN, (char **) &p, flags,ctx);
+ if ( err != LDAP_SUCCESS ) {
+ goto parsing_error;
+ }
+
+ /*
+ * We expect a rdn separator
+ */
+ if ( p < end && p[ 0 ] ) {
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAPV3:
+ if ( !LDAP_DN_RDN_SEP( p[ 0 ] ) ) {
+ rc = LDAP_DECODING_ERROR;
+ goto parsing_error;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_LDAP:
+ case LDAP_DN_FORMAT_LDAPV2:
+ if ( !LDAP_DN_RDN_SEP_V2( p[ 0 ] ) ) {
+ rc = LDAP_DECODING_ERROR;
+ goto parsing_error;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_DCE:
+ if ( !LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) {
+ rc = LDAP_DECODING_ERROR;
+ goto parsing_error;
+ }
+ break;
+ }
+ }
+
+
+ tmpDN[nrdns++] = newRDN;
+ newRDN = NULL;
+
+ /*
+ * make the static RDN array dynamically rescalable
+ */
+ if ( nrdns == num_slots ) {
+ LDAPRDN *tmp;
+
+ if ( tmpDN == tmpDN_ ) {
+ tmp = LDAP_MALLOCX( num_slots * 2 * sizeof( LDAPRDN * ), ctx );
+ if ( tmp == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto parsing_error;
+ }
+ AC_MEMCPY( tmp, tmpDN, num_slots * sizeof( LDAPRDN * ) );
+
+ } else {
+ tmp = LDAP_REALLOCX( tmpDN, num_slots * 2 * sizeof( LDAPRDN * ), ctx );
+ if ( tmp == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto parsing_error;
+ }
+ }
+
+ tmpDN = tmp;
+ num_slots *= 2;
+ }
+
+ if ( p >= end || p[ 0 ] == '\0' ) {
+ /*
+ * the DN is over, phew
+ */
+ newDN = (LDAPDN)LDAP_MALLOCX( sizeof(LDAPRDN *) * (nrdns+1), ctx );
+ if ( newDN == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto parsing_error;
+ } else {
+ int i;
+
+ if ( LDAP_DN_DCE( flags ) ) {
+ /* add in reversed order */
+ for ( i=0; i<nrdns; i++ )
+ newDN[i] = tmpDN[nrdns-1-i];
+ } else {
+ for ( i=0; i<nrdns; i++ )
+ newDN[i] = tmpDN[i];
+ }
+ newDN[nrdns] = NULL;
+ rc = LDAP_SUCCESS;
+ }
+ goto return_result;
+ }
+ }
+
+parsing_error:;
+ if ( newRDN ) {
+ ldap_rdnfree_x( newRDN, ctx );
+ }
+
+ for ( nrdns-- ;nrdns >= 0; nrdns-- ) {
+ ldap_rdnfree_x( tmpDN[nrdns], ctx );
+ }
+
+return_result:;
+
+ if ( tmpDN != tmpDN_ ) {
+ LDAP_FREEX( tmpDN, ctx );
+ }
+
+ Debug3( LDAP_DEBUG_ARGS, "<= ldap_bv2dn(%s)=%d %s\n", str, rc,
+ rc ? ldap_err2string( rc ) : "" );
+ *dn = newDN;
+
+ return( rc );
+}
+
+/*
+ * ldap_str2rdn
+ *
+ * Parses a relative DN according to flags up to a rdn separator
+ * or to the end of str.
+ * Returns the rdn and a pointer to the string continuation, which
+ * corresponds to the rdn separator or to '\0' in case the string is over.
+ */
+int
+ldap_str2rdn( LDAP_CONST char *str, LDAPRDN *rdn,
+ char **n_in, unsigned flags )
+{
+ struct berval bv;
+
+ assert( str != NULL );
+ assert( str[ 0 ] != '\0' ); /* FIXME: is this required? */
+
+ bv.bv_len = strlen( str );
+ bv.bv_val = (char *) str;
+
+ return ldap_bv2rdn_x( &bv, rdn, n_in, flags, NULL );
+}
+
+int
+ldap_bv2rdn( struct berval *bv, LDAPRDN *rdn,
+ char **n_in, unsigned flags )
+{
+ return ldap_bv2rdn_x( bv, rdn, n_in, flags, NULL );
+}
+
+int
+ldap_bv2rdn_x( struct berval *bv, LDAPRDN *rdn,
+ char **n_in, unsigned flags, void *ctx )
+{
+ const char **n = (const char **) n_in;
+ const char *p;
+ int navas = 0;
+ int state = B4AVA;
+ int rc = LDAP_DECODING_ERROR;
+ int attrTypeEncoding = LDAP_AVA_STRING,
+ attrValueEncoding = LDAP_AVA_STRING;
+
+ struct berval attrType = BER_BVNULL;
+ struct berval attrValue = BER_BVNULL;
+
+ LDAPRDN newRDN = NULL;
+ LDAPAVA *tmpRDN_[TMP_AVA_SLOTS], **tmpRDN = tmpRDN_;
+ int num_slots = TMP_AVA_SLOTS;
+
+ char *str;
+ ber_len_t stoplen;
+
+ assert( bv != NULL );
+ assert( bv->bv_len != 0 );
+ assert( bv->bv_val != NULL );
+ assert( rdn || flags & LDAP_DN_SKIP );
+ assert( n != NULL );
+
+ str = bv->bv_val;
+ stoplen = bv->bv_len;
+
+ if ( rdn ) {
+ *rdn = NULL;
+ }
+ *n = NULL;
+
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAP:
+ case LDAP_DN_FORMAT_LDAPV3:
+ case LDAP_DN_FORMAT_LDAPV2:
+ case LDAP_DN_FORMAT_DCE:
+ break;
+
+ /* unsupported in str2dn */
+ case LDAP_DN_FORMAT_UFN:
+ case LDAP_DN_FORMAT_AD_CANONICAL:
+ return LDAP_PARAM_ERROR;
+
+ case LDAP_DN_FORMAT_LBER:
+ default:
+ return LDAP_PARAM_ERROR;
+ }
+
+ if ( bv->bv_len == 0 ) {
+ return LDAP_SUCCESS;
+
+ }
+
+ if( memchr( bv->bv_val, '\0', bv->bv_len ) != NULL ) {
+ /* value must have embedded NULs */
+ return LDAP_DECODING_ERROR;
+ }
+
+ p = str;
+ for ( ; p[ 0 ] || state == GOTAVA; ) {
+
+ /*
+ * The parser in principle advances one token a time,
+ * or toggles state if preferable.
+ */
+ switch (state) {
+
+ /*
+ * an AttributeType can be encoded as:
+ * - its string representation; in detail, implementations
+ * MUST recognize AttributeType string type names listed
+ * in Section 3 of RFC 4514, and MAY recognize other names.
+ * - its numeric OID (a dotted decimal string)
+ */
+ case B4AVA:
+ if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+ if ( !LDAP_DN_ALLOW_ONE_SPACE( flags ) ) {
+ /* error */
+ goto parsing_error;
+ }
+ p++;
+ }
+
+ if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+ if ( !LDAP_DN_ALLOW_SPACES( flags ) ) {
+ /* error */
+ goto parsing_error;
+ }
+
+ /* whitespace is allowed (and trimmed) */
+ p++;
+ while ( p[ 0 ] && LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+ p++;
+ }
+
+ if ( !p[ 0 ] ) {
+ /* error: we expected an AVA */
+ goto parsing_error;
+ }
+ }
+
+ /* oid */
+ if ( LDAP_DN_OID_LEADCHAR( p[ 0 ] ) ) {
+ state = B4OIDATTRTYPE;
+ break;
+ }
+
+ /* else must be alpha */
+ if ( !LDAP_DN_DESC_LEADCHAR( p[ 0 ] ) ) {
+ goto parsing_error;
+ }
+
+ /* LDAPv2 "oid." prefix */
+ if ( LDAP_DN_LDAPV2( flags ) ) {
+ /*
+ * to be overly pedantic, we only accept
+ * "OID." or "oid."
+ */
+ if ( flags & LDAP_DN_PEDANTIC ) {
+ if ( !strncmp( p, "OID.", 4 )
+ || !strncmp( p, "oid.", 4 ) ) {
+ p += 4;
+ state = B4OIDATTRTYPE;
+ break;
+ }
+ } else {
+ if ( !strncasecmp( p, "oid.", 4 ) ) {
+ p += 4;
+ state = B4OIDATTRTYPE;
+ break;
+ }
+ }
+ }
+
+ state = B4STRINGATTRTYPE;
+ break;
+
+ case B4OIDATTRTYPE: {
+ int err = LDAP_SUCCESS;
+
+ attrType.bv_val = ldap_int_parse_numericoid( &p, &err,
+ LDAP_SCHEMA_SKIP);
+
+ if ( err != LDAP_SUCCESS ) {
+ goto parsing_error;
+ }
+ attrType.bv_len = p - attrType.bv_val;
+
+ attrTypeEncoding = LDAP_AVA_BINARY;
+
+ state = B4AVAEQUALS;
+ break;
+ }
+
+ case B4STRINGATTRTYPE: {
+ const char *startPos, *endPos = NULL;
+ ber_len_t len;
+
+ /*
+ * the starting char has been found to be
+ * a LDAP_DN_DESC_LEADCHAR so we don't re-check it
+ * FIXME: DCE attr types seem to have a more
+ * restrictive syntax (no '-' ...)
+ */
+ for ( startPos = p++; p[ 0 ]; p++ ) {
+ if ( LDAP_DN_DESC_CHAR( p[ 0 ] ) ) {
+ continue;
+ }
+
+ if ( LDAP_DN_LANG_SEP( p[ 0 ] ) ) {
+
+ /*
+ * RFC 4514 explicitly does not allow attribute
+ * description options, such as language tags.
+ */
+ if ( flags & LDAP_DN_PEDANTIC ) {
+ goto parsing_error;
+ }
+
+ /*
+ * we trim ';' and following lang
+ * and so from attribute types
+ */
+ endPos = p;
+ for ( ; LDAP_DN_ATTRDESC_CHAR( p[ 0 ] )
+ || LDAP_DN_LANG_SEP( p[ 0 ] ); p++ ) {
+ /* no op */ ;
+ }
+ break;
+ }
+ break;
+ }
+
+ len = ( endPos ? endPos : p ) - startPos;
+ if ( len == 0 ) {
+ goto parsing_error;
+ }
+
+ attrTypeEncoding = LDAP_AVA_STRING;
+
+ /*
+ * here we need to decide whether to use it as is
+ * or turn it in OID form; as a consequence, we
+ * need to decide whether to binary encode the value
+ */
+
+ state = B4AVAEQUALS;
+
+ if ( flags & LDAP_DN_SKIP ) {
+ break;
+ }
+
+ attrType.bv_val = (char *)startPos;
+ attrType.bv_len = len;
+
+ break;
+ }
+
+ case B4AVAEQUALS:
+ /* spaces may not be allowed */
+ if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+ if ( !LDAP_DN_ALLOW_SPACES( flags ) ) {
+ goto parsing_error;
+ }
+
+ /* trim spaces */
+ for ( p++; LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) {
+ /* no op */
+ }
+ }
+
+ /* need equal sign */
+ if ( !LDAP_DN_AVA_EQUALS( p[ 0 ] ) ) {
+ goto parsing_error;
+ }
+ p++;
+
+ /* spaces may not be allowed */
+ if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+ if ( !LDAP_DN_ALLOW_SPACES( flags ) ) {
+ goto parsing_error;
+ }
+
+ /* trim spaces */
+ for ( p++; LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) {
+ /* no op */
+ }
+ }
+
+ /*
+ * octothorpe means a BER encoded value will follow
+ * FIXME: I don't think DCE will allow it
+ */
+ if ( LDAP_DN_OCTOTHORPE( p[ 0 ] ) ) {
+ p++;
+ attrValueEncoding = LDAP_AVA_BINARY;
+ state = B4BINARYVALUE;
+ break;
+ }
+
+ /* STRING value expected */
+
+ /*
+ * if we're pedantic, an attribute type in OID form
+ * SHOULD imply a BER encoded attribute value; we
+ * should at least issue a warning
+ */
+ if ( ( flags & LDAP_DN_PEDANTIC )
+ && ( attrTypeEncoding == LDAP_AVA_BINARY ) ) {
+ /* OID attrType SHOULD use binary encoding */
+ goto parsing_error;
+ }
+
+ attrValueEncoding = LDAP_AVA_STRING;
+
+ /*
+ * LDAPv2 allows the attribute value to be quoted;
+ * also, IA5 values are expected, in principle
+ */
+ if ( LDAP_DN_LDAPV2( flags ) || LDAP_DN_LDAP( flags ) ) {
+ if ( LDAP_DN_QUOTES( p[ 0 ] ) ) {
+ p++;
+ state = B4IA5VALUEQUOTED;
+ break;
+ }
+
+ if ( LDAP_DN_LDAPV2( flags ) ) {
+ state = B4IA5VALUE;
+ break;
+ }
+ }
+
+ /*
+ * here STRING means RFC 4514 string
+ * FIXME: what about DCE strings?
+ */
+ if ( !p[ 0 ] ) {
+ /* empty value */
+ state = GOTAVA;
+ } else {
+ state = B4STRINGVALUE;
+ }
+ break;
+
+ case B4BINARYVALUE:
+ if ( hexstr2binval( p, &attrValue, &p, flags, ctx ) ) {
+ goto parsing_error;
+ }
+
+ state = GOTAVA;
+ break;
+
+ case B4STRINGVALUE:
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAP:
+ case LDAP_DN_FORMAT_LDAPV3:
+ if ( str2strval( p, stoplen - ( p - str ),
+ &attrValue, &p, flags,
+ &attrValueEncoding, ctx ) ) {
+ goto parsing_error;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_DCE:
+ if ( DCE2strval( p, &attrValue, &p, flags, ctx ) ) {
+ goto parsing_error;
+ }
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+ state = GOTAVA;
+ break;
+
+ case B4IA5VALUE:
+ if ( IA52strval( p, &attrValue, &p, flags, ctx ) ) {
+ goto parsing_error;
+ }
+
+ state = GOTAVA;
+ break;
+
+ case B4IA5VALUEQUOTED:
+
+ /* lead quote already stripped */
+ if ( quotedIA52strval( p, &attrValue,
+ &p, flags, ctx ) ) {
+ goto parsing_error;
+ }
+
+ state = GOTAVA;
+ break;
+
+ case GOTAVA: {
+ int rdnsep = 0;
+
+ if ( !( flags & LDAP_DN_SKIP ) ) {
+ LDAPAVA *ava;
+
+ /*
+ * we accept empty values
+ */
+ ava = ldapava_new( &attrType, &attrValue,
+ attrValueEncoding, ctx );
+ if ( ava == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto parsing_error;
+ }
+ tmpRDN[navas++] = ava;
+
+ attrValue.bv_val = NULL;
+ attrValue.bv_len = 0;
+
+ /*
+ * prepare room for new AVAs if needed
+ */
+ if (navas == num_slots) {
+ LDAPAVA **tmp;
+
+ if ( tmpRDN == tmpRDN_ ) {
+ tmp = LDAP_MALLOCX( num_slots * 2 * sizeof( LDAPAVA * ), ctx );
+ if ( tmp == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto parsing_error;
+ }
+ AC_MEMCPY( tmp, tmpRDN, num_slots * sizeof( LDAPAVA * ) );
+
+ } else {
+ tmp = LDAP_REALLOCX( tmpRDN, num_slots * 2 * sizeof( LDAPAVA * ), ctx );
+ if ( tmp == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto parsing_error;
+ }
+ }
+
+ tmpRDN = tmp;
+ num_slots *= 2;
+ }
+ }
+
+ /*
+ * if we got an AVA separator ('+', or ',' for DCE )
+ * we expect a new AVA for this RDN; otherwise
+ * we add the RDN to the DN
+ */
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAP:
+ case LDAP_DN_FORMAT_LDAPV3:
+ case LDAP_DN_FORMAT_LDAPV2:
+ if ( !LDAP_DN_AVA_SEP( p[ 0 ] ) ) {
+ rdnsep = 1;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_DCE:
+ if ( !LDAP_DN_AVA_SEP_DCE( p[ 0 ] ) ) {
+ rdnsep = 1;
+ }
+ break;
+ }
+
+ if ( rdnsep ) {
+ /*
+ * the RDN is over, phew
+ */
+ *n = p;
+ if ( !( flags & LDAP_DN_SKIP ) ) {
+ newRDN = (LDAPRDN)LDAP_MALLOCX(
+ sizeof(LDAPAVA) * (navas+1), ctx );
+ if ( newRDN == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto parsing_error;
+ } else {
+ AC_MEMCPY( newRDN, tmpRDN, sizeof(LDAPAVA *) * navas);
+ newRDN[navas] = NULL;
+ }
+
+ }
+ rc = LDAP_SUCCESS;
+ goto return_result;
+ }
+
+ /* they should have been used in an AVA */
+ attrType.bv_val = NULL;
+ attrValue.bv_val = NULL;
+
+ p++;
+ state = B4AVA;
+ break;
+ }
+
+ default:
+ assert( 0 );
+ goto parsing_error;
+ }
+ }
+ *n = p;
+
+parsing_error:;
+ /* They are set to NULL after they're used in an AVA */
+
+ if ( attrValue.bv_val ) {
+ LDAP_FREEX( attrValue.bv_val, ctx );
+ }
+
+ for ( navas-- ; navas >= 0; navas-- ) {
+ ldapava_free( tmpRDN[navas], ctx );
+ }
+
+return_result:;
+
+ if ( tmpRDN != tmpRDN_ ) {
+ LDAP_FREEX( tmpRDN, ctx );
+ }
+
+ if ( rdn ) {
+ *rdn = newRDN;
+ }
+
+ return( rc );
+}
+
+/*
+ * reads in a UTF-8 string value, unescaping stuff:
+ * '\' + LDAP_DN_NEEDESCAPE(c) -> 'c'
+ * '\' + HEXPAIR(p) -> unhex(p)
+ */
+static int
+str2strval( const char *str, ber_len_t stoplen, struct berval *val, const char **next, unsigned flags, int *retFlags, void *ctx )
+{
+ const char *p, *end, *startPos, *endPos = NULL;
+ ber_len_t len, escapes;
+
+ assert( str != NULL );
+ assert( val != NULL );
+ assert( next != NULL );
+
+ *next = NULL;
+ end = str + stoplen;
+ for ( startPos = p = str, escapes = 0; p < end; p++ ) {
+ if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) {
+ p++;
+ if ( p[ 0 ] == '\0' ) {
+ return( 1 );
+ }
+ if ( LDAP_DN_MAYESCAPE( p[ 0 ] ) ) {
+ escapes++;
+ continue;
+ }
+
+ if ( LDAP_DN_HEXPAIR( p ) ) {
+ char c;
+
+ hexstr2bin( p, &c );
+ escapes += 2;
+
+ if ( !LDAP_DN_ASCII_PRINTABLE( c ) ) {
+
+ /*
+ * we assume the string is UTF-8
+ */
+ *retFlags = LDAP_AVA_NONPRINTABLE;
+ }
+ p++;
+
+ continue;
+ }
+
+ if ( LDAP_DN_PEDANTIC & flags ) {
+ return( 1 );
+ }
+ /*
+ * we do not allow escaping
+ * of chars that don't need
+ * to and do not belong to
+ * HEXDIGITS
+ */
+ return( 1 );
+
+ } else if ( !LDAP_DN_ASCII_PRINTABLE( p[ 0 ] ) ) {
+ if ( p[ 0 ] == '\0' ) {
+ return( 1 );
+ }
+ *retFlags = LDAP_AVA_NONPRINTABLE;
+
+ } else if ( ( LDAP_DN_LDAP( flags ) && LDAP_DN_VALUE_END_V2( p[ 0 ] ) )
+ || ( LDAP_DN_LDAPV3( flags ) && LDAP_DN_VALUE_END( p[ 0 ] ) ) ) {
+ break;
+
+ } else if ( LDAP_DN_NEEDESCAPE( p[ 0 ] ) ) {
+ /*
+ * FIXME: maybe we can add
+ * escapes if not pedantic?
+ */
+ return( 1 );
+ }
+ }
+
+ /*
+ * we do allow unescaped spaces at the end
+ * of the value only in non-pedantic mode
+ */
+ if ( p > startPos + 1 && LDAP_DN_ASCII_SPACE( p[ -1 ] ) &&
+ !LDAP_DN_ESCAPE( p[ -2 ] ) ) {
+ if ( flags & LDAP_DN_PEDANTIC ) {
+ return( 1 );
+ }
+
+ /* strip trailing (unescaped) spaces */
+ for ( endPos = p - 1;
+ endPos > startPos + 1 &&
+ LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) &&
+ !LDAP_DN_ESCAPE( endPos[ -2 ] );
+ endPos-- ) {
+ /* no op */
+ }
+ }
+
+ *next = p;
+ if ( flags & LDAP_DN_SKIP ) {
+ return( 0 );
+ }
+
+ /*
+ * FIXME: test memory?
+ */
+ len = ( endPos ? endPos : p ) - startPos - escapes;
+ val->bv_len = len;
+
+ if ( escapes == 0 ) {
+ if ( *retFlags & LDAP_AVA_NONPRINTABLE ) {
+ val->bv_val = LDAP_MALLOCX( len + 1, ctx );
+ if ( val->bv_val == NULL ) {
+ return( 1 );
+ }
+
+ AC_MEMCPY( val->bv_val, startPos, len );
+ val->bv_val[ len ] = '\0';
+ } else {
+ val->bv_val = LDAP_STRNDUPX( startPos, len, ctx );
+ }
+
+ } else {
+ ber_len_t s, d;
+
+ val->bv_val = LDAP_MALLOCX( len + 1, ctx );
+ if ( val->bv_val == NULL ) {
+ return( 1 );
+ }
+
+ for ( s = 0, d = 0; d < len; ) {
+ if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) {
+ s++;
+ if ( LDAP_DN_MAYESCAPE( startPos[ s ] ) ) {
+ val->bv_val[ d++ ] =
+ startPos[ s++ ];
+
+ } else if ( LDAP_DN_HEXPAIR( &startPos[ s ] ) ) {
+ char c;
+
+ hexstr2bin( &startPos[ s ], &c );
+ val->bv_val[ d++ ] = c;
+ s += 2;
+
+ } else {
+ /* we should never get here */
+ assert( 0 );
+ }
+
+ } else {
+ val->bv_val[ d++ ] = startPos[ s++ ];
+ }
+ }
+
+ val->bv_val[ d ] = '\0';
+ assert( d == len );
+ }
+
+ return( 0 );
+}
+
+static int
+DCE2strval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx )
+{
+ const char *p, *startPos, *endPos = NULL;
+ ber_len_t len, escapes;
+
+ assert( str != NULL );
+ assert( val != NULL );
+ assert( next != NULL );
+
+ *next = NULL;
+
+ for ( startPos = p = str, escapes = 0; p[ 0 ]; p++ ) {
+ if ( LDAP_DN_ESCAPE_DCE( p[ 0 ] ) ) {
+ p++;
+ if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) {
+ escapes++;
+
+ } else {
+ return( 1 );
+ }
+
+ } else if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
+ break;
+ }
+
+ /*
+ * FIXME: can we accept anything else? I guess we need
+ * to stop if a value is not legal
+ */
+ }
+
+ /*
+ * (unescaped) trailing spaces are trimmed must be silently ignored;
+ * so we eat them
+ */
+ if ( p > startPos + 1 && LDAP_DN_ASCII_SPACE( p[ -1 ] ) &&
+ !LDAP_DN_ESCAPE( p[ -2 ] ) ) {
+ if ( flags & LDAP_DN_PEDANTIC ) {
+ return( 1 );
+ }
+
+ /* strip trailing (unescaped) spaces */
+ for ( endPos = p - 1;
+ endPos > startPos + 1 &&
+ LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) &&
+ !LDAP_DN_ESCAPE( endPos[ -2 ] );
+ endPos-- ) {
+ /* no op */
+ }
+ }
+
+ *next = p;
+ if ( flags & LDAP_DN_SKIP ) {
+ return( 0 );
+ }
+
+ len = ( endPos ? endPos : p ) - startPos - escapes;
+ val->bv_len = len;
+ if ( escapes == 0 ){
+ val->bv_val = LDAP_STRNDUPX( startPos, len, ctx );
+
+ } else {
+ ber_len_t s, d;
+
+ val->bv_val = LDAP_MALLOCX( len + 1, ctx );
+ if ( val->bv_val == NULL ) {
+ return( 1 );
+ }
+
+ for ( s = 0, d = 0; d < len; ) {
+ /*
+ * This point is reached only if escapes
+ * are properly used, so all we need to
+ * do is eat them
+ */
+ if ( LDAP_DN_ESCAPE_DCE( startPos[ s ] ) ) {
+ s++;
+
+ }
+ val->bv_val[ d++ ] = startPos[ s++ ];
+ }
+ val->bv_val[ d ] = '\0';
+ assert( strlen( val->bv_val ) == len );
+ }
+
+ return( 0 );
+}
+
+static int
+IA52strval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx )
+{
+ const char *p, *startPos, *endPos = NULL;
+ ber_len_t len, escapes;
+
+ assert( str != NULL );
+ assert( val != NULL );
+ assert( next != NULL );
+
+ *next = NULL;
+
+ /*
+ * LDAPv2 (RFC 1779)
+ */
+
+ for ( startPos = p = str, escapes = 0; p[ 0 ]; p++ ) {
+ if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) {
+ p++;
+ if ( p[ 0 ] == '\0' ) {
+ return( 1 );
+ }
+
+ if ( !LDAP_DN_NEEDESCAPE( p[ 0 ] )
+ && ( LDAP_DN_PEDANTIC & flags ) ) {
+ return( 1 );
+ }
+ escapes++;
+
+ } else if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) {
+ break;
+ }
+
+ /*
+ * FIXME: can we accept anything else? I guess we need
+ * to stop if a value is not legal
+ */
+ }
+
+ /* strip trailing (unescaped) spaces */
+ for ( endPos = p;
+ endPos > startPos + 1 &&
+ LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) &&
+ !LDAP_DN_ESCAPE( endPos[ -2 ] );
+ endPos-- ) {
+ /* no op */
+ }
+
+ *next = p;
+ if ( flags & LDAP_DN_SKIP ) {
+ return( 0 );
+ }
+
+ len = ( endPos ? endPos : p ) - startPos - escapes;
+ val->bv_len = len;
+ if ( escapes == 0 ) {
+ val->bv_val = LDAP_STRNDUPX( startPos, len, ctx );
+
+ } else {
+ ber_len_t s, d;
+
+ val->bv_val = LDAP_MALLOCX( len + 1, ctx );
+ if ( val->bv_val == NULL ) {
+ return( 1 );
+ }
+
+ for ( s = 0, d = 0; d < len; ) {
+ if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) {
+ s++;
+ }
+ val->bv_val[ d++ ] = startPos[ s++ ];
+ }
+ val->bv_val[ d ] = '\0';
+ assert( strlen( val->bv_val ) == len );
+ }
+
+ return( 0 );
+}
+
+static int
+quotedIA52strval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx )
+{
+ const char *p, *startPos, *endPos = NULL;
+ ber_len_t len;
+ unsigned escapes = 0;
+
+ assert( str != NULL );
+ assert( val != NULL );
+ assert( next != NULL );
+
+ *next = NULL;
+
+ /* initial quote already eaten */
+ for ( startPos = p = str; p[ 0 ]; p++ ) {
+ /*
+ * According to RFC 1779, the quoted value can
+ * contain escaped as well as unescaped special values;
+ * as a consequence we tolerate escaped values
+ * (e.g. '"\,"' -> '\,') and escape unescaped specials
+ * (e.g. '","' -> '\,').
+ */
+ if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) {
+ if ( p[ 1 ] == '\0' ) {
+ return( 1 );
+ }
+ p++;
+
+ if ( !LDAP_DN_V2_PAIR( p[ 0 ] )
+ && ( LDAP_DN_PEDANTIC & flags ) ) {
+ /*
+ * do we allow to escape normal chars?
+ * LDAPv2 does not allow any mechanism
+ * for escaping chars with '\' and hex
+ * pair
+ */
+ return( 1 );
+ }
+ escapes++;
+
+ } else if ( LDAP_DN_QUOTES( p[ 0 ] ) ) {
+ endPos = p;
+ /* eat closing quotes */
+ p++;
+ break;
+ }
+
+ /*
+ * FIXME: can we accept anything else? I guess we need
+ * to stop if a value is not legal
+ */
+ }
+
+ if ( endPos == NULL ) {
+ return( 1 );
+ }
+
+ /* Strip trailing (unescaped) spaces */
+ for ( ; p[ 0 ] && LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) {
+ /* no op */
+ }
+
+ *next = p;
+ if ( flags & LDAP_DN_SKIP ) {
+ return( 0 );
+ }
+
+ len = endPos - startPos - escapes;
+ assert( endPos >= startPos + escapes );
+ val->bv_len = len;
+ if ( escapes == 0 ) {
+ val->bv_val = LDAP_STRNDUPX( startPos, len, ctx );
+
+ } else {
+ ber_len_t s, d;
+
+ val->bv_val = LDAP_MALLOCX( len + 1, ctx );
+ if ( val->bv_val == NULL ) {
+ return( 1 );
+ }
+
+ val->bv_len = len;
+
+ for ( s = d = 0; d < len; ) {
+ if ( LDAP_DN_ESCAPE( str[ s ] ) ) {
+ s++;
+ }
+ val->bv_val[ d++ ] = str[ s++ ];
+ }
+ val->bv_val[ d ] = '\0';
+ assert( strlen( val->bv_val ) == len );
+ }
+
+ return( 0 );
+}
+
+static int
+hexstr2bin( const char *str, char *c )
+{
+ char c1, c2;
+
+ assert( str != NULL );
+ assert( c != NULL );
+
+ c1 = str[ 0 ];
+ c2 = str[ 1 ];
+
+ if ( LDAP_DN_ASCII_DIGIT( c1 ) ) {
+ *c = c1 - '0';
+
+ } else {
+ if ( LDAP_DN_ASCII_UCASE_HEXALPHA( c1 ) ) {
+ *c = c1 - 'A' + 10;
+ } else {
+ assert( LDAP_DN_ASCII_LCASE_HEXALPHA( c1 ) );
+ *c = c1 - 'a' + 10;
+ }
+ }
+
+ *c <<= 4;
+
+ if ( LDAP_DN_ASCII_DIGIT( c2 ) ) {
+ *c += c2 - '0';
+
+ } else {
+ if ( LDAP_DN_ASCII_UCASE_HEXALPHA( c2 ) ) {
+ *c += c2 - 'A' + 10;
+ } else {
+ assert( LDAP_DN_ASCII_LCASE_HEXALPHA( c2 ) );
+ *c += c2 - 'a' + 10;
+ }
+ }
+
+ return( 0 );
+}
+
+static int
+hexstr2binval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx )
+{
+ const char *p, *startPos, *endPos = NULL;
+ ber_len_t len;
+ ber_len_t s, d;
+
+ assert( str != NULL );
+ assert( val != NULL );
+ assert( next != NULL );
+
+ *next = NULL;
+
+ for ( startPos = p = str; p[ 0 ]; p += 2 ) {
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAPV3:
+ if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) {
+ goto end_of_value;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_LDAP:
+ case LDAP_DN_FORMAT_LDAPV2:
+ if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) {
+ goto end_of_value;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_DCE:
+ if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
+ goto end_of_value;
+ }
+ break;
+ }
+
+ if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+ if ( flags & LDAP_DN_PEDANTIC ) {
+ return( 1 );
+ }
+ endPos = p;
+
+ for ( ; p[ 0 ]; p++ ) {
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAPV3:
+ if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) {
+ goto end_of_value;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_LDAP:
+ case LDAP_DN_FORMAT_LDAPV2:
+ if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) {
+ goto end_of_value;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_DCE:
+ if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
+ goto end_of_value;
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ if ( !LDAP_DN_HEXPAIR( p ) ) {
+ return( 1 );
+ }
+ }
+
+end_of_value:;
+
+ *next = p;
+ if ( flags & LDAP_DN_SKIP ) {
+ return( 0 );
+ }
+
+ len = ( ( endPos ? endPos : p ) - startPos ) / 2;
+ /* must be even! */
+ assert( 2 * len == (ber_len_t) (( endPos ? endPos : p ) - startPos ));
+
+ val->bv_len = len;
+ val->bv_val = LDAP_MALLOCX( len + 1, ctx );
+ if ( val->bv_val == NULL ) {
+ return( LDAP_NO_MEMORY );
+ }
+
+ for ( s = 0, d = 0; d < len; s += 2, d++ ) {
+ char c;
+
+ hexstr2bin( &startPos[ s ], &c );
+
+ val->bv_val[ d ] = c;
+ }
+
+ val->bv_val[ d ] = '\0';
+
+ return( 0 );
+}
+
+/*
+ * convert a byte in a hexadecimal pair
+ */
+static int
+byte2hexpair( const char *val, char *pair )
+{
+ static const char hexdig[] = "0123456789ABCDEF";
+
+ assert( val != NULL );
+ assert( pair != NULL );
+
+ /*
+ * we assume the string has enough room for the hex encoding
+ * of the value
+ */
+
+ pair[ 0 ] = hexdig[ 0x0f & ( val[ 0 ] >> 4 ) ];
+ pair[ 1 ] = hexdig[ 0x0f & val[ 0 ] ];
+
+ return( 0 );
+}
+
+/*
+ * convert a binary value in hexadecimal pairs
+ */
+static int
+binval2hexstr( struct berval *val, char *str )
+{
+ ber_len_t s, d;
+
+ assert( val != NULL );
+ assert( str != NULL );
+
+ if ( val->bv_len == 0 ) {
+ return( 0 );
+ }
+
+ /*
+ * we assume the string has enough room for the hex encoding
+ * of the value
+ */
+
+ for ( s = 0, d = 0; s < val->bv_len; s++, d += 2 ) {
+ byte2hexpair( &val->bv_val[ s ], &str[ d ] );
+ }
+
+ return( 0 );
+}
+
+/*
+ * Length of the string representation, accounting for escaped hex
+ * of UTF-8 chars
+ */
+static int
+strval2strlen( struct berval *val, unsigned flags, ber_len_t *len )
+{
+ ber_len_t l, cl = 1;
+ char *p, *end;
+ int escaped_byte_len = LDAP_DN_IS_PRETTY( flags ) ? 1 : 3;
+#ifdef PRETTY_ESCAPE
+ int escaped_ascii_len = LDAP_DN_IS_PRETTY( flags ) ? 2 : 3;
+#endif /* PRETTY_ESCAPE */
+
+ assert( val != NULL );
+ assert( len != NULL );
+
+ *len = 0;
+ if ( val->bv_len == 0 ) {
+ return( 0 );
+ }
+
+ end = val->bv_val + val->bv_len - 1;
+ for ( l = 0, p = val->bv_val; p <= end; p += cl ) {
+
+ /*
+ * escape '%x00'
+ */
+ if ( p[ 0 ] == '\0' ) {
+ cl = 1;
+ l += 3;
+ continue;
+ }
+
+ cl = LDAP_UTF8_CHARLEN2( p, cl );
+ if ( cl == 0 ) {
+ /* illegal utf-8 char! */
+ return( -1 );
+
+ } else if ( cl > 1 ) {
+ ber_len_t cnt;
+
+ for ( cnt = 1; cnt < cl; cnt++ ) {
+ if ( ( p[ cnt ] & 0xc0 ) != 0x80 ) {
+ return( -1 );
+ }
+ }
+ l += escaped_byte_len * cl;
+
+ } else if ( LDAP_DN_NEEDESCAPE( p[ 0 ] )
+ || LDAP_DN_SHOULDESCAPE( p[ 0 ] )
+ || ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) )
+ || ( p == end && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) ) {
+#ifdef PRETTY_ESCAPE
+#if 0
+ if ( LDAP_DN_WILLESCAPE_HEX( flags, p[ 0 ] ) ) {
+#else
+ if ( LDAP_DN_WILLESCAPE_CHAR( p[ 0 ] ) ) {
+#endif
+
+ /*
+ * there might be some chars we want
+ * to escape in form of a couple
+ * of hexdigits for optimization purposes
+ */
+ l += 3;
+
+ } else {
+ l += escaped_ascii_len;
+ }
+#else /* ! PRETTY_ESCAPE */
+ l += 3;
+#endif /* ! PRETTY_ESCAPE */
+
+ } else {
+ l++;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+/*
+ * convert to string representation, escaping with hex the UTF-8 stuff;
+ * assume the destination has enough room for escaping
+ */
+static int
+strval2str( struct berval *val, char *str, unsigned flags, ber_len_t *len )
+{
+ ber_len_t s, d, end;
+
+ assert( val != NULL );
+ assert( str != NULL );
+ assert( len != NULL );
+
+ if ( val->bv_len == 0 ) {
+ *len = 0;
+ return( 0 );
+ }
+
+ /*
+ * we assume the string has enough room for the hex encoding
+ * of the value
+ */
+ for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) {
+ ber_len_t cl;
+
+ /*
+ * escape '%x00'
+ */
+ if ( val->bv_val[ s ] == '\0' ) {
+ cl = 1;
+ str[ d++ ] = '\\';
+ str[ d++ ] = '0';
+ str[ d++ ] = '0';
+ s++;
+ continue;
+ }
+
+ /*
+ * The length was checked in strval2strlen();
+ */
+ cl = LDAP_UTF8_CHARLEN( &val->bv_val[ s ] );
+
+ /*
+ * there might be some chars we want to escape in form
+ * of a couple of hexdigits for optimization purposes
+ */
+ if ( ( cl > 1 && !LDAP_DN_IS_PRETTY( flags ) )
+#ifdef PRETTY_ESCAPE
+#if 0
+ || LDAP_DN_WILLESCAPE_HEX( flags, val->bv_val[ s ] )
+#else
+ || LDAP_DN_WILLESCAPE_CHAR( val->bv_val[ s ] )
+#endif
+#else /* ! PRETTY_ESCAPE */
+ || LDAP_DN_NEEDESCAPE( val->bv_val[ s ] )
+ || LDAP_DN_SHOULDESCAPE( val->bv_val[ s ] )
+ || ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
+ || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) )
+
+#endif /* ! PRETTY_ESCAPE */
+ ) {
+ for ( ; cl--; ) {
+ str[ d++ ] = '\\';
+ byte2hexpair( &val->bv_val[ s ], &str[ d ] );
+ s++;
+ d += 2;
+ }
+
+ } else if ( cl > 1 ) {
+ for ( ; cl--; ) {
+ str[ d++ ] = val->bv_val[ s++ ];
+ }
+
+ } else {
+#ifdef PRETTY_ESCAPE
+ if ( LDAP_DN_NEEDESCAPE( val->bv_val[ s ] )
+ || LDAP_DN_SHOULDESCAPE( val->bv_val[ s ] )
+ || ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
+ || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) ) {
+ str[ d++ ] = '\\';
+ if ( !LDAP_DN_IS_PRETTY( flags ) ) {
+ byte2hexpair( &val->bv_val[ s ], &str[ d ] );
+ s++;
+ d += 2;
+ continue;
+ }
+ }
+#endif /* PRETTY_ESCAPE */
+ str[ d++ ] = val->bv_val[ s++ ];
+ }
+ }
+
+ *len = d;
+
+ return( 0 );
+}
+
+/*
+ * Length of the IA5 string representation (no UTF-8 allowed)
+ */
+static int
+strval2IA5strlen( struct berval *val, unsigned flags, ber_len_t *len )
+{
+ ber_len_t l;
+ char *p;
+
+ assert( val != NULL );
+ assert( len != NULL );
+
+ *len = 0;
+ if ( val->bv_len == 0 ) {
+ return( 0 );
+ }
+
+ if ( flags & LDAP_AVA_NONPRINTABLE ) {
+ /*
+ * Turn value into a binary encoded BER
+ */
+ return( -1 );
+
+ } else {
+ for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) {
+ if ( LDAP_DN_NEEDESCAPE( p[ 0 ] )
+ || LDAP_DN_SHOULDESCAPE( p[ 0 ] )
+ || ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) )
+ || ( !p[ 1 ] && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) ) {
+ l += 2;
+
+ } else {
+ l++;
+ }
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+/*
+ * convert to string representation (np UTF-8)
+ * assume the destination has enough room for escaping
+ */
+static int
+strval2IA5str( struct berval *val, char *str, unsigned flags, ber_len_t *len )
+{
+ ber_len_t s, d, end;
+
+ assert( val != NULL );
+ assert( str != NULL );
+ assert( len != NULL );
+
+ if ( val->bv_len == 0 ) {
+ *len = 0;
+ return( 0 );
+ }
+
+ if ( flags & LDAP_AVA_NONPRINTABLE ) {
+ /*
+ * Turn value into a binary encoded BER
+ */
+ *len = 0;
+ return( -1 );
+
+ } else {
+ /*
+ * we assume the string has enough room for the hex encoding
+ * of the value
+ */
+
+ for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) {
+ if ( LDAP_DN_NEEDESCAPE( val->bv_val[ s ] )
+ || LDAP_DN_SHOULDESCAPE( val->bv_val[ s ] )
+ || ( s == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
+ || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) ) {
+ str[ d++ ] = '\\';
+ }
+ str[ d++ ] = val->bv_val[ s++ ];
+ }
+ }
+
+ *len = d;
+
+ return( 0 );
+}
+
+/*
+ * Length of the (supposedly) DCE string representation,
+ * accounting for escaped hex of UTF-8 chars
+ */
+static int
+strval2DCEstrlen( struct berval *val, unsigned flags, ber_len_t *len )
+{
+ ber_len_t l;
+ char *p;
+
+ assert( val != NULL );
+ assert( len != NULL );
+
+ *len = 0;
+ if ( val->bv_len == 0 ) {
+ return( 0 );
+ }
+
+ if ( flags & LDAP_AVA_NONPRINTABLE ) {
+ /*
+ * FIXME: Turn the value into a binary encoded BER?
+ */
+ return( -1 );
+
+ } else {
+ for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) {
+ if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) {
+ l += 2;
+
+ } else {
+ l++;
+ }
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+/*
+ * convert to (supposedly) DCE string representation,
+ * escaping with hex the UTF-8 stuff;
+ * assume the destination has enough room for escaping
+ */
+static int
+strval2DCEstr( struct berval *val, char *str, unsigned flags, ber_len_t *len )
+{
+ ber_len_t s, d;
+
+ assert( val != NULL );
+ assert( str != NULL );
+ assert( len != NULL );
+
+ if ( val->bv_len == 0 ) {
+ *len = 0;
+ return( 0 );
+ }
+
+ if ( flags & LDAP_AVA_NONPRINTABLE ) {
+ /*
+ * FIXME: Turn the value into a binary encoded BER?
+ */
+ *len = 0;
+ return( -1 );
+
+ } else {
+
+ /*
+ * we assume the string has enough room for the hex encoding
+ * of the value
+ */
+
+ for ( s = 0, d = 0; s < val->bv_len; ) {
+ if ( LDAP_DN_NEEDESCAPE_DCE( val->bv_val[ s ] ) ) {
+ str[ d++ ] = '\\';
+ }
+ str[ d++ ] = val->bv_val[ s++ ];
+ }
+ }
+
+ *len = d;
+
+ return( 0 );
+}
+
+/*
+ * Length of the (supposedly) AD canonical string representation,
+ * accounting for chars that need to be escaped
+ */
+static int
+strval2ADstrlen( struct berval *val, unsigned flags, ber_len_t *len )
+{
+ ber_len_t l, cl;
+ char *p;
+
+ assert( val != NULL );
+ assert( len != NULL );
+
+ *len = 0;
+ if ( val->bv_len == 0 ) {
+ return( 0 );
+ }
+
+ for ( l = 0, p = val->bv_val; p[ 0 ]; p += cl ) {
+ cl = LDAP_UTF8_CHARLEN2( p, cl );
+ if ( cl == 0 ) {
+ /* illegal utf-8 char */
+ return -1;
+ } else if ( (cl == 1) && LDAP_DN_NEEDESCAPE_AD( p[ 0 ] ) ) {
+ l += 2;
+ } else {
+ l += cl;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+/*
+ * convert to (supposedly) AD string representation,
+ * assume the destination has enough room for escaping
+ */
+static int
+strval2ADstr( struct berval *val, char *str, unsigned flags, ber_len_t *len )
+{
+ ber_len_t s, d, cl;
+
+ assert( val != NULL );
+ assert( str != NULL );
+ assert( len != NULL );
+
+ if ( val->bv_len == 0 ) {
+ *len = 0;
+ return( 0 );
+ }
+
+ /*
+ * we assume the string has enough room for the escaping
+ * of the value
+ */
+
+ for ( s = 0, d = 0; s < val->bv_len; ) {
+ cl = LDAP_UTF8_CHARLEN2( val->bv_val+s, cl );
+ if ( cl == 0 ) {
+ /* illegal utf-8 char */
+ return -1;
+ } else if ( (cl == 1) && LDAP_DN_NEEDESCAPE_AD(val->bv_val[ s ]) ) {
+ str[ d++ ] = '\\';
+ }
+ for (; cl--;) {
+ str[ d++ ] = val->bv_val[ s++ ];
+ }
+ }
+
+ *len = d;
+
+ return( 0 );
+}
+
+/*
+ * If the DN is terminated by single-AVA RDNs with attribute type of "dc",
+ * the first part of the AD representation of the DN is written in DNS
+ * form, i.e. dot separated domain name components (as suggested
+ * by Luke Howard, http://www.padl.com/~lukeh)
+ */
+static int
+dn2domain( LDAPDN dn, struct berval *bv, int pos, int *iRDN )
+{
+ int i;
+ int domain = 0, first = 1;
+ ber_len_t l = 1; /* we move the null also */
+ char *str;
+
+ /* we are guaranteed there's enough memory in str */
+
+ /* sanity */
+ assert( dn != NULL );
+ assert( bv != NULL );
+ assert( iRDN != NULL );
+ assert( *iRDN >= 0 );
+
+ str = bv->bv_val + pos;
+
+ for ( i = *iRDN; i >= 0; i-- ) {
+ LDAPRDN rdn;
+ LDAPAVA *ava;
+
+ assert( dn[ i ] != NULL );
+ rdn = dn[ i ];
+
+ assert( rdn[ 0 ] != NULL );
+ ava = rdn[ 0 ];
+
+ if ( !LDAP_DN_IS_RDN_DC( rdn ) ) {
+ break;
+ }
+
+ if ( ldif_is_not_printable( ava->la_value.bv_val, ava->la_value.bv_len ) ) {
+ domain = 0;
+ break;
+ }
+
+ domain = 1;
+
+ if ( first ) {
+ first = 0;
+ AC_MEMCPY( str, ava->la_value.bv_val,
+ ava->la_value.bv_len + 1);
+ l += ava->la_value.bv_len;
+
+ } else {
+ AC_MEMCPY( str + ava->la_value.bv_len + 1, bv->bv_val + pos, l);
+ AC_MEMCPY( str, ava->la_value.bv_val,
+ ava->la_value.bv_len );
+ str[ ava->la_value.bv_len ] = '.';
+ l += ava->la_value.bv_len + 1;
+ }
+ }
+
+ *iRDN = i;
+ bv->bv_len = pos + l - 1;
+
+ return( domain );
+}
+
+static int
+rdn2strlen( LDAPRDN rdn, unsigned flags, ber_len_t *len,
+ int ( *s2l )( struct berval *v, unsigned f, ber_len_t *l ) )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ *len = 0;
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ iAVA ];
+
+ /* len(type) + '=' + '+' | ',' */
+ l += ava->la_attr.bv_len + 2;
+
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ /* octothorpe + twice the length */
+ l += 1 + 2 * ava->la_value.bv_len;
+
+ } else {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( ( *s2l )( &ava->la_value, f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+static int
+rdn2str( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len,
+ int ( *s2s ) ( struct berval *v, char * s, unsigned f, ber_len_t *l ) )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ iAVA ];
+
+ AC_MEMCPY( &str[ l ], ava->la_attr.bv_val,
+ ava->la_attr.bv_len );
+ l += ava->la_attr.bv_len;
+
+ str[ l++ ] = '=';
+
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ str[ l++ ] = '#';
+ if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
+ return( -1 );
+ }
+ l += 2 * ava->la_value.bv_len;
+
+ } else {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( ( *s2s )( &ava->la_value, &str[ l ], f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ }
+ str[ l++ ] = ( rdn[ iAVA + 1] ? '+' : ',' );
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+static int
+rdn2DCEstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ *len = 0;
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ iAVA ];
+
+ /* len(type) + '=' + ',' | '/' */
+ l += ava->la_attr.bv_len + 2;
+
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ /* octothorpe + twice the length */
+ l += 1 + 2 * ava->la_value.bv_len;
+ } else {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( strval2DCEstrlen( &ava->la_value, f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+static int
+rdn2DCEstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, int first )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ iAVA ];
+
+ if ( first ) {
+ first = 0;
+ } else {
+ str[ l++ ] = ( iAVA ? ',' : '/' );
+ }
+
+ AC_MEMCPY( &str[ l ], ava->la_attr.bv_val,
+ ava->la_attr.bv_len );
+ l += ava->la_attr.bv_len;
+
+ str[ l++ ] = '=';
+
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ str[ l++ ] = '#';
+ if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
+ return( -1 );
+ }
+ l += 2 * ava->la_value.bv_len;
+ } else {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( strval2DCEstr( &ava->la_value, &str[ l ], f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+static int
+rdn2UFNstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ assert( rdn != NULL );
+ assert( len != NULL );
+
+ *len = 0;
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ iAVA ];
+
+ /* ' + ' | ', ' */
+ l += ( rdn[ iAVA + 1 ] ? 3 : 2 );
+
+ /* FIXME: are binary values allowed in UFN? */
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ /* octothorpe + twice the value */
+ l += 1 + 2 * ava->la_value.bv_len;
+
+ } else {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( strval2strlen( &ava->la_value, f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+static int
+rdn2UFNstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ iAVA ];
+
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ str[ l++ ] = '#';
+ if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
+ return( -1 );
+ }
+ l += 2 * ava->la_value.bv_len;
+
+ } else {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( strval2str( &ava->la_value, &str[ l ], f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ }
+
+ if ( rdn[ iAVA + 1 ] ) {
+ AC_MEMCPY( &str[ l ], " + ", 3 );
+ l += 3;
+
+ } else {
+ AC_MEMCPY( &str[ l ], ", ", 2 );
+ l += 2;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+static int
+rdn2ADstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ assert( rdn != NULL );
+ assert( len != NULL );
+
+ *len = 0;
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ iAVA ];
+
+ /* ',' | '/' */
+ l++;
+
+ /* FIXME: are binary values allowed in UFN? */
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ /* octothorpe + twice the value */
+ l += 1 + 2 * ava->la_value.bv_len;
+ } else {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( strval2ADstrlen( &ava->la_value, f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+static int
+rdn2ADstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, int first )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ iAVA ];
+
+ if ( first ) {
+ first = 0;
+ } else {
+ str[ l++ ] = ( iAVA ? ',' : '/' );
+ }
+
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ str[ l++ ] = '#';
+ if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
+ return( -1 );
+ }
+ l += 2 * ava->la_value.bv_len;
+ } else {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( strval2ADstr( &ava->la_value, &str[ l ], f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+/*
+ * ldap_rdn2str
+ *
+ * Returns in str a string representation of rdn based on flags.
+ * There is some duplication of code between this and ldap_dn2str;
+ * this is wanted to reduce the allocation of temporary buffers.
+ */
+int
+ldap_rdn2str( LDAPRDN rdn, char **str, unsigned flags )
+{
+ struct berval bv;
+ int rc;
+
+ assert( str != NULL );
+
+ if((flags & LDAP_DN_FORMAT_MASK) == LDAP_DN_FORMAT_LBER) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ rc = ldap_rdn2bv_x( rdn, &bv, flags, NULL );
+ *str = bv.bv_val;
+ return rc;
+}
+
+int
+ldap_rdn2bv( LDAPRDN rdn, struct berval *bv, unsigned flags )
+{
+ return ldap_rdn2bv_x( rdn, bv, flags, NULL );
+}
+
+int
+ldap_rdn2bv_x( LDAPRDN rdn, struct berval *bv, unsigned flags, void *ctx )
+{
+ int rc, back;
+ ber_len_t l;
+
+ assert( bv != NULL );
+
+ bv->bv_len = 0;
+ bv->bv_val = NULL;
+
+ if ( rdn == NULL ) {
+ bv->bv_val = LDAP_STRDUPX( "", ctx );
+ return( LDAP_SUCCESS );
+ }
+
+ /*
+ * This routine wastes "back" bytes at the end of the string
+ */
+
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAPV3:
+ if ( rdn2strlen( rdn, flags, &l, strval2strlen ) ) {
+ return LDAP_DECODING_ERROR;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_LDAPV2:
+ if ( rdn2strlen( rdn, flags, &l, strval2IA5strlen ) ) {
+ return LDAP_DECODING_ERROR;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_UFN:
+ if ( rdn2UFNstrlen( rdn, flags, &l ) ) {
+ return LDAP_DECODING_ERROR;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_DCE:
+ if ( rdn2DCEstrlen( rdn, flags, &l ) ) {
+ return LDAP_DECODING_ERROR;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_AD_CANONICAL:
+ if ( rdn2ADstrlen( rdn, flags, &l ) ) {
+ return LDAP_DECODING_ERROR;
+ }
+ break;
+
+ default:
+ return LDAP_PARAM_ERROR;
+ }
+
+ bv->bv_val = LDAP_MALLOCX( l + 1, ctx );
+ if ( bv->bv_val == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAPV3:
+ rc = rdn2str( rdn, bv->bv_val, flags, &l, strval2str );
+ back = 1;
+ break;
+
+ case LDAP_DN_FORMAT_LDAPV2:
+ rc = rdn2str( rdn, bv->bv_val, flags, &l, strval2IA5str );
+ back = 1;
+ break;
+
+ case LDAP_DN_FORMAT_UFN:
+ rc = rdn2UFNstr( rdn, bv->bv_val, flags, &l );
+ back = 2;
+ break;
+
+ case LDAP_DN_FORMAT_DCE:
+ rc = rdn2DCEstr( rdn, bv->bv_val, flags, &l, 1 );
+ back = 0;
+ break;
+
+ case LDAP_DN_FORMAT_AD_CANONICAL:
+ rc = rdn2ADstr( rdn, bv->bv_val, flags, &l, 1 );
+ back = 0;
+ break;
+
+ default:
+ /* need at least one of the previous */
+ return LDAP_PARAM_ERROR;
+ }
+
+ if ( rc ) {
+ LDAP_FREEX( bv->bv_val, ctx );
+ return rc;
+ }
+
+ bv->bv_len = l - back;
+ bv->bv_val[ bv->bv_len ] = '\0';
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Very bulk implementation; many optimizations can be performed
+ * - a NULL dn results in an empty string ""
+ *
+ * FIXME: doubts
+ * a) what do we do if a UTF-8 string must be converted in LDAPv2?
+ * we must encode it in binary form ('#' + HEXPAIRs)
+ * b) does DCE/AD support UTF-8?
+ * no clue; don't think so.
+ * c) what do we do when binary values must be converted in UTF/DCE/AD?
+ * use binary encoded BER
+ */
+int ldap_dn2str( LDAPDN dn, char **str, unsigned flags )
+{
+ struct berval bv;
+ int rc;
+
+ assert( str != NULL );
+
+ if((flags & LDAP_DN_FORMAT_MASK) == LDAP_DN_FORMAT_LBER) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ rc = ldap_dn2bv_x( dn, &bv, flags, NULL );
+ *str = bv.bv_val;
+ return rc;
+}
+
+int ldap_dn2bv( LDAPDN dn, struct berval *bv, unsigned flags )
+{
+ return ldap_dn2bv_x( dn, bv, flags, NULL );
+}
+
+int ldap_dn2bv_x( LDAPDN dn, struct berval *bv, unsigned flags, void *ctx )
+{
+ int iRDN;
+ int rc = LDAP_ENCODING_ERROR;
+ ber_len_t len, l;
+
+ /* stringifying helpers for LDAPv3/LDAPv2 */
+ int ( *sv2l ) ( struct berval *v, unsigned f, ber_len_t *l );
+ int ( *sv2s ) ( struct berval *v, char *s, unsigned f, ber_len_t *l );
+
+ assert( bv != NULL );
+ bv->bv_len = 0;
+ bv->bv_val = NULL;
+
+ Debug1( LDAP_DEBUG_ARGS, "=> ldap_dn2bv(%u)\n", flags );
+
+ /*
+ * a null dn means an empty dn string
+ * FIXME: better raise an error?
+ */
+ if ( dn == NULL || dn[0] == NULL ) {
+ bv->bv_val = LDAP_STRDUPX( "", ctx );
+ return( LDAP_SUCCESS );
+ }
+
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAPV3:
+ sv2l = strval2strlen;
+ sv2s = strval2str;
+
+ if( 0 ) {
+ case LDAP_DN_FORMAT_LDAPV2:
+ sv2l = strval2IA5strlen;
+ sv2s = strval2IA5str;
+ }
+
+ for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) {
+ ber_len_t rdnl;
+ if ( rdn2strlen( dn[ iRDN ], flags, &rdnl, sv2l ) ) {
+ goto return_results;
+ }
+
+ len += rdnl;
+ }
+
+ if ( ( bv->bv_val = LDAP_MALLOCX( len + 1, ctx ) ) == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ break;
+ }
+
+ for ( l = 0, iRDN = 0; dn[ iRDN ]; iRDN++ ) {
+ ber_len_t rdnl;
+
+ if ( rdn2str( dn[ iRDN ], &bv->bv_val[ l ], flags,
+ &rdnl, sv2s ) ) {
+ LDAP_FREEX( bv->bv_val, ctx );
+ bv->bv_val = NULL;
+ goto return_results;
+ }
+ l += rdnl;
+ }
+
+ assert( l == len );
+
+ /*
+ * trim the last ',' (the allocated memory
+ * is one byte longer than required)
+ */
+ bv->bv_len = len - 1;
+ bv->bv_val[ bv->bv_len ] = '\0';
+
+ rc = LDAP_SUCCESS;
+ break;
+
+ case LDAP_DN_FORMAT_UFN: {
+ /*
+ * FIXME: quoting from RFC 1781:
+ *
+ To take a distinguished name, and generate a name of this format with
+ attribute types omitted, the following steps are followed.
+
+ 1. If the first attribute is of type CommonName, the type may be
+ omitted.
+
+ 2. If the last attribute is of type Country, the type may be
+ omitted.
+
+ 3. If the last attribute is of type Country, the last
+ Organisation attribute may have the type omitted.
+
+ 4. All attributes of type OrganisationalUnit may have the type
+ omitted, unless they are after an Organisation attribute or
+ the first attribute is of type OrganisationalUnit.
+
+ * this should be the pedantic implementation.
+ *
+ * Here the standard implementation reflects
+ * the one historically provided by OpenLDAP
+ * (and UMIch, I presume), with the variant
+ * of spaces and plusses (' + ') separating
+ * rdn components.
+ *
+ * A non-standard but nice implementation could
+ * be to turn the final "dc" attributes into a
+ * dot-separated domain.
+ *
+ * Other improvements could involve the use of
+ * friendly country names and so.
+ */
+#ifdef DC_IN_UFN
+ int leftmost_dc = -1;
+ int last_iRDN = -1;
+#endif /* DC_IN_UFN */
+
+ for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) {
+ ber_len_t rdnl;
+
+ if ( rdn2UFNstrlen( dn[ iRDN ], flags, &rdnl ) ) {
+ goto return_results;
+ }
+ len += rdnl;
+
+#ifdef DC_IN_UFN
+ if ( LDAP_DN_IS_RDN_DC( dn[ iRDN ] ) ) {
+ if ( leftmost_dc == -1 ) {
+ leftmost_dc = iRDN;
+ }
+ } else {
+ leftmost_dc = -1;
+ }
+#endif /* DC_IN_UFN */
+ }
+
+ if ( ( bv->bv_val = LDAP_MALLOCX( len + 1, ctx ) ) == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ break;
+ }
+
+#ifdef DC_IN_UFN
+ if ( leftmost_dc == -1 ) {
+#endif /* DC_IN_UFN */
+ for ( l = 0, iRDN = 0; dn[ iRDN ]; iRDN++ ) {
+ ber_len_t vl;
+
+ if ( rdn2UFNstr( dn[ iRDN ], &bv->bv_val[ l ],
+ flags, &vl ) ) {
+ LDAP_FREEX( bv->bv_val, ctx );
+ bv->bv_val = NULL;
+ goto return_results;
+ }
+ l += vl;
+ }
+
+ /*
+ * trim the last ', ' (the allocated memory
+ * is two bytes longer than required)
+ */
+ bv->bv_len = len - 2;
+ bv->bv_val[ bv->bv_len ] = '\0';
+#ifdef DC_IN_UFN
+ } else {
+ last_iRDN = iRDN - 1;
+
+ for ( l = 0, iRDN = 0; iRDN < leftmost_dc; iRDN++ ) {
+ ber_len_t vl;
+
+ if ( rdn2UFNstr( dn[ iRDN ], &bv->bv_val[ l ],
+ flags, &vl ) ) {
+ LDAP_FREEX( bv->bv_val, ctx );
+ bv->bv_val = NULL;
+ goto return_results;
+ }
+ l += vl;
+ }
+
+ if ( !dn2domain( dn, bv, l, &last_iRDN ) ) {
+ LDAP_FREEX( bv->bv_val, ctx );
+ bv->bv_val = NULL;
+ goto return_results;
+ }
+
+ /* the string is correctly terminated by dn2domain */
+ }
+#endif /* DC_IN_UFN */
+
+ rc = LDAP_SUCCESS;
+
+ } break;
+
+ case LDAP_DN_FORMAT_DCE:
+ for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) {
+ ber_len_t rdnl;
+ if ( rdn2DCEstrlen( dn[ iRDN ], flags, &rdnl ) ) {
+ goto return_results;
+ }
+
+ len += rdnl;
+ }
+
+ if ( ( bv->bv_val = LDAP_MALLOCX( len + 1, ctx ) ) == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ break;
+ }
+
+ for ( l = 0; iRDN--; ) {
+ ber_len_t rdnl;
+
+ if ( rdn2DCEstr( dn[ iRDN ], &bv->bv_val[ l ], flags,
+ &rdnl, 0 ) ) {
+ LDAP_FREEX( bv->bv_val, ctx );
+ bv->bv_val = NULL;
+ goto return_results;
+ }
+ l += rdnl;
+ }
+
+ assert( l == len );
+
+ bv->bv_len = len;
+ bv->bv_val[ bv->bv_len ] = '\0';
+
+ rc = LDAP_SUCCESS;
+ break;
+
+ case LDAP_DN_FORMAT_AD_CANONICAL: {
+ int trailing_slash = 1;
+
+ /*
+ * Sort of UFN for DCE DNs: a slash ('/') separated
+ * global->local DN with no types; strictly speaking,
+ * the naming context should be a domain, which is
+ * written in DNS-style, e.g. dot-separated.
+ *
+ * Example:
+ *
+ * "givenName=Bill+sn=Gates,ou=People,dc=microsoft,dc=com"
+ *
+ * will read
+ *
+ * "microsoft.com/People/Bill,Gates"
+ */
+ for ( iRDN = 0, len = -1; dn[ iRDN ]; iRDN++ ) {
+ ber_len_t rdnl;
+
+ if ( rdn2ADstrlen( dn[ iRDN ], flags, &rdnl ) ) {
+ goto return_results;
+ }
+
+ len += rdnl;
+ }
+
+ /* reserve room for trailing '/' in case the DN
+ * is exactly a domain */
+ if ( ( bv->bv_val = LDAP_MALLOCX( len + 1 + 1, ctx ) ) == NULL )
+ {
+ rc = LDAP_NO_MEMORY;
+ break;
+ }
+
+ iRDN--;
+ if ( iRDN && dn2domain( dn, bv, 0, &iRDN ) != 0 ) {
+ for ( l = bv->bv_len; iRDN >= 0 ; iRDN-- ) {
+ ber_len_t rdnl;
+
+ trailing_slash = 0;
+
+ if ( rdn2ADstr( dn[ iRDN ], &bv->bv_val[ l ],
+ flags, &rdnl, 0 ) ) {
+ LDAP_FREEX( bv->bv_val, ctx );
+ bv->bv_val = NULL;
+ goto return_results;
+ }
+ l += rdnl;
+ }
+
+ } else {
+ int first = 1;
+
+ /*
+ * Strictly speaking, AD canonical requires
+ * a DN to be in the form "..., dc=smtg",
+ * i.e. terminated by a domain component
+ */
+ if ( flags & LDAP_DN_PEDANTIC ) {
+ LDAP_FREEX( bv->bv_val, ctx );
+ bv->bv_val = NULL;
+ rc = LDAP_ENCODING_ERROR;
+ break;
+ }
+
+ for ( l = 0; iRDN >= 0 ; iRDN-- ) {
+ ber_len_t rdnl;
+
+ if ( rdn2ADstr( dn[ iRDN ], &bv->bv_val[ l ],
+ flags, &rdnl, first ) ) {
+ LDAP_FREEX( bv->bv_val, ctx );
+ bv->bv_val = NULL;
+ goto return_results;
+ }
+ if ( first ) {
+ first = 0;
+ }
+ l += rdnl;
+ }
+ }
+
+ if ( trailing_slash ) {
+ /* the DN is exactly a domain -- need a trailing
+ * slash; room was reserved in advance */
+ bv->bv_val[ len ] = '/';
+ len++;
+ }
+
+ bv->bv_len = len;
+ bv->bv_val[ bv->bv_len ] = '\0';
+
+ rc = LDAP_SUCCESS;
+ } break;
+
+ default:
+ return LDAP_PARAM_ERROR;
+ }
+
+ Debug3( LDAP_DEBUG_ARGS, "<= ldap_dn2bv(%s)=%d %s\n",
+ bv->bv_val, rc, rc ? ldap_err2string( rc ) : "" );
+
+return_results:;
+ return( rc );
+}
+
diff --git a/libraries/libldap/getentry.c b/libraries/libldap/getentry.c
new file mode 100644
index 0000000..b812189
--- /dev/null
+++ b/libraries/libldap/getentry.c
@@ -0,0 +1,124 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/* ARGSUSED */
+LDAPMessage *
+ldap_first_entry( LDAP *ld, LDAPMessage *chain )
+{
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( chain != NULL );
+
+ return chain->lm_msgtype == LDAP_RES_SEARCH_ENTRY
+ ? chain
+ : ldap_next_entry( ld, chain );
+}
+
+LDAPMessage *
+ldap_next_entry( LDAP *ld, LDAPMessage *entry )
+{
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( entry != NULL );
+
+ for(
+ entry = entry->lm_chain;
+ entry != NULL;
+ entry = entry->lm_chain )
+ {
+ if( entry->lm_msgtype == LDAP_RES_SEARCH_ENTRY ) {
+ return( entry );
+ }
+ }
+
+ return( NULL );
+}
+
+int
+ldap_count_entries( LDAP *ld, LDAPMessage *chain )
+{
+ int i;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ for ( i = 0; chain != NULL; chain = chain->lm_chain ) {
+ if( chain->lm_msgtype == LDAP_RES_SEARCH_ENTRY ) {
+ i++;
+ }
+ }
+
+ return( i );
+}
+
+int
+ldap_get_entry_controls(
+ LDAP *ld,
+ LDAPMessage *entry,
+ LDAPControl ***sctrls )
+{
+ int rc;
+ BerElement be;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( entry != NULL );
+ assert( sctrls != NULL );
+
+ if ( entry->lm_msgtype != LDAP_RES_SEARCH_ENTRY ) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ /* make a local copy of the BerElement */
+ AC_MEMCPY(&be, entry->lm_ber, sizeof(be));
+
+ if ( ber_scanf( &be, "{xx" /*}*/ ) == LBER_ERROR ) {
+ rc = LDAP_DECODING_ERROR;
+ goto cleanup_and_return;
+ }
+
+ rc = ldap_pvt_get_controls( &be, sctrls );
+
+cleanup_and_return:
+ if( rc != LDAP_SUCCESS ) {
+ ld->ld_errno = rc;
+
+ if( ld->ld_matched != NULL ) {
+ LDAP_FREE( ld->ld_matched );
+ ld->ld_matched = NULL;
+ }
+
+ if( ld->ld_error != NULL ) {
+ LDAP_FREE( ld->ld_error );
+ ld->ld_error = NULL;
+ }
+ }
+
+ return rc;
+}
diff --git a/libraries/libldap/getvalues.c b/libraries/libldap/getvalues.c
new file mode 100644
index 0000000..b3ac190
--- /dev/null
+++ b/libraries/libldap/getvalues.c
@@ -0,0 +1,211 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+char **
+ldap_get_values( LDAP *ld, LDAPMessage *entry, LDAP_CONST char *target )
+{
+ BerElement ber;
+ char *attr;
+ int found = 0;
+ char **vals;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( entry != NULL );
+ assert( target != NULL );
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_get_values\n" );
+
+ ber = *entry->lm_ber;
+
+ /* skip sequence, dn, sequence of, and snag the first attr */
+ if ( ber_scanf( &ber, "{x{{a" /*}}}*/, &attr ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return( NULL );
+ }
+
+ if ( strcasecmp( target, attr ) == 0 )
+ found = 1;
+
+ /* break out on success, return out on error */
+ while ( ! found ) {
+ LDAP_FREE(attr);
+ attr = NULL;
+
+ if ( ber_scanf( &ber, /*{*/ "x}{a" /*}*/, &attr ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return( NULL );
+ }
+
+ if ( strcasecmp( target, attr ) == 0 )
+ break;
+
+ }
+
+ LDAP_FREE(attr);
+ attr = NULL;
+
+ /*
+ * if we get this far, we've found the attribute and are sitting
+ * just before the set of values.
+ */
+
+ if ( ber_scanf( &ber, "[v]", &vals ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return( NULL );
+ }
+
+ return( vals );
+}
+
+struct berval **
+ldap_get_values_len( LDAP *ld, LDAPMessage *entry, LDAP_CONST char *target )
+{
+ BerElement ber;
+ char *attr;
+ int found = 0;
+ struct berval **vals;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( entry != NULL );
+ assert( target != NULL );
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_get_values_len\n" );
+
+ ber = *entry->lm_ber;
+
+ /* skip sequence, dn, sequence of, and snag the first attr */
+ if ( ber_scanf( &ber, "{x{{a" /* }}} */, &attr ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return( NULL );
+ }
+
+ if ( strcasecmp( target, attr ) == 0 )
+ found = 1;
+
+ /* break out on success, return out on error */
+ while ( ! found ) {
+ LDAP_FREE( attr );
+ attr = NULL;
+
+ if ( ber_scanf( &ber, /*{*/ "x}{a" /*}*/, &attr ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return( NULL );
+ }
+
+ if ( strcasecmp( target, attr ) == 0 )
+ break;
+ }
+
+ LDAP_FREE( attr );
+ attr = NULL;
+
+ /*
+ * if we get this far, we've found the attribute and are sitting
+ * just before the set of values.
+ */
+
+ if ( ber_scanf( &ber, "[V]", &vals ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return( NULL );
+ }
+
+ return( vals );
+}
+
+int
+ldap_count_values( char **vals )
+{
+ int i;
+
+ if ( vals == NULL )
+ return( 0 );
+
+ for ( i = 0; vals[i] != NULL; i++ )
+ ; /* NULL */
+
+ return( i );
+}
+
+int
+ldap_count_values_len( struct berval **vals )
+{
+ return( ldap_count_values( (char **) vals ) );
+}
+
+void
+ldap_value_free( char **vals )
+{
+ LDAP_VFREE( vals );
+}
+
+void
+ldap_value_free_len( struct berval **vals )
+{
+ ber_bvecfree( vals );
+}
+
+char **
+ldap_value_dup( char *const *vals )
+{
+ char **new;
+ int i;
+
+ if( vals == NULL ) {
+ return NULL;
+ }
+
+ for( i=0; vals[i]; i++ ) {
+ ; /* Count the number of values */
+ }
+
+ if( i == 0 ) {
+ return NULL;
+ }
+
+ new = LDAP_MALLOC( (i+1)*sizeof(char *) ); /* Alloc array of pointers */
+ if( new == NULL ) {
+ return NULL;
+ }
+
+ for( i=0; vals[i]; i++ ) {
+ new[i] = LDAP_STRDUP( vals[i] ); /* Dup each value */
+ if( new[i] == NULL ) {
+ LDAP_VFREE( new );
+ return NULL;
+ }
+ }
+ new[i] = NULL;
+
+ return new;
+}
+
diff --git a/libraries/libldap/init.c b/libraries/libldap/init.c
new file mode 100644
index 0000000..a2409d7
--- /dev/null
+++ b/libraries/libldap/init.c
@@ -0,0 +1,782 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#ifdef HAVE_GETEUID
+#include <ac/unistd.h>
+#endif
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/time.h>
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include "ldap-int.h"
+#include "ldap_defaults.h"
+#include "lutil.h"
+
+struct ldapoptions ldap_int_global_options =
+ { LDAP_UNINITIALIZED, LDAP_DEBUG_NONE
+ LDAP_LDO_NULLARG
+ LDAP_LDO_SOURCEIP_NULLARG
+ LDAP_LDO_CONNECTIONLESS_NULLARG
+ LDAP_LDO_TLS_NULLARG
+ LDAP_LDO_SASL_NULLARG
+ LDAP_LDO_MUTEX_NULLARG };
+
+#define ATTR_NONE 0
+#define ATTR_BOOL 1
+#define ATTR_INT 2
+#define ATTR_KV 3
+#define ATTR_STRING 4
+#define ATTR_OPTION 5
+
+#define ATTR_SASL 6
+#define ATTR_TLS 7
+
+#define ATTR_OPT_TV 8
+#define ATTR_OPT_INT 9
+
+struct ol_keyvalue {
+ const char * key;
+ int value;
+};
+
+static const struct ol_keyvalue deref_kv[] = {
+ {"never", LDAP_DEREF_NEVER},
+ {"searching", LDAP_DEREF_SEARCHING},
+ {"finding", LDAP_DEREF_FINDING},
+ {"always", LDAP_DEREF_ALWAYS},
+ {NULL, 0}
+};
+
+static const struct ol_attribute {
+ int useronly;
+ int type;
+ const char * name;
+ const void * data;
+ size_t offset;
+} attrs[] = {
+ {0, ATTR_OPT_TV, "TIMEOUT", NULL, LDAP_OPT_TIMEOUT},
+ {0, ATTR_OPT_TV, "NETWORK_TIMEOUT", NULL, LDAP_OPT_NETWORK_TIMEOUT},
+ {0, ATTR_OPT_INT, "VERSION", NULL, LDAP_OPT_PROTOCOL_VERSION},
+ {0, ATTR_KV, "DEREF", deref_kv, /* or &deref_kv[0] */
+ offsetof(struct ldapoptions, ldo_deref)},
+ {0, ATTR_INT, "SIZELIMIT", NULL,
+ offsetof(struct ldapoptions, ldo_sizelimit)},
+ {0, ATTR_INT, "TIMELIMIT", NULL,
+ offsetof(struct ldapoptions, ldo_timelimit)},
+ {1, ATTR_STRING, "BINDDN", NULL,
+ offsetof(struct ldapoptions, ldo_defbinddn)},
+ {0, ATTR_STRING, "BASE", NULL,
+ offsetof(struct ldapoptions, ldo_defbase)},
+ {0, ATTR_INT, "PORT", NULL, /* deprecated */
+ offsetof(struct ldapoptions, ldo_defport)},
+ {0, ATTR_OPTION, "HOST", NULL, LDAP_OPT_HOST_NAME}, /* deprecated */
+ {0, ATTR_OPTION, "URI", NULL, LDAP_OPT_URI}, /* replaces HOST/PORT */
+ {0, ATTR_OPTION, "SOCKET_BIND_ADDRESSES", NULL, LDAP_OPT_SOCKET_BIND_ADDRESSES},
+ {0, ATTR_BOOL, "REFERRALS", NULL, LDAP_BOOL_REFERRALS},
+ {0, ATTR_INT, "KEEPALIVE_IDLE", NULL, LDAP_OPT_X_KEEPALIVE_IDLE},
+ {0, ATTR_INT, "KEEPALIVE_PROBES", NULL, LDAP_OPT_X_KEEPALIVE_PROBES},
+ {0, ATTR_INT, "KEEPALIVE_INTERVAL", NULL, LDAP_OPT_X_KEEPALIVE_INTERVAL},
+
+#if 0
+ /* This should only be allowed via ldap_set_option(3) */
+ {0, ATTR_BOOL, "RESTART", NULL, LDAP_BOOL_RESTART},
+#endif
+
+#ifdef HAVE_CYRUS_SASL
+ {0, ATTR_STRING, "SASL_MECH", NULL,
+ offsetof(struct ldapoptions, ldo_def_sasl_mech)},
+ {0, ATTR_STRING, "SASL_REALM", NULL,
+ offsetof(struct ldapoptions, ldo_def_sasl_realm)},
+ {1, ATTR_STRING, "SASL_AUTHCID", NULL,
+ offsetof(struct ldapoptions, ldo_def_sasl_authcid)},
+ {1, ATTR_STRING, "SASL_AUTHZID", NULL,
+ offsetof(struct ldapoptions, ldo_def_sasl_authzid)},
+ {0, ATTR_SASL, "SASL_SECPROPS", NULL, LDAP_OPT_X_SASL_SECPROPS},
+ {0, ATTR_BOOL, "SASL_NOCANON", NULL, LDAP_BOOL_SASL_NOCANON},
+ {0, ATTR_SASL, "SASL_CBINDING", NULL, LDAP_OPT_X_SASL_CBINDING},
+#endif
+
+#ifdef HAVE_TLS
+ {1, ATTR_TLS, "TLS_CERT", NULL, LDAP_OPT_X_TLS_CERTFILE},
+ {1, ATTR_TLS, "TLS_KEY", NULL, LDAP_OPT_X_TLS_KEYFILE},
+ {0, ATTR_TLS, "TLS_CACERT", NULL, LDAP_OPT_X_TLS_CACERTFILE},
+ {0, ATTR_TLS, "TLS_CACERTDIR", NULL, LDAP_OPT_X_TLS_CACERTDIR},
+ {0, ATTR_TLS, "TLS_REQCERT", NULL, LDAP_OPT_X_TLS_REQUIRE_CERT},
+ {0, ATTR_TLS, "TLS_REQSAN", NULL, LDAP_OPT_X_TLS_REQUIRE_SAN},
+ {0, ATTR_TLS, "TLS_RANDFILE", NULL, LDAP_OPT_X_TLS_RANDOM_FILE},
+ {0, ATTR_TLS, "TLS_CIPHER_SUITE", NULL, LDAP_OPT_X_TLS_CIPHER_SUITE},
+ {0, ATTR_TLS, "TLS_PROTOCOL_MIN", NULL, LDAP_OPT_X_TLS_PROTOCOL_MIN},
+ {0, ATTR_TLS, "TLS_PROTOCOL_MAX", NULL, LDAP_OPT_X_TLS_PROTOCOL_MAX},
+ {0, ATTR_TLS, "TLS_PEERKEY_HASH", NULL, LDAP_OPT_X_TLS_PEERKEY_HASH},
+ {0, ATTR_TLS, "TLS_ECNAME", NULL, LDAP_OPT_X_TLS_ECNAME},
+
+#ifdef HAVE_OPENSSL
+ {0, ATTR_TLS, "TLS_CRLCHECK", NULL, LDAP_OPT_X_TLS_CRLCHECK},
+#endif
+#ifdef HAVE_GNUTLS
+ {0, ATTR_TLS, "TLS_CRLFILE", NULL, LDAP_OPT_X_TLS_CRLFILE},
+#endif
+
+#endif
+
+ {0, ATTR_NONE, NULL, NULL, 0}
+};
+
+#define MAX_LDAP_ATTR_LEN sizeof("SOCKET_BIND_ADDRESSES")
+#define MAX_LDAP_ENV_PREFIX_LEN 8
+
+static int
+ldap_int_conf_option(
+ struct ldapoptions *gopts,
+ char *cmd, char *opt, int userconf )
+{
+ int i;
+
+ for(i=0; attrs[i].type != ATTR_NONE; i++) {
+ void *p;
+
+ if( !userconf && attrs[i].useronly ) {
+ continue;
+ }
+
+ if(strcasecmp(cmd, attrs[i].name) != 0) {
+ continue;
+ }
+
+ switch(attrs[i].type) {
+ case ATTR_BOOL:
+ if((strcasecmp(opt, "on") == 0)
+ || (strcasecmp(opt, "yes") == 0)
+ || (strcasecmp(opt, "true") == 0))
+ {
+ LDAP_BOOL_SET(gopts, attrs[i].offset);
+
+ } else {
+ LDAP_BOOL_CLR(gopts, attrs[i].offset);
+ }
+
+ break;
+
+ case ATTR_INT: {
+ char *next;
+ long l;
+ p = &((char *) gopts)[attrs[i].offset];
+ l = strtol( opt, &next, 10 );
+ if ( next != opt && next[ 0 ] == '\0' ) {
+ * (int*) p = l;
+ }
+ } break;
+
+ case ATTR_KV: {
+ const struct ol_keyvalue *kv;
+
+ for(kv = attrs[i].data;
+ kv->key != NULL;
+ kv++) {
+
+ if(strcasecmp(opt, kv->key) == 0) {
+ p = &((char *) gopts)[attrs[i].offset];
+ * (int*) p = kv->value;
+ break;
+ }
+ }
+ } break;
+
+ case ATTR_STRING:
+ p = &((char *) gopts)[attrs[i].offset];
+ if (* (char**) p != NULL) LDAP_FREE(* (char**) p);
+ * (char**) p = LDAP_STRDUP(opt);
+ break;
+ case ATTR_OPTION:
+ ldap_set_option( NULL, attrs[i].offset, opt );
+ break;
+ case ATTR_SASL:
+#ifdef HAVE_CYRUS_SASL
+ ldap_int_sasl_config( gopts, attrs[i].offset, opt );
+#endif
+ break;
+ case ATTR_TLS:
+#ifdef HAVE_TLS
+ ldap_pvt_tls_config( NULL, attrs[i].offset, opt );
+#endif
+ break;
+ case ATTR_OPT_TV: {
+ struct timeval tv;
+ char *next;
+ tv.tv_usec = 0;
+ tv.tv_sec = strtol( opt, &next, 10 );
+ if ( next != opt && next[ 0 ] == '\0' && tv.tv_sec > 0 ) {
+ (void)ldap_set_option( NULL, attrs[i].offset, (const void *)&tv );
+ }
+ } break;
+ case ATTR_OPT_INT: {
+ long l;
+ char *next;
+ l = strtol( opt, &next, 10 );
+ if ( next != opt && next[ 0 ] == '\0' && l > 0 && (long)((int)l) == l ) {
+ int v = (int)l;
+ (void)ldap_set_option( NULL, attrs[i].offset, (const void *)&v );
+ }
+ } break;
+ }
+
+ break;
+ }
+
+ if ( attrs[i].type == ATTR_NONE ) {
+ Debug1( LDAP_DEBUG_TRACE, "ldap_pvt_tls_config: "
+ "unknown option '%s'",
+ cmd );
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+ldap_pvt_conf_option(
+ char *cmd, char *opt, int userconf )
+{
+ struct ldapoptions *gopts;
+ int rc = LDAP_OPT_ERROR;
+
+ /* Get pointer to global option structure */
+ gopts = LDAP_INT_GLOBAL_OPT();
+ if (NULL == gopts) {
+ return LDAP_NO_MEMORY;
+ }
+
+ if ( gopts->ldo_valid != LDAP_INITIALIZED ) {
+ ldap_int_initialize(gopts, NULL);
+ if ( gopts->ldo_valid != LDAP_INITIALIZED )
+ return LDAP_LOCAL_ERROR;
+ }
+
+ return ldap_int_conf_option( gopts, cmd, opt, userconf );
+}
+
+static void openldap_ldap_init_w_conf(
+ const char *file, int userconf )
+{
+ char linebuf[ AC_LINE_MAX ];
+ FILE *fp;
+ int i;
+ char *cmd, *opt;
+ char *start, *end;
+ struct ldapoptions *gopts;
+
+ if ((gopts = LDAP_INT_GLOBAL_OPT()) == NULL) {
+ return; /* Could not allocate mem for global options */
+ }
+
+ if (file == NULL) {
+ /* no file name */
+ return;
+ }
+
+ Debug1(LDAP_DEBUG_TRACE, "ldap_init: trying %s\n", file );
+
+ fp = fopen(file, "r");
+ if(fp == NULL) {
+ /* could not open file */
+ return;
+ }
+
+ Debug1(LDAP_DEBUG_TRACE, "ldap_init: using %s\n", file );
+
+ while((start = fgets(linebuf, sizeof(linebuf), fp)) != NULL) {
+ /* skip lines starting with '#' */
+ if(*start == '#') continue;
+
+ /* trim leading white space */
+ while((*start != '\0') && isspace((unsigned char) *start))
+ start++;
+
+ /* anything left? */
+ if(*start == '\0') continue;
+
+ /* trim trailing white space */
+ end = &start[strlen(start)-1];
+ while(isspace((unsigned char)*end)) end--;
+ end[1] = '\0';
+
+ /* anything left? */
+ if(*start == '\0') continue;
+
+
+ /* parse the command */
+ cmd=start;
+ while((*start != '\0') && !isspace((unsigned char)*start)) {
+ start++;
+ }
+ if(*start == '\0') {
+ /* command has no argument */
+ continue;
+ }
+
+ *start++ = '\0';
+
+ /* we must have some whitespace to skip */
+ while(isspace((unsigned char)*start)) start++;
+ opt = start;
+
+ ldap_int_conf_option( gopts, cmd, opt, userconf );
+ }
+
+ fclose(fp);
+}
+
+static void openldap_ldap_init_w_sysconf(const char *file)
+{
+ openldap_ldap_init_w_conf( file, 0 );
+}
+
+static void openldap_ldap_init_w_userconf(const char *file)
+{
+ char *home;
+ char *path = NULL;
+
+ if (file == NULL) {
+ /* no file name */
+ return;
+ }
+
+ home = getenv("HOME");
+
+ if (home != NULL) {
+ Debug1(LDAP_DEBUG_TRACE, "ldap_init: HOME env is %s\n",
+ home );
+ path = LDAP_MALLOC(strlen(home) + strlen(file) + sizeof( LDAP_DIRSEP "."));
+ } else {
+ Debug0(LDAP_DEBUG_TRACE, "ldap_init: HOME env is NULL\n" );
+ }
+
+ if(home != NULL && path != NULL) {
+ /* we assume UNIX path syntax is used... */
+
+ /* try ~/file */
+ sprintf(path, "%s" LDAP_DIRSEP "%s", home, file);
+ openldap_ldap_init_w_conf(path, 1);
+
+ /* try ~/.file */
+ sprintf(path, "%s" LDAP_DIRSEP ".%s", home, file);
+ openldap_ldap_init_w_conf(path, 1);
+ }
+
+ if(path != NULL) {
+ LDAP_FREE(path);
+ }
+
+ /* try file */
+ openldap_ldap_init_w_conf(file, 1);
+}
+
+static void openldap_ldap_init_w_env(
+ struct ldapoptions *gopts,
+ const char *prefix)
+{
+ char buf[MAX_LDAP_ATTR_LEN+MAX_LDAP_ENV_PREFIX_LEN];
+ int len;
+ int i;
+ void *p;
+ char *value;
+
+ if (prefix == NULL) {
+ prefix = LDAP_ENV_PREFIX;
+ }
+
+ strncpy(buf, prefix, MAX_LDAP_ENV_PREFIX_LEN);
+ buf[MAX_LDAP_ENV_PREFIX_LEN] = '\0';
+ len = strlen(buf);
+
+ for(i=0; attrs[i].type != ATTR_NONE; i++) {
+ strcpy(&buf[len], attrs[i].name);
+ value = getenv(buf);
+
+ if(value == NULL) {
+ continue;
+ }
+
+ switch(attrs[i].type) {
+ case ATTR_BOOL:
+ if((strcasecmp(value, "on") == 0)
+ || (strcasecmp(value, "yes") == 0)
+ || (strcasecmp(value, "true") == 0))
+ {
+ LDAP_BOOL_SET(gopts, attrs[i].offset);
+
+ } else {
+ LDAP_BOOL_CLR(gopts, attrs[i].offset);
+ }
+ break;
+
+ case ATTR_INT:
+ p = &((char *) gopts)[attrs[i].offset];
+ * (int*) p = atoi(value);
+ break;
+
+ case ATTR_KV: {
+ const struct ol_keyvalue *kv;
+
+ for(kv = attrs[i].data;
+ kv->key != NULL;
+ kv++) {
+
+ if(strcasecmp(value, kv->key) == 0) {
+ p = &((char *) gopts)[attrs[i].offset];
+ * (int*) p = kv->value;
+ break;
+ }
+ }
+ } break;
+
+ case ATTR_STRING:
+ p = &((char *) gopts)[attrs[i].offset];
+ if (* (char**) p != NULL) LDAP_FREE(* (char**) p);
+ if (*value == '\0') {
+ * (char**) p = NULL;
+ } else {
+ * (char**) p = LDAP_STRDUP(value);
+ }
+ break;
+ case ATTR_OPTION:
+ ldap_set_option( NULL, attrs[i].offset, value );
+ break;
+ case ATTR_SASL:
+#ifdef HAVE_CYRUS_SASL
+ ldap_int_sasl_config( gopts, attrs[i].offset, value );
+#endif
+ break;
+ case ATTR_TLS:
+#ifdef HAVE_TLS
+ ldap_pvt_tls_config( NULL, attrs[i].offset, value );
+#endif
+ break;
+ case ATTR_OPT_TV: {
+ struct timeval tv;
+ char *next;
+ tv.tv_usec = 0;
+ tv.tv_sec = strtol( value, &next, 10 );
+ if ( next != value && next[ 0 ] == '\0' && tv.tv_sec > 0 ) {
+ (void)ldap_set_option( NULL, attrs[i].offset, (const void *)&tv );
+ }
+ } break;
+ case ATTR_OPT_INT: {
+ long l;
+ char *next;
+ l = strtol( value, &next, 10 );
+ if ( next != value && next[ 0 ] == '\0' && l > 0 && (long)((int)l) == l ) {
+ int v = (int)l;
+ (void)ldap_set_option( NULL, attrs[i].offset, (const void *)&v );
+ }
+ } break;
+ }
+ }
+}
+
+#if defined(__GNUC__)
+/* Declare this function as a destructor so that it will automatically be
+ * invoked either at program exit (if libldap is a static library) or
+ * at unload time (if libldap is a dynamic library).
+ *
+ * Sorry, don't know how to handle this for non-GCC environments.
+ */
+static void ldap_int_destroy_global_options(void)
+ __attribute__ ((destructor));
+#endif
+
+static void
+ldap_int_destroy_global_options(void)
+{
+ struct ldapoptions *gopts = LDAP_INT_GLOBAL_OPT();
+
+ if ( gopts == NULL )
+ return;
+
+ gopts->ldo_valid = LDAP_UNINITIALIZED;
+
+ if ( gopts->ldo_defludp ) {
+ ldap_free_urllist( gopts->ldo_defludp );
+ gopts->ldo_defludp = NULL;
+ }
+
+ if ( gopts->ldo_local_ip_addrs.local_ip_addrs ) {
+ LDAP_FREE( gopts->ldo_local_ip_addrs.local_ip_addrs );
+ gopts->ldo_local_ip_addrs.local_ip_addrs = NULL;
+ }
+
+#if defined(HAVE_WINSOCK) || defined(HAVE_WINSOCK2)
+ WSACleanup( );
+#endif
+
+#if defined(HAVE_TLS) || defined(HAVE_CYRUS_SASL)
+ if ( ldap_int_hostname ) {
+ LDAP_FREE( ldap_int_hostname );
+ ldap_int_hostname = NULL;
+ }
+#endif
+#ifdef HAVE_CYRUS_SASL
+ if ( gopts->ldo_def_sasl_authcid ) {
+ LDAP_FREE( gopts->ldo_def_sasl_authcid );
+ gopts->ldo_def_sasl_authcid = NULL;
+ }
+#endif
+#ifdef HAVE_TLS
+ ldap_int_tls_destroy( gopts );
+#endif
+}
+
+/*
+ * Initialize the global options structure with default values.
+ */
+void ldap_int_initialize_global_options( struct ldapoptions *gopts, int *dbglvl )
+{
+ if (dbglvl)
+ gopts->ldo_debug = *dbglvl;
+ else
+ gopts->ldo_debug = 0;
+
+ gopts->ldo_version = LDAP_VERSION2;
+ gopts->ldo_deref = LDAP_DEREF_NEVER;
+ gopts->ldo_timelimit = LDAP_NO_LIMIT;
+ gopts->ldo_sizelimit = LDAP_NO_LIMIT;
+
+ gopts->ldo_tm_api.tv_sec = -1;
+ gopts->ldo_tm_net.tv_sec = -1;
+
+ memset( &gopts->ldo_local_ip_addrs, 0,
+ sizeof( gopts->ldo_local_ip_addrs ) );
+
+ /* ldo_defludp will be freed by the termination handler
+ */
+ ldap_url_parselist(&gopts->ldo_defludp, "ldap://localhost/");
+ gopts->ldo_defport = LDAP_PORT;
+#if !defined(__GNUC__) && !defined(PIC)
+ /* Do this only for a static library, and only if we can't
+ * arrange for it to be executed as a library destructor
+ */
+ atexit(ldap_int_destroy_global_options);
+#endif
+
+ gopts->ldo_refhoplimit = LDAP_DEFAULT_REFHOPLIMIT;
+ gopts->ldo_rebind_proc = NULL;
+ gopts->ldo_rebind_params = NULL;
+
+ LDAP_BOOL_ZERO(gopts);
+
+ LDAP_BOOL_SET(gopts, LDAP_BOOL_REFERRALS);
+
+#ifdef LDAP_CONNECTIONLESS
+ gopts->ldo_peer = NULL;
+ gopts->ldo_cldapdn = NULL;
+ gopts->ldo_is_udp = 0;
+#endif
+
+#ifdef HAVE_CYRUS_SASL
+ gopts->ldo_def_sasl_mech = NULL;
+ gopts->ldo_def_sasl_realm = NULL;
+ gopts->ldo_def_sasl_authcid = NULL;
+ gopts->ldo_def_sasl_authzid = NULL;
+
+ memset( &gopts->ldo_sasl_secprops,
+ '\0', sizeof(gopts->ldo_sasl_secprops) );
+
+ gopts->ldo_sasl_secprops.max_ssf = INT_MAX;
+ gopts->ldo_sasl_secprops.maxbufsize = SASL_MAX_BUFF_SIZE;
+ gopts->ldo_sasl_secprops.security_flags =
+ SASL_SEC_NOPLAINTEXT | SASL_SEC_NOANONYMOUS;
+#endif
+
+#ifdef HAVE_TLS
+ gopts->ldo_tls_connect_cb = NULL;
+ gopts->ldo_tls_connect_arg = NULL;
+ gopts->ldo_tls_require_cert = LDAP_OPT_X_TLS_DEMAND;
+ gopts->ldo_tls_require_san = LDAP_OPT_X_TLS_ALLOW;
+#endif
+ gopts->ldo_keepalive_probes = 0;
+ gopts->ldo_keepalive_interval = 0;
+ gopts->ldo_keepalive_idle = 0;
+
+ gopts->ldo_tcp_user_timeout = 0;
+
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_init( &gopts->ldo_mutex );
+#endif
+ gopts->ldo_valid = LDAP_INITIALIZED;
+ return;
+}
+
+#if defined(HAVE_TLS) || defined(HAVE_CYRUS_SASL)
+char * ldap_int_hostname = NULL;
+#endif
+
+#ifdef LDAP_R_COMPILE
+int ldap_int_stackguard;
+#endif
+
+void ldap_int_initialize( struct ldapoptions *gopts, int *dbglvl )
+{
+#ifdef LDAP_R_COMPILE
+ static ldap_pvt_thread_mutex_t init_mutex;
+ LDAP_PVT_MUTEX_FIRSTCREATE( init_mutex );
+
+ LDAP_MUTEX_LOCK( &init_mutex );
+#endif
+ if ( gopts->ldo_valid == LDAP_INITIALIZED ) {
+ /* someone else got here first */
+ goto done;
+ }
+
+ ldap_int_error_init();
+
+ ldap_int_utils_init();
+
+#ifdef HAVE_WINSOCK2
+{ WORD wVersionRequested;
+ WSADATA wsaData;
+
+ wVersionRequested = MAKEWORD( 2, 0 );
+ if ( WSAStartup( wVersionRequested, &wsaData ) != 0 ) {
+ /* Tell the user that we couldn't find a usable */
+ /* WinSock DLL. */
+ goto done;
+ }
+
+ /* Confirm that the WinSock DLL supports 2.0.*/
+ /* Note that if the DLL supports versions greater */
+ /* than 2.0 in addition to 2.0, it will still return */
+ /* 2.0 in wVersion since that is the version we */
+ /* requested. */
+
+ if ( LOBYTE( wsaData.wVersion ) != 2 ||
+ HIBYTE( wsaData.wVersion ) != 0 )
+ {
+ /* Tell the user that we couldn't find a usable */
+ /* WinSock DLL. */
+ WSACleanup( );
+ goto done;
+ }
+} /* The WinSock DLL is acceptable. Proceed. */
+#elif defined(HAVE_WINSOCK)
+{ WSADATA wsaData;
+ if ( WSAStartup( 0x0101, &wsaData ) != 0 ) {
+ goto done;
+ }
+}
+#endif
+
+#if defined(HAVE_TLS) || defined(HAVE_CYRUS_SASL)
+ LDAP_MUTEX_LOCK( &ldap_int_hostname_mutex );
+ {
+ char *name = ldap_int_hostname;
+
+ ldap_int_hostname = ldap_pvt_get_fqdn( name );
+
+ if ( name != NULL && name != ldap_int_hostname ) {
+ LDAP_FREE( name );
+ }
+ }
+ LDAP_MUTEX_UNLOCK( &ldap_int_hostname_mutex );
+#endif
+
+#ifndef HAVE_POLL
+ if ( ldap_int_tblsize == 0 ) ldap_int_ip_init();
+#endif
+
+#ifdef HAVE_CYRUS_SASL
+ if ( ldap_int_sasl_init() != 0 ) {
+ goto done;
+ }
+#endif
+
+ ldap_int_initialize_global_options(gopts, dbglvl);
+
+ if( getenv("LDAPNOINIT") != NULL ) {
+ goto done;
+ }
+
+#ifdef LDAP_R_COMPILE
+ if( getenv("LDAPSTACKGUARD") != NULL ) {
+ ldap_int_stackguard = 1;
+ }
+#endif
+
+#ifdef HAVE_CYRUS_SASL
+ {
+ /* set authentication identity to current user name */
+ char *user = getenv("USER");
+
+ if( user == NULL ) user = getenv("USERNAME");
+ if( user == NULL ) user = getenv("LOGNAME");
+
+ if( user != NULL ) {
+ gopts->ldo_def_sasl_authcid = LDAP_STRDUP( user );
+ }
+ }
+#endif
+
+ openldap_ldap_init_w_sysconf(LDAP_CONF_FILE);
+
+#ifdef HAVE_GETEUID
+ if ( geteuid() != getuid() )
+ goto done;
+#endif
+
+ openldap_ldap_init_w_userconf(LDAP_USERRC_FILE);
+
+ {
+ char *altfile = getenv(LDAP_ENV_PREFIX "CONF");
+
+ if( altfile != NULL ) {
+ Debug2(LDAP_DEBUG_TRACE, "ldap_init: %s env is %s\n",
+ LDAP_ENV_PREFIX "CONF", altfile );
+ openldap_ldap_init_w_sysconf( altfile );
+ }
+ else
+ Debug1(LDAP_DEBUG_TRACE, "ldap_init: %s env is NULL\n",
+ LDAP_ENV_PREFIX "CONF" );
+ }
+
+ {
+ char *altfile = getenv(LDAP_ENV_PREFIX "RC");
+
+ if( altfile != NULL ) {
+ Debug2(LDAP_DEBUG_TRACE, "ldap_init: %s env is %s\n",
+ LDAP_ENV_PREFIX "RC", altfile );
+ openldap_ldap_init_w_userconf( altfile );
+ }
+ else
+ Debug1(LDAP_DEBUG_TRACE, "ldap_init: %s env is NULL\n",
+ LDAP_ENV_PREFIX "RC" );
+ }
+
+ openldap_ldap_init_w_env(gopts, NULL);
+
+done:;
+#ifdef LDAP_R_COMPILE
+ LDAP_MUTEX_UNLOCK( &init_mutex );
+#endif
+}
diff --git a/libraries/libldap/lbase64.c b/libraries/libldap/lbase64.c
new file mode 100644
index 0000000..aa4a622
--- /dev/null
+++ b/libraries/libldap/lbase64.c
@@ -0,0 +1,108 @@
+/* lbase64.c - routines for dealing with base64 strings */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission. This
+ * software is provided ``as is'' without express or implied warranty.
+ */
+/* This work was originally developed by the University of Michigan
+ * and distributed as part of U-MICH LDAP.
+ */
+
+#include "portable.h"
+
+#include "ldap-int.h"
+
+#define RIGHT2 0x03
+#define RIGHT4 0x0f
+
+static const unsigned char b642nib[0x80] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+ 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+ 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+ 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+int
+ldap_int_decode_b64_inplace( struct berval *value )
+{
+ char *p, *end, *byte;
+ char nib;
+
+ byte = value->bv_val;
+ end = value->bv_val + value->bv_len;
+
+ for ( p = value->bv_val, value->bv_len = 0;
+ p < end;
+ p += 4, value->bv_len += 3 )
+ {
+ int i;
+ for ( i = 0; i < 4; i++ ) {
+ if ( p[i] != '=' && (p[i] & 0x80 ||
+ b642nib[ p[i] & 0x7f ] > 0x3f) ) {
+ Debug2( LDAP_DEBUG_ANY,
+ _("ldap_pvt_decode_b64_inplace: invalid base64 encoding"
+ " char (%c) 0x%x\n"), p[i], p[i] );
+ return( -1 );
+ }
+ }
+
+ /* first digit */
+ nib = b642nib[ p[0] & 0x7f ];
+ byte[0] = nib << 2;
+ /* second digit */
+ nib = b642nib[ p[1] & 0x7f ];
+ byte[0] |= nib >> 4;
+ byte[1] = (nib & RIGHT4) << 4;
+ /* third digit */
+ if ( p[2] == '=' ) {
+ value->bv_len += 1;
+ break;
+ }
+ nib = b642nib[ p[2] & 0x7f ];
+ byte[1] |= nib >> 2;
+ byte[2] = (nib & RIGHT2) << 6;
+ /* fourth digit */
+ if ( p[3] == '=' ) {
+ value->bv_len += 2;
+ break;
+ }
+ nib = b642nib[ p[3] & 0x7f ];
+ byte[2] |= nib;
+
+ byte += 3;
+ }
+ value->bv_val[ value->bv_len ] = '\0';
+
+ return LDAP_SUCCESS;
+}
diff --git a/libraries/libldap/ldap-int.h b/libraries/libldap/ldap-int.h
new file mode 100644
index 0000000..a401f27
--- /dev/null
+++ b/libraries/libldap/ldap-int.h
@@ -0,0 +1,925 @@
+/* ldap-int.h - defines & prototypes internal to the LDAP library */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#ifndef _LDAP_INT_H
+#define _LDAP_INT_H 1
+
+#ifndef NO_THREADS
+#define LDAP_R_COMPILE 1
+#endif
+
+#include "../liblber/lber-int.h"
+#include "lutil.h"
+#include "ldap_avl.h"
+
+#ifdef LDAP_R_COMPILE
+#include <ldap_pvt_thread.h>
+#endif
+
+#ifdef HAVE_CYRUS_SASL
+ /* the need for this should be removed */
+#ifdef HAVE_SASL_SASL_H
+#include <sasl/sasl.h>
+#else
+#include <sasl.h>
+#endif
+
+#define SASL_MAX_BUFF_SIZE (0xffffff)
+#define SASL_MIN_BUFF_SIZE 4096
+#endif
+
+/* for struct timeval */
+#include <ac/time.h>
+#include <ac/socket.h>
+
+#undef TV2MILLISEC
+#define TV2MILLISEC(tv) (((tv)->tv_sec * 1000) + ((tv)->tv_usec/1000))
+
+/*
+ * Support needed if the library is running in the kernel
+ */
+#if LDAP_INT_IN_KERNEL
+ /*
+ * Platform specific function to return a pointer to the
+ * process-specific global options.
+ *
+ * This function should perform the following functions:
+ * Allocate and initialize a global options struct on a per process basis
+ * Use callers process identifier to return its global options struct
+ * Note: Deallocate structure when the process exits
+ */
+# define LDAP_INT_GLOBAL_OPT() ldap_int_global_opt()
+ struct ldapoptions *ldap_int_global_opt(void);
+#else
+# define LDAP_INT_GLOBAL_OPT() (&ldap_int_global_options)
+#endif
+
+/* if used from server code, ldap_debug already points elsewhere */
+#ifndef ldap_debug
+#define ldap_debug ((LDAP_INT_GLOBAL_OPT())->ldo_debug)
+#endif /* !ldap_debug */
+
+#define LDAP_INT_DEBUG
+#include "ldap_log.h"
+
+#ifdef LDAP_DEBUG
+
+#define DebugTest( level ) \
+ ( ldap_debug & level )
+
+#define Debug0( level, fmt ) \
+ do { if ( DebugTest( (level) ) ) \
+ ldap_log_printf( NULL, (level), fmt ); \
+ } while ( 0 )
+
+#define Debug1( level, fmt, arg1 ) \
+ do { if ( DebugTest( (level) ) ) \
+ ldap_log_printf( NULL, (level), fmt, arg1 ); \
+ } while ( 0 )
+
+#define Debug2( level, fmt, arg1, arg2 ) \
+ do { if ( DebugTest( (level) ) ) \
+ ldap_log_printf( NULL, (level), fmt, arg1, arg2 ); \
+ } while ( 0 )
+
+#define Debug3( level, fmt, arg1, arg2, arg3 ) \
+ do { if ( DebugTest( (level) ) ) \
+ ldap_log_printf( NULL, (level), fmt, arg1, arg2, arg3 ); \
+ } while ( 0 )
+
+#else
+
+#define DebugTest( level ) (0 == 1)
+#define Debug0( level, fmt ) ((void)0)
+#define Debug1( level, fmt, arg1 ) ((void)0)
+#define Debug2( level, fmt, arg1, arg2 ) ((void)0)
+#define Debug3( level, fmt, arg1, arg2, arg3 ) ((void)0)
+
+#endif /* LDAP_DEBUG */
+
+#define LDAP_DEPRECATED 1
+#include "ldap.h"
+
+#include "ldap_pvt.h"
+
+LDAP_BEGIN_DECL
+
+#define LDAP_URL_PREFIX "ldap://"
+#define LDAP_URL_PREFIX_LEN STRLENOF(LDAP_URL_PREFIX)
+#define PLDAP_URL_PREFIX "pldap://"
+#define PLDAP_URL_PREFIX_LEN STRLENOF(PLDAP_URL_PREFIX)
+#define LDAPS_URL_PREFIX "ldaps://"
+#define LDAPS_URL_PREFIX_LEN STRLENOF(LDAPS_URL_PREFIX)
+#define PLDAPS_URL_PREFIX "pldaps://"
+#define PLDAPS_URL_PREFIX_LEN STRLENOF(PLDAPS_URL_PREFIX)
+#define LDAPI_URL_PREFIX "ldapi://"
+#define LDAPI_URL_PREFIX_LEN STRLENOF(LDAPI_URL_PREFIX)
+#ifdef LDAP_CONNECTIONLESS
+#define LDAPC_URL_PREFIX "cldap://"
+#define LDAPC_URL_PREFIX_LEN STRLENOF(LDAPC_URL_PREFIX)
+#endif
+#define LDAP_URL_URLCOLON "URL:"
+#define LDAP_URL_URLCOLON_LEN STRLENOF(LDAP_URL_URLCOLON)
+
+#define LDAP_REF_STR "Referral:\n"
+#define LDAP_REF_STR_LEN STRLENOF(LDAP_REF_STR)
+#define LDAP_LDAP_REF_STR LDAP_URL_PREFIX
+#define LDAP_LDAP_REF_STR_LEN LDAP_URL_PREFIX_LEN
+
+#define LDAP_DEFAULT_REFHOPLIMIT 5
+
+#define LDAP_BOOL_REFERRALS 0
+#define LDAP_BOOL_RESTART 1
+#define LDAP_BOOL_TLS 3
+#define LDAP_BOOL_CONNECT_ASYNC 4
+#define LDAP_BOOL_SASL_NOCANON 5
+#define LDAP_BOOL_KEEPCONN 6
+
+#define LDAP_BOOLEANS unsigned long
+#define LDAP_BOOL(n) ((LDAP_BOOLEANS)1 << (n))
+#define LDAP_BOOL_GET(lo, bool) \
+ ((lo)->ldo_booleans & LDAP_BOOL(bool) ? -1 : 0)
+#define LDAP_BOOL_SET(lo, bool) ((lo)->ldo_booleans |= LDAP_BOOL(bool))
+#define LDAP_BOOL_CLR(lo, bool) ((lo)->ldo_booleans &= ~LDAP_BOOL(bool))
+#define LDAP_BOOL_ZERO(lo) ((lo)->ldo_booleans = 0)
+
+/*
+ * This structure represents both ldap messages and ldap responses.
+ * These are really the same, except in the case of search responses,
+ * where a response has multiple messages.
+ */
+
+struct ldapmsg {
+ ber_int_t lm_msgid; /* the message id */
+ ber_tag_t lm_msgtype; /* the message type */
+ BerElement *lm_ber; /* the ber encoded message contents */
+ struct ldapmsg *lm_chain; /* for search - next msg in the resp */
+ struct ldapmsg *lm_chain_tail;
+ struct ldapmsg *lm_next; /* next response */
+ time_t lm_time; /* used to maintain cache */
+};
+
+#ifdef HAVE_TLS
+struct ldaptls {
+ char *lt_certfile;
+ char *lt_keyfile;
+ char *lt_dhfile;
+ char *lt_cacertfile;
+ char *lt_cacertdir;
+ char *lt_ciphersuite;
+ char *lt_crlfile;
+ char *lt_randfile; /* OpenSSL only */
+ char *lt_ecname; /* OpenSSL only */
+ int lt_protocol_min;
+ int lt_protocol_max;
+ struct berval lt_cacert;
+ struct berval lt_cert;
+ struct berval lt_key;
+};
+#endif
+
+typedef struct ldaplist {
+ struct ldaplist *ll_next;
+ void *ll_data;
+} ldaplist;
+
+/*
+ * LDAP Client Source IP structure
+ */
+typedef struct ldapsourceip {
+ char *local_ip_addrs;
+ struct in_addr ip4_addr;
+ unsigned short has_ipv4;
+#ifdef LDAP_PF_INET6
+ struct in6_addr ip6_addr;
+ unsigned short has_ipv6;
+#endif
+} ldapsourceip;
+
+/*
+ * structure representing get/set'able options
+ * which have global defaults.
+ * Protect access to this struct with ldo_mutex
+ * ldap_log.h:ldapoptions_prefix must match the head of this struct.
+ */
+struct ldapoptions {
+ short ldo_valid;
+#define LDAP_UNINITIALIZED 0x0
+#define LDAP_INITIALIZED 0x1
+#define LDAP_VALID_SESSION 0x2
+#define LDAP_TRASHED_SESSION 0xFF
+ int ldo_debug;
+
+ ber_int_t ldo_version;
+ ber_int_t ldo_deref;
+ ber_int_t ldo_timelimit;
+ ber_int_t ldo_sizelimit;
+
+ /* per API call timeout */
+ struct timeval ldo_tm_api;
+ struct timeval ldo_tm_net;
+
+ LDAPURLDesc *ldo_defludp;
+ int ldo_defport;
+ char* ldo_defbase;
+ char* ldo_defbinddn; /* bind dn */
+
+ /*
+ * Per connection tcp-keepalive settings (Linux only,
+ * ignored where unsupported)
+ */
+ ber_int_t ldo_keepalive_idle;
+ ber_int_t ldo_keepalive_probes;
+ ber_int_t ldo_keepalive_interval;
+
+ /*
+ * Per connection tcp user timeout (Linux >= 2.6.37 only,
+ * ignored where unsupported)
+ */
+ ber_uint_t ldo_tcp_user_timeout;
+
+ int ldo_refhoplimit; /* limit on referral nesting */
+
+ /* LDAPv3 server and client controls */
+ LDAPControl **ldo_sctrls;
+ LDAPControl **ldo_cctrls;
+
+ /* LDAP rebind callback function */
+ LDAP_REBIND_PROC *ldo_rebind_proc;
+ void *ldo_rebind_params;
+ LDAP_NEXTREF_PROC *ldo_nextref_proc;
+ void *ldo_nextref_params;
+ LDAP_URLLIST_PROC *ldo_urllist_proc;
+ void *ldo_urllist_params;
+
+ /* LDAP connection callback stack */
+ ldaplist *ldo_conn_cbs;
+
+ LDAP_BOOLEANS ldo_booleans; /* boolean options */
+
+#define LDAP_LDO_NULLARG ,0,0,0,0 ,{0},{0} ,0,0,0,0, 0,0,0,0,0, 0,0, 0,0,0,0,0,0, 0, 0
+
+ /* LDAP user configured bind IPs */
+ struct ldapsourceip ldo_local_ip_addrs;
+
+#ifdef LDAP_PF_INET6
+#define LDAP_LDO_SOURCEIP_NULLARG ,{0,0,0,0,0}
+#else
+#define LDAP_LDO_SOURCEIP_NULLARG ,{0,0,0}
+#endif
+
+#ifdef LDAP_CONNECTIONLESS
+#define LDAP_IS_UDP(ld) ((ld)->ld_options.ldo_is_udp)
+ void* ldo_peer; /* struct sockaddr* */
+ char* ldo_cldapdn;
+ int ldo_is_udp;
+#define LDAP_LDO_CONNECTIONLESS_NULLARG ,0,0,0
+#else
+#define LDAP_LDO_CONNECTIONLESS_NULLARG
+#endif
+
+#ifdef HAVE_TLS
+ /* tls context */
+ void *ldo_tls_ctx;
+ LDAP_TLS_CONNECT_CB *ldo_tls_connect_cb;
+ void* ldo_tls_connect_arg;
+ struct ldaptls ldo_tls_info;
+#define ldo_tls_certfile ldo_tls_info.lt_certfile
+#define ldo_tls_keyfile ldo_tls_info.lt_keyfile
+#define ldo_tls_dhfile ldo_tls_info.lt_dhfile
+#define ldo_tls_ecname ldo_tls_info.lt_ecname
+#define ldo_tls_cacertfile ldo_tls_info.lt_cacertfile
+#define ldo_tls_cacertdir ldo_tls_info.lt_cacertdir
+#define ldo_tls_ciphersuite ldo_tls_info.lt_ciphersuite
+#define ldo_tls_protocol_min ldo_tls_info.lt_protocol_min
+#define ldo_tls_protocol_max ldo_tls_info.lt_protocol_max
+#define ldo_tls_crlfile ldo_tls_info.lt_crlfile
+#define ldo_tls_randfile ldo_tls_info.lt_randfile
+#define ldo_tls_cacert ldo_tls_info.lt_cacert
+#define ldo_tls_cert ldo_tls_info.lt_cert
+#define ldo_tls_key ldo_tls_info.lt_key
+ int ldo_tls_mode;
+ int ldo_tls_require_cert;
+ int ldo_tls_impl;
+ int ldo_tls_crlcheck;
+ int ldo_tls_require_san;
+ char *ldo_tls_pin_hashalg;
+ struct berval ldo_tls_pin;
+#define LDAP_LDO_TLS_NULLARG ,0,0,0,{0,0,0,0,0,0,0,0,0},0,0,0,0,0,0,{0,0}
+#else
+#define LDAP_LDO_TLS_NULLARG
+#endif
+
+#ifdef HAVE_CYRUS_SASL
+ char* ldo_def_sasl_mech; /* SASL Mechanism(s) */
+ char* ldo_def_sasl_realm; /* SASL realm */
+ char* ldo_def_sasl_authcid; /* SASL authentication identity */
+ char* ldo_def_sasl_authzid; /* SASL authorization identity */
+
+ /* SASL Security Properties */
+ struct sasl_security_properties ldo_sasl_secprops;
+ int ldo_sasl_cbinding;
+#define LDAP_LDO_SASL_NULLARG ,0,0,0,0,{0},0
+#else
+#define LDAP_LDO_SASL_NULLARG
+#endif
+
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_t ldo_mutex;
+#define LDAP_LDO_MUTEX_NULLARG , LDAP_PVT_MUTEX_NULL
+#else
+#define LDAP_LDO_MUTEX_NULLARG
+#endif
+};
+
+
+/*
+ * structure for representing an LDAP server connection
+ */
+typedef struct ldap_conn {
+ Sockbuf *lconn_sb;
+#ifdef HAVE_CYRUS_SASL
+ void *lconn_sasl_authctx; /* context for bind */
+ void *lconn_sasl_sockctx; /* for security layer */
+ void *lconn_sasl_cbind; /* for channel binding */
+#endif
+ int lconn_refcnt;
+ time_t lconn_created; /* time */
+ time_t lconn_lastused; /* time */
+ int lconn_rebind_inprogress; /* set if rebind in progress */
+ char ***lconn_rebind_queue; /* used if rebind in progress */
+ int lconn_status;
+#define LDAP_CONNST_NEEDSOCKET 1
+#define LDAP_CONNST_CONNECTING 2
+#define LDAP_CONNST_CONNECTED 3
+ LDAPURLDesc *lconn_server;
+ BerElement *lconn_ber; /* ber receiving on this conn. */
+
+ struct ldap_conn *lconn_next;
+} LDAPConn;
+
+
+/*
+ * structure used to track outstanding requests
+ */
+typedef struct ldapreq {
+ ber_int_t lr_msgid; /* the message id */
+ int lr_status; /* status of request */
+#define LDAP_REQST_COMPLETED 0
+#define LDAP_REQST_INPROGRESS 1
+#define LDAP_REQST_CHASINGREFS 2
+#define LDAP_REQST_NOTCONNECTED 3
+#define LDAP_REQST_WRITING 4
+ int lr_refcnt; /* count of references */
+ int lr_outrefcnt; /* count of outstanding referrals */
+ int lr_abandoned; /* the request has been abandoned */
+ ber_int_t lr_origid; /* original request's message id */
+ int lr_parentcnt; /* count of parent requests */
+ ber_tag_t lr_res_msgtype; /* result message type */
+ ber_int_t lr_res_errno; /* result LDAP errno */
+ char *lr_res_error; /* result error string */
+ char *lr_res_matched;/* result matched DN string */
+ BerElement *lr_ber; /* ber encoded request contents */
+ LDAPConn *lr_conn; /* connection used to send request */
+ struct berval lr_dn; /* DN of request, in lr_ber */
+ struct ldapreq *lr_parent; /* request that spawned this referral */
+ struct ldapreq *lr_child; /* first child request */
+ struct ldapreq *lr_refnext; /* next referral spawned */
+ struct ldapreq *lr_prev; /* previous request */
+ struct ldapreq *lr_next; /* next request */
+} LDAPRequest;
+
+/*
+ * structure for client cache
+ */
+#define LDAP_CACHE_BUCKETS 31 /* cache hash table size */
+typedef struct ldapcache {
+ LDAPMessage *lc_buckets[LDAP_CACHE_BUCKETS];/* hash table */
+ LDAPMessage *lc_requests; /* unfulfilled reqs */
+ long lc_timeout; /* request timeout */
+ ber_len_t lc_maxmem; /* memory to use */
+ ber_len_t lc_memused; /* memory in use */
+ int lc_enabled; /* enabled? */
+ unsigned long lc_options; /* options */
+#define LDAP_CACHE_OPT_CACHENOERRS 0x00000001
+#define LDAP_CACHE_OPT_CACHEALLERRS 0x00000002
+} LDAPCache;
+
+/*
+ * structure containing referral request info for rebind procedure
+ */
+typedef struct ldapreqinfo {
+ ber_len_t ri_msgid;
+ int ri_request;
+ char *ri_url;
+} LDAPreqinfo;
+
+/*
+ * structure representing an ldap connection
+ */
+
+struct ldap_common {
+ Sockbuf *ldc_sb; /* socket descriptor & buffer */
+#define ld_sb ldc->ldc_sb
+
+ unsigned short ldc_lberoptions;
+#define ld_lberoptions ldc->ldc_lberoptions
+
+ /* protected by msgid_mutex */
+ ber_len_t ldc_msgid;
+#define ld_msgid ldc->ldc_msgid
+
+ /* do not mess with these */
+ /* protected by req_mutex */
+ TAvlnode *ldc_requests; /* list of outstanding requests */
+ /* protected by res_mutex */
+ LDAPMessage *ldc_responses; /* list of outstanding responses */
+#define ld_requests ldc->ldc_requests
+#define ld_responses ldc->ldc_responses
+
+ /* protected by abandon_mutex */
+ ber_len_t ldc_nabandoned;
+ ber_int_t *ldc_abandoned; /* array of abandoned requests */
+#define ld_nabandoned ldc->ldc_nabandoned
+#define ld_abandoned ldc->ldc_abandoned
+
+ /* unused by libldap */
+ LDAPCache *ldc_cache; /* non-null if cache is initialized */
+#define ld_cache ldc->ldc_cache
+
+ /* do not mess with the rest though */
+
+ /* protected by conn_mutex */
+ LDAPConn *ldc_defconn; /* default connection */
+#define ld_defconn ldc->ldc_defconn
+ LDAPConn *ldc_conns; /* list of server connections */
+#define ld_conns ldc->ldc_conns
+ void *ldc_selectinfo;/* platform specifics for select */
+#define ld_selectinfo ldc->ldc_selectinfo
+
+ /* ldap_common refcnt - free only if 0 */
+ /* protected by ldc_mutex */
+ unsigned int ldc_refcnt;
+#define ld_ldcrefcnt ldc->ldc_refcnt
+
+ /* protected by ldo_mutex */
+ struct ldapoptions ldc_options;
+#define ld_options ldc->ldc_options
+
+#define ld_valid ld_options.ldo_valid
+#define ld_debug ld_options.ldo_debug
+
+#define ld_deref ld_options.ldo_deref
+#define ld_timelimit ld_options.ldo_timelimit
+#define ld_sizelimit ld_options.ldo_sizelimit
+
+#define ld_defbinddn ld_options.ldo_defbinddn
+#define ld_defbase ld_options.ldo_defbase
+#define ld_defhost ld_options.ldo_defhost
+#define ld_defport ld_options.ldo_defport
+
+#define ld_refhoplimit ld_options.ldo_refhoplimit
+
+#define ld_sctrls ld_options.ldo_sctrls
+#define ld_cctrls ld_options.ldo_cctrls
+#define ld_rebind_proc ld_options.ldo_rebind_proc
+#define ld_rebind_params ld_options.ldo_rebind_params
+#define ld_nextref_proc ld_options.ldo_nextref_proc
+#define ld_nextref_params ld_options.ldo_nextref_params
+#define ld_urllist_proc ld_options.ldo_urllist_proc
+#define ld_urllist_params ld_options.ldo_urllist_params
+
+#define ld_version ld_options.ldo_version
+
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_t ldc_mutex;
+ ldap_pvt_thread_mutex_t ldc_msgid_mutex;
+ ldap_pvt_thread_mutex_t ldc_conn_mutex;
+ ldap_pvt_thread_mutex_t ldc_req_mutex;
+ ldap_pvt_thread_mutex_t ldc_res_mutex;
+ ldap_pvt_thread_mutex_t ldc_abandon_mutex;
+#define ld_ldopts_mutex ld_options.ldo_mutex
+#define ld_ldcmutex ldc->ldc_mutex
+#define ld_msgid_mutex ldc->ldc_msgid_mutex
+#define ld_conn_mutex ldc->ldc_conn_mutex
+#define ld_req_mutex ldc->ldc_req_mutex
+#define ld_res_mutex ldc->ldc_res_mutex
+#define ld_abandon_mutex ldc->ldc_abandon_mutex
+#endif
+};
+
+struct ldap {
+ /* thread shared */
+ struct ldap_common *ldc;
+
+ /* thread specific */
+ ber_int_t ld_errno;
+ char *ld_error;
+ char *ld_matched;
+ char **ld_referrals;
+};
+
+#define LDAP_VALID(ld) ( (ld)->ld_valid == LDAP_VALID_SESSION )
+#define LDAP_TRASHED(ld) ( (ld)->ld_valid == LDAP_TRASHED_SESSION )
+#define LDAP_TRASH(ld) ( (ld)->ld_valid = LDAP_TRASHED_SESSION )
+
+#ifdef LDAP_R_COMPILE
+LDAP_V ( ldap_pvt_thread_mutex_t ) ldap_int_resolv_mutex;
+LDAP_V ( ldap_pvt_thread_mutex_t ) ldap_int_hostname_mutex;
+LDAP_V ( int ) ldap_int_stackguard;
+
+#endif
+
+#ifdef LDAP_R_COMPILE
+#define LDAP_MUTEX_LOCK(mutex) ldap_pvt_thread_mutex_lock( mutex )
+#define LDAP_MUTEX_UNLOCK(mutex) ldap_pvt_thread_mutex_unlock( mutex )
+#define LDAP_ASSERT_MUTEX_OWNER(mutex) \
+ LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER(mutex)
+#else
+#define LDAP_MUTEX_LOCK(mutex) ((void) 0)
+#define LDAP_MUTEX_UNLOCK(mutex) ((void) 0)
+#define LDAP_ASSERT_MUTEX_OWNER(mutex) ((void) 0)
+#endif
+
+#define LDAP_NEXT_MSGID(ld, id) do { \
+ LDAP_MUTEX_LOCK( &(ld)->ld_msgid_mutex ); \
+ (id) = ++(ld)->ld_msgid; \
+ LDAP_MUTEX_UNLOCK( &(ld)->ld_msgid_mutex ); \
+} while (0)
+
+/*
+ * in abandon.c
+ */
+
+LDAP_F (int)
+ldap_int_bisect_find( ber_int_t *v, ber_len_t n, ber_int_t id, int *idxp );
+LDAP_F (int)
+ldap_int_bisect_insert( ber_int_t **vp, ber_len_t *np, int id, int idx );
+LDAP_F (int)
+ldap_int_bisect_delete( ber_int_t **vp, ber_len_t *np, int id, int idx );
+
+/*
+ * in add.c
+ */
+
+LDAP_F (BerElement *) ldap_build_add_req LDAP_P((
+ LDAP *ld,
+ const char *dn,
+ LDAPMod **attrs,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp ));
+
+/*
+ * in lbase64.c
+ */
+
+LDAP_F (int) ldap_int_decode_b64_inplace LDAP_P((
+ struct berval *value ));
+
+/*
+ * in compare.c
+ */
+
+LDAP_F (BerElement *) ldap_build_compare_req LDAP_P((
+ LDAP *ld,
+ const char *dn,
+ const char *attr,
+ struct berval *bvalue,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp ));
+
+/*
+ * in delete.c
+ */
+
+LDAP_F (BerElement *) ldap_build_delete_req LDAP_P((
+ LDAP *ld,
+ const char *dn,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp ));
+
+/*
+ * in extended.c
+ */
+
+LDAP_F (BerElement *) ldap_build_extended_req LDAP_P((
+ LDAP *ld,
+ const char *reqoid,
+ struct berval *reqdata,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp ));
+
+/*
+ * in init.c
+ */
+
+LDAP_V ( struct ldapoptions ) ldap_int_global_options;
+
+LDAP_F ( void ) ldap_int_initialize LDAP_P((struct ldapoptions *, int *));
+LDAP_F ( void ) ldap_int_initialize_global_options LDAP_P((
+ struct ldapoptions *, int *));
+
+/* memory.c */
+ /* simple macros to realloc for now */
+#define LDAP_MALLOC(s) (ber_memalloc_x((s),NULL))
+#define LDAP_CALLOC(n,s) (ber_memcalloc_x((n),(s),NULL))
+#define LDAP_REALLOC(p,s) (ber_memrealloc_x((p),(s),NULL))
+#define LDAP_FREE(p) (ber_memfree_x((p),NULL))
+#define LDAP_VFREE(v) (ber_memvfree_x((void **)(v),NULL))
+#define LDAP_STRDUP(s) (ber_strdup_x((s),NULL))
+#define LDAP_STRNDUP(s,l) (ber_strndup_x((s),(l),NULL))
+
+#define LDAP_MALLOCX(s,x) (ber_memalloc_x((s),(x)))
+#define LDAP_CALLOCX(n,s,x) (ber_memcalloc_x((n),(s),(x)))
+#define LDAP_REALLOCX(p,s,x) (ber_memrealloc_x((p),(s),(x)))
+#define LDAP_FREEX(p,x) (ber_memfree_x((p),(x)))
+#define LDAP_VFREEX(v,x) (ber_memvfree_x((void **)(v),(x)))
+#define LDAP_STRDUPX(s,x) (ber_strdup_x((s),(x)))
+#define LDAP_STRNDUPX(s,l,x) (ber_strndup_x((s),(l),(x)))
+
+/*
+ * in error.c
+ */
+LDAP_F (void) ldap_int_error_init( void );
+
+/*
+ * in modify.c
+ */
+
+LDAP_F (BerElement *) ldap_build_modify_req LDAP_P((
+ LDAP *ld,
+ const char *dn,
+ LDAPMod **mods,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp ));
+
+/*
+ * in modrdn.c
+ */
+
+LDAP_F (BerElement *) ldap_build_moddn_req LDAP_P((
+ LDAP *ld,
+ const char *dn,
+ const char *newrdn,
+ const char *newSuperior,
+ int deleteoldrdn,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp ));
+
+/*
+ * in unit-int.c
+ */
+LDAP_F (void) ldap_int_utils_init LDAP_P(( void ));
+
+
+/*
+ * in print.c
+ */
+LDAP_F (int) ldap_log_printf LDAP_P((LDAP *ld, int level, const char *fmt, ...)) LDAP_GCCATTR((format(printf, 3, 4)));
+
+/*
+ * in controls.c
+ */
+LDAP_F (int) ldap_int_put_controls LDAP_P((
+ LDAP *ld,
+ LDAPControl *const *ctrls,
+ BerElement *ber ));
+
+LDAP_F (int) ldap_int_client_controls LDAP_P((
+ LDAP *ld,
+ LDAPControl **ctrlp ));
+
+/*
+ * in dsparse.c
+ */
+LDAP_F (int) ldap_int_next_line_tokens LDAP_P(( char **bufp, ber_len_t *blenp, char ***toksp ));
+
+
+/*
+ * in open.c
+ */
+LDAP_F (int) ldap_open_defconn( LDAP *ld );
+LDAP_F (int) ldap_int_open_connection( LDAP *ld,
+ LDAPConn *conn, LDAPURLDesc *srvlist, int async );
+LDAP_F (int) ldap_int_check_async_open( LDAP *ld, ber_socket_t sd );
+
+/*
+ * in os-ip.c
+ */
+#ifndef HAVE_POLL
+LDAP_V (int) ldap_int_tblsize;
+LDAP_F (void) ldap_int_ip_init( void );
+#endif
+
+LDAP_F (int) ldap_int_timeval_dup( struct timeval **dest,
+ const struct timeval *tm );
+LDAP_F (int) ldap_connect_to_host( LDAP *ld, Sockbuf *sb,
+ int proto, LDAPURLDesc *srv, int async );
+LDAP_F (int) ldap_int_poll( LDAP *ld, ber_socket_t s,
+ struct timeval *tvp, int wr );
+
+#if defined(HAVE_TLS) || defined(HAVE_CYRUS_SASL)
+LDAP_V (char *) ldap_int_hostname;
+LDAP_F (char *) ldap_host_connected_to( Sockbuf *sb,
+ const char *host );
+#endif
+
+LDAP_F (int) ldap_int_select( LDAP *ld, struct timeval *timeout );
+LDAP_F (void *) ldap_new_select_info( void );
+LDAP_F (void) ldap_free_select_info( void *sip );
+LDAP_F (void) ldap_mark_select_write( LDAP *ld, Sockbuf *sb );
+LDAP_F (void) ldap_mark_select_read( LDAP *ld, Sockbuf *sb );
+LDAP_F (void) ldap_mark_select_clear( LDAP *ld, Sockbuf *sb );
+LDAP_F (void) ldap_clear_select_write( LDAP *ld, Sockbuf *sb );
+LDAP_F (int) ldap_is_read_ready( LDAP *ld, Sockbuf *sb );
+LDAP_F (int) ldap_is_write_ready( LDAP *ld, Sockbuf *sb );
+
+LDAP_F (int) ldap_validate_and_fill_sourceip ( char** source_ip_lst,
+ ldapsourceip* temp_source_ip );
+
+LDAP_F (int) ldap_int_connect_cbs( LDAP *ld, Sockbuf *sb,
+ ber_socket_t *s, LDAPURLDesc *srv, struct sockaddr *addr );
+
+/*
+ * in os-local.c
+ */
+#ifdef LDAP_PF_LOCAL
+LDAP_F (int) ldap_connect_to_path( LDAP *ld, Sockbuf *sb,
+ LDAPURLDesc *srv, int async );
+#endif /* LDAP_PF_LOCAL */
+
+/*
+ * in request.c
+ */
+LDAP_F (ber_int_t) ldap_send_initial_request( LDAP *ld, ber_tag_t msgtype,
+ const char *dn, BerElement *ber, ber_int_t msgid );
+LDAP_F (BerElement *) ldap_alloc_ber_with_options( LDAP *ld );
+LDAP_F (void) ldap_set_ber_options( LDAP *ld, BerElement *ber );
+
+LDAP_F (int) ldap_send_server_request( LDAP *ld, BerElement *ber,
+ ber_int_t msgid, LDAPRequest *parentreq, LDAPURLDesc **srvlist,
+ LDAPConn *lc, LDAPreqinfo *bind, int noconn, int m_res );
+LDAP_F (LDAPConn *) ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist,
+ int use_ldsb, int connect, LDAPreqinfo *bind, int m_req, int m_res );
+LDAP_F (LDAPRequest *) ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid );
+LDAP_F (void) ldap_return_request( LDAP *ld, LDAPRequest *lr, int freeit );
+LDAP_F (int) ldap_req_cmp( const void *l, const void *r );
+LDAP_F (void) ldap_do_free_request( void *arg );
+LDAP_F (void) ldap_free_request( LDAP *ld, LDAPRequest *lr );
+LDAP_F (void) ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind );
+LDAP_F (void) ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all );
+LDAP_F (void) ldap_dump_requests_and_responses( LDAP *ld );
+LDAP_F (int) ldap_chase_referrals( LDAP *ld, LDAPRequest *lr,
+ char **errstrp, int sref, int *hadrefp );
+LDAP_F (int) ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr,
+ char **refs, int sref, char **referralsp, int *hadrefp );
+LDAP_F (int) ldap_append_referral( LDAP *ld, char **referralsp, char *s );
+LDAP_F (int) ldap_int_flush_request( LDAP *ld, LDAPRequest *lr );
+
+/*
+ * in result.c:
+ */
+LDAP_F (const char *) ldap_int_msgtype2str( ber_tag_t tag );
+
+/*
+ * in search.c
+ */
+LDAP_F (BerElement *) ldap_build_search_req LDAP_P((
+ LDAP *ld,
+ const char *base,
+ ber_int_t scope,
+ const char *filter,
+ char **attrs,
+ ber_int_t attrsonly,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t timelimit,
+ ber_int_t sizelimit,
+ ber_int_t deref,
+ ber_int_t *msgidp));
+
+
+/*
+ * in unbind.c
+ */
+LDAP_F (int) ldap_ld_free LDAP_P((
+ LDAP *ld,
+ int close,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls ));
+
+LDAP_F (int) ldap_send_unbind LDAP_P((
+ LDAP *ld,
+ Sockbuf *sb,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls ));
+
+/*
+ * in url.c
+ */
+LDAP_F (LDAPURLDesc *) ldap_url_dup LDAP_P((
+ LDAPURLDesc *ludp ));
+
+LDAP_F (LDAPURLDesc *) ldap_url_duplist LDAP_P((
+ LDAPURLDesc *ludlist ));
+
+LDAP_F (int) ldap_url_parsehosts LDAP_P((
+ LDAPURLDesc **ludlist,
+ const char *hosts,
+ int port ));
+
+LDAP_F (char *) ldap_url_list2hosts LDAP_P((
+ LDAPURLDesc *ludlist ));
+
+/*
+ * in cyrus.c
+ */
+
+LDAP_F (int) ldap_int_sasl_init LDAP_P(( void ));
+
+LDAP_F (int) ldap_int_sasl_open LDAP_P((
+ LDAP *ld, LDAPConn *conn,
+ const char* host ));
+LDAP_F (int) ldap_int_sasl_close LDAP_P(( LDAP *ld, LDAPConn *conn ));
+
+LDAP_F (int) ldap_int_sasl_external LDAP_P((
+ LDAP *ld, LDAPConn *conn,
+ const char* authid, ber_len_t ssf ));
+
+LDAP_F (int) ldap_int_sasl_get_option LDAP_P(( LDAP *ld,
+ int option, void *arg ));
+LDAP_F (int) ldap_int_sasl_set_option LDAP_P(( LDAP *ld,
+ int option, void *arg ));
+LDAP_F (int) ldap_int_sasl_config LDAP_P(( struct ldapoptions *lo,
+ int option, const char *arg ));
+
+LDAP_F (int) ldap_int_sasl_bind LDAP_P((
+ LDAP *ld,
+ const char *,
+ const char *,
+ LDAPControl **, LDAPControl **,
+
+ /* should be passed in client controls */
+ unsigned flags,
+ LDAP_SASL_INTERACT_PROC *interact,
+ void *defaults,
+ LDAPMessage *result,
+ const char **rmech,
+ int *msgid ));
+
+/* in sasl.c */
+
+LDAP_F (BerElement *) ldap_build_bind_req LDAP_P((
+ LDAP *ld,
+ const char *dn,
+ const char *mech,
+ struct berval *cred,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp ));
+
+/* in schema.c */
+LDAP_F (char *) ldap_int_parse_numericoid LDAP_P((
+ const char **sp,
+ int *code,
+ const int flags ));
+
+/*
+ * in tls.c
+ */
+LDAP_F (int) ldap_int_tls_start LDAP_P(( LDAP *ld,
+ LDAPConn *conn, LDAPURLDesc *srv ));
+
+LDAP_F (void) ldap_int_tls_destroy LDAP_P(( struct ldapoptions *lo ));
+
+/*
+ * in getvalues.c
+ */
+LDAP_F (char **) ldap_value_dup LDAP_P((
+ char *const *vals ));
+
+LDAP_END_DECL
+
+#endif /* _LDAP_INT_H */
diff --git a/libraries/libldap/ldap-tls.h b/libraries/libldap/ldap-tls.h
new file mode 100644
index 0000000..ef2a1d8
--- /dev/null
+++ b/libraries/libldap/ldap-tls.h
@@ -0,0 +1,88 @@
+/* ldap-tls.h - TLS defines & prototypes internal to the LDAP library */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _LDAP_TLS_H
+#define _LDAP_TLS_H 1
+
+struct tls_impl;
+
+struct tls_ctx;
+struct tls_session;
+
+typedef struct tls_ctx tls_ctx;
+typedef struct tls_session tls_session;
+
+typedef int (TI_tls_init)(void);
+typedef void (TI_tls_destroy)(void);
+
+typedef tls_ctx *(TI_ctx_new)(struct ldapoptions *lo);
+typedef void (TI_ctx_ref)(tls_ctx *ctx);
+typedef void (TI_ctx_free)(tls_ctx *ctx);
+typedef int (TI_ctx_init)(struct ldapoptions *lo, struct ldaptls *lt, int is_server);
+
+typedef tls_session *(TI_session_new)(tls_ctx *ctx, int is_server);
+typedef int (TI_session_connect)(LDAP *ld, tls_session *s, const char *name_in);
+typedef int (TI_session_accept)(tls_session *s);
+typedef int (TI_session_upflags)(Sockbuf *sb, tls_session *s, int rc);
+typedef char *(TI_session_errmsg)(tls_session *s, int rc, char *buf, size_t len );
+typedef int (TI_session_dn)(tls_session *sess, struct berval *dn);
+typedef int (TI_session_chkhost)(LDAP *ld, tls_session *s, const char *name_in);
+typedef int (TI_session_strength)(tls_session *sess);
+typedef int (TI_session_unique)(tls_session *sess, struct berval *buf, int is_server);
+typedef int (TI_session_endpoint)(tls_session *sess, struct berval *buf, int is_server);
+typedef const char *(TI_session_name)(tls_session *s);
+typedef int (TI_session_peercert)(tls_session *s, struct berval *der);
+typedef int (TI_session_pinning)(LDAP *ld, tls_session *s, char *hashalg, struct berval *hash);
+
+typedef void (TI_thr_init)(void);
+
+typedef struct tls_impl {
+ const char *ti_name;
+
+ TI_tls_init *ti_tls_init; /* library initialization */
+ TI_tls_destroy *ti_tls_destroy;
+
+ TI_ctx_new *ti_ctx_new;
+ TI_ctx_ref *ti_ctx_ref;
+ TI_ctx_free *ti_ctx_free;
+ TI_ctx_init *ti_ctx_init;
+
+ TI_session_new *ti_session_new;
+ TI_session_connect *ti_session_connect;
+ TI_session_accept *ti_session_accept;
+ TI_session_upflags *ti_session_upflags;
+ TI_session_errmsg *ti_session_errmsg;
+ TI_session_dn *ti_session_my_dn;
+ TI_session_dn *ti_session_peer_dn;
+ TI_session_chkhost *ti_session_chkhost;
+ TI_session_strength *ti_session_strength;
+ TI_session_unique *ti_session_unique;
+ TI_session_endpoint *ti_session_endpoint;
+ TI_session_name *ti_session_version;
+ TI_session_name *ti_session_cipher;
+ TI_session_peercert *ti_session_peercert;
+ TI_session_pinning *ti_session_pinning;
+
+ Sockbuf_IO *ti_sbio;
+
+ TI_thr_init *ti_thr_init;
+
+ int ti_inited;
+} tls_impl;
+
+extern tls_impl ldap_int_tls_impl;
+
+#endif /* _LDAP_TLS_H */
diff --git a/libraries/libldap/ldap.conf b/libraries/libldap/ldap.conf
new file mode 100644
index 0000000..af738ad
--- /dev/null
+++ b/libraries/libldap/ldap.conf
@@ -0,0 +1,13 @@
+#
+# LDAP Defaults
+#
+
+# See ldap.conf(5) for details
+# This file should be world readable but not world writable.
+
+#BASE dc=example,dc=com
+#URI ldap://ldap.example.com ldap://ldap-provider.example.com:666
+
+#SIZELIMIT 12
+#TIMELIMIT 15
+#DEREF never
diff --git a/libraries/libldap/ldap.pc.in b/libraries/libldap/ldap.pc.in
new file mode 100644
index 0000000..3f7dd46
--- /dev/null
+++ b/libraries/libldap/ldap.pc.in
@@ -0,0 +1,13 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+includedir=@includedir@
+libdir=@libdir@
+
+Name: ldap (@PACKAGE@)
+Description: OpenLDAP Lightweight Directory Access Protocol library
+URL: https://www.openldap.org
+Version: @VERSION@
+Requires: lber
+Cflags: -I${includedir}
+Libs: -L${libdir} -lldap
+Libs.private: @LIBS@ @SASL_LIBS@ @TLS_LIBS@ @AUTH_LIBS@
diff --git a/libraries/libldap/ldap_sync.c b/libraries/libldap/ldap_sync.c
new file mode 100644
index 0000000..6c99e3a
--- /dev/null
+++ b/libraries/libldap/ldap_sync.c
@@ -0,0 +1,928 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2006-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This program was originally developed by Pierangelo Masarati
+ * for inclusion in OpenLDAP Software.
+ */
+
+/*
+ * Proof-of-concept API that implement the client-side
+ * of the "LDAP Content Sync Operation" (RFC 4533)
+ */
+
+#include "portable.h"
+
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#ifdef LDAP_SYNC_TRACE
+static const char *
+ldap_sync_state2str( int state )
+{
+ switch ( state ) {
+ case LDAP_SYNC_PRESENT:
+ return "LDAP_SYNC_PRESENT";
+
+ case LDAP_SYNC_ADD:
+ return "LDAP_SYNC_ADD";
+
+ case LDAP_SYNC_MODIFY:
+ return "LDAP_SYNC_MODIFY";
+
+ case LDAP_SYNC_DELETE:
+ return "LDAP_SYNC_DELETE";
+
+ default:
+ return "(unknown)";
+ }
+}
+#endif
+
+/*
+ * initialize the persistent search structure
+ */
+ldap_sync_t *
+ldap_sync_initialize( ldap_sync_t *ls_in )
+{
+ ldap_sync_t *ls = ls_in;
+
+ if ( ls == NULL ) {
+ ls = ldap_memalloc( sizeof( ldap_sync_t ) );
+ if ( ls == NULL ) {
+ return NULL;
+ }
+ }
+ memset( ls, 0, sizeof( ldap_sync_t ) );
+
+ ls->ls_scope = LDAP_SCOPE_SUBTREE;
+ ls->ls_timeout = -1;
+
+ return ls;
+}
+
+/*
+ * destroy the persistent search structure
+ */
+void
+ldap_sync_destroy( ldap_sync_t *ls, int freeit )
+{
+ assert( ls != NULL );
+
+ if ( ls->ls_base != NULL ) {
+ ldap_memfree( ls->ls_base );
+ ls->ls_base = NULL;
+ }
+
+ if ( ls->ls_filter != NULL ) {
+ ldap_memfree( ls->ls_filter );
+ ls->ls_filter = NULL;
+ }
+
+ if ( ls->ls_attrs != NULL ) {
+ int i;
+
+ for ( i = 0; ls->ls_attrs[ i ] != NULL; i++ ) {
+ ldap_memfree( ls->ls_attrs[ i ] );
+ }
+ ldap_memfree( ls->ls_attrs );
+ ls->ls_attrs = NULL;
+ }
+
+ if ( ls->ls_ld != NULL ) {
+ (void)ldap_unbind_ext( ls->ls_ld, NULL, NULL );
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "ldap_unbind_ext()\n" );
+#endif /* LDAP_SYNC_TRACE */
+ ls->ls_ld = NULL;
+ }
+
+ if ( ls->ls_cookie.bv_val != NULL ) {
+ ldap_memfree( ls->ls_cookie.bv_val );
+ ls->ls_cookie.bv_val = NULL;
+ }
+
+ if ( freeit ) {
+ ldap_memfree( ls );
+ }
+}
+
+/*
+ * handle the LDAP_RES_SEARCH_ENTRY response
+ */
+static int
+ldap_sync_search_entry( ldap_sync_t *ls, LDAPMessage *res )
+{
+ LDAPControl **ctrls = NULL;
+ int rc = LDAP_OTHER,
+ i;
+ BerElement *ber = NULL;
+ struct berval entryUUID = { 0 },
+ cookie = { 0 };
+ int state = -1;
+ ber_len_t len;
+ ldap_sync_refresh_t phase;
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\tgot LDAP_RES_SEARCH_ENTRY\n" );
+#endif /* LDAP_SYNC_TRACE */
+
+ assert( ls != NULL );
+ assert( res != NULL );
+
+ phase = ls->ls_refreshPhase;
+
+ /* OK */
+
+ /* extract:
+ * - data
+ * - entryUUID
+ *
+ * check that:
+ * - Sync State Control is "add"
+ */
+
+ /* the control MUST be present */
+
+ /* extract controls */
+ ldap_get_entry_controls( ls->ls_ld, res, &ctrls );
+ if ( ctrls == NULL ) {
+ goto done;
+ }
+
+ /* lookup the sync state control */
+ for ( i = 0; ctrls[ i ] != NULL; i++ ) {
+ if ( strcmp( ctrls[ i ]->ldctl_oid, LDAP_CONTROL_SYNC_STATE ) == 0 ) {
+ break;
+ }
+ }
+
+ /* control must be present; there might be other... */
+ if ( ctrls[ i ] == NULL ) {
+ goto done;
+ }
+
+ /* extract data */
+ ber = ber_init( &ctrls[ i ]->ldctl_value );
+ if ( ber == NULL ) {
+ goto done;
+ }
+ /* scan entryUUID in-place ("m") */
+ if ( ber_scanf( ber, "{em" /*"}"*/, &state, &entryUUID ) == LBER_ERROR
+ || entryUUID.bv_len == 0 )
+ {
+ goto done;
+ }
+
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
+ /* scan cookie in-place ("m") */
+ if ( ber_scanf( ber, /*"{"*/ "m}", &cookie ) == LBER_ERROR ) {
+ goto done;
+ }
+ if ( cookie.bv_val != NULL ) {
+ ber_bvreplace( &ls->ls_cookie, &cookie );
+ }
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot cookie=%s\n",
+ cookie.bv_val ? cookie.bv_val : "(null)" );
+#endif /* LDAP_SYNC_TRACE */
+ }
+
+ switch ( state ) {
+ case LDAP_SYNC_PRESENT:
+ case LDAP_SYNC_DELETE:
+ case LDAP_SYNC_ADD:
+ case LDAP_SYNC_MODIFY:
+ /* NOTE: ldap_sync_refresh_t is defined
+ * as the corresponding LDAP_SYNC_*
+ * for the 4 above cases */
+ phase = state;
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot syncState=%s\n", ldap_sync_state2str( state ) );
+#endif /* LDAP_SYNC_TRACE */
+ break;
+
+ default:
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot unknown syncState=%d\n", state );
+#endif /* LDAP_SYNC_TRACE */
+ goto done;
+ }
+
+ rc = ls->ls_search_entry
+ ? ls->ls_search_entry( ls, res, &entryUUID, phase )
+ : LDAP_SUCCESS;
+
+done:;
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ if ( ctrls != NULL ) {
+ ldap_controls_free( ctrls );
+ }
+
+ return rc;
+}
+
+/*
+ * handle the LDAP_RES_SEARCH_REFERENCE response
+ * (to be implemented yet)
+ */
+static int
+ldap_sync_search_reference( ldap_sync_t *ls, LDAPMessage *res )
+{
+ int rc = 0;
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\tgot LDAP_RES_SEARCH_REFERENCE\n" );
+#endif /* LDAP_SYNC_TRACE */
+
+ assert( ls != NULL );
+ assert( res != NULL );
+
+ if ( ls->ls_search_reference ) {
+ rc = ls->ls_search_reference( ls, res );
+ }
+
+ return rc;
+}
+
+/*
+ * handle the LDAP_RES_SEARCH_RESULT response
+ */
+static int
+ldap_sync_search_result( ldap_sync_t *ls, LDAPMessage *res )
+{
+ int err;
+ char *matched = NULL,
+ *msg = NULL;
+ LDAPControl **ctrls = NULL;
+ int rc;
+ int refreshDeletes = -1;
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\tgot LDAP_RES_SEARCH_RESULT\n" );
+#endif /* LDAP_SYNC_TRACE */
+
+ assert( ls != NULL );
+ assert( res != NULL );
+
+ /* should not happen in refreshAndPersist... */
+ rc = ldap_parse_result( ls->ls_ld,
+ res, &err, &matched, &msg, NULL, &ctrls, 0 );
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr,
+ "\tldap_parse_result(%d, \"%s\", \"%s\") == %d\n",
+ err,
+ matched ? matched : "",
+ msg ? msg : "",
+ rc );
+#endif /* LDAP_SYNC_TRACE */
+ if ( rc == LDAP_SUCCESS ) {
+ rc = err;
+ }
+
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE;
+
+ switch ( rc ) {
+ case LDAP_SUCCESS: {
+ int i;
+ BerElement *ber = NULL;
+ ber_len_t len;
+ struct berval cookie = { 0 };
+
+ rc = LDAP_OTHER;
+
+ /* deal with control; then fallthru to handler */
+ if ( ctrls == NULL ) {
+ goto done;
+ }
+
+ /* lookup the sync state control */
+ for ( i = 0; ctrls[ i ] != NULL; i++ ) {
+ if ( strcmp( ctrls[ i ]->ldctl_oid,
+ LDAP_CONTROL_SYNC_DONE ) == 0 )
+ {
+ break;
+ }
+ }
+
+ /* control must be present; there might be other... */
+ if ( ctrls[ i ] == NULL ) {
+ goto done;
+ }
+
+ /* extract data */
+ ber = ber_init( &ctrls[ i ]->ldctl_value );
+ if ( ber == NULL ) {
+ goto done;
+ }
+
+ if ( ber_scanf( ber, "{" /*"}"*/) == LBER_ERROR ) {
+ goto ber_done;
+ }
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
+ if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) {
+ goto ber_done;
+ }
+ if ( cookie.bv_val != NULL ) {
+ ber_bvreplace( &ls->ls_cookie, &cookie );
+ }
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot cookie=%s\n",
+ cookie.bv_val ? cookie.bv_val : "(null)" );
+#endif /* LDAP_SYNC_TRACE */
+ }
+
+ refreshDeletes = 0;
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES ) {
+ if ( ber_scanf( ber, "b", &refreshDeletes ) == LBER_ERROR ) {
+ goto ber_done;
+ }
+ if ( refreshDeletes ) {
+ refreshDeletes = 1;
+ }
+ }
+
+ if ( ber_scanf( ber, /*"{"*/ "}" ) != LBER_ERROR ) {
+ rc = LDAP_SUCCESS;
+ }
+
+ ber_done:;
+ ber_free( ber, 1 );
+ if ( rc != LDAP_SUCCESS ) {
+ break;
+ }
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot refreshDeletes=%s\n",
+ refreshDeletes ? "TRUE" : "FALSE" );
+#endif /* LDAP_SYNC_TRACE */
+
+ /* FIXME: what should we do with the refreshDelete? */
+ switch ( refreshDeletes ) {
+ case 0:
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_PRESENTS;
+ break;
+
+ default:
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_DELETES;
+ break;
+ }
+
+ } /* fallthru */
+
+ case LDAP_SYNC_REFRESH_REQUIRED:
+ /* TODO: check for Sync Done Control */
+ /* FIXME: perhaps the handler should be called
+ * also in case of failure; we'll deal with this
+ * later when implementing refreshOnly */
+ if ( ls->ls_search_result ) {
+ err = ls->ls_search_result( ls, res, refreshDeletes );
+ }
+ break;
+ }
+
+done:;
+ if ( matched != NULL ) {
+ ldap_memfree( matched );
+ }
+
+ if ( msg != NULL ) {
+ ldap_memfree( msg );
+ }
+
+ if ( ctrls != NULL ) {
+ ldap_controls_free( ctrls );
+ }
+
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE;
+
+ return rc;
+}
+
+/*
+ * handle the LDAP_RES_INTERMEDIATE response
+ */
+static int
+ldap_sync_search_intermediate( ldap_sync_t *ls, LDAPMessage *res, int *refreshDone )
+{
+ int rc;
+ char *retoid = NULL;
+ struct berval *retdata = NULL;
+ BerElement *ber = NULL;
+ ber_len_t len;
+ ber_tag_t syncinfo_tag;
+ struct berval cookie;
+ int refreshDeletes = 0;
+ BerVarray syncUUIDs = NULL;
+ ldap_sync_refresh_t phase;
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\tgot LDAP_RES_INTERMEDIATE\n" );
+#endif /* LDAP_SYNC_TRACE */
+
+ assert( ls != NULL );
+ assert( res != NULL );
+ assert( refreshDone != NULL );
+
+ *refreshDone = 0;
+
+ rc = ldap_parse_intermediate( ls->ls_ld, res,
+ &retoid, &retdata, NULL, 0 );
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t%sldap_parse_intermediate(%s) == %d\n",
+ rc != LDAP_SUCCESS ? "!!! " : "",
+ retoid == NULL ? "\"\"" : retoid,
+ rc );
+#endif /* LDAP_SYNC_TRACE */
+ /* parsing must be successful, and yield the OID
+ * of the sync info intermediate response */
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ rc = LDAP_OTHER;
+
+ if ( retoid == NULL || strcmp( retoid, LDAP_SYNC_INFO ) != 0 ) {
+ goto done;
+ }
+
+ /* init ber using the value in the response */
+ ber = ber_init( retdata );
+ if ( ber == NULL ) {
+ goto done;
+ }
+
+ syncinfo_tag = ber_peek_tag( ber, &len );
+ switch ( syncinfo_tag ) {
+ case LDAP_TAG_SYNC_NEW_COOKIE:
+ if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) {
+ goto done;
+ }
+ if ( cookie.bv_val != NULL ) {
+ ber_bvreplace( &ls->ls_cookie, &cookie );
+ }
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot cookie=%s\n",
+ cookie.bv_val ? cookie.bv_val : "(null)" );
+#endif /* LDAP_SYNC_TRACE */
+ break;
+
+ case LDAP_TAG_SYNC_REFRESH_DELETE:
+ case LDAP_TAG_SYNC_REFRESH_PRESENT:
+ if ( syncinfo_tag == LDAP_TAG_SYNC_REFRESH_DELETE ) {
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot refreshDelete\n" );
+#endif /* LDAP_SYNC_TRACE */
+ switch ( ls->ls_refreshPhase ) {
+ case LDAP_SYNC_CAPI_NONE:
+ case LDAP_SYNC_CAPI_PRESENTS:
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_DELETES;
+ break;
+
+ default:
+ /* TODO: impossible; handle */
+ goto done;
+ }
+
+ } else {
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot refreshPresent\n" );
+#endif /* LDAP_SYNC_TRACE */
+ switch ( ls->ls_refreshPhase ) {
+ case LDAP_SYNC_CAPI_NONE:
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_PRESENTS;
+ break;
+
+ default:
+ /* TODO: impossible; handle */
+ goto done;
+ }
+ }
+
+ if ( ber_scanf( ber, "{" /*"}"*/ ) == LBER_ERROR ) {
+ goto done;
+ }
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
+ if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) {
+ goto done;
+ }
+ if ( cookie.bv_val != NULL ) {
+ ber_bvreplace( &ls->ls_cookie, &cookie );
+ }
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot cookie=%s\n",
+ cookie.bv_val ? cookie.bv_val : "(null)" );
+#endif /* LDAP_SYNC_TRACE */
+ }
+
+ *refreshDone = 1;
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDONE ) {
+ if ( ber_scanf( ber, "b", refreshDone ) == LBER_ERROR ) {
+ goto done;
+ }
+ }
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot refreshDone=%s\n",
+ *refreshDone ? "TRUE" : "FALSE" );
+#endif /* LDAP_SYNC_TRACE */
+
+ if ( ber_scanf( ber, /*"{"*/ "}" ) == LBER_ERROR ) {
+ goto done;
+ }
+
+ if ( *refreshDone ) {
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE;
+ }
+
+ if ( ls->ls_intermediate ) {
+ ls->ls_intermediate( ls, res, NULL, ls->ls_refreshPhase );
+ }
+
+ break;
+
+ case LDAP_TAG_SYNC_ID_SET:
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot syncIdSet\n" );
+#endif /* LDAP_SYNC_TRACE */
+ if ( ber_scanf( ber, "{" /*"}"*/ ) == LBER_ERROR ) {
+ goto done;
+ }
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
+ if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) {
+ goto done;
+ }
+ if ( cookie.bv_val != NULL ) {
+ ber_bvreplace( &ls->ls_cookie, &cookie );
+ }
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot cookie=%s\n",
+ cookie.bv_val ? cookie.bv_val : "(null)" );
+#endif /* LDAP_SYNC_TRACE */
+ }
+
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES ) {
+ if ( ber_scanf( ber, "b", &refreshDeletes ) == LBER_ERROR ) {
+ goto done;
+ }
+ }
+
+ if ( ber_scanf( ber, /*"{"*/ "[W]}", &syncUUIDs ) == LBER_ERROR
+ || syncUUIDs == NULL )
+ {
+ goto done;
+ }
+
+#ifdef LDAP_SYNC_TRACE
+ {
+ int i;
+
+ fprintf( stderr, "\t\tgot refreshDeletes=%s\n",
+ refreshDeletes ? "TRUE" : "FALSE" );
+ for ( i = 0; syncUUIDs[ i ].bv_val != NULL; i++ ) {
+ char buf[ BUFSIZ ];
+ fprintf( stderr, "\t\t%s\n",
+ lutil_uuidstr_from_normalized(
+ syncUUIDs[ i ].bv_val, syncUUIDs[ i ].bv_len,
+ buf, sizeof( buf ) ) );
+ }
+ }
+#endif /* LDAP_SYNC_TRACE */
+
+ if ( refreshDeletes ) {
+ phase = LDAP_SYNC_CAPI_DELETES_IDSET;
+
+ } else {
+ phase = LDAP_SYNC_CAPI_PRESENTS_IDSET;
+ }
+
+ /* FIXME: should touch ls->ls_refreshPhase? */
+ if ( ls->ls_intermediate ) {
+ ls->ls_intermediate( ls, res, syncUUIDs, phase );
+ }
+
+ ber_bvarray_free( syncUUIDs );
+ break;
+
+ default:
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tunknown tag!\n" );
+#endif /* LDAP_SYNC_TRACE */
+ goto done;
+ }
+
+ rc = LDAP_SUCCESS;
+
+done:;
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ if ( retoid != NULL ) {
+ ldap_memfree( retoid );
+ }
+
+ if ( retdata != NULL ) {
+ ber_bvfree( retdata );
+ }
+
+ return rc;
+}
+
+/*
+ * initialize the sync
+ */
+int
+ldap_sync_init( ldap_sync_t *ls, int mode )
+{
+ LDAPControl ctrl = { 0 },
+ *ctrls[ 2 ];
+ BerElement *ber = NULL;
+ int rc;
+ struct timeval tv = { 0 },
+ *tvp = NULL;
+ LDAPMessage *res = NULL;
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "ldap_sync_init(%s)...\n",
+ mode == LDAP_SYNC_REFRESH_AND_PERSIST ?
+ "LDAP_SYNC_REFRESH_AND_PERSIST" :
+ ( mode == LDAP_SYNC_REFRESH_ONLY ?
+ "LDAP_SYNC_REFRESH_ONLY" : "unknown" ) );
+#endif /* LDAP_SYNC_TRACE */
+
+ assert( ls != NULL );
+ assert( ls->ls_ld != NULL );
+
+ /* support both refreshOnly and refreshAndPersist */
+ switch ( mode ) {
+ case LDAP_SYNC_REFRESH_AND_PERSIST:
+ case LDAP_SYNC_REFRESH_ONLY:
+ break;
+
+ default:
+ fprintf( stderr, "ldap_sync_init: unknown mode=%d\n", mode );
+ return LDAP_PARAM_ERROR;
+ }
+
+ /* check consistency of cookie and reloadHint at initial refresh */
+ if ( ls->ls_cookie.bv_val == NULL && ls->ls_reloadHint != 0 ) {
+ fprintf( stderr, "ldap_sync_init: inconsistent cookie/rhint\n" );
+ return LDAP_PARAM_ERROR;
+ }
+
+ ctrls[ 0 ] = &ctrl;
+ ctrls[ 1 ] = NULL;
+
+ /* prepare the Sync Request control */
+ ber = ber_alloc_t( LBER_USE_DER );
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "%sber_alloc_t() %s= NULL\n",
+ ber == NULL ? "!!! " : "",
+ ber == NULL ? "=" : "!" );
+#endif /* LDAP_SYNC_TRACE */
+ if ( ber == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto done;
+ }
+
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_NONE;
+
+ if ( ls->ls_cookie.bv_val != NULL ) {
+ ber_printf( ber, "{eOb}", mode,
+ &ls->ls_cookie, ls->ls_reloadHint );
+
+ } else {
+ ber_printf( ber, "{eb}", mode, ls->ls_reloadHint );
+ }
+
+ rc = ber_flatten2( ber, &ctrl.ldctl_value, 0 );
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr,
+ "%sber_flatten2() == %d\n",
+ rc ? "!!! " : "",
+ rc );
+#endif /* LDAP_SYNC_TRACE */
+ if ( rc < 0 ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ /* make the control critical, as we cannot proceed without */
+ ctrl.ldctl_oid = LDAP_CONTROL_SYNC;
+ ctrl.ldctl_iscritical = 1;
+
+ /* timelimit? */
+ if ( ls->ls_timelimit ) {
+ tv.tv_sec = ls->ls_timelimit;
+ tvp = &tv;
+ }
+
+ /* actually run the search */
+ rc = ldap_search_ext( ls->ls_ld,
+ ls->ls_base, ls->ls_scope, ls->ls_filter,
+ ls->ls_attrs, 0, ctrls, NULL,
+ tvp, ls->ls_sizelimit, &ls->ls_msgid );
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr,
+ "%sldap_search_ext(\"%s\", %d, \"%s\") == %d\n",
+ rc ? "!!! " : "",
+ ls->ls_base, ls->ls_scope, ls->ls_filter, rc );
+#endif /* LDAP_SYNC_TRACE */
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ /* initial content/content update phase */
+ for ( ; ; ) {
+ LDAPMessage *msg = NULL;
+
+ /* NOTE: this very short timeout is just to let
+ * ldap_result() yield long enough to get something */
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+
+ rc = ldap_result( ls->ls_ld, ls->ls_msgid,
+ LDAP_MSG_RECEIVED, &tv, &res );
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr,
+ "\t%sldap_result(%d) == %d\n",
+ rc == -1 ? "!!! " : "",
+ ls->ls_msgid, rc );
+#endif /* LDAP_SYNC_TRACE */
+ switch ( rc ) {
+ case 0:
+ /*
+ * timeout
+ *
+ * TODO: can do something else in the meanwhile)
+ */
+ break;
+
+ case -1:
+ /* smtg bad! */
+ goto done;
+
+ default:
+ for ( msg = ldap_first_message( ls->ls_ld, res );
+ msg != NULL;
+ msg = ldap_next_message( ls->ls_ld, msg ) )
+ {
+ int refreshDone;
+
+ switch ( ldap_msgtype( msg ) ) {
+ case LDAP_RES_SEARCH_ENTRY:
+ rc = ldap_sync_search_entry( ls, res );
+ break;
+
+ case LDAP_RES_SEARCH_REFERENCE:
+ rc = ldap_sync_search_reference( ls, res );
+ break;
+
+ case LDAP_RES_SEARCH_RESULT:
+ rc = ldap_sync_search_result( ls, res );
+ goto done_search;
+
+ case LDAP_RES_INTERMEDIATE:
+ rc = ldap_sync_search_intermediate( ls, res, &refreshDone );
+ if ( rc != LDAP_SUCCESS || refreshDone ) {
+ goto done_search;
+ }
+ break;
+
+ default:
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\tgot something unexpected...\n" );
+#endif /* LDAP_SYNC_TRACE */
+
+ ldap_msgfree( res );
+
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ }
+ ldap_msgfree( res );
+ res = NULL;
+ break;
+ }
+ }
+
+done_search:;
+ ldap_msgfree( res );
+
+done:;
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ return rc;
+}
+
+/*
+ * initialize the refreshOnly sync
+ */
+int
+ldap_sync_init_refresh_only( ldap_sync_t *ls )
+{
+ return ldap_sync_init( ls, LDAP_SYNC_REFRESH_ONLY );
+}
+
+/*
+ * initialize the refreshAndPersist sync
+ */
+int
+ldap_sync_init_refresh_and_persist( ldap_sync_t *ls )
+{
+ return ldap_sync_init( ls, LDAP_SYNC_REFRESH_AND_PERSIST );
+}
+
+/*
+ * poll for new responses
+ */
+int
+ldap_sync_poll( ldap_sync_t *ls )
+{
+ struct timeval tv,
+ *tvp = NULL;
+ LDAPMessage *res = NULL,
+ *msg;
+ int rc = 0;
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "ldap_sync_poll...\n" );
+#endif /* LDAP_SYNC_TRACE */
+
+ assert( ls != NULL );
+ assert( ls->ls_ld != NULL );
+
+ if ( ls->ls_timeout != -1 ) {
+ tv.tv_sec = ls->ls_timeout;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ }
+
+ rc = ldap_result( ls->ls_ld, ls->ls_msgid,
+ LDAP_MSG_RECEIVED, tvp, &res );
+ if ( rc <= 0 ) {
+ return rc;
+ }
+
+ for ( msg = ldap_first_message( ls->ls_ld, res );
+ msg;
+ msg = ldap_next_message( ls->ls_ld, msg ) )
+ {
+ int refreshDone;
+
+ switch ( ldap_msgtype( msg ) ) {
+ case LDAP_RES_SEARCH_ENTRY:
+ rc = ldap_sync_search_entry( ls, res );
+ break;
+
+ case LDAP_RES_SEARCH_REFERENCE:
+ rc = ldap_sync_search_reference( ls, res );
+ break;
+
+ case LDAP_RES_SEARCH_RESULT:
+ rc = ldap_sync_search_result( ls, res );
+ goto done_search;
+
+ case LDAP_RES_INTERMEDIATE:
+ rc = ldap_sync_search_intermediate( ls, res, &refreshDone );
+ if ( rc != LDAP_SUCCESS || refreshDone ) {
+ goto done_search;
+ }
+ break;
+
+ default:
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\tgot something unexpected...\n" );
+#endif /* LDAP_SYNC_TRACE */
+
+ ldap_msgfree( res );
+
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ }
+
+done_search:;
+ ldap_msgfree( res );
+
+done:;
+ return rc;
+}
diff --git a/libraries/libldap/ldap_thr_debug.h b/libraries/libldap/ldap_thr_debug.h
new file mode 100644
index 0000000..db6a052
--- /dev/null
+++ b/libraries/libldap/ldap_thr_debug.h
@@ -0,0 +1,197 @@
+/* ldap_thr_debug.h - preprocessor magic for LDAP_THREAD_DEBUG */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifdef LDAP_THREAD_DEBUG
+
+/*
+ * libldap .c files should include this file after ldap_pvt_thread.h,
+ * with the appropriate LDAP_THREAD*_IMPLEMENTATION macro(s) defined.
+ */
+
+#ifndef _LDAP_PVT_THREAD_H
+#error "ldap_pvt_thread.h" must be included before "ldap_thr_debug.h"
+#endif
+
+/*
+ * Support for thr_debug.c:
+ *
+ * thr_debug.c defines ldap_pvt_thread_* as wrappers around the real
+ * ldap_pvt_thread_* implementation, which this file renames to
+ * ldap_int_thread_*.
+ *
+ * Implementation:
+ *
+ * This file re#defines selected ldap_pvt_thread_* names to
+ * ldap_int_thread_*, which will be used from wrappers in thr_debug.c.
+ * Two ldap_int_*() calls are redirected to call ldap_debug_*(): These
+ * are wrappers around the originals, whose definitions are not renamed.
+ * This file then #includes ldap_pvt_thread.h to declare the renamed
+ * functions/types. If #included from thr_debug.c it finally #undefines
+ * the macros again.
+ *
+ * include/ldap_pvt_thread.h declares the typedefs ldap_pvt_thread*_t as
+ * either wrapper types ldap_debug_thread*_t or their usual definitions
+ * ldap_int_thread*_t, depending on the LDAP_THREAD_DEBUG_WRAP option.
+ * When defining the underlying implementation, this file then redirects
+ * the type names back to the original ldap_int_thread*_t types.
+ * include/ldap_<int,pvt>_thread.h also do some thr_debug magic.
+ *
+ * So,
+ * libldap/<not thr_debug.c> thus define ldap_int_thread_*() instead
+ * of ldap_pvt_thread_*().
+ * thr_debug.c defines the ldap_pvt_*() and ldap_debug_*() functions.
+ * In thread.c, ldap_pvt_thread_<initialize/destroy>() will call
+ * ldap_debug_thread_*() instead of ldap_int_thread_*().
+ * In tpool.c, ldap_int_thread_pool_shutdown() has explicit thr_debug.c
+ * support which treats ldap_pvt_thread_pool_destroy() the same way.
+ */
+
+#ifndef LDAP_THREAD_IMPLEMENTATION /* for first part of threads.c */
+#define ldap_int_thread_initialize ldap_debug_thread_initialize
+#define ldap_int_thread_destroy ldap_debug_thread_destroy
+#else /* LDAP_THREAD_IMPLEMENTATION -- for thr_*.c and end of threads.c */
+#undef ldap_int_thread_initialize
+#undef ldap_int_thread_destroy
+#ifdef LDAP_THREAD_DEBUG_WRAP /* see ldap_pvt_thread.h */
+#define ldap_pvt_thread_mutex_t ldap_int_thread_mutex_t
+#define ldap_pvt_thread_cond_t ldap_int_thread_cond_t
+#endif
+#define ldap_pvt_thread_sleep ldap_int_thread_sleep
+#define ldap_pvt_thread_get_concurrency ldap_int_thread_get_concurrency
+#define ldap_pvt_thread_set_concurrency ldap_int_thread_set_concurrency
+#define ldap_pvt_thread_create ldap_int_thread_create
+#define ldap_pvt_thread_exit ldap_int_thread_exit
+#define ldap_pvt_thread_join ldap_int_thread_join
+#define ldap_pvt_thread_kill ldap_int_thread_kill
+#define ldap_pvt_thread_yield ldap_int_thread_yield
+#define ldap_pvt_thread_cond_init ldap_int_thread_cond_init
+#define ldap_pvt_thread_cond_destroy ldap_int_thread_cond_destroy
+#define ldap_pvt_thread_cond_signal ldap_int_thread_cond_signal
+#define ldap_pvt_thread_cond_broadcast ldap_int_thread_cond_broadcast
+#define ldap_pvt_thread_cond_wait ldap_int_thread_cond_wait
+#define ldap_pvt_thread_mutex_init ldap_int_thread_mutex_init
+#define ldap_pvt_thread_mutex_recursive_init ldap_int_thread_mutex_recursive_init
+#define ldap_pvt_thread_mutex_destroy ldap_int_thread_mutex_destroy
+#define ldap_pvt_thread_mutex_lock ldap_int_thread_mutex_lock
+#define ldap_pvt_thread_mutex_trylock ldap_int_thread_mutex_trylock
+#define ldap_pvt_thread_mutex_unlock ldap_int_thread_mutex_unlock
+#define ldap_pvt_thread_self ldap_int_thread_self
+#endif /* LDAP_THREAD_IMPLEMENTATION */
+
+#ifdef LDAP_THREAD_RDWR_IMPLEMENTATION /* rdwr.c, thr_debug.c */
+#ifdef LDAP_THREAD_DEBUG_WRAP /* see ldap_pvt_thread.h */
+#define ldap_pvt_thread_rdwr_t ldap_int_thread_rdwr_t
+#endif
+#define ldap_pvt_thread_rdwr_init ldap_int_thread_rdwr_init
+#define ldap_pvt_thread_rdwr_destroy ldap_int_thread_rdwr_destroy
+#define ldap_pvt_thread_rdwr_rlock ldap_int_thread_rdwr_rlock
+#define ldap_pvt_thread_rdwr_rtrylock ldap_int_thread_rdwr_rtrylock
+#define ldap_pvt_thread_rdwr_runlock ldap_int_thread_rdwr_runlock
+#define ldap_pvt_thread_rdwr_wlock ldap_int_thread_rdwr_wlock
+#define ldap_pvt_thread_rdwr_wtrylock ldap_int_thread_rdwr_wtrylock
+#define ldap_pvt_thread_rdwr_wunlock ldap_int_thread_rdwr_wunlock
+#define ldap_pvt_thread_rdwr_readers ldap_int_thread_rdwr_readers
+#define ldap_pvt_thread_rdwr_writers ldap_int_thread_rdwr_writers
+#define ldap_pvt_thread_rdwr_active ldap_int_thread_rdwr_active
+#endif /* LDAP_THREAD_RDWR_IMPLEMENTATION */
+
+#ifdef LDAP_THREAD_POOL_IMPLEMENTATION /* tpool.c, thr_debug.c */
+#ifdef LDAP_THREAD_DEBUG_WRAP /* see ldap_pvt_thread.h */
+#define ldap_pvt_thread_pool_t ldap_int_thread_pool_t
+#endif
+#define ldap_pvt_thread_pool_init ldap_int_thread_pool_init
+#define ldap_pvt_thread_pool_submit ldap_int_thread_pool_submit
+#define ldap_pvt_thread_pool_maxthreads ldap_int_thread_pool_maxthreads
+#define ldap_pvt_thread_pool_backload ldap_int_thread_pool_backload
+#define ldap_pvt_thread_pool_pause ldap_int_thread_pool_pause
+#define ldap_pvt_thread_pool_resume ldap_int_thread_pool_resume
+#define ldap_pvt_thread_pool_destroy ldap_int_thread_pool_destroy
+#define ldap_pvt_thread_pool_close ldap_int_thread_pool_close
+#define ldap_pvt_thread_pool_free ldap_int_thread_pool_free
+#define ldap_pvt_thread_pool_getkey ldap_int_thread_pool_getkey
+#define ldap_pvt_thread_pool_setkey ldap_int_thread_pool_setkey
+#define ldap_pvt_thread_pool_purgekey ldap_int_thread_pool_purgekey
+#define ldap_pvt_thread_pool_context ldap_int_thread_pool_context
+#define ldap_pvt_thread_pool_context_reset ldap_int_thread_pool_context_reset
+#endif /* LDAP_THREAD_POOL_IMPLEMENTATION */
+
+#undef _LDAP_PVT_THREAD_H
+#include "ldap_pvt_thread.h"
+
+#ifdef LDAP_THREAD_POOL_IMPLEMENTATION /* tpool.c */
+/*
+ * tpool.c:ldap_int_thread_pool_shutdown() needs this. Could not
+ * use it for ldap_pvt_thread.h above because of its use of LDAP_P().
+ */
+#undef ldap_pvt_thread_pool_destroy
+#define ldap_pvt_thread_pool_destroy(p,r) ldap_int_thread_pool_destroy(p,r)
+#endif
+
+#ifdef LDAP_THREAD_DEBUG_IMPLEMENTATION /* thr_debug.c */
+#undef ldap_pvt_thread_mutex_t
+#undef ldap_pvt_thread_cond_t
+#undef ldap_pvt_thread_sleep
+#undef ldap_pvt_thread_get_concurrency
+#undef ldap_pvt_thread_set_concurrency
+#undef ldap_pvt_thread_create
+#undef ldap_pvt_thread_exit
+#undef ldap_pvt_thread_join
+#undef ldap_pvt_thread_kill
+#undef ldap_pvt_thread_yield
+#undef ldap_pvt_thread_cond_init
+#undef ldap_pvt_thread_cond_destroy
+#undef ldap_pvt_thread_cond_signal
+#undef ldap_pvt_thread_cond_broadcast
+#undef ldap_pvt_thread_cond_wait
+#undef ldap_pvt_thread_mutex_init
+#undef ldap_pvt_thread_mutex_recursive_init
+#undef ldap_pvt_thread_mutex_destroy
+#undef ldap_pvt_thread_mutex_lock
+#undef ldap_pvt_thread_mutex_trylock
+#undef ldap_pvt_thread_mutex_unlock
+#undef ldap_pvt_thread_self
+/* LDAP_THREAD_RDWR_IMPLEMENTATION: */
+#undef ldap_pvt_thread_rdwr_t
+#undef ldap_pvt_thread_rdwr_init
+#undef ldap_pvt_thread_rdwr_destroy
+#undef ldap_pvt_thread_rdwr_rlock
+#undef ldap_pvt_thread_rdwr_rtrylock
+#undef ldap_pvt_thread_rdwr_runlock
+#undef ldap_pvt_thread_rdwr_wlock
+#undef ldap_pvt_thread_rdwr_wtrylock
+#undef ldap_pvt_thread_rdwr_wunlock
+#undef ldap_pvt_thread_rdwr_readers
+#undef ldap_pvt_thread_rdwr_writers
+#undef ldap_pvt_thread_rdwr_active
+/* LDAP_THREAD_POOL_IMPLEMENTATION: */
+#undef ldap_pvt_thread_pool_t
+#undef ldap_pvt_thread_pool_init
+#undef ldap_pvt_thread_pool_submit
+#undef ldap_pvt_thread_pool_maxthreads
+#undef ldap_pvt_thread_pool_backload
+#undef ldap_pvt_thread_pool_pause
+#undef ldap_pvt_thread_pool_resume
+#undef ldap_pvt_thread_pool_destroy
+#undef ldap_pvt_thread_pool_close
+#undef ldap_pvt_thread_pool_free
+#undef ldap_pvt_thread_pool_getkey
+#undef ldap_pvt_thread_pool_setkey
+#undef ldap_pvt_thread_pool_purgekey
+#undef ldap_pvt_thread_pool_context
+#undef ldap_pvt_thread_pool_context_reset
+#endif /* LDAP_THREAD_DEBUG_IMPLEMENTATION */
+
+#endif /* LDAP_THREAD_DEBUG */
diff --git a/libraries/libldap/ldif.c b/libraries/libldap/ldif.c
new file mode 100644
index 0000000..57e44f8
--- /dev/null
+++ b/libraries/libldap/ldif.c
@@ -0,0 +1,919 @@
+/* ldif.c - routines for dealing with LDIF files */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission. This
+ * software is provided ``as is'' without express or implied warranty.
+ */
+/* This work was originally developed by the University of Michigan
+ * and distributed as part of U-MICH LDAP.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/ctype.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/time.h>
+
+int ldif_debug = 0;
+
+#include "ldap-int.h"
+#include "ldif.h"
+
+#define CONTINUED_LINE_MARKER '\r'
+
+#ifdef CSRIMALLOC
+#define ber_memalloc malloc
+#define ber_memcalloc calloc
+#define ber_memrealloc realloc
+#define ber_strdup strdup
+#endif
+
+static const char nib2b64[0x40] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/*
+ * ldif_parse_line - takes a line of the form "type:[:] value" and splits it
+ * into components "type" and "value". if a double colon separates type from
+ * value, then value is encoded in base 64, and parse_line un-decodes it
+ * (in place) before returning. The type and value are stored in malloc'd
+ * memory which must be freed by the caller.
+ *
+ * ldif_parse_line2 - operates in-place on input buffer, returning type
+ * in-place. Will return value in-place if possible, (must malloc for
+ * fetched URLs). If freeval is NULL, all return data will be malloc'd
+ * and the input line will be unmodified. Otherwise freeval is set to
+ * True if the value was malloc'd.
+ */
+
+int
+ldif_parse_line(
+ LDAP_CONST char *line,
+ char **typep,
+ char **valuep,
+ ber_len_t *vlenp
+)
+{
+ struct berval type, value;
+ int rc = ldif_parse_line2( (char *)line, &type, &value, NULL );
+
+ *typep = type.bv_val;
+ *valuep = value.bv_val;
+ *vlenp = value.bv_len;
+ return rc;
+}
+
+int
+ldif_parse_line2(
+ char *line,
+ struct berval *type,
+ struct berval *value,
+ int *freeval
+)
+{
+ char *s, *p, *d;
+ int b64, url;
+
+ BER_BVZERO( type );
+ BER_BVZERO( value );
+
+ /* skip any leading space */
+ while ( isspace( (unsigned char) *line ) ) {
+ line++;
+ }
+
+ if ( freeval ) {
+ *freeval = 0;
+ } else {
+ line = ber_strdup( line );
+
+ if( line == NULL ) {
+ ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
+ _("ldif_parse_line: line malloc failed\n"));
+ return( -1 );
+ }
+ }
+
+ type->bv_val = line;
+
+ s = strchr( type->bv_val, ':' );
+
+ if ( s == NULL ) {
+ ber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug,
+ _("ldif_parse_line: missing ':' after %s\n"),
+ type->bv_val );
+ if ( !freeval ) ber_memfree( line );
+ return( -1 );
+ }
+
+ /* trim any space between type and : */
+ for ( p = &s[-1]; p > type->bv_val && isspace( * (unsigned char *) p ); p-- ) {
+ *p = '\0';
+ }
+ *s++ = '\0';
+ type->bv_len = s - type->bv_val - 1;
+
+ url = 0;
+ b64 = 0;
+
+ if ( *s == '<' ) {
+ s++;
+ url = 1;
+
+ } else if ( *s == ':' ) {
+ /* base 64 encoded value */
+ s++;
+ b64 = 1;
+ }
+
+ /* skip space between : and value */
+ while ( isspace( (unsigned char) *s ) ) {
+ s++;
+ }
+
+ /* check for continued line markers that should be deleted */
+ for ( p = s, d = s; *p; p++ ) {
+ if ( *p != CONTINUED_LINE_MARKER )
+ *d++ = *p;
+ }
+ *d = '\0';
+
+ if ( b64 ) {
+ char *byte = s;
+
+ if ( *s == '\0' ) {
+ /* no value is present, error out */
+ ber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug,
+ _("ldif_parse_line: %s missing base64 value\n"),
+ type->bv_val );
+ if ( !freeval ) ber_memfree( line );
+ return( -1 );
+ }
+
+ value->bv_val = s;
+ value->bv_len = d - s;
+ if ( ldap_int_decode_b64_inplace( value ) != LDAP_SUCCESS ) {
+ ber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug,
+ _("ldif_parse_line: %s base64 decode failed\n"),
+ type->bv_val );
+ if ( !freeval ) ber_memfree( line );
+ return( -1 );
+ }
+ } else if ( url ) {
+ if ( *s == '\0' ) {
+ /* no value is present, error out */
+ ber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug,
+ _("ldif_parse_line: %s missing URL value\n"),
+ type->bv_val );
+ if ( !freeval ) ber_memfree( line );
+ return( -1 );
+ }
+
+ if( ldif_fetch_url( s, &value->bv_val, &value->bv_len ) ) {
+ ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
+ _("ldif_parse_line: %s: URL \"%s\" fetch failed\n"),
+ type->bv_val, s );
+ if ( !freeval ) ber_memfree( line );
+ return( -1 );
+ }
+ if ( freeval ) *freeval = 1;
+
+ } else {
+ value->bv_val = s;
+ value->bv_len = (int) (d - s);
+ }
+
+ if ( !freeval ) {
+ struct berval bv = *type;
+
+ ber_dupbv( type, &bv );
+
+ if( BER_BVISNULL( type )) {
+ ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
+ _("ldif_parse_line: type malloc failed\n"));
+ if( url ) ber_memfree( value->bv_val );
+ ber_memfree( line );
+ return( -1 );
+ }
+
+ if( !url ) {
+ bv = *value;
+ ber_dupbv( value, &bv );
+ if( BER_BVISNULL( value )) {
+ ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
+ _("ldif_parse_line: value malloc failed\n"));
+ ber_memfree( type->bv_val );
+ ber_memfree( line );
+ return( -1 );
+ }
+ }
+
+ ber_memfree( line );
+ }
+
+ return( 0 );
+}
+
+/*
+ * ldif_getline - return the next "line" (minus newline) of input from a
+ * string buffer of lines separated by newlines, terminated by \n\n
+ * or \0. this routine handles continued lines, bundling them into
+ * a single big line before returning. if a line begins with a white
+ * space character, it is a continuation of the previous line. the white
+ * space character (nb: only one char), and preceding newline are changed
+ * into CONTINUED_LINE_MARKER chars, to be deleted later by the
+ * ldif_parse_line() routine above.
+ *
+ * ldif_getline will skip over any line which starts '#'.
+ *
+ * ldif_getline takes a pointer to a pointer to the buffer on the first call,
+ * which it updates and must be supplied on subsequent calls.
+ */
+
+int
+ldif_countlines( LDAP_CONST char *buf )
+{
+ char *nl;
+ int ret = 0;
+
+ if ( !buf ) return ret;
+
+ for ( nl = strchr(buf, '\n'); nl; nl = strchr(nl, '\n') ) {
+ nl++;
+ if ( *nl != ' ' ) ret++;
+ }
+ return ret;
+}
+
+char *
+ldif_getline( char **next )
+{
+ char *line;
+
+ do {
+ if ( *next == NULL || **next == '\n' || **next == '\0' ) {
+ return( NULL );
+ }
+
+ line = *next;
+
+ while ( (*next = strchr( *next, '\n' )) != NULL ) {
+#if CONTINUED_LINE_MARKER != '\r'
+ if ( (*next)[-1] == '\r' ) {
+ (*next)[-1] = CONTINUED_LINE_MARKER;
+ }
+#endif
+
+ if ( (*next)[1] != ' ' ) {
+ if ( (*next)[1] == '\r' && (*next)[2] == '\n' ) {
+ *(*next)++ = '\0';
+ }
+ *(*next)++ = '\0';
+ break;
+ }
+
+ **next = CONTINUED_LINE_MARKER;
+ (*next)[1] = CONTINUED_LINE_MARKER;
+ (*next)++;
+ }
+ } while( *line == '#' );
+
+ return( line );
+}
+
+/*
+ * name and OID of attributeTypes that must be base64 encoded in any case
+ */
+typedef struct must_b64_encode_s {
+ struct berval name;
+ struct berval oid;
+} must_b64_encode_s;
+
+static must_b64_encode_s default_must_b64_encode[] = {
+ { BER_BVC( "userPassword" ), BER_BVC( "2.5.4.35" ) },
+ { BER_BVNULL, BER_BVNULL }
+};
+
+static must_b64_encode_s *must_b64_encode = default_must_b64_encode;
+
+/*
+ * register name and OID of attributeTypes that must always be base64
+ * encoded
+ *
+ * NOTE: this routine mallocs memory in a static struct which must
+ * be explicitly freed when no longer required
+ */
+int
+ldif_must_b64_encode_register( LDAP_CONST char *name, LDAP_CONST char *oid )
+{
+ int i;
+ ber_len_t len;
+
+ assert( must_b64_encode != NULL );
+ assert( name != NULL );
+ assert( oid != NULL );
+
+ len = strlen( name );
+
+ for ( i = 0; !BER_BVISNULL( &must_b64_encode[i].name ); i++ ) {
+ if ( len != must_b64_encode[i].name.bv_len ) {
+ continue;
+ }
+
+ if ( strcasecmp( name, must_b64_encode[i].name.bv_val ) == 0 ) {
+ break;
+ }
+ }
+
+ if ( !BER_BVISNULL( &must_b64_encode[i].name ) ) {
+ return 1;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &must_b64_encode[i].name ); i++ )
+ /* just count */ ;
+
+ if ( must_b64_encode == default_must_b64_encode ) {
+ must_b64_encode = ber_memalloc( sizeof( must_b64_encode_s ) * ( i + 2 ) );
+ if ( must_b64_encode == NULL ) {
+ return 1;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &default_must_b64_encode[i].name ); i++ ) {
+ ber_dupbv( &must_b64_encode[i].name, &default_must_b64_encode[i].name );
+ ber_dupbv( &must_b64_encode[i].oid, &default_must_b64_encode[i].oid );
+ }
+
+ } else {
+ must_b64_encode_s *tmp;
+
+ tmp = ber_memrealloc( must_b64_encode,
+ sizeof( must_b64_encode_s ) * ( i + 2 ) );
+ if ( tmp == NULL ) {
+ return 1;
+ }
+ must_b64_encode = tmp;
+ }
+
+ ber_str2bv( name, len, 1, &must_b64_encode[i].name );
+ ber_str2bv( oid, 0, 1, &must_b64_encode[i].oid );
+
+ BER_BVZERO( &must_b64_encode[i + 1].name );
+
+ return 0;
+}
+
+void
+ldif_must_b64_encode_release( void )
+{
+ int i;
+
+ assert( must_b64_encode != NULL );
+
+ if ( must_b64_encode == default_must_b64_encode ) {
+ return;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &must_b64_encode[i].name ); i++ ) {
+ ber_memfree( must_b64_encode[i].name.bv_val );
+ ber_memfree( must_b64_encode[i].oid.bv_val );
+ }
+
+ ber_memfree( must_b64_encode );
+
+ must_b64_encode = default_must_b64_encode;
+}
+
+/*
+ * returns 1 iff the string corresponds to the name or the OID of any
+ * of the attributeTypes listed in must_b64_encode
+ */
+static int
+ldif_must_b64_encode( LDAP_CONST char *s )
+{
+ int i;
+ struct berval bv;
+
+ assert( must_b64_encode != NULL );
+ assert( s != NULL );
+
+ ber_str2bv( s, 0, 0, &bv );
+
+ for ( i = 0; !BER_BVISNULL( &must_b64_encode[i].name ); i++ ) {
+ if ( ber_bvstrcasecmp( &must_b64_encode[i].name, &bv ) == 0
+ || ber_bvcmp( &must_b64_encode[i].oid, &bv ) == 0 )
+ {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* NOTE: only preserved for binary compatibility */
+void
+ldif_sput(
+ char **out,
+ int type,
+ LDAP_CONST char *name,
+ LDAP_CONST char *val,
+ ber_len_t vlen )
+{
+ ldif_sput_wrap( out, type, name, val, vlen, 0 );
+}
+
+void
+ldif_sput_wrap(
+ char **out,
+ int type,
+ LDAP_CONST char *name,
+ LDAP_CONST char *val,
+ ber_len_t vlen,
+ ber_len_t wrap )
+{
+ const unsigned char *byte, *stop;
+ unsigned char buf[3];
+ unsigned long bits;
+ char *save;
+ int pad;
+ int namelen = 0;
+
+ ber_len_t savelen;
+ ber_len_t len=0;
+ ber_len_t i;
+
+ if ( !wrap )
+ wrap = LDIF_LINE_WIDTH;
+
+ /* prefix */
+ switch( type ) {
+ case LDIF_PUT_COMMENT:
+ *(*out)++ = '#';
+ len++;
+
+ if( vlen ) {
+ *(*out)++ = ' ';
+ len++;
+ }
+
+ break;
+
+ case LDIF_PUT_SEP:
+ *(*out)++ = '\n';
+ return;
+ }
+
+ /* name (attribute type) */
+ if( name != NULL ) {
+ /* put the name + ":" */
+ namelen = strlen(name);
+ strcpy(*out, name);
+ *out += namelen;
+ len += namelen;
+
+ if( type != LDIF_PUT_COMMENT ) {
+ *(*out)++ = ':';
+ len++;
+ }
+
+ }
+#ifdef LDAP_DEBUG
+ else {
+ assert( type == LDIF_PUT_COMMENT );
+ }
+#endif
+
+ if( vlen == 0 ) {
+ *(*out)++ = '\n';
+ return;
+ }
+
+ switch( type ) {
+ case LDIF_PUT_NOVALUE:
+ *(*out)++ = '\n';
+ return;
+
+ case LDIF_PUT_URL: /* url value */
+ *(*out)++ = '<';
+ len++;
+ break;
+
+ case LDIF_PUT_B64: /* base64 value */
+ *(*out)++ = ':';
+ len++;
+ break;
+ }
+
+ switch( type ) {
+ case LDIF_PUT_TEXT:
+ case LDIF_PUT_URL:
+ case LDIF_PUT_B64:
+ *(*out)++ = ' ';
+ len++;
+ /* fall-thru */
+
+ case LDIF_PUT_COMMENT:
+ /* pre-encoded names */
+ for ( i=0; i < vlen; i++ ) {
+ if ( len > wrap ) {
+ *(*out)++ = '\n';
+ *(*out)++ = ' ';
+ len = 1;
+ }
+
+ *(*out)++ = val[i];
+ len++;
+ }
+ *(*out)++ = '\n';
+ return;
+ }
+
+ save = *out;
+ savelen = len;
+
+ *(*out)++ = ' ';
+ len++;
+
+ stop = (const unsigned char *) (val + vlen);
+
+ if ( type == LDIF_PUT_VALUE
+ && isgraph( (unsigned char) val[0] ) && val[0] != ':' && val[0] != '<'
+ && isgraph( (unsigned char) val[vlen-1] )
+#ifndef LDAP_BINARY_DEBUG
+ && strstr( name, ";binary" ) == NULL
+#endif
+#ifndef LDAP_PASSWD_DEBUG
+ && !ldif_must_b64_encode( name )
+#endif
+ ) {
+ int b64 = 0;
+
+ for ( byte = (const unsigned char *) val; byte < stop;
+ byte++, len++ )
+ {
+ if ( !isascii( *byte ) || !isprint( *byte ) ) {
+ b64 = 1;
+ break;
+ }
+ if ( len >= wrap ) {
+ *(*out)++ = '\n';
+ *(*out)++ = ' ';
+ len = 1;
+ }
+ *(*out)++ = *byte;
+ }
+
+ if( !b64 ) {
+ *(*out)++ = '\n';
+ return;
+ }
+ }
+
+ *out = save;
+ *(*out)++ = ':';
+ *(*out)++ = ' ';
+ len = savelen + 2;
+
+ /* convert to base 64 (3 bytes => 4 base 64 digits) */
+ for ( byte = (const unsigned char *) val;
+ byte < stop - 2;
+ byte += 3 )
+ {
+ bits = (byte[0] & 0xff) << 16;
+ bits |= (byte[1] & 0xff) << 8;
+ bits |= (byte[2] & 0xff);
+
+ for ( i = 0; i < 4; i++, len++, bits <<= 6 ) {
+ if ( len >= wrap ) {
+ *(*out)++ = '\n';
+ *(*out)++ = ' ';
+ len = 1;
+ }
+
+ /* get b64 digit from high order 6 bits */
+ *(*out)++ = nib2b64[ (bits & 0xfc0000L) >> 18 ];
+ }
+ }
+
+ /* add padding if necessary */
+ if ( byte < stop ) {
+ for ( i = 0; byte + i < stop; i++ ) {
+ buf[i] = byte[i];
+ }
+ for ( pad = 0; i < 3; i++, pad++ ) {
+ buf[i] = '\0';
+ }
+ byte = buf;
+ bits = (byte[0] & 0xff) << 16;
+ bits |= (byte[1] & 0xff) << 8;
+ bits |= (byte[2] & 0xff);
+
+ for ( i = 0; i < 4; i++, len++, bits <<= 6 ) {
+ if ( len >= wrap ) {
+ *(*out)++ = '\n';
+ *(*out)++ = ' ';
+ len = 1;
+ }
+
+ if( i + pad < 4 ) {
+ /* get b64 digit from low order 6 bits */
+ *(*out)++ = nib2b64[ (bits & 0xfc0000L) >> 18 ];
+ } else {
+ *(*out)++ = '=';
+ }
+ }
+ }
+ *(*out)++ = '\n';
+}
+
+
+/*
+ * ldif_type_and_value return BER malloc'd, zero-terminated LDIF line
+ */
+
+/* NOTE: only preserved for binary compatibility */
+char *
+ldif_put(
+ int type,
+ LDAP_CONST char *name,
+ LDAP_CONST char *val,
+ ber_len_t vlen )
+{
+ return ldif_put_wrap( type, name, val, vlen, 0 );
+}
+
+char *
+ldif_put_wrap(
+ int type,
+ LDAP_CONST char *name,
+ LDAP_CONST char *val,
+ ber_len_t vlen,
+ ber_len_t wrap )
+{
+ char *buf, *p;
+ ber_len_t nlen;
+
+ nlen = ( name != NULL ) ? strlen( name ) : 0;
+
+ buf = (char *) ber_memalloc( LDIF_SIZE_NEEDED_WRAP( nlen, vlen, wrap ) + 1 );
+
+ if ( buf == NULL ) {
+ ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
+ _("ldif_type_and_value: malloc failed!"));
+ return NULL;
+ }
+
+ p = buf;
+ ldif_sput_wrap( &p, type, name, val, vlen, wrap );
+ *p = '\0';
+
+ return( buf );
+}
+
+int ldif_is_not_printable(
+ LDAP_CONST char *val,
+ ber_len_t vlen )
+{
+ if( vlen == 0 || val == NULL ) {
+ return -1;
+ }
+
+ if( isgraph( (unsigned char) val[0] ) && val[0] != ':' && val[0] != '<' &&
+ isgraph( (unsigned char) val[vlen-1] ) )
+ {
+ ber_len_t i;
+
+ for ( i = 0; val[i]; i++ ) {
+ if ( !isascii( val[i] ) || !isprint( (unsigned char) val[i] ) ) {
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+
+ return 1;
+}
+
+LDIFFP *
+ldif_open(
+ LDAP_CONST char *file,
+ LDAP_CONST char *mode
+)
+{
+ FILE *fp = fopen( file, mode );
+ LDIFFP *lfp = NULL;
+
+ if ( fp ) {
+ lfp = ber_memalloc( sizeof( LDIFFP ));
+ if ( lfp == NULL ) {
+ fclose( fp );
+ return NULL;
+ }
+ lfp->fp = fp;
+ lfp->prev = NULL;
+ }
+ return lfp;
+}
+
+LDIFFP *
+ldif_open_mem(
+ char *ldif,
+ size_t size,
+ LDAP_CONST char *mode
+)
+{
+#ifdef HAVE_FMEMOPEN
+ FILE *fp = fmemopen( ldif, size, mode );
+ LDIFFP *lfp = NULL;
+
+ if ( fp ) {
+ lfp = ber_memalloc( sizeof( LDIFFP ));
+ lfp->fp = fp;
+ lfp->prev = NULL;
+ }
+ return lfp;
+#else /* !HAVE_FMEMOPEN */
+ return NULL;
+#endif /* !HAVE_FMEMOPEN */
+}
+
+void
+ldif_close(
+ LDIFFP *lfp
+)
+{
+ LDIFFP *prev;
+
+ while ( lfp ) {
+ fclose( lfp->fp );
+ prev = lfp->prev;
+ ber_memfree( lfp );
+ lfp = prev;
+ }
+}
+
+#define LDIF_MAXLINE 4096
+
+/*
+ * ldif_read_record - read an ldif record. Return 1 for success, 0 for EOF,
+ * -1 for error.
+ */
+int
+ldif_read_record(
+ LDIFFP *lfp,
+ unsigned long *lno, /* ptr to line number counter */
+ char **bufp, /* ptr to malloced output buffer */
+ int *buflenp ) /* ptr to length of *bufp */
+{
+ char line[LDIF_MAXLINE], *nbufp;
+ ber_len_t lcur = 0, len;
+ int last_ch = '\n', found_entry = 0, stop, top_comment = 0;
+
+ for ( stop = 0; !stop; last_ch = line[len-1] ) {
+ /* If we're at the end of this file, see if we should pop
+ * back to a previous file. (return from an include)
+ */
+ while ( feof( lfp->fp )) {
+pop:
+ if ( lfp->prev ) {
+ LDIFFP *tmp = lfp->prev;
+ fclose( lfp->fp );
+ *lfp = *tmp;
+ ber_memfree( tmp );
+ } else {
+ stop = 1;
+ break;
+ }
+ }
+ if ( !stop ) {
+ if ( fgets( line, sizeof( line ), lfp->fp ) == NULL ) {
+ if ( !found_entry && !ferror( lfp->fp ) ) {
+ /* ITS#9811 Reached the end looking for an entry, try again */
+ goto pop;
+ }
+ stop = 1;
+ len = 0;
+ } else {
+ len = strlen( line );
+ }
+ }
+
+ if ( stop ) {
+ /* Add \n in case the file does not end with newline */
+ if (last_ch != '\n') {
+ len = 1;
+ line[0] = '\n';
+ line[1] = '\0';
+ goto last;
+ }
+ break;
+ }
+
+ /* Squash \r\n to \n */
+ if ( len > 1 && line[len-2] == '\r' ) {
+ len--;
+ line[len] = '\0';
+ line[len-1] = '\n';
+ }
+
+ if ( last_ch == '\n' ) {
+ (*lno)++;
+
+ if ( line[0] == '\n' ) {
+ if ( !found_entry ) {
+ lcur = 0;
+ top_comment = 0;
+ continue;
+ }
+ break;
+ }
+
+ if ( !found_entry ) {
+ if ( line[0] == '#' ) {
+ top_comment = 1;
+ } else if ( ! ( top_comment && line[0] == ' ' ) ) {
+ /* Found a new entry */
+ found_entry = 1;
+
+ if ( isdigit( (unsigned char) line[0] ) ) {
+ /* skip index */
+ continue;
+ }
+ if ( !strncasecmp( line, "include:",
+ STRLENOF("include:"))) {
+ FILE *fp2;
+ char *ptr;
+ found_entry = 0;
+
+ if ( line[len-1] == '\n' ) {
+ len--;
+ line[len] = '\0';
+ }
+
+ ptr = line + STRLENOF("include:");
+ while (isspace((unsigned char) *ptr)) ptr++;
+ fp2 = ldif_open_url( ptr );
+ if ( fp2 ) {
+ LDIFFP *lnew = ber_memalloc( sizeof( LDIFFP ));
+ if ( lnew == NULL ) {
+ fclose( fp2 );
+ return 0;
+ }
+ lnew->prev = lfp->prev;
+ lnew->fp = lfp->fp;
+ lfp->prev = lnew;
+ lfp->fp = fp2;
+ line[len] = '\n';
+ len++;
+ continue;
+ } else {
+ /* We failed to open the file, this should
+ * be reported as an error somehow.
+ */
+ ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
+ _("ldif_read_record: include %s failed\n"), ptr );
+ return -1;
+ }
+ }
+ }
+ }
+ }
+
+last:
+ if ( *buflenp - lcur <= len ) {
+ *buflenp += len + LDIF_MAXLINE;
+ nbufp = ber_memrealloc( *bufp, *buflenp );
+ if( nbufp == NULL ) {
+ return 0;
+ }
+ *bufp = nbufp;
+ }
+ strcpy( *bufp + lcur, line );
+ lcur += len;
+ }
+
+ return( found_entry );
+}
diff --git a/libraries/libldap/ldifutil.c b/libraries/libldap/ldifutil.c
new file mode 100644
index 0000000..df557e5
--- /dev/null
+++ b/libraries/libldap/ldifutil.c
@@ -0,0 +1,724 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+/*
+ * This file contains public API to help with parsing LDIF
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+#include <ac/socket.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldif.h"
+
+#define M_SEP 0x7f
+
+/* strings found in LDIF entries */
+static struct berval BV_VERSION = BER_BVC("version");
+static struct berval BV_DN = BER_BVC("dn");
+static struct berval BV_CONTROL = BER_BVC("control");
+static struct berval BV_CHANGETYPE = BER_BVC("changetype");
+static struct berval BV_ADDCT = BER_BVC("add");
+static struct berval BV_MODIFYCT = BER_BVC("modify");
+static struct berval BV_DELETECT = BER_BVC("delete");
+static struct berval BV_MODRDNCT = BER_BVC("modrdn");
+static struct berval BV_MODDNCT = BER_BVC("moddn");
+static struct berval BV_RENAMECT = BER_BVC("rename");
+static struct berval BV_MODOPADD = BER_BVC("add");
+static struct berval BV_MODOPREPLACE = BER_BVC("replace");
+static struct berval BV_MODOPDELETE = BER_BVC("delete");
+static struct berval BV_MODOPINCREMENT = BER_BVC("increment");
+static struct berval BV_NEWRDN = BER_BVC("newrdn");
+static struct berval BV_DELETEOLDRDN = BER_BVC("deleteoldrdn");
+static struct berval BV_NEWSUP = BER_BVC("newsuperior");
+
+#define BV_CASEMATCH(a, b) \
+ ((a)->bv_len == (b)->bv_len && 0 == strcasecmp((a)->bv_val, (b)->bv_val))
+
+static int parse_ldif_control LDAP_P(( struct berval *bval, LDAPControl ***ppctrls ));
+
+void
+ldap_ldif_record_done( LDIFRecord *lr )
+{
+ int i;
+
+ /* the LDAPControl stuff does not allow the use of memory contexts */
+ if (lr->lr_ctrls != NULL) {
+ ldap_controls_free( lr->lr_ctrls );
+ }
+ if ( lr->lr_lm != NULL ) {
+ ber_memfree_x( lr->lr_lm, lr->lr_ctx );
+ }
+ if ( lr->lr_mops != NULL ) {
+ ber_memfree_x( lr->lr_mops, lr->lr_ctx );
+ }
+ for (i=lr->lr_lines-1; i>=0; i--)
+ if ( lr->lr_freeval[i] ) ber_memfree_x( lr->lr_vals[i].bv_val, lr->lr_ctx );
+ ber_memfree_x( lr->lr_btype, lr->lr_ctx );
+
+ memset( lr, 0, sizeof(LDIFRecord) );
+}
+
+/*
+ * ldap_parse_ldif_record_x() will convert an LDIF record read with ldif_read_record()
+ * into an array of LDAPMod* and an array of LDAPControl*, suitable for passing
+ * directly to any other LDAP API function that takes LDAPMod** and LDAPControl**
+ * arguments, such as ldap_modify_s().
+ *
+ * rbuf - the ldif record buffer returned from ldif_read_record - rbuf.bv_val must be
+ * writable - will use ldif_getline to read from it
+ * linenum - the ldif line number returned from ldif_read_record
+ * - used for logging errors (e.g. error at line N)
+ * lr - holds the data to return
+ * errstr - a string used for logging (usually the program name e.g. "ldapmodify"
+ * flags - 0 or some combination of LDIF_DEFAULT_ADD LDIF_ENTRIES_ONLY LDIF_NO_CONTROLS
+ * ctx is the memory allocation context - if NULL, use the standard memory allocator
+ */
+int
+ldap_parse_ldif_record_x(
+ struct berval *rbuf,
+ unsigned long linenum,
+ LDIFRecord *lr,
+ const char *errstr,
+ unsigned int flags,
+ void *ctx )
+{
+ char *line, *dn;
+ int rc, modop;
+ int expect_modop, expect_sep;
+ int ldapadd, new_entry, delete_entry, got_all, no_dn;
+ LDAPMod **pmods;
+ int version;
+ LDAPControl **pctrls;
+ int i, j, k, idn, nmods;
+ struct berval **bvl, bv;
+
+ assert( lr != NULL );
+ assert( rbuf != NULL );
+ memset( lr, 0, sizeof(LDIFRecord) );
+ lr->lr_ctx = ctx; /* save memory context for later */
+ ldapadd = flags & LDIF_DEFAULT_ADD;
+ no_dn = flags & LDIF_NO_DN;
+ expect_modop = flags & LDIF_MODS_ONLY;
+ new_entry = ldapadd;
+
+ rc = got_all = delete_entry = modop = 0;
+ expect_sep = 0;
+ version = 0;
+ pmods = NULL;
+ pctrls = NULL;
+ dn = NULL;
+
+ lr->lr_lines = ldif_countlines( rbuf->bv_val );
+ lr->lr_btype = ber_memcalloc_x( 1, (lr->lr_lines+1)*2*sizeof(struct berval)+lr->lr_lines, ctx );
+ if ( !lr->lr_btype )
+ return LDAP_NO_MEMORY;
+
+ lr->lr_vals = lr->lr_btype+lr->lr_lines+1;
+ lr->lr_freeval = (char *)(lr->lr_vals+lr->lr_lines+1);
+ i = -1;
+
+ while ( rc == 0 && ( line = ldif_getline( &rbuf->bv_val )) != NULL ) {
+ int freev;
+
+ if ( *line == '\n' || *line == '\0' ) {
+ break;
+ }
+
+ ++i;
+
+ if ( line[0] == '-' && !line[1] ) {
+ BER_BVZERO( lr->lr_btype+i );
+ lr->lr_freeval[i] = 0;
+ continue;
+ }
+
+ if ( ( rc = ldif_parse_line2( line, lr->lr_btype+i, lr->lr_vals+i, &freev ) ) < 0 ) {
+ fprintf( stderr, _("%s: invalid format (line %lu) entry: \"%s\"\n"),
+ errstr, linenum+i, dn == NULL ? "" : dn );
+ rc = LDAP_PARAM_ERROR;
+ goto leave;
+ }
+ lr->lr_freeval[i] = freev;
+
+ if ( dn == NULL && !no_dn ) {
+ if ( linenum+i == 1 && BV_CASEMATCH( lr->lr_btype+i, &BV_VERSION )) {
+ /* lutil_atoi() introduces a dependence of libldap
+ * on liblutil; we only allow version 1 by now (ITS#6654)
+ */
+#if 0
+ int v;
+ if( lr->lr_vals[i].bv_len == 0 || lutil_atoi( &v, lr->lr_vals[i].bv_val) != 0 || v != 1 )
+#endif
+ static const struct berval version1 = { 1, "1" };
+ if ( lr->lr_vals[i].bv_len != version1.bv_len || strncmp( lr->lr_vals[i].bv_val, version1.bv_val, version1.bv_len ) != 0 )
+ {
+ fprintf( stderr,
+ _("%s: invalid version %s, line %lu (ignored)\n"),
+ errstr, lr->lr_vals[i].bv_val, linenum );
+ }
+ version++;
+
+ } else if ( BV_CASEMATCH( lr->lr_btype+i, &BV_DN )) {
+ lr->lr_dn = lr->lr_vals[i];
+ dn = lr->lr_dn.bv_val; /* primarily for logging */
+ idn = i;
+ }
+ /* skip all lines until we see "dn:" */
+ }
+ }
+
+ /* check to make sure there was a dn: line */
+ if ( !dn && !no_dn ) {
+ rc = 0;
+ goto leave;
+ }
+
+ lr->lr_lines = i+1;
+
+ if( lr->lr_lines == 0 ) {
+ rc = 0;
+ goto leave;
+ }
+
+ if( version && lr->lr_lines == 1 ) {
+ rc = 0;
+ goto leave;
+ }
+
+ if ( no_dn ) {
+ i = 0;
+ } else {
+ i = idn+1;
+ /* Check for "control" tag after dn and before changetype. */
+ if ( BV_CASEMATCH( lr->lr_btype+i, &BV_CONTROL )) {
+ /* Parse and add it to the list of controls */
+ if ( !( flags & LDIF_NO_CONTROLS ) ) {
+ rc = parse_ldif_control( lr->lr_vals+i, &pctrls );
+ if (rc != 0) {
+ fprintf( stderr,
+ _("%s: Error processing %s line, line %lu: %s\n"),
+ errstr, BV_CONTROL.bv_val, linenum+i, ldap_err2string(rc) );
+ }
+ }
+ i++;
+ if ( i>= lr->lr_lines ) {
+short_input:
+ fprintf( stderr,
+ _("%s: Expecting more input after %s line, line %lu\n"),
+ errstr, lr->lr_btype[i-1].bv_val, linenum+i );
+
+ rc = LDAP_PARAM_ERROR;
+ goto leave;
+ }
+ }
+ }
+
+ /* Check for changetype */
+ if ( BV_CASEMATCH( lr->lr_btype+i, &BV_CHANGETYPE )) {
+#ifdef LIBERAL_CHANGETYPE_MODOP
+ /* trim trailing spaces (and log warning ...) */
+ int icnt;
+ for ( icnt = lr->lr_vals[i].bv_len; --icnt > 0; ) {
+ if ( !isspace( (unsigned char) lr->lr_vals[i].bv_val[icnt] ) ) {
+ break;
+ }
+ }
+
+ if ( ++icnt != lr->lr_vals[i].bv_len ) {
+ fprintf( stderr, _("%s: illegal trailing space after"
+ " \"%s: %s\" trimmed (line %lu, entry \"%s\")\n"),
+ errstr, BV_CHANGETYPE.bv_val, lr->lr_vals[i].bv_val, linenum+i, dn );
+ lr->lr_vals[i].bv_val[icnt] = '\0';
+ }
+#endif /* LIBERAL_CHANGETYPE_MODOP */
+
+ /* if LDIF_ENTRIES_ONLY, then either the changetype must be add, or
+ there must be no changetype, and the flag LDIF_DEFAULT_ADD must be set */
+ if ( flags & LDIF_ENTRIES_ONLY ) {
+ if ( !( BV_CASEMATCH( lr->lr_vals+i, &BV_ADDCT )) ) {
+ ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
+ _("%s: skipping LDIF record beginning at line %lu: "
+ "changetype '%.*s' found but entries only was requested\n"),
+ errstr, linenum,
+ (int)lr->lr_vals[i].bv_len,
+ (const char *)lr->lr_vals[i].bv_val );
+ goto leave;
+ }
+ }
+
+ if ( BV_CASEMATCH( lr->lr_vals+i, &BV_MODIFYCT )) {
+ new_entry = 0;
+ expect_modop = 1;
+ } else if ( BV_CASEMATCH( lr->lr_vals+i, &BV_ADDCT )) {
+ new_entry = 1;
+ modop = LDAP_MOD_ADD;
+ } else if ( BV_CASEMATCH( lr->lr_vals+i, &BV_MODRDNCT )
+ || BV_CASEMATCH( lr->lr_vals+i, &BV_MODDNCT )
+ || BV_CASEMATCH( lr->lr_vals+i, &BV_RENAMECT ))
+ {
+ i++;
+ if ( i >= lr->lr_lines )
+ goto short_input;
+ if ( !BV_CASEMATCH( lr->lr_btype+i, &BV_NEWRDN )) {
+ fprintf( stderr, _("%s: expecting \"%s:\" but saw"
+ " \"%s:\" (line %lu, entry \"%s\")\n"),
+ errstr, BV_NEWRDN.bv_val, lr->lr_btype[i].bv_val, linenum+i, dn );
+ rc = LDAP_PARAM_ERROR;
+ goto leave;
+ }
+ lr->lrop_newrdn = lr->lr_vals[i];
+ i++;
+ if ( i >= lr->lr_lines )
+ goto short_input;
+ if ( !BV_CASEMATCH( lr->lr_btype+i, &BV_DELETEOLDRDN )) {
+ fprintf( stderr, _("%s: expecting \"%s:\" but saw"
+ " \"%s:\" (line %lu, entry \"%s\")\n"),
+ errstr, BV_DELETEOLDRDN.bv_val, lr->lr_btype[i].bv_val, linenum+i, dn );
+ rc = LDAP_PARAM_ERROR;
+ goto leave;
+ }
+ lr->lrop_delold = ( lr->lr_vals[i].bv_val[0] == '0' ) ? 0 : 1;
+ i++;
+ if ( i < lr->lr_lines ) {
+ if ( !BV_CASEMATCH( lr->lr_btype+i, &BV_NEWSUP )) {
+ fprintf( stderr, _("%s: expecting \"%s:\" but saw"
+ " \"%s:\" (line %lu, entry \"%s\")\n"),
+ errstr, BV_NEWSUP.bv_val, lr->lr_btype[i].bv_val, linenum+i, dn );
+ rc = LDAP_PARAM_ERROR;
+ goto leave;
+ }
+ lr->lrop_newsup = lr->lr_vals[i];
+ i++;
+ }
+ got_all = 1;
+ } else if ( BV_CASEMATCH( lr->lr_vals+i, &BV_DELETECT )) {
+ got_all = delete_entry = 1;
+ } else {
+ fprintf( stderr,
+ _("%s: unknown %s \"%s\" (line %lu, entry \"%s\")\n"),
+ errstr, BV_CHANGETYPE.bv_val, lr->lr_vals[i].bv_val, linenum+i, dn );
+ rc = LDAP_PARAM_ERROR;
+ goto leave;
+ }
+ i++;
+ } else if ( ldapadd ) { /* missing changetype => add */
+ new_entry = 1;
+ modop = LDAP_MOD_ADD;
+ } else {
+ /* if LDIF_ENTRIES_ONLY, then either the changetype must be add, or
+ there must be no changetype, and the flag LDIF_DEFAULT_ADD must be set */
+ if ( flags & LDIF_ENTRIES_ONLY ) {
+ ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
+ _("%s: skipping LDIF record beginning at line %lu: "
+ "no changetype found but entries only was requested and "
+ "the default setting for missing changetype is modify\n"),
+ errstr, linenum );
+ goto leave;
+ }
+ expect_modop = 1; /* missing changetype => modify */
+ }
+
+ if ( got_all ) {
+ if ( i < lr->lr_lines ) {
+ fprintf( stderr,
+ _("%s: extra lines at end (line %lu, entry \"%s\")\n"),
+ errstr, linenum+i, dn );
+ rc = LDAP_PARAM_ERROR;
+ goto leave;
+ }
+ goto doit;
+ }
+
+ nmods = lr->lr_lines - i;
+ idn = i;
+
+ if ( new_entry ) {
+ int fv;
+
+ /* Make sure all attributes with multiple values are contiguous */
+ for (; i<lr->lr_lines; i++) {
+ for (j=i+1; j<lr->lr_lines; j++) {
+ if ( !lr->lr_btype[j].bv_val ) {
+ fprintf( stderr,
+ _("%s: missing attributeDescription (line %lu, entry \"%s\")\n"),
+ errstr, linenum+j, dn );
+ rc = LDAP_PARAM_ERROR;
+ goto leave;
+ }
+ if ( BV_CASEMATCH( lr->lr_btype+i, lr->lr_btype+j )) {
+ nmods--;
+ /* out of order, move intervening attributes down */
+ if ( j != i+1 ) {
+ bv = lr->lr_vals[j];
+ fv = lr->lr_freeval[j];
+ for (k=j; k>i; k--) {
+ lr->lr_btype[k] = lr->lr_btype[k-1];
+ lr->lr_vals[k] = lr->lr_vals[k-1];
+ lr->lr_freeval[k] = lr->lr_freeval[k-1];
+ }
+ k++;
+ lr->lr_btype[k] = lr->lr_btype[i];
+ lr->lr_vals[k] = bv;
+ lr->lr_freeval[k] = fv;
+ }
+ i++;
+ }
+ }
+ }
+ /* Allocate space for array of mods, array of pointers to mods,
+ * and array of pointers to values, allowing for NULL terminators
+ * for the pointer arrays...
+ */
+ lr->lr_lm = ber_memalloc_x( nmods * sizeof(LDAPMod) +
+ (nmods+1) * sizeof(LDAPMod*) +
+ (lr->lr_lines + nmods - idn) * sizeof(struct berval *), ctx );
+ if ( lr->lr_lm == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto leave;
+ }
+
+ pmods = (LDAPMod **)(lr->lr_lm+nmods);
+ bvl = (struct berval **)(pmods+nmods+1);
+
+ j = 0;
+ k = -1;
+ BER_BVZERO(&bv);
+ for (i=idn; i<lr->lr_lines; i++) {
+ if ( BV_CASEMATCH( lr->lr_btype+i, &BV_DN )) {
+ fprintf( stderr, _("%s: attributeDescription \"%s\":"
+ " (possible missing newline"
+ " after line %lu, entry \"%s\"?)\n"),
+ errstr, lr->lr_btype[i].bv_val, linenum+i - 1, dn );
+ }
+ if ( !BV_CASEMATCH( lr->lr_btype+i, &bv )) {
+ bvl[k++] = NULL;
+ bv = lr->lr_btype[i];
+ lr->lr_lm[j].mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES;
+ lr->lr_lm[j].mod_type = bv.bv_val;
+ lr->lr_lm[j].mod_bvalues = bvl+k;
+ pmods[j] = lr->lr_lm+j;
+ j++;
+ }
+ bvl[k++] = lr->lr_vals+i;
+ }
+ bvl[k] = NULL;
+ pmods[j] = NULL;
+ goto doit;
+ }
+
+ lr->lr_mops = ber_memalloc_x( lr->lr_lines+1, ctx );
+ if ( lr->lr_mops == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto leave;
+ }
+
+ lr->lr_mops[lr->lr_lines] = M_SEP;
+ if ( i > 0 )
+ lr->lr_mops[i-1] = M_SEP;
+
+ for ( ; i<lr->lr_lines; i++ ) {
+ if ( expect_modop ) {
+#ifdef LIBERAL_CHANGETYPE_MODOP
+ /* trim trailing spaces (and log warning ...) */
+ int icnt;
+ for ( icnt = lr->lr_vals[i].bv_len; --icnt > 0; ) {
+ if ( !isspace( (unsigned char) lr->lr_vals[i].bv_val[icnt] ) ) break;
+ }
+
+ if ( ++icnt != lr->lr_vals[i].bv_len ) {
+ fprintf( stderr, _("%s: illegal trailing space after"
+ " \"%s: %s\" trimmed (line %lu, entry \"%s\")\n"),
+ errstr, type, lr->lr_vals[i].bv_val, linenum+i, dn );
+ lr->lr_vals[i].bv_val[icnt] = '\0';
+ }
+#endif /* LIBERAL_CHANGETYPE_MODOP */
+
+ expect_modop = 0;
+ expect_sep = 1;
+ if ( BV_CASEMATCH( lr->lr_btype+i, &BV_MODOPADD )) {
+ modop = LDAP_MOD_ADD;
+ lr->lr_mops[i] = M_SEP;
+ nmods--;
+ } else if ( BV_CASEMATCH( lr->lr_btype+i, &BV_MODOPREPLACE )) {
+ /* defer handling these since they might have no values.
+ * Use the BVALUES flag to signal that these were
+ * deferred. If values are provided later, this
+ * flag will be switched off.
+ */
+ modop = LDAP_MOD_REPLACE;
+ lr->lr_mops[i] = modop | LDAP_MOD_BVALUES;
+ lr->lr_btype[i] = lr->lr_vals[i];
+ } else if ( BV_CASEMATCH( lr->lr_btype+i, &BV_MODOPDELETE )) {
+ modop = LDAP_MOD_DELETE;
+ lr->lr_mops[i] = modop | LDAP_MOD_BVALUES;
+ lr->lr_btype[i] = lr->lr_vals[i];
+ } else if ( BV_CASEMATCH( lr->lr_btype+i, &BV_MODOPINCREMENT )) {
+ modop = LDAP_MOD_INCREMENT;
+ lr->lr_mops[i] = M_SEP;
+ nmods--;
+ } else { /* no modify op: invalid LDIF */
+ fprintf( stderr, _("%s: modify operation type is missing at"
+ " line %lu, entry \"%s\"\n"),
+ errstr, linenum+i, dn );
+ rc = LDAP_PARAM_ERROR;
+ goto leave;
+ }
+ bv = lr->lr_vals[i];
+ } else if ( expect_sep && BER_BVISEMPTY( lr->lr_btype+i )) {
+ lr->lr_mops[i] = M_SEP;
+ expect_sep = 0;
+ expect_modop = 1;
+ nmods--;
+ } else {
+ if ( !BV_CASEMATCH( lr->lr_btype+i, &bv )) {
+ fprintf( stderr, _("%s: wrong attributeType at"
+ " line %lu, entry \"%s\"\n"),
+ errstr, linenum+i, dn );
+ rc = LDAP_PARAM_ERROR;
+ goto leave;
+ }
+ lr->lr_mops[i] = modop;
+ /* If prev op was deferred and matches this type,
+ * clear the flag
+ */
+ if ( (lr->lr_mops[i-1] & LDAP_MOD_BVALUES)
+ && BV_CASEMATCH( lr->lr_btype+i, lr->lr_btype+i-1 ))
+ {
+ lr->lr_mops[i-1] = M_SEP;
+ nmods--;
+ }
+ }
+ }
+
+ /* Allocate space for array of mods, array of pointers to mods,
+ * and array of pointers to values, allowing for NULL terminators
+ * for the pointer arrays...
+ */
+ lr->lr_lm = ber_memalloc_x( nmods * sizeof(LDAPMod) +
+ (nmods+1) * sizeof(LDAPMod*) +
+ (lr->lr_lines + nmods - idn) * sizeof(struct berval *), ctx );
+ if ( lr->lr_lm == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto leave;
+ }
+
+ pmods = (LDAPMod **)(lr->lr_lm+nmods);
+ bvl = (struct berval **)(pmods+nmods+1);
+
+ j = 0;
+ k = -1;
+ BER_BVZERO(&bv);
+ if ( idn > 0 )
+ lr->lr_mops[idn-1] = M_SEP;
+ for (i=idn; i<lr->lr_lines; i++) {
+ if ( lr->lr_mops[i] == M_SEP )
+ continue;
+ if ( lr->lr_mops[i] != lr->lr_mops[i-1] || !BV_CASEMATCH( lr->lr_btype+i, &bv )) {
+ bvl[k++] = NULL;
+ bv = lr->lr_btype[i];
+ lr->lr_lm[j].mod_op = lr->lr_mops[i] | LDAP_MOD_BVALUES;
+ lr->lr_lm[j].mod_type = bv.bv_val;
+ if ( lr->lr_mops[i] & LDAP_MOD_BVALUES ) {
+ lr->lr_lm[j].mod_bvalues = NULL;
+ } else {
+ lr->lr_lm[j].mod_bvalues = bvl+k;
+ }
+ pmods[j] = lr->lr_lm+j;
+ j++;
+ }
+ bvl[k++] = lr->lr_vals+i;
+ }
+ bvl[k] = NULL;
+ pmods[j] = NULL;
+
+doit:
+ /* first, set the common fields */
+ lr->lr_ctrls = pctrls;
+ /* next, set the op */
+ if ( delete_entry ) {
+ lr->lr_op = LDAP_REQ_DELETE;
+ } else if ( lr->lrop_newrdn.bv_val != NULL ) {
+ lr->lr_op = LDAP_REQ_MODDN;
+ } else {
+ /* for now, either add or modify */
+ lr->lrop_mods = pmods;
+ if ( new_entry ) {
+ lr->lr_op = LDAP_REQ_ADD;
+ } else {
+ lr->lr_op = LDAP_REQ_MODIFY;
+ }
+ }
+
+leave:
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_ldif_record_done( lr );
+ }
+
+ return( rc );
+}
+
+/* Same as ldap_parse_ldif_record_x()
+ * public API does not expose memory context
+ */
+int
+ldap_parse_ldif_record(
+ struct berval *rbuf,
+ unsigned long linenum,
+ LDIFRecord *lr,
+ const char *errstr,
+ unsigned int flags )
+{
+ return ldap_parse_ldif_record_x( rbuf, linenum, lr, errstr, flags, NULL );
+}
+
+/* Parse an LDIF control line of the form
+ control: oid [true/false] [: value] or
+ control: oid [true/false] [:: base64-value] or
+ control: oid [true/false] [:< url]
+ The control is added to the list of controls in *ppctrls.
+*/
+static int
+parse_ldif_control(
+ struct berval *bval,
+ LDAPControl ***ppctrls)
+{
+ char *oid = NULL;
+ int criticality = 0; /* Default is false if not present */
+ int i, rc=0;
+ char *s, *oidStart;
+ LDAPControl *newctrl = NULL;
+ LDAPControl **pctrls = NULL;
+ struct berval type, bv = BER_BVNULL;
+ int freeval = 0;
+
+ if (ppctrls) pctrls = *ppctrls;
+ /* OID should come first. Validate and extract it. */
+ s = bval->bv_val;
+ if (*s == 0) return ( LDAP_PARAM_ERROR );
+ oidStart = s;
+ while (isdigit((unsigned char)*s) || *s == '.') {
+ s++; /* OID should be digits or . */
+ }
+ if (s == oidStart) {
+ return ( LDAP_PARAM_ERROR ); /* OID was not present */
+ }
+ if (*s) { /* End of OID should be space or NULL */
+ if (!isspace((unsigned char)*s)) {
+ return ( LDAP_PARAM_ERROR ); /* else OID contained invalid chars */
+ }
+ *s++ = 0; /* Replace space with null to terminate */
+ }
+
+ oid = ber_strdup(oidStart);
+ if (oid == NULL) return ( LDAP_NO_MEMORY );
+
+ /* Optional Criticality field is next. */
+ while (*s && isspace((unsigned char)*s)) {
+ s++; /* Skip white space before criticality */
+ }
+ if (strncasecmp(s, "true", 4) == 0) {
+ criticality = 1;
+ s += 4;
+ }
+ else if (strncasecmp(s, "false", 5) == 0) {
+ criticality = 0;
+ s += 5;
+ }
+
+ /* Optional value field is next */
+ while (*s && isspace((unsigned char)*s)) {
+ s++; /* Skip white space before value */
+ }
+ if (*s) {
+ if (*s != ':') { /* If value is present, must start with : */
+ rc = LDAP_PARAM_ERROR;
+ goto cleanup;
+ }
+
+ /* Back up so value is in the form
+ a: value
+ a:: base64-value
+ a:< url
+ Then we can use ldif_parse_line2 to extract and decode the value
+ */
+ s--;
+ *s = 'a';
+
+ rc = ldif_parse_line2(s, &type, &bv, &freeval);
+ if (rc < 0) {
+ rc = LDAP_PARAM_ERROR;
+ goto cleanup;
+ }
+ }
+
+ /* Create a new LDAPControl structure. */
+ newctrl = (LDAPControl *)ber_memalloc(sizeof(LDAPControl));
+ if ( newctrl == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto cleanup;
+ }
+ newctrl->ldctl_oid = oid;
+ oid = NULL;
+ newctrl->ldctl_iscritical = criticality;
+ if ( freeval )
+ newctrl->ldctl_value = bv;
+ else
+ ber_dupbv( &newctrl->ldctl_value, &bv );
+
+ /* Add the new control to the passed-in list of controls. */
+ i = 0;
+ if (pctrls) {
+ while ( pctrls[i] ) { /* Count the # of controls passed in */
+ i++;
+ }
+ }
+ /* Allocate 1 more slot for the new control and 1 for the NULL. */
+ pctrls = (LDAPControl **) ber_memrealloc(pctrls,
+ (i+2)*(sizeof(LDAPControl *)));
+ if (pctrls == NULL) {
+ rc = LDAP_NO_MEMORY;
+ goto cleanup;
+ }
+ pctrls[i] = newctrl;
+ newctrl = NULL;
+ pctrls[i+1] = NULL;
+ *ppctrls = pctrls;
+
+cleanup:
+ if (newctrl) {
+ if (newctrl->ldctl_oid) ber_memfree(newctrl->ldctl_oid);
+ if (newctrl->ldctl_value.bv_val) {
+ ber_memfree(newctrl->ldctl_value.bv_val);
+ }
+ ber_memfree(newctrl);
+ }
+ if (oid) ber_memfree(oid);
+
+ return( rc );
+}
+
+
diff --git a/libraries/libldap/libldap.vers.in b/libraries/libldap/libldap.vers.in
new file mode 100644
index 0000000..3a9ad89
--- /dev/null
+++ b/libraries/libldap/libldap.vers.in
@@ -0,0 +1,16 @@
+HIDDEN
+{
+ local:
+ __*;
+ _rest*;
+ _save*;
+};
+
+OPENLDAP_@OPENLDAP_LIBRELEASE@
+{
+ global:
+ ldap_*;
+ ldif_*;
+ local: *;
+};
+
diff --git a/libraries/libldap/messages.c b/libraries/libldap/messages.c
new file mode 100644
index 0000000..c4bf732
--- /dev/null
+++ b/libraries/libldap/messages.c
@@ -0,0 +1,68 @@
+/* messages.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+LDAPMessage *
+ldap_first_message( LDAP *ld, LDAPMessage *chain )
+{
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( chain != NULL );
+
+ return chain;
+}
+
+LDAPMessage *
+ldap_next_message( LDAP *ld, LDAPMessage *msg )
+{
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( msg != NULL );
+
+ return msg->lm_chain;
+}
+
+int
+ldap_count_messages( LDAP *ld, LDAPMessage *chain )
+{
+ int i;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ for ( i = 0; chain != NULL; chain = chain->lm_chain ) {
+ i++;
+ }
+
+ return( i );
+}
+
+BerElement*
+ldap_get_message_ber( LDAPMessage *ld )
+{
+ return ld->lm_ber;
+}
diff --git a/libraries/libldap/modify.c b/libraries/libldap/modify.c
new file mode 100644
index 0000000..cb8d85a
--- /dev/null
+++ b/libraries/libldap/modify.c
@@ -0,0 +1,233 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/* A modify request/response looks like this:
+ * ModifyRequest ::= [APPLICATION 6] SEQUENCE {
+ * object LDAPDN,
+ * changes SEQUENCE OF change SEQUENCE {
+ * operation ENUMERATED {
+ * add (0),
+ * delete (1),
+ * replace (2),
+ * ... },
+ * modification PartialAttribute } }
+ *
+ * PartialAttribute ::= SEQUENCE {
+ * type AttributeDescription,
+ * vals SET OF value AttributeValue }
+ *
+ * AttributeDescription ::= LDAPString
+ * -- Constrained to <attributedescription> [RFC4512]
+ *
+ * AttributeValue ::= OCTET STRING
+ *
+ * ModifyResponse ::= [APPLICATION 7] LDAPResult
+ *
+ * (Source: RFC 4511)
+ */
+
+BerElement *
+ldap_build_modify_req(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPMod **mods,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp )
+{
+ BerElement *ber;
+ int i, rc;
+
+ /* create a message to send */
+ if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
+ return( NULL );
+ }
+
+ LDAP_NEXT_MSGID( ld, *msgidp );
+ rc = ber_printf( ber, "{it{s{" /*}}}*/, *msgidp, LDAP_REQ_MODIFY, dn );
+ if ( rc == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ /* allow mods to be NULL ("touch") */
+ if ( mods ) {
+ /* for each modification to be performed... */
+ for ( i = 0; mods[i] != NULL; i++ ) {
+ if (( mods[i]->mod_op & LDAP_MOD_BVALUES) != 0 ) {
+ rc = ber_printf( ber, "{e{s[V]N}N}",
+ (ber_int_t) ( mods[i]->mod_op & ~LDAP_MOD_BVALUES ),
+ mods[i]->mod_type, mods[i]->mod_bvalues );
+ } else {
+ rc = ber_printf( ber, "{e{s[v]N}N}",
+ (ber_int_t) mods[i]->mod_op,
+ mods[i]->mod_type, mods[i]->mod_values );
+ }
+
+ if ( rc == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+ }
+ }
+
+ if ( ber_printf( ber, /*{{*/ "N}N}" ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ /* Put Server Controls */
+ if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ return( ber );
+}
+
+/*
+ * ldap_modify_ext - initiate an ldap extended modify operation.
+ *
+ * Parameters:
+ *
+ * ld LDAP descriptor
+ * dn DN of the object to modify
+ * mods List of modifications to make. This is null-terminated
+ * array of struct ldapmod's, specifying the modifications
+ * to perform.
+ * sctrls Server Controls
+ * cctrls Client Controls
+ * msgidp Message ID pointer
+ *
+ * Example:
+ * LDAPMod *mods[] = {
+ * { LDAP_MOD_ADD, "cn", { "babs jensen", "babs", 0 } },
+ * { LDAP_MOD_REPLACE, "sn", { "babs jensen", "babs", 0 } },
+ * { LDAP_MOD_DELETE, "ou", 0 },
+ * { LDAP_MOD_INCREMENT, "uidNumber, { "1", 0 } }
+ * 0
+ * }
+ * rc= ldap_modify_ext( ld, dn, mods, sctrls, cctrls, &msgid );
+ */
+int
+ldap_modify_ext( LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPMod **mods,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ BerElement *ber;
+ int rc;
+ ber_int_t id;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_modify_ext\n" );
+
+ /* check client controls */
+ rc = ldap_int_client_controls( ld, cctrls );
+ if( rc != LDAP_SUCCESS ) return rc;
+
+ ber = ldap_build_modify_req( ld, dn, mods, sctrls, cctrls, &id );
+ if( !ber )
+ return ld->ld_errno;
+
+ /* send the message */
+ *msgidp = ldap_send_initial_request( ld, LDAP_REQ_MODIFY, dn, ber, id );
+ return( *msgidp < 0 ? ld->ld_errno : LDAP_SUCCESS );
+}
+
+/*
+ * ldap_modify - initiate an ldap modify operation.
+ *
+ * Parameters:
+ *
+ * ld LDAP descriptor
+ * dn DN of the object to modify
+ * mods List of modifications to make. This is null-terminated
+ * array of struct ldapmod's, specifying the modifications
+ * to perform.
+ *
+ * Example:
+ * LDAPMod *mods[] = {
+ * { LDAP_MOD_ADD, "cn", { "babs jensen", "babs", 0 } },
+ * { LDAP_MOD_REPLACE, "sn", { "babs jensen", "babs", 0 } },
+ * { LDAP_MOD_DELETE, "ou", 0 },
+ * { LDAP_MOD_INCREMENT, "uidNumber, { "1", 0 } }
+ * 0
+ * }
+ * msgid = ldap_modify( ld, dn, mods );
+ */
+int
+ldap_modify( LDAP *ld, LDAP_CONST char *dn, LDAPMod **mods )
+{
+ int rc, msgid;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_modify\n" );
+
+ rc = ldap_modify_ext( ld, dn, mods, NULL, NULL, &msgid );
+
+ if ( rc != LDAP_SUCCESS )
+ return -1;
+
+ return msgid;
+}
+
+int
+ldap_modify_ext_s( LDAP *ld, LDAP_CONST char *dn,
+ LDAPMod **mods, LDAPControl **sctrl, LDAPControl **cctrl )
+{
+ int rc;
+ int msgid;
+ LDAPMessage *res;
+
+ rc = ldap_modify_ext( ld, dn, mods, sctrl, cctrl, &msgid );
+
+ if ( rc != LDAP_SUCCESS )
+ return( rc );
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res )
+ return( ld->ld_errno );
+
+ return( ldap_result2error( ld, res, 1 ) );
+}
+
+int
+ldap_modify_s( LDAP *ld, LDAP_CONST char *dn, LDAPMod **mods )
+{
+ return ldap_modify_ext_s( ld, dn, mods, NULL, NULL );
+}
+
diff --git a/libraries/libldap/modrdn.c b/libraries/libldap/modrdn.c
new file mode 100644
index 0000000..756762e
--- /dev/null
+++ b/libraries/libldap/modrdn.c
@@ -0,0 +1,273 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+/* Copyright 1999, Juan C. Gomez, All rights reserved.
+ * This software is not subject to any license of Silicon Graphics
+ * Inc. or Purdue University.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * without restriction or fee of any kind as long as this notice
+ * is preserved.
+ */
+
+/* ACKNOWLEDGEMENTS:
+ * Juan C. Gomez
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/*
+ * A modify rdn request looks like this:
+ * ModifyRDNRequest ::= SEQUENCE {
+ * entry DistinguishedName,
+ * newrdn RelativeDistinguishedName,
+ * deleteoldrdn BOOLEAN
+ * newSuperior [0] DistinguishedName [v3 only]
+ * }
+ */
+
+BerElement *
+ldap_build_moddn_req(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *newrdn,
+ LDAP_CONST char *newSuperior,
+ int deleteoldrdn,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp )
+{
+ BerElement *ber;
+ int rc;
+
+ /* create a message to send */
+ if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
+ return( NULL );
+ }
+
+ LDAP_NEXT_MSGID( ld, *msgidp );
+ if( newSuperior != NULL ) {
+ /* must be version 3 (or greater) */
+ if ( ld->ld_version < LDAP_VERSION3 ) {
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+ rc = ber_printf( ber, "{it{ssbtsN}", /* '}' */
+ *msgidp, LDAP_REQ_MODDN,
+ dn, newrdn, (ber_int_t) deleteoldrdn,
+ LDAP_TAG_NEWSUPERIOR, newSuperior );
+
+ } else {
+ rc = ber_printf( ber, "{it{ssbN}", /* '}' */
+ *msgidp, LDAP_REQ_MODDN,
+ dn, newrdn, (ber_int_t) deleteoldrdn );
+ }
+
+ if ( rc < 0 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ /* Put Server Controls */
+ if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ rc = ber_printf( ber, /*{*/ "N}" );
+ if ( rc < 0 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ return( ber );
+}
+
+/*
+ * ldap_rename - initiate an ldap extended modifyDN operation.
+ *
+ * Parameters:
+ * ld LDAP descriptor
+ * dn DN of the object to modify
+ * newrdn RDN to give the object
+ * deleteoldrdn nonzero means to delete old rdn values from the entry
+ * newSuperior DN of the new parent if applicable
+ *
+ * Returns the LDAP error code.
+ */
+
+int
+ldap_rename(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *newrdn,
+ LDAP_CONST char *newSuperior,
+ int deleteoldrdn,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ BerElement *ber;
+ int rc;
+ ber_int_t id;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_rename\n" );
+
+ /* check client controls */
+ rc = ldap_int_client_controls( ld, cctrls );
+ if( rc != LDAP_SUCCESS ) return rc;
+
+ ber = ldap_build_moddn_req( ld, dn, newrdn, newSuperior,
+ deleteoldrdn, sctrls, cctrls, &id );
+ if( !ber )
+ return ld->ld_errno;
+
+ /* send the message */
+ *msgidp = ldap_send_initial_request( ld, LDAP_REQ_MODRDN, dn, ber, id );
+
+ if( *msgidp < 0 ) {
+ return( ld->ld_errno );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+
+/*
+ * ldap_rename2 - initiate an ldap (and X.500) modifyDN operation. Parameters:
+ * (LDAP V3 MODIFYDN REQUEST)
+ * ld LDAP descriptor
+ * dn DN of the object to modify
+ * newrdn RDN to give the object
+ * deleteoldrdn nonzero means to delete old rdn values from the entry
+ * newSuperior DN of the new parent if applicable
+ *
+ * ldap_rename2 uses a U-Mich Style API. It returns the msgid.
+ */
+
+int
+ldap_rename2(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *newrdn,
+ LDAP_CONST char *newSuperior,
+ int deleteoldrdn )
+{
+ int msgid;
+ int rc;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_rename2\n" );
+
+ rc = ldap_rename( ld, dn, newrdn, newSuperior,
+ deleteoldrdn, NULL, NULL, &msgid );
+
+ return rc == LDAP_SUCCESS ? msgid : -1;
+}
+
+
+/*
+ * ldap_modrdn2 - initiate an ldap modifyRDN operation. Parameters:
+ *
+ * ld LDAP descriptor
+ * dn DN of the object to modify
+ * newrdn RDN to give the object
+ * deleteoldrdn nonzero means to delete old rdn values from the entry
+ *
+ * Example:
+ * msgid = ldap_modrdn( ld, dn, newrdn );
+ */
+int
+ldap_modrdn2( LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *newrdn,
+ int deleteoldrdn )
+{
+ return ldap_rename2( ld, dn, newrdn, NULL, deleteoldrdn );
+}
+
+int
+ldap_modrdn( LDAP *ld, LDAP_CONST char *dn, LDAP_CONST char *newrdn )
+{
+ return( ldap_rename2( ld, dn, newrdn, NULL, 1 ) );
+}
+
+
+int
+ldap_rename_s(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *newrdn,
+ LDAP_CONST char *newSuperior,
+ int deleteoldrdn,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ int rc;
+ int msgid;
+ LDAPMessage *res;
+
+ rc = ldap_rename( ld, dn, newrdn, newSuperior,
+ deleteoldrdn, sctrls, cctrls, &msgid );
+
+ if( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ rc = ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &res );
+
+ if( rc == -1 || !res ) {
+ return ld->ld_errno;
+ }
+
+ return ldap_result2error( ld, res, 1 );
+}
+
+int
+ldap_rename2_s(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *newrdn,
+ LDAP_CONST char *newSuperior,
+ int deleteoldrdn )
+{
+ return ldap_rename_s( ld, dn, newrdn, newSuperior,
+ deleteoldrdn, NULL, NULL );
+}
+
+int
+ldap_modrdn2_s( LDAP *ld, LDAP_CONST char *dn, LDAP_CONST char *newrdn, int deleteoldrdn )
+{
+ return ldap_rename_s( ld, dn, newrdn, NULL, deleteoldrdn, NULL, NULL );
+}
+
+int
+ldap_modrdn_s( LDAP *ld, LDAP_CONST char *dn, LDAP_CONST char *newrdn )
+{
+ return ldap_rename_s( ld, dn, newrdn, NULL, 1, NULL, NULL );
+}
+
diff --git a/libraries/libldap/msctrl.c b/libraries/libldap/msctrl.c
new file mode 100644
index 0000000..0f9264e
--- /dev/null
+++ b/libraries/libldap/msctrl.c
@@ -0,0 +1,280 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2018 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Howard Chu for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/* MS Active Directory controls - not implemented in slapd(8) */
+
+#ifdef LDAP_CONTROL_X_DIRSYNC
+
+int
+ldap_create_dirsync_value(
+ LDAP *ld,
+ int flags,
+ int maxAttrCount,
+ struct berval *cookie,
+ struct berval *value )
+{
+ BerElement *ber = NULL;
+ ber_tag_t tag;
+
+ if ( ld == NULL || cookie == NULL ||
+ value == NULL )
+ {
+ if ( ld ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ }
+
+ return LDAP_PARAM_ERROR;
+ }
+
+ assert( LDAP_VALID( ld ) );
+ ld->ld_errno = LDAP_SUCCESS;
+
+ /* maxAttrCount less than 0x100000 is treated as 0x100000 by server */
+
+ /* prepare value */
+ value->bv_val = NULL;
+ value->bv_len = 0;
+
+ ber = ldap_alloc_ber_with_options( ld );
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ tag = ber_printf( ber, "{iiO}", flags, maxAttrCount, cookie );
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+
+ if ( ber_flatten2( ber, value, 1 ) == -1 ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ }
+
+done:;
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ return ld->ld_errno;
+}
+
+int
+ldap_create_dirsync_control(
+ LDAP *ld,
+ int flags,
+ int maxAttrCount,
+ struct berval *cookie,
+ LDAPControl **ctrlp )
+{
+ struct berval value;
+
+ if ( ctrlp == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ ld->ld_errno = ldap_create_dirsync_value( ld,
+ flags, maxAttrCount, cookie, &value );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ ld->ld_errno = ldap_control_create( LDAP_CONTROL_X_DIRSYNC,
+ 1, &value, 0, ctrlp );
+ if ( ld->ld_errno != LDAP_SUCCESS ) {
+ LDAP_FREE( value.bv_val );
+ }
+ }
+
+ return ld->ld_errno;
+}
+
+int
+ldap_parse_dirsync_control(
+ LDAP *ld,
+ LDAPControl *ctrl,
+ int *continueFlag,
+ struct berval *cookie )
+{
+ BerElement *ber;
+ ber_tag_t tag;
+ ber_len_t len;
+ int unused;
+
+ if ( ld == NULL ||
+ ctrl == NULL ||
+ continueFlag == NULL ||
+ cookie == NULL )
+ {
+ if ( ld ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ }
+
+ /* NOTE: we want the caller to get all or nothing;
+ * we could allow some of the pointers to be NULL,
+ * if one does not want part of the data */
+ return LDAP_PARAM_ERROR;
+ }
+
+ *continueFlag = 0;
+ BER_BVZERO( cookie );
+
+ ber = ber_init( &ctrl->ldctl_value );
+
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ tag = ber_scanf( ber, "{iio}", continueFlag, &unused, cookie );
+ if ( tag == LBER_DEFAULT )
+ tag = LBER_ERROR;
+
+ (void)ber_free( ber, 1 );
+
+ if ( tag == LBER_ERROR ) {
+ return LDAP_DECODING_ERROR;
+ }
+
+ return ld->ld_errno;
+}
+
+#endif /* LDAP_CONTROL_X_DIRSYNC */
+
+#ifdef LDAP_CONTROL_X_SHOW_DELETED
+
+int
+ldap_create_show_deleted_control( LDAP *ld,
+ LDAPControl **ctrlp )
+{
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( ctrlp != NULL );
+
+ ld->ld_errno = ldap_control_create( LDAP_CONTROL_X_SHOW_DELETED,
+ 0, NULL, 0, ctrlp );
+
+ return ld->ld_errno;
+}
+
+#endif /* LDAP_CONTROL_X_SHOW_DELETED */
+
+#ifdef LDAP_CONTROL_X_EXTENDED_DN
+
+int
+ldap_create_extended_dn_value(
+ LDAP *ld,
+ int flag,
+ struct berval *value )
+{
+ BerElement *ber = NULL;
+ ber_tag_t tag;
+
+ if ( ld == NULL ||
+ value == NULL )
+ {
+ if ( ld ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ }
+
+ return LDAP_PARAM_ERROR;
+ }
+
+ assert( LDAP_VALID( ld ) );
+ ld->ld_errno = LDAP_SUCCESS;
+
+ /* prepare value */
+ value->bv_val = NULL;
+ value->bv_len = 0;
+
+ ber = ldap_alloc_ber_with_options( ld );
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+ tag = ber_printf( ber, "{i}", flag );
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+
+ if ( ber_flatten2( ber, value, 1 ) == -1 ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ }
+
+done:;
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ return ld->ld_errno;
+}
+
+int
+ldap_create_extended_dn_control(
+ LDAP *ld,
+ int flag,
+ LDAPControl **ctrlp )
+{
+ struct berval value;
+
+ if ( ctrlp == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ ld->ld_errno = ldap_create_extended_dn_value( ld, flag, &value );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ ld->ld_errno = ldap_control_create( LDAP_CONTROL_X_EXTENDED_DN,
+ 0, &value, 0, ctrlp );
+ if ( ld->ld_errno != LDAP_SUCCESS ) {
+ LDAP_FREE( value.bv_val );
+ }
+ }
+
+ return ld->ld_errno;
+}
+
+#endif /* LDAP_CONTROL_X_EXTENDED_DN */
+
+#ifdef LDAP_CONTROL_X_SERVER_NOTIFICATION
+
+int
+ldap_create_server_notification_control( LDAP *ld,
+ LDAPControl **ctrlp )
+{
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( ctrlp != NULL );
+
+ ld->ld_errno = ldap_control_create( LDAP_CONTROL_X_SERVER_NOTIFICATION,
+ 0, NULL, 0, ctrlp );
+
+ return ld->ld_errno;
+}
+
+#endif /* LDAP_CONTROL_X_SERVER_NOTIFICATION */
diff --git a/libraries/libldap/open.c b/libraries/libldap/open.c
new file mode 100644
index 0000000..afef818
--- /dev/null
+++ b/libraries/libldap/open.c
@@ -0,0 +1,673 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include <ac/stdlib.h>
+
+#include <ac/param.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include <ac/unistd.h>
+
+#include "ldap-int.h"
+#include "ldap.h"
+#include "ldap_log.h"
+
+/* Caller must hold the conn_mutex since simultaneous accesses are possible */
+int ldap_open_defconn( LDAP *ld )
+{
+ ld->ld_defconn = ldap_new_connection( ld,
+ &ld->ld_options.ldo_defludp, 1, 1, NULL, 0, 0 );
+
+ if( ld->ld_defconn == NULL ) {
+ ld->ld_errno = LDAP_SERVER_DOWN;
+ return -1;
+ }
+
+ ++ld->ld_defconn->lconn_refcnt; /* so it never gets closed/freed */
+ return 0;
+}
+
+/*
+ * ldap_connect - Connect to an ldap server.
+ *
+ * Example:
+ * LDAP *ld;
+ * ldap_initialize( &ld, url );
+ * ldap_connect( ld );
+ */
+int
+ldap_connect( LDAP *ld )
+{
+ ber_socket_t sd = AC_SOCKET_INVALID;
+ int rc = LDAP_SUCCESS;
+
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ) == -1 ) {
+ rc = ldap_open_defconn( ld );
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+
+ return rc;
+}
+
+/*
+ * ldap_open - initialize and connect to an ldap server. A magic cookie to
+ * be used for future communication is returned on success, NULL on failure.
+ * "host" may be a space-separated list of hosts or IP addresses
+ *
+ * Example:
+ * LDAP *ld;
+ * ld = ldap_open( hostname, port );
+ */
+
+LDAP *
+ldap_open( LDAP_CONST char *host, int port )
+{
+ int rc;
+ LDAP *ld;
+
+ Debug2( LDAP_DEBUG_TRACE, "ldap_open(%s, %d)\n",
+ host, port );
+
+ ld = ldap_init( host, port );
+ if ( ld == NULL ) {
+ return( NULL );
+ }
+
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ rc = ldap_open_defconn( ld );
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+
+ if( rc < 0 ) {
+ ldap_ld_free( ld, 0, NULL, NULL );
+ ld = NULL;
+ }
+
+ Debug1( LDAP_DEBUG_TRACE, "ldap_open: %s\n",
+ ld != NULL ? "succeeded" : "failed" );
+
+ return ld;
+}
+
+
+
+int
+ldap_create( LDAP **ldp )
+{
+ LDAP *ld;
+ struct ldapoptions *gopts;
+
+ *ldp = NULL;
+ /* Get pointer to global option structure */
+ if ( (gopts = LDAP_INT_GLOBAL_OPT()) == NULL) {
+ return LDAP_NO_MEMORY;
+ }
+
+ /* Initialize the global options, if not already done. */
+ if( gopts->ldo_valid != LDAP_INITIALIZED ) {
+ ldap_int_initialize(gopts, NULL);
+ if ( gopts->ldo_valid != LDAP_INITIALIZED )
+ return LDAP_LOCAL_ERROR;
+ }
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_create\n" );
+
+ if ( (ld = (LDAP *) LDAP_CALLOC( 1, sizeof(LDAP) )) == NULL ) {
+ return( LDAP_NO_MEMORY );
+ }
+
+ if ( (ld->ldc = (struct ldap_common *) LDAP_CALLOC( 1,
+ sizeof(struct ldap_common) )) == NULL ) {
+ LDAP_FREE( (char *)ld );
+ return( LDAP_NO_MEMORY );
+ }
+ /* copy the global options */
+ LDAP_MUTEX_LOCK( &gopts->ldo_mutex );
+ AC_MEMCPY(&ld->ld_options, gopts, sizeof(ld->ld_options));
+#ifdef LDAP_R_COMPILE
+ /* Properly initialize the structs mutex */
+ ldap_pvt_thread_mutex_init( &(ld->ld_ldopts_mutex) );
+#endif
+
+#ifdef HAVE_TLS
+ if ( ld->ld_options.ldo_tls_pin_hashalg ) {
+ int len = strlen( gopts->ldo_tls_pin_hashalg );
+
+ ld->ld_options.ldo_tls_pin_hashalg =
+ LDAP_MALLOC( len + 1 + gopts->ldo_tls_pin.bv_len );
+ if ( !ld->ld_options.ldo_tls_pin_hashalg ) goto nomem;
+
+ ld->ld_options.ldo_tls_pin.bv_val = ld->ld_options.ldo_tls_pin_hashalg
+ + len + 1;
+ AC_MEMCPY( ld->ld_options.ldo_tls_pin_hashalg, gopts->ldo_tls_pin_hashalg,
+ len + 1 + gopts->ldo_tls_pin.bv_len );
+ } else if ( !BER_BVISEMPTY(&ld->ld_options.ldo_tls_pin) ) {
+ ber_dupbv( &ld->ld_options.ldo_tls_pin, &gopts->ldo_tls_pin );
+ }
+#endif
+ LDAP_MUTEX_UNLOCK( &gopts->ldo_mutex );
+
+ ld->ld_valid = LDAP_VALID_SESSION;
+
+ /* but not pointers to malloc'ed items */
+ ld->ld_options.ldo_sctrls = NULL;
+ ld->ld_options.ldo_cctrls = NULL;
+ ld->ld_options.ldo_defludp = NULL;
+ ld->ld_options.ldo_conn_cbs = NULL;
+
+ ld->ld_options.ldo_defbase = gopts->ldo_defbase
+ ? LDAP_STRDUP( gopts->ldo_defbase ) : NULL;
+
+#ifdef HAVE_CYRUS_SASL
+ ld->ld_options.ldo_def_sasl_mech = gopts->ldo_def_sasl_mech
+ ? LDAP_STRDUP( gopts->ldo_def_sasl_mech ) : NULL;
+ ld->ld_options.ldo_def_sasl_realm = gopts->ldo_def_sasl_realm
+ ? LDAP_STRDUP( gopts->ldo_def_sasl_realm ) : NULL;
+ ld->ld_options.ldo_def_sasl_authcid = gopts->ldo_def_sasl_authcid
+ ? LDAP_STRDUP( gopts->ldo_def_sasl_authcid ) : NULL;
+ ld->ld_options.ldo_def_sasl_authzid = gopts->ldo_def_sasl_authzid
+ ? LDAP_STRDUP( gopts->ldo_def_sasl_authzid ) : NULL;
+#endif
+
+#ifdef HAVE_TLS
+ /* We explicitly inherit the SSL_CTX, don't need the names/paths. Leave
+ * them empty to allow new SSL_CTX's to be created from scratch.
+ */
+ memset( &ld->ld_options.ldo_tls_info, 0,
+ sizeof( ld->ld_options.ldo_tls_info ));
+ ld->ld_options.ldo_tls_ctx = NULL;
+#endif
+
+ if ( gopts->ldo_defludp ) {
+ ld->ld_options.ldo_defludp = ldap_url_duplist(gopts->ldo_defludp);
+
+ if ( ld->ld_options.ldo_defludp == NULL ) goto nomem;
+ }
+
+ if (( ld->ld_selectinfo = ldap_new_select_info()) == NULL ) goto nomem;
+
+ ld->ld_options.ldo_local_ip_addrs.local_ip_addrs = NULL;
+ if( gopts->ldo_local_ip_addrs.local_ip_addrs ) {
+ ld->ld_options.ldo_local_ip_addrs.local_ip_addrs =
+ LDAP_STRDUP( gopts->ldo_local_ip_addrs.local_ip_addrs );
+ if ( ld->ld_options.ldo_local_ip_addrs.local_ip_addrs == NULL )
+ goto nomem;
+ }
+
+ ld->ld_lberoptions = LBER_USE_DER;
+
+ ld->ld_sb = ber_sockbuf_alloc( );
+ if ( ld->ld_sb == NULL ) goto nomem;
+
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_init( &ld->ld_msgid_mutex );
+ ldap_pvt_thread_mutex_init( &ld->ld_conn_mutex );
+ ldap_pvt_thread_mutex_init( &ld->ld_req_mutex );
+ ldap_pvt_thread_mutex_init( &ld->ld_res_mutex );
+ ldap_pvt_thread_mutex_init( &ld->ld_abandon_mutex );
+ ldap_pvt_thread_mutex_init( &ld->ld_ldcmutex );
+#endif
+ ld->ld_ldcrefcnt = 1;
+ *ldp = ld;
+ return LDAP_SUCCESS;
+
+nomem:
+ ldap_free_select_info( ld->ld_selectinfo );
+ ldap_free_urllist( ld->ld_options.ldo_defludp );
+#ifdef HAVE_CYRUS_SASL
+ LDAP_FREE( ld->ld_options.ldo_def_sasl_authzid );
+ LDAP_FREE( ld->ld_options.ldo_def_sasl_authcid );
+ LDAP_FREE( ld->ld_options.ldo_def_sasl_realm );
+ LDAP_FREE( ld->ld_options.ldo_def_sasl_mech );
+#endif
+
+#ifdef HAVE_TLS
+ /* tls_pin_hashalg and tls_pin share the same buffer */
+ if ( ld->ld_options.ldo_tls_pin_hashalg ) {
+ LDAP_FREE( ld->ld_options.ldo_tls_pin_hashalg );
+ } else {
+ LDAP_FREE( ld->ld_options.ldo_tls_pin.bv_val );
+ }
+#endif
+ LDAP_FREE( (char *)ld );
+ return LDAP_NO_MEMORY;
+}
+
+/*
+ * ldap_init - initialize the LDAP library. A magic cookie to be used for
+ * future communication is returned on success, NULL on failure.
+ * "host" may be a space-separated list of hosts or IP addresses
+ *
+ * Example:
+ * LDAP *ld;
+ * ld = ldap_init( host, port );
+ */
+LDAP *
+ldap_init( LDAP_CONST char *defhost, int defport )
+{
+ LDAP *ld;
+ int rc;
+
+ rc = ldap_create(&ld);
+ if ( rc != LDAP_SUCCESS )
+ return NULL;
+
+ if (defport != 0)
+ ld->ld_options.ldo_defport = defport;
+
+ if (defhost != NULL) {
+ rc = ldap_set_option(ld, LDAP_OPT_HOST_NAME, defhost);
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_ld_free(ld, 1, NULL, NULL);
+ return NULL;
+ }
+ }
+
+ return( ld );
+}
+
+
+int
+ldap_initialize( LDAP **ldp, LDAP_CONST char *url )
+{
+ int rc;
+ LDAP *ld;
+
+ *ldp = NULL;
+ rc = ldap_create(&ld);
+ if ( rc != LDAP_SUCCESS )
+ return rc;
+
+ if (url != NULL) {
+ rc = ldap_set_option(ld, LDAP_OPT_URI, url);
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_ld_free(ld, 1, NULL, NULL);
+ return rc;
+ }
+#ifdef LDAP_CONNECTIONLESS
+ if (ldap_is_ldapc_url(url))
+ LDAP_IS_UDP(ld) = 1;
+#endif
+ }
+
+ *ldp = ld;
+ return LDAP_SUCCESS;
+}
+
+int
+ldap_init_fd(
+ ber_socket_t fd,
+ int proto,
+ LDAP_CONST char *url,
+ LDAP **ldp
+)
+{
+ int rc;
+ LDAP *ld;
+ LDAPConn *conn;
+#ifdef LDAP_CONNECTIONLESS
+ ber_socklen_t len;
+#endif
+
+ *ldp = NULL;
+ rc = ldap_create( &ld );
+ if( rc != LDAP_SUCCESS )
+ return( rc );
+
+ if (url != NULL) {
+ rc = ldap_set_option(ld, LDAP_OPT_URI, url);
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_ld_free(ld, 1, NULL, NULL);
+ return rc;
+ }
+ }
+
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ /* Attach the passed socket as the LDAP's connection */
+ conn = ldap_new_connection( ld, NULL, 1, 0, NULL, 0, 0 );
+ if( conn == NULL ) {
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ ldap_unbind_ext( ld, NULL, NULL );
+ return( LDAP_NO_MEMORY );
+ }
+ if( url )
+ conn->lconn_server = ldap_url_dup( ld->ld_options.ldo_defludp );
+ ber_sockbuf_ctrl( conn->lconn_sb, LBER_SB_OPT_SET_FD, &fd );
+ ld->ld_defconn = conn;
+ ++ld->ld_defconn->lconn_refcnt; /* so it never gets closed/freed */
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+
+ switch( proto ) {
+ case LDAP_PROTO_TCP:
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)"tcp_" );
+#endif
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_tcp,
+ LBER_SBIOD_LEVEL_PROVIDER, NULL );
+ break;
+
+#ifdef LDAP_CONNECTIONLESS
+ case LDAP_PROTO_UDP:
+ LDAP_IS_UDP(ld) = 1;
+ if( ld->ld_options.ldo_peer )
+ ldap_memfree( ld->ld_options.ldo_peer );
+ ld->ld_options.ldo_peer = ldap_memcalloc( 1, sizeof( struct sockaddr_storage ) );
+ len = sizeof( struct sockaddr_storage );
+ if( getpeername ( fd, ld->ld_options.ldo_peer, &len ) < 0) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ return( AC_SOCKET_ERROR );
+ }
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)"udp_" );
+#endif
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_udp,
+ LBER_SBIOD_LEVEL_PROVIDER, NULL );
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_readahead,
+ LBER_SBIOD_LEVEL_PROVIDER, NULL );
+ break;
+#endif /* LDAP_CONNECTIONLESS */
+
+ case LDAP_PROTO_IPC:
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)"ipc_" );
+#endif
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_fd,
+ LBER_SBIOD_LEVEL_PROVIDER, NULL );
+ break;
+
+ case LDAP_PROTO_EXT:
+ /* caller must supply sockbuf handlers */
+ break;
+
+ default:
+ ldap_unbind_ext( ld, NULL, NULL );
+ return LDAP_PARAM_ERROR;
+ }
+
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
+ INT_MAX, (void *)"ldap_" );
+#endif
+
+ /* Add the connection to the *LDAP's select pool */
+ ldap_mark_select_read( ld, conn->lconn_sb );
+
+ *ldp = ld;
+ return LDAP_SUCCESS;
+}
+
+/* Protected by ld_conn_mutex */
+int
+ldap_int_open_connection(
+ LDAP *ld,
+ LDAPConn *conn,
+ LDAPURLDesc *srv,
+ int async )
+{
+ int rc = -1;
+ int proto;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_int_open_connection\n" );
+
+ switch ( proto = ldap_pvt_url_scheme2proto( srv->lud_scheme ) ) {
+ case LDAP_PROTO_TCP:
+ rc = ldap_connect_to_host( ld, conn->lconn_sb,
+ proto, srv, async );
+
+ if ( rc == -1 ) return rc;
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)"tcp_" );
+#endif
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_tcp,
+ LBER_SBIOD_LEVEL_PROVIDER, NULL );
+
+ break;
+
+#ifdef LDAP_CONNECTIONLESS
+ case LDAP_PROTO_UDP:
+ LDAP_IS_UDP(ld) = 1;
+ rc = ldap_connect_to_host( ld, conn->lconn_sb,
+ proto, srv, async );
+
+ if ( rc == -1 ) return rc;
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)"udp_" );
+#endif
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_udp,
+ LBER_SBIOD_LEVEL_PROVIDER, NULL );
+
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_readahead,
+ LBER_SBIOD_LEVEL_PROVIDER, NULL );
+
+ break;
+#endif
+ case LDAP_PROTO_IPC:
+#ifdef LDAP_PF_LOCAL
+ /* only IPC mechanism supported is PF_LOCAL (PF_UNIX) */
+ rc = ldap_connect_to_path( ld, conn->lconn_sb,
+ srv, async );
+ if ( rc == -1 ) return rc;
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)"ipc_" );
+#endif
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_fd,
+ LBER_SBIOD_LEVEL_PROVIDER, NULL );
+
+ break;
+#endif /* LDAP_PF_LOCAL */
+ default:
+ return -1;
+ break;
+ }
+
+ conn->lconn_created = time( NULL );
+
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
+ INT_MAX, (void *)"ldap_" );
+#endif
+
+#ifdef LDAP_CONNECTIONLESS
+ if( proto == LDAP_PROTO_UDP ) return 0;
+#endif
+
+#ifdef HAVE_TLS
+ if ((rc == 0 || rc == -2) && ( ld->ld_options.ldo_tls_mode == LDAP_OPT_X_TLS_HARD ||
+ strcmp( srv->lud_scheme, "ldaps" ) == 0 ))
+ {
+ ++conn->lconn_refcnt; /* avoid premature free */
+
+ rc = ldap_int_tls_start( ld, conn, srv );
+
+ --conn->lconn_refcnt;
+
+ if (rc != LDAP_SUCCESS) {
+ /* process connection callbacks */
+ {
+ struct ldapoptions *lo;
+ ldaplist *ll;
+ ldap_conncb *cb;
+
+ lo = &ld->ld_options;
+ LDAP_MUTEX_LOCK( &lo->ldo_mutex );
+ if ( lo->ldo_conn_cbs ) {
+ for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
+ cb = ll->ll_data;
+ cb->lc_del( ld, conn->lconn_sb, cb );
+ }
+ }
+ LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+ lo = LDAP_INT_GLOBAL_OPT();
+ LDAP_MUTEX_LOCK( &lo->ldo_mutex );
+ if ( lo->ldo_conn_cbs ) {
+ for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
+ cb = ll->ll_data;
+ cb->lc_del( ld, conn->lconn_sb, cb );
+ }
+ }
+ LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+ }
+ ber_int_sb_close( conn->lconn_sb );
+ return -1;
+ }
+ }
+#endif
+
+ return( 0 );
+}
+
+/*
+ * ldap_open_internal_connection - open connection and set file descriptor
+ *
+ * note: ldap_init_fd() may be preferable
+ */
+
+int
+ldap_open_internal_connection( LDAP **ldp, ber_socket_t *fdp )
+{
+ int rc;
+ LDAPConn *c;
+ LDAPRequest *lr;
+ LDAP *ld;
+
+ rc = ldap_create( &ld );
+ if( rc != LDAP_SUCCESS ) {
+ *ldp = NULL;
+ return( rc );
+ }
+
+ /* Make it appear that a search request, msgid 0, was sent */
+ lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ));
+ if( lr == NULL ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ *ldp = NULL;
+ return( LDAP_NO_MEMORY );
+ }
+ memset(lr, 0, sizeof( LDAPRequest ));
+ lr->lr_msgid = 0;
+ lr->lr_status = LDAP_REQST_INPROGRESS;
+ lr->lr_res_errno = LDAP_SUCCESS;
+ /* no mutex lock needed, we just created this ld here */
+ rc = ldap_tavl_insert( &ld->ld_requests, lr, ldap_req_cmp, ldap_avl_dup_error );
+ assert( rc == LDAP_SUCCESS );
+
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ /* Attach the passed socket as the *LDAP's connection */
+ c = ldap_new_connection( ld, NULL, 1, 0, NULL, 0, 0 );
+ if( c == NULL ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ *ldp = NULL;
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ return( LDAP_NO_MEMORY );
+ }
+ ber_sockbuf_ctrl( c->lconn_sb, LBER_SB_OPT_SET_FD, fdp );
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( c->lconn_sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)"int_" );
+#endif
+ ber_sockbuf_add_io( c->lconn_sb, &ber_sockbuf_io_tcp,
+ LBER_SBIOD_LEVEL_PROVIDER, NULL );
+ ld->ld_defconn = c;
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+
+ /* Add the connection to the *LDAP's select pool */
+ ldap_mark_select_read( ld, c->lconn_sb );
+
+ /* Make this connection an LDAP V3 protocol connection */
+ rc = LDAP_VERSION3;
+ ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &rc );
+ *ldp = ld;
+
+ ++ld->ld_defconn->lconn_refcnt; /* so it never gets closed/freed */
+
+ return( LDAP_SUCCESS );
+}
+
+LDAP *
+ldap_dup( LDAP *old )
+{
+ LDAP *ld;
+
+ if ( old == NULL ) {
+ return( NULL );
+ }
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_dup\n" );
+
+ if ( (ld = (LDAP *) LDAP_CALLOC( 1, sizeof(LDAP) )) == NULL ) {
+ return( NULL );
+ }
+
+ LDAP_MUTEX_LOCK( &old->ld_ldcmutex );
+ ld->ldc = old->ldc;
+ old->ld_ldcrefcnt++;
+ LDAP_MUTEX_UNLOCK( &old->ld_ldcmutex );
+ return ( ld );
+}
+
+int
+ldap_int_check_async_open( LDAP *ld, ber_socket_t sd )
+{
+ struct timeval tv = { 0 };
+ int rc;
+
+ rc = ldap_int_poll( ld, sd, &tv, 1 );
+ switch ( rc ) {
+ case 0:
+ /* now ready to start tls */
+ ld->ld_defconn->lconn_status = LDAP_CONNST_CONNECTED;
+ break;
+
+ default:
+ ld->ld_errno = LDAP_CONNECT_ERROR;
+ return -1;
+
+ case -2:
+ /* connect not completed yet */
+ ld->ld_errno = LDAP_X_CONNECTING;
+ return rc;
+ }
+
+#ifdef HAVE_TLS
+ if ( ld->ld_options.ldo_tls_mode == LDAP_OPT_X_TLS_HARD ||
+ !strcmp( ld->ld_defconn->lconn_server->lud_scheme, "ldaps" )) {
+
+ ++ld->ld_defconn->lconn_refcnt; /* avoid premature free */
+
+ rc = ldap_int_tls_start( ld, ld->ld_defconn, ld->ld_defconn->lconn_server );
+
+ --ld->ld_defconn->lconn_refcnt;
+ }
+#endif
+ return rc;
+}
diff --git a/libraries/libldap/options.c b/libraries/libldap/options.c
new file mode 100644
index 0000000..7a096ad
--- /dev/null
+++ b/libraries/libldap/options.c
@@ -0,0 +1,1012 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#define LDAP_OPT_REBIND_PROC 0x4e814d
+#define LDAP_OPT_REBIND_PARAMS 0x4e814e
+
+#define LDAP_OPT_NEXTREF_PROC 0x4e815d
+#define LDAP_OPT_NEXTREF_PARAMS 0x4e815e
+
+#define LDAP_OPT_URLLIST_PROC 0x4e816d
+#define LDAP_OPT_URLLIST_PARAMS 0x4e816e
+
+static const LDAPAPIFeatureInfo features[] = {
+#ifdef LDAP_API_FEATURE_X_OPENLDAP
+ { /* OpenLDAP Extensions API Feature */
+ LDAP_FEATURE_INFO_VERSION,
+ "X_OPENLDAP",
+ LDAP_API_FEATURE_X_OPENLDAP
+ },
+#endif
+
+#ifdef LDAP_API_FEATURE_THREAD_SAFE
+ { /* Basic Thread Safe */
+ LDAP_FEATURE_INFO_VERSION,
+ "THREAD_SAFE",
+ LDAP_API_FEATURE_THREAD_SAFE
+ },
+#endif
+#ifdef LDAP_API_FEATURE_SESSION_THREAD_SAFE
+ { /* Session Thread Safe */
+ LDAP_FEATURE_INFO_VERSION,
+ "SESSION_THREAD_SAFE",
+ LDAP_API_FEATURE_SESSION_THREAD_SAFE
+ },
+#endif
+#ifdef LDAP_API_FEATURE_OPERATION_THREAD_SAFE
+ { /* Operation Thread Safe */
+ LDAP_FEATURE_INFO_VERSION,
+ "OPERATION_THREAD_SAFE",
+ LDAP_API_FEATURE_OPERATION_THREAD_SAFE
+ },
+#endif
+#ifdef LDAP_API_FEATURE_X_OPENLDAP_REENTRANT
+ { /* OpenLDAP Reentrant */
+ LDAP_FEATURE_INFO_VERSION,
+ "X_OPENLDAP_REENTRANT",
+ LDAP_API_FEATURE_X_OPENLDAP_REENTRANT
+ },
+#endif
+#ifdef LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE
+ { /* OpenLDAP Thread Safe */
+ LDAP_FEATURE_INFO_VERSION,
+ "X_OPENLDAP_THREAD_SAFE",
+ LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE
+ },
+#endif
+#ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS
+ { /* V2 Referrals */
+ LDAP_FEATURE_INFO_VERSION,
+ "X_OPENLDAP_V2_REFERRALS",
+ LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS
+ },
+#endif
+ {0, NULL, 0}
+};
+
+int
+ldap_get_option(
+ LDAP *ld,
+ int option,
+ void *outvalue)
+{
+ struct ldapoptions *lo;
+ int rc = LDAP_OPT_ERROR;
+
+ /* Get pointer to global option structure */
+ lo = LDAP_INT_GLOBAL_OPT();
+ if (NULL == lo) {
+ return LDAP_NO_MEMORY;
+ }
+
+ if( lo->ldo_valid != LDAP_INITIALIZED ) {
+ ldap_int_initialize(lo, NULL);
+ if ( lo->ldo_valid != LDAP_INITIALIZED )
+ return LDAP_LOCAL_ERROR;
+ }
+
+ if(ld != NULL) {
+ if( !LDAP_VALID( ld ) ) {
+ return LDAP_OPT_ERROR;
+ }
+
+ lo = &ld->ld_options;
+ }
+
+ if(outvalue == NULL) {
+ /* no place to get to */
+ return LDAP_OPT_ERROR;
+ }
+
+ LDAP_MUTEX_LOCK( &lo->ldo_mutex );
+
+ switch(option) {
+ case LDAP_OPT_API_INFO: {
+ struct ldapapiinfo *info = (struct ldapapiinfo *) outvalue;
+
+ if(info == NULL) {
+ /* outvalue must point to an apiinfo structure */
+ break; /* LDAP_OPT_ERROR */
+ }
+
+ if(info->ldapai_info_version != LDAP_API_INFO_VERSION) {
+ /* api info version mismatch */
+ info->ldapai_info_version = LDAP_API_INFO_VERSION;
+ break; /* LDAP_OPT_ERROR */
+ }
+
+ info->ldapai_api_version = LDAP_API_VERSION;
+ info->ldapai_protocol_version = LDAP_VERSION_MAX;
+
+ if(features[0].ldapaif_name == NULL) {
+ info->ldapai_extensions = NULL;
+ } else {
+ int i;
+ info->ldapai_extensions = LDAP_MALLOC(sizeof(char *) *
+ sizeof(features)/sizeof(LDAPAPIFeatureInfo));
+ if ( info->ldapai_extensions == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ break;
+ }
+
+ for(i=0; features[i].ldapaif_name != NULL; i++) {
+ info->ldapai_extensions[i] =
+ LDAP_STRDUP(features[i].ldapaif_name);
+ if ( info->ldapai_extensions[i] == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ break;
+ }
+ }
+ if ( features[i].ldapaif_name != NULL ) {
+ break; /* LDAP_NO_MEMORY */
+ }
+
+ info->ldapai_extensions[i] = NULL;
+ }
+
+ info->ldapai_vendor_name = LDAP_STRDUP(LDAP_VENDOR_NAME);
+ info->ldapai_vendor_version = LDAP_VENDOR_VERSION;
+
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ } break;
+
+ case LDAP_OPT_DESC:
+ if( ld == NULL || ld->ld_sb == NULL ) {
+ /* bad param */
+ break;
+ }
+
+ ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, outvalue );
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_SOCKBUF:
+ if( ld == NULL ) break;
+ *(Sockbuf **)outvalue = ld->ld_sb;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_TIMEOUT:
+ /* the caller has to free outvalue ! */
+ if ( lo->ldo_tm_api.tv_sec < 0 ) {
+ *(void **)outvalue = NULL;
+ } else if ( ldap_int_timeval_dup( outvalue, &lo->ldo_tm_api ) != 0 ) {
+ break; /* LDAP_OPT_ERROR */
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_NETWORK_TIMEOUT:
+ /* the caller has to free outvalue ! */
+ if ( lo->ldo_tm_net.tv_sec < 0 ) {
+ *(void **)outvalue = NULL;
+ } else if ( ldap_int_timeval_dup( outvalue, &lo->ldo_tm_net ) != 0 ) {
+ break; /* LDAP_OPT_ERROR */
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_DEREF:
+ * (int *) outvalue = lo->ldo_deref;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_SIZELIMIT:
+ * (int *) outvalue = lo->ldo_sizelimit;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_TIMELIMIT:
+ * (int *) outvalue = lo->ldo_timelimit;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_REFERRALS:
+ * (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_REFERRALS);
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_RESTART:
+ * (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_RESTART);
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_PROTOCOL_VERSION:
+ * (int *) outvalue = lo->ldo_version;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_SERVER_CONTROLS:
+ * (LDAPControl ***) outvalue =
+ ldap_controls_dup( lo->ldo_sctrls );
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_CLIENT_CONTROLS:
+ * (LDAPControl ***) outvalue =
+ ldap_controls_dup( lo->ldo_cctrls );
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_HOST_NAME:
+ * (char **) outvalue = ldap_url_list2hosts(lo->ldo_defludp);
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_SOCKET_BIND_ADDRESSES:
+ if ( lo->ldo_local_ip_addrs.local_ip_addrs == NULL ) {
+ * (void **) outvalue = NULL;
+ }
+ else {
+ * (char **) outvalue =
+ LDAP_STRDUP( lo->ldo_local_ip_addrs.local_ip_addrs );
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_URI:
+ * (char **) outvalue = ldap_url_list2urls(lo->ldo_defludp);
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_DEFBASE:
+ if( lo->ldo_defbase == NULL ) {
+ * (char **) outvalue = NULL;
+ } else {
+ * (char **) outvalue = LDAP_STRDUP(lo->ldo_defbase);
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_CONNECT_ASYNC:
+ * (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_CONNECT_ASYNC);
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_CONNECT_CB:
+ {
+ /* Getting deletes the specified callback */
+ ldaplist **ll = &lo->ldo_conn_cbs;
+ for (;*ll;ll = &(*ll)->ll_next) {
+ if ((*ll)->ll_data == outvalue) {
+ ldaplist *lc = *ll;
+ *ll = lc->ll_next;
+ LDAP_FREE(lc);
+ break;
+ }
+ }
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_RESULT_CODE:
+ if(ld == NULL) {
+ /* bad param */
+ break;
+ }
+ * (int *) outvalue = ld->ld_errno;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_DIAGNOSTIC_MESSAGE:
+ if(ld == NULL) {
+ /* bad param */
+ break;
+ }
+
+ if( ld->ld_error == NULL ) {
+ * (char **) outvalue = NULL;
+ } else {
+ * (char **) outvalue = LDAP_STRDUP(ld->ld_error);
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_MATCHED_DN:
+ if(ld == NULL) {
+ /* bad param */
+ break;
+ }
+
+ if( ld->ld_matched == NULL ) {
+ * (char **) outvalue = NULL;
+ } else {
+ * (char **) outvalue = LDAP_STRDUP( ld->ld_matched );
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_REFERRAL_URLS:
+ if(ld == NULL) {
+ /* bad param */
+ break;
+ }
+
+ if( ld->ld_referrals == NULL ) {
+ * (char ***) outvalue = NULL;
+ } else {
+ * (char ***) outvalue = ldap_value_dup(ld->ld_referrals);
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_API_FEATURE_INFO: {
+ LDAPAPIFeatureInfo *info = (LDAPAPIFeatureInfo *) outvalue;
+ int i;
+
+ if(info == NULL)
+ break; /* LDAP_OPT_ERROR */
+
+ if(info->ldapaif_info_version != LDAP_FEATURE_INFO_VERSION) {
+ /* api info version mismatch */
+ info->ldapaif_info_version = LDAP_FEATURE_INFO_VERSION;
+ break; /* LDAP_OPT_ERROR */
+ }
+
+ if(info->ldapaif_name == NULL)
+ break; /* LDAP_OPT_ERROR */
+
+ for(i=0; features[i].ldapaif_name != NULL; i++) {
+ if(!strcmp(info->ldapaif_name, features[i].ldapaif_name)) {
+ info->ldapaif_version =
+ features[i].ldapaif_version;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ }
+ }
+ }
+ break;
+
+ case LDAP_OPT_DEBUG_LEVEL:
+ * (int *) outvalue = lo->ldo_debug;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_SESSION_REFCNT:
+ if(ld == NULL) {
+ /* bad param */
+ break;
+ }
+ LDAP_MUTEX_LOCK( &ld->ld_ldcmutex );
+ * (int *) outvalue = ld->ld_ldcrefcnt;
+ LDAP_MUTEX_UNLOCK( &ld->ld_ldcmutex );
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_KEEPCONN:
+ * (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_KEEPCONN);
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_X_KEEPALIVE_IDLE:
+ * (int *) outvalue = lo->ldo_keepalive_idle;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_X_KEEPALIVE_PROBES:
+ * (int *) outvalue = lo->ldo_keepalive_probes;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_X_KEEPALIVE_INTERVAL:
+ * (int *) outvalue = lo->ldo_keepalive_interval;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_TCP_USER_TIMEOUT:
+ * (unsigned int *) outvalue = lo->ldo_tcp_user_timeout;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ default:
+#ifdef HAVE_TLS
+ if ( ldap_pvt_tls_get_option( ld, option, outvalue ) == 0 ) {
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ }
+#endif
+#ifdef HAVE_CYRUS_SASL
+ if ( ldap_int_sasl_get_option( ld, option, outvalue ) == 0 ) {
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ }
+#endif
+ /* bad param */
+ break;
+ }
+
+ LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+ return ( rc );
+}
+
+int
+ldap_set_option(
+ LDAP *ld,
+ int option,
+ LDAP_CONST void *invalue)
+{
+ struct ldapoptions *lo;
+ int *dbglvl = NULL;
+ int rc = LDAP_OPT_ERROR;
+
+ /* Get pointer to global option structure */
+ lo = LDAP_INT_GLOBAL_OPT();
+ if (lo == NULL) {
+ return LDAP_NO_MEMORY;
+ }
+
+ /*
+ * The architecture to turn on debugging has a chicken and egg
+ * problem. Thus, we introduce a fix here.
+ */
+
+ if (option == LDAP_OPT_DEBUG_LEVEL) {
+ dbglvl = (int *) invalue;
+ }
+
+ if( lo->ldo_valid != LDAP_INITIALIZED ) {
+ ldap_int_initialize(lo, dbglvl);
+ if ( lo->ldo_valid != LDAP_INITIALIZED )
+ return LDAP_LOCAL_ERROR;
+ }
+
+ if(ld != NULL) {
+ assert( LDAP_VALID( ld ) );
+
+ if( !LDAP_VALID( ld ) ) {
+ return LDAP_OPT_ERROR;
+ }
+
+ lo = &ld->ld_options;
+ }
+
+ LDAP_MUTEX_LOCK( &lo->ldo_mutex );
+
+ switch ( option ) {
+
+ /* options with boolean values */
+ case LDAP_OPT_REFERRALS:
+ if(invalue == LDAP_OPT_OFF) {
+ LDAP_BOOL_CLR(lo, LDAP_BOOL_REFERRALS);
+ } else {
+ LDAP_BOOL_SET(lo, LDAP_BOOL_REFERRALS);
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_RESTART:
+ if(invalue == LDAP_OPT_OFF) {
+ LDAP_BOOL_CLR(lo, LDAP_BOOL_RESTART);
+ } else {
+ LDAP_BOOL_SET(lo, LDAP_BOOL_RESTART);
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_CONNECT_ASYNC:
+ if(invalue == LDAP_OPT_OFF) {
+ LDAP_BOOL_CLR(lo, LDAP_BOOL_CONNECT_ASYNC);
+ } else {
+ LDAP_BOOL_SET(lo, LDAP_BOOL_CONNECT_ASYNC);
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_KEEPCONN:
+ if(invalue == LDAP_OPT_OFF) {
+ LDAP_BOOL_CLR(lo, LDAP_BOOL_KEEPCONN);
+ } else {
+ LDAP_BOOL_SET(lo, LDAP_BOOL_KEEPCONN);
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ /* options which can withstand invalue == NULL */
+ case LDAP_OPT_SERVER_CONTROLS: {
+ LDAPControl *const *controls =
+ (LDAPControl *const *) invalue;
+
+ if( lo->ldo_sctrls )
+ ldap_controls_free( lo->ldo_sctrls );
+
+ if( controls == NULL || *controls == NULL ) {
+ lo->ldo_sctrls = NULL;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ }
+
+ lo->ldo_sctrls = ldap_controls_dup( controls );
+
+ if(lo->ldo_sctrls == NULL) {
+ /* memory allocation error ? */
+ break; /* LDAP_OPT_ERROR */
+ }
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_CLIENT_CONTROLS: {
+ LDAPControl *const *controls =
+ (LDAPControl *const *) invalue;
+
+ if( lo->ldo_cctrls )
+ ldap_controls_free( lo->ldo_cctrls );
+
+ if( controls == NULL || *controls == NULL ) {
+ lo->ldo_cctrls = NULL;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ }
+
+ lo->ldo_cctrls = ldap_controls_dup( controls );
+
+ if(lo->ldo_cctrls == NULL) {
+ /* memory allocation error ? */
+ break; /* LDAP_OPT_ERROR */
+ }
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+
+ case LDAP_OPT_HOST_NAME: {
+ const char *host = (const char *) invalue;
+ LDAPURLDesc *ludlist = NULL;
+ rc = LDAP_OPT_SUCCESS;
+
+ if(host != NULL) {
+ rc = ldap_url_parsehosts( &ludlist, host,
+ lo->ldo_defport ? lo->ldo_defport : LDAP_PORT );
+
+ } else if(ld == NULL) {
+ /*
+ * must want global default returned
+ * to initial condition.
+ */
+ rc = ldap_url_parselist_ext(&ludlist, "ldap://localhost/", NULL,
+ LDAP_PVT_URL_PARSE_NOEMPTY_HOST
+ | LDAP_PVT_URL_PARSE_DEF_PORT );
+
+ } else {
+ /*
+ * must want the session default
+ * updated to the current global default
+ */
+ ludlist = ldap_url_duplist(
+ ldap_int_global_options.ldo_defludp);
+ if (ludlist == NULL)
+ rc = LDAP_NO_MEMORY;
+ }
+
+ if (rc == LDAP_OPT_SUCCESS) {
+ if (lo->ldo_defludp != NULL)
+ ldap_free_urllist(lo->ldo_defludp);
+ lo->ldo_defludp = ludlist;
+ }
+ break;
+ }
+
+ case LDAP_OPT_SOCKET_BIND_ADDRESSES: {
+ const char *source_ip = (const char *) invalue;
+ char **source_ip_lst = NULL;
+
+ ldapsourceip temp_source_ip;
+ memset( &temp_source_ip, 0, sizeof( ldapsourceip ) );
+ rc = LDAP_OPT_SUCCESS;
+ if( source_ip == NULL ) {
+ if ( ld->ld_options.ldo_local_ip_addrs.local_ip_addrs ) {
+ LDAP_FREE( ld->ld_options.ldo_local_ip_addrs.local_ip_addrs );
+ memset( &ld->ld_options.ldo_local_ip_addrs, 0,
+ sizeof( ldapsourceip ) );
+ }
+ }
+ else {
+ source_ip_lst = ldap_str2charray( source_ip, " " );
+
+ if ( source_ip_lst == NULL )
+ rc = LDAP_NO_MEMORY;
+
+ if( rc == LDAP_OPT_SUCCESS ) {
+ rc = ldap_validate_and_fill_sourceip ( source_ip_lst,
+ &temp_source_ip );
+ ldap_charray_free( source_ip_lst );
+ }
+ if ( rc == LDAP_OPT_SUCCESS ) {
+ if ( lo->ldo_local_ip_addrs.local_ip_addrs != NULL ) {
+ LDAP_FREE( lo->ldo_local_ip_addrs.local_ip_addrs );
+ lo->ldo_local_ip_addrs.local_ip_addrs = NULL;
+ }
+ lo->ldo_local_ip_addrs = temp_source_ip;
+ lo->ldo_local_ip_addrs.local_ip_addrs = LDAP_STRDUP( source_ip );
+ }
+ }
+ break;
+ }
+
+ case LDAP_OPT_URI: {
+ const char *urls = (const char *) invalue;
+ LDAPURLDesc *ludlist = NULL;
+ rc = LDAP_OPT_SUCCESS;
+
+ if(urls != NULL) {
+ rc = ldap_url_parselist_ext(&ludlist, urls, NULL,
+ LDAP_PVT_URL_PARSE_NOEMPTY_HOST
+ | LDAP_PVT_URL_PARSE_DEF_PORT );
+ } else if(ld == NULL) {
+ /*
+ * must want global default returned
+ * to initial condition.
+ */
+ rc = ldap_url_parselist_ext(&ludlist, "ldap://localhost/", NULL,
+ LDAP_PVT_URL_PARSE_NOEMPTY_HOST
+ | LDAP_PVT_URL_PARSE_DEF_PORT );
+
+ } else {
+ /*
+ * must want the session default
+ * updated to the current global default
+ */
+ ludlist = ldap_url_duplist(
+ ldap_int_global_options.ldo_defludp);
+ if (ludlist == NULL)
+ rc = LDAP_URL_ERR_MEM;
+ }
+
+ switch (rc) {
+ case LDAP_URL_SUCCESS: /* Success */
+ rc = LDAP_SUCCESS;
+ break;
+
+ case LDAP_URL_ERR_MEM: /* can't allocate memory space */
+ rc = LDAP_NO_MEMORY;
+ break;
+
+ case LDAP_URL_ERR_PARAM: /* parameter is bad */
+ case LDAP_URL_ERR_BADSCHEME: /* URL doesn't begin with "ldap[si]://" */
+ case LDAP_URL_ERR_BADENCLOSURE: /* URL is missing trailing ">" */
+ case LDAP_URL_ERR_BADURL: /* URL is bad */
+ case LDAP_URL_ERR_BADHOST: /* host port is bad */
+ case LDAP_URL_ERR_BADATTRS: /* bad (or missing) attributes */
+ case LDAP_URL_ERR_BADSCOPE: /* scope string is invalid (or missing) */
+ case LDAP_URL_ERR_BADFILTER: /* bad or missing filter */
+ case LDAP_URL_ERR_BADEXTS: /* bad or missing extensions */
+ rc = LDAP_PARAM_ERROR;
+ break;
+ }
+
+ if (rc == LDAP_SUCCESS) {
+ if (lo->ldo_defludp != NULL)
+ ldap_free_urllist(lo->ldo_defludp);
+ lo->ldo_defludp = ludlist;
+ }
+ break;
+ }
+
+ case LDAP_OPT_DEFBASE: {
+ const char *newbase = (const char *) invalue;
+ char *defbase = NULL;
+
+ if ( newbase != NULL ) {
+ defbase = LDAP_STRDUP( newbase );
+ if ( defbase == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ break;
+ }
+
+ } else if ( ld != NULL ) {
+ defbase = LDAP_STRDUP( ldap_int_global_options.ldo_defbase );
+ if ( defbase == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ break;
+ }
+ }
+
+ if ( lo->ldo_defbase != NULL )
+ LDAP_FREE( lo->ldo_defbase );
+ lo->ldo_defbase = defbase;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_DIAGNOSTIC_MESSAGE: {
+ const char *err = (const char *) invalue;
+
+ if(ld == NULL) {
+ /* need a struct ldap */
+ break; /* LDAP_OPT_ERROR */
+ }
+
+ if( ld->ld_error ) {
+ LDAP_FREE(ld->ld_error);
+ ld->ld_error = NULL;
+ }
+
+ if ( err ) {
+ ld->ld_error = LDAP_STRDUP(err);
+ }
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_MATCHED_DN: {
+ const char *matched = (const char *) invalue;
+
+ if (ld == NULL) {
+ /* need a struct ldap */
+ break; /* LDAP_OPT_ERROR */
+ }
+
+ if( ld->ld_matched ) {
+ LDAP_FREE(ld->ld_matched);
+ ld->ld_matched = NULL;
+ }
+
+ if ( matched ) {
+ ld->ld_matched = LDAP_STRDUP( matched );
+ }
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_REFERRAL_URLS: {
+ char *const *referrals = (char *const *) invalue;
+
+ if(ld == NULL) {
+ /* need a struct ldap */
+ break; /* LDAP_OPT_ERROR */
+ }
+
+ if( ld->ld_referrals ) {
+ LDAP_VFREE(ld->ld_referrals);
+ }
+
+ if ( referrals ) {
+ ld->ld_referrals = ldap_value_dup(referrals);
+ }
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ /* Only accessed from inside this function by ldap_set_rebind_proc() */
+ case LDAP_OPT_REBIND_PROC: {
+ lo->ldo_rebind_proc = (LDAP_REBIND_PROC *)invalue;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ case LDAP_OPT_REBIND_PARAMS: {
+ lo->ldo_rebind_params = (void *)invalue;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ /* Only accessed from inside this function by ldap_set_nextref_proc() */
+ case LDAP_OPT_NEXTREF_PROC: {
+ lo->ldo_nextref_proc = (LDAP_NEXTREF_PROC *)invalue;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ case LDAP_OPT_NEXTREF_PARAMS: {
+ lo->ldo_nextref_params = (void *)invalue;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ /* Only accessed from inside this function by ldap_set_urllist_proc() */
+ case LDAP_OPT_URLLIST_PROC: {
+ lo->ldo_urllist_proc = (LDAP_URLLIST_PROC *)invalue;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ case LDAP_OPT_URLLIST_PARAMS: {
+ lo->ldo_urllist_params = (void *)invalue;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ /* read-only options */
+ case LDAP_OPT_API_INFO:
+ case LDAP_OPT_DESC:
+ case LDAP_OPT_SOCKBUF:
+ case LDAP_OPT_API_FEATURE_INFO:
+ break; /* LDAP_OPT_ERROR */
+
+ /* options which cannot withstand invalue == NULL */
+ case LDAP_OPT_DEREF:
+ case LDAP_OPT_SIZELIMIT:
+ case LDAP_OPT_TIMELIMIT:
+ case LDAP_OPT_PROTOCOL_VERSION:
+ case LDAP_OPT_RESULT_CODE:
+ case LDAP_OPT_DEBUG_LEVEL:
+ case LDAP_OPT_TIMEOUT:
+ case LDAP_OPT_NETWORK_TIMEOUT:
+ case LDAP_OPT_CONNECT_CB:
+ case LDAP_OPT_X_KEEPALIVE_IDLE:
+ case LDAP_OPT_X_KEEPALIVE_PROBES :
+ case LDAP_OPT_X_KEEPALIVE_INTERVAL :
+ case LDAP_OPT_TCP_USER_TIMEOUT:
+ if(invalue == NULL) {
+ /* no place to set from */
+ LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+ return ( LDAP_OPT_ERROR );
+ }
+ break;
+
+ default:
+#ifdef HAVE_TLS
+ if ( ldap_pvt_tls_set_option( ld, option, (void *)invalue ) == 0 ) {
+ LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+ return ( LDAP_OPT_SUCCESS );
+ }
+#endif
+#ifdef HAVE_CYRUS_SASL
+ if ( ldap_int_sasl_set_option( ld, option, (void *)invalue ) == 0 ) {
+ LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+ return ( LDAP_OPT_SUCCESS );
+ }
+#endif
+ /* bad param */
+ break; /* LDAP_OPT_ERROR */
+ }
+
+ /* options which cannot withstand invalue == NULL */
+
+ switch(option) {
+ case LDAP_OPT_DEREF:
+ /* FIXME: check value for protocol compliance? */
+ lo->ldo_deref = * (const int *) invalue;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_SIZELIMIT:
+ /* FIXME: check value for protocol compliance? */
+ lo->ldo_sizelimit = * (const int *) invalue;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_TIMELIMIT:
+ /* FIXME: check value for protocol compliance? */
+ lo->ldo_timelimit = * (const int *) invalue;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_TIMEOUT: {
+ const struct timeval *tv =
+ (const struct timeval *) invalue;
+
+ lo->ldo_tm_api = *tv;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_NETWORK_TIMEOUT: {
+ const struct timeval *tv =
+ (const struct timeval *) invalue;
+
+ lo->ldo_tm_net = *tv;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_PROTOCOL_VERSION: {
+ int vers = * (const int *) invalue;
+ if (vers < LDAP_VERSION_MIN || vers > LDAP_VERSION_MAX) {
+ /* not supported */
+ break;
+ }
+ lo->ldo_version = vers;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_RESULT_CODE: {
+ int err = * (const int *) invalue;
+
+ if(ld == NULL) {
+ /* need a struct ldap */
+ break;
+ }
+
+ ld->ld_errno = err;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_DEBUG_LEVEL:
+ lo->ldo_debug = * (const int *) invalue;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_CONNECT_CB:
+ {
+ /* setting pushes the callback */
+ ldaplist *ll;
+ ll = LDAP_MALLOC( sizeof( *ll ));
+ if ( ll == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ break;
+ }
+
+ ll->ll_data = (void *)invalue;
+ ll->ll_next = lo->ldo_conn_cbs;
+ lo->ldo_conn_cbs = ll;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ case LDAP_OPT_X_KEEPALIVE_IDLE:
+ lo->ldo_keepalive_idle = * (const int *) invalue;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ case LDAP_OPT_X_KEEPALIVE_PROBES :
+ lo->ldo_keepalive_probes = * (const int *) invalue;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ case LDAP_OPT_X_KEEPALIVE_INTERVAL :
+ lo->ldo_keepalive_interval = * (const int *) invalue;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ case LDAP_OPT_TCP_USER_TIMEOUT:
+ lo->ldo_tcp_user_timeout = * (const unsigned int *) invalue;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ }
+ LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+ return ( rc );
+}
+
+int
+ldap_set_rebind_proc( LDAP *ld, LDAP_REBIND_PROC *proc, void *params )
+{
+ int rc;
+ rc = ldap_set_option( ld, LDAP_OPT_REBIND_PROC, (void *)proc );
+ if( rc != LDAP_OPT_SUCCESS ) return rc;
+
+ rc = ldap_set_option( ld, LDAP_OPT_REBIND_PARAMS, (void *)params );
+ return rc;
+}
+
+int
+ldap_set_nextref_proc( LDAP *ld, LDAP_NEXTREF_PROC *proc, void *params )
+{
+ int rc;
+ rc = ldap_set_option( ld, LDAP_OPT_NEXTREF_PROC, (void *)proc );
+ if( rc != LDAP_OPT_SUCCESS ) return rc;
+
+ rc = ldap_set_option( ld, LDAP_OPT_NEXTREF_PARAMS, (void *)params );
+ return rc;
+}
+
+int
+ldap_set_urllist_proc( LDAP *ld, LDAP_URLLIST_PROC *proc, void *params )
+{
+ int rc;
+ rc = ldap_set_option( ld, LDAP_OPT_URLLIST_PROC, (void *)proc );
+ if( rc != LDAP_OPT_SUCCESS ) return rc;
+
+ rc = ldap_set_option( ld, LDAP_OPT_URLLIST_PARAMS, (void *)params );
+ return rc;
+}
diff --git a/libraries/libldap/os-ip.c b/libraries/libldap/os-ip.c
new file mode 100644
index 0000000..a84735e
--- /dev/null
+++ b/libraries/libldap/os-ip.c
@@ -0,0 +1,1265 @@
+/* os-ip.c -- platform-specific TCP & UDP related code */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 Lars Uffmann.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+/* Significant additional contributors include:
+ * Lars Uffman
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif /* HAVE_IO_H */
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "ldap-int.h"
+
+#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
+# ifdef LDAP_PF_INET6
+int ldap_int_inet4or6 = AF_UNSPEC;
+# else
+int ldap_int_inet4or6 = AF_INET;
+# endif
+#endif
+
+static void
+ldap_pvt_set_errno(int err)
+{
+ sock_errset(err);
+}
+
+int
+ldap_int_timeval_dup( struct timeval **dest, const struct timeval *src )
+{
+ struct timeval *new;
+
+ assert( dest != NULL );
+
+ if (src == NULL) {
+ *dest = NULL;
+ return 0;
+ }
+
+ new = (struct timeval *) LDAP_MALLOC(sizeof(struct timeval));
+
+ if( new == NULL ) {
+ *dest = NULL;
+ return 1;
+ }
+
+ AC_MEMCPY( (char *) new, (const char *) src, sizeof(struct timeval));
+
+ *dest = new;
+ return 0;
+}
+
+static int
+ldap_pvt_ndelay_on(LDAP *ld, int fd)
+{
+ Debug1(LDAP_DEBUG_TRACE, "ldap_ndelay_on: %d\n",fd );
+ return ber_pvt_socket_set_nonblock( fd, 1 );
+}
+
+static int
+ldap_pvt_ndelay_off(LDAP *ld, int fd)
+{
+ Debug1(LDAP_DEBUG_TRACE, "ldap_ndelay_off: %d\n",fd );
+ return ber_pvt_socket_set_nonblock( fd, 0 );
+}
+
+static ber_socket_t
+ldap_int_socket(LDAP *ld, int family, int type )
+{
+ ber_socket_t s = socket(family, type, 0);
+ Debug1(LDAP_DEBUG_TRACE, "ldap_new_socket: %d\n",s );
+#ifdef FD_CLOEXEC
+ fcntl(s, F_SETFD, FD_CLOEXEC);
+#endif
+ return ( s );
+}
+
+static int
+ldap_pvt_close_socket(LDAP *ld, int s)
+{
+ Debug1(LDAP_DEBUG_TRACE, "ldap_close_socket: %d\n",s );
+ return tcp_close(s);
+}
+
+static int
+ldap_int_prepare_socket(LDAP *ld, int s, int proto )
+{
+ Debug1(LDAP_DEBUG_TRACE, "ldap_prepare_socket: %d\n", s );
+
+#if defined( SO_KEEPALIVE ) || defined( TCP_NODELAY ) || defined( TCP_USER_TIMEOUT )
+ if ( proto == LDAP_PROTO_TCP ) {
+ int dummy = 1;
+#ifdef SO_KEEPALIVE
+ if ( setsockopt( s, SOL_SOCKET, SO_KEEPALIVE,
+ (char*) &dummy, sizeof(dummy) ) == AC_SOCKET_ERROR )
+ {
+ Debug1(LDAP_DEBUG_TRACE, "ldap_prepare_socket: "
+ "setsockopt(%d, SO_KEEPALIVE) failed (ignored).\n",
+ s );
+ }
+ if ( ld->ld_options.ldo_keepalive_idle > 0 )
+ {
+#ifdef TCP_KEEPIDLE
+ if ( setsockopt( s, IPPROTO_TCP, TCP_KEEPIDLE,
+ (void*) &ld->ld_options.ldo_keepalive_idle,
+ sizeof(ld->ld_options.ldo_keepalive_idle) ) == AC_SOCKET_ERROR )
+ {
+ Debug1(LDAP_DEBUG_TRACE,
+ "ldap_prepare_socket: "
+ "setsockopt(%d, TCP_KEEPIDLE) failed (ignored).\n",
+ s );
+ }
+#else
+ Debug0(LDAP_DEBUG_TRACE, "ldap_prepare_socket: "
+ "sockopt TCP_KEEPIDLE not supported on this system.\n" );
+#endif /* TCP_KEEPIDLE */
+ }
+ if ( ld->ld_options.ldo_keepalive_probes > 0 )
+ {
+#ifdef TCP_KEEPCNT
+ if ( setsockopt( s, IPPROTO_TCP, TCP_KEEPCNT,
+ (void*) &ld->ld_options.ldo_keepalive_probes,
+ sizeof(ld->ld_options.ldo_keepalive_probes) ) == AC_SOCKET_ERROR )
+ {
+ Debug1(LDAP_DEBUG_TRACE,
+ "ldap_prepare_socket: "
+ "setsockopt(%d, TCP_KEEPCNT) failed (ignored).\n",
+ s );
+ }
+#else
+ Debug0(LDAP_DEBUG_TRACE, "ldap_prepare_socket: "
+ "sockopt TCP_KEEPCNT not supported on this system.\n" );
+#endif /* TCP_KEEPCNT */
+ }
+ if ( ld->ld_options.ldo_keepalive_interval > 0 )
+ {
+#ifdef TCP_KEEPINTVL
+ if ( setsockopt( s, IPPROTO_TCP, TCP_KEEPINTVL,
+ (void*) &ld->ld_options.ldo_keepalive_interval,
+ sizeof(ld->ld_options.ldo_keepalive_interval) ) == AC_SOCKET_ERROR )
+ {
+ Debug1(LDAP_DEBUG_TRACE,
+ "ldap_prepare_socket: "
+ "setsockopt(%d, TCP_KEEPINTVL) failed (ignored).\n",
+ s );
+ }
+#else
+ Debug0(LDAP_DEBUG_TRACE, "ldap_prepare_socket: "
+ "sockopt TCP_KEEPINTVL not supported on this system.\n" );
+#endif /* TCP_KEEPINTVL */
+ }
+#endif /* SO_KEEPALIVE */
+#ifdef TCP_NODELAY
+ if ( setsockopt( s, IPPROTO_TCP, TCP_NODELAY,
+ (char*) &dummy, sizeof(dummy) ) == AC_SOCKET_ERROR )
+ {
+ Debug1(LDAP_DEBUG_TRACE, "ldap_prepare_socket: "
+ "setsockopt(%d, TCP_NODELAY) failed (ignored).\n",
+ s );
+ }
+#endif /* TCP_NODELAY */
+ if ( ld->ld_options.ldo_tcp_user_timeout > 0 )
+ {
+#ifdef TCP_USER_TIMEOUT
+ if ( setsockopt( s, IPPROTO_TCP, TCP_USER_TIMEOUT,
+ (void*) &ld->ld_options.ldo_tcp_user_timeout,
+ sizeof(ld->ld_options.ldo_tcp_user_timeout) ) == AC_SOCKET_ERROR )
+ {
+ Debug1(LDAP_DEBUG_TRACE,
+ "ldap_prepare_socket: "
+ "setsockopt(%d, TCP_USER_TIMEOUT) failed (ignored).\n",
+ s );
+ }
+#else
+ Debug0(LDAP_DEBUG_TRACE, "ldap_prepare_socket: "
+ "sockopt TCP_USER_TIMEOUT not supported on this system.\n" );
+#endif /* TCP_USER_TIMEOUT */
+ }
+ }
+#endif /* SO_KEEPALIVE || TCP_NODELAY || TCP_USER_TIMEOUT */
+
+ return 0;
+}
+
+#ifndef HAVE_WINSOCK
+
+#undef TRACE
+#define TRACE do { \
+ char ebuf[128]; \
+ int saved_errno = errno; \
+ Debug3(LDAP_DEBUG_TRACE, "ldap_is_socket_ready: error on socket %d: errno: %d (%s)\n", \
+ s, \
+ saved_errno, \
+ sock_errstr(saved_errno, ebuf, sizeof(ebuf)) ); \
+} while( 0 )
+
+/*
+ * check the socket for errors after select returned.
+ */
+static int
+ldap_pvt_is_socket_ready(LDAP *ld, int s)
+{
+ Debug1(LDAP_DEBUG_TRACE, "ldap_is_sock_ready: %d\n",s );
+
+#if defined( notyet ) /* && defined( SO_ERROR ) */
+{
+ int so_errno;
+ ber_socklen_t dummy = sizeof(so_errno);
+ if ( getsockopt( s, SOL_SOCKET, SO_ERROR, &so_errno, &dummy )
+ == AC_SOCKET_ERROR )
+ {
+ return -1;
+ }
+ if ( so_errno ) {
+ ldap_pvt_set_errno(so_errno);
+ TRACE;
+ return -1;
+ }
+ return 0;
+}
+#else
+{
+ /* error slippery */
+#ifdef LDAP_PF_INET6
+ struct sockaddr_storage sin;
+#else
+ struct sockaddr_in sin;
+#endif
+ char ch;
+ ber_socklen_t dummy = sizeof(sin);
+ if ( getpeername( s, (struct sockaddr *) &sin, &dummy )
+ == AC_SOCKET_ERROR )
+ {
+ /* XXX: needs to be replace with ber_stream_read() */
+ (void)!read(s, &ch, 1);
+ TRACE;
+ return -1;
+ }
+ return 0;
+}
+#endif
+ return -1;
+}
+#undef TRACE
+
+#endif /* HAVE_WINSOCK */
+
+/* NOTE: this is identical to analogous code in os-local.c */
+int
+ldap_int_poll(
+ LDAP *ld,
+ ber_socket_t s,
+ struct timeval *tvp,
+ int wr )
+{
+ int rc;
+
+
+ Debug2(LDAP_DEBUG_TRACE, "ldap_int_poll: fd: %d tm: %ld\n",
+ s, tvp ? tvp->tv_sec : -1L );
+
+#ifdef HAVE_POLL
+ {
+ struct pollfd fd;
+ int timeout = INFTIM;
+ short event = wr ? POLL_WRITE : POLL_READ;
+
+ fd.fd = s;
+ fd.events = event;
+
+ if ( tvp != NULL ) {
+ timeout = TV2MILLISEC( tvp );
+ }
+ do {
+ fd.revents = 0;
+ rc = poll( &fd, 1, timeout );
+
+ } while ( rc == AC_SOCKET_ERROR && errno == EINTR &&
+ LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_RESTART ) );
+
+ if ( rc == AC_SOCKET_ERROR ) {
+ return rc;
+ }
+
+ if ( timeout == 0 && rc == 0 ) {
+ return -2;
+ }
+
+ if ( fd.revents & event ) {
+ if ( ldap_pvt_is_socket_ready( ld, s ) == -1 ) {
+ return -1;
+ }
+
+ if ( ldap_pvt_ndelay_off( ld, s ) == -1 ) {
+ return -1;
+ }
+ return 0;
+ }
+ }
+#else
+ {
+ fd_set wfds, *z = NULL;
+#ifdef HAVE_WINSOCK
+ fd_set efds;
+#endif
+ struct timeval tv = { 0 };
+
+#if defined( FD_SETSIZE ) && !defined( HAVE_WINSOCK )
+ if ( s >= FD_SETSIZE ) {
+ rc = AC_SOCKET_ERROR;
+ tcp_close( s );
+ ldap_pvt_set_errno( EMFILE );
+ return rc;
+ }
+#endif
+
+ if ( tvp != NULL ) {
+ tv = *tvp;
+ }
+
+ do {
+ FD_ZERO(&wfds);
+ FD_SET(s, &wfds );
+
+#ifdef HAVE_WINSOCK
+ FD_ZERO(&efds);
+ FD_SET(s, &efds );
+#endif
+
+ rc = select( ldap_int_tblsize, z, &wfds,
+#ifdef HAVE_WINSOCK
+ &efds,
+#else
+ z,
+#endif
+ tvp ? &tv : NULL );
+ } while ( rc == AC_SOCKET_ERROR && errno == EINTR &&
+ LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_RESTART ) );
+
+ if ( rc == AC_SOCKET_ERROR ) {
+ return rc;
+ }
+
+ if ( rc == 0 && tvp && tvp->tv_sec == 0 && tvp->tv_usec == 0 ) {
+ return -2;
+ }
+
+#ifdef HAVE_WINSOCK
+ /* This means the connection failed */
+ if ( FD_ISSET(s, &efds) ) {
+ int so_errno;
+ ber_socklen_t dummy = sizeof(so_errno);
+ if ( getsockopt( s, SOL_SOCKET, SO_ERROR,
+ (char *) &so_errno, &dummy ) == AC_SOCKET_ERROR || !so_errno )
+ {
+ /* impossible */
+ so_errno = WSAGetLastError();
+ }
+ ldap_pvt_set_errno( so_errno );
+ Debug3(LDAP_DEBUG_TRACE,
+ "ldap_int_poll: error on socket %d: "
+ "errno: %d (%s)\n", s, so_errno, sock_errstr( so_errno, dummy, dummy ));
+ return -1;
+ }
+#endif
+ if ( FD_ISSET(s, &wfds) ) {
+#ifndef HAVE_WINSOCK
+ if ( ldap_pvt_is_socket_ready( ld, s ) == -1 ) {
+ return -1;
+ }
+#endif
+ if ( ldap_pvt_ndelay_off(ld, s) == -1 ) {
+ return -1;
+ }
+ return 0;
+ }
+ }
+#endif
+
+ Debug0(LDAP_DEBUG_TRACE, "ldap_int_poll: timed out\n" );
+ ldap_pvt_set_errno( ETIMEDOUT );
+ return -1;
+}
+
+static int
+ldap_pvt_connect(LDAP *ld, ber_socket_t s,
+ struct sockaddr *sin, ber_socklen_t addrlen,
+ int async)
+{
+ int rc, err;
+ struct timeval tv, *opt_tv = NULL;
+
+#ifdef LDAP_CONNECTIONLESS
+ /* We could do a connect() but that would interfere with
+ * attempts to poll a broadcast address
+ */
+ if (LDAP_IS_UDP(ld)) {
+ if (ld->ld_options.ldo_peer)
+ ldap_memfree(ld->ld_options.ldo_peer);
+ ld->ld_options.ldo_peer=ldap_memcalloc(1, sizeof(struct sockaddr_storage));
+ AC_MEMCPY(ld->ld_options.ldo_peer,sin,addrlen);
+ return ( 0 );
+ }
+#endif
+ if ( ld->ld_options.ldo_tm_net.tv_sec >= 0 ) {
+ tv = ld->ld_options.ldo_tm_net;
+ opt_tv = &tv;
+ }
+
+ Debug3(LDAP_DEBUG_TRACE,
+ "ldap_pvt_connect: fd: %d tm: %ld async: %d\n",
+ s, opt_tv ? tv.tv_sec : -1L, async);
+
+ if ( opt_tv && ldap_pvt_ndelay_on(ld, s) == -1 )
+ return ( -1 );
+
+ do{
+ Debug0(LDAP_DEBUG_TRACE, "attempting to connect: \n" );
+ if ( connect(s, sin, addrlen) != AC_SOCKET_ERROR ) {
+ Debug0(LDAP_DEBUG_TRACE, "connect success\n" );
+
+ if ( !async && opt_tv && ldap_pvt_ndelay_off(ld, s) == -1 )
+ return ( -1 );
+ return ( 0 );
+ }
+ err = sock_errno();
+ Debug1(LDAP_DEBUG_TRACE, "connect errno: %d\n", err );
+
+ } while(err == EINTR &&
+ LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_RESTART ));
+
+ if ( err != EINPROGRESS && err != EWOULDBLOCK ) {
+ return ( -1 );
+ }
+
+ if ( async ) {
+ /* caller will call ldap_int_poll() as appropriate? */
+ return ( -2 );
+ }
+
+ rc = ldap_int_poll( ld, s, opt_tv, 1 );
+
+ Debug1(LDAP_DEBUG_TRACE, "ldap_pvt_connect: %d\n", rc );
+
+ return rc;
+}
+
+#ifndef HAVE_INET_ATON
+int
+ldap_pvt_inet_aton( const char *host, struct in_addr *in)
+{
+ unsigned long u = inet_addr( host );
+
+#ifdef INADDR_NONE
+ if ( u == INADDR_NONE ) return 0;
+#endif
+ if ( u == 0xffffffffUL || u == (unsigned long) -1L ) return 0;
+
+ in->s_addr = u;
+ return 1;
+}
+#endif
+
+int
+ldap_validate_and_fill_sourceip (char** source_ip_lst, ldapsourceip* temp_source_ip )
+{
+ int i = 0;
+ int rc = LDAP_PARAM_ERROR;
+
+ for ( i = 0; source_ip_lst[i] != NULL; i++ ) {
+ Debug1( LDAP_DEBUG_TRACE,
+ "ldap_validate_and_fill_sourceip(%s)\n",
+ source_ip_lst[i] );
+
+ if ( !temp_source_ip->has_ipv4 ) {
+ if ( inet_aton( source_ip_lst[i], &temp_source_ip->ip4_addr ) ) {
+ temp_source_ip->has_ipv4 = 1;
+ rc = LDAP_OPT_SUCCESS;
+ continue;
+ }
+ }
+#ifdef LDAP_PF_INET6
+ if ( !temp_source_ip->has_ipv6 ) {
+ if ( inet_pton( AF_INET6, source_ip_lst[i],
+ & temp_source_ip->ip6_addr ) ) {
+ temp_source_ip->has_ipv6 = 1;
+ rc = LDAP_OPT_SUCCESS;
+ continue;
+ }
+ }
+#endif
+ memset( temp_source_ip, 0, sizeof( * (temp_source_ip ) ) );
+ Debug1( LDAP_DEBUG_TRACE,
+ "ldap_validate_and_fill_sourceip: validation failed for (%s)\n",
+ source_ip_lst[i] );
+ break;
+ }
+ return rc;
+}
+
+int
+ldap_int_connect_cbs(LDAP *ld, Sockbuf *sb, ber_socket_t *s, LDAPURLDesc *srv, struct sockaddr *addr)
+{
+ struct ldapoptions *lo;
+ ldaplist *ll;
+ ldap_conncb *cb;
+ int rc;
+
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_FD, s );
+
+ /* Invoke all handle-specific callbacks first */
+ lo = &ld->ld_options;
+ for (ll = lo->ldo_conn_cbs; ll; ll = ll->ll_next) {
+ cb = ll->ll_data;
+ rc = cb->lc_add( ld, sb, srv, addr, cb );
+ /* on any failure, call the teardown functions for anything
+ * that previously succeeded
+ */
+ if ( rc ) {
+ ldaplist *l2;
+ for (l2 = lo->ldo_conn_cbs; l2 != ll; l2 = l2->ll_next) {
+ cb = l2->ll_data;
+ cb->lc_del( ld, sb, cb );
+ }
+ /* a failure might have implicitly closed the fd */
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, s );
+ return rc;
+ }
+ }
+ lo = LDAP_INT_GLOBAL_OPT();
+ for (ll = lo->ldo_conn_cbs; ll; ll = ll->ll_next) {
+ cb = ll->ll_data;
+ rc = cb->lc_add( ld, sb, srv, addr, cb );
+ if ( rc ) {
+ ldaplist *l2;
+ for (l2 = lo->ldo_conn_cbs; l2 != ll; l2 = l2->ll_next) {
+ cb = l2->ll_data;
+ cb->lc_del( ld, sb, cb );
+ }
+ lo = &ld->ld_options;
+ for (l2 = lo->ldo_conn_cbs; l2; l2 = l2->ll_next) {
+ cb = l2->ll_data;
+ cb->lc_del( ld, sb, cb );
+ }
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, s );
+ return rc;
+ }
+ }
+ return 0;
+}
+
+int
+ldap_connect_to_host(LDAP *ld, Sockbuf *sb,
+ int proto, LDAPURLDesc *srv,
+ int async )
+{
+ int rc;
+ int socktype, port;
+ ber_socket_t s = AC_SOCKET_INVALID;
+ char *host;
+
+#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
+ char serv[7];
+ int err;
+ struct addrinfo hints, *res, *sai;
+#else
+ int i;
+ int use_hp = 0;
+ struct hostent *hp = NULL;
+ struct hostent he_buf;
+ struct in_addr in;
+ char *ha_buf=NULL;
+#endif
+
+ if ( srv->lud_host == NULL || *srv->lud_host == 0 ) {
+ host = "localhost";
+ } else {
+ host = srv->lud_host;
+ }
+
+ port = srv->lud_port;
+
+ if( !port ) {
+ if( strcmp(srv->lud_scheme, "ldaps") == 0 ) {
+ port = LDAPS_PORT;
+ } else {
+ port = LDAP_PORT;
+ }
+ }
+
+ switch(proto) {
+ case LDAP_PROTO_TCP: socktype = SOCK_STREAM;
+ Debug2(LDAP_DEBUG_TRACE, "ldap_connect_to_host: TCP %s:%d\n",
+ host, port );
+ break;
+ case LDAP_PROTO_UDP: socktype = SOCK_DGRAM;
+ Debug2(LDAP_DEBUG_TRACE, "ldap_connect_to_host: UDP %s:%d\n",
+ host, port );
+ break;
+ default:
+ Debug1(LDAP_DEBUG_TRACE,
+ "ldap_connect_to_host: unknown proto: %d\n",
+ proto );
+ return -1;
+ }
+
+#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
+ memset( &hints, '\0', sizeof(hints) );
+#ifdef USE_AI_ADDRCONFIG /* FIXME: configure test needed */
+ /* Use AI_ADDRCONFIG only on systems where its known to be needed. */
+ hints.ai_flags = AI_ADDRCONFIG;
+#endif
+ hints.ai_family = ldap_int_inet4or6;
+ hints.ai_socktype = socktype;
+ snprintf(serv, sizeof serv, "%d", port );
+
+ /* most getaddrinfo(3) use non-threadsafe resolver libraries */
+ LDAP_MUTEX_LOCK(&ldap_int_resolv_mutex);
+
+ err = getaddrinfo( host, serv, &hints, &res );
+
+ LDAP_MUTEX_UNLOCK(&ldap_int_resolv_mutex);
+
+ if ( err != 0 ) {
+ Debug1(LDAP_DEBUG_TRACE,
+ "ldap_connect_to_host: getaddrinfo failed: %s\n",
+ AC_GAI_STRERROR(err) );
+ return -1;
+ }
+ rc = -1;
+
+ for( sai=res; sai != NULL; sai=sai->ai_next) {
+ unsigned short bind_success = 1;
+ if( sai->ai_addr == NULL ) {
+ Debug0(LDAP_DEBUG_TRACE,
+ "ldap_connect_to_host: getaddrinfo "
+ "ai_addr is NULL?\n" );
+ continue;
+ }
+
+#ifndef LDAP_PF_INET6
+ if ( sai->ai_family == AF_INET6 ) continue;
+#endif
+ /* we assume AF_x and PF_x are equal for all x */
+ s = ldap_int_socket( ld, sai->ai_family, socktype );
+ if ( s == AC_SOCKET_INVALID ) {
+ continue;
+ }
+
+ if ( ldap_int_prepare_socket(ld, s, proto ) == -1 ) {
+ ldap_pvt_close_socket(ld, s);
+ break;
+ }
+
+ switch (sai->ai_family) {
+#ifdef LDAP_PF_INET6
+ case AF_INET6: {
+ char addr[INET6_ADDRSTRLEN];
+ inet_ntop( AF_INET6,
+ &((struct sockaddr_in6 *)sai->ai_addr)->sin6_addr,
+ addr, sizeof addr);
+ Debug2(LDAP_DEBUG_TRACE,
+ "ldap_connect_to_host: Trying %s %s\n",
+ addr, serv );
+ if( ld->ld_options.ldo_local_ip_addrs.has_ipv6 ) {
+ struct sockaddr_in6 ip6addr;
+ char bind_addr[INET6_ADDRSTRLEN];
+ ip6addr.sin6_family = AF_INET6;
+ ip6addr.sin6_port = 0;
+ ip6addr.sin6_addr = ld->ld_options.ldo_local_ip_addrs.ip6_addr;
+ inet_ntop( AF_INET6,
+ &(ip6addr.sin6_addr),
+ bind_addr, sizeof bind_addr );
+ Debug1( LDAP_DEBUG_TRACE,
+ "ldap_connect_to_host: From source address %s\n",
+ bind_addr );
+ if ( bind( s, ( struct sockaddr* ) &ip6addr, sizeof ip6addr ) != 0 ) {
+ Debug1( LDAP_DEBUG_TRACE,
+ "ldap_connect_to_host: Failed to bind source address %s\n",
+ bind_addr );
+ bind_success = 0;
+ }
+ }
+ } break;
+#endif
+ case AF_INET: {
+ char addr[INET_ADDRSTRLEN];
+ inet_ntop( AF_INET,
+ &((struct sockaddr_in *)sai->ai_addr)->sin_addr,
+ addr, sizeof addr);
+ Debug2(LDAP_DEBUG_TRACE,
+ "ldap_connect_to_host: Trying %s:%s\n",
+ addr, serv );
+ if( ld->ld_options.ldo_local_ip_addrs.has_ipv4 ) {
+ struct sockaddr_in ip4addr;
+ char bind_addr[INET_ADDRSTRLEN];
+ ip4addr.sin_family = AF_INET;
+ ip4addr.sin_port = 0;
+ ip4addr.sin_addr = ld->ld_options.ldo_local_ip_addrs.ip4_addr;
+ inet_ntop( AF_INET,
+ &(ip4addr.sin_addr),
+ bind_addr, sizeof bind_addr );
+ Debug1( LDAP_DEBUG_TRACE,
+ "ldap_connect_to_host: From source address %s\n",
+ bind_addr );
+ if ( bind(s, ( struct sockaddr* )&ip4addr, sizeof ip4addr ) != 0 ) {
+ Debug1( LDAP_DEBUG_TRACE,
+ "ldap_connect_to_host: Failed to bind source address %s\n",
+ bind_addr );
+ bind_success = 0;
+ }
+ }
+ } break;
+ }
+ if ( bind_success ) {
+ rc = ldap_pvt_connect( ld, s,
+ sai->ai_addr, sai->ai_addrlen, async );
+ if ( rc == 0 || rc == -2 ) {
+ err = ldap_int_connect_cbs( ld, sb, &s, srv, sai->ai_addr );
+ if ( err )
+ rc = err;
+ else
+ break;
+ }
+ }
+ ldap_pvt_close_socket(ld, s);
+ }
+ freeaddrinfo(res);
+
+#else
+ if (! inet_aton( host, &in ) ) {
+ int local_h_errno;
+ rc = ldap_pvt_gethostbyname_a( host, &he_buf, &ha_buf,
+ &hp, &local_h_errno );
+
+ if ( (rc < 0) || (hp == NULL) ) {
+#ifdef HAVE_WINSOCK
+ ldap_pvt_set_errno( WSAGetLastError() );
+#else
+ /* not exactly right, but... */
+ ldap_pvt_set_errno( EHOSTUNREACH );
+#endif
+ if (ha_buf) LDAP_FREE(ha_buf);
+ return -1;
+ }
+
+ use_hp = 1;
+ }
+
+ rc = s = -1;
+ for ( i = 0; !use_hp || (hp->h_addr_list[i] != 0); ++i, rc = -1 ) {
+ struct sockaddr_in sin;
+ unsigned short bind_success = 1;
+#ifdef HAVE_INET_NTOA_B
+ char address[INET_ADDR_LEN];
+ char bind_addr[INET_ADDR_LEN];
+#else
+ char *address;
+ char *bind_addr;
+#endif
+ s = ldap_int_socket( ld, PF_INET, socktype );
+ if ( s == AC_SOCKET_INVALID ) {
+ /* use_hp ? continue : break; */
+ break;
+ }
+
+ if ( ldap_int_prepare_socket( ld, s, proto ) == -1 ) {
+ ldap_pvt_close_socket(ld, s);
+ break;
+ }
+
+ (void)memset((char *)&sin, '\0', sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons((unsigned short) port);
+
+ if( use_hp ) {
+ AC_MEMCPY( &sin.sin_addr, hp->h_addr_list[i],
+ sizeof(sin.sin_addr) );
+ } else {
+ AC_MEMCPY( &sin.sin_addr, &in.s_addr,
+ sizeof(sin.sin_addr) );
+ }
+
+#ifdef HAVE_INET_NTOA_B
+ /* for VxWorks */
+ inet_ntoa_b( sin.sin_address, address );
+#else
+ address = inet_ntoa( sin.sin_addr );
+#endif
+ Debug2( LDAP_DEBUG_TRACE,
+ "ldap_connect_to_host: Trying %s:%d\n",
+ address, port );
+ if( ld->ld_options.ldo_local_ip_addrs.has_ipv4 ) {
+ struct sockaddr_in ip4addr;
+ ip4addr.sin_family = AF_INET;
+ ip4addr.sin_addr = ld->ld_options.ldo_local_ip_addrs.ip4_addr;
+#ifdef HAVE_INET_NTOA_B
+ inet_ntoa_b( ip4addr.sin_address, bind_addr );
+#else
+ bind_addr = inet_ntoa( ip4addr.sin_addr );
+#endif
+ Debug1( LDAP_DEBUG_TRACE,
+ "ldap_connect_to_host: From source address %s\n",
+ bind_addr );
+ if ( bind( s, (struct sockaddr*)&ip4addr, sizeof ip4addr ) != 0 ) {
+ Debug1( LDAP_DEBUG_TRACE,
+ "ldap_connect_to_host: Failed to bind source address %s\n",
+ bind_addr );
+ bind_success = 0;
+ }
+ }
+ if ( bind_success ) {
+ rc = ldap_pvt_connect(ld, s,
+ (struct sockaddr *)&sin, sizeof(sin),
+ async);
+
+ if ( (rc == 0) || (rc == -2) ) {
+ int err = ldap_int_connect_cbs( ld, sb, &s, srv, (struct sockaddr *)&sin );
+ if ( err )
+ rc = err;
+ else
+ break;
+ }
+ }
+
+ ldap_pvt_close_socket(ld, s);
+
+ if (!use_hp) break;
+ }
+ if (ha_buf) LDAP_FREE(ha_buf);
+#endif
+
+ return rc;
+}
+
+#if defined( HAVE_CYRUS_SASL )
+char *
+ldap_host_connected_to( Sockbuf *sb, const char *host )
+{
+ ber_socklen_t len;
+#ifdef LDAP_PF_INET6
+ struct sockaddr_storage sabuf;
+#else
+ struct sockaddr sabuf;
+#endif
+ struct sockaddr *sa = (struct sockaddr *) &sabuf;
+ ber_socket_t sd;
+
+ (void)memset( (char *)sa, '\0', sizeof sabuf );
+ len = sizeof sabuf;
+
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+ if ( getpeername( sd, sa, &len ) == -1 ) {
+ return( NULL );
+ }
+
+ /*
+ * do a reverse lookup on the addr to get the official hostname.
+ * this is necessary for kerberos to work right, since the official
+ * hostname is used as the kerberos instance.
+ */
+
+ switch (sa->sa_family) {
+#ifdef LDAP_PF_LOCAL
+ case AF_LOCAL:
+ return LDAP_STRDUP( ldap_int_hostname );
+#endif
+#ifdef LDAP_PF_INET6
+ case AF_INET6:
+ {
+ struct in6_addr localhost = IN6ADDR_LOOPBACK_INIT;
+ if( memcmp ( &((struct sockaddr_in6 *)sa)->sin6_addr,
+ &localhost, sizeof(localhost)) == 0 )
+ {
+ return LDAP_STRDUP( ldap_int_hostname );
+ }
+ }
+ break;
+#endif
+ case AF_INET:
+ {
+ struct in_addr localhost;
+ localhost.s_addr = htonl( INADDR_ANY );
+
+ if( memcmp ( &((struct sockaddr_in *)sa)->sin_addr,
+ &localhost, sizeof(localhost) ) == 0 )
+ {
+ return LDAP_STRDUP( ldap_int_hostname );
+ }
+
+#ifdef INADDR_LOOPBACK
+ localhost.s_addr = htonl( INADDR_LOOPBACK );
+
+ if( memcmp ( &((struct sockaddr_in *)sa)->sin_addr,
+ &localhost, sizeof(localhost) ) == 0 )
+ {
+ return LDAP_STRDUP( ldap_int_hostname );
+ }
+#endif
+ }
+ break;
+
+ default:
+ return( NULL );
+ break;
+ }
+
+ {
+ char *herr;
+#ifdef NI_MAXHOST
+ char hbuf[NI_MAXHOST];
+#elif defined( MAXHOSTNAMELEN )
+ char hbuf[MAXHOSTNAMELEN];
+#else
+ char hbuf[256];
+#endif
+ hbuf[0] = 0;
+
+ if (ldap_pvt_get_hname( sa, len, hbuf, sizeof(hbuf), &herr ) == 0
+ && hbuf[0] )
+ {
+ return LDAP_STRDUP( hbuf );
+ }
+ }
+
+ return host ? LDAP_STRDUP( host ) : NULL;
+}
+#endif
+
+
+struct selectinfo {
+#ifdef HAVE_POLL
+ /* for UNIX poll(2) */
+ int si_maxfd;
+ struct pollfd si_fds[FD_SETSIZE];
+#else
+ /* for UNIX select(2) */
+ fd_set si_readfds;
+ fd_set si_writefds;
+ fd_set si_use_readfds;
+ fd_set si_use_writefds;
+#endif
+};
+
+void
+ldap_mark_select_write( LDAP *ld, Sockbuf *sb )
+{
+ struct selectinfo *sip;
+ ber_socket_t sd;
+
+ sip = (struct selectinfo *)ld->ld_selectinfo;
+
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+
+#ifdef HAVE_POLL
+ /* for UNIX poll(2) */
+ {
+ int empty=-1;
+ int i;
+ for(i=0; i < sip->si_maxfd; i++) {
+ if( sip->si_fds[i].fd == sd ) {
+ sip->si_fds[i].events |= POLL_WRITE;
+ return;
+ }
+ if( empty==-1 && sip->si_fds[i].fd == -1 ) {
+ empty=i;
+ }
+ }
+
+ if( empty == -1 ) {
+ if( sip->si_maxfd >= FD_SETSIZE ) {
+ /* FIXME */
+ return;
+ }
+ empty = sip->si_maxfd++;
+ }
+
+ sip->si_fds[empty].fd = sd;
+ sip->si_fds[empty].events = POLL_WRITE;
+ }
+#else
+ /* for UNIX select(2) */
+ if ( !FD_ISSET( sd, &sip->si_writefds )) {
+ FD_SET( sd, &sip->si_writefds );
+ }
+#endif
+}
+
+
+void
+ldap_mark_select_read( LDAP *ld, Sockbuf *sb )
+{
+ struct selectinfo *sip;
+ ber_socket_t sd;
+
+ sip = (struct selectinfo *)ld->ld_selectinfo;
+
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+
+#ifdef HAVE_POLL
+ /* for UNIX poll(2) */
+ {
+ int empty=-1;
+ int i;
+ for(i=0; i < sip->si_maxfd; i++) {
+ if( sip->si_fds[i].fd == sd ) {
+ sip->si_fds[i].events |= POLL_READ;
+ return;
+ }
+ if( empty==-1 && sip->si_fds[i].fd == -1 ) {
+ empty=i;
+ }
+ }
+
+ if( empty == -1 ) {
+ if( sip->si_maxfd >= FD_SETSIZE ) {
+ /* FIXME */
+ return;
+ }
+ empty = sip->si_maxfd++;
+ }
+
+ sip->si_fds[empty].fd = sd;
+ sip->si_fds[empty].events = POLL_READ;
+ }
+#else
+ /* for UNIX select(2) */
+ if ( !FD_ISSET( sd, &sip->si_readfds )) {
+ FD_SET( sd, &sip->si_readfds );
+ }
+#endif
+}
+
+
+void
+ldap_mark_select_clear( LDAP *ld, Sockbuf *sb )
+{
+ struct selectinfo *sip;
+ ber_socket_t sd;
+
+ sip = (struct selectinfo *)ld->ld_selectinfo;
+
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+
+#ifdef HAVE_POLL
+ /* for UNIX poll(2) */
+ {
+ int i;
+ for(i=0; i < sip->si_maxfd; i++) {
+ if( sip->si_fds[i].fd == sd ) {
+ sip->si_fds[i].fd = -1;
+ }
+ }
+ }
+#else
+ /* for UNIX select(2) */
+ FD_CLR( sd, &sip->si_writefds );
+ FD_CLR( sd, &sip->si_readfds );
+#endif
+}
+
+void
+ldap_clear_select_write( LDAP *ld, Sockbuf *sb )
+{
+ struct selectinfo *sip;
+ ber_socket_t sd;
+
+ sip = (struct selectinfo *)ld->ld_selectinfo;
+
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+
+#ifdef HAVE_POLL
+ /* for UNIX poll(2) */
+ {
+ int i;
+ for(i=0; i < sip->si_maxfd; i++) {
+ if( sip->si_fds[i].fd == sd ) {
+ sip->si_fds[i].events &= ~POLL_WRITE;
+ }
+ }
+ }
+#else
+ /* for UNIX select(2) */
+ FD_CLR( sd, &sip->si_writefds );
+#endif
+}
+
+
+int
+ldap_is_write_ready( LDAP *ld, Sockbuf *sb )
+{
+ struct selectinfo *sip;
+ ber_socket_t sd;
+
+ sip = (struct selectinfo *)ld->ld_selectinfo;
+
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+
+#ifdef HAVE_POLL
+ /* for UNIX poll(2) */
+ {
+ int i;
+ for(i=0; i < sip->si_maxfd; i++) {
+ if( sip->si_fds[i].fd == sd ) {
+ return sip->si_fds[i].revents & POLL_WRITE;
+ }
+ }
+
+ return 0;
+ }
+#else
+ /* for UNIX select(2) */
+ return( FD_ISSET( sd, &sip->si_use_writefds ));
+#endif
+}
+
+
+int
+ldap_is_read_ready( LDAP *ld, Sockbuf *sb )
+{
+ struct selectinfo *sip;
+ ber_socket_t sd;
+
+ sip = (struct selectinfo *)ld->ld_selectinfo;
+
+ if (ber_sockbuf_ctrl( sb, LBER_SB_OPT_DATA_READY, NULL ))
+ return 1;
+
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+
+#ifdef HAVE_POLL
+ /* for UNIX poll(2) */
+ {
+ int i;
+ for(i=0; i < sip->si_maxfd; i++) {
+ if( sip->si_fds[i].fd == sd ) {
+ return sip->si_fds[i].revents & POLL_READ;
+ }
+ }
+
+ return 0;
+ }
+#else
+ /* for UNIX select(2) */
+ return( FD_ISSET( sd, &sip->si_use_readfds ));
+#endif
+}
+
+
+void *
+ldap_new_select_info( void )
+{
+ struct selectinfo *sip;
+
+ sip = (struct selectinfo *)LDAP_CALLOC( 1, sizeof( struct selectinfo ));
+
+ if ( sip == NULL ) return NULL;
+
+#ifdef HAVE_POLL
+ /* for UNIX poll(2) */
+ /* sip->si_maxfd=0 */
+#else
+ /* for UNIX select(2) */
+ FD_ZERO( &sip->si_readfds );
+ FD_ZERO( &sip->si_writefds );
+#endif
+
+ return( (void *)sip );
+}
+
+
+void
+ldap_free_select_info( void *sip )
+{
+ LDAP_FREE( sip );
+}
+
+
+#ifndef HAVE_POLL
+int ldap_int_tblsize = 0;
+
+void
+ldap_int_ip_init( void )
+{
+#if defined( HAVE_SYSCONF )
+ long tblsize = sysconf( _SC_OPEN_MAX );
+ if( tblsize > INT_MAX ) tblsize = INT_MAX;
+
+#elif defined( HAVE_GETDTABLESIZE )
+ int tblsize = getdtablesize();
+#else
+ int tblsize = FD_SETSIZE;
+#endif /* !USE_SYSCONF */
+
+#ifdef FD_SETSIZE
+ if( tblsize > FD_SETSIZE ) tblsize = FD_SETSIZE;
+#endif /* FD_SETSIZE */
+
+ ldap_int_tblsize = tblsize;
+}
+#endif
+
+
+int
+ldap_int_select( LDAP *ld, struct timeval *timeout )
+{
+ int rc;
+ struct selectinfo *sip;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_int_select\n" );
+
+#ifndef HAVE_POLL
+ if ( ldap_int_tblsize == 0 ) ldap_int_ip_init();
+#endif
+
+ sip = (struct selectinfo *)ld->ld_selectinfo;
+ assert( sip != NULL );
+
+#ifdef HAVE_POLL
+ {
+ int to = timeout ? TV2MILLISEC( timeout ) : INFTIM;
+ rc = poll( sip->si_fds, sip->si_maxfd, to );
+ }
+#else
+ sip->si_use_readfds = sip->si_readfds;
+ sip->si_use_writefds = sip->si_writefds;
+
+ rc = select( ldap_int_tblsize,
+ &sip->si_use_readfds, &sip->si_use_writefds,
+ NULL, timeout );
+#endif
+
+ return rc;
+}
diff --git a/libraries/libldap/os-local.c b/libraries/libldap/os-local.c
new file mode 100644
index 0000000..8b31030
--- /dev/null
+++ b/libraries/libldap/os-local.c
@@ -0,0 +1,351 @@
+/* os-local.c -- platform-specific domain socket code */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+/* Portions (C) Copyright PADL Software Pty Ltd. 1999
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that this notice is preserved
+ * and that due credit is given to PADL Software Pty Ltd. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#ifdef LDAP_PF_LOCAL
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif /* HAVE_IO_H */
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "ldap-int.h"
+#include "ldap_defaults.h"
+
+static void
+ldap_pvt_set_errno(int err)
+{
+ errno = err;
+}
+
+static int
+ldap_pvt_ndelay_on(LDAP *ld, int fd)
+{
+ Debug1(LDAP_DEBUG_TRACE, "ldap_ndelay_on: %d\n",fd );
+ return ber_pvt_socket_set_nonblock( fd, 1 );
+}
+
+static int
+ldap_pvt_ndelay_off(LDAP *ld, int fd)
+{
+ Debug1(LDAP_DEBUG_TRACE, "ldap_ndelay_off: %d\n",fd );
+ return ber_pvt_socket_set_nonblock( fd, 0 );
+}
+
+static ber_socket_t
+ldap_pvt_socket(LDAP *ld)
+{
+ ber_socket_t s = socket(PF_LOCAL, SOCK_STREAM, 0);
+ Debug1(LDAP_DEBUG_TRACE, "ldap_new_socket: %d\n",s );
+#ifdef FD_CLOEXEC
+ fcntl(s, F_SETFD, FD_CLOEXEC);
+#endif
+ return ( s );
+}
+
+static int
+ldap_pvt_close_socket(LDAP *ld, int s)
+{
+ Debug1(LDAP_DEBUG_TRACE, "ldap_close_socket: %d\n",s );
+ return tcp_close(s);
+}
+
+#undef TRACE
+#define TRACE do { \
+ char ebuf[128]; \
+ int saved_errno = errno; \
+ Debug3(LDAP_DEBUG_TRACE, "ldap_is_socket_ready: error on socket %d: errno: %d (%s)\n", \
+ s, \
+ saved_errno, \
+ AC_STRERROR_R(saved_errno, ebuf, sizeof ebuf)); \
+} while( 0 )
+
+/*
+ * check the socket for errors after select returned.
+ */
+static int
+ldap_pvt_is_socket_ready(LDAP *ld, int s)
+{
+ Debug1(LDAP_DEBUG_TRACE, "ldap_is_sock_ready: %d\n",s );
+
+#if defined( notyet ) /* && defined( SO_ERROR ) */
+{
+ int so_errno;
+ ber_socklen_t dummy = sizeof(so_errno);
+ if ( getsockopt( s, SOL_SOCKET, SO_ERROR, &so_errno, &dummy )
+ == AC_SOCKET_ERROR )
+ {
+ return -1;
+ }
+ if ( so_errno ) {
+ ldap_pvt_set_errno(so_errno);
+ TRACE;
+ return -1;
+ }
+ return 0;
+}
+#else
+{
+ /* error slippery */
+ struct sockaddr_un sa;
+ char ch;
+ ber_socklen_t dummy = sizeof(sa);
+ if ( getpeername( s, (struct sockaddr *) &sa, &dummy )
+ == AC_SOCKET_ERROR )
+ {
+ /* XXX: needs to be replace with ber_stream_read() */
+ (void)read(s, &ch, 1);
+ TRACE;
+ return -1;
+ }
+ return 0;
+}
+#endif
+ return -1;
+}
+#undef TRACE
+
+#ifdef LDAP_PF_LOCAL_SENDMSG
+static const char abandonPDU[] = {LDAP_TAG_MESSAGE, 6,
+ LDAP_TAG_MSGID, 1, 0, LDAP_REQ_ABANDON, 1, 0};
+#endif
+
+static int
+ldap_pvt_connect(LDAP *ld, ber_socket_t s, struct sockaddr_un *sa, int async)
+{
+ int rc;
+ struct timeval tv, *opt_tv = NULL;
+
+ if ( ld->ld_options.ldo_tm_net.tv_sec >= 0 ) {
+ tv = ld->ld_options.ldo_tm_net;
+ opt_tv = &tv;
+ }
+
+ Debug3(LDAP_DEBUG_TRACE,
+ "ldap_connect_timeout: fd: %d tm: %ld async: %d\n",
+ s, opt_tv ? tv.tv_sec : -1L, async);
+
+ if ( ldap_pvt_ndelay_on(ld, s) == -1 ) return -1;
+
+ if ( connect(s, (struct sockaddr *) sa, sizeof(struct sockaddr_un))
+ != AC_SOCKET_ERROR )
+ {
+ if ( ldap_pvt_ndelay_off(ld, s) == -1 ) return -1;
+
+#ifdef LDAP_PF_LOCAL_SENDMSG
+ /* Send a dummy message with access rights. Remote side will
+ * obtain our uid/gid by fstat'ing this descriptor. The
+ * descriptor permissions must match exactly, and we also
+ * send the socket name, which must also match.
+ */
+sendcred:
+ {
+ int fds[2];
+ ber_socklen_t salen = sizeof(*sa);
+ if (pipe(fds) == 0) {
+ /* Abandon, noop, has no reply */
+ struct iovec iov;
+ struct msghdr msg = {0};
+# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+# ifndef CMSG_SPACE
+# define CMSG_SPACE(len) (_CMSG_ALIGN( sizeof(struct cmsghdr)) + _CMSG_ALIGN(len) )
+# endif
+# ifndef CMSG_LEN
+# define CMSG_LEN(len) (_CMSG_ALIGN( sizeof(struct cmsghdr)) + (len) )
+# endif
+ union {
+ struct cmsghdr cm;
+ unsigned char control[CMSG_SPACE(sizeof(int))];
+ } control_un;
+ struct cmsghdr *cmsg;
+# endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ iov.iov_base = (char *) abandonPDU;
+ iov.iov_len = sizeof abandonPDU;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+ msg.msg_control = control_un.control;
+ msg.msg_controllen = sizeof( control_un.control );
+ msg.msg_flags = 0;
+
+ cmsg = CMSG_FIRSTHDR( &msg );
+ cmsg->cmsg_len = CMSG_LEN( sizeof(int) );
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+
+ *((int *)CMSG_DATA(cmsg)) = fds[0];
+# else
+ msg.msg_accrights = (char *)fds;
+ msg.msg_accrightslen = sizeof(int);
+# endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */
+ getpeername( s, (struct sockaddr *) sa, &salen );
+ fchmod( fds[0], S_ISUID|S_IRWXU );
+ write( fds[1], sa, salen );
+ sendmsg( s, &msg, 0 );
+ close(fds[0]);
+ close(fds[1]);
+ }
+ }
+#endif
+ return 0;
+ }
+
+ if ( errno != EINPROGRESS && errno != EWOULDBLOCK ) return -1;
+
+#ifdef notyet
+ if ( async ) return -2;
+#endif
+
+#ifdef HAVE_POLL
+ {
+ struct pollfd fd;
+ int timeout = INFTIM;
+
+ if( opt_tv != NULL ) timeout = TV2MILLISEC( &tv );
+
+ fd.fd = s;
+ fd.events = POLL_WRITE;
+
+ do {
+ fd.revents = 0;
+ rc = poll( &fd, 1, timeout );
+ } while( rc == AC_SOCKET_ERROR && errno == EINTR &&
+ LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART ));
+
+ if( rc == AC_SOCKET_ERROR ) return rc;
+
+ if( fd.revents & POLL_WRITE ) {
+ if ( ldap_pvt_is_socket_ready(ld, s) == -1 ) return -1;
+ if ( ldap_pvt_ndelay_off(ld, s) == -1 ) return -1;
+#ifdef LDAP_PF_LOCAL_SENDMSG
+ goto sendcred;
+#else
+ return ( 0 );
+#endif
+ }
+ }
+#else
+ {
+ fd_set wfds, *z=NULL;
+
+#ifdef FD_SETSIZE
+ if ( s >= FD_SETSIZE ) {
+ rc = AC_SOCKET_ERROR;
+ tcp_close( s );
+ ldap_pvt_set_errno( EMFILE );
+ return rc;
+ }
+#endif
+ do {
+ FD_ZERO(&wfds);
+ FD_SET(s, &wfds );
+ rc = select( ldap_int_tblsize, z, &wfds, z, opt_tv ? &tv : NULL );
+ } while( rc == AC_SOCKET_ERROR && errno == EINTR &&
+ LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART ));
+
+ if( rc == AC_SOCKET_ERROR ) return rc;
+
+ if ( FD_ISSET(s, &wfds) ) {
+ if ( ldap_pvt_is_socket_ready(ld, s) == -1 ) return -1;
+ if ( ldap_pvt_ndelay_off(ld, s) == -1 ) return -1;
+#ifdef LDAP_PF_LOCAL_SENDMSG
+ goto sendcred;
+#else
+ return ( 0 );
+#endif
+ }
+ }
+#endif
+
+ Debug0(LDAP_DEBUG_TRACE, "ldap_connect_timeout: timed out\n" );
+ ldap_pvt_set_errno( ETIMEDOUT );
+ return ( -1 );
+}
+
+int
+ldap_connect_to_path(LDAP *ld, Sockbuf *sb, LDAPURLDesc *srv, int async)
+{
+ struct sockaddr_un server;
+ ber_socket_t s;
+ int rc;
+ const char *path = srv->lud_host;
+
+ Debug0(LDAP_DEBUG_TRACE, "ldap_connect_to_path\n" );
+
+ if ( path == NULL || path[0] == '\0' ) {
+ path = LDAPI_SOCK;
+ } else {
+ if ( strlen(path) > (sizeof( server.sun_path ) - 1) ) {
+ ldap_pvt_set_errno( ENAMETOOLONG );
+ return -1;
+ }
+ }
+
+ s = ldap_pvt_socket( ld );
+ if ( s == AC_SOCKET_INVALID ) {
+ return -1;
+ }
+
+ Debug1(LDAP_DEBUG_TRACE, "ldap_connect_to_path: Trying %s\n", path );
+
+ memset( &server, '\0', sizeof(server) );
+ server.sun_family = AF_LOCAL;
+ strcpy( server.sun_path, path );
+
+ rc = ldap_pvt_connect(ld, s, &server, async);
+
+ if (rc == 0) {
+ rc = ldap_int_connect_cbs( ld, sb, &s, srv, (struct sockaddr *)&server );
+ }
+ if ( rc ) {
+ ldap_pvt_close_socket(ld, s);
+ }
+ return rc;
+}
+#else
+static int dummy; /* generate also a warning: 'dummy' defined but not used (at least here) */
+#endif /* LDAP_PF_LOCAL */
diff --git a/libraries/libldap/pagectrl.c b/libraries/libldap/pagectrl.c
new file mode 100644
index 0000000..c7a1499
--- /dev/null
+++ b/libraries/libldap/pagectrl.c
@@ -0,0 +1,271 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Copyright 2006 Hans Leidekker
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/* ---------------------------------------------------------------------------
+ ldap_create_page_control_value
+
+ Create and encode the value of the paged results control (RFC 2696).
+
+ ld (IN) An LDAP session handle
+ pagesize (IN) Page size requested
+ cookie (IN) Opaque structure used by the server to track its
+ location in the search results. NULL on the
+ first call.
+ value (OUT) Control value, SHOULD be freed by calling
+ ldap_memfree() when done.
+
+ pagedResultsControl ::= SEQUENCE {
+ controlType 1.2.840.113556.1.4.319,
+ criticality BOOLEAN DEFAULT FALSE,
+ controlValue searchControlValue }
+
+ searchControlValue ::= SEQUENCE {
+ size INTEGER (0..maxInt),
+ -- requested page size from client
+ -- result set size estimate from server
+ cookie OCTET STRING }
+
+ ---------------------------------------------------------------------------*/
+
+int
+ldap_create_page_control_value(
+ LDAP *ld,
+ ber_int_t pagesize,
+ struct berval *cookie,
+ struct berval *value )
+{
+ BerElement *ber = NULL;
+ ber_tag_t tag;
+ struct berval null_cookie = { 0, NULL };
+
+ if ( ld == NULL || value == NULL ||
+ pagesize < 1 || pagesize > LDAP_MAXINT )
+ {
+ if ( ld )
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return LDAP_PARAM_ERROR;
+ }
+
+ assert( LDAP_VALID( ld ) );
+
+ value->bv_val = NULL;
+ value->bv_len = 0;
+ ld->ld_errno = LDAP_SUCCESS;
+
+ if ( cookie == NULL ) {
+ cookie = &null_cookie;
+ }
+
+ ber = ldap_alloc_ber_with_options( ld );
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ tag = ber_printf( ber, "{iO}", pagesize, cookie );
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+
+ if ( ber_flatten2( ber, value, 1 ) == -1 ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ }
+
+done:;
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ return ld->ld_errno;
+}
+
+
+/* ---------------------------------------------------------------------------
+ ldap_create_page_control
+
+ Create and encode a page control.
+
+ ld (IN) An LDAP session handle
+ pagesize (IN) Page size requested
+ cookie (IN) Opaque structure used by the server to track its
+ location in the search results. NULL on the
+ first call.
+ value (OUT) Control value, SHOULD be freed by calling
+ ldap_memfree() when done.
+ iscritical (IN) Criticality
+ ctrlp (OUT) LDAP control, SHOULD be freed by calling
+ ldap_control_free() when done.
+
+ pagedResultsControl ::= SEQUENCE {
+ controlType 1.2.840.113556.1.4.319,
+ criticality BOOLEAN DEFAULT FALSE,
+ controlValue searchControlValue }
+
+ searchControlValue ::= SEQUENCE {
+ size INTEGER (0..maxInt),
+ -- requested page size from client
+ -- result set size estimate from server
+ cookie OCTET STRING }
+
+ ---------------------------------------------------------------------------*/
+
+int
+ldap_create_page_control(
+ LDAP *ld,
+ ber_int_t pagesize,
+ struct berval *cookie,
+ int iscritical,
+ LDAPControl **ctrlp )
+{
+ struct berval value;
+
+ if ( ctrlp == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ ld->ld_errno = ldap_create_page_control_value( ld,
+ pagesize, cookie, &value );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ ld->ld_errno = ldap_control_create( LDAP_CONTROL_PAGEDRESULTS,
+ iscritical, &value, 0, ctrlp );
+ if ( ld->ld_errno != LDAP_SUCCESS ) {
+ LDAP_FREE( value.bv_val );
+ }
+ }
+
+ return ld->ld_errno;
+}
+
+
+/* ---------------------------------------------------------------------------
+ ldap_parse_pageresponse_control
+
+ Decode a page control.
+
+ ld (IN) An LDAP session handle
+ ctrl (IN) The page response control
+ count (OUT) The number of entries in the page.
+ cookie (OUT) Opaque cookie. Use ldap_memfree() to
+ free the bv_val member of this structure.
+
+ ---------------------------------------------------------------------------*/
+
+int
+ldap_parse_pageresponse_control(
+ LDAP *ld,
+ LDAPControl *ctrl,
+ ber_int_t *countp,
+ struct berval *cookie )
+{
+ BerElement *ber;
+ ber_tag_t tag;
+ ber_int_t count;
+
+ if ( ld == NULL || ctrl == NULL || cookie == NULL ) {
+ if ( ld )
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return LDAP_PARAM_ERROR;
+ }
+
+ /* Create a BerElement from the berval returned in the control. */
+ ber = ber_init( &ctrl->ldctl_value );
+
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ /* Extract the count and cookie from the control. */
+ tag = ber_scanf( ber, "{io}", &count, cookie );
+ ber_free( ber, 1 );
+
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ } else {
+ ld->ld_errno = LDAP_SUCCESS;
+
+ if ( countp != NULL ) {
+ *countp = (unsigned long)count;
+ }
+ }
+
+ return ld->ld_errno;
+}
+
+/* ---------------------------------------------------------------------------
+ ldap_parse_page_control
+
+ Decode a page control.
+
+ ld (IN) An LDAP session handle
+ ctrls (IN) Response controls
+ count (OUT) The number of entries in the page.
+ cookie (OUT) Opaque cookie. Use ldap_memfree() to
+ free the bv_val member of this structure.
+
+ ---------------------------------------------------------------------------*/
+
+int
+ldap_parse_page_control(
+ LDAP *ld,
+ LDAPControl **ctrls,
+ ber_int_t *countp,
+ struct berval **cookiep )
+{
+ LDAPControl *c;
+ struct berval cookie;
+
+ if ( cookiep == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ if ( ctrls == NULL ) {
+ ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
+ return ld->ld_errno;
+ }
+
+ c = ldap_control_find( LDAP_CONTROL_PAGEDRESULTS, ctrls, NULL );
+ if ( c == NULL ) {
+ /* No page control was found. */
+ ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
+ return ld->ld_errno;
+ }
+
+ ld->ld_errno = ldap_parse_pageresponse_control( ld, c, countp, &cookie );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ *cookiep = LDAP_MALLOC( sizeof( struct berval ) );
+ if ( *cookiep == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ } else {
+ **cookiep = cookie;
+ }
+ }
+
+ return ld->ld_errno;
+}
+
diff --git a/libraries/libldap/passwd.c b/libraries/libldap/passwd.c
new file mode 100644
index 0000000..f529de5
--- /dev/null
+++ b/libraries/libldap/passwd.c
@@ -0,0 +1,170 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This program was originally developed by Kurt D. Zeilenga for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/*
+ * LDAP Password Modify (Extended) Operation (RFC 3062)
+ */
+
+int ldap_parse_passwd(
+ LDAP *ld,
+ LDAPMessage *res,
+ struct berval *newpasswd )
+{
+ int rc;
+ struct berval *retdata = NULL;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( res != NULL );
+ assert( newpasswd != NULL );
+
+ newpasswd->bv_val = NULL;
+ newpasswd->bv_len = 0;
+
+ rc = ldap_parse_extended_result( ld, res, NULL, &retdata, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( retdata != NULL ) {
+ ber_tag_t tag;
+ BerElement *ber = ber_init( retdata );
+
+ if ( ber == NULL ) {
+ rc = ld->ld_errno = LDAP_NO_MEMORY;
+ goto done;
+ }
+
+ /* we should check the tag */
+ tag = ber_scanf( ber, "{o}", newpasswd );
+ ber_free( ber, 1 );
+
+ if ( tag == LBER_ERROR ) {
+ rc = ld->ld_errno = LDAP_DECODING_ERROR;
+ }
+ }
+
+done:;
+ ber_bvfree( retdata );
+
+ return rc;
+}
+
+int
+ldap_passwd( LDAP *ld,
+ struct berval *user,
+ struct berval *oldpw,
+ struct berval *newpw,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ int rc;
+ struct berval bv = BER_BVNULL;
+ BerElement *ber = NULL;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( msgidp != NULL );
+
+ if( user != NULL || oldpw != NULL || newpw != NULL ) {
+ /* build change password control */
+ ber = ber_alloc_t( LBER_USE_DER );
+
+ if( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ ber_printf( ber, "{" /*}*/ );
+
+ if( user != NULL ) {
+ ber_printf( ber, "tO",
+ LDAP_TAG_EXOP_MODIFY_PASSWD_ID, user );
+ }
+
+ if( oldpw != NULL ) {
+ ber_printf( ber, "tO",
+ LDAP_TAG_EXOP_MODIFY_PASSWD_OLD, oldpw );
+ }
+
+ if( newpw != NULL ) {
+ ber_printf( ber, "tO",
+ LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, newpw );
+ }
+
+ ber_printf( ber, /*{*/ "N}" );
+
+ rc = ber_flatten2( ber, &bv, 0 );
+
+ if( rc < 0 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ return ld->ld_errno;
+ }
+
+ }
+
+ rc = ldap_extended_operation( ld, LDAP_EXOP_MODIFY_PASSWD,
+ bv.bv_val ? &bv : NULL, sctrls, cctrls, msgidp );
+
+ ber_free( ber, 1 );
+
+ return rc;
+}
+
+int
+ldap_passwd_s(
+ LDAP *ld,
+ struct berval *user,
+ struct berval *oldpw,
+ struct berval *newpw,
+ struct berval *newpasswd,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ int rc;
+ int msgid;
+ LDAPMessage *res;
+
+ rc = ldap_passwd( ld, user, oldpw, newpw, sctrls, cctrls, &msgid );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) {
+ return ld->ld_errno;
+ }
+
+ rc = ldap_parse_passwd( ld, res, newpasswd );
+ if( rc != LDAP_SUCCESS ) {
+ ldap_msgfree( res );
+ return rc;
+ }
+
+ return( ldap_result2error( ld, res, 1 ) );
+}
diff --git a/libraries/libldap/ppolicy.c b/libraries/libldap/ppolicy.c
new file mode 100644
index 0000000..dc93209
--- /dev/null
+++ b/libraries/libldap/ppolicy.c
@@ -0,0 +1,257 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2004 Hewlett-Packard Company.
+ * Portions Copyright 2004 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Howard Chu for inclusion in
+ * OpenLDAP Software, based on prior work by Neil Dunbar (HP).
+ * This work was sponsored by the Hewlett-Packard Company.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
+
+/* IMPLICIT TAGS, all context-specific */
+#define PPOLICY_WARNING 0xa0L /* constructed + 0 */
+#define PPOLICY_ERROR 0x81L /* primitive + 1 */
+
+#define PPOLICY_EXPIRE 0x80L /* primitive + 0 */
+#define PPOLICY_GRACE 0x81L /* primitive + 1 */
+
+/*---
+ ldap_create_passwordpolicy_control
+
+ Create and encode the Password Policy Request
+
+ ld (IN) An LDAP session handle, as obtained from a call to
+ ldap_init().
+
+ ctrlp (OUT) A result parameter that will be assigned the address
+ of an LDAPControl structure that contains the
+ passwordPolicyRequest control created by this function.
+ The memory occupied by the LDAPControl structure
+ SHOULD be freed when it is no longer in use by
+ calling ldap_control_free().
+
+
+ There is no control value for a password policy request
+ ---*/
+
+int
+ldap_create_passwordpolicy_control( LDAP *ld,
+ LDAPControl **ctrlp )
+{
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( ctrlp != NULL );
+
+ ld->ld_errno = ldap_control_create( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
+ 0, NULL, 0, ctrlp );
+
+ return ld->ld_errno;
+}
+
+
+/*---
+ ldap_parse_passwordpolicy_control
+
+ Decode the passwordPolicyResponse control and return information.
+
+ ld (IN) An LDAP session handle.
+
+ ctrl (IN) The address of an
+ LDAPControl structure, either obtained
+ by running through the list of response controls or
+ by a call to ldap_control_find().
+
+ exptimep (OUT) This result parameter is filled in with the number of seconds before
+ the password will expire, if expiration is imminent
+ (imminency defined by the password policy). If expiration
+ is not imminent, the value is set to -1.
+
+ gracep (OUT) This result parameter is filled in with the number of grace logins after
+ the password has expired, before no further login attempts
+ will be allowed.
+
+ errorcodep (OUT) This result parameter is filled in with the error code of the password operation
+ If no error was detected, this error is set to PP_noError.
+
+ Ber encoding
+
+ PasswordPolicyResponseValue ::= SEQUENCE {
+ warning [0] CHOICE {
+ timeBeforeExpiration [0] INTEGER (0 .. maxInt),
+ graceLoginsRemaining [1] INTEGER (0 .. maxInt) } OPTIONAL
+ error [1] ENUMERATED {
+ passwordExpired (0),
+ accountLocked (1),
+ changeAfterReset (2),
+ passwordModNotAllowed (3),
+ mustSupplyOldPassword (4),
+ invalidPasswordSyntax (5),
+ passwordTooShort (6),
+ passwordTooYoung (7),
+ passwordInHistory (8) } OPTIONAL }
+
+---*/
+
+int
+ldap_parse_passwordpolicy_control(
+ LDAP *ld,
+ LDAPControl *ctrl,
+ ber_int_t *expirep,
+ ber_int_t *gracep,
+ LDAPPasswordPolicyError *errorp )
+{
+ BerElement *ber;
+ int exp = -1, grace = -1;
+ ber_tag_t tag;
+ ber_len_t berLen;
+ char *last;
+ int err = PP_noError;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( ctrl != NULL );
+
+ if ( !ctrl->ldctl_value.bv_val ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+ }
+
+ /* Create a BerElement from the berval returned in the control. */
+ ber = ber_init(&ctrl->ldctl_value);
+
+ if (ber == NULL) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return(ld->ld_errno);
+ }
+
+ tag = ber_peek_tag( ber, &berLen );
+ if (tag != LBER_SEQUENCE) goto exit;
+
+ for( tag = ber_first_element( ber, &berLen, &last );
+ tag != LBER_DEFAULT;
+ tag = ber_next_element( ber, &berLen, last ) )
+ {
+ switch (tag) {
+ case PPOLICY_WARNING:
+ ber_skip_tag(ber, &berLen );
+ tag = ber_peek_tag( ber, &berLen );
+ switch( tag ) {
+ case PPOLICY_EXPIRE:
+ if (ber_get_int( ber, &exp ) == LBER_DEFAULT) goto exit;
+ break;
+ case PPOLICY_GRACE:
+ if (ber_get_int( ber, &grace ) == LBER_DEFAULT) goto exit;
+ break;
+ default:
+ goto exit;
+ }
+ break;
+ case PPOLICY_ERROR:
+ if (ber_get_enum( ber, &err ) == LBER_DEFAULT) goto exit;
+ break;
+ default:
+ goto exit;
+ }
+ }
+
+ ber_free(ber, 1);
+
+ /* Return data to the caller for items that were requested. */
+ if (expirep) *expirep = exp;
+ if (gracep) *gracep = grace;
+ if (errorp) *errorp = err;
+
+ ld->ld_errno = LDAP_SUCCESS;
+ return(ld->ld_errno);
+
+ exit:
+ ber_free(ber, 1);
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+}
+
+const char *
+ldap_passwordpolicy_err2txt( LDAPPasswordPolicyError err )
+{
+ switch(err) {
+ case PP_passwordExpired: return "Password expired";
+ case PP_accountLocked: return "Account locked";
+ case PP_changeAfterReset: return "Password must be changed";
+ case PP_passwordModNotAllowed: return "Policy prevents password modification";
+ case PP_mustSupplyOldPassword: return "Policy requires old password in order to change password";
+ case PP_insufficientPasswordQuality: return "Password fails quality checks";
+ case PP_passwordTooShort: return "Password is too short for policy";
+ case PP_passwordTooYoung: return "Password has been changed too recently";
+ case PP_passwordInHistory: return "New password is in list of old passwords";
+ case PP_passwordTooLong: return "Password is too long for policy";
+ case PP_noError: return "No error";
+ default: return "Unknown error code";
+ }
+}
+
+#endif /* LDAP_CONTROL_PASSWORDPOLICYREQUEST */
+
+#ifdef LDAP_CONTROL_X_PASSWORD_EXPIRING
+
+int
+ldap_parse_password_expiring_control(
+ LDAP *ld,
+ LDAPControl *ctrl,
+ long *secondsp )
+{
+ long seconds = 0;
+ char buf[sizeof("-2147483648")];
+ char *next;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( ctrl != NULL );
+
+ if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ||
+ ctrl->ldctl_value.bv_len >= sizeof(buf) ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+ }
+
+ memcpy( buf, ctrl->ldctl_value.bv_val, ctrl->ldctl_value.bv_len );
+ buf[ctrl->ldctl_value.bv_len] = '\0';
+
+ seconds = strtol( buf, &next, 10 );
+ if ( next == buf || next[0] != '\0' ) goto exit;
+
+ if ( secondsp != NULL ) {
+ *secondsp = seconds;
+ }
+
+ ld->ld_errno = LDAP_SUCCESS;
+ return(ld->ld_errno);
+
+ exit:
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+}
+
+#endif /* LDAP_CONTROL_X_PASSWORD_EXPIRING */
diff --git a/libraries/libldap/print.c b/libraries/libldap/print.c
new file mode 100644
index 0000000..6f8681a
--- /dev/null
+++ b/libraries/libldap/print.c
@@ -0,0 +1,62 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/stdarg.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/*
+ * ldap log
+ */
+
+static int ldap_log_check( LDAP *ld, int loglvl )
+{
+ int errlvl;
+
+ if(ld == NULL) {
+ errlvl = ldap_debug;
+ } else {
+ errlvl = ld->ld_debug;
+ }
+
+ return errlvl & loglvl ? 1 : 0;
+}
+
+int ldap_log_printf( LDAP *ld, int loglvl, const char *fmt, ... )
+{
+ char buf[ 1024 ];
+ va_list ap;
+
+ if ( !ldap_log_check( ld, loglvl )) {
+ return 0;
+ }
+
+ va_start( ap, fmt );
+
+ buf[sizeof(buf) - 1] = '\0';
+ vsnprintf( buf, sizeof(buf)-1, fmt, ap );
+
+ va_end(ap);
+
+ (*ber_pvt_log_print)( buf );
+ return 1;
+}
diff --git a/libraries/libldap/psearchctrl.c b/libraries/libldap/psearchctrl.c
new file mode 100644
index 0000000..b465873
--- /dev/null
+++ b/libraries/libldap/psearchctrl.c
@@ -0,0 +1,348 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Howard Chu for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/* Based on draft-ietf-ldapext-c-api-psearch-00 */
+
+/* ---------------------------------------------------------------------------
+ ldap_create_persistentsearch_control_value
+
+ Create and encode the value of the server-side sort control.
+
+ ld (IN) An LDAP session handle, as obtained from a call to
+ ldap_init().
+
+ changetypes (IN) A bit-sensitive field that indicates which kinds of
+ changes the client wants to be informed about. Its
+ value should be LDAP_CHANGETYPE_ANY, or any logical-OR
+ combination of LDAP_CHANGETYPE_ADD,
+ LDAP_CHANGETYPE_DELETE, LDAP_CHANGETYPE_MODIFY, and
+ LDAP_CHANGETYPE_MODDN. This field corresponds to the
+ changeType element of the BER-encoded PersistentSearch
+ control value itself.
+
+ changesonly (IN) A Boolean field that indicates whether the client
+ wishes to only receive searchResultEntry messages for
+ entries that have been changed. If non-zero, only
+ entries that result from changes are returned; other-
+ wise, all of the static entries that match the search
+ criteria are returned before the server begins change
+ notification. This field corresponds to the changes-
+ Only element of the BER-encoded PersistentSearch con-
+ trol value itself.
+
+ return_echg_ctls (IN) A Boolean field that indicates whether the server
+ should send back an Entry Change Notification control
+ with each searchResultEntry that is returned due to a
+ change to an entry. If non-zero, Entry Change
+ Notification controls are requested; if zero, they are
+ not. This field corresponds to the returnECs element
+ of the BER-encoded PersistentSearch control value
+ itself.
+
+ value (OUT) Contains the control value; the bv_val member of the berval structure
+ SHOULD be freed by calling ldap_memfree() when done.
+
+ ---------------------------------------------------------------------------*/
+
+int
+ldap_create_persistentsearch_control_value(
+ LDAP *ld,
+ int changetypes,
+ int changesonly,
+ int return_echg_ctls,
+ struct berval *value )
+{
+ int i;
+ BerElement *ber = NULL;
+ ber_tag_t tag;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ if ( ld == NULL ) return LDAP_PARAM_ERROR;
+ if ( value == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return LDAP_PARAM_ERROR;
+ }
+ if (( changetypes & 0x0f ) != changetypes ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return LDAP_PARAM_ERROR;
+ }
+
+ value->bv_val = NULL;
+ value->bv_len = 0;
+ ld->ld_errno = LDAP_SUCCESS;
+
+ ber = ldap_alloc_ber_with_options( ld );
+ if ( ber == NULL) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ tag = ber_printf( ber, "{ibb}", changetypes, changesonly, return_echg_ctls );
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+
+ if ( ber_flatten2( ber, value, 1 ) == -1 ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ }
+
+ if ( 0 ) {
+error_return:;
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ }
+
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ return ld->ld_errno;
+}
+
+
+/* ---------------------------------------------------------------------------
+ ldap_create_persistentsearch_control
+
+ Create and encode the persistent search control.
+
+ ld (IN) An LDAP session handle, as obtained from a call to
+ ldap_init().
+
+ changetypes (IN) A bit-sensitive field that indicates which kinds of
+ changes the client wants to be informed about. Its
+ value should be LDAP_CHANGETYPE_ANY, or any logical-OR
+ combination of LDAP_CHANGETYPE_ADD,
+ LDAP_CHANGETYPE_DELETE, LDAP_CHANGETYPE_MODIFY, and
+ LDAP_CHANGETYPE_MODDN. This field corresponds to the
+ changeType element of the BER-encoded PersistentSearch
+ control value itself.
+
+ changesonly (IN) A Boolean field that indicates whether the client
+ wishes to only receive searchResultEntry messages for
+ entries that have been changed. If non-zero, only
+ entries that result from changes are returned; other-
+ wise, all of the static entries that match the search
+ criteria are returned before the server begins change
+ notification. This field corresponds to the changes-
+ Only element of the BER-encoded PersistentSearch con-
+ trol value itself.
+
+ return_echg_ctls (IN) A Boolean field that indicates whether the server
+ should send back an Entry Change Notification control
+ with each searchResultEntry that is returned due to a
+ change to an entry. If non-zero, Entry Change
+ Notification controls are requested; if zero, they are
+ not. This field corresponds to the returnECs element
+ of the BER-encoded PersistentSearch control value
+ itself.
+
+ isCritical (IN) 0 - Indicates the control is not critical to the operation.
+ non-zero - The control is critical to the operation.
+
+ ctrlp (OUT) Returns a pointer to the LDAPControl created. This control
+ SHOULD be freed by calling ldap_control_free() when done.
+
+ ---------------------------------------------------------------------------*/
+
+int
+ldap_create_persistentsearch_control(
+ LDAP *ld,
+ int changetypes,
+ int changesonly,
+ int return_echg_ctls,
+ int isCritical,
+ LDAPControl **ctrlp )
+{
+ struct berval value;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ if ( ld == NULL ) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ if ( ctrlp == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ ld->ld_errno = ldap_create_persistentsearch_control_value( ld, changetypes, changesonly, return_echg_ctls, &value );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ ld->ld_errno = ldap_control_create( LDAP_CONTROL_PERSIST_REQUEST,
+ isCritical, &value, 0, ctrlp );
+ if ( ld->ld_errno != LDAP_SUCCESS ) {
+ LDAP_FREE( value.bv_val );
+ }
+ }
+
+ return ld->ld_errno;
+}
+
+
+/* ---------------------------------------------------------------------------
+ ldap_parse_entrychange_control
+
+ Decode the entry change notification control return information.
+
+ ld (IN) An LDAP session handle, as obtained from a call to
+ ldap_init().
+
+ ctrl (IN) The address of the LDAP Control Structure.
+
+ chgtypep (OUT) This result parameter is filled in with one of the
+ following values to indicate the type of change that was
+ made that caused the entry to be returned:
+ LDAP_CONTROL_PERSIST_ENTRY_CHANGE_ADD (1),
+ LDAP_CONTROL_PERSIST_ENTRY_CHANGE_DELETE (2),
+ LDAP_CONTROL_PERSIST_ENTRY_CHANGE_MODIFY (4), or
+ LDAP_CONTROL_PERSIST_ENTRY_CHANGE_RENAME (8).
+ If this parameter is NULL, the change type information
+ is not returned.
+
+ prevdnp (OUT) This result parameter points to the DN the
+ entry had before it was renamed and/or moved by a
+ modifyDN operation. It is set to NULL for other types
+ of changes. If this parameter is NULL, the previous DN
+ information is not returned. The returned value is a
+ pointer to the contents of the control; it is not a
+ copy of the data.
+
+ chgnumpresentp (OUT) This result parameter is filled in with a non-zero
+ value if a change number was returned in the control
+ (the change number is optional and servers MAY choose
+ not to return it). If this parameter is NULL, no indication
+ of whether the change number was present is returned.
+
+ chgnump (OUT) This result parameter is filled in with the change number
+ if one was returned in the control. If this parameter
+ is NULL, the change number is not returned.
+
+ ---------------------------------------------------------------------------*/
+
+int
+ldap_parse_entrychange_control(
+ LDAP *ld,
+ LDAPControl *ctrl,
+ int *chgtypep,
+ struct berval *prevdnp,
+ int *chgnumpresentp,
+ long *chgnump )
+{
+ BerElement *ber;
+ ber_tag_t tag, berTag;
+ ber_len_t berLen;
+ ber_int_t chgtype;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( ctrl != NULL );
+
+ if (ld == NULL) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ if (ctrl == NULL) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return(ld->ld_errno);
+ }
+
+ if ( !ctrl->ldctl_value.bv_val ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+ }
+
+ /* Create a BerElement from the berval returned in the control. */
+ ber = ber_init(&ctrl->ldctl_value);
+
+ if (ber == NULL) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return(ld->ld_errno);
+ }
+
+ if ( prevdnp != NULL ) {
+ BER_BVZERO( prevdnp );
+ }
+ if ( chgnumpresentp != NULL )
+ *chgnumpresentp = 0;
+ if ( chgnump != NULL )
+ *chgnump = 0;
+
+ /* Extract the change type from the control. */
+ tag = ber_scanf(ber, "{e" /*}*/, &chgtype);
+
+ if( tag != LBER_ENUMERATED ) {
+ ber_free(ber, 1);
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+ }
+ if ( chgtypep != NULL )
+ *chgtypep = chgtype;
+
+ tag = ber_peek_tag( ber, &berLen );
+ if ( berLen ) {
+ if (tag == LBER_OCTETSTRING) {
+ if (prevdnp != NULL) {
+ tag = ber_get_stringbv( ber, prevdnp, 0 );
+ } else {
+ struct berval bv;
+ tag = ber_skip_element( ber, &bv );
+ }
+ if ( tag == LBER_ERROR ) {
+ ber_free(ber, 1);
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+ }
+ tag = ber_peek_tag( ber, &berLen );
+ }
+
+ if ( chgnumpresentp != NULL || chgnump != NULL ) {
+ ber_int_t chgnum = 0;
+ int present = 0;
+ if (tag == LBER_INTEGER) {
+ present = 1;
+ tag = ber_get_int( ber, &chgnum );
+ if ( tag == LBER_ERROR ) {
+ ber_free(ber, 1);
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+ }
+ if ( chgnumpresentp != NULL )
+ *chgnumpresentp = present;
+ if ( chgnump != NULL )
+ *chgnump = chgnum;
+ }
+ }
+ }
+
+ ber_free(ber,1);
+
+ ld->ld_errno = LDAP_SUCCESS;
+ return(ld->ld_errno);
+}
diff --git a/libraries/libldap/rdwr.c b/libraries/libldap/rdwr.c
new file mode 100644
index 0000000..dde9403
--- /dev/null
+++ b/libraries/libldap/rdwr.c
@@ -0,0 +1,463 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* This work was initially developed by Kurt D. Zeilenga for inclusion
+ * in OpenLDAP Software. Additional significant contributors include:
+ * Stuart Lynne
+ */
+
+/*
+ * This is an improved implementation of Reader/Writer locks does
+ * not protect writers from starvation. That is, if a writer is
+ * currently waiting on a reader, any new reader will get
+ * the lock before the writer.
+ *
+ * Does not support cancellation nor does any status checking.
+ */
+/* Adapted from publicly available examples for:
+ * "Programming with Posix Threads"
+ * by David R Butenhof, Addison-Wesley
+ * http://cseng.aw.com/bookpage.taf?ISBN=0-201-63392-2
+ */
+
+#include "portable.h"
+
+#include <ac/stdlib.h>
+
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#ifdef LDAP_R_COMPILE
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_RDWR_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+
+/*
+ * implementations that provide their own compatible
+ * reader/writer locks define LDAP_THREAD_HAVE_RDWR
+ * in ldap_pvt_thread.h
+ */
+#ifndef LDAP_THREAD_HAVE_RDWR
+
+struct ldap_int_thread_rdwr_s {
+ ldap_pvt_thread_mutex_t ltrw_mutex;
+ ldap_pvt_thread_cond_t ltrw_read; /* wait for read */
+ ldap_pvt_thread_cond_t ltrw_write; /* wait for write */
+ int ltrw_valid;
+#define LDAP_PVT_THREAD_RDWR_VALID 0x0bad
+ int ltrw_r_active;
+ int ltrw_w_active;
+ int ltrw_r_wait;
+ int ltrw_w_wait;
+#ifdef LDAP_RDWR_DEBUG
+ /* keep track of who has these locks */
+#define MAX_READERS 32
+ int ltrw_more_readers; /* Set if ltrw_readers[] is incomplete */
+ ldap_pvt_thread_t ltrw_readers[MAX_READERS];
+ ldap_pvt_thread_t ltrw_writer;
+#endif
+};
+
+int
+ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+
+ rw = (struct ldap_int_thread_rdwr_s *) LDAP_CALLOC( 1,
+ sizeof( struct ldap_int_thread_rdwr_s ) );
+ if ( !rw )
+ return LDAP_NO_MEMORY;
+
+ /* we should check return results */
+ ldap_pvt_thread_mutex_init( &rw->ltrw_mutex );
+ ldap_pvt_thread_cond_init( &rw->ltrw_read );
+ ldap_pvt_thread_cond_init( &rw->ltrw_write );
+
+ rw->ltrw_valid = LDAP_PVT_THREAD_RDWR_VALID;
+
+ *rwlock = rw;
+ return 0;
+}
+
+int
+ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+
+ if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
+
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ /* active threads? */
+ if( rw->ltrw_r_active > 0 || rw->ltrw_w_active > 0) {
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+ return LDAP_PVT_THREAD_EBUSY;
+ }
+
+ /* waiting threads? */
+ if( rw->ltrw_r_wait > 0 || rw->ltrw_w_wait > 0) {
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+ return LDAP_PVT_THREAD_EBUSY;
+ }
+
+ rw->ltrw_valid = 0;
+
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+
+ ldap_pvt_thread_mutex_destroy( &rw->ltrw_mutex );
+ ldap_pvt_thread_cond_destroy( &rw->ltrw_read );
+ ldap_pvt_thread_cond_destroy( &rw->ltrw_write );
+
+ LDAP_FREE(rw);
+ *rwlock = NULL;
+ return 0;
+}
+
+int ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+
+ if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
+
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ if( rw->ltrw_w_active > 0 ) {
+ /* writer is active */
+
+ rw->ltrw_r_wait++;
+
+ do {
+ ldap_pvt_thread_cond_wait(
+ &rw->ltrw_read, &rw->ltrw_mutex );
+ } while( rw->ltrw_w_active > 0 );
+
+ rw->ltrw_r_wait--;
+ assert( rw->ltrw_r_wait >= 0 );
+ }
+
+#ifdef LDAP_RDWR_DEBUG
+ if( rw->ltrw_r_active < MAX_READERS )
+ rw->ltrw_readers[rw->ltrw_r_active] = ldap_pvt_thread_self();
+ else
+ rw->ltrw_more_readers = 1;
+#endif
+ rw->ltrw_r_active++;
+
+
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+
+ return 0;
+}
+
+int ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+
+ if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
+
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ if( rw->ltrw_w_active > 0) {
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+ return LDAP_PVT_THREAD_EBUSY;
+ }
+
+#ifdef LDAP_RDWR_DEBUG
+ if( rw->ltrw_r_active < MAX_READERS )
+ rw->ltrw_readers[rw->ltrw_r_active] = ldap_pvt_thread_self();
+ else
+ rw->ltrw_more_readers = 1;
+#endif
+ rw->ltrw_r_active++;
+
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+
+ return 0;
+}
+
+int ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+
+ if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
+
+ rw->ltrw_r_active--;
+#ifdef LDAP_RDWR_DEBUG
+ /* Remove us from the list of readers */
+ {
+ ldap_pvt_thread_t self = ldap_pvt_thread_self();
+ int i, j;
+ for( i = j = rw->ltrw_r_active; i >= 0; i--) {
+ if (rw->ltrw_readers[i] == self) {
+ rw->ltrw_readers[i] = rw->ltrw_readers[j];
+ rw->ltrw_readers[j] = 0;
+ break;
+ }
+ }
+ if( !rw->ltrw_more_readers )
+ assert( i >= 0 );
+ else if( j == 0 )
+ rw->ltrw_more_readers = 0;
+ }
+#endif
+
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ if (rw->ltrw_r_active == 0 && rw->ltrw_w_wait > 0 ) {
+ ldap_pvt_thread_cond_signal( &rw->ltrw_write );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+
+ return 0;
+}
+
+int ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+
+ if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
+
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ if ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 ) {
+ rw->ltrw_w_wait++;
+
+ do {
+ ldap_pvt_thread_cond_wait(
+ &rw->ltrw_write, &rw->ltrw_mutex );
+ } while ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 );
+
+ rw->ltrw_w_wait--;
+ assert( rw->ltrw_w_wait >= 0 );
+ }
+
+#ifdef LDAP_RDWR_DEBUG
+ rw->ltrw_writer = ldap_pvt_thread_self();
+#endif
+ rw->ltrw_w_active++;
+
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+
+ return 0;
+}
+
+int ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+
+ if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
+
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ if ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 ) {
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+ return LDAP_PVT_THREAD_EBUSY;
+ }
+
+#ifdef LDAP_RDWR_DEBUG
+ rw->ltrw_writer = ldap_pvt_thread_self();
+#endif
+ rw->ltrw_w_active++;
+
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+
+ return 0;
+}
+
+int ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+
+ if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
+
+ rw->ltrw_w_active--;
+
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ if (rw->ltrw_r_wait > 0) {
+ ldap_pvt_thread_cond_broadcast( &rw->ltrw_read );
+
+ } else if (rw->ltrw_w_wait > 0) {
+ ldap_pvt_thread_cond_signal( &rw->ltrw_write );
+ }
+
+#ifdef LDAP_RDWR_DEBUG
+ assert( rw->ltrw_writer == ldap_pvt_thread_self() );
+ rw->ltrw_writer = 0;
+#endif
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+
+ return 0;
+}
+
+#ifdef LDAP_RDWR_DEBUG
+
+/* just for testing,
+ * return 0 if false, suitable for assert(ldap_pvt_thread_rdwr_Xchk(rdwr))
+ *
+ * Currently they don't check if the calling thread is the one
+ * that has the lock, just that there is a reader or writer.
+ *
+ * Basically sufficient for testing that places that should have
+ * a lock are caught.
+ */
+
+int ldap_pvt_thread_rdwr_readers(ldap_pvt_thread_rdwr_t *rwlock)
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ return( rw->ltrw_r_active );
+}
+
+int ldap_pvt_thread_rdwr_writers(ldap_pvt_thread_rdwr_t *rwlock)
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ return( rw->ltrw_w_active );
+}
+
+int ldap_pvt_thread_rdwr_active(ldap_pvt_thread_rdwr_t *rwlock)
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ return(ldap_pvt_thread_rdwr_readers(rwlock) +
+ ldap_pvt_thread_rdwr_writers(rwlock));
+}
+
+#endif /* LDAP_RDWR_DEBUG */
+
+#endif /* LDAP_THREAD_HAVE_RDWR */
+
+#endif /* LDAP_R_COMPILE */
diff --git a/libraries/libldap/references.c b/libraries/libldap/references.c
new file mode 100644
index 0000000..ee04e63
--- /dev/null
+++ b/libraries/libldap/references.c
@@ -0,0 +1,147 @@
+/* references.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+LDAPMessage *
+ldap_first_reference( LDAP *ld, LDAPMessage *chain )
+{
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( chain != NULL );
+
+ return chain->lm_msgtype == LDAP_RES_SEARCH_REFERENCE
+ ? chain
+ : ldap_next_reference( ld, chain );
+}
+
+LDAPMessage *
+ldap_next_reference( LDAP *ld, LDAPMessage *ref )
+{
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( ref != NULL );
+
+ for (
+ ref = ref->lm_chain;
+ ref != NULL;
+ ref = ref->lm_chain )
+ {
+ if( ref->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ) {
+ return( ref );
+ }
+ }
+
+ return( NULL );
+}
+
+int
+ldap_count_references( LDAP *ld, LDAPMessage *chain )
+{
+ int i;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ for ( i = 0; chain != NULL; chain = chain->lm_chain ) {
+ if( chain->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ) {
+ i++;
+ }
+ }
+
+ return( i );
+}
+
+int
+ldap_parse_reference(
+ LDAP *ld,
+ LDAPMessage *ref,
+ char ***referralsp,
+ LDAPControl ***serverctrls,
+ int freeit)
+{
+ BerElement be;
+ char **refs = NULL;
+ int rc;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( ref != NULL );
+
+ if( ref->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ /* make a private copy of BerElement */
+ AC_MEMCPY(&be, ref->lm_ber, sizeof(be));
+
+ if ( ber_scanf( &be, "{v" /*}*/, &refs ) == LBER_ERROR ) {
+ rc = LDAP_DECODING_ERROR;
+ goto free_and_return;
+ }
+
+ if ( serverctrls == NULL ) {
+ rc = LDAP_SUCCESS;
+ goto free_and_return;
+ }
+
+ if ( ber_scanf( &be, /*{*/ "}" ) == LBER_ERROR ) {
+ rc = LDAP_DECODING_ERROR;
+ goto free_and_return;
+ }
+
+ rc = ldap_pvt_get_controls( &be, serverctrls );
+
+free_and_return:
+
+ if( referralsp != NULL ) {
+ /* provide references regardless of return code */
+ *referralsp = refs;
+
+ } else {
+ LDAP_VFREE( refs );
+ }
+
+ if( freeit ) {
+ ldap_msgfree( ref );
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ ld->ld_errno = rc;
+
+ if( ld->ld_matched != NULL ) {
+ LDAP_FREE( ld->ld_matched );
+ ld->ld_matched = NULL;
+ }
+
+ if( ld->ld_error != NULL ) {
+ LDAP_FREE( ld->ld_error );
+ ld->ld_error = NULL;
+ }
+ }
+
+ return rc;
+}
diff --git a/libraries/libldap/request.c b/libraries/libldap/request.c
new file mode 100644
index 0000000..95e402a
--- /dev/null
+++ b/libraries/libldap/request.c
@@ -0,0 +1,1714 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+/* This notice applies to changes, created by or for Novell, Inc.,
+ * to preexisting works for which notices appear elsewhere in this file.
+ *
+ * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
+ *
+ * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
+ * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
+ * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
+ * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
+ * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
+ * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
+ * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
+ * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
+ *---
+ * Modification to OpenLDAP source by Novell, Inc.
+ * April 2000 sfs Added code to chase V3 referrals
+ * request.c - sending of ldap requests; handling of referrals
+ *---
+ * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
+ * can be found in the file "build/LICENSE-2.0.1" in this distribution
+ * of OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/errno.h>
+#include <ac/param.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include "ldap-int.h"
+#include "lber.h"
+
+/* used by ldap_send_server_request and ldap_new_connection */
+#ifdef LDAP_R_COMPILE
+#define LDAP_CONN_LOCK_IF(nolock) \
+ { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); }
+#define LDAP_CONN_UNLOCK_IF(nolock) \
+ { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); }
+#define LDAP_REQ_LOCK_IF(nolock) \
+ { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); }
+#define LDAP_REQ_UNLOCK_IF(nolock) \
+ { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); }
+#define LDAP_RES_LOCK_IF(nolock) \
+ { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); }
+#define LDAP_RES_UNLOCK_IF(nolock) \
+ { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); }
+#else
+#define LDAP_CONN_LOCK_IF(nolock)
+#define LDAP_CONN_UNLOCK_IF(nolock)
+#define LDAP_REQ_LOCK_IF(nolock)
+#define LDAP_REQ_UNLOCK_IF(nolock)
+#define LDAP_RES_LOCK_IF(nolock)
+#define LDAP_RES_UNLOCK_IF(nolock)
+#endif
+
+static LDAPConn *find_connection LDAP_P(( LDAP *ld, LDAPURLDesc *srv, int any ));
+static void use_connection LDAP_P(( LDAP *ld, LDAPConn *lc ));
+static void ldap_free_request_int LDAP_P(( LDAP *ld, LDAPRequest *lr ));
+
+static BerElement *
+re_encode_request( LDAP *ld,
+ BerElement *origber,
+ ber_int_t msgid,
+ int sref,
+ LDAPURLDesc *srv,
+ int *type );
+
+BerElement *
+ldap_alloc_ber_with_options( LDAP *ld )
+{
+ BerElement *ber;
+
+ ber = ber_alloc_t( ld->ld_lberoptions );
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ }
+
+ return( ber );
+}
+
+
+void
+ldap_set_ber_options( LDAP *ld, BerElement *ber )
+{
+ /* ld_lberoptions is constant, hence no lock */
+ ber->ber_options = ld->ld_lberoptions;
+}
+
+
+/* sets needed mutexes - no mutexes set to this point */
+ber_int_t
+ldap_send_initial_request(
+ LDAP *ld,
+ ber_tag_t msgtype,
+ const char *dn,
+ BerElement *ber,
+ ber_int_t msgid)
+{
+ int rc = 1;
+ ber_socket_t sd = AC_SOCKET_INVALID;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_send_initial_request\n" );
+
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ) == -1 ) {
+ /* not connected yet */
+ rc = ldap_open_defconn( ld );
+ if ( rc == 0 ) {
+ ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb,
+ LBER_SB_OPT_GET_FD, &sd );
+ }
+ }
+ if ( ld->ld_defconn && ld->ld_defconn->lconn_status == LDAP_CONNST_CONNECTING )
+ rc = ldap_int_check_async_open( ld, sd );
+ if( rc < 0 ) {
+ ber_free( ber, 1 );
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ return( -1 );
+ } else if ( rc == 0 ) {
+ Debug0( LDAP_DEBUG_TRACE,
+ "ldap_open_defconn: successful\n" );
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ if (LDAP_IS_UDP(ld)) {
+ if (msgtype == LDAP_REQ_BIND) {
+ LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
+ if (ld->ld_options.ldo_cldapdn)
+ ldap_memfree(ld->ld_options.ldo_cldapdn);
+ ld->ld_options.ldo_cldapdn = ldap_strdup(dn);
+ ber_free( ber, 1 );
+ LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ return 0;
+ }
+ if (msgtype != LDAP_REQ_ABANDON && msgtype != LDAP_REQ_SEARCH)
+ {
+ ber_free( ber, 1 );
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ return LDAP_PARAM_ERROR;
+ }
+ }
+#endif
+ LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+ rc = ldap_send_server_request( ld, ber, msgid, NULL,
+ NULL, NULL, NULL, 0, 0 );
+ LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ return(rc);
+}
+
+
+/* protected by conn_mutex */
+int
+ldap_int_flush_request(
+ LDAP *ld,
+ LDAPRequest *lr )
+{
+ LDAPConn *lc = lr->lr_conn;
+
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
+ if ( ber_flush2( lc->lconn_sb, lr->lr_ber, LBER_FLUSH_FREE_NEVER ) != 0 ) {
+ if (( sock_errno() == EAGAIN ) || ( sock_errno() == ENOTCONN )) {
+ /* ENOTCONN is returned in Solaris 10 */
+ /* need to continue write later */
+ lr->lr_status = LDAP_REQST_WRITING;
+ ldap_mark_select_write( ld, lc->lconn_sb );
+ ld->ld_errno = LDAP_BUSY;
+ return -2;
+ } else {
+ ld->ld_errno = LDAP_SERVER_DOWN;
+ ldap_free_request( ld, lr );
+ ldap_free_connection( ld, lc, 0, 0 );
+ return( -1 );
+ }
+ } else {
+ if ( lr->lr_parent == NULL ) {
+ lr->lr_ber->ber_end = lr->lr_ber->ber_ptr;
+ lr->lr_ber->ber_ptr = lr->lr_ber->ber_buf;
+ }
+ lr->lr_status = LDAP_REQST_INPROGRESS;
+
+ /* sent -- waiting for a response */
+ ldap_mark_select_read( ld, lc->lconn_sb );
+ ldap_clear_select_write( ld, lc->lconn_sb );
+ }
+ return 0;
+}
+
+/*
+ * protected by req_mutex
+ * if m_noconn then protect using conn_lock
+ * else already protected with conn_lock
+ * if m_res then also protected by res_mutex
+ */
+
+int
+ldap_send_server_request(
+ LDAP *ld,
+ BerElement *ber,
+ ber_int_t msgid,
+ LDAPRequest *parentreq,
+ LDAPURLDesc **srvlist,
+ LDAPConn *lc,
+ LDAPreqinfo *bind,
+ int m_noconn,
+ int m_res )
+{
+ LDAPRequest *lr;
+ int incparent, rc;
+
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
+ Debug0( LDAP_DEBUG_TRACE, "ldap_send_server_request\n" );
+
+ incparent = 0;
+ ld->ld_errno = LDAP_SUCCESS; /* optimistic */
+
+ LDAP_CONN_LOCK_IF(m_noconn);
+ if ( lc == NULL ) {
+ if ( srvlist == NULL ) {
+ lc = ld->ld_defconn;
+ } else {
+ lc = find_connection( ld, *srvlist, 1 );
+ if ( lc == NULL ) {
+ if ( (bind != NULL) && (parentreq != NULL) ) {
+ /* Remember the bind in the parent */
+ incparent = 1;
+ ++parentreq->lr_outrefcnt;
+ }
+ lc = ldap_new_connection( ld, srvlist, 0,
+ 1, bind, 1, m_res );
+ }
+ }
+ }
+
+ /* async connect... */
+ if ( lc != NULL && lc->lconn_status == LDAP_CONNST_CONNECTING ) {
+ ber_socket_t sd = AC_SOCKET_ERROR;
+ struct timeval tv = { 0 };
+
+ ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sd );
+
+ /* poll ... */
+ switch ( ldap_int_poll( ld, sd, &tv, 1 ) ) {
+ case 0:
+ /* go on! */
+ lc->lconn_status = LDAP_CONNST_CONNECTED;
+ break;
+
+ case -2:
+ /* async only occurs if a network timeout is set */
+
+ /* honor network timeout */
+ LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
+ if ( time( NULL ) - lc->lconn_created <= ld->ld_options.ldo_tm_net.tv_sec )
+ {
+ /* caller will have to call again */
+ ld->ld_errno = LDAP_X_CONNECTING;
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
+ /* fallthru */
+
+ default:
+ /* error */
+ break;
+ }
+ }
+
+ if ( lc == NULL || lc->lconn_status != LDAP_CONNST_CONNECTED ) {
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ ld->ld_errno = LDAP_SERVER_DOWN;
+ }
+
+ ber_free( ber, 1 );
+ if ( incparent ) {
+ /* Forget about the bind */
+ --parentreq->lr_outrefcnt;
+ }
+ LDAP_CONN_UNLOCK_IF(m_noconn);
+ return( -1 );
+ }
+
+ use_connection( ld, lc );
+
+#ifdef LDAP_CONNECTIONLESS
+ if ( LDAP_IS_UDP( ld )) {
+ BerElement tmpber = *ber;
+ ber_rewind( &tmpber );
+ LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
+ rc = ber_write( &tmpber, ld->ld_options.ldo_peer,
+ sizeof( struct sockaddr_storage ), 0 );
+ LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
+ if ( rc == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ LDAP_CONN_UNLOCK_IF(m_noconn);
+ return rc;
+ }
+ }
+#endif
+
+ /* If we still have an incomplete write, try to finish it before
+ * dealing with the new request. If we don't finish here, return
+ * LDAP_BUSY and let the caller retry later. We only allow a single
+ * request to be in WRITING state.
+ */
+ rc = 0;
+ if ( ld->ld_requests != NULL ) {
+ TAvlnode *node = ldap_tavl_end( ld->ld_requests, TAVL_DIR_RIGHT );
+ LDAPRequest *lr;
+
+ assert( node != NULL );
+ lr = node->avl_data;
+ if ( lr->lr_status == LDAP_REQST_WRITING &&
+ ldap_int_flush_request( ld, lr ) < 0 ) {
+ rc = -1;
+ }
+ }
+ if ( rc ) {
+ ber_free( ber, 1 );
+ LDAP_CONN_UNLOCK_IF(m_noconn);
+ return rc;
+ }
+
+ lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ) );
+ if ( lr == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ ldap_free_connection( ld, lc, 0, 0 );
+ ber_free( ber, 1 );
+ if ( incparent ) {
+ /* Forget about the bind */
+ --parentreq->lr_outrefcnt;
+ }
+ LDAP_CONN_UNLOCK_IF(m_noconn);
+ return( -1 );
+ }
+ lr->lr_msgid = msgid;
+ lr->lr_status = LDAP_REQST_INPROGRESS;
+ lr->lr_res_errno = LDAP_SUCCESS; /* optimistic */
+ lr->lr_ber = ber;
+ lr->lr_conn = lc;
+ if ( parentreq != NULL ) { /* sub-request */
+ if ( !incparent ) {
+ /* Increment if we didn't do it before the bind */
+ ++parentreq->lr_outrefcnt;
+ }
+ lr->lr_origid = parentreq->lr_origid;
+ lr->lr_parentcnt = ++parentreq->lr_parentcnt;
+ lr->lr_parent = parentreq;
+ lr->lr_refnext = parentreq->lr_child;
+ parentreq->lr_child = lr;
+ } else { /* original request */
+ lr->lr_origid = lr->lr_msgid;
+ }
+
+ /* Extract requestDN for future reference */
+#ifdef LDAP_CONNECTIONLESS
+ if ( !LDAP_IS_UDP(ld) )
+#endif
+ {
+ BerElement tmpber = *ber;
+ ber_int_t bint;
+ ber_tag_t tag, rtag;
+
+ ber_reset( &tmpber, 1 );
+ rtag = ber_scanf( &tmpber, "{it", /*}*/ &bint, &tag );
+ switch ( tag ) {
+ case LDAP_REQ_BIND:
+ rtag = ber_scanf( &tmpber, "{i" /*}*/, &bint );
+ break;
+ case LDAP_REQ_DELETE:
+ break;
+ default:
+ rtag = ber_scanf( &tmpber, "{" /*}*/ );
+ case LDAP_REQ_ABANDON:
+ break;
+ }
+ if ( tag != LDAP_REQ_ABANDON ) {
+ ber_skip_tag( &tmpber, &lr->lr_dn.bv_len );
+ lr->lr_dn.bv_val = tmpber.ber_ptr;
+ }
+ }
+
+ rc = ldap_tavl_insert( &ld->ld_requests, lr, ldap_req_cmp, ldap_avl_dup_error );
+ assert( rc == LDAP_SUCCESS );
+
+ ld->ld_errno = LDAP_SUCCESS;
+ if ( ldap_int_flush_request( ld, lr ) == -1 ) {
+ msgid = -1;
+ }
+
+ LDAP_CONN_UNLOCK_IF(m_noconn);
+ return( msgid );
+}
+
+/* return 0 if no StartTLS ext, 1 if present, 2 if critical */
+static int
+find_tls_ext( LDAPURLDesc *srv )
+{
+ int i, crit;
+ char *ext;
+
+ if ( !srv->lud_exts )
+ return 0;
+
+ for (i=0; srv->lud_exts[i]; i++) {
+ crit = 0;
+ ext = srv->lud_exts[i];
+ if ( ext[0] == '!') {
+ ext++;
+ crit = 1;
+ }
+ if ( !strcasecmp( ext, "StartTLS" ) ||
+ !strcasecmp( ext, "X-StartTLS" ) ||
+ !strcmp( ext, LDAP_EXOP_START_TLS )) {
+ return crit + 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * always protected by conn_mutex
+ * optionally protected by req_mutex and res_mutex
+ */
+LDAPConn *
+ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb,
+ int connect, LDAPreqinfo *bind, int m_req, int m_res )
+{
+ LDAPConn *lc;
+ int async = 0;
+
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
+ Debug3( LDAP_DEBUG_TRACE, "ldap_new_connection %d %d %d\n",
+ use_ldsb, connect, (bind != NULL) );
+ /*
+ * make a new LDAP server connection
+ * XXX open connection synchronously for now
+ */
+ lc = (LDAPConn *)LDAP_CALLOC( 1, sizeof( LDAPConn ) );
+ if ( lc == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return( NULL );
+ }
+
+ if ( use_ldsb ) {
+ assert( ld->ld_sb != NULL );
+ lc->lconn_sb = ld->ld_sb;
+
+ } else {
+ lc->lconn_sb = ber_sockbuf_alloc();
+ if ( lc->lconn_sb == NULL ) {
+ LDAP_FREE( (char *)lc );
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return( NULL );
+ }
+ }
+
+ if ( connect ) {
+ LDAPURLDesc **srvp, *srv = NULL;
+
+ async = LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_CONNECT_ASYNC );
+
+ for ( srvp = srvlist; *srvp != NULL; srvp = &(*srvp)->lud_next ) {
+ int rc;
+
+ rc = ldap_int_open_connection( ld, lc, *srvp, async );
+ if ( rc != -1 ) {
+ srv = *srvp;
+
+ /* If we fully connected, async is moot */
+ if ( rc == 0 )
+ async = 0;
+
+ if ( ld->ld_urllist_proc && ( !async || rc != -2 ) ) {
+ ld->ld_urllist_proc( ld, srvlist, srvp, ld->ld_urllist_params );
+ }
+
+ break;
+ }
+ }
+
+ if ( srv == NULL ) {
+ if ( !use_ldsb ) {
+ ber_sockbuf_free( lc->lconn_sb );
+ }
+ LDAP_FREE( (char *)lc );
+ ld->ld_errno = LDAP_SERVER_DOWN;
+ return( NULL );
+ }
+
+ lc->lconn_server = ldap_url_dup( srv );
+ if ( !lc->lconn_server ) {
+ if ( !use_ldsb )
+ ber_sockbuf_free( lc->lconn_sb );
+ LDAP_FREE( (char *)lc );
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return( NULL );
+ }
+ }
+
+ lc->lconn_status = async ? LDAP_CONNST_CONNECTING : LDAP_CONNST_CONNECTED;
+ lc->lconn_next = ld->ld_conns;
+ ld->ld_conns = lc;
+
+ if ( connect ) {
+#ifdef HAVE_TLS
+ if ( lc->lconn_server->lud_exts ) {
+ int rc, ext = find_tls_ext( lc->lconn_server );
+ if ( ext ) {
+ LDAPConn *savedefconn;
+
+ savedefconn = ld->ld_defconn;
+ ++lc->lconn_refcnt; /* avoid premature free */
+ ld->ld_defconn = lc;
+
+ LDAP_REQ_UNLOCK_IF(m_req);
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ LDAP_RES_UNLOCK_IF(m_res);
+ rc = ldap_start_tls_s( ld, NULL, NULL );
+ LDAP_RES_LOCK_IF(m_res);
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ LDAP_REQ_LOCK_IF(m_req);
+ ld->ld_defconn = savedefconn;
+ --lc->lconn_refcnt;
+
+ if ( rc != LDAP_SUCCESS && ext == 2 ) {
+ ldap_free_connection( ld, lc, 1, 0 );
+ return NULL;
+ }
+ }
+ }
+#endif
+ }
+
+ if ( bind != NULL ) {
+ int err = 0;
+ LDAPConn *savedefconn;
+
+ /* Set flag to prevent additional referrals
+ * from being processed on this
+ * connection until the bind has completed
+ */
+ lc->lconn_rebind_inprogress = 1;
+ /* V3 rebind function */
+ if ( ld->ld_rebind_proc != NULL) {
+ LDAPURLDesc *srvfunc;
+
+ srvfunc = ldap_url_dup( *srvlist );
+ if ( srvfunc == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ err = -1;
+ } else {
+ savedefconn = ld->ld_defconn;
+ ++lc->lconn_refcnt; /* avoid premature free */
+ ld->ld_defconn = lc;
+
+ Debug0( LDAP_DEBUG_TRACE, "Call application rebind_proc\n" );
+ LDAP_REQ_UNLOCK_IF(m_req);
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ LDAP_RES_UNLOCK_IF(m_res);
+ err = (*ld->ld_rebind_proc)( ld,
+ bind->ri_url, bind->ri_request, bind->ri_msgid,
+ ld->ld_rebind_params );
+ LDAP_RES_LOCK_IF(m_res);
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ LDAP_REQ_LOCK_IF(m_req);
+
+ ld->ld_defconn = savedefconn;
+ --lc->lconn_refcnt;
+
+ if ( err != 0 ) {
+ err = -1;
+ ldap_free_connection( ld, lc, 1, 0 );
+ lc = NULL;
+ }
+ ldap_free_urldesc( srvfunc );
+ }
+
+ } else {
+ int msgid, rc;
+ struct berval passwd = BER_BVNULL;
+
+ savedefconn = ld->ld_defconn;
+ ++lc->lconn_refcnt; /* avoid premature free */
+ ld->ld_defconn = lc;
+
+ Debug0( LDAP_DEBUG_TRACE,
+ "anonymous rebind via ldap_sasl_bind(\"\")\n" );
+
+ LDAP_REQ_UNLOCK_IF(m_req);
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ LDAP_RES_UNLOCK_IF(m_res);
+ rc = ldap_sasl_bind( ld, "", LDAP_SASL_SIMPLE, &passwd,
+ NULL, NULL, &msgid );
+ if ( rc != LDAP_SUCCESS ) {
+ err = -1;
+
+ } else {
+ for ( err = 1; err > 0; ) {
+ struct timeval tv = { 0, 100000 };
+ LDAPMessage *res = NULL;
+
+ switch ( ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res ) ) {
+ case -1:
+ err = -1;
+ break;
+
+ case 0:
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_yield();
+#endif
+ break;
+
+ case LDAP_RES_BIND:
+ rc = ldap_parse_result( ld, res, &err, NULL, NULL, NULL, NULL, 1 );
+ if ( rc != LDAP_SUCCESS ) {
+ err = -1;
+
+ } else if ( err != LDAP_SUCCESS ) {
+ err = -1;
+ }
+ /* else err == LDAP_SUCCESS == 0 */
+ break;
+
+ default:
+ Debug3( LDAP_DEBUG_TRACE,
+ "ldap_new_connection %p: "
+ "unexpected response %d "
+ "from BIND request id=%d\n",
+ (void *) ld, ldap_msgtype( res ), msgid );
+ err = -1;
+ break;
+ }
+ }
+ }
+ LDAP_RES_LOCK_IF(m_res);
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ LDAP_REQ_LOCK_IF(m_req);
+ ld->ld_defconn = savedefconn;
+ --lc->lconn_refcnt;
+
+ if ( err != 0 ) {
+ ldap_free_connection( ld, lc, 1, 0 );
+ lc = NULL;
+ }
+ }
+ if ( lc != NULL )
+ lc->lconn_rebind_inprogress = 0;
+ }
+ return( lc );
+}
+
+
+/* protected by ld_conn_mutex */
+static LDAPConn *
+find_connection( LDAP *ld, LDAPURLDesc *srv, int any )
+/*
+ * return an existing connection (if any) to the server srv
+ * if "any" is non-zero, check for any server in the "srv" chain
+ */
+{
+ LDAPConn *lc;
+ LDAPURLDesc *lcu, *lsu;
+ int lcu_port, lsu_port;
+ int found = 0;
+
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
+ for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
+ lcu = lc->lconn_server;
+ lcu_port = ldap_pvt_url_scheme_port( lcu->lud_scheme,
+ lcu->lud_port );
+
+ for ( lsu = srv; lsu != NULL; lsu = lsu->lud_next ) {
+ lsu_port = ldap_pvt_url_scheme_port( lsu->lud_scheme,
+ lsu->lud_port );
+
+ if ( lsu_port == lcu_port
+ && strcmp( lcu->lud_scheme, lsu->lud_scheme ) == 0
+ && lcu->lud_host != NULL && lsu->lud_host != NULL
+ && strcasecmp( lsu->lud_host, lcu->lud_host ) == 0 )
+ {
+ found = 1;
+ break;
+ }
+
+ if ( !any ) break;
+ }
+ if ( found )
+ break;
+ }
+ return lc;
+}
+
+
+
+/* protected by ld_conn_mutex */
+static void
+use_connection( LDAP *ld, LDAPConn *lc )
+{
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
+ ++lc->lconn_refcnt;
+ lc->lconn_lastused = time( NULL );
+}
+
+
+/* protected by ld_conn_mutex */
+void
+ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind )
+{
+ LDAPConn *tmplc, *prevlc;
+
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
+ Debug2( LDAP_DEBUG_TRACE,
+ "ldap_free_connection %d %d\n",
+ force, unbind );
+
+ if ( force || --lc->lconn_refcnt <= 0 ) {
+ /* remove from connections list first */
+
+ for ( prevlc = NULL, tmplc = ld->ld_conns;
+ tmplc != NULL;
+ tmplc = tmplc->lconn_next )
+ {
+ if ( tmplc == lc ) {
+ if ( prevlc == NULL ) {
+ ld->ld_conns = tmplc->lconn_next;
+ } else {
+ prevlc->lconn_next = tmplc->lconn_next;
+ }
+ if ( ld->ld_defconn == lc ) {
+ ld->ld_defconn = NULL;
+ }
+ break;
+ }
+ prevlc = tmplc;
+ }
+
+ /* process connection callbacks */
+ {
+ struct ldapoptions *lo;
+ ldaplist *ll;
+ ldap_conncb *cb;
+
+ lo = &ld->ld_options;
+ LDAP_MUTEX_LOCK( &lo->ldo_mutex );
+ if ( lo->ldo_conn_cbs ) {
+ for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
+ cb = ll->ll_data;
+ cb->lc_del( ld, lc->lconn_sb, cb );
+ }
+ }
+ LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+ lo = LDAP_INT_GLOBAL_OPT();
+ LDAP_MUTEX_LOCK( &lo->ldo_mutex );
+ if ( lo->ldo_conn_cbs ) {
+ for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
+ cb = ll->ll_data;
+ cb->lc_del( ld, lc->lconn_sb, cb );
+ }
+ }
+ LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+ }
+
+ if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) {
+ ldap_mark_select_clear( ld, lc->lconn_sb );
+ if ( unbind ) {
+ ldap_send_unbind( ld, lc->lconn_sb,
+ NULL, NULL );
+ }
+ }
+
+ if ( lc->lconn_ber != NULL ) {
+ ber_free( lc->lconn_ber, 1 );
+ }
+
+ ldap_int_sasl_close( ld, lc );
+
+ ldap_free_urllist( lc->lconn_server );
+
+ /* FIXME: is this at all possible?
+ * ldap_ld_free() in unbind.c calls ldap_free_connection()
+ * with force == 1 __after__ explicitly calling
+ * ldap_tavl_free on ld->ld_requests */
+ if ( force ) {
+ ldap_tavl_free( ld->ld_requests, ldap_do_free_request );
+ ld->ld_requests = NULL;
+ }
+
+ if ( lc->lconn_sb != ld->ld_sb ) {
+ ber_sockbuf_free( lc->lconn_sb );
+ } else {
+ ber_int_sb_close( lc->lconn_sb );
+ }
+
+ if ( lc->lconn_rebind_queue != NULL) {
+ int i;
+ for( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) {
+ LDAP_VFREE( lc->lconn_rebind_queue[i] );
+ }
+ LDAP_FREE( lc->lconn_rebind_queue );
+ }
+
+ LDAP_FREE( lc );
+
+ Debug0( LDAP_DEBUG_TRACE,
+ "ldap_free_connection: actually freed\n" );
+
+ } else {
+ lc->lconn_lastused = time( NULL );
+ Debug1( LDAP_DEBUG_TRACE, "ldap_free_connection: refcnt %d\n",
+ lc->lconn_refcnt );
+ }
+}
+
+
+/* Protects self with ld_conn_mutex */
+#ifdef LDAP_DEBUG
+void
+ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all )
+{
+ LDAPConn *lc;
+ char timebuf[32];
+
+ Debug2( LDAP_DEBUG_TRACE, "** ld %p Connection%s:\n", (void *)ld, all ? "s" : "" );
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ for ( lc = lconns; lc != NULL; lc = lc->lconn_next ) {
+ if ( lc->lconn_server != NULL ) {
+ Debug3( LDAP_DEBUG_TRACE, "* host: %s port: %d%s\n",
+ ( lc->lconn_server->lud_host == NULL ) ? "(null)"
+ : lc->lconn_server->lud_host,
+ lc->lconn_server->lud_port, ( lc->lconn_sb ==
+ ld->ld_sb ) ? " (default)" : "" );
+ }
+ if ( lc->lconn_sb != NULL ) {
+ char from[LDAP_IPADDRLEN];
+ struct berval frombv = BER_BVC(from);
+ ber_socket_t sb;
+ if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sb ) == 1 ) {
+ Sockaddr sin;
+ socklen_t len = sizeof( sin );
+ if ( getsockname( sb, (struct sockaddr *)&sin, &len ) == 0 ) {
+ ldap_pvt_sockaddrstr( &sin, &frombv );
+ Debug1( LDAP_DEBUG_TRACE, "* from: %s\n",
+ ( from == NULL ) ? "(null)" : from );
+ }
+ }
+ }
+ Debug2( LDAP_DEBUG_TRACE, " refcnt: %d status: %s\n", lc->lconn_refcnt,
+ ( lc->lconn_status == LDAP_CONNST_NEEDSOCKET )
+ ? "NeedSocket" :
+ ( lc->lconn_status == LDAP_CONNST_CONNECTING )
+ ? "Connecting" : "Connected" );
+ Debug2( LDAP_DEBUG_TRACE, " last used: %s%s\n",
+ ldap_pvt_ctime( &lc->lconn_lastused, timebuf ),
+ lc->lconn_rebind_inprogress ? " rebind in progress" : "" );
+ if ( lc->lconn_rebind_inprogress ) {
+ if ( lc->lconn_rebind_queue != NULL) {
+ int i;
+
+ for ( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) {
+ int j;
+ for( j = 0; lc->lconn_rebind_queue[i][j] != 0; j++ ) {
+ Debug3( LDAP_DEBUG_TRACE, " queue %d entry %d - %s\n",
+ i, j, lc->lconn_rebind_queue[i][j] );
+ }
+ }
+ } else {
+ Debug0( LDAP_DEBUG_TRACE, " queue is empty\n" );
+ }
+ }
+ Debug0( LDAP_DEBUG_TRACE, "\n" );
+ if ( !all ) {
+ break;
+ }
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+}
+
+
+/* protected by req_mutex and res_mutex */
+void
+ldap_dump_requests_and_responses( LDAP *ld )
+{
+ LDAPMessage *lm, *l;
+ TAvlnode *node;
+ int i;
+
+ Debug1( LDAP_DEBUG_TRACE, "** ld %p Outstanding Requests:\n",
+ (void *)ld );
+ node = ldap_tavl_end( ld->ld_requests, TAVL_DIR_LEFT );
+ if ( node == NULL ) {
+ Debug0( LDAP_DEBUG_TRACE, " Empty\n" );
+ }
+ for ( i = 0 ; node != NULL; i++, node = ldap_tavl_next( node, TAVL_DIR_RIGHT ) ) {
+ LDAPRequest *lr = node->avl_data;
+
+ Debug3( LDAP_DEBUG_TRACE, " * msgid %d, origid %d, status %s\n",
+ lr->lr_msgid, lr->lr_origid,
+ ( lr->lr_status == LDAP_REQST_INPROGRESS ) ? "InProgress" :
+ ( lr->lr_status == LDAP_REQST_CHASINGREFS ) ? "ChasingRefs" :
+ ( lr->lr_status == LDAP_REQST_NOTCONNECTED ) ? "NotConnected" :
+ ( lr->lr_status == LDAP_REQST_WRITING ) ? "Writing" :
+ ( lr->lr_status == LDAP_REQST_COMPLETED ) ? "RequestCompleted"
+ : "InvalidStatus" );
+ Debug2( LDAP_DEBUG_TRACE, " outstanding referrals %d, parent count %d\n",
+ lr->lr_outrefcnt, lr->lr_parentcnt );
+ }
+ Debug3( LDAP_DEBUG_TRACE, " ld %p request count %d (abandoned %lu)\n",
+ (void *)ld, i, ld->ld_nabandoned );
+ Debug1( LDAP_DEBUG_TRACE, "** ld %p Response Queue:\n", (void *)ld );
+ if ( ( lm = ld->ld_responses ) == NULL ) {
+ Debug0( LDAP_DEBUG_TRACE, " Empty\n" );
+ }
+ for ( i = 0; lm != NULL; lm = lm->lm_next, i++ ) {
+ Debug2( LDAP_DEBUG_TRACE, " * msgid %d, type %lu\n",
+ lm->lm_msgid, (unsigned long)lm->lm_msgtype );
+ if ( lm->lm_chain != NULL ) {
+ Debug0( LDAP_DEBUG_TRACE, " chained responses:\n" );
+ for ( l = lm->lm_chain; l != NULL; l = l->lm_chain ) {
+ Debug2( LDAP_DEBUG_TRACE,
+ " * msgid %d, type %lu\n",
+ l->lm_msgid,
+ (unsigned long)l->lm_msgtype );
+ }
+ }
+ }
+ Debug2( LDAP_DEBUG_TRACE, " ld %p response count %d\n", (void *)ld, i );
+}
+#endif /* LDAP_DEBUG */
+
+/* protected by req_mutex */
+void
+ldap_do_free_request( void *arg )
+{
+ LDAPRequest *lr = arg;
+
+ Debug3( LDAP_DEBUG_TRACE, "ldap_do_free_request: "
+ "asked to free lr %p msgid %d refcnt %d\n",
+ lr, lr->lr_msgid, lr->lr_refcnt );
+ /* if lr_refcnt > 0, the request has been looked up
+ * by ldap_find_request_by_msgid(); if in the meanwhile
+ * the request is free()'d by someone else, just decrease
+ * the reference count; later on, it will be freed. */
+ if ( lr->lr_refcnt > 0 ) {
+ assert( lr->lr_refcnt == 1 );
+ lr->lr_refcnt = -lr->lr_refcnt;
+ return;
+ }
+
+ if ( lr->lr_ber != NULL ) {
+ ber_free( lr->lr_ber, 1 );
+ lr->lr_ber = NULL;
+ }
+
+ if ( lr->lr_res_error != NULL ) {
+ LDAP_FREE( lr->lr_res_error );
+ lr->lr_res_error = NULL;
+ }
+
+ if ( lr->lr_res_matched != NULL ) {
+ LDAP_FREE( lr->lr_res_matched );
+ lr->lr_res_matched = NULL;
+ }
+
+ LDAP_FREE( lr );
+}
+
+int
+ldap_req_cmp( const void *l, const void *r )
+{
+ const LDAPRequest *left = l, *right = r;
+ return left->lr_msgid - right->lr_msgid;
+}
+
+/* protected by req_mutex */
+static void
+ldap_free_request_int( LDAP *ld, LDAPRequest *lr )
+{
+ LDAPRequest *removed;
+
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
+ removed = ldap_tavl_delete( &ld->ld_requests, lr, ldap_req_cmp );
+ assert( !removed || removed == lr );
+ Debug3( LDAP_DEBUG_TRACE, "ldap_free_request_int: "
+ "lr %p msgid %d%s removed\n",
+ lr, lr->lr_msgid, removed ? "" : " not" );
+
+ ldap_do_free_request( lr );
+}
+
+/* protected by req_mutex */
+void
+ldap_free_request( LDAP *ld, LDAPRequest *lr )
+{
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
+ Debug2( LDAP_DEBUG_TRACE, "ldap_free_request (origid %d, msgid %d)\n",
+ lr->lr_origid, lr->lr_msgid );
+
+ /* free all referrals (child requests) */
+ while ( lr->lr_child ) {
+ ldap_free_request( ld, lr->lr_child );
+ }
+
+ if ( lr->lr_parent != NULL ) {
+ LDAPRequest **lrp;
+
+ --lr->lr_parent->lr_outrefcnt;
+ for ( lrp = &lr->lr_parent->lr_child;
+ *lrp && *lrp != lr;
+ lrp = &(*lrp)->lr_refnext );
+
+ if ( *lrp == lr ) {
+ *lrp = lr->lr_refnext;
+ }
+ }
+ ldap_free_request_int( ld, lr );
+}
+
+/*
+ * call first time with *cntp = -1
+ * when returns *cntp == -1, no referrals are left
+ *
+ * NOTE: may replace *refsp, or shuffle the contents
+ * of the original array.
+ */
+static int ldap_int_nextref(
+ LDAP *ld,
+ char ***refsp,
+ int *cntp,
+ void *params )
+{
+ assert( refsp != NULL );
+ assert( *refsp != NULL );
+ assert( cntp != NULL );
+
+ if ( *cntp < -1 ) {
+ *cntp = -1;
+ return -1;
+ }
+
+ (*cntp)++;
+
+ if ( (*refsp)[ *cntp ] == NULL ) {
+ *cntp = -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Chase v3 referrals
+ *
+ * Parameters:
+ * (IN) ld = LDAP connection handle
+ * (IN) lr = LDAP Request structure
+ * (IN) refs = array of pointers to referral strings that we will chase
+ * The array will be free'd by this function when no longer needed
+ * (IN) sref != 0 if following search reference
+ * (OUT) errstrp = Place to return a string of referrals which could not be followed
+ * (OUT) hadrefp = 1 if successfully followed referral
+ *
+ * Return value - number of referrals followed
+ *
+ * Protected by res_mutex, conn_mutex and req_mutex (try_read1msg)
+ */
+int
+ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char **errstrp, int *hadrefp )
+{
+ char *unfollowed;
+ int unfollowedcnt = 0;
+ LDAPRequest *origreq;
+ LDAPURLDesc *srv = NULL;
+ BerElement *ber;
+ char **refarray = NULL;
+ LDAPConn *lc;
+ int rc, count, i, j, id;
+ LDAPreqinfo rinfo;
+ LDAP_NEXTREF_PROC *nextref_proc = ld->ld_nextref_proc ? ld->ld_nextref_proc : ldap_int_nextref;
+
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
+ Debug0( LDAP_DEBUG_TRACE, "ldap_chase_v3referrals\n" );
+
+ ld->ld_errno = LDAP_SUCCESS; /* optimistic */
+ *hadrefp = 0;
+
+ unfollowed = NULL;
+ rc = count = 0;
+
+ /* If no referrals in array, return */
+ if ( (refs == NULL) || ( (refs)[0] == NULL) ) {
+ rc = 0;
+ goto done;
+ }
+
+ /* Check for hop limit exceeded */
+ if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
+ Debug1( LDAP_DEBUG_ANY,
+ "more than %d referral hops (dropping)\n", ld->ld_refhoplimit );
+ ld->ld_errno = LDAP_REFERRAL_LIMIT_EXCEEDED;
+ rc = -1;
+ goto done;
+ }
+
+ /* find original request */
+ for ( origreq = lr;
+ origreq->lr_parent != NULL;
+ origreq = origreq->lr_parent )
+ {
+ /* empty */ ;
+ }
+
+ refarray = refs;
+ refs = NULL;
+
+ /* parse out & follow referrals */
+ /* NOTE: if nextref_proc == ldap_int_nextref, params is ignored */
+ i = -1;
+ for ( nextref_proc( ld, &refarray, &i, ld->ld_nextref_params );
+ i != -1;
+ nextref_proc( ld, &refarray, &i, ld->ld_nextref_params ) )
+ {
+
+ /* Parse the referral URL */
+ rc = ldap_url_parse_ext( refarray[i], &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ /* ldap_url_parse_ext() returns LDAP_URL_* errors
+ * which do not map on API errors */
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ rc = -1;
+ goto done;
+ }
+
+ if( srv->lud_crit_exts ) {
+ int ok = 0;
+#ifdef HAVE_TLS
+ /* If StartTLS is the only critical ext, OK. */
+ if ( find_tls_ext( srv ) == 2 && srv->lud_crit_exts == 1 )
+ ok = 1;
+#endif
+ if ( !ok ) {
+ /* we do not support any other extensions */
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ rc = -1;
+ goto done;
+ }
+ }
+
+ /* check connection for re-bind in progress */
+ if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
+ /* See if we've already requested this DN with this conn */
+ LDAPRequest *lp;
+ int looped = 0;
+ ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0;
+ for ( lp = origreq; lp; ) {
+ if ( lp->lr_conn == lc
+ && len == lp->lr_dn.bv_len
+ && len
+ && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) == 0 )
+ {
+ looped = 1;
+ break;
+ }
+ if ( lp == origreq ) {
+ lp = lp->lr_child;
+ } else {
+ lp = lp->lr_refnext;
+ }
+ }
+ if ( looped ) {
+ ldap_free_urllist( srv );
+ srv = NULL;
+ ld->ld_errno = LDAP_CLIENT_LOOP;
+ rc = -1;
+ continue;
+ }
+
+ if ( lc->lconn_rebind_inprogress ) {
+ /* We are already chasing a referral or search reference and a
+ * bind on that connection is in progress. We must queue
+ * referrals on that connection, so we don't get a request
+ * going out before the bind operation completes. This happens
+ * if two search references come in one behind the other
+ * for the same server with different contexts.
+ */
+ Debug1( LDAP_DEBUG_TRACE,
+ "ldap_chase_v3referrals: queue referral \"%s\"\n",
+ refarray[i] );
+ if( lc->lconn_rebind_queue == NULL ) {
+ /* Create a referral list */
+ lc->lconn_rebind_queue =
+ (char ***) LDAP_MALLOC( sizeof(void *) * 2);
+
+ if( lc->lconn_rebind_queue == NULL) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ rc = -1;
+ goto done;
+ }
+
+ lc->lconn_rebind_queue[0] = refarray;
+ lc->lconn_rebind_queue[1] = NULL;
+ refarray = NULL;
+
+ } else {
+ /* Count how many referral arrays we already have */
+ for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++) {
+ /* empty */;
+ }
+
+ /* Add the new referral to the list */
+ lc->lconn_rebind_queue = (char ***) LDAP_REALLOC(
+ lc->lconn_rebind_queue, sizeof(void *) * (j + 2));
+
+ if( lc->lconn_rebind_queue == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ rc = -1;
+ goto done;
+ }
+ lc->lconn_rebind_queue[j] = refarray;
+ lc->lconn_rebind_queue[j+1] = NULL;
+ refarray = NULL;
+ }
+
+ /* We have queued the referral/reference, now just return */
+ rc = 0;
+ *hadrefp = 1;
+ count = 1; /* Pretend we already followed referral */
+ goto done;
+ }
+ }
+ /* Re-encode the request with the new starting point of the search.
+ * Note: In the future we also need to replace the filter if one
+ * was provided with the search reference
+ */
+
+ /* For references we don't want old dn if new dn empty */
+ if ( sref && srv->lud_dn == NULL ) {
+ srv->lud_dn = LDAP_STRDUP( "" );
+ }
+
+ LDAP_NEXT_MSGID( ld, id );
+ ber = re_encode_request( ld, origreq->lr_ber, id,
+ sref, srv, &rinfo.ri_request );
+
+ if( ber == NULL ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ rc = -1;
+ goto done;
+ }
+
+ Debug2( LDAP_DEBUG_TRACE,
+ "ldap_chase_v3referral: msgid %d, url \"%s\"\n",
+ lr->lr_msgid, refarray[i] );
+
+ /* Send the new request to the server - may require a bind */
+ rinfo.ri_msgid = origreq->lr_origid;
+ rinfo.ri_url = refarray[i];
+ rc = ldap_send_server_request( ld, ber, id,
+ origreq, &srv, NULL, &rinfo, 0, 1 );
+ if ( rc < 0 ) {
+ /* Failure, try next referral in the list */
+ Debug3( LDAP_DEBUG_ANY, "Unable to chase referral \"%s\" (%d: %s)\n",
+ refarray[i], ld->ld_errno, ldap_err2string( ld->ld_errno ) );
+ unfollowedcnt += ldap_append_referral( ld, &unfollowed, refarray[i] );
+ ldap_free_urllist( srv );
+ srv = NULL;
+ ld->ld_errno = LDAP_REFERRAL;
+ } else {
+ /* Success, no need to try this referral list further */
+ rc = 0;
+ ++count;
+ *hadrefp = 1;
+
+ /* check if there is a queue of referrals that came in during bind */
+ if ( lc == NULL) {
+ lc = find_connection( ld, srv, 1 );
+ if ( lc == NULL ) {
+ ld->ld_errno = LDAP_OPERATIONS_ERROR;
+ rc = -1;
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ goto done;
+ }
+ }
+
+ if ( lc->lconn_rebind_queue != NULL ) {
+ /* Release resources of previous list */
+ LDAP_VFREE( refarray );
+ refarray = NULL;
+ ldap_free_urllist( srv );
+ srv = NULL;
+
+ /* Pull entries off end of queue so list always null terminated */
+ for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++ )
+ ;
+ refarray = lc->lconn_rebind_queue[j - 1];
+ lc->lconn_rebind_queue[j-1] = NULL;
+ /* we pulled off last entry from queue, free queue */
+ if ( j == 1 ) {
+ LDAP_FREE( lc->lconn_rebind_queue );
+ lc->lconn_rebind_queue = NULL;
+ }
+ /* restart the loop the with new referral list */
+ i = -1;
+ continue;
+ }
+ break; /* referral followed, break out of for loop */
+ }
+ } /* end for loop */
+done:
+ LDAP_VFREE( refarray );
+ ldap_free_urllist( srv );
+ LDAP_FREE( *errstrp );
+
+ if( rc == 0 ) {
+ *errstrp = NULL;
+ LDAP_FREE( unfollowed );
+ return count;
+ } else {
+ *errstrp = unfollowed;
+ return rc;
+ }
+}
+
+/*
+ * XXX merging of errors in this routine needs to be improved
+ * Protected by res_mutex, conn_mutex and req_mutex (try_read1msg)
+ */
+int
+ldap_chase_referrals( LDAP *ld,
+ LDAPRequest *lr,
+ char **errstrp,
+ int sref,
+ int *hadrefp )
+{
+ int rc, count, id;
+ unsigned len;
+ char *p, *ref, *unfollowed;
+ LDAPRequest *origreq;
+ LDAPURLDesc *srv;
+ BerElement *ber;
+ LDAPreqinfo rinfo;
+ LDAPConn *lc;
+
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
+ Debug0( LDAP_DEBUG_TRACE, "ldap_chase_referrals\n" );
+
+ ld->ld_errno = LDAP_SUCCESS; /* optimistic */
+ *hadrefp = 0;
+
+ if ( *errstrp == NULL ) {
+ return( 0 );
+ }
+
+ len = strlen( *errstrp );
+ for ( p = *errstrp; len >= LDAP_REF_STR_LEN; ++p, --len ) {
+ if ( strncasecmp( p, LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) {
+ *p = '\0';
+ p += LDAP_REF_STR_LEN;
+ break;
+ }
+ }
+
+ if ( len < LDAP_REF_STR_LEN ) {
+ return( 0 );
+ }
+
+ if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
+ Debug1( LDAP_DEBUG_ANY,
+ "more than %d referral hops (dropping)\n",
+ ld->ld_refhoplimit );
+ /* XXX report as error in ld->ld_errno? */
+ return( 0 );
+ }
+
+ /* find original request */
+ for ( origreq = lr; origreq->lr_parent != NULL;
+ origreq = origreq->lr_parent ) {
+ /* empty */;
+ }
+
+ unfollowed = NULL;
+ rc = count = 0;
+
+ /* parse out & follow referrals */
+ for ( ref = p; rc == 0 && ref != NULL; ref = p ) {
+ p = strchr( ref, '\n' );
+ if ( p != NULL ) {
+ *p++ = '\0';
+ }
+
+ rc = ldap_url_parse_ext( ref, &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ Debug2( LDAP_DEBUG_TRACE,
+ "ignoring %s referral <%s>\n",
+ ref, rc == LDAP_URL_ERR_BADSCHEME ? "unknown" : "incorrect" );
+ rc = ldap_append_referral( ld, &unfollowed, ref );
+ *hadrefp = 1;
+ continue;
+ }
+
+ Debug1( LDAP_DEBUG_TRACE,
+ "chasing LDAP referral: <%s>\n", ref );
+
+ *hadrefp = 1;
+
+ /* See if we've already been here */
+ if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
+ LDAPRequest *lp;
+ int looped = 0;
+ ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0;
+ for ( lp = lr; lp; lp = lp->lr_parent ) {
+ if ( lp->lr_conn == lc
+ && len == lp->lr_dn.bv_len )
+ {
+ if ( len && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) )
+ continue;
+ looped = 1;
+ break;
+ }
+ }
+ if ( looped ) {
+ ldap_free_urllist( srv );
+ ld->ld_errno = LDAP_CLIENT_LOOP;
+ rc = -1;
+ continue;
+ }
+ }
+
+ LDAP_NEXT_MSGID( ld, id );
+ ber = re_encode_request( ld, origreq->lr_ber,
+ id, sref, srv, &rinfo.ri_request );
+
+ if ( ber == NULL ) {
+ ldap_free_urllist( srv );
+ return -1 ;
+ }
+
+ /* copy the complete referral for rebind process */
+ rinfo.ri_url = LDAP_STRDUP( ref );
+
+ rinfo.ri_msgid = origreq->lr_origid;
+
+ rc = ldap_send_server_request( ld, ber, id,
+ lr, &srv, NULL, &rinfo, 0, 1 );
+ LDAP_FREE( rinfo.ri_url );
+
+ if( rc >= 0 ) {
+ ++count;
+ } else {
+ Debug3( LDAP_DEBUG_ANY,
+ "Unable to chase referral \"%s\" (%d: %s)\n",
+ ref, ld->ld_errno, ldap_err2string( ld->ld_errno ) );
+ rc = ldap_append_referral( ld, &unfollowed, ref );
+ }
+
+ ldap_free_urllist(srv);
+ }
+
+ LDAP_FREE( *errstrp );
+ *errstrp = unfollowed;
+
+ return(( rc == 0 ) ? count : rc );
+}
+
+
+int
+ldap_append_referral( LDAP *ld, char **referralsp, char *s )
+{
+ int first;
+
+ if ( *referralsp == NULL ) {
+ first = 1;
+ *referralsp = (char *)LDAP_MALLOC( strlen( s ) + LDAP_REF_STR_LEN
+ + 1 );
+ } else {
+ first = 0;
+ *referralsp = (char *)LDAP_REALLOC( *referralsp,
+ strlen( *referralsp ) + strlen( s ) + 2 );
+ }
+
+ if ( *referralsp == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return( -1 );
+ }
+
+ if ( first ) {
+ strcpy( *referralsp, LDAP_REF_STR );
+ } else {
+ strcat( *referralsp, "\n" );
+ }
+ strcat( *referralsp, s );
+
+ return( 0 );
+}
+
+
+
+static BerElement *
+re_encode_request( LDAP *ld,
+ BerElement *origber,
+ ber_int_t msgid,
+ int sref,
+ LDAPURLDesc *srv,
+ int *type )
+{
+ /*
+ * XXX this routine knows way too much about how the lber library works!
+ */
+ ber_int_t along;
+ ber_tag_t tag;
+ ber_tag_t rtag;
+ ber_int_t ver;
+ ber_int_t scope;
+ int rc;
+ BerElement tmpber, *ber;
+ struct berval dn;
+
+ Debug2( LDAP_DEBUG_TRACE,
+ "re_encode_request: new msgid %ld, new dn <%s>\n",
+ (long) msgid,
+ ( srv == NULL || srv->lud_dn == NULL) ? "NONE" : srv->lud_dn );
+
+ tmpber = *origber;
+
+ /*
+ * all LDAP requests are sequences that start with a message id.
+ * For all except delete, this is followed by a sequence that is
+ * tagged with the operation code. For delete, the provided DN
+ * is not wrapped by a sequence.
+ */
+ rtag = ber_scanf( &tmpber, "{it", /*}*/ &along, &tag );
+
+ if ( rtag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return( NULL );
+ }
+
+ assert( tag != 0);
+ if ( tag == LDAP_REQ_BIND ) {
+ /* bind requests have a version number before the DN & other stuff */
+ rtag = ber_scanf( &tmpber, "{im" /*}*/, &ver, &dn );
+
+ } else if ( tag == LDAP_REQ_DELETE ) {
+ /* delete requests don't have a DN wrapping sequence */
+ rtag = ber_scanf( &tmpber, "m", &dn );
+
+ } else if ( tag == LDAP_REQ_SEARCH ) {
+ /* search requests need to be re-scope-ed */
+ rtag = ber_scanf( &tmpber, "{me" /*"}"*/, &dn, &scope );
+
+ if( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
+ /* use the scope provided in reference */
+ scope = srv->lud_scope;
+
+ } else if ( sref ) {
+ /* use scope implied by previous operation
+ * base -> base
+ * one -> base
+ * subtree -> subtree
+ * subordinate -> subtree
+ */
+ switch( scope ) {
+ default:
+ case LDAP_SCOPE_BASE:
+ case LDAP_SCOPE_ONELEVEL:
+ scope = LDAP_SCOPE_BASE;
+ break;
+ case LDAP_SCOPE_SUBTREE:
+ case LDAP_SCOPE_SUBORDINATE:
+ scope = LDAP_SCOPE_SUBTREE;
+ break;
+ }
+ }
+
+ } else {
+ rtag = ber_scanf( &tmpber, "{m" /*}*/, &dn );
+ }
+
+ if( rtag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return NULL;
+ }
+
+ /* restore character zero'd out by ber_scanf*/
+ dn.bv_val[dn.bv_len] = tmpber.ber_tag;
+
+ if (( ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
+ return NULL;
+ }
+
+ if ( srv->lud_dn ) {
+ ber_str2bv( srv->lud_dn, 0, 0, &dn );
+ }
+
+ if ( tag == LDAP_REQ_BIND ) {
+ rc = ber_printf( ber, "{it{iO" /*}}*/, msgid, tag, ver, &dn );
+ } else if ( tag == LDAP_REQ_DELETE ) {
+ rc = ber_printf( ber, "{itON}", msgid, tag, &dn );
+ } else if ( tag == LDAP_REQ_SEARCH ) {
+ rc = ber_printf( ber, "{it{Oe" /*}}*/, msgid, tag, &dn, scope );
+ } else {
+ rc = ber_printf( ber, "{it{O" /*}}*/, msgid, tag, &dn );
+ }
+
+ if ( rc == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return NULL;
+ }
+
+ if ( tag != LDAP_REQ_DELETE && (
+ ber_write(ber, tmpber.ber_ptr, ( tmpber.ber_end - tmpber.ber_ptr ), 0)
+ != ( tmpber.ber_end - tmpber.ber_ptr ) ||
+ ber_printf( ber, /*{{*/ "N}N}" ) == -1 ) )
+ {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return NULL;
+ }
+
+#ifdef LDAP_DEBUG
+ if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
+ Debug0( LDAP_DEBUG_ANY, "re_encode_request new request is:\n" );
+ ber_log_dump( LDAP_DEBUG_BER, ldap_debug, ber, 0 );
+ }
+#endif /* LDAP_DEBUG */
+
+ *type = tag; /* return request type */
+ return ber;
+}
+
+
+/* protected by req_mutex */
+LDAPRequest *
+ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid )
+{
+ LDAPRequest *lr, needle = {0};
+ needle.lr_msgid = msgid;
+
+ lr = ldap_tavl_find( ld->ld_requests, &needle, ldap_req_cmp );
+ if ( lr != NULL && lr->lr_status != LDAP_REQST_COMPLETED ) {
+ /* lr_refcnt is only negative when we removed it from ld_requests
+ * already, it is positive if we have sub-requests (referrals) */
+ assert( lr->lr_refcnt >= 0 );
+ lr->lr_refcnt++;
+ Debug3( LDAP_DEBUG_TRACE, "ldap_find_request_by_msgid: "
+ "msgid %d, lr %p lr->lr_refcnt = %d\n",
+ msgid, lr, lr->lr_refcnt );
+ return lr;
+ }
+
+ Debug2( LDAP_DEBUG_TRACE, "ldap_find_request_by_msgid: "
+ "msgid %d, lr %p\n", msgid, lr );
+ return NULL;
+}
+
+/* protected by req_mutex */
+void
+ldap_return_request( LDAP *ld, LDAPRequest *lrx, int freeit )
+{
+ LDAPRequest *lr;
+
+ lr = ldap_tavl_find( ld->ld_requests, lrx, ldap_req_cmp );
+ Debug2( LDAP_DEBUG_TRACE, "ldap_return_request: "
+ "lrx %p, lr %p\n", lrx, lr );
+ if ( lr ) {
+ assert( lr == lrx );
+ if ( lr->lr_refcnt > 0 ) {
+ lr->lr_refcnt--;
+ } else if ( lr->lr_refcnt < 0 ) {
+ lr->lr_refcnt++;
+ if ( lr->lr_refcnt == 0 ) {
+ lr = NULL;
+ }
+ }
+ }
+ Debug3( LDAP_DEBUG_TRACE, "ldap_return_request: "
+ "lrx->lr_msgid %d, lrx->lr_refcnt is now %d, lr is %s present\n",
+ lrx->lr_msgid, lrx->lr_refcnt, lr ? "still" : "not" );
+ /* The request is not tracked anymore */
+ if ( lr == NULL ) {
+ ldap_free_request_int( ld, lrx );
+ } else if ( freeit ) {
+ ldap_free_request( ld, lrx );
+ }
+}
diff --git a/libraries/libldap/result.c b/libraries/libldap/result.c
new file mode 100644
index 0000000..40ff1c1
--- /dev/null
+++ b/libraries/libldap/result.c
@@ -0,0 +1,1401 @@
+/* result.c - wait for an ldap result */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+/* This notice applies to changes, created by or for Novell, Inc.,
+ * to preexisting works for which notices appear elsewhere in this file.
+ *
+ * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
+ *
+ * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
+ * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
+ * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
+ * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
+ * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
+ * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
+ * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
+ * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
+ *---
+ * Modification to OpenLDAP source by Novell, Inc.
+ * April 2000 sfs Add code to process V3 referrals and search results
+ *---
+ * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
+ * can be found in the file "build/LICENSE-2.0.1" in this distribution
+ * of OpenLDAP Software.
+ */
+
+/*
+ * LDAPv3 (RFC 4511)
+ * LDAPResult ::= SEQUENCE {
+ * resultCode ENUMERATED { ... },
+ * matchedDN LDAPDN,
+ * diagnosticMessage LDAPString,
+ * referral [3] Referral OPTIONAL
+ * }
+ * Referral ::= SEQUENCE OF LDAPURL (one or more)
+ * LDAPURL ::= LDAPString (limited to URL chars)
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include "ldap-int.h"
+#include "ldap_log.h"
+#include "lutil.h"
+
+static int ldap_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid ));
+static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid ));
+static int wait4msg LDAP_P(( LDAP *ld, ber_int_t msgid, int all, struct timeval *timeout,
+ LDAPMessage **result ));
+static ber_tag_t try_read1msg LDAP_P(( LDAP *ld, ber_int_t msgid,
+ int all, LDAPConn *lc, LDAPMessage **result ));
+static ber_tag_t build_result_ber LDAP_P(( LDAP *ld, BerElement **bp, LDAPRequest *lr ));
+static void merge_error_info LDAP_P(( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr ));
+static LDAPMessage * chkResponseList LDAP_P(( LDAP *ld, int msgid, int all));
+
+#define LDAP_MSG_X_KEEP_LOOKING (-2)
+
+
+/*
+ * ldap_result - wait for an ldap result response to a message from the
+ * ldap server. If msgid is LDAP_RES_ANY (-1), any message will be
+ * accepted. If msgid is LDAP_RES_UNSOLICITED (0), any unsolicited
+ * message is accepted. Otherwise ldap_result will wait for a response
+ * with msgid. If all is LDAP_MSG_ONE (0) the first message with id
+ * msgid will be accepted, otherwise, ldap_result will wait for all
+ * responses with id msgid and then return a pointer to the entire list
+ * of messages. In general, this is only useful for search responses,
+ * which can be of three message types (zero or more entries, zero or
+ * search references, followed by an ldap result). An extension to
+ * LDAPv3 allows partial extended responses to be returned in response
+ * to any request. The type of the first message received is returned.
+ * When waiting, any messages that have been abandoned/discarded are
+ * discarded.
+ *
+ * Example:
+ * ldap_result( s, msgid, all, timeout, result )
+ */
+int
+ldap_result(
+ LDAP *ld,
+ int msgid,
+ int all,
+ struct timeval *timeout,
+ LDAPMessage **result )
+{
+ int rc;
+
+ assert( ld != NULL );
+ assert( result != NULL );
+
+ Debug2( LDAP_DEBUG_TRACE, "ldap_result ld %p msgid %d\n", (void *)ld, msgid );
+
+ if (ld->ld_errno == LDAP_LOCAL_ERROR || ld->ld_errno == LDAP_SERVER_DOWN)
+ return -1;
+
+ LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
+ rc = wait4msg( ld, msgid, all, timeout, result );
+ LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
+
+ return rc;
+}
+
+/* protected by res_mutex */
+static LDAPMessage *
+chkResponseList(
+ LDAP *ld,
+ int msgid,
+ int all)
+{
+ LDAPMessage *lm, **lastlm, *nextlm;
+ int cnt = 0;
+
+ /*
+ * Look through the list of responses we have received on
+ * this association and see if the response we're interested in
+ * is there. If it is, return it. If not, call wait4msg() to
+ * wait until it arrives or timeout occurs.
+ */
+
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
+
+ Debug3( LDAP_DEBUG_TRACE,
+ "ldap_chkResponseList ld %p msgid %d all %d\n",
+ (void *)ld, msgid, all );
+
+ lastlm = &ld->ld_responses;
+ for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
+ nextlm = lm->lm_next;
+ ++cnt;
+
+ if ( ldap_abandoned( ld, lm->lm_msgid ) ) {
+ Debug2( LDAP_DEBUG_ANY,
+ "response list msg abandoned, "
+ "msgid %d message type %s\n",
+ lm->lm_msgid, ldap_int_msgtype2str( lm->lm_msgtype ) );
+
+ switch ( lm->lm_msgtype ) {
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_REFERENCE:
+ case LDAP_RES_INTERMEDIATE:
+ break;
+
+ default:
+ /* there's no need to keep the id
+ * in the abandoned list any longer */
+ ldap_mark_abandoned( ld, lm->lm_msgid );
+ break;
+ }
+
+ /* Remove this entry from list */
+ *lastlm = nextlm;
+
+ ldap_msgfree( lm );
+
+ continue;
+ }
+
+ if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
+ LDAPMessage *tmp;
+
+ if ( all == LDAP_MSG_ONE ||
+ all == LDAP_MSG_RECEIVED ||
+ msgid == LDAP_RES_UNSOLICITED )
+ {
+ break;
+ }
+
+ tmp = lm->lm_chain_tail;
+ if ( tmp->lm_msgtype == LDAP_RES_SEARCH_ENTRY ||
+ tmp->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ||
+ tmp->lm_msgtype == LDAP_RES_INTERMEDIATE )
+ {
+ tmp = NULL;
+ }
+
+ if ( tmp == NULL ) {
+ lm = NULL;
+ }
+
+ break;
+ }
+ lastlm = &lm->lm_next;
+ }
+
+ if ( lm != NULL ) {
+ /* Found an entry, remove it from the list */
+ if ( all == LDAP_MSG_ONE && lm->lm_chain != NULL ) {
+ *lastlm = lm->lm_chain;
+ lm->lm_chain->lm_next = lm->lm_next;
+ lm->lm_chain->lm_chain_tail = ( lm->lm_chain_tail != lm ) ? lm->lm_chain_tail : lm->lm_chain;
+ lm->lm_chain = NULL;
+ lm->lm_chain_tail = NULL;
+ } else {
+ *lastlm = lm->lm_next;
+ }
+ lm->lm_next = NULL;
+ }
+
+#ifdef LDAP_DEBUG
+ if ( lm == NULL) {
+ Debug1( LDAP_DEBUG_TRACE,
+ "ldap_chkResponseList returns ld %p NULL\n", (void *)ld );
+ } else {
+ Debug3( LDAP_DEBUG_TRACE,
+ "ldap_chkResponseList returns ld %p msgid %d, type 0x%02lx\n",
+ (void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype );
+ }
+#endif
+
+ return lm;
+}
+
+/* protected by res_mutex */
+static int
+wait4msg(
+ LDAP *ld,
+ ber_int_t msgid,
+ int all,
+ struct timeval *timeout,
+ LDAPMessage **result )
+{
+ int rc;
+ struct timeval tv = { 0 },
+ tv0 = { 0 },
+ start_time_tv = { 0 },
+ *tvp = NULL;
+ LDAPConn *lc;
+
+ assert( ld != NULL );
+ assert( result != NULL );
+
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
+
+ if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) {
+ tv = ld->ld_options.ldo_tm_api;
+ timeout = &tv;
+ }
+
+#ifdef LDAP_DEBUG
+ if ( timeout == NULL ) {
+ Debug2( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n",
+ (void *)ld, msgid );
+ } else {
+ Debug3( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n",
+ (void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec );
+ }
+#endif /* LDAP_DEBUG */
+
+ if ( timeout != NULL && timeout->tv_sec != -1 ) {
+ tv0 = *timeout;
+ tv = *timeout;
+ tvp = &tv;
+#ifdef HAVE_GETTIMEOFDAY
+ gettimeofday( &start_time_tv, NULL );
+#else /* ! HAVE_GETTIMEOFDAY */
+ start_time_tv.tv_sec = time( NULL );
+ start_time_tv.tv_usec = 0;
+#endif /* ! HAVE_GETTIMEOFDAY */
+ }
+
+ rc = LDAP_MSG_X_KEEP_LOOKING;
+ while ( rc == LDAP_MSG_X_KEEP_LOOKING ) {
+#ifdef LDAP_DEBUG
+ if ( ldap_debug & LDAP_DEBUG_TRACE ) {
+ Debug3( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n",
+ (void *)ld, msgid, all );
+ ldap_dump_connection( ld, ld->ld_conns, 1 );
+ LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+ ldap_dump_requests_and_responses( ld );
+ LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
+ }
+#endif /* LDAP_DEBUG */
+
+ if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) {
+ rc = (*result)->lm_msgtype;
+
+ } else {
+ int lc_ready = 0;
+
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
+ if ( ber_sockbuf_ctrl( lc->lconn_sb,
+ LBER_SB_OPT_DATA_READY, NULL ) )
+ {
+ lc_ready = 2; /* ready at ber level, not socket level */
+ break;
+ }
+ }
+
+ if ( !lc_ready ) {
+ int err;
+ rc = ldap_int_select( ld, tvp );
+ if ( rc == -1 ) {
+ err = sock_errno();
+#ifdef LDAP_DEBUG
+ Debug1( LDAP_DEBUG_TRACE,
+ "ldap_int_select returned -1: errno %d\n",
+ err );
+#endif
+ }
+
+ if ( rc == 0 || ( rc == -1 && (
+ !LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
+ || err != EINTR ) ) )
+ {
+ ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
+ LDAP_TIMEOUT);
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ return( rc );
+ }
+
+ if ( rc == -1 ) {
+ rc = LDAP_MSG_X_KEEP_LOOKING; /* select interrupted: loop */
+
+ } else {
+ lc_ready = 1;
+ }
+ }
+ if ( lc_ready ) {
+ LDAPConn *lnext;
+ int serviced = 0;
+ rc = LDAP_MSG_X_KEEP_LOOKING;
+ LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+ if ( ld->ld_requests != NULL ) {
+ TAvlnode *node = ldap_tavl_end( ld->ld_requests, TAVL_DIR_RIGHT );
+ LDAPRequest *lr;
+
+ assert( node != NULL );
+ lr = node->avl_data;
+ if ( lr->lr_status == LDAP_REQST_WRITING &&
+ ldap_is_write_ready( ld, lr->lr_conn->lconn_sb ) ) {
+ serviced = 1;
+ ldap_int_flush_request( ld, lr );
+ }
+ }
+ for ( lc = ld->ld_conns;
+ rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL;
+ lc = lnext )
+ {
+ if ( lc->lconn_status == LDAP_CONNST_CONNECTED &&
+ ldap_is_read_ready( ld, lc->lconn_sb ) )
+ {
+ serviced = 1;
+ /* Don't let it get freed out from under us */
+ ++lc->lconn_refcnt;
+ rc = try_read1msg( ld, msgid, all, lc, result );
+ lnext = lc->lconn_next;
+
+ /* Only take locks if we're really freeing */
+ if ( lc->lconn_refcnt <= 1 ) {
+ ldap_free_connection( ld, lc, 0, 1 );
+ } else {
+ --lc->lconn_refcnt;
+ }
+ } else {
+ lnext = lc->lconn_next;
+ }
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
+ /* Quit looping if no one handled any socket events */
+ if (!serviced && lc_ready == 1)
+ rc = -1;
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ }
+
+ if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
+ struct timeval curr_time_tv = { 0 },
+ delta_time_tv = { 0 };
+
+#ifdef HAVE_GETTIMEOFDAY
+ gettimeofday( &curr_time_tv, NULL );
+#else /* ! HAVE_GETTIMEOFDAY */
+ curr_time_tv.tv_sec = time( NULL );
+ curr_time_tv.tv_usec = 0;
+#endif /* ! HAVE_GETTIMEOFDAY */
+
+ /* delta_time = tmp_time - start_time */
+ delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
+ delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
+ if ( delta_time_tv.tv_usec < 0 ) {
+ delta_time_tv.tv_sec--;
+ delta_time_tv.tv_usec += 1000000;
+ }
+
+ /* tv0 < delta_time ? */
+ if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) ||
+ ( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) )
+ {
+ rc = 0; /* timed out */
+ ld->ld_errno = LDAP_TIMEOUT;
+ break;
+ }
+
+ /* tv0 -= delta_time */
+ tv0.tv_sec -= delta_time_tv.tv_sec;
+ tv0.tv_usec -= delta_time_tv.tv_usec;
+ if ( tv0.tv_usec < 0 ) {
+ tv0.tv_sec--;
+ tv0.tv_usec += 1000000;
+ }
+
+ tv.tv_sec = tv0.tv_sec;
+ tv.tv_usec = tv0.tv_usec;
+
+ Debug3( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n",
+ (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec );
+
+ start_time_tv.tv_sec = curr_time_tv.tv_sec;
+ start_time_tv.tv_usec = curr_time_tv.tv_usec;
+ }
+ }
+
+ return( rc );
+}
+
+
+/* protected by res_mutex, conn_mutex and req_mutex */
+static ber_tag_t
+try_read1msg(
+ LDAP *ld,
+ ber_int_t msgid,
+ int all,
+ LDAPConn *lc,
+ LDAPMessage **result )
+{
+ BerElement *ber;
+ LDAPMessage *newmsg, *l, *prev;
+ ber_int_t id;
+ ber_tag_t tag;
+ ber_len_t len;
+ int foundit = 0;
+ LDAPRequest *lr, *tmplr, dummy_lr = { 0 };
+ BerElement tmpber;
+ int rc, refer_cnt, hadref, simple_request, err;
+ ber_int_t lderr = -1;
+
+#ifdef LDAP_CONNECTIONLESS
+ LDAPMessage *tmp = NULL, *chain_head = NULL;
+ int moremsgs = 0, isv2 = 0;
+#endif
+
+ assert( ld != NULL );
+ assert( lc != NULL );
+
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
+
+ Debug3( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
+ (void *)ld, msgid, all );
+
+retry:
+ if ( lc->lconn_ber == NULL ) {
+ lc->lconn_ber = ldap_alloc_ber_with_options( ld );
+
+ if ( lc->lconn_ber == NULL ) {
+ return -1;
+ }
+ }
+
+ ber = lc->lconn_ber;
+ assert( LBER_VALID (ber) );
+
+ /* get the next message */
+ sock_errset(0);
+#ifdef LDAP_CONNECTIONLESS
+ if ( LDAP_IS_UDP(ld) ) {
+ struct sockaddr_storage from;
+ if ( ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr_storage) ) < 0 )
+ goto fail;
+ if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1;
+ }
+nextresp3:
+#endif
+ tag = ber_get_next( lc->lconn_sb, &len, ber );
+ switch ( tag ) {
+ case LDAP_TAG_MESSAGE:
+ /*
+ * We read a complete message.
+ * The connection should no longer need this ber.
+ */
+ lc->lconn_ber = NULL;
+ break;
+
+ default:
+ /*
+ * We read a BerElement that isn't LDAP or the stream has desync'd.
+ * In either case, anything we read from now on is probably garbage,
+ * just drop the connection.
+ */
+ ber_free( ber, 1 );
+ lc->lconn_ber = NULL;
+ /* FALLTHRU */
+
+ case LBER_DEFAULT:
+fail:
+ err = sock_errno();
+#ifdef LDAP_DEBUG
+ Debug1( LDAP_DEBUG_CONNS,
+ "ber_get_next failed, errno=%d.\n", err );
+#endif
+ if ( err == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
+ if ( err == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
+ ld->ld_errno = LDAP_SERVER_DOWN;
+ if ( !LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_KEEPCONN )) {
+ --lc->lconn_refcnt;
+ }
+ lc->lconn_status = 0;
+ return -1;
+ }
+
+ /* message id */
+ if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
+ ber_free( ber, 1 );
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return( -1 );
+ }
+
+ /* id == 0 iff unsolicited notification message (RFC 4511) */
+
+ /* id < 0 is invalid, just toss it. FIXME: should we disconnect? */
+ if ( id < 0 ) {
+ goto retry_ber;
+ }
+
+ /* if it's been abandoned, toss it */
+ if ( id > 0 ) {
+ if ( ldap_abandoned( ld, id ) ) {
+ /* the message type */
+ tag = ber_peek_tag( ber, &len );
+ switch ( tag ) {
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_REFERENCE:
+ case LDAP_RES_INTERMEDIATE:
+ case LBER_ERROR:
+ break;
+
+ default:
+ /* there's no need to keep the id
+ * in the abandoned list any longer */
+ ldap_mark_abandoned( ld, id );
+ break;
+ }
+
+ Debug3( LDAP_DEBUG_ANY,
+ "abandoned/discarded ld %p msgid %d message type %s\n",
+ (void *)ld, id, ldap_int_msgtype2str( tag ) );
+
+retry_ber:
+ ber_free( ber, 1 );
+ if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
+ goto retry;
+ }
+ return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
+ }
+
+ lr = ldap_find_request_by_msgid( ld, id );
+ if ( lr == NULL ) {
+ const char *msg = "unknown";
+
+ /* the message type */
+ tag = ber_peek_tag( ber, &len );
+ switch ( tag ) {
+ case LBER_ERROR:
+ break;
+
+ default:
+ msg = ldap_int_msgtype2str( tag );
+ break;
+ }
+
+ Debug3( LDAP_DEBUG_ANY,
+ "no request for response on ld %p msgid %d message type %s (tossing)\n",
+ (void *)ld, id, msg );
+
+ goto retry_ber;
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ if ( LDAP_IS_UDP(ld) && isv2 ) {
+ ber_scanf(ber, "x{");
+ }
+nextresp2:
+ ;
+#endif
+ }
+
+ /* the message type */
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 1 );
+ return( -1 );
+ }
+
+ Debug3( LDAP_DEBUG_TRACE,
+ "read1msg: ld %p msgid %d message type %s\n",
+ (void *)ld, id, ldap_int_msgtype2str( tag ) );
+
+ if ( id == 0 ) {
+ /* unsolicited notification message (RFC 4511) */
+ if ( tag != LDAP_RES_EXTENDED ) {
+ /* toss it */
+ goto retry_ber;
+
+ /* strictly speaking, it's an error; from RFC 4511:
+
+4.4. Unsolicited Notification
+
+ An unsolicited notification is an LDAPMessage sent from the server to
+ the client that is not in response to any LDAPMessage received by the
+ server. It is used to signal an extraordinary condition in the
+ server or in the LDAP session between the client and the server. The
+ notification is of an advisory nature, and the server will not expect
+ any response to be returned from the client.
+
+ The unsolicited notification is structured as an LDAPMessage in which
+ the messageID is zero and protocolOp is set to the extendedResp
+ choice using the ExtendedResponse type (See Section 4.12). The
+ responseName field of the ExtendedResponse always contains an LDAPOID
+ that is unique for this notification.
+
+ * however, since unsolicited responses
+ * are of advisory nature, better
+ * toss it, right now
+ */
+
+#if 0
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 1 );
+ return( -1 );
+#endif
+ }
+
+ lr = &dummy_lr;
+ }
+
+ id = lr->lr_origid;
+ refer_cnt = 0;
+ hadref = simple_request = 0;
+ rc = LDAP_MSG_X_KEEP_LOOKING; /* default is to keep looking (no response found) */
+ lr->lr_res_msgtype = tag;
+
+ /*
+ * Check for V3 search reference
+ */
+ if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
+ if ( ld->ld_version > LDAP_VERSION2 ) {
+ /* This is a V3 search reference */
+ if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
+ lr->lr_parent != NULL )
+ {
+ char **refs = NULL;
+ tmpber = *ber;
+
+ /* Get the referral list */
+ if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
+ rc = LDAP_DECODING_ERROR;
+
+ } else {
+ /* Note: refs array is freed by ldap_chase_v3referrals */
+ refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
+ 1, &lr->lr_res_error, &hadref );
+ if ( refer_cnt > 0 ) {
+ /* successfully chased reference */
+ /* If haven't got end search, set chasing referrals */
+ if ( lr->lr_status != LDAP_REQST_COMPLETED ) {
+ lr->lr_status = LDAP_REQST_CHASINGREFS;
+ Debug1( LDAP_DEBUG_TRACE,
+ "read1msg: search ref chased, "
+ "mark request chasing refs, "
+ "id = %d\n",
+ lr->lr_msgid );
+ }
+ }
+ }
+ }
+ }
+
+ } else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
+ /* All results that just return a status, i.e. don't return data
+ * go through the following code. This code also chases V2 referrals
+ * and checks if all referrals have been chased.
+ */
+ char *lr_res_error = NULL;
+
+ tmpber = *ber; /* struct copy */
+ if ( ber_scanf( &tmpber, "{eAA", &lderr,
+ &lr->lr_res_matched, &lr_res_error )
+ != LBER_ERROR )
+ {
+ if ( lr_res_error != NULL ) {
+ if ( lr->lr_res_error != NULL ) {
+ (void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
+ LDAP_FREE( (char *)lr_res_error );
+
+ } else {
+ lr->lr_res_error = lr_res_error;
+ }
+ lr_res_error = NULL;
+ }
+
+ /* Do we need to check for referrals? */
+ if ( tag != LDAP_RES_BIND &&
+ ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
+ lr->lr_parent != NULL ))
+ {
+ char **refs = NULL;
+ ber_len_t len;
+
+ /* Check if V3 referral */
+ if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
+ if ( ld->ld_version > LDAP_VERSION2 ) {
+ /* Get the referral list */
+ if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
+ rc = LDAP_DECODING_ERROR;
+ lr->lr_status = LDAP_REQST_COMPLETED;
+ Debug2( LDAP_DEBUG_TRACE,
+ "read1msg: referral decode error, "
+ "mark request completed, ld %p msgid %d\n",
+ (void *)ld, lr->lr_msgid );
+
+ } else {
+ /* Chase the referral
+ * refs array is freed by ldap_chase_v3referrals
+ */
+ refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
+ 0, &lr->lr_res_error, &hadref );
+ lr->lr_status = LDAP_REQST_COMPLETED;
+ Debug3( LDAP_DEBUG_TRACE,
+ "read1msg: referral %s chased, "
+ "mark request completed, ld %p msgid %d\n",
+ refer_cnt > 0 ? "" : "not",
+ (void *)ld, lr->lr_msgid);
+ if ( refer_cnt < 0 ) {
+ refer_cnt = 0;
+ }
+ }
+ }
+ } else {
+ switch ( lderr ) {
+ case LDAP_SUCCESS:
+ case LDAP_COMPARE_TRUE:
+ case LDAP_COMPARE_FALSE:
+ break;
+
+ default:
+ if ( lr->lr_res_error == NULL ) {
+ break;
+ }
+
+ /* pedantic, should never happen */
+ if ( lr->lr_res_error[ 0 ] == '\0' ) {
+ LDAP_FREE( lr->lr_res_error );
+ lr->lr_res_error = NULL;
+ break;
+ }
+
+ /* V2 referrals are in error string */
+ refer_cnt = ldap_chase_referrals( ld, lr,
+ &lr->lr_res_error, -1, &hadref );
+ lr->lr_status = LDAP_REQST_COMPLETED;
+ Debug1( LDAP_DEBUG_TRACE,
+ "read1msg: V2 referral chased, "
+ "mark request completed, id = %d\n",
+ lr->lr_msgid );
+ break;
+ }
+ }
+ }
+
+ /* save errno, message, and matched string */
+ if ( !hadref || lr->lr_res_error == NULL ) {
+ lr->lr_res_errno =
+ lderr == LDAP_PARTIAL_RESULTS
+ ? LDAP_SUCCESS : lderr;
+
+ } else if ( ld->ld_errno != LDAP_SUCCESS ) {
+ lr->lr_res_errno = ld->ld_errno;
+
+ } else {
+ lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
+ }
+ }
+
+ /* in any case, don't leave any lr_res_error 'round */
+ if ( lr_res_error ) {
+ LDAP_FREE( lr_res_error );
+ }
+
+ Debug2( LDAP_DEBUG_TRACE,
+ "read1msg: ld %p %d new referrals\n",
+ (void *)ld, refer_cnt );
+
+ if ( refer_cnt != 0 ) { /* chasing referrals */
+ ber_free( ber, 1 );
+ ber = NULL;
+ if ( refer_cnt < 0 ) {
+ ldap_return_request( ld, lr, 0 );
+ return( -1 ); /* fatal error */
+ }
+ lr->lr_res_errno = LDAP_SUCCESS; /* successfully chased referral */
+ if ( lr->lr_res_matched ) {
+ LDAP_FREE( lr->lr_res_matched );
+ lr->lr_res_matched = NULL;
+ }
+
+ } else {
+ if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
+ /* request without any referrals */
+ simple_request = ( hadref ? 0 : 1 );
+
+ } else {
+ /* request with referrals or child request */
+ ber_free( ber, 1 );
+ ber = NULL;
+ }
+
+ lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
+ Debug2( LDAP_DEBUG_TRACE,
+ "read1msg: mark request completed, ld %p msgid %d\n",
+ (void *)ld, lr->lr_msgid );
+ tmplr = lr;
+ while ( lr->lr_parent != NULL ) {
+ merge_error_info( ld, lr->lr_parent, lr );
+
+ lr = lr->lr_parent;
+ if ( --lr->lr_outrefcnt > 0 ) {
+ break; /* not completely done yet */
+ }
+ }
+ /* ITS#6744: Original lr was refcounted when we retrieved it,
+ * must release it now that we're working with the parent
+ */
+ if ( tmplr->lr_parent ) {
+ ldap_return_request( ld, tmplr, 0 );
+ }
+
+ /* Check if all requests are finished, lr is now parent */
+ tmplr = lr;
+ if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
+ for ( tmplr = lr->lr_child;
+ tmplr != NULL;
+ tmplr = tmplr->lr_refnext )
+ {
+ if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
+ }
+ }
+
+ /* This is the parent request if the request has referrals */
+ if ( lr->lr_outrefcnt <= 0 &&
+ lr->lr_parent == NULL &&
+ tmplr == NULL )
+ {
+ id = lr->lr_msgid;
+ tag = lr->lr_res_msgtype;
+ Debug2( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n",
+ (void *)ld, id );
+ Debug3( LDAP_DEBUG_TRACE,
+ "res_errno: %d, res_error: <%s>, "
+ "res_matched: <%s>\n",
+ lr->lr_res_errno,
+ lr->lr_res_error ? lr->lr_res_error : "",
+ lr->lr_res_matched ? lr->lr_res_matched : "" );
+ if ( !simple_request ) {
+ ber_free( ber, 1 );
+ ber = NULL;
+ if ( build_result_ber( ld, &ber, lr )
+ == LBER_ERROR )
+ {
+ rc = -1; /* fatal error */
+ }
+ }
+
+ if ( lr != &dummy_lr ) {
+ ldap_return_request( ld, lr, 1 );
+ }
+ lr = NULL;
+ }
+
+ /*
+ * RFC 4511 unsolicited (id == 0) responses
+ * shouldn't necessarily end the connection
+ */
+ if ( lc != NULL && id != 0 &&
+ !LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_KEEPCONN )) {
+ --lc->lconn_refcnt;
+ lc = NULL;
+ }
+ }
+ }
+
+ if ( lr != NULL ) {
+ if ( lr != &dummy_lr ) {
+ ldap_return_request( ld, lr, 0 );
+ }
+ lr = NULL;
+ }
+
+ if ( ber == NULL ) {
+ return( rc );
+ }
+
+ /* try to handle unsolicited responses as appropriate */
+ if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
+ int is_nod = 0;
+
+ tag = ber_peek_tag( &tmpber, &len );
+
+ /* we have a res oid */
+ if ( tag == LDAP_TAG_EXOP_RES_OID ) {
+ static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
+ struct berval resoid = BER_BVNULL;
+
+ if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 1 );
+ return -1;
+ }
+
+ assert( !BER_BVISEMPTY( &resoid ) );
+
+ is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
+
+ tag = ber_peek_tag( &tmpber, &len );
+ }
+
+#if 0 /* don't need right now */
+ /* we have res data */
+ if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
+ struct berval resdata;
+
+ if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 0 );
+ return ld->ld_errno;
+ }
+
+ /* use it... */
+ }
+#endif
+
+ /* handle RFC 4511 "Notice of Disconnection" locally */
+
+ if ( is_nod ) {
+ if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 1 );
+ return -1;
+ }
+
+ /* get rid of the connection... */
+ if ( lc != NULL &&
+ !LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_KEEPCONN )) {
+ --lc->lconn_refcnt;
+ }
+
+ /* need to return -1, because otherwise
+ * a valid result is expected */
+ ld->ld_errno = lderr;
+ return -1;
+ }
+ }
+
+ /* make a new ldap message */
+ newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
+ if ( newmsg == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return( -1 );
+ }
+ newmsg->lm_msgid = (int)id;
+ newmsg->lm_msgtype = tag;
+ newmsg->lm_ber = ber;
+ newmsg->lm_chain_tail = newmsg;
+
+#ifdef LDAP_CONNECTIONLESS
+ /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
+ * the responses are all a sequence wrapped in one message. In
+ * LDAPv3 each response is in its own message. The datagram must
+ * end with a SearchResult. We can't just parse each response in
+ * separate calls to try_read1msg because the header info is only
+ * present at the beginning of the datagram, not at the beginning
+ * of each response. So parse all the responses at once and queue
+ * them up, then pull off the first response to return to the
+ * caller when all parsing is complete.
+ */
+ if ( LDAP_IS_UDP(ld) ) {
+ /* If not a result, look for more */
+ if ( tag != LDAP_RES_SEARCH_RESULT ) {
+ int ok = 0;
+ moremsgs = 1;
+ if (isv2) {
+ /* LDAPv2: dup the current ber, skip past the current
+ * response, and see if there are any more after it.
+ */
+ ber = ber_dup( ber );
+ ber_scanf( ber, "x" );
+ if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
+ /* There's more - dup the ber buffer so they can all be
+ * individually freed by ldap_msgfree.
+ */
+ struct berval bv;
+ ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
+ bv.bv_val = LDAP_MALLOC( len );
+ if ( bv.bv_val ) {
+ ok = 1;
+ ber_read( ber, bv.bv_val, len );
+ bv.bv_len = len;
+ ber_init2( ber, &bv, ld->ld_lberoptions );
+ }
+ }
+ } else {
+ /* LDAPv3: Just allocate a new ber. Since this is a buffered
+ * datagram, if the sockbuf is readable we still have data
+ * to parse.
+ */
+ ber = ldap_alloc_ber_with_options( ld );
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return -1;
+ }
+
+ if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
+ }
+ /* set up response chain */
+ if ( tmp == NULL ) {
+ newmsg->lm_next = ld->ld_responses;
+ ld->ld_responses = newmsg;
+ chain_head = newmsg;
+ } else {
+ tmp->lm_chain = newmsg;
+ }
+ chain_head->lm_chain_tail = newmsg;
+ tmp = newmsg;
+ /* "ok" means there's more to parse */
+ if ( ok ) {
+ if ( isv2 ) {
+ goto nextresp2;
+
+ } else {
+ goto nextresp3;
+ }
+ } else {
+ /* got to end of datagram without a SearchResult. Free
+ * our dup'd ber, but leave any buffer alone. For v2 case,
+ * the previous response is still using this buffer. For v3,
+ * the new ber has no buffer to free yet.
+ */
+ ber_free( ber, 0 );
+ return -1;
+ }
+ } else if ( moremsgs ) {
+ /* got search result, and we had multiple responses in 1 datagram.
+ * stick the result onto the end of the chain, and then pull the
+ * first response off the head of the chain.
+ */
+ tmp->lm_chain = newmsg;
+ chain_head->lm_chain_tail = newmsg;
+ *result = chkResponseList( ld, msgid, all );
+ ld->ld_errno = LDAP_SUCCESS;
+ return( (*result)->lm_msgtype );
+ }
+ }
+#endif /* LDAP_CONNECTIONLESS */
+
+ /* is this the one we're looking for? */
+ if ( msgid == LDAP_RES_ANY || id == msgid ) {
+ if ( all == LDAP_MSG_ONE
+ || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
+ && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
+ && newmsg->lm_msgtype != LDAP_RES_INTERMEDIATE
+ && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
+ {
+ *result = newmsg;
+ ld->ld_errno = LDAP_SUCCESS;
+ return( tag );
+
+ } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
+ foundit = 1; /* return the chain later */
+ }
+ }
+
+ /*
+ * if not, we must add it to the list of responses. if
+ * the msgid is already there, it must be part of an existing
+ * search response.
+ */
+
+ prev = NULL;
+ for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
+ if ( l->lm_msgid == newmsg->lm_msgid ) {
+ break;
+ }
+ prev = l;
+ }
+
+ /* not part of an existing search response */
+ if ( l == NULL ) {
+ if ( foundit ) {
+ *result = newmsg;
+ goto exit;
+ }
+
+ newmsg->lm_next = ld->ld_responses;
+ ld->ld_responses = newmsg;
+ goto exit;
+ }
+
+ Debug3( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n",
+ (void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype );
+
+ /* part of a search response - add to end of list of entries */
+ l->lm_chain_tail->lm_chain = newmsg;
+ l->lm_chain_tail = newmsg;
+
+ /* return the whole chain if that's what we were looking for */
+ if ( foundit ) {
+ if ( prev == NULL ) {
+ ld->ld_responses = l->lm_next;
+ } else {
+ prev->lm_next = l->lm_next;
+ }
+ *result = l;
+ }
+
+exit:
+ if ( foundit ) {
+ ld->ld_errno = LDAP_SUCCESS;
+ return( tag );
+ }
+ if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
+ goto retry;
+ }
+ return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
+}
+
+
+static ber_tag_t
+build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
+{
+ ber_len_t len;
+ ber_tag_t tag;
+ ber_int_t along;
+ BerElement *ber;
+
+ *bp = NULL;
+ ber = ldap_alloc_ber_with_options( ld );
+
+ if( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return LBER_ERROR;
+ }
+
+ if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
+ lr->lr_res_msgtype, lr->lr_res_errno,
+ lr->lr_res_matched ? lr->lr_res_matched : "",
+ lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
+ {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( LBER_ERROR );
+ }
+
+ ber_reset( ber, 1 );
+
+ if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 1 );
+ return( LBER_ERROR );
+ }
+
+ if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 1 );
+ return( LBER_ERROR );
+ }
+
+ tag = ber_peek_tag( ber, &len );
+
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 1 );
+ return( LBER_ERROR );
+ }
+
+ *bp = ber;
+ return tag;
+}
+
+
+/*
+ * Merge error information in "lr" with "parentr" error code and string.
+ */
+static void
+merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
+{
+ if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
+ parentr->lr_res_errno = lr->lr_res_errno;
+ if ( lr->lr_res_error != NULL ) {
+ (void)ldap_append_referral( ld, &parentr->lr_res_error,
+ lr->lr_res_error );
+ }
+
+ } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
+ parentr->lr_res_errno == LDAP_SUCCESS )
+ {
+ parentr->lr_res_errno = lr->lr_res_errno;
+ if ( parentr->lr_res_error != NULL ) {
+ LDAP_FREE( parentr->lr_res_error );
+ }
+ parentr->lr_res_error = lr->lr_res_error;
+ lr->lr_res_error = NULL;
+ if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
+ if ( parentr->lr_res_matched != NULL ) {
+ LDAP_FREE( parentr->lr_res_matched );
+ }
+ parentr->lr_res_matched = lr->lr_res_matched;
+ lr->lr_res_matched = NULL;
+ }
+ }
+
+ Debug1( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ",
+ parentr->lr_msgid );
+ Debug3( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
+ parentr->lr_res_errno,
+ parentr->lr_res_error ? parentr->lr_res_error : "",
+ parentr->lr_res_matched ? parentr->lr_res_matched : "" );
+}
+
+
+
+int
+ldap_msgtype( LDAPMessage *lm )
+{
+ assert( lm != NULL );
+ return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
+}
+
+
+int
+ldap_msgid( LDAPMessage *lm )
+{
+ assert( lm != NULL );
+
+ return ( lm != NULL ) ? lm->lm_msgid : -1;
+}
+
+
+const char *
+ldap_int_msgtype2str( ber_tag_t tag )
+{
+ switch( tag ) {
+ case LDAP_RES_ADD: return "add";
+ case LDAP_RES_BIND: return "bind";
+ case LDAP_RES_COMPARE: return "compare";
+ case LDAP_RES_DELETE: return "delete";
+ case LDAP_RES_EXTENDED: return "extended-result";
+ case LDAP_RES_INTERMEDIATE: return "intermediate";
+ case LDAP_RES_MODIFY: return "modify";
+ case LDAP_RES_RENAME: return "rename";
+ case LDAP_RES_SEARCH_ENTRY: return "search-entry";
+ case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
+ case LDAP_RES_SEARCH_RESULT: return "search-result";
+ }
+ return "unknown";
+}
+
+int
+ldap_msgfree( LDAPMessage *lm )
+{
+ LDAPMessage *next;
+ int type = 0;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_msgfree\n" );
+
+ for ( ; lm != NULL; lm = next ) {
+ next = lm->lm_chain;
+ type = lm->lm_msgtype;
+ ber_free( lm->lm_ber, 1 );
+ LDAP_FREE( (char *) lm );
+ }
+
+ return type;
+}
+
+/*
+ * ldap_msgdelete - delete a message. It returns:
+ * 0 if the entire message was deleted
+ * -1 if the message was not found, or only part of it was found
+ */
+int
+ldap_msgdelete( LDAP *ld, int msgid )
+{
+ LDAPMessage *lm, *prev;
+ int rc = 0;
+
+ assert( ld != NULL );
+
+ Debug2( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
+ (void *)ld, msgid );
+
+ LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
+ prev = NULL;
+ for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
+ if ( lm->lm_msgid == msgid ) {
+ break;
+ }
+ prev = lm;
+ }
+
+ if ( lm == NULL ) {
+ rc = -1;
+
+ } else {
+ if ( prev == NULL ) {
+ ld->ld_responses = lm->lm_next;
+ } else {
+ prev->lm_next = lm->lm_next;
+ }
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
+ if ( lm ) {
+ switch ( ldap_msgfree( lm ) ) {
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_REFERENCE:
+ case LDAP_RES_INTERMEDIATE:
+ rc = -1;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return rc;
+}
+
+
+/*
+ * ldap_abandoned
+ *
+ * return the location of the message id in the array of abandoned
+ * message ids, or -1
+ */
+static int
+ldap_abandoned( LDAP *ld, ber_int_t msgid )
+{
+ int ret, idx;
+ assert( msgid >= 0 );
+
+ LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
+ ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx );
+ LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
+ return ret;
+}
+
+/*
+ * ldap_mark_abandoned
+ */
+static int
+ldap_mark_abandoned( LDAP *ld, ber_int_t msgid )
+{
+ int ret, idx;
+
+ assert( msgid >= 0 );
+ LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
+ ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx );
+ if (ret <= 0) { /* error or already deleted by another thread */
+ LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
+ return ret;
+ }
+ /* still in abandoned array, so delete */
+ ret = ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,
+ msgid, idx );
+ LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
+ return ret;
+}
diff --git a/libraries/libldap/rq.c b/libraries/libldap/rq.c
new file mode 100644
index 0000000..6b16409
--- /dev/null
+++ b/libraries/libldap/rq.c
@@ -0,0 +1,225 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* This work was initially developed by Jong Hyuk Choi for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdarg.h>
+#include <ac/stdlib.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#ifdef LDAP_R_COMPILE
+
+#include "ldap_pvt_thread.h"
+#include "ldap_queue.h"
+#include "ldap_rq.h"
+
+struct re_s *
+ldap_pvt_runqueue_insert(
+ struct runqueue_s* rq,
+ time_t interval,
+ ldap_pvt_thread_start_t *routine,
+ void *arg,
+ char *tname,
+ char *tspec
+)
+{
+ struct re_s* entry;
+
+ entry = (struct re_s *) LDAP_CALLOC( 1, sizeof( struct re_s ));
+ if ( entry ) {
+ entry->interval.tv_sec = interval;
+ entry->interval.tv_usec = 0;
+ entry->next_sched.tv_sec = time( NULL );
+ entry->next_sched.tv_usec = 0;
+ entry->routine = routine;
+ entry->arg = arg;
+ entry->tname = tname;
+ entry->tspec = tspec;
+ LDAP_STAILQ_INSERT_HEAD( &rq->task_list, entry, tnext );
+ }
+ return entry;
+}
+
+struct re_s *
+ldap_pvt_runqueue_find(
+ struct runqueue_s *rq,
+ ldap_pvt_thread_start_t *routine,
+ void *arg
+)
+{
+ struct re_s* e;
+
+ LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) {
+ if ( e->routine == routine && e->arg == arg )
+ return e;
+ }
+ return NULL;
+}
+
+void
+ldap_pvt_runqueue_remove(
+ struct runqueue_s* rq,
+ struct re_s* entry
+)
+{
+ struct re_s* e;
+
+ LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) {
+ if ( e == entry)
+ break;
+ }
+
+ assert( e == entry );
+
+ LDAP_STAILQ_REMOVE( &rq->task_list, entry, re_s, tnext );
+
+ LDAP_FREE( entry );
+}
+
+struct re_s*
+ldap_pvt_runqueue_next_sched(
+ struct runqueue_s* rq,
+ struct timeval* next_run
+)
+{
+ struct re_s* entry;
+
+ entry = LDAP_STAILQ_FIRST( &rq->task_list );
+ if ( entry == NULL || entry->next_sched.tv_sec == 0 ) {
+ return NULL;
+ } else {
+ *next_run = entry->next_sched;
+ return entry;
+ }
+}
+
+void
+ldap_pvt_runqueue_runtask(
+ struct runqueue_s* rq,
+ struct re_s* entry
+)
+{
+ LDAP_STAILQ_INSERT_TAIL( &rq->run_list, entry, rnext );
+}
+
+void
+ldap_pvt_runqueue_stoptask(
+ struct runqueue_s* rq,
+ struct re_s* entry
+)
+{
+ LDAP_STAILQ_REMOVE( &rq->run_list, entry, re_s, rnext );
+}
+
+int
+ldap_pvt_runqueue_isrunning(
+ struct runqueue_s* rq,
+ struct re_s* entry
+)
+{
+ struct re_s* e;
+
+ LDAP_STAILQ_FOREACH( e, &rq->run_list, rnext ) {
+ if ( e == entry ) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void
+ldap_pvt_runqueue_resched(
+ struct runqueue_s* rq,
+ struct re_s* entry,
+ int defer
+)
+{
+ struct re_s* prev;
+ struct re_s* e;
+
+ LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) {
+ if ( e == entry )
+ break;
+ }
+
+ assert ( e == entry );
+
+ LDAP_STAILQ_REMOVE( &rq->task_list, entry, re_s, tnext );
+
+ if ( !defer ) {
+ entry->next_sched.tv_sec = time( NULL ) + entry->interval.tv_sec;
+ } else {
+ entry->next_sched.tv_sec = 0;
+ }
+
+ if ( LDAP_STAILQ_EMPTY( &rq->task_list )) {
+ LDAP_STAILQ_INSERT_HEAD( &rq->task_list, entry, tnext );
+ } else if ( entry->next_sched.tv_sec == 0 ) {
+ LDAP_STAILQ_INSERT_TAIL( &rq->task_list, entry, tnext );
+ } else {
+ prev = NULL;
+ LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) {
+ if ( e->next_sched.tv_sec == 0 ) {
+ if ( prev == NULL ) {
+ LDAP_STAILQ_INSERT_HEAD( &rq->task_list, entry, tnext );
+ } else {
+ LDAP_STAILQ_INSERT_AFTER( &rq->task_list, prev, entry, tnext );
+ }
+ return;
+ } else if ( e->next_sched.tv_sec > entry->next_sched.tv_sec ) {
+ if ( prev == NULL ) {
+ LDAP_STAILQ_INSERT_HEAD( &rq->task_list, entry, tnext );
+ } else {
+ LDAP_STAILQ_INSERT_AFTER( &rq->task_list, prev, entry, tnext );
+ }
+ return;
+ }
+ prev = e;
+ }
+ LDAP_STAILQ_INSERT_TAIL( &rq->task_list, entry, tnext );
+ }
+}
+
+int
+ldap_pvt_runqueue_persistent_backload(
+ struct runqueue_s* rq
+)
+{
+ struct re_s* e;
+ int count = 0;
+
+ ldap_pvt_thread_mutex_lock( &rq->rq_mutex );
+ if ( !LDAP_STAILQ_EMPTY( &rq->task_list )) {
+ LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) {
+ if ( e->next_sched.tv_sec == 0 )
+ count++;
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &rq->rq_mutex );
+ return count;
+}
+
+#endif /* LDAP_R_COMPILE */
diff --git a/libraries/libldap/sasl.c b/libraries/libldap/sasl.c
new file mode 100644
index 0000000..fa5fc13
--- /dev/null
+++ b/libraries/libldap/sasl.c
@@ -0,0 +1,867 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * BindRequest ::= SEQUENCE {
+ * version INTEGER,
+ * name DistinguishedName, -- who
+ * authentication CHOICE {
+ * simple [0] OCTET STRING -- passwd
+ * krbv42ldap [1] OCTET STRING -- OBSOLETE
+ * krbv42dsa [2] OCTET STRING -- OBSOLETE
+ * sasl [3] SaslCredentials -- LDAPv3
+ * }
+ * }
+ *
+ * BindResponse ::= SEQUENCE {
+ * COMPONENTS OF LDAPResult,
+ * serverSaslCreds OCTET STRING OPTIONAL -- LDAPv3
+ * }
+ *
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/errno.h>
+
+#include "ldap-int.h"
+
+BerElement *
+ldap_build_bind_req(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *mechanism,
+ struct berval *cred,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp )
+{
+ BerElement *ber;
+ int rc;
+
+ if( mechanism == LDAP_SASL_SIMPLE ) {
+ if( dn == NULL && cred != NULL && cred->bv_len ) {
+ /* use default binddn */
+ dn = ld->ld_defbinddn;
+ }
+
+ } else if( ld->ld_version < LDAP_VERSION3 ) {
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ return( NULL );
+ }
+
+ if ( dn == NULL ) {
+ dn = "";
+ }
+
+ /* create a message to send */
+ if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
+ return( NULL );
+ }
+
+ LDAP_NEXT_MSGID( ld, *msgidp );
+ if( mechanism == LDAP_SASL_SIMPLE ) {
+ /* simple bind */
+ rc = ber_printf( ber, "{it{istON}" /*}*/,
+ *msgidp, LDAP_REQ_BIND,
+ ld->ld_version, dn, LDAP_AUTH_SIMPLE,
+ cred );
+
+ } else if ( cred == NULL || cred->bv_val == NULL ) {
+ /* SASL bind w/o credentials */
+ rc = ber_printf( ber, "{it{ist{sN}N}" /*}*/,
+ *msgidp, LDAP_REQ_BIND,
+ ld->ld_version, dn, LDAP_AUTH_SASL,
+ mechanism );
+
+ } else {
+ /* SASL bind w/ credentials */
+ rc = ber_printf( ber, "{it{ist{sON}N}" /*}*/,
+ *msgidp, LDAP_REQ_BIND,
+ ld->ld_version, dn, LDAP_AUTH_SASL,
+ mechanism, cred );
+ }
+
+ if( rc == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ /* Put Server Controls */
+ if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ return( ber );
+}
+
+/*
+ * ldap_sasl_bind - bind to the ldap server (and X.500).
+ * The dn (usually NULL), mechanism, and credentials are provided.
+ * The message id of the request initiated is provided upon successful
+ * (LDAP_SUCCESS) return.
+ *
+ * Example:
+ * ldap_sasl_bind( ld, NULL, "mechanism",
+ * cred, NULL, NULL, &msgid )
+ */
+
+int
+ldap_sasl_bind(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *mechanism,
+ struct berval *cred,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ BerElement *ber;
+ int rc;
+ ber_int_t id;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_sasl_bind\n" );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( msgidp != NULL );
+
+ /* check client controls */
+ rc = ldap_int_client_controls( ld, cctrls );
+ if( rc != LDAP_SUCCESS ) return rc;
+
+ ber = ldap_build_bind_req( ld, dn, mechanism, cred, sctrls, cctrls, &id );
+ if( !ber )
+ return ld->ld_errno;
+
+ /* send the message */
+ *msgidp = ldap_send_initial_request( ld, LDAP_REQ_BIND, dn, ber, id );
+
+ if(*msgidp < 0)
+ return ld->ld_errno;
+
+ return LDAP_SUCCESS;
+}
+
+
+int
+ldap_sasl_bind_s(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *mechanism,
+ struct berval *cred,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ struct berval **servercredp )
+{
+ int rc, msgid;
+ LDAPMessage *result;
+ struct berval *scredp = NULL;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_sasl_bind_s\n" );
+
+ /* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */
+ if( servercredp != NULL ) {
+ if (ld->ld_version < LDAP_VERSION3) {
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ return ld->ld_errno;
+ }
+ *servercredp = NULL;
+ }
+
+ rc = ldap_sasl_bind( ld, dn, mechanism, cred, sctrls, cctrls, &msgid );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return( rc );
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ if (LDAP_IS_UDP(ld)) {
+ return( rc );
+ }
+#endif
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1 || !result ) {
+ return( ld->ld_errno ); /* ldap_result sets ld_errno */
+ }
+
+ /* parse the results */
+ scredp = NULL;
+ if( servercredp != NULL ) {
+ rc = ldap_parse_sasl_bind_result( ld, result, &scredp, 0 );
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_msgfree( result );
+ return( rc );
+ }
+
+ rc = ldap_result2error( ld, result, 1 );
+
+ if ( rc == LDAP_SUCCESS || rc == LDAP_SASL_BIND_IN_PROGRESS ) {
+ if( servercredp != NULL ) {
+ *servercredp = scredp;
+ scredp = NULL;
+ }
+ }
+
+ if ( scredp != NULL ) {
+ ber_bvfree(scredp);
+ }
+
+ return rc;
+}
+
+
+/*
+* Parse BindResponse:
+*
+* BindResponse ::= [APPLICATION 1] SEQUENCE {
+* COMPONENTS OF LDAPResult,
+* serverSaslCreds [7] OCTET STRING OPTIONAL }
+*
+* LDAPResult ::= SEQUENCE {
+* resultCode ENUMERATED,
+* matchedDN LDAPDN,
+* errorMessage LDAPString,
+* referral [3] Referral OPTIONAL }
+*/
+
+int
+ldap_parse_sasl_bind_result(
+ LDAP *ld,
+ LDAPMessage *res,
+ struct berval **servercredp,
+ int freeit )
+{
+ ber_int_t errcode;
+ struct berval* scred;
+
+ ber_tag_t tag;
+ BerElement *ber;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_parse_sasl_bind_result\n" );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( res != NULL );
+
+ if( servercredp != NULL ) {
+ if( ld->ld_version < LDAP_VERSION2 ) {
+ return LDAP_NOT_SUPPORTED;
+ }
+ *servercredp = NULL;
+ }
+
+ if( res->lm_msgtype != LDAP_RES_BIND ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ scred = NULL;
+
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ ld->ld_error = NULL;
+ }
+ if ( ld->ld_matched ) {
+ LDAP_FREE( ld->ld_matched );
+ ld->ld_matched = NULL;
+ }
+
+ /* parse results */
+
+ ber = ber_dup( res->lm_ber );
+
+ if( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ if ( ld->ld_version < LDAP_VERSION2 ) {
+ tag = ber_scanf( ber, "{iA}",
+ &errcode, &ld->ld_error );
+
+ if( tag == LBER_ERROR ) {
+ ber_free( ber, 0 );
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return ld->ld_errno;
+ }
+
+ } else {
+ ber_len_t len;
+
+ tag = ber_scanf( ber, "{eAA" /*}*/,
+ &errcode, &ld->ld_matched, &ld->ld_error );
+
+ if( tag == LBER_ERROR ) {
+ ber_free( ber, 0 );
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return ld->ld_errno;
+ }
+
+ tag = ber_peek_tag(ber, &len);
+
+ if( tag == LDAP_TAG_REFERRAL ) {
+ /* skip 'em */
+ if( ber_scanf( ber, "x" ) == LBER_ERROR ) {
+ ber_free( ber, 0 );
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return ld->ld_errno;
+ }
+
+ tag = ber_peek_tag(ber, &len);
+ }
+
+ if( tag == LDAP_TAG_SASL_RES_CREDS ) {
+ if( ber_scanf( ber, "O", &scred ) == LBER_ERROR ) {
+ ber_free( ber, 0 );
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return ld->ld_errno;
+ }
+ }
+ }
+
+ ber_free( ber, 0 );
+
+ if ( servercredp != NULL ) {
+ *servercredp = scred;
+
+ } else if ( scred != NULL ) {
+ ber_bvfree( scred );
+ }
+
+ ld->ld_errno = errcode;
+
+ if ( freeit ) {
+ ldap_msgfree( res );
+ }
+
+ return( LDAP_SUCCESS );
+}
+
+int
+ldap_pvt_sasl_getmechs ( LDAP *ld, char **pmechlist )
+{
+ /* we need to query the server for supported mechs anyway */
+ LDAPMessage *res, *e;
+ char *attrs[] = { "supportedSASLMechanisms", NULL };
+ char **values, *mechlist;
+ int rc;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_pvt_sasl_getmech\n" );
+
+ rc = ldap_search_s( ld, "", LDAP_SCOPE_BASE,
+ NULL, attrs, 0, &res );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return ld->ld_errno;
+ }
+
+ e = ldap_first_entry( ld, res );
+ if ( e == NULL ) {
+ ldap_msgfree( res );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ ld->ld_errno = LDAP_NO_SUCH_OBJECT;
+ }
+ return ld->ld_errno;
+ }
+
+ values = ldap_get_values( ld, e, "supportedSASLMechanisms" );
+ if ( values == NULL ) {
+ ldap_msgfree( res );
+ ld->ld_errno = LDAP_NO_SUCH_ATTRIBUTE;
+ return ld->ld_errno;
+ }
+
+ mechlist = ldap_charray2str( values, " " );
+ if ( mechlist == NULL ) {
+ LDAP_VFREE( values );
+ ldap_msgfree( res );
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ LDAP_VFREE( values );
+ ldap_msgfree( res );
+
+ *pmechlist = mechlist;
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * ldap_sasl_interactive_bind - interactive SASL authentication
+ *
+ * This routine uses interactive callbacks.
+ *
+ * LDAP_SUCCESS is returned upon success, the ldap error code
+ * otherwise. LDAP_SASL_BIND_IN_PROGRESS is returned if further
+ * calls are needed.
+ */
+int
+ldap_sasl_interactive_bind(
+ LDAP *ld,
+ LDAP_CONST char *dn, /* usually NULL */
+ LDAP_CONST char *mechs,
+ LDAPControl **serverControls,
+ LDAPControl **clientControls,
+ unsigned flags,
+ LDAP_SASL_INTERACT_PROC *interact,
+ void *defaults,
+ LDAPMessage *result,
+ const char **rmech,
+ int *msgid )
+{
+ char *smechs = NULL;
+ int rc;
+
+#ifdef LDAP_CONNECTIONLESS
+ if( LDAP_IS_UDP(ld) ) {
+ /* Just force it to simple bind, silly to make the user
+ * ask all the time. No, we don't ever actually bind, but I'll
+ * let the final bind handler take care of saving the cdn.
+ */
+ rc = ldap_simple_bind( ld, dn, NULL );
+ rc = rc < 0 ? rc : 0;
+ goto done;
+ } else
+#endif
+
+ /* First time */
+ if ( !result ) {
+
+#ifdef HAVE_CYRUS_SASL
+ if( mechs == NULL || *mechs == '\0' ) {
+ mechs = ld->ld_options.ldo_def_sasl_mech;
+ }
+#endif
+
+ if( mechs == NULL || *mechs == '\0' ) {
+ /* FIXME: this needs to be asynchronous too;
+ * perhaps NULL should be disallowed for async usage?
+ */
+ rc = ldap_pvt_sasl_getmechs( ld, &smechs );
+ if( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ Debug1( LDAP_DEBUG_TRACE,
+ "ldap_sasl_interactive_bind: server supports: %s\n",
+ smechs );
+
+ mechs = smechs;
+
+ } else {
+ Debug1( LDAP_DEBUG_TRACE,
+ "ldap_sasl_interactive_bind: user selected: %s\n",
+ mechs );
+ }
+ }
+ rc = ldap_int_sasl_bind( ld, dn, mechs,
+ serverControls, clientControls,
+ flags, interact, defaults, result, rmech, msgid );
+
+done:
+ if ( smechs ) LDAP_FREE( smechs );
+
+ return rc;
+}
+
+/*
+ * ldap_sasl_interactive_bind_s - interactive SASL authentication
+ *
+ * This routine uses interactive callbacks.
+ *
+ * LDAP_SUCCESS is returned upon success, the ldap error code
+ * otherwise.
+ */
+int
+ldap_sasl_interactive_bind_s(
+ LDAP *ld,
+ LDAP_CONST char *dn, /* usually NULL */
+ LDAP_CONST char *mechs,
+ LDAPControl **serverControls,
+ LDAPControl **clientControls,
+ unsigned flags,
+ LDAP_SASL_INTERACT_PROC *interact,
+ void *defaults )
+{
+ const char *rmech = NULL;
+ LDAPMessage *result = NULL;
+ int rc, msgid;
+
+ do {
+ rc = ldap_sasl_interactive_bind( ld, dn, mechs,
+ serverControls, clientControls,
+ flags, interact, defaults, result, &rmech, &msgid );
+
+ ldap_msgfree( result );
+
+ if ( rc != LDAP_SASL_BIND_IN_PROGRESS )
+ break;
+
+#ifdef LDAP_CONNECTIONLESS
+ if (LDAP_IS_UDP(ld)) {
+ break;
+ }
+#endif
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1 || !result ) {
+ return( ld->ld_errno ); /* ldap_result sets ld_errno */
+ }
+ } while ( rc == LDAP_SASL_BIND_IN_PROGRESS );
+
+ return rc;
+}
+
+#ifdef HAVE_CYRUS_SASL
+
+#ifdef HAVE_SASL_SASL_H
+#include <sasl/sasl.h>
+#else
+#include <sasl.h>
+#endif
+
+#endif /* HAVE_CYRUS_SASL */
+
+static int
+sb_sasl_generic_remove( Sockbuf_IO_Desc *sbiod );
+
+static int
+sb_sasl_generic_setup( Sockbuf_IO_Desc *sbiod, void *arg )
+{
+ struct sb_sasl_generic_data *p;
+ struct sb_sasl_generic_install *i;
+
+ assert( sbiod != NULL );
+
+ i = (struct sb_sasl_generic_install *)arg;
+
+ p = LBER_MALLOC( sizeof( *p ) );
+ if ( p == NULL )
+ return -1;
+ p->ops = i->ops;
+ p->ops_private = i->ops_private;
+ p->sbiod = sbiod;
+ p->flags = 0;
+ ber_pvt_sb_buf_init( &p->sec_buf_in );
+ ber_pvt_sb_buf_init( &p->buf_in );
+ ber_pvt_sb_buf_init( &p->buf_out );
+
+ sbiod->sbiod_pvt = p;
+
+ p->ops->init( p, &p->min_send, &p->max_send, &p->max_recv );
+
+ if ( ber_pvt_sb_grow_buffer( &p->sec_buf_in, p->min_send ) < 0 ) {
+ sb_sasl_generic_remove( sbiod );
+ sock_errset(ENOMEM);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+sb_sasl_generic_remove( Sockbuf_IO_Desc *sbiod )
+{
+ struct sb_sasl_generic_data *p;
+
+ assert( sbiod != NULL );
+
+ p = (struct sb_sasl_generic_data *)sbiod->sbiod_pvt;
+
+ p->ops->fini(p);
+
+ ber_pvt_sb_buf_destroy( &p->sec_buf_in );
+ ber_pvt_sb_buf_destroy( &p->buf_in );
+ ber_pvt_sb_buf_destroy( &p->buf_out );
+ LBER_FREE( p );
+ sbiod->sbiod_pvt = NULL;
+ return 0;
+}
+
+static ber_len_t
+sb_sasl_generic_pkt_length(
+ struct sb_sasl_generic_data *p,
+ const unsigned char *buf,
+ int debuglevel )
+{
+ ber_len_t size;
+
+ assert( buf != NULL );
+
+ size = buf[0] << 24
+ | buf[1] << 16
+ | buf[2] << 8
+ | buf[3];
+
+ if ( size > p->max_recv ) {
+ /* somebody is trying to mess me up. */
+ ber_log_printf( LDAP_DEBUG_ANY, debuglevel,
+ "sb_sasl_generic_pkt_length: "
+ "received illegal packet length of %lu bytes\n",
+ (unsigned long)size );
+ size = 16; /* this should lead to an error. */
+ }
+
+ return size + 4; /* include the size !!! */
+}
+
+/* Drop a processed packet from the input buffer */
+static void
+sb_sasl_generic_drop_packet (
+ struct sb_sasl_generic_data *p,
+ int debuglevel )
+{
+ ber_slen_t len;
+
+ len = p->sec_buf_in.buf_ptr - p->sec_buf_in.buf_end;
+ if ( len > 0 )
+ AC_MEMCPY( p->sec_buf_in.buf_base, p->sec_buf_in.buf_base +
+ p->sec_buf_in.buf_end, len );
+
+ if ( len >= 4 ) {
+ p->sec_buf_in.buf_end = sb_sasl_generic_pkt_length(p,
+ (unsigned char *) p->sec_buf_in.buf_base, debuglevel);
+ }
+ else {
+ p->sec_buf_in.buf_end = 0;
+ }
+ p->sec_buf_in.buf_ptr = len;
+}
+
+static ber_slen_t
+sb_sasl_generic_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
+{
+ struct sb_sasl_generic_data *p;
+ ber_slen_t ret, bufptr;
+
+ assert( sbiod != NULL );
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+ p = (struct sb_sasl_generic_data *)sbiod->sbiod_pvt;
+
+ /* Are there anything left in the buffer? */
+ ret = ber_pvt_sb_copy_out( &p->buf_in, buf, len );
+ bufptr = ret;
+ len -= ret;
+
+ if ( len == 0 )
+ return bufptr;
+
+ p->ops->reset_buf( p, &p->buf_in );
+
+ /* Read the length of the packet */
+ while ( p->sec_buf_in.buf_ptr < 4 ) {
+ ret = LBER_SBIOD_READ_NEXT( sbiod, p->sec_buf_in.buf_base +
+ p->sec_buf_in.buf_ptr,
+ 4 - p->sec_buf_in.buf_ptr );
+#ifdef EINTR
+ if ( ( ret < 0 ) && ( errno == EINTR ) )
+ continue;
+#endif
+ if ( ret <= 0 )
+ return bufptr ? bufptr : ret;
+
+ p->sec_buf_in.buf_ptr += ret;
+ }
+
+ /* The new packet always starts at p->sec_buf_in.buf_base */
+ ret = sb_sasl_generic_pkt_length(p, (unsigned char *) p->sec_buf_in.buf_base,
+ sbiod->sbiod_sb->sb_debug );
+
+ /* Grow the packet buffer if necessary */
+ if ( ( p->sec_buf_in.buf_size < (ber_len_t) ret ) &&
+ ber_pvt_sb_grow_buffer( &p->sec_buf_in, ret ) < 0 )
+ {
+ sock_errset(ENOMEM);
+ return -1;
+ }
+ p->sec_buf_in.buf_end = ret;
+
+ /* Did we read the whole encrypted packet? */
+ while ( p->sec_buf_in.buf_ptr < p->sec_buf_in.buf_end ) {
+ /* No, we have got only a part of it */
+ ret = p->sec_buf_in.buf_end - p->sec_buf_in.buf_ptr;
+
+ ret = LBER_SBIOD_READ_NEXT( sbiod, p->sec_buf_in.buf_base +
+ p->sec_buf_in.buf_ptr, ret );
+#ifdef EINTR
+ if ( ( ret < 0 ) && ( errno == EINTR ) )
+ continue;
+#endif
+ if ( ret <= 0 )
+ return bufptr ? bufptr : ret;
+
+ p->sec_buf_in.buf_ptr += ret;
+ }
+
+ /* Decode the packet */
+ ret = p->ops->decode( p, &p->sec_buf_in, &p->buf_in );
+
+ /* Drop the packet from the input buffer */
+ sb_sasl_generic_drop_packet( p, sbiod->sbiod_sb->sb_debug );
+
+ if ( ret != 0 ) {
+ ber_log_printf( LDAP_DEBUG_ANY, sbiod->sbiod_sb->sb_debug,
+ "sb_sasl_generic_read: failed to decode packet\n" );
+ sock_errset(EIO);
+ return -1;
+ }
+
+ bufptr += ber_pvt_sb_copy_out( &p->buf_in, (char*) buf + bufptr, len );
+
+ return bufptr;
+}
+
+static ber_slen_t
+sb_sasl_generic_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
+{
+ struct sb_sasl_generic_data *p;
+ int ret;
+ ber_len_t len2;
+
+ assert( sbiod != NULL );
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+ p = (struct sb_sasl_generic_data *)sbiod->sbiod_pvt;
+
+ /* Is there anything left in the buffer? */
+ if ( p->buf_out.buf_ptr != p->buf_out.buf_end ) {
+ ret = ber_pvt_sb_do_write( sbiod, &p->buf_out );
+ if ( ret < 0 ) return ret;
+
+ /* Still have something left?? */
+ if ( p->buf_out.buf_ptr != p->buf_out.buf_end ) {
+ sock_errset(EAGAIN);
+ return -1;
+ }
+ }
+
+ len2 = p->max_send - 100; /* For safety margin */
+ len2 = len > len2 ? len2 : len;
+
+ /* If we're just retrying a partial write, tell the
+ * caller it's done. Let them call again if there's
+ * still more left to write.
+ */
+ if ( p->flags & LDAP_PVT_SASL_PARTIAL_WRITE ) {
+ p->flags ^= LDAP_PVT_SASL_PARTIAL_WRITE;
+ return len2;
+ }
+
+ /* now encode the next packet. */
+ p->ops->reset_buf( p, &p->buf_out );
+
+ ret = p->ops->encode( p, buf, len2, &p->buf_out );
+
+ if ( ret != 0 ) {
+ ber_log_printf( LDAP_DEBUG_ANY, sbiod->sbiod_sb->sb_debug,
+ "sb_sasl_generic_write: failed to encode packet\n" );
+ sock_errset(EIO);
+ return -1;
+ }
+
+ ret = ber_pvt_sb_do_write( sbiod, &p->buf_out );
+
+ if ( ret < 0 ) {
+ /* error? */
+ int err = sock_errno();
+ /* caller can retry this */
+ if ( err == EAGAIN || err == EWOULDBLOCK || err == EINTR )
+ p->flags |= LDAP_PVT_SASL_PARTIAL_WRITE;
+ return ret;
+ } else if ( p->buf_out.buf_ptr != p->buf_out.buf_end ) {
+ /* partial write? pretend nothing got written */
+ p->flags |= LDAP_PVT_SASL_PARTIAL_WRITE;
+ sock_errset(EAGAIN);
+ len2 = -1;
+ }
+
+ /* return number of bytes encoded, not written, to ensure
+ * no byte is encoded twice (even if only sent once).
+ */
+ return len2;
+}
+
+static int
+sb_sasl_generic_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
+{
+ struct sb_sasl_generic_data *p;
+
+ p = (struct sb_sasl_generic_data *)sbiod->sbiod_pvt;
+
+ if ( opt == LBER_SB_OPT_DATA_READY ) {
+ if ( p->buf_in.buf_ptr != p->buf_in.buf_end ) return 1;
+ }
+
+ return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg );
+}
+
+Sockbuf_IO ldap_pvt_sockbuf_io_sasl_generic = {
+ sb_sasl_generic_setup, /* sbi_setup */
+ sb_sasl_generic_remove, /* sbi_remove */
+ sb_sasl_generic_ctrl, /* sbi_ctrl */
+ sb_sasl_generic_read, /* sbi_read */
+ sb_sasl_generic_write, /* sbi_write */
+ NULL /* sbi_close */
+};
+
+int ldap_pvt_sasl_generic_install(
+ Sockbuf *sb,
+ struct sb_sasl_generic_install *install_arg )
+{
+ Debug0( LDAP_DEBUG_TRACE, "ldap_pvt_sasl_generic_install\n" );
+
+ /* don't install the stuff unless security has been negotiated */
+
+ if ( !ber_sockbuf_ctrl( sb, LBER_SB_OPT_HAS_IO,
+ &ldap_pvt_sockbuf_io_sasl_generic ) )
+ {
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_APPLICATION, (void *)"sasl_generic_" );
+#endif
+ ber_sockbuf_add_io( sb, &ldap_pvt_sockbuf_io_sasl_generic,
+ LBER_SBIOD_LEVEL_APPLICATION, install_arg );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+void ldap_pvt_sasl_generic_remove( Sockbuf *sb )
+{
+ ber_sockbuf_remove_io( sb, &ldap_pvt_sockbuf_io_sasl_generic,
+ LBER_SBIOD_LEVEL_APPLICATION );
+#ifdef LDAP_DEBUG
+ ber_sockbuf_remove_io( sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_APPLICATION );
+#endif
+}
diff --git a/libraries/libldap/sbind.c b/libraries/libldap/sbind.c
new file mode 100644
index 0000000..420ac6a
--- /dev/null
+++ b/libraries/libldap/sbind.c
@@ -0,0 +1,115 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1993 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+/*
+ * BindRequest ::= SEQUENCE {
+ * version INTEGER,
+ * name DistinguishedName, -- who
+ * authentication CHOICE {
+ * simple [0] OCTET STRING -- passwd
+ * krbv42ldap [1] OCTET STRING -- OBSOLETE
+ * krbv42dsa [2] OCTET STRING -- OBSOLETE
+ * sasl [3] SaslCredentials -- LDAPv3
+ * }
+ * }
+ *
+ * BindResponse ::= SEQUENCE {
+ * COMPONENTS OF LDAPResult,
+ * serverSaslCreds OCTET STRING OPTIONAL -- LDAPv3
+ * }
+ *
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/*
+ * ldap_simple_bind - bind to the ldap server (and X.500). The dn and
+ * password of the entry to which to bind are supplied. The message id
+ * of the request initiated is returned.
+ *
+ * Example:
+ * ldap_simple_bind( ld, "cn=manager, o=university of michigan, c=us",
+ * "secret" )
+ */
+
+int
+ldap_simple_bind(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *passwd )
+{
+ int rc;
+ int msgid;
+ struct berval cred;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_simple_bind\n" );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ if ( passwd != NULL ) {
+ cred.bv_val = (char *) passwd;
+ cred.bv_len = strlen( passwd );
+ } else {
+ cred.bv_val = "";
+ cred.bv_len = 0;
+ }
+
+ rc = ldap_sasl_bind( ld, dn, LDAP_SASL_SIMPLE, &cred,
+ NULL, NULL, &msgid );
+
+ return rc == LDAP_SUCCESS ? msgid : -1;
+}
+
+/*
+ * ldap_simple_bind - bind to the ldap server (and X.500) using simple
+ * authentication. The dn and password of the entry to which to bind are
+ * supplied. LDAP_SUCCESS is returned upon success, the ldap error code
+ * otherwise.
+ *
+ * Example:
+ * ldap_simple_bind_s( ld, "cn=manager, o=university of michigan, c=us",
+ * "secret" )
+ */
+
+int
+ldap_simple_bind_s( LDAP *ld, LDAP_CONST char *dn, LDAP_CONST char *passwd )
+{
+ struct berval cred;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_simple_bind_s\n" );
+
+ if ( passwd != NULL ) {
+ cred.bv_val = (char *) passwd;
+ cred.bv_len = strlen( passwd );
+ } else {
+ cred.bv_val = "";
+ cred.bv_len = 0;
+ }
+
+ return ldap_sasl_bind_s( ld, dn, LDAP_SASL_SIMPLE, &cred,
+ NULL, NULL, NULL );
+}
diff --git a/libraries/libldap/schema.c b/libraries/libldap/schema.c
new file mode 100644
index 0000000..a4b89fa
--- /dev/null
+++ b/libraries/libldap/schema.c
@@ -0,0 +1,3400 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * schema.c: parsing routines used by servers and clients to process
+ * schema definitions
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#include <ldap_schema.h>
+
+static const char EndOfInput[] = "end of input";
+
+static const char *
+choose_name( char *names[], const char *fallback )
+{
+ return (names != NULL && names[0] != NULL) ? names[0] : fallback;
+}
+
+LDAP_CONST char *
+ldap_syntax2name( LDAPSyntax * syn )
+{
+ if (!syn) return NULL;
+ return( syn->syn_oid );
+}
+
+LDAP_CONST char *
+ldap_matchingrule2name( LDAPMatchingRule * mr )
+{
+ if (!mr) return NULL;
+ return( choose_name( mr->mr_names, mr->mr_oid ) );
+}
+
+LDAP_CONST char *
+ldap_matchingruleuse2name( LDAPMatchingRuleUse * mru )
+{
+ if (!mru) return NULL;
+ return( choose_name( mru->mru_names, mru->mru_oid ) );
+}
+
+LDAP_CONST char *
+ldap_attributetype2name( LDAPAttributeType * at )
+{
+ if (!at) return NULL;
+ return( choose_name( at->at_names, at->at_oid ) );
+}
+
+LDAP_CONST char *
+ldap_objectclass2name( LDAPObjectClass * oc )
+{
+ if (!oc) return NULL;
+ return( choose_name( oc->oc_names, oc->oc_oid ) );
+}
+
+LDAP_CONST char *
+ldap_contentrule2name( LDAPContentRule * cr )
+{
+ if (!cr) return NULL;
+ return( choose_name( cr->cr_names, cr->cr_oid ) );
+}
+
+LDAP_CONST char *
+ldap_nameform2name( LDAPNameForm * nf )
+{
+ if (!nf) return NULL;
+ return( choose_name( nf->nf_names, nf->nf_oid ) );
+}
+
+LDAP_CONST char *
+ldap_structurerule2name( LDAPStructureRule * sr )
+{
+ if (!sr) return NULL;
+ return( choose_name( sr->sr_names, NULL ) );
+}
+
+/*
+ * When pretty printing the entities we will be appending to a buffer.
+ * Since checking for overflow, realloc'ing and checking if no error
+ * is extremely boring, we will use a protection layer that will let
+ * us blissfully ignore the error until the end. This layer is
+ * implemented with the help of the next type.
+ */
+
+typedef struct safe_string {
+ char * val;
+ ber_len_t size;
+ ber_len_t pos;
+ int at_whsp;
+} safe_string;
+
+static safe_string *
+new_safe_string(int size)
+{
+ safe_string * ss;
+
+ ss = LDAP_MALLOC(sizeof(safe_string));
+ if ( !ss )
+ return(NULL);
+
+ ss->val = LDAP_MALLOC(size);
+ if ( !ss->val ) {
+ LDAP_FREE(ss);
+ return(NULL);
+ }
+
+ ss->size = size;
+ ss->pos = 0;
+ ss->at_whsp = 0;
+
+ return ss;
+}
+
+static void
+safe_string_free(safe_string * ss)
+{
+ if ( !ss )
+ return;
+ LDAP_FREE(ss->val);
+ LDAP_FREE(ss);
+}
+
+#if 0 /* unused */
+static char *
+safe_string_val(safe_string * ss)
+{
+ ss->val[ss->pos] = '\0';
+ return(ss->val);
+}
+#endif
+
+static char *
+safe_strdup(safe_string * ss)
+{
+ char *ret = LDAP_MALLOC(ss->pos+1);
+ if (!ret)
+ return NULL;
+ AC_MEMCPY(ret, ss->val, ss->pos);
+ ret[ss->pos] = '\0';
+ return ret;
+}
+
+static int
+append_to_safe_string(safe_string * ss, char * s)
+{
+ int l = strlen(s);
+ char * temp;
+
+ /*
+ * Some runaway process is trying to append to a string that
+ * overflowed and we could not extend.
+ */
+ if ( !ss->val )
+ return -1;
+
+ /* We always make sure there is at least one position available */
+ if ( ss->pos + l >= ss->size-1 ) {
+ ss->size *= 2;
+ if ( ss->pos + l >= ss->size-1 ) {
+ ss->size = ss->pos + l + 1;
+ }
+
+ temp = LDAP_REALLOC(ss->val, ss->size);
+ if ( !temp ) {
+ /* Trouble, out of memory */
+ LDAP_FREE(ss->val);
+ return -1;
+ }
+ ss->val = temp;
+ }
+ strncpy(&ss->val[ss->pos], s, l);
+ ss->pos += l;
+ if ( ss->pos > 0 && LDAP_SPACE(ss->val[ss->pos-1]) )
+ ss->at_whsp = 1;
+ else
+ ss->at_whsp = 0;
+
+ return 0;
+}
+
+static int
+print_literal(safe_string *ss, char *s)
+{
+ return(append_to_safe_string(ss,s));
+}
+
+static int
+print_whsp(safe_string *ss)
+{
+ if ( ss->at_whsp )
+ return(append_to_safe_string(ss,""));
+ else
+ return(append_to_safe_string(ss," "));
+}
+
+static int
+print_numericoid(safe_string *ss, char *s)
+{
+ if ( s )
+ return(append_to_safe_string(ss,s));
+ else
+ return(append_to_safe_string(ss,""));
+}
+
+/* This one is identical to print_qdescr */
+static int
+print_qdstring(safe_string *ss, char *s)
+{
+ print_whsp(ss);
+ print_literal(ss,"'");
+ append_to_safe_string(ss,s);
+ print_literal(ss,"'");
+ return(print_whsp(ss));
+}
+
+static int
+print_qdescr(safe_string *ss, char *s)
+{
+ print_whsp(ss);
+ print_literal(ss,"'");
+ append_to_safe_string(ss,s);
+ print_literal(ss,"'");
+ return(print_whsp(ss));
+}
+
+static int
+print_qdescrlist(safe_string *ss, char **sa)
+{
+ char **sp;
+ int ret = 0;
+
+ for (sp=sa; *sp; sp++) {
+ ret = print_qdescr(ss,*sp);
+ }
+ /* If the list was empty, we return zero that is potentially
+ * incorrect, but since we will be still appending things, the
+ * overflow will be detected later. Maybe FIX.
+ */
+ return(ret);
+}
+
+static int
+print_qdescrs(safe_string *ss, char **sa)
+{
+ /* The only way to represent an empty list is as a qdescrlist
+ * so, if the list is empty we treat it as a long list.
+ * Really, this is what the syntax mandates. We should not
+ * be here if the list was empty, but if it happens, a label
+ * has already been output and we cannot undo it.
+ */
+ if ( !sa[0] || ( sa[0] && sa[1] ) ) {
+ print_whsp(ss);
+ print_literal(ss,"("/*)*/);
+ print_qdescrlist(ss,sa);
+ print_literal(ss,/*(*/")");
+ return(print_whsp(ss));
+ } else {
+ return(print_qdescr(ss,*sa));
+ }
+}
+
+static int
+print_woid(safe_string *ss, char *s)
+{
+ print_whsp(ss);
+ append_to_safe_string(ss,s);
+ return print_whsp(ss);
+}
+
+static int
+print_oidlist(safe_string *ss, char **sa)
+{
+ char **sp;
+
+ for (sp=sa; *(sp+1); sp++) {
+ print_woid(ss,*sp);
+ print_literal(ss,"$");
+ }
+ return(print_woid(ss,*sp));
+}
+
+static int
+print_oids(safe_string *ss, char **sa)
+{
+ if ( sa[0] && sa[1] ) {
+ print_literal(ss,"("/*)*/);
+ print_oidlist(ss,sa);
+ print_whsp(ss);
+ return(print_literal(ss,/*(*/")"));
+ } else {
+ return(print_woid(ss,*sa));
+ }
+}
+
+static int
+print_noidlen(safe_string *ss, char *s, int l)
+{
+ char buf[64];
+ int ret;
+
+ ret = print_numericoid(ss,s);
+ if ( l ) {
+ snprintf(buf, sizeof buf, "{%d}",l);
+ ret = print_literal(ss,buf);
+ }
+ return(ret);
+}
+
+static int
+print_ruleid(safe_string *ss, int rid)
+{
+ char buf[64];
+ snprintf(buf, sizeof buf, "%d", rid);
+ return print_literal(ss,buf);
+}
+
+static int
+print_ruleids(safe_string *ss, int n, int *rids)
+{
+ int i;
+
+ if( n == 1 ) {
+ print_ruleid(ss,rids[0]);
+ return print_whsp(ss);
+ } else {
+ print_literal(ss,"("/*)*/);
+ for( i=0; i<n; i++ ) {
+ print_whsp(ss);
+ print_ruleid(ss,rids[i]);
+ }
+ print_whsp(ss);
+ return print_literal(ss,/*(*/")");
+ }
+}
+
+
+static int
+print_extensions(safe_string *ss, LDAPSchemaExtensionItem **extensions)
+{
+ LDAPSchemaExtensionItem **ext;
+
+ if ( extensions ) {
+ print_whsp(ss);
+ for ( ext = extensions; *ext != NULL; ext++ ) {
+ print_literal(ss, (*ext)->lsei_name);
+ print_whsp(ss);
+ /* Should be print_qdstrings */
+ print_qdescrs(ss, (*ext)->lsei_values);
+ print_whsp(ss);
+ }
+ }
+
+ return 0;
+}
+
+char *
+ldap_syntax2str( LDAPSyntax * syn )
+{
+ struct berval bv;
+ if (ldap_syntax2bv( syn, &bv ))
+ return(bv.bv_val);
+ else
+ return NULL;
+}
+
+struct berval *
+ldap_syntax2bv( LDAPSyntax * syn, struct berval *bv )
+{
+ safe_string * ss;
+
+ if ( !syn || !bv )
+ return NULL;
+
+ ss = new_safe_string(256);
+ if ( !ss )
+ return NULL;
+
+ print_literal(ss,"("/*)*/);
+ print_whsp(ss);
+
+ print_numericoid(ss, syn->syn_oid);
+ print_whsp(ss);
+
+ if ( syn->syn_desc ) {
+ print_literal(ss,"DESC");
+ print_qdstring(ss,syn->syn_desc);
+ }
+
+ print_whsp(ss);
+
+ print_extensions(ss, syn->syn_extensions);
+
+ print_literal(ss,/*(*/ ")");
+
+ bv->bv_val = safe_strdup(ss);
+ bv->bv_len = ss->pos;
+ safe_string_free(ss);
+ return(bv);
+}
+
+char *
+ldap_matchingrule2str( LDAPMatchingRule * mr )
+{
+ struct berval bv;
+ if (ldap_matchingrule2bv( mr, &bv ))
+ return(bv.bv_val);
+ else
+ return NULL;
+}
+
+struct berval *
+ldap_matchingrule2bv( LDAPMatchingRule * mr, struct berval *bv )
+{
+ safe_string * ss;
+
+ if ( !mr || !bv )
+ return NULL;
+
+ ss = new_safe_string(256);
+ if ( !ss )
+ return NULL;
+
+ print_literal(ss,"(" /*)*/);
+ print_whsp(ss);
+
+ print_numericoid(ss, mr->mr_oid);
+ print_whsp(ss);
+
+ if ( mr->mr_names ) {
+ print_literal(ss,"NAME");
+ print_qdescrs(ss,mr->mr_names);
+ }
+
+ if ( mr->mr_desc ) {
+ print_literal(ss,"DESC");
+ print_qdstring(ss,mr->mr_desc);
+ }
+
+ if ( mr->mr_obsolete ) {
+ print_literal(ss, "OBSOLETE");
+ print_whsp(ss);
+ }
+
+ if ( mr->mr_syntax_oid ) {
+ print_literal(ss,"SYNTAX");
+ print_whsp(ss);
+ print_literal(ss, mr->mr_syntax_oid);
+ print_whsp(ss);
+ }
+
+ print_whsp(ss);
+
+ print_extensions(ss, mr->mr_extensions);
+
+ print_literal(ss,/*(*/")");
+
+ bv->bv_val = safe_strdup(ss);
+ bv->bv_len = ss->pos;
+ safe_string_free(ss);
+ return(bv);
+}
+
+char *
+ldap_matchingruleuse2str( LDAPMatchingRuleUse * mru )
+{
+ struct berval bv;
+ if (ldap_matchingruleuse2bv( mru, &bv ))
+ return(bv.bv_val);
+ else
+ return NULL;
+}
+
+struct berval *
+ldap_matchingruleuse2bv( LDAPMatchingRuleUse * mru, struct berval *bv )
+{
+ safe_string * ss;
+
+ if ( !mru || !bv )
+ return NULL;
+
+ ss = new_safe_string(256);
+ if ( !ss )
+ return NULL;
+
+ print_literal(ss,"(" /*)*/);
+ print_whsp(ss);
+
+ print_numericoid(ss, mru->mru_oid);
+ print_whsp(ss);
+
+ if ( mru->mru_names ) {
+ print_literal(ss,"NAME");
+ print_qdescrs(ss,mru->mru_names);
+ }
+
+ if ( mru->mru_desc ) {
+ print_literal(ss,"DESC");
+ print_qdstring(ss,mru->mru_desc);
+ }
+
+ if ( mru->mru_obsolete ) {
+ print_literal(ss, "OBSOLETE");
+ print_whsp(ss);
+ }
+
+ if ( mru->mru_applies_oids ) {
+ print_literal(ss,"APPLIES");
+ print_whsp(ss);
+ print_oids(ss, mru->mru_applies_oids);
+ print_whsp(ss);
+ }
+
+ print_whsp(ss);
+
+ print_extensions(ss, mru->mru_extensions);
+
+ print_literal(ss,/*(*/")");
+
+ bv->bv_val = safe_strdup(ss);
+ bv->bv_len = ss->pos;
+ safe_string_free(ss);
+ return(bv);
+}
+
+char *
+ldap_objectclass2str( LDAPObjectClass * oc )
+{
+ struct berval bv;
+ if (ldap_objectclass2bv( oc, &bv ))
+ return(bv.bv_val);
+ else
+ return NULL;
+}
+
+struct berval *
+ldap_objectclass2bv( LDAPObjectClass * oc, struct berval *bv )
+{
+ safe_string * ss;
+
+ if ( !oc || !bv )
+ return NULL;
+
+ ss = new_safe_string(256);
+ if ( !ss )
+ return NULL;
+
+ print_literal(ss,"("/*)*/);
+ print_whsp(ss);
+
+ print_numericoid(ss, oc->oc_oid);
+ print_whsp(ss);
+
+ if ( oc->oc_names ) {
+ print_literal(ss,"NAME");
+ print_qdescrs(ss,oc->oc_names);
+ }
+
+ if ( oc->oc_desc ) {
+ print_literal(ss,"DESC");
+ print_qdstring(ss,oc->oc_desc);
+ }
+
+ if ( oc->oc_obsolete ) {
+ print_literal(ss, "OBSOLETE");
+ print_whsp(ss);
+ }
+
+ if ( oc->oc_sup_oids ) {
+ print_literal(ss,"SUP");
+ print_whsp(ss);
+ print_oids(ss,oc->oc_sup_oids);
+ print_whsp(ss);
+ }
+
+ switch (oc->oc_kind) {
+ case LDAP_SCHEMA_ABSTRACT:
+ print_literal(ss,"ABSTRACT");
+ break;
+ case LDAP_SCHEMA_STRUCTURAL:
+ print_literal(ss,"STRUCTURAL");
+ break;
+ case LDAP_SCHEMA_AUXILIARY:
+ print_literal(ss,"AUXILIARY");
+ break;
+ default:
+ print_literal(ss,"KIND-UNKNOWN");
+ break;
+ }
+ print_whsp(ss);
+
+ if ( oc->oc_at_oids_must ) {
+ print_literal(ss,"MUST");
+ print_whsp(ss);
+ print_oids(ss,oc->oc_at_oids_must);
+ print_whsp(ss);
+ }
+
+ if ( oc->oc_at_oids_may ) {
+ print_literal(ss,"MAY");
+ print_whsp(ss);
+ print_oids(ss,oc->oc_at_oids_may);
+ print_whsp(ss);
+ }
+
+ print_whsp(ss);
+
+ print_extensions(ss, oc->oc_extensions);
+
+ print_literal(ss, /*(*/")");
+
+ bv->bv_val = safe_strdup(ss);
+ bv->bv_len = ss->pos;
+ safe_string_free(ss);
+ return(bv);
+}
+
+char *
+ldap_contentrule2str( LDAPContentRule * cr )
+{
+ struct berval bv;
+ if (ldap_contentrule2bv( cr, &bv ))
+ return(bv.bv_val);
+ else
+ return NULL;
+}
+
+struct berval *
+ldap_contentrule2bv( LDAPContentRule * cr, struct berval *bv )
+{
+ safe_string * ss;
+
+ if ( !cr || !bv )
+ return NULL;
+
+ ss = new_safe_string(256);
+ if ( !ss )
+ return NULL;
+
+ print_literal(ss,"("/*)*/);
+ print_whsp(ss);
+
+ print_numericoid(ss, cr->cr_oid);
+ print_whsp(ss);
+
+ if ( cr->cr_names ) {
+ print_literal(ss,"NAME");
+ print_qdescrs(ss,cr->cr_names);
+ }
+
+ if ( cr->cr_desc ) {
+ print_literal(ss,"DESC");
+ print_qdstring(ss,cr->cr_desc);
+ }
+
+ if ( cr->cr_obsolete ) {
+ print_literal(ss, "OBSOLETE");
+ print_whsp(ss);
+ }
+
+ if ( cr->cr_oc_oids_aux ) {
+ print_literal(ss,"AUX");
+ print_whsp(ss);
+ print_oids(ss,cr->cr_oc_oids_aux);
+ print_whsp(ss);
+ }
+
+ if ( cr->cr_at_oids_must ) {
+ print_literal(ss,"MUST");
+ print_whsp(ss);
+ print_oids(ss,cr->cr_at_oids_must);
+ print_whsp(ss);
+ }
+
+ if ( cr->cr_at_oids_may ) {
+ print_literal(ss,"MAY");
+ print_whsp(ss);
+ print_oids(ss,cr->cr_at_oids_may);
+ print_whsp(ss);
+ }
+
+ if ( cr->cr_at_oids_not ) {
+ print_literal(ss,"NOT");
+ print_whsp(ss);
+ print_oids(ss,cr->cr_at_oids_not);
+ print_whsp(ss);
+ }
+
+ print_whsp(ss);
+ print_extensions(ss, cr->cr_extensions);
+
+ print_literal(ss, /*(*/")");
+
+ bv->bv_val = safe_strdup(ss);
+ bv->bv_len = ss->pos;
+ safe_string_free(ss);
+ return(bv);
+}
+
+char *
+ldap_structurerule2str( LDAPStructureRule * sr )
+{
+ struct berval bv;
+ if (ldap_structurerule2bv( sr, &bv ))
+ return(bv.bv_val);
+ else
+ return NULL;
+}
+
+struct berval *
+ldap_structurerule2bv( LDAPStructureRule * sr, struct berval *bv )
+{
+ safe_string * ss;
+
+ if ( !sr || !bv )
+ return NULL;
+
+ ss = new_safe_string(256);
+ if ( !ss )
+ return NULL;
+
+ print_literal(ss,"("/*)*/);
+ print_whsp(ss);
+
+ print_ruleid(ss, sr->sr_ruleid);
+ print_whsp(ss);
+
+ if ( sr->sr_names ) {
+ print_literal(ss,"NAME");
+ print_qdescrs(ss,sr->sr_names);
+ }
+
+ if ( sr->sr_desc ) {
+ print_literal(ss,"DESC");
+ print_qdstring(ss,sr->sr_desc);
+ }
+
+ if ( sr->sr_obsolete ) {
+ print_literal(ss, "OBSOLETE");
+ print_whsp(ss);
+ }
+
+ print_literal(ss,"FORM");
+ print_whsp(ss);
+ print_woid(ss,sr->sr_nameform);
+ print_whsp(ss);
+
+ if ( sr->sr_nsup_ruleids ) {
+ print_literal(ss,"SUP");
+ print_whsp(ss);
+ print_ruleids(ss,sr->sr_nsup_ruleids,sr->sr_sup_ruleids);
+ print_whsp(ss);
+ }
+
+ print_whsp(ss);
+ print_extensions(ss, sr->sr_extensions);
+
+ print_literal(ss, /*(*/")");
+
+ bv->bv_val = safe_strdup(ss);
+ bv->bv_len = ss->pos;
+ safe_string_free(ss);
+ return(bv);
+}
+
+
+char *
+ldap_nameform2str( LDAPNameForm * nf )
+{
+ struct berval bv;
+ if (ldap_nameform2bv( nf, &bv ))
+ return(bv.bv_val);
+ else
+ return NULL;
+}
+
+struct berval *
+ldap_nameform2bv( LDAPNameForm * nf, struct berval *bv )
+{
+ safe_string * ss;
+
+ if ( !nf || !bv )
+ return NULL;
+
+ ss = new_safe_string(256);
+ if ( !ss )
+ return NULL;
+
+ print_literal(ss,"("/*)*/);
+ print_whsp(ss);
+
+ print_numericoid(ss, nf->nf_oid);
+ print_whsp(ss);
+
+ if ( nf->nf_names ) {
+ print_literal(ss,"NAME");
+ print_qdescrs(ss,nf->nf_names);
+ }
+
+ if ( nf->nf_desc ) {
+ print_literal(ss,"DESC");
+ print_qdstring(ss,nf->nf_desc);
+ }
+
+ if ( nf->nf_obsolete ) {
+ print_literal(ss, "OBSOLETE");
+ print_whsp(ss);
+ }
+
+ print_literal(ss,"OC");
+ print_whsp(ss);
+ print_woid(ss,nf->nf_objectclass);
+ print_whsp(ss);
+
+ print_literal(ss,"MUST");
+ print_whsp(ss);
+ print_oids(ss,nf->nf_at_oids_must);
+ print_whsp(ss);
+
+
+ if ( nf->nf_at_oids_may ) {
+ print_literal(ss,"MAY");
+ print_whsp(ss);
+ print_oids(ss,nf->nf_at_oids_may);
+ print_whsp(ss);
+ }
+
+ print_whsp(ss);
+ print_extensions(ss, nf->nf_extensions);
+
+ print_literal(ss, /*(*/")");
+
+ bv->bv_val = safe_strdup(ss);
+ bv->bv_len = ss->pos;
+ safe_string_free(ss);
+ return(bv);
+}
+
+char *
+ldap_attributetype2str( LDAPAttributeType * at )
+{
+ struct berval bv;
+ if (ldap_attributetype2bv( at, &bv ))
+ return(bv.bv_val);
+ else
+ return NULL;
+}
+
+struct berval *
+ldap_attributetype2bv( LDAPAttributeType * at, struct berval *bv )
+{
+ safe_string * ss;
+
+ if ( !at || !bv )
+ return NULL;
+
+ ss = new_safe_string(256);
+ if ( !ss )
+ return NULL;
+
+ print_literal(ss,"("/*)*/);
+ print_whsp(ss);
+
+ print_numericoid(ss, at->at_oid);
+ print_whsp(ss);
+
+ if ( at->at_names ) {
+ print_literal(ss,"NAME");
+ print_qdescrs(ss,at->at_names);
+ }
+
+ if ( at->at_desc ) {
+ print_literal(ss,"DESC");
+ print_qdstring(ss,at->at_desc);
+ }
+
+ if ( at->at_obsolete ) {
+ print_literal(ss, "OBSOLETE");
+ print_whsp(ss);
+ }
+
+ if ( at->at_sup_oid ) {
+ print_literal(ss,"SUP");
+ print_woid(ss,at->at_sup_oid);
+ }
+
+ if ( at->at_equality_oid ) {
+ print_literal(ss,"EQUALITY");
+ print_woid(ss,at->at_equality_oid);
+ }
+
+ if ( at->at_ordering_oid ) {
+ print_literal(ss,"ORDERING");
+ print_woid(ss,at->at_ordering_oid);
+ }
+
+ if ( at->at_substr_oid ) {
+ print_literal(ss,"SUBSTR");
+ print_woid(ss,at->at_substr_oid);
+ }
+
+ if ( at->at_syntax_oid ) {
+ print_literal(ss,"SYNTAX");
+ print_whsp(ss);
+ print_noidlen(ss,at->at_syntax_oid,at->at_syntax_len);
+ print_whsp(ss);
+ }
+
+ if ( at->at_single_value == LDAP_SCHEMA_YES ) {
+ print_literal(ss,"SINGLE-VALUE");
+ print_whsp(ss);
+ }
+
+ if ( at->at_collective == LDAP_SCHEMA_YES ) {
+ print_literal(ss,"COLLECTIVE");
+ print_whsp(ss);
+ }
+
+ if ( at->at_no_user_mod == LDAP_SCHEMA_YES ) {
+ print_literal(ss,"NO-USER-MODIFICATION");
+ print_whsp(ss);
+ }
+
+ if ( at->at_usage != LDAP_SCHEMA_USER_APPLICATIONS ) {
+ print_literal(ss,"USAGE");
+ print_whsp(ss);
+ switch (at->at_usage) {
+ case LDAP_SCHEMA_DIRECTORY_OPERATION:
+ print_literal(ss,"directoryOperation");
+ break;
+ case LDAP_SCHEMA_DISTRIBUTED_OPERATION:
+ print_literal(ss,"distributedOperation");
+ break;
+ case LDAP_SCHEMA_DSA_OPERATION:
+ print_literal(ss,"dSAOperation");
+ break;
+ default:
+ print_literal(ss,"UNKNOWN");
+ break;
+ }
+ }
+
+ print_whsp(ss);
+
+ print_extensions(ss, at->at_extensions);
+
+ print_literal(ss,/*(*/")");
+
+ bv->bv_val = safe_strdup(ss);
+ bv->bv_len = ss->pos;
+ safe_string_free(ss);
+ return(bv);
+}
+
+/*
+ * Now come the parsers. There is one parser for each entity type:
+ * objectclasses, attributetypes, etc.
+ *
+ * Each of them is written as a recursive-descent parser, except that
+ * none of them is really recursive. But the idea is kept: there
+ * is one routine per non-terminal that either gobbles lexical tokens
+ * or calls lower-level routines, etc.
+ *
+ * The scanner is implemented in the routine get_token. Actually,
+ * get_token is more than a scanner and will return tokens that are
+ * in fact non-terminals in the grammar. So you can see the whole
+ * approach as the combination of a low-level bottom-up recognizer
+ * combined with a scanner and a number of top-down parsers. Or just
+ * consider that the real grammars recognized by the parsers are not
+ * those of the standards. As a matter of fact, our parsers are more
+ * liberal than the spec when there is no ambiguity.
+ *
+ * The difference is pretty academic (modulo bugs or incorrect
+ * interpretation of the specs).
+ */
+
+typedef enum tk_t {
+ TK_NOENDQUOTE = -2,
+ TK_OUTOFMEM = -1,
+ TK_EOS = 0,
+ TK_UNEXPCHAR = 1,
+ TK_BAREWORD = 2,
+ TK_QDSTRING = 3,
+ TK_LEFTPAREN = 4,
+ TK_RIGHTPAREN = 5,
+ TK_DOLLAR = 6,
+ TK_QDESCR = TK_QDSTRING
+} tk_t;
+
+static tk_t
+get_token( const char ** sp, char ** token_val )
+{
+ tk_t kind;
+ const char * p;
+ const char * q;
+ char * res;
+
+ *token_val = NULL;
+ switch (**sp) {
+ case '\0':
+ kind = TK_EOS;
+ (*sp)++;
+ break;
+ case '(':
+ kind = TK_LEFTPAREN;
+ (*sp)++;
+ break;
+ case ')':
+ kind = TK_RIGHTPAREN;
+ (*sp)++;
+ break;
+ case '$':
+ kind = TK_DOLLAR;
+ (*sp)++;
+ break;
+ case '\'':
+ kind = TK_QDSTRING;
+ (*sp)++;
+ p = *sp;
+ while ( **sp != '\'' && **sp != '\0' )
+ (*sp)++;
+ if ( **sp == '\'' ) {
+ q = *sp;
+ res = LDAP_MALLOC(q-p+1);
+ if ( !res ) {
+ kind = TK_OUTOFMEM;
+ } else {
+ strncpy(res,p,q-p);
+ res[q-p] = '\0';
+ *token_val = res;
+ }
+ (*sp)++;
+ } else {
+ kind = TK_NOENDQUOTE;
+ }
+ break;
+ default:
+ kind = TK_BAREWORD;
+ p = *sp;
+ while ( !LDAP_SPACE(**sp) &&
+ **sp != '(' &&
+ **sp != ')' &&
+ **sp != '$' &&
+ **sp != '\'' &&
+ /* for suggested minimum upper bound on the number
+ * of characters (RFC 4517) */
+ **sp != '{' &&
+ **sp != '\0' )
+ (*sp)++;
+ q = *sp;
+ res = LDAP_MALLOC(q-p+1);
+ if ( !res ) {
+ kind = TK_OUTOFMEM;
+ } else {
+ strncpy(res,p,q-p);
+ res[q-p] = '\0';
+ *token_val = res;
+ }
+ break;
+/* kind = TK_UNEXPCHAR; */
+/* break; */
+ }
+
+ return kind;
+}
+
+/* Gobble optional whitespace */
+static void
+parse_whsp(const char **sp)
+{
+ while (LDAP_SPACE(**sp))
+ (*sp)++;
+}
+
+/* TBC:!!
+ * General note for all parsers: to guarantee the algorithm halts they
+ * must always advance the pointer even when an error is found. For
+ * this one is not that important since an error here is fatal at the
+ * upper layers, but it is a simple strategy that will not get in
+ * endless loops.
+ */
+
+/* Parse a sequence of dot-separated decimal strings */
+char *
+ldap_int_parse_numericoid(const char **sp, int *code, const int flags)
+{
+ char * res = NULL;
+ const char * start = *sp;
+ int len;
+ int quoted = 0;
+
+ /* Netscape puts the SYNTAX value in quotes (incorrectly) */
+ if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && **sp == '\'' ) {
+ quoted = 1;
+ (*sp)++;
+ start++;
+ }
+ /* Each iteration of this loop gets one decimal string */
+ while (**sp) {
+ if ( !LDAP_DIGIT(**sp) ) {
+ /*
+ * Initial char is not a digit or char after dot is
+ * not a digit
+ */
+ *code = LDAP_SCHERR_NODIGIT;
+ return NULL;
+ }
+ (*sp)++;
+ while ( LDAP_DIGIT(**sp) )
+ (*sp)++;
+ if ( **sp != '.' )
+ break;
+ /* Otherwise, gobble the dot and loop again */
+ (*sp)++;
+ }
+ /* Now *sp points at the char past the numericoid. Perfect. */
+ len = *sp - start;
+ if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && quoted ) {
+ if ( **sp == '\'' ) {
+ (*sp)++;
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ return NULL;
+ }
+ }
+ if (flags & LDAP_SCHEMA_SKIP) {
+ res = (char *)start;
+ } else {
+ res = LDAP_MALLOC(len+1);
+ if (!res) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return(NULL);
+ }
+ strncpy(res,start,len);
+ res[len] = '\0';
+ }
+ return(res);
+}
+
+/* Parse a sequence of dot-separated decimal strings */
+int
+ldap_int_parse_ruleid(const char **sp, int *code, const int flags, int *ruleid)
+{
+ *ruleid=0;
+
+ if ( !LDAP_DIGIT(**sp) ) {
+ *code = LDAP_SCHERR_NODIGIT;
+ return -1;
+ }
+ *ruleid = (**sp) - '0';
+ (*sp)++;
+
+ while ( LDAP_DIGIT(**sp) ) {
+ *ruleid *= 10;
+ *ruleid += (**sp) - '0';
+ (*sp)++;
+ }
+
+ return 0;
+}
+
+/* Parse a qdescr or a list of them enclosed in () */
+static char **
+parse_qdescrs(const char **sp, int *code)
+{
+ char ** res;
+ char ** res1;
+ tk_t kind;
+ char * sval;
+ int size;
+ int pos;
+
+ parse_whsp(sp);
+ kind = get_token(sp,&sval);
+ if ( kind == TK_LEFTPAREN ) {
+ /* Let's presume there will be at least 2 entries */
+ size = 3;
+ res = LDAP_CALLOC(3,sizeof(char *));
+ if ( !res ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+ pos = 0;
+ while (1) {
+ parse_whsp(sp);
+ kind = get_token(sp,&sval);
+ if ( kind == TK_RIGHTPAREN )
+ break;
+ if ( kind == TK_QDESCR ) {
+ if ( pos == size-2 ) {
+ size++;
+ res1 = LDAP_REALLOC(res,size*sizeof(char *));
+ if ( !res1 ) {
+ LDAP_VFREE(res);
+ LDAP_FREE(sval);
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return(NULL);
+ }
+ res = res1;
+ }
+ res[pos++] = sval;
+ res[pos] = NULL;
+ parse_whsp(sp);
+ } else {
+ LDAP_VFREE(res);
+ LDAP_FREE(sval);
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ return(NULL);
+ }
+ }
+ parse_whsp(sp);
+ return(res);
+ } else if ( kind == TK_QDESCR ) {
+ res = LDAP_CALLOC(2,sizeof(char *));
+ if ( !res ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+ res[0] = sval;
+ res[1] = NULL;
+ parse_whsp(sp);
+ return res;
+ } else {
+ LDAP_FREE(sval);
+ *code = LDAP_SCHERR_BADNAME;
+ return NULL;
+ }
+}
+
+/* Parse a woid */
+static char *
+parse_woid(const char **sp, int *code)
+{
+ char * sval;
+ tk_t kind;
+
+ parse_whsp(sp);
+ kind = get_token(sp, &sval);
+ if ( kind != TK_BAREWORD ) {
+ LDAP_FREE(sval);
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ return NULL;
+ }
+ parse_whsp(sp);
+ return sval;
+}
+
+/* Parse a noidlen */
+static char *
+parse_noidlen(const char **sp, int *code, int *len, int flags)
+{
+ char * sval;
+ const char *savepos;
+ int quoted = 0;
+ int allow_quoted = ( flags & LDAP_SCHEMA_ALLOW_QUOTED );
+ int allow_oidmacro = ( flags & LDAP_SCHEMA_ALLOW_OID_MACRO );
+
+ *len = 0;
+ /* Netscape puts the SYNTAX value in quotes (incorrectly) */
+ if ( allow_quoted && **sp == '\'' ) {
+ quoted = 1;
+ (*sp)++;
+ }
+ savepos = *sp;
+ sval = ldap_int_parse_numericoid(sp, code, 0);
+ if ( !sval ) {
+ if ( allow_oidmacro
+ && *sp == savepos
+ && *code == LDAP_SCHERR_NODIGIT )
+ {
+ if ( get_token(sp, &sval) != TK_BAREWORD ) {
+ if ( sval != NULL ) {
+ LDAP_FREE(sval);
+ }
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }
+ }
+ if ( **sp == '{' /*}*/ ) {
+ (*sp)++;
+ *len = atoi(*sp);
+ while ( LDAP_DIGIT(**sp) )
+ (*sp)++;
+ if ( **sp != /*{*/ '}' ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ LDAP_FREE(sval);
+ return NULL;
+ }
+ (*sp)++;
+ }
+ if ( allow_quoted && quoted ) {
+ if ( **sp == '\'' ) {
+ (*sp)++;
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ LDAP_FREE(sval);
+ return NULL;
+ }
+ }
+ return sval;
+}
+
+/*
+ * Next routine will accept a qdstring in place of an oid if
+ * allow_quoted is set. This is necessary to interoperate with
+ * Netscape Directory server that will improperly quote each oid (at
+ * least those of the descr kind) in the SUP clause.
+ */
+
+/* Parse a woid or a $-separated list of them enclosed in () */
+static char **
+parse_oids(const char **sp, int *code, const int allow_quoted)
+{
+ char ** res;
+ char ** res1;
+ tk_t kind;
+ char * sval;
+ int size;
+ int pos;
+
+ /*
+ * Strictly speaking, doing this here accepts whsp before the
+ * ( at the beginning of an oidlist, but this is harmless. Also,
+ * we are very liberal in what we accept as an OID. Maybe
+ * refine later.
+ */
+ parse_whsp(sp);
+ kind = get_token(sp,&sval);
+ if ( kind == TK_LEFTPAREN ) {
+ /* Let's presume there will be at least 2 entries */
+ size = 3;
+ res = LDAP_CALLOC(3,sizeof(char *));
+ if ( !res ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+ pos = 0;
+ parse_whsp(sp);
+ kind = get_token(sp,&sval);
+ if ( kind == TK_BAREWORD ||
+ ( allow_quoted && kind == TK_QDSTRING ) ) {
+ res[pos++] = sval;
+ res[pos] = NULL;
+ } else if ( kind == TK_RIGHTPAREN ) {
+ /* FIXME: be liberal in what we accept... */
+ parse_whsp(sp);
+ LDAP_FREE(res);
+ return NULL;
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ LDAP_FREE(sval);
+ LDAP_VFREE(res);
+ return NULL;
+ }
+ parse_whsp(sp);
+ while (1) {
+ kind = get_token(sp,&sval);
+ if ( kind == TK_RIGHTPAREN )
+ break;
+ if ( kind == TK_DOLLAR ) {
+ parse_whsp(sp);
+ kind = get_token(sp,&sval);
+ if ( kind == TK_BAREWORD ||
+ ( allow_quoted &&
+ kind == TK_QDSTRING ) ) {
+ if ( pos == size-2 ) {
+ size++;
+ res1 = LDAP_REALLOC(res,size*sizeof(char *));
+ if ( !res1 ) {
+ LDAP_FREE(sval);
+ LDAP_VFREE(res);
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return(NULL);
+ }
+ res = res1;
+ }
+ res[pos++] = sval;
+ res[pos] = NULL;
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ LDAP_FREE(sval);
+ LDAP_VFREE(res);
+ return NULL;
+ }
+ parse_whsp(sp);
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ LDAP_FREE(sval);
+ LDAP_VFREE(res);
+ return NULL;
+ }
+ }
+ parse_whsp(sp);
+ return(res);
+ } else if ( kind == TK_BAREWORD ||
+ ( allow_quoted && kind == TK_QDSTRING ) ) {
+ res = LDAP_CALLOC(2,sizeof(char *));
+ if ( !res ) {
+ LDAP_FREE(sval);
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+ res[0] = sval;
+ res[1] = NULL;
+ parse_whsp(sp);
+ return res;
+ } else {
+ LDAP_FREE(sval);
+ *code = LDAP_SCHERR_BADNAME;
+ return NULL;
+ }
+}
+
+static int
+add_extension(LDAPSchemaExtensionItem ***extensions,
+ char * name, char ** values)
+{
+ int n;
+ LDAPSchemaExtensionItem **tmp, *ext;
+
+ ext = LDAP_CALLOC(1, sizeof(LDAPSchemaExtensionItem));
+ if ( !ext )
+ return 1;
+ ext->lsei_name = name;
+ ext->lsei_values = values;
+
+ if ( !*extensions ) {
+ *extensions =
+ LDAP_CALLOC(2, sizeof(LDAPSchemaExtensionItem *));
+ if ( !*extensions ) {
+ LDAP_FREE( ext );
+ return 1;
+ }
+ n = 0;
+ } else {
+ for ( n=0; (*extensions)[n] != NULL; n++ )
+ ;
+ tmp = LDAP_REALLOC(*extensions,
+ (n+2)*sizeof(LDAPSchemaExtensionItem *));
+ if ( !tmp ) {
+ LDAP_FREE( ext );
+ return 1;
+ }
+ *extensions = tmp;
+ }
+ (*extensions)[n] = ext;
+ (*extensions)[n+1] = NULL;
+ return 0;
+}
+
+static void
+free_extensions(LDAPSchemaExtensionItem **extensions)
+{
+ LDAPSchemaExtensionItem **ext;
+
+ if ( extensions ) {
+ for ( ext = extensions; *ext != NULL; ext++ ) {
+ LDAP_FREE((*ext)->lsei_name);
+ LDAP_VFREE((*ext)->lsei_values);
+ LDAP_FREE(*ext);
+ }
+ LDAP_FREE(extensions);
+ }
+}
+
+void
+ldap_syntax_free( LDAPSyntax * syn )
+{
+ if ( !syn ) return;
+ LDAP_FREE(syn->syn_oid);
+ if (syn->syn_names) LDAP_VFREE(syn->syn_names);
+ if (syn->syn_desc) LDAP_FREE(syn->syn_desc);
+ free_extensions(syn->syn_extensions);
+ LDAP_FREE(syn);
+}
+
+LDAPSyntax *
+ldap_str2syntax( LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags )
+{
+ tk_t kind;
+ const char * ss = s;
+ char * sval;
+ int seen_name = 0;
+ int seen_desc = 0;
+ LDAPSyntax * syn;
+ char ** ext_vals;
+
+ if ( !s ) {
+ *code = LDAP_SCHERR_EMPTY;
+ *errp = "";
+ return NULL;
+ }
+
+ *errp = s;
+ syn = LDAP_CALLOC(1,sizeof(LDAPSyntax));
+
+ if ( !syn ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_LEFTPAREN ) {
+ LDAP_FREE(sval);
+ *code = LDAP_SCHERR_NOLEFTPAREN;
+ ldap_syntax_free(syn);
+ return NULL;
+ }
+
+ parse_whsp(&ss);
+ syn->syn_oid = ldap_int_parse_numericoid(&ss,code,0);
+ if ( !syn->syn_oid ) {
+ *errp = ss;
+ ldap_syntax_free(syn);
+ return NULL;
+ }
+ parse_whsp(&ss);
+
+ /*
+ * Beyond this point we will be liberal and accept the items
+ * in any order.
+ */
+ while (1) {
+ kind = get_token(&ss,&sval);
+ switch (kind) {
+ case TK_EOS:
+ *code = LDAP_SCHERR_NORIGHTPAREN;
+ *errp = EndOfInput;
+ ldap_syntax_free(syn);
+ return NULL;
+ case TK_RIGHTPAREN:
+ return syn;
+ case TK_BAREWORD:
+ if ( !strcasecmp(sval,"NAME") ) {
+ LDAP_FREE(sval);
+ if ( seen_name ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_syntax_free(syn);
+ return(NULL);
+ }
+ seen_name = 1;
+ syn->syn_names = parse_qdescrs(&ss,code);
+ if ( !syn->syn_names ) {
+ if ( *code != LDAP_SCHERR_OUTOFMEM )
+ *code = LDAP_SCHERR_BADNAME;
+ *errp = ss;
+ ldap_syntax_free(syn);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"DESC") ) {
+ LDAP_FREE(sval);
+ if ( seen_desc ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_syntax_free(syn);
+ return(NULL);
+ }
+ seen_desc = 1;
+ parse_whsp(&ss);
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_QDSTRING ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_syntax_free(syn);
+ return NULL;
+ }
+ syn->syn_desc = sval;
+ parse_whsp(&ss);
+ } else if ( sval[0] == 'X' && sval[1] == '-' ) {
+ /* Should be parse_qdstrings */
+ ext_vals = parse_qdescrs(&ss, code);
+ if ( !ext_vals ) {
+ *errp = ss;
+ ldap_syntax_free(syn);
+ return NULL;
+ }
+ if ( add_extension(&syn->syn_extensions,
+ sval, ext_vals) ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_syntax_free(syn);
+ return NULL;
+ }
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_syntax_free(syn);
+ return NULL;
+ }
+ break;
+ default:
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_syntax_free(syn);
+ return NULL;
+ }
+ }
+}
+
+void
+ldap_matchingrule_free( LDAPMatchingRule * mr )
+{
+ if (!mr) return;
+ LDAP_FREE(mr->mr_oid);
+ if (mr->mr_names) LDAP_VFREE(mr->mr_names);
+ if (mr->mr_desc) LDAP_FREE(mr->mr_desc);
+ if (mr->mr_syntax_oid) LDAP_FREE(mr->mr_syntax_oid);
+ free_extensions(mr->mr_extensions);
+ LDAP_FREE(mr);
+}
+
+LDAPMatchingRule *
+ldap_str2matchingrule( LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags )
+{
+ tk_t kind;
+ const char * ss = s;
+ char * sval;
+ int seen_name = 0;
+ int seen_desc = 0;
+ int seen_obsolete = 0;
+ int seen_syntax = 0;
+ LDAPMatchingRule * mr;
+ char ** ext_vals;
+ const char * savepos;
+
+ if ( !s ) {
+ *code = LDAP_SCHERR_EMPTY;
+ *errp = "";
+ return NULL;
+ }
+
+ *errp = s;
+ mr = LDAP_CALLOC(1,sizeof(LDAPMatchingRule));
+
+ if ( !mr ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_LEFTPAREN ) {
+ *code = LDAP_SCHERR_NOLEFTPAREN;
+ LDAP_FREE(sval);
+ ldap_matchingrule_free(mr);
+ return NULL;
+ }
+
+ parse_whsp(&ss);
+ savepos = ss;
+ mr->mr_oid = ldap_int_parse_numericoid(&ss,code,flags);
+ if ( !mr->mr_oid ) {
+ if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) {
+ /* Backtracking */
+ ss = savepos;
+ kind = get_token(&ss,&sval);
+ if ( kind == TK_BAREWORD ) {
+ if ( !strcasecmp(sval, "NAME") ||
+ !strcasecmp(sval, "DESC") ||
+ !strcasecmp(sval, "OBSOLETE") ||
+ !strcasecmp(sval, "SYNTAX") ||
+ !strncasecmp(sval, "X-", 2) ) {
+ /* Missing OID, backtrack */
+ ss = savepos;
+ } else {
+ /* Non-numerical OID, ignore */
+ }
+ }
+ LDAP_FREE(sval);
+ } else {
+ *errp = ss;
+ ldap_matchingrule_free(mr);
+ return NULL;
+ }
+ }
+ parse_whsp(&ss);
+
+ /*
+ * Beyond this point we will be liberal and accept the items
+ * in any order.
+ */
+ while (1) {
+ kind = get_token(&ss,&sval);
+ switch (kind) {
+ case TK_EOS:
+ *code = LDAP_SCHERR_NORIGHTPAREN;
+ *errp = EndOfInput;
+ ldap_matchingrule_free(mr);
+ return NULL;
+ case TK_RIGHTPAREN:
+ if( !seen_syntax ) {
+ *code = LDAP_SCHERR_MISSING;
+ ldap_matchingrule_free(mr);
+ return NULL;
+ }
+ return mr;
+ case TK_BAREWORD:
+ if ( !strcasecmp(sval,"NAME") ) {
+ LDAP_FREE(sval);
+ if ( seen_name ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_matchingrule_free(mr);
+ return(NULL);
+ }
+ seen_name = 1;
+ mr->mr_names = parse_qdescrs(&ss,code);
+ if ( !mr->mr_names ) {
+ if ( *code != LDAP_SCHERR_OUTOFMEM )
+ *code = LDAP_SCHERR_BADNAME;
+ *errp = ss;
+ ldap_matchingrule_free(mr);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"DESC") ) {
+ LDAP_FREE(sval);
+ if ( seen_desc ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_matchingrule_free(mr);
+ return(NULL);
+ }
+ seen_desc = 1;
+ parse_whsp(&ss);
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_QDSTRING ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_matchingrule_free(mr);
+ return NULL;
+ }
+ mr->mr_desc = sval;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"OBSOLETE") ) {
+ LDAP_FREE(sval);
+ if ( seen_obsolete ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_matchingrule_free(mr);
+ return(NULL);
+ }
+ seen_obsolete = 1;
+ mr->mr_obsolete = LDAP_SCHEMA_YES;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"SYNTAX") ) {
+ LDAP_FREE(sval);
+ if ( seen_syntax ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_matchingrule_free(mr);
+ return(NULL);
+ }
+ seen_syntax = 1;
+ parse_whsp(&ss);
+ mr->mr_syntax_oid =
+ ldap_int_parse_numericoid(&ss,code,flags);
+ if ( !mr->mr_syntax_oid ) {
+ *errp = ss;
+ ldap_matchingrule_free(mr);
+ return NULL;
+ }
+ parse_whsp(&ss);
+ } else if ( sval[0] == 'X' && sval[1] == '-' ) {
+ /* Should be parse_qdstrings */
+ ext_vals = parse_qdescrs(&ss, code);
+ if ( !ext_vals ) {
+ *errp = ss;
+ ldap_matchingrule_free(mr);
+ return NULL;
+ }
+ if ( add_extension(&mr->mr_extensions,
+ sval, ext_vals) ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_matchingrule_free(mr);
+ return NULL;
+ }
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_matchingrule_free(mr);
+ return NULL;
+ }
+ break;
+ default:
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_matchingrule_free(mr);
+ return NULL;
+ }
+ }
+}
+
+void
+ldap_matchingruleuse_free( LDAPMatchingRuleUse * mru )
+{
+ if (!mru) return;
+ LDAP_FREE(mru->mru_oid);
+ if (mru->mru_names) LDAP_VFREE(mru->mru_names);
+ if (mru->mru_desc) LDAP_FREE(mru->mru_desc);
+ if (mru->mru_applies_oids) LDAP_VFREE(mru->mru_applies_oids);
+ free_extensions(mru->mru_extensions);
+ LDAP_FREE(mru);
+}
+
+LDAPMatchingRuleUse *
+ldap_str2matchingruleuse( LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags )
+{
+ tk_t kind;
+ const char * ss = s;
+ char * sval;
+ int seen_name = 0;
+ int seen_desc = 0;
+ int seen_obsolete = 0;
+ int seen_applies = 0;
+ LDAPMatchingRuleUse * mru;
+ char ** ext_vals;
+ const char * savepos;
+
+ if ( !s ) {
+ *code = LDAP_SCHERR_EMPTY;
+ *errp = "";
+ return NULL;
+ }
+
+ *errp = s;
+ mru = LDAP_CALLOC(1,sizeof(LDAPMatchingRuleUse));
+
+ if ( !mru ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_LEFTPAREN ) {
+ *code = LDAP_SCHERR_NOLEFTPAREN;
+ LDAP_FREE(sval);
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ }
+
+ parse_whsp(&ss);
+ savepos = ss;
+ mru->mru_oid = ldap_int_parse_numericoid(&ss,code,flags);
+ if ( !mru->mru_oid ) {
+ if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) {
+ /* Backtracking */
+ ss = savepos;
+ kind = get_token(&ss,&sval);
+ if ( kind == TK_BAREWORD ) {
+ if ( !strcasecmp(sval, "NAME") ||
+ !strcasecmp(sval, "DESC") ||
+ !strcasecmp(sval, "OBSOLETE") ||
+ !strcasecmp(sval, "APPLIES") ||
+ !strncasecmp(sval, "X-", 2) ) {
+ /* Missing OID, backtrack */
+ ss = savepos;
+ } else {
+ /* Non-numerical OID, ignore */
+ }
+ }
+ LDAP_FREE(sval);
+ } else {
+ *errp = ss;
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ }
+ }
+ parse_whsp(&ss);
+
+ /*
+ * Beyond this point we will be liberal and accept the items
+ * in any order.
+ */
+ while (1) {
+ kind = get_token(&ss,&sval);
+ switch (kind) {
+ case TK_EOS:
+ *code = LDAP_SCHERR_NORIGHTPAREN;
+ *errp = EndOfInput;
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ case TK_RIGHTPAREN:
+ if( !seen_applies ) {
+ *code = LDAP_SCHERR_MISSING;
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ }
+ return mru;
+ case TK_BAREWORD:
+ if ( !strcasecmp(sval,"NAME") ) {
+ LDAP_FREE(sval);
+ if ( seen_name ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_matchingruleuse_free(mru);
+ return(NULL);
+ }
+ seen_name = 1;
+ mru->mru_names = parse_qdescrs(&ss,code);
+ if ( !mru->mru_names ) {
+ if ( *code != LDAP_SCHERR_OUTOFMEM )
+ *code = LDAP_SCHERR_BADNAME;
+ *errp = ss;
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"DESC") ) {
+ LDAP_FREE(sval);
+ if ( seen_desc ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_matchingruleuse_free(mru);
+ return(NULL);
+ }
+ seen_desc = 1;
+ parse_whsp(&ss);
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_QDSTRING ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ }
+ mru->mru_desc = sval;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"OBSOLETE") ) {
+ LDAP_FREE(sval);
+ if ( seen_obsolete ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_matchingruleuse_free(mru);
+ return(NULL);
+ }
+ seen_obsolete = 1;
+ mru->mru_obsolete = LDAP_SCHEMA_YES;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"APPLIES") ) {
+ LDAP_FREE(sval);
+ if ( seen_applies ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_matchingruleuse_free(mru);
+ return(NULL);
+ }
+ seen_applies = 1;
+ mru->mru_applies_oids = parse_oids(&ss,
+ code,
+ flags);
+ if ( !mru->mru_applies_oids && *code != LDAP_SUCCESS ) {
+ *errp = ss;
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ }
+ } else if ( sval[0] == 'X' && sval[1] == '-' ) {
+ /* Should be parse_qdstrings */
+ ext_vals = parse_qdescrs(&ss, code);
+ if ( !ext_vals ) {
+ *errp = ss;
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ }
+ if ( add_extension(&mru->mru_extensions,
+ sval, ext_vals) ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ }
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ }
+ break;
+ default:
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ }
+ }
+}
+
+void
+ldap_attributetype_free(LDAPAttributeType * at)
+{
+ if (!at) return;
+ LDAP_FREE(at->at_oid);
+ if (at->at_names) LDAP_VFREE(at->at_names);
+ if (at->at_desc) LDAP_FREE(at->at_desc);
+ if (at->at_sup_oid) LDAP_FREE(at->at_sup_oid);
+ if (at->at_equality_oid) LDAP_FREE(at->at_equality_oid);
+ if (at->at_ordering_oid) LDAP_FREE(at->at_ordering_oid);
+ if (at->at_substr_oid) LDAP_FREE(at->at_substr_oid);
+ if (at->at_syntax_oid) LDAP_FREE(at->at_syntax_oid);
+ free_extensions(at->at_extensions);
+ LDAP_FREE(at);
+}
+
+LDAPAttributeType *
+ldap_str2attributetype( LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags )
+{
+ tk_t kind;
+ const char * ss = s;
+ char * sval;
+ int seen_name = 0;
+ int seen_desc = 0;
+ int seen_obsolete = 0;
+ int seen_sup = 0;
+ int seen_equality = 0;
+ int seen_ordering = 0;
+ int seen_substr = 0;
+ int seen_syntax = 0;
+ int seen_usage = 0;
+ LDAPAttributeType * at;
+ char ** ext_vals;
+ const char * savepos;
+
+ if ( !s ) {
+ *code = LDAP_SCHERR_EMPTY;
+ *errp = "";
+ return NULL;
+ }
+
+ *errp = s;
+ at = LDAP_CALLOC(1,sizeof(LDAPAttributeType));
+
+ if ( !at ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_LEFTPAREN ) {
+ *code = LDAP_SCHERR_NOLEFTPAREN;
+ LDAP_FREE(sval);
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+
+ /*
+ * Definitions MUST begin with an OID in the numericoid format.
+ * However, this routine is used by clients to parse the response
+ * from servers and very well known servers will provide an OID
+ * in the wrong format or even no OID at all. We do our best to
+ * extract info from those servers.
+ */
+ parse_whsp(&ss);
+ savepos = ss;
+ at->at_oid = ldap_int_parse_numericoid(&ss,code,0);
+ if ( !at->at_oid ) {
+ if ( ( flags & ( LDAP_SCHEMA_ALLOW_NO_OID
+ | LDAP_SCHEMA_ALLOW_OID_MACRO ) )
+ && (ss == savepos) )
+ {
+ /* Backtracking */
+ ss = savepos;
+ kind = get_token(&ss,&sval);
+ if ( kind == TK_BAREWORD ) {
+ if ( !strcasecmp(sval, "NAME") ||
+ !strcasecmp(sval, "DESC") ||
+ !strcasecmp(sval, "OBSOLETE") ||
+ !strcasecmp(sval, "SUP") ||
+ !strcasecmp(sval, "EQUALITY") ||
+ !strcasecmp(sval, "ORDERING") ||
+ !strcasecmp(sval, "SUBSTR") ||
+ !strcasecmp(sval, "SYNTAX") ||
+ !strcasecmp(sval, "SINGLE-VALUE") ||
+ !strcasecmp(sval, "COLLECTIVE") ||
+ !strcasecmp(sval, "NO-USER-MODIFICATION") ||
+ !strcasecmp(sval, "USAGE") ||
+ !strncasecmp(sval, "X-", 2) )
+ {
+ /* Missing OID, backtrack */
+ ss = savepos;
+ } else if ( flags
+ & LDAP_SCHEMA_ALLOW_OID_MACRO)
+ {
+ /* Non-numerical OID ... */
+ int len = ss-savepos;
+ at->at_oid = LDAP_MALLOC(len+1);
+ if ( !at->at_oid ) {
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+
+ strncpy(at->at_oid, savepos, len);
+ at->at_oid[len] = 0;
+ }
+ }
+ LDAP_FREE(sval);
+ } else {
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ }
+ parse_whsp(&ss);
+
+ /*
+ * Beyond this point we will be liberal and accept the items
+ * in any order.
+ */
+ while (1) {
+ kind = get_token(&ss,&sval);
+ switch (kind) {
+ case TK_EOS:
+ *code = LDAP_SCHERR_NORIGHTPAREN;
+ *errp = EndOfInput;
+ ldap_attributetype_free(at);
+ return NULL;
+ case TK_RIGHTPAREN:
+ return at;
+ case TK_BAREWORD:
+ if ( !strcasecmp(sval,"NAME") ) {
+ LDAP_FREE(sval);
+ if ( seen_name ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ seen_name = 1;
+ at->at_names = parse_qdescrs(&ss,code);
+ if ( !at->at_names ) {
+ if ( *code != LDAP_SCHERR_OUTOFMEM )
+ *code = LDAP_SCHERR_BADNAME;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"DESC") ) {
+ LDAP_FREE(sval);
+ if ( seen_desc ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ seen_desc = 1;
+ parse_whsp(&ss);
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_QDSTRING ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ at->at_desc = sval;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"OBSOLETE") ) {
+ LDAP_FREE(sval);
+ if ( seen_obsolete ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ seen_obsolete = 1;
+ at->at_obsolete = LDAP_SCHEMA_YES;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"SUP") ) {
+ LDAP_FREE(sval);
+ if ( seen_sup ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ seen_sup = 1;
+ at->at_sup_oid = parse_woid(&ss,code);
+ if ( !at->at_sup_oid ) {
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"EQUALITY") ) {
+ LDAP_FREE(sval);
+ if ( seen_equality ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ seen_equality = 1;
+ at->at_equality_oid = parse_woid(&ss,code);
+ if ( !at->at_equality_oid ) {
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"ORDERING") ) {
+ LDAP_FREE(sval);
+ if ( seen_ordering ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ seen_ordering = 1;
+ at->at_ordering_oid = parse_woid(&ss,code);
+ if ( !at->at_ordering_oid ) {
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"SUBSTR") ) {
+ LDAP_FREE(sval);
+ if ( seen_substr ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ seen_substr = 1;
+ at->at_substr_oid = parse_woid(&ss,code);
+ if ( !at->at_substr_oid ) {
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"SYNTAX") ) {
+ LDAP_FREE(sval);
+ if ( seen_syntax ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ seen_syntax = 1;
+ parse_whsp(&ss);
+ savepos = ss;
+ at->at_syntax_oid =
+ parse_noidlen(&ss,
+ code,
+ &at->at_syntax_len,
+ flags);
+ if ( !at->at_syntax_oid ) {
+ if ( flags & LDAP_SCHEMA_ALLOW_OID_MACRO ) {
+ kind = get_token(&ss,&sval);
+ if (kind == TK_BAREWORD)
+ {
+ char *sp = strchr(sval, '{');
+ at->at_syntax_oid = sval;
+ if (sp)
+ {
+ *sp++ = 0;
+ at->at_syntax_len = atoi(sp);
+ while ( LDAP_DIGIT(*sp) )
+ sp++;
+ if ( *sp != '}' ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ }
+ }
+ } else {
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ }
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"SINGLE-VALUE") ) {
+ LDAP_FREE(sval);
+ if ( at->at_single_value ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ at->at_single_value = LDAP_SCHEMA_YES;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"COLLECTIVE") ) {
+ LDAP_FREE(sval);
+ if ( at->at_collective ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ at->at_collective = LDAP_SCHEMA_YES;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"NO-USER-MODIFICATION") ) {
+ LDAP_FREE(sval);
+ if ( at->at_no_user_mod ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ at->at_no_user_mod = LDAP_SCHEMA_YES;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"USAGE") ) {
+ LDAP_FREE(sval);
+ if ( seen_usage ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ seen_usage = 1;
+ parse_whsp(&ss);
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_BAREWORD ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ if ( !strcasecmp(sval,"userApplications") )
+ at->at_usage =
+ LDAP_SCHEMA_USER_APPLICATIONS;
+ else if ( !strcasecmp(sval,"directoryOperation") )
+ at->at_usage =
+ LDAP_SCHEMA_DIRECTORY_OPERATION;
+ else if ( !strcasecmp(sval,"distributedOperation") )
+ at->at_usage =
+ LDAP_SCHEMA_DISTRIBUTED_OPERATION;
+ else if ( !strcasecmp(sval,"dSAOperation") )
+ at->at_usage =
+ LDAP_SCHEMA_DSA_OPERATION;
+ else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ LDAP_FREE(sval);
+ parse_whsp(&ss);
+ } else if ( sval[0] == 'X' && sval[1] == '-' ) {
+ /* Should be parse_qdstrings */
+ ext_vals = parse_qdescrs(&ss, code);
+ if ( !ext_vals ) {
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ if ( add_extension(&at->at_extensions,
+ sval, ext_vals) ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ break;
+ default:
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ }
+}
+
+void
+ldap_objectclass_free(LDAPObjectClass * oc)
+{
+ if (!oc) return;
+ LDAP_FREE(oc->oc_oid);
+ if (oc->oc_names) LDAP_VFREE(oc->oc_names);
+ if (oc->oc_desc) LDAP_FREE(oc->oc_desc);
+ if (oc->oc_sup_oids) LDAP_VFREE(oc->oc_sup_oids);
+ if (oc->oc_at_oids_must) LDAP_VFREE(oc->oc_at_oids_must);
+ if (oc->oc_at_oids_may) LDAP_VFREE(oc->oc_at_oids_may);
+ free_extensions(oc->oc_extensions);
+ LDAP_FREE(oc);
+}
+
+LDAPObjectClass *
+ldap_str2objectclass( LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags )
+{
+ tk_t kind;
+ const char * ss = s;
+ char * sval;
+ int seen_name = 0;
+ int seen_desc = 0;
+ int seen_obsolete = 0;
+ int seen_sup = 0;
+ int seen_kind = 0;
+ int seen_must = 0;
+ int seen_may = 0;
+ LDAPObjectClass * oc;
+ char ** ext_vals;
+ const char * savepos;
+
+ if ( !s ) {
+ *code = LDAP_SCHERR_EMPTY;
+ *errp = "";
+ return NULL;
+ }
+
+ *errp = s;
+ oc = LDAP_CALLOC(1,sizeof(LDAPObjectClass));
+
+ if ( !oc ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+ oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
+
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_LEFTPAREN ) {
+ *code = LDAP_SCHERR_NOLEFTPAREN;
+ LDAP_FREE(sval);
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+
+ /*
+ * Definitions MUST begin with an OID in the numericoid format.
+ * However, this routine is used by clients to parse the response
+ * from servers and very well known servers will provide an OID
+ * in the wrong format or even no OID at all. We do our best to
+ * extract info from those servers.
+ */
+ parse_whsp(&ss);
+ savepos = ss;
+ oc->oc_oid = ldap_int_parse_numericoid(&ss,code,0);
+ if ( !oc->oc_oid ) {
+ if ( (flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos) ) {
+ /* Backtracking */
+ ss = savepos;
+ kind = get_token(&ss,&sval);
+ if ( kind == TK_BAREWORD ) {
+ if ( !strcasecmp(sval, "NAME") ||
+ !strcasecmp(sval, "DESC") ||
+ !strcasecmp(sval, "OBSOLETE") ||
+ !strcasecmp(sval, "SUP") ||
+ !strcasecmp(sval, "ABSTRACT") ||
+ !strcasecmp(sval, "STRUCTURAL") ||
+ !strcasecmp(sval, "AUXILIARY") ||
+ !strcasecmp(sval, "MUST") ||
+ !strcasecmp(sval, "MAY") ||
+ !strncasecmp(sval, "X-", 2) ) {
+ /* Missing OID, backtrack */
+ ss = savepos;
+ } else if ( flags &
+ LDAP_SCHEMA_ALLOW_OID_MACRO ) {
+ /* Non-numerical OID, ignore */
+ int len = ss-savepos;
+ oc->oc_oid = LDAP_MALLOC(len+1);
+ if ( !oc->oc_oid ) {
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+
+ strncpy(oc->oc_oid, savepos, len);
+ oc->oc_oid[len] = 0;
+ }
+ }
+ LDAP_FREE(sval);
+ *code = 0;
+ } else {
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ }
+ parse_whsp(&ss);
+
+ /*
+ * Beyond this point we will be liberal an accept the items
+ * in any order.
+ */
+ while (1) {
+ kind = get_token(&ss,&sval);
+ switch (kind) {
+ case TK_EOS:
+ *code = LDAP_SCHERR_NORIGHTPAREN;
+ *errp = EndOfInput;
+ ldap_objectclass_free(oc);
+ return NULL;
+ case TK_RIGHTPAREN:
+ return oc;
+ case TK_BAREWORD:
+ if ( !strcasecmp(sval,"NAME") ) {
+ LDAP_FREE(sval);
+ if ( seen_name ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_name = 1;
+ oc->oc_names = parse_qdescrs(&ss,code);
+ if ( !oc->oc_names ) {
+ if ( *code != LDAP_SCHERR_OUTOFMEM )
+ *code = LDAP_SCHERR_BADNAME;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"DESC") ) {
+ LDAP_FREE(sval);
+ if ( seen_desc ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_desc = 1;
+ parse_whsp(&ss);
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_QDSTRING ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ oc->oc_desc = sval;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"OBSOLETE") ) {
+ LDAP_FREE(sval);
+ if ( seen_obsolete ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_obsolete = 1;
+ oc->oc_obsolete = LDAP_SCHEMA_YES;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"SUP") ) {
+ LDAP_FREE(sval);
+ if ( seen_sup ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_sup = 1;
+ oc->oc_sup_oids = parse_oids(&ss,
+ code,
+ flags);
+ if ( !oc->oc_sup_oids && *code != LDAP_SUCCESS ) {
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ *code = 0;
+ } else if ( !strcasecmp(sval,"ABSTRACT") ) {
+ LDAP_FREE(sval);
+ if ( seen_kind ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_kind = 1;
+ oc->oc_kind = LDAP_SCHEMA_ABSTRACT;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"STRUCTURAL") ) {
+ LDAP_FREE(sval);
+ if ( seen_kind ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_kind = 1;
+ oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"AUXILIARY") ) {
+ LDAP_FREE(sval);
+ if ( seen_kind ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_kind = 1;
+ oc->oc_kind = LDAP_SCHEMA_AUXILIARY;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"MUST") ) {
+ LDAP_FREE(sval);
+ if ( seen_must ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_must = 1;
+ oc->oc_at_oids_must = parse_oids(&ss,code,0);
+ if ( !oc->oc_at_oids_must && *code != LDAP_SUCCESS ) {
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ *code = 0;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"MAY") ) {
+ LDAP_FREE(sval);
+ if ( seen_may ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_may = 1;
+ oc->oc_at_oids_may = parse_oids(&ss,code,0);
+ if ( !oc->oc_at_oids_may && *code != LDAP_SUCCESS ) {
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ *code = 0;
+ parse_whsp(&ss);
+ } else if ( sval[0] == 'X' && sval[1] == '-' ) {
+ /* Should be parse_qdstrings */
+ ext_vals = parse_qdescrs(&ss, code);
+ *code = 0;
+ if ( !ext_vals ) {
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ if ( add_extension(&oc->oc_extensions,
+ sval, ext_vals) ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ break;
+ default:
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ }
+}
+
+void
+ldap_contentrule_free(LDAPContentRule * cr)
+{
+ if (!cr) return;
+ LDAP_FREE(cr->cr_oid);
+ if (cr->cr_names) LDAP_VFREE(cr->cr_names);
+ if (cr->cr_desc) LDAP_FREE(cr->cr_desc);
+ if (cr->cr_oc_oids_aux) LDAP_VFREE(cr->cr_oc_oids_aux);
+ if (cr->cr_at_oids_must) LDAP_VFREE(cr->cr_at_oids_must);
+ if (cr->cr_at_oids_may) LDAP_VFREE(cr->cr_at_oids_may);
+ if (cr->cr_at_oids_not) LDAP_VFREE(cr->cr_at_oids_not);
+ free_extensions(cr->cr_extensions);
+ LDAP_FREE(cr);
+}
+
+LDAPContentRule *
+ldap_str2contentrule( LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags )
+{
+ tk_t kind;
+ const char * ss = s;
+ char * sval;
+ int seen_name = 0;
+ int seen_desc = 0;
+ int seen_obsolete = 0;
+ int seen_aux = 0;
+ int seen_must = 0;
+ int seen_may = 0;
+ int seen_not = 0;
+ LDAPContentRule * cr;
+ char ** ext_vals;
+ const char * savepos;
+
+ if ( !s ) {
+ *code = LDAP_SCHERR_EMPTY;
+ *errp = "";
+ return NULL;
+ }
+
+ *errp = s;
+ cr = LDAP_CALLOC(1,sizeof(LDAPContentRule));
+
+ if ( !cr ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_LEFTPAREN ) {
+ *code = LDAP_SCHERR_NOLEFTPAREN;
+ LDAP_FREE(sval);
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+
+ /*
+ * Definitions MUST begin with an OID in the numericoid format.
+ */
+ parse_whsp(&ss);
+ savepos = ss;
+ cr->cr_oid = ldap_int_parse_numericoid(&ss,code,0);
+ if ( !cr->cr_oid ) {
+ if ( (flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos) ) {
+ /* Backtracking */
+ ss = savepos;
+ kind = get_token(&ss,&sval);
+ if ( kind == TK_BAREWORD ) {
+ if ( !strcasecmp(sval, "NAME") ||
+ !strcasecmp(sval, "DESC") ||
+ !strcasecmp(sval, "OBSOLETE") ||
+ !strcasecmp(sval, "AUX") ||
+ !strcasecmp(sval, "MUST") ||
+ !strcasecmp(sval, "MAY") ||
+ !strcasecmp(sval, "NOT") ||
+ !strncasecmp(sval, "X-", 2) ) {
+ /* Missing OID, backtrack */
+ ss = savepos;
+ } else if ( flags &
+ LDAP_SCHEMA_ALLOW_OID_MACRO ) {
+ /* Non-numerical OID, ignore */
+ int len = ss-savepos;
+ cr->cr_oid = LDAP_MALLOC(len+1);
+ if ( !cr->cr_oid ) {
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+
+ strncpy(cr->cr_oid, savepos, len);
+ cr->cr_oid[len] = 0;
+ }
+ }
+ LDAP_FREE(sval);
+ } else {
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ }
+ parse_whsp(&ss);
+
+ /*
+ * Beyond this point we will be liberal an accept the items
+ * in any order.
+ */
+ while (1) {
+ kind = get_token(&ss,&sval);
+ switch (kind) {
+ case TK_EOS:
+ *code = LDAP_SCHERR_NORIGHTPAREN;
+ *errp = EndOfInput;
+ ldap_contentrule_free(cr);
+ return NULL;
+ case TK_RIGHTPAREN:
+ return cr;
+ case TK_BAREWORD:
+ if ( !strcasecmp(sval,"NAME") ) {
+ LDAP_FREE(sval);
+ if ( seen_name ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return(NULL);
+ }
+ seen_name = 1;
+ cr->cr_names = parse_qdescrs(&ss,code);
+ if ( !cr->cr_names ) {
+ if ( *code != LDAP_SCHERR_OUTOFMEM )
+ *code = LDAP_SCHERR_BADNAME;
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"DESC") ) {
+ LDAP_FREE(sval);
+ if ( seen_desc ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return(NULL);
+ }
+ seen_desc = 1;
+ parse_whsp(&ss);
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_QDSTRING ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ cr->cr_desc = sval;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"OBSOLETE") ) {
+ LDAP_FREE(sval);
+ if ( seen_obsolete ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return(NULL);
+ }
+ seen_obsolete = 1;
+ cr->cr_obsolete = LDAP_SCHEMA_YES;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"AUX") ) {
+ LDAP_FREE(sval);
+ if ( seen_aux ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return(NULL);
+ }
+ seen_aux = 1;
+ cr->cr_oc_oids_aux = parse_oids(&ss,code,0);
+ if ( !cr->cr_oc_oids_aux ) {
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"MUST") ) {
+ LDAP_FREE(sval);
+ if ( seen_must ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return(NULL);
+ }
+ seen_must = 1;
+ cr->cr_at_oids_must = parse_oids(&ss,code,0);
+ if ( !cr->cr_at_oids_must && *code != LDAP_SUCCESS ) {
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"MAY") ) {
+ LDAP_FREE(sval);
+ if ( seen_may ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return(NULL);
+ }
+ seen_may = 1;
+ cr->cr_at_oids_may = parse_oids(&ss,code,0);
+ if ( !cr->cr_at_oids_may && *code != LDAP_SUCCESS ) {
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"NOT") ) {
+ LDAP_FREE(sval);
+ if ( seen_not ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return(NULL);
+ }
+ seen_not = 1;
+ cr->cr_at_oids_not = parse_oids(&ss,code,0);
+ if ( !cr->cr_at_oids_not && *code != LDAP_SUCCESS ) {
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ parse_whsp(&ss);
+ } else if ( sval[0] == 'X' && sval[1] == '-' ) {
+ /* Should be parse_qdstrings */
+ ext_vals = parse_qdescrs(&ss, code);
+ if ( !ext_vals ) {
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ if ( add_extension(&cr->cr_extensions,
+ sval, ext_vals) ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ break;
+ default:
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ }
+}
+
+void
+ldap_structurerule_free(LDAPStructureRule * sr)
+{
+ if (!sr) return;
+ if (sr->sr_names) LDAP_VFREE(sr->sr_names);
+ if (sr->sr_desc) LDAP_FREE(sr->sr_desc);
+ if (sr->sr_nameform) LDAP_FREE(sr->sr_nameform);
+ if (sr->sr_sup_ruleids) LDAP_FREE(sr->sr_sup_ruleids);
+ free_extensions(sr->sr_extensions);
+ LDAP_FREE(sr);
+}
+
+LDAPStructureRule *
+ldap_str2structurerule( LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags )
+{
+ tk_t kind;
+ int ret;
+ const char * ss = s;
+ char * sval;
+ int seen_name = 0;
+ int seen_desc = 0;
+ int seen_obsolete = 0;
+ int seen_nameform = 0;
+ LDAPStructureRule * sr;
+ char ** ext_vals;
+ const char * savepos;
+
+ if ( !s ) {
+ *code = LDAP_SCHERR_EMPTY;
+ *errp = "";
+ return NULL;
+ }
+
+ *errp = s;
+ sr = LDAP_CALLOC(1,sizeof(LDAPStructureRule));
+
+ if ( !sr ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_LEFTPAREN ) {
+ *code = LDAP_SCHERR_NOLEFTPAREN;
+ LDAP_FREE(sval);
+ ldap_structurerule_free(sr);
+ return NULL;
+ }
+
+ /*
+ * Definitions MUST begin with a ruleid.
+ */
+ parse_whsp(&ss);
+ savepos = ss;
+ ret = ldap_int_parse_ruleid(&ss,code,0,&sr->sr_ruleid);
+ if ( ret ) {
+ *errp = ss;
+ ldap_structurerule_free(sr);
+ return NULL;
+ }
+ parse_whsp(&ss);
+
+ /*
+ * Beyond this point we will be liberal an accept the items
+ * in any order.
+ */
+ while (1) {
+ kind = get_token(&ss,&sval);
+ switch (kind) {
+ case TK_EOS:
+ *code = LDAP_SCHERR_NORIGHTPAREN;
+ *errp = EndOfInput;
+ ldap_structurerule_free(sr);
+ return NULL;
+ case TK_RIGHTPAREN:
+ if( !seen_nameform ) {
+ *code = LDAP_SCHERR_MISSING;
+ ldap_structurerule_free(sr);
+ return NULL;
+ }
+ return sr;
+ case TK_BAREWORD:
+ if ( !strcasecmp(sval,"NAME") ) {
+ LDAP_FREE(sval);
+ if ( seen_name ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_structurerule_free(sr);
+ return(NULL);
+ }
+ seen_name = 1;
+ sr->sr_names = parse_qdescrs(&ss,code);
+ if ( !sr->sr_names ) {
+ if ( *code != LDAP_SCHERR_OUTOFMEM )
+ *code = LDAP_SCHERR_BADNAME;
+ *errp = ss;
+ ldap_structurerule_free(sr);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"DESC") ) {
+ LDAP_FREE(sval);
+ if ( seen_desc ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_structurerule_free(sr);
+ return(NULL);
+ }
+ seen_desc = 1;
+ parse_whsp(&ss);
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_QDSTRING ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_structurerule_free(sr);
+ return NULL;
+ }
+ sr->sr_desc = sval;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"OBSOLETE") ) {
+ LDAP_FREE(sval);
+ if ( seen_obsolete ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_structurerule_free(sr);
+ return(NULL);
+ }
+ seen_obsolete = 1;
+ sr->sr_obsolete = LDAP_SCHEMA_YES;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"FORM") ) {
+ LDAP_FREE(sval);
+ if ( seen_nameform ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_structurerule_free(sr);
+ return(NULL);
+ }
+ seen_nameform = 1;
+ sr->sr_nameform = parse_woid(&ss,code);
+ if ( !sr->sr_nameform ) {
+ *errp = ss;
+ ldap_structurerule_free(sr);
+ return NULL;
+ }
+ parse_whsp(&ss);
+ } else if ( sval[0] == 'X' && sval[1] == '-' ) {
+ /* Should be parse_qdstrings */
+ ext_vals = parse_qdescrs(&ss, code);
+ if ( !ext_vals ) {
+ *errp = ss;
+ ldap_structurerule_free(sr);
+ return NULL;
+ }
+ if ( add_extension(&sr->sr_extensions,
+ sval, ext_vals) ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_structurerule_free(sr);
+ return NULL;
+ }
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_structurerule_free(sr);
+ return NULL;
+ }
+ break;
+ default:
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_structurerule_free(sr);
+ return NULL;
+ }
+ }
+}
+
+void
+ldap_nameform_free(LDAPNameForm * nf)
+{
+ if (!nf) return;
+ LDAP_FREE(nf->nf_oid);
+ if (nf->nf_names) LDAP_VFREE(nf->nf_names);
+ if (nf->nf_desc) LDAP_FREE(nf->nf_desc);
+ if (nf->nf_objectclass) LDAP_FREE(nf->nf_objectclass);
+ if (nf->nf_at_oids_must) LDAP_VFREE(nf->nf_at_oids_must);
+ if (nf->nf_at_oids_may) LDAP_VFREE(nf->nf_at_oids_may);
+ free_extensions(nf->nf_extensions);
+ LDAP_FREE(nf);
+}
+
+LDAPNameForm *
+ldap_str2nameform( LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags )
+{
+ tk_t kind;
+ const char * ss = s;
+ char * sval;
+ int seen_name = 0;
+ int seen_desc = 0;
+ int seen_obsolete = 0;
+ int seen_class = 0;
+ int seen_must = 0;
+ int seen_may = 0;
+ LDAPNameForm * nf;
+ char ** ext_vals;
+ const char * savepos;
+
+ if ( !s ) {
+ *code = LDAP_SCHERR_EMPTY;
+ *errp = "";
+ return NULL;
+ }
+
+ *errp = s;
+ nf = LDAP_CALLOC(1,sizeof(LDAPNameForm));
+
+ if ( !nf ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_LEFTPAREN ) {
+ *code = LDAP_SCHERR_NOLEFTPAREN;
+ LDAP_FREE(sval);
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+
+ /*
+ * Definitions MUST begin with an OID in the numericoid format.
+ * However, this routine is used by clients to parse the response
+ * from servers and very well known servers will provide an OID
+ * in the wrong format or even no OID at all. We do our best to
+ * extract info from those servers.
+ */
+ parse_whsp(&ss);
+ savepos = ss;
+ nf->nf_oid = ldap_int_parse_numericoid(&ss,code,0);
+ if ( !nf->nf_oid ) {
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ parse_whsp(&ss);
+
+ /*
+ * Beyond this point we will be liberal an accept the items
+ * in any order.
+ */
+ while (1) {
+ kind = get_token(&ss,&sval);
+ switch (kind) {
+ case TK_EOS:
+ *code = LDAP_SCHERR_NORIGHTPAREN;
+ *errp = EndOfInput;
+ ldap_nameform_free(nf);
+ return NULL;
+ case TK_RIGHTPAREN:
+ if( !seen_class || !seen_must ) {
+ *code = LDAP_SCHERR_MISSING;
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ return nf;
+ case TK_BAREWORD:
+ if ( !strcasecmp(sval,"NAME") ) {
+ LDAP_FREE(sval);
+ if ( seen_name ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return(NULL);
+ }
+ seen_name = 1;
+ nf->nf_names = parse_qdescrs(&ss,code);
+ if ( !nf->nf_names ) {
+ if ( *code != LDAP_SCHERR_OUTOFMEM )
+ *code = LDAP_SCHERR_BADNAME;
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"DESC") ) {
+ LDAP_FREE(sval);
+ if ( seen_desc ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return(NULL);
+ }
+ seen_desc = 1;
+ parse_whsp(&ss);
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_QDSTRING ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ nf->nf_desc = sval;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"OBSOLETE") ) {
+ LDAP_FREE(sval);
+ if ( seen_obsolete ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return(NULL);
+ }
+ seen_obsolete = 1;
+ nf->nf_obsolete = LDAP_SCHEMA_YES;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"OC") ) {
+ LDAP_FREE(sval);
+ if ( seen_class ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return(NULL);
+ }
+ seen_class = 1;
+ nf->nf_objectclass = parse_woid(&ss,code);
+ if ( !nf->nf_objectclass ) {
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"MUST") ) {
+ LDAP_FREE(sval);
+ if ( seen_must ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return(NULL);
+ }
+ seen_must = 1;
+ nf->nf_at_oids_must = parse_oids(&ss,code,0);
+ if ( !nf->nf_at_oids_must && *code != LDAP_SUCCESS ) {
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"MAY") ) {
+ LDAP_FREE(sval);
+ if ( seen_may ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return(NULL);
+ }
+ seen_may = 1;
+ nf->nf_at_oids_may = parse_oids(&ss,code,0);
+ if ( !nf->nf_at_oids_may && *code != LDAP_SUCCESS ) {
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ parse_whsp(&ss);
+ } else if ( sval[0] == 'X' && sval[1] == '-' ) {
+ /* Should be parse_qdstrings */
+ ext_vals = parse_qdescrs(&ss, code);
+ if ( !ext_vals ) {
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ if ( add_extension(&nf->nf_extensions,
+ sval, ext_vals) ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ break;
+ default:
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ }
+}
+
+static char *const err2text[] = {
+ N_("Success"),
+ N_("Out of memory"),
+ N_("Unexpected token"),
+ N_("Missing opening parenthesis"),
+ N_("Missing closing parenthesis"),
+ N_("Expecting digit"),
+ N_("Expecting a name"),
+ N_("Bad description"),
+ N_("Bad superiors"),
+ N_("Duplicate option"),
+ N_("Unexpected end of data"),
+ N_("Missing required field"),
+ N_("Out of order field")
+};
+
+char *
+ldap_scherr2str(int code)
+{
+ if ( code < 0 || code >= (int)(sizeof(err2text)/sizeof(char *)) ) {
+ return _("Unknown error");
+ } else {
+ return _(err2text[code]);
+ }
+}
diff --git a/libraries/libldap/search.c b/libraries/libldap/search.c
new file mode 100644
index 0000000..2381577
--- /dev/null
+++ b/libraries/libldap/search.c
@@ -0,0 +1,545 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_log.h"
+
+/*
+ * ldap_search_ext - initiate an ldap search operation.
+ *
+ * Parameters:
+ *
+ * ld LDAP descriptor
+ * base DN of the base object
+ * scope the search scope - one of
+ * LDAP_SCOPE_BASE (baseObject),
+ * LDAP_SCOPE_ONELEVEL (oneLevel),
+ * LDAP_SCOPE_SUBTREE (subtree), or
+ * LDAP_SCOPE_SUBORDINATE (children) -- OpenLDAP extension
+ * filter a string containing the search filter
+ * (e.g., "(|(cn=bob)(sn=bob))")
+ * attrs list of attribute types to return for matches
+ * attrsonly 1 => attributes only 0 => attributes and values
+ *
+ * Example:
+ * char *attrs[] = { "mail", "title", 0 };
+ * ldap_search_ext( ld, "dc=example,dc=com", LDAP_SCOPE_SUBTREE, "cn~=bob",
+ * attrs, attrsonly, sctrls, ctrls, timeout, sizelimit,
+ * &msgid );
+ */
+int
+ldap_search_ext(
+ LDAP *ld,
+ LDAP_CONST char *base,
+ int scope,
+ LDAP_CONST char *filter,
+ char **attrs,
+ int attrsonly,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ struct timeval *timeout,
+ int sizelimit,
+ int *msgidp )
+{
+ return ldap_pvt_search( ld, base, scope, filter, attrs,
+ attrsonly, sctrls, cctrls, timeout, sizelimit, -1, msgidp );
+}
+
+int
+ldap_pvt_search(
+ LDAP *ld,
+ LDAP_CONST char *base,
+ int scope,
+ LDAP_CONST char *filter,
+ char **attrs,
+ int attrsonly,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ struct timeval *timeout,
+ int sizelimit,
+ int deref,
+ int *msgidp )
+{
+ int rc;
+ BerElement *ber;
+ int timelimit;
+ ber_int_t id;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_search_ext\n" );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ /* check client controls */
+ rc = ldap_int_client_controls( ld, cctrls );
+ if( rc != LDAP_SUCCESS ) return rc;
+
+ /*
+ * if timeout is provided, both tv_sec and tv_usec must
+ * not be zero
+ */
+ if( timeout != NULL ) {
+ if( timeout->tv_sec == 0 && timeout->tv_usec == 0 ) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ /* timelimit must be non-zero if timeout is provided */
+ timelimit = timeout->tv_sec != 0 ? timeout->tv_sec : 1;
+
+ } else {
+ /* no timeout, no timelimit */
+ timelimit = -1;
+ }
+
+ ber = ldap_build_search_req( ld, base, scope, filter, attrs,
+ attrsonly, sctrls, cctrls, timelimit, sizelimit, deref, &id );
+
+ if ( ber == NULL ) {
+ return ld->ld_errno;
+ }
+
+
+ /* send the message */
+ *msgidp = ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber, id );
+
+ if( *msgidp < 0 )
+ return ld->ld_errno;
+
+ return LDAP_SUCCESS;
+}
+
+int
+ldap_search_ext_s(
+ LDAP *ld,
+ LDAP_CONST char *base,
+ int scope,
+ LDAP_CONST char *filter,
+ char **attrs,
+ int attrsonly,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ struct timeval *timeout,
+ int sizelimit,
+ LDAPMessage **res )
+{
+ return ldap_pvt_search_s( ld, base, scope, filter, attrs,
+ attrsonly, sctrls, cctrls, timeout, sizelimit, -1, res );
+}
+
+int
+ldap_pvt_search_s(
+ LDAP *ld,
+ LDAP_CONST char *base,
+ int scope,
+ LDAP_CONST char *filter,
+ char **attrs,
+ int attrsonly,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ struct timeval *timeout,
+ int sizelimit,
+ int deref,
+ LDAPMessage **res )
+{
+ int rc;
+ int msgid;
+
+ *res = NULL;
+
+ rc = ldap_pvt_search( ld, base, scope, filter, attrs, attrsonly,
+ sctrls, cctrls, timeout, sizelimit, deref, &msgid );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return( rc );
+ }
+
+ rc = ldap_result( ld, msgid, LDAP_MSG_ALL, timeout, res );
+
+ if( rc <= 0 ) {
+ /* error(-1) or timeout(0) */
+ if ( ld->ld_errno == LDAP_TIMEOUT ) {
+ /* cleanup request */
+ (void) ldap_abandon( ld, msgid );
+ ld->ld_errno = LDAP_TIMEOUT;
+ }
+ return( ld->ld_errno );
+ }
+
+ if( rc == LDAP_RES_SEARCH_REFERENCE || rc == LDAP_RES_INTERMEDIATE ) {
+ return( ld->ld_errno );
+ }
+
+ return( ldap_result2error( ld, *res, 0 ) );
+}
+
+/*
+ * ldap_search - initiate an ldap search operation.
+ *
+ * Parameters:
+ *
+ * ld LDAP descriptor
+ * base DN of the base object
+ * scope the search scope - one of
+ * LDAP_SCOPE_BASE (baseObject),
+ * LDAP_SCOPE_ONELEVEL (oneLevel),
+ * LDAP_SCOPE_SUBTREE (subtree), or
+ * LDAP_SCOPE_SUBORDINATE (children) -- OpenLDAP extension
+ * filter a string containing the search filter
+ * (e.g., "(|(cn=bob)(sn=bob))")
+ * attrs list of attribute types to return for matches
+ * attrsonly 1 => attributes only 0 => attributes and values
+ *
+ * Example:
+ * char *attrs[] = { "mail", "title", 0 };
+ * msgid = ldap_search( ld, "dc=example,dc=com", LDAP_SCOPE_SUBTREE, "cn~=bob",
+ * attrs, attrsonly );
+ */
+int
+ldap_search(
+ LDAP *ld, LDAP_CONST char *base, int scope, LDAP_CONST char *filter,
+ char **attrs, int attrsonly )
+{
+ BerElement *ber;
+ ber_int_t id;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_search\n" );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ ber = ldap_build_search_req( ld, base, scope, filter, attrs,
+ attrsonly, NULL, NULL, -1, -1, -1, &id );
+
+ if ( ber == NULL ) {
+ return( -1 );
+ }
+
+
+ /* send the message */
+ return ( ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber, id ));
+}
+
+
+BerElement *
+ldap_build_search_req(
+ LDAP *ld,
+ LDAP_CONST char *base,
+ ber_int_t scope,
+ LDAP_CONST char *filter,
+ char **attrs,
+ ber_int_t attrsonly,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t timelimit,
+ ber_int_t sizelimit,
+ ber_int_t deref,
+ ber_int_t *idp)
+{
+ BerElement *ber;
+ int err;
+
+ /*
+ * Create the search request. It looks like this:
+ * SearchRequest := [APPLICATION 3] SEQUENCE {
+ * baseObject DistinguishedName,
+ * scope ENUMERATED {
+ * baseObject (0),
+ * singleLevel (1),
+ * wholeSubtree (2)
+ * },
+ * derefAliases ENUMERATED {
+ * neverDerefaliases (0),
+ * derefInSearching (1),
+ * derefFindingBaseObj (2),
+ * alwaysDerefAliases (3)
+ * },
+ * sizelimit INTEGER (0 .. 65535),
+ * timelimit INTEGER (0 .. 65535),
+ * attrsOnly BOOLEAN,
+ * filter Filter,
+ * attributes SEQUENCE OF AttributeType
+ * }
+ * wrapped in an ldap message.
+ */
+
+ /* create a message to send */
+ if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
+ return( NULL );
+ }
+
+ if ( base == NULL ) {
+ /* no base provided, use session default base */
+ base = ld->ld_options.ldo_defbase;
+
+ if ( base == NULL ) {
+ /* no session default base, use top */
+ base = "";
+ }
+ }
+
+ LDAP_NEXT_MSGID( ld, *idp );
+#ifdef LDAP_CONNECTIONLESS
+ if ( LDAP_IS_UDP(ld) ) {
+ struct sockaddr_storage sa = {0};
+ /* dummy, filled with ldo_peer in request.c */
+ err = ber_write( ber, (char *) &sa, sizeof( sa ), 0 );
+ }
+ if ( LDAP_IS_UDP(ld) && ld->ld_options.ldo_version == LDAP_VERSION2) {
+ char *dn = ld->ld_options.ldo_cldapdn;
+ if (!dn) dn = "";
+ err = ber_printf( ber, "{ist{seeiib", *idp, dn,
+ LDAP_REQ_SEARCH, base, (ber_int_t) scope,
+ (deref < 0) ? ld->ld_deref : deref,
+ (sizelimit < 0) ? ld->ld_sizelimit : sizelimit,
+ (timelimit < 0) ? ld->ld_timelimit : timelimit,
+ attrsonly );
+ } else
+#endif
+ {
+ err = ber_printf( ber, "{it{seeiib", *idp,
+ LDAP_REQ_SEARCH, base, (ber_int_t) scope,
+ (deref < 0) ? ld->ld_deref : deref,
+ (sizelimit < 0) ? ld->ld_sizelimit : sizelimit,
+ (timelimit < 0) ? ld->ld_timelimit : timelimit,
+ attrsonly );
+ }
+
+ if ( err == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ if( filter == NULL ) {
+ filter = "(objectclass=*)";
+ }
+
+ err = ldap_pvt_put_filter( ber, filter );
+
+ if ( err == -1 ) {
+ ld->ld_errno = LDAP_FILTER_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+#ifdef LDAP_DEBUG
+ if ( ldap_debug & LDAP_DEBUG_ARGS ) {
+ char buf[ BUFSIZ ], *ptr = " *";
+
+ if ( attrs != NULL ) {
+ int i, len, rest = sizeof( buf );
+
+ for ( i = 0; attrs[ i ] != NULL && rest > 0; i++ ) {
+ ptr = &buf[ sizeof( buf ) - rest ];
+ len = snprintf( ptr, rest, " %s", attrs[ i ] );
+ rest -= (len >= 0 ? len : (int) sizeof( buf ));
+ }
+
+ if ( rest <= 0 ) {
+ AC_MEMCPY( &buf[ sizeof( buf ) - STRLENOF( "...(truncated)" ) - 1 ],
+ "...(truncated)", STRLENOF( "...(truncated)" ) + 1 );
+ }
+ ptr = buf;
+ }
+
+ Debug1( LDAP_DEBUG_ARGS, "ldap_build_search_req ATTRS:%s\n", ptr );
+ }
+#endif /* LDAP_DEBUG */
+
+ if ( ber_printf( ber, /*{*/ "{v}N}", attrs ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ /* Put Server Controls */
+ if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ return( ber );
+}
+
+int
+ldap_search_st(
+ LDAP *ld, LDAP_CONST char *base, int scope,
+ LDAP_CONST char *filter, char **attrs,
+ int attrsonly, struct timeval *timeout, LDAPMessage **res )
+{
+ int msgid;
+
+ *res = NULL;
+
+ if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
+ == -1 )
+ return( ld->ld_errno );
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, timeout, res ) == -1 || !*res )
+ return( ld->ld_errno );
+
+ if ( ld->ld_errno == LDAP_TIMEOUT ) {
+ (void) ldap_abandon( ld, msgid );
+ ld->ld_errno = LDAP_TIMEOUT;
+ return( ld->ld_errno );
+ }
+
+ return( ldap_result2error( ld, *res, 0 ) );
+}
+
+int
+ldap_search_s(
+ LDAP *ld,
+ LDAP_CONST char *base,
+ int scope,
+ LDAP_CONST char *filter,
+ char **attrs,
+ int attrsonly,
+ LDAPMessage **res )
+{
+ int msgid;
+
+ *res = NULL;
+
+ if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
+ == -1 )
+ return( ld->ld_errno );
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, res ) == -1 || !*res )
+ return( ld->ld_errno );
+
+ return( ldap_result2error( ld, *res, 0 ) );
+}
+
+static char escape[128] = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0, 0, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1
+};
+#define NEEDFLTESCAPE(c) ((c) & 0x80 || escape[ (unsigned)(c) ])
+
+/*
+ * compute the length of the escaped value
+ */
+ber_len_t
+ldap_bv2escaped_filter_value_len( struct berval *in )
+{
+ ber_len_t i, l;
+
+ assert( in != NULL );
+
+ if ( in->bv_len == 0 ) {
+ return 0;
+ }
+
+ for( l = 0, i = 0; i < in->bv_len; l++, i++ ) {
+ char c = in->bv_val[ i ];
+ if ( NEEDFLTESCAPE( c ) ) {
+ l += 2;
+ }
+ }
+
+ return l;
+}
+
+int
+ldap_bv2escaped_filter_value( struct berval *in, struct berval *out )
+{
+ return ldap_bv2escaped_filter_value_x( in, out, 0, NULL );
+}
+
+int
+ldap_bv2escaped_filter_value_x( struct berval *in, struct berval *out, int inplace, void *ctx )
+{
+ ber_len_t i, l;
+
+ assert( in != NULL );
+ assert( out != NULL );
+
+ BER_BVZERO( out );
+
+ if ( in->bv_len == 0 ) {
+ return 0;
+ }
+
+ /* assume we'll escape everything */
+ l = ldap_bv2escaped_filter_value_len( in );
+ if ( l == in->bv_len ) {
+ if ( inplace ) {
+ *out = *in;
+ } else {
+ ber_dupbv( out, in );
+ }
+ return 0;
+ }
+ out->bv_val = LDAP_MALLOCX( l + 1, ctx );
+ if ( out->bv_val == NULL ) {
+ return -1;
+ }
+
+ for ( i = 0; i < in->bv_len; i++ ) {
+ char c = in->bv_val[ i ];
+ if ( NEEDFLTESCAPE( c ) ) {
+ assert( out->bv_len < l - 2 );
+ out->bv_val[out->bv_len++] = '\\';
+ out->bv_val[out->bv_len++] = "0123456789ABCDEF"[0x0f & (c>>4)];
+ out->bv_val[out->bv_len++] = "0123456789ABCDEF"[0x0f & c];
+
+ } else {
+ assert( out->bv_len < l );
+ out->bv_val[out->bv_len++] = c;
+ }
+ }
+
+ out->bv_val[out->bv_len] = '\0';
+
+ return 0;
+}
+
diff --git a/libraries/libldap/sort.c b/libraries/libldap/sort.c
new file mode 100644
index 0000000..6159be8
--- /dev/null
+++ b/libraries/libldap/sort.c
@@ -0,0 +1,183 @@
+/* sort.c -- LDAP library entry and value sort routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1994 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+
+#include "ldap-int.h"
+
+struct entrything {
+ char **et_vals;
+ LDAPMessage *et_msg;
+ int (*et_cmp_fn) LDAP_P((const char *a, const char *b));
+};
+
+static int et_cmp LDAP_P(( const void *aa, const void *bb));
+
+
+int
+ldap_sort_strcasecmp(
+ LDAP_CONST void *a,
+ LDAP_CONST void *b
+)
+{
+ return( strcasecmp( *(char *const *)a, *(char *const *)b ) );
+}
+
+static int
+et_cmp(
+ const void *aa,
+ const void *bb
+)
+{
+ int i, rc;
+ const struct entrything *a = (const struct entrything *)aa;
+ const struct entrything *b = (const struct entrything *)bb;
+
+ if ( a->et_vals == NULL && b->et_vals == NULL )
+ return( 0 );
+ if ( a->et_vals == NULL )
+ return( -1 );
+ if ( b->et_vals == NULL )
+ return( 1 );
+
+ for ( i = 0; a->et_vals[i] && b->et_vals[i]; i++ ) {
+ if ( (rc = a->et_cmp_fn( a->et_vals[i], b->et_vals[i] )) != 0 ) {
+ return( rc );
+ }
+ }
+
+ if ( a->et_vals[i] == NULL && b->et_vals[i] == NULL )
+ return( 0 );
+ if ( a->et_vals[i] == NULL )
+ return( -1 );
+ return( 1 );
+}
+
+int
+ldap_sort_entries(
+ LDAP *ld,
+ LDAPMessage **chain,
+ LDAP_CONST char *attr, /* NULL => sort by DN */
+ int (*cmp) (LDAP_CONST char *, LDAP_CONST char *)
+)
+{
+ int i, count = 0;
+ struct entrything *et;
+ LDAPMessage *e, *ehead = NULL, *etail = NULL;
+ LDAPMessage *ohead = NULL, *otail = NULL;
+ LDAPMessage **ep;
+
+ assert( ld != NULL );
+
+ /* Separate entries from non-entries */
+ for ( e = *chain; e; e=e->lm_chain ) {
+ if ( e->lm_msgtype == LDAP_RES_SEARCH_ENTRY ) {
+ count++;
+ if ( !ehead ) ehead = e;
+ if ( etail ) etail->lm_chain = e;
+ etail = e;
+ } else {
+ if ( !ohead ) ohead = e;
+ if ( otail ) otail->lm_chain = e;
+ otail = e;
+ }
+ }
+
+ if ( count < 2 ) {
+ /* zero or one entries -- already sorted! */
+ if ( ehead ) {
+ etail->lm_chain = ohead;
+ *chain = ehead;
+ } else {
+ *chain = ohead;
+ }
+ return 0;
+ }
+
+ if ( (et = (struct entrything *) LDAP_MALLOC( count *
+ sizeof(struct entrything) )) == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return( -1 );
+ }
+
+ e = ehead;
+ for ( i = 0; i < count; i++ ) {
+ et[i].et_cmp_fn = cmp;
+ et[i].et_msg = e;
+ if ( attr == NULL ) {
+ char *dn;
+
+ dn = ldap_get_dn( ld, e );
+ et[i].et_vals = ldap_explode_dn( dn, 1 );
+ LDAP_FREE( dn );
+ } else {
+ et[i].et_vals = ldap_get_values( ld, e, attr );
+ }
+
+ e = e->lm_chain;
+ }
+
+ qsort( et, count, sizeof(struct entrything), et_cmp );
+
+ ep = chain;
+ for ( i = 0; i < count; i++ ) {
+ *ep = et[i].et_msg;
+ ep = &(*ep)->lm_chain;
+
+ LDAP_VFREE( et[i].et_vals );
+ }
+ *ep = ohead;
+ (*chain)->lm_chain_tail = otail ? otail : etail;
+
+ LDAP_FREE( (char *) et );
+
+ return( 0 );
+}
+
+int
+ldap_sort_values(
+ LDAP *ld,
+ char **vals,
+ int (*cmp) (LDAP_CONST void *, LDAP_CONST void *)
+)
+{
+ int nel;
+
+ for ( nel = 0; vals[nel] != NULL; nel++ )
+ ; /* NULL */
+
+ qsort( vals, nel, sizeof(char *), cmp );
+
+ return( 0 );
+}
diff --git a/libraries/libldap/sortctrl.c b/libraries/libldap/sortctrl.c
new file mode 100644
index 0000000..537dac5
--- /dev/null
+++ b/libraries/libldap/sortctrl.c
@@ -0,0 +1,552 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
+ *
+ * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
+ * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT
+ * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS
+ * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE"
+ * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION
+ * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP
+ * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT
+ * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
+ */
+/* Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
+ * can be found in the file "build/LICENSE-2.0.1" in this distribution
+ * of OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#define LDAP_MATCHRULE_IDENTIFIER 0x80L
+#define LDAP_REVERSEORDER_IDENTIFIER 0x81L
+#define LDAP_ATTRTYPES_IDENTIFIER 0x80L
+
+
+
+/* ---------------------------------------------------------------------------
+ countKeys
+
+ Internal function to determine the number of keys in the string.
+
+ keyString (IN) String of items separated by whitespace.
+ ---------------------------------------------------------------------------*/
+
+static int countKeys(char *keyString)
+{
+ char *p = keyString;
+ int count = 0;
+
+ for (;;)
+ {
+ while (LDAP_SPACE(*p)) /* Skip leading whitespace */
+ p++;
+
+ if (*p == '\0') /* End of string? */
+ return count;
+
+ count++; /* Found start of a key */
+
+ while (!LDAP_SPACE(*p)) /* Skip till next space or end of string. */
+ if (*p++ == '\0')
+ return count;
+ }
+}
+
+
+/* ---------------------------------------------------------------------------
+ readNextKey
+
+ Internal function to parse the next sort key in the string.
+ Allocate an LDAPSortKey structure and initialize it with
+ attribute name, reverse flag, and matching rule OID.
+
+ Each sort key in the string has the format:
+ [whitespace][-]attribute[:[OID]]
+
+ pNextKey (IN/OUT) Points to the next key in the sortkey string to parse.
+ The pointer is updated to point to the next character
+ after the sortkey being parsed.
+
+ key (OUT) Points to the address of an LDAPSortKey structure
+ which has been allocated by this routine and
+ initialized with information from the next sortkey.
+ ---------------------------------------------------------------------------*/
+
+static int readNextKey( char **pNextKey, LDAPSortKey **key)
+{
+ char *p = *pNextKey;
+ int rev = 0;
+ char *attrStart;
+ int attrLen;
+ char *oidStart = NULL;
+ int oidLen = 0;
+
+ /* Skip leading white space. */
+ while (LDAP_SPACE(*p))
+ p++;
+
+ if (*p == '-') /* Check if the reverse flag is present. */
+ {
+ rev=1;
+ p++;
+ }
+
+ /* We're now positioned at the start of the attribute. */
+ attrStart = p;
+
+ /* Get the length of the attribute until the next whitespace or ":". */
+ attrLen = strcspn(p, " \t:");
+ p += attrLen;
+
+ if (attrLen == 0) /* If no attribute name was present, quit. */
+ return LDAP_PARAM_ERROR;
+
+ if (*p == ':')
+ {
+ oidStart = ++p; /* Start of the OID, after the colon */
+ oidLen = strcspn(p, " \t"); /* Get length of OID till next whitespace */
+ p += oidLen;
+ }
+
+ *pNextKey = p; /* Update argument to point to next key */
+
+ /* Allocate an LDAPSortKey structure */
+ *key = LDAP_MALLOC(sizeof(LDAPSortKey));
+ if (*key == NULL) return LDAP_NO_MEMORY;
+
+ /* Allocate memory for the attribute and copy to it. */
+ (*key)->attributeType = LDAP_MALLOC(attrLen+1);
+ if ((*key)->attributeType == NULL) {
+ LDAP_FREE(*key);
+ return LDAP_NO_MEMORY;
+ }
+
+ strncpy((*key)->attributeType, attrStart, attrLen);
+ (*key)->attributeType[attrLen] = 0;
+
+ /* If present, allocate memory for the OID and copy to it. */
+ if (oidLen) {
+ (*key)->orderingRule = LDAP_MALLOC(oidLen+1);
+ if ((*key)->orderingRule == NULL) {
+ LDAP_FREE((*key)->attributeType);
+ LDAP_FREE(*key);
+ return LDAP_NO_MEMORY;
+ }
+ strncpy((*key)->orderingRule, oidStart, oidLen);
+ (*key)->orderingRule[oidLen] = 0;
+
+ } else {
+ (*key)->orderingRule = NULL;
+ }
+
+ (*key)->reverseOrder = rev;
+
+ return LDAP_SUCCESS;
+}
+
+
+/* ---------------------------------------------------------------------------
+ ldap_create_sort_keylist
+
+ Create an array of pointers to LDAPSortKey structures, containing the
+ information specified by the string representation of one or more
+ sort keys.
+
+ sortKeyList (OUT) Points to a null-terminated array of pointers to
+ LDAPSortKey structures allocated by this routine.
+ This memory SHOULD be freed by the calling program
+ using ldap_free_sort_keylist().
+
+ keyString (IN) Points to a string of one or more sort keys.
+
+ ---------------------------------------------------------------------------*/
+
+int
+ldap_create_sort_keylist ( LDAPSortKey ***sortKeyList, char *keyString )
+{
+ int numKeys, rc, i;
+ char *nextKey;
+ LDAPSortKey **keyList = NULL;
+
+ assert( sortKeyList != NULL );
+ assert( keyString != NULL );
+
+ *sortKeyList = NULL;
+
+ /* Determine the number of sort keys so we can allocate memory. */
+ if (( numKeys = countKeys(keyString)) == 0) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ /* Allocate the array of pointers. Initialize to NULL. */
+ keyList=(LDAPSortKey**)LBER_CALLOC(numKeys+1, sizeof(LDAPSortKey*));
+ if ( keyList == NULL) return LDAP_NO_MEMORY;
+
+ /* For each sort key in the string, create an LDAPSortKey structure
+ and add it to the list.
+ */
+ nextKey = keyString; /* Points to the next key in the string */
+ for (i=0; i < numKeys; i++) {
+ rc = readNextKey(&nextKey, &keyList[i]);
+
+ if (rc != LDAP_SUCCESS) {
+ ldap_free_sort_keylist(keyList);
+ return rc;
+ }
+ }
+
+ *sortKeyList = keyList;
+ return LDAP_SUCCESS;
+}
+
+
+/* ---------------------------------------------------------------------------
+ ldap_free_sort_keylist
+
+ Frees the sort key structures created by ldap_create_sort_keylist().
+ Frees the memory referenced by the LDAPSortKey structures,
+ the LDAPSortKey structures themselves, and the array of pointers
+ to the structures.
+
+ keyList (IN) Points to an array of pointers to LDAPSortKey structures.
+ ---------------------------------------------------------------------------*/
+
+void
+ldap_free_sort_keylist ( LDAPSortKey **keyList )
+{
+ int i;
+ LDAPSortKey *nextKeyp;
+
+ if (keyList == NULL) return;
+
+ i=0;
+ while ( 0 != (nextKeyp = keyList[i++]) ) {
+ if (nextKeyp->attributeType) {
+ LBER_FREE(nextKeyp->attributeType);
+ }
+
+ if (nextKeyp->orderingRule != NULL) {
+ LBER_FREE(nextKeyp->orderingRule);
+ }
+
+ LBER_FREE(nextKeyp);
+ }
+
+ LBER_FREE(keyList);
+}
+
+
+/* ---------------------------------------------------------------------------
+ ldap_create_sort_control_value
+
+ Create and encode the value of the server-side sort control.
+
+ ld (IN) An LDAP session handle, as obtained from a call to
+ ldap_init().
+
+ keyList (IN) Points to a null-terminated array of pointers to
+ LDAPSortKey structures, containing a description of
+ each of the sort keys to be used. The description
+ consists of an attribute name, ascending/descending flag,
+ and an optional matching rule (OID) to use.
+
+ value (OUT) Contains the control value; the bv_val member of the berval structure
+ SHOULD be freed by calling ldap_memfree() when done.
+
+
+ Ber encoding
+
+ SortKeyList ::= SEQUENCE OF SEQUENCE {
+ attributeType AttributeDescription,
+ orderingRule [0] MatchingRuleId OPTIONAL,
+ reverseOrder [1] BOOLEAN DEFAULT FALSE }
+
+ ---------------------------------------------------------------------------*/
+
+int
+ldap_create_sort_control_value(
+ LDAP *ld,
+ LDAPSortKey **keyList,
+ struct berval *value )
+{
+ int i;
+ BerElement *ber = NULL;
+ ber_tag_t tag;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ if ( ld == NULL ) return LDAP_PARAM_ERROR;
+ if ( keyList == NULL || value == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return LDAP_PARAM_ERROR;
+ }
+
+ value->bv_val = NULL;
+ value->bv_len = 0;
+ ld->ld_errno = LDAP_SUCCESS;
+
+ ber = ldap_alloc_ber_with_options( ld );
+ if ( ber == NULL) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ tag = ber_printf( ber, "{" /*}*/ );
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+
+ for ( i = 0; keyList[i] != NULL; i++ ) {
+ tag = ber_printf( ber, "{s" /*}*/, keyList[i]->attributeType );
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+
+ if ( keyList[i]->orderingRule != NULL ) {
+ tag = ber_printf( ber, "ts",
+ LDAP_MATCHRULE_IDENTIFIER,
+ keyList[i]->orderingRule );
+
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+ }
+
+ if ( keyList[i]->reverseOrder ) {
+ tag = ber_printf( ber, "tb",
+ LDAP_REVERSEORDER_IDENTIFIER,
+ keyList[i]->reverseOrder );
+
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+ }
+
+ tag = ber_printf( ber, /*{*/ "N}" );
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+ }
+
+ tag = ber_printf( ber, /*{*/ "N}" );
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+
+ if ( ber_flatten2( ber, value, 1 ) == -1 ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ }
+
+ if ( 0 ) {
+error_return:;
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ }
+
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ return ld->ld_errno;
+}
+
+
+/* ---------------------------------------------------------------------------
+ ldap_create_sort_control
+
+ Create and encode the server-side sort control.
+
+ ld (IN) An LDAP session handle, as obtained from a call to
+ ldap_init().
+
+ keyList (IN) Points to a null-terminated array of pointers to
+ LDAPSortKey structures, containing a description of
+ each of the sort keys to be used. The description
+ consists of an attribute name, ascending/descending flag,
+ and an optional matching rule (OID) to use.
+
+ isCritical (IN) 0 - Indicates the control is not critical to the operation.
+ non-zero - The control is critical to the operation.
+
+ ctrlp (OUT) Returns a pointer to the LDAPControl created. This control
+ SHOULD be freed by calling ldap_control_free() when done.
+
+
+ Ber encoding
+
+ SortKeyList ::= SEQUENCE OF SEQUENCE {
+ attributeType AttributeDescription,
+ orderingRule [0] MatchingRuleId OPTIONAL,
+ reverseOrder [1] BOOLEAN DEFAULT FALSE }
+
+ ---------------------------------------------------------------------------*/
+
+int
+ldap_create_sort_control(
+ LDAP *ld,
+ LDAPSortKey **keyList,
+ int isCritical,
+ LDAPControl **ctrlp )
+{
+ struct berval value;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ if ( ld == NULL ) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ if ( ctrlp == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ ld->ld_errno = ldap_create_sort_control_value( ld, keyList, &value );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ ld->ld_errno = ldap_control_create( LDAP_CONTROL_SORTREQUEST,
+ isCritical, &value, 0, ctrlp );
+ if ( ld->ld_errno != LDAP_SUCCESS ) {
+ LDAP_FREE( value.bv_val );
+ }
+ }
+
+ return ld->ld_errno;
+}
+
+
+/* ---------------------------------------------------------------------------
+ ldap_parse_sortedresult_control
+
+ Decode the server-side sort control return information.
+
+ ld (IN) An LDAP session handle, as obtained from a call to
+ ldap_init().
+
+ ctrl (IN) The address of the LDAP Control Structure.
+
+ returnCode (OUT) This result parameter is filled in with the sort control
+ result code. This parameter MUST not be NULL.
+
+ attribute (OUT) If an error occurred the server may return a string
+ indicating the first attribute in the sortkey list
+ that was in error. If a string is returned, the memory
+ should be freed with ldap_memfree. If this parameter is
+ NULL, no string is returned.
+
+
+ Ber encoding for sort control
+
+ SortResult ::= SEQUENCE {
+ sortResult ENUMERATED {
+ success (0), -- results are sorted
+ operationsError (1), -- server internal failure
+ timeLimitExceeded (3), -- timelimit reached before
+ -- sorting was completed
+ strongAuthRequired (8), -- refused to return sorted
+ -- results via insecure
+ -- protocol
+ adminLimitExceeded (11), -- too many matching entries
+ -- for the server to sort
+ noSuchAttribute (16), -- unrecognized attribute
+ -- type in sort key
+ inappropriateMatching (18), -- unrecognized or inappro-
+ -- priate matching rule in
+ -- sort key
+ insufficientAccessRights (50), -- refused to return sorted
+ -- results to this client
+ busy (51), -- too busy to process
+ unwillingToPerform (53), -- unable to sort
+ other (80)
+ },
+ attributeType [0] AttributeDescription OPTIONAL }
+ ---------------------------------------------------------------------------*/
+
+int
+ldap_parse_sortresponse_control(
+ LDAP *ld,
+ LDAPControl *ctrl,
+ ber_int_t *returnCode,
+ char **attribute )
+{
+ BerElement *ber;
+ ber_tag_t tag, berTag;
+ ber_len_t berLen;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ if (ld == NULL) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ if (ctrl == NULL) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return(ld->ld_errno);
+ }
+
+ if (attribute) {
+ *attribute = NULL;
+ }
+
+ if ( strcmp(LDAP_CONTROL_SORTRESPONSE, ctrl->ldctl_oid) != 0 ) {
+ /* Not sort result control */
+ ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
+ return(ld->ld_errno);
+ }
+
+ /* Create a BerElement from the berval returned in the control. */
+ ber = ber_init(&ctrl->ldctl_value);
+
+ if (ber == NULL) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return(ld->ld_errno);
+ }
+
+ /* Extract the result code from the control. */
+ tag = ber_scanf(ber, "{e" /*}*/, returnCode);
+
+ if( tag == LBER_ERROR ) {
+ ber_free(ber, 1);
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+ }
+
+ /* If caller wants the attribute name, and if it's present in the control,
+ extract the attribute name which caused the error. */
+ if (attribute && (LDAP_ATTRTYPES_IDENTIFIER == ber_peek_tag(ber, &berLen)))
+ {
+ tag = ber_scanf(ber, "ta", &berTag, attribute);
+
+ if (tag == LBER_ERROR ) {
+ ber_free(ber, 1);
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+ }
+ }
+
+ ber_free(ber,1);
+
+ ld->ld_errno = LDAP_SUCCESS;
+ return(ld->ld_errno);
+}
diff --git a/libraries/libldap/stctrl.c b/libraries/libldap/stctrl.c
new file mode 100644
index 0000000..0299f83
--- /dev/null
+++ b/libraries/libldap/stctrl.c
@@ -0,0 +1,302 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2007 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Pierangelo Masarati for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#ifdef LDAP_CONTROL_X_SESSION_TRACKING
+
+/*
+ * Client-side of <draft-wahl-ldap-session-03>
+ */
+
+int
+ldap_create_session_tracking_value(
+ LDAP *ld,
+ char *sessionSourceIp,
+ char *sessionSourceName,
+ char *formatOID,
+ struct berval *sessionTrackingIdentifier,
+ struct berval *value )
+{
+ BerElement *ber = NULL;
+ ber_tag_t tag;
+
+ struct berval ip, name, oid, id;
+
+ if ( ld == NULL ||
+ formatOID == NULL ||
+ value == NULL )
+ {
+param_error:;
+ if ( ld ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ }
+
+ return LDAP_PARAM_ERROR;
+ }
+
+ assert( LDAP_VALID( ld ) );
+ ld->ld_errno = LDAP_SUCCESS;
+
+ /* check sizes according to I.D. */
+ if ( sessionSourceIp == NULL ) {
+ BER_BVSTR( &ip, "" );
+
+ } else {
+ ber_str2bv( sessionSourceIp, 0, 0, &ip );
+ /* NOTE: we're strict because we don't want
+ * to send out bad data */
+ if ( ip.bv_len > 128 ) goto param_error;
+ }
+
+ if ( sessionSourceName == NULL ) {
+ BER_BVSTR( &name, "" );
+
+ } else {
+ ber_str2bv( sessionSourceName, 0, 0, &name );
+ /* NOTE: we're strict because we don't want
+ * to send out bad data */
+ if ( name.bv_len > 65536 ) goto param_error;
+ }
+
+ ber_str2bv( formatOID, 0, 0, &oid );
+ /* NOTE: we're strict because we don't want
+ * to send out bad data */
+ if ( oid.bv_len > 1024 ) goto param_error;
+
+ if ( sessionTrackingIdentifier == NULL ||
+ sessionTrackingIdentifier->bv_val == NULL )
+ {
+ BER_BVSTR( &id, "" );
+
+ } else {
+ id = *sessionTrackingIdentifier;
+ }
+
+ /* prepare value */
+ value->bv_val = NULL;
+ value->bv_len = 0;
+
+ ber = ldap_alloc_ber_with_options( ld );
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ tag = ber_printf( ber, "{OOOO}", &ip, &name, &oid, &id );
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+
+ if ( ber_flatten2( ber, value, 1 ) == -1 ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ }
+
+done:;
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ return ld->ld_errno;
+}
+
+/*
+ * NOTE: this API is bad; it could be much more efficient...
+ */
+int
+ldap_create_session_tracking_control(
+ LDAP *ld,
+ char *sessionSourceIp,
+ char *sessionSourceName,
+ char *formatOID,
+ struct berval *sessionTrackingIdentifier,
+ LDAPControl **ctrlp )
+{
+ struct berval value;
+
+ if ( ctrlp == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ ld->ld_errno = ldap_create_session_tracking_value( ld,
+ sessionSourceIp, sessionSourceName, formatOID,
+ sessionTrackingIdentifier, &value );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ ld->ld_errno = ldap_control_create( LDAP_CONTROL_X_SESSION_TRACKING,
+ 0, &value, 0, ctrlp );
+ if ( ld->ld_errno != LDAP_SUCCESS ) {
+ LDAP_FREE( value.bv_val );
+ }
+ }
+
+ return ld->ld_errno;
+}
+
+int
+ldap_parse_session_tracking_control(
+ LDAP *ld,
+ LDAPControl *ctrl,
+ struct berval *ip,
+ struct berval *name,
+ struct berval *oid,
+ struct berval *id )
+{
+ BerElement *ber;
+ ber_tag_t tag;
+ ber_len_t len;
+
+ if ( ld == NULL ||
+ ctrl == NULL ||
+ ip == NULL ||
+ name == NULL ||
+ oid == NULL ||
+ id == NULL )
+ {
+ if ( ld ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ }
+
+ /* NOTE: we want the caller to get all or nothing;
+ * we could allow some of the pointers to be NULL,
+ * if one does not want part of the data */
+ return LDAP_PARAM_ERROR;
+ }
+
+ BER_BVZERO( ip );
+ BER_BVZERO( name );
+ BER_BVZERO( oid );
+ BER_BVZERO( id );
+
+ ber = ber_init( &ctrl->ldctl_value );
+
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ tag = ber_skip_tag( ber, &len );
+ if ( tag != LBER_SEQUENCE ) {
+ tag = LBER_ERROR;
+ goto error;
+ }
+
+ /* sessionSourceIp */
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LBER_DEFAULT ) {
+ tag = LBER_ERROR;
+ goto error;
+ }
+
+ if ( len == 0 ) {
+ tag = ber_skip_tag( ber, &len );
+
+ } else {
+ if ( len > 128 ) {
+ /* should be LDAP_DECODING_ERROR,
+ * but we're liberal in what we accept */
+ }
+ tag = ber_scanf( ber, "o", ip );
+ }
+
+ /* sessionSourceName */
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LBER_DEFAULT ) {
+ tag = LBER_ERROR;
+ goto error;
+ }
+
+ if ( len == 0 ) {
+ tag = ber_skip_tag( ber, &len );
+
+ } else {
+ if ( len > 65536 ) {
+ /* should be LDAP_DECODING_ERROR,
+ * but we're liberal in what we accept */
+ }
+ tag = ber_scanf( ber, "o", name );
+ }
+
+ /* formatOID */
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LBER_DEFAULT ) {
+ tag = LBER_ERROR;
+ goto error;
+ }
+
+ if ( len == 0 ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ goto error;
+
+ } else {
+ if ( len > 1024 ) {
+ /* should be LDAP_DECODING_ERROR,
+ * but we're liberal in what we accept */
+ }
+ tag = ber_scanf( ber, "o", oid );
+ }
+
+ /* FIXME: should check if it is an OID... leave it to the caller */
+
+ /* sessionTrackingIdentifier */
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LBER_DEFAULT ) {
+ tag = LBER_ERROR;
+ goto error;
+ }
+
+ if ( len == 0 ) {
+ tag = ber_skip_tag( ber, &len );
+
+ } else {
+#if 0
+ if ( len > 65536 ) {
+ /* should be LDAP_DECODING_ERROR,
+ * but we're liberal in what we accept */
+ }
+#endif
+ tag = ber_scanf( ber, "o", id );
+ }
+
+ /* closure */
+ tag = ber_skip_tag( ber, &len );
+ if ( tag == LBER_DEFAULT && len == 0 ) {
+ tag = 0;
+ }
+
+error:;
+ (void)ber_free( ber, 1 );
+
+ if ( tag == LBER_ERROR ) {
+ return LDAP_DECODING_ERROR;
+ }
+
+ return ld->ld_errno;
+}
+
+#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
diff --git a/libraries/libldap/string.c b/libraries/libldap/string.c
new file mode 100644
index 0000000..92f37d3
--- /dev/null
+++ b/libraries/libldap/string.c
@@ -0,0 +1,177 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * Locale-specific 1-byte character versions
+ * See utf-8.c for UTF-8 versions
+ */
+
+#include "portable.h"
+
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/ctype.h>
+
+#include "ldap-int.h"
+
+
+#if defined ( HAVE_STRSPN )
+#define int_strspn strspn
+#else
+static int int_strspn( const char *str, const char *delim )
+{
+ int pos;
+ const char *p=delim;
+
+ for( pos=0; (*str) ; pos++,str++) {
+ if (*str!=*p) {
+ for( p=delim; (*p) ; p++ ) {
+ if (*str==*p) {
+ break;
+ }
+ }
+ }
+
+ if (*p=='\0') {
+ return pos;
+ }
+ }
+ return pos;
+}
+#endif
+
+#if defined( HAVE_STRPBRK )
+#define int_strpbrk strpbrk
+#else
+static char *(int_strpbrk)( const char *str, const char *accept )
+{
+ const char *p;
+
+ for( ; (*str) ; str++ ) {
+ for( p=accept; (*p) ; p++) {
+ if (*str==*p) {
+ return str;
+ }
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+char *(ldap_pvt_strtok)( char *str, const char *delim, char **pos )
+{
+ char *p;
+
+ if (pos==NULL) {
+ return NULL;
+ }
+
+ if (str==NULL) {
+ if (*pos==NULL) {
+ return NULL;
+ }
+
+ str=*pos;
+ }
+
+ /* skip any initial delimiters */
+ str += int_strspn( str, delim );
+ if (*str == '\0') {
+ return NULL;
+ }
+
+ p = int_strpbrk( str, delim );
+ if (p==NULL) {
+ *pos = NULL;
+
+ } else {
+ *p ='\0';
+ *pos = p+1;
+ }
+
+ return str;
+}
+
+char *
+ldap_pvt_str2upper( char *str )
+{
+ char *s;
+
+ /* to upper */
+ if ( str ) {
+ for ( s = str; *s; s++ ) {
+ *s = TOUPPER( (unsigned char) *s );
+ }
+ }
+
+ return( str );
+}
+
+struct berval *
+ldap_pvt_str2upperbv( char *str, struct berval *bv )
+{
+ char *s = NULL;
+
+ assert( bv != NULL );
+
+ /* to upper */
+ if ( str ) {
+ for ( s = str; *s; s++ ) {
+ *s = TOUPPER( (unsigned char) *s );
+ }
+ }
+
+ bv->bv_val = str;
+ bv->bv_len = (ber_len_t)(s - str);
+
+ return( bv );
+}
+
+char *
+ldap_pvt_str2lower( char *str )
+{
+ char *s;
+
+ /* to lower */
+ if ( str ) {
+ for ( s = str; *s; s++ ) {
+ *s = TOLOWER( (unsigned char) *s );
+ }
+ }
+
+ return( str );
+}
+
+struct berval *
+ldap_pvt_str2lowerbv( char *str, struct berval *bv )
+{
+ char *s = NULL;
+
+ assert( bv != NULL );
+
+ /* to lower */
+ if ( str ) {
+ for ( s = str; *s; s++ ) {
+ *s = TOLOWER( (unsigned char) *s );
+ }
+ }
+
+ bv->bv_val = str;
+ bv->bv_len = (ber_len_t)(s - str);
+
+ return( bv );
+}
diff --git a/libraries/libldap/t61.c b/libraries/libldap/t61.c
new file mode 100644
index 0000000..6aa609a
--- /dev/null
+++ b/libraries/libldap/t61.c
@@ -0,0 +1,692 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion in
+ * OpenLDAP Software.
+ */
+
+/*
+ * Basic T.61 <-> UTF-8 conversion
+ *
+ * These routines will perform a lossless translation from T.61 to UTF-8
+ * and a lossy translation from UTF-8 to T.61.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_utf8.h"
+
+#include "ldap_defaults.h"
+
+/*
+ * T.61 is somewhat braindead; even in the 7-bit space it is not
+ * completely equivalent to 7-bit US-ASCII. Our definition of the
+ * character set comes from RFC 1345 with a slightly more readable
+ * rendition at http://std.dkuug.dk/i18n/charmaps/T.61-8BIT.
+ *
+ * Even though '#' and '$' are present in the 7-bit US-ASCII space,
+ * (x23 and x24, resp.) in T.61 they are mapped to 8-bit characters
+ * xA6 and xA4.
+ *
+ * Also T.61 lacks
+ * backslash \ (x5C)
+ * caret ^ (x5E)
+ * backquote ` (x60)
+ * left brace { (x7B)
+ * right brace } (x7D)
+ * tilde ~ (x7E)
+ *
+ * In T.61, the codes xC1 to xCF (excluding xC9, unused) are non-spacing
+ * accents of some form or another. There are predefined combinations
+ * for certain characters, but they can also be used arbitrarily. The
+ * table at dkuug.dk maps these accents to the E000 "private use" range
+ * of the Unicode space, but I believe they more properly belong in the
+ * 0300 range (non-spacing accents). The transformation is complicated
+ * slightly because Unicode wants the non-spacing character to follow
+ * the base character, while T.61 has the non-spacing character leading.
+ * Also, T.61 specifically recognizes certain combined pairs as "characters"
+ * but doesn't specify how to treat unrecognized pairs. This code will
+ * always attempt to combine pairs when a known Unicode composite exists.
+ */
+
+static const wchar_t t61_tab[] = {
+ 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007,
+ 0x008, 0x009, 0x00a, 0x00b, 0x00c, 0x00d, 0x00e, 0x00f,
+ 0x010, 0x011, 0x012, 0x013, 0x014, 0x015, 0x016, 0x017,
+ 0x018, 0x019, 0x01a, 0x01b, 0x01c, 0x01d, 0x01e, 0x01f,
+ 0x020, 0x021, 0x022, 0x000, 0x000, 0x025, 0x026, 0x027,
+ 0x028, 0x029, 0x02a, 0x02b, 0x02c, 0x02d, 0x02e, 0x02f,
+ 0x030, 0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037,
+ 0x038, 0x039, 0x03a, 0x03b, 0x03c, 0x03d, 0x03e, 0x03f,
+ 0x040, 0x041, 0x042, 0x043, 0x044, 0x045, 0x046, 0x047,
+ 0x048, 0x049, 0x04a, 0x04b, 0x04c, 0x04d, 0x04e, 0x04f,
+ 0x050, 0x051, 0x052, 0x053, 0x054, 0x055, 0x056, 0x057,
+ 0x058, 0x059, 0x05a, 0x05b, 0x000, 0x05d, 0x000, 0x05f,
+ 0x000, 0x061, 0x062, 0x063, 0x064, 0x065, 0x066, 0x067,
+ 0x068, 0x069, 0x06a, 0x06b, 0x06c, 0x06d, 0x06e, 0x06f,
+ 0x070, 0x071, 0x072, 0x073, 0x074, 0x075, 0x076, 0x077,
+ 0x078, 0x079, 0x07a, 0x000, 0x07c, 0x000, 0x000, 0x07f,
+ 0x080, 0x081, 0x082, 0x083, 0x084, 0x085, 0x086, 0x087,
+ 0x088, 0x089, 0x08a, 0x08b, 0x08c, 0x08d, 0x08e, 0x08f,
+ 0x090, 0x091, 0x092, 0x093, 0x094, 0x095, 0x096, 0x097,
+ 0x098, 0x099, 0x09a, 0x09b, 0x09c, 0x09d, 0x09e, 0x09f,
+ 0x0a0, 0x0a1, 0x0a2, 0x0a3, 0x024, 0x0a5, 0x023, 0x0a7,
+ 0x0a4, 0x000, 0x000, 0x0ab, 0x000, 0x000, 0x000, 0x000,
+ 0x0b0, 0x0b1, 0x0b2, 0x0b3, 0x0d7, 0x0b5, 0x0b6, 0x0b7,
+ 0x0f7, 0x000, 0x000, 0x0bb, 0x0bc, 0x0bd, 0x0be, 0x0bf,
+ 0x000, 0x300, 0x301, 0x302, 0x303, 0x304, 0x306, 0x307,
+ 0x308, 0x000, 0x30a, 0x327, 0x332, 0x30b, 0x328, 0x30c,
+ 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
+ 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
+ 0x2126, 0xc6, 0x0d0, 0x0aa, 0x126, 0x000, 0x132, 0x13f,
+ 0x141, 0x0d8, 0x152, 0x0ba, 0x0de, 0x166, 0x14a, 0x149,
+ 0x138, 0x0e6, 0x111, 0x0f0, 0x127, 0x131, 0x133, 0x140,
+ 0x142, 0x0f8, 0x153, 0x0df, 0x0fe, 0x167, 0x14b, 0x000
+};
+
+typedef wchar_t wvec16[16];
+typedef wchar_t wvec32[32];
+typedef wchar_t wvec64[64];
+
+/* Substitutions when 0xc1-0xcf appears by itself or with space 0x20 */
+static const wvec16 accents = {
+ 0x000, 0x060, 0x0b4, 0x05e, 0x07e, 0x0af, 0x2d8, 0x2d9,
+ 0x0a8, 0x000, 0x2da, 0x0b8, 0x000, 0x2dd, 0x2db, 0x2c7};
+
+/* In the following tables, base characters commented in (parentheses)
+ * are not defined by T.61 but are mapped anyway since their Unicode
+ * composite exists.
+ */
+
+/* Grave accented chars AEIOU (NWY) */
+static const wvec32 c1_vec1 = {
+ /* Upper case */
+ 0, 0xc0, 0, 0, 0, 0xc8, 0, 0, 0, 0xcc, 0, 0, 0, 0, 0x1f8, 0xd2,
+ 0, 0, 0, 0, 0, 0xd9, 0, 0x1e80, 0, 0x1ef2, 0, 0, 0, 0, 0, 0};
+static const wvec32 c1_vec2 = {
+ /* Lower case */
+ 0, 0xe0, 0, 0, 0, 0xe8, 0, 0, 0, 0xec, 0, 0, 0, 0, 0x1f9, 0xf2,
+ 0, 0, 0, 0, 0, 0xf9, 0, 0x1e81, 0, 0x1ef3, 0, 0, 0, 0, 0, 0};
+
+static const wvec32 *c1_grave[] = {
+ NULL, NULL, &c1_vec1, &c1_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Acute accented chars AEIOUYCLNRSZ (GKMPW) */
+static const wvec32 c2_vec1 = {
+ /* Upper case */
+ 0, 0xc1, 0, 0x106, 0, 0xc9, 0, 0x1f4,
+ 0, 0xcd, 0, 0x1e30, 0x139, 0x1e3e, 0x143, 0xd3,
+ 0x1e54, 0, 0x154, 0x15a, 0, 0xda, 0, 0x1e82,
+ 0, 0xdd, 0x179, 0, 0, 0, 0, 0};
+static const wvec32 c2_vec2 = {
+ /* Lower case */
+ 0, 0xe1, 0, 0x107, 0, 0xe9, 0, 0x1f5,
+ 0, 0xed, 0, 0x1e31, 0x13a, 0x1e3f, 0x144, 0xf3,
+ 0x1e55, 0, 0x155, 0x15b, 0, 0xfa, 0, 0x1e83,
+ 0, 0xfd, 0x17a, 0, 0, 0, 0, 0};
+static const wvec32 c2_vec3 = {
+ /* (AE and ae) */
+ 0, 0x1fc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0x1fd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+static const wvec32 *c2_acute[] = {
+ NULL, NULL, &c2_vec1, &c2_vec2, NULL, NULL, NULL, &c2_vec3
+};
+
+/* Circumflex AEIOUYCGHJSW (Z) */
+static const wvec32 c3_vec1 = {
+ /* Upper case */
+ 0, 0xc2, 0, 0x108, 0, 0xca, 0, 0x11c,
+ 0x124, 0xce, 0x134, 0, 0, 0, 0, 0xd4,
+ 0, 0, 0, 0x15c, 0, 0xdb, 0, 0x174,
+ 0, 0x176, 0x1e90, 0, 0, 0, 0, 0};
+static const wvec32 c3_vec2 = {
+ /* Lower case */
+ 0, 0xe2, 0, 0x109, 0, 0xea, 0, 0x11d,
+ 0x125, 0xee, 0x135, 0, 0, 0, 0, 0xf4,
+ 0, 0, 0, 0x15d, 0, 0xfb, 0, 0x175,
+ 0, 0x177, 0x1e91, 0, 0, 0, 0, 0};
+static const wvec32 *c3_circumflex[] = {
+ NULL, NULL, &c3_vec1, &c3_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Tilde AIOUN (EVY) */
+static const wvec32 c4_vec1 = {
+ /* Upper case */
+ 0, 0xc3, 0, 0, 0, 0x1ebc, 0, 0, 0, 0x128, 0, 0, 0, 0, 0xd1, 0xd5,
+ 0, 0, 0, 0, 0, 0x168, 0x1e7c, 0, 0, 0x1ef8, 0, 0, 0, 0, 0, 0};
+static const wvec32 c4_vec2 = {
+ /* Lower case */
+ 0, 0xe3, 0, 0, 0, 0x1ebd, 0, 0, 0, 0x129, 0, 0, 0, 0, 0xf1, 0xf5,
+ 0, 0, 0, 0, 0, 0x169, 0x1e7d, 0, 0, 0x1ef9, 0, 0, 0, 0, 0, 0};
+static const wvec32 *c4_tilde[] = {
+ NULL, NULL, &c4_vec1, &c4_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Macron AEIOU (YG) */
+static const wvec32 c5_vec1 = {
+ /* Upper case */
+ 0, 0x100, 0, 0, 0, 0x112, 0, 0x1e20, 0, 0x12a, 0, 0, 0, 0, 0, 0x14c,
+ 0, 0, 0, 0, 0, 0x16a, 0, 0, 0, 0x232, 0, 0, 0, 0, 0, 0};
+static const wvec32 c5_vec2 = {
+ /* Lower case */
+ 0, 0x101, 0, 0, 0, 0x113, 0, 0x1e21, 0, 0x12b, 0, 0, 0, 0, 0, 0x14d,
+ 0, 0, 0, 0, 0, 0x16b, 0, 0, 0, 0x233, 0, 0, 0, 0, 0, 0};
+static const wvec32 c5_vec3 = {
+ /* (AE and ae) */
+ 0, 0x1e2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0x1e3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 *c5_macron[] = {
+ NULL, NULL, &c5_vec1, &c5_vec2, NULL, NULL, NULL, &c5_vec3
+};
+
+/* Breve AUG (EIO) */
+static const wvec32 c6_vec1 = {
+ /* Upper case */
+ 0, 0x102, 0, 0, 0, 0x114, 0, 0x11e, 0, 0x12c, 0, 0, 0, 0, 0, 0x14e,
+ 0, 0, 0, 0, 0, 0x16c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 c6_vec2 = {
+ /* Lower case */
+ 0, 0x103, 0, 0, 0, 0x115, 0, 0x11f, 0, 0x12d, 0, 0, 0, 0, 0, 0x14f,
+ 0, 0, 0, 0, 0, 0x16d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 *c6_breve[] = {
+ NULL, NULL, &c6_vec1, &c6_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Dot Above CEGIZ (AOBDFHMNPRSTWXY) */
+static const wvec32 c7_vec1 = {
+ /* Upper case */
+ 0, 0x226, 0x1e02, 0x10a, 0x1e0a, 0x116, 0x1e1e, 0x120,
+ 0x1e22, 0x130, 0, 0, 0, 0x1e40, 0x1e44, 0x22e,
+ 0x1e56, 0, 0x1e58, 0x1e60, 0x1e6a, 0, 0, 0x1e86,
+ 0x1e8a, 0x1e8e, 0x17b, 0, 0, 0, 0, 0};
+static const wvec32 c7_vec2 = {
+ /* Lower case */
+ 0, 0x227, 0x1e03, 0x10b, 0x1e0b, 0x117, 0x1e1f, 0x121,
+ 0x1e23, 0, 0, 0, 0, 0x1e41, 0x1e45, 0x22f,
+ 0x1e57, 0, 0x1e59, 0x1e61, 0x1e6b, 0, 0, 0x1e87,
+ 0x1e8b, 0x1e8f, 0x17c, 0, 0, 0, 0, 0};
+static const wvec32 *c7_dotabove[] = {
+ NULL, NULL, &c7_vec1, &c7_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Diaeresis AEIOUY (HWXt) */
+static const wvec32 c8_vec1 = {
+ /* Upper case */
+ 0, 0xc4, 0, 0, 0, 0xcb, 0, 0, 0x1e26, 0xcf, 0, 0, 0, 0, 0, 0xd6,
+ 0, 0, 0, 0, 0, 0xdc, 0, 0x1e84, 0x1e8c, 0x178, 0, 0, 0, 0, 0, 0};
+static const wvec32 c8_vec2 = {
+ /* Lower case */
+ 0, 0xe4, 0, 0, 0, 0xeb, 0, 0, 0x1e27, 0xef, 0, 0, 0, 0, 0, 0xf6,
+ 0, 0, 0, 0, 0x1e97, 0xfc, 0, 0x1e85, 0x1e8d, 0xff, 0, 0, 0, 0, 0, 0};
+static const wvec32 *c8_diaeresis[] = {
+ NULL, NULL, &c8_vec1, &c8_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Ring Above AU (wy) */
+static const wvec32 ca_vec1 = {
+ /* Upper case */
+ 0, 0xc5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0x16e, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 ca_vec2 = {
+ /* Lower case */
+ 0, 0xe5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0x16f, 0, 0x1e98, 0, 0x1e99, 0, 0, 0, 0, 0, 0};
+static const wvec32 *ca_ringabove[] = {
+ NULL, NULL, &ca_vec1, &ca_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Cedilla CGKLNRST (EDH) */
+static const wvec32 cb_vec1 = {
+ /* Upper case */
+ 0, 0, 0, 0xc7, 0x1e10, 0x228, 0, 0x122,
+ 0x1e28, 0, 0, 0x136, 0x13b, 0, 0x145, 0,
+ 0, 0, 0x156, 0x15e, 0x162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 cb_vec2 = {
+ /* Lower case */
+ 0, 0, 0, 0xe7, 0x1e11, 0x229, 0, 0x123,
+ 0x1e29, 0, 0, 0x137, 0x13c, 0, 0x146, 0,
+ 0, 0, 0x157, 0x15f, 0x163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 *cb_cedilla[] = {
+ NULL, NULL, &cb_vec1, &cb_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Double Acute Accent OU */
+static const wvec32 cd_vec1 = {
+ /* Upper case */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x150,
+ 0, 0, 0, 0, 0, 0x170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 cd_vec2 = {
+ /* Lower case */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x151,
+ 0, 0, 0, 0, 0, 0x171, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 *cd_doubleacute[] = {
+ NULL, NULL, &cd_vec1, &cd_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Ogonek AEIU (O) */
+static const wvec32 ce_vec1 = {
+ /* Upper case */
+ 0, 0x104, 0, 0, 0, 0x118, 0, 0, 0, 0x12e, 0, 0, 0, 0, 0, 0x1ea,
+ 0, 0, 0, 0, 0, 0x172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 ce_vec2 = {
+ /* Lower case */
+ 0, 0x105, 0, 0, 0, 0x119, 0, 0, 0, 0x12f, 0, 0, 0, 0, 0, 0x1eb,
+ 0, 0, 0, 0, 0, 0x173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 *ce_ogonek[] = {
+ NULL, NULL, &ce_vec1, &ce_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Caron CDELNRSTZ (AIOUGKjH) */
+static const wvec32 cf_vec1 = {
+ /* Upper case */
+ 0, 0x1cd, 0, 0x10c, 0x10e, 0x11a, 0, 0x1e6,
+ 0x21e, 0x1cf, 0, 0x1e8, 0x13d, 0, 0x147, 0x1d1,
+ 0, 0, 0x158, 0x160, 0x164, 0x1d3, 0, 0,
+ 0, 0, 0x17d, 0, 0, 0, 0, 0};
+static const wvec32 cf_vec2 = {
+ /* Lower case */
+ 0, 0x1ce, 0, 0x10d, 0x10f, 0x11b, 0, 0x1e7,
+ 0x21f, 0x1d0, 0x1f0, 0x1e9, 0x13e, 0, 0x148, 0x1d2,
+ 0, 0, 0x159, 0x161, 0x165, 0x1d4, 0, 0,
+ 0, 0, 0x17e, 0, 0, 0, 0, 0};
+static const wvec32 *cf_caron[] = {
+ NULL, NULL, &cf_vec1, &cf_vec2, NULL, NULL, NULL, NULL
+};
+
+static const wvec32 **cx_tab[] = {
+ NULL, c1_grave, c2_acute, c3_circumflex, c4_tilde, c5_macron,
+ c6_breve, c7_dotabove, c8_diaeresis, NULL, ca_ringabove,
+ cb_cedilla, NULL, cd_doubleacute, ce_ogonek, cf_caron };
+
+int ldap_t61s_valid( struct berval *str )
+{
+ unsigned char *c = (unsigned char *)str->bv_val;
+ int i;
+
+ for (i=0; i < str->bv_len; c++,i++)
+ if (!t61_tab[*c])
+ return 0;
+ return 1;
+}
+
+/* Transform a T.61 string to UTF-8.
+ */
+int ldap_t61s_to_utf8s( struct berval *src, struct berval *dst )
+{
+ unsigned char *c;
+ char *d;
+ int i, wlen = 0;
+
+ /* Just count the length of the UTF-8 result first */
+ for (i=0,c=(unsigned char *)src->bv_val; i < src->bv_len; c++,i++) {
+ /* Invalid T.61 characters? */
+ if (!t61_tab[*c])
+ return LDAP_INVALID_SYNTAX;
+ if ((*c & 0xf0) == 0xc0) {
+ int j = *c & 0x0f;
+ /* If this is the end of the string, or if the base
+ * character is just a space, treat this as a regular
+ * spacing character.
+ */
+ if ((!c[1] || c[1] == 0x20) && accents[j]) {
+ wlen += ldap_x_wc_to_utf8(NULL, accents[j], 0);
+ } else if (cx_tab[j] && cx_tab[j][c[1]>>5] &&
+ /* We have a composite mapping for this pair */
+ (*cx_tab[j][c[1]>>5])[c[1]&0x1f]) {
+ wlen += ldap_x_wc_to_utf8( NULL,
+ (*cx_tab[j][c[1]>>5])[c[1]&0x1f], 0);
+ } else {
+ /* No mapping, just swap it around so the base
+ * character comes first.
+ */
+ wlen += ldap_x_wc_to_utf8(NULL, c[1], 0);
+ wlen += ldap_x_wc_to_utf8(NULL,
+ t61_tab[*c], 0);
+ }
+ c++; i++;
+ continue;
+ } else {
+ wlen += ldap_x_wc_to_utf8(NULL, t61_tab[*c], 0);
+ }
+ }
+
+ /* Now transform the string */
+ dst->bv_len = wlen;
+ dst->bv_val = LDAP_MALLOC( wlen+1 );
+ d = dst->bv_val;
+ if (!d)
+ return LDAP_NO_MEMORY;
+
+ for (i=0,c=(unsigned char *)src->bv_val; i < src->bv_len; c++,i++) {
+ if ((*c & 0xf0) == 0xc0) {
+ int j = *c & 0x0f;
+ /* If this is the end of the string, or if the base
+ * character is just a space, treat this as a regular
+ * spacing character.
+ */
+ if ((!c[1] || c[1] == 0x20) && accents[j]) {
+ d += ldap_x_wc_to_utf8(d, accents[j], 6);
+ } else if (cx_tab[j] && cx_tab[j][c[1]>>5] &&
+ /* We have a composite mapping for this pair */
+ (*cx_tab[j][c[1]>>5])[c[1]&0x1f]) {
+ d += ldap_x_wc_to_utf8(d,
+ (*cx_tab[j][c[1]>>5])[c[1]&0x1f], 6);
+ } else {
+ /* No mapping, just swap it around so the base
+ * character comes first.
+ */
+ d += ldap_x_wc_to_utf8(d, c[1], 6);
+ d += ldap_x_wc_to_utf8(d, t61_tab[*c], 6);
+ }
+ c++; i++;
+ continue;
+ } else {
+ d += ldap_x_wc_to_utf8(d, t61_tab[*c], 6);
+ }
+ }
+ *d = '\0';
+ return LDAP_SUCCESS;
+}
+
+/* For the reverse mapping, we just pay attention to the Latin-oriented
+ * code blocks. These are
+ * 0000 - 007f Basic Latin
+ * 0080 - 00ff Latin-1 Supplement
+ * 0100 - 017f Latin Extended-A
+ * 0180 - 024f Latin Extended-B
+ * 1e00 - 1eff Latin Extended Additional
+ *
+ * We have a special case to map Ohm U2126 back to T.61 0xe0. All other
+ * unrecognized characters are replaced with '?' 0x3f.
+ */
+
+static const wvec64 u000 = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
+ 0x0020, 0x0021, 0x0022, 0x00a6, 0x00a4, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f};
+
+/* In this range, we've mapped caret to xc3/x20, backquote to xc1/x20,
+ * and tilde to xc4/x20. T.61 (stupidly!) doesn't define these characters
+ * on their own, even though it provides them as combiners for other
+ * letters. T.61 doesn't define these pairings either, so this may just
+ * have to be replaced with '?' 0x3f if other software can't cope with it.
+ */
+static const wvec64 u001 = {
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b, 0x003f, 0x005d, 0xc320, 0x005f,
+ 0xc120, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x003f, 0x007c, 0x003f, 0xc420, 0x007f};
+
+static const wvec64 u002 = {
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
+ 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
+ 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
+ 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a8, 0x00a5, 0x003f, 0x00a7,
+ 0xc820, 0x003f, 0x00e3, 0x00ab, 0x003f, 0x003f, 0x003f, 0xc520,
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0xc220, 0x00b5, 0x00b6, 0x00b7,
+ 0xcb20, 0x003f, 0x00eb, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf};
+
+static const wvec64 u003 = {
+ 0xc141, 0xc241, 0xc341, 0xc441, 0xc841, 0xca41, 0x00e1, 0xcb43,
+ 0xc145, 0xc245, 0xc345, 0xc845, 0xc149, 0xc249, 0xc349, 0xc849,
+ 0x00e2, 0xc44e, 0xc14f, 0xc24f, 0xc34f, 0xc44f, 0xc84f, 0x00b4,
+ 0x00e9, 0xc155, 0xc255, 0xc355, 0xc855, 0xc259, 0x00ec, 0x00fb,
+ 0xc161, 0xc261, 0xc361, 0xc461, 0xc861, 0xca61, 0x00f1, 0xcb63,
+ 0xc165, 0xc265, 0xc365, 0xc865, 0xc169, 0xc269, 0xc369, 0xc869,
+ 0x00f3, 0xc46e, 0xc16f, 0xc26f, 0xc36f, 0xc46f, 0xc86f, 0x00b8,
+ 0x00f9, 0xc175, 0xc275, 0xc375, 0xc875, 0xc279, 0x00fc, 0xc879};
+
+/* These codes are used here but not defined by T.61:
+ * x114 = xc6/x45, x115 = xc6/x65, x12c = xc6/x49, x12d = xc6/x69
+ */
+static const wvec64 u010 = {
+ 0xc541, 0xc561, 0xc641, 0xc661, 0xce41, 0xce61, 0xc243, 0xc263,
+ 0xc343, 0xc363, 0xc743, 0xc763, 0xcf43, 0xcf63, 0xcf44, 0xcf64,
+ 0x003f, 0x00f2, 0xc545, 0xc565, 0xc645, 0xc665, 0xc745, 0xc765,
+ 0xce45, 0xce65, 0xcf45, 0xcf65, 0xc347, 0xc367, 0xc647, 0xc667,
+ 0xc747, 0xc767, 0xcb47, 0xcb67, 0xc348, 0xc368, 0x00e4, 0x00f4,
+ 0xc449, 0xc469, 0xc549, 0xc569, 0xc649, 0xc669, 0xce49, 0xce69,
+ 0xc749, 0x00f5, 0x00e6, 0x00f6, 0xc34a, 0xc36a, 0xcb4b, 0xcb6b,
+ 0x00f0, 0xc24c, 0xc26c, 0xcb4c, 0xcb6c, 0xcf4c, 0xcf6c, 0x00e7};
+
+/* These codes are used here but not defined by T.61:
+ * x14e = xc6/x4f, x14f = xc6/x6f
+ */
+static const wvec64 u011 = {
+ 0x00f7, 0x00e8, 0x00f8, 0xc24e, 0xc26e, 0xcb4e, 0xcb6e, 0xcf4e,
+ 0xcf6e, 0x00ef, 0x00ee, 0x00fe, 0xc54f, 0xc56f, 0xc64f, 0xc66f,
+ 0xcd4f, 0xcd6f, 0x00ea, 0x00fa, 0xc252, 0xc272, 0xcb52, 0xcb72,
+ 0xcf52, 0xcf72, 0xc253, 0xc273, 0xc353, 0xc373, 0xcb53, 0xcb73,
+ 0xcf53, 0xcf73, 0xcb54, 0xcb74, 0xcf54, 0xcf74, 0x00ed, 0x00fd,
+ 0xc455, 0xc475, 0xc555, 0xc575, 0xc655, 0xc675, 0xca55, 0xca75,
+ 0xcd55, 0xcd75, 0xce55, 0xce75, 0xc357, 0xc377, 0xc359, 0xc379,
+ 0xc859, 0xc25a, 0xc27a, 0xc75a, 0xc77a, 0xcf5a, 0xcf7a, 0x003f};
+
+/* All of the codes in this block are undefined in T.61.
+ */
+static const wvec64 u013 = {
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0xcf41, 0xcf61, 0xcf49,
+ 0xcf69, 0xcf4f, 0xcf6f, 0xcf55, 0xcf75, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0xc5e1, 0xc5f1, 0x003f, 0x003f, 0xcf47, 0xcf67,
+ 0xcf4b, 0xcf6b, 0xce4f, 0xce6f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0xcf6a, 0x003f, 0x003f, 0x003f, 0xc247, 0xc267, 0x003f, 0x003f,
+ 0xc14e, 0xc16e, 0x003f, 0x003f, 0xc2e1, 0xc2f1, 0x003f, 0x003f};
+
+/* All of the codes in this block are undefined in T.61.
+ */
+static const wvec64 u020 = {
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0xcf48, 0xcf68,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0xc741, 0xc761,
+ 0xcb45, 0xcb65, 0x003f, 0x003f, 0x003f, 0x003f, 0xc74f, 0xc76f,
+ 0x003f, 0x003f, 0xc559, 0xc579, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f};
+
+static const wvec64 u023 = {
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0xcf20,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0xc620, 0xc720, 0xca20, 0xce20, 0x003f, 0xcd20, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f};
+
+/* These are the non-spacing characters by themselves. They should
+ * never appear by themselves in actual text.
+ */
+static const wvec64 u030 = {
+ 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x003f, 0x00c6, 0x00c7,
+ 0x00c8, 0x003f, 0x00ca, 0x00cd, 0x00cf, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x00cb,
+ 0x00ce, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x00cc, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f};
+
+/* None of the following blocks are defined in T.61.
+ */
+static const wvec64 u1e0 = {
+ 0x003f, 0x003f, 0xc742, 0xc762, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0xc744, 0xc764, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0xcb44, 0xcb64, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0xc746, 0xc766,
+ 0xc547, 0xc567, 0xc748, 0xc768, 0x003f, 0x003f, 0xc848, 0xc868,
+ 0xcb48, 0xcb68, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0xc24b, 0xc26b, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0xc24d, 0xc26d,
+};
+
+static const wvec64 u1e1 = {
+ 0xc74d, 0xc76d, 0x003f, 0x003f, 0xc74e, 0xc76e, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0xc250, 0xc270, 0xc750, 0xc770,
+ 0xc752, 0xc772, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0xc753, 0xc773, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0xc754, 0xc774, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0xc456, 0xc476, 0x003f, 0x003f,
+};
+
+static const wvec64 u1e2 = {
+ 0xc157, 0xc177, 0xc257, 0xc277, 0xc857, 0xc877, 0xc757, 0xc777,
+ 0x003f, 0x003f, 0xc758, 0xc778, 0xc858, 0xc878, 0xc759, 0xc779,
+ 0xc35a, 0xc37a, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0xc874,
+ 0xca77, 0xca79, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0xc445, 0xc465, 0x003f, 0x003f,
+};
+
+static const wvec64 u1e3 = {
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0xc159, 0xc179, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0xc459, 0xc479, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+};
+
+static const wvec64 *wc00[] = {
+ &u000, &u001, &u002, &u003,
+ &u010, &u011, NULL, &u013,
+ &u020, NULL, NULL, &u023,
+ &u030, NULL, NULL, NULL};
+
+static const wvec64 *wc1e[] = {
+ &u1e0, &u1e1, &u1e2, &u1e3};
+
+
+int ldap_utf8s_to_t61s( struct berval *src, struct berval *dst )
+{
+ char *c, *d;
+ wchar_t tmp;
+ int i, j, tlen = 0;
+
+ /* Just count the length of the T.61 result first */
+ for (i=0,c=src->bv_val; i < src->bv_len;) {
+ j = ldap_x_utf8_to_wc( &tmp, c );
+ if (j == -1)
+ return LDAP_INVALID_SYNTAX;
+ switch (tmp >> 8) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ if (wc00[tmp >> 6] &&
+ ((*wc00[tmp >> 6])[tmp & 0x3f] & 0xff00)) {
+ tlen++;
+ }
+ tlen++;
+ break;
+ case 0x1e:
+ if ((*wc1e[(tmp >> 6) & 3])[tmp & 0x3f] & 0xff00) {
+ tlen++;
+ }
+ case 0x21:
+ default:
+ tlen ++;
+ break;
+ }
+ i += j;
+ c += j;
+ }
+ dst->bv_len = tlen;
+ dst->bv_val = LDAP_MALLOC( tlen+1 );
+ if (!dst->bv_val)
+ return LDAP_NO_MEMORY;
+
+ d = dst->bv_val;
+ for (i=0,c=src->bv_val; i < src->bv_len;) {
+ j = ldap_x_utf8_to_wc( &tmp, c );
+ switch (tmp >> 8) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ if (wc00[tmp >> 6]) {
+ tmp = (*wc00[tmp >> 6])[tmp & 0x3f];
+ if (tmp & 0xff00)
+ *d++ = (tmp >> 8);
+ *d++ = tmp & 0xff;
+ } else {
+ *d++ = 0x3f;
+ }
+ break;
+ case 0x03:
+ /* swap order of non-spacing characters */
+ if (wc00[tmp >> 6]) {
+ wchar_t t2 = (*wc00[tmp >> 6])[tmp & 0x3f];
+ if (t2 != 0x3f) {
+ d[0] = d[-1];
+ d[-1] = t2;
+ d++;
+ } else {
+ *d++ = 0x3f;
+ }
+ } else {
+ *d++ = 0x3f;
+ }
+ break;
+ case 0x1e:
+ tmp = (*wc1e[(tmp >> 6) & 3])[tmp & 0x3f];
+ if (tmp & 0xff00)
+ *d++ = (tmp >> 8);
+ *d++ = tmp & 0xff;
+ break;
+ case 0x21:
+ if (tmp == 0x2126) {
+ *d++ = 0xe0;
+ break;
+ }
+ /* FALLTHRU */
+ default:
+ *d++ = 0x3f;
+ break;
+ }
+ i += j;
+ c += j;
+ }
+ *d = '\0';
+ return LDAP_SUCCESS;
+}
diff --git a/libraries/libldap/tavl.c b/libraries/libldap/tavl.c
new file mode 100644
index 0000000..18a6e37
--- /dev/null
+++ b/libraries/libldap/tavl.c
@@ -0,0 +1,523 @@
+/* avl.c - routines to implement an avl tree */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2022 The OpenLDAP Foundation.
+ * Portions Copyright (c) 2005 by Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP software.
+ */
+
+#include "portable.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#ifdef CSRIMALLOC
+#define ber_memalloc malloc
+#define ber_memrealloc realloc
+#define ber_memfree free
+#else
+#include "lber.h"
+#endif
+
+#define AVL_INTERNAL
+#include "ldap_avl.h"
+
+/* Maximum tree depth this host's address space could support */
+#define MAX_TREE_DEPTH (sizeof(void *) * CHAR_BIT)
+
+static const int avl_bfs[] = {LH, RH};
+
+/*
+ * Threaded AVL trees - for fast in-order traversal of nodes.
+ */
+/*
+ * ldap_tavl_insert -- insert a node containing data data into the avl tree
+ * with root root. fcmp is a function to call to compare the data portion
+ * of two nodes. it should take two arguments and return <, >, or == 0,
+ * depending on whether its first argument is <, >, or == its second
+ * argument (like strcmp, e.g.). fdup is a function to call when a duplicate
+ * node is inserted. it should return 0, or -1 and its return value
+ * will be the return value from ldap_avl_insert in the case of a duplicate node.
+ * the function will be called with the original node's data as its first
+ * argument and with the incoming duplicate node's data as its second
+ * argument. this could be used, for example, to keep a count with each
+ * node.
+ *
+ * NOTE: this routine may malloc memory
+ */
+int
+ldap_tavl_insert( TAvlnode ** root, void *data, AVL_CMP fcmp, AVL_DUP fdup )
+{
+ TAvlnode *t, *p, *s, *q, *r;
+ int a, cmp, ncmp;
+
+ if ( *root == NULL ) {
+ if (( r = (TAvlnode *) ber_memalloc( sizeof( TAvlnode ))) == NULL ) {
+ return( -1 );
+ }
+ r->avl_link[0] = r->avl_link[1] = NULL;
+ r->avl_data = data;
+ r->avl_bf = EH;
+ r->avl_bits[0] = r->avl_bits[1] = AVL_THREAD;
+ *root = r;
+
+ return( 0 );
+ }
+
+ t = NULL;
+ s = p = *root;
+
+ /* find insertion point */
+ while (1) {
+ cmp = fcmp( data, p->avl_data );
+ if ( cmp == 0 )
+ return (*fdup)( p->avl_data, data );
+
+ cmp = (cmp > 0);
+ q = ldap_avl_child( p, cmp );
+ if (q == NULL) {
+ /* insert */
+ if (( q = (TAvlnode *) ber_memalloc( sizeof( TAvlnode ))) == NULL ) {
+ return( -1 );
+ }
+ q->avl_link[cmp] = p->avl_link[cmp];
+ q->avl_link[!cmp] = p;
+ q->avl_data = data;
+ q->avl_bf = EH;
+ q->avl_bits[0] = q->avl_bits[1] = AVL_THREAD;
+
+ p->avl_link[cmp] = q;
+ p->avl_bits[cmp] = AVL_CHILD;
+ break;
+ } else if ( q->avl_bf ) {
+ t = p;
+ s = q;
+ }
+ p = q;
+ }
+
+ /* adjust balance factors */
+ cmp = fcmp( data, s->avl_data ) > 0;
+ r = p = s->avl_link[cmp];
+ a = avl_bfs[cmp];
+
+ while ( p != q ) {
+ cmp = fcmp( data, p->avl_data ) > 0;
+ p->avl_bf = avl_bfs[cmp];
+ p = p->avl_link[cmp];
+ }
+
+ /* checks and balances */
+
+ if ( s->avl_bf == EH ) {
+ s->avl_bf = a;
+ return 0;
+ } else if ( s->avl_bf == -a ) {
+ s->avl_bf = EH;
+ return 0;
+ } else if ( s->avl_bf == a ) {
+ cmp = (a > 0);
+ ncmp = !cmp;
+ if ( r->avl_bf == a ) {
+ /* single rotation */
+ p = r;
+ if ( r->avl_bits[ncmp] == AVL_THREAD ) {
+ r->avl_bits[ncmp] = AVL_CHILD;
+ s->avl_bits[cmp] = AVL_THREAD;
+ } else {
+ s->avl_link[cmp] = r->avl_link[ncmp];
+ r->avl_link[ncmp] = s;
+ }
+ s->avl_bf = 0;
+ r->avl_bf = 0;
+ } else if ( r->avl_bf == -a ) {
+ /* double rotation */
+ p = r->avl_link[ncmp];
+ if ( p->avl_bits[cmp] == AVL_THREAD ) {
+ p->avl_bits[cmp] = AVL_CHILD;
+ r->avl_bits[ncmp] = AVL_THREAD;
+ } else {
+ r->avl_link[ncmp] = p->avl_link[cmp];
+ p->avl_link[cmp] = r;
+ }
+ if ( p->avl_bits[ncmp] == AVL_THREAD ) {
+ p->avl_bits[ncmp] = AVL_CHILD;
+ s->avl_link[cmp] = p;
+ s->avl_bits[cmp] = AVL_THREAD;
+ } else {
+ s->avl_link[cmp] = p->avl_link[ncmp];
+ p->avl_link[ncmp] = s;
+ }
+ if ( p->avl_bf == a ) {
+ s->avl_bf = -a;
+ r->avl_bf = 0;
+ } else if ( p->avl_bf == -a ) {
+ s->avl_bf = 0;
+ r->avl_bf = a;
+ } else {
+ s->avl_bf = 0;
+ r->avl_bf = 0;
+ }
+ p->avl_bf = 0;
+ }
+ /* Update parent */
+ if ( t == NULL )
+ *root = p;
+ else if ( s == t->avl_right )
+ t->avl_right = p;
+ else
+ t->avl_left = p;
+ }
+
+ return 0;
+}
+
+void*
+ldap_tavl_delete( TAvlnode **root, void* data, AVL_CMP fcmp )
+{
+ TAvlnode *p, *q, *r, *top;
+ int side, side_bf, shorter, nside = -1;
+
+ /* parent stack */
+ TAvlnode *pptr[MAX_TREE_DEPTH];
+ unsigned char pdir[MAX_TREE_DEPTH];
+ int depth = 0;
+
+ if ( *root == NULL )
+ return NULL;
+
+ p = *root;
+
+ while (1) {
+ side = fcmp( data, p->avl_data );
+ if ( !side )
+ break;
+ side = ( side > 0 );
+ pdir[depth] = side;
+ pptr[depth++] = p;
+
+ if ( p->avl_bits[side] == AVL_THREAD )
+ return NULL;
+ p = p->avl_link[side];
+ }
+ data = p->avl_data;
+
+ /* If this node has two children, swap so we are deleting a node with
+ * at most one child.
+ */
+ if ( p->avl_bits[0] == AVL_CHILD && p->avl_bits[1] == AVL_CHILD &&
+ p->avl_link[0] && p->avl_link[1] ) {
+
+ /* find the immediate predecessor <q> */
+ q = p->avl_link[0];
+ side = depth;
+ pdir[depth++] = 0;
+ while (q->avl_bits[1] == AVL_CHILD && q->avl_link[1]) {
+ pdir[depth] = 1;
+ pptr[depth++] = q;
+ q = q->avl_link[1];
+ }
+ /* swap links */
+ r = p->avl_link[0];
+ p->avl_link[0] = q->avl_link[0];
+ q->avl_link[0] = r;
+
+ q->avl_link[1] = p->avl_link[1];
+ p->avl_link[1] = q;
+
+ p->avl_bits[0] = q->avl_bits[0];
+ p->avl_bits[1] = q->avl_bits[1];
+ q->avl_bits[0] = q->avl_bits[1] = AVL_CHILD;
+
+ q->avl_bf = p->avl_bf;
+
+ /* fix stack positions: old parent of p points to q */
+ pptr[side] = q;
+ if ( side ) {
+ r = pptr[side-1];
+ r->avl_link[pdir[side-1]] = q;
+ } else {
+ *root = q;
+ }
+ /* new parent of p points to p */
+ if ( depth-side > 1 ) {
+ r = pptr[depth-1];
+ r->avl_link[1] = p;
+ } else {
+ q->avl_link[0] = p;
+ }
+
+ /* fix right subtree: successor of p points to q */
+ r = q->avl_link[1];
+ while ( r->avl_bits[0] == AVL_CHILD && r->avl_link[0] )
+ r = r->avl_link[0];
+ r->avl_link[0] = q;
+ }
+
+ /* now <p> has at most one child, get it */
+ if ( p->avl_link[0] && p->avl_bits[0] == AVL_CHILD ) {
+ q = p->avl_link[0];
+ /* Preserve thread continuity */
+ r = p->avl_link[1];
+ nside = 1;
+ } else if ( p->avl_link[1] && p->avl_bits[1] == AVL_CHILD ) {
+ q = p->avl_link[1];
+ r = p->avl_link[0];
+ nside = 0;
+ } else {
+ q = NULL;
+ if ( depth > 0 )
+ r = p->avl_link[pdir[depth-1]];
+ else
+ r = NULL;
+ }
+
+ ber_memfree( p );
+
+ /* Update child thread */
+ if ( q ) {
+ for ( ; q->avl_bits[nside] == AVL_CHILD && q->avl_link[nside];
+ q = q->avl_link[nside] ) ;
+ q->avl_link[nside] = r;
+ }
+
+ if ( !depth ) {
+ *root = q;
+ return data;
+ }
+
+ /* set the child into p's parent */
+ depth--;
+ p = pptr[depth];
+ side = pdir[depth];
+ p->avl_link[side] = q;
+
+ if ( !q ) {
+ p->avl_bits[side] = AVL_THREAD;
+ p->avl_link[side] = r;
+ }
+
+ top = NULL;
+ shorter = 1;
+
+ while ( shorter ) {
+ p = pptr[depth];
+ side = pdir[depth];
+ nside = !side;
+ side_bf = avl_bfs[side];
+
+ /* case 1: height unchanged */
+ if ( p->avl_bf == EH ) {
+ /* Tree is now heavier on opposite side */
+ p->avl_bf = avl_bfs[nside];
+ shorter = 0;
+
+ } else if ( p->avl_bf == side_bf ) {
+ /* case 2: taller subtree shortened, height reduced */
+ p->avl_bf = EH;
+ } else {
+ /* case 3: shorter subtree shortened */
+ if ( depth )
+ top = pptr[depth-1]; /* p->parent; */
+ else
+ top = NULL;
+ /* set <q> to the taller of the two subtrees of <p> */
+ q = p->avl_link[nside];
+ if ( q->avl_bf == EH ) {
+ /* case 3a: height unchanged, single rotate */
+ if ( q->avl_bits[side] == AVL_THREAD ) {
+ q->avl_bits[side] = AVL_CHILD;
+ p->avl_bits[nside] = AVL_THREAD;
+ } else {
+ p->avl_link[nside] = q->avl_link[side];
+ q->avl_link[side] = p;
+ }
+ shorter = 0;
+ q->avl_bf = side_bf;
+ p->avl_bf = (- side_bf);
+
+ } else if ( q->avl_bf == p->avl_bf ) {
+ /* case 3b: height reduced, single rotate */
+ if ( q->avl_bits[side] == AVL_THREAD ) {
+ q->avl_bits[side] = AVL_CHILD;
+ p->avl_bits[nside] = AVL_THREAD;
+ } else {
+ p->avl_link[nside] = q->avl_link[side];
+ q->avl_link[side] = p;
+ }
+ shorter = 1;
+ q->avl_bf = EH;
+ p->avl_bf = EH;
+
+ } else {
+ /* case 3c: height reduced, balance factors opposite */
+ r = q->avl_link[side];
+ if ( r->avl_bits[nside] == AVL_THREAD ) {
+ r->avl_bits[nside] = AVL_CHILD;
+ q->avl_bits[side] = AVL_THREAD;
+ } else {
+ q->avl_link[side] = r->avl_link[nside];
+ r->avl_link[nside] = q;
+ }
+
+ if ( r->avl_bits[side] == AVL_THREAD ) {
+ r->avl_bits[side] = AVL_CHILD;
+ p->avl_bits[nside] = AVL_THREAD;
+ p->avl_link[nside] = r;
+ } else {
+ p->avl_link[nside] = r->avl_link[side];
+ r->avl_link[side] = p;
+ }
+
+ if ( r->avl_bf == side_bf ) {
+ q->avl_bf = (- side_bf);
+ p->avl_bf = EH;
+ } else if ( r->avl_bf == (- side_bf)) {
+ q->avl_bf = EH;
+ p->avl_bf = side_bf;
+ } else {
+ q->avl_bf = EH;
+ p->avl_bf = EH;
+ }
+ r->avl_bf = EH;
+ q = r;
+ }
+ /* a rotation has caused <q> (or <r> in case 3c) to become
+ * the root. let <p>'s former parent know this.
+ */
+ if ( top == NULL ) {
+ *root = q;
+ } else if (top->avl_link[0] == p) {
+ top->avl_link[0] = q;
+ } else {
+ top->avl_link[1] = q;
+ }
+ /* end case 3 */
+ p = q;
+ }
+ if ( !depth )
+ break;
+ depth--;
+ } /* end while(shorter) */
+
+ return data;
+}
+
+/*
+ * ldap_tavl_free -- traverse avltree root, freeing the memory it is using.
+ * the dfree() is called to free the data portion of each node. The
+ * number of items actually freed is returned.
+ */
+
+int
+ldap_tavl_free( TAvlnode *root, AVL_FREE dfree )
+{
+ int nleft, nright;
+
+ if ( root == 0 )
+ return( 0 );
+
+ nleft = ldap_tavl_free( ldap_avl_lchild( root ), dfree );
+
+ nright = ldap_tavl_free( ldap_avl_rchild( root ), dfree );
+
+ if ( dfree )
+ (*dfree)( root->avl_data );
+ ber_memfree( root );
+
+ return( nleft + nright + 1 );
+}
+
+/*
+ * ldap_tavl_find -- search avltree root for a node with data data. the function
+ * cmp is used to compare things. it is called with data as its first arg
+ * and the current node data as its second. it should return 0 if they match,
+ * < 0 if arg1 is less than arg2 and > 0 if arg1 is greater than arg2.
+ */
+
+/*
+ * ldap_tavl_find2 - returns TAvlnode instead of data pointer.
+ * ldap_tavl_find3 - as above, but returns TAvlnode even if no match is found.
+ * also set *ret = last comparison result, or -1 if root == NULL.
+ */
+TAvlnode *
+ldap_tavl_find3( TAvlnode *root, const void *data, AVL_CMP fcmp, int *ret )
+{
+ int cmp = -1, dir;
+ TAvlnode *prev = root;
+
+ while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) {
+ prev = root;
+ dir = cmp > 0;
+ root = ldap_avl_child( root, dir );
+ }
+ *ret = cmp;
+ return root ? root : prev;
+}
+
+TAvlnode *
+ldap_tavl_find2( TAvlnode *root, const void *data, AVL_CMP fcmp )
+{
+ int cmp;
+
+ while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) {
+ cmp = cmp > 0;
+ root = ldap_avl_child( root, cmp );
+ }
+ return root;
+}
+
+void*
+ldap_tavl_find( TAvlnode *root, const void* data, AVL_CMP fcmp )
+{
+ int cmp;
+
+ while ( root != 0 && (cmp = (*fcmp)( data, root->avl_data )) != 0 ) {
+ cmp = cmp > 0;
+ root = ldap_avl_child( root, cmp );
+ }
+
+ return( root ? root->avl_data : 0 );
+}
+
+/* Return the leftmost or rightmost node in the tree */
+TAvlnode *
+ldap_tavl_end( TAvlnode *root, int dir )
+{
+ if ( root ) {
+ while ( root->avl_bits[dir] == AVL_CHILD )
+ root = root->avl_link[dir];
+ }
+ return root;
+}
+
+/* Return the next node in the given direction */
+TAvlnode *
+ldap_tavl_next( TAvlnode *root, int dir )
+{
+ if ( root ) {
+ int c = root->avl_bits[dir];
+
+ root = root->avl_link[dir];
+ if ( c == AVL_CHILD ) {
+ dir ^= 1;
+ while ( root->avl_bits[dir] == AVL_CHILD )
+ root = root->avl_link[dir];
+ }
+ }
+ return root;
+}
diff --git a/libraries/libldap/test.c b/libraries/libldap/test.c
new file mode 100644
index 0000000..ffe7616
--- /dev/null
+++ b/libraries/libldap/test.c
@@ -0,0 +1,807 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include <sys/stat.h>
+
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+
+#include <fcntl.h>
+
+/* including the "internal" defs is legit and nec. since this test routine has
+ * a-priori knowledge of libldap internal workings.
+ * hodges@stanford.edu 5-Feb-96
+ */
+#include "ldap-int.h"
+
+/* local functions */
+static char *get_line LDAP_P(( char *line, int len, FILE *fp, const char *prompt ));
+static char **get_list LDAP_P(( const char *prompt ));
+static int file_read LDAP_P(( const char *path, struct berval *bv ));
+static LDAPMod **get_modlist LDAP_P(( const char *prompt1,
+ const char *prompt2, const char *prompt3 ));
+static void handle_result LDAP_P(( LDAP *ld, LDAPMessage *lm ));
+static void print_ldap_result LDAP_P(( LDAP *ld, LDAPMessage *lm,
+ const char *s ));
+static void print_search_entry LDAP_P(( LDAP *ld, LDAPMessage *res ));
+static void free_list LDAP_P(( char **list ));
+
+static char *dnsuffix;
+
+static char *
+get_line( char *line, int len, FILE *fp, const char *prompt )
+{
+ fputs(prompt, stdout);
+
+ if ( fgets( line, len, fp ) == NULL )
+ return( NULL );
+
+ line[ strlen( line ) - 1 ] = '\0';
+
+ return( line );
+}
+
+static char **
+get_list( const char *prompt )
+{
+ static char buf[256];
+ int num;
+ char **result;
+
+ num = 0;
+ result = (char **) 0;
+ while ( 1 ) {
+ get_line( buf, sizeof(buf), stdin, prompt );
+
+ if ( *buf == '\0' )
+ break;
+
+ if ( result == (char **) 0 )
+ result = (char **) malloc( sizeof(char *) );
+ else
+ result = (char **) realloc( result,
+ sizeof(char *) * (num + 1) );
+
+ result[num++] = (char *) strdup( buf );
+ }
+ if ( result == (char **) 0 )
+ return( NULL );
+ result = (char **) realloc( result, sizeof(char *) * (num + 1) );
+ result[num] = NULL;
+
+ return( result );
+}
+
+
+static void
+free_list( char **list )
+{
+ int i;
+
+ if ( list != NULL ) {
+ for ( i = 0; list[ i ] != NULL; ++i ) {
+ free( list[ i ] );
+ }
+ free( (char *)list );
+ }
+}
+
+
+static int
+file_read( const char *path, struct berval *bv )
+{
+ FILE *fp;
+ ber_slen_t rlen;
+ int eof;
+
+ if (( fp = fopen( path, "r" )) == NULL ) {
+ perror( path );
+ return( -1 );
+ }
+
+ if ( fseek( fp, 0L, SEEK_END ) != 0 ) {
+ perror( path );
+ fclose( fp );
+ return( -1 );
+ }
+
+ bv->bv_len = ftell( fp );
+
+ if (( bv->bv_val = (char *)malloc( bv->bv_len )) == NULL ) {
+ perror( "malloc" );
+ fclose( fp );
+ return( -1 );
+ }
+
+ if ( fseek( fp, 0L, SEEK_SET ) != 0 ) {
+ perror( path );
+ fclose( fp );
+ return( -1 );
+ }
+
+ rlen = fread( bv->bv_val, 1, bv->bv_len, fp );
+ eof = feof( fp );
+ fclose( fp );
+
+ if ( (ber_len_t) rlen != bv->bv_len ) {
+ perror( path );
+ free( bv->bv_val );
+ return( -1 );
+ }
+
+ return( bv->bv_len );
+}
+
+
+static LDAPMod **
+get_modlist(
+ const char *prompt1,
+ const char *prompt2,
+ const char *prompt3 )
+{
+ static char buf[256];
+ int num;
+ LDAPMod tmp = { 0 };
+ LDAPMod **result;
+ struct berval **bvals;
+
+ num = 0;
+ result = NULL;
+ while ( 1 ) {
+ if ( prompt1 ) {
+ get_line( buf, sizeof(buf), stdin, prompt1 );
+ tmp.mod_op = atoi( buf );
+
+ if ( tmp.mod_op == -1 || buf[0] == '\0' )
+ break;
+ }
+
+ get_line( buf, sizeof(buf), stdin, prompt2 );
+ if ( buf[0] == '\0' )
+ break;
+ tmp.mod_type = strdup( buf );
+
+ tmp.mod_values = get_list( prompt3 );
+
+ if ( tmp.mod_values != NULL ) {
+ int i;
+
+ for ( i = 0; tmp.mod_values[i] != NULL; ++i )
+ ;
+ bvals = (struct berval **)calloc( i + 1,
+ sizeof( struct berval *));
+ for ( i = 0; tmp.mod_values[i] != NULL; ++i ) {
+ bvals[i] = (struct berval *)malloc(
+ sizeof( struct berval ));
+ if ( strncmp( tmp.mod_values[i], "{FILE}",
+ 6 ) == 0 ) {
+ if ( file_read( tmp.mod_values[i] + 6,
+ bvals[i] ) < 0 ) {
+ free( bvals );
+ for ( i = 0; i<num; i++ )
+ free( result[ i ] );
+ free( result );
+ return( NULL );
+ }
+ } else {
+ bvals[i]->bv_val = tmp.mod_values[i];
+ bvals[i]->bv_len =
+ strlen( tmp.mod_values[i] );
+ }
+ }
+ tmp.mod_bvalues = bvals;
+ tmp.mod_op |= LDAP_MOD_BVALUES;
+ }
+
+ if ( result == NULL )
+ result = (LDAPMod **) malloc( sizeof(LDAPMod *) );
+ else
+ result = (LDAPMod **) realloc( result,
+ sizeof(LDAPMod *) * (num + 1) );
+
+ result[num] = (LDAPMod *) malloc( sizeof(LDAPMod) );
+ *(result[num]) = tmp; /* struct copy */
+ num++;
+ }
+ if ( result == NULL )
+ return( NULL );
+ result = (LDAPMod **) realloc( result, sizeof(LDAPMod *) * (num + 1) );
+ result[num] = NULL;
+
+ return( result );
+}
+
+
+static int
+bind_prompt( LDAP *ld,
+ LDAP_CONST char *url,
+ ber_tag_t request, ber_int_t msgid,
+ void *params )
+{
+ static char dn[256], passwd[256];
+ int authmethod;
+
+ printf("rebind for request=%ld msgid=%ld url=%s\n",
+ request, (long) msgid, url );
+
+ authmethod = LDAP_AUTH_SIMPLE;
+
+ get_line( dn, sizeof(dn), stdin, "re-bind dn? " );
+ strcat( dn, dnsuffix );
+
+ if ( authmethod == LDAP_AUTH_SIMPLE && dn[0] != '\0' ) {
+ get_line( passwd, sizeof(passwd), stdin,
+ "re-bind password? " );
+ } else {
+ passwd[0] = '\0';
+ }
+
+ return ldap_bind_s( ld, dn, passwd, authmethod);
+}
+
+
+int
+main( int argc, char **argv )
+{
+ LDAP *ld = NULL;
+ int i, c, port, errflg, method, id, msgtype;
+ char line[256], command1, command2, command3;
+ char passwd[64], dn[256], rdn[64], attr[64], value[256];
+ char filter[256], *host, **types;
+ char **exdn;
+ static const char usage[] =
+ "usage: %s [-u] [-h host] [-d level] [-s dnsuffix] [-p port] [-t file] [-T file]\n";
+ int bound, all, scope, attrsonly;
+ LDAPMessage *res;
+ LDAPMod **mods, **attrs;
+ struct timeval timeout;
+ char *copyfname = NULL;
+ int copyoptions = 0;
+ LDAPURLDesc *ludp;
+
+ host = NULL;
+ port = LDAP_PORT;
+ dnsuffix = "";
+ errflg = 0;
+
+ while (( c = getopt( argc, argv, "h:d:s:p:t:T:" )) != -1 ) {
+ switch( c ) {
+ case 'd':
+#ifdef LDAP_DEBUG
+ ldap_debug = atoi( optarg );
+#ifdef LBER_DEBUG
+ if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
+ ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &ldap_debug );
+ }
+#endif
+#else
+ printf( "Compile with -DLDAP_DEBUG for debugging\n" );
+#endif
+ break;
+
+ case 'h':
+ host = optarg;
+ break;
+
+ case 's':
+ dnsuffix = optarg;
+ break;
+
+ case 'p':
+ port = atoi( optarg );
+ break;
+
+ case 't': /* copy ber's to given file */
+ copyfname = optarg;
+/* copyoptions = LBER_TO_FILE; */
+ break;
+
+ case 'T': /* only output ber's to given file */
+ copyfname = optarg;
+/* copyoptions = (LBER_TO_FILE | LBER_TO_FILE_ONLY); */
+ break;
+
+ default:
+ ++errflg;
+ }
+ }
+
+ if ( host == NULL && optind == argc - 1 ) {
+ host = argv[ optind ];
+ ++optind;
+ }
+
+ if ( errflg || optind < argc - 1 ) {
+ fprintf( stderr, usage, argv[ 0 ] );
+ exit( EXIT_FAILURE );
+ }
+
+ printf( "ldap_init( %s, %d )\n",
+ host == NULL ? "(null)" : host, port );
+
+ ld = ldap_init( host, port );
+
+ if ( ld == NULL ) {
+ perror( "ldap_init" );
+ exit( EXIT_FAILURE );
+ }
+
+ if ( copyfname != NULL ) {
+ if ( ( ld->ld_sb->sb_fd = open( copyfname, O_WRONLY|O_CREAT|O_EXCL,
+ 0600 )) == -1 ) {
+ perror( copyfname );
+ exit ( EXIT_FAILURE );
+ }
+ ld->ld_sb->sb_options = copyoptions;
+ }
+
+ bound = 0;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ (void) memset( line, '\0', sizeof(line) );
+ while ( get_line( line, sizeof(line), stdin, "\ncommand? " ) != NULL ) {
+ command1 = line[0];
+ command2 = line[1];
+ command3 = line[2];
+
+ switch ( command1 ) {
+ case 'a': /* add or abandon */
+ switch ( command2 ) {
+ case 'd': /* add */
+ get_line( dn, sizeof(dn), stdin, "dn? " );
+ strcat( dn, dnsuffix );
+ if ( (attrs = get_modlist( NULL, "attr? ",
+ "value? " )) == NULL )
+ break;
+ if ( (id = ldap_add( ld, dn, attrs )) == -1 )
+ ldap_perror( ld, "ldap_add" );
+ else
+ printf( "Add initiated with id %d\n",
+ id );
+ break;
+
+ case 'b': /* abandon */
+ get_line( line, sizeof(line), stdin, "msgid? " );
+ id = atoi( line );
+ if ( ldap_abandon( ld, id ) != 0 )
+ ldap_perror( ld, "ldap_abandon" );
+ else
+ printf( "Abandon successful\n" );
+ break;
+ default:
+ printf( "Possibilities: [ad]d, [ab]ort\n" );
+ }
+ break;
+
+ case 'b': /* async bind */
+ method = LDAP_AUTH_SIMPLE;
+ get_line( dn, sizeof(dn), stdin, "dn? " );
+ strcat( dn, dnsuffix );
+
+ if ( method == LDAP_AUTH_SIMPLE && dn[0] != '\0' )
+ get_line( passwd, sizeof(passwd), stdin,
+ "password? " );
+ else
+ passwd[0] = '\0';
+
+ if ( ldap_bind( ld, dn, passwd, method ) == -1 ) {
+ fprintf( stderr, "ldap_bind failed\n" );
+ ldap_perror( ld, "ldap_bind" );
+ } else {
+ printf( "Bind initiated\n" );
+ bound = 1;
+ }
+ break;
+
+ case 'B': /* synch bind */
+ method = LDAP_AUTH_SIMPLE;
+ get_line( dn, sizeof(dn), stdin, "dn? " );
+ strcat( dn, dnsuffix );
+
+ if ( dn[0] != '\0' )
+ get_line( passwd, sizeof(passwd), stdin,
+ "password? " );
+ else
+ passwd[0] = '\0';
+
+ if ( ldap_bind_s( ld, dn, passwd, method ) !=
+ LDAP_SUCCESS ) {
+ fprintf( stderr, "ldap_bind_s failed\n" );
+ ldap_perror( ld, "ldap_bind_s" );
+ } else {
+ printf( "Bind successful\n" );
+ bound = 1;
+ }
+ break;
+
+ case 'c': /* compare */
+ get_line( dn, sizeof(dn), stdin, "dn? " );
+ strcat( dn, dnsuffix );
+ get_line( attr, sizeof(attr), stdin, "attr? " );
+ get_line( value, sizeof(value), stdin, "value? " );
+
+ if ( (id = ldap_compare( ld, dn, attr, value )) == -1 )
+ ldap_perror( ld, "ldap_compare" );
+ else
+ printf( "Compare initiated with id %d\n", id );
+ break;
+
+ case 'd': /* turn on debugging */
+#ifdef LDAP_DEBUG
+ get_line( line, sizeof(line), stdin, "debug level? " );
+ ldap_debug = atoi( line );
+#ifdef LBER_DEBUG
+ if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
+ ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &ldap_debug );
+ }
+#endif
+#else
+ printf( "Compile with -DLDAP_DEBUG for debugging\n" );
+#endif
+ break;
+
+ case 'E': /* explode a dn */
+ get_line( line, sizeof(line), stdin, "dn? " );
+ exdn = ldap_explode_dn( line, 0 );
+ for ( i = 0; exdn != NULL && exdn[i] != NULL; i++ ) {
+ printf( "\t%s\n", exdn[i] );
+ }
+ break;
+
+ case 'g': /* set next msgid */
+ get_line( line, sizeof(line), stdin, "msgid? " );
+ ld->ld_msgid = atoi( line );
+ break;
+
+ case 'v': /* set version number */
+ get_line( line, sizeof(line), stdin, "version? " );
+ ld->ld_version = atoi( line );
+ break;
+
+ case 'm': /* modify or modifyrdn */
+ if ( strncmp( line, "modify", 4 ) == 0 ) {
+ get_line( dn, sizeof(dn), stdin, "dn? " );
+ strcat( dn, dnsuffix );
+ if ( (mods = get_modlist(
+ "mod (0=>add, 1=>delete, 2=>replace -1=>done)? ",
+ "attribute type? ", "attribute value? " ))
+ == NULL )
+ break;
+ if ( (id = ldap_modify( ld, dn, mods )) == -1 )
+ ldap_perror( ld, "ldap_modify" );
+ else
+ printf( "Modify initiated with id %d\n",
+ id );
+ } else if ( strncmp( line, "modrdn", 4 ) == 0 ) {
+ get_line( dn, sizeof(dn), stdin, "dn? " );
+ strcat( dn, dnsuffix );
+ get_line( rdn, sizeof(rdn), stdin, "newrdn? " );
+ if ( (id = ldap_modrdn( ld, dn, rdn )) == -1 )
+ ldap_perror( ld, "ldap_modrdn" );
+ else
+ printf( "Modrdn initiated with id %d\n",
+ id );
+ } else {
+ printf( "Possibilities: [modi]fy, [modr]dn\n" );
+ }
+ break;
+
+ case 'q': /* quit */
+ ldap_unbind( ld );
+ exit( EXIT_SUCCESS );
+ break;
+
+ case 'r': /* result or remove */
+ switch ( command3 ) {
+ case 's': /* result */
+ get_line( line, sizeof(line), stdin,
+ "msgid (-1=>any)? " );
+ if ( line[0] == '\0' )
+ id = -1;
+ else
+ id = atoi( line );
+ get_line( line, sizeof(line), stdin,
+ "all (0=>any, 1=>all)? " );
+ if ( line[0] == '\0' )
+ all = 1;
+ else
+ all = atoi( line );
+ if (( msgtype = ldap_result( ld, id, all,
+ &timeout, &res )) < 1 ) {
+ ldap_perror( ld, "ldap_result" );
+ break;
+ }
+ printf( "\nresult: msgtype %d msgid %d\n",
+ msgtype, res->lm_msgid );
+ handle_result( ld, res );
+ res = NULL;
+ break;
+
+ case 'm': /* remove */
+ get_line( dn, sizeof(dn), stdin, "dn? " );
+ strcat( dn, dnsuffix );
+ if ( (id = ldap_delete( ld, dn )) == -1 )
+ ldap_perror( ld, "ldap_delete" );
+ else
+ printf( "Remove initiated with id %d\n",
+ id );
+ break;
+
+ default:
+ printf( "Possibilities: [rem]ove, [res]ult\n" );
+ break;
+ }
+ break;
+
+ case 's': /* search */
+ get_line( dn, sizeof(dn), stdin, "searchbase? " );
+ strcat( dn, dnsuffix );
+ get_line( line, sizeof(line), stdin,
+ "scope (0=baseObject, 1=oneLevel, 2=subtree, 3=children)? " );
+ scope = atoi( line );
+ get_line( filter, sizeof(filter), stdin,
+ "search filter (e.g. sn=jones)? " );
+ types = get_list( "attrs to return? " );
+ get_line( line, sizeof(line), stdin,
+ "attrsonly (0=attrs&values, 1=attrs only)? " );
+ attrsonly = atoi( line );
+
+ if (( id = ldap_search( ld, dn, scope, filter,
+ types, attrsonly )) == -1 ) {
+ ldap_perror( ld, "ldap_search" );
+ } else {
+ printf( "Search initiated with id %d\n", id );
+ }
+ free_list( types );
+ break;
+
+ case 't': /* set timeout value */
+ get_line( line, sizeof(line), stdin, "timeout? " );
+ timeout.tv_sec = atoi( line );
+ break;
+
+ case 'p': /* parse LDAP URL */
+ get_line( line, sizeof(line), stdin, "LDAP URL? " );
+ if (( i = ldap_url_parse( line, &ludp )) != 0 ) {
+ fprintf( stderr, "ldap_url_parse: error %d\n", i );
+ } else {
+ printf( "\t host: " );
+ if ( ludp->lud_host == NULL ) {
+ printf( "DEFAULT\n" );
+ } else {
+ printf( "<%s>\n", ludp->lud_host );
+ }
+ printf( "\t port: " );
+ if ( ludp->lud_port == 0 ) {
+ printf( "DEFAULT\n" );
+ } else {
+ printf( "%d\n", ludp->lud_port );
+ }
+ printf( "\t dn: <%s>\n", ludp->lud_dn );
+ printf( "\t attrs:" );
+ if ( ludp->lud_attrs == NULL ) {
+ printf( " ALL" );
+ } else {
+ for ( i = 0; ludp->lud_attrs[ i ] != NULL; ++i ) {
+ printf( " <%s>", ludp->lud_attrs[ i ] );
+ }
+ }
+ printf( "\n\t scope: %s\n",
+ ludp->lud_scope == LDAP_SCOPE_BASE ? "baseObject"
+ : ludp->lud_scope == LDAP_SCOPE_ONELEVEL ? "oneLevel"
+ : ludp->lud_scope == LDAP_SCOPE_SUBTREE ? "subtree"
+#ifdef LDAP_SCOPE_SUBORDINATE
+ : ludp->lud_scope == LDAP_SCOPE_SUBORDINATE ? "children"
+#endif
+ : "**invalid**" );
+ printf( "\tfilter: <%s>\n", ludp->lud_filter );
+ ldap_free_urldesc( ludp );
+ }
+ break;
+
+ case 'n': /* set dn suffix, for convenience */
+ get_line( line, sizeof(line), stdin, "DN suffix? " );
+ strcpy( dnsuffix, line );
+ break;
+
+ case 'o': /* set ldap options */
+ get_line( line, sizeof(line), stdin, "alias deref (0=never, 1=searching, 2=finding, 3=always)?" );
+ ld->ld_deref = atoi( line );
+ get_line( line, sizeof(line), stdin, "timelimit?" );
+ ld->ld_timelimit = atoi( line );
+ get_line( line, sizeof(line), stdin, "sizelimit?" );
+ ld->ld_sizelimit = atoi( line );
+
+ LDAP_BOOL_ZERO(&ld->ld_options);
+
+ get_line( line, sizeof(line), stdin,
+ "Recognize and chase referrals (0=no, 1=yes)?" );
+ if ( atoi( line ) != 0 ) {
+ LDAP_BOOL_SET(&ld->ld_options, LDAP_BOOL_REFERRALS);
+ get_line( line, sizeof(line), stdin,
+ "Prompt for bind credentials when chasing referrals (0=no, 1=yes)?" );
+ if ( atoi( line ) != 0 ) {
+ ldap_set_rebind_proc( ld, bind_prompt, NULL );
+ }
+ }
+ break;
+
+ case '?': /* help */
+ printf(
+"Commands: [ad]d [ab]andon [b]ind\n"
+" [B]ind async [c]ompare\n"
+" [modi]fy [modr]dn [rem]ove\n"
+" [res]ult [s]earch [q]uit/unbind\n\n"
+" [d]ebug set ms[g]id\n"
+" d[n]suffix [t]imeout [v]ersion\n"
+" [?]help [o]ptions"
+" [E]xplode dn [p]arse LDAP URL\n" );
+ break;
+
+ default:
+ printf( "Invalid command. Type ? for help.\n" );
+ break;
+ }
+
+ (void) memset( line, '\0', sizeof(line) );
+ }
+
+ return( 0 );
+}
+
+static void
+handle_result( LDAP *ld, LDAPMessage *lm )
+{
+ switch ( lm->lm_msgtype ) {
+ case LDAP_RES_COMPARE:
+ printf( "Compare result\n" );
+ print_ldap_result( ld, lm, "compare" );
+ break;
+
+ case LDAP_RES_SEARCH_RESULT:
+ printf( "Search result\n" );
+ print_ldap_result( ld, lm, "search" );
+ break;
+
+ case LDAP_RES_SEARCH_ENTRY:
+ printf( "Search entry\n" );
+ print_search_entry( ld, lm );
+ break;
+
+ case LDAP_RES_ADD:
+ printf( "Add result\n" );
+ print_ldap_result( ld, lm, "add" );
+ break;
+
+ case LDAP_RES_DELETE:
+ printf( "Delete result\n" );
+ print_ldap_result( ld, lm, "delete" );
+ break;
+
+ case LDAP_RES_MODRDN:
+ printf( "ModRDN result\n" );
+ print_ldap_result( ld, lm, "modrdn" );
+ break;
+
+ case LDAP_RES_BIND:
+ printf( "Bind result\n" );
+ print_ldap_result( ld, lm, "bind" );
+ break;
+
+ default:
+ printf( "Unknown result type 0x%lx\n",
+ (unsigned long) lm->lm_msgtype );
+ print_ldap_result( ld, lm, "unknown" );
+ }
+}
+
+static void
+print_ldap_result( LDAP *ld, LDAPMessage *lm, const char *s )
+{
+ ldap_result2error( ld, lm, 1 );
+ ldap_perror( ld, s );
+/*
+ if ( ld->ld_error != NULL && *ld->ld_error != '\0' )
+ fprintf( stderr, "Additional info: %s\n", ld->ld_error );
+ if ( LDAP_NAME_ERROR( ld->ld_errno ) && ld->ld_matched != NULL )
+ fprintf( stderr, "Matched DN: %s\n", ld->ld_matched );
+*/
+}
+
+static void
+print_search_entry( LDAP *ld, LDAPMessage *res )
+{
+ LDAPMessage *e;
+
+ for ( e = ldap_first_entry( ld, res ); e != NULL;
+ e = ldap_next_entry( ld, e ) )
+ {
+ BerElement *ber = NULL;
+ char *a, *dn, *ufn;
+
+ if ( e->lm_msgtype == LDAP_RES_SEARCH_RESULT )
+ break;
+
+ dn = ldap_get_dn( ld, e );
+ printf( "\tDN: %s\n", dn );
+
+ ufn = ldap_dn2ufn( dn );
+ printf( "\tUFN: %s\n", ufn );
+
+ free( dn );
+ free( ufn );
+
+ for ( a = ldap_first_attribute( ld, e, &ber ); a != NULL;
+ a = ldap_next_attribute( ld, e, ber ) )
+ {
+ struct berval **vals;
+
+ printf( "\t\tATTR: %s\n", a );
+ if ( (vals = ldap_get_values_len( ld, e, a ))
+ == NULL ) {
+ printf( "\t\t\t(no values)\n" );
+ } else {
+ int i;
+ for ( i = 0; vals[i] != NULL; i++ ) {
+ int j, nonascii;
+
+ nonascii = 0;
+ for ( j = 0; (ber_len_t) j < vals[i]->bv_len; j++ )
+ if ( !isascii( vals[i]->bv_val[j] ) ) {
+ nonascii = 1;
+ break;
+ }
+
+ if ( nonascii ) {
+ printf( "\t\t\tlength (%ld) (not ascii)\n", vals[i]->bv_len );
+#ifdef BPRINT_NONASCII
+ ber_bprint( vals[i]->bv_val,
+ vals[i]->bv_len );
+#endif /* BPRINT_NONASCII */
+ continue;
+ }
+ printf( "\t\t\tlength (%ld) %s\n",
+ vals[i]->bv_len, vals[i]->bv_val );
+ }
+ ber_bvecfree( vals );
+ }
+ }
+
+ if(ber != NULL) {
+ ber_free( ber, 0 );
+ }
+ }
+
+ if ( res->lm_msgtype == LDAP_RES_SEARCH_RESULT
+ || res->lm_chain != NULL )
+ print_ldap_result( ld, res, "search" );
+}
diff --git a/libraries/libldap/testavl.c b/libraries/libldap/testavl.c
new file mode 100644
index 0000000..c21584d
--- /dev/null
+++ b/libraries/libldap/testavl.c
@@ -0,0 +1,150 @@
+/* testavl.c - Test Tim Howes AVL code */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1993 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by the University of Michigan
+ * (as part of U-MICH LDAP).
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/string.h>
+
+#define AVL_INTERNAL
+#define AVL_NONREENTRANT
+#include "ldap_avl.h"
+
+static void ravl_print LDAP_P(( Avlnode *root, int depth ));
+static void myprint LDAP_P(( Avlnode *root ));
+static int avl_strcmp LDAP_P(( const void *s, const void *t ));
+
+int
+main( int argc, char **argv )
+{
+ Avlnode *tree = NULL;
+ char command[ 10 ];
+ char name[ 80 ];
+ char *p;
+
+ printf( "> " );
+ while ( fgets( command, sizeof( command ), stdin ) != NULL ) {
+ switch( *command ) {
+ case 'n': /* new tree */
+ ( void ) ldap_avl_free( tree, free );
+ tree = NULL;
+ break;
+ case 'p': /* print */
+ ( void ) myprint( tree );
+ break;
+ case 't': /* traverse with first, next */
+#ifdef AVL_NONREENTRANT
+ printf( "***\n" );
+ for ( p = (char * ) ldap_avl_getfirst( tree );
+ p != NULL;
+ p = (char *) ldap_avl_getnext())
+ printf( "%s\n", p );
+ printf( "***\n" );
+#else
+ printf( "*** reentrant interface not implemented ***" );
+#endif
+ break;
+ case 'f': /* find */
+ printf( "data? " );
+ if ( fgets( name, sizeof( name ), stdin ) == NULL )
+ exit( EXIT_SUCCESS );
+ name[ strlen( name ) - 1 ] = '\0';
+ if ( (p = (char *) ldap_avl_find( tree, name, avl_strcmp ))
+ == NULL )
+ printf( "Not found.\n\n" );
+ else
+ printf( "%s\n\n", p );
+ break;
+ case 'i': /* insert */
+ printf( "data? " );
+ if ( fgets( name, sizeof( name ), stdin ) == NULL )
+ exit( EXIT_SUCCESS );
+ name[ strlen( name ) - 1 ] = '\0';
+ if ( ldap_avl_insert( &tree, strdup( name ), avl_strcmp,
+ ldap_avl_dup_error ) != 0 )
+ printf( "\nNot inserted!\n" );
+ break;
+ case 'd': /* delete */
+ printf( "data? " );
+ if ( fgets( name, sizeof( name ), stdin ) == NULL )
+ exit( EXIT_SUCCESS );
+ name[ strlen( name ) - 1 ] = '\0';
+ if ( ldap_avl_delete( &tree, name, avl_strcmp ) == NULL )
+ printf( "\nNot found!\n" );
+ break;
+ case 'q': /* quit */
+ exit( EXIT_SUCCESS );
+ break;
+ case '\n':
+ break;
+ default:
+ printf("Commands: insert, delete, print, new, quit\n");
+ }
+
+ printf( "> " );
+ }
+
+ return( 0 );
+}
+
+static void ravl_print( Avlnode *root, int depth )
+{
+ int i;
+
+ if ( root == 0 )
+ return;
+
+ ravl_print( root->avl_right, depth+1 );
+
+ for ( i = 0; i < depth; i++ )
+ printf( " " );
+ printf( "%s %d\n", (char *) root->avl_data, root->avl_bf );
+
+ ravl_print( root->avl_left, depth+1 );
+}
+
+static void myprint( Avlnode *root )
+{
+ printf( "********\n" );
+
+ if ( root == 0 )
+ printf( "\tNULL\n" );
+ else
+ ravl_print( root, 0 );
+
+ printf( "********\n" );
+}
+
+static int avl_strcmp( const void *s, const void *t )
+{
+ return strcmp( s, t );
+}
diff --git a/libraries/libldap/testtavl.c b/libraries/libldap/testtavl.c
new file mode 100644
index 0000000..0956642
--- /dev/null
+++ b/libraries/libldap/testtavl.c
@@ -0,0 +1,158 @@
+/* testavl.c - Test Tim Howes AVL code */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1993 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by the University of Michigan
+ * (as part of U-MICH LDAP). Additional contributors include
+ * Howard Chu
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/string.h>
+
+#define AVL_INTERNAL
+#include "ldap_avl.h"
+
+static void ravl_print LDAP_P(( TAvlnode *root, int depth, int thread ));
+static void myprint LDAP_P(( TAvlnode *root ));
+static int avl_strcmp LDAP_P(( const void *s, const void *t ));
+
+int
+main( int argc, char **argv )
+{
+ TAvlnode *tree = NULL, *n;
+ char command[ 10 ];
+ char name[ 80 ];
+ char *p;
+
+ printf( "> " );
+ while ( fgets( command, sizeof( command ), stdin ) != NULL ) {
+ switch( *command ) {
+ case 'n': /* new tree */
+ ( void ) ldap_tavl_free( tree, free );
+ tree = NULL;
+ break;
+ case 'p': /* print */
+ ( void ) myprint( tree );
+ break;
+ case 't': /* traverse with first, next */
+ printf( "***\n" );
+ for ( n = ldap_tavl_end( tree, TAVL_DIR_LEFT );
+ n != NULL;
+ n = ldap_tavl_next( n, TAVL_DIR_RIGHT ))
+ printf( "%s\n", n->avl_data );
+ printf( "***\n" );
+ break;
+ case 'f': /* find */
+ printf( "data? " );
+ if ( fgets( name, sizeof( name ), stdin ) == NULL )
+ exit( EXIT_SUCCESS );
+ name[ strlen( name ) - 1 ] = '\0';
+ if ( (p = (char *) ldap_tavl_find( tree, name, avl_strcmp ))
+ == NULL )
+ printf( "Not found.\n\n" );
+ else
+ printf( "%s\n\n", p );
+ break;
+ case 'i': /* insert */
+ printf( "data? " );
+ if ( fgets( name, sizeof( name ), stdin ) == NULL )
+ exit( EXIT_SUCCESS );
+ name[ strlen( name ) - 1 ] = '\0';
+ if ( ldap_tavl_insert( &tree, strdup( name ), avl_strcmp,
+ ldap_avl_dup_error ) != 0 )
+ printf( "\nNot inserted!\n" );
+ break;
+ case 'd': /* delete */
+ printf( "data? " );
+ if ( fgets( name, sizeof( name ), stdin ) == NULL )
+ exit( EXIT_SUCCESS );
+ name[ strlen( name ) - 1 ] = '\0';
+ if ( ldap_tavl_delete( &tree, name, avl_strcmp ) == NULL )
+ printf( "\nNot found!\n" );
+ break;
+ case 'q': /* quit */
+ exit( EXIT_SUCCESS );
+ break;
+ case '\n':
+ break;
+ default:
+ printf("Commands: insert, delete, print, new, quit\n");
+ }
+
+ printf( "> " );
+ }
+
+ return( 0 );
+}
+
+static const char bfc_array[] = "\\-/";
+static const char *bfcs = bfc_array+1;
+
+static void ravl_print( TAvlnode *root, int depth, int thread )
+{
+ int i;
+
+ if ( root && !thread )
+ ravl_print( root->avl_link[1], depth+1, root->avl_bits[1] == AVL_THREAD );
+
+ for ( i = 0; i < depth; i++ )
+ printf( " " );
+ if ( thread )
+ printf( "~" );
+ else if ( root )
+ printf( "%c", bfcs[root->avl_bf] );
+ else
+ printf( " " );
+ if ( !root) {
+ printf( ".\n" );
+ return;
+ }
+ printf( "%s\n", (char *) root->avl_data );
+
+ if ( !thread )
+ ravl_print( root->avl_link[0], depth+1, root->avl_bits[0] == AVL_THREAD );
+}
+
+static void myprint( TAvlnode *root )
+{
+ printf( "********\n" );
+
+ if ( root == 0 )
+ printf( "\tNULL\n" );
+ else
+ ravl_print( root, 0, 0 );
+
+ printf( "********\n" );
+}
+
+static int avl_strcmp( const void *s, const void *t )
+{
+ return strcmp( s, t );
+}
diff --git a/libraries/libldap/thr_debug.c b/libraries/libldap/thr_debug.c
new file mode 100644
index 0000000..9e028b3
--- /dev/null
+++ b/libraries/libldap/thr_debug.c
@@ -0,0 +1,1338 @@
+/* thr_debug.c - wrapper around the chosen thread wrapper, for debugging. */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * This package provides several types of thread operation debugging:
+ *
+ * - Check the results of operations on threads, mutexes, condition
+ * variables and read/write locks. Also check some thread pool
+ * operations, but not those for which failure can happen in normal
+ * slapd operation.
+ *
+ * - Wrap those types except threads and pools in structs with state
+ * information, and check that on all operations:
+ *
+ * + Check that the resources are initialized and are only used at
+ * their original address (i.e. not realloced or copied).
+ *
+ * + Check the owner (thread ID) on mutex operations.
+ *
+ * + Optionally allocate a reference to a byte of dummy memory.
+ * This lets malloc debuggers see some incorrect use as memory
+ * leaks, access to freed memory, etc.
+ *
+ * - Print an error message and by default abort() upon errors.
+ *
+ * - Print a count of leaked thread resources after cleanup.
+ *
+ * Compile-time (./configure) setup: Macros defined in CPPFLAGS.
+ *
+ * LDAP_THREAD_DEBUG or LDAP_THREAD_DEBUG=2
+ * Enables debugging, but value & 2 turns off type wrapping.
+ *
+ * LDAP_UINTPTR_T=integer type to hold pointers, preferably unsigned.
+ * Used by dummy memory option "scramble". Default = unsigned long.
+ *
+ * LDAP_DEBUG_THREAD_NONE = initializer for a "no thread" thread ID.
+ *
+ * In addition, you may need to set up an implementation-specific way
+ * to enable whatever error checking your thread library provides.
+ * Currently only implemented for Posix threads (pthreads), where
+ * you may need to define LDAP_INT_THREAD_MUTEXATTR. The default
+ * is PTHREAD_MUTEX_ERRORCHECK, or PTHREAD_MUTEX_ERRORCHECK_NP for
+ * Linux threads. See pthread_mutexattr_settype(3).
+ *
+ * Run-time configuration:
+ *
+ * Memory debugging tools:
+ * Tools that report uninitialized memory accesses should disable
+ * such warnings about the function debug_already_initialized().
+ * Alternatively, include "noreinit" (below) in $LDAP_THREAD_DEBUG.
+ *
+ * Environment variable $LDAP_THREAD_DEBUG:
+ * The variable may contain a comma- or space-separated option list.
+ * Options:
+ * off - Disable this package. (It still slows things down).
+ * tracethreads - Report create/join/exit/kill of threads.
+ * noabort - Do not abort() on errors.
+ * noerror - Do not report errors. Implies noabort.
+ * nocount - Do not report counts of unreleased resources.
+ * nosync - Disable tests that use synchronization and thus
+ * clearly affect thread scheduling:
+ * Implies nocount, and cancels threadID if that is set.
+ * Note that if you turn on tracethreads or malloc
+ * debugging, these also use library calls which may
+ * affect thread scheduling (fprintf and malloc).
+ * The following options do not apply if type wrapping is disabled:
+ * nomem - Do not check memory operations.
+ * Implies noreinit,noalloc.
+ * noreinit - Do not catch reinitialization of existing resources.
+ * (That test accesses uninitialized memory).
+ * threadID - Trace thread IDs. Currently mostly useless.
+ * Malloc debugging -- allocate dummy memory for initialized
+ * resources, so malloc debuggers will report them as memory leaks:
+ * noalloc - Default. Do not allocate dummy memory.
+ * alloc - Store a pointer to dummy memory. However, leak
+ * detectors might not catch unreleased resources in
+ * global variables.
+ * scramble - Store bitwise complement of dummy memory pointer.
+ * That never escapes memory leak detectors -
+ * but detection while the program is running will
+ * report active resources as leaks. Do not
+ * use this if a garbage collector is in use:-)
+ * adjptr - Point to end of dummy memory.
+ * Purify reports these as "potential leaks" (PLK).
+ * I have not checked other malloc debuggers.
+ */
+
+#include "portable.h"
+
+#if defined( LDAP_THREAD_DEBUG )
+
+#include <stdio.h>
+#include <ac/errno.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_IMPLEMENTATION
+#define LDAP_THREAD_DEBUG_IMPLEMENTATION
+#define LDAP_THREAD_RDWR_IMPLEMENTATION
+#define LDAP_THREAD_POOL_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* Get the underlying implementation */
+
+#ifndef LDAP_THREAD_DEBUG_WRAP
+#undef LDAP_THREAD_DEBUG_THREAD_ID
+#elif !defined LDAP_THREAD_DEBUG_THREAD_ID
+#define LDAP_THREAD_DEBUG_THREAD_ID 1
+#endif
+
+/* Use native malloc - the OpenLDAP wrappers may defeat malloc debuggers */
+#undef malloc
+#undef calloc
+#undef realloc
+#undef free
+
+
+/* Options from environment variable $LDAP_THREAD_DEBUG */
+enum { Count_no = 0, Count_yes, Count_reported, Count_reported_more };
+static int count = Count_yes;
+#ifdef LDAP_THREAD_DEBUG_WRAP
+enum { Wrap_noalloc, Wrap_alloc, Wrap_scramble, Wrap_adjptr };
+static int wraptype = Wrap_noalloc, wrap_offset, unwrap_offset;
+static int nomem, noreinit;
+#endif
+#if LDAP_THREAD_DEBUG_THREAD_ID +0
+static int threadID;
+#else
+enum { threadID = 0 };
+#endif
+static int nodebug, noabort, noerror, nosync, tracethreads;
+static int wrap_threads;
+static int options_done;
+
+
+/* ldap_pvt_thread_initialize() called, ldap_pvt_thread_destroy() not called */
+static int threading_enabled;
+
+
+/* Resource counts */
+enum {
+ Idx_unexited_thread, Idx_unjoined_thread, Idx_locked_mutex,
+ Idx_mutex, Idx_cond, Idx_rdwr, Idx_tpool, Idx_max
+};
+static int resource_counts[Idx_max];
+static const char *const resource_names[] = {
+ "unexited threads", "unjoined threads", "locked mutexes",
+ "mutexes", "conds", "rdwrs", "thread pools"
+};
+static ldap_int_thread_mutex_t resource_mutexes[Idx_max];
+
+
+/* Hide pointers from malloc debuggers. */
+#define SCRAMBLE(ptr) (~(LDAP_UINTPTR_T) (ptr))
+#define UNSCRAMBLE_usagep(num) ((ldap_debug_usage_info_t *) ~(num))
+#define UNSCRAMBLE_dummyp(num) ((unsigned char *) ~(num))
+
+
+#define WARN(var, msg) (warn (__FILE__, __LINE__, (msg), #var, (var)))
+#define WARN_IF(rc, msg) {if (rc) warn (__FILE__, __LINE__, (msg), #rc, (rc));}
+
+#define ERROR(var, msg) { \
+ if (!noerror) { \
+ errmsg(__FILE__, __LINE__, (msg), #var, (var)); \
+ if( !noabort ) abort(); \
+ } \
+}
+
+#define ERROR_IF(rc, msg) { \
+ if (!noerror) { \
+ int rc_ = (rc); \
+ if (rc_) { \
+ errmsg(__FILE__, __LINE__, (msg), #rc, rc_); \
+ if( !noabort ) abort(); \
+ } \
+ } \
+}
+
+#ifdef LDAP_THREAD_DEBUG_WRAP
+#define MEMERROR_IF(rc, msg, mem_act) { \
+ if (!noerror) { \
+ int rc_ = (rc); \
+ if (rc_) { \
+ errmsg(__FILE__, __LINE__, (msg), #rc, rc_); \
+ if( wraptype != Wrap_noalloc ) { mem_act; } \
+ if( !noabort ) abort(); \
+ } \
+ } \
+}
+#endif /* LDAP_THREAD_DEBUG_WRAP */
+
+#if 0
+static void
+warn( const char *file, int line, const char *msg, const char *var, int val )
+{
+ fprintf( stderr,
+ (strpbrk( var, "!=" )
+ ? "%s:%d: %s warning: %s\n"
+ : "%s:%d: %s warning: %s is %d\n"),
+ file, line, msg, var, val );
+}
+#endif
+
+static void
+errmsg( const char *file, int line, const char *msg, const char *var, int val )
+{
+ fprintf( stderr,
+ (strpbrk( var, "!=" )
+ ? "%s:%d: %s error: %s\n"
+ : "%s:%d: %s error: %s is %d\n"),
+ file, line, msg, var, val );
+}
+
+static void
+count_resource_leaks( void )
+{
+ int i, j;
+ char errbuf[200];
+ if( count == Count_yes ) {
+ count = Count_reported;
+#if 0 /* Could break if there are still threads after atexit */
+ for( i = j = 0; i < Idx_max; i++ )
+ j |= ldap_int_thread_mutex_destroy( &resource_mutexes[i] );
+ WARN_IF( j, "ldap_debug_thread_destroy:mutexes" );
+#endif
+ for( i = j = 0; i < Idx_max; i++ )
+ if( resource_counts[i] )
+ j += sprintf( errbuf + j, ", %d %s",
+ resource_counts[i], resource_names[i] );
+ if( j )
+ fprintf( stderr, "== thr_debug: Leaked%s. ==\n", errbuf + 1 );
+ }
+}
+
+static void
+get_options( void )
+{
+ static const struct option_info_s {
+ const char *name;
+ int *var, val;
+ } option_info[] = {
+ { "off", &nodebug, 1 },
+ { "noabort", &noabort, 1 },
+ { "noerror", &noerror, 1 },
+ { "nocount", &count, Count_no },
+ { "nosync", &nosync, 1 },
+#if LDAP_THREAD_DEBUG_THREAD_ID +0
+ { "threadID", &threadID, 1 },
+#endif
+#ifdef LDAP_THREAD_DEBUG_WRAP
+ { "nomem", &nomem, 1 },
+ { "noreinit", &noreinit, 1 },
+ { "noalloc", &wraptype, Wrap_noalloc },
+ { "alloc", &wraptype, Wrap_alloc },
+ { "adjptr", &wraptype, Wrap_adjptr },
+ { "scramble", &wraptype, Wrap_scramble },
+#endif
+ { "tracethreads", &tracethreads, 1 },
+ { NULL, NULL, 0 }
+ };
+ const char *s = getenv( "LDAP_THREAD_DEBUG" );
+ if( s != NULL ) {
+ while( *(s += strspn( s, ", \t\r\n" )) != '\0' ) {
+ size_t optlen = strcspn( s, ", \t\r\n" );
+ const struct option_info_s *oi = option_info;
+ while( oi->name &&
+ (strncasecmp( oi->name, s, optlen ) || oi->name[optlen]) )
+ oi++;
+ if( oi->name )
+ *oi->var = oi->val;
+ else
+ fprintf( stderr,
+ "== thr_debug: Unknown $%s option '%.*s' ==\n",
+ "LDAP_THREAD_DEBUG", (int) optlen, s );
+ s += optlen;
+ }
+ }
+ if( nodebug ) {
+ tracethreads = 0;
+ nosync = noerror = 1;
+ }
+ if( nosync )
+ count = Count_no;
+ if( noerror )
+ noabort = 1;
+#if LDAP_THREAD_DEBUG_THREAD_ID +0
+ if( nosync )
+ threadID = 0;
+#endif
+#ifdef LDAP_THREAD_DEBUG_WRAP
+ if( noerror )
+ nomem = 1;
+ if( !nomem ) {
+ static const ldap_debug_usage_info_t usage;
+ if( sizeof(LDAP_UINTPTR_T) < sizeof(unsigned char *)
+ || sizeof(LDAP_UINTPTR_T) < sizeof(ldap_debug_usage_info_t *)
+ || UNSCRAMBLE_usagep( SCRAMBLE( &usage ) ) != &usage
+ || UNSCRAMBLE_dummyp( SCRAMBLE( (unsigned char *) 0 ) ) )
+ {
+ fputs( "== thr_debug: Memory checks unsupported, "
+ "adding nomem to $LDAP_THREAD_DEBUG ==\n", stderr );
+ nomem = 1;
+ }
+ }
+ if( nomem ) {
+ noreinit = 1;
+ wraptype = Wrap_noalloc;
+ }
+ unwrap_offset = -(wrap_offset = (wraptype == Wrap_adjptr));
+#endif
+ wrap_threads = (tracethreads || threadID || count);
+ options_done = 1;
+}
+
+
+#ifndef LDAP_THREAD_DEBUG_WRAP
+
+#define WRAPPED(ptr) (ptr)
+#define GET_OWNER(ptr) 0
+#define SET_OWNER(ptr, thread) ((void) 0)
+#define RESET_OWNER(ptr) ((void) 0)
+#define ASSERT_OWNER(ptr, msg) ((void) 0)
+#define ASSERT_NO_OWNER(ptr, msg) ((void) 0)
+
+#define init_usage(ptr, msg) ((void) 0)
+#define check_usage(ptr, msg) ((void) 0)
+#define destroy_usage(ptr) ((void) 0)
+
+#else /* LDAP_THREAD_DEBUG_WRAP */
+
+/* Specialize this if the initializer is not appropriate. */
+/* The ASSERT_NO_OWNER() definition may also need an override. */
+#ifndef LDAP_DEBUG_THREAD_NONE
+#define LDAP_DEBUG_THREAD_NONE { -1 } /* "no thread" ldap_int_thread_t value */
+#endif
+
+static const ldap_int_thread_t ldap_debug_thread_none = LDAP_DEBUG_THREAD_NONE;
+
+#define THREAD_MUTEX_OWNER(mutex) \
+ ldap_int_thread_equal( (mutex)->owner, ldap_int_thread_self() )
+
+void
+ldap_debug_thread_assert_mutex_owner(
+ const char *file,
+ int line,
+ const char *msg,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ if( !(noerror || THREAD_MUTEX_OWNER( mutex )) ) {
+ errmsg( file, line, msg, "ASSERT_MUTEX_OWNER", 0 );
+ if( !noabort ) abort();
+ }
+}
+
+#define WRAPPED(ptr) (&(ptr)->wrapped)
+#define GET_OWNER(ptr) ((ptr)->owner)
+#define SET_OWNER(ptr, thread) ((ptr)->owner = (thread))
+#define RESET_OWNER(ptr) ((ptr)->owner = ldap_debug_thread_none)
+#define ASSERT_OWNER(ptr, msg) ERROR_IF( !THREAD_MUTEX_OWNER( ptr ), msg )
+#ifndef ASSERT_NO_OWNER
+#define ASSERT_NO_OWNER(ptr, msg) ERROR_IF( \
+ !ldap_int_thread_equal( (ptr)->owner, ldap_debug_thread_none ), msg )
+#endif
+
+/* Try to provoke memory access error (for malloc debuggers) */
+#define PEEK(mem) {if (-*(volatile const unsigned char *)(mem)) debug_noop();}
+
+static void debug_noop( void );
+static int debug_already_initialized( const ldap_debug_usage_info_t *usage );
+
+/* Name used for clearer error message */
+#define IS_COPY_OR_MOVED(usage) ((usage)->self != SCRAMBLE( usage ))
+
+#define DUMMY_ADDR(usage) \
+ (wraptype == Wrap_scramble \
+ ? UNSCRAMBLE_dummyp( (usage)->mem.num ) \
+ : (usage)->mem.ptr + unwrap_offset)
+
+/* Mark resource as initialized */
+static void
+init_usage( ldap_debug_usage_info_t *usage, const char *msg )
+{
+ if( !options_done )
+ get_options();
+ if( !nomem ) {
+ if( !noreinit ) {
+ MEMERROR_IF( debug_already_initialized( usage ), msg, {
+ /* Provoke malloc debuggers */
+ unsigned char *dummy = DUMMY_ADDR( usage );
+ PEEK( dummy );
+ free( dummy );
+ free( dummy );
+ } );
+ }
+ if( wraptype != Wrap_noalloc ) {
+ unsigned char *dummy = malloc( 1 );
+ assert( dummy != NULL );
+ if( wraptype == Wrap_scramble ) {
+ usage->mem.num = SCRAMBLE( dummy );
+ /* Verify that ptr<->integer casts work on this host */
+ assert( UNSCRAMBLE_dummyp( usage->mem.num ) == dummy );
+ } else {
+ usage->mem.ptr = dummy + wrap_offset;
+ }
+ }
+ } else {
+ /* Unused, but set for readability in debugger */
+ usage->mem.ptr = NULL;
+ }
+ usage->self = SCRAMBLE( usage ); /* If nomem, only for debugger */
+ usage->magic = ldap_debug_magic;
+ usage->state = ldap_debug_state_inited;
+}
+
+/* Check that resource is initialized and not copied/realloced */
+static void
+check_usage( const ldap_debug_usage_info_t *usage, const char *msg )
+{
+ enum { Is_destroyed = 1 }; /* Name used for clearer error message */
+
+ if( usage->magic != ldap_debug_magic ) {
+ ERROR( usage->magic, msg );
+ return;
+ }
+ switch( usage->state ) {
+ case ldap_debug_state_destroyed:
+ MEMERROR_IF( Is_destroyed, msg, {
+ PEEK( DUMMY_ADDR( usage ) );
+ } );
+ break;
+ default:
+ ERROR( usage->state, msg );
+ break;
+ case ldap_debug_state_inited:
+ if( !nomem ) {
+ MEMERROR_IF( IS_COPY_OR_MOVED( usage ), msg, {
+ PEEK( DUMMY_ADDR( usage ) );
+ PEEK( UNSCRAMBLE_usagep( usage->self ) );
+ } );
+ }
+ break;
+ }
+}
+
+/* Mark resource as destroyed. */
+/* Does not check for errors, call check_usage()/init_usage() first. */
+static void
+destroy_usage( ldap_debug_usage_info_t *usage )
+{
+ if( usage->state == ldap_debug_state_inited ) {
+ if( wraptype != Wrap_noalloc ) {
+ free( DUMMY_ADDR( usage ) );
+ /* Do not reset the DUMMY_ADDR, leave it for malloc debuggers
+ * in case the resource is used after it is freed. */
+ }
+ usage->state = ldap_debug_state_destroyed;
+ }
+}
+
+/* Define these after they are used, so they are hopefully not inlined */
+
+static void
+debug_noop( void )
+{
+}
+
+/*
+ * Valid programs access uninitialized memory here unless "noreinit".
+ *
+ * Returns true if the resource is initialized and not copied/realloced.
+ */
+LDAP_GCCATTR((noinline))
+static int
+debug_already_initialized( const ldap_debug_usage_info_t *usage )
+{
+ /*
+ * 'ret' keeps the Valgrind warning "Conditional jump or move
+ * depends on uninitialised value(s)" _inside_ this function.
+ */
+ volatile int ret = 0;
+ if( usage->state == ldap_debug_state_inited )
+ if( !IS_COPY_OR_MOVED( usage ) )
+ if( usage->magic == ldap_debug_magic )
+ ret = 1;
+ return ret;
+}
+
+#endif /* LDAP_THREAD_DEBUG_WRAP */
+
+
+#if !(LDAP_THREAD_DEBUG_THREAD_ID +0)
+
+typedef void ldap_debug_thread_t;
+#define init_thread_info() {}
+#define with_thread_info_lock(statements) { statements; }
+#define thread_info_detached(t) 0
+#define add_thread_info(msg, thr, det) ((void) 0)
+#define remove_thread_info(tinfo, msg) ((void) 0)
+#define get_thread_info(thread, msg) NULL
+
+#else /* LDAP_THREAD_DEBUG_THREAD_ID */
+
+/*
+ * Thread ID tracking. Currently achieves little.
+ * Should be either expanded or deleted.
+ */
+
+/*
+ * Array of threads. Used instead of making ldap_pvt_thread_t a wrapper
+ * around ldap_int_thread_t, which would slow down ldap_pvt_thread_self().
+ */
+typedef struct {
+ ldap_pvt_thread_t wrapped;
+ ldap_debug_usage_info_t usage;
+ int detached;
+ int idx;
+} ldap_debug_thread_t;
+
+static ldap_debug_thread_t **thread_info;
+static unsigned int thread_info_size, thread_info_used;
+static ldap_int_thread_mutex_t thread_info_mutex;
+
+#define init_thread_info() { \
+ if( threadID ) { \
+ int mutex_init_rc = ldap_int_thread_mutex_init( &thread_info_mutex ); \
+ assert( mutex_init_rc == 0 ); \
+ } \
+}
+
+#define with_thread_info_lock(statements) { \
+ int rc_wtl_ = ldap_int_thread_mutex_lock( &thread_info_mutex ); \
+ assert( rc_wtl_ == 0 ); \
+ { statements; } \
+ rc_wtl_ = ldap_int_thread_mutex_unlock( &thread_info_mutex ); \
+ assert( rc_wtl_ == 0 ); \
+}
+
+#define thread_info_detached(t) ((t)->detached)
+
+static void
+add_thread_info(
+ const char *msg,
+ const ldap_pvt_thread_t *thread,
+ int detached )
+{
+ ldap_debug_thread_t *t;
+
+ if( thread_info_used >= thread_info_size ) {
+ unsigned int more = thread_info_size + 8;
+ unsigned int new_size = thread_info_size + more;
+
+ t = calloc( more, sizeof(ldap_debug_thread_t) );
+ assert( t != NULL );
+ thread_info = realloc( thread_info, new_size * sizeof(*thread_info) );
+ assert( thread_info != NULL );
+ do {
+ t->idx = thread_info_size;
+ thread_info[thread_info_size++] = t++;
+ } while( thread_info_size < new_size );
+ }
+
+ t = thread_info[thread_info_used];
+ init_usage( &t->usage, msg );
+ t->wrapped = *thread;
+ t->detached = detached;
+ thread_info_used++;
+}
+
+static void
+remove_thread_info( ldap_debug_thread_t *t, const char *msg )
+{
+ ldap_debug_thread_t *last;
+ int idx;
+ check_usage( &t->usage, msg );
+ destroy_usage( &t->usage );
+ idx = t->idx;
+ assert( thread_info[idx] == t );
+ last = thread_info[--thread_info_used];
+ assert( last->idx == thread_info_used );
+ (thread_info[idx] = last)->idx = idx;
+ (thread_info[thread_info_used] = t )->idx = thread_info_used;
+}
+
+static ldap_debug_thread_t *
+get_thread_info( ldap_pvt_thread_t thread, const char *msg )
+{
+ unsigned int i;
+ ldap_debug_thread_t *t;
+ for( i = 0; i < thread_info_used; i++ ) {
+ if( ldap_pvt_thread_equal( thread, thread_info[i]->wrapped ) )
+ break;
+ }
+ ERROR_IF( i == thread_info_used, msg );
+ t = thread_info[i];
+ check_usage( &t->usage, msg );
+ return t;
+}
+
+#endif /* LDAP_THREAD_DEBUG_THREAD_ID */
+
+
+static char *
+thread_name( char *buf, int bufsize, ldap_pvt_thread_t thread )
+{
+ int i;
+ --bufsize;
+ if( bufsize > 2*sizeof(thread) )
+ bufsize = 2*sizeof(thread);
+ for( i = 0; i < bufsize; i += 2 )
+ snprintf( buf+i, 3, "%02x", ((unsigned char *)&thread)[i/2] );
+ return buf;
+}
+
+
+/* Add <adjust> (+/-1) to resource count <which> unless "nocount". */
+static void
+adjust_count( int which, int adjust )
+{
+ int rc;
+ switch( count ) {
+ case Count_no:
+ break;
+ case Count_yes:
+ rc = ldap_int_thread_mutex_lock( &resource_mutexes[which] );
+ assert( rc == 0 );
+ resource_counts[which] += adjust;
+ rc = ldap_int_thread_mutex_unlock( &resource_mutexes[which] );
+ assert( rc == 0 );
+ break;
+ case Count_reported:
+ fputs( "== thr_debug: More thread activity after exit ==\n", stderr );
+ count = Count_reported_more;
+ /* FALL THROUGH */
+ case Count_reported_more:
+ /* Not used, but result might be inspected with debugger */
+ /* (Hopefully threading is disabled by now...) */
+ resource_counts[which] += adjust;
+ break;
+ }
+}
+
+
+/* Wrappers for LDAP_THREAD_IMPLEMENTATION: */
+
+/* Used instead of ldap_int_thread_initialize by ldap_pvt_thread_initialize */
+int
+ldap_debug_thread_initialize( void )
+{
+ int i, rc, rc2;
+ if( !options_done )
+ get_options();
+ ERROR_IF( threading_enabled, "ldap_debug_thread_initialize" );
+ threading_enabled = 1;
+ rc = ldap_int_thread_initialize();
+ if( rc ) {
+ ERROR( rc, "ldap_debug_thread_initialize:threads" );
+ threading_enabled = 0;
+ } else {
+ init_thread_info();
+ if( count != Count_no ) {
+ for( i = rc2 = 0; i < Idx_max; i++ )
+ rc2 |= ldap_int_thread_mutex_init( &resource_mutexes[i] );
+ assert( rc2 == 0 );
+ /* FIXME: Only for static libldap as in init.c? If so, why? */
+ atexit( count_resource_leaks );
+ }
+ }
+ return rc;
+}
+
+/* Used instead of ldap_int_thread_destroy by ldap_pvt_thread_destroy */
+int
+ldap_debug_thread_destroy( void )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_debug_thread_destroy" );
+ /* sleep(1) -- need to wait for thread pool to finish? */
+ rc = ldap_int_thread_destroy();
+ if( rc ) {
+ ERROR( rc, "ldap_debug_thread_destroy:threads" );
+ } else {
+ threading_enabled = 0;
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_set_concurrency( int n )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_set_concurrency" );
+ rc = ldap_int_thread_set_concurrency( n );
+ ERROR_IF( rc, "ldap_pvt_thread_set_concurrency" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_get_concurrency( void )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_get_concurrency" );
+ rc = ldap_int_thread_get_concurrency();
+ ERROR_IF( rc, "ldap_pvt_thread_get_concurrency" );
+ return rc;
+}
+
+unsigned int
+ldap_pvt_thread_sleep( unsigned int interval )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_sleep" );
+ rc = ldap_int_thread_sleep( interval );
+ ERROR_IF( rc, "ldap_pvt_thread_sleep" );
+ return 0;
+}
+
+static void
+thread_exiting( const char *how, const char *msg )
+{
+ ldap_pvt_thread_t thread;
+#if 0 /* Detached threads may exit after ldap_debug_thread_destroy(). */
+ ERROR_IF( !threading_enabled, msg );
+#endif
+ thread = ldap_pvt_thread_self();
+ if( tracethreads ) {
+ char buf[40];
+ fprintf( stderr, "== thr_debug: %s thread %s ==\n",
+ how, thread_name( buf, sizeof(buf), thread ) );
+ }
+ if( threadID ) {
+ with_thread_info_lock({
+ ldap_debug_thread_t *t = get_thread_info( thread, msg );
+ if( thread_info_detached( t ) )
+ remove_thread_info( t, msg );
+ });
+ }
+ adjust_count( Idx_unexited_thread, -1 );
+}
+
+void
+ldap_pvt_thread_exit( void *retval )
+{
+ thread_exiting( "Exiting", "ldap_pvt_thread_exit" );
+ ldap_int_thread_exit( retval );
+}
+
+typedef struct {
+ void *(*start_routine)( void * );
+ void *arg;
+} ldap_debug_thread_call_t;
+
+static void *
+ldap_debug_thread_wrapper( void *arg )
+{
+ void *ret;
+ ldap_debug_thread_call_t call = *(ldap_debug_thread_call_t *)arg;
+ free( arg );
+ ret = call.start_routine( call.arg );
+ thread_exiting( "Returning from", "ldap_debug_thread_wrapper" );
+ return ret;
+}
+
+int
+ldap_pvt_thread_create(
+ ldap_pvt_thread_t *thread,
+ int detach,
+ void *(*start_routine)( void * ),
+ void *arg )
+{
+ int rc;
+ if( !options_done )
+ get_options();
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_create" );
+
+ if( wrap_threads ) {
+ ldap_debug_thread_call_t *call = malloc(
+ sizeof( ldap_debug_thread_call_t ) );
+ assert( call != NULL );
+ call->start_routine = start_routine;
+ call->arg = arg;
+ start_routine = ldap_debug_thread_wrapper;
+ arg = call;
+ }
+ if( threadID ) {
+ with_thread_info_lock({
+ rc = ldap_int_thread_create( thread, detach, start_routine, arg );
+ if( rc == 0 )
+ add_thread_info( "ldap_pvt_thread_create", thread, detach );
+ });
+ } else {
+ rc = ldap_int_thread_create( thread, detach, start_routine, arg );
+ }
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_create" );
+ if( wrap_threads )
+ free( arg );
+ } else {
+ if( tracethreads ) {
+ char buf[40], buf2[40];
+ fprintf( stderr,
+ "== thr_debug: Created thread %s%s from thread %s ==\n",
+ thread_name( buf, sizeof(buf), *thread ),
+ detach ? " (detached)" : "",
+ thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) );
+ }
+ adjust_count( Idx_unexited_thread, +1 );
+ if( !detach )
+ adjust_count( Idx_unjoined_thread, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
+{
+ int rc;
+ ldap_debug_thread_t *t = NULL;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_join" );
+ if( tracethreads ) {
+ char buf[40], buf2[40];
+ fprintf( stderr, "== thr_debug: Joining thread %s in thread %s ==\n",
+ thread_name( buf, sizeof(buf), thread ),
+ thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) );
+ }
+ if( threadID )
+ with_thread_info_lock( {
+ t = get_thread_info( thread, "ldap_pvt_thread_join" );
+ ERROR_IF( thread_info_detached( t ), "ldap_pvt_thread_join" );
+ } );
+ rc = ldap_int_thread_join( thread, thread_return );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_join" );
+ } else {
+ if( threadID )
+ with_thread_info_lock(
+ remove_thread_info( t, "ldap_pvt_thread_join" ) );
+ adjust_count( Idx_unjoined_thread, -1 );
+ }
+
+ return rc;
+}
+
+int
+ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_kill" );
+ if( tracethreads ) {
+ char buf[40], buf2[40];
+ fprintf( stderr,
+ "== thr_debug: Killing thread %s (sig %i) from thread %s ==\n",
+ thread_name( buf, sizeof(buf), thread ), signo,
+ thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) );
+ }
+ rc = ldap_int_thread_kill( thread, signo );
+ ERROR_IF( rc, "ldap_pvt_thread_kill" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_yield( void )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_yield" );
+ rc = ldap_int_thread_yield();
+ ERROR_IF( rc, "ldap_pvt_thread_yield" );
+ return rc;
+}
+
+ldap_pvt_thread_t
+ldap_pvt_thread_self( void )
+{
+#if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_self" );
+#endif
+ return ldap_int_thread_self();
+}
+
+int
+ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
+{
+ int rc;
+ init_usage( &cond->usage, "ldap_pvt_thread_cond_init" );
+ rc = ldap_int_thread_cond_init( WRAPPED( cond ) );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_cond_init" );
+ destroy_usage( &cond->usage );
+ } else {
+ adjust_count( Idx_cond, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cond )
+{
+ int rc;
+ check_usage( &cond->usage, "ldap_pvt_thread_cond_destroy" );
+ rc = ldap_int_thread_cond_destroy( WRAPPED( cond ) );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_cond_destroy" );
+ } else {
+ destroy_usage( &cond->usage );
+ adjust_count( Idx_cond, -1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
+{
+ int rc;
+ check_usage( &cond->usage, "ldap_pvt_thread_cond_signal" );
+ rc = ldap_int_thread_cond_signal( WRAPPED( cond ) );
+ ERROR_IF( rc, "ldap_pvt_thread_cond_signal" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond )
+{
+ int rc;
+ check_usage( &cond->usage, "ldap_pvt_thread_cond_broadcast" );
+ rc = ldap_int_thread_cond_broadcast( WRAPPED( cond ) );
+ ERROR_IF( rc, "ldap_pvt_thread_cond_broadcast" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_cond_wait(
+ ldap_pvt_thread_cond_t *cond,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ int rc;
+ ldap_int_thread_t owner;
+ check_usage( &cond->usage, "ldap_pvt_thread_cond_wait:cond" );
+ check_usage( &mutex->usage, "ldap_pvt_thread_cond_wait:mutex" );
+ adjust_count( Idx_locked_mutex, -1 );
+ owner = GET_OWNER( mutex );
+ ASSERT_OWNER( mutex, "ldap_pvt_thread_cond_wait" );
+ RESET_OWNER( mutex );
+ rc = ldap_int_thread_cond_wait( WRAPPED( cond ), WRAPPED( mutex ) );
+ ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_cond_wait" );
+ SET_OWNER( mutex, rc ? owner : ldap_int_thread_self() );
+ adjust_count( Idx_locked_mutex, +1 );
+ ERROR_IF( rc, "ldap_pvt_thread_cond_wait" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_mutex_recursive_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ int rc;
+ init_usage( &mutex->usage, "ldap_pvt_thread_mutex_recursive_init" );
+ rc = ldap_int_thread_mutex_recursive_init( WRAPPED( mutex ) );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_mutex_recursive_init" );
+ destroy_usage( &mutex->usage );
+ } else {
+ RESET_OWNER( mutex );
+ adjust_count( Idx_mutex, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ int rc;
+ init_usage( &mutex->usage, "ldap_pvt_thread_mutex_init" );
+ rc = ldap_int_thread_mutex_init( WRAPPED( mutex ) );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_mutex_init" );
+ destroy_usage( &mutex->usage );
+ } else {
+ RESET_OWNER( mutex );
+ adjust_count( Idx_mutex, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
+{
+ int rc;
+ check_usage( &mutex->usage, "ldap_pvt_thread_mutex_destroy" );
+ ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_destroy" );
+ rc = ldap_int_thread_mutex_destroy( WRAPPED( mutex ) );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_mutex_destroy" );
+ } else {
+ destroy_usage( &mutex->usage );
+ RESET_OWNER( mutex );
+ adjust_count( Idx_mutex, -1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
+{
+ int rc;
+ check_usage( &mutex->usage, "ldap_pvt_thread_mutex_lock" );
+ rc = ldap_int_thread_mutex_lock( WRAPPED( mutex ) );
+ if( rc ) {
+ ERROR_IF( rc, "ldap_pvt_thread_mutex_lock" );
+ } else {
+ ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_lock" );
+ SET_OWNER( mutex, ldap_int_thread_self() );
+ adjust_count( Idx_locked_mutex, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex )
+{
+ int rc;
+ check_usage( &mutex->usage, "ldap_pvt_thread_mutex_trylock" );
+ rc = ldap_int_thread_mutex_trylock( WRAPPED( mutex ) );
+ if( rc == 0 ) {
+ ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_trylock" );
+ SET_OWNER( mutex, ldap_int_thread_self() );
+ adjust_count( Idx_locked_mutex, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
+{
+ int rc;
+ check_usage( &mutex->usage, "ldap_pvt_thread_mutex_unlock" );
+ ASSERT_OWNER( mutex, "ldap_pvt_thread_mutex_unlock" );
+ RESET_OWNER( mutex ); /* Breaks if this thread did not own the mutex */
+ rc = ldap_int_thread_mutex_unlock( WRAPPED( mutex ) );
+ if( rc ) {
+ ERROR_IF( rc, "ldap_pvt_thread_mutex_unlock" );
+ } else {
+ adjust_count( Idx_locked_mutex, -1 );
+ }
+ return rc;
+}
+
+
+/* Wrappers for LDAP_THREAD_RDWR_IMPLEMENTATION: */
+
+int
+ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ int rc;
+ init_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_init" );
+ rc = ldap_int_thread_rdwr_init( WRAPPED( rwlock ) );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_rdwr_init" );
+ destroy_usage( &rwlock->usage );
+ } else {
+ adjust_count( Idx_rdwr, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ int rc;
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_destroy" );
+ rc = ldap_int_thread_rdwr_destroy( WRAPPED( rwlock ) );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_rdwr_destroy" );
+ } else {
+ destroy_usage( &rwlock->usage );
+ adjust_count( Idx_rdwr, -1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ int rc;
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_rlock" );
+ rc = ldap_int_thread_rdwr_rlock( WRAPPED( rwlock ) );
+ ERROR_IF( rc, "ldap_pvt_thread_rdwr_rlock" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_rtrylock" );
+ return ldap_int_thread_rdwr_rtrylock( WRAPPED( rwlock ) );
+}
+
+int
+ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ int rc;
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_runlock" );
+ rc = ldap_int_thread_rdwr_runlock( WRAPPED( rwlock ) );
+ ERROR_IF( rc, "ldap_pvt_thread_rdwr_runlock" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ int rc;
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wlock" );
+ rc = ldap_int_thread_rdwr_wlock( WRAPPED( rwlock ) );
+ ERROR_IF( rc, "ldap_pvt_thread_rdwr_wlock" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wtrylock" );
+ return ldap_int_thread_rdwr_wtrylock( WRAPPED( rwlock ) );
+}
+
+int
+ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ int rc;
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wunlock" );
+ rc = ldap_int_thread_rdwr_wunlock( WRAPPED( rwlock ) );
+ ERROR_IF( rc, "ldap_pvt_thread_rdwr_wunlock" );
+ return rc;
+}
+
+#if defined(LDAP_RDWR_DEBUG) && !defined(LDAP_THREAD_HAVE_RDWR)
+
+int
+ldap_pvt_thread_rdwr_readers( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_readers" );
+ return ldap_int_thread_rdwr_readers( WRAPPED( rwlock ) );
+}
+
+int
+ldap_pvt_thread_rdwr_writers( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_writers" );
+ return ldap_int_thread_rdwr_writers( WRAPPED( rwlock ) );
+}
+
+int
+ldap_pvt_thread_rdwr_active( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_active" );
+ return ldap_int_thread_rdwr_active( WRAPPED( rwlock ) );
+}
+
+#endif /* LDAP_RDWR_DEBUG && !LDAP_THREAD_HAVE_RDWR */
+
+
+/* Some wrappers for LDAP_THREAD_POOL_IMPLEMENTATION: */
+#ifdef LDAP_THREAD_POOL_IMPLEMENTATION
+
+int
+ldap_pvt_thread_pool_init(
+ ldap_pvt_thread_pool_t *tpool,
+ int max_threads,
+ int max_pending )
+{
+ int rc;
+ if( !options_done )
+ get_options();
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_init" );
+ rc = ldap_int_thread_pool_init( tpool, max_threads, max_pending );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_pool_init" );
+ } else {
+ adjust_count( Idx_tpool, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_pool_submit(
+ ldap_pvt_thread_pool_t *tpool,
+ ldap_pvt_thread_start_t *start_routine, void *arg )
+{
+ int rc, has_pool;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_submit" );
+ has_pool = (tpool && *tpool);
+ rc = ldap_int_thread_pool_submit( tpool, start_routine, arg );
+ if( has_pool )
+ ERROR_IF( rc, "ldap_pvt_thread_pool_submit" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_pool_maxthreads(
+ ldap_pvt_thread_pool_t *tpool,
+ int max_threads )
+{
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_maxthreads" );
+ return ldap_int_thread_pool_maxthreads( tpool, max_threads );
+}
+
+int
+ldap_pvt_thread_pool_backload( ldap_pvt_thread_pool_t *tpool )
+{
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_backload" );
+ return ldap_int_thread_pool_backload( tpool );
+}
+
+int
+ldap_pvt_thread_pool_destroy( ldap_pvt_thread_pool_t *tpool, int run_pending )
+{
+ int rc, has_pool;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_destroy" );
+ has_pool = (tpool && *tpool);
+ rc = ldap_int_thread_pool_destroy( tpool, run_pending );
+ if( has_pool ) {
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_pool_destroy" );
+ } else {
+ adjust_count( Idx_tpool, -1 );
+ }
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_pool_close( ldap_pvt_thread_pool_t *tpool, int run_pending )
+{
+ int rc, has_pool;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_close" );
+ has_pool = (tpool && *tpool);
+ rc = ldap_int_thread_pool_close( tpool, run_pending );
+ if( has_pool && rc ) {
+ ERROR( rc, "ldap_pvt_thread_pool_close" );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_pool_free( ldap_pvt_thread_pool_t *tpool )
+{
+ int rc, has_pool;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_free" );
+ has_pool = (tpool && *tpool);
+ rc = ldap_int_thread_pool_free( tpool );
+ if( has_pool ) {
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_pool_free" );
+ } else {
+ adjust_count( Idx_tpool, -1 );
+ }
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_pool_pause( ldap_pvt_thread_pool_t *tpool )
+{
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_pause" );
+ return ldap_int_thread_pool_pause( tpool );
+}
+
+int
+ldap_pvt_thread_pool_resume( ldap_pvt_thread_pool_t *tpool )
+{
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_resume" );
+ return ldap_int_thread_pool_resume( tpool );
+}
+
+int
+ldap_pvt_thread_pool_getkey(
+ void *xctx,
+ void *key,
+ void **data,
+ ldap_pvt_thread_pool_keyfree_t **kfree )
+{
+#if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_getkey" );
+#endif
+ return ldap_int_thread_pool_getkey( xctx, key, data, kfree );
+}
+
+int
+ldap_pvt_thread_pool_setkey(
+ void *xctx,
+ void *key,
+ void *data,
+ ldap_pvt_thread_pool_keyfree_t *kfree,
+ void **olddatap,
+ ldap_pvt_thread_pool_keyfree_t **oldkfreep )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_setkey" );
+ rc = ldap_int_thread_pool_setkey(
+ xctx, key, data, kfree, olddatap, oldkfreep );
+ ERROR_IF( rc, "ldap_pvt_thread_pool_setkey" );
+ return rc;
+}
+
+void
+ldap_pvt_thread_pool_purgekey( void *key )
+{
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_purgekey" );
+ ldap_int_thread_pool_purgekey( key );
+}
+
+void *
+ldap_pvt_thread_pool_context( void )
+{
+#if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_context" );
+#endif
+ return ldap_int_thread_pool_context();
+}
+
+void
+ldap_pvt_thread_pool_context_reset( void *vctx )
+{
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_context_reset" );
+ ldap_int_thread_pool_context_reset( vctx );
+}
+
+#endif /* LDAP_THREAD_POOL_IMPLEMENTATION */
+
+#endif /* LDAP_THREAD_DEBUG */
diff --git a/libraries/libldap/thr_nt.c b/libraries/libldap/thr_nt.c
new file mode 100644
index 0000000..0bb1dea
--- /dev/null
+++ b/libraries/libldap/thr_nt.c
@@ -0,0 +1,252 @@
+/* thr_nt.c - wrapper around NT threads */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#if defined( HAVE_NT_THREADS )
+
+#define _WIN32_WINNT 0x0400
+#include <windows.h>
+#include <process.h>
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+
+typedef struct ldap_int_thread_s {
+ long tid;
+ HANDLE thd;
+} ldap_int_thread_s;
+
+#ifndef NT_MAX_THREADS
+#define NT_MAX_THREADS 1024
+#endif
+
+static ldap_int_thread_s tids[NT_MAX_THREADS];
+static int ntids;
+
+
+/* mingw compiler very sensitive about getting prototypes right */
+typedef unsigned __stdcall thrfunc_t(void *);
+
+int
+ldap_int_thread_initialize( void )
+{
+ return 0;
+}
+
+int
+ldap_int_thread_destroy( void )
+{
+ return 0;
+}
+
+int
+ldap_int_mutex_firstcreate( ldap_int_thread_mutex_t *mutex )
+{
+ if ( *mutex == NULL ) {
+ HANDLE p = CreateMutex( NULL, 0, NULL );
+ if ( InterlockedCompareExchangePointer((PVOID*)mutex, (PVOID)p, NULL) != NULL)
+ CloseHandle( p );
+ }
+ return 0;
+}
+
+int
+ldap_pvt_thread_create( ldap_pvt_thread_t * thread,
+ int detach,
+ void *(*start_routine)( void *),
+ void *arg)
+{
+ unsigned tid;
+ HANDLE thd;
+ int rc = -1;
+
+ thd = (HANDLE) _beginthreadex(NULL, LDAP_PVT_THREAD_STACK_SIZE, (thrfunc_t *) start_routine,
+ arg, 0, &tid);
+
+ if ( thd ) {
+ *thread = (ldap_pvt_thread_t) tid;
+ tids[ntids].tid = tid;
+ tids[ntids].thd = thd;
+ ntids++;
+ rc = 0;
+ }
+ return rc;
+}
+
+void
+ldap_pvt_thread_exit( void *retval )
+{
+ _endthread( );
+}
+
+int
+ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
+{
+ DWORD status;
+ int i;
+
+ for (i=0; i<ntids; i++) {
+ if ( tids[i].tid == thread )
+ break;
+ }
+ if ( i > ntids ) return -1;
+
+ status = WaitForSingleObject( tids[i].thd, INFINITE );
+ for (; i<ntids; i++) {
+ tids[i] = tids[i+1];
+ }
+ ntids--;
+ return status == WAIT_FAILED ? -1 : 0;
+}
+
+int
+ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_yield( void )
+{
+ Sleep( 0 );
+ return 0;
+}
+
+int
+ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
+{
+ *cond = CreateEvent( NULL, FALSE, FALSE, NULL );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cv )
+{
+ CloseHandle( *cv );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
+{
+ SetEvent( *cond );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ SignalObjectAndWait( *mutex, *cond, INFINITE, FALSE );
+ WaitForSingleObject( *mutex, INFINITE );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond )
+{
+ while ( WaitForSingleObject( *cond, 0 ) == WAIT_TIMEOUT )
+ SetEvent( *cond );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ *mutex = CreateMutex( NULL, 0, NULL );
+ return ( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_recursive_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ /* All NT mutexes are recursive */
+ return ldap_pvt_thread_mutex_init( mutex );
+}
+
+int
+ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
+{
+ CloseHandle( *mutex );
+ return ( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
+{
+ DWORD status;
+ status = WaitForSingleObject( *mutex, INFINITE );
+ return status == WAIT_FAILED ? -1 : 0;
+}
+
+int
+ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
+{
+ ReleaseMutex( *mutex );
+ return ( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mp )
+{
+ DWORD status;
+ status = WaitForSingleObject( *mp, 0 );
+ return status == WAIT_FAILED || status == WAIT_TIMEOUT
+ ? -1 : 0;
+}
+
+ldap_pvt_thread_t
+ldap_pvt_thread_self( void )
+{
+ return GetCurrentThreadId();
+}
+
+int
+ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *keyp )
+{
+ DWORD key = TlsAlloc();
+ if ( key != TLS_OUT_OF_INDEXES ) {
+ *keyp = key;
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+int
+ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key )
+{
+ /* TlsFree returns 0 on failure */
+ return( TlsFree( key ) == 0 );
+}
+
+int
+ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data )
+{
+ return ( TlsSetValue( key, data ) == 0 );
+}
+
+int
+ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data )
+{
+ void *ptr = TlsGetValue( key );
+ *data = ptr;
+ return( ptr ? GetLastError() : 0 );
+}
+
+#endif
diff --git a/libraries/libldap/thr_posix.c b/libraries/libldap/thr_posix.c
new file mode 100644
index 0000000..004dc0b
--- /dev/null
+++ b/libraries/libldap/thr_posix.c
@@ -0,0 +1,411 @@
+/* thr_posix.c - wrapper around posix and posixish thread implementations. */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+
+#include "portable.h"
+
+#if defined( HAVE_PTHREADS )
+
+#ifdef __GLIBC__
+#undef _FEATURES_H
+#define _XOPEN_SOURCE 500 /* For pthread_setconcurrency() on glibc */
+#endif
+
+#include <ac/errno.h>
+
+#ifdef REPLACE_BROKEN_YIELD
+#ifndef HAVE_NANOSLEEP
+#include <ac/socket.h>
+#endif
+#include <ac/time.h>
+#endif
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_IMPLEMENTATION
+#define LDAP_THREAD_RDWR_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+#include <signal.h> /* For pthread_kill() */
+
+extern int ldap_int_stackguard;
+
+#if HAVE_PTHREADS < 6
+# define LDAP_INT_THREAD_ATTR_DEFAULT pthread_attr_default
+# define LDAP_INT_THREAD_CONDATTR_DEFAULT pthread_condattr_default
+# define LDAP_INT_THREAD_MUTEXATTR_DEFAULT pthread_mutexattr_default
+#else
+# define LDAP_INT_THREAD_ATTR_DEFAULT NULL
+# define LDAP_INT_THREAD_CONDATTR_DEFAULT NULL
+# define LDAP_INT_THREAD_MUTEXATTR_DEFAULT NULL
+#endif
+
+#ifdef LDAP_THREAD_DEBUG
+# if defined LDAP_INT_THREAD_MUTEXATTR /* May be defined in CPPFLAGS */
+# elif defined HAVE_PTHREAD_KILL_OTHER_THREADS_NP
+ /* LinuxThreads hack */
+# define LDAP_INT_THREAD_MUTEXATTR PTHREAD_MUTEX_ERRORCHECK_NP
+# else
+# define LDAP_INT_THREAD_MUTEXATTR PTHREAD_MUTEX_ERRORCHECK
+# endif
+static pthread_mutexattr_t mutex_attr;
+# undef LDAP_INT_THREAD_MUTEXATTR_DEFAULT
+# define LDAP_INT_THREAD_MUTEXATTR_DEFAULT &mutex_attr
+#endif
+
+static pthread_mutexattr_t mutex_attr_recursive;
+
+#if HAVE_PTHREADS < 7
+#define ERRVAL(val) ((val) < 0 ? errno : 0)
+#else
+#define ERRVAL(val) (val)
+#endif
+
+int
+ldap_int_thread_initialize( void )
+{
+#ifdef LDAP_INT_THREAD_MUTEXATTR
+ pthread_mutexattr_init( &mutex_attr );
+ pthread_mutexattr_settype( &mutex_attr, LDAP_INT_THREAD_MUTEXATTR );
+#endif
+ if (pthread_mutexattr_init(&mutex_attr_recursive))
+ return -1;
+ if (pthread_mutexattr_settype(&mutex_attr_recursive, PTHREAD_MUTEX_RECURSIVE))
+ return -1;
+ return 0;
+}
+
+int
+ldap_int_thread_destroy( void )
+{
+#ifdef HAVE_PTHREAD_KILL_OTHER_THREADS_NP
+ /* LinuxThreads: kill clones */
+ pthread_kill_other_threads_np();
+#endif
+#ifdef LDAP_INT_THREAD_MUTEXATTR
+ pthread_mutexattr_destroy( &mutex_attr );
+#endif
+ pthread_mutexattr_destroy( &mutex_attr_recursive );
+ return 0;
+}
+
+#ifdef LDAP_THREAD_HAVE_SETCONCURRENCY
+int
+ldap_pvt_thread_set_concurrency(int n)
+{
+#ifdef HAVE_PTHREAD_SETCONCURRENCY
+ return pthread_setconcurrency( n );
+#elif defined(HAVE_THR_SETCONCURRENCY)
+ return thr_setconcurrency( n );
+#else
+ return 0;
+#endif
+}
+#endif
+
+#ifdef LDAP_THREAD_HAVE_GETCONCURRENCY
+int
+ldap_pvt_thread_get_concurrency(void)
+{
+#ifdef HAVE_PTHREAD_GETCONCURRENCY
+ return pthread_getconcurrency();
+#elif defined(HAVE_THR_GETCONCURRENCY)
+ return thr_getconcurrency();
+#else
+ return 0;
+#endif
+}
+#endif
+
+/* detachstate appeared in Draft 6, but without manifest constants.
+ * in Draft 7 they were called PTHREAD_CREATE_UNDETACHED and ...DETACHED.
+ * in Draft 8 on, ...UNDETACHED became ...JOINABLE.
+ */
+#ifndef PTHREAD_CREATE_JOINABLE
+#ifdef PTHREAD_CREATE_UNDETACHED
+#define PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED
+#else
+#define PTHREAD_CREATE_JOINABLE 0
+#endif
+#endif
+
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+
+int
+ldap_pvt_thread_create( ldap_pvt_thread_t * thread,
+ int detach,
+ void *(*start_routine)( void * ),
+ void *arg)
+{
+ int rtn;
+ pthread_attr_t attr;
+
+/* Always create the thread attrs, so we can set stacksize if we need to */
+#if HAVE_PTHREADS > 5
+ pthread_attr_init(&attr);
+#else
+ pthread_attr_create(&attr);
+#endif
+
+#ifdef LDAP_PVT_THREAD_SET_STACK_SIZE
+ /* this should be tunable */
+ pthread_attr_setstacksize( &attr, LDAP_PVT_THREAD_STACK_SIZE );
+ if ( ldap_int_stackguard )
+ pthread_attr_setguardsize( &attr, LDAP_PVT_THREAD_STACK_SIZE );
+#endif
+
+#if HAVE_PTHREADS > 5
+ detach = detach ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE;
+#if HAVE_PTHREADS == 6
+ pthread_attr_setdetachstate(&attr, &detach);
+#else
+ pthread_attr_setdetachstate(&attr, detach);
+#endif
+#endif
+
+#if HAVE_PTHREADS < 5
+ rtn = pthread_create( thread, attr, start_routine, arg );
+#else
+ rtn = pthread_create( thread, &attr, start_routine, arg );
+#endif
+
+#if HAVE_PTHREADS > 5
+ pthread_attr_destroy(&attr);
+#else
+ pthread_attr_delete(&attr);
+ if( detach ) {
+ pthread_detach( thread );
+ }
+#endif
+
+#if HAVE_PTHREADS < 7
+ if ( rtn < 0 ) rtn = errno;
+#endif
+ return rtn;
+}
+
+void
+ldap_pvt_thread_exit( void *retval )
+{
+ pthread_exit( retval );
+}
+
+int
+ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
+{
+#if HAVE_PTHREADS < 7
+ void *dummy;
+ if (thread_return==NULL)
+ thread_return=&dummy;
+#endif
+ return ERRVAL( pthread_join( thread, thread_return ) );
+}
+
+int
+ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
+{
+#if defined(HAVE_PTHREAD_KILL) && HAVE_PTHREADS > 4
+ /* MacOS 10.1 is detected as v10 but has no pthread_kill() */
+ return ERRVAL( pthread_kill( thread, signo ) );
+#else
+ /* pthread package with DCE */
+ if (kill( getpid(), signo )<0)
+ return errno;
+ return 0;
+#endif
+}
+
+int
+ldap_pvt_thread_yield( void )
+{
+#ifdef REPLACE_BROKEN_YIELD
+#ifdef HAVE_NANOSLEEP
+ struct timespec t = { 0, 0 };
+ nanosleep(&t, NULL);
+#else
+ struct timeval tv = {0,0};
+ select( 0, NULL, NULL, NULL, &tv );
+#endif
+ return 0;
+
+#elif defined(HAVE_THR_YIELD)
+ thr_yield();
+ return 0;
+
+#elif HAVE_PTHREADS == 10
+ return sched_yield();
+
+#elif defined(_POSIX_THREAD_IS_GNU_PTH)
+ sched_yield();
+ return 0;
+
+#elif HAVE_PTHREADS == 6
+ pthread_yield(NULL);
+ return 0;
+
+#else
+ pthread_yield();
+ return 0;
+#endif
+}
+
+int
+ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
+{
+ return ERRVAL( pthread_cond_init(
+ cond, LDAP_INT_THREAD_CONDATTR_DEFAULT ) );
+}
+
+int
+ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cond )
+{
+ return ERRVAL( pthread_cond_destroy( cond ) );
+}
+
+int
+ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
+{
+ return ERRVAL( pthread_cond_signal( cond ) );
+}
+
+int
+ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond )
+{
+ return ERRVAL( pthread_cond_broadcast( cond ) );
+}
+
+int
+ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ return ERRVAL( pthread_cond_wait( cond, mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ return ERRVAL( pthread_mutex_init(
+ mutex, LDAP_INT_THREAD_MUTEXATTR_DEFAULT ) );
+}
+
+int
+ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
+{
+ return ERRVAL( pthread_mutex_destroy( mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return ERRVAL( pthread_mutex_lock( mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return ERRVAL( pthread_mutex_trylock( mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return ERRVAL( pthread_mutex_unlock( mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_recursive_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ return ERRVAL( pthread_mutex_init( mutex, &mutex_attr_recursive ) );
+}
+
+ldap_pvt_thread_t ldap_pvt_thread_self( void )
+{
+ return pthread_self();
+}
+
+int
+ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *key )
+{
+ return pthread_key_create( key, NULL );
+}
+
+int
+ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key )
+{
+ return pthread_key_delete( key );
+}
+
+int
+ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data )
+{
+ return pthread_setspecific( key, data );
+}
+
+int
+ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data )
+{
+ *data = pthread_getspecific( key );
+ return 0;
+}
+
+#ifdef LDAP_THREAD_HAVE_RDWR
+#ifdef HAVE_PTHREAD_RWLOCK_DESTROY
+int
+ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_init( rw, NULL ) );
+}
+
+int
+ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_destroy( rw ) );
+}
+
+int ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_rdlock( rw ) );
+}
+
+int ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_tryrdlock( rw ) );
+}
+
+int ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_unlock( rw ) );
+}
+
+int ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_wrlock( rw ) );
+}
+
+int ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_trywrlock( rw ) );
+}
+
+int ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_unlock( rw ) );
+}
+
+#endif /* HAVE_PTHREAD_RWLOCK_DESTROY */
+#endif /* LDAP_THREAD_HAVE_RDWR */
+#endif /* HAVE_PTHREADS */
+
diff --git a/libraries/libldap/thr_pth.c b/libraries/libldap/thr_pth.c
new file mode 100644
index 0000000..894082d
--- /dev/null
+++ b/libraries/libldap/thr_pth.c
@@ -0,0 +1,238 @@
+/* thr_pth.c - wrappers around GNU Pth */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#if defined( HAVE_GNU_PTH )
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_IMPLEMENTATION
+#define LDAP_THREAD_RDWR_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+
+#include <errno.h>
+
+/*******************
+ * *
+ * GNU Pth Threads *
+ * *
+ *******************/
+
+static pth_attr_t detach_attr;
+static pth_attr_t joined_attr;
+
+int
+ldap_int_thread_initialize( void )
+{
+ if( !pth_init() ) {
+ return -1;
+ }
+ detach_attr = pth_attr_new();
+ joined_attr = pth_attr_new();
+#ifdef LDAP_PVT_THREAD_SET_STACK_SIZE
+ pth_attr_set( joined_attr, PTH_ATTR_STACK_SIZE, LDAP_PVT_THREAD_STACK_SIZE );
+ pth_attr_set( detach_attr, PTH_ATTR_STACK_SIZE, LDAP_PVT_THREAD_STACK_SIZE );
+#endif
+ return pth_attr_set( detach_attr, PTH_ATTR_JOINABLE, FALSE );
+}
+
+int
+ldap_int_thread_destroy( void )
+{
+ pth_attr_destroy(detach_attr);
+ pth_kill();
+ return 0;
+}
+
+int
+ldap_pvt_thread_create( ldap_pvt_thread_t * thread,
+ int detach,
+ void *(*start_routine)( void *),
+ void *arg)
+{
+ *thread = pth_spawn( detach ? detach_attr : joined_attr,
+ start_routine, arg );
+
+ return *thread == NULL ? errno : 0;
+}
+
+void
+ldap_pvt_thread_exit( void *retval )
+{
+ pth_exit( retval );
+}
+
+int ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
+{
+ return pth_join( thread, thread_return ) ? 0 : errno;
+}
+
+int
+ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
+{
+ return pth_raise( thread, signo ) ? 0 : errno;
+}
+
+int
+ldap_pvt_thread_yield( void )
+{
+ return pth_yield(NULL) ? 0 : errno;
+}
+
+int
+ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
+{
+ return( pth_cond_init( cond ) ? 0 : errno );
+}
+
+int
+ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
+{
+ return( pth_cond_notify( cond, 0 ) ? 0 : errno );
+}
+
+int
+ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond )
+{
+ return( pth_cond_notify( cond, 1 ) ? 0 : errno );
+}
+
+int
+ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ return( pth_cond_await( cond, mutex, NULL ) ? 0 : errno );
+}
+
+int
+ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cv )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( pth_mutex_init( mutex ) ? 0 : errno );
+}
+
+int
+ldap_pvt_thread_mutex_recursive_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ /* All pth mutexes are recursive */
+ return ldap_pvt_thread_mutex_init( mutex );
+}
+
+int
+ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( pth_mutex_acquire( mutex, 0, NULL ) ? 0 : errno );
+}
+
+int
+ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( pth_mutex_release( mutex ) ? 0 : errno );
+}
+
+int
+ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( pth_mutex_acquire( mutex, 1, NULL ) ? 0 : errno );
+}
+
+ldap_pvt_thread_t
+ldap_pvt_thread_self( void )
+{
+ return pth_self();
+}
+
+int
+ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *key )
+{
+ return pth_key_create( key, NULL );
+}
+
+int
+ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key )
+{
+ return pth_key_delete( key );
+}
+
+int
+ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data )
+{
+ return pth_key_setdata( key, data );
+}
+
+int
+ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data )
+{
+ *data = pth_key_getdata( key );
+ return 0;
+}
+
+#ifdef LDAP_THREAD_HAVE_RDWR
+int
+ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rw )
+{
+ return pth_rwlock_init( rw ) ? 0 : errno;
+}
+
+int
+ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rw )
+{
+ return 0;
+}
+
+int ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return pth_rwlock_acquire( rw, PTH_RWLOCK_RD, 0, NULL ) ? 0 : errno;
+}
+
+int ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return pth_rwlock_acquire( rw, PTH_RWLOCK_RD, 1, NULL ) ? 0 : errno;
+}
+
+int ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return pth_rwlock_release( rw ) ? 0 : errno;
+}
+
+int ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return pth_rwlock_acquire( rw, PTH_RWLOCK_RW, 0, NULL ) ? 0 : errno;
+}
+
+int ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return pth_rwlock_acquire( rw, PTH_RWLOCK_RW, 1, NULL ) ? 0 : errno;
+}
+
+int ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return pth_rwlock_release( rw ) ? 0 : errno;
+}
+
+#endif /* LDAP_THREAD_HAVE_RDWR */
+#endif /* HAVE_GNU_PTH */
diff --git a/libraries/libldap/thr_thr.c b/libraries/libldap/thr_thr.c
new file mode 100644
index 0000000..86656da
--- /dev/null
+++ b/libraries/libldap/thr_thr.c
@@ -0,0 +1,192 @@
+/* thr_thr.c - wrappers around solaris threads */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#if defined( HAVE_THR )
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+
+/*******************
+ * *
+ * Solaris Threads *
+ * *
+ *******************/
+
+int
+ldap_int_thread_initialize( void )
+{
+ return 0;
+}
+
+int
+ldap_int_thread_destroy( void )
+{
+ return 0;
+}
+
+#ifdef LDAP_THREAD_HAVE_SETCONCURRENCY
+int
+ldap_pvt_thread_set_concurrency(int n)
+{
+ return thr_setconcurrency( n );
+}
+#endif
+
+#ifdef LDAP_THREAD_HAVE_GETCONCURRENCY
+int
+ldap_pvt_thread_get_concurrency(void)
+{
+ return thr_getconcurrency();
+}
+#endif
+
+int
+ldap_pvt_thread_create( ldap_pvt_thread_t * thread,
+ int detach,
+ void *(*start_routine)( void *),
+ void *arg)
+{
+ return( thr_create( NULL, LDAP_PVT_THREAD_STACK_SIZE, start_routine,
+ arg, detach ? THR_DETACHED : 0, thread ) );
+}
+
+void
+ldap_pvt_thread_exit( void *retval )
+{
+ thr_exit( NULL );
+}
+
+int ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
+{
+ thr_join( thread, NULL, thread_return );
+ return 0;
+}
+
+int
+ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
+{
+ thr_kill( thread, signo );
+ return 0;
+}
+
+int
+ldap_pvt_thread_yield( void )
+{
+ thr_yield();
+ return 0;
+}
+
+int
+ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
+{
+ return( cond_init( cond, USYNC_THREAD, NULL ) );
+}
+
+int
+ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
+{
+ return( cond_signal( cond ) );
+}
+
+int
+ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cv )
+{
+ return( cond_broadcast( cv ) );
+}
+
+int
+ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ return( cond_wait( cond, mutex ) );
+}
+
+int
+ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cv )
+{
+ return( cond_destroy( cv ) );
+}
+
+int
+ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( mutex_init( mutex, USYNC_THREAD, NULL ) );
+}
+
+int
+ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( mutex_destroy( mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( mutex_lock( mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( mutex_unlock( mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mp )
+{
+ return( mutex_trylock( mp ) );
+}
+
+int
+ldap_pvt_thread_mutex_recursive_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( mutex_init( mutex, USYNC_THREAD | LOCK_RECURSIVE, NULL ) );
+}
+
+ldap_pvt_thread_t
+ldap_pvt_thread_self( void )
+{
+ return thr_self();
+}
+
+int
+ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *key )
+{
+ return thr_keycreate( key, NULL );
+}
+
+int
+ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key )
+{
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data )
+{
+ return thr_setspecific( key, data );
+}
+
+int
+ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data )
+{
+ return thr_getspecific( key, data );
+}
+
+#endif /* HAVE_THR */
diff --git a/libraries/libldap/threads.c b/libraries/libldap/threads.c
new file mode 100644
index 0000000..eb5b96e
--- /dev/null
+++ b/libraries/libldap/threads.c
@@ -0,0 +1,111 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdarg.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include "ldap-int.h"
+
+#ifdef LDAP_R_COMPILE
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#include "ldap_thr_debug.h" /* May redirect thread initialize/destroy calls */
+
+
+/*
+ * Common LDAP thread routines
+ * see thr_*.c for implementation specific routines
+ * see rdwr.c for generic reader/writer lock implementation
+ * see tpool.c for generic thread pool implementation
+ */
+
+
+int ldap_pvt_thread_initialize( void )
+{
+ int rc;
+ static int init = 0;
+ ldap_pvt_thread_t tid;
+
+ /* we only get one shot at this */
+ if( init++ ) return -1;
+
+ rc = ldap_int_thread_initialize();
+ if( rc ) return rc;
+
+#ifndef LDAP_THREAD_HAVE_TPOOL
+ rc = ldap_int_thread_pool_startup();
+ if( rc ) return rc;
+#endif
+
+ /* kludge to pull symbol definitions in */
+ tid = ldap_pvt_thread_self();
+ return 0;
+}
+
+int ldap_pvt_thread_destroy( void )
+{
+#ifndef LDAP_THREAD_HAVE_TPOOL
+ (void) ldap_int_thread_pool_shutdown();
+#endif
+ return ldap_int_thread_destroy();
+}
+
+
+/*
+ * Default implementations of some LDAP thread routines
+ */
+
+#define LDAP_THREAD_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+
+
+#ifndef LDAP_THREAD_HAVE_GETCONCURRENCY
+int
+ldap_pvt_thread_get_concurrency ( void )
+{
+ return 1;
+}
+#endif
+
+#ifndef LDAP_THREAD_HAVE_SETCONCURRENCY
+int
+ldap_pvt_thread_set_concurrency ( int concurrency )
+{
+ return 1;
+}
+#endif
+
+#ifndef LDAP_THREAD_HAVE_SLEEP
+/*
+ * Here we assume we have fully preemptive threads and that sleep()
+ * does the right thing.
+ */
+unsigned int
+ldap_pvt_thread_sleep(
+ unsigned int interval
+)
+{
+ sleep( interval );
+ return 0;
+}
+#endif
+
+#endif /* LDAP_R_COMPILE */
diff --git a/libraries/libldap/tls2.c b/libraries/libldap/tls2.c
new file mode 100644
index 0000000..4a5d42b
--- /dev/null
+++ b/libraries/libldap/tls2.c
@@ -0,0 +1,1675 @@
+/* tls.c - Handle tls/ssl. */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS: restructured by Howard Chu.
+ */
+
+#include "portable.h"
+#include "ldap_config.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+#include <ac/param.h>
+#include <ac/dirent.h>
+
+#include "ldap-int.h"
+
+#ifdef HAVE_TLS
+
+#include "ldap-tls.h"
+
+static tls_impl *tls_imp = &ldap_int_tls_impl;
+#define HAS_TLS( sb ) ber_sockbuf_ctrl( sb, LBER_SB_OPT_HAS_IO, \
+ (void *)tls_imp->ti_sbio )
+
+#endif /* HAVE_TLS */
+
+/* RFC2459 minimum required set of supported attribute types
+ * in a certificate DN
+ */
+typedef struct oid_name {
+ struct berval oid;
+ struct berval name;
+} oid_name;
+
+static oid_name oids[] = {
+ { BER_BVC("2.5.4.3"), BER_BVC("cn") },
+ { BER_BVC("2.5.4.4"), BER_BVC("sn") },
+ { BER_BVC("2.5.4.6"), BER_BVC("c") },
+ { BER_BVC("2.5.4.7"), BER_BVC("l") },
+ { BER_BVC("2.5.4.8"), BER_BVC("st") },
+ { BER_BVC("2.5.4.10"), BER_BVC("o") },
+ { BER_BVC("2.5.4.11"), BER_BVC("ou") },
+ { BER_BVC("2.5.4.12"), BER_BVC("title") },
+ { BER_BVC("2.5.4.41"), BER_BVC("name") },
+ { BER_BVC("2.5.4.42"), BER_BVC("givenName") },
+ { BER_BVC("2.5.4.43"), BER_BVC("initials") },
+ { BER_BVC("2.5.4.44"), BER_BVC("generationQualifier") },
+ { BER_BVC("2.5.4.46"), BER_BVC("dnQualifier") },
+ { BER_BVC("1.2.840.113549.1.9.1"), BER_BVC("email") },
+ { BER_BVC("0.9.2342.19200300.100.1.25"), BER_BVC("dc") },
+ { BER_BVNULL, BER_BVNULL }
+};
+
+#ifdef HAVE_TLS
+
+LDAP_F(int) ldap_pvt_tls_check_hostname LDAP_P(( LDAP *ld, void *s, const char *name_in ));
+LDAP_F(int) ldap_pvt_tls_get_peercert LDAP_P(( void *s, struct berval *der ));
+
+void
+ldap_pvt_tls_ctx_free ( void *c )
+{
+ if ( !c ) return;
+ tls_imp->ti_ctx_free( c );
+}
+
+static void
+tls_ctx_ref( tls_ctx *ctx )
+{
+ if ( !ctx ) return;
+
+ tls_imp->ti_ctx_ref( ctx );
+}
+
+#ifdef LDAP_R_COMPILE
+/*
+ * an extra mutex for the default ctx.
+ */
+static ldap_pvt_thread_mutex_t tls_def_ctx_mutex;
+#endif
+
+void
+ldap_int_tls_destroy( struct ldapoptions *lo )
+{
+ if ( lo->ldo_tls_ctx ) {
+ ldap_pvt_tls_ctx_free( lo->ldo_tls_ctx );
+ lo->ldo_tls_ctx = NULL;
+ }
+
+ if ( lo->ldo_tls_certfile ) {
+ LDAP_FREE( lo->ldo_tls_certfile );
+ lo->ldo_tls_certfile = NULL;
+ }
+ if ( lo->ldo_tls_keyfile ) {
+ LDAP_FREE( lo->ldo_tls_keyfile );
+ lo->ldo_tls_keyfile = NULL;
+ }
+ if ( lo->ldo_tls_dhfile ) {
+ LDAP_FREE( lo->ldo_tls_dhfile );
+ lo->ldo_tls_dhfile = NULL;
+ }
+ if ( lo->ldo_tls_ecname ) {
+ LDAP_FREE( lo->ldo_tls_ecname );
+ lo->ldo_tls_ecname = NULL;
+ }
+ if ( lo->ldo_tls_cacertfile ) {
+ LDAP_FREE( lo->ldo_tls_cacertfile );
+ lo->ldo_tls_cacertfile = NULL;
+ }
+ if ( lo->ldo_tls_cacertdir ) {
+ LDAP_FREE( lo->ldo_tls_cacertdir );
+ lo->ldo_tls_cacertdir = NULL;
+ }
+ if ( lo->ldo_tls_ciphersuite ) {
+ LDAP_FREE( lo->ldo_tls_ciphersuite );
+ lo->ldo_tls_ciphersuite = NULL;
+ }
+ if ( lo->ldo_tls_crlfile ) {
+ LDAP_FREE( lo->ldo_tls_crlfile );
+ lo->ldo_tls_crlfile = NULL;
+ }
+ /* tls_pin_hashalg and tls_pin share the same buffer */
+ if ( lo->ldo_tls_pin_hashalg ) {
+ LDAP_FREE( lo->ldo_tls_pin_hashalg );
+ lo->ldo_tls_pin_hashalg = NULL;
+ } else {
+ LDAP_FREE( lo->ldo_tls_pin.bv_val );
+ }
+ BER_BVZERO( &lo->ldo_tls_pin );
+}
+
+/*
+ * Tear down the TLS subsystem. Should only be called once.
+ */
+void
+ldap_pvt_tls_destroy( void )
+{
+ struct ldapoptions *lo = LDAP_INT_GLOBAL_OPT();
+
+ ldap_int_tls_destroy( lo );
+
+ tls_imp->ti_tls_destroy();
+}
+
+/*
+ * Initialize a particular TLS implementation.
+ * Called once per implementation.
+ */
+static int
+tls_init(tls_impl *impl, int do_threads )
+{
+ static int tls_initialized = 0;
+
+ if ( !tls_initialized++ ) {
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_init( &tls_def_ctx_mutex );
+#endif
+ }
+
+ if ( impl->ti_inited++ ) return 0;
+
+ if ( do_threads ) {
+#ifdef LDAP_R_COMPILE
+ impl->ti_thr_init();
+#endif
+ }
+
+ return impl->ti_tls_init();
+}
+
+/*
+ * Initialize TLS subsystem. Called once per implementation.
+ */
+int
+ldap_pvt_tls_init( int do_threads )
+{
+ return tls_init( tls_imp, do_threads );
+}
+
+/*
+ * initialize a new TLS context
+ */
+static int
+ldap_int_tls_init_ctx( struct ldapoptions *lo, int is_server )
+{
+ int rc = 0;
+ tls_impl *ti = tls_imp;
+ struct ldaptls lts = lo->ldo_tls_info;
+
+ if ( lo->ldo_tls_ctx )
+ return 0;
+
+ tls_init( ti, 0 );
+
+ if ( is_server && !lts.lt_certfile && !lts.lt_keyfile &&
+ !lts.lt_cacertfile && !lts.lt_cacertdir &&
+ !lts.lt_cacert.bv_val && !lts.lt_cert.bv_val &&
+ !lts.lt_key.bv_val ) {
+ /* minimum configuration not provided */
+ return LDAP_NOT_SUPPORTED;
+ }
+
+#ifdef HAVE_EBCDIC
+ /* This ASCII/EBCDIC handling is a real pain! */
+ if ( lts.lt_ciphersuite ) {
+ lts.lt_ciphersuite = LDAP_STRDUP( lts.lt_ciphersuite );
+ __atoe( lts.lt_ciphersuite );
+ }
+ if ( lts.lt_cacertfile ) {
+ lts.lt_cacertfile = LDAP_STRDUP( lts.lt_cacertfile );
+ __atoe( lts.lt_cacertfile );
+ }
+ if ( lts.lt_certfile ) {
+ lts.lt_certfile = LDAP_STRDUP( lts.lt_certfile );
+ __atoe( lts.lt_certfile );
+ }
+ if ( lts.lt_keyfile ) {
+ lts.lt_keyfile = LDAP_STRDUP( lts.lt_keyfile );
+ __atoe( lts.lt_keyfile );
+ }
+ if ( lts.lt_crlfile ) {
+ lts.lt_crlfile = LDAP_STRDUP( lts.lt_crlfile );
+ __atoe( lts.lt_crlfile );
+ }
+ if ( lts.lt_cacertdir ) {
+ lts.lt_cacertdir = LDAP_STRDUP( lts.lt_cacertdir );
+ __atoe( lts.lt_cacertdir );
+ }
+ if ( lts.lt_dhfile ) {
+ lts.lt_dhfile = LDAP_STRDUP( lts.lt_dhfile );
+ __atoe( lts.lt_dhfile );
+ }
+ if ( lts.lt_ecname ) {
+ lts.lt_ecname = LDAP_STRDUP( lts.lt_ecname );
+ __atoe( lts.lt_ecname );
+ }
+#endif
+ lo->ldo_tls_ctx = ti->ti_ctx_new( lo );
+ if ( lo->ldo_tls_ctx == NULL ) {
+ Debug0( LDAP_DEBUG_ANY,
+ "TLS: could not allocate default ctx.\n" );
+ rc = -1;
+ goto error_exit;
+ }
+
+ rc = ti->ti_ctx_init( lo, &lts, is_server );
+
+error_exit:
+ if ( rc < 0 && lo->ldo_tls_ctx != NULL ) {
+ ldap_pvt_tls_ctx_free( lo->ldo_tls_ctx );
+ lo->ldo_tls_ctx = NULL;
+ }
+#ifdef HAVE_EBCDIC
+ LDAP_FREE( lts.lt_ciphersuite );
+ LDAP_FREE( lts.lt_cacertfile );
+ LDAP_FREE( lts.lt_certfile );
+ LDAP_FREE( lts.lt_keyfile );
+ LDAP_FREE( lts.lt_crlfile );
+ LDAP_FREE( lts.lt_cacertdir );
+ LDAP_FREE( lts.lt_dhfile );
+ LDAP_FREE( lts.lt_ecname );
+#endif
+ return rc;
+}
+
+/*
+ * initialize the default context
+ */
+int
+ldap_pvt_tls_init_def_ctx( int is_server )
+{
+ struct ldapoptions *lo = LDAP_INT_GLOBAL_OPT();
+ int rc;
+ LDAP_MUTEX_LOCK( &tls_def_ctx_mutex );
+ rc = ldap_int_tls_init_ctx( lo, is_server );
+ LDAP_MUTEX_UNLOCK( &tls_def_ctx_mutex );
+ return rc;
+}
+
+static tls_session *
+alloc_handle( void *ctx_arg, int is_server )
+{
+ tls_ctx *ctx;
+ tls_session *ssl;
+
+ if ( ctx_arg ) {
+ ctx = ctx_arg;
+ } else {
+ struct ldapoptions *lo = LDAP_INT_GLOBAL_OPT();
+ if ( ldap_pvt_tls_init_def_ctx( is_server ) < 0 ) return NULL;
+ ctx = lo->ldo_tls_ctx;
+ }
+
+ ssl = tls_imp->ti_session_new( ctx, is_server );
+ if ( ssl == NULL ) {
+ Debug0( LDAP_DEBUG_ANY,"TLS: can't create ssl handle.\n" );
+ return NULL;
+ }
+ return ssl;
+}
+
+static int
+update_flags( Sockbuf *sb, tls_session * ssl, int rc )
+{
+ sb->sb_trans_needs_read = 0;
+ sb->sb_trans_needs_write = 0;
+
+ return tls_imp->ti_session_upflags( sb, ssl, rc );
+}
+
+/*
+ * Call this to do a TLS connect on a sockbuf. ctx_arg can be
+ * a SSL_CTX * or NULL, in which case the default ctx is used.
+ *
+ * Return value:
+ *
+ * 0 - Success. Connection is ready for communication.
+ * <0 - Error. Can't create a TLS stream.
+ * >0 - Partial success.
+ * Do a select (using information from lber_pvt_sb_needs_{read,write}
+ * and call again.
+ */
+
+static int
+ldap_int_tls_connect( LDAP *ld, LDAPConn *conn, const char *host )
+{
+ Sockbuf *sb = conn->lconn_sb;
+ int err;
+ tls_session *ssl = NULL;
+ const char *sni = host;
+
+ if ( HAS_TLS( sb )) {
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_SSL, (void *)&ssl );
+ } else {
+ struct ldapoptions *lo;
+ tls_ctx *ctx;
+
+ ctx = ld->ld_options.ldo_tls_ctx;
+
+ ssl = alloc_handle( ctx, 0 );
+
+ if ( ssl == NULL ) return -1;
+
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_TRANSPORT, (void *)"tls_" );
+#endif
+ ber_sockbuf_add_io( sb, tls_imp->ti_sbio,
+ LBER_SBIOD_LEVEL_TRANSPORT, (void *)ssl );
+
+ lo = LDAP_INT_GLOBAL_OPT();
+ if( ctx == NULL ) {
+ ctx = lo->ldo_tls_ctx;
+ ld->ld_options.ldo_tls_ctx = ctx;
+ tls_ctx_ref( ctx );
+ }
+ if ( ld->ld_options.ldo_tls_connect_cb )
+ ld->ld_options.ldo_tls_connect_cb( ld, ssl, ctx,
+ ld->ld_options.ldo_tls_connect_arg );
+ if ( lo && lo->ldo_tls_connect_cb && lo->ldo_tls_connect_cb !=
+ ld->ld_options.ldo_tls_connect_cb )
+ lo->ldo_tls_connect_cb( ld, ssl, ctx, lo->ldo_tls_connect_arg );
+ }
+
+ /* pass hostname for SNI, but only if it's an actual name
+ * and not a numeric address
+ */
+ {
+ int numeric = 1;
+ unsigned char *c;
+ for ( c = (unsigned char *)sni; *c; c++ ) {
+ if ( *c == ':' ) /* IPv6 address */
+ break;
+ if ( *c == '.' )
+ continue;
+ if ( !isdigit( *c )) {
+ numeric = 0;
+ break;
+ }
+ }
+ if ( numeric )
+ sni = NULL;
+ }
+ err = tls_imp->ti_session_connect( ld, ssl, sni );
+
+#ifdef HAVE_WINSOCK
+ errno = WSAGetLastError();
+#endif
+
+ if ( err == 0 ) {
+ err = ldap_pvt_tls_check_hostname( ld, ssl, host );
+ }
+
+ if ( err < 0 )
+ {
+ char buf[256], *msg;
+ if ( update_flags( sb, ssl, err )) {
+ return 1;
+ }
+
+ msg = tls_imp->ti_session_errmsg( ssl, err, buf, sizeof(buf) );
+ if ( msg ) {
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP( msg );
+#ifdef HAVE_EBCDIC
+ if ( ld->ld_error ) __etoa(ld->ld_error);
+#endif
+ }
+
+ Debug1( LDAP_DEBUG_ANY,"TLS: can't connect: %s.\n",
+ ld->ld_error ? ld->ld_error : "" );
+
+ ber_sockbuf_remove_io( sb, tls_imp->ti_sbio,
+ LBER_SBIOD_LEVEL_TRANSPORT );
+#ifdef LDAP_DEBUG
+ ber_sockbuf_remove_io( sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_TRANSPORT );
+#endif
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+ldap_pvt_tls_connect( LDAP *ld, Sockbuf *sb, const char *host )
+{
+ LDAPConn conn = { .lconn_sb = sb };
+ return ldap_int_tls_connect( ld, &conn, host );
+}
+
+/*
+ * Call this to do a TLS accept on a sockbuf.
+ * Everything else is the same as with tls_connect.
+ */
+int
+ldap_pvt_tls_accept( Sockbuf *sb, void *ctx_arg )
+{
+ int err;
+ tls_session *ssl = NULL;
+
+ if ( HAS_TLS( sb )) {
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_SSL, (void *)&ssl );
+ } else {
+ ssl = alloc_handle( ctx_arg, 1 );
+ if ( ssl == NULL ) return -1;
+
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_TRANSPORT, (void *)"tls_" );
+#endif
+ ber_sockbuf_add_io( sb, tls_imp->ti_sbio,
+ LBER_SBIOD_LEVEL_TRANSPORT, (void *)ssl );
+ }
+
+ err = tls_imp->ti_session_accept( ssl );
+
+#ifdef HAVE_WINSOCK
+ errno = WSAGetLastError();
+#endif
+
+ if ( err < 0 )
+ {
+ if ( update_flags( sb, ssl, err )) return 1;
+
+ if ( DebugTest( LDAP_DEBUG_ANY ) ) {
+ char buf[256], *msg;
+ msg = tls_imp->ti_session_errmsg( ssl, err, buf, sizeof(buf) );
+ Debug1( LDAP_DEBUG_ANY,"TLS: can't accept: %s.\n",
+ msg ? msg : "(unknown)" );
+ }
+
+ ber_sockbuf_remove_io( sb, tls_imp->ti_sbio,
+ LBER_SBIOD_LEVEL_TRANSPORT );
+#ifdef LDAP_DEBUG
+ ber_sockbuf_remove_io( sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_TRANSPORT );
+#endif
+ return -1;
+ }
+ return 0;
+}
+
+int
+ldap_pvt_tls_inplace ( Sockbuf *sb )
+{
+ return HAS_TLS( sb ) ? 1 : 0;
+}
+
+int
+ldap_tls_inplace( LDAP *ld )
+{
+ Sockbuf *sb = NULL;
+
+ if ( ld->ld_defconn && ld->ld_defconn->lconn_sb ) {
+ sb = ld->ld_defconn->lconn_sb;
+
+ } else if ( ld->ld_sb ) {
+ sb = ld->ld_sb;
+
+ } else {
+ return 0;
+ }
+
+ return ldap_pvt_tls_inplace( sb );
+}
+
+int
+ldap_pvt_tls_get_peer_dn( void *s, struct berval *dn,
+ LDAPDN_rewrite_dummy *func, unsigned flags )
+{
+ tls_session *session = s;
+ struct berval bvdn;
+ int rc;
+
+ rc = tls_imp->ti_session_peer_dn( session, &bvdn );
+ if ( rc ) return rc;
+
+ rc = ldap_X509dn2bv( &bvdn, dn,
+ (LDAPDN_rewrite_func *)func, flags);
+ return rc;
+}
+
+int
+ldap_pvt_tls_check_hostname( LDAP *ld, void *s, const char *name_in )
+{
+ tls_session *session = s;
+
+ if (ld->ld_options.ldo_tls_require_cert != LDAP_OPT_X_TLS_NEVER &&
+ ld->ld_options.ldo_tls_require_cert != LDAP_OPT_X_TLS_ALLOW) {
+ ld->ld_errno = tls_imp->ti_session_chkhost( ld, session, name_in );
+ if (ld->ld_errno != LDAP_SUCCESS) {
+ return ld->ld_errno;
+ }
+ }
+
+ /*
+ * If instructed to do pinning, do it now
+ */
+ if ( !BER_BVISNULL( &ld->ld_options.ldo_tls_pin ) ) {
+ ld->ld_errno = tls_imp->ti_session_pinning( ld, s,
+ ld->ld_options.ldo_tls_pin_hashalg,
+ &ld->ld_options.ldo_tls_pin );
+ if (ld->ld_errno != LDAP_SUCCESS) {
+ return ld->ld_errno;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int
+ldap_pvt_tls_config( LDAP *ld, int option, const char *arg )
+{
+ int i;
+
+ switch( option ) {
+ case LDAP_OPT_X_TLS_CACERTFILE:
+ case LDAP_OPT_X_TLS_CACERTDIR:
+ case LDAP_OPT_X_TLS_CERTFILE:
+ case LDAP_OPT_X_TLS_KEYFILE:
+ case LDAP_OPT_X_TLS_RANDOM_FILE:
+ case LDAP_OPT_X_TLS_CIPHER_SUITE:
+ case LDAP_OPT_X_TLS_DHFILE:
+ case LDAP_OPT_X_TLS_PEERKEY_HASH:
+ case LDAP_OPT_X_TLS_ECNAME:
+ case LDAP_OPT_X_TLS_CRLFILE: /* GnuTLS only */
+ return ldap_pvt_tls_set_option( ld, option, (void *) arg );
+
+ case LDAP_OPT_X_TLS_REQUIRE_CERT:
+ case LDAP_OPT_X_TLS_REQUIRE_SAN:
+ case LDAP_OPT_X_TLS:
+ i = -1;
+ if ( strcasecmp( arg, "never" ) == 0 ) {
+ i = LDAP_OPT_X_TLS_NEVER ;
+
+ } else if ( strcasecmp( arg, "demand" ) == 0 ) {
+ i = LDAP_OPT_X_TLS_DEMAND ;
+
+ } else if ( strcasecmp( arg, "allow" ) == 0 ) {
+ i = LDAP_OPT_X_TLS_ALLOW ;
+
+ } else if ( strcasecmp( arg, "try" ) == 0 ) {
+ i = LDAP_OPT_X_TLS_TRY ;
+
+ } else if ( ( strcasecmp( arg, "hard" ) == 0 ) ||
+ ( strcasecmp( arg, "on" ) == 0 ) ||
+ ( strcasecmp( arg, "yes" ) == 0) ||
+ ( strcasecmp( arg, "true" ) == 0 ) )
+ {
+ i = LDAP_OPT_X_TLS_HARD ;
+ }
+
+ if (i >= 0) {
+ return ldap_pvt_tls_set_option( ld, option, &i );
+ }
+ return -1;
+ case LDAP_OPT_X_TLS_PROTOCOL_MAX:
+ case LDAP_OPT_X_TLS_PROTOCOL_MIN: {
+ char *next;
+ long l;
+ l = strtol( arg, &next, 10 );
+ if ( l < 0 || l > 0xff || next == arg ||
+ ( *next != '\0' && *next != '.' ) )
+ return -1;
+ i = l << 8;
+ if (*next == '.') {
+ arg = next + 1;
+ l = strtol( arg, &next, 10 );
+ if ( l < 0 || l > 0xff || next == arg || *next != '\0' )
+ return -1;
+ i += l;
+ }
+ return ldap_pvt_tls_set_option( ld, option, &i );
+ }
+#ifdef HAVE_OPENSSL
+ case LDAP_OPT_X_TLS_CRLCHECK: /* OpenSSL only */
+ i = -1;
+ if ( strcasecmp( arg, "none" ) == 0 ) {
+ i = LDAP_OPT_X_TLS_CRL_NONE ;
+ } else if ( strcasecmp( arg, "peer" ) == 0 ) {
+ i = LDAP_OPT_X_TLS_CRL_PEER ;
+ } else if ( strcasecmp( arg, "all" ) == 0 ) {
+ i = LDAP_OPT_X_TLS_CRL_ALL ;
+ }
+ if (i >= 0) {
+ return ldap_pvt_tls_set_option( ld, option, &i );
+ }
+ return -1;
+#endif
+ }
+ return -1;
+}
+
+int
+ldap_pvt_tls_get_option( LDAP *ld, int option, void *arg )
+{
+ struct ldapoptions *lo;
+
+ if( option == LDAP_OPT_X_TLS_PACKAGE ) {
+ *(char **)arg = LDAP_STRDUP( tls_imp->ti_name );
+ return 0;
+ }
+
+ if( ld != NULL ) {
+ assert( LDAP_VALID( ld ) );
+
+ if( !LDAP_VALID( ld ) ) {
+ return LDAP_OPT_ERROR;
+ }
+
+ lo = &ld->ld_options;
+
+ } else {
+ /* Get pointer to global option structure */
+ lo = LDAP_INT_GLOBAL_OPT();
+ if ( lo == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+ }
+
+ switch( option ) {
+ case LDAP_OPT_X_TLS:
+ *(int *)arg = lo->ldo_tls_mode;
+ break;
+ case LDAP_OPT_X_TLS_CTX:
+ *(void **)arg = lo->ldo_tls_ctx;
+ if ( lo->ldo_tls_ctx ) {
+ tls_ctx_ref( lo->ldo_tls_ctx );
+ }
+ break;
+ case LDAP_OPT_X_TLS_CACERTFILE:
+ *(char **)arg = lo->ldo_tls_cacertfile ?
+ LDAP_STRDUP( lo->ldo_tls_cacertfile ) : NULL;
+ break;
+ case LDAP_OPT_X_TLS_CACERTDIR:
+ *(char **)arg = lo->ldo_tls_cacertdir ?
+ LDAP_STRDUP( lo->ldo_tls_cacertdir ) : NULL;
+ break;
+ case LDAP_OPT_X_TLS_CERTFILE:
+ *(char **)arg = lo->ldo_tls_certfile ?
+ LDAP_STRDUP( lo->ldo_tls_certfile ) : NULL;
+ break;
+ case LDAP_OPT_X_TLS_KEYFILE:
+ *(char **)arg = lo->ldo_tls_keyfile ?
+ LDAP_STRDUP( lo->ldo_tls_keyfile ) : NULL;
+ break;
+ case LDAP_OPT_X_TLS_DHFILE:
+ *(char **)arg = lo->ldo_tls_dhfile ?
+ LDAP_STRDUP( lo->ldo_tls_dhfile ) : NULL;
+ break;
+ case LDAP_OPT_X_TLS_ECNAME:
+ *(char **)arg = lo->ldo_tls_ecname ?
+ LDAP_STRDUP( lo->ldo_tls_ecname ) : NULL;
+ break;
+ case LDAP_OPT_X_TLS_CRLFILE: /* GnuTLS only */
+ *(char **)arg = lo->ldo_tls_crlfile ?
+ LDAP_STRDUP( lo->ldo_tls_crlfile ) : NULL;
+ break;
+ case LDAP_OPT_X_TLS_REQUIRE_CERT:
+ *(int *)arg = lo->ldo_tls_require_cert;
+ break;
+ case LDAP_OPT_X_TLS_REQUIRE_SAN:
+ *(int *)arg = lo->ldo_tls_require_san;
+ break;
+#ifdef HAVE_OPENSSL
+ case LDAP_OPT_X_TLS_CRLCHECK: /* OpenSSL only */
+ *(int *)arg = lo->ldo_tls_crlcheck;
+ break;
+#endif
+ case LDAP_OPT_X_TLS_CIPHER_SUITE:
+ *(char **)arg = lo->ldo_tls_ciphersuite ?
+ LDAP_STRDUP( lo->ldo_tls_ciphersuite ) : NULL;
+ break;
+ case LDAP_OPT_X_TLS_PROTOCOL_MIN:
+ *(int *)arg = lo->ldo_tls_protocol_min;
+ break;
+ case LDAP_OPT_X_TLS_PROTOCOL_MAX:
+ *(int *)arg = lo->ldo_tls_protocol_max;
+ break;
+ case LDAP_OPT_X_TLS_RANDOM_FILE:
+ *(char **)arg = lo->ldo_tls_randfile ?
+ LDAP_STRDUP( lo->ldo_tls_randfile ) : NULL;
+ break;
+ case LDAP_OPT_X_TLS_SSL_CTX: {
+ void *retval = 0;
+ if ( ld != NULL ) {
+ LDAPConn *conn = ld->ld_defconn;
+ if ( conn != NULL ) {
+ Sockbuf *sb = conn->lconn_sb;
+ retval = ldap_pvt_tls_sb_ctx( sb );
+ }
+ }
+ *(void **)arg = retval;
+ break;
+ }
+ case LDAP_OPT_X_TLS_CONNECT_CB:
+ *(LDAP_TLS_CONNECT_CB **)arg = lo->ldo_tls_connect_cb;
+ break;
+ case LDAP_OPT_X_TLS_CONNECT_ARG:
+ *(void **)arg = lo->ldo_tls_connect_arg;
+ break;
+ case LDAP_OPT_X_TLS_VERSION: {
+ void *sess = NULL;
+ const char *retval = NULL;
+ if ( ld != NULL ) {
+ LDAPConn *conn = ld->ld_defconn;
+ if ( conn != NULL ) {
+ Sockbuf *sb = conn->lconn_sb;
+ sess = ldap_pvt_tls_sb_ctx( sb );
+ if ( sess != NULL )
+ retval = ldap_pvt_tls_get_version( sess );
+ }
+ }
+ *(char **)arg = retval ? LDAP_STRDUP( retval ) : NULL;
+ break;
+ }
+ case LDAP_OPT_X_TLS_CIPHER: {
+ void *sess = NULL;
+ const char *retval = NULL;
+ if ( ld != NULL ) {
+ LDAPConn *conn = ld->ld_defconn;
+ if ( conn != NULL ) {
+ Sockbuf *sb = conn->lconn_sb;
+ sess = ldap_pvt_tls_sb_ctx( sb );
+ if ( sess != NULL )
+ retval = ldap_pvt_tls_get_cipher( sess );
+ }
+ }
+ *(char **)arg = retval ? LDAP_STRDUP( retval ) : NULL;
+ break;
+ }
+ case LDAP_OPT_X_TLS_PEERCERT: {
+ void *sess = NULL;
+ struct berval *bv = arg;
+ bv->bv_len = 0;
+ bv->bv_val = NULL;
+ if ( ld != NULL ) {
+ LDAPConn *conn = ld->ld_defconn;
+ if ( conn != NULL ) {
+ Sockbuf *sb = conn->lconn_sb;
+ sess = ldap_pvt_tls_sb_ctx( sb );
+ if ( sess != NULL )
+ return ldap_pvt_tls_get_peercert( sess, bv );
+ }
+ }
+ break;
+ }
+ case LDAP_OPT_X_TLS_CACERT: {
+ struct berval *bv = arg;
+ if ( lo->ldo_tls_cacert.bv_val ) {
+ ber_dupbv( bv, &lo->ldo_tls_cacert );
+ } else {
+ BER_BVZERO( bv );
+ }
+ break;
+ }
+ case LDAP_OPT_X_TLS_CERT: {
+ struct berval *bv = arg;
+ if ( lo->ldo_tls_cert.bv_val ) {
+ ber_dupbv( bv, &lo->ldo_tls_cert );
+ } else {
+ BER_BVZERO( bv );
+ }
+ break;
+ }
+ case LDAP_OPT_X_TLS_KEY: {
+ struct berval *bv = arg;
+ if ( lo->ldo_tls_key.bv_val ) {
+ ber_dupbv( bv, &lo->ldo_tls_key );
+ } else {
+ BER_BVZERO( bv );
+ }
+ break;
+ }
+
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+int
+ldap_pvt_tls_set_option( LDAP *ld, int option, void *arg )
+{
+ struct ldapoptions *lo;
+
+ if( ld != NULL ) {
+ assert( LDAP_VALID( ld ) );
+
+ if( !LDAP_VALID( ld ) ) {
+ return LDAP_OPT_ERROR;
+ }
+
+ lo = &ld->ld_options;
+
+ } else {
+ /* Get pointer to global option structure */
+ lo = LDAP_INT_GLOBAL_OPT();
+ if ( lo == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+ }
+
+ switch( option ) {
+ case LDAP_OPT_X_TLS:
+ if ( !arg ) return -1;
+
+ switch( *(int *) arg ) {
+ case LDAP_OPT_X_TLS_NEVER:
+ case LDAP_OPT_X_TLS_DEMAND:
+ case LDAP_OPT_X_TLS_ALLOW:
+ case LDAP_OPT_X_TLS_TRY:
+ case LDAP_OPT_X_TLS_HARD:
+ if (lo != NULL) {
+ lo->ldo_tls_mode = *(int *)arg;
+ }
+
+ return 0;
+ }
+ return -1;
+
+ case LDAP_OPT_X_TLS_CTX:
+ if ( lo->ldo_tls_ctx )
+ ldap_pvt_tls_ctx_free( lo->ldo_tls_ctx );
+ lo->ldo_tls_ctx = arg;
+ tls_ctx_ref( lo->ldo_tls_ctx );
+ return 0;
+ case LDAP_OPT_X_TLS_CONNECT_CB:
+ lo->ldo_tls_connect_cb = (LDAP_TLS_CONNECT_CB *)arg;
+ return 0;
+ case LDAP_OPT_X_TLS_CONNECT_ARG:
+ lo->ldo_tls_connect_arg = arg;
+ return 0;
+ case LDAP_OPT_X_TLS_CACERTFILE:
+ if ( lo->ldo_tls_cacertfile ) LDAP_FREE( lo->ldo_tls_cacertfile );
+ lo->ldo_tls_cacertfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL;
+ return 0;
+ case LDAP_OPT_X_TLS_CACERTDIR:
+ if ( lo->ldo_tls_cacertdir ) LDAP_FREE( lo->ldo_tls_cacertdir );
+ lo->ldo_tls_cacertdir = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL;
+ return 0;
+ case LDAP_OPT_X_TLS_CERTFILE:
+ if ( lo->ldo_tls_certfile ) LDAP_FREE( lo->ldo_tls_certfile );
+ lo->ldo_tls_certfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL;
+ return 0;
+ case LDAP_OPT_X_TLS_KEYFILE:
+ if ( lo->ldo_tls_keyfile ) LDAP_FREE( lo->ldo_tls_keyfile );
+ lo->ldo_tls_keyfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL;
+ return 0;
+ case LDAP_OPT_X_TLS_DHFILE:
+ if ( lo->ldo_tls_dhfile ) LDAP_FREE( lo->ldo_tls_dhfile );
+ lo->ldo_tls_dhfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL;
+ return 0;
+ case LDAP_OPT_X_TLS_ECNAME:
+ if ( lo->ldo_tls_ecname ) LDAP_FREE( lo->ldo_tls_ecname );
+ lo->ldo_tls_ecname = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL;
+ return 0;
+ case LDAP_OPT_X_TLS_CRLFILE: /* GnuTLS only */
+ if ( lo->ldo_tls_crlfile ) LDAP_FREE( lo->ldo_tls_crlfile );
+ lo->ldo_tls_crlfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL;
+ return 0;
+ case LDAP_OPT_X_TLS_REQUIRE_CERT:
+ if ( !arg ) return -1;
+ switch( *(int *) arg ) {
+ case LDAP_OPT_X_TLS_NEVER:
+ case LDAP_OPT_X_TLS_DEMAND:
+ case LDAP_OPT_X_TLS_ALLOW:
+ case LDAP_OPT_X_TLS_TRY:
+ case LDAP_OPT_X_TLS_HARD:
+ lo->ldo_tls_require_cert = * (int *) arg;
+ return 0;
+ }
+ return -1;
+ case LDAP_OPT_X_TLS_REQUIRE_SAN:
+ if ( !arg ) return -1;
+ switch( *(int *) arg ) {
+ case LDAP_OPT_X_TLS_NEVER:
+ case LDAP_OPT_X_TLS_DEMAND:
+ case LDAP_OPT_X_TLS_ALLOW:
+ case LDAP_OPT_X_TLS_TRY:
+ case LDAP_OPT_X_TLS_HARD:
+ lo->ldo_tls_require_san = * (int *) arg;
+ return 0;
+ }
+ return -1;
+#ifdef HAVE_OPENSSL
+ case LDAP_OPT_X_TLS_CRLCHECK: /* OpenSSL only */
+ if ( !arg ) return -1;
+ switch( *(int *) arg ) {
+ case LDAP_OPT_X_TLS_CRL_NONE:
+ case LDAP_OPT_X_TLS_CRL_PEER:
+ case LDAP_OPT_X_TLS_CRL_ALL:
+ lo->ldo_tls_crlcheck = * (int *) arg;
+ return 0;
+ }
+ return -1;
+#endif
+ case LDAP_OPT_X_TLS_CIPHER_SUITE:
+ if ( lo->ldo_tls_ciphersuite ) LDAP_FREE( lo->ldo_tls_ciphersuite );
+ lo->ldo_tls_ciphersuite = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL;
+ return 0;
+
+ case LDAP_OPT_X_TLS_PROTOCOL_MIN:
+ if ( !arg ) return -1;
+ lo->ldo_tls_protocol_min = *(int *)arg;
+ return 0;
+ case LDAP_OPT_X_TLS_PROTOCOL_MAX:
+ if ( !arg ) return -1;
+ lo->ldo_tls_protocol_max = *(int *)arg;
+ return 0;
+ case LDAP_OPT_X_TLS_RANDOM_FILE:
+ if ( ld != NULL )
+ return -1;
+ if ( lo->ldo_tls_randfile ) LDAP_FREE (lo->ldo_tls_randfile );
+ lo->ldo_tls_randfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL;
+ break;
+ case LDAP_OPT_X_TLS_NEWCTX:
+ if ( !arg ) return -1;
+ if ( lo->ldo_tls_ctx )
+ ldap_pvt_tls_ctx_free( lo->ldo_tls_ctx );
+ lo->ldo_tls_ctx = NULL;
+ return ldap_int_tls_init_ctx( lo, *(int *)arg );
+ case LDAP_OPT_X_TLS_CACERT:
+ if ( lo->ldo_tls_cacert.bv_val )
+ LDAP_FREE( lo->ldo_tls_cacert.bv_val );
+ if ( arg ) {
+ lo->ldo_tls_cacert.bv_len = ((struct berval *)arg)->bv_len;
+ lo->ldo_tls_cacert.bv_val = LDAP_MALLOC( lo->ldo_tls_cacert.bv_len );
+ if ( !lo->ldo_tls_cacert.bv_val )
+ return -1;
+ AC_MEMCPY( lo->ldo_tls_cacert.bv_val, ((struct berval *)arg)->bv_val, lo->ldo_tls_cacert.bv_len );
+ } else {
+ BER_BVZERO( &lo->ldo_tls_cacert );
+ }
+ break;
+ case LDAP_OPT_X_TLS_CERT:
+ if ( lo->ldo_tls_cert.bv_val )
+ LDAP_FREE( lo->ldo_tls_cert.bv_val );
+ if ( arg ) {
+ lo->ldo_tls_cert.bv_len = ((struct berval *)arg)->bv_len;
+ lo->ldo_tls_cert.bv_val = LDAP_MALLOC( lo->ldo_tls_cert.bv_len );
+ if ( !lo->ldo_tls_cert.bv_val )
+ return -1;
+ AC_MEMCPY( lo->ldo_tls_cert.bv_val, ((struct berval *)arg)->bv_val, lo->ldo_tls_cert.bv_len );
+ } else {
+ BER_BVZERO( &lo->ldo_tls_cert );
+ }
+ break;
+ case LDAP_OPT_X_TLS_KEY:
+ if ( lo->ldo_tls_key.bv_val )
+ LDAP_FREE( lo->ldo_tls_key.bv_val );
+ if ( arg ) {
+ lo->ldo_tls_key.bv_len = ((struct berval *)arg)->bv_len;
+ lo->ldo_tls_key.bv_val = LDAP_MALLOC( lo->ldo_tls_key.bv_len );
+ if ( !lo->ldo_tls_key.bv_val )
+ return -1;
+ AC_MEMCPY( lo->ldo_tls_key.bv_val, ((struct berval *)arg)->bv_val, lo->ldo_tls_key.bv_len );
+ } else {
+ BER_BVZERO( &lo->ldo_tls_key );
+ }
+ break;
+ case LDAP_OPT_X_TLS_PEERKEY_HASH: {
+ /* arg = "[hashalg:]pubkey_hash" */
+ struct berval bv;
+ char *p, *pin = arg;
+ int rc = LDAP_SUCCESS;
+
+ if ( !tls_imp->ti_session_pinning ) return -1;
+
+ if ( !pin || !*pin ) {
+ if ( lo->ldo_tls_pin_hashalg ) {
+ LDAP_FREE( lo->ldo_tls_pin_hashalg );
+ } else if ( lo->ldo_tls_pin.bv_val ) {
+ LDAP_FREE( lo->ldo_tls_pin.bv_val );
+ }
+ lo->ldo_tls_pin_hashalg = NULL;
+ BER_BVZERO( &lo->ldo_tls_pin );
+ return rc;
+ }
+
+ pin = LDAP_STRDUP( pin );
+ p = strchr( pin, ':' );
+
+ /* pubkey (its hash) goes in bv, alg in p */
+ if ( p ) {
+ *p = '\0';
+ bv.bv_val = p+1;
+ p = pin;
+ } else {
+ bv.bv_val = pin;
+ }
+
+ bv.bv_len = strlen(bv.bv_val);
+ if ( ldap_int_decode_b64_inplace( &bv ) ) {
+ LDAP_FREE( pin );
+ return -1;
+ }
+
+ if ( ld != NULL ) {
+ LDAPConn *conn = ld->ld_defconn;
+ if ( conn != NULL ) {
+ Sockbuf *sb = conn->lconn_sb;
+ void *sess = ldap_pvt_tls_sb_ctx( sb );
+ if ( sess != NULL ) {
+ rc = tls_imp->ti_session_pinning( ld, sess, p, &bv );
+ }
+ }
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ if ( lo->ldo_tls_pin_hashalg ) {
+ LDAP_FREE( lo->ldo_tls_pin_hashalg );
+ } else if ( lo->ldo_tls_pin.bv_val ) {
+ LDAP_FREE( lo->ldo_tls_pin.bv_val );
+ }
+ lo->ldo_tls_pin_hashalg = p;
+ lo->ldo_tls_pin = bv;
+ } else {
+ LDAP_FREE( pin );
+ }
+
+ return rc;
+ }
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+int
+ldap_int_tls_start ( LDAP *ld, LDAPConn *conn, LDAPURLDesc *srv )
+{
+ Sockbuf *sb;
+ char *host;
+ void *ssl;
+ int ret, async;
+ struct timeval start_time_tv, tv, tv0;
+ ber_socket_t sd = AC_SOCKET_ERROR;
+
+ if ( !conn )
+ return LDAP_PARAM_ERROR;
+
+ sb = conn->lconn_sb;
+ if( srv ) {
+ host = srv->lud_host;
+ } else {
+ host = conn->lconn_server->lud_host;
+ }
+
+ /* avoid NULL host */
+ if( host == NULL ) {
+ host = "localhost";
+ }
+
+ (void) tls_init( tls_imp, 0 );
+
+ /*
+ * Use non-blocking io during SSL Handshake when a timeout is configured
+ */
+ async = LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_CONNECT_ASYNC );
+ if ( ld->ld_options.ldo_tm_net.tv_sec >= 0 ) {
+ if ( !async ) {
+ /* if async, this has already been set */
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_NONBLOCK, (void*)1 );
+ }
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+ tv = ld->ld_options.ldo_tm_net;
+ tv0 = tv;
+#ifdef HAVE_GETTIMEOFDAY
+ gettimeofday( &start_time_tv, NULL );
+#else /* ! HAVE_GETTIMEOFDAY */
+ time( &start_time_tv.tv_sec );
+ start_time_tv.tv_usec = 0;
+#endif /* ! HAVE_GETTIMEOFDAY */
+ }
+
+ ld->ld_errno = LDAP_SUCCESS;
+ ret = ldap_int_tls_connect( ld, conn, host );
+
+ /* this mainly only happens for non-blocking io
+ * but can also happen when the handshake is too
+ * big for a single network message.
+ */
+ while ( ret > 0 ) {
+ if ( async ) {
+ struct timeval curr_time_tv, delta_tv;
+ int wr=0;
+
+ if ( sb->sb_trans_needs_read ) {
+ wr=0;
+ } else if ( sb->sb_trans_needs_write ) {
+ wr=1;
+ }
+ Debug1( LDAP_DEBUG_TRACE, "ldap_int_tls_start: ldap_int_tls_connect needs %s\n",
+ wr ? "write": "read" );
+
+ /* This is mostly copied from result.c:wait4msg(), should
+ * probably be moved into a separate function */
+#ifdef HAVE_GETTIMEOFDAY
+ gettimeofday( &curr_time_tv, NULL );
+#else /* ! HAVE_GETTIMEOFDAY */
+ time( &curr_time_tv.tv_sec );
+ curr_time_tv.tv_usec = 0;
+#endif /* ! HAVE_GETTIMEOFDAY */
+
+ /* delta = curr - start */
+ delta_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
+ delta_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
+ if ( delta_tv.tv_usec < 0 ) {
+ delta_tv.tv_sec--;
+ delta_tv.tv_usec += 1000000;
+ }
+
+ /* tv0 < delta ? */
+ if ( ( tv0.tv_sec < delta_tv.tv_sec ) ||
+ ( ( tv0.tv_sec == delta_tv.tv_sec ) &&
+ ( tv0.tv_usec < delta_tv.tv_usec ) ) )
+ {
+ ret = -1;
+ ld->ld_errno = LDAP_TIMEOUT;
+ break;
+ }
+ /* timeout -= delta_time */
+ tv0.tv_sec -= delta_tv.tv_sec;
+ tv0.tv_usec -= delta_tv.tv_usec;
+ if ( tv0.tv_usec < 0 ) {
+ tv0.tv_sec--;
+ tv0.tv_usec += 1000000;
+ }
+ start_time_tv.tv_sec = curr_time_tv.tv_sec;
+ start_time_tv.tv_usec = curr_time_tv.tv_usec;
+ tv = tv0;
+ Debug3( LDAP_DEBUG_TRACE, "ldap_int_tls_start: ld %p %ld s %ld us to go\n",
+ (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec );
+ ret = ldap_int_poll( ld, sd, &tv, wr);
+ if ( ret < 0 ) {
+ ld->ld_errno = LDAP_TIMEOUT;
+ break;
+ }
+ }
+ ret = ldap_int_tls_connect( ld, conn, host );
+ }
+
+ if ( ret < 0 ) {
+ if ( ld->ld_errno == LDAP_SUCCESS )
+ ld->ld_errno = LDAP_CONNECT_ERROR;
+ return (ld->ld_errno);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+void *
+ldap_pvt_tls_sb_ctx( Sockbuf *sb )
+{
+ void *p = NULL;
+
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_SSL, (void *)&p );
+ return p;
+}
+
+int
+ldap_pvt_tls_get_strength( void *s )
+{
+ tls_session *session = s;
+
+ return tls_imp->ti_session_strength( session );
+}
+
+int
+ldap_pvt_tls_get_my_dn( void *s, struct berval *dn, LDAPDN_rewrite_dummy *func, unsigned flags )
+{
+ tls_session *session = s;
+ struct berval der_dn;
+ int rc;
+
+ rc = tls_imp->ti_session_my_dn( session, &der_dn );
+ if ( rc == LDAP_SUCCESS )
+ rc = ldap_X509dn2bv(&der_dn, dn, (LDAPDN_rewrite_func *)func, flags );
+ return rc;
+}
+
+int
+ldap_pvt_tls_get_unique( void *s, struct berval *buf, int is_server )
+{
+ tls_session *session = s;
+ return tls_imp->ti_session_unique( session, buf, is_server );
+}
+
+int
+ldap_pvt_tls_get_endpoint( void *s, struct berval *buf, int is_server )
+{
+ tls_session *session = s;
+ return tls_imp->ti_session_endpoint( session, buf, is_server );
+}
+
+const char *
+ldap_pvt_tls_get_version( void *s )
+{
+ tls_session *session = s;
+ return tls_imp->ti_session_version( session );
+}
+
+const char *
+ldap_pvt_tls_get_cipher( void *s )
+{
+ tls_session *session = s;
+ return tls_imp->ti_session_cipher( session );
+}
+
+int
+ldap_pvt_tls_get_peercert( void *s, struct berval *der )
+{
+ tls_session *session = s;
+ return tls_imp->ti_session_peercert( session, der );
+}
+#endif /* HAVE_TLS */
+
+int
+ldap_start_tls( LDAP *ld,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls,
+ int *msgidp )
+{
+ return ldap_extended_operation( ld, LDAP_EXOP_START_TLS,
+ NULL, serverctrls, clientctrls, msgidp );
+}
+
+int
+ldap_install_tls( LDAP *ld )
+{
+#ifndef HAVE_TLS
+ return LDAP_NOT_SUPPORTED;
+#else
+ if ( ldap_tls_inplace( ld ) ) {
+ return LDAP_LOCAL_ERROR;
+ }
+
+ return ldap_int_tls_start( ld, ld->ld_defconn, NULL );
+#endif
+}
+
+int
+ldap_start_tls_s ( LDAP *ld,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls )
+{
+#ifndef HAVE_TLS
+ return LDAP_NOT_SUPPORTED;
+#else
+ int rc;
+ char *rspoid = NULL;
+ struct berval *rspdata = NULL;
+
+ /* XXYYZ: this initiates operation only on default connection! */
+
+ if ( ldap_tls_inplace( ld ) ) {
+ return LDAP_LOCAL_ERROR;
+ }
+
+ rc = ldap_extended_operation_s( ld, LDAP_EXOP_START_TLS,
+ NULL, serverctrls, clientctrls, &rspoid, &rspdata );
+
+ if ( rspoid != NULL ) {
+ LDAP_FREE(rspoid);
+ }
+
+ if ( rspdata != NULL ) {
+ ber_bvfree( rspdata );
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ rc = ldap_int_tls_start( ld, ld->ld_defconn, NULL );
+ }
+
+ return rc;
+#endif
+}
+
+/* These tags probably all belong in lber.h, but they're
+ * not normally encountered when processing LDAP, so maybe
+ * they belong somewhere else instead.
+ */
+
+#define LBER_TAG_OID ((ber_tag_t) 0x06UL)
+
+/* Tags for string types used in a DirectoryString.
+ *
+ * Note that IA5string is not one of the defined choices for
+ * DirectoryString in X.520, but it gets used for email AVAs.
+ */
+#define LBER_TAG_UTF8 ((ber_tag_t) 0x0cUL)
+#define LBER_TAG_PRINTABLE ((ber_tag_t) 0x13UL)
+#define LBER_TAG_TELETEX ((ber_tag_t) 0x14UL)
+#define LBER_TAG_IA5 ((ber_tag_t) 0x16UL)
+#define LBER_TAG_UNIVERSAL ((ber_tag_t) 0x1cUL)
+#define LBER_TAG_BMP ((ber_tag_t) 0x1eUL)
+
+static oid_name *
+find_oid( struct berval *oid )
+{
+ int i;
+
+ for ( i=0; !BER_BVISNULL( &oids[i].oid ); i++ ) {
+ if ( oids[i].oid.bv_len != oid->bv_len ) continue;
+ if ( !strcmp( oids[i].oid.bv_val, oid->bv_val ))
+ return &oids[i];
+ }
+ return NULL;
+}
+
+/* Converts BER Bitstring value to LDAP BitString value (RFC4517)
+ *
+ * berValue : IN
+ * rfc4517Value: OUT
+ *
+ * berValue and ldapValue should not be NULL
+ */
+
+#define BITS_PER_BYTE 8
+#define SQUOTE_LENGTH 1
+#define B_CHAR_LENGTH 1
+#define STR_OVERHEAD (2*SQUOTE_LENGTH + B_CHAR_LENGTH)
+
+static int
+der_to_ldap_BitString (struct berval *berValue,
+ struct berval *ldapValue)
+{
+ ber_len_t bitPadding=0;
+ ber_len_t bits, maxBits;
+ char *tmpStr;
+ unsigned char byte;
+ ber_len_t bitLength;
+ ber_len_t valLen;
+ unsigned char* valPtr;
+
+ ldapValue->bv_len=0;
+ ldapValue->bv_val=NULL;
+
+ /* Gets padding and points to binary data */
+ valLen=berValue->bv_len;
+ valPtr=(unsigned char*)berValue->bv_val;
+ if (valLen) {
+ bitPadding=(ber_len_t)(valPtr[0]);
+ valLen--;
+ valPtr++;
+ }
+ /* If Block is non DER encoding fixes to DER encoding */
+ if (bitPadding >= BITS_PER_BYTE) {
+ if (valLen*BITS_PER_BYTE > bitPadding ) {
+ valLen-=(bitPadding/BITS_PER_BYTE);
+ bitPadding%=BITS_PER_BYTE;
+ } else {
+ valLen=0;
+ bitPadding=0;
+ }
+ }
+ /* Just in case bad encoding */
+ if (valLen*BITS_PER_BYTE < bitPadding ) {
+ bitPadding=0;
+ valLen=0;
+ }
+
+ /* Gets buffer to hold RFC4517 Bit String format */
+ bitLength=valLen*BITS_PER_BYTE-bitPadding;
+ tmpStr=LDAP_MALLOC(bitLength + STR_OVERHEAD + 1);
+
+ if (!tmpStr)
+ return LDAP_NO_MEMORY;
+
+ ldapValue->bv_val=tmpStr;
+ ldapValue->bv_len=bitLength + STR_OVERHEAD;
+
+ /* Formatting in '*binary-digit'B format */
+ maxBits=BITS_PER_BYTE;
+ *tmpStr++ ='\'';
+ while(valLen) {
+ byte=*valPtr;
+ if (valLen==1)
+ maxBits-=bitPadding;
+ for (bits=0; bits<maxBits; bits++) {
+ if (0x80 & byte)
+ *tmpStr='1';
+ else
+ *tmpStr='0';
+ tmpStr++;
+ byte<<=1;
+ }
+ valPtr++;
+ valLen--;
+ }
+ *tmpStr++ ='\'';
+ *tmpStr++ ='B';
+ *tmpStr=0;
+
+ return LDAP_SUCCESS;
+}
+
+/* Convert a structured DN from an X.509 certificate into an LDAPV3 DN.
+ * x509_name must be raw DER. If func is non-NULL, the
+ * constructed DN will use numeric OIDs to identify attributeTypes,
+ * and the func() will be invoked to rewrite the DN with the given
+ * flags.
+ *
+ * Otherwise the DN will use shortNames from a hardcoded table.
+ */
+int
+ldap_X509dn2bv( void *x509_name, struct berval *bv, LDAPDN_rewrite_func *func,
+ unsigned flags )
+{
+ LDAPDN newDN;
+ LDAPRDN newRDN;
+ LDAPAVA *newAVA, *baseAVA;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ char oids[8192], *oidptr = oids, *oidbuf = NULL;
+ void *ptrs[2048];
+ char *dn_end, *rdn_end;
+ int i, navas, nrdns, rc = LDAP_SUCCESS;
+ size_t dnsize, oidrem = sizeof(oids), oidsize = 0;
+ int csize;
+ ber_tag_t tag;
+ ber_len_t len;
+ oid_name *oidname;
+
+ struct berval Oid, Val, oid2, *in = x509_name;
+
+ assert( bv != NULL );
+
+ bv->bv_len = 0;
+ bv->bv_val = NULL;
+
+ navas = 0;
+ nrdns = 0;
+
+ /* A DN is a SEQUENCE of RDNs. An RDN is a SET of AVAs.
+ * An AVA is a SEQUENCE of attr and value.
+ * Count the number of AVAs and RDNs
+ */
+ ber_init2( ber, in, LBER_USE_DER );
+ tag = ber_peek_tag( ber, &len );
+ if ( tag != LBER_SEQUENCE )
+ return LDAP_DECODING_ERROR;
+
+ for ( tag = ber_first_element( ber, &len, &dn_end );
+ tag == LBER_SET;
+ tag = ber_next_element( ber, &len, dn_end )) {
+ nrdns++;
+ for ( tag = ber_first_element( ber, &len, &rdn_end );
+ tag == LBER_SEQUENCE;
+ tag = ber_next_element( ber, &len, rdn_end )) {
+ if ( rdn_end > dn_end )
+ return LDAP_DECODING_ERROR;
+ tag = ber_skip_tag( ber, &len );
+ ber_skip_data( ber, len );
+ navas++;
+ }
+ }
+
+ /* Rewind and prepare to extract */
+ ber_rewind( ber );
+ tag = ber_first_element( ber, &len, &dn_end );
+ if ( tag != LBER_SET )
+ return LDAP_DECODING_ERROR;
+
+ /* Allocate the DN/RDN/AVA stuff as a single block */
+ dnsize = sizeof(LDAPRDN) * (nrdns+1);
+ dnsize += sizeof(LDAPAVA *) * (navas+nrdns);
+ dnsize += sizeof(LDAPAVA) * navas;
+ if (dnsize > sizeof(ptrs)) {
+ newDN = (LDAPDN)LDAP_MALLOC( dnsize );
+ if ( newDN == NULL )
+ return LDAP_NO_MEMORY;
+ } else {
+ newDN = (LDAPDN)(char *)ptrs;
+ }
+
+ newDN[nrdns] = NULL;
+ newRDN = (LDAPRDN)(newDN + nrdns+1);
+ newAVA = (LDAPAVA *)(newRDN + navas + nrdns);
+ baseAVA = newAVA;
+
+ for ( i = nrdns - 1; i >= 0; i-- ) {
+ newDN[i] = newRDN;
+
+ for ( tag = ber_first_element( ber, &len, &rdn_end );
+ tag == LBER_SEQUENCE;
+ tag = ber_next_element( ber, &len, rdn_end )) {
+
+ *newRDN++ = newAVA;
+ tag = ber_skip_tag( ber, &len );
+ tag = ber_get_stringbv( ber, &Oid, LBER_BV_NOTERM );
+ if ( tag != LBER_TAG_OID ) {
+ rc = LDAP_DECODING_ERROR;
+ goto nomem;
+ }
+
+ oid2.bv_val = oidptr;
+ oid2.bv_len = oidrem;
+ if ( ber_decode_oid( &Oid, &oid2 ) < 0 ) {
+ rc = LDAP_DECODING_ERROR;
+ goto nomem;
+ }
+ oidname = find_oid( &oid2 );
+ if ( !oidname ) {
+ newAVA->la_attr = oid2;
+ oidptr += oid2.bv_len + 1;
+ oidrem -= oid2.bv_len + 1;
+
+ /* Running out of OID buffer space? */
+ if (oidrem < 128) {
+ if ( oidsize == 0 ) {
+ oidsize = sizeof(oids) * 2;
+ oidrem = oidsize;
+ oidbuf = LDAP_MALLOC( oidsize );
+ if ( oidbuf == NULL ) goto nomem;
+ oidptr = oidbuf;
+ } else {
+ char *old = oidbuf;
+ oidbuf = LDAP_REALLOC( oidbuf, oidsize*2 );
+ if ( oidbuf == NULL ) goto nomem;
+ /* Buffer moved! Fix AVA pointers */
+ if ( old != oidbuf ) {
+ LDAPAVA *a;
+ long dif = oidbuf - old;
+
+ for (a=baseAVA; a<=newAVA; a++){
+ if (a->la_attr.bv_val >= old &&
+ a->la_attr.bv_val <= (old + oidsize))
+ a->la_attr.bv_val += dif;
+ }
+ }
+ oidptr = oidbuf + oidsize - oidrem;
+ oidrem += oidsize;
+ oidsize *= 2;
+ }
+ }
+ } else {
+ if ( func ) {
+ newAVA->la_attr = oidname->oid;
+ } else {
+ newAVA->la_attr = oidname->name;
+ }
+ }
+ newAVA->la_private = NULL;
+ newAVA->la_flags = LDAP_AVA_STRING;
+ tag = ber_get_stringbv( ber, &Val, LBER_BV_NOTERM );
+ switch(tag) {
+ case LBER_TAG_UNIVERSAL:
+ /* This uses 32-bit ISO 10646-1 */
+ csize = 4; goto to_utf8;
+ case LBER_TAG_BMP:
+ /* This uses 16-bit ISO 10646-1 */
+ csize = 2; goto to_utf8;
+ case LBER_TAG_TELETEX:
+ /* This uses 8-bit, assume ISO 8859-1 */
+ csize = 1;
+to_utf8: rc = ldap_ucs_to_utf8s( &Val, csize, &newAVA->la_value );
+ newAVA->la_flags |= LDAP_AVA_NONPRINTABLE;
+allocd:
+ newAVA->la_flags |= LDAP_AVA_FREE_VALUE;
+ if (rc != LDAP_SUCCESS) goto nomem;
+ break;
+ case LBER_TAG_UTF8:
+ newAVA->la_flags |= LDAP_AVA_NONPRINTABLE;
+ /* This is already in UTF-8 encoding */
+ case LBER_TAG_IA5:
+ case LBER_TAG_PRINTABLE:
+ /* These are always 7-bit strings */
+ newAVA->la_value = Val;
+ break;
+ case LBER_BITSTRING:
+ /* X.690 bitString value converted to RFC4517 Bit String */
+ rc = der_to_ldap_BitString( &Val, &newAVA->la_value );
+ goto allocd;
+ case LBER_DEFAULT:
+ /* decode error */
+ rc = LDAP_DECODING_ERROR;
+ goto nomem;
+ default:
+ /* Not a string type at all */
+ newAVA->la_flags = 0;
+ newAVA->la_value = Val;
+ break;
+ }
+ newAVA++;
+ }
+ *newRDN++ = NULL;
+ tag = ber_next_element( ber, &len, dn_end );
+ }
+
+ if ( func ) {
+ rc = func( newDN, flags, NULL );
+ if ( rc != LDAP_SUCCESS )
+ goto nomem;
+ }
+
+ rc = ldap_dn2bv_x( newDN, bv, LDAP_DN_FORMAT_LDAPV3, NULL );
+
+nomem:
+ for (;baseAVA < newAVA; baseAVA++) {
+ if (baseAVA->la_flags & LDAP_AVA_FREE_ATTR)
+ LDAP_FREE( baseAVA->la_attr.bv_val );
+ if (baseAVA->la_flags & LDAP_AVA_FREE_VALUE)
+ LDAP_FREE( baseAVA->la_value.bv_val );
+ }
+
+ if ( oidsize != 0 )
+ LDAP_FREE( oidbuf );
+ if ( newDN != (LDAPDN)(char *) ptrs )
+ LDAP_FREE( newDN );
+ return rc;
+}
+
diff --git a/libraries/libldap/tls_g.c b/libraries/libldap/tls_g.c
new file mode 100644
index 0000000..9b9136c
--- /dev/null
+++ b/libraries/libldap/tls_g.c
@@ -0,0 +1,1258 @@
+/* tls_g.c - Handle tls/ssl using GNUTLS. */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS: GNUTLS support written by Howard Chu and
+ * Emily Backes; sponsored by The Written Word (thewrittenword.com)
+ * and Stanford University (stanford.edu).
+ */
+
+#include "portable.h"
+
+#ifdef HAVE_GNUTLS
+
+#include "ldap_config.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+#include <ac/param.h>
+#include <ac/dirent.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "ldap-int.h"
+#include "ldap-tls.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <gnutls/abstract.h>
+#include <gnutls/crypto.h>
+
+typedef struct tlsg_ctx {
+ gnutls_certificate_credentials_t cred;
+ gnutls_dh_params_t dh_params;
+ unsigned long verify_depth;
+ int refcount;
+ int reqcert;
+ gnutls_priority_t prios;
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_t ref_mutex;
+#endif
+} tlsg_ctx;
+
+typedef struct tlsg_session {
+ gnutls_session_t session;
+ tlsg_ctx *ctx;
+ struct berval peer_der_dn;
+} tlsg_session;
+
+static int tlsg_parse_ciphers( tlsg_ctx *ctx, char *suites );
+static int tlsg_cert_verify( tlsg_session *s );
+
+#ifdef LDAP_R_COMPILE
+
+static void
+tlsg_thr_init( void )
+{
+ /* do nothing */
+}
+#endif /* LDAP_R_COMPILE */
+
+/*
+ * Initialize TLS subsystem. Should be called only once.
+ */
+static int
+tlsg_init( void )
+{
+ gnutls_global_init();
+ return 0;
+}
+
+/*
+ * Tear down the TLS subsystem. Should only be called once.
+ */
+static void
+tlsg_destroy( void )
+{
+ gnutls_global_deinit();
+}
+
+static tls_ctx *
+tlsg_ctx_new ( struct ldapoptions *lo )
+{
+ tlsg_ctx *ctx;
+
+ ctx = ber_memcalloc ( 1, sizeof (*ctx) );
+ if ( ctx ) {
+ if ( gnutls_certificate_allocate_credentials( &ctx->cred )) {
+ ber_memfree( ctx );
+ return NULL;
+ }
+ ctx->refcount = 1;
+ gnutls_priority_init( &ctx->prios, "NORMAL", NULL );
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_init( &ctx->ref_mutex );
+#endif
+ }
+ return (tls_ctx *)ctx;
+}
+
+static void
+tlsg_ctx_ref( tls_ctx *ctx )
+{
+ tlsg_ctx *c = (tlsg_ctx *)ctx;
+ LDAP_MUTEX_LOCK( &c->ref_mutex );
+ c->refcount++;
+ LDAP_MUTEX_UNLOCK( &c->ref_mutex );
+}
+
+static void
+tlsg_ctx_free ( tls_ctx *ctx )
+{
+ tlsg_ctx *c = (tlsg_ctx *)ctx;
+ int refcount;
+
+ if ( !c ) return;
+
+ LDAP_MUTEX_LOCK( &c->ref_mutex );
+ refcount = --c->refcount;
+ LDAP_MUTEX_UNLOCK( &c->ref_mutex );
+ if ( refcount )
+ return;
+ gnutls_priority_deinit( c->prios );
+ gnutls_certificate_free_credentials( c->cred );
+ if ( c->dh_params )
+ gnutls_dh_params_deinit( c->dh_params );
+ ber_memfree ( c );
+}
+
+static int
+tlsg_getfile( const char *path, gnutls_datum_t *buf )
+{
+ int rc = -1, fd;
+ struct stat st;
+ char ebuf[128];
+
+ fd = open( path, O_RDONLY );
+ if ( fd < 0 ) {
+ Debug2( LDAP_DEBUG_ANY,
+ "TLS: opening `%s' failed: %s\n",
+ path,
+ AC_STRERROR_R( errno, ebuf, sizeof ebuf ));
+ return -1;
+ }
+ if ( fstat( fd, &st ) == 0 ) {
+ buf->size = st.st_size;
+ buf->data = LDAP_MALLOC( st.st_size + 1 );
+ if ( buf->data ) {
+ rc = read( fd, buf->data, st.st_size );
+ close( fd );
+ if ( rc < st.st_size )
+ rc = -1;
+ else
+ rc = 0;
+ }
+ }
+ return rc;
+}
+
+/* This is the GnuTLS default */
+#define VERIFY_DEPTH 6
+
+/*
+ * initialize a new TLS context
+ */
+static int
+tlsg_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server )
+{
+ tlsg_ctx *ctx = lo->ldo_tls_ctx;
+ int rc;
+
+ if ( lo->ldo_tls_ciphersuite &&
+ tlsg_parse_ciphers( ctx, lt->lt_ciphersuite )) {
+ Debug1( LDAP_DEBUG_ANY,
+ "TLS: could not set cipher list %s.\n",
+ lo->ldo_tls_ciphersuite );
+ return -1;
+ }
+
+ if (lo->ldo_tls_cacertdir != NULL) {
+ rc = gnutls_certificate_set_x509_trust_dir(
+ ctx->cred,
+ lt->lt_cacertdir,
+ GNUTLS_X509_FMT_PEM );
+ if ( rc > 0 ) {
+ Debug2( LDAP_DEBUG_TRACE,
+ "TLS: loaded %d CA certificates from directory `%s'.\n",
+ rc, lt->lt_cacertdir );
+ } else {
+ Debug1( LDAP_DEBUG_ANY,
+ "TLS: warning: no certificate found in CA certificate directory `%s'.\n",
+ lt->lt_cacertdir );
+ /* only warn, no return */
+ }
+ }
+
+ if (lo->ldo_tls_cacertfile != NULL) {
+ rc = gnutls_certificate_set_x509_trust_file(
+ ctx->cred,
+ lt->lt_cacertfile,
+ GNUTLS_X509_FMT_PEM );
+ if ( rc < 0 ) {
+ Debug3( LDAP_DEBUG_ANY,
+ "TLS: could not use CA certificate file `%s': %s (%d)\n",
+ lo->ldo_tls_cacertfile,
+ gnutls_strerror( rc ),
+ rc );
+ return -1;
+ } else if ( rc == 0 ) {
+ Debug1( LDAP_DEBUG_ANY,
+ "TLS: warning: no certificate loaded from CA certificate file `%s'.\n",
+ lo->ldo_tls_cacertfile );
+ /* only warn, no return */
+ }
+ }
+
+ if (lo->ldo_tls_cacert.bv_val != NULL ) {
+ gnutls_datum_t buf;
+ buf.data = (unsigned char *)lo->ldo_tls_cacert.bv_val;
+ buf.size = lo->ldo_tls_cacert.bv_len;
+ rc = gnutls_certificate_set_x509_trust_mem(
+ ctx->cred,
+ &buf,
+ GNUTLS_X509_FMT_DER );
+ if ( rc < 0 ) {
+ Debug2( LDAP_DEBUG_ANY,
+ "TLS: could not use CA certificate: %s (%d)\n",
+ gnutls_strerror( rc ),
+ rc );
+ return -1;
+ }
+ }
+
+ if (( lo->ldo_tls_certfile && lo->ldo_tls_keyfile ) ||
+ ( lo->ldo_tls_cert.bv_val && lo->ldo_tls_key.bv_val )) {
+ gnutls_x509_privkey_t key;
+ gnutls_datum_t buf;
+ gnutls_x509_crt_t certs[VERIFY_DEPTH];
+ unsigned int max = VERIFY_DEPTH;
+
+ rc = gnutls_x509_privkey_init( &key );
+ if ( rc ) return -1;
+
+ /* OpenSSL builds the cert chain for us, but GnuTLS
+ * expects it to be present in the certfile. If it's
+ * not, we have to build it ourselves. So we have to
+ * do some special checks here...
+ */
+ if ( lo->ldo_tls_key.bv_val ) {
+ buf.data = (unsigned char *)lo->ldo_tls_key.bv_val;
+ buf.size = lo->ldo_tls_key.bv_len;
+ rc = gnutls_x509_privkey_import( key, &buf,
+ GNUTLS_X509_FMT_DER );
+ } else {
+ rc = tlsg_getfile( lt->lt_keyfile, &buf );
+ if ( rc ) {
+ Debug1( LDAP_DEBUG_ANY,
+ "TLS: could not use private key file `%s`.\n",
+ lt->lt_keyfile);
+ return -1;
+ }
+ rc = gnutls_x509_privkey_import( key, &buf,
+ GNUTLS_X509_FMT_PEM );
+ LDAP_FREE( buf.data );
+ }
+ if ( rc < 0 ) {
+ Debug2( LDAP_DEBUG_ANY,
+ "TLS: could not use private key: %s (%d)\n",
+ gnutls_strerror( rc ),
+ rc );
+ return rc;
+ }
+
+ if ( lo->ldo_tls_cert.bv_val ) {
+ buf.data = (unsigned char *)lo->ldo_tls_cert.bv_val;
+ buf.size = lo->ldo_tls_cert.bv_len;
+ rc = gnutls_x509_crt_list_import( certs, &max, &buf,
+ GNUTLS_X509_FMT_DER, 0 );
+ } else {
+ rc = tlsg_getfile( lt->lt_certfile, &buf );
+ if ( rc ) {
+ Debug1( LDAP_DEBUG_ANY,
+ "TLS: could not use certificate file `%s`.\n",
+ lt->lt_certfile);
+ return -1;
+ }
+ rc = gnutls_x509_crt_list_import( certs, &max, &buf,
+ GNUTLS_X509_FMT_PEM, 0 );
+ LDAP_FREE( buf.data );
+ }
+ if ( rc < 0 ) {
+ Debug2( LDAP_DEBUG_ANY,
+ "TLS: could not use certificate: %s (%d)\n",
+ gnutls_strerror( rc ),
+ rc );
+ return rc;
+ }
+
+ /* If there's only one cert and it's not self-signed,
+ * then we have to build the cert chain.
+ */
+ if ( max == 1 && !gnutls_x509_crt_check_issuer( certs[0], certs[0] )) {
+ unsigned int i;
+ for ( i = 1; i<VERIFY_DEPTH; i++ ) {
+ if ( gnutls_certificate_get_issuer( ctx->cred, certs[i-1], &certs[i], 0 ))
+ break;
+ max++;
+ /* If this CA is self-signed, we're done */
+ if ( gnutls_x509_crt_check_issuer( certs[i], certs[i] ))
+ break;
+ }
+ }
+ rc = gnutls_certificate_set_x509_key( ctx->cred, certs, max, key );
+ if ( rc ) {
+ Debug2( LDAP_DEBUG_ANY,
+ "TLS: could not use certificate with key: %s (%d)\n",
+ gnutls_strerror( rc ),
+ rc );
+ return -1;
+ }
+ } else if (( lo->ldo_tls_certfile || lo->ldo_tls_keyfile )) {
+ Debug0( LDAP_DEBUG_ANY,
+ "TLS: only one of certfile and keyfile specified\n" );
+ return -1;
+ } else if (( lo->ldo_tls_cert.bv_val || lo->ldo_tls_key.bv_val )) {
+ Debug0( LDAP_DEBUG_ANY,
+ "TLS: only one of cert and key specified\n" );
+ return -1;
+ }
+
+ if ( lo->ldo_tls_crlfile ) {
+ rc = gnutls_certificate_set_x509_crl_file(
+ ctx->cred,
+ lt->lt_crlfile,
+ GNUTLS_X509_FMT_PEM );
+ if ( rc < 0 ) return -1;
+ rc = 0;
+ }
+
+ /* FIXME: ITS#5992 - this should be configurable,
+ * and V1 CA certs should be phased out ASAP.
+ */
+ gnutls_certificate_set_verify_flags( ctx->cred,
+ GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT );
+
+ if ( is_server && lo->ldo_tls_dhfile ) {
+ gnutls_datum_t buf;
+ rc = tlsg_getfile( lo->ldo_tls_dhfile, &buf );
+ if ( rc ) return -1;
+ rc = gnutls_dh_params_init( &ctx->dh_params );
+ if ( rc == 0 )
+ rc = gnutls_dh_params_import_pkcs3( ctx->dh_params, &buf,
+ GNUTLS_X509_FMT_PEM );
+ LDAP_FREE( buf.data );
+ if ( rc ) return -1;
+ gnutls_certificate_set_dh_params( ctx->cred, ctx->dh_params );
+ }
+
+ ctx->reqcert = lo->ldo_tls_require_cert;
+
+ return 0;
+}
+
+static tls_session *
+tlsg_session_new ( tls_ctx * ctx, int is_server )
+{
+ tlsg_ctx *c = (tlsg_ctx *)ctx;
+ tlsg_session *session;
+
+ session = ber_memcalloc ( 1, sizeof (*session) );
+ if ( !session )
+ return NULL;
+
+ session->ctx = c;
+ gnutls_init( &session->session, is_server ? GNUTLS_SERVER : GNUTLS_CLIENT );
+ gnutls_priority_set( session->session, c->prios );
+ if ( c->cred )
+ gnutls_credentials_set( session->session, GNUTLS_CRD_CERTIFICATE, c->cred );
+
+ if ( is_server ) {
+ int flag = 0;
+ if ( c->reqcert ) {
+ flag = GNUTLS_CERT_REQUEST;
+ if ( c->reqcert == LDAP_OPT_X_TLS_DEMAND ||
+ c->reqcert == LDAP_OPT_X_TLS_HARD )
+ flag = GNUTLS_CERT_REQUIRE;
+ gnutls_certificate_server_set_request( session->session, flag );
+ }
+ }
+ return (tls_session *)session;
+}
+
+static int
+tlsg_session_accept( tls_session *session )
+{
+ tlsg_session *s = (tlsg_session *)session;
+ int rc;
+
+ rc = gnutls_handshake( s->session );
+ if ( rc == 0 && s->ctx->reqcert != LDAP_OPT_X_TLS_NEVER ) {
+ const gnutls_datum_t *peer_cert_list;
+ unsigned int list_size;
+
+ peer_cert_list = gnutls_certificate_get_peers( s->session,
+ &list_size );
+ if ( !peer_cert_list && s->ctx->reqcert == LDAP_OPT_X_TLS_TRY )
+ rc = 0;
+ else {
+ rc = tlsg_cert_verify( s );
+ if ( rc && s->ctx->reqcert == LDAP_OPT_X_TLS_ALLOW )
+ rc = 0;
+ }
+ }
+ return rc;
+}
+
+static int
+tlsg_session_connect( LDAP *ld, tls_session *session, const char *name_in )
+{
+ tlsg_session *s = (tlsg_session *)session;
+ int rc;
+
+ if ( name_in ) {
+ rc = gnutls_server_name_set( s->session, GNUTLS_NAME_DNS, name_in, strlen(name_in) );
+ if ( rc != GNUTLS_E_SUCCESS ) {
+ return rc;
+ }
+ }
+
+ return tlsg_session_accept( session);
+}
+
+static int
+tlsg_session_upflags( Sockbuf *sb, tls_session *session, int rc )
+{
+ tlsg_session *s = (tlsg_session *)session;
+
+ if ( rc != GNUTLS_E_INTERRUPTED && rc != GNUTLS_E_AGAIN )
+ return 0;
+
+ switch (gnutls_record_get_direction (s->session)) {
+ case 0:
+ sb->sb_trans_needs_read = 1;
+ return 1;
+ case 1:
+ sb->sb_trans_needs_write = 1;
+ return 1;
+ }
+ return 0;
+}
+
+static char *
+tlsg_session_errmsg( tls_session *sess, int rc, char *buf, size_t len )
+{
+ return (char *)gnutls_strerror( rc );
+}
+
+static void
+tlsg_x509_cert_dn( struct berval *cert, struct berval *dn, int get_subject )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len;
+ ber_int_t i;
+
+ ber_init2( ber, cert, LBER_USE_DER );
+ tag = ber_skip_tag( ber, &len ); /* Sequence */
+ tag = ber_skip_tag( ber, &len ); /* Sequence */
+ tag = ber_peek_tag( ber, &len ); /* Context + Constructed (version) */
+ if ( tag == 0xa0 ) { /* Version is optional */
+ tag = ber_skip_tag( ber, &len );
+ tag = ber_get_int( ber, &i ); /* Int: Version */
+ }
+ tag = ber_skip_tag( ber, &len ); /* Int: Serial (can be longer than ber_int_t) */
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len ); /* Sequence: Signature */
+ ber_skip_data( ber, len );
+ if ( !get_subject ) {
+ tag = ber_peek_tag( ber, &len ); /* Sequence: Issuer DN */
+ } else {
+ tag = ber_skip_tag( ber, &len );
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len ); /* Sequence: Validity */
+ ber_skip_data( ber, len );
+ tag = ber_peek_tag( ber, &len ); /* Sequence: Subject DN */
+ }
+ len = ber_ptrlen( ber );
+ dn->bv_val = cert->bv_val + len;
+ dn->bv_len = cert->bv_len - len;
+}
+
+static int
+tlsg_session_my_dn( tls_session *session, struct berval *der_dn )
+{
+ tlsg_session *s = (tlsg_session *)session;
+ const gnutls_datum_t *x;
+ struct berval bv;
+
+ x = gnutls_certificate_get_ours( s->session );
+
+ if (!x) return LDAP_INVALID_CREDENTIALS;
+
+ bv.bv_val = (char *) x->data;
+ bv.bv_len = x->size;
+
+ tlsg_x509_cert_dn( &bv, der_dn, 1 );
+ return 0;
+}
+
+static int
+tlsg_session_peer_dn( tls_session *session, struct berval *der_dn )
+{
+ tlsg_session *s = (tlsg_session *)session;
+ if ( !s->peer_der_dn.bv_val ) {
+ const gnutls_datum_t *peer_cert_list;
+ unsigned int list_size;
+ struct berval bv;
+
+ peer_cert_list = gnutls_certificate_get_peers( s->session,
+ &list_size );
+ if ( !peer_cert_list ) return LDAP_INVALID_CREDENTIALS;
+
+ bv.bv_len = peer_cert_list->size;
+ bv.bv_val = (char *) peer_cert_list->data;
+
+ tlsg_x509_cert_dn( &bv, &s->peer_der_dn, 1 );
+ }
+ *der_dn = s->peer_der_dn;
+ return 0;
+}
+
+/* what kind of hostname were we given? */
+#define IS_DNS 0
+#define IS_IP4 1
+#define IS_IP6 2
+
+#define CN_OID "2.5.4.3"
+
+static int
+tlsg_session_chkhost( LDAP *ld, tls_session *session, const char *name_in )
+{
+ tlsg_session *s = (tlsg_session *)session;
+ int i, ret;
+ int chkSAN = ld->ld_options.ldo_tls_require_san, gotSAN = 0;
+ const gnutls_datum_t *peer_cert_list;
+ unsigned int list_size;
+ char altname[NI_MAXHOST];
+ size_t altnamesize;
+
+ gnutls_x509_crt_t cert;
+ const char *name;
+ char *ptr;
+ char *domain = NULL;
+#ifdef LDAP_PF_INET6
+ struct in6_addr addr;
+#else
+ struct in_addr addr;
+#endif
+ int len1 = 0, len2 = 0;
+ int ntype = IS_DNS;
+
+ if( ldap_int_hostname &&
+ ( !name_in || !strcasecmp( name_in, "localhost" ) ) )
+ {
+ name = ldap_int_hostname;
+ } else {
+ name = name_in;
+ }
+
+ peer_cert_list = gnutls_certificate_get_peers( s->session,
+ &list_size );
+ if ( !peer_cert_list ) {
+ Debug0( LDAP_DEBUG_ANY,
+ "TLS: unable to get peer certificate.\n" );
+ /* If this was a fatal condition, things would have
+ * aborted long before now.
+ */
+ return LDAP_SUCCESS;
+ }
+ ret = gnutls_x509_crt_init( &cert );
+ if ( ret < 0 )
+ return LDAP_LOCAL_ERROR;
+ ret = gnutls_x509_crt_import( cert, peer_cert_list, GNUTLS_X509_FMT_DER );
+ if ( ret ) {
+ gnutls_x509_crt_deinit( cert );
+ return LDAP_LOCAL_ERROR;
+ }
+
+#ifdef LDAP_PF_INET6
+ if (inet_pton(AF_INET6, name, &addr)) {
+ ntype = IS_IP6;
+ } else
+#endif
+ if ((ptr = strrchr(name, '.')) && isdigit((unsigned char)ptr[1])) {
+ if (inet_aton(name, (struct in_addr *)&addr)) ntype = IS_IP4;
+ }
+
+ if (ntype == IS_DNS) {
+ len1 = strlen(name);
+ domain = strchr(name, '.');
+ if (domain) {
+ len2 = len1 - (domain-name);
+ }
+ }
+
+ if (chkSAN) {
+ for ( i=0, ret=0; ret >= 0; i++ ) {
+ altnamesize = sizeof(altname);
+ ret = gnutls_x509_crt_get_subject_alt_name( cert, i,
+ altname, &altnamesize, NULL );
+ if ( ret < 0 ) break;
+
+ gotSAN = 1;
+ /* ignore empty */
+ if ( altnamesize == 0 ) continue;
+
+ if ( ret == GNUTLS_SAN_DNSNAME ) {
+ if (ntype != IS_DNS) continue;
+
+ /* Is this an exact match? */
+ if ((len1 == altnamesize) && !strncasecmp(name, altname, len1)) {
+ break;
+ }
+
+ /* Is this a wildcard match? */
+ if (domain && (altname[0] == '*') && (altname[1] == '.') &&
+ (len2 == altnamesize-1) && !strncasecmp(domain, &altname[1], len2))
+ {
+ break;
+ }
+ } else if ( ret == GNUTLS_SAN_IPADDRESS ) {
+ if (ntype == IS_DNS) continue;
+
+#ifdef LDAP_PF_INET6
+ if (ntype == IS_IP6 && altnamesize != sizeof(struct in6_addr)) {
+ continue;
+ } else
+#endif
+ if (ntype == IS_IP4 && altnamesize != sizeof(struct in_addr)) {
+ continue;
+ }
+ if (!memcmp(altname, &addr, altnamesize)) {
+ break;
+ }
+ }
+ }
+ if ( ret >= 0 ) {
+ ret = LDAP_SUCCESS;
+ }
+ }
+ if (ret != LDAP_SUCCESS && chkSAN) {
+ switch(chkSAN) {
+ case LDAP_OPT_X_TLS_DEMAND:
+ case LDAP_OPT_X_TLS_HARD:
+ if (!gotSAN) {
+ Debug0( LDAP_DEBUG_ANY,
+ "TLS: unable to get subjectAltName from peer certificate.\n" );
+ ret = LDAP_CONNECT_ERROR;
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP(
+ _("TLS: unable to get subjectAltName from peer certificate"));
+ goto done;
+ }
+ /* FALLTHRU */
+ case LDAP_OPT_X_TLS_TRY:
+ if (gotSAN) {
+ Debug1( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match "
+ "subjectAltName in certificate.\n",
+ name );
+ ret = LDAP_CONNECT_ERROR;
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP(
+ _("TLS: hostname does not match subjectAltName in peer certificate"));
+ goto done;
+ }
+ break;
+ case LDAP_OPT_X_TLS_ALLOW:
+ break;
+ }
+ }
+
+ if ( ret != LDAP_SUCCESS ){
+ /* find the last CN */
+ i=0;
+ do {
+ altnamesize = 0;
+ ret = gnutls_x509_crt_get_dn_by_oid( cert, CN_OID,
+ i, 1, altname, &altnamesize );
+ if ( ret == GNUTLS_E_SHORT_MEMORY_BUFFER )
+ i++;
+ else
+ break;
+ } while ( 1 );
+
+ if ( i ) {
+ altnamesize = sizeof(altname);
+ ret = gnutls_x509_crt_get_dn_by_oid( cert, CN_OID,
+ i-1, 0, altname, &altnamesize );
+ }
+
+ if ( ret < 0 ) {
+ Debug0( LDAP_DEBUG_ANY,
+ "TLS: unable to get common name from peer certificate.\n" );
+ ret = LDAP_CONNECT_ERROR;
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP(
+ _("TLS: unable to get CN from peer certificate"));
+
+ } else {
+ ret = LDAP_LOCAL_ERROR;
+ if ( !len1 ) len1 = strlen( name );
+ if ( len1 == altnamesize && strncasecmp(name, altname, altnamesize) == 0 ) {
+ ret = LDAP_SUCCESS;
+
+ } else if (( altname[0] == '*' ) && ( altname[1] == '.' )) {
+ /* Is this a wildcard match? */
+ if( domain &&
+ (len2 == altnamesize-1) && !strncasecmp(domain, &altname[1], len2)) {
+ ret = LDAP_SUCCESS;
+ }
+ }
+ }
+
+ if( ret == LDAP_LOCAL_ERROR ) {
+ altname[altnamesize] = '\0';
+ Debug2( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match "
+ "common name in certificate (%s).\n",
+ name, altname );
+ ret = LDAP_CONNECT_ERROR;
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP(
+ _("TLS: hostname does not match name in peer certificate"));
+ }
+ }
+done:
+ gnutls_x509_crt_deinit( cert );
+ return ret;
+}
+
+static int
+tlsg_session_strength( tls_session *session )
+{
+ tlsg_session *s = (tlsg_session *)session;
+ gnutls_cipher_algorithm_t c;
+
+ c = gnutls_cipher_get( s->session );
+ return gnutls_cipher_get_key_size( c ) * 8;
+}
+
+static int
+tlsg_session_unique( tls_session *sess, struct berval *buf, int is_server)
+{
+ tlsg_session *s = (tlsg_session *)sess;
+ gnutls_datum_t cb;
+ int rc;
+
+ rc = gnutls_session_channel_binding( s->session, GNUTLS_CB_TLS_UNIQUE, &cb );
+ if ( rc == 0 ) {
+ int len = cb.size;
+ if ( len > buf->bv_len )
+ len = buf->bv_len;
+ buf->bv_len = len;
+ memcpy( buf->bv_val, cb.data, len );
+ return len;
+ }
+ return 0;
+}
+
+static int
+tlsg_session_endpoint( tls_session *sess, struct berval *buf, int is_server )
+{
+ tlsg_session *s = (tlsg_session *)sess;
+ const gnutls_datum_t *cert_data;
+ gnutls_x509_crt_t server_cert;
+ gnutls_digest_algorithm_t md;
+ int sign_algo, md_len, rc;
+
+ if ( is_server )
+ cert_data = gnutls_certificate_get_ours( s->session );
+ else
+ cert_data = gnutls_certificate_get_peers( s->session, NULL );
+
+ if ( cert_data == NULL )
+ return 0;
+
+ rc = gnutls_x509_crt_init( &server_cert );
+ if ( rc != GNUTLS_E_SUCCESS )
+ return 0;
+
+ rc = gnutls_x509_crt_import( server_cert, cert_data, GNUTLS_X509_FMT_DER );
+ if ( rc != GNUTLS_E_SUCCESS ) {
+ gnutls_x509_crt_deinit( server_cert );
+ return 0;
+ }
+
+ sign_algo = gnutls_x509_crt_get_signature_algorithm( server_cert );
+ gnutls_x509_crt_deinit( server_cert );
+ if ( sign_algo <= GNUTLS_SIGN_UNKNOWN )
+ return 0;
+
+ md = gnutls_sign_get_hash_algorithm( sign_algo );
+ if ( md == GNUTLS_DIG_UNKNOWN )
+ return 0;
+
+ /* See RFC 5929 */
+ switch (md) {
+ case GNUTLS_DIG_NULL:
+ case GNUTLS_DIG_MD2:
+ case GNUTLS_DIG_MD5:
+ case GNUTLS_DIG_SHA1:
+ md = GNUTLS_DIG_SHA256;
+ }
+
+ md_len = gnutls_hash_get_len( md );
+ if ( md_len == 0 || md_len > buf->bv_len )
+ return 0;
+
+ rc = gnutls_hash_fast( md, cert_data->data, cert_data->size, buf->bv_val );
+ if ( rc != GNUTLS_E_SUCCESS )
+ return 0;
+
+ buf->bv_len = md_len;
+
+ return md_len;
+}
+
+static const char *
+tlsg_session_version( tls_session *sess )
+{
+ tlsg_session *s = (tlsg_session *)sess;
+ return gnutls_protocol_get_name(gnutls_protocol_get_version( s->session ));
+}
+
+static const char *
+tlsg_session_cipher( tls_session *sess )
+{
+ tlsg_session *s = (tlsg_session *)sess;
+ return gnutls_cipher_get_name(gnutls_cipher_get( s->session ));
+}
+
+static int
+tlsg_session_peercert( tls_session *sess, struct berval *der )
+{
+ tlsg_session *s = (tlsg_session *)sess;
+ const gnutls_datum_t *peer_cert_list;
+ unsigned int list_size;
+
+ peer_cert_list = gnutls_certificate_get_peers( s->session, &list_size );
+ if (!peer_cert_list)
+ return -1;
+ der->bv_len = peer_cert_list[0].size;
+ der->bv_val = LDAP_MALLOC( der->bv_len );
+ if (!der->bv_val)
+ return -1;
+ memcpy(der->bv_val, peer_cert_list[0].data, der->bv_len);
+ return 0;
+}
+
+static int
+tlsg_session_pinning( LDAP *ld, tls_session *sess, char *hashalg, struct berval *hash )
+{
+ tlsg_session *s = (tlsg_session *)sess;
+ const gnutls_datum_t *cert_list;
+ unsigned int cert_list_size = 0;
+ gnutls_x509_crt_t crt;
+ gnutls_pubkey_t pubkey;
+ gnutls_datum_t key = {};
+ gnutls_digest_algorithm_t alg;
+ struct berval keyhash;
+ size_t len;
+ int rc = -1;
+
+ if ( hashalg ) {
+ alg = gnutls_digest_get_id( hashalg );
+ if ( alg == GNUTLS_DIG_UNKNOWN ) {
+ Debug1( LDAP_DEBUG_ANY, "tlsg_session_pinning: "
+ "unknown hashing algorithm for GnuTLS: '%s'\n",
+ hashalg );
+ return rc;
+ }
+ }
+
+ cert_list = gnutls_certificate_get_peers( s->session, &cert_list_size );
+ if ( cert_list_size == 0 ) {
+ return rc;
+ }
+
+ if ( gnutls_x509_crt_init( &crt ) < 0 ) {
+ return rc;
+ }
+
+ if ( gnutls_x509_crt_import( crt, &cert_list[0], GNUTLS_X509_FMT_DER ) ) {
+ goto done;
+ }
+
+ if ( gnutls_pubkey_init( &pubkey ) ) {
+ goto done;
+ }
+
+ if ( gnutls_pubkey_import_x509( pubkey, crt, 0 ) < 0 ) {
+ goto done;
+ }
+
+ gnutls_pubkey_export( pubkey, GNUTLS_X509_FMT_DER, key.data, &len );
+ if ( len <= 0 ) {
+ goto done;
+ }
+
+ key.data = LDAP_MALLOC( len );
+ if ( !key.data ) {
+ goto done;
+ }
+
+ key.size = len;
+
+ if ( gnutls_pubkey_export( pubkey, GNUTLS_X509_FMT_DER,
+ key.data, &len ) < 0 ) {
+ goto done;
+ }
+
+ if ( hashalg ) {
+ keyhash.bv_len = gnutls_hash_get_len( alg );
+ keyhash.bv_val = LDAP_MALLOC( keyhash.bv_len );
+ if ( !keyhash.bv_val || gnutls_fingerprint( alg, &key,
+ keyhash.bv_val, &keyhash.bv_len ) < 0 ) {
+ goto done;
+ }
+ } else {
+ keyhash.bv_val = (char *)key.data;
+ keyhash.bv_len = key.size;
+ }
+
+ if ( ber_bvcmp( hash, &keyhash ) ) {
+ rc = LDAP_CONNECT_ERROR;
+ Debug0( LDAP_DEBUG_ANY, "tlsg_session_pinning: "
+ "public key hash does not match provided pin.\n" );
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP(
+ _("TLS: public key hash does not match provided pin"));
+ } else {
+ rc = LDAP_SUCCESS;
+ }
+
+done:
+ if ( pubkey ) {
+ gnutls_pubkey_deinit( pubkey );
+ }
+ if ( crt ) {
+ gnutls_x509_crt_deinit( crt );
+ }
+ if ( keyhash.bv_val != (char *)key.data ) {
+ LDAP_FREE( keyhash.bv_val );
+ }
+ if ( key.data ) {
+ LDAP_FREE( key.data );
+ }
+ return rc;
+}
+
+/* suites is a string of colon-separated cipher suite names. */
+static int
+tlsg_parse_ciphers( tlsg_ctx *ctx, char *suites )
+{
+ const char *err;
+ int rc = gnutls_priority_init( &ctx->prios, suites, &err );
+ if ( rc )
+ ctx->prios = NULL;
+ return rc;
+}
+
+/*
+ * TLS support for LBER Sockbufs
+ */
+
+struct tls_data {
+ tlsg_session *session;
+ Sockbuf_IO_Desc *sbiod;
+};
+
+static ssize_t
+tlsg_recv( gnutls_transport_ptr_t ptr, void *buf, size_t len )
+{
+ struct tls_data *p;
+
+ if ( buf == NULL || len <= 0 ) return 0;
+
+ p = (struct tls_data *)ptr;
+
+ if ( p == NULL || p->sbiod == NULL ) {
+ return 0;
+ }
+
+ return LBER_SBIOD_READ_NEXT( p->sbiod, buf, len );
+}
+
+static ssize_t
+tlsg_send( gnutls_transport_ptr_t ptr, const void *buf, size_t len )
+{
+ struct tls_data *p;
+
+ if ( buf == NULL || len <= 0 ) return 0;
+
+ p = (struct tls_data *)ptr;
+
+ if ( p == NULL || p->sbiod == NULL ) {
+ return 0;
+ }
+
+ return LBER_SBIOD_WRITE_NEXT( p->sbiod, (char *)buf, len );
+}
+
+static int
+tlsg_sb_setup( Sockbuf_IO_Desc *sbiod, void *arg )
+{
+ struct tls_data *p;
+ tlsg_session *session = arg;
+
+ assert( sbiod != NULL );
+
+ p = LBER_MALLOC( sizeof( *p ) );
+ if ( p == NULL ) {
+ return -1;
+ }
+
+ gnutls_transport_set_ptr( session->session, (gnutls_transport_ptr_t)p );
+ gnutls_transport_set_pull_function( session->session, tlsg_recv );
+ gnutls_transport_set_push_function( session->session, tlsg_send );
+ p->session = session;
+ p->sbiod = sbiod;
+ sbiod->sbiod_pvt = p;
+ return 0;
+}
+
+static int
+tlsg_sb_remove( Sockbuf_IO_Desc *sbiod )
+{
+ struct tls_data *p;
+
+ assert( sbiod != NULL );
+ assert( sbiod->sbiod_pvt != NULL );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+ gnutls_deinit ( p->session->session );
+ LBER_FREE( p->session );
+ LBER_FREE( sbiod->sbiod_pvt );
+ sbiod->sbiod_pvt = NULL;
+ return 0;
+}
+
+static int
+tlsg_sb_close( Sockbuf_IO_Desc *sbiod )
+{
+ struct tls_data *p;
+
+ assert( sbiod != NULL );
+ assert( sbiod->sbiod_pvt != NULL );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+ gnutls_bye ( p->session->session, GNUTLS_SHUT_WR );
+ return 0;
+}
+
+static int
+tlsg_sb_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
+{
+ struct tls_data *p;
+
+ assert( sbiod != NULL );
+ assert( sbiod->sbiod_pvt != NULL );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+
+ if ( opt == LBER_SB_OPT_GET_SSL ) {
+ *((tlsg_session **)arg) = p->session;
+ return 1;
+
+ } else if ( opt == LBER_SB_OPT_DATA_READY ) {
+ if( gnutls_record_check_pending( p->session->session ) > 0 ) {
+ return 1;
+ }
+ }
+
+ return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg );
+}
+
+static ber_slen_t
+tlsg_sb_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
+{
+ struct tls_data *p;
+ ber_slen_t ret;
+
+ assert( sbiod != NULL );
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+
+ ret = gnutls_record_recv ( p->session->session, buf, len );
+ switch (ret) {
+ case GNUTLS_E_INTERRUPTED:
+ case GNUTLS_E_AGAIN:
+ sbiod->sbiod_sb->sb_trans_needs_read = 1;
+ sock_errset(EWOULDBLOCK);
+ ret = 0;
+ break;
+ case GNUTLS_E_REHANDSHAKE:
+ for ( ret = gnutls_handshake ( p->session->session );
+ ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN;
+ ret = gnutls_handshake ( p->session->session ) );
+ sbiod->sbiod_sb->sb_trans_needs_read = 1;
+ ret = 0;
+ break;
+ default:
+ sbiod->sbiod_sb->sb_trans_needs_read = 0;
+ }
+ return ret;
+}
+
+static ber_slen_t
+tlsg_sb_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
+{
+ struct tls_data *p;
+ ber_slen_t ret;
+
+ assert( sbiod != NULL );
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+
+ ret = gnutls_record_send ( p->session->session, (char *)buf, len );
+
+ if ( ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN ) {
+ sbiod->sbiod_sb->sb_trans_needs_write = 1;
+ sock_errset(EWOULDBLOCK);
+ ret = 0;
+ } else {
+ sbiod->sbiod_sb->sb_trans_needs_write = 0;
+ }
+ return ret;
+}
+
+static Sockbuf_IO tlsg_sbio =
+{
+ tlsg_sb_setup, /* sbi_setup */
+ tlsg_sb_remove, /* sbi_remove */
+ tlsg_sb_ctrl, /* sbi_ctrl */
+ tlsg_sb_read, /* sbi_read */
+ tlsg_sb_write, /* sbi_write */
+ tlsg_sb_close /* sbi_close */
+};
+
+/* Certs are not automatically verified during the handshake */
+static int
+tlsg_cert_verify( tlsg_session *ssl )
+{
+ unsigned int status = 0;
+ int err;
+ time_t now = time(0);
+ time_t peertime;
+
+ err = gnutls_certificate_verify_peers2( ssl->session, &status );
+ if ( err < 0 ) {
+ Debug1( LDAP_DEBUG_ANY,"TLS: gnutls_certificate_verify_peers2 failed %d\n",
+ err );
+ return -1;
+ }
+ if ( status ) {
+ Debug1( LDAP_DEBUG_TRACE,"TLS: peer cert untrusted or revoked (0x%x)\n",
+ status );
+ return -1;
+ }
+ peertime = gnutls_certificate_expiration_time_peers( ssl->session );
+ if ( peertime == (time_t) -1 ) {
+ Debug0( LDAP_DEBUG_ANY, "TLS: gnutls_certificate_expiration_time_peers failed\n" );
+ return -1;
+ }
+ if ( peertime < now ) {
+ Debug0( LDAP_DEBUG_ANY, "TLS: peer certificate is expired\n" );
+ return -1;
+ }
+ peertime = gnutls_certificate_activation_time_peers( ssl->session );
+ if ( peertime == (time_t) -1 ) {
+ Debug0( LDAP_DEBUG_ANY, "TLS: gnutls_certificate_activation_time_peers failed\n" );
+ return -1;
+ }
+ if ( peertime > now ) {
+ Debug0( LDAP_DEBUG_ANY, "TLS: peer certificate not yet active\n" );
+ return -1;
+ }
+ return 0;
+}
+
+tls_impl ldap_int_tls_impl = {
+ "GnuTLS",
+
+ tlsg_init,
+ tlsg_destroy,
+
+ tlsg_ctx_new,
+ tlsg_ctx_ref,
+ tlsg_ctx_free,
+ tlsg_ctx_init,
+
+ tlsg_session_new,
+ tlsg_session_connect,
+ tlsg_session_accept,
+ tlsg_session_upflags,
+ tlsg_session_errmsg,
+ tlsg_session_my_dn,
+ tlsg_session_peer_dn,
+ tlsg_session_chkhost,
+ tlsg_session_strength,
+ tlsg_session_unique,
+ tlsg_session_endpoint,
+ tlsg_session_version,
+ tlsg_session_cipher,
+ tlsg_session_peercert,
+ tlsg_session_pinning,
+
+ &tlsg_sbio,
+
+#ifdef LDAP_R_COMPILE
+ tlsg_thr_init,
+#else
+ NULL,
+#endif
+
+ 0
+};
+
+#endif /* HAVE_GNUTLS */
diff --git a/libraries/libldap/tls_o.c b/libraries/libldap/tls_o.c
new file mode 100644
index 0000000..834d986
--- /dev/null
+++ b/libraries/libldap/tls_o.c
@@ -0,0 +1,1688 @@
+/* tls_o.c - Handle tls/ssl using OpenSSL */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS: Rewritten by Howard Chu
+ */
+
+#include "portable.h"
+
+#ifdef HAVE_OPENSSL
+
+#include "ldap_config.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+#include <ac/param.h>
+#include <ac/dirent.h>
+
+#include "ldap-int.h"
+#include "ldap-tls.h"
+
+#ifdef HAVE_OPENSSL_SSL_H
+#include <openssl/ssl.h>
+#include <openssl/x509v3.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/safestack.h>
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+#include <openssl/dh.h>
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+#define ASN1_STRING_data(x) ASN1_STRING_get0_data(x)
+#endif
+
+typedef SSL_CTX tlso_ctx;
+typedef SSL tlso_session;
+
+static BIO_METHOD * tlso_bio_method = NULL;
+static BIO_METHOD * tlso_bio_setup( void );
+
+static int tlso_opt_trace = 1;
+
+static void tlso_report_error( void );
+
+static void tlso_info_cb( const SSL *ssl, int where, int ret );
+static int tlso_verify_cb( int ok, X509_STORE_CTX *ctx );
+static int tlso_verify_ok( int ok, X509_STORE_CTX *ctx );
+static int tlso_seed_PRNG( const char *randfile );
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+/*
+ * OpenSSL 1.1 API and later has new locking code
+*/
+static RSA * tlso_tmp_rsa_cb( SSL *ssl, int is_export, int key_length );
+
+#ifdef LDAP_R_COMPILE
+/*
+ * provide mutexes for the OpenSSL library.
+ */
+static ldap_pvt_thread_mutex_t tlso_mutexes[CRYPTO_NUM_LOCKS];
+
+static void tlso_locking_cb( int mode, int type, const char *file, int line )
+{
+ if ( mode & CRYPTO_LOCK ) {
+ ldap_pvt_thread_mutex_lock( &tlso_mutexes[type] );
+ } else {
+ ldap_pvt_thread_mutex_unlock( &tlso_mutexes[type] );
+ }
+}
+
+#if OPENSSL_VERSION_NUMBER >= 0x0909000
+static void tlso_thread_self( CRYPTO_THREADID *id )
+{
+ CRYPTO_THREADID_set_pointer( id, (void *)ldap_pvt_thread_self() );
+}
+#define CRYPTO_set_id_callback(foo) CRYPTO_THREADID_set_callback(foo)
+#else
+static unsigned long tlso_thread_self( void )
+{
+ /* FIXME: CRYPTO_set_id_callback only works when ldap_pvt_thread_t
+ * is an integral type that fits in an unsigned long
+ */
+
+ /* force an error if the ldap_pvt_thread_t type is too large */
+ enum { ok = sizeof( ldap_pvt_thread_t ) <= sizeof( unsigned long ) };
+ typedef struct { int dummy: ok ? 1 : -1; } Check[ok ? 1 : -1];
+
+ return (unsigned long) ldap_pvt_thread_self();
+}
+#endif
+
+static void tlso_thr_init( void )
+{
+ int i;
+
+ for( i=0; i< CRYPTO_NUM_LOCKS ; i++ ) {
+ ldap_pvt_thread_mutex_init( &tlso_mutexes[i] );
+ }
+ CRYPTO_set_locking_callback( tlso_locking_cb );
+ CRYPTO_set_id_callback( tlso_thread_self );
+}
+#endif /* LDAP_R_COMPILE */
+#else
+#ifdef LDAP_R_COMPILE
+static void tlso_thr_init( void ) {}
+#endif
+#endif /* OpenSSL 1.1 */
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+/*
+ * OpenSSL 1.1 API and later makes the BIO method concrete types internal.
+ */
+
+static BIO_METHOD *
+BIO_meth_new( int type, const char *name )
+{
+ BIO_METHOD *method = LDAP_MALLOC( sizeof(BIO_METHOD) );
+ memset( method, 0, sizeof(BIO_METHOD) );
+
+ method->type = type;
+ method->name = name;
+
+ return method;
+}
+
+static void
+BIO_meth_free( BIO_METHOD *meth )
+{
+ if ( meth == NULL ) {
+ return;
+ }
+
+ LDAP_FREE( meth );
+}
+
+#define BIO_meth_set_write(m, f) (m)->bwrite = (f)
+#define BIO_meth_set_read(m, f) (m)->bread = (f)
+#define BIO_meth_set_puts(m, f) (m)->bputs = (f)
+#define BIO_meth_set_gets(m, f) (m)->bgets = (f)
+#define BIO_meth_set_ctrl(m, f) (m)->ctrl = (f)
+#define BIO_meth_set_create(m, f) (m)->create = (f)
+#define BIO_meth_set_destroy(m, f) (m)->destroy = (f)
+
+#endif /* OpenSSL 1.1 */
+
+static STACK_OF(X509_NAME) *
+tlso_ca_list( char * bundle, char * dir, X509 *cert )
+{
+ STACK_OF(X509_NAME) *ca_list = NULL;
+
+ if ( bundle ) {
+ ca_list = SSL_load_client_CA_file( bundle );
+ }
+#if defined(HAVE_DIRENT_H) || defined(dirent)
+ if ( dir ) {
+ int freeit = 0;
+
+ if ( !ca_list ) {
+ ca_list = sk_X509_NAME_new_null();
+ freeit = 1;
+ }
+ if ( !SSL_add_dir_cert_subjects_to_stack( ca_list, dir ) &&
+ freeit ) {
+ sk_X509_NAME_free( ca_list );
+ ca_list = NULL;
+ }
+ }
+#endif
+ if ( cert ) {
+ X509_NAME *xn = X509_get_subject_name( cert );
+ xn = X509_NAME_dup( xn );
+ if ( !ca_list )
+ ca_list = sk_X509_NAME_new_null();
+ if ( xn && ca_list )
+ sk_X509_NAME_push( ca_list, xn );
+ }
+ return ca_list;
+}
+
+/*
+ * Initialize TLS subsystem. Should be called only once.
+ */
+static int
+tlso_init( void )
+{
+ struct ldapoptions *lo = LDAP_INT_GLOBAL_OPT();
+#ifdef HAVE_EBCDIC
+ {
+ char *file = LDAP_STRDUP( lo->ldo_tls_randfile );
+ if ( file ) __atoe( file );
+ (void) tlso_seed_PRNG( file );
+ LDAP_FREE( file );
+ }
+#else
+ (void) tlso_seed_PRNG( lo->ldo_tls_randfile );
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+ SSL_load_error_strings();
+ SSL_library_init();
+ OpenSSL_add_all_digests();
+#else
+ OPENSSL_init_ssl(0, NULL);
+#endif
+
+ /* FIXME: mod_ssl does this */
+ X509V3_add_standard_extensions();
+
+ tlso_bio_method = tlso_bio_setup();
+
+ return 0;
+}
+
+/*
+ * Tear down the TLS subsystem. Should only be called once.
+ */
+static void
+tlso_destroy( void )
+{
+ struct ldapoptions *lo = LDAP_INT_GLOBAL_OPT();
+
+ BIO_meth_free( tlso_bio_method );
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+ EVP_cleanup();
+ ERR_remove_thread_state(NULL);
+ ERR_free_strings();
+#endif
+
+ if ( lo->ldo_tls_randfile ) {
+ LDAP_FREE( lo->ldo_tls_randfile );
+ lo->ldo_tls_randfile = NULL;
+ }
+}
+
+static tls_ctx *
+tlso_ctx_new( struct ldapoptions *lo )
+{
+ return (tls_ctx *) SSL_CTX_new( SSLv23_method() );
+}
+
+static void
+tlso_ctx_ref( tls_ctx *ctx )
+{
+ tlso_ctx *c = (tlso_ctx *)ctx;
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+#define SSL_CTX_up_ref(ctx) CRYPTO_add( &(ctx->references), 1, CRYPTO_LOCK_SSL_CTX )
+#endif
+ SSL_CTX_up_ref( c );
+}
+
+static void
+tlso_ctx_free ( tls_ctx *ctx )
+{
+ tlso_ctx *c = (tlso_ctx *)ctx;
+ SSL_CTX_free( c );
+}
+
+#if OPENSSL_VERSION_NUMBER >= 0x10101000
+static char *
+tlso_stecpy( char *dst, const char *src, const char *end )
+{
+ while ( dst < end && *src )
+ *dst++ = *src++;
+ if ( dst < end )
+ *dst = '\0';
+ return dst;
+}
+
+/* OpenSSL 1.1.1 uses a separate API for TLS1.3 ciphersuites.
+ * Try to find any TLS1.3 ciphers in the given list of suites.
+ */
+static void
+tlso_ctx_cipher13( tlso_ctx *ctx, char *suites )
+{
+ char tls13_suites[1024], *ts = tls13_suites, *te = tls13_suites + sizeof(tls13_suites);
+ char *ptr, *colon, *nptr;
+ char sname[128];
+ STACK_OF(SSL_CIPHER) *cs;
+ SSL *s = SSL_new( ctx );
+ int ret;
+
+ if ( !s )
+ return;
+
+ *ts = '\0';
+
+ /* check individual suites in a separate SSL handle before
+ * mucking with the provided ctx. Init it to a known
+ * mostly-empty state.
+ */
+ SSL_set_ciphersuites( s, "" );
+ SSL_set_cipher_list( s, SSL3_TXT_RSA_NULL_SHA );
+
+ for ( ptr = suites;; ) {
+ colon = strchr( ptr, ':' );
+ if ( colon ) {
+ int len = colon - ptr;
+ if ( len > 63 ) len = 63;
+ strncpy( sname, ptr, len );
+ sname[len] = '\0';
+ nptr = sname;
+ } else {
+ nptr = ptr;
+ }
+ if ( SSL_set_ciphersuites( s, nptr )) {
+ cs = SSL_get_ciphers( s );
+ if ( cs ) {
+ const char *ver = SSL_CIPHER_get_version( sk_SSL_CIPHER_value( cs, 0 ));
+ if ( !strncmp( ver, "TLSv", 4 ) && strncmp( ver+4, "1.3", 3 ) >= 0 ) {
+ if ( tls13_suites[0] )
+ ts = tlso_stecpy( ts, ":", te );
+ ts = tlso_stecpy( ts, sname, te );
+ }
+ }
+ }
+ if ( !colon || ts >= te )
+ break;
+ ptr = colon+1;
+ }
+ SSL_free( s );
+
+ /* If no TLS1.3 ciphersuites were specified, leave current settings untouched. */
+ if ( tls13_suites[0] )
+ SSL_CTX_set_ciphersuites( ctx, tls13_suites );
+}
+#endif /* OpenSSL 1.1.1 */
+
+/*
+ * initialize a new TLS context
+ */
+static int
+tlso_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server )
+{
+ tlso_ctx *ctx = (tlso_ctx *)lo->ldo_tls_ctx;
+ int i;
+
+ if ( is_server ) {
+ SSL_CTX_set_session_id_context( ctx,
+ (const unsigned char *) "OpenLDAP", sizeof("OpenLDAP")-1 );
+ }
+
+ if ( lo->ldo_tls_protocol_min ) {
+ int opt = 0;
+ if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_SSL2 ) {
+ opt |= SSL_OP_NO_SSLv2;
+ SSL_CTX_clear_options( ctx, SSL_OP_NO_SSLv3 );
+ }
+ if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_SSL3 )
+ opt |= SSL_OP_NO_SSLv3;
+#ifdef SSL_OP_NO_TLSv1
+ if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_TLS1_0 )
+ opt |= SSL_OP_NO_TLSv1;
+#endif
+#ifdef SSL_OP_NO_TLSv1_1
+ if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_TLS1_1 )
+ opt |= SSL_OP_NO_TLSv1_1;
+#endif
+#ifdef SSL_OP_NO_TLSv1_2
+ if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_TLS1_2 )
+ opt |= SSL_OP_NO_TLSv1_2;
+#endif
+#ifdef SSL_OP_NO_TLSv1_3
+ if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_TLS1_3 )
+ opt |= SSL_OP_NO_TLSv1_3;
+#endif
+ if ( opt )
+ SSL_CTX_set_options( ctx, opt );
+ }
+ if ( lo->ldo_tls_protocol_max ) {
+ int opt = 0;
+#ifdef SSL_OP_NO_TLSv1_3
+ if ( lo->ldo_tls_protocol_max < LDAP_OPT_X_TLS_PROTOCOL_TLS1_3 )
+ opt |= SSL_OP_NO_TLSv1_3;
+#endif
+#ifdef SSL_OP_NO_TLSv1_2
+ if ( lo->ldo_tls_protocol_max < LDAP_OPT_X_TLS_PROTOCOL_TLS1_2 )
+ opt |= SSL_OP_NO_TLSv1_2;
+#endif
+#ifdef SSL_OP_NO_TLSv1_1
+ if ( lo->ldo_tls_protocol_max < LDAP_OPT_X_TLS_PROTOCOL_TLS1_1 )
+ opt |= SSL_OP_NO_TLSv1_1;
+#endif
+#ifdef SSL_OP_NO_TLSv1
+ if ( lo->ldo_tls_protocol_max < LDAP_OPT_X_TLS_PROTOCOL_TLS1_0 )
+ opt |= SSL_OP_NO_TLSv1;
+#endif
+ if ( lo->ldo_tls_protocol_max < LDAP_OPT_X_TLS_PROTOCOL_SSL3 )
+ opt |= SSL_OP_NO_SSLv3;
+ if ( opt )
+ SSL_CTX_set_options( ctx, opt );
+ }
+
+ if ( lo->ldo_tls_ciphersuite ) {
+#if OPENSSL_VERSION_NUMBER >= 0x10101000
+ tlso_ctx_cipher13( ctx, lt->lt_ciphersuite );
+#endif
+ if ( !SSL_CTX_set_cipher_list( ctx, lt->lt_ciphersuite ) )
+ {
+ Debug1( LDAP_DEBUG_ANY,
+ "TLS: could not set cipher list %s.\n",
+ lo->ldo_tls_ciphersuite );
+ tlso_report_error();
+ return -1;
+ }
+ }
+
+ if ( lo->ldo_tls_cacertfile == NULL && lo->ldo_tls_cacertdir == NULL &&
+ lo->ldo_tls_cacert.bv_val == NULL ) {
+ if ( !SSL_CTX_set_default_verify_paths( ctx ) ) {
+ Debug0( LDAP_DEBUG_ANY, "TLS: "
+ "could not use default certificate paths" );
+ tlso_report_error();
+ return -1;
+ }
+ } else {
+ X509 *cert = NULL;
+ if ( lo->ldo_tls_cacert.bv_val ) {
+ const unsigned char *pp = (const unsigned char *) (lo->ldo_tls_cacert.bv_val);
+ cert = d2i_X509( NULL, &pp, lo->ldo_tls_cacert.bv_len );
+ X509_STORE *store = SSL_CTX_get_cert_store( ctx );
+ if ( !X509_STORE_add_cert( store, cert )) {
+ Debug0( LDAP_DEBUG_ANY, "TLS: "
+ "could not use CA certificate" );
+ tlso_report_error();
+ return -1;
+ }
+ }
+ if (( lt->lt_cacertfile || lt->lt_cacertdir ) && !SSL_CTX_load_verify_locations( ctx,
+ lt->lt_cacertfile, lt->lt_cacertdir ) )
+ {
+ Debug2( LDAP_DEBUG_ANY, "TLS: "
+ "could not load verify locations (file:`%s',dir:`%s').\n",
+ lo->ldo_tls_cacertfile ? lo->ldo_tls_cacertfile : "",
+ lo->ldo_tls_cacertdir ? lo->ldo_tls_cacertdir : "" );
+ tlso_report_error();
+ return -1;
+ }
+
+ if ( is_server ) {
+ STACK_OF(X509_NAME) *calist;
+ /* List of CA names to send to a client */
+ calist = tlso_ca_list( lt->lt_cacertfile, lt->lt_cacertdir, cert );
+ if ( !calist ) {
+ Debug2( LDAP_DEBUG_ANY, "TLS: "
+ "could not load client CA list (file:`%s',dir:`%s').\n",
+ lo->ldo_tls_cacertfile ? lo->ldo_tls_cacertfile : "",
+ lo->ldo_tls_cacertdir ? lo->ldo_tls_cacertdir : "" );
+ tlso_report_error();
+ return -1;
+ }
+
+ SSL_CTX_set_client_CA_list( ctx, calist );
+ }
+ if ( cert )
+ X509_free( cert );
+ }
+
+ if ( lo->ldo_tls_cert.bv_val )
+ {
+ const unsigned char *pp = (const unsigned char *) (lo->ldo_tls_cert.bv_val);
+ X509 *cert = d2i_X509( NULL, &pp, lo->ldo_tls_cert.bv_len );
+ if ( !SSL_CTX_use_certificate( ctx, cert )) {
+ Debug0( LDAP_DEBUG_ANY,
+ "TLS: could not use certificate.\n" );
+ tlso_report_error();
+ return -1;
+ }
+ X509_free( cert );
+ } else
+ if ( lo->ldo_tls_certfile &&
+ !SSL_CTX_use_certificate_chain_file( ctx, lt->lt_certfile) )
+ {
+ Debug1( LDAP_DEBUG_ANY,
+ "TLS: could not use certificate file `%s'.\n",
+ lo->ldo_tls_certfile );
+ tlso_report_error();
+ return -1;
+ }
+
+ /* Key validity is checked automatically if cert has already been set */
+ if ( lo->ldo_tls_key.bv_val )
+ {
+ const unsigned char *pp = (const unsigned char *) (lo->ldo_tls_key.bv_val);
+ EVP_PKEY *pkey = d2i_AutoPrivateKey( NULL, &pp, lo->ldo_tls_key.bv_len );
+ if ( !SSL_CTX_use_PrivateKey( ctx, pkey ))
+ {
+ Debug0( LDAP_DEBUG_ANY,
+ "TLS: could not use private key.\n" );
+ tlso_report_error();
+ return -1;
+ }
+ EVP_PKEY_free( pkey );
+ } else
+ if ( lo->ldo_tls_keyfile &&
+ !SSL_CTX_use_PrivateKey_file( ctx,
+ lt->lt_keyfile, SSL_FILETYPE_PEM ) )
+ {
+ Debug1( LDAP_DEBUG_ANY,
+ "TLS: could not use key file `%s'.\n",
+ lo->ldo_tls_keyfile );
+ tlso_report_error();
+ return -1;
+ }
+
+ if ( is_server && lo->ldo_tls_dhfile ) {
+ DH *dh;
+ BIO *bio;
+
+ if (( bio=BIO_new_file( lt->lt_dhfile,"r" )) == NULL ) {
+ Debug1( LDAP_DEBUG_ANY,
+ "TLS: could not use DH parameters file `%s'.\n",
+ lo->ldo_tls_dhfile );
+ tlso_report_error();
+ return -1;
+ }
+ if (!( dh=PEM_read_bio_DHparams( bio, NULL, NULL, NULL ))) {
+ Debug1( LDAP_DEBUG_ANY,
+ "TLS: could not read DH parameters file `%s'.\n",
+ lo->ldo_tls_dhfile );
+ tlso_report_error();
+ BIO_free( bio );
+ return -1;
+ }
+ BIO_free( bio );
+ SSL_CTX_set_tmp_dh( ctx, dh );
+ SSL_CTX_set_options( ctx, SSL_OP_SINGLE_DH_USE );
+ DH_free( dh );
+ }
+
+ if ( lo->ldo_tls_ecname ) {
+#ifdef OPENSSL_NO_EC
+ Debug0( LDAP_DEBUG_ANY,
+ "TLS: Elliptic Curves not supported.\n" );
+ return -1;
+#else
+ if ( !SSL_CTX_set1_curves_list( ctx, lt->lt_ecname )) {
+ Debug1( LDAP_DEBUG_ANY,
+ "TLS: could not set EC name `%s'.\n",
+ lo->ldo_tls_ecname );
+ tlso_report_error();
+ return -1;
+ }
+ /*
+ * This is a NOP in OpenSSL 1.1.0 and later, where curves are always
+ * auto-negotiated.
+ */
+#if OPENSSL_VERSION_NUMBER < 0x10100000UL
+ if ( SSL_CTX_set_ecdh_auto( ctx, 1 ) <= 0 ) {
+ Debug0( LDAP_DEBUG_ANY,
+ "TLS: could not enable automatic EC negotiation.\n" );
+ }
+#endif
+#endif /* OPENSSL_NO_EC */
+ }
+
+ if ( tlso_opt_trace ) {
+ SSL_CTX_set_info_callback( ctx, tlso_info_cb );
+ }
+
+ i = SSL_VERIFY_NONE;
+ if ( lo->ldo_tls_require_cert ) {
+ i = SSL_VERIFY_PEER;
+ if ( lo->ldo_tls_require_cert == LDAP_OPT_X_TLS_DEMAND ||
+ lo->ldo_tls_require_cert == LDAP_OPT_X_TLS_HARD ) {
+ i |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+ }
+ }
+
+ SSL_CTX_set_verify( ctx, i,
+ lo->ldo_tls_require_cert == LDAP_OPT_X_TLS_ALLOW ?
+ tlso_verify_ok : tlso_verify_cb );
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+ SSL_CTX_set_tmp_rsa_callback( ctx, tlso_tmp_rsa_cb );
+#endif
+ if ( lo->ldo_tls_crlcheck ) {
+ X509_STORE *x509_s = SSL_CTX_get_cert_store( ctx );
+ if ( lo->ldo_tls_crlcheck == LDAP_OPT_X_TLS_CRL_PEER ) {
+ X509_STORE_set_flags( x509_s, X509_V_FLAG_CRL_CHECK );
+ } else if ( lo->ldo_tls_crlcheck == LDAP_OPT_X_TLS_CRL_ALL ) {
+ X509_STORE_set_flags( x509_s,
+ X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL );
+ }
+ }
+ /* Explicitly honor the server side cipher suite preference */
+ SSL_CTX_set_options( ctx, SSL_OP_CIPHER_SERVER_PREFERENCE );
+ return 0;
+}
+
+static tls_session *
+tlso_session_new( tls_ctx *ctx, int is_server )
+{
+ tlso_ctx *c = (tlso_ctx *)ctx;
+ return (tls_session *)SSL_new( c );
+}
+
+static int
+tlso_session_connect( LDAP *ld, tls_session *sess, const char *name_in )
+{
+ tlso_session *s = (tlso_session *)sess;
+ int rc;
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+ if ( name_in ) {
+ rc = SSL_set_tlsext_host_name( s, name_in );
+ if ( !rc ) /* can fail to strdup the name */
+ return -1;
+ }
+#endif
+ /* Caller expects 0 = success, OpenSSL returns 1 = success */
+ rc = SSL_connect( s ) - 1;
+ return rc;
+}
+
+static int
+tlso_session_accept( tls_session *sess )
+{
+ tlso_session *s = (tlso_session *)sess;
+
+ /* Caller expects 0 = success, OpenSSL returns 1 = success */
+ return SSL_accept( s ) - 1;
+}
+
+static int
+tlso_session_upflags( Sockbuf *sb, tls_session *sess, int rc )
+{
+ tlso_session *s = (tlso_session *)sess;
+
+ /* 1 was subtracted above, offset it back now */
+ rc = SSL_get_error(s, rc+1);
+ if (rc == SSL_ERROR_WANT_READ) {
+ sb->sb_trans_needs_read = 1;
+ return 1;
+
+ } else if (rc == SSL_ERROR_WANT_WRITE) {
+ sb->sb_trans_needs_write = 1;
+ return 1;
+
+ } else if (rc == SSL_ERROR_WANT_CONNECT) {
+ return 1;
+ }
+ return 0;
+}
+
+static char *
+tlso_session_errmsg( tls_session *sess, int rc, char *buf, size_t len )
+{
+ char err[256] = "";
+ const char *certerr=NULL;
+ tlso_session *s = (tlso_session *)sess;
+
+ rc = ERR_peek_error();
+ if ( rc ) {
+ ERR_error_string_n( rc, err, sizeof(err) );
+ if ( ( ERR_GET_LIB(rc) == ERR_LIB_SSL ) &&
+ ( ERR_GET_REASON(rc) == SSL_R_CERTIFICATE_VERIFY_FAILED ) ) {
+ int certrc = SSL_get_verify_result(s);
+ certerr = (char *)X509_verify_cert_error_string(certrc);
+ }
+ snprintf(buf, len, "%s%s%s%s", err, certerr ? " (" :"",
+ certerr ? certerr : "", certerr ? ")" : "" );
+ return buf;
+ }
+ return NULL;
+}
+
+static int
+tlso_session_my_dn( tls_session *sess, struct berval *der_dn )
+{
+ tlso_session *s = (tlso_session *)sess;
+ X509 *x;
+ X509_NAME *xn;
+
+ x = SSL_get_certificate( s );
+
+ if (!x) return LDAP_INVALID_CREDENTIALS;
+
+ xn = X509_get_subject_name(x);
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+ der_dn->bv_len = i2d_X509_NAME( xn, NULL );
+ der_dn->bv_val = xn->bytes->data;
+#else
+ {
+ size_t len = 0;
+ der_dn->bv_val = NULL;
+ X509_NAME_get0_der( xn, (const unsigned char **)&der_dn->bv_val, &len );
+ der_dn->bv_len = len;
+ }
+#endif
+ /* Don't X509_free, the session is still using it */
+ return 0;
+}
+
+static X509 *
+tlso_get_cert( SSL *s )
+{
+ /* If peer cert was bad, treat as if no cert was given */
+ if (SSL_get_verify_result(s)) {
+ return NULL;
+ }
+ return SSL_get_peer_certificate(s);
+}
+
+static int
+tlso_session_peer_dn( tls_session *sess, struct berval *der_dn )
+{
+ tlso_session *s = (tlso_session *)sess;
+ X509 *x = tlso_get_cert( s );
+ X509_NAME *xn;
+
+ if ( !x )
+ return LDAP_INVALID_CREDENTIALS;
+
+ xn = X509_get_subject_name(x);
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+ der_dn->bv_len = i2d_X509_NAME( xn, NULL );
+ der_dn->bv_val = xn->bytes->data;
+#else
+ {
+ size_t len = 0;
+ der_dn->bv_val = NULL;
+ X509_NAME_get0_der( xn, (const unsigned char **)&der_dn->bv_val, &len );
+ der_dn->bv_len = len;
+ }
+#endif
+ X509_free(x);
+ return 0;
+}
+
+/* what kind of hostname were we given? */
+#define IS_DNS 0
+#define IS_IP4 1
+#define IS_IP6 2
+
+static int
+tlso_session_chkhost( LDAP *ld, tls_session *sess, const char *name_in )
+{
+ tlso_session *s = (tlso_session *)sess;
+ int i, ret = LDAP_LOCAL_ERROR;
+ int chkSAN = ld->ld_options.ldo_tls_require_san, gotSAN = 0;
+ X509 *x;
+ const char *name;
+ char *ptr;
+ int ntype = IS_DNS, nlen;
+#ifdef LDAP_PF_INET6
+ struct in6_addr addr;
+#else
+ struct in_addr addr;
+#endif
+
+ if( ldap_int_hostname &&
+ ( !name_in || !strcasecmp( name_in, "localhost" ) ) )
+ {
+ name = ldap_int_hostname;
+ } else {
+ name = name_in;
+ }
+ nlen = strlen(name);
+
+ x = tlso_get_cert(s);
+ if (!x) {
+ Debug0( LDAP_DEBUG_ANY,
+ "TLS: unable to get peer certificate.\n" );
+ /* If this was a fatal condition, things would have
+ * aborted long before now.
+ */
+ return LDAP_SUCCESS;
+ }
+
+#ifdef LDAP_PF_INET6
+ if (inet_pton(AF_INET6, name, &addr)) {
+ ntype = IS_IP6;
+ } else
+#endif
+ if ((ptr = strrchr(name, '.')) && isdigit((unsigned char)ptr[1])) {
+ if (inet_aton(name, (struct in_addr *)&addr)) ntype = IS_IP4;
+ }
+
+ if (chkSAN) {
+ i = X509_get_ext_by_NID(x, NID_subject_alt_name, -1);
+ if (i >= 0) {
+ X509_EXTENSION *ex;
+ STACK_OF(GENERAL_NAME) *alt;
+
+ ex = X509_get_ext(x, i);
+ alt = X509V3_EXT_d2i(ex);
+ if (alt) {
+ int n, len2 = 0;
+ char *domain = NULL;
+ GENERAL_NAME *gn;
+
+ gotSAN = 1;
+ if (ntype == IS_DNS) {
+ domain = strchr(name, '.');
+ if (domain) {
+ len2 = nlen - (domain-name);
+ }
+ }
+ n = sk_GENERAL_NAME_num(alt);
+ for (i=0; i<n; i++) {
+ char *sn;
+ int sl;
+ gn = sk_GENERAL_NAME_value(alt, i);
+ if (gn->type == GEN_DNS) {
+ if (ntype != IS_DNS) continue;
+
+ sn = (char *) ASN1_STRING_data(gn->d.ia5);
+ sl = ASN1_STRING_length(gn->d.ia5);
+
+ /* ignore empty */
+ if (sl == 0) continue;
+
+ /* Is this an exact match? */
+ if ((nlen == sl) && !strncasecmp(name, sn, nlen)) {
+ break;
+ }
+
+ /* Is this a wildcard match? */
+ if (domain && (sn[0] == '*') && (sn[1] == '.') &&
+ (len2 == sl-1) && !strncasecmp(domain, &sn[1], len2))
+ {
+ break;
+ }
+
+ } else if (gn->type == GEN_IPADD) {
+ if (ntype == IS_DNS) continue;
+
+ sn = (char *) ASN1_STRING_data(gn->d.ia5);
+ sl = ASN1_STRING_length(gn->d.ia5);
+
+#ifdef LDAP_PF_INET6
+ if (ntype == IS_IP6 && sl != sizeof(struct in6_addr)) {
+ continue;
+ } else
+#endif
+ if (ntype == IS_IP4 && sl != sizeof(struct in_addr)) {
+ continue;
+ }
+ if (!memcmp(sn, &addr, sl)) {
+ break;
+ }
+ }
+ }
+
+ GENERAL_NAMES_free(alt);
+ if (i < n) { /* Found a match */
+ ret = LDAP_SUCCESS;
+ }
+ }
+ }
+ }
+ if (ret != LDAP_SUCCESS && chkSAN) {
+ switch(chkSAN) {
+ case LDAP_OPT_X_TLS_DEMAND:
+ case LDAP_OPT_X_TLS_HARD:
+ if (!gotSAN) {
+ Debug0( LDAP_DEBUG_ANY,
+ "TLS: unable to get subjectAltName from peer certificate.\n" );
+ ret = LDAP_CONNECT_ERROR;
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP(
+ _("TLS: unable to get subjectAltName from peer certificate"));
+ goto done;
+ }
+ /* FALLTHRU */
+ case LDAP_OPT_X_TLS_TRY:
+ if (gotSAN) {
+ Debug1( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match "
+ "subjectAltName in certificate.\n",
+ name );
+ ret = LDAP_CONNECT_ERROR;
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP(
+ _("TLS: hostname does not match subjectAltName in peer certificate"));
+ goto done;
+ }
+ break;
+ case LDAP_OPT_X_TLS_ALLOW:
+ break;
+ }
+ }
+
+ if (ret != LDAP_SUCCESS) {
+ X509_NAME *xn;
+ X509_NAME_ENTRY *ne;
+ ASN1_OBJECT *obj;
+ ASN1_STRING *cn = NULL;
+ int navas;
+
+ /* find the last CN */
+ obj = OBJ_nid2obj( NID_commonName );
+ if ( !obj ) goto no_cn; /* should never happen */
+
+ xn = X509_get_subject_name(x);
+ navas = X509_NAME_entry_count( xn );
+ for ( i=navas-1; i>=0; i-- ) {
+ ne = X509_NAME_get_entry( xn, i );
+ if ( !OBJ_cmp( X509_NAME_ENTRY_get_object(ne), obj )) {
+ cn = X509_NAME_ENTRY_get_data( ne );
+ break;
+ }
+ }
+
+ if( !cn )
+ {
+no_cn:
+ Debug0( LDAP_DEBUG_ANY,
+ "TLS: unable to get common name from peer certificate.\n" );
+ ret = LDAP_CONNECT_ERROR;
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP(
+ _("TLS: unable to get CN from peer certificate"));
+
+ } else if ( cn->length == nlen &&
+ strncasecmp( name, (char *) cn->data, nlen ) == 0 ) {
+ ret = LDAP_SUCCESS;
+
+ } else if (( cn->data[0] == '*' ) && ( cn->data[1] == '.' )) {
+ char *domain = strchr(name, '.');
+ if( domain ) {
+ int dlen;
+
+ dlen = nlen - (domain-name);
+
+ /* Is this a wildcard match? */
+ if ((dlen == cn->length-1) &&
+ !strncasecmp(domain, (char *) &cn->data[1], dlen)) {
+ ret = LDAP_SUCCESS;
+ }
+ }
+ }
+
+ if( ret == LDAP_LOCAL_ERROR ) {
+ Debug3( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match "
+ "common name in certificate (%.*s).\n",
+ name, cn->length, cn->data );
+ ret = LDAP_CONNECT_ERROR;
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP(
+ _("TLS: hostname does not match name in peer certificate"));
+ }
+ }
+done:
+ X509_free(x);
+ return ret;
+}
+
+static int
+tlso_session_strength( tls_session *sess )
+{
+ tlso_session *s = (tlso_session *)sess;
+
+ return SSL_CIPHER_get_bits(SSL_get_current_cipher(s), NULL);
+}
+
+static int
+tlso_session_unique( tls_session *sess, struct berval *buf, int is_server)
+{
+ tlso_session *s = (tlso_session *)sess;
+
+ /* Usually the client sends the finished msg. But if the
+ * session was resumed, the server sent the msg.
+ */
+ if (SSL_session_reused(s) ^ !is_server)
+ buf->bv_len = SSL_get_finished(s, buf->bv_val, buf->bv_len);
+ else
+ buf->bv_len = SSL_get_peer_finished(s, buf->bv_val, buf->bv_len);
+ return buf->bv_len;
+}
+
+static int
+tlso_session_endpoint( tls_session *sess, struct berval *buf, int is_server )
+{
+ tlso_session *s = (tlso_session *)sess;
+ const EVP_MD *md;
+ unsigned int md_len;
+ X509 *cert;
+
+ if ( buf->bv_len < EVP_MAX_MD_SIZE )
+ return 0;
+
+ if ( is_server )
+ cert = SSL_get_certificate( s );
+ else
+ cert = SSL_get_peer_certificate( s );
+
+ if ( cert == NULL )
+ return 0;
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+ md = EVP_get_digestbynid( X509_get_signature_nid( cert ));
+#else
+ md = EVP_get_digestbynid(OBJ_obj2nid( cert->sig_alg->algorithm ));
+#endif
+
+ /* See RFC 5929 */
+ if ( md == NULL ||
+ md == EVP_md_null() ||
+#ifndef OPENSSL_NO_MD2
+ md == EVP_md2() ||
+#endif
+#ifndef OPENSSL_NO_MD4
+ md == EVP_md4() ||
+#endif
+#ifndef OPENSSL_NO_MD5
+ md == EVP_md5() ||
+#endif
+ md == EVP_sha1() )
+ md = EVP_sha256();
+
+ if ( !X509_digest( cert, md, (unsigned char *) (buf->bv_val), &md_len ))
+ md_len = 0;
+
+ buf->bv_len = md_len;
+ if ( !is_server )
+ X509_free( cert );
+
+ return md_len;
+}
+
+static const char *
+tlso_session_version( tls_session *sess )
+{
+ tlso_session *s = (tlso_session *)sess;
+ return SSL_get_version(s);
+}
+
+static const char *
+tlso_session_cipher( tls_session *sess )
+{
+ tlso_session *s = (tlso_session *)sess;
+ return SSL_CIPHER_get_name(SSL_get_current_cipher(s));
+}
+
+static int
+tlso_session_peercert( tls_session *sess, struct berval *der )
+{
+ tlso_session *s = (tlso_session *)sess;
+ int ret = -1;
+ X509 *x = SSL_get_peer_certificate(s);
+ if ( x ) {
+ der->bv_len = i2d_X509(x, NULL);
+ der->bv_val = LDAP_MALLOC(der->bv_len);
+ if ( der->bv_val ) {
+ unsigned char *ptr = (unsigned char *) (der->bv_val);
+ i2d_X509(x, &ptr);
+ ret = 0;
+ }
+ X509_free( x );
+ }
+ return ret;
+}
+
+static int
+tlso_session_pinning( LDAP *ld, tls_session *sess, char *hashalg, struct berval *hash )
+{
+ tlso_session *s = (tlso_session *)sess;
+ unsigned char *tmp, digest[EVP_MAX_MD_SIZE];
+ struct berval key,
+ keyhash = { sizeof(digest), (char *) digest };
+ X509 *cert = SSL_get_peer_certificate(s);
+ int len, rc = LDAP_SUCCESS;
+
+ if ( !cert )
+ return -1;
+
+ len = i2d_X509_PUBKEY( X509_get_X509_PUBKEY(cert), NULL );
+
+ tmp = LDAP_MALLOC( len );
+ key.bv_val = (char *) tmp;
+
+ if ( !key.bv_val ) {
+ rc = -1;
+ goto done;
+ }
+
+ key.bv_len = i2d_X509_PUBKEY( X509_get_X509_PUBKEY(cert), &tmp );
+
+ if ( hashalg ) {
+ const EVP_MD *md;
+ EVP_MD_CTX *mdctx;
+ unsigned int len = keyhash.bv_len;
+
+ md = EVP_get_digestbyname( hashalg );
+ if ( !md ) {
+ Debug1( LDAP_DEBUG_TRACE, "tlso_session_pinning: "
+ "hash %s not recognised by OpenSSL\n", hashalg );
+ rc = -1;
+ goto done;
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+ mdctx = EVP_MD_CTX_new();
+#else
+ mdctx = EVP_MD_CTX_create();
+#endif
+ if ( !mdctx ) {
+ rc = -1;
+ goto done;
+ }
+
+ EVP_DigestInit_ex( mdctx, md, NULL );
+ EVP_DigestUpdate( mdctx, key.bv_val, key.bv_len );
+ EVP_DigestFinal_ex( mdctx, (unsigned char *)keyhash.bv_val, &len );
+ keyhash.bv_len = len;
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+ EVP_MD_CTX_free( mdctx );
+#else
+ EVP_MD_CTX_destroy( mdctx );
+#endif
+ } else {
+ keyhash = key;
+ }
+
+ if ( ber_bvcmp( hash, &keyhash ) ) {
+ rc = LDAP_CONNECT_ERROR;
+ Debug0( LDAP_DEBUG_ANY, "tlso_session_pinning: "
+ "public key hash does not match provided pin.\n" );
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP(
+ _("TLS: public key hash does not match provided pin"));
+ }
+
+done:
+ LDAP_FREE( key.bv_val );
+ X509_free( cert );
+ return rc;
+}
+
+/*
+ * TLS support for LBER Sockbufs
+ */
+
+struct tls_data {
+ tlso_session *session;
+ Sockbuf_IO_Desc *sbiod;
+};
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+#define BIO_set_init(b, x) b->init = x
+#define BIO_set_data(b, x) b->ptr = x
+#define BIO_clear_flags(b, x) b->flags &= ~(x)
+#define BIO_get_data(b) b->ptr
+#endif
+static int
+tlso_bio_create( BIO *b ) {
+ BIO_set_init( b, 1 );
+ BIO_set_data( b, NULL );
+ BIO_clear_flags( b, ~0 );
+ return 1;
+}
+
+static int
+tlso_bio_destroy( BIO *b )
+{
+ if ( b == NULL ) return 0;
+
+ BIO_set_data( b, NULL ); /* sb_tls_remove() will free it */
+ BIO_set_init( b, 0 );
+ BIO_clear_flags( b, ~0 );
+ return 1;
+}
+
+static int
+tlso_bio_read( BIO *b, char *buf, int len )
+{
+ struct tls_data *p;
+ int ret;
+
+ if ( buf == NULL || len <= 0 ) return 0;
+
+ p = (struct tls_data *)BIO_get_data(b);
+
+ if ( p == NULL || p->sbiod == NULL ) {
+ return 0;
+ }
+
+ ret = LBER_SBIOD_READ_NEXT( p->sbiod, buf, len );
+
+ BIO_clear_retry_flags( b );
+ if ( ret < 0 ) {
+ int err = sock_errno();
+ if ( err == EAGAIN || err == EWOULDBLOCK ) {
+ BIO_set_retry_read( b );
+ }
+ }
+
+ return ret;
+}
+
+static int
+tlso_bio_write( BIO *b, const char *buf, int len )
+{
+ struct tls_data *p;
+ int ret;
+
+ if ( buf == NULL || len <= 0 ) return 0;
+
+ p = (struct tls_data *)BIO_get_data(b);
+
+ if ( p == NULL || p->sbiod == NULL ) {
+ return 0;
+ }
+
+ ret = LBER_SBIOD_WRITE_NEXT( p->sbiod, (char *)buf, len );
+
+ BIO_clear_retry_flags( b );
+ if ( ret < 0 ) {
+ int err = sock_errno();
+ if ( err == EAGAIN || err == EWOULDBLOCK ) {
+ BIO_set_retry_write( b );
+ }
+ }
+
+ return ret;
+}
+
+static long
+tlso_bio_ctrl( BIO *b, int cmd, long num, void *ptr )
+{
+ if ( cmd == BIO_CTRL_FLUSH ) {
+ /* The OpenSSL library needs this */
+ return 1;
+ }
+ return 0;
+}
+
+static int
+tlso_bio_gets( BIO *b, char *buf, int len )
+{
+ return -1;
+}
+
+static int
+tlso_bio_puts( BIO *b, const char *str )
+{
+ return tlso_bio_write( b, str, strlen( str ) );
+}
+
+static BIO_METHOD *
+tlso_bio_setup( void )
+{
+ /* it's a source/sink BIO */
+ BIO_METHOD * method = BIO_meth_new( 100 | 0x400, "sockbuf glue" );
+ BIO_meth_set_write( method, tlso_bio_write );
+ BIO_meth_set_read( method, tlso_bio_read );
+ BIO_meth_set_puts( method, tlso_bio_puts );
+ BIO_meth_set_gets( method, tlso_bio_gets );
+ BIO_meth_set_ctrl( method, tlso_bio_ctrl );
+ BIO_meth_set_create( method, tlso_bio_create );
+ BIO_meth_set_destroy( method, tlso_bio_destroy );
+
+ return method;
+}
+
+static int
+tlso_sb_setup( Sockbuf_IO_Desc *sbiod, void *arg )
+{
+ struct tls_data *p;
+ BIO *bio;
+
+ assert( sbiod != NULL );
+
+ p = LBER_MALLOC( sizeof( *p ) );
+ if ( p == NULL ) {
+ return -1;
+ }
+
+ p->session = arg;
+ p->sbiod = sbiod;
+ bio = BIO_new( tlso_bio_method );
+ BIO_set_data( bio, p );
+ SSL_set_bio( p->session, bio, bio );
+ sbiod->sbiod_pvt = p;
+ return 0;
+}
+
+static int
+tlso_sb_remove( Sockbuf_IO_Desc *sbiod )
+{
+ struct tls_data *p;
+
+ assert( sbiod != NULL );
+ assert( sbiod->sbiod_pvt != NULL );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+ SSL_free( p->session );
+ LBER_FREE( sbiod->sbiod_pvt );
+ sbiod->sbiod_pvt = NULL;
+ return 0;
+}
+
+static int
+tlso_sb_close( Sockbuf_IO_Desc *sbiod )
+{
+ struct tls_data *p;
+
+ assert( sbiod != NULL );
+ assert( sbiod->sbiod_pvt != NULL );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+ SSL_shutdown( p->session );
+ return 0;
+}
+
+static int
+tlso_sb_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
+{
+ struct tls_data *p;
+
+ assert( sbiod != NULL );
+ assert( sbiod->sbiod_pvt != NULL );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+
+ if ( opt == LBER_SB_OPT_GET_SSL ) {
+ *((tlso_session **)arg) = p->session;
+ return 1;
+
+ } else if ( opt == LBER_SB_OPT_DATA_READY ) {
+ if( SSL_pending( p->session ) > 0 ) {
+ return 1;
+ }
+ }
+
+ return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg );
+}
+
+static ber_slen_t
+tlso_sb_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
+{
+ struct tls_data *p;
+ ber_slen_t ret;
+ int err;
+
+ assert( sbiod != NULL );
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+
+ ret = SSL_read( p->session, (char *)buf, len );
+#ifdef HAVE_WINSOCK
+ errno = WSAGetLastError();
+#endif
+ err = SSL_get_error( p->session, ret );
+ if (err == SSL_ERROR_WANT_READ ) {
+ sbiod->sbiod_sb->sb_trans_needs_read = 1;
+ sock_errset(EWOULDBLOCK);
+ }
+ else
+ sbiod->sbiod_sb->sb_trans_needs_read = 0;
+ return ret;
+}
+
+static ber_slen_t
+tlso_sb_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
+{
+ struct tls_data *p;
+ ber_slen_t ret;
+ int err;
+
+ assert( sbiod != NULL );
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+
+ ret = SSL_write( p->session, (char *)buf, len );
+#ifdef HAVE_WINSOCK
+ errno = WSAGetLastError();
+#endif
+ err = SSL_get_error( p->session, ret );
+ if (err == SSL_ERROR_WANT_WRITE ) {
+ sbiod->sbiod_sb->sb_trans_needs_write = 1;
+ sock_errset(EWOULDBLOCK);
+
+ } else {
+ sbiod->sbiod_sb->sb_trans_needs_write = 0;
+ }
+ return ret;
+}
+
+static Sockbuf_IO tlso_sbio =
+{
+ tlso_sb_setup, /* sbi_setup */
+ tlso_sb_remove, /* sbi_remove */
+ tlso_sb_ctrl, /* sbi_ctrl */
+ tlso_sb_read, /* sbi_read */
+ tlso_sb_write, /* sbi_write */
+ tlso_sb_close /* sbi_close */
+};
+
+/* Derived from openssl/apps/s_cb.c */
+static void
+tlso_info_cb( const SSL *ssl, int where, int ret )
+{
+ int w;
+ char *op;
+ char *state = (char *) SSL_state_string_long( (SSL *)ssl );
+
+ w = where & ~SSL_ST_MASK;
+ if ( w & SSL_ST_CONNECT ) {
+ op = "SSL_connect";
+ } else if ( w & SSL_ST_ACCEPT ) {
+ op = "SSL_accept";
+ } else {
+ op = "undefined";
+ }
+
+#ifdef HAVE_EBCDIC
+ if ( state ) {
+ state = LDAP_STRDUP( state );
+ __etoa( state );
+ }
+#endif
+ if ( where & SSL_CB_LOOP ) {
+ Debug2( LDAP_DEBUG_TRACE,
+ "TLS trace: %s:%s\n",
+ op, state );
+
+ } else if ( where & SSL_CB_ALERT ) {
+ char *atype = (char *) SSL_alert_type_string_long( ret );
+ char *adesc = (char *) SSL_alert_desc_string_long( ret );
+ op = ( where & SSL_CB_READ ) ? "read" : "write";
+#ifdef HAVE_EBCDIC
+ if ( atype ) {
+ atype = LDAP_STRDUP( atype );
+ __etoa( atype );
+ }
+ if ( adesc ) {
+ adesc = LDAP_STRDUP( adesc );
+ __etoa( adesc );
+ }
+#endif
+ Debug3( LDAP_DEBUG_TRACE,
+ "TLS trace: SSL3 alert %s:%s:%s\n",
+ op, atype, adesc );
+#ifdef HAVE_EBCDIC
+ if ( atype ) LDAP_FREE( atype );
+ if ( adesc ) LDAP_FREE( adesc );
+#endif
+ } else if ( where & SSL_CB_EXIT ) {
+ if ( ret == 0 ) {
+ Debug2( LDAP_DEBUG_TRACE,
+ "TLS trace: %s:failed in %s\n",
+ op, state );
+ } else if ( ret < 0 ) {
+ Debug2( LDAP_DEBUG_TRACE,
+ "TLS trace: %s:error in %s\n",
+ op, state );
+ }
+ }
+#ifdef HAVE_EBCDIC
+ if ( state ) LDAP_FREE( state );
+#endif
+}
+
+static int
+tlso_verify_cb( int ok, X509_STORE_CTX *ctx )
+{
+ X509 *cert;
+ int errnum;
+ int errdepth;
+ X509_NAME *subject;
+ X509_NAME *issuer;
+ char *sname;
+ char *iname;
+ char *certerr = NULL;
+
+ cert = X509_STORE_CTX_get_current_cert( ctx );
+ errnum = X509_STORE_CTX_get_error( ctx );
+ errdepth = X509_STORE_CTX_get_error_depth( ctx );
+
+ /*
+ * X509_get_*_name return pointers to the internal copies of
+ * those things requested. So do not free them.
+ */
+ subject = X509_get_subject_name( cert );
+ issuer = X509_get_issuer_name( cert );
+ /* X509_NAME_oneline, if passed a NULL buf, allocate memory */
+ sname = X509_NAME_oneline( subject, NULL, 0 );
+ iname = X509_NAME_oneline( issuer, NULL, 0 );
+ if ( !ok ) certerr = (char *)X509_verify_cert_error_string( errnum );
+#ifdef HAVE_EBCDIC
+ if ( sname ) __etoa( sname );
+ if ( iname ) __etoa( iname );
+ if ( certerr ) {
+ certerr = LDAP_STRDUP( certerr );
+ __etoa( certerr );
+ }
+#endif
+ Debug3( LDAP_DEBUG_TRACE,
+ "TLS certificate verification: depth: %d, err: %d, subject: %s,",
+ errdepth, errnum,
+ sname ? sname : "-unknown-" );
+ Debug1( LDAP_DEBUG_TRACE, " issuer: %s\n", iname ? iname : "-unknown-" );
+ if ( !ok ) {
+ Debug1( LDAP_DEBUG_ANY,
+ "TLS certificate verification: Error, %s\n",
+ certerr );
+ }
+ if ( sname )
+ OPENSSL_free ( sname );
+ if ( iname )
+ OPENSSL_free ( iname );
+#ifdef HAVE_EBCDIC
+ if ( certerr ) LDAP_FREE( certerr );
+#endif
+ return ok;
+}
+
+static int
+tlso_verify_ok( int ok, X509_STORE_CTX *ctx )
+{
+ (void) tlso_verify_cb( ok, ctx );
+ return 1;
+}
+
+/* Inspired by ERR_print_errors in OpenSSL */
+static void
+tlso_report_error( void )
+{
+ unsigned long l;
+ char buf[200];
+ const char *file;
+ int line;
+
+ while ( ( l = ERR_get_error_line( &file, &line ) ) != 0 ) {
+ ERR_error_string_n( l, buf, sizeof( buf ) );
+#ifdef HAVE_EBCDIC
+ if ( file ) {
+ file = LDAP_STRDUP( file );
+ __etoa( (char *)file );
+ }
+ __etoa( buf );
+#endif
+ Debug3( LDAP_DEBUG_ANY, "TLS: %s %s:%d\n",
+ buf, file, line );
+#ifdef HAVE_EBCDIC
+ if ( file ) LDAP_FREE( (void *)file );
+#endif
+ }
+}
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+static RSA *
+tlso_tmp_rsa_cb( SSL *ssl, int is_export, int key_length )
+{
+ RSA *tmp_rsa;
+ /* FIXME: Pregenerate the key on startup */
+ /* FIXME: Who frees the key? */
+ BIGNUM *bn = BN_new();
+ tmp_rsa = NULL;
+ if ( bn ) {
+ if ( BN_set_word( bn, RSA_F4 )) {
+ tmp_rsa = RSA_new();
+ if ( tmp_rsa && !RSA_generate_key_ex( tmp_rsa, key_length, bn, NULL )) {
+ RSA_free( tmp_rsa );
+ tmp_rsa = NULL;
+ }
+ }
+ BN_free( bn );
+ }
+
+ if ( !tmp_rsa ) {
+ Debug2( LDAP_DEBUG_ANY,
+ "TLS: Failed to generate temporary %d-bit %s RSA key\n",
+ key_length, is_export ? "export" : "domestic" );
+ }
+ return tmp_rsa;
+}
+#endif /* OPENSSL_VERSION_NUMBER < 1.1 */
+
+static int
+tlso_seed_PRNG( const char *randfile )
+{
+#ifndef URANDOM_DEVICE
+ /* no /dev/urandom (or equiv) */
+ long total=0;
+ char buffer[MAXPATHLEN];
+
+ if (randfile == NULL) {
+ /* The seed file is $RANDFILE if defined, otherwise $HOME/.rnd.
+ * If $HOME is not set or buffer too small to hold the pathname,
+ * an error occurs. - From RAND_file_name() man page.
+ * The fact is that when $HOME is NULL, .rnd is used.
+ */
+ randfile = RAND_file_name( buffer, sizeof( buffer ) );
+ }
+#ifndef OPENSSL_NO_EGD
+ else if (RAND_egd(randfile) > 0) {
+ /* EGD socket */
+ return 0;
+ }
+#endif
+
+ if (randfile == NULL) {
+ Debug0( LDAP_DEBUG_ANY,
+ "TLS: Use configuration file or $RANDFILE to define seed PRNG\n" );
+ return -1;
+ }
+
+ total = RAND_load_file(randfile, -1);
+
+ if (RAND_status() == 0) {
+ Debug0( LDAP_DEBUG_ANY,
+ "TLS: PRNG not been seeded with enough data\n" );
+ return -1;
+ }
+
+ /* assume if there was enough bits to seed that it's okay
+ * to write derived bits to the file
+ */
+ RAND_write_file(randfile);
+
+#endif
+
+ return 0;
+}
+
+
+tls_impl ldap_int_tls_impl = {
+ "OpenSSL",
+
+ tlso_init,
+ tlso_destroy,
+
+ tlso_ctx_new,
+ tlso_ctx_ref,
+ tlso_ctx_free,
+ tlso_ctx_init,
+
+ tlso_session_new,
+ tlso_session_connect,
+ tlso_session_accept,
+ tlso_session_upflags,
+ tlso_session_errmsg,
+ tlso_session_my_dn,
+ tlso_session_peer_dn,
+ tlso_session_chkhost,
+ tlso_session_strength,
+ tlso_session_unique,
+ tlso_session_endpoint,
+ tlso_session_version,
+ tlso_session_cipher,
+ tlso_session_peercert,
+ tlso_session_pinning,
+
+ &tlso_sbio,
+
+#ifdef LDAP_R_COMPILE
+ tlso_thr_init,
+#else
+ NULL,
+#endif
+
+ 0
+};
+
+#endif /* HAVE_OPENSSL */
diff --git a/libraries/libldap/tpool.c b/libraries/libldap/tpool.c
new file mode 100644
index 0000000..797d59e
--- /dev/null
+++ b/libraries/libldap/tpool.c
@@ -0,0 +1,1474 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/signal.h>
+#include <ac/stdarg.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/errno.h>
+
+#include "ldap-int.h"
+
+#ifdef LDAP_R_COMPILE
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#include "ldap_queue.h"
+#define LDAP_THREAD_POOL_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename symbols defined below */
+
+#ifndef LDAP_THREAD_HAVE_TPOOL
+
+#ifndef CACHELINE
+#define CACHELINE 64
+#endif
+
+/* Thread-specific key with data and optional free function */
+typedef struct ldap_int_tpool_key_s {
+ void *ltk_key;
+ void *ltk_data;
+ ldap_pvt_thread_pool_keyfree_t *ltk_free;
+} ldap_int_tpool_key_t;
+
+/* Max number of thread-specific keys we store per thread.
+ * We don't expect to use many...
+ */
+#define MAXKEYS 32
+
+/* Max number of threads */
+#define LDAP_MAXTHR 1024 /* must be a power of 2 */
+
+/* (Theoretical) max number of pending requests */
+#define MAX_PENDING (INT_MAX/2) /* INT_MAX - (room to avoid overflow) */
+
+/* pool->ltp_pause values */
+enum { NOT_PAUSED = 0, WANT_PAUSE = 1, PAUSED = 2 };
+
+/* Context: thread ID and thread-specific key/data pairs */
+typedef struct ldap_int_thread_userctx_s {
+ struct ldap_int_thread_poolq_s *ltu_pq;
+ ldap_pvt_thread_t ltu_id;
+ ldap_int_tpool_key_t ltu_key[MAXKEYS];
+} ldap_int_thread_userctx_t;
+
+
+/* Simple {thread ID -> context} hash table; key=ctx->ltu_id.
+ * Protected by ldap_pvt_thread_pool_mutex.
+ */
+static struct {
+ ldap_int_thread_userctx_t *ctx;
+ /* ctx is valid when not NULL or DELETED_THREAD_CTX */
+# define DELETED_THREAD_CTX (&ldap_int_main_thrctx + 1) /* dummy addr */
+} thread_keys[LDAP_MAXTHR];
+
+#define TID_HASH(tid, hash) do { \
+ unsigned const char *ptr_ = (unsigned const char *)&(tid); \
+ unsigned i_; \
+ for (i_ = 0, (hash) = ptr_[0]; ++i_ < sizeof(tid);) \
+ (hash) += ((hash) << 5) ^ ptr_[i_]; \
+} while(0)
+
+
+/* Task for a thread to perform */
+typedef struct ldap_int_thread_task_s {
+ union {
+ LDAP_STAILQ_ENTRY(ldap_int_thread_task_s) q;
+ LDAP_SLIST_ENTRY(ldap_int_thread_task_s) l;
+ } ltt_next;
+ ldap_pvt_thread_start_t *ltt_start_routine;
+ void *ltt_arg;
+ struct ldap_int_thread_poolq_s *ltt_queue;
+} ldap_int_thread_task_t;
+
+typedef LDAP_STAILQ_HEAD(tcq, ldap_int_thread_task_s) ldap_int_tpool_plist_t;
+
+struct ldap_int_thread_poolq_s {
+ void *ltp_free;
+
+ struct ldap_int_thread_pool_s *ltp_pool;
+
+ /* protect members below */
+ ldap_pvt_thread_mutex_t ltp_mutex;
+
+ /* not paused and something to do for pool_<wrapper/pause/destroy>()
+ * Used for normal pool operation, to synch between submitter and
+ * worker threads. Not used for pauses. In normal operation multiple
+ * queues can rendezvous without acquiring the main pool lock.
+ */
+ ldap_pvt_thread_cond_t ltp_cond;
+
+ /* ltp_pause == 0 ? &ltp_pending_list : &empty_pending_list,
+ * maintained to reduce work for pool_wrapper()
+ */
+ ldap_int_tpool_plist_t *ltp_work_list;
+
+ /* pending tasks, and unused task objects */
+ ldap_int_tpool_plist_t ltp_pending_list;
+ LDAP_SLIST_HEAD(tcl, ldap_int_thread_task_s) ltp_free_list;
+
+ /* Max number of threads in this queue */
+ int ltp_max_count;
+
+ /* Max pending + paused + idle tasks, negated when ltp_finishing */
+ int ltp_max_pending;
+
+ int ltp_pending_count; /* Pending + paused + idle tasks */
+ int ltp_active_count; /* Active, not paused/idle tasks */
+ int ltp_open_count; /* Number of threads */
+ int ltp_starting; /* Currently starting threads */
+};
+
+struct ldap_int_thread_pool_s {
+ LDAP_STAILQ_ENTRY(ldap_int_thread_pool_s) ltp_next;
+
+ struct ldap_int_thread_poolq_s **ltp_wqs;
+
+ /* number of poolqs */
+ int ltp_numqs;
+
+ /* protect members below */
+ ldap_pvt_thread_mutex_t ltp_mutex;
+
+ /* paused and waiting for resume
+ * When a pause is in effect all workers switch to waiting on
+ * this cond instead of their per-queue cond.
+ */
+ ldap_pvt_thread_cond_t ltp_cond;
+
+ /* ltp_active_queues < 1 && ltp_pause */
+ ldap_pvt_thread_cond_t ltp_pcond;
+
+ /* number of active queues */
+ int ltp_active_queues;
+
+ /* The pool is finishing, waiting for its threads to close.
+ * They close when ltp_pending_list is done. pool_submit()
+ * rejects new tasks. ltp_max_pending = -(its old value).
+ */
+ int ltp_finishing;
+
+ /* Some active task needs to be the sole active task.
+ * Atomic variable so ldap_pvt_thread_pool_pausing() can read it.
+ */
+ volatile sig_atomic_t ltp_pause;
+
+ /* Max number of threads in pool */
+ int ltp_max_count;
+
+ /* Configured max number of threads in pool, 0 for default (LDAP_MAXTHR) */
+ int ltp_conf_max_count;
+
+ /* Max pending + paused + idle tasks, negated when ltp_finishing */
+ int ltp_max_pending;
+};
+
+static ldap_int_tpool_plist_t empty_pending_list =
+ LDAP_STAILQ_HEAD_INITIALIZER(empty_pending_list);
+
+static int ldap_int_has_thread_pool = 0;
+static LDAP_STAILQ_HEAD(tpq, ldap_int_thread_pool_s)
+ ldap_int_thread_pool_list =
+ LDAP_STAILQ_HEAD_INITIALIZER(ldap_int_thread_pool_list);
+
+static ldap_pvt_thread_mutex_t ldap_pvt_thread_pool_mutex;
+
+static void *ldap_int_thread_pool_wrapper( void *pool );
+
+static ldap_pvt_thread_key_t ldap_tpool_key;
+
+/* Context of the main thread */
+static ldap_int_thread_userctx_t ldap_int_main_thrctx;
+
+int
+ldap_int_thread_pool_startup ( void )
+{
+ ldap_int_main_thrctx.ltu_id = ldap_pvt_thread_self();
+ ldap_pvt_thread_key_create( &ldap_tpool_key );
+ return ldap_pvt_thread_mutex_init(&ldap_pvt_thread_pool_mutex);
+}
+
+int
+ldap_int_thread_pool_shutdown ( void )
+{
+ struct ldap_int_thread_pool_s *pool;
+
+ while ((pool = LDAP_STAILQ_FIRST(&ldap_int_thread_pool_list)) != NULL) {
+ (ldap_pvt_thread_pool_destroy)(&pool, 0); /* ignore thr_debug macro */
+ }
+ ldap_pvt_thread_mutex_destroy(&ldap_pvt_thread_pool_mutex);
+ ldap_pvt_thread_key_destroy( ldap_tpool_key );
+ return(0);
+}
+
+
+/* Create a thread pool */
+int
+ldap_pvt_thread_pool_init_q (
+ ldap_pvt_thread_pool_t *tpool,
+ int max_threads,
+ int max_pending,
+ int numqs )
+{
+ ldap_pvt_thread_pool_t pool;
+ struct ldap_int_thread_poolq_s *pq;
+ int i, rc, rem_thr, rem_pend;
+
+ /* multiple pools are currently not supported (ITS#4943) */
+ assert(!ldap_int_has_thread_pool);
+
+ if (! (0 <= max_threads && max_threads <= LDAP_MAXTHR))
+ max_threads = 0;
+ if (! (1 <= max_pending && max_pending <= MAX_PENDING))
+ max_pending = MAX_PENDING;
+
+ *tpool = NULL;
+ pool = (ldap_pvt_thread_pool_t) LDAP_CALLOC(1,
+ sizeof(struct ldap_int_thread_pool_s));
+
+ if (pool == NULL) return(-1);
+
+ pool->ltp_wqs = LDAP_MALLOC(numqs * sizeof(struct ldap_int_thread_poolq_s *));
+ if (pool->ltp_wqs == NULL) {
+ LDAP_FREE(pool);
+ return(-1);
+ }
+
+ for (i=0; i<numqs; i++) {
+ char *ptr = LDAP_CALLOC(1, sizeof(struct ldap_int_thread_poolq_s) + CACHELINE-1);
+ if (ptr == NULL) {
+ for (--i; i>=0; i--)
+ LDAP_FREE(pool->ltp_wqs[i]->ltp_free);
+ LDAP_FREE(pool->ltp_wqs);
+ LDAP_FREE(pool);
+ return(-1);
+ }
+ pool->ltp_wqs[i] = (struct ldap_int_thread_poolq_s *)(((size_t)ptr + CACHELINE-1) & ~(CACHELINE-1));
+ pool->ltp_wqs[i]->ltp_free = ptr;
+ }
+
+ pool->ltp_numqs = numqs;
+ pool->ltp_conf_max_count = max_threads;
+ if ( !max_threads )
+ max_threads = LDAP_MAXTHR;
+
+ rc = ldap_pvt_thread_mutex_init(&pool->ltp_mutex);
+ if (rc != 0) {
+fail:
+ for (i=0; i<numqs; i++)
+ LDAP_FREE(pool->ltp_wqs[i]->ltp_free);
+ LDAP_FREE(pool->ltp_wqs);
+ LDAP_FREE(pool);
+ return(rc);
+ }
+
+ rc = ldap_pvt_thread_cond_init(&pool->ltp_cond);
+ if (rc != 0)
+ goto fail;
+
+ rc = ldap_pvt_thread_cond_init(&pool->ltp_pcond);
+ if (rc != 0)
+ goto fail;
+
+ rem_thr = max_threads % numqs;
+ rem_pend = max_pending % numqs;
+ for ( i=0; i<numqs; i++ ) {
+ pq = pool->ltp_wqs[i];
+ pq->ltp_pool = pool;
+ rc = ldap_pvt_thread_mutex_init(&pq->ltp_mutex);
+ if (rc != 0)
+ return(rc);
+ rc = ldap_pvt_thread_cond_init(&pq->ltp_cond);
+ if (rc != 0)
+ return(rc);
+ LDAP_STAILQ_INIT(&pq->ltp_pending_list);
+ pq->ltp_work_list = &pq->ltp_pending_list;
+ LDAP_SLIST_INIT(&pq->ltp_free_list);
+
+ pq->ltp_max_count = max_threads / numqs;
+ if ( rem_thr ) {
+ pq->ltp_max_count++;
+ rem_thr--;
+ }
+ pq->ltp_max_pending = max_pending / numqs;
+ if ( rem_pend ) {
+ pq->ltp_max_pending++;
+ rem_pend--;
+ }
+ }
+
+ ldap_int_has_thread_pool = 1;
+
+ pool->ltp_max_count = max_threads;
+ pool->ltp_max_pending = max_pending;
+
+ ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
+ LDAP_STAILQ_INSERT_TAIL(&ldap_int_thread_pool_list, pool, ltp_next);
+ ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
+
+ /* Start no threads just yet. That can break if the process forks
+ * later, as slapd does in order to daemonize. On at least POSIX,
+ * only the forking thread would survive in the child. Yet fork()
+ * can't unlock/clean up other threads' locks and data structures,
+ * unless pthread_atfork() handlers have been set up to do so.
+ */
+
+ *tpool = pool;
+ return(0);
+}
+
+int
+ldap_pvt_thread_pool_init (
+ ldap_pvt_thread_pool_t *tpool,
+ int max_threads,
+ int max_pending )
+{
+ return ldap_pvt_thread_pool_init_q( tpool, max_threads, max_pending, 1 );
+}
+
+/* Submit a task to be performed by the thread pool */
+int
+ldap_pvt_thread_pool_submit (
+ ldap_pvt_thread_pool_t *tpool,
+ ldap_pvt_thread_start_t *start_routine, void *arg )
+{
+ return ldap_pvt_thread_pool_submit2( tpool, start_routine, arg, NULL );
+}
+
+/* Submit a task to be performed by the thread pool */
+int
+ldap_pvt_thread_pool_submit2 (
+ ldap_pvt_thread_pool_t *tpool,
+ ldap_pvt_thread_start_t *start_routine, void *arg,
+ void **cookie )
+{
+ struct ldap_int_thread_pool_s *pool;
+ struct ldap_int_thread_poolq_s *pq;
+ ldap_int_thread_task_t *task;
+ ldap_pvt_thread_t thr;
+ int i, j;
+
+ if (tpool == NULL)
+ return(-1);
+
+ pool = *tpool;
+
+ if (pool == NULL)
+ return(-1);
+
+ if ( pool->ltp_numqs > 1 ) {
+ int min = pool->ltp_wqs[0]->ltp_max_pending + pool->ltp_wqs[0]->ltp_max_count;
+ int min_x = 0, cnt;
+ for ( i = 0; i < pool->ltp_numqs; i++ ) {
+ /* take first queue that has nothing active */
+ if ( !pool->ltp_wqs[i]->ltp_active_count ) {
+ min_x = i;
+ break;
+ }
+ cnt = pool->ltp_wqs[i]->ltp_active_count + pool->ltp_wqs[i]->ltp_pending_count;
+ if ( cnt < min ) {
+ min = cnt;
+ min_x = i;
+ }
+ }
+ i = min_x;
+ } else
+ i = 0;
+
+ j = i;
+ while(1) {
+ ldap_pvt_thread_mutex_lock(&pool->ltp_wqs[i]->ltp_mutex);
+ if (pool->ltp_wqs[i]->ltp_pending_count < pool->ltp_wqs[i]->ltp_max_pending) {
+ break;
+ }
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_wqs[i]->ltp_mutex);
+ i++;
+ i %= pool->ltp_numqs;
+ if ( i == j )
+ return -1;
+ }
+
+ pq = pool->ltp_wqs[i];
+ task = LDAP_SLIST_FIRST(&pq->ltp_free_list);
+ if (task) {
+ LDAP_SLIST_REMOVE_HEAD(&pq->ltp_free_list, ltt_next.l);
+ } else {
+ task = (ldap_int_thread_task_t *) LDAP_MALLOC(sizeof(*task));
+ if (task == NULL)
+ goto failed;
+ }
+
+ task->ltt_start_routine = start_routine;
+ task->ltt_arg = arg;
+ task->ltt_queue = pq;
+ if ( cookie )
+ *cookie = task;
+
+ pq->ltp_pending_count++;
+ LDAP_STAILQ_INSERT_TAIL(&pq->ltp_pending_list, task, ltt_next.q);
+
+ if (pool->ltp_pause)
+ goto done;
+
+ /* should we open (create) a thread? */
+ if (pq->ltp_open_count < pq->ltp_active_count+pq->ltp_pending_count &&
+ pq->ltp_open_count < pq->ltp_max_count)
+ {
+ pq->ltp_starting++;
+ pq->ltp_open_count++;
+
+ if (0 != ldap_pvt_thread_create(
+ &thr, 1, ldap_int_thread_pool_wrapper, pq))
+ {
+ /* couldn't create thread. back out of
+ * ltp_open_count and check for even worse things.
+ */
+ pq->ltp_starting--;
+ pq->ltp_open_count--;
+
+ if (pq->ltp_open_count == 0) {
+ /* no open threads at all?!?
+ */
+ ldap_int_thread_task_t *ptr;
+
+ /* let pool_close know there are no more threads */
+ ldap_pvt_thread_cond_signal(&pq->ltp_cond);
+
+ LDAP_STAILQ_FOREACH(ptr, &pq->ltp_pending_list, ltt_next.q)
+ if (ptr == task) break;
+ if (ptr == task) {
+ /* no open threads, task not handled, so
+ * back out of ltp_pending_count, free the task,
+ * report the error.
+ */
+ pq->ltp_pending_count--;
+ LDAP_STAILQ_REMOVE(&pq->ltp_pending_list, task,
+ ldap_int_thread_task_s, ltt_next.q);
+ LDAP_SLIST_INSERT_HEAD(&pq->ltp_free_list, task,
+ ltt_next.l);
+ goto failed;
+ }
+ }
+ /* there is another open thread, so this
+ * task will be handled eventually.
+ */
+ }
+ }
+ ldap_pvt_thread_cond_signal(&pq->ltp_cond);
+
+ done:
+ ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex);
+ return(0);
+
+ failed:
+ ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex);
+ return(-1);
+}
+
+static void *
+no_task( void *ctx, void *arg )
+{
+ return NULL;
+}
+
+/* Cancel a pending task that was previously submitted.
+ * Return 1 if the task was successfully cancelled, 0 if
+ * not found, -1 for invalid parameters
+ */
+int
+ldap_pvt_thread_pool_retract (
+ void *cookie )
+{
+ ldap_int_thread_task_t *task, *ttmp;
+ struct ldap_int_thread_poolq_s *pq;
+
+ if (cookie == NULL)
+ return(-1);
+
+ ttmp = cookie;
+ pq = ttmp->ltt_queue;
+ if (pq == NULL)
+ return(-1);
+
+ ldap_pvt_thread_mutex_lock(&pq->ltp_mutex);
+ LDAP_STAILQ_FOREACH(task, &pq->ltp_pending_list, ltt_next.q)
+ if (task == ttmp) {
+ /* Could LDAP_STAILQ_REMOVE the task, but that
+ * walks ltp_pending_list again to find it.
+ */
+ task->ltt_start_routine = no_task;
+ task->ltt_arg = NULL;
+ break;
+ }
+ ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex);
+ return task != NULL;
+}
+
+/* Walk the pool and allow tasks to be retracted, only to be called while the
+ * pool is paused */
+int
+ldap_pvt_thread_pool_walk(
+ ldap_pvt_thread_pool_t *tpool,
+ ldap_pvt_thread_start_t *start,
+ ldap_pvt_thread_walk_t *cb, void *arg )
+{
+ struct ldap_int_thread_pool_s *pool;
+ struct ldap_int_thread_poolq_s *pq;
+ ldap_int_thread_task_t *task;
+ int i;
+
+ if (tpool == NULL)
+ return(-1);
+
+ pool = *tpool;
+
+ if (pool == NULL)
+ return(-1);
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+ assert(pool->ltp_pause == PAUSED);
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+
+ for (i=0; i<pool->ltp_numqs; i++) {
+ pq = pool->ltp_wqs[i];
+ LDAP_STAILQ_FOREACH(task, &pq->ltp_pending_list, ltt_next.q) {
+ if ( task->ltt_start_routine == start ) {
+ if ( cb( task->ltt_start_routine, task->ltt_arg, arg ) ) {
+ /* retract */
+ task->ltt_start_routine = no_task;
+ task->ltt_arg = NULL;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/* Set number of work queues in this pool. Should not be
+ * more than the number of CPUs. */
+int
+ldap_pvt_thread_pool_queues(
+ ldap_pvt_thread_pool_t *tpool,
+ int numqs )
+{
+ struct ldap_int_thread_pool_s *pool;
+ struct ldap_int_thread_poolq_s *pq;
+ int i, rc, rem_thr, rem_pend;
+
+ if (numqs < 1 || tpool == NULL)
+ return(-1);
+
+ pool = *tpool;
+
+ if (pool == NULL)
+ return(-1);
+
+ if (numqs < pool->ltp_numqs) {
+ for (i=numqs; i<pool->ltp_numqs; i++)
+ pool->ltp_wqs[i]->ltp_max_count = 0;
+ } else if (numqs > pool->ltp_numqs) {
+ struct ldap_int_thread_poolq_s **wqs;
+ wqs = LDAP_REALLOC(pool->ltp_wqs, numqs * sizeof(struct ldap_int_thread_poolq_s *));
+ if (wqs == NULL)
+ return(-1);
+ pool->ltp_wqs = wqs;
+ for (i=pool->ltp_numqs; i<numqs; i++) {
+ char *ptr = LDAP_CALLOC(1, sizeof(struct ldap_int_thread_poolq_s) + CACHELINE-1);
+ if (ptr == NULL) {
+ for (; i<numqs; i++)
+ pool->ltp_wqs[i] = NULL;
+ return(-1);
+ }
+ pq = (struct ldap_int_thread_poolq_s *)(((size_t)ptr + CACHELINE-1) & ~(CACHELINE-1));
+ pq->ltp_free = ptr;
+ pool->ltp_wqs[i] = pq;
+ pq->ltp_pool = pool;
+ rc = ldap_pvt_thread_mutex_init(&pq->ltp_mutex);
+ if (rc != 0)
+ return(rc);
+ rc = ldap_pvt_thread_cond_init(&pq->ltp_cond);
+ if (rc != 0)
+ return(rc);
+ LDAP_STAILQ_INIT(&pq->ltp_pending_list);
+ pq->ltp_work_list = &pq->ltp_pending_list;
+ LDAP_SLIST_INIT(&pq->ltp_free_list);
+ }
+ }
+ rem_thr = pool->ltp_max_count % numqs;
+ rem_pend = pool->ltp_max_pending % numqs;
+ for ( i=0; i<numqs; i++ ) {
+ pq = pool->ltp_wqs[i];
+ pq->ltp_max_count = pool->ltp_max_count / numqs;
+ if ( rem_thr ) {
+ pq->ltp_max_count++;
+ rem_thr--;
+ }
+ pq->ltp_max_pending = pool->ltp_max_pending / numqs;
+ if ( rem_pend ) {
+ pq->ltp_max_pending++;
+ rem_pend--;
+ }
+ }
+ pool->ltp_numqs = numqs;
+ return 0;
+}
+
+/* Set max #threads. value <= 0 means max supported #threads (LDAP_MAXTHR) */
+int
+ldap_pvt_thread_pool_maxthreads(
+ ldap_pvt_thread_pool_t *tpool,
+ int max_threads )
+{
+ struct ldap_int_thread_pool_s *pool;
+ struct ldap_int_thread_poolq_s *pq;
+ int remthr, i;
+
+ if (! (0 <= max_threads && max_threads <= LDAP_MAXTHR))
+ max_threads = 0;
+
+ if (tpool == NULL)
+ return(-1);
+
+ pool = *tpool;
+
+ if (pool == NULL)
+ return(-1);
+
+ pool->ltp_conf_max_count = max_threads;
+ if ( !max_threads )
+ max_threads = LDAP_MAXTHR;
+ pool->ltp_max_count = max_threads;
+
+ remthr = max_threads % pool->ltp_numqs;
+ max_threads /= pool->ltp_numqs;
+
+ for (i=0; i<pool->ltp_numqs; i++) {
+ pq = pool->ltp_wqs[i];
+ pq->ltp_max_count = max_threads;
+ if (remthr) {
+ pq->ltp_max_count++;
+ remthr--;
+ }
+ }
+ return(0);
+}
+
+/* Inspect the pool */
+int
+ldap_pvt_thread_pool_query(
+ ldap_pvt_thread_pool_t *tpool,
+ ldap_pvt_thread_pool_param_t param,
+ void *value )
+{
+ struct ldap_int_thread_pool_s *pool;
+ int count = -1;
+
+ if ( tpool == NULL || value == NULL ) {
+ return -1;
+ }
+
+ pool = *tpool;
+
+ if ( pool == NULL ) {
+ return 0;
+ }
+
+ switch ( param ) {
+ case LDAP_PVT_THREAD_POOL_PARAM_MAX:
+ count = pool->ltp_conf_max_count;
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_MAX_PENDING:
+ count = pool->ltp_max_pending;
+ if (count < 0)
+ count = -count;
+ if (count == MAX_PENDING)
+ count = 0;
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_PAUSING:
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+ count = (pool->ltp_pause != 0);
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_OPEN:
+ case LDAP_PVT_THREAD_POOL_PARAM_STARTING:
+ case LDAP_PVT_THREAD_POOL_PARAM_ACTIVE:
+ case LDAP_PVT_THREAD_POOL_PARAM_PENDING:
+ case LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD:
+ {
+ int i;
+ count = 0;
+ for (i=0; i<pool->ltp_numqs; i++) {
+ struct ldap_int_thread_poolq_s *pq = pool->ltp_wqs[i];
+ ldap_pvt_thread_mutex_lock(&pq->ltp_mutex);
+ switch(param) {
+ case LDAP_PVT_THREAD_POOL_PARAM_OPEN:
+ count += pq->ltp_open_count;
+ break;
+ case LDAP_PVT_THREAD_POOL_PARAM_STARTING:
+ count += pq->ltp_starting;
+ break;
+ case LDAP_PVT_THREAD_POOL_PARAM_ACTIVE:
+ count += pq->ltp_active_count;
+ break;
+ case LDAP_PVT_THREAD_POOL_PARAM_PENDING:
+ count += pq->ltp_pending_count;
+ break;
+ case LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD:
+ count += pq->ltp_pending_count + pq->ltp_active_count;
+ break;
+ default:
+ break;
+ }
+ ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex);
+ }
+ if (count < 0)
+ count = -count;
+ }
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_ACTIVE_MAX:
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_PENDING_MAX:
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD_MAX:
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_STATE:
+ if (pool->ltp_pause)
+ *((char **)value) = "pausing";
+ else if (!pool->ltp_finishing)
+ *((char **)value) = "running";
+ else {
+ int i;
+ for (i=0; i<pool->ltp_numqs; i++)
+ if (pool->ltp_wqs[i]->ltp_pending_count) break;
+ if (i<pool->ltp_numqs)
+ *((char **)value) = "finishing";
+ else
+ *((char **)value) = "stopping";
+ }
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_UNKNOWN:
+ break;
+ }
+
+ if ( count > -1 ) {
+ *((int *)value) = count;
+ }
+
+ return ( count == -1 ? -1 : 0 );
+}
+
+/*
+ * true if pool is pausing; does not lock any mutex to check.
+ * 0 if not pause, 1 if pause, -1 if error or no pool.
+ */
+int
+ldap_pvt_thread_pool_pausing( ldap_pvt_thread_pool_t *tpool )
+{
+ int rc = -1;
+ struct ldap_int_thread_pool_s *pool;
+
+ if ( tpool != NULL && (pool = *tpool) != NULL ) {
+ rc = (pool->ltp_pause != 0);
+ }
+
+ return rc;
+}
+
+/*
+ * wrapper for ldap_pvt_thread_pool_query(), left around
+ * for backwards compatibility
+ */
+int
+ldap_pvt_thread_pool_backload ( ldap_pvt_thread_pool_t *tpool )
+{
+ int rc, count;
+
+ rc = ldap_pvt_thread_pool_query( tpool,
+ LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD, (void *)&count );
+
+ if ( rc == 0 ) {
+ return count;
+ }
+
+ return rc;
+}
+
+
+/*
+ * wrapper for ldap_pvt_thread_pool_close+free(), left around
+ * for backwards compatibility
+ */
+int
+ldap_pvt_thread_pool_destroy ( ldap_pvt_thread_pool_t *tpool, int run_pending )
+{
+ int rc;
+
+ if ( (rc = ldap_pvt_thread_pool_close( tpool, run_pending )) ) {
+ return rc;
+ }
+
+ return ldap_pvt_thread_pool_free( tpool );
+}
+
+/* Shut down the pool making its threads finish */
+int
+ldap_pvt_thread_pool_close ( ldap_pvt_thread_pool_t *tpool, int run_pending )
+{
+ struct ldap_int_thread_pool_s *pool, *pptr;
+ struct ldap_int_thread_poolq_s *pq;
+ ldap_int_thread_task_t *task;
+ int i;
+
+ if (tpool == NULL)
+ return(-1);
+
+ pool = *tpool;
+
+ if (pool == NULL) return(-1);
+
+ ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
+ LDAP_STAILQ_FOREACH(pptr, &ldap_int_thread_pool_list, ltp_next)
+ if (pptr == pool) break;
+ ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
+
+ if (pool != pptr) return(-1);
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+
+ pool->ltp_finishing = 1;
+ if (pool->ltp_max_pending > 0)
+ pool->ltp_max_pending = -pool->ltp_max_pending;
+
+ ldap_pvt_thread_cond_broadcast(&pool->ltp_cond);
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+
+ for (i=0; i<pool->ltp_numqs; i++) {
+ pq = pool->ltp_wqs[i];
+ ldap_pvt_thread_mutex_lock(&pq->ltp_mutex);
+ if (pq->ltp_max_pending > 0)
+ pq->ltp_max_pending = -pq->ltp_max_pending;
+ if (!run_pending) {
+ while ((task = LDAP_STAILQ_FIRST(&pq->ltp_pending_list)) != NULL) {
+ LDAP_STAILQ_REMOVE_HEAD(&pq->ltp_pending_list, ltt_next.q);
+ LDAP_FREE(task);
+ }
+ pq->ltp_pending_count = 0;
+ }
+
+ while (pq->ltp_open_count) {
+ ldap_pvt_thread_cond_broadcast(&pq->ltp_cond);
+ ldap_pvt_thread_cond_wait(&pq->ltp_cond, &pq->ltp_mutex);
+ }
+
+ while ((task = LDAP_SLIST_FIRST(&pq->ltp_free_list)) != NULL)
+ {
+ LDAP_SLIST_REMOVE_HEAD(&pq->ltp_free_list, ltt_next.l);
+ LDAP_FREE(task);
+ }
+ ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex);
+ }
+
+ return(0);
+}
+
+/* Destroy the pool, everything must have already shut down */
+int
+ldap_pvt_thread_pool_free ( ldap_pvt_thread_pool_t *tpool )
+{
+ struct ldap_int_thread_pool_s *pool, *pptr;
+ struct ldap_int_thread_poolq_s *pq;
+ int i;
+
+ if (tpool == NULL)
+ return(-1);
+
+ pool = *tpool;
+
+ if (pool == NULL) return(-1);
+
+ ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
+ LDAP_STAILQ_FOREACH(pptr, &ldap_int_thread_pool_list, ltp_next)
+ if (pptr == pool) break;
+ if (pptr == pool)
+ LDAP_STAILQ_REMOVE(&ldap_int_thread_pool_list, pool,
+ ldap_int_thread_pool_s, ltp_next);
+ ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
+
+ if (pool != pptr) return(-1);
+
+ ldap_pvt_thread_cond_destroy(&pool->ltp_pcond);
+ ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
+ ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
+ for (i=0; i<pool->ltp_numqs; i++) {
+ pq = pool->ltp_wqs[i];
+
+ assert( !pq->ltp_open_count );
+ assert( LDAP_SLIST_EMPTY(&pq->ltp_free_list) );
+ ldap_pvt_thread_cond_destroy(&pq->ltp_cond);
+ ldap_pvt_thread_mutex_destroy(&pq->ltp_mutex);
+ if (pq->ltp_free) {
+ LDAP_FREE(pq->ltp_free);
+ }
+ }
+ LDAP_FREE(pool->ltp_wqs);
+ LDAP_FREE(pool);
+ *tpool = NULL;
+ ldap_int_has_thread_pool = 0;
+ return(0);
+}
+
+/* Thread loop. Accept and handle submitted tasks. */
+static void *
+ldap_int_thread_pool_wrapper (
+ void *xpool )
+{
+ struct ldap_int_thread_poolq_s *pq = xpool;
+ struct ldap_int_thread_pool_s *pool = pq->ltp_pool;
+ ldap_int_thread_task_t *task;
+ ldap_int_tpool_plist_t *work_list;
+ ldap_int_thread_userctx_t ctx, *kctx;
+ unsigned i, keyslot, hash;
+ int pool_lock = 0, freeme = 0;
+
+ assert(pool != NULL);
+
+ for ( i=0; i<MAXKEYS; i++ ) {
+ ctx.ltu_key[i].ltk_key = NULL;
+ }
+
+ ctx.ltu_pq = pq;
+ ctx.ltu_id = ldap_pvt_thread_self();
+ TID_HASH(ctx.ltu_id, hash);
+
+ ldap_pvt_thread_key_setdata( ldap_tpool_key, &ctx );
+
+ if (pool->ltp_pause) {
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+ /* thread_keys[] is read-only when paused */
+ while (pool->ltp_pause)
+ ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex);
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+ }
+
+ /* find a key slot to give this thread ID and store a
+ * pointer to our keys there; start at the thread ID
+ * itself (mod LDAP_MAXTHR) and look for an empty slot.
+ */
+ ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
+ for (keyslot = hash & (LDAP_MAXTHR-1);
+ (kctx = thread_keys[keyslot].ctx) && kctx != DELETED_THREAD_CTX;
+ keyslot = (keyslot+1) & (LDAP_MAXTHR-1));
+ thread_keys[keyslot].ctx = &ctx;
+ ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
+
+ ldap_pvt_thread_mutex_lock(&pq->ltp_mutex);
+ pq->ltp_starting--;
+ pq->ltp_active_count++;
+
+ for (;;) {
+ work_list = pq->ltp_work_list; /* help the compiler a bit */
+ task = LDAP_STAILQ_FIRST(work_list);
+ if (task == NULL) { /* paused or no pending tasks */
+ if (--(pq->ltp_active_count) < 1) {
+ if (pool->ltp_pause) {
+ ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex);
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+ pool_lock = 1;
+ if (--(pool->ltp_active_queues) < 1) {
+ /* Notify pool_pause it is the sole active thread. */
+ ldap_pvt_thread_cond_signal(&pool->ltp_pcond);
+ }
+ }
+ }
+
+ do {
+ if (pool->ltp_finishing || pq->ltp_open_count > pq->ltp_max_count) {
+ /* Not paused, and either finishing or too many
+ * threads running (can happen if ltp_max_count
+ * was reduced). Let this thread die.
+ */
+ goto done;
+ }
+
+ /* We could check an idle timer here, and let the
+ * thread die if it has been inactive for a while.
+ * Only die if there are other open threads (i.e.,
+ * always have at least one thread open).
+ * The check should be like this:
+ * if (pool->ltp_open_count>1 && pool->ltp_starting==0)
+ * check timer, wait if ltp_pause, leave thread;
+ *
+ * Just use pthread_cond_timedwait() if we want to
+ * check idle time.
+ */
+ if (pool_lock) {
+ ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex);
+ if (!pool->ltp_pause) {
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+ ldap_pvt_thread_mutex_lock(&pq->ltp_mutex);
+ pool_lock = 0;
+ }
+ } else
+ ldap_pvt_thread_cond_wait(&pq->ltp_cond, &pq->ltp_mutex);
+
+ work_list = pq->ltp_work_list;
+ task = LDAP_STAILQ_FIRST(work_list);
+ } while (task == NULL);
+
+ if (pool_lock) {
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+ ldap_pvt_thread_mutex_lock(&pq->ltp_mutex);
+ pool_lock = 0;
+ }
+ pq->ltp_active_count++;
+ }
+
+ LDAP_STAILQ_REMOVE_HEAD(work_list, ltt_next.q);
+ pq->ltp_pending_count--;
+ ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex);
+
+ task->ltt_start_routine(&ctx, task->ltt_arg);
+
+ ldap_pvt_thread_mutex_lock(&pq->ltp_mutex);
+ LDAP_SLIST_INSERT_HEAD(&pq->ltp_free_list, task, ltt_next.l);
+ }
+ done:
+
+ ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
+
+ /* The pool_mutex lock protects ctx->ltu_key from pool_purgekey()
+ * during this call, since it prevents new pauses. */
+ ldap_pvt_thread_pool_context_reset(&ctx);
+
+ thread_keys[keyslot].ctx = DELETED_THREAD_CTX;
+ ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
+
+ pq->ltp_open_count--;
+ if (pq->ltp_open_count == 0) {
+ if (pool->ltp_finishing)
+ /* let pool_destroy know we're all done */
+ ldap_pvt_thread_cond_signal(&pq->ltp_cond);
+ else
+ freeme = 1;
+ }
+
+ if (pool_lock)
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+ else
+ ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex);
+
+ if (freeme) {
+ ldap_pvt_thread_cond_destroy(&pq->ltp_cond);
+ ldap_pvt_thread_mutex_destroy(&pq->ltp_mutex);
+ LDAP_FREE(pq->ltp_free);
+ pq->ltp_free = NULL;
+ }
+ ldap_pvt_thread_exit(NULL);
+ return(NULL);
+}
+
+/* Arguments > ltp_pause to handle_pause(,PAUSE_ARG()). arg=PAUSE_ARG
+ * ensures (arg-ltp_pause) sets GO_* at need and keeps DO_PAUSE/GO_*.
+ */
+#define GO_IDLE 8
+#define GO_UNIDLE 16
+#define CHECK_PAUSE 32 /* if ltp_pause: GO_IDLE; wait; GO_UNIDLE */
+#define DO_PAUSE 64 /* CHECK_PAUSE; pause the pool */
+#define PAUSE_ARG(a) \
+ ((a) | ((a) & (GO_IDLE|GO_UNIDLE) ? GO_IDLE-1 : CHECK_PAUSE))
+
+static int
+handle_pause( ldap_pvt_thread_pool_t *tpool, int pause_type )
+{
+ struct ldap_int_thread_pool_s *pool;
+ struct ldap_int_thread_poolq_s *pq;
+ int ret = 0, pause, max_ltp_pause;
+
+ if (tpool == NULL)
+ return(-1);
+
+ pool = *tpool;
+
+ if (pool == NULL)
+ return(0);
+
+ if (pause_type == CHECK_PAUSE && !pool->ltp_pause)
+ return(0);
+
+ {
+ ldap_int_thread_userctx_t *ctx = ldap_pvt_thread_pool_context();
+ pq = ctx->ltu_pq;
+ if ( !pq )
+ return(-1);
+ }
+
+ /* Let pool_unidle() ignore requests for new pauses */
+ max_ltp_pause = pause_type==PAUSE_ARG(GO_UNIDLE) ? WANT_PAUSE : NOT_PAUSED;
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+
+ pause = pool->ltp_pause; /* NOT_PAUSED, WANT_PAUSE or PAUSED */
+
+ /* If ltp_pause and not GO_IDLE|GO_UNIDLE: Set GO_IDLE,GO_UNIDLE */
+ pause_type -= pause;
+
+ if (pause_type & GO_IDLE) {
+ int do_pool = 0;
+ ldap_pvt_thread_mutex_lock(&pq->ltp_mutex);
+ pq->ltp_pending_count++;
+ pq->ltp_active_count--;
+ if (pause && pq->ltp_active_count < 1) {
+ do_pool = 1;
+ }
+ ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex);
+ if (do_pool) {
+ pool->ltp_active_queues--;
+ if (pool->ltp_active_queues < 1)
+ /* Tell the task waiting to DO_PAUSE it can proceed */
+ ldap_pvt_thread_cond_signal(&pool->ltp_pcond);
+ }
+ }
+
+ if (pause_type & GO_UNIDLE) {
+ /* Wait out pause if any, then cancel GO_IDLE */
+ if (pause > max_ltp_pause) {
+ ret = 1;
+ do {
+ ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex);
+ } while (pool->ltp_pause > max_ltp_pause);
+ }
+ ldap_pvt_thread_mutex_lock(&pq->ltp_mutex);
+ pq->ltp_pending_count--;
+ pq->ltp_active_count++;
+ ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex);
+ }
+
+ if (pause_type & DO_PAUSE) {
+ int i, j;
+ /* Tell everyone else to pause or finish, then await that */
+ ret = 0;
+ assert(!pool->ltp_pause);
+ pool->ltp_pause = WANT_PAUSE;
+ pool->ltp_active_queues = 0;
+
+ for (i=0; i<pool->ltp_numqs; i++)
+ if (pool->ltp_wqs[i] == pq) break;
+
+ ldap_pvt_thread_mutex_lock(&pq->ltp_mutex);
+ /* temporarily remove ourself from active count */
+ pq->ltp_active_count--;
+
+ j=i;
+ do {
+ pq = pool->ltp_wqs[j];
+ if (j != i)
+ ldap_pvt_thread_mutex_lock(&pq->ltp_mutex);
+
+ /* Hide pending tasks from ldap_pvt_thread_pool_wrapper() */
+ pq->ltp_work_list = &empty_pending_list;
+
+ if (pq->ltp_active_count > 0)
+ pool->ltp_active_queues++;
+
+ ldap_pvt_thread_mutex_unlock(&pq->ltp_mutex);
+ if (pool->ltp_numqs > 1) {
+ j++;
+ j %= pool->ltp_numqs;
+ }
+ } while (j != i);
+
+ /* Wait for this task to become the sole active task */
+ while (pool->ltp_active_queues > 0)
+ ldap_pvt_thread_cond_wait(&pool->ltp_pcond, &pool->ltp_mutex);
+
+ /* restore us to active count */
+ pool->ltp_wqs[i]->ltp_active_count++;
+
+ assert(pool->ltp_pause == WANT_PAUSE);
+ pool->ltp_pause = PAUSED;
+ }
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+
+ return(ret);
+}
+
+/* Consider this task idle: It will not block pool_pause() in other tasks. */
+void
+ldap_pvt_thread_pool_idle( ldap_pvt_thread_pool_t *tpool )
+{
+ handle_pause(tpool, PAUSE_ARG(GO_IDLE));
+}
+
+/* Cancel pool_idle(). If the pool is paused, wait it out first. */
+void
+ldap_pvt_thread_pool_unidle( ldap_pvt_thread_pool_t *tpool )
+{
+ handle_pause(tpool, PAUSE_ARG(GO_UNIDLE));
+}
+
+/*
+ * If a pause was requested, wait for it. If several threads
+ * are waiting to pause, let through one or more pauses.
+ * The calling task must be active, not idle.
+ * Return 1 if we waited, 0 if not, -1 at parameter error.
+ */
+int
+ldap_pvt_thread_pool_pausecheck( ldap_pvt_thread_pool_t *tpool )
+{
+ return handle_pause(tpool, PAUSE_ARG(CHECK_PAUSE));
+}
+
+/*
+ * Wait for a pause, from a non-pooled thread.
+ */
+int
+ldap_pvt_thread_pool_pausecheck_native( ldap_pvt_thread_pool_t *tpool )
+{
+ struct ldap_int_thread_pool_s *pool;
+
+ if (tpool == NULL)
+ return(-1);
+
+ pool = *tpool;
+
+ if (pool == NULL)
+ return(0);
+
+ if (!pool->ltp_pause)
+ return(0);
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+ while (pool->ltp_pause)
+ ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex);
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+ return 1;
+}
+
+/*
+ * Pause the pool. The calling task must be active, not idle.
+ * Return when all other tasks are paused or idle.
+ */
+int
+ldap_pvt_thread_pool_pause( ldap_pvt_thread_pool_t *tpool )
+{
+ return handle_pause(tpool, PAUSE_ARG(DO_PAUSE));
+}
+
+/* End a pause */
+int
+ldap_pvt_thread_pool_resume (
+ ldap_pvt_thread_pool_t *tpool )
+{
+ struct ldap_int_thread_pool_s *pool;
+ struct ldap_int_thread_poolq_s *pq;
+ int i;
+
+ if (tpool == NULL)
+ return(-1);
+
+ pool = *tpool;
+
+ if (pool == NULL)
+ return(0);
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+ assert(pool->ltp_pause == PAUSED);
+ pool->ltp_pause = 0;
+ for (i=0; i<pool->ltp_numqs; i++) {
+ pq = pool->ltp_wqs[i];
+ pq->ltp_work_list = &pq->ltp_pending_list;
+ ldap_pvt_thread_cond_broadcast(&pq->ltp_cond);
+ }
+ ldap_pvt_thread_cond_broadcast(&pool->ltp_cond);
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+ return(0);
+}
+
+/*
+ * Get the key's data and optionally free function in the given context.
+ */
+int ldap_pvt_thread_pool_getkey(
+ void *xctx,
+ void *key,
+ void **data,
+ ldap_pvt_thread_pool_keyfree_t **kfree )
+{
+ ldap_int_thread_userctx_t *ctx = xctx;
+ int i;
+
+ if ( !ctx || !key || !data ) return EINVAL;
+
+ for ( i=0; i<MAXKEYS && ctx->ltu_key[i].ltk_key; i++ ) {
+ if ( ctx->ltu_key[i].ltk_key == key ) {
+ *data = ctx->ltu_key[i].ltk_data;
+ if ( kfree ) *kfree = ctx->ltu_key[i].ltk_free;
+ return 0;
+ }
+ }
+ return ENOENT;
+}
+
+static void
+clear_key_idx( ldap_int_thread_userctx_t *ctx, int i )
+{
+ for ( ; i < MAXKEYS-1 && ctx->ltu_key[i+1].ltk_key; i++ )
+ ctx->ltu_key[i] = ctx->ltu_key[i+1];
+ ctx->ltu_key[i].ltk_key = NULL;
+}
+
+/*
+ * Set or remove data for the key in the given context.
+ * key can be any unique pointer.
+ * kfree() is an optional function to free the data (but not the key):
+ * pool_context_reset() and pool_purgekey() call kfree(key, data),
+ * but pool_setkey() does not. For pool_setkey() it is the caller's
+ * responsibility to free any existing data with the same key.
+ * kfree() must not call functions taking a tpool argument.
+ */
+int ldap_pvt_thread_pool_setkey(
+ void *xctx,
+ void *key,
+ void *data,
+ ldap_pvt_thread_pool_keyfree_t *kfree,
+ void **olddatap,
+ ldap_pvt_thread_pool_keyfree_t **oldkfreep )
+{
+ ldap_int_thread_userctx_t *ctx = xctx;
+ int i, found;
+
+ if ( !ctx || !key ) return EINVAL;
+
+ for ( i=found=0; i<MAXKEYS; i++ ) {
+ if ( ctx->ltu_key[i].ltk_key == key ) {
+ found = 1;
+ break;
+ } else if ( !ctx->ltu_key[i].ltk_key ) {
+ break;
+ }
+ }
+
+ if ( olddatap ) {
+ if ( found ) {
+ *olddatap = ctx->ltu_key[i].ltk_data;
+ } else {
+ *olddatap = NULL;
+ }
+ }
+
+ if ( oldkfreep ) {
+ if ( found ) {
+ *oldkfreep = ctx->ltu_key[i].ltk_free;
+ } else {
+ *oldkfreep = 0;
+ }
+ }
+
+ if ( data || kfree ) {
+ if ( i>=MAXKEYS )
+ return ENOMEM;
+ ctx->ltu_key[i].ltk_key = key;
+ ctx->ltu_key[i].ltk_data = data;
+ ctx->ltu_key[i].ltk_free = kfree;
+ } else if ( found ) {
+ clear_key_idx( ctx, i );
+ }
+
+ return 0;
+}
+
+/* Free all elements with this key, no matter which thread they're in.
+ * May only be called while the pool is paused.
+ */
+void ldap_pvt_thread_pool_purgekey( void *key )
+{
+ int i, j;
+ ldap_int_thread_userctx_t *ctx;
+
+ assert ( key != NULL );
+
+ ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
+ for ( i=0; i<LDAP_MAXTHR; i++ ) {
+ ctx = thread_keys[i].ctx;
+ if ( ctx && ctx != DELETED_THREAD_CTX ) {
+ for ( j=0; j<MAXKEYS && ctx->ltu_key[j].ltk_key; j++ ) {
+ if ( ctx->ltu_key[j].ltk_key == key ) {
+ if (ctx->ltu_key[j].ltk_free)
+ ctx->ltu_key[j].ltk_free( ctx->ltu_key[j].ltk_key,
+ ctx->ltu_key[j].ltk_data );
+ clear_key_idx( ctx, j );
+ break;
+ }
+ }
+ }
+ }
+ ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
+}
+
+/*
+ * Find the context of the current thread.
+ * This is necessary if the caller does not have access to the
+ * thread context handle (for example, a slapd plugin calling
+ * slapi_search_internal()). No doubt it is more efficient
+ * for the application to keep track of the thread context
+ * handles itself.
+ */
+void *ldap_pvt_thread_pool_context( )
+{
+ void *ctx = NULL;
+
+ ldap_pvt_thread_key_getdata( ldap_tpool_key, &ctx );
+ return ctx ? ctx : (void *) &ldap_int_main_thrctx;
+}
+
+/*
+ * Free the context's keys.
+ * Must not call functions taking a tpool argument (because this
+ * thread already holds ltp_mutex when called from pool_wrapper()).
+ */
+void ldap_pvt_thread_pool_context_reset( void *vctx )
+{
+ ldap_int_thread_userctx_t *ctx = vctx;
+ int i;
+
+ for ( i=MAXKEYS-1; i>=0; i--) {
+ if ( !ctx->ltu_key[i].ltk_key )
+ continue;
+ if ( ctx->ltu_key[i].ltk_free )
+ ctx->ltu_key[i].ltk_free( ctx->ltu_key[i].ltk_key,
+ ctx->ltu_key[i].ltk_data );
+ ctx->ltu_key[i].ltk_key = NULL;
+ }
+}
+
+ldap_pvt_thread_t ldap_pvt_thread_pool_tid( void *vctx )
+{
+ ldap_int_thread_userctx_t *ctx = vctx;
+
+ return ctx->ltu_id;
+}
+#endif /* LDAP_THREAD_HAVE_TPOOL */
+
+#endif /* LDAP_R_COMPILE */
diff --git a/libraries/libldap/turn.c b/libraries/libldap/turn.c
new file mode 100644
index 0000000..7725f01
--- /dev/null
+++ b/libraries/libldap/turn.c
@@ -0,0 +1,96 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This program was originally developed by Kurt D. Zeilenga for inclusion in
+ * OpenLDAP Software.
+ */
+
+/*
+ * LDAPv3 Turn Operation Request
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_log.h"
+
+int
+ldap_turn(
+ LDAP *ld,
+ int mutual,
+ LDAP_CONST char* identifier,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+#ifdef LDAP_EXOP_X_TURN
+ BerElement *turnvalber = NULL;
+ struct berval turnval;
+ int rc;
+
+ turnvalber = ber_alloc_t( LBER_USE_DER );
+ if( mutual ) {
+ ber_printf( turnvalber, "{bs}", mutual, identifier );
+ } else {
+ ber_printf( turnvalber, "{s}", identifier );
+ }
+ ber_flatten2( turnvalber, &turnval, 0 );
+
+ rc = ldap_extended_operation( ld, LDAP_EXOP_X_TURN,
+ &turnval, sctrls, cctrls, msgidp );
+ ber_free( turnvalber, 1 );
+ return rc;
+#else
+ return LDAP_CONTROL_NOT_FOUND;
+#endif
+}
+
+int
+ldap_turn_s(
+ LDAP *ld,
+ int mutual,
+ LDAP_CONST char* identifier,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+#ifdef LDAP_EXOP_X_TURN
+ BerElement *turnvalber = NULL;
+ struct berval turnval;
+ int rc;
+
+ turnvalber = ber_alloc_t( LBER_USE_DER );
+ if( mutual ) {
+ ber_printf( turnvalber, "{bs}", 0xFF, identifier );
+ } else {
+ ber_printf( turnvalber, "{s}", identifier );
+ }
+ ber_flatten2( turnvalber, &turnval, 0 );
+
+ rc = ldap_extended_operation_s( ld, LDAP_EXOP_X_TURN,
+ &turnval, sctrls, cctrls, NULL, NULL );
+ ber_free( turnvalber, 1 );
+ return rc;
+#else
+ return LDAP_CONTROL_NOT_FOUND;
+#endif
+}
+
diff --git a/libraries/libldap/txn.c b/libraries/libldap/txn.c
new file mode 100644
index 0000000..6409002
--- /dev/null
+++ b/libraries/libldap/txn.c
@@ -0,0 +1,153 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2006-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This program was originally developed by Kurt D. Zeilenga for inclusion
+ * in OpenLDAP Software.
+ */
+
+/*
+ * LDAPv3 Transactions (draft-zeilenga-ldap-txn)
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_log.h"
+
+int
+ldap_txn_start(
+ LDAP *ld,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ return ldap_extended_operation( ld, LDAP_EXOP_TXN_START,
+ NULL, sctrls, cctrls, msgidp );
+}
+
+int
+ldap_txn_start_s(
+ LDAP *ld,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ struct berval **txnid )
+{
+ assert( txnid != NULL );
+
+ return ldap_extended_operation_s( ld, LDAP_EXOP_TXN_START,
+ NULL, sctrls, cctrls, NULL, txnid );
+}
+
+int
+ldap_txn_end(
+ LDAP *ld,
+ int commit,
+ struct berval *txnid,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ int rc;
+ BerElement *txnber = NULL;
+ struct berval txnval;
+
+ assert( txnid != NULL );
+
+ txnber = ber_alloc_t( LBER_USE_DER );
+
+ if( commit ) {
+ ber_printf( txnber, "{ON}", txnid );
+ } else {
+ ber_printf( txnber, "{bON}", commit, txnid );
+ }
+
+ ber_flatten2( txnber, &txnval, 0 );
+
+ rc = ldap_extended_operation( ld, LDAP_EXOP_TXN_END,
+ &txnval, sctrls, cctrls, msgidp );
+
+ ber_free( txnber, 1 );
+ return rc;
+}
+
+int
+ldap_txn_end_s(
+ LDAP *ld,
+ int commit,
+ struct berval *txnid,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *retidp )
+{
+ int rc;
+ BerElement *txnber = NULL;
+ struct berval txnval;
+ struct berval *retdata = NULL;
+
+ if ( retidp != NULL ) *retidp = -1;
+
+ txnber = ber_alloc_t( LBER_USE_DER );
+
+ if( commit ) {
+ ber_printf( txnber, "{ON}", txnid );
+ } else {
+ ber_printf( txnber, "{bON}", commit, txnid );
+ }
+
+ ber_flatten2( txnber, &txnval, 0 );
+
+ rc = ldap_extended_operation_s( ld, LDAP_EXOP_TXN_END,
+ &txnval, sctrls, cctrls, NULL, &retdata );
+
+ ber_free( txnber, 1 );
+
+ /* parse retdata */
+ if( retdata != NULL ) {
+ BerElement *ber;
+ ber_tag_t tag;
+ ber_int_t retid;
+
+ if( retidp == NULL ) goto done;
+
+ ber = ber_init( retdata );
+
+ if( ber == NULL ) {
+ rc = ld->ld_errno = LDAP_NO_MEMORY;
+ goto done;
+ }
+
+ tag = ber_scanf( ber, "i", &retid );
+ ber_free( ber, 1 );
+
+ if ( tag != LBER_INTEGER ) {
+ rc = ld->ld_errno = LDAP_DECODING_ERROR;
+ goto done;
+ }
+
+ *retidp = (int) retid;
+
+done:
+ ber_bvfree( retdata );
+ }
+
+ return rc;
+}
diff --git a/libraries/libldap/unbind.c b/libraries/libldap/unbind.c
new file mode 100644
index 0000000..820d48f
--- /dev/null
+++ b/libraries/libldap/unbind.c
@@ -0,0 +1,319 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/* An Unbind Request looks like this:
+ *
+ * UnbindRequest ::= [APPLICATION 2] NULL
+ *
+ * and has no response. (Source: RFC 4511)
+ */
+
+int
+ldap_unbind_ext(
+ LDAP *ld,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ int rc;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ /* check client controls */
+ rc = ldap_int_client_controls( ld, cctrls );
+ if( rc != LDAP_SUCCESS ) return rc;
+
+ return ldap_ld_free( ld, 1, sctrls, cctrls );
+}
+
+int
+ldap_unbind_ext_s(
+ LDAP *ld,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ return ldap_unbind_ext( ld, sctrls, cctrls );
+}
+
+int
+ldap_unbind( LDAP *ld )
+{
+ Debug0( LDAP_DEBUG_TRACE, "ldap_unbind\n" );
+
+ return( ldap_unbind_ext( ld, NULL, NULL ) );
+}
+
+
+int
+ldap_ld_free(
+ LDAP *ld,
+ int close,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ LDAPMessage *lm, *next;
+ int err = LDAP_SUCCESS;
+
+ LDAP_MUTEX_LOCK( &ld->ld_ldcmutex );
+ /* Someone else is still using this ld. */
+ if (ld->ld_ldcrefcnt > 1) { /* but not last thread */
+ /* clean up self only */
+ ld->ld_ldcrefcnt--;
+ if ( ld->ld_error != NULL ) {
+ LDAP_FREE( ld->ld_error );
+ ld->ld_error = NULL;
+ }
+
+ if ( ld->ld_matched != NULL ) {
+ LDAP_FREE( ld->ld_matched );
+ ld->ld_matched = NULL;
+ }
+ if ( ld->ld_referrals != NULL) {
+ LDAP_VFREE(ld->ld_referrals);
+ ld->ld_referrals = NULL;
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_ldcmutex );
+ LDAP_FREE( (char *) ld );
+ return( err );
+ }
+
+ /* This ld is the last thread. */
+ LDAP_MUTEX_UNLOCK( &ld->ld_ldcmutex );
+
+ /* free LDAP structure and outstanding requests/responses */
+ LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+ ldap_tavl_free( ld->ld_requests, ldap_do_free_request );
+ ld->ld_requests = NULL;
+ LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+
+ /* free and unbind from all open connections */
+ while ( ld->ld_conns != NULL ) {
+ ldap_free_connection( ld, ld->ld_conns, 1, close );
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
+ for ( lm = ld->ld_responses; lm != NULL; lm = next ) {
+ next = lm->lm_next;
+ ldap_msgfree( lm );
+ }
+
+ if ( ld->ld_abandoned != NULL ) {
+ LDAP_FREE( ld->ld_abandoned );
+ ld->ld_abandoned = NULL;
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
+
+ /* Should already be closed by ldap_free_connection which knows not to free
+ * this one */
+ ber_int_sb_destroy( ld->ld_sb );
+ LBER_FREE( ld->ld_sb );
+
+ LDAP_MUTEX_LOCK( &ld->ld_ldopts_mutex );
+
+ /* final close callbacks */
+ {
+ ldaplist *ll, *next;
+
+ for ( ll = ld->ld_options.ldo_conn_cbs; ll; ll = next ) {
+ ldap_conncb *cb = ll->ll_data;
+ next = ll->ll_next;
+ cb->lc_del( ld, NULL, cb );
+ LDAP_FREE( ll );
+ }
+ }
+
+ if ( ld->ld_error != NULL ) {
+ LDAP_FREE( ld->ld_error );
+ ld->ld_error = NULL;
+ }
+
+ if ( ld->ld_matched != NULL ) {
+ LDAP_FREE( ld->ld_matched );
+ ld->ld_matched = NULL;
+ }
+
+ if ( ld->ld_referrals != NULL) {
+ LDAP_VFREE(ld->ld_referrals);
+ ld->ld_referrals = NULL;
+ }
+
+ if ( ld->ld_selectinfo != NULL ) {
+ ldap_free_select_info( ld->ld_selectinfo );
+ ld->ld_selectinfo = NULL;
+ }
+
+ if ( ld->ld_options.ldo_defludp != NULL ) {
+ ldap_free_urllist( ld->ld_options.ldo_defludp );
+ ld->ld_options.ldo_defludp = NULL;
+ }
+
+ if ( ld->ld_options.ldo_local_ip_addrs.local_ip_addrs ) {
+ LDAP_FREE( ld->ld_options.ldo_local_ip_addrs.local_ip_addrs );
+ memset( & ld->ld_options.ldo_local_ip_addrs, 0,
+ sizeof( ldapsourceip ) );
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ if ( ld->ld_options.ldo_peer != NULL ) {
+ LDAP_FREE( ld->ld_options.ldo_peer );
+ ld->ld_options.ldo_peer = NULL;
+ }
+
+ if ( ld->ld_options.ldo_cldapdn != NULL ) {
+ LDAP_FREE( ld->ld_options.ldo_cldapdn );
+ ld->ld_options.ldo_cldapdn = NULL;
+ }
+#endif
+
+ if ( ld->ld_options.ldo_defbase != NULL ) {
+ LDAP_FREE( ld->ld_options.ldo_defbase );
+ ld->ld_options.ldo_defbase = NULL;
+ }
+
+#ifdef HAVE_CYRUS_SASL
+ if ( ld->ld_options.ldo_def_sasl_mech != NULL ) {
+ LDAP_FREE( ld->ld_options.ldo_def_sasl_mech );
+ ld->ld_options.ldo_def_sasl_mech = NULL;
+ }
+
+ if ( ld->ld_options.ldo_def_sasl_realm != NULL ) {
+ LDAP_FREE( ld->ld_options.ldo_def_sasl_realm );
+ ld->ld_options.ldo_def_sasl_realm = NULL;
+ }
+
+ if ( ld->ld_options.ldo_def_sasl_authcid != NULL ) {
+ LDAP_FREE( ld->ld_options.ldo_def_sasl_authcid );
+ ld->ld_options.ldo_def_sasl_authcid = NULL;
+ }
+
+ if ( ld->ld_options.ldo_def_sasl_authzid != NULL ) {
+ LDAP_FREE( ld->ld_options.ldo_def_sasl_authzid );
+ ld->ld_options.ldo_def_sasl_authzid = NULL;
+ }
+#endif
+
+#ifdef HAVE_TLS
+ ldap_int_tls_destroy( &ld->ld_options );
+#endif
+
+ if ( ld->ld_options.ldo_sctrls != NULL ) {
+ ldap_controls_free( ld->ld_options.ldo_sctrls );
+ ld->ld_options.ldo_sctrls = NULL;
+ }
+
+ if ( ld->ld_options.ldo_cctrls != NULL ) {
+ ldap_controls_free( ld->ld_options.ldo_cctrls );
+ ld->ld_options.ldo_cctrls = NULL;
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_ldopts_mutex );
+
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_destroy( &ld->ld_msgid_mutex );
+ ldap_pvt_thread_mutex_destroy( &ld->ld_conn_mutex );
+ ldap_pvt_thread_mutex_destroy( &ld->ld_req_mutex );
+ ldap_pvt_thread_mutex_destroy( &ld->ld_res_mutex );
+ ldap_pvt_thread_mutex_destroy( &ld->ld_abandon_mutex );
+ ldap_pvt_thread_mutex_destroy( &ld->ld_ldopts_mutex );
+ ldap_pvt_thread_mutex_destroy( &ld->ld_ldcmutex );
+#endif
+#ifndef NDEBUG
+ LDAP_TRASH(ld);
+#endif
+ LDAP_FREE( (char *) ld->ldc );
+ LDAP_FREE( (char *) ld );
+
+ return( err );
+}
+
+int
+ldap_destroy( LDAP *ld )
+{
+ return ( ldap_ld_free( ld, 1, NULL, NULL ) );
+}
+
+int
+ldap_unbind_s( LDAP *ld )
+{
+ return( ldap_unbind_ext( ld, NULL, NULL ) );
+}
+
+/* FIXME: this function is called only by ldap_free_connection(),
+ * which, most of the times, is called with ld_req_mutex locked */
+int
+ldap_send_unbind(
+ LDAP *ld,
+ Sockbuf *sb,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ BerElement *ber;
+ ber_int_t id;
+
+ Debug0( LDAP_DEBUG_TRACE, "ldap_send_unbind\n" );
+
+#ifdef LDAP_CONNECTIONLESS
+ if (LDAP_IS_UDP(ld))
+ return LDAP_SUCCESS;
+#endif
+ /* create a message to send */
+ if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
+ return( ld->ld_errno );
+ }
+
+ LDAP_NEXT_MSGID(ld, id);
+
+ /* fill it in */
+ if ( ber_printf( ber, "{itn" /*}*/, id,
+ LDAP_REQ_UNBIND ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( ld->ld_errno );
+ }
+
+ /* Put Server Controls */
+ if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
+ ber_free( ber, 1 );
+ return ld->ld_errno;
+ }
+
+ if ( ber_printf( ber, /*{*/ "N}", LDAP_REQ_UNBIND ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( ld->ld_errno );
+ }
+
+ ld->ld_errno = LDAP_SUCCESS;
+ /* send the message */
+ if ( ber_flush2( sb, ber, LBER_FLUSH_FREE_ALWAYS ) == -1 ) {
+ ld->ld_errno = LDAP_SERVER_DOWN;
+ }
+
+ return( ld->ld_errno );
+}
diff --git a/libraries/libldap/url.c b/libraries/libldap/url.c
new file mode 100644
index 0000000..dcf2aac
--- /dev/null
+++ b/libraries/libldap/url.c
@@ -0,0 +1,1650 @@
+/* LIBLDAP url.c -- LDAP URL (RFC 4516) related routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1996 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+
+/*
+ * LDAP URLs look like this:
+ * [p]ldap[is]://host[:port][/[dn[?[attributes][?[scope][?[filter][?exts]]]]]]
+ *
+ * where:
+ * attributes is a comma separated list
+ * scope is one of these three strings: base one sub (default=base)
+ * filter is an string-represented filter as in RFC 4515
+ *
+ * e.g., ldap://host:port/dc=com?o,cn?base?(o=openldap)?extension
+ *
+ * We also tolerate URLs that look like: <ldapurl> and <URL:ldapurl>
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/ctype.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/* local functions */
+static const char* skip_url_prefix LDAP_P((
+ const char *url,
+ int *enclosedp,
+ const char **scheme ));
+
+int ldap_pvt_url_scheme2proto( const char *scheme )
+{
+ assert( scheme != NULL );
+
+ if( scheme == NULL ) {
+ return -1;
+ }
+
+ if( strcmp("ldap", scheme) == 0 || strcmp("pldap", scheme) == 0 ) {
+ return LDAP_PROTO_TCP;
+ }
+
+ if( strcmp("ldapi", scheme) == 0 ) {
+ return LDAP_PROTO_IPC;
+ }
+
+ if( strcmp("ldaps", scheme) == 0 || strcmp("pldaps", scheme) == 0 ) {
+ return LDAP_PROTO_TCP;
+ }
+#ifdef LDAP_CONNECTIONLESS
+ if( strcmp("cldap", scheme) == 0 ) {
+ return LDAP_PROTO_UDP;
+ }
+#endif
+
+ return -1;
+}
+
+int ldap_pvt_url_scheme_port( const char *scheme, int port )
+{
+ assert( scheme != NULL );
+
+ if( port ) return port;
+ if( scheme == NULL ) return port;
+
+ if( strcmp("ldap", scheme) == 0 || strcmp("pldap", scheme) == 0 ) {
+ return LDAP_PORT;
+ }
+
+ if( strcmp("ldapi", scheme) == 0 ) {
+ return -1;
+ }
+
+ if( strcmp("ldaps", scheme) == 0 || strcmp("pldaps", scheme) == 0 ) {
+ return LDAPS_PORT;
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ if( strcmp("cldap", scheme) == 0 ) {
+ return LDAP_PORT;
+ }
+#endif
+
+ return -1;
+}
+
+int
+ldap_pvt_url_scheme2tls( const char *scheme )
+{
+ assert( scheme != NULL );
+
+ if( scheme == NULL ) {
+ return -1;
+ }
+
+ return strcmp("ldaps", scheme) == 0 || strcmp("pldaps", scheme) == 0;
+}
+
+int
+ldap_pvt_url_scheme2proxied( const char *scheme )
+{
+ assert( scheme != NULL );
+
+ if( scheme == NULL ) {
+ return -1;
+ }
+
+ return strcmp("pldap", scheme) == 0 || strcmp("pldaps", scheme) == 0;
+}
+
+int
+ldap_is_ldap_url( LDAP_CONST char *url )
+{
+ int enclosed;
+ const char * scheme;
+
+ if( url == NULL ) {
+ return 0;
+ }
+
+ if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+ldap_is_ldaps_url( LDAP_CONST char *url )
+{
+ int enclosed;
+ const char * scheme;
+
+ if( url == NULL ) {
+ return 0;
+ }
+
+ if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
+ return 0;
+ }
+
+ return strcmp(scheme, "ldaps") == 0 || strcmp(scheme, "pldaps") == 0;
+}
+
+int
+ldap_is_ldapi_url( LDAP_CONST char *url )
+{
+ int enclosed;
+ const char * scheme;
+
+ if( url == NULL ) {
+ return 0;
+ }
+
+ if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
+ return 0;
+ }
+
+ return strcmp(scheme, "ldapi") == 0;
+}
+
+#ifdef LDAP_CONNECTIONLESS
+int
+ldap_is_ldapc_url( LDAP_CONST char *url )
+{
+ int enclosed;
+ const char * scheme;
+
+ if( url == NULL ) {
+ return 0;
+ }
+
+ if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
+ return 0;
+ }
+
+ return strcmp(scheme, "cldap") == 0;
+}
+#endif
+
+static const char*
+skip_url_prefix(
+ const char *url,
+ int *enclosedp,
+ const char **scheme )
+{
+ /*
+ * return non-zero if this looks like a LDAP URL; zero if not
+ * if non-zero returned, *urlp will be moved past "ldap://" part of URL
+ */
+ const char *p;
+
+ if ( url == NULL ) {
+ return( NULL );
+ }
+
+ p = url;
+
+ /* skip leading '<' (if any) */
+ if ( *p == '<' ) {
+ *enclosedp = 1;
+ ++p;
+ } else {
+ *enclosedp = 0;
+ }
+
+ /* skip leading "URL:" (if any) */
+ if ( strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) {
+ p += LDAP_URL_URLCOLON_LEN;
+ }
+
+ /* check for "ldap://" prefix */
+ if ( strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) {
+ /* skip over "ldap://" prefix and return success */
+ p += LDAP_URL_PREFIX_LEN;
+ *scheme = "ldap";
+ return( p );
+ }
+
+ /* check for "pldap://" prefix */
+ if ( strncasecmp( p, PLDAP_URL_PREFIX, PLDAP_URL_PREFIX_LEN ) == 0 ) {
+ /* skip over "pldap://" prefix and return success */
+ p += PLDAP_URL_PREFIX_LEN;
+ *scheme = "pldap";
+ return( p );
+ }
+
+ /* check for "ldaps://" prefix */
+ if ( strncasecmp( p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) {
+ /* skip over "ldaps://" prefix and return success */
+ p += LDAPS_URL_PREFIX_LEN;
+ *scheme = "ldaps";
+ return( p );
+ }
+
+ /* check for "pldaps://" prefix */
+ if ( strncasecmp( p, PLDAPS_URL_PREFIX, PLDAPS_URL_PREFIX_LEN ) == 0 ) {
+ /* skip over "pldaps://" prefix and return success */
+ p += PLDAPS_URL_PREFIX_LEN;
+ *scheme = "pldaps";
+ return( p );
+ }
+
+ /* check for "ldapi://" prefix */
+ if ( strncasecmp( p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN ) == 0 ) {
+ /* skip over "ldapi://" prefix and return success */
+ p += LDAPI_URL_PREFIX_LEN;
+ *scheme = "ldapi";
+ return( p );
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ /* check for "cldap://" prefix */
+ if ( strncasecmp( p, LDAPC_URL_PREFIX, LDAPC_URL_PREFIX_LEN ) == 0 ) {
+ /* skip over "cldap://" prefix and return success */
+ p += LDAPC_URL_PREFIX_LEN;
+ *scheme = "cldap";
+ return( p );
+ }
+#endif
+
+ return( NULL );
+}
+
+int
+ldap_pvt_scope2bv( int scope, struct berval *bv )
+{
+ switch ( scope ) {
+ case LDAP_SCOPE_BASE:
+ BER_BVSTR( bv, "base" );
+ break;
+
+ case LDAP_SCOPE_ONELEVEL:
+ BER_BVSTR( bv, "one" );
+ break;
+
+ case LDAP_SCOPE_SUBTREE:
+ BER_BVSTR( bv, "sub" );
+ break;
+
+ case LDAP_SCOPE_SUBORDINATE:
+ BER_BVSTR( bv, "subordinate" );
+ break;
+
+ default:
+ return LDAP_OTHER;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+const char *
+ldap_pvt_scope2str( int scope )
+{
+ struct berval bv;
+
+ if ( ldap_pvt_scope2bv( scope, &bv ) == LDAP_SUCCESS ) {
+ return bv.bv_val;
+ }
+
+ return NULL;
+}
+
+int
+ldap_pvt_bv2scope( struct berval *bv )
+{
+ static struct {
+ struct berval bv;
+ int scope;
+ } v[] = {
+ { BER_BVC( "one" ), LDAP_SCOPE_ONELEVEL },
+ { BER_BVC( "onelevel" ), LDAP_SCOPE_ONELEVEL },
+ { BER_BVC( "base" ), LDAP_SCOPE_BASE },
+ { BER_BVC( "sub" ), LDAP_SCOPE_SUBTREE },
+ { BER_BVC( "subtree" ), LDAP_SCOPE_SUBTREE },
+ { BER_BVC( "subord" ), LDAP_SCOPE_SUBORDINATE },
+ { BER_BVC( "subordinate" ), LDAP_SCOPE_SUBORDINATE },
+ { BER_BVC( "children" ), LDAP_SCOPE_SUBORDINATE },
+ { BER_BVNULL, -1 }
+ };
+ int i;
+
+ for ( i = 0; v[ i ].scope != -1; i++ ) {
+ if ( ber_bvstrcasecmp( bv, &v[ i ].bv ) == 0 ) {
+ return v[ i ].scope;
+ }
+ }
+
+ return( -1 );
+}
+
+int
+ldap_pvt_str2scope( const char *p )
+{
+ struct berval bv;
+
+ ber_str2bv( p, 0, 0, &bv );
+
+ return ldap_pvt_bv2scope( &bv );
+}
+
+static const char hex[] = "0123456789ABCDEF";
+
+#define URLESC_NONE 0x0000U
+#define URLESC_COMMA 0x0001U
+#define URLESC_SLASH 0x0002U
+
+static int
+hex_escape_len( const char *s, unsigned list )
+{
+ int len;
+
+ if ( s == NULL ) {
+ return 0;
+ }
+
+ for ( len = 0; s[0]; s++ ) {
+ switch ( s[0] ) {
+ /* RFC 2396: reserved */
+ case '?':
+ len += 3;
+ break;
+
+ case ',':
+ if ( list & URLESC_COMMA ) {
+ len += 3;
+ } else {
+ len++;
+ }
+ break;
+
+ case '/':
+ if ( list & URLESC_SLASH ) {
+ len += 3;
+ } else {
+ len++;
+ }
+ break;
+
+ case ';':
+ case ':':
+ case '@':
+ case '&':
+ case '=':
+ case '+':
+ case '$':
+
+ /* RFC 2396: unreserved mark */
+ case '-':
+ case '_':
+ case '.':
+ case '!':
+ case '~':
+ case '*':
+ case '\'':
+ case '(':
+ case ')':
+ len++;
+ break;
+
+ /* RFC 2396: unreserved alphanum */
+ default:
+ if ( !isalnum( (unsigned char) s[0] ) ) {
+ len += 3;
+ } else {
+ len++;
+ }
+ break;
+ }
+ }
+
+ return len;
+}
+
+static int
+hex_escape( char *buf, int len, const char *s, unsigned list )
+{
+ int i;
+ int pos;
+
+ if ( s == NULL ) {
+ return 0;
+ }
+
+ for ( pos = 0, i = 0; s[i] && pos < len; i++ ) {
+ int escape = 0;
+
+ switch ( s[i] ) {
+ /* RFC 2396: reserved */
+ case '?':
+ escape = 1;
+ break;
+
+ case ',':
+ if ( list & URLESC_COMMA ) {
+ escape = 1;
+ }
+ break;
+
+ case '/':
+ if ( list & URLESC_SLASH ) {
+ escape = 1;
+ }
+ break;
+
+ case ';':
+ case ':':
+ case '@':
+ case '&':
+ case '=':
+ case '+':
+ case '$':
+
+ /* RFC 2396: unreserved mark */
+ case '-':
+ case '_':
+ case '.':
+ case '!':
+ case '~':
+ case '*':
+ case '\'':
+ case '(':
+ case ')':
+ break;
+
+ /* RFC 2396: unreserved alphanum */
+ default:
+ if ( !isalnum( (unsigned char) s[i] ) ) {
+ escape = 1;
+ }
+ break;
+ }
+
+ if ( escape ) {
+ buf[pos++] = '%';
+ buf[pos++] = hex[ (s[i] >> 4) & 0x0f ];
+ buf[pos++] = hex[ s[i] & 0x0f ];
+
+ } else {
+ buf[pos++] = s[i];
+ }
+ }
+
+ buf[pos] = '\0';
+
+ return pos;
+}
+
+static int
+hex_escape_len_list( char **s, unsigned flags )
+{
+ int len;
+ int i;
+
+ if ( s == NULL ) {
+ return 0;
+ }
+
+ len = 0;
+ for ( i = 0; s[i] != NULL; i++ ) {
+ if ( len ) {
+ len++;
+ }
+ len += hex_escape_len( s[i], flags );
+ }
+
+ return len;
+}
+
+static int
+hex_escape_list( char *buf, int len, char **s, unsigned flags )
+{
+ int pos;
+ int i;
+
+ if ( s == NULL ) {
+ return 0;
+ }
+
+ pos = 0;
+ for ( i = 0; s[i] != NULL; i++ ) {
+ int curlen;
+
+ if ( pos ) {
+ buf[pos++] = ',';
+ len--;
+ }
+ curlen = hex_escape( &buf[pos], len, s[i], flags );
+ len -= curlen;
+ pos += curlen;
+ }
+
+ return pos;
+}
+
+static int
+desc2str_len( LDAPURLDesc *u )
+{
+ int sep = 0;
+ int len = 0;
+ int is_ipc = 0;
+ struct berval scope;
+
+ if ( u == NULL || u->lud_scheme == NULL ) {
+ return -1;
+ }
+
+ if ( !strcmp( "ldapi", u->lud_scheme )) {
+ is_ipc = 1;
+ }
+
+ if ( u->lud_exts ) {
+ len += hex_escape_len_list( u->lud_exts, URLESC_COMMA );
+ if ( !sep ) {
+ sep = 5;
+ }
+ }
+
+ if ( u->lud_filter ) {
+ len += hex_escape_len( u->lud_filter, URLESC_NONE );
+ if ( !sep ) {
+ sep = 4;
+ }
+ }
+
+ if ( ldap_pvt_scope2bv( u->lud_scope, &scope ) == LDAP_SUCCESS ) {
+ len += scope.bv_len;
+ if ( !sep ) {
+ sep = 3;
+ }
+ }
+
+ if ( u->lud_attrs ) {
+ len += hex_escape_len_list( u->lud_attrs, URLESC_NONE );
+ if ( !sep ) {
+ sep = 2;
+ }
+ }
+
+ if ( u->lud_dn && u->lud_dn[0] ) {
+ len += hex_escape_len( u->lud_dn, URLESC_NONE );
+ if ( !sep ) {
+ sep = 1;
+ }
+ };
+
+ len += sep;
+
+ if ( u->lud_port ) {
+ unsigned p = u->lud_port;
+ if ( p > 65535 )
+ return -1;
+
+ len += (p > 999 ? 5 + (p > 9999) : p > 99 ? 4 : 2 + (p > 9));
+ }
+
+ if ( u->lud_host && u->lud_host[0] ) {
+ char *ptr;
+ len += hex_escape_len( u->lud_host, URLESC_SLASH );
+ if ( !is_ipc && ( ptr = strchr( u->lud_host, ':' ))) {
+ if ( strchr( ptr+1, ':' ))
+ len += 2; /* IPv6, [] */
+ }
+ }
+
+ len += strlen( u->lud_scheme ) + STRLENOF( "://" );
+
+ return len;
+}
+
+static int
+desc2str( LDAPURLDesc *u, char *s, int len )
+{
+ int i;
+ int sep = 0;
+ int sofar = 0;
+ int is_v6 = 0;
+ int is_ipc = 0;
+ struct berval scope = BER_BVNULL;
+ char *ptr;
+
+ if ( u == NULL ) {
+ return -1;
+ }
+
+ if ( s == NULL ) {
+ return -1;
+ }
+
+ if ( u->lud_scheme && !strcmp( "ldapi", u->lud_scheme )) {
+ is_ipc = 1;
+ }
+
+ ldap_pvt_scope2bv( u->lud_scope, &scope );
+
+ if ( u->lud_exts ) {
+ sep = 5;
+ } else if ( u->lud_filter ) {
+ sep = 4;
+ } else if ( !BER_BVISEMPTY( &scope ) ) {
+ sep = 3;
+ } else if ( u->lud_attrs ) {
+ sep = 2;
+ } else if ( u->lud_dn && u->lud_dn[0] ) {
+ sep = 1;
+ }
+
+ if ( !is_ipc && u->lud_host && ( ptr = strchr( u->lud_host, ':' ))) {
+ if ( strchr( ptr+1, ':' ))
+ is_v6 = 1;
+ }
+
+ if ( u->lud_port ) {
+ sofar = sprintf( s, "%s://%s%s%s:%d", u->lud_scheme,
+ is_v6 ? "[" : "",
+ u->lud_host ? u->lud_host : "",
+ is_v6 ? "]" : "",
+ u->lud_port );
+ len -= sofar;
+
+ } else {
+ sofar = sprintf( s, "%s://", u->lud_scheme );
+ len -= sofar;
+ if ( u->lud_host && u->lud_host[0] ) {
+ if ( is_v6 ) {
+ s[sofar++] = '[';
+ len--;
+ }
+ i = hex_escape( &s[sofar], len, u->lud_host, URLESC_SLASH );
+ sofar += i;
+ len -= i;
+ if ( is_v6 ) {
+ s[sofar++] = ']';
+ len--;
+ }
+ }
+ }
+
+ assert( len >= 0 );
+
+ if ( sep < 1 ) {
+ goto done;
+ }
+
+ s[sofar++] = '/';
+ len--;
+
+ assert( len >= 0 );
+
+ if ( u->lud_dn && u->lud_dn[0] ) {
+ i = hex_escape( &s[sofar], len, u->lud_dn, URLESC_NONE );
+ sofar += i;
+ len -= i;
+
+ assert( len >= 0 );
+ }
+
+ if ( sep < 2 ) {
+ goto done;
+ }
+ s[sofar++] = '?';
+ len--;
+
+ assert( len >= 0 );
+
+ i = hex_escape_list( &s[sofar], len, u->lud_attrs, URLESC_NONE );
+ sofar += i;
+ len -= i;
+
+ assert( len >= 0 );
+
+ if ( sep < 3 ) {
+ goto done;
+ }
+ s[sofar++] = '?';
+ len--;
+
+ assert( len >= 0 );
+
+ if ( !BER_BVISNULL( &scope ) ) {
+ strcpy( &s[sofar], scope.bv_val );
+ sofar += scope.bv_len;
+ len -= scope.bv_len;
+ }
+
+ assert( len >= 0 );
+
+ if ( sep < 4 ) {
+ goto done;
+ }
+ s[sofar++] = '?';
+ len--;
+
+ assert( len >= 0 );
+
+ i = hex_escape( &s[sofar], len, u->lud_filter, URLESC_NONE );
+ sofar += i;
+ len -= i;
+
+ assert( len >= 0 );
+
+ if ( sep < 5 ) {
+ goto done;
+ }
+ s[sofar++] = '?';
+ len--;
+
+ assert( len >= 0 );
+
+ i = hex_escape_list( &s[sofar], len, u->lud_exts, URLESC_COMMA );
+ sofar += i;
+ len -= i;
+
+ assert( len >= 0 );
+
+done:
+ if ( len < 0 ) {
+ return -1;
+ }
+
+ return sofar;
+}
+
+char *
+ldap_url_desc2str( LDAPURLDesc *u )
+{
+ int len;
+ char *s;
+
+ if ( u == NULL ) {
+ return NULL;
+ }
+
+ len = desc2str_len( u );
+ if ( len < 0 ) {
+ return NULL;
+ }
+
+ /* allocate enough to hex escape everything -- overkill */
+ s = LDAP_MALLOC( len + 1 );
+
+ if ( s == NULL ) {
+ return NULL;
+ }
+
+ if ( desc2str( u, s, len ) != len ) {
+ LDAP_FREE( s );
+ return NULL;
+ }
+
+ s[len] = '\0';
+
+ return s;
+}
+
+int
+ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp, unsigned flags )
+{
+/*
+ * Pick apart the pieces of an LDAP URL.
+ */
+
+ LDAPURLDesc *ludp;
+ char *p, *q, *r;
+ int i, enclosed, proto, is_v6 = 0;
+ const char *scheme = NULL;
+ const char *url_tmp;
+ char *url;
+
+ int check_dn = 1;
+
+ if( url_in == NULL || ludpp == NULL ) {
+ return LDAP_URL_ERR_PARAM;
+ }
+
+#ifndef LDAP_INT_IN_KERNEL
+ /* Global options may not be created yet
+ * We can't test if the global options are initialized
+ * because a call to LDAP_INT_GLOBAL_OPT() will try to allocate
+ * the options and cause infinite recursion
+ */
+ Debug1( LDAP_DEBUG_TRACE, "ldap_url_parse_ext(%s)\n", url_in );
+#endif
+
+ *ludpp = NULL; /* pessimistic */
+
+ url_tmp = skip_url_prefix( url_in, &enclosed, &scheme );
+
+ if ( url_tmp == NULL ) {
+ return LDAP_URL_ERR_BADSCHEME;
+ }
+
+ assert( scheme != NULL );
+
+ proto = ldap_pvt_url_scheme2proto( scheme );
+ if ( proto == -1 ) {
+ return LDAP_URL_ERR_BADSCHEME;
+ }
+
+ /* make working copy of the remainder of the URL */
+ url = LDAP_STRDUP( url_tmp );
+ if ( url == NULL ) {
+ return LDAP_URL_ERR_MEM;
+ }
+
+ if ( enclosed ) {
+ p = &url[strlen(url)-1];
+
+ if( *p != '>' ) {
+ LDAP_FREE( url );
+ return LDAP_URL_ERR_BADENCLOSURE;
+ }
+
+ *p = '\0';
+ }
+
+ /* allocate return struct */
+ ludp = (LDAPURLDesc *)LDAP_CALLOC( 1, sizeof( LDAPURLDesc ));
+
+ if ( ludp == NULL ) {
+ LDAP_FREE( url );
+ return LDAP_URL_ERR_MEM;
+ }
+
+ ludp->lud_next = NULL;
+ ludp->lud_host = NULL;
+ ludp->lud_port = 0;
+ ludp->lud_dn = NULL;
+ ludp->lud_attrs = NULL;
+ ludp->lud_scope = ( flags & LDAP_PVT_URL_PARSE_NODEF_SCOPE ) ? LDAP_SCOPE_BASE : LDAP_SCOPE_DEFAULT;
+ ludp->lud_filter = NULL;
+ ludp->lud_exts = NULL;
+
+ ludp->lud_scheme = LDAP_STRDUP( scheme );
+
+ if ( ludp->lud_scheme == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_MEM;
+ }
+
+ /* scan forward for '/' that marks end of hostport and begin. of dn */
+ p = strchr( url, '/' );
+ q = NULL;
+
+ if( p != NULL ) {
+ /* terminate hostport; point to start of dn */
+ *p++ = '\0';
+ } else {
+ /* check for Novell kludge, see below */
+ p = strchr( url, '?' );
+ if ( p ) {
+ *p++ = '\0';
+ q = p;
+ p = NULL;
+ }
+ }
+
+ if ( proto != LDAP_PROTO_IPC ) {
+ /* IPv6 syntax with [ip address]:port */
+ if ( *url == '[' ) {
+ r = strchr( url, ']' );
+ if ( r == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADURL;
+ }
+ *r++ = '\0';
+ q = strchr( r, ':' );
+ if ( q && q != r ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADURL;
+ }
+ is_v6 = 1;
+ } else {
+ q = strchr( url, ':' );
+ }
+
+ if ( q != NULL ) {
+ char *next;
+
+ *q++ = '\0';
+ ldap_pvt_hex_unescape( q );
+
+ if( *q == '\0' ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADURL;
+ }
+
+ ludp->lud_port = strtol( q, &next, 10 );
+ if ( next == q || next[0] != '\0' ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADURL;
+ }
+ /* check for Novell kludge */
+ if ( !p ) {
+ if ( *next != '\0' ) {
+ q = &next[1];
+ } else {
+ q = NULL;
+ }
+ }
+ }
+
+ if ( ( flags & LDAP_PVT_URL_PARSE_DEF_PORT ) && ludp->lud_port == 0 ) {
+ if ( strcmp( ludp->lud_scheme, "ldaps" ) == 0 ) {
+ ludp->lud_port = LDAPS_PORT;
+ } else {
+ ludp->lud_port = LDAP_PORT;
+ }
+ }
+ }
+
+ ldap_pvt_hex_unescape( url );
+
+ /* If [ip address]:port syntax, url is [ip and we skip the [ */
+ ludp->lud_host = LDAP_STRDUP( url + is_v6 );
+
+ if( ludp->lud_host == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_MEM;
+ }
+
+ if ( ( flags & LDAP_PVT_URL_PARSE_NOEMPTY_HOST )
+ && ludp->lud_host != NULL
+ && *ludp->lud_host == '\0' )
+ {
+ LDAP_FREE( ludp->lud_host );
+ ludp->lud_host = NULL;
+ }
+
+ /*
+ * Kludge. ldap://111.222.333.444:389??cn=abc,o=company
+ *
+ * On early Novell releases, search references/referrals were returned
+ * in this format, i.e., the dn was kind of in the scope position,
+ * but the required slash is missing. The whole thing is illegal syntax,
+ * but we need to account for it. Fortunately it can't be confused with
+ * anything real.
+ */
+ if( (p == NULL) && (q != NULL) && (*q == '?') ) {
+ /* ? immediately followed by question */
+ q++;
+ if( *q != '\0' ) {
+ /* parse dn part */
+ ldap_pvt_hex_unescape( q );
+ ludp->lud_dn = LDAP_STRDUP( q );
+
+ } else if ( !( flags & LDAP_PVT_URL_PARSE_NOEMPTY_DN ) ) {
+ ludp->lud_dn = LDAP_STRDUP( "" );
+
+ } else {
+ check_dn = 0;
+ }
+
+ if ( check_dn && ludp->lud_dn == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_MEM;
+ }
+ }
+
+ if( p == NULL ) {
+ LDAP_FREE( url );
+ *ludpp = ludp;
+ return LDAP_URL_SUCCESS;
+ }
+
+ /* scan forward for '?' that may marks end of dn */
+ q = strchr( p, '?' );
+
+ if( q != NULL ) {
+ /* terminate dn part */
+ *q++ = '\0';
+ }
+
+ if( *p != '\0' ) {
+ /* parse dn part */
+ ldap_pvt_hex_unescape( p );
+ ludp->lud_dn = LDAP_STRDUP( p );
+
+ } else if ( !( flags & LDAP_PVT_URL_PARSE_NOEMPTY_DN ) ) {
+ ludp->lud_dn = LDAP_STRDUP( "" );
+
+ } else {
+ check_dn = 0;
+ }
+
+ if( check_dn && ludp->lud_dn == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_MEM;
+ }
+
+ if( q == NULL ) {
+ /* no more */
+ LDAP_FREE( url );
+ *ludpp = ludp;
+ return LDAP_URL_SUCCESS;
+ }
+
+ /* scan forward for '?' that may marks end of attributes */
+ p = q;
+ q = strchr( p, '?' );
+
+ if( q != NULL ) {
+ /* terminate attributes part */
+ *q++ = '\0';
+ }
+
+ if( *p != '\0' ) {
+ /* parse attributes */
+ ldap_pvt_hex_unescape( p );
+ ludp->lud_attrs = ldap_str2charray( p, "," );
+
+ if( ludp->lud_attrs == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADATTRS;
+ }
+ }
+
+ if ( q == NULL ) {
+ /* no more */
+ LDAP_FREE( url );
+ *ludpp = ludp;
+ return LDAP_URL_SUCCESS;
+ }
+
+ /* scan forward for '?' that may marks end of scope */
+ p = q;
+ q = strchr( p, '?' );
+
+ if( q != NULL ) {
+ /* terminate the scope part */
+ *q++ = '\0';
+ }
+
+ if( *p != '\0' ) {
+ /* parse the scope */
+ ldap_pvt_hex_unescape( p );
+ ludp->lud_scope = ldap_pvt_str2scope( p );
+
+ if( ludp->lud_scope == -1 ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADSCOPE;
+ }
+ }
+
+ if ( q == NULL ) {
+ /* no more */
+ LDAP_FREE( url );
+ *ludpp = ludp;
+ return LDAP_URL_SUCCESS;
+ }
+
+ /* scan forward for '?' that may marks end of filter */
+ p = q;
+ q = strchr( p, '?' );
+
+ if( q != NULL ) {
+ /* terminate the filter part */
+ *q++ = '\0';
+ }
+
+ if( *p != '\0' ) {
+ /* parse the filter */
+ ldap_pvt_hex_unescape( p );
+
+ if( ! *p ) {
+ /* missing filter */
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADFILTER;
+ }
+
+ ludp->lud_filter = LDAP_STRDUP( p );
+
+ if( ludp->lud_filter == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_MEM;
+ }
+ }
+
+ if ( q == NULL ) {
+ /* no more */
+ LDAP_FREE( url );
+ *ludpp = ludp;
+ return LDAP_URL_SUCCESS;
+ }
+
+ /* scan forward for '?' that may marks end of extensions */
+ p = q;
+ q = strchr( p, '?' );
+
+ if( q != NULL ) {
+ /* extra '?' */
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADURL;
+ }
+
+ /* parse the extensions */
+ ludp->lud_exts = ldap_str2charray( p, "," );
+
+ if( ludp->lud_exts == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADEXTS;
+ }
+
+ for( i=0; ludp->lud_exts[i] != NULL; i++ ) {
+ ldap_pvt_hex_unescape( ludp->lud_exts[i] );
+
+ if( *ludp->lud_exts[i] == '!' ) {
+ /* count the number of critical extensions */
+ ludp->lud_crit_exts++;
+ }
+ }
+
+ if( i == 0 ) {
+ /* must have 1 or more */
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADEXTS;
+ }
+
+ /* no more */
+ *ludpp = ludp;
+ LDAP_FREE( url );
+ return LDAP_URL_SUCCESS;
+}
+
+int
+ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp )
+{
+ return ldap_url_parse_ext( url_in, ludpp, LDAP_PVT_URL_PARSE_HISTORIC );
+}
+
+LDAPURLDesc *
+ldap_url_dup ( LDAPURLDesc *ludp )
+{
+ LDAPURLDesc *dest;
+
+ if ( ludp == NULL ) {
+ return NULL;
+ }
+
+ dest = LDAP_MALLOC( sizeof(LDAPURLDesc) );
+ if (dest == NULL)
+ return NULL;
+
+ *dest = *ludp;
+ dest->lud_scheme = NULL;
+ dest->lud_host = NULL;
+ dest->lud_dn = NULL;
+ dest->lud_filter = NULL;
+ dest->lud_attrs = NULL;
+ dest->lud_exts = NULL;
+ dest->lud_next = NULL;
+
+ if ( ludp->lud_scheme != NULL ) {
+ dest->lud_scheme = LDAP_STRDUP( ludp->lud_scheme );
+ if (dest->lud_scheme == NULL) {
+ ldap_free_urldesc(dest);
+ return NULL;
+ }
+ }
+
+ if ( ludp->lud_host != NULL ) {
+ dest->lud_host = LDAP_STRDUP( ludp->lud_host );
+ if (dest->lud_host == NULL) {
+ ldap_free_urldesc(dest);
+ return NULL;
+ }
+ }
+
+ if ( ludp->lud_dn != NULL ) {
+ dest->lud_dn = LDAP_STRDUP( ludp->lud_dn );
+ if (dest->lud_dn == NULL) {
+ ldap_free_urldesc(dest);
+ return NULL;
+ }
+ }
+
+ if ( ludp->lud_filter != NULL ) {
+ dest->lud_filter = LDAP_STRDUP( ludp->lud_filter );
+ if (dest->lud_filter == NULL) {
+ ldap_free_urldesc(dest);
+ return NULL;
+ }
+ }
+
+ if ( ludp->lud_attrs != NULL ) {
+ dest->lud_attrs = ldap_charray_dup( ludp->lud_attrs );
+ if (dest->lud_attrs == NULL) {
+ ldap_free_urldesc(dest);
+ return NULL;
+ }
+ }
+
+ if ( ludp->lud_exts != NULL ) {
+ dest->lud_exts = ldap_charray_dup( ludp->lud_exts );
+ if (dest->lud_exts == NULL) {
+ ldap_free_urldesc(dest);
+ return NULL;
+ }
+ }
+
+ return dest;
+}
+
+LDAPURLDesc *
+ldap_url_duplist (LDAPURLDesc *ludlist)
+{
+ LDAPURLDesc *dest, *tail, *ludp, *newludp;
+
+ dest = NULL;
+ tail = NULL;
+ for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
+ newludp = ldap_url_dup(ludp);
+ if (newludp == NULL) {
+ ldap_free_urllist(dest);
+ return NULL;
+ }
+ if (tail == NULL)
+ dest = newludp;
+ else
+ tail->lud_next = newludp;
+ tail = newludp;
+ }
+ return dest;
+}
+
+static int
+ldap_url_parselist_int (LDAPURLDesc **ludlist, const char *url, const char *sep, unsigned flags )
+
+{
+ int i, rc;
+ LDAPURLDesc *ludp;
+ char **urls;
+
+ assert( ludlist != NULL );
+ assert( url != NULL );
+
+ *ludlist = NULL;
+
+ if ( sep == NULL ) {
+ sep = ", ";
+ }
+
+ urls = ldap_str2charray( url, sep );
+ if (urls == NULL)
+ return LDAP_URL_ERR_MEM;
+
+ /* count the URLs... */
+ for (i = 0; urls[i] != NULL; i++) ;
+ /* ...and put them in the "stack" backward */
+ while (--i >= 0) {
+ rc = ldap_url_parse_ext( urls[i], &ludp, flags );
+ if ( rc != 0 ) {
+ ldap_charray_free( urls );
+ ldap_free_urllist( *ludlist );
+ *ludlist = NULL;
+ return rc;
+ }
+ ludp->lud_next = *ludlist;
+ *ludlist = ludp;
+ }
+ ldap_charray_free( urls );
+ return LDAP_URL_SUCCESS;
+}
+
+int
+ldap_url_parselist (LDAPURLDesc **ludlist, const char *url )
+{
+ return ldap_url_parselist_int( ludlist, url, ", ", LDAP_PVT_URL_PARSE_HISTORIC );
+}
+
+int
+ldap_url_parselist_ext (LDAPURLDesc **ludlist, const char *url, const char *sep, unsigned flags )
+{
+ return ldap_url_parselist_int( ludlist, url, sep, flags );
+}
+
+int
+ldap_url_parsehosts(
+ LDAPURLDesc **ludlist,
+ const char *hosts,
+ int port )
+{
+ int i;
+ LDAPURLDesc *ludp;
+ char **specs, *p;
+
+ assert( ludlist != NULL );
+ assert( hosts != NULL );
+
+ *ludlist = NULL;
+
+ specs = ldap_str2charray(hosts, ", ");
+ if (specs == NULL)
+ return LDAP_NO_MEMORY;
+
+ /* count the URLs... */
+ for (i = 0; specs[i] != NULL; i++) /* EMPTY */;
+
+ /* ...and put them in the "stack" backward */
+ while (--i >= 0) {
+ ludp = LDAP_CALLOC( 1, sizeof(LDAPURLDesc) );
+ if (ludp == NULL) {
+ ldap_charray_free(specs);
+ ldap_free_urllist(*ludlist);
+ *ludlist = NULL;
+ return LDAP_NO_MEMORY;
+ }
+ ludp->lud_port = port;
+ ludp->lud_host = specs[i];
+ specs[i] = NULL;
+ p = strchr(ludp->lud_host, ':');
+ if (p != NULL) {
+ /* more than one :, IPv6 address */
+ if ( strchr(p+1, ':') != NULL ) {
+ /* allow [address] and [address]:port */
+ if ( *ludp->lud_host == '[' ) {
+ p = LDAP_STRDUP(ludp->lud_host+1);
+ /* copied, make sure we free source later */
+ specs[i] = ludp->lud_host;
+ ludp->lud_host = p;
+ p = strchr( ludp->lud_host, ']' );
+ if ( p == NULL ) {
+ LDAP_FREE(ludp);
+ ldap_charray_free(specs);
+ return LDAP_PARAM_ERROR;
+ }
+ *p++ = '\0';
+ if ( *p != ':' ) {
+ if ( *p != '\0' ) {
+ LDAP_FREE(ludp);
+ ldap_charray_free(specs);
+ return LDAP_PARAM_ERROR;
+ }
+ p = NULL;
+ }
+ } else {
+ p = NULL;
+ }
+ }
+ if (p != NULL) {
+ char *next;
+
+ *p++ = 0;
+ ldap_pvt_hex_unescape(p);
+ ludp->lud_port = strtol( p, &next, 10 );
+ if ( next == p || next[0] != '\0' ) {
+ LDAP_FREE(ludp);
+ ldap_charray_free(specs);
+ return LDAP_PARAM_ERROR;
+ }
+ }
+ }
+ ldap_pvt_hex_unescape(ludp->lud_host);
+ ludp->lud_scheme = LDAP_STRDUP("ldap");
+ ludp->lud_next = *ludlist;
+ *ludlist = ludp;
+ }
+
+ /* this should be an array of NULLs now */
+ /* except entries starting with [ */
+ ldap_charray_free(specs);
+ return LDAP_SUCCESS;
+}
+
+char *
+ldap_url_list2hosts (LDAPURLDesc *ludlist)
+{
+ LDAPURLDesc *ludp;
+ int size;
+ char *s, *p, buf[32]; /* big enough to hold a long decimal # (overkill) */
+
+ if (ludlist == NULL)
+ return NULL;
+
+ /* figure out how big the string is */
+ size = 1; /* nul-term */
+ for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
+ if ( ludp->lud_host == NULL ) continue;
+ size += strlen(ludp->lud_host) + 1; /* host and space */
+ if (strchr(ludp->lud_host, ':')) /* will add [ ] below */
+ size += 2;
+ if (ludp->lud_port != 0)
+ size += sprintf(buf, ":%d", ludp->lud_port);
+ }
+ s = LDAP_MALLOC(size);
+ if (s == NULL)
+ return NULL;
+
+ p = s;
+ for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
+ if ( ludp->lud_host == NULL ) continue;
+ if (strchr(ludp->lud_host, ':')) {
+ p += sprintf(p, "[%s]", ludp->lud_host);
+ } else {
+ strcpy(p, ludp->lud_host);
+ p += strlen(ludp->lud_host);
+ }
+ if (ludp->lud_port != 0)
+ p += sprintf(p, ":%d", ludp->lud_port);
+ *p++ = ' ';
+ }
+ if (p != s)
+ p--; /* nuke that extra space */
+ *p = '\0';
+ return s;
+}
+
+char *
+ldap_url_list2urls(
+ LDAPURLDesc *ludlist )
+{
+ LDAPURLDesc *ludp;
+ int size, sofar;
+ char *s;
+
+ if ( ludlist == NULL ) {
+ return NULL;
+ }
+
+ /* figure out how big the string is */
+ for ( size = 0, ludp = ludlist; ludp != NULL; ludp = ludp->lud_next ) {
+ int len = desc2str_len( ludp );
+ if ( len < 0 ) {
+ return NULL;
+ }
+ size += len + 1;
+ }
+
+ s = LDAP_MALLOC( size );
+
+ if ( s == NULL ) {
+ return NULL;
+ }
+
+ for ( sofar = 0, ludp = ludlist; ludp != NULL; ludp = ludp->lud_next ) {
+ int len;
+
+ len = desc2str( ludp, &s[sofar], size );
+
+ if ( len < 0 ) {
+ LDAP_FREE( s );
+ return NULL;
+ }
+
+ sofar += len;
+ size -= len;
+
+ s[sofar++] = ' ';
+ size--;
+
+ assert( size >= 0 );
+ }
+
+ s[sofar - 1] = '\0';
+
+ return s;
+}
+
+void
+ldap_free_urllist( LDAPURLDesc *ludlist )
+{
+ LDAPURLDesc *ludp, *next;
+
+ for (ludp = ludlist; ludp != NULL; ludp = next) {
+ next = ludp->lud_next;
+ ldap_free_urldesc(ludp);
+ }
+}
+
+void
+ldap_free_urldesc( LDAPURLDesc *ludp )
+{
+ if ( ludp == NULL ) {
+ return;
+ }
+
+ if ( ludp->lud_scheme != NULL ) {
+ LDAP_FREE( ludp->lud_scheme );
+ }
+
+ if ( ludp->lud_host != NULL ) {
+ LDAP_FREE( ludp->lud_host );
+ }
+
+ if ( ludp->lud_dn != NULL ) {
+ LDAP_FREE( ludp->lud_dn );
+ }
+
+ if ( ludp->lud_filter != NULL ) {
+ LDAP_FREE( ludp->lud_filter);
+ }
+
+ if ( ludp->lud_attrs != NULL ) {
+ LDAP_VFREE( ludp->lud_attrs );
+ }
+
+ if ( ludp->lud_exts != NULL ) {
+ LDAP_VFREE( ludp->lud_exts );
+ }
+
+ LDAP_FREE( ludp );
+}
+
+static int
+ldap_int_is_hexpair( char *s )
+{
+ int i;
+
+ for ( i = 0; i < 2; i++ ) {
+ if ( s[i] >= '0' && s[i] <= '9' ) {
+ continue;
+ }
+
+ if ( s[i] >= 'A' && s[i] <= 'F' ) {
+ continue;
+ }
+
+ if ( s[i] >= 'a' && s[i] <= 'f' ) {
+ continue;
+ }
+
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+ldap_int_unhex( int c )
+{
+ return( c >= '0' && c <= '9' ? c - '0'
+ : c >= 'A' && c <= 'F' ? c - 'A' + 10
+ : c - 'a' + 10 );
+}
+
+void
+ldap_pvt_hex_unescape( char *s )
+{
+ /*
+ * Remove URL hex escapes from s... done in place. The basic concept for
+ * this routine is borrowed from the WWW library HTUnEscape() routine.
+ */
+ char *p,
+ *save_s = s;
+
+ for ( p = s; *s != '\0'; ++s ) {
+ if ( *s == '%' ) {
+ /*
+ * FIXME: what if '%' is followed
+ * by non-hexpair chars?
+ */
+ if ( !ldap_int_is_hexpair( s + 1 ) ) {
+ p = save_s;
+ break;
+ }
+
+ if ( *++s == '\0' ) {
+ break;
+ }
+ *p = ldap_int_unhex( *s ) << 4;
+ if ( *++s == '\0' ) {
+ break;
+ }
+ *p++ += ldap_int_unhex( *s );
+ } else {
+ *p++ = *s;
+ }
+ }
+
+ *p = '\0';
+}
+
diff --git a/libraries/libldap/urltest.c b/libraries/libldap/urltest.c
new file mode 100644
index 0000000..8c86759
--- /dev/null
+++ b/libraries/libldap/urltest.c
@@ -0,0 +1,128 @@
+/* urltest.c -- OpenLDAP URL API Test Program */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This program was initially developed by Pierangelo Masarati
+ * <ando@OpenLDAP.org> for inclusion in OpenLDAP Software.
+ */
+
+/*
+ * This program is designed to test the ldap_url_* functions
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include <ldap.h>
+
+#include "ldap-int.h"
+
+#include "ldap_defaults.h"
+
+int
+main(int argc, char *argv[])
+{
+ const char *url,
+ *scope = NULL;
+ LDAPURLDesc *lud;
+ enum {
+ IS_LDAP = 0,
+ IS_LDAPS,
+ IS_LDAPI
+ } type = IS_LDAP;
+ int rc;
+
+ if ( argc != 2 ) {
+ fprintf( stderr, "usage: urltest <url>\n" );
+ exit( EXIT_FAILURE );
+ }
+
+ url = argv[ 1 ];
+
+ if ( ldap_is_ldaps_url( url ) ) {
+ fprintf( stdout, "LDAPS url\n" );
+ type = IS_LDAPS;
+
+ } else if ( ldap_is_ldapi_url( url ) ) {
+ fprintf( stdout, "LDAPI url\n" );
+ type = IS_LDAPI;
+
+ } else if ( ldap_is_ldap_url( url ) ) {
+ fprintf( stdout, "generic LDAP url\n" );
+
+ } else {
+ fprintf( stderr, "Need a valid LDAP url\n" );
+ exit( EXIT_FAILURE );
+ }
+
+ rc = ldap_url_parse( url, &lud );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ fprintf( stderr, "ldap_url_parse(%s) failed (%d)\n", url, rc );
+ exit( EXIT_FAILURE );
+ }
+
+ fprintf( stdout, "PROTO: %s\n", lud->lud_scheme );
+ switch ( type ) {
+ case IS_LDAPI:
+ fprintf( stdout, "PATH: %s\n", lud->lud_host );
+ break;
+
+ default:
+ fprintf( stdout, "HOST: %s\n", lud->lud_host );
+ if ( lud->lud_port != 0 ) {
+ fprintf( stdout, "PORT: %d\n", lud->lud_port );
+ }
+ }
+
+ if ( lud->lud_dn && lud->lud_dn[ 0 ] ) {
+ fprintf( stdout, "DN: %s\n", lud->lud_dn );
+ }
+
+ if ( lud->lud_attrs ) {
+ int i;
+
+ fprintf( stdout, "ATTRS:\n" );
+ for ( i = 0; lud->lud_attrs[ i ]; i++ ) {
+ fprintf( stdout, "\t%s\n", lud->lud_attrs[ i ] );
+ }
+ }
+
+ scope = ldap_pvt_scope2str( lud->lud_scope );
+ if ( scope ) {
+ fprintf( stdout, "SCOPE: %s\n", scope );
+ }
+
+ if ( lud->lud_filter ) {
+ fprintf( stdout, "FILTER: %s\n", lud->lud_filter );
+ }
+
+ if ( lud->lud_exts ) {
+ int i;
+
+ fprintf( stdout, "EXTS:\n" );
+ for ( i = 0; lud->lud_exts[ i ]; i++ ) {
+ fprintf( stdout, "\t%s\n", lud->lud_exts[ i ] );
+ }
+ }
+
+ fprintf( stdout, "URL: %s\n", ldap_url_desc2str( lud ));
+
+ return EXIT_SUCCESS;
+}
diff --git a/libraries/libldap/utf-8-conv.c b/libraries/libldap/utf-8-conv.c
new file mode 100644
index 0000000..9d8f9c1
--- /dev/null
+++ b/libraries/libldap/utf-8-conv.c
@@ -0,0 +1,485 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
+ *
+ * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
+ * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT
+ * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS
+ * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE"
+ * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION
+ * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP
+ * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT
+ * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
+ *---
+ * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
+ * can be found in the file "build/LICENSE-2.0.1" in this distribution
+ * of OpenLDAP Software.
+ */
+
+/*
+ * UTF-8 Conversion Routines
+ *
+ * These routines convert between Wide Character and UTF-8,
+ * or between MultiByte and UTF-8 encodings.
+ *
+ * Both single character and string versions of the functions are provided.
+ * All functions return -1 if the character or string cannot be converted.
+ */
+
+#include "portable.h"
+
+#if SIZEOF_WCHAR_T >= 4
+/* These routines assume ( sizeof(wchar_t) >= 4 ) */
+
+#include <stdio.h>
+#include <ac/stdlib.h> /* For wctomb, wcstombs, mbtowc, mbstowcs */
+#include <ac/string.h>
+#include <ac/time.h> /* for time_t */
+
+#include "ldap-int.h"
+
+#include <ldap_utf8.h>
+
+static unsigned char mask[] = { 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 };
+
+
+/*-----------------------------------------------------------------------------
+ UTF-8 Format Summary
+
+ASCII chars 7 bits
+ 0xxxxxxx
+
+2-character UTF-8 sequence: 11 bits
+ 110xxxxx 10xxxxxx
+
+3-character UTF-8 16 bits
+ 1110xxxx 10xxxxxx 10xxxxxx
+
+4-char UTF-8 21 bits
+ 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+
+5-char UTF-8 26 bits
+ 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+
+6-char UTF-8 31 bits
+ 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+
+Unicode address space (0 - 0x10FFFF) 21 bits
+ISO-10646 address space (0 - 0x7FFFFFFF) 31 bits
+
+Note: This code does not prevent UTF-8 sequences which are longer than
+ necessary from being decoded.
+*/
+
+/*-----------------------------------------------------------------------------
+ Convert a UTF-8 character to a wide char.
+ Return the length of the UTF-8 input character in bytes.
+*/
+int
+ldap_x_utf8_to_wc ( wchar_t *wchar, const char *utf8char )
+{
+ int utflen, i;
+ wchar_t ch;
+
+ if (utf8char == NULL) return -1;
+
+ /* Get UTF-8 sequence length from 1st byte */
+ utflen = LDAP_UTF8_CHARLEN2(utf8char, utflen);
+
+ if( utflen==0 || utflen > (int)LDAP_MAX_UTF8_LEN ) return -1;
+
+ /* First byte minus length tag */
+ ch = (wchar_t)(utf8char[0] & mask[utflen]);
+
+ for(i=1; i < utflen; i++) {
+ /* Subsequent bytes must start with 10 */
+ if ((utf8char[i] & 0xc0) != 0x80) return -1;
+
+ ch <<= 6; /* 6 bits of data in each subsequent byte */
+ ch |= (wchar_t)(utf8char[i] & 0x3f);
+ }
+
+ if (wchar) *wchar = ch;
+
+ return utflen;
+}
+
+/*-----------------------------------------------------------------------------
+ Convert a UTF-8 string to a wide char string.
+ No more than 'count' wide chars will be written to the output buffer.
+ Return the size of the converted string in wide chars, excl null terminator.
+*/
+int
+ldap_x_utf8s_to_wcs ( wchar_t *wcstr, const char *utf8str, size_t count )
+{
+ size_t wclen = 0;
+ int utflen, i;
+ wchar_t ch;
+
+
+ /* If input ptr is NULL or empty... */
+ if (utf8str == NULL || !*utf8str) {
+ if ( wcstr )
+ *wcstr = 0;
+ return 0;
+ }
+
+ /* Examine next UTF-8 character. If output buffer is NULL, ignore count */
+ while ( *utf8str && (wcstr==NULL || wclen<count) ) {
+ /* Get UTF-8 sequence length from 1st byte */
+ utflen = LDAP_UTF8_CHARLEN2(utf8str, utflen);
+
+ if( utflen==0 || utflen > (int)LDAP_MAX_UTF8_LEN ) return -1;
+
+ /* First byte minus length tag */
+ ch = (wchar_t)(utf8str[0] & mask[utflen]);
+
+ for(i=1; i < utflen; i++) {
+ /* Subsequent bytes must start with 10 */
+ if ((utf8str[i] & 0xc0) != 0x80) return -1;
+
+ ch <<= 6; /* 6 bits of data in each subsequent byte */
+ ch |= (wchar_t)(utf8str[i] & 0x3f);
+ }
+
+ if (wcstr) wcstr[wclen] = ch;
+
+ utf8str += utflen; /* Move to next UTF-8 character */
+ wclen++; /* Count number of wide chars stored/required */
+ }
+
+ /* Add null terminator if there's room in the buffer. */
+ if (wcstr && wclen < count) wcstr[wclen] = 0;
+
+ return wclen;
+}
+
+
+/*-----------------------------------------------------------------------------
+ Convert one wide char to a UTF-8 character.
+ Return the length of the converted UTF-8 character in bytes.
+ No more than 'count' bytes will be written to the output buffer.
+*/
+int
+ldap_x_wc_to_utf8 ( char *utf8char, wchar_t wchar, size_t count )
+{
+ int len=0;
+
+ if (utf8char == NULL) /* Just determine the required UTF-8 char length. */
+ { /* Ignore count */
+ if( wchar < 0 )
+ return -1;
+ if( wchar < 0x80 )
+ return 1;
+ if( wchar < 0x800 )
+ return 2;
+ if( wchar < 0x10000 )
+ return 3;
+ if( wchar < 0x200000 )
+ return 4;
+ if( wchar < 0x4000000 )
+ return 5;
+#if SIZEOF_WCHAR_T > 4
+ /* UL is not strictly needed by ANSI C */
+ if( wchar < (wchar_t)0x80000000UL )
+#endif /* SIZEOF_WCHAR_T > 4 */
+ return 6;
+ return -1;
+ }
+
+
+ if ( wchar < 0 ) { /* Invalid wide character */
+ len = -1;
+
+ } else if( wchar < 0x80 ) {
+ if (count >= 1) {
+ utf8char[len++] = (char)wchar;
+ }
+
+ } else if( wchar < 0x800 ) {
+ if (count >=2) {
+ utf8char[len++] = 0xc0 | ( wchar >> 6 );
+ utf8char[len++] = 0x80 | ( wchar & 0x3f );
+ }
+
+ } else if( wchar < 0x10000 ) {
+ if (count >= 3) {
+ utf8char[len++] = 0xe0 | ( wchar >> 12 );
+ utf8char[len++] = 0x80 | ( (wchar >> 6) & 0x3f );
+ utf8char[len++] = 0x80 | ( wchar & 0x3f );
+ }
+
+ } else if( wchar < 0x200000 ) {
+ if (count >= 4) {
+ utf8char[len++] = 0xf0 | ( wchar >> 18 );
+ utf8char[len++] = 0x80 | ( (wchar >> 12) & 0x3f );
+ utf8char[len++] = 0x80 | ( (wchar >> 6) & 0x3f );
+ utf8char[len++] = 0x80 | ( wchar & 0x3f );
+ }
+
+ } else if( wchar < 0x4000000 ) {
+ if (count >= 5) {
+ utf8char[len++] = 0xf8 | ( wchar >> 24 );
+ utf8char[len++] = 0x80 | ( (wchar >> 18) & 0x3f );
+ utf8char[len++] = 0x80 | ( (wchar >> 12) & 0x3f );
+ utf8char[len++] = 0x80 | ( (wchar >> 6) & 0x3f );
+ utf8char[len++] = 0x80 | ( wchar & 0x3f );
+ }
+
+ } else
+#if SIZEOF_WCHAR_T > 4
+ /* UL is not strictly needed by ANSI C */
+ if( wchar < (wchar_t)0x80000000UL )
+#endif /* SIZEOF_WCHAR_T > 4 */
+ {
+ if (count >= 6) {
+ utf8char[len++] = 0xfc | ( wchar >> 30 );
+ utf8char[len++] = 0x80 | ( (wchar >> 24) & 0x3f );
+ utf8char[len++] = 0x80 | ( (wchar >> 18) & 0x3f );
+ utf8char[len++] = 0x80 | ( (wchar >> 12) & 0x3f );
+ utf8char[len++] = 0x80 | ( (wchar >> 6) & 0x3f );
+ utf8char[len++] = 0x80 | ( wchar & 0x3f );
+ }
+
+#if SIZEOF_WCHAR_T > 4
+ } else {
+ len = -1;
+#endif /* SIZEOF_WCHAR_T > 4 */
+ }
+
+ return len;
+
+}
+
+
+/*-----------------------------------------------------------------------------
+ Convert a wide char string to a UTF-8 string.
+ No more than 'count' bytes will be written to the output buffer.
+ Return the # of bytes written to the output buffer, excl null terminator.
+*/
+int
+ldap_x_wcs_to_utf8s ( char *utf8str, const wchar_t *wcstr, size_t count )
+{
+ int len = 0;
+ int n;
+ char *p = utf8str;
+ wchar_t empty = 0; /* To avoid use of L"" construct */
+
+ if (wcstr == NULL) /* Treat input ptr NULL as an empty string */
+ wcstr = &empty;
+
+ if (utf8str == NULL) /* Just compute size of output, excl null */
+ {
+ while (*wcstr)
+ {
+ /* Get UTF-8 size of next wide char */
+ n = ldap_x_wc_to_utf8( NULL, *wcstr++, LDAP_MAX_UTF8_LEN);
+ if (n == -1)
+ return -1;
+ len += n;
+ }
+
+ return len;
+ }
+
+
+ /* Do the actual conversion. */
+
+ n = 1; /* In case of empty wcstr */
+ while (*wcstr)
+ {
+ n = ldap_x_wc_to_utf8( p, *wcstr++, count);
+
+ if (n <= 0) /* If encoding error (-1) or won't fit (0), quit */
+ break;
+
+ p += n;
+ count -= n; /* Space left in output buffer */
+ }
+
+ /* If not enough room for last character, pad remainder with null
+ so that return value = original count, indicating buffer full. */
+ if (n == 0)
+ {
+ while (count--)
+ *p++ = 0;
+ }
+
+ /* Add a null terminator if there's room. */
+ else if (count)
+ *p = 0;
+
+ if (n == -1) /* Conversion encountered invalid wide char. */
+ return -1;
+
+ /* Return the number of bytes written to output buffer, excl null. */
+ return (p - utf8str);
+}
+
+#ifdef ANDROID
+int wctomb(char *s, wchar_t wc) { return wcrtomb(s,wc,NULL); }
+int mbtowc(wchar_t *pwc, const char *s, size_t n) { return mbrtowc(pwc, s, n, NULL); }
+#endif
+
+/*-----------------------------------------------------------------------------
+ Convert a UTF-8 character to a MultiByte character.
+ Return the size of the converted character in bytes.
+*/
+int
+ldap_x_utf8_to_mb ( char *mbchar, const char *utf8char,
+ int (*f_wctomb)(char *mbchar, wchar_t wchar) )
+{
+ wchar_t wchar;
+ int n;
+ char tmp[6]; /* Large enough for biggest multibyte char */
+
+ if (f_wctomb == NULL) /* If no conversion function was given... */
+ f_wctomb = wctomb; /* use the local ANSI C function */
+
+ /* First convert UTF-8 char to a wide char */
+ n = ldap_x_utf8_to_wc( &wchar, utf8char);
+
+ if (n == -1)
+ return -1; /* Invalid UTF-8 character */
+
+ if (mbchar == NULL)
+ n = f_wctomb( tmp, wchar );
+ else
+ n = f_wctomb( mbchar, wchar);
+
+ return n;
+}
+
+/*-----------------------------------------------------------------------------
+ Convert a UTF-8 string to a MultiByte string.
+ No more than 'count' bytes will be written to the output buffer.
+ Return the size of the converted string in bytes, excl null terminator.
+*/
+int
+ldap_x_utf8s_to_mbs ( char *mbstr, const char *utf8str, size_t count,
+ size_t (*f_wcstombs)(char *mbstr, const wchar_t *wcstr, size_t count) )
+{
+ wchar_t *wcs;
+ size_t wcsize;
+ int n;
+
+ if (f_wcstombs == NULL) /* If no conversion function was given... */
+ f_wcstombs = wcstombs; /* use the local ANSI C function */
+
+ if (utf8str == NULL || *utf8str == 0) /* NULL or empty input string */
+ {
+ if (mbstr)
+ *mbstr = 0;
+ return 0;
+ }
+
+/* Allocate memory for the maximum size wchar string that we could get. */
+ wcsize = strlen(utf8str) + 1;
+ wcs = (wchar_t *)LDAP_MALLOC(wcsize * sizeof(wchar_t));
+ if (wcs == NULL)
+ return -1; /* Memory allocation failure. */
+
+ /* First convert the UTF-8 string to a wide char string */
+ n = ldap_x_utf8s_to_wcs( wcs, utf8str, wcsize);
+
+ /* Then convert wide char string to multi-byte string */
+ if (n != -1)
+ {
+ n = f_wcstombs(mbstr, wcs, count);
+ }
+
+ LDAP_FREE(wcs);
+
+ return n;
+}
+
+/*-----------------------------------------------------------------------------
+ Convert a MultiByte character to a UTF-8 character.
+ 'mbsize' indicates the number of bytes of 'mbchar' to check.
+ Returns the number of bytes written to the output character.
+*/
+int
+ldap_x_mb_to_utf8 ( char *utf8char, const char *mbchar, size_t mbsize,
+ int (*f_mbtowc)(wchar_t *wchar, const char *mbchar, size_t count) )
+{
+ wchar_t wchar;
+ int n;
+
+ if (f_mbtowc == NULL) /* If no conversion function was given... */
+ f_mbtowc = mbtowc; /* use the local ANSI C function */
+
+ if (mbsize == 0) /* 0 is not valid. */
+ return -1;
+
+ if (mbchar == NULL || *mbchar == 0)
+ {
+ if (utf8char)
+ *utf8char = 0;
+ return 1;
+ }
+
+ /* First convert the MB char to a Wide Char */
+ n = f_mbtowc( &wchar, mbchar, mbsize);
+
+ if (n == -1)
+ return -1;
+
+ /* Convert the Wide Char to a UTF-8 character. */
+ n = ldap_x_wc_to_utf8( utf8char, wchar, LDAP_MAX_UTF8_LEN);
+
+ return n;
+}
+
+
+/*-----------------------------------------------------------------------------
+ Convert a MultiByte string to a UTF-8 string.
+ No more than 'count' bytes will be written to the output buffer.
+ Return the size of the converted string in bytes, excl null terminator.
+*/
+int
+ldap_x_mbs_to_utf8s ( char *utf8str, const char *mbstr, size_t count,
+ size_t (*f_mbstowcs)(wchar_t *wcstr, const char *mbstr, size_t count) )
+{
+ wchar_t *wcs;
+ int n;
+ size_t wcsize;
+
+ if (mbstr == NULL) /* Treat NULL input string as an empty string */
+ mbstr = "";
+
+ if (f_mbstowcs == NULL) /* If no conversion function was given... */
+ f_mbstowcs = mbstowcs; /* use the local ANSI C function */
+
+ /* Allocate memory for the maximum size wchar string that we could get. */
+ wcsize = strlen(mbstr) + 1;
+ wcs = (wchar_t *)LDAP_MALLOC( wcsize * sizeof(wchar_t) );
+ if (wcs == NULL)
+ return -1;
+
+ /* First convert multi-byte string to a wide char string */
+ n = f_mbstowcs(wcs, mbstr, wcsize);
+
+ /* Convert wide char string to UTF-8 string */
+ if (n != -1)
+ {
+ n = ldap_x_wcs_to_utf8s( utf8str, wcs, count);
+ }
+
+ LDAP_FREE(wcs);
+
+ return n;
+}
+
+#endif /* SIZEOF_WCHAR_T >= 4 */
diff --git a/libraries/libldap/utf-8.c b/libraries/libldap/utf-8.c
new file mode 100644
index 0000000..c383e41
--- /dev/null
+++ b/libraries/libldap/utf-8.c
@@ -0,0 +1,562 @@
+/* utf-8.c -- Basic UTF-8 routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Basic UTF-8 routines
+ *
+ * These routines are "dumb". Though they understand UTF-8,
+ * they don't grok Unicode. That is, they can push bits,
+ * but don't have a clue what the bits represent. That's
+ * good enough for use with the LDAP Client SDK.
+ *
+ * These routines are not optimized.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap_utf8.h"
+
+#include "ldap-int.h"
+#include "ldap_defaults.h"
+
+/*
+ * return the number of bytes required to hold the
+ * NULL-terminated UTF-8 string NOT INCLUDING the
+ * termination.
+ */
+ber_len_t ldap_utf8_bytes( const char * p )
+{
+ ber_len_t bytes;
+
+ for( bytes=0; p[bytes]; bytes++ ) {
+ /* EMPTY */ ;
+ }
+
+ return bytes;
+}
+
+ber_len_t ldap_utf8_chars( const char * p )
+{
+ /* could be optimized and could check for invalid sequences */
+ ber_len_t chars=0;
+
+ for( ; *p ; LDAP_UTF8_INCR(p) ) {
+ chars++;
+ }
+
+ return chars;
+}
+
+/* return offset to next character */
+int ldap_utf8_offset( const char * p )
+{
+ return LDAP_UTF8_NEXT(p) - p;
+}
+
+/*
+ * Returns length indicated by first byte.
+ */
+const char ldap_utf8_lentab[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 0, 0 };
+
+int ldap_utf8_charlen( const char * p )
+{
+ if (!(*p & 0x80))
+ return 1;
+
+ return ldap_utf8_lentab[*(const unsigned char *)p ^ 0x80];
+}
+
+/*
+ * Make sure the UTF-8 char used the shortest possible encoding
+ * returns charlen if valid, 0 if not.
+ *
+ * Here are the valid UTF-8 encodings, taken from RFC 2279 page 4.
+ * The table is slightly modified from that of the RFC.
+ *
+ * UCS-4 range (hex) UTF-8 sequence (binary)
+ * 0000 0000-0000 007F 0.......
+ * 0000 0080-0000 07FF 110++++. 10......
+ * 0000 0800-0000 FFFF 1110++++ 10+..... 10......
+ * 0001 0000-001F FFFF 11110+++ 10++.... 10...... 10......
+ * 0020 0000-03FF FFFF 111110++ 10+++... 10...... 10...... 10......
+ * 0400 0000-7FFF FFFF 1111110+ 10++++.. 10...... 10...... 10...... 10......
+ *
+ * The '.' bits are "don't cares". When validating a UTF-8 sequence,
+ * at least one of the '+' bits must be set, otherwise the character
+ * should have been encoded in fewer octets. Note that in the two-octet
+ * case, only the first octet needs to be validated, and this is done
+ * in the ldap_utf8_lentab[] above.
+ */
+
+/* mask of required bits in second octet */
+#undef c
+#define c const char
+c ldap_utf8_mintab[] = {
+ (c)0x20, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80,
+ (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80,
+ (c)0x30, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80,
+ (c)0x38, (c)0x80, (c)0x80, (c)0x80, (c)0x3c, (c)0x80, (c)0x00, (c)0x00 };
+#undef c
+
+int ldap_utf8_charlen2( const char * p )
+{
+ int i = LDAP_UTF8_CHARLEN( p );
+
+ if ( i > 2 ) {
+ if ( !( ldap_utf8_mintab[*p & 0x1f] & p[1] ) )
+ i = 0;
+ }
+ return i;
+}
+
+/* conv UTF-8 to UCS-4, useful for comparisons */
+ldap_ucs4_t ldap_x_utf8_to_ucs4( const char * p )
+{
+ const unsigned char *c = (const unsigned char *) p;
+ ldap_ucs4_t ch;
+ int len, i;
+ static unsigned char mask[] = {
+ 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 };
+
+ len = LDAP_UTF8_CHARLEN2(p, len);
+
+ if( len == 0 ) return LDAP_UCS4_INVALID;
+
+ ch = c[0] & mask[len];
+
+ for(i=1; i < len; i++) {
+ if ((c[i] & 0xc0) != 0x80) {
+ return LDAP_UCS4_INVALID;
+ }
+
+ ch <<= 6;
+ ch |= c[i] & 0x3f;
+ }
+
+ return ch;
+}
+
+/* conv UCS-4 to UTF-8, not used */
+int ldap_x_ucs4_to_utf8( ldap_ucs4_t c, char *buf )
+{
+ int len=0;
+ unsigned char* p = (unsigned char *) buf;
+
+ /* not a valid Unicode character */
+ if ( c < 0 ) return 0;
+
+ /* Just return length, don't convert */
+ if(buf == NULL) {
+ if( c < 0x80 ) return 1;
+ else if( c < 0x800 ) return 2;
+ else if( c < 0x10000 ) return 3;
+ else if( c < 0x200000 ) return 4;
+ else if( c < 0x4000000 ) return 5;
+ else return 6;
+ }
+
+ if( c < 0x80 ) {
+ p[len++] = c;
+
+ } else if( c < 0x800 ) {
+ p[len++] = 0xc0 | ( c >> 6 );
+ p[len++] = 0x80 | ( c & 0x3f );
+
+ } else if( c < 0x10000 ) {
+ p[len++] = 0xe0 | ( c >> 12 );
+ p[len++] = 0x80 | ( (c >> 6) & 0x3f );
+ p[len++] = 0x80 | ( c & 0x3f );
+
+ } else if( c < 0x200000 ) {
+ p[len++] = 0xf0 | ( c >> 18 );
+ p[len++] = 0x80 | ( (c >> 12) & 0x3f );
+ p[len++] = 0x80 | ( (c >> 6) & 0x3f );
+ p[len++] = 0x80 | ( c & 0x3f );
+
+ } else if( c < 0x4000000 ) {
+ p[len++] = 0xf8 | ( c >> 24 );
+ p[len++] = 0x80 | ( (c >> 18) & 0x3f );
+ p[len++] = 0x80 | ( (c >> 12) & 0x3f );
+ p[len++] = 0x80 | ( (c >> 6) & 0x3f );
+ p[len++] = 0x80 | ( c & 0x3f );
+
+ } else /* if( c < 0x80000000 ) */ {
+ p[len++] = 0xfc | ( c >> 30 );
+ p[len++] = 0x80 | ( (c >> 24) & 0x3f );
+ p[len++] = 0x80 | ( (c >> 18) & 0x3f );
+ p[len++] = 0x80 | ( (c >> 12) & 0x3f );
+ p[len++] = 0x80 | ( (c >> 6) & 0x3f );
+ p[len++] = 0x80 | ( c & 0x3f );
+ }
+
+ return len;
+}
+
+#define LDAP_UCS_UTF8LEN(c) \
+ c < 0 ? 0 : (c < 0x80 ? 1 : (c < 0x800 ? 2 : (c < 0x10000 ? 3 : \
+ (c < 0x200000 ? 4 : (c < 0x4000000 ? 5 : 6)))))
+
+/* Convert a string to UTF-8 format. The input string is expected to
+ * have characters of 1, 2, or 4 octets (in network byte order)
+ * corresponding to the ASN.1 T61STRING, BMPSTRING, and UNIVERSALSTRING
+ * types respectively. (Here T61STRING just means that there is one
+ * octet per character and characters may use the high bit of the octet.
+ * The characters are assumed to use ISO mappings, no provision is made
+ * for converting from T.61 coding rules to Unicode.)
+ */
+
+int
+ldap_ucs_to_utf8s( struct berval *ucs, int csize, struct berval *utf8s )
+{
+ unsigned char *in, *end;
+ char *ptr;
+ ldap_ucs4_t u;
+ int i, l = 0;
+
+ utf8s->bv_val = NULL;
+ utf8s->bv_len = 0;
+
+ in = (unsigned char *)ucs->bv_val;
+
+ /* Make sure we stop at an even multiple of csize */
+ end = in + ( ucs->bv_len & ~(csize-1) );
+
+ for (; in < end; ) {
+ u = *in++;
+ if (csize > 1) {
+ u <<= 8;
+ u |= *in++;
+ }
+ if (csize > 2) {
+ u <<= 8;
+ u |= *in++;
+ u <<= 8;
+ u |= *in++;
+ }
+ i = LDAP_UCS_UTF8LEN(u);
+ if (i == 0)
+ return LDAP_INVALID_SYNTAX;
+ l += i;
+ }
+
+ utf8s->bv_val = LDAP_MALLOC( l+1 );
+ if (utf8s->bv_val == NULL)
+ return LDAP_NO_MEMORY;
+ utf8s->bv_len = l;
+
+ ptr = utf8s->bv_val;
+ for (in = (unsigned char *)ucs->bv_val; in < end; ) {
+ u = *in++;
+ if (csize > 1) {
+ u <<= 8;
+ u |= *in++;
+ }
+ if (csize > 2) {
+ u <<= 8;
+ u |= *in++;
+ u <<= 8;
+ u |= *in++;
+ }
+ ptr += ldap_x_ucs4_to_utf8(u, ptr);
+ }
+ *ptr = '\0';
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Advance to the next UTF-8 character
+ *
+ * Ignores length of multibyte character, instead rely on
+ * continuation markers to find start of next character.
+ * This allows for "resyncing" of when invalid characters
+ * are provided provided the start of the next character
+ * is appears within the 6 bytes examined.
+ */
+char* ldap_utf8_next( const char * p )
+{
+ int i;
+ const unsigned char *u = (const unsigned char *) p;
+
+ if( LDAP_UTF8_ISASCII(u) ) {
+ return (char *) &p[1];
+ }
+
+ for( i=1; i<6; i++ ) {
+ if ( ( u[i] & 0xc0 ) != 0x80 ) {
+ return (char *) &p[i];
+ }
+ }
+
+ return (char *) &p[i];
+}
+
+/*
+ * Advance to the previous UTF-8 character
+ *
+ * Ignores length of multibyte character, instead rely on
+ * continuation markers to find start of next character.
+ * This allows for "resyncing" of when invalid characters
+ * are provided provided the start of the next character
+ * is appears within the 6 bytes examined.
+ */
+char* ldap_utf8_prev( const char * p )
+{
+ int i;
+ const unsigned char *u = (const unsigned char *) p;
+
+ for( i=-1; i>-6 ; i-- ) {
+ if ( ( u[i] & 0xc0 ) != 0x80 ) {
+ return (char *) &p[i];
+ }
+ }
+
+ return (char *) &p[i];
+}
+
+/*
+ * Copy one UTF-8 character from src to dst returning
+ * number of bytes copied.
+ *
+ * Ignores length of multibyte character, instead rely on
+ * continuation markers to find start of next character.
+ * This allows for "resyncing" of when invalid characters
+ * are provided provided the start of the next character
+ * is appears within the 6 bytes examined.
+ */
+int ldap_utf8_copy( char* dst, const char *src )
+{
+ int i;
+ const unsigned char *u = (const unsigned char *) src;
+
+ dst[0] = src[0];
+
+ if( LDAP_UTF8_ISASCII(u) ) {
+ return 1;
+ }
+
+ for( i=1; i<6; i++ ) {
+ if ( ( u[i] & 0xc0 ) != 0x80 ) {
+ return i;
+ }
+ dst[i] = src[i];
+ }
+
+ return i;
+}
+
+#ifndef UTF8_ALPHA_CTYPE
+/*
+ * UTF-8 ctype routines
+ * Only deals with characters < 0x80 (ie: US-ASCII)
+ */
+
+int ldap_utf8_isascii( const char * p )
+{
+ unsigned c = * (const unsigned char *) p;
+ return LDAP_ASCII(c);
+}
+
+int ldap_utf8_isdigit( const char * p )
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if(!LDAP_ASCII(c)) return 0;
+
+ return LDAP_DIGIT( c );
+}
+
+int ldap_utf8_isxdigit( const char * p )
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if(!LDAP_ASCII(c)) return 0;
+
+ return LDAP_HEX(c);
+}
+
+int ldap_utf8_isspace( const char * p )
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if(!LDAP_ASCII(c)) return 0;
+
+ switch(c) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ case '\v':
+ case '\f':
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * These are not needed by the C SDK and are
+ * not "good enough" for general use.
+ */
+int ldap_utf8_isalpha( const char * p )
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if(!LDAP_ASCII(c)) return 0;
+
+ return LDAP_ALPHA(c);
+}
+
+int ldap_utf8_isalnum( const char * p )
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if(!LDAP_ASCII(c)) return 0;
+
+ return LDAP_ALNUM(c);
+}
+
+int ldap_utf8_islower( const char * p )
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if(!LDAP_ASCII(c)) return 0;
+
+ return LDAP_LOWER(c);
+}
+
+int ldap_utf8_isupper( const char * p )
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if(!LDAP_ASCII(c)) return 0;
+
+ return LDAP_UPPER(c);
+}
+#endif
+
+
+/*
+ * UTF-8 string routines
+ */
+
+/* like strchr() */
+char * (ldap_utf8_strchr)( const char *str, const char *chr )
+{
+ for( ; *str != '\0'; LDAP_UTF8_INCR(str) ) {
+ if( ldap_x_utf8_to_ucs4( str ) == ldap_x_utf8_to_ucs4( chr ) ) {
+ return (char *) str;
+ }
+ }
+
+ return NULL;
+}
+
+/* like strcspn() but returns number of bytes, not characters */
+ber_len_t (ldap_utf8_strcspn)( const char *str, const char *set )
+{
+ const char *cstr;
+ const char *cset;
+
+ for( cstr = str; *cstr != '\0'; LDAP_UTF8_INCR(cstr) ) {
+ for( cset = set; *cset != '\0'; LDAP_UTF8_INCR(cset) ) {
+ if( ldap_x_utf8_to_ucs4( cstr ) == ldap_x_utf8_to_ucs4( cset ) ) {
+ return cstr - str;
+ }
+ }
+ }
+
+ return cstr - str;
+}
+
+/* like strspn() but returns number of bytes, not characters */
+ber_len_t (ldap_utf8_strspn)( const char *str, const char *set )
+{
+ const char *cstr;
+ const char *cset;
+
+ for( cstr = str; *cstr != '\0'; LDAP_UTF8_INCR(cstr) ) {
+ for( cset = set; ; LDAP_UTF8_INCR(cset) ) {
+ if( *cset == '\0' ) {
+ return cstr - str;
+ }
+
+ if( ldap_x_utf8_to_ucs4( cstr ) == ldap_x_utf8_to_ucs4( cset ) ) {
+ break;
+ }
+ }
+ }
+
+ return cstr - str;
+}
+
+/* like strpbrk(), replaces strchr() as well */
+char *(ldap_utf8_strpbrk)( const char *str, const char *set )
+{
+ for( ; *str != '\0'; LDAP_UTF8_INCR(str) ) {
+ const char *cset;
+
+ for( cset = set; *cset != '\0'; LDAP_UTF8_INCR(cset) ) {
+ if( ldap_x_utf8_to_ucs4( str ) == ldap_x_utf8_to_ucs4( cset ) ) {
+ return (char *) str;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/* like strtok_r(), not strtok() */
+char *(ldap_utf8_strtok)(char *str, const char *sep, char **last)
+{
+ char *begin;
+ char *end;
+
+ if( last == NULL ) return NULL;
+
+ begin = str ? str : *last;
+
+ begin += ldap_utf8_strspn( begin, sep );
+
+ if( *begin == '\0' ) {
+ *last = NULL;
+ return NULL;
+ }
+
+ end = &begin[ ldap_utf8_strcspn( begin, sep ) ];
+
+ if( *end != '\0' ) {
+ char *next = LDAP_UTF8_NEXT( end );
+ *end = '\0';
+ end = next;
+ }
+
+ *last = end;
+ return begin;
+}
diff --git a/libraries/libldap/util-int.c b/libraries/libldap/util-int.c
new file mode 100644
index 0000000..57c6523
--- /dev/null
+++ b/libraries/libldap/util-int.c
@@ -0,0 +1,1026 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1998 A. Hartgers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Bart Hartgers for inclusion in
+ * OpenLDAP Software.
+ */
+
+/*
+ * util-int.c Various functions to replace missing threadsafe ones.
+ * Without the real *_r funcs, things will
+ * work, but might not be threadsafe.
+ */
+
+#include "portable.h"
+
+#include <ac/stdlib.h>
+
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include "ldap-int.h"
+
+#ifndef h_errno
+/* newer systems declare this in <netdb.h> for you, older ones don't.
+ * harmless to declare it again (unless defined by a macro).
+ */
+extern int h_errno;
+#endif
+
+#ifdef HAVE_HSTRERROR
+# define HSTRERROR(e) hstrerror(e)
+#else
+# define HSTRERROR(e) hp_strerror(e)
+#endif
+
+#ifndef LDAP_R_COMPILE
+# undef HAVE_REENTRANT_FUNCTIONS
+# undef HAVE_CTIME_R
+# undef HAVE_GETHOSTBYNAME_R
+# undef HAVE_GETHOSTBYADDR_R
+
+#else
+# include <ldap_pvt_thread.h>
+ ldap_pvt_thread_mutex_t ldap_int_resolv_mutex;
+ ldap_pvt_thread_mutex_t ldap_int_hostname_mutex;
+ static ldap_pvt_thread_mutex_t ldap_int_gettime_mutex;
+
+# if (defined( HAVE_CTIME_R ) || defined( HAVE_REENTRANT_FUNCTIONS)) \
+ && defined( CTIME_R_NARGS )
+# define USE_CTIME_R
+# else
+ static ldap_pvt_thread_mutex_t ldap_int_ctime_mutex;
+# endif
+
+/* USE_GMTIME_R and USE_LOCALTIME_R defined in ldap_pvt.h */
+
+#if !defined( USE_GMTIME_R ) || !defined( USE_LOCALTIME_R )
+ /* we use the same mutex for gmtime(3) and localtime(3)
+ * because implementations may use the same buffer
+ * for both functions */
+ static ldap_pvt_thread_mutex_t ldap_int_gmtime_mutex;
+#endif
+
+# if defined(HAVE_GETHOSTBYNAME_R) && \
+ (GETHOSTBYNAME_R_NARGS < 5) || (6 < GETHOSTBYNAME_R_NARGS)
+ /* Don't know how to handle this version, pretend it's not there */
+# undef HAVE_GETHOSTBYNAME_R
+# endif
+# if defined(HAVE_GETHOSTBYADDR_R) && \
+ (GETHOSTBYADDR_R_NARGS < 7) || (8 < GETHOSTBYADDR_R_NARGS)
+ /* Don't know how to handle this version, pretend it's not there */
+# undef HAVE_GETHOSTBYADDR_R
+# endif
+#endif /* LDAP_R_COMPILE */
+
+char *ldap_pvt_ctime( const time_t *tp, char *buf )
+{
+#ifdef USE_CTIME_R
+# if (CTIME_R_NARGS > 3) || (CTIME_R_NARGS < 2)
+# error "CTIME_R_NARGS should be 2 or 3"
+# elif CTIME_R_NARGS > 2 && defined(CTIME_R_RETURNS_INT)
+ return( ctime_r(tp,buf,26) < 0 ? 0 : buf );
+# elif CTIME_R_NARGS > 2
+ return ctime_r(tp,buf,26);
+# else
+ return ctime_r(tp,buf);
+# endif
+
+#else
+
+ LDAP_MUTEX_LOCK( &ldap_int_ctime_mutex );
+ AC_MEMCPY( buf, ctime(tp), 26 );
+ LDAP_MUTEX_UNLOCK( &ldap_int_ctime_mutex );
+
+ return buf;
+#endif
+}
+
+#if !defined( USE_GMTIME_R ) || !defined( USE_LOCALTIME_R )
+int
+ldap_pvt_gmtime_lock( void )
+{
+# ifndef LDAP_R_COMPILE
+ return 0;
+# else /* LDAP_R_COMPILE */
+ return ldap_pvt_thread_mutex_lock( &ldap_int_gmtime_mutex );
+# endif /* LDAP_R_COMPILE */
+}
+
+int
+ldap_pvt_gmtime_unlock( void )
+{
+# ifndef LDAP_R_COMPILE
+ return 0;
+# else /* LDAP_R_COMPILE */
+ return ldap_pvt_thread_mutex_unlock( &ldap_int_gmtime_mutex );
+# endif /* LDAP_R_COMPILE */
+}
+#endif /* !USE_GMTIME_R || !USE_LOCALTIME_R */
+
+#ifndef USE_GMTIME_R
+struct tm *
+ldap_pvt_gmtime( const time_t *timep, struct tm *result )
+{
+ struct tm *tm_ptr;
+
+ LDAP_MUTEX_LOCK( &ldap_int_gmtime_mutex );
+ tm_ptr = gmtime( timep );
+ if ( tm_ptr == NULL ) {
+ result = NULL;
+
+ } else {
+ *result = *tm_ptr;
+ }
+ LDAP_MUTEX_UNLOCK( &ldap_int_gmtime_mutex );
+
+ return result;
+}
+#endif /* !USE_GMTIME_R */
+
+#ifndef USE_LOCALTIME_R
+struct tm *
+ldap_pvt_localtime( const time_t *timep, struct tm *result )
+{
+ struct tm *tm_ptr;
+
+ LDAP_MUTEX_LOCK( &ldap_int_gmtime_mutex );
+ tm_ptr = localtime( timep );
+ if ( tm_ptr == NULL ) {
+ result = NULL;
+
+ } else {
+ *result = *tm_ptr;
+ }
+ LDAP_MUTEX_UNLOCK( &ldap_int_gmtime_mutex );
+
+ return result;
+}
+#endif /* !USE_LOCALTIME_R */
+
+static int _ldap_pvt_gt_subs;
+
+#ifdef _WIN32
+/* Windows SYSTEMTIME only has 10 millisecond resolution, so we
+ * also need to use a high resolution timer to get nanoseconds.
+ * This is pretty clunky.
+ */
+static LARGE_INTEGER _ldap_pvt_gt_freq;
+static LARGE_INTEGER _ldap_pvt_gt_prev;
+static int _ldap_pvt_gt_offset;
+
+#define SEC_TO_UNIX_EPOCH 11644473600LL
+#define TICKS_PER_SECOND 10000000
+#define BILLION 1000000000L
+
+static int
+ldap_pvt_gettimensec(int *sec)
+{
+ LARGE_INTEGER count;
+
+ QueryPerformanceCounter( &count );
+
+ /* It shouldn't ever go backwards, but multiple CPUs might
+ * be able to hit in the same tick.
+ */
+ LDAP_MUTEX_LOCK( &ldap_int_gettime_mutex );
+ /* We assume Windows has at least a vague idea of
+ * when a second begins. So we align our nanosecond count
+ * with the Windows millisecond count using this offset.
+ * We retain the submillisecond portion of our own count.
+ *
+ * Note - this also assumes that the relationship between
+ * the PerformanceCounter and SystemTime stays constant;
+ * that assumption breaks if the SystemTime is adjusted by
+ * an external action.
+ */
+ if ( !_ldap_pvt_gt_freq.QuadPart ) {
+ LARGE_INTEGER c2;
+ ULARGE_INTEGER ut;
+ FILETIME ft0, ft1;
+ long long t;
+ int nsec;
+
+ /* Initialize our offset */
+ QueryPerformanceFrequency( &_ldap_pvt_gt_freq );
+
+ /* Wait for a tick of the system time: 10-15ms */
+ GetSystemTimeAsFileTime( &ft0 );
+ do {
+ GetSystemTimeAsFileTime( &ft1 );
+ } while ( ft1.dwLowDateTime == ft0.dwLowDateTime );
+
+ ut.LowPart = ft1.dwLowDateTime;
+ ut.HighPart = ft1.dwHighDateTime;
+ QueryPerformanceCounter( &c2 );
+
+ /* get second and fraction portion of counter */
+ t = c2.QuadPart % (_ldap_pvt_gt_freq.QuadPart*10);
+
+ /* convert to nanoseconds */
+ t *= BILLION;
+ nsec = t / _ldap_pvt_gt_freq.QuadPart;
+
+ ut.QuadPart /= 10;
+ ut.QuadPart %= (10 * BILLION);
+ _ldap_pvt_gt_offset = nsec - ut.QuadPart;
+ count = c2;
+ }
+ if ( count.QuadPart <= _ldap_pvt_gt_prev.QuadPart ) {
+ _ldap_pvt_gt_subs++;
+ } else {
+ _ldap_pvt_gt_subs = 0;
+ _ldap_pvt_gt_prev = count;
+ }
+ LDAP_MUTEX_UNLOCK( &ldap_int_gettime_mutex );
+
+ /* convert to nanoseconds */
+ count.QuadPart %= _ldap_pvt_gt_freq.QuadPart*10;
+ count.QuadPart *= BILLION;
+ count.QuadPart /= _ldap_pvt_gt_freq.QuadPart;
+ count.QuadPart -= _ldap_pvt_gt_offset;
+
+ /* We've extracted the 1s and nanoseconds.
+ * The 1sec digit is used to detect wraparound in nanosecnds.
+ */
+ if (count.QuadPart < 0)
+ count.QuadPart += (10 * BILLION);
+ else if (count.QuadPart >= (10 * BILLION))
+ count.QuadPart -= (10 * BILLION);
+
+ *sec = count.QuadPart / BILLION;
+ return count.QuadPart % BILLION;
+}
+
+
+/* emulate POSIX clock_gettime */
+int
+ldap_pvt_clock_gettime( int clk_id, struct timespec *tv )
+{
+ FILETIME ft;
+ ULARGE_INTEGER ut;
+ int sec, sec0;
+
+ GetSystemTimeAsFileTime( &ft );
+ ut.LowPart = ft.dwLowDateTime;
+ ut.HighPart = ft.dwHighDateTime;
+
+ /* convert to sec */
+ ut.QuadPart /= TICKS_PER_SECOND;
+
+ tv->tv_nsec = ldap_pvt_gettimensec(&sec);
+ tv->tv_sec = ut.QuadPart - SEC_TO_UNIX_EPOCH;
+
+ /* check for carry from microseconds */
+ sec0 = tv->tv_sec % 10;
+ if (sec0 < sec || (sec0 == 9 && !sec))
+ tv->tv_sec++;
+
+ return 0;
+}
+
+/* emulate POSIX gettimeofday */
+int
+ldap_pvt_gettimeofday( struct timeval *tv, void *unused )
+{
+ struct timespec ts;
+ ldap_pvt_clock_gettime( 0, &ts );
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec / 1000;
+ return 0;
+}
+
+
+/* return a broken out time, with nanoseconds
+ */
+void
+ldap_pvt_gettime( struct lutil_tm *tm )
+{
+ SYSTEMTIME st;
+ int sec, sec0;
+ static const char daysPerMonth[] = {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+ GetSystemTime( &st );
+ tm->tm_nsec = ldap_pvt_gettimensec(&sec);
+ tm->tm_usub = _ldap_pvt_gt_subs;
+
+ /* any difference larger than nanoseconds is
+ * already reflected in st
+ */
+ tm->tm_sec = st.wSecond;
+ tm->tm_min = st.wMinute;
+ tm->tm_hour = st.wHour;
+ tm->tm_mday = st.wDay;
+ tm->tm_mon = st.wMonth - 1;
+ tm->tm_year = st.wYear - 1900;
+
+ /* check for carry from nanoseconds */
+ sec0 = tm->tm_sec % 10;
+ if (sec0 < sec || (sec0 == 9 && !sec)) {
+ tm->tm_sec++;
+ /* FIXME: we don't handle leap seconds */
+ if (tm->tm_sec > 59) {
+ tm->tm_sec = 0;
+ tm->tm_min++;
+ if (tm->tm_min > 59) {
+ tm->tm_min = 0;
+ tm->tm_hour++;
+ if (tm->tm_hour > 23) {
+ int days = daysPerMonth[tm->tm_mon];
+ tm->tm_hour = 0;
+ tm->tm_mday++;
+
+ /* if it's February of a leap year,
+ * add 1 day to this month
+ */
+ if (tm->tm_mon == 1 &&
+ ((!(st.wYear % 4) && (st.wYear % 100)) ||
+ !(st.wYear % 400)))
+ days++;
+
+ if (tm->tm_mday > days) {
+ tm->tm_mday = 1;
+ tm->tm_mon++;
+ if (tm->tm_mon > 11) {
+ tm->tm_mon = 0;
+ tm->tm_year++;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+#else
+
+#ifdef HAVE_CLOCK_GETTIME
+static struct timespec _ldap_pvt_gt_prevTv;
+#else
+static struct timeval _ldap_pvt_gt_prevTv;
+#endif
+
+void
+ldap_pvt_gettime( struct lutil_tm *ltm )
+{
+ struct tm tm;
+ time_t t;
+#ifdef HAVE_CLOCK_GETTIME
+#define FRAC tv_nsec
+#define NSECS(x) x
+ struct timespec tv;
+
+ clock_gettime( CLOCK_REALTIME, &tv );
+#else
+#define FRAC tv_usec
+#define NSECS(x) x * 1000
+ struct timeval tv;
+
+ gettimeofday( &tv, NULL );
+#endif
+ t = tv.tv_sec;
+
+ LDAP_MUTEX_LOCK( &ldap_int_gettime_mutex );
+ if ( tv.tv_sec < _ldap_pvt_gt_prevTv.tv_sec
+ || ( tv.tv_sec == _ldap_pvt_gt_prevTv.tv_sec
+ && tv.FRAC <= _ldap_pvt_gt_prevTv.FRAC )) {
+ _ldap_pvt_gt_subs++;
+ } else {
+ _ldap_pvt_gt_subs = 0;
+ _ldap_pvt_gt_prevTv = tv;
+ }
+ LDAP_MUTEX_UNLOCK( &ldap_int_gettime_mutex );
+
+ ltm->tm_usub = _ldap_pvt_gt_subs;
+
+ ldap_pvt_gmtime( &t, &tm );
+
+ ltm->tm_sec = tm.tm_sec;
+ ltm->tm_min = tm.tm_min;
+ ltm->tm_hour = tm.tm_hour;
+ ltm->tm_mday = tm.tm_mday;
+ ltm->tm_mon = tm.tm_mon;
+ ltm->tm_year = tm.tm_year;
+ ltm->tm_nsec = NSECS(tv.FRAC);
+}
+#endif
+
+size_t
+ldap_pvt_csnstr(char *buf, size_t len, unsigned int replica, unsigned int mod)
+{
+ struct lutil_tm tm;
+ int n;
+
+ ldap_pvt_gettime( &tm );
+
+ n = snprintf( buf, len,
+ "%4d%02d%02d%02d%02d%02d.%06dZ#%06x#%03x#%06x",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour,
+ tm.tm_min, tm.tm_sec, tm.tm_nsec / 1000, tm.tm_usub, replica, mod );
+
+ if( n < 0 ) return 0;
+ return ( (size_t) n < len ) ? n : 0;
+}
+
+#define BUFSTART (1024-32)
+#define BUFMAX (32*1024-32)
+
+#if defined(LDAP_R_COMPILE)
+static char *safe_realloc( char **buf, int len );
+
+#if !(defined(HAVE_GETHOSTBYNAME_R) && defined(HAVE_GETHOSTBYADDR_R))
+static int copy_hostent( struct hostent *res,
+ char **buf, struct hostent * src );
+#endif
+#endif
+
+int ldap_pvt_gethostbyname_a(
+ const char *name,
+ struct hostent *resbuf,
+ char **buf,
+ struct hostent **result,
+ int *herrno_ptr )
+{
+#if defined( HAVE_GETHOSTBYNAME_R )
+
+# define NEED_SAFE_REALLOC 1
+ int r=-1;
+ int buflen=BUFSTART;
+ *buf = NULL;
+ for(;buflen<BUFMAX;) {
+ if (safe_realloc( buf, buflen )==NULL)
+ return r;
+
+#if (GETHOSTBYNAME_R_NARGS < 6)
+ *result=gethostbyname_r( name, resbuf, *buf, buflen, herrno_ptr );
+ r = (*result == NULL) ? -1 : 0;
+#else
+ while((r = gethostbyname_r( name, resbuf, *buf, buflen, result, herrno_ptr )) == ERANGE) {
+ /* Increase the buffer */
+ buflen*=2;
+ if (safe_realloc(buf, buflen) == NULL)
+ return -1;
+ }
+#endif
+
+ Debug2( LDAP_DEBUG_TRACE, "ldap_pvt_gethostbyname_a: host=%s, r=%d\n",
+ name, r );
+
+#ifdef NETDB_INTERNAL
+ if ((r<0) &&
+ (*herrno_ptr==NETDB_INTERNAL) &&
+ (errno==ERANGE))
+ {
+ buflen*=2;
+ continue;
+ }
+#endif
+ return r;
+ }
+ return -1;
+#elif defined( LDAP_R_COMPILE )
+# define NEED_COPY_HOSTENT
+ struct hostent *he;
+ int retval;
+ *buf = NULL;
+
+ LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex );
+
+ he = gethostbyname( name );
+
+ if (he==NULL) {
+ *herrno_ptr = h_errno;
+ retval = -1;
+ } else if (copy_hostent( resbuf, buf, he )<0) {
+ *herrno_ptr = -1;
+ retval = -1;
+ } else {
+ *result = resbuf;
+ retval = 0;
+ }
+
+ LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex );
+
+ return retval;
+#else
+ *buf = NULL;
+ *result = gethostbyname( name );
+
+ if (*result!=NULL) {
+ return 0;
+ }
+
+ *herrno_ptr = h_errno;
+
+ return -1;
+#endif
+}
+
+#if !defined( HAVE_GETNAMEINFO ) && !defined( HAVE_HSTRERROR )
+static const char *
+hp_strerror( int err )
+{
+ switch (err) {
+ case HOST_NOT_FOUND: return _("Host not found (authoritative)");
+ case TRY_AGAIN: return _("Host not found (server fail?)");
+ case NO_RECOVERY: return _("Non-recoverable failure");
+ case NO_DATA: return _("No data of requested type");
+#ifdef NETDB_INTERNAL
+ case NETDB_INTERNAL: return STRERROR( errno );
+#endif
+ }
+ return _("Unknown resolver error");
+}
+#endif
+
+int ldap_pvt_get_hname(
+ const struct sockaddr *sa,
+ int len,
+ char *name,
+ int namelen,
+ char **err )
+{
+ int rc;
+#if defined( HAVE_GETNAMEINFO )
+
+ LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex );
+ rc = getnameinfo( sa, len, name, namelen, NULL, 0, 0 );
+ LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex );
+ if ( rc ) *err = (char *)AC_GAI_STRERROR( rc );
+ return rc;
+
+#else /* !HAVE_GETNAMEINFO */
+ char *addr;
+ int alen;
+ struct hostent *hp = NULL;
+#ifdef HAVE_GETHOSTBYADDR_R
+ struct hostent hb;
+ int buflen=BUFSTART, h_errno;
+ char *buf=NULL;
+#endif
+
+#ifdef LDAP_PF_INET6
+ if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin = (struct sockaddr_in6 *)sa;
+ addr = (char *)&sin->sin6_addr;
+ alen = sizeof(sin->sin6_addr);
+ } else
+#endif
+ if (sa->sa_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+ addr = (char *)&sin->sin_addr;
+ alen = sizeof(sin->sin_addr);
+ } else {
+ rc = NO_RECOVERY;
+ *err = (char *)HSTRERROR( rc );
+ return rc;
+ }
+#if defined( HAVE_GETHOSTBYADDR_R )
+ for(;buflen<BUFMAX;) {
+ if (safe_realloc( &buf, buflen )==NULL) {
+ *err = (char *)STRERROR( ENOMEM );
+ return ENOMEM;
+ }
+#if (GETHOSTBYADDR_R_NARGS < 8)
+ hp=gethostbyaddr_r( addr, alen, sa->sa_family,
+ &hb, buf, buflen, &h_errno );
+ rc = (hp == NULL) ? -1 : 0;
+#else
+ rc = gethostbyaddr_r( addr, alen, sa->sa_family,
+ &hb, buf, buflen,
+ &hp, &h_errno );
+#endif
+#ifdef NETDB_INTERNAL
+ if ((rc<0) &&
+ (h_errno==NETDB_INTERNAL) &&
+ (errno==ERANGE))
+ {
+ buflen*=2;
+ continue;
+ }
+#endif
+ break;
+ }
+ if (hp) {
+ strncpy( name, hp->h_name, namelen );
+ } else {
+ *err = (char *)HSTRERROR( h_errno );
+ }
+ LDAP_FREE(buf);
+#else /* HAVE_GETHOSTBYADDR_R */
+
+ LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex );
+ hp = gethostbyaddr( addr, alen, sa->sa_family );
+ if (hp) {
+ strncpy( name, hp->h_name, namelen );
+ rc = 0;
+ } else {
+ rc = h_errno;
+ *err = (char *)HSTRERROR( h_errno );
+ }
+ LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex );
+
+#endif /* !HAVE_GETHOSTBYADDR_R */
+ return rc;
+#endif /* !HAVE_GETNAMEINFO */
+}
+
+int ldap_pvt_gethostbyaddr_a(
+ const char *addr,
+ int len,
+ int type,
+ struct hostent *resbuf,
+ char **buf,
+ struct hostent **result,
+ int *herrno_ptr )
+{
+#if defined( HAVE_GETHOSTBYADDR_R )
+
+# undef NEED_SAFE_REALLOC
+# define NEED_SAFE_REALLOC
+ int r=-1;
+ int buflen=BUFSTART;
+ *buf = NULL;
+ for(;buflen<BUFMAX;) {
+ if (safe_realloc( buf, buflen )==NULL)
+ return r;
+#if (GETHOSTBYADDR_R_NARGS < 8)
+ *result=gethostbyaddr_r( addr, len, type,
+ resbuf, *buf, buflen, herrno_ptr );
+ r = (*result == NULL) ? -1 : 0;
+#else
+ r = gethostbyaddr_r( addr, len, type,
+ resbuf, *buf, buflen,
+ result, herrno_ptr );
+#endif
+
+#ifdef NETDB_INTERNAL
+ if ((r<0) &&
+ (*herrno_ptr==NETDB_INTERNAL) &&
+ (errno==ERANGE))
+ {
+ buflen*=2;
+ continue;
+ }
+#endif
+ return r;
+ }
+ return -1;
+#elif defined( LDAP_R_COMPILE )
+# undef NEED_COPY_HOSTENT
+# define NEED_COPY_HOSTENT
+ struct hostent *he;
+ int retval;
+ *buf = NULL;
+
+ LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex );
+ he = gethostbyaddr( addr, len, type );
+
+ if (he==NULL) {
+ *herrno_ptr = h_errno;
+ retval = -1;
+ } else if (copy_hostent( resbuf, buf, he )<0) {
+ *herrno_ptr = -1;
+ retval = -1;
+ } else {
+ *result = resbuf;
+ retval = 0;
+ }
+ LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex );
+
+ return retval;
+
+#else /* gethostbyaddr() */
+ *buf = NULL;
+ *result = gethostbyaddr( addr, len, type );
+
+ if (*result!=NULL) {
+ return 0;
+ }
+ return -1;
+#endif
+}
+/*
+ * ldap_int_utils_init() should be called before any other function.
+ */
+
+void ldap_int_utils_init( void )
+{
+ static int done=0;
+ if (done)
+ return;
+ done=1;
+
+#ifdef LDAP_R_COMPILE
+#if !defined( USE_CTIME_R ) && !defined( HAVE_REENTRANT_FUNCTIONS )
+ ldap_pvt_thread_mutex_init( &ldap_int_ctime_mutex );
+#endif
+#if !defined( USE_GMTIME_R ) && !defined( USE_LOCALTIME_R )
+ ldap_pvt_thread_mutex_init( &ldap_int_gmtime_mutex );
+#endif
+ ldap_pvt_thread_mutex_init( &ldap_int_resolv_mutex );
+
+ ldap_pvt_thread_mutex_init( &ldap_int_hostname_mutex );
+
+ ldap_pvt_thread_mutex_init( &ldap_int_gettime_mutex );
+
+#endif
+
+ /* call other module init functions here... */
+}
+
+#if defined( NEED_COPY_HOSTENT )
+# undef NEED_SAFE_REALLOC
+#define NEED_SAFE_REALLOC
+
+static char *cpy_aliases(
+ char ***tgtio,
+ char *buf,
+ char **src )
+{
+ int len;
+ char **tgt=*tgtio;
+ for( ; (*src) ; src++ ) {
+ len = strlen( *src ) + 1;
+ AC_MEMCPY( buf, *src, len );
+ *tgt++=buf;
+ buf+=len;
+ }
+ *tgtio=tgt;
+ return buf;
+}
+
+static char *cpy_addresses(
+ char ***tgtio,
+ char *buf,
+ char **src,
+ int len )
+{
+ char **tgt=*tgtio;
+ for( ; (*src) ; src++ ) {
+ AC_MEMCPY( buf, *src, len );
+ *tgt++=buf;
+ buf+=len;
+ }
+ *tgtio=tgt;
+ return buf;
+}
+
+static int copy_hostent(
+ struct hostent *res,
+ char **buf,
+ struct hostent * src )
+{
+ char **p;
+ char **tp;
+ char *tbuf;
+ int name_len;
+ int n_alias=0;
+ int total_alias_len=0;
+ int n_addr=0;
+ int total_addr_len=0;
+ int total_len;
+
+ /* calculate the size needed for the buffer */
+ name_len = strlen( src->h_name ) + 1;
+
+ if( src->h_aliases != NULL ) {
+ for( p = src->h_aliases; (*p) != NULL; p++ ) {
+ total_alias_len += strlen( *p ) + 1;
+ n_alias++;
+ }
+ }
+
+ if( src->h_addr_list != NULL ) {
+ for( p = src->h_addr_list; (*p) != NULL; p++ ) {
+ n_addr++;
+ }
+ total_addr_len = n_addr * src->h_length;
+ }
+
+ total_len = (n_alias + n_addr + 2) * sizeof( char * ) +
+ total_addr_len + total_alias_len + name_len;
+
+ if (safe_realloc( buf, total_len )) {
+ tp = (char **) *buf;
+ tbuf = *buf + (n_alias + n_addr + 2) * sizeof( char * );
+ AC_MEMCPY( res, src, sizeof( struct hostent ) );
+ /* first the name... */
+ AC_MEMCPY( tbuf, src->h_name, name_len );
+ res->h_name = tbuf; tbuf+=name_len;
+ /* now the aliases */
+ res->h_aliases = tp;
+ if ( src->h_aliases != NULL ) {
+ tbuf = cpy_aliases( &tp, tbuf, src->h_aliases );
+ }
+ *tp++=NULL;
+ /* finally the addresses */
+ res->h_addr_list = tp;
+ if ( src->h_addr_list != NULL ) {
+ tbuf = cpy_addresses( &tp, tbuf, src->h_addr_list, src->h_length );
+ }
+ *tp++=NULL;
+ return 0;
+ }
+ return -1;
+}
+#endif
+
+#if defined( NEED_SAFE_REALLOC )
+static char *safe_realloc( char **buf, int len )
+{
+ char *tmpbuf;
+ tmpbuf = LDAP_REALLOC( *buf, len );
+ if (tmpbuf) {
+ *buf=tmpbuf;
+ }
+ return tmpbuf;
+}
+#endif
+
+char * ldap_pvt_get_fqdn( char *name )
+{
+#ifdef HAVE_GETADDRINFO
+ struct addrinfo hints, *res;
+#else
+ char *ha_buf;
+ struct hostent *hp, he_buf;
+ int local_h_errno;
+#endif
+ int rc;
+ char *fqdn, hostbuf[MAXHOSTNAMELEN+1];
+
+ if( name == NULL ) {
+ if( gethostname( hostbuf, MAXHOSTNAMELEN ) == 0 ) {
+ hostbuf[MAXHOSTNAMELEN] = '\0';
+ name = hostbuf;
+ } else {
+ name = "localhost";
+ }
+ }
+
+#ifdef HAVE_GETADDRINFO
+ memset( &hints, 0, sizeof( hints ));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_CANONNAME;
+
+ LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex );
+ rc = getaddrinfo( name, NULL, &hints, &res );
+ LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex );
+ if ( rc == 0 && res->ai_canonname ) {
+ fqdn = LDAP_STRDUP( res->ai_canonname );
+ } else {
+ fqdn = LDAP_STRDUP( name );
+ }
+ if ( rc == 0 )
+ freeaddrinfo( res );
+#else
+ rc = ldap_pvt_gethostbyname_a( name,
+ &he_buf, &ha_buf, &hp, &local_h_errno );
+
+ if( rc < 0 || hp == NULL || hp->h_name == NULL ) {
+ fqdn = LDAP_STRDUP( name );
+ } else {
+ fqdn = LDAP_STRDUP( hp->h_name );
+ }
+
+ LDAP_FREE( ha_buf );
+#endif
+ return fqdn;
+}
+
+#if ( defined( HAVE_GETADDRINFO ) || defined( HAVE_GETNAMEINFO ) ) \
+ && !defined( HAVE_GAI_STRERROR )
+char *ldap_pvt_gai_strerror (int code) {
+ static struct {
+ int code;
+ const char *msg;
+ } values[] = {
+#ifdef EAI_ADDRFAMILY
+ { EAI_ADDRFAMILY, N_("Address family for hostname not supported") },
+#endif
+ { EAI_AGAIN, N_("Temporary failure in name resolution") },
+ { EAI_BADFLAGS, N_("Bad value for ai_flags") },
+ { EAI_FAIL, N_("Non-recoverable failure in name resolution") },
+ { EAI_FAMILY, N_("ai_family not supported") },
+ { EAI_MEMORY, N_("Memory allocation failure") },
+#ifdef EAI_NODATA
+ { EAI_NODATA, N_("No address associated with hostname") },
+#endif
+ { EAI_NONAME, N_("Name or service not known") },
+ { EAI_SERVICE, N_("Servname not supported for ai_socktype") },
+ { EAI_SOCKTYPE, N_("ai_socktype not supported") },
+#ifdef EAI_SYSTEM
+ { EAI_SYSTEM, N_("System error") },
+#endif
+ { 0, NULL }
+ };
+
+ int i;
+
+ for ( i = 0; values[i].msg != NULL; i++ ) {
+ if ( values[i].code == code ) {
+ return (char *) _(values[i].msg);
+ }
+ }
+
+ return _("Unknown error");
+}
+#endif
+
+/* format a socket address as a string */
+
+#ifdef HAVE_TCPD
+# include <tcpd.h>
+# define SOCKADDR_STRING_UNKNOWN STRING_UNKNOWN
+#else /* ! TCP Wrappers */
+# define SOCKADDR_STRING_UNKNOWN "unknown"
+#endif /* ! TCP Wrappers */
+
+void
+ldap_pvt_sockaddrstr( Sockaddr *sa, struct berval *addrbuf )
+{
+ char *addr;
+ switch( sa->sa_addr.sa_family ) {
+#ifdef LDAP_PF_LOCAL
+ case AF_LOCAL:
+ addrbuf->bv_len = snprintf( addrbuf->bv_val, addrbuf->bv_len,
+ "PATH=%s", sa->sa_un_addr.sun_path );
+ break;
+#endif
+#ifdef LDAP_PF_INET6
+ case AF_INET6:
+ strcpy(addrbuf->bv_val, "IP=");
+ if ( IN6_IS_ADDR_V4MAPPED(&sa->sa_in6_addr.sin6_addr) ) {
+#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
+ addr = (char *)inet_ntop( AF_INET,
+ ((struct in_addr *)&sa->sa_in6_addr.sin6_addr.s6_addr[12]),
+ addrbuf->bv_val+3, addrbuf->bv_len-3 );
+#else /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ addr = inet_ntoa( *((struct in_addr *)
+ &sa->sa_in6_addr.sin6_addr.s6_addr[12]) );
+#endif /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ if ( !addr ) addr = SOCKADDR_STRING_UNKNOWN;
+ if ( addr != addrbuf->bv_val+3 ) {
+ addrbuf->bv_len = sprintf( addrbuf->bv_val+3, "%s:%d", addr,
+ (unsigned) ntohs( sa->sa_in6_addr.sin6_port ) ) + 3;
+ } else {
+ int len = strlen( addr );
+ addrbuf->bv_len = sprintf( addr+len, ":%d",
+ (unsigned) ntohs( sa->sa_in6_addr.sin6_port ) ) + len + 3;
+ }
+ } else {
+ addr = (char *)inet_ntop( AF_INET6,
+ &sa->sa_in6_addr.sin6_addr,
+ addrbuf->bv_val+4, addrbuf->bv_len-4 );
+ if ( !addr ) addr = SOCKADDR_STRING_UNKNOWN;
+ if ( addr != addrbuf->bv_val+4 ) {
+ addrbuf->bv_len = sprintf( addrbuf->bv_val+3, "[%s]:%d", addr,
+ (unsigned) ntohs( sa->sa_in6_addr.sin6_port ) ) + 3;
+ } else {
+ int len = strlen( addr );
+ addrbuf->bv_val[3] = '[';
+ addrbuf->bv_len = sprintf( addr+len, "]:%d",
+ (unsigned) ntohs( sa->sa_in6_addr.sin6_port ) ) + len + 4;
+ }
+ }
+ break;
+#endif /* LDAP_PF_INET6 */
+ case AF_INET:
+ strcpy(addrbuf->bv_val, "IP=");
+#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
+ addr = (char *)inet_ntop( AF_INET, &sa->sa_in_addr.sin_addr,
+ addrbuf->bv_val+3, addrbuf->bv_len-3 );
+#else /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ addr = inet_ntoa( sa->sa_in_addr.sin_addr );
+#endif /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ if ( !addr ) addr = SOCKADDR_STRING_UNKNOWN;
+ if ( addr != addrbuf->bv_val+3 ) {
+ addrbuf->bv_len = sprintf( addrbuf->bv_val+3, "%s:%d", addr,
+ (unsigned) ntohs( sa->sa_in_addr.sin_port ) ) + 3;
+ } else {
+ int len = strlen( addr );
+ addrbuf->bv_len = sprintf( addr+len, ":%d",
+ (unsigned) ntohs( sa->sa_in_addr.sin_port ) ) + len + 3;
+ }
+ break;
+ default:
+ addrbuf->bv_val[0] = '\0';
+ }
+}
diff --git a/libraries/libldap/vc.c b/libraries/libldap/vc.c
new file mode 100644
index 0000000..0fc29a8
--- /dev/null
+++ b/libraries/libldap/vc.c
@@ -0,0 +1,367 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This program was originally developed by Kurt D. Zeilenga for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/*
+ * LDAP Verify Credentials operation
+ *
+ * The request is an extended request with OID 1.3.6.1.4.1.4203.666.6.5 with value of
+ * the BER encoding of:
+ *
+ * VCRequest ::= SEQUENCE {
+ * cookie [0] OCTET STRING OPTIONAL,
+ * name LDAPDN,
+ * authentication AuthenticationChoice,
+ * controls [2] Controls OPTIONAL
+ * }
+ *
+ * where LDAPDN, AuthenticationChoice, and Controls are as defined in RFC 4511.
+ *
+ * The response is an extended response with no OID and a value of the BER encoding of
+ *
+ * VCResponse ::= SEQUENCE {
+ * resultCode ResultCode,
+ * diagnosticMessage LDAPString,
+ * cookie [0] OCTET STRING OPTIONAL,
+ * serverSaslCreds [1] OCTET STRING OPTIONAL,
+ * controls [2] Controls OPTIONAL
+ * }
+ *
+ * where ResultCode is the result code enumeration from RFC 4511, and LDAPString and Controls are as
+ * defined in RFC 4511.
+ */
+
+int ldap_parse_verify_credentials(
+ LDAP *ld,
+ LDAPMessage *res,
+ int * code,
+ char ** diagmsg,
+ struct berval **cookie,
+ struct berval **screds,
+ LDAPControl ***ctrls)
+{
+ int rc;
+ char *retoid = NULL;
+ struct berval *retdata = NULL;
+
+ assert(ld != NULL);
+ assert(LDAP_VALID(ld));
+ assert(res != NULL);
+ assert(code != NULL);
+ assert(diagmsg != NULL);
+
+ rc = ldap_parse_extended_result(ld, res, &retoid, &retdata, 0);
+
+ if( rc != LDAP_SUCCESS ) {
+ ldap_perror(ld, "ldap_parse_verify_credentials");
+ return rc;
+ }
+
+ if (retdata) {
+ ber_tag_t tag;
+ ber_len_t len;
+ ber_int_t i;
+ BerElement * ber = ber_init(retdata);
+ struct berval diagmsg_bv = BER_BVNULL;
+ if (!ber) {
+ rc = ld->ld_errno = LDAP_NO_MEMORY;
+ goto done;
+ }
+
+ rc = LDAP_DECODING_ERROR;
+
+ if (ber_scanf(ber, "{im" /*"}"*/, &i, &diagmsg_bv) == LBER_ERROR) {
+ goto ber_done;
+ }
+ if ( diagmsg != NULL ) {
+ *diagmsg = LDAP_MALLOC( diagmsg_bv.bv_len + 1 );
+ AC_MEMCPY( *diagmsg, diagmsg_bv.bv_val, diagmsg_bv.bv_len );
+ (*diagmsg)[diagmsg_bv.bv_len] = '\0';
+ }
+ *code = i;
+
+ tag = ber_peek_tag(ber, &len);
+ if (tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE) {
+ if (ber_scanf(ber, "O", cookie) == LBER_ERROR)
+ goto ber_done;
+ tag = ber_peek_tag(ber, &len);
+ }
+
+ if (tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_SCREDS) {
+ if (ber_scanf(ber, "O", screds) == LBER_ERROR)
+ goto ber_done;
+ tag = ber_peek_tag(ber, &len);
+ }
+
+ if (tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS) {
+ int nctrls = 0;
+ char * opaque;
+
+ *ctrls = LDAP_MALLOC(1 * sizeof(LDAPControl *));
+
+ if (!*ctrls) {
+ rc = LDAP_NO_MEMORY;
+ goto ber_done;
+ }
+
+ *ctrls[nctrls] = NULL;
+
+ for(tag = ber_first_element(ber, &len, &opaque);
+ tag != LBER_ERROR;
+ tag = ber_next_element(ber, &len, opaque))
+ {
+ LDAPControl *tctrl;
+ LDAPControl **tctrls;
+
+ tctrl = LDAP_CALLOC(1, sizeof(LDAPControl));
+
+ /* allocate pointer space for current controls (nctrls)
+ * + this control + extra NULL
+ */
+ tctrls = !tctrl ? NULL : LDAP_REALLOC(*ctrls, (nctrls+2) * sizeof(LDAPControl *));
+
+ if (!tctrls) {
+ /* allocation failure */
+ if (tctrl) LDAP_FREE(tctrl);
+ ldap_controls_free(*ctrls);
+ *ctrls = NULL;
+ rc = LDAP_NO_MEMORY;
+ goto ber_done;
+ }
+
+ tctrls[nctrls++] = tctrl;
+ tctrls[nctrls] = NULL;
+
+ tag = ber_scanf(ber, "{a" /*"}"*/, &tctrl->ldctl_oid);
+ if (tag == LBER_ERROR) {
+ *ctrls = NULL;
+ ldap_controls_free(tctrls);
+ goto ber_done;
+ }
+
+ tag = ber_peek_tag(ber, &len);
+ if (tag == LBER_BOOLEAN) {
+ ber_int_t crit;
+ tag = ber_scanf(ber, "b", &crit);
+ tctrl->ldctl_iscritical = crit ? (char) 0 : (char) ~0;
+ tag = ber_peek_tag(ber, &len);
+ }
+
+ if (tag == LBER_OCTETSTRING) {
+ tag = ber_scanf( ber, "o", &tctrl->ldctl_value );
+ } else {
+ BER_BVZERO( &tctrl->ldctl_value );
+ }
+
+ *ctrls = tctrls;
+ }
+ }
+
+ rc = LDAP_SUCCESS;
+
+ ber_done:
+ ber_free(ber, 1);
+ }
+
+done:
+ ber_bvfree(retdata);
+ ber_memfree(retoid);
+ return rc;
+}
+
+int
+ldap_verify_credentials(LDAP *ld,
+ struct berval *cookie,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *mechanism,
+ struct berval *cred,
+ LDAPControl **vcctrls,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp)
+{
+ int rc;
+ BerElement *ber;
+ struct berval reqdata;
+
+ assert(ld != NULL);
+ assert(LDAP_VALID(ld));
+ assert(msgidp != NULL);
+
+ ber = ber_alloc_t(LBER_USE_DER);
+ if (dn == NULL) dn = "";
+
+ if (mechanism == LDAP_SASL_SIMPLE) {
+ assert(!cookie);
+
+ rc = ber_printf(ber, "{stO" /*"}"*/,
+ dn, LDAP_AUTH_SIMPLE, cred);
+
+ } else {
+ if (!cred || BER_BVISNULL(cred)) {
+ if (cookie) {
+ rc = ber_printf(ber, "{tOst{sN}" /*"}"*/,
+ LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE, cookie,
+ dn, LDAP_AUTH_SASL, mechanism);
+ } else {
+ rc = ber_printf(ber, "{st{sN}N" /*"}"*/,
+ dn, LDAP_AUTH_SASL, mechanism);
+ }
+ } else {
+ if (cookie) {
+ rc = ber_printf(ber, "{tOst{sON}" /*"}"*/,
+ LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE, cookie,
+ dn, LDAP_AUTH_SASL, mechanism, cred);
+ } else {
+ rc = ber_printf(ber, "{st{sON}" /*"}"*/,
+ dn, LDAP_AUTH_SASL, mechanism, cred);
+ }
+ }
+ }
+
+ if (rc < 0) {
+ rc = ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+
+ if (vcctrls && *vcctrls) {
+ LDAPControl *const *c;
+
+ rc = ber_printf(ber, "t{" /*"}"*/, LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS);
+
+ for (c=vcctrls; *c; c++) {
+ rc = ldap_pvt_put_control(*c, ber);
+ if (rc != LDAP_SUCCESS) {
+ rc = ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+ }
+
+ rc = ber_printf(ber, /*"{{"*/ "}N}");
+
+ } else {
+ rc = ber_printf(ber, /*"{"*/ "N}");
+ }
+
+ if (rc < 0) {
+ rc = ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+
+
+ rc = ber_flatten2(ber, &reqdata, 0);
+ if (rc < 0) {
+ rc = ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+
+ rc = ldap_extended_operation(ld, LDAP_EXOP_VERIFY_CREDENTIALS,
+ &reqdata, sctrls, cctrls, msgidp);
+
+done:
+ ber_free(ber, 1);
+ return rc;
+}
+
+int
+ldap_verify_credentials_s(
+ LDAP *ld,
+ struct berval *cookie,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *mechanism,
+ struct berval *cred,
+ LDAPControl **vcictrls,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *rcode,
+ char **diagmsg,
+ struct berval **scookie,
+ struct berval **scred,
+ LDAPControl ***vcoctrls)
+{
+ int rc;
+ int msgid;
+ LDAPMessage *res;
+
+ rc = ldap_verify_credentials(ld, cookie, dn, mechanism, cred, vcictrls, sctrls, cctrls, &msgid);
+ if (rc != LDAP_SUCCESS) return rc;
+
+ if (ldap_result(ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res) == -1 || !res) {
+ return ld->ld_errno;
+ }
+
+ rc = ldap_parse_verify_credentials(ld, res, rcode, diagmsg, scookie, scred, vcoctrls);
+ if (rc != LDAP_SUCCESS) {
+ ldap_msgfree(res);
+ return rc;
+ }
+
+ return( ldap_result2error(ld, res, 1));
+}
+
+#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS_INTERACTIVE
+int
+ldap_verify_credentials_interactive (
+ LDAP *ld,
+ LDAP_CONST char *dn, /* usually NULL */
+ LDAP_CONST char *mech,
+ LDAPControl **vcControls,
+ LDAPControl **serverControls,
+ LDAPControl **clientControls,
+
+ /* should be client controls */
+ unsigned flags,
+ LDAP_SASL_INTERACT_PROC *proc,
+ void *defaults,
+ void *context;
+
+ /* as obtained from ldap_result() */
+ LDAPMessage *result,
+
+ /* returned during bind processing */
+ const char **rmech,
+ int *msgid )
+{
+ if (!ld && context) {
+ assert(!dn);
+ assert(!mech);
+ assert(!vcControls);
+ assert(!serverControls);
+ assert(!defaults);
+ assert(!result);
+ assert(!rmech);
+ assert(!msgid);
+
+ /* special case to avoid having to expose a separate dispose context API */
+ sasl_dispose((sasl_conn_t)context);
+ return LDAP_SUCCESS;
+ }
+
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ return ld->ld_errno;
+}
+#endif
diff --git a/libraries/libldap/vlvctrl.c b/libraries/libldap/vlvctrl.c
new file mode 100644
index 0000000..db009df
--- /dev/null
+++ b/libraries/libldap/vlvctrl.c
@@ -0,0 +1,361 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
+ *
+ * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
+ * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT
+ * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS
+ * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE"
+ * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION
+ * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP
+ * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT
+ * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
+ *---
+ * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
+ * can be found in the file "build/LICENSE-2.0.1" in this distribution
+ * of OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#define LDAP_VLVBYINDEX_IDENTIFIER 0xa0L
+#define LDAP_VLVBYVALUE_IDENTIFIER 0x81L
+#define LDAP_VLVCONTEXT_IDENTIFIER 0x04L
+
+
+/*---
+ ldap_create_vlv_control
+
+ Create and encode the Virtual List View control.
+
+ ld (IN) An LDAP session handle.
+
+ vlvinfop (IN) The address of an LDAPVLVInfo structure whose contents
+ are used to construct the value of the control
+ that is created.
+
+ value (OUT) A struct berval that contains the value to be assigned to the ldctl_value member
+ of an LDAPControl structure that contains the
+ VirtualListViewRequest control.
+ The bv_val member of the berval structure
+ SHOULD be freed when it is no longer in use by
+ calling ldap_memfree().
+
+
+ Ber encoding
+
+ VirtualListViewRequest ::= SEQUENCE {
+ beforeCount INTEGER (0 .. maxInt),
+ afterCount INTEGER (0 .. maxInt),
+ CHOICE {
+ byoffset [0] SEQUENCE, {
+ offset INTEGER (0 .. maxInt),
+ contentCount INTEGER (0 .. maxInt) }
+ [1] greaterThanOrEqual assertionValue }
+ contextID OCTET STRING OPTIONAL }
+
+
+ Note: The first time the VLV control is created, the ldvlv_context
+ field of the LDAPVLVInfo structure should be set to NULL.
+ The context obtained from calling ldap_parse_vlv_control()
+ should be used as the context in the next ldap_create_vlv_control
+ call.
+
+ ---*/
+
+int
+ldap_create_vlv_control_value(
+ LDAP *ld,
+ LDAPVLVInfo *vlvinfop,
+ struct berval *value )
+{
+ ber_tag_t tag;
+ BerElement *ber;
+
+ if ( ld == NULL || vlvinfop == NULL || value == NULL ) {
+ if ( ld )
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return LDAP_PARAM_ERROR;
+ }
+
+ assert( LDAP_VALID( ld ) );
+
+ value->bv_val = NULL;
+ value->bv_len = 0;
+ ld->ld_errno = LDAP_SUCCESS;
+
+ ber = ldap_alloc_ber_with_options( ld );
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ tag = ber_printf( ber, "{ii" /*}*/,
+ vlvinfop->ldvlv_before_count,
+ vlvinfop->ldvlv_after_count );
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+
+ if ( vlvinfop->ldvlv_attrvalue == NULL ) {
+ tag = ber_printf( ber, "t{iiN}",
+ LDAP_VLVBYINDEX_IDENTIFIER,
+ vlvinfop->ldvlv_offset,
+ vlvinfop->ldvlv_count );
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+
+ } else {
+ tag = ber_printf( ber, "tO",
+ LDAP_VLVBYVALUE_IDENTIFIER,
+ vlvinfop->ldvlv_attrvalue );
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+ }
+
+ if ( vlvinfop->ldvlv_context ) {
+ tag = ber_printf( ber, "tO",
+ LDAP_VLVCONTEXT_IDENTIFIER,
+ vlvinfop->ldvlv_context );
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+ }
+
+ tag = ber_printf( ber, /*{*/ "N}" );
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+
+ if ( ber_flatten2( ber, value, 1 ) == -1 ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ }
+
+ if ( 0 ) {
+error_return:;
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ }
+
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ return ld->ld_errno;
+}
+
+/*---
+ ldap_create_vlv_control
+
+ Create and encode the Virtual List View control.
+
+ ld (IN) An LDAP session handle.
+
+ vlvinfop (IN) The address of an LDAPVLVInfo structure whose contents
+ are used to construct the value of the control
+ that is created.
+
+ ctrlp (OUT) A result parameter that will be assigned the address
+ of an LDAPControl structure that contains the
+ VirtualListViewRequest control created by this function.
+ The memory occupied by the LDAPControl structure
+ SHOULD be freed when it is no longer in use by
+ calling ldap_control_free().
+
+
+ Ber encoding
+
+ VirtualListViewRequest ::= SEQUENCE {
+ beforeCount INTEGER (0 .. maxInt),
+ afterCount INTEGER (0 .. maxInt),
+ CHOICE {
+ byoffset [0] SEQUENCE, {
+ offset INTEGER (0 .. maxInt),
+ contentCount INTEGER (0 .. maxInt) }
+ [1] greaterThanOrEqual assertionValue }
+ contextID OCTET STRING OPTIONAL }
+
+
+ Note: The first time the VLV control is created, the ldvlv_context
+ field of the LDAPVLVInfo structure should be set to NULL.
+ The context obtained from calling ldap_parse_vlv_control()
+ should be used as the context in the next ldap_create_vlv_control
+ call.
+
+ ---*/
+
+int
+ldap_create_vlv_control(
+ LDAP *ld,
+ LDAPVLVInfo *vlvinfop,
+ LDAPControl **ctrlp )
+{
+ struct berval value;
+
+ if ( ctrlp == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ ld->ld_errno = ldap_create_vlv_control_value( ld, vlvinfop, &value );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+
+ ld->ld_errno = ldap_control_create( LDAP_CONTROL_VLVREQUEST,
+ 1, &value, 0, ctrlp );
+ if ( ld->ld_errno != LDAP_SUCCESS ) {
+ LDAP_FREE( value.bv_val );
+ }
+ }
+
+ return ld->ld_errno;
+}
+
+
+/*---
+ ldap_parse_vlvresponse_control
+
+ Decode the Virtual List View control return information.
+
+ ld (IN) An LDAP session handle.
+
+ ctrl (IN) The address of the LDAPControl structure.
+
+ target_posp (OUT) This result parameter is filled in with the list
+ index of the target entry. If this parameter is
+ NULL, the target position is not returned.
+
+ list_countp (OUT) This result parameter is filled in with the server's
+ estimate of the size of the list. If this parameter
+ is NULL, the size is not returned.
+
+ contextp (OUT) This result parameter is filled in with the address
+ of a struct berval that contains the server-
+ generated context identifier if one was returned by
+ the server. If the server did not return a context
+ identifier, this parameter will be set to NULL, even
+ if an error occurred.
+ The returned context SHOULD be used in the next call
+ to create a VLV sort control. The struct berval
+ returned SHOULD be disposed of by calling ber_bvfree()
+ when it is no longer needed. If NULL is passed for
+ contextp, the context identifier is not returned.
+
+ errcodep (OUT) This result parameter is filled in with the VLV
+ result code. If this parameter is NULL, the result
+ code is not returned.
+
+
+ Ber encoding
+
+ VirtualListViewResponse ::= SEQUENCE {
+ targetPosition INTEGER (0 .. maxInt),
+ contentCount INTEGER (0 .. maxInt),
+ virtualListViewResult ENUMERATED {
+ success (0),
+ operationsError (1),
+ unwillingToPerform (53),
+ insufficientAccessRights (50),
+ busy (51),
+ timeLimitExceeded (3),
+ adminLimitExceeded (11),
+ sortControlMissing (60),
+ offsetRangeError (61),
+ other (80) },
+ contextID OCTET STRING OPTIONAL }
+
+---*/
+
+int
+ldap_parse_vlvresponse_control(
+ LDAP *ld,
+ LDAPControl *ctrl,
+ ber_int_t *target_posp,
+ ber_int_t *list_countp,
+ struct berval **contextp,
+ ber_int_t *errcodep )
+{
+ BerElement *ber;
+ ber_int_t pos, count, err;
+ ber_tag_t tag, berTag;
+ ber_len_t berLen;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ if (contextp) {
+ *contextp = NULL; /* Make sure we return a NULL if error occurs. */
+ }
+
+ if (ctrl == NULL) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return(ld->ld_errno);
+ }
+
+ if (strcmp(LDAP_CONTROL_VLVRESPONSE, ctrl->ldctl_oid) != 0) {
+ /* Not VLV Response control */
+ ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
+ return(ld->ld_errno);
+ }
+
+ /* Create a BerElement from the berval returned in the control. */
+ ber = ber_init(&ctrl->ldctl_value);
+
+ if (ber == NULL) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return(ld->ld_errno);
+ }
+
+ /* Extract the data returned in the control. */
+ tag = ber_scanf(ber, "{iie" /*}*/, &pos, &count, &err);
+
+ if( tag == LBER_ERROR) {
+ ber_free(ber, 1);
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+ }
+
+
+ /* Since the context is the last item encoded, if caller doesn't want
+ it returned, don't decode it. */
+ if (contextp) {
+ if (LDAP_VLVCONTEXT_IDENTIFIER == ber_peek_tag(ber, &berLen)) {
+ tag = ber_scanf(ber, "tO", &berTag, contextp);
+
+ if( tag == LBER_ERROR) {
+ ber_free(ber, 1);
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+ }
+ }
+ }
+
+ ber_free(ber, 1);
+
+ /* Return data to the caller for items that were requested. */
+ if (target_posp) *target_posp = pos;
+ if (list_countp) *list_countp = count;
+ if (errcodep) *errcodep = err;
+
+ ld->ld_errno = LDAP_SUCCESS;
+ return(ld->ld_errno);
+}
diff --git a/libraries/libldap/whoami.c b/libraries/libldap/whoami.c
new file mode 100644
index 0000000..f3ebdeb
--- /dev/null
+++ b/libraries/libldap/whoami.c
@@ -0,0 +1,102 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This program was originally developed by Kurt D. Zeilenga for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/*
+ * LDAP Who Am I? (Extended) Operation <draft-zeilenga-ldap-authzid-xx.txt>
+ */
+
+int ldap_parse_whoami(
+ LDAP *ld,
+ LDAPMessage *res,
+ struct berval **authzid )
+{
+ int rc;
+ char *retoid = NULL;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( res != NULL );
+ assert( authzid != NULL );
+
+ *authzid = NULL;
+
+ rc = ldap_parse_extended_result( ld, res, &retoid, authzid, 0 );
+
+ if( rc != LDAP_SUCCESS ) {
+ ldap_perror( ld, "ldap_parse_whoami" );
+ return rc;
+ }
+
+ ber_memfree( retoid );
+ return rc;
+}
+
+int
+ldap_whoami( LDAP *ld,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ int rc;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( msgidp != NULL );
+
+ rc = ldap_extended_operation( ld, LDAP_EXOP_WHO_AM_I,
+ NULL, sctrls, cctrls, msgidp );
+
+ return rc;
+}
+
+int
+ldap_whoami_s(
+ LDAP *ld,
+ struct berval **authzid,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ int rc;
+ int msgid;
+ LDAPMessage *res;
+
+ rc = ldap_whoami( ld, sctrls, cctrls, &msgid );
+ if ( rc != LDAP_SUCCESS ) return rc;
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) {
+ return ld->ld_errno;
+ }
+
+ rc = ldap_parse_whoami( ld, res, authzid );
+ if( rc != LDAP_SUCCESS ) {
+ ldap_msgfree( res );
+ return rc;
+ }
+
+ return( ldap_result2error( ld, res, 1 ) );
+}
diff --git a/libraries/liblmdb/CHANGES b/libraries/liblmdb/CHANGES
new file mode 100644
index 0000000..2b81d4a
--- /dev/null
+++ b/libraries/liblmdb/CHANGES
@@ -0,0 +1,266 @@
+LMDB 0.9 Change Log
+
+LMDB 0.9.29 Release (2021/03/16)
+ ITS#9461 refix ITS#9376
+ ITS#9500 fix regression from ITS#8662
+
+LMDB 0.9.28 Release (2021/02/04)
+ ITS#8662 add -a append option to mdb_load
+
+LMDB 0.9.27 Release (2020/10/26)
+ ITS#9376 fix repeated DUPSORT cursor deletes
+
+LMDB 0.9.26 Release (2020/08/11)
+ ITS#9278 fix robust mutex cleanup for FreeBSD
+
+LMDB 0.9.25 Release (2020/01/30)
+ ITS#9068 fix mdb_dump/load backslashes in printable content
+ ITS#9118 add MAP_NOSYNC for FreeBSD
+ ITS#9155 free mt_spill_pgs in non-nested txn on end
+
+LMDB 0.9.24 Release (2019/07/24)
+ ITS#8969 Tweak mdb_page_split
+ ITS#8975 WIN32 fix writemap set_mapsize crash
+ ITS#9007 Fix loose pages in WRITEMAP
+
+LMDB 0.9.23 Release (2018/12/19)
+ ITS#8756 Fix loose pages in dirty list
+ ITS#8831 Fix mdb_load flag init
+ ITS#8844 Fix mdb_env_close in forked process
+ Documentation
+ ITS#8857 mdb_cursor_del doesn't invalidate cursor
+ ITS#8908 GET_MULTIPLE etc don't change passed in key
+
+LMDB 0.9.22 Release (2018/03/22)
+ Fix MDB_DUPSORT alignment bug (ITS#8819)
+ Fix regression with new db from 0.9.19 (ITS#8760)
+ Fix liblmdb to build on Solaris (ITS#8612)
+ Fix delete behavior with DUPSORT DB (ITS#8622)
+ Fix mdb_cursor_get/mdb_cursor_del behavior (ITS#8722)
+
+LMDB 0.9.21 Release (2017/06/01)
+ Fix xcursor after cursor_del (ITS#8622)
+
+LMDB 0.9.20 (Withdrawn)
+ Fix mdb_load with escaped plaintext (ITS#8558)
+ Fix mdb_cursor_last / mdb_put interaction (ITS#8557)
+
+LMDB 0.9.19 Release (2016/12/28)
+ Fix mdb_env_cwalk cursor init (ITS#8424)
+ Fix robust mutexes on Solaris 10/11 (ITS#8339)
+ Tweak Win32 error message buffer
+ Fix MDB_GET_BOTH on non-dup record (ITS#8393)
+ Optimize mdb_drop
+ Fix xcursors after mdb_cursor_del (ITS#8406)
+ Fix MDB_NEXT_DUP after mdb_cursor_del (ITS#8412)
+ Fix mdb_cursor_put resetting C_EOF (ITS#8489)
+ Fix mdb_env_copyfd2 to return EPIPE on SIGPIPE (ITS#8504)
+ Fix mdb_env_copy with empty DB (ITS#8209)
+ Fix behaviors with fork (ITS#8505)
+ Fix mdb_dbi_open with mainDB cursors (ITS#8542)
+ Fix robust mutexes on kFreeBSD (ITS#8554)
+ Fix utf8_to_utf16 error checks (ITS#7992)
+ Fix F_NOCACHE on MacOS, error is non-fatal (ITS#7682)
+ Build
+ Make shared lib suffix overridable (ITS#8481)
+ Documentation
+ Cleanup doxygen nits
+ Note reserved vs actual mem/disk usage
+
+
+LMDB 0.9.18 Release (2016/02/05)
+ Fix robust mutex detection on glibc 2.10-11 (ITS#8330)
+ Fix page_search_root assert on FreeDB (ITS#8336)
+ Fix MDB_APPENDDUP vs. rewrite(single item) (ITS#8334)
+ Fix mdb_copy of large files on Windows
+ Fix subcursor move after delete (ITS#8355)
+ Fix mdb_midl_shirnk off-by-one (ITS#8363)
+ Check for utf8_to_utf16 failures (ITS#7992)
+ Catch strdup failure in mdb_dbi_open
+ Build
+ Additional makefile var tweaks (ITS#8169)
+ Documentation
+ Add Getting Started page
+ Update WRITEMAP description
+
+
+LMDB 0.9.17 Release (2015/11/30)
+ Fix ITS#7377 catch calloc failure
+ Fix ITS#8237 regression from ITS#7589
+ Fix ITS#8238 page_split for DUPFIXED pages
+ Fix ITS#8221 MDB_PAGE_FULL on delete/rebalance
+ Fix ITS#8258 rebalance/split assert
+ Fix ITS#8263 cursor_put cursor tracking
+ Fix ITS#8264 cursor_del cursor tracking
+ Fix ITS#8310 cursor_del cursor tracking
+ Fix ITS#8299 mdb_del cursor tracking
+ Fix ITS#8300 mdb_del cursor tracking
+ Fix ITS#8304 mdb_del cursor tracking
+ Fix ITS#7771 fakepage cursor tracking
+ Fix ITS#7789 ensure mapsize >= pages in use
+ Fix ITS#7971 mdb_txn_renew0() new reader slots
+ Fix ITS#7969 use __sync_synchronize on non-x86
+ Fix ITS#8311 page_split from update_key
+ Fix ITS#8312 loose pages in nested txn
+ Fix ITS#8313 mdb_rebalance dummy cursor
+ Fix ITS#8315 dirty_room in nested txn
+ Fix ITS#8323 dirty_list in nested txn
+ Fix ITS#8316 page_merge cursor tracking
+ Fix ITS#8321 cursor tracking
+ Fix ITS#8319 mdb_load error messages
+ Fix ITS#8320 mdb_load plaintext input
+ Added mdb_txn_id() (ITS#7994)
+ Added robust mutex support
+ Miscellaneous cleanup/simplification
+ Build
+ Create install dirs if needed (ITS#8256)
+ Fix ThreadProc decl on Win32/MSVC (ITS#8270)
+ Added ssize_t typedef for MSVC (ITS#8067)
+ Use ANSI apis on Windows (ITS#8069)
+ Use O_SYNC if O_DSYNC,MDB_DSYNC are not defined (ITS#7209)
+ Allow passing AR to make (ITS#8168)
+ Allow passing mandir to make install (ITS#8169)
+
+LMDB 0.9.16 Release (2015/08/14)
+ Fix cursor EOF bug (ITS#8190)
+ Fix handling of subDB records (ITS#8181)
+ Fix mdb_midl_shrink() usage (ITS#8200)
+
+LMDB 0.9.15 Release (2015/06/19)
+ Fix txn init (ITS#7961,#7987)
+ Fix MDB_PREV_DUP (ITS#7955,#7671)
+ Fix compact of empty env (ITS#7956)
+ Fix mdb_copy file mode
+ Fix mdb_env_close() after failed mdb_env_open()
+ Fix mdb_rebalance collapsing root (ITS#8062)
+ Fix mdb_load with large values (ITS#8066)
+ Fix to retry writes on EINTR (ITS#8106)
+ Fix mdb_cursor_del on empty DB (ITS#8109)
+ Fix MDB_INTEGERDUP key compare (ITS#8117)
+ Fix error handling (ITS#7959,#8157,etc.)
+ Fix race conditions (ITS#7969,7970)
+ Added workaround for fdatasync bug in ext3fs
+ Build
+ Don't use -fPIC for static lib
+ Update .gitignore (ITS#7952,#7953)
+ Cleanup for "make test" (ITS#7841), "make clean", mtest*.c
+ Misc. Android/Windows cleanup
+ Documentation
+ Fix MDB_APPEND doc
+ Fix MDB_MAXKEYSIZE doc (ITS#8156)
+ Fix mdb_cursor_put,mdb_cursor_del EACCES description
+ Fix mdb_env_sync(MDB_RDONLY env) doc (ITS#8021)
+ Clarify MDB_WRITEMAP doc (ITS#8021)
+ Clarify mdb_env_open doc
+ Clarify mdb_dbi_open doc
+
+LMDB 0.9.14 Release (2014/09/20)
+ Fix to support 64K page size (ITS#7713)
+ Fix to persist decreased as well as increased mapsizes (ITS#7789)
+ Fix cursor bug when deleting last node of a DUPSORT key
+ Fix mdb_env_info to return FIXEDMAP address
+ Fix ambiguous error code from writing to closed DBI (ITS#7825)
+ Fix mdb_copy copying past end of file (ITS#7886)
+ Fix cursor bugs from page_merge/rebalance
+ Fix to dirty fewer pages in deletes (mdb_page_loose())
+ Fix mdb_dbi_open creating subDBs (ITS#7917)
+ Fix mdb_cursor_get(_DUP) with single value (ITS#7913)
+ Fix Windows compat issues in mtests (ITS#7879)
+ Add compacting variant of mdb_copy
+ Add BigEndian integer key compare code
+ Add mdb_dump/mdb_load utilities
+
+LMDB 0.9.13 Release (2014/06/18)
+ Fix mdb_page_alloc unlimited overflow page search
+ Documentation
+ Re-fix MDB_CURRENT doc (ITS#7793)
+ Fix MDB_GET_MULTIPLE/MDB_NEXT_MULTIPLE doc
+
+LMDB 0.9.12 Release (2014/06/13)
+ Fix MDB_GET_BOTH regression (ITS#7875,#7681)
+ Fix MDB_MULTIPLE writing multiple keys (ITS#7834)
+ Fix mdb_rebalance (ITS#7829)
+ Fix mdb_page_split (ITS#7815)
+ Fix md_entries count (ITS#7861,#7828,#7793)
+ Fix MDB_CURRENT (ITS#7793)
+ Fix possible crash on Windows DLL detach
+ Misc code cleanup
+ Documentation
+ mdb_cursor_put: cursor moves on error (ITS#7771)
+
+
+LMDB 0.9.11 Release (2014/01/15)
+ Add mdb_env_set_assert() (ITS#7775)
+ Fix: invalidate txn on page allocation errors (ITS#7377)
+ Fix xcursor tracking in mdb_cursor_del0() (ITS#7771)
+ Fix corruption from deletes (ITS#7756)
+ Fix Windows/MSVC build issues
+ Raise safe limit of max MDB_MAXKEYSIZE
+ Misc code cleanup
+ Documentation
+ Remove spurious note about non-overlapping flags (ITS#7665)
+
+LMDB 0.9.10 Release (2013/11/12)
+ Add MDB_NOMEMINIT option
+ Fix mdb_page_split() again (ITS#7589)
+ Fix MDB_NORDAHEAD definition (ITS#7734)
+ Fix mdb_cursor_del() positioning (ITS#7733)
+ Partial fix for larger page sizes (ITS#7713)
+ Fix Windows64/MSVC build issues
+
+LMDB 0.9.9 Release (2013/10/24)
+ Add mdb_env_get_fd()
+ Add MDB_NORDAHEAD option
+ Add MDB_NOLOCK option
+ Avoid wasting space in mdb_page_split() (ITS#7589)
+ Fix mdb_page_merge() cursor fixup (ITS#7722)
+ Fix mdb_cursor_del() on last delete (ITS#7718)
+ Fix adding WRITEMAP on existing env (ITS#7715)
+ Fix nested txns (ITS#7515)
+ Fix mdb_env_copy() O_DIRECT bug (ITS#7682)
+ Fix mdb_cursor_set(SET_RANGE) return code (ITS#7681)
+ Fix mdb_rebalance() cursor fixup (ITS#7701)
+ Misc code cleanup
+ Documentation
+ Note that by default, readers need write access
+
+
+LMDB 0.9.8 Release (2013/09/09)
+ Allow mdb_env_set_mapsize() on an open environment
+ Fix mdb_dbi_flags() (ITS#7672)
+ Fix mdb_page_unspill() in nested txns
+ Fix mdb_cursor_get(CURRENT|NEXT) after a delete
+ Fix mdb_cursor_get(DUP) to always return key (ITS#7671)
+ Fix mdb_cursor_del() to always advance to next item (ITS#7670)
+ Fix mdb_cursor_set(SET_RANGE) for tree with single page (ITS#7681)
+ Fix mdb_env_copy() retry open if O_DIRECT fails (ITS#7682)
+ Tweak mdb_page_spill() to be less aggressive
+ Documentation
+ Update caveats since mdb_reader_check() added in 0.9.7
+
+LMDB 0.9.7 Release (2013/08/17)
+ Don't leave stale lockfile on failed RDONLY open (ITS#7664)
+ Fix mdb_page_split() ref beyond cursor depth
+ Fix read txn data race (ITS#7635)
+ Fix mdb_rebalance (ITS#7536, #7538)
+ Fix mdb_drop() (ITS#7561)
+ Misc DEBUG macro fixes
+ Add MDB_NOTLS envflag
+ Add mdb_env_copyfd()
+ Add mdb_txn_env() (ITS#7660)
+ Add mdb_dbi_flags() (ITS#7661)
+ Add mdb_env_get_maxkeysize()
+ Add mdb_env_reader_list()/mdb_env_reader_check()
+ Add mdb_page_spill/unspill, remove hard txn size limit
+ Use shorter names for semaphores (ITS#7615)
+ Build
+ Fix install target (ITS#7656)
+ Documentation
+ Misc updates for cursors, DB handles, data lifetime
+
+LMDB 0.9.6 Release (2013/02/25)
+ Many fixes/enhancements
+
+LMDB 0.9.5 Release (2012/11/30)
+ Renamed from libmdb to liblmdb
+ Many fixes/enhancements
diff --git a/libraries/liblmdb/COPYRIGHT b/libraries/liblmdb/COPYRIGHT
new file mode 100644
index 0000000..14eb149
--- /dev/null
+++ b/libraries/liblmdb/COPYRIGHT
@@ -0,0 +1,20 @@
+Copyright 2011-2021 Howard Chu, Symas Corp.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
+OpenLDAP is a registered trademark of the OpenLDAP Foundation.
+
+Individual files and/or contributed packages may be copyright by
+other parties and/or subject to additional restrictions.
+
+This work also contains materials derived from public sources.
+
+Additional information about OpenLDAP can be obtained at
+<http://www.openldap.org/>.
diff --git a/libraries/liblmdb/Doxyfile b/libraries/liblmdb/Doxyfile
new file mode 100644
index 0000000..e51fe75
--- /dev/null
+++ b/libraries/liblmdb/Doxyfile
@@ -0,0 +1,1631 @@
+# Doxyfile 1.7.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = LMDB
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY =
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, JavaScript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = YES
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+INLINE_GROUPED_CLASSES = YES
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = YES
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = lmdb.h midl.h mdb.c midl.c intro.doc
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using JavaScript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvances is that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED = DEBUG=2 __GNUC__=1
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES = tooltag=./man1
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = FreeSans.ttf
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = YES
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/libraries/liblmdb/LICENSE b/libraries/liblmdb/LICENSE
new file mode 100644
index 0000000..05ad757
--- /dev/null
+++ b/libraries/liblmdb/LICENSE
@@ -0,0 +1,47 @@
+The OpenLDAP Public License
+ Version 2.8, 17 August 2003
+
+Redistribution and use of this software and associated documentation
+("Software"), with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Redistributions in source form must retain copyright statements
+ and notices,
+
+2. Redistributions in binary form must reproduce applicable copyright
+ statements and notices, this list of conditions, and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution, and
+
+3. Redistributions must contain a verbatim copy of this document.
+
+The OpenLDAP Foundation may revise this license from time to time.
+Each revision is distinguished by a version number. You may use
+this Software under terms of this license revision or under the
+terms of any subsequent revision of the license.
+
+THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS
+CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S)
+OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+The names of the authors and copyright holders must not be used in
+advertising or otherwise to promote the sale, use or other dealing
+in this Software without specific, written prior permission. Title
+to copyright in this Software shall at all times remain with copyright
+holders.
+
+OpenLDAP is a registered trademark of the OpenLDAP Foundation.
+
+Copyright 1999-2003 The OpenLDAP Foundation, Redwood City,
+California, USA. All Rights Reserved. Permission to copy and
+distribute verbatim copies of this document is granted.
diff --git a/libraries/liblmdb/Makefile b/libraries/liblmdb/Makefile
new file mode 100644
index 0000000..f254511
--- /dev/null
+++ b/libraries/liblmdb/Makefile
@@ -0,0 +1,117 @@
+# Makefile for liblmdb (Lightning memory-mapped database library).
+
+########################################################################
+# Configuration. The compiler options must enable threaded compilation.
+#
+# Preprocessor macros (for CPPFLAGS) of interest...
+# Note that the defaults should already be correct for most
+# platforms; you should not need to change any of these.
+# Read their descriptions in mdb.c if you do:
+#
+# - MDB_USE_POSIX_SEM
+# - MDB_DSYNC
+# - MDB_FDATASYNC
+# - MDB_FDATASYNC_WORKS
+# - MDB_USE_PWRITEV
+# - MDB_USE_ROBUST
+#
+# There may be other macros in mdb.c of interest. You should
+# read mdb.c before changing any of them.
+#
+CC = gcc
+AR = ar
+W = -W -Wall -Wno-unused-parameter -Wbad-function-cast -Wuninitialized
+THREADS = -pthread
+OPT = -O2 -g
+CFLAGS = $(THREADS) $(OPT) $(W) $(XCFLAGS)
+LDLIBS =
+SOLIBS =
+SOEXT = .so
+prefix = /usr/local
+exec_prefix = $(prefix)
+bindir = $(exec_prefix)/bin
+libdir = $(exec_prefix)/lib
+includedir = $(prefix)/include
+datarootdir = $(prefix)/share
+mandir = $(datarootdir)/man
+
+########################################################################
+
+IHDRS = lmdb.h
+ILIBS = liblmdb.a liblmdb$(SOEXT)
+IPROGS = mdb_stat mdb_copy mdb_dump mdb_load
+IDOCS = mdb_stat.1 mdb_copy.1 mdb_dump.1 mdb_load.1
+PROGS = $(IPROGS) mtest mtest2 mtest3 mtest4 mtest5
+all: $(ILIBS) $(PROGS)
+
+install: $(ILIBS) $(IPROGS) $(IHDRS)
+ mkdir -p $(DESTDIR)$(bindir)
+ mkdir -p $(DESTDIR)$(libdir)
+ mkdir -p $(DESTDIR)$(includedir)
+ mkdir -p $(DESTDIR)$(mandir)/man1
+ for f in $(IPROGS); do cp $$f $(DESTDIR)$(bindir); done
+ for f in $(ILIBS); do cp $$f $(DESTDIR)$(libdir); done
+ for f in $(IHDRS); do cp $$f $(DESTDIR)$(includedir); done
+ for f in $(IDOCS); do cp $$f $(DESTDIR)$(mandir)/man1; done
+
+clean:
+ rm -rf $(PROGS) *.[ao] *.[ls]o *~ testdb
+
+test: all
+ rm -rf testdb && mkdir testdb
+ ./mtest && ./mdb_stat testdb
+
+liblmdb.a: mdb.o midl.o
+ $(AR) rs $@ mdb.o midl.o
+
+liblmdb$(SOEXT): mdb.lo midl.lo
+# $(CC) $(LDFLAGS) -pthread -shared -Wl,-Bsymbolic -o $@ mdb.o midl.o $(SOLIBS)
+ $(CC) $(LDFLAGS) -pthread -shared -o $@ mdb.lo midl.lo $(SOLIBS)
+
+mdb_stat: mdb_stat.o liblmdb.a
+mdb_copy: mdb_copy.o liblmdb.a
+mdb_dump: mdb_dump.o liblmdb.a
+mdb_load: mdb_load.o liblmdb.a
+mtest: mtest.o liblmdb.a
+mtest2: mtest2.o liblmdb.a
+mtest3: mtest3.o liblmdb.a
+mtest4: mtest4.o liblmdb.a
+mtest5: mtest5.o liblmdb.a
+mtest6: mtest6.o liblmdb.a
+
+mdb.o: mdb.c lmdb.h midl.h
+ $(CC) $(CFLAGS) $(CPPFLAGS) -c mdb.c
+
+midl.o: midl.c midl.h
+ $(CC) $(CFLAGS) $(CPPFLAGS) -c midl.c
+
+mdb.lo: mdb.c lmdb.h midl.h
+ $(CC) $(CFLAGS) -fPIC $(CPPFLAGS) -c mdb.c -o $@
+
+midl.lo: midl.c midl.h
+ $(CC) $(CFLAGS) -fPIC $(CPPFLAGS) -c midl.c -o $@
+
+%: %.o
+ $(CC) $(CFLAGS) $(LDFLAGS) $^ $(LDLIBS) -o $@
+
+%.o: %.c lmdb.h
+ $(CC) $(CFLAGS) $(CPPFLAGS) -c $<
+
+COV_FLAGS=-fprofile-arcs -ftest-coverage
+COV_OBJS=xmdb.o xmidl.o
+
+coverage: xmtest
+ for i in mtest*.c [0-9]*.c; do j=`basename \$$i .c`; $(MAKE) $$j.o; \
+ gcc -o x$$j $$j.o $(COV_OBJS) -pthread $(COV_FLAGS); \
+ rm -rf testdb; mkdir testdb; ./x$$j; done
+ gcov xmdb.c
+ gcov xmidl.c
+
+xmtest: mtest.o xmdb.o xmidl.o
+ gcc -o xmtest mtest.o xmdb.o xmidl.o -pthread $(COV_FLAGS)
+
+xmdb.o: mdb.c lmdb.h midl.h
+ $(CC) $(CFLAGS) -fPIC $(CPPFLAGS) -O0 $(COV_FLAGS) -c mdb.c -o $@
+
+xmidl.o: midl.c midl.h
+ $(CC) $(CFLAGS) -fPIC $(CPPFLAGS) -O0 $(COV_FLAGS) -c midl.c -o $@
diff --git a/libraries/liblmdb/intro.doc b/libraries/liblmdb/intro.doc
new file mode 100644
index 0000000..b5bb067
--- /dev/null
+++ b/libraries/liblmdb/intro.doc
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2015-2021 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/** @page starting Getting Started
+
+LMDB is compact, fast, powerful, and robust and implements a simplified
+variant of the BerkeleyDB (BDB) API. (BDB is also very powerful, and verbosely
+documented in its own right.) After reading this page, the main
+\ref mdb documentation should make sense. Thanks to Bert Hubert
+for creating the
+<a href="https://github.com/ahupowerdns/ahutils/blob/master/lmdb-semantics.md">
+initial version</a> of this writeup.
+
+Everything starts with an environment, created by #mdb_env_create().
+Once created, this environment must also be opened with #mdb_env_open().
+
+#mdb_env_open() gets passed a name which is interpreted as a directory
+path. Note that this directory must exist already, it is not created
+for you. Within that directory, a lock file and a storage file will be
+generated. If you don't want to use a directory, you can pass the
+#MDB_NOSUBDIR option, in which case the path you provided is used
+directly as the data file, and another file with a "-lock" suffix
+added will be used for the lock file.
+
+Once the environment is open, a transaction can be created within it
+using #mdb_txn_begin(). Transactions may be read-write or read-only,
+and read-write transactions may be nested. A transaction must only
+be used by one thread at a time. Transactions are always required,
+even for read-only access. The transaction provides a consistent
+view of the data.
+
+Once a transaction has been created, a database can be opened within it
+using #mdb_dbi_open(). If only one database will ever be used in the
+environment, a NULL can be passed as the database name. For named
+databases, the #MDB_CREATE flag must be used to create the database
+if it doesn't already exist. Also, #mdb_env_set_maxdbs() must be
+called after #mdb_env_create() and before #mdb_env_open() to set the
+maximum number of named databases you want to support.
+
+Note: a single transaction can open multiple databases. Generally
+databases should only be opened once, by the first transaction in
+the process. After the first transaction completes, the database
+handles can freely be used by all subsequent transactions.
+
+Within a transaction, #mdb_get() and #mdb_put() can store single
+key/value pairs if that is all you need to do (but see \ref Cursors
+below if you want to do more).
+
+A key/value pair is expressed as two #MDB_val structures. This struct
+has two fields, \c mv_size and \c mv_data. The data is a \c void pointer to
+an array of \c mv_size bytes.
+
+Because LMDB is very efficient (and usually zero-copy), the data returned
+in an #MDB_val structure may be memory-mapped straight from disk. In
+other words <b>look but do not touch</b> (or free() for that matter).
+Once a transaction is closed, the values can no longer be used, so
+make a copy if you need to keep them after that.
+
+@section Cursors Cursors
+
+To do more powerful things, we must use a cursor.
+
+Within the transaction, a cursor can be created with #mdb_cursor_open().
+With this cursor we can store/retrieve/delete (multiple) values using
+#mdb_cursor_get(), #mdb_cursor_put(), and #mdb_cursor_del().
+
+#mdb_cursor_get() positions itself depending on the cursor operation
+requested, and for some operations, on the supplied key. For example,
+to list all key/value pairs in a database, use operation #MDB_FIRST for
+the first call to #mdb_cursor_get(), and #MDB_NEXT on subsequent calls,
+until the end is hit.
+
+To retrieve all keys starting from a specified key value, use #MDB_SET.
+For more cursor operations, see the \ref mdb docs.
+
+When using #mdb_cursor_put(), either the function will position the
+cursor for you based on the \b key, or you can use operation
+#MDB_CURRENT to use the current position of the cursor. Note that
+\b key must then match the current position's key.
+
+@subsection summary Summarizing the Opening
+
+So we have a cursor in a transaction which opened a database in an
+environment which is opened from a filesystem after it was
+separately created.
+
+Or, we create an environment, open it from a filesystem, create a
+transaction within it, open a database within that transaction,
+and create a cursor within all of the above.
+
+Got it?
+
+@section thrproc Threads and Processes
+
+LMDB uses POSIX locks on files, and these locks have issues if one
+process opens a file multiple times. Because of this, do not
+#mdb_env_open() a file multiple times from a single process. Instead,
+share the LMDB environment that has opened the file across all threads.
+Otherwise, if a single process opens the same environment multiple times,
+closing it once will remove all the locks held on it, and the other
+instances will be vulnerable to corruption from other processes.
+
+Also note that a transaction is tied to one thread by default using
+Thread Local Storage. If you want to pass read-only transactions across
+threads, you can use the #MDB_NOTLS option on the environment.
+
+@section txns Transactions, Rollbacks, etc.
+
+To actually get anything done, a transaction must be committed using
+#mdb_txn_commit(). Alternatively, all of a transaction's operations
+can be discarded using #mdb_txn_abort(). In a read-only transaction,
+any cursors will \b not automatically be freed. In a read-write
+transaction, all cursors will be freed and must not be used again.
+
+For read-only transactions, obviously there is nothing to commit to
+storage. The transaction still must eventually be aborted to close
+any database handle(s) opened in it, or committed to keep the
+database handles around for reuse in new transactions.
+
+In addition, as long as a transaction is open, a consistent view of
+the database is kept alive, which requires storage. A read-only
+transaction that no longer requires this consistent view should
+be terminated (committed or aborted) when the view is no longer
+needed (but see below for an optimization).
+
+There can be multiple simultaneously active read-only transactions
+but only one that can write. Once a single read-write transaction
+is opened, all further attempts to begin one will block until the
+first one is committed or aborted. This has no effect on read-only
+transactions, however, and they may continue to be opened at any time.
+
+@section dupkeys Duplicate Keys
+
+#mdb_get() and #mdb_put() respectively have no and only some support
+for multiple key/value pairs with identical keys. If there are multiple
+values for a key, #mdb_get() will only return the first value.
+
+When multiple values for one key are required, pass the #MDB_DUPSORT
+flag to #mdb_dbi_open(). In an #MDB_DUPSORT database, by default
+#mdb_put() will not replace the value for a key if the key existed
+already. Instead it will add the new value to the key. In addition,
+#mdb_del() will pay attention to the value field too, allowing for
+specific values of a key to be deleted.
+
+Finally, additional cursor operations become available for
+traversing through and retrieving duplicate values.
+
+@section optim Some Optimization
+
+If you frequently begin and abort read-only transactions, as an
+optimization, it is possible to only reset and renew a transaction.
+
+#mdb_txn_reset() releases any old copies of data kept around for
+a read-only transaction. To reuse this reset transaction, call
+#mdb_txn_renew() on it. Any cursors in this transaction must also
+be renewed using #mdb_cursor_renew().
+
+Note that #mdb_txn_reset() is similar to #mdb_txn_abort() and will
+close any databases you opened within the transaction.
+
+To permanently free a transaction, reset or not, use #mdb_txn_abort().
+
+@section cleanup Cleaning Up
+
+For read-only transactions, any cursors created within it must
+be closed using #mdb_cursor_close().
+
+It is very rarely necessary to close a database handle, and in
+general they should just be left open.
+
+@section onward The Full API
+
+The full \ref mdb documentation lists further details, like how to:
+
+ \li size a database (the default limits are intentionally small)
+ \li drop and clean a database
+ \li detect and report errors
+ \li optimize (bulk) loading speed
+ \li (temporarily) reduce robustness to gain even more speed
+ \li gather statistics about the database
+ \li define custom sort orders
+
+*/
diff --git a/libraries/liblmdb/lmdb.h b/libraries/liblmdb/lmdb.h
new file mode 100644
index 0000000..69aa275
--- /dev/null
+++ b/libraries/liblmdb/lmdb.h
@@ -0,0 +1,1608 @@
+/** @file lmdb.h
+ * @brief Lightning memory-mapped database library
+ *
+ * @mainpage Lightning Memory-Mapped Database Manager (LMDB)
+ *
+ * @section intro_sec Introduction
+ * LMDB is a Btree-based database management library modeled loosely on the
+ * BerkeleyDB API, but much simplified. The entire database is exposed
+ * in a memory map, and all data fetches return data directly
+ * from the mapped memory, so no malloc's or memcpy's occur during
+ * data fetches. As such, the library is extremely simple because it
+ * requires no page caching layer of its own, and it is extremely high
+ * performance and memory-efficient. It is also fully transactional with
+ * full ACID semantics, and when the memory map is read-only, the
+ * database integrity cannot be corrupted by stray pointer writes from
+ * application code.
+ *
+ * The library is fully thread-aware and supports concurrent read/write
+ * access from multiple processes and threads. Data pages use a copy-on-
+ * write strategy so no active data pages are ever overwritten, which
+ * also provides resistance to corruption and eliminates the need of any
+ * special recovery procedures after a system crash. Writes are fully
+ * serialized; only one write transaction may be active at a time, which
+ * guarantees that writers can never deadlock. The database structure is
+ * multi-versioned so readers run with no locks; writers cannot block
+ * readers, and readers don't block writers.
+ *
+ * Unlike other well-known database mechanisms which use either write-ahead
+ * transaction logs or append-only data writes, LMDB requires no maintenance
+ * during operation. Both write-ahead loggers and append-only databases
+ * require periodic checkpointing and/or compaction of their log or database
+ * files otherwise they grow without bound. LMDB tracks free pages within
+ * the database and re-uses them for new write operations, so the database
+ * size does not grow without bound in normal use.
+ *
+ * The memory map can be used as a read-only or read-write map. It is
+ * read-only by default as this provides total immunity to corruption.
+ * Using read-write mode offers much higher write performance, but adds
+ * the possibility for stray application writes thru pointers to silently
+ * corrupt the database. Of course if your application code is known to
+ * be bug-free (...) then this is not an issue.
+ *
+ * If this is your first time using a transactional embedded key/value
+ * store, you may find the \ref starting page to be helpful.
+ *
+ * @section caveats_sec Caveats
+ * Troubleshooting the lock file, plus semaphores on BSD systems:
+ *
+ * - A broken lockfile can cause sync issues.
+ * Stale reader transactions left behind by an aborted program
+ * cause further writes to grow the database quickly, and
+ * stale locks can block further operation.
+ *
+ * Fix: Check for stale readers periodically, using the
+ * #mdb_reader_check function or the \ref mdb_stat_1 "mdb_stat" tool.
+ * Stale writers will be cleared automatically on some systems:
+ * - Windows - automatic
+ * - Linux, systems using POSIX mutexes with Robust option - automatic
+ * - not on BSD, systems using POSIX semaphores.
+ * Otherwise just make all programs using the database close it;
+ * the lockfile is always reset on first open of the environment.
+ *
+ * - On BSD systems or others configured with MDB_USE_POSIX_SEM,
+ * startup can fail due to semaphores owned by another userid.
+ *
+ * Fix: Open and close the database as the user which owns the
+ * semaphores (likely last user) or as root, while no other
+ * process is using the database.
+ *
+ * Restrictions/caveats (in addition to those listed for some functions):
+ *
+ * - Only the database owner should normally use the database on
+ * BSD systems or when otherwise configured with MDB_USE_POSIX_SEM.
+ * Multiple users can cause startup to fail later, as noted above.
+ *
+ * - There is normally no pure read-only mode, since readers need write
+ * access to locks and lock file. Exceptions: On read-only filesystems
+ * or with the #MDB_NOLOCK flag described under #mdb_env_open().
+ *
+ * - An LMDB configuration will often reserve considerable \b unused
+ * memory address space and maybe file size for future growth.
+ * This does not use actual memory or disk space, but users may need
+ * to understand the difference so they won't be scared off.
+ *
+ * - By default, in versions before 0.9.10, unused portions of the data
+ * file might receive garbage data from memory freed by other code.
+ * (This does not happen when using the #MDB_WRITEMAP flag.) As of
+ * 0.9.10 the default behavior is to initialize such memory before
+ * writing to the data file. Since there may be a slight performance
+ * cost due to this initialization, applications may disable it using
+ * the #MDB_NOMEMINIT flag. Applications handling sensitive data
+ * which must not be written should not use this flag. This flag is
+ * irrelevant when using #MDB_WRITEMAP.
+ *
+ * - A thread can only use one transaction at a time, plus any child
+ * transactions. Each transaction belongs to one thread. See below.
+ * The #MDB_NOTLS flag changes this for read-only transactions.
+ *
+ * - Use an MDB_env* in the process which opened it, not after fork().
+ *
+ * - Do not have open an LMDB database twice in the same process at
+ * the same time. Not even from a plain open() call - close()ing it
+ * breaks fcntl() advisory locking. (It is OK to reopen it after
+ * fork() - exec*(), since the lockfile has FD_CLOEXEC set.)
+ *
+ * - Avoid long-lived transactions. Read transactions prevent
+ * reuse of pages freed by newer write transactions, thus the
+ * database can grow quickly. Write transactions prevent
+ * other write transactions, since writes are serialized.
+ *
+ * - Avoid suspending a process with active transactions. These
+ * would then be "long-lived" as above. Also read transactions
+ * suspended when writers commit could sometimes see wrong data.
+ *
+ * ...when several processes can use a database concurrently:
+ *
+ * - Avoid aborting a process with an active transaction.
+ * The transaction becomes "long-lived" as above until a check
+ * for stale readers is performed or the lockfile is reset,
+ * since the process may not remove it from the lockfile.
+ *
+ * This does not apply to write transactions if the system clears
+ * stale writers, see above.
+ *
+ * - If you do that anyway, do a periodic check for stale readers. Or
+ * close the environment once in a while, so the lockfile can get reset.
+ *
+ * - Do not use LMDB databases on remote filesystems, even between
+ * processes on the same host. This breaks flock() on some OSes,
+ * possibly memory map sync, and certainly sync between programs
+ * on different hosts.
+ *
+ * - Opening a database can fail if another process is opening or
+ * closing it at exactly the same time.
+ *
+ * @author Howard Chu, Symas Corporation.
+ *
+ * @copyright Copyright 2011-2021 Howard Chu, Symas Corp. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ *
+ * @par Derived From:
+ * This code is derived from btree.c written by Martin Hedenfalk.
+ *
+ * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef _LMDB_H_
+#define _LMDB_H_
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Unix permissions for creating files, or dummy definition for Windows */
+#ifdef _MSC_VER
+typedef int mdb_mode_t;
+#else
+typedef mode_t mdb_mode_t;
+#endif
+
+/** An abstraction for a file handle.
+ * On POSIX systems file handles are small integers. On Windows
+ * they're opaque pointers.
+ */
+#ifdef _WIN32
+typedef void *mdb_filehandle_t;
+#else
+typedef int mdb_filehandle_t;
+#endif
+
+/** @defgroup mdb LMDB API
+ * @{
+ * @brief OpenLDAP Lightning Memory-Mapped Database Manager
+ */
+/** @defgroup Version Version Macros
+ * @{
+ */
+/** Library major version */
+#define MDB_VERSION_MAJOR 0
+/** Library minor version */
+#define MDB_VERSION_MINOR 9
+/** Library patch version */
+#define MDB_VERSION_PATCH 29
+
+/** Combine args a,b,c into a single integer for easy version comparisons */
+#define MDB_VERINT(a,b,c) (((a) << 24) | ((b) << 16) | (c))
+
+/** The full library version as a single integer */
+#define MDB_VERSION_FULL \
+ MDB_VERINT(MDB_VERSION_MAJOR,MDB_VERSION_MINOR,MDB_VERSION_PATCH)
+
+/** The release date of this library version */
+#define MDB_VERSION_DATE "March 16, 2021"
+
+/** A stringifier for the version info */
+#define MDB_VERSTR(a,b,c,d) "LMDB " #a "." #b "." #c ": (" d ")"
+
+/** A helper for the stringifier macro */
+#define MDB_VERFOO(a,b,c,d) MDB_VERSTR(a,b,c,d)
+
+/** The full library version as a C string */
+#define MDB_VERSION_STRING \
+ MDB_VERFOO(MDB_VERSION_MAJOR,MDB_VERSION_MINOR,MDB_VERSION_PATCH,MDB_VERSION_DATE)
+/** @} */
+
+/** @brief Opaque structure for a database environment.
+ *
+ * A DB environment supports multiple databases, all residing in the same
+ * shared-memory map.
+ */
+typedef struct MDB_env MDB_env;
+
+/** @brief Opaque structure for a transaction handle.
+ *
+ * All database operations require a transaction handle. Transactions may be
+ * read-only or read-write.
+ */
+typedef struct MDB_txn MDB_txn;
+
+/** @brief A handle for an individual database in the DB environment. */
+typedef unsigned int MDB_dbi;
+
+/** @brief Opaque structure for navigating through a database */
+typedef struct MDB_cursor MDB_cursor;
+
+/** @brief Generic structure used for passing keys and data in and out
+ * of the database.
+ *
+ * Values returned from the database are valid only until a subsequent
+ * update operation, or the end of the transaction. Do not modify or
+ * free them, they commonly point into the database itself.
+ *
+ * Key sizes must be between 1 and #mdb_env_get_maxkeysize() inclusive.
+ * The same applies to data sizes in databases with the #MDB_DUPSORT flag.
+ * Other data items can in theory be from 0 to 0xffffffff bytes long.
+ */
+typedef struct MDB_val {
+ size_t mv_size; /**< size of the data item */
+ void *mv_data; /**< address of the data item */
+} MDB_val;
+
+/** @brief A callback function used to compare two keys in a database */
+typedef int (MDB_cmp_func)(const MDB_val *a, const MDB_val *b);
+
+/** @brief A callback function used to relocate a position-dependent data item
+ * in a fixed-address database.
+ *
+ * The \b newptr gives the item's desired address in
+ * the memory map, and \b oldptr gives its previous address. The item's actual
+ * data resides at the address in \b item. This callback is expected to walk
+ * through the fields of the record in \b item and modify any
+ * values based at the \b oldptr address to be relative to the \b newptr address.
+ * @param[in,out] item The item that is to be relocated.
+ * @param[in] oldptr The previous address.
+ * @param[in] newptr The new address to relocate to.
+ * @param[in] relctx An application-provided context, set by #mdb_set_relctx().
+ * @todo This feature is currently unimplemented.
+ */
+typedef void (MDB_rel_func)(MDB_val *item, void *oldptr, void *newptr, void *relctx);
+
+/** @defgroup mdb_env Environment Flags
+ * @{
+ */
+ /** mmap at a fixed address (experimental) */
+#define MDB_FIXEDMAP 0x01
+ /** no environment directory */
+#define MDB_NOSUBDIR 0x4000
+ /** don't fsync after commit */
+#define MDB_NOSYNC 0x10000
+ /** read only */
+#define MDB_RDONLY 0x20000
+ /** don't fsync metapage after commit */
+#define MDB_NOMETASYNC 0x40000
+ /** use writable mmap */
+#define MDB_WRITEMAP 0x80000
+ /** use asynchronous msync when #MDB_WRITEMAP is used */
+#define MDB_MAPASYNC 0x100000
+ /** tie reader locktable slots to #MDB_txn objects instead of to threads */
+#define MDB_NOTLS 0x200000
+ /** don't do any locking, caller must manage their own locks */
+#define MDB_NOLOCK 0x400000
+ /** don't do readahead (no effect on Windows) */
+#define MDB_NORDAHEAD 0x800000
+ /** don't initialize malloc'd memory before writing to datafile */
+#define MDB_NOMEMINIT 0x1000000
+/** @} */
+
+/** @defgroup mdb_dbi_open Database Flags
+ * @{
+ */
+ /** use reverse string keys */
+#define MDB_REVERSEKEY 0x02
+ /** use sorted duplicates */
+#define MDB_DUPSORT 0x04
+ /** numeric keys in native byte order: either unsigned int or size_t.
+ * The keys must all be of the same size. */
+#define MDB_INTEGERKEY 0x08
+ /** with #MDB_DUPSORT, sorted dup items have fixed size */
+#define MDB_DUPFIXED 0x10
+ /** with #MDB_DUPSORT, dups are #MDB_INTEGERKEY-style integers */
+#define MDB_INTEGERDUP 0x20
+ /** with #MDB_DUPSORT, use reverse string dups */
+#define MDB_REVERSEDUP 0x40
+ /** create DB if not already existing */
+#define MDB_CREATE 0x40000
+/** @} */
+
+/** @defgroup mdb_put Write Flags
+ * @{
+ */
+/** For put: Don't write if the key already exists. */
+#define MDB_NOOVERWRITE 0x10
+/** Only for #MDB_DUPSORT<br>
+ * For put: don't write if the key and data pair already exist.<br>
+ * For mdb_cursor_del: remove all duplicate data items.
+ */
+#define MDB_NODUPDATA 0x20
+/** For mdb_cursor_put: overwrite the current key/data pair */
+#define MDB_CURRENT 0x40
+/** For put: Just reserve space for data, don't copy it. Return a
+ * pointer to the reserved space.
+ */
+#define MDB_RESERVE 0x10000
+/** Data is being appended, don't split full pages. */
+#define MDB_APPEND 0x20000
+/** Duplicate data is being appended, don't split full pages. */
+#define MDB_APPENDDUP 0x40000
+/** Store multiple data items in one call. Only for #MDB_DUPFIXED. */
+#define MDB_MULTIPLE 0x80000
+/* @} */
+
+/** @defgroup mdb_copy Copy Flags
+ * @{
+ */
+/** Compacting copy: Omit free space from copy, and renumber all
+ * pages sequentially.
+ */
+#define MDB_CP_COMPACT 0x01
+/* @} */
+
+/** @brief Cursor Get operations.
+ *
+ * This is the set of all operations for retrieving data
+ * using a cursor.
+ */
+typedef enum MDB_cursor_op {
+ MDB_FIRST, /**< Position at first key/data item */
+ MDB_FIRST_DUP, /**< Position at first data item of current key.
+ Only for #MDB_DUPSORT */
+ MDB_GET_BOTH, /**< Position at key/data pair. Only for #MDB_DUPSORT */
+ MDB_GET_BOTH_RANGE, /**< position at key, nearest data. Only for #MDB_DUPSORT */
+ MDB_GET_CURRENT, /**< Return key/data at current cursor position */
+ MDB_GET_MULTIPLE, /**< Return up to a page of duplicate data items
+ from current cursor position. Move cursor to prepare
+ for #MDB_NEXT_MULTIPLE. Only for #MDB_DUPFIXED */
+ MDB_LAST, /**< Position at last key/data item */
+ MDB_LAST_DUP, /**< Position at last data item of current key.
+ Only for #MDB_DUPSORT */
+ MDB_NEXT, /**< Position at next data item */
+ MDB_NEXT_DUP, /**< Position at next data item of current key.
+ Only for #MDB_DUPSORT */
+ MDB_NEXT_MULTIPLE, /**< Return up to a page of duplicate data items
+ from next cursor position. Move cursor to prepare
+ for #MDB_NEXT_MULTIPLE. Only for #MDB_DUPFIXED */
+ MDB_NEXT_NODUP, /**< Position at first data item of next key */
+ MDB_PREV, /**< Position at previous data item */
+ MDB_PREV_DUP, /**< Position at previous data item of current key.
+ Only for #MDB_DUPSORT */
+ MDB_PREV_NODUP, /**< Position at last data item of previous key */
+ MDB_SET, /**< Position at specified key */
+ MDB_SET_KEY, /**< Position at specified key, return key + data */
+ MDB_SET_RANGE, /**< Position at first key greater than or equal to specified key. */
+ MDB_PREV_MULTIPLE /**< Position at previous page and return up to
+ a page of duplicate data items. Only for #MDB_DUPFIXED */
+} MDB_cursor_op;
+
+/** @defgroup errors Return Codes
+ *
+ * BerkeleyDB uses -30800 to -30999, we'll go under them
+ * @{
+ */
+ /** Successful result */
+#define MDB_SUCCESS 0
+ /** key/data pair already exists */
+#define MDB_KEYEXIST (-30799)
+ /** key/data pair not found (EOF) */
+#define MDB_NOTFOUND (-30798)
+ /** Requested page not found - this usually indicates corruption */
+#define MDB_PAGE_NOTFOUND (-30797)
+ /** Located page was wrong type */
+#define MDB_CORRUPTED (-30796)
+ /** Update of meta page failed or environment had fatal error */
+#define MDB_PANIC (-30795)
+ /** Environment version mismatch */
+#define MDB_VERSION_MISMATCH (-30794)
+ /** File is not a valid LMDB file */
+#define MDB_INVALID (-30793)
+ /** Environment mapsize reached */
+#define MDB_MAP_FULL (-30792)
+ /** Environment maxdbs reached */
+#define MDB_DBS_FULL (-30791)
+ /** Environment maxreaders reached */
+#define MDB_READERS_FULL (-30790)
+ /** Too many TLS keys in use - Windows only */
+#define MDB_TLS_FULL (-30789)
+ /** Txn has too many dirty pages */
+#define MDB_TXN_FULL (-30788)
+ /** Cursor stack too deep - internal error */
+#define MDB_CURSOR_FULL (-30787)
+ /** Page has not enough space - internal error */
+#define MDB_PAGE_FULL (-30786)
+ /** Database contents grew beyond environment mapsize */
+#define MDB_MAP_RESIZED (-30785)
+ /** Operation and DB incompatible, or DB type changed. This can mean:
+ * <ul>
+ * <li>The operation expects an #MDB_DUPSORT / #MDB_DUPFIXED database.
+ * <li>Opening a named DB when the unnamed DB has #MDB_DUPSORT / #MDB_INTEGERKEY.
+ * <li>Accessing a data record as a database, or vice versa.
+ * <li>The database was dropped and recreated with different flags.
+ * </ul>
+ */
+#define MDB_INCOMPATIBLE (-30784)
+ /** Invalid reuse of reader locktable slot */
+#define MDB_BAD_RSLOT (-30783)
+ /** Transaction must abort, has a child, or is invalid */
+#define MDB_BAD_TXN (-30782)
+ /** Unsupported size of key/DB name/data, or wrong DUPFIXED size */
+#define MDB_BAD_VALSIZE (-30781)
+ /** The specified DBI was changed unexpectedly */
+#define MDB_BAD_DBI (-30780)
+ /** The last defined error code */
+#define MDB_LAST_ERRCODE MDB_BAD_DBI
+/** @} */
+
+/** @brief Statistics for a database in the environment */
+typedef struct MDB_stat {
+ unsigned int ms_psize; /**< Size of a database page.
+ This is currently the same for all databases. */
+ unsigned int ms_depth; /**< Depth (height) of the B-tree */
+ size_t ms_branch_pages; /**< Number of internal (non-leaf) pages */
+ size_t ms_leaf_pages; /**< Number of leaf pages */
+ size_t ms_overflow_pages; /**< Number of overflow pages */
+ size_t ms_entries; /**< Number of data items */
+} MDB_stat;
+
+/** @brief Information about the environment */
+typedef struct MDB_envinfo {
+ void *me_mapaddr; /**< Address of map, if fixed */
+ size_t me_mapsize; /**< Size of the data memory map */
+ size_t me_last_pgno; /**< ID of the last used page */
+ size_t me_last_txnid; /**< ID of the last committed transaction */
+ unsigned int me_maxreaders; /**< max reader slots in the environment */
+ unsigned int me_numreaders; /**< max reader slots used in the environment */
+} MDB_envinfo;
+
+ /** @brief Return the LMDB library version information.
+ *
+ * @param[out] major if non-NULL, the library major version number is copied here
+ * @param[out] minor if non-NULL, the library minor version number is copied here
+ * @param[out] patch if non-NULL, the library patch version number is copied here
+ * @retval "version string" The library version as a string
+ */
+char *mdb_version(int *major, int *minor, int *patch);
+
+ /** @brief Return a string describing a given error code.
+ *
+ * This function is a superset of the ANSI C X3.159-1989 (ANSI C) strerror(3)
+ * function. If the error code is greater than or equal to 0, then the string
+ * returned by the system function strerror(3) is returned. If the error code
+ * is less than 0, an error string corresponding to the LMDB library error is
+ * returned. See @ref errors for a list of LMDB-specific error codes.
+ * @param[in] err The error code
+ * @retval "error message" The description of the error
+ */
+char *mdb_strerror(int err);
+
+ /** @brief Create an LMDB environment handle.
+ *
+ * This function allocates memory for a #MDB_env structure. To release
+ * the allocated memory and discard the handle, call #mdb_env_close().
+ * Before the handle may be used, it must be opened using #mdb_env_open().
+ * Various other options may also need to be set before opening the handle,
+ * e.g. #mdb_env_set_mapsize(), #mdb_env_set_maxreaders(), #mdb_env_set_maxdbs(),
+ * depending on usage requirements.
+ * @param[out] env The address where the new handle will be stored
+ * @return A non-zero error value on failure and 0 on success.
+ */
+int mdb_env_create(MDB_env **env);
+
+ /** @brief Open an environment handle.
+ *
+ * If this function fails, #mdb_env_close() must be called to discard the #MDB_env handle.
+ * @param[in] env An environment handle returned by #mdb_env_create()
+ * @param[in] path The directory in which the database files reside. This
+ * directory must already exist and be writable.
+ * @param[in] flags Special options for this environment. This parameter
+ * must be set to 0 or by bitwise OR'ing together one or more of the
+ * values described here.
+ * Flags set by mdb_env_set_flags() are also used.
+ * <ul>
+ * <li>#MDB_FIXEDMAP
+ * use a fixed address for the mmap region. This flag must be specified
+ * when creating the environment, and is stored persistently in the environment.
+ * If successful, the memory map will always reside at the same virtual address
+ * and pointers used to reference data items in the database will be constant
+ * across multiple invocations. This option may not always work, depending on
+ * how the operating system has allocated memory to shared libraries and other uses.
+ * The feature is highly experimental.
+ * <li>#MDB_NOSUBDIR
+ * By default, LMDB creates its environment in a directory whose
+ * pathname is given in \b path, and creates its data and lock files
+ * under that directory. With this option, \b path is used as-is for
+ * the database main data file. The database lock file is the \b path
+ * with "-lock" appended.
+ * <li>#MDB_RDONLY
+ * Open the environment in read-only mode. No write operations will be
+ * allowed. LMDB will still modify the lock file - except on read-only
+ * filesystems, where LMDB does not use locks.
+ * <li>#MDB_WRITEMAP
+ * Use a writeable memory map unless MDB_RDONLY is set. This uses
+ * fewer mallocs but loses protection from application bugs
+ * like wild pointer writes and other bad updates into the database.
+ * This may be slightly faster for DBs that fit entirely in RAM, but
+ * is slower for DBs larger than RAM.
+ * Incompatible with nested transactions.
+ * Do not mix processes with and without MDB_WRITEMAP on the same
+ * environment. This can defeat durability (#mdb_env_sync etc).
+ * <li>#MDB_NOMETASYNC
+ * Flush system buffers to disk only once per transaction, omit the
+ * metadata flush. Defer that until the system flushes files to disk,
+ * or next non-MDB_RDONLY commit or #mdb_env_sync(). This optimization
+ * maintains database integrity, but a system crash may undo the last
+ * committed transaction. I.e. it preserves the ACI (atomicity,
+ * consistency, isolation) but not D (durability) database property.
+ * This flag may be changed at any time using #mdb_env_set_flags().
+ * <li>#MDB_NOSYNC
+ * Don't flush system buffers to disk when committing a transaction.
+ * This optimization means a system crash can corrupt the database or
+ * lose the last transactions if buffers are not yet flushed to disk.
+ * The risk is governed by how often the system flushes dirty buffers
+ * to disk and how often #mdb_env_sync() is called. However, if the
+ * filesystem preserves write order and the #MDB_WRITEMAP flag is not
+ * used, transactions exhibit ACI (atomicity, consistency, isolation)
+ * properties and only lose D (durability). I.e. database integrity
+ * is maintained, but a system crash may undo the final transactions.
+ * Note that (#MDB_NOSYNC | #MDB_WRITEMAP) leaves the system with no
+ * hint for when to write transactions to disk, unless #mdb_env_sync()
+ * is called. (#MDB_MAPASYNC | #MDB_WRITEMAP) may be preferable.
+ * This flag may be changed at any time using #mdb_env_set_flags().
+ * <li>#MDB_MAPASYNC
+ * When using #MDB_WRITEMAP, use asynchronous flushes to disk.
+ * As with #MDB_NOSYNC, a system crash can then corrupt the
+ * database or lose the last transactions. Calling #mdb_env_sync()
+ * ensures on-disk database integrity until next commit.
+ * This flag may be changed at any time using #mdb_env_set_flags().
+ * <li>#MDB_NOTLS
+ * Don't use Thread-Local Storage. Tie reader locktable slots to
+ * #MDB_txn objects instead of to threads. I.e. #mdb_txn_reset() keeps
+ * the slot reserved for the #MDB_txn object. A thread may use parallel
+ * read-only transactions. A read-only transaction may span threads if
+ * the user synchronizes its use. Applications that multiplex many
+ * user threads over individual OS threads need this option. Such an
+ * application must also serialize the write transactions in an OS
+ * thread, since LMDB's write locking is unaware of the user threads.
+ * <li>#MDB_NOLOCK
+ * Don't do any locking. If concurrent access is anticipated, the
+ * caller must manage all concurrency itself. For proper operation
+ * the caller must enforce single-writer semantics, and must ensure
+ * that no readers are using old transactions while a writer is
+ * active. The simplest approach is to use an exclusive lock so that
+ * no readers may be active at all when a writer begins.
+ * <li>#MDB_NORDAHEAD
+ * Turn off readahead. Most operating systems perform readahead on
+ * read requests by default. This option turns it off if the OS
+ * supports it. Turning it off may help random read performance
+ * when the DB is larger than RAM and system RAM is full.
+ * The option is not implemented on Windows.
+ * <li>#MDB_NOMEMINIT
+ * Don't initialize malloc'd memory before writing to unused spaces
+ * in the data file. By default, memory for pages written to the data
+ * file is obtained using malloc. While these pages may be reused in
+ * subsequent transactions, freshly malloc'd pages will be initialized
+ * to zeroes before use. This avoids persisting leftover data from other
+ * code (that used the heap and subsequently freed the memory) into the
+ * data file. Note that many other system libraries may allocate
+ * and free memory from the heap for arbitrary uses. E.g., stdio may
+ * use the heap for file I/O buffers. This initialization step has a
+ * modest performance cost so some applications may want to disable
+ * it using this flag. This option can be a problem for applications
+ * which handle sensitive data like passwords, and it makes memory
+ * checkers like Valgrind noisy. This flag is not needed with #MDB_WRITEMAP,
+ * which writes directly to the mmap instead of using malloc for pages. The
+ * initialization is also skipped if #MDB_RESERVE is used; the
+ * caller is expected to overwrite all of the memory that was
+ * reserved in that case.
+ * This flag may be changed at any time using #mdb_env_set_flags().
+ * </ul>
+ * @param[in] mode The UNIX permissions to set on created files and semaphores.
+ * This parameter is ignored on Windows.
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>#MDB_VERSION_MISMATCH - the version of the LMDB library doesn't match the
+ * version that created the database environment.
+ * <li>#MDB_INVALID - the environment file headers are corrupted.
+ * <li>ENOENT - the directory specified by the path parameter doesn't exist.
+ * <li>EACCES - the user didn't have permission to access the environment files.
+ * <li>EAGAIN - the environment was locked by another process.
+ * </ul>
+ */
+int mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode);
+
+ /** @brief Copy an LMDB environment to the specified path.
+ *
+ * This function may be used to make a backup of an existing environment.
+ * No lockfile is created, since it gets recreated at need.
+ * @note This call can trigger significant file size growth if run in
+ * parallel with write transactions, because it employs a read-only
+ * transaction. See long-lived transactions under @ref caveats_sec.
+ * @param[in] env An environment handle returned by #mdb_env_create(). It
+ * must have already been opened successfully.
+ * @param[in] path The directory in which the copy will reside. This
+ * directory must already exist and be writable but must otherwise be
+ * empty.
+ * @return A non-zero error value on failure and 0 on success.
+ */
+int mdb_env_copy(MDB_env *env, const char *path);
+
+ /** @brief Copy an LMDB environment to the specified file descriptor.
+ *
+ * This function may be used to make a backup of an existing environment.
+ * No lockfile is created, since it gets recreated at need.
+ * @note This call can trigger significant file size growth if run in
+ * parallel with write transactions, because it employs a read-only
+ * transaction. See long-lived transactions under @ref caveats_sec.
+ * @param[in] env An environment handle returned by #mdb_env_create(). It
+ * must have already been opened successfully.
+ * @param[in] fd The filedescriptor to write the copy to. It must
+ * have already been opened for Write access.
+ * @return A non-zero error value on failure and 0 on success.
+ */
+int mdb_env_copyfd(MDB_env *env, mdb_filehandle_t fd);
+
+ /** @brief Copy an LMDB environment to the specified path, with options.
+ *
+ * This function may be used to make a backup of an existing environment.
+ * No lockfile is created, since it gets recreated at need.
+ * @note This call can trigger significant file size growth if run in
+ * parallel with write transactions, because it employs a read-only
+ * transaction. See long-lived transactions under @ref caveats_sec.
+ * @param[in] env An environment handle returned by #mdb_env_create(). It
+ * must have already been opened successfully.
+ * @param[in] path The directory in which the copy will reside. This
+ * directory must already exist and be writable but must otherwise be
+ * empty.
+ * @param[in] flags Special options for this operation. This parameter
+ * must be set to 0 or by bitwise OR'ing together one or more of the
+ * values described here.
+ * <ul>
+ * <li>#MDB_CP_COMPACT - Perform compaction while copying: omit free
+ * pages and sequentially renumber all pages in output. This option
+ * consumes more CPU and runs more slowly than the default.
+ * Currently it fails if the environment has suffered a page leak.
+ * </ul>
+ * @return A non-zero error value on failure and 0 on success.
+ */
+int mdb_env_copy2(MDB_env *env, const char *path, unsigned int flags);
+
+ /** @brief Copy an LMDB environment to the specified file descriptor,
+ * with options.
+ *
+ * This function may be used to make a backup of an existing environment.
+ * No lockfile is created, since it gets recreated at need. See
+ * #mdb_env_copy2() for further details.
+ * @note This call can trigger significant file size growth if run in
+ * parallel with write transactions, because it employs a read-only
+ * transaction. See long-lived transactions under @ref caveats_sec.
+ * @param[in] env An environment handle returned by #mdb_env_create(). It
+ * must have already been opened successfully.
+ * @param[in] fd The filedescriptor to write the copy to. It must
+ * have already been opened for Write access.
+ * @param[in] flags Special options for this operation.
+ * See #mdb_env_copy2() for options.
+ * @return A non-zero error value on failure and 0 on success.
+ */
+int mdb_env_copyfd2(MDB_env *env, mdb_filehandle_t fd, unsigned int flags);
+
+ /** @brief Return statistics about the LMDB environment.
+ *
+ * @param[in] env An environment handle returned by #mdb_env_create()
+ * @param[out] stat The address of an #MDB_stat structure
+ * where the statistics will be copied
+ */
+int mdb_env_stat(MDB_env *env, MDB_stat *stat);
+
+ /** @brief Return information about the LMDB environment.
+ *
+ * @param[in] env An environment handle returned by #mdb_env_create()
+ * @param[out] stat The address of an #MDB_envinfo structure
+ * where the information will be copied
+ */
+int mdb_env_info(MDB_env *env, MDB_envinfo *stat);
+
+ /** @brief Flush the data buffers to disk.
+ *
+ * Data is always written to disk when #mdb_txn_commit() is called,
+ * but the operating system may keep it buffered. LMDB always flushes
+ * the OS buffers upon commit as well, unless the environment was
+ * opened with #MDB_NOSYNC or in part #MDB_NOMETASYNC. This call is
+ * not valid if the environment was opened with #MDB_RDONLY.
+ * @param[in] env An environment handle returned by #mdb_env_create()
+ * @param[in] force If non-zero, force a synchronous flush. Otherwise
+ * if the environment has the #MDB_NOSYNC flag set the flushes
+ * will be omitted, and with #MDB_MAPASYNC they will be asynchronous.
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>EACCES - the environment is read-only.
+ * <li>EINVAL - an invalid parameter was specified.
+ * <li>EIO - an error occurred during synchronization.
+ * </ul>
+ */
+int mdb_env_sync(MDB_env *env, int force);
+
+ /** @brief Close the environment and release the memory map.
+ *
+ * Only a single thread may call this function. All transactions, databases,
+ * and cursors must already be closed before calling this function. Attempts to
+ * use any such handles after calling this function will cause a SIGSEGV.
+ * The environment handle will be freed and must not be used again after this call.
+ * @param[in] env An environment handle returned by #mdb_env_create()
+ */
+void mdb_env_close(MDB_env *env);
+
+ /** @brief Set environment flags.
+ *
+ * This may be used to set some flags in addition to those from
+ * #mdb_env_open(), or to unset these flags. If several threads
+ * change the flags at the same time, the result is undefined.
+ * @param[in] env An environment handle returned by #mdb_env_create()
+ * @param[in] flags The flags to change, bitwise OR'ed together
+ * @param[in] onoff A non-zero value sets the flags, zero clears them.
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>EINVAL - an invalid parameter was specified.
+ * </ul>
+ */
+int mdb_env_set_flags(MDB_env *env, unsigned int flags, int onoff);
+
+ /** @brief Get environment flags.
+ *
+ * @param[in] env An environment handle returned by #mdb_env_create()
+ * @param[out] flags The address of an integer to store the flags
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>EINVAL - an invalid parameter was specified.
+ * </ul>
+ */
+int mdb_env_get_flags(MDB_env *env, unsigned int *flags);
+
+ /** @brief Return the path that was used in #mdb_env_open().
+ *
+ * @param[in] env An environment handle returned by #mdb_env_create()
+ * @param[out] path Address of a string pointer to contain the path. This
+ * is the actual string in the environment, not a copy. It should not be
+ * altered in any way.
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>EINVAL - an invalid parameter was specified.
+ * </ul>
+ */
+int mdb_env_get_path(MDB_env *env, const char **path);
+
+ /** @brief Return the filedescriptor for the given environment.
+ *
+ * This function may be called after fork(), so the descriptor can be
+ * closed before exec*(). Other LMDB file descriptors have FD_CLOEXEC.
+ * (Until LMDB 0.9.18, only the lockfile had that.)
+ *
+ * @param[in] env An environment handle returned by #mdb_env_create()
+ * @param[out] fd Address of a mdb_filehandle_t to contain the descriptor.
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>EINVAL - an invalid parameter was specified.
+ * </ul>
+ */
+int mdb_env_get_fd(MDB_env *env, mdb_filehandle_t *fd);
+
+ /** @brief Set the size of the memory map to use for this environment.
+ *
+ * The size should be a multiple of the OS page size. The default is
+ * 10485760 bytes. The size of the memory map is also the maximum size
+ * of the database. The value should be chosen as large as possible,
+ * to accommodate future growth of the database.
+ * This function should be called after #mdb_env_create() and before #mdb_env_open().
+ * It may be called at later times if no transactions are active in
+ * this process. Note that the library does not check for this condition,
+ * the caller must ensure it explicitly.
+ *
+ * The new size takes effect immediately for the current process but
+ * will not be persisted to any others until a write transaction has been
+ * committed by the current process. Also, only mapsize increases are
+ * persisted into the environment.
+ *
+ * If the mapsize is increased by another process, and data has grown
+ * beyond the range of the current mapsize, #mdb_txn_begin() will
+ * return #MDB_MAP_RESIZED. This function may be called with a size
+ * of zero to adopt the new size.
+ *
+ * Any attempt to set a size smaller than the space already consumed
+ * by the environment will be silently changed to the current size of the used space.
+ * @param[in] env An environment handle returned by #mdb_env_create()
+ * @param[in] size The size in bytes
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>EINVAL - an invalid parameter was specified, or the environment has
+ * an active write transaction.
+ * </ul>
+ */
+int mdb_env_set_mapsize(MDB_env *env, size_t size);
+
+ /** @brief Set the maximum number of threads/reader slots for the environment.
+ *
+ * This defines the number of slots in the lock table that is used to track readers in the
+ * the environment. The default is 126.
+ * Starting a read-only transaction normally ties a lock table slot to the
+ * current thread until the environment closes or the thread exits. If
+ * MDB_NOTLS is in use, #mdb_txn_begin() instead ties the slot to the
+ * MDB_txn object until it or the #MDB_env object is destroyed.
+ * This function may only be called after #mdb_env_create() and before #mdb_env_open().
+ * @param[in] env An environment handle returned by #mdb_env_create()
+ * @param[in] readers The maximum number of reader lock table slots
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>EINVAL - an invalid parameter was specified, or the environment is already open.
+ * </ul>
+ */
+int mdb_env_set_maxreaders(MDB_env *env, unsigned int readers);
+
+ /** @brief Get the maximum number of threads/reader slots for the environment.
+ *
+ * @param[in] env An environment handle returned by #mdb_env_create()
+ * @param[out] readers Address of an integer to store the number of readers
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>EINVAL - an invalid parameter was specified.
+ * </ul>
+ */
+int mdb_env_get_maxreaders(MDB_env *env, unsigned int *readers);
+
+ /** @brief Set the maximum number of named databases for the environment.
+ *
+ * This function is only needed if multiple databases will be used in the
+ * environment. Simpler applications that use the environment as a single
+ * unnamed database can ignore this option.
+ * This function may only be called after #mdb_env_create() and before #mdb_env_open().
+ *
+ * Currently a moderate number of slots are cheap but a huge number gets
+ * expensive: 7-120 words per transaction, and every #mdb_dbi_open()
+ * does a linear search of the opened slots.
+ * @param[in] env An environment handle returned by #mdb_env_create()
+ * @param[in] dbs The maximum number of databases
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>EINVAL - an invalid parameter was specified, or the environment is already open.
+ * </ul>
+ */
+int mdb_env_set_maxdbs(MDB_env *env, MDB_dbi dbs);
+
+ /** @brief Get the maximum size of keys and #MDB_DUPSORT data we can write.
+ *
+ * Depends on the compile-time constant #MDB_MAXKEYSIZE. Default 511.
+ * See @ref MDB_val.
+ * @param[in] env An environment handle returned by #mdb_env_create()
+ * @return The maximum size of a key we can write
+ */
+int mdb_env_get_maxkeysize(MDB_env *env);
+
+ /** @brief Set application information associated with the #MDB_env.
+ *
+ * @param[in] env An environment handle returned by #mdb_env_create()
+ * @param[in] ctx An arbitrary pointer for whatever the application needs.
+ * @return A non-zero error value on failure and 0 on success.
+ */
+int mdb_env_set_userctx(MDB_env *env, void *ctx);
+
+ /** @brief Get the application information associated with the #MDB_env.
+ *
+ * @param[in] env An environment handle returned by #mdb_env_create()
+ * @return The pointer set by #mdb_env_set_userctx().
+ */
+void *mdb_env_get_userctx(MDB_env *env);
+
+ /** @brief A callback function for most LMDB assert() failures,
+ * called before printing the message and aborting.
+ *
+ * @param[in] env An environment handle returned by #mdb_env_create().
+ * @param[in] msg The assertion message, not including newline.
+ */
+typedef void MDB_assert_func(MDB_env *env, const char *msg);
+
+ /** Set or reset the assert() callback of the environment.
+ * Disabled if liblmdb is built with NDEBUG.
+ * @note This hack should become obsolete as lmdb's error handling matures.
+ * @param[in] env An environment handle returned by #mdb_env_create().
+ * @param[in] func An #MDB_assert_func function, or 0.
+ * @return A non-zero error value on failure and 0 on success.
+ */
+int mdb_env_set_assert(MDB_env *env, MDB_assert_func *func);
+
+ /** @brief Create a transaction for use with the environment.
+ *
+ * The transaction handle may be discarded using #mdb_txn_abort() or #mdb_txn_commit().
+ * @note A transaction and its cursors must only be used by a single
+ * thread, and a thread may only have a single transaction at a time.
+ * If #MDB_NOTLS is in use, this does not apply to read-only transactions.
+ * @note Cursors may not span transactions.
+ * @param[in] env An environment handle returned by #mdb_env_create()
+ * @param[in] parent If this parameter is non-NULL, the new transaction
+ * will be a nested transaction, with the transaction indicated by \b parent
+ * as its parent. Transactions may be nested to any level. A parent
+ * transaction and its cursors may not issue any other operations than
+ * mdb_txn_commit and mdb_txn_abort while it has active child transactions.
+ * @param[in] flags Special options for this transaction. This parameter
+ * must be set to 0 or by bitwise OR'ing together one or more of the
+ * values described here.
+ * <ul>
+ * <li>#MDB_RDONLY
+ * This transaction will not perform any write operations.
+ * </ul>
+ * @param[out] txn Address where the new #MDB_txn handle will be stored
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>#MDB_PANIC - a fatal error occurred earlier and the environment
+ * must be shut down.
+ * <li>#MDB_MAP_RESIZED - another process wrote data beyond this MDB_env's
+ * mapsize and this environment's map must be resized as well.
+ * See #mdb_env_set_mapsize().
+ * <li>#MDB_READERS_FULL - a read-only transaction was requested and
+ * the reader lock table is full. See #mdb_env_set_maxreaders().
+ * <li>ENOMEM - out of memory.
+ * </ul>
+ */
+int mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **txn);
+
+ /** @brief Returns the transaction's #MDB_env
+ *
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ */
+MDB_env *mdb_txn_env(MDB_txn *txn);
+
+ /** @brief Return the transaction's ID.
+ *
+ * This returns the identifier associated with this transaction. For a
+ * read-only transaction, this corresponds to the snapshot being read;
+ * concurrent readers will frequently have the same transaction ID.
+ *
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ * @return A transaction ID, valid if input is an active transaction.
+ */
+size_t mdb_txn_id(MDB_txn *txn);
+
+ /** @brief Commit all the operations of a transaction into the database.
+ *
+ * The transaction handle is freed. It and its cursors must not be used
+ * again after this call, except with #mdb_cursor_renew().
+ * @note Earlier documentation incorrectly said all cursors would be freed.
+ * Only write-transactions free cursors.
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>EINVAL - an invalid parameter was specified.
+ * <li>ENOSPC - no more disk space.
+ * <li>EIO - a low-level I/O error occurred while writing.
+ * <li>ENOMEM - out of memory.
+ * </ul>
+ */
+int mdb_txn_commit(MDB_txn *txn);
+
+ /** @brief Abandon all the operations of the transaction instead of saving them.
+ *
+ * The transaction handle is freed. It and its cursors must not be used
+ * again after this call, except with #mdb_cursor_renew().
+ * @note Earlier documentation incorrectly said all cursors would be freed.
+ * Only write-transactions free cursors.
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ */
+void mdb_txn_abort(MDB_txn *txn);
+
+ /** @brief Reset a read-only transaction.
+ *
+ * Abort the transaction like #mdb_txn_abort(), but keep the transaction
+ * handle. #mdb_txn_renew() may reuse the handle. This saves allocation
+ * overhead if the process will start a new read-only transaction soon,
+ * and also locking overhead if #MDB_NOTLS is in use. The reader table
+ * lock is released, but the table slot stays tied to its thread or
+ * #MDB_txn. Use mdb_txn_abort() to discard a reset handle, and to free
+ * its lock table slot if MDB_NOTLS is in use.
+ * Cursors opened within the transaction must not be used
+ * again after this call, except with #mdb_cursor_renew().
+ * Reader locks generally don't interfere with writers, but they keep old
+ * versions of database pages allocated. Thus they prevent the old pages
+ * from being reused when writers commit new data, and so under heavy load
+ * the database size may grow much more rapidly than otherwise.
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ */
+void mdb_txn_reset(MDB_txn *txn);
+
+ /** @brief Renew a read-only transaction.
+ *
+ * This acquires a new reader lock for a transaction handle that had been
+ * released by #mdb_txn_reset(). It must be called before a reset transaction
+ * may be used again.
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>#MDB_PANIC - a fatal error occurred earlier and the environment
+ * must be shut down.
+ * <li>EINVAL - an invalid parameter was specified.
+ * </ul>
+ */
+int mdb_txn_renew(MDB_txn *txn);
+
+/** Compat with version <= 0.9.4, avoid clash with libmdb from MDB Tools project */
+#define mdb_open(txn,name,flags,dbi) mdb_dbi_open(txn,name,flags,dbi)
+/** Compat with version <= 0.9.4, avoid clash with libmdb from MDB Tools project */
+#define mdb_close(env,dbi) mdb_dbi_close(env,dbi)
+
+ /** @brief Open a database in the environment.
+ *
+ * A database handle denotes the name and parameters of a database,
+ * independently of whether such a database exists.
+ * The database handle may be discarded by calling #mdb_dbi_close().
+ * The old database handle is returned if the database was already open.
+ * The handle may only be closed once.
+ *
+ * The database handle will be private to the current transaction until
+ * the transaction is successfully committed. If the transaction is
+ * aborted the handle will be closed automatically.
+ * After a successful commit the handle will reside in the shared
+ * environment, and may be used by other transactions.
+ *
+ * This function must not be called from multiple concurrent
+ * transactions in the same process. A transaction that uses
+ * this function must finish (either commit or abort) before
+ * any other transaction in the process may use this function.
+ *
+ * To use named databases (with name != NULL), #mdb_env_set_maxdbs()
+ * must be called before opening the environment. Database names are
+ * keys in the unnamed database, and may be read but not written.
+ *
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ * @param[in] name The name of the database to open. If only a single
+ * database is needed in the environment, this value may be NULL.
+ * @param[in] flags Special options for this database. This parameter
+ * must be set to 0 or by bitwise OR'ing together one or more of the
+ * values described here.
+ * <ul>
+ * <li>#MDB_REVERSEKEY
+ * Keys are strings to be compared in reverse order, from the end
+ * of the strings to the beginning. By default, Keys are treated as strings and
+ * compared from beginning to end.
+ * <li>#MDB_DUPSORT
+ * Duplicate keys may be used in the database. (Or, from another perspective,
+ * keys may have multiple data items, stored in sorted order.) By default
+ * keys must be unique and may have only a single data item.
+ * <li>#MDB_INTEGERKEY
+ * Keys are binary integers in native byte order, either unsigned int
+ * or size_t, and will be sorted as such.
+ * The keys must all be of the same size.
+ * <li>#MDB_DUPFIXED
+ * This flag may only be used in combination with #MDB_DUPSORT. This option
+ * tells the library that the data items for this database are all the same
+ * size, which allows further optimizations in storage and retrieval. When
+ * all data items are the same size, the #MDB_GET_MULTIPLE, #MDB_NEXT_MULTIPLE
+ * and #MDB_PREV_MULTIPLE cursor operations may be used to retrieve multiple
+ * items at once.
+ * <li>#MDB_INTEGERDUP
+ * This option specifies that duplicate data items are binary integers,
+ * similar to #MDB_INTEGERKEY keys.
+ * <li>#MDB_REVERSEDUP
+ * This option specifies that duplicate data items should be compared as
+ * strings in reverse order.
+ * <li>#MDB_CREATE
+ * Create the named database if it doesn't exist. This option is not
+ * allowed in a read-only transaction or a read-only environment.
+ * </ul>
+ * @param[out] dbi Address where the new #MDB_dbi handle will be stored
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>#MDB_NOTFOUND - the specified database doesn't exist in the environment
+ * and #MDB_CREATE was not specified.
+ * <li>#MDB_DBS_FULL - too many databases have been opened. See #mdb_env_set_maxdbs().
+ * </ul>
+ */
+int mdb_dbi_open(MDB_txn *txn, const char *name, unsigned int flags, MDB_dbi *dbi);
+
+ /** @brief Retrieve statistics for a database.
+ *
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ * @param[in] dbi A database handle returned by #mdb_dbi_open()
+ * @param[out] stat The address of an #MDB_stat structure
+ * where the statistics will be copied
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>EINVAL - an invalid parameter was specified.
+ * </ul>
+ */
+int mdb_stat(MDB_txn *txn, MDB_dbi dbi, MDB_stat *stat);
+
+ /** @brief Retrieve the DB flags for a database handle.
+ *
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ * @param[in] dbi A database handle returned by #mdb_dbi_open()
+ * @param[out] flags Address where the flags will be returned.
+ * @return A non-zero error value on failure and 0 on success.
+ */
+int mdb_dbi_flags(MDB_txn *txn, MDB_dbi dbi, unsigned int *flags);
+
+ /** @brief Close a database handle. Normally unnecessary. Use with care:
+ *
+ * This call is not mutex protected. Handles should only be closed by
+ * a single thread, and only if no other threads are going to reference
+ * the database handle or one of its cursors any further. Do not close
+ * a handle if an existing transaction has modified its database.
+ * Doing so can cause misbehavior from database corruption to errors
+ * like MDB_BAD_VALSIZE (since the DB name is gone).
+ *
+ * Closing a database handle is not necessary, but lets #mdb_dbi_open()
+ * reuse the handle value. Usually it's better to set a bigger
+ * #mdb_env_set_maxdbs(), unless that value would be large.
+ *
+ * @param[in] env An environment handle returned by #mdb_env_create()
+ * @param[in] dbi A database handle returned by #mdb_dbi_open()
+ */
+void mdb_dbi_close(MDB_env *env, MDB_dbi dbi);
+
+ /** @brief Empty or delete+close a database.
+ *
+ * See #mdb_dbi_close() for restrictions about closing the DB handle.
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ * @param[in] dbi A database handle returned by #mdb_dbi_open()
+ * @param[in] del 0 to empty the DB, 1 to delete it from the
+ * environment and close the DB handle.
+ * @return A non-zero error value on failure and 0 on success.
+ */
+int mdb_drop(MDB_txn *txn, MDB_dbi dbi, int del);
+
+ /** @brief Set a custom key comparison function for a database.
+ *
+ * The comparison function is called whenever it is necessary to compare a
+ * key specified by the application with a key currently stored in the database.
+ * If no comparison function is specified, and no special key flags were specified
+ * with #mdb_dbi_open(), the keys are compared lexically, with shorter keys collating
+ * before longer keys.
+ * @warning This function must be called before any data access functions are used,
+ * otherwise data corruption may occur. The same comparison function must be used by every
+ * program accessing the database, every time the database is used.
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ * @param[in] dbi A database handle returned by #mdb_dbi_open()
+ * @param[in] cmp A #MDB_cmp_func function
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>EINVAL - an invalid parameter was specified.
+ * </ul>
+ */
+int mdb_set_compare(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp);
+
+ /** @brief Set a custom data comparison function for a #MDB_DUPSORT database.
+ *
+ * This comparison function is called whenever it is necessary to compare a data
+ * item specified by the application with a data item currently stored in the database.
+ * This function only takes effect if the database was opened with the #MDB_DUPSORT
+ * flag.
+ * If no comparison function is specified, and no special key flags were specified
+ * with #mdb_dbi_open(), the data items are compared lexically, with shorter items collating
+ * before longer items.
+ * @warning This function must be called before any data access functions are used,
+ * otherwise data corruption may occur. The same comparison function must be used by every
+ * program accessing the database, every time the database is used.
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ * @param[in] dbi A database handle returned by #mdb_dbi_open()
+ * @param[in] cmp A #MDB_cmp_func function
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>EINVAL - an invalid parameter was specified.
+ * </ul>
+ */
+int mdb_set_dupsort(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp);
+
+ /** @brief Set a relocation function for a #MDB_FIXEDMAP database.
+ *
+ * @todo The relocation function is called whenever it is necessary to move the data
+ * of an item to a different position in the database (e.g. through tree
+ * balancing operations, shifts as a result of adds or deletes, etc.). It is
+ * intended to allow address/position-dependent data items to be stored in
+ * a database in an environment opened with the #MDB_FIXEDMAP option.
+ * Currently the relocation feature is unimplemented and setting
+ * this function has no effect.
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ * @param[in] dbi A database handle returned by #mdb_dbi_open()
+ * @param[in] rel A #MDB_rel_func function
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>EINVAL - an invalid parameter was specified.
+ * </ul>
+ */
+int mdb_set_relfunc(MDB_txn *txn, MDB_dbi dbi, MDB_rel_func *rel);
+
+ /** @brief Set a context pointer for a #MDB_FIXEDMAP database's relocation function.
+ *
+ * See #mdb_set_relfunc and #MDB_rel_func for more details.
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ * @param[in] dbi A database handle returned by #mdb_dbi_open()
+ * @param[in] ctx An arbitrary pointer for whatever the application needs.
+ * It will be passed to the callback function set by #mdb_set_relfunc
+ * as its \b relctx parameter whenever the callback is invoked.
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>EINVAL - an invalid parameter was specified.
+ * </ul>
+ */
+int mdb_set_relctx(MDB_txn *txn, MDB_dbi dbi, void *ctx);
+
+ /** @brief Get items from a database.
+ *
+ * This function retrieves key/data pairs from the database. The address
+ * and length of the data associated with the specified \b key are returned
+ * in the structure to which \b data refers.
+ * If the database supports duplicate keys (#MDB_DUPSORT) then the
+ * first data item for the key will be returned. Retrieval of other
+ * items requires the use of #mdb_cursor_get().
+ *
+ * @note The memory pointed to by the returned values is owned by the
+ * database. The caller need not dispose of the memory, and may not
+ * modify it in any way. For values returned in a read-only transaction
+ * any modification attempts will cause a SIGSEGV.
+ * @note Values returned from the database are valid only until a
+ * subsequent update operation, or the end of the transaction.
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ * @param[in] dbi A database handle returned by #mdb_dbi_open()
+ * @param[in] key The key to search for in the database
+ * @param[out] data The data corresponding to the key
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>#MDB_NOTFOUND - the key was not in the database.
+ * <li>EINVAL - an invalid parameter was specified.
+ * </ul>
+ */
+int mdb_get(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data);
+
+ /** @brief Store items into a database.
+ *
+ * This function stores key/data pairs in the database. The default behavior
+ * is to enter the new key/data pair, replacing any previously existing key
+ * if duplicates are disallowed, or adding a duplicate data item if
+ * duplicates are allowed (#MDB_DUPSORT).
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ * @param[in] dbi A database handle returned by #mdb_dbi_open()
+ * @param[in] key The key to store in the database
+ * @param[in,out] data The data to store
+ * @param[in] flags Special options for this operation. This parameter
+ * must be set to 0 or by bitwise OR'ing together one or more of the
+ * values described here.
+ * <ul>
+ * <li>#MDB_NODUPDATA - enter the new key/data pair only if it does not
+ * already appear in the database. This flag may only be specified
+ * if the database was opened with #MDB_DUPSORT. The function will
+ * return #MDB_KEYEXIST if the key/data pair already appears in the
+ * database.
+ * <li>#MDB_NOOVERWRITE - enter the new key/data pair only if the key
+ * does not already appear in the database. The function will return
+ * #MDB_KEYEXIST if the key already appears in the database, even if
+ * the database supports duplicates (#MDB_DUPSORT). The \b data
+ * parameter will be set to point to the existing item.
+ * <li>#MDB_RESERVE - reserve space for data of the given size, but
+ * don't copy the given data. Instead, return a pointer to the
+ * reserved space, which the caller can fill in later - before
+ * the next update operation or the transaction ends. This saves
+ * an extra memcpy if the data is being generated later.
+ * LMDB does nothing else with this memory, the caller is expected
+ * to modify all of the space requested. This flag must not be
+ * specified if the database was opened with #MDB_DUPSORT.
+ * <li>#MDB_APPEND - append the given key/data pair to the end of the
+ * database. This option allows fast bulk loading when keys are
+ * already known to be in the correct order. Loading unsorted keys
+ * with this flag will cause a #MDB_KEYEXIST error.
+ * <li>#MDB_APPENDDUP - as above, but for sorted dup data.
+ * </ul>
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>#MDB_MAP_FULL - the database is full, see #mdb_env_set_mapsize().
+ * <li>#MDB_TXN_FULL - the transaction has too many dirty pages.
+ * <li>EACCES - an attempt was made to write in a read-only transaction.
+ * <li>EINVAL - an invalid parameter was specified.
+ * </ul>
+ */
+int mdb_put(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data,
+ unsigned int flags);
+
+ /** @brief Delete items from a database.
+ *
+ * This function removes key/data pairs from the database.
+ * If the database does not support sorted duplicate data items
+ * (#MDB_DUPSORT) the data parameter is ignored.
+ * If the database supports sorted duplicates and the data parameter
+ * is NULL, all of the duplicate data items for the key will be
+ * deleted. Otherwise, if the data parameter is non-NULL
+ * only the matching data item will be deleted.
+ * This function will return #MDB_NOTFOUND if the specified key/data
+ * pair is not in the database.
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ * @param[in] dbi A database handle returned by #mdb_dbi_open()
+ * @param[in] key The key to delete from the database
+ * @param[in] data The data to delete
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>EACCES - an attempt was made to write in a read-only transaction.
+ * <li>EINVAL - an invalid parameter was specified.
+ * </ul>
+ */
+int mdb_del(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data);
+
+ /** @brief Create a cursor handle.
+ *
+ * A cursor is associated with a specific transaction and database.
+ * A cursor cannot be used when its database handle is closed. Nor
+ * when its transaction has ended, except with #mdb_cursor_renew().
+ * It can be discarded with #mdb_cursor_close().
+ * A cursor in a write-transaction can be closed before its transaction
+ * ends, and will otherwise be closed when its transaction ends.
+ * A cursor in a read-only transaction must be closed explicitly, before
+ * or after its transaction ends. It can be reused with
+ * #mdb_cursor_renew() before finally closing it.
+ * @note Earlier documentation said that cursors in every transaction
+ * were closed when the transaction committed or aborted.
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ * @param[in] dbi A database handle returned by #mdb_dbi_open()
+ * @param[out] cursor Address where the new #MDB_cursor handle will be stored
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>EINVAL - an invalid parameter was specified.
+ * </ul>
+ */
+int mdb_cursor_open(MDB_txn *txn, MDB_dbi dbi, MDB_cursor **cursor);
+
+ /** @brief Close a cursor handle.
+ *
+ * The cursor handle will be freed and must not be used again after this call.
+ * Its transaction must still be live if it is a write-transaction.
+ * @param[in] cursor A cursor handle returned by #mdb_cursor_open()
+ */
+void mdb_cursor_close(MDB_cursor *cursor);
+
+ /** @brief Renew a cursor handle.
+ *
+ * A cursor is associated with a specific transaction and database.
+ * Cursors that are only used in read-only
+ * transactions may be re-used, to avoid unnecessary malloc/free overhead.
+ * The cursor may be associated with a new read-only transaction, and
+ * referencing the same database handle as it was created with.
+ * This may be done whether the previous transaction is live or dead.
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ * @param[in] cursor A cursor handle returned by #mdb_cursor_open()
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>EINVAL - an invalid parameter was specified.
+ * </ul>
+ */
+int mdb_cursor_renew(MDB_txn *txn, MDB_cursor *cursor);
+
+ /** @brief Return the cursor's transaction handle.
+ *
+ * @param[in] cursor A cursor handle returned by #mdb_cursor_open()
+ */
+MDB_txn *mdb_cursor_txn(MDB_cursor *cursor);
+
+ /** @brief Return the cursor's database handle.
+ *
+ * @param[in] cursor A cursor handle returned by #mdb_cursor_open()
+ */
+MDB_dbi mdb_cursor_dbi(MDB_cursor *cursor);
+
+ /** @brief Retrieve by cursor.
+ *
+ * This function retrieves key/data pairs from the database. The address and length
+ * of the key are returned in the object to which \b key refers (except for the
+ * case of the #MDB_SET option, in which the \b key object is unchanged), and
+ * the address and length of the data are returned in the object to which \b data
+ * refers.
+ * See #mdb_get() for restrictions on using the output values.
+ * @param[in] cursor A cursor handle returned by #mdb_cursor_open()
+ * @param[in,out] key The key for a retrieved item
+ * @param[in,out] data The data of a retrieved item
+ * @param[in] op A cursor operation #MDB_cursor_op
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>#MDB_NOTFOUND - no matching key found.
+ * <li>EINVAL - an invalid parameter was specified.
+ * </ul>
+ */
+int mdb_cursor_get(MDB_cursor *cursor, MDB_val *key, MDB_val *data,
+ MDB_cursor_op op);
+
+ /** @brief Store by cursor.
+ *
+ * This function stores key/data pairs into the database.
+ * The cursor is positioned at the new item, or on failure usually near it.
+ * @note Earlier documentation incorrectly said errors would leave the
+ * state of the cursor unchanged.
+ * @param[in] cursor A cursor handle returned by #mdb_cursor_open()
+ * @param[in] key The key operated on.
+ * @param[in] data The data operated on.
+ * @param[in] flags Options for this operation. This parameter
+ * must be set to 0 or one of the values described here.
+ * <ul>
+ * <li>#MDB_CURRENT - replace the item at the current cursor position.
+ * The \b key parameter must still be provided, and must match it.
+ * If using sorted duplicates (#MDB_DUPSORT) the data item must still
+ * sort into the same place. This is intended to be used when the
+ * new data is the same size as the old. Otherwise it will simply
+ * perform a delete of the old record followed by an insert.
+ * <li>#MDB_NODUPDATA - enter the new key/data pair only if it does not
+ * already appear in the database. This flag may only be specified
+ * if the database was opened with #MDB_DUPSORT. The function will
+ * return #MDB_KEYEXIST if the key/data pair already appears in the
+ * database.
+ * <li>#MDB_NOOVERWRITE - enter the new key/data pair only if the key
+ * does not already appear in the database. The function will return
+ * #MDB_KEYEXIST if the key already appears in the database, even if
+ * the database supports duplicates (#MDB_DUPSORT).
+ * <li>#MDB_RESERVE - reserve space for data of the given size, but
+ * don't copy the given data. Instead, return a pointer to the
+ * reserved space, which the caller can fill in later - before
+ * the next update operation or the transaction ends. This saves
+ * an extra memcpy if the data is being generated later. This flag
+ * must not be specified if the database was opened with #MDB_DUPSORT.
+ * <li>#MDB_APPEND - append the given key/data pair to the end of the
+ * database. No key comparisons are performed. This option allows
+ * fast bulk loading when keys are already known to be in the
+ * correct order. Loading unsorted keys with this flag will cause
+ * a #MDB_KEYEXIST error.
+ * <li>#MDB_APPENDDUP - as above, but for sorted dup data.
+ * <li>#MDB_MULTIPLE - store multiple contiguous data elements in a
+ * single request. This flag may only be specified if the database
+ * was opened with #MDB_DUPFIXED. The \b data argument must be an
+ * array of two MDB_vals. The mv_size of the first MDB_val must be
+ * the size of a single data element. The mv_data of the first MDB_val
+ * must point to the beginning of the array of contiguous data elements.
+ * The mv_size of the second MDB_val must be the count of the number
+ * of data elements to store. On return this field will be set to
+ * the count of the number of elements actually written. The mv_data
+ * of the second MDB_val is unused.
+ * </ul>
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>#MDB_MAP_FULL - the database is full, see #mdb_env_set_mapsize().
+ * <li>#MDB_TXN_FULL - the transaction has too many dirty pages.
+ * <li>EACCES - an attempt was made to write in a read-only transaction.
+ * <li>EINVAL - an invalid parameter was specified.
+ * </ul>
+ */
+int mdb_cursor_put(MDB_cursor *cursor, MDB_val *key, MDB_val *data,
+ unsigned int flags);
+
+ /** @brief Delete current key/data pair
+ *
+ * This function deletes the key/data pair to which the cursor refers.
+ * This does not invalidate the cursor, so operations such as MDB_NEXT
+ * can still be used on it.
+ * Both MDB_NEXT and MDB_GET_CURRENT will return the same record after
+ * this operation.
+ * @param[in] cursor A cursor handle returned by #mdb_cursor_open()
+ * @param[in] flags Options for this operation. This parameter
+ * must be set to 0 or one of the values described here.
+ * <ul>
+ * <li>#MDB_NODUPDATA - delete all of the data items for the current key.
+ * This flag may only be specified if the database was opened with #MDB_DUPSORT.
+ * </ul>
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>EACCES - an attempt was made to write in a read-only transaction.
+ * <li>EINVAL - an invalid parameter was specified.
+ * </ul>
+ */
+int mdb_cursor_del(MDB_cursor *cursor, unsigned int flags);
+
+ /** @brief Return count of duplicates for current key.
+ *
+ * This call is only valid on databases that support sorted duplicate
+ * data items #MDB_DUPSORT.
+ * @param[in] cursor A cursor handle returned by #mdb_cursor_open()
+ * @param[out] countp Address where the count will be stored
+ * @return A non-zero error value on failure and 0 on success. Some possible
+ * errors are:
+ * <ul>
+ * <li>EINVAL - cursor is not initialized, or an invalid parameter was specified.
+ * </ul>
+ */
+int mdb_cursor_count(MDB_cursor *cursor, size_t *countp);
+
+ /** @brief Compare two data items according to a particular database.
+ *
+ * This returns a comparison as if the two data items were keys in the
+ * specified database.
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ * @param[in] dbi A database handle returned by #mdb_dbi_open()
+ * @param[in] a The first item to compare
+ * @param[in] b The second item to compare
+ * @return < 0 if a < b, 0 if a == b, > 0 if a > b
+ */
+int mdb_cmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b);
+
+ /** @brief Compare two data items according to a particular database.
+ *
+ * This returns a comparison as if the two items were data items of
+ * the specified database. The database must have the #MDB_DUPSORT flag.
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ * @param[in] dbi A database handle returned by #mdb_dbi_open()
+ * @param[in] a The first item to compare
+ * @param[in] b The second item to compare
+ * @return < 0 if a < b, 0 if a == b, > 0 if a > b
+ */
+int mdb_dcmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b);
+
+ /** @brief A callback function used to print a message from the library.
+ *
+ * @param[in] msg The string to be printed.
+ * @param[in] ctx An arbitrary context pointer for the callback.
+ * @return < 0 on failure, >= 0 on success.
+ */
+typedef int (MDB_msg_func)(const char *msg, void *ctx);
+
+ /** @brief Dump the entries in the reader lock table.
+ *
+ * @param[in] env An environment handle returned by #mdb_env_create()
+ * @param[in] func A #MDB_msg_func function
+ * @param[in] ctx Anything the message function needs
+ * @return < 0 on failure, >= 0 on success.
+ */
+int mdb_reader_list(MDB_env *env, MDB_msg_func *func, void *ctx);
+
+ /** @brief Check for stale entries in the reader lock table.
+ *
+ * @param[in] env An environment handle returned by #mdb_env_create()
+ * @param[out] dead Number of stale slots that were cleared
+ * @return 0 on success, non-zero on failure.
+ */
+int mdb_reader_check(MDB_env *env, int *dead);
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+/** @page tools LMDB Command Line Tools
+ The following describes the command line tools that are available for LMDB.
+ \li \ref mdb_copy_1
+ \li \ref mdb_dump_1
+ \li \ref mdb_load_1
+ \li \ref mdb_stat_1
+*/
+
+#endif /* _LMDB_H_ */
diff --git a/libraries/liblmdb/mdb.c b/libraries/liblmdb/mdb.c
new file mode 100644
index 0000000..8cecdb2
--- /dev/null
+++ b/libraries/liblmdb/mdb.c
@@ -0,0 +1,10320 @@
+/** @file mdb.c
+ * @brief Lightning memory-mapped database library
+ *
+ * A Btree-based database management library modeled loosely on the
+ * BerkeleyDB API, but much simplified.
+ */
+/*
+ * Copyright 2011-2021 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ *
+ * This code is derived from btree.c written by Martin Hedenfalk.
+ *
+ * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+#if defined(__WIN64__)
+#define _FILE_OFFSET_BITS 64
+#endif
+#ifdef _WIN32
+#include <malloc.h>
+#include <windows.h>
+#include <wchar.h> /* get wcscpy() */
+
+/** getpid() returns int; MinGW defines pid_t but MinGW64 typedefs it
+ * as int64 which is wrong. MSVC doesn't define it at all, so just
+ * don't use it.
+ */
+#define MDB_PID_T int
+#define MDB_THR_T DWORD
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef __GNUC__
+# include <sys/param.h>
+#else
+# define LITTLE_ENDIAN 1234
+# define BIG_ENDIAN 4321
+# define BYTE_ORDER LITTLE_ENDIAN
+# ifndef SSIZE_MAX
+# define SSIZE_MAX INT_MAX
+# endif
+#endif
+#else
+#include <sys/types.h>
+#include <sys/stat.h>
+#define MDB_PID_T pid_t
+#define MDB_THR_T pthread_t
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <sys/mman.h>
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#include <fcntl.h>
+#endif
+
+#if defined(__mips) && defined(__linux)
+/* MIPS has cache coherency issues, requires explicit cache control */
+#include <asm/cachectl.h>
+extern int cacheflush(char *addr, int nbytes, int cache);
+#define CACHEFLUSH(addr, bytes, cache) cacheflush(addr, bytes, cache)
+#else
+#define CACHEFLUSH(addr, bytes, cache)
+#endif
+
+#if defined(__linux) && !defined(MDB_FDATASYNC_WORKS)
+/** fdatasync is broken on ext3/ext4fs on older kernels, see
+ * description in #mdb_env_open2 comments. You can safely
+ * define MDB_FDATASYNC_WORKS if this code will only be run
+ * on kernels 3.6 and newer.
+ */
+#define BROKEN_FDATASYNC
+#endif
+
+#include <errno.h>
+#include <limits.h>
+#include <stddef.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifdef _MSC_VER
+#include <io.h>
+typedef SSIZE_T ssize_t;
+#else
+#include <unistd.h>
+#endif
+
+#if defined(__sun) || defined(ANDROID)
+/* Most platforms have posix_memalign, older may only have memalign */
+#define HAVE_MEMALIGN 1
+#include <malloc.h>
+/* On Solaris, we need the POSIX sigwait function */
+#if defined (__sun)
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+#endif
+
+#if !(defined(BYTE_ORDER) || defined(__BYTE_ORDER))
+#include <netinet/in.h>
+#include <resolv.h> /* defines BYTE_ORDER on HPUX and Solaris */
+#endif
+
+#if defined(__FreeBSD__) && defined(__FreeBSD_version) && __FreeBSD_version >= 1100110
+# define MDB_USE_POSIX_MUTEX 1
+# define MDB_USE_ROBUST 1
+#elif defined(__APPLE__) || defined (BSD) || defined(__FreeBSD_kernel__)
+# define MDB_USE_POSIX_SEM 1
+# define MDB_FDATASYNC fsync
+#elif defined(ANDROID)
+# define MDB_FDATASYNC fsync
+#endif
+
+#ifndef _WIN32
+#include <pthread.h>
+#include <signal.h>
+#ifdef MDB_USE_POSIX_SEM
+# define MDB_USE_HASH 1
+#include <semaphore.h>
+#else
+#define MDB_USE_POSIX_MUTEX 1
+#endif
+#endif
+
+#if defined(_WIN32) + defined(MDB_USE_POSIX_SEM) \
+ + defined(MDB_USE_POSIX_MUTEX) != 1
+# error "Ambiguous shared-lock implementation"
+#endif
+
+#ifdef USE_VALGRIND
+#include <valgrind/memcheck.h>
+#define VGMEMP_CREATE(h,r,z) VALGRIND_CREATE_MEMPOOL(h,r,z)
+#define VGMEMP_ALLOC(h,a,s) VALGRIND_MEMPOOL_ALLOC(h,a,s)
+#define VGMEMP_FREE(h,a) VALGRIND_MEMPOOL_FREE(h,a)
+#define VGMEMP_DESTROY(h) VALGRIND_DESTROY_MEMPOOL(h)
+#define VGMEMP_DEFINED(a,s) VALGRIND_MAKE_MEM_DEFINED(a,s)
+#else
+#define VGMEMP_CREATE(h,r,z)
+#define VGMEMP_ALLOC(h,a,s)
+#define VGMEMP_FREE(h,a)
+#define VGMEMP_DESTROY(h)
+#define VGMEMP_DEFINED(a,s)
+#endif
+
+#ifndef BYTE_ORDER
+# if (defined(_LITTLE_ENDIAN) || defined(_BIG_ENDIAN)) && !(defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN))
+/* Solaris just defines one or the other */
+# define LITTLE_ENDIAN 1234
+# define BIG_ENDIAN 4321
+# ifdef _LITTLE_ENDIAN
+# define BYTE_ORDER LITTLE_ENDIAN
+# else
+# define BYTE_ORDER BIG_ENDIAN
+# endif
+# else
+# define BYTE_ORDER __BYTE_ORDER
+# endif
+#endif
+
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN __LITTLE_ENDIAN
+#endif
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN __BIG_ENDIAN
+#endif
+
+#if defined(__i386) || defined(__x86_64) || defined(_M_IX86)
+#define MISALIGNED_OK 1
+#endif
+
+#include "lmdb.h"
+#include "midl.h"
+
+#if (BYTE_ORDER == LITTLE_ENDIAN) == (BYTE_ORDER == BIG_ENDIAN)
+# error "Unknown or unsupported endianness (BYTE_ORDER)"
+#elif (-6 & 5) || CHAR_BIT != 8 || UINT_MAX < 0xffffffff || ULONG_MAX % 0xFFFF
+# error "Two's complement, reasonably sized integer types, please"
+#endif
+
+#ifdef __GNUC__
+/** Put infrequently used env functions in separate section */
+# ifdef __APPLE__
+# define ESECT __attribute__ ((section("__TEXT,text_env")))
+# else
+# define ESECT __attribute__ ((section("text_env")))
+# endif
+#else
+#define ESECT
+#endif
+
+#ifdef _WIN32
+#define CALL_CONV WINAPI
+#else
+#define CALL_CONV
+#endif
+
+/** @defgroup internal LMDB Internals
+ * @{
+ */
+/** @defgroup compat Compatibility Macros
+ * A bunch of macros to minimize the amount of platform-specific ifdefs
+ * needed throughout the rest of the code. When the features this library
+ * needs are similar enough to POSIX to be hidden in a one-or-two line
+ * replacement, this macro approach is used.
+ * @{
+ */
+
+ /** Features under development */
+#ifndef MDB_DEVEL
+#define MDB_DEVEL 0
+#endif
+
+ /** Wrapper around __func__, which is a C99 feature */
+#if __STDC_VERSION__ >= 199901L
+# define mdb_func_ __func__
+#elif __GNUC__ >= 2 || _MSC_VER >= 1300
+# define mdb_func_ __FUNCTION__
+#else
+/* If a debug message says <mdb_unknown>(), update the #if statements above */
+# define mdb_func_ "<mdb_unknown>"
+#endif
+
+/* Internal error codes, not exposed outside liblmdb */
+#define MDB_NO_ROOT (MDB_LAST_ERRCODE + 10)
+#ifdef _WIN32
+#define MDB_OWNERDEAD ((int) WAIT_ABANDONED)
+#elif defined(MDB_USE_POSIX_MUTEX) && defined(EOWNERDEAD)
+#define MDB_OWNERDEAD EOWNERDEAD /**< #LOCK_MUTEX0() result if dead owner */
+#endif
+
+#ifdef __GLIBC__
+#define GLIBC_VER ((__GLIBC__ << 16 )| __GLIBC_MINOR__)
+#endif
+/** Some platforms define the EOWNERDEAD error code
+ * even though they don't support Robust Mutexes.
+ * Compile with -DMDB_USE_ROBUST=0, or use some other
+ * mechanism like -DMDB_USE_POSIX_SEM instead of
+ * -DMDB_USE_POSIX_MUTEX.
+ * (Posix semaphores are not robust.)
+ */
+#ifndef MDB_USE_ROBUST
+/* Android currently lacks Robust Mutex support. So does glibc < 2.4. */
+# if defined(MDB_USE_POSIX_MUTEX) && (defined(ANDROID) || \
+ (defined(__GLIBC__) && GLIBC_VER < 0x020004))
+# define MDB_USE_ROBUST 0
+# else
+# define MDB_USE_ROBUST 1
+# endif
+#endif /* !MDB_USE_ROBUST */
+
+#if defined(MDB_USE_POSIX_MUTEX) && (MDB_USE_ROBUST)
+/* glibc < 2.12 only provided _np API */
+# if (defined(__GLIBC__) && GLIBC_VER < 0x02000c) || \
+ (defined(PTHREAD_MUTEX_ROBUST_NP) && !defined(PTHREAD_MUTEX_ROBUST))
+# define PTHREAD_MUTEX_ROBUST PTHREAD_MUTEX_ROBUST_NP
+# define pthread_mutexattr_setrobust(attr, flag) pthread_mutexattr_setrobust_np(attr, flag)
+# define pthread_mutex_consistent(mutex) pthread_mutex_consistent_np(mutex)
+# endif
+#endif /* MDB_USE_POSIX_MUTEX && MDB_USE_ROBUST */
+
+#if defined(MDB_OWNERDEAD) && (MDB_USE_ROBUST)
+#define MDB_ROBUST_SUPPORTED 1
+#endif
+
+#ifdef _WIN32
+#define MDB_USE_HASH 1
+#define MDB_PIDLOCK 0
+#define THREAD_RET DWORD
+#define pthread_t HANDLE
+#define pthread_mutex_t HANDLE
+#define pthread_cond_t HANDLE
+typedef HANDLE mdb_mutex_t, mdb_mutexref_t;
+#define pthread_key_t DWORD
+#define pthread_self() GetCurrentThreadId()
+#define pthread_key_create(x,y) \
+ ((*(x) = TlsAlloc()) == TLS_OUT_OF_INDEXES ? ErrCode() : 0)
+#define pthread_key_delete(x) TlsFree(x)
+#define pthread_getspecific(x) TlsGetValue(x)
+#define pthread_setspecific(x,y) (TlsSetValue(x,y) ? 0 : ErrCode())
+#define pthread_mutex_unlock(x) ReleaseMutex(*x)
+#define pthread_mutex_lock(x) WaitForSingleObject(*x, INFINITE)
+#define pthread_cond_signal(x) SetEvent(*x)
+#define pthread_cond_wait(cond,mutex) do{SignalObjectAndWait(*mutex, *cond, INFINITE, FALSE); WaitForSingleObject(*mutex, INFINITE);}while(0)
+#define THREAD_CREATE(thr,start,arg) \
+ (((thr) = CreateThread(NULL, 0, start, arg, 0, NULL)) ? 0 : ErrCode())
+#define THREAD_FINISH(thr) \
+ (WaitForSingleObject(thr, INFINITE) ? ErrCode() : 0)
+#define LOCK_MUTEX0(mutex) WaitForSingleObject(mutex, INFINITE)
+#define UNLOCK_MUTEX(mutex) ReleaseMutex(mutex)
+#define mdb_mutex_consistent(mutex) 0
+#define getpid() GetCurrentProcessId()
+#define MDB_FDATASYNC(fd) (!FlushFileBuffers(fd))
+#define MDB_MSYNC(addr,len,flags) (!FlushViewOfFile(addr,len))
+#define ErrCode() GetLastError()
+#define GET_PAGESIZE(x) {SYSTEM_INFO si; GetSystemInfo(&si); (x) = si.dwPageSize;}
+#define close(fd) (CloseHandle(fd) ? 0 : -1)
+#define munmap(ptr,len) UnmapViewOfFile(ptr)
+#ifdef PROCESS_QUERY_LIMITED_INFORMATION
+#define MDB_PROCESS_QUERY_LIMITED_INFORMATION PROCESS_QUERY_LIMITED_INFORMATION
+#else
+#define MDB_PROCESS_QUERY_LIMITED_INFORMATION 0x1000
+#endif
+#define Z "I"
+#else
+#define THREAD_RET void *
+#define THREAD_CREATE(thr,start,arg) pthread_create(&thr,NULL,start,arg)
+#define THREAD_FINISH(thr) pthread_join(thr,NULL)
+#define Z "z" /**< printf format modifier for size_t */
+
+ /** For MDB_LOCK_FORMAT: True if readers take a pid lock in the lockfile */
+#define MDB_PIDLOCK 1
+
+#ifdef MDB_USE_POSIX_SEM
+
+typedef sem_t *mdb_mutex_t, *mdb_mutexref_t;
+#define LOCK_MUTEX0(mutex) mdb_sem_wait(mutex)
+#define UNLOCK_MUTEX(mutex) sem_post(mutex)
+
+static int
+mdb_sem_wait(sem_t *sem)
+{
+ int rc;
+ while ((rc = sem_wait(sem)) && (rc = errno) == EINTR) ;
+ return rc;
+}
+
+#else /* MDB_USE_POSIX_MUTEX: */
+ /** Shared mutex/semaphore as the original is stored.
+ *
+ * Not for copies. Instead it can be assigned to an #mdb_mutexref_t.
+ * When mdb_mutexref_t is a pointer and mdb_mutex_t is not, then it
+ * is array[size 1] so it can be assigned to the pointer.
+ */
+typedef pthread_mutex_t mdb_mutex_t[1];
+ /** Reference to an #mdb_mutex_t */
+typedef pthread_mutex_t *mdb_mutexref_t;
+ /** Lock the reader or writer mutex.
+ * Returns 0 or a code to give #mdb_mutex_failed(), as in #LOCK_MUTEX().
+ */
+#define LOCK_MUTEX0(mutex) pthread_mutex_lock(mutex)
+ /** Unlock the reader or writer mutex.
+ */
+#define UNLOCK_MUTEX(mutex) pthread_mutex_unlock(mutex)
+ /** Mark mutex-protected data as repaired, after death of previous owner.
+ */
+#define mdb_mutex_consistent(mutex) pthread_mutex_consistent(mutex)
+#endif /* MDB_USE_POSIX_SEM */
+
+ /** Get the error code for the last failed system function.
+ */
+#define ErrCode() errno
+
+ /** An abstraction for a file handle.
+ * On POSIX systems file handles are small integers. On Windows
+ * they're opaque pointers.
+ */
+#define HANDLE int
+
+ /** A value for an invalid file handle.
+ * Mainly used to initialize file variables and signify that they are
+ * unused.
+ */
+#define INVALID_HANDLE_VALUE (-1)
+
+ /** Get the size of a memory page for the system.
+ * This is the basic size that the platform's memory manager uses, and is
+ * fundamental to the use of memory-mapped files.
+ */
+#define GET_PAGESIZE(x) ((x) = sysconf(_SC_PAGE_SIZE))
+#endif
+
+#if defined(_WIN32) || defined(MDB_USE_POSIX_SEM)
+#define MNAME_LEN 32
+#else
+#define MNAME_LEN (sizeof(pthread_mutex_t))
+#endif
+
+/** @} */
+
+#ifdef MDB_ROBUST_SUPPORTED
+ /** Lock mutex, handle any error, set rc = result.
+ * Return 0 on success, nonzero (not rc) on error.
+ */
+#define LOCK_MUTEX(rc, env, mutex) \
+ (((rc) = LOCK_MUTEX0(mutex)) && \
+ ((rc) = mdb_mutex_failed(env, mutex, rc)))
+static int mdb_mutex_failed(MDB_env *env, mdb_mutexref_t mutex, int rc);
+#else
+#define LOCK_MUTEX(rc, env, mutex) ((rc) = LOCK_MUTEX0(mutex))
+#define mdb_mutex_failed(env, mutex, rc) (rc)
+#endif
+
+#ifndef _WIN32
+/** A flag for opening a file and requesting synchronous data writes.
+ * This is only used when writing a meta page. It's not strictly needed;
+ * we could just do a normal write and then immediately perform a flush.
+ * But if this flag is available it saves us an extra system call.
+ *
+ * @note If O_DSYNC is undefined but exists in /usr/include,
+ * preferably set some compiler flag to get the definition.
+ */
+#ifndef MDB_DSYNC
+# ifdef O_DSYNC
+# define MDB_DSYNC O_DSYNC
+# else
+# define MDB_DSYNC O_SYNC
+# endif
+#endif
+#endif
+
+/** Function for flushing the data of a file. Define this to fsync
+ * if fdatasync() is not supported.
+ */
+#ifndef MDB_FDATASYNC
+# define MDB_FDATASYNC fdatasync
+#endif
+
+#ifndef MDB_MSYNC
+# define MDB_MSYNC(addr,len,flags) msync(addr,len,flags)
+#endif
+
+#ifndef MS_SYNC
+#define MS_SYNC 1
+#endif
+
+#ifndef MS_ASYNC
+#define MS_ASYNC 0
+#endif
+
+ /** A page number in the database.
+ * Note that 64 bit page numbers are overkill, since pages themselves
+ * already represent 12-13 bits of addressable memory, and the OS will
+ * always limit applications to a maximum of 63 bits of address space.
+ *
+ * @note In the #MDB_node structure, we only store 48 bits of this value,
+ * which thus limits us to only 60 bits of addressable data.
+ */
+typedef MDB_ID pgno_t;
+
+ /** A transaction ID.
+ * See struct MDB_txn.mt_txnid for details.
+ */
+typedef MDB_ID txnid_t;
+
+/** @defgroup debug Debug Macros
+ * @{
+ */
+#ifndef MDB_DEBUG
+ /** Enable debug output. Needs variable argument macros (a C99 feature).
+ * Set this to 1 for copious tracing. Set to 2 to add dumps of all IDLs
+ * read from and written to the database (used for free space management).
+ */
+#define MDB_DEBUG 0
+#endif
+
+#if MDB_DEBUG
+static int mdb_debug;
+static txnid_t mdb_debug_start;
+
+ /** Print a debug message with printf formatting.
+ * Requires double parenthesis around 2 or more args.
+ */
+# define DPRINTF(args) ((void) ((mdb_debug) && DPRINTF0 args))
+# define DPRINTF0(fmt, ...) \
+ fprintf(stderr, "%s:%d " fmt "\n", mdb_func_, __LINE__, __VA_ARGS__)
+#else
+# define DPRINTF(args) ((void) 0)
+#endif
+ /** Print a debug string.
+ * The string is printed literally, with no format processing.
+ */
+#define DPUTS(arg) DPRINTF(("%s", arg))
+ /** Debugging output value of a cursor DBI: Negative in a sub-cursor. */
+#define DDBI(mc) \
+ (((mc)->mc_flags & C_SUB) ? -(int)(mc)->mc_dbi : (int)(mc)->mc_dbi)
+/** @} */
+
+ /** @brief The maximum size of a database page.
+ *
+ * It is 32k or 64k, since value-PAGEBASE must fit in
+ * #MDB_page.%mp_upper.
+ *
+ * LMDB will use database pages < OS pages if needed.
+ * That causes more I/O in write transactions: The OS must
+ * know (read) the whole page before writing a partial page.
+ *
+ * Note that we don't currently support Huge pages. On Linux,
+ * regular data files cannot use Huge pages, and in general
+ * Huge pages aren't actually pageable. We rely on the OS
+ * demand-pager to read our data and page it out when memory
+ * pressure from other processes is high. So until OSs have
+ * actual paging support for Huge pages, they're not viable.
+ */
+#define MAX_PAGESIZE (PAGEBASE ? 0x10000 : 0x8000)
+
+ /** The minimum number of keys required in a database page.
+ * Setting this to a larger value will place a smaller bound on the
+ * maximum size of a data item. Data items larger than this size will
+ * be pushed into overflow pages instead of being stored directly in
+ * the B-tree node. This value used to default to 4. With a page size
+ * of 4096 bytes that meant that any item larger than 1024 bytes would
+ * go into an overflow page. That also meant that on average 2-3KB of
+ * each overflow page was wasted space. The value cannot be lower than
+ * 2 because then there would no longer be a tree structure. With this
+ * value, items larger than 2KB will go into overflow pages, and on
+ * average only 1KB will be wasted.
+ */
+#define MDB_MINKEYS 2
+
+ /** A stamp that identifies a file as an LMDB file.
+ * There's nothing special about this value other than that it is easily
+ * recognizable, and it will reflect any byte order mismatches.
+ */
+#define MDB_MAGIC 0xBEEFC0DE
+
+ /** The version number for a database's datafile format. */
+#define MDB_DATA_VERSION ((MDB_DEVEL) ? 999 : 1)
+ /** The version number for a database's lockfile format. */
+#define MDB_LOCK_VERSION 1
+
+ /** @brief The max size of a key we can write, or 0 for computed max.
+ *
+ * This macro should normally be left alone or set to 0.
+ * Note that a database with big keys or dupsort data cannot be
+ * reliably modified by a liblmdb which uses a smaller max.
+ * The default is 511 for backwards compat, or 0 when #MDB_DEVEL.
+ *
+ * Other values are allowed, for backwards compat. However:
+ * A value bigger than the computed max can break if you do not
+ * know what you are doing, and liblmdb <= 0.9.10 can break when
+ * modifying a DB with keys/dupsort data bigger than its max.
+ *
+ * Data items in an #MDB_DUPSORT database are also limited to
+ * this size, since they're actually keys of a sub-DB. Keys and
+ * #MDB_DUPSORT data items must fit on a node in a regular page.
+ */
+#ifndef MDB_MAXKEYSIZE
+#define MDB_MAXKEYSIZE ((MDB_DEVEL) ? 0 : 511)
+#endif
+
+ /** The maximum size of a key we can write to the environment. */
+#if MDB_MAXKEYSIZE
+#define ENV_MAXKEY(env) (MDB_MAXKEYSIZE)
+#else
+#define ENV_MAXKEY(env) ((env)->me_maxkey)
+#endif
+
+ /** @brief The maximum size of a data item.
+ *
+ * We only store a 32 bit value for node sizes.
+ */
+#define MAXDATASIZE 0xffffffffUL
+
+#if MDB_DEBUG
+ /** Key size which fits in a #DKBUF.
+ * @ingroup debug
+ */
+#define DKBUF_MAXKEYSIZE ((MDB_MAXKEYSIZE) > 0 ? (MDB_MAXKEYSIZE) : 511)
+ /** A key buffer.
+ * @ingroup debug
+ * This is used for printing a hex dump of a key's contents.
+ */
+#define DKBUF char kbuf[DKBUF_MAXKEYSIZE*2+1]
+ /** Display a key in hex.
+ * @ingroup debug
+ * Invoke a function to display a key in hex.
+ */
+#define DKEY(x) mdb_dkey(x, kbuf)
+#else
+#define DKBUF
+#define DKEY(x) 0
+#endif
+
+ /** An invalid page number.
+ * Mainly used to denote an empty tree.
+ */
+#define P_INVALID (~(pgno_t)0)
+
+ /** Test if the flags \b f are set in a flag word \b w. */
+#define F_ISSET(w, f) (((w) & (f)) == (f))
+
+ /** Round \b n up to an even number. */
+#define EVEN(n) (((n) + 1U) & -2) /* sign-extending -2 to match n+1U */
+
+ /** Used for offsets within a single page.
+ * Since memory pages are typically 4 or 8KB in size, 12-13 bits,
+ * this is plenty.
+ */
+typedef uint16_t indx_t;
+
+ /** Default size of memory map.
+ * This is certainly too small for any actual applications. Apps should always set
+ * the size explicitly using #mdb_env_set_mapsize().
+ */
+#define DEFAULT_MAPSIZE 1048576
+
+/** @defgroup readers Reader Lock Table
+ * Readers don't acquire any locks for their data access. Instead, they
+ * simply record their transaction ID in the reader table. The reader
+ * mutex is needed just to find an empty slot in the reader table. The
+ * slot's address is saved in thread-specific data so that subsequent read
+ * transactions started by the same thread need no further locking to proceed.
+ *
+ * If #MDB_NOTLS is set, the slot address is not saved in thread-specific data.
+ *
+ * No reader table is used if the database is on a read-only filesystem, or
+ * if #MDB_NOLOCK is set.
+ *
+ * Since the database uses multi-version concurrency control, readers don't
+ * actually need any locking. This table is used to keep track of which
+ * readers are using data from which old transactions, so that we'll know
+ * when a particular old transaction is no longer in use. Old transactions
+ * that have discarded any data pages can then have those pages reclaimed
+ * for use by a later write transaction.
+ *
+ * The lock table is constructed such that reader slots are aligned with the
+ * processor's cache line size. Any slot is only ever used by one thread.
+ * This alignment guarantees that there will be no contention or cache
+ * thrashing as threads update their own slot info, and also eliminates
+ * any need for locking when accessing a slot.
+ *
+ * A writer thread will scan every slot in the table to determine the oldest
+ * outstanding reader transaction. Any freed pages older than this will be
+ * reclaimed by the writer. The writer doesn't use any locks when scanning
+ * this table. This means that there's no guarantee that the writer will
+ * see the most up-to-date reader info, but that's not required for correct
+ * operation - all we need is to know the upper bound on the oldest reader,
+ * we don't care at all about the newest reader. So the only consequence of
+ * reading stale information here is that old pages might hang around a
+ * while longer before being reclaimed. That's actually good anyway, because
+ * the longer we delay reclaiming old pages, the more likely it is that a
+ * string of contiguous pages can be found after coalescing old pages from
+ * many old transactions together.
+ * @{
+ */
+ /** Number of slots in the reader table.
+ * This value was chosen somewhat arbitrarily. 126 readers plus a
+ * couple mutexes fit exactly into 8KB on my development machine.
+ * Applications should set the table size using #mdb_env_set_maxreaders().
+ */
+#define DEFAULT_READERS 126
+
+ /** The size of a CPU cache line in bytes. We want our lock structures
+ * aligned to this size to avoid false cache line sharing in the
+ * lock table.
+ * This value works for most CPUs. For Itanium this should be 128.
+ */
+#ifndef CACHELINE
+#define CACHELINE 64
+#endif
+
+ /** The information we store in a single slot of the reader table.
+ * In addition to a transaction ID, we also record the process and
+ * thread ID that owns a slot, so that we can detect stale information,
+ * e.g. threads or processes that went away without cleaning up.
+ * @note We currently don't check for stale records. We simply re-init
+ * the table when we know that we're the only process opening the
+ * lock file.
+ */
+typedef struct MDB_rxbody {
+ /** Current Transaction ID when this transaction began, or (txnid_t)-1.
+ * Multiple readers that start at the same time will probably have the
+ * same ID here. Again, it's not important to exclude them from
+ * anything; all we need to know is which version of the DB they
+ * started from so we can avoid overwriting any data used in that
+ * particular version.
+ */
+ volatile txnid_t mrb_txnid;
+ /** The process ID of the process owning this reader txn. */
+ volatile MDB_PID_T mrb_pid;
+ /** The thread ID of the thread owning this txn. */
+ volatile MDB_THR_T mrb_tid;
+} MDB_rxbody;
+
+ /** The actual reader record, with cacheline padding. */
+typedef struct MDB_reader {
+ union {
+ MDB_rxbody mrx;
+ /** shorthand for mrb_txnid */
+#define mr_txnid mru.mrx.mrb_txnid
+#define mr_pid mru.mrx.mrb_pid
+#define mr_tid mru.mrx.mrb_tid
+ /** cache line alignment */
+ char pad[(sizeof(MDB_rxbody)+CACHELINE-1) & ~(CACHELINE-1)];
+ } mru;
+} MDB_reader;
+
+ /** The header for the reader table.
+ * The table resides in a memory-mapped file. (This is a different file
+ * than is used for the main database.)
+ *
+ * For POSIX the actual mutexes reside in the shared memory of this
+ * mapped file. On Windows, mutexes are named objects allocated by the
+ * kernel; we store the mutex names in this mapped file so that other
+ * processes can grab them. This same approach is also used on
+ * MacOSX/Darwin (using named semaphores) since MacOSX doesn't support
+ * process-shared POSIX mutexes. For these cases where a named object
+ * is used, the object name is derived from a 64 bit FNV hash of the
+ * environment pathname. As such, naming collisions are extremely
+ * unlikely. If a collision occurs, the results are unpredictable.
+ */
+typedef struct MDB_txbody {
+ /** Stamp identifying this as an LMDB file. It must be set
+ * to #MDB_MAGIC. */
+ uint32_t mtb_magic;
+ /** Format of this lock file. Must be set to #MDB_LOCK_FORMAT. */
+ uint32_t mtb_format;
+#if defined(_WIN32) || defined(MDB_USE_POSIX_SEM)
+ char mtb_rmname[MNAME_LEN];
+#else
+ /** Mutex protecting access to this table.
+ * This is the reader table lock used with LOCK_MUTEX().
+ */
+ mdb_mutex_t mtb_rmutex;
+#endif
+ /** The ID of the last transaction committed to the database.
+ * This is recorded here only for convenience; the value can always
+ * be determined by reading the main database meta pages.
+ */
+ volatile txnid_t mtb_txnid;
+ /** The number of slots that have been used in the reader table.
+ * This always records the maximum count, it is not decremented
+ * when readers release their slots.
+ */
+ volatile unsigned mtb_numreaders;
+} MDB_txbody;
+
+ /** The actual reader table definition. */
+typedef struct MDB_txninfo {
+ union {
+ MDB_txbody mtb;
+#define mti_magic mt1.mtb.mtb_magic
+#define mti_format mt1.mtb.mtb_format
+#define mti_rmutex mt1.mtb.mtb_rmutex
+#define mti_rmname mt1.mtb.mtb_rmname
+#define mti_txnid mt1.mtb.mtb_txnid
+#define mti_numreaders mt1.mtb.mtb_numreaders
+ char pad[(sizeof(MDB_txbody)+CACHELINE-1) & ~(CACHELINE-1)];
+ } mt1;
+ union {
+#if defined(_WIN32) || defined(MDB_USE_POSIX_SEM)
+ char mt2_wmname[MNAME_LEN];
+#define mti_wmname mt2.mt2_wmname
+#else
+ mdb_mutex_t mt2_wmutex;
+#define mti_wmutex mt2.mt2_wmutex
+#endif
+ char pad[(MNAME_LEN+CACHELINE-1) & ~(CACHELINE-1)];
+ } mt2;
+ MDB_reader mti_readers[1];
+} MDB_txninfo;
+
+ /** Lockfile format signature: version, features and field layout */
+#define MDB_LOCK_FORMAT \
+ ((uint32_t) \
+ ((MDB_LOCK_VERSION) \
+ /* Flags which describe functionality */ \
+ + (((MDB_PIDLOCK) != 0) << 16)))
+/** @} */
+
+/** Common header for all page types. The page type depends on #mp_flags.
+ *
+ * #P_BRANCH and #P_LEAF pages have unsorted '#MDB_node's at the end, with
+ * sorted #mp_ptrs[] entries referring to them. Exception: #P_LEAF2 pages
+ * omit mp_ptrs and pack sorted #MDB_DUPFIXED values after the page header.
+ *
+ * #P_OVERFLOW records occupy one or more contiguous pages where only the
+ * first has a page header. They hold the real data of #F_BIGDATA nodes.
+ *
+ * #P_SUBP sub-pages are small leaf "pages" with duplicate data.
+ * A node with flag #F_DUPDATA but not #F_SUBDATA contains a sub-page.
+ * (Duplicate data can also go in sub-databases, which use normal pages.)
+ *
+ * #P_META pages contain #MDB_meta, the start point of an LMDB snapshot.
+ *
+ * Each non-metapage up to #MDB_meta.%mm_last_pg is reachable exactly once
+ * in the snapshot: Either used by a database or listed in a freeDB record.
+ */
+typedef struct MDB_page {
+#define mp_pgno mp_p.p_pgno
+#define mp_next mp_p.p_next
+ union {
+ pgno_t p_pgno; /**< page number */
+ struct MDB_page *p_next; /**< for in-memory list of freed pages */
+ } mp_p;
+ uint16_t mp_pad; /**< key size if this is a LEAF2 page */
+/** @defgroup mdb_page Page Flags
+ * @ingroup internal
+ * Flags for the page headers.
+ * @{
+ */
+#define P_BRANCH 0x01 /**< branch page */
+#define P_LEAF 0x02 /**< leaf page */
+#define P_OVERFLOW 0x04 /**< overflow page */
+#define P_META 0x08 /**< meta page */
+#define P_DIRTY 0x10 /**< dirty page, also set for #P_SUBP pages */
+#define P_LEAF2 0x20 /**< for #MDB_DUPFIXED records */
+#define P_SUBP 0x40 /**< for #MDB_DUPSORT sub-pages */
+#define P_LOOSE 0x4000 /**< page was dirtied then freed, can be reused */
+#define P_KEEP 0x8000 /**< leave this page alone during spill */
+/** @} */
+ uint16_t mp_flags; /**< @ref mdb_page */
+#define mp_lower mp_pb.pb.pb_lower
+#define mp_upper mp_pb.pb.pb_upper
+#define mp_pages mp_pb.pb_pages
+ union {
+ struct {
+ indx_t pb_lower; /**< lower bound of free space */
+ indx_t pb_upper; /**< upper bound of free space */
+ } pb;
+ uint32_t pb_pages; /**< number of overflow pages */
+ } mp_pb;
+ indx_t mp_ptrs[1]; /**< dynamic size */
+} MDB_page;
+
+ /** Size of the page header, excluding dynamic data at the end */
+#define PAGEHDRSZ ((unsigned) offsetof(MDB_page, mp_ptrs))
+
+ /** Address of first usable data byte in a page, after the header */
+#define METADATA(p) ((void *)((char *)(p) + PAGEHDRSZ))
+
+ /** ITS#7713, change PAGEBASE to handle 65536 byte pages */
+#define PAGEBASE ((MDB_DEVEL) ? PAGEHDRSZ : 0)
+
+ /** Number of nodes on a page */
+#define NUMKEYS(p) (((p)->mp_lower - (PAGEHDRSZ-PAGEBASE)) >> 1)
+
+ /** The amount of space remaining in the page */
+#define SIZELEFT(p) (indx_t)((p)->mp_upper - (p)->mp_lower)
+
+ /** The percentage of space used in the page, in tenths of a percent. */
+#define PAGEFILL(env, p) (1000L * ((env)->me_psize - PAGEHDRSZ - SIZELEFT(p)) / \
+ ((env)->me_psize - PAGEHDRSZ))
+ /** The minimum page fill factor, in tenths of a percent.
+ * Pages emptier than this are candidates for merging.
+ */
+#define FILL_THRESHOLD 250
+
+ /** Test if a page is a leaf page */
+#define IS_LEAF(p) F_ISSET((p)->mp_flags, P_LEAF)
+ /** Test if a page is a LEAF2 page */
+#define IS_LEAF2(p) F_ISSET((p)->mp_flags, P_LEAF2)
+ /** Test if a page is a branch page */
+#define IS_BRANCH(p) F_ISSET((p)->mp_flags, P_BRANCH)
+ /** Test if a page is an overflow page */
+#define IS_OVERFLOW(p) F_ISSET((p)->mp_flags, P_OVERFLOW)
+ /** Test if a page is a sub page */
+#define IS_SUBP(p) F_ISSET((p)->mp_flags, P_SUBP)
+
+ /** The number of overflow pages needed to store the given size. */
+#define OVPAGES(size, psize) ((PAGEHDRSZ-1 + (size)) / (psize) + 1)
+
+ /** Link in #MDB_txn.%mt_loose_pgs list.
+ * Kept outside the page header, which is needed when reusing the page.
+ */
+#define NEXT_LOOSE_PAGE(p) (*(MDB_page **)((p) + 2))
+
+ /** Header for a single key/data pair within a page.
+ * Used in pages of type #P_BRANCH and #P_LEAF without #P_LEAF2.
+ * We guarantee 2-byte alignment for 'MDB_node's.
+ *
+ * #mn_lo and #mn_hi are used for data size on leaf nodes, and for child
+ * pgno on branch nodes. On 64 bit platforms, #mn_flags is also used
+ * for pgno. (Branch nodes have no flags). Lo and hi are in host byte
+ * order in case some accesses can be optimized to 32-bit word access.
+ *
+ * Leaf node flags describe node contents. #F_BIGDATA says the node's
+ * data part is the page number of an overflow page with actual data.
+ * #F_DUPDATA and #F_SUBDATA can be combined giving duplicate data in
+ * a sub-page/sub-database, and named databases (just #F_SUBDATA).
+ */
+typedef struct MDB_node {
+ /** part of data size or pgno
+ * @{ */
+#if BYTE_ORDER == LITTLE_ENDIAN
+ unsigned short mn_lo, mn_hi;
+#else
+ unsigned short mn_hi, mn_lo;
+#endif
+ /** @} */
+/** @defgroup mdb_node Node Flags
+ * @ingroup internal
+ * Flags for node headers.
+ * @{
+ */
+#define F_BIGDATA 0x01 /**< data put on overflow page */
+#define F_SUBDATA 0x02 /**< data is a sub-database */
+#define F_DUPDATA 0x04 /**< data has duplicates */
+
+/** valid flags for #mdb_node_add() */
+#define NODE_ADD_FLAGS (F_DUPDATA|F_SUBDATA|MDB_RESERVE|MDB_APPEND)
+
+/** @} */
+ unsigned short mn_flags; /**< @ref mdb_node */
+ unsigned short mn_ksize; /**< key size */
+ char mn_data[1]; /**< key and data are appended here */
+} MDB_node;
+
+ /** Size of the node header, excluding dynamic data at the end */
+#define NODESIZE offsetof(MDB_node, mn_data)
+
+ /** Bit position of top word in page number, for shifting mn_flags */
+#define PGNO_TOPWORD ((pgno_t)-1 > 0xffffffffu ? 32 : 0)
+
+ /** Size of a node in a branch page with a given key.
+ * This is just the node header plus the key, there is no data.
+ */
+#define INDXSIZE(k) (NODESIZE + ((k) == NULL ? 0 : (k)->mv_size))
+
+ /** Size of a node in a leaf page with a given key and data.
+ * This is node header plus key plus data size.
+ */
+#define LEAFSIZE(k, d) (NODESIZE + (k)->mv_size + (d)->mv_size)
+
+ /** Address of node \b i in page \b p */
+#define NODEPTR(p, i) ((MDB_node *)((char *)(p) + (p)->mp_ptrs[i] + PAGEBASE))
+
+ /** Address of the key for the node */
+#define NODEKEY(node) (void *)((node)->mn_data)
+
+ /** Address of the data for a node */
+#define NODEDATA(node) (void *)((char *)(node)->mn_data + (node)->mn_ksize)
+
+ /** Get the page number pointed to by a branch node */
+#define NODEPGNO(node) \
+ ((node)->mn_lo | ((pgno_t) (node)->mn_hi << 16) | \
+ (PGNO_TOPWORD ? ((pgno_t) (node)->mn_flags << PGNO_TOPWORD) : 0))
+ /** Set the page number in a branch node */
+#define SETPGNO(node,pgno) do { \
+ (node)->mn_lo = (pgno) & 0xffff; (node)->mn_hi = (pgno) >> 16; \
+ if (PGNO_TOPWORD) (node)->mn_flags = (pgno) >> PGNO_TOPWORD; } while(0)
+
+ /** Get the size of the data in a leaf node */
+#define NODEDSZ(node) ((node)->mn_lo | ((unsigned)(node)->mn_hi << 16))
+ /** Set the size of the data for a leaf node */
+#define SETDSZ(node,size) do { \
+ (node)->mn_lo = (size) & 0xffff; (node)->mn_hi = (size) >> 16;} while(0)
+ /** The size of a key in a node */
+#define NODEKSZ(node) ((node)->mn_ksize)
+
+ /** Copy a page number from src to dst */
+#ifdef MISALIGNED_OK
+#define COPY_PGNO(dst,src) dst = src
+#else
+#if SIZE_MAX > 4294967295UL
+#define COPY_PGNO(dst,src) do { \
+ unsigned short *s, *d; \
+ s = (unsigned short *)&(src); \
+ d = (unsigned short *)&(dst); \
+ *d++ = *s++; \
+ *d++ = *s++; \
+ *d++ = *s++; \
+ *d = *s; \
+} while (0)
+#else
+#define COPY_PGNO(dst,src) do { \
+ unsigned short *s, *d; \
+ s = (unsigned short *)&(src); \
+ d = (unsigned short *)&(dst); \
+ *d++ = *s++; \
+ *d = *s; \
+} while (0)
+#endif
+#endif
+ /** The address of a key in a LEAF2 page.
+ * LEAF2 pages are used for #MDB_DUPFIXED sorted-duplicate sub-DBs.
+ * There are no node headers, keys are stored contiguously.
+ */
+#define LEAF2KEY(p, i, ks) ((char *)(p) + PAGEHDRSZ + ((i)*(ks)))
+
+ /** Set the \b node's key into \b keyptr, if requested. */
+#define MDB_GET_KEY(node, keyptr) { if ((keyptr) != NULL) { \
+ (keyptr)->mv_size = NODEKSZ(node); (keyptr)->mv_data = NODEKEY(node); } }
+
+ /** Set the \b node's key into \b key. */
+#define MDB_GET_KEY2(node, key) { key.mv_size = NODEKSZ(node); key.mv_data = NODEKEY(node); }
+
+ /** Information about a single database in the environment. */
+typedef struct MDB_db {
+ uint32_t md_pad; /**< also ksize for LEAF2 pages */
+ uint16_t md_flags; /**< @ref mdb_dbi_open */
+ uint16_t md_depth; /**< depth of this tree */
+ pgno_t md_branch_pages; /**< number of internal pages */
+ pgno_t md_leaf_pages; /**< number of leaf pages */
+ pgno_t md_overflow_pages; /**< number of overflow pages */
+ size_t md_entries; /**< number of data items */
+ pgno_t md_root; /**< the root page of this tree */
+} MDB_db;
+
+#define MDB_VALID 0x8000 /**< DB handle is valid, for me_dbflags */
+#define PERSISTENT_FLAGS (0xffff & ~(MDB_VALID))
+ /** #mdb_dbi_open() flags */
+#define VALID_FLAGS (MDB_REVERSEKEY|MDB_DUPSORT|MDB_INTEGERKEY|MDB_DUPFIXED|\
+ MDB_INTEGERDUP|MDB_REVERSEDUP|MDB_CREATE)
+
+ /** Handle for the DB used to track free pages. */
+#define FREE_DBI 0
+ /** Handle for the default DB. */
+#define MAIN_DBI 1
+ /** Number of DBs in metapage (free and main) - also hardcoded elsewhere */
+#define CORE_DBS 2
+
+ /** Number of meta pages - also hardcoded elsewhere */
+#define NUM_METAS 2
+
+ /** Meta page content.
+ * A meta page is the start point for accessing a database snapshot.
+ * Pages 0-1 are meta pages. Transaction N writes meta page #(N % 2).
+ */
+typedef struct MDB_meta {
+ /** Stamp identifying this as an LMDB file. It must be set
+ * to #MDB_MAGIC. */
+ uint32_t mm_magic;
+ /** Version number of this file. Must be set to #MDB_DATA_VERSION. */
+ uint32_t mm_version;
+ void *mm_address; /**< address for fixed mapping */
+ size_t mm_mapsize; /**< size of mmap region */
+ MDB_db mm_dbs[CORE_DBS]; /**< first is free space, 2nd is main db */
+ /** The size of pages used in this DB */
+#define mm_psize mm_dbs[FREE_DBI].md_pad
+ /** Any persistent environment flags. @ref mdb_env */
+#define mm_flags mm_dbs[FREE_DBI].md_flags
+ /** Last used page in the datafile.
+ * Actually the file may be shorter if the freeDB lists the final pages.
+ */
+ pgno_t mm_last_pg;
+ volatile txnid_t mm_txnid; /**< txnid that committed this page */
+} MDB_meta;
+
+ /** Buffer for a stack-allocated meta page.
+ * The members define size and alignment, and silence type
+ * aliasing warnings. They are not used directly; that could
+ * mean incorrectly using several union members in parallel.
+ */
+typedef union MDB_metabuf {
+ MDB_page mb_page;
+ struct {
+ char mm_pad[PAGEHDRSZ];
+ MDB_meta mm_meta;
+ } mb_metabuf;
+} MDB_metabuf;
+
+ /** Auxiliary DB info.
+ * The information here is mostly static/read-only. There is
+ * only a single copy of this record in the environment.
+ */
+typedef struct MDB_dbx {
+ MDB_val md_name; /**< name of the database */
+ MDB_cmp_func *md_cmp; /**< function for comparing keys */
+ MDB_cmp_func *md_dcmp; /**< function for comparing data items */
+ MDB_rel_func *md_rel; /**< user relocate function */
+ void *md_relctx; /**< user-provided context for md_rel */
+} MDB_dbx;
+
+ /** A database transaction.
+ * Every operation requires a transaction handle.
+ */
+struct MDB_txn {
+ MDB_txn *mt_parent; /**< parent of a nested txn */
+ /** Nested txn under this txn, set together with flag #MDB_TXN_HAS_CHILD */
+ MDB_txn *mt_child;
+ pgno_t mt_next_pgno; /**< next unallocated page */
+ /** The ID of this transaction. IDs are integers incrementing from 1.
+ * Only committed write transactions increment the ID. If a transaction
+ * aborts, the ID may be re-used by the next writer.
+ */
+ txnid_t mt_txnid;
+ MDB_env *mt_env; /**< the DB environment */
+ /** The list of pages that became unused during this transaction.
+ */
+ MDB_IDL mt_free_pgs;
+ /** The list of loose pages that became unused and may be reused
+ * in this transaction, linked through #NEXT_LOOSE_PAGE(page).
+ */
+ MDB_page *mt_loose_pgs;
+ /** Number of loose pages (#mt_loose_pgs) */
+ int mt_loose_count;
+ /** The sorted list of dirty pages we temporarily wrote to disk
+ * because the dirty list was full. page numbers in here are
+ * shifted left by 1, deleted slots have the LSB set.
+ */
+ MDB_IDL mt_spill_pgs;
+ union {
+ /** For write txns: Modified pages. Sorted when not MDB_WRITEMAP. */
+ MDB_ID2L dirty_list;
+ /** For read txns: This thread/txn's reader table slot, or NULL. */
+ MDB_reader *reader;
+ } mt_u;
+ /** Array of records for each DB known in the environment. */
+ MDB_dbx *mt_dbxs;
+ /** Array of MDB_db records for each known DB */
+ MDB_db *mt_dbs;
+ /** Array of sequence numbers for each DB handle */
+ unsigned int *mt_dbiseqs;
+/** @defgroup mt_dbflag Transaction DB Flags
+ * @ingroup internal
+ * @{
+ */
+#define DB_DIRTY 0x01 /**< DB was written in this txn */
+#define DB_STALE 0x02 /**< Named-DB record is older than txnID */
+#define DB_NEW 0x04 /**< Named-DB handle opened in this txn */
+#define DB_VALID 0x08 /**< DB handle is valid, see also #MDB_VALID */
+#define DB_USRVALID 0x10 /**< As #DB_VALID, but not set for #FREE_DBI */
+#define DB_DUPDATA 0x20 /**< DB is #MDB_DUPSORT data */
+/** @} */
+ /** In write txns, array of cursors for each DB */
+ MDB_cursor **mt_cursors;
+ /** Array of flags for each DB */
+ unsigned char *mt_dbflags;
+ /** Number of DB records in use, or 0 when the txn is finished.
+ * This number only ever increments until the txn finishes; we
+ * don't decrement it when individual DB handles are closed.
+ */
+ MDB_dbi mt_numdbs;
+
+/** @defgroup mdb_txn Transaction Flags
+ * @ingroup internal
+ * @{
+ */
+ /** #mdb_txn_begin() flags */
+#define MDB_TXN_BEGIN_FLAGS MDB_RDONLY
+#define MDB_TXN_RDONLY MDB_RDONLY /**< read-only transaction */
+ /* internal txn flags */
+#define MDB_TXN_WRITEMAP MDB_WRITEMAP /**< copy of #MDB_env flag in writers */
+#define MDB_TXN_FINISHED 0x01 /**< txn is finished or never began */
+#define MDB_TXN_ERROR 0x02 /**< txn is unusable after an error */
+#define MDB_TXN_DIRTY 0x04 /**< must write, even if dirty list is empty */
+#define MDB_TXN_SPILLS 0x08 /**< txn or a parent has spilled pages */
+#define MDB_TXN_HAS_CHILD 0x10 /**< txn has an #MDB_txn.%mt_child */
+ /** most operations on the txn are currently illegal */
+#define MDB_TXN_BLOCKED (MDB_TXN_FINISHED|MDB_TXN_ERROR|MDB_TXN_HAS_CHILD)
+/** @} */
+ unsigned int mt_flags; /**< @ref mdb_txn */
+ /** #dirty_list room: Array size - \#dirty pages visible to this txn.
+ * Includes ancestor txns' dirty pages not hidden by other txns'
+ * dirty/spilled pages. Thus commit(nested txn) has room to merge
+ * dirty_list into mt_parent after freeing hidden mt_parent pages.
+ */
+ unsigned int mt_dirty_room;
+};
+
+/** Enough space for 2^32 nodes with minimum of 2 keys per node. I.e., plenty.
+ * At 4 keys per node, enough for 2^64 nodes, so there's probably no need to
+ * raise this on a 64 bit machine.
+ */
+#define CURSOR_STACK 32
+
+struct MDB_xcursor;
+
+ /** Cursors are used for all DB operations.
+ * A cursor holds a path of (page pointer, key index) from the DB
+ * root to a position in the DB, plus other state. #MDB_DUPSORT
+ * cursors include an xcursor to the current data item. Write txns
+ * track their cursors and keep them up to date when data moves.
+ * Exception: An xcursor's pointer to a #P_SUBP page can be stale.
+ * (A node with #F_DUPDATA but no #F_SUBDATA contains a subpage).
+ */
+struct MDB_cursor {
+ /** Next cursor on this DB in this txn */
+ MDB_cursor *mc_next;
+ /** Backup of the original cursor if this cursor is a shadow */
+ MDB_cursor *mc_backup;
+ /** Context used for databases with #MDB_DUPSORT, otherwise NULL */
+ struct MDB_xcursor *mc_xcursor;
+ /** The transaction that owns this cursor */
+ MDB_txn *mc_txn;
+ /** The database handle this cursor operates on */
+ MDB_dbi mc_dbi;
+ /** The database record for this cursor */
+ MDB_db *mc_db;
+ /** The database auxiliary record for this cursor */
+ MDB_dbx *mc_dbx;
+ /** The @ref mt_dbflag for this database */
+ unsigned char *mc_dbflag;
+ unsigned short mc_snum; /**< number of pushed pages */
+ unsigned short mc_top; /**< index of top page, normally mc_snum-1 */
+/** @defgroup mdb_cursor Cursor Flags
+ * @ingroup internal
+ * Cursor state flags.
+ * @{
+ */
+#define C_INITIALIZED 0x01 /**< cursor has been initialized and is valid */
+#define C_EOF 0x02 /**< No more data */
+#define C_SUB 0x04 /**< Cursor is a sub-cursor */
+#define C_DEL 0x08 /**< last op was a cursor_del */
+#define C_UNTRACK 0x40 /**< Un-track cursor when closing */
+/** @} */
+ unsigned int mc_flags; /**< @ref mdb_cursor */
+ MDB_page *mc_pg[CURSOR_STACK]; /**< stack of pushed pages */
+ indx_t mc_ki[CURSOR_STACK]; /**< stack of page indices */
+};
+
+ /** Context for sorted-dup records.
+ * We could have gone to a fully recursive design, with arbitrarily
+ * deep nesting of sub-databases. But for now we only handle these
+ * levels - main DB, optional sub-DB, sorted-duplicate DB.
+ */
+typedef struct MDB_xcursor {
+ /** A sub-cursor for traversing the Dup DB */
+ MDB_cursor mx_cursor;
+ /** The database record for this Dup DB */
+ MDB_db mx_db;
+ /** The auxiliary DB record for this Dup DB */
+ MDB_dbx mx_dbx;
+ /** The @ref mt_dbflag for this Dup DB */
+ unsigned char mx_dbflag;
+} MDB_xcursor;
+
+ /** Check if there is an inited xcursor */
+#define XCURSOR_INITED(mc) \
+ ((mc)->mc_xcursor && ((mc)->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED))
+
+ /** Update the xcursor's sub-page pointer, if any, in \b mc. Needed
+ * when the node which contains the sub-page may have moved. Called
+ * with leaf page \b mp = mc->mc_pg[\b top].
+ */
+#define XCURSOR_REFRESH(mc, top, mp) do { \
+ MDB_page *xr_pg = (mp); \
+ MDB_node *xr_node; \
+ if (!XCURSOR_INITED(mc) || (mc)->mc_ki[top] >= NUMKEYS(xr_pg)) break; \
+ xr_node = NODEPTR(xr_pg, (mc)->mc_ki[top]); \
+ if ((xr_node->mn_flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA) \
+ (mc)->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(xr_node); \
+} while (0)
+
+ /** State of FreeDB old pages, stored in the MDB_env */
+typedef struct MDB_pgstate {
+ pgno_t *mf_pghead; /**< Reclaimed freeDB pages, or NULL before use */
+ txnid_t mf_pglast; /**< ID of last used record, or 0 if !mf_pghead */
+} MDB_pgstate;
+
+ /** The database environment. */
+struct MDB_env {
+ HANDLE me_fd; /**< The main data file */
+ HANDLE me_lfd; /**< The lock file */
+ HANDLE me_mfd; /**< For writing and syncing the meta pages */
+ /** Failed to update the meta page. Probably an I/O error. */
+#define MDB_FATAL_ERROR 0x80000000U
+ /** Some fields are initialized. */
+#define MDB_ENV_ACTIVE 0x20000000U
+ /** me_txkey is set */
+#define MDB_ENV_TXKEY 0x10000000U
+ /** fdatasync is unreliable */
+#define MDB_FSYNCONLY 0x08000000U
+ uint32_t me_flags; /**< @ref mdb_env */
+ unsigned int me_psize; /**< DB page size, inited from me_os_psize */
+ unsigned int me_os_psize; /**< OS page size, from #GET_PAGESIZE */
+ unsigned int me_maxreaders; /**< size of the reader table */
+ /** Max #MDB_txninfo.%mti_numreaders of interest to #mdb_env_close() */
+ volatile int me_close_readers;
+ MDB_dbi me_numdbs; /**< number of DBs opened */
+ MDB_dbi me_maxdbs; /**< size of the DB table */
+ MDB_PID_T me_pid; /**< process ID of this env */
+ char *me_path; /**< path to the DB files */
+ char *me_map; /**< the memory map of the data file */
+ MDB_txninfo *me_txns; /**< the memory map of the lock file or NULL */
+ MDB_meta *me_metas[NUM_METAS]; /**< pointers to the two meta pages */
+ void *me_pbuf; /**< scratch area for DUPSORT put() */
+ MDB_txn *me_txn; /**< current write transaction */
+ MDB_txn *me_txn0; /**< prealloc'd write transaction */
+ size_t me_mapsize; /**< size of the data memory map */
+ off_t me_size; /**< current file size */
+ pgno_t me_maxpg; /**< me_mapsize / me_psize */
+ MDB_dbx *me_dbxs; /**< array of static DB info */
+ uint16_t *me_dbflags; /**< array of flags from MDB_db.md_flags */
+ unsigned int *me_dbiseqs; /**< array of dbi sequence numbers */
+ pthread_key_t me_txkey; /**< thread-key for readers */
+ txnid_t me_pgoldest; /**< ID of oldest reader last time we looked */
+ MDB_pgstate me_pgstate; /**< state of old pages from freeDB */
+# define me_pglast me_pgstate.mf_pglast
+# define me_pghead me_pgstate.mf_pghead
+ MDB_page *me_dpages; /**< list of malloc'd blocks for re-use */
+ /** IDL of pages that became unused in a write txn */
+ MDB_IDL me_free_pgs;
+ /** ID2L of pages written during a write txn. Length MDB_IDL_UM_SIZE. */
+ MDB_ID2L me_dirty_list;
+ /** Max number of freelist items that can fit in a single overflow page */
+ int me_maxfree_1pg;
+ /** Max size of a node on a page */
+ unsigned int me_nodemax;
+#if !(MDB_MAXKEYSIZE)
+ unsigned int me_maxkey; /**< max size of a key */
+#endif
+ int me_live_reader; /**< have liveness lock in reader table */
+#ifdef _WIN32
+ int me_pidquery; /**< Used in OpenProcess */
+#endif
+#ifdef MDB_USE_POSIX_MUTEX /* Posix mutexes reside in shared mem */
+# define me_rmutex me_txns->mti_rmutex /**< Shared reader lock */
+# define me_wmutex me_txns->mti_wmutex /**< Shared writer lock */
+#else
+ mdb_mutex_t me_rmutex;
+ mdb_mutex_t me_wmutex;
+#endif
+ void *me_userctx; /**< User-settable context */
+ MDB_assert_func *me_assert_func; /**< Callback for assertion failures */
+};
+
+ /** Nested transaction */
+typedef struct MDB_ntxn {
+ MDB_txn mnt_txn; /**< the transaction */
+ MDB_pgstate mnt_pgstate; /**< parent transaction's saved freestate */
+} MDB_ntxn;
+
+ /** max number of pages to commit in one writev() call */
+#define MDB_COMMIT_PAGES 64
+#if defined(IOV_MAX) && IOV_MAX < MDB_COMMIT_PAGES
+#undef MDB_COMMIT_PAGES
+#define MDB_COMMIT_PAGES IOV_MAX
+#endif
+
+ /** max bytes to write in one call */
+#define MAX_WRITE (0x40000000U >> (sizeof(ssize_t) == 4))
+
+ /** Check \b txn and \b dbi arguments to a function */
+#define TXN_DBI_EXIST(txn, dbi, validity) \
+ ((txn) && (dbi)<(txn)->mt_numdbs && ((txn)->mt_dbflags[dbi] & (validity)))
+
+ /** Check for misused \b dbi handles */
+#define TXN_DBI_CHANGED(txn, dbi) \
+ ((txn)->mt_dbiseqs[dbi] != (txn)->mt_env->me_dbiseqs[dbi])
+
+static int mdb_page_alloc(MDB_cursor *mc, int num, MDB_page **mp);
+static int mdb_page_new(MDB_cursor *mc, uint32_t flags, int num, MDB_page **mp);
+static int mdb_page_touch(MDB_cursor *mc);
+
+#define MDB_END_NAMES {"committed", "empty-commit", "abort", "reset", \
+ "reset-tmp", "fail-begin", "fail-beginchild"}
+enum {
+ /* mdb_txn_end operation number, for logging */
+ MDB_END_COMMITTED, MDB_END_EMPTY_COMMIT, MDB_END_ABORT, MDB_END_RESET,
+ MDB_END_RESET_TMP, MDB_END_FAIL_BEGIN, MDB_END_FAIL_BEGINCHILD
+};
+#define MDB_END_OPMASK 0x0F /**< mask for #mdb_txn_end() operation number */
+#define MDB_END_UPDATE 0x10 /**< update env state (DBIs) */
+#define MDB_END_FREE 0x20 /**< free txn unless it is #MDB_env.%me_txn0 */
+#define MDB_END_SLOT MDB_NOTLS /**< release any reader slot if #MDB_NOTLS */
+static void mdb_txn_end(MDB_txn *txn, unsigned mode);
+
+static int mdb_page_get(MDB_cursor *mc, pgno_t pgno, MDB_page **mp, int *lvl);
+static int mdb_page_search_root(MDB_cursor *mc,
+ MDB_val *key, int modify);
+#define MDB_PS_MODIFY 1
+#define MDB_PS_ROOTONLY 2
+#define MDB_PS_FIRST 4
+#define MDB_PS_LAST 8
+static int mdb_page_search(MDB_cursor *mc,
+ MDB_val *key, int flags);
+static int mdb_page_merge(MDB_cursor *csrc, MDB_cursor *cdst);
+
+#define MDB_SPLIT_REPLACE MDB_APPENDDUP /**< newkey is not new */
+static int mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata,
+ pgno_t newpgno, unsigned int nflags);
+
+static int mdb_env_read_header(MDB_env *env, MDB_meta *meta);
+static MDB_meta *mdb_env_pick_meta(const MDB_env *env);
+static int mdb_env_write_meta(MDB_txn *txn);
+#if defined(MDB_USE_POSIX_MUTEX) && !defined(MDB_ROBUST_SUPPORTED) /* Drop unused excl arg */
+# define mdb_env_close0(env, excl) mdb_env_close1(env)
+#endif
+static void mdb_env_close0(MDB_env *env, int excl);
+
+static MDB_node *mdb_node_search(MDB_cursor *mc, MDB_val *key, int *exactp);
+static int mdb_node_add(MDB_cursor *mc, indx_t indx,
+ MDB_val *key, MDB_val *data, pgno_t pgno, unsigned int flags);
+static void mdb_node_del(MDB_cursor *mc, int ksize);
+static void mdb_node_shrink(MDB_page *mp, indx_t indx);
+static int mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst, int fromleft);
+static int mdb_node_read(MDB_cursor *mc, MDB_node *leaf, MDB_val *data);
+static size_t mdb_leaf_size(MDB_env *env, MDB_val *key, MDB_val *data);
+static size_t mdb_branch_size(MDB_env *env, MDB_val *key);
+
+static int mdb_rebalance(MDB_cursor *mc);
+static int mdb_update_key(MDB_cursor *mc, MDB_val *key);
+
+static void mdb_cursor_pop(MDB_cursor *mc);
+static int mdb_cursor_push(MDB_cursor *mc, MDB_page *mp);
+
+static int mdb_cursor_del0(MDB_cursor *mc);
+static int mdb_del0(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data, unsigned flags);
+static int mdb_cursor_sibling(MDB_cursor *mc, int move_right);
+static int mdb_cursor_next(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op);
+static int mdb_cursor_prev(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op);
+static int mdb_cursor_set(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op,
+ int *exactp);
+static int mdb_cursor_first(MDB_cursor *mc, MDB_val *key, MDB_val *data);
+static int mdb_cursor_last(MDB_cursor *mc, MDB_val *key, MDB_val *data);
+
+static void mdb_cursor_init(MDB_cursor *mc, MDB_txn *txn, MDB_dbi dbi, MDB_xcursor *mx);
+static void mdb_xcursor_init0(MDB_cursor *mc);
+static void mdb_xcursor_init1(MDB_cursor *mc, MDB_node *node);
+static void mdb_xcursor_init2(MDB_cursor *mc, MDB_xcursor *src_mx, int force);
+
+static int mdb_drop0(MDB_cursor *mc, int subs);
+static void mdb_default_cmp(MDB_txn *txn, MDB_dbi dbi);
+static int mdb_reader_check0(MDB_env *env, int rlocked, int *dead);
+
+/** @cond */
+static MDB_cmp_func mdb_cmp_memn, mdb_cmp_memnr, mdb_cmp_int, mdb_cmp_cint, mdb_cmp_long;
+/** @endcond */
+
+/** Compare two items pointing at size_t's of unknown alignment. */
+#ifdef MISALIGNED_OK
+# define mdb_cmp_clong mdb_cmp_long
+#else
+# define mdb_cmp_clong mdb_cmp_cint
+#endif
+
+#ifdef _WIN32
+static SECURITY_DESCRIPTOR mdb_null_sd;
+static SECURITY_ATTRIBUTES mdb_all_sa;
+static int mdb_sec_inited;
+
+struct MDB_name;
+static int utf8_to_utf16(const char *src, struct MDB_name *dst, int xtra);
+#endif
+
+/** Return the library version info. */
+char * ESECT
+mdb_version(int *major, int *minor, int *patch)
+{
+ if (major) *major = MDB_VERSION_MAJOR;
+ if (minor) *minor = MDB_VERSION_MINOR;
+ if (patch) *patch = MDB_VERSION_PATCH;
+ return MDB_VERSION_STRING;
+}
+
+/** Table of descriptions for LMDB @ref errors */
+static char *const mdb_errstr[] = {
+ "MDB_KEYEXIST: Key/data pair already exists",
+ "MDB_NOTFOUND: No matching key/data pair found",
+ "MDB_PAGE_NOTFOUND: Requested page not found",
+ "MDB_CORRUPTED: Located page was wrong type",
+ "MDB_PANIC: Update of meta page failed or environment had fatal error",
+ "MDB_VERSION_MISMATCH: Database environment version mismatch",
+ "MDB_INVALID: File is not an LMDB file",
+ "MDB_MAP_FULL: Environment mapsize limit reached",
+ "MDB_DBS_FULL: Environment maxdbs limit reached",
+ "MDB_READERS_FULL: Environment maxreaders limit reached",
+ "MDB_TLS_FULL: Thread-local storage keys full - too many environments open",
+ "MDB_TXN_FULL: Transaction has too many dirty pages - transaction too big",
+ "MDB_CURSOR_FULL: Internal error - cursor stack limit reached",
+ "MDB_PAGE_FULL: Internal error - page has no more space",
+ "MDB_MAP_RESIZED: Database contents grew beyond environment mapsize",
+ "MDB_INCOMPATIBLE: Operation and DB incompatible, or DB flags changed",
+ "MDB_BAD_RSLOT: Invalid reuse of reader locktable slot",
+ "MDB_BAD_TXN: Transaction must abort, has a child, or is invalid",
+ "MDB_BAD_VALSIZE: Unsupported size of key/DB name/data, or wrong DUPFIXED size",
+ "MDB_BAD_DBI: The specified DBI handle was closed/changed unexpectedly",
+};
+
+char *
+mdb_strerror(int err)
+{
+#ifdef _WIN32
+ /** HACK: pad 4KB on stack over the buf. Return system msgs in buf.
+ * This works as long as no function between the call to mdb_strerror
+ * and the actual use of the message uses more than 4K of stack.
+ */
+#define MSGSIZE 1024
+#define PADSIZE 4096
+ char buf[MSGSIZE+PADSIZE], *ptr = buf;
+#endif
+ int i;
+ if (!err)
+ return ("Successful return: 0");
+
+ if (err >= MDB_KEYEXIST && err <= MDB_LAST_ERRCODE) {
+ i = err - MDB_KEYEXIST;
+ return mdb_errstr[i];
+ }
+
+#ifdef _WIN32
+ /* These are the C-runtime error codes we use. The comment indicates
+ * their numeric value, and the Win32 error they would correspond to
+ * if the error actually came from a Win32 API. A major mess, we should
+ * have used LMDB-specific error codes for everything.
+ */
+ switch(err) {
+ case ENOENT: /* 2, FILE_NOT_FOUND */
+ case EIO: /* 5, ACCESS_DENIED */
+ case ENOMEM: /* 12, INVALID_ACCESS */
+ case EACCES: /* 13, INVALID_DATA */
+ case EBUSY: /* 16, CURRENT_DIRECTORY */
+ case EINVAL: /* 22, BAD_COMMAND */
+ case ENOSPC: /* 28, OUT_OF_PAPER */
+ return strerror(err);
+ default:
+ ;
+ }
+ buf[0] = 0;
+ FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, err, 0, ptr, MSGSIZE, (va_list *)buf+MSGSIZE);
+ return ptr;
+#else
+ return strerror(err);
+#endif
+}
+
+/** assert(3) variant in cursor context */
+#define mdb_cassert(mc, expr) mdb_assert0((mc)->mc_txn->mt_env, expr, #expr)
+/** assert(3) variant in transaction context */
+#define mdb_tassert(txn, expr) mdb_assert0((txn)->mt_env, expr, #expr)
+/** assert(3) variant in environment context */
+#define mdb_eassert(env, expr) mdb_assert0(env, expr, #expr)
+
+#ifndef NDEBUG
+# define mdb_assert0(env, expr, expr_txt) ((expr) ? (void)0 : \
+ mdb_assert_fail(env, expr_txt, mdb_func_, __FILE__, __LINE__))
+
+static void ESECT
+mdb_assert_fail(MDB_env *env, const char *expr_txt,
+ const char *func, const char *file, int line)
+{
+ char buf[400];
+ sprintf(buf, "%.100s:%d: Assertion '%.200s' failed in %.40s()",
+ file, line, expr_txt, func);
+ if (env->me_assert_func)
+ env->me_assert_func(env, buf);
+ fprintf(stderr, "%s\n", buf);
+ abort();
+}
+#else
+# define mdb_assert0(env, expr, expr_txt) ((void) 0)
+#endif /* NDEBUG */
+
+#if MDB_DEBUG
+/** Return the page number of \b mp which may be sub-page, for debug output */
+static pgno_t
+mdb_dbg_pgno(MDB_page *mp)
+{
+ pgno_t ret;
+ COPY_PGNO(ret, mp->mp_pgno);
+ return ret;
+}
+
+/** Display a key in hexadecimal and return the address of the result.
+ * @param[in] key the key to display
+ * @param[in] buf the buffer to write into. Should always be #DKBUF.
+ * @return The key in hexadecimal form.
+ */
+char *
+mdb_dkey(MDB_val *key, char *buf)
+{
+ char *ptr = buf;
+ unsigned char *c = key->mv_data;
+ unsigned int i;
+
+ if (!key)
+ return "";
+
+ if (key->mv_size > DKBUF_MAXKEYSIZE)
+ return "MDB_MAXKEYSIZE";
+ /* may want to make this a dynamic check: if the key is mostly
+ * printable characters, print it as-is instead of converting to hex.
+ */
+#if 1
+ buf[0] = '\0';
+ for (i=0; i<key->mv_size; i++)
+ ptr += sprintf(ptr, "%02x", *c++);
+#else
+ sprintf(buf, "%.*s", key->mv_size, key->mv_data);
+#endif
+ return buf;
+}
+
+static const char *
+mdb_leafnode_type(MDB_node *n)
+{
+ static char *const tp[2][2] = {{"", ": DB"}, {": sub-page", ": sub-DB"}};
+ return F_ISSET(n->mn_flags, F_BIGDATA) ? ": overflow page" :
+ tp[F_ISSET(n->mn_flags, F_DUPDATA)][F_ISSET(n->mn_flags, F_SUBDATA)];
+}
+
+/** Display all the keys in the page. */
+void
+mdb_page_list(MDB_page *mp)
+{
+ pgno_t pgno = mdb_dbg_pgno(mp);
+ const char *type, *state = (mp->mp_flags & P_DIRTY) ? ", dirty" : "";
+ MDB_node *node;
+ unsigned int i, nkeys, nsize, total = 0;
+ MDB_val key;
+ DKBUF;
+
+ switch (mp->mp_flags & (P_BRANCH|P_LEAF|P_LEAF2|P_META|P_OVERFLOW|P_SUBP)) {
+ case P_BRANCH: type = "Branch page"; break;
+ case P_LEAF: type = "Leaf page"; break;
+ case P_LEAF|P_SUBP: type = "Sub-page"; break;
+ case P_LEAF|P_LEAF2: type = "LEAF2 page"; break;
+ case P_LEAF|P_LEAF2|P_SUBP: type = "LEAF2 sub-page"; break;
+ case P_OVERFLOW:
+ fprintf(stderr, "Overflow page %"Z"u pages %u%s\n",
+ pgno, mp->mp_pages, state);
+ return;
+ case P_META:
+ fprintf(stderr, "Meta-page %"Z"u txnid %"Z"u\n",
+ pgno, ((MDB_meta *)METADATA(mp))->mm_txnid);
+ return;
+ default:
+ fprintf(stderr, "Bad page %"Z"u flags 0x%X\n", pgno, mp->mp_flags);
+ return;
+ }
+
+ nkeys = NUMKEYS(mp);
+ fprintf(stderr, "%s %"Z"u numkeys %d%s\n", type, pgno, nkeys, state);
+
+ for (i=0; i<nkeys; i++) {
+ if (IS_LEAF2(mp)) { /* LEAF2 pages have no mp_ptrs[] or node headers */
+ key.mv_size = nsize = mp->mp_pad;
+ key.mv_data = LEAF2KEY(mp, i, nsize);
+ total += nsize;
+ fprintf(stderr, "key %d: nsize %d, %s\n", i, nsize, DKEY(&key));
+ continue;
+ }
+ node = NODEPTR(mp, i);
+ key.mv_size = node->mn_ksize;
+ key.mv_data = node->mn_data;
+ nsize = NODESIZE + key.mv_size;
+ if (IS_BRANCH(mp)) {
+ fprintf(stderr, "key %d: page %"Z"u, %s\n", i, NODEPGNO(node),
+ DKEY(&key));
+ total += nsize;
+ } else {
+ if (F_ISSET(node->mn_flags, F_BIGDATA))
+ nsize += sizeof(pgno_t);
+ else
+ nsize += NODEDSZ(node);
+ total += nsize;
+ nsize += sizeof(indx_t);
+ fprintf(stderr, "key %d: nsize %d, %s%s\n",
+ i, nsize, DKEY(&key), mdb_leafnode_type(node));
+ }
+ total = EVEN(total);
+ }
+ fprintf(stderr, "Total: header %d + contents %d + unused %d\n",
+ IS_LEAF2(mp) ? PAGEHDRSZ : PAGEBASE + mp->mp_lower, total, SIZELEFT(mp));
+}
+
+void
+mdb_cursor_chk(MDB_cursor *mc)
+{
+ unsigned int i;
+ MDB_node *node;
+ MDB_page *mp;
+
+ if (!mc->mc_snum || !(mc->mc_flags & C_INITIALIZED)) return;
+ for (i=0; i<mc->mc_top; i++) {
+ mp = mc->mc_pg[i];
+ node = NODEPTR(mp, mc->mc_ki[i]);
+ if (NODEPGNO(node) != mc->mc_pg[i+1]->mp_pgno)
+ printf("oops!\n");
+ }
+ if (mc->mc_ki[i] >= NUMKEYS(mc->mc_pg[i]))
+ printf("ack!\n");
+ if (XCURSOR_INITED(mc)) {
+ node = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
+ if (((node->mn_flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA) &&
+ mc->mc_xcursor->mx_cursor.mc_pg[0] != NODEDATA(node)) {
+ printf("blah!\n");
+ }
+ }
+}
+#endif
+
+#if (MDB_DEBUG) > 2
+/** Count all the pages in each DB and in the freelist
+ * and make sure it matches the actual number of pages
+ * being used.
+ * All named DBs must be open for a correct count.
+ */
+static void mdb_audit(MDB_txn *txn)
+{
+ MDB_cursor mc;
+ MDB_val key, data;
+ MDB_ID freecount, count;
+ MDB_dbi i;
+ int rc;
+
+ freecount = 0;
+ mdb_cursor_init(&mc, txn, FREE_DBI, NULL);
+ while ((rc = mdb_cursor_get(&mc, &key, &data, MDB_NEXT)) == 0)
+ freecount += *(MDB_ID *)data.mv_data;
+ mdb_tassert(txn, rc == MDB_NOTFOUND);
+
+ count = 0;
+ for (i = 0; i<txn->mt_numdbs; i++) {
+ MDB_xcursor mx;
+ if (!(txn->mt_dbflags[i] & DB_VALID))
+ continue;
+ mdb_cursor_init(&mc, txn, i, &mx);
+ if (txn->mt_dbs[i].md_root == P_INVALID)
+ continue;
+ count += txn->mt_dbs[i].md_branch_pages +
+ txn->mt_dbs[i].md_leaf_pages +
+ txn->mt_dbs[i].md_overflow_pages;
+ if (txn->mt_dbs[i].md_flags & MDB_DUPSORT) {
+ rc = mdb_page_search(&mc, NULL, MDB_PS_FIRST);
+ for (; rc == MDB_SUCCESS; rc = mdb_cursor_sibling(&mc, 1)) {
+ unsigned j;
+ MDB_page *mp;
+ mp = mc.mc_pg[mc.mc_top];
+ for (j=0; j<NUMKEYS(mp); j++) {
+ MDB_node *leaf = NODEPTR(mp, j);
+ if (leaf->mn_flags & F_SUBDATA) {
+ MDB_db db;
+ memcpy(&db, NODEDATA(leaf), sizeof(db));
+ count += db.md_branch_pages + db.md_leaf_pages +
+ db.md_overflow_pages;
+ }
+ }
+ }
+ mdb_tassert(txn, rc == MDB_NOTFOUND);
+ }
+ }
+ if (freecount + count + NUM_METAS != txn->mt_next_pgno) {
+ fprintf(stderr, "audit: %"Z"u freecount: %"Z"u count: %"Z"u total: %"Z"u next_pgno: %"Z"u\n",
+ txn->mt_txnid, freecount, count+NUM_METAS,
+ freecount+count+NUM_METAS, txn->mt_next_pgno);
+ }
+}
+#endif
+
+int
+mdb_cmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b)
+{
+ return txn->mt_dbxs[dbi].md_cmp(a, b);
+}
+
+int
+mdb_dcmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b)
+{
+ MDB_cmp_func *dcmp = txn->mt_dbxs[dbi].md_dcmp;
+#if UINT_MAX < SIZE_MAX
+ if (dcmp == mdb_cmp_int && a->mv_size == sizeof(size_t))
+ dcmp = mdb_cmp_clong;
+#endif
+ return dcmp(a, b);
+}
+
+/** Allocate memory for a page.
+ * Re-use old malloc'd pages first for singletons, otherwise just malloc.
+ * Set #MDB_TXN_ERROR on failure.
+ */
+static MDB_page *
+mdb_page_malloc(MDB_txn *txn, unsigned num)
+{
+ MDB_env *env = txn->mt_env;
+ MDB_page *ret = env->me_dpages;
+ size_t psize = env->me_psize, sz = psize, off;
+ /* For ! #MDB_NOMEMINIT, psize counts how much to init.
+ * For a single page alloc, we init everything after the page header.
+ * For multi-page, we init the final page; if the caller needed that
+ * many pages they will be filling in at least up to the last page.
+ */
+ if (num == 1) {
+ if (ret) {
+ VGMEMP_ALLOC(env, ret, sz);
+ VGMEMP_DEFINED(ret, sizeof(ret->mp_next));
+ env->me_dpages = ret->mp_next;
+ return ret;
+ }
+ psize -= off = PAGEHDRSZ;
+ } else {
+ sz *= num;
+ off = sz - psize;
+ }
+ if ((ret = malloc(sz)) != NULL) {
+ VGMEMP_ALLOC(env, ret, sz);
+ if (!(env->me_flags & MDB_NOMEMINIT)) {
+ memset((char *)ret + off, 0, psize);
+ ret->mp_pad = 0;
+ }
+ } else {
+ txn->mt_flags |= MDB_TXN_ERROR;
+ }
+ return ret;
+}
+/** Free a single page.
+ * Saves single pages to a list, for future reuse.
+ * (This is not used for multi-page overflow pages.)
+ */
+static void
+mdb_page_free(MDB_env *env, MDB_page *mp)
+{
+ mp->mp_next = env->me_dpages;
+ VGMEMP_FREE(env, mp);
+ env->me_dpages = mp;
+}
+
+/** Free a dirty page */
+static void
+mdb_dpage_free(MDB_env *env, MDB_page *dp)
+{
+ if (!IS_OVERFLOW(dp) || dp->mp_pages == 1) {
+ mdb_page_free(env, dp);
+ } else {
+ /* large pages just get freed directly */
+ VGMEMP_FREE(env, dp);
+ free(dp);
+ }
+}
+
+/** Return all dirty pages to dpage list */
+static void
+mdb_dlist_free(MDB_txn *txn)
+{
+ MDB_env *env = txn->mt_env;
+ MDB_ID2L dl = txn->mt_u.dirty_list;
+ unsigned i, n = dl[0].mid;
+
+ for (i = 1; i <= n; i++) {
+ mdb_dpage_free(env, dl[i].mptr);
+ }
+ dl[0].mid = 0;
+}
+
+/** Loosen or free a single page.
+ * Saves single pages to a list for future reuse
+ * in this same txn. It has been pulled from the freeDB
+ * and already resides on the dirty list, but has been
+ * deleted. Use these pages first before pulling again
+ * from the freeDB.
+ *
+ * If the page wasn't dirtied in this txn, just add it
+ * to this txn's free list.
+ */
+static int
+mdb_page_loose(MDB_cursor *mc, MDB_page *mp)
+{
+ int loose = 0;
+ pgno_t pgno = mp->mp_pgno;
+ MDB_txn *txn = mc->mc_txn;
+
+ if ((mp->mp_flags & P_DIRTY) && mc->mc_dbi != FREE_DBI) {
+ if (txn->mt_parent) {
+ MDB_ID2 *dl = txn->mt_u.dirty_list;
+ /* If txn has a parent, make sure the page is in our
+ * dirty list.
+ */
+ if (dl[0].mid) {
+ unsigned x = mdb_mid2l_search(dl, pgno);
+ if (x <= dl[0].mid && dl[x].mid == pgno) {
+ if (mp != dl[x].mptr) { /* bad cursor? */
+ mc->mc_flags &= ~(C_INITIALIZED|C_EOF);
+ txn->mt_flags |= MDB_TXN_ERROR;
+ return MDB_CORRUPTED;
+ }
+ /* ok, it's ours */
+ loose = 1;
+ }
+ }
+ } else {
+ /* no parent txn, so it's just ours */
+ loose = 1;
+ }
+ }
+ if (loose) {
+ DPRINTF(("loosen db %d page %"Z"u", DDBI(mc),
+ mp->mp_pgno));
+ NEXT_LOOSE_PAGE(mp) = txn->mt_loose_pgs;
+ txn->mt_loose_pgs = mp;
+ txn->mt_loose_count++;
+ mp->mp_flags |= P_LOOSE;
+ } else {
+ int rc = mdb_midl_append(&txn->mt_free_pgs, pgno);
+ if (rc)
+ return rc;
+ }
+
+ return MDB_SUCCESS;
+}
+
+/** Set or clear P_KEEP in dirty, non-overflow, non-sub pages watched by txn.
+ * @param[in] mc A cursor handle for the current operation.
+ * @param[in] pflags Flags of the pages to update:
+ * P_DIRTY to set P_KEEP, P_DIRTY|P_KEEP to clear it.
+ * @param[in] all No shortcuts. Needed except after a full #mdb_page_flush().
+ * @return 0 on success, non-zero on failure.
+ */
+static int
+mdb_pages_xkeep(MDB_cursor *mc, unsigned pflags, int all)
+{
+ enum { Mask = P_SUBP|P_DIRTY|P_LOOSE|P_KEEP };
+ MDB_txn *txn = mc->mc_txn;
+ MDB_cursor *m3, *m0 = mc;
+ MDB_xcursor *mx;
+ MDB_page *dp, *mp;
+ MDB_node *leaf;
+ unsigned i, j;
+ int rc = MDB_SUCCESS, level;
+
+ /* Mark pages seen by cursors */
+ if (mc->mc_flags & C_UNTRACK)
+ mc = NULL; /* will find mc in mt_cursors */
+ for (i = txn->mt_numdbs;; mc = txn->mt_cursors[--i]) {
+ for (; mc; mc=mc->mc_next) {
+ if (!(mc->mc_flags & C_INITIALIZED))
+ continue;
+ for (m3 = mc;; m3 = &mx->mx_cursor) {
+ mp = NULL;
+ for (j=0; j<m3->mc_snum; j++) {
+ mp = m3->mc_pg[j];
+ if ((mp->mp_flags & Mask) == pflags)
+ mp->mp_flags ^= P_KEEP;
+ }
+ mx = m3->mc_xcursor;
+ /* Proceed to mx if it is at a sub-database */
+ if (! (mx && (mx->mx_cursor.mc_flags & C_INITIALIZED)))
+ break;
+ if (! (mp && (mp->mp_flags & P_LEAF)))
+ break;
+ leaf = NODEPTR(mp, m3->mc_ki[j-1]);
+ if (!(leaf->mn_flags & F_SUBDATA))
+ break;
+ }
+ }
+ if (i == 0)
+ break;
+ }
+
+ if (all) {
+ /* Mark dirty root pages */
+ for (i=0; i<txn->mt_numdbs; i++) {
+ if (txn->mt_dbflags[i] & DB_DIRTY) {
+ pgno_t pgno = txn->mt_dbs[i].md_root;
+ if (pgno == P_INVALID)
+ continue;
+ if ((rc = mdb_page_get(m0, pgno, &dp, &level)) != MDB_SUCCESS)
+ break;
+ if ((dp->mp_flags & Mask) == pflags && level <= 1)
+ dp->mp_flags ^= P_KEEP;
+ }
+ }
+ }
+
+ return rc;
+}
+
+static int mdb_page_flush(MDB_txn *txn, int keep);
+
+/** Spill pages from the dirty list back to disk.
+ * This is intended to prevent running into #MDB_TXN_FULL situations,
+ * but note that they may still occur in a few cases:
+ * 1) our estimate of the txn size could be too small. Currently this
+ * seems unlikely, except with a large number of #MDB_MULTIPLE items.
+ * 2) child txns may run out of space if their parents dirtied a
+ * lot of pages and never spilled them. TODO: we probably should do
+ * a preemptive spill during #mdb_txn_begin() of a child txn, if
+ * the parent's dirty_room is below a given threshold.
+ *
+ * Otherwise, if not using nested txns, it is expected that apps will
+ * not run into #MDB_TXN_FULL any more. The pages are flushed to disk
+ * the same way as for a txn commit, e.g. their P_DIRTY flag is cleared.
+ * If the txn never references them again, they can be left alone.
+ * If the txn only reads them, they can be used without any fuss.
+ * If the txn writes them again, they can be dirtied immediately without
+ * going thru all of the work of #mdb_page_touch(). Such references are
+ * handled by #mdb_page_unspill().
+ *
+ * Also note, we never spill DB root pages, nor pages of active cursors,
+ * because we'll need these back again soon anyway. And in nested txns,
+ * we can't spill a page in a child txn if it was already spilled in a
+ * parent txn. That would alter the parent txns' data even though
+ * the child hasn't committed yet, and we'd have no way to undo it if
+ * the child aborted.
+ *
+ * @param[in] m0 cursor A cursor handle identifying the transaction and
+ * database for which we are checking space.
+ * @param[in] key For a put operation, the key being stored.
+ * @param[in] data For a put operation, the data being stored.
+ * @return 0 on success, non-zero on failure.
+ */
+static int
+mdb_page_spill(MDB_cursor *m0, MDB_val *key, MDB_val *data)
+{
+ MDB_txn *txn = m0->mc_txn;
+ MDB_page *dp;
+ MDB_ID2L dl = txn->mt_u.dirty_list;
+ unsigned int i, j, need;
+ int rc;
+
+ if (m0->mc_flags & C_SUB)
+ return MDB_SUCCESS;
+
+ /* Estimate how much space this op will take */
+ i = m0->mc_db->md_depth;
+ /* Named DBs also dirty the main DB */
+ if (m0->mc_dbi >= CORE_DBS)
+ i += txn->mt_dbs[MAIN_DBI].md_depth;
+ /* For puts, roughly factor in the key+data size */
+ if (key)
+ i += (LEAFSIZE(key, data) + txn->mt_env->me_psize) / txn->mt_env->me_psize;
+ i += i; /* double it for good measure */
+ need = i;
+
+ if (txn->mt_dirty_room > i)
+ return MDB_SUCCESS;
+
+ if (!txn->mt_spill_pgs) {
+ txn->mt_spill_pgs = mdb_midl_alloc(MDB_IDL_UM_MAX);
+ if (!txn->mt_spill_pgs)
+ return ENOMEM;
+ } else {
+ /* purge deleted slots */
+ MDB_IDL sl = txn->mt_spill_pgs;
+ unsigned int num = sl[0];
+ j=0;
+ for (i=1; i<=num; i++) {
+ if (!(sl[i] & 1))
+ sl[++j] = sl[i];
+ }
+ sl[0] = j;
+ }
+
+ /* Preserve pages which may soon be dirtied again */
+ if ((rc = mdb_pages_xkeep(m0, P_DIRTY, 1)) != MDB_SUCCESS)
+ goto done;
+
+ /* Less aggressive spill - we originally spilled the entire dirty list,
+ * with a few exceptions for cursor pages and DB root pages. But this
+ * turns out to be a lot of wasted effort because in a large txn many
+ * of those pages will need to be used again. So now we spill only 1/8th
+ * of the dirty pages. Testing revealed this to be a good tradeoff,
+ * better than 1/2, 1/4, or 1/10.
+ */
+ if (need < MDB_IDL_UM_MAX / 8)
+ need = MDB_IDL_UM_MAX / 8;
+
+ /* Save the page IDs of all the pages we're flushing */
+ /* flush from the tail forward, this saves a lot of shifting later on. */
+ for (i=dl[0].mid; i && need; i--) {
+ MDB_ID pn = dl[i].mid << 1;
+ dp = dl[i].mptr;
+ if (dp->mp_flags & (P_LOOSE|P_KEEP))
+ continue;
+ /* Can't spill twice, make sure it's not already in a parent's
+ * spill list.
+ */
+ if (txn->mt_parent) {
+ MDB_txn *tx2;
+ for (tx2 = txn->mt_parent; tx2; tx2 = tx2->mt_parent) {
+ if (tx2->mt_spill_pgs) {
+ j = mdb_midl_search(tx2->mt_spill_pgs, pn);
+ if (j <= tx2->mt_spill_pgs[0] && tx2->mt_spill_pgs[j] == pn) {
+ dp->mp_flags |= P_KEEP;
+ break;
+ }
+ }
+ }
+ if (tx2)
+ continue;
+ }
+ if ((rc = mdb_midl_append(&txn->mt_spill_pgs, pn)))
+ goto done;
+ need--;
+ }
+ mdb_midl_sort(txn->mt_spill_pgs);
+
+ /* Flush the spilled part of dirty list */
+ if ((rc = mdb_page_flush(txn, i)) != MDB_SUCCESS)
+ goto done;
+
+ /* Reset any dirty pages we kept that page_flush didn't see */
+ rc = mdb_pages_xkeep(m0, P_DIRTY|P_KEEP, i);
+
+done:
+ txn->mt_flags |= rc ? MDB_TXN_ERROR : MDB_TXN_SPILLS;
+ return rc;
+}
+
+/** Find oldest txnid still referenced. Expects txn->mt_txnid > 0. */
+static txnid_t
+mdb_find_oldest(MDB_txn *txn)
+{
+ int i;
+ txnid_t mr, oldest = txn->mt_txnid - 1;
+ if (txn->mt_env->me_txns) {
+ MDB_reader *r = txn->mt_env->me_txns->mti_readers;
+ for (i = txn->mt_env->me_txns->mti_numreaders; --i >= 0; ) {
+ if (r[i].mr_pid) {
+ mr = r[i].mr_txnid;
+ if (oldest > mr)
+ oldest = mr;
+ }
+ }
+ }
+ return oldest;
+}
+
+/** Add a page to the txn's dirty list */
+static void
+mdb_page_dirty(MDB_txn *txn, MDB_page *mp)
+{
+ MDB_ID2 mid;
+ int rc, (*insert)(MDB_ID2L, MDB_ID2 *);
+
+ if (txn->mt_flags & MDB_TXN_WRITEMAP) {
+ insert = mdb_mid2l_append;
+ } else {
+ insert = mdb_mid2l_insert;
+ }
+ mid.mid = mp->mp_pgno;
+ mid.mptr = mp;
+ rc = insert(txn->mt_u.dirty_list, &mid);
+ mdb_tassert(txn, rc == 0);
+ txn->mt_dirty_room--;
+}
+
+/** Allocate page numbers and memory for writing. Maintain me_pglast,
+ * me_pghead and mt_next_pgno. Set #MDB_TXN_ERROR on failure.
+ *
+ * If there are free pages available from older transactions, they
+ * are re-used first. Otherwise allocate a new page at mt_next_pgno.
+ * Do not modify the freedB, just merge freeDB records into me_pghead[]
+ * and move me_pglast to say which records were consumed. Only this
+ * function can create me_pghead and move me_pglast/mt_next_pgno.
+ * @param[in] mc cursor A cursor handle identifying the transaction and
+ * database for which we are allocating.
+ * @param[in] num the number of pages to allocate.
+ * @param[out] mp Address of the allocated page(s). Requests for multiple pages
+ * will always be satisfied by a single contiguous chunk of memory.
+ * @return 0 on success, non-zero on failure.
+ */
+static int
+mdb_page_alloc(MDB_cursor *mc, int num, MDB_page **mp)
+{
+#ifdef MDB_PARANOID /* Seems like we can ignore this now */
+ /* Get at most <Max_retries> more freeDB records once me_pghead
+ * has enough pages. If not enough, use new pages from the map.
+ * If <Paranoid> and mc is updating the freeDB, only get new
+ * records if me_pghead is empty. Then the freelist cannot play
+ * catch-up with itself by growing while trying to save it.
+ */
+ enum { Paranoid = 1, Max_retries = 500 };
+#else
+ enum { Paranoid = 0, Max_retries = INT_MAX /*infinite*/ };
+#endif
+ int rc, retry = num * 60;
+ MDB_txn *txn = mc->mc_txn;
+ MDB_env *env = txn->mt_env;
+ pgno_t pgno, *mop = env->me_pghead;
+ unsigned i, j, mop_len = mop ? mop[0] : 0, n2 = num-1;
+ MDB_page *np;
+ txnid_t oldest = 0, last;
+ MDB_cursor_op op;
+ MDB_cursor m2;
+ int found_old = 0;
+
+ /* If there are any loose pages, just use them */
+ if (num == 1 && txn->mt_loose_pgs) {
+ np = txn->mt_loose_pgs;
+ txn->mt_loose_pgs = NEXT_LOOSE_PAGE(np);
+ txn->mt_loose_count--;
+ DPRINTF(("db %d use loose page %"Z"u", DDBI(mc),
+ np->mp_pgno));
+ *mp = np;
+ return MDB_SUCCESS;
+ }
+
+ *mp = NULL;
+
+ /* If our dirty list is already full, we can't do anything */
+ if (txn->mt_dirty_room == 0) {
+ rc = MDB_TXN_FULL;
+ goto fail;
+ }
+
+ for (op = MDB_FIRST;; op = MDB_NEXT) {
+ MDB_val key, data;
+ MDB_node *leaf;
+ pgno_t *idl;
+
+ /* Seek a big enough contiguous page range. Prefer
+ * pages at the tail, just truncating the list.
+ */
+ if (mop_len > n2) {
+ i = mop_len;
+ do {
+ pgno = mop[i];
+ if (mop[i-n2] == pgno+n2)
+ goto search_done;
+ } while (--i > n2);
+ if (--retry < 0)
+ break;
+ }
+
+ if (op == MDB_FIRST) { /* 1st iteration */
+ /* Prepare to fetch more and coalesce */
+ last = env->me_pglast;
+ oldest = env->me_pgoldest;
+ mdb_cursor_init(&m2, txn, FREE_DBI, NULL);
+ if (last) {
+ op = MDB_SET_RANGE;
+ key.mv_data = &last; /* will look up last+1 */
+ key.mv_size = sizeof(last);
+ }
+ if (Paranoid && mc->mc_dbi == FREE_DBI)
+ retry = -1;
+ }
+ if (Paranoid && retry < 0 && mop_len)
+ break;
+
+ last++;
+ /* Do not fetch more if the record will be too recent */
+ if (oldest <= last) {
+ if (!found_old) {
+ oldest = mdb_find_oldest(txn);
+ env->me_pgoldest = oldest;
+ found_old = 1;
+ }
+ if (oldest <= last)
+ break;
+ }
+ rc = mdb_cursor_get(&m2, &key, NULL, op);
+ if (rc) {
+ if (rc == MDB_NOTFOUND)
+ break;
+ goto fail;
+ }
+ last = *(txnid_t*)key.mv_data;
+ if (oldest <= last) {
+ if (!found_old) {
+ oldest = mdb_find_oldest(txn);
+ env->me_pgoldest = oldest;
+ found_old = 1;
+ }
+ if (oldest <= last)
+ break;
+ }
+ np = m2.mc_pg[m2.mc_top];
+ leaf = NODEPTR(np, m2.mc_ki[m2.mc_top]);
+ if ((rc = mdb_node_read(&m2, leaf, &data)) != MDB_SUCCESS)
+ goto fail;
+
+ idl = (MDB_ID *) data.mv_data;
+ i = idl[0];
+ if (!mop) {
+ if (!(env->me_pghead = mop = mdb_midl_alloc(i))) {
+ rc = ENOMEM;
+ goto fail;
+ }
+ } else {
+ if ((rc = mdb_midl_need(&env->me_pghead, i)) != 0)
+ goto fail;
+ mop = env->me_pghead;
+ }
+ env->me_pglast = last;
+#if (MDB_DEBUG) > 1
+ DPRINTF(("IDL read txn %"Z"u root %"Z"u num %u",
+ last, txn->mt_dbs[FREE_DBI].md_root, i));
+ for (j = i; j; j--)
+ DPRINTF(("IDL %"Z"u", idl[j]));
+#endif
+ /* Merge in descending sorted order */
+ mdb_midl_xmerge(mop, idl);
+ mop_len = mop[0];
+ }
+
+ /* Use new pages from the map when nothing suitable in the freeDB */
+ i = 0;
+ pgno = txn->mt_next_pgno;
+ if (pgno + num >= env->me_maxpg) {
+ DPUTS("DB size maxed out");
+ rc = MDB_MAP_FULL;
+ goto fail;
+ }
+
+search_done:
+ if (env->me_flags & MDB_WRITEMAP) {
+ np = (MDB_page *)(env->me_map + env->me_psize * pgno);
+ } else {
+ if (!(np = mdb_page_malloc(txn, num))) {
+ rc = ENOMEM;
+ goto fail;
+ }
+ }
+ if (i) {
+ mop[0] = mop_len -= num;
+ /* Move any stragglers down */
+ for (j = i-num; j < mop_len; )
+ mop[++j] = mop[++i];
+ } else {
+ txn->mt_next_pgno = pgno + num;
+ }
+ np->mp_pgno = pgno;
+ mdb_page_dirty(txn, np);
+ *mp = np;
+
+ return MDB_SUCCESS;
+
+fail:
+ txn->mt_flags |= MDB_TXN_ERROR;
+ return rc;
+}
+
+/** Copy the used portions of a non-overflow page.
+ * @param[in] dst page to copy into
+ * @param[in] src page to copy from
+ * @param[in] psize size of a page
+ */
+static void
+mdb_page_copy(MDB_page *dst, MDB_page *src, unsigned int psize)
+{
+ enum { Align = sizeof(pgno_t) };
+ indx_t upper = src->mp_upper, lower = src->mp_lower, unused = upper-lower;
+
+ /* If page isn't full, just copy the used portion. Adjust
+ * alignment so memcpy may copy words instead of bytes.
+ */
+ if ((unused &= -Align) && !IS_LEAF2(src)) {
+ upper = (upper + PAGEBASE) & -Align;
+ memcpy(dst, src, (lower + PAGEBASE + (Align-1)) & -Align);
+ memcpy((pgno_t *)((char *)dst+upper), (pgno_t *)((char *)src+upper),
+ psize - upper);
+ } else {
+ memcpy(dst, src, psize - unused);
+ }
+}
+
+/** Pull a page off the txn's spill list, if present.
+ * If a page being referenced was spilled to disk in this txn, bring
+ * it back and make it dirty/writable again.
+ * @param[in] txn the transaction handle.
+ * @param[in] mp the page being referenced. It must not be dirty.
+ * @param[out] ret the writable page, if any. ret is unchanged if
+ * mp wasn't spilled.
+ */
+static int
+mdb_page_unspill(MDB_txn *txn, MDB_page *mp, MDB_page **ret)
+{
+ MDB_env *env = txn->mt_env;
+ const MDB_txn *tx2;
+ unsigned x;
+ pgno_t pgno = mp->mp_pgno, pn = pgno << 1;
+
+ for (tx2 = txn; tx2; tx2=tx2->mt_parent) {
+ if (!tx2->mt_spill_pgs)
+ continue;
+ x = mdb_midl_search(tx2->mt_spill_pgs, pn);
+ if (x <= tx2->mt_spill_pgs[0] && tx2->mt_spill_pgs[x] == pn) {
+ MDB_page *np;
+ int num;
+ if (txn->mt_dirty_room == 0)
+ return MDB_TXN_FULL;
+ if (IS_OVERFLOW(mp))
+ num = mp->mp_pages;
+ else
+ num = 1;
+ if (env->me_flags & MDB_WRITEMAP) {
+ np = mp;
+ } else {
+ np = mdb_page_malloc(txn, num);
+ if (!np)
+ return ENOMEM;
+ if (num > 1)
+ memcpy(np, mp, num * env->me_psize);
+ else
+ mdb_page_copy(np, mp, env->me_psize);
+ }
+ if (tx2 == txn) {
+ /* If in current txn, this page is no longer spilled.
+ * If it happens to be the last page, truncate the spill list.
+ * Otherwise mark it as deleted by setting the LSB.
+ */
+ if (x == txn->mt_spill_pgs[0])
+ txn->mt_spill_pgs[0]--;
+ else
+ txn->mt_spill_pgs[x] |= 1;
+ } /* otherwise, if belonging to a parent txn, the
+ * page remains spilled until child commits
+ */
+
+ mdb_page_dirty(txn, np);
+ np->mp_flags |= P_DIRTY;
+ *ret = np;
+ break;
+ }
+ }
+ return MDB_SUCCESS;
+}
+
+/** Touch a page: make it dirty and re-insert into tree with updated pgno.
+ * Set #MDB_TXN_ERROR on failure.
+ * @param[in] mc cursor pointing to the page to be touched
+ * @return 0 on success, non-zero on failure.
+ */
+static int
+mdb_page_touch(MDB_cursor *mc)
+{
+ MDB_page *mp = mc->mc_pg[mc->mc_top], *np;
+ MDB_txn *txn = mc->mc_txn;
+ MDB_cursor *m2, *m3;
+ pgno_t pgno;
+ int rc;
+
+ if (!F_ISSET(mp->mp_flags, P_DIRTY)) {
+ if (txn->mt_flags & MDB_TXN_SPILLS) {
+ np = NULL;
+ rc = mdb_page_unspill(txn, mp, &np);
+ if (rc)
+ goto fail;
+ if (np)
+ goto done;
+ }
+ if ((rc = mdb_midl_need(&txn->mt_free_pgs, 1)) ||
+ (rc = mdb_page_alloc(mc, 1, &np)))
+ goto fail;
+ pgno = np->mp_pgno;
+ DPRINTF(("touched db %d page %"Z"u -> %"Z"u", DDBI(mc),
+ mp->mp_pgno, pgno));
+ mdb_cassert(mc, mp->mp_pgno != pgno);
+ mdb_midl_xappend(txn->mt_free_pgs, mp->mp_pgno);
+ /* Update the parent page, if any, to point to the new page */
+ if (mc->mc_top) {
+ MDB_page *parent = mc->mc_pg[mc->mc_top-1];
+ MDB_node *node = NODEPTR(parent, mc->mc_ki[mc->mc_top-1]);
+ SETPGNO(node, pgno);
+ } else {
+ mc->mc_db->md_root = pgno;
+ }
+ } else if (txn->mt_parent && !IS_SUBP(mp)) {
+ MDB_ID2 mid, *dl = txn->mt_u.dirty_list;
+ pgno = mp->mp_pgno;
+ /* If txn has a parent, make sure the page is in our
+ * dirty list.
+ */
+ if (dl[0].mid) {
+ unsigned x = mdb_mid2l_search(dl, pgno);
+ if (x <= dl[0].mid && dl[x].mid == pgno) {
+ if (mp != dl[x].mptr) { /* bad cursor? */
+ mc->mc_flags &= ~(C_INITIALIZED|C_EOF);
+ txn->mt_flags |= MDB_TXN_ERROR;
+ return MDB_CORRUPTED;
+ }
+ return 0;
+ }
+ }
+ mdb_cassert(mc, dl[0].mid < MDB_IDL_UM_MAX);
+ /* No - copy it */
+ np = mdb_page_malloc(txn, 1);
+ if (!np)
+ return ENOMEM;
+ mid.mid = pgno;
+ mid.mptr = np;
+ rc = mdb_mid2l_insert(dl, &mid);
+ mdb_cassert(mc, rc == 0);
+ } else {
+ return 0;
+ }
+
+ mdb_page_copy(np, mp, txn->mt_env->me_psize);
+ np->mp_pgno = pgno;
+ np->mp_flags |= P_DIRTY;
+
+done:
+ /* Adjust cursors pointing to mp */
+ mc->mc_pg[mc->mc_top] = np;
+ m2 = txn->mt_cursors[mc->mc_dbi];
+ if (mc->mc_flags & C_SUB) {
+ for (; m2; m2=m2->mc_next) {
+ m3 = &m2->mc_xcursor->mx_cursor;
+ if (m3->mc_snum < mc->mc_snum) continue;
+ if (m3->mc_pg[mc->mc_top] == mp)
+ m3->mc_pg[mc->mc_top] = np;
+ }
+ } else {
+ for (; m2; m2=m2->mc_next) {
+ if (m2->mc_snum < mc->mc_snum) continue;
+ if (m2 == mc) continue;
+ if (m2->mc_pg[mc->mc_top] == mp) {
+ m2->mc_pg[mc->mc_top] = np;
+ if (IS_LEAF(np))
+ XCURSOR_REFRESH(m2, mc->mc_top, np);
+ }
+ }
+ }
+ return 0;
+
+fail:
+ txn->mt_flags |= MDB_TXN_ERROR;
+ return rc;
+}
+
+int
+mdb_env_sync(MDB_env *env, int force)
+{
+ int rc = 0;
+ if (env->me_flags & MDB_RDONLY)
+ return EACCES;
+ if (force || !F_ISSET(env->me_flags, MDB_NOSYNC)) {
+ if (env->me_flags & MDB_WRITEMAP) {
+ int flags = ((env->me_flags & MDB_MAPASYNC) && !force)
+ ? MS_ASYNC : MS_SYNC;
+ if (MDB_MSYNC(env->me_map, env->me_mapsize, flags))
+ rc = ErrCode();
+#ifdef _WIN32
+ else if (flags == MS_SYNC && MDB_FDATASYNC(env->me_fd))
+ rc = ErrCode();
+#endif
+ } else {
+#ifdef BROKEN_FDATASYNC
+ if (env->me_flags & MDB_FSYNCONLY) {
+ if (fsync(env->me_fd))
+ rc = ErrCode();
+ } else
+#endif
+ if (MDB_FDATASYNC(env->me_fd))
+ rc = ErrCode();
+ }
+ }
+ return rc;
+}
+
+/** Back up parent txn's cursors, then grab the originals for tracking */
+static int
+mdb_cursor_shadow(MDB_txn *src, MDB_txn *dst)
+{
+ MDB_cursor *mc, *bk;
+ MDB_xcursor *mx;
+ size_t size;
+ int i;
+
+ for (i = src->mt_numdbs; --i >= 0; ) {
+ if ((mc = src->mt_cursors[i]) != NULL) {
+ size = sizeof(MDB_cursor);
+ if (mc->mc_xcursor)
+ size += sizeof(MDB_xcursor);
+ for (; mc; mc = bk->mc_next) {
+ bk = malloc(size);
+ if (!bk)
+ return ENOMEM;
+ *bk = *mc;
+ mc->mc_backup = bk;
+ mc->mc_db = &dst->mt_dbs[i];
+ /* Kill pointers into src to reduce abuse: The
+ * user may not use mc until dst ends. But we need a valid
+ * txn pointer here for cursor fixups to keep working.
+ */
+ mc->mc_txn = dst;
+ mc->mc_dbflag = &dst->mt_dbflags[i];
+ if ((mx = mc->mc_xcursor) != NULL) {
+ *(MDB_xcursor *)(bk+1) = *mx;
+ mx->mx_cursor.mc_txn = dst;
+ }
+ mc->mc_next = dst->mt_cursors[i];
+ dst->mt_cursors[i] = mc;
+ }
+ }
+ }
+ return MDB_SUCCESS;
+}
+
+/** Close this write txn's cursors, give parent txn's cursors back to parent.
+ * @param[in] txn the transaction handle.
+ * @param[in] merge true to keep changes to parent cursors, false to revert.
+ * @return 0 on success, non-zero on failure.
+ */
+static void
+mdb_cursors_close(MDB_txn *txn, unsigned merge)
+{
+ MDB_cursor **cursors = txn->mt_cursors, *mc, *next, *bk;
+ MDB_xcursor *mx;
+ int i;
+
+ for (i = txn->mt_numdbs; --i >= 0; ) {
+ for (mc = cursors[i]; mc; mc = next) {
+ next = mc->mc_next;
+ if ((bk = mc->mc_backup) != NULL) {
+ if (merge) {
+ /* Commit changes to parent txn */
+ mc->mc_next = bk->mc_next;
+ mc->mc_backup = bk->mc_backup;
+ mc->mc_txn = bk->mc_txn;
+ mc->mc_db = bk->mc_db;
+ mc->mc_dbflag = bk->mc_dbflag;
+ if ((mx = mc->mc_xcursor) != NULL)
+ mx->mx_cursor.mc_txn = bk->mc_txn;
+ } else {
+ /* Abort nested txn */
+ *mc = *bk;
+ if ((mx = mc->mc_xcursor) != NULL)
+ *mx = *(MDB_xcursor *)(bk+1);
+ }
+ mc = bk;
+ }
+ /* Only malloced cursors are permanently tracked. */
+ free(mc);
+ }
+ cursors[i] = NULL;
+ }
+}
+
+#if !(MDB_PIDLOCK) /* Currently the same as defined(_WIN32) */
+enum Pidlock_op {
+ Pidset, Pidcheck
+};
+#else
+enum Pidlock_op {
+ Pidset = F_SETLK, Pidcheck = F_GETLK
+};
+#endif
+
+/** Set or check a pid lock. Set returns 0 on success.
+ * Check returns 0 if the process is certainly dead, nonzero if it may
+ * be alive (the lock exists or an error happened so we do not know).
+ *
+ * On Windows Pidset is a no-op, we merely check for the existence
+ * of the process with the given pid. On POSIX we use a single byte
+ * lock on the lockfile, set at an offset equal to the pid.
+ */
+static int
+mdb_reader_pid(MDB_env *env, enum Pidlock_op op, MDB_PID_T pid)
+{
+#if !(MDB_PIDLOCK) /* Currently the same as defined(_WIN32) */
+ int ret = 0;
+ HANDLE h;
+ if (op == Pidcheck) {
+ h = OpenProcess(env->me_pidquery, FALSE, pid);
+ /* No documented "no such process" code, but other program use this: */
+ if (!h)
+ return ErrCode() != ERROR_INVALID_PARAMETER;
+ /* A process exists until all handles to it close. Has it exited? */
+ ret = WaitForSingleObject(h, 0) != 0;
+ CloseHandle(h);
+ }
+ return ret;
+#else
+ for (;;) {
+ int rc;
+ struct flock lock_info;
+ memset(&lock_info, 0, sizeof(lock_info));
+ lock_info.l_type = F_WRLCK;
+ lock_info.l_whence = SEEK_SET;
+ lock_info.l_start = pid;
+ lock_info.l_len = 1;
+ if ((rc = fcntl(env->me_lfd, op, &lock_info)) == 0) {
+ if (op == F_GETLK && lock_info.l_type != F_UNLCK)
+ rc = -1;
+ } else if ((rc = ErrCode()) == EINTR) {
+ continue;
+ }
+ return rc;
+ }
+#endif
+}
+
+/** Common code for #mdb_txn_begin() and #mdb_txn_renew().
+ * @param[in] txn the transaction handle to initialize
+ * @return 0 on success, non-zero on failure.
+ */
+static int
+mdb_txn_renew0(MDB_txn *txn)
+{
+ MDB_env *env = txn->mt_env;
+ MDB_txninfo *ti = env->me_txns;
+ MDB_meta *meta;
+ unsigned int i, nr, flags = txn->mt_flags;
+ uint16_t x;
+ int rc, new_notls = 0;
+
+ if ((flags &= MDB_TXN_RDONLY) != 0) {
+ if (!ti) {
+ meta = mdb_env_pick_meta(env);
+ txn->mt_txnid = meta->mm_txnid;
+ txn->mt_u.reader = NULL;
+ } else {
+ MDB_reader *r = (env->me_flags & MDB_NOTLS) ? txn->mt_u.reader :
+ pthread_getspecific(env->me_txkey);
+ if (r) {
+ if (r->mr_pid != env->me_pid || r->mr_txnid != (txnid_t)-1)
+ return MDB_BAD_RSLOT;
+ } else {
+ MDB_PID_T pid = env->me_pid;
+ MDB_THR_T tid = pthread_self();
+ mdb_mutexref_t rmutex = env->me_rmutex;
+
+ if (!env->me_live_reader) {
+ rc = mdb_reader_pid(env, Pidset, pid);
+ if (rc)
+ return rc;
+ env->me_live_reader = 1;
+ }
+
+ if (LOCK_MUTEX(rc, env, rmutex))
+ return rc;
+ nr = ti->mti_numreaders;
+ for (i=0; i<nr; i++)
+ if (ti->mti_readers[i].mr_pid == 0)
+ break;
+ if (i == env->me_maxreaders) {
+ UNLOCK_MUTEX(rmutex);
+ return MDB_READERS_FULL;
+ }
+ r = &ti->mti_readers[i];
+ /* Claim the reader slot, carefully since other code
+ * uses the reader table un-mutexed: First reset the
+ * slot, next publish it in mti_numreaders. After
+ * that, it is safe for mdb_env_close() to touch it.
+ * When it will be closed, we can finally claim it.
+ */
+ r->mr_pid = 0;
+ r->mr_txnid = (txnid_t)-1;
+ r->mr_tid = tid;
+ if (i == nr)
+ ti->mti_numreaders = ++nr;
+ env->me_close_readers = nr;
+ r->mr_pid = pid;
+ UNLOCK_MUTEX(rmutex);
+
+ new_notls = (env->me_flags & MDB_NOTLS);
+ if (!new_notls && (rc=pthread_setspecific(env->me_txkey, r))) {
+ r->mr_pid = 0;
+ return rc;
+ }
+ }
+ do /* LY: Retry on a race, ITS#7970. */
+ r->mr_txnid = ti->mti_txnid;
+ while(r->mr_txnid != ti->mti_txnid);
+ txn->mt_txnid = r->mr_txnid;
+ txn->mt_u.reader = r;
+ meta = env->me_metas[txn->mt_txnid & 1];
+ }
+
+ } else {
+ /* Not yet touching txn == env->me_txn0, it may be active */
+ if (ti) {
+ if (LOCK_MUTEX(rc, env, env->me_wmutex))
+ return rc;
+ txn->mt_txnid = ti->mti_txnid;
+ meta = env->me_metas[txn->mt_txnid & 1];
+ } else {
+ meta = mdb_env_pick_meta(env);
+ txn->mt_txnid = meta->mm_txnid;
+ }
+ txn->mt_txnid++;
+#if MDB_DEBUG
+ if (txn->mt_txnid == mdb_debug_start)
+ mdb_debug = 1;
+#endif
+ txn->mt_child = NULL;
+ txn->mt_loose_pgs = NULL;
+ txn->mt_loose_count = 0;
+ txn->mt_dirty_room = MDB_IDL_UM_MAX;
+ txn->mt_u.dirty_list = env->me_dirty_list;
+ txn->mt_u.dirty_list[0].mid = 0;
+ txn->mt_free_pgs = env->me_free_pgs;
+ txn->mt_free_pgs[0] = 0;
+ txn->mt_spill_pgs = NULL;
+ env->me_txn = txn;
+ memcpy(txn->mt_dbiseqs, env->me_dbiseqs, env->me_maxdbs * sizeof(unsigned int));
+ }
+
+ /* Copy the DB info and flags */
+ memcpy(txn->mt_dbs, meta->mm_dbs, CORE_DBS * sizeof(MDB_db));
+
+ /* Moved to here to avoid a data race in read TXNs */
+ txn->mt_next_pgno = meta->mm_last_pg+1;
+
+ txn->mt_flags = flags;
+
+ /* Setup db info */
+ txn->mt_numdbs = env->me_numdbs;
+ for (i=CORE_DBS; i<txn->mt_numdbs; i++) {
+ x = env->me_dbflags[i];
+ txn->mt_dbs[i].md_flags = x & PERSISTENT_FLAGS;
+ txn->mt_dbflags[i] = (x & MDB_VALID) ? DB_VALID|DB_USRVALID|DB_STALE : 0;
+ }
+ txn->mt_dbflags[MAIN_DBI] = DB_VALID|DB_USRVALID;
+ txn->mt_dbflags[FREE_DBI] = DB_VALID;
+
+ if (env->me_flags & MDB_FATAL_ERROR) {
+ DPUTS("environment had fatal error, must shutdown!");
+ rc = MDB_PANIC;
+ } else if (env->me_maxpg < txn->mt_next_pgno) {
+ rc = MDB_MAP_RESIZED;
+ } else {
+ return MDB_SUCCESS;
+ }
+ mdb_txn_end(txn, new_notls /*0 or MDB_END_SLOT*/ | MDB_END_FAIL_BEGIN);
+ return rc;
+}
+
+int
+mdb_txn_renew(MDB_txn *txn)
+{
+ int rc;
+
+ if (!txn || !F_ISSET(txn->mt_flags, MDB_TXN_RDONLY|MDB_TXN_FINISHED))
+ return EINVAL;
+
+ rc = mdb_txn_renew0(txn);
+ if (rc == MDB_SUCCESS) {
+ DPRINTF(("renew txn %"Z"u%c %p on mdbenv %p, root page %"Z"u",
+ txn->mt_txnid, (txn->mt_flags & MDB_TXN_RDONLY) ? 'r' : 'w',
+ (void *)txn, (void *)txn->mt_env, txn->mt_dbs[MAIN_DBI].md_root));
+ }
+ return rc;
+}
+
+int
+mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **ret)
+{
+ MDB_txn *txn;
+ MDB_ntxn *ntxn;
+ int rc, size, tsize;
+
+ flags &= MDB_TXN_BEGIN_FLAGS;
+ flags |= env->me_flags & MDB_WRITEMAP;
+
+ if (env->me_flags & MDB_RDONLY & ~flags) /* write txn in RDONLY env */
+ return EACCES;
+
+ if (parent) {
+ /* Nested transactions: Max 1 child, write txns only, no writemap */
+ flags |= parent->mt_flags;
+ if (flags & (MDB_RDONLY|MDB_WRITEMAP|MDB_TXN_BLOCKED)) {
+ return (parent->mt_flags & MDB_TXN_RDONLY) ? EINVAL : MDB_BAD_TXN;
+ }
+ /* Child txns save MDB_pgstate and use own copy of cursors */
+ size = env->me_maxdbs * (sizeof(MDB_db)+sizeof(MDB_cursor *)+1);
+ size += tsize = sizeof(MDB_ntxn);
+ } else if (flags & MDB_RDONLY) {
+ size = env->me_maxdbs * (sizeof(MDB_db)+1);
+ size += tsize = sizeof(MDB_txn);
+ } else {
+ /* Reuse preallocated write txn. However, do not touch it until
+ * mdb_txn_renew0() succeeds, since it currently may be active.
+ */
+ txn = env->me_txn0;
+ goto renew;
+ }
+ if ((txn = calloc(1, size)) == NULL) {
+ DPRINTF(("calloc: %s", strerror(errno)));
+ return ENOMEM;
+ }
+ txn->mt_dbxs = env->me_dbxs; /* static */
+ txn->mt_dbs = (MDB_db *) ((char *)txn + tsize);
+ txn->mt_dbflags = (unsigned char *)txn + size - env->me_maxdbs;
+ txn->mt_flags = flags;
+ txn->mt_env = env;
+
+ if (parent) {
+ unsigned int i;
+ txn->mt_cursors = (MDB_cursor **)(txn->mt_dbs + env->me_maxdbs);
+ txn->mt_dbiseqs = parent->mt_dbiseqs;
+ txn->mt_u.dirty_list = malloc(sizeof(MDB_ID2)*MDB_IDL_UM_SIZE);
+ if (!txn->mt_u.dirty_list ||
+ !(txn->mt_free_pgs = mdb_midl_alloc(MDB_IDL_UM_MAX)))
+ {
+ free(txn->mt_u.dirty_list);
+ free(txn);
+ return ENOMEM;
+ }
+ txn->mt_txnid = parent->mt_txnid;
+ txn->mt_dirty_room = parent->mt_dirty_room;
+ txn->mt_u.dirty_list[0].mid = 0;
+ txn->mt_spill_pgs = NULL;
+ txn->mt_next_pgno = parent->mt_next_pgno;
+ parent->mt_flags |= MDB_TXN_HAS_CHILD;
+ parent->mt_child = txn;
+ txn->mt_parent = parent;
+ txn->mt_numdbs = parent->mt_numdbs;
+ memcpy(txn->mt_dbs, parent->mt_dbs, txn->mt_numdbs * sizeof(MDB_db));
+ /* Copy parent's mt_dbflags, but clear DB_NEW */
+ for (i=0; i<txn->mt_numdbs; i++)
+ txn->mt_dbflags[i] = parent->mt_dbflags[i] & ~DB_NEW;
+ rc = 0;
+ ntxn = (MDB_ntxn *)txn;
+ ntxn->mnt_pgstate = env->me_pgstate; /* save parent me_pghead & co */
+ if (env->me_pghead) {
+ size = MDB_IDL_SIZEOF(env->me_pghead);
+ env->me_pghead = mdb_midl_alloc(env->me_pghead[0]);
+ if (env->me_pghead)
+ memcpy(env->me_pghead, ntxn->mnt_pgstate.mf_pghead, size);
+ else
+ rc = ENOMEM;
+ }
+ if (!rc)
+ rc = mdb_cursor_shadow(parent, txn);
+ if (rc)
+ mdb_txn_end(txn, MDB_END_FAIL_BEGINCHILD);
+ } else { /* MDB_RDONLY */
+ txn->mt_dbiseqs = env->me_dbiseqs;
+renew:
+ rc = mdb_txn_renew0(txn);
+ }
+ if (rc) {
+ if (txn != env->me_txn0)
+ free(txn);
+ } else {
+ txn->mt_flags |= flags; /* could not change txn=me_txn0 earlier */
+ *ret = txn;
+ DPRINTF(("begin txn %"Z"u%c %p on mdbenv %p, root page %"Z"u",
+ txn->mt_txnid, (flags & MDB_RDONLY) ? 'r' : 'w',
+ (void *) txn, (void *) env, txn->mt_dbs[MAIN_DBI].md_root));
+ }
+
+ return rc;
+}
+
+MDB_env *
+mdb_txn_env(MDB_txn *txn)
+{
+ if(!txn) return NULL;
+ return txn->mt_env;
+}
+
+size_t
+mdb_txn_id(MDB_txn *txn)
+{
+ if(!txn) return 0;
+ return txn->mt_txnid;
+}
+
+/** Export or close DBI handles opened in this txn. */
+static void
+mdb_dbis_update(MDB_txn *txn, int keep)
+{
+ int i;
+ MDB_dbi n = txn->mt_numdbs;
+ MDB_env *env = txn->mt_env;
+ unsigned char *tdbflags = txn->mt_dbflags;
+
+ for (i = n; --i >= CORE_DBS;) {
+ if (tdbflags[i] & DB_NEW) {
+ if (keep) {
+ env->me_dbflags[i] = txn->mt_dbs[i].md_flags | MDB_VALID;
+ } else {
+ char *ptr = env->me_dbxs[i].md_name.mv_data;
+ if (ptr) {
+ env->me_dbxs[i].md_name.mv_data = NULL;
+ env->me_dbxs[i].md_name.mv_size = 0;
+ env->me_dbflags[i] = 0;
+ env->me_dbiseqs[i]++;
+ free(ptr);
+ }
+ }
+ }
+ }
+ if (keep && env->me_numdbs < n)
+ env->me_numdbs = n;
+}
+
+/** End a transaction, except successful commit of a nested transaction.
+ * May be called twice for readonly txns: First reset it, then abort.
+ * @param[in] txn the transaction handle to end
+ * @param[in] mode why and how to end the transaction
+ */
+static void
+mdb_txn_end(MDB_txn *txn, unsigned mode)
+{
+ MDB_env *env = txn->mt_env;
+#if MDB_DEBUG
+ static const char *const names[] = MDB_END_NAMES;
+#endif
+
+ /* Export or close DBI handles opened in this txn */
+ mdb_dbis_update(txn, mode & MDB_END_UPDATE);
+
+ DPRINTF(("%s txn %"Z"u%c %p on mdbenv %p, root page %"Z"u",
+ names[mode & MDB_END_OPMASK],
+ txn->mt_txnid, (txn->mt_flags & MDB_TXN_RDONLY) ? 'r' : 'w',
+ (void *) txn, (void *)env, txn->mt_dbs[MAIN_DBI].md_root));
+
+ if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) {
+ if (txn->mt_u.reader) {
+ txn->mt_u.reader->mr_txnid = (txnid_t)-1;
+ if (!(env->me_flags & MDB_NOTLS)) {
+ txn->mt_u.reader = NULL; /* txn does not own reader */
+ } else if (mode & MDB_END_SLOT) {
+ txn->mt_u.reader->mr_pid = 0;
+ txn->mt_u.reader = NULL;
+ } /* else txn owns the slot until it does MDB_END_SLOT */
+ }
+ txn->mt_numdbs = 0; /* prevent further DBI activity */
+ txn->mt_flags |= MDB_TXN_FINISHED;
+
+ } else if (!F_ISSET(txn->mt_flags, MDB_TXN_FINISHED)) {
+ pgno_t *pghead = env->me_pghead;
+
+ if (!(mode & MDB_END_UPDATE)) /* !(already closed cursors) */
+ mdb_cursors_close(txn, 0);
+ if (!(env->me_flags & MDB_WRITEMAP)) {
+ mdb_dlist_free(txn);
+ }
+
+ txn->mt_numdbs = 0;
+ txn->mt_flags = MDB_TXN_FINISHED;
+
+ if (!txn->mt_parent) {
+ mdb_midl_shrink(&txn->mt_free_pgs);
+ env->me_free_pgs = txn->mt_free_pgs;
+ /* me_pgstate: */
+ env->me_pghead = NULL;
+ env->me_pglast = 0;
+
+ env->me_txn = NULL;
+ mode = 0; /* txn == env->me_txn0, do not free() it */
+
+ /* The writer mutex was locked in mdb_txn_begin. */
+ if (env->me_txns)
+ UNLOCK_MUTEX(env->me_wmutex);
+ } else {
+ txn->mt_parent->mt_child = NULL;
+ txn->mt_parent->mt_flags &= ~MDB_TXN_HAS_CHILD;
+ env->me_pgstate = ((MDB_ntxn *)txn)->mnt_pgstate;
+ mdb_midl_free(txn->mt_free_pgs);
+ free(txn->mt_u.dirty_list);
+ }
+ mdb_midl_free(txn->mt_spill_pgs);
+
+ mdb_midl_free(pghead);
+ }
+
+ if (mode & MDB_END_FREE)
+ free(txn);
+}
+
+void
+mdb_txn_reset(MDB_txn *txn)
+{
+ if (txn == NULL)
+ return;
+
+ /* This call is only valid for read-only txns */
+ if (!(txn->mt_flags & MDB_TXN_RDONLY))
+ return;
+
+ mdb_txn_end(txn, MDB_END_RESET);
+}
+
+void
+mdb_txn_abort(MDB_txn *txn)
+{
+ if (txn == NULL)
+ return;
+
+ if (txn->mt_child)
+ mdb_txn_abort(txn->mt_child);
+
+ mdb_txn_end(txn, MDB_END_ABORT|MDB_END_SLOT|MDB_END_FREE);
+}
+
+/** Save the freelist as of this transaction to the freeDB.
+ * This changes the freelist. Keep trying until it stabilizes.
+ */
+static int
+mdb_freelist_save(MDB_txn *txn)
+{
+ /* env->me_pghead[] can grow and shrink during this call.
+ * env->me_pglast and txn->mt_free_pgs[] can only grow.
+ * Page numbers cannot disappear from txn->mt_free_pgs[].
+ */
+ MDB_cursor mc;
+ MDB_env *env = txn->mt_env;
+ int rc, maxfree_1pg = env->me_maxfree_1pg, more = 1;
+ txnid_t pglast = 0, head_id = 0;
+ pgno_t freecnt = 0, *free_pgs, *mop;
+ ssize_t head_room = 0, total_room = 0, mop_len, clean_limit;
+
+ mdb_cursor_init(&mc, txn, FREE_DBI, NULL);
+
+ if (env->me_pghead) {
+ /* Make sure first page of freeDB is touched and on freelist */
+ rc = mdb_page_search(&mc, NULL, MDB_PS_FIRST|MDB_PS_MODIFY);
+ if (rc && rc != MDB_NOTFOUND)
+ return rc;
+ }
+
+ if (!env->me_pghead && txn->mt_loose_pgs) {
+ /* Put loose page numbers in mt_free_pgs, since
+ * we may be unable to return them to me_pghead.
+ */
+ MDB_page *mp = txn->mt_loose_pgs;
+ MDB_ID2 *dl = txn->mt_u.dirty_list;
+ unsigned x;
+ if ((rc = mdb_midl_need(&txn->mt_free_pgs, txn->mt_loose_count)) != 0)
+ return rc;
+ for (; mp; mp = NEXT_LOOSE_PAGE(mp)) {
+ mdb_midl_xappend(txn->mt_free_pgs, mp->mp_pgno);
+ /* must also remove from dirty list */
+ if (txn->mt_flags & MDB_TXN_WRITEMAP) {
+ for (x=1; x<=dl[0].mid; x++)
+ if (dl[x].mid == mp->mp_pgno)
+ break;
+ mdb_tassert(txn, x <= dl[0].mid);
+ } else {
+ x = mdb_mid2l_search(dl, mp->mp_pgno);
+ mdb_tassert(txn, dl[x].mid == mp->mp_pgno);
+ mdb_dpage_free(env, mp);
+ }
+ dl[x].mptr = NULL;
+ }
+ {
+ /* squash freed slots out of the dirty list */
+ unsigned y;
+ for (y=1; dl[y].mptr && y <= dl[0].mid; y++);
+ if (y <= dl[0].mid) {
+ for(x=y, y++;;) {
+ while (!dl[y].mptr && y <= dl[0].mid) y++;
+ if (y > dl[0].mid) break;
+ dl[x++] = dl[y++];
+ }
+ dl[0].mid = x-1;
+ } else {
+ /* all slots freed */
+ dl[0].mid = 0;
+ }
+ }
+ txn->mt_loose_pgs = NULL;
+ txn->mt_loose_count = 0;
+ }
+
+ /* MDB_RESERVE cancels meminit in ovpage malloc (when no WRITEMAP) */
+ clean_limit = (env->me_flags & (MDB_NOMEMINIT|MDB_WRITEMAP))
+ ? SSIZE_MAX : maxfree_1pg;
+
+ for (;;) {
+ /* Come back here after each Put() in case freelist changed */
+ MDB_val key, data;
+ pgno_t *pgs;
+ ssize_t j;
+
+ /* If using records from freeDB which we have not yet
+ * deleted, delete them and any we reserved for me_pghead.
+ */
+ while (pglast < env->me_pglast) {
+ rc = mdb_cursor_first(&mc, &key, NULL);
+ if (rc)
+ return rc;
+ pglast = head_id = *(txnid_t *)key.mv_data;
+ total_room = head_room = 0;
+ mdb_tassert(txn, pglast <= env->me_pglast);
+ rc = mdb_cursor_del(&mc, 0);
+ if (rc)
+ return rc;
+ }
+
+ /* Save the IDL of pages freed by this txn, to a single record */
+ if (freecnt < txn->mt_free_pgs[0]) {
+ if (!freecnt) {
+ /* Make sure last page of freeDB is touched and on freelist */
+ rc = mdb_page_search(&mc, NULL, MDB_PS_LAST|MDB_PS_MODIFY);
+ if (rc && rc != MDB_NOTFOUND)
+ return rc;
+ }
+ free_pgs = txn->mt_free_pgs;
+ /* Write to last page of freeDB */
+ key.mv_size = sizeof(txn->mt_txnid);
+ key.mv_data = &txn->mt_txnid;
+ do {
+ freecnt = free_pgs[0];
+ data.mv_size = MDB_IDL_SIZEOF(free_pgs);
+ rc = mdb_cursor_put(&mc, &key, &data, MDB_RESERVE);
+ if (rc)
+ return rc;
+ /* Retry if mt_free_pgs[] grew during the Put() */
+ free_pgs = txn->mt_free_pgs;
+ } while (freecnt < free_pgs[0]);
+ mdb_midl_sort(free_pgs);
+ memcpy(data.mv_data, free_pgs, data.mv_size);
+#if (MDB_DEBUG) > 1
+ {
+ unsigned int i = free_pgs[0];
+ DPRINTF(("IDL write txn %"Z"u root %"Z"u num %u",
+ txn->mt_txnid, txn->mt_dbs[FREE_DBI].md_root, i));
+ for (; i; i--)
+ DPRINTF(("IDL %"Z"u", free_pgs[i]));
+ }
+#endif
+ continue;
+ }
+
+ mop = env->me_pghead;
+ mop_len = (mop ? mop[0] : 0) + txn->mt_loose_count;
+
+ /* Reserve records for me_pghead[]. Split it if multi-page,
+ * to avoid searching freeDB for a page range. Use keys in
+ * range [1,me_pglast]: Smaller than txnid of oldest reader.
+ */
+ if (total_room >= mop_len) {
+ if (total_room == mop_len || --more < 0)
+ break;
+ } else if (head_room >= maxfree_1pg && head_id > 1) {
+ /* Keep current record (overflow page), add a new one */
+ head_id--;
+ head_room = 0;
+ }
+ /* (Re)write {key = head_id, IDL length = head_room} */
+ total_room -= head_room;
+ head_room = mop_len - total_room;
+ if (head_room > maxfree_1pg && head_id > 1) {
+ /* Overflow multi-page for part of me_pghead */
+ head_room /= head_id; /* amortize page sizes */
+ head_room += maxfree_1pg - head_room % (maxfree_1pg + 1);
+ } else if (head_room < 0) {
+ /* Rare case, not bothering to delete this record */
+ head_room = 0;
+ }
+ key.mv_size = sizeof(head_id);
+ key.mv_data = &head_id;
+ data.mv_size = (head_room + 1) * sizeof(pgno_t);
+ rc = mdb_cursor_put(&mc, &key, &data, MDB_RESERVE);
+ if (rc)
+ return rc;
+ /* IDL is initially empty, zero out at least the length */
+ pgs = (pgno_t *)data.mv_data;
+ j = head_room > clean_limit ? head_room : 0;
+ do {
+ pgs[j] = 0;
+ } while (--j >= 0);
+ total_room += head_room;
+ }
+
+ /* Return loose page numbers to me_pghead, though usually none are
+ * left at this point. The pages themselves remain in dirty_list.
+ */
+ if (txn->mt_loose_pgs) {
+ MDB_page *mp = txn->mt_loose_pgs;
+ unsigned count = txn->mt_loose_count;
+ MDB_IDL loose;
+ /* Room for loose pages + temp IDL with same */
+ if ((rc = mdb_midl_need(&env->me_pghead, 2*count+1)) != 0)
+ return rc;
+ mop = env->me_pghead;
+ loose = mop + MDB_IDL_ALLOCLEN(mop) - count;
+ for (count = 0; mp; mp = NEXT_LOOSE_PAGE(mp))
+ loose[ ++count ] = mp->mp_pgno;
+ loose[0] = count;
+ mdb_midl_sort(loose);
+ mdb_midl_xmerge(mop, loose);
+ txn->mt_loose_pgs = NULL;
+ txn->mt_loose_count = 0;
+ mop_len = mop[0];
+ }
+
+ /* Fill in the reserved me_pghead records */
+ rc = MDB_SUCCESS;
+ if (mop_len) {
+ MDB_val key, data;
+
+ mop += mop_len;
+ rc = mdb_cursor_first(&mc, &key, &data);
+ for (; !rc; rc = mdb_cursor_next(&mc, &key, &data, MDB_NEXT)) {
+ txnid_t id = *(txnid_t *)key.mv_data;
+ ssize_t len = (ssize_t)(data.mv_size / sizeof(MDB_ID)) - 1;
+ MDB_ID save;
+
+ mdb_tassert(txn, len >= 0 && id <= env->me_pglast);
+ key.mv_data = &id;
+ if (len > mop_len) {
+ len = mop_len;
+ data.mv_size = (len + 1) * sizeof(MDB_ID);
+ }
+ data.mv_data = mop -= len;
+ save = mop[0];
+ mop[0] = len;
+ rc = mdb_cursor_put(&mc, &key, &data, MDB_CURRENT);
+ mop[0] = save;
+ if (rc || !(mop_len -= len))
+ break;
+ }
+ }
+ return rc;
+}
+
+/** Flush (some) dirty pages to the map, after clearing their dirty flag.
+ * @param[in] txn the transaction that's being committed
+ * @param[in] keep number of initial pages in dirty_list to keep dirty.
+ * @return 0 on success, non-zero on failure.
+ */
+static int
+mdb_page_flush(MDB_txn *txn, int keep)
+{
+ MDB_env *env = txn->mt_env;
+ MDB_ID2L dl = txn->mt_u.dirty_list;
+ unsigned psize = env->me_psize, j;
+ int i, pagecount = dl[0].mid, rc;
+ size_t size = 0, pos = 0;
+ pgno_t pgno = 0;
+ MDB_page *dp = NULL;
+#ifdef _WIN32
+ OVERLAPPED ov;
+#else
+ struct iovec iov[MDB_COMMIT_PAGES];
+ ssize_t wpos = 0, wsize = 0, wres;
+ size_t next_pos = 1; /* impossible pos, so pos != next_pos */
+ int n = 0;
+#endif
+
+ j = i = keep;
+
+ if (env->me_flags & MDB_WRITEMAP) {
+ /* Clear dirty flags */
+ while (++i <= pagecount) {
+ dp = dl[i].mptr;
+ /* Don't flush this page yet */
+ if (dp->mp_flags & (P_LOOSE|P_KEEP)) {
+ dp->mp_flags &= ~P_KEEP;
+ dl[++j] = dl[i];
+ continue;
+ }
+ dp->mp_flags &= ~P_DIRTY;
+ }
+ goto done;
+ }
+
+ /* Write the pages */
+ for (;;) {
+ if (++i <= pagecount) {
+ dp = dl[i].mptr;
+ /* Don't flush this page yet */
+ if (dp->mp_flags & (P_LOOSE|P_KEEP)) {
+ dp->mp_flags &= ~P_KEEP;
+ dl[i].mid = 0;
+ continue;
+ }
+ pgno = dl[i].mid;
+ /* clear dirty flag */
+ dp->mp_flags &= ~P_DIRTY;
+ pos = pgno * psize;
+ size = psize;
+ if (IS_OVERFLOW(dp)) size *= dp->mp_pages;
+ }
+#ifdef _WIN32
+ else break;
+
+ /* Windows actually supports scatter/gather I/O, but only on
+ * unbuffered file handles. Since we're relying on the OS page
+ * cache for all our data, that's self-defeating. So we just
+ * write pages one at a time. We use the ov structure to set
+ * the write offset, to at least save the overhead of a Seek
+ * system call.
+ */
+ DPRINTF(("committing page %"Z"u", pgno));
+ memset(&ov, 0, sizeof(ov));
+ ov.Offset = pos & 0xffffffff;
+ ov.OffsetHigh = pos >> 16 >> 16;
+ if (!WriteFile(env->me_fd, dp, size, NULL, &ov)) {
+ rc = ErrCode();
+ DPRINTF(("WriteFile: %d", rc));
+ return rc;
+ }
+#else
+ /* Write up to MDB_COMMIT_PAGES dirty pages at a time. */
+ if (pos!=next_pos || n==MDB_COMMIT_PAGES || wsize+size>MAX_WRITE) {
+ if (n) {
+retry_write:
+ /* Write previous page(s) */
+#ifdef MDB_USE_PWRITEV
+ wres = pwritev(env->me_fd, iov, n, wpos);
+#else
+ if (n == 1) {
+ wres = pwrite(env->me_fd, iov[0].iov_base, wsize, wpos);
+ } else {
+retry_seek:
+ if (lseek(env->me_fd, wpos, SEEK_SET) == -1) {
+ rc = ErrCode();
+ if (rc == EINTR)
+ goto retry_seek;
+ DPRINTF(("lseek: %s", strerror(rc)));
+ return rc;
+ }
+ wres = writev(env->me_fd, iov, n);
+ }
+#endif
+ if (wres != wsize) {
+ if (wres < 0) {
+ rc = ErrCode();
+ if (rc == EINTR)
+ goto retry_write;
+ DPRINTF(("Write error: %s", strerror(rc)));
+ } else {
+ rc = EIO; /* TODO: Use which error code? */
+ DPUTS("short write, filesystem full?");
+ }
+ return rc;
+ }
+ n = 0;
+ }
+ if (i > pagecount)
+ break;
+ wpos = pos;
+ wsize = 0;
+ }
+ DPRINTF(("committing page %"Z"u", pgno));
+ next_pos = pos + size;
+ iov[n].iov_len = size;
+ iov[n].iov_base = (char *)dp;
+ wsize += size;
+ n++;
+#endif /* _WIN32 */
+ }
+
+ /* MIPS has cache coherency issues, this is a no-op everywhere else
+ * Note: for any size >= on-chip cache size, entire on-chip cache is
+ * flushed.
+ */
+ CACHEFLUSH(env->me_map, txn->mt_next_pgno * env->me_psize, DCACHE);
+
+ for (i = keep; ++i <= pagecount; ) {
+ dp = dl[i].mptr;
+ /* This is a page we skipped above */
+ if (!dl[i].mid) {
+ dl[++j] = dl[i];
+ dl[j].mid = dp->mp_pgno;
+ continue;
+ }
+ mdb_dpage_free(env, dp);
+ }
+
+done:
+ i--;
+ txn->mt_dirty_room += i - j;
+ dl[0].mid = j;
+ return MDB_SUCCESS;
+}
+
+int
+mdb_txn_commit(MDB_txn *txn)
+{
+ int rc;
+ unsigned int i, end_mode;
+ MDB_env *env;
+
+ if (txn == NULL)
+ return EINVAL;
+
+ /* mdb_txn_end() mode for a commit which writes nothing */
+ end_mode = MDB_END_EMPTY_COMMIT|MDB_END_UPDATE|MDB_END_SLOT|MDB_END_FREE;
+
+ if (txn->mt_child) {
+ rc = mdb_txn_commit(txn->mt_child);
+ if (rc)
+ goto fail;
+ }
+
+ env = txn->mt_env;
+
+ if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) {
+ goto done;
+ }
+
+ if (txn->mt_flags & (MDB_TXN_FINISHED|MDB_TXN_ERROR)) {
+ DPUTS("txn has failed/finished, can't commit");
+ if (txn->mt_parent)
+ txn->mt_parent->mt_flags |= MDB_TXN_ERROR;
+ rc = MDB_BAD_TXN;
+ goto fail;
+ }
+
+ if (txn->mt_parent) {
+ MDB_txn *parent = txn->mt_parent;
+ MDB_page **lp;
+ MDB_ID2L dst, src;
+ MDB_IDL pspill;
+ unsigned x, y, len, ps_len;
+
+ /* Append our free list to parent's */
+ rc = mdb_midl_append_list(&parent->mt_free_pgs, txn->mt_free_pgs);
+ if (rc)
+ goto fail;
+ mdb_midl_free(txn->mt_free_pgs);
+ /* Failures after this must either undo the changes
+ * to the parent or set MDB_TXN_ERROR in the parent.
+ */
+
+ parent->mt_next_pgno = txn->mt_next_pgno;
+ parent->mt_flags = txn->mt_flags;
+
+ /* Merge our cursors into parent's and close them */
+ mdb_cursors_close(txn, 1);
+
+ /* Update parent's DB table. */
+ memcpy(parent->mt_dbs, txn->mt_dbs, txn->mt_numdbs * sizeof(MDB_db));
+ parent->mt_numdbs = txn->mt_numdbs;
+ parent->mt_dbflags[FREE_DBI] = txn->mt_dbflags[FREE_DBI];
+ parent->mt_dbflags[MAIN_DBI] = txn->mt_dbflags[MAIN_DBI];
+ for (i=CORE_DBS; i<txn->mt_numdbs; i++) {
+ /* preserve parent's DB_NEW status */
+ x = parent->mt_dbflags[i] & DB_NEW;
+ parent->mt_dbflags[i] = txn->mt_dbflags[i] | x;
+ }
+
+ dst = parent->mt_u.dirty_list;
+ src = txn->mt_u.dirty_list;
+ /* Remove anything in our dirty list from parent's spill list */
+ if ((pspill = parent->mt_spill_pgs) && (ps_len = pspill[0])) {
+ x = y = ps_len;
+ pspill[0] = (pgno_t)-1;
+ /* Mark our dirty pages as deleted in parent spill list */
+ for (i=0, len=src[0].mid; ++i <= len; ) {
+ MDB_ID pn = src[i].mid << 1;
+ while (pn > pspill[x])
+ x--;
+ if (pn == pspill[x]) {
+ pspill[x] = 1;
+ y = --x;
+ }
+ }
+ /* Squash deleted pagenums if we deleted any */
+ for (x=y; ++x <= ps_len; )
+ if (!(pspill[x] & 1))
+ pspill[++y] = pspill[x];
+ pspill[0] = y;
+ }
+
+ /* Remove anything in our spill list from parent's dirty list */
+ if (txn->mt_spill_pgs && txn->mt_spill_pgs[0]) {
+ for (i=1; i<=txn->mt_spill_pgs[0]; i++) {
+ MDB_ID pn = txn->mt_spill_pgs[i];
+ if (pn & 1)
+ continue; /* deleted spillpg */
+ pn >>= 1;
+ y = mdb_mid2l_search(dst, pn);
+ if (y <= dst[0].mid && dst[y].mid == pn) {
+ free(dst[y].mptr);
+ while (y < dst[0].mid) {
+ dst[y] = dst[y+1];
+ y++;
+ }
+ dst[0].mid--;
+ }
+ }
+ }
+
+ /* Find len = length of merging our dirty list with parent's */
+ x = dst[0].mid;
+ dst[0].mid = 0; /* simplify loops */
+ if (parent->mt_parent) {
+ len = x + src[0].mid;
+ y = mdb_mid2l_search(src, dst[x].mid + 1) - 1;
+ for (i = x; y && i; y--) {
+ pgno_t yp = src[y].mid;
+ while (yp < dst[i].mid)
+ i--;
+ if (yp == dst[i].mid) {
+ i--;
+ len--;
+ }
+ }
+ } else { /* Simplify the above for single-ancestor case */
+ len = MDB_IDL_UM_MAX - txn->mt_dirty_room;
+ }
+ /* Merge our dirty list with parent's */
+ y = src[0].mid;
+ for (i = len; y; dst[i--] = src[y--]) {
+ pgno_t yp = src[y].mid;
+ while (yp < dst[x].mid)
+ dst[i--] = dst[x--];
+ if (yp == dst[x].mid)
+ free(dst[x--].mptr);
+ }
+ mdb_tassert(txn, i == x);
+ dst[0].mid = len;
+ free(txn->mt_u.dirty_list);
+ parent->mt_dirty_room = txn->mt_dirty_room;
+ if (txn->mt_spill_pgs) {
+ if (parent->mt_spill_pgs) {
+ /* TODO: Prevent failure here, so parent does not fail */
+ rc = mdb_midl_append_list(&parent->mt_spill_pgs, txn->mt_spill_pgs);
+ if (rc)
+ parent->mt_flags |= MDB_TXN_ERROR;
+ mdb_midl_free(txn->mt_spill_pgs);
+ mdb_midl_sort(parent->mt_spill_pgs);
+ } else {
+ parent->mt_spill_pgs = txn->mt_spill_pgs;
+ }
+ }
+
+ /* Append our loose page list to parent's */
+ for (lp = &parent->mt_loose_pgs; *lp; lp = &NEXT_LOOSE_PAGE(*lp))
+ ;
+ *lp = txn->mt_loose_pgs;
+ parent->mt_loose_count += txn->mt_loose_count;
+
+ parent->mt_child = NULL;
+ mdb_midl_free(((MDB_ntxn *)txn)->mnt_pgstate.mf_pghead);
+ free(txn);
+ return rc;
+ }
+
+ if (txn != env->me_txn) {
+ DPUTS("attempt to commit unknown transaction");
+ rc = EINVAL;
+ goto fail;
+ }
+
+ mdb_cursors_close(txn, 0);
+
+ if (!txn->mt_u.dirty_list[0].mid &&
+ !(txn->mt_flags & (MDB_TXN_DIRTY|MDB_TXN_SPILLS)))
+ goto done;
+
+ DPRINTF(("committing txn %"Z"u %p on mdbenv %p, root page %"Z"u",
+ txn->mt_txnid, (void*)txn, (void*)env, txn->mt_dbs[MAIN_DBI].md_root));
+
+ /* Update DB root pointers */
+ if (txn->mt_numdbs > CORE_DBS) {
+ MDB_cursor mc;
+ MDB_dbi i;
+ MDB_val data;
+ data.mv_size = sizeof(MDB_db);
+
+ mdb_cursor_init(&mc, txn, MAIN_DBI, NULL);
+ for (i = CORE_DBS; i < txn->mt_numdbs; i++) {
+ if (txn->mt_dbflags[i] & DB_DIRTY) {
+ if (TXN_DBI_CHANGED(txn, i)) {
+ rc = MDB_BAD_DBI;
+ goto fail;
+ }
+ data.mv_data = &txn->mt_dbs[i];
+ rc = mdb_cursor_put(&mc, &txn->mt_dbxs[i].md_name, &data,
+ F_SUBDATA);
+ if (rc)
+ goto fail;
+ }
+ }
+ }
+
+ rc = mdb_freelist_save(txn);
+ if (rc)
+ goto fail;
+
+ mdb_midl_free(env->me_pghead);
+ env->me_pghead = NULL;
+ mdb_midl_shrink(&txn->mt_free_pgs);
+
+#if (MDB_DEBUG) > 2
+ mdb_audit(txn);
+#endif
+
+ if ((rc = mdb_page_flush(txn, 0)) ||
+ (rc = mdb_env_sync(env, 0)) ||
+ (rc = mdb_env_write_meta(txn)))
+ goto fail;
+ end_mode = MDB_END_COMMITTED|MDB_END_UPDATE;
+
+done:
+ mdb_txn_end(txn, end_mode);
+ return MDB_SUCCESS;
+
+fail:
+ mdb_txn_abort(txn);
+ return rc;
+}
+
+/** Read the environment parameters of a DB environment before
+ * mapping it into memory.
+ * @param[in] env the environment handle
+ * @param[out] meta address of where to store the meta information
+ * @return 0 on success, non-zero on failure.
+ */
+static int ESECT
+mdb_env_read_header(MDB_env *env, MDB_meta *meta)
+{
+ MDB_metabuf pbuf;
+ MDB_page *p;
+ MDB_meta *m;
+ int i, rc, off;
+ enum { Size = sizeof(pbuf) };
+
+ /* We don't know the page size yet, so use a minimum value.
+ * Read both meta pages so we can use the latest one.
+ */
+
+ for (i=off=0; i<NUM_METAS; i++, off += meta->mm_psize) {
+#ifdef _WIN32
+ DWORD len;
+ OVERLAPPED ov;
+ memset(&ov, 0, sizeof(ov));
+ ov.Offset = off;
+ rc = ReadFile(env->me_fd, &pbuf, Size, &len, &ov) ? (int)len : -1;
+ if (rc == -1 && ErrCode() == ERROR_HANDLE_EOF)
+ rc = 0;
+#else
+ rc = pread(env->me_fd, &pbuf, Size, off);
+#endif
+ if (rc != Size) {
+ if (rc == 0 && off == 0)
+ return ENOENT;
+ rc = rc < 0 ? (int) ErrCode() : MDB_INVALID;
+ DPRINTF(("read: %s", mdb_strerror(rc)));
+ return rc;
+ }
+
+ p = (MDB_page *)&pbuf;
+
+ if (!F_ISSET(p->mp_flags, P_META)) {
+ DPRINTF(("page %"Z"u not a meta page", p->mp_pgno));
+ return MDB_INVALID;
+ }
+
+ m = METADATA(p);
+ if (m->mm_magic != MDB_MAGIC) {
+ DPUTS("meta has invalid magic");
+ return MDB_INVALID;
+ }
+
+ if (m->mm_version != MDB_DATA_VERSION) {
+ DPRINTF(("database is version %u, expected version %u",
+ m->mm_version, MDB_DATA_VERSION));
+ return MDB_VERSION_MISMATCH;
+ }
+
+ if (off == 0 || m->mm_txnid > meta->mm_txnid)
+ *meta = *m;
+ }
+ return 0;
+}
+
+/** Fill in most of the zeroed #MDB_meta for an empty database environment */
+static void ESECT
+mdb_env_init_meta0(MDB_env *env, MDB_meta *meta)
+{
+ meta->mm_magic = MDB_MAGIC;
+ meta->mm_version = MDB_DATA_VERSION;
+ meta->mm_mapsize = env->me_mapsize;
+ meta->mm_psize = env->me_psize;
+ meta->mm_last_pg = NUM_METAS-1;
+ meta->mm_flags = env->me_flags & 0xffff;
+ meta->mm_flags |= MDB_INTEGERKEY; /* this is mm_dbs[FREE_DBI].md_flags */
+ meta->mm_dbs[FREE_DBI].md_root = P_INVALID;
+ meta->mm_dbs[MAIN_DBI].md_root = P_INVALID;
+}
+
+/** Write the environment parameters of a freshly created DB environment.
+ * @param[in] env the environment handle
+ * @param[in] meta the #MDB_meta to write
+ * @return 0 on success, non-zero on failure.
+ */
+static int ESECT
+mdb_env_init_meta(MDB_env *env, MDB_meta *meta)
+{
+ MDB_page *p, *q;
+ int rc;
+ unsigned int psize;
+#ifdef _WIN32
+ DWORD len;
+ OVERLAPPED ov;
+ memset(&ov, 0, sizeof(ov));
+#define DO_PWRITE(rc, fd, ptr, size, len, pos) do { \
+ ov.Offset = pos; \
+ rc = WriteFile(fd, ptr, size, &len, &ov); } while(0)
+#else
+ int len;
+#define DO_PWRITE(rc, fd, ptr, size, len, pos) do { \
+ len = pwrite(fd, ptr, size, pos); \
+ if (len == -1 && ErrCode() == EINTR) continue; \
+ rc = (len >= 0); break; } while(1)
+#endif
+
+ DPUTS("writing new meta page");
+
+ psize = env->me_psize;
+
+ p = calloc(NUM_METAS, psize);
+ if (!p)
+ return ENOMEM;
+
+ p->mp_pgno = 0;
+ p->mp_flags = P_META;
+ *(MDB_meta *)METADATA(p) = *meta;
+
+ q = (MDB_page *)((char *)p + psize);
+ q->mp_pgno = 1;
+ q->mp_flags = P_META;
+ *(MDB_meta *)METADATA(q) = *meta;
+
+ DO_PWRITE(rc, env->me_fd, p, psize * NUM_METAS, len, 0);
+ if (!rc)
+ rc = ErrCode();
+ else if ((unsigned) len == psize * NUM_METAS)
+ rc = MDB_SUCCESS;
+ else
+ rc = ENOSPC;
+ free(p);
+ return rc;
+}
+
+/** Update the environment info to commit a transaction.
+ * @param[in] txn the transaction that's being committed
+ * @return 0 on success, non-zero on failure.
+ */
+static int
+mdb_env_write_meta(MDB_txn *txn)
+{
+ MDB_env *env;
+ MDB_meta meta, metab, *mp;
+ unsigned flags;
+ size_t mapsize;
+ off_t off;
+ int rc, len, toggle;
+ char *ptr;
+ HANDLE mfd;
+#ifdef _WIN32
+ OVERLAPPED ov;
+#else
+ int r2;
+#endif
+
+ toggle = txn->mt_txnid & 1;
+ DPRINTF(("writing meta page %d for root page %"Z"u",
+ toggle, txn->mt_dbs[MAIN_DBI].md_root));
+
+ env = txn->mt_env;
+ flags = env->me_flags;
+ mp = env->me_metas[toggle];
+ mapsize = env->me_metas[toggle ^ 1]->mm_mapsize;
+ /* Persist any increases of mapsize config */
+ if (mapsize < env->me_mapsize)
+ mapsize = env->me_mapsize;
+
+ if (flags & MDB_WRITEMAP) {
+ mp->mm_mapsize = mapsize;
+ mp->mm_dbs[FREE_DBI] = txn->mt_dbs[FREE_DBI];
+ mp->mm_dbs[MAIN_DBI] = txn->mt_dbs[MAIN_DBI];
+ mp->mm_last_pg = txn->mt_next_pgno - 1;
+#if (__GNUC__ * 100 + __GNUC_MINOR__ >= 404) && /* TODO: portability */ \
+ !(defined(__i386__) || defined(__x86_64__))
+ /* LY: issue a memory barrier, if not x86. ITS#7969 */
+ __sync_synchronize();
+#endif
+ mp->mm_txnid = txn->mt_txnid;
+ if (!(flags & (MDB_NOMETASYNC|MDB_NOSYNC))) {
+ unsigned meta_size = env->me_psize;
+ rc = (env->me_flags & MDB_MAPASYNC) ? MS_ASYNC : MS_SYNC;
+ ptr = (char *)mp - PAGEHDRSZ;
+#ifndef _WIN32 /* POSIX msync() requires ptr = start of OS page */
+ r2 = (ptr - env->me_map) & (env->me_os_psize - 1);
+ ptr -= r2;
+ meta_size += r2;
+#endif
+ if (MDB_MSYNC(ptr, meta_size, rc)) {
+ rc = ErrCode();
+ goto fail;
+ }
+ }
+ goto done;
+ }
+ metab.mm_txnid = mp->mm_txnid;
+ metab.mm_last_pg = mp->mm_last_pg;
+
+ meta.mm_mapsize = mapsize;
+ meta.mm_dbs[FREE_DBI] = txn->mt_dbs[FREE_DBI];
+ meta.mm_dbs[MAIN_DBI] = txn->mt_dbs[MAIN_DBI];
+ meta.mm_last_pg = txn->mt_next_pgno - 1;
+ meta.mm_txnid = txn->mt_txnid;
+
+ off = offsetof(MDB_meta, mm_mapsize);
+ ptr = (char *)&meta + off;
+ len = sizeof(MDB_meta) - off;
+ off += (char *)mp - env->me_map;
+
+ /* Write to the SYNC fd unless MDB_NOSYNC/MDB_NOMETASYNC.
+ * (me_mfd goes to the same file as me_fd, but writing to it
+ * also syncs to disk. Avoids a separate fdatasync() call.)
+ */
+ mfd = (flags & (MDB_NOSYNC|MDB_NOMETASYNC)) ? env->me_fd : env->me_mfd;
+#ifdef _WIN32
+ {
+ memset(&ov, 0, sizeof(ov));
+ ov.Offset = off;
+ if (!WriteFile(mfd, ptr, len, (DWORD *)&rc, &ov))
+ rc = -1;
+ }
+#else
+retry_write:
+ rc = pwrite(mfd, ptr, len, off);
+#endif
+ if (rc != len) {
+ rc = rc < 0 ? ErrCode() : EIO;
+#ifndef _WIN32
+ if (rc == EINTR)
+ goto retry_write;
+#endif
+ DPUTS("write failed, disk error?");
+ /* On a failure, the pagecache still contains the new data.
+ * Write some old data back, to prevent it from being used.
+ * Use the non-SYNC fd; we know it will fail anyway.
+ */
+ meta.mm_last_pg = metab.mm_last_pg;
+ meta.mm_txnid = metab.mm_txnid;
+#ifdef _WIN32
+ memset(&ov, 0, sizeof(ov));
+ ov.Offset = off;
+ WriteFile(env->me_fd, ptr, len, NULL, &ov);
+#else
+ r2 = pwrite(env->me_fd, ptr, len, off);
+ (void)r2; /* Silence warnings. We don't care about pwrite's return value */
+#endif
+fail:
+ env->me_flags |= MDB_FATAL_ERROR;
+ return rc;
+ }
+ /* MIPS has cache coherency issues, this is a no-op everywhere else */
+ CACHEFLUSH(env->me_map + off, len, DCACHE);
+done:
+ /* Memory ordering issues are irrelevant; since the entire writer
+ * is wrapped by wmutex, all of these changes will become visible
+ * after the wmutex is unlocked. Since the DB is multi-version,
+ * readers will get consistent data regardless of how fresh or
+ * how stale their view of these values is.
+ */
+ if (env->me_txns)
+ env->me_txns->mti_txnid = txn->mt_txnid;
+
+ return MDB_SUCCESS;
+}
+
+/** Check both meta pages to see which one is newer.
+ * @param[in] env the environment handle
+ * @return newest #MDB_meta.
+ */
+static MDB_meta *
+mdb_env_pick_meta(const MDB_env *env)
+{
+ MDB_meta *const *metas = env->me_metas;
+ return metas[ metas[0]->mm_txnid < metas[1]->mm_txnid ];
+}
+
+int ESECT
+mdb_env_create(MDB_env **env)
+{
+ MDB_env *e;
+
+ e = calloc(1, sizeof(MDB_env));
+ if (!e)
+ return ENOMEM;
+
+ e->me_maxreaders = DEFAULT_READERS;
+ e->me_maxdbs = e->me_numdbs = CORE_DBS;
+ e->me_fd = INVALID_HANDLE_VALUE;
+ e->me_lfd = INVALID_HANDLE_VALUE;
+ e->me_mfd = INVALID_HANDLE_VALUE;
+#ifdef MDB_USE_POSIX_SEM
+ e->me_rmutex = SEM_FAILED;
+ e->me_wmutex = SEM_FAILED;
+#endif
+ e->me_pid = getpid();
+ GET_PAGESIZE(e->me_os_psize);
+ VGMEMP_CREATE(e,0,0);
+ *env = e;
+ return MDB_SUCCESS;
+}
+
+static int ESECT
+mdb_env_map(MDB_env *env, void *addr)
+{
+ MDB_page *p;
+ unsigned int flags = env->me_flags;
+#ifdef _WIN32
+ int rc;
+ HANDLE mh;
+ LONG sizelo, sizehi;
+ size_t msize;
+
+ if (flags & MDB_RDONLY) {
+ /* Don't set explicit map size, use whatever exists */
+ msize = 0;
+ sizelo = 0;
+ sizehi = 0;
+ } else {
+ msize = env->me_mapsize;
+ sizelo = msize & 0xffffffff;
+ sizehi = msize >> 16 >> 16; /* only needed on Win64 */
+
+ /* Windows won't create mappings for zero length files.
+ * and won't map more than the file size.
+ * Just set the maxsize right now.
+ */
+ if (!(flags & MDB_WRITEMAP) && (SetFilePointer(env->me_fd, sizelo, &sizehi, 0) != (DWORD)sizelo
+ || !SetEndOfFile(env->me_fd)
+ || SetFilePointer(env->me_fd, 0, NULL, 0) != 0))
+ return ErrCode();
+ }
+
+ mh = CreateFileMapping(env->me_fd, NULL, flags & MDB_WRITEMAP ?
+ PAGE_READWRITE : PAGE_READONLY,
+ sizehi, sizelo, NULL);
+ if (!mh)
+ return ErrCode();
+ env->me_map = MapViewOfFileEx(mh, flags & MDB_WRITEMAP ?
+ FILE_MAP_WRITE : FILE_MAP_READ,
+ 0, 0, msize, addr);
+ rc = env->me_map ? 0 : ErrCode();
+ CloseHandle(mh);
+ if (rc)
+ return rc;
+#else
+ int mmap_flags = MAP_SHARED;
+ int prot = PROT_READ;
+#ifdef MAP_NOSYNC /* Used on FreeBSD */
+ if (flags & MDB_NOSYNC)
+ mmap_flags |= MAP_NOSYNC;
+#endif
+ if (flags & MDB_WRITEMAP) {
+ prot |= PROT_WRITE;
+ if (ftruncate(env->me_fd, env->me_mapsize) < 0)
+ return ErrCode();
+ }
+ env->me_map = mmap(addr, env->me_mapsize, prot, mmap_flags,
+ env->me_fd, 0);
+ if (env->me_map == MAP_FAILED) {
+ env->me_map = NULL;
+ return ErrCode();
+ }
+
+ if (flags & MDB_NORDAHEAD) {
+ /* Turn off readahead. It's harmful when the DB is larger than RAM. */
+#ifdef MADV_RANDOM
+ madvise(env->me_map, env->me_mapsize, MADV_RANDOM);
+#else
+#ifdef POSIX_MADV_RANDOM
+ posix_madvise(env->me_map, env->me_mapsize, POSIX_MADV_RANDOM);
+#endif /* POSIX_MADV_RANDOM */
+#endif /* MADV_RANDOM */
+ }
+#endif /* _WIN32 */
+
+ /* Can happen because the address argument to mmap() is just a
+ * hint. mmap() can pick another, e.g. if the range is in use.
+ * The MAP_FIXED flag would prevent that, but then mmap could
+ * instead unmap existing pages to make room for the new map.
+ */
+ if (addr && env->me_map != addr)
+ return EBUSY; /* TODO: Make a new MDB_* error code? */
+
+ p = (MDB_page *)env->me_map;
+ env->me_metas[0] = METADATA(p);
+ env->me_metas[1] = (MDB_meta *)((char *)env->me_metas[0] + env->me_psize);
+
+ return MDB_SUCCESS;
+}
+
+int ESECT
+mdb_env_set_mapsize(MDB_env *env, size_t size)
+{
+ /* If env is already open, caller is responsible for making
+ * sure there are no active txns.
+ */
+ if (env->me_map) {
+ int rc;
+ MDB_meta *meta;
+ void *old;
+ if (env->me_txn)
+ return EINVAL;
+ meta = mdb_env_pick_meta(env);
+ if (!size)
+ size = meta->mm_mapsize;
+ {
+ /* Silently round up to minimum if the size is too small */
+ size_t minsize = (meta->mm_last_pg + 1) * env->me_psize;
+ if (size < minsize)
+ size = minsize;
+ }
+ munmap(env->me_map, env->me_mapsize);
+ env->me_mapsize = size;
+ old = (env->me_flags & MDB_FIXEDMAP) ? env->me_map : NULL;
+ rc = mdb_env_map(env, old);
+ if (rc)
+ return rc;
+ }
+ env->me_mapsize = size;
+ if (env->me_psize)
+ env->me_maxpg = env->me_mapsize / env->me_psize;
+ return MDB_SUCCESS;
+}
+
+int ESECT
+mdb_env_set_maxdbs(MDB_env *env, MDB_dbi dbs)
+{
+ if (env->me_map)
+ return EINVAL;
+ env->me_maxdbs = dbs + CORE_DBS;
+ return MDB_SUCCESS;
+}
+
+int ESECT
+mdb_env_set_maxreaders(MDB_env *env, unsigned int readers)
+{
+ if (env->me_map || readers < 1)
+ return EINVAL;
+ env->me_maxreaders = readers;
+ return MDB_SUCCESS;
+}
+
+int ESECT
+mdb_env_get_maxreaders(MDB_env *env, unsigned int *readers)
+{
+ if (!env || !readers)
+ return EINVAL;
+ *readers = env->me_maxreaders;
+ return MDB_SUCCESS;
+}
+
+static int ESECT
+mdb_fsize(HANDLE fd, size_t *size)
+{
+#ifdef _WIN32
+ LARGE_INTEGER fsize;
+
+ if (!GetFileSizeEx(fd, &fsize))
+ return ErrCode();
+
+ *size = fsize.QuadPart;
+#else
+ struct stat st;
+
+ if (fstat(fd, &st))
+ return ErrCode();
+
+ *size = st.st_size;
+#endif
+ return MDB_SUCCESS;
+}
+
+
+#ifdef _WIN32
+typedef wchar_t mdb_nchar_t;
+# define MDB_NAME(str) L##str
+# define mdb_name_cpy wcscpy
+#else
+/** Character type for file names: char on Unix, wchar_t on Windows */
+typedef char mdb_nchar_t;
+# define MDB_NAME(str) str /**< #mdb_nchar_t[] string literal */
+# define mdb_name_cpy strcpy /**< Copy name (#mdb_nchar_t string) */
+#endif
+
+/** Filename - string of #mdb_nchar_t[] */
+typedef struct MDB_name {
+ int mn_len; /**< Length */
+ int mn_alloced; /**< True if #mn_val was malloced */
+ mdb_nchar_t *mn_val; /**< Contents */
+} MDB_name;
+
+/** Filename suffixes [datafile,lockfile][without,with MDB_NOSUBDIR] */
+static const mdb_nchar_t *const mdb_suffixes[2][2] = {
+ { MDB_NAME("/data.mdb"), MDB_NAME("") },
+ { MDB_NAME("/lock.mdb"), MDB_NAME("-lock") }
+};
+
+#define MDB_SUFFLEN 9 /**< Max string length in #mdb_suffixes[] */
+
+/** Set up filename + scratch area for filename suffix, for opening files.
+ * It should be freed with #mdb_fname_destroy().
+ * On Windows, paths are converted from char *UTF-8 to wchar_t *UTF-16.
+ *
+ * @param[in] path Pathname for #mdb_env_open().
+ * @param[in] envflags Whether a subdir and/or lockfile will be used.
+ * @param[out] fname Resulting filename, with room for a suffix if necessary.
+ */
+static int ESECT
+mdb_fname_init(const char *path, unsigned envflags, MDB_name *fname)
+{
+ int no_suffix = F_ISSET(envflags, MDB_NOSUBDIR|MDB_NOLOCK);
+ fname->mn_alloced = 0;
+#ifdef _WIN32
+ return utf8_to_utf16(path, fname, no_suffix ? 0 : MDB_SUFFLEN);
+#else
+ fname->mn_len = strlen(path);
+ if (no_suffix)
+ fname->mn_val = (char *) path;
+ else if ((fname->mn_val = malloc(fname->mn_len + MDB_SUFFLEN+1)) != NULL) {
+ fname->mn_alloced = 1;
+ strcpy(fname->mn_val, path);
+ }
+ else
+ return ENOMEM;
+ return MDB_SUCCESS;
+#endif
+}
+
+/** Destroy \b fname from #mdb_fname_init() */
+#define mdb_fname_destroy(fname) \
+ do { if ((fname).mn_alloced) free((fname).mn_val); } while (0)
+
+#ifdef O_CLOEXEC /* POSIX.1-2008: Set FD_CLOEXEC atomically at open() */
+# define MDB_CLOEXEC O_CLOEXEC
+#else
+# define MDB_CLOEXEC 0
+#endif
+
+/** File type, access mode etc. for #mdb_fopen() */
+enum mdb_fopen_type {
+#ifdef _WIN32
+ MDB_O_RDONLY, MDB_O_RDWR, MDB_O_META, MDB_O_COPY, MDB_O_LOCKS
+#else
+ /* A comment in mdb_fopen() explains some O_* flag choices. */
+ MDB_O_RDONLY= O_RDONLY, /**< for RDONLY me_fd */
+ MDB_O_RDWR = O_RDWR |O_CREAT, /**< for me_fd */
+ MDB_O_META = O_WRONLY|MDB_DSYNC |MDB_CLOEXEC, /**< for me_mfd */
+ MDB_O_COPY = O_WRONLY|O_CREAT|O_EXCL|MDB_CLOEXEC, /**< for #mdb_env_copy() */
+ /** Bitmask for open() flags in enum #mdb_fopen_type. The other bits
+ * distinguish otherwise-equal MDB_O_* constants from each other.
+ */
+ MDB_O_MASK = MDB_O_RDWR|MDB_CLOEXEC | MDB_O_RDONLY|MDB_O_META|MDB_O_COPY,
+ MDB_O_LOCKS = MDB_O_RDWR|MDB_CLOEXEC | ((MDB_O_MASK+1) & ~MDB_O_MASK) /**< for me_lfd */
+#endif
+};
+
+/** Open an LMDB file.
+ * @param[in] env The LMDB environment.
+ * @param[in,out] fname Path from from #mdb_fname_init(). A suffix is
+ * appended if necessary to create the filename, without changing mn_len.
+ * @param[in] which Determines file type, access mode, etc.
+ * @param[in] mode The Unix permissions for the file, if we create it.
+ * @param[out] res Resulting file handle.
+ * @return 0 on success, non-zero on failure.
+ */
+static int ESECT
+mdb_fopen(const MDB_env *env, MDB_name *fname,
+ enum mdb_fopen_type which, mdb_mode_t mode,
+ HANDLE *res)
+{
+ int rc = MDB_SUCCESS;
+ HANDLE fd;
+#ifdef _WIN32
+ DWORD acc, share, disp, attrs;
+#else
+ int flags;
+#endif
+
+ if (fname->mn_alloced) /* modifiable copy */
+ mdb_name_cpy(fname->mn_val + fname->mn_len,
+ mdb_suffixes[which==MDB_O_LOCKS][F_ISSET(env->me_flags, MDB_NOSUBDIR)]);
+
+ /* The directory must already exist. Usually the file need not.
+ * MDB_O_META requires the file because we already created it using
+ * MDB_O_RDWR. MDB_O_COPY must not overwrite an existing file.
+ *
+ * With MDB_O_COPY we do not want the OS to cache the writes, since
+ * the source data is already in the OS cache.
+ *
+ * The lockfile needs FD_CLOEXEC (close file descriptor on exec*())
+ * to avoid the flock() issues noted under Caveats in lmdb.h.
+ * Also set it for other filehandles which the user cannot get at
+ * and close himself, which he may need after fork(). I.e. all but
+ * me_fd, which programs do use via mdb_env_get_fd().
+ */
+
+#ifdef _WIN32
+ acc = GENERIC_READ|GENERIC_WRITE;
+ share = FILE_SHARE_READ|FILE_SHARE_WRITE;
+ disp = OPEN_ALWAYS;
+ attrs = FILE_ATTRIBUTE_NORMAL;
+ switch (which) {
+ case MDB_O_RDONLY: /* read-only datafile */
+ acc = GENERIC_READ;
+ disp = OPEN_EXISTING;
+ break;
+ case MDB_O_META: /* for writing metapages */
+ acc = GENERIC_WRITE;
+ disp = OPEN_EXISTING;
+ attrs = FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH;
+ break;
+ case MDB_O_COPY: /* mdb_env_copy() & co */
+ acc = GENERIC_WRITE;
+ share = 0;
+ disp = CREATE_NEW;
+ attrs = FILE_FLAG_NO_BUFFERING|FILE_FLAG_WRITE_THROUGH;
+ break;
+ default: break; /* silence gcc -Wswitch (not all enum values handled) */
+ }
+ fd = CreateFileW(fname->mn_val, acc, share, NULL, disp, attrs, NULL);
+#else
+ fd = open(fname->mn_val, which & MDB_O_MASK, mode);
+#endif
+
+ if (fd == INVALID_HANDLE_VALUE)
+ rc = ErrCode();
+#ifndef _WIN32
+ else {
+ if (which != MDB_O_RDONLY && which != MDB_O_RDWR) {
+ /* Set CLOEXEC if we could not pass it to open() */
+ if (!MDB_CLOEXEC && (flags = fcntl(fd, F_GETFD)) != -1)
+ (void) fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
+ }
+ if (which == MDB_O_COPY && env->me_psize >= env->me_os_psize) {
+ /* This may require buffer alignment. There is no portable
+ * way to ask how much, so we require OS pagesize alignment.
+ */
+# ifdef F_NOCACHE /* __APPLE__ */
+ (void) fcntl(fd, F_NOCACHE, 1);
+# elif defined O_DIRECT
+ /* open(...O_DIRECT...) would break on filesystems without
+ * O_DIRECT support (ITS#7682). Try to set it here instead.
+ */
+ if ((flags = fcntl(fd, F_GETFL)) != -1)
+ (void) fcntl(fd, F_SETFL, flags | O_DIRECT);
+# endif
+ }
+ }
+#endif /* !_WIN32 */
+
+ *res = fd;
+ return rc;
+}
+
+
+#ifdef BROKEN_FDATASYNC
+#include <sys/utsname.h>
+#include <sys/vfs.h>
+#endif
+
+/** Further setup required for opening an LMDB environment
+ */
+static int ESECT
+mdb_env_open2(MDB_env *env)
+{
+ unsigned int flags = env->me_flags;
+ int i, newenv = 0, rc;
+ MDB_meta meta;
+
+#ifdef _WIN32
+ /* See if we should use QueryLimited */
+ rc = GetVersion();
+ if ((rc & 0xff) > 5)
+ env->me_pidquery = MDB_PROCESS_QUERY_LIMITED_INFORMATION;
+ else
+ env->me_pidquery = PROCESS_QUERY_INFORMATION;
+#endif /* _WIN32 */
+
+#ifdef BROKEN_FDATASYNC
+ /* ext3/ext4 fdatasync is broken on some older Linux kernels.
+ * https://lkml.org/lkml/2012/9/3/83
+ * Kernels after 3.6-rc6 are known good.
+ * https://lkml.org/lkml/2012/9/10/556
+ * See if the DB is on ext3/ext4, then check for new enough kernel
+ * Kernels 2.6.32.60, 2.6.34.15, 3.2.30, and 3.5.4 are also known
+ * to be patched.
+ */
+ {
+ struct statfs st;
+ fstatfs(env->me_fd, &st);
+ while (st.f_type == 0xEF53) {
+ struct utsname uts;
+ int i;
+ uname(&uts);
+ if (uts.release[0] < '3') {
+ if (!strncmp(uts.release, "2.6.32.", 7)) {
+ i = atoi(uts.release+7);
+ if (i >= 60)
+ break; /* 2.6.32.60 and newer is OK */
+ } else if (!strncmp(uts.release, "2.6.34.", 7)) {
+ i = atoi(uts.release+7);
+ if (i >= 15)
+ break; /* 2.6.34.15 and newer is OK */
+ }
+ } else if (uts.release[0] == '3') {
+ i = atoi(uts.release+2);
+ if (i > 5)
+ break; /* 3.6 and newer is OK */
+ if (i == 5) {
+ i = atoi(uts.release+4);
+ if (i >= 4)
+ break; /* 3.5.4 and newer is OK */
+ } else if (i == 2) {
+ i = atoi(uts.release+4);
+ if (i >= 30)
+ break; /* 3.2.30 and newer is OK */
+ }
+ } else { /* 4.x and newer is OK */
+ break;
+ }
+ env->me_flags |= MDB_FSYNCONLY;
+ break;
+ }
+ }
+#endif
+
+ if ((i = mdb_env_read_header(env, &meta)) != 0) {
+ if (i != ENOENT)
+ return i;
+ DPUTS("new mdbenv");
+ newenv = 1;
+ env->me_psize = env->me_os_psize;
+ if (env->me_psize > MAX_PAGESIZE)
+ env->me_psize = MAX_PAGESIZE;
+ memset(&meta, 0, sizeof(meta));
+ mdb_env_init_meta0(env, &meta);
+ meta.mm_mapsize = DEFAULT_MAPSIZE;
+ } else {
+ env->me_psize = meta.mm_psize;
+ }
+
+ /* Was a mapsize configured? */
+ if (!env->me_mapsize) {
+ env->me_mapsize = meta.mm_mapsize;
+ }
+ {
+ /* Make sure mapsize >= committed data size. Even when using
+ * mm_mapsize, which could be broken in old files (ITS#7789).
+ */
+ size_t minsize = (meta.mm_last_pg + 1) * meta.mm_psize;
+ if (env->me_mapsize < minsize)
+ env->me_mapsize = minsize;
+ }
+ meta.mm_mapsize = env->me_mapsize;
+
+ if (newenv && !(flags & MDB_FIXEDMAP)) {
+ /* mdb_env_map() may grow the datafile. Write the metapages
+ * first, so the file will be valid if initialization fails.
+ * Except with FIXEDMAP, since we do not yet know mm_address.
+ * We could fill in mm_address later, but then a different
+ * program might end up doing that - one with a memory layout
+ * and map address which does not suit the main program.
+ */
+ rc = mdb_env_init_meta(env, &meta);
+ if (rc)
+ return rc;
+ newenv = 0;
+ }
+
+ rc = mdb_env_map(env, (flags & MDB_FIXEDMAP) ? meta.mm_address : NULL);
+ if (rc)
+ return rc;
+
+ if (newenv) {
+ if (flags & MDB_FIXEDMAP)
+ meta.mm_address = env->me_map;
+ i = mdb_env_init_meta(env, &meta);
+ if (i != MDB_SUCCESS) {
+ return i;
+ }
+ }
+
+ env->me_maxfree_1pg = (env->me_psize - PAGEHDRSZ) / sizeof(pgno_t) - 1;
+ env->me_nodemax = (((env->me_psize - PAGEHDRSZ) / MDB_MINKEYS) & -2)
+ - sizeof(indx_t);
+#if !(MDB_MAXKEYSIZE)
+ env->me_maxkey = env->me_nodemax - (NODESIZE + sizeof(MDB_db));
+#endif
+ env->me_maxpg = env->me_mapsize / env->me_psize;
+
+#if MDB_DEBUG
+ {
+ MDB_meta *meta = mdb_env_pick_meta(env);
+ MDB_db *db = &meta->mm_dbs[MAIN_DBI];
+
+ DPRINTF(("opened database version %u, pagesize %u",
+ meta->mm_version, env->me_psize));
+ DPRINTF(("using meta page %d", (int) (meta->mm_txnid & 1)));
+ DPRINTF(("depth: %u", db->md_depth));
+ DPRINTF(("entries: %"Z"u", db->md_entries));
+ DPRINTF(("branch pages: %"Z"u", db->md_branch_pages));
+ DPRINTF(("leaf pages: %"Z"u", db->md_leaf_pages));
+ DPRINTF(("overflow pages: %"Z"u", db->md_overflow_pages));
+ DPRINTF(("root: %"Z"u", db->md_root));
+ }
+#endif
+
+ return MDB_SUCCESS;
+}
+
+
+/** Release a reader thread's slot in the reader lock table.
+ * This function is called automatically when a thread exits.
+ * @param[in] ptr This points to the slot in the reader lock table.
+ */
+static void
+mdb_env_reader_dest(void *ptr)
+{
+ MDB_reader *reader = ptr;
+
+#ifndef _WIN32
+ if (reader->mr_pid == getpid()) /* catch pthread_exit() in child process */
+#endif
+ /* We omit the mutex, so do this atomically (i.e. skip mr_txnid) */
+ reader->mr_pid = 0;
+}
+
+#ifdef _WIN32
+/** Junk for arranging thread-specific callbacks on Windows. This is
+ * necessarily platform and compiler-specific. Windows supports up
+ * to 1088 keys. Let's assume nobody opens more than 64 environments
+ * in a single process, for now. They can override this if needed.
+ */
+#ifndef MAX_TLS_KEYS
+#define MAX_TLS_KEYS 64
+#endif
+static pthread_key_t mdb_tls_keys[MAX_TLS_KEYS];
+static int mdb_tls_nkeys;
+
+static void NTAPI mdb_tls_callback(PVOID module, DWORD reason, PVOID ptr)
+{
+ int i;
+ switch(reason) {
+ case DLL_PROCESS_ATTACH: break;
+ case DLL_THREAD_ATTACH: break;
+ case DLL_THREAD_DETACH:
+ for (i=0; i<mdb_tls_nkeys; i++) {
+ MDB_reader *r = pthread_getspecific(mdb_tls_keys[i]);
+ if (r) {
+ mdb_env_reader_dest(r);
+ }
+ }
+ break;
+ case DLL_PROCESS_DETACH: break;
+ }
+}
+#ifdef __GNUC__
+#ifdef _WIN64
+const PIMAGE_TLS_CALLBACK mdb_tls_cbp __attribute__((section (".CRT$XLB"))) = mdb_tls_callback;
+#else
+PIMAGE_TLS_CALLBACK mdb_tls_cbp __attribute__((section (".CRT$XLB"))) = mdb_tls_callback;
+#endif
+#else
+#ifdef _WIN64
+/* Force some symbol references.
+ * _tls_used forces the linker to create the TLS directory if not already done
+ * mdb_tls_cbp prevents whole-program-optimizer from dropping the symbol.
+ */
+#pragma comment(linker, "/INCLUDE:_tls_used")
+#pragma comment(linker, "/INCLUDE:mdb_tls_cbp")
+#pragma const_seg(".CRT$XLB")
+extern const PIMAGE_TLS_CALLBACK mdb_tls_cbp;
+const PIMAGE_TLS_CALLBACK mdb_tls_cbp = mdb_tls_callback;
+#pragma const_seg()
+#else /* _WIN32 */
+#pragma comment(linker, "/INCLUDE:__tls_used")
+#pragma comment(linker, "/INCLUDE:_mdb_tls_cbp")
+#pragma data_seg(".CRT$XLB")
+PIMAGE_TLS_CALLBACK mdb_tls_cbp = mdb_tls_callback;
+#pragma data_seg()
+#endif /* WIN 32/64 */
+#endif /* !__GNUC__ */
+#endif
+
+/** Downgrade the exclusive lock on the region back to shared */
+static int ESECT
+mdb_env_share_locks(MDB_env *env, int *excl)
+{
+ int rc = 0;
+ MDB_meta *meta = mdb_env_pick_meta(env);
+
+ env->me_txns->mti_txnid = meta->mm_txnid;
+
+#ifdef _WIN32
+ {
+ OVERLAPPED ov;
+ /* First acquire a shared lock. The Unlock will
+ * then release the existing exclusive lock.
+ */
+ memset(&ov, 0, sizeof(ov));
+ if (!LockFileEx(env->me_lfd, 0, 0, 1, 0, &ov)) {
+ rc = ErrCode();
+ } else {
+ UnlockFile(env->me_lfd, 0, 0, 1, 0);
+ *excl = 0;
+ }
+ }
+#else
+ {
+ struct flock lock_info;
+ /* The shared lock replaces the existing lock */
+ memset((void *)&lock_info, 0, sizeof(lock_info));
+ lock_info.l_type = F_RDLCK;
+ lock_info.l_whence = SEEK_SET;
+ lock_info.l_start = 0;
+ lock_info.l_len = 1;
+ while ((rc = fcntl(env->me_lfd, F_SETLK, &lock_info)) &&
+ (rc = ErrCode()) == EINTR) ;
+ *excl = rc ? -1 : 0; /* error may mean we lost the lock */
+ }
+#endif
+
+ return rc;
+}
+
+/** Try to get exclusive lock, otherwise shared.
+ * Maintain *excl = -1: no/unknown lock, 0: shared, 1: exclusive.
+ */
+static int ESECT
+mdb_env_excl_lock(MDB_env *env, int *excl)
+{
+ int rc = 0;
+#ifdef _WIN32
+ if (LockFile(env->me_lfd, 0, 0, 1, 0)) {
+ *excl = 1;
+ } else {
+ OVERLAPPED ov;
+ memset(&ov, 0, sizeof(ov));
+ if (LockFileEx(env->me_lfd, 0, 0, 1, 0, &ov)) {
+ *excl = 0;
+ } else {
+ rc = ErrCode();
+ }
+ }
+#else
+ struct flock lock_info;
+ memset((void *)&lock_info, 0, sizeof(lock_info));
+ lock_info.l_type = F_WRLCK;
+ lock_info.l_whence = SEEK_SET;
+ lock_info.l_start = 0;
+ lock_info.l_len = 1;
+ while ((rc = fcntl(env->me_lfd, F_SETLK, &lock_info)) &&
+ (rc = ErrCode()) == EINTR) ;
+ if (!rc) {
+ *excl = 1;
+ } else
+# ifndef MDB_USE_POSIX_MUTEX
+ if (*excl < 0) /* always true when MDB_USE_POSIX_MUTEX */
+# endif
+ {
+ lock_info.l_type = F_RDLCK;
+ while ((rc = fcntl(env->me_lfd, F_SETLKW, &lock_info)) &&
+ (rc = ErrCode()) == EINTR) ;
+ if (rc == 0)
+ *excl = 0;
+ }
+#endif
+ return rc;
+}
+
+#ifdef MDB_USE_HASH
+/*
+ * hash_64 - 64 bit Fowler/Noll/Vo-0 FNV-1a hash code
+ *
+ * @(#) $Revision: 5.1 $
+ * @(#) $Id: hash_64a.c,v 5.1 2009/06/30 09:01:38 chongo Exp $
+ * @(#) $Source: /usr/local/src/cmd/fnv/RCS/hash_64a.c,v $
+ *
+ * http://www.isthe.com/chongo/tech/comp/fnv/index.html
+ *
+ ***
+ *
+ * Please do not copyright this code. This code is in the public domain.
+ *
+ * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
+ * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * By:
+ * chongo <Landon Curt Noll> /\oo/\
+ * http://www.isthe.com/chongo/
+ *
+ * Share and Enjoy! :-)
+ */
+
+typedef unsigned long long mdb_hash_t;
+#define MDB_HASH_INIT ((mdb_hash_t)0xcbf29ce484222325ULL)
+
+/** perform a 64 bit Fowler/Noll/Vo FNV-1a hash on a buffer
+ * @param[in] val value to hash
+ * @param[in] hval initial value for hash
+ * @return 64 bit hash
+ *
+ * NOTE: To use the recommended 64 bit FNV-1a hash, use MDB_HASH_INIT as the
+ * hval arg on the first call.
+ */
+static mdb_hash_t
+mdb_hash_val(MDB_val *val, mdb_hash_t hval)
+{
+ unsigned char *s = (unsigned char *)val->mv_data; /* unsigned string */
+ unsigned char *end = s + val->mv_size;
+ /*
+ * FNV-1a hash each octet of the string
+ */
+ while (s < end) {
+ /* xor the bottom with the current octet */
+ hval ^= (mdb_hash_t)*s++;
+
+ /* multiply by the 64 bit FNV magic prime mod 2^64 */
+ hval += (hval << 1) + (hval << 4) + (hval << 5) +
+ (hval << 7) + (hval << 8) + (hval << 40);
+ }
+ /* return our new hash value */
+ return hval;
+}
+
+/** Hash the string and output the encoded hash.
+ * This uses modified RFC1924 Ascii85 encoding to accommodate systems with
+ * very short name limits. We don't care about the encoding being reversible,
+ * we just want to preserve as many bits of the input as possible in a
+ * small printable string.
+ * @param[in] str string to hash
+ * @param[out] encbuf an array of 11 chars to hold the hash
+ */
+static const char mdb_a85[]= "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
+
+static void ESECT
+mdb_pack85(unsigned long l, char *out)
+{
+ int i;
+
+ for (i=0; i<5; i++) {
+ *out++ = mdb_a85[l % 85];
+ l /= 85;
+ }
+}
+
+static void ESECT
+mdb_hash_enc(MDB_val *val, char *encbuf)
+{
+ mdb_hash_t h = mdb_hash_val(val, MDB_HASH_INIT);
+
+ mdb_pack85(h, encbuf);
+ mdb_pack85(h>>32, encbuf+5);
+ encbuf[10] = '\0';
+}
+#endif
+
+/** Open and/or initialize the lock region for the environment.
+ * @param[in] env The LMDB environment.
+ * @param[in] fname Filename + scratch area, from #mdb_fname_init().
+ * @param[in] mode The Unix permissions for the file, if we create it.
+ * @param[in,out] excl In -1, out lock type: -1 none, 0 shared, 1 exclusive
+ * @return 0 on success, non-zero on failure.
+ */
+static int ESECT
+mdb_env_setup_locks(MDB_env *env, MDB_name *fname, int mode, int *excl)
+{
+#ifdef _WIN32
+# define MDB_ERRCODE_ROFS ERROR_WRITE_PROTECT
+#else
+# define MDB_ERRCODE_ROFS EROFS
+#endif
+ int rc;
+ off_t size, rsize;
+
+ rc = mdb_fopen(env, fname, MDB_O_LOCKS, mode, &env->me_lfd);
+ if (rc) {
+ /* Omit lockfile if read-only env on read-only filesystem */
+ if (rc == MDB_ERRCODE_ROFS && (env->me_flags & MDB_RDONLY)) {
+ return MDB_SUCCESS;
+ }
+ goto fail;
+ }
+
+ if (!(env->me_flags & MDB_NOTLS)) {
+ rc = pthread_key_create(&env->me_txkey, mdb_env_reader_dest);
+ if (rc)
+ goto fail;
+ env->me_flags |= MDB_ENV_TXKEY;
+#ifdef _WIN32
+ /* Windows TLS callbacks need help finding their TLS info. */
+ if (mdb_tls_nkeys >= MAX_TLS_KEYS) {
+ rc = MDB_TLS_FULL;
+ goto fail;
+ }
+ mdb_tls_keys[mdb_tls_nkeys++] = env->me_txkey;
+#endif
+ }
+
+ /* Try to get exclusive lock. If we succeed, then
+ * nobody is using the lock region and we should initialize it.
+ */
+ if ((rc = mdb_env_excl_lock(env, excl))) goto fail;
+
+#ifdef _WIN32
+ size = GetFileSize(env->me_lfd, NULL);
+#else
+ size = lseek(env->me_lfd, 0, SEEK_END);
+ if (size == -1) goto fail_errno;
+#endif
+ rsize = (env->me_maxreaders-1) * sizeof(MDB_reader) + sizeof(MDB_txninfo);
+ if (size < rsize && *excl > 0) {
+#ifdef _WIN32
+ if (SetFilePointer(env->me_lfd, rsize, NULL, FILE_BEGIN) != (DWORD)rsize
+ || !SetEndOfFile(env->me_lfd))
+ goto fail_errno;
+#else
+ if (ftruncate(env->me_lfd, rsize) != 0) goto fail_errno;
+#endif
+ } else {
+ rsize = size;
+ size = rsize - sizeof(MDB_txninfo);
+ env->me_maxreaders = size/sizeof(MDB_reader) + 1;
+ }
+ {
+#ifdef _WIN32
+ HANDLE mh;
+ mh = CreateFileMapping(env->me_lfd, NULL, PAGE_READWRITE,
+ 0, 0, NULL);
+ if (!mh) goto fail_errno;
+ env->me_txns = MapViewOfFileEx(mh, FILE_MAP_WRITE, 0, 0, rsize, NULL);
+ CloseHandle(mh);
+ if (!env->me_txns) goto fail_errno;
+#else
+ void *m = mmap(NULL, rsize, PROT_READ|PROT_WRITE, MAP_SHARED,
+ env->me_lfd, 0);
+ if (m == MAP_FAILED) goto fail_errno;
+ env->me_txns = m;
+#endif
+ }
+ if (*excl > 0) {
+#ifdef _WIN32
+ BY_HANDLE_FILE_INFORMATION stbuf;
+ struct {
+ DWORD volume;
+ DWORD nhigh;
+ DWORD nlow;
+ } idbuf;
+ MDB_val val;
+ char encbuf[11];
+
+ if (!mdb_sec_inited) {
+ InitializeSecurityDescriptor(&mdb_null_sd,
+ SECURITY_DESCRIPTOR_REVISION);
+ SetSecurityDescriptorDacl(&mdb_null_sd, TRUE, 0, FALSE);
+ mdb_all_sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ mdb_all_sa.bInheritHandle = FALSE;
+ mdb_all_sa.lpSecurityDescriptor = &mdb_null_sd;
+ mdb_sec_inited = 1;
+ }
+ if (!GetFileInformationByHandle(env->me_lfd, &stbuf)) goto fail_errno;
+ idbuf.volume = stbuf.dwVolumeSerialNumber;
+ idbuf.nhigh = stbuf.nFileIndexHigh;
+ idbuf.nlow = stbuf.nFileIndexLow;
+ val.mv_data = &idbuf;
+ val.mv_size = sizeof(idbuf);
+ mdb_hash_enc(&val, encbuf);
+ sprintf(env->me_txns->mti_rmname, "Global\\MDBr%s", encbuf);
+ sprintf(env->me_txns->mti_wmname, "Global\\MDBw%s", encbuf);
+ env->me_rmutex = CreateMutexA(&mdb_all_sa, FALSE, env->me_txns->mti_rmname);
+ if (!env->me_rmutex) goto fail_errno;
+ env->me_wmutex = CreateMutexA(&mdb_all_sa, FALSE, env->me_txns->mti_wmname);
+ if (!env->me_wmutex) goto fail_errno;
+#elif defined(MDB_USE_POSIX_SEM)
+ struct stat stbuf;
+ struct {
+ dev_t dev;
+ ino_t ino;
+ } idbuf;
+ MDB_val val;
+ char encbuf[11];
+
+#if defined(__NetBSD__)
+#define MDB_SHORT_SEMNAMES 1 /* limited to 14 chars */
+#endif
+ if (fstat(env->me_lfd, &stbuf)) goto fail_errno;
+ idbuf.dev = stbuf.st_dev;
+ idbuf.ino = stbuf.st_ino;
+ val.mv_data = &idbuf;
+ val.mv_size = sizeof(idbuf);
+ mdb_hash_enc(&val, encbuf);
+#ifdef MDB_SHORT_SEMNAMES
+ encbuf[9] = '\0'; /* drop name from 15 chars to 14 chars */
+#endif
+ sprintf(env->me_txns->mti_rmname, "/MDBr%s", encbuf);
+ sprintf(env->me_txns->mti_wmname, "/MDBw%s", encbuf);
+ /* Clean up after a previous run, if needed: Try to
+ * remove both semaphores before doing anything else.
+ */
+ sem_unlink(env->me_txns->mti_rmname);
+ sem_unlink(env->me_txns->mti_wmname);
+ env->me_rmutex = sem_open(env->me_txns->mti_rmname,
+ O_CREAT|O_EXCL, mode, 1);
+ if (env->me_rmutex == SEM_FAILED) goto fail_errno;
+ env->me_wmutex = sem_open(env->me_txns->mti_wmname,
+ O_CREAT|O_EXCL, mode, 1);
+ if (env->me_wmutex == SEM_FAILED) goto fail_errno;
+#else /* MDB_USE_POSIX_MUTEX: */
+ pthread_mutexattr_t mattr;
+
+ /* Solaris needs this before initing a robust mutex. Otherwise
+ * it may skip the init and return EBUSY "seems someone already
+ * inited" or EINVAL "it was inited differently".
+ */
+ memset(env->me_txns->mti_rmutex, 0, sizeof(*env->me_txns->mti_rmutex));
+ memset(env->me_txns->mti_wmutex, 0, sizeof(*env->me_txns->mti_wmutex));
+
+ if ((rc = pthread_mutexattr_init(&mattr)))
+ goto fail;
+
+ rc = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
+#ifdef MDB_ROBUST_SUPPORTED
+ if (!rc) rc = pthread_mutexattr_setrobust(&mattr, PTHREAD_MUTEX_ROBUST);
+#endif
+ if (!rc) rc = pthread_mutex_init(env->me_txns->mti_rmutex, &mattr);
+ if (!rc) rc = pthread_mutex_init(env->me_txns->mti_wmutex, &mattr);
+ pthread_mutexattr_destroy(&mattr);
+ if (rc)
+ goto fail;
+#endif /* _WIN32 || MDB_USE_POSIX_SEM */
+
+ env->me_txns->mti_magic = MDB_MAGIC;
+ env->me_txns->mti_format = MDB_LOCK_FORMAT;
+ env->me_txns->mti_txnid = 0;
+ env->me_txns->mti_numreaders = 0;
+
+ } else {
+ if (env->me_txns->mti_magic != MDB_MAGIC) {
+ DPUTS("lock region has invalid magic");
+ rc = MDB_INVALID;
+ goto fail;
+ }
+ if (env->me_txns->mti_format != MDB_LOCK_FORMAT) {
+ DPRINTF(("lock region has format+version 0x%x, expected 0x%x",
+ env->me_txns->mti_format, MDB_LOCK_FORMAT));
+ rc = MDB_VERSION_MISMATCH;
+ goto fail;
+ }
+ rc = ErrCode();
+ if (rc && rc != EACCES && rc != EAGAIN) {
+ goto fail;
+ }
+#ifdef _WIN32
+ env->me_rmutex = OpenMutexA(SYNCHRONIZE, FALSE, env->me_txns->mti_rmname);
+ if (!env->me_rmutex) goto fail_errno;
+ env->me_wmutex = OpenMutexA(SYNCHRONIZE, FALSE, env->me_txns->mti_wmname);
+ if (!env->me_wmutex) goto fail_errno;
+#elif defined(MDB_USE_POSIX_SEM)
+ env->me_rmutex = sem_open(env->me_txns->mti_rmname, 0);
+ if (env->me_rmutex == SEM_FAILED) goto fail_errno;
+ env->me_wmutex = sem_open(env->me_txns->mti_wmname, 0);
+ if (env->me_wmutex == SEM_FAILED) goto fail_errno;
+#endif
+ }
+ return MDB_SUCCESS;
+
+fail_errno:
+ rc = ErrCode();
+fail:
+ return rc;
+}
+
+ /** Only a subset of the @ref mdb_env flags can be changed
+ * at runtime. Changing other flags requires closing the
+ * environment and re-opening it with the new flags.
+ */
+#define CHANGEABLE (MDB_NOSYNC|MDB_NOMETASYNC|MDB_MAPASYNC|MDB_NOMEMINIT)
+#define CHANGELESS (MDB_FIXEDMAP|MDB_NOSUBDIR|MDB_RDONLY| \
+ MDB_WRITEMAP|MDB_NOTLS|MDB_NOLOCK|MDB_NORDAHEAD)
+
+#if VALID_FLAGS & PERSISTENT_FLAGS & (CHANGEABLE|CHANGELESS)
+# error "Persistent DB flags & env flags overlap, but both go in mm_flags"
+#endif
+
+int ESECT
+mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode)
+{
+ int rc, excl = -1;
+ MDB_name fname;
+
+ if (env->me_fd!=INVALID_HANDLE_VALUE || (flags & ~(CHANGEABLE|CHANGELESS)))
+ return EINVAL;
+
+ flags |= env->me_flags;
+
+ rc = mdb_fname_init(path, flags, &fname);
+ if (rc)
+ return rc;
+
+ if (flags & MDB_RDONLY) {
+ /* silently ignore WRITEMAP when we're only getting read access */
+ flags &= ~MDB_WRITEMAP;
+ } else {
+ if (!((env->me_free_pgs = mdb_midl_alloc(MDB_IDL_UM_MAX)) &&
+ (env->me_dirty_list = calloc(MDB_IDL_UM_SIZE, sizeof(MDB_ID2)))))
+ rc = ENOMEM;
+ }
+ env->me_flags = flags |= MDB_ENV_ACTIVE;
+ if (rc)
+ goto leave;
+
+ env->me_path = strdup(path);
+ env->me_dbxs = calloc(env->me_maxdbs, sizeof(MDB_dbx));
+ env->me_dbflags = calloc(env->me_maxdbs, sizeof(uint16_t));
+ env->me_dbiseqs = calloc(env->me_maxdbs, sizeof(unsigned int));
+ if (!(env->me_dbxs && env->me_path && env->me_dbflags && env->me_dbiseqs)) {
+ rc = ENOMEM;
+ goto leave;
+ }
+ env->me_dbxs[FREE_DBI].md_cmp = mdb_cmp_long; /* aligned MDB_INTEGERKEY */
+
+ /* For RDONLY, get lockfile after we know datafile exists */
+ if (!(flags & (MDB_RDONLY|MDB_NOLOCK))) {
+ rc = mdb_env_setup_locks(env, &fname, mode, &excl);
+ if (rc)
+ goto leave;
+ }
+
+ rc = mdb_fopen(env, &fname,
+ (flags & MDB_RDONLY) ? MDB_O_RDONLY : MDB_O_RDWR,
+ mode, &env->me_fd);
+ if (rc)
+ goto leave;
+
+ if ((flags & (MDB_RDONLY|MDB_NOLOCK)) == MDB_RDONLY) {
+ rc = mdb_env_setup_locks(env, &fname, mode, &excl);
+ if (rc)
+ goto leave;
+ }
+
+ if ((rc = mdb_env_open2(env)) == MDB_SUCCESS) {
+ if (!(flags & (MDB_RDONLY|MDB_WRITEMAP))) {
+ /* Synchronous fd for meta writes. Needed even with
+ * MDB_NOSYNC/MDB_NOMETASYNC, in case these get reset.
+ */
+ rc = mdb_fopen(env, &fname, MDB_O_META, mode, &env->me_mfd);
+ if (rc)
+ goto leave;
+ }
+ DPRINTF(("opened dbenv %p", (void *) env));
+ if (excl > 0) {
+ rc = mdb_env_share_locks(env, &excl);
+ if (rc)
+ goto leave;
+ }
+ if (!(flags & MDB_RDONLY)) {
+ MDB_txn *txn;
+ int tsize = sizeof(MDB_txn), size = tsize + env->me_maxdbs *
+ (sizeof(MDB_db)+sizeof(MDB_cursor *)+sizeof(unsigned int)+1);
+ if ((env->me_pbuf = calloc(1, env->me_psize)) &&
+ (txn = calloc(1, size)))
+ {
+ txn->mt_dbs = (MDB_db *)((char *)txn + tsize);
+ txn->mt_cursors = (MDB_cursor **)(txn->mt_dbs + env->me_maxdbs);
+ txn->mt_dbiseqs = (unsigned int *)(txn->mt_cursors + env->me_maxdbs);
+ txn->mt_dbflags = (unsigned char *)(txn->mt_dbiseqs + env->me_maxdbs);
+ txn->mt_env = env;
+ txn->mt_dbxs = env->me_dbxs;
+ txn->mt_flags = MDB_TXN_FINISHED;
+ env->me_txn0 = txn;
+ } else {
+ rc = ENOMEM;
+ }
+ }
+ }
+
+leave:
+ if (rc) {
+ mdb_env_close0(env, excl);
+ }
+ mdb_fname_destroy(fname);
+ return rc;
+}
+
+/** Destroy resources from mdb_env_open(), clear our readers & DBIs */
+static void ESECT
+mdb_env_close0(MDB_env *env, int excl)
+{
+ int i;
+
+ if (!(env->me_flags & MDB_ENV_ACTIVE))
+ return;
+
+ /* Doing this here since me_dbxs may not exist during mdb_env_close */
+ if (env->me_dbxs) {
+ for (i = env->me_maxdbs; --i >= CORE_DBS; )
+ free(env->me_dbxs[i].md_name.mv_data);
+ free(env->me_dbxs);
+ }
+
+ free(env->me_pbuf);
+ free(env->me_dbiseqs);
+ free(env->me_dbflags);
+ free(env->me_path);
+ free(env->me_dirty_list);
+ free(env->me_txn0);
+ mdb_midl_free(env->me_free_pgs);
+
+ if (env->me_flags & MDB_ENV_TXKEY) {
+ pthread_key_delete(env->me_txkey);
+#ifdef _WIN32
+ /* Delete our key from the global list */
+ for (i=0; i<mdb_tls_nkeys; i++)
+ if (mdb_tls_keys[i] == env->me_txkey) {
+ mdb_tls_keys[i] = mdb_tls_keys[mdb_tls_nkeys-1];
+ mdb_tls_nkeys--;
+ break;
+ }
+#endif
+ }
+
+ if (env->me_map) {
+ munmap(env->me_map, env->me_mapsize);
+ }
+ if (env->me_mfd != INVALID_HANDLE_VALUE)
+ (void) close(env->me_mfd);
+ if (env->me_fd != INVALID_HANDLE_VALUE)
+ (void) close(env->me_fd);
+ if (env->me_txns) {
+ MDB_PID_T pid = getpid();
+ /* Clearing readers is done in this function because
+ * me_txkey with its destructor must be disabled first.
+ *
+ * We skip the the reader mutex, so we touch only
+ * data owned by this process (me_close_readers and
+ * our readers), and clear each reader atomically.
+ */
+ for (i = env->me_close_readers; --i >= 0; )
+ if (env->me_txns->mti_readers[i].mr_pid == pid)
+ env->me_txns->mti_readers[i].mr_pid = 0;
+#ifdef _WIN32
+ if (env->me_rmutex) {
+ CloseHandle(env->me_rmutex);
+ if (env->me_wmutex) CloseHandle(env->me_wmutex);
+ }
+ /* Windows automatically destroys the mutexes when
+ * the last handle closes.
+ */
+#elif defined(MDB_USE_POSIX_SEM)
+ if (env->me_rmutex != SEM_FAILED) {
+ sem_close(env->me_rmutex);
+ if (env->me_wmutex != SEM_FAILED)
+ sem_close(env->me_wmutex);
+ /* If we have the filelock: If we are the
+ * only remaining user, clean up semaphores.
+ */
+ if (excl == 0)
+ mdb_env_excl_lock(env, &excl);
+ if (excl > 0) {
+ sem_unlink(env->me_txns->mti_rmname);
+ sem_unlink(env->me_txns->mti_wmname);
+ }
+ }
+#elif defined(MDB_ROBUST_SUPPORTED)
+ /* If we have the filelock: If we are the
+ * only remaining user, clean up robust
+ * mutexes.
+ */
+ if (excl == 0)
+ mdb_env_excl_lock(env, &excl);
+ if (excl > 0) {
+ pthread_mutex_destroy(env->me_txns->mti_rmutex);
+ pthread_mutex_destroy(env->me_txns->mti_wmutex);
+ }
+#endif
+ munmap((void *)env->me_txns, (env->me_maxreaders-1)*sizeof(MDB_reader)+sizeof(MDB_txninfo));
+ }
+ if (env->me_lfd != INVALID_HANDLE_VALUE) {
+#ifdef _WIN32
+ if (excl >= 0) {
+ /* Unlock the lockfile. Windows would have unlocked it
+ * after closing anyway, but not necessarily at once.
+ */
+ UnlockFile(env->me_lfd, 0, 0, 1, 0);
+ }
+#endif
+ (void) close(env->me_lfd);
+ }
+
+ env->me_flags &= ~(MDB_ENV_ACTIVE|MDB_ENV_TXKEY);
+}
+
+void ESECT
+mdb_env_close(MDB_env *env)
+{
+ MDB_page *dp;
+
+ if (env == NULL)
+ return;
+
+ VGMEMP_DESTROY(env);
+ while ((dp = env->me_dpages) != NULL) {
+ VGMEMP_DEFINED(&dp->mp_next, sizeof(dp->mp_next));
+ env->me_dpages = dp->mp_next;
+ free(dp);
+ }
+
+ mdb_env_close0(env, 0);
+ free(env);
+}
+
+/** Compare two items pointing at aligned size_t's */
+static int
+mdb_cmp_long(const MDB_val *a, const MDB_val *b)
+{
+ return (*(size_t *)a->mv_data < *(size_t *)b->mv_data) ? -1 :
+ *(size_t *)a->mv_data > *(size_t *)b->mv_data;
+}
+
+/** Compare two items pointing at aligned unsigned int's.
+ *
+ * This is also set as #MDB_INTEGERDUP|#MDB_DUPFIXED's #MDB_dbx.%md_dcmp,
+ * but #mdb_cmp_clong() is called instead if the data type is size_t.
+ */
+static int
+mdb_cmp_int(const MDB_val *a, const MDB_val *b)
+{
+ return (*(unsigned int *)a->mv_data < *(unsigned int *)b->mv_data) ? -1 :
+ *(unsigned int *)a->mv_data > *(unsigned int *)b->mv_data;
+}
+
+/** Compare two items pointing at unsigned ints of unknown alignment.
+ * Nodes and keys are guaranteed to be 2-byte aligned.
+ */
+static int
+mdb_cmp_cint(const MDB_val *a, const MDB_val *b)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+ unsigned short *u, *c;
+ int x;
+
+ u = (unsigned short *) ((char *) a->mv_data + a->mv_size);
+ c = (unsigned short *) ((char *) b->mv_data + a->mv_size);
+ do {
+ x = *--u - *--c;
+ } while(!x && u > (unsigned short *)a->mv_data);
+ return x;
+#else
+ unsigned short *u, *c, *end;
+ int x;
+
+ end = (unsigned short *) ((char *) a->mv_data + a->mv_size);
+ u = (unsigned short *)a->mv_data;
+ c = (unsigned short *)b->mv_data;
+ do {
+ x = *u++ - *c++;
+ } while(!x && u < end);
+ return x;
+#endif
+}
+
+/** Compare two items lexically */
+static int
+mdb_cmp_memn(const MDB_val *a, const MDB_val *b)
+{
+ int diff;
+ ssize_t len_diff;
+ unsigned int len;
+
+ len = a->mv_size;
+ len_diff = (ssize_t) a->mv_size - (ssize_t) b->mv_size;
+ if (len_diff > 0) {
+ len = b->mv_size;
+ len_diff = 1;
+ }
+
+ diff = memcmp(a->mv_data, b->mv_data, len);
+ return diff ? diff : len_diff<0 ? -1 : len_diff;
+}
+
+/** Compare two items in reverse byte order */
+static int
+mdb_cmp_memnr(const MDB_val *a, const MDB_val *b)
+{
+ const unsigned char *p1, *p2, *p1_lim;
+ ssize_t len_diff;
+ int diff;
+
+ p1_lim = (const unsigned char *)a->mv_data;
+ p1 = (const unsigned char *)a->mv_data + a->mv_size;
+ p2 = (const unsigned char *)b->mv_data + b->mv_size;
+
+ len_diff = (ssize_t) a->mv_size - (ssize_t) b->mv_size;
+ if (len_diff > 0) {
+ p1_lim += len_diff;
+ len_diff = 1;
+ }
+
+ while (p1 > p1_lim) {
+ diff = *--p1 - *--p2;
+ if (diff)
+ return diff;
+ }
+ return len_diff<0 ? -1 : len_diff;
+}
+
+/** Search for key within a page, using binary search.
+ * Returns the smallest entry larger or equal to the key.
+ * If exactp is non-null, stores whether the found entry was an exact match
+ * in *exactp (1 or 0).
+ * Updates the cursor index with the index of the found entry.
+ * If no entry larger or equal to the key is found, returns NULL.
+ */
+static MDB_node *
+mdb_node_search(MDB_cursor *mc, MDB_val *key, int *exactp)
+{
+ unsigned int i = 0, nkeys;
+ int low, high;
+ int rc = 0;
+ MDB_page *mp = mc->mc_pg[mc->mc_top];
+ MDB_node *node = NULL;
+ MDB_val nodekey;
+ MDB_cmp_func *cmp;
+ DKBUF;
+
+ nkeys = NUMKEYS(mp);
+
+ DPRINTF(("searching %u keys in %s %spage %"Z"u",
+ nkeys, IS_LEAF(mp) ? "leaf" : "branch", IS_SUBP(mp) ? "sub-" : "",
+ mdb_dbg_pgno(mp)));
+
+ low = IS_LEAF(mp) ? 0 : 1;
+ high = nkeys - 1;
+ cmp = mc->mc_dbx->md_cmp;
+
+ /* Branch pages have no data, so if using integer keys,
+ * alignment is guaranteed. Use faster mdb_cmp_int.
+ */
+ if (cmp == mdb_cmp_cint && IS_BRANCH(mp)) {
+ if (NODEPTR(mp, 1)->mn_ksize == sizeof(size_t))
+ cmp = mdb_cmp_long;
+ else
+ cmp = mdb_cmp_int;
+ }
+
+ if (IS_LEAF2(mp)) {
+ nodekey.mv_size = mc->mc_db->md_pad;
+ node = NODEPTR(mp, 0); /* fake */
+ while (low <= high) {
+ i = (low + high) >> 1;
+ nodekey.mv_data = LEAF2KEY(mp, i, nodekey.mv_size);
+ rc = cmp(key, &nodekey);
+ DPRINTF(("found leaf index %u [%s], rc = %i",
+ i, DKEY(&nodekey), rc));
+ if (rc == 0)
+ break;
+ if (rc > 0)
+ low = i + 1;
+ else
+ high = i - 1;
+ }
+ } else {
+ while (low <= high) {
+ i = (low + high) >> 1;
+
+ node = NODEPTR(mp, i);
+ nodekey.mv_size = NODEKSZ(node);
+ nodekey.mv_data = NODEKEY(node);
+
+ rc = cmp(key, &nodekey);
+#if MDB_DEBUG
+ if (IS_LEAF(mp))
+ DPRINTF(("found leaf index %u [%s], rc = %i",
+ i, DKEY(&nodekey), rc));
+ else
+ DPRINTF(("found branch index %u [%s -> %"Z"u], rc = %i",
+ i, DKEY(&nodekey), NODEPGNO(node), rc));
+#endif
+ if (rc == 0)
+ break;
+ if (rc > 0)
+ low = i + 1;
+ else
+ high = i - 1;
+ }
+ }
+
+ if (rc > 0) { /* Found entry is less than the key. */
+ i++; /* Skip to get the smallest entry larger than key. */
+ if (!IS_LEAF2(mp))
+ node = NODEPTR(mp, i);
+ }
+ if (exactp)
+ *exactp = (rc == 0 && nkeys > 0);
+ /* store the key index */
+ mc->mc_ki[mc->mc_top] = i;
+ if (i >= nkeys)
+ /* There is no entry larger or equal to the key. */
+ return NULL;
+
+ /* nodeptr is fake for LEAF2 */
+ return node;
+}
+
+#if 0
+static void
+mdb_cursor_adjust(MDB_cursor *mc, func)
+{
+ MDB_cursor *m2;
+
+ for (m2 = mc->mc_txn->mt_cursors[mc->mc_dbi]; m2; m2=m2->mc_next) {
+ if (m2->mc_pg[m2->mc_top] == mc->mc_pg[mc->mc_top]) {
+ func(mc, m2);
+ }
+ }
+}
+#endif
+
+/** Pop a page off the top of the cursor's stack. */
+static void
+mdb_cursor_pop(MDB_cursor *mc)
+{
+ if (mc->mc_snum) {
+ DPRINTF(("popping page %"Z"u off db %d cursor %p",
+ mc->mc_pg[mc->mc_top]->mp_pgno, DDBI(mc), (void *) mc));
+
+ mc->mc_snum--;
+ if (mc->mc_snum) {
+ mc->mc_top--;
+ } else {
+ mc->mc_flags &= ~C_INITIALIZED;
+ }
+ }
+}
+
+/** Push a page onto the top of the cursor's stack.
+ * Set #MDB_TXN_ERROR on failure.
+ */
+static int
+mdb_cursor_push(MDB_cursor *mc, MDB_page *mp)
+{
+ DPRINTF(("pushing page %"Z"u on db %d cursor %p", mp->mp_pgno,
+ DDBI(mc), (void *) mc));
+
+ if (mc->mc_snum >= CURSOR_STACK) {
+ mc->mc_txn->mt_flags |= MDB_TXN_ERROR;
+ return MDB_CURSOR_FULL;
+ }
+
+ mc->mc_top = mc->mc_snum++;
+ mc->mc_pg[mc->mc_top] = mp;
+ mc->mc_ki[mc->mc_top] = 0;
+
+ return MDB_SUCCESS;
+}
+
+/** Find the address of the page corresponding to a given page number.
+ * Set #MDB_TXN_ERROR on failure.
+ * @param[in] mc the cursor accessing the page.
+ * @param[in] pgno the page number for the page to retrieve.
+ * @param[out] ret address of a pointer where the page's address will be stored.
+ * @param[out] lvl dirty_list inheritance level of found page. 1=current txn, 0=mapped page.
+ * @return 0 on success, non-zero on failure.
+ */
+static int
+mdb_page_get(MDB_cursor *mc, pgno_t pgno, MDB_page **ret, int *lvl)
+{
+ MDB_txn *txn = mc->mc_txn;
+ MDB_env *env = txn->mt_env;
+ MDB_page *p = NULL;
+ int level;
+
+ if (! (txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_WRITEMAP))) {
+ MDB_txn *tx2 = txn;
+ level = 1;
+ do {
+ MDB_ID2L dl = tx2->mt_u.dirty_list;
+ unsigned x;
+ /* Spilled pages were dirtied in this txn and flushed
+ * because the dirty list got full. Bring this page
+ * back in from the map (but don't unspill it here,
+ * leave that unless page_touch happens again).
+ */
+ if (tx2->mt_spill_pgs) {
+ MDB_ID pn = pgno << 1;
+ x = mdb_midl_search(tx2->mt_spill_pgs, pn);
+ if (x <= tx2->mt_spill_pgs[0] && tx2->mt_spill_pgs[x] == pn) {
+ p = (MDB_page *)(env->me_map + env->me_psize * pgno);
+ goto done;
+ }
+ }
+ if (dl[0].mid) {
+ unsigned x = mdb_mid2l_search(dl, pgno);
+ if (x <= dl[0].mid && dl[x].mid == pgno) {
+ p = dl[x].mptr;
+ goto done;
+ }
+ }
+ level++;
+ } while ((tx2 = tx2->mt_parent) != NULL);
+ }
+
+ if (pgno < txn->mt_next_pgno) {
+ level = 0;
+ p = (MDB_page *)(env->me_map + env->me_psize * pgno);
+ } else {
+ DPRINTF(("page %"Z"u not found", pgno));
+ txn->mt_flags |= MDB_TXN_ERROR;
+ return MDB_PAGE_NOTFOUND;
+ }
+
+done:
+ *ret = p;
+ if (lvl)
+ *lvl = level;
+ return MDB_SUCCESS;
+}
+
+/** Finish #mdb_page_search() / #mdb_page_search_lowest().
+ * The cursor is at the root page, set up the rest of it.
+ */
+static int
+mdb_page_search_root(MDB_cursor *mc, MDB_val *key, int flags)
+{
+ MDB_page *mp = mc->mc_pg[mc->mc_top];
+ int rc;
+ DKBUF;
+
+ while (IS_BRANCH(mp)) {
+ MDB_node *node;
+ indx_t i;
+
+ DPRINTF(("branch page %"Z"u has %u keys", mp->mp_pgno, NUMKEYS(mp)));
+ /* Don't assert on branch pages in the FreeDB. We can get here
+ * while in the process of rebalancing a FreeDB branch page; we must
+ * let that proceed. ITS#8336
+ */
+ mdb_cassert(mc, !mc->mc_dbi || NUMKEYS(mp) > 1);
+ DPRINTF(("found index 0 to page %"Z"u", NODEPGNO(NODEPTR(mp, 0))));
+
+ if (flags & (MDB_PS_FIRST|MDB_PS_LAST)) {
+ i = 0;
+ if (flags & MDB_PS_LAST) {
+ i = NUMKEYS(mp) - 1;
+ /* if already init'd, see if we're already in right place */
+ if (mc->mc_flags & C_INITIALIZED) {
+ if (mc->mc_ki[mc->mc_top] == i) {
+ mc->mc_top = mc->mc_snum++;
+ mp = mc->mc_pg[mc->mc_top];
+ goto ready;
+ }
+ }
+ }
+ } else {
+ int exact;
+ node = mdb_node_search(mc, key, &exact);
+ if (node == NULL)
+ i = NUMKEYS(mp) - 1;
+ else {
+ i = mc->mc_ki[mc->mc_top];
+ if (!exact) {
+ mdb_cassert(mc, i > 0);
+ i--;
+ }
+ }
+ DPRINTF(("following index %u for key [%s]", i, DKEY(key)));
+ }
+
+ mdb_cassert(mc, i < NUMKEYS(mp));
+ node = NODEPTR(mp, i);
+
+ if ((rc = mdb_page_get(mc, NODEPGNO(node), &mp, NULL)) != 0)
+ return rc;
+
+ mc->mc_ki[mc->mc_top] = i;
+ if ((rc = mdb_cursor_push(mc, mp)))
+ return rc;
+
+ready:
+ if (flags & MDB_PS_MODIFY) {
+ if ((rc = mdb_page_touch(mc)) != 0)
+ return rc;
+ mp = mc->mc_pg[mc->mc_top];
+ }
+ }
+
+ if (!IS_LEAF(mp)) {
+ DPRINTF(("internal error, index points to a %02X page!?",
+ mp->mp_flags));
+ mc->mc_txn->mt_flags |= MDB_TXN_ERROR;
+ return MDB_CORRUPTED;
+ }
+
+ DPRINTF(("found leaf page %"Z"u for key [%s]", mp->mp_pgno,
+ key ? DKEY(key) : "null"));
+ mc->mc_flags |= C_INITIALIZED;
+ mc->mc_flags &= ~C_EOF;
+
+ return MDB_SUCCESS;
+}
+
+/** Search for the lowest key under the current branch page.
+ * This just bypasses a NUMKEYS check in the current page
+ * before calling mdb_page_search_root(), because the callers
+ * are all in situations where the current page is known to
+ * be underfilled.
+ */
+static int
+mdb_page_search_lowest(MDB_cursor *mc)
+{
+ MDB_page *mp = mc->mc_pg[mc->mc_top];
+ MDB_node *node = NODEPTR(mp, 0);
+ int rc;
+
+ if ((rc = mdb_page_get(mc, NODEPGNO(node), &mp, NULL)) != 0)
+ return rc;
+
+ mc->mc_ki[mc->mc_top] = 0;
+ if ((rc = mdb_cursor_push(mc, mp)))
+ return rc;
+ return mdb_page_search_root(mc, NULL, MDB_PS_FIRST);
+}
+
+/** Search for the page a given key should be in.
+ * Push it and its parent pages on the cursor stack.
+ * @param[in,out] mc the cursor for this operation.
+ * @param[in] key the key to search for, or NULL for first/last page.
+ * @param[in] flags If MDB_PS_MODIFY is set, visited pages in the DB
+ * are touched (updated with new page numbers).
+ * If MDB_PS_FIRST or MDB_PS_LAST is set, find first or last leaf.
+ * This is used by #mdb_cursor_first() and #mdb_cursor_last().
+ * If MDB_PS_ROOTONLY set, just fetch root node, no further lookups.
+ * @return 0 on success, non-zero on failure.
+ */
+static int
+mdb_page_search(MDB_cursor *mc, MDB_val *key, int flags)
+{
+ int rc;
+ pgno_t root;
+
+ /* Make sure the txn is still viable, then find the root from
+ * the txn's db table and set it as the root of the cursor's stack.
+ */
+ if (mc->mc_txn->mt_flags & MDB_TXN_BLOCKED) {
+ DPUTS("transaction may not be used now");
+ return MDB_BAD_TXN;
+ } else {
+ /* Make sure we're using an up-to-date root */
+ if (*mc->mc_dbflag & DB_STALE) {
+ MDB_cursor mc2;
+ if (TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi))
+ return MDB_BAD_DBI;
+ mdb_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, NULL);
+ rc = mdb_page_search(&mc2, &mc->mc_dbx->md_name, 0);
+ if (rc)
+ return rc;
+ {
+ MDB_val data;
+ int exact = 0;
+ uint16_t flags;
+ MDB_node *leaf = mdb_node_search(&mc2,
+ &mc->mc_dbx->md_name, &exact);
+ if (!exact)
+ return MDB_NOTFOUND;
+ if ((leaf->mn_flags & (F_DUPDATA|F_SUBDATA)) != F_SUBDATA)
+ return MDB_INCOMPATIBLE; /* not a named DB */
+ rc = mdb_node_read(&mc2, leaf, &data);
+ if (rc)
+ return rc;
+ memcpy(&flags, ((char *) data.mv_data + offsetof(MDB_db, md_flags)),
+ sizeof(uint16_t));
+ /* The txn may not know this DBI, or another process may
+ * have dropped and recreated the DB with other flags.
+ */
+ if ((mc->mc_db->md_flags & PERSISTENT_FLAGS) != flags)
+ return MDB_INCOMPATIBLE;
+ memcpy(mc->mc_db, data.mv_data, sizeof(MDB_db));
+ }
+ *mc->mc_dbflag &= ~DB_STALE;
+ }
+ root = mc->mc_db->md_root;
+
+ if (root == P_INVALID) { /* Tree is empty. */
+ DPUTS("tree is empty");
+ return MDB_NOTFOUND;
+ }
+ }
+
+ mdb_cassert(mc, root > 1);
+ if (!mc->mc_pg[0] || mc->mc_pg[0]->mp_pgno != root)
+ if ((rc = mdb_page_get(mc, root, &mc->mc_pg[0], NULL)) != 0)
+ return rc;
+
+ mc->mc_snum = 1;
+ mc->mc_top = 0;
+
+ DPRINTF(("db %d root page %"Z"u has flags 0x%X",
+ DDBI(mc), root, mc->mc_pg[0]->mp_flags));
+
+ if (flags & MDB_PS_MODIFY) {
+ if ((rc = mdb_page_touch(mc)))
+ return rc;
+ }
+
+ if (flags & MDB_PS_ROOTONLY)
+ return MDB_SUCCESS;
+
+ return mdb_page_search_root(mc, key, flags);
+}
+
+static int
+mdb_ovpage_free(MDB_cursor *mc, MDB_page *mp)
+{
+ MDB_txn *txn = mc->mc_txn;
+ pgno_t pg = mp->mp_pgno;
+ unsigned x = 0, ovpages = mp->mp_pages;
+ MDB_env *env = txn->mt_env;
+ MDB_IDL sl = txn->mt_spill_pgs;
+ MDB_ID pn = pg << 1;
+ int rc;
+
+ DPRINTF(("free ov page %"Z"u (%d)", pg, ovpages));
+ /* If the page is dirty or on the spill list we just acquired it,
+ * so we should give it back to our current free list, if any.
+ * Otherwise put it onto the list of pages we freed in this txn.
+ *
+ * Won't create me_pghead: me_pglast must be inited along with it.
+ * Unsupported in nested txns: They would need to hide the page
+ * range in ancestor txns' dirty and spilled lists.
+ */
+ if (env->me_pghead &&
+ !txn->mt_parent &&
+ ((mp->mp_flags & P_DIRTY) ||
+ (sl && (x = mdb_midl_search(sl, pn)) <= sl[0] && sl[x] == pn)))
+ {
+ unsigned i, j;
+ pgno_t *mop;
+ MDB_ID2 *dl, ix, iy;
+ rc = mdb_midl_need(&env->me_pghead, ovpages);
+ if (rc)
+ return rc;
+ if (!(mp->mp_flags & P_DIRTY)) {
+ /* This page is no longer spilled */
+ if (x == sl[0])
+ sl[0]--;
+ else
+ sl[x] |= 1;
+ goto release;
+ }
+ /* Remove from dirty list */
+ dl = txn->mt_u.dirty_list;
+ x = dl[0].mid--;
+ for (ix = dl[x]; ix.mptr != mp; ix = iy) {
+ if (x > 1) {
+ x--;
+ iy = dl[x];
+ dl[x] = ix;
+ } else {
+ mdb_cassert(mc, x > 1);
+ j = ++(dl[0].mid);
+ dl[j] = ix; /* Unsorted. OK when MDB_TXN_ERROR. */
+ txn->mt_flags |= MDB_TXN_ERROR;
+ return MDB_CORRUPTED;
+ }
+ }
+ txn->mt_dirty_room++;
+ if (!(env->me_flags & MDB_WRITEMAP))
+ mdb_dpage_free(env, mp);
+release:
+ /* Insert in me_pghead */
+ mop = env->me_pghead;
+ j = mop[0] + ovpages;
+ for (i = mop[0]; i && mop[i] < pg; i--)
+ mop[j--] = mop[i];
+ while (j>i)
+ mop[j--] = pg++;
+ mop[0] += ovpages;
+ } else {
+ rc = mdb_midl_append_range(&txn->mt_free_pgs, pg, ovpages);
+ if (rc)
+ return rc;
+ }
+ mc->mc_db->md_overflow_pages -= ovpages;
+ return 0;
+}
+
+/** Return the data associated with a given node.
+ * @param[in] mc The cursor for this operation.
+ * @param[in] leaf The node being read.
+ * @param[out] data Updated to point to the node's data.
+ * @return 0 on success, non-zero on failure.
+ */
+static int
+mdb_node_read(MDB_cursor *mc, MDB_node *leaf, MDB_val *data)
+{
+ MDB_page *omp; /* overflow page */
+ pgno_t pgno;
+ int rc;
+
+ if (!F_ISSET(leaf->mn_flags, F_BIGDATA)) {
+ data->mv_size = NODEDSZ(leaf);
+ data->mv_data = NODEDATA(leaf);
+ return MDB_SUCCESS;
+ }
+
+ /* Read overflow data.
+ */
+ data->mv_size = NODEDSZ(leaf);
+ memcpy(&pgno, NODEDATA(leaf), sizeof(pgno));
+ if ((rc = mdb_page_get(mc, pgno, &omp, NULL)) != 0) {
+ DPRINTF(("read overflow page %"Z"u failed", pgno));
+ return rc;
+ }
+ data->mv_data = METADATA(omp);
+
+ return MDB_SUCCESS;
+}
+
+int
+mdb_get(MDB_txn *txn, MDB_dbi dbi,
+ MDB_val *key, MDB_val *data)
+{
+ MDB_cursor mc;
+ MDB_xcursor mx;
+ int exact = 0;
+ DKBUF;
+
+ DPRINTF(("===> get db %u key [%s]", dbi, DKEY(key)));
+
+ if (!key || !data || !TXN_DBI_EXIST(txn, dbi, DB_USRVALID))
+ return EINVAL;
+
+ if (txn->mt_flags & MDB_TXN_BLOCKED)
+ return MDB_BAD_TXN;
+
+ mdb_cursor_init(&mc, txn, dbi, &mx);
+ return mdb_cursor_set(&mc, key, data, MDB_SET, &exact);
+}
+
+/** Find a sibling for a page.
+ * Replaces the page at the top of the cursor's stack with the
+ * specified sibling, if one exists.
+ * @param[in] mc The cursor for this operation.
+ * @param[in] move_right Non-zero if the right sibling is requested,
+ * otherwise the left sibling.
+ * @return 0 on success, non-zero on failure.
+ */
+static int
+mdb_cursor_sibling(MDB_cursor *mc, int move_right)
+{
+ int rc;
+ MDB_node *indx;
+ MDB_page *mp;
+
+ if (mc->mc_snum < 2) {
+ return MDB_NOTFOUND; /* root has no siblings */
+ }
+
+ mdb_cursor_pop(mc);
+ DPRINTF(("parent page is page %"Z"u, index %u",
+ mc->mc_pg[mc->mc_top]->mp_pgno, mc->mc_ki[mc->mc_top]));
+
+ if (move_right ? (mc->mc_ki[mc->mc_top] + 1u >= NUMKEYS(mc->mc_pg[mc->mc_top]))
+ : (mc->mc_ki[mc->mc_top] == 0)) {
+ DPRINTF(("no more keys left, moving to %s sibling",
+ move_right ? "right" : "left"));
+ if ((rc = mdb_cursor_sibling(mc, move_right)) != MDB_SUCCESS) {
+ /* undo cursor_pop before returning */
+ mc->mc_top++;
+ mc->mc_snum++;
+ return rc;
+ }
+ } else {
+ if (move_right)
+ mc->mc_ki[mc->mc_top]++;
+ else
+ mc->mc_ki[mc->mc_top]--;
+ DPRINTF(("just moving to %s index key %u",
+ move_right ? "right" : "left", mc->mc_ki[mc->mc_top]));
+ }
+ mdb_cassert(mc, IS_BRANCH(mc->mc_pg[mc->mc_top]));
+
+ indx = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
+ if ((rc = mdb_page_get(mc, NODEPGNO(indx), &mp, NULL)) != 0) {
+ /* mc will be inconsistent if caller does mc_snum++ as above */
+ mc->mc_flags &= ~(C_INITIALIZED|C_EOF);
+ return rc;
+ }
+
+ mdb_cursor_push(mc, mp);
+ if (!move_right)
+ mc->mc_ki[mc->mc_top] = NUMKEYS(mp)-1;
+
+ return MDB_SUCCESS;
+}
+
+/** Move the cursor to the next data item. */
+static int
+mdb_cursor_next(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op)
+{
+ MDB_page *mp;
+ MDB_node *leaf;
+ int rc;
+
+ if ((mc->mc_flags & C_DEL && op == MDB_NEXT_DUP))
+ return MDB_NOTFOUND;
+
+ if (!(mc->mc_flags & C_INITIALIZED))
+ return mdb_cursor_first(mc, key, data);
+
+ mp = mc->mc_pg[mc->mc_top];
+
+ if (mc->mc_flags & C_EOF) {
+ if (mc->mc_ki[mc->mc_top] >= NUMKEYS(mp)-1)
+ return MDB_NOTFOUND;
+ mc->mc_flags ^= C_EOF;
+ }
+
+ if (mc->mc_db->md_flags & MDB_DUPSORT) {
+ leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]);
+ if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
+ if (op == MDB_NEXT || op == MDB_NEXT_DUP) {
+ rc = mdb_cursor_next(&mc->mc_xcursor->mx_cursor, data, NULL, MDB_NEXT);
+ if (op != MDB_NEXT || rc != MDB_NOTFOUND) {
+ if (rc == MDB_SUCCESS)
+ MDB_GET_KEY(leaf, key);
+ return rc;
+ }
+ }
+ } else {
+ mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF);
+ if (op == MDB_NEXT_DUP)
+ return MDB_NOTFOUND;
+ }
+ }
+
+ DPRINTF(("cursor_next: top page is %"Z"u in cursor %p",
+ mdb_dbg_pgno(mp), (void *) mc));
+ if (mc->mc_flags & C_DEL) {
+ mc->mc_flags ^= C_DEL;
+ goto skip;
+ }
+
+ if (mc->mc_ki[mc->mc_top] + 1u >= NUMKEYS(mp)) {
+ DPUTS("=====> move to next sibling page");
+ if ((rc = mdb_cursor_sibling(mc, 1)) != MDB_SUCCESS) {
+ mc->mc_flags |= C_EOF;
+ return rc;
+ }
+ mp = mc->mc_pg[mc->mc_top];
+ DPRINTF(("next page is %"Z"u, key index %u", mp->mp_pgno, mc->mc_ki[mc->mc_top]));
+ } else
+ mc->mc_ki[mc->mc_top]++;
+
+skip:
+ DPRINTF(("==> cursor points to page %"Z"u with %u keys, key index %u",
+ mdb_dbg_pgno(mp), NUMKEYS(mp), mc->mc_ki[mc->mc_top]));
+
+ if (IS_LEAF2(mp)) {
+ key->mv_size = mc->mc_db->md_pad;
+ key->mv_data = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->mv_size);
+ return MDB_SUCCESS;
+ }
+
+ mdb_cassert(mc, IS_LEAF(mp));
+ leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]);
+
+ if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
+ mdb_xcursor_init1(mc, leaf);
+ rc = mdb_cursor_first(&mc->mc_xcursor->mx_cursor, data, NULL);
+ if (rc != MDB_SUCCESS)
+ return rc;
+ } else if (data) {
+ if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS)
+ return rc;
+ }
+
+ MDB_GET_KEY(leaf, key);
+ return MDB_SUCCESS;
+}
+
+/** Move the cursor to the previous data item. */
+static int
+mdb_cursor_prev(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op)
+{
+ MDB_page *mp;
+ MDB_node *leaf;
+ int rc;
+
+ if (!(mc->mc_flags & C_INITIALIZED)) {
+ rc = mdb_cursor_last(mc, key, data);
+ if (rc)
+ return rc;
+ mc->mc_ki[mc->mc_top]++;
+ }
+
+ mp = mc->mc_pg[mc->mc_top];
+
+ if ((mc->mc_db->md_flags & MDB_DUPSORT) &&
+ mc->mc_ki[mc->mc_top] < NUMKEYS(mp)) {
+ leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]);
+ if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
+ if (op == MDB_PREV || op == MDB_PREV_DUP) {
+ rc = mdb_cursor_prev(&mc->mc_xcursor->mx_cursor, data, NULL, MDB_PREV);
+ if (op != MDB_PREV || rc != MDB_NOTFOUND) {
+ if (rc == MDB_SUCCESS) {
+ MDB_GET_KEY(leaf, key);
+ mc->mc_flags &= ~C_EOF;
+ }
+ return rc;
+ }
+ }
+ } else {
+ mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF);
+ if (op == MDB_PREV_DUP)
+ return MDB_NOTFOUND;
+ }
+ }
+
+ DPRINTF(("cursor_prev: top page is %"Z"u in cursor %p",
+ mdb_dbg_pgno(mp), (void *) mc));
+
+ mc->mc_flags &= ~(C_EOF|C_DEL);
+
+ if (mc->mc_ki[mc->mc_top] == 0) {
+ DPUTS("=====> move to prev sibling page");
+ if ((rc = mdb_cursor_sibling(mc, 0)) != MDB_SUCCESS) {
+ return rc;
+ }
+ mp = mc->mc_pg[mc->mc_top];
+ mc->mc_ki[mc->mc_top] = NUMKEYS(mp) - 1;
+ DPRINTF(("prev page is %"Z"u, key index %u", mp->mp_pgno, mc->mc_ki[mc->mc_top]));
+ } else
+ mc->mc_ki[mc->mc_top]--;
+
+ DPRINTF(("==> cursor points to page %"Z"u with %u keys, key index %u",
+ mdb_dbg_pgno(mp), NUMKEYS(mp), mc->mc_ki[mc->mc_top]));
+
+ if (!IS_LEAF(mp))
+ return MDB_CORRUPTED;
+
+ if (IS_LEAF2(mp)) {
+ key->mv_size = mc->mc_db->md_pad;
+ key->mv_data = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->mv_size);
+ return MDB_SUCCESS;
+ }
+
+ leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]);
+
+ if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
+ mdb_xcursor_init1(mc, leaf);
+ rc = mdb_cursor_last(&mc->mc_xcursor->mx_cursor, data, NULL);
+ if (rc != MDB_SUCCESS)
+ return rc;
+ } else if (data) {
+ if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS)
+ return rc;
+ }
+
+ MDB_GET_KEY(leaf, key);
+ return MDB_SUCCESS;
+}
+
+/** Set the cursor on a specific data item. */
+static int
+mdb_cursor_set(MDB_cursor *mc, MDB_val *key, MDB_val *data,
+ MDB_cursor_op op, int *exactp)
+{
+ int rc;
+ MDB_page *mp;
+ MDB_node *leaf = NULL;
+ DKBUF;
+
+ if (key->mv_size == 0)
+ return MDB_BAD_VALSIZE;
+
+ if (mc->mc_xcursor)
+ mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF);
+
+ /* See if we're already on the right page */
+ if (mc->mc_flags & C_INITIALIZED) {
+ MDB_val nodekey;
+
+ mp = mc->mc_pg[mc->mc_top];
+ if (!NUMKEYS(mp)) {
+ mc->mc_ki[mc->mc_top] = 0;
+ return MDB_NOTFOUND;
+ }
+ if (mp->mp_flags & P_LEAF2) {
+ nodekey.mv_size = mc->mc_db->md_pad;
+ nodekey.mv_data = LEAF2KEY(mp, 0, nodekey.mv_size);
+ } else {
+ leaf = NODEPTR(mp, 0);
+ MDB_GET_KEY2(leaf, nodekey);
+ }
+ rc = mc->mc_dbx->md_cmp(key, &nodekey);
+ if (rc == 0) {
+ /* Probably happens rarely, but first node on the page
+ * was the one we wanted.
+ */
+ mc->mc_ki[mc->mc_top] = 0;
+ if (exactp)
+ *exactp = 1;
+ goto set1;
+ }
+ if (rc > 0) {
+ unsigned int i;
+ unsigned int nkeys = NUMKEYS(mp);
+ if (nkeys > 1) {
+ if (mp->mp_flags & P_LEAF2) {
+ nodekey.mv_data = LEAF2KEY(mp,
+ nkeys-1, nodekey.mv_size);
+ } else {
+ leaf = NODEPTR(mp, nkeys-1);
+ MDB_GET_KEY2(leaf, nodekey);
+ }
+ rc = mc->mc_dbx->md_cmp(key, &nodekey);
+ if (rc == 0) {
+ /* last node was the one we wanted */
+ mc->mc_ki[mc->mc_top] = nkeys-1;
+ if (exactp)
+ *exactp = 1;
+ goto set1;
+ }
+ if (rc < 0) {
+ if (mc->mc_ki[mc->mc_top] < NUMKEYS(mp)) {
+ /* This is definitely the right page, skip search_page */
+ if (mp->mp_flags & P_LEAF2) {
+ nodekey.mv_data = LEAF2KEY(mp,
+ mc->mc_ki[mc->mc_top], nodekey.mv_size);
+ } else {
+ leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]);
+ MDB_GET_KEY2(leaf, nodekey);
+ }
+ rc = mc->mc_dbx->md_cmp(key, &nodekey);
+ if (rc == 0) {
+ /* current node was the one we wanted */
+ if (exactp)
+ *exactp = 1;
+ goto set1;
+ }
+ }
+ rc = 0;
+ mc->mc_flags &= ~C_EOF;
+ goto set2;
+ }
+ }
+ /* If any parents have right-sibs, search.
+ * Otherwise, there's nothing further.
+ */
+ for (i=0; i<mc->mc_top; i++)
+ if (mc->mc_ki[i] <
+ NUMKEYS(mc->mc_pg[i])-1)
+ break;
+ if (i == mc->mc_top) {
+ /* There are no other pages */
+ mc->mc_ki[mc->mc_top] = nkeys;
+ return MDB_NOTFOUND;
+ }
+ }
+ if (!mc->mc_top) {
+ /* There are no other pages */
+ mc->mc_ki[mc->mc_top] = 0;
+ if (op == MDB_SET_RANGE && !exactp) {
+ rc = 0;
+ goto set1;
+ } else
+ return MDB_NOTFOUND;
+ }
+ } else {
+ mc->mc_pg[0] = 0;
+ }
+
+ rc = mdb_page_search(mc, key, 0);
+ if (rc != MDB_SUCCESS)
+ return rc;
+
+ mp = mc->mc_pg[mc->mc_top];
+ mdb_cassert(mc, IS_LEAF(mp));
+
+set2:
+ leaf = mdb_node_search(mc, key, exactp);
+ if (exactp != NULL && !*exactp) {
+ /* MDB_SET specified and not an exact match. */
+ return MDB_NOTFOUND;
+ }
+
+ if (leaf == NULL) {
+ DPUTS("===> inexact leaf not found, goto sibling");
+ if ((rc = mdb_cursor_sibling(mc, 1)) != MDB_SUCCESS) {
+ mc->mc_flags |= C_EOF;
+ return rc; /* no entries matched */
+ }
+ mp = mc->mc_pg[mc->mc_top];
+ mdb_cassert(mc, IS_LEAF(mp));
+ leaf = NODEPTR(mp, 0);
+ }
+
+set1:
+ mc->mc_flags |= C_INITIALIZED;
+ mc->mc_flags &= ~C_EOF;
+
+ if (IS_LEAF2(mp)) {
+ if (op == MDB_SET_RANGE || op == MDB_SET_KEY) {
+ key->mv_size = mc->mc_db->md_pad;
+ key->mv_data = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->mv_size);
+ }
+ return MDB_SUCCESS;
+ }
+
+ if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
+ mdb_xcursor_init1(mc, leaf);
+ if (op == MDB_SET || op == MDB_SET_KEY || op == MDB_SET_RANGE) {
+ rc = mdb_cursor_first(&mc->mc_xcursor->mx_cursor, data, NULL);
+ } else {
+ int ex2, *ex2p;
+ if (op == MDB_GET_BOTH) {
+ ex2p = &ex2;
+ ex2 = 0;
+ } else {
+ ex2p = NULL;
+ }
+ rc = mdb_cursor_set(&mc->mc_xcursor->mx_cursor, data, NULL, MDB_SET_RANGE, ex2p);
+ if (rc != MDB_SUCCESS)
+ return rc;
+ }
+ } else if (data) {
+ if (op == MDB_GET_BOTH || op == MDB_GET_BOTH_RANGE) {
+ MDB_val olddata;
+ MDB_cmp_func *dcmp;
+ if ((rc = mdb_node_read(mc, leaf, &olddata)) != MDB_SUCCESS)
+ return rc;
+ dcmp = mc->mc_dbx->md_dcmp;
+#if UINT_MAX < SIZE_MAX
+ if (dcmp == mdb_cmp_int && olddata.mv_size == sizeof(size_t))
+ dcmp = mdb_cmp_clong;
+#endif
+ rc = dcmp(data, &olddata);
+ if (rc) {
+ if (op == MDB_GET_BOTH || rc > 0)
+ return MDB_NOTFOUND;
+ rc = 0;
+ }
+ *data = olddata;
+
+ } else {
+ if (mc->mc_xcursor)
+ mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF);
+ if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS)
+ return rc;
+ }
+ }
+
+ /* The key already matches in all other cases */
+ if (op == MDB_SET_RANGE || op == MDB_SET_KEY)
+ MDB_GET_KEY(leaf, key);
+ DPRINTF(("==> cursor placed on key [%s]", DKEY(key)));
+
+ return rc;
+}
+
+/** Move the cursor to the first item in the database. */
+static int
+mdb_cursor_first(MDB_cursor *mc, MDB_val *key, MDB_val *data)
+{
+ int rc;
+ MDB_node *leaf;
+
+ if (mc->mc_xcursor)
+ mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF);
+
+ if (!(mc->mc_flags & C_INITIALIZED) || mc->mc_top) {
+ rc = mdb_page_search(mc, NULL, MDB_PS_FIRST);
+ if (rc != MDB_SUCCESS)
+ return rc;
+ }
+ mdb_cassert(mc, IS_LEAF(mc->mc_pg[mc->mc_top]));
+
+ leaf = NODEPTR(mc->mc_pg[mc->mc_top], 0);
+ mc->mc_flags |= C_INITIALIZED;
+ mc->mc_flags &= ~C_EOF;
+
+ mc->mc_ki[mc->mc_top] = 0;
+
+ if (IS_LEAF2(mc->mc_pg[mc->mc_top])) {
+ if ( key ) {
+ key->mv_size = mc->mc_db->md_pad;
+ key->mv_data = LEAF2KEY(mc->mc_pg[mc->mc_top], 0, key->mv_size);
+ }
+ return MDB_SUCCESS;
+ }
+
+ if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
+ mdb_xcursor_init1(mc, leaf);
+ rc = mdb_cursor_first(&mc->mc_xcursor->mx_cursor, data, NULL);
+ if (rc)
+ return rc;
+ } else if (data) {
+ if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS)
+ return rc;
+ }
+
+ MDB_GET_KEY(leaf, key);
+ return MDB_SUCCESS;
+}
+
+/** Move the cursor to the last item in the database. */
+static int
+mdb_cursor_last(MDB_cursor *mc, MDB_val *key, MDB_val *data)
+{
+ int rc;
+ MDB_node *leaf;
+
+ if (mc->mc_xcursor)
+ mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF);
+
+ if (!(mc->mc_flags & C_INITIALIZED) || mc->mc_top) {
+ rc = mdb_page_search(mc, NULL, MDB_PS_LAST);
+ if (rc != MDB_SUCCESS)
+ return rc;
+ }
+ mdb_cassert(mc, IS_LEAF(mc->mc_pg[mc->mc_top]));
+
+ mc->mc_ki[mc->mc_top] = NUMKEYS(mc->mc_pg[mc->mc_top]) - 1;
+ mc->mc_flags |= C_INITIALIZED|C_EOF;
+ leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
+
+ if (IS_LEAF2(mc->mc_pg[mc->mc_top])) {
+ if (key) {
+ key->mv_size = mc->mc_db->md_pad;
+ key->mv_data = LEAF2KEY(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top], key->mv_size);
+ }
+ return MDB_SUCCESS;
+ }
+
+ if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
+ mdb_xcursor_init1(mc, leaf);
+ rc = mdb_cursor_last(&mc->mc_xcursor->mx_cursor, data, NULL);
+ if (rc)
+ return rc;
+ } else if (data) {
+ if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS)
+ return rc;
+ }
+
+ MDB_GET_KEY(leaf, key);
+ return MDB_SUCCESS;
+}
+
+int
+mdb_cursor_get(MDB_cursor *mc, MDB_val *key, MDB_val *data,
+ MDB_cursor_op op)
+{
+ int rc;
+ int exact = 0;
+ int (*mfunc)(MDB_cursor *mc, MDB_val *key, MDB_val *data);
+
+ if (mc == NULL)
+ return EINVAL;
+
+ if (mc->mc_txn->mt_flags & MDB_TXN_BLOCKED)
+ return MDB_BAD_TXN;
+
+ switch (op) {
+ case MDB_GET_CURRENT:
+ if (!(mc->mc_flags & C_INITIALIZED)) {
+ rc = EINVAL;
+ } else {
+ MDB_page *mp = mc->mc_pg[mc->mc_top];
+ int nkeys = NUMKEYS(mp);
+ if (!nkeys || mc->mc_ki[mc->mc_top] >= nkeys) {
+ mc->mc_ki[mc->mc_top] = nkeys;
+ rc = MDB_NOTFOUND;
+ break;
+ }
+ rc = MDB_SUCCESS;
+ if (IS_LEAF2(mp)) {
+ key->mv_size = mc->mc_db->md_pad;
+ key->mv_data = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->mv_size);
+ } else {
+ MDB_node *leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]);
+ MDB_GET_KEY(leaf, key);
+ if (data) {
+ if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
+ rc = mdb_cursor_get(&mc->mc_xcursor->mx_cursor, data, NULL, MDB_GET_CURRENT);
+ } else {
+ rc = mdb_node_read(mc, leaf, data);
+ }
+ }
+ }
+ }
+ break;
+ case MDB_GET_BOTH:
+ case MDB_GET_BOTH_RANGE:
+ if (data == NULL) {
+ rc = EINVAL;
+ break;
+ }
+ if (mc->mc_xcursor == NULL) {
+ rc = MDB_INCOMPATIBLE;
+ break;
+ }
+ /* FALLTHRU */
+ case MDB_SET:
+ case MDB_SET_KEY:
+ case MDB_SET_RANGE:
+ if (key == NULL) {
+ rc = EINVAL;
+ } else {
+ rc = mdb_cursor_set(mc, key, data, op,
+ op == MDB_SET_RANGE ? NULL : &exact);
+ }
+ break;
+ case MDB_GET_MULTIPLE:
+ if (data == NULL || !(mc->mc_flags & C_INITIALIZED)) {
+ rc = EINVAL;
+ break;
+ }
+ if (!(mc->mc_db->md_flags & MDB_DUPFIXED)) {
+ rc = MDB_INCOMPATIBLE;
+ break;
+ }
+ rc = MDB_SUCCESS;
+ if (!(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) ||
+ (mc->mc_xcursor->mx_cursor.mc_flags & C_EOF))
+ break;
+ goto fetchm;
+ case MDB_NEXT_MULTIPLE:
+ if (data == NULL) {
+ rc = EINVAL;
+ break;
+ }
+ if (!(mc->mc_db->md_flags & MDB_DUPFIXED)) {
+ rc = MDB_INCOMPATIBLE;
+ break;
+ }
+ rc = mdb_cursor_next(mc, key, data, MDB_NEXT_DUP);
+ if (rc == MDB_SUCCESS) {
+ if (mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) {
+ MDB_cursor *mx;
+fetchm:
+ mx = &mc->mc_xcursor->mx_cursor;
+ data->mv_size = NUMKEYS(mx->mc_pg[mx->mc_top]) *
+ mx->mc_db->md_pad;
+ data->mv_data = METADATA(mx->mc_pg[mx->mc_top]);
+ mx->mc_ki[mx->mc_top] = NUMKEYS(mx->mc_pg[mx->mc_top])-1;
+ } else {
+ rc = MDB_NOTFOUND;
+ }
+ }
+ break;
+ case MDB_PREV_MULTIPLE:
+ if (data == NULL) {
+ rc = EINVAL;
+ break;
+ }
+ if (!(mc->mc_db->md_flags & MDB_DUPFIXED)) {
+ rc = MDB_INCOMPATIBLE;
+ break;
+ }
+ if (!(mc->mc_flags & C_INITIALIZED))
+ rc = mdb_cursor_last(mc, key, data);
+ else
+ rc = MDB_SUCCESS;
+ if (rc == MDB_SUCCESS) {
+ MDB_cursor *mx = &mc->mc_xcursor->mx_cursor;
+ if (mx->mc_flags & C_INITIALIZED) {
+ rc = mdb_cursor_sibling(mx, 0);
+ if (rc == MDB_SUCCESS)
+ goto fetchm;
+ } else {
+ rc = MDB_NOTFOUND;
+ }
+ }
+ break;
+ case MDB_NEXT:
+ case MDB_NEXT_DUP:
+ case MDB_NEXT_NODUP:
+ rc = mdb_cursor_next(mc, key, data, op);
+ break;
+ case MDB_PREV:
+ case MDB_PREV_DUP:
+ case MDB_PREV_NODUP:
+ rc = mdb_cursor_prev(mc, key, data, op);
+ break;
+ case MDB_FIRST:
+ rc = mdb_cursor_first(mc, key, data);
+ break;
+ case MDB_FIRST_DUP:
+ mfunc = mdb_cursor_first;
+ mmove:
+ if (data == NULL || !(mc->mc_flags & C_INITIALIZED)) {
+ rc = EINVAL;
+ break;
+ }
+ if (mc->mc_xcursor == NULL) {
+ rc = MDB_INCOMPATIBLE;
+ break;
+ }
+ if (mc->mc_ki[mc->mc_top] >= NUMKEYS(mc->mc_pg[mc->mc_top])) {
+ mc->mc_ki[mc->mc_top] = NUMKEYS(mc->mc_pg[mc->mc_top]);
+ rc = MDB_NOTFOUND;
+ break;
+ }
+ {
+ MDB_node *leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
+ if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) {
+ MDB_GET_KEY(leaf, key);
+ rc = mdb_node_read(mc, leaf, data);
+ break;
+ }
+ }
+ if (!(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)) {
+ rc = EINVAL;
+ break;
+ }
+ rc = mfunc(&mc->mc_xcursor->mx_cursor, data, NULL);
+ break;
+ case MDB_LAST:
+ rc = mdb_cursor_last(mc, key, data);
+ break;
+ case MDB_LAST_DUP:
+ mfunc = mdb_cursor_last;
+ goto mmove;
+ default:
+ DPRINTF(("unhandled/unimplemented cursor operation %u", op));
+ rc = EINVAL;
+ break;
+ }
+
+ if (mc->mc_flags & C_DEL)
+ mc->mc_flags ^= C_DEL;
+
+ return rc;
+}
+
+/** Touch all the pages in the cursor stack. Set mc_top.
+ * Makes sure all the pages are writable, before attempting a write operation.
+ * @param[in] mc The cursor to operate on.
+ */
+static int
+mdb_cursor_touch(MDB_cursor *mc)
+{
+ int rc = MDB_SUCCESS;
+
+ if (mc->mc_dbi >= CORE_DBS && !(*mc->mc_dbflag & (DB_DIRTY|DB_DUPDATA))) {
+ /* Touch DB record of named DB */
+ MDB_cursor mc2;
+ MDB_xcursor mcx;
+ if (TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi))
+ return MDB_BAD_DBI;
+ mdb_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, &mcx);
+ rc = mdb_page_search(&mc2, &mc->mc_dbx->md_name, MDB_PS_MODIFY);
+ if (rc)
+ return rc;
+ *mc->mc_dbflag |= DB_DIRTY;
+ }
+ mc->mc_top = 0;
+ if (mc->mc_snum) {
+ do {
+ rc = mdb_page_touch(mc);
+ } while (!rc && ++(mc->mc_top) < mc->mc_snum);
+ mc->mc_top = mc->mc_snum-1;
+ }
+ return rc;
+}
+
+/** Do not spill pages to disk if txn is getting full, may fail instead */
+#define MDB_NOSPILL 0x8000
+
+int
+mdb_cursor_put(MDB_cursor *mc, MDB_val *key, MDB_val *data,
+ unsigned int flags)
+{
+ MDB_env *env;
+ MDB_node *leaf = NULL;
+ MDB_page *fp, *mp, *sub_root = NULL;
+ uint16_t fp_flags;
+ MDB_val xdata, *rdata, dkey, olddata;
+ MDB_db dummy;
+ int do_sub = 0, insert_key, insert_data;
+ unsigned int mcount = 0, dcount = 0, nospill;
+ size_t nsize;
+ int rc, rc2;
+ unsigned int nflags;
+ DKBUF;
+
+ if (mc == NULL || key == NULL)
+ return EINVAL;
+
+ env = mc->mc_txn->mt_env;
+
+ /* Check this first so counter will always be zero on any
+ * early failures.
+ */
+ if (flags & MDB_MULTIPLE) {
+ dcount = data[1].mv_size;
+ data[1].mv_size = 0;
+ if (!F_ISSET(mc->mc_db->md_flags, MDB_DUPFIXED))
+ return MDB_INCOMPATIBLE;
+ }
+
+ nospill = flags & MDB_NOSPILL;
+ flags &= ~MDB_NOSPILL;
+
+ if (mc->mc_txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_BLOCKED))
+ return (mc->mc_txn->mt_flags & MDB_TXN_RDONLY) ? EACCES : MDB_BAD_TXN;
+
+ if (key->mv_size-1 >= ENV_MAXKEY(env))
+ return MDB_BAD_VALSIZE;
+
+#if SIZE_MAX > MAXDATASIZE
+ if (data->mv_size > ((mc->mc_db->md_flags & MDB_DUPSORT) ? ENV_MAXKEY(env) : MAXDATASIZE))
+ return MDB_BAD_VALSIZE;
+#else
+ if ((mc->mc_db->md_flags & MDB_DUPSORT) && data->mv_size > ENV_MAXKEY(env))
+ return MDB_BAD_VALSIZE;
+#endif
+
+ DPRINTF(("==> put db %d key [%s], size %"Z"u, data size %"Z"u",
+ DDBI(mc), DKEY(key), key ? key->mv_size : 0, data->mv_size));
+
+ dkey.mv_size = 0;
+
+ if (flags & MDB_CURRENT) {
+ if (!(mc->mc_flags & C_INITIALIZED))
+ return EINVAL;
+ rc = MDB_SUCCESS;
+ } else if (mc->mc_db->md_root == P_INVALID) {
+ /* new database, cursor has nothing to point to */
+ mc->mc_snum = 0;
+ mc->mc_top = 0;
+ mc->mc_flags &= ~C_INITIALIZED;
+ rc = MDB_NO_ROOT;
+ } else {
+ int exact = 0;
+ MDB_val d2;
+ if (flags & MDB_APPEND) {
+ MDB_val k2;
+ rc = mdb_cursor_last(mc, &k2, &d2);
+ if (rc == 0) {
+ rc = mc->mc_dbx->md_cmp(key, &k2);
+ if (rc > 0) {
+ rc = MDB_NOTFOUND;
+ mc->mc_ki[mc->mc_top]++;
+ } else {
+ /* new key is <= last key */
+ rc = MDB_KEYEXIST;
+ }
+ }
+ } else {
+ rc = mdb_cursor_set(mc, key, &d2, MDB_SET, &exact);
+ }
+ if ((flags & MDB_NOOVERWRITE) && rc == 0) {
+ DPRINTF(("duplicate key [%s]", DKEY(key)));
+ *data = d2;
+ return MDB_KEYEXIST;
+ }
+ if (rc && rc != MDB_NOTFOUND)
+ return rc;
+ }
+
+ if (mc->mc_flags & C_DEL)
+ mc->mc_flags ^= C_DEL;
+
+ /* Cursor is positioned, check for room in the dirty list */
+ if (!nospill) {
+ if (flags & MDB_MULTIPLE) {
+ rdata = &xdata;
+ xdata.mv_size = data->mv_size * dcount;
+ } else {
+ rdata = data;
+ }
+ if ((rc2 = mdb_page_spill(mc, key, rdata)))
+ return rc2;
+ }
+
+ if (rc == MDB_NO_ROOT) {
+ MDB_page *np;
+ /* new database, write a root leaf page */
+ DPUTS("allocating new root leaf page");
+ if ((rc2 = mdb_page_new(mc, P_LEAF, 1, &np))) {
+ return rc2;
+ }
+ mdb_cursor_push(mc, np);
+ mc->mc_db->md_root = np->mp_pgno;
+ mc->mc_db->md_depth++;
+ *mc->mc_dbflag |= DB_DIRTY;
+ if ((mc->mc_db->md_flags & (MDB_DUPSORT|MDB_DUPFIXED))
+ == MDB_DUPFIXED)
+ np->mp_flags |= P_LEAF2;
+ mc->mc_flags |= C_INITIALIZED;
+ } else {
+ /* make sure all cursor pages are writable */
+ rc2 = mdb_cursor_touch(mc);
+ if (rc2)
+ return rc2;
+ }
+
+ insert_key = insert_data = rc;
+ if (insert_key) {
+ /* The key does not exist */
+ DPRINTF(("inserting key at index %i", mc->mc_ki[mc->mc_top]));
+ if ((mc->mc_db->md_flags & MDB_DUPSORT) &&
+ LEAFSIZE(key, data) > env->me_nodemax)
+ {
+ /* Too big for a node, insert in sub-DB. Set up an empty
+ * "old sub-page" for prep_subDB to expand to a full page.
+ */
+ fp_flags = P_LEAF|P_DIRTY;
+ fp = env->me_pbuf;
+ fp->mp_pad = data->mv_size; /* used if MDB_DUPFIXED */
+ fp->mp_lower = fp->mp_upper = (PAGEHDRSZ-PAGEBASE);
+ olddata.mv_size = PAGEHDRSZ;
+ goto prep_subDB;
+ }
+ } else {
+ /* there's only a key anyway, so this is a no-op */
+ if (IS_LEAF2(mc->mc_pg[mc->mc_top])) {
+ char *ptr;
+ unsigned int ksize = mc->mc_db->md_pad;
+ if (key->mv_size != ksize)
+ return MDB_BAD_VALSIZE;
+ ptr = LEAF2KEY(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top], ksize);
+ memcpy(ptr, key->mv_data, ksize);
+fix_parent:
+ /* if overwriting slot 0 of leaf, need to
+ * update branch key if there is a parent page
+ */
+ if (mc->mc_top && !mc->mc_ki[mc->mc_top]) {
+ unsigned short dtop = 1;
+ mc->mc_top--;
+ /* slot 0 is always an empty key, find real slot */
+ while (mc->mc_top && !mc->mc_ki[mc->mc_top]) {
+ mc->mc_top--;
+ dtop++;
+ }
+ if (mc->mc_ki[mc->mc_top])
+ rc2 = mdb_update_key(mc, key);
+ else
+ rc2 = MDB_SUCCESS;
+ mc->mc_top += dtop;
+ if (rc2)
+ return rc2;
+ }
+ return MDB_SUCCESS;
+ }
+
+more:
+ leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
+ olddata.mv_size = NODEDSZ(leaf);
+ olddata.mv_data = NODEDATA(leaf);
+
+ /* DB has dups? */
+ if (F_ISSET(mc->mc_db->md_flags, MDB_DUPSORT)) {
+ /* Prepare (sub-)page/sub-DB to accept the new item,
+ * if needed. fp: old sub-page or a header faking
+ * it. mp: new (sub-)page. offset: growth in page
+ * size. xdata: node data with new page or DB.
+ */
+ unsigned i, offset = 0;
+ mp = fp = xdata.mv_data = env->me_pbuf;
+ mp->mp_pgno = mc->mc_pg[mc->mc_top]->mp_pgno;
+
+ /* Was a single item before, must convert now */
+ if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) {
+ MDB_cmp_func *dcmp;
+ /* Just overwrite the current item */
+ if (flags == MDB_CURRENT)
+ goto current;
+ dcmp = mc->mc_dbx->md_dcmp;
+#if UINT_MAX < SIZE_MAX
+ if (dcmp == mdb_cmp_int && olddata.mv_size == sizeof(size_t))
+ dcmp = mdb_cmp_clong;
+#endif
+ /* does data match? */
+ if (!dcmp(data, &olddata)) {
+ if (flags & (MDB_NODUPDATA|MDB_APPENDDUP))
+ return MDB_KEYEXIST;
+ /* overwrite it */
+ goto current;
+ }
+
+ /* Back up original data item */
+ dkey.mv_size = olddata.mv_size;
+ dkey.mv_data = memcpy(fp+1, olddata.mv_data, olddata.mv_size);
+
+ /* Make sub-page header for the dup items, with dummy body */
+ fp->mp_flags = P_LEAF|P_DIRTY|P_SUBP;
+ fp->mp_lower = (PAGEHDRSZ-PAGEBASE);
+ xdata.mv_size = PAGEHDRSZ + dkey.mv_size + data->mv_size;
+ if (mc->mc_db->md_flags & MDB_DUPFIXED) {
+ fp->mp_flags |= P_LEAF2;
+ fp->mp_pad = data->mv_size;
+ xdata.mv_size += 2 * data->mv_size; /* leave space for 2 more */
+ } else {
+ xdata.mv_size += 2 * (sizeof(indx_t) + NODESIZE) +
+ (dkey.mv_size & 1) + (data->mv_size & 1);
+ }
+ fp->mp_upper = xdata.mv_size - PAGEBASE;
+ olddata.mv_size = xdata.mv_size; /* pretend olddata is fp */
+ } else if (leaf->mn_flags & F_SUBDATA) {
+ /* Data is on sub-DB, just store it */
+ flags |= F_DUPDATA|F_SUBDATA;
+ goto put_sub;
+ } else {
+ /* Data is on sub-page */
+ fp = olddata.mv_data;
+ switch (flags) {
+ default:
+ if (!(mc->mc_db->md_flags & MDB_DUPFIXED)) {
+ offset = EVEN(NODESIZE + sizeof(indx_t) +
+ data->mv_size);
+ break;
+ }
+ offset = fp->mp_pad;
+ if (SIZELEFT(fp) < offset) {
+ offset *= 4; /* space for 4 more */
+ break;
+ }
+ /* FALLTHRU */ /* Big enough MDB_DUPFIXED sub-page */
+ case MDB_CURRENT:
+ fp->mp_flags |= P_DIRTY;
+ COPY_PGNO(fp->mp_pgno, mp->mp_pgno);
+ mc->mc_xcursor->mx_cursor.mc_pg[0] = fp;
+ flags |= F_DUPDATA;
+ goto put_sub;
+ }
+ xdata.mv_size = olddata.mv_size + offset;
+ }
+
+ fp_flags = fp->mp_flags;
+ if (NODESIZE + NODEKSZ(leaf) + xdata.mv_size > env->me_nodemax) {
+ /* Too big for a sub-page, convert to sub-DB */
+ fp_flags &= ~P_SUBP;
+prep_subDB:
+ if (mc->mc_db->md_flags & MDB_DUPFIXED) {
+ fp_flags |= P_LEAF2;
+ dummy.md_pad = fp->mp_pad;
+ dummy.md_flags = MDB_DUPFIXED;
+ if (mc->mc_db->md_flags & MDB_INTEGERDUP)
+ dummy.md_flags |= MDB_INTEGERKEY;
+ } else {
+ dummy.md_pad = 0;
+ dummy.md_flags = 0;
+ }
+ dummy.md_depth = 1;
+ dummy.md_branch_pages = 0;
+ dummy.md_leaf_pages = 1;
+ dummy.md_overflow_pages = 0;
+ dummy.md_entries = NUMKEYS(fp);
+ xdata.mv_size = sizeof(MDB_db);
+ xdata.mv_data = &dummy;
+ if ((rc = mdb_page_alloc(mc, 1, &mp)))
+ return rc;
+ offset = env->me_psize - olddata.mv_size;
+ flags |= F_DUPDATA|F_SUBDATA;
+ dummy.md_root = mp->mp_pgno;
+ sub_root = mp;
+ }
+ if (mp != fp) {
+ mp->mp_flags = fp_flags | P_DIRTY;
+ mp->mp_pad = fp->mp_pad;
+ mp->mp_lower = fp->mp_lower;
+ mp->mp_upper = fp->mp_upper + offset;
+ if (fp_flags & P_LEAF2) {
+ memcpy(METADATA(mp), METADATA(fp), NUMKEYS(fp) * fp->mp_pad);
+ } else {
+ memcpy((char *)mp + mp->mp_upper + PAGEBASE, (char *)fp + fp->mp_upper + PAGEBASE,
+ olddata.mv_size - fp->mp_upper - PAGEBASE);
+ memcpy((char *)(&mp->mp_ptrs), (char *)(&fp->mp_ptrs), NUMKEYS(fp) * sizeof(mp->mp_ptrs[0]));
+ for (i=0; i<NUMKEYS(fp); i++)
+ mp->mp_ptrs[i] += offset;
+ }
+ }
+
+ rdata = &xdata;
+ flags |= F_DUPDATA;
+ do_sub = 1;
+ if (!insert_key)
+ mdb_node_del(mc, 0);
+ goto new_sub;
+ }
+current:
+ /* LMDB passes F_SUBDATA in 'flags' to write a DB record */
+ if ((leaf->mn_flags ^ flags) & F_SUBDATA)
+ return MDB_INCOMPATIBLE;
+ /* overflow page overwrites need special handling */
+ if (F_ISSET(leaf->mn_flags, F_BIGDATA)) {
+ MDB_page *omp;
+ pgno_t pg;
+ int level, ovpages, dpages = OVPAGES(data->mv_size, env->me_psize);
+
+ memcpy(&pg, olddata.mv_data, sizeof(pg));
+ if ((rc2 = mdb_page_get(mc, pg, &omp, &level)) != 0)
+ return rc2;
+ ovpages = omp->mp_pages;
+
+ /* Is the ov page large enough? */
+ if (ovpages >= dpages) {
+ if (!(omp->mp_flags & P_DIRTY) &&
+ (level || (env->me_flags & MDB_WRITEMAP)))
+ {
+ rc = mdb_page_unspill(mc->mc_txn, omp, &omp);
+ if (rc)
+ return rc;
+ level = 0; /* dirty in this txn or clean */
+ }
+ /* Is it dirty? */
+ if (omp->mp_flags & P_DIRTY) {
+ /* yes, overwrite it. Note in this case we don't
+ * bother to try shrinking the page if the new data
+ * is smaller than the overflow threshold.
+ */
+ if (level > 1) {
+ /* It is writable only in a parent txn */
+ size_t sz = (size_t) env->me_psize * ovpages, off;
+ MDB_page *np = mdb_page_malloc(mc->mc_txn, ovpages);
+ MDB_ID2 id2;
+ if (!np)
+ return ENOMEM;
+ id2.mid = pg;
+ id2.mptr = np;
+ /* Note - this page is already counted in parent's dirty_room */
+ rc2 = mdb_mid2l_insert(mc->mc_txn->mt_u.dirty_list, &id2);
+ mdb_cassert(mc, rc2 == 0);
+ /* Currently we make the page look as with put() in the
+ * parent txn, in case the user peeks at MDB_RESERVEd
+ * or unused parts. Some users treat ovpages specially.
+ */
+ if (!(flags & MDB_RESERVE)) {
+ /* Skip the part where LMDB will put *data.
+ * Copy end of page, adjusting alignment so
+ * compiler may copy words instead of bytes.
+ */
+ off = (PAGEHDRSZ + data->mv_size) & -sizeof(size_t);
+ memcpy((size_t *)((char *)np + off),
+ (size_t *)((char *)omp + off), sz - off);
+ sz = PAGEHDRSZ;
+ }
+ memcpy(np, omp, sz); /* Copy beginning of page */
+ omp = np;
+ }
+ SETDSZ(leaf, data->mv_size);
+ if (F_ISSET(flags, MDB_RESERVE))
+ data->mv_data = METADATA(omp);
+ else
+ memcpy(METADATA(omp), data->mv_data, data->mv_size);
+ return MDB_SUCCESS;
+ }
+ }
+ if ((rc2 = mdb_ovpage_free(mc, omp)) != MDB_SUCCESS)
+ return rc2;
+ } else if (data->mv_size == olddata.mv_size) {
+ /* same size, just replace it. Note that we could
+ * also reuse this node if the new data is smaller,
+ * but instead we opt to shrink the node in that case.
+ */
+ if (F_ISSET(flags, MDB_RESERVE))
+ data->mv_data = olddata.mv_data;
+ else if (!(mc->mc_flags & C_SUB))
+ memcpy(olddata.mv_data, data->mv_data, data->mv_size);
+ else {
+ memcpy(NODEKEY(leaf), key->mv_data, key->mv_size);
+ goto fix_parent;
+ }
+ return MDB_SUCCESS;
+ }
+ mdb_node_del(mc, 0);
+ }
+
+ rdata = data;
+
+new_sub:
+ nflags = flags & NODE_ADD_FLAGS;
+ nsize = IS_LEAF2(mc->mc_pg[mc->mc_top]) ? key->mv_size : mdb_leaf_size(env, key, rdata);
+ if (SIZELEFT(mc->mc_pg[mc->mc_top]) < nsize) {
+ if (( flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA )
+ nflags &= ~MDB_APPEND; /* sub-page may need room to grow */
+ if (!insert_key)
+ nflags |= MDB_SPLIT_REPLACE;
+ rc = mdb_page_split(mc, key, rdata, P_INVALID, nflags);
+ } else {
+ /* There is room already in this leaf page. */
+ rc = mdb_node_add(mc, mc->mc_ki[mc->mc_top], key, rdata, 0, nflags);
+ if (rc == 0) {
+ /* Adjust other cursors pointing to mp */
+ MDB_cursor *m2, *m3;
+ MDB_dbi dbi = mc->mc_dbi;
+ unsigned i = mc->mc_top;
+ MDB_page *mp = mc->mc_pg[i];
+
+ for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
+ if (mc->mc_flags & C_SUB)
+ m3 = &m2->mc_xcursor->mx_cursor;
+ else
+ m3 = m2;
+ if (m3 == mc || m3->mc_snum < mc->mc_snum || m3->mc_pg[i] != mp) continue;
+ if (m3->mc_ki[i] >= mc->mc_ki[i] && insert_key) {
+ m3->mc_ki[i]++;
+ }
+ XCURSOR_REFRESH(m3, i, mp);
+ }
+ }
+ }
+
+ if (rc == MDB_SUCCESS) {
+ /* Now store the actual data in the child DB. Note that we're
+ * storing the user data in the keys field, so there are strict
+ * size limits on dupdata. The actual data fields of the child
+ * DB are all zero size.
+ */
+ if (do_sub) {
+ int xflags, new_dupdata;
+ size_t ecount;
+put_sub:
+ xdata.mv_size = 0;
+ xdata.mv_data = "";
+ leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
+ if ((flags & (MDB_CURRENT|MDB_APPENDDUP)) == MDB_CURRENT) {
+ xflags = MDB_CURRENT|MDB_NOSPILL;
+ } else {
+ mdb_xcursor_init1(mc, leaf);
+ xflags = (flags & MDB_NODUPDATA) ?
+ MDB_NOOVERWRITE|MDB_NOSPILL : MDB_NOSPILL;
+ }
+ if (sub_root)
+ mc->mc_xcursor->mx_cursor.mc_pg[0] = sub_root;
+ new_dupdata = (int)dkey.mv_size;
+ /* converted, write the original data first */
+ if (dkey.mv_size) {
+ rc = mdb_cursor_put(&mc->mc_xcursor->mx_cursor, &dkey, &xdata, xflags);
+ if (rc)
+ goto bad_sub;
+ /* we've done our job */
+ dkey.mv_size = 0;
+ }
+ if (!(leaf->mn_flags & F_SUBDATA) || sub_root) {
+ /* Adjust other cursors pointing to mp */
+ MDB_cursor *m2;
+ MDB_xcursor *mx = mc->mc_xcursor;
+ unsigned i = mc->mc_top;
+ MDB_page *mp = mc->mc_pg[i];
+
+ for (m2 = mc->mc_txn->mt_cursors[mc->mc_dbi]; m2; m2=m2->mc_next) {
+ if (m2 == mc || m2->mc_snum < mc->mc_snum) continue;
+ if (!(m2->mc_flags & C_INITIALIZED)) continue;
+ if (m2->mc_pg[i] == mp) {
+ if (m2->mc_ki[i] == mc->mc_ki[i]) {
+ mdb_xcursor_init2(m2, mx, new_dupdata);
+ } else if (!insert_key) {
+ XCURSOR_REFRESH(m2, i, mp);
+ }
+ }
+ }
+ }
+ ecount = mc->mc_xcursor->mx_db.md_entries;
+ if (flags & MDB_APPENDDUP)
+ xflags |= MDB_APPEND;
+ rc = mdb_cursor_put(&mc->mc_xcursor->mx_cursor, data, &xdata, xflags);
+ if (flags & F_SUBDATA) {
+ void *db = NODEDATA(leaf);
+ memcpy(db, &mc->mc_xcursor->mx_db, sizeof(MDB_db));
+ }
+ insert_data = mc->mc_xcursor->mx_db.md_entries - ecount;
+ }
+ /* Increment count unless we just replaced an existing item. */
+ if (insert_data)
+ mc->mc_db->md_entries++;
+ if (insert_key) {
+ /* Invalidate txn if we created an empty sub-DB */
+ if (rc)
+ goto bad_sub;
+ /* If we succeeded and the key didn't exist before,
+ * make sure the cursor is marked valid.
+ */
+ mc->mc_flags |= C_INITIALIZED;
+ }
+ if (flags & MDB_MULTIPLE) {
+ if (!rc) {
+ mcount++;
+ /* let caller know how many succeeded, if any */
+ data[1].mv_size = mcount;
+ if (mcount < dcount) {
+ data[0].mv_data = (char *)data[0].mv_data + data[0].mv_size;
+ insert_key = insert_data = 0;
+ goto more;
+ }
+ }
+ }
+ return rc;
+bad_sub:
+ if (rc == MDB_KEYEXIST) /* should not happen, we deleted that item */
+ rc = MDB_CORRUPTED;
+ }
+ mc->mc_txn->mt_flags |= MDB_TXN_ERROR;
+ return rc;
+}
+
+int
+mdb_cursor_del(MDB_cursor *mc, unsigned int flags)
+{
+ MDB_node *leaf;
+ MDB_page *mp;
+ int rc;
+
+ if (mc->mc_txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_BLOCKED))
+ return (mc->mc_txn->mt_flags & MDB_TXN_RDONLY) ? EACCES : MDB_BAD_TXN;
+
+ if (!(mc->mc_flags & C_INITIALIZED))
+ return EINVAL;
+
+ if (mc->mc_ki[mc->mc_top] >= NUMKEYS(mc->mc_pg[mc->mc_top]))
+ return MDB_NOTFOUND;
+
+ if (!(flags & MDB_NOSPILL) && (rc = mdb_page_spill(mc, NULL, NULL)))
+ return rc;
+
+ rc = mdb_cursor_touch(mc);
+ if (rc)
+ return rc;
+
+ mp = mc->mc_pg[mc->mc_top];
+ if (!IS_LEAF(mp))
+ return MDB_CORRUPTED;
+ if (IS_LEAF2(mp))
+ goto del_key;
+ leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]);
+
+ if (F_ISSET(leaf->mn_flags, F_DUPDATA)) {
+ if (flags & MDB_NODUPDATA) {
+ /* mdb_cursor_del0() will subtract the final entry */
+ mc->mc_db->md_entries -= mc->mc_xcursor->mx_db.md_entries - 1;
+ mc->mc_xcursor->mx_cursor.mc_flags &= ~C_INITIALIZED;
+ } else {
+ if (!F_ISSET(leaf->mn_flags, F_SUBDATA)) {
+ mc->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(leaf);
+ }
+ rc = mdb_cursor_del(&mc->mc_xcursor->mx_cursor, MDB_NOSPILL);
+ if (rc)
+ return rc;
+ /* If sub-DB still has entries, we're done */
+ if (mc->mc_xcursor->mx_db.md_entries) {
+ if (leaf->mn_flags & F_SUBDATA) {
+ /* update subDB info */
+ void *db = NODEDATA(leaf);
+ memcpy(db, &mc->mc_xcursor->mx_db, sizeof(MDB_db));
+ } else {
+ MDB_cursor *m2;
+ /* shrink fake page */
+ mdb_node_shrink(mp, mc->mc_ki[mc->mc_top]);
+ leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]);
+ mc->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(leaf);
+ /* fix other sub-DB cursors pointed at fake pages on this page */
+ for (m2 = mc->mc_txn->mt_cursors[mc->mc_dbi]; m2; m2=m2->mc_next) {
+ if (m2 == mc || m2->mc_snum < mc->mc_snum) continue;
+ if (!(m2->mc_flags & C_INITIALIZED)) continue;
+ if (m2->mc_pg[mc->mc_top] == mp) {
+ XCURSOR_REFRESH(m2, mc->mc_top, mp);
+ }
+ }
+ }
+ mc->mc_db->md_entries--;
+ return rc;
+ } else {
+ mc->mc_xcursor->mx_cursor.mc_flags &= ~C_INITIALIZED;
+ }
+ /* otherwise fall thru and delete the sub-DB */
+ }
+
+ if (leaf->mn_flags & F_SUBDATA) {
+ /* add all the child DB's pages to the free list */
+ rc = mdb_drop0(&mc->mc_xcursor->mx_cursor, 0);
+ if (rc)
+ goto fail;
+ }
+ }
+ /* LMDB passes F_SUBDATA in 'flags' to delete a DB record */
+ else if ((leaf->mn_flags ^ flags) & F_SUBDATA) {
+ rc = MDB_INCOMPATIBLE;
+ goto fail;
+ }
+
+ /* add overflow pages to free list */
+ if (F_ISSET(leaf->mn_flags, F_BIGDATA)) {
+ MDB_page *omp;
+ pgno_t pg;
+
+ memcpy(&pg, NODEDATA(leaf), sizeof(pg));
+ if ((rc = mdb_page_get(mc, pg, &omp, NULL)) ||
+ (rc = mdb_ovpage_free(mc, omp)))
+ goto fail;
+ }
+
+del_key:
+ return mdb_cursor_del0(mc);
+
+fail:
+ mc->mc_txn->mt_flags |= MDB_TXN_ERROR;
+ return rc;
+}
+
+/** Allocate and initialize new pages for a database.
+ * Set #MDB_TXN_ERROR on failure.
+ * @param[in] mc a cursor on the database being added to.
+ * @param[in] flags flags defining what type of page is being allocated.
+ * @param[in] num the number of pages to allocate. This is usually 1,
+ * unless allocating overflow pages for a large record.
+ * @param[out] mp Address of a page, or NULL on failure.
+ * @return 0 on success, non-zero on failure.
+ */
+static int
+mdb_page_new(MDB_cursor *mc, uint32_t flags, int num, MDB_page **mp)
+{
+ MDB_page *np;
+ int rc;
+
+ if ((rc = mdb_page_alloc(mc, num, &np)))
+ return rc;
+ DPRINTF(("allocated new mpage %"Z"u, page size %u",
+ np->mp_pgno, mc->mc_txn->mt_env->me_psize));
+ np->mp_flags = flags | P_DIRTY;
+ np->mp_lower = (PAGEHDRSZ-PAGEBASE);
+ np->mp_upper = mc->mc_txn->mt_env->me_psize - PAGEBASE;
+
+ if (IS_BRANCH(np))
+ mc->mc_db->md_branch_pages++;
+ else if (IS_LEAF(np))
+ mc->mc_db->md_leaf_pages++;
+ else if (IS_OVERFLOW(np)) {
+ mc->mc_db->md_overflow_pages += num;
+ np->mp_pages = num;
+ }
+ *mp = np;
+
+ return 0;
+}
+
+/** Calculate the size of a leaf node.
+ * The size depends on the environment's page size; if a data item
+ * is too large it will be put onto an overflow page and the node
+ * size will only include the key and not the data. Sizes are always
+ * rounded up to an even number of bytes, to guarantee 2-byte alignment
+ * of the #MDB_node headers.
+ * @param[in] env The environment handle.
+ * @param[in] key The key for the node.
+ * @param[in] data The data for the node.
+ * @return The number of bytes needed to store the node.
+ */
+static size_t
+mdb_leaf_size(MDB_env *env, MDB_val *key, MDB_val *data)
+{
+ size_t sz;
+
+ sz = LEAFSIZE(key, data);
+ if (sz > env->me_nodemax) {
+ /* put on overflow page */
+ sz -= data->mv_size - sizeof(pgno_t);
+ }
+
+ return EVEN(sz + sizeof(indx_t));
+}
+
+/** Calculate the size of a branch node.
+ * The size should depend on the environment's page size but since
+ * we currently don't support spilling large keys onto overflow
+ * pages, it's simply the size of the #MDB_node header plus the
+ * size of the key. Sizes are always rounded up to an even number
+ * of bytes, to guarantee 2-byte alignment of the #MDB_node headers.
+ * @param[in] env The environment handle.
+ * @param[in] key The key for the node.
+ * @return The number of bytes needed to store the node.
+ */
+static size_t
+mdb_branch_size(MDB_env *env, MDB_val *key)
+{
+ size_t sz;
+
+ sz = INDXSIZE(key);
+ if (sz > env->me_nodemax) {
+ /* put on overflow page */
+ /* not implemented */
+ /* sz -= key->size - sizeof(pgno_t); */
+ }
+
+ return sz + sizeof(indx_t);
+}
+
+/** Add a node to the page pointed to by the cursor.
+ * Set #MDB_TXN_ERROR on failure.
+ * @param[in] mc The cursor for this operation.
+ * @param[in] indx The index on the page where the new node should be added.
+ * @param[in] key The key for the new node.
+ * @param[in] data The data for the new node, if any.
+ * @param[in] pgno The page number, if adding a branch node.
+ * @param[in] flags Flags for the node.
+ * @return 0 on success, non-zero on failure. Possible errors are:
+ * <ul>
+ * <li>ENOMEM - failed to allocate overflow pages for the node.
+ * <li>MDB_PAGE_FULL - there is insufficient room in the page. This error
+ * should never happen since all callers already calculate the
+ * page's free space before calling this function.
+ * </ul>
+ */
+static int
+mdb_node_add(MDB_cursor *mc, indx_t indx,
+ MDB_val *key, MDB_val *data, pgno_t pgno, unsigned int flags)
+{
+ unsigned int i;
+ size_t node_size = NODESIZE;
+ ssize_t room;
+ indx_t ofs;
+ MDB_node *node;
+ MDB_page *mp = mc->mc_pg[mc->mc_top];
+ MDB_page *ofp = NULL; /* overflow page */
+ void *ndata;
+ DKBUF;
+
+ mdb_cassert(mc, mp->mp_upper >= mp->mp_lower);
+
+ DPRINTF(("add to %s %spage %"Z"u index %i, data size %"Z"u key size %"Z"u [%s]",
+ IS_LEAF(mp) ? "leaf" : "branch",
+ IS_SUBP(mp) ? "sub-" : "",
+ mdb_dbg_pgno(mp), indx, data ? data->mv_size : 0,
+ key ? key->mv_size : 0, key ? DKEY(key) : "null"));
+
+ if (IS_LEAF2(mp)) {
+ /* Move higher keys up one slot. */
+ int ksize = mc->mc_db->md_pad, dif;
+ char *ptr = LEAF2KEY(mp, indx, ksize);
+ dif = NUMKEYS(mp) - indx;
+ if (dif > 0)
+ memmove(ptr+ksize, ptr, dif*ksize);
+ /* insert new key */
+ memcpy(ptr, key->mv_data, ksize);
+
+ /* Just using these for counting */
+ mp->mp_lower += sizeof(indx_t);
+ mp->mp_upper -= ksize - sizeof(indx_t);
+ return MDB_SUCCESS;
+ }
+
+ room = (ssize_t)SIZELEFT(mp) - (ssize_t)sizeof(indx_t);
+ if (key != NULL)
+ node_size += key->mv_size;
+ if (IS_LEAF(mp)) {
+ mdb_cassert(mc, key && data);
+ if (F_ISSET(flags, F_BIGDATA)) {
+ /* Data already on overflow page. */
+ node_size += sizeof(pgno_t);
+ } else if (node_size + data->mv_size > mc->mc_txn->mt_env->me_nodemax) {
+ int ovpages = OVPAGES(data->mv_size, mc->mc_txn->mt_env->me_psize);
+ int rc;
+ /* Put data on overflow page. */
+ DPRINTF(("data size is %"Z"u, node would be %"Z"u, put data on overflow page",
+ data->mv_size, node_size+data->mv_size));
+ node_size = EVEN(node_size + sizeof(pgno_t));
+ if ((ssize_t)node_size > room)
+ goto full;
+ if ((rc = mdb_page_new(mc, P_OVERFLOW, ovpages, &ofp)))
+ return rc;
+ DPRINTF(("allocated overflow page %"Z"u", ofp->mp_pgno));
+ flags |= F_BIGDATA;
+ goto update;
+ } else {
+ node_size += data->mv_size;
+ }
+ }
+ node_size = EVEN(node_size);
+ if ((ssize_t)node_size > room)
+ goto full;
+
+update:
+ /* Move higher pointers up one slot. */
+ for (i = NUMKEYS(mp); i > indx; i--)
+ mp->mp_ptrs[i] = mp->mp_ptrs[i - 1];
+
+ /* Adjust free space offsets. */
+ ofs = mp->mp_upper - node_size;
+ mdb_cassert(mc, ofs >= mp->mp_lower + sizeof(indx_t));
+ mp->mp_ptrs[indx] = ofs;
+ mp->mp_upper = ofs;
+ mp->mp_lower += sizeof(indx_t);
+
+ /* Write the node data. */
+ node = NODEPTR(mp, indx);
+ node->mn_ksize = (key == NULL) ? 0 : key->mv_size;
+ node->mn_flags = flags;
+ if (IS_LEAF(mp))
+ SETDSZ(node,data->mv_size);
+ else
+ SETPGNO(node,pgno);
+
+ if (key)
+ memcpy(NODEKEY(node), key->mv_data, key->mv_size);
+
+ if (IS_LEAF(mp)) {
+ ndata = NODEDATA(node);
+ if (ofp == NULL) {
+ if (F_ISSET(flags, F_BIGDATA))
+ memcpy(ndata, data->mv_data, sizeof(pgno_t));
+ else if (F_ISSET(flags, MDB_RESERVE))
+ data->mv_data = ndata;
+ else
+ memcpy(ndata, data->mv_data, data->mv_size);
+ } else {
+ memcpy(ndata, &ofp->mp_pgno, sizeof(pgno_t));
+ ndata = METADATA(ofp);
+ if (F_ISSET(flags, MDB_RESERVE))
+ data->mv_data = ndata;
+ else
+ memcpy(ndata, data->mv_data, data->mv_size);
+ }
+ }
+
+ return MDB_SUCCESS;
+
+full:
+ DPRINTF(("not enough room in page %"Z"u, got %u ptrs",
+ mdb_dbg_pgno(mp), NUMKEYS(mp)));
+ DPRINTF(("upper-lower = %u - %u = %"Z"d", mp->mp_upper,mp->mp_lower,room));
+ DPRINTF(("node size = %"Z"u", node_size));
+ mc->mc_txn->mt_flags |= MDB_TXN_ERROR;
+ return MDB_PAGE_FULL;
+}
+
+/** Delete the specified node from a page.
+ * @param[in] mc Cursor pointing to the node to delete.
+ * @param[in] ksize The size of a node. Only used if the page is
+ * part of a #MDB_DUPFIXED database.
+ */
+static void
+mdb_node_del(MDB_cursor *mc, int ksize)
+{
+ MDB_page *mp = mc->mc_pg[mc->mc_top];
+ indx_t indx = mc->mc_ki[mc->mc_top];
+ unsigned int sz;
+ indx_t i, j, numkeys, ptr;
+ MDB_node *node;
+ char *base;
+
+ DPRINTF(("delete node %u on %s page %"Z"u", indx,
+ IS_LEAF(mp) ? "leaf" : "branch", mdb_dbg_pgno(mp)));
+ numkeys = NUMKEYS(mp);
+ mdb_cassert(mc, indx < numkeys);
+
+ if (IS_LEAF2(mp)) {
+ int x = numkeys - 1 - indx;
+ base = LEAF2KEY(mp, indx, ksize);
+ if (x)
+ memmove(base, base + ksize, x * ksize);
+ mp->mp_lower -= sizeof(indx_t);
+ mp->mp_upper += ksize - sizeof(indx_t);
+ return;
+ }
+
+ node = NODEPTR(mp, indx);
+ sz = NODESIZE + node->mn_ksize;
+ if (IS_LEAF(mp)) {
+ if (F_ISSET(node->mn_flags, F_BIGDATA))
+ sz += sizeof(pgno_t);
+ else
+ sz += NODEDSZ(node);
+ }
+ sz = EVEN(sz);
+
+ ptr = mp->mp_ptrs[indx];
+ for (i = j = 0; i < numkeys; i++) {
+ if (i != indx) {
+ mp->mp_ptrs[j] = mp->mp_ptrs[i];
+ if (mp->mp_ptrs[i] < ptr)
+ mp->mp_ptrs[j] += sz;
+ j++;
+ }
+ }
+
+ base = (char *)mp + mp->mp_upper + PAGEBASE;
+ memmove(base + sz, base, ptr - mp->mp_upper);
+
+ mp->mp_lower -= sizeof(indx_t);
+ mp->mp_upper += sz;
+}
+
+/** Compact the main page after deleting a node on a subpage.
+ * @param[in] mp The main page to operate on.
+ * @param[in] indx The index of the subpage on the main page.
+ */
+static void
+mdb_node_shrink(MDB_page *mp, indx_t indx)
+{
+ MDB_node *node;
+ MDB_page *sp, *xp;
+ char *base;
+ indx_t delta, nsize, len, ptr;
+ int i;
+
+ node = NODEPTR(mp, indx);
+ sp = (MDB_page *)NODEDATA(node);
+ delta = SIZELEFT(sp);
+ nsize = NODEDSZ(node) - delta;
+
+ /* Prepare to shift upward, set len = length(subpage part to shift) */
+ if (IS_LEAF2(sp)) {
+ len = nsize;
+ if (nsize & 1)
+ return; /* do not make the node uneven-sized */
+ } else {
+ xp = (MDB_page *)((char *)sp + delta); /* destination subpage */
+ for (i = NUMKEYS(sp); --i >= 0; )
+ xp->mp_ptrs[i] = sp->mp_ptrs[i] - delta;
+ len = PAGEHDRSZ;
+ }
+ sp->mp_upper = sp->mp_lower;
+ COPY_PGNO(sp->mp_pgno, mp->mp_pgno);
+ SETDSZ(node, nsize);
+
+ /* Shift <lower nodes...initial part of subpage> upward */
+ base = (char *)mp + mp->mp_upper + PAGEBASE;
+ memmove(base + delta, base, (char *)sp + len - base);
+
+ ptr = mp->mp_ptrs[indx];
+ for (i = NUMKEYS(mp); --i >= 0; ) {
+ if (mp->mp_ptrs[i] <= ptr)
+ mp->mp_ptrs[i] += delta;
+ }
+ mp->mp_upper += delta;
+}
+
+/** Initial setup of a sorted-dups cursor.
+ * Sorted duplicates are implemented as a sub-database for the given key.
+ * The duplicate data items are actually keys of the sub-database.
+ * Operations on the duplicate data items are performed using a sub-cursor
+ * initialized when the sub-database is first accessed. This function does
+ * the preliminary setup of the sub-cursor, filling in the fields that
+ * depend only on the parent DB.
+ * @param[in] mc The main cursor whose sorted-dups cursor is to be initialized.
+ */
+static void
+mdb_xcursor_init0(MDB_cursor *mc)
+{
+ MDB_xcursor *mx = mc->mc_xcursor;
+
+ mx->mx_cursor.mc_xcursor = NULL;
+ mx->mx_cursor.mc_txn = mc->mc_txn;
+ mx->mx_cursor.mc_db = &mx->mx_db;
+ mx->mx_cursor.mc_dbx = &mx->mx_dbx;
+ mx->mx_cursor.mc_dbi = mc->mc_dbi;
+ mx->mx_cursor.mc_dbflag = &mx->mx_dbflag;
+ mx->mx_cursor.mc_snum = 0;
+ mx->mx_cursor.mc_top = 0;
+ mx->mx_cursor.mc_flags = C_SUB;
+ mx->mx_dbx.md_name.mv_size = 0;
+ mx->mx_dbx.md_name.mv_data = NULL;
+ mx->mx_dbx.md_cmp = mc->mc_dbx->md_dcmp;
+ mx->mx_dbx.md_dcmp = NULL;
+ mx->mx_dbx.md_rel = mc->mc_dbx->md_rel;
+}
+
+/** Final setup of a sorted-dups cursor.
+ * Sets up the fields that depend on the data from the main cursor.
+ * @param[in] mc The main cursor whose sorted-dups cursor is to be initialized.
+ * @param[in] node The data containing the #MDB_db record for the
+ * sorted-dup database.
+ */
+static void
+mdb_xcursor_init1(MDB_cursor *mc, MDB_node *node)
+{
+ MDB_xcursor *mx = mc->mc_xcursor;
+
+ if (node->mn_flags & F_SUBDATA) {
+ memcpy(&mx->mx_db, NODEDATA(node), sizeof(MDB_db));
+ mx->mx_cursor.mc_pg[0] = 0;
+ mx->mx_cursor.mc_snum = 0;
+ mx->mx_cursor.mc_top = 0;
+ mx->mx_cursor.mc_flags = C_SUB;
+ } else {
+ MDB_page *fp = NODEDATA(node);
+ mx->mx_db.md_pad = 0;
+ mx->mx_db.md_flags = 0;
+ mx->mx_db.md_depth = 1;
+ mx->mx_db.md_branch_pages = 0;
+ mx->mx_db.md_leaf_pages = 1;
+ mx->mx_db.md_overflow_pages = 0;
+ mx->mx_db.md_entries = NUMKEYS(fp);
+ COPY_PGNO(mx->mx_db.md_root, fp->mp_pgno);
+ mx->mx_cursor.mc_snum = 1;
+ mx->mx_cursor.mc_top = 0;
+ mx->mx_cursor.mc_flags = C_INITIALIZED|C_SUB;
+ mx->mx_cursor.mc_pg[0] = fp;
+ mx->mx_cursor.mc_ki[0] = 0;
+ if (mc->mc_db->md_flags & MDB_DUPFIXED) {
+ mx->mx_db.md_flags = MDB_DUPFIXED;
+ mx->mx_db.md_pad = fp->mp_pad;
+ if (mc->mc_db->md_flags & MDB_INTEGERDUP)
+ mx->mx_db.md_flags |= MDB_INTEGERKEY;
+ }
+ }
+ DPRINTF(("Sub-db -%u root page %"Z"u", mx->mx_cursor.mc_dbi,
+ mx->mx_db.md_root));
+ mx->mx_dbflag = DB_VALID|DB_USRVALID|DB_DUPDATA;
+#if UINT_MAX < SIZE_MAX
+ if (mx->mx_dbx.md_cmp == mdb_cmp_int && mx->mx_db.md_pad == sizeof(size_t))
+ mx->mx_dbx.md_cmp = mdb_cmp_clong;
+#endif
+}
+
+
+/** Fixup a sorted-dups cursor due to underlying update.
+ * Sets up some fields that depend on the data from the main cursor.
+ * Almost the same as init1, but skips initialization steps if the
+ * xcursor had already been used.
+ * @param[in] mc The main cursor whose sorted-dups cursor is to be fixed up.
+ * @param[in] src_mx The xcursor of an up-to-date cursor.
+ * @param[in] new_dupdata True if converting from a non-#F_DUPDATA item.
+ */
+static void
+mdb_xcursor_init2(MDB_cursor *mc, MDB_xcursor *src_mx, int new_dupdata)
+{
+ MDB_xcursor *mx = mc->mc_xcursor;
+
+ if (new_dupdata) {
+ mx->mx_cursor.mc_snum = 1;
+ mx->mx_cursor.mc_top = 0;
+ mx->mx_cursor.mc_flags |= C_INITIALIZED;
+ mx->mx_cursor.mc_ki[0] = 0;
+ mx->mx_dbflag = DB_VALID|DB_USRVALID|DB_DUPDATA;
+#if UINT_MAX < SIZE_MAX
+ mx->mx_dbx.md_cmp = src_mx->mx_dbx.md_cmp;
+#endif
+ } else if (!(mx->mx_cursor.mc_flags & C_INITIALIZED)) {
+ return;
+ }
+ mx->mx_db = src_mx->mx_db;
+ mx->mx_cursor.mc_pg[0] = src_mx->mx_cursor.mc_pg[0];
+ DPRINTF(("Sub-db -%u root page %"Z"u", mx->mx_cursor.mc_dbi,
+ mx->mx_db.md_root));
+}
+
+/** Initialize a cursor for a given transaction and database. */
+static void
+mdb_cursor_init(MDB_cursor *mc, MDB_txn *txn, MDB_dbi dbi, MDB_xcursor *mx)
+{
+ mc->mc_next = NULL;
+ mc->mc_backup = NULL;
+ mc->mc_dbi = dbi;
+ mc->mc_txn = txn;
+ mc->mc_db = &txn->mt_dbs[dbi];
+ mc->mc_dbx = &txn->mt_dbxs[dbi];
+ mc->mc_dbflag = &txn->mt_dbflags[dbi];
+ mc->mc_snum = 0;
+ mc->mc_top = 0;
+ mc->mc_pg[0] = 0;
+ mc->mc_ki[0] = 0;
+ mc->mc_flags = 0;
+ if (txn->mt_dbs[dbi].md_flags & MDB_DUPSORT) {
+ mdb_tassert(txn, mx != NULL);
+ mc->mc_xcursor = mx;
+ mdb_xcursor_init0(mc);
+ } else {
+ mc->mc_xcursor = NULL;
+ }
+ if (*mc->mc_dbflag & DB_STALE) {
+ mdb_page_search(mc, NULL, MDB_PS_ROOTONLY);
+ }
+}
+
+int
+mdb_cursor_open(MDB_txn *txn, MDB_dbi dbi, MDB_cursor **ret)
+{
+ MDB_cursor *mc;
+ size_t size = sizeof(MDB_cursor);
+
+ if (!ret || !TXN_DBI_EXIST(txn, dbi, DB_VALID))
+ return EINVAL;
+
+ if (txn->mt_flags & MDB_TXN_BLOCKED)
+ return MDB_BAD_TXN;
+
+ if (dbi == FREE_DBI && !F_ISSET(txn->mt_flags, MDB_TXN_RDONLY))
+ return EINVAL;
+
+ if (txn->mt_dbs[dbi].md_flags & MDB_DUPSORT)
+ size += sizeof(MDB_xcursor);
+
+ if ((mc = malloc(size)) != NULL) {
+ mdb_cursor_init(mc, txn, dbi, (MDB_xcursor *)(mc + 1));
+ if (txn->mt_cursors) {
+ mc->mc_next = txn->mt_cursors[dbi];
+ txn->mt_cursors[dbi] = mc;
+ mc->mc_flags |= C_UNTRACK;
+ }
+ } else {
+ return ENOMEM;
+ }
+
+ *ret = mc;
+
+ return MDB_SUCCESS;
+}
+
+int
+mdb_cursor_renew(MDB_txn *txn, MDB_cursor *mc)
+{
+ if (!mc || !TXN_DBI_EXIST(txn, mc->mc_dbi, DB_VALID))
+ return EINVAL;
+
+ if ((mc->mc_flags & C_UNTRACK) || txn->mt_cursors)
+ return EINVAL;
+
+ if (txn->mt_flags & MDB_TXN_BLOCKED)
+ return MDB_BAD_TXN;
+
+ mdb_cursor_init(mc, txn, mc->mc_dbi, mc->mc_xcursor);
+ return MDB_SUCCESS;
+}
+
+/* Return the count of duplicate data items for the current key */
+int
+mdb_cursor_count(MDB_cursor *mc, size_t *countp)
+{
+ MDB_node *leaf;
+
+ if (mc == NULL || countp == NULL)
+ return EINVAL;
+
+ if (mc->mc_xcursor == NULL)
+ return MDB_INCOMPATIBLE;
+
+ if (mc->mc_txn->mt_flags & MDB_TXN_BLOCKED)
+ return MDB_BAD_TXN;
+
+ if (!(mc->mc_flags & C_INITIALIZED))
+ return EINVAL;
+
+ if (!mc->mc_snum)
+ return MDB_NOTFOUND;
+
+ if (mc->mc_flags & C_EOF) {
+ if (mc->mc_ki[mc->mc_top] >= NUMKEYS(mc->mc_pg[mc->mc_top]))
+ return MDB_NOTFOUND;
+ mc->mc_flags ^= C_EOF;
+ }
+
+ leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
+ if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) {
+ *countp = 1;
+ } else {
+ if (!(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED))
+ return EINVAL;
+
+ *countp = mc->mc_xcursor->mx_db.md_entries;
+ }
+ return MDB_SUCCESS;
+}
+
+void
+mdb_cursor_close(MDB_cursor *mc)
+{
+ if (mc && !mc->mc_backup) {
+ /* remove from txn, if tracked */
+ if ((mc->mc_flags & C_UNTRACK) && mc->mc_txn->mt_cursors) {
+ MDB_cursor **prev = &mc->mc_txn->mt_cursors[mc->mc_dbi];
+ while (*prev && *prev != mc) prev = &(*prev)->mc_next;
+ if (*prev == mc)
+ *prev = mc->mc_next;
+ }
+ free(mc);
+ }
+}
+
+MDB_txn *
+mdb_cursor_txn(MDB_cursor *mc)
+{
+ if (!mc) return NULL;
+ return mc->mc_txn;
+}
+
+MDB_dbi
+mdb_cursor_dbi(MDB_cursor *mc)
+{
+ return mc->mc_dbi;
+}
+
+/** Replace the key for a branch node with a new key.
+ * Set #MDB_TXN_ERROR on failure.
+ * @param[in] mc Cursor pointing to the node to operate on.
+ * @param[in] key The new key to use.
+ * @return 0 on success, non-zero on failure.
+ */
+static int
+mdb_update_key(MDB_cursor *mc, MDB_val *key)
+{
+ MDB_page *mp;
+ MDB_node *node;
+ char *base;
+ size_t len;
+ int delta, ksize, oksize;
+ indx_t ptr, i, numkeys, indx;
+ DKBUF;
+
+ indx = mc->mc_ki[mc->mc_top];
+ mp = mc->mc_pg[mc->mc_top];
+ node = NODEPTR(mp, indx);
+ ptr = mp->mp_ptrs[indx];
+#if MDB_DEBUG
+ {
+ MDB_val k2;
+ char kbuf2[DKBUF_MAXKEYSIZE*2+1];
+ k2.mv_data = NODEKEY(node);
+ k2.mv_size = node->mn_ksize;
+ DPRINTF(("update key %u (ofs %u) [%s] to [%s] on page %"Z"u",
+ indx, ptr,
+ mdb_dkey(&k2, kbuf2),
+ DKEY(key),
+ mp->mp_pgno));
+ }
+#endif
+
+ /* Sizes must be 2-byte aligned. */
+ ksize = EVEN(key->mv_size);
+ oksize = EVEN(node->mn_ksize);
+ delta = ksize - oksize;
+
+ /* Shift node contents if EVEN(key length) changed. */
+ if (delta) {
+ if (delta > 0 && SIZELEFT(mp) < delta) {
+ pgno_t pgno;
+ /* not enough space left, do a delete and split */
+ DPRINTF(("Not enough room, delta = %d, splitting...", delta));
+ pgno = NODEPGNO(node);
+ mdb_node_del(mc, 0);
+ return mdb_page_split(mc, key, NULL, pgno, MDB_SPLIT_REPLACE);
+ }
+
+ numkeys = NUMKEYS(mp);
+ for (i = 0; i < numkeys; i++) {
+ if (mp->mp_ptrs[i] <= ptr)
+ mp->mp_ptrs[i] -= delta;
+ }
+
+ base = (char *)mp + mp->mp_upper + PAGEBASE;
+ len = ptr - mp->mp_upper + NODESIZE;
+ memmove(base - delta, base, len);
+ mp->mp_upper -= delta;
+
+ node = NODEPTR(mp, indx);
+ }
+
+ /* But even if no shift was needed, update ksize */
+ if (node->mn_ksize != key->mv_size)
+ node->mn_ksize = key->mv_size;
+
+ if (key->mv_size)
+ memcpy(NODEKEY(node), key->mv_data, key->mv_size);
+
+ return MDB_SUCCESS;
+}
+
+static void
+mdb_cursor_copy(const MDB_cursor *csrc, MDB_cursor *cdst);
+
+/** Perform \b act while tracking temporary cursor \b mn */
+#define WITH_CURSOR_TRACKING(mn, act) do { \
+ MDB_cursor dummy, *tracked, **tp = &(mn).mc_txn->mt_cursors[mn.mc_dbi]; \
+ if ((mn).mc_flags & C_SUB) { \
+ dummy.mc_flags = C_INITIALIZED; \
+ dummy.mc_xcursor = (MDB_xcursor *)&(mn); \
+ tracked = &dummy; \
+ } else { \
+ tracked = &(mn); \
+ } \
+ tracked->mc_next = *tp; \
+ *tp = tracked; \
+ { act; } \
+ *tp = tracked->mc_next; \
+} while (0)
+
+/** Move a node from csrc to cdst.
+ */
+static int
+mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst, int fromleft)
+{
+ MDB_node *srcnode;
+ MDB_val key, data;
+ pgno_t srcpg;
+ MDB_cursor mn;
+ int rc;
+ unsigned short flags;
+
+ DKBUF;
+
+ /* Mark src and dst as dirty. */
+ if ((rc = mdb_page_touch(csrc)) ||
+ (rc = mdb_page_touch(cdst)))
+ return rc;
+
+ if (IS_LEAF2(csrc->mc_pg[csrc->mc_top])) {
+ key.mv_size = csrc->mc_db->md_pad;
+ key.mv_data = LEAF2KEY(csrc->mc_pg[csrc->mc_top], csrc->mc_ki[csrc->mc_top], key.mv_size);
+ data.mv_size = 0;
+ data.mv_data = NULL;
+ srcpg = 0;
+ flags = 0;
+ } else {
+ srcnode = NODEPTR(csrc->mc_pg[csrc->mc_top], csrc->mc_ki[csrc->mc_top]);
+ mdb_cassert(csrc, !((size_t)srcnode & 1));
+ srcpg = NODEPGNO(srcnode);
+ flags = srcnode->mn_flags;
+ if (csrc->mc_ki[csrc->mc_top] == 0 && IS_BRANCH(csrc->mc_pg[csrc->mc_top])) {
+ unsigned int snum = csrc->mc_snum;
+ MDB_node *s2;
+ /* must find the lowest key below src */
+ rc = mdb_page_search_lowest(csrc);
+ if (rc)
+ return rc;
+ if (IS_LEAF2(csrc->mc_pg[csrc->mc_top])) {
+ key.mv_size = csrc->mc_db->md_pad;
+ key.mv_data = LEAF2KEY(csrc->mc_pg[csrc->mc_top], 0, key.mv_size);
+ } else {
+ s2 = NODEPTR(csrc->mc_pg[csrc->mc_top], 0);
+ key.mv_size = NODEKSZ(s2);
+ key.mv_data = NODEKEY(s2);
+ }
+ csrc->mc_snum = snum--;
+ csrc->mc_top = snum;
+ } else {
+ key.mv_size = NODEKSZ(srcnode);
+ key.mv_data = NODEKEY(srcnode);
+ }
+ data.mv_size = NODEDSZ(srcnode);
+ data.mv_data = NODEDATA(srcnode);
+ }
+ mn.mc_xcursor = NULL;
+ if (IS_BRANCH(cdst->mc_pg[cdst->mc_top]) && cdst->mc_ki[cdst->mc_top] == 0) {
+ unsigned int snum = cdst->mc_snum;
+ MDB_node *s2;
+ MDB_val bkey;
+ /* must find the lowest key below dst */
+ mdb_cursor_copy(cdst, &mn);
+ rc = mdb_page_search_lowest(&mn);
+ if (rc)
+ return rc;
+ if (IS_LEAF2(mn.mc_pg[mn.mc_top])) {
+ bkey.mv_size = mn.mc_db->md_pad;
+ bkey.mv_data = LEAF2KEY(mn.mc_pg[mn.mc_top], 0, bkey.mv_size);
+ } else {
+ s2 = NODEPTR(mn.mc_pg[mn.mc_top], 0);
+ bkey.mv_size = NODEKSZ(s2);
+ bkey.mv_data = NODEKEY(s2);
+ }
+ mn.mc_snum = snum--;
+ mn.mc_top = snum;
+ mn.mc_ki[snum] = 0;
+ rc = mdb_update_key(&mn, &bkey);
+ if (rc)
+ return rc;
+ }
+
+ DPRINTF(("moving %s node %u [%s] on page %"Z"u to node %u on page %"Z"u",
+ IS_LEAF(csrc->mc_pg[csrc->mc_top]) ? "leaf" : "branch",
+ csrc->mc_ki[csrc->mc_top],
+ DKEY(&key),
+ csrc->mc_pg[csrc->mc_top]->mp_pgno,
+ cdst->mc_ki[cdst->mc_top], cdst->mc_pg[cdst->mc_top]->mp_pgno));
+
+ /* Add the node to the destination page.
+ */
+ rc = mdb_node_add(cdst, cdst->mc_ki[cdst->mc_top], &key, &data, srcpg, flags);
+ if (rc != MDB_SUCCESS)
+ return rc;
+
+ /* Delete the node from the source page.
+ */
+ mdb_node_del(csrc, key.mv_size);
+
+ {
+ /* Adjust other cursors pointing to mp */
+ MDB_cursor *m2, *m3;
+ MDB_dbi dbi = csrc->mc_dbi;
+ MDB_page *mpd, *mps;
+
+ mps = csrc->mc_pg[csrc->mc_top];
+ /* If we're adding on the left, bump others up */
+ if (fromleft) {
+ mpd = cdst->mc_pg[csrc->mc_top];
+ for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
+ if (csrc->mc_flags & C_SUB)
+ m3 = &m2->mc_xcursor->mx_cursor;
+ else
+ m3 = m2;
+ if (!(m3->mc_flags & C_INITIALIZED) || m3->mc_top < csrc->mc_top)
+ continue;
+ if (m3 != cdst &&
+ m3->mc_pg[csrc->mc_top] == mpd &&
+ m3->mc_ki[csrc->mc_top] >= cdst->mc_ki[csrc->mc_top]) {
+ m3->mc_ki[csrc->mc_top]++;
+ }
+ if (m3 !=csrc &&
+ m3->mc_pg[csrc->mc_top] == mps &&
+ m3->mc_ki[csrc->mc_top] == csrc->mc_ki[csrc->mc_top]) {
+ m3->mc_pg[csrc->mc_top] = cdst->mc_pg[cdst->mc_top];
+ m3->mc_ki[csrc->mc_top] = cdst->mc_ki[cdst->mc_top];
+ m3->mc_ki[csrc->mc_top-1]++;
+ }
+ if (IS_LEAF(mps))
+ XCURSOR_REFRESH(m3, csrc->mc_top, m3->mc_pg[csrc->mc_top]);
+ }
+ } else
+ /* Adding on the right, bump others down */
+ {
+ for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
+ if (csrc->mc_flags & C_SUB)
+ m3 = &m2->mc_xcursor->mx_cursor;
+ else
+ m3 = m2;
+ if (m3 == csrc) continue;
+ if (!(m3->mc_flags & C_INITIALIZED) || m3->mc_top < csrc->mc_top)
+ continue;
+ if (m3->mc_pg[csrc->mc_top] == mps) {
+ if (!m3->mc_ki[csrc->mc_top]) {
+ m3->mc_pg[csrc->mc_top] = cdst->mc_pg[cdst->mc_top];
+ m3->mc_ki[csrc->mc_top] = cdst->mc_ki[cdst->mc_top];
+ m3->mc_ki[csrc->mc_top-1]--;
+ } else {
+ m3->mc_ki[csrc->mc_top]--;
+ }
+ if (IS_LEAF(mps))
+ XCURSOR_REFRESH(m3, csrc->mc_top, m3->mc_pg[csrc->mc_top]);
+ }
+ }
+ }
+ }
+
+ /* Update the parent separators.
+ */
+ if (csrc->mc_ki[csrc->mc_top] == 0) {
+ if (csrc->mc_ki[csrc->mc_top-1] != 0) {
+ if (IS_LEAF2(csrc->mc_pg[csrc->mc_top])) {
+ key.mv_data = LEAF2KEY(csrc->mc_pg[csrc->mc_top], 0, key.mv_size);
+ } else {
+ srcnode = NODEPTR(csrc->mc_pg[csrc->mc_top], 0);
+ key.mv_size = NODEKSZ(srcnode);
+ key.mv_data = NODEKEY(srcnode);
+ }
+ DPRINTF(("update separator for source page %"Z"u to [%s]",
+ csrc->mc_pg[csrc->mc_top]->mp_pgno, DKEY(&key)));
+ mdb_cursor_copy(csrc, &mn);
+ mn.mc_snum--;
+ mn.mc_top--;
+ /* We want mdb_rebalance to find mn when doing fixups */
+ WITH_CURSOR_TRACKING(mn,
+ rc = mdb_update_key(&mn, &key));
+ if (rc)
+ return rc;
+ }
+ if (IS_BRANCH(csrc->mc_pg[csrc->mc_top])) {
+ MDB_val nullkey;
+ indx_t ix = csrc->mc_ki[csrc->mc_top];
+ nullkey.mv_size = 0;
+ csrc->mc_ki[csrc->mc_top] = 0;
+ rc = mdb_update_key(csrc, &nullkey);
+ csrc->mc_ki[csrc->mc_top] = ix;
+ mdb_cassert(csrc, rc == MDB_SUCCESS);
+ }
+ }
+
+ if (cdst->mc_ki[cdst->mc_top] == 0) {
+ if (cdst->mc_ki[cdst->mc_top-1] != 0) {
+ if (IS_LEAF2(csrc->mc_pg[csrc->mc_top])) {
+ key.mv_data = LEAF2KEY(cdst->mc_pg[cdst->mc_top], 0, key.mv_size);
+ } else {
+ srcnode = NODEPTR(cdst->mc_pg[cdst->mc_top], 0);
+ key.mv_size = NODEKSZ(srcnode);
+ key.mv_data = NODEKEY(srcnode);
+ }
+ DPRINTF(("update separator for destination page %"Z"u to [%s]",
+ cdst->mc_pg[cdst->mc_top]->mp_pgno, DKEY(&key)));
+ mdb_cursor_copy(cdst, &mn);
+ mn.mc_snum--;
+ mn.mc_top--;
+ /* We want mdb_rebalance to find mn when doing fixups */
+ WITH_CURSOR_TRACKING(mn,
+ rc = mdb_update_key(&mn, &key));
+ if (rc)
+ return rc;
+ }
+ if (IS_BRANCH(cdst->mc_pg[cdst->mc_top])) {
+ MDB_val nullkey;
+ indx_t ix = cdst->mc_ki[cdst->mc_top];
+ nullkey.mv_size = 0;
+ cdst->mc_ki[cdst->mc_top] = 0;
+ rc = mdb_update_key(cdst, &nullkey);
+ cdst->mc_ki[cdst->mc_top] = ix;
+ mdb_cassert(cdst, rc == MDB_SUCCESS);
+ }
+ }
+
+ return MDB_SUCCESS;
+}
+
+/** Merge one page into another.
+ * The nodes from the page pointed to by \b csrc will
+ * be copied to the page pointed to by \b cdst and then
+ * the \b csrc page will be freed.
+ * @param[in] csrc Cursor pointing to the source page.
+ * @param[in] cdst Cursor pointing to the destination page.
+ * @return 0 on success, non-zero on failure.
+ */
+static int
+mdb_page_merge(MDB_cursor *csrc, MDB_cursor *cdst)
+{
+ MDB_page *psrc, *pdst;
+ MDB_node *srcnode;
+ MDB_val key, data;
+ unsigned nkeys;
+ int rc;
+ indx_t i, j;
+
+ psrc = csrc->mc_pg[csrc->mc_top];
+ pdst = cdst->mc_pg[cdst->mc_top];
+
+ DPRINTF(("merging page %"Z"u into %"Z"u", psrc->mp_pgno, pdst->mp_pgno));
+
+ mdb_cassert(csrc, csrc->mc_snum > 1); /* can't merge root page */
+ mdb_cassert(csrc, cdst->mc_snum > 1);
+
+ /* Mark dst as dirty. */
+ if ((rc = mdb_page_touch(cdst)))
+ return rc;
+
+ /* get dst page again now that we've touched it. */
+ pdst = cdst->mc_pg[cdst->mc_top];
+
+ /* Move all nodes from src to dst.
+ */
+ j = nkeys = NUMKEYS(pdst);
+ if (IS_LEAF2(psrc)) {
+ key.mv_size = csrc->mc_db->md_pad;
+ key.mv_data = METADATA(psrc);
+ for (i = 0; i < NUMKEYS(psrc); i++, j++) {
+ rc = mdb_node_add(cdst, j, &key, NULL, 0, 0);
+ if (rc != MDB_SUCCESS)
+ return rc;
+ key.mv_data = (char *)key.mv_data + key.mv_size;
+ }
+ } else {
+ for (i = 0; i < NUMKEYS(psrc); i++, j++) {
+ srcnode = NODEPTR(psrc, i);
+ if (i == 0 && IS_BRANCH(psrc)) {
+ MDB_cursor mn;
+ MDB_node *s2;
+ mdb_cursor_copy(csrc, &mn);
+ mn.mc_xcursor = NULL;
+ /* must find the lowest key below src */
+ rc = mdb_page_search_lowest(&mn);
+ if (rc)
+ return rc;
+ if (IS_LEAF2(mn.mc_pg[mn.mc_top])) {
+ key.mv_size = mn.mc_db->md_pad;
+ key.mv_data = LEAF2KEY(mn.mc_pg[mn.mc_top], 0, key.mv_size);
+ } else {
+ s2 = NODEPTR(mn.mc_pg[mn.mc_top], 0);
+ key.mv_size = NODEKSZ(s2);
+ key.mv_data = NODEKEY(s2);
+ }
+ } else {
+ key.mv_size = srcnode->mn_ksize;
+ key.mv_data = NODEKEY(srcnode);
+ }
+
+ data.mv_size = NODEDSZ(srcnode);
+ data.mv_data = NODEDATA(srcnode);
+ rc = mdb_node_add(cdst, j, &key, &data, NODEPGNO(srcnode), srcnode->mn_flags);
+ if (rc != MDB_SUCCESS)
+ return rc;
+ }
+ }
+
+ DPRINTF(("dst page %"Z"u now has %u keys (%.1f%% filled)",
+ pdst->mp_pgno, NUMKEYS(pdst),
+ (float)PAGEFILL(cdst->mc_txn->mt_env, pdst) / 10));
+
+ /* Unlink the src page from parent and add to free list.
+ */
+ csrc->mc_top--;
+ mdb_node_del(csrc, 0);
+ if (csrc->mc_ki[csrc->mc_top] == 0) {
+ key.mv_size = 0;
+ rc = mdb_update_key(csrc, &key);
+ if (rc) {
+ csrc->mc_top++;
+ return rc;
+ }
+ }
+ csrc->mc_top++;
+
+ psrc = csrc->mc_pg[csrc->mc_top];
+ /* If not operating on FreeDB, allow this page to be reused
+ * in this txn. Otherwise just add to free list.
+ */
+ rc = mdb_page_loose(csrc, psrc);
+ if (rc)
+ return rc;
+ if (IS_LEAF(psrc))
+ csrc->mc_db->md_leaf_pages--;
+ else
+ csrc->mc_db->md_branch_pages--;
+ {
+ /* Adjust other cursors pointing to mp */
+ MDB_cursor *m2, *m3;
+ MDB_dbi dbi = csrc->mc_dbi;
+ unsigned int top = csrc->mc_top;
+
+ for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
+ if (csrc->mc_flags & C_SUB)
+ m3 = &m2->mc_xcursor->mx_cursor;
+ else
+ m3 = m2;
+ if (m3 == csrc) continue;
+ if (m3->mc_snum < csrc->mc_snum) continue;
+ if (m3->mc_pg[top] == psrc) {
+ m3->mc_pg[top] = pdst;
+ m3->mc_ki[top] += nkeys;
+ m3->mc_ki[top-1] = cdst->mc_ki[top-1];
+ } else if (m3->mc_pg[top-1] == csrc->mc_pg[top-1] &&
+ m3->mc_ki[top-1] > csrc->mc_ki[top-1]) {
+ m3->mc_ki[top-1]--;
+ }
+ if (IS_LEAF(psrc))
+ XCURSOR_REFRESH(m3, top, m3->mc_pg[top]);
+ }
+ }
+ {
+ unsigned int snum = cdst->mc_snum;
+ uint16_t depth = cdst->mc_db->md_depth;
+ mdb_cursor_pop(cdst);
+ rc = mdb_rebalance(cdst);
+ /* Did the tree height change? */
+ if (depth != cdst->mc_db->md_depth)
+ snum += cdst->mc_db->md_depth - depth;
+ cdst->mc_snum = snum;
+ cdst->mc_top = snum-1;
+ }
+ return rc;
+}
+
+/** Copy the contents of a cursor.
+ * @param[in] csrc The cursor to copy from.
+ * @param[out] cdst The cursor to copy to.
+ */
+static void
+mdb_cursor_copy(const MDB_cursor *csrc, MDB_cursor *cdst)
+{
+ unsigned int i;
+
+ cdst->mc_txn = csrc->mc_txn;
+ cdst->mc_dbi = csrc->mc_dbi;
+ cdst->mc_db = csrc->mc_db;
+ cdst->mc_dbx = csrc->mc_dbx;
+ cdst->mc_snum = csrc->mc_snum;
+ cdst->mc_top = csrc->mc_top;
+ cdst->mc_flags = csrc->mc_flags;
+
+ for (i=0; i<csrc->mc_snum; i++) {
+ cdst->mc_pg[i] = csrc->mc_pg[i];
+ cdst->mc_ki[i] = csrc->mc_ki[i];
+ }
+}
+
+/** Rebalance the tree after a delete operation.
+ * @param[in] mc Cursor pointing to the page where rebalancing
+ * should begin.
+ * @return 0 on success, non-zero on failure.
+ */
+static int
+mdb_rebalance(MDB_cursor *mc)
+{
+ MDB_node *node;
+ int rc, fromleft;
+ unsigned int ptop, minkeys, thresh;
+ MDB_cursor mn;
+ indx_t oldki;
+
+ if (IS_BRANCH(mc->mc_pg[mc->mc_top])) {
+ minkeys = 2;
+ thresh = 1;
+ } else {
+ minkeys = 1;
+ thresh = FILL_THRESHOLD;
+ }
+ DPRINTF(("rebalancing %s page %"Z"u (has %u keys, %.1f%% full)",
+ IS_LEAF(mc->mc_pg[mc->mc_top]) ? "leaf" : "branch",
+ mdb_dbg_pgno(mc->mc_pg[mc->mc_top]), NUMKEYS(mc->mc_pg[mc->mc_top]),
+ (float)PAGEFILL(mc->mc_txn->mt_env, mc->mc_pg[mc->mc_top]) / 10));
+
+ if (PAGEFILL(mc->mc_txn->mt_env, mc->mc_pg[mc->mc_top]) >= thresh &&
+ NUMKEYS(mc->mc_pg[mc->mc_top]) >= minkeys) {
+ DPRINTF(("no need to rebalance page %"Z"u, above fill threshold",
+ mdb_dbg_pgno(mc->mc_pg[mc->mc_top])));
+ return MDB_SUCCESS;
+ }
+
+ if (mc->mc_snum < 2) {
+ MDB_page *mp = mc->mc_pg[0];
+ if (IS_SUBP(mp)) {
+ DPUTS("Can't rebalance a subpage, ignoring");
+ return MDB_SUCCESS;
+ }
+ if (NUMKEYS(mp) == 0) {
+ DPUTS("tree is completely empty");
+ mc->mc_db->md_root = P_INVALID;
+ mc->mc_db->md_depth = 0;
+ mc->mc_db->md_leaf_pages = 0;
+ rc = mdb_midl_append(&mc->mc_txn->mt_free_pgs, mp->mp_pgno);
+ if (rc)
+ return rc;
+ /* Adjust cursors pointing to mp */
+ mc->mc_snum = 0;
+ mc->mc_top = 0;
+ mc->mc_flags &= ~C_INITIALIZED;
+ {
+ MDB_cursor *m2, *m3;
+ MDB_dbi dbi = mc->mc_dbi;
+
+ for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
+ if (mc->mc_flags & C_SUB)
+ m3 = &m2->mc_xcursor->mx_cursor;
+ else
+ m3 = m2;
+ if (!(m3->mc_flags & C_INITIALIZED) || (m3->mc_snum < mc->mc_snum))
+ continue;
+ if (m3->mc_pg[0] == mp) {
+ m3->mc_snum = 0;
+ m3->mc_top = 0;
+ m3->mc_flags &= ~C_INITIALIZED;
+ }
+ }
+ }
+ } else if (IS_BRANCH(mp) && NUMKEYS(mp) == 1) {
+ int i;
+ DPUTS("collapsing root page!");
+ rc = mdb_midl_append(&mc->mc_txn->mt_free_pgs, mp->mp_pgno);
+ if (rc)
+ return rc;
+ mc->mc_db->md_root = NODEPGNO(NODEPTR(mp, 0));
+ rc = mdb_page_get(mc, mc->mc_db->md_root, &mc->mc_pg[0], NULL);
+ if (rc)
+ return rc;
+ mc->mc_db->md_depth--;
+ mc->mc_db->md_branch_pages--;
+ mc->mc_ki[0] = mc->mc_ki[1];
+ for (i = 1; i<mc->mc_db->md_depth; i++) {
+ mc->mc_pg[i] = mc->mc_pg[i+1];
+ mc->mc_ki[i] = mc->mc_ki[i+1];
+ }
+ {
+ /* Adjust other cursors pointing to mp */
+ MDB_cursor *m2, *m3;
+ MDB_dbi dbi = mc->mc_dbi;
+
+ for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
+ if (mc->mc_flags & C_SUB)
+ m3 = &m2->mc_xcursor->mx_cursor;
+ else
+ m3 = m2;
+ if (m3 == mc) continue;
+ if (!(m3->mc_flags & C_INITIALIZED))
+ continue;
+ if (m3->mc_pg[0] == mp) {
+ for (i=0; i<mc->mc_db->md_depth; i++) {
+ m3->mc_pg[i] = m3->mc_pg[i+1];
+ m3->mc_ki[i] = m3->mc_ki[i+1];
+ }
+ m3->mc_snum--;
+ m3->mc_top--;
+ }
+ }
+ }
+ } else
+ DPUTS("root page doesn't need rebalancing");
+ return MDB_SUCCESS;
+ }
+
+ /* The parent (branch page) must have at least 2 pointers,
+ * otherwise the tree is invalid.
+ */
+ ptop = mc->mc_top-1;
+ mdb_cassert(mc, NUMKEYS(mc->mc_pg[ptop]) > 1);
+
+ /* Leaf page fill factor is below the threshold.
+ * Try to move keys from left or right neighbor, or
+ * merge with a neighbor page.
+ */
+
+ /* Find neighbors.
+ */
+ mdb_cursor_copy(mc, &mn);
+ mn.mc_xcursor = NULL;
+
+ oldki = mc->mc_ki[mc->mc_top];
+ if (mc->mc_ki[ptop] == 0) {
+ /* We're the leftmost leaf in our parent.
+ */
+ DPUTS("reading right neighbor");
+ mn.mc_ki[ptop]++;
+ node = NODEPTR(mc->mc_pg[ptop], mn.mc_ki[ptop]);
+ rc = mdb_page_get(mc, NODEPGNO(node), &mn.mc_pg[mn.mc_top], NULL);
+ if (rc)
+ return rc;
+ mn.mc_ki[mn.mc_top] = 0;
+ mc->mc_ki[mc->mc_top] = NUMKEYS(mc->mc_pg[mc->mc_top]);
+ fromleft = 0;
+ } else {
+ /* There is at least one neighbor to the left.
+ */
+ DPUTS("reading left neighbor");
+ mn.mc_ki[ptop]--;
+ node = NODEPTR(mc->mc_pg[ptop], mn.mc_ki[ptop]);
+ rc = mdb_page_get(mc, NODEPGNO(node), &mn.mc_pg[mn.mc_top], NULL);
+ if (rc)
+ return rc;
+ mn.mc_ki[mn.mc_top] = NUMKEYS(mn.mc_pg[mn.mc_top]) - 1;
+ mc->mc_ki[mc->mc_top] = 0;
+ fromleft = 1;
+ }
+
+ DPRINTF(("found neighbor page %"Z"u (%u keys, %.1f%% full)",
+ mn.mc_pg[mn.mc_top]->mp_pgno, NUMKEYS(mn.mc_pg[mn.mc_top]),
+ (float)PAGEFILL(mc->mc_txn->mt_env, mn.mc_pg[mn.mc_top]) / 10));
+
+ /* If the neighbor page is above threshold and has enough keys,
+ * move one key from it. Otherwise we should try to merge them.
+ * (A branch page must never have less than 2 keys.)
+ */
+ if (PAGEFILL(mc->mc_txn->mt_env, mn.mc_pg[mn.mc_top]) >= thresh && NUMKEYS(mn.mc_pg[mn.mc_top]) > minkeys) {
+ rc = mdb_node_move(&mn, mc, fromleft);
+ if (fromleft) {
+ /* if we inserted on left, bump position up */
+ oldki++;
+ }
+ } else {
+ if (!fromleft) {
+ rc = mdb_page_merge(&mn, mc);
+ } else {
+ oldki += NUMKEYS(mn.mc_pg[mn.mc_top]);
+ mn.mc_ki[mn.mc_top] += mc->mc_ki[mn.mc_top] + 1;
+ /* We want mdb_rebalance to find mn when doing fixups */
+ WITH_CURSOR_TRACKING(mn,
+ rc = mdb_page_merge(mc, &mn));
+ mdb_cursor_copy(&mn, mc);
+ }
+ mc->mc_flags &= ~C_EOF;
+ }
+ mc->mc_ki[mc->mc_top] = oldki;
+ return rc;
+}
+
+/** Complete a delete operation started by #mdb_cursor_del(). */
+static int
+mdb_cursor_del0(MDB_cursor *mc)
+{
+ int rc;
+ MDB_page *mp;
+ indx_t ki;
+ unsigned int nkeys;
+ MDB_cursor *m2, *m3;
+ MDB_dbi dbi = mc->mc_dbi;
+
+ ki = mc->mc_ki[mc->mc_top];
+ mp = mc->mc_pg[mc->mc_top];
+ mdb_node_del(mc, mc->mc_db->md_pad);
+ mc->mc_db->md_entries--;
+ {
+ /* Adjust other cursors pointing to mp */
+ for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
+ m3 = (mc->mc_flags & C_SUB) ? &m2->mc_xcursor->mx_cursor : m2;
+ if (! (m2->mc_flags & m3->mc_flags & C_INITIALIZED))
+ continue;
+ if (m3 == mc || m3->mc_snum < mc->mc_snum)
+ continue;
+ if (m3->mc_pg[mc->mc_top] == mp) {
+ if (m3->mc_ki[mc->mc_top] == ki) {
+ m3->mc_flags |= C_DEL;
+ if (mc->mc_db->md_flags & MDB_DUPSORT) {
+ /* Sub-cursor referred into dataset which is gone */
+ m3->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF);
+ }
+ continue;
+ } else if (m3->mc_ki[mc->mc_top] > ki) {
+ m3->mc_ki[mc->mc_top]--;
+ }
+ XCURSOR_REFRESH(m3, mc->mc_top, mp);
+ }
+ }
+ }
+ rc = mdb_rebalance(mc);
+ if (rc)
+ goto fail;
+
+ /* DB is totally empty now, just bail out.
+ * Other cursors adjustments were already done
+ * by mdb_rebalance and aren't needed here.
+ */
+ if (!mc->mc_snum) {
+ mc->mc_flags |= C_EOF;
+ return rc;
+ }
+
+ mp = mc->mc_pg[mc->mc_top];
+ nkeys = NUMKEYS(mp);
+
+ /* Adjust other cursors pointing to mp */
+ for (m2 = mc->mc_txn->mt_cursors[dbi]; !rc && m2; m2=m2->mc_next) {
+ m3 = (mc->mc_flags & C_SUB) ? &m2->mc_xcursor->mx_cursor : m2;
+ if (!(m2->mc_flags & m3->mc_flags & C_INITIALIZED))
+ continue;
+ if (m3->mc_snum < mc->mc_snum)
+ continue;
+ if (m3->mc_pg[mc->mc_top] == mp) {
+ if (m3->mc_ki[mc->mc_top] >= mc->mc_ki[mc->mc_top]) {
+ /* if m3 points past last node in page, find next sibling */
+ if (m3->mc_ki[mc->mc_top] >= nkeys) {
+ rc = mdb_cursor_sibling(m3, 1);
+ if (rc == MDB_NOTFOUND) {
+ m3->mc_flags |= C_EOF;
+ rc = MDB_SUCCESS;
+ continue;
+ }
+ if (rc)
+ goto fail;
+ }
+ if (m3->mc_xcursor && !(m3->mc_flags & C_EOF)) {
+ MDB_node *node = NODEPTR(m3->mc_pg[m3->mc_top], m3->mc_ki[m3->mc_top]);
+ /* If this node has dupdata, it may need to be reinited
+ * because its data has moved.
+ * If the xcursor was not initd it must be reinited.
+ * Else if node points to a subDB, nothing is needed.
+ * Else (xcursor was initd, not a subDB) needs mc_pg[0] reset.
+ */
+ if (node->mn_flags & F_DUPDATA) {
+ if (m3->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) {
+ if (!(node->mn_flags & F_SUBDATA))
+ m3->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(node);
+ } else {
+ mdb_xcursor_init1(m3, node);
+ rc = mdb_cursor_first(&m3->mc_xcursor->mx_cursor, NULL, NULL);
+ if (rc)
+ goto fail;
+ }
+ }
+ m3->mc_xcursor->mx_cursor.mc_flags |= C_DEL;
+ }
+ }
+ }
+ }
+ mc->mc_flags |= C_DEL;
+
+fail:
+ if (rc)
+ mc->mc_txn->mt_flags |= MDB_TXN_ERROR;
+ return rc;
+}
+
+int
+mdb_del(MDB_txn *txn, MDB_dbi dbi,
+ MDB_val *key, MDB_val *data)
+{
+ if (!key || !TXN_DBI_EXIST(txn, dbi, DB_USRVALID))
+ return EINVAL;
+
+ if (txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_BLOCKED))
+ return (txn->mt_flags & MDB_TXN_RDONLY) ? EACCES : MDB_BAD_TXN;
+
+ if (!F_ISSET(txn->mt_dbs[dbi].md_flags, MDB_DUPSORT)) {
+ /* must ignore any data */
+ data = NULL;
+ }
+
+ return mdb_del0(txn, dbi, key, data, 0);
+}
+
+static int
+mdb_del0(MDB_txn *txn, MDB_dbi dbi,
+ MDB_val *key, MDB_val *data, unsigned flags)
+{
+ MDB_cursor mc;
+ MDB_xcursor mx;
+ MDB_cursor_op op;
+ MDB_val rdata, *xdata;
+ int rc, exact = 0;
+ DKBUF;
+
+ DPRINTF(("====> delete db %u key [%s]", dbi, DKEY(key)));
+
+ mdb_cursor_init(&mc, txn, dbi, &mx);
+
+ if (data) {
+ op = MDB_GET_BOTH;
+ rdata = *data;
+ xdata = &rdata;
+ } else {
+ op = MDB_SET;
+ xdata = NULL;
+ flags |= MDB_NODUPDATA;
+ }
+ rc = mdb_cursor_set(&mc, key, xdata, op, &exact);
+ if (rc == 0) {
+ /* let mdb_page_split know about this cursor if needed:
+ * delete will trigger a rebalance; if it needs to move
+ * a node from one page to another, it will have to
+ * update the parent's separator key(s). If the new sepkey
+ * is larger than the current one, the parent page may
+ * run out of space, triggering a split. We need this
+ * cursor to be consistent until the end of the rebalance.
+ */
+ mc.mc_flags |= C_UNTRACK;
+ mc.mc_next = txn->mt_cursors[dbi];
+ txn->mt_cursors[dbi] = &mc;
+ rc = mdb_cursor_del(&mc, flags);
+ txn->mt_cursors[dbi] = mc.mc_next;
+ }
+ return rc;
+}
+
+/** Split a page and insert a new node.
+ * Set #MDB_TXN_ERROR on failure.
+ * @param[in,out] mc Cursor pointing to the page and desired insertion index.
+ * The cursor will be updated to point to the actual page and index where
+ * the node got inserted after the split.
+ * @param[in] newkey The key for the newly inserted node.
+ * @param[in] newdata The data for the newly inserted node.
+ * @param[in] newpgno The page number, if the new node is a branch node.
+ * @param[in] nflags The #NODE_ADD_FLAGS for the new node.
+ * @return 0 on success, non-zero on failure.
+ */
+static int
+mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno,
+ unsigned int nflags)
+{
+ unsigned int flags;
+ int rc = MDB_SUCCESS, new_root = 0, did_split = 0;
+ indx_t newindx;
+ pgno_t pgno = 0;
+ int i, j, split_indx, nkeys, pmax;
+ MDB_env *env = mc->mc_txn->mt_env;
+ MDB_node *node;
+ MDB_val sepkey, rkey, xdata, *rdata = &xdata;
+ MDB_page *copy = NULL;
+ MDB_page *mp, *rp, *pp;
+ int ptop;
+ MDB_cursor mn;
+ DKBUF;
+
+ mp = mc->mc_pg[mc->mc_top];
+ newindx = mc->mc_ki[mc->mc_top];
+ nkeys = NUMKEYS(mp);
+
+ DPRINTF(("-----> splitting %s page %"Z"u and adding [%s] at index %i/%i",
+ IS_LEAF(mp) ? "leaf" : "branch", mp->mp_pgno,
+ DKEY(newkey), mc->mc_ki[mc->mc_top], nkeys));
+
+ /* Create a right sibling. */
+ if ((rc = mdb_page_new(mc, mp->mp_flags, 1, &rp)))
+ return rc;
+ rp->mp_pad = mp->mp_pad;
+ DPRINTF(("new right sibling: page %"Z"u", rp->mp_pgno));
+
+ /* Usually when splitting the root page, the cursor
+ * height is 1. But when called from mdb_update_key,
+ * the cursor height may be greater because it walks
+ * up the stack while finding the branch slot to update.
+ */
+ if (mc->mc_top < 1) {
+ if ((rc = mdb_page_new(mc, P_BRANCH, 1, &pp)))
+ goto done;
+ /* shift current top to make room for new parent */
+ for (i=mc->mc_snum; i>0; i--) {
+ mc->mc_pg[i] = mc->mc_pg[i-1];
+ mc->mc_ki[i] = mc->mc_ki[i-1];
+ }
+ mc->mc_pg[0] = pp;
+ mc->mc_ki[0] = 0;
+ mc->mc_db->md_root = pp->mp_pgno;
+ DPRINTF(("root split! new root = %"Z"u", pp->mp_pgno));
+ new_root = mc->mc_db->md_depth++;
+
+ /* Add left (implicit) pointer. */
+ if ((rc = mdb_node_add(mc, 0, NULL, NULL, mp->mp_pgno, 0)) != MDB_SUCCESS) {
+ /* undo the pre-push */
+ mc->mc_pg[0] = mc->mc_pg[1];
+ mc->mc_ki[0] = mc->mc_ki[1];
+ mc->mc_db->md_root = mp->mp_pgno;
+ mc->mc_db->md_depth--;
+ goto done;
+ }
+ mc->mc_snum++;
+ mc->mc_top++;
+ ptop = 0;
+ } else {
+ ptop = mc->mc_top-1;
+ DPRINTF(("parent branch page is %"Z"u", mc->mc_pg[ptop]->mp_pgno));
+ }
+
+ mdb_cursor_copy(mc, &mn);
+ mn.mc_xcursor = NULL;
+ mn.mc_pg[mn.mc_top] = rp;
+ mn.mc_ki[ptop] = mc->mc_ki[ptop]+1;
+
+ if (nflags & MDB_APPEND) {
+ mn.mc_ki[mn.mc_top] = 0;
+ sepkey = *newkey;
+ split_indx = newindx;
+ nkeys = 0;
+ } else {
+
+ split_indx = (nkeys+1) / 2;
+
+ if (IS_LEAF2(rp)) {
+ char *split, *ins;
+ int x;
+ unsigned int lsize, rsize, ksize;
+ /* Move half of the keys to the right sibling */
+ x = mc->mc_ki[mc->mc_top] - split_indx;
+ ksize = mc->mc_db->md_pad;
+ split = LEAF2KEY(mp, split_indx, ksize);
+ rsize = (nkeys - split_indx) * ksize;
+ lsize = (nkeys - split_indx) * sizeof(indx_t);
+ mp->mp_lower -= lsize;
+ rp->mp_lower += lsize;
+ mp->mp_upper += rsize - lsize;
+ rp->mp_upper -= rsize - lsize;
+ sepkey.mv_size = ksize;
+ if (newindx == split_indx) {
+ sepkey.mv_data = newkey->mv_data;
+ } else {
+ sepkey.mv_data = split;
+ }
+ if (x<0) {
+ ins = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], ksize);
+ memcpy(rp->mp_ptrs, split, rsize);
+ sepkey.mv_data = rp->mp_ptrs;
+ memmove(ins+ksize, ins, (split_indx - mc->mc_ki[mc->mc_top]) * ksize);
+ memcpy(ins, newkey->mv_data, ksize);
+ mp->mp_lower += sizeof(indx_t);
+ mp->mp_upper -= ksize - sizeof(indx_t);
+ } else {
+ if (x)
+ memcpy(rp->mp_ptrs, split, x * ksize);
+ ins = LEAF2KEY(rp, x, ksize);
+ memcpy(ins, newkey->mv_data, ksize);
+ memcpy(ins+ksize, split + x * ksize, rsize - x * ksize);
+ rp->mp_lower += sizeof(indx_t);
+ rp->mp_upper -= ksize - sizeof(indx_t);
+ mc->mc_ki[mc->mc_top] = x;
+ }
+ } else {
+ int psize, nsize, k;
+ /* Maximum free space in an empty page */
+ pmax = env->me_psize - PAGEHDRSZ;
+ if (IS_LEAF(mp))
+ nsize = mdb_leaf_size(env, newkey, newdata);
+ else
+ nsize = mdb_branch_size(env, newkey);
+ nsize = EVEN(nsize);
+
+ /* grab a page to hold a temporary copy */
+ copy = mdb_page_malloc(mc->mc_txn, 1);
+ if (copy == NULL) {
+ rc = ENOMEM;
+ goto done;
+ }
+ copy->mp_pgno = mp->mp_pgno;
+ copy->mp_flags = mp->mp_flags;
+ copy->mp_lower = (PAGEHDRSZ-PAGEBASE);
+ copy->mp_upper = env->me_psize - PAGEBASE;
+
+ /* prepare to insert */
+ for (i=0, j=0; i<nkeys; i++) {
+ if (i == newindx) {
+ copy->mp_ptrs[j++] = 0;
+ }
+ copy->mp_ptrs[j++] = mp->mp_ptrs[i];
+ }
+
+ /* When items are relatively large the split point needs
+ * to be checked, because being off-by-one will make the
+ * difference between success or failure in mdb_node_add.
+ *
+ * It's also relevant if a page happens to be laid out
+ * such that one half of its nodes are all "small" and
+ * the other half of its nodes are "large." If the new
+ * item is also "large" and falls on the half with
+ * "large" nodes, it also may not fit.
+ *
+ * As a final tweak, if the new item goes on the last
+ * spot on the page (and thus, onto the new page), bias
+ * the split so the new page is emptier than the old page.
+ * This yields better packing during sequential inserts.
+ */
+ if (nkeys < 32 || nsize > pmax/16 || newindx >= nkeys) {
+ /* Find split point */
+ psize = 0;
+ if (newindx <= split_indx || newindx >= nkeys) {
+ i = 0; j = 1;
+ k = newindx >= nkeys ? nkeys : split_indx+1+IS_LEAF(mp);
+ } else {
+ i = nkeys; j = -1;
+ k = split_indx-1;
+ }
+ for (; i!=k; i+=j) {
+ if (i == newindx) {
+ psize += nsize;
+ node = NULL;
+ } else {
+ node = (MDB_node *)((char *)mp + copy->mp_ptrs[i] + PAGEBASE);
+ psize += NODESIZE + NODEKSZ(node) + sizeof(indx_t);
+ if (IS_LEAF(mp)) {
+ if (F_ISSET(node->mn_flags, F_BIGDATA))
+ psize += sizeof(pgno_t);
+ else
+ psize += NODEDSZ(node);
+ }
+ psize = EVEN(psize);
+ }
+ if (psize > pmax || i == k-j) {
+ split_indx = i + (j<0);
+ break;
+ }
+ }
+ }
+ if (split_indx == newindx) {
+ sepkey.mv_size = newkey->mv_size;
+ sepkey.mv_data = newkey->mv_data;
+ } else {
+ node = (MDB_node *)((char *)mp + copy->mp_ptrs[split_indx] + PAGEBASE);
+ sepkey.mv_size = node->mn_ksize;
+ sepkey.mv_data = NODEKEY(node);
+ }
+ }
+ }
+
+ DPRINTF(("separator is %d [%s]", split_indx, DKEY(&sepkey)));
+
+ /* Copy separator key to the parent.
+ */
+ if (SIZELEFT(mn.mc_pg[ptop]) < mdb_branch_size(env, &sepkey)) {
+ int snum = mc->mc_snum;
+ mn.mc_snum--;
+ mn.mc_top--;
+ did_split = 1;
+ /* We want other splits to find mn when doing fixups */
+ WITH_CURSOR_TRACKING(mn,
+ rc = mdb_page_split(&mn, &sepkey, NULL, rp->mp_pgno, 0));
+ if (rc)
+ goto done;
+
+ /* root split? */
+ if (mc->mc_snum > snum) {
+ ptop++;
+ }
+ /* Right page might now have changed parent.
+ * Check if left page also changed parent.
+ */
+ if (mn.mc_pg[ptop] != mc->mc_pg[ptop] &&
+ mc->mc_ki[ptop] >= NUMKEYS(mc->mc_pg[ptop])) {
+ for (i=0; i<ptop; i++) {
+ mc->mc_pg[i] = mn.mc_pg[i];
+ mc->mc_ki[i] = mn.mc_ki[i];
+ }
+ mc->mc_pg[ptop] = mn.mc_pg[ptop];
+ if (mn.mc_ki[ptop]) {
+ mc->mc_ki[ptop] = mn.mc_ki[ptop] - 1;
+ } else {
+ /* find right page's left sibling */
+ mc->mc_ki[ptop] = mn.mc_ki[ptop];
+ mdb_cursor_sibling(mc, 0);
+ }
+ }
+ } else {
+ mn.mc_top--;
+ rc = mdb_node_add(&mn, mn.mc_ki[ptop], &sepkey, NULL, rp->mp_pgno, 0);
+ mn.mc_top++;
+ }
+ if (rc != MDB_SUCCESS) {
+ goto done;
+ }
+ if (nflags & MDB_APPEND) {
+ mc->mc_pg[mc->mc_top] = rp;
+ mc->mc_ki[mc->mc_top] = 0;
+ rc = mdb_node_add(mc, 0, newkey, newdata, newpgno, nflags);
+ if (rc)
+ goto done;
+ for (i=0; i<mc->mc_top; i++)
+ mc->mc_ki[i] = mn.mc_ki[i];
+ } else if (!IS_LEAF2(mp)) {
+ /* Move nodes */
+ mc->mc_pg[mc->mc_top] = rp;
+ i = split_indx;
+ j = 0;
+ do {
+ if (i == newindx) {
+ rkey.mv_data = newkey->mv_data;
+ rkey.mv_size = newkey->mv_size;
+ if (IS_LEAF(mp)) {
+ rdata = newdata;
+ } else
+ pgno = newpgno;
+ flags = nflags;
+ /* Update index for the new key. */
+ mc->mc_ki[mc->mc_top] = j;
+ } else {
+ node = (MDB_node *)((char *)mp + copy->mp_ptrs[i] + PAGEBASE);
+ rkey.mv_data = NODEKEY(node);
+ rkey.mv_size = node->mn_ksize;
+ if (IS_LEAF(mp)) {
+ xdata.mv_data = NODEDATA(node);
+ xdata.mv_size = NODEDSZ(node);
+ rdata = &xdata;
+ } else
+ pgno = NODEPGNO(node);
+ flags = node->mn_flags;
+ }
+
+ if (!IS_LEAF(mp) && j == 0) {
+ /* First branch index doesn't need key data. */
+ rkey.mv_size = 0;
+ }
+
+ rc = mdb_node_add(mc, j, &rkey, rdata, pgno, flags);
+ if (rc)
+ goto done;
+ if (i == nkeys) {
+ i = 0;
+ j = 0;
+ mc->mc_pg[mc->mc_top] = copy;
+ } else {
+ i++;
+ j++;
+ }
+ } while (i != split_indx);
+
+ nkeys = NUMKEYS(copy);
+ for (i=0; i<nkeys; i++)
+ mp->mp_ptrs[i] = copy->mp_ptrs[i];
+ mp->mp_lower = copy->mp_lower;
+ mp->mp_upper = copy->mp_upper;
+ memcpy(NODEPTR(mp, nkeys-1), NODEPTR(copy, nkeys-1),
+ env->me_psize - copy->mp_upper - PAGEBASE);
+
+ /* reset back to original page */
+ if (newindx < split_indx) {
+ mc->mc_pg[mc->mc_top] = mp;
+ } else {
+ mc->mc_pg[mc->mc_top] = rp;
+ mc->mc_ki[ptop]++;
+ /* Make sure mc_ki is still valid.
+ */
+ if (mn.mc_pg[ptop] != mc->mc_pg[ptop] &&
+ mc->mc_ki[ptop] >= NUMKEYS(mc->mc_pg[ptop])) {
+ for (i=0; i<=ptop; i++) {
+ mc->mc_pg[i] = mn.mc_pg[i];
+ mc->mc_ki[i] = mn.mc_ki[i];
+ }
+ }
+ }
+ if (nflags & MDB_RESERVE) {
+ node = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]);
+ if (!(node->mn_flags & F_BIGDATA))
+ newdata->mv_data = NODEDATA(node);
+ }
+ } else {
+ if (newindx >= split_indx) {
+ mc->mc_pg[mc->mc_top] = rp;
+ mc->mc_ki[ptop]++;
+ /* Make sure mc_ki is still valid.
+ */
+ if (mn.mc_pg[ptop] != mc->mc_pg[ptop] &&
+ mc->mc_ki[ptop] >= NUMKEYS(mc->mc_pg[ptop])) {
+ for (i=0; i<=ptop; i++) {
+ mc->mc_pg[i] = mn.mc_pg[i];
+ mc->mc_ki[i] = mn.mc_ki[i];
+ }
+ }
+ }
+ }
+
+ {
+ /* Adjust other cursors pointing to mp */
+ MDB_cursor *m2, *m3;
+ MDB_dbi dbi = mc->mc_dbi;
+ nkeys = NUMKEYS(mp);
+
+ for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) {
+ if (mc->mc_flags & C_SUB)
+ m3 = &m2->mc_xcursor->mx_cursor;
+ else
+ m3 = m2;
+ if (m3 == mc)
+ continue;
+ if (!(m2->mc_flags & m3->mc_flags & C_INITIALIZED))
+ continue;
+ if (new_root) {
+ int k;
+ /* sub cursors may be on different DB */
+ if (m3->mc_pg[0] != mp)
+ continue;
+ /* root split */
+ for (k=new_root; k>=0; k--) {
+ m3->mc_ki[k+1] = m3->mc_ki[k];
+ m3->mc_pg[k+1] = m3->mc_pg[k];
+ }
+ if (m3->mc_ki[0] >= nkeys) {
+ m3->mc_ki[0] = 1;
+ } else {
+ m3->mc_ki[0] = 0;
+ }
+ m3->mc_pg[0] = mc->mc_pg[0];
+ m3->mc_snum++;
+ m3->mc_top++;
+ }
+ if (m3->mc_top >= mc->mc_top && m3->mc_pg[mc->mc_top] == mp) {
+ if (m3->mc_ki[mc->mc_top] >= newindx && !(nflags & MDB_SPLIT_REPLACE))
+ m3->mc_ki[mc->mc_top]++;
+ if (m3->mc_ki[mc->mc_top] >= nkeys) {
+ m3->mc_pg[mc->mc_top] = rp;
+ m3->mc_ki[mc->mc_top] -= nkeys;
+ for (i=0; i<mc->mc_top; i++) {
+ m3->mc_ki[i] = mn.mc_ki[i];
+ m3->mc_pg[i] = mn.mc_pg[i];
+ }
+ }
+ } else if (!did_split && m3->mc_top >= ptop && m3->mc_pg[ptop] == mc->mc_pg[ptop] &&
+ m3->mc_ki[ptop] >= mc->mc_ki[ptop]) {
+ m3->mc_ki[ptop]++;
+ }
+ if (IS_LEAF(mp))
+ XCURSOR_REFRESH(m3, mc->mc_top, m3->mc_pg[mc->mc_top]);
+ }
+ }
+ DPRINTF(("mp left: %d, rp left: %d", SIZELEFT(mp), SIZELEFT(rp)));
+
+done:
+ if (copy) /* tmp page */
+ mdb_page_free(env, copy);
+ if (rc)
+ mc->mc_txn->mt_flags |= MDB_TXN_ERROR;
+ return rc;
+}
+
+int
+mdb_put(MDB_txn *txn, MDB_dbi dbi,
+ MDB_val *key, MDB_val *data, unsigned int flags)
+{
+ MDB_cursor mc;
+ MDB_xcursor mx;
+ int rc;
+
+ if (!key || !data || !TXN_DBI_EXIST(txn, dbi, DB_USRVALID))
+ return EINVAL;
+
+ if (flags & ~(MDB_NOOVERWRITE|MDB_NODUPDATA|MDB_RESERVE|MDB_APPEND|MDB_APPENDDUP))
+ return EINVAL;
+
+ if (txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_BLOCKED))
+ return (txn->mt_flags & MDB_TXN_RDONLY) ? EACCES : MDB_BAD_TXN;
+
+ mdb_cursor_init(&mc, txn, dbi, &mx);
+ mc.mc_next = txn->mt_cursors[dbi];
+ txn->mt_cursors[dbi] = &mc;
+ rc = mdb_cursor_put(&mc, key, data, flags);
+ txn->mt_cursors[dbi] = mc.mc_next;
+ return rc;
+}
+
+#ifndef MDB_WBUF
+#define MDB_WBUF (1024*1024)
+#endif
+#define MDB_EOF 0x10 /**< #mdb_env_copyfd1() is done reading */
+
+ /** State needed for a double-buffering compacting copy. */
+typedef struct mdb_copy {
+ MDB_env *mc_env;
+ MDB_txn *mc_txn;
+ pthread_mutex_t mc_mutex;
+ pthread_cond_t mc_cond; /**< Condition variable for #mc_new */
+ char *mc_wbuf[2];
+ char *mc_over[2];
+ int mc_wlen[2];
+ int mc_olen[2];
+ pgno_t mc_next_pgno;
+ HANDLE mc_fd;
+ int mc_toggle; /**< Buffer number in provider */
+ int mc_new; /**< (0-2 buffers to write) | (#MDB_EOF at end) */
+ /** Error code. Never cleared if set. Both threads can set nonzero
+ * to fail the copy. Not mutex-protected, LMDB expects atomic int.
+ */
+ volatile int mc_error;
+} mdb_copy;
+
+ /** Dedicated writer thread for compacting copy. */
+static THREAD_RET ESECT CALL_CONV
+mdb_env_copythr(void *arg)
+{
+ mdb_copy *my = arg;
+ char *ptr;
+ int toggle = 0, wsize, rc;
+#ifdef _WIN32
+ DWORD len;
+#define DO_WRITE(rc, fd, ptr, w2, len) rc = WriteFile(fd, ptr, w2, &len, NULL)
+#else
+ int len;
+#define DO_WRITE(rc, fd, ptr, w2, len) len = write(fd, ptr, w2); rc = (len >= 0)
+#ifdef SIGPIPE
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set, SIGPIPE);
+ if ((rc = pthread_sigmask(SIG_BLOCK, &set, NULL)) != 0)
+ my->mc_error = rc;
+#endif
+#endif
+
+ pthread_mutex_lock(&my->mc_mutex);
+ for(;;) {
+ while (!my->mc_new)
+ pthread_cond_wait(&my->mc_cond, &my->mc_mutex);
+ if (my->mc_new == 0 + MDB_EOF) /* 0 buffers, just EOF */
+ break;
+ wsize = my->mc_wlen[toggle];
+ ptr = my->mc_wbuf[toggle];
+again:
+ rc = MDB_SUCCESS;
+ while (wsize > 0 && !my->mc_error) {
+ DO_WRITE(rc, my->mc_fd, ptr, wsize, len);
+ if (!rc) {
+ rc = ErrCode();
+#if defined(SIGPIPE) && !defined(_WIN32)
+ if (rc == EPIPE) {
+ /* Collect the pending SIGPIPE, otherwise at least OS X
+ * gives it to the process on thread-exit (ITS#8504).
+ */
+ int tmp;
+ sigwait(&set, &tmp);
+ }
+#endif
+ break;
+ } else if (len > 0) {
+ rc = MDB_SUCCESS;
+ ptr += len;
+ wsize -= len;
+ continue;
+ } else {
+ rc = EIO;
+ break;
+ }
+ }
+ if (rc) {
+ my->mc_error = rc;
+ }
+ /* If there's an overflow page tail, write it too */
+ if (my->mc_olen[toggle]) {
+ wsize = my->mc_olen[toggle];
+ ptr = my->mc_over[toggle];
+ my->mc_olen[toggle] = 0;
+ goto again;
+ }
+ my->mc_wlen[toggle] = 0;
+ toggle ^= 1;
+ /* Return the empty buffer to provider */
+ my->mc_new--;
+ pthread_cond_signal(&my->mc_cond);
+ }
+ pthread_mutex_unlock(&my->mc_mutex);
+ return (THREAD_RET)0;
+#undef DO_WRITE
+}
+
+ /** Give buffer and/or #MDB_EOF to writer thread, await unused buffer.
+ *
+ * @param[in] my control structure.
+ * @param[in] adjust (1 to hand off 1 buffer) | (MDB_EOF when ending).
+ */
+static int ESECT
+mdb_env_cthr_toggle(mdb_copy *my, int adjust)
+{
+ pthread_mutex_lock(&my->mc_mutex);
+ my->mc_new += adjust;
+ pthread_cond_signal(&my->mc_cond);
+ while (my->mc_new & 2) /* both buffers in use */
+ pthread_cond_wait(&my->mc_cond, &my->mc_mutex);
+ pthread_mutex_unlock(&my->mc_mutex);
+
+ my->mc_toggle ^= (adjust & 1);
+ /* Both threads reset mc_wlen, to be safe from threading errors */
+ my->mc_wlen[my->mc_toggle] = 0;
+ return my->mc_error;
+}
+
+ /** Depth-first tree traversal for compacting copy.
+ * @param[in] my control structure.
+ * @param[in,out] pg database root.
+ * @param[in] flags includes #F_DUPDATA if it is a sorted-duplicate sub-DB.
+ */
+static int ESECT
+mdb_env_cwalk(mdb_copy *my, pgno_t *pg, int flags)
+{
+ MDB_cursor mc = {0};
+ MDB_node *ni;
+ MDB_page *mo, *mp, *leaf;
+ char *buf, *ptr;
+ int rc, toggle;
+ unsigned int i;
+
+ /* Empty DB, nothing to do */
+ if (*pg == P_INVALID)
+ return MDB_SUCCESS;
+
+ mc.mc_snum = 1;
+ mc.mc_txn = my->mc_txn;
+
+ rc = mdb_page_get(&mc, *pg, &mc.mc_pg[0], NULL);
+ if (rc)
+ return rc;
+ rc = mdb_page_search_root(&mc, NULL, MDB_PS_FIRST);
+ if (rc)
+ return rc;
+
+ /* Make cursor pages writable */
+ buf = ptr = malloc(my->mc_env->me_psize * mc.mc_snum);
+ if (buf == NULL)
+ return ENOMEM;
+
+ for (i=0; i<mc.mc_top; i++) {
+ mdb_page_copy((MDB_page *)ptr, mc.mc_pg[i], my->mc_env->me_psize);
+ mc.mc_pg[i] = (MDB_page *)ptr;
+ ptr += my->mc_env->me_psize;
+ }
+
+ /* This is writable space for a leaf page. Usually not needed. */
+ leaf = (MDB_page *)ptr;
+
+ toggle = my->mc_toggle;
+ while (mc.mc_snum > 0) {
+ unsigned n;
+ mp = mc.mc_pg[mc.mc_top];
+ n = NUMKEYS(mp);
+
+ if (IS_LEAF(mp)) {
+ if (!IS_LEAF2(mp) && !(flags & F_DUPDATA)) {
+ for (i=0; i<n; i++) {
+ ni = NODEPTR(mp, i);
+ if (ni->mn_flags & F_BIGDATA) {
+ MDB_page *omp;
+ pgno_t pg;
+
+ /* Need writable leaf */
+ if (mp != leaf) {
+ mc.mc_pg[mc.mc_top] = leaf;
+ mdb_page_copy(leaf, mp, my->mc_env->me_psize);
+ mp = leaf;
+ ni = NODEPTR(mp, i);
+ }
+
+ memcpy(&pg, NODEDATA(ni), sizeof(pg));
+ memcpy(NODEDATA(ni), &my->mc_next_pgno, sizeof(pgno_t));
+ rc = mdb_page_get(&mc, pg, &omp, NULL);
+ if (rc)
+ goto done;
+ if (my->mc_wlen[toggle] >= MDB_WBUF) {
+ rc = mdb_env_cthr_toggle(my, 1);
+ if (rc)
+ goto done;
+ toggle = my->mc_toggle;
+ }
+ mo = (MDB_page *)(my->mc_wbuf[toggle] + my->mc_wlen[toggle]);
+ memcpy(mo, omp, my->mc_env->me_psize);
+ mo->mp_pgno = my->mc_next_pgno;
+ my->mc_next_pgno += omp->mp_pages;
+ my->mc_wlen[toggle] += my->mc_env->me_psize;
+ if (omp->mp_pages > 1) {
+ my->mc_olen[toggle] = my->mc_env->me_psize * (omp->mp_pages - 1);
+ my->mc_over[toggle] = (char *)omp + my->mc_env->me_psize;
+ rc = mdb_env_cthr_toggle(my, 1);
+ if (rc)
+ goto done;
+ toggle = my->mc_toggle;
+ }
+ } else if (ni->mn_flags & F_SUBDATA) {
+ MDB_db db;
+
+ /* Need writable leaf */
+ if (mp != leaf) {
+ mc.mc_pg[mc.mc_top] = leaf;
+ mdb_page_copy(leaf, mp, my->mc_env->me_psize);
+ mp = leaf;
+ ni = NODEPTR(mp, i);
+ }
+
+ memcpy(&db, NODEDATA(ni), sizeof(db));
+ my->mc_toggle = toggle;
+ rc = mdb_env_cwalk(my, &db.md_root, ni->mn_flags & F_DUPDATA);
+ if (rc)
+ goto done;
+ toggle = my->mc_toggle;
+ memcpy(NODEDATA(ni), &db, sizeof(db));
+ }
+ }
+ }
+ } else {
+ mc.mc_ki[mc.mc_top]++;
+ if (mc.mc_ki[mc.mc_top] < n) {
+ pgno_t pg;
+again:
+ ni = NODEPTR(mp, mc.mc_ki[mc.mc_top]);
+ pg = NODEPGNO(ni);
+ rc = mdb_page_get(&mc, pg, &mp, NULL);
+ if (rc)
+ goto done;
+ mc.mc_top++;
+ mc.mc_snum++;
+ mc.mc_ki[mc.mc_top] = 0;
+ if (IS_BRANCH(mp)) {
+ /* Whenever we advance to a sibling branch page,
+ * we must proceed all the way down to its first leaf.
+ */
+ mdb_page_copy(mc.mc_pg[mc.mc_top], mp, my->mc_env->me_psize);
+ goto again;
+ } else
+ mc.mc_pg[mc.mc_top] = mp;
+ continue;
+ }
+ }
+ if (my->mc_wlen[toggle] >= MDB_WBUF) {
+ rc = mdb_env_cthr_toggle(my, 1);
+ if (rc)
+ goto done;
+ toggle = my->mc_toggle;
+ }
+ mo = (MDB_page *)(my->mc_wbuf[toggle] + my->mc_wlen[toggle]);
+ mdb_page_copy(mo, mp, my->mc_env->me_psize);
+ mo->mp_pgno = my->mc_next_pgno++;
+ my->mc_wlen[toggle] += my->mc_env->me_psize;
+ if (mc.mc_top) {
+ /* Update parent if there is one */
+ ni = NODEPTR(mc.mc_pg[mc.mc_top-1], mc.mc_ki[mc.mc_top-1]);
+ SETPGNO(ni, mo->mp_pgno);
+ mdb_cursor_pop(&mc);
+ } else {
+ /* Otherwise we're done */
+ *pg = mo->mp_pgno;
+ break;
+ }
+ }
+done:
+ free(buf);
+ return rc;
+}
+
+ /** Copy environment with compaction. */
+static int ESECT
+mdb_env_copyfd1(MDB_env *env, HANDLE fd)
+{
+ MDB_meta *mm;
+ MDB_page *mp;
+ mdb_copy my = {0};
+ MDB_txn *txn = NULL;
+ pthread_t thr;
+ pgno_t root, new_root;
+ int rc = MDB_SUCCESS;
+
+#ifdef _WIN32
+ if (!(my.mc_mutex = CreateMutex(NULL, FALSE, NULL)) ||
+ !(my.mc_cond = CreateEvent(NULL, FALSE, FALSE, NULL))) {
+ rc = ErrCode();
+ goto done;
+ }
+ my.mc_wbuf[0] = _aligned_malloc(MDB_WBUF*2, env->me_os_psize);
+ if (my.mc_wbuf[0] == NULL) {
+ /* _aligned_malloc() sets errno, but we use Windows error codes */
+ rc = ERROR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+#else
+ if ((rc = pthread_mutex_init(&my.mc_mutex, NULL)) != 0)
+ return rc;
+ if ((rc = pthread_cond_init(&my.mc_cond, NULL)) != 0)
+ goto done2;
+#ifdef HAVE_MEMALIGN
+ my.mc_wbuf[0] = memalign(env->me_os_psize, MDB_WBUF*2);
+ if (my.mc_wbuf[0] == NULL) {
+ rc = errno;
+ goto done;
+ }
+#else
+ {
+ void *p;
+ if ((rc = posix_memalign(&p, env->me_os_psize, MDB_WBUF*2)) != 0)
+ goto done;
+ my.mc_wbuf[0] = p;
+ }
+#endif
+#endif
+ memset(my.mc_wbuf[0], 0, MDB_WBUF*2);
+ my.mc_wbuf[1] = my.mc_wbuf[0] + MDB_WBUF;
+ my.mc_next_pgno = NUM_METAS;
+ my.mc_env = env;
+ my.mc_fd = fd;
+ rc = THREAD_CREATE(thr, mdb_env_copythr, &my);
+ if (rc)
+ goto done;
+
+ rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
+ if (rc)
+ goto finish;
+
+ mp = (MDB_page *)my.mc_wbuf[0];
+ memset(mp, 0, NUM_METAS * env->me_psize);
+ mp->mp_pgno = 0;
+ mp->mp_flags = P_META;
+ mm = (MDB_meta *)METADATA(mp);
+ mdb_env_init_meta0(env, mm);
+ mm->mm_address = env->me_metas[0]->mm_address;
+
+ mp = (MDB_page *)(my.mc_wbuf[0] + env->me_psize);
+ mp->mp_pgno = 1;
+ mp->mp_flags = P_META;
+ *(MDB_meta *)METADATA(mp) = *mm;
+ mm = (MDB_meta *)METADATA(mp);
+
+ /* Set metapage 1 with current main DB */
+ root = new_root = txn->mt_dbs[MAIN_DBI].md_root;
+ if (root != P_INVALID) {
+ /* Count free pages + freeDB pages. Subtract from last_pg
+ * to find the new last_pg, which also becomes the new root.
+ */
+ MDB_ID freecount = 0;
+ MDB_cursor mc;
+ MDB_val key, data;
+ mdb_cursor_init(&mc, txn, FREE_DBI, NULL);
+ while ((rc = mdb_cursor_get(&mc, &key, &data, MDB_NEXT)) == 0)
+ freecount += *(MDB_ID *)data.mv_data;
+ if (rc != MDB_NOTFOUND)
+ goto finish;
+ freecount += txn->mt_dbs[FREE_DBI].md_branch_pages +
+ txn->mt_dbs[FREE_DBI].md_leaf_pages +
+ txn->mt_dbs[FREE_DBI].md_overflow_pages;
+
+ new_root = txn->mt_next_pgno - 1 - freecount;
+ mm->mm_last_pg = new_root;
+ mm->mm_dbs[MAIN_DBI] = txn->mt_dbs[MAIN_DBI];
+ mm->mm_dbs[MAIN_DBI].md_root = new_root;
+ } else {
+ /* When the DB is empty, handle it specially to
+ * fix any breakage like page leaks from ITS#8174.
+ */
+ mm->mm_dbs[MAIN_DBI].md_flags = txn->mt_dbs[MAIN_DBI].md_flags;
+ }
+ if (root != P_INVALID || mm->mm_dbs[MAIN_DBI].md_flags) {
+ mm->mm_txnid = 1; /* use metapage 1 */
+ }
+
+ my.mc_wlen[0] = env->me_psize * NUM_METAS;
+ my.mc_txn = txn;
+ rc = mdb_env_cwalk(&my, &root, 0);
+ if (rc == MDB_SUCCESS && root != new_root) {
+ rc = MDB_INCOMPATIBLE; /* page leak or corrupt DB */
+ }
+
+finish:
+ if (rc)
+ my.mc_error = rc;
+ mdb_env_cthr_toggle(&my, 1 | MDB_EOF);
+ rc = THREAD_FINISH(thr);
+ mdb_txn_abort(txn);
+
+done:
+#ifdef _WIN32
+ if (my.mc_wbuf[0]) _aligned_free(my.mc_wbuf[0]);
+ if (my.mc_cond) CloseHandle(my.mc_cond);
+ if (my.mc_mutex) CloseHandle(my.mc_mutex);
+#else
+ free(my.mc_wbuf[0]);
+ pthread_cond_destroy(&my.mc_cond);
+done2:
+ pthread_mutex_destroy(&my.mc_mutex);
+#endif
+ return rc ? rc : my.mc_error;
+}
+
+ /** Copy environment as-is. */
+static int ESECT
+mdb_env_copyfd0(MDB_env *env, HANDLE fd)
+{
+ MDB_txn *txn = NULL;
+ mdb_mutexref_t wmutex = NULL;
+ int rc;
+ size_t wsize, w3;
+ char *ptr;
+#ifdef _WIN32
+ DWORD len, w2;
+#define DO_WRITE(rc, fd, ptr, w2, len) rc = WriteFile(fd, ptr, w2, &len, NULL)
+#else
+ ssize_t len;
+ size_t w2;
+#define DO_WRITE(rc, fd, ptr, w2, len) len = write(fd, ptr, w2); rc = (len >= 0)
+#endif
+
+ /* Do the lock/unlock of the reader mutex before starting the
+ * write txn. Otherwise other read txns could block writers.
+ */
+ rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
+ if (rc)
+ return rc;
+
+ if (env->me_txns) {
+ /* We must start the actual read txn after blocking writers */
+ mdb_txn_end(txn, MDB_END_RESET_TMP);
+
+ /* Temporarily block writers until we snapshot the meta pages */
+ wmutex = env->me_wmutex;
+ if (LOCK_MUTEX(rc, env, wmutex))
+ goto leave;
+
+ rc = mdb_txn_renew0(txn);
+ if (rc) {
+ UNLOCK_MUTEX(wmutex);
+ goto leave;
+ }
+ }
+
+ wsize = env->me_psize * NUM_METAS;
+ ptr = env->me_map;
+ w2 = wsize;
+ while (w2 > 0) {
+ DO_WRITE(rc, fd, ptr, w2, len);
+ if (!rc) {
+ rc = ErrCode();
+ break;
+ } else if (len > 0) {
+ rc = MDB_SUCCESS;
+ ptr += len;
+ w2 -= len;
+ continue;
+ } else {
+ /* Non-blocking or async handles are not supported */
+ rc = EIO;
+ break;
+ }
+ }
+ if (wmutex)
+ UNLOCK_MUTEX(wmutex);
+
+ if (rc)
+ goto leave;
+
+ w3 = txn->mt_next_pgno * env->me_psize;
+ {
+ size_t fsize = 0;
+ if ((rc = mdb_fsize(env->me_fd, &fsize)))
+ goto leave;
+ if (w3 > fsize)
+ w3 = fsize;
+ }
+ wsize = w3 - wsize;
+ while (wsize > 0) {
+ if (wsize > MAX_WRITE)
+ w2 = MAX_WRITE;
+ else
+ w2 = wsize;
+ DO_WRITE(rc, fd, ptr, w2, len);
+ if (!rc) {
+ rc = ErrCode();
+ break;
+ } else if (len > 0) {
+ rc = MDB_SUCCESS;
+ ptr += len;
+ wsize -= len;
+ continue;
+ } else {
+ rc = EIO;
+ break;
+ }
+ }
+
+leave:
+ mdb_txn_abort(txn);
+ return rc;
+}
+
+int ESECT
+mdb_env_copyfd2(MDB_env *env, HANDLE fd, unsigned int flags)
+{
+ if (flags & MDB_CP_COMPACT)
+ return mdb_env_copyfd1(env, fd);
+ else
+ return mdb_env_copyfd0(env, fd);
+}
+
+int ESECT
+mdb_env_copyfd(MDB_env *env, HANDLE fd)
+{
+ return mdb_env_copyfd2(env, fd, 0);
+}
+
+int ESECT
+mdb_env_copy2(MDB_env *env, const char *path, unsigned int flags)
+{
+ int rc;
+ MDB_name fname;
+ HANDLE newfd = INVALID_HANDLE_VALUE;
+
+ rc = mdb_fname_init(path, env->me_flags | MDB_NOLOCK, &fname);
+ if (rc == MDB_SUCCESS) {
+ rc = mdb_fopen(env, &fname, MDB_O_COPY, 0666, &newfd);
+ mdb_fname_destroy(fname);
+ }
+ if (rc == MDB_SUCCESS) {
+ rc = mdb_env_copyfd2(env, newfd, flags);
+ if (close(newfd) < 0 && rc == MDB_SUCCESS)
+ rc = ErrCode();
+ }
+ return rc;
+}
+
+int ESECT
+mdb_env_copy(MDB_env *env, const char *path)
+{
+ return mdb_env_copy2(env, path, 0);
+}
+
+int ESECT
+mdb_env_set_flags(MDB_env *env, unsigned int flag, int onoff)
+{
+ if (flag & ~CHANGEABLE)
+ return EINVAL;
+ if (onoff)
+ env->me_flags |= flag;
+ else
+ env->me_flags &= ~flag;
+ return MDB_SUCCESS;
+}
+
+int ESECT
+mdb_env_get_flags(MDB_env *env, unsigned int *arg)
+{
+ if (!env || !arg)
+ return EINVAL;
+
+ *arg = env->me_flags & (CHANGEABLE|CHANGELESS);
+ return MDB_SUCCESS;
+}
+
+int ESECT
+mdb_env_set_userctx(MDB_env *env, void *ctx)
+{
+ if (!env)
+ return EINVAL;
+ env->me_userctx = ctx;
+ return MDB_SUCCESS;
+}
+
+void * ESECT
+mdb_env_get_userctx(MDB_env *env)
+{
+ return env ? env->me_userctx : NULL;
+}
+
+int ESECT
+mdb_env_set_assert(MDB_env *env, MDB_assert_func *func)
+{
+ if (!env)
+ return EINVAL;
+#ifndef NDEBUG
+ env->me_assert_func = func;
+#endif
+ return MDB_SUCCESS;
+}
+
+int ESECT
+mdb_env_get_path(MDB_env *env, const char **arg)
+{
+ if (!env || !arg)
+ return EINVAL;
+
+ *arg = env->me_path;
+ return MDB_SUCCESS;
+}
+
+int ESECT
+mdb_env_get_fd(MDB_env *env, mdb_filehandle_t *arg)
+{
+ if (!env || !arg)
+ return EINVAL;
+
+ *arg = env->me_fd;
+ return MDB_SUCCESS;
+}
+
+/** Common code for #mdb_stat() and #mdb_env_stat().
+ * @param[in] env the environment to operate in.
+ * @param[in] db the #MDB_db record containing the stats to return.
+ * @param[out] arg the address of an #MDB_stat structure to receive the stats.
+ * @return 0, this function always succeeds.
+ */
+static int ESECT
+mdb_stat0(MDB_env *env, MDB_db *db, MDB_stat *arg)
+{
+ arg->ms_psize = env->me_psize;
+ arg->ms_depth = db->md_depth;
+ arg->ms_branch_pages = db->md_branch_pages;
+ arg->ms_leaf_pages = db->md_leaf_pages;
+ arg->ms_overflow_pages = db->md_overflow_pages;
+ arg->ms_entries = db->md_entries;
+
+ return MDB_SUCCESS;
+}
+
+int ESECT
+mdb_env_stat(MDB_env *env, MDB_stat *arg)
+{
+ MDB_meta *meta;
+
+ if (env == NULL || arg == NULL)
+ return EINVAL;
+
+ meta = mdb_env_pick_meta(env);
+
+ return mdb_stat0(env, &meta->mm_dbs[MAIN_DBI], arg);
+}
+
+int ESECT
+mdb_env_info(MDB_env *env, MDB_envinfo *arg)
+{
+ MDB_meta *meta;
+
+ if (env == NULL || arg == NULL)
+ return EINVAL;
+
+ meta = mdb_env_pick_meta(env);
+ arg->me_mapaddr = meta->mm_address;
+ arg->me_last_pgno = meta->mm_last_pg;
+ arg->me_last_txnid = meta->mm_txnid;
+
+ arg->me_mapsize = env->me_mapsize;
+ arg->me_maxreaders = env->me_maxreaders;
+ arg->me_numreaders = env->me_txns ? env->me_txns->mti_numreaders : 0;
+ return MDB_SUCCESS;
+}
+
+/** Set the default comparison functions for a database.
+ * Called immediately after a database is opened to set the defaults.
+ * The user can then override them with #mdb_set_compare() or
+ * #mdb_set_dupsort().
+ * @param[in] txn A transaction handle returned by #mdb_txn_begin()
+ * @param[in] dbi A database handle returned by #mdb_dbi_open()
+ */
+static void
+mdb_default_cmp(MDB_txn *txn, MDB_dbi dbi)
+{
+ uint16_t f = txn->mt_dbs[dbi].md_flags;
+
+ txn->mt_dbxs[dbi].md_cmp =
+ (f & MDB_REVERSEKEY) ? mdb_cmp_memnr :
+ (f & MDB_INTEGERKEY) ? mdb_cmp_cint : mdb_cmp_memn;
+
+ txn->mt_dbxs[dbi].md_dcmp =
+ !(f & MDB_DUPSORT) ? 0 :
+ ((f & MDB_INTEGERDUP)
+ ? ((f & MDB_DUPFIXED) ? mdb_cmp_int : mdb_cmp_cint)
+ : ((f & MDB_REVERSEDUP) ? mdb_cmp_memnr : mdb_cmp_memn));
+}
+
+int mdb_dbi_open(MDB_txn *txn, const char *name, unsigned int flags, MDB_dbi *dbi)
+{
+ MDB_val key, data;
+ MDB_dbi i;
+ MDB_cursor mc;
+ MDB_db dummy;
+ int rc, dbflag, exact;
+ unsigned int unused = 0, seq;
+ char *namedup;
+ size_t len;
+
+ if (flags & ~VALID_FLAGS)
+ return EINVAL;
+ if (txn->mt_flags & MDB_TXN_BLOCKED)
+ return MDB_BAD_TXN;
+
+ /* main DB? */
+ if (!name) {
+ *dbi = MAIN_DBI;
+ if (flags & PERSISTENT_FLAGS) {
+ uint16_t f2 = flags & PERSISTENT_FLAGS;
+ /* make sure flag changes get committed */
+ if ((txn->mt_dbs[MAIN_DBI].md_flags | f2) != txn->mt_dbs[MAIN_DBI].md_flags) {
+ txn->mt_dbs[MAIN_DBI].md_flags |= f2;
+ txn->mt_flags |= MDB_TXN_DIRTY;
+ }
+ }
+ mdb_default_cmp(txn, MAIN_DBI);
+ return MDB_SUCCESS;
+ }
+
+ if (txn->mt_dbxs[MAIN_DBI].md_cmp == NULL) {
+ mdb_default_cmp(txn, MAIN_DBI);
+ }
+
+ /* Is the DB already open? */
+ len = strlen(name);
+ for (i=CORE_DBS; i<txn->mt_numdbs; i++) {
+ if (!txn->mt_dbxs[i].md_name.mv_size) {
+ /* Remember this free slot */
+ if (!unused) unused = i;
+ continue;
+ }
+ if (len == txn->mt_dbxs[i].md_name.mv_size &&
+ !strncmp(name, txn->mt_dbxs[i].md_name.mv_data, len)) {
+ *dbi = i;
+ return MDB_SUCCESS;
+ }
+ }
+
+ /* If no free slot and max hit, fail */
+ if (!unused && txn->mt_numdbs >= txn->mt_env->me_maxdbs)
+ return MDB_DBS_FULL;
+
+ /* Cannot mix named databases with some mainDB flags */
+ if (txn->mt_dbs[MAIN_DBI].md_flags & (MDB_DUPSORT|MDB_INTEGERKEY))
+ return (flags & MDB_CREATE) ? MDB_INCOMPATIBLE : MDB_NOTFOUND;
+
+ /* Find the DB info */
+ dbflag = DB_NEW|DB_VALID|DB_USRVALID;
+ exact = 0;
+ key.mv_size = len;
+ key.mv_data = (void *)name;
+ mdb_cursor_init(&mc, txn, MAIN_DBI, NULL);
+ rc = mdb_cursor_set(&mc, &key, &data, MDB_SET, &exact);
+ if (rc == MDB_SUCCESS) {
+ /* make sure this is actually a DB */
+ MDB_node *node = NODEPTR(mc.mc_pg[mc.mc_top], mc.mc_ki[mc.mc_top]);
+ if ((node->mn_flags & (F_DUPDATA|F_SUBDATA)) != F_SUBDATA)
+ return MDB_INCOMPATIBLE;
+ } else {
+ if (rc != MDB_NOTFOUND || !(flags & MDB_CREATE))
+ return rc;
+ if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY))
+ return EACCES;
+ }
+
+ /* Done here so we cannot fail after creating a new DB */
+ if ((namedup = strdup(name)) == NULL)
+ return ENOMEM;
+
+ if (rc) {
+ /* MDB_NOTFOUND and MDB_CREATE: Create new DB */
+ data.mv_size = sizeof(MDB_db);
+ data.mv_data = &dummy;
+ memset(&dummy, 0, sizeof(dummy));
+ dummy.md_root = P_INVALID;
+ dummy.md_flags = flags & PERSISTENT_FLAGS;
+ WITH_CURSOR_TRACKING(mc,
+ rc = mdb_cursor_put(&mc, &key, &data, F_SUBDATA));
+ dbflag |= DB_DIRTY;
+ }
+
+ if (rc) {
+ free(namedup);
+ } else {
+ /* Got info, register DBI in this txn */
+ unsigned int slot = unused ? unused : txn->mt_numdbs;
+ txn->mt_dbxs[slot].md_name.mv_data = namedup;
+ txn->mt_dbxs[slot].md_name.mv_size = len;
+ txn->mt_dbxs[slot].md_rel = NULL;
+ txn->mt_dbflags[slot] = dbflag;
+ /* txn-> and env-> are the same in read txns, use
+ * tmp variable to avoid undefined assignment
+ */
+ seq = ++txn->mt_env->me_dbiseqs[slot];
+ txn->mt_dbiseqs[slot] = seq;
+
+ memcpy(&txn->mt_dbs[slot], data.mv_data, sizeof(MDB_db));
+ *dbi = slot;
+ mdb_default_cmp(txn, slot);
+ if (!unused) {
+ txn->mt_numdbs++;
+ }
+ }
+
+ return rc;
+}
+
+int ESECT
+mdb_stat(MDB_txn *txn, MDB_dbi dbi, MDB_stat *arg)
+{
+ if (!arg || !TXN_DBI_EXIST(txn, dbi, DB_VALID))
+ return EINVAL;
+
+ if (txn->mt_flags & MDB_TXN_BLOCKED)
+ return MDB_BAD_TXN;
+
+ if (txn->mt_dbflags[dbi] & DB_STALE) {
+ MDB_cursor mc;
+ MDB_xcursor mx;
+ /* Stale, must read the DB's root. cursor_init does it for us. */
+ mdb_cursor_init(&mc, txn, dbi, &mx);
+ }
+ return mdb_stat0(txn->mt_env, &txn->mt_dbs[dbi], arg);
+}
+
+void mdb_dbi_close(MDB_env *env, MDB_dbi dbi)
+{
+ char *ptr;
+ if (dbi < CORE_DBS || dbi >= env->me_maxdbs)
+ return;
+ ptr = env->me_dbxs[dbi].md_name.mv_data;
+ /* If there was no name, this was already closed */
+ if (ptr) {
+ env->me_dbxs[dbi].md_name.mv_data = NULL;
+ env->me_dbxs[dbi].md_name.mv_size = 0;
+ env->me_dbflags[dbi] = 0;
+ env->me_dbiseqs[dbi]++;
+ free(ptr);
+ }
+}
+
+int mdb_dbi_flags(MDB_txn *txn, MDB_dbi dbi, unsigned int *flags)
+{
+ /* We could return the flags for the FREE_DBI too but what's the point? */
+ if (!TXN_DBI_EXIST(txn, dbi, DB_USRVALID))
+ return EINVAL;
+ *flags = txn->mt_dbs[dbi].md_flags & PERSISTENT_FLAGS;
+ return MDB_SUCCESS;
+}
+
+/** Add all the DB's pages to the free list.
+ * @param[in] mc Cursor on the DB to free.
+ * @param[in] subs non-Zero to check for sub-DBs in this DB.
+ * @return 0 on success, non-zero on failure.
+ */
+static int
+mdb_drop0(MDB_cursor *mc, int subs)
+{
+ int rc;
+
+ rc = mdb_page_search(mc, NULL, MDB_PS_FIRST);
+ if (rc == MDB_SUCCESS) {
+ MDB_txn *txn = mc->mc_txn;
+ MDB_node *ni;
+ MDB_cursor mx;
+ unsigned int i;
+
+ /* DUPSORT sub-DBs have no ovpages/DBs. Omit scanning leaves.
+ * This also avoids any P_LEAF2 pages, which have no nodes.
+ * Also if the DB doesn't have sub-DBs and has no overflow
+ * pages, omit scanning leaves.
+ */
+ if ((mc->mc_flags & C_SUB) ||
+ (!subs && !mc->mc_db->md_overflow_pages))
+ mdb_cursor_pop(mc);
+
+ mdb_cursor_copy(mc, &mx);
+ while (mc->mc_snum > 0) {
+ MDB_page *mp = mc->mc_pg[mc->mc_top];
+ unsigned n = NUMKEYS(mp);
+ if (IS_LEAF(mp)) {
+ for (i=0; i<n; i++) {
+ ni = NODEPTR(mp, i);
+ if (ni->mn_flags & F_BIGDATA) {
+ MDB_page *omp;
+ pgno_t pg;
+ memcpy(&pg, NODEDATA(ni), sizeof(pg));
+ rc = mdb_page_get(mc, pg, &omp, NULL);
+ if (rc != 0)
+ goto done;
+ mdb_cassert(mc, IS_OVERFLOW(omp));
+ rc = mdb_midl_append_range(&txn->mt_free_pgs,
+ pg, omp->mp_pages);
+ if (rc)
+ goto done;
+ mc->mc_db->md_overflow_pages -= omp->mp_pages;
+ if (!mc->mc_db->md_overflow_pages && !subs)
+ break;
+ } else if (subs && (ni->mn_flags & F_SUBDATA)) {
+ mdb_xcursor_init1(mc, ni);
+ rc = mdb_drop0(&mc->mc_xcursor->mx_cursor, 0);
+ if (rc)
+ goto done;
+ }
+ }
+ if (!subs && !mc->mc_db->md_overflow_pages)
+ goto pop;
+ } else {
+ if ((rc = mdb_midl_need(&txn->mt_free_pgs, n)) != 0)
+ goto done;
+ for (i=0; i<n; i++) {
+ pgno_t pg;
+ ni = NODEPTR(mp, i);
+ pg = NODEPGNO(ni);
+ /* free it */
+ mdb_midl_xappend(txn->mt_free_pgs, pg);
+ }
+ }
+ if (!mc->mc_top)
+ break;
+ mc->mc_ki[mc->mc_top] = i;
+ rc = mdb_cursor_sibling(mc, 1);
+ if (rc) {
+ if (rc != MDB_NOTFOUND)
+ goto done;
+ /* no more siblings, go back to beginning
+ * of previous level.
+ */
+pop:
+ mdb_cursor_pop(mc);
+ mc->mc_ki[0] = 0;
+ for (i=1; i<mc->mc_snum; i++) {
+ mc->mc_ki[i] = 0;
+ mc->mc_pg[i] = mx.mc_pg[i];
+ }
+ }
+ }
+ /* free it */
+ rc = mdb_midl_append(&txn->mt_free_pgs, mc->mc_db->md_root);
+done:
+ if (rc)
+ txn->mt_flags |= MDB_TXN_ERROR;
+ } else if (rc == MDB_NOTFOUND) {
+ rc = MDB_SUCCESS;
+ }
+ mc->mc_flags &= ~C_INITIALIZED;
+ return rc;
+}
+
+int mdb_drop(MDB_txn *txn, MDB_dbi dbi, int del)
+{
+ MDB_cursor *mc, *m2;
+ int rc;
+
+ if ((unsigned)del > 1 || !TXN_DBI_EXIST(txn, dbi, DB_USRVALID))
+ return EINVAL;
+
+ if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY))
+ return EACCES;
+
+ if (TXN_DBI_CHANGED(txn, dbi))
+ return MDB_BAD_DBI;
+
+ rc = mdb_cursor_open(txn, dbi, &mc);
+ if (rc)
+ return rc;
+
+ rc = mdb_drop0(mc, mc->mc_db->md_flags & MDB_DUPSORT);
+ /* Invalidate the dropped DB's cursors */
+ for (m2 = txn->mt_cursors[dbi]; m2; m2 = m2->mc_next)
+ m2->mc_flags &= ~(C_INITIALIZED|C_EOF);
+ if (rc)
+ goto leave;
+
+ /* Can't delete the main DB */
+ if (del && dbi >= CORE_DBS) {
+ rc = mdb_del0(txn, MAIN_DBI, &mc->mc_dbx->md_name, NULL, F_SUBDATA);
+ if (!rc) {
+ txn->mt_dbflags[dbi] = DB_STALE;
+ mdb_dbi_close(txn->mt_env, dbi);
+ } else {
+ txn->mt_flags |= MDB_TXN_ERROR;
+ }
+ } else {
+ /* reset the DB record, mark it dirty */
+ txn->mt_dbflags[dbi] |= DB_DIRTY;
+ txn->mt_dbs[dbi].md_depth = 0;
+ txn->mt_dbs[dbi].md_branch_pages = 0;
+ txn->mt_dbs[dbi].md_leaf_pages = 0;
+ txn->mt_dbs[dbi].md_overflow_pages = 0;
+ txn->mt_dbs[dbi].md_entries = 0;
+ txn->mt_dbs[dbi].md_root = P_INVALID;
+
+ txn->mt_flags |= MDB_TXN_DIRTY;
+ }
+leave:
+ mdb_cursor_close(mc);
+ return rc;
+}
+
+int mdb_set_compare(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp)
+{
+ if (!TXN_DBI_EXIST(txn, dbi, DB_USRVALID))
+ return EINVAL;
+
+ txn->mt_dbxs[dbi].md_cmp = cmp;
+ return MDB_SUCCESS;
+}
+
+int mdb_set_dupsort(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp)
+{
+ if (!TXN_DBI_EXIST(txn, dbi, DB_USRVALID))
+ return EINVAL;
+
+ txn->mt_dbxs[dbi].md_dcmp = cmp;
+ return MDB_SUCCESS;
+}
+
+int mdb_set_relfunc(MDB_txn *txn, MDB_dbi dbi, MDB_rel_func *rel)
+{
+ if (!TXN_DBI_EXIST(txn, dbi, DB_USRVALID))
+ return EINVAL;
+
+ txn->mt_dbxs[dbi].md_rel = rel;
+ return MDB_SUCCESS;
+}
+
+int mdb_set_relctx(MDB_txn *txn, MDB_dbi dbi, void *ctx)
+{
+ if (!TXN_DBI_EXIST(txn, dbi, DB_USRVALID))
+ return EINVAL;
+
+ txn->mt_dbxs[dbi].md_relctx = ctx;
+ return MDB_SUCCESS;
+}
+
+int ESECT
+mdb_env_get_maxkeysize(MDB_env *env)
+{
+ return ENV_MAXKEY(env);
+}
+
+int ESECT
+mdb_reader_list(MDB_env *env, MDB_msg_func *func, void *ctx)
+{
+ unsigned int i, rdrs;
+ MDB_reader *mr;
+ char buf[64];
+ int rc = 0, first = 1;
+
+ if (!env || !func)
+ return -1;
+ if (!env->me_txns) {
+ return func("(no reader locks)\n", ctx);
+ }
+ rdrs = env->me_txns->mti_numreaders;
+ mr = env->me_txns->mti_readers;
+ for (i=0; i<rdrs; i++) {
+ if (mr[i].mr_pid) {
+ txnid_t txnid = mr[i].mr_txnid;
+ sprintf(buf, txnid == (txnid_t)-1 ?
+ "%10d %"Z"x -\n" : "%10d %"Z"x %"Z"u\n",
+ (int)mr[i].mr_pid, (size_t)mr[i].mr_tid, txnid);
+ if (first) {
+ first = 0;
+ rc = func(" pid thread txnid\n", ctx);
+ if (rc < 0)
+ break;
+ }
+ rc = func(buf, ctx);
+ if (rc < 0)
+ break;
+ }
+ }
+ if (first) {
+ rc = func("(no active readers)\n", ctx);
+ }
+ return rc;
+}
+
+/** Insert pid into list if not already present.
+ * return -1 if already present.
+ */
+static int ESECT
+mdb_pid_insert(MDB_PID_T *ids, MDB_PID_T pid)
+{
+ /* binary search of pid in list */
+ unsigned base = 0;
+ unsigned cursor = 1;
+ int val = 0;
+ unsigned n = ids[0];
+
+ while( 0 < n ) {
+ unsigned pivot = n >> 1;
+ cursor = base + pivot + 1;
+ val = pid - ids[cursor];
+
+ if( val < 0 ) {
+ n = pivot;
+
+ } else if ( val > 0 ) {
+ base = cursor;
+ n -= pivot + 1;
+
+ } else {
+ /* found, so it's a duplicate */
+ return -1;
+ }
+ }
+
+ if( val > 0 ) {
+ ++cursor;
+ }
+ ids[0]++;
+ for (n = ids[0]; n > cursor; n--)
+ ids[n] = ids[n-1];
+ ids[n] = pid;
+ return 0;
+}
+
+int ESECT
+mdb_reader_check(MDB_env *env, int *dead)
+{
+ if (!env)
+ return EINVAL;
+ if (dead)
+ *dead = 0;
+ return env->me_txns ? mdb_reader_check0(env, 0, dead) : MDB_SUCCESS;
+}
+
+/** As #mdb_reader_check(). \b rlocked is set if caller locked #me_rmutex. */
+static int ESECT
+mdb_reader_check0(MDB_env *env, int rlocked, int *dead)
+{
+ mdb_mutexref_t rmutex = rlocked ? NULL : env->me_rmutex;
+ unsigned int i, j, rdrs;
+ MDB_reader *mr;
+ MDB_PID_T *pids, pid;
+ int rc = MDB_SUCCESS, count = 0;
+
+ rdrs = env->me_txns->mti_numreaders;
+ pids = malloc((rdrs+1) * sizeof(MDB_PID_T));
+ if (!pids)
+ return ENOMEM;
+ pids[0] = 0;
+ mr = env->me_txns->mti_readers;
+ for (i=0; i<rdrs; i++) {
+ pid = mr[i].mr_pid;
+ if (pid && pid != env->me_pid) {
+ if (mdb_pid_insert(pids, pid) == 0) {
+ if (!mdb_reader_pid(env, Pidcheck, pid)) {
+ /* Stale reader found */
+ j = i;
+ if (rmutex) {
+ if ((rc = LOCK_MUTEX0(rmutex)) != 0) {
+ if ((rc = mdb_mutex_failed(env, rmutex, rc)))
+ break;
+ rdrs = 0; /* the above checked all readers */
+ } else {
+ /* Recheck, a new process may have reused pid */
+ if (mdb_reader_pid(env, Pidcheck, pid))
+ j = rdrs;
+ }
+ }
+ for (; j<rdrs; j++)
+ if (mr[j].mr_pid == pid) {
+ DPRINTF(("clear stale reader pid %u txn %"Z"d",
+ (unsigned) pid, mr[j].mr_txnid));
+ mr[j].mr_pid = 0;
+ count++;
+ }
+ if (rmutex)
+ UNLOCK_MUTEX(rmutex);
+ }
+ }
+ }
+ }
+ free(pids);
+ if (dead)
+ *dead = count;
+ return rc;
+}
+
+#ifdef MDB_ROBUST_SUPPORTED
+/** Handle #LOCK_MUTEX0() failure.
+ * Try to repair the lock file if the mutex owner died.
+ * @param[in] env the environment handle
+ * @param[in] mutex LOCK_MUTEX0() mutex
+ * @param[in] rc LOCK_MUTEX0() error (nonzero)
+ * @return 0 on success with the mutex locked, or an error code on failure.
+ */
+static int ESECT
+mdb_mutex_failed(MDB_env *env, mdb_mutexref_t mutex, int rc)
+{
+ int rlocked, rc2;
+ MDB_meta *meta;
+
+ if (rc == MDB_OWNERDEAD) {
+ /* We own the mutex. Clean up after dead previous owner. */
+ rc = MDB_SUCCESS;
+ rlocked = (mutex == env->me_rmutex);
+ if (!rlocked) {
+ /* Keep mti_txnid updated, otherwise next writer can
+ * overwrite data which latest meta page refers to.
+ */
+ meta = mdb_env_pick_meta(env);
+ env->me_txns->mti_txnid = meta->mm_txnid;
+ /* env is hosed if the dead thread was ours */
+ if (env->me_txn) {
+ env->me_flags |= MDB_FATAL_ERROR;
+ env->me_txn = NULL;
+ rc = MDB_PANIC;
+ }
+ }
+ DPRINTF(("%cmutex owner died, %s", (rlocked ? 'r' : 'w'),
+ (rc ? "this process' env is hosed" : "recovering")));
+ rc2 = mdb_reader_check0(env, rlocked, NULL);
+ if (rc2 == 0)
+ rc2 = mdb_mutex_consistent(mutex);
+ if (rc || (rc = rc2)) {
+ DPRINTF(("LOCK_MUTEX recovery failed, %s", mdb_strerror(rc)));
+ UNLOCK_MUTEX(mutex);
+ }
+ } else {
+#ifdef _WIN32
+ rc = ErrCode();
+#endif
+ DPRINTF(("LOCK_MUTEX failed, %s", mdb_strerror(rc)));
+ }
+
+ return rc;
+}
+#endif /* MDB_ROBUST_SUPPORTED */
+
+#if defined(_WIN32)
+/** Convert \b src to new wchar_t[] string with room for \b xtra extra chars */
+static int ESECT
+utf8_to_utf16(const char *src, MDB_name *dst, int xtra)
+{
+ int rc, need = 0;
+ wchar_t *result = NULL;
+ for (;;) { /* malloc result, then fill it in */
+ need = MultiByteToWideChar(CP_UTF8, 0, src, -1, result, need);
+ if (!need) {
+ rc = ErrCode();
+ free(result);
+ return rc;
+ }
+ if (!result) {
+ result = malloc(sizeof(wchar_t) * (need + xtra));
+ if (!result)
+ return ENOMEM;
+ continue;
+ }
+ dst->mn_alloced = 1;
+ dst->mn_len = need - 1;
+ dst->mn_val = result;
+ return MDB_SUCCESS;
+ }
+}
+#endif /* defined(_WIN32) */
+/** @} */
diff --git a/libraries/liblmdb/mdb_copy.1 b/libraries/liblmdb/mdb_copy.1
new file mode 100644
index 0000000..0c53746
--- /dev/null
+++ b/libraries/liblmdb/mdb_copy.1
@@ -0,0 +1,55 @@
+.TH MDB_COPY 1 "2014/07/01" "LMDB 0.9.14"
+.\" Copyright 2012-2021 Howard Chu, Symas Corp. All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+mdb_copy \- LMDB environment copy tool
+.SH SYNOPSIS
+.B mdb_copy
+[\c
+.BR \-V ]
+[\c
+.BR \-c ]
+[\c
+.BR \-n ]
+.B srcpath
+[\c
+.BR dstpath ]
+.SH DESCRIPTION
+The
+.B mdb_copy
+utility copies an LMDB environment. The environment can
+be copied regardless of whether it is currently in use.
+No lockfile is created, since it gets recreated at need.
+
+If
+.I dstpath
+is specified it must be the path of an empty directory
+for storing the backup. Otherwise, the backup will be
+written to stdout.
+
+.SH OPTIONS
+.TP
+.BR \-V
+Write the library version number to the standard output, and exit.
+.TP
+.BR \-c
+Compact while copying. Only current data pages will be copied; freed
+or unused pages will be omitted from the copy. This option will
+slow down the backup process as it is more CPU-intensive.
+Currently it fails if the environment has suffered a page leak.
+.TP
+.BR \-n
+Open LDMB environment(s) which do not use subdirectories.
+
+.SH DIAGNOSTICS
+Exit status is zero if no errors occur.
+Errors result in a non-zero exit status and
+a diagnostic message being written to standard error.
+.SH CAVEATS
+This utility can trigger significant file size growth if run
+in parallel with write transactions, because pages which they
+free during copying cannot be reused until the copy is done.
+.SH "SEE ALSO"
+.BR mdb_stat (1)
+.SH AUTHOR
+Howard Chu of Symas Corporation <http://www.symas.com>
diff --git a/libraries/liblmdb/mdb_copy.c b/libraries/liblmdb/mdb_copy.c
new file mode 100644
index 0000000..9b75a30
--- /dev/null
+++ b/libraries/liblmdb/mdb_copy.c
@@ -0,0 +1,82 @@
+/* mdb_copy.c - memory-mapped database backup tool */
+/*
+ * Copyright 2012-2021 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+#ifdef _WIN32
+#include <windows.h>
+#define MDB_STDOUT GetStdHandle(STD_OUTPUT_HANDLE)
+#else
+#define MDB_STDOUT 1
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include "lmdb.h"
+
+static void
+sighandle(int sig)
+{
+}
+
+int main(int argc,char * argv[])
+{
+ int rc;
+ MDB_env *env;
+ const char *progname = argv[0], *act;
+ unsigned flags = MDB_RDONLY;
+ unsigned cpflags = 0;
+
+ for (; argc > 1 && argv[1][0] == '-'; argc--, argv++) {
+ if (argv[1][1] == 'n' && argv[1][2] == '\0')
+ flags |= MDB_NOSUBDIR;
+ else if (argv[1][1] == 'c' && argv[1][2] == '\0')
+ cpflags |= MDB_CP_COMPACT;
+ else if (argv[1][1] == 'V' && argv[1][2] == '\0') {
+ printf("%s\n", MDB_VERSION_STRING);
+ exit(0);
+ } else
+ argc = 0;
+ }
+
+ if (argc<2 || argc>3) {
+ fprintf(stderr, "usage: %s [-V] [-c] [-n] srcpath [dstpath]\n", progname);
+ exit(EXIT_FAILURE);
+ }
+
+#ifdef SIGPIPE
+ signal(SIGPIPE, sighandle);
+#endif
+#ifdef SIGHUP
+ signal(SIGHUP, sighandle);
+#endif
+ signal(SIGINT, sighandle);
+ signal(SIGTERM, sighandle);
+
+ act = "opening environment";
+ rc = mdb_env_create(&env);
+ if (rc == MDB_SUCCESS) {
+ rc = mdb_env_open(env, argv[1], flags, 0600);
+ }
+ if (rc == MDB_SUCCESS) {
+ act = "copying";
+ if (argc == 2)
+ rc = mdb_env_copyfd2(env, MDB_STDOUT, cpflags);
+ else
+ rc = mdb_env_copy2(env, argv[2], cpflags);
+ }
+ if (rc)
+ fprintf(stderr, "%s: %s failed, error %d (%s)\n",
+ progname, act, rc, mdb_strerror(rc));
+ mdb_env_close(env);
+
+ return rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/libraries/liblmdb/mdb_dump.1 b/libraries/liblmdb/mdb_dump.1
new file mode 100644
index 0000000..5f2d771
--- /dev/null
+++ b/libraries/liblmdb/mdb_dump.1
@@ -0,0 +1,75 @@
+.TH MDB_DUMP 1 "2015/09/30" "LMDB 0.9.17"
+.\" Copyright 2014-2021 Howard Chu, Symas Corp. All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+mdb_dump \- LMDB environment export tool
+.SH SYNOPSIS
+.B mdb_dump
+[\c
+.BR \-V ]
+[\c
+.BI \-f \ file\fR]
+[\c
+.BR \-l ]
+[\c
+.BR \-n ]
+[\c
+.BR \-p ]
+[\c
+.BR \-a \ |
+.BI \-s \ subdb\fR]
+.BR \ envpath
+.SH DESCRIPTION
+The
+.B mdb_dump
+utility reads a database and writes its contents to the
+standard output using a portable flat-text format
+understood by the
+.BR mdb_load (1)
+utility.
+.SH OPTIONS
+.TP
+.BR \-V
+Write the library version number to the standard output, and exit.
+.TP
+.BR \-f \ file
+Write to the specified file instead of to the standard output.
+.TP
+.BR \-l
+List the databases stored in the environment. Just the
+names will be listed, no data will be output.
+.TP
+.BR \-n
+Dump an LMDB database which does not use subdirectories.
+.TP
+.BR \-p
+If characters in either the key or data items are printing characters (as
+defined by isprint(3)), output them directly. This option permits users to
+use standard text editors and tools to modify the contents of databases.
+
+Note: different systems may have different notions about what characters
+are considered printing characters, and databases dumped in this manner may
+be less portable to external systems.
+.TP
+.BR \-a
+Dump all of the subdatabases in the environment.
+.TP
+.BR \-s \ subdb
+Dump a specific subdatabase. If no database is specified, only the main database is dumped.
+.SH DIAGNOSTICS
+Exit status is zero if no errors occur.
+Errors result in a non-zero exit status and
+a diagnostic message being written to standard error.
+
+Dumping and reloading databases that use user-defined comparison functions
+will result in new databases that use the default comparison functions.
+\fBIn this case it is quite likely that the reloaded database will be
+damaged beyond repair permitting neither record storage nor retrieval.\fP
+
+The only available workaround is to modify the source for the
+.BR mdb_load (1)
+utility to load the database using the correct comparison functions.
+.SH "SEE ALSO"
+.BR mdb_load (1)
+.SH AUTHOR
+Howard Chu of Symas Corporation <http://www.symas.com>
diff --git a/libraries/liblmdb/mdb_dump.c b/libraries/liblmdb/mdb_dump.c
new file mode 100644
index 0000000..671ec57
--- /dev/null
+++ b/libraries/liblmdb/mdb_dump.c
@@ -0,0 +1,319 @@
+/* mdb_dump.c - memory-mapped database dump tool */
+/*
+ * Copyright 2011-2021 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <signal.h>
+#include "lmdb.h"
+
+#ifdef _WIN32
+#define Z "I"
+#else
+#define Z "z"
+#endif
+
+#define PRINT 1
+static int mode;
+
+typedef struct flagbit {
+ int bit;
+ char *name;
+} flagbit;
+
+flagbit dbflags[] = {
+ { MDB_REVERSEKEY, "reversekey" },
+ { MDB_DUPSORT, "dupsort" },
+ { MDB_INTEGERKEY, "integerkey" },
+ { MDB_DUPFIXED, "dupfixed" },
+ { MDB_INTEGERDUP, "integerdup" },
+ { MDB_REVERSEDUP, "reversedup" },
+ { 0, NULL }
+};
+
+static volatile sig_atomic_t gotsig;
+
+static void dumpsig( int sig )
+{
+ gotsig=1;
+}
+
+static const char hexc[] = "0123456789abcdef";
+
+static void hex(unsigned char c)
+{
+ putchar(hexc[c >> 4]);
+ putchar(hexc[c & 0xf]);
+}
+
+static void text(MDB_val *v)
+{
+ unsigned char *c, *end;
+
+ putchar(' ');
+ c = v->mv_data;
+ end = c + v->mv_size;
+ while (c < end) {
+ if (isprint(*c)) {
+ if (*c == '\\')
+ putchar('\\');
+ putchar(*c);
+ } else {
+ putchar('\\');
+ hex(*c);
+ }
+ c++;
+ }
+ putchar('\n');
+}
+
+static void byte(MDB_val *v)
+{
+ unsigned char *c, *end;
+
+ putchar(' ');
+ c = v->mv_data;
+ end = c + v->mv_size;
+ while (c < end) {
+ hex(*c++);
+ }
+ putchar('\n');
+}
+
+/* Dump in BDB-compatible format */
+static int dumpit(MDB_txn *txn, MDB_dbi dbi, char *name)
+{
+ MDB_cursor *mc;
+ MDB_stat ms;
+ MDB_val key, data;
+ MDB_envinfo info;
+ unsigned int flags;
+ int rc, i;
+
+ rc = mdb_dbi_flags(txn, dbi, &flags);
+ if (rc) return rc;
+
+ rc = mdb_stat(txn, dbi, &ms);
+ if (rc) return rc;
+
+ rc = mdb_env_info(mdb_txn_env(txn), &info);
+ if (rc) return rc;
+
+ printf("VERSION=3\n");
+ printf("format=%s\n", mode & PRINT ? "print" : "bytevalue");
+ if (name)
+ printf("database=%s\n", name);
+ printf("type=btree\n");
+ printf("mapsize=%" Z "u\n", info.me_mapsize);
+ if (info.me_mapaddr)
+ printf("mapaddr=%p\n", info.me_mapaddr);
+ printf("maxreaders=%u\n", info.me_maxreaders);
+
+ if (flags & MDB_DUPSORT)
+ printf("duplicates=1\n");
+
+ for (i=0; dbflags[i].bit; i++)
+ if (flags & dbflags[i].bit)
+ printf("%s=1\n", dbflags[i].name);
+
+ printf("db_pagesize=%d\n", ms.ms_psize);
+ printf("HEADER=END\n");
+
+ rc = mdb_cursor_open(txn, dbi, &mc);
+ if (rc) return rc;
+
+ while ((rc = mdb_cursor_get(mc, &key, &data, MDB_NEXT) == MDB_SUCCESS)) {
+ if (gotsig) {
+ rc = EINTR;
+ break;
+ }
+ if (mode & PRINT) {
+ text(&key);
+ text(&data);
+ } else {
+ byte(&key);
+ byte(&data);
+ }
+ }
+ printf("DATA=END\n");
+ if (rc == MDB_NOTFOUND)
+ rc = MDB_SUCCESS;
+
+ return rc;
+}
+
+static void usage(char *prog)
+{
+ fprintf(stderr, "usage: %s [-V] [-f output] [-l] [-n] [-p] [-a|-s subdb] dbpath\n", prog);
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char *argv[])
+{
+ int i, rc;
+ MDB_env *env;
+ MDB_txn *txn;
+ MDB_dbi dbi;
+ char *prog = argv[0];
+ char *envname;
+ char *subname = NULL;
+ int alldbs = 0, envflags = 0, list = 0;
+
+ if (argc < 2) {
+ usage(prog);
+ }
+
+ /* -a: dump main DB and all subDBs
+ * -s: dump only the named subDB
+ * -n: use NOSUBDIR flag on env_open
+ * -p: use printable characters
+ * -f: write to file instead of stdout
+ * -V: print version and exit
+ * (default) dump only the main DB
+ */
+ while ((i = getopt(argc, argv, "af:lnps:V")) != EOF) {
+ switch(i) {
+ case 'V':
+ printf("%s\n", MDB_VERSION_STRING);
+ exit(0);
+ break;
+ case 'l':
+ list = 1;
+ /*FALLTHROUGH*/;
+ case 'a':
+ if (subname)
+ usage(prog);
+ alldbs++;
+ break;
+ case 'f':
+ if (freopen(optarg, "w", stdout) == NULL) {
+ fprintf(stderr, "%s: %s: reopen: %s\n",
+ prog, optarg, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'n':
+ envflags |= MDB_NOSUBDIR;
+ break;
+ case 'p':
+ mode |= PRINT;
+ break;
+ case 's':
+ if (alldbs)
+ usage(prog);
+ subname = optarg;
+ break;
+ default:
+ usage(prog);
+ }
+ }
+
+ if (optind != argc - 1)
+ usage(prog);
+
+#ifdef SIGPIPE
+ signal(SIGPIPE, dumpsig);
+#endif
+#ifdef SIGHUP
+ signal(SIGHUP, dumpsig);
+#endif
+ signal(SIGINT, dumpsig);
+ signal(SIGTERM, dumpsig);
+
+ envname = argv[optind];
+ rc = mdb_env_create(&env);
+ if (rc) {
+ fprintf(stderr, "mdb_env_create failed, error %d %s\n", rc, mdb_strerror(rc));
+ return EXIT_FAILURE;
+ }
+
+ if (alldbs || subname) {
+ mdb_env_set_maxdbs(env, 2);
+ }
+
+ rc = mdb_env_open(env, envname, envflags | MDB_RDONLY, 0664);
+ if (rc) {
+ fprintf(stderr, "mdb_env_open failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto env_close;
+ }
+
+ rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
+ if (rc) {
+ fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto env_close;
+ }
+
+ rc = mdb_open(txn, subname, 0, &dbi);
+ if (rc) {
+ fprintf(stderr, "mdb_open failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto txn_abort;
+ }
+
+ if (alldbs) {
+ MDB_cursor *cursor;
+ MDB_val key;
+ int count = 0;
+
+ rc = mdb_cursor_open(txn, dbi, &cursor);
+ if (rc) {
+ fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto txn_abort;
+ }
+ while ((rc = mdb_cursor_get(cursor, &key, NULL, MDB_NEXT_NODUP)) == 0) {
+ char *str;
+ MDB_dbi db2;
+ if (memchr(key.mv_data, '\0', key.mv_size))
+ continue;
+ count++;
+ str = malloc(key.mv_size+1);
+ memcpy(str, key.mv_data, key.mv_size);
+ str[key.mv_size] = '\0';
+ rc = mdb_open(txn, str, 0, &db2);
+ if (rc == MDB_SUCCESS) {
+ if (list) {
+ printf("%s\n", str);
+ list++;
+ } else {
+ rc = dumpit(txn, db2, str);
+ if (rc)
+ break;
+ }
+ mdb_close(env, db2);
+ }
+ free(str);
+ if (rc) continue;
+ }
+ mdb_cursor_close(cursor);
+ if (!count) {
+ fprintf(stderr, "%s: %s does not contain multiple databases\n", prog, envname);
+ rc = MDB_NOTFOUND;
+ } else if (rc == MDB_NOTFOUND) {
+ rc = MDB_SUCCESS;
+ }
+ } else {
+ rc = dumpit(txn, dbi, subname);
+ }
+ if (rc && rc != MDB_NOTFOUND)
+ fprintf(stderr, "%s: %s: %s\n", prog, envname, mdb_strerror(rc));
+
+ mdb_close(env, dbi);
+txn_abort:
+ mdb_txn_abort(txn);
+env_close:
+ mdb_env_close(env);
+
+ return rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/libraries/liblmdb/mdb_load.1 b/libraries/liblmdb/mdb_load.1
new file mode 100644
index 0000000..78439a1
--- /dev/null
+++ b/libraries/liblmdb/mdb_load.1
@@ -0,0 +1,84 @@
+.TH MDB_LOAD 1 "2015/09/30" "LMDB 0.9.17"
+.\" Copyright 2014-2021 Howard Chu, Symas Corp. All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+mdb_load \- LMDB environment import tool
+.SH SYNOPSIS
+.B mdb_load
+[\c
+.BR \-V ]
+[\c
+.BI \-f \ file\fR]
+[\c
+.BR \-n ]
+[\c
+.BI \-s \ subdb\fR]
+[\c
+.BR \-N ]
+[\c
+.BR \-T ]
+.BR \ envpath
+.SH DESCRIPTION
+The
+.B mdb_load
+utility reads from the standard input and loads it into the
+LMDB environment
+.BR envpath .
+
+The input to
+.B mdb_load
+must be in the output format specified by the
+.BR mdb_dump (1)
+utility or as specified by the
+.B -T
+option below.
+.SH OPTIONS
+.TP
+.BR \-V
+Write the library version number to the standard output, and exit.
+.TP
+.BR \-a
+Append all records in the order they appear in the input. The input is assumed to already be
+in correctly sorted order and no sorting or checking for redundant values will be performed.
+This option must be used to reload data that was produced by running
+.B mdb_dump
+on a database that uses custom compare functions.
+.TP
+.BR \-f \ file
+Read from the specified file instead of from the standard input.
+.TP
+.BR \-n
+Load an LMDB database which does not use subdirectories.
+.TP
+.BR \-s \ subdb
+Load a specific subdatabase. If no database is specified, data is loaded into the main database.
+.TP
+.BR \-N
+Don't overwrite existing records when loading into an already existing database; just skip them.
+.TP
+.BR \-T
+Load data from simple text files. The input must be paired lines of text, where the first
+line of the pair is the key item, and the second line of the pair is its corresponding
+data item.
+
+A simple escape mechanism, where newline and backslash (\\) characters are special, is
+applied to the text input. Newline characters are interpreted as record separators.
+Backslash characters in the text will be interpreted in one of two ways: If the backslash
+character precedes another backslash character, the pair will be interpreted as a literal
+backslash. If the backslash character precedes any other character, the two characters
+following the backslash will be interpreted as a hexadecimal specification of a single
+character; for example, \\0a is a newline character in the ASCII character set.
+
+For this reason, any backslash or newline characters that naturally occur in the text
+input must be escaped to avoid misinterpretation by
+.BR mdb_load .
+
+.SH DIAGNOSTICS
+Exit status is zero if no errors occur.
+Errors result in a non-zero exit status and
+a diagnostic message being written to standard error.
+
+.SH "SEE ALSO"
+.BR mdb_dump (1)
+.SH AUTHOR
+Howard Chu of Symas Corporation <http://www.symas.com>
diff --git a/libraries/liblmdb/mdb_load.c b/libraries/liblmdb/mdb_load.c
new file mode 100644
index 0000000..d2a3cec
--- /dev/null
+++ b/libraries/liblmdb/mdb_load.c
@@ -0,0 +1,496 @@
+/* mdb_load.c - memory-mapped database load tool */
+/*
+ * Copyright 2011-2021 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include "lmdb.h"
+
+#define PRINT 1
+#define NOHDR 2
+static int mode;
+
+static char *subname = NULL;
+
+static size_t lineno;
+static int version;
+
+static int flags;
+
+static char *prog;
+
+static int Eof;
+
+static MDB_envinfo info;
+
+static MDB_val kbuf, dbuf;
+static MDB_val k0buf;
+
+#ifdef _WIN32
+#define Z "I"
+#else
+#define Z "z"
+#endif
+
+#define STRLENOF(s) (sizeof(s)-1)
+
+typedef struct flagbit {
+ int bit;
+ char *name;
+ int len;
+} flagbit;
+
+#define S(s) s, STRLENOF(s)
+
+flagbit dbflags[] = {
+ { MDB_REVERSEKEY, S("reversekey") },
+ { MDB_DUPSORT, S("dupsort") },
+ { MDB_INTEGERKEY, S("integerkey") },
+ { MDB_DUPFIXED, S("dupfixed") },
+ { MDB_INTEGERDUP, S("integerdup") },
+ { MDB_REVERSEDUP, S("reversedup") },
+ { 0, NULL, 0 }
+};
+
+static void readhdr(void)
+{
+ char *ptr;
+
+ flags = 0;
+ while (fgets(dbuf.mv_data, dbuf.mv_size, stdin) != NULL) {
+ lineno++;
+ if (!strncmp(dbuf.mv_data, "VERSION=", STRLENOF("VERSION="))) {
+ version=atoi((char *)dbuf.mv_data+STRLENOF("VERSION="));
+ if (version > 3) {
+ fprintf(stderr, "%s: line %" Z "d: unsupported VERSION %d\n",
+ prog, lineno, version);
+ exit(EXIT_FAILURE);
+ }
+ } else if (!strncmp(dbuf.mv_data, "HEADER=END", STRLENOF("HEADER=END"))) {
+ break;
+ } else if (!strncmp(dbuf.mv_data, "format=", STRLENOF("format="))) {
+ if (!strncmp((char *)dbuf.mv_data+STRLENOF("FORMAT="), "print", STRLENOF("print")))
+ mode |= PRINT;
+ else if (strncmp((char *)dbuf.mv_data+STRLENOF("FORMAT="), "bytevalue", STRLENOF("bytevalue"))) {
+ fprintf(stderr, "%s: line %" Z "d: unsupported FORMAT %s\n",
+ prog, lineno, (char *)dbuf.mv_data+STRLENOF("FORMAT="));
+ exit(EXIT_FAILURE);
+ }
+ } else if (!strncmp(dbuf.mv_data, "database=", STRLENOF("database="))) {
+ ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size);
+ if (ptr) *ptr = '\0';
+ if (subname) free(subname);
+ subname = strdup((char *)dbuf.mv_data+STRLENOF("database="));
+ } else if (!strncmp(dbuf.mv_data, "type=", STRLENOF("type="))) {
+ if (strncmp((char *)dbuf.mv_data+STRLENOF("type="), "btree", STRLENOF("btree"))) {
+ fprintf(stderr, "%s: line %" Z "d: unsupported type %s\n",
+ prog, lineno, (char *)dbuf.mv_data+STRLENOF("type="));
+ exit(EXIT_FAILURE);
+ }
+ } else if (!strncmp(dbuf.mv_data, "mapaddr=", STRLENOF("mapaddr="))) {
+ int i;
+ ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size);
+ if (ptr) *ptr = '\0';
+ i = sscanf((char *)dbuf.mv_data+STRLENOF("mapaddr="), "%p", &info.me_mapaddr);
+ if (i != 1) {
+ fprintf(stderr, "%s: line %" Z "d: invalid mapaddr %s\n",
+ prog, lineno, (char *)dbuf.mv_data+STRLENOF("mapaddr="));
+ exit(EXIT_FAILURE);
+ }
+ } else if (!strncmp(dbuf.mv_data, "mapsize=", STRLENOF("mapsize="))) {
+ int i;
+ ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size);
+ if (ptr) *ptr = '\0';
+ i = sscanf((char *)dbuf.mv_data+STRLENOF("mapsize="), "%" Z "u", &info.me_mapsize);
+ if (i != 1) {
+ fprintf(stderr, "%s: line %" Z "d: invalid mapsize %s\n",
+ prog, lineno, (char *)dbuf.mv_data+STRLENOF("mapsize="));
+ exit(EXIT_FAILURE);
+ }
+ } else if (!strncmp(dbuf.mv_data, "maxreaders=", STRLENOF("maxreaders="))) {
+ int i;
+ ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size);
+ if (ptr) *ptr = '\0';
+ i = sscanf((char *)dbuf.mv_data+STRLENOF("maxreaders="), "%u", &info.me_maxreaders);
+ if (i != 1) {
+ fprintf(stderr, "%s: line %" Z "d: invalid maxreaders %s\n",
+ prog, lineno, (char *)dbuf.mv_data+STRLENOF("maxreaders="));
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ int i;
+ for (i=0; dbflags[i].bit; i++) {
+ if (!strncmp(dbuf.mv_data, dbflags[i].name, dbflags[i].len) &&
+ ((char *)dbuf.mv_data)[dbflags[i].len] == '=') {
+ flags |= dbflags[i].bit;
+ break;
+ }
+ }
+ if (!dbflags[i].bit) {
+ ptr = memchr(dbuf.mv_data, '=', dbuf.mv_size);
+ if (!ptr) {
+ fprintf(stderr, "%s: line %" Z "d: unexpected format\n",
+ prog, lineno);
+ exit(EXIT_FAILURE);
+ } else {
+ *ptr = '\0';
+ fprintf(stderr, "%s: line %" Z "d: unrecognized keyword ignored: %s\n",
+ prog, lineno, (char *)dbuf.mv_data);
+ }
+ }
+ }
+ }
+}
+
+static void badend(void)
+{
+ fprintf(stderr, "%s: line %" Z "d: unexpected end of input\n",
+ prog, lineno);
+}
+
+static int unhex(unsigned char *c2)
+{
+ int x, c;
+ x = *c2++ & 0x4f;
+ if (x & 0x40)
+ x -= 55;
+ c = x << 4;
+ x = *c2 & 0x4f;
+ if (x & 0x40)
+ x -= 55;
+ c |= x;
+ return c;
+}
+
+static int readline(MDB_val *out, MDB_val *buf)
+{
+ unsigned char *c1, *c2, *end;
+ size_t len, l2;
+ int c;
+
+ if (!(mode & NOHDR)) {
+ c = fgetc(stdin);
+ if (c == EOF) {
+ Eof = 1;
+ return EOF;
+ }
+ if (c != ' ') {
+ lineno++;
+ if (fgets(buf->mv_data, buf->mv_size, stdin) == NULL) {
+badend:
+ Eof = 1;
+ badend();
+ return EOF;
+ }
+ if (c == 'D' && !strncmp(buf->mv_data, "ATA=END", STRLENOF("ATA=END")))
+ return EOF;
+ goto badend;
+ }
+ }
+ if (fgets(buf->mv_data, buf->mv_size, stdin) == NULL) {
+ Eof = 1;
+ return EOF;
+ }
+ lineno++;
+
+ c1 = buf->mv_data;
+ len = strlen((char *)c1);
+ l2 = len;
+
+ /* Is buffer too short? */
+ while (c1[len-1] != '\n') {
+ buf->mv_data = realloc(buf->mv_data, buf->mv_size*2);
+ if (!buf->mv_data) {
+ Eof = 1;
+ fprintf(stderr, "%s: line %" Z "d: out of memory, line too long\n",
+ prog, lineno);
+ return EOF;
+ }
+ c1 = buf->mv_data;
+ c1 += l2;
+ if (fgets((char *)c1, buf->mv_size+1, stdin) == NULL) {
+ Eof = 1;
+ badend();
+ return EOF;
+ }
+ buf->mv_size *= 2;
+ len = strlen((char *)c1);
+ l2 += len;
+ }
+ c1 = c2 = buf->mv_data;
+ len = l2;
+ c1[--len] = '\0';
+ end = c1 + len;
+
+ if (mode & PRINT) {
+ while (c2 < end) {
+ if (*c2 == '\\') {
+ if (c2[1] == '\\') {
+ *c1++ = *c2;
+ } else {
+ if (c2+3 > end || !isxdigit(c2[1]) || !isxdigit(c2[2])) {
+ Eof = 1;
+ badend();
+ return EOF;
+ }
+ *c1++ = unhex(++c2);
+ }
+ c2 += 2;
+ } else {
+ /* copies are redundant when no escapes were used */
+ *c1++ = *c2++;
+ }
+ }
+ } else {
+ /* odd length not allowed */
+ if (len & 1) {
+ Eof = 1;
+ badend();
+ return EOF;
+ }
+ while (c2 < end) {
+ if (!isxdigit(*c2) || !isxdigit(c2[1])) {
+ Eof = 1;
+ badend();
+ return EOF;
+ }
+ *c1++ = unhex(c2);
+ c2 += 2;
+ }
+ }
+ c2 = out->mv_data = buf->mv_data;
+ out->mv_size = c1 - c2;
+
+ return 0;
+}
+
+static void usage(void)
+{
+ fprintf(stderr, "usage: %s [-V] [-a] [-f input] [-n] [-s name] [-N] [-T] dbpath\n", prog);
+ exit(EXIT_FAILURE);
+}
+
+static int greater(const MDB_val *a, const MDB_val *b)
+{
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ int i, rc;
+ MDB_env *env;
+ MDB_txn *txn;
+ MDB_cursor *mc;
+ MDB_dbi dbi;
+ char *envname;
+ int envflags = MDB_NOSYNC, putflags = 0;
+ int dohdr = 0, append = 0;
+ MDB_val prevk;
+
+ prog = argv[0];
+
+ if (argc < 2) {
+ usage();
+ }
+
+ /* -a: append records in input order
+ * -f: load file instead of stdin
+ * -n: use NOSUBDIR flag on env_open
+ * -s: load into named subDB
+ * -N: use NOOVERWRITE on puts
+ * -T: read plaintext
+ * -V: print version and exit
+ */
+ while ((i = getopt(argc, argv, "af:ns:NTV")) != EOF) {
+ switch(i) {
+ case 'V':
+ printf("%s\n", MDB_VERSION_STRING);
+ exit(0);
+ break;
+ case 'a':
+ append = 1;
+ break;
+ case 'f':
+ if (freopen(optarg, "r", stdin) == NULL) {
+ fprintf(stderr, "%s: %s: reopen: %s\n",
+ prog, optarg, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'n':
+ envflags |= MDB_NOSUBDIR;
+ break;
+ case 's':
+ subname = strdup(optarg);
+ break;
+ case 'N':
+ putflags = MDB_NOOVERWRITE|MDB_NODUPDATA;
+ break;
+ case 'T':
+ mode |= NOHDR | PRINT;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (optind != argc - 1)
+ usage();
+
+ dbuf.mv_size = 4096;
+ dbuf.mv_data = malloc(dbuf.mv_size);
+
+ if (!(mode & NOHDR))
+ readhdr();
+
+ envname = argv[optind];
+ rc = mdb_env_create(&env);
+ if (rc) {
+ fprintf(stderr, "mdb_env_create failed, error %d %s\n", rc, mdb_strerror(rc));
+ return EXIT_FAILURE;
+ }
+
+ mdb_env_set_maxdbs(env, 2);
+
+ if (info.me_maxreaders)
+ mdb_env_set_maxreaders(env, info.me_maxreaders);
+
+ if (info.me_mapsize)
+ mdb_env_set_mapsize(env, info.me_mapsize);
+
+ if (info.me_mapaddr)
+ envflags |= MDB_FIXEDMAP;
+
+ rc = mdb_env_open(env, envname, envflags, 0664);
+ if (rc) {
+ fprintf(stderr, "mdb_env_open failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto env_close;
+ }
+
+ kbuf.mv_size = mdb_env_get_maxkeysize(env) * 2 + 2;
+ kbuf.mv_data = malloc(kbuf.mv_size * 2);
+ k0buf.mv_size = kbuf.mv_size;
+ k0buf.mv_data = (char *)kbuf.mv_data + kbuf.mv_size;
+ prevk.mv_data = k0buf.mv_data;
+
+ while(!Eof) {
+ MDB_val key, data;
+ int batch = 0;
+ flags = 0;
+ int appflag;
+
+ if (!dohdr) {
+ dohdr = 1;
+ } else if (!(mode & NOHDR))
+ readhdr();
+
+ rc = mdb_txn_begin(env, NULL, 0, &txn);
+ if (rc) {
+ fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto env_close;
+ }
+
+ rc = mdb_open(txn, subname, flags|MDB_CREATE, &dbi);
+ if (rc) {
+ fprintf(stderr, "mdb_open failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto txn_abort;
+ }
+ prevk.mv_size = 0;
+ if (append) {
+ mdb_set_compare(txn, dbi, greater);
+ if (flags & MDB_DUPSORT)
+ mdb_set_dupsort(txn, dbi, greater);
+ }
+
+ rc = mdb_cursor_open(txn, dbi, &mc);
+ if (rc) {
+ fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto txn_abort;
+ }
+
+ while(1) {
+ rc = readline(&key, &kbuf);
+ if (rc) /* rc == EOF */
+ break;
+
+ rc = readline(&data, &dbuf);
+ if (rc) {
+ fprintf(stderr, "%s: line %" Z "d: failed to read key value\n", prog, lineno);
+ goto txn_abort;
+ }
+
+ if (append) {
+ appflag = MDB_APPEND;
+ if (flags & MDB_DUPSORT) {
+ if (prevk.mv_size == key.mv_size && !memcmp(prevk.mv_data, key.mv_data, key.mv_size))
+ appflag = MDB_CURRENT|MDB_APPENDDUP;
+ else {
+ memcpy(prevk.mv_data, key.mv_data, key.mv_size);
+ prevk.mv_size = key.mv_size;
+ }
+ }
+ } else {
+ appflag = 0;
+ }
+ rc = mdb_cursor_put(mc, &key, &data, putflags|appflag);
+ if (rc == MDB_KEYEXIST && putflags)
+ continue;
+ if (rc) {
+ fprintf(stderr, "mdb_cursor_put failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto txn_abort;
+ }
+ batch++;
+ if (batch == 100) {
+ rc = mdb_txn_commit(txn);
+ if (rc) {
+ fprintf(stderr, "%s: line %" Z "d: txn_commit: %s\n",
+ prog, lineno, mdb_strerror(rc));
+ goto env_close;
+ }
+ rc = mdb_txn_begin(env, NULL, 0, &txn);
+ if (rc) {
+ fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto env_close;
+ }
+ rc = mdb_cursor_open(txn, dbi, &mc);
+ if (rc) {
+ fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto txn_abort;
+ }
+ if (appflag & MDB_APPENDDUP) {
+ MDB_val k, d;
+ mdb_cursor_get(mc, &k, &d, MDB_LAST);
+ }
+ batch = 0;
+ }
+ }
+ rc = mdb_txn_commit(txn);
+ txn = NULL;
+ if (rc) {
+ fprintf(stderr, "%s: line %" Z "d: txn_commit: %s\n",
+ prog, lineno, mdb_strerror(rc));
+ goto env_close;
+ }
+ mdb_dbi_close(env, dbi);
+ }
+
+txn_abort:
+ mdb_txn_abort(txn);
+env_close:
+ mdb_env_close(env);
+
+ return rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/libraries/liblmdb/mdb_stat.1 b/libraries/liblmdb/mdb_stat.1
new file mode 100644
index 0000000..62e8ce1
--- /dev/null
+++ b/libraries/liblmdb/mdb_stat.1
@@ -0,0 +1,64 @@
+.TH MDB_STAT 1 "2015/09/30" "LMDB 0.9.17"
+.\" Copyright 2012-2021 Howard Chu, Symas Corp. All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+mdb_stat \- LMDB environment status tool
+.SH SYNOPSIS
+.B mdb_stat
+[\c
+.BR \-V ]
+[\c
+.BR \-e ]
+[\c
+.BR \-f [ f [ f ]]]
+[\c
+.BR \-n ]
+[\c
+.BR \-r [ r ]]
+[\c
+.BR \-a \ |
+.BI \-s \ subdb\fR]
+.BR \ envpath
+.SH DESCRIPTION
+The
+.B mdb_stat
+utility displays the status of an LMDB environment.
+.SH OPTIONS
+.TP
+.BR \-V
+Write the library version number to the standard output, and exit.
+.TP
+.BR \-e
+Display information about the database environment.
+.TP
+.BR \-f
+Display information about the environment freelist.
+If \fB\-ff\fP is given, summarize each freelist entry.
+If \fB\-fff\fP is given, display the full list of page IDs in the freelist.
+.TP
+.BR \-n
+Display the status of an LMDB database which does not use subdirectories.
+.TP
+.BR \-r
+Display information about the environment reader table.
+Shows the process ID, thread ID, and transaction ID for each active
+reader slot. The process ID and transaction ID are in decimal, the
+thread ID is in hexadecimal. The transaction ID is displayed as "-"
+if the reader does not currently have a read transaction open.
+If \fB\-rr\fP is given, check for stale entries in the reader
+table and clear them. The reader table will be printed again
+after the check is performed.
+.TP
+.BR \-a
+Display the status of all of the subdatabases in the environment.
+.TP
+.BR \-s \ subdb
+Display the status of a specific subdatabase.
+.SH DIAGNOSTICS
+Exit status is zero if no errors occur.
+Errors result in a non-zero exit status and
+a diagnostic message being written to standard error.
+.SH "SEE ALSO"
+.BR mdb_copy (1)
+.SH AUTHOR
+Howard Chu of Symas Corporation <http://www.symas.com>
diff --git a/libraries/liblmdb/mdb_stat.c b/libraries/liblmdb/mdb_stat.c
new file mode 100644
index 0000000..3a81175
--- /dev/null
+++ b/libraries/liblmdb/mdb_stat.c
@@ -0,0 +1,263 @@
+/* mdb_stat.c - memory-mapped database status tool */
+/*
+ * Copyright 2011-2021 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "lmdb.h"
+
+#ifdef _WIN32
+#define Z "I"
+#else
+#define Z "z"
+#endif
+
+static void prstat(MDB_stat *ms)
+{
+#if 0
+ printf(" Page size: %u\n", ms->ms_psize);
+#endif
+ printf(" Tree depth: %u\n", ms->ms_depth);
+ printf(" Branch pages: %"Z"u\n", ms->ms_branch_pages);
+ printf(" Leaf pages: %"Z"u\n", ms->ms_leaf_pages);
+ printf(" Overflow pages: %"Z"u\n", ms->ms_overflow_pages);
+ printf(" Entries: %"Z"u\n", ms->ms_entries);
+}
+
+static void usage(char *prog)
+{
+ fprintf(stderr, "usage: %s [-V] [-n] [-e] [-r[r]] [-f[f[f]]] [-a|-s subdb] dbpath\n", prog);
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char *argv[])
+{
+ int i, rc;
+ MDB_env *env;
+ MDB_txn *txn;
+ MDB_dbi dbi;
+ MDB_stat mst;
+ MDB_envinfo mei;
+ char *prog = argv[0];
+ char *envname;
+ char *subname = NULL;
+ int alldbs = 0, envinfo = 0, envflags = 0, freinfo = 0, rdrinfo = 0;
+
+ if (argc < 2) {
+ usage(prog);
+ }
+
+ /* -a: print stat of main DB and all subDBs
+ * -s: print stat of only the named subDB
+ * -e: print env info
+ * -f: print freelist info
+ * -r: print reader info
+ * -n: use NOSUBDIR flag on env_open
+ * -V: print version and exit
+ * (default) print stat of only the main DB
+ */
+ while ((i = getopt(argc, argv, "Vaefnrs:")) != EOF) {
+ switch(i) {
+ case 'V':
+ printf("%s\n", MDB_VERSION_STRING);
+ exit(0);
+ break;
+ case 'a':
+ if (subname)
+ usage(prog);
+ alldbs++;
+ break;
+ case 'e':
+ envinfo++;
+ break;
+ case 'f':
+ freinfo++;
+ break;
+ case 'n':
+ envflags |= MDB_NOSUBDIR;
+ break;
+ case 'r':
+ rdrinfo++;
+ break;
+ case 's':
+ if (alldbs)
+ usage(prog);
+ subname = optarg;
+ break;
+ default:
+ usage(prog);
+ }
+ }
+
+ if (optind != argc - 1)
+ usage(prog);
+
+ envname = argv[optind];
+ rc = mdb_env_create(&env);
+ if (rc) {
+ fprintf(stderr, "mdb_env_create failed, error %d %s\n", rc, mdb_strerror(rc));
+ return EXIT_FAILURE;
+ }
+
+ if (alldbs || subname) {
+ mdb_env_set_maxdbs(env, 4);
+ }
+
+ rc = mdb_env_open(env, envname, envflags | MDB_RDONLY, 0664);
+ if (rc) {
+ fprintf(stderr, "mdb_env_open failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto env_close;
+ }
+
+ if (envinfo) {
+ (void)mdb_env_stat(env, &mst);
+ (void)mdb_env_info(env, &mei);
+ printf("Environment Info\n");
+ printf(" Map address: %p\n", mei.me_mapaddr);
+ printf(" Map size: %"Z"u\n", mei.me_mapsize);
+ printf(" Page size: %u\n", mst.ms_psize);
+ printf(" Max pages: %"Z"u\n", mei.me_mapsize / mst.ms_psize);
+ printf(" Number of pages used: %"Z"u\n", mei.me_last_pgno+1);
+ printf(" Last transaction ID: %"Z"u\n", mei.me_last_txnid);
+ printf(" Max readers: %u\n", mei.me_maxreaders);
+ printf(" Number of readers used: %u\n", mei.me_numreaders);
+ }
+
+ if (rdrinfo) {
+ printf("Reader Table Status\n");
+ rc = mdb_reader_list(env, (MDB_msg_func *)fputs, stdout);
+ if (rdrinfo > 1) {
+ int dead;
+ mdb_reader_check(env, &dead);
+ printf(" %d stale readers cleared.\n", dead);
+ rc = mdb_reader_list(env, (MDB_msg_func *)fputs, stdout);
+ }
+ if (!(subname || alldbs || freinfo))
+ goto env_close;
+ }
+
+ rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
+ if (rc) {
+ fprintf(stderr, "mdb_txn_begin failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto env_close;
+ }
+
+ if (freinfo) {
+ MDB_cursor *cursor;
+ MDB_val key, data;
+ size_t pages = 0, *iptr;
+
+ printf("Freelist Status\n");
+ dbi = 0;
+ rc = mdb_cursor_open(txn, dbi, &cursor);
+ if (rc) {
+ fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto txn_abort;
+ }
+ rc = mdb_stat(txn, dbi, &mst);
+ if (rc) {
+ fprintf(stderr, "mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto txn_abort;
+ }
+ prstat(&mst);
+ while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
+ iptr = data.mv_data;
+ pages += *iptr;
+ if (freinfo > 1) {
+ char *bad = "";
+ size_t pg, prev;
+ ssize_t i, j, span = 0;
+ j = *iptr++;
+ for (i = j, prev = 1; --i >= 0; ) {
+ pg = iptr[i];
+ if (pg <= prev)
+ bad = " [bad sequence]";
+ prev = pg;
+ pg += span;
+ for (; i >= span && iptr[i-span] == pg; span++, pg++) ;
+ }
+ printf(" Transaction %"Z"u, %"Z"d pages, maxspan %"Z"d%s\n",
+ *(size_t *)key.mv_data, j, span, bad);
+ if (freinfo > 2) {
+ for (--j; j >= 0; ) {
+ pg = iptr[j];
+ for (span=1; --j >= 0 && iptr[j] == pg+span; span++) ;
+ printf(span>1 ? " %9"Z"u[%"Z"d]\n" : " %9"Z"u\n",
+ pg, span);
+ }
+ }
+ }
+ }
+ mdb_cursor_close(cursor);
+ printf(" Free pages: %"Z"u\n", pages);
+ }
+
+ rc = mdb_open(txn, subname, 0, &dbi);
+ if (rc) {
+ fprintf(stderr, "mdb_open failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto txn_abort;
+ }
+
+ rc = mdb_stat(txn, dbi, &mst);
+ if (rc) {
+ fprintf(stderr, "mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto txn_abort;
+ }
+ printf("Status of %s\n", subname ? subname : "Main DB");
+ prstat(&mst);
+
+ if (alldbs) {
+ MDB_cursor *cursor;
+ MDB_val key;
+
+ rc = mdb_cursor_open(txn, dbi, &cursor);
+ if (rc) {
+ fprintf(stderr, "mdb_cursor_open failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto txn_abort;
+ }
+ while ((rc = mdb_cursor_get(cursor, &key, NULL, MDB_NEXT_NODUP)) == 0) {
+ char *str;
+ MDB_dbi db2;
+ if (memchr(key.mv_data, '\0', key.mv_size))
+ continue;
+ str = malloc(key.mv_size+1);
+ memcpy(str, key.mv_data, key.mv_size);
+ str[key.mv_size] = '\0';
+ rc = mdb_open(txn, str, 0, &db2);
+ if (rc == MDB_SUCCESS)
+ printf("Status of %s\n", str);
+ free(str);
+ if (rc) continue;
+ rc = mdb_stat(txn, db2, &mst);
+ if (rc) {
+ fprintf(stderr, "mdb_stat failed, error %d %s\n", rc, mdb_strerror(rc));
+ goto txn_abort;
+ }
+ prstat(&mst);
+ mdb_close(env, db2);
+ }
+ mdb_cursor_close(cursor);
+ }
+
+ if (rc == MDB_NOTFOUND)
+ rc = MDB_SUCCESS;
+
+ mdb_close(env, dbi);
+txn_abort:
+ mdb_txn_abort(txn);
+env_close:
+ mdb_env_close(env);
+
+ return rc ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/libraries/liblmdb/midl.c b/libraries/liblmdb/midl.c
new file mode 100644
index 0000000..1cbe879
--- /dev/null
+++ b/libraries/liblmdb/midl.c
@@ -0,0 +1,359 @@
+/** @file midl.c
+ * @brief ldap bdb back-end ID List functions */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2021 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include "midl.h"
+
+/** @defgroup internal LMDB Internals
+ * @{
+ */
+/** @defgroup idls ID List Management
+ * @{
+ */
+#define CMP(x,y) ( (x) < (y) ? -1 : (x) > (y) )
+
+unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id )
+{
+ /*
+ * binary search of id in ids
+ * if found, returns position of id
+ * if not found, returns first position greater than id
+ */
+ unsigned base = 0;
+ unsigned cursor = 1;
+ int val = 0;
+ unsigned n = ids[0];
+
+ while( 0 < n ) {
+ unsigned pivot = n >> 1;
+ cursor = base + pivot + 1;
+ val = CMP( ids[cursor], id );
+
+ if( val < 0 ) {
+ n = pivot;
+
+ } else if ( val > 0 ) {
+ base = cursor;
+ n -= pivot + 1;
+
+ } else {
+ return cursor;
+ }
+ }
+
+ if( val > 0 ) {
+ ++cursor;
+ }
+ return cursor;
+}
+
+#if 0 /* superseded by append/sort */
+int mdb_midl_insert( MDB_IDL ids, MDB_ID id )
+{
+ unsigned x, i;
+
+ x = mdb_midl_search( ids, id );
+ assert( x > 0 );
+
+ if( x < 1 ) {
+ /* internal error */
+ return -2;
+ }
+
+ if ( x <= ids[0] && ids[x] == id ) {
+ /* duplicate */
+ assert(0);
+ return -1;
+ }
+
+ if ( ++ids[0] >= MDB_IDL_DB_MAX ) {
+ /* no room */
+ --ids[0];
+ return -2;
+
+ } else {
+ /* insert id */
+ for (i=ids[0]; i>x; i--)
+ ids[i] = ids[i-1];
+ ids[x] = id;
+ }
+
+ return 0;
+}
+#endif
+
+MDB_IDL mdb_midl_alloc(int num)
+{
+ MDB_IDL ids = malloc((num+2) * sizeof(MDB_ID));
+ if (ids) {
+ *ids++ = num;
+ *ids = 0;
+ }
+ return ids;
+}
+
+void mdb_midl_free(MDB_IDL ids)
+{
+ if (ids)
+ free(ids-1);
+}
+
+void mdb_midl_shrink( MDB_IDL *idp )
+{
+ MDB_IDL ids = *idp;
+ if (*(--ids) > MDB_IDL_UM_MAX &&
+ (ids = realloc(ids, (MDB_IDL_UM_MAX+2) * sizeof(MDB_ID))))
+ {
+ *ids++ = MDB_IDL_UM_MAX;
+ *idp = ids;
+ }
+}
+
+static int mdb_midl_grow( MDB_IDL *idp, int num )
+{
+ MDB_IDL idn = *idp-1;
+ /* grow it */
+ idn = realloc(idn, (*idn + num + 2) * sizeof(MDB_ID));
+ if (!idn)
+ return ENOMEM;
+ *idn++ += num;
+ *idp = idn;
+ return 0;
+}
+
+int mdb_midl_need( MDB_IDL *idp, unsigned num )
+{
+ MDB_IDL ids = *idp;
+ num += ids[0];
+ if (num > ids[-1]) {
+ num = (num + num/4 + (256 + 2)) & -256;
+ if (!(ids = realloc(ids-1, num * sizeof(MDB_ID))))
+ return ENOMEM;
+ *ids++ = num - 2;
+ *idp = ids;
+ }
+ return 0;
+}
+
+int mdb_midl_append( MDB_IDL *idp, MDB_ID id )
+{
+ MDB_IDL ids = *idp;
+ /* Too big? */
+ if (ids[0] >= ids[-1]) {
+ if (mdb_midl_grow(idp, MDB_IDL_UM_MAX))
+ return ENOMEM;
+ ids = *idp;
+ }
+ ids[0]++;
+ ids[ids[0]] = id;
+ return 0;
+}
+
+int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app )
+{
+ MDB_IDL ids = *idp;
+ /* Too big? */
+ if (ids[0] + app[0] >= ids[-1]) {
+ if (mdb_midl_grow(idp, app[0]))
+ return ENOMEM;
+ ids = *idp;
+ }
+ memcpy(&ids[ids[0]+1], &app[1], app[0] * sizeof(MDB_ID));
+ ids[0] += app[0];
+ return 0;
+}
+
+int mdb_midl_append_range( MDB_IDL *idp, MDB_ID id, unsigned n )
+{
+ MDB_ID *ids = *idp, len = ids[0];
+ /* Too big? */
+ if (len + n > ids[-1]) {
+ if (mdb_midl_grow(idp, n | MDB_IDL_UM_MAX))
+ return ENOMEM;
+ ids = *idp;
+ }
+ ids[0] = len + n;
+ ids += len;
+ while (n)
+ ids[n--] = id++;
+ return 0;
+}
+
+void mdb_midl_xmerge( MDB_IDL idl, MDB_IDL merge )
+{
+ MDB_ID old_id, merge_id, i = merge[0], j = idl[0], k = i+j, total = k;
+ idl[0] = (MDB_ID)-1; /* delimiter for idl scan below */
+ old_id = idl[j];
+ while (i) {
+ merge_id = merge[i--];
+ for (; old_id < merge_id; old_id = idl[--j])
+ idl[k--] = old_id;
+ idl[k--] = merge_id;
+ }
+ idl[0] = total;
+}
+
+/* Quicksort + Insertion sort for small arrays */
+
+#define SMALL 8
+#define MIDL_SWAP(a,b) { itmp=(a); (a)=(b); (b)=itmp; }
+
+void
+mdb_midl_sort( MDB_IDL ids )
+{
+ /* Max possible depth of int-indexed tree * 2 items/level */
+ int istack[sizeof(int)*CHAR_BIT * 2];
+ int i,j,k,l,ir,jstack;
+ MDB_ID a, itmp;
+
+ ir = (int)ids[0];
+ l = 1;
+ jstack = 0;
+ for(;;) {
+ if (ir - l < SMALL) { /* Insertion sort */
+ for (j=l+1;j<=ir;j++) {
+ a = ids[j];
+ for (i=j-1;i>=1;i--) {
+ if (ids[i] >= a) break;
+ ids[i+1] = ids[i];
+ }
+ ids[i+1] = a;
+ }
+ if (jstack == 0) break;
+ ir = istack[jstack--];
+ l = istack[jstack--];
+ } else {
+ k = (l + ir) >> 1; /* Choose median of left, center, right */
+ MIDL_SWAP(ids[k], ids[l+1]);
+ if (ids[l] < ids[ir]) {
+ MIDL_SWAP(ids[l], ids[ir]);
+ }
+ if (ids[l+1] < ids[ir]) {
+ MIDL_SWAP(ids[l+1], ids[ir]);
+ }
+ if (ids[l] < ids[l+1]) {
+ MIDL_SWAP(ids[l], ids[l+1]);
+ }
+ i = l+1;
+ j = ir;
+ a = ids[l+1];
+ for(;;) {
+ do i++; while(ids[i] > a);
+ do j--; while(ids[j] < a);
+ if (j < i) break;
+ MIDL_SWAP(ids[i],ids[j]);
+ }
+ ids[l+1] = ids[j];
+ ids[j] = a;
+ jstack += 2;
+ if (ir-i+1 >= j-l) {
+ istack[jstack] = ir;
+ istack[jstack-1] = i;
+ ir = j-1;
+ } else {
+ istack[jstack] = j-1;
+ istack[jstack-1] = l;
+ l = i;
+ }
+ }
+ }
+}
+
+unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id )
+{
+ /*
+ * binary search of id in ids
+ * if found, returns position of id
+ * if not found, returns first position greater than id
+ */
+ unsigned base = 0;
+ unsigned cursor = 1;
+ int val = 0;
+ unsigned n = (unsigned)ids[0].mid;
+
+ while( 0 < n ) {
+ unsigned pivot = n >> 1;
+ cursor = base + pivot + 1;
+ val = CMP( id, ids[cursor].mid );
+
+ if( val < 0 ) {
+ n = pivot;
+
+ } else if ( val > 0 ) {
+ base = cursor;
+ n -= pivot + 1;
+
+ } else {
+ return cursor;
+ }
+ }
+
+ if( val > 0 ) {
+ ++cursor;
+ }
+ return cursor;
+}
+
+int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id )
+{
+ unsigned x, i;
+
+ x = mdb_mid2l_search( ids, id->mid );
+
+ if( x < 1 ) {
+ /* internal error */
+ return -2;
+ }
+
+ if ( x <= ids[0].mid && ids[x].mid == id->mid ) {
+ /* duplicate */
+ return -1;
+ }
+
+ if ( ids[0].mid >= MDB_IDL_UM_MAX ) {
+ /* too big */
+ return -2;
+
+ } else {
+ /* insert id */
+ ids[0].mid++;
+ for (i=(unsigned)ids[0].mid; i>x; i--)
+ ids[i] = ids[i-1];
+ ids[x] = *id;
+ }
+
+ return 0;
+}
+
+int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id )
+{
+ /* Too big? */
+ if (ids[0].mid >= MDB_IDL_UM_MAX) {
+ return -2;
+ }
+ ids[0].mid++;
+ ids[ids[0].mid] = *id;
+ return 0;
+}
+
+/** @} */
+/** @} */
diff --git a/libraries/liblmdb/midl.h b/libraries/liblmdb/midl.h
new file mode 100644
index 0000000..2075206
--- /dev/null
+++ b/libraries/liblmdb/midl.h
@@ -0,0 +1,186 @@
+/** @file midl.h
+ * @brief LMDB ID List header file.
+ *
+ * This file was originally part of back-bdb but has been
+ * modified for use in libmdb. Most of the macros defined
+ * in this file are unused, just left over from the original.
+ *
+ * This file is only used internally in libmdb and its definitions
+ * are not exposed publicly.
+ */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2021 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _MDB_MIDL_H_
+#define _MDB_MIDL_H_
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @defgroup internal LMDB Internals
+ * @{
+ */
+
+/** @defgroup idls ID List Management
+ * @{
+ */
+ /** A generic unsigned ID number. These were entryIDs in back-bdb.
+ * Preferably it should have the same size as a pointer.
+ */
+typedef size_t MDB_ID;
+
+ /** An IDL is an ID List, a sorted array of IDs. The first
+ * element of the array is a counter for how many actual
+ * IDs are in the list. In the original back-bdb code, IDLs are
+ * sorted in ascending order. For libmdb IDLs are sorted in
+ * descending order.
+ */
+typedef MDB_ID *MDB_IDL;
+
+/* IDL sizes - likely should be even bigger
+ * limiting factors: sizeof(ID), thread stack size
+ */
+#define MDB_IDL_LOGN 16 /* DB_SIZE is 2^16, UM_SIZE is 2^17 */
+#define MDB_IDL_DB_SIZE (1<<MDB_IDL_LOGN)
+#define MDB_IDL_UM_SIZE (1<<(MDB_IDL_LOGN+1))
+
+#define MDB_IDL_DB_MAX (MDB_IDL_DB_SIZE-1)
+#define MDB_IDL_UM_MAX (MDB_IDL_UM_SIZE-1)
+
+#define MDB_IDL_SIZEOF(ids) (((ids)[0]+1) * sizeof(MDB_ID))
+#define MDB_IDL_IS_ZERO(ids) ( (ids)[0] == 0 )
+#define MDB_IDL_CPY( dst, src ) (memcpy( dst, src, MDB_IDL_SIZEOF( src ) ))
+#define MDB_IDL_FIRST( ids ) ( (ids)[1] )
+#define MDB_IDL_LAST( ids ) ( (ids)[(ids)[0]] )
+
+ /** Current max length of an #mdb_midl_alloc()ed IDL */
+#define MDB_IDL_ALLOCLEN( ids ) ( (ids)[-1] )
+
+ /** Append ID to IDL. The IDL must be big enough. */
+#define mdb_midl_xappend(idl, id) do { \
+ MDB_ID *xidl = (idl), xlen = ++(xidl[0]); \
+ xidl[xlen] = (id); \
+ } while (0)
+
+ /** Search for an ID in an IDL.
+ * @param[in] ids The IDL to search.
+ * @param[in] id The ID to search for.
+ * @return The index of the first ID greater than or equal to \b id.
+ */
+unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id );
+
+ /** Allocate an IDL.
+ * Allocates memory for an IDL of the given size.
+ * @return IDL on success, NULL on failure.
+ */
+MDB_IDL mdb_midl_alloc(int num);
+
+ /** Free an IDL.
+ * @param[in] ids The IDL to free.
+ */
+void mdb_midl_free(MDB_IDL ids);
+
+ /** Shrink an IDL.
+ * Return the IDL to the default size if it has grown larger.
+ * @param[in,out] idp Address of the IDL to shrink.
+ */
+void mdb_midl_shrink(MDB_IDL *idp);
+
+ /** Make room for num additional elements in an IDL.
+ * @param[in,out] idp Address of the IDL.
+ * @param[in] num Number of elements to make room for.
+ * @return 0 on success, ENOMEM on failure.
+ */
+int mdb_midl_need(MDB_IDL *idp, unsigned num);
+
+ /** Append an ID onto an IDL.
+ * @param[in,out] idp Address of the IDL to append to.
+ * @param[in] id The ID to append.
+ * @return 0 on success, ENOMEM if the IDL is too large.
+ */
+int mdb_midl_append( MDB_IDL *idp, MDB_ID id );
+
+ /** Append an IDL onto an IDL.
+ * @param[in,out] idp Address of the IDL to append to.
+ * @param[in] app The IDL to append.
+ * @return 0 on success, ENOMEM if the IDL is too large.
+ */
+int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app );
+
+ /** Append an ID range onto an IDL.
+ * @param[in,out] idp Address of the IDL to append to.
+ * @param[in] id The lowest ID to append.
+ * @param[in] n Number of IDs to append.
+ * @return 0 on success, ENOMEM if the IDL is too large.
+ */
+int mdb_midl_append_range( MDB_IDL *idp, MDB_ID id, unsigned n );
+
+ /** Merge an IDL onto an IDL. The destination IDL must be big enough.
+ * @param[in] idl The IDL to merge into.
+ * @param[in] merge The IDL to merge.
+ */
+void mdb_midl_xmerge( MDB_IDL idl, MDB_IDL merge );
+
+ /** Sort an IDL.
+ * @param[in,out] ids The IDL to sort.
+ */
+void mdb_midl_sort( MDB_IDL ids );
+
+ /** An ID2 is an ID/pointer pair.
+ */
+typedef struct MDB_ID2 {
+ MDB_ID mid; /**< The ID */
+ void *mptr; /**< The pointer */
+} MDB_ID2;
+
+ /** An ID2L is an ID2 List, a sorted array of ID2s.
+ * The first element's \b mid member is a count of how many actual
+ * elements are in the array. The \b mptr member of the first element is unused.
+ * The array is sorted in ascending order by \b mid.
+ */
+typedef MDB_ID2 *MDB_ID2L;
+
+ /** Search for an ID in an ID2L.
+ * @param[in] ids The ID2L to search.
+ * @param[in] id The ID to search for.
+ * @return The index of the first ID2 whose \b mid member is greater than or equal to \b id.
+ */
+unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id );
+
+
+ /** Insert an ID2 into a ID2L.
+ * @param[in,out] ids The ID2L to insert into.
+ * @param[in] id The ID2 to insert.
+ * @return 0 on success, -1 if the ID was already present in the ID2L.
+ */
+int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id );
+
+ /** Append an ID2 into a ID2L.
+ * @param[in,out] ids The ID2L to append into.
+ * @param[in] id The ID2 to append.
+ * @return 0 on success, -2 if the ID2L is too big.
+ */
+int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id );
+
+/** @} */
+/** @} */
+#ifdef __cplusplus
+}
+#endif
+#endif /* _MDB_MIDL_H_ */
diff --git a/libraries/liblmdb/mtest.c b/libraries/liblmdb/mtest.c
new file mode 100644
index 0000000..c03daa1
--- /dev/null
+++ b/libraries/liblmdb/mtest.c
@@ -0,0 +1,177 @@
+/* mtest.c - memory-mapped database tester/toy */
+/*
+ * Copyright 2011-2021 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include "lmdb.h"
+
+#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
+#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))
+#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \
+ "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort()))
+
+int main(int argc,char * argv[])
+{
+ int i = 0, j = 0, rc;
+ MDB_env *env;
+ MDB_dbi dbi;
+ MDB_val key, data;
+ MDB_txn *txn;
+ MDB_stat mst;
+ MDB_cursor *cursor, *cur2;
+ MDB_cursor_op op;
+ int count;
+ int *values;
+ char sval[32] = "";
+
+ srand(time(NULL));
+
+ count = (rand()%384) + 64;
+ values = (int *)malloc(count*sizeof(int));
+
+ for(i = 0;i<count;i++) {
+ values[i] = rand()%1024;
+ }
+
+ E(mdb_env_create(&env));
+ E(mdb_env_set_maxreaders(env, 1));
+ E(mdb_env_set_mapsize(env, 10485760));
+ E(mdb_env_open(env, "./testdb", MDB_FIXEDMAP /*|MDB_NOSYNC*/, 0664));
+
+ E(mdb_txn_begin(env, NULL, 0, &txn));
+ E(mdb_dbi_open(txn, NULL, 0, &dbi));
+
+ key.mv_size = sizeof(int);
+ key.mv_data = sval;
+
+ printf("Adding %d values\n", count);
+ for (i=0;i<count;i++) {
+ sprintf(sval, "%03x %d foo bar", values[i], values[i]);
+ /* Set <data> in each iteration, since MDB_NOOVERWRITE may modify it */
+ data.mv_size = sizeof(sval);
+ data.mv_data = sval;
+ if (RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NOOVERWRITE))) {
+ j++;
+ data.mv_size = sizeof(sval);
+ data.mv_data = sval;
+ }
+ }
+ if (j) printf("%d duplicates skipped\n", j);
+ E(mdb_txn_commit(txn));
+ E(mdb_env_stat(env, &mst));
+
+ E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn));
+ E(mdb_cursor_open(txn, dbi, &cursor));
+ while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
+ printf("key: %p %.*s, data: %p %.*s\n",
+ key.mv_data, (int) key.mv_size, (char *) key.mv_data,
+ data.mv_data, (int) data.mv_size, (char *) data.mv_data);
+ }
+ CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
+ mdb_cursor_close(cursor);
+ mdb_txn_abort(txn);
+
+ j=0;
+ key.mv_data = sval;
+ for (i= count - 1; i > -1; i-= (rand()%5)) {
+ j++;
+ txn=NULL;
+ E(mdb_txn_begin(env, NULL, 0, &txn));
+ sprintf(sval, "%03x ", values[i]);
+ if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, NULL))) {
+ j--;
+ mdb_txn_abort(txn);
+ } else {
+ E(mdb_txn_commit(txn));
+ }
+ }
+ free(values);
+ printf("Deleted %d values\n", j);
+
+ E(mdb_env_stat(env, &mst));
+ E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn));
+ E(mdb_cursor_open(txn, dbi, &cursor));
+ printf("Cursor next\n");
+ while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
+ printf("key: %.*s, data: %.*s\n",
+ (int) key.mv_size, (char *) key.mv_data,
+ (int) data.mv_size, (char *) data.mv_data);
+ }
+ CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
+ printf("Cursor last\n");
+ E(mdb_cursor_get(cursor, &key, &data, MDB_LAST));
+ printf("key: %.*s, data: %.*s\n",
+ (int) key.mv_size, (char *) key.mv_data,
+ (int) data.mv_size, (char *) data.mv_data);
+ printf("Cursor prev\n");
+ while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) {
+ printf("key: %.*s, data: %.*s\n",
+ (int) key.mv_size, (char *) key.mv_data,
+ (int) data.mv_size, (char *) data.mv_data);
+ }
+ CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
+ printf("Cursor last/prev\n");
+ E(mdb_cursor_get(cursor, &key, &data, MDB_LAST));
+ printf("key: %.*s, data: %.*s\n",
+ (int) key.mv_size, (char *) key.mv_data,
+ (int) data.mv_size, (char *) data.mv_data);
+ E(mdb_cursor_get(cursor, &key, &data, MDB_PREV));
+ printf("key: %.*s, data: %.*s\n",
+ (int) key.mv_size, (char *) key.mv_data,
+ (int) data.mv_size, (char *) data.mv_data);
+
+ mdb_cursor_close(cursor);
+ mdb_txn_abort(txn);
+
+ printf("Deleting with cursor\n");
+ E(mdb_txn_begin(env, NULL, 0, &txn));
+ E(mdb_cursor_open(txn, dbi, &cur2));
+ for (i=0; i<50; i++) {
+ if (RES(MDB_NOTFOUND, mdb_cursor_get(cur2, &key, &data, MDB_NEXT)))
+ break;
+ printf("key: %p %.*s, data: %p %.*s\n",
+ key.mv_data, (int) key.mv_size, (char *) key.mv_data,
+ data.mv_data, (int) data.mv_size, (char *) data.mv_data);
+ E(mdb_del(txn, dbi, &key, NULL));
+ }
+
+ printf("Restarting cursor in txn\n");
+ for (op=MDB_FIRST, i=0; i<=32; op=MDB_NEXT, i++) {
+ if (RES(MDB_NOTFOUND, mdb_cursor_get(cur2, &key, &data, op)))
+ break;
+ printf("key: %p %.*s, data: %p %.*s\n",
+ key.mv_data, (int) key.mv_size, (char *) key.mv_data,
+ data.mv_data, (int) data.mv_size, (char *) data.mv_data);
+ }
+ mdb_cursor_close(cur2);
+ E(mdb_txn_commit(txn));
+
+ printf("Restarting cursor outside txn\n");
+ E(mdb_txn_begin(env, NULL, 0, &txn));
+ E(mdb_cursor_open(txn, dbi, &cursor));
+ for (op=MDB_FIRST, i=0; i<=32; op=MDB_NEXT, i++) {
+ if (RES(MDB_NOTFOUND, mdb_cursor_get(cursor, &key, &data, op)))
+ break;
+ printf("key: %p %.*s, data: %p %.*s\n",
+ key.mv_data, (int) key.mv_size, (char *) key.mv_data,
+ data.mv_data, (int) data.mv_size, (char *) data.mv_data);
+ }
+ mdb_cursor_close(cursor);
+ mdb_txn_abort(txn);
+
+ mdb_dbi_close(env, dbi);
+ mdb_env_close(env);
+
+ return 0;
+}
diff --git a/libraries/liblmdb/mtest2.c b/libraries/liblmdb/mtest2.c
new file mode 100644
index 0000000..1ce4c94
--- /dev/null
+++ b/libraries/liblmdb/mtest2.c
@@ -0,0 +1,124 @@
+/* mtest2.c - memory-mapped database tester/toy */
+/*
+ * Copyright 2011-2021 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* Just like mtest.c, but using a subDB instead of the main DB */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include "lmdb.h"
+
+#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
+#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))
+#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \
+ "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort()))
+
+int main(int argc,char * argv[])
+{
+ int i = 0, j = 0, rc;
+ MDB_env *env;
+ MDB_dbi dbi;
+ MDB_val key, data;
+ MDB_txn *txn;
+ MDB_stat mst;
+ MDB_cursor *cursor;
+ int count;
+ int *values;
+ char sval[32] = "";
+
+ srand(time(NULL));
+
+ count = (rand()%384) + 64;
+ values = (int *)malloc(count*sizeof(int));
+
+ for(i = 0;i<count;i++) {
+ values[i] = rand()%1024;
+ }
+
+ E(mdb_env_create(&env));
+ E(mdb_env_set_maxreaders(env, 1));
+ E(mdb_env_set_mapsize(env, 10485760));
+ E(mdb_env_set_maxdbs(env, 4));
+ E(mdb_env_open(env, "./testdb", MDB_FIXEDMAP|MDB_NOSYNC, 0664));
+
+ E(mdb_txn_begin(env, NULL, 0, &txn));
+ E(mdb_dbi_open(txn, "id1", MDB_CREATE, &dbi));
+
+ key.mv_size = sizeof(int);
+ key.mv_data = sval;
+
+ printf("Adding %d values\n", count);
+ for (i=0;i<count;i++) {
+ sprintf(sval, "%03x %d foo bar", values[i], values[i]);
+ data.mv_size = sizeof(sval);
+ data.mv_data = sval;
+ if (RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NOOVERWRITE)))
+ j++;
+ }
+ if (j) printf("%d duplicates skipped\n", j);
+ E(mdb_txn_commit(txn));
+ E(mdb_env_stat(env, &mst));
+
+ E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn));
+ E(mdb_cursor_open(txn, dbi, &cursor));
+ while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
+ printf("key: %p %.*s, data: %p %.*s\n",
+ key.mv_data, (int) key.mv_size, (char *) key.mv_data,
+ data.mv_data, (int) data.mv_size, (char *) data.mv_data);
+ }
+ CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
+ mdb_cursor_close(cursor);
+ mdb_txn_abort(txn);
+
+ j=0;
+ key.mv_data = sval;
+ for (i= count - 1; i > -1; i-= (rand()%5)) {
+ j++;
+ txn=NULL;
+ E(mdb_txn_begin(env, NULL, 0, &txn));
+ sprintf(sval, "%03x ", values[i]);
+ if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, NULL))) {
+ j--;
+ mdb_txn_abort(txn);
+ } else {
+ E(mdb_txn_commit(txn));
+ }
+ }
+ free(values);
+ printf("Deleted %d values\n", j);
+
+ E(mdb_env_stat(env, &mst));
+ E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn));
+ E(mdb_cursor_open(txn, dbi, &cursor));
+ printf("Cursor next\n");
+ while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
+ printf("key: %.*s, data: %.*s\n",
+ (int) key.mv_size, (char *) key.mv_data,
+ (int) data.mv_size, (char *) data.mv_data);
+ }
+ CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
+ printf("Cursor prev\n");
+ while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) {
+ printf("key: %.*s, data: %.*s\n",
+ (int) key.mv_size, (char *) key.mv_data,
+ (int) data.mv_size, (char *) data.mv_data);
+ }
+ CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
+ mdb_cursor_close(cursor);
+ mdb_txn_abort(txn);
+
+ mdb_dbi_close(env, dbi);
+ mdb_env_close(env);
+ return 0;
+}
diff --git a/libraries/liblmdb/mtest3.c b/libraries/liblmdb/mtest3.c
new file mode 100644
index 0000000..f8da0d3
--- /dev/null
+++ b/libraries/liblmdb/mtest3.c
@@ -0,0 +1,133 @@
+/* mtest3.c - memory-mapped database tester/toy */
+/*
+ * Copyright 2011-2021 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* Tests for sorted duplicate DBs */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "lmdb.h"
+
+#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
+#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))
+#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \
+ "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort()))
+
+int main(int argc,char * argv[])
+{
+ int i = 0, j = 0, rc;
+ MDB_env *env;
+ MDB_dbi dbi;
+ MDB_val key, data;
+ MDB_txn *txn;
+ MDB_stat mst;
+ MDB_cursor *cursor;
+ int count;
+ int *values;
+ char sval[32];
+ char kval[sizeof(int)];
+
+ srand(time(NULL));
+
+ memset(sval, 0, sizeof(sval));
+
+ count = (rand()%384) + 64;
+ values = (int *)malloc(count*sizeof(int));
+
+ for(i = 0;i<count;i++) {
+ values[i] = rand()%1024;
+ }
+
+ E(mdb_env_create(&env));
+ E(mdb_env_set_mapsize(env, 10485760));
+ E(mdb_env_set_maxdbs(env, 4));
+ E(mdb_env_open(env, "./testdb", MDB_FIXEDMAP|MDB_NOSYNC, 0664));
+
+ E(mdb_txn_begin(env, NULL, 0, &txn));
+ E(mdb_dbi_open(txn, "id2", MDB_CREATE|MDB_DUPSORT, &dbi));
+
+ key.mv_size = sizeof(int);
+ key.mv_data = kval;
+ data.mv_size = sizeof(sval);
+ data.mv_data = sval;
+
+ printf("Adding %d values\n", count);
+ for (i=0;i<count;i++) {
+ if (!(i & 0x0f))
+ sprintf(kval, "%03x", values[i]);
+ sprintf(sval, "%03x %d foo bar", values[i], values[i]);
+ if (RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NODUPDATA)))
+ j++;
+ }
+ if (j) printf("%d duplicates skipped\n", j);
+ E(mdb_txn_commit(txn));
+ E(mdb_env_stat(env, &mst));
+
+ E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn));
+ E(mdb_cursor_open(txn, dbi, &cursor));
+ while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
+ printf("key: %p %.*s, data: %p %.*s\n",
+ key.mv_data, (int) key.mv_size, (char *) key.mv_data,
+ data.mv_data, (int) data.mv_size, (char *) data.mv_data);
+ }
+ CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
+ mdb_cursor_close(cursor);
+ mdb_txn_abort(txn);
+
+ j=0;
+
+ for (i= count - 1; i > -1; i-= (rand()%5)) {
+ j++;
+ txn=NULL;
+ E(mdb_txn_begin(env, NULL, 0, &txn));
+ sprintf(kval, "%03x", values[i & ~0x0f]);
+ sprintf(sval, "%03x %d foo bar", values[i], values[i]);
+ key.mv_size = sizeof(int);
+ key.mv_data = kval;
+ data.mv_size = sizeof(sval);
+ data.mv_data = sval;
+ if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, &data))) {
+ j--;
+ mdb_txn_abort(txn);
+ } else {
+ E(mdb_txn_commit(txn));
+ }
+ }
+ free(values);
+ printf("Deleted %d values\n", j);
+
+ E(mdb_env_stat(env, &mst));
+ E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn));
+ E(mdb_cursor_open(txn, dbi, &cursor));
+ printf("Cursor next\n");
+ while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
+ printf("key: %.*s, data: %.*s\n",
+ (int) key.mv_size, (char *) key.mv_data,
+ (int) data.mv_size, (char *) data.mv_data);
+ }
+ CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
+ printf("Cursor prev\n");
+ while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) {
+ printf("key: %.*s, data: %.*s\n",
+ (int) key.mv_size, (char *) key.mv_data,
+ (int) data.mv_size, (char *) data.mv_data);
+ }
+ CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
+ mdb_cursor_close(cursor);
+ mdb_txn_abort(txn);
+
+ mdb_dbi_close(env, dbi);
+ mdb_env_close(env);
+ return 0;
+}
diff --git a/libraries/liblmdb/mtest4.c b/libraries/liblmdb/mtest4.c
new file mode 100644
index 0000000..3d7476c
--- /dev/null
+++ b/libraries/liblmdb/mtest4.c
@@ -0,0 +1,168 @@
+/* mtest4.c - memory-mapped database tester/toy */
+/*
+ * Copyright 2011-2021 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* Tests for sorted duplicate DBs with fixed-size keys */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "lmdb.h"
+
+#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
+#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))
+#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \
+ "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort()))
+
+int main(int argc,char * argv[])
+{
+ int i = 0, j = 0, rc;
+ MDB_env *env;
+ MDB_dbi dbi;
+ MDB_val key, data;
+ MDB_txn *txn;
+ MDB_stat mst;
+ MDB_cursor *cursor;
+ int count;
+ int *values;
+ char sval[8];
+ char kval[sizeof(int)];
+
+ memset(sval, 0, sizeof(sval));
+
+ count = 510;
+ values = (int *)malloc(count*sizeof(int));
+
+ for(i = 0;i<count;i++) {
+ values[i] = i*5;
+ }
+
+ E(mdb_env_create(&env));
+ E(mdb_env_set_mapsize(env, 10485760));
+ E(mdb_env_set_maxdbs(env, 4));
+ E(mdb_env_open(env, "./testdb", MDB_FIXEDMAP|MDB_NOSYNC, 0664));
+
+ E(mdb_txn_begin(env, NULL, 0, &txn));
+ E(mdb_dbi_open(txn, "id4", MDB_CREATE|MDB_DUPSORT|MDB_DUPFIXED, &dbi));
+
+ key.mv_size = sizeof(int);
+ key.mv_data = kval;
+ data.mv_size = sizeof(sval);
+ data.mv_data = sval;
+
+ printf("Adding %d values\n", count);
+ strcpy(kval, "001");
+ for (i=0;i<count;i++) {
+ sprintf(sval, "%07x", values[i]);
+ if (RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NODUPDATA)))
+ j++;
+ }
+ if (j) printf("%d duplicates skipped\n", j);
+ E(mdb_txn_commit(txn));
+ E(mdb_env_stat(env, &mst));
+
+ /* there should be one full page of dups now.
+ */
+ E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn));
+ E(mdb_cursor_open(txn, dbi, &cursor));
+ while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
+ printf("key: %p %.*s, data: %p %.*s\n",
+ key.mv_data, (int) key.mv_size, (char *) key.mv_data,
+ data.mv_data, (int) data.mv_size, (char *) data.mv_data);
+ }
+ CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
+ mdb_cursor_close(cursor);
+ mdb_txn_abort(txn);
+
+ /* test all 3 branches of split code:
+ * 1: new key in lower half
+ * 2: new key at split point
+ * 3: new key in upper half
+ */
+
+ key.mv_size = sizeof(int);
+ key.mv_data = kval;
+ data.mv_size = sizeof(sval);
+ data.mv_data = sval;
+
+ sprintf(sval, "%07x", values[3]+1);
+ E(mdb_txn_begin(env, NULL, 0, &txn));
+ (void)RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NODUPDATA));
+ mdb_txn_abort(txn);
+
+ sprintf(sval, "%07x", values[255]+1);
+ E(mdb_txn_begin(env, NULL, 0, &txn));
+ (void)RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NODUPDATA));
+ mdb_txn_abort(txn);
+
+ sprintf(sval, "%07x", values[500]+1);
+ E(mdb_txn_begin(env, NULL, 0, &txn));
+ (void)RES(MDB_KEYEXIST, mdb_put(txn, dbi, &key, &data, MDB_NODUPDATA));
+ E(mdb_txn_commit(txn));
+
+ /* Try MDB_NEXT_MULTIPLE */
+ E(mdb_txn_begin(env, NULL, 0, &txn));
+ E(mdb_cursor_open(txn, dbi, &cursor));
+ while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT_MULTIPLE)) == 0) {
+ printf("key: %.*s, data: %.*s\n",
+ (int) key.mv_size, (char *) key.mv_data,
+ (int) data.mv_size, (char *) data.mv_data);
+ }
+ CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
+ mdb_cursor_close(cursor);
+ mdb_txn_abort(txn);
+ j=0;
+
+ for (i= count - 1; i > -1; i-= (rand()%3)) {
+ j++;
+ txn=NULL;
+ E(mdb_txn_begin(env, NULL, 0, &txn));
+ sprintf(sval, "%07x", values[i]);
+ key.mv_size = sizeof(int);
+ key.mv_data = kval;
+ data.mv_size = sizeof(sval);
+ data.mv_data = sval;
+ if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, &data))) {
+ j--;
+ mdb_txn_abort(txn);
+ } else {
+ E(mdb_txn_commit(txn));
+ }
+ }
+ free(values);
+ printf("Deleted %d values\n", j);
+
+ E(mdb_env_stat(env, &mst));
+ E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn));
+ E(mdb_cursor_open(txn, dbi, &cursor));
+ printf("Cursor next\n");
+ while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
+ printf("key: %.*s, data: %.*s\n",
+ (int) key.mv_size, (char *) key.mv_data,
+ (int) data.mv_size, (char *) data.mv_data);
+ }
+ CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
+ printf("Cursor prev\n");
+ while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) {
+ printf("key: %.*s, data: %.*s\n",
+ (int) key.mv_size, (char *) key.mv_data,
+ (int) data.mv_size, (char *) data.mv_data);
+ }
+ CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
+ mdb_cursor_close(cursor);
+ mdb_txn_abort(txn);
+
+ mdb_dbi_close(env, dbi);
+ mdb_env_close(env);
+ return 0;
+}
diff --git a/libraries/liblmdb/mtest5.c b/libraries/liblmdb/mtest5.c
new file mode 100644
index 0000000..d7a7307
--- /dev/null
+++ b/libraries/liblmdb/mtest5.c
@@ -0,0 +1,135 @@
+/* mtest5.c - memory-mapped database tester/toy */
+/*
+ * Copyright 2011-2021 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* Tests for sorted duplicate DBs using cursor_put */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "lmdb.h"
+
+#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
+#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))
+#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \
+ "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort()))
+
+int main(int argc,char * argv[])
+{
+ int i = 0, j = 0, rc;
+ MDB_env *env;
+ MDB_dbi dbi;
+ MDB_val key, data;
+ MDB_txn *txn;
+ MDB_stat mst;
+ MDB_cursor *cursor;
+ int count;
+ int *values;
+ char sval[32];
+ char kval[sizeof(int)];
+
+ srand(time(NULL));
+
+ memset(sval, 0, sizeof(sval));
+
+ count = (rand()%384) + 64;
+ values = (int *)malloc(count*sizeof(int));
+
+ for(i = 0;i<count;i++) {
+ values[i] = rand()%1024;
+ }
+
+ E(mdb_env_create(&env));
+ E(mdb_env_set_mapsize(env, 10485760));
+ E(mdb_env_set_maxdbs(env, 4));
+ E(mdb_env_open(env, "./testdb", MDB_FIXEDMAP|MDB_NOSYNC, 0664));
+
+ E(mdb_txn_begin(env, NULL, 0, &txn));
+ E(mdb_dbi_open(txn, "id2", MDB_CREATE|MDB_DUPSORT, &dbi));
+ E(mdb_cursor_open(txn, dbi, &cursor));
+
+ key.mv_size = sizeof(int);
+ key.mv_data = kval;
+ data.mv_size = sizeof(sval);
+ data.mv_data = sval;
+
+ printf("Adding %d values\n", count);
+ for (i=0;i<count;i++) {
+ if (!(i & 0x0f))
+ sprintf(kval, "%03x", values[i]);
+ sprintf(sval, "%03x %d foo bar", values[i], values[i]);
+ if (RES(MDB_KEYEXIST, mdb_cursor_put(cursor, &key, &data, MDB_NODUPDATA)))
+ j++;
+ }
+ if (j) printf("%d duplicates skipped\n", j);
+ mdb_cursor_close(cursor);
+ E(mdb_txn_commit(txn));
+ E(mdb_env_stat(env, &mst));
+
+ E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn));
+ E(mdb_cursor_open(txn, dbi, &cursor));
+ while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
+ printf("key: %p %.*s, data: %p %.*s\n",
+ key.mv_data, (int) key.mv_size, (char *) key.mv_data,
+ data.mv_data, (int) data.mv_size, (char *) data.mv_data);
+ }
+ CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
+ mdb_cursor_close(cursor);
+ mdb_txn_abort(txn);
+
+ j=0;
+
+ for (i= count - 1; i > -1; i-= (rand()%5)) {
+ j++;
+ txn=NULL;
+ E(mdb_txn_begin(env, NULL, 0, &txn));
+ sprintf(kval, "%03x", values[i & ~0x0f]);
+ sprintf(sval, "%03x %d foo bar", values[i], values[i]);
+ key.mv_size = sizeof(int);
+ key.mv_data = kval;
+ data.mv_size = sizeof(sval);
+ data.mv_data = sval;
+ if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, &data))) {
+ j--;
+ mdb_txn_abort(txn);
+ } else {
+ E(mdb_txn_commit(txn));
+ }
+ }
+ free(values);
+ printf("Deleted %d values\n", j);
+
+ E(mdb_env_stat(env, &mst));
+ E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn));
+ E(mdb_cursor_open(txn, dbi, &cursor));
+ printf("Cursor next\n");
+ while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
+ printf("key: %.*s, data: %.*s\n",
+ (int) key.mv_size, (char *) key.mv_data,
+ (int) data.mv_size, (char *) data.mv_data);
+ }
+ CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
+ printf("Cursor prev\n");
+ while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) {
+ printf("key: %.*s, data: %.*s\n",
+ (int) key.mv_size, (char *) key.mv_data,
+ (int) data.mv_size, (char *) data.mv_data);
+ }
+ CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
+ mdb_cursor_close(cursor);
+ mdb_txn_abort(txn);
+
+ mdb_dbi_close(env, dbi);
+ mdb_env_close(env);
+ return 0;
+}
diff --git a/libraries/liblmdb/mtest6.c b/libraries/liblmdb/mtest6.c
new file mode 100644
index 0000000..cf8ba96
--- /dev/null
+++ b/libraries/liblmdb/mtest6.c
@@ -0,0 +1,141 @@
+/* mtest6.c - memory-mapped database tester/toy */
+/*
+ * Copyright 2011-2021 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* Tests for DB splits and merges */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "lmdb.h"
+
+#define E(expr) CHECK((rc = (expr)) == MDB_SUCCESS, #expr)
+#define RES(err, expr) ((rc = expr) == (err) || (CHECK(!rc, #expr), 0))
+#define CHECK(test, msg) ((test) ? (void)0 : ((void)fprintf(stderr, \
+ "%s:%d: %s: %s\n", __FILE__, __LINE__, msg, mdb_strerror(rc)), abort()))
+
+char dkbuf[1024];
+
+int main(int argc,char * argv[])
+{
+ int i = 0, j = 0, rc;
+ MDB_env *env;
+ MDB_dbi dbi;
+ MDB_val key, data, sdata;
+ MDB_txn *txn;
+ MDB_stat mst;
+ MDB_cursor *cursor;
+ int count;
+ int *values;
+ long kval;
+ char *sval;
+
+ srand(time(NULL));
+
+ E(mdb_env_create(&env));
+ E(mdb_env_set_mapsize(env, 10485760));
+ E(mdb_env_set_maxdbs(env, 4));
+ E(mdb_env_open(env, "./testdb", MDB_FIXEDMAP|MDB_NOSYNC, 0664));
+
+ E(mdb_txn_begin(env, NULL, 0, &txn));
+ E(mdb_dbi_open(txn, "id6", MDB_CREATE|MDB_INTEGERKEY, &dbi));
+ E(mdb_cursor_open(txn, dbi, &cursor));
+ E(mdb_stat(txn, dbi, &mst));
+
+ sval = calloc(1, mst.ms_psize / 4);
+ key.mv_size = sizeof(long);
+ key.mv_data = &kval;
+ sdata.mv_size = mst.ms_psize / 4 - 30;
+ sdata.mv_data = sval;
+
+ printf("Adding 12 values, should yield 3 splits\n");
+ for (i=0;i<12;i++) {
+ kval = i*5;
+ sprintf(sval, "%08x", kval);
+ data = sdata;
+ (void)RES(MDB_KEYEXIST, mdb_cursor_put(cursor, &key, &data, MDB_NOOVERWRITE));
+ }
+ printf("Adding 12 more values, should yield 3 splits\n");
+ for (i=0;i<12;i++) {
+ kval = i*5+4;
+ sprintf(sval, "%08x", kval);
+ data = sdata;
+ (void)RES(MDB_KEYEXIST, mdb_cursor_put(cursor, &key, &data, MDB_NOOVERWRITE));
+ }
+ printf("Adding 12 more values, should yield 3 splits\n");
+ for (i=0;i<12;i++) {
+ kval = i*5+1;
+ sprintf(sval, "%08x", kval);
+ data = sdata;
+ (void)RES(MDB_KEYEXIST, mdb_cursor_put(cursor, &key, &data, MDB_NOOVERWRITE));
+ }
+ E(mdb_cursor_get(cursor, &key, &data, MDB_FIRST));
+
+ do {
+ printf("key: %p %s, data: %p %.*s\n",
+ key.mv_data, mdb_dkey(&key, dkbuf),
+ data.mv_data, (int) data.mv_size, (char *) data.mv_data);
+ } while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0);
+ CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
+ mdb_cursor_close(cursor);
+ mdb_txn_commit(txn);
+
+#if 0
+ j=0;
+
+ for (i= count - 1; i > -1; i-= (rand()%5)) {
+ j++;
+ txn=NULL;
+ E(mdb_txn_begin(env, NULL, 0, &txn));
+ sprintf(kval, "%03x", values[i & ~0x0f]);
+ sprintf(sval, "%03x %d foo bar", values[i], values[i]);
+ key.mv_size = sizeof(int);
+ key.mv_data = kval;
+ data.mv_size = sizeof(sval);
+ data.mv_data = sval;
+ if (RES(MDB_NOTFOUND, mdb_del(txn, dbi, &key, &data))) {
+ j--;
+ mdb_txn_abort(txn);
+ } else {
+ E(mdb_txn_commit(txn));
+ }
+ }
+ free(values);
+ printf("Deleted %d values\n", j);
+
+ E(mdb_env_stat(env, &mst));
+ E(mdb_txn_begin(env, NULL, MDB_RDONLY, &txn));
+ E(mdb_cursor_open(txn, dbi, &cursor));
+ printf("Cursor next\n");
+ while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
+ printf("key: %.*s, data: %.*s\n",
+ (int) key.mv_size, (char *) key.mv_data,
+ (int) data.mv_size, (char *) data.mv_data);
+ }
+ CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
+ printf("Cursor prev\n");
+ while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_PREV)) == 0) {
+ printf("key: %.*s, data: %.*s\n",
+ (int) key.mv_size, (char *) key.mv_data,
+ (int) data.mv_size, (char *) data.mv_data);
+ }
+ CHECK(rc == MDB_NOTFOUND, "mdb_cursor_get");
+ mdb_cursor_close(cursor);
+ mdb_txn_abort(txn);
+
+ mdb_dbi_close(env, dbi);
+#endif
+ mdb_env_close(env);
+
+ return 0;
+}
diff --git a/libraries/liblmdb/sample-bdb.txt b/libraries/liblmdb/sample-bdb.txt
new file mode 100644
index 0000000..8ca927c
--- /dev/null
+++ b/libraries/liblmdb/sample-bdb.txt
@@ -0,0 +1,73 @@
+/* sample-bdb.txt - BerkeleyDB toy/sample
+ *
+ * Do a line-by-line comparison of this and sample-mdb.txt
+ */
+/*
+ * Copyright 2012-2021 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <db.h>
+
+int main(int argc,char * argv[])
+{
+ int rc;
+ DB_ENV *env;
+ DB *dbi;
+ DBT key, data;
+ DB_TXN *txn;
+ DBC *cursor;
+ char sval[32], kval[32];
+
+ /* Note: Most error checking omitted for simplicity */
+
+#define FLAGS (DB_INIT_LOCK|DB_INIT_LOG|DB_INIT_TXN|DB_INIT_MPOOL|DB_CREATE|DB_THREAD)
+ rc = db_env_create(&env, 0);
+ rc = env->open(env, "./testdb", FLAGS, 0664);
+ rc = db_create(&dbi, env, 0);
+ rc = env->txn_begin(env, NULL, &txn, 0);
+ rc = dbi->open(dbi, txn, "test.bdb", NULL, DB_BTREE, DB_CREATE, 0664);
+
+ memset(&key, 0, sizeof(DBT));
+ memset(&data, 0, sizeof(DBT));
+ key.size = sizeof(int);
+ key.data = sval;
+ data.size = sizeof(sval);
+ data.data = sval;
+
+ sprintf(sval, "%03x %d foo bar", 32, 3141592);
+ rc = dbi->put(dbi, txn, &key, &data, 0);
+ rc = txn->commit(txn, 0);
+ if (rc) {
+ fprintf(stderr, "txn->commit: (%d) %s\n", rc, db_strerror(rc));
+ goto leave;
+ }
+ rc = env->txn_begin(env, NULL, &txn, 0);
+ rc = dbi->cursor(dbi, txn, &cursor, 0);
+ key.flags = DB_DBT_USERMEM;
+ key.data = kval;
+ key.ulen = sizeof(kval);
+ data.flags = DB_DBT_USERMEM;
+ data.data = sval;
+ data.ulen = sizeof(sval);
+ while ((rc = cursor->c_get(cursor, &key, &data, DB_NEXT)) == 0) {
+ printf("key: %p %.*s, data: %p %.*s\n",
+ key.data, (int) key.size, (char *) key.data,
+ data.data, (int) data.size, (char *) data.data);
+ }
+ rc = cursor->c_close(cursor);
+ rc = txn->abort(txn);
+leave:
+ rc = dbi->close(dbi, 0);
+ rc = env->close(env, 0);
+ return rc;
+}
diff --git a/libraries/liblmdb/sample-mdb.txt b/libraries/liblmdb/sample-mdb.txt
new file mode 100644
index 0000000..2e17316
--- /dev/null
+++ b/libraries/liblmdb/sample-mdb.txt
@@ -0,0 +1,62 @@
+/* sample-mdb.txt - MDB toy/sample
+ *
+ * Do a line-by-line comparison of this and sample-bdb.txt
+ */
+/*
+ * Copyright 2012-2021 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+#include <stdio.h>
+#include "lmdb.h"
+
+int main(int argc,char * argv[])
+{
+ int rc;
+ MDB_env *env;
+ MDB_dbi dbi;
+ MDB_val key, data;
+ MDB_txn *txn;
+ MDB_cursor *cursor;
+ char sval[32];
+
+ /* Note: Most error checking omitted for simplicity */
+
+ rc = mdb_env_create(&env);
+ rc = mdb_env_open(env, "./testdb", 0, 0664);
+ rc = mdb_txn_begin(env, NULL, 0, &txn);
+ rc = mdb_dbi_open(txn, NULL, 0, &dbi);
+
+ key.mv_size = sizeof(int);
+ key.mv_data = sval;
+ data.mv_size = sizeof(sval);
+ data.mv_data = sval;
+
+ sprintf(sval, "%03x %d foo bar", 32, 3141592);
+ rc = mdb_put(txn, dbi, &key, &data, 0);
+ rc = mdb_txn_commit(txn);
+ if (rc) {
+ fprintf(stderr, "mdb_txn_commit: (%d) %s\n", rc, mdb_strerror(rc));
+ goto leave;
+ }
+ rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
+ rc = mdb_cursor_open(txn, dbi, &cursor);
+ while ((rc = mdb_cursor_get(cursor, &key, &data, MDB_NEXT)) == 0) {
+ printf("key: %p %.*s, data: %p %.*s\n",
+ key.mv_data, (int) key.mv_size, (char *) key.mv_data,
+ data.mv_data, (int) data.mv_size, (char *) data.mv_data);
+ }
+ mdb_cursor_close(cursor);
+ mdb_txn_abort(txn);
+leave:
+ mdb_dbi_close(env, dbi);
+ mdb_env_close(env);
+ return 0;
+}
diff --git a/libraries/liblmdb/tooltag b/libraries/liblmdb/tooltag
new file mode 100644
index 0000000..229bf16
--- /dev/null
+++ b/libraries/liblmdb/tooltag
@@ -0,0 +1,22 @@
+<tagfile>
+ <compound kind="page">
+ <name>mdb_copy_1</name>
+ <title>mdb_copy - environment copy tool</title>
+ <filename>mdb_copy.1</filename>
+ </compound>
+ <compound kind="page">
+ <name>mdb_dump_1</name>
+ <title>mdb_dump - environment export tool</title>
+ <filename>mdb_dump.1</filename>
+ </compound>
+ <compound kind="page">
+ <name>mdb_load_1</name>
+ <title>mdb_load - environment import tool</title>
+ <filename>mdb_load.1</filename>
+ </compound>
+ <compound kind="page">
+ <name>mdb_stat_1</name>
+ <title>mdb_stat - environment status tool</title>
+ <filename>mdb_stat.1</filename>
+ </compound>
+</tagfile>
diff --git a/libraries/liblunicode/CompositionExclusions.txt b/libraries/liblunicode/CompositionExclusions.txt
new file mode 100644
index 0000000..07a60b8
--- /dev/null
+++ b/libraries/liblunicode/CompositionExclusions.txt
@@ -0,0 +1,176 @@
+# CompositionExclusions-3.2.0.txt
+# Date: 2002-03-19,23:30:28 GMT [MD]
+#
+# This file lists the characters from the UAX #15 Composition Exclusion Table.
+#
+# The format of the comments in this file has been updated since the last version,
+# CompositionExclusions-3.txt. The only substantive change to this file between that
+# version and this one is the addition of U+2ADC FORKING.
+#
+# For more information, see
+# http://www.unicode.org/unicode/reports/tr15/#Primary Exclusion List Table
+# ================================================
+
+# (1) Script Specifics
+# This list of characters cannot be derived from the UnicodeData file.
+# ================================================
+
+0958 # DEVANAGARI LETTER QA
+0959 # DEVANAGARI LETTER KHHA
+095A # DEVANAGARI LETTER GHHA
+095B # DEVANAGARI LETTER ZA
+095C # DEVANAGARI LETTER DDDHA
+095D # DEVANAGARI LETTER RHA
+095E # DEVANAGARI LETTER FA
+095F # DEVANAGARI LETTER YYA
+09DC # BENGALI LETTER RRA
+09DD # BENGALI LETTER RHA
+09DF # BENGALI LETTER YYA
+0A33 # GURMUKHI LETTER LLA
+0A36 # GURMUKHI LETTER SHA
+0A59 # GURMUKHI LETTER KHHA
+0A5A # GURMUKHI LETTER GHHA
+0A5B # GURMUKHI LETTER ZA
+0A5E # GURMUKHI LETTER FA
+0B5C # ORIYA LETTER RRA
+0B5D # ORIYA LETTER RHA
+0F43 # TIBETAN LETTER GHA
+0F4D # TIBETAN LETTER DDHA
+0F52 # TIBETAN LETTER DHA
+0F57 # TIBETAN LETTER BHA
+0F5C # TIBETAN LETTER DZHA
+0F69 # TIBETAN LETTER KSSA
+0F76 # TIBETAN VOWEL SIGN VOCALIC R
+0F78 # TIBETAN VOWEL SIGN VOCALIC L
+0F93 # TIBETAN SUBJOINED LETTER GHA
+0F9D # TIBETAN SUBJOINED LETTER DDHA
+0FA2 # TIBETAN SUBJOINED LETTER DHA
+0FA7 # TIBETAN SUBJOINED LETTER BHA
+0FAC # TIBETAN SUBJOINED LETTER DZHA
+0FB9 # TIBETAN SUBJOINED LETTER KSSA
+FB1D # HEBREW LETTER YOD WITH HIRIQ
+FB1F # HEBREW LIGATURE YIDDISH YOD YOD PATAH
+FB2A # HEBREW LETTER SHIN WITH SHIN DOT
+FB2B # HEBREW LETTER SHIN WITH SIN DOT
+FB2C # HEBREW LETTER SHIN WITH DAGESH AND SHIN DOT
+FB2D # HEBREW LETTER SHIN WITH DAGESH AND SIN DOT
+FB2E # HEBREW LETTER ALEF WITH PATAH
+FB2F # HEBREW LETTER ALEF WITH QAMATS
+FB30 # HEBREW LETTER ALEF WITH MAPIQ
+FB31 # HEBREW LETTER BET WITH DAGESH
+FB32 # HEBREW LETTER GIMEL WITH DAGESH
+FB33 # HEBREW LETTER DALET WITH DAGESH
+FB34 # HEBREW LETTER HE WITH MAPIQ
+FB35 # HEBREW LETTER VAV WITH DAGESH
+FB36 # HEBREW LETTER ZAYIN WITH DAGESH
+FB38 # HEBREW LETTER TET WITH DAGESH
+FB39 # HEBREW LETTER YOD WITH DAGESH
+FB3A # HEBREW LETTER FINAL KAF WITH DAGESH
+FB3B # HEBREW LETTER KAF WITH DAGESH
+FB3C # HEBREW LETTER LAMED WITH DAGESH
+FB3E # HEBREW LETTER MEM WITH DAGESH
+FB40 # HEBREW LETTER NUN WITH DAGESH
+FB41 # HEBREW LETTER SAMEKH WITH DAGESH
+FB43 # HEBREW LETTER FINAL PE WITH DAGESH
+FB44 # HEBREW LETTER PE WITH DAGESH
+FB46 # HEBREW LETTER TSADI WITH DAGESH
+FB47 # HEBREW LETTER QOF WITH DAGESH
+FB48 # HEBREW LETTER RESH WITH DAGESH
+FB49 # HEBREW LETTER SHIN WITH DAGESH
+FB4A # HEBREW LETTER TAV WITH DAGESH
+FB4B # HEBREW LETTER VAV WITH HOLAM
+FB4C # HEBREW LETTER BET WITH RAFE
+FB4D # HEBREW LETTER KAF WITH RAFE
+FB4E # HEBREW LETTER PE WITH RAFE
+
+# Total code points: 67
+
+# ================================================
+# (2) Post Composition Version precomposed characters
+# These characters cannot be derived solely from the UnicodeData.txt file
+# in this version of Unicode.
+# ================================================
+
+2ADC # FORKING
+1D15E # MUSICAL SYMBOL HALF NOTE
+1D15F # MUSICAL SYMBOL QUARTER NOTE
+1D160 # MUSICAL SYMBOL EIGHTH NOTE
+1D161 # MUSICAL SYMBOL SIXTEENTH NOTE
+1D162 # MUSICAL SYMBOL THIRTY-SECOND NOTE
+1D163 # MUSICAL SYMBOL SIXTY-FOURTH NOTE
+1D164 # MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE
+1D1BB # MUSICAL SYMBOL MINIMA
+1D1BC # MUSICAL SYMBOL MINIMA BLACK
+1D1BD # MUSICAL SYMBOL SEMIMINIMA WHITE
+1D1BE # MUSICAL SYMBOL SEMIMINIMA BLACK
+1D1BF # MUSICAL SYMBOL FUSA WHITE
+1D1C0 # MUSICAL SYMBOL FUSA BLACK
+
+# Total code points: 14
+
+# ================================================
+# (3) Singleton Decompositions
+# These characters can be derived from the UnicodeData file
+# by including all characters whose canonical decomposition
+# consists of a single character.
+# These characters are simply quoted here for reference.
+# ================================================
+
+# 0340..0341 [2] COMBINING GRAVE TONE MARK..COMBINING ACUTE TONE MARK
+# 0343 COMBINING GREEK KORONIS
+# 0374 GREEK NUMERAL SIGN
+# 037E GREEK QUESTION MARK
+# 0387 GREEK ANO TELEIA
+# 1F71 GREEK SMALL LETTER ALPHA WITH OXIA
+# 1F73 GREEK SMALL LETTER EPSILON WITH OXIA
+# 1F75 GREEK SMALL LETTER ETA WITH OXIA
+# 1F77 GREEK SMALL LETTER IOTA WITH OXIA
+# 1F79 GREEK SMALL LETTER OMICRON WITH OXIA
+# 1F7B GREEK SMALL LETTER UPSILON WITH OXIA
+# 1F7D GREEK SMALL LETTER OMEGA WITH OXIA
+# 1FBB GREEK CAPITAL LETTER ALPHA WITH OXIA
+# 1FBE GREEK PROSGEGRAMMENI
+# 1FC9 GREEK CAPITAL LETTER EPSILON WITH OXIA
+# 1FCB GREEK CAPITAL LETTER ETA WITH OXIA
+# 1FD3 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA
+# 1FDB GREEK CAPITAL LETTER IOTA WITH OXIA
+# 1FE3 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA
+# 1FEB GREEK CAPITAL LETTER UPSILON WITH OXIA
+# 1FEE..1FEF [2] GREEK DIALYTIKA AND OXIA..GREEK VARIA
+# 1FF9 GREEK CAPITAL LETTER OMICRON WITH OXIA
+# 1FFB GREEK CAPITAL LETTER OMEGA WITH OXIA
+# 1FFD GREEK OXIA
+# 2000..2001 [2] EN QUAD..EM QUAD
+# 2126 OHM SIGN
+# 212A..212B [2] KELVIN SIGN..ANGSTROM SIGN
+# 2329 LEFT-POINTING ANGLE BRACKET
+# 232A RIGHT-POINTING ANGLE BRACKET
+# F900..FA0D [270] CJK COMPATIBILITY IDEOGRAPH-F900..CJK COMPATIBILITY IDEOGRAPH-FA0D
+# FA10 CJK COMPATIBILITY IDEOGRAPH-FA10
+# FA12 CJK COMPATIBILITY IDEOGRAPH-FA12
+# FA15..FA1E [10] CJK COMPATIBILITY IDEOGRAPH-FA15..CJK COMPATIBILITY IDEOGRAPH-FA1E
+# FA20 CJK COMPATIBILITY IDEOGRAPH-FA20
+# FA22 CJK COMPATIBILITY IDEOGRAPH-FA22
+# FA25..FA26 [2] CJK COMPATIBILITY IDEOGRAPH-FA25..CJK COMPATIBILITY IDEOGRAPH-FA26
+# FA2A..FA2D [4] CJK COMPATIBILITY IDEOGRAPH-FA2A..CJK COMPATIBILITY IDEOGRAPH-FA2D
+# FA30..FA6A [59] CJK COMPATIBILITY IDEOGRAPH-FA30..CJK COMPATIBILITY IDEOGRAPH-FA6A
+# 2F800..2FA1D [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D
+
+# Total code points: 924
+
+# ================================================
+# (4) Non-Starter Decompositions
+# These characters can be derived from the UnicodeData file
+# by including all characters whose canonical decomposition consists
+# of a sequence of characters, the first of which has a non-zero
+# combining class.
+# These characters are simply quoted here for reference.
+# ================================================
+
+# 0344 COMBINING GREEK DIALYTIKA TONOS
+# 0F73 TIBETAN VOWEL SIGN II
+# 0F75 TIBETAN VOWEL SIGN UU
+# 0F81 TIBETAN VOWEL SIGN REVERSED II
+
+# Total code points: 4
+
diff --git a/libraries/liblunicode/Makefile.in b/libraries/liblunicode/Makefile.in
new file mode 100644
index 0000000..1b81041
--- /dev/null
+++ b/libraries/liblunicode/Makefile.in
@@ -0,0 +1,54 @@
+# Makefile.in for LDAP -llunicode
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+LIBRARY = liblunicode.a
+
+XXDIR = $(srcdir)/ucdata/
+XXHEADERS = ucdata.h ure.h uctable.h
+
+XXSRCS = ucdata.c ucgendat.c ure.c urestubs.c
+SRCS = ucstr.c
+OBJS = ucdata.o ure.o urestubs.o ucstr.o
+
+XLIB = $(LIBRARY)
+XLIBS = $(LDAP_LIBLUTIL_A) $(LDAP_LIBLBER_LA)
+#PROGRAMS = ucgendat
+
+LDAP_INCDIR= ../../include
+LDAP_LIBDIR= ../../libraries
+
+uctable.h: $(XXDIR)/uctable.h
+
+$(XXDIR)/uctable.h: $(XXDIR)/ucgendat.c $(srcdir)/UnicodeData.txt $(srcdir)/CompositionExclusions.txt
+ $(MAKE) ucgendat
+ ./ucgendat $(srcdir)/UnicodeData.txt -x $(srcdir)/CompositionExclusions.txt
+
+ucgendat: $(XLIBS) ucgendat.o
+ $(LTLINK) -o $@ ucgendat.o $(LIBS)
+
+.links :
+ @for i in $(XXSRCS) $(XXHEADERS); do \
+ $(RM) $$i ; \
+ ii=`find $(srcdir) -name $$i` ; \
+ $(LN_S) $$ii . ; \
+ done
+ touch .links
+
+$(XXSRCS) $(XXHEADERS) : .links
+
+clean-local: FORCE
+ @$(RM) *.dat .links $(XXHEADERS) ucgendat
+
+depend-common: .links
diff --git a/libraries/liblunicode/UCD-Terms b/libraries/liblunicode/UCD-Terms
new file mode 100644
index 0000000..4ec4da2
--- /dev/null
+++ b/libraries/liblunicode/UCD-Terms
@@ -0,0 +1,29 @@
+UCD Terms of Use (http://www.unicode.org/Public/UNIDATA/UCD.html)
+
+Disclaimer
+
+The Unicode Character Database is provided as is by Unicode, Inc.
+No claims are made as to fitness for any particular purpose. No
+warranties of any kind are expressed or implied. The recipient
+agrees to determine applicability of information provided. If this
+file has been purchased on magnetic or optical media from Unicode,
+Inc., the sole remedy for any claim will be exchange of defective
+media within 90 days of receipt.
+
+This disclaimer is applicable for all other data files accompanying
+the Unicode Character Database, some of which have been compiled
+by the Unicode Consortium, and some of which have been supplied by
+other sources.
+
+Limitations on Rights to Redistribute This Data
+
+Recipient is granted the right to make copies in any form for
+internal distribution and to freely use the information supplied
+in the creation of products supporting the Unicode (TM) Standard.
+The files in the Unicode Character Database can be redistributed
+to third parties or other organizations (whether for profit or not)
+as long as this notice and the disclaimer notice are retained.
+Information can be extracted from these files and used in documentation
+or programs, as long as there is an accompanying notice indicating
+the source.
+
diff --git a/libraries/liblunicode/UnicodeData.txt b/libraries/liblunicode/UnicodeData.txt
new file mode 100644
index 0000000..125a692
--- /dev/null
+++ b/libraries/liblunicode/UnicodeData.txt
@@ -0,0 +1,13874 @@
+0000;<control>;Cc;0;BN;;;;;N;NULL;;;;
+0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;;
+0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;;
+0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;;
+0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;;
+0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;;
+0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;;
+0007;<control>;Cc;0;BN;;;;;N;BELL;;;;
+0008;<control>;Cc;0;BN;;;;;N;BACKSPACE;;;;
+0009;<control>;Cc;0;S;;;;;N;CHARACTER TABULATION;;;;
+000A;<control>;Cc;0;B;;;;;N;LINE FEED (LF);;;;
+000B;<control>;Cc;0;S;;;;;N;LINE TABULATION;;;;
+000C;<control>;Cc;0;WS;;;;;N;FORM FEED (FF);;;;
+000D;<control>;Cc;0;B;;;;;N;CARRIAGE RETURN (CR);;;;
+000E;<control>;Cc;0;BN;;;;;N;SHIFT OUT;;;;
+000F;<control>;Cc;0;BN;;;;;N;SHIFT IN;;;;
+0010;<control>;Cc;0;BN;;;;;N;DATA LINK ESCAPE;;;;
+0011;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL ONE;;;;
+0012;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL TWO;;;;
+0013;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL THREE;;;;
+0014;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL FOUR;;;;
+0015;<control>;Cc;0;BN;;;;;N;NEGATIVE ACKNOWLEDGE;;;;
+0016;<control>;Cc;0;BN;;;;;N;SYNCHRONOUS IDLE;;;;
+0017;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION BLOCK;;;;
+0018;<control>;Cc;0;BN;;;;;N;CANCEL;;;;
+0019;<control>;Cc;0;BN;;;;;N;END OF MEDIUM;;;;
+001A;<control>;Cc;0;BN;;;;;N;SUBSTITUTE;;;;
+001B;<control>;Cc;0;BN;;;;;N;ESCAPE;;;;
+001C;<control>;Cc;0;B;;;;;N;INFORMATION SEPARATOR FOUR;;;;
+001D;<control>;Cc;0;B;;;;;N;INFORMATION SEPARATOR THREE;;;;
+001E;<control>;Cc;0;B;;;;;N;INFORMATION SEPARATOR TWO;;;;
+001F;<control>;Cc;0;S;;;;;N;INFORMATION SEPARATOR ONE;;;;
+0020;SPACE;Zs;0;WS;;;;;N;;;;;
+0021;EXCLAMATION MARK;Po;0;ON;;;;;N;;;;;
+0022;QUOTATION MARK;Po;0;ON;;;;;N;;;;;
+0023;NUMBER SIGN;Po;0;ET;;;;;N;;;;;
+0024;DOLLAR SIGN;Sc;0;ET;;;;;N;;;;;
+0025;PERCENT SIGN;Po;0;ET;;;;;N;;;;;
+0026;AMPERSAND;Po;0;ON;;;;;N;;;;;
+0027;APOSTROPHE;Po;0;ON;;;;;N;APOSTROPHE-QUOTE;;;;
+0028;LEFT PARENTHESIS;Ps;0;ON;;;;;Y;OPENING PARENTHESIS;;;;
+0029;RIGHT PARENTHESIS;Pe;0;ON;;;;;Y;CLOSING PARENTHESIS;;;;
+002A;ASTERISK;Po;0;ON;;;;;N;;;;;
+002B;PLUS SIGN;Sm;0;ET;;;;;N;;;;;
+002C;COMMA;Po;0;CS;;;;;N;;;;;
+002D;HYPHEN-MINUS;Pd;0;ET;;;;;N;;;;;
+002E;FULL STOP;Po;0;CS;;;;;N;PERIOD;;;;
+002F;SOLIDUS;Po;0;ES;;;;;N;SLASH;;;;
+0030;DIGIT ZERO;Nd;0;EN;;0;0;0;N;;;;;
+0031;DIGIT ONE;Nd;0;EN;;1;1;1;N;;;;;
+0032;DIGIT TWO;Nd;0;EN;;2;2;2;N;;;;;
+0033;DIGIT THREE;Nd;0;EN;;3;3;3;N;;;;;
+0034;DIGIT FOUR;Nd;0;EN;;4;4;4;N;;;;;
+0035;DIGIT FIVE;Nd;0;EN;;5;5;5;N;;;;;
+0036;DIGIT SIX;Nd;0;EN;;6;6;6;N;;;;;
+0037;DIGIT SEVEN;Nd;0;EN;;7;7;7;N;;;;;
+0038;DIGIT EIGHT;Nd;0;EN;;8;8;8;N;;;;;
+0039;DIGIT NINE;Nd;0;EN;;9;9;9;N;;;;;
+003A;COLON;Po;0;CS;;;;;N;;;;;
+003B;SEMICOLON;Po;0;ON;;;;;N;;;;;
+003C;LESS-THAN SIGN;Sm;0;ON;;;;;Y;;;;;
+003D;EQUALS SIGN;Sm;0;ON;;;;;N;;;;;
+003E;GREATER-THAN SIGN;Sm;0;ON;;;;;Y;;;;;
+003F;QUESTION MARK;Po;0;ON;;;;;N;;;;;
+0040;COMMERCIAL AT;Po;0;ON;;;;;N;;;;;
+0041;LATIN CAPITAL LETTER A;Lu;0;L;;;;;N;;;;0061;
+0042;LATIN CAPITAL LETTER B;Lu;0;L;;;;;N;;;;0062;
+0043;LATIN CAPITAL LETTER C;Lu;0;L;;;;;N;;;;0063;
+0044;LATIN CAPITAL LETTER D;Lu;0;L;;;;;N;;;;0064;
+0045;LATIN CAPITAL LETTER E;Lu;0;L;;;;;N;;;;0065;
+0046;LATIN CAPITAL LETTER F;Lu;0;L;;;;;N;;;;0066;
+0047;LATIN CAPITAL LETTER G;Lu;0;L;;;;;N;;;;0067;
+0048;LATIN CAPITAL LETTER H;Lu;0;L;;;;;N;;;;0068;
+0049;LATIN CAPITAL LETTER I;Lu;0;L;;;;;N;;;;0069;
+004A;LATIN CAPITAL LETTER J;Lu;0;L;;;;;N;;;;006A;
+004B;LATIN CAPITAL LETTER K;Lu;0;L;;;;;N;;;;006B;
+004C;LATIN CAPITAL LETTER L;Lu;0;L;;;;;N;;;;006C;
+004D;LATIN CAPITAL LETTER M;Lu;0;L;;;;;N;;;;006D;
+004E;LATIN CAPITAL LETTER N;Lu;0;L;;;;;N;;;;006E;
+004F;LATIN CAPITAL LETTER O;Lu;0;L;;;;;N;;;;006F;
+0050;LATIN CAPITAL LETTER P;Lu;0;L;;;;;N;;;;0070;
+0051;LATIN CAPITAL LETTER Q;Lu;0;L;;;;;N;;;;0071;
+0052;LATIN CAPITAL LETTER R;Lu;0;L;;;;;N;;;;0072;
+0053;LATIN CAPITAL LETTER S;Lu;0;L;;;;;N;;;;0073;
+0054;LATIN CAPITAL LETTER T;Lu;0;L;;;;;N;;;;0074;
+0055;LATIN CAPITAL LETTER U;Lu;0;L;;;;;N;;;;0075;
+0056;LATIN CAPITAL LETTER V;Lu;0;L;;;;;N;;;;0076;
+0057;LATIN CAPITAL LETTER W;Lu;0;L;;;;;N;;;;0077;
+0058;LATIN CAPITAL LETTER X;Lu;0;L;;;;;N;;;;0078;
+0059;LATIN CAPITAL LETTER Y;Lu;0;L;;;;;N;;;;0079;
+005A;LATIN CAPITAL LETTER Z;Lu;0;L;;;;;N;;;;007A;
+005B;LEFT SQUARE BRACKET;Ps;0;ON;;;;;Y;OPENING SQUARE BRACKET;;;;
+005C;REVERSE SOLIDUS;Po;0;ON;;;;;N;BACKSLASH;;;;
+005D;RIGHT SQUARE BRACKET;Pe;0;ON;;;;;Y;CLOSING SQUARE BRACKET;;;;
+005E;CIRCUMFLEX ACCENT;Sk;0;ON;;;;;N;SPACING CIRCUMFLEX;;;;
+005F;LOW LINE;Pc;0;ON;;;;;N;SPACING UNDERSCORE;;;;
+0060;GRAVE ACCENT;Sk;0;ON;;;;;N;SPACING GRAVE;;;;
+0061;LATIN SMALL LETTER A;Ll;0;L;;;;;N;;;0041;;0041
+0062;LATIN SMALL LETTER B;Ll;0;L;;;;;N;;;0042;;0042
+0063;LATIN SMALL LETTER C;Ll;0;L;;;;;N;;;0043;;0043
+0064;LATIN SMALL LETTER D;Ll;0;L;;;;;N;;;0044;;0044
+0065;LATIN SMALL LETTER E;Ll;0;L;;;;;N;;;0045;;0045
+0066;LATIN SMALL LETTER F;Ll;0;L;;;;;N;;;0046;;0046
+0067;LATIN SMALL LETTER G;Ll;0;L;;;;;N;;;0047;;0047
+0068;LATIN SMALL LETTER H;Ll;0;L;;;;;N;;;0048;;0048
+0069;LATIN SMALL LETTER I;Ll;0;L;;;;;N;;;0049;;0049
+006A;LATIN SMALL LETTER J;Ll;0;L;;;;;N;;;004A;;004A
+006B;LATIN SMALL LETTER K;Ll;0;L;;;;;N;;;004B;;004B
+006C;LATIN SMALL LETTER L;Ll;0;L;;;;;N;;;004C;;004C
+006D;LATIN SMALL LETTER M;Ll;0;L;;;;;N;;;004D;;004D
+006E;LATIN SMALL LETTER N;Ll;0;L;;;;;N;;;004E;;004E
+006F;LATIN SMALL LETTER O;Ll;0;L;;;;;N;;;004F;;004F
+0070;LATIN SMALL LETTER P;Ll;0;L;;;;;N;;;0050;;0050
+0071;LATIN SMALL LETTER Q;Ll;0;L;;;;;N;;;0051;;0051
+0072;LATIN SMALL LETTER R;Ll;0;L;;;;;N;;;0052;;0052
+0073;LATIN SMALL LETTER S;Ll;0;L;;;;;N;;;0053;;0053
+0074;LATIN SMALL LETTER T;Ll;0;L;;;;;N;;;0054;;0054
+0075;LATIN SMALL LETTER U;Ll;0;L;;;;;N;;;0055;;0055
+0076;LATIN SMALL LETTER V;Ll;0;L;;;;;N;;;0056;;0056
+0077;LATIN SMALL LETTER W;Ll;0;L;;;;;N;;;0057;;0057
+0078;LATIN SMALL LETTER X;Ll;0;L;;;;;N;;;0058;;0058
+0079;LATIN SMALL LETTER Y;Ll;0;L;;;;;N;;;0059;;0059
+007A;LATIN SMALL LETTER Z;Ll;0;L;;;;;N;;;005A;;005A
+007B;LEFT CURLY BRACKET;Ps;0;ON;;;;;Y;OPENING CURLY BRACKET;;;;
+007C;VERTICAL LINE;Sm;0;ON;;;;;N;VERTICAL BAR;;;;
+007D;RIGHT CURLY BRACKET;Pe;0;ON;;;;;Y;CLOSING CURLY BRACKET;;;;
+007E;TILDE;Sm;0;ON;;;;;N;;;;;
+007F;<control>;Cc;0;BN;;;;;N;DELETE;;;;
+0080;<control>;Cc;0;BN;;;;;N;;;;;
+0081;<control>;Cc;0;BN;;;;;N;;;;;
+0082;<control>;Cc;0;BN;;;;;N;BREAK PERMITTED HERE;;;;
+0083;<control>;Cc;0;BN;;;;;N;NO BREAK HERE;;;;
+0084;<control>;Cc;0;BN;;;;;N;;;;;
+0085;<control>;Cc;0;B;;;;;N;NEXT LINE (NEL);;;;
+0086;<control>;Cc;0;BN;;;;;N;START OF SELECTED AREA;;;;
+0087;<control>;Cc;0;BN;;;;;N;END OF SELECTED AREA;;;;
+0088;<control>;Cc;0;BN;;;;;N;CHARACTER TABULATION SET;;;;
+0089;<control>;Cc;0;BN;;;;;N;CHARACTER TABULATION WITH JUSTIFICATION;;;;
+008A;<control>;Cc;0;BN;;;;;N;LINE TABULATION SET;;;;
+008B;<control>;Cc;0;BN;;;;;N;PARTIAL LINE FORWARD;;;;
+008C;<control>;Cc;0;BN;;;;;N;PARTIAL LINE BACKWARD;;;;
+008D;<control>;Cc;0;BN;;;;;N;REVERSE LINE FEED;;;;
+008E;<control>;Cc;0;BN;;;;;N;SINGLE SHIFT TWO;;;;
+008F;<control>;Cc;0;BN;;;;;N;SINGLE SHIFT THREE;;;;
+0090;<control>;Cc;0;BN;;;;;N;DEVICE CONTROL STRING;;;;
+0091;<control>;Cc;0;BN;;;;;N;PRIVATE USE ONE;;;;
+0092;<control>;Cc;0;BN;;;;;N;PRIVATE USE TWO;;;;
+0093;<control>;Cc;0;BN;;;;;N;SET TRANSMIT STATE;;;;
+0094;<control>;Cc;0;BN;;;;;N;CANCEL CHARACTER;;;;
+0095;<control>;Cc;0;BN;;;;;N;MESSAGE WAITING;;;;
+0096;<control>;Cc;0;BN;;;;;N;START OF GUARDED AREA;;;;
+0097;<control>;Cc;0;BN;;;;;N;END OF GUARDED AREA;;;;
+0098;<control>;Cc;0;BN;;;;;N;START OF STRING;;;;
+0099;<control>;Cc;0;BN;;;;;N;;;;;
+009A;<control>;Cc;0;BN;;;;;N;SINGLE CHARACTER INTRODUCER;;;;
+009B;<control>;Cc;0;BN;;;;;N;CONTROL SEQUENCE INTRODUCER;;;;
+009C;<control>;Cc;0;BN;;;;;N;STRING TERMINATOR;;;;
+009D;<control>;Cc;0;BN;;;;;N;OPERATING SYSTEM COMMAND;;;;
+009E;<control>;Cc;0;BN;;;;;N;PRIVACY MESSAGE;;;;
+009F;<control>;Cc;0;BN;;;;;N;APPLICATION PROGRAM COMMAND;;;;
+00A0;NO-BREAK SPACE;Zs;0;CS;<noBreak> 0020;;;;N;NON-BREAKING SPACE;;;;
+00A1;INVERTED EXCLAMATION MARK;Po;0;ON;;;;;N;;;;;
+00A2;CENT SIGN;Sc;0;ET;;;;;N;;;;;
+00A3;POUND SIGN;Sc;0;ET;;;;;N;;;;;
+00A4;CURRENCY SIGN;Sc;0;ET;;;;;N;;;;;
+00A5;YEN SIGN;Sc;0;ET;;;;;N;;;;;
+00A6;BROKEN BAR;So;0;ON;;;;;N;BROKEN VERTICAL BAR;;;;
+00A7;SECTION SIGN;So;0;ON;;;;;N;;;;;
+00A8;DIAERESIS;Sk;0;ON;<compat> 0020 0308;;;;N;SPACING DIAERESIS;;;;
+00A9;COPYRIGHT SIGN;So;0;ON;;;;;N;;;;;
+00AA;FEMININE ORDINAL INDICATOR;Ll;0;L;<super> 0061;;;;N;;;;;
+00AB;LEFT-POINTING DOUBLE ANGLE QUOTATION MARK;Pi;0;ON;;;;;Y;LEFT POINTING GUILLEMET;*;;;
+00AC;NOT SIGN;Sm;0;ON;;;;;N;;;;;
+00AD;SOFT HYPHEN;Pd;0;ON;;;;;N;;;;;
+00AE;REGISTERED SIGN;So;0;ON;;;;;N;REGISTERED TRADE MARK SIGN;;;;
+00AF;MACRON;Sk;0;ON;<compat> 0020 0304;;;;N;SPACING MACRON;;;;
+00B0;DEGREE SIGN;So;0;ET;;;;;N;;;;;
+00B1;PLUS-MINUS SIGN;Sm;0;ET;;;;;N;PLUS-OR-MINUS SIGN;;;;
+00B2;SUPERSCRIPT TWO;No;0;EN;<super> 0032;2;2;2;N;SUPERSCRIPT DIGIT TWO;;;;
+00B3;SUPERSCRIPT THREE;No;0;EN;<super> 0033;3;3;3;N;SUPERSCRIPT DIGIT THREE;;;;
+00B4;ACUTE ACCENT;Sk;0;ON;<compat> 0020 0301;;;;N;SPACING ACUTE;;;;
+00B5;MICRO SIGN;Ll;0;L;<compat> 03BC;;;;N;;;039C;;039C
+00B6;PILCROW SIGN;So;0;ON;;;;;N;PARAGRAPH SIGN;;;;
+00B7;MIDDLE DOT;Po;0;ON;;;;;N;;;;;
+00B8;CEDILLA;Sk;0;ON;<compat> 0020 0327;;;;N;SPACING CEDILLA;;;;
+00B9;SUPERSCRIPT ONE;No;0;EN;<super> 0031;1;1;1;N;SUPERSCRIPT DIGIT ONE;;;;
+00BA;MASCULINE ORDINAL INDICATOR;Ll;0;L;<super> 006F;;;;N;;;;;
+00BB;RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK;Pf;0;ON;;;;;Y;RIGHT POINTING GUILLEMET;*;;;
+00BC;VULGAR FRACTION ONE QUARTER;No;0;ON;<fraction> 0031 2044 0034;;;1/4;N;FRACTION ONE QUARTER;;;;
+00BD;VULGAR FRACTION ONE HALF;No;0;ON;<fraction> 0031 2044 0032;;;1/2;N;FRACTION ONE HALF;;;;
+00BE;VULGAR FRACTION THREE QUARTERS;No;0;ON;<fraction> 0033 2044 0034;;;3/4;N;FRACTION THREE QUARTERS;;;;
+00BF;INVERTED QUESTION MARK;Po;0;ON;;;;;N;;;;;
+00C0;LATIN CAPITAL LETTER A WITH GRAVE;Lu;0;L;0041 0300;;;;N;LATIN CAPITAL LETTER A GRAVE;;;00E0;
+00C1;LATIN CAPITAL LETTER A WITH ACUTE;Lu;0;L;0041 0301;;;;N;LATIN CAPITAL LETTER A ACUTE;;;00E1;
+00C2;LATIN CAPITAL LETTER A WITH CIRCUMFLEX;Lu;0;L;0041 0302;;;;N;LATIN CAPITAL LETTER A CIRCUMFLEX;;;00E2;
+00C3;LATIN CAPITAL LETTER A WITH TILDE;Lu;0;L;0041 0303;;;;N;LATIN CAPITAL LETTER A TILDE;;;00E3;
+00C4;LATIN CAPITAL LETTER A WITH DIAERESIS;Lu;0;L;0041 0308;;;;N;LATIN CAPITAL LETTER A DIAERESIS;;;00E4;
+00C5;LATIN CAPITAL LETTER A WITH RING ABOVE;Lu;0;L;0041 030A;;;;N;LATIN CAPITAL LETTER A RING;;;00E5;
+00C6;LATIN CAPITAL LETTER AE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER A E;ash *;;00E6;
+00C7;LATIN CAPITAL LETTER C WITH CEDILLA;Lu;0;L;0043 0327;;;;N;LATIN CAPITAL LETTER C CEDILLA;;;00E7;
+00C8;LATIN CAPITAL LETTER E WITH GRAVE;Lu;0;L;0045 0300;;;;N;LATIN CAPITAL LETTER E GRAVE;;;00E8;
+00C9;LATIN CAPITAL LETTER E WITH ACUTE;Lu;0;L;0045 0301;;;;N;LATIN CAPITAL LETTER E ACUTE;;;00E9;
+00CA;LATIN CAPITAL LETTER E WITH CIRCUMFLEX;Lu;0;L;0045 0302;;;;N;LATIN CAPITAL LETTER E CIRCUMFLEX;;;00EA;
+00CB;LATIN CAPITAL LETTER E WITH DIAERESIS;Lu;0;L;0045 0308;;;;N;LATIN CAPITAL LETTER E DIAERESIS;;;00EB;
+00CC;LATIN CAPITAL LETTER I WITH GRAVE;Lu;0;L;0049 0300;;;;N;LATIN CAPITAL LETTER I GRAVE;;;00EC;
+00CD;LATIN CAPITAL LETTER I WITH ACUTE;Lu;0;L;0049 0301;;;;N;LATIN CAPITAL LETTER I ACUTE;;;00ED;
+00CE;LATIN CAPITAL LETTER I WITH CIRCUMFLEX;Lu;0;L;0049 0302;;;;N;LATIN CAPITAL LETTER I CIRCUMFLEX;;;00EE;
+00CF;LATIN CAPITAL LETTER I WITH DIAERESIS;Lu;0;L;0049 0308;;;;N;LATIN CAPITAL LETTER I DIAERESIS;;;00EF;
+00D0;LATIN CAPITAL LETTER ETH;Lu;0;L;;;;;N;;Icelandic;;00F0;
+00D1;LATIN CAPITAL LETTER N WITH TILDE;Lu;0;L;004E 0303;;;;N;LATIN CAPITAL LETTER N TILDE;;;00F1;
+00D2;LATIN CAPITAL LETTER O WITH GRAVE;Lu;0;L;004F 0300;;;;N;LATIN CAPITAL LETTER O GRAVE;;;00F2;
+00D3;LATIN CAPITAL LETTER O WITH ACUTE;Lu;0;L;004F 0301;;;;N;LATIN CAPITAL LETTER O ACUTE;;;00F3;
+00D4;LATIN CAPITAL LETTER O WITH CIRCUMFLEX;Lu;0;L;004F 0302;;;;N;LATIN CAPITAL LETTER O CIRCUMFLEX;;;00F4;
+00D5;LATIN CAPITAL LETTER O WITH TILDE;Lu;0;L;004F 0303;;;;N;LATIN CAPITAL LETTER O TILDE;;;00F5;
+00D6;LATIN CAPITAL LETTER O WITH DIAERESIS;Lu;0;L;004F 0308;;;;N;LATIN CAPITAL LETTER O DIAERESIS;;;00F6;
+00D7;MULTIPLICATION SIGN;Sm;0;ON;;;;;N;;;;;
+00D8;LATIN CAPITAL LETTER O WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O SLASH;;;00F8;
+00D9;LATIN CAPITAL LETTER U WITH GRAVE;Lu;0;L;0055 0300;;;;N;LATIN CAPITAL LETTER U GRAVE;;;00F9;
+00DA;LATIN CAPITAL LETTER U WITH ACUTE;Lu;0;L;0055 0301;;;;N;LATIN CAPITAL LETTER U ACUTE;;;00FA;
+00DB;LATIN CAPITAL LETTER U WITH CIRCUMFLEX;Lu;0;L;0055 0302;;;;N;LATIN CAPITAL LETTER U CIRCUMFLEX;;;00FB;
+00DC;LATIN CAPITAL LETTER U WITH DIAERESIS;Lu;0;L;0055 0308;;;;N;LATIN CAPITAL LETTER U DIAERESIS;;;00FC;
+00DD;LATIN CAPITAL LETTER Y WITH ACUTE;Lu;0;L;0059 0301;;;;N;LATIN CAPITAL LETTER Y ACUTE;;;00FD;
+00DE;LATIN CAPITAL LETTER THORN;Lu;0;L;;;;;N;;Icelandic;;00FE;
+00DF;LATIN SMALL LETTER SHARP S;Ll;0;L;;;;;N;;German;;;
+00E0;LATIN SMALL LETTER A WITH GRAVE;Ll;0;L;0061 0300;;;;N;LATIN SMALL LETTER A GRAVE;;00C0;;00C0
+00E1;LATIN SMALL LETTER A WITH ACUTE;Ll;0;L;0061 0301;;;;N;LATIN SMALL LETTER A ACUTE;;00C1;;00C1
+00E2;LATIN SMALL LETTER A WITH CIRCUMFLEX;Ll;0;L;0061 0302;;;;N;LATIN SMALL LETTER A CIRCUMFLEX;;00C2;;00C2
+00E3;LATIN SMALL LETTER A WITH TILDE;Ll;0;L;0061 0303;;;;N;LATIN SMALL LETTER A TILDE;;00C3;;00C3
+00E4;LATIN SMALL LETTER A WITH DIAERESIS;Ll;0;L;0061 0308;;;;N;LATIN SMALL LETTER A DIAERESIS;;00C4;;00C4
+00E5;LATIN SMALL LETTER A WITH RING ABOVE;Ll;0;L;0061 030A;;;;N;LATIN SMALL LETTER A RING;;00C5;;00C5
+00E6;LATIN SMALL LETTER AE;Ll;0;L;;;;;N;LATIN SMALL LETTER A E;ash *;00C6;;00C6
+00E7;LATIN SMALL LETTER C WITH CEDILLA;Ll;0;L;0063 0327;;;;N;LATIN SMALL LETTER C CEDILLA;;00C7;;00C7
+00E8;LATIN SMALL LETTER E WITH GRAVE;Ll;0;L;0065 0300;;;;N;LATIN SMALL LETTER E GRAVE;;00C8;;00C8
+00E9;LATIN SMALL LETTER E WITH ACUTE;Ll;0;L;0065 0301;;;;N;LATIN SMALL LETTER E ACUTE;;00C9;;00C9
+00EA;LATIN SMALL LETTER E WITH CIRCUMFLEX;Ll;0;L;0065 0302;;;;N;LATIN SMALL LETTER E CIRCUMFLEX;;00CA;;00CA
+00EB;LATIN SMALL LETTER E WITH DIAERESIS;Ll;0;L;0065 0308;;;;N;LATIN SMALL LETTER E DIAERESIS;;00CB;;00CB
+00EC;LATIN SMALL LETTER I WITH GRAVE;Ll;0;L;0069 0300;;;;N;LATIN SMALL LETTER I GRAVE;;00CC;;00CC
+00ED;LATIN SMALL LETTER I WITH ACUTE;Ll;0;L;0069 0301;;;;N;LATIN SMALL LETTER I ACUTE;;00CD;;00CD
+00EE;LATIN SMALL LETTER I WITH CIRCUMFLEX;Ll;0;L;0069 0302;;;;N;LATIN SMALL LETTER I CIRCUMFLEX;;00CE;;00CE
+00EF;LATIN SMALL LETTER I WITH DIAERESIS;Ll;0;L;0069 0308;;;;N;LATIN SMALL LETTER I DIAERESIS;;00CF;;00CF
+00F0;LATIN SMALL LETTER ETH;Ll;0;L;;;;;N;;Icelandic;00D0;;00D0
+00F1;LATIN SMALL LETTER N WITH TILDE;Ll;0;L;006E 0303;;;;N;LATIN SMALL LETTER N TILDE;;00D1;;00D1
+00F2;LATIN SMALL LETTER O WITH GRAVE;Ll;0;L;006F 0300;;;;N;LATIN SMALL LETTER O GRAVE;;00D2;;00D2
+00F3;LATIN SMALL LETTER O WITH ACUTE;Ll;0;L;006F 0301;;;;N;LATIN SMALL LETTER O ACUTE;;00D3;;00D3
+00F4;LATIN SMALL LETTER O WITH CIRCUMFLEX;Ll;0;L;006F 0302;;;;N;LATIN SMALL LETTER O CIRCUMFLEX;;00D4;;00D4
+00F5;LATIN SMALL LETTER O WITH TILDE;Ll;0;L;006F 0303;;;;N;LATIN SMALL LETTER O TILDE;;00D5;;00D5
+00F6;LATIN SMALL LETTER O WITH DIAERESIS;Ll;0;L;006F 0308;;;;N;LATIN SMALL LETTER O DIAERESIS;;00D6;;00D6
+00F7;DIVISION SIGN;Sm;0;ON;;;;;N;;;;;
+00F8;LATIN SMALL LETTER O WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER O SLASH;;00D8;;00D8
+00F9;LATIN SMALL LETTER U WITH GRAVE;Ll;0;L;0075 0300;;;;N;LATIN SMALL LETTER U GRAVE;;00D9;;00D9
+00FA;LATIN SMALL LETTER U WITH ACUTE;Ll;0;L;0075 0301;;;;N;LATIN SMALL LETTER U ACUTE;;00DA;;00DA
+00FB;LATIN SMALL LETTER U WITH CIRCUMFLEX;Ll;0;L;0075 0302;;;;N;LATIN SMALL LETTER U CIRCUMFLEX;;00DB;;00DB
+00FC;LATIN SMALL LETTER U WITH DIAERESIS;Ll;0;L;0075 0308;;;;N;LATIN SMALL LETTER U DIAERESIS;;00DC;;00DC
+00FD;LATIN SMALL LETTER Y WITH ACUTE;Ll;0;L;0079 0301;;;;N;LATIN SMALL LETTER Y ACUTE;;00DD;;00DD
+00FE;LATIN SMALL LETTER THORN;Ll;0;L;;;;;N;;Icelandic;00DE;;00DE
+00FF;LATIN SMALL LETTER Y WITH DIAERESIS;Ll;0;L;0079 0308;;;;N;LATIN SMALL LETTER Y DIAERESIS;;0178;;0178
+0100;LATIN CAPITAL LETTER A WITH MACRON;Lu;0;L;0041 0304;;;;N;LATIN CAPITAL LETTER A MACRON;;;0101;
+0101;LATIN SMALL LETTER A WITH MACRON;Ll;0;L;0061 0304;;;;N;LATIN SMALL LETTER A MACRON;;0100;;0100
+0102;LATIN CAPITAL LETTER A WITH BREVE;Lu;0;L;0041 0306;;;;N;LATIN CAPITAL LETTER A BREVE;;;0103;
+0103;LATIN SMALL LETTER A WITH BREVE;Ll;0;L;0061 0306;;;;N;LATIN SMALL LETTER A BREVE;;0102;;0102
+0104;LATIN CAPITAL LETTER A WITH OGONEK;Lu;0;L;0041 0328;;;;N;LATIN CAPITAL LETTER A OGONEK;;;0105;
+0105;LATIN SMALL LETTER A WITH OGONEK;Ll;0;L;0061 0328;;;;N;LATIN SMALL LETTER A OGONEK;;0104;;0104
+0106;LATIN CAPITAL LETTER C WITH ACUTE;Lu;0;L;0043 0301;;;;N;LATIN CAPITAL LETTER C ACUTE;;;0107;
+0107;LATIN SMALL LETTER C WITH ACUTE;Ll;0;L;0063 0301;;;;N;LATIN SMALL LETTER C ACUTE;;0106;;0106
+0108;LATIN CAPITAL LETTER C WITH CIRCUMFLEX;Lu;0;L;0043 0302;;;;N;LATIN CAPITAL LETTER C CIRCUMFLEX;;;0109;
+0109;LATIN SMALL LETTER C WITH CIRCUMFLEX;Ll;0;L;0063 0302;;;;N;LATIN SMALL LETTER C CIRCUMFLEX;;0108;;0108
+010A;LATIN CAPITAL LETTER C WITH DOT ABOVE;Lu;0;L;0043 0307;;;;N;LATIN CAPITAL LETTER C DOT;;;010B;
+010B;LATIN SMALL LETTER C WITH DOT ABOVE;Ll;0;L;0063 0307;;;;N;LATIN SMALL LETTER C DOT;;010A;;010A
+010C;LATIN CAPITAL LETTER C WITH CARON;Lu;0;L;0043 030C;;;;N;LATIN CAPITAL LETTER C HACEK;;;010D;
+010D;LATIN SMALL LETTER C WITH CARON;Ll;0;L;0063 030C;;;;N;LATIN SMALL LETTER C HACEK;;010C;;010C
+010E;LATIN CAPITAL LETTER D WITH CARON;Lu;0;L;0044 030C;;;;N;LATIN CAPITAL LETTER D HACEK;;;010F;
+010F;LATIN SMALL LETTER D WITH CARON;Ll;0;L;0064 030C;;;;N;LATIN SMALL LETTER D HACEK;;010E;;010E
+0110;LATIN CAPITAL LETTER D WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D BAR;;;0111;
+0111;LATIN SMALL LETTER D WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER D BAR;;0110;;0110
+0112;LATIN CAPITAL LETTER E WITH MACRON;Lu;0;L;0045 0304;;;;N;LATIN CAPITAL LETTER E MACRON;;;0113;
+0113;LATIN SMALL LETTER E WITH MACRON;Ll;0;L;0065 0304;;;;N;LATIN SMALL LETTER E MACRON;;0112;;0112
+0114;LATIN CAPITAL LETTER E WITH BREVE;Lu;0;L;0045 0306;;;;N;LATIN CAPITAL LETTER E BREVE;;;0115;
+0115;LATIN SMALL LETTER E WITH BREVE;Ll;0;L;0065 0306;;;;N;LATIN SMALL LETTER E BREVE;;0114;;0114
+0116;LATIN CAPITAL LETTER E WITH DOT ABOVE;Lu;0;L;0045 0307;;;;N;LATIN CAPITAL LETTER E DOT;;;0117;
+0117;LATIN SMALL LETTER E WITH DOT ABOVE;Ll;0;L;0065 0307;;;;N;LATIN SMALL LETTER E DOT;;0116;;0116
+0118;LATIN CAPITAL LETTER E WITH OGONEK;Lu;0;L;0045 0328;;;;N;LATIN CAPITAL LETTER E OGONEK;;;0119;
+0119;LATIN SMALL LETTER E WITH OGONEK;Ll;0;L;0065 0328;;;;N;LATIN SMALL LETTER E OGONEK;;0118;;0118
+011A;LATIN CAPITAL LETTER E WITH CARON;Lu;0;L;0045 030C;;;;N;LATIN CAPITAL LETTER E HACEK;;;011B;
+011B;LATIN SMALL LETTER E WITH CARON;Ll;0;L;0065 030C;;;;N;LATIN SMALL LETTER E HACEK;;011A;;011A
+011C;LATIN CAPITAL LETTER G WITH CIRCUMFLEX;Lu;0;L;0047 0302;;;;N;LATIN CAPITAL LETTER G CIRCUMFLEX;;;011D;
+011D;LATIN SMALL LETTER G WITH CIRCUMFLEX;Ll;0;L;0067 0302;;;;N;LATIN SMALL LETTER G CIRCUMFLEX;;011C;;011C
+011E;LATIN CAPITAL LETTER G WITH BREVE;Lu;0;L;0047 0306;;;;N;LATIN CAPITAL LETTER G BREVE;;;011F;
+011F;LATIN SMALL LETTER G WITH BREVE;Ll;0;L;0067 0306;;;;N;LATIN SMALL LETTER G BREVE;;011E;;011E
+0120;LATIN CAPITAL LETTER G WITH DOT ABOVE;Lu;0;L;0047 0307;;;;N;LATIN CAPITAL LETTER G DOT;;;0121;
+0121;LATIN SMALL LETTER G WITH DOT ABOVE;Ll;0;L;0067 0307;;;;N;LATIN SMALL LETTER G DOT;;0120;;0120
+0122;LATIN CAPITAL LETTER G WITH CEDILLA;Lu;0;L;0047 0327;;;;N;LATIN CAPITAL LETTER G CEDILLA;;;0123;
+0123;LATIN SMALL LETTER G WITH CEDILLA;Ll;0;L;0067 0327;;;;N;LATIN SMALL LETTER G CEDILLA;;0122;;0122
+0124;LATIN CAPITAL LETTER H WITH CIRCUMFLEX;Lu;0;L;0048 0302;;;;N;LATIN CAPITAL LETTER H CIRCUMFLEX;;;0125;
+0125;LATIN SMALL LETTER H WITH CIRCUMFLEX;Ll;0;L;0068 0302;;;;N;LATIN SMALL LETTER H CIRCUMFLEX;;0124;;0124
+0126;LATIN CAPITAL LETTER H WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER H BAR;;;0127;
+0127;LATIN SMALL LETTER H WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER H BAR;;0126;;0126
+0128;LATIN CAPITAL LETTER I WITH TILDE;Lu;0;L;0049 0303;;;;N;LATIN CAPITAL LETTER I TILDE;;;0129;
+0129;LATIN SMALL LETTER I WITH TILDE;Ll;0;L;0069 0303;;;;N;LATIN SMALL LETTER I TILDE;;0128;;0128
+012A;LATIN CAPITAL LETTER I WITH MACRON;Lu;0;L;0049 0304;;;;N;LATIN CAPITAL LETTER I MACRON;;;012B;
+012B;LATIN SMALL LETTER I WITH MACRON;Ll;0;L;0069 0304;;;;N;LATIN SMALL LETTER I MACRON;;012A;;012A
+012C;LATIN CAPITAL LETTER I WITH BREVE;Lu;0;L;0049 0306;;;;N;LATIN CAPITAL LETTER I BREVE;;;012D;
+012D;LATIN SMALL LETTER I WITH BREVE;Ll;0;L;0069 0306;;;;N;LATIN SMALL LETTER I BREVE;;012C;;012C
+012E;LATIN CAPITAL LETTER I WITH OGONEK;Lu;0;L;0049 0328;;;;N;LATIN CAPITAL LETTER I OGONEK;;;012F;
+012F;LATIN SMALL LETTER I WITH OGONEK;Ll;0;L;0069 0328;;;;N;LATIN SMALL LETTER I OGONEK;;012E;;012E
+0130;LATIN CAPITAL LETTER I WITH DOT ABOVE;Lu;0;L;0049 0307;;;;N;LATIN CAPITAL LETTER I DOT;;;0069;
+0131;LATIN SMALL LETTER DOTLESS I;Ll;0;L;;;;;N;;;0049;;0049
+0132;LATIN CAPITAL LIGATURE IJ;Lu;0;L;<compat> 0049 004A;;;;N;LATIN CAPITAL LETTER I J;;;0133;
+0133;LATIN SMALL LIGATURE IJ;Ll;0;L;<compat> 0069 006A;;;;N;LATIN SMALL LETTER I J;;0132;;0132
+0134;LATIN CAPITAL LETTER J WITH CIRCUMFLEX;Lu;0;L;004A 0302;;;;N;LATIN CAPITAL LETTER J CIRCUMFLEX;;;0135;
+0135;LATIN SMALL LETTER J WITH CIRCUMFLEX;Ll;0;L;006A 0302;;;;N;LATIN SMALL LETTER J CIRCUMFLEX;;0134;;0134
+0136;LATIN CAPITAL LETTER K WITH CEDILLA;Lu;0;L;004B 0327;;;;N;LATIN CAPITAL LETTER K CEDILLA;;;0137;
+0137;LATIN SMALL LETTER K WITH CEDILLA;Ll;0;L;006B 0327;;;;N;LATIN SMALL LETTER K CEDILLA;;0136;;0136
+0138;LATIN SMALL LETTER KRA;Ll;0;L;;;;;N;;Greenlandic;;;
+0139;LATIN CAPITAL LETTER L WITH ACUTE;Lu;0;L;004C 0301;;;;N;LATIN CAPITAL LETTER L ACUTE;;;013A;
+013A;LATIN SMALL LETTER L WITH ACUTE;Ll;0;L;006C 0301;;;;N;LATIN SMALL LETTER L ACUTE;;0139;;0139
+013B;LATIN CAPITAL LETTER L WITH CEDILLA;Lu;0;L;004C 0327;;;;N;LATIN CAPITAL LETTER L CEDILLA;;;013C;
+013C;LATIN SMALL LETTER L WITH CEDILLA;Ll;0;L;006C 0327;;;;N;LATIN SMALL LETTER L CEDILLA;;013B;;013B
+013D;LATIN CAPITAL LETTER L WITH CARON;Lu;0;L;004C 030C;;;;N;LATIN CAPITAL LETTER L HACEK;;;013E;
+013E;LATIN SMALL LETTER L WITH CARON;Ll;0;L;006C 030C;;;;N;LATIN SMALL LETTER L HACEK;;013D;;013D
+013F;LATIN CAPITAL LETTER L WITH MIDDLE DOT;Lu;0;L;<compat> 004C 00B7;;;;N;;;;0140;
+0140;LATIN SMALL LETTER L WITH MIDDLE DOT;Ll;0;L;<compat> 006C 00B7;;;;N;;;013F;;013F
+0141;LATIN CAPITAL LETTER L WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER L SLASH;;;0142;
+0142;LATIN SMALL LETTER L WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER L SLASH;;0141;;0141
+0143;LATIN CAPITAL LETTER N WITH ACUTE;Lu;0;L;004E 0301;;;;N;LATIN CAPITAL LETTER N ACUTE;;;0144;
+0144;LATIN SMALL LETTER N WITH ACUTE;Ll;0;L;006E 0301;;;;N;LATIN SMALL LETTER N ACUTE;;0143;;0143
+0145;LATIN CAPITAL LETTER N WITH CEDILLA;Lu;0;L;004E 0327;;;;N;LATIN CAPITAL LETTER N CEDILLA;;;0146;
+0146;LATIN SMALL LETTER N WITH CEDILLA;Ll;0;L;006E 0327;;;;N;LATIN SMALL LETTER N CEDILLA;;0145;;0145
+0147;LATIN CAPITAL LETTER N WITH CARON;Lu;0;L;004E 030C;;;;N;LATIN CAPITAL LETTER N HACEK;;;0148;
+0148;LATIN SMALL LETTER N WITH CARON;Ll;0;L;006E 030C;;;;N;LATIN SMALL LETTER N HACEK;;0147;;0147
+0149;LATIN SMALL LETTER N PRECEDED BY APOSTROPHE;Ll;0;L;<compat> 02BC 006E;;;;N;LATIN SMALL LETTER APOSTROPHE N;;;;
+014A;LATIN CAPITAL LETTER ENG;Lu;0;L;;;;;N;;Sami;;014B;
+014B;LATIN SMALL LETTER ENG;Ll;0;L;;;;;N;;Sami;014A;;014A
+014C;LATIN CAPITAL LETTER O WITH MACRON;Lu;0;L;004F 0304;;;;N;LATIN CAPITAL LETTER O MACRON;;;014D;
+014D;LATIN SMALL LETTER O WITH MACRON;Ll;0;L;006F 0304;;;;N;LATIN SMALL LETTER O MACRON;;014C;;014C
+014E;LATIN CAPITAL LETTER O WITH BREVE;Lu;0;L;004F 0306;;;;N;LATIN CAPITAL LETTER O BREVE;;;014F;
+014F;LATIN SMALL LETTER O WITH BREVE;Ll;0;L;006F 0306;;;;N;LATIN SMALL LETTER O BREVE;;014E;;014E
+0150;LATIN CAPITAL LETTER O WITH DOUBLE ACUTE;Lu;0;L;004F 030B;;;;N;LATIN CAPITAL LETTER O DOUBLE ACUTE;;;0151;
+0151;LATIN SMALL LETTER O WITH DOUBLE ACUTE;Ll;0;L;006F 030B;;;;N;LATIN SMALL LETTER O DOUBLE ACUTE;;0150;;0150
+0152;LATIN CAPITAL LIGATURE OE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O E;;;0153;
+0153;LATIN SMALL LIGATURE OE;Ll;0;L;;;;;N;LATIN SMALL LETTER O E;;0152;;0152
+0154;LATIN CAPITAL LETTER R WITH ACUTE;Lu;0;L;0052 0301;;;;N;LATIN CAPITAL LETTER R ACUTE;;;0155;
+0155;LATIN SMALL LETTER R WITH ACUTE;Ll;0;L;0072 0301;;;;N;LATIN SMALL LETTER R ACUTE;;0154;;0154
+0156;LATIN CAPITAL LETTER R WITH CEDILLA;Lu;0;L;0052 0327;;;;N;LATIN CAPITAL LETTER R CEDILLA;;;0157;
+0157;LATIN SMALL LETTER R WITH CEDILLA;Ll;0;L;0072 0327;;;;N;LATIN SMALL LETTER R CEDILLA;;0156;;0156
+0158;LATIN CAPITAL LETTER R WITH CARON;Lu;0;L;0052 030C;;;;N;LATIN CAPITAL LETTER R HACEK;;;0159;
+0159;LATIN SMALL LETTER R WITH CARON;Ll;0;L;0072 030C;;;;N;LATIN SMALL LETTER R HACEK;;0158;;0158
+015A;LATIN CAPITAL LETTER S WITH ACUTE;Lu;0;L;0053 0301;;;;N;LATIN CAPITAL LETTER S ACUTE;;;015B;
+015B;LATIN SMALL LETTER S WITH ACUTE;Ll;0;L;0073 0301;;;;N;LATIN SMALL LETTER S ACUTE;;015A;;015A
+015C;LATIN CAPITAL LETTER S WITH CIRCUMFLEX;Lu;0;L;0053 0302;;;;N;LATIN CAPITAL LETTER S CIRCUMFLEX;;;015D;
+015D;LATIN SMALL LETTER S WITH CIRCUMFLEX;Ll;0;L;0073 0302;;;;N;LATIN SMALL LETTER S CIRCUMFLEX;;015C;;015C
+015E;LATIN CAPITAL LETTER S WITH CEDILLA;Lu;0;L;0053 0327;;;;N;LATIN CAPITAL LETTER S CEDILLA;*;;015F;
+015F;LATIN SMALL LETTER S WITH CEDILLA;Ll;0;L;0073 0327;;;;N;LATIN SMALL LETTER S CEDILLA;*;015E;;015E
+0160;LATIN CAPITAL LETTER S WITH CARON;Lu;0;L;0053 030C;;;;N;LATIN CAPITAL LETTER S HACEK;;;0161;
+0161;LATIN SMALL LETTER S WITH CARON;Ll;0;L;0073 030C;;;;N;LATIN SMALL LETTER S HACEK;;0160;;0160
+0162;LATIN CAPITAL LETTER T WITH CEDILLA;Lu;0;L;0054 0327;;;;N;LATIN CAPITAL LETTER T CEDILLA;*;;0163;
+0163;LATIN SMALL LETTER T WITH CEDILLA;Ll;0;L;0074 0327;;;;N;LATIN SMALL LETTER T CEDILLA;*;0162;;0162
+0164;LATIN CAPITAL LETTER T WITH CARON;Lu;0;L;0054 030C;;;;N;LATIN CAPITAL LETTER T HACEK;;;0165;
+0165;LATIN SMALL LETTER T WITH CARON;Ll;0;L;0074 030C;;;;N;LATIN SMALL LETTER T HACEK;;0164;;0164
+0166;LATIN CAPITAL LETTER T WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T BAR;;;0167;
+0167;LATIN SMALL LETTER T WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER T BAR;;0166;;0166
+0168;LATIN CAPITAL LETTER U WITH TILDE;Lu;0;L;0055 0303;;;;N;LATIN CAPITAL LETTER U TILDE;;;0169;
+0169;LATIN SMALL LETTER U WITH TILDE;Ll;0;L;0075 0303;;;;N;LATIN SMALL LETTER U TILDE;;0168;;0168
+016A;LATIN CAPITAL LETTER U WITH MACRON;Lu;0;L;0055 0304;;;;N;LATIN CAPITAL LETTER U MACRON;;;016B;
+016B;LATIN SMALL LETTER U WITH MACRON;Ll;0;L;0075 0304;;;;N;LATIN SMALL LETTER U MACRON;;016A;;016A
+016C;LATIN CAPITAL LETTER U WITH BREVE;Lu;0;L;0055 0306;;;;N;LATIN CAPITAL LETTER U BREVE;;;016D;
+016D;LATIN SMALL LETTER U WITH BREVE;Ll;0;L;0075 0306;;;;N;LATIN SMALL LETTER U BREVE;;016C;;016C
+016E;LATIN CAPITAL LETTER U WITH RING ABOVE;Lu;0;L;0055 030A;;;;N;LATIN CAPITAL LETTER U RING;;;016F;
+016F;LATIN SMALL LETTER U WITH RING ABOVE;Ll;0;L;0075 030A;;;;N;LATIN SMALL LETTER U RING;;016E;;016E
+0170;LATIN CAPITAL LETTER U WITH DOUBLE ACUTE;Lu;0;L;0055 030B;;;;N;LATIN CAPITAL LETTER U DOUBLE ACUTE;;;0171;
+0171;LATIN SMALL LETTER U WITH DOUBLE ACUTE;Ll;0;L;0075 030B;;;;N;LATIN SMALL LETTER U DOUBLE ACUTE;;0170;;0170
+0172;LATIN CAPITAL LETTER U WITH OGONEK;Lu;0;L;0055 0328;;;;N;LATIN CAPITAL LETTER U OGONEK;;;0173;
+0173;LATIN SMALL LETTER U WITH OGONEK;Ll;0;L;0075 0328;;;;N;LATIN SMALL LETTER U OGONEK;;0172;;0172
+0174;LATIN CAPITAL LETTER W WITH CIRCUMFLEX;Lu;0;L;0057 0302;;;;N;LATIN CAPITAL LETTER W CIRCUMFLEX;;;0175;
+0175;LATIN SMALL LETTER W WITH CIRCUMFLEX;Ll;0;L;0077 0302;;;;N;LATIN SMALL LETTER W CIRCUMFLEX;;0174;;0174
+0176;LATIN CAPITAL LETTER Y WITH CIRCUMFLEX;Lu;0;L;0059 0302;;;;N;LATIN CAPITAL LETTER Y CIRCUMFLEX;;;0177;
+0177;LATIN SMALL LETTER Y WITH CIRCUMFLEX;Ll;0;L;0079 0302;;;;N;LATIN SMALL LETTER Y CIRCUMFLEX;;0176;;0176
+0178;LATIN CAPITAL LETTER Y WITH DIAERESIS;Lu;0;L;0059 0308;;;;N;LATIN CAPITAL LETTER Y DIAERESIS;;;00FF;
+0179;LATIN CAPITAL LETTER Z WITH ACUTE;Lu;0;L;005A 0301;;;;N;LATIN CAPITAL LETTER Z ACUTE;;;017A;
+017A;LATIN SMALL LETTER Z WITH ACUTE;Ll;0;L;007A 0301;;;;N;LATIN SMALL LETTER Z ACUTE;;0179;;0179
+017B;LATIN CAPITAL LETTER Z WITH DOT ABOVE;Lu;0;L;005A 0307;;;;N;LATIN CAPITAL LETTER Z DOT;;;017C;
+017C;LATIN SMALL LETTER Z WITH DOT ABOVE;Ll;0;L;007A 0307;;;;N;LATIN SMALL LETTER Z DOT;;017B;;017B
+017D;LATIN CAPITAL LETTER Z WITH CARON;Lu;0;L;005A 030C;;;;N;LATIN CAPITAL LETTER Z HACEK;;;017E;
+017E;LATIN SMALL LETTER Z WITH CARON;Ll;0;L;007A 030C;;;;N;LATIN SMALL LETTER Z HACEK;;017D;;017D
+017F;LATIN SMALL LETTER LONG S;Ll;0;L;<compat> 0073;;;;N;;;0053;;0053
+0180;LATIN SMALL LETTER B WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER B BAR;;;;
+0181;LATIN CAPITAL LETTER B WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER B HOOK;;;0253;
+0182;LATIN CAPITAL LETTER B WITH TOPBAR;Lu;0;L;;;;;N;LATIN CAPITAL LETTER B TOPBAR;;;0183;
+0183;LATIN SMALL LETTER B WITH TOPBAR;Ll;0;L;;;;;N;LATIN SMALL LETTER B TOPBAR;;0182;;0182
+0184;LATIN CAPITAL LETTER TONE SIX;Lu;0;L;;;;;N;;;;0185;
+0185;LATIN SMALL LETTER TONE SIX;Ll;0;L;;;;;N;;;0184;;0184
+0186;LATIN CAPITAL LETTER OPEN O;Lu;0;L;;;;;N;;;;0254;
+0187;LATIN CAPITAL LETTER C WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER C HOOK;;;0188;
+0188;LATIN SMALL LETTER C WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER C HOOK;;0187;;0187
+0189;LATIN CAPITAL LETTER AFRICAN D;Lu;0;L;;;;;N;;*;;0256;
+018A;LATIN CAPITAL LETTER D WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D HOOK;;;0257;
+018B;LATIN CAPITAL LETTER D WITH TOPBAR;Lu;0;L;;;;;N;LATIN CAPITAL LETTER D TOPBAR;;;018C;
+018C;LATIN SMALL LETTER D WITH TOPBAR;Ll;0;L;;;;;N;LATIN SMALL LETTER D TOPBAR;;018B;;018B
+018D;LATIN SMALL LETTER TURNED DELTA;Ll;0;L;;;;;N;;;;;
+018E;LATIN CAPITAL LETTER REVERSED E;Lu;0;L;;;;;N;LATIN CAPITAL LETTER TURNED E;;;01DD;
+018F;LATIN CAPITAL LETTER SCHWA;Lu;0;L;;;;;N;;;;0259;
+0190;LATIN CAPITAL LETTER OPEN E;Lu;0;L;;;;;N;LATIN CAPITAL LETTER EPSILON;;;025B;
+0191;LATIN CAPITAL LETTER F WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER F HOOK;;;0192;
+0192;LATIN SMALL LETTER F WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT F;;0191;;0191
+0193;LATIN CAPITAL LETTER G WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER G HOOK;;;0260;
+0194;LATIN CAPITAL LETTER GAMMA;Lu;0;L;;;;;N;;;;0263;
+0195;LATIN SMALL LETTER HV;Ll;0;L;;;;;N;LATIN SMALL LETTER H V;hwair;01F6;;01F6
+0196;LATIN CAPITAL LETTER IOTA;Lu;0;L;;;;;N;;;;0269;
+0197;LATIN CAPITAL LETTER I WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER BARRED I;;;0268;
+0198;LATIN CAPITAL LETTER K WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER K HOOK;;;0199;
+0199;LATIN SMALL LETTER K WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER K HOOK;;0198;;0198
+019A;LATIN SMALL LETTER L WITH BAR;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED L;;;;
+019B;LATIN SMALL LETTER LAMBDA WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED LAMBDA;;;;
+019C;LATIN CAPITAL LETTER TURNED M;Lu;0;L;;;;;N;;;;026F;
+019D;LATIN CAPITAL LETTER N WITH LEFT HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER N HOOK;;;0272;
+019E;LATIN SMALL LETTER N WITH LONG RIGHT LEG;Ll;0;L;;;;;N;;;0220;;0220
+019F;LATIN CAPITAL LETTER O WITH MIDDLE TILDE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER BARRED O;*;;0275;
+01A0;LATIN CAPITAL LETTER O WITH HORN;Lu;0;L;004F 031B;;;;N;LATIN CAPITAL LETTER O HORN;;;01A1;
+01A1;LATIN SMALL LETTER O WITH HORN;Ll;0;L;006F 031B;;;;N;LATIN SMALL LETTER O HORN;;01A0;;01A0
+01A2;LATIN CAPITAL LETTER OI;Lu;0;L;;;;;N;LATIN CAPITAL LETTER O I;gha;;01A3;
+01A3;LATIN SMALL LETTER OI;Ll;0;L;;;;;N;LATIN SMALL LETTER O I;gha;01A2;;01A2
+01A4;LATIN CAPITAL LETTER P WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER P HOOK;;;01A5;
+01A5;LATIN SMALL LETTER P WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER P HOOK;;01A4;;01A4
+01A6;LATIN LETTER YR;Lu;0;L;;;;;N;LATIN LETTER Y R;*;;0280;
+01A7;LATIN CAPITAL LETTER TONE TWO;Lu;0;L;;;;;N;;;;01A8;
+01A8;LATIN SMALL LETTER TONE TWO;Ll;0;L;;;;;N;;;01A7;;01A7
+01A9;LATIN CAPITAL LETTER ESH;Lu;0;L;;;;;N;;;;0283;
+01AA;LATIN LETTER REVERSED ESH LOOP;Ll;0;L;;;;;N;;;;;
+01AB;LATIN SMALL LETTER T WITH PALATAL HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T PALATAL HOOK;;;;
+01AC;LATIN CAPITAL LETTER T WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T HOOK;;;01AD;
+01AD;LATIN SMALL LETTER T WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T HOOK;;01AC;;01AC
+01AE;LATIN CAPITAL LETTER T WITH RETROFLEX HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER T RETROFLEX HOOK;;;0288;
+01AF;LATIN CAPITAL LETTER U WITH HORN;Lu;0;L;0055 031B;;;;N;LATIN CAPITAL LETTER U HORN;;;01B0;
+01B0;LATIN SMALL LETTER U WITH HORN;Ll;0;L;0075 031B;;;;N;LATIN SMALL LETTER U HORN;;01AF;;01AF
+01B1;LATIN CAPITAL LETTER UPSILON;Lu;0;L;;;;;N;;;;028A;
+01B2;LATIN CAPITAL LETTER V WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER SCRIPT V;;;028B;
+01B3;LATIN CAPITAL LETTER Y WITH HOOK;Lu;0;L;;;;;N;LATIN CAPITAL LETTER Y HOOK;;;01B4;
+01B4;LATIN SMALL LETTER Y WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Y HOOK;;01B3;;01B3
+01B5;LATIN CAPITAL LETTER Z WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER Z BAR;;;01B6;
+01B6;LATIN SMALL LETTER Z WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER Z BAR;;01B5;;01B5
+01B7;LATIN CAPITAL LETTER EZH;Lu;0;L;;;;;N;LATIN CAPITAL LETTER YOGH;;;0292;
+01B8;LATIN CAPITAL LETTER EZH REVERSED;Lu;0;L;;;;;N;LATIN CAPITAL LETTER REVERSED YOGH;;;01B9;
+01B9;LATIN SMALL LETTER EZH REVERSED;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED YOGH;;01B8;;01B8
+01BA;LATIN SMALL LETTER EZH WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH WITH TAIL;;;;
+01BB;LATIN LETTER TWO WITH STROKE;Lo;0;L;;;;;N;LATIN LETTER TWO BAR;;;;
+01BC;LATIN CAPITAL LETTER TONE FIVE;Lu;0;L;;;;;N;;;;01BD;
+01BD;LATIN SMALL LETTER TONE FIVE;Ll;0;L;;;;;N;;;01BC;;01BC
+01BE;LATIN LETTER INVERTED GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER INVERTED GLOTTAL STOP BAR;;;;
+01BF;LATIN LETTER WYNN;Ll;0;L;;;;;N;;;01F7;;01F7
+01C0;LATIN LETTER DENTAL CLICK;Lo;0;L;;;;;N;LATIN LETTER PIPE;;;;
+01C1;LATIN LETTER LATERAL CLICK;Lo;0;L;;;;;N;LATIN LETTER DOUBLE PIPE;;;;
+01C2;LATIN LETTER ALVEOLAR CLICK;Lo;0;L;;;;;N;LATIN LETTER PIPE DOUBLE BAR;;;;
+01C3;LATIN LETTER RETROFLEX CLICK;Lo;0;L;;;;;N;LATIN LETTER EXCLAMATION MARK;;;;
+01C4;LATIN CAPITAL LETTER DZ WITH CARON;Lu;0;L;<compat> 0044 017D;;;;N;LATIN CAPITAL LETTER D Z HACEK;;;01C6;01C5
+01C5;LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON;Lt;0;L;<compat> 0044 017E;;;;N;LATIN LETTER CAPITAL D SMALL Z HACEK;;01C4;01C6;
+01C6;LATIN SMALL LETTER DZ WITH CARON;Ll;0;L;<compat> 0064 017E;;;;N;LATIN SMALL LETTER D Z HACEK;;01C4;;01C5
+01C7;LATIN CAPITAL LETTER LJ;Lu;0;L;<compat> 004C 004A;;;;N;LATIN CAPITAL LETTER L J;;;01C9;01C8
+01C8;LATIN CAPITAL LETTER L WITH SMALL LETTER J;Lt;0;L;<compat> 004C 006A;;;;N;LATIN LETTER CAPITAL L SMALL J;;01C7;01C9;
+01C9;LATIN SMALL LETTER LJ;Ll;0;L;<compat> 006C 006A;;;;N;LATIN SMALL LETTER L J;;01C7;;01C8
+01CA;LATIN CAPITAL LETTER NJ;Lu;0;L;<compat> 004E 004A;;;;N;LATIN CAPITAL LETTER N J;;;01CC;01CB
+01CB;LATIN CAPITAL LETTER N WITH SMALL LETTER J;Lt;0;L;<compat> 004E 006A;;;;N;LATIN LETTER CAPITAL N SMALL J;;01CA;01CC;
+01CC;LATIN SMALL LETTER NJ;Ll;0;L;<compat> 006E 006A;;;;N;LATIN SMALL LETTER N J;;01CA;;01CB
+01CD;LATIN CAPITAL LETTER A WITH CARON;Lu;0;L;0041 030C;;;;N;LATIN CAPITAL LETTER A HACEK;;;01CE;
+01CE;LATIN SMALL LETTER A WITH CARON;Ll;0;L;0061 030C;;;;N;LATIN SMALL LETTER A HACEK;;01CD;;01CD
+01CF;LATIN CAPITAL LETTER I WITH CARON;Lu;0;L;0049 030C;;;;N;LATIN CAPITAL LETTER I HACEK;;;01D0;
+01D0;LATIN SMALL LETTER I WITH CARON;Ll;0;L;0069 030C;;;;N;LATIN SMALL LETTER I HACEK;;01CF;;01CF
+01D1;LATIN CAPITAL LETTER O WITH CARON;Lu;0;L;004F 030C;;;;N;LATIN CAPITAL LETTER O HACEK;;;01D2;
+01D2;LATIN SMALL LETTER O WITH CARON;Ll;0;L;006F 030C;;;;N;LATIN SMALL LETTER O HACEK;;01D1;;01D1
+01D3;LATIN CAPITAL LETTER U WITH CARON;Lu;0;L;0055 030C;;;;N;LATIN CAPITAL LETTER U HACEK;;;01D4;
+01D4;LATIN SMALL LETTER U WITH CARON;Ll;0;L;0075 030C;;;;N;LATIN SMALL LETTER U HACEK;;01D3;;01D3
+01D5;LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON;Lu;0;L;00DC 0304;;;;N;LATIN CAPITAL LETTER U DIAERESIS MACRON;;;01D6;
+01D6;LATIN SMALL LETTER U WITH DIAERESIS AND MACRON;Ll;0;L;00FC 0304;;;;N;LATIN SMALL LETTER U DIAERESIS MACRON;;01D5;;01D5
+01D7;LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE;Lu;0;L;00DC 0301;;;;N;LATIN CAPITAL LETTER U DIAERESIS ACUTE;;;01D8;
+01D8;LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE;Ll;0;L;00FC 0301;;;;N;LATIN SMALL LETTER U DIAERESIS ACUTE;;01D7;;01D7
+01D9;LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON;Lu;0;L;00DC 030C;;;;N;LATIN CAPITAL LETTER U DIAERESIS HACEK;;;01DA;
+01DA;LATIN SMALL LETTER U WITH DIAERESIS AND CARON;Ll;0;L;00FC 030C;;;;N;LATIN SMALL LETTER U DIAERESIS HACEK;;01D9;;01D9
+01DB;LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE;Lu;0;L;00DC 0300;;;;N;LATIN CAPITAL LETTER U DIAERESIS GRAVE;;;01DC;
+01DC;LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE;Ll;0;L;00FC 0300;;;;N;LATIN SMALL LETTER U DIAERESIS GRAVE;;01DB;;01DB
+01DD;LATIN SMALL LETTER TURNED E;Ll;0;L;;;;;N;;;018E;;018E
+01DE;LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON;Lu;0;L;00C4 0304;;;;N;LATIN CAPITAL LETTER A DIAERESIS MACRON;;;01DF;
+01DF;LATIN SMALL LETTER A WITH DIAERESIS AND MACRON;Ll;0;L;00E4 0304;;;;N;LATIN SMALL LETTER A DIAERESIS MACRON;;01DE;;01DE
+01E0;LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON;Lu;0;L;0226 0304;;;;N;LATIN CAPITAL LETTER A DOT MACRON;;;01E1;
+01E1;LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON;Ll;0;L;0227 0304;;;;N;LATIN SMALL LETTER A DOT MACRON;;01E0;;01E0
+01E2;LATIN CAPITAL LETTER AE WITH MACRON;Lu;0;L;00C6 0304;;;;N;LATIN CAPITAL LETTER A E MACRON;ash *;;01E3;
+01E3;LATIN SMALL LETTER AE WITH MACRON;Ll;0;L;00E6 0304;;;;N;LATIN SMALL LETTER A E MACRON;ash *;01E2;;01E2
+01E4;LATIN CAPITAL LETTER G WITH STROKE;Lu;0;L;;;;;N;LATIN CAPITAL LETTER G BAR;;;01E5;
+01E5;LATIN SMALL LETTER G WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER G BAR;;01E4;;01E4
+01E6;LATIN CAPITAL LETTER G WITH CARON;Lu;0;L;0047 030C;;;;N;LATIN CAPITAL LETTER G HACEK;;;01E7;
+01E7;LATIN SMALL LETTER G WITH CARON;Ll;0;L;0067 030C;;;;N;LATIN SMALL LETTER G HACEK;;01E6;;01E6
+01E8;LATIN CAPITAL LETTER K WITH CARON;Lu;0;L;004B 030C;;;;N;LATIN CAPITAL LETTER K HACEK;;;01E9;
+01E9;LATIN SMALL LETTER K WITH CARON;Ll;0;L;006B 030C;;;;N;LATIN SMALL LETTER K HACEK;;01E8;;01E8
+01EA;LATIN CAPITAL LETTER O WITH OGONEK;Lu;0;L;004F 0328;;;;N;LATIN CAPITAL LETTER O OGONEK;;;01EB;
+01EB;LATIN SMALL LETTER O WITH OGONEK;Ll;0;L;006F 0328;;;;N;LATIN SMALL LETTER O OGONEK;;01EA;;01EA
+01EC;LATIN CAPITAL LETTER O WITH OGONEK AND MACRON;Lu;0;L;01EA 0304;;;;N;LATIN CAPITAL LETTER O OGONEK MACRON;;;01ED;
+01ED;LATIN SMALL LETTER O WITH OGONEK AND MACRON;Ll;0;L;01EB 0304;;;;N;LATIN SMALL LETTER O OGONEK MACRON;;01EC;;01EC
+01EE;LATIN CAPITAL LETTER EZH WITH CARON;Lu;0;L;01B7 030C;;;;N;LATIN CAPITAL LETTER YOGH HACEK;;;01EF;
+01EF;LATIN SMALL LETTER EZH WITH CARON;Ll;0;L;0292 030C;;;;N;LATIN SMALL LETTER YOGH HACEK;;01EE;;01EE
+01F0;LATIN SMALL LETTER J WITH CARON;Ll;0;L;006A 030C;;;;N;LATIN SMALL LETTER J HACEK;;;;
+01F1;LATIN CAPITAL LETTER DZ;Lu;0;L;<compat> 0044 005A;;;;N;;;;01F3;01F2
+01F2;LATIN CAPITAL LETTER D WITH SMALL LETTER Z;Lt;0;L;<compat> 0044 007A;;;;N;;;01F1;01F3;
+01F3;LATIN SMALL LETTER DZ;Ll;0;L;<compat> 0064 007A;;;;N;;;01F1;;01F2
+01F4;LATIN CAPITAL LETTER G WITH ACUTE;Lu;0;L;0047 0301;;;;N;;;;01F5;
+01F5;LATIN SMALL LETTER G WITH ACUTE;Ll;0;L;0067 0301;;;;N;;;01F4;;01F4
+01F6;LATIN CAPITAL LETTER HWAIR;Lu;0;L;;;;;N;;;;0195;
+01F7;LATIN CAPITAL LETTER WYNN;Lu;0;L;;;;;N;;;;01BF;
+01F8;LATIN CAPITAL LETTER N WITH GRAVE;Lu;0;L;004E 0300;;;;N;;;;01F9;
+01F9;LATIN SMALL LETTER N WITH GRAVE;Ll;0;L;006E 0300;;;;N;;;01F8;;01F8
+01FA;LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE;Lu;0;L;00C5 0301;;;;N;;;;01FB;
+01FB;LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE;Ll;0;L;00E5 0301;;;;N;;;01FA;;01FA
+01FC;LATIN CAPITAL LETTER AE WITH ACUTE;Lu;0;L;00C6 0301;;;;N;;ash *;;01FD;
+01FD;LATIN SMALL LETTER AE WITH ACUTE;Ll;0;L;00E6 0301;;;;N;;ash *;01FC;;01FC
+01FE;LATIN CAPITAL LETTER O WITH STROKE AND ACUTE;Lu;0;L;00D8 0301;;;;N;;;;01FF;
+01FF;LATIN SMALL LETTER O WITH STROKE AND ACUTE;Ll;0;L;00F8 0301;;;;N;;;01FE;;01FE
+0200;LATIN CAPITAL LETTER A WITH DOUBLE GRAVE;Lu;0;L;0041 030F;;;;N;;;;0201;
+0201;LATIN SMALL LETTER A WITH DOUBLE GRAVE;Ll;0;L;0061 030F;;;;N;;;0200;;0200
+0202;LATIN CAPITAL LETTER A WITH INVERTED BREVE;Lu;0;L;0041 0311;;;;N;;;;0203;
+0203;LATIN SMALL LETTER A WITH INVERTED BREVE;Ll;0;L;0061 0311;;;;N;;;0202;;0202
+0204;LATIN CAPITAL LETTER E WITH DOUBLE GRAVE;Lu;0;L;0045 030F;;;;N;;;;0205;
+0205;LATIN SMALL LETTER E WITH DOUBLE GRAVE;Ll;0;L;0065 030F;;;;N;;;0204;;0204
+0206;LATIN CAPITAL LETTER E WITH INVERTED BREVE;Lu;0;L;0045 0311;;;;N;;;;0207;
+0207;LATIN SMALL LETTER E WITH INVERTED BREVE;Ll;0;L;0065 0311;;;;N;;;0206;;0206
+0208;LATIN CAPITAL LETTER I WITH DOUBLE GRAVE;Lu;0;L;0049 030F;;;;N;;;;0209;
+0209;LATIN SMALL LETTER I WITH DOUBLE GRAVE;Ll;0;L;0069 030F;;;;N;;;0208;;0208
+020A;LATIN CAPITAL LETTER I WITH INVERTED BREVE;Lu;0;L;0049 0311;;;;N;;;;020B;
+020B;LATIN SMALL LETTER I WITH INVERTED BREVE;Ll;0;L;0069 0311;;;;N;;;020A;;020A
+020C;LATIN CAPITAL LETTER O WITH DOUBLE GRAVE;Lu;0;L;004F 030F;;;;N;;;;020D;
+020D;LATIN SMALL LETTER O WITH DOUBLE GRAVE;Ll;0;L;006F 030F;;;;N;;;020C;;020C
+020E;LATIN CAPITAL LETTER O WITH INVERTED BREVE;Lu;0;L;004F 0311;;;;N;;;;020F;
+020F;LATIN SMALL LETTER O WITH INVERTED BREVE;Ll;0;L;006F 0311;;;;N;;;020E;;020E
+0210;LATIN CAPITAL LETTER R WITH DOUBLE GRAVE;Lu;0;L;0052 030F;;;;N;;;;0211;
+0211;LATIN SMALL LETTER R WITH DOUBLE GRAVE;Ll;0;L;0072 030F;;;;N;;;0210;;0210
+0212;LATIN CAPITAL LETTER R WITH INVERTED BREVE;Lu;0;L;0052 0311;;;;N;;;;0213;
+0213;LATIN SMALL LETTER R WITH INVERTED BREVE;Ll;0;L;0072 0311;;;;N;;;0212;;0212
+0214;LATIN CAPITAL LETTER U WITH DOUBLE GRAVE;Lu;0;L;0055 030F;;;;N;;;;0215;
+0215;LATIN SMALL LETTER U WITH DOUBLE GRAVE;Ll;0;L;0075 030F;;;;N;;;0214;;0214
+0216;LATIN CAPITAL LETTER U WITH INVERTED BREVE;Lu;0;L;0055 0311;;;;N;;;;0217;
+0217;LATIN SMALL LETTER U WITH INVERTED BREVE;Ll;0;L;0075 0311;;;;N;;;0216;;0216
+0218;LATIN CAPITAL LETTER S WITH COMMA BELOW;Lu;0;L;0053 0326;;;;N;;*;;0219;
+0219;LATIN SMALL LETTER S WITH COMMA BELOW;Ll;0;L;0073 0326;;;;N;;*;0218;;0218
+021A;LATIN CAPITAL LETTER T WITH COMMA BELOW;Lu;0;L;0054 0326;;;;N;;*;;021B;
+021B;LATIN SMALL LETTER T WITH COMMA BELOW;Ll;0;L;0074 0326;;;;N;;*;021A;;021A
+021C;LATIN CAPITAL LETTER YOGH;Lu;0;L;;;;;N;;;;021D;
+021D;LATIN SMALL LETTER YOGH;Ll;0;L;;;;;N;;;021C;;021C
+021E;LATIN CAPITAL LETTER H WITH CARON;Lu;0;L;0048 030C;;;;N;;;;021F;
+021F;LATIN SMALL LETTER H WITH CARON;Ll;0;L;0068 030C;;;;N;;;021E;;021E
+0220;LATIN CAPITAL LETTER N WITH LONG RIGHT LEG;Lu;0;L;;;;;N;;;;019E;
+0222;LATIN CAPITAL LETTER OU;Lu;0;L;;;;;N;;;;0223;
+0223;LATIN SMALL LETTER OU;Ll;0;L;;;;;N;;;0222;;0222
+0224;LATIN CAPITAL LETTER Z WITH HOOK;Lu;0;L;;;;;N;;;;0225;
+0225;LATIN SMALL LETTER Z WITH HOOK;Ll;0;L;;;;;N;;;0224;;0224
+0226;LATIN CAPITAL LETTER A WITH DOT ABOVE;Lu;0;L;0041 0307;;;;N;;;;0227;
+0227;LATIN SMALL LETTER A WITH DOT ABOVE;Ll;0;L;0061 0307;;;;N;;;0226;;0226
+0228;LATIN CAPITAL LETTER E WITH CEDILLA;Lu;0;L;0045 0327;;;;N;;;;0229;
+0229;LATIN SMALL LETTER E WITH CEDILLA;Ll;0;L;0065 0327;;;;N;;;0228;;0228
+022A;LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON;Lu;0;L;00D6 0304;;;;N;;;;022B;
+022B;LATIN SMALL LETTER O WITH DIAERESIS AND MACRON;Ll;0;L;00F6 0304;;;;N;;;022A;;022A
+022C;LATIN CAPITAL LETTER O WITH TILDE AND MACRON;Lu;0;L;00D5 0304;;;;N;;;;022D;
+022D;LATIN SMALL LETTER O WITH TILDE AND MACRON;Ll;0;L;00F5 0304;;;;N;;;022C;;022C
+022E;LATIN CAPITAL LETTER O WITH DOT ABOVE;Lu;0;L;004F 0307;;;;N;;;;022F;
+022F;LATIN SMALL LETTER O WITH DOT ABOVE;Ll;0;L;006F 0307;;;;N;;;022E;;022E
+0230;LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON;Lu;0;L;022E 0304;;;;N;;;;0231;
+0231;LATIN SMALL LETTER O WITH DOT ABOVE AND MACRON;Ll;0;L;022F 0304;;;;N;;;0230;;0230
+0232;LATIN CAPITAL LETTER Y WITH MACRON;Lu;0;L;0059 0304;;;;N;;;;0233;
+0233;LATIN SMALL LETTER Y WITH MACRON;Ll;0;L;0079 0304;;;;N;;;0232;;0232
+0250;LATIN SMALL LETTER TURNED A;Ll;0;L;;;;;N;;;;;
+0251;LATIN SMALL LETTER ALPHA;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT A;;;;
+0252;LATIN SMALL LETTER TURNED ALPHA;Ll;0;L;;;;;N;LATIN SMALL LETTER TURNED SCRIPT A;;;;
+0253;LATIN SMALL LETTER B WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER B HOOK;;0181;;0181
+0254;LATIN SMALL LETTER OPEN O;Ll;0;L;;;;;N;;;0186;;0186
+0255;LATIN SMALL LETTER C WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER C CURL;;;;
+0256;LATIN SMALL LETTER D WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER D RETROFLEX HOOK;;0189;;0189
+0257;LATIN SMALL LETTER D WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER D HOOK;;018A;;018A
+0258;LATIN SMALL LETTER REVERSED E;Ll;0;L;;;;;N;;;;;
+0259;LATIN SMALL LETTER SCHWA;Ll;0;L;;;;;N;;;018F;;018F
+025A;LATIN SMALL LETTER SCHWA WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCHWA HOOK;;;;
+025B;LATIN SMALL LETTER OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER EPSILON;;0190;;0190
+025C;LATIN SMALL LETTER REVERSED OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED EPSILON;;;;
+025D;LATIN SMALL LETTER REVERSED OPEN E WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED EPSILON HOOK;;;;
+025E;LATIN SMALL LETTER CLOSED REVERSED OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER CLOSED REVERSED EPSILON;;;;
+025F;LATIN SMALL LETTER DOTLESS J WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER DOTLESS J BAR;;;;
+0260;LATIN SMALL LETTER G WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER G HOOK;;0193;;0193
+0261;LATIN SMALL LETTER SCRIPT G;Ll;0;L;;;;;N;;;;;
+0262;LATIN LETTER SMALL CAPITAL G;Ll;0;L;;;;;N;;;;;
+0263;LATIN SMALL LETTER GAMMA;Ll;0;L;;;;;N;;;0194;;0194
+0264;LATIN SMALL LETTER RAMS HORN;Ll;0;L;;;;;N;LATIN SMALL LETTER BABY GAMMA;;;;
+0265;LATIN SMALL LETTER TURNED H;Ll;0;L;;;;;N;;;;;
+0266;LATIN SMALL LETTER H WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER H HOOK;;;;
+0267;LATIN SMALL LETTER HENG WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER HENG HOOK;;;;
+0268;LATIN SMALL LETTER I WITH STROKE;Ll;0;L;;;;;N;LATIN SMALL LETTER BARRED I;;0197;;0197
+0269;LATIN SMALL LETTER IOTA;Ll;0;L;;;;;N;;;0196;;0196
+026A;LATIN LETTER SMALL CAPITAL I;Ll;0;L;;;;;N;;;;;
+026B;LATIN SMALL LETTER L WITH MIDDLE TILDE;Ll;0;L;;;;;N;;;;;
+026C;LATIN SMALL LETTER L WITH BELT;Ll;0;L;;;;;N;LATIN SMALL LETTER L BELT;;;;
+026D;LATIN SMALL LETTER L WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER L RETROFLEX HOOK;;;;
+026E;LATIN SMALL LETTER LEZH;Ll;0;L;;;;;N;LATIN SMALL LETTER L YOGH;;;;
+026F;LATIN SMALL LETTER TURNED M;Ll;0;L;;;;;N;;;019C;;019C
+0270;LATIN SMALL LETTER TURNED M WITH LONG LEG;Ll;0;L;;;;;N;;;;;
+0271;LATIN SMALL LETTER M WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER M HOOK;;;;
+0272;LATIN SMALL LETTER N WITH LEFT HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER N HOOK;;019D;;019D
+0273;LATIN SMALL LETTER N WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER N RETROFLEX HOOK;;;;
+0274;LATIN LETTER SMALL CAPITAL N;Ll;0;L;;;;;N;;;;;
+0275;LATIN SMALL LETTER BARRED O;Ll;0;L;;;;;N;;;019F;;019F
+0276;LATIN LETTER SMALL CAPITAL OE;Ll;0;L;;;;;N;LATIN LETTER SMALL CAPITAL O E;;;;
+0277;LATIN SMALL LETTER CLOSED OMEGA;Ll;0;L;;;;;N;;;;;
+0278;LATIN SMALL LETTER PHI;Ll;0;L;;;;;N;;;;;
+0279;LATIN SMALL LETTER TURNED R;Ll;0;L;;;;;N;;;;;
+027A;LATIN SMALL LETTER TURNED R WITH LONG LEG;Ll;0;L;;;;;N;;;;;
+027B;LATIN SMALL LETTER TURNED R WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER TURNED R HOOK;;;;
+027C;LATIN SMALL LETTER R WITH LONG LEG;Ll;0;L;;;;;N;;;;;
+027D;LATIN SMALL LETTER R WITH TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER R HOOK;;;;
+027E;LATIN SMALL LETTER R WITH FISHHOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER FISHHOOK R;;;;
+027F;LATIN SMALL LETTER REVERSED R WITH FISHHOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED FISHHOOK R;;;;
+0280;LATIN LETTER SMALL CAPITAL R;Ll;0;L;;;;;N;;*;01A6;;01A6
+0281;LATIN LETTER SMALL CAPITAL INVERTED R;Ll;0;L;;;;;N;;;;;
+0282;LATIN SMALL LETTER S WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER S HOOK;;;;
+0283;LATIN SMALL LETTER ESH;Ll;0;L;;;;;N;;;01A9;;01A9
+0284;LATIN SMALL LETTER DOTLESS J WITH STROKE AND HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER DOTLESS J BAR HOOK;;;;
+0285;LATIN SMALL LETTER SQUAT REVERSED ESH;Ll;0;L;;;;;N;;;;;
+0286;LATIN SMALL LETTER ESH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER ESH CURL;;;;
+0287;LATIN SMALL LETTER TURNED T;Ll;0;L;;;;;N;;;;;
+0288;LATIN SMALL LETTER T WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER T RETROFLEX HOOK;;01AE;;01AE
+0289;LATIN SMALL LETTER U BAR;Ll;0;L;;;;;N;;;;;
+028A;LATIN SMALL LETTER UPSILON;Ll;0;L;;;;;N;;;01B1;;01B1
+028B;LATIN SMALL LETTER V WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER SCRIPT V;;01B2;;01B2
+028C;LATIN SMALL LETTER TURNED V;Ll;0;L;;;;;N;;;;;
+028D;LATIN SMALL LETTER TURNED W;Ll;0;L;;;;;N;;;;;
+028E;LATIN SMALL LETTER TURNED Y;Ll;0;L;;;;;N;;;;;
+028F;LATIN LETTER SMALL CAPITAL Y;Ll;0;L;;;;;N;;;;;
+0290;LATIN SMALL LETTER Z WITH RETROFLEX HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Z RETROFLEX HOOK;;;;
+0291;LATIN SMALL LETTER Z WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER Z CURL;;;;
+0292;LATIN SMALL LETTER EZH;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH;;01B7;;01B7
+0293;LATIN SMALL LETTER EZH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER YOGH CURL;;;;
+0294;LATIN LETTER GLOTTAL STOP;Ll;0;L;;;;;N;;;;;
+0295;LATIN LETTER PHARYNGEAL VOICED FRICATIVE;Ll;0;L;;;;;N;LATIN LETTER REVERSED GLOTTAL STOP;;;;
+0296;LATIN LETTER INVERTED GLOTTAL STOP;Ll;0;L;;;;;N;;;;;
+0297;LATIN LETTER STRETCHED C;Ll;0;L;;;;;N;;;;;
+0298;LATIN LETTER BILABIAL CLICK;Ll;0;L;;;;;N;LATIN LETTER BULLSEYE;;;;
+0299;LATIN LETTER SMALL CAPITAL B;Ll;0;L;;;;;N;;;;;
+029A;LATIN SMALL LETTER CLOSED OPEN E;Ll;0;L;;;;;N;LATIN SMALL LETTER CLOSED EPSILON;;;;
+029B;LATIN LETTER SMALL CAPITAL G WITH HOOK;Ll;0;L;;;;;N;LATIN LETTER SMALL CAPITAL G HOOK;;;;
+029C;LATIN LETTER SMALL CAPITAL H;Ll;0;L;;;;;N;;;;;
+029D;LATIN SMALL LETTER J WITH CROSSED-TAIL;Ll;0;L;;;;;N;LATIN SMALL LETTER CROSSED-TAIL J;;;;
+029E;LATIN SMALL LETTER TURNED K;Ll;0;L;;;;;N;;;;;
+029F;LATIN LETTER SMALL CAPITAL L;Ll;0;L;;;;;N;;;;;
+02A0;LATIN SMALL LETTER Q WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER Q HOOK;;;;
+02A1;LATIN LETTER GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER GLOTTAL STOP BAR;;;;
+02A2;LATIN LETTER REVERSED GLOTTAL STOP WITH STROKE;Ll;0;L;;;;;N;LATIN LETTER REVERSED GLOTTAL STOP BAR;;;;
+02A3;LATIN SMALL LETTER DZ DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER D Z;;;;
+02A4;LATIN SMALL LETTER DEZH DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER D YOGH;;;;
+02A5;LATIN SMALL LETTER DZ DIGRAPH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER D Z CURL;;;;
+02A6;LATIN SMALL LETTER TS DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER T S;;;;
+02A7;LATIN SMALL LETTER TESH DIGRAPH;Ll;0;L;;;;;N;LATIN SMALL LETTER T ESH;;;;
+02A8;LATIN SMALL LETTER TC DIGRAPH WITH CURL;Ll;0;L;;;;;N;LATIN SMALL LETTER T C CURL;;;;
+02A9;LATIN SMALL LETTER FENG DIGRAPH;Ll;0;L;;;;;N;;;;;
+02AA;LATIN SMALL LETTER LS DIGRAPH;Ll;0;L;;;;;N;;;;;
+02AB;LATIN SMALL LETTER LZ DIGRAPH;Ll;0;L;;;;;N;;;;;
+02AC;LATIN LETTER BILABIAL PERCUSSIVE;Ll;0;L;;;;;N;;;;;
+02AD;LATIN LETTER BIDENTAL PERCUSSIVE;Ll;0;L;;;;;N;;;;;
+02B0;MODIFIER LETTER SMALL H;Lm;0;L;<super> 0068;;;;N;;;;;
+02B1;MODIFIER LETTER SMALL H WITH HOOK;Lm;0;L;<super> 0266;;;;N;MODIFIER LETTER SMALL H HOOK;;;;
+02B2;MODIFIER LETTER SMALL J;Lm;0;L;<super> 006A;;;;N;;;;;
+02B3;MODIFIER LETTER SMALL R;Lm;0;L;<super> 0072;;;;N;;;;;
+02B4;MODIFIER LETTER SMALL TURNED R;Lm;0;L;<super> 0279;;;;N;;;;;
+02B5;MODIFIER LETTER SMALL TURNED R WITH HOOK;Lm;0;L;<super> 027B;;;;N;MODIFIER LETTER SMALL TURNED R HOOK;;;;
+02B6;MODIFIER LETTER SMALL CAPITAL INVERTED R;Lm;0;L;<super> 0281;;;;N;;;;;
+02B7;MODIFIER LETTER SMALL W;Lm;0;L;<super> 0077;;;;N;;;;;
+02B8;MODIFIER LETTER SMALL Y;Lm;0;L;<super> 0079;;;;N;;;;;
+02B9;MODIFIER LETTER PRIME;Sk;0;ON;;;;;N;;;;;
+02BA;MODIFIER LETTER DOUBLE PRIME;Sk;0;ON;;;;;N;;;;;
+02BB;MODIFIER LETTER TURNED COMMA;Lm;0;L;;;;;N;;;;;
+02BC;MODIFIER LETTER APOSTROPHE;Lm;0;L;;;;;N;;;;;
+02BD;MODIFIER LETTER REVERSED COMMA;Lm;0;L;;;;;N;;;;;
+02BE;MODIFIER LETTER RIGHT HALF RING;Lm;0;L;;;;;N;;;;;
+02BF;MODIFIER LETTER LEFT HALF RING;Lm;0;L;;;;;N;;;;;
+02C0;MODIFIER LETTER GLOTTAL STOP;Lm;0;L;;;;;N;;;;;
+02C1;MODIFIER LETTER REVERSED GLOTTAL STOP;Lm;0;L;;;;;N;;;;;
+02C2;MODIFIER LETTER LEFT ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02C3;MODIFIER LETTER RIGHT ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02C4;MODIFIER LETTER UP ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02C5;MODIFIER LETTER DOWN ARROWHEAD;Sk;0;ON;;;;;N;;;;;
+02C6;MODIFIER LETTER CIRCUMFLEX ACCENT;Sk;0;ON;;;;;N;MODIFIER LETTER CIRCUMFLEX;;;;
+02C7;CARON;Sk;0;ON;;;;;N;MODIFIER LETTER HACEK;Mandarin Chinese third tone;;;
+02C8;MODIFIER LETTER VERTICAL LINE;Sk;0;ON;;;;;N;;;;;
+02C9;MODIFIER LETTER MACRON;Sk;0;ON;;;;;N;;Mandarin Chinese first tone;;;
+02CA;MODIFIER LETTER ACUTE ACCENT;Sk;0;ON;;;;;N;MODIFIER LETTER ACUTE;Mandarin Chinese second tone;;;
+02CB;MODIFIER LETTER GRAVE ACCENT;Sk;0;ON;;;;;N;MODIFIER LETTER GRAVE;Mandarin Chinese fourth tone;;;
+02CC;MODIFIER LETTER LOW VERTICAL LINE;Sk;0;ON;;;;;N;;;;;
+02CD;MODIFIER LETTER LOW MACRON;Sk;0;ON;;;;;N;;;;;
+02CE;MODIFIER LETTER LOW GRAVE ACCENT;Sk;0;ON;;;;;N;MODIFIER LETTER LOW GRAVE;;;;
+02CF;MODIFIER LETTER LOW ACUTE ACCENT;Sk;0;ON;;;;;N;MODIFIER LETTER LOW ACUTE;;;;
+02D0;MODIFIER LETTER TRIANGULAR COLON;Lm;0;L;;;;;N;;;;;
+02D1;MODIFIER LETTER HALF TRIANGULAR COLON;Lm;0;L;;;;;N;;;;;
+02D2;MODIFIER LETTER CENTRED RIGHT HALF RING;Sk;0;ON;;;;;N;MODIFIER LETTER CENTERED RIGHT HALF RING;;;;
+02D3;MODIFIER LETTER CENTRED LEFT HALF RING;Sk;0;ON;;;;;N;MODIFIER LETTER CENTERED LEFT HALF RING;;;;
+02D4;MODIFIER LETTER UP TACK;Sk;0;ON;;;;;N;;;;;
+02D5;MODIFIER LETTER DOWN TACK;Sk;0;ON;;;;;N;;;;;
+02D6;MODIFIER LETTER PLUS SIGN;Sk;0;ON;;;;;N;;;;;
+02D7;MODIFIER LETTER MINUS SIGN;Sk;0;ON;;;;;N;;;;;
+02D8;BREVE;Sk;0;ON;<compat> 0020 0306;;;;N;SPACING BREVE;;;;
+02D9;DOT ABOVE;Sk;0;ON;<compat> 0020 0307;;;;N;SPACING DOT ABOVE;Mandarin Chinese light tone;;;
+02DA;RING ABOVE;Sk;0;ON;<compat> 0020 030A;;;;N;SPACING RING ABOVE;;;;
+02DB;OGONEK;Sk;0;ON;<compat> 0020 0328;;;;N;SPACING OGONEK;;;;
+02DC;SMALL TILDE;Sk;0;ON;<compat> 0020 0303;;;;N;SPACING TILDE;;;;
+02DD;DOUBLE ACUTE ACCENT;Sk;0;ON;<compat> 0020 030B;;;;N;SPACING DOUBLE ACUTE;;;;
+02DE;MODIFIER LETTER RHOTIC HOOK;Sk;0;ON;;;;;N;;;;;
+02DF;MODIFIER LETTER CROSS ACCENT;Sk;0;ON;;;;;N;;;;;
+02E0;MODIFIER LETTER SMALL GAMMA;Lm;0;L;<super> 0263;;;;N;;;;;
+02E1;MODIFIER LETTER SMALL L;Lm;0;L;<super> 006C;;;;N;;;;;
+02E2;MODIFIER LETTER SMALL S;Lm;0;L;<super> 0073;;;;N;;;;;
+02E3;MODIFIER LETTER SMALL X;Lm;0;L;<super> 0078;;;;N;;;;;
+02E4;MODIFIER LETTER SMALL REVERSED GLOTTAL STOP;Lm;0;L;<super> 0295;;;;N;;;;;
+02E5;MODIFIER LETTER EXTRA-HIGH TONE BAR;Sk;0;ON;;;;;N;;;;;
+02E6;MODIFIER LETTER HIGH TONE BAR;Sk;0;ON;;;;;N;;;;;
+02E7;MODIFIER LETTER MID TONE BAR;Sk;0;ON;;;;;N;;;;;
+02E8;MODIFIER LETTER LOW TONE BAR;Sk;0;ON;;;;;N;;;;;
+02E9;MODIFIER LETTER EXTRA-LOW TONE BAR;Sk;0;ON;;;;;N;;;;;
+02EA;MODIFIER LETTER YIN DEPARTING TONE MARK;Sk;0;ON;;;;;N;;;;;
+02EB;MODIFIER LETTER YANG DEPARTING TONE MARK;Sk;0;ON;;;;;N;;;;;
+02EC;MODIFIER LETTER VOICING;Sk;0;ON;;;;;N;;;;;
+02ED;MODIFIER LETTER UNASPIRATED;Sk;0;ON;;;;;N;;;;;
+02EE;MODIFIER LETTER DOUBLE APOSTROPHE;Lm;0;L;;;;;N;;;;;
+0300;COMBINING GRAVE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING GRAVE;Varia;;;
+0301;COMBINING ACUTE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING ACUTE;Oxia, Tonos;;;
+0302;COMBINING CIRCUMFLEX ACCENT;Mn;230;NSM;;;;;N;NON-SPACING CIRCUMFLEX;;;;
+0303;COMBINING TILDE;Mn;230;NSM;;;;;N;NON-SPACING TILDE;;;;
+0304;COMBINING MACRON;Mn;230;NSM;;;;;N;NON-SPACING MACRON;;;;
+0305;COMBINING OVERLINE;Mn;230;NSM;;;;;N;NON-SPACING OVERSCORE;;;;
+0306;COMBINING BREVE;Mn;230;NSM;;;;;N;NON-SPACING BREVE;Vrachy;;;
+0307;COMBINING DOT ABOVE;Mn;230;NSM;;;;;N;NON-SPACING DOT ABOVE;;;;
+0308;COMBINING DIAERESIS;Mn;230;NSM;;;;;N;NON-SPACING DIAERESIS;Dialytika;;;
+0309;COMBINING HOOK ABOVE;Mn;230;NSM;;;;;N;NON-SPACING HOOK ABOVE;;;;
+030A;COMBINING RING ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RING ABOVE;;;;
+030B;COMBINING DOUBLE ACUTE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE ACUTE;;;;
+030C;COMBINING CARON;Mn;230;NSM;;;;;N;NON-SPACING HACEK;;;;
+030D;COMBINING VERTICAL LINE ABOVE;Mn;230;NSM;;;;;N;NON-SPACING VERTICAL LINE ABOVE;;;;
+030E;COMBINING DOUBLE VERTICAL LINE ABOVE;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE VERTICAL LINE ABOVE;;;;
+030F;COMBINING DOUBLE GRAVE ACCENT;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE GRAVE;;;;
+0310;COMBINING CANDRABINDU;Mn;230;NSM;;;;;N;NON-SPACING CANDRABINDU;;;;
+0311;COMBINING INVERTED BREVE;Mn;230;NSM;;;;;N;NON-SPACING INVERTED BREVE;;;;
+0312;COMBINING TURNED COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING TURNED COMMA ABOVE;;;;
+0313;COMBINING COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING COMMA ABOVE;Psili;;;
+0314;COMBINING REVERSED COMMA ABOVE;Mn;230;NSM;;;;;N;NON-SPACING REVERSED COMMA ABOVE;Dasia;;;
+0315;COMBINING COMMA ABOVE RIGHT;Mn;232;NSM;;;;;N;NON-SPACING COMMA ABOVE RIGHT;;;;
+0316;COMBINING GRAVE ACCENT BELOW;Mn;220;NSM;;;;;N;NON-SPACING GRAVE BELOW;;;;
+0317;COMBINING ACUTE ACCENT BELOW;Mn;220;NSM;;;;;N;NON-SPACING ACUTE BELOW;;;;
+0318;COMBINING LEFT TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING LEFT TACK BELOW;;;;
+0319;COMBINING RIGHT TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING RIGHT TACK BELOW;;;;
+031A;COMBINING LEFT ANGLE ABOVE;Mn;232;NSM;;;;;N;NON-SPACING LEFT ANGLE ABOVE;;;;
+031B;COMBINING HORN;Mn;216;NSM;;;;;N;NON-SPACING HORN;;;;
+031C;COMBINING LEFT HALF RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING LEFT HALF RING BELOW;;;;
+031D;COMBINING UP TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING UP TACK BELOW;;;;
+031E;COMBINING DOWN TACK BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOWN TACK BELOW;;;;
+031F;COMBINING PLUS SIGN BELOW;Mn;220;NSM;;;;;N;NON-SPACING PLUS SIGN BELOW;;;;
+0320;COMBINING MINUS SIGN BELOW;Mn;220;NSM;;;;;N;NON-SPACING MINUS SIGN BELOW;;;;
+0321;COMBINING PALATALIZED HOOK BELOW;Mn;202;NSM;;;;;N;NON-SPACING PALATALIZED HOOK BELOW;;;;
+0322;COMBINING RETROFLEX HOOK BELOW;Mn;202;NSM;;;;;N;NON-SPACING RETROFLEX HOOK BELOW;;;;
+0323;COMBINING DOT BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOT BELOW;;;;
+0324;COMBINING DIAERESIS BELOW;Mn;220;NSM;;;;;N;NON-SPACING DOUBLE DOT BELOW;;;;
+0325;COMBINING RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING RING BELOW;;;;
+0326;COMBINING COMMA BELOW;Mn;220;NSM;;;;;N;NON-SPACING COMMA BELOW;;;;
+0327;COMBINING CEDILLA;Mn;202;NSM;;;;;N;NON-SPACING CEDILLA;;;;
+0328;COMBINING OGONEK;Mn;202;NSM;;;;;N;NON-SPACING OGONEK;;;;
+0329;COMBINING VERTICAL LINE BELOW;Mn;220;NSM;;;;;N;NON-SPACING VERTICAL LINE BELOW;;;;
+032A;COMBINING BRIDGE BELOW;Mn;220;NSM;;;;;N;NON-SPACING BRIDGE BELOW;;;;
+032B;COMBINING INVERTED DOUBLE ARCH BELOW;Mn;220;NSM;;;;;N;NON-SPACING INVERTED DOUBLE ARCH BELOW;;;;
+032C;COMBINING CARON BELOW;Mn;220;NSM;;;;;N;NON-SPACING HACEK BELOW;;;;
+032D;COMBINING CIRCUMFLEX ACCENT BELOW;Mn;220;NSM;;;;;N;NON-SPACING CIRCUMFLEX BELOW;;;;
+032E;COMBINING BREVE BELOW;Mn;220;NSM;;;;;N;NON-SPACING BREVE BELOW;;;;
+032F;COMBINING INVERTED BREVE BELOW;Mn;220;NSM;;;;;N;NON-SPACING INVERTED BREVE BELOW;;;;
+0330;COMBINING TILDE BELOW;Mn;220;NSM;;;;;N;NON-SPACING TILDE BELOW;;;;
+0331;COMBINING MACRON BELOW;Mn;220;NSM;;;;;N;NON-SPACING MACRON BELOW;;;;
+0332;COMBINING LOW LINE;Mn;220;NSM;;;;;N;NON-SPACING UNDERSCORE;;;;
+0333;COMBINING DOUBLE LOW LINE;Mn;220;NSM;;;;;N;NON-SPACING DOUBLE UNDERSCORE;;;;
+0334;COMBINING TILDE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING TILDE OVERLAY;;;;
+0335;COMBINING SHORT STROKE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING SHORT BAR OVERLAY;;;;
+0336;COMBINING LONG STROKE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG BAR OVERLAY;;;;
+0337;COMBINING SHORT SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING SHORT SLASH OVERLAY;;;;
+0338;COMBINING LONG SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG SLASH OVERLAY;;;;
+0339;COMBINING RIGHT HALF RING BELOW;Mn;220;NSM;;;;;N;NON-SPACING RIGHT HALF RING BELOW;;;;
+033A;COMBINING INVERTED BRIDGE BELOW;Mn;220;NSM;;;;;N;NON-SPACING INVERTED BRIDGE BELOW;;;;
+033B;COMBINING SQUARE BELOW;Mn;220;NSM;;;;;N;NON-SPACING SQUARE BELOW;;;;
+033C;COMBINING SEAGULL BELOW;Mn;220;NSM;;;;;N;NON-SPACING SEAGULL BELOW;;;;
+033D;COMBINING X ABOVE;Mn;230;NSM;;;;;N;NON-SPACING X ABOVE;;;;
+033E;COMBINING VERTICAL TILDE;Mn;230;NSM;;;;;N;NON-SPACING VERTICAL TILDE;;;;
+033F;COMBINING DOUBLE OVERLINE;Mn;230;NSM;;;;;N;NON-SPACING DOUBLE OVERSCORE;;;;
+0340;COMBINING GRAVE TONE MARK;Mn;230;NSM;0300;;;;N;NON-SPACING GRAVE TONE MARK;Vietnamese;;;
+0341;COMBINING ACUTE TONE MARK;Mn;230;NSM;0301;;;;N;NON-SPACING ACUTE TONE MARK;Vietnamese;;;
+0342;COMBINING GREEK PERISPOMENI;Mn;230;NSM;;;;;N;;;;;
+0343;COMBINING GREEK KORONIS;Mn;230;NSM;0313;;;;N;;;;;
+0344;COMBINING GREEK DIALYTIKA TONOS;Mn;230;NSM;0308 0301;;;;N;GREEK NON-SPACING DIAERESIS TONOS;;;;
+0345;COMBINING GREEK YPOGEGRAMMENI;Mn;240;NSM;;;;;N;GREEK NON-SPACING IOTA BELOW;;0399;;0399
+0346;COMBINING BRIDGE ABOVE;Mn;230;NSM;;;;;N;;;;;
+0347;COMBINING EQUALS SIGN BELOW;Mn;220;NSM;;;;;N;;;;;
+0348;COMBINING DOUBLE VERTICAL LINE BELOW;Mn;220;NSM;;;;;N;;;;;
+0349;COMBINING LEFT ANGLE BELOW;Mn;220;NSM;;;;;N;;;;;
+034A;COMBINING NOT TILDE ABOVE;Mn;230;NSM;;;;;N;;;;;
+034B;COMBINING HOMOTHETIC ABOVE;Mn;230;NSM;;;;;N;;;;;
+034C;COMBINING ALMOST EQUAL TO ABOVE;Mn;230;NSM;;;;;N;;;;;
+034D;COMBINING LEFT RIGHT ARROW BELOW;Mn;220;NSM;;;;;N;;;;;
+034E;COMBINING UPWARDS ARROW BELOW;Mn;220;NSM;;;;;N;;;;;
+034F;COMBINING GRAPHEME JOINER;Mn;0;NSM;;;;;N;;;;;
+0360;COMBINING DOUBLE TILDE;Mn;234;NSM;;;;;N;;;;;
+0361;COMBINING DOUBLE INVERTED BREVE;Mn;234;NSM;;;;;N;;;;;
+0362;COMBINING DOUBLE RIGHTWARDS ARROW BELOW;Mn;233;NSM;;;;;N;;;;;
+0363;COMBINING LATIN SMALL LETTER A;Mn;230;NSM;;;;;N;;;;;
+0364;COMBINING LATIN SMALL LETTER E;Mn;230;NSM;;;;;N;;;;;
+0365;COMBINING LATIN SMALL LETTER I;Mn;230;NSM;;;;;N;;;;;
+0366;COMBINING LATIN SMALL LETTER O;Mn;230;NSM;;;;;N;;;;;
+0367;COMBINING LATIN SMALL LETTER U;Mn;230;NSM;;;;;N;;;;;
+0368;COMBINING LATIN SMALL LETTER C;Mn;230;NSM;;;;;N;;;;;
+0369;COMBINING LATIN SMALL LETTER D;Mn;230;NSM;;;;;N;;;;;
+036A;COMBINING LATIN SMALL LETTER H;Mn;230;NSM;;;;;N;;;;;
+036B;COMBINING LATIN SMALL LETTER M;Mn;230;NSM;;;;;N;;;;;
+036C;COMBINING LATIN SMALL LETTER R;Mn;230;NSM;;;;;N;;;;;
+036D;COMBINING LATIN SMALL LETTER T;Mn;230;NSM;;;;;N;;;;;
+036E;COMBINING LATIN SMALL LETTER V;Mn;230;NSM;;;;;N;;;;;
+036F;COMBINING LATIN SMALL LETTER X;Mn;230;NSM;;;;;N;;;;;
+0374;GREEK NUMERAL SIGN;Sk;0;ON;02B9;;;;N;GREEK UPPER NUMERAL SIGN;Dexia keraia;;;
+0375;GREEK LOWER NUMERAL SIGN;Sk;0;ON;;;;;N;;Aristeri keraia;;;
+037A;GREEK YPOGEGRAMMENI;Lm;0;L;<compat> 0020 0345;;;;N;GREEK SPACING IOTA BELOW;;;;
+037E;GREEK QUESTION MARK;Po;0;ON;003B;;;;N;;Erotimatiko;;;
+0384;GREEK TONOS;Sk;0;ON;<compat> 0020 0301;;;;N;GREEK SPACING TONOS;;;;
+0385;GREEK DIALYTIKA TONOS;Sk;0;ON;00A8 0301;;;;N;GREEK SPACING DIAERESIS TONOS;;;;
+0386;GREEK CAPITAL LETTER ALPHA WITH TONOS;Lu;0;L;0391 0301;;;;N;GREEK CAPITAL LETTER ALPHA TONOS;;;03AC;
+0387;GREEK ANO TELEIA;Po;0;ON;00B7;;;;N;;;;;
+0388;GREEK CAPITAL LETTER EPSILON WITH TONOS;Lu;0;L;0395 0301;;;;N;GREEK CAPITAL LETTER EPSILON TONOS;;;03AD;
+0389;GREEK CAPITAL LETTER ETA WITH TONOS;Lu;0;L;0397 0301;;;;N;GREEK CAPITAL LETTER ETA TONOS;;;03AE;
+038A;GREEK CAPITAL LETTER IOTA WITH TONOS;Lu;0;L;0399 0301;;;;N;GREEK CAPITAL LETTER IOTA TONOS;;;03AF;
+038C;GREEK CAPITAL LETTER OMICRON WITH TONOS;Lu;0;L;039F 0301;;;;N;GREEK CAPITAL LETTER OMICRON TONOS;;;03CC;
+038E;GREEK CAPITAL LETTER UPSILON WITH TONOS;Lu;0;L;03A5 0301;;;;N;GREEK CAPITAL LETTER UPSILON TONOS;;;03CD;
+038F;GREEK CAPITAL LETTER OMEGA WITH TONOS;Lu;0;L;03A9 0301;;;;N;GREEK CAPITAL LETTER OMEGA TONOS;;;03CE;
+0390;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS;Ll;0;L;03CA 0301;;;;N;GREEK SMALL LETTER IOTA DIAERESIS TONOS;;;;
+0391;GREEK CAPITAL LETTER ALPHA;Lu;0;L;;;;;N;;;;03B1;
+0392;GREEK CAPITAL LETTER BETA;Lu;0;L;;;;;N;;;;03B2;
+0393;GREEK CAPITAL LETTER GAMMA;Lu;0;L;;;;;N;;;;03B3;
+0394;GREEK CAPITAL LETTER DELTA;Lu;0;L;;;;;N;;;;03B4;
+0395;GREEK CAPITAL LETTER EPSILON;Lu;0;L;;;;;N;;;;03B5;
+0396;GREEK CAPITAL LETTER ZETA;Lu;0;L;;;;;N;;;;03B6;
+0397;GREEK CAPITAL LETTER ETA;Lu;0;L;;;;;N;;;;03B7;
+0398;GREEK CAPITAL LETTER THETA;Lu;0;L;;;;;N;;;;03B8;
+0399;GREEK CAPITAL LETTER IOTA;Lu;0;L;;;;;N;;;;03B9;
+039A;GREEK CAPITAL LETTER KAPPA;Lu;0;L;;;;;N;;;;03BA;
+039B;GREEK CAPITAL LETTER LAMDA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER LAMBDA;;;03BB;
+039C;GREEK CAPITAL LETTER MU;Lu;0;L;;;;;N;;;;03BC;
+039D;GREEK CAPITAL LETTER NU;Lu;0;L;;;;;N;;;;03BD;
+039E;GREEK CAPITAL LETTER XI;Lu;0;L;;;;;N;;;;03BE;
+039F;GREEK CAPITAL LETTER OMICRON;Lu;0;L;;;;;N;;;;03BF;
+03A0;GREEK CAPITAL LETTER PI;Lu;0;L;;;;;N;;;;03C0;
+03A1;GREEK CAPITAL LETTER RHO;Lu;0;L;;;;;N;;;;03C1;
+03A3;GREEK CAPITAL LETTER SIGMA;Lu;0;L;;;;;N;;;;03C3;
+03A4;GREEK CAPITAL LETTER TAU;Lu;0;L;;;;;N;;;;03C4;
+03A5;GREEK CAPITAL LETTER UPSILON;Lu;0;L;;;;;N;;;;03C5;
+03A6;GREEK CAPITAL LETTER PHI;Lu;0;L;;;;;N;;;;03C6;
+03A7;GREEK CAPITAL LETTER CHI;Lu;0;L;;;;;N;;;;03C7;
+03A8;GREEK CAPITAL LETTER PSI;Lu;0;L;;;;;N;;;;03C8;
+03A9;GREEK CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;03C9;
+03AA;GREEK CAPITAL LETTER IOTA WITH DIALYTIKA;Lu;0;L;0399 0308;;;;N;GREEK CAPITAL LETTER IOTA DIAERESIS;;;03CA;
+03AB;GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA;Lu;0;L;03A5 0308;;;;N;GREEK CAPITAL LETTER UPSILON DIAERESIS;;;03CB;
+03AC;GREEK SMALL LETTER ALPHA WITH TONOS;Ll;0;L;03B1 0301;;;;N;GREEK SMALL LETTER ALPHA TONOS;;0386;;0386
+03AD;GREEK SMALL LETTER EPSILON WITH TONOS;Ll;0;L;03B5 0301;;;;N;GREEK SMALL LETTER EPSILON TONOS;;0388;;0388
+03AE;GREEK SMALL LETTER ETA WITH TONOS;Ll;0;L;03B7 0301;;;;N;GREEK SMALL LETTER ETA TONOS;;0389;;0389
+03AF;GREEK SMALL LETTER IOTA WITH TONOS;Ll;0;L;03B9 0301;;;;N;GREEK SMALL LETTER IOTA TONOS;;038A;;038A
+03B0;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS;Ll;0;L;03CB 0301;;;;N;GREEK SMALL LETTER UPSILON DIAERESIS TONOS;;;;
+03B1;GREEK SMALL LETTER ALPHA;Ll;0;L;;;;;N;;;0391;;0391
+03B2;GREEK SMALL LETTER BETA;Ll;0;L;;;;;N;;;0392;;0392
+03B3;GREEK SMALL LETTER GAMMA;Ll;0;L;;;;;N;;;0393;;0393
+03B4;GREEK SMALL LETTER DELTA;Ll;0;L;;;;;N;;;0394;;0394
+03B5;GREEK SMALL LETTER EPSILON;Ll;0;L;;;;;N;;;0395;;0395
+03B6;GREEK SMALL LETTER ZETA;Ll;0;L;;;;;N;;;0396;;0396
+03B7;GREEK SMALL LETTER ETA;Ll;0;L;;;;;N;;;0397;;0397
+03B8;GREEK SMALL LETTER THETA;Ll;0;L;;;;;N;;;0398;;0398
+03B9;GREEK SMALL LETTER IOTA;Ll;0;L;;;;;N;;;0399;;0399
+03BA;GREEK SMALL LETTER KAPPA;Ll;0;L;;;;;N;;;039A;;039A
+03BB;GREEK SMALL LETTER LAMDA;Ll;0;L;;;;;N;GREEK SMALL LETTER LAMBDA;;039B;;039B
+03BC;GREEK SMALL LETTER MU;Ll;0;L;;;;;N;;;039C;;039C
+03BD;GREEK SMALL LETTER NU;Ll;0;L;;;;;N;;;039D;;039D
+03BE;GREEK SMALL LETTER XI;Ll;0;L;;;;;N;;;039E;;039E
+03BF;GREEK SMALL LETTER OMICRON;Ll;0;L;;;;;N;;;039F;;039F
+03C0;GREEK SMALL LETTER PI;Ll;0;L;;;;;N;;;03A0;;03A0
+03C1;GREEK SMALL LETTER RHO;Ll;0;L;;;;;N;;;03A1;;03A1
+03C2;GREEK SMALL LETTER FINAL SIGMA;Ll;0;L;;;;;N;;;03A3;;03A3
+03C3;GREEK SMALL LETTER SIGMA;Ll;0;L;;;;;N;;;03A3;;03A3
+03C4;GREEK SMALL LETTER TAU;Ll;0;L;;;;;N;;;03A4;;03A4
+03C5;GREEK SMALL LETTER UPSILON;Ll;0;L;;;;;N;;;03A5;;03A5
+03C6;GREEK SMALL LETTER PHI;Ll;0;L;;;;;N;;;03A6;;03A6
+03C7;GREEK SMALL LETTER CHI;Ll;0;L;;;;;N;;;03A7;;03A7
+03C8;GREEK SMALL LETTER PSI;Ll;0;L;;;;;N;;;03A8;;03A8
+03C9;GREEK SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;03A9;;03A9
+03CA;GREEK SMALL LETTER IOTA WITH DIALYTIKA;Ll;0;L;03B9 0308;;;;N;GREEK SMALL LETTER IOTA DIAERESIS;;03AA;;03AA
+03CB;GREEK SMALL LETTER UPSILON WITH DIALYTIKA;Ll;0;L;03C5 0308;;;;N;GREEK SMALL LETTER UPSILON DIAERESIS;;03AB;;03AB
+03CC;GREEK SMALL LETTER OMICRON WITH TONOS;Ll;0;L;03BF 0301;;;;N;GREEK SMALL LETTER OMICRON TONOS;;038C;;038C
+03CD;GREEK SMALL LETTER UPSILON WITH TONOS;Ll;0;L;03C5 0301;;;;N;GREEK SMALL LETTER UPSILON TONOS;;038E;;038E
+03CE;GREEK SMALL LETTER OMEGA WITH TONOS;Ll;0;L;03C9 0301;;;;N;GREEK SMALL LETTER OMEGA TONOS;;038F;;038F
+03D0;GREEK BETA SYMBOL;Ll;0;L;<compat> 03B2;;;;N;GREEK SMALL LETTER CURLED BETA;;0392;;0392
+03D1;GREEK THETA SYMBOL;Ll;0;L;<compat> 03B8;;;;N;GREEK SMALL LETTER SCRIPT THETA;;0398;;0398
+03D2;GREEK UPSILON WITH HOOK SYMBOL;Lu;0;L;<compat> 03A5;;;;N;GREEK CAPITAL LETTER UPSILON HOOK;;;;
+03D3;GREEK UPSILON WITH ACUTE AND HOOK SYMBOL;Lu;0;L;03D2 0301;;;;N;GREEK CAPITAL LETTER UPSILON HOOK TONOS;;;;
+03D4;GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL;Lu;0;L;03D2 0308;;;;N;GREEK CAPITAL LETTER UPSILON HOOK DIAERESIS;;;;
+03D5;GREEK PHI SYMBOL;Ll;0;L;<compat> 03C6;;;;N;GREEK SMALL LETTER SCRIPT PHI;;03A6;;03A6
+03D6;GREEK PI SYMBOL;Ll;0;L;<compat> 03C0;;;;N;GREEK SMALL LETTER OMEGA PI;;03A0;;03A0
+03D7;GREEK KAI SYMBOL;Ll;0;L;;;;;N;;;;;
+03D8;GREEK LETTER ARCHAIC KOPPA;Lu;0;L;;;;;N;;*;;03D9;
+03D9;GREEK SMALL LETTER ARCHAIC KOPPA;Ll;0;L;;;;;N;;*;03D8;;03D8
+03DA;GREEK LETTER STIGMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER STIGMA;;;03DB;
+03DB;GREEK SMALL LETTER STIGMA;Ll;0;L;;;;;N;;;03DA;;03DA
+03DC;GREEK LETTER DIGAMMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER DIGAMMA;;;03DD;
+03DD;GREEK SMALL LETTER DIGAMMA;Ll;0;L;;;;;N;;;03DC;;03DC
+03DE;GREEK LETTER KOPPA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER KOPPA;;;03DF;
+03DF;GREEK SMALL LETTER KOPPA;Ll;0;L;;;;;N;;;03DE;;03DE
+03E0;GREEK LETTER SAMPI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SAMPI;;;03E1;
+03E1;GREEK SMALL LETTER SAMPI;Ll;0;L;;;;;N;;;03E0;;03E0
+03E2;COPTIC CAPITAL LETTER SHEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SHEI;;;03E3;
+03E3;COPTIC SMALL LETTER SHEI;Ll;0;L;;;;;N;GREEK SMALL LETTER SHEI;;03E2;;03E2
+03E4;COPTIC CAPITAL LETTER FEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER FEI;;;03E5;
+03E5;COPTIC SMALL LETTER FEI;Ll;0;L;;;;;N;GREEK SMALL LETTER FEI;;03E4;;03E4
+03E6;COPTIC CAPITAL LETTER KHEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER KHEI;;;03E7;
+03E7;COPTIC SMALL LETTER KHEI;Ll;0;L;;;;;N;GREEK SMALL LETTER KHEI;;03E6;;03E6
+03E8;COPTIC CAPITAL LETTER HORI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER HORI;;;03E9;
+03E9;COPTIC SMALL LETTER HORI;Ll;0;L;;;;;N;GREEK SMALL LETTER HORI;;03E8;;03E8
+03EA;COPTIC CAPITAL LETTER GANGIA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER GANGIA;;;03EB;
+03EB;COPTIC SMALL LETTER GANGIA;Ll;0;L;;;;;N;GREEK SMALL LETTER GANGIA;;03EA;;03EA
+03EC;COPTIC CAPITAL LETTER SHIMA;Lu;0;L;;;;;N;GREEK CAPITAL LETTER SHIMA;;;03ED;
+03ED;COPTIC SMALL LETTER SHIMA;Ll;0;L;;;;;N;GREEK SMALL LETTER SHIMA;;03EC;;03EC
+03EE;COPTIC CAPITAL LETTER DEI;Lu;0;L;;;;;N;GREEK CAPITAL LETTER DEI;;;03EF;
+03EF;COPTIC SMALL LETTER DEI;Ll;0;L;;;;;N;GREEK SMALL LETTER DEI;;03EE;;03EE
+03F0;GREEK KAPPA SYMBOL;Ll;0;L;<compat> 03BA;;;;N;GREEK SMALL LETTER SCRIPT KAPPA;;039A;;039A
+03F1;GREEK RHO SYMBOL;Ll;0;L;<compat> 03C1;;;;N;GREEK SMALL LETTER TAILED RHO;;03A1;;03A1
+03F2;GREEK LUNATE SIGMA SYMBOL;Ll;0;L;<compat> 03C2;;;;N;GREEK SMALL LETTER LUNATE SIGMA;;03A3;;03A3
+03F3;GREEK LETTER YOT;Ll;0;L;;;;;N;;;;;
+03F4;GREEK CAPITAL THETA SYMBOL;Lu;0;L;<compat> 0398;;;;N;;;;03B8;
+03F5;GREEK LUNATE EPSILON SYMBOL;Ll;0;L;<compat> 03B5;;;;N;;;0395;;0395
+03F6;GREEK REVERSED LUNATE EPSILON SYMBOL;Sm;0;ON;;;;;N;;;;;
+0400;CYRILLIC CAPITAL LETTER IE WITH GRAVE;Lu;0;L;0415 0300;;;;N;;;;0450;
+0401;CYRILLIC CAPITAL LETTER IO;Lu;0;L;0415 0308;;;;N;;;;0451;
+0402;CYRILLIC CAPITAL LETTER DJE;Lu;0;L;;;;;N;;Serbocroatian;;0452;
+0403;CYRILLIC CAPITAL LETTER GJE;Lu;0;L;0413 0301;;;;N;;;;0453;
+0404;CYRILLIC CAPITAL LETTER UKRAINIAN IE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER E;;;0454;
+0405;CYRILLIC CAPITAL LETTER DZE;Lu;0;L;;;;;N;;;;0455;
+0406;CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER I;;;0456;
+0407;CYRILLIC CAPITAL LETTER YI;Lu;0;L;0406 0308;;;;N;;Ukrainian;;0457;
+0408;CYRILLIC CAPITAL LETTER JE;Lu;0;L;;;;;N;;;;0458;
+0409;CYRILLIC CAPITAL LETTER LJE;Lu;0;L;;;;;N;;;;0459;
+040A;CYRILLIC CAPITAL LETTER NJE;Lu;0;L;;;;;N;;;;045A;
+040B;CYRILLIC CAPITAL LETTER TSHE;Lu;0;L;;;;;N;;Serbocroatian;;045B;
+040C;CYRILLIC CAPITAL LETTER KJE;Lu;0;L;041A 0301;;;;N;;;;045C;
+040D;CYRILLIC CAPITAL LETTER I WITH GRAVE;Lu;0;L;0418 0300;;;;N;;;;045D;
+040E;CYRILLIC CAPITAL LETTER SHORT U;Lu;0;L;0423 0306;;;;N;;Byelorussian;;045E;
+040F;CYRILLIC CAPITAL LETTER DZHE;Lu;0;L;;;;;N;;;;045F;
+0410;CYRILLIC CAPITAL LETTER A;Lu;0;L;;;;;N;;;;0430;
+0411;CYRILLIC CAPITAL LETTER BE;Lu;0;L;;;;;N;;;;0431;
+0412;CYRILLIC CAPITAL LETTER VE;Lu;0;L;;;;;N;;;;0432;
+0413;CYRILLIC CAPITAL LETTER GHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE;;;0433;
+0414;CYRILLIC CAPITAL LETTER DE;Lu;0;L;;;;;N;;;;0434;
+0415;CYRILLIC CAPITAL LETTER IE;Lu;0;L;;;;;N;;;;0435;
+0416;CYRILLIC CAPITAL LETTER ZHE;Lu;0;L;;;;;N;;;;0436;
+0417;CYRILLIC CAPITAL LETTER ZE;Lu;0;L;;;;;N;;;;0437;
+0418;CYRILLIC CAPITAL LETTER I;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER II;;;0438;
+0419;CYRILLIC CAPITAL LETTER SHORT I;Lu;0;L;0418 0306;;;;N;CYRILLIC CAPITAL LETTER SHORT II;;;0439;
+041A;CYRILLIC CAPITAL LETTER KA;Lu;0;L;;;;;N;;;;043A;
+041B;CYRILLIC CAPITAL LETTER EL;Lu;0;L;;;;;N;;;;043B;
+041C;CYRILLIC CAPITAL LETTER EM;Lu;0;L;;;;;N;;;;043C;
+041D;CYRILLIC CAPITAL LETTER EN;Lu;0;L;;;;;N;;;;043D;
+041E;CYRILLIC CAPITAL LETTER O;Lu;0;L;;;;;N;;;;043E;
+041F;CYRILLIC CAPITAL LETTER PE;Lu;0;L;;;;;N;;;;043F;
+0420;CYRILLIC CAPITAL LETTER ER;Lu;0;L;;;;;N;;;;0440;
+0421;CYRILLIC CAPITAL LETTER ES;Lu;0;L;;;;;N;;;;0441;
+0422;CYRILLIC CAPITAL LETTER TE;Lu;0;L;;;;;N;;;;0442;
+0423;CYRILLIC CAPITAL LETTER U;Lu;0;L;;;;;N;;;;0443;
+0424;CYRILLIC CAPITAL LETTER EF;Lu;0;L;;;;;N;;;;0444;
+0425;CYRILLIC CAPITAL LETTER HA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KHA;;;0445;
+0426;CYRILLIC CAPITAL LETTER TSE;Lu;0;L;;;;;N;;;;0446;
+0427;CYRILLIC CAPITAL LETTER CHE;Lu;0;L;;;;;N;;;;0447;
+0428;CYRILLIC CAPITAL LETTER SHA;Lu;0;L;;;;;N;;;;0448;
+0429;CYRILLIC CAPITAL LETTER SHCHA;Lu;0;L;;;;;N;;;;0449;
+042A;CYRILLIC CAPITAL LETTER HARD SIGN;Lu;0;L;;;;;N;;;;044A;
+042B;CYRILLIC CAPITAL LETTER YERU;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER YERI;;;044B;
+042C;CYRILLIC CAPITAL LETTER SOFT SIGN;Lu;0;L;;;;;N;;;;044C;
+042D;CYRILLIC CAPITAL LETTER E;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER REVERSED E;;;044D;
+042E;CYRILLIC CAPITAL LETTER YU;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IU;;;044E;
+042F;CYRILLIC CAPITAL LETTER YA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IA;;;044F;
+0430;CYRILLIC SMALL LETTER A;Ll;0;L;;;;;N;;;0410;;0410
+0431;CYRILLIC SMALL LETTER BE;Ll;0;L;;;;;N;;;0411;;0411
+0432;CYRILLIC SMALL LETTER VE;Ll;0;L;;;;;N;;;0412;;0412
+0433;CYRILLIC SMALL LETTER GHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE;;0413;;0413
+0434;CYRILLIC SMALL LETTER DE;Ll;0;L;;;;;N;;;0414;;0414
+0435;CYRILLIC SMALL LETTER IE;Ll;0;L;;;;;N;;;0415;;0415
+0436;CYRILLIC SMALL LETTER ZHE;Ll;0;L;;;;;N;;;0416;;0416
+0437;CYRILLIC SMALL LETTER ZE;Ll;0;L;;;;;N;;;0417;;0417
+0438;CYRILLIC SMALL LETTER I;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER II;;0418;;0418
+0439;CYRILLIC SMALL LETTER SHORT I;Ll;0;L;0438 0306;;;;N;CYRILLIC SMALL LETTER SHORT II;;0419;;0419
+043A;CYRILLIC SMALL LETTER KA;Ll;0;L;;;;;N;;;041A;;041A
+043B;CYRILLIC SMALL LETTER EL;Ll;0;L;;;;;N;;;041B;;041B
+043C;CYRILLIC SMALL LETTER EM;Ll;0;L;;;;;N;;;041C;;041C
+043D;CYRILLIC SMALL LETTER EN;Ll;0;L;;;;;N;;;041D;;041D
+043E;CYRILLIC SMALL LETTER O;Ll;0;L;;;;;N;;;041E;;041E
+043F;CYRILLIC SMALL LETTER PE;Ll;0;L;;;;;N;;;041F;;041F
+0440;CYRILLIC SMALL LETTER ER;Ll;0;L;;;;;N;;;0420;;0420
+0441;CYRILLIC SMALL LETTER ES;Ll;0;L;;;;;N;;;0421;;0421
+0442;CYRILLIC SMALL LETTER TE;Ll;0;L;;;;;N;;;0422;;0422
+0443;CYRILLIC SMALL LETTER U;Ll;0;L;;;;;N;;;0423;;0423
+0444;CYRILLIC SMALL LETTER EF;Ll;0;L;;;;;N;;;0424;;0424
+0445;CYRILLIC SMALL LETTER HA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KHA;;0425;;0425
+0446;CYRILLIC SMALL LETTER TSE;Ll;0;L;;;;;N;;;0426;;0426
+0447;CYRILLIC SMALL LETTER CHE;Ll;0;L;;;;;N;;;0427;;0427
+0448;CYRILLIC SMALL LETTER SHA;Ll;0;L;;;;;N;;;0428;;0428
+0449;CYRILLIC SMALL LETTER SHCHA;Ll;0;L;;;;;N;;;0429;;0429
+044A;CYRILLIC SMALL LETTER HARD SIGN;Ll;0;L;;;;;N;;;042A;;042A
+044B;CYRILLIC SMALL LETTER YERU;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER YERI;;042B;;042B
+044C;CYRILLIC SMALL LETTER SOFT SIGN;Ll;0;L;;;;;N;;;042C;;042C
+044D;CYRILLIC SMALL LETTER E;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER REVERSED E;;042D;;042D
+044E;CYRILLIC SMALL LETTER YU;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IU;;042E;;042E
+044F;CYRILLIC SMALL LETTER YA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IA;;042F;;042F
+0450;CYRILLIC SMALL LETTER IE WITH GRAVE;Ll;0;L;0435 0300;;;;N;;;0400;;0400
+0451;CYRILLIC SMALL LETTER IO;Ll;0;L;0435 0308;;;;N;;;0401;;0401
+0452;CYRILLIC SMALL LETTER DJE;Ll;0;L;;;;;N;;Serbocroatian;0402;;0402
+0453;CYRILLIC SMALL LETTER GJE;Ll;0;L;0433 0301;;;;N;;;0403;;0403
+0454;CYRILLIC SMALL LETTER UKRAINIAN IE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER E;;0404;;0404
+0455;CYRILLIC SMALL LETTER DZE;Ll;0;L;;;;;N;;;0405;;0405
+0456;CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER I;;0406;;0406
+0457;CYRILLIC SMALL LETTER YI;Ll;0;L;0456 0308;;;;N;;Ukrainian;0407;;0407
+0458;CYRILLIC SMALL LETTER JE;Ll;0;L;;;;;N;;;0408;;0408
+0459;CYRILLIC SMALL LETTER LJE;Ll;0;L;;;;;N;;;0409;;0409
+045A;CYRILLIC SMALL LETTER NJE;Ll;0;L;;;;;N;;;040A;;040A
+045B;CYRILLIC SMALL LETTER TSHE;Ll;0;L;;;;;N;;Serbocroatian;040B;;040B
+045C;CYRILLIC SMALL LETTER KJE;Ll;0;L;043A 0301;;;;N;;;040C;;040C
+045D;CYRILLIC SMALL LETTER I WITH GRAVE;Ll;0;L;0438 0300;;;;N;;;040D;;040D
+045E;CYRILLIC SMALL LETTER SHORT U;Ll;0;L;0443 0306;;;;N;;Byelorussian;040E;;040E
+045F;CYRILLIC SMALL LETTER DZHE;Ll;0;L;;;;;N;;;040F;;040F
+0460;CYRILLIC CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;0461;
+0461;CYRILLIC SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;0460;;0460
+0462;CYRILLIC CAPITAL LETTER YAT;Lu;0;L;;;;;N;;;;0463;
+0463;CYRILLIC SMALL LETTER YAT;Ll;0;L;;;;;N;;;0462;;0462
+0464;CYRILLIC CAPITAL LETTER IOTIFIED E;Lu;0;L;;;;;N;;;;0465;
+0465;CYRILLIC SMALL LETTER IOTIFIED E;Ll;0;L;;;;;N;;;0464;;0464
+0466;CYRILLIC CAPITAL LETTER LITTLE YUS;Lu;0;L;;;;;N;;;;0467;
+0467;CYRILLIC SMALL LETTER LITTLE YUS;Ll;0;L;;;;;N;;;0466;;0466
+0468;CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS;Lu;0;L;;;;;N;;;;0469;
+0469;CYRILLIC SMALL LETTER IOTIFIED LITTLE YUS;Ll;0;L;;;;;N;;;0468;;0468
+046A;CYRILLIC CAPITAL LETTER BIG YUS;Lu;0;L;;;;;N;;;;046B;
+046B;CYRILLIC SMALL LETTER BIG YUS;Ll;0;L;;;;;N;;;046A;;046A
+046C;CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS;Lu;0;L;;;;;N;;;;046D;
+046D;CYRILLIC SMALL LETTER IOTIFIED BIG YUS;Ll;0;L;;;;;N;;;046C;;046C
+046E;CYRILLIC CAPITAL LETTER KSI;Lu;0;L;;;;;N;;;;046F;
+046F;CYRILLIC SMALL LETTER KSI;Ll;0;L;;;;;N;;;046E;;046E
+0470;CYRILLIC CAPITAL LETTER PSI;Lu;0;L;;;;;N;;;;0471;
+0471;CYRILLIC SMALL LETTER PSI;Ll;0;L;;;;;N;;;0470;;0470
+0472;CYRILLIC CAPITAL LETTER FITA;Lu;0;L;;;;;N;;;;0473;
+0473;CYRILLIC SMALL LETTER FITA;Ll;0;L;;;;;N;;;0472;;0472
+0474;CYRILLIC CAPITAL LETTER IZHITSA;Lu;0;L;;;;;N;;;;0475;
+0475;CYRILLIC SMALL LETTER IZHITSA;Ll;0;L;;;;;N;;;0474;;0474
+0476;CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT;Lu;0;L;0474 030F;;;;N;CYRILLIC CAPITAL LETTER IZHITSA DOUBLE GRAVE;;;0477;
+0477;CYRILLIC SMALL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT;Ll;0;L;0475 030F;;;;N;CYRILLIC SMALL LETTER IZHITSA DOUBLE GRAVE;;0476;;0476
+0478;CYRILLIC CAPITAL LETTER UK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER UK DIGRAPH;;;0479;
+0479;CYRILLIC SMALL LETTER UK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER UK DIGRAPH;;0478;;0478
+047A;CYRILLIC CAPITAL LETTER ROUND OMEGA;Lu;0;L;;;;;N;;;;047B;
+047B;CYRILLIC SMALL LETTER ROUND OMEGA;Ll;0;L;;;;;N;;;047A;;047A
+047C;CYRILLIC CAPITAL LETTER OMEGA WITH TITLO;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER OMEGA TITLO;;;047D;
+047D;CYRILLIC SMALL LETTER OMEGA WITH TITLO;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER OMEGA TITLO;;047C;;047C
+047E;CYRILLIC CAPITAL LETTER OT;Lu;0;L;;;;;N;;;;047F;
+047F;CYRILLIC SMALL LETTER OT;Ll;0;L;;;;;N;;;047E;;047E
+0480;CYRILLIC CAPITAL LETTER KOPPA;Lu;0;L;;;;;N;;;;0481;
+0481;CYRILLIC SMALL LETTER KOPPA;Ll;0;L;;;;;N;;;0480;;0480
+0482;CYRILLIC THOUSANDS SIGN;So;0;L;;;;;N;;;;;
+0483;COMBINING CYRILLIC TITLO;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING TITLO;;;;
+0484;COMBINING CYRILLIC PALATALIZATION;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING PALATALIZATION;;;;
+0485;COMBINING CYRILLIC DASIA PNEUMATA;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING DASIA PNEUMATA;;;;
+0486;COMBINING CYRILLIC PSILI PNEUMATA;Mn;230;NSM;;;;;N;CYRILLIC NON-SPACING PSILI PNEUMATA;;;;
+0488;COMBINING CYRILLIC HUNDRED THOUSANDS SIGN;Me;0;NSM;;;;;N;;;;;
+0489;COMBINING CYRILLIC MILLIONS SIGN;Me;0;NSM;;;;;N;;;;;
+048A;CYRILLIC CAPITAL LETTER SHORT I WITH TAIL;Lu;0;L;;;;;N;;;;048B;
+048B;CYRILLIC SMALL LETTER SHORT I WITH TAIL;Ll;0;L;;;;;N;;;048A;;048A
+048C;CYRILLIC CAPITAL LETTER SEMISOFT SIGN;Lu;0;L;;;;;N;;;;048D;
+048D;CYRILLIC SMALL LETTER SEMISOFT SIGN;Ll;0;L;;;;;N;;;048C;;048C
+048E;CYRILLIC CAPITAL LETTER ER WITH TICK;Lu;0;L;;;;;N;;;;048F;
+048F;CYRILLIC SMALL LETTER ER WITH TICK;Ll;0;L;;;;;N;;;048E;;048E
+0490;CYRILLIC CAPITAL LETTER GHE WITH UPTURN;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE WITH UPTURN;;;0491;
+0491;CYRILLIC SMALL LETTER GHE WITH UPTURN;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE WITH UPTURN;;0490;;0490
+0492;CYRILLIC CAPITAL LETTER GHE WITH STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE BAR;;;0493;
+0493;CYRILLIC SMALL LETTER GHE WITH STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE BAR;;0492;;0492
+0494;CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER GE HOOK;;;0495;
+0495;CYRILLIC SMALL LETTER GHE WITH MIDDLE HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER GE HOOK;;0494;;0494
+0496;CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER ZHE WITH RIGHT DESCENDER;;;0497;
+0497;CYRILLIC SMALL LETTER ZHE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER ZHE WITH RIGHT DESCENDER;;0496;;0496
+0498;CYRILLIC CAPITAL LETTER ZE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER ZE CEDILLA;;;0499;
+0499;CYRILLIC SMALL LETTER ZE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER ZE CEDILLA;;0498;;0498
+049A;CYRILLIC CAPITAL LETTER KA WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA WITH RIGHT DESCENDER;;;049B;
+049B;CYRILLIC SMALL LETTER KA WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA WITH RIGHT DESCENDER;;049A;;049A
+049C;CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA VERTICAL BAR;;;049D;
+049D;CYRILLIC SMALL LETTER KA WITH VERTICAL STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA VERTICAL BAR;;049C;;049C
+049E;CYRILLIC CAPITAL LETTER KA WITH STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA BAR;;;049F;
+049F;CYRILLIC SMALL LETTER KA WITH STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA BAR;;049E;;049E
+04A0;CYRILLIC CAPITAL LETTER BASHKIR KA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER REVERSED GE KA;;;04A1;
+04A1;CYRILLIC SMALL LETTER BASHKIR KA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER REVERSED GE KA;;04A0;;04A0
+04A2;CYRILLIC CAPITAL LETTER EN WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER EN WITH RIGHT DESCENDER;;;04A3;
+04A3;CYRILLIC SMALL LETTER EN WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER EN WITH RIGHT DESCENDER;;04A2;;04A2
+04A4;CYRILLIC CAPITAL LIGATURE EN GHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER EN GE;;;04A5;
+04A5;CYRILLIC SMALL LIGATURE EN GHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER EN GE;;04A4;;04A4
+04A6;CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER PE HOOK;Abkhasian;;04A7;
+04A7;CYRILLIC SMALL LETTER PE WITH MIDDLE HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER PE HOOK;Abkhasian;04A6;;04A6
+04A8;CYRILLIC CAPITAL LETTER ABKHASIAN HA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER O HOOK;;;04A9;
+04A9;CYRILLIC SMALL LETTER ABKHASIAN HA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER O HOOK;;04A8;;04A8
+04AA;CYRILLIC CAPITAL LETTER ES WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER ES CEDILLA;;;04AB;
+04AB;CYRILLIC SMALL LETTER ES WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER ES CEDILLA;;04AA;;04AA
+04AC;CYRILLIC CAPITAL LETTER TE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER TE WITH RIGHT DESCENDER;;;04AD;
+04AD;CYRILLIC SMALL LETTER TE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER TE WITH RIGHT DESCENDER;;04AC;;04AC
+04AE;CYRILLIC CAPITAL LETTER STRAIGHT U;Lu;0;L;;;;;N;;;;04AF;
+04AF;CYRILLIC SMALL LETTER STRAIGHT U;Ll;0;L;;;;;N;;;04AE;;04AE
+04B0;CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER STRAIGHT U BAR;;;04B1;
+04B1;CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER STRAIGHT U BAR;;04B0;;04B0
+04B2;CYRILLIC CAPITAL LETTER HA WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KHA WITH RIGHT DESCENDER;;;04B3;
+04B3;CYRILLIC SMALL LETTER HA WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KHA WITH RIGHT DESCENDER;;04B2;;04B2
+04B4;CYRILLIC CAPITAL LIGATURE TE TSE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER TE TSE;Abkhasian;;04B5;
+04B5;CYRILLIC SMALL LIGATURE TE TSE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER TE TSE;Abkhasian;04B4;;04B4
+04B6;CYRILLIC CAPITAL LETTER CHE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER CHE WITH RIGHT DESCENDER;;;04B7;
+04B7;CYRILLIC SMALL LETTER CHE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER CHE WITH RIGHT DESCENDER;;04B6;;04B6
+04B8;CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER CHE VERTICAL BAR;;;04B9;
+04B9;CYRILLIC SMALL LETTER CHE WITH VERTICAL STROKE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER CHE VERTICAL BAR;;04B8;;04B8
+04BA;CYRILLIC CAPITAL LETTER SHHA;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER H;;;04BB;
+04BB;CYRILLIC SMALL LETTER SHHA;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER H;;04BA;;04BA
+04BC;CYRILLIC CAPITAL LETTER ABKHASIAN CHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IE HOOK;;;04BD;
+04BD;CYRILLIC SMALL LETTER ABKHASIAN CHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IE HOOK;;04BC;;04BC
+04BE;CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER IE HOOK OGONEK;;;04BF;
+04BF;CYRILLIC SMALL LETTER ABKHASIAN CHE WITH DESCENDER;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER IE HOOK OGONEK;;04BE;;04BE
+04C0;CYRILLIC LETTER PALOCHKA;Lu;0;L;;;;;N;CYRILLIC LETTER I;;;;
+04C1;CYRILLIC CAPITAL LETTER ZHE WITH BREVE;Lu;0;L;0416 0306;;;;N;CYRILLIC CAPITAL LETTER SHORT ZHE;;;04C2;
+04C2;CYRILLIC SMALL LETTER ZHE WITH BREVE;Ll;0;L;0436 0306;;;;N;CYRILLIC SMALL LETTER SHORT ZHE;;04C1;;04C1
+04C3;CYRILLIC CAPITAL LETTER KA WITH HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER KA HOOK;;;04C4;
+04C4;CYRILLIC SMALL LETTER KA WITH HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER KA HOOK;;04C3;;04C3
+04C5;CYRILLIC CAPITAL LETTER EL WITH TAIL;Lu;0;L;;;;;N;;;;04C6;
+04C6;CYRILLIC SMALL LETTER EL WITH TAIL;Ll;0;L;;;;;N;;;04C5;;04C5
+04C7;CYRILLIC CAPITAL LETTER EN WITH HOOK;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER EN HOOK;;;04C8;
+04C8;CYRILLIC SMALL LETTER EN WITH HOOK;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER EN HOOK;;04C7;;04C7
+04C9;CYRILLIC CAPITAL LETTER EN WITH TAIL;Lu;0;L;;;;;N;;;;04CA;
+04CA;CYRILLIC SMALL LETTER EN WITH TAIL;Ll;0;L;;;;;N;;;04C9;;04C9
+04CB;CYRILLIC CAPITAL LETTER KHAKASSIAN CHE;Lu;0;L;;;;;N;CYRILLIC CAPITAL LETTER CHE WITH LEFT DESCENDER;;;04CC;
+04CC;CYRILLIC SMALL LETTER KHAKASSIAN CHE;Ll;0;L;;;;;N;CYRILLIC SMALL LETTER CHE WITH LEFT DESCENDER;;04CB;;04CB
+04CD;CYRILLIC CAPITAL LETTER EM WITH TAIL;Lu;0;L;;;;;N;;;;04CE;
+04CE;CYRILLIC SMALL LETTER EM WITH TAIL;Ll;0;L;;;;;N;;;04CD;;04CD
+04D0;CYRILLIC CAPITAL LETTER A WITH BREVE;Lu;0;L;0410 0306;;;;N;;;;04D1;
+04D1;CYRILLIC SMALL LETTER A WITH BREVE;Ll;0;L;0430 0306;;;;N;;;04D0;;04D0
+04D2;CYRILLIC CAPITAL LETTER A WITH DIAERESIS;Lu;0;L;0410 0308;;;;N;;;;04D3;
+04D3;CYRILLIC SMALL LETTER A WITH DIAERESIS;Ll;0;L;0430 0308;;;;N;;;04D2;;04D2
+04D4;CYRILLIC CAPITAL LIGATURE A IE;Lu;0;L;;;;;N;;;;04D5;
+04D5;CYRILLIC SMALL LIGATURE A IE;Ll;0;L;;;;;N;;;04D4;;04D4
+04D6;CYRILLIC CAPITAL LETTER IE WITH BREVE;Lu;0;L;0415 0306;;;;N;;;;04D7;
+04D7;CYRILLIC SMALL LETTER IE WITH BREVE;Ll;0;L;0435 0306;;;;N;;;04D6;;04D6
+04D8;CYRILLIC CAPITAL LETTER SCHWA;Lu;0;L;;;;;N;;;;04D9;
+04D9;CYRILLIC SMALL LETTER SCHWA;Ll;0;L;;;;;N;;;04D8;;04D8
+04DA;CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS;Lu;0;L;04D8 0308;;;;N;;;;04DB;
+04DB;CYRILLIC SMALL LETTER SCHWA WITH DIAERESIS;Ll;0;L;04D9 0308;;;;N;;;04DA;;04DA
+04DC;CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS;Lu;0;L;0416 0308;;;;N;;;;04DD;
+04DD;CYRILLIC SMALL LETTER ZHE WITH DIAERESIS;Ll;0;L;0436 0308;;;;N;;;04DC;;04DC
+04DE;CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS;Lu;0;L;0417 0308;;;;N;;;;04DF;
+04DF;CYRILLIC SMALL LETTER ZE WITH DIAERESIS;Ll;0;L;0437 0308;;;;N;;;04DE;;04DE
+04E0;CYRILLIC CAPITAL LETTER ABKHASIAN DZE;Lu;0;L;;;;;N;;;;04E1;
+04E1;CYRILLIC SMALL LETTER ABKHASIAN DZE;Ll;0;L;;;;;N;;;04E0;;04E0
+04E2;CYRILLIC CAPITAL LETTER I WITH MACRON;Lu;0;L;0418 0304;;;;N;;;;04E3;
+04E3;CYRILLIC SMALL LETTER I WITH MACRON;Ll;0;L;0438 0304;;;;N;;;04E2;;04E2
+04E4;CYRILLIC CAPITAL LETTER I WITH DIAERESIS;Lu;0;L;0418 0308;;;;N;;;;04E5;
+04E5;CYRILLIC SMALL LETTER I WITH DIAERESIS;Ll;0;L;0438 0308;;;;N;;;04E4;;04E4
+04E6;CYRILLIC CAPITAL LETTER O WITH DIAERESIS;Lu;0;L;041E 0308;;;;N;;;;04E7;
+04E7;CYRILLIC SMALL LETTER O WITH DIAERESIS;Ll;0;L;043E 0308;;;;N;;;04E6;;04E6
+04E8;CYRILLIC CAPITAL LETTER BARRED O;Lu;0;L;;;;;N;;;;04E9;
+04E9;CYRILLIC SMALL LETTER BARRED O;Ll;0;L;;;;;N;;;04E8;;04E8
+04EA;CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS;Lu;0;L;04E8 0308;;;;N;;;;04EB;
+04EB;CYRILLIC SMALL LETTER BARRED O WITH DIAERESIS;Ll;0;L;04E9 0308;;;;N;;;04EA;;04EA
+04EC;CYRILLIC CAPITAL LETTER E WITH DIAERESIS;Lu;0;L;042D 0308;;;;N;;;;04ED;
+04ED;CYRILLIC SMALL LETTER E WITH DIAERESIS;Ll;0;L;044D 0308;;;;N;;;04EC;;04EC
+04EE;CYRILLIC CAPITAL LETTER U WITH MACRON;Lu;0;L;0423 0304;;;;N;;;;04EF;
+04EF;CYRILLIC SMALL LETTER U WITH MACRON;Ll;0;L;0443 0304;;;;N;;;04EE;;04EE
+04F0;CYRILLIC CAPITAL LETTER U WITH DIAERESIS;Lu;0;L;0423 0308;;;;N;;;;04F1;
+04F1;CYRILLIC SMALL LETTER U WITH DIAERESIS;Ll;0;L;0443 0308;;;;N;;;04F0;;04F0
+04F2;CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE;Lu;0;L;0423 030B;;;;N;;;;04F3;
+04F3;CYRILLIC SMALL LETTER U WITH DOUBLE ACUTE;Ll;0;L;0443 030B;;;;N;;;04F2;;04F2
+04F4;CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS;Lu;0;L;0427 0308;;;;N;;;;04F5;
+04F5;CYRILLIC SMALL LETTER CHE WITH DIAERESIS;Ll;0;L;0447 0308;;;;N;;;04F4;;04F4
+04F8;CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS;Lu;0;L;042B 0308;;;;N;;;;04F9;
+04F9;CYRILLIC SMALL LETTER YERU WITH DIAERESIS;Ll;0;L;044B 0308;;;;N;;;04F8;;04F8
+0500;CYRILLIC CAPITAL LETTER KOMI DE;Lu;0;L;;;;;N;;;;0501;
+0501;CYRILLIC SMALL LETTER KOMI DE;Ll;0;L;;;;;N;;;0500;;0500
+0502;CYRILLIC CAPITAL LETTER KOMI DJE;Lu;0;L;;;;;N;;;;0503;
+0503;CYRILLIC SMALL LETTER KOMI DJE;Ll;0;L;;;;;N;;;0502;;0502
+0504;CYRILLIC CAPITAL LETTER KOMI ZJE;Lu;0;L;;;;;N;;;;0505;
+0505;CYRILLIC SMALL LETTER KOMI ZJE;Ll;0;L;;;;;N;;;0504;;0504
+0506;CYRILLIC CAPITAL LETTER KOMI DZJE;Lu;0;L;;;;;N;;;;0507;
+0507;CYRILLIC SMALL LETTER KOMI DZJE;Ll;0;L;;;;;N;;;0506;;0506
+0508;CYRILLIC CAPITAL LETTER KOMI LJE;Lu;0;L;;;;;N;;;;0509;
+0509;CYRILLIC SMALL LETTER KOMI LJE;Ll;0;L;;;;;N;;;0508;;0508
+050A;CYRILLIC CAPITAL LETTER KOMI NJE;Lu;0;L;;;;;N;;;;050B;
+050B;CYRILLIC SMALL LETTER KOMI NJE;Ll;0;L;;;;;N;;;050A;;050A
+050C;CYRILLIC CAPITAL LETTER KOMI SJE;Lu;0;L;;;;;N;;;;050D;
+050D;CYRILLIC SMALL LETTER KOMI SJE;Ll;0;L;;;;;N;;;050C;;050C
+050E;CYRILLIC CAPITAL LETTER KOMI TJE;Lu;0;L;;;;;N;;;;050F;
+050F;CYRILLIC SMALL LETTER KOMI TJE;Ll;0;L;;;;;N;;;050E;;050E
+0531;ARMENIAN CAPITAL LETTER AYB;Lu;0;L;;;;;N;;;;0561;
+0532;ARMENIAN CAPITAL LETTER BEN;Lu;0;L;;;;;N;;;;0562;
+0533;ARMENIAN CAPITAL LETTER GIM;Lu;0;L;;;;;N;;;;0563;
+0534;ARMENIAN CAPITAL LETTER DA;Lu;0;L;;;;;N;;;;0564;
+0535;ARMENIAN CAPITAL LETTER ECH;Lu;0;L;;;;;N;;;;0565;
+0536;ARMENIAN CAPITAL LETTER ZA;Lu;0;L;;;;;N;;;;0566;
+0537;ARMENIAN CAPITAL LETTER EH;Lu;0;L;;;;;N;;;;0567;
+0538;ARMENIAN CAPITAL LETTER ET;Lu;0;L;;;;;N;;;;0568;
+0539;ARMENIAN CAPITAL LETTER TO;Lu;0;L;;;;;N;;;;0569;
+053A;ARMENIAN CAPITAL LETTER ZHE;Lu;0;L;;;;;N;;;;056A;
+053B;ARMENIAN CAPITAL LETTER INI;Lu;0;L;;;;;N;;;;056B;
+053C;ARMENIAN CAPITAL LETTER LIWN;Lu;0;L;;;;;N;;;;056C;
+053D;ARMENIAN CAPITAL LETTER XEH;Lu;0;L;;;;;N;;;;056D;
+053E;ARMENIAN CAPITAL LETTER CA;Lu;0;L;;;;;N;;;;056E;
+053F;ARMENIAN CAPITAL LETTER KEN;Lu;0;L;;;;;N;;;;056F;
+0540;ARMENIAN CAPITAL LETTER HO;Lu;0;L;;;;;N;;;;0570;
+0541;ARMENIAN CAPITAL LETTER JA;Lu;0;L;;;;;N;;;;0571;
+0542;ARMENIAN CAPITAL LETTER GHAD;Lu;0;L;;;;;N;ARMENIAN CAPITAL LETTER LAD;;;0572;
+0543;ARMENIAN CAPITAL LETTER CHEH;Lu;0;L;;;;;N;;;;0573;
+0544;ARMENIAN CAPITAL LETTER MEN;Lu;0;L;;;;;N;;;;0574;
+0545;ARMENIAN CAPITAL LETTER YI;Lu;0;L;;;;;N;;;;0575;
+0546;ARMENIAN CAPITAL LETTER NOW;Lu;0;L;;;;;N;;;;0576;
+0547;ARMENIAN CAPITAL LETTER SHA;Lu;0;L;;;;;N;;;;0577;
+0548;ARMENIAN CAPITAL LETTER VO;Lu;0;L;;;;;N;;;;0578;
+0549;ARMENIAN CAPITAL LETTER CHA;Lu;0;L;;;;;N;;;;0579;
+054A;ARMENIAN CAPITAL LETTER PEH;Lu;0;L;;;;;N;;;;057A;
+054B;ARMENIAN CAPITAL LETTER JHEH;Lu;0;L;;;;;N;;;;057B;
+054C;ARMENIAN CAPITAL LETTER RA;Lu;0;L;;;;;N;;;;057C;
+054D;ARMENIAN CAPITAL LETTER SEH;Lu;0;L;;;;;N;;;;057D;
+054E;ARMENIAN CAPITAL LETTER VEW;Lu;0;L;;;;;N;;;;057E;
+054F;ARMENIAN CAPITAL LETTER TIWN;Lu;0;L;;;;;N;;;;057F;
+0550;ARMENIAN CAPITAL LETTER REH;Lu;0;L;;;;;N;;;;0580;
+0551;ARMENIAN CAPITAL LETTER CO;Lu;0;L;;;;;N;;;;0581;
+0552;ARMENIAN CAPITAL LETTER YIWN;Lu;0;L;;;;;N;;;;0582;
+0553;ARMENIAN CAPITAL LETTER PIWR;Lu;0;L;;;;;N;;;;0583;
+0554;ARMENIAN CAPITAL LETTER KEH;Lu;0;L;;;;;N;;;;0584;
+0555;ARMENIAN CAPITAL LETTER OH;Lu;0;L;;;;;N;;;;0585;
+0556;ARMENIAN CAPITAL LETTER FEH;Lu;0;L;;;;;N;;;;0586;
+0559;ARMENIAN MODIFIER LETTER LEFT HALF RING;Lm;0;L;;;;;N;;;;;
+055A;ARMENIAN APOSTROPHE;Po;0;L;;;;;N;ARMENIAN MODIFIER LETTER RIGHT HALF RING;;;;
+055B;ARMENIAN EMPHASIS MARK;Po;0;L;;;;;N;;;;;
+055C;ARMENIAN EXCLAMATION MARK;Po;0;L;;;;;N;;;;;
+055D;ARMENIAN COMMA;Po;0;L;;;;;N;;;;;
+055E;ARMENIAN QUESTION MARK;Po;0;L;;;;;N;;;;;
+055F;ARMENIAN ABBREVIATION MARK;Po;0;L;;;;;N;;;;;
+0561;ARMENIAN SMALL LETTER AYB;Ll;0;L;;;;;N;;;0531;;0531
+0562;ARMENIAN SMALL LETTER BEN;Ll;0;L;;;;;N;;;0532;;0532
+0563;ARMENIAN SMALL LETTER GIM;Ll;0;L;;;;;N;;;0533;;0533
+0564;ARMENIAN SMALL LETTER DA;Ll;0;L;;;;;N;;;0534;;0534
+0565;ARMENIAN SMALL LETTER ECH;Ll;0;L;;;;;N;;;0535;;0535
+0566;ARMENIAN SMALL LETTER ZA;Ll;0;L;;;;;N;;;0536;;0536
+0567;ARMENIAN SMALL LETTER EH;Ll;0;L;;;;;N;;;0537;;0537
+0568;ARMENIAN SMALL LETTER ET;Ll;0;L;;;;;N;;;0538;;0538
+0569;ARMENIAN SMALL LETTER TO;Ll;0;L;;;;;N;;;0539;;0539
+056A;ARMENIAN SMALL LETTER ZHE;Ll;0;L;;;;;N;;;053A;;053A
+056B;ARMENIAN SMALL LETTER INI;Ll;0;L;;;;;N;;;053B;;053B
+056C;ARMENIAN SMALL LETTER LIWN;Ll;0;L;;;;;N;;;053C;;053C
+056D;ARMENIAN SMALL LETTER XEH;Ll;0;L;;;;;N;;;053D;;053D
+056E;ARMENIAN SMALL LETTER CA;Ll;0;L;;;;;N;;;053E;;053E
+056F;ARMENIAN SMALL LETTER KEN;Ll;0;L;;;;;N;;;053F;;053F
+0570;ARMENIAN SMALL LETTER HO;Ll;0;L;;;;;N;;;0540;;0540
+0571;ARMENIAN SMALL LETTER JA;Ll;0;L;;;;;N;;;0541;;0541
+0572;ARMENIAN SMALL LETTER GHAD;Ll;0;L;;;;;N;ARMENIAN SMALL LETTER LAD;;0542;;0542
+0573;ARMENIAN SMALL LETTER CHEH;Ll;0;L;;;;;N;;;0543;;0543
+0574;ARMENIAN SMALL LETTER MEN;Ll;0;L;;;;;N;;;0544;;0544
+0575;ARMENIAN SMALL LETTER YI;Ll;0;L;;;;;N;;;0545;;0545
+0576;ARMENIAN SMALL LETTER NOW;Ll;0;L;;;;;N;;;0546;;0546
+0577;ARMENIAN SMALL LETTER SHA;Ll;0;L;;;;;N;;;0547;;0547
+0578;ARMENIAN SMALL LETTER VO;Ll;0;L;;;;;N;;;0548;;0548
+0579;ARMENIAN SMALL LETTER CHA;Ll;0;L;;;;;N;;;0549;;0549
+057A;ARMENIAN SMALL LETTER PEH;Ll;0;L;;;;;N;;;054A;;054A
+057B;ARMENIAN SMALL LETTER JHEH;Ll;0;L;;;;;N;;;054B;;054B
+057C;ARMENIAN SMALL LETTER RA;Ll;0;L;;;;;N;;;054C;;054C
+057D;ARMENIAN SMALL LETTER SEH;Ll;0;L;;;;;N;;;054D;;054D
+057E;ARMENIAN SMALL LETTER VEW;Ll;0;L;;;;;N;;;054E;;054E
+057F;ARMENIAN SMALL LETTER TIWN;Ll;0;L;;;;;N;;;054F;;054F
+0580;ARMENIAN SMALL LETTER REH;Ll;0;L;;;;;N;;;0550;;0550
+0581;ARMENIAN SMALL LETTER CO;Ll;0;L;;;;;N;;;0551;;0551
+0582;ARMENIAN SMALL LETTER YIWN;Ll;0;L;;;;;N;;;0552;;0552
+0583;ARMENIAN SMALL LETTER PIWR;Ll;0;L;;;;;N;;;0553;;0553
+0584;ARMENIAN SMALL LETTER KEH;Ll;0;L;;;;;N;;;0554;;0554
+0585;ARMENIAN SMALL LETTER OH;Ll;0;L;;;;;N;;;0555;;0555
+0586;ARMENIAN SMALL LETTER FEH;Ll;0;L;;;;;N;;;0556;;0556
+0587;ARMENIAN SMALL LIGATURE ECH YIWN;Ll;0;L;<compat> 0565 0582;;;;N;;;;;
+0589;ARMENIAN FULL STOP;Po;0;L;;;;;N;ARMENIAN PERIOD;;;;
+058A;ARMENIAN HYPHEN;Pd;0;ON;;;;;N;;;;;
+0591;HEBREW ACCENT ETNAHTA;Mn;220;NSM;;;;;N;;;;;
+0592;HEBREW ACCENT SEGOL;Mn;230;NSM;;;;;N;;;;;
+0593;HEBREW ACCENT SHALSHELET;Mn;230;NSM;;;;;N;;;;;
+0594;HEBREW ACCENT ZAQEF QATAN;Mn;230;NSM;;;;;N;;;;;
+0595;HEBREW ACCENT ZAQEF GADOL;Mn;230;NSM;;;;;N;;;;;
+0596;HEBREW ACCENT TIPEHA;Mn;220;NSM;;;;;N;;*;;;
+0597;HEBREW ACCENT REVIA;Mn;230;NSM;;;;;N;;;;;
+0598;HEBREW ACCENT ZARQA;Mn;230;NSM;;;;;N;;*;;;
+0599;HEBREW ACCENT PASHTA;Mn;230;NSM;;;;;N;;;;;
+059A;HEBREW ACCENT YETIV;Mn;222;NSM;;;;;N;;;;;
+059B;HEBREW ACCENT TEVIR;Mn;220;NSM;;;;;N;;;;;
+059C;HEBREW ACCENT GERESH;Mn;230;NSM;;;;;N;;;;;
+059D;HEBREW ACCENT GERESH MUQDAM;Mn;230;NSM;;;;;N;;;;;
+059E;HEBREW ACCENT GERSHAYIM;Mn;230;NSM;;;;;N;;;;;
+059F;HEBREW ACCENT QARNEY PARA;Mn;230;NSM;;;;;N;;;;;
+05A0;HEBREW ACCENT TELISHA GEDOLA;Mn;230;NSM;;;;;N;;;;;
+05A1;HEBREW ACCENT PAZER;Mn;230;NSM;;;;;N;;;;;
+05A3;HEBREW ACCENT MUNAH;Mn;220;NSM;;;;;N;;;;;
+05A4;HEBREW ACCENT MAHAPAKH;Mn;220;NSM;;;;;N;;;;;
+05A5;HEBREW ACCENT MERKHA;Mn;220;NSM;;;;;N;;*;;;
+05A6;HEBREW ACCENT MERKHA KEFULA;Mn;220;NSM;;;;;N;;;;;
+05A7;HEBREW ACCENT DARGA;Mn;220;NSM;;;;;N;;;;;
+05A8;HEBREW ACCENT QADMA;Mn;230;NSM;;;;;N;;*;;;
+05A9;HEBREW ACCENT TELISHA QETANA;Mn;230;NSM;;;;;N;;;;;
+05AA;HEBREW ACCENT YERAH BEN YOMO;Mn;220;NSM;;;;;N;;*;;;
+05AB;HEBREW ACCENT OLE;Mn;230;NSM;;;;;N;;;;;
+05AC;HEBREW ACCENT ILUY;Mn;230;NSM;;;;;N;;;;;
+05AD;HEBREW ACCENT DEHI;Mn;222;NSM;;;;;N;;;;;
+05AE;HEBREW ACCENT ZINOR;Mn;228;NSM;;;;;N;;;;;
+05AF;HEBREW MARK MASORA CIRCLE;Mn;230;NSM;;;;;N;;;;;
+05B0;HEBREW POINT SHEVA;Mn;10;NSM;;;;;N;;;;;
+05B1;HEBREW POINT HATAF SEGOL;Mn;11;NSM;;;;;N;;;;;
+05B2;HEBREW POINT HATAF PATAH;Mn;12;NSM;;;;;N;;;;;
+05B3;HEBREW POINT HATAF QAMATS;Mn;13;NSM;;;;;N;;;;;
+05B4;HEBREW POINT HIRIQ;Mn;14;NSM;;;;;N;;;;;
+05B5;HEBREW POINT TSERE;Mn;15;NSM;;;;;N;;;;;
+05B6;HEBREW POINT SEGOL;Mn;16;NSM;;;;;N;;;;;
+05B7;HEBREW POINT PATAH;Mn;17;NSM;;;;;N;;;;;
+05B8;HEBREW POINT QAMATS;Mn;18;NSM;;;;;N;;;;;
+05B9;HEBREW POINT HOLAM;Mn;19;NSM;;;;;N;;;;;
+05BB;HEBREW POINT QUBUTS;Mn;20;NSM;;;;;N;;;;;
+05BC;HEBREW POINT DAGESH OR MAPIQ;Mn;21;NSM;;;;;N;HEBREW POINT DAGESH;or shuruq;;;
+05BD;HEBREW POINT METEG;Mn;22;NSM;;;;;N;;*;;;
+05BE;HEBREW PUNCTUATION MAQAF;Po;0;R;;;;;N;;;;;
+05BF;HEBREW POINT RAFE;Mn;23;NSM;;;;;N;;;;;
+05C0;HEBREW PUNCTUATION PASEQ;Po;0;R;;;;;N;HEBREW POINT PASEQ;*;;;
+05C1;HEBREW POINT SHIN DOT;Mn;24;NSM;;;;;N;;;;;
+05C2;HEBREW POINT SIN DOT;Mn;25;NSM;;;;;N;;;;;
+05C3;HEBREW PUNCTUATION SOF PASUQ;Po;0;R;;;;;N;;*;;;
+05C4;HEBREW MARK UPPER DOT;Mn;230;NSM;;;;;N;;;;;
+05D0;HEBREW LETTER ALEF;Lo;0;R;;;;;N;;;;;
+05D1;HEBREW LETTER BET;Lo;0;R;;;;;N;;;;;
+05D2;HEBREW LETTER GIMEL;Lo;0;R;;;;;N;;;;;
+05D3;HEBREW LETTER DALET;Lo;0;R;;;;;N;;;;;
+05D4;HEBREW LETTER HE;Lo;0;R;;;;;N;;;;;
+05D5;HEBREW LETTER VAV;Lo;0;R;;;;;N;;;;;
+05D6;HEBREW LETTER ZAYIN;Lo;0;R;;;;;N;;;;;
+05D7;HEBREW LETTER HET;Lo;0;R;;;;;N;;;;;
+05D8;HEBREW LETTER TET;Lo;0;R;;;;;N;;;;;
+05D9;HEBREW LETTER YOD;Lo;0;R;;;;;N;;;;;
+05DA;HEBREW LETTER FINAL KAF;Lo;0;R;;;;;N;;;;;
+05DB;HEBREW LETTER KAF;Lo;0;R;;;;;N;;;;;
+05DC;HEBREW LETTER LAMED;Lo;0;R;;;;;N;;;;;
+05DD;HEBREW LETTER FINAL MEM;Lo;0;R;;;;;N;;;;;
+05DE;HEBREW LETTER MEM;Lo;0;R;;;;;N;;;;;
+05DF;HEBREW LETTER FINAL NUN;Lo;0;R;;;;;N;;;;;
+05E0;HEBREW LETTER NUN;Lo;0;R;;;;;N;;;;;
+05E1;HEBREW LETTER SAMEKH;Lo;0;R;;;;;N;;;;;
+05E2;HEBREW LETTER AYIN;Lo;0;R;;;;;N;;;;;
+05E3;HEBREW LETTER FINAL PE;Lo;0;R;;;;;N;;;;;
+05E4;HEBREW LETTER PE;Lo;0;R;;;;;N;;;;;
+05E5;HEBREW LETTER FINAL TSADI;Lo;0;R;;;;;N;;;;;
+05E6;HEBREW LETTER TSADI;Lo;0;R;;;;;N;;;;;
+05E7;HEBREW LETTER QOF;Lo;0;R;;;;;N;;;;;
+05E8;HEBREW LETTER RESH;Lo;0;R;;;;;N;;;;;
+05E9;HEBREW LETTER SHIN;Lo;0;R;;;;;N;;;;;
+05EA;HEBREW LETTER TAV;Lo;0;R;;;;;N;;;;;
+05F0;HEBREW LIGATURE YIDDISH DOUBLE VAV;Lo;0;R;;;;;N;HEBREW LETTER DOUBLE VAV;;;;
+05F1;HEBREW LIGATURE YIDDISH VAV YOD;Lo;0;R;;;;;N;HEBREW LETTER VAV YOD;;;;
+05F2;HEBREW LIGATURE YIDDISH DOUBLE YOD;Lo;0;R;;;;;N;HEBREW LETTER DOUBLE YOD;;;;
+05F3;HEBREW PUNCTUATION GERESH;Po;0;R;;;;;N;;;;;
+05F4;HEBREW PUNCTUATION GERSHAYIM;Po;0;R;;;;;N;;;;;
+060C;ARABIC COMMA;Po;0;CS;;;;;N;;;;;
+061B;ARABIC SEMICOLON;Po;0;AL;;;;;N;;;;;
+061F;ARABIC QUESTION MARK;Po;0;AL;;;;;N;;;;;
+0621;ARABIC LETTER HAMZA;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAH;;;;
+0622;ARABIC LETTER ALEF WITH MADDA ABOVE;Lo;0;AL;0627 0653;;;;N;ARABIC LETTER MADDAH ON ALEF;;;;
+0623;ARABIC LETTER ALEF WITH HAMZA ABOVE;Lo;0;AL;0627 0654;;;;N;ARABIC LETTER HAMZAH ON ALEF;;;;
+0624;ARABIC LETTER WAW WITH HAMZA ABOVE;Lo;0;AL;0648 0654;;;;N;ARABIC LETTER HAMZAH ON WAW;;;;
+0625;ARABIC LETTER ALEF WITH HAMZA BELOW;Lo;0;AL;0627 0655;;;;N;ARABIC LETTER HAMZAH UNDER ALEF;;;;
+0626;ARABIC LETTER YEH WITH HAMZA ABOVE;Lo;0;AL;064A 0654;;;;N;ARABIC LETTER HAMZAH ON YA;;;;
+0627;ARABIC LETTER ALEF;Lo;0;AL;;;;;N;;;;;
+0628;ARABIC LETTER BEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA;;;;
+0629;ARABIC LETTER TEH MARBUTA;Lo;0;AL;;;;;N;ARABIC LETTER TAA MARBUTAH;;;;
+062A;ARABIC LETTER TEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA;;;;
+062B;ARABIC LETTER THEH;Lo;0;AL;;;;;N;ARABIC LETTER THAA;;;;
+062C;ARABIC LETTER JEEM;Lo;0;AL;;;;;N;;;;;
+062D;ARABIC LETTER HAH;Lo;0;AL;;;;;N;ARABIC LETTER HAA;;;;
+062E;ARABIC LETTER KHAH;Lo;0;AL;;;;;N;ARABIC LETTER KHAA;;;;
+062F;ARABIC LETTER DAL;Lo;0;AL;;;;;N;;;;;
+0630;ARABIC LETTER THAL;Lo;0;AL;;;;;N;;;;;
+0631;ARABIC LETTER REH;Lo;0;AL;;;;;N;ARABIC LETTER RA;;;;
+0632;ARABIC LETTER ZAIN;Lo;0;AL;;;;;N;;;;;
+0633;ARABIC LETTER SEEN;Lo;0;AL;;;;;N;;;;;
+0634;ARABIC LETTER SHEEN;Lo;0;AL;;;;;N;;;;;
+0635;ARABIC LETTER SAD;Lo;0;AL;;;;;N;;;;;
+0636;ARABIC LETTER DAD;Lo;0;AL;;;;;N;;;;;
+0637;ARABIC LETTER TAH;Lo;0;AL;;;;;N;;;;;
+0638;ARABIC LETTER ZAH;Lo;0;AL;;;;;N;ARABIC LETTER DHAH;;;;
+0639;ARABIC LETTER AIN;Lo;0;AL;;;;;N;;;;;
+063A;ARABIC LETTER GHAIN;Lo;0;AL;;;;;N;;;;;
+0640;ARABIC TATWEEL;Lm;0;AL;;;;;N;;;;;
+0641;ARABIC LETTER FEH;Lo;0;AL;;;;;N;ARABIC LETTER FA;;;;
+0642;ARABIC LETTER QAF;Lo;0;AL;;;;;N;;;;;
+0643;ARABIC LETTER KAF;Lo;0;AL;;;;;N;ARABIC LETTER CAF;;;;
+0644;ARABIC LETTER LAM;Lo;0;AL;;;;;N;;;;;
+0645;ARABIC LETTER MEEM;Lo;0;AL;;;;;N;;;;;
+0646;ARABIC LETTER NOON;Lo;0;AL;;;;;N;;;;;
+0647;ARABIC LETTER HEH;Lo;0;AL;;;;;N;ARABIC LETTER HA;;;;
+0648;ARABIC LETTER WAW;Lo;0;AL;;;;;N;;;;;
+0649;ARABIC LETTER ALEF MAKSURA;Lo;0;AL;;;;;N;ARABIC LETTER ALEF MAQSURAH;;;;
+064A;ARABIC LETTER YEH;Lo;0;AL;;;;;N;ARABIC LETTER YA;;;;
+064B;ARABIC FATHATAN;Mn;27;NSM;;;;;N;;;;;
+064C;ARABIC DAMMATAN;Mn;28;NSM;;;;;N;;;;;
+064D;ARABIC KASRATAN;Mn;29;NSM;;;;;N;;;;;
+064E;ARABIC FATHA;Mn;30;NSM;;;;;N;ARABIC FATHAH;;;;
+064F;ARABIC DAMMA;Mn;31;NSM;;;;;N;ARABIC DAMMAH;;;;
+0650;ARABIC KASRA;Mn;32;NSM;;;;;N;ARABIC KASRAH;;;;
+0651;ARABIC SHADDA;Mn;33;NSM;;;;;N;ARABIC SHADDAH;;;;
+0652;ARABIC SUKUN;Mn;34;NSM;;;;;N;;;;;
+0653;ARABIC MADDAH ABOVE;Mn;230;NSM;;;;;N;;;;;
+0654;ARABIC HAMZA ABOVE;Mn;230;NSM;;;;;N;;;;;
+0655;ARABIC HAMZA BELOW;Mn;220;NSM;;;;;N;;;;;
+0660;ARABIC-INDIC DIGIT ZERO;Nd;0;AN;;0;0;0;N;;;;;
+0661;ARABIC-INDIC DIGIT ONE;Nd;0;AN;;1;1;1;N;;;;;
+0662;ARABIC-INDIC DIGIT TWO;Nd;0;AN;;2;2;2;N;;;;;
+0663;ARABIC-INDIC DIGIT THREE;Nd;0;AN;;3;3;3;N;;;;;
+0664;ARABIC-INDIC DIGIT FOUR;Nd;0;AN;;4;4;4;N;;;;;
+0665;ARABIC-INDIC DIGIT FIVE;Nd;0;AN;;5;5;5;N;;;;;
+0666;ARABIC-INDIC DIGIT SIX;Nd;0;AN;;6;6;6;N;;;;;
+0667;ARABIC-INDIC DIGIT SEVEN;Nd;0;AN;;7;7;7;N;;;;;
+0668;ARABIC-INDIC DIGIT EIGHT;Nd;0;AN;;8;8;8;N;;;;;
+0669;ARABIC-INDIC DIGIT NINE;Nd;0;AN;;9;9;9;N;;;;;
+066A;ARABIC PERCENT SIGN;Po;0;ET;;;;;N;;;;;
+066B;ARABIC DECIMAL SEPARATOR;Po;0;AN;;;;;N;;;;;
+066C;ARABIC THOUSANDS SEPARATOR;Po;0;AN;;;;;N;;;;;
+066D;ARABIC FIVE POINTED STAR;Po;0;AL;;;;;N;;;;;
+066E;ARABIC LETTER DOTLESS BEH;Lo;0;AL;;;;;N;;;;;
+066F;ARABIC LETTER DOTLESS QAF;Lo;0;AL;;;;;N;;;;;
+0670;ARABIC LETTER SUPERSCRIPT ALEF;Mn;35;NSM;;;;;N;ARABIC ALEF ABOVE;;;;
+0671;ARABIC LETTER ALEF WASLA;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAT WASL ON ALEF;;;;
+0672;ARABIC LETTER ALEF WITH WAVY HAMZA ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER WAVY HAMZAH ON ALEF;;;;
+0673;ARABIC LETTER ALEF WITH WAVY HAMZA BELOW;Lo;0;AL;;;;;N;ARABIC LETTER WAVY HAMZAH UNDER ALEF;;;;
+0674;ARABIC LETTER HIGH HAMZA;Lo;0;AL;;;;;N;ARABIC LETTER HIGH HAMZAH;;;;
+0675;ARABIC LETTER HIGH HAMZA ALEF;Lo;0;AL;<compat> 0627 0674;;;;N;ARABIC LETTER HIGH HAMZAH ALEF;;;;
+0676;ARABIC LETTER HIGH HAMZA WAW;Lo;0;AL;<compat> 0648 0674;;;;N;ARABIC LETTER HIGH HAMZAH WAW;;;;
+0677;ARABIC LETTER U WITH HAMZA ABOVE;Lo;0;AL;<compat> 06C7 0674;;;;N;ARABIC LETTER HIGH HAMZAH WAW WITH DAMMAH;;;;
+0678;ARABIC LETTER HIGH HAMZA YEH;Lo;0;AL;<compat> 064A 0674;;;;N;ARABIC LETTER HIGH HAMZAH YA;;;;
+0679;ARABIC LETTER TTEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH SMALL TAH;;;;
+067A;ARABIC LETTER TTEHEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH TWO DOTS VERTICAL ABOVE;;;;
+067B;ARABIC LETTER BEEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA WITH TWO DOTS VERTICAL BELOW;;;;
+067C;ARABIC LETTER TEH WITH RING;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH RING;;;;
+067D;ARABIC LETTER TEH WITH THREE DOTS ABOVE DOWNWARDS;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH THREE DOTS ABOVE DOWNWARD;;;;
+067E;ARABIC LETTER PEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH THREE DOTS BELOW;;;;
+067F;ARABIC LETTER TEHEH;Lo;0;AL;;;;;N;ARABIC LETTER TAA WITH FOUR DOTS ABOVE;;;;
+0680;ARABIC LETTER BEHEH;Lo;0;AL;;;;;N;ARABIC LETTER BAA WITH FOUR DOTS BELOW;;;;
+0681;ARABIC LETTER HAH WITH HAMZA ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER HAMZAH ON HAA;;;;
+0682;ARABIC LETTER HAH WITH TWO DOTS VERTICAL ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH TWO DOTS VERTICAL ABOVE;;;;
+0683;ARABIC LETTER NYEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE TWO DOTS;;;;
+0684;ARABIC LETTER DYEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE TWO DOTS VERTICAL;;;;
+0685;ARABIC LETTER HAH WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH THREE DOTS ABOVE;;;;
+0686;ARABIC LETTER TCHEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE THREE DOTS DOWNWARD;;;;
+0687;ARABIC LETTER TCHEHEH;Lo;0;AL;;;;;N;ARABIC LETTER HAA WITH MIDDLE FOUR DOTS;;;;
+0688;ARABIC LETTER DDAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH SMALL TAH;;;;
+0689;ARABIC LETTER DAL WITH RING;Lo;0;AL;;;;;N;;;;;
+068A;ARABIC LETTER DAL WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+068B;ARABIC LETTER DAL WITH DOT BELOW AND SMALL TAH;Lo;0;AL;;;;;N;;;;;
+068C;ARABIC LETTER DAHAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH TWO DOTS ABOVE;;;;
+068D;ARABIC LETTER DDAHAL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH TWO DOTS BELOW;;;;
+068E;ARABIC LETTER DUL;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH THREE DOTS ABOVE;;;;
+068F;ARABIC LETTER DAL WITH THREE DOTS ABOVE DOWNWARDS;Lo;0;AL;;;;;N;ARABIC LETTER DAL WITH THREE DOTS ABOVE DOWNWARD;;;;
+0690;ARABIC LETTER DAL WITH FOUR DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+0691;ARABIC LETTER RREH;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL TAH;;;;
+0692;ARABIC LETTER REH WITH SMALL V;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL V;;;;
+0693;ARABIC LETTER REH WITH RING;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH RING;;;;
+0694;ARABIC LETTER REH WITH DOT BELOW;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH DOT BELOW;;;;
+0695;ARABIC LETTER REH WITH SMALL V BELOW;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH SMALL V BELOW;;;;
+0696;ARABIC LETTER REH WITH DOT BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH DOT BELOW AND DOT ABOVE;;;;
+0697;ARABIC LETTER REH WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH TWO DOTS ABOVE;;;;
+0698;ARABIC LETTER JEH;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH THREE DOTS ABOVE;;;;
+0699;ARABIC LETTER REH WITH FOUR DOTS ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER RA WITH FOUR DOTS ABOVE;;;;
+069A;ARABIC LETTER SEEN WITH DOT BELOW AND DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+069B;ARABIC LETTER SEEN WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+069C;ARABIC LETTER SEEN WITH THREE DOTS BELOW AND THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+069D;ARABIC LETTER SAD WITH TWO DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+069E;ARABIC LETTER SAD WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+069F;ARABIC LETTER TAH WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06A0;ARABIC LETTER AIN WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06A1;ARABIC LETTER DOTLESS FEH;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS FA;;;;
+06A2;ARABIC LETTER FEH WITH DOT MOVED BELOW;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH DOT MOVED BELOW;;;;
+06A3;ARABIC LETTER FEH WITH DOT BELOW;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH DOT BELOW;;;;
+06A4;ARABIC LETTER VEH;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH THREE DOTS ABOVE;;;;
+06A5;ARABIC LETTER FEH WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH THREE DOTS BELOW;;;;
+06A6;ARABIC LETTER PEHEH;Lo;0;AL;;;;;N;ARABIC LETTER FA WITH FOUR DOTS ABOVE;;;;
+06A7;ARABIC LETTER QAF WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+06A8;ARABIC LETTER QAF WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06A9;ARABIC LETTER KEHEH;Lo;0;AL;;;;;N;ARABIC LETTER OPEN CAF;;;;
+06AA;ARABIC LETTER SWASH KAF;Lo;0;AL;;;;;N;ARABIC LETTER SWASH CAF;;;;
+06AB;ARABIC LETTER KAF WITH RING;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH RING;;;;
+06AC;ARABIC LETTER KAF WITH DOT ABOVE;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH DOT ABOVE;;;;
+06AD;ARABIC LETTER NG;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH THREE DOTS ABOVE;;;;
+06AE;ARABIC LETTER KAF WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;ARABIC LETTER CAF WITH THREE DOTS BELOW;;;;
+06AF;ARABIC LETTER GAF;Lo;0;AL;;;;;N;;*;;;
+06B0;ARABIC LETTER GAF WITH RING;Lo;0;AL;;;;;N;;;;;
+06B1;ARABIC LETTER NGOEH;Lo;0;AL;;;;;N;ARABIC LETTER GAF WITH TWO DOTS ABOVE;;;;
+06B2;ARABIC LETTER GAF WITH TWO DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+06B3;ARABIC LETTER GUEH;Lo;0;AL;;;;;N;ARABIC LETTER GAF WITH TWO DOTS VERTICAL BELOW;;;;
+06B4;ARABIC LETTER GAF WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06B5;ARABIC LETTER LAM WITH SMALL V;Lo;0;AL;;;;;N;;;;;
+06B6;ARABIC LETTER LAM WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+06B7;ARABIC LETTER LAM WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06B8;ARABIC LETTER LAM WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;;;;;
+06B9;ARABIC LETTER NOON WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+06BA;ARABIC LETTER NOON GHUNNA;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS NOON;;;;
+06BB;ARABIC LETTER RNOON;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS NOON WITH SMALL TAH;;;;
+06BC;ARABIC LETTER NOON WITH RING;Lo;0;AL;;;;;N;;;;;
+06BD;ARABIC LETTER NOON WITH THREE DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06BE;ARABIC LETTER HEH DOACHASHMEE;Lo;0;AL;;;;;N;ARABIC LETTER KNOTTED HA;;;;
+06BF;ARABIC LETTER TCHEH WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+06C0;ARABIC LETTER HEH WITH YEH ABOVE;Lo;0;AL;06D5 0654;;;;N;ARABIC LETTER HAMZAH ON HA;;;;
+06C1;ARABIC LETTER HEH GOAL;Lo;0;AL;;;;;N;ARABIC LETTER HA GOAL;;;;
+06C2;ARABIC LETTER HEH GOAL WITH HAMZA ABOVE;Lo;0;AL;06C1 0654;;;;N;ARABIC LETTER HAMZAH ON HA GOAL;;;;
+06C3;ARABIC LETTER TEH MARBUTA GOAL;Lo;0;AL;;;;;N;ARABIC LETTER TAA MARBUTAH GOAL;;;;
+06C4;ARABIC LETTER WAW WITH RING;Lo;0;AL;;;;;N;;;;;
+06C5;ARABIC LETTER KIRGHIZ OE;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH BAR;;;;
+06C6;ARABIC LETTER OE;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH SMALL V;;;;
+06C7;ARABIC LETTER U;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH DAMMAH;;;;
+06C8;ARABIC LETTER YU;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH ALEF ABOVE;;;;
+06C9;ARABIC LETTER KIRGHIZ YU;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH INVERTED SMALL V;;;;
+06CA;ARABIC LETTER WAW WITH TWO DOTS ABOVE;Lo;0;AL;;;;;N;;;;;
+06CB;ARABIC LETTER VE;Lo;0;AL;;;;;N;ARABIC LETTER WAW WITH THREE DOTS ABOVE;;;;
+06CC;ARABIC LETTER FARSI YEH;Lo;0;AL;;;;;N;ARABIC LETTER DOTLESS YA;;;;
+06CD;ARABIC LETTER YEH WITH TAIL;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH TAIL;;;;
+06CE;ARABIC LETTER YEH WITH SMALL V;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH SMALL V;;;;
+06CF;ARABIC LETTER WAW WITH DOT ABOVE;Lo;0;AL;;;;;N;;;;;
+06D0;ARABIC LETTER E;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH TWO DOTS VERTICAL BELOW;*;;;
+06D1;ARABIC LETTER YEH WITH THREE DOTS BELOW;Lo;0;AL;;;;;N;ARABIC LETTER YA WITH THREE DOTS BELOW;;;;
+06D2;ARABIC LETTER YEH BARREE;Lo;0;AL;;;;;N;ARABIC LETTER YA BARREE;;;;
+06D3;ARABIC LETTER YEH BARREE WITH HAMZA ABOVE;Lo;0;AL;06D2 0654;;;;N;ARABIC LETTER HAMZAH ON YA BARREE;;;;
+06D4;ARABIC FULL STOP;Po;0;AL;;;;;N;ARABIC PERIOD;;;;
+06D5;ARABIC LETTER AE;Lo;0;AL;;;;;N;;;;;
+06D6;ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA;Mn;230;NSM;;;;;N;;;;;
+06D7;ARABIC SMALL HIGH LIGATURE QAF WITH LAM WITH ALEF MAKSURA;Mn;230;NSM;;;;;N;;;;;
+06D8;ARABIC SMALL HIGH MEEM INITIAL FORM;Mn;230;NSM;;;;;N;;;;;
+06D9;ARABIC SMALL HIGH LAM ALEF;Mn;230;NSM;;;;;N;;;;;
+06DA;ARABIC SMALL HIGH JEEM;Mn;230;NSM;;;;;N;;;;;
+06DB;ARABIC SMALL HIGH THREE DOTS;Mn;230;NSM;;;;;N;;;;;
+06DC;ARABIC SMALL HIGH SEEN;Mn;230;NSM;;;;;N;;;;;
+06DD;ARABIC END OF AYAH;Cf;0;AL;;;;;N;;;;;
+06DE;ARABIC START OF RUB EL HIZB;Me;0;NSM;;;;;N;;;;;
+06DF;ARABIC SMALL HIGH ROUNDED ZERO;Mn;230;NSM;;;;;N;;;;;
+06E0;ARABIC SMALL HIGH UPRIGHT RECTANGULAR ZERO;Mn;230;NSM;;;;;N;;;;;
+06E1;ARABIC SMALL HIGH DOTLESS HEAD OF KHAH;Mn;230;NSM;;;;;N;;;;;
+06E2;ARABIC SMALL HIGH MEEM ISOLATED FORM;Mn;230;NSM;;;;;N;;;;;
+06E3;ARABIC SMALL LOW SEEN;Mn;220;NSM;;;;;N;;;;;
+06E4;ARABIC SMALL HIGH MADDA;Mn;230;NSM;;;;;N;;;;;
+06E5;ARABIC SMALL WAW;Lm;0;AL;;;;;N;;;;;
+06E6;ARABIC SMALL YEH;Lm;0;AL;;;;;N;;;;;
+06E7;ARABIC SMALL HIGH YEH;Mn;230;NSM;;;;;N;;;;;
+06E8;ARABIC SMALL HIGH NOON;Mn;230;NSM;;;;;N;;;;;
+06E9;ARABIC PLACE OF SAJDAH;So;0;ON;;;;;N;;;;;
+06EA;ARABIC EMPTY CENTRE LOW STOP;Mn;220;NSM;;;;;N;;;;;
+06EB;ARABIC EMPTY CENTRE HIGH STOP;Mn;230;NSM;;;;;N;;;;;
+06EC;ARABIC ROUNDED HIGH STOP WITH FILLED CENTRE;Mn;230;NSM;;;;;N;;;;;
+06ED;ARABIC SMALL LOW MEEM;Mn;220;NSM;;;;;N;;;;;
+06F0;EXTENDED ARABIC-INDIC DIGIT ZERO;Nd;0;EN;;0;0;0;N;EASTERN ARABIC-INDIC DIGIT ZERO;;;;
+06F1;EXTENDED ARABIC-INDIC DIGIT ONE;Nd;0;EN;;1;1;1;N;EASTERN ARABIC-INDIC DIGIT ONE;;;;
+06F2;EXTENDED ARABIC-INDIC DIGIT TWO;Nd;0;EN;;2;2;2;N;EASTERN ARABIC-INDIC DIGIT TWO;;;;
+06F3;EXTENDED ARABIC-INDIC DIGIT THREE;Nd;0;EN;;3;3;3;N;EASTERN ARABIC-INDIC DIGIT THREE;;;;
+06F4;EXTENDED ARABIC-INDIC DIGIT FOUR;Nd;0;EN;;4;4;4;N;EASTERN ARABIC-INDIC DIGIT FOUR;;;;
+06F5;EXTENDED ARABIC-INDIC DIGIT FIVE;Nd;0;EN;;5;5;5;N;EASTERN ARABIC-INDIC DIGIT FIVE;;;;
+06F6;EXTENDED ARABIC-INDIC DIGIT SIX;Nd;0;EN;;6;6;6;N;EASTERN ARABIC-INDIC DIGIT SIX;;;;
+06F7;EXTENDED ARABIC-INDIC DIGIT SEVEN;Nd;0;EN;;7;7;7;N;EASTERN ARABIC-INDIC DIGIT SEVEN;;;;
+06F8;EXTENDED ARABIC-INDIC DIGIT EIGHT;Nd;0;EN;;8;8;8;N;EASTERN ARABIC-INDIC DIGIT EIGHT;;;;
+06F9;EXTENDED ARABIC-INDIC DIGIT NINE;Nd;0;EN;;9;9;9;N;EASTERN ARABIC-INDIC DIGIT NINE;;;;
+06FA;ARABIC LETTER SHEEN WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+06FB;ARABIC LETTER DAD WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+06FC;ARABIC LETTER GHAIN WITH DOT BELOW;Lo;0;AL;;;;;N;;;;;
+06FD;ARABIC SIGN SINDHI AMPERSAND;So;0;AL;;;;;N;;;;;
+06FE;ARABIC SIGN SINDHI POSTPOSITION MEN;So;0;AL;;;;;N;;;;;
+0700;SYRIAC END OF PARAGRAPH;Po;0;AL;;;;;N;;;;;
+0701;SYRIAC SUPRALINEAR FULL STOP;Po;0;AL;;;;;N;;;;;
+0702;SYRIAC SUBLINEAR FULL STOP;Po;0;AL;;;;;N;;;;;
+0703;SYRIAC SUPRALINEAR COLON;Po;0;AL;;;;;N;;;;;
+0704;SYRIAC SUBLINEAR COLON;Po;0;AL;;;;;N;;;;;
+0705;SYRIAC HORIZONTAL COLON;Po;0;AL;;;;;N;;;;;
+0706;SYRIAC COLON SKEWED LEFT;Po;0;AL;;;;;N;;;;;
+0707;SYRIAC COLON SKEWED RIGHT;Po;0;AL;;;;;N;;;;;
+0708;SYRIAC SUPRALINEAR COLON SKEWED LEFT;Po;0;AL;;;;;N;;;;;
+0709;SYRIAC SUBLINEAR COLON SKEWED RIGHT;Po;0;AL;;;;;N;;;;;
+070A;SYRIAC CONTRACTION;Po;0;AL;;;;;N;;;;;
+070B;SYRIAC HARKLEAN OBELUS;Po;0;AL;;;;;N;;;;;
+070C;SYRIAC HARKLEAN METOBELUS;Po;0;AL;;;;;N;;;;;
+070D;SYRIAC HARKLEAN ASTERISCUS;Po;0;AL;;;;;N;;;;;
+070F;SYRIAC ABBREVIATION MARK;Cf;0;BN;;;;;N;;;;;
+0710;SYRIAC LETTER ALAPH;Lo;0;AL;;;;;N;;;;;
+0711;SYRIAC LETTER SUPERSCRIPT ALAPH;Mn;36;NSM;;;;;N;;;;;
+0712;SYRIAC LETTER BETH;Lo;0;AL;;;;;N;;;;;
+0713;SYRIAC LETTER GAMAL;Lo;0;AL;;;;;N;;;;;
+0714;SYRIAC LETTER GAMAL GARSHUNI;Lo;0;AL;;;;;N;;;;;
+0715;SYRIAC LETTER DALATH;Lo;0;AL;;;;;N;;;;;
+0716;SYRIAC LETTER DOTLESS DALATH RISH;Lo;0;AL;;;;;N;;;;;
+0717;SYRIAC LETTER HE;Lo;0;AL;;;;;N;;;;;
+0718;SYRIAC LETTER WAW;Lo;0;AL;;;;;N;;;;;
+0719;SYRIAC LETTER ZAIN;Lo;0;AL;;;;;N;;;;;
+071A;SYRIAC LETTER HETH;Lo;0;AL;;;;;N;;;;;
+071B;SYRIAC LETTER TETH;Lo;0;AL;;;;;N;;;;;
+071C;SYRIAC LETTER TETH GARSHUNI;Lo;0;AL;;;;;N;;;;;
+071D;SYRIAC LETTER YUDH;Lo;0;AL;;;;;N;;;;;
+071E;SYRIAC LETTER YUDH HE;Lo;0;AL;;;;;N;;;;;
+071F;SYRIAC LETTER KAPH;Lo;0;AL;;;;;N;;;;;
+0720;SYRIAC LETTER LAMADH;Lo;0;AL;;;;;N;;;;;
+0721;SYRIAC LETTER MIM;Lo;0;AL;;;;;N;;;;;
+0722;SYRIAC LETTER NUN;Lo;0;AL;;;;;N;;;;;
+0723;SYRIAC LETTER SEMKATH;Lo;0;AL;;;;;N;;;;;
+0724;SYRIAC LETTER FINAL SEMKATH;Lo;0;AL;;;;;N;;;;;
+0725;SYRIAC LETTER E;Lo;0;AL;;;;;N;;;;;
+0726;SYRIAC LETTER PE;Lo;0;AL;;;;;N;;;;;
+0727;SYRIAC LETTER REVERSED PE;Lo;0;AL;;;;;N;;;;;
+0728;SYRIAC LETTER SADHE;Lo;0;AL;;;;;N;;;;;
+0729;SYRIAC LETTER QAPH;Lo;0;AL;;;;;N;;;;;
+072A;SYRIAC LETTER RISH;Lo;0;AL;;;;;N;;;;;
+072B;SYRIAC LETTER SHIN;Lo;0;AL;;;;;N;;;;;
+072C;SYRIAC LETTER TAW;Lo;0;AL;;;;;N;;;;;
+0730;SYRIAC PTHAHA ABOVE;Mn;230;NSM;;;;;N;;;;;
+0731;SYRIAC PTHAHA BELOW;Mn;220;NSM;;;;;N;;;;;
+0732;SYRIAC PTHAHA DOTTED;Mn;230;NSM;;;;;N;;;;;
+0733;SYRIAC ZQAPHA ABOVE;Mn;230;NSM;;;;;N;;;;;
+0734;SYRIAC ZQAPHA BELOW;Mn;220;NSM;;;;;N;;;;;
+0735;SYRIAC ZQAPHA DOTTED;Mn;230;NSM;;;;;N;;;;;
+0736;SYRIAC RBASA ABOVE;Mn;230;NSM;;;;;N;;;;;
+0737;SYRIAC RBASA BELOW;Mn;220;NSM;;;;;N;;;;;
+0738;SYRIAC DOTTED ZLAMA HORIZONTAL;Mn;220;NSM;;;;;N;;;;;
+0739;SYRIAC DOTTED ZLAMA ANGULAR;Mn;220;NSM;;;;;N;;;;;
+073A;SYRIAC HBASA ABOVE;Mn;230;NSM;;;;;N;;;;;
+073B;SYRIAC HBASA BELOW;Mn;220;NSM;;;;;N;;;;;
+073C;SYRIAC HBASA-ESASA DOTTED;Mn;220;NSM;;;;;N;;;;;
+073D;SYRIAC ESASA ABOVE;Mn;230;NSM;;;;;N;;;;;
+073E;SYRIAC ESASA BELOW;Mn;220;NSM;;;;;N;;;;;
+073F;SYRIAC RWAHA;Mn;230;NSM;;;;;N;;;;;
+0740;SYRIAC FEMININE DOT;Mn;230;NSM;;;;;N;;;;;
+0741;SYRIAC QUSHSHAYA;Mn;230;NSM;;;;;N;;;;;
+0742;SYRIAC RUKKAKHA;Mn;220;NSM;;;;;N;;;;;
+0743;SYRIAC TWO VERTICAL DOTS ABOVE;Mn;230;NSM;;;;;N;;;;;
+0744;SYRIAC TWO VERTICAL DOTS BELOW;Mn;220;NSM;;;;;N;;;;;
+0745;SYRIAC THREE DOTS ABOVE;Mn;230;NSM;;;;;N;;;;;
+0746;SYRIAC THREE DOTS BELOW;Mn;220;NSM;;;;;N;;;;;
+0747;SYRIAC OBLIQUE LINE ABOVE;Mn;230;NSM;;;;;N;;;;;
+0748;SYRIAC OBLIQUE LINE BELOW;Mn;220;NSM;;;;;N;;;;;
+0749;SYRIAC MUSIC;Mn;230;NSM;;;;;N;;;;;
+074A;SYRIAC BARREKH;Mn;230;NSM;;;;;N;;;;;
+0780;THAANA LETTER HAA;Lo;0;AL;;;;;N;;;;;
+0781;THAANA LETTER SHAVIYANI;Lo;0;AL;;;;;N;;;;;
+0782;THAANA LETTER NOONU;Lo;0;AL;;;;;N;;;;;
+0783;THAANA LETTER RAA;Lo;0;AL;;;;;N;;;;;
+0784;THAANA LETTER BAA;Lo;0;AL;;;;;N;;;;;
+0785;THAANA LETTER LHAVIYANI;Lo;0;AL;;;;;N;;;;;
+0786;THAANA LETTER KAAFU;Lo;0;AL;;;;;N;;;;;
+0787;THAANA LETTER ALIFU;Lo;0;AL;;;;;N;;;;;
+0788;THAANA LETTER VAAVU;Lo;0;AL;;;;;N;;;;;
+0789;THAANA LETTER MEEMU;Lo;0;AL;;;;;N;;;;;
+078A;THAANA LETTER FAAFU;Lo;0;AL;;;;;N;;;;;
+078B;THAANA LETTER DHAALU;Lo;0;AL;;;;;N;;;;;
+078C;THAANA LETTER THAA;Lo;0;AL;;;;;N;;;;;
+078D;THAANA LETTER LAAMU;Lo;0;AL;;;;;N;;;;;
+078E;THAANA LETTER GAAFU;Lo;0;AL;;;;;N;;;;;
+078F;THAANA LETTER GNAVIYANI;Lo;0;AL;;;;;N;;;;;
+0790;THAANA LETTER SEENU;Lo;0;AL;;;;;N;;;;;
+0791;THAANA LETTER DAVIYANI;Lo;0;AL;;;;;N;;;;;
+0792;THAANA LETTER ZAVIYANI;Lo;0;AL;;;;;N;;;;;
+0793;THAANA LETTER TAVIYANI;Lo;0;AL;;;;;N;;;;;
+0794;THAANA LETTER YAA;Lo;0;AL;;;;;N;;;;;
+0795;THAANA LETTER PAVIYANI;Lo;0;AL;;;;;N;;;;;
+0796;THAANA LETTER JAVIYANI;Lo;0;AL;;;;;N;;;;;
+0797;THAANA LETTER CHAVIYANI;Lo;0;AL;;;;;N;;;;;
+0798;THAANA LETTER TTAA;Lo;0;AL;;;;;N;;;;;
+0799;THAANA LETTER HHAA;Lo;0;AL;;;;;N;;;;;
+079A;THAANA LETTER KHAA;Lo;0;AL;;;;;N;;;;;
+079B;THAANA LETTER THAALU;Lo;0;AL;;;;;N;;;;;
+079C;THAANA LETTER ZAA;Lo;0;AL;;;;;N;;;;;
+079D;THAANA LETTER SHEENU;Lo;0;AL;;;;;N;;;;;
+079E;THAANA LETTER SAADHU;Lo;0;AL;;;;;N;;;;;
+079F;THAANA LETTER DAADHU;Lo;0;AL;;;;;N;;;;;
+07A0;THAANA LETTER TO;Lo;0;AL;;;;;N;;;;;
+07A1;THAANA LETTER ZO;Lo;0;AL;;;;;N;;;;;
+07A2;THAANA LETTER AINU;Lo;0;AL;;;;;N;;;;;
+07A3;THAANA LETTER GHAINU;Lo;0;AL;;;;;N;;;;;
+07A4;THAANA LETTER QAAFU;Lo;0;AL;;;;;N;;;;;
+07A5;THAANA LETTER WAAVU;Lo;0;AL;;;;;N;;;;;
+07A6;THAANA ABAFILI;Mn;0;NSM;;;;;N;;;;;
+07A7;THAANA AABAAFILI;Mn;0;NSM;;;;;N;;;;;
+07A8;THAANA IBIFILI;Mn;0;NSM;;;;;N;;;;;
+07A9;THAANA EEBEEFILI;Mn;0;NSM;;;;;N;;;;;
+07AA;THAANA UBUFILI;Mn;0;NSM;;;;;N;;;;;
+07AB;THAANA OOBOOFILI;Mn;0;NSM;;;;;N;;;;;
+07AC;THAANA EBEFILI;Mn;0;NSM;;;;;N;;;;;
+07AD;THAANA EYBEYFILI;Mn;0;NSM;;;;;N;;;;;
+07AE;THAANA OBOFILI;Mn;0;NSM;;;;;N;;;;;
+07AF;THAANA OABOAFILI;Mn;0;NSM;;;;;N;;;;;
+07B0;THAANA SUKUN;Mn;0;NSM;;;;;N;;;;;
+07B1;THAANA LETTER NAA;Lo;0;AL;;;;;N;;;;;
+0901;DEVANAGARI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+0902;DEVANAGARI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+0903;DEVANAGARI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0905;DEVANAGARI LETTER A;Lo;0;L;;;;;N;;;;;
+0906;DEVANAGARI LETTER AA;Lo;0;L;;;;;N;;;;;
+0907;DEVANAGARI LETTER I;Lo;0;L;;;;;N;;;;;
+0908;DEVANAGARI LETTER II;Lo;0;L;;;;;N;;;;;
+0909;DEVANAGARI LETTER U;Lo;0;L;;;;;N;;;;;
+090A;DEVANAGARI LETTER UU;Lo;0;L;;;;;N;;;;;
+090B;DEVANAGARI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+090C;DEVANAGARI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+090D;DEVANAGARI LETTER CANDRA E;Lo;0;L;;;;;N;;;;;
+090E;DEVANAGARI LETTER SHORT E;Lo;0;L;;;;;N;;;;;
+090F;DEVANAGARI LETTER E;Lo;0;L;;;;;N;;;;;
+0910;DEVANAGARI LETTER AI;Lo;0;L;;;;;N;;;;;
+0911;DEVANAGARI LETTER CANDRA O;Lo;0;L;;;;;N;;;;;
+0912;DEVANAGARI LETTER SHORT O;Lo;0;L;;;;;N;;;;;
+0913;DEVANAGARI LETTER O;Lo;0;L;;;;;N;;;;;
+0914;DEVANAGARI LETTER AU;Lo;0;L;;;;;N;;;;;
+0915;DEVANAGARI LETTER KA;Lo;0;L;;;;;N;;;;;
+0916;DEVANAGARI LETTER KHA;Lo;0;L;;;;;N;;;;;
+0917;DEVANAGARI LETTER GA;Lo;0;L;;;;;N;;;;;
+0918;DEVANAGARI LETTER GHA;Lo;0;L;;;;;N;;;;;
+0919;DEVANAGARI LETTER NGA;Lo;0;L;;;;;N;;;;;
+091A;DEVANAGARI LETTER CA;Lo;0;L;;;;;N;;;;;
+091B;DEVANAGARI LETTER CHA;Lo;0;L;;;;;N;;;;;
+091C;DEVANAGARI LETTER JA;Lo;0;L;;;;;N;;;;;
+091D;DEVANAGARI LETTER JHA;Lo;0;L;;;;;N;;;;;
+091E;DEVANAGARI LETTER NYA;Lo;0;L;;;;;N;;;;;
+091F;DEVANAGARI LETTER TTA;Lo;0;L;;;;;N;;;;;
+0920;DEVANAGARI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0921;DEVANAGARI LETTER DDA;Lo;0;L;;;;;N;;;;;
+0922;DEVANAGARI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0923;DEVANAGARI LETTER NNA;Lo;0;L;;;;;N;;;;;
+0924;DEVANAGARI LETTER TA;Lo;0;L;;;;;N;;;;;
+0925;DEVANAGARI LETTER THA;Lo;0;L;;;;;N;;;;;
+0926;DEVANAGARI LETTER DA;Lo;0;L;;;;;N;;;;;
+0927;DEVANAGARI LETTER DHA;Lo;0;L;;;;;N;;;;;
+0928;DEVANAGARI LETTER NA;Lo;0;L;;;;;N;;;;;
+0929;DEVANAGARI LETTER NNNA;Lo;0;L;0928 093C;;;;N;;;;;
+092A;DEVANAGARI LETTER PA;Lo;0;L;;;;;N;;;;;
+092B;DEVANAGARI LETTER PHA;Lo;0;L;;;;;N;;;;;
+092C;DEVANAGARI LETTER BA;Lo;0;L;;;;;N;;;;;
+092D;DEVANAGARI LETTER BHA;Lo;0;L;;;;;N;;;;;
+092E;DEVANAGARI LETTER MA;Lo;0;L;;;;;N;;;;;
+092F;DEVANAGARI LETTER YA;Lo;0;L;;;;;N;;;;;
+0930;DEVANAGARI LETTER RA;Lo;0;L;;;;;N;;;;;
+0931;DEVANAGARI LETTER RRA;Lo;0;L;0930 093C;;;;N;;;;;
+0932;DEVANAGARI LETTER LA;Lo;0;L;;;;;N;;;;;
+0933;DEVANAGARI LETTER LLA;Lo;0;L;;;;;N;;;;;
+0934;DEVANAGARI LETTER LLLA;Lo;0;L;0933 093C;;;;N;;;;;
+0935;DEVANAGARI LETTER VA;Lo;0;L;;;;;N;;;;;
+0936;DEVANAGARI LETTER SHA;Lo;0;L;;;;;N;;;;;
+0937;DEVANAGARI LETTER SSA;Lo;0;L;;;;;N;;;;;
+0938;DEVANAGARI LETTER SA;Lo;0;L;;;;;N;;;;;
+0939;DEVANAGARI LETTER HA;Lo;0;L;;;;;N;;;;;
+093C;DEVANAGARI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+093D;DEVANAGARI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+093E;DEVANAGARI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+093F;DEVANAGARI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+0940;DEVANAGARI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+0941;DEVANAGARI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+0942;DEVANAGARI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+0943;DEVANAGARI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+0944;DEVANAGARI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+0945;DEVANAGARI VOWEL SIGN CANDRA E;Mn;0;NSM;;;;;N;;;;;
+0946;DEVANAGARI VOWEL SIGN SHORT E;Mn;0;NSM;;;;;N;;;;;
+0947;DEVANAGARI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+0948;DEVANAGARI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+0949;DEVANAGARI VOWEL SIGN CANDRA O;Mc;0;L;;;;;N;;;;;
+094A;DEVANAGARI VOWEL SIGN SHORT O;Mc;0;L;;;;;N;;;;;
+094B;DEVANAGARI VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+094C;DEVANAGARI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+094D;DEVANAGARI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0950;DEVANAGARI OM;Lo;0;L;;;;;N;;;;;
+0951;DEVANAGARI STRESS SIGN UDATTA;Mn;230;NSM;;;;;N;;;;;
+0952;DEVANAGARI STRESS SIGN ANUDATTA;Mn;220;NSM;;;;;N;;;;;
+0953;DEVANAGARI GRAVE ACCENT;Mn;230;NSM;;;;;N;;;;;
+0954;DEVANAGARI ACUTE ACCENT;Mn;230;NSM;;;;;N;;;;;
+0958;DEVANAGARI LETTER QA;Lo;0;L;0915 093C;;;;N;;;;;
+0959;DEVANAGARI LETTER KHHA;Lo;0;L;0916 093C;;;;N;;;;;
+095A;DEVANAGARI LETTER GHHA;Lo;0;L;0917 093C;;;;N;;;;;
+095B;DEVANAGARI LETTER ZA;Lo;0;L;091C 093C;;;;N;;;;;
+095C;DEVANAGARI LETTER DDDHA;Lo;0;L;0921 093C;;;;N;;;;;
+095D;DEVANAGARI LETTER RHA;Lo;0;L;0922 093C;;;;N;;;;;
+095E;DEVANAGARI LETTER FA;Lo;0;L;092B 093C;;;;N;;;;;
+095F;DEVANAGARI LETTER YYA;Lo;0;L;092F 093C;;;;N;;;;;
+0960;DEVANAGARI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0961;DEVANAGARI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0962;DEVANAGARI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+0963;DEVANAGARI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+0964;DEVANAGARI DANDA;Po;0;L;;;;;N;;;;;
+0965;DEVANAGARI DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+0966;DEVANAGARI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0967;DEVANAGARI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0968;DEVANAGARI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0969;DEVANAGARI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+096A;DEVANAGARI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+096B;DEVANAGARI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+096C;DEVANAGARI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+096D;DEVANAGARI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+096E;DEVANAGARI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+096F;DEVANAGARI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0970;DEVANAGARI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;;
+0981;BENGALI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+0982;BENGALI SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+0983;BENGALI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0985;BENGALI LETTER A;Lo;0;L;;;;;N;;;;;
+0986;BENGALI LETTER AA;Lo;0;L;;;;;N;;;;;
+0987;BENGALI LETTER I;Lo;0;L;;;;;N;;;;;
+0988;BENGALI LETTER II;Lo;0;L;;;;;N;;;;;
+0989;BENGALI LETTER U;Lo;0;L;;;;;N;;;;;
+098A;BENGALI LETTER UU;Lo;0;L;;;;;N;;;;;
+098B;BENGALI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+098C;BENGALI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+098F;BENGALI LETTER E;Lo;0;L;;;;;N;;;;;
+0990;BENGALI LETTER AI;Lo;0;L;;;;;N;;;;;
+0993;BENGALI LETTER O;Lo;0;L;;;;;N;;;;;
+0994;BENGALI LETTER AU;Lo;0;L;;;;;N;;;;;
+0995;BENGALI LETTER KA;Lo;0;L;;;;;N;;;;;
+0996;BENGALI LETTER KHA;Lo;0;L;;;;;N;;;;;
+0997;BENGALI LETTER GA;Lo;0;L;;;;;N;;;;;
+0998;BENGALI LETTER GHA;Lo;0;L;;;;;N;;;;;
+0999;BENGALI LETTER NGA;Lo;0;L;;;;;N;;;;;
+099A;BENGALI LETTER CA;Lo;0;L;;;;;N;;;;;
+099B;BENGALI LETTER CHA;Lo;0;L;;;;;N;;;;;
+099C;BENGALI LETTER JA;Lo;0;L;;;;;N;;;;;
+099D;BENGALI LETTER JHA;Lo;0;L;;;;;N;;;;;
+099E;BENGALI LETTER NYA;Lo;0;L;;;;;N;;;;;
+099F;BENGALI LETTER TTA;Lo;0;L;;;;;N;;;;;
+09A0;BENGALI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+09A1;BENGALI LETTER DDA;Lo;0;L;;;;;N;;;;;
+09A2;BENGALI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+09A3;BENGALI LETTER NNA;Lo;0;L;;;;;N;;;;;
+09A4;BENGALI LETTER TA;Lo;0;L;;;;;N;;;;;
+09A5;BENGALI LETTER THA;Lo;0;L;;;;;N;;;;;
+09A6;BENGALI LETTER DA;Lo;0;L;;;;;N;;;;;
+09A7;BENGALI LETTER DHA;Lo;0;L;;;;;N;;;;;
+09A8;BENGALI LETTER NA;Lo;0;L;;;;;N;;;;;
+09AA;BENGALI LETTER PA;Lo;0;L;;;;;N;;;;;
+09AB;BENGALI LETTER PHA;Lo;0;L;;;;;N;;;;;
+09AC;BENGALI LETTER BA;Lo;0;L;;;;;N;;;;;
+09AD;BENGALI LETTER BHA;Lo;0;L;;;;;N;;;;;
+09AE;BENGALI LETTER MA;Lo;0;L;;;;;N;;;;;
+09AF;BENGALI LETTER YA;Lo;0;L;;;;;N;;;;;
+09B0;BENGALI LETTER RA;Lo;0;L;;;;;N;;;;;
+09B2;BENGALI LETTER LA;Lo;0;L;;;;;N;;;;;
+09B6;BENGALI LETTER SHA;Lo;0;L;;;;;N;;;;;
+09B7;BENGALI LETTER SSA;Lo;0;L;;;;;N;;;;;
+09B8;BENGALI LETTER SA;Lo;0;L;;;;;N;;;;;
+09B9;BENGALI LETTER HA;Lo;0;L;;;;;N;;;;;
+09BC;BENGALI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+09BE;BENGALI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+09BF;BENGALI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+09C0;BENGALI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+09C1;BENGALI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+09C2;BENGALI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+09C3;BENGALI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+09C4;BENGALI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+09C7;BENGALI VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+09C8;BENGALI VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+09CB;BENGALI VOWEL SIGN O;Mc;0;L;09C7 09BE;;;;N;;;;;
+09CC;BENGALI VOWEL SIGN AU;Mc;0;L;09C7 09D7;;;;N;;;;;
+09CD;BENGALI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+09D7;BENGALI AU LENGTH MARK;Mc;0;L;;;;;N;;;;;
+09DC;BENGALI LETTER RRA;Lo;0;L;09A1 09BC;;;;N;;;;;
+09DD;BENGALI LETTER RHA;Lo;0;L;09A2 09BC;;;;N;;;;;
+09DF;BENGALI LETTER YYA;Lo;0;L;09AF 09BC;;;;N;;;;;
+09E0;BENGALI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+09E1;BENGALI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+09E2;BENGALI VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+09E3;BENGALI VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+09E6;BENGALI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+09E7;BENGALI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+09E8;BENGALI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+09E9;BENGALI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+09EA;BENGALI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+09EB;BENGALI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+09EC;BENGALI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+09ED;BENGALI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+09EE;BENGALI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+09EF;BENGALI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+09F0;BENGALI LETTER RA WITH MIDDLE DIAGONAL;Lo;0;L;;;;;N;;Assamese;;;
+09F1;BENGALI LETTER RA WITH LOWER DIAGONAL;Lo;0;L;;;;;N;BENGALI LETTER VA WITH LOWER DIAGONAL;Assamese;;;
+09F2;BENGALI RUPEE MARK;Sc;0;ET;;;;;N;;;;;
+09F3;BENGALI RUPEE SIGN;Sc;0;ET;;;;;N;;;;;
+09F4;BENGALI CURRENCY NUMERATOR ONE;No;0;L;;;;1;N;;;;;
+09F5;BENGALI CURRENCY NUMERATOR TWO;No;0;L;;;;2;N;;;;;
+09F6;BENGALI CURRENCY NUMERATOR THREE;No;0;L;;;;3;N;;;;;
+09F7;BENGALI CURRENCY NUMERATOR FOUR;No;0;L;;;;4;N;;;;;
+09F8;BENGALI CURRENCY NUMERATOR ONE LESS THAN THE DENOMINATOR;No;0;L;;;;;N;;;;;
+09F9;BENGALI CURRENCY DENOMINATOR SIXTEEN;No;0;L;;;;16;N;;;;;
+09FA;BENGALI ISSHAR;So;0;L;;;;;N;;;;;
+0A02;GURMUKHI SIGN BINDI;Mn;0;NSM;;;;;N;;;;;
+0A05;GURMUKHI LETTER A;Lo;0;L;;;;;N;;;;;
+0A06;GURMUKHI LETTER AA;Lo;0;L;;;;;N;;;;;
+0A07;GURMUKHI LETTER I;Lo;0;L;;;;;N;;;;;
+0A08;GURMUKHI LETTER II;Lo;0;L;;;;;N;;;;;
+0A09;GURMUKHI LETTER U;Lo;0;L;;;;;N;;;;;
+0A0A;GURMUKHI LETTER UU;Lo;0;L;;;;;N;;;;;
+0A0F;GURMUKHI LETTER EE;Lo;0;L;;;;;N;;;;;
+0A10;GURMUKHI LETTER AI;Lo;0;L;;;;;N;;;;;
+0A13;GURMUKHI LETTER OO;Lo;0;L;;;;;N;;;;;
+0A14;GURMUKHI LETTER AU;Lo;0;L;;;;;N;;;;;
+0A15;GURMUKHI LETTER KA;Lo;0;L;;;;;N;;;;;
+0A16;GURMUKHI LETTER KHA;Lo;0;L;;;;;N;;;;;
+0A17;GURMUKHI LETTER GA;Lo;0;L;;;;;N;;;;;
+0A18;GURMUKHI LETTER GHA;Lo;0;L;;;;;N;;;;;
+0A19;GURMUKHI LETTER NGA;Lo;0;L;;;;;N;;;;;
+0A1A;GURMUKHI LETTER CA;Lo;0;L;;;;;N;;;;;
+0A1B;GURMUKHI LETTER CHA;Lo;0;L;;;;;N;;;;;
+0A1C;GURMUKHI LETTER JA;Lo;0;L;;;;;N;;;;;
+0A1D;GURMUKHI LETTER JHA;Lo;0;L;;;;;N;;;;;
+0A1E;GURMUKHI LETTER NYA;Lo;0;L;;;;;N;;;;;
+0A1F;GURMUKHI LETTER TTA;Lo;0;L;;;;;N;;;;;
+0A20;GURMUKHI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0A21;GURMUKHI LETTER DDA;Lo;0;L;;;;;N;;;;;
+0A22;GURMUKHI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0A23;GURMUKHI LETTER NNA;Lo;0;L;;;;;N;;;;;
+0A24;GURMUKHI LETTER TA;Lo;0;L;;;;;N;;;;;
+0A25;GURMUKHI LETTER THA;Lo;0;L;;;;;N;;;;;
+0A26;GURMUKHI LETTER DA;Lo;0;L;;;;;N;;;;;
+0A27;GURMUKHI LETTER DHA;Lo;0;L;;;;;N;;;;;
+0A28;GURMUKHI LETTER NA;Lo;0;L;;;;;N;;;;;
+0A2A;GURMUKHI LETTER PA;Lo;0;L;;;;;N;;;;;
+0A2B;GURMUKHI LETTER PHA;Lo;0;L;;;;;N;;;;;
+0A2C;GURMUKHI LETTER BA;Lo;0;L;;;;;N;;;;;
+0A2D;GURMUKHI LETTER BHA;Lo;0;L;;;;;N;;;;;
+0A2E;GURMUKHI LETTER MA;Lo;0;L;;;;;N;;;;;
+0A2F;GURMUKHI LETTER YA;Lo;0;L;;;;;N;;;;;
+0A30;GURMUKHI LETTER RA;Lo;0;L;;;;;N;;;;;
+0A32;GURMUKHI LETTER LA;Lo;0;L;;;;;N;;;;;
+0A33;GURMUKHI LETTER LLA;Lo;0;L;0A32 0A3C;;;;N;;;;;
+0A35;GURMUKHI LETTER VA;Lo;0;L;;;;;N;;;;;
+0A36;GURMUKHI LETTER SHA;Lo;0;L;0A38 0A3C;;;;N;;;;;
+0A38;GURMUKHI LETTER SA;Lo;0;L;;;;;N;;;;;
+0A39;GURMUKHI LETTER HA;Lo;0;L;;;;;N;;;;;
+0A3C;GURMUKHI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+0A3E;GURMUKHI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0A3F;GURMUKHI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+0A40;GURMUKHI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+0A41;GURMUKHI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+0A42;GURMUKHI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+0A47;GURMUKHI VOWEL SIGN EE;Mn;0;NSM;;;;;N;;;;;
+0A48;GURMUKHI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+0A4B;GURMUKHI VOWEL SIGN OO;Mn;0;NSM;;;;;N;;;;;
+0A4C;GURMUKHI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
+0A4D;GURMUKHI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0A59;GURMUKHI LETTER KHHA;Lo;0;L;0A16 0A3C;;;;N;;;;;
+0A5A;GURMUKHI LETTER GHHA;Lo;0;L;0A17 0A3C;;;;N;;;;;
+0A5B;GURMUKHI LETTER ZA;Lo;0;L;0A1C 0A3C;;;;N;;;;;
+0A5C;GURMUKHI LETTER RRA;Lo;0;L;;;;;N;;;;;
+0A5E;GURMUKHI LETTER FA;Lo;0;L;0A2B 0A3C;;;;N;;;;;
+0A66;GURMUKHI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0A67;GURMUKHI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0A68;GURMUKHI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0A69;GURMUKHI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0A6A;GURMUKHI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0A6B;GURMUKHI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0A6C;GURMUKHI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0A6D;GURMUKHI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0A6E;GURMUKHI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0A6F;GURMUKHI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0A70;GURMUKHI TIPPI;Mn;0;NSM;;;;;N;;;;;
+0A71;GURMUKHI ADDAK;Mn;0;NSM;;;;;N;;;;;
+0A72;GURMUKHI IRI;Lo;0;L;;;;;N;;;;;
+0A73;GURMUKHI URA;Lo;0;L;;;;;N;;;;;
+0A74;GURMUKHI EK ONKAR;Lo;0;L;;;;;N;;;;;
+0A81;GUJARATI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+0A82;GUJARATI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+0A83;GUJARATI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0A85;GUJARATI LETTER A;Lo;0;L;;;;;N;;;;;
+0A86;GUJARATI LETTER AA;Lo;0;L;;;;;N;;;;;
+0A87;GUJARATI LETTER I;Lo;0;L;;;;;N;;;;;
+0A88;GUJARATI LETTER II;Lo;0;L;;;;;N;;;;;
+0A89;GUJARATI LETTER U;Lo;0;L;;;;;N;;;;;
+0A8A;GUJARATI LETTER UU;Lo;0;L;;;;;N;;;;;
+0A8B;GUJARATI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+0A8D;GUJARATI VOWEL CANDRA E;Lo;0;L;;;;;N;;;;;
+0A8F;GUJARATI LETTER E;Lo;0;L;;;;;N;;;;;
+0A90;GUJARATI LETTER AI;Lo;0;L;;;;;N;;;;;
+0A91;GUJARATI VOWEL CANDRA O;Lo;0;L;;;;;N;;;;;
+0A93;GUJARATI LETTER O;Lo;0;L;;;;;N;;;;;
+0A94;GUJARATI LETTER AU;Lo;0;L;;;;;N;;;;;
+0A95;GUJARATI LETTER KA;Lo;0;L;;;;;N;;;;;
+0A96;GUJARATI LETTER KHA;Lo;0;L;;;;;N;;;;;
+0A97;GUJARATI LETTER GA;Lo;0;L;;;;;N;;;;;
+0A98;GUJARATI LETTER GHA;Lo;0;L;;;;;N;;;;;
+0A99;GUJARATI LETTER NGA;Lo;0;L;;;;;N;;;;;
+0A9A;GUJARATI LETTER CA;Lo;0;L;;;;;N;;;;;
+0A9B;GUJARATI LETTER CHA;Lo;0;L;;;;;N;;;;;
+0A9C;GUJARATI LETTER JA;Lo;0;L;;;;;N;;;;;
+0A9D;GUJARATI LETTER JHA;Lo;0;L;;;;;N;;;;;
+0A9E;GUJARATI LETTER NYA;Lo;0;L;;;;;N;;;;;
+0A9F;GUJARATI LETTER TTA;Lo;0;L;;;;;N;;;;;
+0AA0;GUJARATI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0AA1;GUJARATI LETTER DDA;Lo;0;L;;;;;N;;;;;
+0AA2;GUJARATI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0AA3;GUJARATI LETTER NNA;Lo;0;L;;;;;N;;;;;
+0AA4;GUJARATI LETTER TA;Lo;0;L;;;;;N;;;;;
+0AA5;GUJARATI LETTER THA;Lo;0;L;;;;;N;;;;;
+0AA6;GUJARATI LETTER DA;Lo;0;L;;;;;N;;;;;
+0AA7;GUJARATI LETTER DHA;Lo;0;L;;;;;N;;;;;
+0AA8;GUJARATI LETTER NA;Lo;0;L;;;;;N;;;;;
+0AAA;GUJARATI LETTER PA;Lo;0;L;;;;;N;;;;;
+0AAB;GUJARATI LETTER PHA;Lo;0;L;;;;;N;;;;;
+0AAC;GUJARATI LETTER BA;Lo;0;L;;;;;N;;;;;
+0AAD;GUJARATI LETTER BHA;Lo;0;L;;;;;N;;;;;
+0AAE;GUJARATI LETTER MA;Lo;0;L;;;;;N;;;;;
+0AAF;GUJARATI LETTER YA;Lo;0;L;;;;;N;;;;;
+0AB0;GUJARATI LETTER RA;Lo;0;L;;;;;N;;;;;
+0AB2;GUJARATI LETTER LA;Lo;0;L;;;;;N;;;;;
+0AB3;GUJARATI LETTER LLA;Lo;0;L;;;;;N;;;;;
+0AB5;GUJARATI LETTER VA;Lo;0;L;;;;;N;;;;;
+0AB6;GUJARATI LETTER SHA;Lo;0;L;;;;;N;;;;;
+0AB7;GUJARATI LETTER SSA;Lo;0;L;;;;;N;;;;;
+0AB8;GUJARATI LETTER SA;Lo;0;L;;;;;N;;;;;
+0AB9;GUJARATI LETTER HA;Lo;0;L;;;;;N;;;;;
+0ABC;GUJARATI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+0ABD;GUJARATI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+0ABE;GUJARATI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0ABF;GUJARATI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+0AC0;GUJARATI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+0AC1;GUJARATI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+0AC2;GUJARATI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+0AC3;GUJARATI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+0AC4;GUJARATI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+0AC5;GUJARATI VOWEL SIGN CANDRA E;Mn;0;NSM;;;;;N;;;;;
+0AC7;GUJARATI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+0AC8;GUJARATI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+0AC9;GUJARATI VOWEL SIGN CANDRA O;Mc;0;L;;;;;N;;;;;
+0ACB;GUJARATI VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+0ACC;GUJARATI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+0ACD;GUJARATI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0AD0;GUJARATI OM;Lo;0;L;;;;;N;;;;;
+0AE0;GUJARATI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0AE6;GUJARATI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0AE7;GUJARATI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0AE8;GUJARATI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0AE9;GUJARATI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0AEA;GUJARATI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0AEB;GUJARATI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0AEC;GUJARATI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0AED;GUJARATI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0AEE;GUJARATI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0AEF;GUJARATI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0B01;ORIYA SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+0B02;ORIYA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+0B03;ORIYA SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0B05;ORIYA LETTER A;Lo;0;L;;;;;N;;;;;
+0B06;ORIYA LETTER AA;Lo;0;L;;;;;N;;;;;
+0B07;ORIYA LETTER I;Lo;0;L;;;;;N;;;;;
+0B08;ORIYA LETTER II;Lo;0;L;;;;;N;;;;;
+0B09;ORIYA LETTER U;Lo;0;L;;;;;N;;;;;
+0B0A;ORIYA LETTER UU;Lo;0;L;;;;;N;;;;;
+0B0B;ORIYA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+0B0C;ORIYA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+0B0F;ORIYA LETTER E;Lo;0;L;;;;;N;;;;;
+0B10;ORIYA LETTER AI;Lo;0;L;;;;;N;;;;;
+0B13;ORIYA LETTER O;Lo;0;L;;;;;N;;;;;
+0B14;ORIYA LETTER AU;Lo;0;L;;;;;N;;;;;
+0B15;ORIYA LETTER KA;Lo;0;L;;;;;N;;;;;
+0B16;ORIYA LETTER KHA;Lo;0;L;;;;;N;;;;;
+0B17;ORIYA LETTER GA;Lo;0;L;;;;;N;;;;;
+0B18;ORIYA LETTER GHA;Lo;0;L;;;;;N;;;;;
+0B19;ORIYA LETTER NGA;Lo;0;L;;;;;N;;;;;
+0B1A;ORIYA LETTER CA;Lo;0;L;;;;;N;;;;;
+0B1B;ORIYA LETTER CHA;Lo;0;L;;;;;N;;;;;
+0B1C;ORIYA LETTER JA;Lo;0;L;;;;;N;;;;;
+0B1D;ORIYA LETTER JHA;Lo;0;L;;;;;N;;;;;
+0B1E;ORIYA LETTER NYA;Lo;0;L;;;;;N;;;;;
+0B1F;ORIYA LETTER TTA;Lo;0;L;;;;;N;;;;;
+0B20;ORIYA LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0B21;ORIYA LETTER DDA;Lo;0;L;;;;;N;;;;;
+0B22;ORIYA LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0B23;ORIYA LETTER NNA;Lo;0;L;;;;;N;;;;;
+0B24;ORIYA LETTER TA;Lo;0;L;;;;;N;;;;;
+0B25;ORIYA LETTER THA;Lo;0;L;;;;;N;;;;;
+0B26;ORIYA LETTER DA;Lo;0;L;;;;;N;;;;;
+0B27;ORIYA LETTER DHA;Lo;0;L;;;;;N;;;;;
+0B28;ORIYA LETTER NA;Lo;0;L;;;;;N;;;;;
+0B2A;ORIYA LETTER PA;Lo;0;L;;;;;N;;;;;
+0B2B;ORIYA LETTER PHA;Lo;0;L;;;;;N;;;;;
+0B2C;ORIYA LETTER BA;Lo;0;L;;;;;N;;;;;
+0B2D;ORIYA LETTER BHA;Lo;0;L;;;;;N;;;;;
+0B2E;ORIYA LETTER MA;Lo;0;L;;;;;N;;;;;
+0B2F;ORIYA LETTER YA;Lo;0;L;;;;;N;;;;;
+0B30;ORIYA LETTER RA;Lo;0;L;;;;;N;;;;;
+0B32;ORIYA LETTER LA;Lo;0;L;;;;;N;;;;;
+0B33;ORIYA LETTER LLA;Lo;0;L;;;;;N;;;;;
+0B36;ORIYA LETTER SHA;Lo;0;L;;;;;N;;;;;
+0B37;ORIYA LETTER SSA;Lo;0;L;;;;;N;;;;;
+0B38;ORIYA LETTER SA;Lo;0;L;;;;;N;;;;;
+0B39;ORIYA LETTER HA;Lo;0;L;;;;;N;;;;;
+0B3C;ORIYA SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+0B3D;ORIYA SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+0B3E;ORIYA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0B3F;ORIYA VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+0B40;ORIYA VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+0B41;ORIYA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+0B42;ORIYA VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+0B43;ORIYA VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+0B47;ORIYA VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+0B48;ORIYA VOWEL SIGN AI;Mc;0;L;0B47 0B56;;;;N;;;;;
+0B4B;ORIYA VOWEL SIGN O;Mc;0;L;0B47 0B3E;;;;N;;;;;
+0B4C;ORIYA VOWEL SIGN AU;Mc;0;L;0B47 0B57;;;;N;;;;;
+0B4D;ORIYA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0B56;ORIYA AI LENGTH MARK;Mn;0;NSM;;;;;N;;;;;
+0B57;ORIYA AU LENGTH MARK;Mc;0;L;;;;;N;;;;;
+0B5C;ORIYA LETTER RRA;Lo;0;L;0B21 0B3C;;;;N;;;;;
+0B5D;ORIYA LETTER RHA;Lo;0;L;0B22 0B3C;;;;N;;;;;
+0B5F;ORIYA LETTER YYA;Lo;0;L;;;;;N;;;;;
+0B60;ORIYA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0B61;ORIYA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0B66;ORIYA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0B67;ORIYA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0B68;ORIYA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0B69;ORIYA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0B6A;ORIYA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0B6B;ORIYA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0B6C;ORIYA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0B6D;ORIYA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0B6E;ORIYA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0B6F;ORIYA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0B70;ORIYA ISSHAR;So;0;L;;;;;N;;;;;
+0B82;TAMIL SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+0B83;TAMIL SIGN VISARGA;Lo;0;L;;;;;N;;;;;
+0B85;TAMIL LETTER A;Lo;0;L;;;;;N;;;;;
+0B86;TAMIL LETTER AA;Lo;0;L;;;;;N;;;;;
+0B87;TAMIL LETTER I;Lo;0;L;;;;;N;;;;;
+0B88;TAMIL LETTER II;Lo;0;L;;;;;N;;;;;
+0B89;TAMIL LETTER U;Lo;0;L;;;;;N;;;;;
+0B8A;TAMIL LETTER UU;Lo;0;L;;;;;N;;;;;
+0B8E;TAMIL LETTER E;Lo;0;L;;;;;N;;;;;
+0B8F;TAMIL LETTER EE;Lo;0;L;;;;;N;;;;;
+0B90;TAMIL LETTER AI;Lo;0;L;;;;;N;;;;;
+0B92;TAMIL LETTER O;Lo;0;L;;;;;N;;;;;
+0B93;TAMIL LETTER OO;Lo;0;L;;;;;N;;;;;
+0B94;TAMIL LETTER AU;Lo;0;L;0B92 0BD7;;;;N;;;;;
+0B95;TAMIL LETTER KA;Lo;0;L;;;;;N;;;;;
+0B99;TAMIL LETTER NGA;Lo;0;L;;;;;N;;;;;
+0B9A;TAMIL LETTER CA;Lo;0;L;;;;;N;;;;;
+0B9C;TAMIL LETTER JA;Lo;0;L;;;;;N;;;;;
+0B9E;TAMIL LETTER NYA;Lo;0;L;;;;;N;;;;;
+0B9F;TAMIL LETTER TTA;Lo;0;L;;;;;N;;;;;
+0BA3;TAMIL LETTER NNA;Lo;0;L;;;;;N;;;;;
+0BA4;TAMIL LETTER TA;Lo;0;L;;;;;N;;;;;
+0BA8;TAMIL LETTER NA;Lo;0;L;;;;;N;;;;;
+0BA9;TAMIL LETTER NNNA;Lo;0;L;;;;;N;;;;;
+0BAA;TAMIL LETTER PA;Lo;0;L;;;;;N;;;;;
+0BAE;TAMIL LETTER MA;Lo;0;L;;;;;N;;;;;
+0BAF;TAMIL LETTER YA;Lo;0;L;;;;;N;;;;;
+0BB0;TAMIL LETTER RA;Lo;0;L;;;;;N;;;;;
+0BB1;TAMIL LETTER RRA;Lo;0;L;;;;;N;;;;;
+0BB2;TAMIL LETTER LA;Lo;0;L;;;;;N;;;;;
+0BB3;TAMIL LETTER LLA;Lo;0;L;;;;;N;;;;;
+0BB4;TAMIL LETTER LLLA;Lo;0;L;;;;;N;;;;;
+0BB5;TAMIL LETTER VA;Lo;0;L;;;;;N;;;;;
+0BB7;TAMIL LETTER SSA;Lo;0;L;;;;;N;;;;;
+0BB8;TAMIL LETTER SA;Lo;0;L;;;;;N;;;;;
+0BB9;TAMIL LETTER HA;Lo;0;L;;;;;N;;;;;
+0BBE;TAMIL VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0BBF;TAMIL VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+0BC0;TAMIL VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+0BC1;TAMIL VOWEL SIGN U;Mc;0;L;;;;;N;;;;;
+0BC2;TAMIL VOWEL SIGN UU;Mc;0;L;;;;;N;;;;;
+0BC6;TAMIL VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+0BC7;TAMIL VOWEL SIGN EE;Mc;0;L;;;;;N;;;;;
+0BC8;TAMIL VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+0BCA;TAMIL VOWEL SIGN O;Mc;0;L;0BC6 0BBE;;;;N;;;;;
+0BCB;TAMIL VOWEL SIGN OO;Mc;0;L;0BC7 0BBE;;;;N;;;;;
+0BCC;TAMIL VOWEL SIGN AU;Mc;0;L;0BC6 0BD7;;;;N;;;;;
+0BCD;TAMIL SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0BD7;TAMIL AU LENGTH MARK;Mc;0;L;;;;;N;;;;;
+0BE7;TAMIL DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0BE8;TAMIL DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0BE9;TAMIL DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0BEA;TAMIL DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0BEB;TAMIL DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0BEC;TAMIL DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0BED;TAMIL DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0BEE;TAMIL DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0BEF;TAMIL DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0BF0;TAMIL NUMBER TEN;No;0;L;;;;10;N;;;;;
+0BF1;TAMIL NUMBER ONE HUNDRED;No;0;L;;;;100;N;;;;;
+0BF2;TAMIL NUMBER ONE THOUSAND;No;0;L;;;;1000;N;;;;;
+0C01;TELUGU SIGN CANDRABINDU;Mc;0;L;;;;;N;;;;;
+0C02;TELUGU SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+0C03;TELUGU SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0C05;TELUGU LETTER A;Lo;0;L;;;;;N;;;;;
+0C06;TELUGU LETTER AA;Lo;0;L;;;;;N;;;;;
+0C07;TELUGU LETTER I;Lo;0;L;;;;;N;;;;;
+0C08;TELUGU LETTER II;Lo;0;L;;;;;N;;;;;
+0C09;TELUGU LETTER U;Lo;0;L;;;;;N;;;;;
+0C0A;TELUGU LETTER UU;Lo;0;L;;;;;N;;;;;
+0C0B;TELUGU LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+0C0C;TELUGU LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+0C0E;TELUGU LETTER E;Lo;0;L;;;;;N;;;;;
+0C0F;TELUGU LETTER EE;Lo;0;L;;;;;N;;;;;
+0C10;TELUGU LETTER AI;Lo;0;L;;;;;N;;;;;
+0C12;TELUGU LETTER O;Lo;0;L;;;;;N;;;;;
+0C13;TELUGU LETTER OO;Lo;0;L;;;;;N;;;;;
+0C14;TELUGU LETTER AU;Lo;0;L;;;;;N;;;;;
+0C15;TELUGU LETTER KA;Lo;0;L;;;;;N;;;;;
+0C16;TELUGU LETTER KHA;Lo;0;L;;;;;N;;;;;
+0C17;TELUGU LETTER GA;Lo;0;L;;;;;N;;;;;
+0C18;TELUGU LETTER GHA;Lo;0;L;;;;;N;;;;;
+0C19;TELUGU LETTER NGA;Lo;0;L;;;;;N;;;;;
+0C1A;TELUGU LETTER CA;Lo;0;L;;;;;N;;;;;
+0C1B;TELUGU LETTER CHA;Lo;0;L;;;;;N;;;;;
+0C1C;TELUGU LETTER JA;Lo;0;L;;;;;N;;;;;
+0C1D;TELUGU LETTER JHA;Lo;0;L;;;;;N;;;;;
+0C1E;TELUGU LETTER NYA;Lo;0;L;;;;;N;;;;;
+0C1F;TELUGU LETTER TTA;Lo;0;L;;;;;N;;;;;
+0C20;TELUGU LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0C21;TELUGU LETTER DDA;Lo;0;L;;;;;N;;;;;
+0C22;TELUGU LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0C23;TELUGU LETTER NNA;Lo;0;L;;;;;N;;;;;
+0C24;TELUGU LETTER TA;Lo;0;L;;;;;N;;;;;
+0C25;TELUGU LETTER THA;Lo;0;L;;;;;N;;;;;
+0C26;TELUGU LETTER DA;Lo;0;L;;;;;N;;;;;
+0C27;TELUGU LETTER DHA;Lo;0;L;;;;;N;;;;;
+0C28;TELUGU LETTER NA;Lo;0;L;;;;;N;;;;;
+0C2A;TELUGU LETTER PA;Lo;0;L;;;;;N;;;;;
+0C2B;TELUGU LETTER PHA;Lo;0;L;;;;;N;;;;;
+0C2C;TELUGU LETTER BA;Lo;0;L;;;;;N;;;;;
+0C2D;TELUGU LETTER BHA;Lo;0;L;;;;;N;;;;;
+0C2E;TELUGU LETTER MA;Lo;0;L;;;;;N;;;;;
+0C2F;TELUGU LETTER YA;Lo;0;L;;;;;N;;;;;
+0C30;TELUGU LETTER RA;Lo;0;L;;;;;N;;;;;
+0C31;TELUGU LETTER RRA;Lo;0;L;;;;;N;;;;;
+0C32;TELUGU LETTER LA;Lo;0;L;;;;;N;;;;;
+0C33;TELUGU LETTER LLA;Lo;0;L;;;;;N;;;;;
+0C35;TELUGU LETTER VA;Lo;0;L;;;;;N;;;;;
+0C36;TELUGU LETTER SHA;Lo;0;L;;;;;N;;;;;
+0C37;TELUGU LETTER SSA;Lo;0;L;;;;;N;;;;;
+0C38;TELUGU LETTER SA;Lo;0;L;;;;;N;;;;;
+0C39;TELUGU LETTER HA;Lo;0;L;;;;;N;;;;;
+0C3E;TELUGU VOWEL SIGN AA;Mn;0;NSM;;;;;N;;;;;
+0C3F;TELUGU VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+0C40;TELUGU VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+0C41;TELUGU VOWEL SIGN U;Mc;0;L;;;;;N;;;;;
+0C42;TELUGU VOWEL SIGN UU;Mc;0;L;;;;;N;;;;;
+0C43;TELUGU VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;;
+0C44;TELUGU VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;;
+0C46;TELUGU VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+0C47;TELUGU VOWEL SIGN EE;Mn;0;NSM;;;;;N;;;;;
+0C48;TELUGU VOWEL SIGN AI;Mn;0;NSM;0C46 0C56;;;;N;;;;;
+0C4A;TELUGU VOWEL SIGN O;Mn;0;NSM;;;;;N;;;;;
+0C4B;TELUGU VOWEL SIGN OO;Mn;0;NSM;;;;;N;;;;;
+0C4C;TELUGU VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
+0C4D;TELUGU SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0C55;TELUGU LENGTH MARK;Mn;84;NSM;;;;;N;;;;;
+0C56;TELUGU AI LENGTH MARK;Mn;91;NSM;;;;;N;;;;;
+0C60;TELUGU LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0C61;TELUGU LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0C66;TELUGU DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0C67;TELUGU DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0C68;TELUGU DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0C69;TELUGU DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0C6A;TELUGU DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0C6B;TELUGU DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0C6C;TELUGU DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0C6D;TELUGU DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0C6E;TELUGU DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0C6F;TELUGU DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0C82;KANNADA SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+0C83;KANNADA SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0C85;KANNADA LETTER A;Lo;0;L;;;;;N;;;;;
+0C86;KANNADA LETTER AA;Lo;0;L;;;;;N;;;;;
+0C87;KANNADA LETTER I;Lo;0;L;;;;;N;;;;;
+0C88;KANNADA LETTER II;Lo;0;L;;;;;N;;;;;
+0C89;KANNADA LETTER U;Lo;0;L;;;;;N;;;;;
+0C8A;KANNADA LETTER UU;Lo;0;L;;;;;N;;;;;
+0C8B;KANNADA LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+0C8C;KANNADA LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+0C8E;KANNADA LETTER E;Lo;0;L;;;;;N;;;;;
+0C8F;KANNADA LETTER EE;Lo;0;L;;;;;N;;;;;
+0C90;KANNADA LETTER AI;Lo;0;L;;;;;N;;;;;
+0C92;KANNADA LETTER O;Lo;0;L;;;;;N;;;;;
+0C93;KANNADA LETTER OO;Lo;0;L;;;;;N;;;;;
+0C94;KANNADA LETTER AU;Lo;0;L;;;;;N;;;;;
+0C95;KANNADA LETTER KA;Lo;0;L;;;;;N;;;;;
+0C96;KANNADA LETTER KHA;Lo;0;L;;;;;N;;;;;
+0C97;KANNADA LETTER GA;Lo;0;L;;;;;N;;;;;
+0C98;KANNADA LETTER GHA;Lo;0;L;;;;;N;;;;;
+0C99;KANNADA LETTER NGA;Lo;0;L;;;;;N;;;;;
+0C9A;KANNADA LETTER CA;Lo;0;L;;;;;N;;;;;
+0C9B;KANNADA LETTER CHA;Lo;0;L;;;;;N;;;;;
+0C9C;KANNADA LETTER JA;Lo;0;L;;;;;N;;;;;
+0C9D;KANNADA LETTER JHA;Lo;0;L;;;;;N;;;;;
+0C9E;KANNADA LETTER NYA;Lo;0;L;;;;;N;;;;;
+0C9F;KANNADA LETTER TTA;Lo;0;L;;;;;N;;;;;
+0CA0;KANNADA LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0CA1;KANNADA LETTER DDA;Lo;0;L;;;;;N;;;;;
+0CA2;KANNADA LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0CA3;KANNADA LETTER NNA;Lo;0;L;;;;;N;;;;;
+0CA4;KANNADA LETTER TA;Lo;0;L;;;;;N;;;;;
+0CA5;KANNADA LETTER THA;Lo;0;L;;;;;N;;;;;
+0CA6;KANNADA LETTER DA;Lo;0;L;;;;;N;;;;;
+0CA7;KANNADA LETTER DHA;Lo;0;L;;;;;N;;;;;
+0CA8;KANNADA LETTER NA;Lo;0;L;;;;;N;;;;;
+0CAA;KANNADA LETTER PA;Lo;0;L;;;;;N;;;;;
+0CAB;KANNADA LETTER PHA;Lo;0;L;;;;;N;;;;;
+0CAC;KANNADA LETTER BA;Lo;0;L;;;;;N;;;;;
+0CAD;KANNADA LETTER BHA;Lo;0;L;;;;;N;;;;;
+0CAE;KANNADA LETTER MA;Lo;0;L;;;;;N;;;;;
+0CAF;KANNADA LETTER YA;Lo;0;L;;;;;N;;;;;
+0CB0;KANNADA LETTER RA;Lo;0;L;;;;;N;;;;;
+0CB1;KANNADA LETTER RRA;Lo;0;L;;;;;N;;;;;
+0CB2;KANNADA LETTER LA;Lo;0;L;;;;;N;;;;;
+0CB3;KANNADA LETTER LLA;Lo;0;L;;;;;N;;;;;
+0CB5;KANNADA LETTER VA;Lo;0;L;;;;;N;;;;;
+0CB6;KANNADA LETTER SHA;Lo;0;L;;;;;N;;;;;
+0CB7;KANNADA LETTER SSA;Lo;0;L;;;;;N;;;;;
+0CB8;KANNADA LETTER SA;Lo;0;L;;;;;N;;;;;
+0CB9;KANNADA LETTER HA;Lo;0;L;;;;;N;;;;;
+0CBE;KANNADA VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0CBF;KANNADA VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+0CC0;KANNADA VOWEL SIGN II;Mc;0;L;0CBF 0CD5;;;;N;;;;;
+0CC1;KANNADA VOWEL SIGN U;Mc;0;L;;;;;N;;;;;
+0CC2;KANNADA VOWEL SIGN UU;Mc;0;L;;;;;N;;;;;
+0CC3;KANNADA VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;;
+0CC4;KANNADA VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;;
+0CC6;KANNADA VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+0CC7;KANNADA VOWEL SIGN EE;Mc;0;L;0CC6 0CD5;;;;N;;;;;
+0CC8;KANNADA VOWEL SIGN AI;Mc;0;L;0CC6 0CD6;;;;N;;;;;
+0CCA;KANNADA VOWEL SIGN O;Mc;0;L;0CC6 0CC2;;;;N;;;;;
+0CCB;KANNADA VOWEL SIGN OO;Mc;0;L;0CCA 0CD5;;;;N;;;;;
+0CCC;KANNADA VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
+0CCD;KANNADA SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0CD5;KANNADA LENGTH MARK;Mc;0;L;;;;;N;;;;;
+0CD6;KANNADA AI LENGTH MARK;Mc;0;L;;;;;N;;;;;
+0CDE;KANNADA LETTER FA;Lo;0;L;;;;;N;;;;;
+0CE0;KANNADA LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0CE1;KANNADA LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0CE6;KANNADA DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0CE7;KANNADA DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0CE8;KANNADA DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0CE9;KANNADA DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0CEA;KANNADA DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0CEB;KANNADA DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0CEC;KANNADA DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0CED;KANNADA DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0CEE;KANNADA DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0CEF;KANNADA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0D02;MALAYALAM SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+0D03;MALAYALAM SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+0D05;MALAYALAM LETTER A;Lo;0;L;;;;;N;;;;;
+0D06;MALAYALAM LETTER AA;Lo;0;L;;;;;N;;;;;
+0D07;MALAYALAM LETTER I;Lo;0;L;;;;;N;;;;;
+0D08;MALAYALAM LETTER II;Lo;0;L;;;;;N;;;;;
+0D09;MALAYALAM LETTER U;Lo;0;L;;;;;N;;;;;
+0D0A;MALAYALAM LETTER UU;Lo;0;L;;;;;N;;;;;
+0D0B;MALAYALAM LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+0D0C;MALAYALAM LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+0D0E;MALAYALAM LETTER E;Lo;0;L;;;;;N;;;;;
+0D0F;MALAYALAM LETTER EE;Lo;0;L;;;;;N;;;;;
+0D10;MALAYALAM LETTER AI;Lo;0;L;;;;;N;;;;;
+0D12;MALAYALAM LETTER O;Lo;0;L;;;;;N;;;;;
+0D13;MALAYALAM LETTER OO;Lo;0;L;;;;;N;;;;;
+0D14;MALAYALAM LETTER AU;Lo;0;L;;;;;N;;;;;
+0D15;MALAYALAM LETTER KA;Lo;0;L;;;;;N;;;;;
+0D16;MALAYALAM LETTER KHA;Lo;0;L;;;;;N;;;;;
+0D17;MALAYALAM LETTER GA;Lo;0;L;;;;;N;;;;;
+0D18;MALAYALAM LETTER GHA;Lo;0;L;;;;;N;;;;;
+0D19;MALAYALAM LETTER NGA;Lo;0;L;;;;;N;;;;;
+0D1A;MALAYALAM LETTER CA;Lo;0;L;;;;;N;;;;;
+0D1B;MALAYALAM LETTER CHA;Lo;0;L;;;;;N;;;;;
+0D1C;MALAYALAM LETTER JA;Lo;0;L;;;;;N;;;;;
+0D1D;MALAYALAM LETTER JHA;Lo;0;L;;;;;N;;;;;
+0D1E;MALAYALAM LETTER NYA;Lo;0;L;;;;;N;;;;;
+0D1F;MALAYALAM LETTER TTA;Lo;0;L;;;;;N;;;;;
+0D20;MALAYALAM LETTER TTHA;Lo;0;L;;;;;N;;;;;
+0D21;MALAYALAM LETTER DDA;Lo;0;L;;;;;N;;;;;
+0D22;MALAYALAM LETTER DDHA;Lo;0;L;;;;;N;;;;;
+0D23;MALAYALAM LETTER NNA;Lo;0;L;;;;;N;;;;;
+0D24;MALAYALAM LETTER TA;Lo;0;L;;;;;N;;;;;
+0D25;MALAYALAM LETTER THA;Lo;0;L;;;;;N;;;;;
+0D26;MALAYALAM LETTER DA;Lo;0;L;;;;;N;;;;;
+0D27;MALAYALAM LETTER DHA;Lo;0;L;;;;;N;;;;;
+0D28;MALAYALAM LETTER NA;Lo;0;L;;;;;N;;;;;
+0D2A;MALAYALAM LETTER PA;Lo;0;L;;;;;N;;;;;
+0D2B;MALAYALAM LETTER PHA;Lo;0;L;;;;;N;;;;;
+0D2C;MALAYALAM LETTER BA;Lo;0;L;;;;;N;;;;;
+0D2D;MALAYALAM LETTER BHA;Lo;0;L;;;;;N;;;;;
+0D2E;MALAYALAM LETTER MA;Lo;0;L;;;;;N;;;;;
+0D2F;MALAYALAM LETTER YA;Lo;0;L;;;;;N;;;;;
+0D30;MALAYALAM LETTER RA;Lo;0;L;;;;;N;;;;;
+0D31;MALAYALAM LETTER RRA;Lo;0;L;;;;;N;;;;;
+0D32;MALAYALAM LETTER LA;Lo;0;L;;;;;N;;;;;
+0D33;MALAYALAM LETTER LLA;Lo;0;L;;;;;N;;;;;
+0D34;MALAYALAM LETTER LLLA;Lo;0;L;;;;;N;;;;;
+0D35;MALAYALAM LETTER VA;Lo;0;L;;;;;N;;;;;
+0D36;MALAYALAM LETTER SHA;Lo;0;L;;;;;N;;;;;
+0D37;MALAYALAM LETTER SSA;Lo;0;L;;;;;N;;;;;
+0D38;MALAYALAM LETTER SA;Lo;0;L;;;;;N;;;;;
+0D39;MALAYALAM LETTER HA;Lo;0;L;;;;;N;;;;;
+0D3E;MALAYALAM VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+0D3F;MALAYALAM VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+0D40;MALAYALAM VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+0D41;MALAYALAM VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+0D42;MALAYALAM VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+0D43;MALAYALAM VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+0D46;MALAYALAM VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+0D47;MALAYALAM VOWEL SIGN EE;Mc;0;L;;;;;N;;;;;
+0D48;MALAYALAM VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+0D4A;MALAYALAM VOWEL SIGN O;Mc;0;L;0D46 0D3E;;;;N;;;;;
+0D4B;MALAYALAM VOWEL SIGN OO;Mc;0;L;0D47 0D3E;;;;N;;;;;
+0D4C;MALAYALAM VOWEL SIGN AU;Mc;0;L;0D46 0D57;;;;N;;;;;
+0D4D;MALAYALAM SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+0D57;MALAYALAM AU LENGTH MARK;Mc;0;L;;;;;N;;;;;
+0D60;MALAYALAM LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+0D61;MALAYALAM LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+0D66;MALAYALAM DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0D67;MALAYALAM DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0D68;MALAYALAM DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0D69;MALAYALAM DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0D6A;MALAYALAM DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0D6B;MALAYALAM DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0D6C;MALAYALAM DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0D6D;MALAYALAM DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0D6E;MALAYALAM DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0D6F;MALAYALAM DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0D82;SINHALA SIGN ANUSVARAYA;Mc;0;L;;;;;N;;;;;
+0D83;SINHALA SIGN VISARGAYA;Mc;0;L;;;;;N;;;;;
+0D85;SINHALA LETTER AYANNA;Lo;0;L;;;;;N;;;;;
+0D86;SINHALA LETTER AAYANNA;Lo;0;L;;;;;N;;;;;
+0D87;SINHALA LETTER AEYANNA;Lo;0;L;;;;;N;;;;;
+0D88;SINHALA LETTER AEEYANNA;Lo;0;L;;;;;N;;;;;
+0D89;SINHALA LETTER IYANNA;Lo;0;L;;;;;N;;;;;
+0D8A;SINHALA LETTER IIYANNA;Lo;0;L;;;;;N;;;;;
+0D8B;SINHALA LETTER UYANNA;Lo;0;L;;;;;N;;;;;
+0D8C;SINHALA LETTER UUYANNA;Lo;0;L;;;;;N;;;;;
+0D8D;SINHALA LETTER IRUYANNA;Lo;0;L;;;;;N;;;;;
+0D8E;SINHALA LETTER IRUUYANNA;Lo;0;L;;;;;N;;;;;
+0D8F;SINHALA LETTER ILUYANNA;Lo;0;L;;;;;N;;;;;
+0D90;SINHALA LETTER ILUUYANNA;Lo;0;L;;;;;N;;;;;
+0D91;SINHALA LETTER EYANNA;Lo;0;L;;;;;N;;;;;
+0D92;SINHALA LETTER EEYANNA;Lo;0;L;;;;;N;;;;;
+0D93;SINHALA LETTER AIYANNA;Lo;0;L;;;;;N;;;;;
+0D94;SINHALA LETTER OYANNA;Lo;0;L;;;;;N;;;;;
+0D95;SINHALA LETTER OOYANNA;Lo;0;L;;;;;N;;;;;
+0D96;SINHALA LETTER AUYANNA;Lo;0;L;;;;;N;;;;;
+0D9A;SINHALA LETTER ALPAPRAANA KAYANNA;Lo;0;L;;;;;N;;;;;
+0D9B;SINHALA LETTER MAHAAPRAANA KAYANNA;Lo;0;L;;;;;N;;;;;
+0D9C;SINHALA LETTER ALPAPRAANA GAYANNA;Lo;0;L;;;;;N;;;;;
+0D9D;SINHALA LETTER MAHAAPRAANA GAYANNA;Lo;0;L;;;;;N;;;;;
+0D9E;SINHALA LETTER KANTAJA NAASIKYAYA;Lo;0;L;;;;;N;;;;;
+0D9F;SINHALA LETTER SANYAKA GAYANNA;Lo;0;L;;;;;N;;;;;
+0DA0;SINHALA LETTER ALPAPRAANA CAYANNA;Lo;0;L;;;;;N;;;;;
+0DA1;SINHALA LETTER MAHAAPRAANA CAYANNA;Lo;0;L;;;;;N;;;;;
+0DA2;SINHALA LETTER ALPAPRAANA JAYANNA;Lo;0;L;;;;;N;;;;;
+0DA3;SINHALA LETTER MAHAAPRAANA JAYANNA;Lo;0;L;;;;;N;;;;;
+0DA4;SINHALA LETTER TAALUJA NAASIKYAYA;Lo;0;L;;;;;N;;;;;
+0DA5;SINHALA LETTER TAALUJA SANYOOGA NAAKSIKYAYA;Lo;0;L;;;;;N;;;;;
+0DA6;SINHALA LETTER SANYAKA JAYANNA;Lo;0;L;;;;;N;;;;;
+0DA7;SINHALA LETTER ALPAPRAANA TTAYANNA;Lo;0;L;;;;;N;;;;;
+0DA8;SINHALA LETTER MAHAAPRAANA TTAYANNA;Lo;0;L;;;;;N;;;;;
+0DA9;SINHALA LETTER ALPAPRAANA DDAYANNA;Lo;0;L;;;;;N;;;;;
+0DAA;SINHALA LETTER MAHAAPRAANA DDAYANNA;Lo;0;L;;;;;N;;;;;
+0DAB;SINHALA LETTER MUURDHAJA NAYANNA;Lo;0;L;;;;;N;;;;;
+0DAC;SINHALA LETTER SANYAKA DDAYANNA;Lo;0;L;;;;;N;;;;;
+0DAD;SINHALA LETTER ALPAPRAANA TAYANNA;Lo;0;L;;;;;N;;;;;
+0DAE;SINHALA LETTER MAHAAPRAANA TAYANNA;Lo;0;L;;;;;N;;;;;
+0DAF;SINHALA LETTER ALPAPRAANA DAYANNA;Lo;0;L;;;;;N;;;;;
+0DB0;SINHALA LETTER MAHAAPRAANA DAYANNA;Lo;0;L;;;;;N;;;;;
+0DB1;SINHALA LETTER DANTAJA NAYANNA;Lo;0;L;;;;;N;;;;;
+0DB3;SINHALA LETTER SANYAKA DAYANNA;Lo;0;L;;;;;N;;;;;
+0DB4;SINHALA LETTER ALPAPRAANA PAYANNA;Lo;0;L;;;;;N;;;;;
+0DB5;SINHALA LETTER MAHAAPRAANA PAYANNA;Lo;0;L;;;;;N;;;;;
+0DB6;SINHALA LETTER ALPAPRAANA BAYANNA;Lo;0;L;;;;;N;;;;;
+0DB7;SINHALA LETTER MAHAAPRAANA BAYANNA;Lo;0;L;;;;;N;;;;;
+0DB8;SINHALA LETTER MAYANNA;Lo;0;L;;;;;N;;;;;
+0DB9;SINHALA LETTER AMBA BAYANNA;Lo;0;L;;;;;N;;;;;
+0DBA;SINHALA LETTER YAYANNA;Lo;0;L;;;;;N;;;;;
+0DBB;SINHALA LETTER RAYANNA;Lo;0;L;;;;;N;;;;;
+0DBD;SINHALA LETTER DANTAJA LAYANNA;Lo;0;L;;;;;N;;;;;
+0DC0;SINHALA LETTER VAYANNA;Lo;0;L;;;;;N;;;;;
+0DC1;SINHALA LETTER TAALUJA SAYANNA;Lo;0;L;;;;;N;;;;;
+0DC2;SINHALA LETTER MUURDHAJA SAYANNA;Lo;0;L;;;;;N;;;;;
+0DC3;SINHALA LETTER DANTAJA SAYANNA;Lo;0;L;;;;;N;;;;;
+0DC4;SINHALA LETTER HAYANNA;Lo;0;L;;;;;N;;;;;
+0DC5;SINHALA LETTER MUURDHAJA LAYANNA;Lo;0;L;;;;;N;;;;;
+0DC6;SINHALA LETTER FAYANNA;Lo;0;L;;;;;N;;;;;
+0DCA;SINHALA SIGN AL-LAKUNA;Mn;9;NSM;;;;;N;;;;;
+0DCF;SINHALA VOWEL SIGN AELA-PILLA;Mc;0;L;;;;;N;;;;;
+0DD0;SINHALA VOWEL SIGN KETTI AEDA-PILLA;Mc;0;L;;;;;N;;;;;
+0DD1;SINHALA VOWEL SIGN DIGA AEDA-PILLA;Mc;0;L;;;;;N;;;;;
+0DD2;SINHALA VOWEL SIGN KETTI IS-PILLA;Mn;0;NSM;;;;;N;;;;;
+0DD3;SINHALA VOWEL SIGN DIGA IS-PILLA;Mn;0;NSM;;;;;N;;;;;
+0DD4;SINHALA VOWEL SIGN KETTI PAA-PILLA;Mn;0;NSM;;;;;N;;;;;
+0DD6;SINHALA VOWEL SIGN DIGA PAA-PILLA;Mn;0;NSM;;;;;N;;;;;
+0DD8;SINHALA VOWEL SIGN GAETTA-PILLA;Mc;0;L;;;;;N;;;;;
+0DD9;SINHALA VOWEL SIGN KOMBUVA;Mc;0;L;;;;;N;;;;;
+0DDA;SINHALA VOWEL SIGN DIGA KOMBUVA;Mc;0;L;0DD9 0DCA;;;;N;;;;;
+0DDB;SINHALA VOWEL SIGN KOMBU DEKA;Mc;0;L;;;;;N;;;;;
+0DDC;SINHALA VOWEL SIGN KOMBUVA HAA AELA-PILLA;Mc;0;L;0DD9 0DCF;;;;N;;;;;
+0DDD;SINHALA VOWEL SIGN KOMBUVA HAA DIGA AELA-PILLA;Mc;0;L;0DDC 0DCA;;;;N;;;;;
+0DDE;SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA;Mc;0;L;0DD9 0DDF;;;;N;;;;;
+0DDF;SINHALA VOWEL SIGN GAYANUKITTA;Mc;0;L;;;;;N;;;;;
+0DF2;SINHALA VOWEL SIGN DIGA GAETTA-PILLA;Mc;0;L;;;;;N;;;;;
+0DF3;SINHALA VOWEL SIGN DIGA GAYANUKITTA;Mc;0;L;;;;;N;;;;;
+0DF4;SINHALA PUNCTUATION KUNDDALIYA;Po;0;L;;;;;N;;;;;
+0E01;THAI CHARACTER KO KAI;Lo;0;L;;;;;N;THAI LETTER KO KAI;;;;
+0E02;THAI CHARACTER KHO KHAI;Lo;0;L;;;;;N;THAI LETTER KHO KHAI;;;;
+0E03;THAI CHARACTER KHO KHUAT;Lo;0;L;;;;;N;THAI LETTER KHO KHUAT;;;;
+0E04;THAI CHARACTER KHO KHWAI;Lo;0;L;;;;;N;THAI LETTER KHO KHWAI;;;;
+0E05;THAI CHARACTER KHO KHON;Lo;0;L;;;;;N;THAI LETTER KHO KHON;;;;
+0E06;THAI CHARACTER KHO RAKHANG;Lo;0;L;;;;;N;THAI LETTER KHO RAKHANG;;;;
+0E07;THAI CHARACTER NGO NGU;Lo;0;L;;;;;N;THAI LETTER NGO NGU;;;;
+0E08;THAI CHARACTER CHO CHAN;Lo;0;L;;;;;N;THAI LETTER CHO CHAN;;;;
+0E09;THAI CHARACTER CHO CHING;Lo;0;L;;;;;N;THAI LETTER CHO CHING;;;;
+0E0A;THAI CHARACTER CHO CHANG;Lo;0;L;;;;;N;THAI LETTER CHO CHANG;;;;
+0E0B;THAI CHARACTER SO SO;Lo;0;L;;;;;N;THAI LETTER SO SO;;;;
+0E0C;THAI CHARACTER CHO CHOE;Lo;0;L;;;;;N;THAI LETTER CHO CHOE;;;;
+0E0D;THAI CHARACTER YO YING;Lo;0;L;;;;;N;THAI LETTER YO YING;;;;
+0E0E;THAI CHARACTER DO CHADA;Lo;0;L;;;;;N;THAI LETTER DO CHADA;;;;
+0E0F;THAI CHARACTER TO PATAK;Lo;0;L;;;;;N;THAI LETTER TO PATAK;;;;
+0E10;THAI CHARACTER THO THAN;Lo;0;L;;;;;N;THAI LETTER THO THAN;;;;
+0E11;THAI CHARACTER THO NANGMONTHO;Lo;0;L;;;;;N;THAI LETTER THO NANGMONTHO;;;;
+0E12;THAI CHARACTER THO PHUTHAO;Lo;0;L;;;;;N;THAI LETTER THO PHUTHAO;;;;
+0E13;THAI CHARACTER NO NEN;Lo;0;L;;;;;N;THAI LETTER NO NEN;;;;
+0E14;THAI CHARACTER DO DEK;Lo;0;L;;;;;N;THAI LETTER DO DEK;;;;
+0E15;THAI CHARACTER TO TAO;Lo;0;L;;;;;N;THAI LETTER TO TAO;;;;
+0E16;THAI CHARACTER THO THUNG;Lo;0;L;;;;;N;THAI LETTER THO THUNG;;;;
+0E17;THAI CHARACTER THO THAHAN;Lo;0;L;;;;;N;THAI LETTER THO THAHAN;;;;
+0E18;THAI CHARACTER THO THONG;Lo;0;L;;;;;N;THAI LETTER THO THONG;;;;
+0E19;THAI CHARACTER NO NU;Lo;0;L;;;;;N;THAI LETTER NO NU;;;;
+0E1A;THAI CHARACTER BO BAIMAI;Lo;0;L;;;;;N;THAI LETTER BO BAIMAI;;;;
+0E1B;THAI CHARACTER PO PLA;Lo;0;L;;;;;N;THAI LETTER PO PLA;;;;
+0E1C;THAI CHARACTER PHO PHUNG;Lo;0;L;;;;;N;THAI LETTER PHO PHUNG;;;;
+0E1D;THAI CHARACTER FO FA;Lo;0;L;;;;;N;THAI LETTER FO FA;;;;
+0E1E;THAI CHARACTER PHO PHAN;Lo;0;L;;;;;N;THAI LETTER PHO PHAN;;;;
+0E1F;THAI CHARACTER FO FAN;Lo;0;L;;;;;N;THAI LETTER FO FAN;;;;
+0E20;THAI CHARACTER PHO SAMPHAO;Lo;0;L;;;;;N;THAI LETTER PHO SAMPHAO;;;;
+0E21;THAI CHARACTER MO MA;Lo;0;L;;;;;N;THAI LETTER MO MA;;;;
+0E22;THAI CHARACTER YO YAK;Lo;0;L;;;;;N;THAI LETTER YO YAK;;;;
+0E23;THAI CHARACTER RO RUA;Lo;0;L;;;;;N;THAI LETTER RO RUA;;;;
+0E24;THAI CHARACTER RU;Lo;0;L;;;;;N;THAI LETTER RU;;;;
+0E25;THAI CHARACTER LO LING;Lo;0;L;;;;;N;THAI LETTER LO LING;;;;
+0E26;THAI CHARACTER LU;Lo;0;L;;;;;N;THAI LETTER LU;;;;
+0E27;THAI CHARACTER WO WAEN;Lo;0;L;;;;;N;THAI LETTER WO WAEN;;;;
+0E28;THAI CHARACTER SO SALA;Lo;0;L;;;;;N;THAI LETTER SO SALA;;;;
+0E29;THAI CHARACTER SO RUSI;Lo;0;L;;;;;N;THAI LETTER SO RUSI;;;;
+0E2A;THAI CHARACTER SO SUA;Lo;0;L;;;;;N;THAI LETTER SO SUA;;;;
+0E2B;THAI CHARACTER HO HIP;Lo;0;L;;;;;N;THAI LETTER HO HIP;;;;
+0E2C;THAI CHARACTER LO CHULA;Lo;0;L;;;;;N;THAI LETTER LO CHULA;;;;
+0E2D;THAI CHARACTER O ANG;Lo;0;L;;;;;N;THAI LETTER O ANG;;;;
+0E2E;THAI CHARACTER HO NOKHUK;Lo;0;L;;;;;N;THAI LETTER HO NOK HUK;;;;
+0E2F;THAI CHARACTER PAIYANNOI;Lo;0;L;;;;;N;THAI PAI YAN NOI;paiyan noi;;;
+0E30;THAI CHARACTER SARA A;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA A;;;;
+0E31;THAI CHARACTER MAI HAN-AKAT;Mn;0;NSM;;;;;N;THAI VOWEL SIGN MAI HAN-AKAT;;;;
+0E32;THAI CHARACTER SARA AA;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA AA;;;;
+0E33;THAI CHARACTER SARA AM;Lo;0;L;<compat> 0E4D 0E32;;;;N;THAI VOWEL SIGN SARA AM;;;;
+0E34;THAI CHARACTER SARA I;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA I;;;;
+0E35;THAI CHARACTER SARA II;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA II;;;;
+0E36;THAI CHARACTER SARA UE;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA UE;;;;
+0E37;THAI CHARACTER SARA UEE;Mn;0;NSM;;;;;N;THAI VOWEL SIGN SARA UEE;sara uue;;;
+0E38;THAI CHARACTER SARA U;Mn;103;NSM;;;;;N;THAI VOWEL SIGN SARA U;;;;
+0E39;THAI CHARACTER SARA UU;Mn;103;NSM;;;;;N;THAI VOWEL SIGN SARA UU;;;;
+0E3A;THAI CHARACTER PHINTHU;Mn;9;NSM;;;;;N;THAI VOWEL SIGN PHINTHU;;;;
+0E3F;THAI CURRENCY SYMBOL BAHT;Sc;0;ET;;;;;N;THAI BAHT SIGN;;;;
+0E40;THAI CHARACTER SARA E;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA E;;;;
+0E41;THAI CHARACTER SARA AE;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA AE;;;;
+0E42;THAI CHARACTER SARA O;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA O;;;;
+0E43;THAI CHARACTER SARA AI MAIMUAN;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA MAI MUAN;sara ai mai muan;;;
+0E44;THAI CHARACTER SARA AI MAIMALAI;Lo;0;L;;;;;N;THAI VOWEL SIGN SARA MAI MALAI;sara ai mai malai;;;
+0E45;THAI CHARACTER LAKKHANGYAO;Lo;0;L;;;;;N;THAI LAK KHANG YAO;lakkhang yao;;;
+0E46;THAI CHARACTER MAIYAMOK;Lm;0;L;;;;;N;THAI MAI YAMOK;mai yamok;;;
+0E47;THAI CHARACTER MAITAIKHU;Mn;0;NSM;;;;;N;THAI VOWEL SIGN MAI TAI KHU;mai taikhu;;;
+0E48;THAI CHARACTER MAI EK;Mn;107;NSM;;;;;N;THAI TONE MAI EK;;;;
+0E49;THAI CHARACTER MAI THO;Mn;107;NSM;;;;;N;THAI TONE MAI THO;;;;
+0E4A;THAI CHARACTER MAI TRI;Mn;107;NSM;;;;;N;THAI TONE MAI TRI;;;;
+0E4B;THAI CHARACTER MAI CHATTAWA;Mn;107;NSM;;;;;N;THAI TONE MAI CHATTAWA;;;;
+0E4C;THAI CHARACTER THANTHAKHAT;Mn;0;NSM;;;;;N;THAI THANTHAKHAT;;;;
+0E4D;THAI CHARACTER NIKHAHIT;Mn;0;NSM;;;;;N;THAI NIKKHAHIT;nikkhahit;;;
+0E4E;THAI CHARACTER YAMAKKAN;Mn;0;NSM;;;;;N;THAI YAMAKKAN;;;;
+0E4F;THAI CHARACTER FONGMAN;Po;0;L;;;;;N;THAI FONGMAN;;;;
+0E50;THAI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0E51;THAI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0E52;THAI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0E53;THAI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0E54;THAI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0E55;THAI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0E56;THAI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0E57;THAI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0E58;THAI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0E59;THAI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0E5A;THAI CHARACTER ANGKHANKHU;Po;0;L;;;;;N;THAI ANGKHANKHU;;;;
+0E5B;THAI CHARACTER KHOMUT;Po;0;L;;;;;N;THAI KHOMUT;;;;
+0E81;LAO LETTER KO;Lo;0;L;;;;;N;;;;;
+0E82;LAO LETTER KHO SUNG;Lo;0;L;;;;;N;;;;;
+0E84;LAO LETTER KHO TAM;Lo;0;L;;;;;N;;;;;
+0E87;LAO LETTER NGO;Lo;0;L;;;;;N;;;;;
+0E88;LAO LETTER CO;Lo;0;L;;;;;N;;;;;
+0E8A;LAO LETTER SO TAM;Lo;0;L;;;;;N;;;;;
+0E8D;LAO LETTER NYO;Lo;0;L;;;;;N;;;;;
+0E94;LAO LETTER DO;Lo;0;L;;;;;N;;;;;
+0E95;LAO LETTER TO;Lo;0;L;;;;;N;;;;;
+0E96;LAO LETTER THO SUNG;Lo;0;L;;;;;N;;;;;
+0E97;LAO LETTER THO TAM;Lo;0;L;;;;;N;;;;;
+0E99;LAO LETTER NO;Lo;0;L;;;;;N;;;;;
+0E9A;LAO LETTER BO;Lo;0;L;;;;;N;;;;;
+0E9B;LAO LETTER PO;Lo;0;L;;;;;N;;;;;
+0E9C;LAO LETTER PHO SUNG;Lo;0;L;;;;;N;;;;;
+0E9D;LAO LETTER FO TAM;Lo;0;L;;;;;N;;;;;
+0E9E;LAO LETTER PHO TAM;Lo;0;L;;;;;N;;;;;
+0E9F;LAO LETTER FO SUNG;Lo;0;L;;;;;N;;;;;
+0EA1;LAO LETTER MO;Lo;0;L;;;;;N;;;;;
+0EA2;LAO LETTER YO;Lo;0;L;;;;;N;;;;;
+0EA3;LAO LETTER LO LING;Lo;0;L;;;;;N;;;;;
+0EA5;LAO LETTER LO LOOT;Lo;0;L;;;;;N;;;;;
+0EA7;LAO LETTER WO;Lo;0;L;;;;;N;;;;;
+0EAA;LAO LETTER SO SUNG;Lo;0;L;;;;;N;;;;;
+0EAB;LAO LETTER HO SUNG;Lo;0;L;;;;;N;;;;;
+0EAD;LAO LETTER O;Lo;0;L;;;;;N;;;;;
+0EAE;LAO LETTER HO TAM;Lo;0;L;;;;;N;;;;;
+0EAF;LAO ELLIPSIS;Lo;0;L;;;;;N;;;;;
+0EB0;LAO VOWEL SIGN A;Lo;0;L;;;;;N;;;;;
+0EB1;LAO VOWEL SIGN MAI KAN;Mn;0;NSM;;;;;N;;;;;
+0EB2;LAO VOWEL SIGN AA;Lo;0;L;;;;;N;;;;;
+0EB3;LAO VOWEL SIGN AM;Lo;0;L;<compat> 0ECD 0EB2;;;;N;;;;;
+0EB4;LAO VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+0EB5;LAO VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+0EB6;LAO VOWEL SIGN Y;Mn;0;NSM;;;;;N;;;;;
+0EB7;LAO VOWEL SIGN YY;Mn;0;NSM;;;;;N;;;;;
+0EB8;LAO VOWEL SIGN U;Mn;118;NSM;;;;;N;;;;;
+0EB9;LAO VOWEL SIGN UU;Mn;118;NSM;;;;;N;;;;;
+0EBB;LAO VOWEL SIGN MAI KON;Mn;0;NSM;;;;;N;;;;;
+0EBC;LAO SEMIVOWEL SIGN LO;Mn;0;NSM;;;;;N;;;;;
+0EBD;LAO SEMIVOWEL SIGN NYO;Lo;0;L;;;;;N;;;;;
+0EC0;LAO VOWEL SIGN E;Lo;0;L;;;;;N;;;;;
+0EC1;LAO VOWEL SIGN EI;Lo;0;L;;;;;N;;;;;
+0EC2;LAO VOWEL SIGN O;Lo;0;L;;;;;N;;;;;
+0EC3;LAO VOWEL SIGN AY;Lo;0;L;;;;;N;;;;;
+0EC4;LAO VOWEL SIGN AI;Lo;0;L;;;;;N;;;;;
+0EC6;LAO KO LA;Lm;0;L;;;;;N;;;;;
+0EC8;LAO TONE MAI EK;Mn;122;NSM;;;;;N;;;;;
+0EC9;LAO TONE MAI THO;Mn;122;NSM;;;;;N;;;;;
+0ECA;LAO TONE MAI TI;Mn;122;NSM;;;;;N;;;;;
+0ECB;LAO TONE MAI CATAWA;Mn;122;NSM;;;;;N;;;;;
+0ECC;LAO CANCELLATION MARK;Mn;0;NSM;;;;;N;;;;;
+0ECD;LAO NIGGAHITA;Mn;0;NSM;;;;;N;;;;;
+0ED0;LAO DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0ED1;LAO DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0ED2;LAO DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0ED3;LAO DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0ED4;LAO DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0ED5;LAO DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0ED6;LAO DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0ED7;LAO DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0ED8;LAO DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0ED9;LAO DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0EDC;LAO HO NO;Lo;0;L;<compat> 0EAB 0E99;;;;N;;;;;
+0EDD;LAO HO MO;Lo;0;L;<compat> 0EAB 0EA1;;;;N;;;;;
+0F00;TIBETAN SYLLABLE OM;Lo;0;L;;;;;N;;;;;
+0F01;TIBETAN MARK GTER YIG MGO TRUNCATED A;So;0;L;;;;;N;;ter yik go a thung;;;
+0F02;TIBETAN MARK GTER YIG MGO -UM RNAM BCAD MA;So;0;L;;;;;N;;ter yik go wum nam chey ma;;;
+0F03;TIBETAN MARK GTER YIG MGO -UM GTER TSHEG MA;So;0;L;;;;;N;;ter yik go wum ter tsek ma;;;
+0F04;TIBETAN MARK INITIAL YIG MGO MDUN MA;Po;0;L;;;;;N;TIBETAN SINGLE ORNAMENT;yik go dun ma;;;
+0F05;TIBETAN MARK CLOSING YIG MGO SGAB MA;Po;0;L;;;;;N;;yik go kab ma;;;
+0F06;TIBETAN MARK CARET YIG MGO PHUR SHAD MA;Po;0;L;;;;;N;;yik go pur shey ma;;;
+0F07;TIBETAN MARK YIG MGO TSHEG SHAD MA;Po;0;L;;;;;N;;yik go tsek shey ma;;;
+0F08;TIBETAN MARK SBRUL SHAD;Po;0;L;;;;;N;TIBETAN RGYANSHAD;drul shey;;;
+0F09;TIBETAN MARK BSKUR YIG MGO;Po;0;L;;;;;N;;kur yik go;;;
+0F0A;TIBETAN MARK BKA- SHOG YIG MGO;Po;0;L;;;;;N;;ka sho yik go;;;
+0F0B;TIBETAN MARK INTERSYLLABIC TSHEG;Po;0;L;;;;;N;TIBETAN TSEG;tsek;;;
+0F0C;TIBETAN MARK DELIMITER TSHEG BSTAR;Po;0;L;<noBreak> 0F0B;;;;N;;tsek tar;;;
+0F0D;TIBETAN MARK SHAD;Po;0;L;;;;;N;TIBETAN SHAD;shey;;;
+0F0E;TIBETAN MARK NYIS SHAD;Po;0;L;;;;;N;TIBETAN DOUBLE SHAD;nyi shey;;;
+0F0F;TIBETAN MARK TSHEG SHAD;Po;0;L;;;;;N;;tsek shey;;;
+0F10;TIBETAN MARK NYIS TSHEG SHAD;Po;0;L;;;;;N;;nyi tsek shey;;;
+0F11;TIBETAN MARK RIN CHEN SPUNGS SHAD;Po;0;L;;;;;N;TIBETAN RINCHANPHUNGSHAD;rinchen pung shey;;;
+0F12;TIBETAN MARK RGYA GRAM SHAD;Po;0;L;;;;;N;;gya tram shey;;;
+0F13;TIBETAN MARK CARET -DZUD RTAGS ME LONG CAN;So;0;L;;;;;N;;dzu ta me long chen;;;
+0F14;TIBETAN MARK GTER TSHEG;So;0;L;;;;;N;TIBETAN COMMA;ter tsek;;;
+0F15;TIBETAN LOGOTYPE SIGN CHAD RTAGS;So;0;L;;;;;N;;che ta;;;
+0F16;TIBETAN LOGOTYPE SIGN LHAG RTAGS;So;0;L;;;;;N;;hlak ta;;;
+0F17;TIBETAN ASTROLOGICAL SIGN SGRA GCAN -CHAR RTAGS;So;0;L;;;;;N;;trachen char ta;;;
+0F18;TIBETAN ASTROLOGICAL SIGN -KHYUD PA;Mn;220;NSM;;;;;N;;kyu pa;;;
+0F19;TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS;Mn;220;NSM;;;;;N;;dong tsu;;;
+0F1A;TIBETAN SIGN RDEL DKAR GCIG;So;0;L;;;;;N;;deka chig;;;
+0F1B;TIBETAN SIGN RDEL DKAR GNYIS;So;0;L;;;;;N;;deka nyi;;;
+0F1C;TIBETAN SIGN RDEL DKAR GSUM;So;0;L;;;;;N;;deka sum;;;
+0F1D;TIBETAN SIGN RDEL NAG GCIG;So;0;L;;;;;N;;dena chig;;;
+0F1E;TIBETAN SIGN RDEL NAG GNYIS;So;0;L;;;;;N;;dena nyi;;;
+0F1F;TIBETAN SIGN RDEL DKAR RDEL NAG;So;0;L;;;;;N;;deka dena;;;
+0F20;TIBETAN DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+0F21;TIBETAN DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+0F22;TIBETAN DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+0F23;TIBETAN DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+0F24;TIBETAN DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+0F25;TIBETAN DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+0F26;TIBETAN DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+0F27;TIBETAN DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+0F28;TIBETAN DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+0F29;TIBETAN DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0F2A;TIBETAN DIGIT HALF ONE;No;0;L;;;;1/2;N;;;;;
+0F2B;TIBETAN DIGIT HALF TWO;No;0;L;;;;3/2;N;;;;;
+0F2C;TIBETAN DIGIT HALF THREE;No;0;L;;;;5/2;N;;;;;
+0F2D;TIBETAN DIGIT HALF FOUR;No;0;L;;;;7/2;N;;;;;
+0F2E;TIBETAN DIGIT HALF FIVE;No;0;L;;;;9/2;N;;;;;
+0F2F;TIBETAN DIGIT HALF SIX;No;0;L;;;;11/2;N;;;;;
+0F30;TIBETAN DIGIT HALF SEVEN;No;0;L;;;;13/2;N;;;;;
+0F31;TIBETAN DIGIT HALF EIGHT;No;0;L;;;;15/2;N;;;;;
+0F32;TIBETAN DIGIT HALF NINE;No;0;L;;;;17/2;N;;;;;
+0F33;TIBETAN DIGIT HALF ZERO;No;0;L;;;;-1/2;N;;;;;
+0F34;TIBETAN MARK BSDUS RTAGS;So;0;L;;;;;N;;du ta;;;
+0F35;TIBETAN MARK NGAS BZUNG NYI ZLA;Mn;220;NSM;;;;;N;TIBETAN HONORIFIC UNDER RING;nge zung nyi da;;;
+0F36;TIBETAN MARK CARET -DZUD RTAGS BZHI MIG CAN;So;0;L;;;;;N;;dzu ta shi mig chen;;;
+0F37;TIBETAN MARK NGAS BZUNG SGOR RTAGS;Mn;220;NSM;;;;;N;TIBETAN UNDER RING;nge zung gor ta;;;
+0F38;TIBETAN MARK CHE MGO;So;0;L;;;;;N;;che go;;;
+0F39;TIBETAN MARK TSA -PHRU;Mn;216;NSM;;;;;N;TIBETAN LENITION MARK;tsa tru;;;
+0F3A;TIBETAN MARK GUG RTAGS GYON;Ps;0;ON;;;;;N;;gug ta yun;;;
+0F3B;TIBETAN MARK GUG RTAGS GYAS;Pe;0;ON;;;;;N;;gug ta ye;;;
+0F3C;TIBETAN MARK ANG KHANG GYON;Ps;0;ON;;;;;N;TIBETAN LEFT BRACE;ang kang yun;;;
+0F3D;TIBETAN MARK ANG KHANG GYAS;Pe;0;ON;;;;;N;TIBETAN RIGHT BRACE;ang kang ye;;;
+0F3E;TIBETAN SIGN YAR TSHES;Mc;0;L;;;;;N;;yar tse;;;
+0F3F;TIBETAN SIGN MAR TSHES;Mc;0;L;;;;;N;;mar tse;;;
+0F40;TIBETAN LETTER KA;Lo;0;L;;;;;N;;;;;
+0F41;TIBETAN LETTER KHA;Lo;0;L;;;;;N;;;;;
+0F42;TIBETAN LETTER GA;Lo;0;L;;;;;N;;;;;
+0F43;TIBETAN LETTER GHA;Lo;0;L;0F42 0FB7;;;;N;;;;;
+0F44;TIBETAN LETTER NGA;Lo;0;L;;;;;N;;;;;
+0F45;TIBETAN LETTER CA;Lo;0;L;;;;;N;;;;;
+0F46;TIBETAN LETTER CHA;Lo;0;L;;;;;N;;;;;
+0F47;TIBETAN LETTER JA;Lo;0;L;;;;;N;;;;;
+0F49;TIBETAN LETTER NYA;Lo;0;L;;;;;N;;;;;
+0F4A;TIBETAN LETTER TTA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED TA;;;;
+0F4B;TIBETAN LETTER TTHA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED THA;;;;
+0F4C;TIBETAN LETTER DDA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED DA;;;;
+0F4D;TIBETAN LETTER DDHA;Lo;0;L;0F4C 0FB7;;;;N;;;;;
+0F4E;TIBETAN LETTER NNA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED NA;;;;
+0F4F;TIBETAN LETTER TA;Lo;0;L;;;;;N;;;;;
+0F50;TIBETAN LETTER THA;Lo;0;L;;;;;N;;;;;
+0F51;TIBETAN LETTER DA;Lo;0;L;;;;;N;;;;;
+0F52;TIBETAN LETTER DHA;Lo;0;L;0F51 0FB7;;;;N;;;;;
+0F53;TIBETAN LETTER NA;Lo;0;L;;;;;N;;;;;
+0F54;TIBETAN LETTER PA;Lo;0;L;;;;;N;;;;;
+0F55;TIBETAN LETTER PHA;Lo;0;L;;;;;N;;;;;
+0F56;TIBETAN LETTER BA;Lo;0;L;;;;;N;;;;;
+0F57;TIBETAN LETTER BHA;Lo;0;L;0F56 0FB7;;;;N;;;;;
+0F58;TIBETAN LETTER MA;Lo;0;L;;;;;N;;;;;
+0F59;TIBETAN LETTER TSA;Lo;0;L;;;;;N;;;;;
+0F5A;TIBETAN LETTER TSHA;Lo;0;L;;;;;N;;;;;
+0F5B;TIBETAN LETTER DZA;Lo;0;L;;;;;N;;;;;
+0F5C;TIBETAN LETTER DZHA;Lo;0;L;0F5B 0FB7;;;;N;;;;;
+0F5D;TIBETAN LETTER WA;Lo;0;L;;;;;N;;;;;
+0F5E;TIBETAN LETTER ZHA;Lo;0;L;;;;;N;;;;;
+0F5F;TIBETAN LETTER ZA;Lo;0;L;;;;;N;;;;;
+0F60;TIBETAN LETTER -A;Lo;0;L;;;;;N;TIBETAN LETTER AA;;;;
+0F61;TIBETAN LETTER YA;Lo;0;L;;;;;N;;;;;
+0F62;TIBETAN LETTER RA;Lo;0;L;;;;;N;;*;;;
+0F63;TIBETAN LETTER LA;Lo;0;L;;;;;N;;;;;
+0F64;TIBETAN LETTER SHA;Lo;0;L;;;;;N;;;;;
+0F65;TIBETAN LETTER SSA;Lo;0;L;;;;;N;TIBETAN LETTER REVERSED SHA;;;;
+0F66;TIBETAN LETTER SA;Lo;0;L;;;;;N;;;;;
+0F67;TIBETAN LETTER HA;Lo;0;L;;;;;N;;;;;
+0F68;TIBETAN LETTER A;Lo;0;L;;;;;N;;;;;
+0F69;TIBETAN LETTER KSSA;Lo;0;L;0F40 0FB5;;;;N;;;;;
+0F6A;TIBETAN LETTER FIXED-FORM RA;Lo;0;L;;;;;N;;*;;;
+0F71;TIBETAN VOWEL SIGN AA;Mn;129;NSM;;;;;N;;;;;
+0F72;TIBETAN VOWEL SIGN I;Mn;130;NSM;;;;;N;;;;;
+0F73;TIBETAN VOWEL SIGN II;Mn;0;NSM;0F71 0F72;;;;N;;;;;
+0F74;TIBETAN VOWEL SIGN U;Mn;132;NSM;;;;;N;;;;;
+0F75;TIBETAN VOWEL SIGN UU;Mn;0;NSM;0F71 0F74;;;;N;;;;;
+0F76;TIBETAN VOWEL SIGN VOCALIC R;Mn;0;NSM;0FB2 0F80;;;;N;;;;;
+0F77;TIBETAN VOWEL SIGN VOCALIC RR;Mn;0;NSM;<compat> 0FB2 0F81;;;;N;;;;;
+0F78;TIBETAN VOWEL SIGN VOCALIC L;Mn;0;NSM;0FB3 0F80;;;;N;;;;;
+0F79;TIBETAN VOWEL SIGN VOCALIC LL;Mn;0;NSM;<compat> 0FB3 0F81;;;;N;;;;;
+0F7A;TIBETAN VOWEL SIGN E;Mn;130;NSM;;;;;N;;;;;
+0F7B;TIBETAN VOWEL SIGN EE;Mn;130;NSM;;;;;N;TIBETAN VOWEL SIGN AI;;;;
+0F7C;TIBETAN VOWEL SIGN O;Mn;130;NSM;;;;;N;;;;;
+0F7D;TIBETAN VOWEL SIGN OO;Mn;130;NSM;;;;;N;TIBETAN VOWEL SIGN AU;;;;
+0F7E;TIBETAN SIGN RJES SU NGA RO;Mn;0;NSM;;;;;N;TIBETAN ANUSVARA;je su nga ro;;;
+0F7F;TIBETAN SIGN RNAM BCAD;Mc;0;L;;;;;N;TIBETAN VISARGA;nam chey;;;
+0F80;TIBETAN VOWEL SIGN REVERSED I;Mn;130;NSM;;;;;N;TIBETAN VOWEL SIGN SHORT I;;;;
+0F81;TIBETAN VOWEL SIGN REVERSED II;Mn;0;NSM;0F71 0F80;;;;N;;;;;
+0F82;TIBETAN SIGN NYI ZLA NAA DA;Mn;230;NSM;;;;;N;TIBETAN CANDRABINDU WITH ORNAMENT;nyi da na da;;;
+0F83;TIBETAN SIGN SNA LDAN;Mn;230;NSM;;;;;N;TIBETAN CANDRABINDU;nan de;;;
+0F84;TIBETAN MARK HALANTA;Mn;9;NSM;;;;;N;TIBETAN VIRAMA;;;;
+0F85;TIBETAN MARK PALUTA;Po;0;L;;;;;N;TIBETAN CHUCHENYIGE;;;;
+0F86;TIBETAN SIGN LCI RTAGS;Mn;230;NSM;;;;;N;;ji ta;;;
+0F87;TIBETAN SIGN YANG RTAGS;Mn;230;NSM;;;;;N;;yang ta;;;
+0F88;TIBETAN SIGN LCE TSA CAN;Lo;0;L;;;;;N;;che tsa chen;;;
+0F89;TIBETAN SIGN MCHU CAN;Lo;0;L;;;;;N;;chu chen;;;
+0F8A;TIBETAN SIGN GRU CAN RGYINGS;Lo;0;L;;;;;N;;tru chen ging;;;
+0F8B;TIBETAN SIGN GRU MED RGYINGS;Lo;0;L;;;;;N;;tru me ging;;;
+0F90;TIBETAN SUBJOINED LETTER KA;Mn;0;NSM;;;;;N;;;;;
+0F91;TIBETAN SUBJOINED LETTER KHA;Mn;0;NSM;;;;;N;;;;;
+0F92;TIBETAN SUBJOINED LETTER GA;Mn;0;NSM;;;;;N;;;;;
+0F93;TIBETAN SUBJOINED LETTER GHA;Mn;0;NSM;0F92 0FB7;;;;N;;;;;
+0F94;TIBETAN SUBJOINED LETTER NGA;Mn;0;NSM;;;;;N;;;;;
+0F95;TIBETAN SUBJOINED LETTER CA;Mn;0;NSM;;;;;N;;;;;
+0F96;TIBETAN SUBJOINED LETTER CHA;Mn;0;NSM;;;;;N;;;;;
+0F97;TIBETAN SUBJOINED LETTER JA;Mn;0;NSM;;;;;N;;;;;
+0F99;TIBETAN SUBJOINED LETTER NYA;Mn;0;NSM;;;;;N;;;;;
+0F9A;TIBETAN SUBJOINED LETTER TTA;Mn;0;NSM;;;;;N;;;;;
+0F9B;TIBETAN SUBJOINED LETTER TTHA;Mn;0;NSM;;;;;N;;;;;
+0F9C;TIBETAN SUBJOINED LETTER DDA;Mn;0;NSM;;;;;N;;;;;
+0F9D;TIBETAN SUBJOINED LETTER DDHA;Mn;0;NSM;0F9C 0FB7;;;;N;;;;;
+0F9E;TIBETAN SUBJOINED LETTER NNA;Mn;0;NSM;;;;;N;;;;;
+0F9F;TIBETAN SUBJOINED LETTER TA;Mn;0;NSM;;;;;N;;;;;
+0FA0;TIBETAN SUBJOINED LETTER THA;Mn;0;NSM;;;;;N;;;;;
+0FA1;TIBETAN SUBJOINED LETTER DA;Mn;0;NSM;;;;;N;;;;;
+0FA2;TIBETAN SUBJOINED LETTER DHA;Mn;0;NSM;0FA1 0FB7;;;;N;;;;;
+0FA3;TIBETAN SUBJOINED LETTER NA;Mn;0;NSM;;;;;N;;;;;
+0FA4;TIBETAN SUBJOINED LETTER PA;Mn;0;NSM;;;;;N;;;;;
+0FA5;TIBETAN SUBJOINED LETTER PHA;Mn;0;NSM;;;;;N;;;;;
+0FA6;TIBETAN SUBJOINED LETTER BA;Mn;0;NSM;;;;;N;;;;;
+0FA7;TIBETAN SUBJOINED LETTER BHA;Mn;0;NSM;0FA6 0FB7;;;;N;;;;;
+0FA8;TIBETAN SUBJOINED LETTER MA;Mn;0;NSM;;;;;N;;;;;
+0FA9;TIBETAN SUBJOINED LETTER TSA;Mn;0;NSM;;;;;N;;;;;
+0FAA;TIBETAN SUBJOINED LETTER TSHA;Mn;0;NSM;;;;;N;;;;;
+0FAB;TIBETAN SUBJOINED LETTER DZA;Mn;0;NSM;;;;;N;;;;;
+0FAC;TIBETAN SUBJOINED LETTER DZHA;Mn;0;NSM;0FAB 0FB7;;;;N;;;;;
+0FAD;TIBETAN SUBJOINED LETTER WA;Mn;0;NSM;;;;;N;;*;;;
+0FAE;TIBETAN SUBJOINED LETTER ZHA;Mn;0;NSM;;;;;N;;;;;
+0FAF;TIBETAN SUBJOINED LETTER ZA;Mn;0;NSM;;;;;N;;;;;
+0FB0;TIBETAN SUBJOINED LETTER -A;Mn;0;NSM;;;;;N;;;;;
+0FB1;TIBETAN SUBJOINED LETTER YA;Mn;0;NSM;;;;;N;;*;;;
+0FB2;TIBETAN SUBJOINED LETTER RA;Mn;0;NSM;;;;;N;;*;;;
+0FB3;TIBETAN SUBJOINED LETTER LA;Mn;0;NSM;;;;;N;;;;;
+0FB4;TIBETAN SUBJOINED LETTER SHA;Mn;0;NSM;;;;;N;;;;;
+0FB5;TIBETAN SUBJOINED LETTER SSA;Mn;0;NSM;;;;;N;;;;;
+0FB6;TIBETAN SUBJOINED LETTER SA;Mn;0;NSM;;;;;N;;;;;
+0FB7;TIBETAN SUBJOINED LETTER HA;Mn;0;NSM;;;;;N;;;;;
+0FB8;TIBETAN SUBJOINED LETTER A;Mn;0;NSM;;;;;N;;;;;
+0FB9;TIBETAN SUBJOINED LETTER KSSA;Mn;0;NSM;0F90 0FB5;;;;N;;;;;
+0FBA;TIBETAN SUBJOINED LETTER FIXED-FORM WA;Mn;0;NSM;;;;;N;;*;;;
+0FBB;TIBETAN SUBJOINED LETTER FIXED-FORM YA;Mn;0;NSM;;;;;N;;*;;;
+0FBC;TIBETAN SUBJOINED LETTER FIXED-FORM RA;Mn;0;NSM;;;;;N;;*;;;
+0FBE;TIBETAN KU RU KHA;So;0;L;;;;;N;;kuruka;;;
+0FBF;TIBETAN KU RU KHA BZHI MIG CAN;So;0;L;;;;;N;;kuruka shi mik chen;;;
+0FC0;TIBETAN CANTILLATION SIGN HEAVY BEAT;So;0;L;;;;;N;;;;;
+0FC1;TIBETAN CANTILLATION SIGN LIGHT BEAT;So;0;L;;;;;N;;;;;
+0FC2;TIBETAN CANTILLATION SIGN CANG TE-U;So;0;L;;;;;N;;chang tyu;;;
+0FC3;TIBETAN CANTILLATION SIGN SBUB -CHAL;So;0;L;;;;;N;;bub chey;;;
+0FC4;TIBETAN SYMBOL DRIL BU;So;0;L;;;;;N;;drilbu;;;
+0FC5;TIBETAN SYMBOL RDO RJE;So;0;L;;;;;N;;dorje;;;
+0FC6;TIBETAN SYMBOL PADMA GDAN;Mn;220;NSM;;;;;N;;pema den;;;
+0FC7;TIBETAN SYMBOL RDO RJE RGYA GRAM;So;0;L;;;;;N;;dorje gya dram;;;
+0FC8;TIBETAN SYMBOL PHUR PA;So;0;L;;;;;N;;phurba;;;
+0FC9;TIBETAN SYMBOL NOR BU;So;0;L;;;;;N;;norbu;;;
+0FCA;TIBETAN SYMBOL NOR BU NYIS -KHYIL;So;0;L;;;;;N;;norbu nyi khyi;;;
+0FCB;TIBETAN SYMBOL NOR BU GSUM -KHYIL;So;0;L;;;;;N;;norbu sum khyi;;;
+0FCC;TIBETAN SYMBOL NOR BU BZHI -KHYIL;So;0;L;;;;;N;;norbu shi khyi;;;
+0FCF;TIBETAN SIGN RDEL NAG GSUM;So;0;L;;;;;N;;dena sum;;;
+1000;MYANMAR LETTER KA;Lo;0;L;;;;;N;;;;;
+1001;MYANMAR LETTER KHA;Lo;0;L;;;;;N;;;;;
+1002;MYANMAR LETTER GA;Lo;0;L;;;;;N;;;;;
+1003;MYANMAR LETTER GHA;Lo;0;L;;;;;N;;;;;
+1004;MYANMAR LETTER NGA;Lo;0;L;;;;;N;;;;;
+1005;MYANMAR LETTER CA;Lo;0;L;;;;;N;;;;;
+1006;MYANMAR LETTER CHA;Lo;0;L;;;;;N;;;;;
+1007;MYANMAR LETTER JA;Lo;0;L;;;;;N;;;;;
+1008;MYANMAR LETTER JHA;Lo;0;L;;;;;N;;;;;
+1009;MYANMAR LETTER NYA;Lo;0;L;;;;;N;;;;;
+100A;MYANMAR LETTER NNYA;Lo;0;L;;;;;N;;;;;
+100B;MYANMAR LETTER TTA;Lo;0;L;;;;;N;;;;;
+100C;MYANMAR LETTER TTHA;Lo;0;L;;;;;N;;;;;
+100D;MYANMAR LETTER DDA;Lo;0;L;;;;;N;;;;;
+100E;MYANMAR LETTER DDHA;Lo;0;L;;;;;N;;;;;
+100F;MYANMAR LETTER NNA;Lo;0;L;;;;;N;;;;;
+1010;MYANMAR LETTER TA;Lo;0;L;;;;;N;;;;;
+1011;MYANMAR LETTER THA;Lo;0;L;;;;;N;;;;;
+1012;MYANMAR LETTER DA;Lo;0;L;;;;;N;;;;;
+1013;MYANMAR LETTER DHA;Lo;0;L;;;;;N;;;;;
+1014;MYANMAR LETTER NA;Lo;0;L;;;;;N;;;;;
+1015;MYANMAR LETTER PA;Lo;0;L;;;;;N;;;;;
+1016;MYANMAR LETTER PHA;Lo;0;L;;;;;N;;;;;
+1017;MYANMAR LETTER BA;Lo;0;L;;;;;N;;;;;
+1018;MYANMAR LETTER BHA;Lo;0;L;;;;;N;;;;;
+1019;MYANMAR LETTER MA;Lo;0;L;;;;;N;;;;;
+101A;MYANMAR LETTER YA;Lo;0;L;;;;;N;;;;;
+101B;MYANMAR LETTER RA;Lo;0;L;;;;;N;;;;;
+101C;MYANMAR LETTER LA;Lo;0;L;;;;;N;;;;;
+101D;MYANMAR LETTER WA;Lo;0;L;;;;;N;;;;;
+101E;MYANMAR LETTER SA;Lo;0;L;;;;;N;;;;;
+101F;MYANMAR LETTER HA;Lo;0;L;;;;;N;;;;;
+1020;MYANMAR LETTER LLA;Lo;0;L;;;;;N;;;;;
+1021;MYANMAR LETTER A;Lo;0;L;;;;;N;;;;;
+1023;MYANMAR LETTER I;Lo;0;L;;;;;N;;;;;
+1024;MYANMAR LETTER II;Lo;0;L;;;;;N;;;;;
+1025;MYANMAR LETTER U;Lo;0;L;;;;;N;;;;;
+1026;MYANMAR LETTER UU;Lo;0;L;1025 102E;;;;N;;;;;
+1027;MYANMAR LETTER E;Lo;0;L;;;;;N;;;;;
+1029;MYANMAR LETTER O;Lo;0;L;;;;;N;;;;;
+102A;MYANMAR LETTER AU;Lo;0;L;;;;;N;;;;;
+102C;MYANMAR VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+102D;MYANMAR VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+102E;MYANMAR VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+102F;MYANMAR VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1030;MYANMAR VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+1031;MYANMAR VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+1032;MYANMAR VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+1036;MYANMAR SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+1037;MYANMAR SIGN DOT BELOW;Mn;7;NSM;;;;;N;;;;;
+1038;MYANMAR SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+1039;MYANMAR SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+1040;MYANMAR DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1041;MYANMAR DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1042;MYANMAR DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1043;MYANMAR DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1044;MYANMAR DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1045;MYANMAR DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1046;MYANMAR DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1047;MYANMAR DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1048;MYANMAR DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1049;MYANMAR DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+104A;MYANMAR SIGN LITTLE SECTION;Po;0;L;;;;;N;;;;;
+104B;MYANMAR SIGN SECTION;Po;0;L;;;;;N;;;;;
+104C;MYANMAR SYMBOL LOCATIVE;Po;0;L;;;;;N;;;;;
+104D;MYANMAR SYMBOL COMPLETED;Po;0;L;;;;;N;;;;;
+104E;MYANMAR SYMBOL AFOREMENTIONED;Po;0;L;;;;;N;;;;;
+104F;MYANMAR SYMBOL GENITIVE;Po;0;L;;;;;N;;;;;
+1050;MYANMAR LETTER SHA;Lo;0;L;;;;;N;;;;;
+1051;MYANMAR LETTER SSA;Lo;0;L;;;;;N;;;;;
+1052;MYANMAR LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+1053;MYANMAR LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+1054;MYANMAR LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+1055;MYANMAR LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+1056;MYANMAR VOWEL SIGN VOCALIC R;Mc;0;L;;;;;N;;;;;
+1057;MYANMAR VOWEL SIGN VOCALIC RR;Mc;0;L;;;;;N;;;;;
+1058;MYANMAR VOWEL SIGN VOCALIC L;Mn;0;NSM;;;;;N;;;;;
+1059;MYANMAR VOWEL SIGN VOCALIC LL;Mn;0;NSM;;;;;N;;;;;
+10A0;GEORGIAN CAPITAL LETTER AN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A1;GEORGIAN CAPITAL LETTER BAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A2;GEORGIAN CAPITAL LETTER GAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A3;GEORGIAN CAPITAL LETTER DON;Lu;0;L;;;;;N;;Khutsuri;;;
+10A4;GEORGIAN CAPITAL LETTER EN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A5;GEORGIAN CAPITAL LETTER VIN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A6;GEORGIAN CAPITAL LETTER ZEN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A7;GEORGIAN CAPITAL LETTER TAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A8;GEORGIAN CAPITAL LETTER IN;Lu;0;L;;;;;N;;Khutsuri;;;
+10A9;GEORGIAN CAPITAL LETTER KAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10AA;GEORGIAN CAPITAL LETTER LAS;Lu;0;L;;;;;N;;Khutsuri;;;
+10AB;GEORGIAN CAPITAL LETTER MAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10AC;GEORGIAN CAPITAL LETTER NAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10AD;GEORGIAN CAPITAL LETTER ON;Lu;0;L;;;;;N;;Khutsuri;;;
+10AE;GEORGIAN CAPITAL LETTER PAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10AF;GEORGIAN CAPITAL LETTER ZHAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10B0;GEORGIAN CAPITAL LETTER RAE;Lu;0;L;;;;;N;;Khutsuri;;;
+10B1;GEORGIAN CAPITAL LETTER SAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10B2;GEORGIAN CAPITAL LETTER TAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10B3;GEORGIAN CAPITAL LETTER UN;Lu;0;L;;;;;N;;Khutsuri;;;
+10B4;GEORGIAN CAPITAL LETTER PHAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10B5;GEORGIAN CAPITAL LETTER KHAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10B6;GEORGIAN CAPITAL LETTER GHAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10B7;GEORGIAN CAPITAL LETTER QAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10B8;GEORGIAN CAPITAL LETTER SHIN;Lu;0;L;;;;;N;;Khutsuri;;;
+10B9;GEORGIAN CAPITAL LETTER CHIN;Lu;0;L;;;;;N;;Khutsuri;;;
+10BA;GEORGIAN CAPITAL LETTER CAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10BB;GEORGIAN CAPITAL LETTER JIL;Lu;0;L;;;;;N;;Khutsuri;;;
+10BC;GEORGIAN CAPITAL LETTER CIL;Lu;0;L;;;;;N;;Khutsuri;;;
+10BD;GEORGIAN CAPITAL LETTER CHAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10BE;GEORGIAN CAPITAL LETTER XAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10BF;GEORGIAN CAPITAL LETTER JHAN;Lu;0;L;;;;;N;;Khutsuri;;;
+10C0;GEORGIAN CAPITAL LETTER HAE;Lu;0;L;;;;;N;;Khutsuri;;;
+10C1;GEORGIAN CAPITAL LETTER HE;Lu;0;L;;;;;N;;Khutsuri;;;
+10C2;GEORGIAN CAPITAL LETTER HIE;Lu;0;L;;;;;N;;Khutsuri;;;
+10C3;GEORGIAN CAPITAL LETTER WE;Lu;0;L;;;;;N;;Khutsuri;;;
+10C4;GEORGIAN CAPITAL LETTER HAR;Lu;0;L;;;;;N;;Khutsuri;;;
+10C5;GEORGIAN CAPITAL LETTER HOE;Lu;0;L;;;;;N;;Khutsuri;;;
+10D0;GEORGIAN LETTER AN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER AN;;;;
+10D1;GEORGIAN LETTER BAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER BAN;;;;
+10D2;GEORGIAN LETTER GAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER GAN;;;;
+10D3;GEORGIAN LETTER DON;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER DON;;;;
+10D4;GEORGIAN LETTER EN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER EN;;;;
+10D5;GEORGIAN LETTER VIN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER VIN;;;;
+10D6;GEORGIAN LETTER ZEN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER ZEN;;;;
+10D7;GEORGIAN LETTER TAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER TAN;;;;
+10D8;GEORGIAN LETTER IN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER IN;;;;
+10D9;GEORGIAN LETTER KAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER KAN;;;;
+10DA;GEORGIAN LETTER LAS;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER LAS;;;;
+10DB;GEORGIAN LETTER MAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER MAN;;;;
+10DC;GEORGIAN LETTER NAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER NAR;;;;
+10DD;GEORGIAN LETTER ON;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER ON;;;;
+10DE;GEORGIAN LETTER PAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER PAR;;;;
+10DF;GEORGIAN LETTER ZHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER ZHAR;;;;
+10E0;GEORGIAN LETTER RAE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER RAE;;;;
+10E1;GEORGIAN LETTER SAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER SAN;;;;
+10E2;GEORGIAN LETTER TAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER TAR;;;;
+10E3;GEORGIAN LETTER UN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER UN;;;;
+10E4;GEORGIAN LETTER PHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER PHAR;;;;
+10E5;GEORGIAN LETTER KHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER KHAR;;;;
+10E6;GEORGIAN LETTER GHAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER GHAN;;;;
+10E7;GEORGIAN LETTER QAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER QAR;;;;
+10E8;GEORGIAN LETTER SHIN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER SHIN;;;;
+10E9;GEORGIAN LETTER CHIN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CHIN;;;;
+10EA;GEORGIAN LETTER CAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CAN;;;;
+10EB;GEORGIAN LETTER JIL;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER JIL;;;;
+10EC;GEORGIAN LETTER CIL;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CIL;;;;
+10ED;GEORGIAN LETTER CHAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER CHAR;;;;
+10EE;GEORGIAN LETTER XAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER XAN;;;;
+10EF;GEORGIAN LETTER JHAN;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER JHAN;;;;
+10F0;GEORGIAN LETTER HAE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HAE;;;;
+10F1;GEORGIAN LETTER HE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HE;;;;
+10F2;GEORGIAN LETTER HIE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HIE;;;;
+10F3;GEORGIAN LETTER WE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER WE;;;;
+10F4;GEORGIAN LETTER HAR;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HAR;;;;
+10F5;GEORGIAN LETTER HOE;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER HOE;;;;
+10F6;GEORGIAN LETTER FI;Lo;0;L;;;;;N;GEORGIAN SMALL LETTER FI;;;;
+10F7;GEORGIAN LETTER YN;Lo;0;L;;;;;N;;;;;
+10F8;GEORGIAN LETTER ELIFI;Lo;0;L;;;;;N;;;;;
+10FB;GEORGIAN PARAGRAPH SEPARATOR;Po;0;L;;;;;N;;;;;
+1100;HANGUL CHOSEONG KIYEOK;Lo;0;L;;;;;N;;g *;;;
+1101;HANGUL CHOSEONG SSANGKIYEOK;Lo;0;L;;;;;N;;gg *;;;
+1102;HANGUL CHOSEONG NIEUN;Lo;0;L;;;;;N;;n *;;;
+1103;HANGUL CHOSEONG TIKEUT;Lo;0;L;;;;;N;;d *;;;
+1104;HANGUL CHOSEONG SSANGTIKEUT;Lo;0;L;;;;;N;;dd *;;;
+1105;HANGUL CHOSEONG RIEUL;Lo;0;L;;;;;N;;r *;;;
+1106;HANGUL CHOSEONG MIEUM;Lo;0;L;;;;;N;;m *;;;
+1107;HANGUL CHOSEONG PIEUP;Lo;0;L;;;;;N;;b *;;;
+1108;HANGUL CHOSEONG SSANGPIEUP;Lo;0;L;;;;;N;;bb *;;;
+1109;HANGUL CHOSEONG SIOS;Lo;0;L;;;;;N;;s *;;;
+110A;HANGUL CHOSEONG SSANGSIOS;Lo;0;L;;;;;N;;ss *;;;
+110B;HANGUL CHOSEONG IEUNG;Lo;0;L;;;;;N;;;;;
+110C;HANGUL CHOSEONG CIEUC;Lo;0;L;;;;;N;;j *;;;
+110D;HANGUL CHOSEONG SSANGCIEUC;Lo;0;L;;;;;N;;jj *;;;
+110E;HANGUL CHOSEONG CHIEUCH;Lo;0;L;;;;;N;;c *;;;
+110F;HANGUL CHOSEONG KHIEUKH;Lo;0;L;;;;;N;;k *;;;
+1110;HANGUL CHOSEONG THIEUTH;Lo;0;L;;;;;N;;t *;;;
+1111;HANGUL CHOSEONG PHIEUPH;Lo;0;L;;;;;N;;p *;;;
+1112;HANGUL CHOSEONG HIEUH;Lo;0;L;;;;;N;;h *;;;
+1113;HANGUL CHOSEONG NIEUN-KIYEOK;Lo;0;L;;;;;N;;;;;
+1114;HANGUL CHOSEONG SSANGNIEUN;Lo;0;L;;;;;N;;;;;
+1115;HANGUL CHOSEONG NIEUN-TIKEUT;Lo;0;L;;;;;N;;;;;
+1116;HANGUL CHOSEONG NIEUN-PIEUP;Lo;0;L;;;;;N;;;;;
+1117;HANGUL CHOSEONG TIKEUT-KIYEOK;Lo;0;L;;;;;N;;;;;
+1118;HANGUL CHOSEONG RIEUL-NIEUN;Lo;0;L;;;;;N;;;;;
+1119;HANGUL CHOSEONG SSANGRIEUL;Lo;0;L;;;;;N;;;;;
+111A;HANGUL CHOSEONG RIEUL-HIEUH;Lo;0;L;;;;;N;;;;;
+111B;HANGUL CHOSEONG KAPYEOUNRIEUL;Lo;0;L;;;;;N;;;;;
+111C;HANGUL CHOSEONG MIEUM-PIEUP;Lo;0;L;;;;;N;;;;;
+111D;HANGUL CHOSEONG KAPYEOUNMIEUM;Lo;0;L;;;;;N;;;;;
+111E;HANGUL CHOSEONG PIEUP-KIYEOK;Lo;0;L;;;;;N;;;;;
+111F;HANGUL CHOSEONG PIEUP-NIEUN;Lo;0;L;;;;;N;;;;;
+1120;HANGUL CHOSEONG PIEUP-TIKEUT;Lo;0;L;;;;;N;;;;;
+1121;HANGUL CHOSEONG PIEUP-SIOS;Lo;0;L;;;;;N;;;;;
+1122;HANGUL CHOSEONG PIEUP-SIOS-KIYEOK;Lo;0;L;;;;;N;;;;;
+1123;HANGUL CHOSEONG PIEUP-SIOS-TIKEUT;Lo;0;L;;;;;N;;;;;
+1124;HANGUL CHOSEONG PIEUP-SIOS-PIEUP;Lo;0;L;;;;;N;;;;;
+1125;HANGUL CHOSEONG PIEUP-SSANGSIOS;Lo;0;L;;;;;N;;;;;
+1126;HANGUL CHOSEONG PIEUP-SIOS-CIEUC;Lo;0;L;;;;;N;;;;;
+1127;HANGUL CHOSEONG PIEUP-CIEUC;Lo;0;L;;;;;N;;;;;
+1128;HANGUL CHOSEONG PIEUP-CHIEUCH;Lo;0;L;;;;;N;;;;;
+1129;HANGUL CHOSEONG PIEUP-THIEUTH;Lo;0;L;;;;;N;;;;;
+112A;HANGUL CHOSEONG PIEUP-PHIEUPH;Lo;0;L;;;;;N;;;;;
+112B;HANGUL CHOSEONG KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;;
+112C;HANGUL CHOSEONG KAPYEOUNSSANGPIEUP;Lo;0;L;;;;;N;;;;;
+112D;HANGUL CHOSEONG SIOS-KIYEOK;Lo;0;L;;;;;N;;;;;
+112E;HANGUL CHOSEONG SIOS-NIEUN;Lo;0;L;;;;;N;;;;;
+112F;HANGUL CHOSEONG SIOS-TIKEUT;Lo;0;L;;;;;N;;;;;
+1130;HANGUL CHOSEONG SIOS-RIEUL;Lo;0;L;;;;;N;;;;;
+1131;HANGUL CHOSEONG SIOS-MIEUM;Lo;0;L;;;;;N;;;;;
+1132;HANGUL CHOSEONG SIOS-PIEUP;Lo;0;L;;;;;N;;;;;
+1133;HANGUL CHOSEONG SIOS-PIEUP-KIYEOK;Lo;0;L;;;;;N;;;;;
+1134;HANGUL CHOSEONG SIOS-SSANGSIOS;Lo;0;L;;;;;N;;;;;
+1135;HANGUL CHOSEONG SIOS-IEUNG;Lo;0;L;;;;;N;;;;;
+1136;HANGUL CHOSEONG SIOS-CIEUC;Lo;0;L;;;;;N;;;;;
+1137;HANGUL CHOSEONG SIOS-CHIEUCH;Lo;0;L;;;;;N;;;;;
+1138;HANGUL CHOSEONG SIOS-KHIEUKH;Lo;0;L;;;;;N;;;;;
+1139;HANGUL CHOSEONG SIOS-THIEUTH;Lo;0;L;;;;;N;;;;;
+113A;HANGUL CHOSEONG SIOS-PHIEUPH;Lo;0;L;;;;;N;;;;;
+113B;HANGUL CHOSEONG SIOS-HIEUH;Lo;0;L;;;;;N;;;;;
+113C;HANGUL CHOSEONG CHITUEUMSIOS;Lo;0;L;;;;;N;;;;;
+113D;HANGUL CHOSEONG CHITUEUMSSANGSIOS;Lo;0;L;;;;;N;;;;;
+113E;HANGUL CHOSEONG CEONGCHIEUMSIOS;Lo;0;L;;;;;N;;;;;
+113F;HANGUL CHOSEONG CEONGCHIEUMSSANGSIOS;Lo;0;L;;;;;N;;;;;
+1140;HANGUL CHOSEONG PANSIOS;Lo;0;L;;;;;N;;;;;
+1141;HANGUL CHOSEONG IEUNG-KIYEOK;Lo;0;L;;;;;N;;;;;
+1142;HANGUL CHOSEONG IEUNG-TIKEUT;Lo;0;L;;;;;N;;;;;
+1143;HANGUL CHOSEONG IEUNG-MIEUM;Lo;0;L;;;;;N;;;;;
+1144;HANGUL CHOSEONG IEUNG-PIEUP;Lo;0;L;;;;;N;;;;;
+1145;HANGUL CHOSEONG IEUNG-SIOS;Lo;0;L;;;;;N;;;;;
+1146;HANGUL CHOSEONG IEUNG-PANSIOS;Lo;0;L;;;;;N;;;;;
+1147;HANGUL CHOSEONG SSANGIEUNG;Lo;0;L;;;;;N;;;;;
+1148;HANGUL CHOSEONG IEUNG-CIEUC;Lo;0;L;;;;;N;;;;;
+1149;HANGUL CHOSEONG IEUNG-CHIEUCH;Lo;0;L;;;;;N;;;;;
+114A;HANGUL CHOSEONG IEUNG-THIEUTH;Lo;0;L;;;;;N;;;;;
+114B;HANGUL CHOSEONG IEUNG-PHIEUPH;Lo;0;L;;;;;N;;;;;
+114C;HANGUL CHOSEONG YESIEUNG;Lo;0;L;;;;;N;;;;;
+114D;HANGUL CHOSEONG CIEUC-IEUNG;Lo;0;L;;;;;N;;;;;
+114E;HANGUL CHOSEONG CHITUEUMCIEUC;Lo;0;L;;;;;N;;;;;
+114F;HANGUL CHOSEONG CHITUEUMSSANGCIEUC;Lo;0;L;;;;;N;;;;;
+1150;HANGUL CHOSEONG CEONGCHIEUMCIEUC;Lo;0;L;;;;;N;;;;;
+1151;HANGUL CHOSEONG CEONGCHIEUMSSANGCIEUC;Lo;0;L;;;;;N;;;;;
+1152;HANGUL CHOSEONG CHIEUCH-KHIEUKH;Lo;0;L;;;;;N;;;;;
+1153;HANGUL CHOSEONG CHIEUCH-HIEUH;Lo;0;L;;;;;N;;;;;
+1154;HANGUL CHOSEONG CHITUEUMCHIEUCH;Lo;0;L;;;;;N;;;;;
+1155;HANGUL CHOSEONG CEONGCHIEUMCHIEUCH;Lo;0;L;;;;;N;;;;;
+1156;HANGUL CHOSEONG PHIEUPH-PIEUP;Lo;0;L;;;;;N;;;;;
+1157;HANGUL CHOSEONG KAPYEOUNPHIEUPH;Lo;0;L;;;;;N;;;;;
+1158;HANGUL CHOSEONG SSANGHIEUH;Lo;0;L;;;;;N;;;;;
+1159;HANGUL CHOSEONG YEORINHIEUH;Lo;0;L;;;;;N;;;;;
+115F;HANGUL CHOSEONG FILLER;Lo;0;L;;;;;N;;;;;
+1160;HANGUL JUNGSEONG FILLER;Lo;0;L;;;;;N;;;;;
+1161;HANGUL JUNGSEONG A;Lo;0;L;;;;;N;;;;;
+1162;HANGUL JUNGSEONG AE;Lo;0;L;;;;;N;;;;;
+1163;HANGUL JUNGSEONG YA;Lo;0;L;;;;;N;;;;;
+1164;HANGUL JUNGSEONG YAE;Lo;0;L;;;;;N;;;;;
+1165;HANGUL JUNGSEONG EO;Lo;0;L;;;;;N;;;;;
+1166;HANGUL JUNGSEONG E;Lo;0;L;;;;;N;;;;;
+1167;HANGUL JUNGSEONG YEO;Lo;0;L;;;;;N;;;;;
+1168;HANGUL JUNGSEONG YE;Lo;0;L;;;;;N;;;;;
+1169;HANGUL JUNGSEONG O;Lo;0;L;;;;;N;;;;;
+116A;HANGUL JUNGSEONG WA;Lo;0;L;;;;;N;;;;;
+116B;HANGUL JUNGSEONG WAE;Lo;0;L;;;;;N;;;;;
+116C;HANGUL JUNGSEONG OE;Lo;0;L;;;;;N;;;;;
+116D;HANGUL JUNGSEONG YO;Lo;0;L;;;;;N;;;;;
+116E;HANGUL JUNGSEONG U;Lo;0;L;;;;;N;;;;;
+116F;HANGUL JUNGSEONG WEO;Lo;0;L;;;;;N;;;;;
+1170;HANGUL JUNGSEONG WE;Lo;0;L;;;;;N;;;;;
+1171;HANGUL JUNGSEONG WI;Lo;0;L;;;;;N;;;;;
+1172;HANGUL JUNGSEONG YU;Lo;0;L;;;;;N;;;;;
+1173;HANGUL JUNGSEONG EU;Lo;0;L;;;;;N;;;;;
+1174;HANGUL JUNGSEONG YI;Lo;0;L;;;;;N;;;;;
+1175;HANGUL JUNGSEONG I;Lo;0;L;;;;;N;;;;;
+1176;HANGUL JUNGSEONG A-O;Lo;0;L;;;;;N;;;;;
+1177;HANGUL JUNGSEONG A-U;Lo;0;L;;;;;N;;;;;
+1178;HANGUL JUNGSEONG YA-O;Lo;0;L;;;;;N;;;;;
+1179;HANGUL JUNGSEONG YA-YO;Lo;0;L;;;;;N;;;;;
+117A;HANGUL JUNGSEONG EO-O;Lo;0;L;;;;;N;;;;;
+117B;HANGUL JUNGSEONG EO-U;Lo;0;L;;;;;N;;;;;
+117C;HANGUL JUNGSEONG EO-EU;Lo;0;L;;;;;N;;;;;
+117D;HANGUL JUNGSEONG YEO-O;Lo;0;L;;;;;N;;;;;
+117E;HANGUL JUNGSEONG YEO-U;Lo;0;L;;;;;N;;;;;
+117F;HANGUL JUNGSEONG O-EO;Lo;0;L;;;;;N;;;;;
+1180;HANGUL JUNGSEONG O-E;Lo;0;L;;;;;N;;;;;
+1181;HANGUL JUNGSEONG O-YE;Lo;0;L;;;;;N;;;;;
+1182;HANGUL JUNGSEONG O-O;Lo;0;L;;;;;N;;;;;
+1183;HANGUL JUNGSEONG O-U;Lo;0;L;;;;;N;;;;;
+1184;HANGUL JUNGSEONG YO-YA;Lo;0;L;;;;;N;;;;;
+1185;HANGUL JUNGSEONG YO-YAE;Lo;0;L;;;;;N;;;;;
+1186;HANGUL JUNGSEONG YO-YEO;Lo;0;L;;;;;N;;;;;
+1187;HANGUL JUNGSEONG YO-O;Lo;0;L;;;;;N;;;;;
+1188;HANGUL JUNGSEONG YO-I;Lo;0;L;;;;;N;;;;;
+1189;HANGUL JUNGSEONG U-A;Lo;0;L;;;;;N;;;;;
+118A;HANGUL JUNGSEONG U-AE;Lo;0;L;;;;;N;;;;;
+118B;HANGUL JUNGSEONG U-EO-EU;Lo;0;L;;;;;N;;;;;
+118C;HANGUL JUNGSEONG U-YE;Lo;0;L;;;;;N;;;;;
+118D;HANGUL JUNGSEONG U-U;Lo;0;L;;;;;N;;;;;
+118E;HANGUL JUNGSEONG YU-A;Lo;0;L;;;;;N;;;;;
+118F;HANGUL JUNGSEONG YU-EO;Lo;0;L;;;;;N;;;;;
+1190;HANGUL JUNGSEONG YU-E;Lo;0;L;;;;;N;;;;;
+1191;HANGUL JUNGSEONG YU-YEO;Lo;0;L;;;;;N;;;;;
+1192;HANGUL JUNGSEONG YU-YE;Lo;0;L;;;;;N;;;;;
+1193;HANGUL JUNGSEONG YU-U;Lo;0;L;;;;;N;;;;;
+1194;HANGUL JUNGSEONG YU-I;Lo;0;L;;;;;N;;;;;
+1195;HANGUL JUNGSEONG EU-U;Lo;0;L;;;;;N;;;;;
+1196;HANGUL JUNGSEONG EU-EU;Lo;0;L;;;;;N;;;;;
+1197;HANGUL JUNGSEONG YI-U;Lo;0;L;;;;;N;;;;;
+1198;HANGUL JUNGSEONG I-A;Lo;0;L;;;;;N;;;;;
+1199;HANGUL JUNGSEONG I-YA;Lo;0;L;;;;;N;;;;;
+119A;HANGUL JUNGSEONG I-O;Lo;0;L;;;;;N;;;;;
+119B;HANGUL JUNGSEONG I-U;Lo;0;L;;;;;N;;;;;
+119C;HANGUL JUNGSEONG I-EU;Lo;0;L;;;;;N;;;;;
+119D;HANGUL JUNGSEONG I-ARAEA;Lo;0;L;;;;;N;;;;;
+119E;HANGUL JUNGSEONG ARAEA;Lo;0;L;;;;;N;;;;;
+119F;HANGUL JUNGSEONG ARAEA-EO;Lo;0;L;;;;;N;;;;;
+11A0;HANGUL JUNGSEONG ARAEA-U;Lo;0;L;;;;;N;;;;;
+11A1;HANGUL JUNGSEONG ARAEA-I;Lo;0;L;;;;;N;;;;;
+11A2;HANGUL JUNGSEONG SSANGARAEA;Lo;0;L;;;;;N;;;;;
+11A8;HANGUL JONGSEONG KIYEOK;Lo;0;L;;;;;N;;g *;;;
+11A9;HANGUL JONGSEONG SSANGKIYEOK;Lo;0;L;;;;;N;;gg *;;;
+11AA;HANGUL JONGSEONG KIYEOK-SIOS;Lo;0;L;;;;;N;;gs *;;;
+11AB;HANGUL JONGSEONG NIEUN;Lo;0;L;;;;;N;;n *;;;
+11AC;HANGUL JONGSEONG NIEUN-CIEUC;Lo;0;L;;;;;N;;nj *;;;
+11AD;HANGUL JONGSEONG NIEUN-HIEUH;Lo;0;L;;;;;N;;nh *;;;
+11AE;HANGUL JONGSEONG TIKEUT;Lo;0;L;;;;;N;;d *;;;
+11AF;HANGUL JONGSEONG RIEUL;Lo;0;L;;;;;N;;l *;;;
+11B0;HANGUL JONGSEONG RIEUL-KIYEOK;Lo;0;L;;;;;N;;lg *;;;
+11B1;HANGUL JONGSEONG RIEUL-MIEUM;Lo;0;L;;;;;N;;lm *;;;
+11B2;HANGUL JONGSEONG RIEUL-PIEUP;Lo;0;L;;;;;N;;lb *;;;
+11B3;HANGUL JONGSEONG RIEUL-SIOS;Lo;0;L;;;;;N;;ls *;;;
+11B4;HANGUL JONGSEONG RIEUL-THIEUTH;Lo;0;L;;;;;N;;lt *;;;
+11B5;HANGUL JONGSEONG RIEUL-PHIEUPH;Lo;0;L;;;;;N;;lp *;;;
+11B6;HANGUL JONGSEONG RIEUL-HIEUH;Lo;0;L;;;;;N;;lh *;;;
+11B7;HANGUL JONGSEONG MIEUM;Lo;0;L;;;;;N;;m *;;;
+11B8;HANGUL JONGSEONG PIEUP;Lo;0;L;;;;;N;;b *;;;
+11B9;HANGUL JONGSEONG PIEUP-SIOS;Lo;0;L;;;;;N;;bs *;;;
+11BA;HANGUL JONGSEONG SIOS;Lo;0;L;;;;;N;;s *;;;
+11BB;HANGUL JONGSEONG SSANGSIOS;Lo;0;L;;;;;N;;ss *;;;
+11BC;HANGUL JONGSEONG IEUNG;Lo;0;L;;;;;N;;ng *;;;
+11BD;HANGUL JONGSEONG CIEUC;Lo;0;L;;;;;N;;j *;;;
+11BE;HANGUL JONGSEONG CHIEUCH;Lo;0;L;;;;;N;;c *;;;
+11BF;HANGUL JONGSEONG KHIEUKH;Lo;0;L;;;;;N;;k *;;;
+11C0;HANGUL JONGSEONG THIEUTH;Lo;0;L;;;;;N;;t *;;;
+11C1;HANGUL JONGSEONG PHIEUPH;Lo;0;L;;;;;N;;p *;;;
+11C2;HANGUL JONGSEONG HIEUH;Lo;0;L;;;;;N;;h *;;;
+11C3;HANGUL JONGSEONG KIYEOK-RIEUL;Lo;0;L;;;;;N;;;;;
+11C4;HANGUL JONGSEONG KIYEOK-SIOS-KIYEOK;Lo;0;L;;;;;N;;;;;
+11C5;HANGUL JONGSEONG NIEUN-KIYEOK;Lo;0;L;;;;;N;;;;;
+11C6;HANGUL JONGSEONG NIEUN-TIKEUT;Lo;0;L;;;;;N;;;;;
+11C7;HANGUL JONGSEONG NIEUN-SIOS;Lo;0;L;;;;;N;;;;;
+11C8;HANGUL JONGSEONG NIEUN-PANSIOS;Lo;0;L;;;;;N;;;;;
+11C9;HANGUL JONGSEONG NIEUN-THIEUTH;Lo;0;L;;;;;N;;;;;
+11CA;HANGUL JONGSEONG TIKEUT-KIYEOK;Lo;0;L;;;;;N;;;;;
+11CB;HANGUL JONGSEONG TIKEUT-RIEUL;Lo;0;L;;;;;N;;;;;
+11CC;HANGUL JONGSEONG RIEUL-KIYEOK-SIOS;Lo;0;L;;;;;N;;;;;
+11CD;HANGUL JONGSEONG RIEUL-NIEUN;Lo;0;L;;;;;N;;;;;
+11CE;HANGUL JONGSEONG RIEUL-TIKEUT;Lo;0;L;;;;;N;;;;;
+11CF;HANGUL JONGSEONG RIEUL-TIKEUT-HIEUH;Lo;0;L;;;;;N;;;;;
+11D0;HANGUL JONGSEONG SSANGRIEUL;Lo;0;L;;;;;N;;;;;
+11D1;HANGUL JONGSEONG RIEUL-MIEUM-KIYEOK;Lo;0;L;;;;;N;;;;;
+11D2;HANGUL JONGSEONG RIEUL-MIEUM-SIOS;Lo;0;L;;;;;N;;;;;
+11D3;HANGUL JONGSEONG RIEUL-PIEUP-SIOS;Lo;0;L;;;;;N;;;;;
+11D4;HANGUL JONGSEONG RIEUL-PIEUP-HIEUH;Lo;0;L;;;;;N;;;;;
+11D5;HANGUL JONGSEONG RIEUL-KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;;
+11D6;HANGUL JONGSEONG RIEUL-SSANGSIOS;Lo;0;L;;;;;N;;;;;
+11D7;HANGUL JONGSEONG RIEUL-PANSIOS;Lo;0;L;;;;;N;;;;;
+11D8;HANGUL JONGSEONG RIEUL-KHIEUKH;Lo;0;L;;;;;N;;;;;
+11D9;HANGUL JONGSEONG RIEUL-YEORINHIEUH;Lo;0;L;;;;;N;;;;;
+11DA;HANGUL JONGSEONG MIEUM-KIYEOK;Lo;0;L;;;;;N;;;;;
+11DB;HANGUL JONGSEONG MIEUM-RIEUL;Lo;0;L;;;;;N;;;;;
+11DC;HANGUL JONGSEONG MIEUM-PIEUP;Lo;0;L;;;;;N;;;;;
+11DD;HANGUL JONGSEONG MIEUM-SIOS;Lo;0;L;;;;;N;;;;;
+11DE;HANGUL JONGSEONG MIEUM-SSANGSIOS;Lo;0;L;;;;;N;;;;;
+11DF;HANGUL JONGSEONG MIEUM-PANSIOS;Lo;0;L;;;;;N;;;;;
+11E0;HANGUL JONGSEONG MIEUM-CHIEUCH;Lo;0;L;;;;;N;;;;;
+11E1;HANGUL JONGSEONG MIEUM-HIEUH;Lo;0;L;;;;;N;;;;;
+11E2;HANGUL JONGSEONG KAPYEOUNMIEUM;Lo;0;L;;;;;N;;;;;
+11E3;HANGUL JONGSEONG PIEUP-RIEUL;Lo;0;L;;;;;N;;;;;
+11E4;HANGUL JONGSEONG PIEUP-PHIEUPH;Lo;0;L;;;;;N;;;;;
+11E5;HANGUL JONGSEONG PIEUP-HIEUH;Lo;0;L;;;;;N;;;;;
+11E6;HANGUL JONGSEONG KAPYEOUNPIEUP;Lo;0;L;;;;;N;;;;;
+11E7;HANGUL JONGSEONG SIOS-KIYEOK;Lo;0;L;;;;;N;;;;;
+11E8;HANGUL JONGSEONG SIOS-TIKEUT;Lo;0;L;;;;;N;;;;;
+11E9;HANGUL JONGSEONG SIOS-RIEUL;Lo;0;L;;;;;N;;;;;
+11EA;HANGUL JONGSEONG SIOS-PIEUP;Lo;0;L;;;;;N;;;;;
+11EB;HANGUL JONGSEONG PANSIOS;Lo;0;L;;;;;N;;;;;
+11EC;HANGUL JONGSEONG IEUNG-KIYEOK;Lo;0;L;;;;;N;;;;;
+11ED;HANGUL JONGSEONG IEUNG-SSANGKIYEOK;Lo;0;L;;;;;N;;;;;
+11EE;HANGUL JONGSEONG SSANGIEUNG;Lo;0;L;;;;;N;;;;;
+11EF;HANGUL JONGSEONG IEUNG-KHIEUKH;Lo;0;L;;;;;N;;;;;
+11F0;HANGUL JONGSEONG YESIEUNG;Lo;0;L;;;;;N;;;;;
+11F1;HANGUL JONGSEONG YESIEUNG-SIOS;Lo;0;L;;;;;N;;;;;
+11F2;HANGUL JONGSEONG YESIEUNG-PANSIOS;Lo;0;L;;;;;N;;;;;
+11F3;HANGUL JONGSEONG PHIEUPH-PIEUP;Lo;0;L;;;;;N;;;;;
+11F4;HANGUL JONGSEONG KAPYEOUNPHIEUPH;Lo;0;L;;;;;N;;;;;
+11F5;HANGUL JONGSEONG HIEUH-NIEUN;Lo;0;L;;;;;N;;;;;
+11F6;HANGUL JONGSEONG HIEUH-RIEUL;Lo;0;L;;;;;N;;;;;
+11F7;HANGUL JONGSEONG HIEUH-MIEUM;Lo;0;L;;;;;N;;;;;
+11F8;HANGUL JONGSEONG HIEUH-PIEUP;Lo;0;L;;;;;N;;;;;
+11F9;HANGUL JONGSEONG YEORINHIEUH;Lo;0;L;;;;;N;;;;;
+1200;ETHIOPIC SYLLABLE HA;Lo;0;L;;;;;N;;;;;
+1201;ETHIOPIC SYLLABLE HU;Lo;0;L;;;;;N;;;;;
+1202;ETHIOPIC SYLLABLE HI;Lo;0;L;;;;;N;;;;;
+1203;ETHIOPIC SYLLABLE HAA;Lo;0;L;;;;;N;;;;;
+1204;ETHIOPIC SYLLABLE HEE;Lo;0;L;;;;;N;;;;;
+1205;ETHIOPIC SYLLABLE HE;Lo;0;L;;;;;N;;;;;
+1206;ETHIOPIC SYLLABLE HO;Lo;0;L;;;;;N;;;;;
+1208;ETHIOPIC SYLLABLE LA;Lo;0;L;;;;;N;;;;;
+1209;ETHIOPIC SYLLABLE LU;Lo;0;L;;;;;N;;;;;
+120A;ETHIOPIC SYLLABLE LI;Lo;0;L;;;;;N;;;;;
+120B;ETHIOPIC SYLLABLE LAA;Lo;0;L;;;;;N;;;;;
+120C;ETHIOPIC SYLLABLE LEE;Lo;0;L;;;;;N;;;;;
+120D;ETHIOPIC SYLLABLE LE;Lo;0;L;;;;;N;;;;;
+120E;ETHIOPIC SYLLABLE LO;Lo;0;L;;;;;N;;;;;
+120F;ETHIOPIC SYLLABLE LWA;Lo;0;L;;;;;N;;;;;
+1210;ETHIOPIC SYLLABLE HHA;Lo;0;L;;;;;N;;;;;
+1211;ETHIOPIC SYLLABLE HHU;Lo;0;L;;;;;N;;;;;
+1212;ETHIOPIC SYLLABLE HHI;Lo;0;L;;;;;N;;;;;
+1213;ETHIOPIC SYLLABLE HHAA;Lo;0;L;;;;;N;;;;;
+1214;ETHIOPIC SYLLABLE HHEE;Lo;0;L;;;;;N;;;;;
+1215;ETHIOPIC SYLLABLE HHE;Lo;0;L;;;;;N;;;;;
+1216;ETHIOPIC SYLLABLE HHO;Lo;0;L;;;;;N;;;;;
+1217;ETHIOPIC SYLLABLE HHWA;Lo;0;L;;;;;N;;;;;
+1218;ETHIOPIC SYLLABLE MA;Lo;0;L;;;;;N;;;;;
+1219;ETHIOPIC SYLLABLE MU;Lo;0;L;;;;;N;;;;;
+121A;ETHIOPIC SYLLABLE MI;Lo;0;L;;;;;N;;;;;
+121B;ETHIOPIC SYLLABLE MAA;Lo;0;L;;;;;N;;;;;
+121C;ETHIOPIC SYLLABLE MEE;Lo;0;L;;;;;N;;;;;
+121D;ETHIOPIC SYLLABLE ME;Lo;0;L;;;;;N;;;;;
+121E;ETHIOPIC SYLLABLE MO;Lo;0;L;;;;;N;;;;;
+121F;ETHIOPIC SYLLABLE MWA;Lo;0;L;;;;;N;;;;;
+1220;ETHIOPIC SYLLABLE SZA;Lo;0;L;;;;;N;;;;;
+1221;ETHIOPIC SYLLABLE SZU;Lo;0;L;;;;;N;;;;;
+1222;ETHIOPIC SYLLABLE SZI;Lo;0;L;;;;;N;;;;;
+1223;ETHIOPIC SYLLABLE SZAA;Lo;0;L;;;;;N;;;;;
+1224;ETHIOPIC SYLLABLE SZEE;Lo;0;L;;;;;N;;;;;
+1225;ETHIOPIC SYLLABLE SZE;Lo;0;L;;;;;N;;;;;
+1226;ETHIOPIC SYLLABLE SZO;Lo;0;L;;;;;N;;;;;
+1227;ETHIOPIC SYLLABLE SZWA;Lo;0;L;;;;;N;;;;;
+1228;ETHIOPIC SYLLABLE RA;Lo;0;L;;;;;N;;;;;
+1229;ETHIOPIC SYLLABLE RU;Lo;0;L;;;;;N;;;;;
+122A;ETHIOPIC SYLLABLE RI;Lo;0;L;;;;;N;;;;;
+122B;ETHIOPIC SYLLABLE RAA;Lo;0;L;;;;;N;;;;;
+122C;ETHIOPIC SYLLABLE REE;Lo;0;L;;;;;N;;;;;
+122D;ETHIOPIC SYLLABLE RE;Lo;0;L;;;;;N;;;;;
+122E;ETHIOPIC SYLLABLE RO;Lo;0;L;;;;;N;;;;;
+122F;ETHIOPIC SYLLABLE RWA;Lo;0;L;;;;;N;;;;;
+1230;ETHIOPIC SYLLABLE SA;Lo;0;L;;;;;N;;;;;
+1231;ETHIOPIC SYLLABLE SU;Lo;0;L;;;;;N;;;;;
+1232;ETHIOPIC SYLLABLE SI;Lo;0;L;;;;;N;;;;;
+1233;ETHIOPIC SYLLABLE SAA;Lo;0;L;;;;;N;;;;;
+1234;ETHIOPIC SYLLABLE SEE;Lo;0;L;;;;;N;;;;;
+1235;ETHIOPIC SYLLABLE SE;Lo;0;L;;;;;N;;;;;
+1236;ETHIOPIC SYLLABLE SO;Lo;0;L;;;;;N;;;;;
+1237;ETHIOPIC SYLLABLE SWA;Lo;0;L;;;;;N;;;;;
+1238;ETHIOPIC SYLLABLE SHA;Lo;0;L;;;;;N;;;;;
+1239;ETHIOPIC SYLLABLE SHU;Lo;0;L;;;;;N;;;;;
+123A;ETHIOPIC SYLLABLE SHI;Lo;0;L;;;;;N;;;;;
+123B;ETHIOPIC SYLLABLE SHAA;Lo;0;L;;;;;N;;;;;
+123C;ETHIOPIC SYLLABLE SHEE;Lo;0;L;;;;;N;;;;;
+123D;ETHIOPIC SYLLABLE SHE;Lo;0;L;;;;;N;;;;;
+123E;ETHIOPIC SYLLABLE SHO;Lo;0;L;;;;;N;;;;;
+123F;ETHIOPIC SYLLABLE SHWA;Lo;0;L;;;;;N;;;;;
+1240;ETHIOPIC SYLLABLE QA;Lo;0;L;;;;;N;;;;;
+1241;ETHIOPIC SYLLABLE QU;Lo;0;L;;;;;N;;;;;
+1242;ETHIOPIC SYLLABLE QI;Lo;0;L;;;;;N;;;;;
+1243;ETHIOPIC SYLLABLE QAA;Lo;0;L;;;;;N;;;;;
+1244;ETHIOPIC SYLLABLE QEE;Lo;0;L;;;;;N;;;;;
+1245;ETHIOPIC SYLLABLE QE;Lo;0;L;;;;;N;;;;;
+1246;ETHIOPIC SYLLABLE QO;Lo;0;L;;;;;N;;;;;
+1248;ETHIOPIC SYLLABLE QWA;Lo;0;L;;;;;N;;;;;
+124A;ETHIOPIC SYLLABLE QWI;Lo;0;L;;;;;N;;;;;
+124B;ETHIOPIC SYLLABLE QWAA;Lo;0;L;;;;;N;;;;;
+124C;ETHIOPIC SYLLABLE QWEE;Lo;0;L;;;;;N;;;;;
+124D;ETHIOPIC SYLLABLE QWE;Lo;0;L;;;;;N;;;;;
+1250;ETHIOPIC SYLLABLE QHA;Lo;0;L;;;;;N;;;;;
+1251;ETHIOPIC SYLLABLE QHU;Lo;0;L;;;;;N;;;;;
+1252;ETHIOPIC SYLLABLE QHI;Lo;0;L;;;;;N;;;;;
+1253;ETHIOPIC SYLLABLE QHAA;Lo;0;L;;;;;N;;;;;
+1254;ETHIOPIC SYLLABLE QHEE;Lo;0;L;;;;;N;;;;;
+1255;ETHIOPIC SYLLABLE QHE;Lo;0;L;;;;;N;;;;;
+1256;ETHIOPIC SYLLABLE QHO;Lo;0;L;;;;;N;;;;;
+1258;ETHIOPIC SYLLABLE QHWA;Lo;0;L;;;;;N;;;;;
+125A;ETHIOPIC SYLLABLE QHWI;Lo;0;L;;;;;N;;;;;
+125B;ETHIOPIC SYLLABLE QHWAA;Lo;0;L;;;;;N;;;;;
+125C;ETHIOPIC SYLLABLE QHWEE;Lo;0;L;;;;;N;;;;;
+125D;ETHIOPIC SYLLABLE QHWE;Lo;0;L;;;;;N;;;;;
+1260;ETHIOPIC SYLLABLE BA;Lo;0;L;;;;;N;;;;;
+1261;ETHIOPIC SYLLABLE BU;Lo;0;L;;;;;N;;;;;
+1262;ETHIOPIC SYLLABLE BI;Lo;0;L;;;;;N;;;;;
+1263;ETHIOPIC SYLLABLE BAA;Lo;0;L;;;;;N;;;;;
+1264;ETHIOPIC SYLLABLE BEE;Lo;0;L;;;;;N;;;;;
+1265;ETHIOPIC SYLLABLE BE;Lo;0;L;;;;;N;;;;;
+1266;ETHIOPIC SYLLABLE BO;Lo;0;L;;;;;N;;;;;
+1267;ETHIOPIC SYLLABLE BWA;Lo;0;L;;;;;N;;;;;
+1268;ETHIOPIC SYLLABLE VA;Lo;0;L;;;;;N;;;;;
+1269;ETHIOPIC SYLLABLE VU;Lo;0;L;;;;;N;;;;;
+126A;ETHIOPIC SYLLABLE VI;Lo;0;L;;;;;N;;;;;
+126B;ETHIOPIC SYLLABLE VAA;Lo;0;L;;;;;N;;;;;
+126C;ETHIOPIC SYLLABLE VEE;Lo;0;L;;;;;N;;;;;
+126D;ETHIOPIC SYLLABLE VE;Lo;0;L;;;;;N;;;;;
+126E;ETHIOPIC SYLLABLE VO;Lo;0;L;;;;;N;;;;;
+126F;ETHIOPIC SYLLABLE VWA;Lo;0;L;;;;;N;;;;;
+1270;ETHIOPIC SYLLABLE TA;Lo;0;L;;;;;N;;;;;
+1271;ETHIOPIC SYLLABLE TU;Lo;0;L;;;;;N;;;;;
+1272;ETHIOPIC SYLLABLE TI;Lo;0;L;;;;;N;;;;;
+1273;ETHIOPIC SYLLABLE TAA;Lo;0;L;;;;;N;;;;;
+1274;ETHIOPIC SYLLABLE TEE;Lo;0;L;;;;;N;;;;;
+1275;ETHIOPIC SYLLABLE TE;Lo;0;L;;;;;N;;;;;
+1276;ETHIOPIC SYLLABLE TO;Lo;0;L;;;;;N;;;;;
+1277;ETHIOPIC SYLLABLE TWA;Lo;0;L;;;;;N;;;;;
+1278;ETHIOPIC SYLLABLE CA;Lo;0;L;;;;;N;;;;;
+1279;ETHIOPIC SYLLABLE CU;Lo;0;L;;;;;N;;;;;
+127A;ETHIOPIC SYLLABLE CI;Lo;0;L;;;;;N;;;;;
+127B;ETHIOPIC SYLLABLE CAA;Lo;0;L;;;;;N;;;;;
+127C;ETHIOPIC SYLLABLE CEE;Lo;0;L;;;;;N;;;;;
+127D;ETHIOPIC SYLLABLE CE;Lo;0;L;;;;;N;;;;;
+127E;ETHIOPIC SYLLABLE CO;Lo;0;L;;;;;N;;;;;
+127F;ETHIOPIC SYLLABLE CWA;Lo;0;L;;;;;N;;;;;
+1280;ETHIOPIC SYLLABLE XA;Lo;0;L;;;;;N;;;;;
+1281;ETHIOPIC SYLLABLE XU;Lo;0;L;;;;;N;;;;;
+1282;ETHIOPIC SYLLABLE XI;Lo;0;L;;;;;N;;;;;
+1283;ETHIOPIC SYLLABLE XAA;Lo;0;L;;;;;N;;;;;
+1284;ETHIOPIC SYLLABLE XEE;Lo;0;L;;;;;N;;;;;
+1285;ETHIOPIC SYLLABLE XE;Lo;0;L;;;;;N;;;;;
+1286;ETHIOPIC SYLLABLE XO;Lo;0;L;;;;;N;;;;;
+1288;ETHIOPIC SYLLABLE XWA;Lo;0;L;;;;;N;;;;;
+128A;ETHIOPIC SYLLABLE XWI;Lo;0;L;;;;;N;;;;;
+128B;ETHIOPIC SYLLABLE XWAA;Lo;0;L;;;;;N;;;;;
+128C;ETHIOPIC SYLLABLE XWEE;Lo;0;L;;;;;N;;;;;
+128D;ETHIOPIC SYLLABLE XWE;Lo;0;L;;;;;N;;;;;
+1290;ETHIOPIC SYLLABLE NA;Lo;0;L;;;;;N;;;;;
+1291;ETHIOPIC SYLLABLE NU;Lo;0;L;;;;;N;;;;;
+1292;ETHIOPIC SYLLABLE NI;Lo;0;L;;;;;N;;;;;
+1293;ETHIOPIC SYLLABLE NAA;Lo;0;L;;;;;N;;;;;
+1294;ETHIOPIC SYLLABLE NEE;Lo;0;L;;;;;N;;;;;
+1295;ETHIOPIC SYLLABLE NE;Lo;0;L;;;;;N;;;;;
+1296;ETHIOPIC SYLLABLE NO;Lo;0;L;;;;;N;;;;;
+1297;ETHIOPIC SYLLABLE NWA;Lo;0;L;;;;;N;;;;;
+1298;ETHIOPIC SYLLABLE NYA;Lo;0;L;;;;;N;;;;;
+1299;ETHIOPIC SYLLABLE NYU;Lo;0;L;;;;;N;;;;;
+129A;ETHIOPIC SYLLABLE NYI;Lo;0;L;;;;;N;;;;;
+129B;ETHIOPIC SYLLABLE NYAA;Lo;0;L;;;;;N;;;;;
+129C;ETHIOPIC SYLLABLE NYEE;Lo;0;L;;;;;N;;;;;
+129D;ETHIOPIC SYLLABLE NYE;Lo;0;L;;;;;N;;;;;
+129E;ETHIOPIC SYLLABLE NYO;Lo;0;L;;;;;N;;;;;
+129F;ETHIOPIC SYLLABLE NYWA;Lo;0;L;;;;;N;;;;;
+12A0;ETHIOPIC SYLLABLE GLOTTAL A;Lo;0;L;;;;;N;;;;;
+12A1;ETHIOPIC SYLLABLE GLOTTAL U;Lo;0;L;;;;;N;;;;;
+12A2;ETHIOPIC SYLLABLE GLOTTAL I;Lo;0;L;;;;;N;;;;;
+12A3;ETHIOPIC SYLLABLE GLOTTAL AA;Lo;0;L;;;;;N;;;;;
+12A4;ETHIOPIC SYLLABLE GLOTTAL EE;Lo;0;L;;;;;N;;;;;
+12A5;ETHIOPIC SYLLABLE GLOTTAL E;Lo;0;L;;;;;N;;;;;
+12A6;ETHIOPIC SYLLABLE GLOTTAL O;Lo;0;L;;;;;N;;;;;
+12A7;ETHIOPIC SYLLABLE GLOTTAL WA;Lo;0;L;;;;;N;;;;;
+12A8;ETHIOPIC SYLLABLE KA;Lo;0;L;;;;;N;;;;;
+12A9;ETHIOPIC SYLLABLE KU;Lo;0;L;;;;;N;;;;;
+12AA;ETHIOPIC SYLLABLE KI;Lo;0;L;;;;;N;;;;;
+12AB;ETHIOPIC SYLLABLE KAA;Lo;0;L;;;;;N;;;;;
+12AC;ETHIOPIC SYLLABLE KEE;Lo;0;L;;;;;N;;;;;
+12AD;ETHIOPIC SYLLABLE KE;Lo;0;L;;;;;N;;;;;
+12AE;ETHIOPIC SYLLABLE KO;Lo;0;L;;;;;N;;;;;
+12B0;ETHIOPIC SYLLABLE KWA;Lo;0;L;;;;;N;;;;;
+12B2;ETHIOPIC SYLLABLE KWI;Lo;0;L;;;;;N;;;;;
+12B3;ETHIOPIC SYLLABLE KWAA;Lo;0;L;;;;;N;;;;;
+12B4;ETHIOPIC SYLLABLE KWEE;Lo;0;L;;;;;N;;;;;
+12B5;ETHIOPIC SYLLABLE KWE;Lo;0;L;;;;;N;;;;;
+12B8;ETHIOPIC SYLLABLE KXA;Lo;0;L;;;;;N;;;;;
+12B9;ETHIOPIC SYLLABLE KXU;Lo;0;L;;;;;N;;;;;
+12BA;ETHIOPIC SYLLABLE KXI;Lo;0;L;;;;;N;;;;;
+12BB;ETHIOPIC SYLLABLE KXAA;Lo;0;L;;;;;N;;;;;
+12BC;ETHIOPIC SYLLABLE KXEE;Lo;0;L;;;;;N;;;;;
+12BD;ETHIOPIC SYLLABLE KXE;Lo;0;L;;;;;N;;;;;
+12BE;ETHIOPIC SYLLABLE KXO;Lo;0;L;;;;;N;;;;;
+12C0;ETHIOPIC SYLLABLE KXWA;Lo;0;L;;;;;N;;;;;
+12C2;ETHIOPIC SYLLABLE KXWI;Lo;0;L;;;;;N;;;;;
+12C3;ETHIOPIC SYLLABLE KXWAA;Lo;0;L;;;;;N;;;;;
+12C4;ETHIOPIC SYLLABLE KXWEE;Lo;0;L;;;;;N;;;;;
+12C5;ETHIOPIC SYLLABLE KXWE;Lo;0;L;;;;;N;;;;;
+12C8;ETHIOPIC SYLLABLE WA;Lo;0;L;;;;;N;;;;;
+12C9;ETHIOPIC SYLLABLE WU;Lo;0;L;;;;;N;;;;;
+12CA;ETHIOPIC SYLLABLE WI;Lo;0;L;;;;;N;;;;;
+12CB;ETHIOPIC SYLLABLE WAA;Lo;0;L;;;;;N;;;;;
+12CC;ETHIOPIC SYLLABLE WEE;Lo;0;L;;;;;N;;;;;
+12CD;ETHIOPIC SYLLABLE WE;Lo;0;L;;;;;N;;;;;
+12CE;ETHIOPIC SYLLABLE WO;Lo;0;L;;;;;N;;;;;
+12D0;ETHIOPIC SYLLABLE PHARYNGEAL A;Lo;0;L;;;;;N;;;;;
+12D1;ETHIOPIC SYLLABLE PHARYNGEAL U;Lo;0;L;;;;;N;;;;;
+12D2;ETHIOPIC SYLLABLE PHARYNGEAL I;Lo;0;L;;;;;N;;;;;
+12D3;ETHIOPIC SYLLABLE PHARYNGEAL AA;Lo;0;L;;;;;N;;;;;
+12D4;ETHIOPIC SYLLABLE PHARYNGEAL EE;Lo;0;L;;;;;N;;;;;
+12D5;ETHIOPIC SYLLABLE PHARYNGEAL E;Lo;0;L;;;;;N;;;;;
+12D6;ETHIOPIC SYLLABLE PHARYNGEAL O;Lo;0;L;;;;;N;;;;;
+12D8;ETHIOPIC SYLLABLE ZA;Lo;0;L;;;;;N;;;;;
+12D9;ETHIOPIC SYLLABLE ZU;Lo;0;L;;;;;N;;;;;
+12DA;ETHIOPIC SYLLABLE ZI;Lo;0;L;;;;;N;;;;;
+12DB;ETHIOPIC SYLLABLE ZAA;Lo;0;L;;;;;N;;;;;
+12DC;ETHIOPIC SYLLABLE ZEE;Lo;0;L;;;;;N;;;;;
+12DD;ETHIOPIC SYLLABLE ZE;Lo;0;L;;;;;N;;;;;
+12DE;ETHIOPIC SYLLABLE ZO;Lo;0;L;;;;;N;;;;;
+12DF;ETHIOPIC SYLLABLE ZWA;Lo;0;L;;;;;N;;;;;
+12E0;ETHIOPIC SYLLABLE ZHA;Lo;0;L;;;;;N;;;;;
+12E1;ETHIOPIC SYLLABLE ZHU;Lo;0;L;;;;;N;;;;;
+12E2;ETHIOPIC SYLLABLE ZHI;Lo;0;L;;;;;N;;;;;
+12E3;ETHIOPIC SYLLABLE ZHAA;Lo;0;L;;;;;N;;;;;
+12E4;ETHIOPIC SYLLABLE ZHEE;Lo;0;L;;;;;N;;;;;
+12E5;ETHIOPIC SYLLABLE ZHE;Lo;0;L;;;;;N;;;;;
+12E6;ETHIOPIC SYLLABLE ZHO;Lo;0;L;;;;;N;;;;;
+12E7;ETHIOPIC SYLLABLE ZHWA;Lo;0;L;;;;;N;;;;;
+12E8;ETHIOPIC SYLLABLE YA;Lo;0;L;;;;;N;;;;;
+12E9;ETHIOPIC SYLLABLE YU;Lo;0;L;;;;;N;;;;;
+12EA;ETHIOPIC SYLLABLE YI;Lo;0;L;;;;;N;;;;;
+12EB;ETHIOPIC SYLLABLE YAA;Lo;0;L;;;;;N;;;;;
+12EC;ETHIOPIC SYLLABLE YEE;Lo;0;L;;;;;N;;;;;
+12ED;ETHIOPIC SYLLABLE YE;Lo;0;L;;;;;N;;;;;
+12EE;ETHIOPIC SYLLABLE YO;Lo;0;L;;;;;N;;;;;
+12F0;ETHIOPIC SYLLABLE DA;Lo;0;L;;;;;N;;;;;
+12F1;ETHIOPIC SYLLABLE DU;Lo;0;L;;;;;N;;;;;
+12F2;ETHIOPIC SYLLABLE DI;Lo;0;L;;;;;N;;;;;
+12F3;ETHIOPIC SYLLABLE DAA;Lo;0;L;;;;;N;;;;;
+12F4;ETHIOPIC SYLLABLE DEE;Lo;0;L;;;;;N;;;;;
+12F5;ETHIOPIC SYLLABLE DE;Lo;0;L;;;;;N;;;;;
+12F6;ETHIOPIC SYLLABLE DO;Lo;0;L;;;;;N;;;;;
+12F7;ETHIOPIC SYLLABLE DWA;Lo;0;L;;;;;N;;;;;
+12F8;ETHIOPIC SYLLABLE DDA;Lo;0;L;;;;;N;;;;;
+12F9;ETHIOPIC SYLLABLE DDU;Lo;0;L;;;;;N;;;;;
+12FA;ETHIOPIC SYLLABLE DDI;Lo;0;L;;;;;N;;;;;
+12FB;ETHIOPIC SYLLABLE DDAA;Lo;0;L;;;;;N;;;;;
+12FC;ETHIOPIC SYLLABLE DDEE;Lo;0;L;;;;;N;;;;;
+12FD;ETHIOPIC SYLLABLE DDE;Lo;0;L;;;;;N;;;;;
+12FE;ETHIOPIC SYLLABLE DDO;Lo;0;L;;;;;N;;;;;
+12FF;ETHIOPIC SYLLABLE DDWA;Lo;0;L;;;;;N;;;;;
+1300;ETHIOPIC SYLLABLE JA;Lo;0;L;;;;;N;;;;;
+1301;ETHIOPIC SYLLABLE JU;Lo;0;L;;;;;N;;;;;
+1302;ETHIOPIC SYLLABLE JI;Lo;0;L;;;;;N;;;;;
+1303;ETHIOPIC SYLLABLE JAA;Lo;0;L;;;;;N;;;;;
+1304;ETHIOPIC SYLLABLE JEE;Lo;0;L;;;;;N;;;;;
+1305;ETHIOPIC SYLLABLE JE;Lo;0;L;;;;;N;;;;;
+1306;ETHIOPIC SYLLABLE JO;Lo;0;L;;;;;N;;;;;
+1307;ETHIOPIC SYLLABLE JWA;Lo;0;L;;;;;N;;;;;
+1308;ETHIOPIC SYLLABLE GA;Lo;0;L;;;;;N;;;;;
+1309;ETHIOPIC SYLLABLE GU;Lo;0;L;;;;;N;;;;;
+130A;ETHIOPIC SYLLABLE GI;Lo;0;L;;;;;N;;;;;
+130B;ETHIOPIC SYLLABLE GAA;Lo;0;L;;;;;N;;;;;
+130C;ETHIOPIC SYLLABLE GEE;Lo;0;L;;;;;N;;;;;
+130D;ETHIOPIC SYLLABLE GE;Lo;0;L;;;;;N;;;;;
+130E;ETHIOPIC SYLLABLE GO;Lo;0;L;;;;;N;;;;;
+1310;ETHIOPIC SYLLABLE GWA;Lo;0;L;;;;;N;;;;;
+1312;ETHIOPIC SYLLABLE GWI;Lo;0;L;;;;;N;;;;;
+1313;ETHIOPIC SYLLABLE GWAA;Lo;0;L;;;;;N;;;;;
+1314;ETHIOPIC SYLLABLE GWEE;Lo;0;L;;;;;N;;;;;
+1315;ETHIOPIC SYLLABLE GWE;Lo;0;L;;;;;N;;;;;
+1318;ETHIOPIC SYLLABLE GGA;Lo;0;L;;;;;N;;;;;
+1319;ETHIOPIC SYLLABLE GGU;Lo;0;L;;;;;N;;;;;
+131A;ETHIOPIC SYLLABLE GGI;Lo;0;L;;;;;N;;;;;
+131B;ETHIOPIC SYLLABLE GGAA;Lo;0;L;;;;;N;;;;;
+131C;ETHIOPIC SYLLABLE GGEE;Lo;0;L;;;;;N;;;;;
+131D;ETHIOPIC SYLLABLE GGE;Lo;0;L;;;;;N;;;;;
+131E;ETHIOPIC SYLLABLE GGO;Lo;0;L;;;;;N;;;;;
+1320;ETHIOPIC SYLLABLE THA;Lo;0;L;;;;;N;;;;;
+1321;ETHIOPIC SYLLABLE THU;Lo;0;L;;;;;N;;;;;
+1322;ETHIOPIC SYLLABLE THI;Lo;0;L;;;;;N;;;;;
+1323;ETHIOPIC SYLLABLE THAA;Lo;0;L;;;;;N;;;;;
+1324;ETHIOPIC SYLLABLE THEE;Lo;0;L;;;;;N;;;;;
+1325;ETHIOPIC SYLLABLE THE;Lo;0;L;;;;;N;;;;;
+1326;ETHIOPIC SYLLABLE THO;Lo;0;L;;;;;N;;;;;
+1327;ETHIOPIC SYLLABLE THWA;Lo;0;L;;;;;N;;;;;
+1328;ETHIOPIC SYLLABLE CHA;Lo;0;L;;;;;N;;;;;
+1329;ETHIOPIC SYLLABLE CHU;Lo;0;L;;;;;N;;;;;
+132A;ETHIOPIC SYLLABLE CHI;Lo;0;L;;;;;N;;;;;
+132B;ETHIOPIC SYLLABLE CHAA;Lo;0;L;;;;;N;;;;;
+132C;ETHIOPIC SYLLABLE CHEE;Lo;0;L;;;;;N;;;;;
+132D;ETHIOPIC SYLLABLE CHE;Lo;0;L;;;;;N;;;;;
+132E;ETHIOPIC SYLLABLE CHO;Lo;0;L;;;;;N;;;;;
+132F;ETHIOPIC SYLLABLE CHWA;Lo;0;L;;;;;N;;;;;
+1330;ETHIOPIC SYLLABLE PHA;Lo;0;L;;;;;N;;;;;
+1331;ETHIOPIC SYLLABLE PHU;Lo;0;L;;;;;N;;;;;
+1332;ETHIOPIC SYLLABLE PHI;Lo;0;L;;;;;N;;;;;
+1333;ETHIOPIC SYLLABLE PHAA;Lo;0;L;;;;;N;;;;;
+1334;ETHIOPIC SYLLABLE PHEE;Lo;0;L;;;;;N;;;;;
+1335;ETHIOPIC SYLLABLE PHE;Lo;0;L;;;;;N;;;;;
+1336;ETHIOPIC SYLLABLE PHO;Lo;0;L;;;;;N;;;;;
+1337;ETHIOPIC SYLLABLE PHWA;Lo;0;L;;;;;N;;;;;
+1338;ETHIOPIC SYLLABLE TSA;Lo;0;L;;;;;N;;;;;
+1339;ETHIOPIC SYLLABLE TSU;Lo;0;L;;;;;N;;;;;
+133A;ETHIOPIC SYLLABLE TSI;Lo;0;L;;;;;N;;;;;
+133B;ETHIOPIC SYLLABLE TSAA;Lo;0;L;;;;;N;;;;;
+133C;ETHIOPIC SYLLABLE TSEE;Lo;0;L;;;;;N;;;;;
+133D;ETHIOPIC SYLLABLE TSE;Lo;0;L;;;;;N;;;;;
+133E;ETHIOPIC SYLLABLE TSO;Lo;0;L;;;;;N;;;;;
+133F;ETHIOPIC SYLLABLE TSWA;Lo;0;L;;;;;N;;;;;
+1340;ETHIOPIC SYLLABLE TZA;Lo;0;L;;;;;N;;;;;
+1341;ETHIOPIC SYLLABLE TZU;Lo;0;L;;;;;N;;;;;
+1342;ETHIOPIC SYLLABLE TZI;Lo;0;L;;;;;N;;;;;
+1343;ETHIOPIC SYLLABLE TZAA;Lo;0;L;;;;;N;;;;;
+1344;ETHIOPIC SYLLABLE TZEE;Lo;0;L;;;;;N;;;;;
+1345;ETHIOPIC SYLLABLE TZE;Lo;0;L;;;;;N;;;;;
+1346;ETHIOPIC SYLLABLE TZO;Lo;0;L;;;;;N;;;;;
+1348;ETHIOPIC SYLLABLE FA;Lo;0;L;;;;;N;;;;;
+1349;ETHIOPIC SYLLABLE FU;Lo;0;L;;;;;N;;;;;
+134A;ETHIOPIC SYLLABLE FI;Lo;0;L;;;;;N;;;;;
+134B;ETHIOPIC SYLLABLE FAA;Lo;0;L;;;;;N;;;;;
+134C;ETHIOPIC SYLLABLE FEE;Lo;0;L;;;;;N;;;;;
+134D;ETHIOPIC SYLLABLE FE;Lo;0;L;;;;;N;;;;;
+134E;ETHIOPIC SYLLABLE FO;Lo;0;L;;;;;N;;;;;
+134F;ETHIOPIC SYLLABLE FWA;Lo;0;L;;;;;N;;;;;
+1350;ETHIOPIC SYLLABLE PA;Lo;0;L;;;;;N;;;;;
+1351;ETHIOPIC SYLLABLE PU;Lo;0;L;;;;;N;;;;;
+1352;ETHIOPIC SYLLABLE PI;Lo;0;L;;;;;N;;;;;
+1353;ETHIOPIC SYLLABLE PAA;Lo;0;L;;;;;N;;;;;
+1354;ETHIOPIC SYLLABLE PEE;Lo;0;L;;;;;N;;;;;
+1355;ETHIOPIC SYLLABLE PE;Lo;0;L;;;;;N;;;;;
+1356;ETHIOPIC SYLLABLE PO;Lo;0;L;;;;;N;;;;;
+1357;ETHIOPIC SYLLABLE PWA;Lo;0;L;;;;;N;;;;;
+1358;ETHIOPIC SYLLABLE RYA;Lo;0;L;;;;;N;;;;;
+1359;ETHIOPIC SYLLABLE MYA;Lo;0;L;;;;;N;;;;;
+135A;ETHIOPIC SYLLABLE FYA;Lo;0;L;;;;;N;;;;;
+1361;ETHIOPIC WORDSPACE;Po;0;L;;;;;N;;;;;
+1362;ETHIOPIC FULL STOP;Po;0;L;;;;;N;;;;;
+1363;ETHIOPIC COMMA;Po;0;L;;;;;N;;;;;
+1364;ETHIOPIC SEMICOLON;Po;0;L;;;;;N;;;;;
+1365;ETHIOPIC COLON;Po;0;L;;;;;N;;;;;
+1366;ETHIOPIC PREFACE COLON;Po;0;L;;;;;N;;;;;
+1367;ETHIOPIC QUESTION MARK;Po;0;L;;;;;N;;;;;
+1368;ETHIOPIC PARAGRAPH SEPARATOR;Po;0;L;;;;;N;;;;;
+1369;ETHIOPIC DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+136A;ETHIOPIC DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+136B;ETHIOPIC DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+136C;ETHIOPIC DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+136D;ETHIOPIC DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+136E;ETHIOPIC DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+136F;ETHIOPIC DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1370;ETHIOPIC DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1371;ETHIOPIC DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1372;ETHIOPIC NUMBER TEN;No;0;L;;;;10;N;;;;;
+1373;ETHIOPIC NUMBER TWENTY;No;0;L;;;;20;N;;;;;
+1374;ETHIOPIC NUMBER THIRTY;No;0;L;;;;30;N;;;;;
+1375;ETHIOPIC NUMBER FORTY;No;0;L;;;;40;N;;;;;
+1376;ETHIOPIC NUMBER FIFTY;No;0;L;;;;50;N;;;;;
+1377;ETHIOPIC NUMBER SIXTY;No;0;L;;;;60;N;;;;;
+1378;ETHIOPIC NUMBER SEVENTY;No;0;L;;;;70;N;;;;;
+1379;ETHIOPIC NUMBER EIGHTY;No;0;L;;;;80;N;;;;;
+137A;ETHIOPIC NUMBER NINETY;No;0;L;;;;90;N;;;;;
+137B;ETHIOPIC NUMBER HUNDRED;No;0;L;;;;100;N;;;;;
+137C;ETHIOPIC NUMBER TEN THOUSAND;No;0;L;;;;10000;N;;;;;
+13A0;CHEROKEE LETTER A;Lo;0;L;;;;;N;;;;;
+13A1;CHEROKEE LETTER E;Lo;0;L;;;;;N;;;;;
+13A2;CHEROKEE LETTER I;Lo;0;L;;;;;N;;;;;
+13A3;CHEROKEE LETTER O;Lo;0;L;;;;;N;;;;;
+13A4;CHEROKEE LETTER U;Lo;0;L;;;;;N;;;;;
+13A5;CHEROKEE LETTER V;Lo;0;L;;;;;N;;;;;
+13A6;CHEROKEE LETTER GA;Lo;0;L;;;;;N;;;;;
+13A7;CHEROKEE LETTER KA;Lo;0;L;;;;;N;;;;;
+13A8;CHEROKEE LETTER GE;Lo;0;L;;;;;N;;;;;
+13A9;CHEROKEE LETTER GI;Lo;0;L;;;;;N;;;;;
+13AA;CHEROKEE LETTER GO;Lo;0;L;;;;;N;;;;;
+13AB;CHEROKEE LETTER GU;Lo;0;L;;;;;N;;;;;
+13AC;CHEROKEE LETTER GV;Lo;0;L;;;;;N;;;;;
+13AD;CHEROKEE LETTER HA;Lo;0;L;;;;;N;;;;;
+13AE;CHEROKEE LETTER HE;Lo;0;L;;;;;N;;;;;
+13AF;CHEROKEE LETTER HI;Lo;0;L;;;;;N;;;;;
+13B0;CHEROKEE LETTER HO;Lo;0;L;;;;;N;;;;;
+13B1;CHEROKEE LETTER HU;Lo;0;L;;;;;N;;;;;
+13B2;CHEROKEE LETTER HV;Lo;0;L;;;;;N;;;;;
+13B3;CHEROKEE LETTER LA;Lo;0;L;;;;;N;;;;;
+13B4;CHEROKEE LETTER LE;Lo;0;L;;;;;N;;;;;
+13B5;CHEROKEE LETTER LI;Lo;0;L;;;;;N;;;;;
+13B6;CHEROKEE LETTER LO;Lo;0;L;;;;;N;;;;;
+13B7;CHEROKEE LETTER LU;Lo;0;L;;;;;N;;;;;
+13B8;CHEROKEE LETTER LV;Lo;0;L;;;;;N;;;;;
+13B9;CHEROKEE LETTER MA;Lo;0;L;;;;;N;;;;;
+13BA;CHEROKEE LETTER ME;Lo;0;L;;;;;N;;;;;
+13BB;CHEROKEE LETTER MI;Lo;0;L;;;;;N;;;;;
+13BC;CHEROKEE LETTER MO;Lo;0;L;;;;;N;;;;;
+13BD;CHEROKEE LETTER MU;Lo;0;L;;;;;N;;;;;
+13BE;CHEROKEE LETTER NA;Lo;0;L;;;;;N;;;;;
+13BF;CHEROKEE LETTER HNA;Lo;0;L;;;;;N;;;;;
+13C0;CHEROKEE LETTER NAH;Lo;0;L;;;;;N;;;;;
+13C1;CHEROKEE LETTER NE;Lo;0;L;;;;;N;;;;;
+13C2;CHEROKEE LETTER NI;Lo;0;L;;;;;N;;;;;
+13C3;CHEROKEE LETTER NO;Lo;0;L;;;;;N;;;;;
+13C4;CHEROKEE LETTER NU;Lo;0;L;;;;;N;;;;;
+13C5;CHEROKEE LETTER NV;Lo;0;L;;;;;N;;;;;
+13C6;CHEROKEE LETTER QUA;Lo;0;L;;;;;N;;;;;
+13C7;CHEROKEE LETTER QUE;Lo;0;L;;;;;N;;;;;
+13C8;CHEROKEE LETTER QUI;Lo;0;L;;;;;N;;;;;
+13C9;CHEROKEE LETTER QUO;Lo;0;L;;;;;N;;;;;
+13CA;CHEROKEE LETTER QUU;Lo;0;L;;;;;N;;;;;
+13CB;CHEROKEE LETTER QUV;Lo;0;L;;;;;N;;;;;
+13CC;CHEROKEE LETTER SA;Lo;0;L;;;;;N;;;;;
+13CD;CHEROKEE LETTER S;Lo;0;L;;;;;N;;;;;
+13CE;CHEROKEE LETTER SE;Lo;0;L;;;;;N;;;;;
+13CF;CHEROKEE LETTER SI;Lo;0;L;;;;;N;;;;;
+13D0;CHEROKEE LETTER SO;Lo;0;L;;;;;N;;;;;
+13D1;CHEROKEE LETTER SU;Lo;0;L;;;;;N;;;;;
+13D2;CHEROKEE LETTER SV;Lo;0;L;;;;;N;;;;;
+13D3;CHEROKEE LETTER DA;Lo;0;L;;;;;N;;;;;
+13D4;CHEROKEE LETTER TA;Lo;0;L;;;;;N;;;;;
+13D5;CHEROKEE LETTER DE;Lo;0;L;;;;;N;;;;;
+13D6;CHEROKEE LETTER TE;Lo;0;L;;;;;N;;;;;
+13D7;CHEROKEE LETTER DI;Lo;0;L;;;;;N;;;;;
+13D8;CHEROKEE LETTER TI;Lo;0;L;;;;;N;;;;;
+13D9;CHEROKEE LETTER DO;Lo;0;L;;;;;N;;;;;
+13DA;CHEROKEE LETTER DU;Lo;0;L;;;;;N;;;;;
+13DB;CHEROKEE LETTER DV;Lo;0;L;;;;;N;;;;;
+13DC;CHEROKEE LETTER DLA;Lo;0;L;;;;;N;;;;;
+13DD;CHEROKEE LETTER TLA;Lo;0;L;;;;;N;;;;;
+13DE;CHEROKEE LETTER TLE;Lo;0;L;;;;;N;;;;;
+13DF;CHEROKEE LETTER TLI;Lo;0;L;;;;;N;;;;;
+13E0;CHEROKEE LETTER TLO;Lo;0;L;;;;;N;;;;;
+13E1;CHEROKEE LETTER TLU;Lo;0;L;;;;;N;;;;;
+13E2;CHEROKEE LETTER TLV;Lo;0;L;;;;;N;;;;;
+13E3;CHEROKEE LETTER TSA;Lo;0;L;;;;;N;;;;;
+13E4;CHEROKEE LETTER TSE;Lo;0;L;;;;;N;;;;;
+13E5;CHEROKEE LETTER TSI;Lo;0;L;;;;;N;;;;;
+13E6;CHEROKEE LETTER TSO;Lo;0;L;;;;;N;;;;;
+13E7;CHEROKEE LETTER TSU;Lo;0;L;;;;;N;;;;;
+13E8;CHEROKEE LETTER TSV;Lo;0;L;;;;;N;;;;;
+13E9;CHEROKEE LETTER WA;Lo;0;L;;;;;N;;;;;
+13EA;CHEROKEE LETTER WE;Lo;0;L;;;;;N;;;;;
+13EB;CHEROKEE LETTER WI;Lo;0;L;;;;;N;;;;;
+13EC;CHEROKEE LETTER WO;Lo;0;L;;;;;N;;;;;
+13ED;CHEROKEE LETTER WU;Lo;0;L;;;;;N;;;;;
+13EE;CHEROKEE LETTER WV;Lo;0;L;;;;;N;;;;;
+13EF;CHEROKEE LETTER YA;Lo;0;L;;;;;N;;;;;
+13F0;CHEROKEE LETTER YE;Lo;0;L;;;;;N;;;;;
+13F1;CHEROKEE LETTER YI;Lo;0;L;;;;;N;;;;;
+13F2;CHEROKEE LETTER YO;Lo;0;L;;;;;N;;;;;
+13F3;CHEROKEE LETTER YU;Lo;0;L;;;;;N;;;;;
+13F4;CHEROKEE LETTER YV;Lo;0;L;;;;;N;;;;;
+1401;CANADIAN SYLLABICS E;Lo;0;L;;;;;N;;;;;
+1402;CANADIAN SYLLABICS AAI;Lo;0;L;;;;;N;;;;;
+1403;CANADIAN SYLLABICS I;Lo;0;L;;;;;N;;;;;
+1404;CANADIAN SYLLABICS II;Lo;0;L;;;;;N;;;;;
+1405;CANADIAN SYLLABICS O;Lo;0;L;;;;;N;;;;;
+1406;CANADIAN SYLLABICS OO;Lo;0;L;;;;;N;;;;;
+1407;CANADIAN SYLLABICS Y-CREE OO;Lo;0;L;;;;;N;;;;;
+1408;CANADIAN SYLLABICS CARRIER EE;Lo;0;L;;;;;N;;;;;
+1409;CANADIAN SYLLABICS CARRIER I;Lo;0;L;;;;;N;;;;;
+140A;CANADIAN SYLLABICS A;Lo;0;L;;;;;N;;;;;
+140B;CANADIAN SYLLABICS AA;Lo;0;L;;;;;N;;;;;
+140C;CANADIAN SYLLABICS WE;Lo;0;L;;;;;N;;;;;
+140D;CANADIAN SYLLABICS WEST-CREE WE;Lo;0;L;;;;;N;;;;;
+140E;CANADIAN SYLLABICS WI;Lo;0;L;;;;;N;;;;;
+140F;CANADIAN SYLLABICS WEST-CREE WI;Lo;0;L;;;;;N;;;;;
+1410;CANADIAN SYLLABICS WII;Lo;0;L;;;;;N;;;;;
+1411;CANADIAN SYLLABICS WEST-CREE WII;Lo;0;L;;;;;N;;;;;
+1412;CANADIAN SYLLABICS WO;Lo;0;L;;;;;N;;;;;
+1413;CANADIAN SYLLABICS WEST-CREE WO;Lo;0;L;;;;;N;;;;;
+1414;CANADIAN SYLLABICS WOO;Lo;0;L;;;;;N;;;;;
+1415;CANADIAN SYLLABICS WEST-CREE WOO;Lo;0;L;;;;;N;;;;;
+1416;CANADIAN SYLLABICS NASKAPI WOO;Lo;0;L;;;;;N;;;;;
+1417;CANADIAN SYLLABICS WA;Lo;0;L;;;;;N;;;;;
+1418;CANADIAN SYLLABICS WEST-CREE WA;Lo;0;L;;;;;N;;;;;
+1419;CANADIAN SYLLABICS WAA;Lo;0;L;;;;;N;;;;;
+141A;CANADIAN SYLLABICS WEST-CREE WAA;Lo;0;L;;;;;N;;;;;
+141B;CANADIAN SYLLABICS NASKAPI WAA;Lo;0;L;;;;;N;;;;;
+141C;CANADIAN SYLLABICS AI;Lo;0;L;;;;;N;;;;;
+141D;CANADIAN SYLLABICS Y-CREE W;Lo;0;L;;;;;N;;;;;
+141E;CANADIAN SYLLABICS GLOTTAL STOP;Lo;0;L;;;;;N;;;;;
+141F;CANADIAN SYLLABICS FINAL ACUTE;Lo;0;L;;;;;N;;;;;
+1420;CANADIAN SYLLABICS FINAL GRAVE;Lo;0;L;;;;;N;;;;;
+1421;CANADIAN SYLLABICS FINAL BOTTOM HALF RING;Lo;0;L;;;;;N;;;;;
+1422;CANADIAN SYLLABICS FINAL TOP HALF RING;Lo;0;L;;;;;N;;;;;
+1423;CANADIAN SYLLABICS FINAL RIGHT HALF RING;Lo;0;L;;;;;N;;;;;
+1424;CANADIAN SYLLABICS FINAL RING;Lo;0;L;;;;;N;;;;;
+1425;CANADIAN SYLLABICS FINAL DOUBLE ACUTE;Lo;0;L;;;;;N;;;;;
+1426;CANADIAN SYLLABICS FINAL DOUBLE SHORT VERTICAL STROKES;Lo;0;L;;;;;N;;;;;
+1427;CANADIAN SYLLABICS FINAL MIDDLE DOT;Lo;0;L;;;;;N;;;;;
+1428;CANADIAN SYLLABICS FINAL SHORT HORIZONTAL STROKE;Lo;0;L;;;;;N;;;;;
+1429;CANADIAN SYLLABICS FINAL PLUS;Lo;0;L;;;;;N;;;;;
+142A;CANADIAN SYLLABICS FINAL DOWN TACK;Lo;0;L;;;;;N;;;;;
+142B;CANADIAN SYLLABICS EN;Lo;0;L;;;;;N;;;;;
+142C;CANADIAN SYLLABICS IN;Lo;0;L;;;;;N;;;;;
+142D;CANADIAN SYLLABICS ON;Lo;0;L;;;;;N;;;;;
+142E;CANADIAN SYLLABICS AN;Lo;0;L;;;;;N;;;;;
+142F;CANADIAN SYLLABICS PE;Lo;0;L;;;;;N;;;;;
+1430;CANADIAN SYLLABICS PAAI;Lo;0;L;;;;;N;;;;;
+1431;CANADIAN SYLLABICS PI;Lo;0;L;;;;;N;;;;;
+1432;CANADIAN SYLLABICS PII;Lo;0;L;;;;;N;;;;;
+1433;CANADIAN SYLLABICS PO;Lo;0;L;;;;;N;;;;;
+1434;CANADIAN SYLLABICS POO;Lo;0;L;;;;;N;;;;;
+1435;CANADIAN SYLLABICS Y-CREE POO;Lo;0;L;;;;;N;;;;;
+1436;CANADIAN SYLLABICS CARRIER HEE;Lo;0;L;;;;;N;;;;;
+1437;CANADIAN SYLLABICS CARRIER HI;Lo;0;L;;;;;N;;;;;
+1438;CANADIAN SYLLABICS PA;Lo;0;L;;;;;N;;;;;
+1439;CANADIAN SYLLABICS PAA;Lo;0;L;;;;;N;;;;;
+143A;CANADIAN SYLLABICS PWE;Lo;0;L;;;;;N;;;;;
+143B;CANADIAN SYLLABICS WEST-CREE PWE;Lo;0;L;;;;;N;;;;;
+143C;CANADIAN SYLLABICS PWI;Lo;0;L;;;;;N;;;;;
+143D;CANADIAN SYLLABICS WEST-CREE PWI;Lo;0;L;;;;;N;;;;;
+143E;CANADIAN SYLLABICS PWII;Lo;0;L;;;;;N;;;;;
+143F;CANADIAN SYLLABICS WEST-CREE PWII;Lo;0;L;;;;;N;;;;;
+1440;CANADIAN SYLLABICS PWO;Lo;0;L;;;;;N;;;;;
+1441;CANADIAN SYLLABICS WEST-CREE PWO;Lo;0;L;;;;;N;;;;;
+1442;CANADIAN SYLLABICS PWOO;Lo;0;L;;;;;N;;;;;
+1443;CANADIAN SYLLABICS WEST-CREE PWOO;Lo;0;L;;;;;N;;;;;
+1444;CANADIAN SYLLABICS PWA;Lo;0;L;;;;;N;;;;;
+1445;CANADIAN SYLLABICS WEST-CREE PWA;Lo;0;L;;;;;N;;;;;
+1446;CANADIAN SYLLABICS PWAA;Lo;0;L;;;;;N;;;;;
+1447;CANADIAN SYLLABICS WEST-CREE PWAA;Lo;0;L;;;;;N;;;;;
+1448;CANADIAN SYLLABICS Y-CREE PWAA;Lo;0;L;;;;;N;;;;;
+1449;CANADIAN SYLLABICS P;Lo;0;L;;;;;N;;;;;
+144A;CANADIAN SYLLABICS WEST-CREE P;Lo;0;L;;;;;N;;;;;
+144B;CANADIAN SYLLABICS CARRIER H;Lo;0;L;;;;;N;;;;;
+144C;CANADIAN SYLLABICS TE;Lo;0;L;;;;;N;;;;;
+144D;CANADIAN SYLLABICS TAAI;Lo;0;L;;;;;N;;;;;
+144E;CANADIAN SYLLABICS TI;Lo;0;L;;;;;N;;;;;
+144F;CANADIAN SYLLABICS TII;Lo;0;L;;;;;N;;;;;
+1450;CANADIAN SYLLABICS TO;Lo;0;L;;;;;N;;;;;
+1451;CANADIAN SYLLABICS TOO;Lo;0;L;;;;;N;;;;;
+1452;CANADIAN SYLLABICS Y-CREE TOO;Lo;0;L;;;;;N;;;;;
+1453;CANADIAN SYLLABICS CARRIER DEE;Lo;0;L;;;;;N;;;;;
+1454;CANADIAN SYLLABICS CARRIER DI;Lo;0;L;;;;;N;;;;;
+1455;CANADIAN SYLLABICS TA;Lo;0;L;;;;;N;;;;;
+1456;CANADIAN SYLLABICS TAA;Lo;0;L;;;;;N;;;;;
+1457;CANADIAN SYLLABICS TWE;Lo;0;L;;;;;N;;;;;
+1458;CANADIAN SYLLABICS WEST-CREE TWE;Lo;0;L;;;;;N;;;;;
+1459;CANADIAN SYLLABICS TWI;Lo;0;L;;;;;N;;;;;
+145A;CANADIAN SYLLABICS WEST-CREE TWI;Lo;0;L;;;;;N;;;;;
+145B;CANADIAN SYLLABICS TWII;Lo;0;L;;;;;N;;;;;
+145C;CANADIAN SYLLABICS WEST-CREE TWII;Lo;0;L;;;;;N;;;;;
+145D;CANADIAN SYLLABICS TWO;Lo;0;L;;;;;N;;;;;
+145E;CANADIAN SYLLABICS WEST-CREE TWO;Lo;0;L;;;;;N;;;;;
+145F;CANADIAN SYLLABICS TWOO;Lo;0;L;;;;;N;;;;;
+1460;CANADIAN SYLLABICS WEST-CREE TWOO;Lo;0;L;;;;;N;;;;;
+1461;CANADIAN SYLLABICS TWA;Lo;0;L;;;;;N;;;;;
+1462;CANADIAN SYLLABICS WEST-CREE TWA;Lo;0;L;;;;;N;;;;;
+1463;CANADIAN SYLLABICS TWAA;Lo;0;L;;;;;N;;;;;
+1464;CANADIAN SYLLABICS WEST-CREE TWAA;Lo;0;L;;;;;N;;;;;
+1465;CANADIAN SYLLABICS NASKAPI TWAA;Lo;0;L;;;;;N;;;;;
+1466;CANADIAN SYLLABICS T;Lo;0;L;;;;;N;;;;;
+1467;CANADIAN SYLLABICS TTE;Lo;0;L;;;;;N;;;;;
+1468;CANADIAN SYLLABICS TTI;Lo;0;L;;;;;N;;;;;
+1469;CANADIAN SYLLABICS TTO;Lo;0;L;;;;;N;;;;;
+146A;CANADIAN SYLLABICS TTA;Lo;0;L;;;;;N;;;;;
+146B;CANADIAN SYLLABICS KE;Lo;0;L;;;;;N;;;;;
+146C;CANADIAN SYLLABICS KAAI;Lo;0;L;;;;;N;;;;;
+146D;CANADIAN SYLLABICS KI;Lo;0;L;;;;;N;;;;;
+146E;CANADIAN SYLLABICS KII;Lo;0;L;;;;;N;;;;;
+146F;CANADIAN SYLLABICS KO;Lo;0;L;;;;;N;;;;;
+1470;CANADIAN SYLLABICS KOO;Lo;0;L;;;;;N;;;;;
+1471;CANADIAN SYLLABICS Y-CREE KOO;Lo;0;L;;;;;N;;;;;
+1472;CANADIAN SYLLABICS KA;Lo;0;L;;;;;N;;;;;
+1473;CANADIAN SYLLABICS KAA;Lo;0;L;;;;;N;;;;;
+1474;CANADIAN SYLLABICS KWE;Lo;0;L;;;;;N;;;;;
+1475;CANADIAN SYLLABICS WEST-CREE KWE;Lo;0;L;;;;;N;;;;;
+1476;CANADIAN SYLLABICS KWI;Lo;0;L;;;;;N;;;;;
+1477;CANADIAN SYLLABICS WEST-CREE KWI;Lo;0;L;;;;;N;;;;;
+1478;CANADIAN SYLLABICS KWII;Lo;0;L;;;;;N;;;;;
+1479;CANADIAN SYLLABICS WEST-CREE KWII;Lo;0;L;;;;;N;;;;;
+147A;CANADIAN SYLLABICS KWO;Lo;0;L;;;;;N;;;;;
+147B;CANADIAN SYLLABICS WEST-CREE KWO;Lo;0;L;;;;;N;;;;;
+147C;CANADIAN SYLLABICS KWOO;Lo;0;L;;;;;N;;;;;
+147D;CANADIAN SYLLABICS WEST-CREE KWOO;Lo;0;L;;;;;N;;;;;
+147E;CANADIAN SYLLABICS KWA;Lo;0;L;;;;;N;;;;;
+147F;CANADIAN SYLLABICS WEST-CREE KWA;Lo;0;L;;;;;N;;;;;
+1480;CANADIAN SYLLABICS KWAA;Lo;0;L;;;;;N;;;;;
+1481;CANADIAN SYLLABICS WEST-CREE KWAA;Lo;0;L;;;;;N;;;;;
+1482;CANADIAN SYLLABICS NASKAPI KWAA;Lo;0;L;;;;;N;;;;;
+1483;CANADIAN SYLLABICS K;Lo;0;L;;;;;N;;;;;
+1484;CANADIAN SYLLABICS KW;Lo;0;L;;;;;N;;;;;
+1485;CANADIAN SYLLABICS SOUTH-SLAVEY KEH;Lo;0;L;;;;;N;;;;;
+1486;CANADIAN SYLLABICS SOUTH-SLAVEY KIH;Lo;0;L;;;;;N;;;;;
+1487;CANADIAN SYLLABICS SOUTH-SLAVEY KOH;Lo;0;L;;;;;N;;;;;
+1488;CANADIAN SYLLABICS SOUTH-SLAVEY KAH;Lo;0;L;;;;;N;;;;;
+1489;CANADIAN SYLLABICS CE;Lo;0;L;;;;;N;;;;;
+148A;CANADIAN SYLLABICS CAAI;Lo;0;L;;;;;N;;;;;
+148B;CANADIAN SYLLABICS CI;Lo;0;L;;;;;N;;;;;
+148C;CANADIAN SYLLABICS CII;Lo;0;L;;;;;N;;;;;
+148D;CANADIAN SYLLABICS CO;Lo;0;L;;;;;N;;;;;
+148E;CANADIAN SYLLABICS COO;Lo;0;L;;;;;N;;;;;
+148F;CANADIAN SYLLABICS Y-CREE COO;Lo;0;L;;;;;N;;;;;
+1490;CANADIAN SYLLABICS CA;Lo;0;L;;;;;N;;;;;
+1491;CANADIAN SYLLABICS CAA;Lo;0;L;;;;;N;;;;;
+1492;CANADIAN SYLLABICS CWE;Lo;0;L;;;;;N;;;;;
+1493;CANADIAN SYLLABICS WEST-CREE CWE;Lo;0;L;;;;;N;;;;;
+1494;CANADIAN SYLLABICS CWI;Lo;0;L;;;;;N;;;;;
+1495;CANADIAN SYLLABICS WEST-CREE CWI;Lo;0;L;;;;;N;;;;;
+1496;CANADIAN SYLLABICS CWII;Lo;0;L;;;;;N;;;;;
+1497;CANADIAN SYLLABICS WEST-CREE CWII;Lo;0;L;;;;;N;;;;;
+1498;CANADIAN SYLLABICS CWO;Lo;0;L;;;;;N;;;;;
+1499;CANADIAN SYLLABICS WEST-CREE CWO;Lo;0;L;;;;;N;;;;;
+149A;CANADIAN SYLLABICS CWOO;Lo;0;L;;;;;N;;;;;
+149B;CANADIAN SYLLABICS WEST-CREE CWOO;Lo;0;L;;;;;N;;;;;
+149C;CANADIAN SYLLABICS CWA;Lo;0;L;;;;;N;;;;;
+149D;CANADIAN SYLLABICS WEST-CREE CWA;Lo;0;L;;;;;N;;;;;
+149E;CANADIAN SYLLABICS CWAA;Lo;0;L;;;;;N;;;;;
+149F;CANADIAN SYLLABICS WEST-CREE CWAA;Lo;0;L;;;;;N;;;;;
+14A0;CANADIAN SYLLABICS NASKAPI CWAA;Lo;0;L;;;;;N;;;;;
+14A1;CANADIAN SYLLABICS C;Lo;0;L;;;;;N;;;;;
+14A2;CANADIAN SYLLABICS SAYISI TH;Lo;0;L;;;;;N;;;;;
+14A3;CANADIAN SYLLABICS ME;Lo;0;L;;;;;N;;;;;
+14A4;CANADIAN SYLLABICS MAAI;Lo;0;L;;;;;N;;;;;
+14A5;CANADIAN SYLLABICS MI;Lo;0;L;;;;;N;;;;;
+14A6;CANADIAN SYLLABICS MII;Lo;0;L;;;;;N;;;;;
+14A7;CANADIAN SYLLABICS MO;Lo;0;L;;;;;N;;;;;
+14A8;CANADIAN SYLLABICS MOO;Lo;0;L;;;;;N;;;;;
+14A9;CANADIAN SYLLABICS Y-CREE MOO;Lo;0;L;;;;;N;;;;;
+14AA;CANADIAN SYLLABICS MA;Lo;0;L;;;;;N;;;;;
+14AB;CANADIAN SYLLABICS MAA;Lo;0;L;;;;;N;;;;;
+14AC;CANADIAN SYLLABICS MWE;Lo;0;L;;;;;N;;;;;
+14AD;CANADIAN SYLLABICS WEST-CREE MWE;Lo;0;L;;;;;N;;;;;
+14AE;CANADIAN SYLLABICS MWI;Lo;0;L;;;;;N;;;;;
+14AF;CANADIAN SYLLABICS WEST-CREE MWI;Lo;0;L;;;;;N;;;;;
+14B0;CANADIAN SYLLABICS MWII;Lo;0;L;;;;;N;;;;;
+14B1;CANADIAN SYLLABICS WEST-CREE MWII;Lo;0;L;;;;;N;;;;;
+14B2;CANADIAN SYLLABICS MWO;Lo;0;L;;;;;N;;;;;
+14B3;CANADIAN SYLLABICS WEST-CREE MWO;Lo;0;L;;;;;N;;;;;
+14B4;CANADIAN SYLLABICS MWOO;Lo;0;L;;;;;N;;;;;
+14B5;CANADIAN SYLLABICS WEST-CREE MWOO;Lo;0;L;;;;;N;;;;;
+14B6;CANADIAN SYLLABICS MWA;Lo;0;L;;;;;N;;;;;
+14B7;CANADIAN SYLLABICS WEST-CREE MWA;Lo;0;L;;;;;N;;;;;
+14B8;CANADIAN SYLLABICS MWAA;Lo;0;L;;;;;N;;;;;
+14B9;CANADIAN SYLLABICS WEST-CREE MWAA;Lo;0;L;;;;;N;;;;;
+14BA;CANADIAN SYLLABICS NASKAPI MWAA;Lo;0;L;;;;;N;;;;;
+14BB;CANADIAN SYLLABICS M;Lo;0;L;;;;;N;;;;;
+14BC;CANADIAN SYLLABICS WEST-CREE M;Lo;0;L;;;;;N;;;;;
+14BD;CANADIAN SYLLABICS MH;Lo;0;L;;;;;N;;;;;
+14BE;CANADIAN SYLLABICS ATHAPASCAN M;Lo;0;L;;;;;N;;;;;
+14BF;CANADIAN SYLLABICS SAYISI M;Lo;0;L;;;;;N;;;;;
+14C0;CANADIAN SYLLABICS NE;Lo;0;L;;;;;N;;;;;
+14C1;CANADIAN SYLLABICS NAAI;Lo;0;L;;;;;N;;;;;
+14C2;CANADIAN SYLLABICS NI;Lo;0;L;;;;;N;;;;;
+14C3;CANADIAN SYLLABICS NII;Lo;0;L;;;;;N;;;;;
+14C4;CANADIAN SYLLABICS NO;Lo;0;L;;;;;N;;;;;
+14C5;CANADIAN SYLLABICS NOO;Lo;0;L;;;;;N;;;;;
+14C6;CANADIAN SYLLABICS Y-CREE NOO;Lo;0;L;;;;;N;;;;;
+14C7;CANADIAN SYLLABICS NA;Lo;0;L;;;;;N;;;;;
+14C8;CANADIAN SYLLABICS NAA;Lo;0;L;;;;;N;;;;;
+14C9;CANADIAN SYLLABICS NWE;Lo;0;L;;;;;N;;;;;
+14CA;CANADIAN SYLLABICS WEST-CREE NWE;Lo;0;L;;;;;N;;;;;
+14CB;CANADIAN SYLLABICS NWA;Lo;0;L;;;;;N;;;;;
+14CC;CANADIAN SYLLABICS WEST-CREE NWA;Lo;0;L;;;;;N;;;;;
+14CD;CANADIAN SYLLABICS NWAA;Lo;0;L;;;;;N;;;;;
+14CE;CANADIAN SYLLABICS WEST-CREE NWAA;Lo;0;L;;;;;N;;;;;
+14CF;CANADIAN SYLLABICS NASKAPI NWAA;Lo;0;L;;;;;N;;;;;
+14D0;CANADIAN SYLLABICS N;Lo;0;L;;;;;N;;;;;
+14D1;CANADIAN SYLLABICS CARRIER NG;Lo;0;L;;;;;N;;;;;
+14D2;CANADIAN SYLLABICS NH;Lo;0;L;;;;;N;;;;;
+14D3;CANADIAN SYLLABICS LE;Lo;0;L;;;;;N;;;;;
+14D4;CANADIAN SYLLABICS LAAI;Lo;0;L;;;;;N;;;;;
+14D5;CANADIAN SYLLABICS LI;Lo;0;L;;;;;N;;;;;
+14D6;CANADIAN SYLLABICS LII;Lo;0;L;;;;;N;;;;;
+14D7;CANADIAN SYLLABICS LO;Lo;0;L;;;;;N;;;;;
+14D8;CANADIAN SYLLABICS LOO;Lo;0;L;;;;;N;;;;;
+14D9;CANADIAN SYLLABICS Y-CREE LOO;Lo;0;L;;;;;N;;;;;
+14DA;CANADIAN SYLLABICS LA;Lo;0;L;;;;;N;;;;;
+14DB;CANADIAN SYLLABICS LAA;Lo;0;L;;;;;N;;;;;
+14DC;CANADIAN SYLLABICS LWE;Lo;0;L;;;;;N;;;;;
+14DD;CANADIAN SYLLABICS WEST-CREE LWE;Lo;0;L;;;;;N;;;;;
+14DE;CANADIAN SYLLABICS LWI;Lo;0;L;;;;;N;;;;;
+14DF;CANADIAN SYLLABICS WEST-CREE LWI;Lo;0;L;;;;;N;;;;;
+14E0;CANADIAN SYLLABICS LWII;Lo;0;L;;;;;N;;;;;
+14E1;CANADIAN SYLLABICS WEST-CREE LWII;Lo;0;L;;;;;N;;;;;
+14E2;CANADIAN SYLLABICS LWO;Lo;0;L;;;;;N;;;;;
+14E3;CANADIAN SYLLABICS WEST-CREE LWO;Lo;0;L;;;;;N;;;;;
+14E4;CANADIAN SYLLABICS LWOO;Lo;0;L;;;;;N;;;;;
+14E5;CANADIAN SYLLABICS WEST-CREE LWOO;Lo;0;L;;;;;N;;;;;
+14E6;CANADIAN SYLLABICS LWA;Lo;0;L;;;;;N;;;;;
+14E7;CANADIAN SYLLABICS WEST-CREE LWA;Lo;0;L;;;;;N;;;;;
+14E8;CANADIAN SYLLABICS LWAA;Lo;0;L;;;;;N;;;;;
+14E9;CANADIAN SYLLABICS WEST-CREE LWAA;Lo;0;L;;;;;N;;;;;
+14EA;CANADIAN SYLLABICS L;Lo;0;L;;;;;N;;;;;
+14EB;CANADIAN SYLLABICS WEST-CREE L;Lo;0;L;;;;;N;;;;;
+14EC;CANADIAN SYLLABICS MEDIAL L;Lo;0;L;;;;;N;;;;;
+14ED;CANADIAN SYLLABICS SE;Lo;0;L;;;;;N;;;;;
+14EE;CANADIAN SYLLABICS SAAI;Lo;0;L;;;;;N;;;;;
+14EF;CANADIAN SYLLABICS SI;Lo;0;L;;;;;N;;;;;
+14F0;CANADIAN SYLLABICS SII;Lo;0;L;;;;;N;;;;;
+14F1;CANADIAN SYLLABICS SO;Lo;0;L;;;;;N;;;;;
+14F2;CANADIAN SYLLABICS SOO;Lo;0;L;;;;;N;;;;;
+14F3;CANADIAN SYLLABICS Y-CREE SOO;Lo;0;L;;;;;N;;;;;
+14F4;CANADIAN SYLLABICS SA;Lo;0;L;;;;;N;;;;;
+14F5;CANADIAN SYLLABICS SAA;Lo;0;L;;;;;N;;;;;
+14F6;CANADIAN SYLLABICS SWE;Lo;0;L;;;;;N;;;;;
+14F7;CANADIAN SYLLABICS WEST-CREE SWE;Lo;0;L;;;;;N;;;;;
+14F8;CANADIAN SYLLABICS SWI;Lo;0;L;;;;;N;;;;;
+14F9;CANADIAN SYLLABICS WEST-CREE SWI;Lo;0;L;;;;;N;;;;;
+14FA;CANADIAN SYLLABICS SWII;Lo;0;L;;;;;N;;;;;
+14FB;CANADIAN SYLLABICS WEST-CREE SWII;Lo;0;L;;;;;N;;;;;
+14FC;CANADIAN SYLLABICS SWO;Lo;0;L;;;;;N;;;;;
+14FD;CANADIAN SYLLABICS WEST-CREE SWO;Lo;0;L;;;;;N;;;;;
+14FE;CANADIAN SYLLABICS SWOO;Lo;0;L;;;;;N;;;;;
+14FF;CANADIAN SYLLABICS WEST-CREE SWOO;Lo;0;L;;;;;N;;;;;
+1500;CANADIAN SYLLABICS SWA;Lo;0;L;;;;;N;;;;;
+1501;CANADIAN SYLLABICS WEST-CREE SWA;Lo;0;L;;;;;N;;;;;
+1502;CANADIAN SYLLABICS SWAA;Lo;0;L;;;;;N;;;;;
+1503;CANADIAN SYLLABICS WEST-CREE SWAA;Lo;0;L;;;;;N;;;;;
+1504;CANADIAN SYLLABICS NASKAPI SWAA;Lo;0;L;;;;;N;;;;;
+1505;CANADIAN SYLLABICS S;Lo;0;L;;;;;N;;;;;
+1506;CANADIAN SYLLABICS ATHAPASCAN S;Lo;0;L;;;;;N;;;;;
+1507;CANADIAN SYLLABICS SW;Lo;0;L;;;;;N;;;;;
+1508;CANADIAN SYLLABICS BLACKFOOT S;Lo;0;L;;;;;N;;;;;
+1509;CANADIAN SYLLABICS MOOSE-CREE SK;Lo;0;L;;;;;N;;;;;
+150A;CANADIAN SYLLABICS NASKAPI SKW;Lo;0;L;;;;;N;;;;;
+150B;CANADIAN SYLLABICS NASKAPI S-W;Lo;0;L;;;;;N;;;;;
+150C;CANADIAN SYLLABICS NASKAPI SPWA;Lo;0;L;;;;;N;;;;;
+150D;CANADIAN SYLLABICS NASKAPI STWA;Lo;0;L;;;;;N;;;;;
+150E;CANADIAN SYLLABICS NASKAPI SKWA;Lo;0;L;;;;;N;;;;;
+150F;CANADIAN SYLLABICS NASKAPI SCWA;Lo;0;L;;;;;N;;;;;
+1510;CANADIAN SYLLABICS SHE;Lo;0;L;;;;;N;;;;;
+1511;CANADIAN SYLLABICS SHI;Lo;0;L;;;;;N;;;;;
+1512;CANADIAN SYLLABICS SHII;Lo;0;L;;;;;N;;;;;
+1513;CANADIAN SYLLABICS SHO;Lo;0;L;;;;;N;;;;;
+1514;CANADIAN SYLLABICS SHOO;Lo;0;L;;;;;N;;;;;
+1515;CANADIAN SYLLABICS SHA;Lo;0;L;;;;;N;;;;;
+1516;CANADIAN SYLLABICS SHAA;Lo;0;L;;;;;N;;;;;
+1517;CANADIAN SYLLABICS SHWE;Lo;0;L;;;;;N;;;;;
+1518;CANADIAN SYLLABICS WEST-CREE SHWE;Lo;0;L;;;;;N;;;;;
+1519;CANADIAN SYLLABICS SHWI;Lo;0;L;;;;;N;;;;;
+151A;CANADIAN SYLLABICS WEST-CREE SHWI;Lo;0;L;;;;;N;;;;;
+151B;CANADIAN SYLLABICS SHWII;Lo;0;L;;;;;N;;;;;
+151C;CANADIAN SYLLABICS WEST-CREE SHWII;Lo;0;L;;;;;N;;;;;
+151D;CANADIAN SYLLABICS SHWO;Lo;0;L;;;;;N;;;;;
+151E;CANADIAN SYLLABICS WEST-CREE SHWO;Lo;0;L;;;;;N;;;;;
+151F;CANADIAN SYLLABICS SHWOO;Lo;0;L;;;;;N;;;;;
+1520;CANADIAN SYLLABICS WEST-CREE SHWOO;Lo;0;L;;;;;N;;;;;
+1521;CANADIAN SYLLABICS SHWA;Lo;0;L;;;;;N;;;;;
+1522;CANADIAN SYLLABICS WEST-CREE SHWA;Lo;0;L;;;;;N;;;;;
+1523;CANADIAN SYLLABICS SHWAA;Lo;0;L;;;;;N;;;;;
+1524;CANADIAN SYLLABICS WEST-CREE SHWAA;Lo;0;L;;;;;N;;;;;
+1525;CANADIAN SYLLABICS SH;Lo;0;L;;;;;N;;;;;
+1526;CANADIAN SYLLABICS YE;Lo;0;L;;;;;N;;;;;
+1527;CANADIAN SYLLABICS YAAI;Lo;0;L;;;;;N;;;;;
+1528;CANADIAN SYLLABICS YI;Lo;0;L;;;;;N;;;;;
+1529;CANADIAN SYLLABICS YII;Lo;0;L;;;;;N;;;;;
+152A;CANADIAN SYLLABICS YO;Lo;0;L;;;;;N;;;;;
+152B;CANADIAN SYLLABICS YOO;Lo;0;L;;;;;N;;;;;
+152C;CANADIAN SYLLABICS Y-CREE YOO;Lo;0;L;;;;;N;;;;;
+152D;CANADIAN SYLLABICS YA;Lo;0;L;;;;;N;;;;;
+152E;CANADIAN SYLLABICS YAA;Lo;0;L;;;;;N;;;;;
+152F;CANADIAN SYLLABICS YWE;Lo;0;L;;;;;N;;;;;
+1530;CANADIAN SYLLABICS WEST-CREE YWE;Lo;0;L;;;;;N;;;;;
+1531;CANADIAN SYLLABICS YWI;Lo;0;L;;;;;N;;;;;
+1532;CANADIAN SYLLABICS WEST-CREE YWI;Lo;0;L;;;;;N;;;;;
+1533;CANADIAN SYLLABICS YWII;Lo;0;L;;;;;N;;;;;
+1534;CANADIAN SYLLABICS WEST-CREE YWII;Lo;0;L;;;;;N;;;;;
+1535;CANADIAN SYLLABICS YWO;Lo;0;L;;;;;N;;;;;
+1536;CANADIAN SYLLABICS WEST-CREE YWO;Lo;0;L;;;;;N;;;;;
+1537;CANADIAN SYLLABICS YWOO;Lo;0;L;;;;;N;;;;;
+1538;CANADIAN SYLLABICS WEST-CREE YWOO;Lo;0;L;;;;;N;;;;;
+1539;CANADIAN SYLLABICS YWA;Lo;0;L;;;;;N;;;;;
+153A;CANADIAN SYLLABICS WEST-CREE YWA;Lo;0;L;;;;;N;;;;;
+153B;CANADIAN SYLLABICS YWAA;Lo;0;L;;;;;N;;;;;
+153C;CANADIAN SYLLABICS WEST-CREE YWAA;Lo;0;L;;;;;N;;;;;
+153D;CANADIAN SYLLABICS NASKAPI YWAA;Lo;0;L;;;;;N;;;;;
+153E;CANADIAN SYLLABICS Y;Lo;0;L;;;;;N;;;;;
+153F;CANADIAN SYLLABICS BIBLE-CREE Y;Lo;0;L;;;;;N;;;;;
+1540;CANADIAN SYLLABICS WEST-CREE Y;Lo;0;L;;;;;N;;;;;
+1541;CANADIAN SYLLABICS SAYISI YI;Lo;0;L;;;;;N;;;;;
+1542;CANADIAN SYLLABICS RE;Lo;0;L;;;;;N;;;;;
+1543;CANADIAN SYLLABICS R-CREE RE;Lo;0;L;;;;;N;;;;;
+1544;CANADIAN SYLLABICS WEST-CREE LE;Lo;0;L;;;;;N;;;;;
+1545;CANADIAN SYLLABICS RAAI;Lo;0;L;;;;;N;;;;;
+1546;CANADIAN SYLLABICS RI;Lo;0;L;;;;;N;;;;;
+1547;CANADIAN SYLLABICS RII;Lo;0;L;;;;;N;;;;;
+1548;CANADIAN SYLLABICS RO;Lo;0;L;;;;;N;;;;;
+1549;CANADIAN SYLLABICS ROO;Lo;0;L;;;;;N;;;;;
+154A;CANADIAN SYLLABICS WEST-CREE LO;Lo;0;L;;;;;N;;;;;
+154B;CANADIAN SYLLABICS RA;Lo;0;L;;;;;N;;;;;
+154C;CANADIAN SYLLABICS RAA;Lo;0;L;;;;;N;;;;;
+154D;CANADIAN SYLLABICS WEST-CREE LA;Lo;0;L;;;;;N;;;;;
+154E;CANADIAN SYLLABICS RWAA;Lo;0;L;;;;;N;;;;;
+154F;CANADIAN SYLLABICS WEST-CREE RWAA;Lo;0;L;;;;;N;;;;;
+1550;CANADIAN SYLLABICS R;Lo;0;L;;;;;N;;;;;
+1551;CANADIAN SYLLABICS WEST-CREE R;Lo;0;L;;;;;N;;;;;
+1552;CANADIAN SYLLABICS MEDIAL R;Lo;0;L;;;;;N;;;;;
+1553;CANADIAN SYLLABICS FE;Lo;0;L;;;;;N;;;;;
+1554;CANADIAN SYLLABICS FAAI;Lo;0;L;;;;;N;;;;;
+1555;CANADIAN SYLLABICS FI;Lo;0;L;;;;;N;;;;;
+1556;CANADIAN SYLLABICS FII;Lo;0;L;;;;;N;;;;;
+1557;CANADIAN SYLLABICS FO;Lo;0;L;;;;;N;;;;;
+1558;CANADIAN SYLLABICS FOO;Lo;0;L;;;;;N;;;;;
+1559;CANADIAN SYLLABICS FA;Lo;0;L;;;;;N;;;;;
+155A;CANADIAN SYLLABICS FAA;Lo;0;L;;;;;N;;;;;
+155B;CANADIAN SYLLABICS FWAA;Lo;0;L;;;;;N;;;;;
+155C;CANADIAN SYLLABICS WEST-CREE FWAA;Lo;0;L;;;;;N;;;;;
+155D;CANADIAN SYLLABICS F;Lo;0;L;;;;;N;;;;;
+155E;CANADIAN SYLLABICS THE;Lo;0;L;;;;;N;;;;;
+155F;CANADIAN SYLLABICS N-CREE THE;Lo;0;L;;;;;N;;;;;
+1560;CANADIAN SYLLABICS THI;Lo;0;L;;;;;N;;;;;
+1561;CANADIAN SYLLABICS N-CREE THI;Lo;0;L;;;;;N;;;;;
+1562;CANADIAN SYLLABICS THII;Lo;0;L;;;;;N;;;;;
+1563;CANADIAN SYLLABICS N-CREE THII;Lo;0;L;;;;;N;;;;;
+1564;CANADIAN SYLLABICS THO;Lo;0;L;;;;;N;;;;;
+1565;CANADIAN SYLLABICS THOO;Lo;0;L;;;;;N;;;;;
+1566;CANADIAN SYLLABICS THA;Lo;0;L;;;;;N;;;;;
+1567;CANADIAN SYLLABICS THAA;Lo;0;L;;;;;N;;;;;
+1568;CANADIAN SYLLABICS THWAA;Lo;0;L;;;;;N;;;;;
+1569;CANADIAN SYLLABICS WEST-CREE THWAA;Lo;0;L;;;;;N;;;;;
+156A;CANADIAN SYLLABICS TH;Lo;0;L;;;;;N;;;;;
+156B;CANADIAN SYLLABICS TTHE;Lo;0;L;;;;;N;;;;;
+156C;CANADIAN SYLLABICS TTHI;Lo;0;L;;;;;N;;;;;
+156D;CANADIAN SYLLABICS TTHO;Lo;0;L;;;;;N;;;;;
+156E;CANADIAN SYLLABICS TTHA;Lo;0;L;;;;;N;;;;;
+156F;CANADIAN SYLLABICS TTH;Lo;0;L;;;;;N;;;;;
+1570;CANADIAN SYLLABICS TYE;Lo;0;L;;;;;N;;;;;
+1571;CANADIAN SYLLABICS TYI;Lo;0;L;;;;;N;;;;;
+1572;CANADIAN SYLLABICS TYO;Lo;0;L;;;;;N;;;;;
+1573;CANADIAN SYLLABICS TYA;Lo;0;L;;;;;N;;;;;
+1574;CANADIAN SYLLABICS NUNAVIK HE;Lo;0;L;;;;;N;;;;;
+1575;CANADIAN SYLLABICS NUNAVIK HI;Lo;0;L;;;;;N;;;;;
+1576;CANADIAN SYLLABICS NUNAVIK HII;Lo;0;L;;;;;N;;;;;
+1577;CANADIAN SYLLABICS NUNAVIK HO;Lo;0;L;;;;;N;;;;;
+1578;CANADIAN SYLLABICS NUNAVIK HOO;Lo;0;L;;;;;N;;;;;
+1579;CANADIAN SYLLABICS NUNAVIK HA;Lo;0;L;;;;;N;;;;;
+157A;CANADIAN SYLLABICS NUNAVIK HAA;Lo;0;L;;;;;N;;;;;
+157B;CANADIAN SYLLABICS NUNAVIK H;Lo;0;L;;;;;N;;;;;
+157C;CANADIAN SYLLABICS NUNAVUT H;Lo;0;L;;;;;N;;;;;
+157D;CANADIAN SYLLABICS HK;Lo;0;L;;;;;N;;;;;
+157E;CANADIAN SYLLABICS QAAI;Lo;0;L;;;;;N;;;;;
+157F;CANADIAN SYLLABICS QI;Lo;0;L;;;;;N;;;;;
+1580;CANADIAN SYLLABICS QII;Lo;0;L;;;;;N;;;;;
+1581;CANADIAN SYLLABICS QO;Lo;0;L;;;;;N;;;;;
+1582;CANADIAN SYLLABICS QOO;Lo;0;L;;;;;N;;;;;
+1583;CANADIAN SYLLABICS QA;Lo;0;L;;;;;N;;;;;
+1584;CANADIAN SYLLABICS QAA;Lo;0;L;;;;;N;;;;;
+1585;CANADIAN SYLLABICS Q;Lo;0;L;;;;;N;;;;;
+1586;CANADIAN SYLLABICS TLHE;Lo;0;L;;;;;N;;;;;
+1587;CANADIAN SYLLABICS TLHI;Lo;0;L;;;;;N;;;;;
+1588;CANADIAN SYLLABICS TLHO;Lo;0;L;;;;;N;;;;;
+1589;CANADIAN SYLLABICS TLHA;Lo;0;L;;;;;N;;;;;
+158A;CANADIAN SYLLABICS WEST-CREE RE;Lo;0;L;;;;;N;;;;;
+158B;CANADIAN SYLLABICS WEST-CREE RI;Lo;0;L;;;;;N;;;;;
+158C;CANADIAN SYLLABICS WEST-CREE RO;Lo;0;L;;;;;N;;;;;
+158D;CANADIAN SYLLABICS WEST-CREE RA;Lo;0;L;;;;;N;;;;;
+158E;CANADIAN SYLLABICS NGAAI;Lo;0;L;;;;;N;;;;;
+158F;CANADIAN SYLLABICS NGI;Lo;0;L;;;;;N;;;;;
+1590;CANADIAN SYLLABICS NGII;Lo;0;L;;;;;N;;;;;
+1591;CANADIAN SYLLABICS NGO;Lo;0;L;;;;;N;;;;;
+1592;CANADIAN SYLLABICS NGOO;Lo;0;L;;;;;N;;;;;
+1593;CANADIAN SYLLABICS NGA;Lo;0;L;;;;;N;;;;;
+1594;CANADIAN SYLLABICS NGAA;Lo;0;L;;;;;N;;;;;
+1595;CANADIAN SYLLABICS NG;Lo;0;L;;;;;N;;;;;
+1596;CANADIAN SYLLABICS NNG;Lo;0;L;;;;;N;;;;;
+1597;CANADIAN SYLLABICS SAYISI SHE;Lo;0;L;;;;;N;;;;;
+1598;CANADIAN SYLLABICS SAYISI SHI;Lo;0;L;;;;;N;;;;;
+1599;CANADIAN SYLLABICS SAYISI SHO;Lo;0;L;;;;;N;;;;;
+159A;CANADIAN SYLLABICS SAYISI SHA;Lo;0;L;;;;;N;;;;;
+159B;CANADIAN SYLLABICS WOODS-CREE THE;Lo;0;L;;;;;N;;;;;
+159C;CANADIAN SYLLABICS WOODS-CREE THI;Lo;0;L;;;;;N;;;;;
+159D;CANADIAN SYLLABICS WOODS-CREE THO;Lo;0;L;;;;;N;;;;;
+159E;CANADIAN SYLLABICS WOODS-CREE THA;Lo;0;L;;;;;N;;;;;
+159F;CANADIAN SYLLABICS WOODS-CREE TH;Lo;0;L;;;;;N;;;;;
+15A0;CANADIAN SYLLABICS LHI;Lo;0;L;;;;;N;;;;;
+15A1;CANADIAN SYLLABICS LHII;Lo;0;L;;;;;N;;;;;
+15A2;CANADIAN SYLLABICS LHO;Lo;0;L;;;;;N;;;;;
+15A3;CANADIAN SYLLABICS LHOO;Lo;0;L;;;;;N;;;;;
+15A4;CANADIAN SYLLABICS LHA;Lo;0;L;;;;;N;;;;;
+15A5;CANADIAN SYLLABICS LHAA;Lo;0;L;;;;;N;;;;;
+15A6;CANADIAN SYLLABICS LH;Lo;0;L;;;;;N;;;;;
+15A7;CANADIAN SYLLABICS TH-CREE THE;Lo;0;L;;;;;N;;;;;
+15A8;CANADIAN SYLLABICS TH-CREE THI;Lo;0;L;;;;;N;;;;;
+15A9;CANADIAN SYLLABICS TH-CREE THII;Lo;0;L;;;;;N;;;;;
+15AA;CANADIAN SYLLABICS TH-CREE THO;Lo;0;L;;;;;N;;;;;
+15AB;CANADIAN SYLLABICS TH-CREE THOO;Lo;0;L;;;;;N;;;;;
+15AC;CANADIAN SYLLABICS TH-CREE THA;Lo;0;L;;;;;N;;;;;
+15AD;CANADIAN SYLLABICS TH-CREE THAA;Lo;0;L;;;;;N;;;;;
+15AE;CANADIAN SYLLABICS TH-CREE TH;Lo;0;L;;;;;N;;;;;
+15AF;CANADIAN SYLLABICS AIVILIK B;Lo;0;L;;;;;N;;;;;
+15B0;CANADIAN SYLLABICS BLACKFOOT E;Lo;0;L;;;;;N;;;;;
+15B1;CANADIAN SYLLABICS BLACKFOOT I;Lo;0;L;;;;;N;;;;;
+15B2;CANADIAN SYLLABICS BLACKFOOT O;Lo;0;L;;;;;N;;;;;
+15B3;CANADIAN SYLLABICS BLACKFOOT A;Lo;0;L;;;;;N;;;;;
+15B4;CANADIAN SYLLABICS BLACKFOOT WE;Lo;0;L;;;;;N;;;;;
+15B5;CANADIAN SYLLABICS BLACKFOOT WI;Lo;0;L;;;;;N;;;;;
+15B6;CANADIAN SYLLABICS BLACKFOOT WO;Lo;0;L;;;;;N;;;;;
+15B7;CANADIAN SYLLABICS BLACKFOOT WA;Lo;0;L;;;;;N;;;;;
+15B8;CANADIAN SYLLABICS BLACKFOOT NE;Lo;0;L;;;;;N;;;;;
+15B9;CANADIAN SYLLABICS BLACKFOOT NI;Lo;0;L;;;;;N;;;;;
+15BA;CANADIAN SYLLABICS BLACKFOOT NO;Lo;0;L;;;;;N;;;;;
+15BB;CANADIAN SYLLABICS BLACKFOOT NA;Lo;0;L;;;;;N;;;;;
+15BC;CANADIAN SYLLABICS BLACKFOOT KE;Lo;0;L;;;;;N;;;;;
+15BD;CANADIAN SYLLABICS BLACKFOOT KI;Lo;0;L;;;;;N;;;;;
+15BE;CANADIAN SYLLABICS BLACKFOOT KO;Lo;0;L;;;;;N;;;;;
+15BF;CANADIAN SYLLABICS BLACKFOOT KA;Lo;0;L;;;;;N;;;;;
+15C0;CANADIAN SYLLABICS SAYISI HE;Lo;0;L;;;;;N;;;;;
+15C1;CANADIAN SYLLABICS SAYISI HI;Lo;0;L;;;;;N;;;;;
+15C2;CANADIAN SYLLABICS SAYISI HO;Lo;0;L;;;;;N;;;;;
+15C3;CANADIAN SYLLABICS SAYISI HA;Lo;0;L;;;;;N;;;;;
+15C4;CANADIAN SYLLABICS CARRIER GHU;Lo;0;L;;;;;N;;;;;
+15C5;CANADIAN SYLLABICS CARRIER GHO;Lo;0;L;;;;;N;;;;;
+15C6;CANADIAN SYLLABICS CARRIER GHE;Lo;0;L;;;;;N;;;;;
+15C7;CANADIAN SYLLABICS CARRIER GHEE;Lo;0;L;;;;;N;;;;;
+15C8;CANADIAN SYLLABICS CARRIER GHI;Lo;0;L;;;;;N;;;;;
+15C9;CANADIAN SYLLABICS CARRIER GHA;Lo;0;L;;;;;N;;;;;
+15CA;CANADIAN SYLLABICS CARRIER RU;Lo;0;L;;;;;N;;;;;
+15CB;CANADIAN SYLLABICS CARRIER RO;Lo;0;L;;;;;N;;;;;
+15CC;CANADIAN SYLLABICS CARRIER RE;Lo;0;L;;;;;N;;;;;
+15CD;CANADIAN SYLLABICS CARRIER REE;Lo;0;L;;;;;N;;;;;
+15CE;CANADIAN SYLLABICS CARRIER RI;Lo;0;L;;;;;N;;;;;
+15CF;CANADIAN SYLLABICS CARRIER RA;Lo;0;L;;;;;N;;;;;
+15D0;CANADIAN SYLLABICS CARRIER WU;Lo;0;L;;;;;N;;;;;
+15D1;CANADIAN SYLLABICS CARRIER WO;Lo;0;L;;;;;N;;;;;
+15D2;CANADIAN SYLLABICS CARRIER WE;Lo;0;L;;;;;N;;;;;
+15D3;CANADIAN SYLLABICS CARRIER WEE;Lo;0;L;;;;;N;;;;;
+15D4;CANADIAN SYLLABICS CARRIER WI;Lo;0;L;;;;;N;;;;;
+15D5;CANADIAN SYLLABICS CARRIER WA;Lo;0;L;;;;;N;;;;;
+15D6;CANADIAN SYLLABICS CARRIER HWU;Lo;0;L;;;;;N;;;;;
+15D7;CANADIAN SYLLABICS CARRIER HWO;Lo;0;L;;;;;N;;;;;
+15D8;CANADIAN SYLLABICS CARRIER HWE;Lo;0;L;;;;;N;;;;;
+15D9;CANADIAN SYLLABICS CARRIER HWEE;Lo;0;L;;;;;N;;;;;
+15DA;CANADIAN SYLLABICS CARRIER HWI;Lo;0;L;;;;;N;;;;;
+15DB;CANADIAN SYLLABICS CARRIER HWA;Lo;0;L;;;;;N;;;;;
+15DC;CANADIAN SYLLABICS CARRIER THU;Lo;0;L;;;;;N;;;;;
+15DD;CANADIAN SYLLABICS CARRIER THO;Lo;0;L;;;;;N;;;;;
+15DE;CANADIAN SYLLABICS CARRIER THE;Lo;0;L;;;;;N;;;;;
+15DF;CANADIAN SYLLABICS CARRIER THEE;Lo;0;L;;;;;N;;;;;
+15E0;CANADIAN SYLLABICS CARRIER THI;Lo;0;L;;;;;N;;;;;
+15E1;CANADIAN SYLLABICS CARRIER THA;Lo;0;L;;;;;N;;;;;
+15E2;CANADIAN SYLLABICS CARRIER TTU;Lo;0;L;;;;;N;;;;;
+15E3;CANADIAN SYLLABICS CARRIER TTO;Lo;0;L;;;;;N;;;;;
+15E4;CANADIAN SYLLABICS CARRIER TTE;Lo;0;L;;;;;N;;;;;
+15E5;CANADIAN SYLLABICS CARRIER TTEE;Lo;0;L;;;;;N;;;;;
+15E6;CANADIAN SYLLABICS CARRIER TTI;Lo;0;L;;;;;N;;;;;
+15E7;CANADIAN SYLLABICS CARRIER TTA;Lo;0;L;;;;;N;;;;;
+15E8;CANADIAN SYLLABICS CARRIER PU;Lo;0;L;;;;;N;;;;;
+15E9;CANADIAN SYLLABICS CARRIER PO;Lo;0;L;;;;;N;;;;;
+15EA;CANADIAN SYLLABICS CARRIER PE;Lo;0;L;;;;;N;;;;;
+15EB;CANADIAN SYLLABICS CARRIER PEE;Lo;0;L;;;;;N;;;;;
+15EC;CANADIAN SYLLABICS CARRIER PI;Lo;0;L;;;;;N;;;;;
+15ED;CANADIAN SYLLABICS CARRIER PA;Lo;0;L;;;;;N;;;;;
+15EE;CANADIAN SYLLABICS CARRIER P;Lo;0;L;;;;;N;;;;;
+15EF;CANADIAN SYLLABICS CARRIER GU;Lo;0;L;;;;;N;;;;;
+15F0;CANADIAN SYLLABICS CARRIER GO;Lo;0;L;;;;;N;;;;;
+15F1;CANADIAN SYLLABICS CARRIER GE;Lo;0;L;;;;;N;;;;;
+15F2;CANADIAN SYLLABICS CARRIER GEE;Lo;0;L;;;;;N;;;;;
+15F3;CANADIAN SYLLABICS CARRIER GI;Lo;0;L;;;;;N;;;;;
+15F4;CANADIAN SYLLABICS CARRIER GA;Lo;0;L;;;;;N;;;;;
+15F5;CANADIAN SYLLABICS CARRIER KHU;Lo;0;L;;;;;N;;;;;
+15F6;CANADIAN SYLLABICS CARRIER KHO;Lo;0;L;;;;;N;;;;;
+15F7;CANADIAN SYLLABICS CARRIER KHE;Lo;0;L;;;;;N;;;;;
+15F8;CANADIAN SYLLABICS CARRIER KHEE;Lo;0;L;;;;;N;;;;;
+15F9;CANADIAN SYLLABICS CARRIER KHI;Lo;0;L;;;;;N;;;;;
+15FA;CANADIAN SYLLABICS CARRIER KHA;Lo;0;L;;;;;N;;;;;
+15FB;CANADIAN SYLLABICS CARRIER KKU;Lo;0;L;;;;;N;;;;;
+15FC;CANADIAN SYLLABICS CARRIER KKO;Lo;0;L;;;;;N;;;;;
+15FD;CANADIAN SYLLABICS CARRIER KKE;Lo;0;L;;;;;N;;;;;
+15FE;CANADIAN SYLLABICS CARRIER KKEE;Lo;0;L;;;;;N;;;;;
+15FF;CANADIAN SYLLABICS CARRIER KKI;Lo;0;L;;;;;N;;;;;
+1600;CANADIAN SYLLABICS CARRIER KKA;Lo;0;L;;;;;N;;;;;
+1601;CANADIAN SYLLABICS CARRIER KK;Lo;0;L;;;;;N;;;;;
+1602;CANADIAN SYLLABICS CARRIER NU;Lo;0;L;;;;;N;;;;;
+1603;CANADIAN SYLLABICS CARRIER NO;Lo;0;L;;;;;N;;;;;
+1604;CANADIAN SYLLABICS CARRIER NE;Lo;0;L;;;;;N;;;;;
+1605;CANADIAN SYLLABICS CARRIER NEE;Lo;0;L;;;;;N;;;;;
+1606;CANADIAN SYLLABICS CARRIER NI;Lo;0;L;;;;;N;;;;;
+1607;CANADIAN SYLLABICS CARRIER NA;Lo;0;L;;;;;N;;;;;
+1608;CANADIAN SYLLABICS CARRIER MU;Lo;0;L;;;;;N;;;;;
+1609;CANADIAN SYLLABICS CARRIER MO;Lo;0;L;;;;;N;;;;;
+160A;CANADIAN SYLLABICS CARRIER ME;Lo;0;L;;;;;N;;;;;
+160B;CANADIAN SYLLABICS CARRIER MEE;Lo;0;L;;;;;N;;;;;
+160C;CANADIAN SYLLABICS CARRIER MI;Lo;0;L;;;;;N;;;;;
+160D;CANADIAN SYLLABICS CARRIER MA;Lo;0;L;;;;;N;;;;;
+160E;CANADIAN SYLLABICS CARRIER YU;Lo;0;L;;;;;N;;;;;
+160F;CANADIAN SYLLABICS CARRIER YO;Lo;0;L;;;;;N;;;;;
+1610;CANADIAN SYLLABICS CARRIER YE;Lo;0;L;;;;;N;;;;;
+1611;CANADIAN SYLLABICS CARRIER YEE;Lo;0;L;;;;;N;;;;;
+1612;CANADIAN SYLLABICS CARRIER YI;Lo;0;L;;;;;N;;;;;
+1613;CANADIAN SYLLABICS CARRIER YA;Lo;0;L;;;;;N;;;;;
+1614;CANADIAN SYLLABICS CARRIER JU;Lo;0;L;;;;;N;;;;;
+1615;CANADIAN SYLLABICS SAYISI JU;Lo;0;L;;;;;N;;;;;
+1616;CANADIAN SYLLABICS CARRIER JO;Lo;0;L;;;;;N;;;;;
+1617;CANADIAN SYLLABICS CARRIER JE;Lo;0;L;;;;;N;;;;;
+1618;CANADIAN SYLLABICS CARRIER JEE;Lo;0;L;;;;;N;;;;;
+1619;CANADIAN SYLLABICS CARRIER JI;Lo;0;L;;;;;N;;;;;
+161A;CANADIAN SYLLABICS SAYISI JI;Lo;0;L;;;;;N;;;;;
+161B;CANADIAN SYLLABICS CARRIER JA;Lo;0;L;;;;;N;;;;;
+161C;CANADIAN SYLLABICS CARRIER JJU;Lo;0;L;;;;;N;;;;;
+161D;CANADIAN SYLLABICS CARRIER JJO;Lo;0;L;;;;;N;;;;;
+161E;CANADIAN SYLLABICS CARRIER JJE;Lo;0;L;;;;;N;;;;;
+161F;CANADIAN SYLLABICS CARRIER JJEE;Lo;0;L;;;;;N;;;;;
+1620;CANADIAN SYLLABICS CARRIER JJI;Lo;0;L;;;;;N;;;;;
+1621;CANADIAN SYLLABICS CARRIER JJA;Lo;0;L;;;;;N;;;;;
+1622;CANADIAN SYLLABICS CARRIER LU;Lo;0;L;;;;;N;;;;;
+1623;CANADIAN SYLLABICS CARRIER LO;Lo;0;L;;;;;N;;;;;
+1624;CANADIAN SYLLABICS CARRIER LE;Lo;0;L;;;;;N;;;;;
+1625;CANADIAN SYLLABICS CARRIER LEE;Lo;0;L;;;;;N;;;;;
+1626;CANADIAN SYLLABICS CARRIER LI;Lo;0;L;;;;;N;;;;;
+1627;CANADIAN SYLLABICS CARRIER LA;Lo;0;L;;;;;N;;;;;
+1628;CANADIAN SYLLABICS CARRIER DLU;Lo;0;L;;;;;N;;;;;
+1629;CANADIAN SYLLABICS CARRIER DLO;Lo;0;L;;;;;N;;;;;
+162A;CANADIAN SYLLABICS CARRIER DLE;Lo;0;L;;;;;N;;;;;
+162B;CANADIAN SYLLABICS CARRIER DLEE;Lo;0;L;;;;;N;;;;;
+162C;CANADIAN SYLLABICS CARRIER DLI;Lo;0;L;;;;;N;;;;;
+162D;CANADIAN SYLLABICS CARRIER DLA;Lo;0;L;;;;;N;;;;;
+162E;CANADIAN SYLLABICS CARRIER LHU;Lo;0;L;;;;;N;;;;;
+162F;CANADIAN SYLLABICS CARRIER LHO;Lo;0;L;;;;;N;;;;;
+1630;CANADIAN SYLLABICS CARRIER LHE;Lo;0;L;;;;;N;;;;;
+1631;CANADIAN SYLLABICS CARRIER LHEE;Lo;0;L;;;;;N;;;;;
+1632;CANADIAN SYLLABICS CARRIER LHI;Lo;0;L;;;;;N;;;;;
+1633;CANADIAN SYLLABICS CARRIER LHA;Lo;0;L;;;;;N;;;;;
+1634;CANADIAN SYLLABICS CARRIER TLHU;Lo;0;L;;;;;N;;;;;
+1635;CANADIAN SYLLABICS CARRIER TLHO;Lo;0;L;;;;;N;;;;;
+1636;CANADIAN SYLLABICS CARRIER TLHE;Lo;0;L;;;;;N;;;;;
+1637;CANADIAN SYLLABICS CARRIER TLHEE;Lo;0;L;;;;;N;;;;;
+1638;CANADIAN SYLLABICS CARRIER TLHI;Lo;0;L;;;;;N;;;;;
+1639;CANADIAN SYLLABICS CARRIER TLHA;Lo;0;L;;;;;N;;;;;
+163A;CANADIAN SYLLABICS CARRIER TLU;Lo;0;L;;;;;N;;;;;
+163B;CANADIAN SYLLABICS CARRIER TLO;Lo;0;L;;;;;N;;;;;
+163C;CANADIAN SYLLABICS CARRIER TLE;Lo;0;L;;;;;N;;;;;
+163D;CANADIAN SYLLABICS CARRIER TLEE;Lo;0;L;;;;;N;;;;;
+163E;CANADIAN SYLLABICS CARRIER TLI;Lo;0;L;;;;;N;;;;;
+163F;CANADIAN SYLLABICS CARRIER TLA;Lo;0;L;;;;;N;;;;;
+1640;CANADIAN SYLLABICS CARRIER ZU;Lo;0;L;;;;;N;;;;;
+1641;CANADIAN SYLLABICS CARRIER ZO;Lo;0;L;;;;;N;;;;;
+1642;CANADIAN SYLLABICS CARRIER ZE;Lo;0;L;;;;;N;;;;;
+1643;CANADIAN SYLLABICS CARRIER ZEE;Lo;0;L;;;;;N;;;;;
+1644;CANADIAN SYLLABICS CARRIER ZI;Lo;0;L;;;;;N;;;;;
+1645;CANADIAN SYLLABICS CARRIER ZA;Lo;0;L;;;;;N;;;;;
+1646;CANADIAN SYLLABICS CARRIER Z;Lo;0;L;;;;;N;;;;;
+1647;CANADIAN SYLLABICS CARRIER INITIAL Z;Lo;0;L;;;;;N;;;;;
+1648;CANADIAN SYLLABICS CARRIER DZU;Lo;0;L;;;;;N;;;;;
+1649;CANADIAN SYLLABICS CARRIER DZO;Lo;0;L;;;;;N;;;;;
+164A;CANADIAN SYLLABICS CARRIER DZE;Lo;0;L;;;;;N;;;;;
+164B;CANADIAN SYLLABICS CARRIER DZEE;Lo;0;L;;;;;N;;;;;
+164C;CANADIAN SYLLABICS CARRIER DZI;Lo;0;L;;;;;N;;;;;
+164D;CANADIAN SYLLABICS CARRIER DZA;Lo;0;L;;;;;N;;;;;
+164E;CANADIAN SYLLABICS CARRIER SU;Lo;0;L;;;;;N;;;;;
+164F;CANADIAN SYLLABICS CARRIER SO;Lo;0;L;;;;;N;;;;;
+1650;CANADIAN SYLLABICS CARRIER SE;Lo;0;L;;;;;N;;;;;
+1651;CANADIAN SYLLABICS CARRIER SEE;Lo;0;L;;;;;N;;;;;
+1652;CANADIAN SYLLABICS CARRIER SI;Lo;0;L;;;;;N;;;;;
+1653;CANADIAN SYLLABICS CARRIER SA;Lo;0;L;;;;;N;;;;;
+1654;CANADIAN SYLLABICS CARRIER SHU;Lo;0;L;;;;;N;;;;;
+1655;CANADIAN SYLLABICS CARRIER SHO;Lo;0;L;;;;;N;;;;;
+1656;CANADIAN SYLLABICS CARRIER SHE;Lo;0;L;;;;;N;;;;;
+1657;CANADIAN SYLLABICS CARRIER SHEE;Lo;0;L;;;;;N;;;;;
+1658;CANADIAN SYLLABICS CARRIER SHI;Lo;0;L;;;;;N;;;;;
+1659;CANADIAN SYLLABICS CARRIER SHA;Lo;0;L;;;;;N;;;;;
+165A;CANADIAN SYLLABICS CARRIER SH;Lo;0;L;;;;;N;;;;;
+165B;CANADIAN SYLLABICS CARRIER TSU;Lo;0;L;;;;;N;;;;;
+165C;CANADIAN SYLLABICS CARRIER TSO;Lo;0;L;;;;;N;;;;;
+165D;CANADIAN SYLLABICS CARRIER TSE;Lo;0;L;;;;;N;;;;;
+165E;CANADIAN SYLLABICS CARRIER TSEE;Lo;0;L;;;;;N;;;;;
+165F;CANADIAN SYLLABICS CARRIER TSI;Lo;0;L;;;;;N;;;;;
+1660;CANADIAN SYLLABICS CARRIER TSA;Lo;0;L;;;;;N;;;;;
+1661;CANADIAN SYLLABICS CARRIER CHU;Lo;0;L;;;;;N;;;;;
+1662;CANADIAN SYLLABICS CARRIER CHO;Lo;0;L;;;;;N;;;;;
+1663;CANADIAN SYLLABICS CARRIER CHE;Lo;0;L;;;;;N;;;;;
+1664;CANADIAN SYLLABICS CARRIER CHEE;Lo;0;L;;;;;N;;;;;
+1665;CANADIAN SYLLABICS CARRIER CHI;Lo;0;L;;;;;N;;;;;
+1666;CANADIAN SYLLABICS CARRIER CHA;Lo;0;L;;;;;N;;;;;
+1667;CANADIAN SYLLABICS CARRIER TTSU;Lo;0;L;;;;;N;;;;;
+1668;CANADIAN SYLLABICS CARRIER TTSO;Lo;0;L;;;;;N;;;;;
+1669;CANADIAN SYLLABICS CARRIER TTSE;Lo;0;L;;;;;N;;;;;
+166A;CANADIAN SYLLABICS CARRIER TTSEE;Lo;0;L;;;;;N;;;;;
+166B;CANADIAN SYLLABICS CARRIER TTSI;Lo;0;L;;;;;N;;;;;
+166C;CANADIAN SYLLABICS CARRIER TTSA;Lo;0;L;;;;;N;;;;;
+166D;CANADIAN SYLLABICS CHI SIGN;Po;0;L;;;;;N;;;;;
+166E;CANADIAN SYLLABICS FULL STOP;Po;0;L;;;;;N;;;;;
+166F;CANADIAN SYLLABICS QAI;Lo;0;L;;;;;N;;;;;
+1670;CANADIAN SYLLABICS NGAI;Lo;0;L;;;;;N;;;;;
+1671;CANADIAN SYLLABICS NNGI;Lo;0;L;;;;;N;;;;;
+1672;CANADIAN SYLLABICS NNGII;Lo;0;L;;;;;N;;;;;
+1673;CANADIAN SYLLABICS NNGO;Lo;0;L;;;;;N;;;;;
+1674;CANADIAN SYLLABICS NNGOO;Lo;0;L;;;;;N;;;;;
+1675;CANADIAN SYLLABICS NNGA;Lo;0;L;;;;;N;;;;;
+1676;CANADIAN SYLLABICS NNGAA;Lo;0;L;;;;;N;;;;;
+1680;OGHAM SPACE MARK;Zs;0;WS;;;;;N;;;;;
+1681;OGHAM LETTER BEITH;Lo;0;L;;;;;N;;;;;
+1682;OGHAM LETTER LUIS;Lo;0;L;;;;;N;;;;;
+1683;OGHAM LETTER FEARN;Lo;0;L;;;;;N;;;;;
+1684;OGHAM LETTER SAIL;Lo;0;L;;;;;N;;;;;
+1685;OGHAM LETTER NION;Lo;0;L;;;;;N;;;;;
+1686;OGHAM LETTER UATH;Lo;0;L;;;;;N;;;;;
+1687;OGHAM LETTER DAIR;Lo;0;L;;;;;N;;;;;
+1688;OGHAM LETTER TINNE;Lo;0;L;;;;;N;;;;;
+1689;OGHAM LETTER COLL;Lo;0;L;;;;;N;;;;;
+168A;OGHAM LETTER CEIRT;Lo;0;L;;;;;N;;;;;
+168B;OGHAM LETTER MUIN;Lo;0;L;;;;;N;;;;;
+168C;OGHAM LETTER GORT;Lo;0;L;;;;;N;;;;;
+168D;OGHAM LETTER NGEADAL;Lo;0;L;;;;;N;;;;;
+168E;OGHAM LETTER STRAIF;Lo;0;L;;;;;N;;;;;
+168F;OGHAM LETTER RUIS;Lo;0;L;;;;;N;;;;;
+1690;OGHAM LETTER AILM;Lo;0;L;;;;;N;;;;;
+1691;OGHAM LETTER ONN;Lo;0;L;;;;;N;;;;;
+1692;OGHAM LETTER UR;Lo;0;L;;;;;N;;;;;
+1693;OGHAM LETTER EADHADH;Lo;0;L;;;;;N;;;;;
+1694;OGHAM LETTER IODHADH;Lo;0;L;;;;;N;;;;;
+1695;OGHAM LETTER EABHADH;Lo;0;L;;;;;N;;;;;
+1696;OGHAM LETTER OR;Lo;0;L;;;;;N;;;;;
+1697;OGHAM LETTER UILLEANN;Lo;0;L;;;;;N;;;;;
+1698;OGHAM LETTER IFIN;Lo;0;L;;;;;N;;;;;
+1699;OGHAM LETTER EAMHANCHOLL;Lo;0;L;;;;;N;;;;;
+169A;OGHAM LETTER PEITH;Lo;0;L;;;;;N;;;;;
+169B;OGHAM FEATHER MARK;Ps;0;ON;;;;;N;;;;;
+169C;OGHAM REVERSED FEATHER MARK;Pe;0;ON;;;;;N;;;;;
+16A0;RUNIC LETTER FEHU FEOH FE F;Lo;0;L;;;;;N;;;;;
+16A1;RUNIC LETTER V;Lo;0;L;;;;;N;;;;;
+16A2;RUNIC LETTER URUZ UR U;Lo;0;L;;;;;N;;;;;
+16A3;RUNIC LETTER YR;Lo;0;L;;;;;N;;;;;
+16A4;RUNIC LETTER Y;Lo;0;L;;;;;N;;;;;
+16A5;RUNIC LETTER W;Lo;0;L;;;;;N;;;;;
+16A6;RUNIC LETTER THURISAZ THURS THORN;Lo;0;L;;;;;N;;;;;
+16A7;RUNIC LETTER ETH;Lo;0;L;;;;;N;;;;;
+16A8;RUNIC LETTER ANSUZ A;Lo;0;L;;;;;N;;;;;
+16A9;RUNIC LETTER OS O;Lo;0;L;;;;;N;;;;;
+16AA;RUNIC LETTER AC A;Lo;0;L;;;;;N;;;;;
+16AB;RUNIC LETTER AESC;Lo;0;L;;;;;N;;;;;
+16AC;RUNIC LETTER LONG-BRANCH-OSS O;Lo;0;L;;;;;N;;;;;
+16AD;RUNIC LETTER SHORT-TWIG-OSS O;Lo;0;L;;;;;N;;;;;
+16AE;RUNIC LETTER O;Lo;0;L;;;;;N;;;;;
+16AF;RUNIC LETTER OE;Lo;0;L;;;;;N;;;;;
+16B0;RUNIC LETTER ON;Lo;0;L;;;;;N;;;;;
+16B1;RUNIC LETTER RAIDO RAD REID R;Lo;0;L;;;;;N;;;;;
+16B2;RUNIC LETTER KAUNA;Lo;0;L;;;;;N;;;;;
+16B3;RUNIC LETTER CEN;Lo;0;L;;;;;N;;;;;
+16B4;RUNIC LETTER KAUN K;Lo;0;L;;;;;N;;;;;
+16B5;RUNIC LETTER G;Lo;0;L;;;;;N;;;;;
+16B6;RUNIC LETTER ENG;Lo;0;L;;;;;N;;;;;
+16B7;RUNIC LETTER GEBO GYFU G;Lo;0;L;;;;;N;;;;;
+16B8;RUNIC LETTER GAR;Lo;0;L;;;;;N;;;;;
+16B9;RUNIC LETTER WUNJO WYNN W;Lo;0;L;;;;;N;;;;;
+16BA;RUNIC LETTER HAGLAZ H;Lo;0;L;;;;;N;;;;;
+16BB;RUNIC LETTER HAEGL H;Lo;0;L;;;;;N;;;;;
+16BC;RUNIC LETTER LONG-BRANCH-HAGALL H;Lo;0;L;;;;;N;;;;;
+16BD;RUNIC LETTER SHORT-TWIG-HAGALL H;Lo;0;L;;;;;N;;;;;
+16BE;RUNIC LETTER NAUDIZ NYD NAUD N;Lo;0;L;;;;;N;;;;;
+16BF;RUNIC LETTER SHORT-TWIG-NAUD N;Lo;0;L;;;;;N;;;;;
+16C0;RUNIC LETTER DOTTED-N;Lo;0;L;;;;;N;;;;;
+16C1;RUNIC LETTER ISAZ IS ISS I;Lo;0;L;;;;;N;;;;;
+16C2;RUNIC LETTER E;Lo;0;L;;;;;N;;;;;
+16C3;RUNIC LETTER JERAN J;Lo;0;L;;;;;N;;;;;
+16C4;RUNIC LETTER GER;Lo;0;L;;;;;N;;;;;
+16C5;RUNIC LETTER LONG-BRANCH-AR AE;Lo;0;L;;;;;N;;;;;
+16C6;RUNIC LETTER SHORT-TWIG-AR A;Lo;0;L;;;;;N;;;;;
+16C7;RUNIC LETTER IWAZ EOH;Lo;0;L;;;;;N;;;;;
+16C8;RUNIC LETTER PERTHO PEORTH P;Lo;0;L;;;;;N;;;;;
+16C9;RUNIC LETTER ALGIZ EOLHX;Lo;0;L;;;;;N;;;;;
+16CA;RUNIC LETTER SOWILO S;Lo;0;L;;;;;N;;;;;
+16CB;RUNIC LETTER SIGEL LONG-BRANCH-SOL S;Lo;0;L;;;;;N;;;;;
+16CC;RUNIC LETTER SHORT-TWIG-SOL S;Lo;0;L;;;;;N;;;;;
+16CD;RUNIC LETTER C;Lo;0;L;;;;;N;;;;;
+16CE;RUNIC LETTER Z;Lo;0;L;;;;;N;;;;;
+16CF;RUNIC LETTER TIWAZ TIR TYR T;Lo;0;L;;;;;N;;;;;
+16D0;RUNIC LETTER SHORT-TWIG-TYR T;Lo;0;L;;;;;N;;;;;
+16D1;RUNIC LETTER D;Lo;0;L;;;;;N;;;;;
+16D2;RUNIC LETTER BERKANAN BEORC BJARKAN B;Lo;0;L;;;;;N;;;;;
+16D3;RUNIC LETTER SHORT-TWIG-BJARKAN B;Lo;0;L;;;;;N;;;;;
+16D4;RUNIC LETTER DOTTED-P;Lo;0;L;;;;;N;;;;;
+16D5;RUNIC LETTER OPEN-P;Lo;0;L;;;;;N;;;;;
+16D6;RUNIC LETTER EHWAZ EH E;Lo;0;L;;;;;N;;;;;
+16D7;RUNIC LETTER MANNAZ MAN M;Lo;0;L;;;;;N;;;;;
+16D8;RUNIC LETTER LONG-BRANCH-MADR M;Lo;0;L;;;;;N;;;;;
+16D9;RUNIC LETTER SHORT-TWIG-MADR M;Lo;0;L;;;;;N;;;;;
+16DA;RUNIC LETTER LAUKAZ LAGU LOGR L;Lo;0;L;;;;;N;;;;;
+16DB;RUNIC LETTER DOTTED-L;Lo;0;L;;;;;N;;;;;
+16DC;RUNIC LETTER INGWAZ;Lo;0;L;;;;;N;;;;;
+16DD;RUNIC LETTER ING;Lo;0;L;;;;;N;;;;;
+16DE;RUNIC LETTER DAGAZ DAEG D;Lo;0;L;;;;;N;;;;;
+16DF;RUNIC LETTER OTHALAN ETHEL O;Lo;0;L;;;;;N;;;;;
+16E0;RUNIC LETTER EAR;Lo;0;L;;;;;N;;;;;
+16E1;RUNIC LETTER IOR;Lo;0;L;;;;;N;;;;;
+16E2;RUNIC LETTER CWEORTH;Lo;0;L;;;;;N;;;;;
+16E3;RUNIC LETTER CALC;Lo;0;L;;;;;N;;;;;
+16E4;RUNIC LETTER CEALC;Lo;0;L;;;;;N;;;;;
+16E5;RUNIC LETTER STAN;Lo;0;L;;;;;N;;;;;
+16E6;RUNIC LETTER LONG-BRANCH-YR;Lo;0;L;;;;;N;;;;;
+16E7;RUNIC LETTER SHORT-TWIG-YR;Lo;0;L;;;;;N;;;;;
+16E8;RUNIC LETTER ICELANDIC-YR;Lo;0;L;;;;;N;;;;;
+16E9;RUNIC LETTER Q;Lo;0;L;;;;;N;;;;;
+16EA;RUNIC LETTER X;Lo;0;L;;;;;N;;;;;
+16EB;RUNIC SINGLE PUNCTUATION;Po;0;L;;;;;N;;;;;
+16EC;RUNIC MULTIPLE PUNCTUATION;Po;0;L;;;;;N;;;;;
+16ED;RUNIC CROSS PUNCTUATION;Po;0;L;;;;;N;;;;;
+16EE;RUNIC ARLAUG SYMBOL;Nl;0;L;;;;17;N;;golden number 17;;;
+16EF;RUNIC TVIMADUR SYMBOL;Nl;0;L;;;;18;N;;golden number 18;;;
+16F0;RUNIC BELGTHOR SYMBOL;Nl;0;L;;;;19;N;;golden number 19;;;
+1700;TAGALOG LETTER A;Lo;0;L;;;;;N;;;;;
+1701;TAGALOG LETTER I;Lo;0;L;;;;;N;;;;;
+1702;TAGALOG LETTER U;Lo;0;L;;;;;N;;;;;
+1703;TAGALOG LETTER KA;Lo;0;L;;;;;N;;;;;
+1704;TAGALOG LETTER GA;Lo;0;L;;;;;N;;;;;
+1705;TAGALOG LETTER NGA;Lo;0;L;;;;;N;;;;;
+1706;TAGALOG LETTER TA;Lo;0;L;;;;;N;;;;;
+1707;TAGALOG LETTER DA;Lo;0;L;;;;;N;;;;;
+1708;TAGALOG LETTER NA;Lo;0;L;;;;;N;;;;;
+1709;TAGALOG LETTER PA;Lo;0;L;;;;;N;;;;;
+170A;TAGALOG LETTER BA;Lo;0;L;;;;;N;;;;;
+170B;TAGALOG LETTER MA;Lo;0;L;;;;;N;;;;;
+170C;TAGALOG LETTER YA;Lo;0;L;;;;;N;;;;;
+170E;TAGALOG LETTER LA;Lo;0;L;;;;;N;;;;;
+170F;TAGALOG LETTER WA;Lo;0;L;;;;;N;;;;;
+1710;TAGALOG LETTER SA;Lo;0;L;;;;;N;;;;;
+1711;TAGALOG LETTER HA;Lo;0;L;;;;;N;;;;;
+1712;TAGALOG VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+1713;TAGALOG VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1714;TAGALOG SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+1720;HANUNOO LETTER A;Lo;0;L;;;;;N;;;;;
+1721;HANUNOO LETTER I;Lo;0;L;;;;;N;;;;;
+1722;HANUNOO LETTER U;Lo;0;L;;;;;N;;;;;
+1723;HANUNOO LETTER KA;Lo;0;L;;;;;N;;;;;
+1724;HANUNOO LETTER GA;Lo;0;L;;;;;N;;;;;
+1725;HANUNOO LETTER NGA;Lo;0;L;;;;;N;;;;;
+1726;HANUNOO LETTER TA;Lo;0;L;;;;;N;;;;;
+1727;HANUNOO LETTER DA;Lo;0;L;;;;;N;;;;;
+1728;HANUNOO LETTER NA;Lo;0;L;;;;;N;;;;;
+1729;HANUNOO LETTER PA;Lo;0;L;;;;;N;;;;;
+172A;HANUNOO LETTER BA;Lo;0;L;;;;;N;;;;;
+172B;HANUNOO LETTER MA;Lo;0;L;;;;;N;;;;;
+172C;HANUNOO LETTER YA;Lo;0;L;;;;;N;;;;;
+172D;HANUNOO LETTER RA;Lo;0;L;;;;;N;;;;;
+172E;HANUNOO LETTER LA;Lo;0;L;;;;;N;;;;;
+172F;HANUNOO LETTER WA;Lo;0;L;;;;;N;;;;;
+1730;HANUNOO LETTER SA;Lo;0;L;;;;;N;;;;;
+1731;HANUNOO LETTER HA;Lo;0;L;;;;;N;;;;;
+1732;HANUNOO VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+1733;HANUNOO VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1734;HANUNOO SIGN PAMUDPOD;Mn;9;NSM;;;;;N;;;;;
+1735;PHILIPPINE SINGLE PUNCTUATION;Po;0;L;;;;;N;;;;;
+1736;PHILIPPINE DOUBLE PUNCTUATION;Po;0;L;;;;;N;;;;;
+1740;BUHID LETTER A;Lo;0;L;;;;;N;;;;;
+1741;BUHID LETTER I;Lo;0;L;;;;;N;;;;;
+1742;BUHID LETTER U;Lo;0;L;;;;;N;;;;;
+1743;BUHID LETTER KA;Lo;0;L;;;;;N;;;;;
+1744;BUHID LETTER GA;Lo;0;L;;;;;N;;;;;
+1745;BUHID LETTER NGA;Lo;0;L;;;;;N;;;;;
+1746;BUHID LETTER TA;Lo;0;L;;;;;N;;;;;
+1747;BUHID LETTER DA;Lo;0;L;;;;;N;;;;;
+1748;BUHID LETTER NA;Lo;0;L;;;;;N;;;;;
+1749;BUHID LETTER PA;Lo;0;L;;;;;N;;;;;
+174A;BUHID LETTER BA;Lo;0;L;;;;;N;;;;;
+174B;BUHID LETTER MA;Lo;0;L;;;;;N;;;;;
+174C;BUHID LETTER YA;Lo;0;L;;;;;N;;;;;
+174D;BUHID LETTER RA;Lo;0;L;;;;;N;;;;;
+174E;BUHID LETTER LA;Lo;0;L;;;;;N;;;;;
+174F;BUHID LETTER WA;Lo;0;L;;;;;N;;;;;
+1750;BUHID LETTER SA;Lo;0;L;;;;;N;;;;;
+1751;BUHID LETTER HA;Lo;0;L;;;;;N;;;;;
+1752;BUHID VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+1753;BUHID VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1760;TAGBANWA LETTER A;Lo;0;L;;;;;N;;;;;
+1761;TAGBANWA LETTER I;Lo;0;L;;;;;N;;;;;
+1762;TAGBANWA LETTER U;Lo;0;L;;;;;N;;;;;
+1763;TAGBANWA LETTER KA;Lo;0;L;;;;;N;;;;;
+1764;TAGBANWA LETTER GA;Lo;0;L;;;;;N;;;;;
+1765;TAGBANWA LETTER NGA;Lo;0;L;;;;;N;;;;;
+1766;TAGBANWA LETTER TA;Lo;0;L;;;;;N;;;;;
+1767;TAGBANWA LETTER DA;Lo;0;L;;;;;N;;;;;
+1768;TAGBANWA LETTER NA;Lo;0;L;;;;;N;;;;;
+1769;TAGBANWA LETTER PA;Lo;0;L;;;;;N;;;;;
+176A;TAGBANWA LETTER BA;Lo;0;L;;;;;N;;;;;
+176B;TAGBANWA LETTER MA;Lo;0;L;;;;;N;;;;;
+176C;TAGBANWA LETTER YA;Lo;0;L;;;;;N;;;;;
+176E;TAGBANWA LETTER LA;Lo;0;L;;;;;N;;;;;
+176F;TAGBANWA LETTER WA;Lo;0;L;;;;;N;;;;;
+1770;TAGBANWA LETTER SA;Lo;0;L;;;;;N;;;;;
+1772;TAGBANWA VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+1773;TAGBANWA VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+1780;KHMER LETTER KA;Lo;0;L;;;;;N;;;;;
+1781;KHMER LETTER KHA;Lo;0;L;;;;;N;;;;;
+1782;KHMER LETTER KO;Lo;0;L;;;;;N;;;;;
+1783;KHMER LETTER KHO;Lo;0;L;;;;;N;;;;;
+1784;KHMER LETTER NGO;Lo;0;L;;;;;N;;;;;
+1785;KHMER LETTER CA;Lo;0;L;;;;;N;;;;;
+1786;KHMER LETTER CHA;Lo;0;L;;;;;N;;;;;
+1787;KHMER LETTER CO;Lo;0;L;;;;;N;;;;;
+1788;KHMER LETTER CHO;Lo;0;L;;;;;N;;;;;
+1789;KHMER LETTER NYO;Lo;0;L;;;;;N;;;;;
+178A;KHMER LETTER DA;Lo;0;L;;;;;N;;;;;
+178B;KHMER LETTER TTHA;Lo;0;L;;;;;N;;;;;
+178C;KHMER LETTER DO;Lo;0;L;;;;;N;;;;;
+178D;KHMER LETTER TTHO;Lo;0;L;;;;;N;;;;;
+178E;KHMER LETTER NNO;Lo;0;L;;;;;N;;;;;
+178F;KHMER LETTER TA;Lo;0;L;;;;;N;;;;;
+1790;KHMER LETTER THA;Lo;0;L;;;;;N;;;;;
+1791;KHMER LETTER TO;Lo;0;L;;;;;N;;;;;
+1792;KHMER LETTER THO;Lo;0;L;;;;;N;;;;;
+1793;KHMER LETTER NO;Lo;0;L;;;;;N;;;;;
+1794;KHMER LETTER BA;Lo;0;L;;;;;N;;;;;
+1795;KHMER LETTER PHA;Lo;0;L;;;;;N;;;;;
+1796;KHMER LETTER PO;Lo;0;L;;;;;N;;;;;
+1797;KHMER LETTER PHO;Lo;0;L;;;;;N;;;;;
+1798;KHMER LETTER MO;Lo;0;L;;;;;N;;;;;
+1799;KHMER LETTER YO;Lo;0;L;;;;;N;;;;;
+179A;KHMER LETTER RO;Lo;0;L;;;;;N;;;;;
+179B;KHMER LETTER LO;Lo;0;L;;;;;N;;;;;
+179C;KHMER LETTER VO;Lo;0;L;;;;;N;;;;;
+179D;KHMER LETTER SHA;Lo;0;L;;;;;N;;;;;
+179E;KHMER LETTER SSO;Lo;0;L;;;;;N;;;;;
+179F;KHMER LETTER SA;Lo;0;L;;;;;N;;;;;
+17A0;KHMER LETTER HA;Lo;0;L;;;;;N;;;;;
+17A1;KHMER LETTER LA;Lo;0;L;;;;;N;;;;;
+17A2;KHMER LETTER QA;Lo;0;L;;;;;N;;;;;
+17A3;KHMER INDEPENDENT VOWEL QAQ;Lo;0;L;;;;;N;;;;;
+17A4;KHMER INDEPENDENT VOWEL QAA;Lo;0;L;;;;;N;;;;;
+17A5;KHMER INDEPENDENT VOWEL QI;Lo;0;L;;;;;N;;;;;
+17A6;KHMER INDEPENDENT VOWEL QII;Lo;0;L;;;;;N;;;;;
+17A7;KHMER INDEPENDENT VOWEL QU;Lo;0;L;;;;;N;;;;;
+17A8;KHMER INDEPENDENT VOWEL QUK;Lo;0;L;;;;;N;;;;;
+17A9;KHMER INDEPENDENT VOWEL QUU;Lo;0;L;;;;;N;;;;;
+17AA;KHMER INDEPENDENT VOWEL QUUV;Lo;0;L;;;;;N;;;;;
+17AB;KHMER INDEPENDENT VOWEL RY;Lo;0;L;;;;;N;;;;;
+17AC;KHMER INDEPENDENT VOWEL RYY;Lo;0;L;;;;;N;;;;;
+17AD;KHMER INDEPENDENT VOWEL LY;Lo;0;L;;;;;N;;;;;
+17AE;KHMER INDEPENDENT VOWEL LYY;Lo;0;L;;;;;N;;;;;
+17AF;KHMER INDEPENDENT VOWEL QE;Lo;0;L;;;;;N;;;;;
+17B0;KHMER INDEPENDENT VOWEL QAI;Lo;0;L;;;;;N;;;;;
+17B1;KHMER INDEPENDENT VOWEL QOO TYPE ONE;Lo;0;L;;;;;N;;;;;
+17B2;KHMER INDEPENDENT VOWEL QOO TYPE TWO;Lo;0;L;;;;;N;;;;;
+17B3;KHMER INDEPENDENT VOWEL QAU;Lo;0;L;;;;;N;;;;;
+17B4;KHMER VOWEL INHERENT AQ;Mc;0;L;;;;;N;;;;;
+17B5;KHMER VOWEL INHERENT AA;Mc;0;L;;;;;N;;;;;
+17B6;KHMER VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+17B7;KHMER VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+17B8;KHMER VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+17B9;KHMER VOWEL SIGN Y;Mn;0;NSM;;;;;N;;;;;
+17BA;KHMER VOWEL SIGN YY;Mn;0;NSM;;;;;N;;;;;
+17BB;KHMER VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+17BC;KHMER VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+17BD;KHMER VOWEL SIGN UA;Mn;0;NSM;;;;;N;;;;;
+17BE;KHMER VOWEL SIGN OE;Mc;0;L;;;;;N;;;;;
+17BF;KHMER VOWEL SIGN YA;Mc;0;L;;;;;N;;;;;
+17C0;KHMER VOWEL SIGN IE;Mc;0;L;;;;;N;;;;;
+17C1;KHMER VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+17C2;KHMER VOWEL SIGN AE;Mc;0;L;;;;;N;;;;;
+17C3;KHMER VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+17C4;KHMER VOWEL SIGN OO;Mc;0;L;;;;;N;;;;;
+17C5;KHMER VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+17C6;KHMER SIGN NIKAHIT;Mn;0;NSM;;;;;N;;;;;
+17C7;KHMER SIGN REAHMUK;Mc;0;L;;;;;N;;;;;
+17C8;KHMER SIGN YUUKALEAPINTU;Mc;0;L;;;;;N;;;;;
+17C9;KHMER SIGN MUUSIKATOAN;Mn;0;NSM;;;;;N;;;;;
+17CA;KHMER SIGN TRIISAP;Mn;0;NSM;;;;;N;;;;;
+17CB;KHMER SIGN BANTOC;Mn;0;NSM;;;;;N;;;;;
+17CC;KHMER SIGN ROBAT;Mn;0;NSM;;;;;N;;;;;
+17CD;KHMER SIGN TOANDAKHIAT;Mn;0;NSM;;;;;N;;;;;
+17CE;KHMER SIGN KAKABAT;Mn;0;NSM;;;;;N;;;;;
+17CF;KHMER SIGN AHSDA;Mn;0;NSM;;;;;N;;;;;
+17D0;KHMER SIGN SAMYOK SANNYA;Mn;0;NSM;;;;;N;;;;;
+17D1;KHMER SIGN VIRIAM;Mn;0;NSM;;;;;N;;;;;
+17D2;KHMER SIGN COENG;Mn;9;NSM;;;;;N;;;;;
+17D3;KHMER SIGN BATHAMASAT;Mn;0;NSM;;;;;N;;;;;
+17D4;KHMER SIGN KHAN;Po;0;L;;;;;N;;;;;
+17D5;KHMER SIGN BARIYOOSAN;Po;0;L;;;;;N;;;;;
+17D6;KHMER SIGN CAMNUC PII KUUH;Po;0;L;;;;;N;;;;;
+17D7;KHMER SIGN LEK TOO;Lm;0;L;;;;;N;;;;;
+17D8;KHMER SIGN BEYYAL;Po;0;L;;;;;N;;;;;
+17D9;KHMER SIGN PHNAEK MUAN;Po;0;L;;;;;N;;;;;
+17DA;KHMER SIGN KOOMUUT;Po;0;L;;;;;N;;;;;
+17DB;KHMER CURRENCY SYMBOL RIEL;Sc;0;ET;;;;;N;;;;;
+17DC;KHMER SIGN AVAKRAHASANYA;Lo;0;L;;;;;N;;;;;
+17E0;KHMER DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+17E1;KHMER DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+17E2;KHMER DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+17E3;KHMER DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+17E4;KHMER DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+17E5;KHMER DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+17E6;KHMER DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+17E7;KHMER DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+17E8;KHMER DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+17E9;KHMER DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1800;MONGOLIAN BIRGA;Po;0;ON;;;;;N;;;;;
+1801;MONGOLIAN ELLIPSIS;Po;0;ON;;;;;N;;;;;
+1802;MONGOLIAN COMMA;Po;0;ON;;;;;N;;;;;
+1803;MONGOLIAN FULL STOP;Po;0;ON;;;;;N;;;;;
+1804;MONGOLIAN COLON;Po;0;ON;;;;;N;;;;;
+1805;MONGOLIAN FOUR DOTS;Po;0;ON;;;;;N;;;;;
+1806;MONGOLIAN TODO SOFT HYPHEN;Pd;0;ON;;;;;N;;;;;
+1807;MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER;Po;0;ON;;;;;N;;;;;
+1808;MONGOLIAN MANCHU COMMA;Po;0;ON;;;;;N;;;;;
+1809;MONGOLIAN MANCHU FULL STOP;Po;0;ON;;;;;N;;;;;
+180A;MONGOLIAN NIRUGU;Po;0;ON;;;;;N;;;;;
+180B;MONGOLIAN FREE VARIATION SELECTOR ONE;Mn;0;NSM;;;;;N;;;;;
+180C;MONGOLIAN FREE VARIATION SELECTOR TWO;Mn;0;NSM;;;;;N;;;;;
+180D;MONGOLIAN FREE VARIATION SELECTOR THREE;Mn;0;NSM;;;;;N;;;;;
+180E;MONGOLIAN VOWEL SEPARATOR;Cf;0;BN;;;;;N;;;;;
+1810;MONGOLIAN DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1811;MONGOLIAN DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1812;MONGOLIAN DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1813;MONGOLIAN DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1814;MONGOLIAN DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1815;MONGOLIAN DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1816;MONGOLIAN DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1817;MONGOLIAN DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1818;MONGOLIAN DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1819;MONGOLIAN DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1820;MONGOLIAN LETTER A;Lo;0;L;;;;;N;;;;;
+1821;MONGOLIAN LETTER E;Lo;0;L;;;;;N;;;;;
+1822;MONGOLIAN LETTER I;Lo;0;L;;;;;N;;;;;
+1823;MONGOLIAN LETTER O;Lo;0;L;;;;;N;;;;;
+1824;MONGOLIAN LETTER U;Lo;0;L;;;;;N;;;;;
+1825;MONGOLIAN LETTER OE;Lo;0;L;;;;;N;;;;;
+1826;MONGOLIAN LETTER UE;Lo;0;L;;;;;N;;;;;
+1827;MONGOLIAN LETTER EE;Lo;0;L;;;;;N;;;;;
+1828;MONGOLIAN LETTER NA;Lo;0;L;;;;;N;;;;;
+1829;MONGOLIAN LETTER ANG;Lo;0;L;;;;;N;;;;;
+182A;MONGOLIAN LETTER BA;Lo;0;L;;;;;N;;;;;
+182B;MONGOLIAN LETTER PA;Lo;0;L;;;;;N;;;;;
+182C;MONGOLIAN LETTER QA;Lo;0;L;;;;;N;;;;;
+182D;MONGOLIAN LETTER GA;Lo;0;L;;;;;N;;;;;
+182E;MONGOLIAN LETTER MA;Lo;0;L;;;;;N;;;;;
+182F;MONGOLIAN LETTER LA;Lo;0;L;;;;;N;;;;;
+1830;MONGOLIAN LETTER SA;Lo;0;L;;;;;N;;;;;
+1831;MONGOLIAN LETTER SHA;Lo;0;L;;;;;N;;;;;
+1832;MONGOLIAN LETTER TA;Lo;0;L;;;;;N;;;;;
+1833;MONGOLIAN LETTER DA;Lo;0;L;;;;;N;;;;;
+1834;MONGOLIAN LETTER CHA;Lo;0;L;;;;;N;;;;;
+1835;MONGOLIAN LETTER JA;Lo;0;L;;;;;N;;;;;
+1836;MONGOLIAN LETTER YA;Lo;0;L;;;;;N;;;;;
+1837;MONGOLIAN LETTER RA;Lo;0;L;;;;;N;;;;;
+1838;MONGOLIAN LETTER WA;Lo;0;L;;;;;N;;;;;
+1839;MONGOLIAN LETTER FA;Lo;0;L;;;;;N;;;;;
+183A;MONGOLIAN LETTER KA;Lo;0;L;;;;;N;;;;;
+183B;MONGOLIAN LETTER KHA;Lo;0;L;;;;;N;;;;;
+183C;MONGOLIAN LETTER TSA;Lo;0;L;;;;;N;;;;;
+183D;MONGOLIAN LETTER ZA;Lo;0;L;;;;;N;;;;;
+183E;MONGOLIAN LETTER HAA;Lo;0;L;;;;;N;;;;;
+183F;MONGOLIAN LETTER ZRA;Lo;0;L;;;;;N;;;;;
+1840;MONGOLIAN LETTER LHA;Lo;0;L;;;;;N;;;;;
+1841;MONGOLIAN LETTER ZHI;Lo;0;L;;;;;N;;;;;
+1842;MONGOLIAN LETTER CHI;Lo;0;L;;;;;N;;;;;
+1843;MONGOLIAN LETTER TODO LONG VOWEL SIGN;Lm;0;L;;;;;N;;;;;
+1844;MONGOLIAN LETTER TODO E;Lo;0;L;;;;;N;;;;;
+1845;MONGOLIAN LETTER TODO I;Lo;0;L;;;;;N;;;;;
+1846;MONGOLIAN LETTER TODO O;Lo;0;L;;;;;N;;;;;
+1847;MONGOLIAN LETTER TODO U;Lo;0;L;;;;;N;;;;;
+1848;MONGOLIAN LETTER TODO OE;Lo;0;L;;;;;N;;;;;
+1849;MONGOLIAN LETTER TODO UE;Lo;0;L;;;;;N;;;;;
+184A;MONGOLIAN LETTER TODO ANG;Lo;0;L;;;;;N;;;;;
+184B;MONGOLIAN LETTER TODO BA;Lo;0;L;;;;;N;;;;;
+184C;MONGOLIAN LETTER TODO PA;Lo;0;L;;;;;N;;;;;
+184D;MONGOLIAN LETTER TODO QA;Lo;0;L;;;;;N;;;;;
+184E;MONGOLIAN LETTER TODO GA;Lo;0;L;;;;;N;;;;;
+184F;MONGOLIAN LETTER TODO MA;Lo;0;L;;;;;N;;;;;
+1850;MONGOLIAN LETTER TODO TA;Lo;0;L;;;;;N;;;;;
+1851;MONGOLIAN LETTER TODO DA;Lo;0;L;;;;;N;;;;;
+1852;MONGOLIAN LETTER TODO CHA;Lo;0;L;;;;;N;;;;;
+1853;MONGOLIAN LETTER TODO JA;Lo;0;L;;;;;N;;;;;
+1854;MONGOLIAN LETTER TODO TSA;Lo;0;L;;;;;N;;;;;
+1855;MONGOLIAN LETTER TODO YA;Lo;0;L;;;;;N;;;;;
+1856;MONGOLIAN LETTER TODO WA;Lo;0;L;;;;;N;;;;;
+1857;MONGOLIAN LETTER TODO KA;Lo;0;L;;;;;N;;;;;
+1858;MONGOLIAN LETTER TODO GAA;Lo;0;L;;;;;N;;;;;
+1859;MONGOLIAN LETTER TODO HAA;Lo;0;L;;;;;N;;;;;
+185A;MONGOLIAN LETTER TODO JIA;Lo;0;L;;;;;N;;;;;
+185B;MONGOLIAN LETTER TODO NIA;Lo;0;L;;;;;N;;;;;
+185C;MONGOLIAN LETTER TODO DZA;Lo;0;L;;;;;N;;;;;
+185D;MONGOLIAN LETTER SIBE E;Lo;0;L;;;;;N;;;;;
+185E;MONGOLIAN LETTER SIBE I;Lo;0;L;;;;;N;;;;;
+185F;MONGOLIAN LETTER SIBE IY;Lo;0;L;;;;;N;;;;;
+1860;MONGOLIAN LETTER SIBE UE;Lo;0;L;;;;;N;;;;;
+1861;MONGOLIAN LETTER SIBE U;Lo;0;L;;;;;N;;;;;
+1862;MONGOLIAN LETTER SIBE ANG;Lo;0;L;;;;;N;;;;;
+1863;MONGOLIAN LETTER SIBE KA;Lo;0;L;;;;;N;;;;;
+1864;MONGOLIAN LETTER SIBE GA;Lo;0;L;;;;;N;;;;;
+1865;MONGOLIAN LETTER SIBE HA;Lo;0;L;;;;;N;;;;;
+1866;MONGOLIAN LETTER SIBE PA;Lo;0;L;;;;;N;;;;;
+1867;MONGOLIAN LETTER SIBE SHA;Lo;0;L;;;;;N;;;;;
+1868;MONGOLIAN LETTER SIBE TA;Lo;0;L;;;;;N;;;;;
+1869;MONGOLIAN LETTER SIBE DA;Lo;0;L;;;;;N;;;;;
+186A;MONGOLIAN LETTER SIBE JA;Lo;0;L;;;;;N;;;;;
+186B;MONGOLIAN LETTER SIBE FA;Lo;0;L;;;;;N;;;;;
+186C;MONGOLIAN LETTER SIBE GAA;Lo;0;L;;;;;N;;;;;
+186D;MONGOLIAN LETTER SIBE HAA;Lo;0;L;;;;;N;;;;;
+186E;MONGOLIAN LETTER SIBE TSA;Lo;0;L;;;;;N;;;;;
+186F;MONGOLIAN LETTER SIBE ZA;Lo;0;L;;;;;N;;;;;
+1870;MONGOLIAN LETTER SIBE RAA;Lo;0;L;;;;;N;;;;;
+1871;MONGOLIAN LETTER SIBE CHA;Lo;0;L;;;;;N;;;;;
+1872;MONGOLIAN LETTER SIBE ZHA;Lo;0;L;;;;;N;;;;;
+1873;MONGOLIAN LETTER MANCHU I;Lo;0;L;;;;;N;;;;;
+1874;MONGOLIAN LETTER MANCHU KA;Lo;0;L;;;;;N;;;;;
+1875;MONGOLIAN LETTER MANCHU RA;Lo;0;L;;;;;N;;;;;
+1876;MONGOLIAN LETTER MANCHU FA;Lo;0;L;;;;;N;;;;;
+1877;MONGOLIAN LETTER MANCHU ZHA;Lo;0;L;;;;;N;;;;;
+1880;MONGOLIAN LETTER ALI GALI ANUSVARA ONE;Lo;0;L;;;;;N;;;;;
+1881;MONGOLIAN LETTER ALI GALI VISARGA ONE;Lo;0;L;;;;;N;;;;;
+1882;MONGOLIAN LETTER ALI GALI DAMARU;Lo;0;L;;;;;N;;;;;
+1883;MONGOLIAN LETTER ALI GALI UBADAMA;Lo;0;L;;;;;N;;;;;
+1884;MONGOLIAN LETTER ALI GALI INVERTED UBADAMA;Lo;0;L;;;;;N;;;;;
+1885;MONGOLIAN LETTER ALI GALI BALUDA;Lo;0;L;;;;;N;;;;;
+1886;MONGOLIAN LETTER ALI GALI THREE BALUDA;Lo;0;L;;;;;N;;;;;
+1887;MONGOLIAN LETTER ALI GALI A;Lo;0;L;;;;;N;;;;;
+1888;MONGOLIAN LETTER ALI GALI I;Lo;0;L;;;;;N;;;;;
+1889;MONGOLIAN LETTER ALI GALI KA;Lo;0;L;;;;;N;;;;;
+188A;MONGOLIAN LETTER ALI GALI NGA;Lo;0;L;;;;;N;;;;;
+188B;MONGOLIAN LETTER ALI GALI CA;Lo;0;L;;;;;N;;;;;
+188C;MONGOLIAN LETTER ALI GALI TTA;Lo;0;L;;;;;N;;;;;
+188D;MONGOLIAN LETTER ALI GALI TTHA;Lo;0;L;;;;;N;;;;;
+188E;MONGOLIAN LETTER ALI GALI DDA;Lo;0;L;;;;;N;;;;;
+188F;MONGOLIAN LETTER ALI GALI NNA;Lo;0;L;;;;;N;;;;;
+1890;MONGOLIAN LETTER ALI GALI TA;Lo;0;L;;;;;N;;;;;
+1891;MONGOLIAN LETTER ALI GALI DA;Lo;0;L;;;;;N;;;;;
+1892;MONGOLIAN LETTER ALI GALI PA;Lo;0;L;;;;;N;;;;;
+1893;MONGOLIAN LETTER ALI GALI PHA;Lo;0;L;;;;;N;;;;;
+1894;MONGOLIAN LETTER ALI GALI SSA;Lo;0;L;;;;;N;;;;;
+1895;MONGOLIAN LETTER ALI GALI ZHA;Lo;0;L;;;;;N;;;;;
+1896;MONGOLIAN LETTER ALI GALI ZA;Lo;0;L;;;;;N;;;;;
+1897;MONGOLIAN LETTER ALI GALI AH;Lo;0;L;;;;;N;;;;;
+1898;MONGOLIAN LETTER TODO ALI GALI TA;Lo;0;L;;;;;N;;;;;
+1899;MONGOLIAN LETTER TODO ALI GALI ZHA;Lo;0;L;;;;;N;;;;;
+189A;MONGOLIAN LETTER MANCHU ALI GALI GHA;Lo;0;L;;;;;N;;;;;
+189B;MONGOLIAN LETTER MANCHU ALI GALI NGA;Lo;0;L;;;;;N;;;;;
+189C;MONGOLIAN LETTER MANCHU ALI GALI CA;Lo;0;L;;;;;N;;;;;
+189D;MONGOLIAN LETTER MANCHU ALI GALI JHA;Lo;0;L;;;;;N;;;;;
+189E;MONGOLIAN LETTER MANCHU ALI GALI TTA;Lo;0;L;;;;;N;;;;;
+189F;MONGOLIAN LETTER MANCHU ALI GALI DDHA;Lo;0;L;;;;;N;;;;;
+18A0;MONGOLIAN LETTER MANCHU ALI GALI TA;Lo;0;L;;;;;N;;;;;
+18A1;MONGOLIAN LETTER MANCHU ALI GALI DHA;Lo;0;L;;;;;N;;;;;
+18A2;MONGOLIAN LETTER MANCHU ALI GALI SSA;Lo;0;L;;;;;N;;;;;
+18A3;MONGOLIAN LETTER MANCHU ALI GALI CYA;Lo;0;L;;;;;N;;;;;
+18A4;MONGOLIAN LETTER MANCHU ALI GALI ZHA;Lo;0;L;;;;;N;;;;;
+18A5;MONGOLIAN LETTER MANCHU ALI GALI ZA;Lo;0;L;;;;;N;;;;;
+18A6;MONGOLIAN LETTER ALI GALI HALF U;Lo;0;L;;;;;N;;;;;
+18A7;MONGOLIAN LETTER ALI GALI HALF YA;Lo;0;L;;;;;N;;;;;
+18A8;MONGOLIAN LETTER MANCHU ALI GALI BHA;Lo;0;L;;;;;N;;;;;
+18A9;MONGOLIAN LETTER ALI GALI DAGALGA;Mn;228;NSM;;;;;N;;;;;
+1E00;LATIN CAPITAL LETTER A WITH RING BELOW;Lu;0;L;0041 0325;;;;N;;;;1E01;
+1E01;LATIN SMALL LETTER A WITH RING BELOW;Ll;0;L;0061 0325;;;;N;;;1E00;;1E00
+1E02;LATIN CAPITAL LETTER B WITH DOT ABOVE;Lu;0;L;0042 0307;;;;N;;;;1E03;
+1E03;LATIN SMALL LETTER B WITH DOT ABOVE;Ll;0;L;0062 0307;;;;N;;;1E02;;1E02
+1E04;LATIN CAPITAL LETTER B WITH DOT BELOW;Lu;0;L;0042 0323;;;;N;;;;1E05;
+1E05;LATIN SMALL LETTER B WITH DOT BELOW;Ll;0;L;0062 0323;;;;N;;;1E04;;1E04
+1E06;LATIN CAPITAL LETTER B WITH LINE BELOW;Lu;0;L;0042 0331;;;;N;;;;1E07;
+1E07;LATIN SMALL LETTER B WITH LINE BELOW;Ll;0;L;0062 0331;;;;N;;;1E06;;1E06
+1E08;LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE;Lu;0;L;00C7 0301;;;;N;;;;1E09;
+1E09;LATIN SMALL LETTER C WITH CEDILLA AND ACUTE;Ll;0;L;00E7 0301;;;;N;;;1E08;;1E08
+1E0A;LATIN CAPITAL LETTER D WITH DOT ABOVE;Lu;0;L;0044 0307;;;;N;;;;1E0B;
+1E0B;LATIN SMALL LETTER D WITH DOT ABOVE;Ll;0;L;0064 0307;;;;N;;;1E0A;;1E0A
+1E0C;LATIN CAPITAL LETTER D WITH DOT BELOW;Lu;0;L;0044 0323;;;;N;;;;1E0D;
+1E0D;LATIN SMALL LETTER D WITH DOT BELOW;Ll;0;L;0064 0323;;;;N;;;1E0C;;1E0C
+1E0E;LATIN CAPITAL LETTER D WITH LINE BELOW;Lu;0;L;0044 0331;;;;N;;;;1E0F;
+1E0F;LATIN SMALL LETTER D WITH LINE BELOW;Ll;0;L;0064 0331;;;;N;;;1E0E;;1E0E
+1E10;LATIN CAPITAL LETTER D WITH CEDILLA;Lu;0;L;0044 0327;;;;N;;;;1E11;
+1E11;LATIN SMALL LETTER D WITH CEDILLA;Ll;0;L;0064 0327;;;;N;;;1E10;;1E10
+1E12;LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW;Lu;0;L;0044 032D;;;;N;;;;1E13;
+1E13;LATIN SMALL LETTER D WITH CIRCUMFLEX BELOW;Ll;0;L;0064 032D;;;;N;;;1E12;;1E12
+1E14;LATIN CAPITAL LETTER E WITH MACRON AND GRAVE;Lu;0;L;0112 0300;;;;N;;;;1E15;
+1E15;LATIN SMALL LETTER E WITH MACRON AND GRAVE;Ll;0;L;0113 0300;;;;N;;;1E14;;1E14
+1E16;LATIN CAPITAL LETTER E WITH MACRON AND ACUTE;Lu;0;L;0112 0301;;;;N;;;;1E17;
+1E17;LATIN SMALL LETTER E WITH MACRON AND ACUTE;Ll;0;L;0113 0301;;;;N;;;1E16;;1E16
+1E18;LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW;Lu;0;L;0045 032D;;;;N;;;;1E19;
+1E19;LATIN SMALL LETTER E WITH CIRCUMFLEX BELOW;Ll;0;L;0065 032D;;;;N;;;1E18;;1E18
+1E1A;LATIN CAPITAL LETTER E WITH TILDE BELOW;Lu;0;L;0045 0330;;;;N;;;;1E1B;
+1E1B;LATIN SMALL LETTER E WITH TILDE BELOW;Ll;0;L;0065 0330;;;;N;;;1E1A;;1E1A
+1E1C;LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE;Lu;0;L;0228 0306;;;;N;;;;1E1D;
+1E1D;LATIN SMALL LETTER E WITH CEDILLA AND BREVE;Ll;0;L;0229 0306;;;;N;;;1E1C;;1E1C
+1E1E;LATIN CAPITAL LETTER F WITH DOT ABOVE;Lu;0;L;0046 0307;;;;N;;;;1E1F;
+1E1F;LATIN SMALL LETTER F WITH DOT ABOVE;Ll;0;L;0066 0307;;;;N;;;1E1E;;1E1E
+1E20;LATIN CAPITAL LETTER G WITH MACRON;Lu;0;L;0047 0304;;;;N;;;;1E21;
+1E21;LATIN SMALL LETTER G WITH MACRON;Ll;0;L;0067 0304;;;;N;;;1E20;;1E20
+1E22;LATIN CAPITAL LETTER H WITH DOT ABOVE;Lu;0;L;0048 0307;;;;N;;;;1E23;
+1E23;LATIN SMALL LETTER H WITH DOT ABOVE;Ll;0;L;0068 0307;;;;N;;;1E22;;1E22
+1E24;LATIN CAPITAL LETTER H WITH DOT BELOW;Lu;0;L;0048 0323;;;;N;;;;1E25;
+1E25;LATIN SMALL LETTER H WITH DOT BELOW;Ll;0;L;0068 0323;;;;N;;;1E24;;1E24
+1E26;LATIN CAPITAL LETTER H WITH DIAERESIS;Lu;0;L;0048 0308;;;;N;;;;1E27;
+1E27;LATIN SMALL LETTER H WITH DIAERESIS;Ll;0;L;0068 0308;;;;N;;;1E26;;1E26
+1E28;LATIN CAPITAL LETTER H WITH CEDILLA;Lu;0;L;0048 0327;;;;N;;;;1E29;
+1E29;LATIN SMALL LETTER H WITH CEDILLA;Ll;0;L;0068 0327;;;;N;;;1E28;;1E28
+1E2A;LATIN CAPITAL LETTER H WITH BREVE BELOW;Lu;0;L;0048 032E;;;;N;;;;1E2B;
+1E2B;LATIN SMALL LETTER H WITH BREVE BELOW;Ll;0;L;0068 032E;;;;N;;;1E2A;;1E2A
+1E2C;LATIN CAPITAL LETTER I WITH TILDE BELOW;Lu;0;L;0049 0330;;;;N;;;;1E2D;
+1E2D;LATIN SMALL LETTER I WITH TILDE BELOW;Ll;0;L;0069 0330;;;;N;;;1E2C;;1E2C
+1E2E;LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE;Lu;0;L;00CF 0301;;;;N;;;;1E2F;
+1E2F;LATIN SMALL LETTER I WITH DIAERESIS AND ACUTE;Ll;0;L;00EF 0301;;;;N;;;1E2E;;1E2E
+1E30;LATIN CAPITAL LETTER K WITH ACUTE;Lu;0;L;004B 0301;;;;N;;;;1E31;
+1E31;LATIN SMALL LETTER K WITH ACUTE;Ll;0;L;006B 0301;;;;N;;;1E30;;1E30
+1E32;LATIN CAPITAL LETTER K WITH DOT BELOW;Lu;0;L;004B 0323;;;;N;;;;1E33;
+1E33;LATIN SMALL LETTER K WITH DOT BELOW;Ll;0;L;006B 0323;;;;N;;;1E32;;1E32
+1E34;LATIN CAPITAL LETTER K WITH LINE BELOW;Lu;0;L;004B 0331;;;;N;;;;1E35;
+1E35;LATIN SMALL LETTER K WITH LINE BELOW;Ll;0;L;006B 0331;;;;N;;;1E34;;1E34
+1E36;LATIN CAPITAL LETTER L WITH DOT BELOW;Lu;0;L;004C 0323;;;;N;;;;1E37;
+1E37;LATIN SMALL LETTER L WITH DOT BELOW;Ll;0;L;006C 0323;;;;N;;;1E36;;1E36
+1E38;LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON;Lu;0;L;1E36 0304;;;;N;;;;1E39;
+1E39;LATIN SMALL LETTER L WITH DOT BELOW AND MACRON;Ll;0;L;1E37 0304;;;;N;;;1E38;;1E38
+1E3A;LATIN CAPITAL LETTER L WITH LINE BELOW;Lu;0;L;004C 0331;;;;N;;;;1E3B;
+1E3B;LATIN SMALL LETTER L WITH LINE BELOW;Ll;0;L;006C 0331;;;;N;;;1E3A;;1E3A
+1E3C;LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW;Lu;0;L;004C 032D;;;;N;;;;1E3D;
+1E3D;LATIN SMALL LETTER L WITH CIRCUMFLEX BELOW;Ll;0;L;006C 032D;;;;N;;;1E3C;;1E3C
+1E3E;LATIN CAPITAL LETTER M WITH ACUTE;Lu;0;L;004D 0301;;;;N;;;;1E3F;
+1E3F;LATIN SMALL LETTER M WITH ACUTE;Ll;0;L;006D 0301;;;;N;;;1E3E;;1E3E
+1E40;LATIN CAPITAL LETTER M WITH DOT ABOVE;Lu;0;L;004D 0307;;;;N;;;;1E41;
+1E41;LATIN SMALL LETTER M WITH DOT ABOVE;Ll;0;L;006D 0307;;;;N;;;1E40;;1E40
+1E42;LATIN CAPITAL LETTER M WITH DOT BELOW;Lu;0;L;004D 0323;;;;N;;;;1E43;
+1E43;LATIN SMALL LETTER M WITH DOT BELOW;Ll;0;L;006D 0323;;;;N;;;1E42;;1E42
+1E44;LATIN CAPITAL LETTER N WITH DOT ABOVE;Lu;0;L;004E 0307;;;;N;;;;1E45;
+1E45;LATIN SMALL LETTER N WITH DOT ABOVE;Ll;0;L;006E 0307;;;;N;;;1E44;;1E44
+1E46;LATIN CAPITAL LETTER N WITH DOT BELOW;Lu;0;L;004E 0323;;;;N;;;;1E47;
+1E47;LATIN SMALL LETTER N WITH DOT BELOW;Ll;0;L;006E 0323;;;;N;;;1E46;;1E46
+1E48;LATIN CAPITAL LETTER N WITH LINE BELOW;Lu;0;L;004E 0331;;;;N;;;;1E49;
+1E49;LATIN SMALL LETTER N WITH LINE BELOW;Ll;0;L;006E 0331;;;;N;;;1E48;;1E48
+1E4A;LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW;Lu;0;L;004E 032D;;;;N;;;;1E4B;
+1E4B;LATIN SMALL LETTER N WITH CIRCUMFLEX BELOW;Ll;0;L;006E 032D;;;;N;;;1E4A;;1E4A
+1E4C;LATIN CAPITAL LETTER O WITH TILDE AND ACUTE;Lu;0;L;00D5 0301;;;;N;;;;1E4D;
+1E4D;LATIN SMALL LETTER O WITH TILDE AND ACUTE;Ll;0;L;00F5 0301;;;;N;;;1E4C;;1E4C
+1E4E;LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS;Lu;0;L;00D5 0308;;;;N;;;;1E4F;
+1E4F;LATIN SMALL LETTER O WITH TILDE AND DIAERESIS;Ll;0;L;00F5 0308;;;;N;;;1E4E;;1E4E
+1E50;LATIN CAPITAL LETTER O WITH MACRON AND GRAVE;Lu;0;L;014C 0300;;;;N;;;;1E51;
+1E51;LATIN SMALL LETTER O WITH MACRON AND GRAVE;Ll;0;L;014D 0300;;;;N;;;1E50;;1E50
+1E52;LATIN CAPITAL LETTER O WITH MACRON AND ACUTE;Lu;0;L;014C 0301;;;;N;;;;1E53;
+1E53;LATIN SMALL LETTER O WITH MACRON AND ACUTE;Ll;0;L;014D 0301;;;;N;;;1E52;;1E52
+1E54;LATIN CAPITAL LETTER P WITH ACUTE;Lu;0;L;0050 0301;;;;N;;;;1E55;
+1E55;LATIN SMALL LETTER P WITH ACUTE;Ll;0;L;0070 0301;;;;N;;;1E54;;1E54
+1E56;LATIN CAPITAL LETTER P WITH DOT ABOVE;Lu;0;L;0050 0307;;;;N;;;;1E57;
+1E57;LATIN SMALL LETTER P WITH DOT ABOVE;Ll;0;L;0070 0307;;;;N;;;1E56;;1E56
+1E58;LATIN CAPITAL LETTER R WITH DOT ABOVE;Lu;0;L;0052 0307;;;;N;;;;1E59;
+1E59;LATIN SMALL LETTER R WITH DOT ABOVE;Ll;0;L;0072 0307;;;;N;;;1E58;;1E58
+1E5A;LATIN CAPITAL LETTER R WITH DOT BELOW;Lu;0;L;0052 0323;;;;N;;;;1E5B;
+1E5B;LATIN SMALL LETTER R WITH DOT BELOW;Ll;0;L;0072 0323;;;;N;;;1E5A;;1E5A
+1E5C;LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON;Lu;0;L;1E5A 0304;;;;N;;;;1E5D;
+1E5D;LATIN SMALL LETTER R WITH DOT BELOW AND MACRON;Ll;0;L;1E5B 0304;;;;N;;;1E5C;;1E5C
+1E5E;LATIN CAPITAL LETTER R WITH LINE BELOW;Lu;0;L;0052 0331;;;;N;;;;1E5F;
+1E5F;LATIN SMALL LETTER R WITH LINE BELOW;Ll;0;L;0072 0331;;;;N;;;1E5E;;1E5E
+1E60;LATIN CAPITAL LETTER S WITH DOT ABOVE;Lu;0;L;0053 0307;;;;N;;;;1E61;
+1E61;LATIN SMALL LETTER S WITH DOT ABOVE;Ll;0;L;0073 0307;;;;N;;;1E60;;1E60
+1E62;LATIN CAPITAL LETTER S WITH DOT BELOW;Lu;0;L;0053 0323;;;;N;;;;1E63;
+1E63;LATIN SMALL LETTER S WITH DOT BELOW;Ll;0;L;0073 0323;;;;N;;;1E62;;1E62
+1E64;LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE;Lu;0;L;015A 0307;;;;N;;;;1E65;
+1E65;LATIN SMALL LETTER S WITH ACUTE AND DOT ABOVE;Ll;0;L;015B 0307;;;;N;;;1E64;;1E64
+1E66;LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE;Lu;0;L;0160 0307;;;;N;;;;1E67;
+1E67;LATIN SMALL LETTER S WITH CARON AND DOT ABOVE;Ll;0;L;0161 0307;;;;N;;;1E66;;1E66
+1E68;LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE;Lu;0;L;1E62 0307;;;;N;;;;1E69;
+1E69;LATIN SMALL LETTER S WITH DOT BELOW AND DOT ABOVE;Ll;0;L;1E63 0307;;;;N;;;1E68;;1E68
+1E6A;LATIN CAPITAL LETTER T WITH DOT ABOVE;Lu;0;L;0054 0307;;;;N;;;;1E6B;
+1E6B;LATIN SMALL LETTER T WITH DOT ABOVE;Ll;0;L;0074 0307;;;;N;;;1E6A;;1E6A
+1E6C;LATIN CAPITAL LETTER T WITH DOT BELOW;Lu;0;L;0054 0323;;;;N;;;;1E6D;
+1E6D;LATIN SMALL LETTER T WITH DOT BELOW;Ll;0;L;0074 0323;;;;N;;;1E6C;;1E6C
+1E6E;LATIN CAPITAL LETTER T WITH LINE BELOW;Lu;0;L;0054 0331;;;;N;;;;1E6F;
+1E6F;LATIN SMALL LETTER T WITH LINE BELOW;Ll;0;L;0074 0331;;;;N;;;1E6E;;1E6E
+1E70;LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW;Lu;0;L;0054 032D;;;;N;;;;1E71;
+1E71;LATIN SMALL LETTER T WITH CIRCUMFLEX BELOW;Ll;0;L;0074 032D;;;;N;;;1E70;;1E70
+1E72;LATIN CAPITAL LETTER U WITH DIAERESIS BELOW;Lu;0;L;0055 0324;;;;N;;;;1E73;
+1E73;LATIN SMALL LETTER U WITH DIAERESIS BELOW;Ll;0;L;0075 0324;;;;N;;;1E72;;1E72
+1E74;LATIN CAPITAL LETTER U WITH TILDE BELOW;Lu;0;L;0055 0330;;;;N;;;;1E75;
+1E75;LATIN SMALL LETTER U WITH TILDE BELOW;Ll;0;L;0075 0330;;;;N;;;1E74;;1E74
+1E76;LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW;Lu;0;L;0055 032D;;;;N;;;;1E77;
+1E77;LATIN SMALL LETTER U WITH CIRCUMFLEX BELOW;Ll;0;L;0075 032D;;;;N;;;1E76;;1E76
+1E78;LATIN CAPITAL LETTER U WITH TILDE AND ACUTE;Lu;0;L;0168 0301;;;;N;;;;1E79;
+1E79;LATIN SMALL LETTER U WITH TILDE AND ACUTE;Ll;0;L;0169 0301;;;;N;;;1E78;;1E78
+1E7A;LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS;Lu;0;L;016A 0308;;;;N;;;;1E7B;
+1E7B;LATIN SMALL LETTER U WITH MACRON AND DIAERESIS;Ll;0;L;016B 0308;;;;N;;;1E7A;;1E7A
+1E7C;LATIN CAPITAL LETTER V WITH TILDE;Lu;0;L;0056 0303;;;;N;;;;1E7D;
+1E7D;LATIN SMALL LETTER V WITH TILDE;Ll;0;L;0076 0303;;;;N;;;1E7C;;1E7C
+1E7E;LATIN CAPITAL LETTER V WITH DOT BELOW;Lu;0;L;0056 0323;;;;N;;;;1E7F;
+1E7F;LATIN SMALL LETTER V WITH DOT BELOW;Ll;0;L;0076 0323;;;;N;;;1E7E;;1E7E
+1E80;LATIN CAPITAL LETTER W WITH GRAVE;Lu;0;L;0057 0300;;;;N;;;;1E81;
+1E81;LATIN SMALL LETTER W WITH GRAVE;Ll;0;L;0077 0300;;;;N;;;1E80;;1E80
+1E82;LATIN CAPITAL LETTER W WITH ACUTE;Lu;0;L;0057 0301;;;;N;;;;1E83;
+1E83;LATIN SMALL LETTER W WITH ACUTE;Ll;0;L;0077 0301;;;;N;;;1E82;;1E82
+1E84;LATIN CAPITAL LETTER W WITH DIAERESIS;Lu;0;L;0057 0308;;;;N;;;;1E85;
+1E85;LATIN SMALL LETTER W WITH DIAERESIS;Ll;0;L;0077 0308;;;;N;;;1E84;;1E84
+1E86;LATIN CAPITAL LETTER W WITH DOT ABOVE;Lu;0;L;0057 0307;;;;N;;;;1E87;
+1E87;LATIN SMALL LETTER W WITH DOT ABOVE;Ll;0;L;0077 0307;;;;N;;;1E86;;1E86
+1E88;LATIN CAPITAL LETTER W WITH DOT BELOW;Lu;0;L;0057 0323;;;;N;;;;1E89;
+1E89;LATIN SMALL LETTER W WITH DOT BELOW;Ll;0;L;0077 0323;;;;N;;;1E88;;1E88
+1E8A;LATIN CAPITAL LETTER X WITH DOT ABOVE;Lu;0;L;0058 0307;;;;N;;;;1E8B;
+1E8B;LATIN SMALL LETTER X WITH DOT ABOVE;Ll;0;L;0078 0307;;;;N;;;1E8A;;1E8A
+1E8C;LATIN CAPITAL LETTER X WITH DIAERESIS;Lu;0;L;0058 0308;;;;N;;;;1E8D;
+1E8D;LATIN SMALL LETTER X WITH DIAERESIS;Ll;0;L;0078 0308;;;;N;;;1E8C;;1E8C
+1E8E;LATIN CAPITAL LETTER Y WITH DOT ABOVE;Lu;0;L;0059 0307;;;;N;;;;1E8F;
+1E8F;LATIN SMALL LETTER Y WITH DOT ABOVE;Ll;0;L;0079 0307;;;;N;;;1E8E;;1E8E
+1E90;LATIN CAPITAL LETTER Z WITH CIRCUMFLEX;Lu;0;L;005A 0302;;;;N;;;;1E91;
+1E91;LATIN SMALL LETTER Z WITH CIRCUMFLEX;Ll;0;L;007A 0302;;;;N;;;1E90;;1E90
+1E92;LATIN CAPITAL LETTER Z WITH DOT BELOW;Lu;0;L;005A 0323;;;;N;;;;1E93;
+1E93;LATIN SMALL LETTER Z WITH DOT BELOW;Ll;0;L;007A 0323;;;;N;;;1E92;;1E92
+1E94;LATIN CAPITAL LETTER Z WITH LINE BELOW;Lu;0;L;005A 0331;;;;N;;;;1E95;
+1E95;LATIN SMALL LETTER Z WITH LINE BELOW;Ll;0;L;007A 0331;;;;N;;;1E94;;1E94
+1E96;LATIN SMALL LETTER H WITH LINE BELOW;Ll;0;L;0068 0331;;;;N;;;;;
+1E97;LATIN SMALL LETTER T WITH DIAERESIS;Ll;0;L;0074 0308;;;;N;;;;;
+1E98;LATIN SMALL LETTER W WITH RING ABOVE;Ll;0;L;0077 030A;;;;N;;;;;
+1E99;LATIN SMALL LETTER Y WITH RING ABOVE;Ll;0;L;0079 030A;;;;N;;;;;
+1E9A;LATIN SMALL LETTER A WITH RIGHT HALF RING;Ll;0;L;<compat> 0061 02BE;;;;N;;;;;
+1E9B;LATIN SMALL LETTER LONG S WITH DOT ABOVE;Ll;0;L;017F 0307;;;;N;;;1E60;;1E60
+1EA0;LATIN CAPITAL LETTER A WITH DOT BELOW;Lu;0;L;0041 0323;;;;N;;;;1EA1;
+1EA1;LATIN SMALL LETTER A WITH DOT BELOW;Ll;0;L;0061 0323;;;;N;;;1EA0;;1EA0
+1EA2;LATIN CAPITAL LETTER A WITH HOOK ABOVE;Lu;0;L;0041 0309;;;;N;;;;1EA3;
+1EA3;LATIN SMALL LETTER A WITH HOOK ABOVE;Ll;0;L;0061 0309;;;;N;;;1EA2;;1EA2
+1EA4;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE;Lu;0;L;00C2 0301;;;;N;;;;1EA5;
+1EA5;LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE;Ll;0;L;00E2 0301;;;;N;;;1EA4;;1EA4
+1EA6;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE;Lu;0;L;00C2 0300;;;;N;;;;1EA7;
+1EA7;LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE;Ll;0;L;00E2 0300;;;;N;;;1EA6;;1EA6
+1EA8;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE;Lu;0;L;00C2 0309;;;;N;;;;1EA9;
+1EA9;LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE;Ll;0;L;00E2 0309;;;;N;;;1EA8;;1EA8
+1EAA;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE;Lu;0;L;00C2 0303;;;;N;;;;1EAB;
+1EAB;LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE;Ll;0;L;00E2 0303;;;;N;;;1EAA;;1EAA
+1EAC;LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW;Lu;0;L;1EA0 0302;;;;N;;;;1EAD;
+1EAD;LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW;Ll;0;L;1EA1 0302;;;;N;;;1EAC;;1EAC
+1EAE;LATIN CAPITAL LETTER A WITH BREVE AND ACUTE;Lu;0;L;0102 0301;;;;N;;;;1EAF;
+1EAF;LATIN SMALL LETTER A WITH BREVE AND ACUTE;Ll;0;L;0103 0301;;;;N;;;1EAE;;1EAE
+1EB0;LATIN CAPITAL LETTER A WITH BREVE AND GRAVE;Lu;0;L;0102 0300;;;;N;;;;1EB1;
+1EB1;LATIN SMALL LETTER A WITH BREVE AND GRAVE;Ll;0;L;0103 0300;;;;N;;;1EB0;;1EB0
+1EB2;LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE;Lu;0;L;0102 0309;;;;N;;;;1EB3;
+1EB3;LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE;Ll;0;L;0103 0309;;;;N;;;1EB2;;1EB2
+1EB4;LATIN CAPITAL LETTER A WITH BREVE AND TILDE;Lu;0;L;0102 0303;;;;N;;;;1EB5;
+1EB5;LATIN SMALL LETTER A WITH BREVE AND TILDE;Ll;0;L;0103 0303;;;;N;;;1EB4;;1EB4
+1EB6;LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW;Lu;0;L;1EA0 0306;;;;N;;;;1EB7;
+1EB7;LATIN SMALL LETTER A WITH BREVE AND DOT BELOW;Ll;0;L;1EA1 0306;;;;N;;;1EB6;;1EB6
+1EB8;LATIN CAPITAL LETTER E WITH DOT BELOW;Lu;0;L;0045 0323;;;;N;;;;1EB9;
+1EB9;LATIN SMALL LETTER E WITH DOT BELOW;Ll;0;L;0065 0323;;;;N;;;1EB8;;1EB8
+1EBA;LATIN CAPITAL LETTER E WITH HOOK ABOVE;Lu;0;L;0045 0309;;;;N;;;;1EBB;
+1EBB;LATIN SMALL LETTER E WITH HOOK ABOVE;Ll;0;L;0065 0309;;;;N;;;1EBA;;1EBA
+1EBC;LATIN CAPITAL LETTER E WITH TILDE;Lu;0;L;0045 0303;;;;N;;;;1EBD;
+1EBD;LATIN SMALL LETTER E WITH TILDE;Ll;0;L;0065 0303;;;;N;;;1EBC;;1EBC
+1EBE;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE;Lu;0;L;00CA 0301;;;;N;;;;1EBF;
+1EBF;LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE;Ll;0;L;00EA 0301;;;;N;;;1EBE;;1EBE
+1EC0;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE;Lu;0;L;00CA 0300;;;;N;;;;1EC1;
+1EC1;LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE;Ll;0;L;00EA 0300;;;;N;;;1EC0;;1EC0
+1EC2;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE;Lu;0;L;00CA 0309;;;;N;;;;1EC3;
+1EC3;LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE;Ll;0;L;00EA 0309;;;;N;;;1EC2;;1EC2
+1EC4;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE;Lu;0;L;00CA 0303;;;;N;;;;1EC5;
+1EC5;LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE;Ll;0;L;00EA 0303;;;;N;;;1EC4;;1EC4
+1EC6;LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW;Lu;0;L;1EB8 0302;;;;N;;;;1EC7;
+1EC7;LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW;Ll;0;L;1EB9 0302;;;;N;;;1EC6;;1EC6
+1EC8;LATIN CAPITAL LETTER I WITH HOOK ABOVE;Lu;0;L;0049 0309;;;;N;;;;1EC9;
+1EC9;LATIN SMALL LETTER I WITH HOOK ABOVE;Ll;0;L;0069 0309;;;;N;;;1EC8;;1EC8
+1ECA;LATIN CAPITAL LETTER I WITH DOT BELOW;Lu;0;L;0049 0323;;;;N;;;;1ECB;
+1ECB;LATIN SMALL LETTER I WITH DOT BELOW;Ll;0;L;0069 0323;;;;N;;;1ECA;;1ECA
+1ECC;LATIN CAPITAL LETTER O WITH DOT BELOW;Lu;0;L;004F 0323;;;;N;;;;1ECD;
+1ECD;LATIN SMALL LETTER O WITH DOT BELOW;Ll;0;L;006F 0323;;;;N;;;1ECC;;1ECC
+1ECE;LATIN CAPITAL LETTER O WITH HOOK ABOVE;Lu;0;L;004F 0309;;;;N;;;;1ECF;
+1ECF;LATIN SMALL LETTER O WITH HOOK ABOVE;Ll;0;L;006F 0309;;;;N;;;1ECE;;1ECE
+1ED0;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE;Lu;0;L;00D4 0301;;;;N;;;;1ED1;
+1ED1;LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE;Ll;0;L;00F4 0301;;;;N;;;1ED0;;1ED0
+1ED2;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE;Lu;0;L;00D4 0300;;;;N;;;;1ED3;
+1ED3;LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE;Ll;0;L;00F4 0300;;;;N;;;1ED2;;1ED2
+1ED4;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE;Lu;0;L;00D4 0309;;;;N;;;;1ED5;
+1ED5;LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE;Ll;0;L;00F4 0309;;;;N;;;1ED4;;1ED4
+1ED6;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE;Lu;0;L;00D4 0303;;;;N;;;;1ED7;
+1ED7;LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE;Ll;0;L;00F4 0303;;;;N;;;1ED6;;1ED6
+1ED8;LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW;Lu;0;L;1ECC 0302;;;;N;;;;1ED9;
+1ED9;LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW;Ll;0;L;1ECD 0302;;;;N;;;1ED8;;1ED8
+1EDA;LATIN CAPITAL LETTER O WITH HORN AND ACUTE;Lu;0;L;01A0 0301;;;;N;;;;1EDB;
+1EDB;LATIN SMALL LETTER O WITH HORN AND ACUTE;Ll;0;L;01A1 0301;;;;N;;;1EDA;;1EDA
+1EDC;LATIN CAPITAL LETTER O WITH HORN AND GRAVE;Lu;0;L;01A0 0300;;;;N;;;;1EDD;
+1EDD;LATIN SMALL LETTER O WITH HORN AND GRAVE;Ll;0;L;01A1 0300;;;;N;;;1EDC;;1EDC
+1EDE;LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE;Lu;0;L;01A0 0309;;;;N;;;;1EDF;
+1EDF;LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE;Ll;0;L;01A1 0309;;;;N;;;1EDE;;1EDE
+1EE0;LATIN CAPITAL LETTER O WITH HORN AND TILDE;Lu;0;L;01A0 0303;;;;N;;;;1EE1;
+1EE1;LATIN SMALL LETTER O WITH HORN AND TILDE;Ll;0;L;01A1 0303;;;;N;;;1EE0;;1EE0
+1EE2;LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW;Lu;0;L;01A0 0323;;;;N;;;;1EE3;
+1EE3;LATIN SMALL LETTER O WITH HORN AND DOT BELOW;Ll;0;L;01A1 0323;;;;N;;;1EE2;;1EE2
+1EE4;LATIN CAPITAL LETTER U WITH DOT BELOW;Lu;0;L;0055 0323;;;;N;;;;1EE5;
+1EE5;LATIN SMALL LETTER U WITH DOT BELOW;Ll;0;L;0075 0323;;;;N;;;1EE4;;1EE4
+1EE6;LATIN CAPITAL LETTER U WITH HOOK ABOVE;Lu;0;L;0055 0309;;;;N;;;;1EE7;
+1EE7;LATIN SMALL LETTER U WITH HOOK ABOVE;Ll;0;L;0075 0309;;;;N;;;1EE6;;1EE6
+1EE8;LATIN CAPITAL LETTER U WITH HORN AND ACUTE;Lu;0;L;01AF 0301;;;;N;;;;1EE9;
+1EE9;LATIN SMALL LETTER U WITH HORN AND ACUTE;Ll;0;L;01B0 0301;;;;N;;;1EE8;;1EE8
+1EEA;LATIN CAPITAL LETTER U WITH HORN AND GRAVE;Lu;0;L;01AF 0300;;;;N;;;;1EEB;
+1EEB;LATIN SMALL LETTER U WITH HORN AND GRAVE;Ll;0;L;01B0 0300;;;;N;;;1EEA;;1EEA
+1EEC;LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE;Lu;0;L;01AF 0309;;;;N;;;;1EED;
+1EED;LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE;Ll;0;L;01B0 0309;;;;N;;;1EEC;;1EEC
+1EEE;LATIN CAPITAL LETTER U WITH HORN AND TILDE;Lu;0;L;01AF 0303;;;;N;;;;1EEF;
+1EEF;LATIN SMALL LETTER U WITH HORN AND TILDE;Ll;0;L;01B0 0303;;;;N;;;1EEE;;1EEE
+1EF0;LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW;Lu;0;L;01AF 0323;;;;N;;;;1EF1;
+1EF1;LATIN SMALL LETTER U WITH HORN AND DOT BELOW;Ll;0;L;01B0 0323;;;;N;;;1EF0;;1EF0
+1EF2;LATIN CAPITAL LETTER Y WITH GRAVE;Lu;0;L;0059 0300;;;;N;;;;1EF3;
+1EF3;LATIN SMALL LETTER Y WITH GRAVE;Ll;0;L;0079 0300;;;;N;;;1EF2;;1EF2
+1EF4;LATIN CAPITAL LETTER Y WITH DOT BELOW;Lu;0;L;0059 0323;;;;N;;;;1EF5;
+1EF5;LATIN SMALL LETTER Y WITH DOT BELOW;Ll;0;L;0079 0323;;;;N;;;1EF4;;1EF4
+1EF6;LATIN CAPITAL LETTER Y WITH HOOK ABOVE;Lu;0;L;0059 0309;;;;N;;;;1EF7;
+1EF7;LATIN SMALL LETTER Y WITH HOOK ABOVE;Ll;0;L;0079 0309;;;;N;;;1EF6;;1EF6
+1EF8;LATIN CAPITAL LETTER Y WITH TILDE;Lu;0;L;0059 0303;;;;N;;;;1EF9;
+1EF9;LATIN SMALL LETTER Y WITH TILDE;Ll;0;L;0079 0303;;;;N;;;1EF8;;1EF8
+1F00;GREEK SMALL LETTER ALPHA WITH PSILI;Ll;0;L;03B1 0313;;;;N;;;1F08;;1F08
+1F01;GREEK SMALL LETTER ALPHA WITH DASIA;Ll;0;L;03B1 0314;;;;N;;;1F09;;1F09
+1F02;GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA;Ll;0;L;1F00 0300;;;;N;;;1F0A;;1F0A
+1F03;GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA;Ll;0;L;1F01 0300;;;;N;;;1F0B;;1F0B
+1F04;GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA;Ll;0;L;1F00 0301;;;;N;;;1F0C;;1F0C
+1F05;GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA;Ll;0;L;1F01 0301;;;;N;;;1F0D;;1F0D
+1F06;GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI;Ll;0;L;1F00 0342;;;;N;;;1F0E;;1F0E
+1F07;GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI;Ll;0;L;1F01 0342;;;;N;;;1F0F;;1F0F
+1F08;GREEK CAPITAL LETTER ALPHA WITH PSILI;Lu;0;L;0391 0313;;;;N;;;;1F00;
+1F09;GREEK CAPITAL LETTER ALPHA WITH DASIA;Lu;0;L;0391 0314;;;;N;;;;1F01;
+1F0A;GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA;Lu;0;L;1F08 0300;;;;N;;;;1F02;
+1F0B;GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA;Lu;0;L;1F09 0300;;;;N;;;;1F03;
+1F0C;GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA;Lu;0;L;1F08 0301;;;;N;;;;1F04;
+1F0D;GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA;Lu;0;L;1F09 0301;;;;N;;;;1F05;
+1F0E;GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI;Lu;0;L;1F08 0342;;;;N;;;;1F06;
+1F0F;GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI;Lu;0;L;1F09 0342;;;;N;;;;1F07;
+1F10;GREEK SMALL LETTER EPSILON WITH PSILI;Ll;0;L;03B5 0313;;;;N;;;1F18;;1F18
+1F11;GREEK SMALL LETTER EPSILON WITH DASIA;Ll;0;L;03B5 0314;;;;N;;;1F19;;1F19
+1F12;GREEK SMALL LETTER EPSILON WITH PSILI AND VARIA;Ll;0;L;1F10 0300;;;;N;;;1F1A;;1F1A
+1F13;GREEK SMALL LETTER EPSILON WITH DASIA AND VARIA;Ll;0;L;1F11 0300;;;;N;;;1F1B;;1F1B
+1F14;GREEK SMALL LETTER EPSILON WITH PSILI AND OXIA;Ll;0;L;1F10 0301;;;;N;;;1F1C;;1F1C
+1F15;GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA;Ll;0;L;1F11 0301;;;;N;;;1F1D;;1F1D
+1F18;GREEK CAPITAL LETTER EPSILON WITH PSILI;Lu;0;L;0395 0313;;;;N;;;;1F10;
+1F19;GREEK CAPITAL LETTER EPSILON WITH DASIA;Lu;0;L;0395 0314;;;;N;;;;1F11;
+1F1A;GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA;Lu;0;L;1F18 0300;;;;N;;;;1F12;
+1F1B;GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA;Lu;0;L;1F19 0300;;;;N;;;;1F13;
+1F1C;GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA;Lu;0;L;1F18 0301;;;;N;;;;1F14;
+1F1D;GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA;Lu;0;L;1F19 0301;;;;N;;;;1F15;
+1F20;GREEK SMALL LETTER ETA WITH PSILI;Ll;0;L;03B7 0313;;;;N;;;1F28;;1F28
+1F21;GREEK SMALL LETTER ETA WITH DASIA;Ll;0;L;03B7 0314;;;;N;;;1F29;;1F29
+1F22;GREEK SMALL LETTER ETA WITH PSILI AND VARIA;Ll;0;L;1F20 0300;;;;N;;;1F2A;;1F2A
+1F23;GREEK SMALL LETTER ETA WITH DASIA AND VARIA;Ll;0;L;1F21 0300;;;;N;;;1F2B;;1F2B
+1F24;GREEK SMALL LETTER ETA WITH PSILI AND OXIA;Ll;0;L;1F20 0301;;;;N;;;1F2C;;1F2C
+1F25;GREEK SMALL LETTER ETA WITH DASIA AND OXIA;Ll;0;L;1F21 0301;;;;N;;;1F2D;;1F2D
+1F26;GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI;Ll;0;L;1F20 0342;;;;N;;;1F2E;;1F2E
+1F27;GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI;Ll;0;L;1F21 0342;;;;N;;;1F2F;;1F2F
+1F28;GREEK CAPITAL LETTER ETA WITH PSILI;Lu;0;L;0397 0313;;;;N;;;;1F20;
+1F29;GREEK CAPITAL LETTER ETA WITH DASIA;Lu;0;L;0397 0314;;;;N;;;;1F21;
+1F2A;GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA;Lu;0;L;1F28 0300;;;;N;;;;1F22;
+1F2B;GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA;Lu;0;L;1F29 0300;;;;N;;;;1F23;
+1F2C;GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA;Lu;0;L;1F28 0301;;;;N;;;;1F24;
+1F2D;GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA;Lu;0;L;1F29 0301;;;;N;;;;1F25;
+1F2E;GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI;Lu;0;L;1F28 0342;;;;N;;;;1F26;
+1F2F;GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI;Lu;0;L;1F29 0342;;;;N;;;;1F27;
+1F30;GREEK SMALL LETTER IOTA WITH PSILI;Ll;0;L;03B9 0313;;;;N;;;1F38;;1F38
+1F31;GREEK SMALL LETTER IOTA WITH DASIA;Ll;0;L;03B9 0314;;;;N;;;1F39;;1F39
+1F32;GREEK SMALL LETTER IOTA WITH PSILI AND VARIA;Ll;0;L;1F30 0300;;;;N;;;1F3A;;1F3A
+1F33;GREEK SMALL LETTER IOTA WITH DASIA AND VARIA;Ll;0;L;1F31 0300;;;;N;;;1F3B;;1F3B
+1F34;GREEK SMALL LETTER IOTA WITH PSILI AND OXIA;Ll;0;L;1F30 0301;;;;N;;;1F3C;;1F3C
+1F35;GREEK SMALL LETTER IOTA WITH DASIA AND OXIA;Ll;0;L;1F31 0301;;;;N;;;1F3D;;1F3D
+1F36;GREEK SMALL LETTER IOTA WITH PSILI AND PERISPOMENI;Ll;0;L;1F30 0342;;;;N;;;1F3E;;1F3E
+1F37;GREEK SMALL LETTER IOTA WITH DASIA AND PERISPOMENI;Ll;0;L;1F31 0342;;;;N;;;1F3F;;1F3F
+1F38;GREEK CAPITAL LETTER IOTA WITH PSILI;Lu;0;L;0399 0313;;;;N;;;;1F30;
+1F39;GREEK CAPITAL LETTER IOTA WITH DASIA;Lu;0;L;0399 0314;;;;N;;;;1F31;
+1F3A;GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA;Lu;0;L;1F38 0300;;;;N;;;;1F32;
+1F3B;GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA;Lu;0;L;1F39 0300;;;;N;;;;1F33;
+1F3C;GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA;Lu;0;L;1F38 0301;;;;N;;;;1F34;
+1F3D;GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA;Lu;0;L;1F39 0301;;;;N;;;;1F35;
+1F3E;GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI;Lu;0;L;1F38 0342;;;;N;;;;1F36;
+1F3F;GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI;Lu;0;L;1F39 0342;;;;N;;;;1F37;
+1F40;GREEK SMALL LETTER OMICRON WITH PSILI;Ll;0;L;03BF 0313;;;;N;;;1F48;;1F48
+1F41;GREEK SMALL LETTER OMICRON WITH DASIA;Ll;0;L;03BF 0314;;;;N;;;1F49;;1F49
+1F42;GREEK SMALL LETTER OMICRON WITH PSILI AND VARIA;Ll;0;L;1F40 0300;;;;N;;;1F4A;;1F4A
+1F43;GREEK SMALL LETTER OMICRON WITH DASIA AND VARIA;Ll;0;L;1F41 0300;;;;N;;;1F4B;;1F4B
+1F44;GREEK SMALL LETTER OMICRON WITH PSILI AND OXIA;Ll;0;L;1F40 0301;;;;N;;;1F4C;;1F4C
+1F45;GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA;Ll;0;L;1F41 0301;;;;N;;;1F4D;;1F4D
+1F48;GREEK CAPITAL LETTER OMICRON WITH PSILI;Lu;0;L;039F 0313;;;;N;;;;1F40;
+1F49;GREEK CAPITAL LETTER OMICRON WITH DASIA;Lu;0;L;039F 0314;;;;N;;;;1F41;
+1F4A;GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA;Lu;0;L;1F48 0300;;;;N;;;;1F42;
+1F4B;GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA;Lu;0;L;1F49 0300;;;;N;;;;1F43;
+1F4C;GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA;Lu;0;L;1F48 0301;;;;N;;;;1F44;
+1F4D;GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA;Lu;0;L;1F49 0301;;;;N;;;;1F45;
+1F50;GREEK SMALL LETTER UPSILON WITH PSILI;Ll;0;L;03C5 0313;;;;N;;;;;
+1F51;GREEK SMALL LETTER UPSILON WITH DASIA;Ll;0;L;03C5 0314;;;;N;;;1F59;;1F59
+1F52;GREEK SMALL LETTER UPSILON WITH PSILI AND VARIA;Ll;0;L;1F50 0300;;;;N;;;;;
+1F53;GREEK SMALL LETTER UPSILON WITH DASIA AND VARIA;Ll;0;L;1F51 0300;;;;N;;;1F5B;;1F5B
+1F54;GREEK SMALL LETTER UPSILON WITH PSILI AND OXIA;Ll;0;L;1F50 0301;;;;N;;;;;
+1F55;GREEK SMALL LETTER UPSILON WITH DASIA AND OXIA;Ll;0;L;1F51 0301;;;;N;;;1F5D;;1F5D
+1F56;GREEK SMALL LETTER UPSILON WITH PSILI AND PERISPOMENI;Ll;0;L;1F50 0342;;;;N;;;;;
+1F57;GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI;Ll;0;L;1F51 0342;;;;N;;;1F5F;;1F5F
+1F59;GREEK CAPITAL LETTER UPSILON WITH DASIA;Lu;0;L;03A5 0314;;;;N;;;;1F51;
+1F5B;GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA;Lu;0;L;1F59 0300;;;;N;;;;1F53;
+1F5D;GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA;Lu;0;L;1F59 0301;;;;N;;;;1F55;
+1F5F;GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI;Lu;0;L;1F59 0342;;;;N;;;;1F57;
+1F60;GREEK SMALL LETTER OMEGA WITH PSILI;Ll;0;L;03C9 0313;;;;N;;;1F68;;1F68
+1F61;GREEK SMALL LETTER OMEGA WITH DASIA;Ll;0;L;03C9 0314;;;;N;;;1F69;;1F69
+1F62;GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA;Ll;0;L;1F60 0300;;;;N;;;1F6A;;1F6A
+1F63;GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA;Ll;0;L;1F61 0300;;;;N;;;1F6B;;1F6B
+1F64;GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA;Ll;0;L;1F60 0301;;;;N;;;1F6C;;1F6C
+1F65;GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA;Ll;0;L;1F61 0301;;;;N;;;1F6D;;1F6D
+1F66;GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI;Ll;0;L;1F60 0342;;;;N;;;1F6E;;1F6E
+1F67;GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI;Ll;0;L;1F61 0342;;;;N;;;1F6F;;1F6F
+1F68;GREEK CAPITAL LETTER OMEGA WITH PSILI;Lu;0;L;03A9 0313;;;;N;;;;1F60;
+1F69;GREEK CAPITAL LETTER OMEGA WITH DASIA;Lu;0;L;03A9 0314;;;;N;;;;1F61;
+1F6A;GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA;Lu;0;L;1F68 0300;;;;N;;;;1F62;
+1F6B;GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA;Lu;0;L;1F69 0300;;;;N;;;;1F63;
+1F6C;GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA;Lu;0;L;1F68 0301;;;;N;;;;1F64;
+1F6D;GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA;Lu;0;L;1F69 0301;;;;N;;;;1F65;
+1F6E;GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI;Lu;0;L;1F68 0342;;;;N;;;;1F66;
+1F6F;GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI;Lu;0;L;1F69 0342;;;;N;;;;1F67;
+1F70;GREEK SMALL LETTER ALPHA WITH VARIA;Ll;0;L;03B1 0300;;;;N;;;1FBA;;1FBA
+1F71;GREEK SMALL LETTER ALPHA WITH OXIA;Ll;0;L;03AC;;;;N;;;1FBB;;1FBB
+1F72;GREEK SMALL LETTER EPSILON WITH VARIA;Ll;0;L;03B5 0300;;;;N;;;1FC8;;1FC8
+1F73;GREEK SMALL LETTER EPSILON WITH OXIA;Ll;0;L;03AD;;;;N;;;1FC9;;1FC9
+1F74;GREEK SMALL LETTER ETA WITH VARIA;Ll;0;L;03B7 0300;;;;N;;;1FCA;;1FCA
+1F75;GREEK SMALL LETTER ETA WITH OXIA;Ll;0;L;03AE;;;;N;;;1FCB;;1FCB
+1F76;GREEK SMALL LETTER IOTA WITH VARIA;Ll;0;L;03B9 0300;;;;N;;;1FDA;;1FDA
+1F77;GREEK SMALL LETTER IOTA WITH OXIA;Ll;0;L;03AF;;;;N;;;1FDB;;1FDB
+1F78;GREEK SMALL LETTER OMICRON WITH VARIA;Ll;0;L;03BF 0300;;;;N;;;1FF8;;1FF8
+1F79;GREEK SMALL LETTER OMICRON WITH OXIA;Ll;0;L;03CC;;;;N;;;1FF9;;1FF9
+1F7A;GREEK SMALL LETTER UPSILON WITH VARIA;Ll;0;L;03C5 0300;;;;N;;;1FEA;;1FEA
+1F7B;GREEK SMALL LETTER UPSILON WITH OXIA;Ll;0;L;03CD;;;;N;;;1FEB;;1FEB
+1F7C;GREEK SMALL LETTER OMEGA WITH VARIA;Ll;0;L;03C9 0300;;;;N;;;1FFA;;1FFA
+1F7D;GREEK SMALL LETTER OMEGA WITH OXIA;Ll;0;L;03CE;;;;N;;;1FFB;;1FFB
+1F80;GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI;Ll;0;L;1F00 0345;;;;N;;;1F88;;1F88
+1F81;GREEK SMALL LETTER ALPHA WITH DASIA AND YPOGEGRAMMENI;Ll;0;L;1F01 0345;;;;N;;;1F89;;1F89
+1F82;GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F02 0345;;;;N;;;1F8A;;1F8A
+1F83;GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F03 0345;;;;N;;;1F8B;;1F8B
+1F84;GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F04 0345;;;;N;;;1F8C;;1F8C
+1F85;GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F05 0345;;;;N;;;1F8D;;1F8D
+1F86;GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F06 0345;;;;N;;;1F8E;;1F8E
+1F87;GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F07 0345;;;;N;;;1F8F;;1F8F
+1F88;GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI;Lt;0;L;1F08 0345;;;;N;;;;1F80;
+1F89;GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI;Lt;0;L;1F09 0345;;;;N;;;;1F81;
+1F8A;GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F0A 0345;;;;N;;;;1F82;
+1F8B;GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F0B 0345;;;;N;;;;1F83;
+1F8C;GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F0C 0345;;;;N;;;;1F84;
+1F8D;GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F0D 0345;;;;N;;;;1F85;
+1F8E;GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F0E 0345;;;;N;;;;1F86;
+1F8F;GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F0F 0345;;;;N;;;;1F87;
+1F90;GREEK SMALL LETTER ETA WITH PSILI AND YPOGEGRAMMENI;Ll;0;L;1F20 0345;;;;N;;;1F98;;1F98
+1F91;GREEK SMALL LETTER ETA WITH DASIA AND YPOGEGRAMMENI;Ll;0;L;1F21 0345;;;;N;;;1F99;;1F99
+1F92;GREEK SMALL LETTER ETA WITH PSILI AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F22 0345;;;;N;;;1F9A;;1F9A
+1F93;GREEK SMALL LETTER ETA WITH DASIA AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F23 0345;;;;N;;;1F9B;;1F9B
+1F94;GREEK SMALL LETTER ETA WITH PSILI AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F24 0345;;;;N;;;1F9C;;1F9C
+1F95;GREEK SMALL LETTER ETA WITH DASIA AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F25 0345;;;;N;;;1F9D;;1F9D
+1F96;GREEK SMALL LETTER ETA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F26 0345;;;;N;;;1F9E;;1F9E
+1F97;GREEK SMALL LETTER ETA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F27 0345;;;;N;;;1F9F;;1F9F
+1F98;GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI;Lt;0;L;1F28 0345;;;;N;;;;1F90;
+1F99;GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI;Lt;0;L;1F29 0345;;;;N;;;;1F91;
+1F9A;GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F2A 0345;;;;N;;;;1F92;
+1F9B;GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F2B 0345;;;;N;;;;1F93;
+1F9C;GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F2C 0345;;;;N;;;;1F94;
+1F9D;GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F2D 0345;;;;N;;;;1F95;
+1F9E;GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F2E 0345;;;;N;;;;1F96;
+1F9F;GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F2F 0345;;;;N;;;;1F97;
+1FA0;GREEK SMALL LETTER OMEGA WITH PSILI AND YPOGEGRAMMENI;Ll;0;L;1F60 0345;;;;N;;;1FA8;;1FA8
+1FA1;GREEK SMALL LETTER OMEGA WITH DASIA AND YPOGEGRAMMENI;Ll;0;L;1F61 0345;;;;N;;;1FA9;;1FA9
+1FA2;GREEK SMALL LETTER OMEGA WITH PSILI AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F62 0345;;;;N;;;1FAA;;1FAA
+1FA3;GREEK SMALL LETTER OMEGA WITH DASIA AND VARIA AND YPOGEGRAMMENI;Ll;0;L;1F63 0345;;;;N;;;1FAB;;1FAB
+1FA4;GREEK SMALL LETTER OMEGA WITH PSILI AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F64 0345;;;;N;;;1FAC;;1FAC
+1FA5;GREEK SMALL LETTER OMEGA WITH DASIA AND OXIA AND YPOGEGRAMMENI;Ll;0;L;1F65 0345;;;;N;;;1FAD;;1FAD
+1FA6;GREEK SMALL LETTER OMEGA WITH PSILI AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F66 0345;;;;N;;;1FAE;;1FAE
+1FA7;GREEK SMALL LETTER OMEGA WITH DASIA AND PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1F67 0345;;;;N;;;1FAF;;1FAF
+1FA8;GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI;Lt;0;L;1F68 0345;;;;N;;;;1FA0;
+1FA9;GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI;Lt;0;L;1F69 0345;;;;N;;;;1FA1;
+1FAA;GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F6A 0345;;;;N;;;;1FA2;
+1FAB;GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI;Lt;0;L;1F6B 0345;;;;N;;;;1FA3;
+1FAC;GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F6C 0345;;;;N;;;;1FA4;
+1FAD;GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI;Lt;0;L;1F6D 0345;;;;N;;;;1FA5;
+1FAE;GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F6E 0345;;;;N;;;;1FA6;
+1FAF;GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI;Lt;0;L;1F6F 0345;;;;N;;;;1FA7;
+1FB0;GREEK SMALL LETTER ALPHA WITH VRACHY;Ll;0;L;03B1 0306;;;;N;;;1FB8;;1FB8
+1FB1;GREEK SMALL LETTER ALPHA WITH MACRON;Ll;0;L;03B1 0304;;;;N;;;1FB9;;1FB9
+1FB2;GREEK SMALL LETTER ALPHA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F70 0345;;;;N;;;;;
+1FB3;GREEK SMALL LETTER ALPHA WITH YPOGEGRAMMENI;Ll;0;L;03B1 0345;;;;N;;;1FBC;;1FBC
+1FB4;GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03AC 0345;;;;N;;;;;
+1FB6;GREEK SMALL LETTER ALPHA WITH PERISPOMENI;Ll;0;L;03B1 0342;;;;N;;;;;
+1FB7;GREEK SMALL LETTER ALPHA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FB6 0345;;;;N;;;;;
+1FB8;GREEK CAPITAL LETTER ALPHA WITH VRACHY;Lu;0;L;0391 0306;;;;N;;;;1FB0;
+1FB9;GREEK CAPITAL LETTER ALPHA WITH MACRON;Lu;0;L;0391 0304;;;;N;;;;1FB1;
+1FBA;GREEK CAPITAL LETTER ALPHA WITH VARIA;Lu;0;L;0391 0300;;;;N;;;;1F70;
+1FBB;GREEK CAPITAL LETTER ALPHA WITH OXIA;Lu;0;L;0386;;;;N;;;;1F71;
+1FBC;GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI;Lt;0;L;0391 0345;;;;N;;;;1FB3;
+1FBD;GREEK KORONIS;Sk;0;ON;<compat> 0020 0313;;;;N;;;;;
+1FBE;GREEK PROSGEGRAMMENI;Ll;0;L;03B9;;;;N;;;0399;;0399
+1FBF;GREEK PSILI;Sk;0;ON;<compat> 0020 0313;;;;N;;;;;
+1FC0;GREEK PERISPOMENI;Sk;0;ON;<compat> 0020 0342;;;;N;;;;;
+1FC1;GREEK DIALYTIKA AND PERISPOMENI;Sk;0;ON;00A8 0342;;;;N;;;;;
+1FC2;GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F74 0345;;;;N;;;;;
+1FC3;GREEK SMALL LETTER ETA WITH YPOGEGRAMMENI;Ll;0;L;03B7 0345;;;;N;;;1FCC;;1FCC
+1FC4;GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03AE 0345;;;;N;;;;;
+1FC6;GREEK SMALL LETTER ETA WITH PERISPOMENI;Ll;0;L;03B7 0342;;;;N;;;;;
+1FC7;GREEK SMALL LETTER ETA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FC6 0345;;;;N;;;;;
+1FC8;GREEK CAPITAL LETTER EPSILON WITH VARIA;Lu;0;L;0395 0300;;;;N;;;;1F72;
+1FC9;GREEK CAPITAL LETTER EPSILON WITH OXIA;Lu;0;L;0388;;;;N;;;;1F73;
+1FCA;GREEK CAPITAL LETTER ETA WITH VARIA;Lu;0;L;0397 0300;;;;N;;;;1F74;
+1FCB;GREEK CAPITAL LETTER ETA WITH OXIA;Lu;0;L;0389;;;;N;;;;1F75;
+1FCC;GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI;Lt;0;L;0397 0345;;;;N;;;;1FC3;
+1FCD;GREEK PSILI AND VARIA;Sk;0;ON;1FBF 0300;;;;N;;;;;
+1FCE;GREEK PSILI AND OXIA;Sk;0;ON;1FBF 0301;;;;N;;;;;
+1FCF;GREEK PSILI AND PERISPOMENI;Sk;0;ON;1FBF 0342;;;;N;;;;;
+1FD0;GREEK SMALL LETTER IOTA WITH VRACHY;Ll;0;L;03B9 0306;;;;N;;;1FD8;;1FD8
+1FD1;GREEK SMALL LETTER IOTA WITH MACRON;Ll;0;L;03B9 0304;;;;N;;;1FD9;;1FD9
+1FD2;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND VARIA;Ll;0;L;03CA 0300;;;;N;;;;;
+1FD3;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA;Ll;0;L;0390;;;;N;;;;;
+1FD6;GREEK SMALL LETTER IOTA WITH PERISPOMENI;Ll;0;L;03B9 0342;;;;N;;;;;
+1FD7;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND PERISPOMENI;Ll;0;L;03CA 0342;;;;N;;;;;
+1FD8;GREEK CAPITAL LETTER IOTA WITH VRACHY;Lu;0;L;0399 0306;;;;N;;;;1FD0;
+1FD9;GREEK CAPITAL LETTER IOTA WITH MACRON;Lu;0;L;0399 0304;;;;N;;;;1FD1;
+1FDA;GREEK CAPITAL LETTER IOTA WITH VARIA;Lu;0;L;0399 0300;;;;N;;;;1F76;
+1FDB;GREEK CAPITAL LETTER IOTA WITH OXIA;Lu;0;L;038A;;;;N;;;;1F77;
+1FDD;GREEK DASIA AND VARIA;Sk;0;ON;1FFE 0300;;;;N;;;;;
+1FDE;GREEK DASIA AND OXIA;Sk;0;ON;1FFE 0301;;;;N;;;;;
+1FDF;GREEK DASIA AND PERISPOMENI;Sk;0;ON;1FFE 0342;;;;N;;;;;
+1FE0;GREEK SMALL LETTER UPSILON WITH VRACHY;Ll;0;L;03C5 0306;;;;N;;;1FE8;;1FE8
+1FE1;GREEK SMALL LETTER UPSILON WITH MACRON;Ll;0;L;03C5 0304;;;;N;;;1FE9;;1FE9
+1FE2;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND VARIA;Ll;0;L;03CB 0300;;;;N;;;;;
+1FE3;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND OXIA;Ll;0;L;03B0;;;;N;;;;;
+1FE4;GREEK SMALL LETTER RHO WITH PSILI;Ll;0;L;03C1 0313;;;;N;;;;;
+1FE5;GREEK SMALL LETTER RHO WITH DASIA;Ll;0;L;03C1 0314;;;;N;;;1FEC;;1FEC
+1FE6;GREEK SMALL LETTER UPSILON WITH PERISPOMENI;Ll;0;L;03C5 0342;;;;N;;;;;
+1FE7;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND PERISPOMENI;Ll;0;L;03CB 0342;;;;N;;;;;
+1FE8;GREEK CAPITAL LETTER UPSILON WITH VRACHY;Lu;0;L;03A5 0306;;;;N;;;;1FE0;
+1FE9;GREEK CAPITAL LETTER UPSILON WITH MACRON;Lu;0;L;03A5 0304;;;;N;;;;1FE1;
+1FEA;GREEK CAPITAL LETTER UPSILON WITH VARIA;Lu;0;L;03A5 0300;;;;N;;;;1F7A;
+1FEB;GREEK CAPITAL LETTER UPSILON WITH OXIA;Lu;0;L;038E;;;;N;;;;1F7B;
+1FEC;GREEK CAPITAL LETTER RHO WITH DASIA;Lu;0;L;03A1 0314;;;;N;;;;1FE5;
+1FED;GREEK DIALYTIKA AND VARIA;Sk;0;ON;00A8 0300;;;;N;;;;;
+1FEE;GREEK DIALYTIKA AND OXIA;Sk;0;ON;0385;;;;N;;;;;
+1FEF;GREEK VARIA;Sk;0;ON;0060;;;;N;;;;;
+1FF2;GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI;Ll;0;L;1F7C 0345;;;;N;;;;;
+1FF3;GREEK SMALL LETTER OMEGA WITH YPOGEGRAMMENI;Ll;0;L;03C9 0345;;;;N;;;1FFC;;1FFC
+1FF4;GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI;Ll;0;L;03CE 0345;;;;N;;;;;
+1FF6;GREEK SMALL LETTER OMEGA WITH PERISPOMENI;Ll;0;L;03C9 0342;;;;N;;;;;
+1FF7;GREEK SMALL LETTER OMEGA WITH PERISPOMENI AND YPOGEGRAMMENI;Ll;0;L;1FF6 0345;;;;N;;;;;
+1FF8;GREEK CAPITAL LETTER OMICRON WITH VARIA;Lu;0;L;039F 0300;;;;N;;;;1F78;
+1FF9;GREEK CAPITAL LETTER OMICRON WITH OXIA;Lu;0;L;038C;;;;N;;;;1F79;
+1FFA;GREEK CAPITAL LETTER OMEGA WITH VARIA;Lu;0;L;03A9 0300;;;;N;;;;1F7C;
+1FFB;GREEK CAPITAL LETTER OMEGA WITH OXIA;Lu;0;L;038F;;;;N;;;;1F7D;
+1FFC;GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI;Lt;0;L;03A9 0345;;;;N;;;;1FF3;
+1FFD;GREEK OXIA;Sk;0;ON;00B4;;;;N;;;;;
+1FFE;GREEK DASIA;Sk;0;ON;<compat> 0020 0314;;;;N;;;;;
+2000;EN QUAD;Zs;0;WS;2002;;;;N;;;;;
+2001;EM QUAD;Zs;0;WS;2003;;;;N;;;;;
+2002;EN SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2003;EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2004;THREE-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2005;FOUR-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2006;SIX-PER-EM SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2007;FIGURE SPACE;Zs;0;WS;<noBreak> 0020;;;;N;;;;;
+2008;PUNCTUATION SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2009;THIN SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+200A;HAIR SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+200B;ZERO WIDTH SPACE;Zs;0;BN;;;;;N;;;;;
+200C;ZERO WIDTH NON-JOINER;Cf;0;BN;;;;;N;;;;;
+200D;ZERO WIDTH JOINER;Cf;0;BN;;;;;N;;;;;
+200E;LEFT-TO-RIGHT MARK;Cf;0;L;;;;;N;;;;;
+200F;RIGHT-TO-LEFT MARK;Cf;0;R;;;;;N;;;;;
+2010;HYPHEN;Pd;0;ON;;;;;N;;;;;
+2011;NON-BREAKING HYPHEN;Pd;0;ON;<noBreak> 2010;;;;N;;;;;
+2012;FIGURE DASH;Pd;0;ON;;;;;N;;;;;
+2013;EN DASH;Pd;0;ON;;;;;N;;;;;
+2014;EM DASH;Pd;0;ON;;;;;N;;;;;
+2015;HORIZONTAL BAR;Pd;0;ON;;;;;N;QUOTATION DASH;;;;
+2016;DOUBLE VERTICAL LINE;Po;0;ON;;;;;N;DOUBLE VERTICAL BAR;;;;
+2017;DOUBLE LOW LINE;Po;0;ON;<compat> 0020 0333;;;;N;SPACING DOUBLE UNDERSCORE;;;;
+2018;LEFT SINGLE QUOTATION MARK;Pi;0;ON;;;;;N;SINGLE TURNED COMMA QUOTATION MARK;;;;
+2019;RIGHT SINGLE QUOTATION MARK;Pf;0;ON;;;;;N;SINGLE COMMA QUOTATION MARK;;;;
+201A;SINGLE LOW-9 QUOTATION MARK;Ps;0;ON;;;;;N;LOW SINGLE COMMA QUOTATION MARK;;;;
+201B;SINGLE HIGH-REVERSED-9 QUOTATION MARK;Pi;0;ON;;;;;N;SINGLE REVERSED COMMA QUOTATION MARK;;;;
+201C;LEFT DOUBLE QUOTATION MARK;Pi;0;ON;;;;;N;DOUBLE TURNED COMMA QUOTATION MARK;;;;
+201D;RIGHT DOUBLE QUOTATION MARK;Pf;0;ON;;;;;N;DOUBLE COMMA QUOTATION MARK;;;;
+201E;DOUBLE LOW-9 QUOTATION MARK;Ps;0;ON;;;;;N;LOW DOUBLE COMMA QUOTATION MARK;;;;
+201F;DOUBLE HIGH-REVERSED-9 QUOTATION MARK;Pi;0;ON;;;;;N;DOUBLE REVERSED COMMA QUOTATION MARK;;;;
+2020;DAGGER;Po;0;ON;;;;;N;;;;;
+2021;DOUBLE DAGGER;Po;0;ON;;;;;N;;;;;
+2022;BULLET;Po;0;ON;;;;;N;;;;;
+2023;TRIANGULAR BULLET;Po;0;ON;;;;;N;;;;;
+2024;ONE DOT LEADER;Po;0;ON;<compat> 002E;;;;N;;;;;
+2025;TWO DOT LEADER;Po;0;ON;<compat> 002E 002E;;;;N;;;;;
+2026;HORIZONTAL ELLIPSIS;Po;0;ON;<compat> 002E 002E 002E;;;;N;;;;;
+2027;HYPHENATION POINT;Po;0;ON;;;;;N;;;;;
+2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;;
+2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;;
+202A;LEFT-TO-RIGHT EMBEDDING;Cf;0;LRE;;;;;N;;;;;
+202B;RIGHT-TO-LEFT EMBEDDING;Cf;0;RLE;;;;;N;;;;;
+202C;POP DIRECTIONAL FORMATTING;Cf;0;PDF;;;;;N;;;;;
+202D;LEFT-TO-RIGHT OVERRIDE;Cf;0;LRO;;;;;N;;;;;
+202E;RIGHT-TO-LEFT OVERRIDE;Cf;0;RLO;;;;;N;;;;;
+202F;NARROW NO-BREAK SPACE;Zs;0;WS;<noBreak> 0020;;;;N;;;;;
+2030;PER MILLE SIGN;Po;0;ET;;;;;N;;;;;
+2031;PER TEN THOUSAND SIGN;Po;0;ET;;;;;N;;;;;
+2032;PRIME;Po;0;ET;;;;;N;;;;;
+2033;DOUBLE PRIME;Po;0;ET;<compat> 2032 2032;;;;N;;;;;
+2034;TRIPLE PRIME;Po;0;ET;<compat> 2032 2032 2032;;;;N;;;;;
+2035;REVERSED PRIME;Po;0;ON;;;;;N;;;;;
+2036;REVERSED DOUBLE PRIME;Po;0;ON;<compat> 2035 2035;;;;N;;;;;
+2037;REVERSED TRIPLE PRIME;Po;0;ON;<compat> 2035 2035 2035;;;;N;;;;;
+2038;CARET;Po;0;ON;;;;;N;;;;;
+2039;SINGLE LEFT-POINTING ANGLE QUOTATION MARK;Pi;0;ON;;;;;Y;LEFT POINTING SINGLE GUILLEMET;;;;
+203A;SINGLE RIGHT-POINTING ANGLE QUOTATION MARK;Pf;0;ON;;;;;Y;RIGHT POINTING SINGLE GUILLEMET;;;;
+203B;REFERENCE MARK;Po;0;ON;;;;;N;;;;;
+203C;DOUBLE EXCLAMATION MARK;Po;0;ON;<compat> 0021 0021;;;;N;;;;;
+203D;INTERROBANG;Po;0;ON;;;;;N;;;;;
+203E;OVERLINE;Po;0;ON;<compat> 0020 0305;;;;N;SPACING OVERSCORE;;;;
+203F;UNDERTIE;Pc;0;ON;;;;;N;;Enotikon;;;
+2040;CHARACTER TIE;Pc;0;ON;;;;;N;;;;;
+2041;CARET INSERTION POINT;Po;0;ON;;;;;N;;;;;
+2042;ASTERISM;Po;0;ON;;;;;N;;;;;
+2043;HYPHEN BULLET;Po;0;ON;;;;;N;;;;;
+2044;FRACTION SLASH;Sm;0;ON;;;;;N;;;;;
+2045;LEFT SQUARE BRACKET WITH QUILL;Ps;0;ON;;;;;Y;;;;;
+2046;RIGHT SQUARE BRACKET WITH QUILL;Pe;0;ON;;;;;Y;;;;;
+2047;DOUBLE QUESTION MARK;Po;0;ON;<compat> 003F 003F;;;;N;;;;;
+2048;QUESTION EXCLAMATION MARK;Po;0;ON;<compat> 003F 0021;;;;N;;;;;
+2049;EXCLAMATION QUESTION MARK;Po;0;ON;<compat> 0021 003F;;;;N;;;;;
+204A;TIRONIAN SIGN ET;Po;0;ON;;;;;N;;;;;
+204B;REVERSED PILCROW SIGN;Po;0;ON;;;;;N;;;;;
+204C;BLACK LEFTWARDS BULLET;Po;0;ON;;;;;N;;;;;
+204D;BLACK RIGHTWARDS BULLET;Po;0;ON;;;;;N;;;;;
+204E;LOW ASTERISK;Po;0;ON;;;;;N;;;;;
+204F;REVERSED SEMICOLON;Po;0;ON;;;;;N;;;;;
+2050;CLOSE UP;Po;0;ON;;;;;N;;;;;
+2051;TWO ASTERISKS ALIGNED VERTICALLY;Po;0;ON;;;;;N;;;;;
+2052;COMMERCIAL MINUS SIGN;Sm;0;ON;;;;;N;;;;;
+2057;QUADRUPLE PRIME;Po;0;ON;<compat> 2032 2032 2032 2032;;;;N;;;;;
+205F;MEDIUM MATHEMATICAL SPACE;Zs;0;WS;<compat> 0020;;;;N;;;;;
+2060;WORD JOINER;Cf;0;BN;;;;;N;;;;;
+2061;FUNCTION APPLICATION;Cf;0;BN;;;;;N;;;;;
+2062;INVISIBLE TIMES;Cf;0;BN;;;;;N;;;;;
+2063;INVISIBLE SEPARATOR;Cf;0;BN;;;;;N;;;;;
+206A;INHIBIT SYMMETRIC SWAPPING;Cf;0;BN;;;;;N;;;;;
+206B;ACTIVATE SYMMETRIC SWAPPING;Cf;0;BN;;;;;N;;;;;
+206C;INHIBIT ARABIC FORM SHAPING;Cf;0;BN;;;;;N;;;;;
+206D;ACTIVATE ARABIC FORM SHAPING;Cf;0;BN;;;;;N;;;;;
+206E;NATIONAL DIGIT SHAPES;Cf;0;BN;;;;;N;;;;;
+206F;NOMINAL DIGIT SHAPES;Cf;0;BN;;;;;N;;;;;
+2070;SUPERSCRIPT ZERO;No;0;EN;<super> 0030;0;0;0;N;SUPERSCRIPT DIGIT ZERO;;;;
+2071;SUPERSCRIPT LATIN SMALL LETTER I;Ll;0;L;<super> 0069;;;;N;;;;;
+2074;SUPERSCRIPT FOUR;No;0;EN;<super> 0034;4;4;4;N;SUPERSCRIPT DIGIT FOUR;;;;
+2075;SUPERSCRIPT FIVE;No;0;EN;<super> 0035;5;5;5;N;SUPERSCRIPT DIGIT FIVE;;;;
+2076;SUPERSCRIPT SIX;No;0;EN;<super> 0036;6;6;6;N;SUPERSCRIPT DIGIT SIX;;;;
+2077;SUPERSCRIPT SEVEN;No;0;EN;<super> 0037;7;7;7;N;SUPERSCRIPT DIGIT SEVEN;;;;
+2078;SUPERSCRIPT EIGHT;No;0;EN;<super> 0038;8;8;8;N;SUPERSCRIPT DIGIT EIGHT;;;;
+2079;SUPERSCRIPT NINE;No;0;EN;<super> 0039;9;9;9;N;SUPERSCRIPT DIGIT NINE;;;;
+207A;SUPERSCRIPT PLUS SIGN;Sm;0;ET;<super> 002B;;;;N;;;;;
+207B;SUPERSCRIPT MINUS;Sm;0;ET;<super> 2212;;;;N;SUPERSCRIPT HYPHEN-MINUS;;;;
+207C;SUPERSCRIPT EQUALS SIGN;Sm;0;ON;<super> 003D;;;;N;;;;;
+207D;SUPERSCRIPT LEFT PARENTHESIS;Ps;0;ON;<super> 0028;;;;Y;SUPERSCRIPT OPENING PARENTHESIS;;;;
+207E;SUPERSCRIPT RIGHT PARENTHESIS;Pe;0;ON;<super> 0029;;;;Y;SUPERSCRIPT CLOSING PARENTHESIS;;;;
+207F;SUPERSCRIPT LATIN SMALL LETTER N;Ll;0;L;<super> 006E;;;;N;;;;;
+2080;SUBSCRIPT ZERO;No;0;EN;<sub> 0030;0;0;0;N;SUBSCRIPT DIGIT ZERO;;;;
+2081;SUBSCRIPT ONE;No;0;EN;<sub> 0031;1;1;1;N;SUBSCRIPT DIGIT ONE;;;;
+2082;SUBSCRIPT TWO;No;0;EN;<sub> 0032;2;2;2;N;SUBSCRIPT DIGIT TWO;;;;
+2083;SUBSCRIPT THREE;No;0;EN;<sub> 0033;3;3;3;N;SUBSCRIPT DIGIT THREE;;;;
+2084;SUBSCRIPT FOUR;No;0;EN;<sub> 0034;4;4;4;N;SUBSCRIPT DIGIT FOUR;;;;
+2085;SUBSCRIPT FIVE;No;0;EN;<sub> 0035;5;5;5;N;SUBSCRIPT DIGIT FIVE;;;;
+2086;SUBSCRIPT SIX;No;0;EN;<sub> 0036;6;6;6;N;SUBSCRIPT DIGIT SIX;;;;
+2087;SUBSCRIPT SEVEN;No;0;EN;<sub> 0037;7;7;7;N;SUBSCRIPT DIGIT SEVEN;;;;
+2088;SUBSCRIPT EIGHT;No;0;EN;<sub> 0038;8;8;8;N;SUBSCRIPT DIGIT EIGHT;;;;
+2089;SUBSCRIPT NINE;No;0;EN;<sub> 0039;9;9;9;N;SUBSCRIPT DIGIT NINE;;;;
+208A;SUBSCRIPT PLUS SIGN;Sm;0;ET;<sub> 002B;;;;N;;;;;
+208B;SUBSCRIPT MINUS;Sm;0;ET;<sub> 2212;;;;N;SUBSCRIPT HYPHEN-MINUS;;;;
+208C;SUBSCRIPT EQUALS SIGN;Sm;0;ON;<sub> 003D;;;;N;;;;;
+208D;SUBSCRIPT LEFT PARENTHESIS;Ps;0;ON;<sub> 0028;;;;Y;SUBSCRIPT OPENING PARENTHESIS;;;;
+208E;SUBSCRIPT RIGHT PARENTHESIS;Pe;0;ON;<sub> 0029;;;;Y;SUBSCRIPT CLOSING PARENTHESIS;;;;
+20A0;EURO-CURRENCY SIGN;Sc;0;ET;;;;;N;;;;;
+20A1;COLON SIGN;Sc;0;ET;;;;;N;;;;;
+20A2;CRUZEIRO SIGN;Sc;0;ET;;;;;N;;;;;
+20A3;FRENCH FRANC SIGN;Sc;0;ET;;;;;N;;;;;
+20A4;LIRA SIGN;Sc;0;ET;;;;;N;;;;;
+20A5;MILL SIGN;Sc;0;ET;;;;;N;;;;;
+20A6;NAIRA SIGN;Sc;0;ET;;;;;N;;;;;
+20A7;PESETA SIGN;Sc;0;ET;;;;;N;;;;;
+20A8;RUPEE SIGN;Sc;0;ET;<compat> 0052 0073;;;;N;;;;;
+20A9;WON SIGN;Sc;0;ET;;;;;N;;;;;
+20AA;NEW SHEQEL SIGN;Sc;0;ET;;;;;N;;;;;
+20AB;DONG SIGN;Sc;0;ET;;;;;N;;;;;
+20AC;EURO SIGN;Sc;0;ET;;;;;N;;;;;
+20AD;KIP SIGN;Sc;0;ET;;;;;N;;;;;
+20AE;TUGRIK SIGN;Sc;0;ET;;;;;N;;;;;
+20AF;DRACHMA SIGN;Sc;0;ET;;;;;N;;;;;
+20B0;GERMAN PENNY SIGN;Sc;0;ET;;;;;N;;;;;
+20B1;PESO SIGN;Sc;0;ET;;;;;N;;;;;
+20D0;COMBINING LEFT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT HARPOON ABOVE;;;;
+20D1;COMBINING RIGHT HARPOON ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT HARPOON ABOVE;;;;
+20D2;COMBINING LONG VERTICAL LINE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING LONG VERTICAL BAR OVERLAY;;;;
+20D3;COMBINING SHORT VERTICAL LINE OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING SHORT VERTICAL BAR OVERLAY;;;;
+20D4;COMBINING ANTICLOCKWISE ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING ANTICLOCKWISE ARROW ABOVE;;;;
+20D5;COMBINING CLOCKWISE ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING CLOCKWISE ARROW ABOVE;;;;
+20D6;COMBINING LEFT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT ARROW ABOVE;;;;
+20D7;COMBINING RIGHT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING RIGHT ARROW ABOVE;;;;
+20D8;COMBINING RING OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING RING OVERLAY;;;;
+20D9;COMBINING CLOCKWISE RING OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING CLOCKWISE RING OVERLAY;;;;
+20DA;COMBINING ANTICLOCKWISE RING OVERLAY;Mn;1;NSM;;;;;N;NON-SPACING ANTICLOCKWISE RING OVERLAY;;;;
+20DB;COMBINING THREE DOTS ABOVE;Mn;230;NSM;;;;;N;NON-SPACING THREE DOTS ABOVE;;;;
+20DC;COMBINING FOUR DOTS ABOVE;Mn;230;NSM;;;;;N;NON-SPACING FOUR DOTS ABOVE;;;;
+20DD;COMBINING ENCLOSING CIRCLE;Me;0;NSM;;;;;N;ENCLOSING CIRCLE;;;;
+20DE;COMBINING ENCLOSING SQUARE;Me;0;NSM;;;;;N;ENCLOSING SQUARE;;;;
+20DF;COMBINING ENCLOSING DIAMOND;Me;0;NSM;;;;;N;ENCLOSING DIAMOND;;;;
+20E0;COMBINING ENCLOSING CIRCLE BACKSLASH;Me;0;NSM;;;;;N;ENCLOSING CIRCLE SLASH;;;;
+20E1;COMBINING LEFT RIGHT ARROW ABOVE;Mn;230;NSM;;;;;N;NON-SPACING LEFT RIGHT ARROW ABOVE;;;;
+20E2;COMBINING ENCLOSING SCREEN;Me;0;NSM;;;;;N;;;;;
+20E3;COMBINING ENCLOSING KEYCAP;Me;0;NSM;;;;;N;;;;;
+20E4;COMBINING ENCLOSING UPWARD POINTING TRIANGLE;Me;0;NSM;;;;;N;;;;;
+20E5;COMBINING REVERSE SOLIDUS OVERLAY;Mn;1;NSM;;;;;N;;;;;
+20E6;COMBINING DOUBLE VERTICAL STROKE OVERLAY;Mn;1;NSM;;;;;N;;;;;
+20E7;COMBINING ANNUITY SYMBOL;Mn;230;NSM;;;;;N;;;;;
+20E8;COMBINING TRIPLE UNDERDOT;Mn;220;NSM;;;;;N;;;;;
+20E9;COMBINING WIDE BRIDGE ABOVE;Mn;230;NSM;;;;;N;;;;;
+20EA;COMBINING LEFTWARDS ARROW OVERLAY;Mn;1;NSM;;;;;N;;;;;
+2100;ACCOUNT OF;So;0;ON;<compat> 0061 002F 0063;;;;N;;;;;
+2101;ADDRESSED TO THE SUBJECT;So;0;ON;<compat> 0061 002F 0073;;;;N;;;;;
+2102;DOUBLE-STRUCK CAPITAL C;Lu;0;L;<font> 0043;;;;N;DOUBLE-STRUCK C;;;;
+2103;DEGREE CELSIUS;So;0;ON;<compat> 00B0 0043;;;;N;DEGREES CENTIGRADE;;;;
+2104;CENTRE LINE SYMBOL;So;0;ON;;;;;N;C L SYMBOL;;;;
+2105;CARE OF;So;0;ON;<compat> 0063 002F 006F;;;;N;;;;;
+2106;CADA UNA;So;0;ON;<compat> 0063 002F 0075;;;;N;;;;;
+2107;EULER CONSTANT;Lu;0;L;<compat> 0190;;;;N;EULERS;;;;
+2108;SCRUPLE;So;0;ON;;;;;N;;;;;
+2109;DEGREE FAHRENHEIT;So;0;ON;<compat> 00B0 0046;;;;N;DEGREES FAHRENHEIT;;;;
+210A;SCRIPT SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+210B;SCRIPT CAPITAL H;Lu;0;L;<font> 0048;;;;N;SCRIPT H;;;;
+210C;BLACK-LETTER CAPITAL H;Lu;0;L;<font> 0048;;;;N;BLACK-LETTER H;;;;
+210D;DOUBLE-STRUCK CAPITAL H;Lu;0;L;<font> 0048;;;;N;DOUBLE-STRUCK H;;;;
+210E;PLANCK CONSTANT;Ll;0;L;<font> 0068;;;;N;;;;;
+210F;PLANCK CONSTANT OVER TWO PI;Ll;0;L;<font> 0127;;;;N;PLANCK CONSTANT OVER 2 PI;;;;
+2110;SCRIPT CAPITAL I;Lu;0;L;<font> 0049;;;;N;SCRIPT I;;;;
+2111;BLACK-LETTER CAPITAL I;Lu;0;L;<font> 0049;;;;N;BLACK-LETTER I;;;;
+2112;SCRIPT CAPITAL L;Lu;0;L;<font> 004C;;;;N;SCRIPT L;;;;
+2113;SCRIPT SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+2114;L B BAR SYMBOL;So;0;ON;;;;;N;;;;;
+2115;DOUBLE-STRUCK CAPITAL N;Lu;0;L;<font> 004E;;;;N;DOUBLE-STRUCK N;;;;
+2116;NUMERO SIGN;So;0;ON;<compat> 004E 006F;;;;N;NUMERO;;;;
+2117;SOUND RECORDING COPYRIGHT;So;0;ON;;;;;N;;;;;
+2118;SCRIPT CAPITAL P;So;0;ON;;;;;N;SCRIPT P;;;;
+2119;DOUBLE-STRUCK CAPITAL P;Lu;0;L;<font> 0050;;;;N;DOUBLE-STRUCK P;;;;
+211A;DOUBLE-STRUCK CAPITAL Q;Lu;0;L;<font> 0051;;;;N;DOUBLE-STRUCK Q;;;;
+211B;SCRIPT CAPITAL R;Lu;0;L;<font> 0052;;;;N;SCRIPT R;;;;
+211C;BLACK-LETTER CAPITAL R;Lu;0;L;<font> 0052;;;;N;BLACK-LETTER R;;;;
+211D;DOUBLE-STRUCK CAPITAL R;Lu;0;L;<font> 0052;;;;N;DOUBLE-STRUCK R;;;;
+211E;PRESCRIPTION TAKE;So;0;ON;;;;;N;;;;;
+211F;RESPONSE;So;0;ON;;;;;N;;;;;
+2120;SERVICE MARK;So;0;ON;<super> 0053 004D;;;;N;;;;;
+2121;TELEPHONE SIGN;So;0;ON;<compat> 0054 0045 004C;;;;N;T E L SYMBOL;;;;
+2122;TRADE MARK SIGN;So;0;ON;<super> 0054 004D;;;;N;TRADEMARK;;;;
+2123;VERSICLE;So;0;ON;;;;;N;;;;;
+2124;DOUBLE-STRUCK CAPITAL Z;Lu;0;L;<font> 005A;;;;N;DOUBLE-STRUCK Z;;;;
+2125;OUNCE SIGN;So;0;ON;;;;;N;OUNCE;;;;
+2126;OHM SIGN;Lu;0;L;03A9;;;;N;OHM;;;03C9;
+2127;INVERTED OHM SIGN;So;0;ON;;;;;N;MHO;;;;
+2128;BLACK-LETTER CAPITAL Z;Lu;0;L;<font> 005A;;;;N;BLACK-LETTER Z;;;;
+2129;TURNED GREEK SMALL LETTER IOTA;So;0;ON;;;;;N;;;;;
+212A;KELVIN SIGN;Lu;0;L;004B;;;;N;DEGREES KELVIN;;;006B;
+212B;ANGSTROM SIGN;Lu;0;L;00C5;;;;N;ANGSTROM UNIT;;;00E5;
+212C;SCRIPT CAPITAL B;Lu;0;L;<font> 0042;;;;N;SCRIPT B;;;;
+212D;BLACK-LETTER CAPITAL C;Lu;0;L;<font> 0043;;;;N;BLACK-LETTER C;;;;
+212E;ESTIMATED SYMBOL;So;0;ET;;;;;N;;;;;
+212F;SCRIPT SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+2130;SCRIPT CAPITAL E;Lu;0;L;<font> 0045;;;;N;SCRIPT E;;;;
+2131;SCRIPT CAPITAL F;Lu;0;L;<font> 0046;;;;N;SCRIPT F;;;;
+2132;TURNED CAPITAL F;So;0;ON;;;;;N;TURNED F;;;;
+2133;SCRIPT CAPITAL M;Lu;0;L;<font> 004D;;;;N;SCRIPT M;;;;
+2134;SCRIPT SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+2135;ALEF SYMBOL;Lo;0;L;<compat> 05D0;;;;N;FIRST TRANSFINITE CARDINAL;;;;
+2136;BET SYMBOL;Lo;0;L;<compat> 05D1;;;;N;SECOND TRANSFINITE CARDINAL;;;;
+2137;GIMEL SYMBOL;Lo;0;L;<compat> 05D2;;;;N;THIRD TRANSFINITE CARDINAL;;;;
+2138;DALET SYMBOL;Lo;0;L;<compat> 05D3;;;;N;FOURTH TRANSFINITE CARDINAL;;;;
+2139;INFORMATION SOURCE;Ll;0;L;<font> 0069;;;;N;;;;;
+213A;ROTATED CAPITAL Q;So;0;ON;;;;;N;;;;;
+213D;DOUBLE-STRUCK SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+213E;DOUBLE-STRUCK CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+213F;DOUBLE-STRUCK CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+2140;DOUBLE-STRUCK N-ARY SUMMATION;Sm;0;ON;<font> 2211;;;;Y;;;;;
+2141;TURNED SANS-SERIF CAPITAL G;Sm;0;ON;;;;;N;;;;;
+2142;TURNED SANS-SERIF CAPITAL L;Sm;0;ON;;;;;N;;;;;
+2143;REVERSED SANS-SERIF CAPITAL L;Sm;0;ON;;;;;N;;;;;
+2144;TURNED SANS-SERIF CAPITAL Y;Sm;0;ON;;;;;N;;;;;
+2145;DOUBLE-STRUCK ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+2146;DOUBLE-STRUCK ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+2147;DOUBLE-STRUCK ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+2148;DOUBLE-STRUCK ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+2149;DOUBLE-STRUCK ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+214A;PROPERTY LINE;So;0;ON;;;;;N;;;;;
+214B;TURNED AMPERSAND;Sm;0;ON;;;;;N;;;;;
+2153;VULGAR FRACTION ONE THIRD;No;0;ON;<fraction> 0031 2044 0033;;;1/3;N;FRACTION ONE THIRD;;;;
+2154;VULGAR FRACTION TWO THIRDS;No;0;ON;<fraction> 0032 2044 0033;;;2/3;N;FRACTION TWO THIRDS;;;;
+2155;VULGAR FRACTION ONE FIFTH;No;0;ON;<fraction> 0031 2044 0035;;;1/5;N;FRACTION ONE FIFTH;;;;
+2156;VULGAR FRACTION TWO FIFTHS;No;0;ON;<fraction> 0032 2044 0035;;;2/5;N;FRACTION TWO FIFTHS;;;;
+2157;VULGAR FRACTION THREE FIFTHS;No;0;ON;<fraction> 0033 2044 0035;;;3/5;N;FRACTION THREE FIFTHS;;;;
+2158;VULGAR FRACTION FOUR FIFTHS;No;0;ON;<fraction> 0034 2044 0035;;;4/5;N;FRACTION FOUR FIFTHS;;;;
+2159;VULGAR FRACTION ONE SIXTH;No;0;ON;<fraction> 0031 2044 0036;;;1/6;N;FRACTION ONE SIXTH;;;;
+215A;VULGAR FRACTION FIVE SIXTHS;No;0;ON;<fraction> 0035 2044 0036;;;5/6;N;FRACTION FIVE SIXTHS;;;;
+215B;VULGAR FRACTION ONE EIGHTH;No;0;ON;<fraction> 0031 2044 0038;;;1/8;N;FRACTION ONE EIGHTH;;;;
+215C;VULGAR FRACTION THREE EIGHTHS;No;0;ON;<fraction> 0033 2044 0038;;;3/8;N;FRACTION THREE EIGHTHS;;;;
+215D;VULGAR FRACTION FIVE EIGHTHS;No;0;ON;<fraction> 0035 2044 0038;;;5/8;N;FRACTION FIVE EIGHTHS;;;;
+215E;VULGAR FRACTION SEVEN EIGHTHS;No;0;ON;<fraction> 0037 2044 0038;;;7/8;N;FRACTION SEVEN EIGHTHS;;;;
+215F;FRACTION NUMERATOR ONE;No;0;ON;<fraction> 0031 2044;;;1;N;;;;;
+2160;ROMAN NUMERAL ONE;Nl;0;L;<compat> 0049;;;1;N;;;;2170;
+2161;ROMAN NUMERAL TWO;Nl;0;L;<compat> 0049 0049;;;2;N;;;;2171;
+2162;ROMAN NUMERAL THREE;Nl;0;L;<compat> 0049 0049 0049;;;3;N;;;;2172;
+2163;ROMAN NUMERAL FOUR;Nl;0;L;<compat> 0049 0056;;;4;N;;;;2173;
+2164;ROMAN NUMERAL FIVE;Nl;0;L;<compat> 0056;;;5;N;;;;2174;
+2165;ROMAN NUMERAL SIX;Nl;0;L;<compat> 0056 0049;;;6;N;;;;2175;
+2166;ROMAN NUMERAL SEVEN;Nl;0;L;<compat> 0056 0049 0049;;;7;N;;;;2176;
+2167;ROMAN NUMERAL EIGHT;Nl;0;L;<compat> 0056 0049 0049 0049;;;8;N;;;;2177;
+2168;ROMAN NUMERAL NINE;Nl;0;L;<compat> 0049 0058;;;9;N;;;;2178;
+2169;ROMAN NUMERAL TEN;Nl;0;L;<compat> 0058;;;10;N;;;;2179;
+216A;ROMAN NUMERAL ELEVEN;Nl;0;L;<compat> 0058 0049;;;11;N;;;;217A;
+216B;ROMAN NUMERAL TWELVE;Nl;0;L;<compat> 0058 0049 0049;;;12;N;;;;217B;
+216C;ROMAN NUMERAL FIFTY;Nl;0;L;<compat> 004C;;;50;N;;;;217C;
+216D;ROMAN NUMERAL ONE HUNDRED;Nl;0;L;<compat> 0043;;;100;N;;;;217D;
+216E;ROMAN NUMERAL FIVE HUNDRED;Nl;0;L;<compat> 0044;;;500;N;;;;217E;
+216F;ROMAN NUMERAL ONE THOUSAND;Nl;0;L;<compat> 004D;;;1000;N;;;;217F;
+2170;SMALL ROMAN NUMERAL ONE;Nl;0;L;<compat> 0069;;;1;N;;;2160;;2160
+2171;SMALL ROMAN NUMERAL TWO;Nl;0;L;<compat> 0069 0069;;;2;N;;;2161;;2161
+2172;SMALL ROMAN NUMERAL THREE;Nl;0;L;<compat> 0069 0069 0069;;;3;N;;;2162;;2162
+2173;SMALL ROMAN NUMERAL FOUR;Nl;0;L;<compat> 0069 0076;;;4;N;;;2163;;2163
+2174;SMALL ROMAN NUMERAL FIVE;Nl;0;L;<compat> 0076;;;5;N;;;2164;;2164
+2175;SMALL ROMAN NUMERAL SIX;Nl;0;L;<compat> 0076 0069;;;6;N;;;2165;;2165
+2176;SMALL ROMAN NUMERAL SEVEN;Nl;0;L;<compat> 0076 0069 0069;;;7;N;;;2166;;2166
+2177;SMALL ROMAN NUMERAL EIGHT;Nl;0;L;<compat> 0076 0069 0069 0069;;;8;N;;;2167;;2167
+2178;SMALL ROMAN NUMERAL NINE;Nl;0;L;<compat> 0069 0078;;;9;N;;;2168;;2168
+2179;SMALL ROMAN NUMERAL TEN;Nl;0;L;<compat> 0078;;;10;N;;;2169;;2169
+217A;SMALL ROMAN NUMERAL ELEVEN;Nl;0;L;<compat> 0078 0069;;;11;N;;;216A;;216A
+217B;SMALL ROMAN NUMERAL TWELVE;Nl;0;L;<compat> 0078 0069 0069;;;12;N;;;216B;;216B
+217C;SMALL ROMAN NUMERAL FIFTY;Nl;0;L;<compat> 006C;;;50;N;;;216C;;216C
+217D;SMALL ROMAN NUMERAL ONE HUNDRED;Nl;0;L;<compat> 0063;;;100;N;;;216D;;216D
+217E;SMALL ROMAN NUMERAL FIVE HUNDRED;Nl;0;L;<compat> 0064;;;500;N;;;216E;;216E
+217F;SMALL ROMAN NUMERAL ONE THOUSAND;Nl;0;L;<compat> 006D;;;1000;N;;;216F;;216F
+2180;ROMAN NUMERAL ONE THOUSAND C D;Nl;0;L;;;;1000;N;;;;;
+2181;ROMAN NUMERAL FIVE THOUSAND;Nl;0;L;;;;5000;N;;;;;
+2182;ROMAN NUMERAL TEN THOUSAND;Nl;0;L;;;;10000;N;;;;;
+2183;ROMAN NUMERAL REVERSED ONE HUNDRED;Nl;0;L;;;;;N;;;;;
+2190;LEFTWARDS ARROW;Sm;0;ON;;;;;N;LEFT ARROW;;;;
+2191;UPWARDS ARROW;Sm;0;ON;;;;;N;UP ARROW;;;;
+2192;RIGHTWARDS ARROW;Sm;0;ON;;;;;N;RIGHT ARROW;;;;
+2193;DOWNWARDS ARROW;Sm;0;ON;;;;;N;DOWN ARROW;;;;
+2194;LEFT RIGHT ARROW;Sm;0;ON;;;;;N;;;;;
+2195;UP DOWN ARROW;So;0;ON;;;;;N;;;;;
+2196;NORTH WEST ARROW;So;0;ON;;;;;N;UPPER LEFT ARROW;;;;
+2197;NORTH EAST ARROW;So;0;ON;;;;;N;UPPER RIGHT ARROW;;;;
+2198;SOUTH EAST ARROW;So;0;ON;;;;;N;LOWER RIGHT ARROW;;;;
+2199;SOUTH WEST ARROW;So;0;ON;;;;;N;LOWER LEFT ARROW;;;;
+219A;LEFTWARDS ARROW WITH STROKE;Sm;0;ON;2190 0338;;;;N;LEFT ARROW WITH STROKE;;;;
+219B;RIGHTWARDS ARROW WITH STROKE;Sm;0;ON;2192 0338;;;;N;RIGHT ARROW WITH STROKE;;;;
+219C;LEFTWARDS WAVE ARROW;So;0;ON;;;;;N;LEFT WAVE ARROW;;;;
+219D;RIGHTWARDS WAVE ARROW;So;0;ON;;;;;N;RIGHT WAVE ARROW;;;;
+219E;LEFTWARDS TWO HEADED ARROW;So;0;ON;;;;;N;LEFT TWO HEADED ARROW;;;;
+219F;UPWARDS TWO HEADED ARROW;So;0;ON;;;;;N;UP TWO HEADED ARROW;;;;
+21A0;RIGHTWARDS TWO HEADED ARROW;Sm;0;ON;;;;;N;RIGHT TWO HEADED ARROW;;;;
+21A1;DOWNWARDS TWO HEADED ARROW;So;0;ON;;;;;N;DOWN TWO HEADED ARROW;;;;
+21A2;LEFTWARDS ARROW WITH TAIL;So;0;ON;;;;;N;LEFT ARROW WITH TAIL;;;;
+21A3;RIGHTWARDS ARROW WITH TAIL;Sm;0;ON;;;;;N;RIGHT ARROW WITH TAIL;;;;
+21A4;LEFTWARDS ARROW FROM BAR;So;0;ON;;;;;N;LEFT ARROW FROM BAR;;;;
+21A5;UPWARDS ARROW FROM BAR;So;0;ON;;;;;N;UP ARROW FROM BAR;;;;
+21A6;RIGHTWARDS ARROW FROM BAR;Sm;0;ON;;;;;N;RIGHT ARROW FROM BAR;;;;
+21A7;DOWNWARDS ARROW FROM BAR;So;0;ON;;;;;N;DOWN ARROW FROM BAR;;;;
+21A8;UP DOWN ARROW WITH BASE;So;0;ON;;;;;N;;;;;
+21A9;LEFTWARDS ARROW WITH HOOK;So;0;ON;;;;;N;LEFT ARROW WITH HOOK;;;;
+21AA;RIGHTWARDS ARROW WITH HOOK;So;0;ON;;;;;N;RIGHT ARROW WITH HOOK;;;;
+21AB;LEFTWARDS ARROW WITH LOOP;So;0;ON;;;;;N;LEFT ARROW WITH LOOP;;;;
+21AC;RIGHTWARDS ARROW WITH LOOP;So;0;ON;;;;;N;RIGHT ARROW WITH LOOP;;;;
+21AD;LEFT RIGHT WAVE ARROW;So;0;ON;;;;;N;;;;;
+21AE;LEFT RIGHT ARROW WITH STROKE;Sm;0;ON;2194 0338;;;;N;;;;;
+21AF;DOWNWARDS ZIGZAG ARROW;So;0;ON;;;;;N;DOWN ZIGZAG ARROW;;;;
+21B0;UPWARDS ARROW WITH TIP LEFTWARDS;So;0;ON;;;;;N;UP ARROW WITH TIP LEFT;;;;
+21B1;UPWARDS ARROW WITH TIP RIGHTWARDS;So;0;ON;;;;;N;UP ARROW WITH TIP RIGHT;;;;
+21B2;DOWNWARDS ARROW WITH TIP LEFTWARDS;So;0;ON;;;;;N;DOWN ARROW WITH TIP LEFT;;;;
+21B3;DOWNWARDS ARROW WITH TIP RIGHTWARDS;So;0;ON;;;;;N;DOWN ARROW WITH TIP RIGHT;;;;
+21B4;RIGHTWARDS ARROW WITH CORNER DOWNWARDS;So;0;ON;;;;;N;RIGHT ARROW WITH CORNER DOWN;;;;
+21B5;DOWNWARDS ARROW WITH CORNER LEFTWARDS;So;0;ON;;;;;N;DOWN ARROW WITH CORNER LEFT;;;;
+21B6;ANTICLOCKWISE TOP SEMICIRCLE ARROW;So;0;ON;;;;;N;;;;;
+21B7;CLOCKWISE TOP SEMICIRCLE ARROW;So;0;ON;;;;;N;;;;;
+21B8;NORTH WEST ARROW TO LONG BAR;So;0;ON;;;;;N;UPPER LEFT ARROW TO LONG BAR;;;;
+21B9;LEFTWARDS ARROW TO BAR OVER RIGHTWARDS ARROW TO BAR;So;0;ON;;;;;N;LEFT ARROW TO BAR OVER RIGHT ARROW TO BAR;;;;
+21BA;ANTICLOCKWISE OPEN CIRCLE ARROW;So;0;ON;;;;;N;;;;;
+21BB;CLOCKWISE OPEN CIRCLE ARROW;So;0;ON;;;;;N;;;;;
+21BC;LEFTWARDS HARPOON WITH BARB UPWARDS;So;0;ON;;;;;N;LEFT HARPOON WITH BARB UP;;;;
+21BD;LEFTWARDS HARPOON WITH BARB DOWNWARDS;So;0;ON;;;;;N;LEFT HARPOON WITH BARB DOWN;;;;
+21BE;UPWARDS HARPOON WITH BARB RIGHTWARDS;So;0;ON;;;;;N;UP HARPOON WITH BARB RIGHT;;;;
+21BF;UPWARDS HARPOON WITH BARB LEFTWARDS;So;0;ON;;;;;N;UP HARPOON WITH BARB LEFT;;;;
+21C0;RIGHTWARDS HARPOON WITH BARB UPWARDS;So;0;ON;;;;;N;RIGHT HARPOON WITH BARB UP;;;;
+21C1;RIGHTWARDS HARPOON WITH BARB DOWNWARDS;So;0;ON;;;;;N;RIGHT HARPOON WITH BARB DOWN;;;;
+21C2;DOWNWARDS HARPOON WITH BARB RIGHTWARDS;So;0;ON;;;;;N;DOWN HARPOON WITH BARB RIGHT;;;;
+21C3;DOWNWARDS HARPOON WITH BARB LEFTWARDS;So;0;ON;;;;;N;DOWN HARPOON WITH BARB LEFT;;;;
+21C4;RIGHTWARDS ARROW OVER LEFTWARDS ARROW;So;0;ON;;;;;N;RIGHT ARROW OVER LEFT ARROW;;;;
+21C5;UPWARDS ARROW LEFTWARDS OF DOWNWARDS ARROW;So;0;ON;;;;;N;UP ARROW LEFT OF DOWN ARROW;;;;
+21C6;LEFTWARDS ARROW OVER RIGHTWARDS ARROW;So;0;ON;;;;;N;LEFT ARROW OVER RIGHT ARROW;;;;
+21C7;LEFTWARDS PAIRED ARROWS;So;0;ON;;;;;N;LEFT PAIRED ARROWS;;;;
+21C8;UPWARDS PAIRED ARROWS;So;0;ON;;;;;N;UP PAIRED ARROWS;;;;
+21C9;RIGHTWARDS PAIRED ARROWS;So;0;ON;;;;;N;RIGHT PAIRED ARROWS;;;;
+21CA;DOWNWARDS PAIRED ARROWS;So;0;ON;;;;;N;DOWN PAIRED ARROWS;;;;
+21CB;LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON;So;0;ON;;;;;N;LEFT HARPOON OVER RIGHT HARPOON;;;;
+21CC;RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON;So;0;ON;;;;;N;RIGHT HARPOON OVER LEFT HARPOON;;;;
+21CD;LEFTWARDS DOUBLE ARROW WITH STROKE;So;0;ON;21D0 0338;;;;N;LEFT DOUBLE ARROW WITH STROKE;;;;
+21CE;LEFT RIGHT DOUBLE ARROW WITH STROKE;Sm;0;ON;21D4 0338;;;;N;;;;;
+21CF;RIGHTWARDS DOUBLE ARROW WITH STROKE;Sm;0;ON;21D2 0338;;;;N;RIGHT DOUBLE ARROW WITH STROKE;;;;
+21D0;LEFTWARDS DOUBLE ARROW;So;0;ON;;;;;N;LEFT DOUBLE ARROW;;;;
+21D1;UPWARDS DOUBLE ARROW;So;0;ON;;;;;N;UP DOUBLE ARROW;;;;
+21D2;RIGHTWARDS DOUBLE ARROW;Sm;0;ON;;;;;N;RIGHT DOUBLE ARROW;;;;
+21D3;DOWNWARDS DOUBLE ARROW;So;0;ON;;;;;N;DOWN DOUBLE ARROW;;;;
+21D4;LEFT RIGHT DOUBLE ARROW;Sm;0;ON;;;;;N;;;;;
+21D5;UP DOWN DOUBLE ARROW;So;0;ON;;;;;N;;;;;
+21D6;NORTH WEST DOUBLE ARROW;So;0;ON;;;;;N;UPPER LEFT DOUBLE ARROW;;;;
+21D7;NORTH EAST DOUBLE ARROW;So;0;ON;;;;;N;UPPER RIGHT DOUBLE ARROW;;;;
+21D8;SOUTH EAST DOUBLE ARROW;So;0;ON;;;;;N;LOWER RIGHT DOUBLE ARROW;;;;
+21D9;SOUTH WEST DOUBLE ARROW;So;0;ON;;;;;N;LOWER LEFT DOUBLE ARROW;;;;
+21DA;LEFTWARDS TRIPLE ARROW;So;0;ON;;;;;N;LEFT TRIPLE ARROW;;;;
+21DB;RIGHTWARDS TRIPLE ARROW;So;0;ON;;;;;N;RIGHT TRIPLE ARROW;;;;
+21DC;LEFTWARDS SQUIGGLE ARROW;So;0;ON;;;;;N;LEFT SQUIGGLE ARROW;;;;
+21DD;RIGHTWARDS SQUIGGLE ARROW;So;0;ON;;;;;N;RIGHT SQUIGGLE ARROW;;;;
+21DE;UPWARDS ARROW WITH DOUBLE STROKE;So;0;ON;;;;;N;UP ARROW WITH DOUBLE STROKE;;;;
+21DF;DOWNWARDS ARROW WITH DOUBLE STROKE;So;0;ON;;;;;N;DOWN ARROW WITH DOUBLE STROKE;;;;
+21E0;LEFTWARDS DASHED ARROW;So;0;ON;;;;;N;LEFT DASHED ARROW;;;;
+21E1;UPWARDS DASHED ARROW;So;0;ON;;;;;N;UP DASHED ARROW;;;;
+21E2;RIGHTWARDS DASHED ARROW;So;0;ON;;;;;N;RIGHT DASHED ARROW;;;;
+21E3;DOWNWARDS DASHED ARROW;So;0;ON;;;;;N;DOWN DASHED ARROW;;;;
+21E4;LEFTWARDS ARROW TO BAR;So;0;ON;;;;;N;LEFT ARROW TO BAR;;;;
+21E5;RIGHTWARDS ARROW TO BAR;So;0;ON;;;;;N;RIGHT ARROW TO BAR;;;;
+21E6;LEFTWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE LEFT ARROW;;;;
+21E7;UPWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE UP ARROW;;;;
+21E8;RIGHTWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE RIGHT ARROW;;;;
+21E9;DOWNWARDS WHITE ARROW;So;0;ON;;;;;N;WHITE DOWN ARROW;;;;
+21EA;UPWARDS WHITE ARROW FROM BAR;So;0;ON;;;;;N;WHITE UP ARROW FROM BAR;;;;
+21EB;UPWARDS WHITE ARROW ON PEDESTAL;So;0;ON;;;;;N;;;;;
+21EC;UPWARDS WHITE ARROW ON PEDESTAL WITH HORIZONTAL BAR;So;0;ON;;;;;N;;;;;
+21ED;UPWARDS WHITE ARROW ON PEDESTAL WITH VERTICAL BAR;So;0;ON;;;;;N;;;;;
+21EE;UPWARDS WHITE DOUBLE ARROW;So;0;ON;;;;;N;;;;;
+21EF;UPWARDS WHITE DOUBLE ARROW ON PEDESTAL;So;0;ON;;;;;N;;;;;
+21F0;RIGHTWARDS WHITE ARROW FROM WALL;So;0;ON;;;;;N;;;;;
+21F1;NORTH WEST ARROW TO CORNER;So;0;ON;;;;;N;;;;;
+21F2;SOUTH EAST ARROW TO CORNER;So;0;ON;;;;;N;;;;;
+21F3;UP DOWN WHITE ARROW;So;0;ON;;;;;N;;;;;
+21F4;RIGHT ARROW WITH SMALL CIRCLE;Sm;0;ON;;;;;N;;;;;
+21F5;DOWNWARDS ARROW LEFTWARDS OF UPWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+21F6;THREE RIGHTWARDS ARROWS;Sm;0;ON;;;;;N;;;;;
+21F7;LEFTWARDS ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+21F8;RIGHTWARDS ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+21F9;LEFT RIGHT ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+21FA;LEFTWARDS ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+21FB;RIGHTWARDS ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+21FC;LEFT RIGHT ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+21FD;LEFTWARDS OPEN-HEADED ARROW;Sm;0;ON;;;;;N;;;;;
+21FE;RIGHTWARDS OPEN-HEADED ARROW;Sm;0;ON;;;;;N;;;;;
+21FF;LEFT RIGHT OPEN-HEADED ARROW;Sm;0;ON;;;;;N;;;;;
+2200;FOR ALL;Sm;0;ON;;;;;N;;;;;
+2201;COMPLEMENT;Sm;0;ON;;;;;Y;;;;;
+2202;PARTIAL DIFFERENTIAL;Sm;0;ON;;;;;Y;;;;;
+2203;THERE EXISTS;Sm;0;ON;;;;;Y;;;;;
+2204;THERE DOES NOT EXIST;Sm;0;ON;2203 0338;;;;Y;;;;;
+2205;EMPTY SET;Sm;0;ON;;;;;N;;;;;
+2206;INCREMENT;Sm;0;ON;;;;;N;;;;;
+2207;NABLA;Sm;0;ON;;;;;N;;;;;
+2208;ELEMENT OF;Sm;0;ON;;;;;Y;;;;;
+2209;NOT AN ELEMENT OF;Sm;0;ON;2208 0338;;;;Y;;;;;
+220A;SMALL ELEMENT OF;Sm;0;ON;;;;;Y;;;;;
+220B;CONTAINS AS MEMBER;Sm;0;ON;;;;;Y;;;;;
+220C;DOES NOT CONTAIN AS MEMBER;Sm;0;ON;220B 0338;;;;Y;;;;;
+220D;SMALL CONTAINS AS MEMBER;Sm;0;ON;;;;;Y;;;;;
+220E;END OF PROOF;Sm;0;ON;;;;;N;;;;;
+220F;N-ARY PRODUCT;Sm;0;ON;;;;;N;;;;;
+2210;N-ARY COPRODUCT;Sm;0;ON;;;;;N;;;;;
+2211;N-ARY SUMMATION;Sm;0;ON;;;;;Y;;;;;
+2212;MINUS SIGN;Sm;0;ET;;;;;N;;;;;
+2213;MINUS-OR-PLUS SIGN;Sm;0;ET;;;;;N;;;;;
+2214;DOT PLUS;Sm;0;ON;;;;;N;;;;;
+2215;DIVISION SLASH;Sm;0;ON;;;;;Y;;;;;
+2216;SET MINUS;Sm;0;ON;;;;;Y;;;;;
+2217;ASTERISK OPERATOR;Sm;0;ON;;;;;N;;;;;
+2218;RING OPERATOR;Sm;0;ON;;;;;N;;;;;
+2219;BULLET OPERATOR;Sm;0;ON;;;;;N;;;;;
+221A;SQUARE ROOT;Sm;0;ON;;;;;Y;;;;;
+221B;CUBE ROOT;Sm;0;ON;;;;;Y;;;;;
+221C;FOURTH ROOT;Sm;0;ON;;;;;Y;;;;;
+221D;PROPORTIONAL TO;Sm;0;ON;;;;;Y;;;;;
+221E;INFINITY;Sm;0;ON;;;;;N;;;;;
+221F;RIGHT ANGLE;Sm;0;ON;;;;;Y;;;;;
+2220;ANGLE;Sm;0;ON;;;;;Y;;;;;
+2221;MEASURED ANGLE;Sm;0;ON;;;;;Y;;;;;
+2222;SPHERICAL ANGLE;Sm;0;ON;;;;;Y;;;;;
+2223;DIVIDES;Sm;0;ON;;;;;N;;;;;
+2224;DOES NOT DIVIDE;Sm;0;ON;2223 0338;;;;Y;;;;;
+2225;PARALLEL TO;Sm;0;ON;;;;;N;;;;;
+2226;NOT PARALLEL TO;Sm;0;ON;2225 0338;;;;Y;;;;;
+2227;LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+2228;LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+2229;INTERSECTION;Sm;0;ON;;;;;N;;;;;
+222A;UNION;Sm;0;ON;;;;;N;;;;;
+222B;INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+222C;DOUBLE INTEGRAL;Sm;0;ON;<compat> 222B 222B;;;;Y;;;;;
+222D;TRIPLE INTEGRAL;Sm;0;ON;<compat> 222B 222B 222B;;;;Y;;;;;
+222E;CONTOUR INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+222F;SURFACE INTEGRAL;Sm;0;ON;<compat> 222E 222E;;;;Y;;;;;
+2230;VOLUME INTEGRAL;Sm;0;ON;<compat> 222E 222E 222E;;;;Y;;;;;
+2231;CLOCKWISE INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2232;CLOCKWISE CONTOUR INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2233;ANTICLOCKWISE CONTOUR INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2234;THEREFORE;Sm;0;ON;;;;;N;;;;;
+2235;BECAUSE;Sm;0;ON;;;;;N;;;;;
+2236;RATIO;Sm;0;ON;;;;;N;;;;;
+2237;PROPORTION;Sm;0;ON;;;;;N;;;;;
+2238;DOT MINUS;Sm;0;ON;;;;;N;;;;;
+2239;EXCESS;Sm;0;ON;;;;;Y;;;;;
+223A;GEOMETRIC PROPORTION;Sm;0;ON;;;;;N;;;;;
+223B;HOMOTHETIC;Sm;0;ON;;;;;Y;;;;;
+223C;TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;;
+223D;REVERSED TILDE;Sm;0;ON;;;;;Y;;lazy S;;;
+223E;INVERTED LAZY S;Sm;0;ON;;;;;Y;;;;;
+223F;SINE WAVE;Sm;0;ON;;;;;Y;;;;;
+2240;WREATH PRODUCT;Sm;0;ON;;;;;Y;;;;;
+2241;NOT TILDE;Sm;0;ON;223C 0338;;;;Y;;;;;
+2242;MINUS TILDE;Sm;0;ON;;;;;Y;;;;;
+2243;ASYMPTOTICALLY EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2244;NOT ASYMPTOTICALLY EQUAL TO;Sm;0;ON;2243 0338;;;;Y;;;;;
+2245;APPROXIMATELY EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2246;APPROXIMATELY BUT NOT ACTUALLY EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2247;NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO;Sm;0;ON;2245 0338;;;;Y;;;;;
+2248;ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2249;NOT ALMOST EQUAL TO;Sm;0;ON;2248 0338;;;;Y;;;;;
+224A;ALMOST EQUAL OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+224B;TRIPLE TILDE;Sm;0;ON;;;;;Y;;;;;
+224C;ALL EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+224D;EQUIVALENT TO;Sm;0;ON;;;;;N;;;;;
+224E;GEOMETRICALLY EQUIVALENT TO;Sm;0;ON;;;;;N;;;;;
+224F;DIFFERENCE BETWEEN;Sm;0;ON;;;;;N;;;;;
+2250;APPROACHES THE LIMIT;Sm;0;ON;;;;;N;;;;;
+2251;GEOMETRICALLY EQUAL TO;Sm;0;ON;;;;;N;;;;;
+2252;APPROXIMATELY EQUAL TO OR THE IMAGE OF;Sm;0;ON;;;;;Y;;;;;
+2253;IMAGE OF OR APPROXIMATELY EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2254;COLON EQUALS;Sm;0;ON;;;;;Y;COLON EQUAL;;;;
+2255;EQUALS COLON;Sm;0;ON;;;;;Y;EQUAL COLON;;;;
+2256;RING IN EQUAL TO;Sm;0;ON;;;;;N;;;;;
+2257;RING EQUAL TO;Sm;0;ON;;;;;N;;;;;
+2258;CORRESPONDS TO;Sm;0;ON;;;;;N;;;;;
+2259;ESTIMATES;Sm;0;ON;;;;;N;;;;;
+225A;EQUIANGULAR TO;Sm;0;ON;;;;;N;;;;;
+225B;STAR EQUALS;Sm;0;ON;;;;;N;;;;;
+225C;DELTA EQUAL TO;Sm;0;ON;;;;;N;;;;;
+225D;EQUAL TO BY DEFINITION;Sm;0;ON;;;;;N;;;;;
+225E;MEASURED BY;Sm;0;ON;;;;;N;;;;;
+225F;QUESTIONED EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2260;NOT EQUAL TO;Sm;0;ON;003D 0338;;;;Y;;;;;
+2261;IDENTICAL TO;Sm;0;ON;;;;;N;;;;;
+2262;NOT IDENTICAL TO;Sm;0;ON;2261 0338;;;;Y;;;;;
+2263;STRICTLY EQUIVALENT TO;Sm;0;ON;;;;;N;;;;;
+2264;LESS-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN OR EQUAL TO;;;;
+2265;GREATER-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN OR EQUAL TO;;;;
+2266;LESS-THAN OVER EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN OVER EQUAL TO;;;;
+2267;GREATER-THAN OVER EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN OVER EQUAL TO;;;;
+2268;LESS-THAN BUT NOT EQUAL TO;Sm;0;ON;;;;;Y;LESS THAN BUT NOT EQUAL TO;;;;
+2269;GREATER-THAN BUT NOT EQUAL TO;Sm;0;ON;;;;;Y;GREATER THAN BUT NOT EQUAL TO;;;;
+226A;MUCH LESS-THAN;Sm;0;ON;;;;;Y;MUCH LESS THAN;;;;
+226B;MUCH GREATER-THAN;Sm;0;ON;;;;;Y;MUCH GREATER THAN;;;;
+226C;BETWEEN;Sm;0;ON;;;;;N;;;;;
+226D;NOT EQUIVALENT TO;Sm;0;ON;224D 0338;;;;N;;;;;
+226E;NOT LESS-THAN;Sm;0;ON;003C 0338;;;;Y;NOT LESS THAN;;;;
+226F;NOT GREATER-THAN;Sm;0;ON;003E 0338;;;;Y;NOT GREATER THAN;;;;
+2270;NEITHER LESS-THAN NOR EQUAL TO;Sm;0;ON;2264 0338;;;;Y;NEITHER LESS THAN NOR EQUAL TO;;;;
+2271;NEITHER GREATER-THAN NOR EQUAL TO;Sm;0;ON;2265 0338;;;;Y;NEITHER GREATER THAN NOR EQUAL TO;;;;
+2272;LESS-THAN OR EQUIVALENT TO;Sm;0;ON;;;;;Y;LESS THAN OR EQUIVALENT TO;;;;
+2273;GREATER-THAN OR EQUIVALENT TO;Sm;0;ON;;;;;Y;GREATER THAN OR EQUIVALENT TO;;;;
+2274;NEITHER LESS-THAN NOR EQUIVALENT TO;Sm;0;ON;2272 0338;;;;Y;NEITHER LESS THAN NOR EQUIVALENT TO;;;;
+2275;NEITHER GREATER-THAN NOR EQUIVALENT TO;Sm;0;ON;2273 0338;;;;Y;NEITHER GREATER THAN NOR EQUIVALENT TO;;;;
+2276;LESS-THAN OR GREATER-THAN;Sm;0;ON;;;;;Y;LESS THAN OR GREATER THAN;;;;
+2277;GREATER-THAN OR LESS-THAN;Sm;0;ON;;;;;Y;GREATER THAN OR LESS THAN;;;;
+2278;NEITHER LESS-THAN NOR GREATER-THAN;Sm;0;ON;2276 0338;;;;Y;NEITHER LESS THAN NOR GREATER THAN;;;;
+2279;NEITHER GREATER-THAN NOR LESS-THAN;Sm;0;ON;2277 0338;;;;Y;NEITHER GREATER THAN NOR LESS THAN;;;;
+227A;PRECEDES;Sm;0;ON;;;;;Y;;;;;
+227B;SUCCEEDS;Sm;0;ON;;;;;Y;;;;;
+227C;PRECEDES OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+227D;SUCCEEDS OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+227E;PRECEDES OR EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;;
+227F;SUCCEEDS OR EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;;
+2280;DOES NOT PRECEDE;Sm;0;ON;227A 0338;;;;Y;;;;;
+2281;DOES NOT SUCCEED;Sm;0;ON;227B 0338;;;;Y;;;;;
+2282;SUBSET OF;Sm;0;ON;;;;;Y;;;;;
+2283;SUPERSET OF;Sm;0;ON;;;;;Y;;;;;
+2284;NOT A SUBSET OF;Sm;0;ON;2282 0338;;;;Y;;;;;
+2285;NOT A SUPERSET OF;Sm;0;ON;2283 0338;;;;Y;;;;;
+2286;SUBSET OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2287;SUPERSET OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2288;NEITHER A SUBSET OF NOR EQUAL TO;Sm;0;ON;2286 0338;;;;Y;;;;;
+2289;NEITHER A SUPERSET OF NOR EQUAL TO;Sm;0;ON;2287 0338;;;;Y;;;;;
+228A;SUBSET OF WITH NOT EQUAL TO;Sm;0;ON;;;;;Y;SUBSET OF OR NOT EQUAL TO;;;;
+228B;SUPERSET OF WITH NOT EQUAL TO;Sm;0;ON;;;;;Y;SUPERSET OF OR NOT EQUAL TO;;;;
+228C;MULTISET;Sm;0;ON;;;;;Y;;;;;
+228D;MULTISET MULTIPLICATION;Sm;0;ON;;;;;N;;;;;
+228E;MULTISET UNION;Sm;0;ON;;;;;N;;;;;
+228F;SQUARE IMAGE OF;Sm;0;ON;;;;;Y;;;;;
+2290;SQUARE ORIGINAL OF;Sm;0;ON;;;;;Y;;;;;
+2291;SQUARE IMAGE OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2292;SQUARE ORIGINAL OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2293;SQUARE CAP;Sm;0;ON;;;;;N;;;;;
+2294;SQUARE CUP;Sm;0;ON;;;;;N;;;;;
+2295;CIRCLED PLUS;Sm;0;ON;;;;;N;;;;;
+2296;CIRCLED MINUS;Sm;0;ON;;;;;N;;;;;
+2297;CIRCLED TIMES;Sm;0;ON;;;;;N;;;;;
+2298;CIRCLED DIVISION SLASH;Sm;0;ON;;;;;Y;;;;;
+2299;CIRCLED DOT OPERATOR;Sm;0;ON;;;;;N;;;;;
+229A;CIRCLED RING OPERATOR;Sm;0;ON;;;;;N;;;;;
+229B;CIRCLED ASTERISK OPERATOR;Sm;0;ON;;;;;N;;;;;
+229C;CIRCLED EQUALS;Sm;0;ON;;;;;N;;;;;
+229D;CIRCLED DASH;Sm;0;ON;;;;;N;;;;;
+229E;SQUARED PLUS;Sm;0;ON;;;;;N;;;;;
+229F;SQUARED MINUS;Sm;0;ON;;;;;N;;;;;
+22A0;SQUARED TIMES;Sm;0;ON;;;;;N;;;;;
+22A1;SQUARED DOT OPERATOR;Sm;0;ON;;;;;N;;;;;
+22A2;RIGHT TACK;Sm;0;ON;;;;;Y;;;;;
+22A3;LEFT TACK;Sm;0;ON;;;;;Y;;;;;
+22A4;DOWN TACK;Sm;0;ON;;;;;N;;;;;
+22A5;UP TACK;Sm;0;ON;;;;;N;;;;;
+22A6;ASSERTION;Sm;0;ON;;;;;Y;;;;;
+22A7;MODELS;Sm;0;ON;;;;;Y;;;;;
+22A8;TRUE;Sm;0;ON;;;;;Y;;;;;
+22A9;FORCES;Sm;0;ON;;;;;Y;;;;;
+22AA;TRIPLE VERTICAL BAR RIGHT TURNSTILE;Sm;0;ON;;;;;Y;;;;;
+22AB;DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE;Sm;0;ON;;;;;Y;;;;;
+22AC;DOES NOT PROVE;Sm;0;ON;22A2 0338;;;;Y;;;;;
+22AD;NOT TRUE;Sm;0;ON;22A8 0338;;;;Y;;;;;
+22AE;DOES NOT FORCE;Sm;0;ON;22A9 0338;;;;Y;;;;;
+22AF;NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE;Sm;0;ON;22AB 0338;;;;Y;;;;;
+22B0;PRECEDES UNDER RELATION;Sm;0;ON;;;;;Y;;;;;
+22B1;SUCCEEDS UNDER RELATION;Sm;0;ON;;;;;Y;;;;;
+22B2;NORMAL SUBGROUP OF;Sm;0;ON;;;;;Y;;;;;
+22B3;CONTAINS AS NORMAL SUBGROUP;Sm;0;ON;;;;;Y;;;;;
+22B4;NORMAL SUBGROUP OF OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+22B5;CONTAINS AS NORMAL SUBGROUP OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+22B6;ORIGINAL OF;Sm;0;ON;;;;;Y;;;;;
+22B7;IMAGE OF;Sm;0;ON;;;;;Y;;;;;
+22B8;MULTIMAP;Sm;0;ON;;;;;Y;;;;;
+22B9;HERMITIAN CONJUGATE MATRIX;Sm;0;ON;;;;;N;;;;;
+22BA;INTERCALATE;Sm;0;ON;;;;;N;;;;;
+22BB;XOR;Sm;0;ON;;;;;N;;;;;
+22BC;NAND;Sm;0;ON;;;;;N;;;;;
+22BD;NOR;Sm;0;ON;;;;;N;;;;;
+22BE;RIGHT ANGLE WITH ARC;Sm;0;ON;;;;;Y;;;;;
+22BF;RIGHT TRIANGLE;Sm;0;ON;;;;;Y;;;;;
+22C0;N-ARY LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+22C1;N-ARY LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+22C2;N-ARY INTERSECTION;Sm;0;ON;;;;;N;;;;;
+22C3;N-ARY UNION;Sm;0;ON;;;;;N;;;;;
+22C4;DIAMOND OPERATOR;Sm;0;ON;;;;;N;;;;;
+22C5;DOT OPERATOR;Sm;0;ON;;;;;N;;;;;
+22C6;STAR OPERATOR;Sm;0;ON;;;;;N;;;;;
+22C7;DIVISION TIMES;Sm;0;ON;;;;;N;;;;;
+22C8;BOWTIE;Sm;0;ON;;;;;N;;;;;
+22C9;LEFT NORMAL FACTOR SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;;
+22CA;RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;;
+22CB;LEFT SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;;
+22CC;RIGHT SEMIDIRECT PRODUCT;Sm;0;ON;;;;;Y;;;;;
+22CD;REVERSED TILDE EQUALS;Sm;0;ON;;;;;Y;;;;;
+22CE;CURLY LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+22CF;CURLY LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+22D0;DOUBLE SUBSET;Sm;0;ON;;;;;Y;;;;;
+22D1;DOUBLE SUPERSET;Sm;0;ON;;;;;Y;;;;;
+22D2;DOUBLE INTERSECTION;Sm;0;ON;;;;;N;;;;;
+22D3;DOUBLE UNION;Sm;0;ON;;;;;N;;;;;
+22D4;PITCHFORK;Sm;0;ON;;;;;N;;;;;
+22D5;EQUAL AND PARALLEL TO;Sm;0;ON;;;;;N;;;;;
+22D6;LESS-THAN WITH DOT;Sm;0;ON;;;;;Y;LESS THAN WITH DOT;;;;
+22D7;GREATER-THAN WITH DOT;Sm;0;ON;;;;;Y;GREATER THAN WITH DOT;;;;
+22D8;VERY MUCH LESS-THAN;Sm;0;ON;;;;;Y;VERY MUCH LESS THAN;;;;
+22D9;VERY MUCH GREATER-THAN;Sm;0;ON;;;;;Y;VERY MUCH GREATER THAN;;;;
+22DA;LESS-THAN EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;LESS THAN EQUAL TO OR GREATER THAN;;;;
+22DB;GREATER-THAN EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;GREATER THAN EQUAL TO OR LESS THAN;;;;
+22DC;EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;EQUAL TO OR LESS THAN;;;;
+22DD;EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;EQUAL TO OR GREATER THAN;;;;
+22DE;EQUAL TO OR PRECEDES;Sm;0;ON;;;;;Y;;;;;
+22DF;EQUAL TO OR SUCCEEDS;Sm;0;ON;;;;;Y;;;;;
+22E0;DOES NOT PRECEDE OR EQUAL;Sm;0;ON;227C 0338;;;;Y;;;;;
+22E1;DOES NOT SUCCEED OR EQUAL;Sm;0;ON;227D 0338;;;;Y;;;;;
+22E2;NOT SQUARE IMAGE OF OR EQUAL TO;Sm;0;ON;2291 0338;;;;Y;;;;;
+22E3;NOT SQUARE ORIGINAL OF OR EQUAL TO;Sm;0;ON;2292 0338;;;;Y;;;;;
+22E4;SQUARE IMAGE OF OR NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+22E5;SQUARE ORIGINAL OF OR NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+22E6;LESS-THAN BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;LESS THAN BUT NOT EQUIVALENT TO;;;;
+22E7;GREATER-THAN BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;GREATER THAN BUT NOT EQUIVALENT TO;;;;
+22E8;PRECEDES BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;;
+22E9;SUCCEEDS BUT NOT EQUIVALENT TO;Sm;0;ON;;;;;Y;;;;;
+22EA;NOT NORMAL SUBGROUP OF;Sm;0;ON;22B2 0338;;;;Y;;;;;
+22EB;DOES NOT CONTAIN AS NORMAL SUBGROUP;Sm;0;ON;22B3 0338;;;;Y;;;;;
+22EC;NOT NORMAL SUBGROUP OF OR EQUAL TO;Sm;0;ON;22B4 0338;;;;Y;;;;;
+22ED;DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL;Sm;0;ON;22B5 0338;;;;Y;;;;;
+22EE;VERTICAL ELLIPSIS;Sm;0;ON;;;;;N;;;;;
+22EF;MIDLINE HORIZONTAL ELLIPSIS;Sm;0;ON;;;;;N;;;;;
+22F0;UP RIGHT DIAGONAL ELLIPSIS;Sm;0;ON;;;;;Y;;;;;
+22F1;DOWN RIGHT DIAGONAL ELLIPSIS;Sm;0;ON;;;;;Y;;;;;
+22F2;ELEMENT OF WITH LONG HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;;
+22F3;ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;;
+22F4;SMALL ELEMENT OF WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;;
+22F5;ELEMENT OF WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;;
+22F6;ELEMENT OF WITH OVERBAR;Sm;0;ON;;;;;Y;;;;;
+22F7;SMALL ELEMENT OF WITH OVERBAR;Sm;0;ON;;;;;Y;;;;;
+22F8;ELEMENT OF WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;;
+22F9;ELEMENT OF WITH TWO HORIZONTAL STROKES;Sm;0;ON;;;;;Y;;;;;
+22FA;CONTAINS WITH LONG HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;;
+22FB;CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;;
+22FC;SMALL CONTAINS WITH VERTICAL BAR AT END OF HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;;
+22FD;CONTAINS WITH OVERBAR;Sm;0;ON;;;;;Y;;;;;
+22FE;SMALL CONTAINS WITH OVERBAR;Sm;0;ON;;;;;Y;;;;;
+22FF;Z NOTATION BAG MEMBERSHIP;Sm;0;ON;;;;;Y;;;;;
+2300;DIAMETER SIGN;So;0;ON;;;;;N;;;;;
+2301;ELECTRIC ARROW;So;0;ON;;;;;N;;;;;
+2302;HOUSE;So;0;ON;;;;;N;;;;;
+2303;UP ARROWHEAD;So;0;ON;;;;;N;;;;;
+2304;DOWN ARROWHEAD;So;0;ON;;;;;N;;;;;
+2305;PROJECTIVE;So;0;ON;;;;;N;;;;;
+2306;PERSPECTIVE;So;0;ON;;;;;N;;;;;
+2307;WAVY LINE;So;0;ON;;;;;N;;;;;
+2308;LEFT CEILING;Sm;0;ON;;;;;Y;;;;;
+2309;RIGHT CEILING;Sm;0;ON;;;;;Y;;;;;
+230A;LEFT FLOOR;Sm;0;ON;;;;;Y;;;;;
+230B;RIGHT FLOOR;Sm;0;ON;;;;;Y;;;;;
+230C;BOTTOM RIGHT CROP;So;0;ON;;;;;N;;;;;
+230D;BOTTOM LEFT CROP;So;0;ON;;;;;N;;;;;
+230E;TOP RIGHT CROP;So;0;ON;;;;;N;;;;;
+230F;TOP LEFT CROP;So;0;ON;;;;;N;;;;;
+2310;REVERSED NOT SIGN;So;0;ON;;;;;N;;;;;
+2311;SQUARE LOZENGE;So;0;ON;;;;;N;;;;;
+2312;ARC;So;0;ON;;;;;N;;;;;
+2313;SEGMENT;So;0;ON;;;;;N;;;;;
+2314;SECTOR;So;0;ON;;;;;N;;;;;
+2315;TELEPHONE RECORDER;So;0;ON;;;;;N;;;;;
+2316;POSITION INDICATOR;So;0;ON;;;;;N;;;;;
+2317;VIEWDATA SQUARE;So;0;ON;;;;;N;;;;;
+2318;PLACE OF INTEREST SIGN;So;0;ON;;;;;N;COMMAND KEY;;;;
+2319;TURNED NOT SIGN;So;0;ON;;;;;N;;;;;
+231A;WATCH;So;0;ON;;;;;N;;;;;
+231B;HOURGLASS;So;0;ON;;;;;N;;;;;
+231C;TOP LEFT CORNER;So;0;ON;;;;;N;;;;;
+231D;TOP RIGHT CORNER;So;0;ON;;;;;N;;;;;
+231E;BOTTOM LEFT CORNER;So;0;ON;;;;;N;;;;;
+231F;BOTTOM RIGHT CORNER;So;0;ON;;;;;N;;;;;
+2320;TOP HALF INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2321;BOTTOM HALF INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2322;FROWN;So;0;ON;;;;;N;;;;;
+2323;SMILE;So;0;ON;;;;;N;;;;;
+2324;UP ARROWHEAD BETWEEN TWO HORIZONTAL BARS;So;0;ON;;;;;N;ENTER KEY;;;;
+2325;OPTION KEY;So;0;ON;;;;;N;;;;;
+2326;ERASE TO THE RIGHT;So;0;ON;;;;;N;DELETE TO THE RIGHT KEY;;;;
+2327;X IN A RECTANGLE BOX;So;0;ON;;;;;N;CLEAR KEY;;;;
+2328;KEYBOARD;So;0;ON;;;;;N;;;;;
+2329;LEFT-POINTING ANGLE BRACKET;Ps;0;ON;3008;;;;Y;BRA;;;;
+232A;RIGHT-POINTING ANGLE BRACKET;Pe;0;ON;3009;;;;Y;KET;;;;
+232B;ERASE TO THE LEFT;So;0;ON;;;;;N;DELETE TO THE LEFT KEY;;;;
+232C;BENZENE RING;So;0;ON;;;;;N;;;;;
+232D;CYLINDRICITY;So;0;ON;;;;;N;;;;;
+232E;ALL AROUND-PROFILE;So;0;ON;;;;;N;;;;;
+232F;SYMMETRY;So;0;ON;;;;;N;;;;;
+2330;TOTAL RUNOUT;So;0;ON;;;;;N;;;;;
+2331;DIMENSION ORIGIN;So;0;ON;;;;;N;;;;;
+2332;CONICAL TAPER;So;0;ON;;;;;N;;;;;
+2333;SLOPE;So;0;ON;;;;;N;;;;;
+2334;COUNTERBORE;So;0;ON;;;;;N;;;;;
+2335;COUNTERSINK;So;0;ON;;;;;N;;;;;
+2336;APL FUNCTIONAL SYMBOL I-BEAM;So;0;L;;;;;N;;;;;
+2337;APL FUNCTIONAL SYMBOL SQUISH QUAD;So;0;L;;;;;N;;;;;
+2338;APL FUNCTIONAL SYMBOL QUAD EQUAL;So;0;L;;;;;N;;;;;
+2339;APL FUNCTIONAL SYMBOL QUAD DIVIDE;So;0;L;;;;;N;;;;;
+233A;APL FUNCTIONAL SYMBOL QUAD DIAMOND;So;0;L;;;;;N;;;;;
+233B;APL FUNCTIONAL SYMBOL QUAD JOT;So;0;L;;;;;N;;;;;
+233C;APL FUNCTIONAL SYMBOL QUAD CIRCLE;So;0;L;;;;;N;;;;;
+233D;APL FUNCTIONAL SYMBOL CIRCLE STILE;So;0;L;;;;;N;;;;;
+233E;APL FUNCTIONAL SYMBOL CIRCLE JOT;So;0;L;;;;;N;;;;;
+233F;APL FUNCTIONAL SYMBOL SLASH BAR;So;0;L;;;;;N;;;;;
+2340;APL FUNCTIONAL SYMBOL BACKSLASH BAR;So;0;L;;;;;N;;;;;
+2341;APL FUNCTIONAL SYMBOL QUAD SLASH;So;0;L;;;;;N;;;;;
+2342;APL FUNCTIONAL SYMBOL QUAD BACKSLASH;So;0;L;;;;;N;;;;;
+2343;APL FUNCTIONAL SYMBOL QUAD LESS-THAN;So;0;L;;;;;N;;;;;
+2344;APL FUNCTIONAL SYMBOL QUAD GREATER-THAN;So;0;L;;;;;N;;;;;
+2345;APL FUNCTIONAL SYMBOL LEFTWARDS VANE;So;0;L;;;;;N;;;;;
+2346;APL FUNCTIONAL SYMBOL RIGHTWARDS VANE;So;0;L;;;;;N;;;;;
+2347;APL FUNCTIONAL SYMBOL QUAD LEFTWARDS ARROW;So;0;L;;;;;N;;;;;
+2348;APL FUNCTIONAL SYMBOL QUAD RIGHTWARDS ARROW;So;0;L;;;;;N;;;;;
+2349;APL FUNCTIONAL SYMBOL CIRCLE BACKSLASH;So;0;L;;;;;N;;;;;
+234A;APL FUNCTIONAL SYMBOL DOWN TACK UNDERBAR;So;0;L;;;;;N;;*;;;
+234B;APL FUNCTIONAL SYMBOL DELTA STILE;So;0;L;;;;;N;;;;;
+234C;APL FUNCTIONAL SYMBOL QUAD DOWN CARET;So;0;L;;;;;N;;;;;
+234D;APL FUNCTIONAL SYMBOL QUAD DELTA;So;0;L;;;;;N;;;;;
+234E;APL FUNCTIONAL SYMBOL DOWN TACK JOT;So;0;L;;;;;N;;*;;;
+234F;APL FUNCTIONAL SYMBOL UPWARDS VANE;So;0;L;;;;;N;;;;;
+2350;APL FUNCTIONAL SYMBOL QUAD UPWARDS ARROW;So;0;L;;;;;N;;;;;
+2351;APL FUNCTIONAL SYMBOL UP TACK OVERBAR;So;0;L;;;;;N;;*;;;
+2352;APL FUNCTIONAL SYMBOL DEL STILE;So;0;L;;;;;N;;;;;
+2353;APL FUNCTIONAL SYMBOL QUAD UP CARET;So;0;L;;;;;N;;;;;
+2354;APL FUNCTIONAL SYMBOL QUAD DEL;So;0;L;;;;;N;;;;;
+2355;APL FUNCTIONAL SYMBOL UP TACK JOT;So;0;L;;;;;N;;*;;;
+2356;APL FUNCTIONAL SYMBOL DOWNWARDS VANE;So;0;L;;;;;N;;;;;
+2357;APL FUNCTIONAL SYMBOL QUAD DOWNWARDS ARROW;So;0;L;;;;;N;;;;;
+2358;APL FUNCTIONAL SYMBOL QUOTE UNDERBAR;So;0;L;;;;;N;;;;;
+2359;APL FUNCTIONAL SYMBOL DELTA UNDERBAR;So;0;L;;;;;N;;;;;
+235A;APL FUNCTIONAL SYMBOL DIAMOND UNDERBAR;So;0;L;;;;;N;;;;;
+235B;APL FUNCTIONAL SYMBOL JOT UNDERBAR;So;0;L;;;;;N;;;;;
+235C;APL FUNCTIONAL SYMBOL CIRCLE UNDERBAR;So;0;L;;;;;N;;;;;
+235D;APL FUNCTIONAL SYMBOL UP SHOE JOT;So;0;L;;;;;N;;;;;
+235E;APL FUNCTIONAL SYMBOL QUOTE QUAD;So;0;L;;;;;N;;;;;
+235F;APL FUNCTIONAL SYMBOL CIRCLE STAR;So;0;L;;;;;N;;;;;
+2360;APL FUNCTIONAL SYMBOL QUAD COLON;So;0;L;;;;;N;;;;;
+2361;APL FUNCTIONAL SYMBOL UP TACK DIAERESIS;So;0;L;;;;;N;;*;;;
+2362;APL FUNCTIONAL SYMBOL DEL DIAERESIS;So;0;L;;;;;N;;;;;
+2363;APL FUNCTIONAL SYMBOL STAR DIAERESIS;So;0;L;;;;;N;;;;;
+2364;APL FUNCTIONAL SYMBOL JOT DIAERESIS;So;0;L;;;;;N;;;;;
+2365;APL FUNCTIONAL SYMBOL CIRCLE DIAERESIS;So;0;L;;;;;N;;;;;
+2366;APL FUNCTIONAL SYMBOL DOWN SHOE STILE;So;0;L;;;;;N;;;;;
+2367;APL FUNCTIONAL SYMBOL LEFT SHOE STILE;So;0;L;;;;;N;;;;;
+2368;APL FUNCTIONAL SYMBOL TILDE DIAERESIS;So;0;L;;;;;N;;;;;
+2369;APL FUNCTIONAL SYMBOL GREATER-THAN DIAERESIS;So;0;L;;;;;N;;;;;
+236A;APL FUNCTIONAL SYMBOL COMMA BAR;So;0;L;;;;;N;;;;;
+236B;APL FUNCTIONAL SYMBOL DEL TILDE;So;0;L;;;;;N;;;;;
+236C;APL FUNCTIONAL SYMBOL ZILDE;So;0;L;;;;;N;;;;;
+236D;APL FUNCTIONAL SYMBOL STILE TILDE;So;0;L;;;;;N;;;;;
+236E;APL FUNCTIONAL SYMBOL SEMICOLON UNDERBAR;So;0;L;;;;;N;;;;;
+236F;APL FUNCTIONAL SYMBOL QUAD NOT EQUAL;So;0;L;;;;;N;;;;;
+2370;APL FUNCTIONAL SYMBOL QUAD QUESTION;So;0;L;;;;;N;;;;;
+2371;APL FUNCTIONAL SYMBOL DOWN CARET TILDE;So;0;L;;;;;N;;;;;
+2372;APL FUNCTIONAL SYMBOL UP CARET TILDE;So;0;L;;;;;N;;;;;
+2373;APL FUNCTIONAL SYMBOL IOTA;So;0;L;;;;;N;;;;;
+2374;APL FUNCTIONAL SYMBOL RHO;So;0;L;;;;;N;;;;;
+2375;APL FUNCTIONAL SYMBOL OMEGA;So;0;L;;;;;N;;;;;
+2376;APL FUNCTIONAL SYMBOL ALPHA UNDERBAR;So;0;L;;;;;N;;;;;
+2377;APL FUNCTIONAL SYMBOL EPSILON UNDERBAR;So;0;L;;;;;N;;;;;
+2378;APL FUNCTIONAL SYMBOL IOTA UNDERBAR;So;0;L;;;;;N;;;;;
+2379;APL FUNCTIONAL SYMBOL OMEGA UNDERBAR;So;0;L;;;;;N;;;;;
+237A;APL FUNCTIONAL SYMBOL ALPHA;So;0;L;;;;;N;;;;;
+237B;NOT CHECK MARK;So;0;ON;;;;;N;;;;;
+237C;RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW;Sm;0;ON;;;;;N;;;;;
+237D;SHOULDERED OPEN BOX;So;0;ON;;;;;N;;;;;
+237E;BELL SYMBOL;So;0;ON;;;;;N;;;;;
+237F;VERTICAL LINE WITH MIDDLE DOT;So;0;ON;;;;;N;;;;;
+2380;INSERTION SYMBOL;So;0;ON;;;;;N;;;;;
+2381;CONTINUOUS UNDERLINE SYMBOL;So;0;ON;;;;;N;;;;;
+2382;DISCONTINUOUS UNDERLINE SYMBOL;So;0;ON;;;;;N;;;;;
+2383;EMPHASIS SYMBOL;So;0;ON;;;;;N;;;;;
+2384;COMPOSITION SYMBOL;So;0;ON;;;;;N;;;;;
+2385;WHITE SQUARE WITH CENTRE VERTICAL LINE;So;0;ON;;;;;N;;;;;
+2386;ENTER SYMBOL;So;0;ON;;;;;N;;;;;
+2387;ALTERNATIVE KEY SYMBOL;So;0;ON;;;;;N;;;;;
+2388;HELM SYMBOL;So;0;ON;;;;;N;;;;;
+2389;CIRCLED HORIZONTAL BAR WITH NOTCH;So;0;ON;;;;;N;;pause;;;
+238A;CIRCLED TRIANGLE DOWN;So;0;ON;;;;;N;;break;;;
+238B;BROKEN CIRCLE WITH NORTHWEST ARROW;So;0;ON;;;;;N;;escape;;;
+238C;UNDO SYMBOL;So;0;ON;;;;;N;;;;;
+238D;MONOSTABLE SYMBOL;So;0;ON;;;;;N;;;;;
+238E;HYSTERESIS SYMBOL;So;0;ON;;;;;N;;;;;
+238F;OPEN-CIRCUIT-OUTPUT H-TYPE SYMBOL;So;0;ON;;;;;N;;;;;
+2390;OPEN-CIRCUIT-OUTPUT L-TYPE SYMBOL;So;0;ON;;;;;N;;;;;
+2391;PASSIVE-PULL-DOWN-OUTPUT SYMBOL;So;0;ON;;;;;N;;;;;
+2392;PASSIVE-PULL-UP-OUTPUT SYMBOL;So;0;ON;;;;;N;;;;;
+2393;DIRECT CURRENT SYMBOL FORM TWO;So;0;ON;;;;;N;;;;;
+2394;SOFTWARE-FUNCTION SYMBOL;So;0;ON;;;;;N;;;;;
+2395;APL FUNCTIONAL SYMBOL QUAD;So;0;L;;;;;N;;;;;
+2396;DECIMAL SEPARATOR KEY SYMBOL;So;0;ON;;;;;N;;;;;
+2397;PREVIOUS PAGE;So;0;ON;;;;;N;;;;;
+2398;NEXT PAGE;So;0;ON;;;;;N;;;;;
+2399;PRINT SCREEN SYMBOL;So;0;ON;;;;;N;;;;;
+239A;CLEAR SCREEN SYMBOL;So;0;ON;;;;;N;;;;;
+239B;LEFT PARENTHESIS UPPER HOOK;Sm;0;ON;;;;;N;;;;;
+239C;LEFT PARENTHESIS EXTENSION;Sm;0;ON;;;;;N;;;;;
+239D;LEFT PARENTHESIS LOWER HOOK;Sm;0;ON;;;;;N;;;;;
+239E;RIGHT PARENTHESIS UPPER HOOK;Sm;0;ON;;;;;N;;;;;
+239F;RIGHT PARENTHESIS EXTENSION;Sm;0;ON;;;;;N;;;;;
+23A0;RIGHT PARENTHESIS LOWER HOOK;Sm;0;ON;;;;;N;;;;;
+23A1;LEFT SQUARE BRACKET UPPER CORNER;Sm;0;ON;;;;;N;;;;;
+23A2;LEFT SQUARE BRACKET EXTENSION;Sm;0;ON;;;;;N;;;;;
+23A3;LEFT SQUARE BRACKET LOWER CORNER;Sm;0;ON;;;;;N;;;;;
+23A4;RIGHT SQUARE BRACKET UPPER CORNER;Sm;0;ON;;;;;N;;;;;
+23A5;RIGHT SQUARE BRACKET EXTENSION;Sm;0;ON;;;;;N;;;;;
+23A6;RIGHT SQUARE BRACKET LOWER CORNER;Sm;0;ON;;;;;N;;;;;
+23A7;LEFT CURLY BRACKET UPPER HOOK;Sm;0;ON;;;;;N;;;;;
+23A8;LEFT CURLY BRACKET MIDDLE PIECE;Sm;0;ON;;;;;N;;;;;
+23A9;LEFT CURLY BRACKET LOWER HOOK;Sm;0;ON;;;;;N;;;;;
+23AA;CURLY BRACKET EXTENSION;Sm;0;ON;;;;;N;;;;;
+23AB;RIGHT CURLY BRACKET UPPER HOOK;Sm;0;ON;;;;;N;;;;;
+23AC;RIGHT CURLY BRACKET MIDDLE PIECE;Sm;0;ON;;;;;N;;;;;
+23AD;RIGHT CURLY BRACKET LOWER HOOK;Sm;0;ON;;;;;N;;;;;
+23AE;INTEGRAL EXTENSION;Sm;0;ON;;;;;N;;;;;
+23AF;HORIZONTAL LINE EXTENSION;Sm;0;ON;;;;;N;;;;;
+23B0;UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION;Sm;0;ON;;;;;N;;;;;
+23B1;UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION;Sm;0;ON;;;;;N;;;;;
+23B2;SUMMATION TOP;Sm;0;ON;;;;;N;;;;;
+23B3;SUMMATION BOTTOM;Sm;0;ON;;;;;N;;;;;
+23B4;TOP SQUARE BRACKET;Ps;0;ON;;;;;N;;;;;
+23B5;BOTTOM SQUARE BRACKET;Pe;0;ON;;;;;N;;;;;
+23B6;BOTTOM SQUARE BRACKET OVER TOP SQUARE BRACKET;Po;0;ON;;;;;N;;;;;
+23B7;RADICAL SYMBOL BOTTOM;So;0;ON;;;;;N;;;;;
+23B8;LEFT VERTICAL BOX LINE;So;0;ON;;;;;N;;;;;
+23B9;RIGHT VERTICAL BOX LINE;So;0;ON;;;;;N;;;;;
+23BA;HORIZONTAL SCAN LINE-1;So;0;ON;;;;;N;;;;;
+23BB;HORIZONTAL SCAN LINE-3;So;0;ON;;;;;N;;;;;
+23BC;HORIZONTAL SCAN LINE-7;So;0;ON;;;;;N;;;;;
+23BD;HORIZONTAL SCAN LINE-9;So;0;ON;;;;;N;;;;;
+23BE;DENTISTRY SYMBOL LIGHT VERTICAL AND TOP RIGHT;So;0;ON;;;;;N;;;;;
+23BF;DENTISTRY SYMBOL LIGHT VERTICAL AND BOTTOM RIGHT;So;0;ON;;;;;N;;;;;
+23C0;DENTISTRY SYMBOL LIGHT VERTICAL WITH CIRCLE;So;0;ON;;;;;N;;;;;
+23C1;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL WITH CIRCLE;So;0;ON;;;;;N;;;;;
+23C2;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL WITH CIRCLE;So;0;ON;;;;;N;;;;;
+23C3;DENTISTRY SYMBOL LIGHT VERTICAL WITH TRIANGLE;So;0;ON;;;;;N;;;;;
+23C4;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL WITH TRIANGLE;So;0;ON;;;;;N;;;;;
+23C5;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL WITH TRIANGLE;So;0;ON;;;;;N;;;;;
+23C6;DENTISTRY SYMBOL LIGHT VERTICAL AND WAVE;So;0;ON;;;;;N;;;;;
+23C7;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL WITH WAVE;So;0;ON;;;;;N;;;;;
+23C8;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL WITH WAVE;So;0;ON;;;;;N;;;;;
+23C9;DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL;So;0;ON;;;;;N;;;;;
+23CA;DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL;So;0;ON;;;;;N;;;;;
+23CB;DENTISTRY SYMBOL LIGHT VERTICAL AND TOP LEFT;So;0;ON;;;;;N;;;;;
+23CC;DENTISTRY SYMBOL LIGHT VERTICAL AND BOTTOM LEFT;So;0;ON;;;;;N;;;;;
+23CD;SQUARE FOOT;So;0;ON;;;;;N;;;;;
+23CE;RETURN SYMBOL;So;0;ON;;;;;N;;;;;
+2400;SYMBOL FOR NULL;So;0;ON;;;;;N;GRAPHIC FOR NULL;;;;
+2401;SYMBOL FOR START OF HEADING;So;0;ON;;;;;N;GRAPHIC FOR START OF HEADING;;;;
+2402;SYMBOL FOR START OF TEXT;So;0;ON;;;;;N;GRAPHIC FOR START OF TEXT;;;;
+2403;SYMBOL FOR END OF TEXT;So;0;ON;;;;;N;GRAPHIC FOR END OF TEXT;;;;
+2404;SYMBOL FOR END OF TRANSMISSION;So;0;ON;;;;;N;GRAPHIC FOR END OF TRANSMISSION;;;;
+2405;SYMBOL FOR ENQUIRY;So;0;ON;;;;;N;GRAPHIC FOR ENQUIRY;;;;
+2406;SYMBOL FOR ACKNOWLEDGE;So;0;ON;;;;;N;GRAPHIC FOR ACKNOWLEDGE;;;;
+2407;SYMBOL FOR BELL;So;0;ON;;;;;N;GRAPHIC FOR BELL;;;;
+2408;SYMBOL FOR BACKSPACE;So;0;ON;;;;;N;GRAPHIC FOR BACKSPACE;;;;
+2409;SYMBOL FOR HORIZONTAL TABULATION;So;0;ON;;;;;N;GRAPHIC FOR HORIZONTAL TABULATION;;;;
+240A;SYMBOL FOR LINE FEED;So;0;ON;;;;;N;GRAPHIC FOR LINE FEED;;;;
+240B;SYMBOL FOR VERTICAL TABULATION;So;0;ON;;;;;N;GRAPHIC FOR VERTICAL TABULATION;;;;
+240C;SYMBOL FOR FORM FEED;So;0;ON;;;;;N;GRAPHIC FOR FORM FEED;;;;
+240D;SYMBOL FOR CARRIAGE RETURN;So;0;ON;;;;;N;GRAPHIC FOR CARRIAGE RETURN;;;;
+240E;SYMBOL FOR SHIFT OUT;So;0;ON;;;;;N;GRAPHIC FOR SHIFT OUT;;;;
+240F;SYMBOL FOR SHIFT IN;So;0;ON;;;;;N;GRAPHIC FOR SHIFT IN;;;;
+2410;SYMBOL FOR DATA LINK ESCAPE;So;0;ON;;;;;N;GRAPHIC FOR DATA LINK ESCAPE;;;;
+2411;SYMBOL FOR DEVICE CONTROL ONE;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL ONE;;;;
+2412;SYMBOL FOR DEVICE CONTROL TWO;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL TWO;;;;
+2413;SYMBOL FOR DEVICE CONTROL THREE;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL THREE;;;;
+2414;SYMBOL FOR DEVICE CONTROL FOUR;So;0;ON;;;;;N;GRAPHIC FOR DEVICE CONTROL FOUR;;;;
+2415;SYMBOL FOR NEGATIVE ACKNOWLEDGE;So;0;ON;;;;;N;GRAPHIC FOR NEGATIVE ACKNOWLEDGE;;;;
+2416;SYMBOL FOR SYNCHRONOUS IDLE;So;0;ON;;;;;N;GRAPHIC FOR SYNCHRONOUS IDLE;;;;
+2417;SYMBOL FOR END OF TRANSMISSION BLOCK;So;0;ON;;;;;N;GRAPHIC FOR END OF TRANSMISSION BLOCK;;;;
+2418;SYMBOL FOR CANCEL;So;0;ON;;;;;N;GRAPHIC FOR CANCEL;;;;
+2419;SYMBOL FOR END OF MEDIUM;So;0;ON;;;;;N;GRAPHIC FOR END OF MEDIUM;;;;
+241A;SYMBOL FOR SUBSTITUTE;So;0;ON;;;;;N;GRAPHIC FOR SUBSTITUTE;;;;
+241B;SYMBOL FOR ESCAPE;So;0;ON;;;;;N;GRAPHIC FOR ESCAPE;;;;
+241C;SYMBOL FOR FILE SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR FILE SEPARATOR;;;;
+241D;SYMBOL FOR GROUP SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR GROUP SEPARATOR;;;;
+241E;SYMBOL FOR RECORD SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR RECORD SEPARATOR;;;;
+241F;SYMBOL FOR UNIT SEPARATOR;So;0;ON;;;;;N;GRAPHIC FOR UNIT SEPARATOR;;;;
+2420;SYMBOL FOR SPACE;So;0;ON;;;;;N;GRAPHIC FOR SPACE;;;;
+2421;SYMBOL FOR DELETE;So;0;ON;;;;;N;GRAPHIC FOR DELETE;;;;
+2422;BLANK SYMBOL;So;0;ON;;;;;N;BLANK;;;;
+2423;OPEN BOX;So;0;ON;;;;;N;;;;;
+2424;SYMBOL FOR NEWLINE;So;0;ON;;;;;N;GRAPHIC FOR NEWLINE;;;;
+2425;SYMBOL FOR DELETE FORM TWO;So;0;ON;;;;;N;;;;;
+2426;SYMBOL FOR SUBSTITUTE FORM TWO;So;0;ON;;;;;N;;;;;
+2440;OCR HOOK;So;0;ON;;;;;N;;;;;
+2441;OCR CHAIR;So;0;ON;;;;;N;;;;;
+2442;OCR FORK;So;0;ON;;;;;N;;;;;
+2443;OCR INVERTED FORK;So;0;ON;;;;;N;;;;;
+2444;OCR BELT BUCKLE;So;0;ON;;;;;N;;;;;
+2445;OCR BOW TIE;So;0;ON;;;;;N;;;;;
+2446;OCR BRANCH BANK IDENTIFICATION;So;0;ON;;;;;N;;;;;
+2447;OCR AMOUNT OF CHECK;So;0;ON;;;;;N;;;;;
+2448;OCR DASH;So;0;ON;;;;;N;;;;;
+2449;OCR CUSTOMER ACCOUNT NUMBER;So;0;ON;;;;;N;;;;;
+244A;OCR DOUBLE BACKSLASH;So;0;ON;;;;;N;;;;;
+2460;CIRCLED DIGIT ONE;No;0;EN;<circle> 0031;;1;1;N;;;;;
+2461;CIRCLED DIGIT TWO;No;0;EN;<circle> 0032;;2;2;N;;;;;
+2462;CIRCLED DIGIT THREE;No;0;EN;<circle> 0033;;3;3;N;;;;;
+2463;CIRCLED DIGIT FOUR;No;0;EN;<circle> 0034;;4;4;N;;;;;
+2464;CIRCLED DIGIT FIVE;No;0;EN;<circle> 0035;;5;5;N;;;;;
+2465;CIRCLED DIGIT SIX;No;0;EN;<circle> 0036;;6;6;N;;;;;
+2466;CIRCLED DIGIT SEVEN;No;0;EN;<circle> 0037;;7;7;N;;;;;
+2467;CIRCLED DIGIT EIGHT;No;0;EN;<circle> 0038;;8;8;N;;;;;
+2468;CIRCLED DIGIT NINE;No;0;EN;<circle> 0039;;9;9;N;;;;;
+2469;CIRCLED NUMBER TEN;No;0;EN;<circle> 0031 0030;;;10;N;;;;;
+246A;CIRCLED NUMBER ELEVEN;No;0;EN;<circle> 0031 0031;;;11;N;;;;;
+246B;CIRCLED NUMBER TWELVE;No;0;EN;<circle> 0031 0032;;;12;N;;;;;
+246C;CIRCLED NUMBER THIRTEEN;No;0;EN;<circle> 0031 0033;;;13;N;;;;;
+246D;CIRCLED NUMBER FOURTEEN;No;0;EN;<circle> 0031 0034;;;14;N;;;;;
+246E;CIRCLED NUMBER FIFTEEN;No;0;EN;<circle> 0031 0035;;;15;N;;;;;
+246F;CIRCLED NUMBER SIXTEEN;No;0;EN;<circle> 0031 0036;;;16;N;;;;;
+2470;CIRCLED NUMBER SEVENTEEN;No;0;EN;<circle> 0031 0037;;;17;N;;;;;
+2471;CIRCLED NUMBER EIGHTEEN;No;0;EN;<circle> 0031 0038;;;18;N;;;;;
+2472;CIRCLED NUMBER NINETEEN;No;0;EN;<circle> 0031 0039;;;19;N;;;;;
+2473;CIRCLED NUMBER TWENTY;No;0;EN;<circle> 0032 0030;;;20;N;;;;;
+2474;PARENTHESIZED DIGIT ONE;No;0;EN;<compat> 0028 0031 0029;;1;1;N;;;;;
+2475;PARENTHESIZED DIGIT TWO;No;0;EN;<compat> 0028 0032 0029;;2;2;N;;;;;
+2476;PARENTHESIZED DIGIT THREE;No;0;EN;<compat> 0028 0033 0029;;3;3;N;;;;;
+2477;PARENTHESIZED DIGIT FOUR;No;0;EN;<compat> 0028 0034 0029;;4;4;N;;;;;
+2478;PARENTHESIZED DIGIT FIVE;No;0;EN;<compat> 0028 0035 0029;;5;5;N;;;;;
+2479;PARENTHESIZED DIGIT SIX;No;0;EN;<compat> 0028 0036 0029;;6;6;N;;;;;
+247A;PARENTHESIZED DIGIT SEVEN;No;0;EN;<compat> 0028 0037 0029;;7;7;N;;;;;
+247B;PARENTHESIZED DIGIT EIGHT;No;0;EN;<compat> 0028 0038 0029;;8;8;N;;;;;
+247C;PARENTHESIZED DIGIT NINE;No;0;EN;<compat> 0028 0039 0029;;9;9;N;;;;;
+247D;PARENTHESIZED NUMBER TEN;No;0;EN;<compat> 0028 0031 0030 0029;;;10;N;;;;;
+247E;PARENTHESIZED NUMBER ELEVEN;No;0;EN;<compat> 0028 0031 0031 0029;;;11;N;;;;;
+247F;PARENTHESIZED NUMBER TWELVE;No;0;EN;<compat> 0028 0031 0032 0029;;;12;N;;;;;
+2480;PARENTHESIZED NUMBER THIRTEEN;No;0;EN;<compat> 0028 0031 0033 0029;;;13;N;;;;;
+2481;PARENTHESIZED NUMBER FOURTEEN;No;0;EN;<compat> 0028 0031 0034 0029;;;14;N;;;;;
+2482;PARENTHESIZED NUMBER FIFTEEN;No;0;EN;<compat> 0028 0031 0035 0029;;;15;N;;;;;
+2483;PARENTHESIZED NUMBER SIXTEEN;No;0;EN;<compat> 0028 0031 0036 0029;;;16;N;;;;;
+2484;PARENTHESIZED NUMBER SEVENTEEN;No;0;EN;<compat> 0028 0031 0037 0029;;;17;N;;;;;
+2485;PARENTHESIZED NUMBER EIGHTEEN;No;0;EN;<compat> 0028 0031 0038 0029;;;18;N;;;;;
+2486;PARENTHESIZED NUMBER NINETEEN;No;0;EN;<compat> 0028 0031 0039 0029;;;19;N;;;;;
+2487;PARENTHESIZED NUMBER TWENTY;No;0;EN;<compat> 0028 0032 0030 0029;;;20;N;;;;;
+2488;DIGIT ONE FULL STOP;No;0;EN;<compat> 0031 002E;;1;1;N;DIGIT ONE PERIOD;;;;
+2489;DIGIT TWO FULL STOP;No;0;EN;<compat> 0032 002E;;2;2;N;DIGIT TWO PERIOD;;;;
+248A;DIGIT THREE FULL STOP;No;0;EN;<compat> 0033 002E;;3;3;N;DIGIT THREE PERIOD;;;;
+248B;DIGIT FOUR FULL STOP;No;0;EN;<compat> 0034 002E;;4;4;N;DIGIT FOUR PERIOD;;;;
+248C;DIGIT FIVE FULL STOP;No;0;EN;<compat> 0035 002E;;5;5;N;DIGIT FIVE PERIOD;;;;
+248D;DIGIT SIX FULL STOP;No;0;EN;<compat> 0036 002E;;6;6;N;DIGIT SIX PERIOD;;;;
+248E;DIGIT SEVEN FULL STOP;No;0;EN;<compat> 0037 002E;;7;7;N;DIGIT SEVEN PERIOD;;;;
+248F;DIGIT EIGHT FULL STOP;No;0;EN;<compat> 0038 002E;;8;8;N;DIGIT EIGHT PERIOD;;;;
+2490;DIGIT NINE FULL STOP;No;0;EN;<compat> 0039 002E;;9;9;N;DIGIT NINE PERIOD;;;;
+2491;NUMBER TEN FULL STOP;No;0;EN;<compat> 0031 0030 002E;;;10;N;NUMBER TEN PERIOD;;;;
+2492;NUMBER ELEVEN FULL STOP;No;0;EN;<compat> 0031 0031 002E;;;11;N;NUMBER ELEVEN PERIOD;;;;
+2493;NUMBER TWELVE FULL STOP;No;0;EN;<compat> 0031 0032 002E;;;12;N;NUMBER TWELVE PERIOD;;;;
+2494;NUMBER THIRTEEN FULL STOP;No;0;EN;<compat> 0031 0033 002E;;;13;N;NUMBER THIRTEEN PERIOD;;;;
+2495;NUMBER FOURTEEN FULL STOP;No;0;EN;<compat> 0031 0034 002E;;;14;N;NUMBER FOURTEEN PERIOD;;;;
+2496;NUMBER FIFTEEN FULL STOP;No;0;EN;<compat> 0031 0035 002E;;;15;N;NUMBER FIFTEEN PERIOD;;;;
+2497;NUMBER SIXTEEN FULL STOP;No;0;EN;<compat> 0031 0036 002E;;;16;N;NUMBER SIXTEEN PERIOD;;;;
+2498;NUMBER SEVENTEEN FULL STOP;No;0;EN;<compat> 0031 0037 002E;;;17;N;NUMBER SEVENTEEN PERIOD;;;;
+2499;NUMBER EIGHTEEN FULL STOP;No;0;EN;<compat> 0031 0038 002E;;;18;N;NUMBER EIGHTEEN PERIOD;;;;
+249A;NUMBER NINETEEN FULL STOP;No;0;EN;<compat> 0031 0039 002E;;;19;N;NUMBER NINETEEN PERIOD;;;;
+249B;NUMBER TWENTY FULL STOP;No;0;EN;<compat> 0032 0030 002E;;;20;N;NUMBER TWENTY PERIOD;;;;
+249C;PARENTHESIZED LATIN SMALL LETTER A;So;0;L;<compat> 0028 0061 0029;;;;N;;;;;
+249D;PARENTHESIZED LATIN SMALL LETTER B;So;0;L;<compat> 0028 0062 0029;;;;N;;;;;
+249E;PARENTHESIZED LATIN SMALL LETTER C;So;0;L;<compat> 0028 0063 0029;;;;N;;;;;
+249F;PARENTHESIZED LATIN SMALL LETTER D;So;0;L;<compat> 0028 0064 0029;;;;N;;;;;
+24A0;PARENTHESIZED LATIN SMALL LETTER E;So;0;L;<compat> 0028 0065 0029;;;;N;;;;;
+24A1;PARENTHESIZED LATIN SMALL LETTER F;So;0;L;<compat> 0028 0066 0029;;;;N;;;;;
+24A2;PARENTHESIZED LATIN SMALL LETTER G;So;0;L;<compat> 0028 0067 0029;;;;N;;;;;
+24A3;PARENTHESIZED LATIN SMALL LETTER H;So;0;L;<compat> 0028 0068 0029;;;;N;;;;;
+24A4;PARENTHESIZED LATIN SMALL LETTER I;So;0;L;<compat> 0028 0069 0029;;;;N;;;;;
+24A5;PARENTHESIZED LATIN SMALL LETTER J;So;0;L;<compat> 0028 006A 0029;;;;N;;;;;
+24A6;PARENTHESIZED LATIN SMALL LETTER K;So;0;L;<compat> 0028 006B 0029;;;;N;;;;;
+24A7;PARENTHESIZED LATIN SMALL LETTER L;So;0;L;<compat> 0028 006C 0029;;;;N;;;;;
+24A8;PARENTHESIZED LATIN SMALL LETTER M;So;0;L;<compat> 0028 006D 0029;;;;N;;;;;
+24A9;PARENTHESIZED LATIN SMALL LETTER N;So;0;L;<compat> 0028 006E 0029;;;;N;;;;;
+24AA;PARENTHESIZED LATIN SMALL LETTER O;So;0;L;<compat> 0028 006F 0029;;;;N;;;;;
+24AB;PARENTHESIZED LATIN SMALL LETTER P;So;0;L;<compat> 0028 0070 0029;;;;N;;;;;
+24AC;PARENTHESIZED LATIN SMALL LETTER Q;So;0;L;<compat> 0028 0071 0029;;;;N;;;;;
+24AD;PARENTHESIZED LATIN SMALL LETTER R;So;0;L;<compat> 0028 0072 0029;;;;N;;;;;
+24AE;PARENTHESIZED LATIN SMALL LETTER S;So;0;L;<compat> 0028 0073 0029;;;;N;;;;;
+24AF;PARENTHESIZED LATIN SMALL LETTER T;So;0;L;<compat> 0028 0074 0029;;;;N;;;;;
+24B0;PARENTHESIZED LATIN SMALL LETTER U;So;0;L;<compat> 0028 0075 0029;;;;N;;;;;
+24B1;PARENTHESIZED LATIN SMALL LETTER V;So;0;L;<compat> 0028 0076 0029;;;;N;;;;;
+24B2;PARENTHESIZED LATIN SMALL LETTER W;So;0;L;<compat> 0028 0077 0029;;;;N;;;;;
+24B3;PARENTHESIZED LATIN SMALL LETTER X;So;0;L;<compat> 0028 0078 0029;;;;N;;;;;
+24B4;PARENTHESIZED LATIN SMALL LETTER Y;So;0;L;<compat> 0028 0079 0029;;;;N;;;;;
+24B5;PARENTHESIZED LATIN SMALL LETTER Z;So;0;L;<compat> 0028 007A 0029;;;;N;;;;;
+24B6;CIRCLED LATIN CAPITAL LETTER A;So;0;L;<circle> 0041;;;;N;;;;24D0;
+24B7;CIRCLED LATIN CAPITAL LETTER B;So;0;L;<circle> 0042;;;;N;;;;24D1;
+24B8;CIRCLED LATIN CAPITAL LETTER C;So;0;L;<circle> 0043;;;;N;;;;24D2;
+24B9;CIRCLED LATIN CAPITAL LETTER D;So;0;L;<circle> 0044;;;;N;;;;24D3;
+24BA;CIRCLED LATIN CAPITAL LETTER E;So;0;L;<circle> 0045;;;;N;;;;24D4;
+24BB;CIRCLED LATIN CAPITAL LETTER F;So;0;L;<circle> 0046;;;;N;;;;24D5;
+24BC;CIRCLED LATIN CAPITAL LETTER G;So;0;L;<circle> 0047;;;;N;;;;24D6;
+24BD;CIRCLED LATIN CAPITAL LETTER H;So;0;L;<circle> 0048;;;;N;;;;24D7;
+24BE;CIRCLED LATIN CAPITAL LETTER I;So;0;L;<circle> 0049;;;;N;;;;24D8;
+24BF;CIRCLED LATIN CAPITAL LETTER J;So;0;L;<circle> 004A;;;;N;;;;24D9;
+24C0;CIRCLED LATIN CAPITAL LETTER K;So;0;L;<circle> 004B;;;;N;;;;24DA;
+24C1;CIRCLED LATIN CAPITAL LETTER L;So;0;L;<circle> 004C;;;;N;;;;24DB;
+24C2;CIRCLED LATIN CAPITAL LETTER M;So;0;L;<circle> 004D;;;;N;;;;24DC;
+24C3;CIRCLED LATIN CAPITAL LETTER N;So;0;L;<circle> 004E;;;;N;;;;24DD;
+24C4;CIRCLED LATIN CAPITAL LETTER O;So;0;L;<circle> 004F;;;;N;;;;24DE;
+24C5;CIRCLED LATIN CAPITAL LETTER P;So;0;L;<circle> 0050;;;;N;;;;24DF;
+24C6;CIRCLED LATIN CAPITAL LETTER Q;So;0;L;<circle> 0051;;;;N;;;;24E0;
+24C7;CIRCLED LATIN CAPITAL LETTER R;So;0;L;<circle> 0052;;;;N;;;;24E1;
+24C8;CIRCLED LATIN CAPITAL LETTER S;So;0;L;<circle> 0053;;;;N;;;;24E2;
+24C9;CIRCLED LATIN CAPITAL LETTER T;So;0;L;<circle> 0054;;;;N;;;;24E3;
+24CA;CIRCLED LATIN CAPITAL LETTER U;So;0;L;<circle> 0055;;;;N;;;;24E4;
+24CB;CIRCLED LATIN CAPITAL LETTER V;So;0;L;<circle> 0056;;;;N;;;;24E5;
+24CC;CIRCLED LATIN CAPITAL LETTER W;So;0;L;<circle> 0057;;;;N;;;;24E6;
+24CD;CIRCLED LATIN CAPITAL LETTER X;So;0;L;<circle> 0058;;;;N;;;;24E7;
+24CE;CIRCLED LATIN CAPITAL LETTER Y;So;0;L;<circle> 0059;;;;N;;;;24E8;
+24CF;CIRCLED LATIN CAPITAL LETTER Z;So;0;L;<circle> 005A;;;;N;;;;24E9;
+24D0;CIRCLED LATIN SMALL LETTER A;So;0;L;<circle> 0061;;;;N;;;24B6;;24B6
+24D1;CIRCLED LATIN SMALL LETTER B;So;0;L;<circle> 0062;;;;N;;;24B7;;24B7
+24D2;CIRCLED LATIN SMALL LETTER C;So;0;L;<circle> 0063;;;;N;;;24B8;;24B8
+24D3;CIRCLED LATIN SMALL LETTER D;So;0;L;<circle> 0064;;;;N;;;24B9;;24B9
+24D4;CIRCLED LATIN SMALL LETTER E;So;0;L;<circle> 0065;;;;N;;;24BA;;24BA
+24D5;CIRCLED LATIN SMALL LETTER F;So;0;L;<circle> 0066;;;;N;;;24BB;;24BB
+24D6;CIRCLED LATIN SMALL LETTER G;So;0;L;<circle> 0067;;;;N;;;24BC;;24BC
+24D7;CIRCLED LATIN SMALL LETTER H;So;0;L;<circle> 0068;;;;N;;;24BD;;24BD
+24D8;CIRCLED LATIN SMALL LETTER I;So;0;L;<circle> 0069;;;;N;;;24BE;;24BE
+24D9;CIRCLED LATIN SMALL LETTER J;So;0;L;<circle> 006A;;;;N;;;24BF;;24BF
+24DA;CIRCLED LATIN SMALL LETTER K;So;0;L;<circle> 006B;;;;N;;;24C0;;24C0
+24DB;CIRCLED LATIN SMALL LETTER L;So;0;L;<circle> 006C;;;;N;;;24C1;;24C1
+24DC;CIRCLED LATIN SMALL LETTER M;So;0;L;<circle> 006D;;;;N;;;24C2;;24C2
+24DD;CIRCLED LATIN SMALL LETTER N;So;0;L;<circle> 006E;;;;N;;;24C3;;24C3
+24DE;CIRCLED LATIN SMALL LETTER O;So;0;L;<circle> 006F;;;;N;;;24C4;;24C4
+24DF;CIRCLED LATIN SMALL LETTER P;So;0;L;<circle> 0070;;;;N;;;24C5;;24C5
+24E0;CIRCLED LATIN SMALL LETTER Q;So;0;L;<circle> 0071;;;;N;;;24C6;;24C6
+24E1;CIRCLED LATIN SMALL LETTER R;So;0;L;<circle> 0072;;;;N;;;24C7;;24C7
+24E2;CIRCLED LATIN SMALL LETTER S;So;0;L;<circle> 0073;;;;N;;;24C8;;24C8
+24E3;CIRCLED LATIN SMALL LETTER T;So;0;L;<circle> 0074;;;;N;;;24C9;;24C9
+24E4;CIRCLED LATIN SMALL LETTER U;So;0;L;<circle> 0075;;;;N;;;24CA;;24CA
+24E5;CIRCLED LATIN SMALL LETTER V;So;0;L;<circle> 0076;;;;N;;;24CB;;24CB
+24E6;CIRCLED LATIN SMALL LETTER W;So;0;L;<circle> 0077;;;;N;;;24CC;;24CC
+24E7;CIRCLED LATIN SMALL LETTER X;So;0;L;<circle> 0078;;;;N;;;24CD;;24CD
+24E8;CIRCLED LATIN SMALL LETTER Y;So;0;L;<circle> 0079;;;;N;;;24CE;;24CE
+24E9;CIRCLED LATIN SMALL LETTER Z;So;0;L;<circle> 007A;;;;N;;;24CF;;24CF
+24EA;CIRCLED DIGIT ZERO;No;0;EN;<circle> 0030;;0;0;N;;;;;
+24EB;NEGATIVE CIRCLED NUMBER ELEVEN;No;0;ON;;;;11;N;;;;;
+24EC;NEGATIVE CIRCLED NUMBER TWELVE;No;0;ON;;;;12;N;;;;;
+24ED;NEGATIVE CIRCLED NUMBER THIRTEEN;No;0;ON;;;;13;N;;;;;
+24EE;NEGATIVE CIRCLED NUMBER FOURTEEN;No;0;ON;;;;14;N;;;;;
+24EF;NEGATIVE CIRCLED NUMBER FIFTEEN;No;0;ON;;;;15;N;;;;;
+24F0;NEGATIVE CIRCLED NUMBER SIXTEEN;No;0;ON;;;;16;N;;;;;
+24F1;NEGATIVE CIRCLED NUMBER SEVENTEEN;No;0;ON;;;;17;N;;;;;
+24F2;NEGATIVE CIRCLED NUMBER EIGHTEEN;No;0;ON;;;;18;N;;;;;
+24F3;NEGATIVE CIRCLED NUMBER NINETEEN;No;0;ON;;;;19;N;;;;;
+24F4;NEGATIVE CIRCLED NUMBER TWENTY;No;0;ON;;;;20;N;;;;;
+24F5;DOUBLE CIRCLED DIGIT ONE;No;0;ON;;;1;1;N;;;;;
+24F6;DOUBLE CIRCLED DIGIT TWO;No;0;ON;;;2;2;N;;;;;
+24F7;DOUBLE CIRCLED DIGIT THREE;No;0;ON;;;3;3;N;;;;;
+24F8;DOUBLE CIRCLED DIGIT FOUR;No;0;ON;;;4;4;N;;;;;
+24F9;DOUBLE CIRCLED DIGIT FIVE;No;0;ON;;;5;5;N;;;;;
+24FA;DOUBLE CIRCLED DIGIT SIX;No;0;ON;;;6;6;N;;;;;
+24FB;DOUBLE CIRCLED DIGIT SEVEN;No;0;ON;;;7;7;N;;;;;
+24FC;DOUBLE CIRCLED DIGIT EIGHT;No;0;ON;;;8;8;N;;;;;
+24FD;DOUBLE CIRCLED DIGIT NINE;No;0;ON;;;9;9;N;;;;;
+24FE;DOUBLE CIRCLED NUMBER TEN;No;0;ON;;;;10;N;;;;;
+2500;BOX DRAWINGS LIGHT HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT HORIZONTAL;;;;
+2501;BOX DRAWINGS HEAVY HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY HORIZONTAL;;;;
+2502;BOX DRAWINGS LIGHT VERTICAL;So;0;ON;;;;;N;FORMS LIGHT VERTICAL;;;;
+2503;BOX DRAWINGS HEAVY VERTICAL;So;0;ON;;;;;N;FORMS HEAVY VERTICAL;;;;
+2504;BOX DRAWINGS LIGHT TRIPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT TRIPLE DASH HORIZONTAL;;;;
+2505;BOX DRAWINGS HEAVY TRIPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY TRIPLE DASH HORIZONTAL;;;;
+2506;BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS LIGHT TRIPLE DASH VERTICAL;;;;
+2507;BOX DRAWINGS HEAVY TRIPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS HEAVY TRIPLE DASH VERTICAL;;;;
+2508;BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT QUADRUPLE DASH HORIZONTAL;;;;
+2509;BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY QUADRUPLE DASH HORIZONTAL;;;;
+250A;BOX DRAWINGS LIGHT QUADRUPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS LIGHT QUADRUPLE DASH VERTICAL;;;;
+250B;BOX DRAWINGS HEAVY QUADRUPLE DASH VERTICAL;So;0;ON;;;;;N;FORMS HEAVY QUADRUPLE DASH VERTICAL;;;;
+250C;BOX DRAWINGS LIGHT DOWN AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT DOWN AND RIGHT;;;;
+250D;BOX DRAWINGS DOWN LIGHT AND RIGHT HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND RIGHT HEAVY;;;;
+250E;BOX DRAWINGS DOWN HEAVY AND RIGHT LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND RIGHT LIGHT;;;;
+250F;BOX DRAWINGS HEAVY DOWN AND RIGHT;So;0;ON;;;;;N;FORMS HEAVY DOWN AND RIGHT;;;;
+2510;BOX DRAWINGS LIGHT DOWN AND LEFT;So;0;ON;;;;;N;FORMS LIGHT DOWN AND LEFT;;;;
+2511;BOX DRAWINGS DOWN LIGHT AND LEFT HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND LEFT HEAVY;;;;
+2512;BOX DRAWINGS DOWN HEAVY AND LEFT LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND LEFT LIGHT;;;;
+2513;BOX DRAWINGS HEAVY DOWN AND LEFT;So;0;ON;;;;;N;FORMS HEAVY DOWN AND LEFT;;;;
+2514;BOX DRAWINGS LIGHT UP AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT UP AND RIGHT;;;;
+2515;BOX DRAWINGS UP LIGHT AND RIGHT HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND RIGHT HEAVY;;;;
+2516;BOX DRAWINGS UP HEAVY AND RIGHT LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND RIGHT LIGHT;;;;
+2517;BOX DRAWINGS HEAVY UP AND RIGHT;So;0;ON;;;;;N;FORMS HEAVY UP AND RIGHT;;;;
+2518;BOX DRAWINGS LIGHT UP AND LEFT;So;0;ON;;;;;N;FORMS LIGHT UP AND LEFT;;;;
+2519;BOX DRAWINGS UP LIGHT AND LEFT HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND LEFT HEAVY;;;;
+251A;BOX DRAWINGS UP HEAVY AND LEFT LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND LEFT LIGHT;;;;
+251B;BOX DRAWINGS HEAVY UP AND LEFT;So;0;ON;;;;;N;FORMS HEAVY UP AND LEFT;;;;
+251C;BOX DRAWINGS LIGHT VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND RIGHT;;;;
+251D;BOX DRAWINGS VERTICAL LIGHT AND RIGHT HEAVY;So;0;ON;;;;;N;FORMS VERTICAL LIGHT AND RIGHT HEAVY;;;;
+251E;BOX DRAWINGS UP HEAVY AND RIGHT DOWN LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND RIGHT DOWN LIGHT;;;;
+251F;BOX DRAWINGS DOWN HEAVY AND RIGHT UP LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND RIGHT UP LIGHT;;;;
+2520;BOX DRAWINGS VERTICAL HEAVY AND RIGHT LIGHT;So;0;ON;;;;;N;FORMS VERTICAL HEAVY AND RIGHT LIGHT;;;;
+2521;BOX DRAWINGS DOWN LIGHT AND RIGHT UP HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND RIGHT UP HEAVY;;;;
+2522;BOX DRAWINGS UP LIGHT AND RIGHT DOWN HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND RIGHT DOWN HEAVY;;;;
+2523;BOX DRAWINGS HEAVY VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS HEAVY VERTICAL AND RIGHT;;;;
+2524;BOX DRAWINGS LIGHT VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND LEFT;;;;
+2525;BOX DRAWINGS VERTICAL LIGHT AND LEFT HEAVY;So;0;ON;;;;;N;FORMS VERTICAL LIGHT AND LEFT HEAVY;;;;
+2526;BOX DRAWINGS UP HEAVY AND LEFT DOWN LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND LEFT DOWN LIGHT;;;;
+2527;BOX DRAWINGS DOWN HEAVY AND LEFT UP LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND LEFT UP LIGHT;;;;
+2528;BOX DRAWINGS VERTICAL HEAVY AND LEFT LIGHT;So;0;ON;;;;;N;FORMS VERTICAL HEAVY AND LEFT LIGHT;;;;
+2529;BOX DRAWINGS DOWN LIGHT AND LEFT UP HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND LEFT UP HEAVY;;;;
+252A;BOX DRAWINGS UP LIGHT AND LEFT DOWN HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND LEFT DOWN HEAVY;;;;
+252B;BOX DRAWINGS HEAVY VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS HEAVY VERTICAL AND LEFT;;;;
+252C;BOX DRAWINGS LIGHT DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT DOWN AND HORIZONTAL;;;;
+252D;BOX DRAWINGS LEFT HEAVY AND RIGHT DOWN LIGHT;So;0;ON;;;;;N;FORMS LEFT HEAVY AND RIGHT DOWN LIGHT;;;;
+252E;BOX DRAWINGS RIGHT HEAVY AND LEFT DOWN LIGHT;So;0;ON;;;;;N;FORMS RIGHT HEAVY AND LEFT DOWN LIGHT;;;;
+252F;BOX DRAWINGS DOWN LIGHT AND HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND HORIZONTAL HEAVY;;;;
+2530;BOX DRAWINGS DOWN HEAVY AND HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND HORIZONTAL LIGHT;;;;
+2531;BOX DRAWINGS RIGHT LIGHT AND LEFT DOWN HEAVY;So;0;ON;;;;;N;FORMS RIGHT LIGHT AND LEFT DOWN HEAVY;;;;
+2532;BOX DRAWINGS LEFT LIGHT AND RIGHT DOWN HEAVY;So;0;ON;;;;;N;FORMS LEFT LIGHT AND RIGHT DOWN HEAVY;;;;
+2533;BOX DRAWINGS HEAVY DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY DOWN AND HORIZONTAL;;;;
+2534;BOX DRAWINGS LIGHT UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT UP AND HORIZONTAL;;;;
+2535;BOX DRAWINGS LEFT HEAVY AND RIGHT UP LIGHT;So;0;ON;;;;;N;FORMS LEFT HEAVY AND RIGHT UP LIGHT;;;;
+2536;BOX DRAWINGS RIGHT HEAVY AND LEFT UP LIGHT;So;0;ON;;;;;N;FORMS RIGHT HEAVY AND LEFT UP LIGHT;;;;
+2537;BOX DRAWINGS UP LIGHT AND HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND HORIZONTAL HEAVY;;;;
+2538;BOX DRAWINGS UP HEAVY AND HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND HORIZONTAL LIGHT;;;;
+2539;BOX DRAWINGS RIGHT LIGHT AND LEFT UP HEAVY;So;0;ON;;;;;N;FORMS RIGHT LIGHT AND LEFT UP HEAVY;;;;
+253A;BOX DRAWINGS LEFT LIGHT AND RIGHT UP HEAVY;So;0;ON;;;;;N;FORMS LEFT LIGHT AND RIGHT UP HEAVY;;;;
+253B;BOX DRAWINGS HEAVY UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY UP AND HORIZONTAL;;;;
+253C;BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT VERTICAL AND HORIZONTAL;;;;
+253D;BOX DRAWINGS LEFT HEAVY AND RIGHT VERTICAL LIGHT;So;0;ON;;;;;N;FORMS LEFT HEAVY AND RIGHT VERTICAL LIGHT;;;;
+253E;BOX DRAWINGS RIGHT HEAVY AND LEFT VERTICAL LIGHT;So;0;ON;;;;;N;FORMS RIGHT HEAVY AND LEFT VERTICAL LIGHT;;;;
+253F;BOX DRAWINGS VERTICAL LIGHT AND HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS VERTICAL LIGHT AND HORIZONTAL HEAVY;;;;
+2540;BOX DRAWINGS UP HEAVY AND DOWN HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS UP HEAVY AND DOWN HORIZONTAL LIGHT;;;;
+2541;BOX DRAWINGS DOWN HEAVY AND UP HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS DOWN HEAVY AND UP HORIZONTAL LIGHT;;;;
+2542;BOX DRAWINGS VERTICAL HEAVY AND HORIZONTAL LIGHT;So;0;ON;;;;;N;FORMS VERTICAL HEAVY AND HORIZONTAL LIGHT;;;;
+2543;BOX DRAWINGS LEFT UP HEAVY AND RIGHT DOWN LIGHT;So;0;ON;;;;;N;FORMS LEFT UP HEAVY AND RIGHT DOWN LIGHT;;;;
+2544;BOX DRAWINGS RIGHT UP HEAVY AND LEFT DOWN LIGHT;So;0;ON;;;;;N;FORMS RIGHT UP HEAVY AND LEFT DOWN LIGHT;;;;
+2545;BOX DRAWINGS LEFT DOWN HEAVY AND RIGHT UP LIGHT;So;0;ON;;;;;N;FORMS LEFT DOWN HEAVY AND RIGHT UP LIGHT;;;;
+2546;BOX DRAWINGS RIGHT DOWN HEAVY AND LEFT UP LIGHT;So;0;ON;;;;;N;FORMS RIGHT DOWN HEAVY AND LEFT UP LIGHT;;;;
+2547;BOX DRAWINGS DOWN LIGHT AND UP HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS DOWN LIGHT AND UP HORIZONTAL HEAVY;;;;
+2548;BOX DRAWINGS UP LIGHT AND DOWN HORIZONTAL HEAVY;So;0;ON;;;;;N;FORMS UP LIGHT AND DOWN HORIZONTAL HEAVY;;;;
+2549;BOX DRAWINGS RIGHT LIGHT AND LEFT VERTICAL HEAVY;So;0;ON;;;;;N;FORMS RIGHT LIGHT AND LEFT VERTICAL HEAVY;;;;
+254A;BOX DRAWINGS LEFT LIGHT AND RIGHT VERTICAL HEAVY;So;0;ON;;;;;N;FORMS LEFT LIGHT AND RIGHT VERTICAL HEAVY;;;;
+254B;BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY VERTICAL AND HORIZONTAL;;;;
+254C;BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS LIGHT DOUBLE DASH HORIZONTAL;;;;
+254D;BOX DRAWINGS HEAVY DOUBLE DASH HORIZONTAL;So;0;ON;;;;;N;FORMS HEAVY DOUBLE DASH HORIZONTAL;;;;
+254E;BOX DRAWINGS LIGHT DOUBLE DASH VERTICAL;So;0;ON;;;;;N;FORMS LIGHT DOUBLE DASH VERTICAL;;;;
+254F;BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL;So;0;ON;;;;;N;FORMS HEAVY DOUBLE DASH VERTICAL;;;;
+2550;BOX DRAWINGS DOUBLE HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE HORIZONTAL;;;;
+2551;BOX DRAWINGS DOUBLE VERTICAL;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL;;;;
+2552;BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND RIGHT DOUBLE;;;;
+2553;BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND RIGHT SINGLE;;;;
+2554;BOX DRAWINGS DOUBLE DOWN AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND RIGHT;;;;
+2555;BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND LEFT DOUBLE;;;;
+2556;BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND LEFT SINGLE;;;;
+2557;BOX DRAWINGS DOUBLE DOWN AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND LEFT;;;;
+2558;BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND RIGHT DOUBLE;;;;
+2559;BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND RIGHT SINGLE;;;;
+255A;BOX DRAWINGS DOUBLE UP AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE UP AND RIGHT;;;;
+255B;BOX DRAWINGS UP SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND LEFT DOUBLE;;;;
+255C;BOX DRAWINGS UP DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND LEFT SINGLE;;;;
+255D;BOX DRAWINGS DOUBLE UP AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE UP AND LEFT;;;;
+255E;BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND RIGHT DOUBLE;;;;
+255F;BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND RIGHT SINGLE;;;;
+2560;BOX DRAWINGS DOUBLE VERTICAL AND RIGHT;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND RIGHT;;;;
+2561;BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND LEFT DOUBLE;;;;
+2562;BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND LEFT SINGLE;;;;
+2563;BOX DRAWINGS DOUBLE VERTICAL AND LEFT;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND LEFT;;;;
+2564;BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS DOWN SINGLE AND HORIZONTAL DOUBLE;;;;
+2565;BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS DOWN DOUBLE AND HORIZONTAL SINGLE;;;;
+2566;BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE DOWN AND HORIZONTAL;;;;
+2567;BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS UP SINGLE AND HORIZONTAL DOUBLE;;;;
+2568;BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS UP DOUBLE AND HORIZONTAL SINGLE;;;;
+2569;BOX DRAWINGS DOUBLE UP AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE UP AND HORIZONTAL;;;;
+256A;BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE;So;0;ON;;;;;N;FORMS VERTICAL SINGLE AND HORIZONTAL DOUBLE;;;;
+256B;BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE;So;0;ON;;;;;N;FORMS VERTICAL DOUBLE AND HORIZONTAL SINGLE;;;;
+256C;BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL;So;0;ON;;;;;N;FORMS DOUBLE VERTICAL AND HORIZONTAL;;;;
+256D;BOX DRAWINGS LIGHT ARC DOWN AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT ARC DOWN AND RIGHT;;;;
+256E;BOX DRAWINGS LIGHT ARC DOWN AND LEFT;So;0;ON;;;;;N;FORMS LIGHT ARC DOWN AND LEFT;;;;
+256F;BOX DRAWINGS LIGHT ARC UP AND LEFT;So;0;ON;;;;;N;FORMS LIGHT ARC UP AND LEFT;;;;
+2570;BOX DRAWINGS LIGHT ARC UP AND RIGHT;So;0;ON;;;;;N;FORMS LIGHT ARC UP AND RIGHT;;;;
+2571;BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT;So;0;ON;;;;;N;FORMS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT;;;;
+2572;BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT;So;0;ON;;;;;N;FORMS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT;;;;
+2573;BOX DRAWINGS LIGHT DIAGONAL CROSS;So;0;ON;;;;;N;FORMS LIGHT DIAGONAL CROSS;;;;
+2574;BOX DRAWINGS LIGHT LEFT;So;0;ON;;;;;N;FORMS LIGHT LEFT;;;;
+2575;BOX DRAWINGS LIGHT UP;So;0;ON;;;;;N;FORMS LIGHT UP;;;;
+2576;BOX DRAWINGS LIGHT RIGHT;So;0;ON;;;;;N;FORMS LIGHT RIGHT;;;;
+2577;BOX DRAWINGS LIGHT DOWN;So;0;ON;;;;;N;FORMS LIGHT DOWN;;;;
+2578;BOX DRAWINGS HEAVY LEFT;So;0;ON;;;;;N;FORMS HEAVY LEFT;;;;
+2579;BOX DRAWINGS HEAVY UP;So;0;ON;;;;;N;FORMS HEAVY UP;;;;
+257A;BOX DRAWINGS HEAVY RIGHT;So;0;ON;;;;;N;FORMS HEAVY RIGHT;;;;
+257B;BOX DRAWINGS HEAVY DOWN;So;0;ON;;;;;N;FORMS HEAVY DOWN;;;;
+257C;BOX DRAWINGS LIGHT LEFT AND HEAVY RIGHT;So;0;ON;;;;;N;FORMS LIGHT LEFT AND HEAVY RIGHT;;;;
+257D;BOX DRAWINGS LIGHT UP AND HEAVY DOWN;So;0;ON;;;;;N;FORMS LIGHT UP AND HEAVY DOWN;;;;
+257E;BOX DRAWINGS HEAVY LEFT AND LIGHT RIGHT;So;0;ON;;;;;N;FORMS HEAVY LEFT AND LIGHT RIGHT;;;;
+257F;BOX DRAWINGS HEAVY UP AND LIGHT DOWN;So;0;ON;;;;;N;FORMS HEAVY UP AND LIGHT DOWN;;;;
+2580;UPPER HALF BLOCK;So;0;ON;;;;;N;;;;;
+2581;LOWER ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;;
+2582;LOWER ONE QUARTER BLOCK;So;0;ON;;;;;N;;;;;
+2583;LOWER THREE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+2584;LOWER HALF BLOCK;So;0;ON;;;;;N;;;;;
+2585;LOWER FIVE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+2586;LOWER THREE QUARTERS BLOCK;So;0;ON;;;;;N;LOWER THREE QUARTER BLOCK;;;;
+2587;LOWER SEVEN EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+2588;FULL BLOCK;So;0;ON;;;;;N;;;;;
+2589;LEFT SEVEN EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+258A;LEFT THREE QUARTERS BLOCK;So;0;ON;;;;;N;LEFT THREE QUARTER BLOCK;;;;
+258B;LEFT FIVE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+258C;LEFT HALF BLOCK;So;0;ON;;;;;N;;;;;
+258D;LEFT THREE EIGHTHS BLOCK;So;0;ON;;;;;N;;;;;
+258E;LEFT ONE QUARTER BLOCK;So;0;ON;;;;;N;;;;;
+258F;LEFT ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;;
+2590;RIGHT HALF BLOCK;So;0;ON;;;;;N;;;;;
+2591;LIGHT SHADE;So;0;ON;;;;;N;;;;;
+2592;MEDIUM SHADE;So;0;ON;;;;;N;;;;;
+2593;DARK SHADE;So;0;ON;;;;;N;;;;;
+2594;UPPER ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;;
+2595;RIGHT ONE EIGHTH BLOCK;So;0;ON;;;;;N;;;;;
+2596;QUADRANT LOWER LEFT;So;0;ON;;;;;N;;;;;
+2597;QUADRANT LOWER RIGHT;So;0;ON;;;;;N;;;;;
+2598;QUADRANT UPPER LEFT;So;0;ON;;;;;N;;;;;
+2599;QUADRANT UPPER LEFT AND LOWER LEFT AND LOWER RIGHT;So;0;ON;;;;;N;;;;;
+259A;QUADRANT UPPER LEFT AND LOWER RIGHT;So;0;ON;;;;;N;;;;;
+259B;QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER LEFT;So;0;ON;;;;;N;;;;;
+259C;QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER RIGHT;So;0;ON;;;;;N;;;;;
+259D;QUADRANT UPPER RIGHT;So;0;ON;;;;;N;;;;;
+259E;QUADRANT UPPER RIGHT AND LOWER LEFT;So;0;ON;;;;;N;;;;;
+259F;QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT;So;0;ON;;;;;N;;;;;
+25A0;BLACK SQUARE;So;0;ON;;;;;N;;;;;
+25A1;WHITE SQUARE;So;0;ON;;;;;N;;;;;
+25A2;WHITE SQUARE WITH ROUNDED CORNERS;So;0;ON;;;;;N;;;;;
+25A3;WHITE SQUARE CONTAINING BLACK SMALL SQUARE;So;0;ON;;;;;N;;;;;
+25A4;SQUARE WITH HORIZONTAL FILL;So;0;ON;;;;;N;;;;;
+25A5;SQUARE WITH VERTICAL FILL;So;0;ON;;;;;N;;;;;
+25A6;SQUARE WITH ORTHOGONAL CROSSHATCH FILL;So;0;ON;;;;;N;;;;;
+25A7;SQUARE WITH UPPER LEFT TO LOWER RIGHT FILL;So;0;ON;;;;;N;;;;;
+25A8;SQUARE WITH UPPER RIGHT TO LOWER LEFT FILL;So;0;ON;;;;;N;;;;;
+25A9;SQUARE WITH DIAGONAL CROSSHATCH FILL;So;0;ON;;;;;N;;;;;
+25AA;BLACK SMALL SQUARE;So;0;ON;;;;;N;;;;;
+25AB;WHITE SMALL SQUARE;So;0;ON;;;;;N;;;;;
+25AC;BLACK RECTANGLE;So;0;ON;;;;;N;;;;;
+25AD;WHITE RECTANGLE;So;0;ON;;;;;N;;;;;
+25AE;BLACK VERTICAL RECTANGLE;So;0;ON;;;;;N;;;;;
+25AF;WHITE VERTICAL RECTANGLE;So;0;ON;;;;;N;;;;;
+25B0;BLACK PARALLELOGRAM;So;0;ON;;;;;N;;;;;
+25B1;WHITE PARALLELOGRAM;So;0;ON;;;;;N;;;;;
+25B2;BLACK UP-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK UP POINTING TRIANGLE;;;;
+25B3;WHITE UP-POINTING TRIANGLE;So;0;ON;;;;;N;WHITE UP POINTING TRIANGLE;;;;
+25B4;BLACK UP-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK UP POINTING SMALL TRIANGLE;;;;
+25B5;WHITE UP-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE UP POINTING SMALL TRIANGLE;;;;
+25B6;BLACK RIGHT-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK RIGHT POINTING TRIANGLE;;;;
+25B7;WHITE RIGHT-POINTING TRIANGLE;Sm;0;ON;;;;;N;WHITE RIGHT POINTING TRIANGLE;;;;
+25B8;BLACK RIGHT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK RIGHT POINTING SMALL TRIANGLE;;;;
+25B9;WHITE RIGHT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE RIGHT POINTING SMALL TRIANGLE;;;;
+25BA;BLACK RIGHT-POINTING POINTER;So;0;ON;;;;;N;BLACK RIGHT POINTING POINTER;;;;
+25BB;WHITE RIGHT-POINTING POINTER;So;0;ON;;;;;N;WHITE RIGHT POINTING POINTER;;;;
+25BC;BLACK DOWN-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK DOWN POINTING TRIANGLE;;;;
+25BD;WHITE DOWN-POINTING TRIANGLE;So;0;ON;;;;;N;WHITE DOWN POINTING TRIANGLE;;;;
+25BE;BLACK DOWN-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK DOWN POINTING SMALL TRIANGLE;;;;
+25BF;WHITE DOWN-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE DOWN POINTING SMALL TRIANGLE;;;;
+25C0;BLACK LEFT-POINTING TRIANGLE;So;0;ON;;;;;N;BLACK LEFT POINTING TRIANGLE;;;;
+25C1;WHITE LEFT-POINTING TRIANGLE;Sm;0;ON;;;;;N;WHITE LEFT POINTING TRIANGLE;;;;
+25C2;BLACK LEFT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;BLACK LEFT POINTING SMALL TRIANGLE;;;;
+25C3;WHITE LEFT-POINTING SMALL TRIANGLE;So;0;ON;;;;;N;WHITE LEFT POINTING SMALL TRIANGLE;;;;
+25C4;BLACK LEFT-POINTING POINTER;So;0;ON;;;;;N;BLACK LEFT POINTING POINTER;;;;
+25C5;WHITE LEFT-POINTING POINTER;So;0;ON;;;;;N;WHITE LEFT POINTING POINTER;;;;
+25C6;BLACK DIAMOND;So;0;ON;;;;;N;;;;;
+25C7;WHITE DIAMOND;So;0;ON;;;;;N;;;;;
+25C8;WHITE DIAMOND CONTAINING BLACK SMALL DIAMOND;So;0;ON;;;;;N;;;;;
+25C9;FISHEYE;So;0;ON;;;;;N;;;;;
+25CA;LOZENGE;So;0;ON;;;;;N;;;;;
+25CB;WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+25CC;DOTTED CIRCLE;So;0;ON;;;;;N;;;;;
+25CD;CIRCLE WITH VERTICAL FILL;So;0;ON;;;;;N;;;;;
+25CE;BULLSEYE;So;0;ON;;;;;N;;;;;
+25CF;BLACK CIRCLE;So;0;ON;;;;;N;;;;;
+25D0;CIRCLE WITH LEFT HALF BLACK;So;0;ON;;;;;N;;;;;
+25D1;CIRCLE WITH RIGHT HALF BLACK;So;0;ON;;;;;N;;;;;
+25D2;CIRCLE WITH LOWER HALF BLACK;So;0;ON;;;;;N;;;;;
+25D3;CIRCLE WITH UPPER HALF BLACK;So;0;ON;;;;;N;;;;;
+25D4;CIRCLE WITH UPPER RIGHT QUADRANT BLACK;So;0;ON;;;;;N;;;;;
+25D5;CIRCLE WITH ALL BUT UPPER LEFT QUADRANT BLACK;So;0;ON;;;;;N;;;;;
+25D6;LEFT HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;;
+25D7;RIGHT HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;;
+25D8;INVERSE BULLET;So;0;ON;;;;;N;;;;;
+25D9;INVERSE WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+25DA;UPPER HALF INVERSE WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+25DB;LOWER HALF INVERSE WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+25DC;UPPER LEFT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;;
+25DD;UPPER RIGHT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;;
+25DE;LOWER RIGHT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;;
+25DF;LOWER LEFT QUADRANT CIRCULAR ARC;So;0;ON;;;;;N;;;;;
+25E0;UPPER HALF CIRCLE;So;0;ON;;;;;N;;;;;
+25E1;LOWER HALF CIRCLE;So;0;ON;;;;;N;;;;;
+25E2;BLACK LOWER RIGHT TRIANGLE;So;0;ON;;;;;N;;;;;
+25E3;BLACK LOWER LEFT TRIANGLE;So;0;ON;;;;;N;;;;;
+25E4;BLACK UPPER LEFT TRIANGLE;So;0;ON;;;;;N;;;;;
+25E5;BLACK UPPER RIGHT TRIANGLE;So;0;ON;;;;;N;;;;;
+25E6;WHITE BULLET;So;0;ON;;;;;N;;;;;
+25E7;SQUARE WITH LEFT HALF BLACK;So;0;ON;;;;;N;;;;;
+25E8;SQUARE WITH RIGHT HALF BLACK;So;0;ON;;;;;N;;;;;
+25E9;SQUARE WITH UPPER LEFT DIAGONAL HALF BLACK;So;0;ON;;;;;N;;;;;
+25EA;SQUARE WITH LOWER RIGHT DIAGONAL HALF BLACK;So;0;ON;;;;;N;;;;;
+25EB;WHITE SQUARE WITH VERTICAL BISECTING LINE;So;0;ON;;;;;N;;;;;
+25EC;WHITE UP-POINTING TRIANGLE WITH DOT;So;0;ON;;;;;N;WHITE UP POINTING TRIANGLE WITH DOT;;;;
+25ED;UP-POINTING TRIANGLE WITH LEFT HALF BLACK;So;0;ON;;;;;N;UP POINTING TRIANGLE WITH LEFT HALF BLACK;;;;
+25EE;UP-POINTING TRIANGLE WITH RIGHT HALF BLACK;So;0;ON;;;;;N;UP POINTING TRIANGLE WITH RIGHT HALF BLACK;;;;
+25EF;LARGE CIRCLE;So;0;ON;;;;;N;;;;;
+25F0;WHITE SQUARE WITH UPPER LEFT QUADRANT;So;0;ON;;;;;N;;;;;
+25F1;WHITE SQUARE WITH LOWER LEFT QUADRANT;So;0;ON;;;;;N;;;;;
+25F2;WHITE SQUARE WITH LOWER RIGHT QUADRANT;So;0;ON;;;;;N;;;;;
+25F3;WHITE SQUARE WITH UPPER RIGHT QUADRANT;So;0;ON;;;;;N;;;;;
+25F4;WHITE CIRCLE WITH UPPER LEFT QUADRANT;So;0;ON;;;;;N;;;;;
+25F5;WHITE CIRCLE WITH LOWER LEFT QUADRANT;So;0;ON;;;;;N;;;;;
+25F6;WHITE CIRCLE WITH LOWER RIGHT QUADRANT;So;0;ON;;;;;N;;;;;
+25F7;WHITE CIRCLE WITH UPPER RIGHT QUADRANT;So;0;ON;;;;;N;;;;;
+25F8;UPPER LEFT TRIANGLE;Sm;0;ON;;;;;N;;;;;
+25F9;UPPER RIGHT TRIANGLE;Sm;0;ON;;;;;N;;;;;
+25FA;LOWER LEFT TRIANGLE;Sm;0;ON;;;;;N;;;;;
+25FB;WHITE MEDIUM SQUARE;Sm;0;ON;;;;;N;;;;;
+25FC;BLACK MEDIUM SQUARE;Sm;0;ON;;;;;N;;;;;
+25FD;WHITE MEDIUM SMALL SQUARE;Sm;0;ON;;;;;N;;;;;
+25FE;BLACK MEDIUM SMALL SQUARE;Sm;0;ON;;;;;N;;;;;
+25FF;LOWER RIGHT TRIANGLE;Sm;0;ON;;;;;N;;;;;
+2600;BLACK SUN WITH RAYS;So;0;ON;;;;;N;;;;;
+2601;CLOUD;So;0;ON;;;;;N;;;;;
+2602;UMBRELLA;So;0;ON;;;;;N;;;;;
+2603;SNOWMAN;So;0;ON;;;;;N;;;;;
+2604;COMET;So;0;ON;;;;;N;;;;;
+2605;BLACK STAR;So;0;ON;;;;;N;;;;;
+2606;WHITE STAR;So;0;ON;;;;;N;;;;;
+2607;LIGHTNING;So;0;ON;;;;;N;;;;;
+2608;THUNDERSTORM;So;0;ON;;;;;N;;;;;
+2609;SUN;So;0;ON;;;;;N;;;;;
+260A;ASCENDING NODE;So;0;ON;;;;;N;;;;;
+260B;DESCENDING NODE;So;0;ON;;;;;N;;;;;
+260C;CONJUNCTION;So;0;ON;;;;;N;;;;;
+260D;OPPOSITION;So;0;ON;;;;;N;;;;;
+260E;BLACK TELEPHONE;So;0;ON;;;;;N;;;;;
+260F;WHITE TELEPHONE;So;0;ON;;;;;N;;;;;
+2610;BALLOT BOX;So;0;ON;;;;;N;;;;;
+2611;BALLOT BOX WITH CHECK;So;0;ON;;;;;N;;;;;
+2612;BALLOT BOX WITH X;So;0;ON;;;;;N;;;;;
+2613;SALTIRE;So;0;ON;;;;;N;;;;;
+2616;WHITE SHOGI PIECE;So;0;ON;;;;;N;;;;;
+2617;BLACK SHOGI PIECE;So;0;ON;;;;;N;;;;;
+2619;REVERSED ROTATED FLORAL HEART BULLET;So;0;ON;;;;;N;;;;;
+261A;BLACK LEFT POINTING INDEX;So;0;ON;;;;;N;;;;;
+261B;BLACK RIGHT POINTING INDEX;So;0;ON;;;;;N;;;;;
+261C;WHITE LEFT POINTING INDEX;So;0;ON;;;;;N;;;;;
+261D;WHITE UP POINTING INDEX;So;0;ON;;;;;N;;;;;
+261E;WHITE RIGHT POINTING INDEX;So;0;ON;;;;;N;;;;;
+261F;WHITE DOWN POINTING INDEX;So;0;ON;;;;;N;;;;;
+2620;SKULL AND CROSSBONES;So;0;ON;;;;;N;;;;;
+2621;CAUTION SIGN;So;0;ON;;;;;N;;;;;
+2622;RADIOACTIVE SIGN;So;0;ON;;;;;N;;;;;
+2623;BIOHAZARD SIGN;So;0;ON;;;;;N;;;;;
+2624;CADUCEUS;So;0;ON;;;;;N;;;;;
+2625;ANKH;So;0;ON;;;;;N;;;;;
+2626;ORTHODOX CROSS;So;0;ON;;;;;N;;;;;
+2627;CHI RHO;So;0;ON;;;;;N;;;;;
+2628;CROSS OF LORRAINE;So;0;ON;;;;;N;;;;;
+2629;CROSS OF JERUSALEM;So;0;ON;;;;;N;;;;;
+262A;STAR AND CRESCENT;So;0;ON;;;;;N;;;;;
+262B;FARSI SYMBOL;So;0;ON;;;;;N;SYMBOL OF IRAN;;;;
+262C;ADI SHAKTI;So;0;ON;;;;;N;;;;;
+262D;HAMMER AND SICKLE;So;0;ON;;;;;N;;;;;
+262E;PEACE SYMBOL;So;0;ON;;;;;N;;;;;
+262F;YIN YANG;So;0;ON;;;;;N;;;;;
+2630;TRIGRAM FOR HEAVEN;So;0;ON;;;;;N;;;;;
+2631;TRIGRAM FOR LAKE;So;0;ON;;;;;N;;;;;
+2632;TRIGRAM FOR FIRE;So;0;ON;;;;;N;;;;;
+2633;TRIGRAM FOR THUNDER;So;0;ON;;;;;N;;;;;
+2634;TRIGRAM FOR WIND;So;0;ON;;;;;N;;;;;
+2635;TRIGRAM FOR WATER;So;0;ON;;;;;N;;;;;
+2636;TRIGRAM FOR MOUNTAIN;So;0;ON;;;;;N;;;;;
+2637;TRIGRAM FOR EARTH;So;0;ON;;;;;N;;;;;
+2638;WHEEL OF DHARMA;So;0;ON;;;;;N;;;;;
+2639;WHITE FROWNING FACE;So;0;ON;;;;;N;;;;;
+263A;WHITE SMILING FACE;So;0;ON;;;;;N;;;;;
+263B;BLACK SMILING FACE;So;0;ON;;;;;N;;;;;
+263C;WHITE SUN WITH RAYS;So;0;ON;;;;;N;;;;;
+263D;FIRST QUARTER MOON;So;0;ON;;;;;N;;;;;
+263E;LAST QUARTER MOON;So;0;ON;;;;;N;;;;;
+263F;MERCURY;So;0;ON;;;;;N;;;;;
+2640;FEMALE SIGN;So;0;ON;;;;;N;;;;;
+2641;EARTH;So;0;ON;;;;;N;;;;;
+2642;MALE SIGN;So;0;ON;;;;;N;;;;;
+2643;JUPITER;So;0;ON;;;;;N;;;;;
+2644;SATURN;So;0;ON;;;;;N;;;;;
+2645;URANUS;So;0;ON;;;;;N;;;;;
+2646;NEPTUNE;So;0;ON;;;;;N;;;;;
+2647;PLUTO;So;0;ON;;;;;N;;;;;
+2648;ARIES;So;0;ON;;;;;N;;;;;
+2649;TAURUS;So;0;ON;;;;;N;;;;;
+264A;GEMINI;So;0;ON;;;;;N;;;;;
+264B;CANCER;So;0;ON;;;;;N;;;;;
+264C;LEO;So;0;ON;;;;;N;;;;;
+264D;VIRGO;So;0;ON;;;;;N;;;;;
+264E;LIBRA;So;0;ON;;;;;N;;;;;
+264F;SCORPIUS;So;0;ON;;;;;N;;;;;
+2650;SAGITTARIUS;So;0;ON;;;;;N;;;;;
+2651;CAPRICORN;So;0;ON;;;;;N;;;;;
+2652;AQUARIUS;So;0;ON;;;;;N;;;;;
+2653;PISCES;So;0;ON;;;;;N;;;;;
+2654;WHITE CHESS KING;So;0;ON;;;;;N;;;;;
+2655;WHITE CHESS QUEEN;So;0;ON;;;;;N;;;;;
+2656;WHITE CHESS ROOK;So;0;ON;;;;;N;;;;;
+2657;WHITE CHESS BISHOP;So;0;ON;;;;;N;;;;;
+2658;WHITE CHESS KNIGHT;So;0;ON;;;;;N;;;;;
+2659;WHITE CHESS PAWN;So;0;ON;;;;;N;;;;;
+265A;BLACK CHESS KING;So;0;ON;;;;;N;;;;;
+265B;BLACK CHESS QUEEN;So;0;ON;;;;;N;;;;;
+265C;BLACK CHESS ROOK;So;0;ON;;;;;N;;;;;
+265D;BLACK CHESS BISHOP;So;0;ON;;;;;N;;;;;
+265E;BLACK CHESS KNIGHT;So;0;ON;;;;;N;;;;;
+265F;BLACK CHESS PAWN;So;0;ON;;;;;N;;;;;
+2660;BLACK SPADE SUIT;So;0;ON;;;;;N;;;;;
+2661;WHITE HEART SUIT;So;0;ON;;;;;N;;;;;
+2662;WHITE DIAMOND SUIT;So;0;ON;;;;;N;;;;;
+2663;BLACK CLUB SUIT;So;0;ON;;;;;N;;;;;
+2664;WHITE SPADE SUIT;So;0;ON;;;;;N;;;;;
+2665;BLACK HEART SUIT;So;0;ON;;;;;N;;;;;
+2666;BLACK DIAMOND SUIT;So;0;ON;;;;;N;;;;;
+2667;WHITE CLUB SUIT;So;0;ON;;;;;N;;;;;
+2668;HOT SPRINGS;So;0;ON;;;;;N;;;;;
+2669;QUARTER NOTE;So;0;ON;;;;;N;;;;;
+266A;EIGHTH NOTE;So;0;ON;;;;;N;;;;;
+266B;BEAMED EIGHTH NOTES;So;0;ON;;;;;N;BARRED EIGHTH NOTES;;;;
+266C;BEAMED SIXTEENTH NOTES;So;0;ON;;;;;N;BARRED SIXTEENTH NOTES;;;;
+266D;MUSIC FLAT SIGN;So;0;ON;;;;;N;FLAT;;;;
+266E;MUSIC NATURAL SIGN;So;0;ON;;;;;N;NATURAL;;;;
+266F;MUSIC SHARP SIGN;Sm;0;ON;;;;;N;SHARP;;;;
+2670;WEST SYRIAC CROSS;So;0;ON;;;;;N;;;;;
+2671;EAST SYRIAC CROSS;So;0;ON;;;;;N;;;;;
+2672;UNIVERSAL RECYCLING SYMBOL;So;0;ON;;;;;N;;;;;
+2673;RECYCLING SYMBOL FOR TYPE-1 PLASTICS;So;0;ON;;;;;N;;pete;;;
+2674;RECYCLING SYMBOL FOR TYPE-2 PLASTICS;So;0;ON;;;;;N;;hdpe;;;
+2675;RECYCLING SYMBOL FOR TYPE-3 PLASTICS;So;0;ON;;;;;N;;pvc;;;
+2676;RECYCLING SYMBOL FOR TYPE-4 PLASTICS;So;0;ON;;;;;N;;ldpe;;;
+2677;RECYCLING SYMBOL FOR TYPE-5 PLASTICS;So;0;ON;;;;;N;;pp;;;
+2678;RECYCLING SYMBOL FOR TYPE-6 PLASTICS;So;0;ON;;;;;N;;ps;;;
+2679;RECYCLING SYMBOL FOR TYPE-7 PLASTICS;So;0;ON;;;;;N;;other;;;
+267A;RECYCLING SYMBOL FOR GENERIC MATERIALS;So;0;ON;;;;;N;;;;;
+267B;BLACK UNIVERSAL RECYCLING SYMBOL;So;0;ON;;;;;N;;;;;
+267C;RECYCLED PAPER SYMBOL;So;0;ON;;;;;N;;;;;
+267D;PARTIALLY-RECYCLED PAPER SYMBOL;So;0;ON;;;;;N;;;;;
+2680;DIE FACE-1;So;0;ON;;;;;N;;;;;
+2681;DIE FACE-2;So;0;ON;;;;;N;;;;;
+2682;DIE FACE-3;So;0;ON;;;;;N;;;;;
+2683;DIE FACE-4;So;0;ON;;;;;N;;;;;
+2684;DIE FACE-5;So;0;ON;;;;;N;;;;;
+2685;DIE FACE-6;So;0;ON;;;;;N;;;;;
+2686;WHITE CIRCLE WITH DOT RIGHT;So;0;ON;;;;;N;;;;;
+2687;WHITE CIRCLE WITH TWO DOTS;So;0;ON;;;;;N;;;;;
+2688;BLACK CIRCLE WITH WHITE DOT RIGHT;So;0;ON;;;;;N;;;;;
+2689;BLACK CIRCLE WITH TWO WHITE DOTS;So;0;ON;;;;;N;;;;;
+2701;UPPER BLADE SCISSORS;So;0;ON;;;;;N;;;;;
+2702;BLACK SCISSORS;So;0;ON;;;;;N;;;;;
+2703;LOWER BLADE SCISSORS;So;0;ON;;;;;N;;;;;
+2704;WHITE SCISSORS;So;0;ON;;;;;N;;;;;
+2706;TELEPHONE LOCATION SIGN;So;0;ON;;;;;N;;;;;
+2707;TAPE DRIVE;So;0;ON;;;;;N;;;;;
+2708;AIRPLANE;So;0;ON;;;;;N;;;;;
+2709;ENVELOPE;So;0;ON;;;;;N;;;;;
+270C;VICTORY HAND;So;0;ON;;;;;N;;;;;
+270D;WRITING HAND;So;0;ON;;;;;N;;;;;
+270E;LOWER RIGHT PENCIL;So;0;ON;;;;;N;;;;;
+270F;PENCIL;So;0;ON;;;;;N;;;;;
+2710;UPPER RIGHT PENCIL;So;0;ON;;;;;N;;;;;
+2711;WHITE NIB;So;0;ON;;;;;N;;;;;
+2712;BLACK NIB;So;0;ON;;;;;N;;;;;
+2713;CHECK MARK;So;0;ON;;;;;N;;;;;
+2714;HEAVY CHECK MARK;So;0;ON;;;;;N;;;;;
+2715;MULTIPLICATION X;So;0;ON;;;;;N;;;;;
+2716;HEAVY MULTIPLICATION X;So;0;ON;;;;;N;;;;;
+2717;BALLOT X;So;0;ON;;;;;N;;;;;
+2718;HEAVY BALLOT X;So;0;ON;;;;;N;;;;;
+2719;OUTLINED GREEK CROSS;So;0;ON;;;;;N;;;;;
+271A;HEAVY GREEK CROSS;So;0;ON;;;;;N;;;;;
+271B;OPEN CENTRE CROSS;So;0;ON;;;;;N;OPEN CENTER CROSS;;;;
+271C;HEAVY OPEN CENTRE CROSS;So;0;ON;;;;;N;HEAVY OPEN CENTER CROSS;;;;
+271D;LATIN CROSS;So;0;ON;;;;;N;;;;;
+271E;SHADOWED WHITE LATIN CROSS;So;0;ON;;;;;N;;;;;
+271F;OUTLINED LATIN CROSS;So;0;ON;;;;;N;;;;;
+2720;MALTESE CROSS;So;0;ON;;;;;N;;;;;
+2721;STAR OF DAVID;So;0;ON;;;;;N;;;;;
+2722;FOUR TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+2723;FOUR BALLOON-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+2724;HEAVY FOUR BALLOON-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+2725;FOUR CLUB-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+2726;BLACK FOUR POINTED STAR;So;0;ON;;;;;N;;;;;
+2727;WHITE FOUR POINTED STAR;So;0;ON;;;;;N;;;;;
+2729;STRESS OUTLINED WHITE STAR;So;0;ON;;;;;N;;;;;
+272A;CIRCLED WHITE STAR;So;0;ON;;;;;N;;;;;
+272B;OPEN CENTRE BLACK STAR;So;0;ON;;;;;N;OPEN CENTER BLACK STAR;;;;
+272C;BLACK CENTRE WHITE STAR;So;0;ON;;;;;N;BLACK CENTER WHITE STAR;;;;
+272D;OUTLINED BLACK STAR;So;0;ON;;;;;N;;;;;
+272E;HEAVY OUTLINED BLACK STAR;So;0;ON;;;;;N;;;;;
+272F;PINWHEEL STAR;So;0;ON;;;;;N;;;;;
+2730;SHADOWED WHITE STAR;So;0;ON;;;;;N;;;;;
+2731;HEAVY ASTERISK;So;0;ON;;;;;N;;;;;
+2732;OPEN CENTRE ASTERISK;So;0;ON;;;;;N;OPEN CENTER ASTERISK;;;;
+2733;EIGHT SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+2734;EIGHT POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+2735;EIGHT POINTED PINWHEEL STAR;So;0;ON;;;;;N;;;;;
+2736;SIX POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+2737;EIGHT POINTED RECTILINEAR BLACK STAR;So;0;ON;;;;;N;;;;;
+2738;HEAVY EIGHT POINTED RECTILINEAR BLACK STAR;So;0;ON;;;;;N;;;;;
+2739;TWELVE POINTED BLACK STAR;So;0;ON;;;;;N;;;;;
+273A;SIXTEEN POINTED ASTERISK;So;0;ON;;;;;N;;;;;
+273B;TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+273C;OPEN CENTRE TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;OPEN CENTER TEARDROP-SPOKED ASTERISK;;;;
+273D;HEAVY TEARDROP-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+273E;SIX PETALLED BLACK AND WHITE FLORETTE;So;0;ON;;;;;N;;;;;
+273F;BLACK FLORETTE;So;0;ON;;;;;N;;;;;
+2740;WHITE FLORETTE;So;0;ON;;;;;N;;;;;
+2741;EIGHT PETALLED OUTLINED BLACK FLORETTE;So;0;ON;;;;;N;;;;;
+2742;CIRCLED OPEN CENTRE EIGHT POINTED STAR;So;0;ON;;;;;N;CIRCLED OPEN CENTER EIGHT POINTED STAR;;;;
+2743;HEAVY TEARDROP-SPOKED PINWHEEL ASTERISK;So;0;ON;;;;;N;;;;;
+2744;SNOWFLAKE;So;0;ON;;;;;N;;;;;
+2745;TIGHT TRIFOLIATE SNOWFLAKE;So;0;ON;;;;;N;;;;;
+2746;HEAVY CHEVRON SNOWFLAKE;So;0;ON;;;;;N;;;;;
+2747;SPARKLE;So;0;ON;;;;;N;;;;;
+2748;HEAVY SPARKLE;So;0;ON;;;;;N;;;;;
+2749;BALLOON-SPOKED ASTERISK;So;0;ON;;;;;N;;;;;
+274A;EIGHT TEARDROP-SPOKED PROPELLER ASTERISK;So;0;ON;;;;;N;;;;;
+274B;HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK;So;0;ON;;;;;N;;;;;
+274D;SHADOWED WHITE CIRCLE;So;0;ON;;;;;N;;;;;
+274F;LOWER RIGHT DROP-SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;;
+2750;UPPER RIGHT DROP-SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;;
+2751;LOWER RIGHT SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;;
+2752;UPPER RIGHT SHADOWED WHITE SQUARE;So;0;ON;;;;;N;;;;;
+2756;BLACK DIAMOND MINUS WHITE X;So;0;ON;;;;;N;;;;;
+2758;LIGHT VERTICAL BAR;So;0;ON;;;;;N;;;;;
+2759;MEDIUM VERTICAL BAR;So;0;ON;;;;;N;;;;;
+275A;HEAVY VERTICAL BAR;So;0;ON;;;;;N;;;;;
+275B;HEAVY SINGLE TURNED COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+275C;HEAVY SINGLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+275D;HEAVY DOUBLE TURNED COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+275E;HEAVY DOUBLE COMMA QUOTATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+2761;CURVED STEM PARAGRAPH SIGN ORNAMENT;So;0;ON;;;;;N;;;;;
+2762;HEAVY EXCLAMATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+2763;HEAVY HEART EXCLAMATION MARK ORNAMENT;So;0;ON;;;;;N;;;;;
+2764;HEAVY BLACK HEART;So;0;ON;;;;;N;;;;;
+2765;ROTATED HEAVY BLACK HEART BULLET;So;0;ON;;;;;N;;;;;
+2766;FLORAL HEART;So;0;ON;;;;;N;;;;;
+2767;ROTATED FLORAL HEART BULLET;So;0;ON;;;;;N;;;;;
+2768;MEDIUM LEFT PARENTHESIS ORNAMENT;Ps;0;ON;;;;;Y;;;;;
+2769;MEDIUM RIGHT PARENTHESIS ORNAMENT;Pe;0;ON;;;;;Y;;;;;
+276A;MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT;Ps;0;ON;;;;;Y;;;;;
+276B;MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT;Pe;0;ON;;;;;Y;;;;;
+276C;MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;;
+276D;MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;;
+276E;HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT;Ps;0;ON;;;;;Y;;;;;
+276F;HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT;Pe;0;ON;;;;;Y;;;;;
+2770;HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;;
+2771;HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;;
+2772;LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;;
+2773;LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;;
+2774;MEDIUM LEFT CURLY BRACKET ORNAMENT;Ps;0;ON;;;;;Y;;;;;
+2775;MEDIUM RIGHT CURLY BRACKET ORNAMENT;Pe;0;ON;;;;;Y;;;;;
+2776;DINGBAT NEGATIVE CIRCLED DIGIT ONE;No;0;ON;;;1;1;N;INVERSE CIRCLED DIGIT ONE;;;;
+2777;DINGBAT NEGATIVE CIRCLED DIGIT TWO;No;0;ON;;;2;2;N;INVERSE CIRCLED DIGIT TWO;;;;
+2778;DINGBAT NEGATIVE CIRCLED DIGIT THREE;No;0;ON;;;3;3;N;INVERSE CIRCLED DIGIT THREE;;;;
+2779;DINGBAT NEGATIVE CIRCLED DIGIT FOUR;No;0;ON;;;4;4;N;INVERSE CIRCLED DIGIT FOUR;;;;
+277A;DINGBAT NEGATIVE CIRCLED DIGIT FIVE;No;0;ON;;;5;5;N;INVERSE CIRCLED DIGIT FIVE;;;;
+277B;DINGBAT NEGATIVE CIRCLED DIGIT SIX;No;0;ON;;;6;6;N;INVERSE CIRCLED DIGIT SIX;;;;
+277C;DINGBAT NEGATIVE CIRCLED DIGIT SEVEN;No;0;ON;;;7;7;N;INVERSE CIRCLED DIGIT SEVEN;;;;
+277D;DINGBAT NEGATIVE CIRCLED DIGIT EIGHT;No;0;ON;;;8;8;N;INVERSE CIRCLED DIGIT EIGHT;;;;
+277E;DINGBAT NEGATIVE CIRCLED DIGIT NINE;No;0;ON;;;9;9;N;INVERSE CIRCLED DIGIT NINE;;;;
+277F;DINGBAT NEGATIVE CIRCLED NUMBER TEN;No;0;ON;;;;10;N;INVERSE CIRCLED NUMBER TEN;;;;
+2780;DINGBAT CIRCLED SANS-SERIF DIGIT ONE;No;0;ON;;;1;1;N;CIRCLED SANS-SERIF DIGIT ONE;;;;
+2781;DINGBAT CIRCLED SANS-SERIF DIGIT TWO;No;0;ON;;;2;2;N;CIRCLED SANS-SERIF DIGIT TWO;;;;
+2782;DINGBAT CIRCLED SANS-SERIF DIGIT THREE;No;0;ON;;;3;3;N;CIRCLED SANS-SERIF DIGIT THREE;;;;
+2783;DINGBAT CIRCLED SANS-SERIF DIGIT FOUR;No;0;ON;;;4;4;N;CIRCLED SANS-SERIF DIGIT FOUR;;;;
+2784;DINGBAT CIRCLED SANS-SERIF DIGIT FIVE;No;0;ON;;;5;5;N;CIRCLED SANS-SERIF DIGIT FIVE;;;;
+2785;DINGBAT CIRCLED SANS-SERIF DIGIT SIX;No;0;ON;;;6;6;N;CIRCLED SANS-SERIF DIGIT SIX;;;;
+2786;DINGBAT CIRCLED SANS-SERIF DIGIT SEVEN;No;0;ON;;;7;7;N;CIRCLED SANS-SERIF DIGIT SEVEN;;;;
+2787;DINGBAT CIRCLED SANS-SERIF DIGIT EIGHT;No;0;ON;;;8;8;N;CIRCLED SANS-SERIF DIGIT EIGHT;;;;
+2788;DINGBAT CIRCLED SANS-SERIF DIGIT NINE;No;0;ON;;;9;9;N;CIRCLED SANS-SERIF DIGIT NINE;;;;
+2789;DINGBAT CIRCLED SANS-SERIF NUMBER TEN;No;0;ON;;;;10;N;CIRCLED SANS-SERIF NUMBER TEN;;;;
+278A;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ONE;No;0;ON;;;1;1;N;INVERSE CIRCLED SANS-SERIF DIGIT ONE;;;;
+278B;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT TWO;No;0;ON;;;2;2;N;INVERSE CIRCLED SANS-SERIF DIGIT TWO;;;;
+278C;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT THREE;No;0;ON;;;3;3;N;INVERSE CIRCLED SANS-SERIF DIGIT THREE;;;;
+278D;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FOUR;No;0;ON;;;4;4;N;INVERSE CIRCLED SANS-SERIF DIGIT FOUR;;;;
+278E;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT FIVE;No;0;ON;;;5;5;N;INVERSE CIRCLED SANS-SERIF DIGIT FIVE;;;;
+278F;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SIX;No;0;ON;;;6;6;N;INVERSE CIRCLED SANS-SERIF DIGIT SIX;;;;
+2790;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT SEVEN;No;0;ON;;;7;7;N;INVERSE CIRCLED SANS-SERIF DIGIT SEVEN;;;;
+2791;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT EIGHT;No;0;ON;;;8;8;N;INVERSE CIRCLED SANS-SERIF DIGIT EIGHT;;;;
+2792;DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT NINE;No;0;ON;;;9;9;N;INVERSE CIRCLED SANS-SERIF DIGIT NINE;;;;
+2793;DINGBAT NEGATIVE CIRCLED SANS-SERIF NUMBER TEN;No;0;ON;;;;10;N;INVERSE CIRCLED SANS-SERIF NUMBER TEN;;;;
+2794;HEAVY WIDE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY WIDE-HEADED RIGHT ARROW;;;;
+2798;HEAVY SOUTH EAST ARROW;So;0;ON;;;;;N;HEAVY LOWER RIGHT ARROW;;;;
+2799;HEAVY RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY RIGHT ARROW;;;;
+279A;HEAVY NORTH EAST ARROW;So;0;ON;;;;;N;HEAVY UPPER RIGHT ARROW;;;;
+279B;DRAFTING POINT RIGHTWARDS ARROW;So;0;ON;;;;;N;DRAFTING POINT RIGHT ARROW;;;;
+279C;HEAVY ROUND-TIPPED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY ROUND-TIPPED RIGHT ARROW;;;;
+279D;TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;TRIANGLE-HEADED RIGHT ARROW;;;;
+279E;HEAVY TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY TRIANGLE-HEADED RIGHT ARROW;;;;
+279F;DASHED TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;DASHED TRIANGLE-HEADED RIGHT ARROW;;;;
+27A0;HEAVY DASHED TRIANGLE-HEADED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY DASHED TRIANGLE-HEADED RIGHT ARROW;;;;
+27A1;BLACK RIGHTWARDS ARROW;So;0;ON;;;;;N;BLACK RIGHT ARROW;;;;
+27A2;THREE-D TOP-LIGHTED RIGHTWARDS ARROWHEAD;So;0;ON;;;;;N;THREE-D TOP-LIGHTED RIGHT ARROWHEAD;;;;
+27A3;THREE-D BOTTOM-LIGHTED RIGHTWARDS ARROWHEAD;So;0;ON;;;;;N;THREE-D BOTTOM-LIGHTED RIGHT ARROWHEAD;;;;
+27A4;BLACK RIGHTWARDS ARROWHEAD;So;0;ON;;;;;N;BLACK RIGHT ARROWHEAD;;;;
+27A5;HEAVY BLACK CURVED DOWNWARDS AND RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY BLACK CURVED DOWN AND RIGHT ARROW;;;;
+27A6;HEAVY BLACK CURVED UPWARDS AND RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY BLACK CURVED UP AND RIGHT ARROW;;;;
+27A7;SQUAT BLACK RIGHTWARDS ARROW;So;0;ON;;;;;N;SQUAT BLACK RIGHT ARROW;;;;
+27A8;HEAVY CONCAVE-POINTED BLACK RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY CONCAVE-POINTED BLACK RIGHT ARROW;;;;
+27A9;RIGHT-SHADED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;RIGHT-SHADED WHITE RIGHT ARROW;;;;
+27AA;LEFT-SHADED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;LEFT-SHADED WHITE RIGHT ARROW;;;;
+27AB;BACK-TILTED SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;BACK-TILTED SHADOWED WHITE RIGHT ARROW;;;;
+27AC;FRONT-TILTED SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;FRONT-TILTED SHADOWED WHITE RIGHT ARROW;;;;
+27AD;HEAVY LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY LOWER RIGHT-SHADOWED WHITE RIGHT ARROW;;;;
+27AE;HEAVY UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY UPPER RIGHT-SHADOWED WHITE RIGHT ARROW;;;;
+27AF;NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHT ARROW;;;;
+27B1;NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHT ARROW;;;;
+27B2;CIRCLED HEAVY WHITE RIGHTWARDS ARROW;So;0;ON;;;;;N;CIRCLED HEAVY WHITE RIGHT ARROW;;;;
+27B3;WHITE-FEATHERED RIGHTWARDS ARROW;So;0;ON;;;;;N;WHITE-FEATHERED RIGHT ARROW;;;;
+27B4;BLACK-FEATHERED SOUTH EAST ARROW;So;0;ON;;;;;N;BLACK-FEATHERED LOWER RIGHT ARROW;;;;
+27B5;BLACK-FEATHERED RIGHTWARDS ARROW;So;0;ON;;;;;N;BLACK-FEATHERED RIGHT ARROW;;;;
+27B6;BLACK-FEATHERED NORTH EAST ARROW;So;0;ON;;;;;N;BLACK-FEATHERED UPPER RIGHT ARROW;;;;
+27B7;HEAVY BLACK-FEATHERED SOUTH EAST ARROW;So;0;ON;;;;;N;HEAVY BLACK-FEATHERED LOWER RIGHT ARROW;;;;
+27B8;HEAVY BLACK-FEATHERED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY BLACK-FEATHERED RIGHT ARROW;;;;
+27B9;HEAVY BLACK-FEATHERED NORTH EAST ARROW;So;0;ON;;;;;N;HEAVY BLACK-FEATHERED UPPER RIGHT ARROW;;;;
+27BA;TEARDROP-BARBED RIGHTWARDS ARROW;So;0;ON;;;;;N;TEARDROP-BARBED RIGHT ARROW;;;;
+27BB;HEAVY TEARDROP-SHANKED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY TEARDROP-SHANKED RIGHT ARROW;;;;
+27BC;WEDGE-TAILED RIGHTWARDS ARROW;So;0;ON;;;;;N;WEDGE-TAILED RIGHT ARROW;;;;
+27BD;HEAVY WEDGE-TAILED RIGHTWARDS ARROW;So;0;ON;;;;;N;HEAVY WEDGE-TAILED RIGHT ARROW;;;;
+27BE;OPEN-OUTLINED RIGHTWARDS ARROW;So;0;ON;;;;;N;OPEN-OUTLINED RIGHT ARROW;;;;
+27D0;WHITE DIAMOND WITH CENTRED DOT;Sm;0;ON;;;;;N;;;;;
+27D1;AND WITH DOT;Sm;0;ON;;;;;N;;;;;
+27D2;ELEMENT OF OPENING UPWARDS;Sm;0;ON;;;;;N;;;;;
+27D3;LOWER RIGHT CORNER WITH DOT;Sm;0;ON;;;;;Y;;;;;
+27D4;UPPER LEFT CORNER WITH DOT;Sm;0;ON;;;;;Y;;;;;
+27D5;LEFT OUTER JOIN;Sm;0;ON;;;;;Y;;;;;
+27D6;RIGHT OUTER JOIN;Sm;0;ON;;;;;Y;;;;;
+27D7;FULL OUTER JOIN;Sm;0;ON;;;;;N;;;;;
+27D8;LARGE UP TACK;Sm;0;ON;;;;;N;;;;;
+27D9;LARGE DOWN TACK;Sm;0;ON;;;;;N;;;;;
+27DA;LEFT AND RIGHT DOUBLE TURNSTILE;Sm;0;ON;;;;;N;;;;;
+27DB;LEFT AND RIGHT TACK;Sm;0;ON;;;;;N;;;;;
+27DC;LEFT MULTIMAP;Sm;0;ON;;;;;Y;;;;;
+27DD;LONG RIGHT TACK;Sm;0;ON;;;;;Y;;;;;
+27DE;LONG LEFT TACK;Sm;0;ON;;;;;Y;;;;;
+27DF;UP TACK WITH CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;;
+27E0;LOZENGE DIVIDED BY HORIZONTAL RULE;Sm;0;ON;;;;;N;;;;;
+27E1;WHITE CONCAVE-SIDED DIAMOND;Sm;0;ON;;;;;N;;;;;
+27E2;WHITE CONCAVE-SIDED DIAMOND WITH LEFTWARDS TICK;Sm;0;ON;;;;;Y;;;;;
+27E3;WHITE CONCAVE-SIDED DIAMOND WITH RIGHTWARDS TICK;Sm;0;ON;;;;;Y;;;;;
+27E4;WHITE SQUARE WITH LEFTWARDS TICK;Sm;0;ON;;;;;Y;;;;;
+27E5;WHITE SQUARE WITH RIGHTWARDS TICK;Sm;0;ON;;;;;Y;;;;;
+27E6;MATHEMATICAL LEFT WHITE SQUARE BRACKET;Ps;0;ON;;;;;Y;;;;;
+27E7;MATHEMATICAL RIGHT WHITE SQUARE BRACKET;Pe;0;ON;;;;;Y;;;;;
+27E8;MATHEMATICAL LEFT ANGLE BRACKET;Ps;0;ON;;;;;Y;;;;;
+27E9;MATHEMATICAL RIGHT ANGLE BRACKET;Pe;0;ON;;;;;Y;;;;;
+27EA;MATHEMATICAL LEFT DOUBLE ANGLE BRACKET;Ps;0;ON;;;;;Y;;;;;
+27EB;MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET;Pe;0;ON;;;;;Y;;;;;
+27F0;UPWARDS QUADRUPLE ARROW;Sm;0;ON;;;;;N;;;;;
+27F1;DOWNWARDS QUADRUPLE ARROW;Sm;0;ON;;;;;N;;;;;
+27F2;ANTICLOCKWISE GAPPED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;;
+27F3;CLOCKWISE GAPPED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;;
+27F4;RIGHT ARROW WITH CIRCLED PLUS;Sm;0;ON;;;;;N;;;;;
+27F5;LONG LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+27F6;LONG RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+27F7;LONG LEFT RIGHT ARROW;Sm;0;ON;;;;;N;;;;;
+27F8;LONG LEFTWARDS DOUBLE ARROW;Sm;0;ON;;;;;N;;;;;
+27F9;LONG RIGHTWARDS DOUBLE ARROW;Sm;0;ON;;;;;N;;;;;
+27FA;LONG LEFT RIGHT DOUBLE ARROW;Sm;0;ON;;;;;N;;;;;
+27FB;LONG LEFTWARDS ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+27FC;LONG RIGHTWARDS ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+27FD;LONG LEFTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+27FE;LONG RIGHTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+27FF;LONG RIGHTWARDS SQUIGGLE ARROW;Sm;0;ON;;;;;N;;;;;
+2800;BRAILLE PATTERN BLANK;So;0;ON;;;;;N;;;;;
+2801;BRAILLE PATTERN DOTS-1;So;0;ON;;;;;N;;;;;
+2802;BRAILLE PATTERN DOTS-2;So;0;ON;;;;;N;;;;;
+2803;BRAILLE PATTERN DOTS-12;So;0;ON;;;;;N;;;;;
+2804;BRAILLE PATTERN DOTS-3;So;0;ON;;;;;N;;;;;
+2805;BRAILLE PATTERN DOTS-13;So;0;ON;;;;;N;;;;;
+2806;BRAILLE PATTERN DOTS-23;So;0;ON;;;;;N;;;;;
+2807;BRAILLE PATTERN DOTS-123;So;0;ON;;;;;N;;;;;
+2808;BRAILLE PATTERN DOTS-4;So;0;ON;;;;;N;;;;;
+2809;BRAILLE PATTERN DOTS-14;So;0;ON;;;;;N;;;;;
+280A;BRAILLE PATTERN DOTS-24;So;0;ON;;;;;N;;;;;
+280B;BRAILLE PATTERN DOTS-124;So;0;ON;;;;;N;;;;;
+280C;BRAILLE PATTERN DOTS-34;So;0;ON;;;;;N;;;;;
+280D;BRAILLE PATTERN DOTS-134;So;0;ON;;;;;N;;;;;
+280E;BRAILLE PATTERN DOTS-234;So;0;ON;;;;;N;;;;;
+280F;BRAILLE PATTERN DOTS-1234;So;0;ON;;;;;N;;;;;
+2810;BRAILLE PATTERN DOTS-5;So;0;ON;;;;;N;;;;;
+2811;BRAILLE PATTERN DOTS-15;So;0;ON;;;;;N;;;;;
+2812;BRAILLE PATTERN DOTS-25;So;0;ON;;;;;N;;;;;
+2813;BRAILLE PATTERN DOTS-125;So;0;ON;;;;;N;;;;;
+2814;BRAILLE PATTERN DOTS-35;So;0;ON;;;;;N;;;;;
+2815;BRAILLE PATTERN DOTS-135;So;0;ON;;;;;N;;;;;
+2816;BRAILLE PATTERN DOTS-235;So;0;ON;;;;;N;;;;;
+2817;BRAILLE PATTERN DOTS-1235;So;0;ON;;;;;N;;;;;
+2818;BRAILLE PATTERN DOTS-45;So;0;ON;;;;;N;;;;;
+2819;BRAILLE PATTERN DOTS-145;So;0;ON;;;;;N;;;;;
+281A;BRAILLE PATTERN DOTS-245;So;0;ON;;;;;N;;;;;
+281B;BRAILLE PATTERN DOTS-1245;So;0;ON;;;;;N;;;;;
+281C;BRAILLE PATTERN DOTS-345;So;0;ON;;;;;N;;;;;
+281D;BRAILLE PATTERN DOTS-1345;So;0;ON;;;;;N;;;;;
+281E;BRAILLE PATTERN DOTS-2345;So;0;ON;;;;;N;;;;;
+281F;BRAILLE PATTERN DOTS-12345;So;0;ON;;;;;N;;;;;
+2820;BRAILLE PATTERN DOTS-6;So;0;ON;;;;;N;;;;;
+2821;BRAILLE PATTERN DOTS-16;So;0;ON;;;;;N;;;;;
+2822;BRAILLE PATTERN DOTS-26;So;0;ON;;;;;N;;;;;
+2823;BRAILLE PATTERN DOTS-126;So;0;ON;;;;;N;;;;;
+2824;BRAILLE PATTERN DOTS-36;So;0;ON;;;;;N;;;;;
+2825;BRAILLE PATTERN DOTS-136;So;0;ON;;;;;N;;;;;
+2826;BRAILLE PATTERN DOTS-236;So;0;ON;;;;;N;;;;;
+2827;BRAILLE PATTERN DOTS-1236;So;0;ON;;;;;N;;;;;
+2828;BRAILLE PATTERN DOTS-46;So;0;ON;;;;;N;;;;;
+2829;BRAILLE PATTERN DOTS-146;So;0;ON;;;;;N;;;;;
+282A;BRAILLE PATTERN DOTS-246;So;0;ON;;;;;N;;;;;
+282B;BRAILLE PATTERN DOTS-1246;So;0;ON;;;;;N;;;;;
+282C;BRAILLE PATTERN DOTS-346;So;0;ON;;;;;N;;;;;
+282D;BRAILLE PATTERN DOTS-1346;So;0;ON;;;;;N;;;;;
+282E;BRAILLE PATTERN DOTS-2346;So;0;ON;;;;;N;;;;;
+282F;BRAILLE PATTERN DOTS-12346;So;0;ON;;;;;N;;;;;
+2830;BRAILLE PATTERN DOTS-56;So;0;ON;;;;;N;;;;;
+2831;BRAILLE PATTERN DOTS-156;So;0;ON;;;;;N;;;;;
+2832;BRAILLE PATTERN DOTS-256;So;0;ON;;;;;N;;;;;
+2833;BRAILLE PATTERN DOTS-1256;So;0;ON;;;;;N;;;;;
+2834;BRAILLE PATTERN DOTS-356;So;0;ON;;;;;N;;;;;
+2835;BRAILLE PATTERN DOTS-1356;So;0;ON;;;;;N;;;;;
+2836;BRAILLE PATTERN DOTS-2356;So;0;ON;;;;;N;;;;;
+2837;BRAILLE PATTERN DOTS-12356;So;0;ON;;;;;N;;;;;
+2838;BRAILLE PATTERN DOTS-456;So;0;ON;;;;;N;;;;;
+2839;BRAILLE PATTERN DOTS-1456;So;0;ON;;;;;N;;;;;
+283A;BRAILLE PATTERN DOTS-2456;So;0;ON;;;;;N;;;;;
+283B;BRAILLE PATTERN DOTS-12456;So;0;ON;;;;;N;;;;;
+283C;BRAILLE PATTERN DOTS-3456;So;0;ON;;;;;N;;;;;
+283D;BRAILLE PATTERN DOTS-13456;So;0;ON;;;;;N;;;;;
+283E;BRAILLE PATTERN DOTS-23456;So;0;ON;;;;;N;;;;;
+283F;BRAILLE PATTERN DOTS-123456;So;0;ON;;;;;N;;;;;
+2840;BRAILLE PATTERN DOTS-7;So;0;ON;;;;;N;;;;;
+2841;BRAILLE PATTERN DOTS-17;So;0;ON;;;;;N;;;;;
+2842;BRAILLE PATTERN DOTS-27;So;0;ON;;;;;N;;;;;
+2843;BRAILLE PATTERN DOTS-127;So;0;ON;;;;;N;;;;;
+2844;BRAILLE PATTERN DOTS-37;So;0;ON;;;;;N;;;;;
+2845;BRAILLE PATTERN DOTS-137;So;0;ON;;;;;N;;;;;
+2846;BRAILLE PATTERN DOTS-237;So;0;ON;;;;;N;;;;;
+2847;BRAILLE PATTERN DOTS-1237;So;0;ON;;;;;N;;;;;
+2848;BRAILLE PATTERN DOTS-47;So;0;ON;;;;;N;;;;;
+2849;BRAILLE PATTERN DOTS-147;So;0;ON;;;;;N;;;;;
+284A;BRAILLE PATTERN DOTS-247;So;0;ON;;;;;N;;;;;
+284B;BRAILLE PATTERN DOTS-1247;So;0;ON;;;;;N;;;;;
+284C;BRAILLE PATTERN DOTS-347;So;0;ON;;;;;N;;;;;
+284D;BRAILLE PATTERN DOTS-1347;So;0;ON;;;;;N;;;;;
+284E;BRAILLE PATTERN DOTS-2347;So;0;ON;;;;;N;;;;;
+284F;BRAILLE PATTERN DOTS-12347;So;0;ON;;;;;N;;;;;
+2850;BRAILLE PATTERN DOTS-57;So;0;ON;;;;;N;;;;;
+2851;BRAILLE PATTERN DOTS-157;So;0;ON;;;;;N;;;;;
+2852;BRAILLE PATTERN DOTS-257;So;0;ON;;;;;N;;;;;
+2853;BRAILLE PATTERN DOTS-1257;So;0;ON;;;;;N;;;;;
+2854;BRAILLE PATTERN DOTS-357;So;0;ON;;;;;N;;;;;
+2855;BRAILLE PATTERN DOTS-1357;So;0;ON;;;;;N;;;;;
+2856;BRAILLE PATTERN DOTS-2357;So;0;ON;;;;;N;;;;;
+2857;BRAILLE PATTERN DOTS-12357;So;0;ON;;;;;N;;;;;
+2858;BRAILLE PATTERN DOTS-457;So;0;ON;;;;;N;;;;;
+2859;BRAILLE PATTERN DOTS-1457;So;0;ON;;;;;N;;;;;
+285A;BRAILLE PATTERN DOTS-2457;So;0;ON;;;;;N;;;;;
+285B;BRAILLE PATTERN DOTS-12457;So;0;ON;;;;;N;;;;;
+285C;BRAILLE PATTERN DOTS-3457;So;0;ON;;;;;N;;;;;
+285D;BRAILLE PATTERN DOTS-13457;So;0;ON;;;;;N;;;;;
+285E;BRAILLE PATTERN DOTS-23457;So;0;ON;;;;;N;;;;;
+285F;BRAILLE PATTERN DOTS-123457;So;0;ON;;;;;N;;;;;
+2860;BRAILLE PATTERN DOTS-67;So;0;ON;;;;;N;;;;;
+2861;BRAILLE PATTERN DOTS-167;So;0;ON;;;;;N;;;;;
+2862;BRAILLE PATTERN DOTS-267;So;0;ON;;;;;N;;;;;
+2863;BRAILLE PATTERN DOTS-1267;So;0;ON;;;;;N;;;;;
+2864;BRAILLE PATTERN DOTS-367;So;0;ON;;;;;N;;;;;
+2865;BRAILLE PATTERN DOTS-1367;So;0;ON;;;;;N;;;;;
+2866;BRAILLE PATTERN DOTS-2367;So;0;ON;;;;;N;;;;;
+2867;BRAILLE PATTERN DOTS-12367;So;0;ON;;;;;N;;;;;
+2868;BRAILLE PATTERN DOTS-467;So;0;ON;;;;;N;;;;;
+2869;BRAILLE PATTERN DOTS-1467;So;0;ON;;;;;N;;;;;
+286A;BRAILLE PATTERN DOTS-2467;So;0;ON;;;;;N;;;;;
+286B;BRAILLE PATTERN DOTS-12467;So;0;ON;;;;;N;;;;;
+286C;BRAILLE PATTERN DOTS-3467;So;0;ON;;;;;N;;;;;
+286D;BRAILLE PATTERN DOTS-13467;So;0;ON;;;;;N;;;;;
+286E;BRAILLE PATTERN DOTS-23467;So;0;ON;;;;;N;;;;;
+286F;BRAILLE PATTERN DOTS-123467;So;0;ON;;;;;N;;;;;
+2870;BRAILLE PATTERN DOTS-567;So;0;ON;;;;;N;;;;;
+2871;BRAILLE PATTERN DOTS-1567;So;0;ON;;;;;N;;;;;
+2872;BRAILLE PATTERN DOTS-2567;So;0;ON;;;;;N;;;;;
+2873;BRAILLE PATTERN DOTS-12567;So;0;ON;;;;;N;;;;;
+2874;BRAILLE PATTERN DOTS-3567;So;0;ON;;;;;N;;;;;
+2875;BRAILLE PATTERN DOTS-13567;So;0;ON;;;;;N;;;;;
+2876;BRAILLE PATTERN DOTS-23567;So;0;ON;;;;;N;;;;;
+2877;BRAILLE PATTERN DOTS-123567;So;0;ON;;;;;N;;;;;
+2878;BRAILLE PATTERN DOTS-4567;So;0;ON;;;;;N;;;;;
+2879;BRAILLE PATTERN DOTS-14567;So;0;ON;;;;;N;;;;;
+287A;BRAILLE PATTERN DOTS-24567;So;0;ON;;;;;N;;;;;
+287B;BRAILLE PATTERN DOTS-124567;So;0;ON;;;;;N;;;;;
+287C;BRAILLE PATTERN DOTS-34567;So;0;ON;;;;;N;;;;;
+287D;BRAILLE PATTERN DOTS-134567;So;0;ON;;;;;N;;;;;
+287E;BRAILLE PATTERN DOTS-234567;So;0;ON;;;;;N;;;;;
+287F;BRAILLE PATTERN DOTS-1234567;So;0;ON;;;;;N;;;;;
+2880;BRAILLE PATTERN DOTS-8;So;0;ON;;;;;N;;;;;
+2881;BRAILLE PATTERN DOTS-18;So;0;ON;;;;;N;;;;;
+2882;BRAILLE PATTERN DOTS-28;So;0;ON;;;;;N;;;;;
+2883;BRAILLE PATTERN DOTS-128;So;0;ON;;;;;N;;;;;
+2884;BRAILLE PATTERN DOTS-38;So;0;ON;;;;;N;;;;;
+2885;BRAILLE PATTERN DOTS-138;So;0;ON;;;;;N;;;;;
+2886;BRAILLE PATTERN DOTS-238;So;0;ON;;;;;N;;;;;
+2887;BRAILLE PATTERN DOTS-1238;So;0;ON;;;;;N;;;;;
+2888;BRAILLE PATTERN DOTS-48;So;0;ON;;;;;N;;;;;
+2889;BRAILLE PATTERN DOTS-148;So;0;ON;;;;;N;;;;;
+288A;BRAILLE PATTERN DOTS-248;So;0;ON;;;;;N;;;;;
+288B;BRAILLE PATTERN DOTS-1248;So;0;ON;;;;;N;;;;;
+288C;BRAILLE PATTERN DOTS-348;So;0;ON;;;;;N;;;;;
+288D;BRAILLE PATTERN DOTS-1348;So;0;ON;;;;;N;;;;;
+288E;BRAILLE PATTERN DOTS-2348;So;0;ON;;;;;N;;;;;
+288F;BRAILLE PATTERN DOTS-12348;So;0;ON;;;;;N;;;;;
+2890;BRAILLE PATTERN DOTS-58;So;0;ON;;;;;N;;;;;
+2891;BRAILLE PATTERN DOTS-158;So;0;ON;;;;;N;;;;;
+2892;BRAILLE PATTERN DOTS-258;So;0;ON;;;;;N;;;;;
+2893;BRAILLE PATTERN DOTS-1258;So;0;ON;;;;;N;;;;;
+2894;BRAILLE PATTERN DOTS-358;So;0;ON;;;;;N;;;;;
+2895;BRAILLE PATTERN DOTS-1358;So;0;ON;;;;;N;;;;;
+2896;BRAILLE PATTERN DOTS-2358;So;0;ON;;;;;N;;;;;
+2897;BRAILLE PATTERN DOTS-12358;So;0;ON;;;;;N;;;;;
+2898;BRAILLE PATTERN DOTS-458;So;0;ON;;;;;N;;;;;
+2899;BRAILLE PATTERN DOTS-1458;So;0;ON;;;;;N;;;;;
+289A;BRAILLE PATTERN DOTS-2458;So;0;ON;;;;;N;;;;;
+289B;BRAILLE PATTERN DOTS-12458;So;0;ON;;;;;N;;;;;
+289C;BRAILLE PATTERN DOTS-3458;So;0;ON;;;;;N;;;;;
+289D;BRAILLE PATTERN DOTS-13458;So;0;ON;;;;;N;;;;;
+289E;BRAILLE PATTERN DOTS-23458;So;0;ON;;;;;N;;;;;
+289F;BRAILLE PATTERN DOTS-123458;So;0;ON;;;;;N;;;;;
+28A0;BRAILLE PATTERN DOTS-68;So;0;ON;;;;;N;;;;;
+28A1;BRAILLE PATTERN DOTS-168;So;0;ON;;;;;N;;;;;
+28A2;BRAILLE PATTERN DOTS-268;So;0;ON;;;;;N;;;;;
+28A3;BRAILLE PATTERN DOTS-1268;So;0;ON;;;;;N;;;;;
+28A4;BRAILLE PATTERN DOTS-368;So;0;ON;;;;;N;;;;;
+28A5;BRAILLE PATTERN DOTS-1368;So;0;ON;;;;;N;;;;;
+28A6;BRAILLE PATTERN DOTS-2368;So;0;ON;;;;;N;;;;;
+28A7;BRAILLE PATTERN DOTS-12368;So;0;ON;;;;;N;;;;;
+28A8;BRAILLE PATTERN DOTS-468;So;0;ON;;;;;N;;;;;
+28A9;BRAILLE PATTERN DOTS-1468;So;0;ON;;;;;N;;;;;
+28AA;BRAILLE PATTERN DOTS-2468;So;0;ON;;;;;N;;;;;
+28AB;BRAILLE PATTERN DOTS-12468;So;0;ON;;;;;N;;;;;
+28AC;BRAILLE PATTERN DOTS-3468;So;0;ON;;;;;N;;;;;
+28AD;BRAILLE PATTERN DOTS-13468;So;0;ON;;;;;N;;;;;
+28AE;BRAILLE PATTERN DOTS-23468;So;0;ON;;;;;N;;;;;
+28AF;BRAILLE PATTERN DOTS-123468;So;0;ON;;;;;N;;;;;
+28B0;BRAILLE PATTERN DOTS-568;So;0;ON;;;;;N;;;;;
+28B1;BRAILLE PATTERN DOTS-1568;So;0;ON;;;;;N;;;;;
+28B2;BRAILLE PATTERN DOTS-2568;So;0;ON;;;;;N;;;;;
+28B3;BRAILLE PATTERN DOTS-12568;So;0;ON;;;;;N;;;;;
+28B4;BRAILLE PATTERN DOTS-3568;So;0;ON;;;;;N;;;;;
+28B5;BRAILLE PATTERN DOTS-13568;So;0;ON;;;;;N;;;;;
+28B6;BRAILLE PATTERN DOTS-23568;So;0;ON;;;;;N;;;;;
+28B7;BRAILLE PATTERN DOTS-123568;So;0;ON;;;;;N;;;;;
+28B8;BRAILLE PATTERN DOTS-4568;So;0;ON;;;;;N;;;;;
+28B9;BRAILLE PATTERN DOTS-14568;So;0;ON;;;;;N;;;;;
+28BA;BRAILLE PATTERN DOTS-24568;So;0;ON;;;;;N;;;;;
+28BB;BRAILLE PATTERN DOTS-124568;So;0;ON;;;;;N;;;;;
+28BC;BRAILLE PATTERN DOTS-34568;So;0;ON;;;;;N;;;;;
+28BD;BRAILLE PATTERN DOTS-134568;So;0;ON;;;;;N;;;;;
+28BE;BRAILLE PATTERN DOTS-234568;So;0;ON;;;;;N;;;;;
+28BF;BRAILLE PATTERN DOTS-1234568;So;0;ON;;;;;N;;;;;
+28C0;BRAILLE PATTERN DOTS-78;So;0;ON;;;;;N;;;;;
+28C1;BRAILLE PATTERN DOTS-178;So;0;ON;;;;;N;;;;;
+28C2;BRAILLE PATTERN DOTS-278;So;0;ON;;;;;N;;;;;
+28C3;BRAILLE PATTERN DOTS-1278;So;0;ON;;;;;N;;;;;
+28C4;BRAILLE PATTERN DOTS-378;So;0;ON;;;;;N;;;;;
+28C5;BRAILLE PATTERN DOTS-1378;So;0;ON;;;;;N;;;;;
+28C6;BRAILLE PATTERN DOTS-2378;So;0;ON;;;;;N;;;;;
+28C7;BRAILLE PATTERN DOTS-12378;So;0;ON;;;;;N;;;;;
+28C8;BRAILLE PATTERN DOTS-478;So;0;ON;;;;;N;;;;;
+28C9;BRAILLE PATTERN DOTS-1478;So;0;ON;;;;;N;;;;;
+28CA;BRAILLE PATTERN DOTS-2478;So;0;ON;;;;;N;;;;;
+28CB;BRAILLE PATTERN DOTS-12478;So;0;ON;;;;;N;;;;;
+28CC;BRAILLE PATTERN DOTS-3478;So;0;ON;;;;;N;;;;;
+28CD;BRAILLE PATTERN DOTS-13478;So;0;ON;;;;;N;;;;;
+28CE;BRAILLE PATTERN DOTS-23478;So;0;ON;;;;;N;;;;;
+28CF;BRAILLE PATTERN DOTS-123478;So;0;ON;;;;;N;;;;;
+28D0;BRAILLE PATTERN DOTS-578;So;0;ON;;;;;N;;;;;
+28D1;BRAILLE PATTERN DOTS-1578;So;0;ON;;;;;N;;;;;
+28D2;BRAILLE PATTERN DOTS-2578;So;0;ON;;;;;N;;;;;
+28D3;BRAILLE PATTERN DOTS-12578;So;0;ON;;;;;N;;;;;
+28D4;BRAILLE PATTERN DOTS-3578;So;0;ON;;;;;N;;;;;
+28D5;BRAILLE PATTERN DOTS-13578;So;0;ON;;;;;N;;;;;
+28D6;BRAILLE PATTERN DOTS-23578;So;0;ON;;;;;N;;;;;
+28D7;BRAILLE PATTERN DOTS-123578;So;0;ON;;;;;N;;;;;
+28D8;BRAILLE PATTERN DOTS-4578;So;0;ON;;;;;N;;;;;
+28D9;BRAILLE PATTERN DOTS-14578;So;0;ON;;;;;N;;;;;
+28DA;BRAILLE PATTERN DOTS-24578;So;0;ON;;;;;N;;;;;
+28DB;BRAILLE PATTERN DOTS-124578;So;0;ON;;;;;N;;;;;
+28DC;BRAILLE PATTERN DOTS-34578;So;0;ON;;;;;N;;;;;
+28DD;BRAILLE PATTERN DOTS-134578;So;0;ON;;;;;N;;;;;
+28DE;BRAILLE PATTERN DOTS-234578;So;0;ON;;;;;N;;;;;
+28DF;BRAILLE PATTERN DOTS-1234578;So;0;ON;;;;;N;;;;;
+28E0;BRAILLE PATTERN DOTS-678;So;0;ON;;;;;N;;;;;
+28E1;BRAILLE PATTERN DOTS-1678;So;0;ON;;;;;N;;;;;
+28E2;BRAILLE PATTERN DOTS-2678;So;0;ON;;;;;N;;;;;
+28E3;BRAILLE PATTERN DOTS-12678;So;0;ON;;;;;N;;;;;
+28E4;BRAILLE PATTERN DOTS-3678;So;0;ON;;;;;N;;;;;
+28E5;BRAILLE PATTERN DOTS-13678;So;0;ON;;;;;N;;;;;
+28E6;BRAILLE PATTERN DOTS-23678;So;0;ON;;;;;N;;;;;
+28E7;BRAILLE PATTERN DOTS-123678;So;0;ON;;;;;N;;;;;
+28E8;BRAILLE PATTERN DOTS-4678;So;0;ON;;;;;N;;;;;
+28E9;BRAILLE PATTERN DOTS-14678;So;0;ON;;;;;N;;;;;
+28EA;BRAILLE PATTERN DOTS-24678;So;0;ON;;;;;N;;;;;
+28EB;BRAILLE PATTERN DOTS-124678;So;0;ON;;;;;N;;;;;
+28EC;BRAILLE PATTERN DOTS-34678;So;0;ON;;;;;N;;;;;
+28ED;BRAILLE PATTERN DOTS-134678;So;0;ON;;;;;N;;;;;
+28EE;BRAILLE PATTERN DOTS-234678;So;0;ON;;;;;N;;;;;
+28EF;BRAILLE PATTERN DOTS-1234678;So;0;ON;;;;;N;;;;;
+28F0;BRAILLE PATTERN DOTS-5678;So;0;ON;;;;;N;;;;;
+28F1;BRAILLE PATTERN DOTS-15678;So;0;ON;;;;;N;;;;;
+28F2;BRAILLE PATTERN DOTS-25678;So;0;ON;;;;;N;;;;;
+28F3;BRAILLE PATTERN DOTS-125678;So;0;ON;;;;;N;;;;;
+28F4;BRAILLE PATTERN DOTS-35678;So;0;ON;;;;;N;;;;;
+28F5;BRAILLE PATTERN DOTS-135678;So;0;ON;;;;;N;;;;;
+28F6;BRAILLE PATTERN DOTS-235678;So;0;ON;;;;;N;;;;;
+28F7;BRAILLE PATTERN DOTS-1235678;So;0;ON;;;;;N;;;;;
+28F8;BRAILLE PATTERN DOTS-45678;So;0;ON;;;;;N;;;;;
+28F9;BRAILLE PATTERN DOTS-145678;So;0;ON;;;;;N;;;;;
+28FA;BRAILLE PATTERN DOTS-245678;So;0;ON;;;;;N;;;;;
+28FB;BRAILLE PATTERN DOTS-1245678;So;0;ON;;;;;N;;;;;
+28FC;BRAILLE PATTERN DOTS-345678;So;0;ON;;;;;N;;;;;
+28FD;BRAILLE PATTERN DOTS-1345678;So;0;ON;;;;;N;;;;;
+28FE;BRAILLE PATTERN DOTS-2345678;So;0;ON;;;;;N;;;;;
+28FF;BRAILLE PATTERN DOTS-12345678;So;0;ON;;;;;N;;;;;
+2900;RIGHTWARDS TWO-HEADED ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2901;RIGHTWARDS TWO-HEADED ARROW WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2902;LEFTWARDS DOUBLE ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2903;RIGHTWARDS DOUBLE ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2904;LEFT RIGHT DOUBLE ARROW WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2905;RIGHTWARDS TWO-HEADED ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+2906;LEFTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+2907;RIGHTWARDS DOUBLE ARROW FROM BAR;Sm;0;ON;;;;;N;;;;;
+2908;DOWNWARDS ARROW WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;;
+2909;UPWARDS ARROW WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;;
+290A;UPWARDS TRIPLE ARROW;Sm;0;ON;;;;;N;;;;;
+290B;DOWNWARDS TRIPLE ARROW;Sm;0;ON;;;;;N;;;;;
+290C;LEFTWARDS DOUBLE DASH ARROW;Sm;0;ON;;;;;N;;;;;
+290D;RIGHTWARDS DOUBLE DASH ARROW;Sm;0;ON;;;;;N;;;;;
+290E;LEFTWARDS TRIPLE DASH ARROW;Sm;0;ON;;;;;N;;;;;
+290F;RIGHTWARDS TRIPLE DASH ARROW;Sm;0;ON;;;;;N;;;;;
+2910;RIGHTWARDS TWO-HEADED TRIPLE DASH ARROW;Sm;0;ON;;;;;N;;;;;
+2911;RIGHTWARDS ARROW WITH DOTTED STEM;Sm;0;ON;;;;;N;;;;;
+2912;UPWARDS ARROW TO BAR;Sm;0;ON;;;;;N;;;;;
+2913;DOWNWARDS ARROW TO BAR;Sm;0;ON;;;;;N;;;;;
+2914;RIGHTWARDS ARROW WITH TAIL WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2915;RIGHTWARDS ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2916;RIGHTWARDS TWO-HEADED ARROW WITH TAIL;Sm;0;ON;;;;;N;;;;;
+2917;RIGHTWARDS TWO-HEADED ARROW WITH TAIL WITH VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2918;RIGHTWARDS TWO-HEADED ARROW WITH TAIL WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2919;LEFTWARDS ARROW-TAIL;Sm;0;ON;;;;;N;;;;;
+291A;RIGHTWARDS ARROW-TAIL;Sm;0;ON;;;;;N;;;;;
+291B;LEFTWARDS DOUBLE ARROW-TAIL;Sm;0;ON;;;;;N;;;;;
+291C;RIGHTWARDS DOUBLE ARROW-TAIL;Sm;0;ON;;;;;N;;;;;
+291D;LEFTWARDS ARROW TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;;
+291E;RIGHTWARDS ARROW TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;;
+291F;LEFTWARDS ARROW FROM BAR TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;;
+2920;RIGHTWARDS ARROW FROM BAR TO BLACK DIAMOND;Sm;0;ON;;;;;N;;;;;
+2921;NORTH WEST AND SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+2922;NORTH EAST AND SOUTH WEST ARROW;Sm;0;ON;;;;;N;;;;;
+2923;NORTH WEST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;;
+2924;NORTH EAST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;;
+2925;SOUTH EAST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;;
+2926;SOUTH WEST ARROW WITH HOOK;Sm;0;ON;;;;;N;;;;;
+2927;NORTH WEST ARROW AND NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+2928;NORTH EAST ARROW AND SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+2929;SOUTH EAST ARROW AND SOUTH WEST ARROW;Sm;0;ON;;;;;N;;;;;
+292A;SOUTH WEST ARROW AND NORTH WEST ARROW;Sm;0;ON;;;;;N;;;;;
+292B;RISING DIAGONAL CROSSING FALLING DIAGONAL;Sm;0;ON;;;;;N;;;;;
+292C;FALLING DIAGONAL CROSSING RISING DIAGONAL;Sm;0;ON;;;;;N;;;;;
+292D;SOUTH EAST ARROW CROSSING NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+292E;NORTH EAST ARROW CROSSING SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+292F;FALLING DIAGONAL CROSSING NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+2930;RISING DIAGONAL CROSSING SOUTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+2931;NORTH EAST ARROW CROSSING NORTH WEST ARROW;Sm;0;ON;;;;;N;;;;;
+2932;NORTH WEST ARROW CROSSING NORTH EAST ARROW;Sm;0;ON;;;;;N;;;;;
+2933;WAVE ARROW POINTING DIRECTLY RIGHT;Sm;0;ON;;;;;N;;;;;
+2934;ARROW POINTING RIGHTWARDS THEN CURVING UPWARDS;Sm;0;ON;;;;;N;;;;;
+2935;ARROW POINTING RIGHTWARDS THEN CURVING DOWNWARDS;Sm;0;ON;;;;;N;;;;;
+2936;ARROW POINTING DOWNWARDS THEN CURVING LEFTWARDS;Sm;0;ON;;;;;N;;;;;
+2937;ARROW POINTING DOWNWARDS THEN CURVING RIGHTWARDS;Sm;0;ON;;;;;N;;;;;
+2938;RIGHT-SIDE ARC CLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;;
+2939;LEFT-SIDE ARC ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;;
+293A;TOP ARC ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;;
+293B;BOTTOM ARC ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;;
+293C;TOP ARC CLOCKWISE ARROW WITH MINUS;Sm;0;ON;;;;;N;;;;;
+293D;TOP ARC ANTICLOCKWISE ARROW WITH PLUS;Sm;0;ON;;;;;N;;;;;
+293E;LOWER RIGHT SEMICIRCULAR CLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;;
+293F;LOWER LEFT SEMICIRCULAR ANTICLOCKWISE ARROW;Sm;0;ON;;;;;N;;;;;
+2940;ANTICLOCKWISE CLOSED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;;
+2941;CLOCKWISE CLOSED CIRCLE ARROW;Sm;0;ON;;;;;N;;;;;
+2942;RIGHTWARDS ARROW ABOVE SHORT LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2943;LEFTWARDS ARROW ABOVE SHORT RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2944;SHORT RIGHTWARDS ARROW ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2945;RIGHTWARDS ARROW WITH PLUS BELOW;Sm;0;ON;;;;;N;;;;;
+2946;LEFTWARDS ARROW WITH PLUS BELOW;Sm;0;ON;;;;;N;;;;;
+2947;RIGHTWARDS ARROW THROUGH X;Sm;0;ON;;;;;N;;;;;
+2948;LEFT RIGHT ARROW THROUGH SMALL CIRCLE;Sm;0;ON;;;;;N;;;;;
+2949;UPWARDS TWO-HEADED ARROW FROM SMALL CIRCLE;Sm;0;ON;;;;;N;;;;;
+294A;LEFT BARB UP RIGHT BARB DOWN HARPOON;Sm;0;ON;;;;;N;;;;;
+294B;LEFT BARB DOWN RIGHT BARB UP HARPOON;Sm;0;ON;;;;;N;;;;;
+294C;UP BARB RIGHT DOWN BARB LEFT HARPOON;Sm;0;ON;;;;;N;;;;;
+294D;UP BARB LEFT DOWN BARB RIGHT HARPOON;Sm;0;ON;;;;;N;;;;;
+294E;LEFT BARB UP RIGHT BARB UP HARPOON;Sm;0;ON;;;;;N;;;;;
+294F;UP BARB RIGHT DOWN BARB RIGHT HARPOON;Sm;0;ON;;;;;N;;;;;
+2950;LEFT BARB DOWN RIGHT BARB DOWN HARPOON;Sm;0;ON;;;;;N;;;;;
+2951;UP BARB LEFT DOWN BARB LEFT HARPOON;Sm;0;ON;;;;;N;;;;;
+2952;LEFTWARDS HARPOON WITH BARB UP TO BAR;Sm;0;ON;;;;;N;;;;;
+2953;RIGHTWARDS HARPOON WITH BARB UP TO BAR;Sm;0;ON;;;;;N;;;;;
+2954;UPWARDS HARPOON WITH BARB RIGHT TO BAR;Sm;0;ON;;;;;N;;;;;
+2955;DOWNWARDS HARPOON WITH BARB RIGHT TO BAR;Sm;0;ON;;;;;N;;;;;
+2956;LEFTWARDS HARPOON WITH BARB DOWN TO BAR;Sm;0;ON;;;;;N;;;;;
+2957;RIGHTWARDS HARPOON WITH BARB DOWN TO BAR;Sm;0;ON;;;;;N;;;;;
+2958;UPWARDS HARPOON WITH BARB LEFT TO BAR;Sm;0;ON;;;;;N;;;;;
+2959;DOWNWARDS HARPOON WITH BARB LEFT TO BAR;Sm;0;ON;;;;;N;;;;;
+295A;LEFTWARDS HARPOON WITH BARB UP FROM BAR;Sm;0;ON;;;;;N;;;;;
+295B;RIGHTWARDS HARPOON WITH BARB UP FROM BAR;Sm;0;ON;;;;;N;;;;;
+295C;UPWARDS HARPOON WITH BARB RIGHT FROM BAR;Sm;0;ON;;;;;N;;;;;
+295D;DOWNWARDS HARPOON WITH BARB RIGHT FROM BAR;Sm;0;ON;;;;;N;;;;;
+295E;LEFTWARDS HARPOON WITH BARB DOWN FROM BAR;Sm;0;ON;;;;;N;;;;;
+295F;RIGHTWARDS HARPOON WITH BARB DOWN FROM BAR;Sm;0;ON;;;;;N;;;;;
+2960;UPWARDS HARPOON WITH BARB LEFT FROM BAR;Sm;0;ON;;;;;N;;;;;
+2961;DOWNWARDS HARPOON WITH BARB LEFT FROM BAR;Sm;0;ON;;;;;N;;;;;
+2962;LEFTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;;
+2963;UPWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;;
+2964;RIGHTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;;
+2965;DOWNWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;;
+2966;LEFTWARDS HARPOON WITH BARB UP ABOVE RIGHTWARDS HARPOON WITH BARB UP;Sm;0;ON;;;;;N;;;;;
+2967;LEFTWARDS HARPOON WITH BARB DOWN ABOVE RIGHTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;;
+2968;RIGHTWARDS HARPOON WITH BARB UP ABOVE LEFTWARDS HARPOON WITH BARB UP;Sm;0;ON;;;;;N;;;;;
+2969;RIGHTWARDS HARPOON WITH BARB DOWN ABOVE LEFTWARDS HARPOON WITH BARB DOWN;Sm;0;ON;;;;;N;;;;;
+296A;LEFTWARDS HARPOON WITH BARB UP ABOVE LONG DASH;Sm;0;ON;;;;;N;;;;;
+296B;LEFTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH;Sm;0;ON;;;;;N;;;;;
+296C;RIGHTWARDS HARPOON WITH BARB UP ABOVE LONG DASH;Sm;0;ON;;;;;N;;;;;
+296D;RIGHTWARDS HARPOON WITH BARB DOWN BELOW LONG DASH;Sm;0;ON;;;;;N;;;;;
+296E;UPWARDS HARPOON WITH BARB LEFT BESIDE DOWNWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;;
+296F;DOWNWARDS HARPOON WITH BARB LEFT BESIDE UPWARDS HARPOON WITH BARB RIGHT;Sm;0;ON;;;;;N;;;;;
+2970;RIGHT DOUBLE ARROW WITH ROUNDED HEAD;Sm;0;ON;;;;;N;;;;;
+2971;EQUALS SIGN ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2972;TILDE OPERATOR ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2973;LEFTWARDS ARROW ABOVE TILDE OPERATOR;Sm;0;ON;;;;;N;;;;;
+2974;RIGHTWARDS ARROW ABOVE TILDE OPERATOR;Sm;0;ON;;;;;N;;;;;
+2975;RIGHTWARDS ARROW ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;N;;;;;
+2976;LESS-THAN ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2977;LEFTWARDS ARROW THROUGH LESS-THAN;Sm;0;ON;;;;;N;;;;;
+2978;GREATER-THAN ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+2979;SUBSET ABOVE RIGHTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+297A;LEFTWARDS ARROW THROUGH SUBSET;Sm;0;ON;;;;;N;;;;;
+297B;SUPERSET ABOVE LEFTWARDS ARROW;Sm;0;ON;;;;;N;;;;;
+297C;LEFT FISH TAIL;Sm;0;ON;;;;;N;;;;;
+297D;RIGHT FISH TAIL;Sm;0;ON;;;;;N;;;;;
+297E;UP FISH TAIL;Sm;0;ON;;;;;N;;;;;
+297F;DOWN FISH TAIL;Sm;0;ON;;;;;N;;;;;
+2980;TRIPLE VERTICAL BAR DELIMITER;Sm;0;ON;;;;;N;;;;;
+2981;Z NOTATION SPOT;Sm;0;ON;;;;;N;;;;;
+2982;Z NOTATION TYPE COLON;Sm;0;ON;;;;;N;;;;;
+2983;LEFT WHITE CURLY BRACKET;Ps;0;ON;;;;;Y;;;;;
+2984;RIGHT WHITE CURLY BRACKET;Pe;0;ON;;;;;Y;;;;;
+2985;LEFT WHITE PARENTHESIS;Ps;0;ON;;;;;Y;;;;;
+2986;RIGHT WHITE PARENTHESIS;Pe;0;ON;;;;;Y;;;;;
+2987;Z NOTATION LEFT IMAGE BRACKET;Ps;0;ON;;;;;Y;;;;;
+2988;Z NOTATION RIGHT IMAGE BRACKET;Pe;0;ON;;;;;Y;;;;;
+2989;Z NOTATION LEFT BINDING BRACKET;Ps;0;ON;;;;;Y;;;;;
+298A;Z NOTATION RIGHT BINDING BRACKET;Pe;0;ON;;;;;Y;;;;;
+298B;LEFT SQUARE BRACKET WITH UNDERBAR;Ps;0;ON;;;;;Y;;;;;
+298C;RIGHT SQUARE BRACKET WITH UNDERBAR;Pe;0;ON;;;;;Y;;;;;
+298D;LEFT SQUARE BRACKET WITH TICK IN TOP CORNER;Ps;0;ON;;;;;Y;;;;;
+298E;RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER;Pe;0;ON;;;;;Y;;;;;
+298F;LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER;Ps;0;ON;;;;;Y;;;;;
+2990;RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER;Pe;0;ON;;;;;Y;;;;;
+2991;LEFT ANGLE BRACKET WITH DOT;Ps;0;ON;;;;;Y;;;;;
+2992;RIGHT ANGLE BRACKET WITH DOT;Pe;0;ON;;;;;Y;;;;;
+2993;LEFT ARC LESS-THAN BRACKET;Ps;0;ON;;;;;Y;;;;;
+2994;RIGHT ARC GREATER-THAN BRACKET;Pe;0;ON;;;;;Y;;;;;
+2995;DOUBLE LEFT ARC GREATER-THAN BRACKET;Ps;0;ON;;;;;Y;;;;;
+2996;DOUBLE RIGHT ARC LESS-THAN BRACKET;Pe;0;ON;;;;;Y;;;;;
+2997;LEFT BLACK TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;;;;;
+2998;RIGHT BLACK TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;;;;;
+2999;DOTTED FENCE;Sm;0;ON;;;;;N;;;;;
+299A;VERTICAL ZIGZAG LINE;Sm;0;ON;;;;;N;;;;;
+299B;MEASURED ANGLE OPENING LEFT;Sm;0;ON;;;;;Y;;;;;
+299C;RIGHT ANGLE VARIANT WITH SQUARE;Sm;0;ON;;;;;Y;;;;;
+299D;MEASURED RIGHT ANGLE WITH DOT;Sm;0;ON;;;;;Y;;;;;
+299E;ANGLE WITH S INSIDE;Sm;0;ON;;;;;Y;;;;;
+299F;ACUTE ANGLE;Sm;0;ON;;;;;Y;;;;;
+29A0;SPHERICAL ANGLE OPENING LEFT;Sm;0;ON;;;;;Y;;;;;
+29A1;SPHERICAL ANGLE OPENING UP;Sm;0;ON;;;;;Y;;;;;
+29A2;TURNED ANGLE;Sm;0;ON;;;;;Y;;;;;
+29A3;REVERSED ANGLE;Sm;0;ON;;;;;Y;;;;;
+29A4;ANGLE WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;;
+29A5;REVERSED ANGLE WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;;
+29A6;OBLIQUE ANGLE OPENING UP;Sm;0;ON;;;;;Y;;;;;
+29A7;OBLIQUE ANGLE OPENING DOWN;Sm;0;ON;;;;;Y;;;;;
+29A8;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND RIGHT;Sm;0;ON;;;;;Y;;;;;
+29A9;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING UP AND LEFT;Sm;0;ON;;;;;Y;;;;;
+29AA;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND RIGHT;Sm;0;ON;;;;;Y;;;;;
+29AB;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING DOWN AND LEFT;Sm;0;ON;;;;;Y;;;;;
+29AC;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND UP;Sm;0;ON;;;;;Y;;;;;
+29AD;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND UP;Sm;0;ON;;;;;Y;;;;;
+29AE;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING RIGHT AND DOWN;Sm;0;ON;;;;;Y;;;;;
+29AF;MEASURED ANGLE WITH OPEN ARM ENDING IN ARROW POINTING LEFT AND DOWN;Sm;0;ON;;;;;Y;;;;;
+29B0;REVERSED EMPTY SET;Sm;0;ON;;;;;N;;;;;
+29B1;EMPTY SET WITH OVERBAR;Sm;0;ON;;;;;N;;;;;
+29B2;EMPTY SET WITH SMALL CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;;
+29B3;EMPTY SET WITH RIGHT ARROW ABOVE;Sm;0;ON;;;;;N;;;;;
+29B4;EMPTY SET WITH LEFT ARROW ABOVE;Sm;0;ON;;;;;N;;;;;
+29B5;CIRCLE WITH HORIZONTAL BAR;Sm;0;ON;;;;;N;;;;;
+29B6;CIRCLED VERTICAL BAR;Sm;0;ON;;;;;N;;;;;
+29B7;CIRCLED PARALLEL;Sm;0;ON;;;;;N;;;;;
+29B8;CIRCLED REVERSE SOLIDUS;Sm;0;ON;;;;;Y;;;;;
+29B9;CIRCLED PERPENDICULAR;Sm;0;ON;;;;;N;;;;;
+29BA;CIRCLE DIVIDED BY HORIZONTAL BAR AND TOP HALF DIVIDED BY VERTICAL BAR;Sm;0;ON;;;;;N;;;;;
+29BB;CIRCLE WITH SUPERIMPOSED X;Sm;0;ON;;;;;N;;;;;
+29BC;CIRCLED ANTICLOCKWISE-ROTATED DIVISION SIGN;Sm;0;ON;;;;;N;;;;;
+29BD;UP ARROW THROUGH CIRCLE;Sm;0;ON;;;;;N;;;;;
+29BE;CIRCLED WHITE BULLET;Sm;0;ON;;;;;N;;;;;
+29BF;CIRCLED BULLET;Sm;0;ON;;;;;N;;;;;
+29C0;CIRCLED LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+29C1;CIRCLED GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+29C2;CIRCLE WITH SMALL CIRCLE TO THE RIGHT;Sm;0;ON;;;;;Y;;;;;
+29C3;CIRCLE WITH TWO HORIZONTAL STROKES TO THE RIGHT;Sm;0;ON;;;;;Y;;;;;
+29C4;SQUARED RISING DIAGONAL SLASH;Sm;0;ON;;;;;Y;;;;;
+29C5;SQUARED FALLING DIAGONAL SLASH;Sm;0;ON;;;;;Y;;;;;
+29C6;SQUARED ASTERISK;Sm;0;ON;;;;;N;;;;;
+29C7;SQUARED SMALL CIRCLE;Sm;0;ON;;;;;N;;;;;
+29C8;SQUARED SQUARE;Sm;0;ON;;;;;N;;;;;
+29C9;TWO JOINED SQUARES;Sm;0;ON;;;;;Y;;;;;
+29CA;TRIANGLE WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;;
+29CB;TRIANGLE WITH UNDERBAR;Sm;0;ON;;;;;N;;;;;
+29CC;S IN TRIANGLE;Sm;0;ON;;;;;N;;;;;
+29CD;TRIANGLE WITH SERIFS AT BOTTOM;Sm;0;ON;;;;;N;;;;;
+29CE;RIGHT TRIANGLE ABOVE LEFT TRIANGLE;Sm;0;ON;;;;;Y;;;;;
+29CF;LEFT TRIANGLE BESIDE VERTICAL BAR;Sm;0;ON;;;;;Y;;;;;
+29D0;VERTICAL BAR BESIDE RIGHT TRIANGLE;Sm;0;ON;;;;;Y;;;;;
+29D1;BOWTIE WITH LEFT HALF BLACK;Sm;0;ON;;;;;Y;;;;;
+29D2;BOWTIE WITH RIGHT HALF BLACK;Sm;0;ON;;;;;Y;;;;;
+29D3;BLACK BOWTIE;Sm;0;ON;;;;;N;;;;;
+29D4;TIMES WITH LEFT HALF BLACK;Sm;0;ON;;;;;Y;;;;;
+29D5;TIMES WITH RIGHT HALF BLACK;Sm;0;ON;;;;;Y;;;;;
+29D6;WHITE HOURGLASS;Sm;0;ON;;;;;N;;;;;
+29D7;BLACK HOURGLASS;Sm;0;ON;;;;;N;;;;;
+29D8;LEFT WIGGLY FENCE;Ps;0;ON;;;;;Y;;;;;
+29D9;RIGHT WIGGLY FENCE;Pe;0;ON;;;;;Y;;;;;
+29DA;LEFT DOUBLE WIGGLY FENCE;Ps;0;ON;;;;;Y;;;;;
+29DB;RIGHT DOUBLE WIGGLY FENCE;Pe;0;ON;;;;;Y;;;;;
+29DC;INCOMPLETE INFINITY;Sm;0;ON;;;;;Y;;;;;
+29DD;TIE OVER INFINITY;Sm;0;ON;;;;;N;;;;;
+29DE;INFINITY NEGATED WITH VERTICAL BAR;Sm;0;ON;;;;;N;;;;;
+29DF;DOUBLE-ENDED MULTIMAP;Sm;0;ON;;;;;N;;;;;
+29E0;SQUARE WITH CONTOURED OUTLINE;Sm;0;ON;;;;;N;;;;;
+29E1;INCREASES AS;Sm;0;ON;;;;;Y;;;;;
+29E2;SHUFFLE PRODUCT;Sm;0;ON;;;;;N;;;;;
+29E3;EQUALS SIGN AND SLANTED PARALLEL;Sm;0;ON;;;;;Y;;;;;
+29E4;EQUALS SIGN AND SLANTED PARALLEL WITH TILDE ABOVE;Sm;0;ON;;;;;Y;;;;;
+29E5;IDENTICAL TO AND SLANTED PARALLEL;Sm;0;ON;;;;;Y;;;;;
+29E6;GLEICH STARK;Sm;0;ON;;;;;N;;;;;
+29E7;THERMODYNAMIC;Sm;0;ON;;;;;N;;;;;
+29E8;DOWN-POINTING TRIANGLE WITH LEFT HALF BLACK;Sm;0;ON;;;;;Y;;;;;
+29E9;DOWN-POINTING TRIANGLE WITH RIGHT HALF BLACK;Sm;0;ON;;;;;Y;;;;;
+29EA;BLACK DIAMOND WITH DOWN ARROW;Sm;0;ON;;;;;N;;;;;
+29EB;BLACK LOZENGE;Sm;0;ON;;;;;N;;;;;
+29EC;WHITE CIRCLE WITH DOWN ARROW;Sm;0;ON;;;;;N;;;;;
+29ED;BLACK CIRCLE WITH DOWN ARROW;Sm;0;ON;;;;;N;;;;;
+29EE;ERROR-BARRED WHITE SQUARE;Sm;0;ON;;;;;N;;;;;
+29EF;ERROR-BARRED BLACK SQUARE;Sm;0;ON;;;;;N;;;;;
+29F0;ERROR-BARRED WHITE DIAMOND;Sm;0;ON;;;;;N;;;;;
+29F1;ERROR-BARRED BLACK DIAMOND;Sm;0;ON;;;;;N;;;;;
+29F2;ERROR-BARRED WHITE CIRCLE;Sm;0;ON;;;;;N;;;;;
+29F3;ERROR-BARRED BLACK CIRCLE;Sm;0;ON;;;;;N;;;;;
+29F4;RULE-DELAYED;Sm;0;ON;;;;;Y;;;;;
+29F5;REVERSE SOLIDUS OPERATOR;Sm;0;ON;;;;;Y;;;;;
+29F6;SOLIDUS WITH OVERBAR;Sm;0;ON;;;;;Y;;;;;
+29F7;REVERSE SOLIDUS WITH HORIZONTAL STROKE;Sm;0;ON;;;;;Y;;;;;
+29F8;BIG SOLIDUS;Sm;0;ON;;;;;Y;;;;;
+29F9;BIG REVERSE SOLIDUS;Sm;0;ON;;;;;Y;;;;;
+29FA;DOUBLE PLUS;Sm;0;ON;;;;;N;;;;;
+29FB;TRIPLE PLUS;Sm;0;ON;;;;;N;;;;;
+29FC;LEFT-POINTING CURVED ANGLE BRACKET;Ps;0;ON;;;;;Y;;;;;
+29FD;RIGHT-POINTING CURVED ANGLE BRACKET;Pe;0;ON;;;;;Y;;;;;
+29FE;TINY;Sm;0;ON;;;;;N;;;;;
+29FF;MINY;Sm;0;ON;;;;;N;;;;;
+2A00;N-ARY CIRCLED DOT OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A01;N-ARY CIRCLED PLUS OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A02;N-ARY CIRCLED TIMES OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A03;N-ARY UNION OPERATOR WITH DOT;Sm;0;ON;;;;;N;;;;;
+2A04;N-ARY UNION OPERATOR WITH PLUS;Sm;0;ON;;;;;N;;;;;
+2A05;N-ARY SQUARE INTERSECTION OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A06;N-ARY SQUARE UNION OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A07;TWO LOGICAL AND OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A08;TWO LOGICAL OR OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A09;N-ARY TIMES OPERATOR;Sm;0;ON;;;;;N;;;;;
+2A0A;MODULO TWO SUM;Sm;0;ON;;;;;Y;;;;;
+2A0B;SUMMATION WITH INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2A0C;QUADRUPLE INTEGRAL OPERATOR;Sm;0;ON;<compat> 222B 222B 222B 222B;;;;Y;;;;;
+2A0D;FINITE PART INTEGRAL;Sm;0;ON;;;;;Y;;;;;
+2A0E;INTEGRAL WITH DOUBLE STROKE;Sm;0;ON;;;;;Y;;;;;
+2A0F;INTEGRAL AVERAGE WITH SLASH;Sm;0;ON;;;;;Y;;;;;
+2A10;CIRCULATION FUNCTION;Sm;0;ON;;;;;Y;;;;;
+2A11;ANTICLOCKWISE INTEGRATION;Sm;0;ON;;;;;Y;;;;;
+2A12;LINE INTEGRATION WITH RECTANGULAR PATH AROUND POLE;Sm;0;ON;;;;;Y;;;;;
+2A13;LINE INTEGRATION WITH SEMICIRCULAR PATH AROUND POLE;Sm;0;ON;;;;;Y;;;;;
+2A14;LINE INTEGRATION NOT INCLUDING THE POLE;Sm;0;ON;;;;;Y;;;;;
+2A15;INTEGRAL AROUND A POINT OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2A16;QUATERNION INTEGRAL OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2A17;INTEGRAL WITH LEFTWARDS ARROW WITH HOOK;Sm;0;ON;;;;;Y;;;;;
+2A18;INTEGRAL WITH TIMES SIGN;Sm;0;ON;;;;;Y;;;;;
+2A19;INTEGRAL WITH INTERSECTION;Sm;0;ON;;;;;Y;;;;;
+2A1A;INTEGRAL WITH UNION;Sm;0;ON;;;;;Y;;;;;
+2A1B;INTEGRAL WITH OVERBAR;Sm;0;ON;;;;;Y;;;;;
+2A1C;INTEGRAL WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;;
+2A1D;JOIN;Sm;0;ON;;;;;N;;;;;
+2A1E;LARGE LEFT TRIANGLE OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2A1F;Z NOTATION SCHEMA COMPOSITION;Sm;0;ON;;;;;Y;;;;;
+2A20;Z NOTATION SCHEMA PIPING;Sm;0;ON;;;;;Y;;;;;
+2A21;Z NOTATION SCHEMA PROJECTION;Sm;0;ON;;;;;Y;;;;;
+2A22;PLUS SIGN WITH SMALL CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;;
+2A23;PLUS SIGN WITH CIRCUMFLEX ACCENT ABOVE;Sm;0;ON;;;;;N;;;;;
+2A24;PLUS SIGN WITH TILDE ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A25;PLUS SIGN WITH DOT BELOW;Sm;0;ON;;;;;N;;;;;
+2A26;PLUS SIGN WITH TILDE BELOW;Sm;0;ON;;;;;Y;;;;;
+2A27;PLUS SIGN WITH SUBSCRIPT TWO;Sm;0;ON;;;;;N;;;;;
+2A28;PLUS SIGN WITH BLACK TRIANGLE;Sm;0;ON;;;;;N;;;;;
+2A29;MINUS SIGN WITH COMMA ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A2A;MINUS SIGN WITH DOT BELOW;Sm;0;ON;;;;;N;;;;;
+2A2B;MINUS SIGN WITH FALLING DOTS;Sm;0;ON;;;;;Y;;;;;
+2A2C;MINUS SIGN WITH RISING DOTS;Sm;0;ON;;;;;Y;;;;;
+2A2D;PLUS SIGN IN LEFT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;;
+2A2E;PLUS SIGN IN RIGHT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;;
+2A2F;VECTOR OR CROSS PRODUCT;Sm;0;ON;;;;;N;;;;;
+2A30;MULTIPLICATION SIGN WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;;
+2A31;MULTIPLICATION SIGN WITH UNDERBAR;Sm;0;ON;;;;;N;;;;;
+2A32;SEMIDIRECT PRODUCT WITH BOTTOM CLOSED;Sm;0;ON;;;;;N;;;;;
+2A33;SMASH PRODUCT;Sm;0;ON;;;;;N;;;;;
+2A34;MULTIPLICATION SIGN IN LEFT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;;
+2A35;MULTIPLICATION SIGN IN RIGHT HALF CIRCLE;Sm;0;ON;;;;;Y;;;;;
+2A36;CIRCLED MULTIPLICATION SIGN WITH CIRCUMFLEX ACCENT;Sm;0;ON;;;;;N;;;;;
+2A37;MULTIPLICATION SIGN IN DOUBLE CIRCLE;Sm;0;ON;;;;;N;;;;;
+2A38;CIRCLED DIVISION SIGN;Sm;0;ON;;;;;N;;;;;
+2A39;PLUS SIGN IN TRIANGLE;Sm;0;ON;;;;;N;;;;;
+2A3A;MINUS SIGN IN TRIANGLE;Sm;0;ON;;;;;N;;;;;
+2A3B;MULTIPLICATION SIGN IN TRIANGLE;Sm;0;ON;;;;;N;;;;;
+2A3C;INTERIOR PRODUCT;Sm;0;ON;;;;;Y;;;;;
+2A3D;RIGHTHAND INTERIOR PRODUCT;Sm;0;ON;;;;;Y;;;;;
+2A3E;Z NOTATION RELATIONAL COMPOSITION;Sm;0;ON;;;;;Y;;;;;
+2A3F;AMALGAMATION OR COPRODUCT;Sm;0;ON;;;;;N;;;;;
+2A40;INTERSECTION WITH DOT;Sm;0;ON;;;;;N;;;;;
+2A41;UNION WITH MINUS SIGN;Sm;0;ON;;;;;N;;;;;
+2A42;UNION WITH OVERBAR;Sm;0;ON;;;;;N;;;;;
+2A43;INTERSECTION WITH OVERBAR;Sm;0;ON;;;;;N;;;;;
+2A44;INTERSECTION WITH LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+2A45;UNION WITH LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+2A46;UNION ABOVE INTERSECTION;Sm;0;ON;;;;;N;;;;;
+2A47;INTERSECTION ABOVE UNION;Sm;0;ON;;;;;N;;;;;
+2A48;UNION ABOVE BAR ABOVE INTERSECTION;Sm;0;ON;;;;;N;;;;;
+2A49;INTERSECTION ABOVE BAR ABOVE UNION;Sm;0;ON;;;;;N;;;;;
+2A4A;UNION BESIDE AND JOINED WITH UNION;Sm;0;ON;;;;;N;;;;;
+2A4B;INTERSECTION BESIDE AND JOINED WITH INTERSECTION;Sm;0;ON;;;;;N;;;;;
+2A4C;CLOSED UNION WITH SERIFS;Sm;0;ON;;;;;N;;;;;
+2A4D;CLOSED INTERSECTION WITH SERIFS;Sm;0;ON;;;;;N;;;;;
+2A4E;DOUBLE SQUARE INTERSECTION;Sm;0;ON;;;;;N;;;;;
+2A4F;DOUBLE SQUARE UNION;Sm;0;ON;;;;;N;;;;;
+2A50;CLOSED UNION WITH SERIFS AND SMASH PRODUCT;Sm;0;ON;;;;;N;;;;;
+2A51;LOGICAL AND WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;;
+2A52;LOGICAL OR WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;;
+2A53;DOUBLE LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+2A54;DOUBLE LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+2A55;TWO INTERSECTING LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+2A56;TWO INTERSECTING LOGICAL OR;Sm;0;ON;;;;;N;;;;;
+2A57;SLOPING LARGE OR;Sm;0;ON;;;;;Y;;;;;
+2A58;SLOPING LARGE AND;Sm;0;ON;;;;;Y;;;;;
+2A59;LOGICAL OR OVERLAPPING LOGICAL AND;Sm;0;ON;;;;;N;;;;;
+2A5A;LOGICAL AND WITH MIDDLE STEM;Sm;0;ON;;;;;N;;;;;
+2A5B;LOGICAL OR WITH MIDDLE STEM;Sm;0;ON;;;;;N;;;;;
+2A5C;LOGICAL AND WITH HORIZONTAL DASH;Sm;0;ON;;;;;N;;;;;
+2A5D;LOGICAL OR WITH HORIZONTAL DASH;Sm;0;ON;;;;;N;;;;;
+2A5E;LOGICAL AND WITH DOUBLE OVERBAR;Sm;0;ON;;;;;N;;;;;
+2A5F;LOGICAL AND WITH UNDERBAR;Sm;0;ON;;;;;N;;;;;
+2A60;LOGICAL AND WITH DOUBLE UNDERBAR;Sm;0;ON;;;;;N;;;;;
+2A61;SMALL VEE WITH UNDERBAR;Sm;0;ON;;;;;N;;;;;
+2A62;LOGICAL OR WITH DOUBLE OVERBAR;Sm;0;ON;;;;;N;;;;;
+2A63;LOGICAL OR WITH DOUBLE UNDERBAR;Sm;0;ON;;;;;N;;;;;
+2A64;Z NOTATION DOMAIN ANTIRESTRICTION;Sm;0;ON;;;;;Y;;;;;
+2A65;Z NOTATION RANGE ANTIRESTRICTION;Sm;0;ON;;;;;Y;;;;;
+2A66;EQUALS SIGN WITH DOT BELOW;Sm;0;ON;;;;;N;;;;;
+2A67;IDENTICAL WITH DOT ABOVE;Sm;0;ON;;;;;N;;;;;
+2A68;TRIPLE HORIZONTAL BAR WITH DOUBLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2A69;TRIPLE HORIZONTAL BAR WITH TRIPLE VERTICAL STROKE;Sm;0;ON;;;;;N;;;;;
+2A6A;TILDE OPERATOR WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A6B;TILDE OPERATOR WITH RISING DOTS;Sm;0;ON;;;;;Y;;;;;
+2A6C;SIMILAR MINUS SIMILAR;Sm;0;ON;;;;;Y;;;;;
+2A6D;CONGRUENT WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A6E;EQUALS WITH ASTERISK;Sm;0;ON;;;;;N;;;;;
+2A6F;ALMOST EQUAL TO WITH CIRCUMFLEX ACCENT;Sm;0;ON;;;;;Y;;;;;
+2A70;APPROXIMATELY EQUAL OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2A71;EQUALS SIGN ABOVE PLUS SIGN;Sm;0;ON;;;;;N;;;;;
+2A72;PLUS SIGN ABOVE EQUALS SIGN;Sm;0;ON;;;;;N;;;;;
+2A73;EQUALS SIGN ABOVE TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2A74;DOUBLE COLON EQUAL;Sm;0;ON;<compat> 003A 003A 003D;;;;Y;;;;;
+2A75;TWO CONSECUTIVE EQUALS SIGNS;Sm;0;ON;<compat> 003D 003D;;;;N;;;;;
+2A76;THREE CONSECUTIVE EQUALS SIGNS;Sm;0;ON;<compat> 003D 003D 003D;;;;N;;;;;
+2A77;EQUALS SIGN WITH TWO DOTS ABOVE AND TWO DOTS BELOW;Sm;0;ON;;;;;N;;;;;
+2A78;EQUIVALENT WITH FOUR DOTS ABOVE;Sm;0;ON;;;;;N;;;;;
+2A79;LESS-THAN WITH CIRCLE INSIDE;Sm;0;ON;;;;;Y;;;;;
+2A7A;GREATER-THAN WITH CIRCLE INSIDE;Sm;0;ON;;;;;Y;;;;;
+2A7B;LESS-THAN WITH QUESTION MARK ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A7C;GREATER-THAN WITH QUESTION MARK ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A7D;LESS-THAN OR SLANTED EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2A7E;GREATER-THAN OR SLANTED EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2A7F;LESS-THAN OR SLANTED EQUAL TO WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;;
+2A80;GREATER-THAN OR SLANTED EQUAL TO WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;;
+2A81;LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A82;GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;;
+2A83;LESS-THAN OR SLANTED EQUAL TO WITH DOT ABOVE RIGHT;Sm;0;ON;;;;;Y;;;;;
+2A84;GREATER-THAN OR SLANTED EQUAL TO WITH DOT ABOVE LEFT;Sm;0;ON;;;;;Y;;;;;
+2A85;LESS-THAN OR APPROXIMATE;Sm;0;ON;;;;;Y;;;;;
+2A86;GREATER-THAN OR APPROXIMATE;Sm;0;ON;;;;;Y;;;;;
+2A87;LESS-THAN AND SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2A88;GREATER-THAN AND SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2A89;LESS-THAN AND NOT APPROXIMATE;Sm;0;ON;;;;;Y;;;;;
+2A8A;GREATER-THAN AND NOT APPROXIMATE;Sm;0;ON;;;;;Y;;;;;
+2A8B;LESS-THAN ABOVE DOUBLE-LINE EQUAL ABOVE GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2A8C;GREATER-THAN ABOVE DOUBLE-LINE EQUAL ABOVE LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2A8D;LESS-THAN ABOVE SIMILAR OR EQUAL;Sm;0;ON;;;;;Y;;;;;
+2A8E;GREATER-THAN ABOVE SIMILAR OR EQUAL;Sm;0;ON;;;;;Y;;;;;
+2A8F;LESS-THAN ABOVE SIMILAR ABOVE GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2A90;GREATER-THAN ABOVE SIMILAR ABOVE LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2A91;LESS-THAN ABOVE GREATER-THAN ABOVE DOUBLE-LINE EQUAL;Sm;0;ON;;;;;Y;;;;;
+2A92;GREATER-THAN ABOVE LESS-THAN ABOVE DOUBLE-LINE EQUAL;Sm;0;ON;;;;;Y;;;;;
+2A93;LESS-THAN ABOVE SLANTED EQUAL ABOVE GREATER-THAN ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;;
+2A94;GREATER-THAN ABOVE SLANTED EQUAL ABOVE LESS-THAN ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;;
+2A95;SLANTED EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2A96;SLANTED EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2A97;SLANTED EQUAL TO OR LESS-THAN WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;;
+2A98;SLANTED EQUAL TO OR GREATER-THAN WITH DOT INSIDE;Sm;0;ON;;;;;Y;;;;;
+2A99;DOUBLE-LINE EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2A9A;DOUBLE-LINE EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2A9B;DOUBLE-LINE SLANTED EQUAL TO OR LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2A9C;DOUBLE-LINE SLANTED EQUAL TO OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2A9D;SIMILAR OR LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2A9E;SIMILAR OR GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2A9F;SIMILAR ABOVE LESS-THAN ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AA0;SIMILAR ABOVE GREATER-THAN ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AA1;DOUBLE NESTED LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2AA2;DOUBLE NESTED GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2AA3;DOUBLE NESTED LESS-THAN WITH UNDERBAR;Sm;0;ON;;;;;Y;;;;;
+2AA4;GREATER-THAN OVERLAPPING LESS-THAN;Sm;0;ON;;;;;N;;;;;
+2AA5;GREATER-THAN BESIDE LESS-THAN;Sm;0;ON;;;;;N;;;;;
+2AA6;LESS-THAN CLOSED BY CURVE;Sm;0;ON;;;;;Y;;;;;
+2AA7;GREATER-THAN CLOSED BY CURVE;Sm;0;ON;;;;;Y;;;;;
+2AA8;LESS-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;;
+2AA9;GREATER-THAN CLOSED BY CURVE ABOVE SLANTED EQUAL;Sm;0;ON;;;;;Y;;;;;
+2AAA;SMALLER THAN;Sm;0;ON;;;;;Y;;;;;
+2AAB;LARGER THAN;Sm;0;ON;;;;;Y;;;;;
+2AAC;SMALLER THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AAD;LARGER THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AAE;EQUALS SIGN WITH BUMPY ABOVE;Sm;0;ON;;;;;N;;;;;
+2AAF;PRECEDES ABOVE SINGLE-LINE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AB0;SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AB1;PRECEDES ABOVE SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AB2;SUCCEEDS ABOVE SINGLE-LINE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AB3;PRECEDES ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AB4;SUCCEEDS ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AB5;PRECEDES ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AB6;SUCCEEDS ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AB7;PRECEDES ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AB8;SUCCEEDS ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AB9;PRECEDES ABOVE NOT ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2ABA;SUCCEEDS ABOVE NOT ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2ABB;DOUBLE PRECEDES;Sm;0;ON;;;;;Y;;;;;
+2ABC;DOUBLE SUCCEEDS;Sm;0;ON;;;;;Y;;;;;
+2ABD;SUBSET WITH DOT;Sm;0;ON;;;;;Y;;;;;
+2ABE;SUPERSET WITH DOT;Sm;0;ON;;;;;Y;;;;;
+2ABF;SUBSET WITH PLUS SIGN BELOW;Sm;0;ON;;;;;Y;;;;;
+2AC0;SUPERSET WITH PLUS SIGN BELOW;Sm;0;ON;;;;;Y;;;;;
+2AC1;SUBSET WITH MULTIPLICATION SIGN BELOW;Sm;0;ON;;;;;Y;;;;;
+2AC2;SUPERSET WITH MULTIPLICATION SIGN BELOW;Sm;0;ON;;;;;Y;;;;;
+2AC3;SUBSET OF OR EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;;
+2AC4;SUPERSET OF OR EQUAL TO WITH DOT ABOVE;Sm;0;ON;;;;;Y;;;;;
+2AC5;SUBSET OF ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AC6;SUPERSET OF ABOVE EQUALS SIGN;Sm;0;ON;;;;;Y;;;;;
+2AC7;SUBSET OF ABOVE TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2AC8;SUPERSET OF ABOVE TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2AC9;SUBSET OF ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2ACA;SUPERSET OF ABOVE ALMOST EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2ACB;SUBSET OF ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2ACC;SUPERSET OF ABOVE NOT EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2ACD;SQUARE LEFT OPEN BOX OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2ACE;SQUARE RIGHT OPEN BOX OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2ACF;CLOSED SUBSET;Sm;0;ON;;;;;Y;;;;;
+2AD0;CLOSED SUPERSET;Sm;0;ON;;;;;Y;;;;;
+2AD1;CLOSED SUBSET OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AD2;CLOSED SUPERSET OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AD3;SUBSET ABOVE SUPERSET;Sm;0;ON;;;;;Y;;;;;
+2AD4;SUPERSET ABOVE SUBSET;Sm;0;ON;;;;;Y;;;;;
+2AD5;SUBSET ABOVE SUBSET;Sm;0;ON;;;;;Y;;;;;
+2AD6;SUPERSET ABOVE SUPERSET;Sm;0;ON;;;;;Y;;;;;
+2AD7;SUPERSET BESIDE SUBSET;Sm;0;ON;;;;;N;;;;;
+2AD8;SUPERSET BESIDE AND JOINED BY DASH WITH SUBSET;Sm;0;ON;;;;;N;;;;;
+2AD9;ELEMENT OF OPENING DOWNWARDS;Sm;0;ON;;;;;N;;;;;
+2ADA;PITCHFORK WITH TEE TOP;Sm;0;ON;;;;;N;;;;;
+2ADB;TRANSVERSAL INTERSECTION;Sm;0;ON;;;;;N;;;;;
+2ADC;FORKING;Sm;0;ON;2ADD 0338;;;;Y;;not independent;;;
+2ADD;NONFORKING;Sm;0;ON;;;;;N;;independent;;;
+2ADE;SHORT LEFT TACK;Sm;0;ON;;;;;Y;;;;;
+2ADF;SHORT DOWN TACK;Sm;0;ON;;;;;N;;;;;
+2AE0;SHORT UP TACK;Sm;0;ON;;;;;N;;;;;
+2AE1;PERPENDICULAR WITH S;Sm;0;ON;;;;;N;;;;;
+2AE2;VERTICAL BAR TRIPLE RIGHT TURNSTILE;Sm;0;ON;;;;;Y;;;;;
+2AE3;DOUBLE VERTICAL BAR LEFT TURNSTILE;Sm;0;ON;;;;;Y;;;;;
+2AE4;VERTICAL BAR DOUBLE LEFT TURNSTILE;Sm;0;ON;;;;;Y;;;;;
+2AE5;DOUBLE VERTICAL BAR DOUBLE LEFT TURNSTILE;Sm;0;ON;;;;;Y;;;;;
+2AE6;LONG DASH FROM LEFT MEMBER OF DOUBLE VERTICAL;Sm;0;ON;;;;;Y;;;;;
+2AE7;SHORT DOWN TACK WITH OVERBAR;Sm;0;ON;;;;;N;;;;;
+2AE8;SHORT UP TACK WITH UNDERBAR;Sm;0;ON;;;;;N;;;;;
+2AE9;SHORT UP TACK ABOVE SHORT DOWN TACK;Sm;0;ON;;;;;N;;;;;
+2AEA;DOUBLE DOWN TACK;Sm;0;ON;;;;;N;;;;;
+2AEB;DOUBLE UP TACK;Sm;0;ON;;;;;N;;;;;
+2AEC;DOUBLE STROKE NOT SIGN;Sm;0;ON;;;;;Y;;;;;
+2AED;REVERSED DOUBLE STROKE NOT SIGN;Sm;0;ON;;;;;Y;;;;;
+2AEE;DOES NOT DIVIDE WITH REVERSED NEGATION SLASH;Sm;0;ON;;;;;Y;;;;;
+2AEF;VERTICAL LINE WITH CIRCLE ABOVE;Sm;0;ON;;;;;N;;;;;
+2AF0;VERTICAL LINE WITH CIRCLE BELOW;Sm;0;ON;;;;;N;;;;;
+2AF1;DOWN TACK WITH CIRCLE BELOW;Sm;0;ON;;;;;N;;;;;
+2AF2;PARALLEL WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;;
+2AF3;PARALLEL WITH TILDE OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2AF4;TRIPLE VERTICAL BAR BINARY RELATION;Sm;0;ON;;;;;N;;;;;
+2AF5;TRIPLE VERTICAL BAR WITH HORIZONTAL STROKE;Sm;0;ON;;;;;N;;;;;
+2AF6;TRIPLE COLON OPERATOR;Sm;0;ON;;;;;N;;;;;
+2AF7;TRIPLE NESTED LESS-THAN;Sm;0;ON;;;;;Y;;;;;
+2AF8;TRIPLE NESTED GREATER-THAN;Sm;0;ON;;;;;Y;;;;;
+2AF9;DOUBLE-LINE SLANTED LESS-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AFA;DOUBLE-LINE SLANTED GREATER-THAN OR EQUAL TO;Sm;0;ON;;;;;Y;;;;;
+2AFB;TRIPLE SOLIDUS BINARY RELATION;Sm;0;ON;;;;;Y;;;;;
+2AFC;LARGE TRIPLE VERTICAL BAR OPERATOR;Sm;0;ON;;;;;N;;;;;
+2AFD;DOUBLE SOLIDUS OPERATOR;Sm;0;ON;;;;;Y;;;;;
+2AFE;WHITE VERTICAL BAR;Sm;0;ON;;;;;N;;;;;
+2AFF;N-ARY WHITE VERTICAL BAR;Sm;0;ON;;;;;N;;;;;
+2E80;CJK RADICAL REPEAT;So;0;ON;;;;;N;;;;;
+2E81;CJK RADICAL CLIFF;So;0;ON;;;;;N;;;;;
+2E82;CJK RADICAL SECOND ONE;So;0;ON;;;;;N;;;;;
+2E83;CJK RADICAL SECOND TWO;So;0;ON;;;;;N;;;;;
+2E84;CJK RADICAL SECOND THREE;So;0;ON;;;;;N;;;;;
+2E85;CJK RADICAL PERSON;So;0;ON;;;;;N;;;;;
+2E86;CJK RADICAL BOX;So;0;ON;;;;;N;;;;;
+2E87;CJK RADICAL TABLE;So;0;ON;;;;;N;;;;;
+2E88;CJK RADICAL KNIFE ONE;So;0;ON;;;;;N;;;;;
+2E89;CJK RADICAL KNIFE TWO;So;0;ON;;;;;N;;;;;
+2E8A;CJK RADICAL DIVINATION;So;0;ON;;;;;N;;;;;
+2E8B;CJK RADICAL SEAL;So;0;ON;;;;;N;;;;;
+2E8C;CJK RADICAL SMALL ONE;So;0;ON;;;;;N;;;;;
+2E8D;CJK RADICAL SMALL TWO;So;0;ON;;;;;N;;;;;
+2E8E;CJK RADICAL LAME ONE;So;0;ON;;;;;N;;;;;
+2E8F;CJK RADICAL LAME TWO;So;0;ON;;;;;N;;;;;
+2E90;CJK RADICAL LAME THREE;So;0;ON;;;;;N;;;;;
+2E91;CJK RADICAL LAME FOUR;So;0;ON;;;;;N;;;;;
+2E92;CJK RADICAL SNAKE;So;0;ON;;;;;N;;;;;
+2E93;CJK RADICAL THREAD;So;0;ON;;;;;N;;;;;
+2E94;CJK RADICAL SNOUT ONE;So;0;ON;;;;;N;;;;;
+2E95;CJK RADICAL SNOUT TWO;So;0;ON;;;;;N;;;;;
+2E96;CJK RADICAL HEART ONE;So;0;ON;;;;;N;;;;;
+2E97;CJK RADICAL HEART TWO;So;0;ON;;;;;N;;;;;
+2E98;CJK RADICAL HAND;So;0;ON;;;;;N;;;;;
+2E99;CJK RADICAL RAP;So;0;ON;;;;;N;;;;;
+2E9B;CJK RADICAL CHOKE;So;0;ON;;;;;N;;;;;
+2E9C;CJK RADICAL SUN;So;0;ON;;;;;N;;;;;
+2E9D;CJK RADICAL MOON;So;0;ON;;;;;N;;;;;
+2E9E;CJK RADICAL DEATH;So;0;ON;;;;;N;;;;;
+2E9F;CJK RADICAL MOTHER;So;0;ON;<compat> 6BCD;;;;N;;;;;
+2EA0;CJK RADICAL CIVILIAN;So;0;ON;;;;;N;;;;;
+2EA1;CJK RADICAL WATER ONE;So;0;ON;;;;;N;;;;;
+2EA2;CJK RADICAL WATER TWO;So;0;ON;;;;;N;;;;;
+2EA3;CJK RADICAL FIRE;So;0;ON;;;;;N;;;;;
+2EA4;CJK RADICAL PAW ONE;So;0;ON;;;;;N;;;;;
+2EA5;CJK RADICAL PAW TWO;So;0;ON;;;;;N;;;;;
+2EA6;CJK RADICAL SIMPLIFIED HALF TREE TRUNK;So;0;ON;;;;;N;;;;;
+2EA7;CJK RADICAL COW;So;0;ON;;;;;N;;;;;
+2EA8;CJK RADICAL DOG;So;0;ON;;;;;N;;;;;
+2EA9;CJK RADICAL JADE;So;0;ON;;;;;N;;;;;
+2EAA;CJK RADICAL BOLT OF CLOTH;So;0;ON;;;;;N;;;;;
+2EAB;CJK RADICAL EYE;So;0;ON;;;;;N;;;;;
+2EAC;CJK RADICAL SPIRIT ONE;So;0;ON;;;;;N;;;;;
+2EAD;CJK RADICAL SPIRIT TWO;So;0;ON;;;;;N;;;;;
+2EAE;CJK RADICAL BAMBOO;So;0;ON;;;;;N;;;;;
+2EAF;CJK RADICAL SILK;So;0;ON;;;;;N;;;;;
+2EB0;CJK RADICAL C-SIMPLIFIED SILK;So;0;ON;;;;;N;;;;;
+2EB1;CJK RADICAL NET ONE;So;0;ON;;;;;N;;;;;
+2EB2;CJK RADICAL NET TWO;So;0;ON;;;;;N;;;;;
+2EB3;CJK RADICAL NET THREE;So;0;ON;;;;;N;;;;;
+2EB4;CJK RADICAL NET FOUR;So;0;ON;;;;;N;;;;;
+2EB5;CJK RADICAL MESH;So;0;ON;;;;;N;;;;;
+2EB6;CJK RADICAL SHEEP;So;0;ON;;;;;N;;;;;
+2EB7;CJK RADICAL RAM;So;0;ON;;;;;N;;;;;
+2EB8;CJK RADICAL EWE;So;0;ON;;;;;N;;;;;
+2EB9;CJK RADICAL OLD;So;0;ON;;;;;N;;;;;
+2EBA;CJK RADICAL BRUSH ONE;So;0;ON;;;;;N;;;;;
+2EBB;CJK RADICAL BRUSH TWO;So;0;ON;;;;;N;;;;;
+2EBC;CJK RADICAL MEAT;So;0;ON;;;;;N;;;;;
+2EBD;CJK RADICAL MORTAR;So;0;ON;;;;;N;;;;;
+2EBE;CJK RADICAL GRASS ONE;So;0;ON;;;;;N;;;;;
+2EBF;CJK RADICAL GRASS TWO;So;0;ON;;;;;N;;;;;
+2EC0;CJK RADICAL GRASS THREE;So;0;ON;;;;;N;;;;;
+2EC1;CJK RADICAL TIGER;So;0;ON;;;;;N;;;;;
+2EC2;CJK RADICAL CLOTHES;So;0;ON;;;;;N;;;;;
+2EC3;CJK RADICAL WEST ONE;So;0;ON;;;;;N;;;;;
+2EC4;CJK RADICAL WEST TWO;So;0;ON;;;;;N;;;;;
+2EC5;CJK RADICAL C-SIMPLIFIED SEE;So;0;ON;;;;;N;;;;;
+2EC6;CJK RADICAL SIMPLIFIED HORN;So;0;ON;;;;;N;;;;;
+2EC7;CJK RADICAL HORN;So;0;ON;;;;;N;;;;;
+2EC8;CJK RADICAL C-SIMPLIFIED SPEECH;So;0;ON;;;;;N;;;;;
+2EC9;CJK RADICAL C-SIMPLIFIED SHELL;So;0;ON;;;;;N;;;;;
+2ECA;CJK RADICAL FOOT;So;0;ON;;;;;N;;;;;
+2ECB;CJK RADICAL C-SIMPLIFIED CART;So;0;ON;;;;;N;;;;;
+2ECC;CJK RADICAL SIMPLIFIED WALK;So;0;ON;;;;;N;;;;;
+2ECD;CJK RADICAL WALK ONE;So;0;ON;;;;;N;;;;;
+2ECE;CJK RADICAL WALK TWO;So;0;ON;;;;;N;;;;;
+2ECF;CJK RADICAL CITY;So;0;ON;;;;;N;;;;;
+2ED0;CJK RADICAL C-SIMPLIFIED GOLD;So;0;ON;;;;;N;;;;;
+2ED1;CJK RADICAL LONG ONE;So;0;ON;;;;;N;;;;;
+2ED2;CJK RADICAL LONG TWO;So;0;ON;;;;;N;;;;;
+2ED3;CJK RADICAL C-SIMPLIFIED LONG;So;0;ON;;;;;N;;;;;
+2ED4;CJK RADICAL C-SIMPLIFIED GATE;So;0;ON;;;;;N;;;;;
+2ED5;CJK RADICAL MOUND ONE;So;0;ON;;;;;N;;;;;
+2ED6;CJK RADICAL MOUND TWO;So;0;ON;;;;;N;;;;;
+2ED7;CJK RADICAL RAIN;So;0;ON;;;;;N;;;;;
+2ED8;CJK RADICAL BLUE;So;0;ON;;;;;N;;;;;
+2ED9;CJK RADICAL C-SIMPLIFIED TANNED LEATHER;So;0;ON;;;;;N;;;;;
+2EDA;CJK RADICAL C-SIMPLIFIED LEAF;So;0;ON;;;;;N;;;;;
+2EDB;CJK RADICAL C-SIMPLIFIED WIND;So;0;ON;;;;;N;;;;;
+2EDC;CJK RADICAL C-SIMPLIFIED FLY;So;0;ON;;;;;N;;;;;
+2EDD;CJK RADICAL EAT ONE;So;0;ON;;;;;N;;;;;
+2EDE;CJK RADICAL EAT TWO;So;0;ON;;;;;N;;;;;
+2EDF;CJK RADICAL EAT THREE;So;0;ON;;;;;N;;;;;
+2EE0;CJK RADICAL C-SIMPLIFIED EAT;So;0;ON;;;;;N;;;;;
+2EE1;CJK RADICAL HEAD;So;0;ON;;;;;N;;;;;
+2EE2;CJK RADICAL C-SIMPLIFIED HORSE;So;0;ON;;;;;N;;;;;
+2EE3;CJK RADICAL BONE;So;0;ON;;;;;N;;;;;
+2EE4;CJK RADICAL GHOST;So;0;ON;;;;;N;;;;;
+2EE5;CJK RADICAL C-SIMPLIFIED FISH;So;0;ON;;;;;N;;;;;
+2EE6;CJK RADICAL C-SIMPLIFIED BIRD;So;0;ON;;;;;N;;;;;
+2EE7;CJK RADICAL C-SIMPLIFIED SALT;So;0;ON;;;;;N;;;;;
+2EE8;CJK RADICAL SIMPLIFIED WHEAT;So;0;ON;;;;;N;;;;;
+2EE9;CJK RADICAL SIMPLIFIED YELLOW;So;0;ON;;;;;N;;;;;
+2EEA;CJK RADICAL C-SIMPLIFIED FROG;So;0;ON;;;;;N;;;;;
+2EEB;CJK RADICAL J-SIMPLIFIED EVEN;So;0;ON;;;;;N;;;;;
+2EEC;CJK RADICAL C-SIMPLIFIED EVEN;So;0;ON;;;;;N;;;;;
+2EED;CJK RADICAL J-SIMPLIFIED TOOTH;So;0;ON;;;;;N;;;;;
+2EEE;CJK RADICAL C-SIMPLIFIED TOOTH;So;0;ON;;;;;N;;;;;
+2EEF;CJK RADICAL J-SIMPLIFIED DRAGON;So;0;ON;;;;;N;;;;;
+2EF0;CJK RADICAL C-SIMPLIFIED DRAGON;So;0;ON;;;;;N;;;;;
+2EF1;CJK RADICAL TURTLE;So;0;ON;;;;;N;;;;;
+2EF2;CJK RADICAL J-SIMPLIFIED TURTLE;So;0;ON;;;;;N;;;;;
+2EF3;CJK RADICAL C-SIMPLIFIED TURTLE;So;0;ON;<compat> 9F9F;;;;N;;;;;
+2F00;KANGXI RADICAL ONE;So;0;ON;<compat> 4E00;;;;N;;;;;
+2F01;KANGXI RADICAL LINE;So;0;ON;<compat> 4E28;;;;N;;;;;
+2F02;KANGXI RADICAL DOT;So;0;ON;<compat> 4E36;;;;N;;;;;
+2F03;KANGXI RADICAL SLASH;So;0;ON;<compat> 4E3F;;;;N;;;;;
+2F04;KANGXI RADICAL SECOND;So;0;ON;<compat> 4E59;;;;N;;;;;
+2F05;KANGXI RADICAL HOOK;So;0;ON;<compat> 4E85;;;;N;;;;;
+2F06;KANGXI RADICAL TWO;So;0;ON;<compat> 4E8C;;;;N;;;;;
+2F07;KANGXI RADICAL LID;So;0;ON;<compat> 4EA0;;;;N;;;;;
+2F08;KANGXI RADICAL MAN;So;0;ON;<compat> 4EBA;;;;N;;;;;
+2F09;KANGXI RADICAL LEGS;So;0;ON;<compat> 513F;;;;N;;;;;
+2F0A;KANGXI RADICAL ENTER;So;0;ON;<compat> 5165;;;;N;;;;;
+2F0B;KANGXI RADICAL EIGHT;So;0;ON;<compat> 516B;;;;N;;;;;
+2F0C;KANGXI RADICAL DOWN BOX;So;0;ON;<compat> 5182;;;;N;;;;;
+2F0D;KANGXI RADICAL COVER;So;0;ON;<compat> 5196;;;;N;;;;;
+2F0E;KANGXI RADICAL ICE;So;0;ON;<compat> 51AB;;;;N;;;;;
+2F0F;KANGXI RADICAL TABLE;So;0;ON;<compat> 51E0;;;;N;;;;;
+2F10;KANGXI RADICAL OPEN BOX;So;0;ON;<compat> 51F5;;;;N;;;;;
+2F11;KANGXI RADICAL KNIFE;So;0;ON;<compat> 5200;;;;N;;;;;
+2F12;KANGXI RADICAL POWER;So;0;ON;<compat> 529B;;;;N;;;;;
+2F13;KANGXI RADICAL WRAP;So;0;ON;<compat> 52F9;;;;N;;;;;
+2F14;KANGXI RADICAL SPOON;So;0;ON;<compat> 5315;;;;N;;;;;
+2F15;KANGXI RADICAL RIGHT OPEN BOX;So;0;ON;<compat> 531A;;;;N;;;;;
+2F16;KANGXI RADICAL HIDING ENCLOSURE;So;0;ON;<compat> 5338;;;;N;;;;;
+2F17;KANGXI RADICAL TEN;So;0;ON;<compat> 5341;;;;N;;;;;
+2F18;KANGXI RADICAL DIVINATION;So;0;ON;<compat> 535C;;;;N;;;;;
+2F19;KANGXI RADICAL SEAL;So;0;ON;<compat> 5369;;;;N;;;;;
+2F1A;KANGXI RADICAL CLIFF;So;0;ON;<compat> 5382;;;;N;;;;;
+2F1B;KANGXI RADICAL PRIVATE;So;0;ON;<compat> 53B6;;;;N;;;;;
+2F1C;KANGXI RADICAL AGAIN;So;0;ON;<compat> 53C8;;;;N;;;;;
+2F1D;KANGXI RADICAL MOUTH;So;0;ON;<compat> 53E3;;;;N;;;;;
+2F1E;KANGXI RADICAL ENCLOSURE;So;0;ON;<compat> 56D7;;;;N;;;;;
+2F1F;KANGXI RADICAL EARTH;So;0;ON;<compat> 571F;;;;N;;;;;
+2F20;KANGXI RADICAL SCHOLAR;So;0;ON;<compat> 58EB;;;;N;;;;;
+2F21;KANGXI RADICAL GO;So;0;ON;<compat> 5902;;;;N;;;;;
+2F22;KANGXI RADICAL GO SLOWLY;So;0;ON;<compat> 590A;;;;N;;;;;
+2F23;KANGXI RADICAL EVENING;So;0;ON;<compat> 5915;;;;N;;;;;
+2F24;KANGXI RADICAL BIG;So;0;ON;<compat> 5927;;;;N;;;;;
+2F25;KANGXI RADICAL WOMAN;So;0;ON;<compat> 5973;;;;N;;;;;
+2F26;KANGXI RADICAL CHILD;So;0;ON;<compat> 5B50;;;;N;;;;;
+2F27;KANGXI RADICAL ROOF;So;0;ON;<compat> 5B80;;;;N;;;;;
+2F28;KANGXI RADICAL INCH;So;0;ON;<compat> 5BF8;;;;N;;;;;
+2F29;KANGXI RADICAL SMALL;So;0;ON;<compat> 5C0F;;;;N;;;;;
+2F2A;KANGXI RADICAL LAME;So;0;ON;<compat> 5C22;;;;N;;;;;
+2F2B;KANGXI RADICAL CORPSE;So;0;ON;<compat> 5C38;;;;N;;;;;
+2F2C;KANGXI RADICAL SPROUT;So;0;ON;<compat> 5C6E;;;;N;;;;;
+2F2D;KANGXI RADICAL MOUNTAIN;So;0;ON;<compat> 5C71;;;;N;;;;;
+2F2E;KANGXI RADICAL RIVER;So;0;ON;<compat> 5DDB;;;;N;;;;;
+2F2F;KANGXI RADICAL WORK;So;0;ON;<compat> 5DE5;;;;N;;;;;
+2F30;KANGXI RADICAL ONESELF;So;0;ON;<compat> 5DF1;;;;N;;;;;
+2F31;KANGXI RADICAL TURBAN;So;0;ON;<compat> 5DFE;;;;N;;;;;
+2F32;KANGXI RADICAL DRY;So;0;ON;<compat> 5E72;;;;N;;;;;
+2F33;KANGXI RADICAL SHORT THREAD;So;0;ON;<compat> 5E7A;;;;N;;;;;
+2F34;KANGXI RADICAL DOTTED CLIFF;So;0;ON;<compat> 5E7F;;;;N;;;;;
+2F35;KANGXI RADICAL LONG STRIDE;So;0;ON;<compat> 5EF4;;;;N;;;;;
+2F36;KANGXI RADICAL TWO HANDS;So;0;ON;<compat> 5EFE;;;;N;;;;;
+2F37;KANGXI RADICAL SHOOT;So;0;ON;<compat> 5F0B;;;;N;;;;;
+2F38;KANGXI RADICAL BOW;So;0;ON;<compat> 5F13;;;;N;;;;;
+2F39;KANGXI RADICAL SNOUT;So;0;ON;<compat> 5F50;;;;N;;;;;
+2F3A;KANGXI RADICAL BRISTLE;So;0;ON;<compat> 5F61;;;;N;;;;;
+2F3B;KANGXI RADICAL STEP;So;0;ON;<compat> 5F73;;;;N;;;;;
+2F3C;KANGXI RADICAL HEART;So;0;ON;<compat> 5FC3;;;;N;;;;;
+2F3D;KANGXI RADICAL HALBERD;So;0;ON;<compat> 6208;;;;N;;;;;
+2F3E;KANGXI RADICAL DOOR;So;0;ON;<compat> 6236;;;;N;;;;;
+2F3F;KANGXI RADICAL HAND;So;0;ON;<compat> 624B;;;;N;;;;;
+2F40;KANGXI RADICAL BRANCH;So;0;ON;<compat> 652F;;;;N;;;;;
+2F41;KANGXI RADICAL RAP;So;0;ON;<compat> 6534;;;;N;;;;;
+2F42;KANGXI RADICAL SCRIPT;So;0;ON;<compat> 6587;;;;N;;;;;
+2F43;KANGXI RADICAL DIPPER;So;0;ON;<compat> 6597;;;;N;;;;;
+2F44;KANGXI RADICAL AXE;So;0;ON;<compat> 65A4;;;;N;;;;;
+2F45;KANGXI RADICAL SQUARE;So;0;ON;<compat> 65B9;;;;N;;;;;
+2F46;KANGXI RADICAL NOT;So;0;ON;<compat> 65E0;;;;N;;;;;
+2F47;KANGXI RADICAL SUN;So;0;ON;<compat> 65E5;;;;N;;;;;
+2F48;KANGXI RADICAL SAY;So;0;ON;<compat> 66F0;;;;N;;;;;
+2F49;KANGXI RADICAL MOON;So;0;ON;<compat> 6708;;;;N;;;;;
+2F4A;KANGXI RADICAL TREE;So;0;ON;<compat> 6728;;;;N;;;;;
+2F4B;KANGXI RADICAL LACK;So;0;ON;<compat> 6B20;;;;N;;;;;
+2F4C;KANGXI RADICAL STOP;So;0;ON;<compat> 6B62;;;;N;;;;;
+2F4D;KANGXI RADICAL DEATH;So;0;ON;<compat> 6B79;;;;N;;;;;
+2F4E;KANGXI RADICAL WEAPON;So;0;ON;<compat> 6BB3;;;;N;;;;;
+2F4F;KANGXI RADICAL DO NOT;So;0;ON;<compat> 6BCB;;;;N;;;;;
+2F50;KANGXI RADICAL COMPARE;So;0;ON;<compat> 6BD4;;;;N;;;;;
+2F51;KANGXI RADICAL FUR;So;0;ON;<compat> 6BDB;;;;N;;;;;
+2F52;KANGXI RADICAL CLAN;So;0;ON;<compat> 6C0F;;;;N;;;;;
+2F53;KANGXI RADICAL STEAM;So;0;ON;<compat> 6C14;;;;N;;;;;
+2F54;KANGXI RADICAL WATER;So;0;ON;<compat> 6C34;;;;N;;;;;
+2F55;KANGXI RADICAL FIRE;So;0;ON;<compat> 706B;;;;N;;;;;
+2F56;KANGXI RADICAL CLAW;So;0;ON;<compat> 722A;;;;N;;;;;
+2F57;KANGXI RADICAL FATHER;So;0;ON;<compat> 7236;;;;N;;;;;
+2F58;KANGXI RADICAL DOUBLE X;So;0;ON;<compat> 723B;;;;N;;;;;
+2F59;KANGXI RADICAL HALF TREE TRUNK;So;0;ON;<compat> 723F;;;;N;;;;;
+2F5A;KANGXI RADICAL SLICE;So;0;ON;<compat> 7247;;;;N;;;;;
+2F5B;KANGXI RADICAL FANG;So;0;ON;<compat> 7259;;;;N;;;;;
+2F5C;KANGXI RADICAL COW;So;0;ON;<compat> 725B;;;;N;;;;;
+2F5D;KANGXI RADICAL DOG;So;0;ON;<compat> 72AC;;;;N;;;;;
+2F5E;KANGXI RADICAL PROFOUND;So;0;ON;<compat> 7384;;;;N;;;;;
+2F5F;KANGXI RADICAL JADE;So;0;ON;<compat> 7389;;;;N;;;;;
+2F60;KANGXI RADICAL MELON;So;0;ON;<compat> 74DC;;;;N;;;;;
+2F61;KANGXI RADICAL TILE;So;0;ON;<compat> 74E6;;;;N;;;;;
+2F62;KANGXI RADICAL SWEET;So;0;ON;<compat> 7518;;;;N;;;;;
+2F63;KANGXI RADICAL LIFE;So;0;ON;<compat> 751F;;;;N;;;;;
+2F64;KANGXI RADICAL USE;So;0;ON;<compat> 7528;;;;N;;;;;
+2F65;KANGXI RADICAL FIELD;So;0;ON;<compat> 7530;;;;N;;;;;
+2F66;KANGXI RADICAL BOLT OF CLOTH;So;0;ON;<compat> 758B;;;;N;;;;;
+2F67;KANGXI RADICAL SICKNESS;So;0;ON;<compat> 7592;;;;N;;;;;
+2F68;KANGXI RADICAL DOTTED TENT;So;0;ON;<compat> 7676;;;;N;;;;;
+2F69;KANGXI RADICAL WHITE;So;0;ON;<compat> 767D;;;;N;;;;;
+2F6A;KANGXI RADICAL SKIN;So;0;ON;<compat> 76AE;;;;N;;;;;
+2F6B;KANGXI RADICAL DISH;So;0;ON;<compat> 76BF;;;;N;;;;;
+2F6C;KANGXI RADICAL EYE;So;0;ON;<compat> 76EE;;;;N;;;;;
+2F6D;KANGXI RADICAL SPEAR;So;0;ON;<compat> 77DB;;;;N;;;;;
+2F6E;KANGXI RADICAL ARROW;So;0;ON;<compat> 77E2;;;;N;;;;;
+2F6F;KANGXI RADICAL STONE;So;0;ON;<compat> 77F3;;;;N;;;;;
+2F70;KANGXI RADICAL SPIRIT;So;0;ON;<compat> 793A;;;;N;;;;;
+2F71;KANGXI RADICAL TRACK;So;0;ON;<compat> 79B8;;;;N;;;;;
+2F72;KANGXI RADICAL GRAIN;So;0;ON;<compat> 79BE;;;;N;;;;;
+2F73;KANGXI RADICAL CAVE;So;0;ON;<compat> 7A74;;;;N;;;;;
+2F74;KANGXI RADICAL STAND;So;0;ON;<compat> 7ACB;;;;N;;;;;
+2F75;KANGXI RADICAL BAMBOO;So;0;ON;<compat> 7AF9;;;;N;;;;;
+2F76;KANGXI RADICAL RICE;So;0;ON;<compat> 7C73;;;;N;;;;;
+2F77;KANGXI RADICAL SILK;So;0;ON;<compat> 7CF8;;;;N;;;;;
+2F78;KANGXI RADICAL JAR;So;0;ON;<compat> 7F36;;;;N;;;;;
+2F79;KANGXI RADICAL NET;So;0;ON;<compat> 7F51;;;;N;;;;;
+2F7A;KANGXI RADICAL SHEEP;So;0;ON;<compat> 7F8A;;;;N;;;;;
+2F7B;KANGXI RADICAL FEATHER;So;0;ON;<compat> 7FBD;;;;N;;;;;
+2F7C;KANGXI RADICAL OLD;So;0;ON;<compat> 8001;;;;N;;;;;
+2F7D;KANGXI RADICAL AND;So;0;ON;<compat> 800C;;;;N;;;;;
+2F7E;KANGXI RADICAL PLOW;So;0;ON;<compat> 8012;;;;N;;;;;
+2F7F;KANGXI RADICAL EAR;So;0;ON;<compat> 8033;;;;N;;;;;
+2F80;KANGXI RADICAL BRUSH;So;0;ON;<compat> 807F;;;;N;;;;;
+2F81;KANGXI RADICAL MEAT;So;0;ON;<compat> 8089;;;;N;;;;;
+2F82;KANGXI RADICAL MINISTER;So;0;ON;<compat> 81E3;;;;N;;;;;
+2F83;KANGXI RADICAL SELF;So;0;ON;<compat> 81EA;;;;N;;;;;
+2F84;KANGXI RADICAL ARRIVE;So;0;ON;<compat> 81F3;;;;N;;;;;
+2F85;KANGXI RADICAL MORTAR;So;0;ON;<compat> 81FC;;;;N;;;;;
+2F86;KANGXI RADICAL TONGUE;So;0;ON;<compat> 820C;;;;N;;;;;
+2F87;KANGXI RADICAL OPPOSE;So;0;ON;<compat> 821B;;;;N;;;;;
+2F88;KANGXI RADICAL BOAT;So;0;ON;<compat> 821F;;;;N;;;;;
+2F89;KANGXI RADICAL STOPPING;So;0;ON;<compat> 826E;;;;N;;;;;
+2F8A;KANGXI RADICAL COLOR;So;0;ON;<compat> 8272;;;;N;;;;;
+2F8B;KANGXI RADICAL GRASS;So;0;ON;<compat> 8278;;;;N;;;;;
+2F8C;KANGXI RADICAL TIGER;So;0;ON;<compat> 864D;;;;N;;;;;
+2F8D;KANGXI RADICAL INSECT;So;0;ON;<compat> 866B;;;;N;;;;;
+2F8E;KANGXI RADICAL BLOOD;So;0;ON;<compat> 8840;;;;N;;;;;
+2F8F;KANGXI RADICAL WALK ENCLOSURE;So;0;ON;<compat> 884C;;;;N;;;;;
+2F90;KANGXI RADICAL CLOTHES;So;0;ON;<compat> 8863;;;;N;;;;;
+2F91;KANGXI RADICAL WEST;So;0;ON;<compat> 897E;;;;N;;;;;
+2F92;KANGXI RADICAL SEE;So;0;ON;<compat> 898B;;;;N;;;;;
+2F93;KANGXI RADICAL HORN;So;0;ON;<compat> 89D2;;;;N;;;;;
+2F94;KANGXI RADICAL SPEECH;So;0;ON;<compat> 8A00;;;;N;;;;;
+2F95;KANGXI RADICAL VALLEY;So;0;ON;<compat> 8C37;;;;N;;;;;
+2F96;KANGXI RADICAL BEAN;So;0;ON;<compat> 8C46;;;;N;;;;;
+2F97;KANGXI RADICAL PIG;So;0;ON;<compat> 8C55;;;;N;;;;;
+2F98;KANGXI RADICAL BADGER;So;0;ON;<compat> 8C78;;;;N;;;;;
+2F99;KANGXI RADICAL SHELL;So;0;ON;<compat> 8C9D;;;;N;;;;;
+2F9A;KANGXI RADICAL RED;So;0;ON;<compat> 8D64;;;;N;;;;;
+2F9B;KANGXI RADICAL RUN;So;0;ON;<compat> 8D70;;;;N;;;;;
+2F9C;KANGXI RADICAL FOOT;So;0;ON;<compat> 8DB3;;;;N;;;;;
+2F9D;KANGXI RADICAL BODY;So;0;ON;<compat> 8EAB;;;;N;;;;;
+2F9E;KANGXI RADICAL CART;So;0;ON;<compat> 8ECA;;;;N;;;;;
+2F9F;KANGXI RADICAL BITTER;So;0;ON;<compat> 8F9B;;;;N;;;;;
+2FA0;KANGXI RADICAL MORNING;So;0;ON;<compat> 8FB0;;;;N;;;;;
+2FA1;KANGXI RADICAL WALK;So;0;ON;<compat> 8FB5;;;;N;;;;;
+2FA2;KANGXI RADICAL CITY;So;0;ON;<compat> 9091;;;;N;;;;;
+2FA3;KANGXI RADICAL WINE;So;0;ON;<compat> 9149;;;;N;;;;;
+2FA4;KANGXI RADICAL DISTINGUISH;So;0;ON;<compat> 91C6;;;;N;;;;;
+2FA5;KANGXI RADICAL VILLAGE;So;0;ON;<compat> 91CC;;;;N;;;;;
+2FA6;KANGXI RADICAL GOLD;So;0;ON;<compat> 91D1;;;;N;;;;;
+2FA7;KANGXI RADICAL LONG;So;0;ON;<compat> 9577;;;;N;;;;;
+2FA8;KANGXI RADICAL GATE;So;0;ON;<compat> 9580;;;;N;;;;;
+2FA9;KANGXI RADICAL MOUND;So;0;ON;<compat> 961C;;;;N;;;;;
+2FAA;KANGXI RADICAL SLAVE;So;0;ON;<compat> 96B6;;;;N;;;;;
+2FAB;KANGXI RADICAL SHORT TAILED BIRD;So;0;ON;<compat> 96B9;;;;N;;;;;
+2FAC;KANGXI RADICAL RAIN;So;0;ON;<compat> 96E8;;;;N;;;;;
+2FAD;KANGXI RADICAL BLUE;So;0;ON;<compat> 9751;;;;N;;;;;
+2FAE;KANGXI RADICAL WRONG;So;0;ON;<compat> 975E;;;;N;;;;;
+2FAF;KANGXI RADICAL FACE;So;0;ON;<compat> 9762;;;;N;;;;;
+2FB0;KANGXI RADICAL LEATHER;So;0;ON;<compat> 9769;;;;N;;;;;
+2FB1;KANGXI RADICAL TANNED LEATHER;So;0;ON;<compat> 97CB;;;;N;;;;;
+2FB2;KANGXI RADICAL LEEK;So;0;ON;<compat> 97ED;;;;N;;;;;
+2FB3;KANGXI RADICAL SOUND;So;0;ON;<compat> 97F3;;;;N;;;;;
+2FB4;KANGXI RADICAL LEAF;So;0;ON;<compat> 9801;;;;N;;;;;
+2FB5;KANGXI RADICAL WIND;So;0;ON;<compat> 98A8;;;;N;;;;;
+2FB6;KANGXI RADICAL FLY;So;0;ON;<compat> 98DB;;;;N;;;;;
+2FB7;KANGXI RADICAL EAT;So;0;ON;<compat> 98DF;;;;N;;;;;
+2FB8;KANGXI RADICAL HEAD;So;0;ON;<compat> 9996;;;;N;;;;;
+2FB9;KANGXI RADICAL FRAGRANT;So;0;ON;<compat> 9999;;;;N;;;;;
+2FBA;KANGXI RADICAL HORSE;So;0;ON;<compat> 99AC;;;;N;;;;;
+2FBB;KANGXI RADICAL BONE;So;0;ON;<compat> 9AA8;;;;N;;;;;
+2FBC;KANGXI RADICAL TALL;So;0;ON;<compat> 9AD8;;;;N;;;;;
+2FBD;KANGXI RADICAL HAIR;So;0;ON;<compat> 9ADF;;;;N;;;;;
+2FBE;KANGXI RADICAL FIGHT;So;0;ON;<compat> 9B25;;;;N;;;;;
+2FBF;KANGXI RADICAL SACRIFICIAL WINE;So;0;ON;<compat> 9B2F;;;;N;;;;;
+2FC0;KANGXI RADICAL CAULDRON;So;0;ON;<compat> 9B32;;;;N;;;;;
+2FC1;KANGXI RADICAL GHOST;So;0;ON;<compat> 9B3C;;;;N;;;;;
+2FC2;KANGXI RADICAL FISH;So;0;ON;<compat> 9B5A;;;;N;;;;;
+2FC3;KANGXI RADICAL BIRD;So;0;ON;<compat> 9CE5;;;;N;;;;;
+2FC4;KANGXI RADICAL SALT;So;0;ON;<compat> 9E75;;;;N;;;;;
+2FC5;KANGXI RADICAL DEER;So;0;ON;<compat> 9E7F;;;;N;;;;;
+2FC6;KANGXI RADICAL WHEAT;So;0;ON;<compat> 9EA5;;;;N;;;;;
+2FC7;KANGXI RADICAL HEMP;So;0;ON;<compat> 9EBB;;;;N;;;;;
+2FC8;KANGXI RADICAL YELLOW;So;0;ON;<compat> 9EC3;;;;N;;;;;
+2FC9;KANGXI RADICAL MILLET;So;0;ON;<compat> 9ECD;;;;N;;;;;
+2FCA;KANGXI RADICAL BLACK;So;0;ON;<compat> 9ED1;;;;N;;;;;
+2FCB;KANGXI RADICAL EMBROIDERY;So;0;ON;<compat> 9EF9;;;;N;;;;;
+2FCC;KANGXI RADICAL FROG;So;0;ON;<compat> 9EFD;;;;N;;;;;
+2FCD;KANGXI RADICAL TRIPOD;So;0;ON;<compat> 9F0E;;;;N;;;;;
+2FCE;KANGXI RADICAL DRUM;So;0;ON;<compat> 9F13;;;;N;;;;;
+2FCF;KANGXI RADICAL RAT;So;0;ON;<compat> 9F20;;;;N;;;;;
+2FD0;KANGXI RADICAL NOSE;So;0;ON;<compat> 9F3B;;;;N;;;;;
+2FD1;KANGXI RADICAL EVEN;So;0;ON;<compat> 9F4A;;;;N;;;;;
+2FD2;KANGXI RADICAL TOOTH;So;0;ON;<compat> 9F52;;;;N;;;;;
+2FD3;KANGXI RADICAL DRAGON;So;0;ON;<compat> 9F8D;;;;N;;;;;
+2FD4;KANGXI RADICAL TURTLE;So;0;ON;<compat> 9F9C;;;;N;;;;;
+2FD5;KANGXI RADICAL FLUTE;So;0;ON;<compat> 9FA0;;;;N;;;;;
+2FF0;IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO RIGHT;So;0;ON;;;;;N;;;;;
+2FF1;IDEOGRAPHIC DESCRIPTION CHARACTER ABOVE TO BELOW;So;0;ON;;;;;N;;;;;
+2FF2;IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO MIDDLE AND RIGHT;So;0;ON;;;;;N;;;;;
+2FF3;IDEOGRAPHIC DESCRIPTION CHARACTER ABOVE TO MIDDLE AND BELOW;So;0;ON;;;;;N;;;;;
+2FF4;IDEOGRAPHIC DESCRIPTION CHARACTER FULL SURROUND;So;0;ON;;;;;N;;;;;
+2FF5;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM ABOVE;So;0;ON;;;;;N;;;;;
+2FF6;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM BELOW;So;0;ON;;;;;N;;;;;
+2FF7;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM LEFT;So;0;ON;;;;;N;;;;;
+2FF8;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM UPPER LEFT;So;0;ON;;;;;N;;;;;
+2FF9;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM UPPER RIGHT;So;0;ON;;;;;N;;;;;
+2FFA;IDEOGRAPHIC DESCRIPTION CHARACTER SURROUND FROM LOWER LEFT;So;0;ON;;;;;N;;;;;
+2FFB;IDEOGRAPHIC DESCRIPTION CHARACTER OVERLAID;So;0;ON;;;;;N;;;;;
+3000;IDEOGRAPHIC SPACE;Zs;0;WS;<wide> 0020;;;;N;;;;;
+3001;IDEOGRAPHIC COMMA;Po;0;ON;;;;;N;;;;;
+3002;IDEOGRAPHIC FULL STOP;Po;0;ON;;;;;N;IDEOGRAPHIC PERIOD;;;;
+3003;DITTO MARK;Po;0;ON;;;;;N;;;;;
+3004;JAPANESE INDUSTRIAL STANDARD SYMBOL;So;0;ON;;;;;N;;;;;
+3005;IDEOGRAPHIC ITERATION MARK;Lm;0;L;;;;;N;;;;;
+3006;IDEOGRAPHIC CLOSING MARK;Lo;0;L;;;;;N;;;;;
+3007;IDEOGRAPHIC NUMBER ZERO;Nl;0;L;;;;0;N;;;;;
+3008;LEFT ANGLE BRACKET;Ps;0;ON;;;;;Y;OPENING ANGLE BRACKET;;;;
+3009;RIGHT ANGLE BRACKET;Pe;0;ON;;;;;Y;CLOSING ANGLE BRACKET;;;;
+300A;LEFT DOUBLE ANGLE BRACKET;Ps;0;ON;;;;;Y;OPENING DOUBLE ANGLE BRACKET;;;;
+300B;RIGHT DOUBLE ANGLE BRACKET;Pe;0;ON;;;;;Y;CLOSING DOUBLE ANGLE BRACKET;;;;
+300C;LEFT CORNER BRACKET;Ps;0;ON;;;;;Y;OPENING CORNER BRACKET;;;;
+300D;RIGHT CORNER BRACKET;Pe;0;ON;;;;;Y;CLOSING CORNER BRACKET;;;;
+300E;LEFT WHITE CORNER BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE CORNER BRACKET;;;;
+300F;RIGHT WHITE CORNER BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE CORNER BRACKET;;;;
+3010;LEFT BLACK LENTICULAR BRACKET;Ps;0;ON;;;;;Y;OPENING BLACK LENTICULAR BRACKET;;;;
+3011;RIGHT BLACK LENTICULAR BRACKET;Pe;0;ON;;;;;Y;CLOSING BLACK LENTICULAR BRACKET;;;;
+3012;POSTAL MARK;So;0;ON;;;;;N;;;;;
+3013;GETA MARK;So;0;ON;;;;;N;;;;;
+3014;LEFT TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;OPENING TORTOISE SHELL BRACKET;;;;
+3015;RIGHT TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;CLOSING TORTOISE SHELL BRACKET;;;;
+3016;LEFT WHITE LENTICULAR BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE LENTICULAR BRACKET;;;;
+3017;RIGHT WHITE LENTICULAR BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE LENTICULAR BRACKET;;;;
+3018;LEFT WHITE TORTOISE SHELL BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE TORTOISE SHELL BRACKET;;;;
+3019;RIGHT WHITE TORTOISE SHELL BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE TORTOISE SHELL BRACKET;;;;
+301A;LEFT WHITE SQUARE BRACKET;Ps;0;ON;;;;;Y;OPENING WHITE SQUARE BRACKET;;;;
+301B;RIGHT WHITE SQUARE BRACKET;Pe;0;ON;;;;;Y;CLOSING WHITE SQUARE BRACKET;;;;
+301C;WAVE DASH;Pd;0;ON;;;;;N;;;;;
+301D;REVERSED DOUBLE PRIME QUOTATION MARK;Ps;0;ON;;;;;N;;;;;
+301E;DOUBLE PRIME QUOTATION MARK;Pe;0;ON;;;;;N;;;;;
+301F;LOW DOUBLE PRIME QUOTATION MARK;Pe;0;ON;;;;;N;;;;;
+3020;POSTAL MARK FACE;So;0;ON;;;;;N;;;;;
+3021;HANGZHOU NUMERAL ONE;Nl;0;L;;;;1;N;;;;;
+3022;HANGZHOU NUMERAL TWO;Nl;0;L;;;;2;N;;;;;
+3023;HANGZHOU NUMERAL THREE;Nl;0;L;;;;3;N;;;;;
+3024;HANGZHOU NUMERAL FOUR;Nl;0;L;;;;4;N;;;;;
+3025;HANGZHOU NUMERAL FIVE;Nl;0;L;;;;5;N;;;;;
+3026;HANGZHOU NUMERAL SIX;Nl;0;L;;;;6;N;;;;;
+3027;HANGZHOU NUMERAL SEVEN;Nl;0;L;;;;7;N;;;;;
+3028;HANGZHOU NUMERAL EIGHT;Nl;0;L;;;;8;N;;;;;
+3029;HANGZHOU NUMERAL NINE;Nl;0;L;;;;9;N;;;;;
+302A;IDEOGRAPHIC LEVEL TONE MARK;Mn;218;NSM;;;;;N;;;;;
+302B;IDEOGRAPHIC RISING TONE MARK;Mn;228;NSM;;;;;N;;;;;
+302C;IDEOGRAPHIC DEPARTING TONE MARK;Mn;232;NSM;;;;;N;;;;;
+302D;IDEOGRAPHIC ENTERING TONE MARK;Mn;222;NSM;;;;;N;;;;;
+302E;HANGUL SINGLE DOT TONE MARK;Mn;224;NSM;;;;;N;;;;;
+302F;HANGUL DOUBLE DOT TONE MARK;Mn;224;NSM;;;;;N;;;;;
+3030;WAVY DASH;Pd;0;ON;;;;;N;;;;;
+3031;VERTICAL KANA REPEAT MARK;Lm;0;L;;;;;N;;;;;
+3032;VERTICAL KANA REPEAT WITH VOICED SOUND MARK;Lm;0;L;;;;;N;;;;;
+3033;VERTICAL KANA REPEAT MARK UPPER HALF;Lm;0;L;;;;;N;;;;;
+3034;VERTICAL KANA REPEAT WITH VOICED SOUND MARK UPPER HALF;Lm;0;L;;;;;N;;;;;
+3035;VERTICAL KANA REPEAT MARK LOWER HALF;Lm;0;L;;;;;N;;;;;
+3036;CIRCLED POSTAL MARK;So;0;ON;<compat> 3012;;;;N;;;;;
+3037;IDEOGRAPHIC TELEGRAPH LINE FEED SEPARATOR SYMBOL;So;0;ON;;;;;N;;;;;
+3038;HANGZHOU NUMERAL TEN;Nl;0;L;<compat> 5341;;;10;N;;;;;
+3039;HANGZHOU NUMERAL TWENTY;Nl;0;L;<compat> 5344;;;20;N;;;;;
+303A;HANGZHOU NUMERAL THIRTY;Nl;0;L;<compat> 5345;;;30;N;;;;;
+303B;VERTICAL IDEOGRAPHIC ITERATION MARK;Lm;0;L;;;;;N;;;;;
+303C;MASU MARK;Lo;0;L;;;;;N;;;;;
+303D;PART ALTERNATION MARK;Po;0;ON;;;;;N;;;;;
+303E;IDEOGRAPHIC VARIATION INDICATOR;So;0;ON;;;;;N;;;;;
+303F;IDEOGRAPHIC HALF FILL SPACE;So;0;ON;;;;;N;;;;;
+3041;HIRAGANA LETTER SMALL A;Lo;0;L;;;;;N;;;;;
+3042;HIRAGANA LETTER A;Lo;0;L;;;;;N;;;;;
+3043;HIRAGANA LETTER SMALL I;Lo;0;L;;;;;N;;;;;
+3044;HIRAGANA LETTER I;Lo;0;L;;;;;N;;;;;
+3045;HIRAGANA LETTER SMALL U;Lo;0;L;;;;;N;;;;;
+3046;HIRAGANA LETTER U;Lo;0;L;;;;;N;;;;;
+3047;HIRAGANA LETTER SMALL E;Lo;0;L;;;;;N;;;;;
+3048;HIRAGANA LETTER E;Lo;0;L;;;;;N;;;;;
+3049;HIRAGANA LETTER SMALL O;Lo;0;L;;;;;N;;;;;
+304A;HIRAGANA LETTER O;Lo;0;L;;;;;N;;;;;
+304B;HIRAGANA LETTER KA;Lo;0;L;;;;;N;;;;;
+304C;HIRAGANA LETTER GA;Lo;0;L;304B 3099;;;;N;;;;;
+304D;HIRAGANA LETTER KI;Lo;0;L;;;;;N;;;;;
+304E;HIRAGANA LETTER GI;Lo;0;L;304D 3099;;;;N;;;;;
+304F;HIRAGANA LETTER KU;Lo;0;L;;;;;N;;;;;
+3050;HIRAGANA LETTER GU;Lo;0;L;304F 3099;;;;N;;;;;
+3051;HIRAGANA LETTER KE;Lo;0;L;;;;;N;;;;;
+3052;HIRAGANA LETTER GE;Lo;0;L;3051 3099;;;;N;;;;;
+3053;HIRAGANA LETTER KO;Lo;0;L;;;;;N;;;;;
+3054;HIRAGANA LETTER GO;Lo;0;L;3053 3099;;;;N;;;;;
+3055;HIRAGANA LETTER SA;Lo;0;L;;;;;N;;;;;
+3056;HIRAGANA LETTER ZA;Lo;0;L;3055 3099;;;;N;;;;;
+3057;HIRAGANA LETTER SI;Lo;0;L;;;;;N;;;;;
+3058;HIRAGANA LETTER ZI;Lo;0;L;3057 3099;;;;N;;;;;
+3059;HIRAGANA LETTER SU;Lo;0;L;;;;;N;;;;;
+305A;HIRAGANA LETTER ZU;Lo;0;L;3059 3099;;;;N;;;;;
+305B;HIRAGANA LETTER SE;Lo;0;L;;;;;N;;;;;
+305C;HIRAGANA LETTER ZE;Lo;0;L;305B 3099;;;;N;;;;;
+305D;HIRAGANA LETTER SO;Lo;0;L;;;;;N;;;;;
+305E;HIRAGANA LETTER ZO;Lo;0;L;305D 3099;;;;N;;;;;
+305F;HIRAGANA LETTER TA;Lo;0;L;;;;;N;;;;;
+3060;HIRAGANA LETTER DA;Lo;0;L;305F 3099;;;;N;;;;;
+3061;HIRAGANA LETTER TI;Lo;0;L;;;;;N;;;;;
+3062;HIRAGANA LETTER DI;Lo;0;L;3061 3099;;;;N;;;;;
+3063;HIRAGANA LETTER SMALL TU;Lo;0;L;;;;;N;;;;;
+3064;HIRAGANA LETTER TU;Lo;0;L;;;;;N;;;;;
+3065;HIRAGANA LETTER DU;Lo;0;L;3064 3099;;;;N;;;;;
+3066;HIRAGANA LETTER TE;Lo;0;L;;;;;N;;;;;
+3067;HIRAGANA LETTER DE;Lo;0;L;3066 3099;;;;N;;;;;
+3068;HIRAGANA LETTER TO;Lo;0;L;;;;;N;;;;;
+3069;HIRAGANA LETTER DO;Lo;0;L;3068 3099;;;;N;;;;;
+306A;HIRAGANA LETTER NA;Lo;0;L;;;;;N;;;;;
+306B;HIRAGANA LETTER NI;Lo;0;L;;;;;N;;;;;
+306C;HIRAGANA LETTER NU;Lo;0;L;;;;;N;;;;;
+306D;HIRAGANA LETTER NE;Lo;0;L;;;;;N;;;;;
+306E;HIRAGANA LETTER NO;Lo;0;L;;;;;N;;;;;
+306F;HIRAGANA LETTER HA;Lo;0;L;;;;;N;;;;;
+3070;HIRAGANA LETTER BA;Lo;0;L;306F 3099;;;;N;;;;;
+3071;HIRAGANA LETTER PA;Lo;0;L;306F 309A;;;;N;;;;;
+3072;HIRAGANA LETTER HI;Lo;0;L;;;;;N;;;;;
+3073;HIRAGANA LETTER BI;Lo;0;L;3072 3099;;;;N;;;;;
+3074;HIRAGANA LETTER PI;Lo;0;L;3072 309A;;;;N;;;;;
+3075;HIRAGANA LETTER HU;Lo;0;L;;;;;N;;;;;
+3076;HIRAGANA LETTER BU;Lo;0;L;3075 3099;;;;N;;;;;
+3077;HIRAGANA LETTER PU;Lo;0;L;3075 309A;;;;N;;;;;
+3078;HIRAGANA LETTER HE;Lo;0;L;;;;;N;;;;;
+3079;HIRAGANA LETTER BE;Lo;0;L;3078 3099;;;;N;;;;;
+307A;HIRAGANA LETTER PE;Lo;0;L;3078 309A;;;;N;;;;;
+307B;HIRAGANA LETTER HO;Lo;0;L;;;;;N;;;;;
+307C;HIRAGANA LETTER BO;Lo;0;L;307B 3099;;;;N;;;;;
+307D;HIRAGANA LETTER PO;Lo;0;L;307B 309A;;;;N;;;;;
+307E;HIRAGANA LETTER MA;Lo;0;L;;;;;N;;;;;
+307F;HIRAGANA LETTER MI;Lo;0;L;;;;;N;;;;;
+3080;HIRAGANA LETTER MU;Lo;0;L;;;;;N;;;;;
+3081;HIRAGANA LETTER ME;Lo;0;L;;;;;N;;;;;
+3082;HIRAGANA LETTER MO;Lo;0;L;;;;;N;;;;;
+3083;HIRAGANA LETTER SMALL YA;Lo;0;L;;;;;N;;;;;
+3084;HIRAGANA LETTER YA;Lo;0;L;;;;;N;;;;;
+3085;HIRAGANA LETTER SMALL YU;Lo;0;L;;;;;N;;;;;
+3086;HIRAGANA LETTER YU;Lo;0;L;;;;;N;;;;;
+3087;HIRAGANA LETTER SMALL YO;Lo;0;L;;;;;N;;;;;
+3088;HIRAGANA LETTER YO;Lo;0;L;;;;;N;;;;;
+3089;HIRAGANA LETTER RA;Lo;0;L;;;;;N;;;;;
+308A;HIRAGANA LETTER RI;Lo;0;L;;;;;N;;;;;
+308B;HIRAGANA LETTER RU;Lo;0;L;;;;;N;;;;;
+308C;HIRAGANA LETTER RE;Lo;0;L;;;;;N;;;;;
+308D;HIRAGANA LETTER RO;Lo;0;L;;;;;N;;;;;
+308E;HIRAGANA LETTER SMALL WA;Lo;0;L;;;;;N;;;;;
+308F;HIRAGANA LETTER WA;Lo;0;L;;;;;N;;;;;
+3090;HIRAGANA LETTER WI;Lo;0;L;;;;;N;;;;;
+3091;HIRAGANA LETTER WE;Lo;0;L;;;;;N;;;;;
+3092;HIRAGANA LETTER WO;Lo;0;L;;;;;N;;;;;
+3093;HIRAGANA LETTER N;Lo;0;L;;;;;N;;;;;
+3094;HIRAGANA LETTER VU;Lo;0;L;3046 3099;;;;N;;;;;
+3095;HIRAGANA LETTER SMALL KA;Lo;0;L;;;;;N;;;;;
+3096;HIRAGANA LETTER SMALL KE;Lo;0;L;;;;;N;;;;;
+3099;COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK;Mn;8;NSM;;;;;N;NON-SPACING KATAKANA-HIRAGANA VOICED SOUND MARK;;;;
+309A;COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK;Mn;8;NSM;;;;;N;NON-SPACING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK;;;;
+309B;KATAKANA-HIRAGANA VOICED SOUND MARK;Sk;0;ON;<compat> 0020 3099;;;;N;;;;;
+309C;KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK;Sk;0;ON;<compat> 0020 309A;;;;N;;;;;
+309D;HIRAGANA ITERATION MARK;Lm;0;L;;;;;N;;;;;
+309E;HIRAGANA VOICED ITERATION MARK;Lm;0;L;309D 3099;;;;N;;;;;
+309F;HIRAGANA DIGRAPH YORI;Lo;0;L;<vertical> 3088 308A;;;;N;;;;;
+30A0;KATAKANA-HIRAGANA DOUBLE HYPHEN;Pd;0;ON;;;;;N;;;;;
+30A1;KATAKANA LETTER SMALL A;Lo;0;L;;;;;N;;;;;
+30A2;KATAKANA LETTER A;Lo;0;L;;;;;N;;;;;
+30A3;KATAKANA LETTER SMALL I;Lo;0;L;;;;;N;;;;;
+30A4;KATAKANA LETTER I;Lo;0;L;;;;;N;;;;;
+30A5;KATAKANA LETTER SMALL U;Lo;0;L;;;;;N;;;;;
+30A6;KATAKANA LETTER U;Lo;0;L;;;;;N;;;;;
+30A7;KATAKANA LETTER SMALL E;Lo;0;L;;;;;N;;;;;
+30A8;KATAKANA LETTER E;Lo;0;L;;;;;N;;;;;
+30A9;KATAKANA LETTER SMALL O;Lo;0;L;;;;;N;;;;;
+30AA;KATAKANA LETTER O;Lo;0;L;;;;;N;;;;;
+30AB;KATAKANA LETTER KA;Lo;0;L;;;;;N;;;;;
+30AC;KATAKANA LETTER GA;Lo;0;L;30AB 3099;;;;N;;;;;
+30AD;KATAKANA LETTER KI;Lo;0;L;;;;;N;;;;;
+30AE;KATAKANA LETTER GI;Lo;0;L;30AD 3099;;;;N;;;;;
+30AF;KATAKANA LETTER KU;Lo;0;L;;;;;N;;;;;
+30B0;KATAKANA LETTER GU;Lo;0;L;30AF 3099;;;;N;;;;;
+30B1;KATAKANA LETTER KE;Lo;0;L;;;;;N;;;;;
+30B2;KATAKANA LETTER GE;Lo;0;L;30B1 3099;;;;N;;;;;
+30B3;KATAKANA LETTER KO;Lo;0;L;;;;;N;;;;;
+30B4;KATAKANA LETTER GO;Lo;0;L;30B3 3099;;;;N;;;;;
+30B5;KATAKANA LETTER SA;Lo;0;L;;;;;N;;;;;
+30B6;KATAKANA LETTER ZA;Lo;0;L;30B5 3099;;;;N;;;;;
+30B7;KATAKANA LETTER SI;Lo;0;L;;;;;N;;;;;
+30B8;KATAKANA LETTER ZI;Lo;0;L;30B7 3099;;;;N;;;;;
+30B9;KATAKANA LETTER SU;Lo;0;L;;;;;N;;;;;
+30BA;KATAKANA LETTER ZU;Lo;0;L;30B9 3099;;;;N;;;;;
+30BB;KATAKANA LETTER SE;Lo;0;L;;;;;N;;;;;
+30BC;KATAKANA LETTER ZE;Lo;0;L;30BB 3099;;;;N;;;;;
+30BD;KATAKANA LETTER SO;Lo;0;L;;;;;N;;;;;
+30BE;KATAKANA LETTER ZO;Lo;0;L;30BD 3099;;;;N;;;;;
+30BF;KATAKANA LETTER TA;Lo;0;L;;;;;N;;;;;
+30C0;KATAKANA LETTER DA;Lo;0;L;30BF 3099;;;;N;;;;;
+30C1;KATAKANA LETTER TI;Lo;0;L;;;;;N;;;;;
+30C2;KATAKANA LETTER DI;Lo;0;L;30C1 3099;;;;N;;;;;
+30C3;KATAKANA LETTER SMALL TU;Lo;0;L;;;;;N;;;;;
+30C4;KATAKANA LETTER TU;Lo;0;L;;;;;N;;;;;
+30C5;KATAKANA LETTER DU;Lo;0;L;30C4 3099;;;;N;;;;;
+30C6;KATAKANA LETTER TE;Lo;0;L;;;;;N;;;;;
+30C7;KATAKANA LETTER DE;Lo;0;L;30C6 3099;;;;N;;;;;
+30C8;KATAKANA LETTER TO;Lo;0;L;;;;;N;;;;;
+30C9;KATAKANA LETTER DO;Lo;0;L;30C8 3099;;;;N;;;;;
+30CA;KATAKANA LETTER NA;Lo;0;L;;;;;N;;;;;
+30CB;KATAKANA LETTER NI;Lo;0;L;;;;;N;;;;;
+30CC;KATAKANA LETTER NU;Lo;0;L;;;;;N;;;;;
+30CD;KATAKANA LETTER NE;Lo;0;L;;;;;N;;;;;
+30CE;KATAKANA LETTER NO;Lo;0;L;;;;;N;;;;;
+30CF;KATAKANA LETTER HA;Lo;0;L;;;;;N;;;;;
+30D0;KATAKANA LETTER BA;Lo;0;L;30CF 3099;;;;N;;;;;
+30D1;KATAKANA LETTER PA;Lo;0;L;30CF 309A;;;;N;;;;;
+30D2;KATAKANA LETTER HI;Lo;0;L;;;;;N;;;;;
+30D3;KATAKANA LETTER BI;Lo;0;L;30D2 3099;;;;N;;;;;
+30D4;KATAKANA LETTER PI;Lo;0;L;30D2 309A;;;;N;;;;;
+30D5;KATAKANA LETTER HU;Lo;0;L;;;;;N;;;;;
+30D6;KATAKANA LETTER BU;Lo;0;L;30D5 3099;;;;N;;;;;
+30D7;KATAKANA LETTER PU;Lo;0;L;30D5 309A;;;;N;;;;;
+30D8;KATAKANA LETTER HE;Lo;0;L;;;;;N;;;;;
+30D9;KATAKANA LETTER BE;Lo;0;L;30D8 3099;;;;N;;;;;
+30DA;KATAKANA LETTER PE;Lo;0;L;30D8 309A;;;;N;;;;;
+30DB;KATAKANA LETTER HO;Lo;0;L;;;;;N;;;;;
+30DC;KATAKANA LETTER BO;Lo;0;L;30DB 3099;;;;N;;;;;
+30DD;KATAKANA LETTER PO;Lo;0;L;30DB 309A;;;;N;;;;;
+30DE;KATAKANA LETTER MA;Lo;0;L;;;;;N;;;;;
+30DF;KATAKANA LETTER MI;Lo;0;L;;;;;N;;;;;
+30E0;KATAKANA LETTER MU;Lo;0;L;;;;;N;;;;;
+30E1;KATAKANA LETTER ME;Lo;0;L;;;;;N;;;;;
+30E2;KATAKANA LETTER MO;Lo;0;L;;;;;N;;;;;
+30E3;KATAKANA LETTER SMALL YA;Lo;0;L;;;;;N;;;;;
+30E4;KATAKANA LETTER YA;Lo;0;L;;;;;N;;;;;
+30E5;KATAKANA LETTER SMALL YU;Lo;0;L;;;;;N;;;;;
+30E6;KATAKANA LETTER YU;Lo;0;L;;;;;N;;;;;
+30E7;KATAKANA LETTER SMALL YO;Lo;0;L;;;;;N;;;;;
+30E8;KATAKANA LETTER YO;Lo;0;L;;;;;N;;;;;
+30E9;KATAKANA LETTER RA;Lo;0;L;;;;;N;;;;;
+30EA;KATAKANA LETTER RI;Lo;0;L;;;;;N;;;;;
+30EB;KATAKANA LETTER RU;Lo;0;L;;;;;N;;;;;
+30EC;KATAKANA LETTER RE;Lo;0;L;;;;;N;;;;;
+30ED;KATAKANA LETTER RO;Lo;0;L;;;;;N;;;;;
+30EE;KATAKANA LETTER SMALL WA;Lo;0;L;;;;;N;;;;;
+30EF;KATAKANA LETTER WA;Lo;0;L;;;;;N;;;;;
+30F0;KATAKANA LETTER WI;Lo;0;L;;;;;N;;;;;
+30F1;KATAKANA LETTER WE;Lo;0;L;;;;;N;;;;;
+30F2;KATAKANA LETTER WO;Lo;0;L;;;;;N;;;;;
+30F3;KATAKANA LETTER N;Lo;0;L;;;;;N;;;;;
+30F4;KATAKANA LETTER VU;Lo;0;L;30A6 3099;;;;N;;;;;
+30F5;KATAKANA LETTER SMALL KA;Lo;0;L;;;;;N;;;;;
+30F6;KATAKANA LETTER SMALL KE;Lo;0;L;;;;;N;;;;;
+30F7;KATAKANA LETTER VA;Lo;0;L;30EF 3099;;;;N;;;;;
+30F8;KATAKANA LETTER VI;Lo;0;L;30F0 3099;;;;N;;;;;
+30F9;KATAKANA LETTER VE;Lo;0;L;30F1 3099;;;;N;;;;;
+30FA;KATAKANA LETTER VO;Lo;0;L;30F2 3099;;;;N;;;;;
+30FB;KATAKANA MIDDLE DOT;Pc;0;ON;;;;;N;;;;;
+30FC;KATAKANA-HIRAGANA PROLONGED SOUND MARK;Lm;0;L;;;;;N;;;;;
+30FD;KATAKANA ITERATION MARK;Lm;0;L;;;;;N;;;;;
+30FE;KATAKANA VOICED ITERATION MARK;Lm;0;L;30FD 3099;;;;N;;;;;
+30FF;KATAKANA DIGRAPH KOTO;Lo;0;L;<vertical> 30B3 30C8;;;;N;;;;;
+3105;BOPOMOFO LETTER B;Lo;0;L;;;;;N;;;;;
+3106;BOPOMOFO LETTER P;Lo;0;L;;;;;N;;;;;
+3107;BOPOMOFO LETTER M;Lo;0;L;;;;;N;;;;;
+3108;BOPOMOFO LETTER F;Lo;0;L;;;;;N;;;;;
+3109;BOPOMOFO LETTER D;Lo;0;L;;;;;N;;;;;
+310A;BOPOMOFO LETTER T;Lo;0;L;;;;;N;;;;;
+310B;BOPOMOFO LETTER N;Lo;0;L;;;;;N;;;;;
+310C;BOPOMOFO LETTER L;Lo;0;L;;;;;N;;;;;
+310D;BOPOMOFO LETTER G;Lo;0;L;;;;;N;;;;;
+310E;BOPOMOFO LETTER K;Lo;0;L;;;;;N;;;;;
+310F;BOPOMOFO LETTER H;Lo;0;L;;;;;N;;;;;
+3110;BOPOMOFO LETTER J;Lo;0;L;;;;;N;;;;;
+3111;BOPOMOFO LETTER Q;Lo;0;L;;;;;N;;;;;
+3112;BOPOMOFO LETTER X;Lo;0;L;;;;;N;;;;;
+3113;BOPOMOFO LETTER ZH;Lo;0;L;;;;;N;;;;;
+3114;BOPOMOFO LETTER CH;Lo;0;L;;;;;N;;;;;
+3115;BOPOMOFO LETTER SH;Lo;0;L;;;;;N;;;;;
+3116;BOPOMOFO LETTER R;Lo;0;L;;;;;N;;;;;
+3117;BOPOMOFO LETTER Z;Lo;0;L;;;;;N;;;;;
+3118;BOPOMOFO LETTER C;Lo;0;L;;;;;N;;;;;
+3119;BOPOMOFO LETTER S;Lo;0;L;;;;;N;;;;;
+311A;BOPOMOFO LETTER A;Lo;0;L;;;;;N;;;;;
+311B;BOPOMOFO LETTER O;Lo;0;L;;;;;N;;;;;
+311C;BOPOMOFO LETTER E;Lo;0;L;;;;;N;;;;;
+311D;BOPOMOFO LETTER EH;Lo;0;L;;;;;N;;;;;
+311E;BOPOMOFO LETTER AI;Lo;0;L;;;;;N;;;;;
+311F;BOPOMOFO LETTER EI;Lo;0;L;;;;;N;;;;;
+3120;BOPOMOFO LETTER AU;Lo;0;L;;;;;N;;;;;
+3121;BOPOMOFO LETTER OU;Lo;0;L;;;;;N;;;;;
+3122;BOPOMOFO LETTER AN;Lo;0;L;;;;;N;;;;;
+3123;BOPOMOFO LETTER EN;Lo;0;L;;;;;N;;;;;
+3124;BOPOMOFO LETTER ANG;Lo;0;L;;;;;N;;;;;
+3125;BOPOMOFO LETTER ENG;Lo;0;L;;;;;N;;;;;
+3126;BOPOMOFO LETTER ER;Lo;0;L;;;;;N;;;;;
+3127;BOPOMOFO LETTER I;Lo;0;L;;;;;N;;;;;
+3128;BOPOMOFO LETTER U;Lo;0;L;;;;;N;;;;;
+3129;BOPOMOFO LETTER IU;Lo;0;L;;;;;N;;;;;
+312A;BOPOMOFO LETTER V;Lo;0;L;;;;;N;;;;;
+312B;BOPOMOFO LETTER NG;Lo;0;L;;;;;N;;;;;
+312C;BOPOMOFO LETTER GN;Lo;0;L;;;;;N;;;;;
+3131;HANGUL LETTER KIYEOK;Lo;0;L;<compat> 1100;;;;N;HANGUL LETTER GIYEOG;;;;
+3132;HANGUL LETTER SSANGKIYEOK;Lo;0;L;<compat> 1101;;;;N;HANGUL LETTER SSANG GIYEOG;;;;
+3133;HANGUL LETTER KIYEOK-SIOS;Lo;0;L;<compat> 11AA;;;;N;HANGUL LETTER GIYEOG SIOS;;;;
+3134;HANGUL LETTER NIEUN;Lo;0;L;<compat> 1102;;;;N;;;;;
+3135;HANGUL LETTER NIEUN-CIEUC;Lo;0;L;<compat> 11AC;;;;N;HANGUL LETTER NIEUN JIEUJ;;;;
+3136;HANGUL LETTER NIEUN-HIEUH;Lo;0;L;<compat> 11AD;;;;N;HANGUL LETTER NIEUN HIEUH;;;;
+3137;HANGUL LETTER TIKEUT;Lo;0;L;<compat> 1103;;;;N;HANGUL LETTER DIGEUD;;;;
+3138;HANGUL LETTER SSANGTIKEUT;Lo;0;L;<compat> 1104;;;;N;HANGUL LETTER SSANG DIGEUD;;;;
+3139;HANGUL LETTER RIEUL;Lo;0;L;<compat> 1105;;;;N;HANGUL LETTER LIEUL;;;;
+313A;HANGUL LETTER RIEUL-KIYEOK;Lo;0;L;<compat> 11B0;;;;N;HANGUL LETTER LIEUL GIYEOG;;;;
+313B;HANGUL LETTER RIEUL-MIEUM;Lo;0;L;<compat> 11B1;;;;N;HANGUL LETTER LIEUL MIEUM;;;;
+313C;HANGUL LETTER RIEUL-PIEUP;Lo;0;L;<compat> 11B2;;;;N;HANGUL LETTER LIEUL BIEUB;;;;
+313D;HANGUL LETTER RIEUL-SIOS;Lo;0;L;<compat> 11B3;;;;N;HANGUL LETTER LIEUL SIOS;;;;
+313E;HANGUL LETTER RIEUL-THIEUTH;Lo;0;L;<compat> 11B4;;;;N;HANGUL LETTER LIEUL TIEUT;;;;
+313F;HANGUL LETTER RIEUL-PHIEUPH;Lo;0;L;<compat> 11B5;;;;N;HANGUL LETTER LIEUL PIEUP;;;;
+3140;HANGUL LETTER RIEUL-HIEUH;Lo;0;L;<compat> 111A;;;;N;HANGUL LETTER LIEUL HIEUH;;;;
+3141;HANGUL LETTER MIEUM;Lo;0;L;<compat> 1106;;;;N;;;;;
+3142;HANGUL LETTER PIEUP;Lo;0;L;<compat> 1107;;;;N;HANGUL LETTER BIEUB;;;;
+3143;HANGUL LETTER SSANGPIEUP;Lo;0;L;<compat> 1108;;;;N;HANGUL LETTER SSANG BIEUB;;;;
+3144;HANGUL LETTER PIEUP-SIOS;Lo;0;L;<compat> 1121;;;;N;HANGUL LETTER BIEUB SIOS;;;;
+3145;HANGUL LETTER SIOS;Lo;0;L;<compat> 1109;;;;N;;;;;
+3146;HANGUL LETTER SSANGSIOS;Lo;0;L;<compat> 110A;;;;N;HANGUL LETTER SSANG SIOS;;;;
+3147;HANGUL LETTER IEUNG;Lo;0;L;<compat> 110B;;;;N;;;;;
+3148;HANGUL LETTER CIEUC;Lo;0;L;<compat> 110C;;;;N;HANGUL LETTER JIEUJ;;;;
+3149;HANGUL LETTER SSANGCIEUC;Lo;0;L;<compat> 110D;;;;N;HANGUL LETTER SSANG JIEUJ;;;;
+314A;HANGUL LETTER CHIEUCH;Lo;0;L;<compat> 110E;;;;N;HANGUL LETTER CIEUC;;;;
+314B;HANGUL LETTER KHIEUKH;Lo;0;L;<compat> 110F;;;;N;HANGUL LETTER KIYEOK;;;;
+314C;HANGUL LETTER THIEUTH;Lo;0;L;<compat> 1110;;;;N;HANGUL LETTER TIEUT;;;;
+314D;HANGUL LETTER PHIEUPH;Lo;0;L;<compat> 1111;;;;N;HANGUL LETTER PIEUP;;;;
+314E;HANGUL LETTER HIEUH;Lo;0;L;<compat> 1112;;;;N;;;;;
+314F;HANGUL LETTER A;Lo;0;L;<compat> 1161;;;;N;;;;;
+3150;HANGUL LETTER AE;Lo;0;L;<compat> 1162;;;;N;;;;;
+3151;HANGUL LETTER YA;Lo;0;L;<compat> 1163;;;;N;;;;;
+3152;HANGUL LETTER YAE;Lo;0;L;<compat> 1164;;;;N;;;;;
+3153;HANGUL LETTER EO;Lo;0;L;<compat> 1165;;;;N;;;;;
+3154;HANGUL LETTER E;Lo;0;L;<compat> 1166;;;;N;;;;;
+3155;HANGUL LETTER YEO;Lo;0;L;<compat> 1167;;;;N;;;;;
+3156;HANGUL LETTER YE;Lo;0;L;<compat> 1168;;;;N;;;;;
+3157;HANGUL LETTER O;Lo;0;L;<compat> 1169;;;;N;;;;;
+3158;HANGUL LETTER WA;Lo;0;L;<compat> 116A;;;;N;;;;;
+3159;HANGUL LETTER WAE;Lo;0;L;<compat> 116B;;;;N;;;;;
+315A;HANGUL LETTER OE;Lo;0;L;<compat> 116C;;;;N;;;;;
+315B;HANGUL LETTER YO;Lo;0;L;<compat> 116D;;;;N;;;;;
+315C;HANGUL LETTER U;Lo;0;L;<compat> 116E;;;;N;;;;;
+315D;HANGUL LETTER WEO;Lo;0;L;<compat> 116F;;;;N;;;;;
+315E;HANGUL LETTER WE;Lo;0;L;<compat> 1170;;;;N;;;;;
+315F;HANGUL LETTER WI;Lo;0;L;<compat> 1171;;;;N;;;;;
+3160;HANGUL LETTER YU;Lo;0;L;<compat> 1172;;;;N;;;;;
+3161;HANGUL LETTER EU;Lo;0;L;<compat> 1173;;;;N;;;;;
+3162;HANGUL LETTER YI;Lo;0;L;<compat> 1174;;;;N;;;;;
+3163;HANGUL LETTER I;Lo;0;L;<compat> 1175;;;;N;;;;;
+3164;HANGUL FILLER;Lo;0;L;<compat> 1160;;;;N;HANGUL CAE OM;;;;
+3165;HANGUL LETTER SSANGNIEUN;Lo;0;L;<compat> 1114;;;;N;HANGUL LETTER SSANG NIEUN;;;;
+3166;HANGUL LETTER NIEUN-TIKEUT;Lo;0;L;<compat> 1115;;;;N;HANGUL LETTER NIEUN DIGEUD;;;;
+3167;HANGUL LETTER NIEUN-SIOS;Lo;0;L;<compat> 11C7;;;;N;HANGUL LETTER NIEUN SIOS;;;;
+3168;HANGUL LETTER NIEUN-PANSIOS;Lo;0;L;<compat> 11C8;;;;N;HANGUL LETTER NIEUN BAN CHI EUM;;;;
+3169;HANGUL LETTER RIEUL-KIYEOK-SIOS;Lo;0;L;<compat> 11CC;;;;N;HANGUL LETTER LIEUL GIYEOG SIOS;;;;
+316A;HANGUL LETTER RIEUL-TIKEUT;Lo;0;L;<compat> 11CE;;;;N;HANGUL LETTER LIEUL DIGEUD;;;;
+316B;HANGUL LETTER RIEUL-PIEUP-SIOS;Lo;0;L;<compat> 11D3;;;;N;HANGUL LETTER LIEUL BIEUB SIOS;;;;
+316C;HANGUL LETTER RIEUL-PANSIOS;Lo;0;L;<compat> 11D7;;;;N;HANGUL LETTER LIEUL BAN CHI EUM;;;;
+316D;HANGUL LETTER RIEUL-YEORINHIEUH;Lo;0;L;<compat> 11D9;;;;N;HANGUL LETTER LIEUL YEOLIN HIEUH;;;;
+316E;HANGUL LETTER MIEUM-PIEUP;Lo;0;L;<compat> 111C;;;;N;HANGUL LETTER MIEUM BIEUB;;;;
+316F;HANGUL LETTER MIEUM-SIOS;Lo;0;L;<compat> 11DD;;;;N;HANGUL LETTER MIEUM SIOS;;;;
+3170;HANGUL LETTER MIEUM-PANSIOS;Lo;0;L;<compat> 11DF;;;;N;HANGUL LETTER BIEUB BAN CHI EUM;;;;
+3171;HANGUL LETTER KAPYEOUNMIEUM;Lo;0;L;<compat> 111D;;;;N;HANGUL LETTER MIEUM SUN GYEONG EUM;;;;
+3172;HANGUL LETTER PIEUP-KIYEOK;Lo;0;L;<compat> 111E;;;;N;HANGUL LETTER BIEUB GIYEOG;;;;
+3173;HANGUL LETTER PIEUP-TIKEUT;Lo;0;L;<compat> 1120;;;;N;HANGUL LETTER BIEUB DIGEUD;;;;
+3174;HANGUL LETTER PIEUP-SIOS-KIYEOK;Lo;0;L;<compat> 1122;;;;N;HANGUL LETTER BIEUB SIOS GIYEOG;;;;
+3175;HANGUL LETTER PIEUP-SIOS-TIKEUT;Lo;0;L;<compat> 1123;;;;N;HANGUL LETTER BIEUB SIOS DIGEUD;;;;
+3176;HANGUL LETTER PIEUP-CIEUC;Lo;0;L;<compat> 1127;;;;N;HANGUL LETTER BIEUB JIEUJ;;;;
+3177;HANGUL LETTER PIEUP-THIEUTH;Lo;0;L;<compat> 1129;;;;N;HANGUL LETTER BIEUB TIEUT;;;;
+3178;HANGUL LETTER KAPYEOUNPIEUP;Lo;0;L;<compat> 112B;;;;N;HANGUL LETTER BIEUB SUN GYEONG EUM;;;;
+3179;HANGUL LETTER KAPYEOUNSSANGPIEUP;Lo;0;L;<compat> 112C;;;;N;HANGUL LETTER SSANG BIEUB SUN GYEONG EUM;;;;
+317A;HANGUL LETTER SIOS-KIYEOK;Lo;0;L;<compat> 112D;;;;N;HANGUL LETTER SIOS GIYEOG;;;;
+317B;HANGUL LETTER SIOS-NIEUN;Lo;0;L;<compat> 112E;;;;N;HANGUL LETTER SIOS NIEUN;;;;
+317C;HANGUL LETTER SIOS-TIKEUT;Lo;0;L;<compat> 112F;;;;N;HANGUL LETTER SIOS DIGEUD;;;;
+317D;HANGUL LETTER SIOS-PIEUP;Lo;0;L;<compat> 1132;;;;N;HANGUL LETTER SIOS BIEUB;;;;
+317E;HANGUL LETTER SIOS-CIEUC;Lo;0;L;<compat> 1136;;;;N;HANGUL LETTER SIOS JIEUJ;;;;
+317F;HANGUL LETTER PANSIOS;Lo;0;L;<compat> 1140;;;;N;HANGUL LETTER BAN CHI EUM;;;;
+3180;HANGUL LETTER SSANGIEUNG;Lo;0;L;<compat> 1147;;;;N;HANGUL LETTER SSANG IEUNG;;;;
+3181;HANGUL LETTER YESIEUNG;Lo;0;L;<compat> 114C;;;;N;HANGUL LETTER NGIEUNG;;;;
+3182;HANGUL LETTER YESIEUNG-SIOS;Lo;0;L;<compat> 11F1;;;;N;HANGUL LETTER NGIEUNG SIOS;;;;
+3183;HANGUL LETTER YESIEUNG-PANSIOS;Lo;0;L;<compat> 11F2;;;;N;HANGUL LETTER NGIEUNG BAN CHI EUM;;;;
+3184;HANGUL LETTER KAPYEOUNPHIEUPH;Lo;0;L;<compat> 1157;;;;N;HANGUL LETTER PIEUP SUN GYEONG EUM;;;;
+3185;HANGUL LETTER SSANGHIEUH;Lo;0;L;<compat> 1158;;;;N;HANGUL LETTER SSANG HIEUH;;;;
+3186;HANGUL LETTER YEORINHIEUH;Lo;0;L;<compat> 1159;;;;N;HANGUL LETTER YEOLIN HIEUH;;;;
+3187;HANGUL LETTER YO-YA;Lo;0;L;<compat> 1184;;;;N;HANGUL LETTER YOYA;;;;
+3188;HANGUL LETTER YO-YAE;Lo;0;L;<compat> 1185;;;;N;HANGUL LETTER YOYAE;;;;
+3189;HANGUL LETTER YO-I;Lo;0;L;<compat> 1188;;;;N;HANGUL LETTER YOI;;;;
+318A;HANGUL LETTER YU-YEO;Lo;0;L;<compat> 1191;;;;N;HANGUL LETTER YUYEO;;;;
+318B;HANGUL LETTER YU-YE;Lo;0;L;<compat> 1192;;;;N;HANGUL LETTER YUYE;;;;
+318C;HANGUL LETTER YU-I;Lo;0;L;<compat> 1194;;;;N;HANGUL LETTER YUI;;;;
+318D;HANGUL LETTER ARAEA;Lo;0;L;<compat> 119E;;;;N;HANGUL LETTER ALAE A;;;;
+318E;HANGUL LETTER ARAEAE;Lo;0;L;<compat> 11A1;;;;N;HANGUL LETTER ALAE AE;;;;
+3190;IDEOGRAPHIC ANNOTATION LINKING MARK;So;0;L;;;;;N;KANBUN TATETEN;Kanbun Tateten;;;
+3191;IDEOGRAPHIC ANNOTATION REVERSE MARK;So;0;L;;;;;N;KAERITEN RE;Kaeriten;;;
+3192;IDEOGRAPHIC ANNOTATION ONE MARK;No;0;L;<super> 4E00;;;1;N;KAERITEN ITI;Kaeriten;;;
+3193;IDEOGRAPHIC ANNOTATION TWO MARK;No;0;L;<super> 4E8C;;;2;N;KAERITEN NI;Kaeriten;;;
+3194;IDEOGRAPHIC ANNOTATION THREE MARK;No;0;L;<super> 4E09;;;3;N;KAERITEN SAN;Kaeriten;;;
+3195;IDEOGRAPHIC ANNOTATION FOUR MARK;No;0;L;<super> 56DB;;;4;N;KAERITEN SI;Kaeriten;;;
+3196;IDEOGRAPHIC ANNOTATION TOP MARK;So;0;L;<super> 4E0A;;;;N;KAERITEN ZYOU;Kaeriten;;;
+3197;IDEOGRAPHIC ANNOTATION MIDDLE MARK;So;0;L;<super> 4E2D;;;;N;KAERITEN TYUU;Kaeriten;;;
+3198;IDEOGRAPHIC ANNOTATION BOTTOM MARK;So;0;L;<super> 4E0B;;;;N;KAERITEN GE;Kaeriten;;;
+3199;IDEOGRAPHIC ANNOTATION FIRST MARK;So;0;L;<super> 7532;;;;N;KAERITEN KOU;Kaeriten;;;
+319A;IDEOGRAPHIC ANNOTATION SECOND MARK;So;0;L;<super> 4E59;;;;N;KAERITEN OTU;Kaeriten;;;
+319B;IDEOGRAPHIC ANNOTATION THIRD MARK;So;0;L;<super> 4E19;;;;N;KAERITEN HEI;Kaeriten;;;
+319C;IDEOGRAPHIC ANNOTATION FOURTH MARK;So;0;L;<super> 4E01;;;;N;KAERITEN TEI;Kaeriten;;;
+319D;IDEOGRAPHIC ANNOTATION HEAVEN MARK;So;0;L;<super> 5929;;;;N;KAERITEN TEN;Kaeriten;;;
+319E;IDEOGRAPHIC ANNOTATION EARTH MARK;So;0;L;<super> 5730;;;;N;KAERITEN TI;Kaeriten;;;
+319F;IDEOGRAPHIC ANNOTATION MAN MARK;So;0;L;<super> 4EBA;;;;N;KAERITEN ZIN;Kaeriten;;;
+31A0;BOPOMOFO LETTER BU;Lo;0;L;;;;;N;;;;;
+31A1;BOPOMOFO LETTER ZI;Lo;0;L;;;;;N;;;;;
+31A2;BOPOMOFO LETTER JI;Lo;0;L;;;;;N;;;;;
+31A3;BOPOMOFO LETTER GU;Lo;0;L;;;;;N;;;;;
+31A4;BOPOMOFO LETTER EE;Lo;0;L;;;;;N;;;;;
+31A5;BOPOMOFO LETTER ENN;Lo;0;L;;;;;N;;;;;
+31A6;BOPOMOFO LETTER OO;Lo;0;L;;;;;N;;;;;
+31A7;BOPOMOFO LETTER ONN;Lo;0;L;;;;;N;;;;;
+31A8;BOPOMOFO LETTER IR;Lo;0;L;;;;;N;;;;;
+31A9;BOPOMOFO LETTER ANN;Lo;0;L;;;;;N;;;;;
+31AA;BOPOMOFO LETTER INN;Lo;0;L;;;;;N;;;;;
+31AB;BOPOMOFO LETTER UNN;Lo;0;L;;;;;N;;;;;
+31AC;BOPOMOFO LETTER IM;Lo;0;L;;;;;N;;;;;
+31AD;BOPOMOFO LETTER NGG;Lo;0;L;;;;;N;;;;;
+31AE;BOPOMOFO LETTER AINN;Lo;0;L;;;;;N;;;;;
+31AF;BOPOMOFO LETTER AUNN;Lo;0;L;;;;;N;;;;;
+31B0;BOPOMOFO LETTER AM;Lo;0;L;;;;;N;;;;;
+31B1;BOPOMOFO LETTER OM;Lo;0;L;;;;;N;;;;;
+31B2;BOPOMOFO LETTER ONG;Lo;0;L;;;;;N;;;;;
+31B3;BOPOMOFO LETTER INNN;Lo;0;L;;;;;N;;;;;
+31B4;BOPOMOFO FINAL LETTER P;Lo;0;L;;;;;N;;;;;
+31B5;BOPOMOFO FINAL LETTER T;Lo;0;L;;;;;N;;;;;
+31B6;BOPOMOFO FINAL LETTER K;Lo;0;L;;;;;N;;;;;
+31B7;BOPOMOFO FINAL LETTER H;Lo;0;L;;;;;N;;;;;
+31F0;KATAKANA LETTER SMALL KU;Lo;0;L;;;;;N;;;;;
+31F1;KATAKANA LETTER SMALL SI;Lo;0;L;;;;;N;;;;;
+31F2;KATAKANA LETTER SMALL SU;Lo;0;L;;;;;N;;;;;
+31F3;KATAKANA LETTER SMALL TO;Lo;0;L;;;;;N;;;;;
+31F4;KATAKANA LETTER SMALL NU;Lo;0;L;;;;;N;;;;;
+31F5;KATAKANA LETTER SMALL HA;Lo;0;L;;;;;N;;;;;
+31F6;KATAKANA LETTER SMALL HI;Lo;0;L;;;;;N;;;;;
+31F7;KATAKANA LETTER SMALL HU;Lo;0;L;;;;;N;;;;;
+31F8;KATAKANA LETTER SMALL HE;Lo;0;L;;;;;N;;;;;
+31F9;KATAKANA LETTER SMALL HO;Lo;0;L;;;;;N;;;;;
+31FA;KATAKANA LETTER SMALL MU;Lo;0;L;;;;;N;;;;;
+31FB;KATAKANA LETTER SMALL RA;Lo;0;L;;;;;N;;;;;
+31FC;KATAKANA LETTER SMALL RI;Lo;0;L;;;;;N;;;;;
+31FD;KATAKANA LETTER SMALL RU;Lo;0;L;;;;;N;;;;;
+31FE;KATAKANA LETTER SMALL RE;Lo;0;L;;;;;N;;;;;
+31FF;KATAKANA LETTER SMALL RO;Lo;0;L;;;;;N;;;;;
+3200;PARENTHESIZED HANGUL KIYEOK;So;0;L;<compat> 0028 1100 0029;;;;N;PARENTHESIZED HANGUL GIYEOG;;;;
+3201;PARENTHESIZED HANGUL NIEUN;So;0;L;<compat> 0028 1102 0029;;;;N;;;;;
+3202;PARENTHESIZED HANGUL TIKEUT;So;0;L;<compat> 0028 1103 0029;;;;N;PARENTHESIZED HANGUL DIGEUD;;;;
+3203;PARENTHESIZED HANGUL RIEUL;So;0;L;<compat> 0028 1105 0029;;;;N;PARENTHESIZED HANGUL LIEUL;;;;
+3204;PARENTHESIZED HANGUL MIEUM;So;0;L;<compat> 0028 1106 0029;;;;N;;;;;
+3205;PARENTHESIZED HANGUL PIEUP;So;0;L;<compat> 0028 1107 0029;;;;N;PARENTHESIZED HANGUL BIEUB;;;;
+3206;PARENTHESIZED HANGUL SIOS;So;0;L;<compat> 0028 1109 0029;;;;N;;;;;
+3207;PARENTHESIZED HANGUL IEUNG;So;0;L;<compat> 0028 110B 0029;;;;N;;;;;
+3208;PARENTHESIZED HANGUL CIEUC;So;0;L;<compat> 0028 110C 0029;;;;N;PARENTHESIZED HANGUL JIEUJ;;;;
+3209;PARENTHESIZED HANGUL CHIEUCH;So;0;L;<compat> 0028 110E 0029;;;;N;PARENTHESIZED HANGUL CIEUC;;;;
+320A;PARENTHESIZED HANGUL KHIEUKH;So;0;L;<compat> 0028 110F 0029;;;;N;PARENTHESIZED HANGUL KIYEOK;;;;
+320B;PARENTHESIZED HANGUL THIEUTH;So;0;L;<compat> 0028 1110 0029;;;;N;PARENTHESIZED HANGUL TIEUT;;;;
+320C;PARENTHESIZED HANGUL PHIEUPH;So;0;L;<compat> 0028 1111 0029;;;;N;PARENTHESIZED HANGUL PIEUP;;;;
+320D;PARENTHESIZED HANGUL HIEUH;So;0;L;<compat> 0028 1112 0029;;;;N;;;;;
+320E;PARENTHESIZED HANGUL KIYEOK A;So;0;L;<compat> 0028 1100 1161 0029;;;;N;PARENTHESIZED HANGUL GA;;;;
+320F;PARENTHESIZED HANGUL NIEUN A;So;0;L;<compat> 0028 1102 1161 0029;;;;N;PARENTHESIZED HANGUL NA;;;;
+3210;PARENTHESIZED HANGUL TIKEUT A;So;0;L;<compat> 0028 1103 1161 0029;;;;N;PARENTHESIZED HANGUL DA;;;;
+3211;PARENTHESIZED HANGUL RIEUL A;So;0;L;<compat> 0028 1105 1161 0029;;;;N;PARENTHESIZED HANGUL LA;;;;
+3212;PARENTHESIZED HANGUL MIEUM A;So;0;L;<compat> 0028 1106 1161 0029;;;;N;PARENTHESIZED HANGUL MA;;;;
+3213;PARENTHESIZED HANGUL PIEUP A;So;0;L;<compat> 0028 1107 1161 0029;;;;N;PARENTHESIZED HANGUL BA;;;;
+3214;PARENTHESIZED HANGUL SIOS A;So;0;L;<compat> 0028 1109 1161 0029;;;;N;PARENTHESIZED HANGUL SA;;;;
+3215;PARENTHESIZED HANGUL IEUNG A;So;0;L;<compat> 0028 110B 1161 0029;;;;N;PARENTHESIZED HANGUL A;;;;
+3216;PARENTHESIZED HANGUL CIEUC A;So;0;L;<compat> 0028 110C 1161 0029;;;;N;PARENTHESIZED HANGUL JA;;;;
+3217;PARENTHESIZED HANGUL CHIEUCH A;So;0;L;<compat> 0028 110E 1161 0029;;;;N;PARENTHESIZED HANGUL CA;;;;
+3218;PARENTHESIZED HANGUL KHIEUKH A;So;0;L;<compat> 0028 110F 1161 0029;;;;N;PARENTHESIZED HANGUL KA;;;;
+3219;PARENTHESIZED HANGUL THIEUTH A;So;0;L;<compat> 0028 1110 1161 0029;;;;N;PARENTHESIZED HANGUL TA;;;;
+321A;PARENTHESIZED HANGUL PHIEUPH A;So;0;L;<compat> 0028 1111 1161 0029;;;;N;PARENTHESIZED HANGUL PA;;;;
+321B;PARENTHESIZED HANGUL HIEUH A;So;0;L;<compat> 0028 1112 1161 0029;;;;N;PARENTHESIZED HANGUL HA;;;;
+321C;PARENTHESIZED HANGUL CIEUC U;So;0;L;<compat> 0028 110C 116E 0029;;;;N;PARENTHESIZED HANGUL JU;;;;
+3220;PARENTHESIZED IDEOGRAPH ONE;No;0;L;<compat> 0028 4E00 0029;;;1;N;;;;;
+3221;PARENTHESIZED IDEOGRAPH TWO;No;0;L;<compat> 0028 4E8C 0029;;;2;N;;;;;
+3222;PARENTHESIZED IDEOGRAPH THREE;No;0;L;<compat> 0028 4E09 0029;;;3;N;;;;;
+3223;PARENTHESIZED IDEOGRAPH FOUR;No;0;L;<compat> 0028 56DB 0029;;;4;N;;;;;
+3224;PARENTHESIZED IDEOGRAPH FIVE;No;0;L;<compat> 0028 4E94 0029;;;5;N;;;;;
+3225;PARENTHESIZED IDEOGRAPH SIX;No;0;L;<compat> 0028 516D 0029;;;6;N;;;;;
+3226;PARENTHESIZED IDEOGRAPH SEVEN;No;0;L;<compat> 0028 4E03 0029;;;7;N;;;;;
+3227;PARENTHESIZED IDEOGRAPH EIGHT;No;0;L;<compat> 0028 516B 0029;;;8;N;;;;;
+3228;PARENTHESIZED IDEOGRAPH NINE;No;0;L;<compat> 0028 4E5D 0029;;;9;N;;;;;
+3229;PARENTHESIZED IDEOGRAPH TEN;No;0;L;<compat> 0028 5341 0029;;;10;N;;;;;
+322A;PARENTHESIZED IDEOGRAPH MOON;So;0;L;<compat> 0028 6708 0029;;;;N;;;;;
+322B;PARENTHESIZED IDEOGRAPH FIRE;So;0;L;<compat> 0028 706B 0029;;;;N;;;;;
+322C;PARENTHESIZED IDEOGRAPH WATER;So;0;L;<compat> 0028 6C34 0029;;;;N;;;;;
+322D;PARENTHESIZED IDEOGRAPH WOOD;So;0;L;<compat> 0028 6728 0029;;;;N;;;;;
+322E;PARENTHESIZED IDEOGRAPH METAL;So;0;L;<compat> 0028 91D1 0029;;;;N;;;;;
+322F;PARENTHESIZED IDEOGRAPH EARTH;So;0;L;<compat> 0028 571F 0029;;;;N;;;;;
+3230;PARENTHESIZED IDEOGRAPH SUN;So;0;L;<compat> 0028 65E5 0029;;;;N;;;;;
+3231;PARENTHESIZED IDEOGRAPH STOCK;So;0;L;<compat> 0028 682A 0029;;;;N;;;;;
+3232;PARENTHESIZED IDEOGRAPH HAVE;So;0;L;<compat> 0028 6709 0029;;;;N;;;;;
+3233;PARENTHESIZED IDEOGRAPH SOCIETY;So;0;L;<compat> 0028 793E 0029;;;;N;;;;;
+3234;PARENTHESIZED IDEOGRAPH NAME;So;0;L;<compat> 0028 540D 0029;;;;N;;;;;
+3235;PARENTHESIZED IDEOGRAPH SPECIAL;So;0;L;<compat> 0028 7279 0029;;;;N;;;;;
+3236;PARENTHESIZED IDEOGRAPH FINANCIAL;So;0;L;<compat> 0028 8CA1 0029;;;;N;;;;;
+3237;PARENTHESIZED IDEOGRAPH CONGRATULATION;So;0;L;<compat> 0028 795D 0029;;;;N;;;;;
+3238;PARENTHESIZED IDEOGRAPH LABOR;So;0;L;<compat> 0028 52B4 0029;;;;N;;;;;
+3239;PARENTHESIZED IDEOGRAPH REPRESENT;So;0;L;<compat> 0028 4EE3 0029;;;;N;;;;;
+323A;PARENTHESIZED IDEOGRAPH CALL;So;0;L;<compat> 0028 547C 0029;;;;N;;;;;
+323B;PARENTHESIZED IDEOGRAPH STUDY;So;0;L;<compat> 0028 5B66 0029;;;;N;;;;;
+323C;PARENTHESIZED IDEOGRAPH SUPERVISE;So;0;L;<compat> 0028 76E3 0029;;;;N;;;;;
+323D;PARENTHESIZED IDEOGRAPH ENTERPRISE;So;0;L;<compat> 0028 4F01 0029;;;;N;;;;;
+323E;PARENTHESIZED IDEOGRAPH RESOURCE;So;0;L;<compat> 0028 8CC7 0029;;;;N;;;;;
+323F;PARENTHESIZED IDEOGRAPH ALLIANCE;So;0;L;<compat> 0028 5354 0029;;;;N;;;;;
+3240;PARENTHESIZED IDEOGRAPH FESTIVAL;So;0;L;<compat> 0028 796D 0029;;;;N;;;;;
+3241;PARENTHESIZED IDEOGRAPH REST;So;0;L;<compat> 0028 4F11 0029;;;;N;;;;;
+3242;PARENTHESIZED IDEOGRAPH SELF;So;0;L;<compat> 0028 81EA 0029;;;;N;;;;;
+3243;PARENTHESIZED IDEOGRAPH REACH;So;0;L;<compat> 0028 81F3 0029;;;;N;;;;;
+3251;CIRCLED NUMBER TWENTY ONE;No;0;ON;<circle> 0032 0031;;;21;N;;;;;
+3252;CIRCLED NUMBER TWENTY TWO;No;0;ON;<circle> 0032 0032;;;22;N;;;;;
+3253;CIRCLED NUMBER TWENTY THREE;No;0;ON;<circle> 0032 0033;;;23;N;;;;;
+3254;CIRCLED NUMBER TWENTY FOUR;No;0;ON;<circle> 0032 0034;;;24;N;;;;;
+3255;CIRCLED NUMBER TWENTY FIVE;No;0;ON;<circle> 0032 0035;;;25;N;;;;;
+3256;CIRCLED NUMBER TWENTY SIX;No;0;ON;<circle> 0032 0036;;;26;N;;;;;
+3257;CIRCLED NUMBER TWENTY SEVEN;No;0;ON;<circle> 0032 0037;;;27;N;;;;;
+3258;CIRCLED NUMBER TWENTY EIGHT;No;0;ON;<circle> 0032 0038;;;28;N;;;;;
+3259;CIRCLED NUMBER TWENTY NINE;No;0;ON;<circle> 0032 0039;;;29;N;;;;;
+325A;CIRCLED NUMBER THIRTY;No;0;ON;<circle> 0033 0030;;;30;N;;;;;
+325B;CIRCLED NUMBER THIRTY ONE;No;0;ON;<circle> 0033 0031;;;31;N;;;;;
+325C;CIRCLED NUMBER THIRTY TWO;No;0;ON;<circle> 0033 0032;;;32;N;;;;;
+325D;CIRCLED NUMBER THIRTY THREE;No;0;ON;<circle> 0033 0033;;;33;N;;;;;
+325E;CIRCLED NUMBER THIRTY FOUR;No;0;ON;<circle> 0033 0034;;;34;N;;;;;
+325F;CIRCLED NUMBER THIRTY FIVE;No;0;ON;<circle> 0033 0035;;;35;N;;;;;
+3260;CIRCLED HANGUL KIYEOK;So;0;L;<circle> 1100;;;;N;CIRCLED HANGUL GIYEOG;;;;
+3261;CIRCLED HANGUL NIEUN;So;0;L;<circle> 1102;;;;N;;;;;
+3262;CIRCLED HANGUL TIKEUT;So;0;L;<circle> 1103;;;;N;CIRCLED HANGUL DIGEUD;;;;
+3263;CIRCLED HANGUL RIEUL;So;0;L;<circle> 1105;;;;N;CIRCLED HANGUL LIEUL;;;;
+3264;CIRCLED HANGUL MIEUM;So;0;L;<circle> 1106;;;;N;;;;;
+3265;CIRCLED HANGUL PIEUP;So;0;L;<circle> 1107;;;;N;CIRCLED HANGUL BIEUB;;;;
+3266;CIRCLED HANGUL SIOS;So;0;L;<circle> 1109;;;;N;;;;;
+3267;CIRCLED HANGUL IEUNG;So;0;L;<circle> 110B;;;;N;;;;;
+3268;CIRCLED HANGUL CIEUC;So;0;L;<circle> 110C;;;;N;CIRCLED HANGUL JIEUJ;;;;
+3269;CIRCLED HANGUL CHIEUCH;So;0;L;<circle> 110E;;;;N;CIRCLED HANGUL CIEUC;;;;
+326A;CIRCLED HANGUL KHIEUKH;So;0;L;<circle> 110F;;;;N;CIRCLED HANGUL KIYEOK;;;;
+326B;CIRCLED HANGUL THIEUTH;So;0;L;<circle> 1110;;;;N;CIRCLED HANGUL TIEUT;;;;
+326C;CIRCLED HANGUL PHIEUPH;So;0;L;<circle> 1111;;;;N;CIRCLED HANGUL PIEUP;;;;
+326D;CIRCLED HANGUL HIEUH;So;0;L;<circle> 1112;;;;N;;;;;
+326E;CIRCLED HANGUL KIYEOK A;So;0;L;<circle> 1100 1161;;;;N;CIRCLED HANGUL GA;;;;
+326F;CIRCLED HANGUL NIEUN A;So;0;L;<circle> 1102 1161;;;;N;CIRCLED HANGUL NA;;;;
+3270;CIRCLED HANGUL TIKEUT A;So;0;L;<circle> 1103 1161;;;;N;CIRCLED HANGUL DA;;;;
+3271;CIRCLED HANGUL RIEUL A;So;0;L;<circle> 1105 1161;;;;N;CIRCLED HANGUL LA;;;;
+3272;CIRCLED HANGUL MIEUM A;So;0;L;<circle> 1106 1161;;;;N;CIRCLED HANGUL MA;;;;
+3273;CIRCLED HANGUL PIEUP A;So;0;L;<circle> 1107 1161;;;;N;CIRCLED HANGUL BA;;;;
+3274;CIRCLED HANGUL SIOS A;So;0;L;<circle> 1109 1161;;;;N;CIRCLED HANGUL SA;;;;
+3275;CIRCLED HANGUL IEUNG A;So;0;L;<circle> 110B 1161;;;;N;CIRCLED HANGUL A;;;;
+3276;CIRCLED HANGUL CIEUC A;So;0;L;<circle> 110C 1161;;;;N;CIRCLED HANGUL JA;;;;
+3277;CIRCLED HANGUL CHIEUCH A;So;0;L;<circle> 110E 1161;;;;N;CIRCLED HANGUL CA;;;;
+3278;CIRCLED HANGUL KHIEUKH A;So;0;L;<circle> 110F 1161;;;;N;CIRCLED HANGUL KA;;;;
+3279;CIRCLED HANGUL THIEUTH A;So;0;L;<circle> 1110 1161;;;;N;CIRCLED HANGUL TA;;;;
+327A;CIRCLED HANGUL PHIEUPH A;So;0;L;<circle> 1111 1161;;;;N;CIRCLED HANGUL PA;;;;
+327B;CIRCLED HANGUL HIEUH A;So;0;L;<circle> 1112 1161;;;;N;CIRCLED HANGUL HA;;;;
+327F;KOREAN STANDARD SYMBOL;So;0;L;;;;;N;;;;;
+3280;CIRCLED IDEOGRAPH ONE;No;0;L;<circle> 4E00;;;1;N;;;;;
+3281;CIRCLED IDEOGRAPH TWO;No;0;L;<circle> 4E8C;;;2;N;;;;;
+3282;CIRCLED IDEOGRAPH THREE;No;0;L;<circle> 4E09;;;3;N;;;;;
+3283;CIRCLED IDEOGRAPH FOUR;No;0;L;<circle> 56DB;;;4;N;;;;;
+3284;CIRCLED IDEOGRAPH FIVE;No;0;L;<circle> 4E94;;;5;N;;;;;
+3285;CIRCLED IDEOGRAPH SIX;No;0;L;<circle> 516D;;;6;N;;;;;
+3286;CIRCLED IDEOGRAPH SEVEN;No;0;L;<circle> 4E03;;;7;N;;;;;
+3287;CIRCLED IDEOGRAPH EIGHT;No;0;L;<circle> 516B;;;8;N;;;;;
+3288;CIRCLED IDEOGRAPH NINE;No;0;L;<circle> 4E5D;;;9;N;;;;;
+3289;CIRCLED IDEOGRAPH TEN;No;0;L;<circle> 5341;;;10;N;;;;;
+328A;CIRCLED IDEOGRAPH MOON;So;0;L;<circle> 6708;;;;N;;;;;
+328B;CIRCLED IDEOGRAPH FIRE;So;0;L;<circle> 706B;;;;N;;;;;
+328C;CIRCLED IDEOGRAPH WATER;So;0;L;<circle> 6C34;;;;N;;;;;
+328D;CIRCLED IDEOGRAPH WOOD;So;0;L;<circle> 6728;;;;N;;;;;
+328E;CIRCLED IDEOGRAPH METAL;So;0;L;<circle> 91D1;;;;N;;;;;
+328F;CIRCLED IDEOGRAPH EARTH;So;0;L;<circle> 571F;;;;N;;;;;
+3290;CIRCLED IDEOGRAPH SUN;So;0;L;<circle> 65E5;;;;N;;;;;
+3291;CIRCLED IDEOGRAPH STOCK;So;0;L;<circle> 682A;;;;N;;;;;
+3292;CIRCLED IDEOGRAPH HAVE;So;0;L;<circle> 6709;;;;N;;;;;
+3293;CIRCLED IDEOGRAPH SOCIETY;So;0;L;<circle> 793E;;;;N;;;;;
+3294;CIRCLED IDEOGRAPH NAME;So;0;L;<circle> 540D;;;;N;;;;;
+3295;CIRCLED IDEOGRAPH SPECIAL;So;0;L;<circle> 7279;;;;N;;;;;
+3296;CIRCLED IDEOGRAPH FINANCIAL;So;0;L;<circle> 8CA1;;;;N;;;;;
+3297;CIRCLED IDEOGRAPH CONGRATULATION;So;0;L;<circle> 795D;;;;N;;;;;
+3298;CIRCLED IDEOGRAPH LABOR;So;0;L;<circle> 52B4;;;;N;;;;;
+3299;CIRCLED IDEOGRAPH SECRET;So;0;L;<circle> 79D8;;;;N;;;;;
+329A;CIRCLED IDEOGRAPH MALE;So;0;L;<circle> 7537;;;;N;;;;;
+329B;CIRCLED IDEOGRAPH FEMALE;So;0;L;<circle> 5973;;;;N;;;;;
+329C;CIRCLED IDEOGRAPH SUITABLE;So;0;L;<circle> 9069;;;;N;;;;;
+329D;CIRCLED IDEOGRAPH EXCELLENT;So;0;L;<circle> 512A;;;;N;;;;;
+329E;CIRCLED IDEOGRAPH PRINT;So;0;L;<circle> 5370;;;;N;;;;;
+329F;CIRCLED IDEOGRAPH ATTENTION;So;0;L;<circle> 6CE8;;;;N;;;;;
+32A0;CIRCLED IDEOGRAPH ITEM;So;0;L;<circle> 9805;;;;N;;;;;
+32A1;CIRCLED IDEOGRAPH REST;So;0;L;<circle> 4F11;;;;N;;;;;
+32A2;CIRCLED IDEOGRAPH COPY;So;0;L;<circle> 5199;;;;N;;;;;
+32A3;CIRCLED IDEOGRAPH CORRECT;So;0;L;<circle> 6B63;;;;N;;;;;
+32A4;CIRCLED IDEOGRAPH HIGH;So;0;L;<circle> 4E0A;;;;N;;;;;
+32A5;CIRCLED IDEOGRAPH CENTRE;So;0;L;<circle> 4E2D;;;;N;CIRCLED IDEOGRAPH CENTER;;;;
+32A6;CIRCLED IDEOGRAPH LOW;So;0;L;<circle> 4E0B;;;;N;;;;;
+32A7;CIRCLED IDEOGRAPH LEFT;So;0;L;<circle> 5DE6;;;;N;;;;;
+32A8;CIRCLED IDEOGRAPH RIGHT;So;0;L;<circle> 53F3;;;;N;;;;;
+32A9;CIRCLED IDEOGRAPH MEDICINE;So;0;L;<circle> 533B;;;;N;;;;;
+32AA;CIRCLED IDEOGRAPH RELIGION;So;0;L;<circle> 5B97;;;;N;;;;;
+32AB;CIRCLED IDEOGRAPH STUDY;So;0;L;<circle> 5B66;;;;N;;;;;
+32AC;CIRCLED IDEOGRAPH SUPERVISE;So;0;L;<circle> 76E3;;;;N;;;;;
+32AD;CIRCLED IDEOGRAPH ENTERPRISE;So;0;L;<circle> 4F01;;;;N;;;;;
+32AE;CIRCLED IDEOGRAPH RESOURCE;So;0;L;<circle> 8CC7;;;;N;;;;;
+32AF;CIRCLED IDEOGRAPH ALLIANCE;So;0;L;<circle> 5354;;;;N;;;;;
+32B0;CIRCLED IDEOGRAPH NIGHT;So;0;L;<circle> 591C;;;;N;;;;;
+32B1;CIRCLED NUMBER THIRTY SIX;No;0;ON;<circle> 0033 0036;;;36;N;;;;;
+32B2;CIRCLED NUMBER THIRTY SEVEN;No;0;ON;<circle> 0033 0037;;;37;N;;;;;
+32B3;CIRCLED NUMBER THIRTY EIGHT;No;0;ON;<circle> 0033 0038;;;38;N;;;;;
+32B4;CIRCLED NUMBER THIRTY NINE;No;0;ON;<circle> 0033 0039;;;39;N;;;;;
+32B5;CIRCLED NUMBER FORTY;No;0;ON;<circle> 0034 0030;;;40;N;;;;;
+32B6;CIRCLED NUMBER FORTY ONE;No;0;ON;<circle> 0034 0031;;;41;N;;;;;
+32B7;CIRCLED NUMBER FORTY TWO;No;0;ON;<circle> 0034 0032;;;42;N;;;;;
+32B8;CIRCLED NUMBER FORTY THREE;No;0;ON;<circle> 0034 0033;;;43;N;;;;;
+32B9;CIRCLED NUMBER FORTY FOUR;No;0;ON;<circle> 0034 0034;;;44;N;;;;;
+32BA;CIRCLED NUMBER FORTY FIVE;No;0;ON;<circle> 0034 0035;;;45;N;;;;;
+32BB;CIRCLED NUMBER FORTY SIX;No;0;ON;<circle> 0034 0036;;;46;N;;;;;
+32BC;CIRCLED NUMBER FORTY SEVEN;No;0;ON;<circle> 0034 0037;;;47;N;;;;;
+32BD;CIRCLED NUMBER FORTY EIGHT;No;0;ON;<circle> 0034 0038;;;48;N;;;;;
+32BE;CIRCLED NUMBER FORTY NINE;No;0;ON;<circle> 0034 0039;;;49;N;;;;;
+32BF;CIRCLED NUMBER FIFTY;No;0;ON;<circle> 0035 0030;;;50;N;;;;;
+32C0;IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY;So;0;L;<compat> 0031 6708;;;;N;;;;;
+32C1;IDEOGRAPHIC TELEGRAPH SYMBOL FOR FEBRUARY;So;0;L;<compat> 0032 6708;;;;N;;;;;
+32C2;IDEOGRAPHIC TELEGRAPH SYMBOL FOR MARCH;So;0;L;<compat> 0033 6708;;;;N;;;;;
+32C3;IDEOGRAPHIC TELEGRAPH SYMBOL FOR APRIL;So;0;L;<compat> 0034 6708;;;;N;;;;;
+32C4;IDEOGRAPHIC TELEGRAPH SYMBOL FOR MAY;So;0;L;<compat> 0035 6708;;;;N;;;;;
+32C5;IDEOGRAPHIC TELEGRAPH SYMBOL FOR JUNE;So;0;L;<compat> 0036 6708;;;;N;;;;;
+32C6;IDEOGRAPHIC TELEGRAPH SYMBOL FOR JULY;So;0;L;<compat> 0037 6708;;;;N;;;;;
+32C7;IDEOGRAPHIC TELEGRAPH SYMBOL FOR AUGUST;So;0;L;<compat> 0038 6708;;;;N;;;;;
+32C8;IDEOGRAPHIC TELEGRAPH SYMBOL FOR SEPTEMBER;So;0;L;<compat> 0039 6708;;;;N;;;;;
+32C9;IDEOGRAPHIC TELEGRAPH SYMBOL FOR OCTOBER;So;0;L;<compat> 0031 0030 6708;;;;N;;;;;
+32CA;IDEOGRAPHIC TELEGRAPH SYMBOL FOR NOVEMBER;So;0;L;<compat> 0031 0031 6708;;;;N;;;;;
+32CB;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DECEMBER;So;0;L;<compat> 0031 0032 6708;;;;N;;;;;
+32D0;CIRCLED KATAKANA A;So;0;L;<circle> 30A2;;;;N;;;;;
+32D1;CIRCLED KATAKANA I;So;0;L;<circle> 30A4;;;;N;;;;;
+32D2;CIRCLED KATAKANA U;So;0;L;<circle> 30A6;;;;N;;;;;
+32D3;CIRCLED KATAKANA E;So;0;L;<circle> 30A8;;;;N;;;;;
+32D4;CIRCLED KATAKANA O;So;0;L;<circle> 30AA;;;;N;;;;;
+32D5;CIRCLED KATAKANA KA;So;0;L;<circle> 30AB;;;;N;;;;;
+32D6;CIRCLED KATAKANA KI;So;0;L;<circle> 30AD;;;;N;;;;;
+32D7;CIRCLED KATAKANA KU;So;0;L;<circle> 30AF;;;;N;;;;;
+32D8;CIRCLED KATAKANA KE;So;0;L;<circle> 30B1;;;;N;;;;;
+32D9;CIRCLED KATAKANA KO;So;0;L;<circle> 30B3;;;;N;;;;;
+32DA;CIRCLED KATAKANA SA;So;0;L;<circle> 30B5;;;;N;;;;;
+32DB;CIRCLED KATAKANA SI;So;0;L;<circle> 30B7;;;;N;;;;;
+32DC;CIRCLED KATAKANA SU;So;0;L;<circle> 30B9;;;;N;;;;;
+32DD;CIRCLED KATAKANA SE;So;0;L;<circle> 30BB;;;;N;;;;;
+32DE;CIRCLED KATAKANA SO;So;0;L;<circle> 30BD;;;;N;;;;;
+32DF;CIRCLED KATAKANA TA;So;0;L;<circle> 30BF;;;;N;;;;;
+32E0;CIRCLED KATAKANA TI;So;0;L;<circle> 30C1;;;;N;;;;;
+32E1;CIRCLED KATAKANA TU;So;0;L;<circle> 30C4;;;;N;;;;;
+32E2;CIRCLED KATAKANA TE;So;0;L;<circle> 30C6;;;;N;;;;;
+32E3;CIRCLED KATAKANA TO;So;0;L;<circle> 30C8;;;;N;;;;;
+32E4;CIRCLED KATAKANA NA;So;0;L;<circle> 30CA;;;;N;;;;;
+32E5;CIRCLED KATAKANA NI;So;0;L;<circle> 30CB;;;;N;;;;;
+32E6;CIRCLED KATAKANA NU;So;0;L;<circle> 30CC;;;;N;;;;;
+32E7;CIRCLED KATAKANA NE;So;0;L;<circle> 30CD;;;;N;;;;;
+32E8;CIRCLED KATAKANA NO;So;0;L;<circle> 30CE;;;;N;;;;;
+32E9;CIRCLED KATAKANA HA;So;0;L;<circle> 30CF;;;;N;;;;;
+32EA;CIRCLED KATAKANA HI;So;0;L;<circle> 30D2;;;;N;;;;;
+32EB;CIRCLED KATAKANA HU;So;0;L;<circle> 30D5;;;;N;;;;;
+32EC;CIRCLED KATAKANA HE;So;0;L;<circle> 30D8;;;;N;;;;;
+32ED;CIRCLED KATAKANA HO;So;0;L;<circle> 30DB;;;;N;;;;;
+32EE;CIRCLED KATAKANA MA;So;0;L;<circle> 30DE;;;;N;;;;;
+32EF;CIRCLED KATAKANA MI;So;0;L;<circle> 30DF;;;;N;;;;;
+32F0;CIRCLED KATAKANA MU;So;0;L;<circle> 30E0;;;;N;;;;;
+32F1;CIRCLED KATAKANA ME;So;0;L;<circle> 30E1;;;;N;;;;;
+32F2;CIRCLED KATAKANA MO;So;0;L;<circle> 30E2;;;;N;;;;;
+32F3;CIRCLED KATAKANA YA;So;0;L;<circle> 30E4;;;;N;;;;;
+32F4;CIRCLED KATAKANA YU;So;0;L;<circle> 30E6;;;;N;;;;;
+32F5;CIRCLED KATAKANA YO;So;0;L;<circle> 30E8;;;;N;;;;;
+32F6;CIRCLED KATAKANA RA;So;0;L;<circle> 30E9;;;;N;;;;;
+32F7;CIRCLED KATAKANA RI;So;0;L;<circle> 30EA;;;;N;;;;;
+32F8;CIRCLED KATAKANA RU;So;0;L;<circle> 30EB;;;;N;;;;;
+32F9;CIRCLED KATAKANA RE;So;0;L;<circle> 30EC;;;;N;;;;;
+32FA;CIRCLED KATAKANA RO;So;0;L;<circle> 30ED;;;;N;;;;;
+32FB;CIRCLED KATAKANA WA;So;0;L;<circle> 30EF;;;;N;;;;;
+32FC;CIRCLED KATAKANA WI;So;0;L;<circle> 30F0;;;;N;;;;;
+32FD;CIRCLED KATAKANA WE;So;0;L;<circle> 30F1;;;;N;;;;;
+32FE;CIRCLED KATAKANA WO;So;0;L;<circle> 30F2;;;;N;;;;;
+3300;SQUARE APAATO;So;0;L;<square> 30A2 30D1 30FC 30C8;;;;N;SQUARED APAATO;;;;
+3301;SQUARE ARUHUA;So;0;L;<square> 30A2 30EB 30D5 30A1;;;;N;SQUARED ARUHUA;;;;
+3302;SQUARE ANPEA;So;0;L;<square> 30A2 30F3 30DA 30A2;;;;N;SQUARED ANPEA;;;;
+3303;SQUARE AARU;So;0;L;<square> 30A2 30FC 30EB;;;;N;SQUARED AARU;;;;
+3304;SQUARE ININGU;So;0;L;<square> 30A4 30CB 30F3 30B0;;;;N;SQUARED ININGU;;;;
+3305;SQUARE INTI;So;0;L;<square> 30A4 30F3 30C1;;;;N;SQUARED INTI;;;;
+3306;SQUARE UON;So;0;L;<square> 30A6 30A9 30F3;;;;N;SQUARED UON;;;;
+3307;SQUARE ESUKUUDO;So;0;L;<square> 30A8 30B9 30AF 30FC 30C9;;;;N;SQUARED ESUKUUDO;;;;
+3308;SQUARE EEKAA;So;0;L;<square> 30A8 30FC 30AB 30FC;;;;N;SQUARED EEKAA;;;;
+3309;SQUARE ONSU;So;0;L;<square> 30AA 30F3 30B9;;;;N;SQUARED ONSU;;;;
+330A;SQUARE OOMU;So;0;L;<square> 30AA 30FC 30E0;;;;N;SQUARED OOMU;;;;
+330B;SQUARE KAIRI;So;0;L;<square> 30AB 30A4 30EA;;;;N;SQUARED KAIRI;;;;
+330C;SQUARE KARATTO;So;0;L;<square> 30AB 30E9 30C3 30C8;;;;N;SQUARED KARATTO;;;;
+330D;SQUARE KARORII;So;0;L;<square> 30AB 30ED 30EA 30FC;;;;N;SQUARED KARORII;;;;
+330E;SQUARE GARON;So;0;L;<square> 30AC 30ED 30F3;;;;N;SQUARED GARON;;;;
+330F;SQUARE GANMA;So;0;L;<square> 30AC 30F3 30DE;;;;N;SQUARED GANMA;;;;
+3310;SQUARE GIGA;So;0;L;<square> 30AE 30AC;;;;N;SQUARED GIGA;;;;
+3311;SQUARE GINII;So;0;L;<square> 30AE 30CB 30FC;;;;N;SQUARED GINII;;;;
+3312;SQUARE KYURII;So;0;L;<square> 30AD 30E5 30EA 30FC;;;;N;SQUARED KYURII;;;;
+3313;SQUARE GIRUDAA;So;0;L;<square> 30AE 30EB 30C0 30FC;;;;N;SQUARED GIRUDAA;;;;
+3314;SQUARE KIRO;So;0;L;<square> 30AD 30ED;;;;N;SQUARED KIRO;;;;
+3315;SQUARE KIROGURAMU;So;0;L;<square> 30AD 30ED 30B0 30E9 30E0;;;;N;SQUARED KIROGURAMU;;;;
+3316;SQUARE KIROMEETORU;So;0;L;<square> 30AD 30ED 30E1 30FC 30C8 30EB;;;;N;SQUARED KIROMEETORU;;;;
+3317;SQUARE KIROWATTO;So;0;L;<square> 30AD 30ED 30EF 30C3 30C8;;;;N;SQUARED KIROWATTO;;;;
+3318;SQUARE GURAMU;So;0;L;<square> 30B0 30E9 30E0;;;;N;SQUARED GURAMU;;;;
+3319;SQUARE GURAMUTON;So;0;L;<square> 30B0 30E9 30E0 30C8 30F3;;;;N;SQUARED GURAMUTON;;;;
+331A;SQUARE KURUZEIRO;So;0;L;<square> 30AF 30EB 30BC 30A4 30ED;;;;N;SQUARED KURUZEIRO;;;;
+331B;SQUARE KUROONE;So;0;L;<square> 30AF 30ED 30FC 30CD;;;;N;SQUARED KUROONE;;;;
+331C;SQUARE KEESU;So;0;L;<square> 30B1 30FC 30B9;;;;N;SQUARED KEESU;;;;
+331D;SQUARE KORUNA;So;0;L;<square> 30B3 30EB 30CA;;;;N;SQUARED KORUNA;;;;
+331E;SQUARE KOOPO;So;0;L;<square> 30B3 30FC 30DD;;;;N;SQUARED KOOPO;;;;
+331F;SQUARE SAIKURU;So;0;L;<square> 30B5 30A4 30AF 30EB;;;;N;SQUARED SAIKURU;;;;
+3320;SQUARE SANTIIMU;So;0;L;<square> 30B5 30F3 30C1 30FC 30E0;;;;N;SQUARED SANTIIMU;;;;
+3321;SQUARE SIRINGU;So;0;L;<square> 30B7 30EA 30F3 30B0;;;;N;SQUARED SIRINGU;;;;
+3322;SQUARE SENTI;So;0;L;<square> 30BB 30F3 30C1;;;;N;SQUARED SENTI;;;;
+3323;SQUARE SENTO;So;0;L;<square> 30BB 30F3 30C8;;;;N;SQUARED SENTO;;;;
+3324;SQUARE DAASU;So;0;L;<square> 30C0 30FC 30B9;;;;N;SQUARED DAASU;;;;
+3325;SQUARE DESI;So;0;L;<square> 30C7 30B7;;;;N;SQUARED DESI;;;;
+3326;SQUARE DORU;So;0;L;<square> 30C9 30EB;;;;N;SQUARED DORU;;;;
+3327;SQUARE TON;So;0;L;<square> 30C8 30F3;;;;N;SQUARED TON;;;;
+3328;SQUARE NANO;So;0;L;<square> 30CA 30CE;;;;N;SQUARED NANO;;;;
+3329;SQUARE NOTTO;So;0;L;<square> 30CE 30C3 30C8;;;;N;SQUARED NOTTO;;;;
+332A;SQUARE HAITU;So;0;L;<square> 30CF 30A4 30C4;;;;N;SQUARED HAITU;;;;
+332B;SQUARE PAASENTO;So;0;L;<square> 30D1 30FC 30BB 30F3 30C8;;;;N;SQUARED PAASENTO;;;;
+332C;SQUARE PAATU;So;0;L;<square> 30D1 30FC 30C4;;;;N;SQUARED PAATU;;;;
+332D;SQUARE BAARERU;So;0;L;<square> 30D0 30FC 30EC 30EB;;;;N;SQUARED BAARERU;;;;
+332E;SQUARE PIASUTORU;So;0;L;<square> 30D4 30A2 30B9 30C8 30EB;;;;N;SQUARED PIASUTORU;;;;
+332F;SQUARE PIKURU;So;0;L;<square> 30D4 30AF 30EB;;;;N;SQUARED PIKURU;;;;
+3330;SQUARE PIKO;So;0;L;<square> 30D4 30B3;;;;N;SQUARED PIKO;;;;
+3331;SQUARE BIRU;So;0;L;<square> 30D3 30EB;;;;N;SQUARED BIRU;;;;
+3332;SQUARE HUARADDO;So;0;L;<square> 30D5 30A1 30E9 30C3 30C9;;;;N;SQUARED HUARADDO;;;;
+3333;SQUARE HUIITO;So;0;L;<square> 30D5 30A3 30FC 30C8;;;;N;SQUARED HUIITO;;;;
+3334;SQUARE BUSSYERU;So;0;L;<square> 30D6 30C3 30B7 30A7 30EB;;;;N;SQUARED BUSSYERU;;;;
+3335;SQUARE HURAN;So;0;L;<square> 30D5 30E9 30F3;;;;N;SQUARED HURAN;;;;
+3336;SQUARE HEKUTAARU;So;0;L;<square> 30D8 30AF 30BF 30FC 30EB;;;;N;SQUARED HEKUTAARU;;;;
+3337;SQUARE PESO;So;0;L;<square> 30DA 30BD;;;;N;SQUARED PESO;;;;
+3338;SQUARE PENIHI;So;0;L;<square> 30DA 30CB 30D2;;;;N;SQUARED PENIHI;;;;
+3339;SQUARE HERUTU;So;0;L;<square> 30D8 30EB 30C4;;;;N;SQUARED HERUTU;;;;
+333A;SQUARE PENSU;So;0;L;<square> 30DA 30F3 30B9;;;;N;SQUARED PENSU;;;;
+333B;SQUARE PEEZI;So;0;L;<square> 30DA 30FC 30B8;;;;N;SQUARED PEEZI;;;;
+333C;SQUARE BEETA;So;0;L;<square> 30D9 30FC 30BF;;;;N;SQUARED BEETA;;;;
+333D;SQUARE POINTO;So;0;L;<square> 30DD 30A4 30F3 30C8;;;;N;SQUARED POINTO;;;;
+333E;SQUARE BORUTO;So;0;L;<square> 30DC 30EB 30C8;;;;N;SQUARED BORUTO;;;;
+333F;SQUARE HON;So;0;L;<square> 30DB 30F3;;;;N;SQUARED HON;;;;
+3340;SQUARE PONDO;So;0;L;<square> 30DD 30F3 30C9;;;;N;SQUARED PONDO;;;;
+3341;SQUARE HOORU;So;0;L;<square> 30DB 30FC 30EB;;;;N;SQUARED HOORU;;;;
+3342;SQUARE HOON;So;0;L;<square> 30DB 30FC 30F3;;;;N;SQUARED HOON;;;;
+3343;SQUARE MAIKURO;So;0;L;<square> 30DE 30A4 30AF 30ED;;;;N;SQUARED MAIKURO;;;;
+3344;SQUARE MAIRU;So;0;L;<square> 30DE 30A4 30EB;;;;N;SQUARED MAIRU;;;;
+3345;SQUARE MAHHA;So;0;L;<square> 30DE 30C3 30CF;;;;N;SQUARED MAHHA;;;;
+3346;SQUARE MARUKU;So;0;L;<square> 30DE 30EB 30AF;;;;N;SQUARED MARUKU;;;;
+3347;SQUARE MANSYON;So;0;L;<square> 30DE 30F3 30B7 30E7 30F3;;;;N;SQUARED MANSYON;;;;
+3348;SQUARE MIKURON;So;0;L;<square> 30DF 30AF 30ED 30F3;;;;N;SQUARED MIKURON;;;;
+3349;SQUARE MIRI;So;0;L;<square> 30DF 30EA;;;;N;SQUARED MIRI;;;;
+334A;SQUARE MIRIBAARU;So;0;L;<square> 30DF 30EA 30D0 30FC 30EB;;;;N;SQUARED MIRIBAARU;;;;
+334B;SQUARE MEGA;So;0;L;<square> 30E1 30AC;;;;N;SQUARED MEGA;;;;
+334C;SQUARE MEGATON;So;0;L;<square> 30E1 30AC 30C8 30F3;;;;N;SQUARED MEGATON;;;;
+334D;SQUARE MEETORU;So;0;L;<square> 30E1 30FC 30C8 30EB;;;;N;SQUARED MEETORU;;;;
+334E;SQUARE YAADO;So;0;L;<square> 30E4 30FC 30C9;;;;N;SQUARED YAADO;;;;
+334F;SQUARE YAARU;So;0;L;<square> 30E4 30FC 30EB;;;;N;SQUARED YAARU;;;;
+3350;SQUARE YUAN;So;0;L;<square> 30E6 30A2 30F3;;;;N;SQUARED YUAN;;;;
+3351;SQUARE RITTORU;So;0;L;<square> 30EA 30C3 30C8 30EB;;;;N;SQUARED RITTORU;;;;
+3352;SQUARE RIRA;So;0;L;<square> 30EA 30E9;;;;N;SQUARED RIRA;;;;
+3353;SQUARE RUPII;So;0;L;<square> 30EB 30D4 30FC;;;;N;SQUARED RUPII;;;;
+3354;SQUARE RUUBURU;So;0;L;<square> 30EB 30FC 30D6 30EB;;;;N;SQUARED RUUBURU;;;;
+3355;SQUARE REMU;So;0;L;<square> 30EC 30E0;;;;N;SQUARED REMU;;;;
+3356;SQUARE RENTOGEN;So;0;L;<square> 30EC 30F3 30C8 30B2 30F3;;;;N;SQUARED RENTOGEN;;;;
+3357;SQUARE WATTO;So;0;L;<square> 30EF 30C3 30C8;;;;N;SQUARED WATTO;;;;
+3358;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ZERO;So;0;L;<compat> 0030 70B9;;;;N;;;;;
+3359;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ONE;So;0;L;<compat> 0031 70B9;;;;N;;;;;
+335A;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWO;So;0;L;<compat> 0032 70B9;;;;N;;;;;
+335B;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THREE;So;0;L;<compat> 0033 70B9;;;;N;;;;;
+335C;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOUR;So;0;L;<compat> 0034 70B9;;;;N;;;;;
+335D;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIVE;So;0;L;<compat> 0035 70B9;;;;N;;;;;
+335E;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIX;So;0;L;<compat> 0036 70B9;;;;N;;;;;
+335F;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVEN;So;0;L;<compat> 0037 70B9;;;;N;;;;;
+3360;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHT;So;0;L;<compat> 0038 70B9;;;;N;;;;;
+3361;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINE;So;0;L;<compat> 0039 70B9;;;;N;;;;;
+3362;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TEN;So;0;L;<compat> 0031 0030 70B9;;;;N;;;;;
+3363;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR ELEVEN;So;0;L;<compat> 0031 0031 70B9;;;;N;;;;;
+3364;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWELVE;So;0;L;<compat> 0031 0032 70B9;;;;N;;;;;
+3365;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR THIRTEEN;So;0;L;<compat> 0031 0033 70B9;;;;N;;;;;
+3366;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FOURTEEN;So;0;L;<compat> 0031 0034 70B9;;;;N;;;;;
+3367;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR FIFTEEN;So;0;L;<compat> 0031 0035 70B9;;;;N;;;;;
+3368;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SIXTEEN;So;0;L;<compat> 0031 0036 70B9;;;;N;;;;;
+3369;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR SEVENTEEN;So;0;L;<compat> 0031 0037 70B9;;;;N;;;;;
+336A;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR EIGHTEEN;So;0;L;<compat> 0031 0038 70B9;;;;N;;;;;
+336B;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR NINETEEN;So;0;L;<compat> 0031 0039 70B9;;;;N;;;;;
+336C;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY;So;0;L;<compat> 0032 0030 70B9;;;;N;;;;;
+336D;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-ONE;So;0;L;<compat> 0032 0031 70B9;;;;N;;;;;
+336E;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-TWO;So;0;L;<compat> 0032 0032 70B9;;;;N;;;;;
+336F;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-THREE;So;0;L;<compat> 0032 0033 70B9;;;;N;;;;;
+3370;IDEOGRAPHIC TELEGRAPH SYMBOL FOR HOUR TWENTY-FOUR;So;0;L;<compat> 0032 0034 70B9;;;;N;;;;;
+3371;SQUARE HPA;So;0;L;<square> 0068 0050 0061;;;;N;;;;;
+3372;SQUARE DA;So;0;L;<square> 0064 0061;;;;N;;;;;
+3373;SQUARE AU;So;0;L;<square> 0041 0055;;;;N;;;;;
+3374;SQUARE BAR;So;0;L;<square> 0062 0061 0072;;;;N;;;;;
+3375;SQUARE OV;So;0;L;<square> 006F 0056;;;;N;;;;;
+3376;SQUARE PC;So;0;L;<square> 0070 0063;;;;N;;;;;
+337B;SQUARE ERA NAME HEISEI;So;0;L;<square> 5E73 6210;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME HEISEI;;;;
+337C;SQUARE ERA NAME SYOUWA;So;0;L;<square> 662D 548C;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME SYOUWA;;;;
+337D;SQUARE ERA NAME TAISYOU;So;0;L;<square> 5927 6B63;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME TAISYOU;;;;
+337E;SQUARE ERA NAME MEIZI;So;0;L;<square> 660E 6CBB;;;;N;SQUARED TWO IDEOGRAPHS ERA NAME MEIZI;;;;
+337F;SQUARE CORPORATION;So;0;L;<square> 682A 5F0F 4F1A 793E;;;;N;SQUARED FOUR IDEOGRAPHS CORPORATION;;;;
+3380;SQUARE PA AMPS;So;0;L;<square> 0070 0041;;;;N;SQUARED PA AMPS;;;;
+3381;SQUARE NA;So;0;L;<square> 006E 0041;;;;N;SQUARED NA;;;;
+3382;SQUARE MU A;So;0;L;<square> 03BC 0041;;;;N;SQUARED MU A;;;;
+3383;SQUARE MA;So;0;L;<square> 006D 0041;;;;N;SQUARED MA;;;;
+3384;SQUARE KA;So;0;L;<square> 006B 0041;;;;N;SQUARED KA;;;;
+3385;SQUARE KB;So;0;L;<square> 004B 0042;;;;N;SQUARED KB;;;;
+3386;SQUARE MB;So;0;L;<square> 004D 0042;;;;N;SQUARED MB;;;;
+3387;SQUARE GB;So;0;L;<square> 0047 0042;;;;N;SQUARED GB;;;;
+3388;SQUARE CAL;So;0;L;<square> 0063 0061 006C;;;;N;SQUARED CAL;;;;
+3389;SQUARE KCAL;So;0;L;<square> 006B 0063 0061 006C;;;;N;SQUARED KCAL;;;;
+338A;SQUARE PF;So;0;L;<square> 0070 0046;;;;N;SQUARED PF;;;;
+338B;SQUARE NF;So;0;L;<square> 006E 0046;;;;N;SQUARED NF;;;;
+338C;SQUARE MU F;So;0;L;<square> 03BC 0046;;;;N;SQUARED MU F;;;;
+338D;SQUARE MU G;So;0;L;<square> 03BC 0067;;;;N;SQUARED MU G;;;;
+338E;SQUARE MG;So;0;L;<square> 006D 0067;;;;N;SQUARED MG;;;;
+338F;SQUARE KG;So;0;L;<square> 006B 0067;;;;N;SQUARED KG;;;;
+3390;SQUARE HZ;So;0;L;<square> 0048 007A;;;;N;SQUARED HZ;;;;
+3391;SQUARE KHZ;So;0;L;<square> 006B 0048 007A;;;;N;SQUARED KHZ;;;;
+3392;SQUARE MHZ;So;0;L;<square> 004D 0048 007A;;;;N;SQUARED MHZ;;;;
+3393;SQUARE GHZ;So;0;L;<square> 0047 0048 007A;;;;N;SQUARED GHZ;;;;
+3394;SQUARE THZ;So;0;L;<square> 0054 0048 007A;;;;N;SQUARED THZ;;;;
+3395;SQUARE MU L;So;0;L;<square> 03BC 2113;;;;N;SQUARED MU L;;;;
+3396;SQUARE ML;So;0;L;<square> 006D 2113;;;;N;SQUARED ML;;;;
+3397;SQUARE DL;So;0;L;<square> 0064 2113;;;;N;SQUARED DL;;;;
+3398;SQUARE KL;So;0;L;<square> 006B 2113;;;;N;SQUARED KL;;;;
+3399;SQUARE FM;So;0;L;<square> 0066 006D;;;;N;SQUARED FM;;;;
+339A;SQUARE NM;So;0;L;<square> 006E 006D;;;;N;SQUARED NM;;;;
+339B;SQUARE MU M;So;0;L;<square> 03BC 006D;;;;N;SQUARED MU M;;;;
+339C;SQUARE MM;So;0;L;<square> 006D 006D;;;;N;SQUARED MM;;;;
+339D;SQUARE CM;So;0;L;<square> 0063 006D;;;;N;SQUARED CM;;;;
+339E;SQUARE KM;So;0;L;<square> 006B 006D;;;;N;SQUARED KM;;;;
+339F;SQUARE MM SQUARED;So;0;L;<square> 006D 006D 00B2;;;;N;SQUARED MM SQUARED;;;;
+33A0;SQUARE CM SQUARED;So;0;L;<square> 0063 006D 00B2;;;;N;SQUARED CM SQUARED;;;;
+33A1;SQUARE M SQUARED;So;0;L;<square> 006D 00B2;;;;N;SQUARED M SQUARED;;;;
+33A2;SQUARE KM SQUARED;So;0;L;<square> 006B 006D 00B2;;;;N;SQUARED KM SQUARED;;;;
+33A3;SQUARE MM CUBED;So;0;L;<square> 006D 006D 00B3;;;;N;SQUARED MM CUBED;;;;
+33A4;SQUARE CM CUBED;So;0;L;<square> 0063 006D 00B3;;;;N;SQUARED CM CUBED;;;;
+33A5;SQUARE M CUBED;So;0;L;<square> 006D 00B3;;;;N;SQUARED M CUBED;;;;
+33A6;SQUARE KM CUBED;So;0;L;<square> 006B 006D 00B3;;;;N;SQUARED KM CUBED;;;;
+33A7;SQUARE M OVER S;So;0;L;<square> 006D 2215 0073;;;;N;SQUARED M OVER S;;;;
+33A8;SQUARE M OVER S SQUARED;So;0;L;<square> 006D 2215 0073 00B2;;;;N;SQUARED M OVER S SQUARED;;;;
+33A9;SQUARE PA;So;0;L;<square> 0050 0061;;;;N;SQUARED PA;;;;
+33AA;SQUARE KPA;So;0;L;<square> 006B 0050 0061;;;;N;SQUARED KPA;;;;
+33AB;SQUARE MPA;So;0;L;<square> 004D 0050 0061;;;;N;SQUARED MPA;;;;
+33AC;SQUARE GPA;So;0;L;<square> 0047 0050 0061;;;;N;SQUARED GPA;;;;
+33AD;SQUARE RAD;So;0;L;<square> 0072 0061 0064;;;;N;SQUARED RAD;;;;
+33AE;SQUARE RAD OVER S;So;0;L;<square> 0072 0061 0064 2215 0073;;;;N;SQUARED RAD OVER S;;;;
+33AF;SQUARE RAD OVER S SQUARED;So;0;L;<square> 0072 0061 0064 2215 0073 00B2;;;;N;SQUARED RAD OVER S SQUARED;;;;
+33B0;SQUARE PS;So;0;L;<square> 0070 0073;;;;N;SQUARED PS;;;;
+33B1;SQUARE NS;So;0;L;<square> 006E 0073;;;;N;SQUARED NS;;;;
+33B2;SQUARE MU S;So;0;L;<square> 03BC 0073;;;;N;SQUARED MU S;;;;
+33B3;SQUARE MS;So;0;L;<square> 006D 0073;;;;N;SQUARED MS;;;;
+33B4;SQUARE PV;So;0;L;<square> 0070 0056;;;;N;SQUARED PV;;;;
+33B5;SQUARE NV;So;0;L;<square> 006E 0056;;;;N;SQUARED NV;;;;
+33B6;SQUARE MU V;So;0;L;<square> 03BC 0056;;;;N;SQUARED MU V;;;;
+33B7;SQUARE MV;So;0;L;<square> 006D 0056;;;;N;SQUARED MV;;;;
+33B8;SQUARE KV;So;0;L;<square> 006B 0056;;;;N;SQUARED KV;;;;
+33B9;SQUARE MV MEGA;So;0;L;<square> 004D 0056;;;;N;SQUARED MV MEGA;;;;
+33BA;SQUARE PW;So;0;L;<square> 0070 0057;;;;N;SQUARED PW;;;;
+33BB;SQUARE NW;So;0;L;<square> 006E 0057;;;;N;SQUARED NW;;;;
+33BC;SQUARE MU W;So;0;L;<square> 03BC 0057;;;;N;SQUARED MU W;;;;
+33BD;SQUARE MW;So;0;L;<square> 006D 0057;;;;N;SQUARED MW;;;;
+33BE;SQUARE KW;So;0;L;<square> 006B 0057;;;;N;SQUARED KW;;;;
+33BF;SQUARE MW MEGA;So;0;L;<square> 004D 0057;;;;N;SQUARED MW MEGA;;;;
+33C0;SQUARE K OHM;So;0;L;<square> 006B 03A9;;;;N;SQUARED K OHM;;;;
+33C1;SQUARE M OHM;So;0;L;<square> 004D 03A9;;;;N;SQUARED M OHM;;;;
+33C2;SQUARE AM;So;0;L;<square> 0061 002E 006D 002E;;;;N;SQUARED AM;;;;
+33C3;SQUARE BQ;So;0;L;<square> 0042 0071;;;;N;SQUARED BQ;;;;
+33C4;SQUARE CC;So;0;L;<square> 0063 0063;;;;N;SQUARED CC;;;;
+33C5;SQUARE CD;So;0;L;<square> 0063 0064;;;;N;SQUARED CD;;;;
+33C6;SQUARE C OVER KG;So;0;L;<square> 0043 2215 006B 0067;;;;N;SQUARED C OVER KG;;;;
+33C7;SQUARE CO;So;0;L;<square> 0043 006F 002E;;;;N;SQUARED CO;;;;
+33C8;SQUARE DB;So;0;L;<square> 0064 0042;;;;N;SQUARED DB;;;;
+33C9;SQUARE GY;So;0;L;<square> 0047 0079;;;;N;SQUARED GY;;;;
+33CA;SQUARE HA;So;0;L;<square> 0068 0061;;;;N;SQUARED HA;;;;
+33CB;SQUARE HP;So;0;L;<square> 0048 0050;;;;N;SQUARED HP;;;;
+33CC;SQUARE IN;So;0;L;<square> 0069 006E;;;;N;SQUARED IN;;;;
+33CD;SQUARE KK;So;0;L;<square> 004B 004B;;;;N;SQUARED KK;;;;
+33CE;SQUARE KM CAPITAL;So;0;L;<square> 004B 004D;;;;N;SQUARED KM CAPITAL;;;;
+33CF;SQUARE KT;So;0;L;<square> 006B 0074;;;;N;SQUARED KT;;;;
+33D0;SQUARE LM;So;0;L;<square> 006C 006D;;;;N;SQUARED LM;;;;
+33D1;SQUARE LN;So;0;L;<square> 006C 006E;;;;N;SQUARED LN;;;;
+33D2;SQUARE LOG;So;0;L;<square> 006C 006F 0067;;;;N;SQUARED LOG;;;;
+33D3;SQUARE LX;So;0;L;<square> 006C 0078;;;;N;SQUARED LX;;;;
+33D4;SQUARE MB SMALL;So;0;L;<square> 006D 0062;;;;N;SQUARED MB SMALL;;;;
+33D5;SQUARE MIL;So;0;L;<square> 006D 0069 006C;;;;N;SQUARED MIL;;;;
+33D6;SQUARE MOL;So;0;L;<square> 006D 006F 006C;;;;N;SQUARED MOL;;;;
+33D7;SQUARE PH;So;0;L;<square> 0050 0048;;;;N;SQUARED PH;;;;
+33D8;SQUARE PM;So;0;L;<square> 0070 002E 006D 002E;;;;N;SQUARED PM;;;;
+33D9;SQUARE PPM;So;0;L;<square> 0050 0050 004D;;;;N;SQUARED PPM;;;;
+33DA;SQUARE PR;So;0;L;<square> 0050 0052;;;;N;SQUARED PR;;;;
+33DB;SQUARE SR;So;0;L;<square> 0073 0072;;;;N;SQUARED SR;;;;
+33DC;SQUARE SV;So;0;L;<square> 0053 0076;;;;N;SQUARED SV;;;;
+33DD;SQUARE WB;So;0;L;<square> 0057 0062;;;;N;SQUARED WB;;;;
+33E0;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ONE;So;0;L;<compat> 0031 65E5;;;;N;;;;;
+33E1;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWO;So;0;L;<compat> 0032 65E5;;;;N;;;;;
+33E2;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THREE;So;0;L;<compat> 0033 65E5;;;;N;;;;;
+33E3;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOUR;So;0;L;<compat> 0034 65E5;;;;N;;;;;
+33E4;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIVE;So;0;L;<compat> 0035 65E5;;;;N;;;;;
+33E5;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIX;So;0;L;<compat> 0036 65E5;;;;N;;;;;
+33E6;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVEN;So;0;L;<compat> 0037 65E5;;;;N;;;;;
+33E7;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHT;So;0;L;<compat> 0038 65E5;;;;N;;;;;
+33E8;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINE;So;0;L;<compat> 0039 65E5;;;;N;;;;;
+33E9;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TEN;So;0;L;<compat> 0031 0030 65E5;;;;N;;;;;
+33EA;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY ELEVEN;So;0;L;<compat> 0031 0031 65E5;;;;N;;;;;
+33EB;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWELVE;So;0;L;<compat> 0031 0032 65E5;;;;N;;;;;
+33EC;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTEEN;So;0;L;<compat> 0031 0033 65E5;;;;N;;;;;
+33ED;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FOURTEEN;So;0;L;<compat> 0031 0034 65E5;;;;N;;;;;
+33EE;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY FIFTEEN;So;0;L;<compat> 0031 0035 65E5;;;;N;;;;;
+33EF;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SIXTEEN;So;0;L;<compat> 0031 0036 65E5;;;;N;;;;;
+33F0;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY SEVENTEEN;So;0;L;<compat> 0031 0037 65E5;;;;N;;;;;
+33F1;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY EIGHTEEN;So;0;L;<compat> 0031 0038 65E5;;;;N;;;;;
+33F2;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY NINETEEN;So;0;L;<compat> 0031 0039 65E5;;;;N;;;;;
+33F3;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY;So;0;L;<compat> 0032 0030 65E5;;;;N;;;;;
+33F4;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-ONE;So;0;L;<compat> 0032 0031 65E5;;;;N;;;;;
+33F5;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-TWO;So;0;L;<compat> 0032 0032 65E5;;;;N;;;;;
+33F6;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-THREE;So;0;L;<compat> 0032 0033 65E5;;;;N;;;;;
+33F7;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FOUR;So;0;L;<compat> 0032 0034 65E5;;;;N;;;;;
+33F8;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-FIVE;So;0;L;<compat> 0032 0035 65E5;;;;N;;;;;
+33F9;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SIX;So;0;L;<compat> 0032 0036 65E5;;;;N;;;;;
+33FA;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-SEVEN;So;0;L;<compat> 0032 0037 65E5;;;;N;;;;;
+33FB;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-EIGHT;So;0;L;<compat> 0032 0038 65E5;;;;N;;;;;
+33FC;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY TWENTY-NINE;So;0;L;<compat> 0032 0039 65E5;;;;N;;;;;
+33FD;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY;So;0;L;<compat> 0033 0030 65E5;;;;N;;;;;
+33FE;IDEOGRAPHIC TELEGRAPH SYMBOL FOR DAY THIRTY-ONE;So;0;L;<compat> 0033 0031 65E5;;;;N;;;;;
+3400;<CJK Ideograph Extension A, First>;Lo;0;L;;;;;N;;;;;
+4DB5;<CJK Ideograph Extension A, Last>;Lo;0;L;;;;;N;;;;;
+4E00;<CJK Ideograph, First>;Lo;0;L;;;;;N;;;;;
+9FA5;<CJK Ideograph, Last>;Lo;0;L;;;;;N;;;;;
+A000;YI SYLLABLE IT;Lo;0;L;;;;;N;;;;;
+A001;YI SYLLABLE IX;Lo;0;L;;;;;N;;;;;
+A002;YI SYLLABLE I;Lo;0;L;;;;;N;;;;;
+A003;YI SYLLABLE IP;Lo;0;L;;;;;N;;;;;
+A004;YI SYLLABLE IET;Lo;0;L;;;;;N;;;;;
+A005;YI SYLLABLE IEX;Lo;0;L;;;;;N;;;;;
+A006;YI SYLLABLE IE;Lo;0;L;;;;;N;;;;;
+A007;YI SYLLABLE IEP;Lo;0;L;;;;;N;;;;;
+A008;YI SYLLABLE AT;Lo;0;L;;;;;N;;;;;
+A009;YI SYLLABLE AX;Lo;0;L;;;;;N;;;;;
+A00A;YI SYLLABLE A;Lo;0;L;;;;;N;;;;;
+A00B;YI SYLLABLE AP;Lo;0;L;;;;;N;;;;;
+A00C;YI SYLLABLE UOX;Lo;0;L;;;;;N;;;;;
+A00D;YI SYLLABLE UO;Lo;0;L;;;;;N;;;;;
+A00E;YI SYLLABLE UOP;Lo;0;L;;;;;N;;;;;
+A00F;YI SYLLABLE OT;Lo;0;L;;;;;N;;;;;
+A010;YI SYLLABLE OX;Lo;0;L;;;;;N;;;;;
+A011;YI SYLLABLE O;Lo;0;L;;;;;N;;;;;
+A012;YI SYLLABLE OP;Lo;0;L;;;;;N;;;;;
+A013;YI SYLLABLE EX;Lo;0;L;;;;;N;;;;;
+A014;YI SYLLABLE E;Lo;0;L;;;;;N;;;;;
+A015;YI SYLLABLE WU;Lo;0;L;;;;;N;;;;;
+A016;YI SYLLABLE BIT;Lo;0;L;;;;;N;;;;;
+A017;YI SYLLABLE BIX;Lo;0;L;;;;;N;;;;;
+A018;YI SYLLABLE BI;Lo;0;L;;;;;N;;;;;
+A019;YI SYLLABLE BIP;Lo;0;L;;;;;N;;;;;
+A01A;YI SYLLABLE BIET;Lo;0;L;;;;;N;;;;;
+A01B;YI SYLLABLE BIEX;Lo;0;L;;;;;N;;;;;
+A01C;YI SYLLABLE BIE;Lo;0;L;;;;;N;;;;;
+A01D;YI SYLLABLE BIEP;Lo;0;L;;;;;N;;;;;
+A01E;YI SYLLABLE BAT;Lo;0;L;;;;;N;;;;;
+A01F;YI SYLLABLE BAX;Lo;0;L;;;;;N;;;;;
+A020;YI SYLLABLE BA;Lo;0;L;;;;;N;;;;;
+A021;YI SYLLABLE BAP;Lo;0;L;;;;;N;;;;;
+A022;YI SYLLABLE BUOX;Lo;0;L;;;;;N;;;;;
+A023;YI SYLLABLE BUO;Lo;0;L;;;;;N;;;;;
+A024;YI SYLLABLE BUOP;Lo;0;L;;;;;N;;;;;
+A025;YI SYLLABLE BOT;Lo;0;L;;;;;N;;;;;
+A026;YI SYLLABLE BOX;Lo;0;L;;;;;N;;;;;
+A027;YI SYLLABLE BO;Lo;0;L;;;;;N;;;;;
+A028;YI SYLLABLE BOP;Lo;0;L;;;;;N;;;;;
+A029;YI SYLLABLE BEX;Lo;0;L;;;;;N;;;;;
+A02A;YI SYLLABLE BE;Lo;0;L;;;;;N;;;;;
+A02B;YI SYLLABLE BEP;Lo;0;L;;;;;N;;;;;
+A02C;YI SYLLABLE BUT;Lo;0;L;;;;;N;;;;;
+A02D;YI SYLLABLE BUX;Lo;0;L;;;;;N;;;;;
+A02E;YI SYLLABLE BU;Lo;0;L;;;;;N;;;;;
+A02F;YI SYLLABLE BUP;Lo;0;L;;;;;N;;;;;
+A030;YI SYLLABLE BURX;Lo;0;L;;;;;N;;;;;
+A031;YI SYLLABLE BUR;Lo;0;L;;;;;N;;;;;
+A032;YI SYLLABLE BYT;Lo;0;L;;;;;N;;;;;
+A033;YI SYLLABLE BYX;Lo;0;L;;;;;N;;;;;
+A034;YI SYLLABLE BY;Lo;0;L;;;;;N;;;;;
+A035;YI SYLLABLE BYP;Lo;0;L;;;;;N;;;;;
+A036;YI SYLLABLE BYRX;Lo;0;L;;;;;N;;;;;
+A037;YI SYLLABLE BYR;Lo;0;L;;;;;N;;;;;
+A038;YI SYLLABLE PIT;Lo;0;L;;;;;N;;;;;
+A039;YI SYLLABLE PIX;Lo;0;L;;;;;N;;;;;
+A03A;YI SYLLABLE PI;Lo;0;L;;;;;N;;;;;
+A03B;YI SYLLABLE PIP;Lo;0;L;;;;;N;;;;;
+A03C;YI SYLLABLE PIEX;Lo;0;L;;;;;N;;;;;
+A03D;YI SYLLABLE PIE;Lo;0;L;;;;;N;;;;;
+A03E;YI SYLLABLE PIEP;Lo;0;L;;;;;N;;;;;
+A03F;YI SYLLABLE PAT;Lo;0;L;;;;;N;;;;;
+A040;YI SYLLABLE PAX;Lo;0;L;;;;;N;;;;;
+A041;YI SYLLABLE PA;Lo;0;L;;;;;N;;;;;
+A042;YI SYLLABLE PAP;Lo;0;L;;;;;N;;;;;
+A043;YI SYLLABLE PUOX;Lo;0;L;;;;;N;;;;;
+A044;YI SYLLABLE PUO;Lo;0;L;;;;;N;;;;;
+A045;YI SYLLABLE PUOP;Lo;0;L;;;;;N;;;;;
+A046;YI SYLLABLE POT;Lo;0;L;;;;;N;;;;;
+A047;YI SYLLABLE POX;Lo;0;L;;;;;N;;;;;
+A048;YI SYLLABLE PO;Lo;0;L;;;;;N;;;;;
+A049;YI SYLLABLE POP;Lo;0;L;;;;;N;;;;;
+A04A;YI SYLLABLE PUT;Lo;0;L;;;;;N;;;;;
+A04B;YI SYLLABLE PUX;Lo;0;L;;;;;N;;;;;
+A04C;YI SYLLABLE PU;Lo;0;L;;;;;N;;;;;
+A04D;YI SYLLABLE PUP;Lo;0;L;;;;;N;;;;;
+A04E;YI SYLLABLE PURX;Lo;0;L;;;;;N;;;;;
+A04F;YI SYLLABLE PUR;Lo;0;L;;;;;N;;;;;
+A050;YI SYLLABLE PYT;Lo;0;L;;;;;N;;;;;
+A051;YI SYLLABLE PYX;Lo;0;L;;;;;N;;;;;
+A052;YI SYLLABLE PY;Lo;0;L;;;;;N;;;;;
+A053;YI SYLLABLE PYP;Lo;0;L;;;;;N;;;;;
+A054;YI SYLLABLE PYRX;Lo;0;L;;;;;N;;;;;
+A055;YI SYLLABLE PYR;Lo;0;L;;;;;N;;;;;
+A056;YI SYLLABLE BBIT;Lo;0;L;;;;;N;;;;;
+A057;YI SYLLABLE BBIX;Lo;0;L;;;;;N;;;;;
+A058;YI SYLLABLE BBI;Lo;0;L;;;;;N;;;;;
+A059;YI SYLLABLE BBIP;Lo;0;L;;;;;N;;;;;
+A05A;YI SYLLABLE BBIET;Lo;0;L;;;;;N;;;;;
+A05B;YI SYLLABLE BBIEX;Lo;0;L;;;;;N;;;;;
+A05C;YI SYLLABLE BBIE;Lo;0;L;;;;;N;;;;;
+A05D;YI SYLLABLE BBIEP;Lo;0;L;;;;;N;;;;;
+A05E;YI SYLLABLE BBAT;Lo;0;L;;;;;N;;;;;
+A05F;YI SYLLABLE BBAX;Lo;0;L;;;;;N;;;;;
+A060;YI SYLLABLE BBA;Lo;0;L;;;;;N;;;;;
+A061;YI SYLLABLE BBAP;Lo;0;L;;;;;N;;;;;
+A062;YI SYLLABLE BBUOX;Lo;0;L;;;;;N;;;;;
+A063;YI SYLLABLE BBUO;Lo;0;L;;;;;N;;;;;
+A064;YI SYLLABLE BBUOP;Lo;0;L;;;;;N;;;;;
+A065;YI SYLLABLE BBOT;Lo;0;L;;;;;N;;;;;
+A066;YI SYLLABLE BBOX;Lo;0;L;;;;;N;;;;;
+A067;YI SYLLABLE BBO;Lo;0;L;;;;;N;;;;;
+A068;YI SYLLABLE BBOP;Lo;0;L;;;;;N;;;;;
+A069;YI SYLLABLE BBEX;Lo;0;L;;;;;N;;;;;
+A06A;YI SYLLABLE BBE;Lo;0;L;;;;;N;;;;;
+A06B;YI SYLLABLE BBEP;Lo;0;L;;;;;N;;;;;
+A06C;YI SYLLABLE BBUT;Lo;0;L;;;;;N;;;;;
+A06D;YI SYLLABLE BBUX;Lo;0;L;;;;;N;;;;;
+A06E;YI SYLLABLE BBU;Lo;0;L;;;;;N;;;;;
+A06F;YI SYLLABLE BBUP;Lo;0;L;;;;;N;;;;;
+A070;YI SYLLABLE BBURX;Lo;0;L;;;;;N;;;;;
+A071;YI SYLLABLE BBUR;Lo;0;L;;;;;N;;;;;
+A072;YI SYLLABLE BBYT;Lo;0;L;;;;;N;;;;;
+A073;YI SYLLABLE BBYX;Lo;0;L;;;;;N;;;;;
+A074;YI SYLLABLE BBY;Lo;0;L;;;;;N;;;;;
+A075;YI SYLLABLE BBYP;Lo;0;L;;;;;N;;;;;
+A076;YI SYLLABLE NBIT;Lo;0;L;;;;;N;;;;;
+A077;YI SYLLABLE NBIX;Lo;0;L;;;;;N;;;;;
+A078;YI SYLLABLE NBI;Lo;0;L;;;;;N;;;;;
+A079;YI SYLLABLE NBIP;Lo;0;L;;;;;N;;;;;
+A07A;YI SYLLABLE NBIEX;Lo;0;L;;;;;N;;;;;
+A07B;YI SYLLABLE NBIE;Lo;0;L;;;;;N;;;;;
+A07C;YI SYLLABLE NBIEP;Lo;0;L;;;;;N;;;;;
+A07D;YI SYLLABLE NBAT;Lo;0;L;;;;;N;;;;;
+A07E;YI SYLLABLE NBAX;Lo;0;L;;;;;N;;;;;
+A07F;YI SYLLABLE NBA;Lo;0;L;;;;;N;;;;;
+A080;YI SYLLABLE NBAP;Lo;0;L;;;;;N;;;;;
+A081;YI SYLLABLE NBOT;Lo;0;L;;;;;N;;;;;
+A082;YI SYLLABLE NBOX;Lo;0;L;;;;;N;;;;;
+A083;YI SYLLABLE NBO;Lo;0;L;;;;;N;;;;;
+A084;YI SYLLABLE NBOP;Lo;0;L;;;;;N;;;;;
+A085;YI SYLLABLE NBUT;Lo;0;L;;;;;N;;;;;
+A086;YI SYLLABLE NBUX;Lo;0;L;;;;;N;;;;;
+A087;YI SYLLABLE NBU;Lo;0;L;;;;;N;;;;;
+A088;YI SYLLABLE NBUP;Lo;0;L;;;;;N;;;;;
+A089;YI SYLLABLE NBURX;Lo;0;L;;;;;N;;;;;
+A08A;YI SYLLABLE NBUR;Lo;0;L;;;;;N;;;;;
+A08B;YI SYLLABLE NBYT;Lo;0;L;;;;;N;;;;;
+A08C;YI SYLLABLE NBYX;Lo;0;L;;;;;N;;;;;
+A08D;YI SYLLABLE NBY;Lo;0;L;;;;;N;;;;;
+A08E;YI SYLLABLE NBYP;Lo;0;L;;;;;N;;;;;
+A08F;YI SYLLABLE NBYRX;Lo;0;L;;;;;N;;;;;
+A090;YI SYLLABLE NBYR;Lo;0;L;;;;;N;;;;;
+A091;YI SYLLABLE HMIT;Lo;0;L;;;;;N;;;;;
+A092;YI SYLLABLE HMIX;Lo;0;L;;;;;N;;;;;
+A093;YI SYLLABLE HMI;Lo;0;L;;;;;N;;;;;
+A094;YI SYLLABLE HMIP;Lo;0;L;;;;;N;;;;;
+A095;YI SYLLABLE HMIEX;Lo;0;L;;;;;N;;;;;
+A096;YI SYLLABLE HMIE;Lo;0;L;;;;;N;;;;;
+A097;YI SYLLABLE HMIEP;Lo;0;L;;;;;N;;;;;
+A098;YI SYLLABLE HMAT;Lo;0;L;;;;;N;;;;;
+A099;YI SYLLABLE HMAX;Lo;0;L;;;;;N;;;;;
+A09A;YI SYLLABLE HMA;Lo;0;L;;;;;N;;;;;
+A09B;YI SYLLABLE HMAP;Lo;0;L;;;;;N;;;;;
+A09C;YI SYLLABLE HMUOX;Lo;0;L;;;;;N;;;;;
+A09D;YI SYLLABLE HMUO;Lo;0;L;;;;;N;;;;;
+A09E;YI SYLLABLE HMUOP;Lo;0;L;;;;;N;;;;;
+A09F;YI SYLLABLE HMOT;Lo;0;L;;;;;N;;;;;
+A0A0;YI SYLLABLE HMOX;Lo;0;L;;;;;N;;;;;
+A0A1;YI SYLLABLE HMO;Lo;0;L;;;;;N;;;;;
+A0A2;YI SYLLABLE HMOP;Lo;0;L;;;;;N;;;;;
+A0A3;YI SYLLABLE HMUT;Lo;0;L;;;;;N;;;;;
+A0A4;YI SYLLABLE HMUX;Lo;0;L;;;;;N;;;;;
+A0A5;YI SYLLABLE HMU;Lo;0;L;;;;;N;;;;;
+A0A6;YI SYLLABLE HMUP;Lo;0;L;;;;;N;;;;;
+A0A7;YI SYLLABLE HMURX;Lo;0;L;;;;;N;;;;;
+A0A8;YI SYLLABLE HMUR;Lo;0;L;;;;;N;;;;;
+A0A9;YI SYLLABLE HMYX;Lo;0;L;;;;;N;;;;;
+A0AA;YI SYLLABLE HMY;Lo;0;L;;;;;N;;;;;
+A0AB;YI SYLLABLE HMYP;Lo;0;L;;;;;N;;;;;
+A0AC;YI SYLLABLE HMYRX;Lo;0;L;;;;;N;;;;;
+A0AD;YI SYLLABLE HMYR;Lo;0;L;;;;;N;;;;;
+A0AE;YI SYLLABLE MIT;Lo;0;L;;;;;N;;;;;
+A0AF;YI SYLLABLE MIX;Lo;0;L;;;;;N;;;;;
+A0B0;YI SYLLABLE MI;Lo;0;L;;;;;N;;;;;
+A0B1;YI SYLLABLE MIP;Lo;0;L;;;;;N;;;;;
+A0B2;YI SYLLABLE MIEX;Lo;0;L;;;;;N;;;;;
+A0B3;YI SYLLABLE MIE;Lo;0;L;;;;;N;;;;;
+A0B4;YI SYLLABLE MIEP;Lo;0;L;;;;;N;;;;;
+A0B5;YI SYLLABLE MAT;Lo;0;L;;;;;N;;;;;
+A0B6;YI SYLLABLE MAX;Lo;0;L;;;;;N;;;;;
+A0B7;YI SYLLABLE MA;Lo;0;L;;;;;N;;;;;
+A0B8;YI SYLLABLE MAP;Lo;0;L;;;;;N;;;;;
+A0B9;YI SYLLABLE MUOT;Lo;0;L;;;;;N;;;;;
+A0BA;YI SYLLABLE MUOX;Lo;0;L;;;;;N;;;;;
+A0BB;YI SYLLABLE MUO;Lo;0;L;;;;;N;;;;;
+A0BC;YI SYLLABLE MUOP;Lo;0;L;;;;;N;;;;;
+A0BD;YI SYLLABLE MOT;Lo;0;L;;;;;N;;;;;
+A0BE;YI SYLLABLE MOX;Lo;0;L;;;;;N;;;;;
+A0BF;YI SYLLABLE MO;Lo;0;L;;;;;N;;;;;
+A0C0;YI SYLLABLE MOP;Lo;0;L;;;;;N;;;;;
+A0C1;YI SYLLABLE MEX;Lo;0;L;;;;;N;;;;;
+A0C2;YI SYLLABLE ME;Lo;0;L;;;;;N;;;;;
+A0C3;YI SYLLABLE MUT;Lo;0;L;;;;;N;;;;;
+A0C4;YI SYLLABLE MUX;Lo;0;L;;;;;N;;;;;
+A0C5;YI SYLLABLE MU;Lo;0;L;;;;;N;;;;;
+A0C6;YI SYLLABLE MUP;Lo;0;L;;;;;N;;;;;
+A0C7;YI SYLLABLE MURX;Lo;0;L;;;;;N;;;;;
+A0C8;YI SYLLABLE MUR;Lo;0;L;;;;;N;;;;;
+A0C9;YI SYLLABLE MYT;Lo;0;L;;;;;N;;;;;
+A0CA;YI SYLLABLE MYX;Lo;0;L;;;;;N;;;;;
+A0CB;YI SYLLABLE MY;Lo;0;L;;;;;N;;;;;
+A0CC;YI SYLLABLE MYP;Lo;0;L;;;;;N;;;;;
+A0CD;YI SYLLABLE FIT;Lo;0;L;;;;;N;;;;;
+A0CE;YI SYLLABLE FIX;Lo;0;L;;;;;N;;;;;
+A0CF;YI SYLLABLE FI;Lo;0;L;;;;;N;;;;;
+A0D0;YI SYLLABLE FIP;Lo;0;L;;;;;N;;;;;
+A0D1;YI SYLLABLE FAT;Lo;0;L;;;;;N;;;;;
+A0D2;YI SYLLABLE FAX;Lo;0;L;;;;;N;;;;;
+A0D3;YI SYLLABLE FA;Lo;0;L;;;;;N;;;;;
+A0D4;YI SYLLABLE FAP;Lo;0;L;;;;;N;;;;;
+A0D5;YI SYLLABLE FOX;Lo;0;L;;;;;N;;;;;
+A0D6;YI SYLLABLE FO;Lo;0;L;;;;;N;;;;;
+A0D7;YI SYLLABLE FOP;Lo;0;L;;;;;N;;;;;
+A0D8;YI SYLLABLE FUT;Lo;0;L;;;;;N;;;;;
+A0D9;YI SYLLABLE FUX;Lo;0;L;;;;;N;;;;;
+A0DA;YI SYLLABLE FU;Lo;0;L;;;;;N;;;;;
+A0DB;YI SYLLABLE FUP;Lo;0;L;;;;;N;;;;;
+A0DC;YI SYLLABLE FURX;Lo;0;L;;;;;N;;;;;
+A0DD;YI SYLLABLE FUR;Lo;0;L;;;;;N;;;;;
+A0DE;YI SYLLABLE FYT;Lo;0;L;;;;;N;;;;;
+A0DF;YI SYLLABLE FYX;Lo;0;L;;;;;N;;;;;
+A0E0;YI SYLLABLE FY;Lo;0;L;;;;;N;;;;;
+A0E1;YI SYLLABLE FYP;Lo;0;L;;;;;N;;;;;
+A0E2;YI SYLLABLE VIT;Lo;0;L;;;;;N;;;;;
+A0E3;YI SYLLABLE VIX;Lo;0;L;;;;;N;;;;;
+A0E4;YI SYLLABLE VI;Lo;0;L;;;;;N;;;;;
+A0E5;YI SYLLABLE VIP;Lo;0;L;;;;;N;;;;;
+A0E6;YI SYLLABLE VIET;Lo;0;L;;;;;N;;;;;
+A0E7;YI SYLLABLE VIEX;Lo;0;L;;;;;N;;;;;
+A0E8;YI SYLLABLE VIE;Lo;0;L;;;;;N;;;;;
+A0E9;YI SYLLABLE VIEP;Lo;0;L;;;;;N;;;;;
+A0EA;YI SYLLABLE VAT;Lo;0;L;;;;;N;;;;;
+A0EB;YI SYLLABLE VAX;Lo;0;L;;;;;N;;;;;
+A0EC;YI SYLLABLE VA;Lo;0;L;;;;;N;;;;;
+A0ED;YI SYLLABLE VAP;Lo;0;L;;;;;N;;;;;
+A0EE;YI SYLLABLE VOT;Lo;0;L;;;;;N;;;;;
+A0EF;YI SYLLABLE VOX;Lo;0;L;;;;;N;;;;;
+A0F0;YI SYLLABLE VO;Lo;0;L;;;;;N;;;;;
+A0F1;YI SYLLABLE VOP;Lo;0;L;;;;;N;;;;;
+A0F2;YI SYLLABLE VEX;Lo;0;L;;;;;N;;;;;
+A0F3;YI SYLLABLE VEP;Lo;0;L;;;;;N;;;;;
+A0F4;YI SYLLABLE VUT;Lo;0;L;;;;;N;;;;;
+A0F5;YI SYLLABLE VUX;Lo;0;L;;;;;N;;;;;
+A0F6;YI SYLLABLE VU;Lo;0;L;;;;;N;;;;;
+A0F7;YI SYLLABLE VUP;Lo;0;L;;;;;N;;;;;
+A0F8;YI SYLLABLE VURX;Lo;0;L;;;;;N;;;;;
+A0F9;YI SYLLABLE VUR;Lo;0;L;;;;;N;;;;;
+A0FA;YI SYLLABLE VYT;Lo;0;L;;;;;N;;;;;
+A0FB;YI SYLLABLE VYX;Lo;0;L;;;;;N;;;;;
+A0FC;YI SYLLABLE VY;Lo;0;L;;;;;N;;;;;
+A0FD;YI SYLLABLE VYP;Lo;0;L;;;;;N;;;;;
+A0FE;YI SYLLABLE VYRX;Lo;0;L;;;;;N;;;;;
+A0FF;YI SYLLABLE VYR;Lo;0;L;;;;;N;;;;;
+A100;YI SYLLABLE DIT;Lo;0;L;;;;;N;;;;;
+A101;YI SYLLABLE DIX;Lo;0;L;;;;;N;;;;;
+A102;YI SYLLABLE DI;Lo;0;L;;;;;N;;;;;
+A103;YI SYLLABLE DIP;Lo;0;L;;;;;N;;;;;
+A104;YI SYLLABLE DIEX;Lo;0;L;;;;;N;;;;;
+A105;YI SYLLABLE DIE;Lo;0;L;;;;;N;;;;;
+A106;YI SYLLABLE DIEP;Lo;0;L;;;;;N;;;;;
+A107;YI SYLLABLE DAT;Lo;0;L;;;;;N;;;;;
+A108;YI SYLLABLE DAX;Lo;0;L;;;;;N;;;;;
+A109;YI SYLLABLE DA;Lo;0;L;;;;;N;;;;;
+A10A;YI SYLLABLE DAP;Lo;0;L;;;;;N;;;;;
+A10B;YI SYLLABLE DUOX;Lo;0;L;;;;;N;;;;;
+A10C;YI SYLLABLE DUO;Lo;0;L;;;;;N;;;;;
+A10D;YI SYLLABLE DOT;Lo;0;L;;;;;N;;;;;
+A10E;YI SYLLABLE DOX;Lo;0;L;;;;;N;;;;;
+A10F;YI SYLLABLE DO;Lo;0;L;;;;;N;;;;;
+A110;YI SYLLABLE DOP;Lo;0;L;;;;;N;;;;;
+A111;YI SYLLABLE DEX;Lo;0;L;;;;;N;;;;;
+A112;YI SYLLABLE DE;Lo;0;L;;;;;N;;;;;
+A113;YI SYLLABLE DEP;Lo;0;L;;;;;N;;;;;
+A114;YI SYLLABLE DUT;Lo;0;L;;;;;N;;;;;
+A115;YI SYLLABLE DUX;Lo;0;L;;;;;N;;;;;
+A116;YI SYLLABLE DU;Lo;0;L;;;;;N;;;;;
+A117;YI SYLLABLE DUP;Lo;0;L;;;;;N;;;;;
+A118;YI SYLLABLE DURX;Lo;0;L;;;;;N;;;;;
+A119;YI SYLLABLE DUR;Lo;0;L;;;;;N;;;;;
+A11A;YI SYLLABLE TIT;Lo;0;L;;;;;N;;;;;
+A11B;YI SYLLABLE TIX;Lo;0;L;;;;;N;;;;;
+A11C;YI SYLLABLE TI;Lo;0;L;;;;;N;;;;;
+A11D;YI SYLLABLE TIP;Lo;0;L;;;;;N;;;;;
+A11E;YI SYLLABLE TIEX;Lo;0;L;;;;;N;;;;;
+A11F;YI SYLLABLE TIE;Lo;0;L;;;;;N;;;;;
+A120;YI SYLLABLE TIEP;Lo;0;L;;;;;N;;;;;
+A121;YI SYLLABLE TAT;Lo;0;L;;;;;N;;;;;
+A122;YI SYLLABLE TAX;Lo;0;L;;;;;N;;;;;
+A123;YI SYLLABLE TA;Lo;0;L;;;;;N;;;;;
+A124;YI SYLLABLE TAP;Lo;0;L;;;;;N;;;;;
+A125;YI SYLLABLE TUOT;Lo;0;L;;;;;N;;;;;
+A126;YI SYLLABLE TUOX;Lo;0;L;;;;;N;;;;;
+A127;YI SYLLABLE TUO;Lo;0;L;;;;;N;;;;;
+A128;YI SYLLABLE TUOP;Lo;0;L;;;;;N;;;;;
+A129;YI SYLLABLE TOT;Lo;0;L;;;;;N;;;;;
+A12A;YI SYLLABLE TOX;Lo;0;L;;;;;N;;;;;
+A12B;YI SYLLABLE TO;Lo;0;L;;;;;N;;;;;
+A12C;YI SYLLABLE TOP;Lo;0;L;;;;;N;;;;;
+A12D;YI SYLLABLE TEX;Lo;0;L;;;;;N;;;;;
+A12E;YI SYLLABLE TE;Lo;0;L;;;;;N;;;;;
+A12F;YI SYLLABLE TEP;Lo;0;L;;;;;N;;;;;
+A130;YI SYLLABLE TUT;Lo;0;L;;;;;N;;;;;
+A131;YI SYLLABLE TUX;Lo;0;L;;;;;N;;;;;
+A132;YI SYLLABLE TU;Lo;0;L;;;;;N;;;;;
+A133;YI SYLLABLE TUP;Lo;0;L;;;;;N;;;;;
+A134;YI SYLLABLE TURX;Lo;0;L;;;;;N;;;;;
+A135;YI SYLLABLE TUR;Lo;0;L;;;;;N;;;;;
+A136;YI SYLLABLE DDIT;Lo;0;L;;;;;N;;;;;
+A137;YI SYLLABLE DDIX;Lo;0;L;;;;;N;;;;;
+A138;YI SYLLABLE DDI;Lo;0;L;;;;;N;;;;;
+A139;YI SYLLABLE DDIP;Lo;0;L;;;;;N;;;;;
+A13A;YI SYLLABLE DDIEX;Lo;0;L;;;;;N;;;;;
+A13B;YI SYLLABLE DDIE;Lo;0;L;;;;;N;;;;;
+A13C;YI SYLLABLE DDIEP;Lo;0;L;;;;;N;;;;;
+A13D;YI SYLLABLE DDAT;Lo;0;L;;;;;N;;;;;
+A13E;YI SYLLABLE DDAX;Lo;0;L;;;;;N;;;;;
+A13F;YI SYLLABLE DDA;Lo;0;L;;;;;N;;;;;
+A140;YI SYLLABLE DDAP;Lo;0;L;;;;;N;;;;;
+A141;YI SYLLABLE DDUOX;Lo;0;L;;;;;N;;;;;
+A142;YI SYLLABLE DDUO;Lo;0;L;;;;;N;;;;;
+A143;YI SYLLABLE DDUOP;Lo;0;L;;;;;N;;;;;
+A144;YI SYLLABLE DDOT;Lo;0;L;;;;;N;;;;;
+A145;YI SYLLABLE DDOX;Lo;0;L;;;;;N;;;;;
+A146;YI SYLLABLE DDO;Lo;0;L;;;;;N;;;;;
+A147;YI SYLLABLE DDOP;Lo;0;L;;;;;N;;;;;
+A148;YI SYLLABLE DDEX;Lo;0;L;;;;;N;;;;;
+A149;YI SYLLABLE DDE;Lo;0;L;;;;;N;;;;;
+A14A;YI SYLLABLE DDEP;Lo;0;L;;;;;N;;;;;
+A14B;YI SYLLABLE DDUT;Lo;0;L;;;;;N;;;;;
+A14C;YI SYLLABLE DDUX;Lo;0;L;;;;;N;;;;;
+A14D;YI SYLLABLE DDU;Lo;0;L;;;;;N;;;;;
+A14E;YI SYLLABLE DDUP;Lo;0;L;;;;;N;;;;;
+A14F;YI SYLLABLE DDURX;Lo;0;L;;;;;N;;;;;
+A150;YI SYLLABLE DDUR;Lo;0;L;;;;;N;;;;;
+A151;YI SYLLABLE NDIT;Lo;0;L;;;;;N;;;;;
+A152;YI SYLLABLE NDIX;Lo;0;L;;;;;N;;;;;
+A153;YI SYLLABLE NDI;Lo;0;L;;;;;N;;;;;
+A154;YI SYLLABLE NDIP;Lo;0;L;;;;;N;;;;;
+A155;YI SYLLABLE NDIEX;Lo;0;L;;;;;N;;;;;
+A156;YI SYLLABLE NDIE;Lo;0;L;;;;;N;;;;;
+A157;YI SYLLABLE NDAT;Lo;0;L;;;;;N;;;;;
+A158;YI SYLLABLE NDAX;Lo;0;L;;;;;N;;;;;
+A159;YI SYLLABLE NDA;Lo;0;L;;;;;N;;;;;
+A15A;YI SYLLABLE NDAP;Lo;0;L;;;;;N;;;;;
+A15B;YI SYLLABLE NDOT;Lo;0;L;;;;;N;;;;;
+A15C;YI SYLLABLE NDOX;Lo;0;L;;;;;N;;;;;
+A15D;YI SYLLABLE NDO;Lo;0;L;;;;;N;;;;;
+A15E;YI SYLLABLE NDOP;Lo;0;L;;;;;N;;;;;
+A15F;YI SYLLABLE NDEX;Lo;0;L;;;;;N;;;;;
+A160;YI SYLLABLE NDE;Lo;0;L;;;;;N;;;;;
+A161;YI SYLLABLE NDEP;Lo;0;L;;;;;N;;;;;
+A162;YI SYLLABLE NDUT;Lo;0;L;;;;;N;;;;;
+A163;YI SYLLABLE NDUX;Lo;0;L;;;;;N;;;;;
+A164;YI SYLLABLE NDU;Lo;0;L;;;;;N;;;;;
+A165;YI SYLLABLE NDUP;Lo;0;L;;;;;N;;;;;
+A166;YI SYLLABLE NDURX;Lo;0;L;;;;;N;;;;;
+A167;YI SYLLABLE NDUR;Lo;0;L;;;;;N;;;;;
+A168;YI SYLLABLE HNIT;Lo;0;L;;;;;N;;;;;
+A169;YI SYLLABLE HNIX;Lo;0;L;;;;;N;;;;;
+A16A;YI SYLLABLE HNI;Lo;0;L;;;;;N;;;;;
+A16B;YI SYLLABLE HNIP;Lo;0;L;;;;;N;;;;;
+A16C;YI SYLLABLE HNIET;Lo;0;L;;;;;N;;;;;
+A16D;YI SYLLABLE HNIEX;Lo;0;L;;;;;N;;;;;
+A16E;YI SYLLABLE HNIE;Lo;0;L;;;;;N;;;;;
+A16F;YI SYLLABLE HNIEP;Lo;0;L;;;;;N;;;;;
+A170;YI SYLLABLE HNAT;Lo;0;L;;;;;N;;;;;
+A171;YI SYLLABLE HNAX;Lo;0;L;;;;;N;;;;;
+A172;YI SYLLABLE HNA;Lo;0;L;;;;;N;;;;;
+A173;YI SYLLABLE HNAP;Lo;0;L;;;;;N;;;;;
+A174;YI SYLLABLE HNUOX;Lo;0;L;;;;;N;;;;;
+A175;YI SYLLABLE HNUO;Lo;0;L;;;;;N;;;;;
+A176;YI SYLLABLE HNOT;Lo;0;L;;;;;N;;;;;
+A177;YI SYLLABLE HNOX;Lo;0;L;;;;;N;;;;;
+A178;YI SYLLABLE HNOP;Lo;0;L;;;;;N;;;;;
+A179;YI SYLLABLE HNEX;Lo;0;L;;;;;N;;;;;
+A17A;YI SYLLABLE HNE;Lo;0;L;;;;;N;;;;;
+A17B;YI SYLLABLE HNEP;Lo;0;L;;;;;N;;;;;
+A17C;YI SYLLABLE HNUT;Lo;0;L;;;;;N;;;;;
+A17D;YI SYLLABLE NIT;Lo;0;L;;;;;N;;;;;
+A17E;YI SYLLABLE NIX;Lo;0;L;;;;;N;;;;;
+A17F;YI SYLLABLE NI;Lo;0;L;;;;;N;;;;;
+A180;YI SYLLABLE NIP;Lo;0;L;;;;;N;;;;;
+A181;YI SYLLABLE NIEX;Lo;0;L;;;;;N;;;;;
+A182;YI SYLLABLE NIE;Lo;0;L;;;;;N;;;;;
+A183;YI SYLLABLE NIEP;Lo;0;L;;;;;N;;;;;
+A184;YI SYLLABLE NAX;Lo;0;L;;;;;N;;;;;
+A185;YI SYLLABLE NA;Lo;0;L;;;;;N;;;;;
+A186;YI SYLLABLE NAP;Lo;0;L;;;;;N;;;;;
+A187;YI SYLLABLE NUOX;Lo;0;L;;;;;N;;;;;
+A188;YI SYLLABLE NUO;Lo;0;L;;;;;N;;;;;
+A189;YI SYLLABLE NUOP;Lo;0;L;;;;;N;;;;;
+A18A;YI SYLLABLE NOT;Lo;0;L;;;;;N;;;;;
+A18B;YI SYLLABLE NOX;Lo;0;L;;;;;N;;;;;
+A18C;YI SYLLABLE NO;Lo;0;L;;;;;N;;;;;
+A18D;YI SYLLABLE NOP;Lo;0;L;;;;;N;;;;;
+A18E;YI SYLLABLE NEX;Lo;0;L;;;;;N;;;;;
+A18F;YI SYLLABLE NE;Lo;0;L;;;;;N;;;;;
+A190;YI SYLLABLE NEP;Lo;0;L;;;;;N;;;;;
+A191;YI SYLLABLE NUT;Lo;0;L;;;;;N;;;;;
+A192;YI SYLLABLE NUX;Lo;0;L;;;;;N;;;;;
+A193;YI SYLLABLE NU;Lo;0;L;;;;;N;;;;;
+A194;YI SYLLABLE NUP;Lo;0;L;;;;;N;;;;;
+A195;YI SYLLABLE NURX;Lo;0;L;;;;;N;;;;;
+A196;YI SYLLABLE NUR;Lo;0;L;;;;;N;;;;;
+A197;YI SYLLABLE HLIT;Lo;0;L;;;;;N;;;;;
+A198;YI SYLLABLE HLIX;Lo;0;L;;;;;N;;;;;
+A199;YI SYLLABLE HLI;Lo;0;L;;;;;N;;;;;
+A19A;YI SYLLABLE HLIP;Lo;0;L;;;;;N;;;;;
+A19B;YI SYLLABLE HLIEX;Lo;0;L;;;;;N;;;;;
+A19C;YI SYLLABLE HLIE;Lo;0;L;;;;;N;;;;;
+A19D;YI SYLLABLE HLIEP;Lo;0;L;;;;;N;;;;;
+A19E;YI SYLLABLE HLAT;Lo;0;L;;;;;N;;;;;
+A19F;YI SYLLABLE HLAX;Lo;0;L;;;;;N;;;;;
+A1A0;YI SYLLABLE HLA;Lo;0;L;;;;;N;;;;;
+A1A1;YI SYLLABLE HLAP;Lo;0;L;;;;;N;;;;;
+A1A2;YI SYLLABLE HLUOX;Lo;0;L;;;;;N;;;;;
+A1A3;YI SYLLABLE HLUO;Lo;0;L;;;;;N;;;;;
+A1A4;YI SYLLABLE HLUOP;Lo;0;L;;;;;N;;;;;
+A1A5;YI SYLLABLE HLOX;Lo;0;L;;;;;N;;;;;
+A1A6;YI SYLLABLE HLO;Lo;0;L;;;;;N;;;;;
+A1A7;YI SYLLABLE HLOP;Lo;0;L;;;;;N;;;;;
+A1A8;YI SYLLABLE HLEX;Lo;0;L;;;;;N;;;;;
+A1A9;YI SYLLABLE HLE;Lo;0;L;;;;;N;;;;;
+A1AA;YI SYLLABLE HLEP;Lo;0;L;;;;;N;;;;;
+A1AB;YI SYLLABLE HLUT;Lo;0;L;;;;;N;;;;;
+A1AC;YI SYLLABLE HLUX;Lo;0;L;;;;;N;;;;;
+A1AD;YI SYLLABLE HLU;Lo;0;L;;;;;N;;;;;
+A1AE;YI SYLLABLE HLUP;Lo;0;L;;;;;N;;;;;
+A1AF;YI SYLLABLE HLURX;Lo;0;L;;;;;N;;;;;
+A1B0;YI SYLLABLE HLUR;Lo;0;L;;;;;N;;;;;
+A1B1;YI SYLLABLE HLYT;Lo;0;L;;;;;N;;;;;
+A1B2;YI SYLLABLE HLYX;Lo;0;L;;;;;N;;;;;
+A1B3;YI SYLLABLE HLY;Lo;0;L;;;;;N;;;;;
+A1B4;YI SYLLABLE HLYP;Lo;0;L;;;;;N;;;;;
+A1B5;YI SYLLABLE HLYRX;Lo;0;L;;;;;N;;;;;
+A1B6;YI SYLLABLE HLYR;Lo;0;L;;;;;N;;;;;
+A1B7;YI SYLLABLE LIT;Lo;0;L;;;;;N;;;;;
+A1B8;YI SYLLABLE LIX;Lo;0;L;;;;;N;;;;;
+A1B9;YI SYLLABLE LI;Lo;0;L;;;;;N;;;;;
+A1BA;YI SYLLABLE LIP;Lo;0;L;;;;;N;;;;;
+A1BB;YI SYLLABLE LIET;Lo;0;L;;;;;N;;;;;
+A1BC;YI SYLLABLE LIEX;Lo;0;L;;;;;N;;;;;
+A1BD;YI SYLLABLE LIE;Lo;0;L;;;;;N;;;;;
+A1BE;YI SYLLABLE LIEP;Lo;0;L;;;;;N;;;;;
+A1BF;YI SYLLABLE LAT;Lo;0;L;;;;;N;;;;;
+A1C0;YI SYLLABLE LAX;Lo;0;L;;;;;N;;;;;
+A1C1;YI SYLLABLE LA;Lo;0;L;;;;;N;;;;;
+A1C2;YI SYLLABLE LAP;Lo;0;L;;;;;N;;;;;
+A1C3;YI SYLLABLE LUOT;Lo;0;L;;;;;N;;;;;
+A1C4;YI SYLLABLE LUOX;Lo;0;L;;;;;N;;;;;
+A1C5;YI SYLLABLE LUO;Lo;0;L;;;;;N;;;;;
+A1C6;YI SYLLABLE LUOP;Lo;0;L;;;;;N;;;;;
+A1C7;YI SYLLABLE LOT;Lo;0;L;;;;;N;;;;;
+A1C8;YI SYLLABLE LOX;Lo;0;L;;;;;N;;;;;
+A1C9;YI SYLLABLE LO;Lo;0;L;;;;;N;;;;;
+A1CA;YI SYLLABLE LOP;Lo;0;L;;;;;N;;;;;
+A1CB;YI SYLLABLE LEX;Lo;0;L;;;;;N;;;;;
+A1CC;YI SYLLABLE LE;Lo;0;L;;;;;N;;;;;
+A1CD;YI SYLLABLE LEP;Lo;0;L;;;;;N;;;;;
+A1CE;YI SYLLABLE LUT;Lo;0;L;;;;;N;;;;;
+A1CF;YI SYLLABLE LUX;Lo;0;L;;;;;N;;;;;
+A1D0;YI SYLLABLE LU;Lo;0;L;;;;;N;;;;;
+A1D1;YI SYLLABLE LUP;Lo;0;L;;;;;N;;;;;
+A1D2;YI SYLLABLE LURX;Lo;0;L;;;;;N;;;;;
+A1D3;YI SYLLABLE LUR;Lo;0;L;;;;;N;;;;;
+A1D4;YI SYLLABLE LYT;Lo;0;L;;;;;N;;;;;
+A1D5;YI SYLLABLE LYX;Lo;0;L;;;;;N;;;;;
+A1D6;YI SYLLABLE LY;Lo;0;L;;;;;N;;;;;
+A1D7;YI SYLLABLE LYP;Lo;0;L;;;;;N;;;;;
+A1D8;YI SYLLABLE LYRX;Lo;0;L;;;;;N;;;;;
+A1D9;YI SYLLABLE LYR;Lo;0;L;;;;;N;;;;;
+A1DA;YI SYLLABLE GIT;Lo;0;L;;;;;N;;;;;
+A1DB;YI SYLLABLE GIX;Lo;0;L;;;;;N;;;;;
+A1DC;YI SYLLABLE GI;Lo;0;L;;;;;N;;;;;
+A1DD;YI SYLLABLE GIP;Lo;0;L;;;;;N;;;;;
+A1DE;YI SYLLABLE GIET;Lo;0;L;;;;;N;;;;;
+A1DF;YI SYLLABLE GIEX;Lo;0;L;;;;;N;;;;;
+A1E0;YI SYLLABLE GIE;Lo;0;L;;;;;N;;;;;
+A1E1;YI SYLLABLE GIEP;Lo;0;L;;;;;N;;;;;
+A1E2;YI SYLLABLE GAT;Lo;0;L;;;;;N;;;;;
+A1E3;YI SYLLABLE GAX;Lo;0;L;;;;;N;;;;;
+A1E4;YI SYLLABLE GA;Lo;0;L;;;;;N;;;;;
+A1E5;YI SYLLABLE GAP;Lo;0;L;;;;;N;;;;;
+A1E6;YI SYLLABLE GUOT;Lo;0;L;;;;;N;;;;;
+A1E7;YI SYLLABLE GUOX;Lo;0;L;;;;;N;;;;;
+A1E8;YI SYLLABLE GUO;Lo;0;L;;;;;N;;;;;
+A1E9;YI SYLLABLE GUOP;Lo;0;L;;;;;N;;;;;
+A1EA;YI SYLLABLE GOT;Lo;0;L;;;;;N;;;;;
+A1EB;YI SYLLABLE GOX;Lo;0;L;;;;;N;;;;;
+A1EC;YI SYLLABLE GO;Lo;0;L;;;;;N;;;;;
+A1ED;YI SYLLABLE GOP;Lo;0;L;;;;;N;;;;;
+A1EE;YI SYLLABLE GET;Lo;0;L;;;;;N;;;;;
+A1EF;YI SYLLABLE GEX;Lo;0;L;;;;;N;;;;;
+A1F0;YI SYLLABLE GE;Lo;0;L;;;;;N;;;;;
+A1F1;YI SYLLABLE GEP;Lo;0;L;;;;;N;;;;;
+A1F2;YI SYLLABLE GUT;Lo;0;L;;;;;N;;;;;
+A1F3;YI SYLLABLE GUX;Lo;0;L;;;;;N;;;;;
+A1F4;YI SYLLABLE GU;Lo;0;L;;;;;N;;;;;
+A1F5;YI SYLLABLE GUP;Lo;0;L;;;;;N;;;;;
+A1F6;YI SYLLABLE GURX;Lo;0;L;;;;;N;;;;;
+A1F7;YI SYLLABLE GUR;Lo;0;L;;;;;N;;;;;
+A1F8;YI SYLLABLE KIT;Lo;0;L;;;;;N;;;;;
+A1F9;YI SYLLABLE KIX;Lo;0;L;;;;;N;;;;;
+A1FA;YI SYLLABLE KI;Lo;0;L;;;;;N;;;;;
+A1FB;YI SYLLABLE KIP;Lo;0;L;;;;;N;;;;;
+A1FC;YI SYLLABLE KIEX;Lo;0;L;;;;;N;;;;;
+A1FD;YI SYLLABLE KIE;Lo;0;L;;;;;N;;;;;
+A1FE;YI SYLLABLE KIEP;Lo;0;L;;;;;N;;;;;
+A1FF;YI SYLLABLE KAT;Lo;0;L;;;;;N;;;;;
+A200;YI SYLLABLE KAX;Lo;0;L;;;;;N;;;;;
+A201;YI SYLLABLE KA;Lo;0;L;;;;;N;;;;;
+A202;YI SYLLABLE KAP;Lo;0;L;;;;;N;;;;;
+A203;YI SYLLABLE KUOX;Lo;0;L;;;;;N;;;;;
+A204;YI SYLLABLE KUO;Lo;0;L;;;;;N;;;;;
+A205;YI SYLLABLE KUOP;Lo;0;L;;;;;N;;;;;
+A206;YI SYLLABLE KOT;Lo;0;L;;;;;N;;;;;
+A207;YI SYLLABLE KOX;Lo;0;L;;;;;N;;;;;
+A208;YI SYLLABLE KO;Lo;0;L;;;;;N;;;;;
+A209;YI SYLLABLE KOP;Lo;0;L;;;;;N;;;;;
+A20A;YI SYLLABLE KET;Lo;0;L;;;;;N;;;;;
+A20B;YI SYLLABLE KEX;Lo;0;L;;;;;N;;;;;
+A20C;YI SYLLABLE KE;Lo;0;L;;;;;N;;;;;
+A20D;YI SYLLABLE KEP;Lo;0;L;;;;;N;;;;;
+A20E;YI SYLLABLE KUT;Lo;0;L;;;;;N;;;;;
+A20F;YI SYLLABLE KUX;Lo;0;L;;;;;N;;;;;
+A210;YI SYLLABLE KU;Lo;0;L;;;;;N;;;;;
+A211;YI SYLLABLE KUP;Lo;0;L;;;;;N;;;;;
+A212;YI SYLLABLE KURX;Lo;0;L;;;;;N;;;;;
+A213;YI SYLLABLE KUR;Lo;0;L;;;;;N;;;;;
+A214;YI SYLLABLE GGIT;Lo;0;L;;;;;N;;;;;
+A215;YI SYLLABLE GGIX;Lo;0;L;;;;;N;;;;;
+A216;YI SYLLABLE GGI;Lo;0;L;;;;;N;;;;;
+A217;YI SYLLABLE GGIEX;Lo;0;L;;;;;N;;;;;
+A218;YI SYLLABLE GGIE;Lo;0;L;;;;;N;;;;;
+A219;YI SYLLABLE GGIEP;Lo;0;L;;;;;N;;;;;
+A21A;YI SYLLABLE GGAT;Lo;0;L;;;;;N;;;;;
+A21B;YI SYLLABLE GGAX;Lo;0;L;;;;;N;;;;;
+A21C;YI SYLLABLE GGA;Lo;0;L;;;;;N;;;;;
+A21D;YI SYLLABLE GGAP;Lo;0;L;;;;;N;;;;;
+A21E;YI SYLLABLE GGUOT;Lo;0;L;;;;;N;;;;;
+A21F;YI SYLLABLE GGUOX;Lo;0;L;;;;;N;;;;;
+A220;YI SYLLABLE GGUO;Lo;0;L;;;;;N;;;;;
+A221;YI SYLLABLE GGUOP;Lo;0;L;;;;;N;;;;;
+A222;YI SYLLABLE GGOT;Lo;0;L;;;;;N;;;;;
+A223;YI SYLLABLE GGOX;Lo;0;L;;;;;N;;;;;
+A224;YI SYLLABLE GGO;Lo;0;L;;;;;N;;;;;
+A225;YI SYLLABLE GGOP;Lo;0;L;;;;;N;;;;;
+A226;YI SYLLABLE GGET;Lo;0;L;;;;;N;;;;;
+A227;YI SYLLABLE GGEX;Lo;0;L;;;;;N;;;;;
+A228;YI SYLLABLE GGE;Lo;0;L;;;;;N;;;;;
+A229;YI SYLLABLE GGEP;Lo;0;L;;;;;N;;;;;
+A22A;YI SYLLABLE GGUT;Lo;0;L;;;;;N;;;;;
+A22B;YI SYLLABLE GGUX;Lo;0;L;;;;;N;;;;;
+A22C;YI SYLLABLE GGU;Lo;0;L;;;;;N;;;;;
+A22D;YI SYLLABLE GGUP;Lo;0;L;;;;;N;;;;;
+A22E;YI SYLLABLE GGURX;Lo;0;L;;;;;N;;;;;
+A22F;YI SYLLABLE GGUR;Lo;0;L;;;;;N;;;;;
+A230;YI SYLLABLE MGIEX;Lo;0;L;;;;;N;;;;;
+A231;YI SYLLABLE MGIE;Lo;0;L;;;;;N;;;;;
+A232;YI SYLLABLE MGAT;Lo;0;L;;;;;N;;;;;
+A233;YI SYLLABLE MGAX;Lo;0;L;;;;;N;;;;;
+A234;YI SYLLABLE MGA;Lo;0;L;;;;;N;;;;;
+A235;YI SYLLABLE MGAP;Lo;0;L;;;;;N;;;;;
+A236;YI SYLLABLE MGUOX;Lo;0;L;;;;;N;;;;;
+A237;YI SYLLABLE MGUO;Lo;0;L;;;;;N;;;;;
+A238;YI SYLLABLE MGUOP;Lo;0;L;;;;;N;;;;;
+A239;YI SYLLABLE MGOT;Lo;0;L;;;;;N;;;;;
+A23A;YI SYLLABLE MGOX;Lo;0;L;;;;;N;;;;;
+A23B;YI SYLLABLE MGO;Lo;0;L;;;;;N;;;;;
+A23C;YI SYLLABLE MGOP;Lo;0;L;;;;;N;;;;;
+A23D;YI SYLLABLE MGEX;Lo;0;L;;;;;N;;;;;
+A23E;YI SYLLABLE MGE;Lo;0;L;;;;;N;;;;;
+A23F;YI SYLLABLE MGEP;Lo;0;L;;;;;N;;;;;
+A240;YI SYLLABLE MGUT;Lo;0;L;;;;;N;;;;;
+A241;YI SYLLABLE MGUX;Lo;0;L;;;;;N;;;;;
+A242;YI SYLLABLE MGU;Lo;0;L;;;;;N;;;;;
+A243;YI SYLLABLE MGUP;Lo;0;L;;;;;N;;;;;
+A244;YI SYLLABLE MGURX;Lo;0;L;;;;;N;;;;;
+A245;YI SYLLABLE MGUR;Lo;0;L;;;;;N;;;;;
+A246;YI SYLLABLE HXIT;Lo;0;L;;;;;N;;;;;
+A247;YI SYLLABLE HXIX;Lo;0;L;;;;;N;;;;;
+A248;YI SYLLABLE HXI;Lo;0;L;;;;;N;;;;;
+A249;YI SYLLABLE HXIP;Lo;0;L;;;;;N;;;;;
+A24A;YI SYLLABLE HXIET;Lo;0;L;;;;;N;;;;;
+A24B;YI SYLLABLE HXIEX;Lo;0;L;;;;;N;;;;;
+A24C;YI SYLLABLE HXIE;Lo;0;L;;;;;N;;;;;
+A24D;YI SYLLABLE HXIEP;Lo;0;L;;;;;N;;;;;
+A24E;YI SYLLABLE HXAT;Lo;0;L;;;;;N;;;;;
+A24F;YI SYLLABLE HXAX;Lo;0;L;;;;;N;;;;;
+A250;YI SYLLABLE HXA;Lo;0;L;;;;;N;;;;;
+A251;YI SYLLABLE HXAP;Lo;0;L;;;;;N;;;;;
+A252;YI SYLLABLE HXUOT;Lo;0;L;;;;;N;;;;;
+A253;YI SYLLABLE HXUOX;Lo;0;L;;;;;N;;;;;
+A254;YI SYLLABLE HXUO;Lo;0;L;;;;;N;;;;;
+A255;YI SYLLABLE HXUOP;Lo;0;L;;;;;N;;;;;
+A256;YI SYLLABLE HXOT;Lo;0;L;;;;;N;;;;;
+A257;YI SYLLABLE HXOX;Lo;0;L;;;;;N;;;;;
+A258;YI SYLLABLE HXO;Lo;0;L;;;;;N;;;;;
+A259;YI SYLLABLE HXOP;Lo;0;L;;;;;N;;;;;
+A25A;YI SYLLABLE HXEX;Lo;0;L;;;;;N;;;;;
+A25B;YI SYLLABLE HXE;Lo;0;L;;;;;N;;;;;
+A25C;YI SYLLABLE HXEP;Lo;0;L;;;;;N;;;;;
+A25D;YI SYLLABLE NGIEX;Lo;0;L;;;;;N;;;;;
+A25E;YI SYLLABLE NGIE;Lo;0;L;;;;;N;;;;;
+A25F;YI SYLLABLE NGIEP;Lo;0;L;;;;;N;;;;;
+A260;YI SYLLABLE NGAT;Lo;0;L;;;;;N;;;;;
+A261;YI SYLLABLE NGAX;Lo;0;L;;;;;N;;;;;
+A262;YI SYLLABLE NGA;Lo;0;L;;;;;N;;;;;
+A263;YI SYLLABLE NGAP;Lo;0;L;;;;;N;;;;;
+A264;YI SYLLABLE NGUOT;Lo;0;L;;;;;N;;;;;
+A265;YI SYLLABLE NGUOX;Lo;0;L;;;;;N;;;;;
+A266;YI SYLLABLE NGUO;Lo;0;L;;;;;N;;;;;
+A267;YI SYLLABLE NGOT;Lo;0;L;;;;;N;;;;;
+A268;YI SYLLABLE NGOX;Lo;0;L;;;;;N;;;;;
+A269;YI SYLLABLE NGO;Lo;0;L;;;;;N;;;;;
+A26A;YI SYLLABLE NGOP;Lo;0;L;;;;;N;;;;;
+A26B;YI SYLLABLE NGEX;Lo;0;L;;;;;N;;;;;
+A26C;YI SYLLABLE NGE;Lo;0;L;;;;;N;;;;;
+A26D;YI SYLLABLE NGEP;Lo;0;L;;;;;N;;;;;
+A26E;YI SYLLABLE HIT;Lo;0;L;;;;;N;;;;;
+A26F;YI SYLLABLE HIEX;Lo;0;L;;;;;N;;;;;
+A270;YI SYLLABLE HIE;Lo;0;L;;;;;N;;;;;
+A271;YI SYLLABLE HAT;Lo;0;L;;;;;N;;;;;
+A272;YI SYLLABLE HAX;Lo;0;L;;;;;N;;;;;
+A273;YI SYLLABLE HA;Lo;0;L;;;;;N;;;;;
+A274;YI SYLLABLE HAP;Lo;0;L;;;;;N;;;;;
+A275;YI SYLLABLE HUOT;Lo;0;L;;;;;N;;;;;
+A276;YI SYLLABLE HUOX;Lo;0;L;;;;;N;;;;;
+A277;YI SYLLABLE HUO;Lo;0;L;;;;;N;;;;;
+A278;YI SYLLABLE HUOP;Lo;0;L;;;;;N;;;;;
+A279;YI SYLLABLE HOT;Lo;0;L;;;;;N;;;;;
+A27A;YI SYLLABLE HOX;Lo;0;L;;;;;N;;;;;
+A27B;YI SYLLABLE HO;Lo;0;L;;;;;N;;;;;
+A27C;YI SYLLABLE HOP;Lo;0;L;;;;;N;;;;;
+A27D;YI SYLLABLE HEX;Lo;0;L;;;;;N;;;;;
+A27E;YI SYLLABLE HE;Lo;0;L;;;;;N;;;;;
+A27F;YI SYLLABLE HEP;Lo;0;L;;;;;N;;;;;
+A280;YI SYLLABLE WAT;Lo;0;L;;;;;N;;;;;
+A281;YI SYLLABLE WAX;Lo;0;L;;;;;N;;;;;
+A282;YI SYLLABLE WA;Lo;0;L;;;;;N;;;;;
+A283;YI SYLLABLE WAP;Lo;0;L;;;;;N;;;;;
+A284;YI SYLLABLE WUOX;Lo;0;L;;;;;N;;;;;
+A285;YI SYLLABLE WUO;Lo;0;L;;;;;N;;;;;
+A286;YI SYLLABLE WUOP;Lo;0;L;;;;;N;;;;;
+A287;YI SYLLABLE WOX;Lo;0;L;;;;;N;;;;;
+A288;YI SYLLABLE WO;Lo;0;L;;;;;N;;;;;
+A289;YI SYLLABLE WOP;Lo;0;L;;;;;N;;;;;
+A28A;YI SYLLABLE WEX;Lo;0;L;;;;;N;;;;;
+A28B;YI SYLLABLE WE;Lo;0;L;;;;;N;;;;;
+A28C;YI SYLLABLE WEP;Lo;0;L;;;;;N;;;;;
+A28D;YI SYLLABLE ZIT;Lo;0;L;;;;;N;;;;;
+A28E;YI SYLLABLE ZIX;Lo;0;L;;;;;N;;;;;
+A28F;YI SYLLABLE ZI;Lo;0;L;;;;;N;;;;;
+A290;YI SYLLABLE ZIP;Lo;0;L;;;;;N;;;;;
+A291;YI SYLLABLE ZIEX;Lo;0;L;;;;;N;;;;;
+A292;YI SYLLABLE ZIE;Lo;0;L;;;;;N;;;;;
+A293;YI SYLLABLE ZIEP;Lo;0;L;;;;;N;;;;;
+A294;YI SYLLABLE ZAT;Lo;0;L;;;;;N;;;;;
+A295;YI SYLLABLE ZAX;Lo;0;L;;;;;N;;;;;
+A296;YI SYLLABLE ZA;Lo;0;L;;;;;N;;;;;
+A297;YI SYLLABLE ZAP;Lo;0;L;;;;;N;;;;;
+A298;YI SYLLABLE ZUOX;Lo;0;L;;;;;N;;;;;
+A299;YI SYLLABLE ZUO;Lo;0;L;;;;;N;;;;;
+A29A;YI SYLLABLE ZUOP;Lo;0;L;;;;;N;;;;;
+A29B;YI SYLLABLE ZOT;Lo;0;L;;;;;N;;;;;
+A29C;YI SYLLABLE ZOX;Lo;0;L;;;;;N;;;;;
+A29D;YI SYLLABLE ZO;Lo;0;L;;;;;N;;;;;
+A29E;YI SYLLABLE ZOP;Lo;0;L;;;;;N;;;;;
+A29F;YI SYLLABLE ZEX;Lo;0;L;;;;;N;;;;;
+A2A0;YI SYLLABLE ZE;Lo;0;L;;;;;N;;;;;
+A2A1;YI SYLLABLE ZEP;Lo;0;L;;;;;N;;;;;
+A2A2;YI SYLLABLE ZUT;Lo;0;L;;;;;N;;;;;
+A2A3;YI SYLLABLE ZUX;Lo;0;L;;;;;N;;;;;
+A2A4;YI SYLLABLE ZU;Lo;0;L;;;;;N;;;;;
+A2A5;YI SYLLABLE ZUP;Lo;0;L;;;;;N;;;;;
+A2A6;YI SYLLABLE ZURX;Lo;0;L;;;;;N;;;;;
+A2A7;YI SYLLABLE ZUR;Lo;0;L;;;;;N;;;;;
+A2A8;YI SYLLABLE ZYT;Lo;0;L;;;;;N;;;;;
+A2A9;YI SYLLABLE ZYX;Lo;0;L;;;;;N;;;;;
+A2AA;YI SYLLABLE ZY;Lo;0;L;;;;;N;;;;;
+A2AB;YI SYLLABLE ZYP;Lo;0;L;;;;;N;;;;;
+A2AC;YI SYLLABLE ZYRX;Lo;0;L;;;;;N;;;;;
+A2AD;YI SYLLABLE ZYR;Lo;0;L;;;;;N;;;;;
+A2AE;YI SYLLABLE CIT;Lo;0;L;;;;;N;;;;;
+A2AF;YI SYLLABLE CIX;Lo;0;L;;;;;N;;;;;
+A2B0;YI SYLLABLE CI;Lo;0;L;;;;;N;;;;;
+A2B1;YI SYLLABLE CIP;Lo;0;L;;;;;N;;;;;
+A2B2;YI SYLLABLE CIET;Lo;0;L;;;;;N;;;;;
+A2B3;YI SYLLABLE CIEX;Lo;0;L;;;;;N;;;;;
+A2B4;YI SYLLABLE CIE;Lo;0;L;;;;;N;;;;;
+A2B5;YI SYLLABLE CIEP;Lo;0;L;;;;;N;;;;;
+A2B6;YI SYLLABLE CAT;Lo;0;L;;;;;N;;;;;
+A2B7;YI SYLLABLE CAX;Lo;0;L;;;;;N;;;;;
+A2B8;YI SYLLABLE CA;Lo;0;L;;;;;N;;;;;
+A2B9;YI SYLLABLE CAP;Lo;0;L;;;;;N;;;;;
+A2BA;YI SYLLABLE CUOX;Lo;0;L;;;;;N;;;;;
+A2BB;YI SYLLABLE CUO;Lo;0;L;;;;;N;;;;;
+A2BC;YI SYLLABLE CUOP;Lo;0;L;;;;;N;;;;;
+A2BD;YI SYLLABLE COT;Lo;0;L;;;;;N;;;;;
+A2BE;YI SYLLABLE COX;Lo;0;L;;;;;N;;;;;
+A2BF;YI SYLLABLE CO;Lo;0;L;;;;;N;;;;;
+A2C0;YI SYLLABLE COP;Lo;0;L;;;;;N;;;;;
+A2C1;YI SYLLABLE CEX;Lo;0;L;;;;;N;;;;;
+A2C2;YI SYLLABLE CE;Lo;0;L;;;;;N;;;;;
+A2C3;YI SYLLABLE CEP;Lo;0;L;;;;;N;;;;;
+A2C4;YI SYLLABLE CUT;Lo;0;L;;;;;N;;;;;
+A2C5;YI SYLLABLE CUX;Lo;0;L;;;;;N;;;;;
+A2C6;YI SYLLABLE CU;Lo;0;L;;;;;N;;;;;
+A2C7;YI SYLLABLE CUP;Lo;0;L;;;;;N;;;;;
+A2C8;YI SYLLABLE CURX;Lo;0;L;;;;;N;;;;;
+A2C9;YI SYLLABLE CUR;Lo;0;L;;;;;N;;;;;
+A2CA;YI SYLLABLE CYT;Lo;0;L;;;;;N;;;;;
+A2CB;YI SYLLABLE CYX;Lo;0;L;;;;;N;;;;;
+A2CC;YI SYLLABLE CY;Lo;0;L;;;;;N;;;;;
+A2CD;YI SYLLABLE CYP;Lo;0;L;;;;;N;;;;;
+A2CE;YI SYLLABLE CYRX;Lo;0;L;;;;;N;;;;;
+A2CF;YI SYLLABLE CYR;Lo;0;L;;;;;N;;;;;
+A2D0;YI SYLLABLE ZZIT;Lo;0;L;;;;;N;;;;;
+A2D1;YI SYLLABLE ZZIX;Lo;0;L;;;;;N;;;;;
+A2D2;YI SYLLABLE ZZI;Lo;0;L;;;;;N;;;;;
+A2D3;YI SYLLABLE ZZIP;Lo;0;L;;;;;N;;;;;
+A2D4;YI SYLLABLE ZZIET;Lo;0;L;;;;;N;;;;;
+A2D5;YI SYLLABLE ZZIEX;Lo;0;L;;;;;N;;;;;
+A2D6;YI SYLLABLE ZZIE;Lo;0;L;;;;;N;;;;;
+A2D7;YI SYLLABLE ZZIEP;Lo;0;L;;;;;N;;;;;
+A2D8;YI SYLLABLE ZZAT;Lo;0;L;;;;;N;;;;;
+A2D9;YI SYLLABLE ZZAX;Lo;0;L;;;;;N;;;;;
+A2DA;YI SYLLABLE ZZA;Lo;0;L;;;;;N;;;;;
+A2DB;YI SYLLABLE ZZAP;Lo;0;L;;;;;N;;;;;
+A2DC;YI SYLLABLE ZZOX;Lo;0;L;;;;;N;;;;;
+A2DD;YI SYLLABLE ZZO;Lo;0;L;;;;;N;;;;;
+A2DE;YI SYLLABLE ZZOP;Lo;0;L;;;;;N;;;;;
+A2DF;YI SYLLABLE ZZEX;Lo;0;L;;;;;N;;;;;
+A2E0;YI SYLLABLE ZZE;Lo;0;L;;;;;N;;;;;
+A2E1;YI SYLLABLE ZZEP;Lo;0;L;;;;;N;;;;;
+A2E2;YI SYLLABLE ZZUX;Lo;0;L;;;;;N;;;;;
+A2E3;YI SYLLABLE ZZU;Lo;0;L;;;;;N;;;;;
+A2E4;YI SYLLABLE ZZUP;Lo;0;L;;;;;N;;;;;
+A2E5;YI SYLLABLE ZZURX;Lo;0;L;;;;;N;;;;;
+A2E6;YI SYLLABLE ZZUR;Lo;0;L;;;;;N;;;;;
+A2E7;YI SYLLABLE ZZYT;Lo;0;L;;;;;N;;;;;
+A2E8;YI SYLLABLE ZZYX;Lo;0;L;;;;;N;;;;;
+A2E9;YI SYLLABLE ZZY;Lo;0;L;;;;;N;;;;;
+A2EA;YI SYLLABLE ZZYP;Lo;0;L;;;;;N;;;;;
+A2EB;YI SYLLABLE ZZYRX;Lo;0;L;;;;;N;;;;;
+A2EC;YI SYLLABLE ZZYR;Lo;0;L;;;;;N;;;;;
+A2ED;YI SYLLABLE NZIT;Lo;0;L;;;;;N;;;;;
+A2EE;YI SYLLABLE NZIX;Lo;0;L;;;;;N;;;;;
+A2EF;YI SYLLABLE NZI;Lo;0;L;;;;;N;;;;;
+A2F0;YI SYLLABLE NZIP;Lo;0;L;;;;;N;;;;;
+A2F1;YI SYLLABLE NZIEX;Lo;0;L;;;;;N;;;;;
+A2F2;YI SYLLABLE NZIE;Lo;0;L;;;;;N;;;;;
+A2F3;YI SYLLABLE NZIEP;Lo;0;L;;;;;N;;;;;
+A2F4;YI SYLLABLE NZAT;Lo;0;L;;;;;N;;;;;
+A2F5;YI SYLLABLE NZAX;Lo;0;L;;;;;N;;;;;
+A2F6;YI SYLLABLE NZA;Lo;0;L;;;;;N;;;;;
+A2F7;YI SYLLABLE NZAP;Lo;0;L;;;;;N;;;;;
+A2F8;YI SYLLABLE NZUOX;Lo;0;L;;;;;N;;;;;
+A2F9;YI SYLLABLE NZUO;Lo;0;L;;;;;N;;;;;
+A2FA;YI SYLLABLE NZOX;Lo;0;L;;;;;N;;;;;
+A2FB;YI SYLLABLE NZOP;Lo;0;L;;;;;N;;;;;
+A2FC;YI SYLLABLE NZEX;Lo;0;L;;;;;N;;;;;
+A2FD;YI SYLLABLE NZE;Lo;0;L;;;;;N;;;;;
+A2FE;YI SYLLABLE NZUX;Lo;0;L;;;;;N;;;;;
+A2FF;YI SYLLABLE NZU;Lo;0;L;;;;;N;;;;;
+A300;YI SYLLABLE NZUP;Lo;0;L;;;;;N;;;;;
+A301;YI SYLLABLE NZURX;Lo;0;L;;;;;N;;;;;
+A302;YI SYLLABLE NZUR;Lo;0;L;;;;;N;;;;;
+A303;YI SYLLABLE NZYT;Lo;0;L;;;;;N;;;;;
+A304;YI SYLLABLE NZYX;Lo;0;L;;;;;N;;;;;
+A305;YI SYLLABLE NZY;Lo;0;L;;;;;N;;;;;
+A306;YI SYLLABLE NZYP;Lo;0;L;;;;;N;;;;;
+A307;YI SYLLABLE NZYRX;Lo;0;L;;;;;N;;;;;
+A308;YI SYLLABLE NZYR;Lo;0;L;;;;;N;;;;;
+A309;YI SYLLABLE SIT;Lo;0;L;;;;;N;;;;;
+A30A;YI SYLLABLE SIX;Lo;0;L;;;;;N;;;;;
+A30B;YI SYLLABLE SI;Lo;0;L;;;;;N;;;;;
+A30C;YI SYLLABLE SIP;Lo;0;L;;;;;N;;;;;
+A30D;YI SYLLABLE SIEX;Lo;0;L;;;;;N;;;;;
+A30E;YI SYLLABLE SIE;Lo;0;L;;;;;N;;;;;
+A30F;YI SYLLABLE SIEP;Lo;0;L;;;;;N;;;;;
+A310;YI SYLLABLE SAT;Lo;0;L;;;;;N;;;;;
+A311;YI SYLLABLE SAX;Lo;0;L;;;;;N;;;;;
+A312;YI SYLLABLE SA;Lo;0;L;;;;;N;;;;;
+A313;YI SYLLABLE SAP;Lo;0;L;;;;;N;;;;;
+A314;YI SYLLABLE SUOX;Lo;0;L;;;;;N;;;;;
+A315;YI SYLLABLE SUO;Lo;0;L;;;;;N;;;;;
+A316;YI SYLLABLE SUOP;Lo;0;L;;;;;N;;;;;
+A317;YI SYLLABLE SOT;Lo;0;L;;;;;N;;;;;
+A318;YI SYLLABLE SOX;Lo;0;L;;;;;N;;;;;
+A319;YI SYLLABLE SO;Lo;0;L;;;;;N;;;;;
+A31A;YI SYLLABLE SOP;Lo;0;L;;;;;N;;;;;
+A31B;YI SYLLABLE SEX;Lo;0;L;;;;;N;;;;;
+A31C;YI SYLLABLE SE;Lo;0;L;;;;;N;;;;;
+A31D;YI SYLLABLE SEP;Lo;0;L;;;;;N;;;;;
+A31E;YI SYLLABLE SUT;Lo;0;L;;;;;N;;;;;
+A31F;YI SYLLABLE SUX;Lo;0;L;;;;;N;;;;;
+A320;YI SYLLABLE SU;Lo;0;L;;;;;N;;;;;
+A321;YI SYLLABLE SUP;Lo;0;L;;;;;N;;;;;
+A322;YI SYLLABLE SURX;Lo;0;L;;;;;N;;;;;
+A323;YI SYLLABLE SUR;Lo;0;L;;;;;N;;;;;
+A324;YI SYLLABLE SYT;Lo;0;L;;;;;N;;;;;
+A325;YI SYLLABLE SYX;Lo;0;L;;;;;N;;;;;
+A326;YI SYLLABLE SY;Lo;0;L;;;;;N;;;;;
+A327;YI SYLLABLE SYP;Lo;0;L;;;;;N;;;;;
+A328;YI SYLLABLE SYRX;Lo;0;L;;;;;N;;;;;
+A329;YI SYLLABLE SYR;Lo;0;L;;;;;N;;;;;
+A32A;YI SYLLABLE SSIT;Lo;0;L;;;;;N;;;;;
+A32B;YI SYLLABLE SSIX;Lo;0;L;;;;;N;;;;;
+A32C;YI SYLLABLE SSI;Lo;0;L;;;;;N;;;;;
+A32D;YI SYLLABLE SSIP;Lo;0;L;;;;;N;;;;;
+A32E;YI SYLLABLE SSIEX;Lo;0;L;;;;;N;;;;;
+A32F;YI SYLLABLE SSIE;Lo;0;L;;;;;N;;;;;
+A330;YI SYLLABLE SSIEP;Lo;0;L;;;;;N;;;;;
+A331;YI SYLLABLE SSAT;Lo;0;L;;;;;N;;;;;
+A332;YI SYLLABLE SSAX;Lo;0;L;;;;;N;;;;;
+A333;YI SYLLABLE SSA;Lo;0;L;;;;;N;;;;;
+A334;YI SYLLABLE SSAP;Lo;0;L;;;;;N;;;;;
+A335;YI SYLLABLE SSOT;Lo;0;L;;;;;N;;;;;
+A336;YI SYLLABLE SSOX;Lo;0;L;;;;;N;;;;;
+A337;YI SYLLABLE SSO;Lo;0;L;;;;;N;;;;;
+A338;YI SYLLABLE SSOP;Lo;0;L;;;;;N;;;;;
+A339;YI SYLLABLE SSEX;Lo;0;L;;;;;N;;;;;
+A33A;YI SYLLABLE SSE;Lo;0;L;;;;;N;;;;;
+A33B;YI SYLLABLE SSEP;Lo;0;L;;;;;N;;;;;
+A33C;YI SYLLABLE SSUT;Lo;0;L;;;;;N;;;;;
+A33D;YI SYLLABLE SSUX;Lo;0;L;;;;;N;;;;;
+A33E;YI SYLLABLE SSU;Lo;0;L;;;;;N;;;;;
+A33F;YI SYLLABLE SSUP;Lo;0;L;;;;;N;;;;;
+A340;YI SYLLABLE SSYT;Lo;0;L;;;;;N;;;;;
+A341;YI SYLLABLE SSYX;Lo;0;L;;;;;N;;;;;
+A342;YI SYLLABLE SSY;Lo;0;L;;;;;N;;;;;
+A343;YI SYLLABLE SSYP;Lo;0;L;;;;;N;;;;;
+A344;YI SYLLABLE SSYRX;Lo;0;L;;;;;N;;;;;
+A345;YI SYLLABLE SSYR;Lo;0;L;;;;;N;;;;;
+A346;YI SYLLABLE ZHAT;Lo;0;L;;;;;N;;;;;
+A347;YI SYLLABLE ZHAX;Lo;0;L;;;;;N;;;;;
+A348;YI SYLLABLE ZHA;Lo;0;L;;;;;N;;;;;
+A349;YI SYLLABLE ZHAP;Lo;0;L;;;;;N;;;;;
+A34A;YI SYLLABLE ZHUOX;Lo;0;L;;;;;N;;;;;
+A34B;YI SYLLABLE ZHUO;Lo;0;L;;;;;N;;;;;
+A34C;YI SYLLABLE ZHUOP;Lo;0;L;;;;;N;;;;;
+A34D;YI SYLLABLE ZHOT;Lo;0;L;;;;;N;;;;;
+A34E;YI SYLLABLE ZHOX;Lo;0;L;;;;;N;;;;;
+A34F;YI SYLLABLE ZHO;Lo;0;L;;;;;N;;;;;
+A350;YI SYLLABLE ZHOP;Lo;0;L;;;;;N;;;;;
+A351;YI SYLLABLE ZHET;Lo;0;L;;;;;N;;;;;
+A352;YI SYLLABLE ZHEX;Lo;0;L;;;;;N;;;;;
+A353;YI SYLLABLE ZHE;Lo;0;L;;;;;N;;;;;
+A354;YI SYLLABLE ZHEP;Lo;0;L;;;;;N;;;;;
+A355;YI SYLLABLE ZHUT;Lo;0;L;;;;;N;;;;;
+A356;YI SYLLABLE ZHUX;Lo;0;L;;;;;N;;;;;
+A357;YI SYLLABLE ZHU;Lo;0;L;;;;;N;;;;;
+A358;YI SYLLABLE ZHUP;Lo;0;L;;;;;N;;;;;
+A359;YI SYLLABLE ZHURX;Lo;0;L;;;;;N;;;;;
+A35A;YI SYLLABLE ZHUR;Lo;0;L;;;;;N;;;;;
+A35B;YI SYLLABLE ZHYT;Lo;0;L;;;;;N;;;;;
+A35C;YI SYLLABLE ZHYX;Lo;0;L;;;;;N;;;;;
+A35D;YI SYLLABLE ZHY;Lo;0;L;;;;;N;;;;;
+A35E;YI SYLLABLE ZHYP;Lo;0;L;;;;;N;;;;;
+A35F;YI SYLLABLE ZHYRX;Lo;0;L;;;;;N;;;;;
+A360;YI SYLLABLE ZHYR;Lo;0;L;;;;;N;;;;;
+A361;YI SYLLABLE CHAT;Lo;0;L;;;;;N;;;;;
+A362;YI SYLLABLE CHAX;Lo;0;L;;;;;N;;;;;
+A363;YI SYLLABLE CHA;Lo;0;L;;;;;N;;;;;
+A364;YI SYLLABLE CHAP;Lo;0;L;;;;;N;;;;;
+A365;YI SYLLABLE CHUOT;Lo;0;L;;;;;N;;;;;
+A366;YI SYLLABLE CHUOX;Lo;0;L;;;;;N;;;;;
+A367;YI SYLLABLE CHUO;Lo;0;L;;;;;N;;;;;
+A368;YI SYLLABLE CHUOP;Lo;0;L;;;;;N;;;;;
+A369;YI SYLLABLE CHOT;Lo;0;L;;;;;N;;;;;
+A36A;YI SYLLABLE CHOX;Lo;0;L;;;;;N;;;;;
+A36B;YI SYLLABLE CHO;Lo;0;L;;;;;N;;;;;
+A36C;YI SYLLABLE CHOP;Lo;0;L;;;;;N;;;;;
+A36D;YI SYLLABLE CHET;Lo;0;L;;;;;N;;;;;
+A36E;YI SYLLABLE CHEX;Lo;0;L;;;;;N;;;;;
+A36F;YI SYLLABLE CHE;Lo;0;L;;;;;N;;;;;
+A370;YI SYLLABLE CHEP;Lo;0;L;;;;;N;;;;;
+A371;YI SYLLABLE CHUX;Lo;0;L;;;;;N;;;;;
+A372;YI SYLLABLE CHU;Lo;0;L;;;;;N;;;;;
+A373;YI SYLLABLE CHUP;Lo;0;L;;;;;N;;;;;
+A374;YI SYLLABLE CHURX;Lo;0;L;;;;;N;;;;;
+A375;YI SYLLABLE CHUR;Lo;0;L;;;;;N;;;;;
+A376;YI SYLLABLE CHYT;Lo;0;L;;;;;N;;;;;
+A377;YI SYLLABLE CHYX;Lo;0;L;;;;;N;;;;;
+A378;YI SYLLABLE CHY;Lo;0;L;;;;;N;;;;;
+A379;YI SYLLABLE CHYP;Lo;0;L;;;;;N;;;;;
+A37A;YI SYLLABLE CHYRX;Lo;0;L;;;;;N;;;;;
+A37B;YI SYLLABLE CHYR;Lo;0;L;;;;;N;;;;;
+A37C;YI SYLLABLE RRAX;Lo;0;L;;;;;N;;;;;
+A37D;YI SYLLABLE RRA;Lo;0;L;;;;;N;;;;;
+A37E;YI SYLLABLE RRUOX;Lo;0;L;;;;;N;;;;;
+A37F;YI SYLLABLE RRUO;Lo;0;L;;;;;N;;;;;
+A380;YI SYLLABLE RROT;Lo;0;L;;;;;N;;;;;
+A381;YI SYLLABLE RROX;Lo;0;L;;;;;N;;;;;
+A382;YI SYLLABLE RRO;Lo;0;L;;;;;N;;;;;
+A383;YI SYLLABLE RROP;Lo;0;L;;;;;N;;;;;
+A384;YI SYLLABLE RRET;Lo;0;L;;;;;N;;;;;
+A385;YI SYLLABLE RREX;Lo;0;L;;;;;N;;;;;
+A386;YI SYLLABLE RRE;Lo;0;L;;;;;N;;;;;
+A387;YI SYLLABLE RREP;Lo;0;L;;;;;N;;;;;
+A388;YI SYLLABLE RRUT;Lo;0;L;;;;;N;;;;;
+A389;YI SYLLABLE RRUX;Lo;0;L;;;;;N;;;;;
+A38A;YI SYLLABLE RRU;Lo;0;L;;;;;N;;;;;
+A38B;YI SYLLABLE RRUP;Lo;0;L;;;;;N;;;;;
+A38C;YI SYLLABLE RRURX;Lo;0;L;;;;;N;;;;;
+A38D;YI SYLLABLE RRUR;Lo;0;L;;;;;N;;;;;
+A38E;YI SYLLABLE RRYT;Lo;0;L;;;;;N;;;;;
+A38F;YI SYLLABLE RRYX;Lo;0;L;;;;;N;;;;;
+A390;YI SYLLABLE RRY;Lo;0;L;;;;;N;;;;;
+A391;YI SYLLABLE RRYP;Lo;0;L;;;;;N;;;;;
+A392;YI SYLLABLE RRYRX;Lo;0;L;;;;;N;;;;;
+A393;YI SYLLABLE RRYR;Lo;0;L;;;;;N;;;;;
+A394;YI SYLLABLE NRAT;Lo;0;L;;;;;N;;;;;
+A395;YI SYLLABLE NRAX;Lo;0;L;;;;;N;;;;;
+A396;YI SYLLABLE NRA;Lo;0;L;;;;;N;;;;;
+A397;YI SYLLABLE NRAP;Lo;0;L;;;;;N;;;;;
+A398;YI SYLLABLE NROX;Lo;0;L;;;;;N;;;;;
+A399;YI SYLLABLE NRO;Lo;0;L;;;;;N;;;;;
+A39A;YI SYLLABLE NROP;Lo;0;L;;;;;N;;;;;
+A39B;YI SYLLABLE NRET;Lo;0;L;;;;;N;;;;;
+A39C;YI SYLLABLE NREX;Lo;0;L;;;;;N;;;;;
+A39D;YI SYLLABLE NRE;Lo;0;L;;;;;N;;;;;
+A39E;YI SYLLABLE NREP;Lo;0;L;;;;;N;;;;;
+A39F;YI SYLLABLE NRUT;Lo;0;L;;;;;N;;;;;
+A3A0;YI SYLLABLE NRUX;Lo;0;L;;;;;N;;;;;
+A3A1;YI SYLLABLE NRU;Lo;0;L;;;;;N;;;;;
+A3A2;YI SYLLABLE NRUP;Lo;0;L;;;;;N;;;;;
+A3A3;YI SYLLABLE NRURX;Lo;0;L;;;;;N;;;;;
+A3A4;YI SYLLABLE NRUR;Lo;0;L;;;;;N;;;;;
+A3A5;YI SYLLABLE NRYT;Lo;0;L;;;;;N;;;;;
+A3A6;YI SYLLABLE NRYX;Lo;0;L;;;;;N;;;;;
+A3A7;YI SYLLABLE NRY;Lo;0;L;;;;;N;;;;;
+A3A8;YI SYLLABLE NRYP;Lo;0;L;;;;;N;;;;;
+A3A9;YI SYLLABLE NRYRX;Lo;0;L;;;;;N;;;;;
+A3AA;YI SYLLABLE NRYR;Lo;0;L;;;;;N;;;;;
+A3AB;YI SYLLABLE SHAT;Lo;0;L;;;;;N;;;;;
+A3AC;YI SYLLABLE SHAX;Lo;0;L;;;;;N;;;;;
+A3AD;YI SYLLABLE SHA;Lo;0;L;;;;;N;;;;;
+A3AE;YI SYLLABLE SHAP;Lo;0;L;;;;;N;;;;;
+A3AF;YI SYLLABLE SHUOX;Lo;0;L;;;;;N;;;;;
+A3B0;YI SYLLABLE SHUO;Lo;0;L;;;;;N;;;;;
+A3B1;YI SYLLABLE SHUOP;Lo;0;L;;;;;N;;;;;
+A3B2;YI SYLLABLE SHOT;Lo;0;L;;;;;N;;;;;
+A3B3;YI SYLLABLE SHOX;Lo;0;L;;;;;N;;;;;
+A3B4;YI SYLLABLE SHO;Lo;0;L;;;;;N;;;;;
+A3B5;YI SYLLABLE SHOP;Lo;0;L;;;;;N;;;;;
+A3B6;YI SYLLABLE SHET;Lo;0;L;;;;;N;;;;;
+A3B7;YI SYLLABLE SHEX;Lo;0;L;;;;;N;;;;;
+A3B8;YI SYLLABLE SHE;Lo;0;L;;;;;N;;;;;
+A3B9;YI SYLLABLE SHEP;Lo;0;L;;;;;N;;;;;
+A3BA;YI SYLLABLE SHUT;Lo;0;L;;;;;N;;;;;
+A3BB;YI SYLLABLE SHUX;Lo;0;L;;;;;N;;;;;
+A3BC;YI SYLLABLE SHU;Lo;0;L;;;;;N;;;;;
+A3BD;YI SYLLABLE SHUP;Lo;0;L;;;;;N;;;;;
+A3BE;YI SYLLABLE SHURX;Lo;0;L;;;;;N;;;;;
+A3BF;YI SYLLABLE SHUR;Lo;0;L;;;;;N;;;;;
+A3C0;YI SYLLABLE SHYT;Lo;0;L;;;;;N;;;;;
+A3C1;YI SYLLABLE SHYX;Lo;0;L;;;;;N;;;;;
+A3C2;YI SYLLABLE SHY;Lo;0;L;;;;;N;;;;;
+A3C3;YI SYLLABLE SHYP;Lo;0;L;;;;;N;;;;;
+A3C4;YI SYLLABLE SHYRX;Lo;0;L;;;;;N;;;;;
+A3C5;YI SYLLABLE SHYR;Lo;0;L;;;;;N;;;;;
+A3C6;YI SYLLABLE RAT;Lo;0;L;;;;;N;;;;;
+A3C7;YI SYLLABLE RAX;Lo;0;L;;;;;N;;;;;
+A3C8;YI SYLLABLE RA;Lo;0;L;;;;;N;;;;;
+A3C9;YI SYLLABLE RAP;Lo;0;L;;;;;N;;;;;
+A3CA;YI SYLLABLE RUOX;Lo;0;L;;;;;N;;;;;
+A3CB;YI SYLLABLE RUO;Lo;0;L;;;;;N;;;;;
+A3CC;YI SYLLABLE RUOP;Lo;0;L;;;;;N;;;;;
+A3CD;YI SYLLABLE ROT;Lo;0;L;;;;;N;;;;;
+A3CE;YI SYLLABLE ROX;Lo;0;L;;;;;N;;;;;
+A3CF;YI SYLLABLE RO;Lo;0;L;;;;;N;;;;;
+A3D0;YI SYLLABLE ROP;Lo;0;L;;;;;N;;;;;
+A3D1;YI SYLLABLE REX;Lo;0;L;;;;;N;;;;;
+A3D2;YI SYLLABLE RE;Lo;0;L;;;;;N;;;;;
+A3D3;YI SYLLABLE REP;Lo;0;L;;;;;N;;;;;
+A3D4;YI SYLLABLE RUT;Lo;0;L;;;;;N;;;;;
+A3D5;YI SYLLABLE RUX;Lo;0;L;;;;;N;;;;;
+A3D6;YI SYLLABLE RU;Lo;0;L;;;;;N;;;;;
+A3D7;YI SYLLABLE RUP;Lo;0;L;;;;;N;;;;;
+A3D8;YI SYLLABLE RURX;Lo;0;L;;;;;N;;;;;
+A3D9;YI SYLLABLE RUR;Lo;0;L;;;;;N;;;;;
+A3DA;YI SYLLABLE RYT;Lo;0;L;;;;;N;;;;;
+A3DB;YI SYLLABLE RYX;Lo;0;L;;;;;N;;;;;
+A3DC;YI SYLLABLE RY;Lo;0;L;;;;;N;;;;;
+A3DD;YI SYLLABLE RYP;Lo;0;L;;;;;N;;;;;
+A3DE;YI SYLLABLE RYRX;Lo;0;L;;;;;N;;;;;
+A3DF;YI SYLLABLE RYR;Lo;0;L;;;;;N;;;;;
+A3E0;YI SYLLABLE JIT;Lo;0;L;;;;;N;;;;;
+A3E1;YI SYLLABLE JIX;Lo;0;L;;;;;N;;;;;
+A3E2;YI SYLLABLE JI;Lo;0;L;;;;;N;;;;;
+A3E3;YI SYLLABLE JIP;Lo;0;L;;;;;N;;;;;
+A3E4;YI SYLLABLE JIET;Lo;0;L;;;;;N;;;;;
+A3E5;YI SYLLABLE JIEX;Lo;0;L;;;;;N;;;;;
+A3E6;YI SYLLABLE JIE;Lo;0;L;;;;;N;;;;;
+A3E7;YI SYLLABLE JIEP;Lo;0;L;;;;;N;;;;;
+A3E8;YI SYLLABLE JUOT;Lo;0;L;;;;;N;;;;;
+A3E9;YI SYLLABLE JUOX;Lo;0;L;;;;;N;;;;;
+A3EA;YI SYLLABLE JUO;Lo;0;L;;;;;N;;;;;
+A3EB;YI SYLLABLE JUOP;Lo;0;L;;;;;N;;;;;
+A3EC;YI SYLLABLE JOT;Lo;0;L;;;;;N;;;;;
+A3ED;YI SYLLABLE JOX;Lo;0;L;;;;;N;;;;;
+A3EE;YI SYLLABLE JO;Lo;0;L;;;;;N;;;;;
+A3EF;YI SYLLABLE JOP;Lo;0;L;;;;;N;;;;;
+A3F0;YI SYLLABLE JUT;Lo;0;L;;;;;N;;;;;
+A3F1;YI SYLLABLE JUX;Lo;0;L;;;;;N;;;;;
+A3F2;YI SYLLABLE JU;Lo;0;L;;;;;N;;;;;
+A3F3;YI SYLLABLE JUP;Lo;0;L;;;;;N;;;;;
+A3F4;YI SYLLABLE JURX;Lo;0;L;;;;;N;;;;;
+A3F5;YI SYLLABLE JUR;Lo;0;L;;;;;N;;;;;
+A3F6;YI SYLLABLE JYT;Lo;0;L;;;;;N;;;;;
+A3F7;YI SYLLABLE JYX;Lo;0;L;;;;;N;;;;;
+A3F8;YI SYLLABLE JY;Lo;0;L;;;;;N;;;;;
+A3F9;YI SYLLABLE JYP;Lo;0;L;;;;;N;;;;;
+A3FA;YI SYLLABLE JYRX;Lo;0;L;;;;;N;;;;;
+A3FB;YI SYLLABLE JYR;Lo;0;L;;;;;N;;;;;
+A3FC;YI SYLLABLE QIT;Lo;0;L;;;;;N;;;;;
+A3FD;YI SYLLABLE QIX;Lo;0;L;;;;;N;;;;;
+A3FE;YI SYLLABLE QI;Lo;0;L;;;;;N;;;;;
+A3FF;YI SYLLABLE QIP;Lo;0;L;;;;;N;;;;;
+A400;YI SYLLABLE QIET;Lo;0;L;;;;;N;;;;;
+A401;YI SYLLABLE QIEX;Lo;0;L;;;;;N;;;;;
+A402;YI SYLLABLE QIE;Lo;0;L;;;;;N;;;;;
+A403;YI SYLLABLE QIEP;Lo;0;L;;;;;N;;;;;
+A404;YI SYLLABLE QUOT;Lo;0;L;;;;;N;;;;;
+A405;YI SYLLABLE QUOX;Lo;0;L;;;;;N;;;;;
+A406;YI SYLLABLE QUO;Lo;0;L;;;;;N;;;;;
+A407;YI SYLLABLE QUOP;Lo;0;L;;;;;N;;;;;
+A408;YI SYLLABLE QOT;Lo;0;L;;;;;N;;;;;
+A409;YI SYLLABLE QOX;Lo;0;L;;;;;N;;;;;
+A40A;YI SYLLABLE QO;Lo;0;L;;;;;N;;;;;
+A40B;YI SYLLABLE QOP;Lo;0;L;;;;;N;;;;;
+A40C;YI SYLLABLE QUT;Lo;0;L;;;;;N;;;;;
+A40D;YI SYLLABLE QUX;Lo;0;L;;;;;N;;;;;
+A40E;YI SYLLABLE QU;Lo;0;L;;;;;N;;;;;
+A40F;YI SYLLABLE QUP;Lo;0;L;;;;;N;;;;;
+A410;YI SYLLABLE QURX;Lo;0;L;;;;;N;;;;;
+A411;YI SYLLABLE QUR;Lo;0;L;;;;;N;;;;;
+A412;YI SYLLABLE QYT;Lo;0;L;;;;;N;;;;;
+A413;YI SYLLABLE QYX;Lo;0;L;;;;;N;;;;;
+A414;YI SYLLABLE QY;Lo;0;L;;;;;N;;;;;
+A415;YI SYLLABLE QYP;Lo;0;L;;;;;N;;;;;
+A416;YI SYLLABLE QYRX;Lo;0;L;;;;;N;;;;;
+A417;YI SYLLABLE QYR;Lo;0;L;;;;;N;;;;;
+A418;YI SYLLABLE JJIT;Lo;0;L;;;;;N;;;;;
+A419;YI SYLLABLE JJIX;Lo;0;L;;;;;N;;;;;
+A41A;YI SYLLABLE JJI;Lo;0;L;;;;;N;;;;;
+A41B;YI SYLLABLE JJIP;Lo;0;L;;;;;N;;;;;
+A41C;YI SYLLABLE JJIET;Lo;0;L;;;;;N;;;;;
+A41D;YI SYLLABLE JJIEX;Lo;0;L;;;;;N;;;;;
+A41E;YI SYLLABLE JJIE;Lo;0;L;;;;;N;;;;;
+A41F;YI SYLLABLE JJIEP;Lo;0;L;;;;;N;;;;;
+A420;YI SYLLABLE JJUOX;Lo;0;L;;;;;N;;;;;
+A421;YI SYLLABLE JJUO;Lo;0;L;;;;;N;;;;;
+A422;YI SYLLABLE JJUOP;Lo;0;L;;;;;N;;;;;
+A423;YI SYLLABLE JJOT;Lo;0;L;;;;;N;;;;;
+A424;YI SYLLABLE JJOX;Lo;0;L;;;;;N;;;;;
+A425;YI SYLLABLE JJO;Lo;0;L;;;;;N;;;;;
+A426;YI SYLLABLE JJOP;Lo;0;L;;;;;N;;;;;
+A427;YI SYLLABLE JJUT;Lo;0;L;;;;;N;;;;;
+A428;YI SYLLABLE JJUX;Lo;0;L;;;;;N;;;;;
+A429;YI SYLLABLE JJU;Lo;0;L;;;;;N;;;;;
+A42A;YI SYLLABLE JJUP;Lo;0;L;;;;;N;;;;;
+A42B;YI SYLLABLE JJURX;Lo;0;L;;;;;N;;;;;
+A42C;YI SYLLABLE JJUR;Lo;0;L;;;;;N;;;;;
+A42D;YI SYLLABLE JJYT;Lo;0;L;;;;;N;;;;;
+A42E;YI SYLLABLE JJYX;Lo;0;L;;;;;N;;;;;
+A42F;YI SYLLABLE JJY;Lo;0;L;;;;;N;;;;;
+A430;YI SYLLABLE JJYP;Lo;0;L;;;;;N;;;;;
+A431;YI SYLLABLE NJIT;Lo;0;L;;;;;N;;;;;
+A432;YI SYLLABLE NJIX;Lo;0;L;;;;;N;;;;;
+A433;YI SYLLABLE NJI;Lo;0;L;;;;;N;;;;;
+A434;YI SYLLABLE NJIP;Lo;0;L;;;;;N;;;;;
+A435;YI SYLLABLE NJIET;Lo;0;L;;;;;N;;;;;
+A436;YI SYLLABLE NJIEX;Lo;0;L;;;;;N;;;;;
+A437;YI SYLLABLE NJIE;Lo;0;L;;;;;N;;;;;
+A438;YI SYLLABLE NJIEP;Lo;0;L;;;;;N;;;;;
+A439;YI SYLLABLE NJUOX;Lo;0;L;;;;;N;;;;;
+A43A;YI SYLLABLE NJUO;Lo;0;L;;;;;N;;;;;
+A43B;YI SYLLABLE NJOT;Lo;0;L;;;;;N;;;;;
+A43C;YI SYLLABLE NJOX;Lo;0;L;;;;;N;;;;;
+A43D;YI SYLLABLE NJO;Lo;0;L;;;;;N;;;;;
+A43E;YI SYLLABLE NJOP;Lo;0;L;;;;;N;;;;;
+A43F;YI SYLLABLE NJUX;Lo;0;L;;;;;N;;;;;
+A440;YI SYLLABLE NJU;Lo;0;L;;;;;N;;;;;
+A441;YI SYLLABLE NJUP;Lo;0;L;;;;;N;;;;;
+A442;YI SYLLABLE NJURX;Lo;0;L;;;;;N;;;;;
+A443;YI SYLLABLE NJUR;Lo;0;L;;;;;N;;;;;
+A444;YI SYLLABLE NJYT;Lo;0;L;;;;;N;;;;;
+A445;YI SYLLABLE NJYX;Lo;0;L;;;;;N;;;;;
+A446;YI SYLLABLE NJY;Lo;0;L;;;;;N;;;;;
+A447;YI SYLLABLE NJYP;Lo;0;L;;;;;N;;;;;
+A448;YI SYLLABLE NJYRX;Lo;0;L;;;;;N;;;;;
+A449;YI SYLLABLE NJYR;Lo;0;L;;;;;N;;;;;
+A44A;YI SYLLABLE NYIT;Lo;0;L;;;;;N;;;;;
+A44B;YI SYLLABLE NYIX;Lo;0;L;;;;;N;;;;;
+A44C;YI SYLLABLE NYI;Lo;0;L;;;;;N;;;;;
+A44D;YI SYLLABLE NYIP;Lo;0;L;;;;;N;;;;;
+A44E;YI SYLLABLE NYIET;Lo;0;L;;;;;N;;;;;
+A44F;YI SYLLABLE NYIEX;Lo;0;L;;;;;N;;;;;
+A450;YI SYLLABLE NYIE;Lo;0;L;;;;;N;;;;;
+A451;YI SYLLABLE NYIEP;Lo;0;L;;;;;N;;;;;
+A452;YI SYLLABLE NYUOX;Lo;0;L;;;;;N;;;;;
+A453;YI SYLLABLE NYUO;Lo;0;L;;;;;N;;;;;
+A454;YI SYLLABLE NYUOP;Lo;0;L;;;;;N;;;;;
+A455;YI SYLLABLE NYOT;Lo;0;L;;;;;N;;;;;
+A456;YI SYLLABLE NYOX;Lo;0;L;;;;;N;;;;;
+A457;YI SYLLABLE NYO;Lo;0;L;;;;;N;;;;;
+A458;YI SYLLABLE NYOP;Lo;0;L;;;;;N;;;;;
+A459;YI SYLLABLE NYUT;Lo;0;L;;;;;N;;;;;
+A45A;YI SYLLABLE NYUX;Lo;0;L;;;;;N;;;;;
+A45B;YI SYLLABLE NYU;Lo;0;L;;;;;N;;;;;
+A45C;YI SYLLABLE NYUP;Lo;0;L;;;;;N;;;;;
+A45D;YI SYLLABLE XIT;Lo;0;L;;;;;N;;;;;
+A45E;YI SYLLABLE XIX;Lo;0;L;;;;;N;;;;;
+A45F;YI SYLLABLE XI;Lo;0;L;;;;;N;;;;;
+A460;YI SYLLABLE XIP;Lo;0;L;;;;;N;;;;;
+A461;YI SYLLABLE XIET;Lo;0;L;;;;;N;;;;;
+A462;YI SYLLABLE XIEX;Lo;0;L;;;;;N;;;;;
+A463;YI SYLLABLE XIE;Lo;0;L;;;;;N;;;;;
+A464;YI SYLLABLE XIEP;Lo;0;L;;;;;N;;;;;
+A465;YI SYLLABLE XUOX;Lo;0;L;;;;;N;;;;;
+A466;YI SYLLABLE XUO;Lo;0;L;;;;;N;;;;;
+A467;YI SYLLABLE XOT;Lo;0;L;;;;;N;;;;;
+A468;YI SYLLABLE XOX;Lo;0;L;;;;;N;;;;;
+A469;YI SYLLABLE XO;Lo;0;L;;;;;N;;;;;
+A46A;YI SYLLABLE XOP;Lo;0;L;;;;;N;;;;;
+A46B;YI SYLLABLE XYT;Lo;0;L;;;;;N;;;;;
+A46C;YI SYLLABLE XYX;Lo;0;L;;;;;N;;;;;
+A46D;YI SYLLABLE XY;Lo;0;L;;;;;N;;;;;
+A46E;YI SYLLABLE XYP;Lo;0;L;;;;;N;;;;;
+A46F;YI SYLLABLE XYRX;Lo;0;L;;;;;N;;;;;
+A470;YI SYLLABLE XYR;Lo;0;L;;;;;N;;;;;
+A471;YI SYLLABLE YIT;Lo;0;L;;;;;N;;;;;
+A472;YI SYLLABLE YIX;Lo;0;L;;;;;N;;;;;
+A473;YI SYLLABLE YI;Lo;0;L;;;;;N;;;;;
+A474;YI SYLLABLE YIP;Lo;0;L;;;;;N;;;;;
+A475;YI SYLLABLE YIET;Lo;0;L;;;;;N;;;;;
+A476;YI SYLLABLE YIEX;Lo;0;L;;;;;N;;;;;
+A477;YI SYLLABLE YIE;Lo;0;L;;;;;N;;;;;
+A478;YI SYLLABLE YIEP;Lo;0;L;;;;;N;;;;;
+A479;YI SYLLABLE YUOT;Lo;0;L;;;;;N;;;;;
+A47A;YI SYLLABLE YUOX;Lo;0;L;;;;;N;;;;;
+A47B;YI SYLLABLE YUO;Lo;0;L;;;;;N;;;;;
+A47C;YI SYLLABLE YUOP;Lo;0;L;;;;;N;;;;;
+A47D;YI SYLLABLE YOT;Lo;0;L;;;;;N;;;;;
+A47E;YI SYLLABLE YOX;Lo;0;L;;;;;N;;;;;
+A47F;YI SYLLABLE YO;Lo;0;L;;;;;N;;;;;
+A480;YI SYLLABLE YOP;Lo;0;L;;;;;N;;;;;
+A481;YI SYLLABLE YUT;Lo;0;L;;;;;N;;;;;
+A482;YI SYLLABLE YUX;Lo;0;L;;;;;N;;;;;
+A483;YI SYLLABLE YU;Lo;0;L;;;;;N;;;;;
+A484;YI SYLLABLE YUP;Lo;0;L;;;;;N;;;;;
+A485;YI SYLLABLE YURX;Lo;0;L;;;;;N;;;;;
+A486;YI SYLLABLE YUR;Lo;0;L;;;;;N;;;;;
+A487;YI SYLLABLE YYT;Lo;0;L;;;;;N;;;;;
+A488;YI SYLLABLE YYX;Lo;0;L;;;;;N;;;;;
+A489;YI SYLLABLE YY;Lo;0;L;;;;;N;;;;;
+A48A;YI SYLLABLE YYP;Lo;0;L;;;;;N;;;;;
+A48B;YI SYLLABLE YYRX;Lo;0;L;;;;;N;;;;;
+A48C;YI SYLLABLE YYR;Lo;0;L;;;;;N;;;;;
+A490;YI RADICAL QOT;So;0;ON;;;;;N;;;;;
+A491;YI RADICAL LI;So;0;ON;;;;;N;;;;;
+A492;YI RADICAL KIT;So;0;ON;;;;;N;;;;;
+A493;YI RADICAL NYIP;So;0;ON;;;;;N;;;;;
+A494;YI RADICAL CYP;So;0;ON;;;;;N;;;;;
+A495;YI RADICAL SSI;So;0;ON;;;;;N;;;;;
+A496;YI RADICAL GGOP;So;0;ON;;;;;N;;;;;
+A497;YI RADICAL GEP;So;0;ON;;;;;N;;;;;
+A498;YI RADICAL MI;So;0;ON;;;;;N;;;;;
+A499;YI RADICAL HXIT;So;0;ON;;;;;N;;;;;
+A49A;YI RADICAL LYR;So;0;ON;;;;;N;;;;;
+A49B;YI RADICAL BBUT;So;0;ON;;;;;N;;;;;
+A49C;YI RADICAL MOP;So;0;ON;;;;;N;;;;;
+A49D;YI RADICAL YO;So;0;ON;;;;;N;;;;;
+A49E;YI RADICAL PUT;So;0;ON;;;;;N;;;;;
+A49F;YI RADICAL HXUO;So;0;ON;;;;;N;;;;;
+A4A0;YI RADICAL TAT;So;0;ON;;;;;N;;;;;
+A4A1;YI RADICAL GA;So;0;ON;;;;;N;;;;;
+A4A2;YI RADICAL ZUP;So;0;ON;;;;;N;;;;;
+A4A3;YI RADICAL CYT;So;0;ON;;;;;N;;;;;
+A4A4;YI RADICAL DDUR;So;0;ON;;;;;N;;;;;
+A4A5;YI RADICAL BUR;So;0;ON;;;;;N;;;;;
+A4A6;YI RADICAL GGUO;So;0;ON;;;;;N;;;;;
+A4A7;YI RADICAL NYOP;So;0;ON;;;;;N;;;;;
+A4A8;YI RADICAL TU;So;0;ON;;;;;N;;;;;
+A4A9;YI RADICAL OP;So;0;ON;;;;;N;;;;;
+A4AA;YI RADICAL JJUT;So;0;ON;;;;;N;;;;;
+A4AB;YI RADICAL ZOT;So;0;ON;;;;;N;;;;;
+A4AC;YI RADICAL PYT;So;0;ON;;;;;N;;;;;
+A4AD;YI RADICAL HMO;So;0;ON;;;;;N;;;;;
+A4AE;YI RADICAL YIT;So;0;ON;;;;;N;;;;;
+A4AF;YI RADICAL VUR;So;0;ON;;;;;N;;;;;
+A4B0;YI RADICAL SHY;So;0;ON;;;;;N;;;;;
+A4B1;YI RADICAL VEP;So;0;ON;;;;;N;;;;;
+A4B2;YI RADICAL ZA;So;0;ON;;;;;N;;;;;
+A4B3;YI RADICAL JO;So;0;ON;;;;;N;;;;;
+A4B4;YI RADICAL NZUP;So;0;ON;;;;;N;;;;;
+A4B5;YI RADICAL JJY;So;0;ON;;;;;N;;;;;
+A4B6;YI RADICAL GOT;So;0;ON;;;;;N;;;;;
+A4B7;YI RADICAL JJIE;So;0;ON;;;;;N;;;;;
+A4B8;YI RADICAL WO;So;0;ON;;;;;N;;;;;
+A4B9;YI RADICAL DU;So;0;ON;;;;;N;;;;;
+A4BA;YI RADICAL SHUR;So;0;ON;;;;;N;;;;;
+A4BB;YI RADICAL LIE;So;0;ON;;;;;N;;;;;
+A4BC;YI RADICAL CY;So;0;ON;;;;;N;;;;;
+A4BD;YI RADICAL CUOP;So;0;ON;;;;;N;;;;;
+A4BE;YI RADICAL CIP;So;0;ON;;;;;N;;;;;
+A4BF;YI RADICAL HXOP;So;0;ON;;;;;N;;;;;
+A4C0;YI RADICAL SHAT;So;0;ON;;;;;N;;;;;
+A4C1;YI RADICAL ZUR;So;0;ON;;;;;N;;;;;
+A4C2;YI RADICAL SHOP;So;0;ON;;;;;N;;;;;
+A4C3;YI RADICAL CHE;So;0;ON;;;;;N;;;;;
+A4C4;YI RADICAL ZZIET;So;0;ON;;;;;N;;;;;
+A4C5;YI RADICAL NBIE;So;0;ON;;;;;N;;;;;
+A4C6;YI RADICAL KE;So;0;ON;;;;;N;;;;;
+AC00;<Hangul Syllable, First>;Lo;0;L;;;;;N;;;;;
+D7A3;<Hangul Syllable, Last>;Lo;0;L;;;;;N;;;;;
+D800;<Non Private Use High Surrogate, First>;Cs;0;L;;;;;N;;;;;
+DB7F;<Non Private Use High Surrogate, Last>;Cs;0;L;;;;;N;;;;;
+DB80;<Private Use High Surrogate, First>;Cs;0;L;;;;;N;;;;;
+DBFF;<Private Use High Surrogate, Last>;Cs;0;L;;;;;N;;;;;
+DC00;<Low Surrogate, First>;Cs;0;L;;;;;N;;;;;
+DFFF;<Low Surrogate, Last>;Cs;0;L;;;;;N;;;;;
+E000;<Private Use, First>;Co;0;L;;;;;N;;;;;
+F8FF;<Private Use, Last>;Co;0;L;;;;;N;;;;;
+F900;CJK COMPATIBILITY IDEOGRAPH-F900;Lo;0;L;8C48;;;;N;;;;;
+F901;CJK COMPATIBILITY IDEOGRAPH-F901;Lo;0;L;66F4;;;;N;;;;;
+F902;CJK COMPATIBILITY IDEOGRAPH-F902;Lo;0;L;8ECA;;;;N;;;;;
+F903;CJK COMPATIBILITY IDEOGRAPH-F903;Lo;0;L;8CC8;;;;N;;;;;
+F904;CJK COMPATIBILITY IDEOGRAPH-F904;Lo;0;L;6ED1;;;;N;;;;;
+F905;CJK COMPATIBILITY IDEOGRAPH-F905;Lo;0;L;4E32;;;;N;;;;;
+F906;CJK COMPATIBILITY IDEOGRAPH-F906;Lo;0;L;53E5;;;;N;;;;;
+F907;CJK COMPATIBILITY IDEOGRAPH-F907;Lo;0;L;9F9C;;;;N;;;;;
+F908;CJK COMPATIBILITY IDEOGRAPH-F908;Lo;0;L;9F9C;;;;N;;;;;
+F909;CJK COMPATIBILITY IDEOGRAPH-F909;Lo;0;L;5951;;;;N;;;;;
+F90A;CJK COMPATIBILITY IDEOGRAPH-F90A;Lo;0;L;91D1;;;;N;;;;;
+F90B;CJK COMPATIBILITY IDEOGRAPH-F90B;Lo;0;L;5587;;;;N;;;;;
+F90C;CJK COMPATIBILITY IDEOGRAPH-F90C;Lo;0;L;5948;;;;N;;;;;
+F90D;CJK COMPATIBILITY IDEOGRAPH-F90D;Lo;0;L;61F6;;;;N;;;;;
+F90E;CJK COMPATIBILITY IDEOGRAPH-F90E;Lo;0;L;7669;;;;N;;;;;
+F90F;CJK COMPATIBILITY IDEOGRAPH-F90F;Lo;0;L;7F85;;;;N;;;;;
+F910;CJK COMPATIBILITY IDEOGRAPH-F910;Lo;0;L;863F;;;;N;;;;;
+F911;CJK COMPATIBILITY IDEOGRAPH-F911;Lo;0;L;87BA;;;;N;;;;;
+F912;CJK COMPATIBILITY IDEOGRAPH-F912;Lo;0;L;88F8;;;;N;;;;;
+F913;CJK COMPATIBILITY IDEOGRAPH-F913;Lo;0;L;908F;;;;N;;;;;
+F914;CJK COMPATIBILITY IDEOGRAPH-F914;Lo;0;L;6A02;;;;N;;;;;
+F915;CJK COMPATIBILITY IDEOGRAPH-F915;Lo;0;L;6D1B;;;;N;;;;;
+F916;CJK COMPATIBILITY IDEOGRAPH-F916;Lo;0;L;70D9;;;;N;;;;;
+F917;CJK COMPATIBILITY IDEOGRAPH-F917;Lo;0;L;73DE;;;;N;;;;;
+F918;CJK COMPATIBILITY IDEOGRAPH-F918;Lo;0;L;843D;;;;N;;;;;
+F919;CJK COMPATIBILITY IDEOGRAPH-F919;Lo;0;L;916A;;;;N;;;;;
+F91A;CJK COMPATIBILITY IDEOGRAPH-F91A;Lo;0;L;99F1;;;;N;;;;;
+F91B;CJK COMPATIBILITY IDEOGRAPH-F91B;Lo;0;L;4E82;;;;N;;;;;
+F91C;CJK COMPATIBILITY IDEOGRAPH-F91C;Lo;0;L;5375;;;;N;;;;;
+F91D;CJK COMPATIBILITY IDEOGRAPH-F91D;Lo;0;L;6B04;;;;N;;;;;
+F91E;CJK COMPATIBILITY IDEOGRAPH-F91E;Lo;0;L;721B;;;;N;;;;;
+F91F;CJK COMPATIBILITY IDEOGRAPH-F91F;Lo;0;L;862D;;;;N;;;;;
+F920;CJK COMPATIBILITY IDEOGRAPH-F920;Lo;0;L;9E1E;;;;N;;;;;
+F921;CJK COMPATIBILITY IDEOGRAPH-F921;Lo;0;L;5D50;;;;N;;;;;
+F922;CJK COMPATIBILITY IDEOGRAPH-F922;Lo;0;L;6FEB;;;;N;;;;;
+F923;CJK COMPATIBILITY IDEOGRAPH-F923;Lo;0;L;85CD;;;;N;;;;;
+F924;CJK COMPATIBILITY IDEOGRAPH-F924;Lo;0;L;8964;;;;N;;;;;
+F925;CJK COMPATIBILITY IDEOGRAPH-F925;Lo;0;L;62C9;;;;N;;;;;
+F926;CJK COMPATIBILITY IDEOGRAPH-F926;Lo;0;L;81D8;;;;N;;;;;
+F927;CJK COMPATIBILITY IDEOGRAPH-F927;Lo;0;L;881F;;;;N;;;;;
+F928;CJK COMPATIBILITY IDEOGRAPH-F928;Lo;0;L;5ECA;;;;N;;;;;
+F929;CJK COMPATIBILITY IDEOGRAPH-F929;Lo;0;L;6717;;;;N;;;;;
+F92A;CJK COMPATIBILITY IDEOGRAPH-F92A;Lo;0;L;6D6A;;;;N;;;;;
+F92B;CJK COMPATIBILITY IDEOGRAPH-F92B;Lo;0;L;72FC;;;;N;;;;;
+F92C;CJK COMPATIBILITY IDEOGRAPH-F92C;Lo;0;L;90CE;;;;N;;;;;
+F92D;CJK COMPATIBILITY IDEOGRAPH-F92D;Lo;0;L;4F86;;;;N;;;;;
+F92E;CJK COMPATIBILITY IDEOGRAPH-F92E;Lo;0;L;51B7;;;;N;;;;;
+F92F;CJK COMPATIBILITY IDEOGRAPH-F92F;Lo;0;L;52DE;;;;N;;;;;
+F930;CJK COMPATIBILITY IDEOGRAPH-F930;Lo;0;L;64C4;;;;N;;;;;
+F931;CJK COMPATIBILITY IDEOGRAPH-F931;Lo;0;L;6AD3;;;;N;;;;;
+F932;CJK COMPATIBILITY IDEOGRAPH-F932;Lo;0;L;7210;;;;N;;;;;
+F933;CJK COMPATIBILITY IDEOGRAPH-F933;Lo;0;L;76E7;;;;N;;;;;
+F934;CJK COMPATIBILITY IDEOGRAPH-F934;Lo;0;L;8001;;;;N;;;;;
+F935;CJK COMPATIBILITY IDEOGRAPH-F935;Lo;0;L;8606;;;;N;;;;;
+F936;CJK COMPATIBILITY IDEOGRAPH-F936;Lo;0;L;865C;;;;N;;;;;
+F937;CJK COMPATIBILITY IDEOGRAPH-F937;Lo;0;L;8DEF;;;;N;;;;;
+F938;CJK COMPATIBILITY IDEOGRAPH-F938;Lo;0;L;9732;;;;N;;;;;
+F939;CJK COMPATIBILITY IDEOGRAPH-F939;Lo;0;L;9B6F;;;;N;;;;;
+F93A;CJK COMPATIBILITY IDEOGRAPH-F93A;Lo;0;L;9DFA;;;;N;;;;;
+F93B;CJK COMPATIBILITY IDEOGRAPH-F93B;Lo;0;L;788C;;;;N;;;;;
+F93C;CJK COMPATIBILITY IDEOGRAPH-F93C;Lo;0;L;797F;;;;N;;;;;
+F93D;CJK COMPATIBILITY IDEOGRAPH-F93D;Lo;0;L;7DA0;;;;N;;;;;
+F93E;CJK COMPATIBILITY IDEOGRAPH-F93E;Lo;0;L;83C9;;;;N;;;;;
+F93F;CJK COMPATIBILITY IDEOGRAPH-F93F;Lo;0;L;9304;;;;N;;;;;
+F940;CJK COMPATIBILITY IDEOGRAPH-F940;Lo;0;L;9E7F;;;;N;;;;;
+F941;CJK COMPATIBILITY IDEOGRAPH-F941;Lo;0;L;8AD6;;;;N;;;;;
+F942;CJK COMPATIBILITY IDEOGRAPH-F942;Lo;0;L;58DF;;;;N;;;;;
+F943;CJK COMPATIBILITY IDEOGRAPH-F943;Lo;0;L;5F04;;;;N;;;;;
+F944;CJK COMPATIBILITY IDEOGRAPH-F944;Lo;0;L;7C60;;;;N;;;;;
+F945;CJK COMPATIBILITY IDEOGRAPH-F945;Lo;0;L;807E;;;;N;;;;;
+F946;CJK COMPATIBILITY IDEOGRAPH-F946;Lo;0;L;7262;;;;N;;;;;
+F947;CJK COMPATIBILITY IDEOGRAPH-F947;Lo;0;L;78CA;;;;N;;;;;
+F948;CJK COMPATIBILITY IDEOGRAPH-F948;Lo;0;L;8CC2;;;;N;;;;;
+F949;CJK COMPATIBILITY IDEOGRAPH-F949;Lo;0;L;96F7;;;;N;;;;;
+F94A;CJK COMPATIBILITY IDEOGRAPH-F94A;Lo;0;L;58D8;;;;N;;;;;
+F94B;CJK COMPATIBILITY IDEOGRAPH-F94B;Lo;0;L;5C62;;;;N;;;;;
+F94C;CJK COMPATIBILITY IDEOGRAPH-F94C;Lo;0;L;6A13;;;;N;;;;;
+F94D;CJK COMPATIBILITY IDEOGRAPH-F94D;Lo;0;L;6DDA;;;;N;;;;;
+F94E;CJK COMPATIBILITY IDEOGRAPH-F94E;Lo;0;L;6F0F;;;;N;;;;;
+F94F;CJK COMPATIBILITY IDEOGRAPH-F94F;Lo;0;L;7D2F;;;;N;;;;;
+F950;CJK COMPATIBILITY IDEOGRAPH-F950;Lo;0;L;7E37;;;;N;;;;;
+F951;CJK COMPATIBILITY IDEOGRAPH-F951;Lo;0;L;964B;;;;N;;;;;
+F952;CJK COMPATIBILITY IDEOGRAPH-F952;Lo;0;L;52D2;;;;N;;;;;
+F953;CJK COMPATIBILITY IDEOGRAPH-F953;Lo;0;L;808B;;;;N;;;;;
+F954;CJK COMPATIBILITY IDEOGRAPH-F954;Lo;0;L;51DC;;;;N;;;;;
+F955;CJK COMPATIBILITY IDEOGRAPH-F955;Lo;0;L;51CC;;;;N;;;;;
+F956;CJK COMPATIBILITY IDEOGRAPH-F956;Lo;0;L;7A1C;;;;N;;;;;
+F957;CJK COMPATIBILITY IDEOGRAPH-F957;Lo;0;L;7DBE;;;;N;;;;;
+F958;CJK COMPATIBILITY IDEOGRAPH-F958;Lo;0;L;83F1;;;;N;;;;;
+F959;CJK COMPATIBILITY IDEOGRAPH-F959;Lo;0;L;9675;;;;N;;;;;
+F95A;CJK COMPATIBILITY IDEOGRAPH-F95A;Lo;0;L;8B80;;;;N;;;;;
+F95B;CJK COMPATIBILITY IDEOGRAPH-F95B;Lo;0;L;62CF;;;;N;;;;;
+F95C;CJK COMPATIBILITY IDEOGRAPH-F95C;Lo;0;L;6A02;;;;N;;;;;
+F95D;CJK COMPATIBILITY IDEOGRAPH-F95D;Lo;0;L;8AFE;;;;N;;;;;
+F95E;CJK COMPATIBILITY IDEOGRAPH-F95E;Lo;0;L;4E39;;;;N;;;;;
+F95F;CJK COMPATIBILITY IDEOGRAPH-F95F;Lo;0;L;5BE7;;;;N;;;;;
+F960;CJK COMPATIBILITY IDEOGRAPH-F960;Lo;0;L;6012;;;;N;;;;;
+F961;CJK COMPATIBILITY IDEOGRAPH-F961;Lo;0;L;7387;;;;N;;;;;
+F962;CJK COMPATIBILITY IDEOGRAPH-F962;Lo;0;L;7570;;;;N;;;;;
+F963;CJK COMPATIBILITY IDEOGRAPH-F963;Lo;0;L;5317;;;;N;;;;;
+F964;CJK COMPATIBILITY IDEOGRAPH-F964;Lo;0;L;78FB;;;;N;;;;;
+F965;CJK COMPATIBILITY IDEOGRAPH-F965;Lo;0;L;4FBF;;;;N;;;;;
+F966;CJK COMPATIBILITY IDEOGRAPH-F966;Lo;0;L;5FA9;;;;N;;;;;
+F967;CJK COMPATIBILITY IDEOGRAPH-F967;Lo;0;L;4E0D;;;;N;;;;;
+F968;CJK COMPATIBILITY IDEOGRAPH-F968;Lo;0;L;6CCC;;;;N;;;;;
+F969;CJK COMPATIBILITY IDEOGRAPH-F969;Lo;0;L;6578;;;;N;;;;;
+F96A;CJK COMPATIBILITY IDEOGRAPH-F96A;Lo;0;L;7D22;;;;N;;;;;
+F96B;CJK COMPATIBILITY IDEOGRAPH-F96B;Lo;0;L;53C3;;;;N;;;;;
+F96C;CJK COMPATIBILITY IDEOGRAPH-F96C;Lo;0;L;585E;;;;N;;;;;
+F96D;CJK COMPATIBILITY IDEOGRAPH-F96D;Lo;0;L;7701;;;;N;;;;;
+F96E;CJK COMPATIBILITY IDEOGRAPH-F96E;Lo;0;L;8449;;;;N;;;;;
+F96F;CJK COMPATIBILITY IDEOGRAPH-F96F;Lo;0;L;8AAA;;;;N;;;;;
+F970;CJK COMPATIBILITY IDEOGRAPH-F970;Lo;0;L;6BBA;;;;N;;;;;
+F971;CJK COMPATIBILITY IDEOGRAPH-F971;Lo;0;L;8FB0;;;;N;;;;;
+F972;CJK COMPATIBILITY IDEOGRAPH-F972;Lo;0;L;6C88;;;;N;;;;;
+F973;CJK COMPATIBILITY IDEOGRAPH-F973;Lo;0;L;62FE;;;;N;;;;;
+F974;CJK COMPATIBILITY IDEOGRAPH-F974;Lo;0;L;82E5;;;;N;;;;;
+F975;CJK COMPATIBILITY IDEOGRAPH-F975;Lo;0;L;63A0;;;;N;;;;;
+F976;CJK COMPATIBILITY IDEOGRAPH-F976;Lo;0;L;7565;;;;N;;;;;
+F977;CJK COMPATIBILITY IDEOGRAPH-F977;Lo;0;L;4EAE;;;;N;;;;;
+F978;CJK COMPATIBILITY IDEOGRAPH-F978;Lo;0;L;5169;;;;N;;;;;
+F979;CJK COMPATIBILITY IDEOGRAPH-F979;Lo;0;L;51C9;;;;N;;;;;
+F97A;CJK COMPATIBILITY IDEOGRAPH-F97A;Lo;0;L;6881;;;;N;;;;;
+F97B;CJK COMPATIBILITY IDEOGRAPH-F97B;Lo;0;L;7CE7;;;;N;;;;;
+F97C;CJK COMPATIBILITY IDEOGRAPH-F97C;Lo;0;L;826F;;;;N;;;;;
+F97D;CJK COMPATIBILITY IDEOGRAPH-F97D;Lo;0;L;8AD2;;;;N;;;;;
+F97E;CJK COMPATIBILITY IDEOGRAPH-F97E;Lo;0;L;91CF;;;;N;;;;;
+F97F;CJK COMPATIBILITY IDEOGRAPH-F97F;Lo;0;L;52F5;;;;N;;;;;
+F980;CJK COMPATIBILITY IDEOGRAPH-F980;Lo;0;L;5442;;;;N;;;;;
+F981;CJK COMPATIBILITY IDEOGRAPH-F981;Lo;0;L;5973;;;;N;;;;;
+F982;CJK COMPATIBILITY IDEOGRAPH-F982;Lo;0;L;5EEC;;;;N;;;;;
+F983;CJK COMPATIBILITY IDEOGRAPH-F983;Lo;0;L;65C5;;;;N;;;;;
+F984;CJK COMPATIBILITY IDEOGRAPH-F984;Lo;0;L;6FFE;;;;N;;;;;
+F985;CJK COMPATIBILITY IDEOGRAPH-F985;Lo;0;L;792A;;;;N;;;;;
+F986;CJK COMPATIBILITY IDEOGRAPH-F986;Lo;0;L;95AD;;;;N;;;;;
+F987;CJK COMPATIBILITY IDEOGRAPH-F987;Lo;0;L;9A6A;;;;N;;;;;
+F988;CJK COMPATIBILITY IDEOGRAPH-F988;Lo;0;L;9E97;;;;N;;;;;
+F989;CJK COMPATIBILITY IDEOGRAPH-F989;Lo;0;L;9ECE;;;;N;;;;;
+F98A;CJK COMPATIBILITY IDEOGRAPH-F98A;Lo;0;L;529B;;;;N;;;;;
+F98B;CJK COMPATIBILITY IDEOGRAPH-F98B;Lo;0;L;66C6;;;;N;;;;;
+F98C;CJK COMPATIBILITY IDEOGRAPH-F98C;Lo;0;L;6B77;;;;N;;;;;
+F98D;CJK COMPATIBILITY IDEOGRAPH-F98D;Lo;0;L;8F62;;;;N;;;;;
+F98E;CJK COMPATIBILITY IDEOGRAPH-F98E;Lo;0;L;5E74;;;;N;;;;;
+F98F;CJK COMPATIBILITY IDEOGRAPH-F98F;Lo;0;L;6190;;;;N;;;;;
+F990;CJK COMPATIBILITY IDEOGRAPH-F990;Lo;0;L;6200;;;;N;;;;;
+F991;CJK COMPATIBILITY IDEOGRAPH-F991;Lo;0;L;649A;;;;N;;;;;
+F992;CJK COMPATIBILITY IDEOGRAPH-F992;Lo;0;L;6F23;;;;N;;;;;
+F993;CJK COMPATIBILITY IDEOGRAPH-F993;Lo;0;L;7149;;;;N;;;;;
+F994;CJK COMPATIBILITY IDEOGRAPH-F994;Lo;0;L;7489;;;;N;;;;;
+F995;CJK COMPATIBILITY IDEOGRAPH-F995;Lo;0;L;79CA;;;;N;;;;;
+F996;CJK COMPATIBILITY IDEOGRAPH-F996;Lo;0;L;7DF4;;;;N;;;;;
+F997;CJK COMPATIBILITY IDEOGRAPH-F997;Lo;0;L;806F;;;;N;;;;;
+F998;CJK COMPATIBILITY IDEOGRAPH-F998;Lo;0;L;8F26;;;;N;;;;;
+F999;CJK COMPATIBILITY IDEOGRAPH-F999;Lo;0;L;84EE;;;;N;;;;;
+F99A;CJK COMPATIBILITY IDEOGRAPH-F99A;Lo;0;L;9023;;;;N;;;;;
+F99B;CJK COMPATIBILITY IDEOGRAPH-F99B;Lo;0;L;934A;;;;N;;;;;
+F99C;CJK COMPATIBILITY IDEOGRAPH-F99C;Lo;0;L;5217;;;;N;;;;;
+F99D;CJK COMPATIBILITY IDEOGRAPH-F99D;Lo;0;L;52A3;;;;N;;;;;
+F99E;CJK COMPATIBILITY IDEOGRAPH-F99E;Lo;0;L;54BD;;;;N;;;;;
+F99F;CJK COMPATIBILITY IDEOGRAPH-F99F;Lo;0;L;70C8;;;;N;;;;;
+F9A0;CJK COMPATIBILITY IDEOGRAPH-F9A0;Lo;0;L;88C2;;;;N;;;;;
+F9A1;CJK COMPATIBILITY IDEOGRAPH-F9A1;Lo;0;L;8AAA;;;;N;;;;;
+F9A2;CJK COMPATIBILITY IDEOGRAPH-F9A2;Lo;0;L;5EC9;;;;N;;;;;
+F9A3;CJK COMPATIBILITY IDEOGRAPH-F9A3;Lo;0;L;5FF5;;;;N;;;;;
+F9A4;CJK COMPATIBILITY IDEOGRAPH-F9A4;Lo;0;L;637B;;;;N;;;;;
+F9A5;CJK COMPATIBILITY IDEOGRAPH-F9A5;Lo;0;L;6BAE;;;;N;;;;;
+F9A6;CJK COMPATIBILITY IDEOGRAPH-F9A6;Lo;0;L;7C3E;;;;N;;;;;
+F9A7;CJK COMPATIBILITY IDEOGRAPH-F9A7;Lo;0;L;7375;;;;N;;;;;
+F9A8;CJK COMPATIBILITY IDEOGRAPH-F9A8;Lo;0;L;4EE4;;;;N;;;;;
+F9A9;CJK COMPATIBILITY IDEOGRAPH-F9A9;Lo;0;L;56F9;;;;N;;;;;
+F9AA;CJK COMPATIBILITY IDEOGRAPH-F9AA;Lo;0;L;5BE7;;;;N;;;;;
+F9AB;CJK COMPATIBILITY IDEOGRAPH-F9AB;Lo;0;L;5DBA;;;;N;;;;;
+F9AC;CJK COMPATIBILITY IDEOGRAPH-F9AC;Lo;0;L;601C;;;;N;;;;;
+F9AD;CJK COMPATIBILITY IDEOGRAPH-F9AD;Lo;0;L;73B2;;;;N;;;;;
+F9AE;CJK COMPATIBILITY IDEOGRAPH-F9AE;Lo;0;L;7469;;;;N;;;;;
+F9AF;CJK COMPATIBILITY IDEOGRAPH-F9AF;Lo;0;L;7F9A;;;;N;;;;;
+F9B0;CJK COMPATIBILITY IDEOGRAPH-F9B0;Lo;0;L;8046;;;;N;;;;;
+F9B1;CJK COMPATIBILITY IDEOGRAPH-F9B1;Lo;0;L;9234;;;;N;;;;;
+F9B2;CJK COMPATIBILITY IDEOGRAPH-F9B2;Lo;0;L;96F6;;;;N;;;;;
+F9B3;CJK COMPATIBILITY IDEOGRAPH-F9B3;Lo;0;L;9748;;;;N;;;;;
+F9B4;CJK COMPATIBILITY IDEOGRAPH-F9B4;Lo;0;L;9818;;;;N;;;;;
+F9B5;CJK COMPATIBILITY IDEOGRAPH-F9B5;Lo;0;L;4F8B;;;;N;;;;;
+F9B6;CJK COMPATIBILITY IDEOGRAPH-F9B6;Lo;0;L;79AE;;;;N;;;;;
+F9B7;CJK COMPATIBILITY IDEOGRAPH-F9B7;Lo;0;L;91B4;;;;N;;;;;
+F9B8;CJK COMPATIBILITY IDEOGRAPH-F9B8;Lo;0;L;96B8;;;;N;;;;;
+F9B9;CJK COMPATIBILITY IDEOGRAPH-F9B9;Lo;0;L;60E1;;;;N;;;;;
+F9BA;CJK COMPATIBILITY IDEOGRAPH-F9BA;Lo;0;L;4E86;;;;N;;;;;
+F9BB;CJK COMPATIBILITY IDEOGRAPH-F9BB;Lo;0;L;50DA;;;;N;;;;;
+F9BC;CJK COMPATIBILITY IDEOGRAPH-F9BC;Lo;0;L;5BEE;;;;N;;;;;
+F9BD;CJK COMPATIBILITY IDEOGRAPH-F9BD;Lo;0;L;5C3F;;;;N;;;;;
+F9BE;CJK COMPATIBILITY IDEOGRAPH-F9BE;Lo;0;L;6599;;;;N;;;;;
+F9BF;CJK COMPATIBILITY IDEOGRAPH-F9BF;Lo;0;L;6A02;;;;N;;;;;
+F9C0;CJK COMPATIBILITY IDEOGRAPH-F9C0;Lo;0;L;71CE;;;;N;;;;;
+F9C1;CJK COMPATIBILITY IDEOGRAPH-F9C1;Lo;0;L;7642;;;;N;;;;;
+F9C2;CJK COMPATIBILITY IDEOGRAPH-F9C2;Lo;0;L;84FC;;;;N;;;;;
+F9C3;CJK COMPATIBILITY IDEOGRAPH-F9C3;Lo;0;L;907C;;;;N;;;;;
+F9C4;CJK COMPATIBILITY IDEOGRAPH-F9C4;Lo;0;L;9F8D;;;;N;;;;;
+F9C5;CJK COMPATIBILITY IDEOGRAPH-F9C5;Lo;0;L;6688;;;;N;;;;;
+F9C6;CJK COMPATIBILITY IDEOGRAPH-F9C6;Lo;0;L;962E;;;;N;;;;;
+F9C7;CJK COMPATIBILITY IDEOGRAPH-F9C7;Lo;0;L;5289;;;;N;;;;;
+F9C8;CJK COMPATIBILITY IDEOGRAPH-F9C8;Lo;0;L;677B;;;;N;;;;;
+F9C9;CJK COMPATIBILITY IDEOGRAPH-F9C9;Lo;0;L;67F3;;;;N;;;;;
+F9CA;CJK COMPATIBILITY IDEOGRAPH-F9CA;Lo;0;L;6D41;;;;N;;;;;
+F9CB;CJK COMPATIBILITY IDEOGRAPH-F9CB;Lo;0;L;6E9C;;;;N;;;;;
+F9CC;CJK COMPATIBILITY IDEOGRAPH-F9CC;Lo;0;L;7409;;;;N;;;;;
+F9CD;CJK COMPATIBILITY IDEOGRAPH-F9CD;Lo;0;L;7559;;;;N;;;;;
+F9CE;CJK COMPATIBILITY IDEOGRAPH-F9CE;Lo;0;L;786B;;;;N;;;;;
+F9CF;CJK COMPATIBILITY IDEOGRAPH-F9CF;Lo;0;L;7D10;;;;N;;;;;
+F9D0;CJK COMPATIBILITY IDEOGRAPH-F9D0;Lo;0;L;985E;;;;N;;;;;
+F9D1;CJK COMPATIBILITY IDEOGRAPH-F9D1;Lo;0;L;516D;;;;N;;;;;
+F9D2;CJK COMPATIBILITY IDEOGRAPH-F9D2;Lo;0;L;622E;;;;N;;;;;
+F9D3;CJK COMPATIBILITY IDEOGRAPH-F9D3;Lo;0;L;9678;;;;N;;;;;
+F9D4;CJK COMPATIBILITY IDEOGRAPH-F9D4;Lo;0;L;502B;;;;N;;;;;
+F9D5;CJK COMPATIBILITY IDEOGRAPH-F9D5;Lo;0;L;5D19;;;;N;;;;;
+F9D6;CJK COMPATIBILITY IDEOGRAPH-F9D6;Lo;0;L;6DEA;;;;N;;;;;
+F9D7;CJK COMPATIBILITY IDEOGRAPH-F9D7;Lo;0;L;8F2A;;;;N;;;;;
+F9D8;CJK COMPATIBILITY IDEOGRAPH-F9D8;Lo;0;L;5F8B;;;;N;;;;;
+F9D9;CJK COMPATIBILITY IDEOGRAPH-F9D9;Lo;0;L;6144;;;;N;;;;;
+F9DA;CJK COMPATIBILITY IDEOGRAPH-F9DA;Lo;0;L;6817;;;;N;;;;;
+F9DB;CJK COMPATIBILITY IDEOGRAPH-F9DB;Lo;0;L;7387;;;;N;;;;;
+F9DC;CJK COMPATIBILITY IDEOGRAPH-F9DC;Lo;0;L;9686;;;;N;;;;;
+F9DD;CJK COMPATIBILITY IDEOGRAPH-F9DD;Lo;0;L;5229;;;;N;;;;;
+F9DE;CJK COMPATIBILITY IDEOGRAPH-F9DE;Lo;0;L;540F;;;;N;;;;;
+F9DF;CJK COMPATIBILITY IDEOGRAPH-F9DF;Lo;0;L;5C65;;;;N;;;;;
+F9E0;CJK COMPATIBILITY IDEOGRAPH-F9E0;Lo;0;L;6613;;;;N;;;;;
+F9E1;CJK COMPATIBILITY IDEOGRAPH-F9E1;Lo;0;L;674E;;;;N;;;;;
+F9E2;CJK COMPATIBILITY IDEOGRAPH-F9E2;Lo;0;L;68A8;;;;N;;;;;
+F9E3;CJK COMPATIBILITY IDEOGRAPH-F9E3;Lo;0;L;6CE5;;;;N;;;;;
+F9E4;CJK COMPATIBILITY IDEOGRAPH-F9E4;Lo;0;L;7406;;;;N;;;;;
+F9E5;CJK COMPATIBILITY IDEOGRAPH-F9E5;Lo;0;L;75E2;;;;N;;;;;
+F9E6;CJK COMPATIBILITY IDEOGRAPH-F9E6;Lo;0;L;7F79;;;;N;;;;;
+F9E7;CJK COMPATIBILITY IDEOGRAPH-F9E7;Lo;0;L;88CF;;;;N;;;;;
+F9E8;CJK COMPATIBILITY IDEOGRAPH-F9E8;Lo;0;L;88E1;;;;N;;;;;
+F9E9;CJK COMPATIBILITY IDEOGRAPH-F9E9;Lo;0;L;91CC;;;;N;;;;;
+F9EA;CJK COMPATIBILITY IDEOGRAPH-F9EA;Lo;0;L;96E2;;;;N;;;;;
+F9EB;CJK COMPATIBILITY IDEOGRAPH-F9EB;Lo;0;L;533F;;;;N;;;;;
+F9EC;CJK COMPATIBILITY IDEOGRAPH-F9EC;Lo;0;L;6EBA;;;;N;;;;;
+F9ED;CJK COMPATIBILITY IDEOGRAPH-F9ED;Lo;0;L;541D;;;;N;;;;;
+F9EE;CJK COMPATIBILITY IDEOGRAPH-F9EE;Lo;0;L;71D0;;;;N;;;;;
+F9EF;CJK COMPATIBILITY IDEOGRAPH-F9EF;Lo;0;L;7498;;;;N;;;;;
+F9F0;CJK COMPATIBILITY IDEOGRAPH-F9F0;Lo;0;L;85FA;;;;N;;;;;
+F9F1;CJK COMPATIBILITY IDEOGRAPH-F9F1;Lo;0;L;96A3;;;;N;;;;;
+F9F2;CJK COMPATIBILITY IDEOGRAPH-F9F2;Lo;0;L;9C57;;;;N;;;;;
+F9F3;CJK COMPATIBILITY IDEOGRAPH-F9F3;Lo;0;L;9E9F;;;;N;;;;;
+F9F4;CJK COMPATIBILITY IDEOGRAPH-F9F4;Lo;0;L;6797;;;;N;;;;;
+F9F5;CJK COMPATIBILITY IDEOGRAPH-F9F5;Lo;0;L;6DCB;;;;N;;;;;
+F9F6;CJK COMPATIBILITY IDEOGRAPH-F9F6;Lo;0;L;81E8;;;;N;;;;;
+F9F7;CJK COMPATIBILITY IDEOGRAPH-F9F7;Lo;0;L;7ACB;;;;N;;;;;
+F9F8;CJK COMPATIBILITY IDEOGRAPH-F9F8;Lo;0;L;7B20;;;;N;;;;;
+F9F9;CJK COMPATIBILITY IDEOGRAPH-F9F9;Lo;0;L;7C92;;;;N;;;;;
+F9FA;CJK COMPATIBILITY IDEOGRAPH-F9FA;Lo;0;L;72C0;;;;N;;;;;
+F9FB;CJK COMPATIBILITY IDEOGRAPH-F9FB;Lo;0;L;7099;;;;N;;;;;
+F9FC;CJK COMPATIBILITY IDEOGRAPH-F9FC;Lo;0;L;8B58;;;;N;;;;;
+F9FD;CJK COMPATIBILITY IDEOGRAPH-F9FD;Lo;0;L;4EC0;;;;N;;;;;
+F9FE;CJK COMPATIBILITY IDEOGRAPH-F9FE;Lo;0;L;8336;;;;N;;;;;
+F9FF;CJK COMPATIBILITY IDEOGRAPH-F9FF;Lo;0;L;523A;;;;N;;;;;
+FA00;CJK COMPATIBILITY IDEOGRAPH-FA00;Lo;0;L;5207;;;;N;;;;;
+FA01;CJK COMPATIBILITY IDEOGRAPH-FA01;Lo;0;L;5EA6;;;;N;;;;;
+FA02;CJK COMPATIBILITY IDEOGRAPH-FA02;Lo;0;L;62D3;;;;N;;;;;
+FA03;CJK COMPATIBILITY IDEOGRAPH-FA03;Lo;0;L;7CD6;;;;N;;;;;
+FA04;CJK COMPATIBILITY IDEOGRAPH-FA04;Lo;0;L;5B85;;;;N;;;;;
+FA05;CJK COMPATIBILITY IDEOGRAPH-FA05;Lo;0;L;6D1E;;;;N;;;;;
+FA06;CJK COMPATIBILITY IDEOGRAPH-FA06;Lo;0;L;66B4;;;;N;;;;;
+FA07;CJK COMPATIBILITY IDEOGRAPH-FA07;Lo;0;L;8F3B;;;;N;;;;;
+FA08;CJK COMPATIBILITY IDEOGRAPH-FA08;Lo;0;L;884C;;;;N;;;;;
+FA09;CJK COMPATIBILITY IDEOGRAPH-FA09;Lo;0;L;964D;;;;N;;;;;
+FA0A;CJK COMPATIBILITY IDEOGRAPH-FA0A;Lo;0;L;898B;;;;N;;;;;
+FA0B;CJK COMPATIBILITY IDEOGRAPH-FA0B;Lo;0;L;5ED3;;;;N;;;;;
+FA0C;CJK COMPATIBILITY IDEOGRAPH-FA0C;Lo;0;L;5140;;;;N;;;;;
+FA0D;CJK COMPATIBILITY IDEOGRAPH-FA0D;Lo;0;L;55C0;;;;N;;;;;
+FA0E;CJK COMPATIBILITY IDEOGRAPH-FA0E;Lo;0;L;;;;;N;;;;;
+FA0F;CJK COMPATIBILITY IDEOGRAPH-FA0F;Lo;0;L;;;;;N;;;;;
+FA10;CJK COMPATIBILITY IDEOGRAPH-FA10;Lo;0;L;585A;;;;N;;;;;
+FA11;CJK COMPATIBILITY IDEOGRAPH-FA11;Lo;0;L;;;;;N;;;;;
+FA12;CJK COMPATIBILITY IDEOGRAPH-FA12;Lo;0;L;6674;;;;N;;;;;
+FA13;CJK COMPATIBILITY IDEOGRAPH-FA13;Lo;0;L;;;;;N;;;;;
+FA14;CJK COMPATIBILITY IDEOGRAPH-FA14;Lo;0;L;;;;;N;;;;;
+FA15;CJK COMPATIBILITY IDEOGRAPH-FA15;Lo;0;L;51DE;;;;N;;;;;
+FA16;CJK COMPATIBILITY IDEOGRAPH-FA16;Lo;0;L;732A;;;;N;;;;;
+FA17;CJK COMPATIBILITY IDEOGRAPH-FA17;Lo;0;L;76CA;;;;N;;;;;
+FA18;CJK COMPATIBILITY IDEOGRAPH-FA18;Lo;0;L;793C;;;;N;;;;;
+FA19;CJK COMPATIBILITY IDEOGRAPH-FA19;Lo;0;L;795E;;;;N;;;;;
+FA1A;CJK COMPATIBILITY IDEOGRAPH-FA1A;Lo;0;L;7965;;;;N;;;;;
+FA1B;CJK COMPATIBILITY IDEOGRAPH-FA1B;Lo;0;L;798F;;;;N;;;;;
+FA1C;CJK COMPATIBILITY IDEOGRAPH-FA1C;Lo;0;L;9756;;;;N;;;;;
+FA1D;CJK COMPATIBILITY IDEOGRAPH-FA1D;Lo;0;L;7CBE;;;;N;;;;;
+FA1E;CJK COMPATIBILITY IDEOGRAPH-FA1E;Lo;0;L;7FBD;;;;N;;;;;
+FA1F;CJK COMPATIBILITY IDEOGRAPH-FA1F;Lo;0;L;;;;;N;;*;;;
+FA20;CJK COMPATIBILITY IDEOGRAPH-FA20;Lo;0;L;8612;;;;N;;;;;
+FA21;CJK COMPATIBILITY IDEOGRAPH-FA21;Lo;0;L;;;;;N;;;;;
+FA22;CJK COMPATIBILITY IDEOGRAPH-FA22;Lo;0;L;8AF8;;;;N;;;;;
+FA23;CJK COMPATIBILITY IDEOGRAPH-FA23;Lo;0;L;;;;;N;;*;;;
+FA24;CJK COMPATIBILITY IDEOGRAPH-FA24;Lo;0;L;;;;;N;;;;;
+FA25;CJK COMPATIBILITY IDEOGRAPH-FA25;Lo;0;L;9038;;;;N;;;;;
+FA26;CJK COMPATIBILITY IDEOGRAPH-FA26;Lo;0;L;90FD;;;;N;;;;;
+FA27;CJK COMPATIBILITY IDEOGRAPH-FA27;Lo;0;L;;;;;N;;;;;
+FA28;CJK COMPATIBILITY IDEOGRAPH-FA28;Lo;0;L;;;;;N;;;;;
+FA29;CJK COMPATIBILITY IDEOGRAPH-FA29;Lo;0;L;;;;;N;;;;;
+FA2A;CJK COMPATIBILITY IDEOGRAPH-FA2A;Lo;0;L;98EF;;;;N;;;;;
+FA2B;CJK COMPATIBILITY IDEOGRAPH-FA2B;Lo;0;L;98FC;;;;N;;;;;
+FA2C;CJK COMPATIBILITY IDEOGRAPH-FA2C;Lo;0;L;9928;;;;N;;;;;
+FA2D;CJK COMPATIBILITY IDEOGRAPH-FA2D;Lo;0;L;9DB4;;;;N;;;;;
+FA30;CJK COMPATIBILITY IDEOGRAPH-FA30;Lo;0;L;4FAE;;;;N;;;;;
+FA31;CJK COMPATIBILITY IDEOGRAPH-FA31;Lo;0;L;50E7;;;;N;;;;;
+FA32;CJK COMPATIBILITY IDEOGRAPH-FA32;Lo;0;L;514D;;;;N;;;;;
+FA33;CJK COMPATIBILITY IDEOGRAPH-FA33;Lo;0;L;52C9;;;;N;;;;;
+FA34;CJK COMPATIBILITY IDEOGRAPH-FA34;Lo;0;L;52E4;;;;N;;;;;
+FA35;CJK COMPATIBILITY IDEOGRAPH-FA35;Lo;0;L;5351;;;;N;;;;;
+FA36;CJK COMPATIBILITY IDEOGRAPH-FA36;Lo;0;L;559D;;;;N;;;;;
+FA37;CJK COMPATIBILITY IDEOGRAPH-FA37;Lo;0;L;5606;;;;N;;;;;
+FA38;CJK COMPATIBILITY IDEOGRAPH-FA38;Lo;0;L;5668;;;;N;;;;;
+FA39;CJK COMPATIBILITY IDEOGRAPH-FA39;Lo;0;L;5840;;;;N;;;;;
+FA3A;CJK COMPATIBILITY IDEOGRAPH-FA3A;Lo;0;L;58A8;;;;N;;;;;
+FA3B;CJK COMPATIBILITY IDEOGRAPH-FA3B;Lo;0;L;5C64;;;;N;;;;;
+FA3C;CJK COMPATIBILITY IDEOGRAPH-FA3C;Lo;0;L;5C6E;;;;N;;;;;
+FA3D;CJK COMPATIBILITY IDEOGRAPH-FA3D;Lo;0;L;6094;;;;N;;;;;
+FA3E;CJK COMPATIBILITY IDEOGRAPH-FA3E;Lo;0;L;6168;;;;N;;;;;
+FA3F;CJK COMPATIBILITY IDEOGRAPH-FA3F;Lo;0;L;618E;;;;N;;;;;
+FA40;CJK COMPATIBILITY IDEOGRAPH-FA40;Lo;0;L;61F2;;;;N;;;;;
+FA41;CJK COMPATIBILITY IDEOGRAPH-FA41;Lo;0;L;654F;;;;N;;;;;
+FA42;CJK COMPATIBILITY IDEOGRAPH-FA42;Lo;0;L;65E2;;;;N;;;;;
+FA43;CJK COMPATIBILITY IDEOGRAPH-FA43;Lo;0;L;6691;;;;N;;;;;
+FA44;CJK COMPATIBILITY IDEOGRAPH-FA44;Lo;0;L;6885;;;;N;;;;;
+FA45;CJK COMPATIBILITY IDEOGRAPH-FA45;Lo;0;L;6D77;;;;N;;;;;
+FA46;CJK COMPATIBILITY IDEOGRAPH-FA46;Lo;0;L;6E1A;;;;N;;;;;
+FA47;CJK COMPATIBILITY IDEOGRAPH-FA47;Lo;0;L;6F22;;;;N;;;;;
+FA48;CJK COMPATIBILITY IDEOGRAPH-FA48;Lo;0;L;716E;;;;N;;;;;
+FA49;CJK COMPATIBILITY IDEOGRAPH-FA49;Lo;0;L;722B;;;;N;;;;;
+FA4A;CJK COMPATIBILITY IDEOGRAPH-FA4A;Lo;0;L;7422;;;;N;;;;;
+FA4B;CJK COMPATIBILITY IDEOGRAPH-FA4B;Lo;0;L;7891;;;;N;;;;;
+FA4C;CJK COMPATIBILITY IDEOGRAPH-FA4C;Lo;0;L;793E;;;;N;;;;;
+FA4D;CJK COMPATIBILITY IDEOGRAPH-FA4D;Lo;0;L;7949;;;;N;;;;;
+FA4E;CJK COMPATIBILITY IDEOGRAPH-FA4E;Lo;0;L;7948;;;;N;;;;;
+FA4F;CJK COMPATIBILITY IDEOGRAPH-FA4F;Lo;0;L;7950;;;;N;;;;;
+FA50;CJK COMPATIBILITY IDEOGRAPH-FA50;Lo;0;L;7956;;;;N;;;;;
+FA51;CJK COMPATIBILITY IDEOGRAPH-FA51;Lo;0;L;795D;;;;N;;;;;
+FA52;CJK COMPATIBILITY IDEOGRAPH-FA52;Lo;0;L;798D;;;;N;;;;;
+FA53;CJK COMPATIBILITY IDEOGRAPH-FA53;Lo;0;L;798E;;;;N;;;;;
+FA54;CJK COMPATIBILITY IDEOGRAPH-FA54;Lo;0;L;7A40;;;;N;;;;;
+FA55;CJK COMPATIBILITY IDEOGRAPH-FA55;Lo;0;L;7A81;;;;N;;;;;
+FA56;CJK COMPATIBILITY IDEOGRAPH-FA56;Lo;0;L;7BC0;;;;N;;;;;
+FA57;CJK COMPATIBILITY IDEOGRAPH-FA57;Lo;0;L;7DF4;;;;N;;;;;
+FA58;CJK COMPATIBILITY IDEOGRAPH-FA58;Lo;0;L;7E09;;;;N;;;;;
+FA59;CJK COMPATIBILITY IDEOGRAPH-FA59;Lo;0;L;7E41;;;;N;;;;;
+FA5A;CJK COMPATIBILITY IDEOGRAPH-FA5A;Lo;0;L;7F72;;;;N;;;;;
+FA5B;CJK COMPATIBILITY IDEOGRAPH-FA5B;Lo;0;L;8005;;;;N;;;;;
+FA5C;CJK COMPATIBILITY IDEOGRAPH-FA5C;Lo;0;L;81ED;;;;N;;;;;
+FA5D;CJK COMPATIBILITY IDEOGRAPH-FA5D;Lo;0;L;8279;;;;N;;;;;
+FA5E;CJK COMPATIBILITY IDEOGRAPH-FA5E;Lo;0;L;8279;;;;N;;;;;
+FA5F;CJK COMPATIBILITY IDEOGRAPH-FA5F;Lo;0;L;8457;;;;N;;;;;
+FA60;CJK COMPATIBILITY IDEOGRAPH-FA60;Lo;0;L;8910;;;;N;;;;;
+FA61;CJK COMPATIBILITY IDEOGRAPH-FA61;Lo;0;L;8996;;;;N;;;;;
+FA62;CJK COMPATIBILITY IDEOGRAPH-FA62;Lo;0;L;8B01;;;;N;;;;;
+FA63;CJK COMPATIBILITY IDEOGRAPH-FA63;Lo;0;L;8B39;;;;N;;;;;
+FA64;CJK COMPATIBILITY IDEOGRAPH-FA64;Lo;0;L;8CD3;;;;N;;;;;
+FA65;CJK COMPATIBILITY IDEOGRAPH-FA65;Lo;0;L;8D08;;;;N;;;;;
+FA66;CJK COMPATIBILITY IDEOGRAPH-FA66;Lo;0;L;8FB6;;;;N;;;;;
+FA67;CJK COMPATIBILITY IDEOGRAPH-FA67;Lo;0;L;9038;;;;N;;;;;
+FA68;CJK COMPATIBILITY IDEOGRAPH-FA68;Lo;0;L;96E3;;;;N;;;;;
+FA69;CJK COMPATIBILITY IDEOGRAPH-FA69;Lo;0;L;97FF;;;;N;;;;;
+FA6A;CJK COMPATIBILITY IDEOGRAPH-FA6A;Lo;0;L;983B;;;;N;;;;;
+FB00;LATIN SMALL LIGATURE FF;Ll;0;L;<compat> 0066 0066;;;;N;;;;;
+FB01;LATIN SMALL LIGATURE FI;Ll;0;L;<compat> 0066 0069;;;;N;;;;;
+FB02;LATIN SMALL LIGATURE FL;Ll;0;L;<compat> 0066 006C;;;;N;;;;;
+FB03;LATIN SMALL LIGATURE FFI;Ll;0;L;<compat> 0066 0066 0069;;;;N;;;;;
+FB04;LATIN SMALL LIGATURE FFL;Ll;0;L;<compat> 0066 0066 006C;;;;N;;;;;
+FB05;LATIN SMALL LIGATURE LONG S T;Ll;0;L;<compat> 017F 0074;;;;N;;;;;
+FB06;LATIN SMALL LIGATURE ST;Ll;0;L;<compat> 0073 0074;;;;N;;;;;
+FB13;ARMENIAN SMALL LIGATURE MEN NOW;Ll;0;L;<compat> 0574 0576;;;;N;;;;;
+FB14;ARMENIAN SMALL LIGATURE MEN ECH;Ll;0;L;<compat> 0574 0565;;;;N;;;;;
+FB15;ARMENIAN SMALL LIGATURE MEN INI;Ll;0;L;<compat> 0574 056B;;;;N;;;;;
+FB16;ARMENIAN SMALL LIGATURE VEW NOW;Ll;0;L;<compat> 057E 0576;;;;N;;;;;
+FB17;ARMENIAN SMALL LIGATURE MEN XEH;Ll;0;L;<compat> 0574 056D;;;;N;;;;;
+FB1D;HEBREW LETTER YOD WITH HIRIQ;Lo;0;R;05D9 05B4;;;;N;;;;;
+FB1E;HEBREW POINT JUDEO-SPANISH VARIKA;Mn;26;NSM;;;;;N;HEBREW POINT VARIKA;;;;
+FB1F;HEBREW LIGATURE YIDDISH YOD YOD PATAH;Lo;0;R;05F2 05B7;;;;N;;;;;
+FB20;HEBREW LETTER ALTERNATIVE AYIN;Lo;0;R;<font> 05E2;;;;N;;;;;
+FB21;HEBREW LETTER WIDE ALEF;Lo;0;R;<font> 05D0;;;;N;;;;;
+FB22;HEBREW LETTER WIDE DALET;Lo;0;R;<font> 05D3;;;;N;;;;;
+FB23;HEBREW LETTER WIDE HE;Lo;0;R;<font> 05D4;;;;N;;;;;
+FB24;HEBREW LETTER WIDE KAF;Lo;0;R;<font> 05DB;;;;N;;;;;
+FB25;HEBREW LETTER WIDE LAMED;Lo;0;R;<font> 05DC;;;;N;;;;;
+FB26;HEBREW LETTER WIDE FINAL MEM;Lo;0;R;<font> 05DD;;;;N;;;;;
+FB27;HEBREW LETTER WIDE RESH;Lo;0;R;<font> 05E8;;;;N;;;;;
+FB28;HEBREW LETTER WIDE TAV;Lo;0;R;<font> 05EA;;;;N;;;;;
+FB29;HEBREW LETTER ALTERNATIVE PLUS SIGN;Sm;0;ET;<font> 002B;;;;N;;;;;
+FB2A;HEBREW LETTER SHIN WITH SHIN DOT;Lo;0;R;05E9 05C1;;;;N;;;;;
+FB2B;HEBREW LETTER SHIN WITH SIN DOT;Lo;0;R;05E9 05C2;;;;N;;;;;
+FB2C;HEBREW LETTER SHIN WITH DAGESH AND SHIN DOT;Lo;0;R;FB49 05C1;;;;N;;;;;
+FB2D;HEBREW LETTER SHIN WITH DAGESH AND SIN DOT;Lo;0;R;FB49 05C2;;;;N;;;;;
+FB2E;HEBREW LETTER ALEF WITH PATAH;Lo;0;R;05D0 05B7;;;;N;;;;;
+FB2F;HEBREW LETTER ALEF WITH QAMATS;Lo;0;R;05D0 05B8;;;;N;;;;;
+FB30;HEBREW LETTER ALEF WITH MAPIQ;Lo;0;R;05D0 05BC;;;;N;;;;;
+FB31;HEBREW LETTER BET WITH DAGESH;Lo;0;R;05D1 05BC;;;;N;;;;;
+FB32;HEBREW LETTER GIMEL WITH DAGESH;Lo;0;R;05D2 05BC;;;;N;;;;;
+FB33;HEBREW LETTER DALET WITH DAGESH;Lo;0;R;05D3 05BC;;;;N;;;;;
+FB34;HEBREW LETTER HE WITH MAPIQ;Lo;0;R;05D4 05BC;;;;N;;;;;
+FB35;HEBREW LETTER VAV WITH DAGESH;Lo;0;R;05D5 05BC;;;;N;;;;;
+FB36;HEBREW LETTER ZAYIN WITH DAGESH;Lo;0;R;05D6 05BC;;;;N;;;;;
+FB38;HEBREW LETTER TET WITH DAGESH;Lo;0;R;05D8 05BC;;;;N;;;;;
+FB39;HEBREW LETTER YOD WITH DAGESH;Lo;0;R;05D9 05BC;;;;N;;;;;
+FB3A;HEBREW LETTER FINAL KAF WITH DAGESH;Lo;0;R;05DA 05BC;;;;N;;;;;
+FB3B;HEBREW LETTER KAF WITH DAGESH;Lo;0;R;05DB 05BC;;;;N;;;;;
+FB3C;HEBREW LETTER LAMED WITH DAGESH;Lo;0;R;05DC 05BC;;;;N;;;;;
+FB3E;HEBREW LETTER MEM WITH DAGESH;Lo;0;R;05DE 05BC;;;;N;;;;;
+FB40;HEBREW LETTER NUN WITH DAGESH;Lo;0;R;05E0 05BC;;;;N;;;;;
+FB41;HEBREW LETTER SAMEKH WITH DAGESH;Lo;0;R;05E1 05BC;;;;N;;;;;
+FB43;HEBREW LETTER FINAL PE WITH DAGESH;Lo;0;R;05E3 05BC;;;;N;;;;;
+FB44;HEBREW LETTER PE WITH DAGESH;Lo;0;R;05E4 05BC;;;;N;;;;;
+FB46;HEBREW LETTER TSADI WITH DAGESH;Lo;0;R;05E6 05BC;;;;N;;;;;
+FB47;HEBREW LETTER QOF WITH DAGESH;Lo;0;R;05E7 05BC;;;;N;;;;;
+FB48;HEBREW LETTER RESH WITH DAGESH;Lo;0;R;05E8 05BC;;;;N;;;;;
+FB49;HEBREW LETTER SHIN WITH DAGESH;Lo;0;R;05E9 05BC;;;;N;;;;;
+FB4A;HEBREW LETTER TAV WITH DAGESH;Lo;0;R;05EA 05BC;;;;N;;;;;
+FB4B;HEBREW LETTER VAV WITH HOLAM;Lo;0;R;05D5 05B9;;;;N;;;;;
+FB4C;HEBREW LETTER BET WITH RAFE;Lo;0;R;05D1 05BF;;;;N;;;;;
+FB4D;HEBREW LETTER KAF WITH RAFE;Lo;0;R;05DB 05BF;;;;N;;;;;
+FB4E;HEBREW LETTER PE WITH RAFE;Lo;0;R;05E4 05BF;;;;N;;;;;
+FB4F;HEBREW LIGATURE ALEF LAMED;Lo;0;R;<compat> 05D0 05DC;;;;N;;;;;
+FB50;ARABIC LETTER ALEF WASLA ISOLATED FORM;Lo;0;AL;<isolated> 0671;;;;N;;;;;
+FB51;ARABIC LETTER ALEF WASLA FINAL FORM;Lo;0;AL;<final> 0671;;;;N;;;;;
+FB52;ARABIC LETTER BEEH ISOLATED FORM;Lo;0;AL;<isolated> 067B;;;;N;;;;;
+FB53;ARABIC LETTER BEEH FINAL FORM;Lo;0;AL;<final> 067B;;;;N;;;;;
+FB54;ARABIC LETTER BEEH INITIAL FORM;Lo;0;AL;<initial> 067B;;;;N;;;;;
+FB55;ARABIC LETTER BEEH MEDIAL FORM;Lo;0;AL;<medial> 067B;;;;N;;;;;
+FB56;ARABIC LETTER PEH ISOLATED FORM;Lo;0;AL;<isolated> 067E;;;;N;;;;;
+FB57;ARABIC LETTER PEH FINAL FORM;Lo;0;AL;<final> 067E;;;;N;;;;;
+FB58;ARABIC LETTER PEH INITIAL FORM;Lo;0;AL;<initial> 067E;;;;N;;;;;
+FB59;ARABIC LETTER PEH MEDIAL FORM;Lo;0;AL;<medial> 067E;;;;N;;;;;
+FB5A;ARABIC LETTER BEHEH ISOLATED FORM;Lo;0;AL;<isolated> 0680;;;;N;;;;;
+FB5B;ARABIC LETTER BEHEH FINAL FORM;Lo;0;AL;<final> 0680;;;;N;;;;;
+FB5C;ARABIC LETTER BEHEH INITIAL FORM;Lo;0;AL;<initial> 0680;;;;N;;;;;
+FB5D;ARABIC LETTER BEHEH MEDIAL FORM;Lo;0;AL;<medial> 0680;;;;N;;;;;
+FB5E;ARABIC LETTER TTEHEH ISOLATED FORM;Lo;0;AL;<isolated> 067A;;;;N;;;;;
+FB5F;ARABIC LETTER TTEHEH FINAL FORM;Lo;0;AL;<final> 067A;;;;N;;;;;
+FB60;ARABIC LETTER TTEHEH INITIAL FORM;Lo;0;AL;<initial> 067A;;;;N;;;;;
+FB61;ARABIC LETTER TTEHEH MEDIAL FORM;Lo;0;AL;<medial> 067A;;;;N;;;;;
+FB62;ARABIC LETTER TEHEH ISOLATED FORM;Lo;0;AL;<isolated> 067F;;;;N;;;;;
+FB63;ARABIC LETTER TEHEH FINAL FORM;Lo;0;AL;<final> 067F;;;;N;;;;;
+FB64;ARABIC LETTER TEHEH INITIAL FORM;Lo;0;AL;<initial> 067F;;;;N;;;;;
+FB65;ARABIC LETTER TEHEH MEDIAL FORM;Lo;0;AL;<medial> 067F;;;;N;;;;;
+FB66;ARABIC LETTER TTEH ISOLATED FORM;Lo;0;AL;<isolated> 0679;;;;N;;;;;
+FB67;ARABIC LETTER TTEH FINAL FORM;Lo;0;AL;<final> 0679;;;;N;;;;;
+FB68;ARABIC LETTER TTEH INITIAL FORM;Lo;0;AL;<initial> 0679;;;;N;;;;;
+FB69;ARABIC LETTER TTEH MEDIAL FORM;Lo;0;AL;<medial> 0679;;;;N;;;;;
+FB6A;ARABIC LETTER VEH ISOLATED FORM;Lo;0;AL;<isolated> 06A4;;;;N;;;;;
+FB6B;ARABIC LETTER VEH FINAL FORM;Lo;0;AL;<final> 06A4;;;;N;;;;;
+FB6C;ARABIC LETTER VEH INITIAL FORM;Lo;0;AL;<initial> 06A4;;;;N;;;;;
+FB6D;ARABIC LETTER VEH MEDIAL FORM;Lo;0;AL;<medial> 06A4;;;;N;;;;;
+FB6E;ARABIC LETTER PEHEH ISOLATED FORM;Lo;0;AL;<isolated> 06A6;;;;N;;;;;
+FB6F;ARABIC LETTER PEHEH FINAL FORM;Lo;0;AL;<final> 06A6;;;;N;;;;;
+FB70;ARABIC LETTER PEHEH INITIAL FORM;Lo;0;AL;<initial> 06A6;;;;N;;;;;
+FB71;ARABIC LETTER PEHEH MEDIAL FORM;Lo;0;AL;<medial> 06A6;;;;N;;;;;
+FB72;ARABIC LETTER DYEH ISOLATED FORM;Lo;0;AL;<isolated> 0684;;;;N;;;;;
+FB73;ARABIC LETTER DYEH FINAL FORM;Lo;0;AL;<final> 0684;;;;N;;;;;
+FB74;ARABIC LETTER DYEH INITIAL FORM;Lo;0;AL;<initial> 0684;;;;N;;;;;
+FB75;ARABIC LETTER DYEH MEDIAL FORM;Lo;0;AL;<medial> 0684;;;;N;;;;;
+FB76;ARABIC LETTER NYEH ISOLATED FORM;Lo;0;AL;<isolated> 0683;;;;N;;;;;
+FB77;ARABIC LETTER NYEH FINAL FORM;Lo;0;AL;<final> 0683;;;;N;;;;;
+FB78;ARABIC LETTER NYEH INITIAL FORM;Lo;0;AL;<initial> 0683;;;;N;;;;;
+FB79;ARABIC LETTER NYEH MEDIAL FORM;Lo;0;AL;<medial> 0683;;;;N;;;;;
+FB7A;ARABIC LETTER TCHEH ISOLATED FORM;Lo;0;AL;<isolated> 0686;;;;N;;;;;
+FB7B;ARABIC LETTER TCHEH FINAL FORM;Lo;0;AL;<final> 0686;;;;N;;;;;
+FB7C;ARABIC LETTER TCHEH INITIAL FORM;Lo;0;AL;<initial> 0686;;;;N;;;;;
+FB7D;ARABIC LETTER TCHEH MEDIAL FORM;Lo;0;AL;<medial> 0686;;;;N;;;;;
+FB7E;ARABIC LETTER TCHEHEH ISOLATED FORM;Lo;0;AL;<isolated> 0687;;;;N;;;;;
+FB7F;ARABIC LETTER TCHEHEH FINAL FORM;Lo;0;AL;<final> 0687;;;;N;;;;;
+FB80;ARABIC LETTER TCHEHEH INITIAL FORM;Lo;0;AL;<initial> 0687;;;;N;;;;;
+FB81;ARABIC LETTER TCHEHEH MEDIAL FORM;Lo;0;AL;<medial> 0687;;;;N;;;;;
+FB82;ARABIC LETTER DDAHAL ISOLATED FORM;Lo;0;AL;<isolated> 068D;;;;N;;;;;
+FB83;ARABIC LETTER DDAHAL FINAL FORM;Lo;0;AL;<final> 068D;;;;N;;;;;
+FB84;ARABIC LETTER DAHAL ISOLATED FORM;Lo;0;AL;<isolated> 068C;;;;N;;;;;
+FB85;ARABIC LETTER DAHAL FINAL FORM;Lo;0;AL;<final> 068C;;;;N;;;;;
+FB86;ARABIC LETTER DUL ISOLATED FORM;Lo;0;AL;<isolated> 068E;;;;N;;;;;
+FB87;ARABIC LETTER DUL FINAL FORM;Lo;0;AL;<final> 068E;;;;N;;;;;
+FB88;ARABIC LETTER DDAL ISOLATED FORM;Lo;0;AL;<isolated> 0688;;;;N;;;;;
+FB89;ARABIC LETTER DDAL FINAL FORM;Lo;0;AL;<final> 0688;;;;N;;;;;
+FB8A;ARABIC LETTER JEH ISOLATED FORM;Lo;0;AL;<isolated> 0698;;;;N;;;;;
+FB8B;ARABIC LETTER JEH FINAL FORM;Lo;0;AL;<final> 0698;;;;N;;;;;
+FB8C;ARABIC LETTER RREH ISOLATED FORM;Lo;0;AL;<isolated> 0691;;;;N;;;;;
+FB8D;ARABIC LETTER RREH FINAL FORM;Lo;0;AL;<final> 0691;;;;N;;;;;
+FB8E;ARABIC LETTER KEHEH ISOLATED FORM;Lo;0;AL;<isolated> 06A9;;;;N;;;;;
+FB8F;ARABIC LETTER KEHEH FINAL FORM;Lo;0;AL;<final> 06A9;;;;N;;;;;
+FB90;ARABIC LETTER KEHEH INITIAL FORM;Lo;0;AL;<initial> 06A9;;;;N;;;;;
+FB91;ARABIC LETTER KEHEH MEDIAL FORM;Lo;0;AL;<medial> 06A9;;;;N;;;;;
+FB92;ARABIC LETTER GAF ISOLATED FORM;Lo;0;AL;<isolated> 06AF;;;;N;;;;;
+FB93;ARABIC LETTER GAF FINAL FORM;Lo;0;AL;<final> 06AF;;;;N;;;;;
+FB94;ARABIC LETTER GAF INITIAL FORM;Lo;0;AL;<initial> 06AF;;;;N;;;;;
+FB95;ARABIC LETTER GAF MEDIAL FORM;Lo;0;AL;<medial> 06AF;;;;N;;;;;
+FB96;ARABIC LETTER GUEH ISOLATED FORM;Lo;0;AL;<isolated> 06B3;;;;N;;;;;
+FB97;ARABIC LETTER GUEH FINAL FORM;Lo;0;AL;<final> 06B3;;;;N;;;;;
+FB98;ARABIC LETTER GUEH INITIAL FORM;Lo;0;AL;<initial> 06B3;;;;N;;;;;
+FB99;ARABIC LETTER GUEH MEDIAL FORM;Lo;0;AL;<medial> 06B3;;;;N;;;;;
+FB9A;ARABIC LETTER NGOEH ISOLATED FORM;Lo;0;AL;<isolated> 06B1;;;;N;;;;;
+FB9B;ARABIC LETTER NGOEH FINAL FORM;Lo;0;AL;<final> 06B1;;;;N;;;;;
+FB9C;ARABIC LETTER NGOEH INITIAL FORM;Lo;0;AL;<initial> 06B1;;;;N;;;;;
+FB9D;ARABIC LETTER NGOEH MEDIAL FORM;Lo;0;AL;<medial> 06B1;;;;N;;;;;
+FB9E;ARABIC LETTER NOON GHUNNA ISOLATED FORM;Lo;0;AL;<isolated> 06BA;;;;N;;;;;
+FB9F;ARABIC LETTER NOON GHUNNA FINAL FORM;Lo;0;AL;<final> 06BA;;;;N;;;;;
+FBA0;ARABIC LETTER RNOON ISOLATED FORM;Lo;0;AL;<isolated> 06BB;;;;N;;;;;
+FBA1;ARABIC LETTER RNOON FINAL FORM;Lo;0;AL;<final> 06BB;;;;N;;;;;
+FBA2;ARABIC LETTER RNOON INITIAL FORM;Lo;0;AL;<initial> 06BB;;;;N;;;;;
+FBA3;ARABIC LETTER RNOON MEDIAL FORM;Lo;0;AL;<medial> 06BB;;;;N;;;;;
+FBA4;ARABIC LETTER HEH WITH YEH ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 06C0;;;;N;;;;;
+FBA5;ARABIC LETTER HEH WITH YEH ABOVE FINAL FORM;Lo;0;AL;<final> 06C0;;;;N;;;;;
+FBA6;ARABIC LETTER HEH GOAL ISOLATED FORM;Lo;0;AL;<isolated> 06C1;;;;N;;;;;
+FBA7;ARABIC LETTER HEH GOAL FINAL FORM;Lo;0;AL;<final> 06C1;;;;N;;;;;
+FBA8;ARABIC LETTER HEH GOAL INITIAL FORM;Lo;0;AL;<initial> 06C1;;;;N;;;;;
+FBA9;ARABIC LETTER HEH GOAL MEDIAL FORM;Lo;0;AL;<medial> 06C1;;;;N;;;;;
+FBAA;ARABIC LETTER HEH DOACHASHMEE ISOLATED FORM;Lo;0;AL;<isolated> 06BE;;;;N;;;;;
+FBAB;ARABIC LETTER HEH DOACHASHMEE FINAL FORM;Lo;0;AL;<final> 06BE;;;;N;;;;;
+FBAC;ARABIC LETTER HEH DOACHASHMEE INITIAL FORM;Lo;0;AL;<initial> 06BE;;;;N;;;;;
+FBAD;ARABIC LETTER HEH DOACHASHMEE MEDIAL FORM;Lo;0;AL;<medial> 06BE;;;;N;;;;;
+FBAE;ARABIC LETTER YEH BARREE ISOLATED FORM;Lo;0;AL;<isolated> 06D2;;;;N;;;;;
+FBAF;ARABIC LETTER YEH BARREE FINAL FORM;Lo;0;AL;<final> 06D2;;;;N;;;;;
+FBB0;ARABIC LETTER YEH BARREE WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 06D3;;;;N;;;;;
+FBB1;ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 06D3;;;;N;;;;;
+FBD3;ARABIC LETTER NG ISOLATED FORM;Lo;0;AL;<isolated> 06AD;;;;N;;;;;
+FBD4;ARABIC LETTER NG FINAL FORM;Lo;0;AL;<final> 06AD;;;;N;;;;;
+FBD5;ARABIC LETTER NG INITIAL FORM;Lo;0;AL;<initial> 06AD;;;;N;;;;;
+FBD6;ARABIC LETTER NG MEDIAL FORM;Lo;0;AL;<medial> 06AD;;;;N;;;;;
+FBD7;ARABIC LETTER U ISOLATED FORM;Lo;0;AL;<isolated> 06C7;;;;N;;;;;
+FBD8;ARABIC LETTER U FINAL FORM;Lo;0;AL;<final> 06C7;;;;N;;;;;
+FBD9;ARABIC LETTER OE ISOLATED FORM;Lo;0;AL;<isolated> 06C6;;;;N;;;;;
+FBDA;ARABIC LETTER OE FINAL FORM;Lo;0;AL;<final> 06C6;;;;N;;;;;
+FBDB;ARABIC LETTER YU ISOLATED FORM;Lo;0;AL;<isolated> 06C8;;;;N;;;;;
+FBDC;ARABIC LETTER YU FINAL FORM;Lo;0;AL;<final> 06C8;;;;N;;;;;
+FBDD;ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0677;;;;N;;;;;
+FBDE;ARABIC LETTER VE ISOLATED FORM;Lo;0;AL;<isolated> 06CB;;;;N;;;;;
+FBDF;ARABIC LETTER VE FINAL FORM;Lo;0;AL;<final> 06CB;;;;N;;;;;
+FBE0;ARABIC LETTER KIRGHIZ OE ISOLATED FORM;Lo;0;AL;<isolated> 06C5;;;;N;;;;;
+FBE1;ARABIC LETTER KIRGHIZ OE FINAL FORM;Lo;0;AL;<final> 06C5;;;;N;;;;;
+FBE2;ARABIC LETTER KIRGHIZ YU ISOLATED FORM;Lo;0;AL;<isolated> 06C9;;;;N;;;;;
+FBE3;ARABIC LETTER KIRGHIZ YU FINAL FORM;Lo;0;AL;<final> 06C9;;;;N;;;;;
+FBE4;ARABIC LETTER E ISOLATED FORM;Lo;0;AL;<isolated> 06D0;;;;N;;;;;
+FBE5;ARABIC LETTER E FINAL FORM;Lo;0;AL;<final> 06D0;;;;N;;;;;
+FBE6;ARABIC LETTER E INITIAL FORM;Lo;0;AL;<initial> 06D0;;;;N;;;;;
+FBE7;ARABIC LETTER E MEDIAL FORM;Lo;0;AL;<medial> 06D0;;;;N;;;;;
+FBE8;ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA INITIAL FORM;Lo;0;AL;<initial> 0649;;;;N;;;;;
+FBE9;ARABIC LETTER UIGHUR KAZAKH KIRGHIZ ALEF MAKSURA MEDIAL FORM;Lo;0;AL;<medial> 0649;;;;N;;;;;
+FBEA;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0626 0627;;;;N;;;;;
+FBEB;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF FINAL FORM;Lo;0;AL;<final> 0626 0627;;;;N;;;;;
+FBEC;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE ISOLATED FORM;Lo;0;AL;<isolated> 0626 06D5;;;;N;;;;;
+FBED;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH AE FINAL FORM;Lo;0;AL;<final> 0626 06D5;;;;N;;;;;
+FBEE;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW ISOLATED FORM;Lo;0;AL;<isolated> 0626 0648;;;;N;;;;;
+FBEF;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH WAW FINAL FORM;Lo;0;AL;<final> 0626 0648;;;;N;;;;;
+FBF0;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U ISOLATED FORM;Lo;0;AL;<isolated> 0626 06C7;;;;N;;;;;
+FBF1;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH U FINAL FORM;Lo;0;AL;<final> 0626 06C7;;;;N;;;;;
+FBF2;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE ISOLATED FORM;Lo;0;AL;<isolated> 0626 06C6;;;;N;;;;;
+FBF3;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH OE FINAL FORM;Lo;0;AL;<final> 0626 06C6;;;;N;;;;;
+FBF4;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU ISOLATED FORM;Lo;0;AL;<isolated> 0626 06C8;;;;N;;;;;
+FBF5;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YU FINAL FORM;Lo;0;AL;<final> 0626 06C8;;;;N;;;;;
+FBF6;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E ISOLATED FORM;Lo;0;AL;<isolated> 0626 06D0;;;;N;;;;;
+FBF7;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E FINAL FORM;Lo;0;AL;<final> 0626 06D0;;;;N;;;;;
+FBF8;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH E INITIAL FORM;Lo;0;AL;<initial> 0626 06D0;;;;N;;;;;
+FBF9;ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0626 0649;;;;N;;;;;
+FBFA;ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0626 0649;;;;N;;;;;
+FBFB;ARABIC LIGATURE UIGHUR KIRGHIZ YEH WITH HAMZA ABOVE WITH ALEF MAKSURA INITIAL FORM;Lo;0;AL;<initial> 0626 0649;;;;N;;;;;
+FBFC;ARABIC LETTER FARSI YEH ISOLATED FORM;Lo;0;AL;<isolated> 06CC;;;;N;;;;;
+FBFD;ARABIC LETTER FARSI YEH FINAL FORM;Lo;0;AL;<final> 06CC;;;;N;;;;;
+FBFE;ARABIC LETTER FARSI YEH INITIAL FORM;Lo;0;AL;<initial> 06CC;;;;N;;;;;
+FBFF;ARABIC LETTER FARSI YEH MEDIAL FORM;Lo;0;AL;<medial> 06CC;;;;N;;;;;
+FC00;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0626 062C;;;;N;;;;;
+FC01;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0626 062D;;;;N;;;;;
+FC02;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0626 0645;;;;N;;;;;
+FC03;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0626 0649;;;;N;;;;;
+FC04;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0626 064A;;;;N;;;;;
+FC05;ARABIC LIGATURE BEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0628 062C;;;;N;;;;;
+FC06;ARABIC LIGATURE BEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0628 062D;;;;N;;;;;
+FC07;ARABIC LIGATURE BEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0628 062E;;;;N;;;;;
+FC08;ARABIC LIGATURE BEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0628 0645;;;;N;;;;;
+FC09;ARABIC LIGATURE BEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0628 0649;;;;N;;;;;
+FC0A;ARABIC LIGATURE BEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0628 064A;;;;N;;;;;
+FC0B;ARABIC LIGATURE TEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062A 062C;;;;N;;;;;
+FC0C;ARABIC LIGATURE TEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 062A 062D;;;;N;;;;;
+FC0D;ARABIC LIGATURE TEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 062A 062E;;;;N;;;;;
+FC0E;ARABIC LIGATURE TEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062A 0645;;;;N;;;;;
+FC0F;ARABIC LIGATURE TEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062A 0649;;;;N;;;;;
+FC10;ARABIC LIGATURE TEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062A 064A;;;;N;;;;;
+FC11;ARABIC LIGATURE THEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062B 062C;;;;N;;;;;
+FC12;ARABIC LIGATURE THEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062B 0645;;;;N;;;;;
+FC13;ARABIC LIGATURE THEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062B 0649;;;;N;;;;;
+FC14;ARABIC LIGATURE THEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062B 064A;;;;N;;;;;
+FC15;ARABIC LIGATURE JEEM WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 062C 062D;;;;N;;;;;
+FC16;ARABIC LIGATURE JEEM WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062C 0645;;;;N;;;;;
+FC17;ARABIC LIGATURE HAH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062D 062C;;;;N;;;;;
+FC18;ARABIC LIGATURE HAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062D 0645;;;;N;;;;;
+FC19;ARABIC LIGATURE KHAH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062E 062C;;;;N;;;;;
+FC1A;ARABIC LIGATURE KHAH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 062E 062D;;;;N;;;;;
+FC1B;ARABIC LIGATURE KHAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 062E 0645;;;;N;;;;;
+FC1C;ARABIC LIGATURE SEEN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0633 062C;;;;N;;;;;
+FC1D;ARABIC LIGATURE SEEN WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0633 062D;;;;N;;;;;
+FC1E;ARABIC LIGATURE SEEN WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0633 062E;;;;N;;;;;
+FC1F;ARABIC LIGATURE SEEN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0633 0645;;;;N;;;;;
+FC20;ARABIC LIGATURE SAD WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0635 062D;;;;N;;;;;
+FC21;ARABIC LIGATURE SAD WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0635 0645;;;;N;;;;;
+FC22;ARABIC LIGATURE DAD WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0636 062C;;;;N;;;;;
+FC23;ARABIC LIGATURE DAD WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0636 062D;;;;N;;;;;
+FC24;ARABIC LIGATURE DAD WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0636 062E;;;;N;;;;;
+FC25;ARABIC LIGATURE DAD WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0636 0645;;;;N;;;;;
+FC26;ARABIC LIGATURE TAH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0637 062D;;;;N;;;;;
+FC27;ARABIC LIGATURE TAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0637 0645;;;;N;;;;;
+FC28;ARABIC LIGATURE ZAH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0638 0645;;;;N;;;;;
+FC29;ARABIC LIGATURE AIN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0639 062C;;;;N;;;;;
+FC2A;ARABIC LIGATURE AIN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0639 0645;;;;N;;;;;
+FC2B;ARABIC LIGATURE GHAIN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 063A 062C;;;;N;;;;;
+FC2C;ARABIC LIGATURE GHAIN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 063A 0645;;;;N;;;;;
+FC2D;ARABIC LIGATURE FEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0641 062C;;;;N;;;;;
+FC2E;ARABIC LIGATURE FEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0641 062D;;;;N;;;;;
+FC2F;ARABIC LIGATURE FEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0641 062E;;;;N;;;;;
+FC30;ARABIC LIGATURE FEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0641 0645;;;;N;;;;;
+FC31;ARABIC LIGATURE FEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0641 0649;;;;N;;;;;
+FC32;ARABIC LIGATURE FEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0641 064A;;;;N;;;;;
+FC33;ARABIC LIGATURE QAF WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0642 062D;;;;N;;;;;
+FC34;ARABIC LIGATURE QAF WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0642 0645;;;;N;;;;;
+FC35;ARABIC LIGATURE QAF WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0642 0649;;;;N;;;;;
+FC36;ARABIC LIGATURE QAF WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0642 064A;;;;N;;;;;
+FC37;ARABIC LIGATURE KAF WITH ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0643 0627;;;;N;;;;;
+FC38;ARABIC LIGATURE KAF WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0643 062C;;;;N;;;;;
+FC39;ARABIC LIGATURE KAF WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0643 062D;;;;N;;;;;
+FC3A;ARABIC LIGATURE KAF WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0643 062E;;;;N;;;;;
+FC3B;ARABIC LIGATURE KAF WITH LAM ISOLATED FORM;Lo;0;AL;<isolated> 0643 0644;;;;N;;;;;
+FC3C;ARABIC LIGATURE KAF WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0643 0645;;;;N;;;;;
+FC3D;ARABIC LIGATURE KAF WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0643 0649;;;;N;;;;;
+FC3E;ARABIC LIGATURE KAF WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0643 064A;;;;N;;;;;
+FC3F;ARABIC LIGATURE LAM WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0644 062C;;;;N;;;;;
+FC40;ARABIC LIGATURE LAM WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0644 062D;;;;N;;;;;
+FC41;ARABIC LIGATURE LAM WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0644 062E;;;;N;;;;;
+FC42;ARABIC LIGATURE LAM WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0644 0645;;;;N;;;;;
+FC43;ARABIC LIGATURE LAM WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0644 0649;;;;N;;;;;
+FC44;ARABIC LIGATURE LAM WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0644 064A;;;;N;;;;;
+FC45;ARABIC LIGATURE MEEM WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0645 062C;;;;N;;;;;
+FC46;ARABIC LIGATURE MEEM WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0645 062D;;;;N;;;;;
+FC47;ARABIC LIGATURE MEEM WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0645 062E;;;;N;;;;;
+FC48;ARABIC LIGATURE MEEM WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0645 0645;;;;N;;;;;
+FC49;ARABIC LIGATURE MEEM WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0645 0649;;;;N;;;;;
+FC4A;ARABIC LIGATURE MEEM WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0645 064A;;;;N;;;;;
+FC4B;ARABIC LIGATURE NOON WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0646 062C;;;;N;;;;;
+FC4C;ARABIC LIGATURE NOON WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0646 062D;;;;N;;;;;
+FC4D;ARABIC LIGATURE NOON WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0646 062E;;;;N;;;;;
+FC4E;ARABIC LIGATURE NOON WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0646 0645;;;;N;;;;;
+FC4F;ARABIC LIGATURE NOON WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0646 0649;;;;N;;;;;
+FC50;ARABIC LIGATURE NOON WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0646 064A;;;;N;;;;;
+FC51;ARABIC LIGATURE HEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0647 062C;;;;N;;;;;
+FC52;ARABIC LIGATURE HEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0647 0645;;;;N;;;;;
+FC53;ARABIC LIGATURE HEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0647 0649;;;;N;;;;;
+FC54;ARABIC LIGATURE HEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0647 064A;;;;N;;;;;
+FC55;ARABIC LIGATURE YEH WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 064A 062C;;;;N;;;;;
+FC56;ARABIC LIGATURE YEH WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 064A 062D;;;;N;;;;;
+FC57;ARABIC LIGATURE YEH WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 064A 062E;;;;N;;;;;
+FC58;ARABIC LIGATURE YEH WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 064A 0645;;;;N;;;;;
+FC59;ARABIC LIGATURE YEH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 064A 0649;;;;N;;;;;
+FC5A;ARABIC LIGATURE YEH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 064A 064A;;;;N;;;;;
+FC5B;ARABIC LIGATURE THAL WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0630 0670;;;;N;;;;;
+FC5C;ARABIC LIGATURE REH WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0631 0670;;;;N;;;;;
+FC5D;ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0649 0670;;;;N;;;;;
+FC5E;ARABIC LIGATURE SHADDA WITH DAMMATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064C 0651;;;;N;;;;;
+FC5F;ARABIC LIGATURE SHADDA WITH KASRATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064D 0651;;;;N;;;;;
+FC60;ARABIC LIGATURE SHADDA WITH FATHA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064E 0651;;;;N;;;;;
+FC61;ARABIC LIGATURE SHADDA WITH DAMMA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064F 0651;;;;N;;;;;
+FC62;ARABIC LIGATURE SHADDA WITH KASRA ISOLATED FORM;Lo;0;AL;<isolated> 0020 0650 0651;;;;N;;;;;
+FC63;ARABIC LIGATURE SHADDA WITH SUPERSCRIPT ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0020 0651 0670;;;;N;;;;;
+FC64;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH REH FINAL FORM;Lo;0;AL;<final> 0626 0631;;;;N;;;;;
+FC65;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ZAIN FINAL FORM;Lo;0;AL;<final> 0626 0632;;;;N;;;;;
+FC66;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM FINAL FORM;Lo;0;AL;<final> 0626 0645;;;;N;;;;;
+FC67;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH NOON FINAL FORM;Lo;0;AL;<final> 0626 0646;;;;N;;;;;
+FC68;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0626 0649;;;;N;;;;;
+FC69;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH YEH FINAL FORM;Lo;0;AL;<final> 0626 064A;;;;N;;;;;
+FC6A;ARABIC LIGATURE BEH WITH REH FINAL FORM;Lo;0;AL;<final> 0628 0631;;;;N;;;;;
+FC6B;ARABIC LIGATURE BEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 0628 0632;;;;N;;;;;
+FC6C;ARABIC LIGATURE BEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0628 0645;;;;N;;;;;
+FC6D;ARABIC LIGATURE BEH WITH NOON FINAL FORM;Lo;0;AL;<final> 0628 0646;;;;N;;;;;
+FC6E;ARABIC LIGATURE BEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0628 0649;;;;N;;;;;
+FC6F;ARABIC LIGATURE BEH WITH YEH FINAL FORM;Lo;0;AL;<final> 0628 064A;;;;N;;;;;
+FC70;ARABIC LIGATURE TEH WITH REH FINAL FORM;Lo;0;AL;<final> 062A 0631;;;;N;;;;;
+FC71;ARABIC LIGATURE TEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 062A 0632;;;;N;;;;;
+FC72;ARABIC LIGATURE TEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 062A 0645;;;;N;;;;;
+FC73;ARABIC LIGATURE TEH WITH NOON FINAL FORM;Lo;0;AL;<final> 062A 0646;;;;N;;;;;
+FC74;ARABIC LIGATURE TEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 0649;;;;N;;;;;
+FC75;ARABIC LIGATURE TEH WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 064A;;;;N;;;;;
+FC76;ARABIC LIGATURE THEH WITH REH FINAL FORM;Lo;0;AL;<final> 062B 0631;;;;N;;;;;
+FC77;ARABIC LIGATURE THEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 062B 0632;;;;N;;;;;
+FC78;ARABIC LIGATURE THEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 062B 0645;;;;N;;;;;
+FC79;ARABIC LIGATURE THEH WITH NOON FINAL FORM;Lo;0;AL;<final> 062B 0646;;;;N;;;;;
+FC7A;ARABIC LIGATURE THEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062B 0649;;;;N;;;;;
+FC7B;ARABIC LIGATURE THEH WITH YEH FINAL FORM;Lo;0;AL;<final> 062B 064A;;;;N;;;;;
+FC7C;ARABIC LIGATURE FEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0641 0649;;;;N;;;;;
+FC7D;ARABIC LIGATURE FEH WITH YEH FINAL FORM;Lo;0;AL;<final> 0641 064A;;;;N;;;;;
+FC7E;ARABIC LIGATURE QAF WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0642 0649;;;;N;;;;;
+FC7F;ARABIC LIGATURE QAF WITH YEH FINAL FORM;Lo;0;AL;<final> 0642 064A;;;;N;;;;;
+FC80;ARABIC LIGATURE KAF WITH ALEF FINAL FORM;Lo;0;AL;<final> 0643 0627;;;;N;;;;;
+FC81;ARABIC LIGATURE KAF WITH LAM FINAL FORM;Lo;0;AL;<final> 0643 0644;;;;N;;;;;
+FC82;ARABIC LIGATURE KAF WITH MEEM FINAL FORM;Lo;0;AL;<final> 0643 0645;;;;N;;;;;
+FC83;ARABIC LIGATURE KAF WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0643 0649;;;;N;;;;;
+FC84;ARABIC LIGATURE KAF WITH YEH FINAL FORM;Lo;0;AL;<final> 0643 064A;;;;N;;;;;
+FC85;ARABIC LIGATURE LAM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 0645;;;;N;;;;;
+FC86;ARABIC LIGATURE LAM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0644 0649;;;;N;;;;;
+FC87;ARABIC LIGATURE LAM WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 064A;;;;N;;;;;
+FC88;ARABIC LIGATURE MEEM WITH ALEF FINAL FORM;Lo;0;AL;<final> 0645 0627;;;;N;;;;;
+FC89;ARABIC LIGATURE MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0645 0645;;;;N;;;;;
+FC8A;ARABIC LIGATURE NOON WITH REH FINAL FORM;Lo;0;AL;<final> 0646 0631;;;;N;;;;;
+FC8B;ARABIC LIGATURE NOON WITH ZAIN FINAL FORM;Lo;0;AL;<final> 0646 0632;;;;N;;;;;
+FC8C;ARABIC LIGATURE NOON WITH MEEM FINAL FORM;Lo;0;AL;<final> 0646 0645;;;;N;;;;;
+FC8D;ARABIC LIGATURE NOON WITH NOON FINAL FORM;Lo;0;AL;<final> 0646 0646;;;;N;;;;;
+FC8E;ARABIC LIGATURE NOON WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 0649;;;;N;;;;;
+FC8F;ARABIC LIGATURE NOON WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 064A;;;;N;;;;;
+FC90;ARABIC LIGATURE ALEF MAKSURA WITH SUPERSCRIPT ALEF FINAL FORM;Lo;0;AL;<final> 0649 0670;;;;N;;;;;
+FC91;ARABIC LIGATURE YEH WITH REH FINAL FORM;Lo;0;AL;<final> 064A 0631;;;;N;;;;;
+FC92;ARABIC LIGATURE YEH WITH ZAIN FINAL FORM;Lo;0;AL;<final> 064A 0632;;;;N;;;;;
+FC93;ARABIC LIGATURE YEH WITH MEEM FINAL FORM;Lo;0;AL;<final> 064A 0645;;;;N;;;;;
+FC94;ARABIC LIGATURE YEH WITH NOON FINAL FORM;Lo;0;AL;<final> 064A 0646;;;;N;;;;;
+FC95;ARABIC LIGATURE YEH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 064A 0649;;;;N;;;;;
+FC96;ARABIC LIGATURE YEH WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 064A;;;;N;;;;;
+FC97;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0626 062C;;;;N;;;;;
+FC98;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0626 062D;;;;N;;;;;
+FC99;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0626 062E;;;;N;;;;;
+FC9A;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0626 0645;;;;N;;;;;
+FC9B;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0626 0647;;;;N;;;;;
+FC9C;ARABIC LIGATURE BEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0628 062C;;;;N;;;;;
+FC9D;ARABIC LIGATURE BEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0628 062D;;;;N;;;;;
+FC9E;ARABIC LIGATURE BEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0628 062E;;;;N;;;;;
+FC9F;ARABIC LIGATURE BEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0628 0645;;;;N;;;;;
+FCA0;ARABIC LIGATURE BEH WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0628 0647;;;;N;;;;;
+FCA1;ARABIC LIGATURE TEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062C;;;;N;;;;;
+FCA2;ARABIC LIGATURE TEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062A 062D;;;;N;;;;;
+FCA3;ARABIC LIGATURE TEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 062A 062E;;;;N;;;;;
+FCA4;ARABIC LIGATURE TEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 0645;;;;N;;;;;
+FCA5;ARABIC LIGATURE TEH WITH HEH INITIAL FORM;Lo;0;AL;<initial> 062A 0647;;;;N;;;;;
+FCA6;ARABIC LIGATURE THEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062B 0645;;;;N;;;;;
+FCA7;ARABIC LIGATURE JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062C 062D;;;;N;;;;;
+FCA8;ARABIC LIGATURE JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062C 0645;;;;N;;;;;
+FCA9;ARABIC LIGATURE HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062D 062C;;;;N;;;;;
+FCAA;ARABIC LIGATURE HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062D 0645;;;;N;;;;;
+FCAB;ARABIC LIGATURE KHAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062E 062C;;;;N;;;;;
+FCAC;ARABIC LIGATURE KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062E 0645;;;;N;;;;;
+FCAD;ARABIC LIGATURE SEEN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0633 062C;;;;N;;;;;
+FCAE;ARABIC LIGATURE SEEN WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0633 062D;;;;N;;;;;
+FCAF;ARABIC LIGATURE SEEN WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0633 062E;;;;N;;;;;
+FCB0;ARABIC LIGATURE SEEN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0633 0645;;;;N;;;;;
+FCB1;ARABIC LIGATURE SAD WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0635 062D;;;;N;;;;;
+FCB2;ARABIC LIGATURE SAD WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0635 062E;;;;N;;;;;
+FCB3;ARABIC LIGATURE SAD WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0635 0645;;;;N;;;;;
+FCB4;ARABIC LIGATURE DAD WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0636 062C;;;;N;;;;;
+FCB5;ARABIC LIGATURE DAD WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0636 062D;;;;N;;;;;
+FCB6;ARABIC LIGATURE DAD WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0636 062E;;;;N;;;;;
+FCB7;ARABIC LIGATURE DAD WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0636 0645;;;;N;;;;;
+FCB8;ARABIC LIGATURE TAH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0637 062D;;;;N;;;;;
+FCB9;ARABIC LIGATURE ZAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0638 0645;;;;N;;;;;
+FCBA;ARABIC LIGATURE AIN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0639 062C;;;;N;;;;;
+FCBB;ARABIC LIGATURE AIN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0639 0645;;;;N;;;;;
+FCBC;ARABIC LIGATURE GHAIN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 063A 062C;;;;N;;;;;
+FCBD;ARABIC LIGATURE GHAIN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 063A 0645;;;;N;;;;;
+FCBE;ARABIC LIGATURE FEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0641 062C;;;;N;;;;;
+FCBF;ARABIC LIGATURE FEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0641 062D;;;;N;;;;;
+FCC0;ARABIC LIGATURE FEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0641 062E;;;;N;;;;;
+FCC1;ARABIC LIGATURE FEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0641 0645;;;;N;;;;;
+FCC2;ARABIC LIGATURE QAF WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0642 062D;;;;N;;;;;
+FCC3;ARABIC LIGATURE QAF WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0642 0645;;;;N;;;;;
+FCC4;ARABIC LIGATURE KAF WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0643 062C;;;;N;;;;;
+FCC5;ARABIC LIGATURE KAF WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0643 062D;;;;N;;;;;
+FCC6;ARABIC LIGATURE KAF WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0643 062E;;;;N;;;;;
+FCC7;ARABIC LIGATURE KAF WITH LAM INITIAL FORM;Lo;0;AL;<initial> 0643 0644;;;;N;;;;;
+FCC8;ARABIC LIGATURE KAF WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0643 0645;;;;N;;;;;
+FCC9;ARABIC LIGATURE LAM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062C;;;;N;;;;;
+FCCA;ARABIC LIGATURE LAM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0644 062D;;;;N;;;;;
+FCCB;ARABIC LIGATURE LAM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0644 062E;;;;N;;;;;
+FCCC;ARABIC LIGATURE LAM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 0645;;;;N;;;;;
+FCCD;ARABIC LIGATURE LAM WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0644 0647;;;;N;;;;;
+FCCE;ARABIC LIGATURE MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062C;;;;N;;;;;
+FCCF;ARABIC LIGATURE MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0645 062D;;;;N;;;;;
+FCD0;ARABIC LIGATURE MEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0645 062E;;;;N;;;;;
+FCD1;ARABIC LIGATURE MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 0645;;;;N;;;;;
+FCD2;ARABIC LIGATURE NOON WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0646 062C;;;;N;;;;;
+FCD3;ARABIC LIGATURE NOON WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0646 062D;;;;N;;;;;
+FCD4;ARABIC LIGATURE NOON WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0646 062E;;;;N;;;;;
+FCD5;ARABIC LIGATURE NOON WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0646 0645;;;;N;;;;;
+FCD6;ARABIC LIGATURE NOON WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0646 0647;;;;N;;;;;
+FCD7;ARABIC LIGATURE HEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0647 062C;;;;N;;;;;
+FCD8;ARABIC LIGATURE HEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0647 0645;;;;N;;;;;
+FCD9;ARABIC LIGATURE HEH WITH SUPERSCRIPT ALEF INITIAL FORM;Lo;0;AL;<initial> 0647 0670;;;;N;;;;;
+FCDA;ARABIC LIGATURE YEH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 064A 062C;;;;N;;;;;
+FCDB;ARABIC LIGATURE YEH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 064A 062D;;;;N;;;;;
+FCDC;ARABIC LIGATURE YEH WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 064A 062E;;;;N;;;;;
+FCDD;ARABIC LIGATURE YEH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 064A 0645;;;;N;;;;;
+FCDE;ARABIC LIGATURE YEH WITH HEH INITIAL FORM;Lo;0;AL;<initial> 064A 0647;;;;N;;;;;
+FCDF;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0626 0645;;;;N;;;;;
+FCE0;ARABIC LIGATURE YEH WITH HAMZA ABOVE WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0626 0647;;;;N;;;;;
+FCE1;ARABIC LIGATURE BEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0628 0645;;;;N;;;;;
+FCE2;ARABIC LIGATURE BEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0628 0647;;;;N;;;;;
+FCE3;ARABIC LIGATURE TEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 062A 0645;;;;N;;;;;
+FCE4;ARABIC LIGATURE TEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 062A 0647;;;;N;;;;;
+FCE5;ARABIC LIGATURE THEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 062B 0645;;;;N;;;;;
+FCE6;ARABIC LIGATURE THEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 062B 0647;;;;N;;;;;
+FCE7;ARABIC LIGATURE SEEN WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0633 0645;;;;N;;;;;
+FCE8;ARABIC LIGATURE SEEN WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0633 0647;;;;N;;;;;
+FCE9;ARABIC LIGATURE SHEEN WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0634 0645;;;;N;;;;;
+FCEA;ARABIC LIGATURE SHEEN WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0634 0647;;;;N;;;;;
+FCEB;ARABIC LIGATURE KAF WITH LAM MEDIAL FORM;Lo;0;AL;<medial> 0643 0644;;;;N;;;;;
+FCEC;ARABIC LIGATURE KAF WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0643 0645;;;;N;;;;;
+FCED;ARABIC LIGATURE LAM WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0644 0645;;;;N;;;;;
+FCEE;ARABIC LIGATURE NOON WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0646 0645;;;;N;;;;;
+FCEF;ARABIC LIGATURE NOON WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 0646 0647;;;;N;;;;;
+FCF0;ARABIC LIGATURE YEH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 064A 0645;;;;N;;;;;
+FCF1;ARABIC LIGATURE YEH WITH HEH MEDIAL FORM;Lo;0;AL;<medial> 064A 0647;;;;N;;;;;
+FCF2;ARABIC LIGATURE SHADDA WITH FATHA MEDIAL FORM;Lo;0;AL;<medial> 0640 064E 0651;;;;N;;;;;
+FCF3;ARABIC LIGATURE SHADDA WITH DAMMA MEDIAL FORM;Lo;0;AL;<medial> 0640 064F 0651;;;;N;;;;;
+FCF4;ARABIC LIGATURE SHADDA WITH KASRA MEDIAL FORM;Lo;0;AL;<medial> 0640 0650 0651;;;;N;;;;;
+FCF5;ARABIC LIGATURE TAH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0637 0649;;;;N;;;;;
+FCF6;ARABIC LIGATURE TAH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0637 064A;;;;N;;;;;
+FCF7;ARABIC LIGATURE AIN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0639 0649;;;;N;;;;;
+FCF8;ARABIC LIGATURE AIN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0639 064A;;;;N;;;;;
+FCF9;ARABIC LIGATURE GHAIN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 063A 0649;;;;N;;;;;
+FCFA;ARABIC LIGATURE GHAIN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 063A 064A;;;;N;;;;;
+FCFB;ARABIC LIGATURE SEEN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0633 0649;;;;N;;;;;
+FCFC;ARABIC LIGATURE SEEN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0633 064A;;;;N;;;;;
+FCFD;ARABIC LIGATURE SHEEN WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0634 0649;;;;N;;;;;
+FCFE;ARABIC LIGATURE SHEEN WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0634 064A;;;;N;;;;;
+FCFF;ARABIC LIGATURE HAH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062D 0649;;;;N;;;;;
+FD00;ARABIC LIGATURE HAH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062D 064A;;;;N;;;;;
+FD01;ARABIC LIGATURE JEEM WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062C 0649;;;;N;;;;;
+FD02;ARABIC LIGATURE JEEM WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062C 064A;;;;N;;;;;
+FD03;ARABIC LIGATURE KHAH WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 062E 0649;;;;N;;;;;
+FD04;ARABIC LIGATURE KHAH WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 062E 064A;;;;N;;;;;
+FD05;ARABIC LIGATURE SAD WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0635 0649;;;;N;;;;;
+FD06;ARABIC LIGATURE SAD WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0635 064A;;;;N;;;;;
+FD07;ARABIC LIGATURE DAD WITH ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0636 0649;;;;N;;;;;
+FD08;ARABIC LIGATURE DAD WITH YEH ISOLATED FORM;Lo;0;AL;<isolated> 0636 064A;;;;N;;;;;
+FD09;ARABIC LIGATURE SHEEN WITH JEEM ISOLATED FORM;Lo;0;AL;<isolated> 0634 062C;;;;N;;;;;
+FD0A;ARABIC LIGATURE SHEEN WITH HAH ISOLATED FORM;Lo;0;AL;<isolated> 0634 062D;;;;N;;;;;
+FD0B;ARABIC LIGATURE SHEEN WITH KHAH ISOLATED FORM;Lo;0;AL;<isolated> 0634 062E;;;;N;;;;;
+FD0C;ARABIC LIGATURE SHEEN WITH MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0634 0645;;;;N;;;;;
+FD0D;ARABIC LIGATURE SHEEN WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0634 0631;;;;N;;;;;
+FD0E;ARABIC LIGATURE SEEN WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0633 0631;;;;N;;;;;
+FD0F;ARABIC LIGATURE SAD WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0635 0631;;;;N;;;;;
+FD10;ARABIC LIGATURE DAD WITH REH ISOLATED FORM;Lo;0;AL;<isolated> 0636 0631;;;;N;;;;;
+FD11;ARABIC LIGATURE TAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0637 0649;;;;N;;;;;
+FD12;ARABIC LIGATURE TAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0637 064A;;;;N;;;;;
+FD13;ARABIC LIGATURE AIN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0639 0649;;;;N;;;;;
+FD14;ARABIC LIGATURE AIN WITH YEH FINAL FORM;Lo;0;AL;<final> 0639 064A;;;;N;;;;;
+FD15;ARABIC LIGATURE GHAIN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 063A 0649;;;;N;;;;;
+FD16;ARABIC LIGATURE GHAIN WITH YEH FINAL FORM;Lo;0;AL;<final> 063A 064A;;;;N;;;;;
+FD17;ARABIC LIGATURE SEEN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0633 0649;;;;N;;;;;
+FD18;ARABIC LIGATURE SEEN WITH YEH FINAL FORM;Lo;0;AL;<final> 0633 064A;;;;N;;;;;
+FD19;ARABIC LIGATURE SHEEN WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0634 0649;;;;N;;;;;
+FD1A;ARABIC LIGATURE SHEEN WITH YEH FINAL FORM;Lo;0;AL;<final> 0634 064A;;;;N;;;;;
+FD1B;ARABIC LIGATURE HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062D 0649;;;;N;;;;;
+FD1C;ARABIC LIGATURE HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062D 064A;;;;N;;;;;
+FD1D;ARABIC LIGATURE JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062C 0649;;;;N;;;;;
+FD1E;ARABIC LIGATURE JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062C 064A;;;;N;;;;;
+FD1F;ARABIC LIGATURE KHAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062E 0649;;;;N;;;;;
+FD20;ARABIC LIGATURE KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062E 064A;;;;N;;;;;
+FD21;ARABIC LIGATURE SAD WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0635 0649;;;;N;;;;;
+FD22;ARABIC LIGATURE SAD WITH YEH FINAL FORM;Lo;0;AL;<final> 0635 064A;;;;N;;;;;
+FD23;ARABIC LIGATURE DAD WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0636 0649;;;;N;;;;;
+FD24;ARABIC LIGATURE DAD WITH YEH FINAL FORM;Lo;0;AL;<final> 0636 064A;;;;N;;;;;
+FD25;ARABIC LIGATURE SHEEN WITH JEEM FINAL FORM;Lo;0;AL;<final> 0634 062C;;;;N;;;;;
+FD26;ARABIC LIGATURE SHEEN WITH HAH FINAL FORM;Lo;0;AL;<final> 0634 062D;;;;N;;;;;
+FD27;ARABIC LIGATURE SHEEN WITH KHAH FINAL FORM;Lo;0;AL;<final> 0634 062E;;;;N;;;;;
+FD28;ARABIC LIGATURE SHEEN WITH MEEM FINAL FORM;Lo;0;AL;<final> 0634 0645;;;;N;;;;;
+FD29;ARABIC LIGATURE SHEEN WITH REH FINAL FORM;Lo;0;AL;<final> 0634 0631;;;;N;;;;;
+FD2A;ARABIC LIGATURE SEEN WITH REH FINAL FORM;Lo;0;AL;<final> 0633 0631;;;;N;;;;;
+FD2B;ARABIC LIGATURE SAD WITH REH FINAL FORM;Lo;0;AL;<final> 0635 0631;;;;N;;;;;
+FD2C;ARABIC LIGATURE DAD WITH REH FINAL FORM;Lo;0;AL;<final> 0636 0631;;;;N;;;;;
+FD2D;ARABIC LIGATURE SHEEN WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0634 062C;;;;N;;;;;
+FD2E;ARABIC LIGATURE SHEEN WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0634 062D;;;;N;;;;;
+FD2F;ARABIC LIGATURE SHEEN WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0634 062E;;;;N;;;;;
+FD30;ARABIC LIGATURE SHEEN WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0634 0645;;;;N;;;;;
+FD31;ARABIC LIGATURE SEEN WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0633 0647;;;;N;;;;;
+FD32;ARABIC LIGATURE SHEEN WITH HEH INITIAL FORM;Lo;0;AL;<initial> 0634 0647;;;;N;;;;;
+FD33;ARABIC LIGATURE TAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0637 0645;;;;N;;;;;
+FD34;ARABIC LIGATURE SEEN WITH JEEM MEDIAL FORM;Lo;0;AL;<medial> 0633 062C;;;;N;;;;;
+FD35;ARABIC LIGATURE SEEN WITH HAH MEDIAL FORM;Lo;0;AL;<medial> 0633 062D;;;;N;;;;;
+FD36;ARABIC LIGATURE SEEN WITH KHAH MEDIAL FORM;Lo;0;AL;<medial> 0633 062E;;;;N;;;;;
+FD37;ARABIC LIGATURE SHEEN WITH JEEM MEDIAL FORM;Lo;0;AL;<medial> 0634 062C;;;;N;;;;;
+FD38;ARABIC LIGATURE SHEEN WITH HAH MEDIAL FORM;Lo;0;AL;<medial> 0634 062D;;;;N;;;;;
+FD39;ARABIC LIGATURE SHEEN WITH KHAH MEDIAL FORM;Lo;0;AL;<medial> 0634 062E;;;;N;;;;;
+FD3A;ARABIC LIGATURE TAH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0637 0645;;;;N;;;;;
+FD3B;ARABIC LIGATURE ZAH WITH MEEM MEDIAL FORM;Lo;0;AL;<medial> 0638 0645;;;;N;;;;;
+FD3C;ARABIC LIGATURE ALEF WITH FATHATAN FINAL FORM;Lo;0;AL;<final> 0627 064B;;;;N;;;;;
+FD3D;ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM;Lo;0;AL;<isolated> 0627 064B;;;;N;;;;;
+FD3E;ORNATE LEFT PARENTHESIS;Ps;0;ON;;;;;N;;;;;
+FD3F;ORNATE RIGHT PARENTHESIS;Pe;0;ON;;;;;N;;;;;
+FD50;ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062C 0645;;;;N;;;;;
+FD51;ARABIC LIGATURE TEH WITH HAH WITH JEEM FINAL FORM;Lo;0;AL;<final> 062A 062D 062C;;;;N;;;;;
+FD52;ARABIC LIGATURE TEH WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062D 062C;;;;N;;;;;
+FD53;ARABIC LIGATURE TEH WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062D 0645;;;;N;;;;;
+FD54;ARABIC LIGATURE TEH WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 062A 062E 0645;;;;N;;;;;
+FD55;ARABIC LIGATURE TEH WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 062A 0645 062C;;;;N;;;;;
+FD56;ARABIC LIGATURE TEH WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062A 0645 062D;;;;N;;;;;
+FD57;ARABIC LIGATURE TEH WITH MEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 062A 0645 062E;;;;N;;;;;
+FD58;ARABIC LIGATURE JEEM WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 062C 0645 062D;;;;N;;;;;
+FD59;ARABIC LIGATURE JEEM WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 062C 0645 062D;;;;N;;;;;
+FD5A;ARABIC LIGATURE HAH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062D 0645 064A;;;;N;;;;;
+FD5B;ARABIC LIGATURE HAH WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062D 0645 0649;;;;N;;;;;
+FD5C;ARABIC LIGATURE SEEN WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0633 062D 062C;;;;N;;;;;
+FD5D;ARABIC LIGATURE SEEN WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0633 062C 062D;;;;N;;;;;
+FD5E;ARABIC LIGATURE SEEN WITH JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0633 062C 0649;;;;N;;;;;
+FD5F;ARABIC LIGATURE SEEN WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0633 0645 062D;;;;N;;;;;
+FD60;ARABIC LIGATURE SEEN WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0633 0645 062D;;;;N;;;;;
+FD61;ARABIC LIGATURE SEEN WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0633 0645 062C;;;;N;;;;;
+FD62;ARABIC LIGATURE SEEN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0633 0645 0645;;;;N;;;;;
+FD63;ARABIC LIGATURE SEEN WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0633 0645 0645;;;;N;;;;;
+FD64;ARABIC LIGATURE SAD WITH HAH WITH HAH FINAL FORM;Lo;0;AL;<final> 0635 062D 062D;;;;N;;;;;
+FD65;ARABIC LIGATURE SAD WITH HAH WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0635 062D 062D;;;;N;;;;;
+FD66;ARABIC LIGATURE SAD WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0635 0645 0645;;;;N;;;;;
+FD67;ARABIC LIGATURE SHEEN WITH HAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0634 062D 0645;;;;N;;;;;
+FD68;ARABIC LIGATURE SHEEN WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0634 062D 0645;;;;N;;;;;
+FD69;ARABIC LIGATURE SHEEN WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0634 062C 064A;;;;N;;;;;
+FD6A;ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH FINAL FORM;Lo;0;AL;<final> 0634 0645 062E;;;;N;;;;;
+FD6B;ARABIC LIGATURE SHEEN WITH MEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0634 0645 062E;;;;N;;;;;
+FD6C;ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0634 0645 0645;;;;N;;;;;
+FD6D;ARABIC LIGATURE SHEEN WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0634 0645 0645;;;;N;;;;;
+FD6E;ARABIC LIGATURE DAD WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0636 062D 0649;;;;N;;;;;
+FD6F;ARABIC LIGATURE DAD WITH KHAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0636 062E 0645;;;;N;;;;;
+FD70;ARABIC LIGATURE DAD WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0636 062E 0645;;;;N;;;;;
+FD71;ARABIC LIGATURE TAH WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0637 0645 062D;;;;N;;;;;
+FD72;ARABIC LIGATURE TAH WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0637 0645 062D;;;;N;;;;;
+FD73;ARABIC LIGATURE TAH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0637 0645 0645;;;;N;;;;;
+FD74;ARABIC LIGATURE TAH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0637 0645 064A;;;;N;;;;;
+FD75;ARABIC LIGATURE AIN WITH JEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0639 062C 0645;;;;N;;;;;
+FD76;ARABIC LIGATURE AIN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0639 0645 0645;;;;N;;;;;
+FD77;ARABIC LIGATURE AIN WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0639 0645 0645;;;;N;;;;;
+FD78;ARABIC LIGATURE AIN WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0639 0645 0649;;;;N;;;;;
+FD79;ARABIC LIGATURE GHAIN WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 063A 0645 0645;;;;N;;;;;
+FD7A;ARABIC LIGATURE GHAIN WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 063A 0645 064A;;;;N;;;;;
+FD7B;ARABIC LIGATURE GHAIN WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 063A 0645 0649;;;;N;;;;;
+FD7C;ARABIC LIGATURE FEH WITH KHAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0641 062E 0645;;;;N;;;;;
+FD7D;ARABIC LIGATURE FEH WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0641 062E 0645;;;;N;;;;;
+FD7E;ARABIC LIGATURE QAF WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0642 0645 062D;;;;N;;;;;
+FD7F;ARABIC LIGATURE QAF WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0642 0645 0645;;;;N;;;;;
+FD80;ARABIC LIGATURE LAM WITH HAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 062D 0645;;;;N;;;;;
+FD81;ARABIC LIGATURE LAM WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 062D 064A;;;;N;;;;;
+FD82;ARABIC LIGATURE LAM WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0644 062D 0649;;;;N;;;;;
+FD83;ARABIC LIGATURE LAM WITH JEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062C 062C;;;;N;;;;;
+FD84;ARABIC LIGATURE LAM WITH JEEM WITH JEEM FINAL FORM;Lo;0;AL;<final> 0644 062C 062C;;;;N;;;;;
+FD85;ARABIC LIGATURE LAM WITH KHAH WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 062E 0645;;;;N;;;;;
+FD86;ARABIC LIGATURE LAM WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062E 0645;;;;N;;;;;
+FD87;ARABIC LIGATURE LAM WITH MEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0644 0645 062D;;;;N;;;;;
+FD88;ARABIC LIGATURE LAM WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0644 0645 062D;;;;N;;;;;
+FD89;ARABIC LIGATURE MEEM WITH HAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062D 062C;;;;N;;;;;
+FD8A;ARABIC LIGATURE MEEM WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062D 0645;;;;N;;;;;
+FD8B;ARABIC LIGATURE MEEM WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 062D 064A;;;;N;;;;;
+FD8C;ARABIC LIGATURE MEEM WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0645 062C 062D;;;;N;;;;;
+FD8D;ARABIC LIGATURE MEEM WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062C 0645;;;;N;;;;;
+FD8E;ARABIC LIGATURE MEEM WITH KHAH WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062E 062C;;;;N;;;;;
+FD8F;ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0645 062E 0645;;;;N;;;;;
+FD92;ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM;Lo;0;AL;<initial> 0645 062C 062E;;;;N;;;;;
+FD93;ARABIC LIGATURE HEH WITH MEEM WITH JEEM INITIAL FORM;Lo;0;AL;<initial> 0647 0645 062C;;;;N;;;;;
+FD94;ARABIC LIGATURE HEH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0647 0645 0645;;;;N;;;;;
+FD95;ARABIC LIGATURE NOON WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0646 062D 0645;;;;N;;;;;
+FD96;ARABIC LIGATURE NOON WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 062D 0649;;;;N;;;;;
+FD97;ARABIC LIGATURE NOON WITH JEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0646 062C 0645;;;;N;;;;;
+FD98;ARABIC LIGATURE NOON WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0646 062C 0645;;;;N;;;;;
+FD99;ARABIC LIGATURE NOON WITH JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 062C 0649;;;;N;;;;;
+FD9A;ARABIC LIGATURE NOON WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 0645 064A;;;;N;;;;;
+FD9B;ARABIC LIGATURE NOON WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0646 0645 0649;;;;N;;;;;
+FD9C;ARABIC LIGATURE YEH WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 064A 0645 0645;;;;N;;;;;
+FD9D;ARABIC LIGATURE YEH WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 064A 0645 0645;;;;N;;;;;
+FD9E;ARABIC LIGATURE BEH WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0628 062E 064A;;;;N;;;;;
+FD9F;ARABIC LIGATURE TEH WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 062C 064A;;;;N;;;;;
+FDA0;ARABIC LIGATURE TEH WITH JEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 062C 0649;;;;N;;;;;
+FDA1;ARABIC LIGATURE TEH WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 062E 064A;;;;N;;;;;
+FDA2;ARABIC LIGATURE TEH WITH KHAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 062E 0649;;;;N;;;;;
+FDA3;ARABIC LIGATURE TEH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062A 0645 064A;;;;N;;;;;
+FDA4;ARABIC LIGATURE TEH WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062A 0645 0649;;;;N;;;;;
+FDA5;ARABIC LIGATURE JEEM WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062C 0645 064A;;;;N;;;;;
+FDA6;ARABIC LIGATURE JEEM WITH HAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062C 062D 0649;;;;N;;;;;
+FDA7;ARABIC LIGATURE JEEM WITH MEEM WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 062C 0645 0649;;;;N;;;;;
+FDA8;ARABIC LIGATURE SEEN WITH KHAH WITH ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0633 062E 0649;;;;N;;;;;
+FDA9;ARABIC LIGATURE SAD WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0635 062D 064A;;;;N;;;;;
+FDAA;ARABIC LIGATURE SHEEN WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0634 062D 064A;;;;N;;;;;
+FDAB;ARABIC LIGATURE DAD WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0636 062D 064A;;;;N;;;;;
+FDAC;ARABIC LIGATURE LAM WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 062C 064A;;;;N;;;;;
+FDAD;ARABIC LIGATURE LAM WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0644 0645 064A;;;;N;;;;;
+FDAE;ARABIC LIGATURE YEH WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 062D 064A;;;;N;;;;;
+FDAF;ARABIC LIGATURE YEH WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 062C 064A;;;;N;;;;;
+FDB0;ARABIC LIGATURE YEH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 064A 0645 064A;;;;N;;;;;
+FDB1;ARABIC LIGATURE MEEM WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 0645 064A;;;;N;;;;;
+FDB2;ARABIC LIGATURE QAF WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0642 0645 064A;;;;N;;;;;
+FDB3;ARABIC LIGATURE NOON WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 062D 064A;;;;N;;;;;
+FDB4;ARABIC LIGATURE QAF WITH MEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0642 0645 062D;;;;N;;;;;
+FDB5;ARABIC LIGATURE LAM WITH HAH WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062D 0645;;;;N;;;;;
+FDB6;ARABIC LIGATURE AIN WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0639 0645 064A;;;;N;;;;;
+FDB7;ARABIC LIGATURE KAF WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0643 0645 064A;;;;N;;;;;
+FDB8;ARABIC LIGATURE NOON WITH JEEM WITH HAH INITIAL FORM;Lo;0;AL;<initial> 0646 062C 062D;;;;N;;;;;
+FDB9;ARABIC LIGATURE MEEM WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 062E 064A;;;;N;;;;;
+FDBA;ARABIC LIGATURE LAM WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0644 062C 0645;;;;N;;;;;
+FDBB;ARABIC LIGATURE KAF WITH MEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0643 0645 0645;;;;N;;;;;
+FDBC;ARABIC LIGATURE LAM WITH JEEM WITH MEEM FINAL FORM;Lo;0;AL;<final> 0644 062C 0645;;;;N;;;;;
+FDBD;ARABIC LIGATURE NOON WITH JEEM WITH HAH FINAL FORM;Lo;0;AL;<final> 0646 062C 062D;;;;N;;;;;
+FDBE;ARABIC LIGATURE JEEM WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 062C 062D 064A;;;;N;;;;;
+FDBF;ARABIC LIGATURE HAH WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 062D 062C 064A;;;;N;;;;;
+FDC0;ARABIC LIGATURE MEEM WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0645 062C 064A;;;;N;;;;;
+FDC1;ARABIC LIGATURE FEH WITH MEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0641 0645 064A;;;;N;;;;;
+FDC2;ARABIC LIGATURE BEH WITH HAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0628 062D 064A;;;;N;;;;;
+FDC3;ARABIC LIGATURE KAF WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0643 0645 0645;;;;N;;;;;
+FDC4;ARABIC LIGATURE AIN WITH JEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0639 062C 0645;;;;N;;;;;
+FDC5;ARABIC LIGATURE SAD WITH MEEM WITH MEEM INITIAL FORM;Lo;0;AL;<initial> 0635 0645 0645;;;;N;;;;;
+FDC6;ARABIC LIGATURE SEEN WITH KHAH WITH YEH FINAL FORM;Lo;0;AL;<final> 0633 062E 064A;;;;N;;;;;
+FDC7;ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM;Lo;0;AL;<final> 0646 062C 064A;;;;N;;;;;
+FDF0;ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL;<isolated> 0635 0644 06D2;;;;N;;;;;
+FDF1;ARABIC LIGATURE QALA USED AS KORANIC STOP SIGN ISOLATED FORM;Lo;0;AL;<isolated> 0642 0644 06D2;;;;N;;;;;
+FDF2;ARABIC LIGATURE ALLAH ISOLATED FORM;Lo;0;AL;<isolated> 0627 0644 0644 0647;;;;N;;;;;
+FDF3;ARABIC LIGATURE AKBAR ISOLATED FORM;Lo;0;AL;<isolated> 0627 0643 0628 0631;;;;N;;;;;
+FDF4;ARABIC LIGATURE MOHAMMAD ISOLATED FORM;Lo;0;AL;<isolated> 0645 062D 0645 062F;;;;N;;;;;
+FDF5;ARABIC LIGATURE SALAM ISOLATED FORM;Lo;0;AL;<isolated> 0635 0644 0639 0645;;;;N;;;;;
+FDF6;ARABIC LIGATURE RASOUL ISOLATED FORM;Lo;0;AL;<isolated> 0631 0633 0648 0644;;;;N;;;;;
+FDF7;ARABIC LIGATURE ALAYHE ISOLATED FORM;Lo;0;AL;<isolated> 0639 0644 064A 0647;;;;N;;;;;
+FDF8;ARABIC LIGATURE WASALLAM ISOLATED FORM;Lo;0;AL;<isolated> 0648 0633 0644 0645;;;;N;;;;;
+FDF9;ARABIC LIGATURE SALLA ISOLATED FORM;Lo;0;AL;<isolated> 0635 0644 0649;;;;N;;;;;
+FDFA;ARABIC LIGATURE SALLALLAHOU ALAYHE WASALLAM;Lo;0;AL;<isolated> 0635 0644 0649 0020 0627 0644 0644 0647 0020 0639 0644 064A 0647 0020 0648 0633 0644 0645;;;;N;ARABIC LETTER SALLALLAHOU ALAYHE WASALLAM;;;;
+FDFB;ARABIC LIGATURE JALLAJALALOUHOU;Lo;0;AL;<isolated> 062C 0644 0020 062C 0644 0627 0644 0647;;;;N;ARABIC LETTER JALLAJALALOUHOU;;;;
+FDFC;RIAL SIGN;Sc;0;AL;<isolated> 0631 06CC 0627 0644;;;;N;;;;;
+FE00;VARIATION SELECTOR-1;Mn;0;NSM;;;;;N;;;;;
+FE01;VARIATION SELECTOR-2;Mn;0;NSM;;;;;N;;;;;
+FE02;VARIATION SELECTOR-3;Mn;0;NSM;;;;;N;;;;;
+FE03;VARIATION SELECTOR-4;Mn;0;NSM;;;;;N;;;;;
+FE04;VARIATION SELECTOR-5;Mn;0;NSM;;;;;N;;;;;
+FE05;VARIATION SELECTOR-6;Mn;0;NSM;;;;;N;;;;;
+FE06;VARIATION SELECTOR-7;Mn;0;NSM;;;;;N;;;;;
+FE07;VARIATION SELECTOR-8;Mn;0;NSM;;;;;N;;;;;
+FE08;VARIATION SELECTOR-9;Mn;0;NSM;;;;;N;;;;;
+FE09;VARIATION SELECTOR-10;Mn;0;NSM;;;;;N;;;;;
+FE0A;VARIATION SELECTOR-11;Mn;0;NSM;;;;;N;;;;;
+FE0B;VARIATION SELECTOR-12;Mn;0;NSM;;;;;N;;;;;
+FE0C;VARIATION SELECTOR-13;Mn;0;NSM;;;;;N;;;;;
+FE0D;VARIATION SELECTOR-14;Mn;0;NSM;;;;;N;;;;;
+FE0E;VARIATION SELECTOR-15;Mn;0;NSM;;;;;N;;;;;
+FE0F;VARIATION SELECTOR-16;Mn;0;NSM;;;;;N;;;;;
+FE20;COMBINING LIGATURE LEFT HALF;Mn;230;NSM;;;;;N;;;;;
+FE21;COMBINING LIGATURE RIGHT HALF;Mn;230;NSM;;;;;N;;;;;
+FE22;COMBINING DOUBLE TILDE LEFT HALF;Mn;230;NSM;;;;;N;;;;;
+FE23;COMBINING DOUBLE TILDE RIGHT HALF;Mn;230;NSM;;;;;N;;;;;
+FE30;PRESENTATION FORM FOR VERTICAL TWO DOT LEADER;Po;0;ON;<vertical> 2025;;;;N;GLYPH FOR VERTICAL TWO DOT LEADER;;;;
+FE31;PRESENTATION FORM FOR VERTICAL EM DASH;Pd;0;ON;<vertical> 2014;;;;N;GLYPH FOR VERTICAL EM DASH;;;;
+FE32;PRESENTATION FORM FOR VERTICAL EN DASH;Pd;0;ON;<vertical> 2013;;;;N;GLYPH FOR VERTICAL EN DASH;;;;
+FE33;PRESENTATION FORM FOR VERTICAL LOW LINE;Pc;0;ON;<vertical> 005F;;;;N;GLYPH FOR VERTICAL SPACING UNDERSCORE;;;;
+FE34;PRESENTATION FORM FOR VERTICAL WAVY LOW LINE;Pc;0;ON;<vertical> 005F;;;;N;GLYPH FOR VERTICAL SPACING WAVY UNDERSCORE;;;;
+FE35;PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS;Ps;0;ON;<vertical> 0028;;;;N;GLYPH FOR VERTICAL OPENING PARENTHESIS;;;;
+FE36;PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS;Pe;0;ON;<vertical> 0029;;;;N;GLYPH FOR VERTICAL CLOSING PARENTHESIS;;;;
+FE37;PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET;Ps;0;ON;<vertical> 007B;;;;N;GLYPH FOR VERTICAL OPENING CURLY BRACKET;;;;
+FE38;PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET;Pe;0;ON;<vertical> 007D;;;;N;GLYPH FOR VERTICAL CLOSING CURLY BRACKET;;;;
+FE39;PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET;Ps;0;ON;<vertical> 3014;;;;N;GLYPH FOR VERTICAL OPENING TORTOISE SHELL BRACKET;;;;
+FE3A;PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET;Pe;0;ON;<vertical> 3015;;;;N;GLYPH FOR VERTICAL CLOSING TORTOISE SHELL BRACKET;;;;
+FE3B;PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET;Ps;0;ON;<vertical> 3010;;;;N;GLYPH FOR VERTICAL OPENING BLACK LENTICULAR BRACKET;;;;
+FE3C;PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET;Pe;0;ON;<vertical> 3011;;;;N;GLYPH FOR VERTICAL CLOSING BLACK LENTICULAR BRACKET;;;;
+FE3D;PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET;Ps;0;ON;<vertical> 300A;;;;N;GLYPH FOR VERTICAL OPENING DOUBLE ANGLE BRACKET;;;;
+FE3E;PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET;Pe;0;ON;<vertical> 300B;;;;N;GLYPH FOR VERTICAL CLOSING DOUBLE ANGLE BRACKET;;;;
+FE3F;PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET;Ps;0;ON;<vertical> 3008;;;;N;GLYPH FOR VERTICAL OPENING ANGLE BRACKET;;;;
+FE40;PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET;Pe;0;ON;<vertical> 3009;;;;N;GLYPH FOR VERTICAL CLOSING ANGLE BRACKET;;;;
+FE41;PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET;Ps;0;ON;<vertical> 300C;;;;N;GLYPH FOR VERTICAL OPENING CORNER BRACKET;;;;
+FE42;PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET;Pe;0;ON;<vertical> 300D;;;;N;GLYPH FOR VERTICAL CLOSING CORNER BRACKET;;;;
+FE43;PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET;Ps;0;ON;<vertical> 300E;;;;N;GLYPH FOR VERTICAL OPENING WHITE CORNER BRACKET;;;;
+FE44;PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET;Pe;0;ON;<vertical> 300F;;;;N;GLYPH FOR VERTICAL CLOSING WHITE CORNER BRACKET;;;;
+FE45;SESAME DOT;Po;0;ON;;;;;N;;;;;
+FE46;WHITE SESAME DOT;Po;0;ON;;;;;N;;;;;
+FE49;DASHED OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING DASHED OVERSCORE;;;;
+FE4A;CENTRELINE OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING CENTERLINE OVERSCORE;;;;
+FE4B;WAVY OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING WAVY OVERSCORE;;;;
+FE4C;DOUBLE WAVY OVERLINE;Po;0;ON;<compat> 203E;;;;N;SPACING DOUBLE WAVY OVERSCORE;;;;
+FE4D;DASHED LOW LINE;Pc;0;ON;<compat> 005F;;;;N;SPACING DASHED UNDERSCORE;;;;
+FE4E;CENTRELINE LOW LINE;Pc;0;ON;<compat> 005F;;;;N;SPACING CENTERLINE UNDERSCORE;;;;
+FE4F;WAVY LOW LINE;Pc;0;ON;<compat> 005F;;;;N;SPACING WAVY UNDERSCORE;;;;
+FE50;SMALL COMMA;Po;0;CS;<small> 002C;;;;N;;;;;
+FE51;SMALL IDEOGRAPHIC COMMA;Po;0;ON;<small> 3001;;;;N;;;;;
+FE52;SMALL FULL STOP;Po;0;CS;<small> 002E;;;;N;SMALL PERIOD;;;;
+FE54;SMALL SEMICOLON;Po;0;ON;<small> 003B;;;;N;;;;;
+FE55;SMALL COLON;Po;0;CS;<small> 003A;;;;N;;;;;
+FE56;SMALL QUESTION MARK;Po;0;ON;<small> 003F;;;;N;;;;;
+FE57;SMALL EXCLAMATION MARK;Po;0;ON;<small> 0021;;;;N;;;;;
+FE58;SMALL EM DASH;Pd;0;ON;<small> 2014;;;;N;;;;;
+FE59;SMALL LEFT PARENTHESIS;Ps;0;ON;<small> 0028;;;;N;SMALL OPENING PARENTHESIS;;;;
+FE5A;SMALL RIGHT PARENTHESIS;Pe;0;ON;<small> 0029;;;;N;SMALL CLOSING PARENTHESIS;;;;
+FE5B;SMALL LEFT CURLY BRACKET;Ps;0;ON;<small> 007B;;;;N;SMALL OPENING CURLY BRACKET;;;;
+FE5C;SMALL RIGHT CURLY BRACKET;Pe;0;ON;<small> 007D;;;;N;SMALL CLOSING CURLY BRACKET;;;;
+FE5D;SMALL LEFT TORTOISE SHELL BRACKET;Ps;0;ON;<small> 3014;;;;N;SMALL OPENING TORTOISE SHELL BRACKET;;;;
+FE5E;SMALL RIGHT TORTOISE SHELL BRACKET;Pe;0;ON;<small> 3015;;;;N;SMALL CLOSING TORTOISE SHELL BRACKET;;;;
+FE5F;SMALL NUMBER SIGN;Po;0;ET;<small> 0023;;;;N;;;;;
+FE60;SMALL AMPERSAND;Po;0;ON;<small> 0026;;;;N;;;;;
+FE61;SMALL ASTERISK;Po;0;ON;<small> 002A;;;;N;;;;;
+FE62;SMALL PLUS SIGN;Sm;0;ET;<small> 002B;;;;N;;;;;
+FE63;SMALL HYPHEN-MINUS;Pd;0;ET;<small> 002D;;;;N;;;;;
+FE64;SMALL LESS-THAN SIGN;Sm;0;ON;<small> 003C;;;;N;;;;;
+FE65;SMALL GREATER-THAN SIGN;Sm;0;ON;<small> 003E;;;;N;;;;;
+FE66;SMALL EQUALS SIGN;Sm;0;ON;<small> 003D;;;;N;;;;;
+FE68;SMALL REVERSE SOLIDUS;Po;0;ON;<small> 005C;;;;N;SMALL BACKSLASH;;;;
+FE69;SMALL DOLLAR SIGN;Sc;0;ET;<small> 0024;;;;N;;;;;
+FE6A;SMALL PERCENT SIGN;Po;0;ET;<small> 0025;;;;N;;;;;
+FE6B;SMALL COMMERCIAL AT;Po;0;ON;<small> 0040;;;;N;;;;;
+FE70;ARABIC FATHATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064B;;;;N;ARABIC SPACING FATHATAN;;;;
+FE71;ARABIC TATWEEL WITH FATHATAN ABOVE;Lo;0;AL;<medial> 0640 064B;;;;N;ARABIC FATHATAN ON TATWEEL;;;;
+FE72;ARABIC DAMMATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064C;;;;N;ARABIC SPACING DAMMATAN;;;;
+FE73;ARABIC TAIL FRAGMENT;Lo;0;AL;;;;;N;;;;;
+FE74;ARABIC KASRATAN ISOLATED FORM;Lo;0;AL;<isolated> 0020 064D;;;;N;ARABIC SPACING KASRATAN;;;;
+FE76;ARABIC FATHA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064E;;;;N;ARABIC SPACING FATHAH;;;;
+FE77;ARABIC FATHA MEDIAL FORM;Lo;0;AL;<medial> 0640 064E;;;;N;ARABIC FATHAH ON TATWEEL;;;;
+FE78;ARABIC DAMMA ISOLATED FORM;Lo;0;AL;<isolated> 0020 064F;;;;N;ARABIC SPACING DAMMAH;;;;
+FE79;ARABIC DAMMA MEDIAL FORM;Lo;0;AL;<medial> 0640 064F;;;;N;ARABIC DAMMAH ON TATWEEL;;;;
+FE7A;ARABIC KASRA ISOLATED FORM;Lo;0;AL;<isolated> 0020 0650;;;;N;ARABIC SPACING KASRAH;;;;
+FE7B;ARABIC KASRA MEDIAL FORM;Lo;0;AL;<medial> 0640 0650;;;;N;ARABIC KASRAH ON TATWEEL;;;;
+FE7C;ARABIC SHADDA ISOLATED FORM;Lo;0;AL;<isolated> 0020 0651;;;;N;ARABIC SPACING SHADDAH;;;;
+FE7D;ARABIC SHADDA MEDIAL FORM;Lo;0;AL;<medial> 0640 0651;;;;N;ARABIC SHADDAH ON TATWEEL;;;;
+FE7E;ARABIC SUKUN ISOLATED FORM;Lo;0;AL;<isolated> 0020 0652;;;;N;ARABIC SPACING SUKUN;;;;
+FE7F;ARABIC SUKUN MEDIAL FORM;Lo;0;AL;<medial> 0640 0652;;;;N;ARABIC SUKUN ON TATWEEL;;;;
+FE80;ARABIC LETTER HAMZA ISOLATED FORM;Lo;0;AL;<isolated> 0621;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH;;;;
+FE81;ARABIC LETTER ALEF WITH MADDA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0622;;;;N;GLYPH FOR ISOLATE ARABIC MADDAH ON ALEF;;;;
+FE82;ARABIC LETTER ALEF WITH MADDA ABOVE FINAL FORM;Lo;0;AL;<final> 0622;;;;N;GLYPH FOR FINAL ARABIC MADDAH ON ALEF;;;;
+FE83;ARABIC LETTER ALEF WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0623;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON ALEF;;;;
+FE84;ARABIC LETTER ALEF WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0623;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON ALEF;;;;
+FE85;ARABIC LETTER WAW WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0624;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON WAW;;;;
+FE86;ARABIC LETTER WAW WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0624;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON WAW;;;;
+FE87;ARABIC LETTER ALEF WITH HAMZA BELOW ISOLATED FORM;Lo;0;AL;<isolated> 0625;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH UNDER ALEF;;;;
+FE88;ARABIC LETTER ALEF WITH HAMZA BELOW FINAL FORM;Lo;0;AL;<final> 0625;;;;N;GLYPH FOR FINAL ARABIC HAMZAH UNDER ALEF;;;;
+FE89;ARABIC LETTER YEH WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0626;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON YA;;;;
+FE8A;ARABIC LETTER YEH WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0626;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON YA;;;;
+FE8B;ARABIC LETTER YEH WITH HAMZA ABOVE INITIAL FORM;Lo;0;AL;<initial> 0626;;;;N;GLYPH FOR INITIAL ARABIC HAMZAH ON YA;;;;
+FE8C;ARABIC LETTER YEH WITH HAMZA ABOVE MEDIAL FORM;Lo;0;AL;<medial> 0626;;;;N;GLYPH FOR MEDIAL ARABIC HAMZAH ON YA;;;;
+FE8D;ARABIC LETTER ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0627;;;;N;GLYPH FOR ISOLATE ARABIC ALEF;;;;
+FE8E;ARABIC LETTER ALEF FINAL FORM;Lo;0;AL;<final> 0627;;;;N;GLYPH FOR FINAL ARABIC ALEF;;;;
+FE8F;ARABIC LETTER BEH ISOLATED FORM;Lo;0;AL;<isolated> 0628;;;;N;GLYPH FOR ISOLATE ARABIC BAA;;;;
+FE90;ARABIC LETTER BEH FINAL FORM;Lo;0;AL;<final> 0628;;;;N;GLYPH FOR FINAL ARABIC BAA;;;;
+FE91;ARABIC LETTER BEH INITIAL FORM;Lo;0;AL;<initial> 0628;;;;N;GLYPH FOR INITIAL ARABIC BAA;;;;
+FE92;ARABIC LETTER BEH MEDIAL FORM;Lo;0;AL;<medial> 0628;;;;N;GLYPH FOR MEDIAL ARABIC BAA;;;;
+FE93;ARABIC LETTER TEH MARBUTA ISOLATED FORM;Lo;0;AL;<isolated> 0629;;;;N;GLYPH FOR ISOLATE ARABIC TAA MARBUTAH;;;;
+FE94;ARABIC LETTER TEH MARBUTA FINAL FORM;Lo;0;AL;<final> 0629;;;;N;GLYPH FOR FINAL ARABIC TAA MARBUTAH;;;;
+FE95;ARABIC LETTER TEH ISOLATED FORM;Lo;0;AL;<isolated> 062A;;;;N;GLYPH FOR ISOLATE ARABIC TAA;;;;
+FE96;ARABIC LETTER TEH FINAL FORM;Lo;0;AL;<final> 062A;;;;N;GLYPH FOR FINAL ARABIC TAA;;;;
+FE97;ARABIC LETTER TEH INITIAL FORM;Lo;0;AL;<initial> 062A;;;;N;GLYPH FOR INITIAL ARABIC TAA;;;;
+FE98;ARABIC LETTER TEH MEDIAL FORM;Lo;0;AL;<medial> 062A;;;;N;GLYPH FOR MEDIAL ARABIC TAA;;;;
+FE99;ARABIC LETTER THEH ISOLATED FORM;Lo;0;AL;<isolated> 062B;;;;N;GLYPH FOR ISOLATE ARABIC THAA;;;;
+FE9A;ARABIC LETTER THEH FINAL FORM;Lo;0;AL;<final> 062B;;;;N;GLYPH FOR FINAL ARABIC THAA;;;;
+FE9B;ARABIC LETTER THEH INITIAL FORM;Lo;0;AL;<initial> 062B;;;;N;GLYPH FOR INITIAL ARABIC THAA;;;;
+FE9C;ARABIC LETTER THEH MEDIAL FORM;Lo;0;AL;<medial> 062B;;;;N;GLYPH FOR MEDIAL ARABIC THAA;;;;
+FE9D;ARABIC LETTER JEEM ISOLATED FORM;Lo;0;AL;<isolated> 062C;;;;N;GLYPH FOR ISOLATE ARABIC JEEM;;;;
+FE9E;ARABIC LETTER JEEM FINAL FORM;Lo;0;AL;<final> 062C;;;;N;GLYPH FOR FINAL ARABIC JEEM;;;;
+FE9F;ARABIC LETTER JEEM INITIAL FORM;Lo;0;AL;<initial> 062C;;;;N;GLYPH FOR INITIAL ARABIC JEEM;;;;
+FEA0;ARABIC LETTER JEEM MEDIAL FORM;Lo;0;AL;<medial> 062C;;;;N;GLYPH FOR MEDIAL ARABIC JEEM;;;;
+FEA1;ARABIC LETTER HAH ISOLATED FORM;Lo;0;AL;<isolated> 062D;;;;N;GLYPH FOR ISOLATE ARABIC HAA;;;;
+FEA2;ARABIC LETTER HAH FINAL FORM;Lo;0;AL;<final> 062D;;;;N;GLYPH FOR FINAL ARABIC HAA;;;;
+FEA3;ARABIC LETTER HAH INITIAL FORM;Lo;0;AL;<initial> 062D;;;;N;GLYPH FOR INITIAL ARABIC HAA;;;;
+FEA4;ARABIC LETTER HAH MEDIAL FORM;Lo;0;AL;<medial> 062D;;;;N;GLYPH FOR MEDIAL ARABIC HAA;;;;
+FEA5;ARABIC LETTER KHAH ISOLATED FORM;Lo;0;AL;<isolated> 062E;;;;N;GLYPH FOR ISOLATE ARABIC KHAA;;;;
+FEA6;ARABIC LETTER KHAH FINAL FORM;Lo;0;AL;<final> 062E;;;;N;GLYPH FOR FINAL ARABIC KHAA;;;;
+FEA7;ARABIC LETTER KHAH INITIAL FORM;Lo;0;AL;<initial> 062E;;;;N;GLYPH FOR INITIAL ARABIC KHAA;;;;
+FEA8;ARABIC LETTER KHAH MEDIAL FORM;Lo;0;AL;<medial> 062E;;;;N;GLYPH FOR MEDIAL ARABIC KHAA;;;;
+FEA9;ARABIC LETTER DAL ISOLATED FORM;Lo;0;AL;<isolated> 062F;;;;N;GLYPH FOR ISOLATE ARABIC DAL;;;;
+FEAA;ARABIC LETTER DAL FINAL FORM;Lo;0;AL;<final> 062F;;;;N;GLYPH FOR FINAL ARABIC DAL;;;;
+FEAB;ARABIC LETTER THAL ISOLATED FORM;Lo;0;AL;<isolated> 0630;;;;N;GLYPH FOR ISOLATE ARABIC THAL;;;;
+FEAC;ARABIC LETTER THAL FINAL FORM;Lo;0;AL;<final> 0630;;;;N;GLYPH FOR FINAL ARABIC THAL;;;;
+FEAD;ARABIC LETTER REH ISOLATED FORM;Lo;0;AL;<isolated> 0631;;;;N;GLYPH FOR ISOLATE ARABIC RA;;;;
+FEAE;ARABIC LETTER REH FINAL FORM;Lo;0;AL;<final> 0631;;;;N;GLYPH FOR FINAL ARABIC RA;;;;
+FEAF;ARABIC LETTER ZAIN ISOLATED FORM;Lo;0;AL;<isolated> 0632;;;;N;GLYPH FOR ISOLATE ARABIC ZAIN;;;;
+FEB0;ARABIC LETTER ZAIN FINAL FORM;Lo;0;AL;<final> 0632;;;;N;GLYPH FOR FINAL ARABIC ZAIN;;;;
+FEB1;ARABIC LETTER SEEN ISOLATED FORM;Lo;0;AL;<isolated> 0633;;;;N;GLYPH FOR ISOLATE ARABIC SEEN;;;;
+FEB2;ARABIC LETTER SEEN FINAL FORM;Lo;0;AL;<final> 0633;;;;N;GLYPH FOR FINAL ARABIC SEEN;;;;
+FEB3;ARABIC LETTER SEEN INITIAL FORM;Lo;0;AL;<initial> 0633;;;;N;GLYPH FOR INITIAL ARABIC SEEN;;;;
+FEB4;ARABIC LETTER SEEN MEDIAL FORM;Lo;0;AL;<medial> 0633;;;;N;GLYPH FOR MEDIAL ARABIC SEEN;;;;
+FEB5;ARABIC LETTER SHEEN ISOLATED FORM;Lo;0;AL;<isolated> 0634;;;;N;GLYPH FOR ISOLATE ARABIC SHEEN;;;;
+FEB6;ARABIC LETTER SHEEN FINAL FORM;Lo;0;AL;<final> 0634;;;;N;GLYPH FOR FINAL ARABIC SHEEN;;;;
+FEB7;ARABIC LETTER SHEEN INITIAL FORM;Lo;0;AL;<initial> 0634;;;;N;GLYPH FOR INITIAL ARABIC SHEEN;;;;
+FEB8;ARABIC LETTER SHEEN MEDIAL FORM;Lo;0;AL;<medial> 0634;;;;N;GLYPH FOR MEDIAL ARABIC SHEEN;;;;
+FEB9;ARABIC LETTER SAD ISOLATED FORM;Lo;0;AL;<isolated> 0635;;;;N;GLYPH FOR ISOLATE ARABIC SAD;;;;
+FEBA;ARABIC LETTER SAD FINAL FORM;Lo;0;AL;<final> 0635;;;;N;GLYPH FOR FINAL ARABIC SAD;;;;
+FEBB;ARABIC LETTER SAD INITIAL FORM;Lo;0;AL;<initial> 0635;;;;N;GLYPH FOR INITIAL ARABIC SAD;;;;
+FEBC;ARABIC LETTER SAD MEDIAL FORM;Lo;0;AL;<medial> 0635;;;;N;GLYPH FOR MEDIAL ARABIC SAD;;;;
+FEBD;ARABIC LETTER DAD ISOLATED FORM;Lo;0;AL;<isolated> 0636;;;;N;GLYPH FOR ISOLATE ARABIC DAD;;;;
+FEBE;ARABIC LETTER DAD FINAL FORM;Lo;0;AL;<final> 0636;;;;N;GLYPH FOR FINAL ARABIC DAD;;;;
+FEBF;ARABIC LETTER DAD INITIAL FORM;Lo;0;AL;<initial> 0636;;;;N;GLYPH FOR INITIAL ARABIC DAD;;;;
+FEC0;ARABIC LETTER DAD MEDIAL FORM;Lo;0;AL;<medial> 0636;;;;N;GLYPH FOR MEDIAL ARABIC DAD;;;;
+FEC1;ARABIC LETTER TAH ISOLATED FORM;Lo;0;AL;<isolated> 0637;;;;N;GLYPH FOR ISOLATE ARABIC TAH;;;;
+FEC2;ARABIC LETTER TAH FINAL FORM;Lo;0;AL;<final> 0637;;;;N;GLYPH FOR FINAL ARABIC TAH;;;;
+FEC3;ARABIC LETTER TAH INITIAL FORM;Lo;0;AL;<initial> 0637;;;;N;GLYPH FOR INITIAL ARABIC TAH;;;;
+FEC4;ARABIC LETTER TAH MEDIAL FORM;Lo;0;AL;<medial> 0637;;;;N;GLYPH FOR MEDIAL ARABIC TAH;;;;
+FEC5;ARABIC LETTER ZAH ISOLATED FORM;Lo;0;AL;<isolated> 0638;;;;N;GLYPH FOR ISOLATE ARABIC DHAH;;;;
+FEC6;ARABIC LETTER ZAH FINAL FORM;Lo;0;AL;<final> 0638;;;;N;GLYPH FOR FINAL ARABIC DHAH;;;;
+FEC7;ARABIC LETTER ZAH INITIAL FORM;Lo;0;AL;<initial> 0638;;;;N;GLYPH FOR INITIAL ARABIC DHAH;;;;
+FEC8;ARABIC LETTER ZAH MEDIAL FORM;Lo;0;AL;<medial> 0638;;;;N;GLYPH FOR MEDIAL ARABIC DHAH;;;;
+FEC9;ARABIC LETTER AIN ISOLATED FORM;Lo;0;AL;<isolated> 0639;;;;N;GLYPH FOR ISOLATE ARABIC AIN;;;;
+FECA;ARABIC LETTER AIN FINAL FORM;Lo;0;AL;<final> 0639;;;;N;GLYPH FOR FINAL ARABIC AIN;;;;
+FECB;ARABIC LETTER AIN INITIAL FORM;Lo;0;AL;<initial> 0639;;;;N;GLYPH FOR INITIAL ARABIC AIN;;;;
+FECC;ARABIC LETTER AIN MEDIAL FORM;Lo;0;AL;<medial> 0639;;;;N;GLYPH FOR MEDIAL ARABIC AIN;;;;
+FECD;ARABIC LETTER GHAIN ISOLATED FORM;Lo;0;AL;<isolated> 063A;;;;N;GLYPH FOR ISOLATE ARABIC GHAIN;;;;
+FECE;ARABIC LETTER GHAIN FINAL FORM;Lo;0;AL;<final> 063A;;;;N;GLYPH FOR FINAL ARABIC GHAIN;;;;
+FECF;ARABIC LETTER GHAIN INITIAL FORM;Lo;0;AL;<initial> 063A;;;;N;GLYPH FOR INITIAL ARABIC GHAIN;;;;
+FED0;ARABIC LETTER GHAIN MEDIAL FORM;Lo;0;AL;<medial> 063A;;;;N;GLYPH FOR MEDIAL ARABIC GHAIN;;;;
+FED1;ARABIC LETTER FEH ISOLATED FORM;Lo;0;AL;<isolated> 0641;;;;N;GLYPH FOR ISOLATE ARABIC FA;;;;
+FED2;ARABIC LETTER FEH FINAL FORM;Lo;0;AL;<final> 0641;;;;N;GLYPH FOR FINAL ARABIC FA;;;;
+FED3;ARABIC LETTER FEH INITIAL FORM;Lo;0;AL;<initial> 0641;;;;N;GLYPH FOR INITIAL ARABIC FA;;;;
+FED4;ARABIC LETTER FEH MEDIAL FORM;Lo;0;AL;<medial> 0641;;;;N;GLYPH FOR MEDIAL ARABIC FA;;;;
+FED5;ARABIC LETTER QAF ISOLATED FORM;Lo;0;AL;<isolated> 0642;;;;N;GLYPH FOR ISOLATE ARABIC QAF;;;;
+FED6;ARABIC LETTER QAF FINAL FORM;Lo;0;AL;<final> 0642;;;;N;GLYPH FOR FINAL ARABIC QAF;;;;
+FED7;ARABIC LETTER QAF INITIAL FORM;Lo;0;AL;<initial> 0642;;;;N;GLYPH FOR INITIAL ARABIC QAF;;;;
+FED8;ARABIC LETTER QAF MEDIAL FORM;Lo;0;AL;<medial> 0642;;;;N;GLYPH FOR MEDIAL ARABIC QAF;;;;
+FED9;ARABIC LETTER KAF ISOLATED FORM;Lo;0;AL;<isolated> 0643;;;;N;GLYPH FOR ISOLATE ARABIC CAF;;;;
+FEDA;ARABIC LETTER KAF FINAL FORM;Lo;0;AL;<final> 0643;;;;N;GLYPH FOR FINAL ARABIC CAF;;;;
+FEDB;ARABIC LETTER KAF INITIAL FORM;Lo;0;AL;<initial> 0643;;;;N;GLYPH FOR INITIAL ARABIC CAF;;;;
+FEDC;ARABIC LETTER KAF MEDIAL FORM;Lo;0;AL;<medial> 0643;;;;N;GLYPH FOR MEDIAL ARABIC CAF;;;;
+FEDD;ARABIC LETTER LAM ISOLATED FORM;Lo;0;AL;<isolated> 0644;;;;N;GLYPH FOR ISOLATE ARABIC LAM;;;;
+FEDE;ARABIC LETTER LAM FINAL FORM;Lo;0;AL;<final> 0644;;;;N;GLYPH FOR FINAL ARABIC LAM;;;;
+FEDF;ARABIC LETTER LAM INITIAL FORM;Lo;0;AL;<initial> 0644;;;;N;GLYPH FOR INITIAL ARABIC LAM;;;;
+FEE0;ARABIC LETTER LAM MEDIAL FORM;Lo;0;AL;<medial> 0644;;;;N;GLYPH FOR MEDIAL ARABIC LAM;;;;
+FEE1;ARABIC LETTER MEEM ISOLATED FORM;Lo;0;AL;<isolated> 0645;;;;N;GLYPH FOR ISOLATE ARABIC MEEM;;;;
+FEE2;ARABIC LETTER MEEM FINAL FORM;Lo;0;AL;<final> 0645;;;;N;GLYPH FOR FINAL ARABIC MEEM;;;;
+FEE3;ARABIC LETTER MEEM INITIAL FORM;Lo;0;AL;<initial> 0645;;;;N;GLYPH FOR INITIAL ARABIC MEEM;;;;
+FEE4;ARABIC LETTER MEEM MEDIAL FORM;Lo;0;AL;<medial> 0645;;;;N;GLYPH FOR MEDIAL ARABIC MEEM;;;;
+FEE5;ARABIC LETTER NOON ISOLATED FORM;Lo;0;AL;<isolated> 0646;;;;N;GLYPH FOR ISOLATE ARABIC NOON;;;;
+FEE6;ARABIC LETTER NOON FINAL FORM;Lo;0;AL;<final> 0646;;;;N;GLYPH FOR FINAL ARABIC NOON;;;;
+FEE7;ARABIC LETTER NOON INITIAL FORM;Lo;0;AL;<initial> 0646;;;;N;GLYPH FOR INITIAL ARABIC NOON;;;;
+FEE8;ARABIC LETTER NOON MEDIAL FORM;Lo;0;AL;<medial> 0646;;;;N;GLYPH FOR MEDIAL ARABIC NOON;;;;
+FEE9;ARABIC LETTER HEH ISOLATED FORM;Lo;0;AL;<isolated> 0647;;;;N;GLYPH FOR ISOLATE ARABIC HA;;;;
+FEEA;ARABIC LETTER HEH FINAL FORM;Lo;0;AL;<final> 0647;;;;N;GLYPH FOR FINAL ARABIC HA;;;;
+FEEB;ARABIC LETTER HEH INITIAL FORM;Lo;0;AL;<initial> 0647;;;;N;GLYPH FOR INITIAL ARABIC HA;;;;
+FEEC;ARABIC LETTER HEH MEDIAL FORM;Lo;0;AL;<medial> 0647;;;;N;GLYPH FOR MEDIAL ARABIC HA;;;;
+FEED;ARABIC LETTER WAW ISOLATED FORM;Lo;0;AL;<isolated> 0648;;;;N;GLYPH FOR ISOLATE ARABIC WAW;;;;
+FEEE;ARABIC LETTER WAW FINAL FORM;Lo;0;AL;<final> 0648;;;;N;GLYPH FOR FINAL ARABIC WAW;;;;
+FEEF;ARABIC LETTER ALEF MAKSURA ISOLATED FORM;Lo;0;AL;<isolated> 0649;;;;N;GLYPH FOR ISOLATE ARABIC ALEF MAQSURAH;;;;
+FEF0;ARABIC LETTER ALEF MAKSURA FINAL FORM;Lo;0;AL;<final> 0649;;;;N;GLYPH FOR FINAL ARABIC ALEF MAQSURAH;;;;
+FEF1;ARABIC LETTER YEH ISOLATED FORM;Lo;0;AL;<isolated> 064A;;;;N;GLYPH FOR ISOLATE ARABIC YA;;;;
+FEF2;ARABIC LETTER YEH FINAL FORM;Lo;0;AL;<final> 064A;;;;N;GLYPH FOR FINAL ARABIC YA;;;;
+FEF3;ARABIC LETTER YEH INITIAL FORM;Lo;0;AL;<initial> 064A;;;;N;GLYPH FOR INITIAL ARABIC YA;;;;
+FEF4;ARABIC LETTER YEH MEDIAL FORM;Lo;0;AL;<medial> 064A;;;;N;GLYPH FOR MEDIAL ARABIC YA;;;;
+FEF5;ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0644 0622;;;;N;GLYPH FOR ISOLATE ARABIC MADDAH ON LIGATURE LAM ALEF;;;;
+FEF6;ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM;Lo;0;AL;<final> 0644 0622;;;;N;GLYPH FOR FINAL ARABIC MADDAH ON LIGATURE LAM ALEF;;;;
+FEF7;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM;Lo;0;AL;<isolated> 0644 0623;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH ON LIGATURE LAM ALEF;;;;
+FEF8;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM;Lo;0;AL;<final> 0644 0623;;;;N;GLYPH FOR FINAL ARABIC HAMZAH ON LIGATURE LAM ALEF;;;;
+FEF9;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW ISOLATED FORM;Lo;0;AL;<isolated> 0644 0625;;;;N;GLYPH FOR ISOLATE ARABIC HAMZAH UNDER LIGATURE LAM ALEF;;;;
+FEFA;ARABIC LIGATURE LAM WITH ALEF WITH HAMZA BELOW FINAL FORM;Lo;0;AL;<final> 0644 0625;;;;N;GLYPH FOR FINAL ARABIC HAMZAH UNDER LIGATURE LAM ALEF;;;;
+FEFB;ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM;Lo;0;AL;<isolated> 0644 0627;;;;N;GLYPH FOR ISOLATE ARABIC LIGATURE LAM ALEF;;;;
+FEFC;ARABIC LIGATURE LAM WITH ALEF FINAL FORM;Lo;0;AL;<final> 0644 0627;;;;N;GLYPH FOR FINAL ARABIC LIGATURE LAM ALEF;;;;
+FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;;
+FF01;FULLWIDTH EXCLAMATION MARK;Po;0;ON;<wide> 0021;;;;N;;;;;
+FF02;FULLWIDTH QUOTATION MARK;Po;0;ON;<wide> 0022;;;;N;;;;;
+FF03;FULLWIDTH NUMBER SIGN;Po;0;ET;<wide> 0023;;;;N;;;;;
+FF04;FULLWIDTH DOLLAR SIGN;Sc;0;ET;<wide> 0024;;;;N;;;;;
+FF05;FULLWIDTH PERCENT SIGN;Po;0;ET;<wide> 0025;;;;N;;;;;
+FF06;FULLWIDTH AMPERSAND;Po;0;ON;<wide> 0026;;;;N;;;;;
+FF07;FULLWIDTH APOSTROPHE;Po;0;ON;<wide> 0027;;;;N;;;;;
+FF08;FULLWIDTH LEFT PARENTHESIS;Ps;0;ON;<wide> 0028;;;;Y;FULLWIDTH OPENING PARENTHESIS;;;;
+FF09;FULLWIDTH RIGHT PARENTHESIS;Pe;0;ON;<wide> 0029;;;;Y;FULLWIDTH CLOSING PARENTHESIS;;;;
+FF0A;FULLWIDTH ASTERISK;Po;0;ON;<wide> 002A;;;;N;;;;;
+FF0B;FULLWIDTH PLUS SIGN;Sm;0;ET;<wide> 002B;;;;N;;;;;
+FF0C;FULLWIDTH COMMA;Po;0;CS;<wide> 002C;;;;N;;;;;
+FF0D;FULLWIDTH HYPHEN-MINUS;Pd;0;ET;<wide> 002D;;;;N;;;;;
+FF0E;FULLWIDTH FULL STOP;Po;0;CS;<wide> 002E;;;;N;FULLWIDTH PERIOD;;;;
+FF0F;FULLWIDTH SOLIDUS;Po;0;ES;<wide> 002F;;;;N;FULLWIDTH SLASH;;;;
+FF10;FULLWIDTH DIGIT ZERO;Nd;0;EN;<wide> 0030;0;0;0;N;;;;;
+FF11;FULLWIDTH DIGIT ONE;Nd;0;EN;<wide> 0031;1;1;1;N;;;;;
+FF12;FULLWIDTH DIGIT TWO;Nd;0;EN;<wide> 0032;2;2;2;N;;;;;
+FF13;FULLWIDTH DIGIT THREE;Nd;0;EN;<wide> 0033;3;3;3;N;;;;;
+FF14;FULLWIDTH DIGIT FOUR;Nd;0;EN;<wide> 0034;4;4;4;N;;;;;
+FF15;FULLWIDTH DIGIT FIVE;Nd;0;EN;<wide> 0035;5;5;5;N;;;;;
+FF16;FULLWIDTH DIGIT SIX;Nd;0;EN;<wide> 0036;6;6;6;N;;;;;
+FF17;FULLWIDTH DIGIT SEVEN;Nd;0;EN;<wide> 0037;7;7;7;N;;;;;
+FF18;FULLWIDTH DIGIT EIGHT;Nd;0;EN;<wide> 0038;8;8;8;N;;;;;
+FF19;FULLWIDTH DIGIT NINE;Nd;0;EN;<wide> 0039;9;9;9;N;;;;;
+FF1A;FULLWIDTH COLON;Po;0;CS;<wide> 003A;;;;N;;;;;
+FF1B;FULLWIDTH SEMICOLON;Po;0;ON;<wide> 003B;;;;N;;;;;
+FF1C;FULLWIDTH LESS-THAN SIGN;Sm;0;ON;<wide> 003C;;;;Y;;;;;
+FF1D;FULLWIDTH EQUALS SIGN;Sm;0;ON;<wide> 003D;;;;N;;;;;
+FF1E;FULLWIDTH GREATER-THAN SIGN;Sm;0;ON;<wide> 003E;;;;Y;;;;;
+FF1F;FULLWIDTH QUESTION MARK;Po;0;ON;<wide> 003F;;;;N;;;;;
+FF20;FULLWIDTH COMMERCIAL AT;Po;0;ON;<wide> 0040;;;;N;;;;;
+FF21;FULLWIDTH LATIN CAPITAL LETTER A;Lu;0;L;<wide> 0041;;;;N;;;;FF41;
+FF22;FULLWIDTH LATIN CAPITAL LETTER B;Lu;0;L;<wide> 0042;;;;N;;;;FF42;
+FF23;FULLWIDTH LATIN CAPITAL LETTER C;Lu;0;L;<wide> 0043;;;;N;;;;FF43;
+FF24;FULLWIDTH LATIN CAPITAL LETTER D;Lu;0;L;<wide> 0044;;;;N;;;;FF44;
+FF25;FULLWIDTH LATIN CAPITAL LETTER E;Lu;0;L;<wide> 0045;;;;N;;;;FF45;
+FF26;FULLWIDTH LATIN CAPITAL LETTER F;Lu;0;L;<wide> 0046;;;;N;;;;FF46;
+FF27;FULLWIDTH LATIN CAPITAL LETTER G;Lu;0;L;<wide> 0047;;;;N;;;;FF47;
+FF28;FULLWIDTH LATIN CAPITAL LETTER H;Lu;0;L;<wide> 0048;;;;N;;;;FF48;
+FF29;FULLWIDTH LATIN CAPITAL LETTER I;Lu;0;L;<wide> 0049;;;;N;;;;FF49;
+FF2A;FULLWIDTH LATIN CAPITAL LETTER J;Lu;0;L;<wide> 004A;;;;N;;;;FF4A;
+FF2B;FULLWIDTH LATIN CAPITAL LETTER K;Lu;0;L;<wide> 004B;;;;N;;;;FF4B;
+FF2C;FULLWIDTH LATIN CAPITAL LETTER L;Lu;0;L;<wide> 004C;;;;N;;;;FF4C;
+FF2D;FULLWIDTH LATIN CAPITAL LETTER M;Lu;0;L;<wide> 004D;;;;N;;;;FF4D;
+FF2E;FULLWIDTH LATIN CAPITAL LETTER N;Lu;0;L;<wide> 004E;;;;N;;;;FF4E;
+FF2F;FULLWIDTH LATIN CAPITAL LETTER O;Lu;0;L;<wide> 004F;;;;N;;;;FF4F;
+FF30;FULLWIDTH LATIN CAPITAL LETTER P;Lu;0;L;<wide> 0050;;;;N;;;;FF50;
+FF31;FULLWIDTH LATIN CAPITAL LETTER Q;Lu;0;L;<wide> 0051;;;;N;;;;FF51;
+FF32;FULLWIDTH LATIN CAPITAL LETTER R;Lu;0;L;<wide> 0052;;;;N;;;;FF52;
+FF33;FULLWIDTH LATIN CAPITAL LETTER S;Lu;0;L;<wide> 0053;;;;N;;;;FF53;
+FF34;FULLWIDTH LATIN CAPITAL LETTER T;Lu;0;L;<wide> 0054;;;;N;;;;FF54;
+FF35;FULLWIDTH LATIN CAPITAL LETTER U;Lu;0;L;<wide> 0055;;;;N;;;;FF55;
+FF36;FULLWIDTH LATIN CAPITAL LETTER V;Lu;0;L;<wide> 0056;;;;N;;;;FF56;
+FF37;FULLWIDTH LATIN CAPITAL LETTER W;Lu;0;L;<wide> 0057;;;;N;;;;FF57;
+FF38;FULLWIDTH LATIN CAPITAL LETTER X;Lu;0;L;<wide> 0058;;;;N;;;;FF58;
+FF39;FULLWIDTH LATIN CAPITAL LETTER Y;Lu;0;L;<wide> 0059;;;;N;;;;FF59;
+FF3A;FULLWIDTH LATIN CAPITAL LETTER Z;Lu;0;L;<wide> 005A;;;;N;;;;FF5A;
+FF3B;FULLWIDTH LEFT SQUARE BRACKET;Ps;0;ON;<wide> 005B;;;;Y;FULLWIDTH OPENING SQUARE BRACKET;;;;
+FF3C;FULLWIDTH REVERSE SOLIDUS;Po;0;ON;<wide> 005C;;;;N;FULLWIDTH BACKSLASH;;;;
+FF3D;FULLWIDTH RIGHT SQUARE BRACKET;Pe;0;ON;<wide> 005D;;;;Y;FULLWIDTH CLOSING SQUARE BRACKET;;;;
+FF3E;FULLWIDTH CIRCUMFLEX ACCENT;Sk;0;ON;<wide> 005E;;;;N;FULLWIDTH SPACING CIRCUMFLEX;;;;
+FF3F;FULLWIDTH LOW LINE;Pc;0;ON;<wide> 005F;;;;N;FULLWIDTH SPACING UNDERSCORE;;;;
+FF40;FULLWIDTH GRAVE ACCENT;Sk;0;ON;<wide> 0060;;;;N;FULLWIDTH SPACING GRAVE;;;;
+FF41;FULLWIDTH LATIN SMALL LETTER A;Ll;0;L;<wide> 0061;;;;N;;;FF21;;FF21
+FF42;FULLWIDTH LATIN SMALL LETTER B;Ll;0;L;<wide> 0062;;;;N;;;FF22;;FF22
+FF43;FULLWIDTH LATIN SMALL LETTER C;Ll;0;L;<wide> 0063;;;;N;;;FF23;;FF23
+FF44;FULLWIDTH LATIN SMALL LETTER D;Ll;0;L;<wide> 0064;;;;N;;;FF24;;FF24
+FF45;FULLWIDTH LATIN SMALL LETTER E;Ll;0;L;<wide> 0065;;;;N;;;FF25;;FF25
+FF46;FULLWIDTH LATIN SMALL LETTER F;Ll;0;L;<wide> 0066;;;;N;;;FF26;;FF26
+FF47;FULLWIDTH LATIN SMALL LETTER G;Ll;0;L;<wide> 0067;;;;N;;;FF27;;FF27
+FF48;FULLWIDTH LATIN SMALL LETTER H;Ll;0;L;<wide> 0068;;;;N;;;FF28;;FF28
+FF49;FULLWIDTH LATIN SMALL LETTER I;Ll;0;L;<wide> 0069;;;;N;;;FF29;;FF29
+FF4A;FULLWIDTH LATIN SMALL LETTER J;Ll;0;L;<wide> 006A;;;;N;;;FF2A;;FF2A
+FF4B;FULLWIDTH LATIN SMALL LETTER K;Ll;0;L;<wide> 006B;;;;N;;;FF2B;;FF2B
+FF4C;FULLWIDTH LATIN SMALL LETTER L;Ll;0;L;<wide> 006C;;;;N;;;FF2C;;FF2C
+FF4D;FULLWIDTH LATIN SMALL LETTER M;Ll;0;L;<wide> 006D;;;;N;;;FF2D;;FF2D
+FF4E;FULLWIDTH LATIN SMALL LETTER N;Ll;0;L;<wide> 006E;;;;N;;;FF2E;;FF2E
+FF4F;FULLWIDTH LATIN SMALL LETTER O;Ll;0;L;<wide> 006F;;;;N;;;FF2F;;FF2F
+FF50;FULLWIDTH LATIN SMALL LETTER P;Ll;0;L;<wide> 0070;;;;N;;;FF30;;FF30
+FF51;FULLWIDTH LATIN SMALL LETTER Q;Ll;0;L;<wide> 0071;;;;N;;;FF31;;FF31
+FF52;FULLWIDTH LATIN SMALL LETTER R;Ll;0;L;<wide> 0072;;;;N;;;FF32;;FF32
+FF53;FULLWIDTH LATIN SMALL LETTER S;Ll;0;L;<wide> 0073;;;;N;;;FF33;;FF33
+FF54;FULLWIDTH LATIN SMALL LETTER T;Ll;0;L;<wide> 0074;;;;N;;;FF34;;FF34
+FF55;FULLWIDTH LATIN SMALL LETTER U;Ll;0;L;<wide> 0075;;;;N;;;FF35;;FF35
+FF56;FULLWIDTH LATIN SMALL LETTER V;Ll;0;L;<wide> 0076;;;;N;;;FF36;;FF36
+FF57;FULLWIDTH LATIN SMALL LETTER W;Ll;0;L;<wide> 0077;;;;N;;;FF37;;FF37
+FF58;FULLWIDTH LATIN SMALL LETTER X;Ll;0;L;<wide> 0078;;;;N;;;FF38;;FF38
+FF59;FULLWIDTH LATIN SMALL LETTER Y;Ll;0;L;<wide> 0079;;;;N;;;FF39;;FF39
+FF5A;FULLWIDTH LATIN SMALL LETTER Z;Ll;0;L;<wide> 007A;;;;N;;;FF3A;;FF3A
+FF5B;FULLWIDTH LEFT CURLY BRACKET;Ps;0;ON;<wide> 007B;;;;Y;FULLWIDTH OPENING CURLY BRACKET;;;;
+FF5C;FULLWIDTH VERTICAL LINE;Sm;0;ON;<wide> 007C;;;;N;FULLWIDTH VERTICAL BAR;;;;
+FF5D;FULLWIDTH RIGHT CURLY BRACKET;Pe;0;ON;<wide> 007D;;;;Y;FULLWIDTH CLOSING CURLY BRACKET;;;;
+FF5E;FULLWIDTH TILDE;Sm;0;ON;<wide> 007E;;;;N;FULLWIDTH SPACING TILDE;;;;
+FF5F;FULLWIDTH LEFT WHITE PARENTHESIS;Ps;0;ON;<wide> 2985;;;;Y;;*;;;
+FF60;FULLWIDTH RIGHT WHITE PARENTHESIS;Pe;0;ON;<wide> 2986;;;;Y;;*;;;
+FF61;HALFWIDTH IDEOGRAPHIC FULL STOP;Po;0;ON;<narrow> 3002;;;;N;HALFWIDTH IDEOGRAPHIC PERIOD;;;;
+FF62;HALFWIDTH LEFT CORNER BRACKET;Ps;0;ON;<narrow> 300C;;;;Y;HALFWIDTH OPENING CORNER BRACKET;;;;
+FF63;HALFWIDTH RIGHT CORNER BRACKET;Pe;0;ON;<narrow> 300D;;;;Y;HALFWIDTH CLOSING CORNER BRACKET;;;;
+FF64;HALFWIDTH IDEOGRAPHIC COMMA;Po;0;ON;<narrow> 3001;;;;N;;;;;
+FF65;HALFWIDTH KATAKANA MIDDLE DOT;Pc;0;ON;<narrow> 30FB;;;;N;;;;;
+FF66;HALFWIDTH KATAKANA LETTER WO;Lo;0;L;<narrow> 30F2;;;;N;;;;;
+FF67;HALFWIDTH KATAKANA LETTER SMALL A;Lo;0;L;<narrow> 30A1;;;;N;;;;;
+FF68;HALFWIDTH KATAKANA LETTER SMALL I;Lo;0;L;<narrow> 30A3;;;;N;;;;;
+FF69;HALFWIDTH KATAKANA LETTER SMALL U;Lo;0;L;<narrow> 30A5;;;;N;;;;;
+FF6A;HALFWIDTH KATAKANA LETTER SMALL E;Lo;0;L;<narrow> 30A7;;;;N;;;;;
+FF6B;HALFWIDTH KATAKANA LETTER SMALL O;Lo;0;L;<narrow> 30A9;;;;N;;;;;
+FF6C;HALFWIDTH KATAKANA LETTER SMALL YA;Lo;0;L;<narrow> 30E3;;;;N;;;;;
+FF6D;HALFWIDTH KATAKANA LETTER SMALL YU;Lo;0;L;<narrow> 30E5;;;;N;;;;;
+FF6E;HALFWIDTH KATAKANA LETTER SMALL YO;Lo;0;L;<narrow> 30E7;;;;N;;;;;
+FF6F;HALFWIDTH KATAKANA LETTER SMALL TU;Lo;0;L;<narrow> 30C3;;;;N;;;;;
+FF70;HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK;Lm;0;L;<narrow> 30FC;;;;N;;;;;
+FF71;HALFWIDTH KATAKANA LETTER A;Lo;0;L;<narrow> 30A2;;;;N;;;;;
+FF72;HALFWIDTH KATAKANA LETTER I;Lo;0;L;<narrow> 30A4;;;;N;;;;;
+FF73;HALFWIDTH KATAKANA LETTER U;Lo;0;L;<narrow> 30A6;;;;N;;;;;
+FF74;HALFWIDTH KATAKANA LETTER E;Lo;0;L;<narrow> 30A8;;;;N;;;;;
+FF75;HALFWIDTH KATAKANA LETTER O;Lo;0;L;<narrow> 30AA;;;;N;;;;;
+FF76;HALFWIDTH KATAKANA LETTER KA;Lo;0;L;<narrow> 30AB;;;;N;;;;;
+FF77;HALFWIDTH KATAKANA LETTER KI;Lo;0;L;<narrow> 30AD;;;;N;;;;;
+FF78;HALFWIDTH KATAKANA LETTER KU;Lo;0;L;<narrow> 30AF;;;;N;;;;;
+FF79;HALFWIDTH KATAKANA LETTER KE;Lo;0;L;<narrow> 30B1;;;;N;;;;;
+FF7A;HALFWIDTH KATAKANA LETTER KO;Lo;0;L;<narrow> 30B3;;;;N;;;;;
+FF7B;HALFWIDTH KATAKANA LETTER SA;Lo;0;L;<narrow> 30B5;;;;N;;;;;
+FF7C;HALFWIDTH KATAKANA LETTER SI;Lo;0;L;<narrow> 30B7;;;;N;;;;;
+FF7D;HALFWIDTH KATAKANA LETTER SU;Lo;0;L;<narrow> 30B9;;;;N;;;;;
+FF7E;HALFWIDTH KATAKANA LETTER SE;Lo;0;L;<narrow> 30BB;;;;N;;;;;
+FF7F;HALFWIDTH KATAKANA LETTER SO;Lo;0;L;<narrow> 30BD;;;;N;;;;;
+FF80;HALFWIDTH KATAKANA LETTER TA;Lo;0;L;<narrow> 30BF;;;;N;;;;;
+FF81;HALFWIDTH KATAKANA LETTER TI;Lo;0;L;<narrow> 30C1;;;;N;;;;;
+FF82;HALFWIDTH KATAKANA LETTER TU;Lo;0;L;<narrow> 30C4;;;;N;;;;;
+FF83;HALFWIDTH KATAKANA LETTER TE;Lo;0;L;<narrow> 30C6;;;;N;;;;;
+FF84;HALFWIDTH KATAKANA LETTER TO;Lo;0;L;<narrow> 30C8;;;;N;;;;;
+FF85;HALFWIDTH KATAKANA LETTER NA;Lo;0;L;<narrow> 30CA;;;;N;;;;;
+FF86;HALFWIDTH KATAKANA LETTER NI;Lo;0;L;<narrow> 30CB;;;;N;;;;;
+FF87;HALFWIDTH KATAKANA LETTER NU;Lo;0;L;<narrow> 30CC;;;;N;;;;;
+FF88;HALFWIDTH KATAKANA LETTER NE;Lo;0;L;<narrow> 30CD;;;;N;;;;;
+FF89;HALFWIDTH KATAKANA LETTER NO;Lo;0;L;<narrow> 30CE;;;;N;;;;;
+FF8A;HALFWIDTH KATAKANA LETTER HA;Lo;0;L;<narrow> 30CF;;;;N;;;;;
+FF8B;HALFWIDTH KATAKANA LETTER HI;Lo;0;L;<narrow> 30D2;;;;N;;;;;
+FF8C;HALFWIDTH KATAKANA LETTER HU;Lo;0;L;<narrow> 30D5;;;;N;;;;;
+FF8D;HALFWIDTH KATAKANA LETTER HE;Lo;0;L;<narrow> 30D8;;;;N;;;;;
+FF8E;HALFWIDTH KATAKANA LETTER HO;Lo;0;L;<narrow> 30DB;;;;N;;;;;
+FF8F;HALFWIDTH KATAKANA LETTER MA;Lo;0;L;<narrow> 30DE;;;;N;;;;;
+FF90;HALFWIDTH KATAKANA LETTER MI;Lo;0;L;<narrow> 30DF;;;;N;;;;;
+FF91;HALFWIDTH KATAKANA LETTER MU;Lo;0;L;<narrow> 30E0;;;;N;;;;;
+FF92;HALFWIDTH KATAKANA LETTER ME;Lo;0;L;<narrow> 30E1;;;;N;;;;;
+FF93;HALFWIDTH KATAKANA LETTER MO;Lo;0;L;<narrow> 30E2;;;;N;;;;;
+FF94;HALFWIDTH KATAKANA LETTER YA;Lo;0;L;<narrow> 30E4;;;;N;;;;;
+FF95;HALFWIDTH KATAKANA LETTER YU;Lo;0;L;<narrow> 30E6;;;;N;;;;;
+FF96;HALFWIDTH KATAKANA LETTER YO;Lo;0;L;<narrow> 30E8;;;;N;;;;;
+FF97;HALFWIDTH KATAKANA LETTER RA;Lo;0;L;<narrow> 30E9;;;;N;;;;;
+FF98;HALFWIDTH KATAKANA LETTER RI;Lo;0;L;<narrow> 30EA;;;;N;;;;;
+FF99;HALFWIDTH KATAKANA LETTER RU;Lo;0;L;<narrow> 30EB;;;;N;;;;;
+FF9A;HALFWIDTH KATAKANA LETTER RE;Lo;0;L;<narrow> 30EC;;;;N;;;;;
+FF9B;HALFWIDTH KATAKANA LETTER RO;Lo;0;L;<narrow> 30ED;;;;N;;;;;
+FF9C;HALFWIDTH KATAKANA LETTER WA;Lo;0;L;<narrow> 30EF;;;;N;;;;;
+FF9D;HALFWIDTH KATAKANA LETTER N;Lo;0;L;<narrow> 30F3;;;;N;;;;;
+FF9E;HALFWIDTH KATAKANA VOICED SOUND MARK;Lm;0;L;<narrow> 3099;;;;N;;halfwidth katakana-hiragana voiced sound mark;;;
+FF9F;HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK;Lm;0;L;<narrow> 309A;;;;N;;halfwidth katakana-hiragana semi-voiced sound mark;;;
+FFA0;HALFWIDTH HANGUL FILLER;Lo;0;L;<narrow> 3164;;;;N;HALFWIDTH HANGUL CAE OM;;;;
+FFA1;HALFWIDTH HANGUL LETTER KIYEOK;Lo;0;L;<narrow> 3131;;;;N;HALFWIDTH HANGUL LETTER GIYEOG;;;;
+FFA2;HALFWIDTH HANGUL LETTER SSANGKIYEOK;Lo;0;L;<narrow> 3132;;;;N;HALFWIDTH HANGUL LETTER SSANG GIYEOG;;;;
+FFA3;HALFWIDTH HANGUL LETTER KIYEOK-SIOS;Lo;0;L;<narrow> 3133;;;;N;HALFWIDTH HANGUL LETTER GIYEOG SIOS;;;;
+FFA4;HALFWIDTH HANGUL LETTER NIEUN;Lo;0;L;<narrow> 3134;;;;N;;;;;
+FFA5;HALFWIDTH HANGUL LETTER NIEUN-CIEUC;Lo;0;L;<narrow> 3135;;;;N;HALFWIDTH HANGUL LETTER NIEUN JIEUJ;;;;
+FFA6;HALFWIDTH HANGUL LETTER NIEUN-HIEUH;Lo;0;L;<narrow> 3136;;;;N;HALFWIDTH HANGUL LETTER NIEUN HIEUH;;;;
+FFA7;HALFWIDTH HANGUL LETTER TIKEUT;Lo;0;L;<narrow> 3137;;;;N;HALFWIDTH HANGUL LETTER DIGEUD;;;;
+FFA8;HALFWIDTH HANGUL LETTER SSANGTIKEUT;Lo;0;L;<narrow> 3138;;;;N;HALFWIDTH HANGUL LETTER SSANG DIGEUD;;;;
+FFA9;HALFWIDTH HANGUL LETTER RIEUL;Lo;0;L;<narrow> 3139;;;;N;HALFWIDTH HANGUL LETTER LIEUL;;;;
+FFAA;HALFWIDTH HANGUL LETTER RIEUL-KIYEOK;Lo;0;L;<narrow> 313A;;;;N;HALFWIDTH HANGUL LETTER LIEUL GIYEOG;;;;
+FFAB;HALFWIDTH HANGUL LETTER RIEUL-MIEUM;Lo;0;L;<narrow> 313B;;;;N;HALFWIDTH HANGUL LETTER LIEUL MIEUM;;;;
+FFAC;HALFWIDTH HANGUL LETTER RIEUL-PIEUP;Lo;0;L;<narrow> 313C;;;;N;HALFWIDTH HANGUL LETTER LIEUL BIEUB;;;;
+FFAD;HALFWIDTH HANGUL LETTER RIEUL-SIOS;Lo;0;L;<narrow> 313D;;;;N;HALFWIDTH HANGUL LETTER LIEUL SIOS;;;;
+FFAE;HALFWIDTH HANGUL LETTER RIEUL-THIEUTH;Lo;0;L;<narrow> 313E;;;;N;HALFWIDTH HANGUL LETTER LIEUL TIEUT;;;;
+FFAF;HALFWIDTH HANGUL LETTER RIEUL-PHIEUPH;Lo;0;L;<narrow> 313F;;;;N;HALFWIDTH HANGUL LETTER LIEUL PIEUP;;;;
+FFB0;HALFWIDTH HANGUL LETTER RIEUL-HIEUH;Lo;0;L;<narrow> 3140;;;;N;HALFWIDTH HANGUL LETTER LIEUL HIEUH;;;;
+FFB1;HALFWIDTH HANGUL LETTER MIEUM;Lo;0;L;<narrow> 3141;;;;N;;;;;
+FFB2;HALFWIDTH HANGUL LETTER PIEUP;Lo;0;L;<narrow> 3142;;;;N;HALFWIDTH HANGUL LETTER BIEUB;;;;
+FFB3;HALFWIDTH HANGUL LETTER SSANGPIEUP;Lo;0;L;<narrow> 3143;;;;N;HALFWIDTH HANGUL LETTER SSANG BIEUB;;;;
+FFB4;HALFWIDTH HANGUL LETTER PIEUP-SIOS;Lo;0;L;<narrow> 3144;;;;N;HALFWIDTH HANGUL LETTER BIEUB SIOS;;;;
+FFB5;HALFWIDTH HANGUL LETTER SIOS;Lo;0;L;<narrow> 3145;;;;N;;;;;
+FFB6;HALFWIDTH HANGUL LETTER SSANGSIOS;Lo;0;L;<narrow> 3146;;;;N;HALFWIDTH HANGUL LETTER SSANG SIOS;;;;
+FFB7;HALFWIDTH HANGUL LETTER IEUNG;Lo;0;L;<narrow> 3147;;;;N;;;;;
+FFB8;HALFWIDTH HANGUL LETTER CIEUC;Lo;0;L;<narrow> 3148;;;;N;HALFWIDTH HANGUL LETTER JIEUJ;;;;
+FFB9;HALFWIDTH HANGUL LETTER SSANGCIEUC;Lo;0;L;<narrow> 3149;;;;N;HALFWIDTH HANGUL LETTER SSANG JIEUJ;;;;
+FFBA;HALFWIDTH HANGUL LETTER CHIEUCH;Lo;0;L;<narrow> 314A;;;;N;HALFWIDTH HANGUL LETTER CIEUC;;;;
+FFBB;HALFWIDTH HANGUL LETTER KHIEUKH;Lo;0;L;<narrow> 314B;;;;N;HALFWIDTH HANGUL LETTER KIYEOK;;;;
+FFBC;HALFWIDTH HANGUL LETTER THIEUTH;Lo;0;L;<narrow> 314C;;;;N;HALFWIDTH HANGUL LETTER TIEUT;;;;
+FFBD;HALFWIDTH HANGUL LETTER PHIEUPH;Lo;0;L;<narrow> 314D;;;;N;HALFWIDTH HANGUL LETTER PIEUP;;;;
+FFBE;HALFWIDTH HANGUL LETTER HIEUH;Lo;0;L;<narrow> 314E;;;;N;;;;;
+FFC2;HALFWIDTH HANGUL LETTER A;Lo;0;L;<narrow> 314F;;;;N;;;;;
+FFC3;HALFWIDTH HANGUL LETTER AE;Lo;0;L;<narrow> 3150;;;;N;;;;;
+FFC4;HALFWIDTH HANGUL LETTER YA;Lo;0;L;<narrow> 3151;;;;N;;;;;
+FFC5;HALFWIDTH HANGUL LETTER YAE;Lo;0;L;<narrow> 3152;;;;N;;;;;
+FFC6;HALFWIDTH HANGUL LETTER EO;Lo;0;L;<narrow> 3153;;;;N;;;;;
+FFC7;HALFWIDTH HANGUL LETTER E;Lo;0;L;<narrow> 3154;;;;N;;;;;
+FFCA;HALFWIDTH HANGUL LETTER YEO;Lo;0;L;<narrow> 3155;;;;N;;;;;
+FFCB;HALFWIDTH HANGUL LETTER YE;Lo;0;L;<narrow> 3156;;;;N;;;;;
+FFCC;HALFWIDTH HANGUL LETTER O;Lo;0;L;<narrow> 3157;;;;N;;;;;
+FFCD;HALFWIDTH HANGUL LETTER WA;Lo;0;L;<narrow> 3158;;;;N;;;;;
+FFCE;HALFWIDTH HANGUL LETTER WAE;Lo;0;L;<narrow> 3159;;;;N;;;;;
+FFCF;HALFWIDTH HANGUL LETTER OE;Lo;0;L;<narrow> 315A;;;;N;;;;;
+FFD2;HALFWIDTH HANGUL LETTER YO;Lo;0;L;<narrow> 315B;;;;N;;;;;
+FFD3;HALFWIDTH HANGUL LETTER U;Lo;0;L;<narrow> 315C;;;;N;;;;;
+FFD4;HALFWIDTH HANGUL LETTER WEO;Lo;0;L;<narrow> 315D;;;;N;;;;;
+FFD5;HALFWIDTH HANGUL LETTER WE;Lo;0;L;<narrow> 315E;;;;N;;;;;
+FFD6;HALFWIDTH HANGUL LETTER WI;Lo;0;L;<narrow> 315F;;;;N;;;;;
+FFD7;HALFWIDTH HANGUL LETTER YU;Lo;0;L;<narrow> 3160;;;;N;;;;;
+FFDA;HALFWIDTH HANGUL LETTER EU;Lo;0;L;<narrow> 3161;;;;N;;;;;
+FFDB;HALFWIDTH HANGUL LETTER YI;Lo;0;L;<narrow> 3162;;;;N;;;;;
+FFDC;HALFWIDTH HANGUL LETTER I;Lo;0;L;<narrow> 3163;;;;N;;;;;
+FFE0;FULLWIDTH CENT SIGN;Sc;0;ET;<wide> 00A2;;;;N;;;;;
+FFE1;FULLWIDTH POUND SIGN;Sc;0;ET;<wide> 00A3;;;;N;;;;;
+FFE2;FULLWIDTH NOT SIGN;Sm;0;ON;<wide> 00AC;;;;N;;;;;
+FFE3;FULLWIDTH MACRON;Sk;0;ON;<wide> 00AF;;;;N;FULLWIDTH SPACING MACRON;*;;;
+FFE4;FULLWIDTH BROKEN BAR;So;0;ON;<wide> 00A6;;;;N;FULLWIDTH BROKEN VERTICAL BAR;;;;
+FFE5;FULLWIDTH YEN SIGN;Sc;0;ET;<wide> 00A5;;;;N;;;;;
+FFE6;FULLWIDTH WON SIGN;Sc;0;ET;<wide> 20A9;;;;N;;;;;
+FFE8;HALFWIDTH FORMS LIGHT VERTICAL;So;0;ON;<narrow> 2502;;;;N;;;;;
+FFE9;HALFWIDTH LEFTWARDS ARROW;Sm;0;ON;<narrow> 2190;;;;N;;;;;
+FFEA;HALFWIDTH UPWARDS ARROW;Sm;0;ON;<narrow> 2191;;;;N;;;;;
+FFEB;HALFWIDTH RIGHTWARDS ARROW;Sm;0;ON;<narrow> 2192;;;;N;;;;;
+FFEC;HALFWIDTH DOWNWARDS ARROW;Sm;0;ON;<narrow> 2193;;;;N;;;;;
+FFED;HALFWIDTH BLACK SQUARE;So;0;ON;<narrow> 25A0;;;;N;;;;;
+FFEE;HALFWIDTH WHITE CIRCLE;So;0;ON;<narrow> 25CB;;;;N;;;;;
+FFF9;INTERLINEAR ANNOTATION ANCHOR;Cf;0;BN;;;;;N;;;;;
+FFFA;INTERLINEAR ANNOTATION SEPARATOR;Cf;0;BN;;;;;N;;;;;
+FFFB;INTERLINEAR ANNOTATION TERMINATOR;Cf;0;BN;;;;;N;;;;;
+FFFC;OBJECT REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
+FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
+10300;OLD ITALIC LETTER A;Lo;0;L;;;;;N;;;;;
+10301;OLD ITALIC LETTER BE;Lo;0;L;;;;;N;;;;;
+10302;OLD ITALIC LETTER KE;Lo;0;L;;;;;N;;;;;
+10303;OLD ITALIC LETTER DE;Lo;0;L;;;;;N;;;;;
+10304;OLD ITALIC LETTER E;Lo;0;L;;;;;N;;;;;
+10305;OLD ITALIC LETTER VE;Lo;0;L;;;;;N;;;;;
+10306;OLD ITALIC LETTER ZE;Lo;0;L;;;;;N;;;;;
+10307;OLD ITALIC LETTER HE;Lo;0;L;;;;;N;;;;;
+10308;OLD ITALIC LETTER THE;Lo;0;L;;;;;N;;;;;
+10309;OLD ITALIC LETTER I;Lo;0;L;;;;;N;;;;;
+1030A;OLD ITALIC LETTER KA;Lo;0;L;;;;;N;;;;;
+1030B;OLD ITALIC LETTER EL;Lo;0;L;;;;;N;;;;;
+1030C;OLD ITALIC LETTER EM;Lo;0;L;;;;;N;;;;;
+1030D;OLD ITALIC LETTER EN;Lo;0;L;;;;;N;;;;;
+1030E;OLD ITALIC LETTER ESH;Lo;0;L;;;;;N;;;;;
+1030F;OLD ITALIC LETTER O;Lo;0;L;;;;;N;;Faliscan;;;
+10310;OLD ITALIC LETTER PE;Lo;0;L;;;;;N;;;;;
+10311;OLD ITALIC LETTER SHE;Lo;0;L;;;;;N;;;;;
+10312;OLD ITALIC LETTER KU;Lo;0;L;;;;;N;;;;;
+10313;OLD ITALIC LETTER ER;Lo;0;L;;;;;N;;;;;
+10314;OLD ITALIC LETTER ES;Lo;0;L;;;;;N;;;;;
+10315;OLD ITALIC LETTER TE;Lo;0;L;;;;;N;;;;;
+10316;OLD ITALIC LETTER U;Lo;0;L;;;;;N;;;;;
+10317;OLD ITALIC LETTER EKS;Lo;0;L;;;;;N;;Faliscan;;;
+10318;OLD ITALIC LETTER PHE;Lo;0;L;;;;;N;;;;;
+10319;OLD ITALIC LETTER KHE;Lo;0;L;;;;;N;;;;;
+1031A;OLD ITALIC LETTER EF;Lo;0;L;;;;;N;;;;;
+1031B;OLD ITALIC LETTER ERS;Lo;0;L;;;;;N;;Umbrian;;;
+1031C;OLD ITALIC LETTER CHE;Lo;0;L;;;;;N;;Umbrian;;;
+1031D;OLD ITALIC LETTER II;Lo;0;L;;;;;N;;Oscan;;;
+1031E;OLD ITALIC LETTER UU;Lo;0;L;;;;;N;;Oscan;;;
+10320;OLD ITALIC NUMERAL ONE;No;0;L;;;;1;N;;;;;
+10321;OLD ITALIC NUMERAL FIVE;No;0;L;;;;5;N;;;;;
+10322;OLD ITALIC NUMERAL TEN;No;0;L;;;;10;N;;;;;
+10323;OLD ITALIC NUMERAL FIFTY;No;0;L;;;;50;N;;;;;
+10330;GOTHIC LETTER AHSA;Lo;0;L;;;;;N;;;;;
+10331;GOTHIC LETTER BAIRKAN;Lo;0;L;;;;;N;;;;;
+10332;GOTHIC LETTER GIBA;Lo;0;L;;;;;N;;;;;
+10333;GOTHIC LETTER DAGS;Lo;0;L;;;;;N;;;;;
+10334;GOTHIC LETTER AIHVUS;Lo;0;L;;;;;N;;;;;
+10335;GOTHIC LETTER QAIRTHRA;Lo;0;L;;;;;N;;;;;
+10336;GOTHIC LETTER IUJA;Lo;0;L;;;;;N;;;;;
+10337;GOTHIC LETTER HAGL;Lo;0;L;;;;;N;;;;;
+10338;GOTHIC LETTER THIUTH;Lo;0;L;;;;;N;;;;;
+10339;GOTHIC LETTER EIS;Lo;0;L;;;;;N;;;;;
+1033A;GOTHIC LETTER KUSMA;Lo;0;L;;;;;N;;;;;
+1033B;GOTHIC LETTER LAGUS;Lo;0;L;;;;;N;;;;;
+1033C;GOTHIC LETTER MANNA;Lo;0;L;;;;;N;;;;;
+1033D;GOTHIC LETTER NAUTHS;Lo;0;L;;;;;N;;;;;
+1033E;GOTHIC LETTER JER;Lo;0;L;;;;;N;;;;;
+1033F;GOTHIC LETTER URUS;Lo;0;L;;;;;N;;;;;
+10340;GOTHIC LETTER PAIRTHRA;Lo;0;L;;;;;N;;;;;
+10341;GOTHIC LETTER NINETY;Lo;0;L;;;;;N;;;;;
+10342;GOTHIC LETTER RAIDA;Lo;0;L;;;;;N;;;;;
+10343;GOTHIC LETTER SAUIL;Lo;0;L;;;;;N;;;;;
+10344;GOTHIC LETTER TEIWS;Lo;0;L;;;;;N;;;;;
+10345;GOTHIC LETTER WINJA;Lo;0;L;;;;;N;;;;;
+10346;GOTHIC LETTER FAIHU;Lo;0;L;;;;;N;;;;;
+10347;GOTHIC LETTER IGGWS;Lo;0;L;;;;;N;;;;;
+10348;GOTHIC LETTER HWAIR;Lo;0;L;;;;;N;;;;;
+10349;GOTHIC LETTER OTHAL;Lo;0;L;;;;;N;;;;;
+1034A;GOTHIC LETTER NINE HUNDRED;Nl;0;L;;;;;N;;;;;
+10400;DESERET CAPITAL LETTER LONG I;Lu;0;L;;;;;N;;;;10428;
+10401;DESERET CAPITAL LETTER LONG E;Lu;0;L;;;;;N;;;;10429;
+10402;DESERET CAPITAL LETTER LONG A;Lu;0;L;;;;;N;;;;1042A;
+10403;DESERET CAPITAL LETTER LONG AH;Lu;0;L;;;;;N;;;;1042B;
+10404;DESERET CAPITAL LETTER LONG O;Lu;0;L;;;;;N;;;;1042C;
+10405;DESERET CAPITAL LETTER LONG OO;Lu;0;L;;;;;N;;;;1042D;
+10406;DESERET CAPITAL LETTER SHORT I;Lu;0;L;;;;;N;;;;1042E;
+10407;DESERET CAPITAL LETTER SHORT E;Lu;0;L;;;;;N;;;;1042F;
+10408;DESERET CAPITAL LETTER SHORT A;Lu;0;L;;;;;N;;;;10430;
+10409;DESERET CAPITAL LETTER SHORT AH;Lu;0;L;;;;;N;;;;10431;
+1040A;DESERET CAPITAL LETTER SHORT O;Lu;0;L;;;;;N;;;;10432;
+1040B;DESERET CAPITAL LETTER SHORT OO;Lu;0;L;;;;;N;;;;10433;
+1040C;DESERET CAPITAL LETTER AY;Lu;0;L;;;;;N;;;;10434;
+1040D;DESERET CAPITAL LETTER OW;Lu;0;L;;;;;N;;;;10435;
+1040E;DESERET CAPITAL LETTER WU;Lu;0;L;;;;;N;;;;10436;
+1040F;DESERET CAPITAL LETTER YEE;Lu;0;L;;;;;N;;;;10437;
+10410;DESERET CAPITAL LETTER H;Lu;0;L;;;;;N;;;;10438;
+10411;DESERET CAPITAL LETTER PEE;Lu;0;L;;;;;N;;;;10439;
+10412;DESERET CAPITAL LETTER BEE;Lu;0;L;;;;;N;;;;1043A;
+10413;DESERET CAPITAL LETTER TEE;Lu;0;L;;;;;N;;;;1043B;
+10414;DESERET CAPITAL LETTER DEE;Lu;0;L;;;;;N;;;;1043C;
+10415;DESERET CAPITAL LETTER CHEE;Lu;0;L;;;;;N;;;;1043D;
+10416;DESERET CAPITAL LETTER JEE;Lu;0;L;;;;;N;;;;1043E;
+10417;DESERET CAPITAL LETTER KAY;Lu;0;L;;;;;N;;;;1043F;
+10418;DESERET CAPITAL LETTER GAY;Lu;0;L;;;;;N;;;;10440;
+10419;DESERET CAPITAL LETTER EF;Lu;0;L;;;;;N;;;;10441;
+1041A;DESERET CAPITAL LETTER VEE;Lu;0;L;;;;;N;;;;10442;
+1041B;DESERET CAPITAL LETTER ETH;Lu;0;L;;;;;N;;;;10443;
+1041C;DESERET CAPITAL LETTER THEE;Lu;0;L;;;;;N;;;;10444;
+1041D;DESERET CAPITAL LETTER ES;Lu;0;L;;;;;N;;;;10445;
+1041E;DESERET CAPITAL LETTER ZEE;Lu;0;L;;;;;N;;;;10446;
+1041F;DESERET CAPITAL LETTER ESH;Lu;0;L;;;;;N;;;;10447;
+10420;DESERET CAPITAL LETTER ZHEE;Lu;0;L;;;;;N;;;;10448;
+10421;DESERET CAPITAL LETTER ER;Lu;0;L;;;;;N;;;;10449;
+10422;DESERET CAPITAL LETTER EL;Lu;0;L;;;;;N;;;;1044A;
+10423;DESERET CAPITAL LETTER EM;Lu;0;L;;;;;N;;;;1044B;
+10424;DESERET CAPITAL LETTER EN;Lu;0;L;;;;;N;;;;1044C;
+10425;DESERET CAPITAL LETTER ENG;Lu;0;L;;;;;N;;;;1044D;
+10428;DESERET SMALL LETTER LONG I;Ll;0;L;;;;;N;;;10400;;10400
+10429;DESERET SMALL LETTER LONG E;Ll;0;L;;;;;N;;;10401;;10401
+1042A;DESERET SMALL LETTER LONG A;Ll;0;L;;;;;N;;;10402;;10402
+1042B;DESERET SMALL LETTER LONG AH;Ll;0;L;;;;;N;;;10403;;10403
+1042C;DESERET SMALL LETTER LONG O;Ll;0;L;;;;;N;;;10404;;10404
+1042D;DESERET SMALL LETTER LONG OO;Ll;0;L;;;;;N;;;10405;;10405
+1042E;DESERET SMALL LETTER SHORT I;Ll;0;L;;;;;N;;;10406;;10406
+1042F;DESERET SMALL LETTER SHORT E;Ll;0;L;;;;;N;;;10407;;10407
+10430;DESERET SMALL LETTER SHORT A;Ll;0;L;;;;;N;;;10408;;10408
+10431;DESERET SMALL LETTER SHORT AH;Ll;0;L;;;;;N;;;10409;;10409
+10432;DESERET SMALL LETTER SHORT O;Ll;0;L;;;;;N;;;1040A;;1040A
+10433;DESERET SMALL LETTER SHORT OO;Ll;0;L;;;;;N;;;1040B;;1040B
+10434;DESERET SMALL LETTER AY;Ll;0;L;;;;;N;;;1040C;;1040C
+10435;DESERET SMALL LETTER OW;Ll;0;L;;;;;N;;;1040D;;1040D
+10436;DESERET SMALL LETTER WU;Ll;0;L;;;;;N;;;1040E;;1040E
+10437;DESERET SMALL LETTER YEE;Ll;0;L;;;;;N;;;1040F;;1040F
+10438;DESERET SMALL LETTER H;Ll;0;L;;;;;N;;;10410;;10410
+10439;DESERET SMALL LETTER PEE;Ll;0;L;;;;;N;;;10411;;10411
+1043A;DESERET SMALL LETTER BEE;Ll;0;L;;;;;N;;;10412;;10412
+1043B;DESERET SMALL LETTER TEE;Ll;0;L;;;;;N;;;10413;;10413
+1043C;DESERET SMALL LETTER DEE;Ll;0;L;;;;;N;;;10414;;10414
+1043D;DESERET SMALL LETTER CHEE;Ll;0;L;;;;;N;;;10415;;10415
+1043E;DESERET SMALL LETTER JEE;Ll;0;L;;;;;N;;;10416;;10416
+1043F;DESERET SMALL LETTER KAY;Ll;0;L;;;;;N;;;10417;;10417
+10440;DESERET SMALL LETTER GAY;Ll;0;L;;;;;N;;;10418;;10418
+10441;DESERET SMALL LETTER EF;Ll;0;L;;;;;N;;;10419;;10419
+10442;DESERET SMALL LETTER VEE;Ll;0;L;;;;;N;;;1041A;;1041A
+10443;DESERET SMALL LETTER ETH;Ll;0;L;;;;;N;;;1041B;;1041B
+10444;DESERET SMALL LETTER THEE;Ll;0;L;;;;;N;;;1041C;;1041C
+10445;DESERET SMALL LETTER ES;Ll;0;L;;;;;N;;;1041D;;1041D
+10446;DESERET SMALL LETTER ZEE;Ll;0;L;;;;;N;;;1041E;;1041E
+10447;DESERET SMALL LETTER ESH;Ll;0;L;;;;;N;;;1041F;;1041F
+10448;DESERET SMALL LETTER ZHEE;Ll;0;L;;;;;N;;;10420;;10420
+10449;DESERET SMALL LETTER ER;Ll;0;L;;;;;N;;;10421;;10421
+1044A;DESERET SMALL LETTER EL;Ll;0;L;;;;;N;;;10422;;10422
+1044B;DESERET SMALL LETTER EM;Ll;0;L;;;;;N;;;10423;;10423
+1044C;DESERET SMALL LETTER EN;Ll;0;L;;;;;N;;;10424;;10424
+1044D;DESERET SMALL LETTER ENG;Ll;0;L;;;;;N;;;10425;;10425
+1D000;BYZANTINE MUSICAL SYMBOL PSILI;So;0;L;;;;;N;;;;;
+1D001;BYZANTINE MUSICAL SYMBOL DASEIA;So;0;L;;;;;N;;;;;
+1D002;BYZANTINE MUSICAL SYMBOL PERISPOMENI;So;0;L;;;;;N;;;;;
+1D003;BYZANTINE MUSICAL SYMBOL OXEIA EKFONITIKON;So;0;L;;;;;N;;;;;
+1D004;BYZANTINE MUSICAL SYMBOL OXEIA DIPLI;So;0;L;;;;;N;;;;;
+1D005;BYZANTINE MUSICAL SYMBOL VAREIA EKFONITIKON;So;0;L;;;;;N;;;;;
+1D006;BYZANTINE MUSICAL SYMBOL VAREIA DIPLI;So;0;L;;;;;N;;;;;
+1D007;BYZANTINE MUSICAL SYMBOL KATHISTI;So;0;L;;;;;N;;;;;
+1D008;BYZANTINE MUSICAL SYMBOL SYRMATIKI;So;0;L;;;;;N;;;;;
+1D009;BYZANTINE MUSICAL SYMBOL PARAKLITIKI;So;0;L;;;;;N;;;;;
+1D00A;BYZANTINE MUSICAL SYMBOL YPOKRISIS;So;0;L;;;;;N;;;;;
+1D00B;BYZANTINE MUSICAL SYMBOL YPOKRISIS DIPLI;So;0;L;;;;;N;;;;;
+1D00C;BYZANTINE MUSICAL SYMBOL KREMASTI;So;0;L;;;;;N;;;;;
+1D00D;BYZANTINE MUSICAL SYMBOL APESO EKFONITIKON;So;0;L;;;;;N;;;;;
+1D00E;BYZANTINE MUSICAL SYMBOL EXO EKFONITIKON;So;0;L;;;;;N;;;;;
+1D00F;BYZANTINE MUSICAL SYMBOL TELEIA;So;0;L;;;;;N;;;;;
+1D010;BYZANTINE MUSICAL SYMBOL KENTIMATA;So;0;L;;;;;N;;;;;
+1D011;BYZANTINE MUSICAL SYMBOL APOSTROFOS;So;0;L;;;;;N;;;;;
+1D012;BYZANTINE MUSICAL SYMBOL APOSTROFOS DIPLI;So;0;L;;;;;N;;;;;
+1D013;BYZANTINE MUSICAL SYMBOL SYNEVMA;So;0;L;;;;;N;;;;;
+1D014;BYZANTINE MUSICAL SYMBOL THITA;So;0;L;;;;;N;;;;;
+1D015;BYZANTINE MUSICAL SYMBOL OLIGON ARCHAION;So;0;L;;;;;N;;;;;
+1D016;BYZANTINE MUSICAL SYMBOL GORGON ARCHAION;So;0;L;;;;;N;;;;;
+1D017;BYZANTINE MUSICAL SYMBOL PSILON;So;0;L;;;;;N;;;;;
+1D018;BYZANTINE MUSICAL SYMBOL CHAMILON;So;0;L;;;;;N;;;;;
+1D019;BYZANTINE MUSICAL SYMBOL VATHY;So;0;L;;;;;N;;;;;
+1D01A;BYZANTINE MUSICAL SYMBOL ISON ARCHAION;So;0;L;;;;;N;;;;;
+1D01B;BYZANTINE MUSICAL SYMBOL KENTIMA ARCHAION;So;0;L;;;;;N;;;;;
+1D01C;BYZANTINE MUSICAL SYMBOL KENTIMATA ARCHAION;So;0;L;;;;;N;;;;;
+1D01D;BYZANTINE MUSICAL SYMBOL SAXIMATA;So;0;L;;;;;N;;;;;
+1D01E;BYZANTINE MUSICAL SYMBOL PARICHON;So;0;L;;;;;N;;;;;
+1D01F;BYZANTINE MUSICAL SYMBOL STAVROS APODEXIA;So;0;L;;;;;N;;;;;
+1D020;BYZANTINE MUSICAL SYMBOL OXEIAI ARCHAION;So;0;L;;;;;N;;;;;
+1D021;BYZANTINE MUSICAL SYMBOL VAREIAI ARCHAION;So;0;L;;;;;N;;;;;
+1D022;BYZANTINE MUSICAL SYMBOL APODERMA ARCHAION;So;0;L;;;;;N;;;;;
+1D023;BYZANTINE MUSICAL SYMBOL APOTHEMA;So;0;L;;;;;N;;;;;
+1D024;BYZANTINE MUSICAL SYMBOL KLASMA;So;0;L;;;;;N;;;;;
+1D025;BYZANTINE MUSICAL SYMBOL REVMA;So;0;L;;;;;N;;;;;
+1D026;BYZANTINE MUSICAL SYMBOL PIASMA ARCHAION;So;0;L;;;;;N;;;;;
+1D027;BYZANTINE MUSICAL SYMBOL TINAGMA;So;0;L;;;;;N;;;;;
+1D028;BYZANTINE MUSICAL SYMBOL ANATRICHISMA;So;0;L;;;;;N;;;;;
+1D029;BYZANTINE MUSICAL SYMBOL SEISMA;So;0;L;;;;;N;;;;;
+1D02A;BYZANTINE MUSICAL SYMBOL SYNAGMA ARCHAION;So;0;L;;;;;N;;;;;
+1D02B;BYZANTINE MUSICAL SYMBOL SYNAGMA META STAVROU;So;0;L;;;;;N;;;;;
+1D02C;BYZANTINE MUSICAL SYMBOL OYRANISMA ARCHAION;So;0;L;;;;;N;;;;;
+1D02D;BYZANTINE MUSICAL SYMBOL THEMA;So;0;L;;;;;N;;;;;
+1D02E;BYZANTINE MUSICAL SYMBOL LEMOI;So;0;L;;;;;N;;;;;
+1D02F;BYZANTINE MUSICAL SYMBOL DYO;So;0;L;;;;;N;;;;;
+1D030;BYZANTINE MUSICAL SYMBOL TRIA;So;0;L;;;;;N;;;;;
+1D031;BYZANTINE MUSICAL SYMBOL TESSERA;So;0;L;;;;;N;;;;;
+1D032;BYZANTINE MUSICAL SYMBOL KRATIMATA;So;0;L;;;;;N;;;;;
+1D033;BYZANTINE MUSICAL SYMBOL APESO EXO NEO;So;0;L;;;;;N;;;;;
+1D034;BYZANTINE MUSICAL SYMBOL FTHORA ARCHAION;So;0;L;;;;;N;;;;;
+1D035;BYZANTINE MUSICAL SYMBOL IMIFTHORA;So;0;L;;;;;N;;;;;
+1D036;BYZANTINE MUSICAL SYMBOL TROMIKON ARCHAION;So;0;L;;;;;N;;;;;
+1D037;BYZANTINE MUSICAL SYMBOL KATAVA TROMIKON;So;0;L;;;;;N;;;;;
+1D038;BYZANTINE MUSICAL SYMBOL PELASTON;So;0;L;;;;;N;;;;;
+1D039;BYZANTINE MUSICAL SYMBOL PSIFISTON;So;0;L;;;;;N;;;;;
+1D03A;BYZANTINE MUSICAL SYMBOL KONTEVMA;So;0;L;;;;;N;;;;;
+1D03B;BYZANTINE MUSICAL SYMBOL CHOREVMA ARCHAION;So;0;L;;;;;N;;;;;
+1D03C;BYZANTINE MUSICAL SYMBOL RAPISMA;So;0;L;;;;;N;;;;;
+1D03D;BYZANTINE MUSICAL SYMBOL PARAKALESMA ARCHAION;So;0;L;;;;;N;;;;;
+1D03E;BYZANTINE MUSICAL SYMBOL PARAKLITIKI ARCHAION;So;0;L;;;;;N;;;;;
+1D03F;BYZANTINE MUSICAL SYMBOL ICHADIN;So;0;L;;;;;N;;;;;
+1D040;BYZANTINE MUSICAL SYMBOL NANA;So;0;L;;;;;N;;;;;
+1D041;BYZANTINE MUSICAL SYMBOL PETASMA;So;0;L;;;;;N;;;;;
+1D042;BYZANTINE MUSICAL SYMBOL KONTEVMA ALLO;So;0;L;;;;;N;;;;;
+1D043;BYZANTINE MUSICAL SYMBOL TROMIKON ALLO;So;0;L;;;;;N;;;;;
+1D044;BYZANTINE MUSICAL SYMBOL STRAGGISMATA;So;0;L;;;;;N;;;;;
+1D045;BYZANTINE MUSICAL SYMBOL GRONTHISMATA;So;0;L;;;;;N;;;;;
+1D046;BYZANTINE MUSICAL SYMBOL ISON NEO;So;0;L;;;;;N;;;;;
+1D047;BYZANTINE MUSICAL SYMBOL OLIGON NEO;So;0;L;;;;;N;;;;;
+1D048;BYZANTINE MUSICAL SYMBOL OXEIA NEO;So;0;L;;;;;N;;;;;
+1D049;BYZANTINE MUSICAL SYMBOL PETASTI;So;0;L;;;;;N;;;;;
+1D04A;BYZANTINE MUSICAL SYMBOL KOUFISMA;So;0;L;;;;;N;;;;;
+1D04B;BYZANTINE MUSICAL SYMBOL PETASTOKOUFISMA;So;0;L;;;;;N;;;;;
+1D04C;BYZANTINE MUSICAL SYMBOL KRATIMOKOUFISMA;So;0;L;;;;;N;;;;;
+1D04D;BYZANTINE MUSICAL SYMBOL PELASTON NEO;So;0;L;;;;;N;;;;;
+1D04E;BYZANTINE MUSICAL SYMBOL KENTIMATA NEO ANO;So;0;L;;;;;N;;;;;
+1D04F;BYZANTINE MUSICAL SYMBOL KENTIMA NEO ANO;So;0;L;;;;;N;;;;;
+1D050;BYZANTINE MUSICAL SYMBOL YPSILI;So;0;L;;;;;N;;;;;
+1D051;BYZANTINE MUSICAL SYMBOL APOSTROFOS NEO;So;0;L;;;;;N;;;;;
+1D052;BYZANTINE MUSICAL SYMBOL APOSTROFOI SYNDESMOS NEO;So;0;L;;;;;N;;;;;
+1D053;BYZANTINE MUSICAL SYMBOL YPORROI;So;0;L;;;;;N;;;;;
+1D054;BYZANTINE MUSICAL SYMBOL KRATIMOYPORROON;So;0;L;;;;;N;;;;;
+1D055;BYZANTINE MUSICAL SYMBOL ELAFRON;So;0;L;;;;;N;;;;;
+1D056;BYZANTINE MUSICAL SYMBOL CHAMILI;So;0;L;;;;;N;;;;;
+1D057;BYZANTINE MUSICAL SYMBOL MIKRON ISON;So;0;L;;;;;N;;;;;
+1D058;BYZANTINE MUSICAL SYMBOL VAREIA NEO;So;0;L;;;;;N;;;;;
+1D059;BYZANTINE MUSICAL SYMBOL PIASMA NEO;So;0;L;;;;;N;;;;;
+1D05A;BYZANTINE MUSICAL SYMBOL PSIFISTON NEO;So;0;L;;;;;N;;;;;
+1D05B;BYZANTINE MUSICAL SYMBOL OMALON;So;0;L;;;;;N;;;;;
+1D05C;BYZANTINE MUSICAL SYMBOL ANTIKENOMA;So;0;L;;;;;N;;;;;
+1D05D;BYZANTINE MUSICAL SYMBOL LYGISMA;So;0;L;;;;;N;;;;;
+1D05E;BYZANTINE MUSICAL SYMBOL PARAKLITIKI NEO;So;0;L;;;;;N;;;;;
+1D05F;BYZANTINE MUSICAL SYMBOL PARAKALESMA NEO;So;0;L;;;;;N;;;;;
+1D060;BYZANTINE MUSICAL SYMBOL ETERON PARAKALESMA;So;0;L;;;;;N;;;;;
+1D061;BYZANTINE MUSICAL SYMBOL KYLISMA;So;0;L;;;;;N;;;;;
+1D062;BYZANTINE MUSICAL SYMBOL ANTIKENOKYLISMA;So;0;L;;;;;N;;;;;
+1D063;BYZANTINE MUSICAL SYMBOL TROMIKON NEO;So;0;L;;;;;N;;;;;
+1D064;BYZANTINE MUSICAL SYMBOL EKSTREPTON;So;0;L;;;;;N;;;;;
+1D065;BYZANTINE MUSICAL SYMBOL SYNAGMA NEO;So;0;L;;;;;N;;;;;
+1D066;BYZANTINE MUSICAL SYMBOL SYRMA;So;0;L;;;;;N;;;;;
+1D067;BYZANTINE MUSICAL SYMBOL CHOREVMA NEO;So;0;L;;;;;N;;;;;
+1D068;BYZANTINE MUSICAL SYMBOL EPEGERMA;So;0;L;;;;;N;;;;;
+1D069;BYZANTINE MUSICAL SYMBOL SEISMA NEO;So;0;L;;;;;N;;;;;
+1D06A;BYZANTINE MUSICAL SYMBOL XIRON KLASMA;So;0;L;;;;;N;;;;;
+1D06B;BYZANTINE MUSICAL SYMBOL TROMIKOPSIFISTON;So;0;L;;;;;N;;;;;
+1D06C;BYZANTINE MUSICAL SYMBOL PSIFISTOLYGISMA;So;0;L;;;;;N;;;;;
+1D06D;BYZANTINE MUSICAL SYMBOL TROMIKOLYGISMA;So;0;L;;;;;N;;;;;
+1D06E;BYZANTINE MUSICAL SYMBOL TROMIKOPARAKALESMA;So;0;L;;;;;N;;;;;
+1D06F;BYZANTINE MUSICAL SYMBOL PSIFISTOPARAKALESMA;So;0;L;;;;;N;;;;;
+1D070;BYZANTINE MUSICAL SYMBOL TROMIKOSYNAGMA;So;0;L;;;;;N;;;;;
+1D071;BYZANTINE MUSICAL SYMBOL PSIFISTOSYNAGMA;So;0;L;;;;;N;;;;;
+1D072;BYZANTINE MUSICAL SYMBOL GORGOSYNTHETON;So;0;L;;;;;N;;;;;
+1D073;BYZANTINE MUSICAL SYMBOL ARGOSYNTHETON;So;0;L;;;;;N;;;;;
+1D074;BYZANTINE MUSICAL SYMBOL ETERON ARGOSYNTHETON;So;0;L;;;;;N;;;;;
+1D075;BYZANTINE MUSICAL SYMBOL OYRANISMA NEO;So;0;L;;;;;N;;;;;
+1D076;BYZANTINE MUSICAL SYMBOL THEMATISMOS ESO;So;0;L;;;;;N;;;;;
+1D077;BYZANTINE MUSICAL SYMBOL THEMATISMOS EXO;So;0;L;;;;;N;;;;;
+1D078;BYZANTINE MUSICAL SYMBOL THEMA APLOUN;So;0;L;;;;;N;;;;;
+1D079;BYZANTINE MUSICAL SYMBOL THES KAI APOTHES;So;0;L;;;;;N;;;;;
+1D07A;BYZANTINE MUSICAL SYMBOL KATAVASMA;So;0;L;;;;;N;;;;;
+1D07B;BYZANTINE MUSICAL SYMBOL ENDOFONON;So;0;L;;;;;N;;;;;
+1D07C;BYZANTINE MUSICAL SYMBOL YFEN KATO;So;0;L;;;;;N;;;;;
+1D07D;BYZANTINE MUSICAL SYMBOL YFEN ANO;So;0;L;;;;;N;;;;;
+1D07E;BYZANTINE MUSICAL SYMBOL STAVROS;So;0;L;;;;;N;;;;;
+1D07F;BYZANTINE MUSICAL SYMBOL KLASMA ANO;So;0;L;;;;;N;;;;;
+1D080;BYZANTINE MUSICAL SYMBOL DIPLI ARCHAION;So;0;L;;;;;N;;;;;
+1D081;BYZANTINE MUSICAL SYMBOL KRATIMA ARCHAION;So;0;L;;;;;N;;;;;
+1D082;BYZANTINE MUSICAL SYMBOL KRATIMA ALLO;So;0;L;;;;;N;;;;;
+1D083;BYZANTINE MUSICAL SYMBOL KRATIMA NEO;So;0;L;;;;;N;;;;;
+1D084;BYZANTINE MUSICAL SYMBOL APODERMA NEO;So;0;L;;;;;N;;;;;
+1D085;BYZANTINE MUSICAL SYMBOL APLI;So;0;L;;;;;N;;;;;
+1D086;BYZANTINE MUSICAL SYMBOL DIPLI;So;0;L;;;;;N;;;;;
+1D087;BYZANTINE MUSICAL SYMBOL TRIPLI;So;0;L;;;;;N;;;;;
+1D088;BYZANTINE MUSICAL SYMBOL TETRAPLI;So;0;L;;;;;N;;;;;
+1D089;BYZANTINE MUSICAL SYMBOL KORONIS;So;0;L;;;;;N;;;;;
+1D08A;BYZANTINE MUSICAL SYMBOL LEIMMA ENOS CHRONOU;So;0;L;;;;;N;;;;;
+1D08B;BYZANTINE MUSICAL SYMBOL LEIMMA DYO CHRONON;So;0;L;;;;;N;;;;;
+1D08C;BYZANTINE MUSICAL SYMBOL LEIMMA TRION CHRONON;So;0;L;;;;;N;;;;;
+1D08D;BYZANTINE MUSICAL SYMBOL LEIMMA TESSARON CHRONON;So;0;L;;;;;N;;;;;
+1D08E;BYZANTINE MUSICAL SYMBOL LEIMMA IMISEOS CHRONOU;So;0;L;;;;;N;;;;;
+1D08F;BYZANTINE MUSICAL SYMBOL GORGON NEO ANO;So;0;L;;;;;N;;;;;
+1D090;BYZANTINE MUSICAL SYMBOL GORGON PARESTIGMENON ARISTERA;So;0;L;;;;;N;;;;;
+1D091;BYZANTINE MUSICAL SYMBOL GORGON PARESTIGMENON DEXIA;So;0;L;;;;;N;;;;;
+1D092;BYZANTINE MUSICAL SYMBOL DIGORGON;So;0;L;;;;;N;;;;;
+1D093;BYZANTINE MUSICAL SYMBOL DIGORGON PARESTIGMENON ARISTERA KATO;So;0;L;;;;;N;;;;;
+1D094;BYZANTINE MUSICAL SYMBOL DIGORGON PARESTIGMENON ARISTERA ANO;So;0;L;;;;;N;;;;;
+1D095;BYZANTINE MUSICAL SYMBOL DIGORGON PARESTIGMENON DEXIA;So;0;L;;;;;N;;;;;
+1D096;BYZANTINE MUSICAL SYMBOL TRIGORGON;So;0;L;;;;;N;;;;;
+1D097;BYZANTINE MUSICAL SYMBOL ARGON;So;0;L;;;;;N;;;;;
+1D098;BYZANTINE MUSICAL SYMBOL IMIDIARGON;So;0;L;;;;;N;;;;;
+1D099;BYZANTINE MUSICAL SYMBOL DIARGON;So;0;L;;;;;N;;;;;
+1D09A;BYZANTINE MUSICAL SYMBOL AGOGI POLI ARGI;So;0;L;;;;;N;;;;;
+1D09B;BYZANTINE MUSICAL SYMBOL AGOGI ARGOTERI;So;0;L;;;;;N;;;;;
+1D09C;BYZANTINE MUSICAL SYMBOL AGOGI ARGI;So;0;L;;;;;N;;;;;
+1D09D;BYZANTINE MUSICAL SYMBOL AGOGI METRIA;So;0;L;;;;;N;;;;;
+1D09E;BYZANTINE MUSICAL SYMBOL AGOGI MESI;So;0;L;;;;;N;;;;;
+1D09F;BYZANTINE MUSICAL SYMBOL AGOGI GORGI;So;0;L;;;;;N;;;;;
+1D0A0;BYZANTINE MUSICAL SYMBOL AGOGI GORGOTERI;So;0;L;;;;;N;;;;;
+1D0A1;BYZANTINE MUSICAL SYMBOL AGOGI POLI GORGI;So;0;L;;;;;N;;;;;
+1D0A2;BYZANTINE MUSICAL SYMBOL MARTYRIA PROTOS ICHOS;So;0;L;;;;;N;;;;;
+1D0A3;BYZANTINE MUSICAL SYMBOL MARTYRIA ALLI PROTOS ICHOS;So;0;L;;;;;N;;;;;
+1D0A4;BYZANTINE MUSICAL SYMBOL MARTYRIA DEYTEROS ICHOS;So;0;L;;;;;N;;;;;
+1D0A5;BYZANTINE MUSICAL SYMBOL MARTYRIA ALLI DEYTEROS ICHOS;So;0;L;;;;;N;;;;;
+1D0A6;BYZANTINE MUSICAL SYMBOL MARTYRIA TRITOS ICHOS;So;0;L;;;;;N;;;;;
+1D0A7;BYZANTINE MUSICAL SYMBOL MARTYRIA TRIFONIAS;So;0;L;;;;;N;;;;;
+1D0A8;BYZANTINE MUSICAL SYMBOL MARTYRIA TETARTOS ICHOS;So;0;L;;;;;N;;;;;
+1D0A9;BYZANTINE MUSICAL SYMBOL MARTYRIA TETARTOS LEGETOS ICHOS;So;0;L;;;;;N;;;;;
+1D0AA;BYZANTINE MUSICAL SYMBOL MARTYRIA LEGETOS ICHOS;So;0;L;;;;;N;;;;;
+1D0AB;BYZANTINE MUSICAL SYMBOL MARTYRIA PLAGIOS ICHOS;So;0;L;;;;;N;;;;;
+1D0AC;BYZANTINE MUSICAL SYMBOL ISAKIA TELOUS ICHIMATOS;So;0;L;;;;;N;;;;;
+1D0AD;BYZANTINE MUSICAL SYMBOL APOSTROFOI TELOUS ICHIMATOS;So;0;L;;;;;N;;;;;
+1D0AE;BYZANTINE MUSICAL SYMBOL FANEROSIS TETRAFONIAS;So;0;L;;;;;N;;;;;
+1D0AF;BYZANTINE MUSICAL SYMBOL FANEROSIS MONOFONIAS;So;0;L;;;;;N;;;;;
+1D0B0;BYZANTINE MUSICAL SYMBOL FANEROSIS DIFONIAS;So;0;L;;;;;N;;;;;
+1D0B1;BYZANTINE MUSICAL SYMBOL MARTYRIA VARYS ICHOS;So;0;L;;;;;N;;;;;
+1D0B2;BYZANTINE MUSICAL SYMBOL MARTYRIA PROTOVARYS ICHOS;So;0;L;;;;;N;;;;;
+1D0B3;BYZANTINE MUSICAL SYMBOL MARTYRIA PLAGIOS TETARTOS ICHOS;So;0;L;;;;;N;;;;;
+1D0B4;BYZANTINE MUSICAL SYMBOL GORTHMIKON N APLOUN;So;0;L;;;;;N;;;;;
+1D0B5;BYZANTINE MUSICAL SYMBOL GORTHMIKON N DIPLOUN;So;0;L;;;;;N;;;;;
+1D0B6;BYZANTINE MUSICAL SYMBOL ENARXIS KAI FTHORA VOU;So;0;L;;;;;N;;;;;
+1D0B7;BYZANTINE MUSICAL SYMBOL IMIFONON;So;0;L;;;;;N;;;;;
+1D0B8;BYZANTINE MUSICAL SYMBOL IMIFTHORON;So;0;L;;;;;N;;;;;
+1D0B9;BYZANTINE MUSICAL SYMBOL FTHORA ARCHAION DEYTEROU ICHOU;So;0;L;;;;;N;;;;;
+1D0BA;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI PA;So;0;L;;;;;N;;;;;
+1D0BB;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI NANA;So;0;L;;;;;N;;;;;
+1D0BC;BYZANTINE MUSICAL SYMBOL FTHORA NAOS ICHOS;So;0;L;;;;;N;;;;;
+1D0BD;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI DI;So;0;L;;;;;N;;;;;
+1D0BE;BYZANTINE MUSICAL SYMBOL FTHORA SKLIRON DIATONON DI;So;0;L;;;;;N;;;;;
+1D0BF;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI KE;So;0;L;;;;;N;;;;;
+1D0C0;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI ZO;So;0;L;;;;;N;;;;;
+1D0C1;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI NI KATO;So;0;L;;;;;N;;;;;
+1D0C2;BYZANTINE MUSICAL SYMBOL FTHORA DIATONIKI NI ANO;So;0;L;;;;;N;;;;;
+1D0C3;BYZANTINE MUSICAL SYMBOL FTHORA MALAKON CHROMA DIFONIAS;So;0;L;;;;;N;;;;;
+1D0C4;BYZANTINE MUSICAL SYMBOL FTHORA MALAKON CHROMA MONOFONIAS;So;0;L;;;;;N;;;;;
+1D0C5;BYZANTINE MUSICAL SYMBOL FHTORA SKLIRON CHROMA VASIS;So;0;L;;;;;N;;;;;
+1D0C6;BYZANTINE MUSICAL SYMBOL FTHORA SKLIRON CHROMA SYNAFI;So;0;L;;;;;N;;;;;
+1D0C7;BYZANTINE MUSICAL SYMBOL FTHORA NENANO;So;0;L;;;;;N;;;;;
+1D0C8;BYZANTINE MUSICAL SYMBOL CHROA ZYGOS;So;0;L;;;;;N;;;;;
+1D0C9;BYZANTINE MUSICAL SYMBOL CHROA KLITON;So;0;L;;;;;N;;;;;
+1D0CA;BYZANTINE MUSICAL SYMBOL CHROA SPATHI;So;0;L;;;;;N;;;;;
+1D0CB;BYZANTINE MUSICAL SYMBOL FTHORA I YFESIS TETARTIMORION;So;0;L;;;;;N;;;;;
+1D0CC;BYZANTINE MUSICAL SYMBOL FTHORA ENARMONIOS ANTIFONIA;So;0;L;;;;;N;;;;;
+1D0CD;BYZANTINE MUSICAL SYMBOL YFESIS TRITIMORION;So;0;L;;;;;N;;;;;
+1D0CE;BYZANTINE MUSICAL SYMBOL DIESIS TRITIMORION;So;0;L;;;;;N;;;;;
+1D0CF;BYZANTINE MUSICAL SYMBOL DIESIS TETARTIMORION;So;0;L;;;;;N;;;;;
+1D0D0;BYZANTINE MUSICAL SYMBOL DIESIS APLI DYO DODEKATA;So;0;L;;;;;N;;;;;
+1D0D1;BYZANTINE MUSICAL SYMBOL DIESIS MONOGRAMMOS TESSERA DODEKATA;So;0;L;;;;;N;;;;;
+1D0D2;BYZANTINE MUSICAL SYMBOL DIESIS DIGRAMMOS EX DODEKATA;So;0;L;;;;;N;;;;;
+1D0D3;BYZANTINE MUSICAL SYMBOL DIESIS TRIGRAMMOS OKTO DODEKATA;So;0;L;;;;;N;;;;;
+1D0D4;BYZANTINE MUSICAL SYMBOL YFESIS APLI DYO DODEKATA;So;0;L;;;;;N;;;;;
+1D0D5;BYZANTINE MUSICAL SYMBOL YFESIS MONOGRAMMOS TESSERA DODEKATA;So;0;L;;;;;N;;;;;
+1D0D6;BYZANTINE MUSICAL SYMBOL YFESIS DIGRAMMOS EX DODEKATA;So;0;L;;;;;N;;;;;
+1D0D7;BYZANTINE MUSICAL SYMBOL YFESIS TRIGRAMMOS OKTO DODEKATA;So;0;L;;;;;N;;;;;
+1D0D8;BYZANTINE MUSICAL SYMBOL GENIKI DIESIS;So;0;L;;;;;N;;;;;
+1D0D9;BYZANTINE MUSICAL SYMBOL GENIKI YFESIS;So;0;L;;;;;N;;;;;
+1D0DA;BYZANTINE MUSICAL SYMBOL DIASTOLI APLI MIKRI;So;0;L;;;;;N;;;;;
+1D0DB;BYZANTINE MUSICAL SYMBOL DIASTOLI APLI MEGALI;So;0;L;;;;;N;;;;;
+1D0DC;BYZANTINE MUSICAL SYMBOL DIASTOLI DIPLI;So;0;L;;;;;N;;;;;
+1D0DD;BYZANTINE MUSICAL SYMBOL DIASTOLI THESEOS;So;0;L;;;;;N;;;;;
+1D0DE;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS;So;0;L;;;;;N;;;;;
+1D0DF;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS DISIMOU;So;0;L;;;;;N;;;;;
+1D0E0;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS TRISIMOU;So;0;L;;;;;N;;;;;
+1D0E1;BYZANTINE MUSICAL SYMBOL SIMANSIS THESEOS TETRASIMOU;So;0;L;;;;;N;;;;;
+1D0E2;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS;So;0;L;;;;;N;;;;;
+1D0E3;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS DISIMOU;So;0;L;;;;;N;;;;;
+1D0E4;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS TRISIMOU;So;0;L;;;;;N;;;;;
+1D0E5;BYZANTINE MUSICAL SYMBOL SIMANSIS ARSEOS TETRASIMOU;So;0;L;;;;;N;;;;;
+1D0E6;BYZANTINE MUSICAL SYMBOL DIGRAMMA GG;So;0;L;;;;;N;;;;;
+1D0E7;BYZANTINE MUSICAL SYMBOL DIFTOGGOS OU;So;0;L;;;;;N;;;;;
+1D0E8;BYZANTINE MUSICAL SYMBOL STIGMA;So;0;L;;;;;N;;;;;
+1D0E9;BYZANTINE MUSICAL SYMBOL ARKTIKO PA;So;0;L;;;;;N;;;;;
+1D0EA;BYZANTINE MUSICAL SYMBOL ARKTIKO VOU;So;0;L;;;;;N;;;;;
+1D0EB;BYZANTINE MUSICAL SYMBOL ARKTIKO GA;So;0;L;;;;;N;;;;;
+1D0EC;BYZANTINE MUSICAL SYMBOL ARKTIKO DI;So;0;L;;;;;N;;;;;
+1D0ED;BYZANTINE MUSICAL SYMBOL ARKTIKO KE;So;0;L;;;;;N;;;;;
+1D0EE;BYZANTINE MUSICAL SYMBOL ARKTIKO ZO;So;0;L;;;;;N;;;;;
+1D0EF;BYZANTINE MUSICAL SYMBOL ARKTIKO NI;So;0;L;;;;;N;;;;;
+1D0F0;BYZANTINE MUSICAL SYMBOL KENTIMATA NEO MESO;So;0;L;;;;;N;;;;;
+1D0F1;BYZANTINE MUSICAL SYMBOL KENTIMA NEO MESO;So;0;L;;;;;N;;;;;
+1D0F2;BYZANTINE MUSICAL SYMBOL KENTIMATA NEO KATO;So;0;L;;;;;N;;;;;
+1D0F3;BYZANTINE MUSICAL SYMBOL KENTIMA NEO KATO;So;0;L;;;;;N;;;;;
+1D0F4;BYZANTINE MUSICAL SYMBOL KLASMA KATO;So;0;L;;;;;N;;;;;
+1D0F5;BYZANTINE MUSICAL SYMBOL GORGON NEO KATO;So;0;L;;;;;N;;;;;
+1D100;MUSICAL SYMBOL SINGLE BARLINE;So;0;L;;;;;N;;;;;
+1D101;MUSICAL SYMBOL DOUBLE BARLINE;So;0;L;;;;;N;;;;;
+1D102;MUSICAL SYMBOL FINAL BARLINE;So;0;L;;;;;N;;;;;
+1D103;MUSICAL SYMBOL REVERSE FINAL BARLINE;So;0;L;;;;;N;;;;;
+1D104;MUSICAL SYMBOL DASHED BARLINE;So;0;L;;;;;N;;;;;
+1D105;MUSICAL SYMBOL SHORT BARLINE;So;0;L;;;;;N;;;;;
+1D106;MUSICAL SYMBOL LEFT REPEAT SIGN;So;0;L;;;;;N;;;;;
+1D107;MUSICAL SYMBOL RIGHT REPEAT SIGN;So;0;L;;;;;N;;;;;
+1D108;MUSICAL SYMBOL REPEAT DOTS;So;0;L;;;;;N;;;;;
+1D109;MUSICAL SYMBOL DAL SEGNO;So;0;L;;;;;N;;;;;
+1D10A;MUSICAL SYMBOL DA CAPO;So;0;L;;;;;N;;;;;
+1D10B;MUSICAL SYMBOL SEGNO;So;0;L;;;;;N;;;;;
+1D10C;MUSICAL SYMBOL CODA;So;0;L;;;;;N;;;;;
+1D10D;MUSICAL SYMBOL REPEATED FIGURE-1;So;0;L;;;;;N;;;;;
+1D10E;MUSICAL SYMBOL REPEATED FIGURE-2;So;0;L;;;;;N;;;;;
+1D10F;MUSICAL SYMBOL REPEATED FIGURE-3;So;0;L;;;;;N;;;;;
+1D110;MUSICAL SYMBOL FERMATA;So;0;L;;;;;N;;;;;
+1D111;MUSICAL SYMBOL FERMATA BELOW;So;0;L;;;;;N;;;;;
+1D112;MUSICAL SYMBOL BREATH MARK;So;0;L;;;;;N;;;;;
+1D113;MUSICAL SYMBOL CAESURA;So;0;L;;;;;N;;;;;
+1D114;MUSICAL SYMBOL BRACE;So;0;L;;;;;N;;;;;
+1D115;MUSICAL SYMBOL BRACKET;So;0;L;;;;;N;;;;;
+1D116;MUSICAL SYMBOL ONE-LINE STAFF;So;0;L;;;;;N;;;;;
+1D117;MUSICAL SYMBOL TWO-LINE STAFF;So;0;L;;;;;N;;;;;
+1D118;MUSICAL SYMBOL THREE-LINE STAFF;So;0;L;;;;;N;;;;;
+1D119;MUSICAL SYMBOL FOUR-LINE STAFF;So;0;L;;;;;N;;;;;
+1D11A;MUSICAL SYMBOL FIVE-LINE STAFF;So;0;L;;;;;N;;;;;
+1D11B;MUSICAL SYMBOL SIX-LINE STAFF;So;0;L;;;;;N;;;;;
+1D11C;MUSICAL SYMBOL SIX-STRING FRETBOARD;So;0;L;;;;;N;;;;;
+1D11D;MUSICAL SYMBOL FOUR-STRING FRETBOARD;So;0;L;;;;;N;;;;;
+1D11E;MUSICAL SYMBOL G CLEF;So;0;L;;;;;N;;;;;
+1D11F;MUSICAL SYMBOL G CLEF OTTAVA ALTA;So;0;L;;;;;N;;;;;
+1D120;MUSICAL SYMBOL G CLEF OTTAVA BASSA;So;0;L;;;;;N;;;;;
+1D121;MUSICAL SYMBOL C CLEF;So;0;L;;;;;N;;;;;
+1D122;MUSICAL SYMBOL F CLEF;So;0;L;;;;;N;;;;;
+1D123;MUSICAL SYMBOL F CLEF OTTAVA ALTA;So;0;L;;;;;N;;;;;
+1D124;MUSICAL SYMBOL F CLEF OTTAVA BASSA;So;0;L;;;;;N;;;;;
+1D125;MUSICAL SYMBOL DRUM CLEF-1;So;0;L;;;;;N;;;;;
+1D126;MUSICAL SYMBOL DRUM CLEF-2;So;0;L;;;;;N;;;;;
+1D12A;MUSICAL SYMBOL DOUBLE SHARP;So;0;L;;;;;N;;;;;
+1D12B;MUSICAL SYMBOL DOUBLE FLAT;So;0;L;;;;;N;;;;;
+1D12C;MUSICAL SYMBOL FLAT UP;So;0;L;;;;;N;;;;;
+1D12D;MUSICAL SYMBOL FLAT DOWN;So;0;L;;;;;N;;;;;
+1D12E;MUSICAL SYMBOL NATURAL UP;So;0;L;;;;;N;;;;;
+1D12F;MUSICAL SYMBOL NATURAL DOWN;So;0;L;;;;;N;;;;;
+1D130;MUSICAL SYMBOL SHARP UP;So;0;L;;;;;N;;;;;
+1D131;MUSICAL SYMBOL SHARP DOWN;So;0;L;;;;;N;;;;;
+1D132;MUSICAL SYMBOL QUARTER TONE SHARP;So;0;L;;;;;N;;;;;
+1D133;MUSICAL SYMBOL QUARTER TONE FLAT;So;0;L;;;;;N;;;;;
+1D134;MUSICAL SYMBOL COMMON TIME;So;0;L;;;;;N;;;;;
+1D135;MUSICAL SYMBOL CUT TIME;So;0;L;;;;;N;;;;;
+1D136;MUSICAL SYMBOL OTTAVA ALTA;So;0;L;;;;;N;;;;;
+1D137;MUSICAL SYMBOL OTTAVA BASSA;So;0;L;;;;;N;;;;;
+1D138;MUSICAL SYMBOL QUINDICESIMA ALTA;So;0;L;;;;;N;;;;;
+1D139;MUSICAL SYMBOL QUINDICESIMA BASSA;So;0;L;;;;;N;;;;;
+1D13A;MUSICAL SYMBOL MULTI REST;So;0;L;;;;;N;;;;;
+1D13B;MUSICAL SYMBOL WHOLE REST;So;0;L;;;;;N;;;;;
+1D13C;MUSICAL SYMBOL HALF REST;So;0;L;;;;;N;;;;;
+1D13D;MUSICAL SYMBOL QUARTER REST;So;0;L;;;;;N;;;;;
+1D13E;MUSICAL SYMBOL EIGHTH REST;So;0;L;;;;;N;;;;;
+1D13F;MUSICAL SYMBOL SIXTEENTH REST;So;0;L;;;;;N;;;;;
+1D140;MUSICAL SYMBOL THIRTY-SECOND REST;So;0;L;;;;;N;;;;;
+1D141;MUSICAL SYMBOL SIXTY-FOURTH REST;So;0;L;;;;;N;;;;;
+1D142;MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH REST;So;0;L;;;;;N;;;;;
+1D143;MUSICAL SYMBOL X NOTEHEAD;So;0;L;;;;;N;;;;;
+1D144;MUSICAL SYMBOL PLUS NOTEHEAD;So;0;L;;;;;N;;;;;
+1D145;MUSICAL SYMBOL CIRCLE X NOTEHEAD;So;0;L;;;;;N;;;;;
+1D146;MUSICAL SYMBOL SQUARE NOTEHEAD WHITE;So;0;L;;;;;N;;;;;
+1D147;MUSICAL SYMBOL SQUARE NOTEHEAD BLACK;So;0;L;;;;;N;;;;;
+1D148;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP WHITE;So;0;L;;;;;N;;;;;
+1D149;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP BLACK;So;0;L;;;;;N;;;;;
+1D14A;MUSICAL SYMBOL TRIANGLE NOTEHEAD LEFT WHITE;So;0;L;;;;;N;;;;;
+1D14B;MUSICAL SYMBOL TRIANGLE NOTEHEAD LEFT BLACK;So;0;L;;;;;N;;;;;
+1D14C;MUSICAL SYMBOL TRIANGLE NOTEHEAD RIGHT WHITE;So;0;L;;;;;N;;;;;
+1D14D;MUSICAL SYMBOL TRIANGLE NOTEHEAD RIGHT BLACK;So;0;L;;;;;N;;;;;
+1D14E;MUSICAL SYMBOL TRIANGLE NOTEHEAD DOWN WHITE;So;0;L;;;;;N;;;;;
+1D14F;MUSICAL SYMBOL TRIANGLE NOTEHEAD DOWN BLACK;So;0;L;;;;;N;;;;;
+1D150;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP RIGHT WHITE;So;0;L;;;;;N;;;;;
+1D151;MUSICAL SYMBOL TRIANGLE NOTEHEAD UP RIGHT BLACK;So;0;L;;;;;N;;;;;
+1D152;MUSICAL SYMBOL MOON NOTEHEAD WHITE;So;0;L;;;;;N;;;;;
+1D153;MUSICAL SYMBOL MOON NOTEHEAD BLACK;So;0;L;;;;;N;;;;;
+1D154;MUSICAL SYMBOL TRIANGLE-ROUND NOTEHEAD DOWN WHITE;So;0;L;;;;;N;;;;;
+1D155;MUSICAL SYMBOL TRIANGLE-ROUND NOTEHEAD DOWN BLACK;So;0;L;;;;;N;;;;;
+1D156;MUSICAL SYMBOL PARENTHESIS NOTEHEAD;So;0;L;;;;;N;;;;;
+1D157;MUSICAL SYMBOL VOID NOTEHEAD;So;0;L;;;;;N;;;;;
+1D158;MUSICAL SYMBOL NOTEHEAD BLACK;So;0;L;;;;;N;;;;;
+1D159;MUSICAL SYMBOL NULL NOTEHEAD;So;0;L;;;;;N;;;;;
+1D15A;MUSICAL SYMBOL CLUSTER NOTEHEAD WHITE;So;0;L;;;;;N;;;;;
+1D15B;MUSICAL SYMBOL CLUSTER NOTEHEAD BLACK;So;0;L;;;;;N;;;;;
+1D15C;MUSICAL SYMBOL BREVE;So;0;L;;;;;N;;;;;
+1D15D;MUSICAL SYMBOL WHOLE NOTE;So;0;L;;;;;N;;;;;
+1D15E;MUSICAL SYMBOL HALF NOTE;So;0;L;1D157 1D165;;;;N;;;;;
+1D15F;MUSICAL SYMBOL QUARTER NOTE;So;0;L;1D158 1D165;;;;N;;;;;
+1D160;MUSICAL SYMBOL EIGHTH NOTE;So;0;L;1D15F 1D16E;;;;N;;;;;
+1D161;MUSICAL SYMBOL SIXTEENTH NOTE;So;0;L;1D15F 1D16F;;;;N;;;;;
+1D162;MUSICAL SYMBOL THIRTY-SECOND NOTE;So;0;L;1D15F 1D170;;;;N;;;;;
+1D163;MUSICAL SYMBOL SIXTY-FOURTH NOTE;So;0;L;1D15F 1D171;;;;N;;;;;
+1D164;MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE;So;0;L;1D15F 1D172;;;;N;;;;;
+1D165;MUSICAL SYMBOL COMBINING STEM;Mc;216;L;;;;;N;;;;;
+1D166;MUSICAL SYMBOL COMBINING SPRECHGESANG STEM;Mc;216;L;;;;;N;;;;;
+1D167;MUSICAL SYMBOL COMBINING TREMOLO-1;Mn;1;NSM;;;;;N;;;;;
+1D168;MUSICAL SYMBOL COMBINING TREMOLO-2;Mn;1;NSM;;;;;N;;;;;
+1D169;MUSICAL SYMBOL COMBINING TREMOLO-3;Mn;1;NSM;;;;;N;;;;;
+1D16A;MUSICAL SYMBOL FINGERED TREMOLO-1;So;0;L;;;;;N;;;;;
+1D16B;MUSICAL SYMBOL FINGERED TREMOLO-2;So;0;L;;;;;N;;;;;
+1D16C;MUSICAL SYMBOL FINGERED TREMOLO-3;So;0;L;;;;;N;;;;;
+1D16D;MUSICAL SYMBOL COMBINING AUGMENTATION DOT;Mc;226;L;;;;;N;;;;;
+1D16E;MUSICAL SYMBOL COMBINING FLAG-1;Mc;216;L;;;;;N;;;;;
+1D16F;MUSICAL SYMBOL COMBINING FLAG-2;Mc;216;L;;;;;N;;;;;
+1D170;MUSICAL SYMBOL COMBINING FLAG-3;Mc;216;L;;;;;N;;;;;
+1D171;MUSICAL SYMBOL COMBINING FLAG-4;Mc;216;L;;;;;N;;;;;
+1D172;MUSICAL SYMBOL COMBINING FLAG-5;Mc;216;L;;;;;N;;;;;
+1D173;MUSICAL SYMBOL BEGIN BEAM;Cf;0;BN;;;;;N;;;;;
+1D174;MUSICAL SYMBOL END BEAM;Cf;0;BN;;;;;N;;;;;
+1D175;MUSICAL SYMBOL BEGIN TIE;Cf;0;BN;;;;;N;;;;;
+1D176;MUSICAL SYMBOL END TIE;Cf;0;BN;;;;;N;;;;;
+1D177;MUSICAL SYMBOL BEGIN SLUR;Cf;0;BN;;;;;N;;;;;
+1D178;MUSICAL SYMBOL END SLUR;Cf;0;BN;;;;;N;;;;;
+1D179;MUSICAL SYMBOL BEGIN PHRASE;Cf;0;BN;;;;;N;;;;;
+1D17A;MUSICAL SYMBOL END PHRASE;Cf;0;BN;;;;;N;;;;;
+1D17B;MUSICAL SYMBOL COMBINING ACCENT;Mn;220;NSM;;;;;N;;;;;
+1D17C;MUSICAL SYMBOL COMBINING STACCATO;Mn;220;NSM;;;;;N;;;;;
+1D17D;MUSICAL SYMBOL COMBINING TENUTO;Mn;220;NSM;;;;;N;;;;;
+1D17E;MUSICAL SYMBOL COMBINING STACCATISSIMO;Mn;220;NSM;;;;;N;;;;;
+1D17F;MUSICAL SYMBOL COMBINING MARCATO;Mn;220;NSM;;;;;N;;;;;
+1D180;MUSICAL SYMBOL COMBINING MARCATO-STACCATO;Mn;220;NSM;;;;;N;;;;;
+1D181;MUSICAL SYMBOL COMBINING ACCENT-STACCATO;Mn;220;NSM;;;;;N;;;;;
+1D182;MUSICAL SYMBOL COMBINING LOURE;Mn;220;NSM;;;;;N;;;;;
+1D183;MUSICAL SYMBOL ARPEGGIATO UP;So;0;L;;;;;N;;;;;
+1D184;MUSICAL SYMBOL ARPEGGIATO DOWN;So;0;L;;;;;N;;;;;
+1D185;MUSICAL SYMBOL COMBINING DOIT;Mn;230;NSM;;;;;N;;;;;
+1D186;MUSICAL SYMBOL COMBINING RIP;Mn;230;NSM;;;;;N;;;;;
+1D187;MUSICAL SYMBOL COMBINING FLIP;Mn;230;NSM;;;;;N;;;;;
+1D188;MUSICAL SYMBOL COMBINING SMEAR;Mn;230;NSM;;;;;N;;;;;
+1D189;MUSICAL SYMBOL COMBINING BEND;Mn;230;NSM;;;;;N;;;;;
+1D18A;MUSICAL SYMBOL COMBINING DOUBLE TONGUE;Mn;220;NSM;;;;;N;;;;;
+1D18B;MUSICAL SYMBOL COMBINING TRIPLE TONGUE;Mn;220;NSM;;;;;N;;;;;
+1D18C;MUSICAL SYMBOL RINFORZANDO;So;0;L;;;;;N;;;;;
+1D18D;MUSICAL SYMBOL SUBITO;So;0;L;;;;;N;;;;;
+1D18E;MUSICAL SYMBOL Z;So;0;L;;;;;N;;;;;
+1D18F;MUSICAL SYMBOL PIANO;So;0;L;;;;;N;;;;;
+1D190;MUSICAL SYMBOL MEZZO;So;0;L;;;;;N;;;;;
+1D191;MUSICAL SYMBOL FORTE;So;0;L;;;;;N;;;;;
+1D192;MUSICAL SYMBOL CRESCENDO;So;0;L;;;;;N;;;;;
+1D193;MUSICAL SYMBOL DECRESCENDO;So;0;L;;;;;N;;;;;
+1D194;MUSICAL SYMBOL GRACE NOTE SLASH;So;0;L;;;;;N;;;;;
+1D195;MUSICAL SYMBOL GRACE NOTE NO SLASH;So;0;L;;;;;N;;;;;
+1D196;MUSICAL SYMBOL TR;So;0;L;;;;;N;;;;;
+1D197;MUSICAL SYMBOL TURN;So;0;L;;;;;N;;;;;
+1D198;MUSICAL SYMBOL INVERTED TURN;So;0;L;;;;;N;;;;;
+1D199;MUSICAL SYMBOL TURN SLASH;So;0;L;;;;;N;;;;;
+1D19A;MUSICAL SYMBOL TURN UP;So;0;L;;;;;N;;;;;
+1D19B;MUSICAL SYMBOL ORNAMENT STROKE-1;So;0;L;;;;;N;;;;;
+1D19C;MUSICAL SYMBOL ORNAMENT STROKE-2;So;0;L;;;;;N;;;;;
+1D19D;MUSICAL SYMBOL ORNAMENT STROKE-3;So;0;L;;;;;N;;;;;
+1D19E;MUSICAL SYMBOL ORNAMENT STROKE-4;So;0;L;;;;;N;;;;;
+1D19F;MUSICAL SYMBOL ORNAMENT STROKE-5;So;0;L;;;;;N;;;;;
+1D1A0;MUSICAL SYMBOL ORNAMENT STROKE-6;So;0;L;;;;;N;;;;;
+1D1A1;MUSICAL SYMBOL ORNAMENT STROKE-7;So;0;L;;;;;N;;;;;
+1D1A2;MUSICAL SYMBOL ORNAMENT STROKE-8;So;0;L;;;;;N;;;;;
+1D1A3;MUSICAL SYMBOL ORNAMENT STROKE-9;So;0;L;;;;;N;;;;;
+1D1A4;MUSICAL SYMBOL ORNAMENT STROKE-10;So;0;L;;;;;N;;;;;
+1D1A5;MUSICAL SYMBOL ORNAMENT STROKE-11;So;0;L;;;;;N;;;;;
+1D1A6;MUSICAL SYMBOL HAUPTSTIMME;So;0;L;;;;;N;;;;;
+1D1A7;MUSICAL SYMBOL NEBENSTIMME;So;0;L;;;;;N;;;;;
+1D1A8;MUSICAL SYMBOL END OF STIMME;So;0;L;;;;;N;;;;;
+1D1A9;MUSICAL SYMBOL DEGREE SLASH;So;0;L;;;;;N;;;;;
+1D1AA;MUSICAL SYMBOL COMBINING DOWN BOW;Mn;230;NSM;;;;;N;;;;;
+1D1AB;MUSICAL SYMBOL COMBINING UP BOW;Mn;230;NSM;;;;;N;;;;;
+1D1AC;MUSICAL SYMBOL COMBINING HARMONIC;Mn;230;NSM;;;;;N;;;;;
+1D1AD;MUSICAL SYMBOL COMBINING SNAP PIZZICATO;Mn;230;NSM;;;;;N;;;;;
+1D1AE;MUSICAL SYMBOL PEDAL MARK;So;0;L;;;;;N;;;;;
+1D1AF;MUSICAL SYMBOL PEDAL UP MARK;So;0;L;;;;;N;;;;;
+1D1B0;MUSICAL SYMBOL HALF PEDAL MARK;So;0;L;;;;;N;;;;;
+1D1B1;MUSICAL SYMBOL GLISSANDO UP;So;0;L;;;;;N;;;;;
+1D1B2;MUSICAL SYMBOL GLISSANDO DOWN;So;0;L;;;;;N;;;;;
+1D1B3;MUSICAL SYMBOL WITH FINGERNAILS;So;0;L;;;;;N;;;;;
+1D1B4;MUSICAL SYMBOL DAMP;So;0;L;;;;;N;;;;;
+1D1B5;MUSICAL SYMBOL DAMP ALL;So;0;L;;;;;N;;;;;
+1D1B6;MUSICAL SYMBOL MAXIMA;So;0;L;;;;;N;;;;;
+1D1B7;MUSICAL SYMBOL LONGA;So;0;L;;;;;N;;;;;
+1D1B8;MUSICAL SYMBOL BREVIS;So;0;L;;;;;N;;;;;
+1D1B9;MUSICAL SYMBOL SEMIBREVIS WHITE;So;0;L;;;;;N;;;;;
+1D1BA;MUSICAL SYMBOL SEMIBREVIS BLACK;So;0;L;;;;;N;;;;;
+1D1BB;MUSICAL SYMBOL MINIMA;So;0;L;1D1B9 1D165;;;;N;;;;;
+1D1BC;MUSICAL SYMBOL MINIMA BLACK;So;0;L;1D1BA 1D165;;;;N;;;;;
+1D1BD;MUSICAL SYMBOL SEMIMINIMA WHITE;So;0;L;1D1BB 1D16E;;;;N;;;;;
+1D1BE;MUSICAL SYMBOL SEMIMINIMA BLACK;So;0;L;1D1BC 1D16E;;;;N;;;;;
+1D1BF;MUSICAL SYMBOL FUSA WHITE;So;0;L;1D1BB 1D16F;;;;N;;;;;
+1D1C0;MUSICAL SYMBOL FUSA BLACK;So;0;L;1D1BC 1D16F;;;;N;;;;;
+1D1C1;MUSICAL SYMBOL LONGA PERFECTA REST;So;0;L;;;;;N;;;;;
+1D1C2;MUSICAL SYMBOL LONGA IMPERFECTA REST;So;0;L;;;;;N;;;;;
+1D1C3;MUSICAL SYMBOL BREVIS REST;So;0;L;;;;;N;;;;;
+1D1C4;MUSICAL SYMBOL SEMIBREVIS REST;So;0;L;;;;;N;;;;;
+1D1C5;MUSICAL SYMBOL MINIMA REST;So;0;L;;;;;N;;;;;
+1D1C6;MUSICAL SYMBOL SEMIMINIMA REST;So;0;L;;;;;N;;;;;
+1D1C7;MUSICAL SYMBOL TEMPUS PERFECTUM CUM PROLATIONE PERFECTA;So;0;L;;;;;N;;;;;
+1D1C8;MUSICAL SYMBOL TEMPUS PERFECTUM CUM PROLATIONE IMPERFECTA;So;0;L;;;;;N;;;;;
+1D1C9;MUSICAL SYMBOL TEMPUS PERFECTUM CUM PROLATIONE PERFECTA DIMINUTION-1;So;0;L;;;;;N;;;;;
+1D1CA;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE PERFECTA;So;0;L;;;;;N;;;;;
+1D1CB;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA;So;0;L;;;;;N;;;;;
+1D1CC;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA DIMINUTION-1;So;0;L;;;;;N;;;;;
+1D1CD;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA DIMINUTION-2;So;0;L;;;;;N;;;;;
+1D1CE;MUSICAL SYMBOL TEMPUS IMPERFECTUM CUM PROLATIONE IMPERFECTA DIMINUTION-3;So;0;L;;;;;N;;;;;
+1D1CF;MUSICAL SYMBOL CROIX;So;0;L;;;;;N;;;;;
+1D1D0;MUSICAL SYMBOL GREGORIAN C CLEF;So;0;L;;;;;N;;;;;
+1D1D1;MUSICAL SYMBOL GREGORIAN F CLEF;So;0;L;;;;;N;;;;;
+1D1D2;MUSICAL SYMBOL SQUARE B;So;0;L;;;;;N;;;;;
+1D1D3;MUSICAL SYMBOL VIRGA;So;0;L;;;;;N;;;;;
+1D1D4;MUSICAL SYMBOL PODATUS;So;0;L;;;;;N;;;;;
+1D1D5;MUSICAL SYMBOL CLIVIS;So;0;L;;;;;N;;;;;
+1D1D6;MUSICAL SYMBOL SCANDICUS;So;0;L;;;;;N;;;;;
+1D1D7;MUSICAL SYMBOL CLIMACUS;So;0;L;;;;;N;;;;;
+1D1D8;MUSICAL SYMBOL TORCULUS;So;0;L;;;;;N;;;;;
+1D1D9;MUSICAL SYMBOL PORRECTUS;So;0;L;;;;;N;;;;;
+1D1DA;MUSICAL SYMBOL PORRECTUS FLEXUS;So;0;L;;;;;N;;;;;
+1D1DB;MUSICAL SYMBOL SCANDICUS FLEXUS;So;0;L;;;;;N;;;;;
+1D1DC;MUSICAL SYMBOL TORCULUS RESUPINUS;So;0;L;;;;;N;;;;;
+1D1DD;MUSICAL SYMBOL PES SUBPUNCTIS;So;0;L;;;;;N;;;;;
+1D400;MATHEMATICAL BOLD CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D401;MATHEMATICAL BOLD CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D402;MATHEMATICAL BOLD CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D403;MATHEMATICAL BOLD CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D404;MATHEMATICAL BOLD CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D405;MATHEMATICAL BOLD CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D406;MATHEMATICAL BOLD CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D407;MATHEMATICAL BOLD CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D408;MATHEMATICAL BOLD CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D409;MATHEMATICAL BOLD CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D40A;MATHEMATICAL BOLD CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D40B;MATHEMATICAL BOLD CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D40C;MATHEMATICAL BOLD CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D40D;MATHEMATICAL BOLD CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D40E;MATHEMATICAL BOLD CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D40F;MATHEMATICAL BOLD CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D410;MATHEMATICAL BOLD CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D411;MATHEMATICAL BOLD CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D412;MATHEMATICAL BOLD CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D413;MATHEMATICAL BOLD CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D414;MATHEMATICAL BOLD CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D415;MATHEMATICAL BOLD CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D416;MATHEMATICAL BOLD CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D417;MATHEMATICAL BOLD CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D418;MATHEMATICAL BOLD CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D419;MATHEMATICAL BOLD CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D41A;MATHEMATICAL BOLD SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D41B;MATHEMATICAL BOLD SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D41C;MATHEMATICAL BOLD SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D41D;MATHEMATICAL BOLD SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D41E;MATHEMATICAL BOLD SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D41F;MATHEMATICAL BOLD SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D420;MATHEMATICAL BOLD SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D421;MATHEMATICAL BOLD SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D422;MATHEMATICAL BOLD SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D423;MATHEMATICAL BOLD SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D424;MATHEMATICAL BOLD SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D425;MATHEMATICAL BOLD SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D426;MATHEMATICAL BOLD SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D427;MATHEMATICAL BOLD SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D428;MATHEMATICAL BOLD SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D429;MATHEMATICAL BOLD SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D42A;MATHEMATICAL BOLD SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D42B;MATHEMATICAL BOLD SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D42C;MATHEMATICAL BOLD SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D42D;MATHEMATICAL BOLD SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D42E;MATHEMATICAL BOLD SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D42F;MATHEMATICAL BOLD SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D430;MATHEMATICAL BOLD SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D431;MATHEMATICAL BOLD SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D432;MATHEMATICAL BOLD SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D433;MATHEMATICAL BOLD SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D434;MATHEMATICAL ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D435;MATHEMATICAL ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D436;MATHEMATICAL ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D437;MATHEMATICAL ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D438;MATHEMATICAL ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D439;MATHEMATICAL ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D43A;MATHEMATICAL ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D43B;MATHEMATICAL ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D43C;MATHEMATICAL ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D43D;MATHEMATICAL ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D43E;MATHEMATICAL ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D43F;MATHEMATICAL ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D440;MATHEMATICAL ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D441;MATHEMATICAL ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D442;MATHEMATICAL ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D443;MATHEMATICAL ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D444;MATHEMATICAL ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D445;MATHEMATICAL ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D446;MATHEMATICAL ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D447;MATHEMATICAL ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D448;MATHEMATICAL ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D449;MATHEMATICAL ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D44A;MATHEMATICAL ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D44B;MATHEMATICAL ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D44C;MATHEMATICAL ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D44D;MATHEMATICAL ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D44E;MATHEMATICAL ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D44F;MATHEMATICAL ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D450;MATHEMATICAL ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D451;MATHEMATICAL ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D452;MATHEMATICAL ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D453;MATHEMATICAL ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D454;MATHEMATICAL ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D456;MATHEMATICAL ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D457;MATHEMATICAL ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D458;MATHEMATICAL ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D459;MATHEMATICAL ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D45A;MATHEMATICAL ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D45B;MATHEMATICAL ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D45C;MATHEMATICAL ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D45D;MATHEMATICAL ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D45E;MATHEMATICAL ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D45F;MATHEMATICAL ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D460;MATHEMATICAL ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D461;MATHEMATICAL ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D462;MATHEMATICAL ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D463;MATHEMATICAL ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D464;MATHEMATICAL ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D465;MATHEMATICAL ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D466;MATHEMATICAL ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D467;MATHEMATICAL ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D468;MATHEMATICAL BOLD ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D469;MATHEMATICAL BOLD ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D46A;MATHEMATICAL BOLD ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D46B;MATHEMATICAL BOLD ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D46C;MATHEMATICAL BOLD ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D46D;MATHEMATICAL BOLD ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D46E;MATHEMATICAL BOLD ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D46F;MATHEMATICAL BOLD ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D470;MATHEMATICAL BOLD ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D471;MATHEMATICAL BOLD ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D472;MATHEMATICAL BOLD ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D473;MATHEMATICAL BOLD ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D474;MATHEMATICAL BOLD ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D475;MATHEMATICAL BOLD ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D476;MATHEMATICAL BOLD ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D477;MATHEMATICAL BOLD ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D478;MATHEMATICAL BOLD ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D479;MATHEMATICAL BOLD ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D47A;MATHEMATICAL BOLD ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D47B;MATHEMATICAL BOLD ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D47C;MATHEMATICAL BOLD ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D47D;MATHEMATICAL BOLD ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D47E;MATHEMATICAL BOLD ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D47F;MATHEMATICAL BOLD ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D480;MATHEMATICAL BOLD ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D481;MATHEMATICAL BOLD ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D482;MATHEMATICAL BOLD ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D483;MATHEMATICAL BOLD ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D484;MATHEMATICAL BOLD ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D485;MATHEMATICAL BOLD ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D486;MATHEMATICAL BOLD ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D487;MATHEMATICAL BOLD ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D488;MATHEMATICAL BOLD ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D489;MATHEMATICAL BOLD ITALIC SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D48A;MATHEMATICAL BOLD ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D48B;MATHEMATICAL BOLD ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D48C;MATHEMATICAL BOLD ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D48D;MATHEMATICAL BOLD ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D48E;MATHEMATICAL BOLD ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D48F;MATHEMATICAL BOLD ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D490;MATHEMATICAL BOLD ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D491;MATHEMATICAL BOLD ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D492;MATHEMATICAL BOLD ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D493;MATHEMATICAL BOLD ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D494;MATHEMATICAL BOLD ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D495;MATHEMATICAL BOLD ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D496;MATHEMATICAL BOLD ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D497;MATHEMATICAL BOLD ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D498;MATHEMATICAL BOLD ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D499;MATHEMATICAL BOLD ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D49A;MATHEMATICAL BOLD ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D49B;MATHEMATICAL BOLD ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D49C;MATHEMATICAL SCRIPT CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D49E;MATHEMATICAL SCRIPT CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D49F;MATHEMATICAL SCRIPT CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D4A2;MATHEMATICAL SCRIPT CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D4A5;MATHEMATICAL SCRIPT CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D4A6;MATHEMATICAL SCRIPT CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D4A9;MATHEMATICAL SCRIPT CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D4AA;MATHEMATICAL SCRIPT CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D4AB;MATHEMATICAL SCRIPT CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D4AC;MATHEMATICAL SCRIPT CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D4AE;MATHEMATICAL SCRIPT CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D4AF;MATHEMATICAL SCRIPT CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D4B0;MATHEMATICAL SCRIPT CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D4B1;MATHEMATICAL SCRIPT CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D4B2;MATHEMATICAL SCRIPT CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D4B3;MATHEMATICAL SCRIPT CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D4B4;MATHEMATICAL SCRIPT CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D4B5;MATHEMATICAL SCRIPT CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D4B6;MATHEMATICAL SCRIPT SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D4B7;MATHEMATICAL SCRIPT SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D4B8;MATHEMATICAL SCRIPT SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D4B9;MATHEMATICAL SCRIPT SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D4BB;MATHEMATICAL SCRIPT SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D4BD;MATHEMATICAL SCRIPT SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D4BE;MATHEMATICAL SCRIPT SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D4BF;MATHEMATICAL SCRIPT SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D4C0;MATHEMATICAL SCRIPT SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D4C2;MATHEMATICAL SCRIPT SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D4C3;MATHEMATICAL SCRIPT SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D4C5;MATHEMATICAL SCRIPT SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D4C6;MATHEMATICAL SCRIPT SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D4C7;MATHEMATICAL SCRIPT SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D4C8;MATHEMATICAL SCRIPT SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D4C9;MATHEMATICAL SCRIPT SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D4CA;MATHEMATICAL SCRIPT SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D4CB;MATHEMATICAL SCRIPT SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D4CC;MATHEMATICAL SCRIPT SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D4CD;MATHEMATICAL SCRIPT SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D4CE;MATHEMATICAL SCRIPT SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D4CF;MATHEMATICAL SCRIPT SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D4D0;MATHEMATICAL BOLD SCRIPT CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D4D1;MATHEMATICAL BOLD SCRIPT CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D4D2;MATHEMATICAL BOLD SCRIPT CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D4D3;MATHEMATICAL BOLD SCRIPT CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D4D4;MATHEMATICAL BOLD SCRIPT CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D4D5;MATHEMATICAL BOLD SCRIPT CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D4D6;MATHEMATICAL BOLD SCRIPT CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D4D7;MATHEMATICAL BOLD SCRIPT CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D4D8;MATHEMATICAL BOLD SCRIPT CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D4D9;MATHEMATICAL BOLD SCRIPT CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D4DA;MATHEMATICAL BOLD SCRIPT CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D4DB;MATHEMATICAL BOLD SCRIPT CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D4DC;MATHEMATICAL BOLD SCRIPT CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D4DD;MATHEMATICAL BOLD SCRIPT CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D4DE;MATHEMATICAL BOLD SCRIPT CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D4DF;MATHEMATICAL BOLD SCRIPT CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D4E0;MATHEMATICAL BOLD SCRIPT CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D4E1;MATHEMATICAL BOLD SCRIPT CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D4E2;MATHEMATICAL BOLD SCRIPT CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D4E3;MATHEMATICAL BOLD SCRIPT CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D4E4;MATHEMATICAL BOLD SCRIPT CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D4E5;MATHEMATICAL BOLD SCRIPT CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D4E6;MATHEMATICAL BOLD SCRIPT CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D4E7;MATHEMATICAL BOLD SCRIPT CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D4E8;MATHEMATICAL BOLD SCRIPT CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D4E9;MATHEMATICAL BOLD SCRIPT CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D4EA;MATHEMATICAL BOLD SCRIPT SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D4EB;MATHEMATICAL BOLD SCRIPT SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D4EC;MATHEMATICAL BOLD SCRIPT SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D4ED;MATHEMATICAL BOLD SCRIPT SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D4EE;MATHEMATICAL BOLD SCRIPT SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D4EF;MATHEMATICAL BOLD SCRIPT SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D4F0;MATHEMATICAL BOLD SCRIPT SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D4F1;MATHEMATICAL BOLD SCRIPT SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D4F2;MATHEMATICAL BOLD SCRIPT SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D4F3;MATHEMATICAL BOLD SCRIPT SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D4F4;MATHEMATICAL BOLD SCRIPT SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D4F5;MATHEMATICAL BOLD SCRIPT SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D4F6;MATHEMATICAL BOLD SCRIPT SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D4F7;MATHEMATICAL BOLD SCRIPT SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D4F8;MATHEMATICAL BOLD SCRIPT SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D4F9;MATHEMATICAL BOLD SCRIPT SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D4FA;MATHEMATICAL BOLD SCRIPT SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D4FB;MATHEMATICAL BOLD SCRIPT SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D4FC;MATHEMATICAL BOLD SCRIPT SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D4FD;MATHEMATICAL BOLD SCRIPT SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D4FE;MATHEMATICAL BOLD SCRIPT SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D4FF;MATHEMATICAL BOLD SCRIPT SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D500;MATHEMATICAL BOLD SCRIPT SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D501;MATHEMATICAL BOLD SCRIPT SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D502;MATHEMATICAL BOLD SCRIPT SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D503;MATHEMATICAL BOLD SCRIPT SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D504;MATHEMATICAL FRAKTUR CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D505;MATHEMATICAL FRAKTUR CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D507;MATHEMATICAL FRAKTUR CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D508;MATHEMATICAL FRAKTUR CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D509;MATHEMATICAL FRAKTUR CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D50A;MATHEMATICAL FRAKTUR CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D50D;MATHEMATICAL FRAKTUR CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D50E;MATHEMATICAL FRAKTUR CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D50F;MATHEMATICAL FRAKTUR CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D510;MATHEMATICAL FRAKTUR CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D511;MATHEMATICAL FRAKTUR CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D512;MATHEMATICAL FRAKTUR CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D513;MATHEMATICAL FRAKTUR CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D514;MATHEMATICAL FRAKTUR CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D516;MATHEMATICAL FRAKTUR CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D517;MATHEMATICAL FRAKTUR CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D518;MATHEMATICAL FRAKTUR CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D519;MATHEMATICAL FRAKTUR CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D51A;MATHEMATICAL FRAKTUR CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D51B;MATHEMATICAL FRAKTUR CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D51C;MATHEMATICAL FRAKTUR CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D51E;MATHEMATICAL FRAKTUR SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D51F;MATHEMATICAL FRAKTUR SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D520;MATHEMATICAL FRAKTUR SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D521;MATHEMATICAL FRAKTUR SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D522;MATHEMATICAL FRAKTUR SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D523;MATHEMATICAL FRAKTUR SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D524;MATHEMATICAL FRAKTUR SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D525;MATHEMATICAL FRAKTUR SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D526;MATHEMATICAL FRAKTUR SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D527;MATHEMATICAL FRAKTUR SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D528;MATHEMATICAL FRAKTUR SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D529;MATHEMATICAL FRAKTUR SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D52A;MATHEMATICAL FRAKTUR SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D52B;MATHEMATICAL FRAKTUR SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D52C;MATHEMATICAL FRAKTUR SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D52D;MATHEMATICAL FRAKTUR SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D52E;MATHEMATICAL FRAKTUR SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D52F;MATHEMATICAL FRAKTUR SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D530;MATHEMATICAL FRAKTUR SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D531;MATHEMATICAL FRAKTUR SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D532;MATHEMATICAL FRAKTUR SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D533;MATHEMATICAL FRAKTUR SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D534;MATHEMATICAL FRAKTUR SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D535;MATHEMATICAL FRAKTUR SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D536;MATHEMATICAL FRAKTUR SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D537;MATHEMATICAL FRAKTUR SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D538;MATHEMATICAL DOUBLE-STRUCK CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D539;MATHEMATICAL DOUBLE-STRUCK CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D53B;MATHEMATICAL DOUBLE-STRUCK CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D53C;MATHEMATICAL DOUBLE-STRUCK CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D53D;MATHEMATICAL DOUBLE-STRUCK CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D53E;MATHEMATICAL DOUBLE-STRUCK CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D540;MATHEMATICAL DOUBLE-STRUCK CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D541;MATHEMATICAL DOUBLE-STRUCK CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D542;MATHEMATICAL DOUBLE-STRUCK CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D543;MATHEMATICAL DOUBLE-STRUCK CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D544;MATHEMATICAL DOUBLE-STRUCK CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D546;MATHEMATICAL DOUBLE-STRUCK CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D54A;MATHEMATICAL DOUBLE-STRUCK CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D54B;MATHEMATICAL DOUBLE-STRUCK CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D54C;MATHEMATICAL DOUBLE-STRUCK CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D54D;MATHEMATICAL DOUBLE-STRUCK CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D54E;MATHEMATICAL DOUBLE-STRUCK CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D54F;MATHEMATICAL DOUBLE-STRUCK CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D550;MATHEMATICAL DOUBLE-STRUCK CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D552;MATHEMATICAL DOUBLE-STRUCK SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D553;MATHEMATICAL DOUBLE-STRUCK SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D554;MATHEMATICAL DOUBLE-STRUCK SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D555;MATHEMATICAL DOUBLE-STRUCK SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D556;MATHEMATICAL DOUBLE-STRUCK SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D557;MATHEMATICAL DOUBLE-STRUCK SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D558;MATHEMATICAL DOUBLE-STRUCK SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D559;MATHEMATICAL DOUBLE-STRUCK SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D55A;MATHEMATICAL DOUBLE-STRUCK SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D55B;MATHEMATICAL DOUBLE-STRUCK SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D55C;MATHEMATICAL DOUBLE-STRUCK SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D55D;MATHEMATICAL DOUBLE-STRUCK SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D55E;MATHEMATICAL DOUBLE-STRUCK SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D55F;MATHEMATICAL DOUBLE-STRUCK SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D560;MATHEMATICAL DOUBLE-STRUCK SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D561;MATHEMATICAL DOUBLE-STRUCK SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D562;MATHEMATICAL DOUBLE-STRUCK SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D563;MATHEMATICAL DOUBLE-STRUCK SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D564;MATHEMATICAL DOUBLE-STRUCK SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D565;MATHEMATICAL DOUBLE-STRUCK SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D566;MATHEMATICAL DOUBLE-STRUCK SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D567;MATHEMATICAL DOUBLE-STRUCK SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D568;MATHEMATICAL DOUBLE-STRUCK SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D569;MATHEMATICAL DOUBLE-STRUCK SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D56A;MATHEMATICAL DOUBLE-STRUCK SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D56B;MATHEMATICAL DOUBLE-STRUCK SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D56C;MATHEMATICAL BOLD FRAKTUR CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D56D;MATHEMATICAL BOLD FRAKTUR CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D56E;MATHEMATICAL BOLD FRAKTUR CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D56F;MATHEMATICAL BOLD FRAKTUR CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D570;MATHEMATICAL BOLD FRAKTUR CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D571;MATHEMATICAL BOLD FRAKTUR CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D572;MATHEMATICAL BOLD FRAKTUR CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D573;MATHEMATICAL BOLD FRAKTUR CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D574;MATHEMATICAL BOLD FRAKTUR CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D575;MATHEMATICAL BOLD FRAKTUR CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D576;MATHEMATICAL BOLD FRAKTUR CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D577;MATHEMATICAL BOLD FRAKTUR CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D578;MATHEMATICAL BOLD FRAKTUR CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D579;MATHEMATICAL BOLD FRAKTUR CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D57A;MATHEMATICAL BOLD FRAKTUR CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D57B;MATHEMATICAL BOLD FRAKTUR CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D57C;MATHEMATICAL BOLD FRAKTUR CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D57D;MATHEMATICAL BOLD FRAKTUR CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D57E;MATHEMATICAL BOLD FRAKTUR CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D57F;MATHEMATICAL BOLD FRAKTUR CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D580;MATHEMATICAL BOLD FRAKTUR CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D581;MATHEMATICAL BOLD FRAKTUR CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D582;MATHEMATICAL BOLD FRAKTUR CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D583;MATHEMATICAL BOLD FRAKTUR CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D584;MATHEMATICAL BOLD FRAKTUR CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D585;MATHEMATICAL BOLD FRAKTUR CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D586;MATHEMATICAL BOLD FRAKTUR SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D587;MATHEMATICAL BOLD FRAKTUR SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D588;MATHEMATICAL BOLD FRAKTUR SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D589;MATHEMATICAL BOLD FRAKTUR SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D58A;MATHEMATICAL BOLD FRAKTUR SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D58B;MATHEMATICAL BOLD FRAKTUR SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D58C;MATHEMATICAL BOLD FRAKTUR SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D58D;MATHEMATICAL BOLD FRAKTUR SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D58E;MATHEMATICAL BOLD FRAKTUR SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D58F;MATHEMATICAL BOLD FRAKTUR SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D590;MATHEMATICAL BOLD FRAKTUR SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D591;MATHEMATICAL BOLD FRAKTUR SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D592;MATHEMATICAL BOLD FRAKTUR SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D593;MATHEMATICAL BOLD FRAKTUR SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D594;MATHEMATICAL BOLD FRAKTUR SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D595;MATHEMATICAL BOLD FRAKTUR SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D596;MATHEMATICAL BOLD FRAKTUR SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D597;MATHEMATICAL BOLD FRAKTUR SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D598;MATHEMATICAL BOLD FRAKTUR SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D599;MATHEMATICAL BOLD FRAKTUR SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D59A;MATHEMATICAL BOLD FRAKTUR SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D59B;MATHEMATICAL BOLD FRAKTUR SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D59C;MATHEMATICAL BOLD FRAKTUR SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D59D;MATHEMATICAL BOLD FRAKTUR SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D59E;MATHEMATICAL BOLD FRAKTUR SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D59F;MATHEMATICAL BOLD FRAKTUR SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D5A0;MATHEMATICAL SANS-SERIF CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D5A1;MATHEMATICAL SANS-SERIF CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D5A2;MATHEMATICAL SANS-SERIF CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D5A3;MATHEMATICAL SANS-SERIF CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D5A4;MATHEMATICAL SANS-SERIF CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D5A5;MATHEMATICAL SANS-SERIF CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D5A6;MATHEMATICAL SANS-SERIF CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D5A7;MATHEMATICAL SANS-SERIF CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D5A8;MATHEMATICAL SANS-SERIF CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D5A9;MATHEMATICAL SANS-SERIF CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D5AA;MATHEMATICAL SANS-SERIF CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D5AB;MATHEMATICAL SANS-SERIF CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D5AC;MATHEMATICAL SANS-SERIF CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D5AD;MATHEMATICAL SANS-SERIF CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D5AE;MATHEMATICAL SANS-SERIF CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D5AF;MATHEMATICAL SANS-SERIF CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D5B0;MATHEMATICAL SANS-SERIF CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D5B1;MATHEMATICAL SANS-SERIF CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D5B2;MATHEMATICAL SANS-SERIF CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D5B3;MATHEMATICAL SANS-SERIF CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D5B4;MATHEMATICAL SANS-SERIF CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D5B5;MATHEMATICAL SANS-SERIF CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D5B6;MATHEMATICAL SANS-SERIF CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D5B7;MATHEMATICAL SANS-SERIF CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D5B8;MATHEMATICAL SANS-SERIF CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D5B9;MATHEMATICAL SANS-SERIF CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D5BA;MATHEMATICAL SANS-SERIF SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D5BB;MATHEMATICAL SANS-SERIF SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D5BC;MATHEMATICAL SANS-SERIF SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D5BD;MATHEMATICAL SANS-SERIF SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D5BE;MATHEMATICAL SANS-SERIF SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D5BF;MATHEMATICAL SANS-SERIF SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D5C0;MATHEMATICAL SANS-SERIF SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D5C1;MATHEMATICAL SANS-SERIF SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D5C2;MATHEMATICAL SANS-SERIF SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D5C3;MATHEMATICAL SANS-SERIF SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D5C4;MATHEMATICAL SANS-SERIF SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D5C5;MATHEMATICAL SANS-SERIF SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D5C6;MATHEMATICAL SANS-SERIF SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D5C7;MATHEMATICAL SANS-SERIF SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D5C8;MATHEMATICAL SANS-SERIF SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D5C9;MATHEMATICAL SANS-SERIF SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D5CA;MATHEMATICAL SANS-SERIF SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D5CB;MATHEMATICAL SANS-SERIF SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D5CC;MATHEMATICAL SANS-SERIF SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D5CD;MATHEMATICAL SANS-SERIF SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D5CE;MATHEMATICAL SANS-SERIF SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D5CF;MATHEMATICAL SANS-SERIF SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D5D0;MATHEMATICAL SANS-SERIF SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D5D1;MATHEMATICAL SANS-SERIF SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D5D2;MATHEMATICAL SANS-SERIF SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D5D3;MATHEMATICAL SANS-SERIF SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D5D4;MATHEMATICAL SANS-SERIF BOLD CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D5D5;MATHEMATICAL SANS-SERIF BOLD CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D5D6;MATHEMATICAL SANS-SERIF BOLD CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D5D7;MATHEMATICAL SANS-SERIF BOLD CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D5D8;MATHEMATICAL SANS-SERIF BOLD CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D5D9;MATHEMATICAL SANS-SERIF BOLD CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D5DA;MATHEMATICAL SANS-SERIF BOLD CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D5DB;MATHEMATICAL SANS-SERIF BOLD CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D5DC;MATHEMATICAL SANS-SERIF BOLD CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D5DD;MATHEMATICAL SANS-SERIF BOLD CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D5DE;MATHEMATICAL SANS-SERIF BOLD CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D5DF;MATHEMATICAL SANS-SERIF BOLD CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D5E0;MATHEMATICAL SANS-SERIF BOLD CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D5E1;MATHEMATICAL SANS-SERIF BOLD CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D5E2;MATHEMATICAL SANS-SERIF BOLD CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D5E3;MATHEMATICAL SANS-SERIF BOLD CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D5E4;MATHEMATICAL SANS-SERIF BOLD CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D5E5;MATHEMATICAL SANS-SERIF BOLD CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D5E6;MATHEMATICAL SANS-SERIF BOLD CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D5E7;MATHEMATICAL SANS-SERIF BOLD CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D5E8;MATHEMATICAL SANS-SERIF BOLD CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D5E9;MATHEMATICAL SANS-SERIF BOLD CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D5EA;MATHEMATICAL SANS-SERIF BOLD CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D5EB;MATHEMATICAL SANS-SERIF BOLD CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D5EC;MATHEMATICAL SANS-SERIF BOLD CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D5ED;MATHEMATICAL SANS-SERIF BOLD CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D5EE;MATHEMATICAL SANS-SERIF BOLD SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D5EF;MATHEMATICAL SANS-SERIF BOLD SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D5F0;MATHEMATICAL SANS-SERIF BOLD SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D5F1;MATHEMATICAL SANS-SERIF BOLD SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D5F2;MATHEMATICAL SANS-SERIF BOLD SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D5F3;MATHEMATICAL SANS-SERIF BOLD SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D5F4;MATHEMATICAL SANS-SERIF BOLD SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D5F5;MATHEMATICAL SANS-SERIF BOLD SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D5F6;MATHEMATICAL SANS-SERIF BOLD SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D5F7;MATHEMATICAL SANS-SERIF BOLD SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D5F8;MATHEMATICAL SANS-SERIF BOLD SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D5F9;MATHEMATICAL SANS-SERIF BOLD SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D5FA;MATHEMATICAL SANS-SERIF BOLD SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D5FB;MATHEMATICAL SANS-SERIF BOLD SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D5FC;MATHEMATICAL SANS-SERIF BOLD SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D5FD;MATHEMATICAL SANS-SERIF BOLD SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D5FE;MATHEMATICAL SANS-SERIF BOLD SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D5FF;MATHEMATICAL SANS-SERIF BOLD SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D600;MATHEMATICAL SANS-SERIF BOLD SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D601;MATHEMATICAL SANS-SERIF BOLD SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D602;MATHEMATICAL SANS-SERIF BOLD SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D603;MATHEMATICAL SANS-SERIF BOLD SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D604;MATHEMATICAL SANS-SERIF BOLD SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D605;MATHEMATICAL SANS-SERIF BOLD SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D606;MATHEMATICAL SANS-SERIF BOLD SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D607;MATHEMATICAL SANS-SERIF BOLD SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D608;MATHEMATICAL SANS-SERIF ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D609;MATHEMATICAL SANS-SERIF ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D60A;MATHEMATICAL SANS-SERIF ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D60B;MATHEMATICAL SANS-SERIF ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D60C;MATHEMATICAL SANS-SERIF ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D60D;MATHEMATICAL SANS-SERIF ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D60E;MATHEMATICAL SANS-SERIF ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D60F;MATHEMATICAL SANS-SERIF ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D610;MATHEMATICAL SANS-SERIF ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D611;MATHEMATICAL SANS-SERIF ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D612;MATHEMATICAL SANS-SERIF ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D613;MATHEMATICAL SANS-SERIF ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D614;MATHEMATICAL SANS-SERIF ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D615;MATHEMATICAL SANS-SERIF ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D616;MATHEMATICAL SANS-SERIF ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D617;MATHEMATICAL SANS-SERIF ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D618;MATHEMATICAL SANS-SERIF ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D619;MATHEMATICAL SANS-SERIF ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D61A;MATHEMATICAL SANS-SERIF ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D61B;MATHEMATICAL SANS-SERIF ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D61C;MATHEMATICAL SANS-SERIF ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D61D;MATHEMATICAL SANS-SERIF ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D61E;MATHEMATICAL SANS-SERIF ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D61F;MATHEMATICAL SANS-SERIF ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D620;MATHEMATICAL SANS-SERIF ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D621;MATHEMATICAL SANS-SERIF ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D622;MATHEMATICAL SANS-SERIF ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D623;MATHEMATICAL SANS-SERIF ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D624;MATHEMATICAL SANS-SERIF ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D625;MATHEMATICAL SANS-SERIF ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D626;MATHEMATICAL SANS-SERIF ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D627;MATHEMATICAL SANS-SERIF ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D628;MATHEMATICAL SANS-SERIF ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D629;MATHEMATICAL SANS-SERIF ITALIC SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D62A;MATHEMATICAL SANS-SERIF ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D62B;MATHEMATICAL SANS-SERIF ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D62C;MATHEMATICAL SANS-SERIF ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D62D;MATHEMATICAL SANS-SERIF ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D62E;MATHEMATICAL SANS-SERIF ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D62F;MATHEMATICAL SANS-SERIF ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D630;MATHEMATICAL SANS-SERIF ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D631;MATHEMATICAL SANS-SERIF ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D632;MATHEMATICAL SANS-SERIF ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D633;MATHEMATICAL SANS-SERIF ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D634;MATHEMATICAL SANS-SERIF ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D635;MATHEMATICAL SANS-SERIF ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D636;MATHEMATICAL SANS-SERIF ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D637;MATHEMATICAL SANS-SERIF ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D638;MATHEMATICAL SANS-SERIF ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D639;MATHEMATICAL SANS-SERIF ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D63A;MATHEMATICAL SANS-SERIF ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D63B;MATHEMATICAL SANS-SERIF ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D63C;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D63D;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D63E;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D63F;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D640;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D641;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D642;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D643;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D644;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D645;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D646;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D647;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D648;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D649;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D64A;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D64B;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D64C;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D64D;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D64E;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D64F;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D650;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D651;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D652;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D653;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D654;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D655;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D656;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D657;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D658;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D659;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D65A;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D65B;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D65C;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D65D;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D65E;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D65F;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D660;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D661;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D662;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D663;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D664;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D665;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D666;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D667;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D668;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D669;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D66A;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D66B;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D66C;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D66D;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D66E;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D66F;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D670;MATHEMATICAL MONOSPACE CAPITAL A;Lu;0;L;<font> 0041;;;;N;;;;;
+1D671;MATHEMATICAL MONOSPACE CAPITAL B;Lu;0;L;<font> 0042;;;;N;;;;;
+1D672;MATHEMATICAL MONOSPACE CAPITAL C;Lu;0;L;<font> 0043;;;;N;;;;;
+1D673;MATHEMATICAL MONOSPACE CAPITAL D;Lu;0;L;<font> 0044;;;;N;;;;;
+1D674;MATHEMATICAL MONOSPACE CAPITAL E;Lu;0;L;<font> 0045;;;;N;;;;;
+1D675;MATHEMATICAL MONOSPACE CAPITAL F;Lu;0;L;<font> 0046;;;;N;;;;;
+1D676;MATHEMATICAL MONOSPACE CAPITAL G;Lu;0;L;<font> 0047;;;;N;;;;;
+1D677;MATHEMATICAL MONOSPACE CAPITAL H;Lu;0;L;<font> 0048;;;;N;;;;;
+1D678;MATHEMATICAL MONOSPACE CAPITAL I;Lu;0;L;<font> 0049;;;;N;;;;;
+1D679;MATHEMATICAL MONOSPACE CAPITAL J;Lu;0;L;<font> 004A;;;;N;;;;;
+1D67A;MATHEMATICAL MONOSPACE CAPITAL K;Lu;0;L;<font> 004B;;;;N;;;;;
+1D67B;MATHEMATICAL MONOSPACE CAPITAL L;Lu;0;L;<font> 004C;;;;N;;;;;
+1D67C;MATHEMATICAL MONOSPACE CAPITAL M;Lu;0;L;<font> 004D;;;;N;;;;;
+1D67D;MATHEMATICAL MONOSPACE CAPITAL N;Lu;0;L;<font> 004E;;;;N;;;;;
+1D67E;MATHEMATICAL MONOSPACE CAPITAL O;Lu;0;L;<font> 004F;;;;N;;;;;
+1D67F;MATHEMATICAL MONOSPACE CAPITAL P;Lu;0;L;<font> 0050;;;;N;;;;;
+1D680;MATHEMATICAL MONOSPACE CAPITAL Q;Lu;0;L;<font> 0051;;;;N;;;;;
+1D681;MATHEMATICAL MONOSPACE CAPITAL R;Lu;0;L;<font> 0052;;;;N;;;;;
+1D682;MATHEMATICAL MONOSPACE CAPITAL S;Lu;0;L;<font> 0053;;;;N;;;;;
+1D683;MATHEMATICAL MONOSPACE CAPITAL T;Lu;0;L;<font> 0054;;;;N;;;;;
+1D684;MATHEMATICAL MONOSPACE CAPITAL U;Lu;0;L;<font> 0055;;;;N;;;;;
+1D685;MATHEMATICAL MONOSPACE CAPITAL V;Lu;0;L;<font> 0056;;;;N;;;;;
+1D686;MATHEMATICAL MONOSPACE CAPITAL W;Lu;0;L;<font> 0057;;;;N;;;;;
+1D687;MATHEMATICAL MONOSPACE CAPITAL X;Lu;0;L;<font> 0058;;;;N;;;;;
+1D688;MATHEMATICAL MONOSPACE CAPITAL Y;Lu;0;L;<font> 0059;;;;N;;;;;
+1D689;MATHEMATICAL MONOSPACE CAPITAL Z;Lu;0;L;<font> 005A;;;;N;;;;;
+1D68A;MATHEMATICAL MONOSPACE SMALL A;Ll;0;L;<font> 0061;;;;N;;;;;
+1D68B;MATHEMATICAL MONOSPACE SMALL B;Ll;0;L;<font> 0062;;;;N;;;;;
+1D68C;MATHEMATICAL MONOSPACE SMALL C;Ll;0;L;<font> 0063;;;;N;;;;;
+1D68D;MATHEMATICAL MONOSPACE SMALL D;Ll;0;L;<font> 0064;;;;N;;;;;
+1D68E;MATHEMATICAL MONOSPACE SMALL E;Ll;0;L;<font> 0065;;;;N;;;;;
+1D68F;MATHEMATICAL MONOSPACE SMALL F;Ll;0;L;<font> 0066;;;;N;;;;;
+1D690;MATHEMATICAL MONOSPACE SMALL G;Ll;0;L;<font> 0067;;;;N;;;;;
+1D691;MATHEMATICAL MONOSPACE SMALL H;Ll;0;L;<font> 0068;;;;N;;;;;
+1D692;MATHEMATICAL MONOSPACE SMALL I;Ll;0;L;<font> 0069;;;;N;;;;;
+1D693;MATHEMATICAL MONOSPACE SMALL J;Ll;0;L;<font> 006A;;;;N;;;;;
+1D694;MATHEMATICAL MONOSPACE SMALL K;Ll;0;L;<font> 006B;;;;N;;;;;
+1D695;MATHEMATICAL MONOSPACE SMALL L;Ll;0;L;<font> 006C;;;;N;;;;;
+1D696;MATHEMATICAL MONOSPACE SMALL M;Ll;0;L;<font> 006D;;;;N;;;;;
+1D697;MATHEMATICAL MONOSPACE SMALL N;Ll;0;L;<font> 006E;;;;N;;;;;
+1D698;MATHEMATICAL MONOSPACE SMALL O;Ll;0;L;<font> 006F;;;;N;;;;;
+1D699;MATHEMATICAL MONOSPACE SMALL P;Ll;0;L;<font> 0070;;;;N;;;;;
+1D69A;MATHEMATICAL MONOSPACE SMALL Q;Ll;0;L;<font> 0071;;;;N;;;;;
+1D69B;MATHEMATICAL MONOSPACE SMALL R;Ll;0;L;<font> 0072;;;;N;;;;;
+1D69C;MATHEMATICAL MONOSPACE SMALL S;Ll;0;L;<font> 0073;;;;N;;;;;
+1D69D;MATHEMATICAL MONOSPACE SMALL T;Ll;0;L;<font> 0074;;;;N;;;;;
+1D69E;MATHEMATICAL MONOSPACE SMALL U;Ll;0;L;<font> 0075;;;;N;;;;;
+1D69F;MATHEMATICAL MONOSPACE SMALL V;Ll;0;L;<font> 0076;;;;N;;;;;
+1D6A0;MATHEMATICAL MONOSPACE SMALL W;Ll;0;L;<font> 0077;;;;N;;;;;
+1D6A1;MATHEMATICAL MONOSPACE SMALL X;Ll;0;L;<font> 0078;;;;N;;;;;
+1D6A2;MATHEMATICAL MONOSPACE SMALL Y;Ll;0;L;<font> 0079;;;;N;;;;;
+1D6A3;MATHEMATICAL MONOSPACE SMALL Z;Ll;0;L;<font> 007A;;;;N;;;;;
+1D6A8;MATHEMATICAL BOLD CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;;
+1D6A9;MATHEMATICAL BOLD CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;;
+1D6AA;MATHEMATICAL BOLD CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+1D6AB;MATHEMATICAL BOLD CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;;
+1D6AC;MATHEMATICAL BOLD CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;;
+1D6AD;MATHEMATICAL BOLD CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;;
+1D6AE;MATHEMATICAL BOLD CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;;
+1D6AF;MATHEMATICAL BOLD CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;;
+1D6B0;MATHEMATICAL BOLD CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;;
+1D6B1;MATHEMATICAL BOLD CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;;
+1D6B2;MATHEMATICAL BOLD CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;;
+1D6B3;MATHEMATICAL BOLD CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;;
+1D6B4;MATHEMATICAL BOLD CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;;
+1D6B5;MATHEMATICAL BOLD CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;;
+1D6B6;MATHEMATICAL BOLD CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;;
+1D6B7;MATHEMATICAL BOLD CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+1D6B8;MATHEMATICAL BOLD CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;;
+1D6B9;MATHEMATICAL BOLD CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;;
+1D6BA;MATHEMATICAL BOLD CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;;
+1D6BB;MATHEMATICAL BOLD CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;;
+1D6BC;MATHEMATICAL BOLD CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;;
+1D6BD;MATHEMATICAL BOLD CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;;
+1D6BE;MATHEMATICAL BOLD CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;;
+1D6BF;MATHEMATICAL BOLD CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;;
+1D6C0;MATHEMATICAL BOLD CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;;
+1D6C1;MATHEMATICAL BOLD NABLA;Sm;0;L;<font> 2207;;;;N;;;;;
+1D6C2;MATHEMATICAL BOLD SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;;
+1D6C3;MATHEMATICAL BOLD SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;;
+1D6C4;MATHEMATICAL BOLD SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+1D6C5;MATHEMATICAL BOLD SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;;
+1D6C6;MATHEMATICAL BOLD SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;;
+1D6C7;MATHEMATICAL BOLD SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;;
+1D6C8;MATHEMATICAL BOLD SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;;
+1D6C9;MATHEMATICAL BOLD SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;;
+1D6CA;MATHEMATICAL BOLD SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;;
+1D6CB;MATHEMATICAL BOLD SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;;
+1D6CC;MATHEMATICAL BOLD SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;;
+1D6CD;MATHEMATICAL BOLD SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;;
+1D6CE;MATHEMATICAL BOLD SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;;
+1D6CF;MATHEMATICAL BOLD SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;;
+1D6D0;MATHEMATICAL BOLD SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;;
+1D6D1;MATHEMATICAL BOLD SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;;
+1D6D2;MATHEMATICAL BOLD SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;;
+1D6D3;MATHEMATICAL BOLD SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;;
+1D6D4;MATHEMATICAL BOLD SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;;
+1D6D5;MATHEMATICAL BOLD SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;;
+1D6D6;MATHEMATICAL BOLD SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;;
+1D6D7;MATHEMATICAL BOLD SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;;
+1D6D8;MATHEMATICAL BOLD SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;;
+1D6D9;MATHEMATICAL BOLD SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;;
+1D6DA;MATHEMATICAL BOLD SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;;
+1D6DB;MATHEMATICAL BOLD PARTIAL DIFFERENTIAL;Sm;0;L;<font> 2202;;;;N;;;;;
+1D6DC;MATHEMATICAL BOLD EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;;
+1D6DD;MATHEMATICAL BOLD THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;;
+1D6DE;MATHEMATICAL BOLD KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;;
+1D6DF;MATHEMATICAL BOLD PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;;
+1D6E0;MATHEMATICAL BOLD RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;;
+1D6E1;MATHEMATICAL BOLD PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;;
+1D6E2;MATHEMATICAL ITALIC CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;;
+1D6E3;MATHEMATICAL ITALIC CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;;
+1D6E4;MATHEMATICAL ITALIC CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+1D6E5;MATHEMATICAL ITALIC CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;;
+1D6E6;MATHEMATICAL ITALIC CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;;
+1D6E7;MATHEMATICAL ITALIC CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;;
+1D6E8;MATHEMATICAL ITALIC CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;;
+1D6E9;MATHEMATICAL ITALIC CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;;
+1D6EA;MATHEMATICAL ITALIC CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;;
+1D6EB;MATHEMATICAL ITALIC CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;;
+1D6EC;MATHEMATICAL ITALIC CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;;
+1D6ED;MATHEMATICAL ITALIC CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;;
+1D6EE;MATHEMATICAL ITALIC CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;;
+1D6EF;MATHEMATICAL ITALIC CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;;
+1D6F0;MATHEMATICAL ITALIC CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;;
+1D6F1;MATHEMATICAL ITALIC CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+1D6F2;MATHEMATICAL ITALIC CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;;
+1D6F3;MATHEMATICAL ITALIC CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;;
+1D6F4;MATHEMATICAL ITALIC CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;;
+1D6F5;MATHEMATICAL ITALIC CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;;
+1D6F6;MATHEMATICAL ITALIC CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;;
+1D6F7;MATHEMATICAL ITALIC CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;;
+1D6F8;MATHEMATICAL ITALIC CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;;
+1D6F9;MATHEMATICAL ITALIC CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;;
+1D6FA;MATHEMATICAL ITALIC CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;;
+1D6FB;MATHEMATICAL ITALIC NABLA;Sm;0;L;<font> 2207;;;;N;;;;;
+1D6FC;MATHEMATICAL ITALIC SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;;
+1D6FD;MATHEMATICAL ITALIC SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;;
+1D6FE;MATHEMATICAL ITALIC SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+1D6FF;MATHEMATICAL ITALIC SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;;
+1D700;MATHEMATICAL ITALIC SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;;
+1D701;MATHEMATICAL ITALIC SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;;
+1D702;MATHEMATICAL ITALIC SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;;
+1D703;MATHEMATICAL ITALIC SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;;
+1D704;MATHEMATICAL ITALIC SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;;
+1D705;MATHEMATICAL ITALIC SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;;
+1D706;MATHEMATICAL ITALIC SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;;
+1D707;MATHEMATICAL ITALIC SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;;
+1D708;MATHEMATICAL ITALIC SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;;
+1D709;MATHEMATICAL ITALIC SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;;
+1D70A;MATHEMATICAL ITALIC SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;;
+1D70B;MATHEMATICAL ITALIC SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;;
+1D70C;MATHEMATICAL ITALIC SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;;
+1D70D;MATHEMATICAL ITALIC SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;;
+1D70E;MATHEMATICAL ITALIC SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;;
+1D70F;MATHEMATICAL ITALIC SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;;
+1D710;MATHEMATICAL ITALIC SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;;
+1D711;MATHEMATICAL ITALIC SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;;
+1D712;MATHEMATICAL ITALIC SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;;
+1D713;MATHEMATICAL ITALIC SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;;
+1D714;MATHEMATICAL ITALIC SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;;
+1D715;MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL;Sm;0;L;<font> 2202;;;;N;;;;;
+1D716;MATHEMATICAL ITALIC EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;;
+1D717;MATHEMATICAL ITALIC THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;;
+1D718;MATHEMATICAL ITALIC KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;;
+1D719;MATHEMATICAL ITALIC PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;;
+1D71A;MATHEMATICAL ITALIC RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;;
+1D71B;MATHEMATICAL ITALIC PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;;
+1D71C;MATHEMATICAL BOLD ITALIC CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;;
+1D71D;MATHEMATICAL BOLD ITALIC CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;;
+1D71E;MATHEMATICAL BOLD ITALIC CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+1D71F;MATHEMATICAL BOLD ITALIC CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;;
+1D720;MATHEMATICAL BOLD ITALIC CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;;
+1D721;MATHEMATICAL BOLD ITALIC CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;;
+1D722;MATHEMATICAL BOLD ITALIC CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;;
+1D723;MATHEMATICAL BOLD ITALIC CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;;
+1D724;MATHEMATICAL BOLD ITALIC CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;;
+1D725;MATHEMATICAL BOLD ITALIC CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;;
+1D726;MATHEMATICAL BOLD ITALIC CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;;
+1D727;MATHEMATICAL BOLD ITALIC CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;;
+1D728;MATHEMATICAL BOLD ITALIC CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;;
+1D729;MATHEMATICAL BOLD ITALIC CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;;
+1D72A;MATHEMATICAL BOLD ITALIC CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;;
+1D72B;MATHEMATICAL BOLD ITALIC CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+1D72C;MATHEMATICAL BOLD ITALIC CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;;
+1D72D;MATHEMATICAL BOLD ITALIC CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;;
+1D72E;MATHEMATICAL BOLD ITALIC CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;;
+1D72F;MATHEMATICAL BOLD ITALIC CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;;
+1D730;MATHEMATICAL BOLD ITALIC CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;;
+1D731;MATHEMATICAL BOLD ITALIC CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;;
+1D732;MATHEMATICAL BOLD ITALIC CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;;
+1D733;MATHEMATICAL BOLD ITALIC CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;;
+1D734;MATHEMATICAL BOLD ITALIC CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;;
+1D735;MATHEMATICAL BOLD ITALIC NABLA;Sm;0;L;<font> 2207;;;;N;;;;;
+1D736;MATHEMATICAL BOLD ITALIC SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;;
+1D737;MATHEMATICAL BOLD ITALIC SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;;
+1D738;MATHEMATICAL BOLD ITALIC SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+1D739;MATHEMATICAL BOLD ITALIC SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;;
+1D73A;MATHEMATICAL BOLD ITALIC SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;;
+1D73B;MATHEMATICAL BOLD ITALIC SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;;
+1D73C;MATHEMATICAL BOLD ITALIC SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;;
+1D73D;MATHEMATICAL BOLD ITALIC SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;;
+1D73E;MATHEMATICAL BOLD ITALIC SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;;
+1D73F;MATHEMATICAL BOLD ITALIC SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;;
+1D740;MATHEMATICAL BOLD ITALIC SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;;
+1D741;MATHEMATICAL BOLD ITALIC SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;;
+1D742;MATHEMATICAL BOLD ITALIC SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;;
+1D743;MATHEMATICAL BOLD ITALIC SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;;
+1D744;MATHEMATICAL BOLD ITALIC SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;;
+1D745;MATHEMATICAL BOLD ITALIC SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;;
+1D746;MATHEMATICAL BOLD ITALIC SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;;
+1D747;MATHEMATICAL BOLD ITALIC SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;;
+1D748;MATHEMATICAL BOLD ITALIC SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;;
+1D749;MATHEMATICAL BOLD ITALIC SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;;
+1D74A;MATHEMATICAL BOLD ITALIC SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;;
+1D74B;MATHEMATICAL BOLD ITALIC SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;;
+1D74C;MATHEMATICAL BOLD ITALIC SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;;
+1D74D;MATHEMATICAL BOLD ITALIC SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;;
+1D74E;MATHEMATICAL BOLD ITALIC SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;;
+1D74F;MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL;Sm;0;L;<font> 2202;;;;N;;;;;
+1D750;MATHEMATICAL BOLD ITALIC EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;;
+1D751;MATHEMATICAL BOLD ITALIC THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;;
+1D752;MATHEMATICAL BOLD ITALIC KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;;
+1D753;MATHEMATICAL BOLD ITALIC PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;;
+1D754;MATHEMATICAL BOLD ITALIC RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;;
+1D755;MATHEMATICAL BOLD ITALIC PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;;
+1D756;MATHEMATICAL SANS-SERIF BOLD CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;;
+1D757;MATHEMATICAL SANS-SERIF BOLD CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;;
+1D758;MATHEMATICAL SANS-SERIF BOLD CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+1D759;MATHEMATICAL SANS-SERIF BOLD CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;;
+1D75A;MATHEMATICAL SANS-SERIF BOLD CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;;
+1D75B;MATHEMATICAL SANS-SERIF BOLD CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;;
+1D75C;MATHEMATICAL SANS-SERIF BOLD CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;;
+1D75D;MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;;
+1D75E;MATHEMATICAL SANS-SERIF BOLD CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;;
+1D75F;MATHEMATICAL SANS-SERIF BOLD CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;;
+1D760;MATHEMATICAL SANS-SERIF BOLD CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;;
+1D761;MATHEMATICAL SANS-SERIF BOLD CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;;
+1D762;MATHEMATICAL SANS-SERIF BOLD CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;;
+1D763;MATHEMATICAL SANS-SERIF BOLD CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;;
+1D764;MATHEMATICAL SANS-SERIF BOLD CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;;
+1D765;MATHEMATICAL SANS-SERIF BOLD CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+1D766;MATHEMATICAL SANS-SERIF BOLD CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;;
+1D767;MATHEMATICAL SANS-SERIF BOLD CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;;
+1D768;MATHEMATICAL SANS-SERIF BOLD CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;;
+1D769;MATHEMATICAL SANS-SERIF BOLD CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;;
+1D76A;MATHEMATICAL SANS-SERIF BOLD CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;;
+1D76B;MATHEMATICAL SANS-SERIF BOLD CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;;
+1D76C;MATHEMATICAL SANS-SERIF BOLD CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;;
+1D76D;MATHEMATICAL SANS-SERIF BOLD CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;;
+1D76E;MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;;
+1D76F;MATHEMATICAL SANS-SERIF BOLD NABLA;Sm;0;L;<font> 2207;;;;N;;;;;
+1D770;MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;;
+1D771;MATHEMATICAL SANS-SERIF BOLD SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;;
+1D772;MATHEMATICAL SANS-SERIF BOLD SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+1D773;MATHEMATICAL SANS-SERIF BOLD SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;;
+1D774;MATHEMATICAL SANS-SERIF BOLD SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;;
+1D775;MATHEMATICAL SANS-SERIF BOLD SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;;
+1D776;MATHEMATICAL SANS-SERIF BOLD SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;;
+1D777;MATHEMATICAL SANS-SERIF BOLD SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;;
+1D778;MATHEMATICAL SANS-SERIF BOLD SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;;
+1D779;MATHEMATICAL SANS-SERIF BOLD SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;;
+1D77A;MATHEMATICAL SANS-SERIF BOLD SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;;
+1D77B;MATHEMATICAL SANS-SERIF BOLD SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;;
+1D77C;MATHEMATICAL SANS-SERIF BOLD SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;;
+1D77D;MATHEMATICAL SANS-SERIF BOLD SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;;
+1D77E;MATHEMATICAL SANS-SERIF BOLD SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;;
+1D77F;MATHEMATICAL SANS-SERIF BOLD SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;;
+1D780;MATHEMATICAL SANS-SERIF BOLD SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;;
+1D781;MATHEMATICAL SANS-SERIF BOLD SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;;
+1D782;MATHEMATICAL SANS-SERIF BOLD SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;;
+1D783;MATHEMATICAL SANS-SERIF BOLD SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;;
+1D784;MATHEMATICAL SANS-SERIF BOLD SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;;
+1D785;MATHEMATICAL SANS-SERIF BOLD SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;;
+1D786;MATHEMATICAL SANS-SERIF BOLD SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;;
+1D787;MATHEMATICAL SANS-SERIF BOLD SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;;
+1D788;MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;;
+1D789;MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL;Sm;0;L;<font> 2202;;;;N;;;;;
+1D78A;MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;;
+1D78B;MATHEMATICAL SANS-SERIF BOLD THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;;
+1D78C;MATHEMATICAL SANS-SERIF BOLD KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;;
+1D78D;MATHEMATICAL SANS-SERIF BOLD PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;;
+1D78E;MATHEMATICAL SANS-SERIF BOLD RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;;
+1D78F;MATHEMATICAL SANS-SERIF BOLD PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;;
+1D790;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ALPHA;Lu;0;L;<font> 0391;;;;N;;;;;
+1D791;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL BETA;Lu;0;L;<font> 0392;;;;N;;;;;
+1D792;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL GAMMA;Lu;0;L;<font> 0393;;;;N;;;;;
+1D793;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL DELTA;Lu;0;L;<font> 0394;;;;N;;;;;
+1D794;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL EPSILON;Lu;0;L;<font> 0395;;;;N;;;;;
+1D795;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ZETA;Lu;0;L;<font> 0396;;;;N;;;;;
+1D796;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL ETA;Lu;0;L;<font> 0397;;;;N;;;;;
+1D797;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA;Lu;0;L;<font> 0398;;;;N;;;;;
+1D798;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL IOTA;Lu;0;L;<font> 0399;;;;N;;;;;
+1D799;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL KAPPA;Lu;0;L;<font> 039A;;;;N;;;;;
+1D79A;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL LAMDA;Lu;0;L;<font> 039B;;;;N;;;;;
+1D79B;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL MU;Lu;0;L;<font> 039C;;;;N;;;;;
+1D79C;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL NU;Lu;0;L;<font> 039D;;;;N;;;;;
+1D79D;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL XI;Lu;0;L;<font> 039E;;;;N;;;;;
+1D79E;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMICRON;Lu;0;L;<font> 039F;;;;N;;;;;
+1D79F;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PI;Lu;0;L;<font> 03A0;;;;N;;;;;
+1D7A0;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL RHO;Lu;0;L;<font> 03A1;;;;N;;;;;
+1D7A1;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL THETA SYMBOL;Lu;0;L;<font> 03F4;;;;N;;;;;
+1D7A2;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL SIGMA;Lu;0;L;<font> 03A3;;;;N;;;;;
+1D7A3;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL TAU;Lu;0;L;<font> 03A4;;;;N;;;;;
+1D7A4;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL UPSILON;Lu;0;L;<font> 03A5;;;;N;;;;;
+1D7A5;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PHI;Lu;0;L;<font> 03A6;;;;N;;;;;
+1D7A6;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL CHI;Lu;0;L;<font> 03A7;;;;N;;;;;
+1D7A7;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL PSI;Lu;0;L;<font> 03A8;;;;N;;;;;
+1D7A8;MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA;Lu;0;L;<font> 03A9;;;;N;;;;;
+1D7A9;MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA;Sm;0;L;<font> 2207;;;;N;;;;;
+1D7AA;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA;Ll;0;L;<font> 03B1;;;;N;;;;;
+1D7AB;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL BETA;Ll;0;L;<font> 03B2;;;;N;;;;;
+1D7AC;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL GAMMA;Ll;0;L;<font> 03B3;;;;N;;;;;
+1D7AD;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL DELTA;Ll;0;L;<font> 03B4;;;;N;;;;;
+1D7AE;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL EPSILON;Ll;0;L;<font> 03B5;;;;N;;;;;
+1D7AF;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ZETA;Ll;0;L;<font> 03B6;;;;N;;;;;
+1D7B0;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ETA;Ll;0;L;<font> 03B7;;;;N;;;;;
+1D7B1;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL THETA;Ll;0;L;<font> 03B8;;;;N;;;;;
+1D7B2;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL IOTA;Ll;0;L;<font> 03B9;;;;N;;;;;
+1D7B3;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL KAPPA;Ll;0;L;<font> 03BA;;;;N;;;;;
+1D7B4;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL LAMDA;Ll;0;L;<font> 03BB;;;;N;;;;;
+1D7B5;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL MU;Ll;0;L;<font> 03BC;;;;N;;;;;
+1D7B6;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL NU;Ll;0;L;<font> 03BD;;;;N;;;;;
+1D7B7;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL XI;Ll;0;L;<font> 03BE;;;;N;;;;;
+1D7B8;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMICRON;Ll;0;L;<font> 03BF;;;;N;;;;;
+1D7B9;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PI;Ll;0;L;<font> 03C0;;;;N;;;;;
+1D7BA;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL RHO;Ll;0;L;<font> 03C1;;;;N;;;;;
+1D7BB;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL FINAL SIGMA;Ll;0;L;<font> 03C2;;;;N;;;;;
+1D7BC;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL SIGMA;Ll;0;L;<font> 03C3;;;;N;;;;;
+1D7BD;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL TAU;Ll;0;L;<font> 03C4;;;;N;;;;;
+1D7BE;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL UPSILON;Ll;0;L;<font> 03C5;;;;N;;;;;
+1D7BF;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PHI;Ll;0;L;<font> 03C6;;;;N;;;;;
+1D7C0;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL CHI;Ll;0;L;<font> 03C7;;;;N;;;;;
+1D7C1;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL PSI;Ll;0;L;<font> 03C8;;;;N;;;;;
+1D7C2;MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA;Ll;0;L;<font> 03C9;;;;N;;;;;
+1D7C3;MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL;Sm;0;L;<font> 2202;;;;N;;;;;
+1D7C4;MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL;Ll;0;L;<font> 03F5;;;;N;;;;;
+1D7C5;MATHEMATICAL SANS-SERIF BOLD ITALIC THETA SYMBOL;Ll;0;L;<font> 03D1;;;;N;;;;;
+1D7C6;MATHEMATICAL SANS-SERIF BOLD ITALIC KAPPA SYMBOL;Ll;0;L;<font> 03F0;;;;N;;;;;
+1D7C7;MATHEMATICAL SANS-SERIF BOLD ITALIC PHI SYMBOL;Ll;0;L;<font> 03D5;;;;N;;;;;
+1D7C8;MATHEMATICAL SANS-SERIF BOLD ITALIC RHO SYMBOL;Ll;0;L;<font> 03F1;;;;N;;;;;
+1D7C9;MATHEMATICAL SANS-SERIF BOLD ITALIC PI SYMBOL;Ll;0;L;<font> 03D6;;;;N;;;;;
+1D7CE;MATHEMATICAL BOLD DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;;
+1D7CF;MATHEMATICAL BOLD DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;;
+1D7D0;MATHEMATICAL BOLD DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;;
+1D7D1;MATHEMATICAL BOLD DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;;
+1D7D2;MATHEMATICAL BOLD DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;;
+1D7D3;MATHEMATICAL BOLD DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;;
+1D7D4;MATHEMATICAL BOLD DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;;
+1D7D5;MATHEMATICAL BOLD DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
+1D7D6;MATHEMATICAL BOLD DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
+1D7D7;MATHEMATICAL BOLD DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
+1D7D8;MATHEMATICAL DOUBLE-STRUCK DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;;
+1D7D9;MATHEMATICAL DOUBLE-STRUCK DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;;
+1D7DA;MATHEMATICAL DOUBLE-STRUCK DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;;
+1D7DB;MATHEMATICAL DOUBLE-STRUCK DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;;
+1D7DC;MATHEMATICAL DOUBLE-STRUCK DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;;
+1D7DD;MATHEMATICAL DOUBLE-STRUCK DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;;
+1D7DE;MATHEMATICAL DOUBLE-STRUCK DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;;
+1D7DF;MATHEMATICAL DOUBLE-STRUCK DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
+1D7E0;MATHEMATICAL DOUBLE-STRUCK DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
+1D7E1;MATHEMATICAL DOUBLE-STRUCK DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
+1D7E2;MATHEMATICAL SANS-SERIF DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;;
+1D7E3;MATHEMATICAL SANS-SERIF DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;;
+1D7E4;MATHEMATICAL SANS-SERIF DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;;
+1D7E5;MATHEMATICAL SANS-SERIF DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;;
+1D7E6;MATHEMATICAL SANS-SERIF DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;;
+1D7E7;MATHEMATICAL SANS-SERIF DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;;
+1D7E8;MATHEMATICAL SANS-SERIF DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;;
+1D7E9;MATHEMATICAL SANS-SERIF DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
+1D7EA;MATHEMATICAL SANS-SERIF DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
+1D7EB;MATHEMATICAL SANS-SERIF DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
+1D7EC;MATHEMATICAL SANS-SERIF BOLD DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;;
+1D7ED;MATHEMATICAL SANS-SERIF BOLD DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;;
+1D7EE;MATHEMATICAL SANS-SERIF BOLD DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;;
+1D7EF;MATHEMATICAL SANS-SERIF BOLD DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;;
+1D7F0;MATHEMATICAL SANS-SERIF BOLD DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;;
+1D7F1;MATHEMATICAL SANS-SERIF BOLD DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;;
+1D7F2;MATHEMATICAL SANS-SERIF BOLD DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;;
+1D7F3;MATHEMATICAL SANS-SERIF BOLD DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
+1D7F4;MATHEMATICAL SANS-SERIF BOLD DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
+1D7F5;MATHEMATICAL SANS-SERIF BOLD DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
+1D7F6;MATHEMATICAL MONOSPACE DIGIT ZERO;Nd;0;EN;<font> 0030;0;0;0;N;;;;;
+1D7F7;MATHEMATICAL MONOSPACE DIGIT ONE;Nd;0;EN;<font> 0031;1;1;1;N;;;;;
+1D7F8;MATHEMATICAL MONOSPACE DIGIT TWO;Nd;0;EN;<font> 0032;2;2;2;N;;;;;
+1D7F9;MATHEMATICAL MONOSPACE DIGIT THREE;Nd;0;EN;<font> 0033;3;3;3;N;;;;;
+1D7FA;MATHEMATICAL MONOSPACE DIGIT FOUR;Nd;0;EN;<font> 0034;4;4;4;N;;;;;
+1D7FB;MATHEMATICAL MONOSPACE DIGIT FIVE;Nd;0;EN;<font> 0035;5;5;5;N;;;;;
+1D7FC;MATHEMATICAL MONOSPACE DIGIT SIX;Nd;0;EN;<font> 0036;6;6;6;N;;;;;
+1D7FD;MATHEMATICAL MONOSPACE DIGIT SEVEN;Nd;0;EN;<font> 0037;7;7;7;N;;;;;
+1D7FE;MATHEMATICAL MONOSPACE DIGIT EIGHT;Nd;0;EN;<font> 0038;8;8;8;N;;;;;
+1D7FF;MATHEMATICAL MONOSPACE DIGIT NINE;Nd;0;EN;<font> 0039;9;9;9;N;;;;;
+20000;<CJK Ideograph Extension B, First>;Lo;0;L;;;;;N;;;;;
+2A6D6;<CJK Ideograph Extension B, Last>;Lo;0;L;;;;;N;;;;;
+2F800;CJK COMPATIBILITY IDEOGRAPH-2F800;Lo;0;L;4E3D;;;;N;;;;;
+2F801;CJK COMPATIBILITY IDEOGRAPH-2F801;Lo;0;L;4E38;;;;N;;;;;
+2F802;CJK COMPATIBILITY IDEOGRAPH-2F802;Lo;0;L;4E41;;;;N;;;;;
+2F803;CJK COMPATIBILITY IDEOGRAPH-2F803;Lo;0;L;20122;;;;N;;;;;
+2F804;CJK COMPATIBILITY IDEOGRAPH-2F804;Lo;0;L;4F60;;;;N;;;;;
+2F805;CJK COMPATIBILITY IDEOGRAPH-2F805;Lo;0;L;4FAE;;;;N;;;;;
+2F806;CJK COMPATIBILITY IDEOGRAPH-2F806;Lo;0;L;4FBB;;;;N;;;;;
+2F807;CJK COMPATIBILITY IDEOGRAPH-2F807;Lo;0;L;5002;;;;N;;;;;
+2F808;CJK COMPATIBILITY IDEOGRAPH-2F808;Lo;0;L;507A;;;;N;;;;;
+2F809;CJK COMPATIBILITY IDEOGRAPH-2F809;Lo;0;L;5099;;;;N;;;;;
+2F80A;CJK COMPATIBILITY IDEOGRAPH-2F80A;Lo;0;L;50E7;;;;N;;;;;
+2F80B;CJK COMPATIBILITY IDEOGRAPH-2F80B;Lo;0;L;50CF;;;;N;;;;;
+2F80C;CJK COMPATIBILITY IDEOGRAPH-2F80C;Lo;0;L;349E;;;;N;;;;;
+2F80D;CJK COMPATIBILITY IDEOGRAPH-2F80D;Lo;0;L;2063A;;;;N;;;;;
+2F80E;CJK COMPATIBILITY IDEOGRAPH-2F80E;Lo;0;L;514D;;;;N;;;;;
+2F80F;CJK COMPATIBILITY IDEOGRAPH-2F80F;Lo;0;L;5154;;;;N;;;;;
+2F810;CJK COMPATIBILITY IDEOGRAPH-2F810;Lo;0;L;5164;;;;N;;;;;
+2F811;CJK COMPATIBILITY IDEOGRAPH-2F811;Lo;0;L;5177;;;;N;;;;;
+2F812;CJK COMPATIBILITY IDEOGRAPH-2F812;Lo;0;L;2051C;;;;N;;;;;
+2F813;CJK COMPATIBILITY IDEOGRAPH-2F813;Lo;0;L;34B9;;;;N;;;;;
+2F814;CJK COMPATIBILITY IDEOGRAPH-2F814;Lo;0;L;5167;;;;N;;;;;
+2F815;CJK COMPATIBILITY IDEOGRAPH-2F815;Lo;0;L;518D;;;;N;;;;;
+2F816;CJK COMPATIBILITY IDEOGRAPH-2F816;Lo;0;L;2054B;;;;N;;;;;
+2F817;CJK COMPATIBILITY IDEOGRAPH-2F817;Lo;0;L;5197;;;;N;;;;;
+2F818;CJK COMPATIBILITY IDEOGRAPH-2F818;Lo;0;L;51A4;;;;N;;;;;
+2F819;CJK COMPATIBILITY IDEOGRAPH-2F819;Lo;0;L;4ECC;;;;N;;;;;
+2F81A;CJK COMPATIBILITY IDEOGRAPH-2F81A;Lo;0;L;51AC;;;;N;;;;;
+2F81B;CJK COMPATIBILITY IDEOGRAPH-2F81B;Lo;0;L;51B5;;;;N;;;;;
+2F81C;CJK COMPATIBILITY IDEOGRAPH-2F81C;Lo;0;L;291DF;;;;N;;;;;
+2F81D;CJK COMPATIBILITY IDEOGRAPH-2F81D;Lo;0;L;51F5;;;;N;;;;;
+2F81E;CJK COMPATIBILITY IDEOGRAPH-2F81E;Lo;0;L;5203;;;;N;;;;;
+2F81F;CJK COMPATIBILITY IDEOGRAPH-2F81F;Lo;0;L;34DF;;;;N;;;;;
+2F820;CJK COMPATIBILITY IDEOGRAPH-2F820;Lo;0;L;523B;;;;N;;;;;
+2F821;CJK COMPATIBILITY IDEOGRAPH-2F821;Lo;0;L;5246;;;;N;;;;;
+2F822;CJK COMPATIBILITY IDEOGRAPH-2F822;Lo;0;L;5272;;;;N;;;;;
+2F823;CJK COMPATIBILITY IDEOGRAPH-2F823;Lo;0;L;5277;;;;N;;;;;
+2F824;CJK COMPATIBILITY IDEOGRAPH-2F824;Lo;0;L;3515;;;;N;;;;;
+2F825;CJK COMPATIBILITY IDEOGRAPH-2F825;Lo;0;L;52C7;;;;N;;;;;
+2F826;CJK COMPATIBILITY IDEOGRAPH-2F826;Lo;0;L;52C9;;;;N;;;;;
+2F827;CJK COMPATIBILITY IDEOGRAPH-2F827;Lo;0;L;52E4;;;;N;;;;;
+2F828;CJK COMPATIBILITY IDEOGRAPH-2F828;Lo;0;L;52FA;;;;N;;;;;
+2F829;CJK COMPATIBILITY IDEOGRAPH-2F829;Lo;0;L;5305;;;;N;;;;;
+2F82A;CJK COMPATIBILITY IDEOGRAPH-2F82A;Lo;0;L;5306;;;;N;;;;;
+2F82B;CJK COMPATIBILITY IDEOGRAPH-2F82B;Lo;0;L;5317;;;;N;;;;;
+2F82C;CJK COMPATIBILITY IDEOGRAPH-2F82C;Lo;0;L;5349;;;;N;;;;;
+2F82D;CJK COMPATIBILITY IDEOGRAPH-2F82D;Lo;0;L;5351;;;;N;;;;;
+2F82E;CJK COMPATIBILITY IDEOGRAPH-2F82E;Lo;0;L;535A;;;;N;;;;;
+2F82F;CJK COMPATIBILITY IDEOGRAPH-2F82F;Lo;0;L;5373;;;;N;;;;;
+2F830;CJK COMPATIBILITY IDEOGRAPH-2F830;Lo;0;L;537D;;;;N;;;;;
+2F831;CJK COMPATIBILITY IDEOGRAPH-2F831;Lo;0;L;537F;;;;N;;;;;
+2F832;CJK COMPATIBILITY IDEOGRAPH-2F832;Lo;0;L;537F;;;;N;;;;;
+2F833;CJK COMPATIBILITY IDEOGRAPH-2F833;Lo;0;L;537F;;;;N;;;;;
+2F834;CJK COMPATIBILITY IDEOGRAPH-2F834;Lo;0;L;20A2C;;;;N;;;;;
+2F835;CJK COMPATIBILITY IDEOGRAPH-2F835;Lo;0;L;7070;;;;N;;;;;
+2F836;CJK COMPATIBILITY IDEOGRAPH-2F836;Lo;0;L;53CA;;;;N;;;;;
+2F837;CJK COMPATIBILITY IDEOGRAPH-2F837;Lo;0;L;53DF;;;;N;;;;;
+2F838;CJK COMPATIBILITY IDEOGRAPH-2F838;Lo;0;L;20B63;;;;N;;;;;
+2F839;CJK COMPATIBILITY IDEOGRAPH-2F839;Lo;0;L;53EB;;;;N;;;;;
+2F83A;CJK COMPATIBILITY IDEOGRAPH-2F83A;Lo;0;L;53F1;;;;N;;;;;
+2F83B;CJK COMPATIBILITY IDEOGRAPH-2F83B;Lo;0;L;5406;;;;N;;;;;
+2F83C;CJK COMPATIBILITY IDEOGRAPH-2F83C;Lo;0;L;549E;;;;N;;;;;
+2F83D;CJK COMPATIBILITY IDEOGRAPH-2F83D;Lo;0;L;5438;;;;N;;;;;
+2F83E;CJK COMPATIBILITY IDEOGRAPH-2F83E;Lo;0;L;5448;;;;N;;;;;
+2F83F;CJK COMPATIBILITY IDEOGRAPH-2F83F;Lo;0;L;5468;;;;N;;;;;
+2F840;CJK COMPATIBILITY IDEOGRAPH-2F840;Lo;0;L;54A2;;;;N;;;;;
+2F841;CJK COMPATIBILITY IDEOGRAPH-2F841;Lo;0;L;54F6;;;;N;;;;;
+2F842;CJK COMPATIBILITY IDEOGRAPH-2F842;Lo;0;L;5510;;;;N;;;;;
+2F843;CJK COMPATIBILITY IDEOGRAPH-2F843;Lo;0;L;5553;;;;N;;;;;
+2F844;CJK COMPATIBILITY IDEOGRAPH-2F844;Lo;0;L;5563;;;;N;;;;;
+2F845;CJK COMPATIBILITY IDEOGRAPH-2F845;Lo;0;L;5584;;;;N;;;;;
+2F846;CJK COMPATIBILITY IDEOGRAPH-2F846;Lo;0;L;5584;;;;N;;;;;
+2F847;CJK COMPATIBILITY IDEOGRAPH-2F847;Lo;0;L;5599;;;;N;;;;;
+2F848;CJK COMPATIBILITY IDEOGRAPH-2F848;Lo;0;L;55AB;;;;N;;;;;
+2F849;CJK COMPATIBILITY IDEOGRAPH-2F849;Lo;0;L;55B3;;;;N;;;;;
+2F84A;CJK COMPATIBILITY IDEOGRAPH-2F84A;Lo;0;L;55C2;;;;N;;;;;
+2F84B;CJK COMPATIBILITY IDEOGRAPH-2F84B;Lo;0;L;5716;;;;N;;;;;
+2F84C;CJK COMPATIBILITY IDEOGRAPH-2F84C;Lo;0;L;5606;;;;N;;;;;
+2F84D;CJK COMPATIBILITY IDEOGRAPH-2F84D;Lo;0;L;5717;;;;N;;;;;
+2F84E;CJK COMPATIBILITY IDEOGRAPH-2F84E;Lo;0;L;5651;;;;N;;;;;
+2F84F;CJK COMPATIBILITY IDEOGRAPH-2F84F;Lo;0;L;5674;;;;N;;;;;
+2F850;CJK COMPATIBILITY IDEOGRAPH-2F850;Lo;0;L;5207;;;;N;;;;;
+2F851;CJK COMPATIBILITY IDEOGRAPH-2F851;Lo;0;L;58EE;;;;N;;;;;
+2F852;CJK COMPATIBILITY IDEOGRAPH-2F852;Lo;0;L;57CE;;;;N;;;;;
+2F853;CJK COMPATIBILITY IDEOGRAPH-2F853;Lo;0;L;57F4;;;;N;;;;;
+2F854;CJK COMPATIBILITY IDEOGRAPH-2F854;Lo;0;L;580D;;;;N;;;;;
+2F855;CJK COMPATIBILITY IDEOGRAPH-2F855;Lo;0;L;578B;;;;N;;;;;
+2F856;CJK COMPATIBILITY IDEOGRAPH-2F856;Lo;0;L;5832;;;;N;;;;;
+2F857;CJK COMPATIBILITY IDEOGRAPH-2F857;Lo;0;L;5831;;;;N;;;;;
+2F858;CJK COMPATIBILITY IDEOGRAPH-2F858;Lo;0;L;58AC;;;;N;;;;;
+2F859;CJK COMPATIBILITY IDEOGRAPH-2F859;Lo;0;L;214E4;;;;N;;;;;
+2F85A;CJK COMPATIBILITY IDEOGRAPH-2F85A;Lo;0;L;58F2;;;;N;;;;;
+2F85B;CJK COMPATIBILITY IDEOGRAPH-2F85B;Lo;0;L;58F7;;;;N;;;;;
+2F85C;CJK COMPATIBILITY IDEOGRAPH-2F85C;Lo;0;L;5906;;;;N;;;;;
+2F85D;CJK COMPATIBILITY IDEOGRAPH-2F85D;Lo;0;L;591A;;;;N;;;;;
+2F85E;CJK COMPATIBILITY IDEOGRAPH-2F85E;Lo;0;L;5922;;;;N;;;;;
+2F85F;CJK COMPATIBILITY IDEOGRAPH-2F85F;Lo;0;L;5962;;;;N;;;;;
+2F860;CJK COMPATIBILITY IDEOGRAPH-2F860;Lo;0;L;216A8;;;;N;;;;;
+2F861;CJK COMPATIBILITY IDEOGRAPH-2F861;Lo;0;L;216EA;;;;N;;;;;
+2F862;CJK COMPATIBILITY IDEOGRAPH-2F862;Lo;0;L;59EC;;;;N;;;;;
+2F863;CJK COMPATIBILITY IDEOGRAPH-2F863;Lo;0;L;5A1B;;;;N;;;;;
+2F864;CJK COMPATIBILITY IDEOGRAPH-2F864;Lo;0;L;5A27;;;;N;;;;;
+2F865;CJK COMPATIBILITY IDEOGRAPH-2F865;Lo;0;L;59D8;;;;N;;;;;
+2F866;CJK COMPATIBILITY IDEOGRAPH-2F866;Lo;0;L;5A66;;;;N;;;;;
+2F867;CJK COMPATIBILITY IDEOGRAPH-2F867;Lo;0;L;36EE;;;;N;;;;;
+2F868;CJK COMPATIBILITY IDEOGRAPH-2F868;Lo;0;L;2136A;;;;N;;;;;
+2F869;CJK COMPATIBILITY IDEOGRAPH-2F869;Lo;0;L;5B08;;;;N;;;;;
+2F86A;CJK COMPATIBILITY IDEOGRAPH-2F86A;Lo;0;L;5B3E;;;;N;;;;;
+2F86B;CJK COMPATIBILITY IDEOGRAPH-2F86B;Lo;0;L;5B3E;;;;N;;;;;
+2F86C;CJK COMPATIBILITY IDEOGRAPH-2F86C;Lo;0;L;219C8;;;;N;;;;;
+2F86D;CJK COMPATIBILITY IDEOGRAPH-2F86D;Lo;0;L;5BC3;;;;N;;;;;
+2F86E;CJK COMPATIBILITY IDEOGRAPH-2F86E;Lo;0;L;5BD8;;;;N;;;;;
+2F86F;CJK COMPATIBILITY IDEOGRAPH-2F86F;Lo;0;L;5BE7;;;;N;;;;;
+2F870;CJK COMPATIBILITY IDEOGRAPH-2F870;Lo;0;L;5BF3;;;;N;;;;;
+2F871;CJK COMPATIBILITY IDEOGRAPH-2F871;Lo;0;L;21B18;;;;N;;;;;
+2F872;CJK COMPATIBILITY IDEOGRAPH-2F872;Lo;0;L;5BFF;;;;N;;;;;
+2F873;CJK COMPATIBILITY IDEOGRAPH-2F873;Lo;0;L;5C06;;;;N;;;;;
+2F874;CJK COMPATIBILITY IDEOGRAPH-2F874;Lo;0;L;5F33;;;;N;;;;;
+2F875;CJK COMPATIBILITY IDEOGRAPH-2F875;Lo;0;L;5C22;;;;N;;;;;
+2F876;CJK COMPATIBILITY IDEOGRAPH-2F876;Lo;0;L;3781;;;;N;;;;;
+2F877;CJK COMPATIBILITY IDEOGRAPH-2F877;Lo;0;L;5C60;;;;N;;;;;
+2F878;CJK COMPATIBILITY IDEOGRAPH-2F878;Lo;0;L;5C6E;;;;N;;;;;
+2F879;CJK COMPATIBILITY IDEOGRAPH-2F879;Lo;0;L;5CC0;;;;N;;;;;
+2F87A;CJK COMPATIBILITY IDEOGRAPH-2F87A;Lo;0;L;5C8D;;;;N;;;;;
+2F87B;CJK COMPATIBILITY IDEOGRAPH-2F87B;Lo;0;L;21DE4;;;;N;;;;;
+2F87C;CJK COMPATIBILITY IDEOGRAPH-2F87C;Lo;0;L;5D43;;;;N;;;;;
+2F87D;CJK COMPATIBILITY IDEOGRAPH-2F87D;Lo;0;L;21DE6;;;;N;;;;;
+2F87E;CJK COMPATIBILITY IDEOGRAPH-2F87E;Lo;0;L;5D6E;;;;N;;;;;
+2F87F;CJK COMPATIBILITY IDEOGRAPH-2F87F;Lo;0;L;5D6B;;;;N;;;;;
+2F880;CJK COMPATIBILITY IDEOGRAPH-2F880;Lo;0;L;5D7C;;;;N;;;;;
+2F881;CJK COMPATIBILITY IDEOGRAPH-2F881;Lo;0;L;5DE1;;;;N;;;;;
+2F882;CJK COMPATIBILITY IDEOGRAPH-2F882;Lo;0;L;5DE2;;;;N;;;;;
+2F883;CJK COMPATIBILITY IDEOGRAPH-2F883;Lo;0;L;382F;;;;N;;;;;
+2F884;CJK COMPATIBILITY IDEOGRAPH-2F884;Lo;0;L;5DFD;;;;N;;;;;
+2F885;CJK COMPATIBILITY IDEOGRAPH-2F885;Lo;0;L;5E28;;;;N;;;;;
+2F886;CJK COMPATIBILITY IDEOGRAPH-2F886;Lo;0;L;5E3D;;;;N;;;;;
+2F887;CJK COMPATIBILITY IDEOGRAPH-2F887;Lo;0;L;5E69;;;;N;;;;;
+2F888;CJK COMPATIBILITY IDEOGRAPH-2F888;Lo;0;L;3862;;;;N;;;;;
+2F889;CJK COMPATIBILITY IDEOGRAPH-2F889;Lo;0;L;22183;;;;N;;;;;
+2F88A;CJK COMPATIBILITY IDEOGRAPH-2F88A;Lo;0;L;387C;;;;N;;;;;
+2F88B;CJK COMPATIBILITY IDEOGRAPH-2F88B;Lo;0;L;5EB0;;;;N;;;;;
+2F88C;CJK COMPATIBILITY IDEOGRAPH-2F88C;Lo;0;L;5EB3;;;;N;;;;;
+2F88D;CJK COMPATIBILITY IDEOGRAPH-2F88D;Lo;0;L;5EB6;;;;N;;;;;
+2F88E;CJK COMPATIBILITY IDEOGRAPH-2F88E;Lo;0;L;5ECA;;;;N;;;;;
+2F88F;CJK COMPATIBILITY IDEOGRAPH-2F88F;Lo;0;L;2A392;;;;N;;;;;
+2F890;CJK COMPATIBILITY IDEOGRAPH-2F890;Lo;0;L;5EFE;;;;N;;;;;
+2F891;CJK COMPATIBILITY IDEOGRAPH-2F891;Lo;0;L;22331;;;;N;;;;;
+2F892;CJK COMPATIBILITY IDEOGRAPH-2F892;Lo;0;L;22331;;;;N;;;;;
+2F893;CJK COMPATIBILITY IDEOGRAPH-2F893;Lo;0;L;8201;;;;N;;;;;
+2F894;CJK COMPATIBILITY IDEOGRAPH-2F894;Lo;0;L;5F22;;;;N;;;;;
+2F895;CJK COMPATIBILITY IDEOGRAPH-2F895;Lo;0;L;5F22;;;;N;;;;;
+2F896;CJK COMPATIBILITY IDEOGRAPH-2F896;Lo;0;L;38C7;;;;N;;;;;
+2F897;CJK COMPATIBILITY IDEOGRAPH-2F897;Lo;0;L;232B8;;;;N;;;;;
+2F898;CJK COMPATIBILITY IDEOGRAPH-2F898;Lo;0;L;261DA;;;;N;;;;;
+2F899;CJK COMPATIBILITY IDEOGRAPH-2F899;Lo;0;L;5F62;;;;N;;;;;
+2F89A;CJK COMPATIBILITY IDEOGRAPH-2F89A;Lo;0;L;5F6B;;;;N;;;;;
+2F89B;CJK COMPATIBILITY IDEOGRAPH-2F89B;Lo;0;L;38E3;;;;N;;;;;
+2F89C;CJK COMPATIBILITY IDEOGRAPH-2F89C;Lo;0;L;5F9A;;;;N;;;;;
+2F89D;CJK COMPATIBILITY IDEOGRAPH-2F89D;Lo;0;L;5FCD;;;;N;;;;;
+2F89E;CJK COMPATIBILITY IDEOGRAPH-2F89E;Lo;0;L;5FD7;;;;N;;;;;
+2F89F;CJK COMPATIBILITY IDEOGRAPH-2F89F;Lo;0;L;5FF9;;;;N;;;;;
+2F8A0;CJK COMPATIBILITY IDEOGRAPH-2F8A0;Lo;0;L;6081;;;;N;;;;;
+2F8A1;CJK COMPATIBILITY IDEOGRAPH-2F8A1;Lo;0;L;393A;;;;N;;;;;
+2F8A2;CJK COMPATIBILITY IDEOGRAPH-2F8A2;Lo;0;L;391C;;;;N;;;;;
+2F8A3;CJK COMPATIBILITY IDEOGRAPH-2F8A3;Lo;0;L;6094;;;;N;;;;;
+2F8A4;CJK COMPATIBILITY IDEOGRAPH-2F8A4;Lo;0;L;226D4;;;;N;;;;;
+2F8A5;CJK COMPATIBILITY IDEOGRAPH-2F8A5;Lo;0;L;60C7;;;;N;;;;;
+2F8A6;CJK COMPATIBILITY IDEOGRAPH-2F8A6;Lo;0;L;6148;;;;N;;;;;
+2F8A7;CJK COMPATIBILITY IDEOGRAPH-2F8A7;Lo;0;L;614C;;;;N;;;;;
+2F8A8;CJK COMPATIBILITY IDEOGRAPH-2F8A8;Lo;0;L;614E;;;;N;;;;;
+2F8A9;CJK COMPATIBILITY IDEOGRAPH-2F8A9;Lo;0;L;614C;;;;N;;;;;
+2F8AA;CJK COMPATIBILITY IDEOGRAPH-2F8AA;Lo;0;L;617A;;;;N;;;;;
+2F8AB;CJK COMPATIBILITY IDEOGRAPH-2F8AB;Lo;0;L;618E;;;;N;;;;;
+2F8AC;CJK COMPATIBILITY IDEOGRAPH-2F8AC;Lo;0;L;61B2;;;;N;;;;;
+2F8AD;CJK COMPATIBILITY IDEOGRAPH-2F8AD;Lo;0;L;61A4;;;;N;;;;;
+2F8AE;CJK COMPATIBILITY IDEOGRAPH-2F8AE;Lo;0;L;61AF;;;;N;;;;;
+2F8AF;CJK COMPATIBILITY IDEOGRAPH-2F8AF;Lo;0;L;61DE;;;;N;;;;;
+2F8B0;CJK COMPATIBILITY IDEOGRAPH-2F8B0;Lo;0;L;61F2;;;;N;;;;;
+2F8B1;CJK COMPATIBILITY IDEOGRAPH-2F8B1;Lo;0;L;61F6;;;;N;;;;;
+2F8B2;CJK COMPATIBILITY IDEOGRAPH-2F8B2;Lo;0;L;6210;;;;N;;;;;
+2F8B3;CJK COMPATIBILITY IDEOGRAPH-2F8B3;Lo;0;L;621B;;;;N;;;;;
+2F8B4;CJK COMPATIBILITY IDEOGRAPH-2F8B4;Lo;0;L;625D;;;;N;;;;;
+2F8B5;CJK COMPATIBILITY IDEOGRAPH-2F8B5;Lo;0;L;62B1;;;;N;;;;;
+2F8B6;CJK COMPATIBILITY IDEOGRAPH-2F8B6;Lo;0;L;62D4;;;;N;;;;;
+2F8B7;CJK COMPATIBILITY IDEOGRAPH-2F8B7;Lo;0;L;6350;;;;N;;;;;
+2F8B8;CJK COMPATIBILITY IDEOGRAPH-2F8B8;Lo;0;L;22B0C;;;;N;;;;;
+2F8B9;CJK COMPATIBILITY IDEOGRAPH-2F8B9;Lo;0;L;633D;;;;N;;;;;
+2F8BA;CJK COMPATIBILITY IDEOGRAPH-2F8BA;Lo;0;L;62FC;;;;N;;;;;
+2F8BB;CJK COMPATIBILITY IDEOGRAPH-2F8BB;Lo;0;L;6368;;;;N;;;;;
+2F8BC;CJK COMPATIBILITY IDEOGRAPH-2F8BC;Lo;0;L;6383;;;;N;;;;;
+2F8BD;CJK COMPATIBILITY IDEOGRAPH-2F8BD;Lo;0;L;63E4;;;;N;;;;;
+2F8BE;CJK COMPATIBILITY IDEOGRAPH-2F8BE;Lo;0;L;22BF1;;;;N;;;;;
+2F8BF;CJK COMPATIBILITY IDEOGRAPH-2F8BF;Lo;0;L;6422;;;;N;;;;;
+2F8C0;CJK COMPATIBILITY IDEOGRAPH-2F8C0;Lo;0;L;63C5;;;;N;;;;;
+2F8C1;CJK COMPATIBILITY IDEOGRAPH-2F8C1;Lo;0;L;63A9;;;;N;;;;;
+2F8C2;CJK COMPATIBILITY IDEOGRAPH-2F8C2;Lo;0;L;3A2E;;;;N;;;;;
+2F8C3;CJK COMPATIBILITY IDEOGRAPH-2F8C3;Lo;0;L;6469;;;;N;;;;;
+2F8C4;CJK COMPATIBILITY IDEOGRAPH-2F8C4;Lo;0;L;647E;;;;N;;;;;
+2F8C5;CJK COMPATIBILITY IDEOGRAPH-2F8C5;Lo;0;L;649D;;;;N;;;;;
+2F8C6;CJK COMPATIBILITY IDEOGRAPH-2F8C6;Lo;0;L;6477;;;;N;;;;;
+2F8C7;CJK COMPATIBILITY IDEOGRAPH-2F8C7;Lo;0;L;3A6C;;;;N;;;;;
+2F8C8;CJK COMPATIBILITY IDEOGRAPH-2F8C8;Lo;0;L;654F;;;;N;;;;;
+2F8C9;CJK COMPATIBILITY IDEOGRAPH-2F8C9;Lo;0;L;656C;;;;N;;;;;
+2F8CA;CJK COMPATIBILITY IDEOGRAPH-2F8CA;Lo;0;L;2300A;;;;N;;;;;
+2F8CB;CJK COMPATIBILITY IDEOGRAPH-2F8CB;Lo;0;L;65E3;;;;N;;;;;
+2F8CC;CJK COMPATIBILITY IDEOGRAPH-2F8CC;Lo;0;L;66F8;;;;N;;;;;
+2F8CD;CJK COMPATIBILITY IDEOGRAPH-2F8CD;Lo;0;L;6649;;;;N;;;;;
+2F8CE;CJK COMPATIBILITY IDEOGRAPH-2F8CE;Lo;0;L;3B19;;;;N;;;;;
+2F8CF;CJK COMPATIBILITY IDEOGRAPH-2F8CF;Lo;0;L;6691;;;;N;;;;;
+2F8D0;CJK COMPATIBILITY IDEOGRAPH-2F8D0;Lo;0;L;3B08;;;;N;;;;;
+2F8D1;CJK COMPATIBILITY IDEOGRAPH-2F8D1;Lo;0;L;3AE4;;;;N;;;;;
+2F8D2;CJK COMPATIBILITY IDEOGRAPH-2F8D2;Lo;0;L;5192;;;;N;;;;;
+2F8D3;CJK COMPATIBILITY IDEOGRAPH-2F8D3;Lo;0;L;5195;;;;N;;;;;
+2F8D4;CJK COMPATIBILITY IDEOGRAPH-2F8D4;Lo;0;L;6700;;;;N;;;;;
+2F8D5;CJK COMPATIBILITY IDEOGRAPH-2F8D5;Lo;0;L;669C;;;;N;;;;;
+2F8D6;CJK COMPATIBILITY IDEOGRAPH-2F8D6;Lo;0;L;80AD;;;;N;;;;;
+2F8D7;CJK COMPATIBILITY IDEOGRAPH-2F8D7;Lo;0;L;43D9;;;;N;;;;;
+2F8D8;CJK COMPATIBILITY IDEOGRAPH-2F8D8;Lo;0;L;6717;;;;N;;;;;
+2F8D9;CJK COMPATIBILITY IDEOGRAPH-2F8D9;Lo;0;L;671B;;;;N;;;;;
+2F8DA;CJK COMPATIBILITY IDEOGRAPH-2F8DA;Lo;0;L;6721;;;;N;;;;;
+2F8DB;CJK COMPATIBILITY IDEOGRAPH-2F8DB;Lo;0;L;675E;;;;N;;;;;
+2F8DC;CJK COMPATIBILITY IDEOGRAPH-2F8DC;Lo;0;L;6753;;;;N;;;;;
+2F8DD;CJK COMPATIBILITY IDEOGRAPH-2F8DD;Lo;0;L;233C3;;;;N;;;;;
+2F8DE;CJK COMPATIBILITY IDEOGRAPH-2F8DE;Lo;0;L;3B49;;;;N;;;;;
+2F8DF;CJK COMPATIBILITY IDEOGRAPH-2F8DF;Lo;0;L;67FA;;;;N;;;;;
+2F8E0;CJK COMPATIBILITY IDEOGRAPH-2F8E0;Lo;0;L;6785;;;;N;;;;;
+2F8E1;CJK COMPATIBILITY IDEOGRAPH-2F8E1;Lo;0;L;6852;;;;N;;;;;
+2F8E2;CJK COMPATIBILITY IDEOGRAPH-2F8E2;Lo;0;L;6885;;;;N;;;;;
+2F8E3;CJK COMPATIBILITY IDEOGRAPH-2F8E3;Lo;0;L;2346D;;;;N;;;;;
+2F8E4;CJK COMPATIBILITY IDEOGRAPH-2F8E4;Lo;0;L;688E;;;;N;;;;;
+2F8E5;CJK COMPATIBILITY IDEOGRAPH-2F8E5;Lo;0;L;681F;;;;N;;;;;
+2F8E6;CJK COMPATIBILITY IDEOGRAPH-2F8E6;Lo;0;L;6914;;;;N;;;;;
+2F8E7;CJK COMPATIBILITY IDEOGRAPH-2F8E7;Lo;0;L;3B9D;;;;N;;;;;
+2F8E8;CJK COMPATIBILITY IDEOGRAPH-2F8E8;Lo;0;L;6942;;;;N;;;;;
+2F8E9;CJK COMPATIBILITY IDEOGRAPH-2F8E9;Lo;0;L;69A3;;;;N;;;;;
+2F8EA;CJK COMPATIBILITY IDEOGRAPH-2F8EA;Lo;0;L;69EA;;;;N;;;;;
+2F8EB;CJK COMPATIBILITY IDEOGRAPH-2F8EB;Lo;0;L;6AA8;;;;N;;;;;
+2F8EC;CJK COMPATIBILITY IDEOGRAPH-2F8EC;Lo;0;L;236A3;;;;N;;;;;
+2F8ED;CJK COMPATIBILITY IDEOGRAPH-2F8ED;Lo;0;L;6ADB;;;;N;;;;;
+2F8EE;CJK COMPATIBILITY IDEOGRAPH-2F8EE;Lo;0;L;3C18;;;;N;;;;;
+2F8EF;CJK COMPATIBILITY IDEOGRAPH-2F8EF;Lo;0;L;6B21;;;;N;;;;;
+2F8F0;CJK COMPATIBILITY IDEOGRAPH-2F8F0;Lo;0;L;238A7;;;;N;;;;;
+2F8F1;CJK COMPATIBILITY IDEOGRAPH-2F8F1;Lo;0;L;6B54;;;;N;;;;;
+2F8F2;CJK COMPATIBILITY IDEOGRAPH-2F8F2;Lo;0;L;3C4E;;;;N;;;;;
+2F8F3;CJK COMPATIBILITY IDEOGRAPH-2F8F3;Lo;0;L;6B72;;;;N;;;;;
+2F8F4;CJK COMPATIBILITY IDEOGRAPH-2F8F4;Lo;0;L;6B9F;;;;N;;;;;
+2F8F5;CJK COMPATIBILITY IDEOGRAPH-2F8F5;Lo;0;L;6BBA;;;;N;;;;;
+2F8F6;CJK COMPATIBILITY IDEOGRAPH-2F8F6;Lo;0;L;6BBB;;;;N;;;;;
+2F8F7;CJK COMPATIBILITY IDEOGRAPH-2F8F7;Lo;0;L;23A8D;;;;N;;;;;
+2F8F8;CJK COMPATIBILITY IDEOGRAPH-2F8F8;Lo;0;L;21D0B;;;;N;;;;;
+2F8F9;CJK COMPATIBILITY IDEOGRAPH-2F8F9;Lo;0;L;23AFA;;;;N;;;;;
+2F8FA;CJK COMPATIBILITY IDEOGRAPH-2F8FA;Lo;0;L;6C4E;;;;N;;;;;
+2F8FB;CJK COMPATIBILITY IDEOGRAPH-2F8FB;Lo;0;L;23CBC;;;;N;;;;;
+2F8FC;CJK COMPATIBILITY IDEOGRAPH-2F8FC;Lo;0;L;6CBF;;;;N;;;;;
+2F8FD;CJK COMPATIBILITY IDEOGRAPH-2F8FD;Lo;0;L;6CCD;;;;N;;;;;
+2F8FE;CJK COMPATIBILITY IDEOGRAPH-2F8FE;Lo;0;L;6C67;;;;N;;;;;
+2F8FF;CJK COMPATIBILITY IDEOGRAPH-2F8FF;Lo;0;L;6D16;;;;N;;;;;
+2F900;CJK COMPATIBILITY IDEOGRAPH-2F900;Lo;0;L;6D3E;;;;N;;;;;
+2F901;CJK COMPATIBILITY IDEOGRAPH-2F901;Lo;0;L;6D77;;;;N;;;;;
+2F902;CJK COMPATIBILITY IDEOGRAPH-2F902;Lo;0;L;6D41;;;;N;;;;;
+2F903;CJK COMPATIBILITY IDEOGRAPH-2F903;Lo;0;L;6D69;;;;N;;;;;
+2F904;CJK COMPATIBILITY IDEOGRAPH-2F904;Lo;0;L;6D78;;;;N;;;;;
+2F905;CJK COMPATIBILITY IDEOGRAPH-2F905;Lo;0;L;6D85;;;;N;;;;;
+2F906;CJK COMPATIBILITY IDEOGRAPH-2F906;Lo;0;L;23D1E;;;;N;;;;;
+2F907;CJK COMPATIBILITY IDEOGRAPH-2F907;Lo;0;L;6D34;;;;N;;;;;
+2F908;CJK COMPATIBILITY IDEOGRAPH-2F908;Lo;0;L;6E2F;;;;N;;;;;
+2F909;CJK COMPATIBILITY IDEOGRAPH-2F909;Lo;0;L;6E6E;;;;N;;;;;
+2F90A;CJK COMPATIBILITY IDEOGRAPH-2F90A;Lo;0;L;3D33;;;;N;;;;;
+2F90B;CJK COMPATIBILITY IDEOGRAPH-2F90B;Lo;0;L;6ECB;;;;N;;;;;
+2F90C;CJK COMPATIBILITY IDEOGRAPH-2F90C;Lo;0;L;6EC7;;;;N;;;;;
+2F90D;CJK COMPATIBILITY IDEOGRAPH-2F90D;Lo;0;L;23ED1;;;;N;;;;;
+2F90E;CJK COMPATIBILITY IDEOGRAPH-2F90E;Lo;0;L;6DF9;;;;N;;;;;
+2F90F;CJK COMPATIBILITY IDEOGRAPH-2F90F;Lo;0;L;6F6E;;;;N;;;;;
+2F910;CJK COMPATIBILITY IDEOGRAPH-2F910;Lo;0;L;23F5E;;;;N;;;;;
+2F911;CJK COMPATIBILITY IDEOGRAPH-2F911;Lo;0;L;23F8E;;;;N;;;;;
+2F912;CJK COMPATIBILITY IDEOGRAPH-2F912;Lo;0;L;6FC6;;;;N;;;;;
+2F913;CJK COMPATIBILITY IDEOGRAPH-2F913;Lo;0;L;7039;;;;N;;;;;
+2F914;CJK COMPATIBILITY IDEOGRAPH-2F914;Lo;0;L;701E;;;;N;;;;;
+2F915;CJK COMPATIBILITY IDEOGRAPH-2F915;Lo;0;L;701B;;;;N;;;;;
+2F916;CJK COMPATIBILITY IDEOGRAPH-2F916;Lo;0;L;3D96;;;;N;;;;;
+2F917;CJK COMPATIBILITY IDEOGRAPH-2F917;Lo;0;L;704A;;;;N;;;;;
+2F918;CJK COMPATIBILITY IDEOGRAPH-2F918;Lo;0;L;707D;;;;N;;;;;
+2F919;CJK COMPATIBILITY IDEOGRAPH-2F919;Lo;0;L;7077;;;;N;;;;;
+2F91A;CJK COMPATIBILITY IDEOGRAPH-2F91A;Lo;0;L;70AD;;;;N;;;;;
+2F91B;CJK COMPATIBILITY IDEOGRAPH-2F91B;Lo;0;L;20525;;;;N;;;;;
+2F91C;CJK COMPATIBILITY IDEOGRAPH-2F91C;Lo;0;L;7145;;;;N;;;;;
+2F91D;CJK COMPATIBILITY IDEOGRAPH-2F91D;Lo;0;L;24263;;;;N;;;;;
+2F91E;CJK COMPATIBILITY IDEOGRAPH-2F91E;Lo;0;L;719C;;;;N;;;;;
+2F91F;CJK COMPATIBILITY IDEOGRAPH-2F91F;Lo;0;L;43AB;;;;N;;;;;
+2F920;CJK COMPATIBILITY IDEOGRAPH-2F920;Lo;0;L;7228;;;;N;;;;;
+2F921;CJK COMPATIBILITY IDEOGRAPH-2F921;Lo;0;L;7235;;;;N;;;;;
+2F922;CJK COMPATIBILITY IDEOGRAPH-2F922;Lo;0;L;7250;;;;N;;;;;
+2F923;CJK COMPATIBILITY IDEOGRAPH-2F923;Lo;0;L;24608;;;;N;;;;;
+2F924;CJK COMPATIBILITY IDEOGRAPH-2F924;Lo;0;L;7280;;;;N;;;;;
+2F925;CJK COMPATIBILITY IDEOGRAPH-2F925;Lo;0;L;7295;;;;N;;;;;
+2F926;CJK COMPATIBILITY IDEOGRAPH-2F926;Lo;0;L;24735;;;;N;;;;;
+2F927;CJK COMPATIBILITY IDEOGRAPH-2F927;Lo;0;L;24814;;;;N;;;;;
+2F928;CJK COMPATIBILITY IDEOGRAPH-2F928;Lo;0;L;737A;;;;N;;;;;
+2F929;CJK COMPATIBILITY IDEOGRAPH-2F929;Lo;0;L;738B;;;;N;;;;;
+2F92A;CJK COMPATIBILITY IDEOGRAPH-2F92A;Lo;0;L;3EAC;;;;N;;;;;
+2F92B;CJK COMPATIBILITY IDEOGRAPH-2F92B;Lo;0;L;73A5;;;;N;;;;;
+2F92C;CJK COMPATIBILITY IDEOGRAPH-2F92C;Lo;0;L;3EB8;;;;N;;;;;
+2F92D;CJK COMPATIBILITY IDEOGRAPH-2F92D;Lo;0;L;3EB8;;;;N;;;;;
+2F92E;CJK COMPATIBILITY IDEOGRAPH-2F92E;Lo;0;L;7447;;;;N;;;;;
+2F92F;CJK COMPATIBILITY IDEOGRAPH-2F92F;Lo;0;L;745C;;;;N;;;;;
+2F930;CJK COMPATIBILITY IDEOGRAPH-2F930;Lo;0;L;7471;;;;N;;;;;
+2F931;CJK COMPATIBILITY IDEOGRAPH-2F931;Lo;0;L;7485;;;;N;;;;;
+2F932;CJK COMPATIBILITY IDEOGRAPH-2F932;Lo;0;L;74CA;;;;N;;;;;
+2F933;CJK COMPATIBILITY IDEOGRAPH-2F933;Lo;0;L;3F1B;;;;N;;;;;
+2F934;CJK COMPATIBILITY IDEOGRAPH-2F934;Lo;0;L;7524;;;;N;;;;;
+2F935;CJK COMPATIBILITY IDEOGRAPH-2F935;Lo;0;L;24C36;;;;N;;;;;
+2F936;CJK COMPATIBILITY IDEOGRAPH-2F936;Lo;0;L;753E;;;;N;;;;;
+2F937;CJK COMPATIBILITY IDEOGRAPH-2F937;Lo;0;L;24C92;;;;N;;;;;
+2F938;CJK COMPATIBILITY IDEOGRAPH-2F938;Lo;0;L;7570;;;;N;;;;;
+2F939;CJK COMPATIBILITY IDEOGRAPH-2F939;Lo;0;L;2219F;;;;N;;;;;
+2F93A;CJK COMPATIBILITY IDEOGRAPH-2F93A;Lo;0;L;7610;;;;N;;;;;
+2F93B;CJK COMPATIBILITY IDEOGRAPH-2F93B;Lo;0;L;24FA1;;;;N;;;;;
+2F93C;CJK COMPATIBILITY IDEOGRAPH-2F93C;Lo;0;L;24FB8;;;;N;;;;;
+2F93D;CJK COMPATIBILITY IDEOGRAPH-2F93D;Lo;0;L;25044;;;;N;;;;;
+2F93E;CJK COMPATIBILITY IDEOGRAPH-2F93E;Lo;0;L;3FFC;;;;N;;;;;
+2F93F;CJK COMPATIBILITY IDEOGRAPH-2F93F;Lo;0;L;4008;;;;N;;;;;
+2F940;CJK COMPATIBILITY IDEOGRAPH-2F940;Lo;0;L;76F4;;;;N;;;;;
+2F941;CJK COMPATIBILITY IDEOGRAPH-2F941;Lo;0;L;250F3;;;;N;;;;;
+2F942;CJK COMPATIBILITY IDEOGRAPH-2F942;Lo;0;L;250F2;;;;N;;;;;
+2F943;CJK COMPATIBILITY IDEOGRAPH-2F943;Lo;0;L;25119;;;;N;;;;;
+2F944;CJK COMPATIBILITY IDEOGRAPH-2F944;Lo;0;L;25133;;;;N;;;;;
+2F945;CJK COMPATIBILITY IDEOGRAPH-2F945;Lo;0;L;771E;;;;N;;;;;
+2F946;CJK COMPATIBILITY IDEOGRAPH-2F946;Lo;0;L;771F;;;;N;;;;;
+2F947;CJK COMPATIBILITY IDEOGRAPH-2F947;Lo;0;L;771F;;;;N;;;;;
+2F948;CJK COMPATIBILITY IDEOGRAPH-2F948;Lo;0;L;774A;;;;N;;;;;
+2F949;CJK COMPATIBILITY IDEOGRAPH-2F949;Lo;0;L;4039;;;;N;;;;;
+2F94A;CJK COMPATIBILITY IDEOGRAPH-2F94A;Lo;0;L;778B;;;;N;;;;;
+2F94B;CJK COMPATIBILITY IDEOGRAPH-2F94B;Lo;0;L;4046;;;;N;;;;;
+2F94C;CJK COMPATIBILITY IDEOGRAPH-2F94C;Lo;0;L;4096;;;;N;;;;;
+2F94D;CJK COMPATIBILITY IDEOGRAPH-2F94D;Lo;0;L;2541D;;;;N;;;;;
+2F94E;CJK COMPATIBILITY IDEOGRAPH-2F94E;Lo;0;L;784E;;;;N;;;;;
+2F94F;CJK COMPATIBILITY IDEOGRAPH-2F94F;Lo;0;L;788C;;;;N;;;;;
+2F950;CJK COMPATIBILITY IDEOGRAPH-2F950;Lo;0;L;78CC;;;;N;;;;;
+2F951;CJK COMPATIBILITY IDEOGRAPH-2F951;Lo;0;L;40E3;;;;N;;;;;
+2F952;CJK COMPATIBILITY IDEOGRAPH-2F952;Lo;0;L;25626;;;;N;;;;;
+2F953;CJK COMPATIBILITY IDEOGRAPH-2F953;Lo;0;L;7956;;;;N;;;;;
+2F954;CJK COMPATIBILITY IDEOGRAPH-2F954;Lo;0;L;2569A;;;;N;;;;;
+2F955;CJK COMPATIBILITY IDEOGRAPH-2F955;Lo;0;L;256C5;;;;N;;;;;
+2F956;CJK COMPATIBILITY IDEOGRAPH-2F956;Lo;0;L;798F;;;;N;;;;;
+2F957;CJK COMPATIBILITY IDEOGRAPH-2F957;Lo;0;L;79EB;;;;N;;;;;
+2F958;CJK COMPATIBILITY IDEOGRAPH-2F958;Lo;0;L;412F;;;;N;;;;;
+2F959;CJK COMPATIBILITY IDEOGRAPH-2F959;Lo;0;L;7A40;;;;N;;;;;
+2F95A;CJK COMPATIBILITY IDEOGRAPH-2F95A;Lo;0;L;7A4A;;;;N;;;;;
+2F95B;CJK COMPATIBILITY IDEOGRAPH-2F95B;Lo;0;L;7A4F;;;;N;;;;;
+2F95C;CJK COMPATIBILITY IDEOGRAPH-2F95C;Lo;0;L;2597C;;;;N;;;;;
+2F95D;CJK COMPATIBILITY IDEOGRAPH-2F95D;Lo;0;L;25AA7;;;;N;;;;;
+2F95E;CJK COMPATIBILITY IDEOGRAPH-2F95E;Lo;0;L;25AA7;;;;N;;;;;
+2F95F;CJK COMPATIBILITY IDEOGRAPH-2F95F;Lo;0;L;7AAE;;;;N;;;;;
+2F960;CJK COMPATIBILITY IDEOGRAPH-2F960;Lo;0;L;4202;;;;N;;;;;
+2F961;CJK COMPATIBILITY IDEOGRAPH-2F961;Lo;0;L;25BAB;;;;N;;;;;
+2F962;CJK COMPATIBILITY IDEOGRAPH-2F962;Lo;0;L;7BC6;;;;N;;;;;
+2F963;CJK COMPATIBILITY IDEOGRAPH-2F963;Lo;0;L;7BC9;;;;N;;;;;
+2F964;CJK COMPATIBILITY IDEOGRAPH-2F964;Lo;0;L;4227;;;;N;;;;;
+2F965;CJK COMPATIBILITY IDEOGRAPH-2F965;Lo;0;L;25C80;;;;N;;;;;
+2F966;CJK COMPATIBILITY IDEOGRAPH-2F966;Lo;0;L;7CD2;;;;N;;;;;
+2F967;CJK COMPATIBILITY IDEOGRAPH-2F967;Lo;0;L;42A0;;;;N;;;;;
+2F968;CJK COMPATIBILITY IDEOGRAPH-2F968;Lo;0;L;7CE8;;;;N;;;;;
+2F969;CJK COMPATIBILITY IDEOGRAPH-2F969;Lo;0;L;7CE3;;;;N;;;;;
+2F96A;CJK COMPATIBILITY IDEOGRAPH-2F96A;Lo;0;L;7D00;;;;N;;;;;
+2F96B;CJK COMPATIBILITY IDEOGRAPH-2F96B;Lo;0;L;25F86;;;;N;;;;;
+2F96C;CJK COMPATIBILITY IDEOGRAPH-2F96C;Lo;0;L;7D63;;;;N;;;;;
+2F96D;CJK COMPATIBILITY IDEOGRAPH-2F96D;Lo;0;L;4301;;;;N;;;;;
+2F96E;CJK COMPATIBILITY IDEOGRAPH-2F96E;Lo;0;L;7DC7;;;;N;;;;;
+2F96F;CJK COMPATIBILITY IDEOGRAPH-2F96F;Lo;0;L;7E02;;;;N;;;;;
+2F970;CJK COMPATIBILITY IDEOGRAPH-2F970;Lo;0;L;7E45;;;;N;;;;;
+2F971;CJK COMPATIBILITY IDEOGRAPH-2F971;Lo;0;L;4334;;;;N;;;;;
+2F972;CJK COMPATIBILITY IDEOGRAPH-2F972;Lo;0;L;26228;;;;N;;;;;
+2F973;CJK COMPATIBILITY IDEOGRAPH-2F973;Lo;0;L;26247;;;;N;;;;;
+2F974;CJK COMPATIBILITY IDEOGRAPH-2F974;Lo;0;L;4359;;;;N;;;;;
+2F975;CJK COMPATIBILITY IDEOGRAPH-2F975;Lo;0;L;262D9;;;;N;;;;;
+2F976;CJK COMPATIBILITY IDEOGRAPH-2F976;Lo;0;L;7F7A;;;;N;;;;;
+2F977;CJK COMPATIBILITY IDEOGRAPH-2F977;Lo;0;L;2633E;;;;N;;;;;
+2F978;CJK COMPATIBILITY IDEOGRAPH-2F978;Lo;0;L;7F95;;;;N;;;;;
+2F979;CJK COMPATIBILITY IDEOGRAPH-2F979;Lo;0;L;7FFA;;;;N;;;;;
+2F97A;CJK COMPATIBILITY IDEOGRAPH-2F97A;Lo;0;L;8005;;;;N;;;;;
+2F97B;CJK COMPATIBILITY IDEOGRAPH-2F97B;Lo;0;L;264DA;;;;N;;;;;
+2F97C;CJK COMPATIBILITY IDEOGRAPH-2F97C;Lo;0;L;26523;;;;N;;;;;
+2F97D;CJK COMPATIBILITY IDEOGRAPH-2F97D;Lo;0;L;8060;;;;N;;;;;
+2F97E;CJK COMPATIBILITY IDEOGRAPH-2F97E;Lo;0;L;265A8;;;;N;;;;;
+2F97F;CJK COMPATIBILITY IDEOGRAPH-2F97F;Lo;0;L;8070;;;;N;;;;;
+2F980;CJK COMPATIBILITY IDEOGRAPH-2F980;Lo;0;L;2335F;;;;N;;;;;
+2F981;CJK COMPATIBILITY IDEOGRAPH-2F981;Lo;0;L;43D5;;;;N;;;;;
+2F982;CJK COMPATIBILITY IDEOGRAPH-2F982;Lo;0;L;80B2;;;;N;;;;;
+2F983;CJK COMPATIBILITY IDEOGRAPH-2F983;Lo;0;L;8103;;;;N;;;;;
+2F984;CJK COMPATIBILITY IDEOGRAPH-2F984;Lo;0;L;440B;;;;N;;;;;
+2F985;CJK COMPATIBILITY IDEOGRAPH-2F985;Lo;0;L;813E;;;;N;;;;;
+2F986;CJK COMPATIBILITY IDEOGRAPH-2F986;Lo;0;L;5AB5;;;;N;;;;;
+2F987;CJK COMPATIBILITY IDEOGRAPH-2F987;Lo;0;L;267A7;;;;N;;;;;
+2F988;CJK COMPATIBILITY IDEOGRAPH-2F988;Lo;0;L;267B5;;;;N;;;;;
+2F989;CJK COMPATIBILITY IDEOGRAPH-2F989;Lo;0;L;23393;;;;N;;;;;
+2F98A;CJK COMPATIBILITY IDEOGRAPH-2F98A;Lo;0;L;2339C;;;;N;;;;;
+2F98B;CJK COMPATIBILITY IDEOGRAPH-2F98B;Lo;0;L;8201;;;;N;;;;;
+2F98C;CJK COMPATIBILITY IDEOGRAPH-2F98C;Lo;0;L;8204;;;;N;;;;;
+2F98D;CJK COMPATIBILITY IDEOGRAPH-2F98D;Lo;0;L;8F9E;;;;N;;;;;
+2F98E;CJK COMPATIBILITY IDEOGRAPH-2F98E;Lo;0;L;446B;;;;N;;;;;
+2F98F;CJK COMPATIBILITY IDEOGRAPH-2F98F;Lo;0;L;8291;;;;N;;;;;
+2F990;CJK COMPATIBILITY IDEOGRAPH-2F990;Lo;0;L;828B;;;;N;;;;;
+2F991;CJK COMPATIBILITY IDEOGRAPH-2F991;Lo;0;L;829D;;;;N;;;;;
+2F992;CJK COMPATIBILITY IDEOGRAPH-2F992;Lo;0;L;52B3;;;;N;;;;;
+2F993;CJK COMPATIBILITY IDEOGRAPH-2F993;Lo;0;L;82B1;;;;N;;;;;
+2F994;CJK COMPATIBILITY IDEOGRAPH-2F994;Lo;0;L;82B3;;;;N;;;;;
+2F995;CJK COMPATIBILITY IDEOGRAPH-2F995;Lo;0;L;82BD;;;;N;;;;;
+2F996;CJK COMPATIBILITY IDEOGRAPH-2F996;Lo;0;L;82E6;;;;N;;;;;
+2F997;CJK COMPATIBILITY IDEOGRAPH-2F997;Lo;0;L;26B3C;;;;N;;;;;
+2F998;CJK COMPATIBILITY IDEOGRAPH-2F998;Lo;0;L;82E5;;;;N;;;;;
+2F999;CJK COMPATIBILITY IDEOGRAPH-2F999;Lo;0;L;831D;;;;N;;;;;
+2F99A;CJK COMPATIBILITY IDEOGRAPH-2F99A;Lo;0;L;8363;;;;N;;;;;
+2F99B;CJK COMPATIBILITY IDEOGRAPH-2F99B;Lo;0;L;83AD;;;;N;;;;;
+2F99C;CJK COMPATIBILITY IDEOGRAPH-2F99C;Lo;0;L;8323;;;;N;;;;;
+2F99D;CJK COMPATIBILITY IDEOGRAPH-2F99D;Lo;0;L;83BD;;;;N;;;;;
+2F99E;CJK COMPATIBILITY IDEOGRAPH-2F99E;Lo;0;L;83E7;;;;N;;;;;
+2F99F;CJK COMPATIBILITY IDEOGRAPH-2F99F;Lo;0;L;8457;;;;N;;;;;
+2F9A0;CJK COMPATIBILITY IDEOGRAPH-2F9A0;Lo;0;L;8353;;;;N;;;;;
+2F9A1;CJK COMPATIBILITY IDEOGRAPH-2F9A1;Lo;0;L;83CA;;;;N;;;;;
+2F9A2;CJK COMPATIBILITY IDEOGRAPH-2F9A2;Lo;0;L;83CC;;;;N;;;;;
+2F9A3;CJK COMPATIBILITY IDEOGRAPH-2F9A3;Lo;0;L;83DC;;;;N;;;;;
+2F9A4;CJK COMPATIBILITY IDEOGRAPH-2F9A4;Lo;0;L;26C36;;;;N;;;;;
+2F9A5;CJK COMPATIBILITY IDEOGRAPH-2F9A5;Lo;0;L;26D6B;;;;N;;;;;
+2F9A6;CJK COMPATIBILITY IDEOGRAPH-2F9A6;Lo;0;L;26CD5;;;;N;;;;;
+2F9A7;CJK COMPATIBILITY IDEOGRAPH-2F9A7;Lo;0;L;452B;;;;N;;;;;
+2F9A8;CJK COMPATIBILITY IDEOGRAPH-2F9A8;Lo;0;L;84F1;;;;N;;;;;
+2F9A9;CJK COMPATIBILITY IDEOGRAPH-2F9A9;Lo;0;L;84F3;;;;N;;;;;
+2F9AA;CJK COMPATIBILITY IDEOGRAPH-2F9AA;Lo;0;L;8516;;;;N;;;;;
+2F9AB;CJK COMPATIBILITY IDEOGRAPH-2F9AB;Lo;0;L;273CA;;;;N;;;;;
+2F9AC;CJK COMPATIBILITY IDEOGRAPH-2F9AC;Lo;0;L;8564;;;;N;;;;;
+2F9AD;CJK COMPATIBILITY IDEOGRAPH-2F9AD;Lo;0;L;26F2C;;;;N;;;;;
+2F9AE;CJK COMPATIBILITY IDEOGRAPH-2F9AE;Lo;0;L;455D;;;;N;;;;;
+2F9AF;CJK COMPATIBILITY IDEOGRAPH-2F9AF;Lo;0;L;4561;;;;N;;;;;
+2F9B0;CJK COMPATIBILITY IDEOGRAPH-2F9B0;Lo;0;L;26FB1;;;;N;;;;;
+2F9B1;CJK COMPATIBILITY IDEOGRAPH-2F9B1;Lo;0;L;270D2;;;;N;;;;;
+2F9B2;CJK COMPATIBILITY IDEOGRAPH-2F9B2;Lo;0;L;456B;;;;N;;;;;
+2F9B3;CJK COMPATIBILITY IDEOGRAPH-2F9B3;Lo;0;L;8650;;;;N;;;;;
+2F9B4;CJK COMPATIBILITY IDEOGRAPH-2F9B4;Lo;0;L;865C;;;;N;;;;;
+2F9B5;CJK COMPATIBILITY IDEOGRAPH-2F9B5;Lo;0;L;8667;;;;N;;;;;
+2F9B6;CJK COMPATIBILITY IDEOGRAPH-2F9B6;Lo;0;L;8669;;;;N;;;;;
+2F9B7;CJK COMPATIBILITY IDEOGRAPH-2F9B7;Lo;0;L;86A9;;;;N;;;;;
+2F9B8;CJK COMPATIBILITY IDEOGRAPH-2F9B8;Lo;0;L;8688;;;;N;;;;;
+2F9B9;CJK COMPATIBILITY IDEOGRAPH-2F9B9;Lo;0;L;870E;;;;N;;;;;
+2F9BA;CJK COMPATIBILITY IDEOGRAPH-2F9BA;Lo;0;L;86E2;;;;N;;;;;
+2F9BB;CJK COMPATIBILITY IDEOGRAPH-2F9BB;Lo;0;L;8779;;;;N;;;;;
+2F9BC;CJK COMPATIBILITY IDEOGRAPH-2F9BC;Lo;0;L;8728;;;;N;;;;;
+2F9BD;CJK COMPATIBILITY IDEOGRAPH-2F9BD;Lo;0;L;876B;;;;N;;;;;
+2F9BE;CJK COMPATIBILITY IDEOGRAPH-2F9BE;Lo;0;L;8786;;;;N;;;;;
+2F9BF;CJK COMPATIBILITY IDEOGRAPH-2F9BF;Lo;0;L;4D57;;;;N;;;;;
+2F9C0;CJK COMPATIBILITY IDEOGRAPH-2F9C0;Lo;0;L;87E1;;;;N;;;;;
+2F9C1;CJK COMPATIBILITY IDEOGRAPH-2F9C1;Lo;0;L;8801;;;;N;;;;;
+2F9C2;CJK COMPATIBILITY IDEOGRAPH-2F9C2;Lo;0;L;45F9;;;;N;;;;;
+2F9C3;CJK COMPATIBILITY IDEOGRAPH-2F9C3;Lo;0;L;8860;;;;N;;;;;
+2F9C4;CJK COMPATIBILITY IDEOGRAPH-2F9C4;Lo;0;L;8863;;;;N;;;;;
+2F9C5;CJK COMPATIBILITY IDEOGRAPH-2F9C5;Lo;0;L;27667;;;;N;;;;;
+2F9C6;CJK COMPATIBILITY IDEOGRAPH-2F9C6;Lo;0;L;88D7;;;;N;;;;;
+2F9C7;CJK COMPATIBILITY IDEOGRAPH-2F9C7;Lo;0;L;88DE;;;;N;;;;;
+2F9C8;CJK COMPATIBILITY IDEOGRAPH-2F9C8;Lo;0;L;4635;;;;N;;;;;
+2F9C9;CJK COMPATIBILITY IDEOGRAPH-2F9C9;Lo;0;L;88FA;;;;N;;;;;
+2F9CA;CJK COMPATIBILITY IDEOGRAPH-2F9CA;Lo;0;L;34BB;;;;N;;;;;
+2F9CB;CJK COMPATIBILITY IDEOGRAPH-2F9CB;Lo;0;L;278AE;;;;N;;;;;
+2F9CC;CJK COMPATIBILITY IDEOGRAPH-2F9CC;Lo;0;L;27966;;;;N;;;;;
+2F9CD;CJK COMPATIBILITY IDEOGRAPH-2F9CD;Lo;0;L;46BE;;;;N;;;;;
+2F9CE;CJK COMPATIBILITY IDEOGRAPH-2F9CE;Lo;0;L;46C7;;;;N;;;;;
+2F9CF;CJK COMPATIBILITY IDEOGRAPH-2F9CF;Lo;0;L;8AA0;;;;N;;;;;
+2F9D0;CJK COMPATIBILITY IDEOGRAPH-2F9D0;Lo;0;L;8AED;;;;N;;;;;
+2F9D1;CJK COMPATIBILITY IDEOGRAPH-2F9D1;Lo;0;L;8B8A;;;;N;;;;;
+2F9D2;CJK COMPATIBILITY IDEOGRAPH-2F9D2;Lo;0;L;8C55;;;;N;;;;;
+2F9D3;CJK COMPATIBILITY IDEOGRAPH-2F9D3;Lo;0;L;27CA8;;;;N;;;;;
+2F9D4;CJK COMPATIBILITY IDEOGRAPH-2F9D4;Lo;0;L;8CAB;;;;N;;;;;
+2F9D5;CJK COMPATIBILITY IDEOGRAPH-2F9D5;Lo;0;L;8CC1;;;;N;;;;;
+2F9D6;CJK COMPATIBILITY IDEOGRAPH-2F9D6;Lo;0;L;8D1B;;;;N;;;;;
+2F9D7;CJK COMPATIBILITY IDEOGRAPH-2F9D7;Lo;0;L;8D77;;;;N;;;;;
+2F9D8;CJK COMPATIBILITY IDEOGRAPH-2F9D8;Lo;0;L;27F2F;;;;N;;;;;
+2F9D9;CJK COMPATIBILITY IDEOGRAPH-2F9D9;Lo;0;L;20804;;;;N;;;;;
+2F9DA;CJK COMPATIBILITY IDEOGRAPH-2F9DA;Lo;0;L;8DCB;;;;N;;;;;
+2F9DB;CJK COMPATIBILITY IDEOGRAPH-2F9DB;Lo;0;L;8DBC;;;;N;;;;;
+2F9DC;CJK COMPATIBILITY IDEOGRAPH-2F9DC;Lo;0;L;8DF0;;;;N;;;;;
+2F9DD;CJK COMPATIBILITY IDEOGRAPH-2F9DD;Lo;0;L;208DE;;;;N;;;;;
+2F9DE;CJK COMPATIBILITY IDEOGRAPH-2F9DE;Lo;0;L;8ED4;;;;N;;;;;
+2F9DF;CJK COMPATIBILITY IDEOGRAPH-2F9DF;Lo;0;L;8F38;;;;N;;;;;
+2F9E0;CJK COMPATIBILITY IDEOGRAPH-2F9E0;Lo;0;L;285D2;;;;N;;;;;
+2F9E1;CJK COMPATIBILITY IDEOGRAPH-2F9E1;Lo;0;L;285ED;;;;N;;;;;
+2F9E2;CJK COMPATIBILITY IDEOGRAPH-2F9E2;Lo;0;L;9094;;;;N;;;;;
+2F9E3;CJK COMPATIBILITY IDEOGRAPH-2F9E3;Lo;0;L;90F1;;;;N;;;;;
+2F9E4;CJK COMPATIBILITY IDEOGRAPH-2F9E4;Lo;0;L;9111;;;;N;;;;;
+2F9E5;CJK COMPATIBILITY IDEOGRAPH-2F9E5;Lo;0;L;2872E;;;;N;;;;;
+2F9E6;CJK COMPATIBILITY IDEOGRAPH-2F9E6;Lo;0;L;911B;;;;N;;;;;
+2F9E7;CJK COMPATIBILITY IDEOGRAPH-2F9E7;Lo;0;L;9238;;;;N;;;;;
+2F9E8;CJK COMPATIBILITY IDEOGRAPH-2F9E8;Lo;0;L;92D7;;;;N;;;;;
+2F9E9;CJK COMPATIBILITY IDEOGRAPH-2F9E9;Lo;0;L;92D8;;;;N;;;;;
+2F9EA;CJK COMPATIBILITY IDEOGRAPH-2F9EA;Lo;0;L;927C;;;;N;;;;;
+2F9EB;CJK COMPATIBILITY IDEOGRAPH-2F9EB;Lo;0;L;93F9;;;;N;;;;;
+2F9EC;CJK COMPATIBILITY IDEOGRAPH-2F9EC;Lo;0;L;9415;;;;N;;;;;
+2F9ED;CJK COMPATIBILITY IDEOGRAPH-2F9ED;Lo;0;L;28BFA;;;;N;;;;;
+2F9EE;CJK COMPATIBILITY IDEOGRAPH-2F9EE;Lo;0;L;958B;;;;N;;;;;
+2F9EF;CJK COMPATIBILITY IDEOGRAPH-2F9EF;Lo;0;L;4995;;;;N;;;;;
+2F9F0;CJK COMPATIBILITY IDEOGRAPH-2F9F0;Lo;0;L;95B7;;;;N;;;;;
+2F9F1;CJK COMPATIBILITY IDEOGRAPH-2F9F1;Lo;0;L;28D77;;;;N;;;;;
+2F9F2;CJK COMPATIBILITY IDEOGRAPH-2F9F2;Lo;0;L;49E6;;;;N;;;;;
+2F9F3;CJK COMPATIBILITY IDEOGRAPH-2F9F3;Lo;0;L;96C3;;;;N;;;;;
+2F9F4;CJK COMPATIBILITY IDEOGRAPH-2F9F4;Lo;0;L;5DB2;;;;N;;;;;
+2F9F5;CJK COMPATIBILITY IDEOGRAPH-2F9F5;Lo;0;L;9723;;;;N;;;;;
+2F9F6;CJK COMPATIBILITY IDEOGRAPH-2F9F6;Lo;0;L;29145;;;;N;;;;;
+2F9F7;CJK COMPATIBILITY IDEOGRAPH-2F9F7;Lo;0;L;2921A;;;;N;;;;;
+2F9F8;CJK COMPATIBILITY IDEOGRAPH-2F9F8;Lo;0;L;4A6E;;;;N;;;;;
+2F9F9;CJK COMPATIBILITY IDEOGRAPH-2F9F9;Lo;0;L;4A76;;;;N;;;;;
+2F9FA;CJK COMPATIBILITY IDEOGRAPH-2F9FA;Lo;0;L;97E0;;;;N;;;;;
+2F9FB;CJK COMPATIBILITY IDEOGRAPH-2F9FB;Lo;0;L;2940A;;;;N;;;;;
+2F9FC;CJK COMPATIBILITY IDEOGRAPH-2F9FC;Lo;0;L;4AB2;;;;N;;;;;
+2F9FD;CJK COMPATIBILITY IDEOGRAPH-2F9FD;Lo;0;L;29496;;;;N;;;;;
+2F9FE;CJK COMPATIBILITY IDEOGRAPH-2F9FE;Lo;0;L;980B;;;;N;;;;;
+2F9FF;CJK COMPATIBILITY IDEOGRAPH-2F9FF;Lo;0;L;980B;;;;N;;;;;
+2FA00;CJK COMPATIBILITY IDEOGRAPH-2FA00;Lo;0;L;9829;;;;N;;;;;
+2FA01;CJK COMPATIBILITY IDEOGRAPH-2FA01;Lo;0;L;295B6;;;;N;;;;;
+2FA02;CJK COMPATIBILITY IDEOGRAPH-2FA02;Lo;0;L;98E2;;;;N;;;;;
+2FA03;CJK COMPATIBILITY IDEOGRAPH-2FA03;Lo;0;L;4B33;;;;N;;;;;
+2FA04;CJK COMPATIBILITY IDEOGRAPH-2FA04;Lo;0;L;9929;;;;N;;;;;
+2FA05;CJK COMPATIBILITY IDEOGRAPH-2FA05;Lo;0;L;99A7;;;;N;;;;;
+2FA06;CJK COMPATIBILITY IDEOGRAPH-2FA06;Lo;0;L;99C2;;;;N;;;;;
+2FA07;CJK COMPATIBILITY IDEOGRAPH-2FA07;Lo;0;L;99FE;;;;N;;;;;
+2FA08;CJK COMPATIBILITY IDEOGRAPH-2FA08;Lo;0;L;4BCE;;;;N;;;;;
+2FA09;CJK COMPATIBILITY IDEOGRAPH-2FA09;Lo;0;L;29B30;;;;N;;;;;
+2FA0A;CJK COMPATIBILITY IDEOGRAPH-2FA0A;Lo;0;L;9B12;;;;N;;;;;
+2FA0B;CJK COMPATIBILITY IDEOGRAPH-2FA0B;Lo;0;L;9C40;;;;N;;;;;
+2FA0C;CJK COMPATIBILITY IDEOGRAPH-2FA0C;Lo;0;L;9CFD;;;;N;;;;;
+2FA0D;CJK COMPATIBILITY IDEOGRAPH-2FA0D;Lo;0;L;4CCE;;;;N;;;;;
+2FA0E;CJK COMPATIBILITY IDEOGRAPH-2FA0E;Lo;0;L;4CED;;;;N;;;;;
+2FA0F;CJK COMPATIBILITY IDEOGRAPH-2FA0F;Lo;0;L;9D67;;;;N;;;;;
+2FA10;CJK COMPATIBILITY IDEOGRAPH-2FA10;Lo;0;L;2A0CE;;;;N;;;;;
+2FA11;CJK COMPATIBILITY IDEOGRAPH-2FA11;Lo;0;L;4CF8;;;;N;;;;;
+2FA12;CJK COMPATIBILITY IDEOGRAPH-2FA12;Lo;0;L;2A105;;;;N;;;;;
+2FA13;CJK COMPATIBILITY IDEOGRAPH-2FA13;Lo;0;L;2A20E;;;;N;;;;;
+2FA14;CJK COMPATIBILITY IDEOGRAPH-2FA14;Lo;0;L;2A291;;;;N;;;;;
+2FA15;CJK COMPATIBILITY IDEOGRAPH-2FA15;Lo;0;L;9EBB;;;;N;;;;;
+2FA16;CJK COMPATIBILITY IDEOGRAPH-2FA16;Lo;0;L;4D56;;;;N;;;;;
+2FA17;CJK COMPATIBILITY IDEOGRAPH-2FA17;Lo;0;L;9EF9;;;;N;;;;;
+2FA18;CJK COMPATIBILITY IDEOGRAPH-2FA18;Lo;0;L;9EFE;;;;N;;;;;
+2FA19;CJK COMPATIBILITY IDEOGRAPH-2FA19;Lo;0;L;9F05;;;;N;;;;;
+2FA1A;CJK COMPATIBILITY IDEOGRAPH-2FA1A;Lo;0;L;9F0F;;;;N;;;;;
+2FA1B;CJK COMPATIBILITY IDEOGRAPH-2FA1B;Lo;0;L;9F16;;;;N;;;;;
+2FA1C;CJK COMPATIBILITY IDEOGRAPH-2FA1C;Lo;0;L;9F3B;;;;N;;;;;
+2FA1D;CJK COMPATIBILITY IDEOGRAPH-2FA1D;Lo;0;L;2A600;;;;N;;;;;
+E0001;LANGUAGE TAG;Cf;0;BN;;;;;N;;;;;
+E0020;TAG SPACE;Cf;0;BN;;;;;N;;;;;
+E0021;TAG EXCLAMATION MARK;Cf;0;BN;;;;;N;;;;;
+E0022;TAG QUOTATION MARK;Cf;0;BN;;;;;N;;;;;
+E0023;TAG NUMBER SIGN;Cf;0;BN;;;;;N;;;;;
+E0024;TAG DOLLAR SIGN;Cf;0;BN;;;;;N;;;;;
+E0025;TAG PERCENT SIGN;Cf;0;BN;;;;;N;;;;;
+E0026;TAG AMPERSAND;Cf;0;BN;;;;;N;;;;;
+E0027;TAG APOSTROPHE;Cf;0;BN;;;;;N;;;;;
+E0028;TAG LEFT PARENTHESIS;Cf;0;BN;;;;;N;;;;;
+E0029;TAG RIGHT PARENTHESIS;Cf;0;BN;;;;;N;;;;;
+E002A;TAG ASTERISK;Cf;0;BN;;;;;N;;;;;
+E002B;TAG PLUS SIGN;Cf;0;BN;;;;;N;;;;;
+E002C;TAG COMMA;Cf;0;BN;;;;;N;;;;;
+E002D;TAG HYPHEN-MINUS;Cf;0;BN;;;;;N;;;;;
+E002E;TAG FULL STOP;Cf;0;BN;;;;;N;;;;;
+E002F;TAG SOLIDUS;Cf;0;BN;;;;;N;;;;;
+E0030;TAG DIGIT ZERO;Cf;0;BN;;;;;N;;;;;
+E0031;TAG DIGIT ONE;Cf;0;BN;;;;;N;;;;;
+E0032;TAG DIGIT TWO;Cf;0;BN;;;;;N;;;;;
+E0033;TAG DIGIT THREE;Cf;0;BN;;;;;N;;;;;
+E0034;TAG DIGIT FOUR;Cf;0;BN;;;;;N;;;;;
+E0035;TAG DIGIT FIVE;Cf;0;BN;;;;;N;;;;;
+E0036;TAG DIGIT SIX;Cf;0;BN;;;;;N;;;;;
+E0037;TAG DIGIT SEVEN;Cf;0;BN;;;;;N;;;;;
+E0038;TAG DIGIT EIGHT;Cf;0;BN;;;;;N;;;;;
+E0039;TAG DIGIT NINE;Cf;0;BN;;;;;N;;;;;
+E003A;TAG COLON;Cf;0;BN;;;;;N;;;;;
+E003B;TAG SEMICOLON;Cf;0;BN;;;;;N;;;;;
+E003C;TAG LESS-THAN SIGN;Cf;0;BN;;;;;N;;;;;
+E003D;TAG EQUALS SIGN;Cf;0;BN;;;;;N;;;;;
+E003E;TAG GREATER-THAN SIGN;Cf;0;BN;;;;;N;;;;;
+E003F;TAG QUESTION MARK;Cf;0;BN;;;;;N;;;;;
+E0040;TAG COMMERCIAL AT;Cf;0;BN;;;;;N;;;;;
+E0041;TAG LATIN CAPITAL LETTER A;Cf;0;BN;;;;;N;;;;;
+E0042;TAG LATIN CAPITAL LETTER B;Cf;0;BN;;;;;N;;;;;
+E0043;TAG LATIN CAPITAL LETTER C;Cf;0;BN;;;;;N;;;;;
+E0044;TAG LATIN CAPITAL LETTER D;Cf;0;BN;;;;;N;;;;;
+E0045;TAG LATIN CAPITAL LETTER E;Cf;0;BN;;;;;N;;;;;
+E0046;TAG LATIN CAPITAL LETTER F;Cf;0;BN;;;;;N;;;;;
+E0047;TAG LATIN CAPITAL LETTER G;Cf;0;BN;;;;;N;;;;;
+E0048;TAG LATIN CAPITAL LETTER H;Cf;0;BN;;;;;N;;;;;
+E0049;TAG LATIN CAPITAL LETTER I;Cf;0;BN;;;;;N;;;;;
+E004A;TAG LATIN CAPITAL LETTER J;Cf;0;BN;;;;;N;;;;;
+E004B;TAG LATIN CAPITAL LETTER K;Cf;0;BN;;;;;N;;;;;
+E004C;TAG LATIN CAPITAL LETTER L;Cf;0;BN;;;;;N;;;;;
+E004D;TAG LATIN CAPITAL LETTER M;Cf;0;BN;;;;;N;;;;;
+E004E;TAG LATIN CAPITAL LETTER N;Cf;0;BN;;;;;N;;;;;
+E004F;TAG LATIN CAPITAL LETTER O;Cf;0;BN;;;;;N;;;;;
+E0050;TAG LATIN CAPITAL LETTER P;Cf;0;BN;;;;;N;;;;;
+E0051;TAG LATIN CAPITAL LETTER Q;Cf;0;BN;;;;;N;;;;;
+E0052;TAG LATIN CAPITAL LETTER R;Cf;0;BN;;;;;N;;;;;
+E0053;TAG LATIN CAPITAL LETTER S;Cf;0;BN;;;;;N;;;;;
+E0054;TAG LATIN CAPITAL LETTER T;Cf;0;BN;;;;;N;;;;;
+E0055;TAG LATIN CAPITAL LETTER U;Cf;0;BN;;;;;N;;;;;
+E0056;TAG LATIN CAPITAL LETTER V;Cf;0;BN;;;;;N;;;;;
+E0057;TAG LATIN CAPITAL LETTER W;Cf;0;BN;;;;;N;;;;;
+E0058;TAG LATIN CAPITAL LETTER X;Cf;0;BN;;;;;N;;;;;
+E0059;TAG LATIN CAPITAL LETTER Y;Cf;0;BN;;;;;N;;;;;
+E005A;TAG LATIN CAPITAL LETTER Z;Cf;0;BN;;;;;N;;;;;
+E005B;TAG LEFT SQUARE BRACKET;Cf;0;BN;;;;;N;;;;;
+E005C;TAG REVERSE SOLIDUS;Cf;0;BN;;;;;N;;;;;
+E005D;TAG RIGHT SQUARE BRACKET;Cf;0;BN;;;;;N;;;;;
+E005E;TAG CIRCUMFLEX ACCENT;Cf;0;BN;;;;;N;;;;;
+E005F;TAG LOW LINE;Cf;0;BN;;;;;N;;;;;
+E0060;TAG GRAVE ACCENT;Cf;0;BN;;;;;N;;;;;
+E0061;TAG LATIN SMALL LETTER A;Cf;0;BN;;;;;N;;;;;
+E0062;TAG LATIN SMALL LETTER B;Cf;0;BN;;;;;N;;;;;
+E0063;TAG LATIN SMALL LETTER C;Cf;0;BN;;;;;N;;;;;
+E0064;TAG LATIN SMALL LETTER D;Cf;0;BN;;;;;N;;;;;
+E0065;TAG LATIN SMALL LETTER E;Cf;0;BN;;;;;N;;;;;
+E0066;TAG LATIN SMALL LETTER F;Cf;0;BN;;;;;N;;;;;
+E0067;TAG LATIN SMALL LETTER G;Cf;0;BN;;;;;N;;;;;
+E0068;TAG LATIN SMALL LETTER H;Cf;0;BN;;;;;N;;;;;
+E0069;TAG LATIN SMALL LETTER I;Cf;0;BN;;;;;N;;;;;
+E006A;TAG LATIN SMALL LETTER J;Cf;0;BN;;;;;N;;;;;
+E006B;TAG LATIN SMALL LETTER K;Cf;0;BN;;;;;N;;;;;
+E006C;TAG LATIN SMALL LETTER L;Cf;0;BN;;;;;N;;;;;
+E006D;TAG LATIN SMALL LETTER M;Cf;0;BN;;;;;N;;;;;
+E006E;TAG LATIN SMALL LETTER N;Cf;0;BN;;;;;N;;;;;
+E006F;TAG LATIN SMALL LETTER O;Cf;0;BN;;;;;N;;;;;
+E0070;TAG LATIN SMALL LETTER P;Cf;0;BN;;;;;N;;;;;
+E0071;TAG LATIN SMALL LETTER Q;Cf;0;BN;;;;;N;;;;;
+E0072;TAG LATIN SMALL LETTER R;Cf;0;BN;;;;;N;;;;;
+E0073;TAG LATIN SMALL LETTER S;Cf;0;BN;;;;;N;;;;;
+E0074;TAG LATIN SMALL LETTER T;Cf;0;BN;;;;;N;;;;;
+E0075;TAG LATIN SMALL LETTER U;Cf;0;BN;;;;;N;;;;;
+E0076;TAG LATIN SMALL LETTER V;Cf;0;BN;;;;;N;;;;;
+E0077;TAG LATIN SMALL LETTER W;Cf;0;BN;;;;;N;;;;;
+E0078;TAG LATIN SMALL LETTER X;Cf;0;BN;;;;;N;;;;;
+E0079;TAG LATIN SMALL LETTER Y;Cf;0;BN;;;;;N;;;;;
+E007A;TAG LATIN SMALL LETTER Z;Cf;0;BN;;;;;N;;;;;
+E007B;TAG LEFT CURLY BRACKET;Cf;0;BN;;;;;N;;;;;
+E007C;TAG VERTICAL LINE;Cf;0;BN;;;;;N;;;;;
+E007D;TAG RIGHT CURLY BRACKET;Cf;0;BN;;;;;N;;;;;
+E007E;TAG TILDE;Cf;0;BN;;;;;N;;;;;
+E007F;CANCEL TAG;Cf;0;BN;;;;;N;;;;;
+F0000;<Plane 15 Private Use, First>;Co;0;L;;;;;N;;;;;
+FFFFD;<Plane 15 Private Use, Last>;Co;0;L;;;;;N;;;;;
+100000;<Plane 16 Private Use, First>;Co;0;L;;;;;N;;;;;
+10FFFD;<Plane 16 Private Use, Last>;Co;0;L;;;;;N;;;;;
diff --git a/libraries/liblunicode/ucdata/MUTTUCData.txt b/libraries/liblunicode/ucdata/MUTTUCData.txt
new file mode 100644
index 0000000..82c4659
--- /dev/null
+++ b/libraries/liblunicode/ucdata/MUTTUCData.txt
@@ -0,0 +1,303 @@
+#
+# $Id: MUTTUCData.txt,v 1.3 1999/10/29 00:04:35 mleisher Exp $
+#
+# Copyright 1999 Computing Research Labs, New Mexico State University
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+# OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+# THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+#
+# Implementation specific character properties.
+#
+#
+# Space, other.
+#
+0009;;Ss;;;;;;;;;;;;
+000A;;Ss;;;;;;;;;;;;
+000B;;Ss;;;;;;;;;;;;
+000C;;Ss;;;;;;;;;;;;
+000D;;Ss;;;;;;;;;;;;
+#
+# Non-breaking.
+#
+00A0;;Nb;;;;;;;;;;;;
+2007;;Nb;;;;;;;;;;;;
+2011;;Nb;;;;;;;;;;;;
+FEFF;;Nb;;;;;;;;;;;;
+#
+# Symmetric.
+#
+0028;;Sy;;;;;;;;;;;;
+0029;;Sy;;;;;;;;;;;;
+005B;;Sy;;;;;;;;;;;;
+005D;;Sy;;;;;;;;;;;;
+007B;;Sy;;;;;;;;;;;;
+007D;;Sy;;;;;;;;;;;;
+00AB;;Sy;;;;;;;;;;;;
+00BB;;Sy;;;;;;;;;;;;
+0F3A;;Sy;;;;;;;;;;;;
+0F3B;;Sy;;;;;;;;;;;;
+0F3C;;Sy;;;;;;;;;;;;
+0F3D;;Sy;;;;;;;;;;;;
+0F3E;;Sy;;;;;;;;;;;;
+0F3F;;Sy;;;;;;;;;;;;
+2018;;Sy;;;;;;;;;;;;
+2019;;Sy;;;;;;;;;;;;
+201A;;Sy;;;;;;;;;;;;
+201B;;Sy;;;;;;;;;;;;
+201C;;Sy;;;;;;;;;;;;
+201D;;Sy;;;;;;;;;;;;
+201E;;Sy;;;;;;;;;;;;
+201F;;Sy;;;;;;;;;;;;
+2039;;Sy;;;;;;;;;;;;
+203A;;Sy;;;;;;;;;;;;
+2045;;Sy;;;;;;;;;;;;
+2046;;Sy;;;;;;;;;;;;
+207D;;Sy;;;;;;;;;;;;
+207E;;Sy;;;;;;;;;;;;
+208D;;Sy;;;;;;;;;;;;
+208E;;Sy;;;;;;;;;;;;
+2329;;Sy;;;;;;;;;;;;
+232A;;Sy;;;;;;;;;;;;
+3008;;Sy;;;;;;;;;;;;
+3009;;Sy;;;;;;;;;;;;
+300A;;Sy;;;;;;;;;;;;
+300B;;Sy;;;;;;;;;;;;
+300C;;Sy;;;;;;;;;;;;
+300D;;Sy;;;;;;;;;;;;
+300E;;Sy;;;;;;;;;;;;
+300F;;Sy;;;;;;;;;;;;
+3010;;Sy;;;;;;;;;;;;
+3011;;Sy;;;;;;;;;;;;
+3014;;Sy;;;;;;;;;;;;
+3015;;Sy;;;;;;;;;;;;
+3016;;Sy;;;;;;;;;;;;
+3017;;Sy;;;;;;;;;;;;
+3018;;Sy;;;;;;;;;;;;
+3019;;Sy;;;;;;;;;;;;
+301A;;Sy;;;;;;;;;;;;
+301B;;Sy;;;;;;;;;;;;
+301D;;Sy;;;;;;;;;;;;
+301E;;Sy;;;;;;;;;;;;
+301F;;Sy;;;;;;;;;;;;
+FD3E;;Sy;;;;;;;;;;;;
+FD3F;;Sy;;;;;;;;;;;;
+FE35;;Sy;;;;;;;;;;;;
+FE36;;Sy;;;;;;;;;;;;
+FE37;;Sy;;;;;;;;;;;;
+FE38;;Sy;;;;;;;;;;;;
+FE39;;Sy;;;;;;;;;;;;
+FE3A;;Sy;;;;;;;;;;;;
+FE3B;;Sy;;;;;;;;;;;;
+FE3C;;Sy;;;;;;;;;;;;
+FE3D;;Sy;;;;;;;;;;;;
+FE3E;;Sy;;;;;;;;;;;;
+FE3F;;Sy;;;;;;;;;;;;
+FE40;;Sy;;;;;;;;;;;;
+FE41;;Sy;;;;;;;;;;;;
+FE42;;Sy;;;;;;;;;;;;
+FE43;;Sy;;;;;;;;;;;;
+FE44;;Sy;;;;;;;;;;;;
+FE59;;Sy;;;;;;;;;;;;
+FE5A;;Sy;;;;;;;;;;;;
+FE5B;;Sy;;;;;;;;;;;;
+FE5C;;Sy;;;;;;;;;;;;
+FE5D;;Sy;;;;;;;;;;;;
+FE5E;;Sy;;;;;;;;;;;;
+FF08;;Sy;;;;;;;;;;;;
+FF09;;Sy;;;;;;;;;;;;
+FF3B;;Sy;;;;;;;;;;;;
+FF3D;;Sy;;;;;;;;;;;;
+FF5B;;Sy;;;;;;;;;;;;
+FF5D;;Sy;;;;;;;;;;;;
+FF62;;Sy;;;;;;;;;;;;
+FF63;;Sy;;;;;;;;;;;;
+#
+# Hex digit.
+#
+0030;;Hd;;;;;;;;;;;;
+0031;;Hd;;;;;;;;;;;;
+0032;;Hd;;;;;;;;;;;;
+0033;;Hd;;;;;;;;;;;;
+0034;;Hd;;;;;;;;;;;;
+0035;;Hd;;;;;;;;;;;;
+0036;;Hd;;;;;;;;;;;;
+0037;;Hd;;;;;;;;;;;;
+0038;;Hd;;;;;;;;;;;;
+0039;;Hd;;;;;;;;;;;;
+0041;;Hd;;;;;;;;;;;;
+0042;;Hd;;;;;;;;;;;;
+0043;;Hd;;;;;;;;;;;;
+0044;;Hd;;;;;;;;;;;;
+0045;;Hd;;;;;;;;;;;;
+0046;;Hd;;;;;;;;;;;;
+0061;;Hd;;;;;;;;;;;;
+0062;;Hd;;;;;;;;;;;;
+0063;;Hd;;;;;;;;;;;;
+0064;;Hd;;;;;;;;;;;;
+0065;;Hd;;;;;;;;;;;;
+0066;;Hd;;;;;;;;;;;;
+FF10;;Hd;;;;;;;;;;;;
+FF11;;Hd;;;;;;;;;;;;
+FF12;;Hd;;;;;;;;;;;;
+FF13;;Hd;;;;;;;;;;;;
+FF14;;Hd;;;;;;;;;;;;
+FF15;;Hd;;;;;;;;;;;;
+FF16;;Hd;;;;;;;;;;;;
+FF17;;Hd;;;;;;;;;;;;
+FF18;;Hd;;;;;;;;;;;;
+FF19;;Hd;;;;;;;;;;;;
+FF21;;Hd;;;;;;;;;;;;
+FF22;;Hd;;;;;;;;;;;;
+FF23;;Hd;;;;;;;;;;;;
+FF24;;Hd;;;;;;;;;;;;
+FF25;;Hd;;;;;;;;;;;;
+FF26;;Hd;;;;;;;;;;;;
+FF41;;Hd;;;;;;;;;;;;
+FF42;;Hd;;;;;;;;;;;;
+FF43;;Hd;;;;;;;;;;;;
+FF44;;Hd;;;;;;;;;;;;
+FF45;;Hd;;;;;;;;;;;;
+FF46;;Hd;;;;;;;;;;;;
+#
+# Quote marks.
+#
+0022;;Qm;;;;;;;;;;;;
+0027;;Qm;;;;;;;;;;;;
+00AB;;Qm;;;;;;;;;;;;
+00BB;;Qm;;;;;;;;;;;;
+2018;;Qm;;;;;;;;;;;;
+2019;;Qm;;;;;;;;;;;;
+201A;;Qm;;;;;;;;;;;;
+201B;;Qm;;;;;;;;;;;;
+201C;;Qm;;;;;;;;;;;;
+201D;;Qm;;;;;;;;;;;;
+201E;;Qm;;;;;;;;;;;;
+201F;;Qm;;;;;;;;;;;;
+2039;;Qm;;;;;;;;;;;;
+203A;;Qm;;;;;;;;;;;;
+300C;;Qm;;;;;;;;;;;;
+300D;;Qm;;;;;;;;;;;;
+300E;;Qm;;;;;;;;;;;;
+300F;;Qm;;;;;;;;;;;;
+301D;;Qm;;;;;;;;;;;;
+301E;;Qm;;;;;;;;;;;;
+301F;;Qm;;;;;;;;;;;;
+FE41;;Qm;;;;;;;;;;;;
+FE42;;Qm;;;;;;;;;;;;
+FE43;;Qm;;;;;;;;;;;;
+FE44;;Qm;;;;;;;;;;;;
+FF02;;Qm;;;;;;;;;;;;
+FF07;;Qm;;;;;;;;;;;;
+FF62;;Qm;;;;;;;;;;;;
+FF63;;Qm;;;;;;;;;;;;
+#
+# Special Devanagari forms
+#
+E900;DEVANAGARI KSHA LIGATURE;Lo;0;L;0915 094D 0937;;;;N;;;;;
+E901;DEVANAGARI GNYA LIGATURE;Lo;0;L;091C 094D 091E;;;;N;;;;;
+E902;DEVANAGARI TTA LIGATURE;Lo;0;L;0924 094D 0924;;;;N;;;;;
+E903;DEVANAGARI TRA LIGATURE;Lo;0;L;0924 094D 0930;;;;N;;;;;
+E904;DEVANAGARI SHCHA LIGATURE;Lo;0;L;0936 094D 091B;;;;N;;;;;
+E905;DEVANAGARI SHRA LIGATURE;Lo;0;L;0936 094D 0930;;;;N;;;;;
+E906;DEVANAGARI SHVA LIGATURE;Lo;0;L;0936 094D 0935;;;;N;;;;;
+E907;DEVANAGARI KRA LIGATURE;Lo;0;L;;;;;N;;;;;
+E908;DEVANAGARI JRA LIGATURE;Lo;0;L;;;;;N;;;;;
+E909;DEVANAGARI ZRA LIGATURE;Lo;0;L;;;;;N;;;;;
+E90A;DEVANAGARI PHRA LIGATURE;Lo;0;L;;;;;N;;;;;
+E90B;DEVANAGARI FRA LIGATURE;Lo;0;L;;;;;N;;;;;
+E90C;DEVANAGARI PRA LIGATURE;Lo;0;L;;;;;N;;;;;
+E90D;DEVANAGARI SRA LIGATURE;Lo;0;L;;;;;N;;;;;
+E90E;DEVANAGARI RU LIGATURE;Lo;0;L;;;;;N;;;;;
+E90F;DEVANAGARI RUU LIGATURE;Lo;0;L;;;;;N;;;;;
+E915;DEVANAGARI HALF LETTER KA;Lo;0;L;;;;;N;;;;;
+E916;DEVANAGARI HALF LETTER KHA;Lo;0;L;;;;;N;;;;;
+E917;DEVANAGARI HALF LETTER GA;Lo;0;L;;;;;N;;;;;
+E918;DEVANAGARI HALF LETTER GHA;Lo;0;L;;;;;N;;;;;
+E919;DEVANAGARI HALF LETTER NGA;Lo;0;L;;;;;N;;;;;
+E91A;DEVANAGARI HALF LETTER CA;Lo;0;L;;;;;N;;;;;
+E91B;DEVANAGARI HALF LETTER CHA;Lo;0;L;;;;;N;;;;;
+E91C;DEVANAGARI HALF LETTER JA;Lo;0;L;;;;;N;;;;;
+E91D;DEVANAGARI HALF LETTER JHA;Lo;0;L;;;;;N;;;;;
+E91E;DEVANAGARI HALF LETTER NYA;Lo;0;L;;;;;N;;;;;
+E91F;DEVANAGARI HALF LETTER TTA;Lo;0;L;;;;;N;;;;;
+E920;DEVANAGARI HALF LETTER TTHA;Lo;0;L;;;;;N;;;;;
+E921;DEVANAGARI HALF LETTER DDA;Lo;0;L;;;;;N;;;;;
+E922;DEVANAGARI HALF LETTER DDHA;Lo;0;L;;;;;N;;;;;
+E923;DEVANAGARI HALF LETTER NNA;Lo;0;L;;;;;N;;;;;
+E924;DEVANAGARI HALF LETTER TA;Lo;0;L;;;;;N;;;;;
+E925;DEVANAGARI HALF LETTER THA;Lo;0;L;;;;;N;;;;;
+E926;DEVANAGARI HALF LETTER DA;Lo;0;L;;;;;N;;;;;
+E927;DEVANAGARI HALF LETTER DHA;Lo;0;L;;;;;N;;;;;
+E928;DEVANAGARI HALF LETTER NA;Lo;0;L;;;;;N;;;;;
+E929;DEVANAGARI HALF LETTER NNNA;Lo;0;L;0928 093C;;;;N;;;;;
+E92A;DEVANAGARI HALF LETTER PA;Lo;0;L;;;;;N;;;;;
+E92B;DEVANAGARI HALF LETTER PHA;Lo;0;L;;;;;N;;;;;
+E92C;DEVANAGARI HALF LETTER BA;Lo;0;L;;;;;N;;;;;
+E92D;DEVANAGARI HALF LETTER BHA;Lo;0;L;;;;;N;;;;;
+E92E;DEVANAGARI HALF LETTER MA;Lo;0;L;;;;;N;;;;;
+E92F;DEVANAGARI HALF LETTER YA;Lo;0;L;;;;;N;;;;;
+E930;DEVANAGARI HALF LETTER RA;Lo;0;L;;;;;N;;;;;
+E931;DEVANAGARI HALF LETTER RRA;Lo;0;L;0930 093C;;;;N;;;;;
+E932;DEVANAGARI HALF LETTER LA;Lo;0;L;;;;;N;;;;;
+E933;DEVANAGARI HALF LETTER LLA;Lo;0;L;;;;;N;;;;;
+E934;DEVANAGARI HALF LETTER LLLA;Lo;0;L;0933 093C;;;;N;;;;;
+E935;DEVANAGARI HALF LETTER VA;Lo;0;L;;;;;N;;;;;
+E936;DEVANAGARI HALF LETTER SHA;Lo;0;L;;;;;N;;;;;
+E937;DEVANAGARI HALF LETTER SSA;Lo;0;L;;;;;N;;;;;
+E938;DEVANAGARI HALF LETTER SA;Lo;0;L;;;;;N;;;;;
+E939;DEVANAGARI HALF LETTER HA;Lo;0;L;;;;;N;;;;;
+E940;DEVANAGARI KKA LIGATURE;Lo;0;L;0915 094D 0915;;;;N;;;;;
+E941;DEVANAGARI KTA LIGATURE;Lo;0;L;0915 094D 0924;;;;N;;;;;
+E942;DEVANAGARI NGKA LIGATURE;Lo;0;L;0919 094D 0915;;;;N;;;;;
+E943;DEVANAGARI NGKHA LIGATURE;Lo;0;L;0919 094D 0916;;;;N;;;;;
+E944;DEVANAGARI NGGA LIGATURE;Lo;0;L;0919 094D 0917;;;;N;;;;;
+E945;DEVANAGARI NGGHA LIGATURE;Lo;0;L;0919 094D 0918;;;;N;;;;;
+E946;DEVANAGARI NYJA LIGATURE;Lo;0;L;091E 094D 091C;;;;N;;;;;
+E947;DEVANAGARI DGHA LIGATURE;Lo;0;L;0926 094D 0918;;;;N;;;;;
+E948;DEVANAGARI DDA LIGATURE;Lo;0;L;0926 094D 0926;;;;N;;;;;
+E949;DEVANAGARI DDHA LIGATURE;Lo;0;L;0926 094D 0927;;;;N;;;;;
+E94A;DEVANAGARI DBA LIGATURE;Lo;0;L;0926 094D 092C;;;;N;;;;;
+E94B;DEVANAGARI DBHA LIGATURE;Lo;0;L;0926 094D 092D;;;;N;;;;;
+E94C;DEVANAGARI DMA LIGATURE;Lo;0;L;0926 094D 092E;;;;N;;;;;
+E94D;DEVANAGARI DYA LIGATURE;Lo;0;L;0926 094D 092F;;;;N;;;;;
+E94E;DEVANAGARI DVA LIGATURE;Lo;0;L;0926 094D 0935;;;;N;;;;;
+E94F;DEVANAGARI TT-TTA LIGATURE;Lo;0;L;091F 094D 091F;;;;N;;;;;
+E950;DEVANAGARI TT-TTHA LIGATURE;Lo;0;L;091F 094D 0920;;;;N;;;;;
+E951;DEVANAGARI TTH-TTHA LIGATURE;Lo;0;L;0920 094D 0920;;;;N;;;;;
+E952;DEVANAGARI DD-GA LIGATURE;Lo;0;L;0921 094D 0917;;;;N;;;;;
+E953;DEVANAGARI DD-DDA LIGATURE;Lo;0;L;0921 094D 0921;;;;N;;;;;
+E954;DEVANAGARI DD-DDHA LIGATURE;Lo;0;L;0921 094D 0922;;;;N;;;;;
+E955;DEVANAGARI NNA LIGATURE;Lo;0;L;0928 094D 0928;;;;N;;;;;
+E956;DEVANAGARI HMA LIGATURE;Lo;0;L;0939 094D 092E;;;;N;;;;;
+E957;DEVANAGARI HYA LIGATURE;Lo;0;L;0939 094D 092F;;;;N;;;;;
+E958;DEVANAGARI HLA LIGATURE;Lo;0;L;0939 094D 0932;;;;N;;;;;
+E959;DEVANAGARI HVA LIGATURE;Lo;0;L;0939 094D 0935;;;;N;;;;;
+E95A;DEVANAGARI STRA LIGATURE;Lo;0;L;0938 094D 0924 094D 0930;;;;N;;;;;
+E970;DEVANAGARI HALF KSHA LIGATURE;Lo;0;L;0915 094D 0937;;;;N;;;;;
+E971;DEVANAGARI HALF GNYA LIGATURE;Lo;0;L;091C 094D 091E;;;;N;;;;;
+E972;DEVANAGARI HALF TTA LIGATURE;Lo;0;L;0924 094D 0924;;;;N;;;;;
+E973;DEVANAGARI HALF TRA LIGATURE;Lo;0;L;0924 094D 0930;;;;N;;;;;
+E974;DEVANAGARI HALF SHCHA LIGATURE;Lo;0;L;0936 094D 091B;;;;N;;;;;
+E975;DEVANAGARI HALF SHRA LIGATURE;Lo;0;L;0936 094D 0930;;;;N;;;;;
+E976;DEVANAGARI HALF SHVA LIGATURE;Lo;0;L;0936 094D 0935;;;;N;;;;;
+E97B;DEVANAGARI SIGN RRA-REPHA;Mn;36;L;;;;;N;;;;;
+E97C;DEVANAGARI HAR LIGATURE;Lo;0;L;0939 0943;;;;N;;;;;
+E97D;DEVANAGARI SIGN EYELASH RA;Lo;0;L;;;;;N;;;;;
+E97E;DEVANAGARI SIGN REPHA;Mn;36;L;;;;;N;;;;;
+E97F;DEVANAGARI SIGN SUBJOINED RA;Mn;36;L;;;;;N;;;;;
diff --git a/libraries/liblunicode/ucdata/README b/libraries/liblunicode/ucdata/README
new file mode 100644
index 0000000..6a02cc1
--- /dev/null
+++ b/libraries/liblunicode/ucdata/README
@@ -0,0 +1,313 @@
+#
+# $Id: README,v 1.33 2001/01/02 18:46:19 mleisher Exp $
+#
+
+ MUTT UCData Package 2.5
+ -----------------------
+
+This is a package that supports ctype-like operations for Unicode UCS-2 text
+(and surrogates), case mapping, decomposition lookup, and provides a
+bidirectional reordering algorithm. To use it, you will need to get the
+latest "UnicodeData-*.txt" (or later) file from the Unicode Web or FTP site.
+
+The character information portion of the package consists of three parts:
+
+ 1. A program called "ucgendat" which generates five data files from the
+ UnicodeData-*.txt file. The files are:
+
+ A. case.dat - the case mappings.
+ B. ctype.dat - the character property tables.
+ C. comp.dat - the character composition pairs.
+ D. decomp.dat - the character decompositions.
+ E. cmbcl.dat - the non-zero combining classes.
+ F. num.dat - the codes representing numbers.
+
+ 2. The "ucdata.[ch]" files which implement the functions needed to
+ check to see if a character matches groups of properties, to map between
+ upper, lower, and title case, to look up the decomposition of a
+ character, look up the combining class of a character, and get the number
+ value of a character.
+
+ 3. The UCData.java class which provides the same API (with minor changes for
+ the numbers) and loads the same binary data files as the C code.
+
+A short reference to the functions available is in the "api.txt" file.
+
+Techie Details
+==============
+
+The "ucgendat" program parses files from the command line which are all in the
+Unicode Character Database (UCDB) format. An additional properties file,
+"MUTTUCData.txt", provides some extra properties for some characters.
+
+The program looks for the two character properties fields (2 and 4), the
+combining class field (3), the decomposition field (5), the numeric value
+field (8), and the case mapping fields (12, 13, and 14). The decompositions
+are recursively expanded before being written out.
+
+The decomposition table contains all the canonical decompositions. This means
+all decompositions that do not have tags such as "<compat>" or "<font>".
+
+The data is almost all stored as unsigned longs (32-bits assumed) and the
+routines that load the data take care of endian swaps when necessary. This
+also means that supplementary characters (>= 0x10000) can be placed in the
+data files the "ucgendat" program parses.
+
+The data is written as external files and broken into six parts so it can be
+selectively updated at runtime if necessary.
+
+The data files currently generated from the "ucgendat" program total about 56K
+in size all together.
+
+The format of the binary data files is documented in the "format.txt" file.
+
+==========================================================================
+
+ The "Pretty Good Bidi Algorithm"
+ --------------------------------
+
+This routine provides an alternative to the Unicode Bidi algorithm. The
+difference is that this version of the PGBA does not handle the explicit
+directional codes (LRE, RLE, LRO, RLO, PDF). It should now produce the same
+results as the Unicode BiDi algorithm for implicit reordering. Included are
+functions for doing cursor motion in both logical and visual order.
+
+This implementation is provided to demonstrate an effective alternate method
+for implicit reordering. To make this useful for an application, it probably
+needs some changes to the memory allocation and deallocation, as well as data
+structure additions for rendering.
+
+Mark Leisher <mleisher@crl.nmsu.edu>
+19 November 1999
+
+-----------------------------------------------------------------------------
+
+CHANGES
+=======
+Version 2.5
+-----------
+1. Changed the number lookup to set the denominator to 1 in cases of digits.
+ This restores functional compatibility with John Cowan's UCType package.
+
+2. Added support for the AL property.
+
+3. Modified load and reload functions to return error codes.
+
+Version 2.4
+-----------
+1. Improved some bidi algorithm documentation in the code.
+
+2. Fixed a code mixup that produced a non-working version.
+
+Version 2.3
+-----------
+1. Fixed a misspelling in the ucpgba.h header file.
+
+2. Fixed a bug which caused trailing weak non-digit sequences to be left out of
+ the reordered string in the bidi algorithm.
+
+3. Fixed a problem with weak sequences containing non-spacing marks in the
+ bidi algorithm.
+
+4. Fixed a problem with text runs of the opposite direction of the string
+ surrounding a weak + neutral text run appearing in the wrong order in the
+ bidi algorithm.
+
+5. Added a default overall direction parameter to the reordering function for
+ cases of strings with no strong directional characters in the bidi
+ algorithm.
+
+6. The bidi API documentation was improved.
+
+7. Added a man page for the bidi API.
+
+Version 2.2
+-----------
+1. Fixed a problem with the bidi algorithm locating directional section
+ boundaries.
+
+2. Fixed a problem with the bidi algorithm starting the reordering correctly.
+
+3. Fixed a problem with the bidi algorithm determining end boundaries for LTR
+ segments.
+
+4. Fixed a problem with the bidi algorithm reordering weak (digits and number
+ separators) segments.
+
+5. Added automatic switching of symmetrically paired characters when
+ reversing RTL segments.
+
+6. Added a missing symmetric character to the extra character properties in
+ MUTTUCData.txt.
+
+7. Added support for doing logical and visual cursor traversal.
+
+Version 2.1
+-----------
+1. Updated the ucgendat program to handle the Unicode 3.0 character database
+ properties. The AL and BM bidi properties gets marked as strong RTL and
+ Other Neutral, the NSM, LRE, RLE, PDF, LRO, and RLO controls all get marked
+ as Other Neutral.
+
+2. Fixed some problems with testing against signed values in the UCData.java
+ code and some minor cleanup.
+
+3. Added the "Pretty Good Bidi Algorithm."
+
+Version 2.0
+-----------
+1. Removed the old Java stuff for a new class that loads directly from the
+ same data files as the C code does.
+
+2. Fixed a problem with choosing the correct field when mapping case.
+
+3. Adjust some search routines to start their search in the correct position.
+
+4. Moved the copyright year to 1999.
+
+Version 1.9
+-----------
+1. Fixed a problem with an incorrect amount of storage being allocated for the
+ combining class nodes.
+
+2. Fixed an invalid initialization in the number code.
+
+3. Changed the Java template file formatting a bit.
+
+4. Added tables and function for getting decompositions in the Java class.
+
+Version 1.8
+-----------
+1. Fixed a problem with adding certain ranges.
+
+2. Added two more macros for testing for identifiers.
+
+3. Tested with the UnicodeData-2.1.5.txt file.
+
+Version 1.7
+-----------
+1. Fixed a problem with looking up decompositions in "ucgendat."
+
+Version 1.6
+-----------
+1. Added two new properties introduced with UnicodeData-2.1.4.txt.
+
+2. Changed the "ucgendat.c" program a little to automatically align the
+ property data on a 4-byte boundary when new properties are added.
+
+3. Changed the "ucgendat.c" programs to only generate canonical
+ decompositions.
+
+4. Added two new macros ucisinitialpunct() and ucisfinalpunct() to check for
+ initial and final punctuation characters.
+
+5. Minor additions and changes to the documentation.
+
+Version 1.5
+-----------
+1. Changed all file open calls to include binary mode with "b" for DOS/WIN
+ platforms.
+
+2. Wrapped the unistd.h include so it won't be included when compiled under
+ Win32.
+
+3. Fixed a bad range check for hex digits in ucgendat.c.
+
+4. Fixed a bad endian swap for combining classes.
+
+5. Added code to make a number table and associated lookup functions.
+ Functions added are ucnumber(), ucdigit(), and ucgetnumber(). The last
+ function is to maintain compatibility with John Cowan's "uctype" package.
+
+Version 1.4
+-----------
+1. Fixed a bug with adding a range.
+
+2. Fixed a bug with inserting a range in order.
+
+3. Fixed incorrectly specified ucisdefined() and ucisundefined() macros.
+
+4. Added the missing unload for the combining class data.
+
+5. Fixed a bad macro placement in ucisweak().
+
+Version 1.3
+-----------
+1. Bug with case mapping calculations fixed.
+
+2. Bug with empty character property entries fixed.
+
+3. Bug with incorrect type in the combining class lookup fixed.
+
+4. Some corrections done to api.txt.
+
+5. Bug in certain character property lookups fixed.
+
+6. Added a character property table that records the defined characters.
+
+7. Replaced ucisunknown() with ucisdefined() and ucisundefined().
+
+Version 1.2
+-----------
+1. Added code to ucgendat to generate a combining class table.
+
+2. Fixed an endian problem with the byte count of decompositions.
+
+3. Fixed some minor problems in the "format.txt" file.
+
+4. Removed some bogus "Ss" values from MUTTUCData.txt file.
+
+5. Added API function to get combining class.
+
+6. Changed the open mode to "rb" so binary data files will be opened correctly
+ on DOS/WIN as well as other platforms.
+
+7. Added the "api.txt" file.
+
+Version 1.1
+-----------
+1. Added ucisxdigit() which I overlooked.
+
+2. Added UC_LT to the ucisalpha() macro which I overlooked.
+
+3. Change uciscntrl() to include UC_CF.
+
+4. Added ucisocntrl() and ucfntcntrl() macros.
+
+5. Added a ucisblank() which I overlooked.
+
+6. Added missing properties to ucissymbol() and ucisnumber().
+
+7. Added ucisgraph() and ucisprint().
+
+8. Changed the "Mr" property to "Sy" to mark this subset of mirroring
+ characters as symmetric to avoid trampling the Unicode/ISO10646 sense of
+ mirroring.
+
+9. Added another property called "Ss" which includes control characters
+ traditionally seen as spaces in the isspace() macro.
+
+10. Added a bunch of macros to be API compatible with John Cowan's package.
+
+ACKNOWLEDGEMENTS
+================
+
+Thanks go to John Cowan <cowan@locke.ccil.org> for pointing out lots of
+missing things and giving me stuff, particularly a bunch of new macros.
+
+Thanks go to Bob Verbrugge <bob_verbrugge@nl.compuware.com> for pointing out
+various bugs.
+
+Thanks go to Christophe Pierret <cpierret@businessobjects.com> for pointing
+out that file modes need to have "b" for DOS/WIN machines, pointing out
+unistd.h is not a Win 32 header, and pointing out a problem with ucisalnum().
+
+Thanks go to Kent Johnson <kent@pondview.mv.com> for finding a bug that caused
+incomplete decompositions to be generated by the "ucgendat" program.
+
+Thanks go to Valeriy E. Ushakov <uwe@ptc.spbu.ru> for spotting an allocation
+error and an initialization error.
+
+Thanks go to Stig Venaas <Stig.Venaas@uninett.no> for providing a patch to
+support return types on load and reload, and for major updates to handle
+canonical composition and decomposition.
diff --git a/libraries/liblunicode/ucdata/api.txt b/libraries/liblunicode/ucdata/api.txt
new file mode 100644
index 0000000..f4be819
--- /dev/null
+++ b/libraries/liblunicode/ucdata/api.txt
@@ -0,0 +1,401 @@
+#
+# $Id: api.txt,v 1.3 2001/01/02 18:46:20 mleisher Exp $
+#
+
+ The MUTT UCData API
+ -------------------
+
+
+####
+NOTE: This library has been customized for use with OpenLDAP. The character
+data tables are hardcoded into the library and the load/unload/reload
+functions are no-ops. Also, the MUTT API claimed to be compatible with
+John Cowan's library but its ucnumber behavior was broken. This has been
+fixed in the OpenLDAP release.
+
+By default, the implementation specific properties in MUTTUCData.txt are
+not incorporated into the OpenLDAP build. You can supply them to ucgendat
+and recreate uctable.h if you need them.
+ -- hyc@openldap.org
+####
+
+
+-----------------------------------------------------------------------------
+
+Macros that combine to select data tables for ucdata_load(), ucdata_unload(),
+and ucdata_reload().
+
+#define UCDATA_CASE 0x01
+#define UCDATA_CTYPE 0x02
+#define UCDATA_DECOMP 0x04
+#define UCDATA_CMBCL 0x08
+#define UCDATA_NUM 0x10
+#define UCDATA_COMP 0x20
+#define UCATA_ALL (UCDATA_CASE|UCDATA_CTYPE|UCDATA_DECOMP|\
+ UCDATA_CMBCL|UCDATA_NUM|UCDATA_COMP)
+-----------------------------------------------------------------------------
+
+void ucdata_load(char *paths, int masks)
+
+ This function initializes the UCData library by locating the data files in
+ one of the colon-separated directories in the `paths' parameter. The data
+ files to be loaded are specified in the `masks' parameter as a bitwise
+ combination of the macros listed above.
+
+ This should be called before using any of the other functions.
+
+ NOTE: the ucdata_setup(char *paths) function is now a macro that expands
+ into this function at compile time.
+
+-----------------------------------------------------------------------------
+
+void ucdata_unload(int masks)
+
+ This function unloads the data tables specified in the `masks' parameter.
+
+ This function should be called when the application is done using the UCData
+ package.
+
+ NOTE: the ucdata_cleanup() function is now a macro that expands into this
+ function at compile time.
+
+-----------------------------------------------------------------------------
+
+void ucdata_reload(char *paths, int masks)
+
+ This function reloads the data files from one of the colon-separated
+ directories in the `paths' parameter. The data files to be reloaded are
+ specified in the `masks' parameter as a bitwise combination of the macros
+ listed above.
+
+ If the data files have already been loaded, they are unloaded before the
+ data files are loaded again.
+
+-----------------------------------------------------------------------------
+
+int ucdecomp(unsigned long code, unsigned long *num, unsigned long **decomp)
+
+ This function determines if a character has a decomposition and returns the
+ decomposition information if it exists.
+
+ If a zero is returned, there is no decomposition. If a non-zero is
+ returned, then the `num' and `decomp' variables are filled in with the
+ appropriate values.
+
+ Example call:
+
+ unsigned long i, num, *decomp;
+
+ if (ucdecomp(0x1d5, &num, &decomp) != 0) {
+ for (i = 0; i < num; i++)
+ printf("0x%08lX,", decomp[i]);
+ putchar('\n');
+ }
+
+int uccanondecomp(const unsigned long *in, int inlen, unsigned long **out,
+ int *outlen)
+
+ This function decomposes an input string and does canonical reordering of
+ the characters at the same time.
+
+ If a -1 is returned, memory allocation was not successful. If a zero is
+ returned, no decomposition occurred. Any other value means the output string
+ contains the fully decomposed string in canonical order.
+
+ If the "outlen" parameter comes back with a value > 0, then the string
+ returned in the "out" parameter needs to be deallocated by the caller.
+
+-----------------------------------------------------------------------------
+
+int ucdecomp_hangul(unsigned long code, unsigned long *num,
+ unsigned long decomp[])
+
+ This function determines if a Hangul syllable has a decomposition and
+ returns the decomposition information.
+
+ An array of at least size 3 should be passed to the function for the
+ decomposition of the syllable.
+
+ If a zero is returned, the character is not a Hangul syllable. If a
+ non-zero is returned, the `num' field will be 2 or 3 and the syllable will
+ be decomposed into the `decomp' array arithmetically.
+
+ Example call:
+
+ unsigned long i, num, decomp[3];
+
+ if (ucdecomp_hangul(0xb1ba, &num, &decomp) != 0) {
+ for (i = 0; i < num; i++)
+ printf("0x%08lX,", decomp[i]);
+ putchar('\n');
+ }
+
+-----------------------------------------------------------------------------
+
+int uccomp(unsigned long ch1, unsigned long ch2, unsigned long *comp)
+
+ This function takes a pair of characters and determines if they combine to
+ form another character.
+
+ If a zero is returned, no composition is formed by the character pair. Any
+ other value indicates the "comp" parameter has a value.
+
+int uccomp_hangul(unsigned long *str, int len)
+
+ This function composes the Hangul Jamo in the string. The composition is
+ done in-place.
+
+ The return value provides the new length of the string. This will be
+ smaller than "len" if compositions occurred.
+
+int uccanoncomp(unsigned long *str, int len)
+
+ This function does a canonical composition of characters in the string.
+
+ The return value is the new length of the string.
+
+-----------------------------------------------------------------------------
+
+struct ucnumber {
+ int numerator;
+ int denominator;
+};
+
+int ucnumber_lookup(unsigned long code, struct ucnumber *num)
+
+ This function determines if the code is a number and fills in the `num'
+ field with the numerator and denominator. If the code happens to be a
+ single digit, the denominator field will be 1.
+
+####
+The original code would set numerator = denominator for regular digits.
+However, the Readme also claimed to be compatible with John Cowan's uctype
+library, but this behavior is both nonsensical and incompatible with the
+Cowan library. As such, it has been fixed here as described above.
+ -- hyc@openldap.org
+####
+
+ If the function returns 0, the code is not a number. Any other return
+ value means the code is a number.
+
+int ucdigit_lookup(unsigned long code, int *digit)
+
+ This function determines if the code is a digit and fills in the `digit'
+ field with the digit value.
+
+ If the function returns 0, the code is not a number. Any other return
+ value means the code is a number.
+
+struct ucnumber ucgetnumber(unsigned long code)
+
+ This is a compatibility function with John Cowan's "uctype" package. It
+ uses ucnumber_lookup().
+
+int ucgetdigit(unsigned long code)
+
+ This is a compatibility function with John Cowan's "uctype" package. It
+ uses ucdigit_lookup().
+
+-----------------------------------------------------------------------------
+
+unsigned long uctoupper(unsigned long code)
+
+ This function returns the code unchanged if it is already upper case or has
+ no upper case equivalent. Otherwise the upper case equivalent is returned.
+
+-----------------------------------------------------------------------------
+
+unsigned long uctolower(unsigned long code)
+
+ This function returns the code unchanged if it is already lower case or has
+ no lower case equivalent. Otherwise the lower case equivalent is returned.
+
+-----------------------------------------------------------------------------
+
+unsigned long uctotitle(unsigned long code)
+
+ This function returns the code unchanged if it is already title case or has
+ no title case equivalent. Otherwise the title case equivalent is returned.
+
+-----------------------------------------------------------------------------
+
+int ucisalpha(unsigned long code)
+int ucisalnum(unsigned long code)
+int ucisdigit(unsigned long code)
+int uciscntrl(unsigned long code)
+int ucisspace(unsigned long code)
+int ucisblank(unsigned long code)
+int ucispunct(unsigned long code)
+int ucisgraph(unsigned long code)
+int ucisprint(unsigned long code)
+int ucisxdigit(unsigned long code)
+
+int ucisupper(unsigned long code)
+int ucislower(unsigned long code)
+int ucistitle(unsigned long code)
+
+ These functions (actually macros) determine if a character has these
+ properties. These behave in a fashion very similar to the venerable ctype
+ package.
+
+-----------------------------------------------------------------------------
+
+int ucisisocntrl(unsigned long code)
+
+ Is the character a C0 control character (< 32) ?
+
+int ucisfmtcntrl(unsigned long code)
+
+ Is the character a format control character?
+
+int ucissymbol(unsigned long code)
+
+ Is the character a symbol?
+
+int ucisnumber(unsigned long code)
+
+ Is the character a number or digit?
+
+int ucisnonspacing(unsigned long code)
+
+ Is the character non-spacing?
+
+int ucisopenpunct(unsigned long code)
+
+ Is the character an open/left punctuation (i.e. '[')
+
+int ucisclosepunct(unsigned long code)
+
+ Is the character an close/right punctuation (i.e. ']')
+
+int ucisinitialpunct(unsigned long code)
+
+ Is the character an initial punctuation (i.e. U+2018 LEFT SINGLE QUOTATION
+ MARK)
+
+int ucisfinalpunct(unsigned long code)
+
+ Is the character a final punctuation (i.e. U+2019 RIGHT SINGLE QUOTATION
+ MARK)
+
+int uciscomposite(unsigned long code)
+
+ Can the character be decomposed into a set of other characters?
+
+int ucisquote(unsigned long code)
+
+ Is the character one of the many quotation marks?
+
+int ucissymmetric(unsigned long code)
+
+ Is the character one that has an opposite form (i.e. <>)
+
+int ucismirroring(unsigned long code)
+
+ Is the character mirroring (superset of symmetric)?
+
+int ucisnonbreaking(unsigned long code)
+
+ Is the character non-breaking (i.e. non-breaking space)?
+
+int ucisrtl(unsigned long code)
+
+ Does the character have strong right-to-left directionality (i.e. Arabic
+ letters)?
+
+int ucisltr(unsigned long code)
+
+ Does the character have strong left-to-right directionality (i.e. Latin
+ letters)?
+
+int ucisstrong(unsigned long code)
+
+ Does the character have strong directionality?
+
+int ucisweak(unsigned long code)
+
+ Does the character have weak directionality (i.e. numbers)?
+
+int ucisneutral(unsigned long code)
+
+ Does the character have neutral directionality (i.e. whitespace)?
+
+int ucisseparator(unsigned long code)
+
+ Is the character a block or segment separator?
+
+int ucislsep(unsigned long code)
+
+ Is the character a line separator?
+
+int ucispsep(unsigned long code)
+
+ Is the character a paragraph separator?
+
+int ucismark(unsigned long code)
+
+ Is the character a mark of some kind?
+
+int ucisnsmark(unsigned long code)
+
+ Is the character a non-spacing mark?
+
+int ucisspmark(unsigned long code)
+
+ Is the character a spacing mark?
+
+int ucismodif(unsigned long code)
+
+ Is the character a modifier letter?
+
+int ucismodifsymbol(unsigned long code)
+
+ Is the character a modifier symbol?
+
+int ucisletnum(unsigned long code)
+
+ Is the character a number represented by a letter?
+
+int ucisconnect(unsigned long code)
+
+ Is the character connecting punctuation?
+
+int ucisdash(unsigned long code)
+
+ Is the character dash punctuation?
+
+int ucismath(unsigned long code)
+
+ Is the character a math character?
+
+int uciscurrency(unsigned long code)
+
+ Is the character a currency character?
+
+int ucisenclosing(unsigned long code)
+
+ Is the character enclosing (i.e. enclosing box)?
+
+int ucisprivate(unsigned long code)
+
+ Is the character from the Private Use Area?
+
+int ucissurrogate(unsigned long code)
+
+ Is the character one of the surrogate codes?
+
+int ucisdefined(unsigned long code)
+
+ Is the character defined (appeared in one of the data files)?
+
+int ucisundefined(unsigned long code)
+
+ Is the character not defined (non-Unicode)?
+
+int ucishan(unsigned long code)
+
+ Is the character a Han ideograph?
+
+int ucishangul(unsigned long code)
+
+ Is the character a pre-composed Hangul syllable?
diff --git a/libraries/liblunicode/ucdata/bidiapi.txt b/libraries/liblunicode/ucdata/bidiapi.txt
new file mode 100644
index 0000000..65be2b9
--- /dev/null
+++ b/libraries/liblunicode/ucdata/bidiapi.txt
@@ -0,0 +1,84 @@
+#
+# $Id: bidiapi.txt,v 1.2 1999/11/19 15:24:29 mleisher Exp $
+#
+
+ "Pretty Good Bidi Algorithm" API
+
+The PGBA (Pretty Good Bidi Algorithm) is an effective alternative to the
+Unicode BiDi algorithm. It currently provides only implicit reordering and
+does not yet support explicit reordering codes that the Unicode BiDi algorithm
+supports. In addition to reordering, the PGBA includes cursor movement
+support for both visual and logical navigation.
+
+-----------------------------------------------------------------------------
+
+#define UCPGBA_LTR 0
+#define UCPGBA_RTL 1
+
+ These macros appear in the `direction' field of the data structures.
+
+#define UCPGBA_CURSOR_VISUAL 0
+#define UCPGBA_CURSOR_LOGICAL 1
+
+ These macros are used to set the cursor movement for each reordered string.
+
+-----------------------------------------------------------------------------
+
+ucstring_t *ucstring_create(unsigned long *source, unsigned long start,
+ unsigned long end, int default_direction,
+ int cursor_motion)
+
+ This function will create a reordered string by using the implicit
+ directionality of the characters in the specified substring.
+
+ The `default_direction' parameter should be one of UCPGBA_LTR or UCPGBA_RTL
+ and is used only in cases where a string contains no characters with strong
+ directionality.
+
+ The `cursor_motion' parameter should be one of UCPGBA_CURSOR_VISUAL or
+ UCPGBA_CURSOR_LOGICAL, and is used to specify the initial cursor motion
+ behavior. This behavior can be switched at any time using
+ ustring_set_cursor_motion().
+
+-----------------------------------------------------------------------------
+
+void ucstring_free(ucstring_t *string)
+
+ This function will deallocate the memory used by the string, including the
+ string itself.
+
+-----------------------------------------------------------------------------
+
+void ucstring_cursor_info(ustring_t *string, int *direction,
+ unsigned long *position)
+
+ This function will return the text position of the internal cursor and the
+ directionality of the text at that position. The position returned is the
+ original text position of the character.
+
+-----------------------------------------------------------------------------
+
+int ucstring_set_cursor_motion(ucstring_t *string, int cursor_motion)
+
+ This function will change the cursor motion type and return the previous
+ cursor motion type.
+
+-----------------------------------------------------------------------------
+
+int ucstring_cursor_right(ucstring_t *string, int count)
+
+ This function will move the internal cursor to the right according to the
+ type of cursor motion set for the string.
+
+ If no cursor motion is performed, it returns 0. Otherwise it will return a
+ 1.
+
+-----------------------------------------------------------------------------
+
+int ucstring_cursor_left(ucstring_t *string, int count)
+
+ This function will move the internal cursor to the left according to the
+ type of cursor motion set for the string.
+
+ If no cursor motion is performed, it returns 0. Otherwise it will return a
+ 1.
diff --git a/libraries/liblunicode/ucdata/format.txt b/libraries/liblunicode/ucdata/format.txt
new file mode 100644
index 0000000..e285b39
--- /dev/null
+++ b/libraries/liblunicode/ucdata/format.txt
@@ -0,0 +1,267 @@
+#
+# $Id: format.txt,v 1.2 2001/01/02 18:46:20 mleisher Exp $
+#
+
+CHARACTER DATA
+==============
+
+This package generates some data files that contain character properties useful
+for text processing.
+
+CHARACTER PROPERTIES
+====================
+
+The first data file is called "ctype.dat" and contains a compressed form of
+the character properties found in the Unicode Character Database (UCDB).
+Additional properties can be specified in limited UCDB format in another file
+to avoid modifying the original UCDB.
+
+The following is a property name and code table to be used with the character
+data:
+
+NAME CODE DESCRIPTION
+---------------------
+Mn 0 Mark, Non-Spacing
+Mc 1 Mark, Spacing Combining
+Me 2 Mark, Enclosing
+Nd 3 Number, Decimal Digit
+Nl 4 Number, Letter
+No 5 Number, Other
+Zs 6 Separator, Space
+Zl 7 Separator, Line
+Zp 8 Separator, Paragraph
+Cc 9 Other, Control
+Cf 10 Other, Format
+Cs 11 Other, Surrogate
+Co 12 Other, Private Use
+Cn 13 Other, Not Assigned
+Lu 14 Letter, Uppercase
+Ll 15 Letter, Lowercase
+Lt 16 Letter, Titlecase
+Lm 17 Letter, Modifier
+Lo 18 Letter, Other
+Pc 19 Punctuation, Connector
+Pd 20 Punctuation, Dash
+Ps 21 Punctuation, Open
+Pe 22 Punctuation, Close
+Po 23 Punctuation, Other
+Sm 24 Symbol, Math
+Sc 25 Symbol, Currency
+Sk 26 Symbol, Modifier
+So 27 Symbol, Other
+L 28 Left-To-Right
+R 29 Right-To-Left
+EN 30 European Number
+ES 31 European Number Separator
+ET 32 European Number Terminator
+AN 33 Arabic Number
+CS 34 Common Number Separator
+B 35 Block Separator
+S 36 Segment Separator
+WS 37 Whitespace
+ON 38 Other Neutrals
+Pi 47 Punctuation, Initial
+Pf 48 Punctuation, Final
+#
+# Implementation specific properties.
+#
+Cm 39 Composite
+Nb 40 Non-Breaking
+Sy 41 Symmetric (characters which are part of open/close pairs)
+Hd 42 Hex Digit
+Qm 43 Quote Mark
+Mr 44 Mirroring
+Ss 45 Space, Other (controls viewed as spaces in ctype isspace())
+Cp 46 Defined character
+
+The actual binary data is formatted as follows:
+
+ Assumptions: unsigned short is at least 16-bits in size and unsigned long
+ is at least 32-bits in size.
+
+ unsigned short ByteOrderMark
+ unsigned short OffsetArraySize
+ unsigned long Bytes
+ unsigned short Offsets[OffsetArraySize + 1]
+ unsigned long Ranges[N], N = value of Offsets[OffsetArraySize]
+
+ The Bytes field provides the total byte count used for the Offsets[] and
+ Ranges[] arrays. The Offsets[] array is aligned on a 4-byte boundary and
+ there is always one extra node on the end to hold the final index of the
+ Ranges[] array. The Ranges[] array contains pairs of 4-byte values
+ representing a range of Unicode characters. The pairs are arranged in
+ increasing order by the first character code in the range.
+
+ Determining if a particular character is in the property list requires a
+ simple binary search to determine if a character is in any of the ranges
+ for the property.
+
+ If the ByteOrderMark is equal to 0xFFFE, then the data was generated on a
+ machine with a different endian order and the values must be byte-swapped.
+
+ To swap a 16-bit value:
+ c = (c >> 8) | ((c & 0xff) << 8)
+
+ To swap a 32-bit value:
+ c = ((c & 0xff) << 24) | (((c >> 8) & 0xff) << 16) |
+ (((c >> 16) & 0xff) << 8) | (c >> 24)
+
+CASE MAPPINGS
+=============
+
+The next data file is called "case.dat" and contains three case mapping tables
+in the following order: upper, lower, and title case. Each table is in
+increasing order by character code and each mapping contains 3 unsigned longs
+which represent the possible mappings.
+
+The format for the binary form of these tables is:
+
+ unsigned short ByteOrderMark
+ unsigned short NumMappingNodes, count of all mapping nodes
+ unsigned short CaseTableSizes[2], upper and lower mapping node counts
+ unsigned long CaseTables[NumMappingNodes]
+
+ The starting indexes of the case tables are calculated as following:
+
+ UpperIndex = 0;
+ LowerIndex = CaseTableSizes[0] * 3;
+ TitleIndex = LowerIndex + CaseTableSizes[1] * 3;
+
+ The order of the fields for the three tables are:
+
+ Upper case
+ ----------
+ unsigned long upper;
+ unsigned long lower;
+ unsigned long title;
+
+ Lower case
+ ----------
+ unsigned long lower;
+ unsigned long upper;
+ unsigned long title;
+
+ Title case
+ ----------
+ unsigned long title;
+ unsigned long upper;
+ unsigned long lower;
+
+ If the ByteOrderMark is equal to 0xFFFE, endian swapping is required in the
+ same way as described in the CHARACTER PROPERTIES section.
+
+ Because the tables are in increasing order by character code, locating a
+ mapping requires a simple binary search on one of the 3 codes that make up
+ each node.
+
+ It is important to note that there can only be 65536 mapping nodes which
+ divided into 3 portions allows 21845 nodes for each case mapping table. The
+ distribution of mappings may be more or less than 21845 per table, but only
+ 65536 are allowed.
+
+COMPOSITIONS
+============
+
+This data file is called "comp.dat" and contains data that tracks character
+pairs that have a single Unicode value representing the combination of the two
+characters.
+
+The format for the binary form of this table is:
+
+ unsigned short ByteOrderMark
+ unsigned short NumCompositionNodes, count of composition nodes
+ unsigned long Bytes, total number of bytes used for composition nodes
+ unsigned long CompositionNodes[NumCompositionNodes * 4]
+
+ If the ByteOrderMark is equal to 0xFFFE, endian swapping is required in the
+ same way as described in the CHARACTER PROPERTIES section.
+
+ The CompositionNodes[] array consists of groups of 4 unsigned longs. The
+ first of these is the character code representing the combination of two
+ other character codes, the second records the number of character codes that
+ make up the composition (not currently used), and the last two are the pair
+ of character codes whose combination is represented by the character code in
+ the first field.
+
+DECOMPOSITIONS
+==============
+
+The next data file is called "decomp.dat" and contains the decomposition data
+for all characters with decompositions containing more than one character and
+are *not* compatibility decompositions. Compatibility decompositions are
+signaled in the UCDB format by the use of the <compat> tag in the
+decomposition field. Each list of character codes represents a full
+decomposition of a composite character. The nodes are arranged in increasing
+order by character code.
+
+The format for the binary form of this table is:
+
+ unsigned short ByteOrderMark
+ unsigned short NumDecompNodes, count of all decomposition nodes
+ unsigned long Bytes
+ unsigned long DecompNodes[(NumDecompNodes * 2) + 1]
+ unsigned long Decomp[N], N = sum of all counts in DecompNodes[]
+
+ If the ByteOrderMark is equal to 0xFFFE, endian swapping is required in the
+ same way as described in the CHARACTER PROPERTIES section.
+
+ The DecompNodes[] array consists of pairs of unsigned longs, the first of
+ which is the character code and the second is the initial index of the list
+ of character codes representing the decomposition.
+
+ Locating the decomposition of a composite character requires a binary search
+ for a character code in the DecompNodes[] array and using its index to
+ locate the start of the decomposition. The length of the decomposition list
+ is the index in the following element in DecompNode[] minus the current
+ index.
+
+COMBINING CLASSES
+=================
+
+The fourth data file is called "cmbcl.dat" and contains the characters with
+non-zero combining classes.
+
+The format for the binary form of this table is:
+
+ unsigned short ByteOrderMark
+ unsigned short NumCCLNodes
+ unsigned long Bytes
+ unsigned long CCLNodes[NumCCLNodes * 3]
+
+ If the ByteOrderMark is equal to 0xFFFE, endian swapping is required in the
+ same way as described in the CHARACTER PROPERTIES section.
+
+ The CCLNodes[] array consists of groups of three unsigned longs. The first
+ and second are the beginning and ending of a range and the third is the
+ combining class of that range.
+
+ If a character is not found in this table, then the combining class is
+ assumed to be 0.
+
+ It is important to note that only 65536 distinct ranges plus combining class
+ can be specified because the NumCCLNodes is usually a 16-bit number.
+
+NUMBER TABLE
+============
+
+The final data file is called "num.dat" and contains the characters that have
+a numeric value associated with them.
+
+The format for the binary form of the table is:
+
+ unsigned short ByteOrderMark
+ unsigned short NumNumberNodes
+ unsigned long Bytes
+ unsigned long NumberNodes[NumNumberNodes]
+ unsigned short ValueNodes[(Bytes - (NumNumberNodes * sizeof(unsigned long)))
+ / sizeof(short)]
+
+ If the ByteOrderMark is equal to 0xFFFE, endian swapping is required in the
+ same way as described in the CHARACTER PROPERTIES section.
+
+ The NumberNodes array contains pairs of values, the first of which is the
+ character code and the second an index into the ValueNodes array. The
+ ValueNodes array contains pairs of integers which represent the numerator
+ and denominator of the numeric value of the character. If the character
+ happens to map to an integer, both the values in ValueNodes will be the
+ same.
diff --git a/libraries/liblunicode/ucdata/ucdata.c b/libraries/liblunicode/ucdata/ucdata.c
new file mode 100644
index 0000000..cee004b
--- /dev/null
+++ b/libraries/liblunicode/ucdata/ucdata.c
@@ -0,0 +1,1501 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Copyright 2001 Computing Research Labs, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+ * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* $Id: ucdata.c,v 1.4 2001/01/02 18:46:20 mleisher Exp $" */
+
+#include "portable.h"
+#include "ldap_config.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include <ac/bytes.h>
+
+#include "lber_pvt.h"
+#include "ucdata.h"
+
+#ifndef HARDCODE_DATA
+#define HARDCODE_DATA 1
+#endif
+
+#if HARDCODE_DATA
+#include "uctable.h"
+#endif
+
+/**************************************************************************
+ *
+ * Miscellaneous types, data, and support functions.
+ *
+ **************************************************************************/
+
+typedef struct {
+ ac_uint2 bom;
+ ac_uint2 cnt;
+ union {
+ ac_uint4 bytes;
+ ac_uint2 len[2];
+ } size;
+} _ucheader_t;
+
+/*
+ * A simple array of 32-bit masks for lookup.
+ */
+static ac_uint4 masks32[32] = {
+ 0x00000001UL, 0x00000002UL, 0x00000004UL, 0x00000008UL,
+ 0x00000010UL, 0x00000020UL, 0x00000040UL, 0x00000080UL,
+ 0x00000100UL, 0x00000200UL, 0x00000400UL, 0x00000800UL,
+ 0x00001000UL, 0x00002000UL, 0x00004000UL, 0x00008000UL,
+ 0x00010000UL, 0x00020000UL, 0x00040000UL, 0x00080000UL,
+ 0x00100000UL, 0x00200000UL, 0x00400000UL, 0x00800000UL,
+ 0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL,
+ 0x10000000UL, 0x20000000UL, 0x40000000UL, 0x80000000UL
+};
+
+#define endian_short(cc) (((cc) >> 8) | (((cc) & 0xff) << 8))
+#define endian_long(cc) ((((cc) & 0xff) << 24)|((((cc) >> 8) & 0xff) << 16)|\
+ ((((cc) >> 16) & 0xff) << 8)|((cc) >> 24))
+
+#if !HARDCODE_DATA
+static FILE *
+_ucopenfile(char *paths, char *filename, char *mode)
+{
+ FILE *f;
+ char *fp, *dp, *pp, path[BUFSIZ];
+
+ if (filename == 0 || *filename == 0)
+ return 0;
+
+ dp = paths;
+ while (dp && *dp) {
+ pp = path;
+ while (*dp && *dp != ':')
+ *pp++ = *dp++;
+ *pp++ = *LDAP_DIRSEP;
+
+ fp = filename;
+ while (*fp)
+ *pp++ = *fp++;
+ *pp = 0;
+
+ if ((f = fopen(path, mode)) != 0)
+ return f;
+
+ if (*dp == ':')
+ dp++;
+ }
+
+ return 0;
+}
+#endif
+
+/**************************************************************************
+ *
+ * Support for the character properties.
+ *
+ **************************************************************************/
+
+#if !HARDCODE_DATA
+
+static ac_uint4 _ucprop_size;
+static ac_uint2 *_ucprop_offsets;
+static ac_uint4 *_ucprop_ranges;
+
+/*
+ * Return -1 on error, 0 if okay
+ */
+static int
+_ucprop_load(char *paths, int reload)
+{
+ FILE *in;
+ ac_uint4 size, i;
+ _ucheader_t hdr;
+
+ if (_ucprop_size > 0) {
+ if (!reload)
+ /*
+ * The character properties have already been loaded.
+ */
+ return 0;
+
+ /*
+ * Unload the current character property data in preparation for
+ * loading a new copy. Only the first array has to be deallocated
+ * because all the memory for the arrays is allocated as a single
+ * block.
+ */
+ free((char *) _ucprop_offsets);
+ _ucprop_size = 0;
+ }
+
+ if ((in = _ucopenfile(paths, "ctype.dat", "rb")) == 0)
+ return -1;
+
+ /*
+ * Load the header.
+ */
+ fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
+
+ if (hdr.bom == 0xfffe) {
+ hdr.cnt = endian_short(hdr.cnt);
+ hdr.size.bytes = endian_long(hdr.size.bytes);
+ }
+
+ if ((_ucprop_size = hdr.cnt) == 0) {
+ fclose(in);
+ return -1;
+ }
+
+ /*
+ * Allocate all the storage needed for the lookup table.
+ */
+ _ucprop_offsets = (ac_uint2 *) malloc(hdr.size.bytes);
+
+ /*
+ * Calculate the offset into the storage for the ranges. The offsets
+ * array is on a 4-byte boundary and one larger than the value provided in
+ * the header count field. This means the offset to the ranges must be
+ * calculated after aligning the count to a 4-byte boundary.
+ */
+ if ((size = ((hdr.cnt + 1) * sizeof(ac_uint2))) & 3)
+ size += 4 - (size & 3);
+ size >>= 1;
+ _ucprop_ranges = (ac_uint4 *) (_ucprop_offsets + size);
+
+ /*
+ * Load the offset array.
+ */
+ fread((char *) _ucprop_offsets, sizeof(ac_uint2), size, in);
+
+ /*
+ * Do an endian swap if necessary. Don't forget there is an extra node on
+ * the end with the final index.
+ */
+ if (hdr.bom == 0xfffe) {
+ for (i = 0; i <= _ucprop_size; i++)
+ _ucprop_offsets[i] = endian_short(_ucprop_offsets[i]);
+ }
+
+ /*
+ * Load the ranges. The number of elements is in the last array position
+ * of the offsets.
+ */
+ fread((char *) _ucprop_ranges, sizeof(ac_uint4),
+ _ucprop_offsets[_ucprop_size], in);
+
+ fclose(in);
+
+ /*
+ * Do an endian swap if necessary.
+ */
+ if (hdr.bom == 0xfffe) {
+ for (i = 0; i < _ucprop_offsets[_ucprop_size]; i++)
+ _ucprop_ranges[i] = endian_long(_ucprop_ranges[i]);
+ }
+ return 0;
+}
+
+static void
+_ucprop_unload(void)
+{
+ if (_ucprop_size == 0)
+ return;
+
+ /*
+ * Only need to free the offsets because the memory is allocated as a
+ * single block.
+ */
+ free((char *) _ucprop_offsets);
+ _ucprop_size = 0;
+}
+#endif
+
+static int
+_ucprop_lookup(ac_uint4 code, ac_uint4 n)
+{
+ long l, r, m;
+
+ if (_ucprop_size == 0)
+ return 0;
+
+ /*
+ * There is an extra node on the end of the offsets to allow this routine
+ * to work right. If the index is 0xffff, then there are no nodes for the
+ * property.
+ */
+ if ((l = _ucprop_offsets[n]) == 0xffff)
+ return 0;
+
+ /*
+ * Locate the next offset that is not 0xffff. The sentinel at the end of
+ * the array is the max index value.
+ */
+ for (m = 1;
+ n + m < _ucprop_size && _ucprop_offsets[n + m] == 0xffff; m++) ;
+
+ r = _ucprop_offsets[n + m] - 1;
+
+ while (l <= r) {
+ /*
+ * Determine a "mid" point and adjust to make sure the mid point is at
+ * the beginning of a range pair.
+ */
+ m = (l + r) >> 1;
+ m -= (m & 1);
+ if (code > _ucprop_ranges[m + 1])
+ l = m + 2;
+ else if (code < _ucprop_ranges[m])
+ r = m - 2;
+ else if (code >= _ucprop_ranges[m] && code <= _ucprop_ranges[m + 1])
+ return 1;
+ }
+ return 0;
+}
+
+int
+ucisprop(ac_uint4 code, ac_uint4 mask1, ac_uint4 mask2)
+{
+ ac_uint4 i;
+
+ if (mask1 == 0 && mask2 == 0)
+ return 0;
+
+ for (i = 0; mask1 && i < 32; i++) {
+ if ((mask1 & masks32[i]) && _ucprop_lookup(code, i))
+ return 1;
+ }
+
+ for (i = 32; mask2 && i < _ucprop_size; i++) {
+ if ((mask2 & masks32[i & 31]) && _ucprop_lookup(code, i))
+ return 1;
+ }
+
+ return 0;
+}
+
+/**************************************************************************
+ *
+ * Support for case mapping.
+ *
+ **************************************************************************/
+
+#if !HARDCODE_DATA
+
+/* These record the number of slots in the map.
+ * There are 3 words per slot.
+ */
+static ac_uint4 _uccase_size;
+static ac_uint2 _uccase_len[2];
+static ac_uint4 *_uccase_map;
+
+/*
+ * Return -1 on error, 0 if okay
+ */
+static int
+_uccase_load(char *paths, int reload)
+{
+ FILE *in;
+ ac_uint4 i;
+ _ucheader_t hdr;
+
+ if (_uccase_size > 0) {
+ if (!reload)
+ /*
+ * The case mappings have already been loaded.
+ */
+ return 0;
+
+ free((char *) _uccase_map);
+ _uccase_size = 0;
+ }
+
+ if ((in = _ucopenfile(paths, "case.dat", "rb")) == 0)
+ return -1;
+
+ /*
+ * Load the header.
+ */
+ fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
+
+ if (hdr.bom == 0xfffe) {
+ hdr.cnt = endian_short(hdr.cnt);
+ hdr.size.len[0] = endian_short(hdr.size.len[0]);
+ hdr.size.len[1] = endian_short(hdr.size.len[1]);
+ }
+
+ /*
+ * Set the node count and lengths of the upper and lower case mapping
+ * tables.
+ */
+ _uccase_size = hdr.cnt;
+ _uccase_len[0] = hdr.size.len[0];
+ _uccase_len[1] = hdr.size.len[1];
+
+ _uccase_map = (ac_uint4 *)
+ malloc(_uccase_size * 3 * sizeof(ac_uint4));
+
+ /*
+ * Load the case mapping table.
+ */
+ fread((char *) _uccase_map, sizeof(ac_uint4), _uccase_size * 3, in);
+
+ /*
+ * Do an endian swap if necessary.
+ */
+ if (hdr.bom == 0xfffe) {
+ for (i = 0; i < _uccase_size * 3; i++)
+ _uccase_map[i] = endian_long(_uccase_map[i]);
+ }
+ fclose(in);
+ return 0;
+}
+
+static void
+_uccase_unload(void)
+{
+ if (_uccase_size == 0)
+ return;
+
+ free((char *) _uccase_map);
+ _uccase_size = 0;
+}
+#endif
+
+static ac_uint4
+_uccase_lookup(ac_uint4 code, long l, long r, int field)
+{
+ long m;
+ const ac_uint4 *tmp;
+
+ /*
+ * Do the binary search.
+ */
+ while (l <= r) {
+ /*
+ * Determine a "mid" point and adjust to make sure the mid point is at
+ * the beginning of a case mapping triple.
+ */
+ m = (l + r) >> 1;
+ tmp = &_uccase_map[m*3];
+ if (code > *tmp)
+ l = m + 1;
+ else if (code < *tmp)
+ r = m - 1;
+ else if (code == *tmp)
+ return tmp[field];
+ }
+
+ return code;
+}
+
+ac_uint4
+uctoupper(ac_uint4 code)
+{
+ int field;
+ long l, r;
+
+ if (ucisupper(code))
+ return code;
+
+ if (ucislower(code)) {
+ /*
+ * The character is lower case.
+ */
+ field = 2;
+ l = _uccase_len[0];
+ r = (l + _uccase_len[1]) - 1;
+ } else {
+ /*
+ * The character is title case.
+ */
+ field = 1;
+ l = _uccase_len[0] + _uccase_len[1];
+ r = _uccase_size - 1;
+ }
+ return _uccase_lookup(code, l, r, field);
+}
+
+ac_uint4
+uctolower(ac_uint4 code)
+{
+ int field;
+ long l, r;
+
+ if (ucislower(code))
+ return code;
+
+ if (ucisupper(code)) {
+ /*
+ * The character is upper case.
+ */
+ field = 1;
+ l = 0;
+ r = _uccase_len[0] - 1;
+ } else {
+ /*
+ * The character is title case.
+ */
+ field = 2;
+ l = _uccase_len[0] + _uccase_len[1];
+ r = _uccase_size - 1;
+ }
+ return _uccase_lookup(code, l, r, field);
+}
+
+ac_uint4
+uctotitle(ac_uint4 code)
+{
+ int field;
+ long l, r;
+
+ if (ucistitle(code))
+ return code;
+
+ /*
+ * The offset will always be the same for converting to title case.
+ */
+ field = 2;
+
+ if (ucisupper(code)) {
+ /*
+ * The character is upper case.
+ */
+ l = 0;
+ r = _uccase_len[0] - 1;
+ } else {
+ /*
+ * The character is lower case.
+ */
+ l = _uccase_len[0];
+ r = (l + _uccase_len[1]) - 1;
+ }
+ return _uccase_lookup(code, l, r, field);
+}
+
+/**************************************************************************
+ *
+ * Support for compositions.
+ *
+ **************************************************************************/
+
+#if !HARDCODE_DATA
+
+static ac_uint4 _uccomp_size;
+static ac_uint4 *_uccomp_data;
+
+/*
+ * Return -1 on error, 0 if okay
+ */
+static int
+_uccomp_load(char *paths, int reload)
+{
+ FILE *in;
+ ac_uint4 size, i;
+ _ucheader_t hdr;
+
+ if (_uccomp_size > 0) {
+ if (!reload)
+ /*
+ * The compositions have already been loaded.
+ */
+ return 0;
+
+ free((char *) _uccomp_data);
+ _uccomp_size = 0;
+ }
+
+ if ((in = _ucopenfile(paths, "comp.dat", "rb")) == 0)
+ return -1;
+
+ /*
+ * Load the header.
+ */
+ fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
+
+ if (hdr.bom == 0xfffe) {
+ hdr.cnt = endian_short(hdr.cnt);
+ hdr.size.bytes = endian_long(hdr.size.bytes);
+ }
+
+ _uccomp_size = hdr.cnt;
+ _uccomp_data = (ac_uint4 *) malloc(hdr.size.bytes);
+
+ /*
+ * Read the composition data in.
+ */
+ size = hdr.size.bytes / sizeof(ac_uint4);
+ fread((char *) _uccomp_data, sizeof(ac_uint4), size, in);
+
+ /*
+ * Do an endian swap if necessary.
+ */
+ if (hdr.bom == 0xfffe) {
+ for (i = 0; i < size; i++)
+ _uccomp_data[i] = endian_long(_uccomp_data[i]);
+ }
+
+ /*
+ * Assume that the data is ordered on count, so that all compositions
+ * of length 2 come first. Only handling length 2 for now.
+ */
+ for (i = 1; i < size; i += 4)
+ if (_uccomp_data[i] != 2)
+ break;
+ _uccomp_size = i - 1;
+
+ fclose(in);
+ return 0;
+}
+
+static void
+_uccomp_unload(void)
+{
+ if (_uccomp_size == 0)
+ return;
+
+ free((char *) _uccomp_data);
+ _uccomp_size = 0;
+}
+#endif
+
+int
+uccomp(ac_uint4 node1, ac_uint4 node2, ac_uint4 *comp)
+{
+ int l, r, m;
+
+ l = 0;
+ r = _uccomp_size - 1;
+
+ while (l <= r) {
+ m = ((r + l) >> 1);
+ m -= m & 3;
+ if (node1 > _uccomp_data[m+2])
+ l = m + 4;
+ else if (node1 < _uccomp_data[m+2])
+ r = m - 4;
+ else if (node2 > _uccomp_data[m+3])
+ l = m + 4;
+ else if (node2 < _uccomp_data[m+3])
+ r = m - 4;
+ else {
+ *comp = _uccomp_data[m];
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int
+uccomp_hangul(ac_uint4 *str, int len)
+{
+ const int SBase = 0xAC00, LBase = 0x1100,
+ VBase = 0x1161, TBase = 0x11A7,
+ LCount = 19, VCount = 21, TCount = 28,
+ NCount = VCount * TCount, /* 588 */
+ SCount = LCount * NCount; /* 11172 */
+
+ int i, rlen;
+ ac_uint4 ch, last, lindex, sindex;
+
+ last = str[0];
+ rlen = 1;
+ for ( i = 1; i < len; i++ ) {
+ ch = str[i];
+
+ /* check if two current characters are L and V */
+ lindex = last - LBase;
+ if (lindex < (ac_uint4) LCount) {
+ ac_uint4 vindex = ch - VBase;
+ if (vindex < (ac_uint4) VCount) {
+ /* make syllable of form LV */
+ last = SBase + (lindex * VCount + vindex) * TCount;
+ str[rlen-1] = last; /* reset last */
+ continue;
+ }
+ }
+
+ /* check if two current characters are LV and T */
+ sindex = last - SBase;
+ if (sindex < (ac_uint4) SCount
+ && (sindex % TCount) == 0)
+ {
+ ac_uint4 tindex = ch - TBase;
+ if (tindex <= (ac_uint4) TCount) {
+ /* make syllable of form LVT */
+ last += tindex;
+ str[rlen-1] = last; /* reset last */
+ continue;
+ }
+ }
+
+ /* if neither case was true, just add the character */
+ last = ch;
+ str[rlen] = ch;
+ rlen++;
+ }
+ return rlen;
+}
+
+int
+uccanoncomp(ac_uint4 *str, int len)
+{
+ int i, stpos, copos;
+ ac_uint4 cl, prevcl, st, ch, co;
+
+ st = str[0];
+ stpos = 0;
+ copos = 1;
+ prevcl = uccombining_class(st) == 0 ? 0 : 256;
+
+ for (i = 1; i < len; i++) {
+ ch = str[i];
+ cl = uccombining_class(ch);
+ if (uccomp(st, ch, &co) && (prevcl < cl || prevcl == 0))
+ st = str[stpos] = co;
+ else {
+ if (cl == 0) {
+ stpos = copos;
+ st = ch;
+ }
+ prevcl = cl;
+ str[copos++] = ch;
+ }
+ }
+
+ return uccomp_hangul(str, copos);
+}
+
+/**************************************************************************
+ *
+ * Support for decompositions.
+ *
+ **************************************************************************/
+
+#if !HARDCODE_DATA
+
+static ac_uint4 _ucdcmp_size;
+static ac_uint4 *_ucdcmp_nodes;
+static ac_uint4 *_ucdcmp_decomp;
+
+static ac_uint4 _uckdcmp_size;
+static ac_uint4 *_uckdcmp_nodes;
+static ac_uint4 *_uckdcmp_decomp;
+
+/*
+ * Return -1 on error, 0 if okay
+ */
+static int
+_ucdcmp_load(char *paths, int reload)
+{
+ FILE *in;
+ ac_uint4 size, i;
+ _ucheader_t hdr;
+
+ if (_ucdcmp_size > 0) {
+ if (!reload)
+ /*
+ * The decompositions have already been loaded.
+ */
+ return 0;
+
+ free((char *) _ucdcmp_nodes);
+ _ucdcmp_size = 0;
+ }
+
+ if ((in = _ucopenfile(paths, "decomp.dat", "rb")) == 0)
+ return -1;
+
+ /*
+ * Load the header.
+ */
+ fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
+
+ if (hdr.bom == 0xfffe) {
+ hdr.cnt = endian_short(hdr.cnt);
+ hdr.size.bytes = endian_long(hdr.size.bytes);
+ }
+
+ _ucdcmp_size = hdr.cnt << 1;
+ _ucdcmp_nodes = (ac_uint4 *) malloc(hdr.size.bytes);
+ _ucdcmp_decomp = _ucdcmp_nodes + (_ucdcmp_size + 1);
+
+ /*
+ * Read the decomposition data in.
+ */
+ size = hdr.size.bytes / sizeof(ac_uint4);
+ fread((char *) _ucdcmp_nodes, sizeof(ac_uint4), size, in);
+
+ /*
+ * Do an endian swap if necessary.
+ */
+ if (hdr.bom == 0xfffe) {
+ for (i = 0; i < size; i++)
+ _ucdcmp_nodes[i] = endian_long(_ucdcmp_nodes[i]);
+ }
+ fclose(in);
+ return 0;
+}
+
+/*
+ * Return -1 on error, 0 if okay
+ */
+static int
+_uckdcmp_load(char *paths, int reload)
+{
+ FILE *in;
+ ac_uint4 size, i;
+ _ucheader_t hdr;
+
+ if (_uckdcmp_size > 0) {
+ if (!reload)
+ /*
+ * The decompositions have already been loaded.
+ */
+ return 0;
+
+ free((char *) _uckdcmp_nodes);
+ _uckdcmp_size = 0;
+ }
+
+ if ((in = _ucopenfile(paths, "kdecomp.dat", "rb")) == 0)
+ return -1;
+
+ /*
+ * Load the header.
+ */
+ fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
+
+ if (hdr.bom == 0xfffe) {
+ hdr.cnt = endian_short(hdr.cnt);
+ hdr.size.bytes = endian_long(hdr.size.bytes);
+ }
+
+ _uckdcmp_size = hdr.cnt << 1;
+ _uckdcmp_nodes = (ac_uint4 *) malloc(hdr.size.bytes);
+ _uckdcmp_decomp = _uckdcmp_nodes + (_uckdcmp_size + 1);
+
+ /*
+ * Read the decomposition data in.
+ */
+ size = hdr.size.bytes / sizeof(ac_uint4);
+ fread((char *) _uckdcmp_nodes, sizeof(ac_uint4), size, in);
+
+ /*
+ * Do an endian swap if necessary.
+ */
+ if (hdr.bom == 0xfffe) {
+ for (i = 0; i < size; i++)
+ _uckdcmp_nodes[i] = endian_long(_uckdcmp_nodes[i]);
+ }
+ fclose(in);
+ return 0;
+}
+
+static void
+_ucdcmp_unload(void)
+{
+ if (_ucdcmp_size == 0)
+ return;
+
+ /*
+ * Only need to free the offsets because the memory is allocated as a
+ * single block.
+ */
+ free((char *) _ucdcmp_nodes);
+ _ucdcmp_size = 0;
+}
+
+static void
+_uckdcmp_unload(void)
+{
+ if (_uckdcmp_size == 0)
+ return;
+
+ /*
+ * Only need to free the offsets because the memory is allocated as a
+ * single block.
+ */
+ free((char *) _uckdcmp_nodes);
+ _uckdcmp_size = 0;
+}
+#endif
+
+int
+ucdecomp(ac_uint4 code, ac_uint4 *num, ac_uint4 **decomp)
+{
+ long l, r, m;
+
+ if (code < _ucdcmp_nodes[0]) {
+ return 0;
+ }
+
+ l = 0;
+ r = _ucdcmp_nodes[_ucdcmp_size] - 1;
+
+ while (l <= r) {
+ /*
+ * Determine a "mid" point and adjust to make sure the mid point is at
+ * the beginning of a code+offset pair.
+ */
+ m = (l + r) >> 1;
+ m -= (m & 1);
+ if (code > _ucdcmp_nodes[m])
+ l = m + 2;
+ else if (code < _ucdcmp_nodes[m])
+ r = m - 2;
+ else if (code == _ucdcmp_nodes[m]) {
+ *num = _ucdcmp_nodes[m + 3] - _ucdcmp_nodes[m + 1];
+ *decomp = (ac_uint4*)&_ucdcmp_decomp[_ucdcmp_nodes[m + 1]];
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int
+uckdecomp(ac_uint4 code, ac_uint4 *num, ac_uint4 **decomp)
+{
+ long l, r, m;
+
+ if (code < _uckdcmp_nodes[0]) {
+ return 0;
+ }
+
+ l = 0;
+ r = _uckdcmp_nodes[_uckdcmp_size] - 1;
+
+ while (l <= r) {
+ /*
+ * Determine a "mid" point and adjust to make sure the mid point is at
+ * the beginning of a code+offset pair.
+ */
+ m = (l + r) >> 1;
+ m -= (m & 1);
+ if (code > _uckdcmp_nodes[m])
+ l = m + 2;
+ else if (code < _uckdcmp_nodes[m])
+ r = m - 2;
+ else if (code == _uckdcmp_nodes[m]) {
+ *num = _uckdcmp_nodes[m + 3] - _uckdcmp_nodes[m + 1];
+ *decomp = (ac_uint4*)&_uckdcmp_decomp[_uckdcmp_nodes[m + 1]];
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int
+ucdecomp_hangul(ac_uint4 code, ac_uint4 *num, ac_uint4 decomp[])
+{
+ if (!ucishangul(code))
+ return 0;
+
+ code -= 0xac00;
+ decomp[0] = 0x1100 + (ac_uint4) (code / 588);
+ decomp[1] = 0x1161 + (ac_uint4) ((code % 588) / 28);
+ decomp[2] = 0x11a7 + (ac_uint4) (code % 28);
+ *num = (decomp[2] != 0x11a7) ? 3 : 2;
+
+ return 1;
+}
+
+/* mode == 0 for canonical, mode == 1 for compatibility */
+static int
+uccanoncompatdecomp(const ac_uint4 *in, int inlen,
+ ac_uint4 **out, int *outlen, short mode, void *ctx)
+{
+ int l, size;
+ unsigned i, j, k;
+ ac_uint4 num, class, *decomp, hangdecomp[3];
+
+ size = inlen * 2;
+ *out = (ac_uint4 *) ber_memalloc_x(size * sizeof(**out), ctx);
+ if (*out == NULL)
+ return *outlen = -1;
+
+ i = 0;
+ for (j = 0; j < (unsigned) inlen; j++) {
+ if (mode ? uckdecomp(in[j], &num, &decomp) : ucdecomp(in[j], &num, &decomp)) {
+ if ( size - i < num) {
+ size = inlen + i - j + num - 1;
+ *out = (ac_uint4 *) ber_memrealloc_x(*out, size * sizeof(**out), ctx );
+ if (*out == NULL)
+ return *outlen = -1;
+ }
+ for (k = 0; k < num; k++) {
+ class = uccombining_class(decomp[k]);
+ if (class == 0) {
+ (*out)[i] = decomp[k];
+ } else {
+ for (l = i; l > 0; l--)
+ if (class >= uccombining_class((*out)[l-1]))
+ break;
+ AC_MEMCPY(*out + l + 1, *out + l, (i - l) * sizeof(**out));
+ (*out)[l] = decomp[k];
+ }
+ i++;
+ }
+ } else if (ucdecomp_hangul(in[j], &num, hangdecomp)) {
+ if (size - i < num) {
+ size = inlen + i - j + num - 1;
+ *out = (ac_uint4 *) ber_memrealloc_x(*out, size * sizeof(**out), ctx);
+ if (*out == NULL)
+ return *outlen = -1;
+ }
+ for (k = 0; k < num; k++) {
+ (*out)[i] = hangdecomp[k];
+ i++;
+ }
+ } else {
+ if (size - i < 1) {
+ size = inlen + i - j;
+ *out = (ac_uint4 *) ber_memrealloc_x(*out, size * sizeof(**out), ctx);
+ if (*out == NULL)
+ return *outlen = -1;
+ }
+ class = uccombining_class(in[j]);
+ if (class == 0) {
+ (*out)[i] = in[j];
+ } else {
+ for (l = i; l > 0; l--)
+ if (class >= uccombining_class((*out)[l-1]))
+ break;
+ AC_MEMCPY(*out + l + 1, *out + l, (i - l) * sizeof(**out));
+ (*out)[l] = in[j];
+ }
+ i++;
+ }
+ }
+ return *outlen = i;
+}
+
+int
+uccanondecomp(const ac_uint4 *in, int inlen,
+ ac_uint4 **out, int *outlen, void *ctx)
+{
+ return uccanoncompatdecomp(in, inlen, out, outlen, 0, ctx);
+}
+
+int
+uccompatdecomp(const ac_uint4 *in, int inlen,
+ ac_uint4 **out, int *outlen, void *ctx)
+{
+ return uccanoncompatdecomp(in, inlen, out, outlen, 1, ctx);
+}
+
+/**************************************************************************
+ *
+ * Support for combining classes.
+ *
+ **************************************************************************/
+
+#if !HARDCODE_DATA
+static ac_uint4 _uccmcl_size;
+static ac_uint4 *_uccmcl_nodes;
+
+/*
+ * Return -1 on error, 0 if okay
+ */
+static int
+_uccmcl_load(char *paths, int reload)
+{
+ FILE *in;
+ ac_uint4 i;
+ _ucheader_t hdr;
+
+ if (_uccmcl_size > 0) {
+ if (!reload)
+ /*
+ * The combining classes have already been loaded.
+ */
+ return 0;
+
+ free((char *) _uccmcl_nodes);
+ _uccmcl_size = 0;
+ }
+
+ if ((in = _ucopenfile(paths, "cmbcl.dat", "rb")) == 0)
+ return -1;
+
+ /*
+ * Load the header.
+ */
+ fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
+
+ if (hdr.bom == 0xfffe) {
+ hdr.cnt = endian_short(hdr.cnt);
+ hdr.size.bytes = endian_long(hdr.size.bytes);
+ }
+
+ _uccmcl_size = hdr.cnt * 3;
+ _uccmcl_nodes = (ac_uint4 *) malloc(hdr.size.bytes);
+
+ /*
+ * Read the combining classes in.
+ */
+ fread((char *) _uccmcl_nodes, sizeof(ac_uint4), _uccmcl_size, in);
+
+ /*
+ * Do an endian swap if necessary.
+ */
+ if (hdr.bom == 0xfffe) {
+ for (i = 0; i < _uccmcl_size; i++)
+ _uccmcl_nodes[i] = endian_long(_uccmcl_nodes[i]);
+ }
+ fclose(in);
+ return 0;
+}
+
+static void
+_uccmcl_unload(void)
+{
+ if (_uccmcl_size == 0)
+ return;
+
+ free((char *) _uccmcl_nodes);
+ _uccmcl_size = 0;
+}
+#endif
+
+ac_uint4
+uccombining_class(ac_uint4 code)
+{
+ long l, r, m;
+
+ l = 0;
+ r = _uccmcl_size - 1;
+
+ while (l <= r) {
+ m = (l + r) >> 1;
+ m -= (m % 3);
+ if (code > _uccmcl_nodes[m + 1])
+ l = m + 3;
+ else if (code < _uccmcl_nodes[m])
+ r = m - 3;
+ else if (code >= _uccmcl_nodes[m] && code <= _uccmcl_nodes[m + 1])
+ return _uccmcl_nodes[m + 2];
+ }
+ return 0;
+}
+
+/**************************************************************************
+ *
+ * Support for numeric values.
+ *
+ **************************************************************************/
+
+#if !HARDCODE_DATA
+static ac_uint4 *_ucnum_nodes;
+static ac_uint4 _ucnum_size;
+static short *_ucnum_vals;
+
+/*
+ * Return -1 on error, 0 if okay
+ */
+static int
+_ucnumb_load(char *paths, int reload)
+{
+ FILE *in;
+ ac_uint4 size, i;
+ _ucheader_t hdr;
+
+ if (_ucnum_size > 0) {
+ if (!reload)
+ /*
+ * The numbers have already been loaded.
+ */
+ return 0;
+
+ free((char *) _ucnum_nodes);
+ _ucnum_size = 0;
+ }
+
+ if ((in = _ucopenfile(paths, "num.dat", "rb")) == 0)
+ return -1;
+
+ /*
+ * Load the header.
+ */
+ fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
+
+ if (hdr.bom == 0xfffe) {
+ hdr.cnt = endian_short(hdr.cnt);
+ hdr.size.bytes = endian_long(hdr.size.bytes);
+ }
+
+ _ucnum_size = hdr.cnt;
+ _ucnum_nodes = (ac_uint4 *) malloc(hdr.size.bytes);
+ _ucnum_vals = (short *) (_ucnum_nodes + _ucnum_size);
+
+ /*
+ * Read the combining classes in.
+ */
+ fread((char *) _ucnum_nodes, sizeof(unsigned char), hdr.size.bytes, in);
+
+ /*
+ * Do an endian swap if necessary.
+ */
+ if (hdr.bom == 0xfffe) {
+ for (i = 0; i < _ucnum_size; i++)
+ _ucnum_nodes[i] = endian_long(_ucnum_nodes[i]);
+
+ /*
+ * Determine the number of values that have to be adjusted.
+ */
+ size = (hdr.size.bytes -
+ (_ucnum_size * (sizeof(ac_uint4) << 1))) /
+ sizeof(short);
+
+ for (i = 0; i < size; i++)
+ _ucnum_vals[i] = endian_short(_ucnum_vals[i]);
+ }
+ fclose(in);
+ return 0;
+}
+
+static void
+_ucnumb_unload(void)
+{
+ if (_ucnum_size == 0)
+ return;
+
+ free((char *) _ucnum_nodes);
+ _ucnum_size = 0;
+}
+#endif
+
+int
+ucnumber_lookup(ac_uint4 code, struct ucnumber *num)
+{
+ long l, r, m;
+ short *vp;
+
+ l = 0;
+ r = _ucnum_size - 1;
+ while (l <= r) {
+ /*
+ * Determine a "mid" point and adjust to make sure the mid point is at
+ * the beginning of a code+offset pair.
+ */
+ m = (l + r) >> 1;
+ m -= (m & 1);
+ if (code > _ucnum_nodes[m])
+ l = m + 2;
+ else if (code < _ucnum_nodes[m])
+ r = m - 2;
+ else {
+ vp = (short *)_ucnum_vals + _ucnum_nodes[m + 1];
+ num->numerator = (int) *vp++;
+ num->denominator = (int) *vp;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int
+ucdigit_lookup(ac_uint4 code, int *digit)
+{
+ long l, r, m;
+ short *vp;
+
+ l = 0;
+ r = _ucnum_size - 1;
+ while (l <= r) {
+ /*
+ * Determine a "mid" point and adjust to make sure the mid point is at
+ * the beginning of a code+offset pair.
+ */
+ m = (l + r) >> 1;
+ m -= (m & 1);
+ if (code > _ucnum_nodes[m])
+ l = m + 2;
+ else if (code < _ucnum_nodes[m])
+ r = m - 2;
+ else {
+ vp = (short *)_ucnum_vals + _ucnum_nodes[m + 1];
+ if (*vp == *(vp + 1)) {
+ *digit = *vp;
+ return 1;
+ }
+ return 0;
+ }
+ }
+ return 0;
+}
+
+struct ucnumber
+ucgetnumber(ac_uint4 code)
+{
+ struct ucnumber num;
+
+ /*
+ * Initialize with some arbitrary value, because the caller simply cannot
+ * tell for sure if the code is a number without calling the ucisnumber()
+ * macro before calling this function.
+ */
+ num.numerator = num.denominator = -111;
+
+ (void) ucnumber_lookup(code, &num);
+
+ return num;
+}
+
+int
+ucgetdigit(ac_uint4 code)
+{
+ int dig;
+
+ /*
+ * Initialize with some arbitrary value, because the caller simply cannot
+ * tell for sure if the code is a number without calling the ucisdigit()
+ * macro before calling this function.
+ */
+ dig = -111;
+
+ (void) ucdigit_lookup(code, &dig);
+
+ return dig;
+}
+
+/**************************************************************************
+ *
+ * Setup and cleanup routines.
+ *
+ **************************************************************************/
+
+#if HARDCODE_DATA
+int ucdata_load(char *paths, int masks) { return 0; }
+void ucdata_unload(int masks) { }
+int ucdata_reload(char *paths, int masks) { return 0; }
+#else
+/*
+ * Return 0 if okay, negative on error
+ */
+int
+ucdata_load(char *paths, int masks)
+{
+ int error = 0;
+
+ if (masks & UCDATA_CTYPE)
+ error |= _ucprop_load(paths, 0) < 0 ? UCDATA_CTYPE : 0;
+ if (masks & UCDATA_CASE)
+ error |= _uccase_load(paths, 0) < 0 ? UCDATA_CASE : 0;
+ if (masks & UCDATA_DECOMP)
+ error |= _ucdcmp_load(paths, 0) < 0 ? UCDATA_DECOMP : 0;
+ if (masks & UCDATA_CMBCL)
+ error |= _uccmcl_load(paths, 0) < 0 ? UCDATA_CMBCL : 0;
+ if (masks & UCDATA_NUM)
+ error |= _ucnumb_load(paths, 0) < 0 ? UCDATA_NUM : 0;
+ if (masks & UCDATA_COMP)
+ error |= _uccomp_load(paths, 0) < 0 ? UCDATA_COMP : 0;
+ if (masks & UCDATA_KDECOMP)
+ error |= _uckdcmp_load(paths, 0) < 0 ? UCDATA_KDECOMP : 0;
+
+ return -error;
+}
+
+void
+ucdata_unload(int masks)
+{
+ if (masks & UCDATA_CTYPE)
+ _ucprop_unload();
+ if (masks & UCDATA_CASE)
+ _uccase_unload();
+ if (masks & UCDATA_DECOMP)
+ _ucdcmp_unload();
+ if (masks & UCDATA_CMBCL)
+ _uccmcl_unload();
+ if (masks & UCDATA_NUM)
+ _ucnumb_unload();
+ if (masks & UCDATA_COMP)
+ _uccomp_unload();
+ if (masks & UCDATA_KDECOMP)
+ _uckdcmp_unload();
+}
+
+/*
+ * Return 0 if okay, negative on error
+ */
+int
+ucdata_reload(char *paths, int masks)
+{
+ int error = 0;
+
+ if (masks & UCDATA_CTYPE)
+ error |= _ucprop_load(paths, 1) < 0 ? UCDATA_CTYPE : 0;
+ if (masks & UCDATA_CASE)
+ error |= _uccase_load(paths, 1) < 0 ? UCDATA_CASE : 0;
+ if (masks & UCDATA_DECOMP)
+ error |= _ucdcmp_load(paths, 1) < 0 ? UCDATA_DECOMP : 0;
+ if (masks & UCDATA_CMBCL)
+ error |= _uccmcl_load(paths, 1) < 0 ? UCDATA_CMBCL : 0;
+ if (masks & UCDATA_NUM)
+ error |= _ucnumb_load(paths, 1) < 0 ? UCDATA_NUM : 0;
+ if (masks & UCDATA_COMP)
+ error |= _uccomp_load(paths, 1) < 0 ? UCDATA_COMP : 0;
+ if (masks & UCDATA_KDECOMP)
+ error |= _uckdcmp_load(paths, 1) < 0 ? UCDATA_KDECOMP : 0;
+
+ return -error;
+}
+#endif
+
+#ifdef TEST
+
+void
+main(void)
+{
+ int dig;
+ ac_uint4 i, lo, *dec;
+ struct ucnumber num;
+
+/* ucdata_setup("."); */
+
+ if (ucisweak(0x30))
+ printf("WEAK\n");
+ else
+ printf("NOT WEAK\n");
+
+ printf("LOWER 0x%04lX\n", uctolower(0xff3a));
+ printf("UPPER 0x%04lX\n", uctoupper(0xff5a));
+
+ if (ucisalpha(0x1d5))
+ printf("ALPHA\n");
+ else
+ printf("NOT ALPHA\n");
+
+ if (ucisupper(0x1d5)) {
+ printf("UPPER\n");
+ lo = uctolower(0x1d5);
+ printf("0x%04lx\n", lo);
+ lo = uctotitle(0x1d5);
+ printf("0x%04lx\n", lo);
+ } else
+ printf("NOT UPPER\n");
+
+ if (ucistitle(0x1d5))
+ printf("TITLE\n");
+ else
+ printf("NOT TITLE\n");
+
+ if (uciscomposite(0x1d5))
+ printf("COMPOSITE\n");
+ else
+ printf("NOT COMPOSITE\n");
+
+ if (ucdecomp(0x1d5, &lo, &dec)) {
+ for (i = 0; i < lo; i++)
+ printf("0x%04lx ", dec[i]);
+ putchar('\n');
+ }
+
+ if ((lo = uccombining_class(0x41)) != 0)
+ printf("0x41 CCL %ld\n", lo);
+
+ if (ucisxdigit(0xfeff))
+ printf("0xFEFF HEX DIGIT\n");
+ else
+ printf("0xFEFF NOT HEX DIGIT\n");
+
+ if (ucisdefined(0x10000))
+ printf("0x10000 DEFINED\n");
+ else
+ printf("0x10000 NOT DEFINED\n");
+
+ if (ucnumber_lookup(0x30, &num)) {
+ if (num.denominator != 1)
+ printf("UCNUMBER: 0x30 = %d/%d\n", num.numerator, num.denominator);
+ else
+ printf("UCNUMBER: 0x30 = %d\n", num.numerator);
+ } else
+ printf("UCNUMBER: 0x30 NOT A NUMBER\n");
+
+ if (ucnumber_lookup(0xbc, &num)) {
+ if (num.denominator != 1)
+ printf("UCNUMBER: 0xbc = %d/%d\n", num.numerator, num.denominator);
+ else
+ printf("UCNUMBER: 0xbc = %d\n", num.numerator);
+ } else
+ printf("UCNUMBER: 0xbc NOT A NUMBER\n");
+
+
+ if (ucnumber_lookup(0xff19, &num)) {
+ if (num.denominator != 1)
+ printf("UCNUMBER: 0xff19 = %d/%d\n", num.numerator, num.denominator);
+ else
+ printf("UCNUMBER: 0xff19 = %d\n", num.numerator);
+ } else
+ printf("UCNUMBER: 0xff19 NOT A NUMBER\n");
+
+ if (ucnumber_lookup(0x4e00, &num)) {
+ if (num.denominator != 1)
+ printf("UCNUMBER: 0x4e00 = %d/%d\n", num.numerator, num.denominator);
+ else
+ printf("UCNUMBER: 0x4e00 = %d\n", num.numerator);
+ } else
+ printf("UCNUMBER: 0x4e00 NOT A NUMBER\n");
+
+ if (ucdigit_lookup(0x06f9, &dig))
+ printf("UCDIGIT: 0x6f9 = %d\n", dig);
+ else
+ printf("UCDIGIT: 0x6f9 NOT A NUMBER\n");
+
+ dig = ucgetdigit(0x0969);
+ printf("UCGETDIGIT: 0x969 = %d\n", dig);
+
+ num = ucgetnumber(0x30);
+ if (num.denominator != 1)
+ printf("UCGETNUMBER: 0x30 = %d/%d\n", num.numerator, num.denominator);
+ else
+ printf("UCGETNUMBER: 0x30 = %d\n", num.numerator);
+
+ num = ucgetnumber(0xbc);
+ if (num.denominator != 1)
+ printf("UCGETNUMBER: 0xbc = %d/%d\n", num.numerator, num.denominator);
+ else
+ printf("UCGETNUMBER: 0xbc = %d\n", num.numerator);
+
+ num = ucgetnumber(0xff19);
+ if (num.denominator != 1)
+ printf("UCGETNUMBER: 0xff19 = %d/%d\n", num.numerator, num.denominator);
+ else
+ printf("UCGETNUMBER: 0xff19 = %d\n", num.numerator);
+
+/* ucdata_cleanup(); */
+ exit(0);
+}
+
+#endif /* TEST */
diff --git a/libraries/liblunicode/ucdata/ucdata.h b/libraries/liblunicode/ucdata/ucdata.h
new file mode 100644
index 0000000..3067463
--- /dev/null
+++ b/libraries/liblunicode/ucdata/ucdata.h
@@ -0,0 +1,364 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Copyright 2001 Computing Research Labs, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+ * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* $Id: ucdata.h,v 1.6 2001/01/02 18:46:20 mleisher Exp $ */
+
+#ifndef _h_ucdata
+#define _h_ucdata
+
+LDAP_BEGIN_DECL
+
+#define UCDATA_VERSION "2.4"
+
+/**************************************************************************
+ *
+ * Masks and macros for character properties.
+ *
+ **************************************************************************/
+
+/*
+ * Values that can appear in the `mask1' parameter of the ucisprop()
+ * function.
+ */
+#define UC_MN 0x00000001 /* Mark, Non-Spacing */
+#define UC_MC 0x00000002 /* Mark, Spacing Combining */
+#define UC_ME 0x00000004 /* Mark, Enclosing */
+#define UC_ND 0x00000008 /* Number, Decimal Digit */
+#define UC_NL 0x00000010 /* Number, Letter */
+#define UC_NO 0x00000020 /* Number, Other */
+#define UC_ZS 0x00000040 /* Separator, Space */
+#define UC_ZL 0x00000080 /* Separator, Line */
+#define UC_ZP 0x00000100 /* Separator, Paragraph */
+#define UC_CC 0x00000200 /* Other, Control */
+#define UC_CF 0x00000400 /* Other, Format */
+#define UC_OS 0x00000800 /* Other, Surrogate */
+#define UC_CO 0x00001000 /* Other, Private Use */
+#define UC_CN 0x00002000 /* Other, Not Assigned */
+#define UC_LU 0x00004000 /* Letter, Uppercase */
+#define UC_LL 0x00008000 /* Letter, Lowercase */
+#define UC_LT 0x00010000 /* Letter, Titlecase */
+#define UC_LM 0x00020000 /* Letter, Modifier */
+#define UC_LO 0x00040000 /* Letter, Other */
+#define UC_PC 0x00080000 /* Punctuation, Connector */
+#define UC_PD 0x00100000 /* Punctuation, Dash */
+#define UC_PS 0x00200000 /* Punctuation, Open */
+#define UC_PE 0x00400000 /* Punctuation, Close */
+#define UC_PO 0x00800000 /* Punctuation, Other */
+#define UC_SM 0x01000000 /* Symbol, Math */
+#define UC_SC 0x02000000 /* Symbol, Currency */
+#define UC_SK 0x04000000 /* Symbol, Modifier */
+#define UC_SO 0x08000000 /* Symbol, Other */
+#define UC_L 0x10000000 /* Left-To-Right */
+#define UC_R 0x20000000 /* Right-To-Left */
+#define UC_EN 0x40000000 /* European Number */
+#define UC_ES 0x80000000 /* European Number Separator */
+
+/*
+ * Values that can appear in the `mask2' parameter of the ucisprop()
+ * function.
+ */
+#define UC_ET 0x00000001 /* European Number Terminator */
+#define UC_AN 0x00000002 /* Arabic Number */
+#define UC_CS 0x00000004 /* Common Number Separator */
+#define UC_B 0x00000008 /* Block Separator */
+#define UC_S 0x00000010 /* Segment Separator */
+#define UC_WS 0x00000020 /* Whitespace */
+#define UC_ON 0x00000040 /* Other Neutrals */
+/*
+ * Implementation specific character properties.
+ */
+#define UC_CM 0x00000080 /* Composite */
+#define UC_NB 0x00000100 /* Non-Breaking */
+#define UC_SY 0x00000200 /* Symmetric */
+#define UC_HD 0x00000400 /* Hex Digit */
+#define UC_QM 0x00000800 /* Quote Mark */
+#define UC_MR 0x00001000 /* Mirroring */
+#define UC_SS 0x00002000 /* Space, other */
+
+#define UC_CP 0x00004000 /* Defined */
+
+/*
+ * Added for UnicodeData-2.1.3.
+ */
+#define UC_PI 0x00008000 /* Punctuation, Initial */
+#define UC_PF 0x00010000 /* Punctuation, Final */
+
+/*
+ * This is the primary function for testing to see if a character has some set
+ * of properties. The macros that test for various character properties all
+ * call this function with some set of masks.
+ */
+LDAP_LUNICODE_F (int)
+ucisprop LDAP_P((ac_uint4 code, ac_uint4 mask1, ac_uint4 mask2));
+
+#define ucisalpha(cc) ucisprop(cc, UC_LU|UC_LL|UC_LM|UC_LO|UC_LT, 0)
+#define ucisdigit(cc) ucisprop(cc, UC_ND, 0)
+#define ucisalnum(cc) ucisprop(cc, UC_LU|UC_LL|UC_LM|UC_LO|UC_LT|UC_ND, 0)
+#define uciscntrl(cc) ucisprop(cc, UC_CC|UC_CF, 0)
+#define ucisspace(cc) ucisprop(cc, UC_ZS|UC_SS, 0)
+#define ucisblank(cc) ucisprop(cc, UC_ZS, 0)
+#define ucispunct(cc) ucisprop(cc, UC_PD|UC_PS|UC_PE|UC_PO, UC_PI|UC_PF)
+#define ucisgraph(cc) ucisprop(cc, UC_MN|UC_MC|UC_ME|UC_ND|UC_NL|UC_NO|\
+ UC_LU|UC_LL|UC_LT|UC_LM|UC_LO|UC_PC|UC_PD|\
+ UC_PS|UC_PE|UC_PO|UC_SM|UC_SM|UC_SC|UC_SK|\
+ UC_SO, UC_PI|UC_PF)
+#define ucisprint(cc) ucisprop(cc, UC_MN|UC_MC|UC_ME|UC_ND|UC_NL|UC_NO|\
+ UC_LU|UC_LL|UC_LT|UC_LM|UC_LO|UC_PC|UC_PD|\
+ UC_PS|UC_PE|UC_PO|UC_SM|UC_SM|UC_SC|UC_SK|\
+ UC_SO|UC_ZS, UC_PI|UC_PF)
+#define ucisupper(cc) ucisprop(cc, UC_LU, 0)
+#define ucislower(cc) ucisprop(cc, UC_LL, 0)
+#define ucistitle(cc) ucisprop(cc, UC_LT, 0)
+#define ucisxdigit(cc) ucisprop(cc, 0, UC_HD)
+
+#define ucisisocntrl(cc) ucisprop(cc, UC_CC, 0)
+#define ucisfmtcntrl(cc) ucisprop(cc, UC_CF, 0)
+
+#define ucissymbol(cc) ucisprop(cc, UC_SM|UC_SC|UC_SO|UC_SK, 0)
+#define ucisnumber(cc) ucisprop(cc, UC_ND|UC_NO|UC_NL, 0)
+#define ucisnonspacing(cc) ucisprop(cc, UC_MN, 0)
+#define ucisopenpunct(cc) ucisprop(cc, UC_PS, 0)
+#define ucisclosepunct(cc) ucisprop(cc, UC_PE, 0)
+#define ucisinitialpunct(cc) ucisprop(cc, 0, UC_PI)
+#define ucisfinalpunct(cc) ucisprop(cc, 0, UC_PF)
+
+#define uciscomposite(cc) ucisprop(cc, 0, UC_CM)
+#define ucishex(cc) ucisprop(cc, 0, UC_HD)
+#define ucisquote(cc) ucisprop(cc, 0, UC_QM)
+#define ucissymmetric(cc) ucisprop(cc, 0, UC_SY)
+#define ucismirroring(cc) ucisprop(cc, 0, UC_MR)
+#define ucisnonbreaking(cc) ucisprop(cc, 0, UC_NB)
+
+/*
+ * Directionality macros.
+ */
+#define ucisrtl(cc) ucisprop(cc, UC_R, 0)
+#define ucisltr(cc) ucisprop(cc, UC_L, 0)
+#define ucisstrong(cc) ucisprop(cc, UC_L|UC_R, 0)
+#define ucisweak(cc) ucisprop(cc, UC_EN|UC_ES, UC_ET|UC_AN|UC_CS)
+#define ucisneutral(cc) ucisprop(cc, 0, UC_B|UC_S|UC_WS|UC_ON)
+#define ucisseparator(cc) ucisprop(cc, 0, UC_B|UC_S)
+
+/*
+ * Other macros inspired by John Cowan.
+ */
+#define ucismark(cc) ucisprop(cc, UC_MN|UC_MC|UC_ME, 0)
+#define ucismodif(cc) ucisprop(cc, UC_LM, 0)
+#define ucisletnum(cc) ucisprop(cc, UC_NL, 0)
+#define ucisconnect(cc) ucisprop(cc, UC_PC, 0)
+#define ucisdash(cc) ucisprop(cc, UC_PD, 0)
+#define ucismath(cc) ucisprop(cc, UC_SM, 0)
+#define uciscurrency(cc) ucisprop(cc, UC_SC, 0)
+#define ucismodifsymbol(cc) ucisprop(cc, UC_SK, 0)
+#define ucisnsmark(cc) ucisprop(cc, UC_MN, 0)
+#define ucisspmark(cc) ucisprop(cc, UC_MC, 0)
+#define ucisenclosing(cc) ucisprop(cc, UC_ME, 0)
+#define ucisprivate(cc) ucisprop(cc, UC_CO, 0)
+#define ucissurrogate(cc) ucisprop(cc, UC_OS, 0)
+#define ucislsep(cc) ucisprop(cc, UC_ZL, 0)
+#define ucispsep(cc) ucisprop(cc, UC_ZP, 0)
+
+#define ucisidentstart(cc) ucisprop(cc, UC_LU|UC_LL|UC_LT|UC_LO|UC_NL, 0)
+#define ucisidentpart(cc) ucisprop(cc, UC_LU|UC_LL|UC_LT|UC_LO|UC_NL|\
+ UC_MN|UC_MC|UC_ND|UC_PC|UC_CF, 0)
+
+#define ucisdefined(cc) ucisprop(cc, 0, UC_CP)
+#define ucisundefined(cc) !ucisprop(cc, 0, UC_CP)
+
+/*
+ * Other miscellaneous character property macros.
+ */
+#define ucishan(cc) (((cc) >= 0x4e00 && (cc) <= 0x9fff) ||\
+ ((cc) >= 0xf900 && (cc) <= 0xfaff))
+#define ucishangul(cc) ((cc) >= 0xac00 && (cc) <= 0xd7ff)
+
+/**************************************************************************
+ *
+ * Functions for case conversion.
+ *
+ **************************************************************************/
+
+LDAP_LUNICODE_F (ac_uint4) uctoupper LDAP_P((ac_uint4 code));
+LDAP_LUNICODE_F (ac_uint4) uctolower LDAP_P((ac_uint4 code));
+LDAP_LUNICODE_F (ac_uint4) uctotitle LDAP_P((ac_uint4 code));
+
+/**************************************************************************
+ *
+ * Functions for getting compositions.
+ *
+ **************************************************************************/
+
+/*
+ * This routine determines if there exists a composition of node1 and node2.
+ * If it returns 0, there is no composition. Any other value indicates a
+ * composition was returned in comp.
+ */
+LDAP_LUNICODE_F (int) uccomp LDAP_P((ac_uint4 node1, ac_uint4 node2,
+ ac_uint4 *comp));
+
+/*
+ * Does Hangul composition on the string str with length len, and returns
+ * the length of the composed string.
+ */
+LDAP_LUNICODE_F (int) uccomp_hangul LDAP_P((ac_uint4 *str, int len));
+
+/*
+ * Does canonical composition on the string str with length len, and returns
+ * the length of the composed string.
+ */
+LDAP_LUNICODE_F (int) uccanoncomp LDAP_P((ac_uint4 *str, int len));
+
+/**************************************************************************
+ *
+ * Functions for getting decompositions.
+ *
+ **************************************************************************/
+
+/*
+ * This routine determines if the code has a decomposition. If it returns 0,
+ * there is no decomposition. Any other value indicates a decomposition was
+ * returned.
+ */
+LDAP_LUNICODE_F (int)
+ucdecomp LDAP_P((ac_uint4 code, ac_uint4 *num,
+ ac_uint4 **decomp));
+
+/*
+ * Equivalent to ucdecomp() except that it includes compatibility
+ * decompositions.
+ */
+LDAP_LUNICODE_F (int)
+uckdecomp LDAP_P((ac_uint4 code, ac_uint4 *num,
+ ac_uint4 **decomp));
+
+/*
+ * If the code is a Hangul syllable, this routine decomposes it into the array
+ * passed. The array size should be at least 3.
+ */
+LDAP_LUNICODE_F (int)
+ucdecomp_hangul LDAP_P((ac_uint4 code, ac_uint4 *num,
+ ac_uint4 decomp[]));
+
+/*
+ * This routine does canonical decomposition of the string in of length
+ * inlen, and returns the decomposed string in out with length outlen.
+ * The memory for out is allocated by this routine. It returns the length
+ * of the decomposed string if okay, and -1 on error.
+ */
+LDAP_LUNICODE_F (int)
+uccanondecomp LDAP_P((const ac_uint4 *in, int inlen,
+ ac_uint4 **out, int *outlen, void *ctx));
+
+/*
+ * Equivalent to uccanondecomp() except that it includes compatibility
+ * decompositions.
+ */
+LDAP_LUNICODE_F (int)
+uccompatdecomp LDAP_P((const ac_uint4 *in, int inlen,
+ ac_uint4 **out, int *outlen, void *ctx));
+
+/**************************************************************************
+ *
+ * Functions for getting combining classes.
+ *
+ **************************************************************************/
+
+/*
+ * This will return the combining class for a character to be used with the
+ * Canonical Ordering algorithm.
+ */
+LDAP_LUNICODE_F (ac_uint4) uccombining_class LDAP_P((ac_uint4 code));
+
+/**************************************************************************
+ *
+ * Functions for getting numbers and digits.
+ *
+ **************************************************************************/
+
+struct ucnumber {
+ int numerator;
+ int denominator;
+};
+
+LDAP_LUNICODE_F (int)
+ucnumber_lookup LDAP_P((ac_uint4 code, struct ucnumber *num));
+
+LDAP_LUNICODE_F (int)
+ucdigit_lookup LDAP_P((ac_uint4 code, int *digit));
+
+/*
+ * For compatibility with John Cowan's "uctype" package.
+ */
+LDAP_LUNICODE_F (struct ucnumber) ucgetnumber LDAP_P((ac_uint4 code));
+LDAP_LUNICODE_F (int) ucgetdigit LDAP_P((ac_uint4 code));
+
+/**************************************************************************
+ *
+ * Functions library initialization and cleanup.
+ *
+ **************************************************************************/
+
+/*
+ * Macros for specifying the data tables to be loaded, unloaded, or reloaded
+ * by the ucdata_load(), ucdata_unload(), and ucdata_reload() routines.
+ */
+#define UCDATA_CASE 0x01
+#define UCDATA_CTYPE 0x02
+#define UCDATA_DECOMP 0x04
+#define UCDATA_CMBCL 0x08
+#define UCDATA_NUM 0x10
+#define UCDATA_COMP 0x20
+#define UCDATA_KDECOMP 0x40
+
+#define UCDATA_ALL (UCDATA_CASE|UCDATA_CTYPE|UCDATA_DECOMP|\
+ UCDATA_CMBCL|UCDATA_NUM|UCDATA_COMP|UCDATA_KDECOMP)
+
+/*
+ * Functions to load, unload, and reload specific data files.
+ */
+LDAP_LUNICODE_F (int) ucdata_load LDAP_P((char *paths, int mask));
+LDAP_LUNICODE_F (void) ucdata_unload LDAP_P((int mask));
+LDAP_LUNICODE_F (int) ucdata_reload LDAP_P((char *paths, int mask));
+
+#ifdef UCDATA_DEPRECATED
+/*
+ * Deprecated functions, now just compatibility macros.
+ */
+#define ucdata_setup(p) ucdata_load(p, UCDATA_ALL)
+#define ucdata_cleanup() ucdata_unload(UCDATA_ALL)
+#endif
+
+LDAP_END_DECL
+
+#endif /* _h_ucdata */
diff --git a/libraries/liblunicode/ucdata/ucdata.man b/libraries/liblunicode/ucdata/ucdata.man
new file mode 100644
index 0000000..7bee4be
--- /dev/null
+++ b/libraries/liblunicode/ucdata/ucdata.man
@@ -0,0 +1,504 @@
+.\"
+.\" $Id: ucdata.man,v 1.5 2001/01/02 18:46:20 mleisher Exp $
+.\"
+.TH ucdata 3 "03 January 2001"
+.SH NAME
+ucdata \- package for providing Unicode/ISO10646 character information
+
+.SH SYNOPSIS
+#include <ucdata.h>
+.sp
+void ucdata_load(char * paths, int masks)
+.sp
+void ucdata_unload(int masks)
+.sp
+void ucdata_reload(char * paths, int masks)
+.sp
+int ucdecomp(unsigned long code, unsigned long *num, unsigned long **decomp)
+.sp
+int uccanondecomp(const unsigned long *in, int inlen, unsigned long **out,
+int *outlen)
+.sp
+int ucdecomp_hangul(unsigned long code, unsigned long *num,
+unsigned long decomp[])
+.sp
+int uccomp(unsigned long ch1, unsigned long ch2, unsigned long *comp)
+.sp
+int uccomp_hangul(unsigned long *str, int len)
+.sp
+int uccanoncomp(unsigned long *str, int len)
+.nf
+struct ucnumber {
+ int numerator;
+ int denominator;
+};
+.sp
+int ucnumber_lookup(unsigned long code, struct ucnumber *num)
+.sp
+int ucdigit_lookup(unsigned long code, int *digit)
+.sp
+struct ucnumber ucgetnumber(unsigned long code)
+.sp
+int ucgetdigit(unsigned long code)
+.sp
+unsigned long uctoupper(unsigned long code)
+.sp
+unsigned long uctolower(unsigned long code)
+.sp
+unsigned long uctotitle(unsigned long code)
+.sp
+int ucisalpha(unsigned long code)
+.sp
+int ucisalnum(unsigned long code)
+.sp
+int ucisdigit(unsigned long code)
+.sp
+int uciscntrl(unsigned long code)
+.sp
+int ucisspace(unsigned long code)
+.sp
+int ucisblank(unsigned long code)
+.sp
+int ucispunct(unsigned long code)
+.sp
+int ucisgraph(unsigned long code)
+.sp
+int ucisprint(unsigned long code)
+.sp
+int ucisxdigit(unsigned long code)
+.sp
+int ucisupper(unsigned long code)
+.sp
+int ucislower(unsigned long code)
+.sp
+int ucistitle(unsigned long code)
+.sp
+int ucisisocntrl(unsigned long code)
+.sp
+int ucisfmtcntrl(unsigned long code)
+.sp
+int ucissymbol(unsigned long code)
+.sp
+int ucisnumber(unsigned long code)
+.sp
+int ucisnonspacing(unsigned long code)
+.sp
+int ucisopenpunct(unsigned long code)
+.sp
+int ucisclosepunct(unsigned long code)
+.sp
+int ucisinitialpunct(unsigned long code)
+.sp
+int ucisfinalpunct(unsigned long code)
+.sp
+int uciscomposite(unsigned long code)
+.sp
+int ucisquote(unsigned long code)
+.sp
+int ucissymmetric(unsigned long code)
+.sp
+int ucismirroring(unsigned long code)
+.sp
+int ucisnonbreaking(unsigned long code)
+.sp
+int ucisrtl(unsigned long code)
+.sp
+int ucisltr(unsigned long code)
+.sp
+int ucisstrong(unsigned long code)
+.sp
+int ucisweak(unsigned long code)
+.sp
+int ucisneutral(unsigned long code)
+.sp
+int ucisseparator(unsigned long code)
+.sp
+int ucislsep(unsigned long code)
+.sp
+int ucispsep(unsigned long code)
+.sp
+int ucismark(unsigned long code)
+.sp
+int ucisnsmark(unsigned long code)
+.sp
+int ucisspmark(unsigned long code)
+.sp
+int ucismodif(unsigned long code)
+.sp
+int ucismodifsymbol(unsigned long code)
+.sp
+int ucisletnum(unsigned long code)
+.sp
+int ucisconnect(unsigned long code)
+.sp
+int ucisdash(unsigned long code)
+.sp
+int ucismath(unsigned long code)
+.sp
+int uciscurrency(unsigned long code)
+.sp
+int ucisenclosing(unsigned long code)
+.sp
+int ucisprivate(unsigned long code)
+.sp
+int ucissurrogate(unsigned long code)
+.sp
+int ucisidentstart(unsigned long code)
+.sp
+int ucisidentpart(unsigned long code)
+.sp
+int ucisdefined(unsigned long code)
+.sp
+int ucisundefined(unsigned long code)
+.sp
+int ucishan(unsigned long code)
+.sp
+int ucishangul(unsigned long code)
+
+.SH DESCRIPTION
+.TP 4
+.BR Macros
+.br
+UCDATA_CASE
+.br
+UCDATA_CTYPE
+.br
+UCDATA_DECOMP
+.br
+UCDATA_CMBCL
+.br
+UCDATA_NUM
+.br
+UCDATA_ALL
+.br
+.TP 4
+.BR ucdata_load()
+This function initializes the UCData library by locating the data files in one
+of the colon-separated directories in the `paths' parameter. The data files
+to be loaded are specified in the `masks' parameter as a bitwise combination
+of the macros listed above.
+.sp
+This should be called before using any of the other functions.
+.TP 4
+.BR ucdata_unload()
+This function unloads the data tables specified in the `masks' parameter.
+.sp
+This function should be called when the application is done using the UCData
+package.
+.TP 4
+.BR ucdata_reload()
+This function reloads the data files from one of the colon-separated
+directories in the `paths' parameter. The data files to be reloaded are
+specified in the `masks' parameter as a bitwise combination of the macros
+listed above.
+.TP 4
+.BR ucdecomp()
+This function determines if a character has a decomposition and returns the
+decomposition information if it exists.
+.sp
+If a zero is returned, there is no decomposition. If a non-zero is
+returned, then the `num' and `decomp' variables are filled in with the
+appropriate values.
+.sp
+Example call:
+.sp
+.nf
+ unsigned long i, num, *decomp;
+
+ if (ucdecomp(0x1d5, &num, &decomp) != 0) {
+ for (i = 0; i < num; i++)
+ printf("0x%08lX,", decomp[i]);
+ putchar('\n');
+ }
+.TP 4
+.BR uccanondecomp()
+This function will decompose a string, insuring the characters are in
+canonical order for comparison.
+.sp
+If a decomposed string is returned, the caller is responsible for deallocating
+the string.
+.sp
+If a -1 is returned, memory allocation failed. If a zero is returned, no
+decomposition was done. Any other value means a decomposition string was
+created and the values returned in the `out' and `outlen' parameters.
+.TP 4
+.BR ucdecomp_hangul()
+This function determines if a Hangul syllable has a
+decomposition and returns the decomposition information.
+.sp
+An array of at least size 3 should be passed to the function
+for the decomposition of the syllable.
+.sp
+If a zero is returned, the character is not a Hangul
+syllable. If a non-zero is returned, the `num' field
+will be 2 or 3 and the syllable will be decomposed into
+the `decomp' array arithmetically.
+.sp
+Example call:
+.sp
+.nf
+ unsigned long i, num, decomp[3];
+
+ if (ucdecomp_hangul(0xb1ba, &num, &decomp) != 0) {
+ for (i = 0; i < num; i++)
+ printf("0x%08lX,", decomp[i]);
+ putchar('\n');
+ }
+.TP 4
+.BR uccomp()
+This function determines if a pair of characters have a composition, and
+returns that composition if one exists.
+.sp
+A zero is returned is no composition exists for the character pair. Any other
+value indicates the `comp' field holds the character code representing the
+composition of the two character codes.
+.TP 4
+.BR uccomp_hangul()
+This composes the Hangul Jamo in-place in the string.
+.sp
+The returned value is the new length of the string.
+.TP 4
+.BR uccanoncomp()
+This function does a full composition in-place in the string, including the
+Hangul composition.
+.sp
+The returned value is the new length of the string.
+.TP 4
+.BR ucnumber_lookup()
+This function determines if the code is a number and
+fills in the `num' field with the numerator and
+denominator. If the code happens to be a single digit,
+the numerator and denominator fields will be the same.
+.sp
+If the function returns 0, the code is not a number.
+Any other return value means the code is a number.
+.TP 4
+.BR ucdigit_lookup()
+This function determines if the code is a digit and
+fills in the `digit' field with the digit value.
+.sp
+If the function returns 0, the code is not a number.
+Any other return value means the code is a number.
+.TP 4
+.BR ucgetnumber()
+This is a compatibility function with John Cowan's
+"uctype" package. It uses ucnumber_lookup().
+.TP 4
+.BR ucgetdigit()
+This is a compatibility function with John Cowan's
+"uctype" package. It uses ucdigit_lookup().
+.TP 4
+.BR uctoupper()
+This function returns the code unchanged if it is
+already upper case or has no upper case equivalent.
+Otherwise the upper case equivalent is returned.
+.TP 4
+.BR uctolower()
+This function returns the code unchanged if it is
+already lower case or has no lower case equivalent.
+Otherwise the lower case equivalent is returned.
+.TP 4
+.BR uctotitle()
+This function returns the code unchanged if it is
+already title case or has no title case equivalent.
+Otherwise the title case equivalent is returned.
+.TP 4
+.BR ucisalpha()
+Test if \fIcode\fR is an alpha character.
+.TP 4
+.BR ucisalnum()
+Test if \fIcode\fR is an alpha or digit character.
+.TP 4
+.BR ucisdigit()
+Test if \fIcode\fR is a digit character.
+.TP 4
+.BR uciscntrl()
+Test if \fIcode\fR is a control character.
+.TP 4
+.BR ucisspace()
+Test if \fIcode\fR is a space character.
+.TP 4
+.BR ucisblank()
+Test if \fIcode\fR is a blank character.
+.TP 4
+.BR ucispunct()
+Test if \fIcode\fR is a punctuation character.
+.TP 4
+.BR ucisgraph()
+Test if \fIcode\fR is a graphical (visible) character.
+.TP 4
+.BR ucisprint()
+Test if \fIcode\fR is a printable character.
+.TP 4
+.BR ucisxdigit()
+Test if \fIcode\fR is a hexadecimal digit character.
+.TP 4
+.BR ucisupper()
+Test if \fIcode\fR is an upper case character.
+.TP 4
+.BR ucislower()
+Test if \fIcode\fR is a lower case character.
+.TP 4
+.BR ucistitle()
+Test if \fIcode\fR is a title case character.
+.TP 4
+.BR ucisisocntrl()
+Is the character a C0 control character (< 32)?
+.TP 4
+.BR ucisfmtcntrl()
+Is the character a format control character?
+.TP 4
+.BR ucissymbol()
+Is the character a symbol?
+.TP 4
+.BR ucisnumber()
+Is the character a number or digit?
+.TP 4
+.BR ucisnonspacing()
+Is the character non-spacing?
+.TP 4
+.BR ucisopenpunct()
+Is the character an open/left punctuation (i.e. '[')
+.TP 4
+.BR ucisclosepunct()
+Is the character an close/right punctuation (i.e. ']')
+.TP 4
+.BR ucisinitialpunct()
+Is the character an initial punctuation (i.e. U+2018 LEFT
+SINGLE QUOTATION MARK)
+.TP 4
+.BR ucisfinalpunct()
+Is the character a final punctuation (i.e. U+2019 RIGHT
+SINGLE QUOTATION MARK)
+.TP 4
+.BR uciscomposite()
+Can the character be decomposed into a set of other
+characters?
+.TP 4
+.BR ucisquote()
+Is the character one of the many quotation marks?
+.TP 4
+.BR ucissymmetric()
+Is the character one that has an opposite form
+(i.e. <>)
+.TP 4
+.BR ucismirroring()
+Is the character mirroring (superset of symmetric)?
+.TP 4
+.BR ucisnonbreaking()
+Is the character non-breaking (i.e. non-breaking
+space)?
+.TP 4
+.BR ucisrtl()
+Does the character have strong right-to-left
+directionality (i.e. Arabic letters)?
+.TP 4
+.BR ucisltr()
+Does the character have strong left-to-right
+directionality (i.e. Latin letters)?
+.TP 4
+.BR ucisstrong()
+Does the character have strong directionality?
+.TP 4
+.BR ucisweak()
+Does the character have weak directionality
+(i.e. numbers)?
+.TP 4
+.BR ucisneutral()
+Does the character have neutral directionality
+(i.e. whitespace)?
+.TP 4
+.BR ucisseparator()
+Is the character a block or segment separator?
+.TP 4
+.BR ucislsep()
+Is the character a line separator?
+.TP 4
+.BR ucispsep()
+Is the character a paragraph separator?
+.TP 4
+.BR ucismark()
+Is the character a mark of some kind?
+.TP 4
+.BR ucisnsmark()
+Is the character a non-spacing mark?
+.TP 4
+.BR ucisspmark()
+Is the character a spacing mark?
+.TP 4
+.BR ucismodif()
+Is the character a modifier letter?
+.TP 4
+.BR ucismodifsymbol()
+Is the character a modifier symbol?
+.TP 4
+.BR ucisletnum()
+Is the character a number represented by a letter?
+.TP 4
+.BR ucisconnect()
+Is the character connecting punctuation?
+.TP 4
+.BR ucisdash()
+Is the character dash punctuation?
+.TP 4
+.BR ucismath()
+Is the character a math character?
+.TP 4
+.BR uciscurrency()
+Is the character a currency character?
+.TP 4
+.BR ucisenclosing()
+Is the character enclosing (i.e. enclosing box)?
+.TP 4
+.BR ucisprivate()
+Is the character from the Private Use Area?
+.TP 4
+.BR ucissurrogate()
+Is the character one of the surrogate codes?
+.TP 4
+.BR ucisidentstart()
+Is the character a legal initial character of an identifier?
+.TP 4
+.BR ucisidentpart()
+Is the character a legal identifier character?
+.TP 4
+.BR ucisdefined()
+Is the character defined (appeared in one of the data
+files)?
+.TP 4
+.BR ucisundefined()
+Is the character not defined (non-Unicode)?
+.TP 4
+.BR ucishan()
+Is the character a Han ideograph?
+.TP 4
+.BR ucishangul()
+Is the character a pre-composed Hangul syllable?
+
+.SH "SEE ALSO"
+ctype(3)
+
+.SH ACKNOWLEDGMENTS
+These are people who have helped with patches or
+alerted me about problems.
+.sp
+John Cowan <cowan@locke.ccil.org>
+.br
+Bob Verbrugge <bob_verbrugge@nl.compuware.com>
+.br
+Christophe Pierret <cpierret@businessobjects.com>
+.br
+Kent Johnson <kent@pondview.mv.com>
+.br
+Valeriy E. Ushakov <uwe@ptc.spbu.ru>
+.br
+Stig Venaas <Stig.Venaas@uninett.no>
+
+.SH AUTHOR
+Mark Leisher
+.br
+Computing Research Lab
+.br
+New Mexico State University
+.br
+Email: mleisher@crl.nmsu.edu
diff --git a/libraries/liblunicode/ucdata/ucgendat.c b/libraries/liblunicode/ucdata/ucgendat.c
new file mode 100644
index 0000000..6187756
--- /dev/null
+++ b/libraries/liblunicode/ucdata/ucgendat.c
@@ -0,0 +1,1960 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Copyright 2001 Computing Research Labs, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+ * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* $Id: ucgendat.c,v 1.4 2001/01/02 18:46:20 mleisher Exp $" */
+
+#include "portable.h"
+#include "ldap_config.h"
+
+#include <stdio.h>
+#include <ac/ctype.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include <ac/bytes.h>
+
+#include <lutil.h>
+
+#ifndef HARDCODE_DATA
+#define HARDCODE_DATA 1
+#endif
+
+#undef ishdigit
+#define ishdigit(cc) (((cc) >= '0' && (cc) <= '9') ||\
+ ((cc) >= 'A' && (cc) <= 'F') ||\
+ ((cc) >= 'a' && (cc) <= 'f'))
+
+/*
+ * A header written to the output file with the byte-order-mark and the number
+ * of property nodes.
+ */
+static ac_uint2 hdr[2] = {0xfeff, 0};
+
+#define NUMPROPS 50
+#define NEEDPROPS (NUMPROPS + (4 - (NUMPROPS & 3)))
+
+typedef struct {
+ char *name;
+ int len;
+} _prop_t;
+
+/*
+ * List of properties expected to be found in the Unicode Character Database
+ * including some implementation specific properties.
+ *
+ * The implementation specific properties are:
+ * Cm = Composed (can be decomposed)
+ * Nb = Non-breaking
+ * Sy = Symmetric (has left and right forms)
+ * Hd = Hex digit
+ * Qm = Quote marks
+ * Mr = Mirroring
+ * Ss = Space, other
+ * Cp = Defined character
+ */
+static _prop_t props[NUMPROPS] = {
+ {"Mn", 2}, {"Mc", 2}, {"Me", 2}, {"Nd", 2}, {"Nl", 2}, {"No", 2},
+ {"Zs", 2}, {"Zl", 2}, {"Zp", 2}, {"Cc", 2}, {"Cf", 2}, {"Cs", 2},
+ {"Co", 2}, {"Cn", 2}, {"Lu", 2}, {"Ll", 2}, {"Lt", 2}, {"Lm", 2},
+ {"Lo", 2}, {"Pc", 2}, {"Pd", 2}, {"Ps", 2}, {"Pe", 2}, {"Po", 2},
+ {"Sm", 2}, {"Sc", 2}, {"Sk", 2}, {"So", 2}, {"L", 1}, {"R", 1},
+ {"EN", 2}, {"ES", 2}, {"ET", 2}, {"AN", 2}, {"CS", 2}, {"B", 1},
+ {"S", 1}, {"WS", 2}, {"ON", 2},
+ {"Cm", 2}, {"Nb", 2}, {"Sy", 2}, {"Hd", 2}, {"Qm", 2}, {"Mr", 2},
+ {"Ss", 2}, {"Cp", 2}, {"Pi", 2}, {"Pf", 2}, {"AL", 2}
+};
+
+typedef struct {
+ ac_uint4 *ranges;
+ ac_uint2 used;
+ ac_uint2 size;
+} _ranges_t;
+
+static _ranges_t proptbl[NUMPROPS];
+
+/*
+ * Make sure this array is sized to be on a 4-byte boundary at compile time.
+ */
+static ac_uint2 propcnt[NEEDPROPS];
+
+/*
+ * Array used to collect a decomposition before adding it to the decomposition
+ * table.
+ */
+static ac_uint4 dectmp[64];
+static ac_uint4 dectmp_size;
+
+typedef struct {
+ ac_uint4 code;
+ ac_uint2 size;
+ ac_uint2 used;
+ ac_uint4 *decomp;
+} _decomp_t;
+
+/*
+ * List of decomposition. Created and expanded in order as the characters are
+ * encountered. First list contains canonical mappings, second also includes
+ * compatibility mappings.
+ */
+static _decomp_t *decomps;
+static ac_uint4 decomps_used;
+static ac_uint4 decomps_size;
+
+static _decomp_t *kdecomps;
+static ac_uint4 kdecomps_used;
+static ac_uint4 kdecomps_size;
+
+/*
+ * Composition exclusion table stuff.
+ */
+#define COMPEX_SET(c) (compexs[(c) >> 5] |= (1 << ((c) & 31)))
+#define COMPEX_TEST(c) (compexs[(c) >> 5] & (1 << ((c) & 31)))
+static ac_uint4 compexs[8192];
+
+/*
+ * Struct for holding a composition pair, and array of composition pairs
+ */
+typedef struct {
+ ac_uint4 comp;
+ ac_uint4 count;
+ ac_uint4 code1;
+ ac_uint4 code2;
+} _comp_t;
+
+static _comp_t *comps;
+static ac_uint4 comps_used;
+
+/*
+ * Types and lists for handling lists of case mappings.
+ */
+typedef struct {
+ ac_uint4 key;
+ ac_uint4 other1;
+ ac_uint4 other2;
+} _case_t;
+
+static _case_t *upper;
+static _case_t *lower;
+static _case_t *title;
+static ac_uint4 upper_used;
+static ac_uint4 upper_size;
+static ac_uint4 lower_used;
+static ac_uint4 lower_size;
+static ac_uint4 title_used;
+static ac_uint4 title_size;
+
+/*
+ * Array used to collect case mappings before adding them to a list.
+ */
+static ac_uint4 cases[3];
+
+/*
+ * An array to hold ranges for combining classes.
+ */
+static ac_uint4 *ccl;
+static ac_uint4 ccl_used;
+static ac_uint4 ccl_size;
+
+/*
+ * Structures for handling numbers.
+ */
+typedef struct {
+ ac_uint4 code;
+ ac_uint4 idx;
+} _codeidx_t;
+
+typedef struct {
+ short numerator;
+ short denominator;
+} _num_t;
+
+/*
+ * Arrays to hold the mapping of codes to numbers.
+ */
+static _codeidx_t *ncodes;
+static ac_uint4 ncodes_used;
+static ac_uint4 ncodes_size;
+
+static _num_t *nums;
+static ac_uint4 nums_used;
+static ac_uint4 nums_size;
+
+/*
+ * Array for holding numbers.
+ */
+static _num_t *nums;
+static ac_uint4 nums_used;
+static ac_uint4 nums_size;
+
+static void
+add_range(ac_uint4 start, ac_uint4 end, char *p1, char *p2)
+{
+ int i, j, k, len;
+ _ranges_t *rlp;
+ char *name;
+
+ for (k = 0; k < 2; k++) {
+ if (k == 0) {
+ name = p1;
+ len = 2;
+ } else {
+ if (p2 == 0)
+ break;
+
+ name = p2;
+ len = 1;
+ }
+
+ for (i = 0; i < NUMPROPS; i++) {
+ if (props[i].len == len && memcmp(props[i].name, name, len) == 0)
+ break;
+ }
+
+ if (i == NUMPROPS)
+ continue;
+
+ rlp = &proptbl[i];
+
+ /*
+ * Resize the range list if necessary.
+ */
+ if (rlp->used == rlp->size) {
+ if (rlp->size == 0)
+ rlp->ranges = (ac_uint4 *)
+ malloc(sizeof(ac_uint4) << 3);
+ else
+ rlp->ranges = (ac_uint4 *)
+ realloc((char *) rlp->ranges,
+ sizeof(ac_uint4) * (rlp->size + 8));
+ rlp->size += 8;
+ }
+
+ /*
+ * If this is the first code for this property list, just add it
+ * and return.
+ */
+ if (rlp->used == 0) {
+ rlp->ranges[0] = start;
+ rlp->ranges[1] = end;
+ rlp->used += 2;
+ continue;
+ }
+
+ /*
+ * Optimize the case of adding the range to the end.
+ */
+ j = rlp->used - 1;
+ if (start > rlp->ranges[j]) {
+ j = rlp->used;
+ rlp->ranges[j++] = start;
+ rlp->ranges[j++] = end;
+ rlp->used = j;
+ continue;
+ }
+
+ /*
+ * Need to locate the insertion point.
+ */
+ for (i = 0;
+ i < rlp->used && start > rlp->ranges[i + 1] + 1; i += 2) ;
+
+ /*
+ * If the start value lies in the current range, then simply set the
+ * new end point of the range to the end value passed as a parameter.
+ */
+ if (rlp->ranges[i] <= start && start <= rlp->ranges[i + 1] + 1) {
+ rlp->ranges[i + 1] = end;
+ return;
+ }
+
+ /*
+ * Shift following values up by two.
+ */
+ for (j = rlp->used; j > i; j -= 2) {
+ rlp->ranges[j] = rlp->ranges[j - 2];
+ rlp->ranges[j + 1] = rlp->ranges[j - 1];
+ }
+
+ /*
+ * Add the new range at the insertion point.
+ */
+ rlp->ranges[i] = start;
+ rlp->ranges[i + 1] = end;
+ rlp->used += 2;
+ }
+}
+
+static void
+ordered_range_insert(ac_uint4 c, char *name, int len)
+{
+ int i, j;
+ ac_uint4 s, e;
+ _ranges_t *rlp;
+
+ if (len == 0)
+ return;
+
+ /*
+ * Deal with directionality codes introduced in Unicode 3.0.
+ */
+ if ((len == 2 && memcmp(name, "BN", 2) == 0) ||
+ (len == 3 &&
+ (memcmp(name, "NSM", 3) == 0 || memcmp(name, "PDF", 3) == 0 ||
+ memcmp(name, "LRE", 3) == 0 || memcmp(name, "LRO", 3) == 0 ||
+ memcmp(name, "RLE", 3) == 0 || memcmp(name, "RLO", 3) == 0))) {
+ /*
+ * Mark all of these as Other Neutral to preserve compatibility with
+ * older versions.
+ */
+ len = 2;
+ name = "ON";
+ }
+
+ for (i = 0; i < NUMPROPS; i++) {
+ if (props[i].len == len && memcmp(props[i].name, name, len) == 0)
+ break;
+ }
+
+ if (i == NUMPROPS)
+ return;
+
+ /*
+ * Have a match, so insert the code in order.
+ */
+ rlp = &proptbl[i];
+
+ /*
+ * Resize the range list if necessary.
+ */
+ if (rlp->used == rlp->size) {
+ if (rlp->size == 0)
+ rlp->ranges = (ac_uint4 *)
+ malloc(sizeof(ac_uint4) << 3);
+ else
+ rlp->ranges = (ac_uint4 *)
+ realloc((char *) rlp->ranges,
+ sizeof(ac_uint4) * (rlp->size + 8));
+ rlp->size += 8;
+ }
+
+ /*
+ * If this is the first code for this property list, just add it
+ * and return.
+ */
+ if (rlp->used == 0) {
+ rlp->ranges[0] = rlp->ranges[1] = c;
+ rlp->used += 2;
+ return;
+ }
+
+ /*
+ * Optimize the cases of extending the last range and adding new ranges to
+ * the end.
+ */
+ j = rlp->used - 1;
+ e = rlp->ranges[j];
+ s = rlp->ranges[j - 1];
+
+ if (c == e + 1) {
+ /*
+ * Extend the last range.
+ */
+ rlp->ranges[j] = c;
+ return;
+ }
+
+ if (c > e + 1) {
+ /*
+ * Start another range on the end.
+ */
+ j = rlp->used;
+ rlp->ranges[j] = rlp->ranges[j + 1] = c;
+ rlp->used += 2;
+ return;
+ }
+
+ if (c >= s)
+ /*
+ * The code is a duplicate of a code in the last range, so just return.
+ */
+ return;
+
+ /*
+ * The code should be inserted somewhere before the last range in the
+ * list. Locate the insertion point.
+ */
+ for (i = 0;
+ i < rlp->used && c > rlp->ranges[i + 1] + 1; i += 2) ;
+
+ s = rlp->ranges[i];
+ e = rlp->ranges[i + 1];
+
+ if (c == e + 1)
+ /*
+ * Simply extend the current range.
+ */
+ rlp->ranges[i + 1] = c;
+ else if (c < s) {
+ /*
+ * Add a new entry before the current location. Shift all entries
+ * before the current one up by one to make room.
+ */
+ for (j = rlp->used; j > i; j -= 2) {
+ rlp->ranges[j] = rlp->ranges[j - 2];
+ rlp->ranges[j + 1] = rlp->ranges[j - 1];
+ }
+ rlp->ranges[i] = rlp->ranges[i + 1] = c;
+
+ rlp->used += 2;
+ }
+}
+
+static void
+add_decomp(ac_uint4 code, short compat)
+{
+ ac_uint4 i, j, size;
+ _decomp_t **pdecomps;
+ ac_uint4 *pdecomps_used;
+ ac_uint4 *pdecomps_size;
+
+ if (compat) {
+ pdecomps = &kdecomps;
+ pdecomps_used = &kdecomps_used;
+ pdecomps_size = &kdecomps_size;
+ } else {
+ pdecomps = &decomps;
+ pdecomps_used = &decomps_used;
+ pdecomps_size = &decomps_size;
+ }
+
+ /*
+ * Add the code to the composite property.
+ */
+ if (!compat) {
+ ordered_range_insert(code, "Cm", 2);
+ }
+
+ /*
+ * Locate the insertion point for the code.
+ */
+ for (i = 0; i < *pdecomps_used && code > (*pdecomps)[i].code; i++) ;
+
+ /*
+ * Allocate space for a new decomposition.
+ */
+ if (*pdecomps_used == *pdecomps_size) {
+ if (*pdecomps_size == 0)
+ *pdecomps = (_decomp_t *) malloc(sizeof(_decomp_t) << 3);
+ else
+ *pdecomps = (_decomp_t *)
+ realloc((char *) *pdecomps,
+ sizeof(_decomp_t) * (*pdecomps_size + 8));
+ (void) memset((char *) (*pdecomps + *pdecomps_size), '\0',
+ sizeof(_decomp_t) << 3);
+ *pdecomps_size += 8;
+ }
+
+ if (i < *pdecomps_used && code != (*pdecomps)[i].code) {
+ /*
+ * Shift the decomps up by one if the codes don't match.
+ */
+ for (j = *pdecomps_used; j > i; j--)
+ (void) AC_MEMCPY((char *) &(*pdecomps)[j], (char *) &(*pdecomps)[j - 1],
+ sizeof(_decomp_t));
+ }
+
+ /*
+ * Insert or replace a decomposition.
+ */
+ size = dectmp_size + (4 - (dectmp_size & 3));
+ if ((*pdecomps)[i].size < size) {
+ if ((*pdecomps)[i].size == 0)
+ (*pdecomps)[i].decomp = (ac_uint4 *)
+ malloc(sizeof(ac_uint4) * size);
+ else
+ (*pdecomps)[i].decomp = (ac_uint4 *)
+ realloc((char *) (*pdecomps)[i].decomp,
+ sizeof(ac_uint4) * size);
+ (*pdecomps)[i].size = size;
+ }
+
+ if ((*pdecomps)[i].code != code)
+ (*pdecomps_used)++;
+
+ (*pdecomps)[i].code = code;
+ (*pdecomps)[i].used = dectmp_size;
+ (void) AC_MEMCPY((char *) (*pdecomps)[i].decomp, (char *) dectmp,
+ sizeof(ac_uint4) * dectmp_size);
+
+ /*
+ * NOTICE: This needs changing later so it is more general than simply
+ * pairs. This calculation is done here to simplify allocation elsewhere.
+ */
+ if (!compat && dectmp_size == 2)
+ comps_used++;
+}
+
+static void
+add_title(ac_uint4 code)
+{
+ ac_uint4 i, j;
+
+ /*
+ * Always map the code to itself.
+ */
+ cases[2] = code;
+
+ /*
+ * If the upper case character is not present, then make it the same as
+ * the title case.
+ */
+ if (cases[0] == 0)
+ cases[0] = code;
+
+ if (title_used == title_size) {
+ if (title_size == 0)
+ title = (_case_t *) malloc(sizeof(_case_t) << 3);
+ else
+ title = (_case_t *) realloc((char *) title,
+ sizeof(_case_t) * (title_size + 8));
+ title_size += 8;
+ }
+
+ /*
+ * Locate the insertion point.
+ */
+ for (i = 0; i < title_used && code > title[i].key; i++) ;
+
+ if (i < title_used) {
+ /*
+ * Shift the array up by one.
+ */
+ for (j = title_used; j > i; j--)
+ (void) AC_MEMCPY((char *) &title[j], (char *) &title[j - 1],
+ sizeof(_case_t));
+ }
+
+ title[i].key = cases[2]; /* Title */
+ title[i].other1 = cases[0]; /* Upper */
+ title[i].other2 = cases[1]; /* Lower */
+
+ title_used++;
+}
+
+static void
+add_upper(ac_uint4 code)
+{
+ ac_uint4 i, j;
+
+ /*
+ * Always map the code to itself.
+ */
+ cases[0] = code;
+
+ /*
+ * If the title case character is not present, then make it the same as
+ * the upper case.
+ */
+ if (cases[2] == 0)
+ cases[2] = code;
+
+ if (upper_used == upper_size) {
+ if (upper_size == 0)
+ upper = (_case_t *) malloc(sizeof(_case_t) << 3);
+ else
+ upper = (_case_t *) realloc((char *) upper,
+ sizeof(_case_t) * (upper_size + 8));
+ upper_size += 8;
+ }
+
+ /*
+ * Locate the insertion point.
+ */
+ for (i = 0; i < upper_used && code > upper[i].key; i++) ;
+
+ if (i < upper_used) {
+ /*
+ * Shift the array up by one.
+ */
+ for (j = upper_used; j > i; j--)
+ (void) AC_MEMCPY((char *) &upper[j], (char *) &upper[j - 1],
+ sizeof(_case_t));
+ }
+
+ upper[i].key = cases[0]; /* Upper */
+ upper[i].other1 = cases[1]; /* Lower */
+ upper[i].other2 = cases[2]; /* Title */
+
+ upper_used++;
+}
+
+static void
+add_lower(ac_uint4 code)
+{
+ ac_uint4 i, j;
+
+ /*
+ * Always map the code to itself.
+ */
+ cases[1] = code;
+
+ /*
+ * If the title case character is empty, then make it the same as the
+ * upper case.
+ */
+ if (cases[2] == 0)
+ cases[2] = cases[0];
+
+ if (lower_used == lower_size) {
+ if (lower_size == 0)
+ lower = (_case_t *) malloc(sizeof(_case_t) << 3);
+ else
+ lower = (_case_t *) realloc((char *) lower,
+ sizeof(_case_t) * (lower_size + 8));
+ lower_size += 8;
+ }
+
+ /*
+ * Locate the insertion point.
+ */
+ for (i = 0; i < lower_used && code > lower[i].key; i++) ;
+
+ if (i < lower_used) {
+ /*
+ * Shift the array up by one.
+ */
+ for (j = lower_used; j > i; j--)
+ (void) AC_MEMCPY((char *) &lower[j], (char *) &lower[j - 1],
+ sizeof(_case_t));
+ }
+
+ lower[i].key = cases[1]; /* Lower */
+ lower[i].other1 = cases[0]; /* Upper */
+ lower[i].other2 = cases[2]; /* Title */
+
+ lower_used++;
+}
+
+static void
+ordered_ccl_insert(ac_uint4 c, ac_uint4 ccl_code)
+{
+ ac_uint4 i, j;
+
+ if (ccl_used == ccl_size) {
+ if (ccl_size == 0)
+ ccl = (ac_uint4 *) malloc(sizeof(ac_uint4) * 24);
+ else
+ ccl = (ac_uint4 *)
+ realloc((char *) ccl, sizeof(ac_uint4) * (ccl_size + 24));
+ ccl_size += 24;
+ }
+
+ /*
+ * Optimize adding the first item.
+ */
+ if (ccl_used == 0) {
+ ccl[0] = ccl[1] = c;
+ ccl[2] = ccl_code;
+ ccl_used += 3;
+ return;
+ }
+
+ /*
+ * Handle the special case of extending the range on the end. This
+ * requires that the combining class codes are the same.
+ */
+ if (ccl_code == ccl[ccl_used - 1] && c == ccl[ccl_used - 2] + 1) {
+ ccl[ccl_used - 2] = c;
+ return;
+ }
+
+ /*
+ * Handle the special case of adding another range on the end.
+ */
+ if (c > ccl[ccl_used - 2] + 1 ||
+ (c == ccl[ccl_used - 2] + 1 && ccl_code != ccl[ccl_used - 1])) {
+ ccl[ccl_used++] = c;
+ ccl[ccl_used++] = c;
+ ccl[ccl_used++] = ccl_code;
+ return;
+ }
+
+ /*
+ * Locate either the insertion point or range for the code.
+ */
+ for (i = 0; i < ccl_used && c > ccl[i + 1] + 1; i += 3) ;
+
+ if (ccl_code == ccl[i + 2] && c == ccl[i + 1] + 1) {
+ /*
+ * Extend an existing range.
+ */
+ ccl[i + 1] = c;
+ return;
+ } else if (c < ccl[i]) {
+ /*
+ * Start a new range before the current location.
+ */
+ for (j = ccl_used; j > i; j -= 3) {
+ ccl[j] = ccl[j - 3];
+ ccl[j - 1] = ccl[j - 4];
+ ccl[j - 2] = ccl[j - 5];
+ }
+ ccl[i] = ccl[i + 1] = c;
+ ccl[i + 2] = ccl_code;
+ }
+}
+
+/*
+ * Adds a number if it does not already exist and returns an index value
+ * multiplied by 2.
+ */
+static ac_uint4
+make_number(short num, short denom)
+{
+ ac_uint4 n;
+
+ /*
+ * Determine if the number already exists.
+ */
+ for (n = 0; n < nums_used; n++) {
+ if (nums[n].numerator == num && nums[n].denominator == denom)
+ return n << 1;
+ }
+
+ if (nums_used == nums_size) {
+ if (nums_size == 0)
+ nums = (_num_t *) malloc(sizeof(_num_t) << 3);
+ else
+ nums = (_num_t *) realloc((char *) nums,
+ sizeof(_num_t) * (nums_size + 8));
+ nums_size += 8;
+ }
+
+ n = nums_used++;
+ nums[n].numerator = num;
+ nums[n].denominator = denom;
+
+ return n << 1;
+}
+
+static void
+add_number(ac_uint4 code, short num, short denom)
+{
+ ac_uint4 i, j;
+
+ /*
+ * Insert the code in order.
+ */
+ for (i = 0; i < ncodes_used && code > ncodes[i].code; i++) ;
+
+ /*
+ * Handle the case of the codes matching and simply replace the number
+ * that was there before.
+ */
+ if (i < ncodes_used && code == ncodes[i].code) {
+ ncodes[i].idx = make_number(num, denom);
+ return;
+ }
+
+ /*
+ * Resize the array if necessary.
+ */
+ if (ncodes_used == ncodes_size) {
+ if (ncodes_size == 0)
+ ncodes = (_codeidx_t *) malloc(sizeof(_codeidx_t) << 3);
+ else
+ ncodes = (_codeidx_t *)
+ realloc((char *) ncodes, sizeof(_codeidx_t) * (ncodes_size + 8));
+
+ ncodes_size += 8;
+ }
+
+ /*
+ * Shift things around to insert the code if necessary.
+ */
+ if (i < ncodes_used) {
+ for (j = ncodes_used; j > i; j--) {
+ ncodes[j].code = ncodes[j - 1].code;
+ ncodes[j].idx = ncodes[j - 1].idx;
+ }
+ }
+ ncodes[i].code = code;
+ ncodes[i].idx = make_number(num, denom);
+
+ ncodes_used++;
+}
+
+/*
+ * This routine assumes that the line is a valid Unicode Character Database
+ * entry.
+ */
+static void
+read_cdata(FILE *in)
+{
+ ac_uint4 i, lineno, skip, code, ccl_code;
+ short wnum, neg, number[2], compat;
+ char line[512], *s, *e, *first_prop;
+
+ lineno = skip = 0;
+ while (fgets(line, sizeof(line), in)) {
+ if( (s=strchr(line, '\n')) ) *s = '\0';
+ lineno++;
+
+ /*
+ * Skip blank lines and lines that start with a '#'.
+ */
+ if (line[0] == 0 || line[0] == '#')
+ continue;
+
+ /*
+ * If lines need to be skipped, do it here.
+ */
+ if (skip) {
+ skip--;
+ continue;
+ }
+
+ /*
+ * Collect the code. The code can be up to 6 hex digits in length to
+ * allow surrogates to be specified.
+ */
+ for (s = line, i = code = 0; *s != ';' && i < 6; i++, s++) {
+ code <<= 4;
+ if (*s >= '0' && *s <= '9')
+ code += *s - '0';
+ else if (*s >= 'A' && *s <= 'F')
+ code += (*s - 'A') + 10;
+ else if (*s >= 'a' && *s <= 'f')
+ code += (*s - 'a') + 10;
+ }
+
+ /*
+ * Handle the following special cases:
+ * 1. 4E00-9FA5 CJK Ideographs.
+ * 2. AC00-D7A3 Hangul Syllables.
+ * 3. D800-DFFF Surrogates.
+ * 4. E000-F8FF Private Use Area.
+ * 5. F900-FA2D Han compatibility.
+ * ...Plus additional ranges in newer Unicode versions...
+ */
+ switch (code) {
+ case 0x3400:
+ /* CJK Ideograph Extension A */
+ add_range(0x3400, 0x4db5, "Lo", "L");
+
+ add_range(0x3400, 0x4db5, "Cp", 0);
+
+ skip = 1;
+ break;
+ case 0x4e00:
+ /*
+ * The Han ideographs.
+ */
+ add_range(0x4e00, 0x9fff, "Lo", "L");
+
+ /*
+ * Add the characters to the defined category.
+ */
+ add_range(0x4e00, 0x9fa5, "Cp", 0);
+
+ skip = 1;
+ break;
+ case 0xac00:
+ /*
+ * The Hangul syllables.
+ */
+ add_range(0xac00, 0xd7a3, "Lo", "L");
+
+ /*
+ * Add the characters to the defined category.
+ */
+ add_range(0xac00, 0xd7a3, "Cp", 0);
+
+ skip = 1;
+ break;
+ case 0xd800:
+ /*
+ * Make a range of all surrogates and assume some default
+ * properties.
+ */
+ add_range(0x010000, 0x10ffff, "Cs", "L");
+ skip = 5;
+ break;
+ case 0xe000:
+ /*
+ * The Private Use area. Add with a default set of properties.
+ */
+ add_range(0xe000, 0xf8ff, "Co", "L");
+ skip = 1;
+ break;
+ case 0xf900:
+ /*
+ * The CJK compatibility area.
+ */
+ add_range(0xf900, 0xfaff, "Lo", "L");
+
+ /*
+ * Add the characters to the defined category.
+ */
+ add_range(0xf900, 0xfaff, "Cp", 0);
+
+ skip = 1;
+ break;
+ case 0x20000:
+ /* CJK Ideograph Extension B */
+ add_range(0x20000, 0x2a6d6, "Lo", "L");
+
+ add_range(0x20000, 0x2a6d6, "Cp", 0);
+
+ skip = 1;
+ break;
+ case 0xf0000:
+ /* Plane 15 private use */
+ add_range(0xf0000, 0xffffd, "Co", "L");
+ skip = 1;
+ break;
+
+ case 0x100000:
+ /* Plane 16 private use */
+ add_range(0x100000, 0x10fffd, "Co", "L");
+ skip = 1;
+ break;
+ }
+
+ if (skip)
+ continue;
+
+ /*
+ * Add the code to the defined category.
+ */
+ ordered_range_insert(code, "Cp", 2);
+
+ /*
+ * Locate the first character property field.
+ */
+ for (i = 0; *s != 0 && i < 2; s++) {
+ if (*s == ';')
+ i++;
+ }
+ for (e = s; *e && *e != ';'; e++) ;
+
+ first_prop = s;
+
+ ordered_range_insert(code, s, e - s);
+
+ /*
+ * Locate the combining class code.
+ */
+ for (s = e; *s != 0 && i < 3; s++) {
+ if (*s == ';')
+ i++;
+ }
+
+ /*
+ * Convert the combining class code from decimal.
+ */
+ for (ccl_code = 0, e = s; *e && *e != ';'; e++)
+ ccl_code = (ccl_code * 10) + (*e - '0');
+
+ /*
+ * Add the code if it not 0.
+ */
+ if (ccl_code != 0)
+ ordered_ccl_insert(code, ccl_code);
+
+ /*
+ * Locate the second character property field.
+ */
+ for (s = e; *s != 0 && i < 4; s++) {
+ if (*s == ';')
+ i++;
+ }
+ for (e = s; *e && *e != ';'; e++) ;
+
+ ordered_range_insert(code, s, e - s);
+
+ /*
+ * Check for a decomposition.
+ */
+ s = ++e;
+ if (*s != ';') {
+ compat = *s == '<';
+ if (compat) {
+ /*
+ * Skip compatibility formatting tag.
+ */
+ while (*s++ != '>');
+ }
+ /*
+ * Collect the codes of the decomposition.
+ */
+ for (dectmp_size = 0; *s != ';'; ) {
+ /*
+ * Skip all leading non-hex digits.
+ */
+ while (!ishdigit(*s))
+ s++;
+
+ for (dectmp[dectmp_size] = 0; ishdigit(*s); s++) {
+ dectmp[dectmp_size] <<= 4;
+ if (*s >= '0' && *s <= '9')
+ dectmp[dectmp_size] += *s - '0';
+ else if (*s >= 'A' && *s <= 'F')
+ dectmp[dectmp_size] += (*s - 'A') + 10;
+ else if (*s >= 'a' && *s <= 'f')
+ dectmp[dectmp_size] += (*s - 'a') + 10;
+ }
+ dectmp_size++;
+ }
+
+ /*
+ * If there are any codes in the temporary decomposition array,
+ * then add the character with its decomposition.
+ */
+ if (dectmp_size > 0) {
+ if (!compat) {
+ add_decomp(code, 0);
+ }
+ add_decomp(code, 1);
+ }
+ }
+
+ /*
+ * Skip to the number field.
+ */
+ for (i = 0; i < 3 && *s; s++) {
+ if (*s == ';')
+ i++;
+ }
+
+ /*
+ * Scan the number in.
+ */
+ number[0] = number[1] = 0;
+ for (e = s, neg = wnum = 0; *e && *e != ';'; e++) {
+ if (*e == '-') {
+ neg = 1;
+ continue;
+ }
+
+ if (*e == '/') {
+ /*
+ * Move the the denominator of the fraction.
+ */
+ if (neg)
+ number[wnum] *= -1;
+ neg = 0;
+ e++;
+ wnum++;
+ }
+ number[wnum] = (number[wnum] * 10) + (*e - '0');
+ }
+
+ if (e > s) {
+ /*
+ * Adjust the denominator in case of integers and add the number.
+ */
+ if (wnum == 0)
+ number[1] = 1;
+
+ add_number(code, number[0], number[1]);
+ }
+
+ /*
+ * Skip to the start of the possible case mappings.
+ */
+ for (s = e, i = 0; i < 4 && *s; s++) {
+ if (*s == ';')
+ i++;
+ }
+
+ /*
+ * Collect the case mappings.
+ */
+ cases[0] = cases[1] = cases[2] = 0;
+ for (i = 0; i < 3; i++) {
+ while (ishdigit(*s)) {
+ cases[i] <<= 4;
+ if (*s >= '0' && *s <= '9')
+ cases[i] += *s - '0';
+ else if (*s >= 'A' && *s <= 'F')
+ cases[i] += (*s - 'A') + 10;
+ else if (*s >= 'a' && *s <= 'f')
+ cases[i] += (*s - 'a') + 10;
+ s++;
+ }
+ if (*s == ';')
+ s++;
+ }
+ if (!strncmp(first_prop,"Lt",2) && (cases[0] || cases[1]))
+ /*
+ * Add the upper and lower mappings for a title case character.
+ */
+ add_title(code);
+ else if (cases[1])
+ /*
+ * Add the lower and title case mappings for the upper case
+ * character.
+ */
+ add_upper(code);
+ else if (cases[0])
+ /*
+ * Add the upper and title case mappings for the lower case
+ * character.
+ */
+ add_lower(code);
+ }
+}
+
+static _decomp_t *
+find_decomp(ac_uint4 code, short compat)
+{
+ long l, r, m;
+ _decomp_t *decs;
+
+ l = 0;
+ r = (compat ? kdecomps_used : decomps_used) - 1;
+ decs = compat ? kdecomps : decomps;
+ while (l <= r) {
+ m = (l + r) >> 1;
+ if (code > decs[m].code)
+ l = m + 1;
+ else if (code < decs[m].code)
+ r = m - 1;
+ else
+ return &decs[m];
+ }
+ return 0;
+}
+
+static void
+decomp_it(_decomp_t *d, short compat)
+{
+ ac_uint4 i;
+ _decomp_t *dp;
+
+ for (i = 0; i < d->used; i++) {
+ if ((dp = find_decomp(d->decomp[i], compat)) != 0)
+ decomp_it(dp, compat);
+ else
+ dectmp[dectmp_size++] = d->decomp[i];
+ }
+}
+
+/*
+ * Expand all decompositions by recursively decomposing each character
+ * in the decomposition.
+ */
+static void
+expand_decomp(void)
+{
+ ac_uint4 i;
+
+ for (i = 0; i < decomps_used; i++) {
+ dectmp_size = 0;
+ decomp_it(&decomps[i], 0);
+ if (dectmp_size > 0)
+ add_decomp(decomps[i].code, 0);
+ }
+
+ for (i = 0; i < kdecomps_used; i++) {
+ dectmp_size = 0;
+ decomp_it(&kdecomps[i], 1);
+ if (dectmp_size > 0)
+ add_decomp(kdecomps[i].code, 1);
+ }
+}
+
+static int
+cmpcomps(const void *v_comp1, const void *v_comp2)
+{
+ const _comp_t *comp1 = v_comp1, *comp2 = v_comp2;
+ long diff = comp1->code1 - comp2->code1;
+
+ if (!diff)
+ diff = comp1->code2 - comp2->code2;
+ return (int) diff;
+}
+
+/*
+ * Load composition exclusion data
+ */
+static void
+read_compexdata(FILE *in)
+{
+ ac_uint2 i;
+ ac_uint4 code;
+ char line[512], *s;
+
+ (void) memset((char *) compexs, 0, sizeof(compexs));
+
+ while (fgets(line, sizeof(line), in)) {
+ if( (s=strchr(line, '\n')) ) *s = '\0';
+ /*
+ * Skip blank lines and lines that start with a '#'.
+ */
+ if (line[0] == 0 || line[0] == '#')
+ continue;
+
+ /*
+ * Collect the code. Assume max 6 digits
+ */
+
+ for (s = line, i = code = 0; *s != '#' && i < 6; i++, s++) {
+ if (isspace((unsigned char)*s)) break;
+ code <<= 4;
+ if (*s >= '0' && *s <= '9')
+ code += *s - '0';
+ else if (*s >= 'A' && *s <= 'F')
+ code += (*s - 'A') + 10;
+ else if (*s >= 'a' && *s <= 'f')
+ code += (*s - 'a') + 10;
+ }
+ COMPEX_SET(code);
+ }
+}
+
+/*
+ * Creates array of compositions from decomposition array
+ */
+static void
+create_comps(void)
+{
+ ac_uint4 i, cu;
+
+ comps = (_comp_t *) malloc(comps_used * sizeof(_comp_t));
+
+ for (i = cu = 0; i < decomps_used; i++) {
+ if (decomps[i].used != 2 || COMPEX_TEST(decomps[i].code))
+ continue;
+ comps[cu].comp = decomps[i].code;
+ comps[cu].count = 2;
+ comps[cu].code1 = decomps[i].decomp[0];
+ comps[cu].code2 = decomps[i].decomp[1];
+ cu++;
+ }
+ comps_used = cu;
+ qsort(comps, comps_used, sizeof(_comp_t), cmpcomps);
+}
+
+#if HARDCODE_DATA
+static void
+write_case(FILE *out, _case_t *tab, int num, int first)
+{
+ int i;
+
+ for (i=0; i<num; i++) {
+ if (first) first = 0;
+ else fprintf(out, ",");
+ fprintf(out, "\n\t0x%08lx, 0x%08lx, 0x%08lx",
+ (unsigned long) tab[i].key, (unsigned long) tab[i].other1,
+ (unsigned long) tab[i].other2);
+ }
+}
+
+#define PREF "static const "
+
+#endif
+
+static void
+write_cdata(char *opath)
+{
+ FILE *out;
+ ac_uint4 bytes;
+ ac_uint4 i, idx, nprops;
+#if !(HARDCODE_DATA)
+ ac_uint2 casecnt[2];
+#endif
+ char path[BUFSIZ];
+#if HARDCODE_DATA
+ int j, k;
+
+ /*****************************************************************
+ *
+ * Generate the ctype data.
+ *
+ *****************************************************************/
+
+ /*
+ * Open the output file.
+ */
+ snprintf(path, sizeof path, "%s" LDAP_DIRSEP "uctable.h", opath);
+ if ((out = fopen(path, "w")) == 0)
+ return;
+#else
+ /*
+ * Open the ctype.dat file.
+ */
+ snprintf(path, sizeof path, "%s" LDAP_DIRSEP "ctype.dat", opath);
+ if ((out = fopen(path, "wb")) == 0)
+ return;
+#endif
+
+ /*
+ * Collect the offsets for the properties. The offsets array is
+ * on a 4-byte boundary to keep things efficient for architectures
+ * that need such a thing.
+ */
+ for (i = idx = 0; i < NUMPROPS; i++) {
+ propcnt[i] = (proptbl[i].used != 0) ? idx : 0xffff;
+ idx += proptbl[i].used;
+ }
+
+ /*
+ * Add the sentinel index which is used by the binary search as the upper
+ * bound for a search.
+ */
+ propcnt[i] = idx;
+
+ /*
+ * Record the actual number of property lists. This may be different than
+ * the number of offsets actually written because of aligning on a 4-byte
+ * boundary.
+ */
+ hdr[1] = NUMPROPS;
+
+ /*
+ * Calculate the byte count needed and pad the property counts array to a
+ * 4-byte boundary.
+ */
+ if ((bytes = sizeof(ac_uint2) * (NUMPROPS + 1)) & 3)
+ bytes += 4 - (bytes & 3);
+ nprops = bytes / sizeof(ac_uint2);
+ bytes += sizeof(ac_uint4) * idx;
+
+#if HARDCODE_DATA
+ fprintf(out, PREF "ac_uint4 _ucprop_size = %d;\n\n", NUMPROPS);
+
+ fprintf(out, PREF "ac_uint2 _ucprop_offsets[] = {");
+
+ for (i = 0; i<nprops; i++) {
+ if (i) fprintf(out, ",");
+ if (!(i&7)) fprintf(out, "\n\t");
+ else fprintf(out, " ");
+ fprintf(out, "0x%04x", propcnt[i]);
+ }
+ fprintf(out, "\n};\n\n");
+
+ fprintf(out, PREF "ac_uint4 _ucprop_ranges[] = {");
+
+ k = 0;
+ for (i = 0; i < NUMPROPS; i++) {
+ if (proptbl[i].used > 0) {
+ for (j=0; j<proptbl[i].used; j++) {
+ if (k) fprintf(out, ",");
+ if (!(k&3)) fprintf(out,"\n\t");
+ else fprintf(out, " ");
+ k++;
+ fprintf(out, "0x%08lx", (unsigned long) proptbl[i].ranges[j]);
+ }
+ }
+ }
+ fprintf(out, "\n};\n\n");
+#else
+ /*
+ * Write the header.
+ */
+ fwrite((char *) hdr, sizeof(ac_uint2), 2, out);
+
+ /*
+ * Write the byte count.
+ */
+ fwrite((char *) &bytes, sizeof(ac_uint4), 1, out);
+
+ /*
+ * Write the property list counts.
+ */
+ fwrite((char *) propcnt, sizeof(ac_uint2), nprops, out);
+
+ /*
+ * Write the property lists.
+ */
+ for (i = 0; i < NUMPROPS; i++) {
+ if (proptbl[i].used > 0)
+ fwrite((char *) proptbl[i].ranges, sizeof(ac_uint4),
+ proptbl[i].used, out);
+ }
+
+ fclose(out);
+#endif
+
+ /*****************************************************************
+ *
+ * Generate the case mapping data.
+ *
+ *****************************************************************/
+
+#if HARDCODE_DATA
+ fprintf(out, PREF "ac_uint4 _uccase_size = %ld;\n\n",
+ (long) (upper_used + lower_used + title_used));
+
+ fprintf(out, PREF "ac_uint2 _uccase_len[2] = {%ld, %ld};\n\n",
+ (long) upper_used, (long) lower_used);
+ fprintf(out, PREF "ac_uint4 _uccase_map[] = {");
+
+ if (upper_used > 0)
+ /*
+ * Write the upper case table.
+ */
+ write_case(out, upper, upper_used, 1);
+
+ if (lower_used > 0)
+ /*
+ * Write the lower case table.
+ */
+ write_case(out, lower, lower_used, !upper_used);
+
+ if (title_used > 0)
+ /*
+ * Write the title case table.
+ */
+ write_case(out, title, title_used, !(upper_used||lower_used));
+
+ if (!(upper_used || lower_used || title_used))
+ fprintf(out, "\t0");
+
+ fprintf(out, "\n};\n\n");
+#else
+ /*
+ * Open the case.dat file.
+ */
+ snprintf(path, sizeof path, "%s" LDAP_DIRSEP "case.dat", opath);
+ if ((out = fopen(path, "wb")) == 0)
+ return;
+
+ /*
+ * Write the case mapping tables.
+ */
+ hdr[1] = upper_used + lower_used + title_used;
+ casecnt[0] = upper_used;
+ casecnt[1] = lower_used;
+
+ /*
+ * Write the header.
+ */
+ fwrite((char *) hdr, sizeof(ac_uint2), 2, out);
+
+ /*
+ * Write the upper and lower case table sizes.
+ */
+ fwrite((char *) casecnt, sizeof(ac_uint2), 2, out);
+
+ if (upper_used > 0)
+ /*
+ * Write the upper case table.
+ */
+ fwrite((char *) upper, sizeof(_case_t), upper_used, out);
+
+ if (lower_used > 0)
+ /*
+ * Write the lower case table.
+ */
+ fwrite((char *) lower, sizeof(_case_t), lower_used, out);
+
+ if (title_used > 0)
+ /*
+ * Write the title case table.
+ */
+ fwrite((char *) title, sizeof(_case_t), title_used, out);
+
+ fclose(out);
+#endif
+
+ /*****************************************************************
+ *
+ * Generate the composition data.
+ *
+ *****************************************************************/
+
+ /*
+ * Create compositions from decomposition data
+ */
+ create_comps();
+
+#if HARDCODE_DATA
+ fprintf(out, PREF "ac_uint4 _uccomp_size = %ld;\n\n",
+ comps_used * 4L);
+
+ fprintf(out, PREF "ac_uint4 _uccomp_data[] = {");
+
+ /*
+ * Now, if comps exist, write them out.
+ */
+ if (comps_used > 0) {
+ for (i=0; i<comps_used; i++) {
+ if (i) fprintf(out, ",");
+ fprintf(out, "\n\t0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx",
+ (unsigned long) comps[i].comp, (unsigned long) comps[i].count,
+ (unsigned long) comps[i].code1, (unsigned long) comps[i].code2);
+ }
+ } else {
+ fprintf(out, "\t0");
+ }
+ fprintf(out, "\n};\n\n");
+#else
+ /*
+ * Open the comp.dat file.
+ */
+ snprintf(path, sizeof path, "%s" LDAP_DIRSEP "comp.dat", opath);
+ if ((out = fopen(path, "wb")) == 0)
+ return;
+
+ /*
+ * Write the header.
+ */
+ hdr[1] = (ac_uint2) comps_used * 4;
+ fwrite((char *) hdr, sizeof(ac_uint2), 2, out);
+
+ /*
+ * Write out the byte count to maintain header size.
+ */
+ bytes = comps_used * sizeof(_comp_t);
+ fwrite((char *) &bytes, sizeof(ac_uint4), 1, out);
+
+ /*
+ * Now, if comps exist, write them out.
+ */
+ if (comps_used > 0)
+ fwrite((char *) comps, sizeof(_comp_t), comps_used, out);
+
+ fclose(out);
+#endif
+
+ /*****************************************************************
+ *
+ * Generate the decomposition data.
+ *
+ *****************************************************************/
+
+ /*
+ * Fully expand all decompositions before generating the output file.
+ */
+ expand_decomp();
+
+#if HARDCODE_DATA
+ fprintf(out, PREF "ac_uint4 _ucdcmp_size = %ld;\n\n",
+ decomps_used * 2L);
+
+ fprintf(out, PREF "ac_uint4 _ucdcmp_nodes[] = {");
+
+ if (decomps_used) {
+ /*
+ * Write the list of decomp nodes.
+ */
+ for (i = idx = 0; i < decomps_used; i++) {
+ fprintf(out, "\n\t0x%08lx, 0x%08lx,",
+ (unsigned long) decomps[i].code, (unsigned long) idx);
+ idx += decomps[i].used;
+ }
+
+ /*
+ * Write the sentinel index as the last decomp node.
+ */
+ fprintf(out, "\n\t0x%08lx\n};\n\n", (unsigned long) idx);
+
+ fprintf(out, PREF "ac_uint4 _ucdcmp_decomp[] = {");
+ /*
+ * Write the decompositions themselves.
+ */
+ k = 0;
+ for (i = 0; i < decomps_used; i++)
+ for (j=0; j<decomps[i].used; j++) {
+ if (k) fprintf(out, ",");
+ if (!(k&3)) fprintf(out,"\n\t");
+ else fprintf(out, " ");
+ k++;
+ fprintf(out, "0x%08lx", (unsigned long) decomps[i].decomp[j]);
+ }
+ fprintf(out, "\n};\n\n");
+ }
+#else
+ /*
+ * Open the decomp.dat file.
+ */
+ snprintf(path, sizeof path, "%s" LDAP_DIRSEP "decomp.dat", opath);
+ if ((out = fopen(path, "wb")) == 0)
+ return;
+
+ hdr[1] = decomps_used;
+
+ /*
+ * Write the header.
+ */
+ fwrite((char *) hdr, sizeof(ac_uint2), 2, out);
+
+ /*
+ * Write a temporary byte count which will be calculated as the
+ * decompositions are written out.
+ */
+ bytes = 0;
+ fwrite((char *) &bytes, sizeof(ac_uint4), 1, out);
+
+ if (decomps_used) {
+ /*
+ * Write the list of decomp nodes.
+ */
+ for (i = idx = 0; i < decomps_used; i++) {
+ fwrite((char *) &decomps[i].code, sizeof(ac_uint4), 1, out);
+ fwrite((char *) &idx, sizeof(ac_uint4), 1, out);
+ idx += decomps[i].used;
+ }
+
+ /*
+ * Write the sentinel index as the last decomp node.
+ */
+ fwrite((char *) &idx, sizeof(ac_uint4), 1, out);
+
+ /*
+ * Write the decompositions themselves.
+ */
+ for (i = 0; i < decomps_used; i++)
+ fwrite((char *) decomps[i].decomp, sizeof(ac_uint4),
+ decomps[i].used, out);
+
+ /*
+ * Seek back to the beginning and write the byte count.
+ */
+ bytes = (sizeof(ac_uint4) * idx) +
+ (sizeof(ac_uint4) * ((hdr[1] << 1) + 1));
+ fseek(out, sizeof(ac_uint2) << 1, 0L);
+ fwrite((char *) &bytes, sizeof(ac_uint4), 1, out);
+
+ fclose(out);
+ }
+#endif
+
+#ifdef HARDCODE_DATA
+ fprintf(out, PREF "ac_uint4 _uckdcmp_size = %ld;\n\n",
+ kdecomps_used * 2L);
+
+ fprintf(out, PREF "ac_uint4 _uckdcmp_nodes[] = {");
+
+ if (kdecomps_used) {
+ /*
+ * Write the list of kdecomp nodes.
+ */
+ for (i = idx = 0; i < kdecomps_used; i++) {
+ fprintf(out, "\n\t0x%08lx, 0x%08lx,",
+ (unsigned long) kdecomps[i].code, (unsigned long) idx);
+ idx += kdecomps[i].used;
+ }
+
+ /*
+ * Write the sentinel index as the last decomp node.
+ */
+ fprintf(out, "\n\t0x%08lx\n};\n\n", (unsigned long) idx);
+
+ fprintf(out, PREF "ac_uint4 _uckdcmp_decomp[] = {");
+
+ /*
+ * Write the decompositions themselves.
+ */
+ k = 0;
+ for (i = 0; i < kdecomps_used; i++)
+ for (j=0; j<kdecomps[i].used; j++) {
+ if (k) fprintf(out, ",");
+ if (!(k&3)) fprintf(out,"\n\t");
+ else fprintf(out, " ");
+ k++;
+ fprintf(out, "0x%08lx", (unsigned long) kdecomps[i].decomp[j]);
+ }
+ fprintf(out, "\n};\n\n");
+ }
+#else
+ /*
+ * Open the kdecomp.dat file.
+ */
+ snprintf(path, sizeof path, "%s" LDAP_DIRSEP "kdecomp.dat", opath);
+ if ((out = fopen(path, "wb")) == 0)
+ return;
+
+ hdr[1] = kdecomps_used;
+
+ /*
+ * Write the header.
+ */
+ fwrite((char *) hdr, sizeof(ac_uint2), 2, out);
+
+ /*
+ * Write a temporary byte count which will be calculated as the
+ * decompositions are written out.
+ */
+ bytes = 0;
+ fwrite((char *) &bytes, sizeof(ac_uint4), 1, out);
+
+ if (kdecomps_used) {
+ /*
+ * Write the list of kdecomp nodes.
+ */
+ for (i = idx = 0; i < kdecomps_used; i++) {
+ fwrite((char *) &kdecomps[i].code, sizeof(ac_uint4), 1, out);
+ fwrite((char *) &idx, sizeof(ac_uint4), 1, out);
+ idx += kdecomps[i].used;
+ }
+
+ /*
+ * Write the sentinel index as the last decomp node.
+ */
+ fwrite((char *) &idx, sizeof(ac_uint4), 1, out);
+
+ /*
+ * Write the decompositions themselves.
+ */
+ for (i = 0; i < kdecomps_used; i++)
+ fwrite((char *) kdecomps[i].decomp, sizeof(ac_uint4),
+ kdecomps[i].used, out);
+
+ /*
+ * Seek back to the beginning and write the byte count.
+ */
+ bytes = (sizeof(ac_uint4) * idx) +
+ (sizeof(ac_uint4) * ((hdr[1] << 1) + 1));
+ fseek(out, sizeof(ac_uint2) << 1, 0L);
+ fwrite((char *) &bytes, sizeof(ac_uint4), 1, out);
+
+ fclose(out);
+ }
+#endif
+
+ /*****************************************************************
+ *
+ * Generate the combining class data.
+ *
+ *****************************************************************/
+#ifdef HARDCODE_DATA
+ fprintf(out, PREF "ac_uint4 _uccmcl_size = %ld;\n\n", (long) ccl_used);
+
+ fprintf(out, PREF "ac_uint4 _uccmcl_nodes[] = {");
+
+ if (ccl_used > 0) {
+ /*
+ * Write the combining class ranges out.
+ */
+ for (i = 0; i<ccl_used; i++) {
+ if (i) fprintf(out, ",");
+ if (!(i&3)) fprintf(out, "\n\t");
+ else fprintf(out, " ");
+ fprintf(out, "0x%08lx", (unsigned long) ccl[i]);
+ }
+ } else {
+ fprintf(out, "\t0");
+ }
+ fprintf(out, "\n};\n\n");
+#else
+ /*
+ * Open the cmbcl.dat file.
+ */
+ snprintf(path, sizeof path, "%s" LDAP_DIRSEP "cmbcl.dat", opath);
+ if ((out = fopen(path, "wb")) == 0)
+ return;
+
+ /*
+ * Set the number of ranges used. Each range has a combining class which
+ * means each entry is a 3-tuple.
+ */
+ hdr[1] = ccl_used / 3;
+
+ /*
+ * Write the header.
+ */
+ fwrite((char *) hdr, sizeof(ac_uint2), 2, out);
+
+ /*
+ * Write out the byte count to maintain header size.
+ */
+ bytes = ccl_used * sizeof(ac_uint4);
+ fwrite((char *) &bytes, sizeof(ac_uint4), 1, out);
+
+ if (ccl_used > 0)
+ /*
+ * Write the combining class ranges out.
+ */
+ fwrite((char *) ccl, sizeof(ac_uint4), ccl_used, out);
+
+ fclose(out);
+#endif
+
+ /*****************************************************************
+ *
+ * Generate the number data.
+ *
+ *****************************************************************/
+
+#if HARDCODE_DATA
+ fprintf(out, PREF "ac_uint4 _ucnum_size = %lu;\n\n",
+ (unsigned long)ncodes_used<<1);
+
+ fprintf(out, PREF "ac_uint4 _ucnum_nodes[] = {");
+
+ /*
+ * Now, if number mappings exist, write them out.
+ */
+ if (ncodes_used > 0) {
+ for (i = 0; i<ncodes_used; i++) {
+ if (i) fprintf(out, ",");
+ if (!(i&1)) fprintf(out, "\n\t");
+ else fprintf(out, " ");
+ fprintf(out, "0x%08lx, 0x%08lx",
+ (unsigned long) ncodes[i].code, (unsigned long) ncodes[i].idx);
+ }
+ fprintf(out, "\n};\n\n");
+
+ fprintf(out, PREF "short _ucnum_vals[] = {");
+ for (i = 0; i<nums_used; i++) {
+ if (i) fprintf(out, ",");
+ if (!(i&3)) fprintf(out, "\n\t");
+ else fprintf(out, " ");
+ if (nums[i].numerator < 0) {
+ fprintf(out, "%6d, 0x%04x",
+ nums[i].numerator, nums[i].denominator);
+ } else {
+ fprintf(out, "0x%04x, 0x%04x",
+ nums[i].numerator, nums[i].denominator);
+ }
+ }
+ fprintf(out, "\n};\n\n");
+ }
+#else
+ /*
+ * Open the num.dat file.
+ */
+ snprintf(path, sizeof path, "%s" LDAP_DIRSEP "num.dat", opath);
+ if ((out = fopen(path, "wb")) == 0)
+ return;
+
+ /*
+ * The count part of the header will be the total number of codes that
+ * have numbers.
+ */
+ hdr[1] = (ac_uint2) (ncodes_used << 1);
+ bytes = (ncodes_used * sizeof(_codeidx_t)) + (nums_used * sizeof(_num_t));
+
+ /*
+ * Write the header.
+ */
+ fwrite((char *) hdr, sizeof(ac_uint2), 2, out);
+
+ /*
+ * Write out the byte count to maintain header size.
+ */
+ fwrite((char *) &bytes, sizeof(ac_uint4), 1, out);
+
+ /*
+ * Now, if number mappings exist, write them out.
+ */
+ if (ncodes_used > 0) {
+ fwrite((char *) ncodes, sizeof(_codeidx_t), ncodes_used, out);
+ fwrite((char *) nums, sizeof(_num_t), nums_used, out);
+ }
+#endif
+
+ fclose(out);
+}
+
+static void
+usage(char *prog)
+{
+ fprintf(stderr,
+ "Usage: %s [-o output-directory|-x composition-exclusions]", prog);
+ fprintf(stderr, " datafile1 datafile2 ...\n\n");
+ fprintf(stderr,
+ "-o output-directory\n\t\tWrite the output files to a different");
+ fprintf(stderr, " directory (default: .).\n");
+ fprintf(stderr,
+ "-x composition-exclusion\n\t\tFile of composition codes");
+ fprintf(stderr, " that should be excluded.\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ FILE *in;
+ char *prog, *opath;
+
+ prog = lutil_progname( "ucgendat", argc, argv );
+
+ opath = 0;
+ in = stdin;
+
+ argc--;
+ argv++;
+
+ while (argc > 0) {
+ if (argv[0][0] == '-') {
+ switch (argv[0][1]) {
+ case 'o':
+ argc--;
+ argv++;
+ opath = argv[0];
+ break;
+ case 'x':
+ argc--;
+ argv++;
+ if ((in = fopen(argv[0], "r")) == 0)
+ fprintf(stderr,
+ "%s: unable to open composition exclusion file %s\n",
+ prog, argv[0]);
+ else {
+ read_compexdata(in);
+ fclose(in);
+ in = 0;
+ }
+ break;
+ default:
+ usage(prog);
+ }
+ } else {
+ if (in != stdin && in != NULL)
+ fclose(in);
+ if ((in = fopen(argv[0], "r")) == 0)
+ fprintf(stderr, "%s: unable to open ctype file %s\n",
+ prog, argv[0]);
+ else {
+ read_cdata(in);
+ fclose(in);
+ in = 0;
+ }
+ }
+ argc--;
+ argv++;
+ }
+
+ if (opath == 0)
+ opath = ".";
+ write_cdata(opath);
+
+ return 0;
+}
diff --git a/libraries/liblunicode/ucdata/ucpgba.c b/libraries/liblunicode/ucdata/ucpgba.c
new file mode 100644
index 0000000..489ffec
--- /dev/null
+++ b/libraries/liblunicode/ucdata/ucpgba.c
@@ -0,0 +1,750 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Copyright 2001 Computing Research Labs, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+ * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* $Id: ucpgba.c,v 1.5 2001/01/02 18:46:20 mleisher Exp $ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ucdata.h"
+#include "ucpgba.h"
+
+/*
+ * These macros are used while reordering of RTL runs of text for the
+ * special case of non-spacing characters being in runs of weakly
+ * directional text. They check for weak and non-spacing, and digits and
+ * non-spacing.
+ */
+#define ISWEAKSPECIAL(cc) ucisprop(cc, UC_EN|UC_ES|UC_MN, UC_ET|UC_AN|UC_CS)
+#define ISDIGITSPECIAL(cc) ucisprop(cc, UC_ND|UC_MN, 0)
+
+/*
+ * These macros are used while breaking a string into runs of text in
+ * different directions. Descriptions:
+ *
+ * ISLTR_LTR - Test for members of an LTR run in an LTR context. This looks
+ * for characters with ltr, non-spacing, weak, and neutral
+ * properties.
+ *
+ * ISRTL_RTL - Test for members of an RTL run in an RTL context. This looks
+ * for characters with rtl, non-spacing, weak, and neutral
+ * properties.
+ *
+ * ISRTL_NEUTRAL - Test for RTL or neutral characters.
+ *
+ * ISWEAK_NEUTRAL - Test for weak or neutral characters.
+ */
+#define ISLTR_LTR(cc) ucisprop(cc, UC_L|UC_MN|UC_EN|UC_ES,\
+ UC_ET|UC_CS|UC_B|UC_S|UC_WS|UC_ON)
+
+#define ISRTL_RTL(cc) ucisprop(cc, UC_R|UC_MN|UC_EN|UC_ES,\
+ UC_ET|UC_AN|UC_CS|UC_B|UC_S|UC_WS|UC_ON)
+
+#define ISRTL_NEUTRAL(cc) ucisprop(cc, UC_R, UC_B|UC_S|UC_WS|UC_ON)
+#define ISWEAK_NEUTRAL(cc) ucisprop(cc, UC_EN|UC_ES, \
+ UC_B|UC_S|UC_WS|UC_ON|UC_ET|UC_AN|UC_CS)
+
+/*
+ * This table is temporarily hard-coded here until it can be constructed
+ * automatically somehow.
+ */
+static unsigned long _symmetric_pairs[] = {
+ 0x0028, 0x0029, 0x0029, 0x0028, 0x003C, 0x003E, 0x003E, 0x003C,
+ 0x005B, 0x005D, 0x005D, 0x005B, 0x007B, 0x007D, 0x007D, 0x007B,
+ 0x2045, 0x2046, 0x2046, 0x2045, 0x207D, 0x207E, 0x207E, 0x207D,
+ 0x208D, 0x208E, 0x208E, 0x208D, 0x3008, 0x3009, 0x3009, 0x3008,
+ 0x300A, 0x300B, 0x300B, 0x300A, 0x300C, 0x300D, 0x300D, 0x300C,
+ 0x300E, 0x300F, 0x300F, 0x300E, 0x3010, 0x3011, 0x3011, 0x3010,
+ 0x3014, 0x3015, 0x3015, 0x3014, 0x3016, 0x3017, 0x3017, 0x3016,
+ 0x3018, 0x3019, 0x3019, 0x3018, 0x301A, 0x301B, 0x301B, 0x301A,
+ 0xFD3E, 0xFD3F, 0xFD3F, 0xFD3E, 0xFE59, 0xFE5A, 0xFE5A, 0xFE59,
+ 0xFE5B, 0xFE5C, 0xFE5C, 0xFE5B, 0xFE5D, 0xFE5E, 0xFE5E, 0xFE5D,
+ 0xFF08, 0xFF09, 0xFF09, 0xFF08, 0xFF3B, 0xFF3D, 0xFF3D, 0xFF3B,
+ 0xFF5B, 0xFF5D, 0xFF5D, 0xFF5B, 0xFF62, 0xFF63, 0xFF63, 0xFF62,
+};
+
+static int _symmetric_pairs_size =
+sizeof(_symmetric_pairs)/sizeof(_symmetric_pairs[0]);
+
+/*
+ * This routine looks up the other form of a symmetric pair.
+ */
+static unsigned long
+_ucsymmetric_pair(unsigned long c)
+{
+ int i;
+
+ for (i = 0; i < _symmetric_pairs_size; i += 2) {
+ if (_symmetric_pairs[i] == c)
+ return _symmetric_pairs[i+1];
+ }
+ return c;
+}
+
+/*
+ * This routine creates a new run, copies the text into it, links it into the
+ * logical text order chain and returns it to the caller to be linked into
+ * the visual text order chain.
+ */
+static ucrun_t *
+_add_run(ucstring_t *str, unsigned long *src,
+ unsigned long start, unsigned long end, int direction)
+{
+ long i, t;
+ ucrun_t *run;
+
+ run = (ucrun_t *) malloc(sizeof(ucrun_t));
+ run->visual_next = run->visual_prev = 0;
+ run->direction = direction;
+
+ run->cursor = ~0;
+
+ run->chars = (unsigned long *)
+ malloc(sizeof(unsigned long) * ((end - start) << 1));
+ run->positions = run->chars + (end - start);
+
+ run->source = src;
+ run->start = start;
+ run->end = end;
+
+ if (direction == UCPGBA_RTL) {
+ /*
+ * Copy the source text into the run in reverse order and select
+ * replacements for the pairwise punctuation and the <> characters.
+ */
+ for (i = 0, t = end - 1; start < end; start++, t--, i++) {
+ run->positions[i] = t;
+ if (ucissymmetric(src[t]) || src[t] == '<' || src[t] == '>')
+ run->chars[i] = _ucsymmetric_pair(src[t]);
+ else
+ run->chars[i] = src[t];
+ }
+ } else {
+ /*
+ * Copy the source text into the run directly.
+ */
+ for (i = start; i < end; i++) {
+ run->positions[i - start] = i;
+ run->chars[i - start] = src[i];
+ }
+ }
+
+ /*
+ * Add the run to the logical list for cursor traversal.
+ */
+ if (str->logical_first == 0)
+ str->logical_first = str->logical_last = run;
+ else {
+ run->logical_prev = str->logical_last;
+ str->logical_last->logical_next = run;
+ str->logical_last = run;
+ }
+
+ return run;
+}
+
+static void
+_ucadd_rtl_segment(ucstring_t *str, unsigned long *source, unsigned long start,
+ unsigned long end)
+{
+ unsigned long s, e;
+ ucrun_t *run, *lrun;
+
+ /*
+ * This is used to splice runs into strings with overall LTR direction.
+ * The `lrun' variable will never be NULL because at least one LTR run was
+ * added before this RTL run.
+ */
+ lrun = str->visual_last;
+
+ for (e = s = start; s < end;) {
+ for (; e < end && ISRTL_NEUTRAL(source[e]); e++) ;
+
+ if (e > s) {
+ run = _add_run(str, source, s, e, UCPGBA_RTL);
+
+ /*
+ * Add the run to the visual list for cursor traversal.
+ */
+ if (str->visual_first != 0) {
+ if (str->direction == UCPGBA_LTR) {
+ run->visual_prev = lrun;
+ run->visual_next = lrun->visual_next;
+ if (lrun->visual_next != 0)
+ lrun->visual_next->visual_prev = run;
+ lrun->visual_next = run;
+ if (lrun == str->visual_last)
+ str->visual_last = run;
+ } else {
+ run->visual_next = str->visual_first;
+ str->visual_first->visual_prev = run;
+ str->visual_first = run;
+ }
+ } else
+ str->visual_first = str->visual_last = run;
+ }
+
+ /*
+ * Handle digits in a special way. This makes sure the weakly
+ * directional characters appear on the expected sides of a number
+ * depending on whether that number is Arabic or not.
+ */
+ for (s = e; e < end && ISWEAKSPECIAL(source[e]); e++) {
+ if (!ISDIGITSPECIAL(source[e]) &&
+ (e + 1 == end || !ISDIGITSPECIAL(source[e + 1])))
+ break;
+ }
+
+ if (e > s) {
+ run = _add_run(str, source, s, e, UCPGBA_LTR);
+
+ /*
+ * Add the run to the visual list for cursor traversal.
+ */
+ if (str->visual_first != 0) {
+ if (str->direction == UCPGBA_LTR) {
+ run->visual_prev = lrun;
+ run->visual_next = lrun->visual_next;
+ if (lrun->visual_next != 0)
+ lrun->visual_next->visual_prev = run;
+ lrun->visual_next = run;
+ if (lrun == str->visual_last)
+ str->visual_last = run;
+ } else {
+ run->visual_next = str->visual_first;
+ str->visual_first->visual_prev = run;
+ str->visual_first = run;
+ }
+ } else
+ str->visual_first = str->visual_last = run;
+ }
+
+ /*
+ * Collect all weak non-digit sequences for an RTL segment. These
+ * will appear as part of the next RTL segment or will be added as
+ * an RTL segment by themselves.
+ */
+ for (s = e; e < end && ucisweak(source[e]) && !ucisdigit(source[e]);
+ e++) ;
+ }
+
+ /*
+ * Capture any weak non-digit sequences that occur at the end of the RTL
+ * run.
+ */
+ if (e > s) {
+ run = _add_run(str, source, s, e, UCPGBA_RTL);
+
+ /*
+ * Add the run to the visual list for cursor traversal.
+ */
+ if (str->visual_first != 0) {
+ if (str->direction == UCPGBA_LTR) {
+ run->visual_prev = lrun;
+ run->visual_next = lrun->visual_next;
+ if (lrun->visual_next != 0)
+ lrun->visual_next->visual_prev = run;
+ lrun->visual_next = run;
+ if (lrun == str->visual_last)
+ str->visual_last = run;
+ } else {
+ run->visual_next = str->visual_first;
+ str->visual_first->visual_prev = run;
+ str->visual_first = run;
+ }
+ } else
+ str->visual_first = str->visual_last = run;
+ }
+}
+
+static void
+_ucadd_ltr_segment(ucstring_t *str, unsigned long *source, unsigned long start,
+ unsigned long end)
+{
+ ucrun_t *run;
+
+ run = _add_run(str, source, start, end, UCPGBA_LTR);
+
+ /*
+ * Add the run to the visual list for cursor traversal.
+ */
+ if (str->visual_first != 0) {
+ if (str->direction == UCPGBA_LTR) {
+ run->visual_prev = str->visual_last;
+ str->visual_last->visual_next = run;
+ str->visual_last = run;
+ } else {
+ run->visual_next = str->visual_first;
+ str->visual_first->visual_prev = run;
+ str->visual_first = run;
+ }
+ } else
+ str->visual_first = str->visual_last = run;
+}
+
+ucstring_t *
+ucstring_create(unsigned long *source, unsigned long start, unsigned long end,
+ int default_direction, int cursor_motion)
+{
+ int rtl_first;
+ unsigned long s, e, ld;
+ ucstring_t *str;
+
+ str = (ucstring_t *) malloc(sizeof(ucstring_t));
+
+ /*
+ * Set the initial values.
+ */
+ str->cursor_motion = cursor_motion;
+ str->logical_first = str->logical_last = 0;
+ str->visual_first = str->visual_last = str->cursor = 0;
+ str->source = source;
+ str->start = start;
+ str->end = end;
+
+ /*
+ * If the length of the string is 0, then just return it at this point.
+ */
+ if (start == end)
+ return str;
+
+ /*
+ * This flag indicates whether the collection loop for RTL is called
+ * before the LTR loop the first time.
+ */
+ rtl_first = 0;
+
+ /*
+ * Look for the first character in the string that has strong
+ * directionality.
+ */
+ for (s = start; s < end && !ucisstrong(source[s]); s++) ;
+
+ if (s == end)
+ /*
+ * If the string contains no characters with strong directionality, use
+ * the default direction.
+ */
+ str->direction = default_direction;
+ else
+ str->direction = ucisrtl(source[s]) ? UCPGBA_RTL : UCPGBA_LTR;
+
+ if (str->direction == UCPGBA_RTL)
+ /*
+ * Set the flag that causes the RTL collection loop to run first.
+ */
+ rtl_first = 1;
+
+ /*
+ * This loop now separates the string into runs based on directionality.
+ */
+ for (s = e = 0; s < end; s = e) {
+ if (!rtl_first) {
+ /*
+ * Determine the next run of LTR text.
+ */
+
+ ld = s;
+ while (e < end && ISLTR_LTR(source[e])) {
+ if (ucisdigit(source[e]) &&
+ !(0x660 <= source[e] && source[e] <= 0x669))
+ ld = e;
+ e++;
+ }
+ if (str->direction != UCPGBA_LTR) {
+ while (e > ld && ISWEAK_NEUTRAL(source[e - 1]))
+ e--;
+ }
+
+ /*
+ * Add the LTR segment to the string.
+ */
+ if (e > s)
+ _ucadd_ltr_segment(str, source, s, e);
+ }
+
+ /*
+ * Determine the next run of RTL text.
+ */
+ ld = s = e;
+ while (e < end && ISRTL_RTL(source[e])) {
+ if (ucisdigit(source[e]) &&
+ !(0x660 <= source[e] && source[e] <= 0x669))
+ ld = e;
+ e++;
+ }
+ if (str->direction != UCPGBA_RTL) {
+ while (e > ld && ISWEAK_NEUTRAL(source[e - 1]))
+ e--;
+ }
+
+ /*
+ * Add the RTL segment to the string.
+ */
+ if (e > s)
+ _ucadd_rtl_segment(str, source, s, e);
+
+ /*
+ * Clear the flag that allowed the RTL collection loop to run first
+ * for strings with overall RTL directionality.
+ */
+ rtl_first = 0;
+ }
+
+ /*
+ * Set up the initial cursor run.
+ */
+ str->cursor = str->logical_first;
+ if (str != 0)
+ str->cursor->cursor = (str->cursor->direction == UCPGBA_RTL) ?
+ str->cursor->end - str->cursor->start : 0;
+
+ return str;
+}
+
+void
+ucstring_free(ucstring_t *s)
+{
+ ucrun_t *l, *r;
+
+ if (s == 0)
+ return;
+
+ for (l = 0, r = s->visual_first; r != 0; r = r->visual_next) {
+ if (r->end > r->start)
+ free((char *) r->chars);
+ if (l)
+ free((char *) l);
+ l = r;
+ }
+ if (l)
+ free((char *) l);
+
+ free((char *) s);
+}
+
+int
+ucstring_set_cursor_motion(ucstring_t *str, int cursor_motion)
+{
+ int n;
+
+ if (str == 0)
+ return -1;
+
+ n = str->cursor_motion;
+ str->cursor_motion = cursor_motion;
+ return n;
+}
+
+static int
+_ucstring_visual_cursor_right(ucstring_t *str, int count)
+{
+ int cnt = count;
+ unsigned long size;
+ ucrun_t *cursor;
+
+ if (str == 0)
+ return 0;
+
+ cursor = str->cursor;
+ while (cnt > 0) {
+ size = cursor->end - cursor->start;
+ if ((cursor->direction == UCPGBA_RTL && cursor->cursor + 1 == size) ||
+ cursor->cursor + 1 > size) {
+ /*
+ * If the next run is NULL, then the cursor is already on the
+ * far right end already.
+ */
+ if (cursor->visual_next == 0)
+ /*
+ * If movement occurred, then report it.
+ */
+ return (cnt != count);
+
+ /*
+ * Move to the next run.
+ */
+ str->cursor = cursor = cursor->visual_next;
+ cursor->cursor = (cursor->direction == UCPGBA_RTL) ? -1 : 0;
+ size = cursor->end - cursor->start;
+ } else
+ cursor->cursor++;
+ cnt--;
+ }
+ return 1;
+}
+
+static int
+_ucstring_logical_cursor_right(ucstring_t *str, int count)
+{
+ int cnt = count;
+ unsigned long size;
+ ucrun_t *cursor;
+
+ if (str == 0)
+ return 0;
+
+ cursor = str->cursor;
+ while (cnt > 0) {
+ size = cursor->end - cursor->start;
+ if (str->direction == UCPGBA_RTL) {
+ if (cursor->direction == UCPGBA_RTL) {
+ if (cursor->cursor + 1 == size) {
+ if (cursor == str->logical_first)
+ /*
+ * Already at the beginning of the string.
+ */
+ return (cnt != count);
+
+ str->cursor = cursor = cursor->logical_prev;
+ size = cursor->end - cursor->start;
+ cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
+ size : 0;
+ } else
+ cursor->cursor++;
+ } else {
+ if (cursor->cursor == 0) {
+ if (cursor == str->logical_first)
+ /*
+ * At the beginning of the string already.
+ */
+ return (cnt != count);
+
+ str->cursor = cursor = cursor->logical_prev;
+ size = cursor->end - cursor->start;
+ cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
+ size : 0;
+ } else
+ cursor->cursor--;
+ }
+ } else {
+ if (cursor->direction == UCPGBA_RTL) {
+ if (cursor->cursor == 0) {
+ if (cursor == str->logical_last)
+ /*
+ * Already at the end of the string.
+ */
+ return (cnt != count);
+
+ str->cursor = cursor = cursor->logical_next;
+ size = cursor->end - cursor->start;
+ cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
+ 0 : size - 1;
+ } else
+ cursor->cursor--;
+ } else {
+ if (cursor->cursor + 1 > size) {
+ if (cursor == str->logical_last)
+ /*
+ * Already at the end of the string.
+ */
+ return (cnt != count);
+
+ str->cursor = cursor = cursor->logical_next;
+ cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
+ 0 : size - 1;
+ } else
+ cursor->cursor++;
+ }
+ }
+ cnt--;
+ }
+ return 1;
+}
+
+int
+ucstring_cursor_right(ucstring_t *str, int count)
+{
+ if (str == 0)
+ return 0;
+ return (str->cursor_motion == UCPGBA_CURSOR_VISUAL) ?
+ _ucstring_visual_cursor_right(str, count) :
+ _ucstring_logical_cursor_right(str, count);
+}
+
+static int
+_ucstring_visual_cursor_left(ucstring_t *str, int count)
+{
+ int cnt = count;
+ unsigned long size;
+ ucrun_t *cursor;
+
+ if (str == 0)
+ return 0;
+
+ cursor = str->cursor;
+ while (cnt > 0) {
+ size = cursor->end - cursor->start;
+ if ((cursor->direction == UCPGBA_LTR && cursor->cursor == 0) ||
+ cursor->cursor - 1 < -1) {
+ /*
+ * If the preceding run is NULL, then the cursor is already on the
+ * far left end already.
+ */
+ if (cursor->visual_prev == 0)
+ /*
+ * If movement occurred, then report it.
+ */
+ return (cnt != count);
+
+ /*
+ * Move to the previous run.
+ */
+ str->cursor = cursor = cursor->visual_prev;
+ size = cursor->end - cursor->start;
+ cursor->cursor = (cursor->direction == UCPGBA_RTL) ?
+ size : size - 1;
+ } else
+ cursor->cursor--;
+ cnt--;
+ }
+ return 1;
+}
+
+static int
+_ucstring_logical_cursor_left(ucstring_t *str, int count)
+{
+ int cnt = count;
+ unsigned long size;
+ ucrun_t *cursor;
+
+ if (str == 0)
+ return 0;
+
+ cursor = str->cursor;
+ while (cnt > 0) {
+ size = cursor->end - cursor->start;
+ if (str->direction == UCPGBA_RTL) {
+ if (cursor->direction == UCPGBA_RTL) {
+ if (cursor->cursor == -1) {
+ if (cursor == str->logical_last)
+ /*
+ * Already at the end of the string.
+ */
+ return (cnt != count);
+
+ str->cursor = cursor = cursor->logical_next;
+ size = cursor->end - cursor->start;
+ cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
+ 0 : size - 1;
+ } else
+ cursor->cursor--;
+ } else {
+ if (cursor->cursor + 1 > size) {
+ if (cursor == str->logical_last)
+ /*
+ * At the end of the string already.
+ */
+ return (cnt != count);
+
+ str->cursor = cursor = cursor->logical_next;
+ size = cursor->end - cursor->start;
+ cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
+ 0 : size - 1;
+ } else
+ cursor->cursor++;
+ }
+ } else {
+ if (cursor->direction == UCPGBA_RTL) {
+ if (cursor->cursor + 1 == size) {
+ if (cursor == str->logical_first)
+ /*
+ * Already at the beginning of the string.
+ */
+ return (cnt != count);
+
+ str->cursor = cursor = cursor->logical_prev;
+ size = cursor->end - cursor->start;
+ cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
+ size : 0;
+ } else
+ cursor->cursor++;
+ } else {
+ if (cursor->cursor == 0) {
+ if (cursor == str->logical_first)
+ /*
+ * Already at the beginning of the string.
+ */
+ return (cnt != count);
+
+ str->cursor = cursor = cursor->logical_prev;
+ cursor->cursor = (cursor->direction == UCPGBA_LTR) ?
+ size : 0;
+ } else
+ cursor->cursor--;
+ }
+ }
+ cnt--;
+ }
+ return 1;
+}
+
+int
+ucstring_cursor_left(ucstring_t *str, int count)
+{
+ if (str == 0)
+ return 0;
+ return (str->cursor_motion == UCPGBA_CURSOR_VISUAL) ?
+ _ucstring_visual_cursor_left(str, count) :
+ _ucstring_logical_cursor_left(str, count);
+}
+
+void
+ucstring_cursor_info(ucstring_t *str, int *direction, unsigned long *position)
+{
+ long c;
+ unsigned long size;
+ ucrun_t *cursor;
+
+ if (str == 0 || direction == 0 || position == 0)
+ return;
+
+ cursor = str->cursor;
+
+ *direction = cursor->direction;
+
+ c = cursor->cursor;
+ size = cursor->end - cursor->start;
+
+ if (c == size)
+ *position = (cursor->direction == UCPGBA_RTL) ?
+ cursor->start : cursor->positions[c - 1];
+ else if (c == -1)
+ *position = (cursor->direction == UCPGBA_RTL) ?
+ cursor->end : cursor->start;
+ else
+ *position = cursor->positions[c];
+}
diff --git a/libraries/liblunicode/ucdata/ucpgba.h b/libraries/liblunicode/ucdata/ucpgba.h
new file mode 100644
index 0000000..5281baa
--- /dev/null
+++ b/libraries/liblunicode/ucdata/ucpgba.h
@@ -0,0 +1,167 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Copyright 1999 Computing Research Labs, New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+ * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* $Id: ucpgba.h,v 1.4 1999/11/19 15:24:30 mleisher Exp $ */
+
+#ifndef _h_ucpgba
+#define _h_ucpgba
+
+#include "portable.h"
+
+LDAP_BEGIN_DECL
+
+/***************************************************************************
+ *
+ * Macros and types.
+ *
+ ***************************************************************************/
+
+/*
+ * These are the direction values that can appear in render runs and render
+ * strings.
+ */
+#define UCPGBA_LTR 0
+#define UCPGBA_RTL 1
+
+/*
+ * These are the flags for cursor motion.
+ */
+#define UCPGBA_CURSOR_VISUAL 0
+#define UCPGBA_CURSOR_LOGICAL 1
+
+/*
+ * This structure is used to contain runs of text in a particular direction.
+ */
+typedef struct _ucrun_t {
+ struct _ucrun_t *visual_prev; /* Pointer to the previous visual run. */
+ struct _ucrun_t *visual_next; /* Pointer to the next visual run. */
+
+ struct _ucrun_t *logical_prev; /* Pointer to the previous logical run. */
+ struct _ucrun_t *logical_next; /* Pointer to the next logical run. */
+
+ int direction; /* Direction of the run. */
+
+ long cursor; /* Position of "cursor" in the string. */
+
+ unsigned long *chars; /* List of characters for the run. */
+ unsigned long *positions; /* List of original positions in source. */
+
+ unsigned long *source; /* The source string. */
+ unsigned long start; /* Beginning offset in the source string. */
+ unsigned long end; /* Ending offset in the source string. */
+} ucrun_t;
+
+/*
+ * This represents a string of runs rendered up to a point that is not
+ * platform specific.
+ */
+typedef struct _ucstring_t {
+ int direction; /* Overall direction of the string. */
+
+ int cursor_motion; /* Logical or visual cursor motion flag. */
+
+ ucrun_t *cursor; /* The run containing the "cursor." */
+
+ ucrun_t *logical_first; /* First run in the logical order. */
+ ucrun_t *logical_last; /* Last run in the logical order. */
+
+ ucrun_t *visual_first; /* First run in the visual order. */
+ ucrun_t *visual_last; /* Last run in the visual order. */
+
+ unsigned long *source; /* The source string. */
+ unsigned long start; /* The beginning offset in the source. */
+ unsigned long end; /* The ending offset in the source. */
+} ucstring_t;
+
+/***************************************************************************
+ *
+ * API
+ *
+ ***************************************************************************/
+
+/*
+ * This creates and reorders the specified substring using the
+ * "Pretty Good Bidi Algorithm." A default direction is provided for cases
+ * of a string containing no strong direction characters and the default
+ * cursor motion should be provided.
+ */
+LDAP_LUNICODE_F (ucstring_t *)
+ucstring_create LDAP_P((unsigned long *source,
+ unsigned long start,
+ unsigned long end,
+ int default_direction,
+ int cursor_motion));
+/*
+ * This releases the string.
+ */
+LDAP_LUNICODE_F (void) ucstring_free LDAP_P((ucstring_t *string));
+
+/*
+ * This changes the cursor motion flag for the string.
+ */
+LDAP_LUNICODE_F (int)
+ucstring_set_cursor_motion LDAP_P((ucstring_t *string,
+ int cursor_motion));
+
+/*
+ * This function will move the cursor to the right depending on the
+ * type of cursor motion that was specified for the string.
+ *
+ * A 0 is returned if no cursor motion is performed, otherwise a
+ * 1 is returned.
+ */
+LDAP_LUNICODE_F (int)
+ucstring_cursor_right LDAP_P((ucstring_t *string, int count));
+
+/*
+ * This function will move the cursor to the left depending on the
+ * type of cursor motion that was specified for the string.
+ *
+ * A 0 is returned if no cursor motion is performed, otherwise a
+ * 1 is returned.
+ */
+LDAP_LUNICODE_F (int)
+ucstring_cursor_left LDAP_P((ucstring_t *string, int count));
+
+/*
+ * This routine retrieves the direction of the run containing the cursor
+ * and the actual position in the original text string.
+ */
+LDAP_LUNICODE_F (void)
+ucstring_cursor_info LDAP_P((ucstring_t *string, int *direction,
+ unsigned long *position));
+
+LDAP_END_DECL
+
+#endif /* _h_ucpgba */
diff --git a/libraries/liblunicode/ucdata/ucpgba.man b/libraries/liblunicode/ucdata/ucpgba.man
new file mode 100644
index 0000000..6d8225e
--- /dev/null
+++ b/libraries/liblunicode/ucdata/ucpgba.man
@@ -0,0 +1,97 @@
+.\"
+.\" $Id: ucpgba.man,v 1.1 1999/11/19 16:08:34 mleisher Exp $
+.\"
+.TH ucpgba 3 "19 November 1999"
+.SH NAME
+ucpgba \- functions for doing bidirectional reordering of Unicode text and
+logical and visual cursor motion
+
+.SH SYNOPSIS
+.nf
+#include <ucdata.h>
+#include <ucpgba.h>
+
+ucstring_t *ucstring_create(unsigned long *source, unsigned long start,
+ unsigned long end, int default_direction,
+ int cursor_motion)
+.sp
+void ucstring_free(ucstring_t *string)
+.sp
+int ucstring_set_cursor_motion(ucstring_t *string, int cursor_motion)
+.sp
+int ucstring_cursor_right(ucstring_t *string, int count)
+.sp
+int ucstring_cursor_left(ucstring_t *string, int count)
+.sp
+void ucstring_cursor_info(ucstring_t *string, int *direction,
+ unsigned long *position)
+
+.SH DESCRIPTION
+.TP 4
+.BR Macros
+UCPGBA_LTR
+.br
+UCPGBA_RTL
+.br
+UCPGBA_CURSOR_VISUAL
+.br
+UCPGBA_CURSOR_LOGICAL
+
+.TP 4
+.BR ucstring_create()
+This function will create a reordered string by using the implicit
+directionality of the characters in the specified substring.
+.sp
+The `default_direction' parameter should be one of UCPGBA_LTR or UCPGBA_RTL
+and is used only in cases where a string contains no characters with strong
+directionality.
+.sp
+The `cursor_motion' parameter should be one of UCPGBA_CURSOR_VISUAL or
+UCPGBA_CURSOR_LOGICAL, and is used to specify the initial cursor motion
+behavior. This behavior can be switched at any time using
+ustring_set_cursor_motion().
+
+.TP 4
+.BR ucstring_free()
+This function will deallocate the memory used by the string, including the
+string itself.
+
+.TP 4
+.BR ucstring_cursor_info()
+This function will return the text position of the internal cursor and the
+directionality of the text at that position. The position returned is the
+original text position of the character.
+
+.TP 4
+.BR ucstring_set_cursor_motion()
+This function will change the cursor motion type and return the previous
+cursor motion type.
+
+.TP 4
+.BR ucstring_cursor_right()
+This function will move the internal cursor to the right according to the
+type of cursor motion set for the string.
+.sp
+If no cursor motion is performed, it returns 0. Otherwise it will return a 1.
+
+.TP 4
+.BR ucstring_cursor_left()
+This function will move the internal cursor to the left according to the
+type of cursor motion set for the string.
+.sp
+If no cursor motion is performed, it returns 0. Otherwise it will return a 1.
+
+.SH "SEE ALSO"
+ucdata(3)
+
+.SH ACKNOWLEDGMENTS
+These are people who have helped with patches or alerted me about problems.
+
+.SH AUTHOR
+Mark Leisher
+.br
+Computing Research Lab
+.br
+New Mexico State University
+.br
+Email: mleisher@crl.nmsu.edu
diff --git a/libraries/liblunicode/ucdata/uctable.h b/libraries/liblunicode/ucdata/uctable.h
new file mode 100644
index 0000000..f6c06e9
--- /dev/null
+++ b/libraries/liblunicode/ucdata/uctable.h
@@ -0,0 +1,14306 @@
+static const ac_uint4 _ucprop_size = 50;
+
+static const ac_uint2 _ucprop_offsets[] = {
+ 0x0000, 0x00d0, 0x0138, 0x0140, 0x016a, 0x0176, 0x019e, 0x01ac,
+ 0x01ae, 0x01b0, 0x01b4, 0x01cc, 0x01ce, 0xffff, 0x01d4, 0x051a,
+ 0x0862, 0x0876, 0x089e, 0x0a32, 0x0a40, 0x0a58, 0x0ad8, 0x0b54,
+ 0x0be0, 0x0c54, 0x0c6a, 0x0c96, 0x0d66, 0x0fee, 0x100a, 0x1020,
+ 0x1024, 0x1054, 0x1058, 0x106e, 0x1078, 0x107e, 0x108e, 0x1240,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0x13e8, 0x16e4,
+ 0x16ee, 0x16f6, 0x1720, 0x0000
+};
+
+static const ac_uint4 _ucprop_ranges[] = {
+ 0x00000300, 0x0000034f, 0x00000360, 0x0000036f,
+ 0x00000483, 0x00000486, 0x00000591, 0x000005a1,
+ 0x000005a3, 0x000005b9, 0x000005bb, 0x000005bd,
+ 0x000005bf, 0x000005bf, 0x000005c1, 0x000005c2,
+ 0x000005c4, 0x000005c4, 0x0000064b, 0x00000655,
+ 0x00000670, 0x00000670, 0x000006d6, 0x000006dc,
+ 0x000006df, 0x000006e4, 0x000006e7, 0x000006e8,
+ 0x000006ea, 0x000006ed, 0x00000711, 0x00000711,
+ 0x00000730, 0x0000074a, 0x000007a6, 0x000007b0,
+ 0x00000901, 0x00000902, 0x0000093c, 0x0000093c,
+ 0x00000941, 0x00000948, 0x0000094d, 0x0000094d,
+ 0x00000951, 0x00000954, 0x00000962, 0x00000963,
+ 0x00000981, 0x00000981, 0x000009bc, 0x000009bc,
+ 0x000009c1, 0x000009c4, 0x000009cd, 0x000009cd,
+ 0x000009e2, 0x000009e3, 0x00000a02, 0x00000a02,
+ 0x00000a3c, 0x00000a3c, 0x00000a41, 0x00000a42,
+ 0x00000a47, 0x00000a48, 0x00000a4b, 0x00000a4d,
+ 0x00000a70, 0x00000a71, 0x00000a81, 0x00000a82,
+ 0x00000abc, 0x00000abc, 0x00000ac1, 0x00000ac5,
+ 0x00000ac7, 0x00000ac8, 0x00000acd, 0x00000acd,
+ 0x00000b01, 0x00000b01, 0x00000b3c, 0x00000b3c,
+ 0x00000b3f, 0x00000b3f, 0x00000b41, 0x00000b43,
+ 0x00000b4d, 0x00000b4d, 0x00000b56, 0x00000b56,
+ 0x00000b82, 0x00000b82, 0x00000bc0, 0x00000bc0,
+ 0x00000bcd, 0x00000bcd, 0x00000c3e, 0x00000c40,
+ 0x00000c46, 0x00000c48, 0x00000c4a, 0x00000c4d,
+ 0x00000c55, 0x00000c56, 0x00000cbf, 0x00000cbf,
+ 0x00000cc6, 0x00000cc6, 0x00000ccc, 0x00000ccd,
+ 0x00000d41, 0x00000d43, 0x00000d4d, 0x00000d4d,
+ 0x00000dca, 0x00000dca, 0x00000dd2, 0x00000dd4,
+ 0x00000dd6, 0x00000dd6, 0x00000e31, 0x00000e31,
+ 0x00000e34, 0x00000e3a, 0x00000e47, 0x00000e4e,
+ 0x00000eb1, 0x00000eb1, 0x00000eb4, 0x00000eb9,
+ 0x00000ebb, 0x00000ebc, 0x00000ec8, 0x00000ecd,
+ 0x00000f18, 0x00000f19, 0x00000f35, 0x00000f35,
+ 0x00000f37, 0x00000f37, 0x00000f39, 0x00000f39,
+ 0x00000f71, 0x00000f7e, 0x00000f80, 0x00000f84,
+ 0x00000f86, 0x00000f87, 0x00000f90, 0x00000f97,
+ 0x00000f99, 0x00000fbc, 0x00000fc6, 0x00000fc6,
+ 0x0000102d, 0x00001030, 0x00001032, 0x00001032,
+ 0x00001036, 0x00001037, 0x00001039, 0x00001039,
+ 0x00001058, 0x00001059, 0x00001712, 0x00001714,
+ 0x00001732, 0x00001734, 0x00001752, 0x00001753,
+ 0x00001772, 0x00001773, 0x000017b7, 0x000017bd,
+ 0x000017c6, 0x000017c6, 0x000017c9, 0x000017d3,
+ 0x0000180b, 0x0000180d, 0x000018a9, 0x000018a9,
+ 0x000020d0, 0x000020dc, 0x000020e1, 0x000020e1,
+ 0x000020e5, 0x000020ea, 0x0000302a, 0x0000302f,
+ 0x00003099, 0x0000309a, 0x0000fb1e, 0x0000fb1e,
+ 0x0000fe00, 0x0000fe0f, 0x0000fe20, 0x0000fe23,
+ 0x0001d167, 0x0001d169, 0x0001d17b, 0x0001d182,
+ 0x0001d185, 0x0001d18b, 0x0001d1aa, 0x0001d1ad,
+ 0x00000903, 0x00000903, 0x0000093e, 0x00000940,
+ 0x00000949, 0x0000094c, 0x00000982, 0x00000983,
+ 0x000009be, 0x000009c0, 0x000009c7, 0x000009c8,
+ 0x000009cb, 0x000009cc, 0x000009d7, 0x000009d7,
+ 0x00000a3e, 0x00000a40, 0x00000a83, 0x00000a83,
+ 0x00000abe, 0x00000ac0, 0x00000ac9, 0x00000ac9,
+ 0x00000acb, 0x00000acc, 0x00000b02, 0x00000b03,
+ 0x00000b3e, 0x00000b3e, 0x00000b40, 0x00000b40,
+ 0x00000b47, 0x00000b48, 0x00000b4b, 0x00000b4c,
+ 0x00000b57, 0x00000b57, 0x00000bbe, 0x00000bbf,
+ 0x00000bc1, 0x00000bc2, 0x00000bc6, 0x00000bc8,
+ 0x00000bca, 0x00000bcc, 0x00000bd7, 0x00000bd7,
+ 0x00000c01, 0x00000c03, 0x00000c41, 0x00000c44,
+ 0x00000c82, 0x00000c83, 0x00000cbe, 0x00000cbe,
+ 0x00000cc0, 0x00000cc4, 0x00000cc7, 0x00000cc8,
+ 0x00000cca, 0x00000ccb, 0x00000cd5, 0x00000cd6,
+ 0x00000d02, 0x00000d03, 0x00000d3e, 0x00000d40,
+ 0x00000d46, 0x00000d48, 0x00000d4a, 0x00000d4c,
+ 0x00000d57, 0x00000d57, 0x00000d82, 0x00000d83,
+ 0x00000dcf, 0x00000dd1, 0x00000dd8, 0x00000ddf,
+ 0x00000df2, 0x00000df3, 0x00000f3e, 0x00000f3f,
+ 0x00000f7f, 0x00000f7f, 0x0000102c, 0x0000102c,
+ 0x00001031, 0x00001031, 0x00001038, 0x00001038,
+ 0x00001056, 0x00001057, 0x000017b4, 0x000017b6,
+ 0x000017be, 0x000017c5, 0x000017c7, 0x000017c8,
+ 0x0001d165, 0x0001d166, 0x0001d16d, 0x0001d172,
+ 0x00000488, 0x00000489, 0x000006de, 0x000006de,
+ 0x000020dd, 0x000020e0, 0x000020e2, 0x000020e4,
+ 0x00000030, 0x00000039, 0x00000660, 0x00000669,
+ 0x000006f0, 0x000006f9, 0x00000966, 0x0000096f,
+ 0x000009e6, 0x000009ef, 0x00000a66, 0x00000a6f,
+ 0x00000ae6, 0x00000aef, 0x00000b66, 0x00000b6f,
+ 0x00000be7, 0x00000bef, 0x00000c66, 0x00000c6f,
+ 0x00000ce6, 0x00000cef, 0x00000d66, 0x00000d6f,
+ 0x00000e50, 0x00000e59, 0x00000ed0, 0x00000ed9,
+ 0x00000f20, 0x00000f29, 0x00001040, 0x00001049,
+ 0x00001369, 0x00001371, 0x000017e0, 0x000017e9,
+ 0x00001810, 0x00001819, 0x0000ff10, 0x0000ff19,
+ 0x0001d7ce, 0x0001d7ff, 0x000016ee, 0x000016f0,
+ 0x00002160, 0x00002183, 0x00003007, 0x00003007,
+ 0x00003021, 0x00003029, 0x00003038, 0x0000303a,
+ 0x0001034a, 0x0001034a, 0x000000b2, 0x000000b3,
+ 0x000000b9, 0x000000b9, 0x000000bc, 0x000000be,
+ 0x000009f4, 0x000009f9, 0x00000bf0, 0x00000bf2,
+ 0x00000f2a, 0x00000f33, 0x00001372, 0x0000137c,
+ 0x00002070, 0x00002070, 0x00002074, 0x00002079,
+ 0x00002080, 0x00002089, 0x00002153, 0x0000215f,
+ 0x00002460, 0x0000249b, 0x000024ea, 0x000024fe,
+ 0x00002776, 0x00002793, 0x00003192, 0x00003195,
+ 0x00003220, 0x00003229, 0x00003251, 0x0000325f,
+ 0x00003280, 0x00003289, 0x000032b1, 0x000032bf,
+ 0x00010320, 0x00010323, 0x00000020, 0x00000020,
+ 0x000000a0, 0x000000a0, 0x00001680, 0x00001680,
+ 0x00002000, 0x0000200b, 0x0000202f, 0x0000202f,
+ 0x0000205f, 0x0000205f, 0x00003000, 0x00003000,
+ 0x00002028, 0x00002028, 0x00002029, 0x00002029,
+ 0x00000000, 0x0000001f, 0x0000007f, 0x0000009f,
+ 0x000006dd, 0x000006dd, 0x0000070f, 0x0000070f,
+ 0x0000180e, 0x0000180e, 0x0000200c, 0x0000200f,
+ 0x0000202a, 0x0000202e, 0x00002060, 0x00002063,
+ 0x0000206a, 0x0000206f, 0x0000feff, 0x0000feff,
+ 0x0000fff9, 0x0000fffb, 0x0001d173, 0x0001d17a,
+ 0x000e0001, 0x000e0001, 0x000e0020, 0x000e007f,
+ 0x00010000, 0x0010ffff, 0x0000e000, 0x0000f8ff,
+ 0x000f0000, 0x000ffffd, 0x00100000, 0x0010fffd,
+ 0x00000041, 0x0000005a, 0x000000c0, 0x000000d6,
+ 0x000000d8, 0x000000de, 0x00000100, 0x00000100,
+ 0x00000102, 0x00000102, 0x00000104, 0x00000104,
+ 0x00000106, 0x00000106, 0x00000108, 0x00000108,
+ 0x0000010a, 0x0000010a, 0x0000010c, 0x0000010c,
+ 0x0000010e, 0x0000010e, 0x00000110, 0x00000110,
+ 0x00000112, 0x00000112, 0x00000114, 0x00000114,
+ 0x00000116, 0x00000116, 0x00000118, 0x00000118,
+ 0x0000011a, 0x0000011a, 0x0000011c, 0x0000011c,
+ 0x0000011e, 0x0000011e, 0x00000120, 0x00000120,
+ 0x00000122, 0x00000122, 0x00000124, 0x00000124,
+ 0x00000126, 0x00000126, 0x00000128, 0x00000128,
+ 0x0000012a, 0x0000012a, 0x0000012c, 0x0000012c,
+ 0x0000012e, 0x0000012e, 0x00000130, 0x00000130,
+ 0x00000132, 0x00000132, 0x00000134, 0x00000134,
+ 0x00000136, 0x00000136, 0x00000139, 0x00000139,
+ 0x0000013b, 0x0000013b, 0x0000013d, 0x0000013d,
+ 0x0000013f, 0x0000013f, 0x00000141, 0x00000141,
+ 0x00000143, 0x00000143, 0x00000145, 0x00000145,
+ 0x00000147, 0x00000147, 0x0000014a, 0x0000014a,
+ 0x0000014c, 0x0000014c, 0x0000014e, 0x0000014e,
+ 0x00000150, 0x00000150, 0x00000152, 0x00000152,
+ 0x00000154, 0x00000154, 0x00000156, 0x00000156,
+ 0x00000158, 0x00000158, 0x0000015a, 0x0000015a,
+ 0x0000015c, 0x0000015c, 0x0000015e, 0x0000015e,
+ 0x00000160, 0x00000160, 0x00000162, 0x00000162,
+ 0x00000164, 0x00000164, 0x00000166, 0x00000166,
+ 0x00000168, 0x00000168, 0x0000016a, 0x0000016a,
+ 0x0000016c, 0x0000016c, 0x0000016e, 0x0000016e,
+ 0x00000170, 0x00000170, 0x00000172, 0x00000172,
+ 0x00000174, 0x00000174, 0x00000176, 0x00000176,
+ 0x00000178, 0x00000179, 0x0000017b, 0x0000017b,
+ 0x0000017d, 0x0000017d, 0x00000181, 0x00000182,
+ 0x00000184, 0x00000184, 0x00000186, 0x00000187,
+ 0x00000189, 0x0000018b, 0x0000018e, 0x00000191,
+ 0x00000193, 0x00000194, 0x00000196, 0x00000198,
+ 0x0000019c, 0x0000019d, 0x0000019f, 0x000001a0,
+ 0x000001a2, 0x000001a2, 0x000001a4, 0x000001a4,
+ 0x000001a6, 0x000001a7, 0x000001a9, 0x000001a9,
+ 0x000001ac, 0x000001ac, 0x000001ae, 0x000001af,
+ 0x000001b1, 0x000001b3, 0x000001b5, 0x000001b5,
+ 0x000001b7, 0x000001b8, 0x000001bc, 0x000001bc,
+ 0x000001c4, 0x000001c4, 0x000001c7, 0x000001c7,
+ 0x000001ca, 0x000001ca, 0x000001cd, 0x000001cd,
+ 0x000001cf, 0x000001cf, 0x000001d1, 0x000001d1,
+ 0x000001d3, 0x000001d3, 0x000001d5, 0x000001d5,
+ 0x000001d7, 0x000001d7, 0x000001d9, 0x000001d9,
+ 0x000001db, 0x000001db, 0x000001de, 0x000001de,
+ 0x000001e0, 0x000001e0, 0x000001e2, 0x000001e2,
+ 0x000001e4, 0x000001e4, 0x000001e6, 0x000001e6,
+ 0x000001e8, 0x000001e8, 0x000001ea, 0x000001ea,
+ 0x000001ec, 0x000001ec, 0x000001ee, 0x000001ee,
+ 0x000001f1, 0x000001f1, 0x000001f4, 0x000001f4,
+ 0x000001f6, 0x000001f8, 0x000001fa, 0x000001fa,
+ 0x000001fc, 0x000001fc, 0x000001fe, 0x000001fe,
+ 0x00000200, 0x00000200, 0x00000202, 0x00000202,
+ 0x00000204, 0x00000204, 0x00000206, 0x00000206,
+ 0x00000208, 0x00000208, 0x0000020a, 0x0000020a,
+ 0x0000020c, 0x0000020c, 0x0000020e, 0x0000020e,
+ 0x00000210, 0x00000210, 0x00000212, 0x00000212,
+ 0x00000214, 0x00000214, 0x00000216, 0x00000216,
+ 0x00000218, 0x00000218, 0x0000021a, 0x0000021a,
+ 0x0000021c, 0x0000021c, 0x0000021e, 0x0000021e,
+ 0x00000220, 0x00000220, 0x00000222, 0x00000222,
+ 0x00000224, 0x00000224, 0x00000226, 0x00000226,
+ 0x00000228, 0x00000228, 0x0000022a, 0x0000022a,
+ 0x0000022c, 0x0000022c, 0x0000022e, 0x0000022e,
+ 0x00000230, 0x00000230, 0x00000232, 0x00000232,
+ 0x00000386, 0x00000386, 0x00000388, 0x0000038a,
+ 0x0000038c, 0x0000038c, 0x0000038e, 0x0000038f,
+ 0x00000391, 0x000003a1, 0x000003a3, 0x000003ab,
+ 0x000003d2, 0x000003d4, 0x000003d8, 0x000003d8,
+ 0x000003da, 0x000003da, 0x000003dc, 0x000003dc,
+ 0x000003de, 0x000003de, 0x000003e0, 0x000003e0,
+ 0x000003e2, 0x000003e2, 0x000003e4, 0x000003e4,
+ 0x000003e6, 0x000003e6, 0x000003e8, 0x000003e8,
+ 0x000003ea, 0x000003ea, 0x000003ec, 0x000003ec,
+ 0x000003ee, 0x000003ee, 0x000003f4, 0x000003f4,
+ 0x00000400, 0x0000042f, 0x00000460, 0x00000460,
+ 0x00000462, 0x00000462, 0x00000464, 0x00000464,
+ 0x00000466, 0x00000466, 0x00000468, 0x00000468,
+ 0x0000046a, 0x0000046a, 0x0000046c, 0x0000046c,
+ 0x0000046e, 0x0000046e, 0x00000470, 0x00000470,
+ 0x00000472, 0x00000472, 0x00000474, 0x00000474,
+ 0x00000476, 0x00000476, 0x00000478, 0x00000478,
+ 0x0000047a, 0x0000047a, 0x0000047c, 0x0000047c,
+ 0x0000047e, 0x0000047e, 0x00000480, 0x00000480,
+ 0x0000048a, 0x0000048a, 0x0000048c, 0x0000048c,
+ 0x0000048e, 0x0000048e, 0x00000490, 0x00000490,
+ 0x00000492, 0x00000492, 0x00000494, 0x00000494,
+ 0x00000496, 0x00000496, 0x00000498, 0x00000498,
+ 0x0000049a, 0x0000049a, 0x0000049c, 0x0000049c,
+ 0x0000049e, 0x0000049e, 0x000004a0, 0x000004a0,
+ 0x000004a2, 0x000004a2, 0x000004a4, 0x000004a4,
+ 0x000004a6, 0x000004a6, 0x000004a8, 0x000004a8,
+ 0x000004aa, 0x000004aa, 0x000004ac, 0x000004ac,
+ 0x000004ae, 0x000004ae, 0x000004b0, 0x000004b0,
+ 0x000004b2, 0x000004b2, 0x000004b4, 0x000004b4,
+ 0x000004b6, 0x000004b6, 0x000004b8, 0x000004b8,
+ 0x000004ba, 0x000004ba, 0x000004bc, 0x000004bc,
+ 0x000004be, 0x000004be, 0x000004c0, 0x000004c1,
+ 0x000004c3, 0x000004c3, 0x000004c5, 0x000004c5,
+ 0x000004c7, 0x000004c7, 0x000004c9, 0x000004c9,
+ 0x000004cb, 0x000004cb, 0x000004cd, 0x000004cd,
+ 0x000004d0, 0x000004d0, 0x000004d2, 0x000004d2,
+ 0x000004d4, 0x000004d4, 0x000004d6, 0x000004d6,
+ 0x000004d8, 0x000004d8, 0x000004da, 0x000004da,
+ 0x000004dc, 0x000004dc, 0x000004de, 0x000004de,
+ 0x000004e0, 0x000004e0, 0x000004e2, 0x000004e2,
+ 0x000004e4, 0x000004e4, 0x000004e6, 0x000004e6,
+ 0x000004e8, 0x000004e8, 0x000004ea, 0x000004ea,
+ 0x000004ec, 0x000004ec, 0x000004ee, 0x000004ee,
+ 0x000004f0, 0x000004f0, 0x000004f2, 0x000004f2,
+ 0x000004f4, 0x000004f4, 0x000004f8, 0x000004f8,
+ 0x00000500, 0x00000500, 0x00000502, 0x00000502,
+ 0x00000504, 0x00000504, 0x00000506, 0x00000506,
+ 0x00000508, 0x00000508, 0x0000050a, 0x0000050a,
+ 0x0000050c, 0x0000050c, 0x0000050e, 0x0000050e,
+ 0x00000531, 0x00000556, 0x000010a0, 0x000010c5,
+ 0x00001e00, 0x00001e00, 0x00001e02, 0x00001e02,
+ 0x00001e04, 0x00001e04, 0x00001e06, 0x00001e06,
+ 0x00001e08, 0x00001e08, 0x00001e0a, 0x00001e0a,
+ 0x00001e0c, 0x00001e0c, 0x00001e0e, 0x00001e0e,
+ 0x00001e10, 0x00001e10, 0x00001e12, 0x00001e12,
+ 0x00001e14, 0x00001e14, 0x00001e16, 0x00001e16,
+ 0x00001e18, 0x00001e18, 0x00001e1a, 0x00001e1a,
+ 0x00001e1c, 0x00001e1c, 0x00001e1e, 0x00001e1e,
+ 0x00001e20, 0x00001e20, 0x00001e22, 0x00001e22,
+ 0x00001e24, 0x00001e24, 0x00001e26, 0x00001e26,
+ 0x00001e28, 0x00001e28, 0x00001e2a, 0x00001e2a,
+ 0x00001e2c, 0x00001e2c, 0x00001e2e, 0x00001e2e,
+ 0x00001e30, 0x00001e30, 0x00001e32, 0x00001e32,
+ 0x00001e34, 0x00001e34, 0x00001e36, 0x00001e36,
+ 0x00001e38, 0x00001e38, 0x00001e3a, 0x00001e3a,
+ 0x00001e3c, 0x00001e3c, 0x00001e3e, 0x00001e3e,
+ 0x00001e40, 0x00001e40, 0x00001e42, 0x00001e42,
+ 0x00001e44, 0x00001e44, 0x00001e46, 0x00001e46,
+ 0x00001e48, 0x00001e48, 0x00001e4a, 0x00001e4a,
+ 0x00001e4c, 0x00001e4c, 0x00001e4e, 0x00001e4e,
+ 0x00001e50, 0x00001e50, 0x00001e52, 0x00001e52,
+ 0x00001e54, 0x00001e54, 0x00001e56, 0x00001e56,
+ 0x00001e58, 0x00001e58, 0x00001e5a, 0x00001e5a,
+ 0x00001e5c, 0x00001e5c, 0x00001e5e, 0x00001e5e,
+ 0x00001e60, 0x00001e60, 0x00001e62, 0x00001e62,
+ 0x00001e64, 0x00001e64, 0x00001e66, 0x00001e66,
+ 0x00001e68, 0x00001e68, 0x00001e6a, 0x00001e6a,
+ 0x00001e6c, 0x00001e6c, 0x00001e6e, 0x00001e6e,
+ 0x00001e70, 0x00001e70, 0x00001e72, 0x00001e72,
+ 0x00001e74, 0x00001e74, 0x00001e76, 0x00001e76,
+ 0x00001e78, 0x00001e78, 0x00001e7a, 0x00001e7a,
+ 0x00001e7c, 0x00001e7c, 0x00001e7e, 0x00001e7e,
+ 0x00001e80, 0x00001e80, 0x00001e82, 0x00001e82,
+ 0x00001e84, 0x00001e84, 0x00001e86, 0x00001e86,
+ 0x00001e88, 0x00001e88, 0x00001e8a, 0x00001e8a,
+ 0x00001e8c, 0x00001e8c, 0x00001e8e, 0x00001e8e,
+ 0x00001e90, 0x00001e90, 0x00001e92, 0x00001e92,
+ 0x00001e94, 0x00001e94, 0x00001ea0, 0x00001ea0,
+ 0x00001ea2, 0x00001ea2, 0x00001ea4, 0x00001ea4,
+ 0x00001ea6, 0x00001ea6, 0x00001ea8, 0x00001ea8,
+ 0x00001eaa, 0x00001eaa, 0x00001eac, 0x00001eac,
+ 0x00001eae, 0x00001eae, 0x00001eb0, 0x00001eb0,
+ 0x00001eb2, 0x00001eb2, 0x00001eb4, 0x00001eb4,
+ 0x00001eb6, 0x00001eb6, 0x00001eb8, 0x00001eb8,
+ 0x00001eba, 0x00001eba, 0x00001ebc, 0x00001ebc,
+ 0x00001ebe, 0x00001ebe, 0x00001ec0, 0x00001ec0,
+ 0x00001ec2, 0x00001ec2, 0x00001ec4, 0x00001ec4,
+ 0x00001ec6, 0x00001ec6, 0x00001ec8, 0x00001ec8,
+ 0x00001eca, 0x00001eca, 0x00001ecc, 0x00001ecc,
+ 0x00001ece, 0x00001ece, 0x00001ed0, 0x00001ed0,
+ 0x00001ed2, 0x00001ed2, 0x00001ed4, 0x00001ed4,
+ 0x00001ed6, 0x00001ed6, 0x00001ed8, 0x00001ed8,
+ 0x00001eda, 0x00001eda, 0x00001edc, 0x00001edc,
+ 0x00001ede, 0x00001ede, 0x00001ee0, 0x00001ee0,
+ 0x00001ee2, 0x00001ee2, 0x00001ee4, 0x00001ee4,
+ 0x00001ee6, 0x00001ee6, 0x00001ee8, 0x00001ee8,
+ 0x00001eea, 0x00001eea, 0x00001eec, 0x00001eec,
+ 0x00001eee, 0x00001eee, 0x00001ef0, 0x00001ef0,
+ 0x00001ef2, 0x00001ef2, 0x00001ef4, 0x00001ef4,
+ 0x00001ef6, 0x00001ef6, 0x00001ef8, 0x00001ef8,
+ 0x00001f08, 0x00001f0f, 0x00001f18, 0x00001f1d,
+ 0x00001f28, 0x00001f2f, 0x00001f38, 0x00001f3f,
+ 0x00001f48, 0x00001f4d, 0x00001f59, 0x00001f59,
+ 0x00001f5b, 0x00001f5b, 0x00001f5d, 0x00001f5d,
+ 0x00001f5f, 0x00001f5f, 0x00001f68, 0x00001f6f,
+ 0x00001fb8, 0x00001fbb, 0x00001fc8, 0x00001fcb,
+ 0x00001fd8, 0x00001fdb, 0x00001fe8, 0x00001fec,
+ 0x00001ff8, 0x00001ffb, 0x00002102, 0x00002102,
+ 0x00002107, 0x00002107, 0x0000210b, 0x0000210d,
+ 0x00002110, 0x00002112, 0x00002115, 0x00002115,
+ 0x00002119, 0x0000211d, 0x00002124, 0x00002124,
+ 0x00002126, 0x00002126, 0x00002128, 0x00002128,
+ 0x0000212a, 0x0000212d, 0x00002130, 0x00002131,
+ 0x00002133, 0x00002133, 0x0000213e, 0x0000213f,
+ 0x00002145, 0x00002145, 0x0000ff21, 0x0000ff3a,
+ 0x00010400, 0x00010425, 0x0001d400, 0x0001d419,
+ 0x0001d434, 0x0001d44d, 0x0001d468, 0x0001d481,
+ 0x0001d49c, 0x0001d49c, 0x0001d49e, 0x0001d49f,
+ 0x0001d4a2, 0x0001d4a2, 0x0001d4a5, 0x0001d4a6,
+ 0x0001d4a9, 0x0001d4ac, 0x0001d4ae, 0x0001d4b5,
+ 0x0001d4d0, 0x0001d4e9, 0x0001d504, 0x0001d505,
+ 0x0001d507, 0x0001d50a, 0x0001d50d, 0x0001d514,
+ 0x0001d516, 0x0001d51c, 0x0001d538, 0x0001d539,
+ 0x0001d53b, 0x0001d53e, 0x0001d540, 0x0001d544,
+ 0x0001d546, 0x0001d546, 0x0001d54a, 0x0001d550,
+ 0x0001d56c, 0x0001d585, 0x0001d5a0, 0x0001d5b9,
+ 0x0001d5d4, 0x0001d5ed, 0x0001d608, 0x0001d621,
+ 0x0001d63c, 0x0001d655, 0x0001d670, 0x0001d689,
+ 0x0001d6a8, 0x0001d6c0, 0x0001d6e2, 0x0001d6fa,
+ 0x0001d71c, 0x0001d734, 0x0001d756, 0x0001d76e,
+ 0x0001d790, 0x0001d7a8, 0x00000061, 0x0000007a,
+ 0x000000aa, 0x000000aa, 0x000000b5, 0x000000b5,
+ 0x000000ba, 0x000000ba, 0x000000df, 0x000000f6,
+ 0x000000f8, 0x000000ff, 0x00000101, 0x00000101,
+ 0x00000103, 0x00000103, 0x00000105, 0x00000105,
+ 0x00000107, 0x00000107, 0x00000109, 0x00000109,
+ 0x0000010b, 0x0000010b, 0x0000010d, 0x0000010d,
+ 0x0000010f, 0x0000010f, 0x00000111, 0x00000111,
+ 0x00000113, 0x00000113, 0x00000115, 0x00000115,
+ 0x00000117, 0x00000117, 0x00000119, 0x00000119,
+ 0x0000011b, 0x0000011b, 0x0000011d, 0x0000011d,
+ 0x0000011f, 0x0000011f, 0x00000121, 0x00000121,
+ 0x00000123, 0x00000123, 0x00000125, 0x00000125,
+ 0x00000127, 0x00000127, 0x00000129, 0x00000129,
+ 0x0000012b, 0x0000012b, 0x0000012d, 0x0000012d,
+ 0x0000012f, 0x0000012f, 0x00000131, 0x00000131,
+ 0x00000133, 0x00000133, 0x00000135, 0x00000135,
+ 0x00000137, 0x00000138, 0x0000013a, 0x0000013a,
+ 0x0000013c, 0x0000013c, 0x0000013e, 0x0000013e,
+ 0x00000140, 0x00000140, 0x00000142, 0x00000142,
+ 0x00000144, 0x00000144, 0x00000146, 0x00000146,
+ 0x00000148, 0x00000149, 0x0000014b, 0x0000014b,
+ 0x0000014d, 0x0000014d, 0x0000014f, 0x0000014f,
+ 0x00000151, 0x00000151, 0x00000153, 0x00000153,
+ 0x00000155, 0x00000155, 0x00000157, 0x00000157,
+ 0x00000159, 0x00000159, 0x0000015b, 0x0000015b,
+ 0x0000015d, 0x0000015d, 0x0000015f, 0x0000015f,
+ 0x00000161, 0x00000161, 0x00000163, 0x00000163,
+ 0x00000165, 0x00000165, 0x00000167, 0x00000167,
+ 0x00000169, 0x00000169, 0x0000016b, 0x0000016b,
+ 0x0000016d, 0x0000016d, 0x0000016f, 0x0000016f,
+ 0x00000171, 0x00000171, 0x00000173, 0x00000173,
+ 0x00000175, 0x00000175, 0x00000177, 0x00000177,
+ 0x0000017a, 0x0000017a, 0x0000017c, 0x0000017c,
+ 0x0000017e, 0x00000180, 0x00000183, 0x00000183,
+ 0x00000185, 0x00000185, 0x00000188, 0x00000188,
+ 0x0000018c, 0x0000018d, 0x00000192, 0x00000192,
+ 0x00000195, 0x00000195, 0x00000199, 0x0000019b,
+ 0x0000019e, 0x0000019e, 0x000001a1, 0x000001a1,
+ 0x000001a3, 0x000001a3, 0x000001a5, 0x000001a5,
+ 0x000001a8, 0x000001a8, 0x000001aa, 0x000001ab,
+ 0x000001ad, 0x000001ad, 0x000001b0, 0x000001b0,
+ 0x000001b4, 0x000001b4, 0x000001b6, 0x000001b6,
+ 0x000001b9, 0x000001ba, 0x000001bd, 0x000001bf,
+ 0x000001c6, 0x000001c6, 0x000001c9, 0x000001c9,
+ 0x000001cc, 0x000001cc, 0x000001ce, 0x000001ce,
+ 0x000001d0, 0x000001d0, 0x000001d2, 0x000001d2,
+ 0x000001d4, 0x000001d4, 0x000001d6, 0x000001d6,
+ 0x000001d8, 0x000001d8, 0x000001da, 0x000001da,
+ 0x000001dc, 0x000001dd, 0x000001df, 0x000001df,
+ 0x000001e1, 0x000001e1, 0x000001e3, 0x000001e3,
+ 0x000001e5, 0x000001e5, 0x000001e7, 0x000001e7,
+ 0x000001e9, 0x000001e9, 0x000001eb, 0x000001eb,
+ 0x000001ed, 0x000001ed, 0x000001ef, 0x000001f0,
+ 0x000001f3, 0x000001f3, 0x000001f5, 0x000001f5,
+ 0x000001f9, 0x000001f9, 0x000001fb, 0x000001fb,
+ 0x000001fd, 0x000001fd, 0x000001ff, 0x000001ff,
+ 0x00000201, 0x00000201, 0x00000203, 0x00000203,
+ 0x00000205, 0x00000205, 0x00000207, 0x00000207,
+ 0x00000209, 0x00000209, 0x0000020b, 0x0000020b,
+ 0x0000020d, 0x0000020d, 0x0000020f, 0x0000020f,
+ 0x00000211, 0x00000211, 0x00000213, 0x00000213,
+ 0x00000215, 0x00000215, 0x00000217, 0x00000217,
+ 0x00000219, 0x00000219, 0x0000021b, 0x0000021b,
+ 0x0000021d, 0x0000021d, 0x0000021f, 0x0000021f,
+ 0x00000223, 0x00000223, 0x00000225, 0x00000225,
+ 0x00000227, 0x00000227, 0x00000229, 0x00000229,
+ 0x0000022b, 0x0000022b, 0x0000022d, 0x0000022d,
+ 0x0000022f, 0x0000022f, 0x00000231, 0x00000231,
+ 0x00000233, 0x00000233, 0x00000250, 0x000002ad,
+ 0x00000390, 0x00000390, 0x000003ac, 0x000003ce,
+ 0x000003d0, 0x000003d1, 0x000003d5, 0x000003d7,
+ 0x000003d9, 0x000003d9, 0x000003db, 0x000003db,
+ 0x000003dd, 0x000003dd, 0x000003df, 0x000003df,
+ 0x000003e1, 0x000003e1, 0x000003e3, 0x000003e3,
+ 0x000003e5, 0x000003e5, 0x000003e7, 0x000003e7,
+ 0x000003e9, 0x000003e9, 0x000003eb, 0x000003eb,
+ 0x000003ed, 0x000003ed, 0x000003ef, 0x000003f3,
+ 0x000003f5, 0x000003f5, 0x00000430, 0x0000045f,
+ 0x00000461, 0x00000461, 0x00000463, 0x00000463,
+ 0x00000465, 0x00000465, 0x00000467, 0x00000467,
+ 0x00000469, 0x00000469, 0x0000046b, 0x0000046b,
+ 0x0000046d, 0x0000046d, 0x0000046f, 0x0000046f,
+ 0x00000471, 0x00000471, 0x00000473, 0x00000473,
+ 0x00000475, 0x00000475, 0x00000477, 0x00000477,
+ 0x00000479, 0x00000479, 0x0000047b, 0x0000047b,
+ 0x0000047d, 0x0000047d, 0x0000047f, 0x0000047f,
+ 0x00000481, 0x00000481, 0x0000048b, 0x0000048b,
+ 0x0000048d, 0x0000048d, 0x0000048f, 0x0000048f,
+ 0x00000491, 0x00000491, 0x00000493, 0x00000493,
+ 0x00000495, 0x00000495, 0x00000497, 0x00000497,
+ 0x00000499, 0x00000499, 0x0000049b, 0x0000049b,
+ 0x0000049d, 0x0000049d, 0x0000049f, 0x0000049f,
+ 0x000004a1, 0x000004a1, 0x000004a3, 0x000004a3,
+ 0x000004a5, 0x000004a5, 0x000004a7, 0x000004a7,
+ 0x000004a9, 0x000004a9, 0x000004ab, 0x000004ab,
+ 0x000004ad, 0x000004ad, 0x000004af, 0x000004af,
+ 0x000004b1, 0x000004b1, 0x000004b3, 0x000004b3,
+ 0x000004b5, 0x000004b5, 0x000004b7, 0x000004b7,
+ 0x000004b9, 0x000004b9, 0x000004bb, 0x000004bb,
+ 0x000004bd, 0x000004bd, 0x000004bf, 0x000004bf,
+ 0x000004c2, 0x000004c2, 0x000004c4, 0x000004c4,
+ 0x000004c6, 0x000004c6, 0x000004c8, 0x000004c8,
+ 0x000004ca, 0x000004ca, 0x000004cc, 0x000004cc,
+ 0x000004ce, 0x000004ce, 0x000004d1, 0x000004d1,
+ 0x000004d3, 0x000004d3, 0x000004d5, 0x000004d5,
+ 0x000004d7, 0x000004d7, 0x000004d9, 0x000004d9,
+ 0x000004db, 0x000004db, 0x000004dd, 0x000004dd,
+ 0x000004df, 0x000004df, 0x000004e1, 0x000004e1,
+ 0x000004e3, 0x000004e3, 0x000004e5, 0x000004e5,
+ 0x000004e7, 0x000004e7, 0x000004e9, 0x000004e9,
+ 0x000004eb, 0x000004eb, 0x000004ed, 0x000004ed,
+ 0x000004ef, 0x000004ef, 0x000004f1, 0x000004f1,
+ 0x000004f3, 0x000004f3, 0x000004f5, 0x000004f5,
+ 0x000004f9, 0x000004f9, 0x00000501, 0x00000501,
+ 0x00000503, 0x00000503, 0x00000505, 0x00000505,
+ 0x00000507, 0x00000507, 0x00000509, 0x00000509,
+ 0x0000050b, 0x0000050b, 0x0000050d, 0x0000050d,
+ 0x0000050f, 0x0000050f, 0x00000561, 0x00000587,
+ 0x00001e01, 0x00001e01, 0x00001e03, 0x00001e03,
+ 0x00001e05, 0x00001e05, 0x00001e07, 0x00001e07,
+ 0x00001e09, 0x00001e09, 0x00001e0b, 0x00001e0b,
+ 0x00001e0d, 0x00001e0d, 0x00001e0f, 0x00001e0f,
+ 0x00001e11, 0x00001e11, 0x00001e13, 0x00001e13,
+ 0x00001e15, 0x00001e15, 0x00001e17, 0x00001e17,
+ 0x00001e19, 0x00001e19, 0x00001e1b, 0x00001e1b,
+ 0x00001e1d, 0x00001e1d, 0x00001e1f, 0x00001e1f,
+ 0x00001e21, 0x00001e21, 0x00001e23, 0x00001e23,
+ 0x00001e25, 0x00001e25, 0x00001e27, 0x00001e27,
+ 0x00001e29, 0x00001e29, 0x00001e2b, 0x00001e2b,
+ 0x00001e2d, 0x00001e2d, 0x00001e2f, 0x00001e2f,
+ 0x00001e31, 0x00001e31, 0x00001e33, 0x00001e33,
+ 0x00001e35, 0x00001e35, 0x00001e37, 0x00001e37,
+ 0x00001e39, 0x00001e39, 0x00001e3b, 0x00001e3b,
+ 0x00001e3d, 0x00001e3d, 0x00001e3f, 0x00001e3f,
+ 0x00001e41, 0x00001e41, 0x00001e43, 0x00001e43,
+ 0x00001e45, 0x00001e45, 0x00001e47, 0x00001e47,
+ 0x00001e49, 0x00001e49, 0x00001e4b, 0x00001e4b,
+ 0x00001e4d, 0x00001e4d, 0x00001e4f, 0x00001e4f,
+ 0x00001e51, 0x00001e51, 0x00001e53, 0x00001e53,
+ 0x00001e55, 0x00001e55, 0x00001e57, 0x00001e57,
+ 0x00001e59, 0x00001e59, 0x00001e5b, 0x00001e5b,
+ 0x00001e5d, 0x00001e5d, 0x00001e5f, 0x00001e5f,
+ 0x00001e61, 0x00001e61, 0x00001e63, 0x00001e63,
+ 0x00001e65, 0x00001e65, 0x00001e67, 0x00001e67,
+ 0x00001e69, 0x00001e69, 0x00001e6b, 0x00001e6b,
+ 0x00001e6d, 0x00001e6d, 0x00001e6f, 0x00001e6f,
+ 0x00001e71, 0x00001e71, 0x00001e73, 0x00001e73,
+ 0x00001e75, 0x00001e75, 0x00001e77, 0x00001e77,
+ 0x00001e79, 0x00001e79, 0x00001e7b, 0x00001e7b,
+ 0x00001e7d, 0x00001e7d, 0x00001e7f, 0x00001e7f,
+ 0x00001e81, 0x00001e81, 0x00001e83, 0x00001e83,
+ 0x00001e85, 0x00001e85, 0x00001e87, 0x00001e87,
+ 0x00001e89, 0x00001e89, 0x00001e8b, 0x00001e8b,
+ 0x00001e8d, 0x00001e8d, 0x00001e8f, 0x00001e8f,
+ 0x00001e91, 0x00001e91, 0x00001e93, 0x00001e93,
+ 0x00001e95, 0x00001e9b, 0x00001ea1, 0x00001ea1,
+ 0x00001ea3, 0x00001ea3, 0x00001ea5, 0x00001ea5,
+ 0x00001ea7, 0x00001ea7, 0x00001ea9, 0x00001ea9,
+ 0x00001eab, 0x00001eab, 0x00001ead, 0x00001ead,
+ 0x00001eaf, 0x00001eaf, 0x00001eb1, 0x00001eb1,
+ 0x00001eb3, 0x00001eb3, 0x00001eb5, 0x00001eb5,
+ 0x00001eb7, 0x00001eb7, 0x00001eb9, 0x00001eb9,
+ 0x00001ebb, 0x00001ebb, 0x00001ebd, 0x00001ebd,
+ 0x00001ebf, 0x00001ebf, 0x00001ec1, 0x00001ec1,
+ 0x00001ec3, 0x00001ec3, 0x00001ec5, 0x00001ec5,
+ 0x00001ec7, 0x00001ec7, 0x00001ec9, 0x00001ec9,
+ 0x00001ecb, 0x00001ecb, 0x00001ecd, 0x00001ecd,
+ 0x00001ecf, 0x00001ecf, 0x00001ed1, 0x00001ed1,
+ 0x00001ed3, 0x00001ed3, 0x00001ed5, 0x00001ed5,
+ 0x00001ed7, 0x00001ed7, 0x00001ed9, 0x00001ed9,
+ 0x00001edb, 0x00001edb, 0x00001edd, 0x00001edd,
+ 0x00001edf, 0x00001edf, 0x00001ee1, 0x00001ee1,
+ 0x00001ee3, 0x00001ee3, 0x00001ee5, 0x00001ee5,
+ 0x00001ee7, 0x00001ee7, 0x00001ee9, 0x00001ee9,
+ 0x00001eeb, 0x00001eeb, 0x00001eed, 0x00001eed,
+ 0x00001eef, 0x00001eef, 0x00001ef1, 0x00001ef1,
+ 0x00001ef3, 0x00001ef3, 0x00001ef5, 0x00001ef5,
+ 0x00001ef7, 0x00001ef7, 0x00001ef9, 0x00001ef9,
+ 0x00001f00, 0x00001f07, 0x00001f10, 0x00001f15,
+ 0x00001f20, 0x00001f27, 0x00001f30, 0x00001f37,
+ 0x00001f40, 0x00001f45, 0x00001f50, 0x00001f57,
+ 0x00001f60, 0x00001f67, 0x00001f70, 0x00001f7d,
+ 0x00001f80, 0x00001f87, 0x00001f90, 0x00001f97,
+ 0x00001fa0, 0x00001fa7, 0x00001fb0, 0x00001fb4,
+ 0x00001fb6, 0x00001fb7, 0x00001fbe, 0x00001fbe,
+ 0x00001fc2, 0x00001fc4, 0x00001fc6, 0x00001fc7,
+ 0x00001fd0, 0x00001fd3, 0x00001fd6, 0x00001fd7,
+ 0x00001fe0, 0x00001fe7, 0x00001ff2, 0x00001ff4,
+ 0x00001ff6, 0x00001ff7, 0x00002071, 0x00002071,
+ 0x0000207f, 0x0000207f, 0x0000210a, 0x0000210a,
+ 0x0000210e, 0x0000210f, 0x00002113, 0x00002113,
+ 0x0000212f, 0x0000212f, 0x00002134, 0x00002134,
+ 0x00002139, 0x00002139, 0x0000213d, 0x0000213d,
+ 0x00002146, 0x00002149, 0x0000fb00, 0x0000fb06,
+ 0x0000fb13, 0x0000fb17, 0x0000ff41, 0x0000ff5a,
+ 0x00010428, 0x0001044d, 0x0001d41a, 0x0001d433,
+ 0x0001d44e, 0x0001d454, 0x0001d456, 0x0001d467,
+ 0x0001d482, 0x0001d49b, 0x0001d4b6, 0x0001d4b9,
+ 0x0001d4bb, 0x0001d4bb, 0x0001d4bd, 0x0001d4c0,
+ 0x0001d4c2, 0x0001d4c3, 0x0001d4c5, 0x0001d4cf,
+ 0x0001d4ea, 0x0001d503, 0x0001d51e, 0x0001d537,
+ 0x0001d552, 0x0001d56b, 0x0001d586, 0x0001d59f,
+ 0x0001d5ba, 0x0001d5d3, 0x0001d5ee, 0x0001d607,
+ 0x0001d622, 0x0001d63b, 0x0001d656, 0x0001d66f,
+ 0x0001d68a, 0x0001d6a3, 0x0001d6c2, 0x0001d6da,
+ 0x0001d6dc, 0x0001d6e1, 0x0001d6fc, 0x0001d714,
+ 0x0001d716, 0x0001d71b, 0x0001d736, 0x0001d74e,
+ 0x0001d750, 0x0001d755, 0x0001d770, 0x0001d788,
+ 0x0001d78a, 0x0001d78f, 0x0001d7aa, 0x0001d7c2,
+ 0x0001d7c4, 0x0001d7c9, 0x000001c5, 0x000001c5,
+ 0x000001c8, 0x000001c8, 0x000001cb, 0x000001cb,
+ 0x000001f2, 0x000001f2, 0x00001f88, 0x00001f8f,
+ 0x00001f98, 0x00001f9f, 0x00001fa8, 0x00001faf,
+ 0x00001fbc, 0x00001fbc, 0x00001fcc, 0x00001fcc,
+ 0x00001ffc, 0x00001ffc, 0x000002b0, 0x000002b8,
+ 0x000002bb, 0x000002c1, 0x000002d0, 0x000002d1,
+ 0x000002e0, 0x000002e4, 0x000002ee, 0x000002ee,
+ 0x0000037a, 0x0000037a, 0x00000559, 0x00000559,
+ 0x00000640, 0x00000640, 0x000006e5, 0x000006e6,
+ 0x00000e46, 0x00000e46, 0x00000ec6, 0x00000ec6,
+ 0x000017d7, 0x000017d7, 0x00001843, 0x00001843,
+ 0x00003005, 0x00003005, 0x00003031, 0x00003035,
+ 0x0000303b, 0x0000303b, 0x0000309d, 0x0000309e,
+ 0x000030fc, 0x000030fe, 0x0000ff70, 0x0000ff70,
+ 0x0000ff9e, 0x0000ff9f, 0x000001bb, 0x000001bb,
+ 0x000001c0, 0x000001c3, 0x000005d0, 0x000005ea,
+ 0x000005f0, 0x000005f2, 0x00000621, 0x0000063a,
+ 0x00000641, 0x0000064a, 0x0000066e, 0x0000066f,
+ 0x00000671, 0x000006d3, 0x000006d5, 0x000006d5,
+ 0x000006fa, 0x000006fc, 0x00000710, 0x00000710,
+ 0x00000712, 0x0000072c, 0x00000780, 0x000007a5,
+ 0x000007b1, 0x000007b1, 0x00000905, 0x00000939,
+ 0x0000093d, 0x0000093d, 0x00000950, 0x00000950,
+ 0x00000958, 0x00000961, 0x00000985, 0x0000098c,
+ 0x0000098f, 0x00000990, 0x00000993, 0x000009a8,
+ 0x000009aa, 0x000009b0, 0x000009b2, 0x000009b2,
+ 0x000009b6, 0x000009b9, 0x000009dc, 0x000009dd,
+ 0x000009df, 0x000009e1, 0x000009f0, 0x000009f1,
+ 0x00000a05, 0x00000a0a, 0x00000a0f, 0x00000a10,
+ 0x00000a13, 0x00000a28, 0x00000a2a, 0x00000a30,
+ 0x00000a32, 0x00000a33, 0x00000a35, 0x00000a36,
+ 0x00000a38, 0x00000a39, 0x00000a59, 0x00000a5c,
+ 0x00000a5e, 0x00000a5e, 0x00000a72, 0x00000a74,
+ 0x00000a85, 0x00000a8b, 0x00000a8d, 0x00000a8d,
+ 0x00000a8f, 0x00000a91, 0x00000a93, 0x00000aa8,
+ 0x00000aaa, 0x00000ab0, 0x00000ab2, 0x00000ab3,
+ 0x00000ab5, 0x00000ab9, 0x00000abd, 0x00000abd,
+ 0x00000ad0, 0x00000ad0, 0x00000ae0, 0x00000ae0,
+ 0x00000b05, 0x00000b0c, 0x00000b0f, 0x00000b10,
+ 0x00000b13, 0x00000b28, 0x00000b2a, 0x00000b30,
+ 0x00000b32, 0x00000b33, 0x00000b36, 0x00000b39,
+ 0x00000b3d, 0x00000b3d, 0x00000b5c, 0x00000b5d,
+ 0x00000b5f, 0x00000b61, 0x00000b83, 0x00000b83,
+ 0x00000b85, 0x00000b8a, 0x00000b8e, 0x00000b90,
+ 0x00000b92, 0x00000b95, 0x00000b99, 0x00000b9a,
+ 0x00000b9c, 0x00000b9c, 0x00000b9e, 0x00000b9f,
+ 0x00000ba3, 0x00000ba4, 0x00000ba8, 0x00000baa,
+ 0x00000bae, 0x00000bb5, 0x00000bb7, 0x00000bb9,
+ 0x00000c05, 0x00000c0c, 0x00000c0e, 0x00000c10,
+ 0x00000c12, 0x00000c28, 0x00000c2a, 0x00000c33,
+ 0x00000c35, 0x00000c39, 0x00000c60, 0x00000c61,
+ 0x00000c85, 0x00000c8c, 0x00000c8e, 0x00000c90,
+ 0x00000c92, 0x00000ca8, 0x00000caa, 0x00000cb3,
+ 0x00000cb5, 0x00000cb9, 0x00000cde, 0x00000cde,
+ 0x00000ce0, 0x00000ce1, 0x00000d05, 0x00000d0c,
+ 0x00000d0e, 0x00000d10, 0x00000d12, 0x00000d28,
+ 0x00000d2a, 0x00000d39, 0x00000d60, 0x00000d61,
+ 0x00000d85, 0x00000d96, 0x00000d9a, 0x00000db1,
+ 0x00000db3, 0x00000dbb, 0x00000dbd, 0x00000dbd,
+ 0x00000dc0, 0x00000dc6, 0x00000e01, 0x00000e30,
+ 0x00000e32, 0x00000e33, 0x00000e40, 0x00000e45,
+ 0x00000e81, 0x00000e82, 0x00000e84, 0x00000e84,
+ 0x00000e87, 0x00000e88, 0x00000e8a, 0x00000e8a,
+ 0x00000e8d, 0x00000e8d, 0x00000e94, 0x00000e97,
+ 0x00000e99, 0x00000e9f, 0x00000ea1, 0x00000ea3,
+ 0x00000ea5, 0x00000ea5, 0x00000ea7, 0x00000ea7,
+ 0x00000eaa, 0x00000eab, 0x00000ead, 0x00000eb0,
+ 0x00000eb2, 0x00000eb3, 0x00000ebd, 0x00000ebd,
+ 0x00000ec0, 0x00000ec4, 0x00000edc, 0x00000edd,
+ 0x00000f00, 0x00000f00, 0x00000f40, 0x00000f47,
+ 0x00000f49, 0x00000f6a, 0x00000f88, 0x00000f8b,
+ 0x00001000, 0x00001021, 0x00001023, 0x00001027,
+ 0x00001029, 0x0000102a, 0x00001050, 0x00001055,
+ 0x000010d0, 0x000010f8, 0x00001100, 0x00001159,
+ 0x0000115f, 0x000011a2, 0x000011a8, 0x000011f9,
+ 0x00001200, 0x00001206, 0x00001208, 0x00001246,
+ 0x00001248, 0x00001248, 0x0000124a, 0x0000124d,
+ 0x00001250, 0x00001256, 0x00001258, 0x00001258,
+ 0x0000125a, 0x0000125d, 0x00001260, 0x00001286,
+ 0x00001288, 0x00001288, 0x0000128a, 0x0000128d,
+ 0x00001290, 0x000012ae, 0x000012b0, 0x000012b0,
+ 0x000012b2, 0x000012b5, 0x000012b8, 0x000012be,
+ 0x000012c0, 0x000012c0, 0x000012c2, 0x000012c5,
+ 0x000012c8, 0x000012ce, 0x000012d0, 0x000012d6,
+ 0x000012d8, 0x000012ee, 0x000012f0, 0x0000130e,
+ 0x00001310, 0x00001310, 0x00001312, 0x00001315,
+ 0x00001318, 0x0000131e, 0x00001320, 0x00001346,
+ 0x00001348, 0x0000135a, 0x000013a0, 0x000013f4,
+ 0x00001401, 0x0000166c, 0x0000166f, 0x00001676,
+ 0x00001681, 0x0000169a, 0x000016a0, 0x000016ea,
+ 0x00001700, 0x0000170c, 0x0000170e, 0x00001711,
+ 0x00001720, 0x00001731, 0x00001740, 0x00001751,
+ 0x00001760, 0x0000176c, 0x0000176e, 0x00001770,
+ 0x00001780, 0x000017b3, 0x000017dc, 0x000017dc,
+ 0x00001820, 0x00001842, 0x00001844, 0x00001877,
+ 0x00001880, 0x000018a8, 0x00002135, 0x00002138,
+ 0x00003006, 0x00003006, 0x0000303c, 0x0000303c,
+ 0x00003041, 0x00003096, 0x0000309f, 0x0000309f,
+ 0x000030a1, 0x000030fa, 0x000030ff, 0x000030ff,
+ 0x00003105, 0x0000312c, 0x00003131, 0x0000318e,
+ 0x000031a0, 0x000031b7, 0x000031f0, 0x000031ff,
+ 0x00003400, 0x00004db5, 0x00004e00, 0x0000a48c,
+ 0x0000ac00, 0x0000d7a3, 0x0000f900, 0x0000faff,
+ 0x0000fb1d, 0x0000fb1d, 0x0000fb1f, 0x0000fb28,
+ 0x0000fb2a, 0x0000fb36, 0x0000fb38, 0x0000fb3c,
+ 0x0000fb3e, 0x0000fb3e, 0x0000fb40, 0x0000fb41,
+ 0x0000fb43, 0x0000fb44, 0x0000fb46, 0x0000fbb1,
+ 0x0000fbd3, 0x0000fd3d, 0x0000fd50, 0x0000fd8f,
+ 0x0000fd92, 0x0000fdc7, 0x0000fdf0, 0x0000fdfb,
+ 0x0000fe70, 0x0000fe74, 0x0000fe76, 0x0000fefc,
+ 0x0000ff66, 0x0000ff6f, 0x0000ff71, 0x0000ff9d,
+ 0x0000ffa0, 0x0000ffbe, 0x0000ffc2, 0x0000ffc7,
+ 0x0000ffca, 0x0000ffcf, 0x0000ffd2, 0x0000ffd7,
+ 0x0000ffda, 0x0000ffdc, 0x00010300, 0x0001031e,
+ 0x00010330, 0x00010349, 0x00020000, 0x0002a6d6,
+ 0x0002f800, 0x0002fa1d, 0x0000005f, 0x0000005f,
+ 0x0000203f, 0x00002040, 0x000030fb, 0x000030fb,
+ 0x0000fe33, 0x0000fe34, 0x0000fe4d, 0x0000fe4f,
+ 0x0000ff3f, 0x0000ff3f, 0x0000ff65, 0x0000ff65,
+ 0x0000002d, 0x0000002d, 0x000000ad, 0x000000ad,
+ 0x0000058a, 0x0000058a, 0x00001806, 0x00001806,
+ 0x00002010, 0x00002015, 0x0000301c, 0x0000301c,
+ 0x00003030, 0x00003030, 0x000030a0, 0x000030a0,
+ 0x0000fe31, 0x0000fe32, 0x0000fe58, 0x0000fe58,
+ 0x0000fe63, 0x0000fe63, 0x0000ff0d, 0x0000ff0d,
+ 0x00000028, 0x00000028, 0x0000005b, 0x0000005b,
+ 0x0000007b, 0x0000007b, 0x00000f3a, 0x00000f3a,
+ 0x00000f3c, 0x00000f3c, 0x0000169b, 0x0000169b,
+ 0x0000201a, 0x0000201a, 0x0000201e, 0x0000201e,
+ 0x00002045, 0x00002045, 0x0000207d, 0x0000207d,
+ 0x0000208d, 0x0000208d, 0x00002329, 0x00002329,
+ 0x000023b4, 0x000023b4, 0x00002768, 0x00002768,
+ 0x0000276a, 0x0000276a, 0x0000276c, 0x0000276c,
+ 0x0000276e, 0x0000276e, 0x00002770, 0x00002770,
+ 0x00002772, 0x00002772, 0x00002774, 0x00002774,
+ 0x000027e6, 0x000027e6, 0x000027e8, 0x000027e8,
+ 0x000027ea, 0x000027ea, 0x00002983, 0x00002983,
+ 0x00002985, 0x00002985, 0x00002987, 0x00002987,
+ 0x00002989, 0x00002989, 0x0000298b, 0x0000298b,
+ 0x0000298d, 0x0000298d, 0x0000298f, 0x0000298f,
+ 0x00002991, 0x00002991, 0x00002993, 0x00002993,
+ 0x00002995, 0x00002995, 0x00002997, 0x00002997,
+ 0x000029d8, 0x000029d8, 0x000029da, 0x000029da,
+ 0x000029fc, 0x000029fc, 0x00003008, 0x00003008,
+ 0x0000300a, 0x0000300a, 0x0000300c, 0x0000300c,
+ 0x0000300e, 0x0000300e, 0x00003010, 0x00003010,
+ 0x00003014, 0x00003014, 0x00003016, 0x00003016,
+ 0x00003018, 0x00003018, 0x0000301a, 0x0000301a,
+ 0x0000301d, 0x0000301d, 0x0000fd3e, 0x0000fd3e,
+ 0x0000fe35, 0x0000fe35, 0x0000fe37, 0x0000fe37,
+ 0x0000fe39, 0x0000fe39, 0x0000fe3b, 0x0000fe3b,
+ 0x0000fe3d, 0x0000fe3d, 0x0000fe3f, 0x0000fe3f,
+ 0x0000fe41, 0x0000fe41, 0x0000fe43, 0x0000fe43,
+ 0x0000fe59, 0x0000fe59, 0x0000fe5b, 0x0000fe5b,
+ 0x0000fe5d, 0x0000fe5d, 0x0000ff08, 0x0000ff08,
+ 0x0000ff3b, 0x0000ff3b, 0x0000ff5b, 0x0000ff5b,
+ 0x0000ff5f, 0x0000ff5f, 0x0000ff62, 0x0000ff62,
+ 0x00000029, 0x00000029, 0x0000005d, 0x0000005d,
+ 0x0000007d, 0x0000007d, 0x00000f3b, 0x00000f3b,
+ 0x00000f3d, 0x00000f3d, 0x0000169c, 0x0000169c,
+ 0x00002046, 0x00002046, 0x0000207e, 0x0000207e,
+ 0x0000208e, 0x0000208e, 0x0000232a, 0x0000232a,
+ 0x000023b5, 0x000023b5, 0x00002769, 0x00002769,
+ 0x0000276b, 0x0000276b, 0x0000276d, 0x0000276d,
+ 0x0000276f, 0x0000276f, 0x00002771, 0x00002771,
+ 0x00002773, 0x00002773, 0x00002775, 0x00002775,
+ 0x000027e7, 0x000027e7, 0x000027e9, 0x000027e9,
+ 0x000027eb, 0x000027eb, 0x00002984, 0x00002984,
+ 0x00002986, 0x00002986, 0x00002988, 0x00002988,
+ 0x0000298a, 0x0000298a, 0x0000298c, 0x0000298c,
+ 0x0000298e, 0x0000298e, 0x00002990, 0x00002990,
+ 0x00002992, 0x00002992, 0x00002994, 0x00002994,
+ 0x00002996, 0x00002996, 0x00002998, 0x00002998,
+ 0x000029d9, 0x000029d9, 0x000029db, 0x000029db,
+ 0x000029fd, 0x000029fd, 0x00003009, 0x00003009,
+ 0x0000300b, 0x0000300b, 0x0000300d, 0x0000300d,
+ 0x0000300f, 0x0000300f, 0x00003011, 0x00003011,
+ 0x00003015, 0x00003015, 0x00003017, 0x00003017,
+ 0x00003019, 0x00003019, 0x0000301b, 0x0000301b,
+ 0x0000301e, 0x0000301f, 0x0000fd3f, 0x0000fd3f,
+ 0x0000fe36, 0x0000fe36, 0x0000fe38, 0x0000fe38,
+ 0x0000fe3a, 0x0000fe3a, 0x0000fe3c, 0x0000fe3c,
+ 0x0000fe3e, 0x0000fe3e, 0x0000fe40, 0x0000fe40,
+ 0x0000fe42, 0x0000fe42, 0x0000fe44, 0x0000fe44,
+ 0x0000fe5a, 0x0000fe5a, 0x0000fe5c, 0x0000fe5c,
+ 0x0000fe5e, 0x0000fe5e, 0x0000ff09, 0x0000ff09,
+ 0x0000ff3d, 0x0000ff3d, 0x0000ff5d, 0x0000ff5d,
+ 0x0000ff60, 0x0000ff60, 0x0000ff63, 0x0000ff63,
+ 0x00000021, 0x00000023, 0x00000025, 0x00000027,
+ 0x0000002a, 0x0000002a, 0x0000002c, 0x0000002c,
+ 0x0000002e, 0x0000002f, 0x0000003a, 0x0000003b,
+ 0x0000003f, 0x00000040, 0x0000005c, 0x0000005c,
+ 0x000000a1, 0x000000a1, 0x000000b7, 0x000000b7,
+ 0x000000bf, 0x000000bf, 0x0000037e, 0x0000037e,
+ 0x00000387, 0x00000387, 0x0000055a, 0x0000055f,
+ 0x00000589, 0x00000589, 0x000005be, 0x000005be,
+ 0x000005c0, 0x000005c0, 0x000005c3, 0x000005c3,
+ 0x000005f3, 0x000005f4, 0x0000060c, 0x0000060c,
+ 0x0000061b, 0x0000061b, 0x0000061f, 0x0000061f,
+ 0x0000066a, 0x0000066d, 0x000006d4, 0x000006d4,
+ 0x00000700, 0x0000070d, 0x00000964, 0x00000965,
+ 0x00000970, 0x00000970, 0x00000df4, 0x00000df4,
+ 0x00000e4f, 0x00000e4f, 0x00000e5a, 0x00000e5b,
+ 0x00000f04, 0x00000f12, 0x00000f85, 0x00000f85,
+ 0x0000104a, 0x0000104f, 0x000010fb, 0x000010fb,
+ 0x00001361, 0x00001368, 0x0000166d, 0x0000166e,
+ 0x000016eb, 0x000016ed, 0x00001735, 0x00001736,
+ 0x000017d4, 0x000017d6, 0x000017d8, 0x000017da,
+ 0x00001800, 0x00001805, 0x00001807, 0x0000180a,
+ 0x00002016, 0x00002017, 0x00002020, 0x00002027,
+ 0x00002030, 0x00002038, 0x0000203b, 0x0000203e,
+ 0x00002041, 0x00002043, 0x00002047, 0x00002051,
+ 0x00002057, 0x00002057, 0x000023b6, 0x000023b6,
+ 0x00003001, 0x00003003, 0x0000303d, 0x0000303d,
+ 0x0000fe30, 0x0000fe30, 0x0000fe45, 0x0000fe46,
+ 0x0000fe49, 0x0000fe4c, 0x0000fe50, 0x0000fe52,
+ 0x0000fe54, 0x0000fe57, 0x0000fe5f, 0x0000fe61,
+ 0x0000fe68, 0x0000fe68, 0x0000fe6a, 0x0000fe6b,
+ 0x0000ff01, 0x0000ff03, 0x0000ff05, 0x0000ff07,
+ 0x0000ff0a, 0x0000ff0a, 0x0000ff0c, 0x0000ff0c,
+ 0x0000ff0e, 0x0000ff0f, 0x0000ff1a, 0x0000ff1b,
+ 0x0000ff1f, 0x0000ff20, 0x0000ff3c, 0x0000ff3c,
+ 0x0000ff61, 0x0000ff61, 0x0000ff64, 0x0000ff64,
+ 0x0000002b, 0x0000002b, 0x0000003c, 0x0000003e,
+ 0x0000007c, 0x0000007c, 0x0000007e, 0x0000007e,
+ 0x000000ac, 0x000000ac, 0x000000b1, 0x000000b1,
+ 0x000000d7, 0x000000d7, 0x000000f7, 0x000000f7,
+ 0x000003f6, 0x000003f6, 0x00002044, 0x00002044,
+ 0x00002052, 0x00002052, 0x0000207a, 0x0000207c,
+ 0x0000208a, 0x0000208c, 0x00002140, 0x00002144,
+ 0x0000214b, 0x0000214b, 0x00002190, 0x00002194,
+ 0x0000219a, 0x0000219b, 0x000021a0, 0x000021a0,
+ 0x000021a3, 0x000021a3, 0x000021a6, 0x000021a6,
+ 0x000021ae, 0x000021ae, 0x000021ce, 0x000021cf,
+ 0x000021d2, 0x000021d2, 0x000021d4, 0x000021d4,
+ 0x000021f4, 0x000022ff, 0x00002308, 0x0000230b,
+ 0x00002320, 0x00002321, 0x0000237c, 0x0000237c,
+ 0x0000239b, 0x000023b3, 0x000025b7, 0x000025b7,
+ 0x000025c1, 0x000025c1, 0x000025f8, 0x000025ff,
+ 0x0000266f, 0x0000266f, 0x000027d0, 0x000027e5,
+ 0x000027f0, 0x000027ff, 0x00002900, 0x00002982,
+ 0x00002999, 0x000029d7, 0x000029dc, 0x000029fb,
+ 0x000029fe, 0x00002aff, 0x0000fb29, 0x0000fb29,
+ 0x0000fe62, 0x0000fe62, 0x0000fe64, 0x0000fe66,
+ 0x0000ff0b, 0x0000ff0b, 0x0000ff1c, 0x0000ff1e,
+ 0x0000ff5c, 0x0000ff5c, 0x0000ff5e, 0x0000ff5e,
+ 0x0000ffe2, 0x0000ffe2, 0x0000ffe9, 0x0000ffec,
+ 0x0001d6c1, 0x0001d6c1, 0x0001d6db, 0x0001d6db,
+ 0x0001d6fb, 0x0001d6fb, 0x0001d715, 0x0001d715,
+ 0x0001d735, 0x0001d735, 0x0001d74f, 0x0001d74f,
+ 0x0001d76f, 0x0001d76f, 0x0001d789, 0x0001d789,
+ 0x0001d7a9, 0x0001d7a9, 0x0001d7c3, 0x0001d7c3,
+ 0x00000024, 0x00000024, 0x000000a2, 0x000000a5,
+ 0x000009f2, 0x000009f3, 0x00000e3f, 0x00000e3f,
+ 0x000017db, 0x000017db, 0x000020a0, 0x000020b1,
+ 0x0000fdfc, 0x0000fdfc, 0x0000fe69, 0x0000fe69,
+ 0x0000ff04, 0x0000ff04, 0x0000ffe0, 0x0000ffe1,
+ 0x0000ffe5, 0x0000ffe6, 0x0000005e, 0x0000005e,
+ 0x00000060, 0x00000060, 0x000000a8, 0x000000a8,
+ 0x000000af, 0x000000af, 0x000000b4, 0x000000b4,
+ 0x000000b8, 0x000000b8, 0x000002b9, 0x000002ba,
+ 0x000002c2, 0x000002cf, 0x000002d2, 0x000002df,
+ 0x000002e5, 0x000002ed, 0x00000374, 0x00000375,
+ 0x00000384, 0x00000385, 0x00001fbd, 0x00001fbd,
+ 0x00001fbf, 0x00001fc1, 0x00001fcd, 0x00001fcf,
+ 0x00001fdd, 0x00001fdf, 0x00001fed, 0x00001fef,
+ 0x00001ffd, 0x00001ffe, 0x0000309b, 0x0000309c,
+ 0x0000ff3e, 0x0000ff3e, 0x0000ff40, 0x0000ff40,
+ 0x0000ffe3, 0x0000ffe3, 0x000000a6, 0x000000a7,
+ 0x000000a9, 0x000000a9, 0x000000ae, 0x000000ae,
+ 0x000000b0, 0x000000b0, 0x000000b6, 0x000000b6,
+ 0x00000482, 0x00000482, 0x000006e9, 0x000006e9,
+ 0x000006fd, 0x000006fe, 0x000009fa, 0x000009fa,
+ 0x00000b70, 0x00000b70, 0x00000f01, 0x00000f03,
+ 0x00000f13, 0x00000f17, 0x00000f1a, 0x00000f1f,
+ 0x00000f34, 0x00000f34, 0x00000f36, 0x00000f36,
+ 0x00000f38, 0x00000f38, 0x00000fbe, 0x00000fc5,
+ 0x00000fc7, 0x00000fcc, 0x00000fcf, 0x00000fcf,
+ 0x00002100, 0x00002101, 0x00002103, 0x00002106,
+ 0x00002108, 0x00002109, 0x00002114, 0x00002114,
+ 0x00002116, 0x00002118, 0x0000211e, 0x00002123,
+ 0x00002125, 0x00002125, 0x00002127, 0x00002127,
+ 0x00002129, 0x00002129, 0x0000212e, 0x0000212e,
+ 0x00002132, 0x00002132, 0x0000213a, 0x0000213a,
+ 0x0000214a, 0x0000214a, 0x00002195, 0x00002199,
+ 0x0000219c, 0x0000219f, 0x000021a1, 0x000021a2,
+ 0x000021a4, 0x000021a5, 0x000021a7, 0x000021ad,
+ 0x000021af, 0x000021cd, 0x000021d0, 0x000021d1,
+ 0x000021d3, 0x000021d3, 0x000021d5, 0x000021f3,
+ 0x00002300, 0x00002307, 0x0000230c, 0x0000231f,
+ 0x00002322, 0x00002328, 0x0000232b, 0x0000237b,
+ 0x0000237d, 0x0000239a, 0x000023b7, 0x000023ce,
+ 0x00002400, 0x00002426, 0x00002440, 0x0000244a,
+ 0x0000249c, 0x000024e9, 0x00002500, 0x000025b6,
+ 0x000025b8, 0x000025c0, 0x000025c2, 0x000025f7,
+ 0x00002600, 0x00002613, 0x00002616, 0x00002617,
+ 0x00002619, 0x0000266e, 0x00002670, 0x0000267d,
+ 0x00002680, 0x00002689, 0x00002701, 0x00002704,
+ 0x00002706, 0x00002709, 0x0000270c, 0x00002727,
+ 0x00002729, 0x0000274b, 0x0000274d, 0x0000274d,
+ 0x0000274f, 0x00002752, 0x00002756, 0x00002756,
+ 0x00002758, 0x0000275e, 0x00002761, 0x00002767,
+ 0x00002794, 0x00002794, 0x00002798, 0x000027af,
+ 0x000027b1, 0x000027be, 0x00002800, 0x000028ff,
+ 0x00002e80, 0x00002e99, 0x00002e9b, 0x00002ef3,
+ 0x00002f00, 0x00002fd5, 0x00002ff0, 0x00002ffb,
+ 0x00003004, 0x00003004, 0x00003012, 0x00003013,
+ 0x00003020, 0x00003020, 0x00003036, 0x00003037,
+ 0x0000303e, 0x0000303f, 0x00003190, 0x00003191,
+ 0x00003196, 0x0000319f, 0x00003200, 0x0000321c,
+ 0x0000322a, 0x00003243, 0x00003260, 0x0000327b,
+ 0x0000327f, 0x0000327f, 0x0000328a, 0x000032b0,
+ 0x000032c0, 0x000032cb, 0x000032d0, 0x000032fe,
+ 0x00003300, 0x00003376, 0x0000337b, 0x000033dd,
+ 0x000033e0, 0x000033fe, 0x0000a490, 0x0000a4c6,
+ 0x0000ffe4, 0x0000ffe4, 0x0000ffe8, 0x0000ffe8,
+ 0x0000ffed, 0x0000ffee, 0x0000fffc, 0x0000fffd,
+ 0x0001d000, 0x0001d0f5, 0x0001d100, 0x0001d126,
+ 0x0001d12a, 0x0001d164, 0x0001d16a, 0x0001d16c,
+ 0x0001d183, 0x0001d184, 0x0001d18c, 0x0001d1a9,
+ 0x0001d1ae, 0x0001d1dd, 0x00000041, 0x0000005a,
+ 0x00000061, 0x0000007a, 0x000000aa, 0x000000aa,
+ 0x000000b5, 0x000000b5, 0x000000ba, 0x000000ba,
+ 0x000000c0, 0x000000d6, 0x000000d8, 0x000000f6,
+ 0x000000f8, 0x00000220, 0x00000222, 0x00000233,
+ 0x00000250, 0x000002ad, 0x000002b0, 0x000002b8,
+ 0x000002bb, 0x000002c1, 0x000002d0, 0x000002d1,
+ 0x000002e0, 0x000002e4, 0x000002ee, 0x000002ee,
+ 0x0000037a, 0x0000037a, 0x00000386, 0x00000386,
+ 0x00000388, 0x0000038a, 0x0000038c, 0x0000038c,
+ 0x0000038e, 0x000003a1, 0x000003a3, 0x000003ce,
+ 0x000003d0, 0x000003f5, 0x00000400, 0x00000482,
+ 0x0000048a, 0x000004ce, 0x000004d0, 0x000004f5,
+ 0x000004f8, 0x000004f9, 0x00000500, 0x0000050f,
+ 0x00000531, 0x00000556, 0x00000559, 0x0000055f,
+ 0x00000561, 0x00000587, 0x00000589, 0x00000589,
+ 0x00000903, 0x00000903, 0x00000905, 0x00000939,
+ 0x0000093d, 0x00000940, 0x00000949, 0x0000094c,
+ 0x00000950, 0x00000950, 0x00000958, 0x00000961,
+ 0x00000964, 0x00000970, 0x00000982, 0x00000983,
+ 0x00000985, 0x0000098c, 0x0000098f, 0x00000990,
+ 0x00000993, 0x000009a8, 0x000009aa, 0x000009b0,
+ 0x000009b2, 0x000009b2, 0x000009b6, 0x000009b9,
+ 0x000009be, 0x000009c0, 0x000009c7, 0x000009c8,
+ 0x000009cb, 0x000009cc, 0x000009d7, 0x000009d7,
+ 0x000009dc, 0x000009dd, 0x000009df, 0x000009e1,
+ 0x000009e6, 0x000009f1, 0x000009f4, 0x000009fa,
+ 0x00000a05, 0x00000a0a, 0x00000a0f, 0x00000a10,
+ 0x00000a13, 0x00000a28, 0x00000a2a, 0x00000a30,
+ 0x00000a32, 0x00000a33, 0x00000a35, 0x00000a36,
+ 0x00000a38, 0x00000a39, 0x00000a3e, 0x00000a40,
+ 0x00000a59, 0x00000a5c, 0x00000a5e, 0x00000a5e,
+ 0x00000a66, 0x00000a6f, 0x00000a72, 0x00000a74,
+ 0x00000a83, 0x00000a83, 0x00000a85, 0x00000a8b,
+ 0x00000a8d, 0x00000a8d, 0x00000a8f, 0x00000a91,
+ 0x00000a93, 0x00000aa8, 0x00000aaa, 0x00000ab0,
+ 0x00000ab2, 0x00000ab3, 0x00000ab5, 0x00000ab9,
+ 0x00000abd, 0x00000ac0, 0x00000ac9, 0x00000ac9,
+ 0x00000acb, 0x00000acc, 0x00000ad0, 0x00000ad0,
+ 0x00000ae0, 0x00000ae0, 0x00000ae6, 0x00000aef,
+ 0x00000b02, 0x00000b03, 0x00000b05, 0x00000b0c,
+ 0x00000b0f, 0x00000b10, 0x00000b13, 0x00000b28,
+ 0x00000b2a, 0x00000b30, 0x00000b32, 0x00000b33,
+ 0x00000b36, 0x00000b39, 0x00000b3d, 0x00000b3e,
+ 0x00000b40, 0x00000b40, 0x00000b47, 0x00000b48,
+ 0x00000b4b, 0x00000b4c, 0x00000b57, 0x00000b57,
+ 0x00000b5c, 0x00000b5d, 0x00000b5f, 0x00000b61,
+ 0x00000b66, 0x00000b70, 0x00000b83, 0x00000b83,
+ 0x00000b85, 0x00000b8a, 0x00000b8e, 0x00000b90,
+ 0x00000b92, 0x00000b95, 0x00000b99, 0x00000b9a,
+ 0x00000b9c, 0x00000b9c, 0x00000b9e, 0x00000b9f,
+ 0x00000ba3, 0x00000ba4, 0x00000ba8, 0x00000baa,
+ 0x00000bae, 0x00000bb5, 0x00000bb7, 0x00000bb9,
+ 0x00000bbe, 0x00000bbf, 0x00000bc1, 0x00000bc2,
+ 0x00000bc6, 0x00000bc8, 0x00000bca, 0x00000bcc,
+ 0x00000bd7, 0x00000bd7, 0x00000be7, 0x00000bf2,
+ 0x00000c01, 0x00000c03, 0x00000c05, 0x00000c0c,
+ 0x00000c0e, 0x00000c10, 0x00000c12, 0x00000c28,
+ 0x00000c2a, 0x00000c33, 0x00000c35, 0x00000c39,
+ 0x00000c41, 0x00000c44, 0x00000c60, 0x00000c61,
+ 0x00000c66, 0x00000c6f, 0x00000c82, 0x00000c83,
+ 0x00000c85, 0x00000c8c, 0x00000c8e, 0x00000c90,
+ 0x00000c92, 0x00000ca8, 0x00000caa, 0x00000cb3,
+ 0x00000cb5, 0x00000cb9, 0x00000cbe, 0x00000cbe,
+ 0x00000cc0, 0x00000cc4, 0x00000cc7, 0x00000cc8,
+ 0x00000cca, 0x00000ccb, 0x00000cd5, 0x00000cd6,
+ 0x00000cde, 0x00000cde, 0x00000ce0, 0x00000ce1,
+ 0x00000ce6, 0x00000cef, 0x00000d02, 0x00000d03,
+ 0x00000d05, 0x00000d0c, 0x00000d0e, 0x00000d10,
+ 0x00000d12, 0x00000d28, 0x00000d2a, 0x00000d39,
+ 0x00000d3e, 0x00000d40, 0x00000d46, 0x00000d48,
+ 0x00000d4a, 0x00000d4c, 0x00000d57, 0x00000d57,
+ 0x00000d60, 0x00000d61, 0x00000d66, 0x00000d6f,
+ 0x00000d82, 0x00000d83, 0x00000d85, 0x00000d96,
+ 0x00000d9a, 0x00000db1, 0x00000db3, 0x00000dbb,
+ 0x00000dbd, 0x00000dbd, 0x00000dc0, 0x00000dc6,
+ 0x00000dcf, 0x00000dd1, 0x00000dd8, 0x00000ddf,
+ 0x00000df2, 0x00000df4, 0x00000e01, 0x00000e30,
+ 0x00000e32, 0x00000e33, 0x00000e40, 0x00000e46,
+ 0x00000e4f, 0x00000e5b, 0x00000e81, 0x00000e82,
+ 0x00000e84, 0x00000e84, 0x00000e87, 0x00000e88,
+ 0x00000e8a, 0x00000e8a, 0x00000e8d, 0x00000e8d,
+ 0x00000e94, 0x00000e97, 0x00000e99, 0x00000e9f,
+ 0x00000ea1, 0x00000ea3, 0x00000ea5, 0x00000ea5,
+ 0x00000ea7, 0x00000ea7, 0x00000eaa, 0x00000eab,
+ 0x00000ead, 0x00000eb0, 0x00000eb2, 0x00000eb3,
+ 0x00000ebd, 0x00000ebd, 0x00000ec0, 0x00000ec4,
+ 0x00000ec6, 0x00000ec6, 0x00000ed0, 0x00000ed9,
+ 0x00000edc, 0x00000edd, 0x00000f00, 0x00000f17,
+ 0x00000f1a, 0x00000f34, 0x00000f36, 0x00000f36,
+ 0x00000f38, 0x00000f38, 0x00000f3e, 0x00000f47,
+ 0x00000f49, 0x00000f6a, 0x00000f7f, 0x00000f7f,
+ 0x00000f85, 0x00000f85, 0x00000f88, 0x00000f8b,
+ 0x00000fbe, 0x00000fc5, 0x00000fc7, 0x00000fcc,
+ 0x00000fcf, 0x00000fcf, 0x00001000, 0x00001021,
+ 0x00001023, 0x00001027, 0x00001029, 0x0000102a,
+ 0x0000102c, 0x0000102c, 0x00001031, 0x00001031,
+ 0x00001038, 0x00001038, 0x00001040, 0x00001057,
+ 0x000010a0, 0x000010c5, 0x000010d0, 0x000010f8,
+ 0x000010fb, 0x000010fb, 0x00001100, 0x00001159,
+ 0x0000115f, 0x000011a2, 0x000011a8, 0x000011f9,
+ 0x00001200, 0x00001206, 0x00001208, 0x00001246,
+ 0x00001248, 0x00001248, 0x0000124a, 0x0000124d,
+ 0x00001250, 0x00001256, 0x00001258, 0x00001258,
+ 0x0000125a, 0x0000125d, 0x00001260, 0x00001286,
+ 0x00001288, 0x00001288, 0x0000128a, 0x0000128d,
+ 0x00001290, 0x000012ae, 0x000012b0, 0x000012b0,
+ 0x000012b2, 0x000012b5, 0x000012b8, 0x000012be,
+ 0x000012c0, 0x000012c0, 0x000012c2, 0x000012c5,
+ 0x000012c8, 0x000012ce, 0x000012d0, 0x000012d6,
+ 0x000012d8, 0x000012ee, 0x000012f0, 0x0000130e,
+ 0x00001310, 0x00001310, 0x00001312, 0x00001315,
+ 0x00001318, 0x0000131e, 0x00001320, 0x00001346,
+ 0x00001348, 0x0000135a, 0x00001361, 0x0000137c,
+ 0x000013a0, 0x000013f4, 0x00001401, 0x00001676,
+ 0x00001681, 0x0000169a, 0x000016a0, 0x000016f0,
+ 0x00001700, 0x0000170c, 0x0000170e, 0x00001711,
+ 0x00001720, 0x00001731, 0x00001735, 0x00001736,
+ 0x00001740, 0x00001751, 0x00001760, 0x0000176c,
+ 0x0000176e, 0x00001770, 0x00001780, 0x000017b6,
+ 0x000017be, 0x000017c5, 0x000017c7, 0x000017c8,
+ 0x000017d4, 0x000017da, 0x000017dc, 0x000017dc,
+ 0x000017e0, 0x000017e9, 0x00001810, 0x00001819,
+ 0x00001820, 0x00001877, 0x00001880, 0x000018a8,
+ 0x00001e00, 0x00001e9b, 0x00001ea0, 0x00001ef9,
+ 0x00001f00, 0x00001f15, 0x00001f18, 0x00001f1d,
+ 0x00001f20, 0x00001f45, 0x00001f48, 0x00001f4d,
+ 0x00001f50, 0x00001f57, 0x00001f59, 0x00001f59,
+ 0x00001f5b, 0x00001f5b, 0x00001f5d, 0x00001f5d,
+ 0x00001f5f, 0x00001f7d, 0x00001f80, 0x00001fb4,
+ 0x00001fb6, 0x00001fbc, 0x00001fbe, 0x00001fbe,
+ 0x00001fc2, 0x00001fc4, 0x00001fc6, 0x00001fcc,
+ 0x00001fd0, 0x00001fd3, 0x00001fd6, 0x00001fdb,
+ 0x00001fe0, 0x00001fec, 0x00001ff2, 0x00001ff4,
+ 0x00001ff6, 0x00001ffc, 0x0000200e, 0x0000200e,
+ 0x00002071, 0x00002071, 0x0000207f, 0x0000207f,
+ 0x00002102, 0x00002102, 0x00002107, 0x00002107,
+ 0x0000210a, 0x00002113, 0x00002115, 0x00002115,
+ 0x00002119, 0x0000211d, 0x00002124, 0x00002124,
+ 0x00002126, 0x00002126, 0x00002128, 0x00002128,
+ 0x0000212a, 0x0000212d, 0x0000212f, 0x00002131,
+ 0x00002133, 0x00002139, 0x0000213d, 0x0000213f,
+ 0x00002145, 0x00002149, 0x00002160, 0x00002183,
+ 0x00002336, 0x0000237a, 0x00002395, 0x00002395,
+ 0x0000249c, 0x000024e9, 0x00003005, 0x00003007,
+ 0x00003021, 0x00003029, 0x00003031, 0x00003035,
+ 0x00003038, 0x0000303c, 0x00003041, 0x00003096,
+ 0x0000309d, 0x0000309f, 0x000030a1, 0x000030fa,
+ 0x000030fc, 0x000030ff, 0x00003105, 0x0000312c,
+ 0x00003131, 0x0000318e, 0x00003190, 0x000031b7,
+ 0x000031f0, 0x0000321c, 0x00003220, 0x00003243,
+ 0x00003260, 0x0000327b, 0x0000327f, 0x000032b0,
+ 0x000032c0, 0x000032cb, 0x000032d0, 0x000032fe,
+ 0x00003300, 0x00003376, 0x0000337b, 0x000033dd,
+ 0x000033e0, 0x000033fe, 0x00003400, 0x00004db5,
+ 0x00004e00, 0x0000a48c, 0x0000ac00, 0x0000d7a3,
+ 0x0000e000, 0x0000fb06, 0x0000fb13, 0x0000fb17,
+ 0x0000ff21, 0x0000ff3a, 0x0000ff41, 0x0000ff5a,
+ 0x0000ff66, 0x0000ffbe, 0x0000ffc2, 0x0000ffc7,
+ 0x0000ffca, 0x0000ffcf, 0x0000ffd2, 0x0000ffd7,
+ 0x0000ffda, 0x0000ffdc, 0x00010000, 0x0002a6d6,
+ 0x0002f800, 0x0002fa1d, 0x000f0000, 0x000ffffd,
+ 0x00100000, 0x0010fffd, 0x000005be, 0x000005be,
+ 0x000005c0, 0x000005c0, 0x000005c3, 0x000005c3,
+ 0x000005d0, 0x000005ea, 0x000005f0, 0x000005f4,
+ 0x0000200f, 0x0000200f, 0x0000fb1d, 0x0000fb1d,
+ 0x0000fb1f, 0x0000fb28, 0x0000fb2a, 0x0000fb36,
+ 0x0000fb38, 0x0000fb3c, 0x0000fb3e, 0x0000fb3e,
+ 0x0000fb40, 0x0000fb41, 0x0000fb43, 0x0000fb44,
+ 0x0000fb46, 0x0000fb4f, 0x00000030, 0x00000039,
+ 0x000000b2, 0x000000b3, 0x000000b9, 0x000000b9,
+ 0x000006f0, 0x000006f9, 0x00002070, 0x00002070,
+ 0x00002074, 0x00002079, 0x00002080, 0x00002089,
+ 0x00002460, 0x0000249b, 0x000024ea, 0x000024ea,
+ 0x0000ff10, 0x0000ff19, 0x0001d7ce, 0x0001d7ff,
+ 0x0000002f, 0x0000002f, 0x0000ff0f, 0x0000ff0f,
+ 0x00000023, 0x00000025, 0x0000002b, 0x0000002b,
+ 0x0000002d, 0x0000002d, 0x000000a2, 0x000000a5,
+ 0x000000b0, 0x000000b1, 0x0000066a, 0x0000066a,
+ 0x000009f2, 0x000009f3, 0x00000e3f, 0x00000e3f,
+ 0x000017db, 0x000017db, 0x00002030, 0x00002034,
+ 0x0000207a, 0x0000207b, 0x0000208a, 0x0000208b,
+ 0x000020a0, 0x000020b1, 0x0000212e, 0x0000212e,
+ 0x00002212, 0x00002213, 0x0000fb29, 0x0000fb29,
+ 0x0000fe5f, 0x0000fe5f, 0x0000fe62, 0x0000fe63,
+ 0x0000fe69, 0x0000fe6a, 0x0000ff03, 0x0000ff05,
+ 0x0000ff0b, 0x0000ff0b, 0x0000ff0d, 0x0000ff0d,
+ 0x0000ffe0, 0x0000ffe1, 0x0000ffe5, 0x0000ffe6,
+ 0x00000660, 0x00000669, 0x0000066b, 0x0000066c,
+ 0x0000002c, 0x0000002c, 0x0000002e, 0x0000002e,
+ 0x0000003a, 0x0000003a, 0x000000a0, 0x000000a0,
+ 0x0000060c, 0x0000060c, 0x0000fe50, 0x0000fe50,
+ 0x0000fe52, 0x0000fe52, 0x0000fe55, 0x0000fe55,
+ 0x0000ff0c, 0x0000ff0c, 0x0000ff0e, 0x0000ff0e,
+ 0x0000ff1a, 0x0000ff1a, 0x0000000a, 0x0000000a,
+ 0x0000000d, 0x0000000d, 0x0000001c, 0x0000001e,
+ 0x00000085, 0x00000085, 0x00002029, 0x00002029,
+ 0x00000009, 0x00000009, 0x0000000b, 0x0000000b,
+ 0x0000001f, 0x0000001f, 0x0000000c, 0x0000000c,
+ 0x00000020, 0x00000020, 0x00001680, 0x00001680,
+ 0x00002000, 0x0000200a, 0x00002028, 0x00002028,
+ 0x0000202f, 0x0000202f, 0x0000205f, 0x0000205f,
+ 0x00003000, 0x00003000, 0x00000000, 0x00000008,
+ 0x0000000e, 0x0000001b, 0x00000021, 0x00000022,
+ 0x00000026, 0x0000002a, 0x0000003b, 0x00000040,
+ 0x0000005b, 0x00000060, 0x0000007b, 0x00000084,
+ 0x00000086, 0x0000009f, 0x000000a1, 0x000000a1,
+ 0x000000a6, 0x000000a9, 0x000000ab, 0x000000af,
+ 0x000000b4, 0x000000b4, 0x000000b6, 0x000000b8,
+ 0x000000bb, 0x000000bf, 0x000000d7, 0x000000d7,
+ 0x000000f7, 0x000000f7, 0x000002b9, 0x000002ba,
+ 0x000002c2, 0x000002cf, 0x000002d2, 0x000002df,
+ 0x000002e5, 0x000002ed, 0x00000300, 0x0000034f,
+ 0x00000360, 0x0000036f, 0x00000374, 0x00000375,
+ 0x0000037e, 0x0000037e, 0x00000384, 0x00000385,
+ 0x00000387, 0x00000387, 0x000003f6, 0x000003f6,
+ 0x00000483, 0x00000486, 0x00000488, 0x00000489,
+ 0x0000058a, 0x0000058a, 0x00000591, 0x000005a1,
+ 0x000005a3, 0x000005b9, 0x000005bb, 0x000005bd,
+ 0x000005bf, 0x000005bf, 0x000005c1, 0x000005c2,
+ 0x000005c4, 0x000005c4, 0x0000064b, 0x00000655,
+ 0x00000670, 0x00000670, 0x000006d6, 0x000006dc,
+ 0x000006de, 0x000006e4, 0x000006e7, 0x000006ed,
+ 0x0000070f, 0x0000070f, 0x00000711, 0x00000711,
+ 0x00000730, 0x0000074a, 0x000007a6, 0x000007b0,
+ 0x00000901, 0x00000902, 0x0000093c, 0x0000093c,
+ 0x00000941, 0x00000948, 0x0000094d, 0x0000094d,
+ 0x00000951, 0x00000954, 0x00000962, 0x00000963,
+ 0x00000981, 0x00000981, 0x000009bc, 0x000009bc,
+ 0x000009c1, 0x000009c4, 0x000009cd, 0x000009cd,
+ 0x000009e2, 0x000009e3, 0x00000a02, 0x00000a02,
+ 0x00000a3c, 0x00000a3c, 0x00000a41, 0x00000a42,
+ 0x00000a47, 0x00000a48, 0x00000a4b, 0x00000a4d,
+ 0x00000a70, 0x00000a71, 0x00000a81, 0x00000a82,
+ 0x00000abc, 0x00000abc, 0x00000ac1, 0x00000ac5,
+ 0x00000ac7, 0x00000ac8, 0x00000acd, 0x00000acd,
+ 0x00000b01, 0x00000b01, 0x00000b3c, 0x00000b3c,
+ 0x00000b3f, 0x00000b3f, 0x00000b41, 0x00000b43,
+ 0x00000b4d, 0x00000b4d, 0x00000b56, 0x00000b56,
+ 0x00000b82, 0x00000b82, 0x00000bc0, 0x00000bc0,
+ 0x00000bcd, 0x00000bcd, 0x00000c3e, 0x00000c40,
+ 0x00000c46, 0x00000c48, 0x00000c4a, 0x00000c4d,
+ 0x00000c55, 0x00000c56, 0x00000cbf, 0x00000cbf,
+ 0x00000cc6, 0x00000cc6, 0x00000ccc, 0x00000ccd,
+ 0x00000d41, 0x00000d43, 0x00000d4d, 0x00000d4d,
+ 0x00000dca, 0x00000dca, 0x00000dd2, 0x00000dd4,
+ 0x00000dd6, 0x00000dd6, 0x00000e31, 0x00000e31,
+ 0x00000e34, 0x00000e3a, 0x00000e47, 0x00000e4e,
+ 0x00000eb1, 0x00000eb1, 0x00000eb4, 0x00000eb9,
+ 0x00000ebb, 0x00000ebc, 0x00000ec8, 0x00000ecd,
+ 0x00000f18, 0x00000f19, 0x00000f35, 0x00000f35,
+ 0x00000f37, 0x00000f37, 0x00000f39, 0x00000f3d,
+ 0x00000f71, 0x00000f7e, 0x00000f80, 0x00000f84,
+ 0x00000f86, 0x00000f87, 0x00000f90, 0x00000f97,
+ 0x00000f99, 0x00000fbc, 0x00000fc6, 0x00000fc6,
+ 0x0000102d, 0x00001030, 0x00001032, 0x00001032,
+ 0x00001036, 0x00001037, 0x00001039, 0x00001039,
+ 0x00001058, 0x00001059, 0x0000169b, 0x0000169c,
+ 0x00001712, 0x00001714, 0x00001732, 0x00001734,
+ 0x00001752, 0x00001753, 0x00001772, 0x00001773,
+ 0x000017b7, 0x000017bd, 0x000017c6, 0x000017c6,
+ 0x000017c9, 0x000017d3, 0x00001800, 0x0000180e,
+ 0x000018a9, 0x000018a9, 0x00001fbd, 0x00001fbd,
+ 0x00001fbf, 0x00001fc1, 0x00001fcd, 0x00001fcf,
+ 0x00001fdd, 0x00001fdf, 0x00001fed, 0x00001fef,
+ 0x00001ffd, 0x00001ffe, 0x0000200b, 0x0000200d,
+ 0x00002010, 0x00002027, 0x0000202a, 0x0000202e,
+ 0x00002035, 0x00002052, 0x00002057, 0x00002057,
+ 0x00002060, 0x00002063, 0x0000206a, 0x0000206f,
+ 0x0000207c, 0x0000207e, 0x0000208c, 0x0000208e,
+ 0x000020d0, 0x000020ea, 0x00002100, 0x00002101,
+ 0x00002103, 0x00002106, 0x00002108, 0x00002109,
+ 0x00002114, 0x00002114, 0x00002116, 0x00002118,
+ 0x0000211e, 0x00002123, 0x00002125, 0x00002125,
+ 0x00002127, 0x00002127, 0x00002129, 0x00002129,
+ 0x00002132, 0x00002132, 0x0000213a, 0x0000213a,
+ 0x00002140, 0x00002144, 0x0000214a, 0x0000214b,
+ 0x00002153, 0x0000215f, 0x00002190, 0x00002211,
+ 0x00002214, 0x00002335, 0x0000237b, 0x00002394,
+ 0x00002396, 0x000023ce, 0x00002400, 0x00002426,
+ 0x00002440, 0x0000244a, 0x000024eb, 0x000024fe,
+ 0x00002500, 0x00002613, 0x00002616, 0x00002617,
+ 0x00002619, 0x0000267d, 0x00002680, 0x00002689,
+ 0x00002701, 0x00002704, 0x00002706, 0x00002709,
+ 0x0000270c, 0x00002727, 0x00002729, 0x0000274b,
+ 0x0000274d, 0x0000274d, 0x0000274f, 0x00002752,
+ 0x00002756, 0x00002756, 0x00002758, 0x0000275e,
+ 0x00002761, 0x00002794, 0x00002798, 0x000027af,
+ 0x000027b1, 0x000027be, 0x000027d0, 0x000027eb,
+ 0x000027f0, 0x00002aff, 0x00002e80, 0x00002e99,
+ 0x00002e9b, 0x00002ef3, 0x00002f00, 0x00002fd5,
+ 0x00002ff0, 0x00002ffb, 0x00003001, 0x00003004,
+ 0x00003008, 0x00003020, 0x0000302a, 0x00003030,
+ 0x00003036, 0x00003037, 0x0000303d, 0x0000303f,
+ 0x00003099, 0x0000309c, 0x000030a0, 0x000030a0,
+ 0x000030fb, 0x000030fb, 0x00003251, 0x0000325f,
+ 0x000032b1, 0x000032bf, 0x0000a490, 0x0000a4c6,
+ 0x0000fb1e, 0x0000fb1e, 0x0000fd3e, 0x0000fd3f,
+ 0x0000fe00, 0x0000fe0f, 0x0000fe20, 0x0000fe23,
+ 0x0000fe30, 0x0000fe46, 0x0000fe49, 0x0000fe4f,
+ 0x0000fe51, 0x0000fe51, 0x0000fe54, 0x0000fe54,
+ 0x0000fe56, 0x0000fe5e, 0x0000fe60, 0x0000fe61,
+ 0x0000fe64, 0x0000fe66, 0x0000fe68, 0x0000fe68,
+ 0x0000fe6b, 0x0000fe6b, 0x0000feff, 0x0000feff,
+ 0x0000ff01, 0x0000ff02, 0x0000ff06, 0x0000ff0a,
+ 0x0000ff1b, 0x0000ff20, 0x0000ff3b, 0x0000ff40,
+ 0x0000ff5b, 0x0000ff65, 0x0000ffe2, 0x0000ffe4,
+ 0x0000ffe8, 0x0000ffee, 0x0000fff9, 0x0000fffd,
+ 0x0001d167, 0x0001d169, 0x0001d173, 0x0001d182,
+ 0x0001d185, 0x0001d18b, 0x0001d1aa, 0x0001d1ad,
+ 0x000e0001, 0x000e0001, 0x000e0020, 0x000e007f,
+ 0x000000c0, 0x000000c5, 0x000000c7, 0x000000cf,
+ 0x000000d1, 0x000000d6, 0x000000d9, 0x000000dd,
+ 0x000000e0, 0x000000e5, 0x000000e7, 0x000000ef,
+ 0x000000f1, 0x000000f6, 0x000000f9, 0x000000fd,
+ 0x000000ff, 0x0000010f, 0x00000112, 0x00000125,
+ 0x00000128, 0x00000130, 0x00000134, 0x00000137,
+ 0x00000139, 0x0000013e, 0x00000143, 0x00000148,
+ 0x0000014c, 0x00000151, 0x00000154, 0x00000165,
+ 0x00000168, 0x0000017e, 0x000001a0, 0x000001a1,
+ 0x000001af, 0x000001b0, 0x000001cd, 0x000001dc,
+ 0x000001de, 0x000001e3, 0x000001e6, 0x000001f0,
+ 0x000001f4, 0x000001f5, 0x000001f8, 0x0000021b,
+ 0x0000021e, 0x0000021f, 0x00000226, 0x00000233,
+ 0x00000340, 0x00000341, 0x00000343, 0x00000344,
+ 0x00000374, 0x00000374, 0x0000037e, 0x0000037e,
+ 0x00000385, 0x0000038a, 0x0000038c, 0x0000038c,
+ 0x0000038e, 0x00000390, 0x000003aa, 0x000003b0,
+ 0x000003ca, 0x000003ce, 0x000003d3, 0x000003d4,
+ 0x00000400, 0x00000401, 0x00000403, 0x00000403,
+ 0x00000407, 0x00000407, 0x0000040c, 0x0000040e,
+ 0x00000419, 0x00000419, 0x00000439, 0x00000439,
+ 0x00000450, 0x00000451, 0x00000453, 0x00000453,
+ 0x00000457, 0x00000457, 0x0000045c, 0x0000045e,
+ 0x00000476, 0x00000477, 0x000004c1, 0x000004c2,
+ 0x000004d0, 0x000004d3, 0x000004d6, 0x000004d7,
+ 0x000004da, 0x000004df, 0x000004e2, 0x000004e7,
+ 0x000004ea, 0x000004f5, 0x000004f8, 0x000004f9,
+ 0x00000622, 0x00000626, 0x000006c0, 0x000006c0,
+ 0x000006c2, 0x000006c2, 0x000006d3, 0x000006d3,
+ 0x00000929, 0x00000929, 0x00000931, 0x00000931,
+ 0x00000934, 0x00000934, 0x00000958, 0x0000095f,
+ 0x000009cb, 0x000009cc, 0x000009dc, 0x000009dd,
+ 0x000009df, 0x000009df, 0x00000a33, 0x00000a33,
+ 0x00000a36, 0x00000a36, 0x00000a59, 0x00000a5b,
+ 0x00000a5e, 0x00000a5e, 0x00000b48, 0x00000b48,
+ 0x00000b4b, 0x00000b4c, 0x00000b5c, 0x00000b5d,
+ 0x00000b94, 0x00000b94, 0x00000bca, 0x00000bcc,
+ 0x00000c48, 0x00000c48, 0x00000cc0, 0x00000cc0,
+ 0x00000cc7, 0x00000cc8, 0x00000cca, 0x00000ccb,
+ 0x00000d4a, 0x00000d4c, 0x00000dda, 0x00000dda,
+ 0x00000ddc, 0x00000dde, 0x00000f43, 0x00000f43,
+ 0x00000f4d, 0x00000f4d, 0x00000f52, 0x00000f52,
+ 0x00000f57, 0x00000f57, 0x00000f5c, 0x00000f5c,
+ 0x00000f69, 0x00000f69, 0x00000f73, 0x00000f73,
+ 0x00000f75, 0x00000f76, 0x00000f78, 0x00000f78,
+ 0x00000f81, 0x00000f81, 0x00000f93, 0x00000f93,
+ 0x00000f9d, 0x00000f9d, 0x00000fa2, 0x00000fa2,
+ 0x00000fa7, 0x00000fa7, 0x00000fac, 0x00000fac,
+ 0x00000fb9, 0x00000fb9, 0x00001026, 0x00001026,
+ 0x00001e00, 0x00001e99, 0x00001e9b, 0x00001e9b,
+ 0x00001ea0, 0x00001ef9, 0x00001f00, 0x00001f15,
+ 0x00001f18, 0x00001f1d, 0x00001f20, 0x00001f45,
+ 0x00001f48, 0x00001f4d, 0x00001f50, 0x00001f57,
+ 0x00001f59, 0x00001f59, 0x00001f5b, 0x00001f5b,
+ 0x00001f5d, 0x00001f5d, 0x00001f5f, 0x00001f7d,
+ 0x00001f80, 0x00001fb4, 0x00001fb6, 0x00001fbc,
+ 0x00001fbe, 0x00001fbe, 0x00001fc1, 0x00001fc4,
+ 0x00001fc6, 0x00001fd3, 0x00001fd6, 0x00001fdb,
+ 0x00001fdd, 0x00001fef, 0x00001ff2, 0x00001ff4,
+ 0x00001ff6, 0x00001ffd, 0x00002000, 0x00002001,
+ 0x00002126, 0x00002126, 0x0000212a, 0x0000212b,
+ 0x0000219a, 0x0000219b, 0x000021ae, 0x000021ae,
+ 0x000021cd, 0x000021cf, 0x00002204, 0x00002204,
+ 0x00002209, 0x00002209, 0x0000220c, 0x0000220c,
+ 0x00002224, 0x00002224, 0x00002226, 0x00002226,
+ 0x00002241, 0x00002241, 0x00002244, 0x00002244,
+ 0x00002247, 0x00002247, 0x00002249, 0x00002249,
+ 0x00002260, 0x00002260, 0x00002262, 0x00002262,
+ 0x0000226d, 0x00002271, 0x00002274, 0x00002275,
+ 0x00002278, 0x00002279, 0x00002280, 0x00002281,
+ 0x00002284, 0x00002285, 0x00002288, 0x00002289,
+ 0x000022ac, 0x000022af, 0x000022e0, 0x000022e3,
+ 0x000022ea, 0x000022ed, 0x00002329, 0x0000232a,
+ 0x00002adc, 0x00002adc, 0x0000304c, 0x0000304c,
+ 0x0000304e, 0x0000304e, 0x00003050, 0x00003050,
+ 0x00003052, 0x00003052, 0x00003054, 0x00003054,
+ 0x00003056, 0x00003056, 0x00003058, 0x00003058,
+ 0x0000305a, 0x0000305a, 0x0000305c, 0x0000305c,
+ 0x0000305e, 0x0000305e, 0x00003060, 0x00003060,
+ 0x00003062, 0x00003062, 0x00003065, 0x00003065,
+ 0x00003067, 0x00003067, 0x00003069, 0x00003069,
+ 0x00003070, 0x00003071, 0x00003073, 0x00003074,
+ 0x00003076, 0x00003077, 0x00003079, 0x0000307a,
+ 0x0000307c, 0x0000307d, 0x00003094, 0x00003094,
+ 0x0000309e, 0x0000309e, 0x000030ac, 0x000030ac,
+ 0x000030ae, 0x000030ae, 0x000030b0, 0x000030b0,
+ 0x000030b2, 0x000030b2, 0x000030b4, 0x000030b4,
+ 0x000030b6, 0x000030b6, 0x000030b8, 0x000030b8,
+ 0x000030ba, 0x000030ba, 0x000030bc, 0x000030bc,
+ 0x000030be, 0x000030be, 0x000030c0, 0x000030c0,
+ 0x000030c2, 0x000030c2, 0x000030c5, 0x000030c5,
+ 0x000030c7, 0x000030c7, 0x000030c9, 0x000030c9,
+ 0x000030d0, 0x000030d1, 0x000030d3, 0x000030d4,
+ 0x000030d6, 0x000030d7, 0x000030d9, 0x000030da,
+ 0x000030dc, 0x000030dd, 0x000030f4, 0x000030f4,
+ 0x000030f7, 0x000030fa, 0x000030fe, 0x000030fe,
+ 0x0000f902, 0x0000fa0d, 0x0000fa10, 0x0000fa10,
+ 0x0000fa12, 0x0000fa12, 0x0000fa15, 0x0000fa1e,
+ 0x0000fa20, 0x0000fa20, 0x0000fa22, 0x0000fa22,
+ 0x0000fa25, 0x0000fa26, 0x0000fa2a, 0x0000fa2d,
+ 0x0000fa30, 0x0000fa6a, 0x0000fb1d, 0x0000fb1d,
+ 0x0000fb1f, 0x0000fb1f, 0x0000fb2a, 0x0000fb36,
+ 0x0000fb38, 0x0000fb3c, 0x0000fb3e, 0x0000fb3e,
+ 0x0000fb40, 0x0000fb41, 0x0000fb43, 0x0000fb44,
+ 0x0000fb46, 0x0000fb4e, 0x0001d15e, 0x0001d164,
+ 0x0001d1bb, 0x0001d1c0, 0x0002f800, 0x0002fa1d,
+ 0x00000000, 0x00000220, 0x00000222, 0x00000233,
+ 0x00000250, 0x000002ad, 0x000002b0, 0x000002ee,
+ 0x00000300, 0x0000034f, 0x00000360, 0x0000036f,
+ 0x00000374, 0x00000375, 0x0000037a, 0x0000037a,
+ 0x0000037e, 0x0000037e, 0x00000384, 0x0000038a,
+ 0x0000038c, 0x0000038c, 0x0000038e, 0x000003a1,
+ 0x000003a3, 0x000003ce, 0x000003d0, 0x000003f6,
+ 0x00000400, 0x00000486, 0x00000488, 0x000004ce,
+ 0x000004d0, 0x000004f5, 0x000004f8, 0x000004f9,
+ 0x00000500, 0x0000050f, 0x00000531, 0x00000556,
+ 0x00000559, 0x0000055f, 0x00000561, 0x00000587,
+ 0x00000589, 0x0000058a, 0x00000591, 0x000005a1,
+ 0x000005a3, 0x000005b9, 0x000005bb, 0x000005c4,
+ 0x000005d0, 0x000005ea, 0x000005f0, 0x000005f4,
+ 0x0000060c, 0x0000060c, 0x0000061b, 0x0000061b,
+ 0x0000061f, 0x0000061f, 0x00000621, 0x0000063a,
+ 0x00000640, 0x00000655, 0x00000660, 0x000006ed,
+ 0x000006f0, 0x000006fe, 0x00000700, 0x0000070d,
+ 0x0000070f, 0x0000072c, 0x00000730, 0x0000074a,
+ 0x00000780, 0x000007b1, 0x00000901, 0x00000903,
+ 0x00000905, 0x00000939, 0x0000093c, 0x0000094d,
+ 0x00000950, 0x00000954, 0x00000958, 0x00000970,
+ 0x00000981, 0x00000983, 0x00000985, 0x0000098c,
+ 0x0000098f, 0x00000990, 0x00000993, 0x000009a8,
+ 0x000009aa, 0x000009b0, 0x000009b2, 0x000009b2,
+ 0x000009b6, 0x000009b9, 0x000009bc, 0x000009bc,
+ 0x000009be, 0x000009c4, 0x000009c7, 0x000009c8,
+ 0x000009cb, 0x000009cd, 0x000009d7, 0x000009d7,
+ 0x000009dc, 0x000009dd, 0x000009df, 0x000009e3,
+ 0x000009e6, 0x000009fa, 0x00000a02, 0x00000a02,
+ 0x00000a05, 0x00000a0a, 0x00000a0f, 0x00000a10,
+ 0x00000a13, 0x00000a28, 0x00000a2a, 0x00000a30,
+ 0x00000a32, 0x00000a33, 0x00000a35, 0x00000a36,
+ 0x00000a38, 0x00000a39, 0x00000a3c, 0x00000a3c,
+ 0x00000a3e, 0x00000a42, 0x00000a47, 0x00000a48,
+ 0x00000a4b, 0x00000a4d, 0x00000a59, 0x00000a5c,
+ 0x00000a5e, 0x00000a5e, 0x00000a66, 0x00000a74,
+ 0x00000a81, 0x00000a83, 0x00000a85, 0x00000a8b,
+ 0x00000a8d, 0x00000a8d, 0x00000a8f, 0x00000a91,
+ 0x00000a93, 0x00000aa8, 0x00000aaa, 0x00000ab0,
+ 0x00000ab2, 0x00000ab3, 0x00000ab5, 0x00000ab9,
+ 0x00000abc, 0x00000ac5, 0x00000ac7, 0x00000ac9,
+ 0x00000acb, 0x00000acd, 0x00000ad0, 0x00000ad0,
+ 0x00000ae0, 0x00000ae0, 0x00000ae6, 0x00000aef,
+ 0x00000b01, 0x00000b03, 0x00000b05, 0x00000b0c,
+ 0x00000b0f, 0x00000b10, 0x00000b13, 0x00000b28,
+ 0x00000b2a, 0x00000b30, 0x00000b32, 0x00000b33,
+ 0x00000b36, 0x00000b39, 0x00000b3c, 0x00000b43,
+ 0x00000b47, 0x00000b48, 0x00000b4b, 0x00000b4d,
+ 0x00000b56, 0x00000b57, 0x00000b5c, 0x00000b5d,
+ 0x00000b5f, 0x00000b61, 0x00000b66, 0x00000b70,
+ 0x00000b82, 0x00000b83, 0x00000b85, 0x00000b8a,
+ 0x00000b8e, 0x00000b90, 0x00000b92, 0x00000b95,
+ 0x00000b99, 0x00000b9a, 0x00000b9c, 0x00000b9c,
+ 0x00000b9e, 0x00000b9f, 0x00000ba3, 0x00000ba4,
+ 0x00000ba8, 0x00000baa, 0x00000bae, 0x00000bb5,
+ 0x00000bb7, 0x00000bb9, 0x00000bbe, 0x00000bc2,
+ 0x00000bc6, 0x00000bc8, 0x00000bca, 0x00000bcd,
+ 0x00000bd7, 0x00000bd7, 0x00000be7, 0x00000bf2,
+ 0x00000c01, 0x00000c03, 0x00000c05, 0x00000c0c,
+ 0x00000c0e, 0x00000c10, 0x00000c12, 0x00000c28,
+ 0x00000c2a, 0x00000c33, 0x00000c35, 0x00000c39,
+ 0x00000c3e, 0x00000c44, 0x00000c46, 0x00000c48,
+ 0x00000c4a, 0x00000c4d, 0x00000c55, 0x00000c56,
+ 0x00000c60, 0x00000c61, 0x00000c66, 0x00000c6f,
+ 0x00000c82, 0x00000c83, 0x00000c85, 0x00000c8c,
+ 0x00000c8e, 0x00000c90, 0x00000c92, 0x00000ca8,
+ 0x00000caa, 0x00000cb3, 0x00000cb5, 0x00000cb9,
+ 0x00000cbe, 0x00000cc4, 0x00000cc6, 0x00000cc8,
+ 0x00000cca, 0x00000ccd, 0x00000cd5, 0x00000cd6,
+ 0x00000cde, 0x00000cde, 0x00000ce0, 0x00000ce1,
+ 0x00000ce6, 0x00000cef, 0x00000d02, 0x00000d03,
+ 0x00000d05, 0x00000d0c, 0x00000d0e, 0x00000d10,
+ 0x00000d12, 0x00000d28, 0x00000d2a, 0x00000d39,
+ 0x00000d3e, 0x00000d43, 0x00000d46, 0x00000d48,
+ 0x00000d4a, 0x00000d4d, 0x00000d57, 0x00000d57,
+ 0x00000d60, 0x00000d61, 0x00000d66, 0x00000d6f,
+ 0x00000d82, 0x00000d83, 0x00000d85, 0x00000d96,
+ 0x00000d9a, 0x00000db1, 0x00000db3, 0x00000dbb,
+ 0x00000dbd, 0x00000dbd, 0x00000dc0, 0x00000dc6,
+ 0x00000dca, 0x00000dca, 0x00000dcf, 0x00000dd4,
+ 0x00000dd6, 0x00000dd6, 0x00000dd8, 0x00000ddf,
+ 0x00000df2, 0x00000df4, 0x00000e01, 0x00000e3a,
+ 0x00000e3f, 0x00000e5b, 0x00000e81, 0x00000e82,
+ 0x00000e84, 0x00000e84, 0x00000e87, 0x00000e88,
+ 0x00000e8a, 0x00000e8a, 0x00000e8d, 0x00000e8d,
+ 0x00000e94, 0x00000e97, 0x00000e99, 0x00000e9f,
+ 0x00000ea1, 0x00000ea3, 0x00000ea5, 0x00000ea5,
+ 0x00000ea7, 0x00000ea7, 0x00000eaa, 0x00000eab,
+ 0x00000ead, 0x00000eb9, 0x00000ebb, 0x00000ebd,
+ 0x00000ec0, 0x00000ec4, 0x00000ec6, 0x00000ec6,
+ 0x00000ec8, 0x00000ecd, 0x00000ed0, 0x00000ed9,
+ 0x00000edc, 0x00000edd, 0x00000f00, 0x00000f47,
+ 0x00000f49, 0x00000f6a, 0x00000f71, 0x00000f8b,
+ 0x00000f90, 0x00000f97, 0x00000f99, 0x00000fbc,
+ 0x00000fbe, 0x00000fcc, 0x00000fcf, 0x00000fcf,
+ 0x00001000, 0x00001021, 0x00001023, 0x00001027,
+ 0x00001029, 0x0000102a, 0x0000102c, 0x00001032,
+ 0x00001036, 0x00001039, 0x00001040, 0x00001059,
+ 0x000010a0, 0x000010c5, 0x000010d0, 0x000010f8,
+ 0x000010fb, 0x000010fb, 0x00001100, 0x00001159,
+ 0x0000115f, 0x000011a2, 0x000011a8, 0x000011f9,
+ 0x00001200, 0x00001206, 0x00001208, 0x00001246,
+ 0x00001248, 0x00001248, 0x0000124a, 0x0000124d,
+ 0x00001250, 0x00001256, 0x00001258, 0x00001258,
+ 0x0000125a, 0x0000125d, 0x00001260, 0x00001286,
+ 0x00001288, 0x00001288, 0x0000128a, 0x0000128d,
+ 0x00001290, 0x000012ae, 0x000012b0, 0x000012b0,
+ 0x000012b2, 0x000012b5, 0x000012b8, 0x000012be,
+ 0x000012c0, 0x000012c0, 0x000012c2, 0x000012c5,
+ 0x000012c8, 0x000012ce, 0x000012d0, 0x000012d6,
+ 0x000012d8, 0x000012ee, 0x000012f0, 0x0000130e,
+ 0x00001310, 0x00001310, 0x00001312, 0x00001315,
+ 0x00001318, 0x0000131e, 0x00001320, 0x00001346,
+ 0x00001348, 0x0000135a, 0x00001361, 0x0000137c,
+ 0x000013a0, 0x000013f4, 0x00001401, 0x00001676,
+ 0x00001680, 0x0000169c, 0x000016a0, 0x000016f0,
+ 0x00001700, 0x0000170c, 0x0000170e, 0x00001714,
+ 0x00001720, 0x00001736, 0x00001740, 0x00001753,
+ 0x00001760, 0x0000176c, 0x0000176e, 0x00001770,
+ 0x00001772, 0x00001773, 0x00001780, 0x000017dc,
+ 0x000017e0, 0x000017e9, 0x00001800, 0x0000180e,
+ 0x00001810, 0x00001819, 0x00001820, 0x00001877,
+ 0x00001880, 0x000018a9, 0x00001e00, 0x00001e9b,
+ 0x00001ea0, 0x00001ef9, 0x00001f00, 0x00001f15,
+ 0x00001f18, 0x00001f1d, 0x00001f20, 0x00001f45,
+ 0x00001f48, 0x00001f4d, 0x00001f50, 0x00001f57,
+ 0x00001f59, 0x00001f59, 0x00001f5b, 0x00001f5b,
+ 0x00001f5d, 0x00001f5d, 0x00001f5f, 0x00001f7d,
+ 0x00001f80, 0x00001fb4, 0x00001fb6, 0x00001fc4,
+ 0x00001fc6, 0x00001fd3, 0x00001fd6, 0x00001fdb,
+ 0x00001fdd, 0x00001fef, 0x00001ff2, 0x00001ff4,
+ 0x00001ff6, 0x00001ffe, 0x00002000, 0x00002052,
+ 0x00002057, 0x00002057, 0x0000205f, 0x00002063,
+ 0x0000206a, 0x00002071, 0x00002074, 0x0000208e,
+ 0x000020a0, 0x000020b1, 0x000020d0, 0x000020ea,
+ 0x00002100, 0x0000213a, 0x0000213d, 0x0000214b,
+ 0x00002153, 0x00002183, 0x00002190, 0x000023ce,
+ 0x00002400, 0x00002426, 0x00002440, 0x0000244a,
+ 0x00002460, 0x000024fe, 0x00002500, 0x00002613,
+ 0x00002616, 0x00002617, 0x00002619, 0x0000267d,
+ 0x00002680, 0x00002689, 0x00002701, 0x00002704,
+ 0x00002706, 0x00002709, 0x0000270c, 0x00002727,
+ 0x00002729, 0x0000274b, 0x0000274d, 0x0000274d,
+ 0x0000274f, 0x00002752, 0x00002756, 0x00002756,
+ 0x00002758, 0x0000275e, 0x00002761, 0x00002794,
+ 0x00002798, 0x000027af, 0x000027b1, 0x000027be,
+ 0x000027d0, 0x000027eb, 0x000027f0, 0x00002aff,
+ 0x00002e80, 0x00002e99, 0x00002e9b, 0x00002ef3,
+ 0x00002f00, 0x00002fd5, 0x00002ff0, 0x00002ffb,
+ 0x00003000, 0x0000303f, 0x00003041, 0x00003096,
+ 0x00003099, 0x000030ff, 0x00003105, 0x0000312c,
+ 0x00003131, 0x0000318e, 0x00003190, 0x000031b7,
+ 0x000031f0, 0x0000321c, 0x00003220, 0x00003243,
+ 0x00003251, 0x0000327b, 0x0000327f, 0x000032cb,
+ 0x000032d0, 0x000032fe, 0x00003300, 0x00003376,
+ 0x0000337b, 0x000033dd, 0x000033e0, 0x000033fe,
+ 0x00003400, 0x00004db5, 0x00004e00, 0x00009fa5,
+ 0x0000a000, 0x0000a48c, 0x0000a490, 0x0000a4c6,
+ 0x0000ac00, 0x0000d7a3, 0x0000f900, 0x0000fb06,
+ 0x0000fb13, 0x0000fb17, 0x0000fb1d, 0x0000fb36,
+ 0x0000fb38, 0x0000fb3c, 0x0000fb3e, 0x0000fb3e,
+ 0x0000fb40, 0x0000fb41, 0x0000fb43, 0x0000fb44,
+ 0x0000fb46, 0x0000fbb1, 0x0000fbd3, 0x0000fd3f,
+ 0x0000fd50, 0x0000fd8f, 0x0000fd92, 0x0000fdc7,
+ 0x0000fdf0, 0x0000fdfc, 0x0000fe00, 0x0000fe0f,
+ 0x0000fe20, 0x0000fe23, 0x0000fe30, 0x0000fe46,
+ 0x0000fe49, 0x0000fe52, 0x0000fe54, 0x0000fe66,
+ 0x0000fe68, 0x0000fe6b, 0x0000fe70, 0x0000fe74,
+ 0x0000fe76, 0x0000fefc, 0x0000feff, 0x0000feff,
+ 0x0000ff01, 0x0000ffbe, 0x0000ffc2, 0x0000ffc7,
+ 0x0000ffca, 0x0000ffcf, 0x0000ffd2, 0x0000ffd7,
+ 0x0000ffda, 0x0000ffdc, 0x0000ffe0, 0x0000ffe6,
+ 0x0000ffe8, 0x0000ffee, 0x0000fff9, 0x0000fffd,
+ 0x00010300, 0x0001031e, 0x00010320, 0x00010323,
+ 0x00010330, 0x0001034a, 0x00010400, 0x00010425,
+ 0x00010428, 0x0001044d, 0x0001d000, 0x0001d0f5,
+ 0x0001d100, 0x0001d126, 0x0001d12a, 0x0001d1dd,
+ 0x0001d400, 0x0001d454, 0x0001d456, 0x0001d49c,
+ 0x0001d49e, 0x0001d49f, 0x0001d4a2, 0x0001d4a2,
+ 0x0001d4a5, 0x0001d4a6, 0x0001d4a9, 0x0001d4ac,
+ 0x0001d4ae, 0x0001d4b9, 0x0001d4bb, 0x0001d4bb,
+ 0x0001d4bd, 0x0001d4c0, 0x0001d4c2, 0x0001d4c3,
+ 0x0001d4c5, 0x0001d505, 0x0001d507, 0x0001d50a,
+ 0x0001d50d, 0x0001d514, 0x0001d516, 0x0001d51c,
+ 0x0001d51e, 0x0001d539, 0x0001d53b, 0x0001d53e,
+ 0x0001d540, 0x0001d544, 0x0001d546, 0x0001d546,
+ 0x0001d54a, 0x0001d550, 0x0001d552, 0x0001d6a3,
+ 0x0001d6a8, 0x0001d7c9, 0x0001d7ce, 0x0001d7ff,
+ 0x00020000, 0x0002a6d6, 0x0002f800, 0x0002fa1d,
+ 0x000e0001, 0x000e0001, 0x000e0020, 0x000e007f,
+ 0x000000ab, 0x000000ab, 0x00002018, 0x00002018,
+ 0x0000201b, 0x0000201c, 0x0000201f, 0x0000201f,
+ 0x00002039, 0x00002039, 0x000000bb, 0x000000bb,
+ 0x00002019, 0x00002019, 0x0000201d, 0x0000201d,
+ 0x0000203a, 0x0000203a, 0x0000061b, 0x0000061b,
+ 0x0000061f, 0x0000061f, 0x00000621, 0x0000063a,
+ 0x00000640, 0x0000064a, 0x0000066d, 0x0000066f,
+ 0x00000671, 0x000006d5, 0x000006dd, 0x000006dd,
+ 0x000006e5, 0x000006e6, 0x000006fa, 0x000006fe,
+ 0x00000700, 0x0000070d, 0x00000710, 0x00000710,
+ 0x00000712, 0x0000072c, 0x00000780, 0x000007a5,
+ 0x000007b1, 0x000007b1, 0x0000fb50, 0x0000fbb1,
+ 0x0000fbd3, 0x0000fd3d, 0x0000fd50, 0x0000fd8f,
+ 0x0000fd92, 0x0000fdc7, 0x0000fdf0, 0x0000fdfc,
+ 0x0000fe70, 0x0000fe74, 0x0000fe76, 0x0000fefc
+};
+
+static const ac_uint4 _uccase_size = 1504;
+
+static const ac_uint2 _uccase_len[2] = {718, 755};
+
+static const ac_uint4 _uccase_map[] = {
+ 0x00000041, 0x00000061, 0x00000041,
+ 0x00000042, 0x00000062, 0x00000042,
+ 0x00000043, 0x00000063, 0x00000043,
+ 0x00000044, 0x00000064, 0x00000044,
+ 0x00000045, 0x00000065, 0x00000045,
+ 0x00000046, 0x00000066, 0x00000046,
+ 0x00000047, 0x00000067, 0x00000047,
+ 0x00000048, 0x00000068, 0x00000048,
+ 0x00000049, 0x00000069, 0x00000049,
+ 0x0000004a, 0x0000006a, 0x0000004a,
+ 0x0000004b, 0x0000006b, 0x0000004b,
+ 0x0000004c, 0x0000006c, 0x0000004c,
+ 0x0000004d, 0x0000006d, 0x0000004d,
+ 0x0000004e, 0x0000006e, 0x0000004e,
+ 0x0000004f, 0x0000006f, 0x0000004f,
+ 0x00000050, 0x00000070, 0x00000050,
+ 0x00000051, 0x00000071, 0x00000051,
+ 0x00000052, 0x00000072, 0x00000052,
+ 0x00000053, 0x00000073, 0x00000053,
+ 0x00000054, 0x00000074, 0x00000054,
+ 0x00000055, 0x00000075, 0x00000055,
+ 0x00000056, 0x00000076, 0x00000056,
+ 0x00000057, 0x00000077, 0x00000057,
+ 0x00000058, 0x00000078, 0x00000058,
+ 0x00000059, 0x00000079, 0x00000059,
+ 0x0000005a, 0x0000007a, 0x0000005a,
+ 0x000000c0, 0x000000e0, 0x000000c0,
+ 0x000000c1, 0x000000e1, 0x000000c1,
+ 0x000000c2, 0x000000e2, 0x000000c2,
+ 0x000000c3, 0x000000e3, 0x000000c3,
+ 0x000000c4, 0x000000e4, 0x000000c4,
+ 0x000000c5, 0x000000e5, 0x000000c5,
+ 0x000000c6, 0x000000e6, 0x000000c6,
+ 0x000000c7, 0x000000e7, 0x000000c7,
+ 0x000000c8, 0x000000e8, 0x000000c8,
+ 0x000000c9, 0x000000e9, 0x000000c9,
+ 0x000000ca, 0x000000ea, 0x000000ca,
+ 0x000000cb, 0x000000eb, 0x000000cb,
+ 0x000000cc, 0x000000ec, 0x000000cc,
+ 0x000000cd, 0x000000ed, 0x000000cd,
+ 0x000000ce, 0x000000ee, 0x000000ce,
+ 0x000000cf, 0x000000ef, 0x000000cf,
+ 0x000000d0, 0x000000f0, 0x000000d0,
+ 0x000000d1, 0x000000f1, 0x000000d1,
+ 0x000000d2, 0x000000f2, 0x000000d2,
+ 0x000000d3, 0x000000f3, 0x000000d3,
+ 0x000000d4, 0x000000f4, 0x000000d4,
+ 0x000000d5, 0x000000f5, 0x000000d5,
+ 0x000000d6, 0x000000f6, 0x000000d6,
+ 0x000000d8, 0x000000f8, 0x000000d8,
+ 0x000000d9, 0x000000f9, 0x000000d9,
+ 0x000000da, 0x000000fa, 0x000000da,
+ 0x000000db, 0x000000fb, 0x000000db,
+ 0x000000dc, 0x000000fc, 0x000000dc,
+ 0x000000dd, 0x000000fd, 0x000000dd,
+ 0x000000de, 0x000000fe, 0x000000de,
+ 0x00000100, 0x00000101, 0x00000100,
+ 0x00000102, 0x00000103, 0x00000102,
+ 0x00000104, 0x00000105, 0x00000104,
+ 0x00000106, 0x00000107, 0x00000106,
+ 0x00000108, 0x00000109, 0x00000108,
+ 0x0000010a, 0x0000010b, 0x0000010a,
+ 0x0000010c, 0x0000010d, 0x0000010c,
+ 0x0000010e, 0x0000010f, 0x0000010e,
+ 0x00000110, 0x00000111, 0x00000110,
+ 0x00000112, 0x00000113, 0x00000112,
+ 0x00000114, 0x00000115, 0x00000114,
+ 0x00000116, 0x00000117, 0x00000116,
+ 0x00000118, 0x00000119, 0x00000118,
+ 0x0000011a, 0x0000011b, 0x0000011a,
+ 0x0000011c, 0x0000011d, 0x0000011c,
+ 0x0000011e, 0x0000011f, 0x0000011e,
+ 0x00000120, 0x00000121, 0x00000120,
+ 0x00000122, 0x00000123, 0x00000122,
+ 0x00000124, 0x00000125, 0x00000124,
+ 0x00000126, 0x00000127, 0x00000126,
+ 0x00000128, 0x00000129, 0x00000128,
+ 0x0000012a, 0x0000012b, 0x0000012a,
+ 0x0000012c, 0x0000012d, 0x0000012c,
+ 0x0000012e, 0x0000012f, 0x0000012e,
+ 0x00000130, 0x00000069, 0x00000130,
+ 0x00000132, 0x00000133, 0x00000132,
+ 0x00000134, 0x00000135, 0x00000134,
+ 0x00000136, 0x00000137, 0x00000136,
+ 0x00000139, 0x0000013a, 0x00000139,
+ 0x0000013b, 0x0000013c, 0x0000013b,
+ 0x0000013d, 0x0000013e, 0x0000013d,
+ 0x0000013f, 0x00000140, 0x0000013f,
+ 0x00000141, 0x00000142, 0x00000141,
+ 0x00000143, 0x00000144, 0x00000143,
+ 0x00000145, 0x00000146, 0x00000145,
+ 0x00000147, 0x00000148, 0x00000147,
+ 0x0000014a, 0x0000014b, 0x0000014a,
+ 0x0000014c, 0x0000014d, 0x0000014c,
+ 0x0000014e, 0x0000014f, 0x0000014e,
+ 0x00000150, 0x00000151, 0x00000150,
+ 0x00000152, 0x00000153, 0x00000152,
+ 0x00000154, 0x00000155, 0x00000154,
+ 0x00000156, 0x00000157, 0x00000156,
+ 0x00000158, 0x00000159, 0x00000158,
+ 0x0000015a, 0x0000015b, 0x0000015a,
+ 0x0000015c, 0x0000015d, 0x0000015c,
+ 0x0000015e, 0x0000015f, 0x0000015e,
+ 0x00000160, 0x00000161, 0x00000160,
+ 0x00000162, 0x00000163, 0x00000162,
+ 0x00000164, 0x00000165, 0x00000164,
+ 0x00000166, 0x00000167, 0x00000166,
+ 0x00000168, 0x00000169, 0x00000168,
+ 0x0000016a, 0x0000016b, 0x0000016a,
+ 0x0000016c, 0x0000016d, 0x0000016c,
+ 0x0000016e, 0x0000016f, 0x0000016e,
+ 0x00000170, 0x00000171, 0x00000170,
+ 0x00000172, 0x00000173, 0x00000172,
+ 0x00000174, 0x00000175, 0x00000174,
+ 0x00000176, 0x00000177, 0x00000176,
+ 0x00000178, 0x000000ff, 0x00000178,
+ 0x00000179, 0x0000017a, 0x00000179,
+ 0x0000017b, 0x0000017c, 0x0000017b,
+ 0x0000017d, 0x0000017e, 0x0000017d,
+ 0x00000181, 0x00000253, 0x00000181,
+ 0x00000182, 0x00000183, 0x00000182,
+ 0x00000184, 0x00000185, 0x00000184,
+ 0x00000186, 0x00000254, 0x00000186,
+ 0x00000187, 0x00000188, 0x00000187,
+ 0x00000189, 0x00000256, 0x00000189,
+ 0x0000018a, 0x00000257, 0x0000018a,
+ 0x0000018b, 0x0000018c, 0x0000018b,
+ 0x0000018e, 0x000001dd, 0x0000018e,
+ 0x0000018f, 0x00000259, 0x0000018f,
+ 0x00000190, 0x0000025b, 0x00000190,
+ 0x00000191, 0x00000192, 0x00000191,
+ 0x00000193, 0x00000260, 0x00000193,
+ 0x00000194, 0x00000263, 0x00000194,
+ 0x00000196, 0x00000269, 0x00000196,
+ 0x00000197, 0x00000268, 0x00000197,
+ 0x00000198, 0x00000199, 0x00000198,
+ 0x0000019c, 0x0000026f, 0x0000019c,
+ 0x0000019d, 0x00000272, 0x0000019d,
+ 0x0000019f, 0x00000275, 0x0000019f,
+ 0x000001a0, 0x000001a1, 0x000001a0,
+ 0x000001a2, 0x000001a3, 0x000001a2,
+ 0x000001a4, 0x000001a5, 0x000001a4,
+ 0x000001a6, 0x00000280, 0x000001a6,
+ 0x000001a7, 0x000001a8, 0x000001a7,
+ 0x000001a9, 0x00000283, 0x000001a9,
+ 0x000001ac, 0x000001ad, 0x000001ac,
+ 0x000001ae, 0x00000288, 0x000001ae,
+ 0x000001af, 0x000001b0, 0x000001af,
+ 0x000001b1, 0x0000028a, 0x000001b1,
+ 0x000001b2, 0x0000028b, 0x000001b2,
+ 0x000001b3, 0x000001b4, 0x000001b3,
+ 0x000001b5, 0x000001b6, 0x000001b5,
+ 0x000001b7, 0x00000292, 0x000001b7,
+ 0x000001b8, 0x000001b9, 0x000001b8,
+ 0x000001bc, 0x000001bd, 0x000001bc,
+ 0x000001c4, 0x000001c6, 0x000001c5,
+ 0x000001c7, 0x000001c9, 0x000001c8,
+ 0x000001ca, 0x000001cc, 0x000001cb,
+ 0x000001cd, 0x000001ce, 0x000001cd,
+ 0x000001cf, 0x000001d0, 0x000001cf,
+ 0x000001d1, 0x000001d2, 0x000001d1,
+ 0x000001d3, 0x000001d4, 0x000001d3,
+ 0x000001d5, 0x000001d6, 0x000001d5,
+ 0x000001d7, 0x000001d8, 0x000001d7,
+ 0x000001d9, 0x000001da, 0x000001d9,
+ 0x000001db, 0x000001dc, 0x000001db,
+ 0x000001de, 0x000001df, 0x000001de,
+ 0x000001e0, 0x000001e1, 0x000001e0,
+ 0x000001e2, 0x000001e3, 0x000001e2,
+ 0x000001e4, 0x000001e5, 0x000001e4,
+ 0x000001e6, 0x000001e7, 0x000001e6,
+ 0x000001e8, 0x000001e9, 0x000001e8,
+ 0x000001ea, 0x000001eb, 0x000001ea,
+ 0x000001ec, 0x000001ed, 0x000001ec,
+ 0x000001ee, 0x000001ef, 0x000001ee,
+ 0x000001f1, 0x000001f3, 0x000001f2,
+ 0x000001f4, 0x000001f5, 0x000001f4,
+ 0x000001f6, 0x00000195, 0x000001f6,
+ 0x000001f7, 0x000001bf, 0x000001f7,
+ 0x000001f8, 0x000001f9, 0x000001f8,
+ 0x000001fa, 0x000001fb, 0x000001fa,
+ 0x000001fc, 0x000001fd, 0x000001fc,
+ 0x000001fe, 0x000001ff, 0x000001fe,
+ 0x00000200, 0x00000201, 0x00000200,
+ 0x00000202, 0x00000203, 0x00000202,
+ 0x00000204, 0x00000205, 0x00000204,
+ 0x00000206, 0x00000207, 0x00000206,
+ 0x00000208, 0x00000209, 0x00000208,
+ 0x0000020a, 0x0000020b, 0x0000020a,
+ 0x0000020c, 0x0000020d, 0x0000020c,
+ 0x0000020e, 0x0000020f, 0x0000020e,
+ 0x00000210, 0x00000211, 0x00000210,
+ 0x00000212, 0x00000213, 0x00000212,
+ 0x00000214, 0x00000215, 0x00000214,
+ 0x00000216, 0x00000217, 0x00000216,
+ 0x00000218, 0x00000219, 0x00000218,
+ 0x0000021a, 0x0000021b, 0x0000021a,
+ 0x0000021c, 0x0000021d, 0x0000021c,
+ 0x0000021e, 0x0000021f, 0x0000021e,
+ 0x00000220, 0x0000019e, 0x00000220,
+ 0x00000222, 0x00000223, 0x00000222,
+ 0x00000224, 0x00000225, 0x00000224,
+ 0x00000226, 0x00000227, 0x00000226,
+ 0x00000228, 0x00000229, 0x00000228,
+ 0x0000022a, 0x0000022b, 0x0000022a,
+ 0x0000022c, 0x0000022d, 0x0000022c,
+ 0x0000022e, 0x0000022f, 0x0000022e,
+ 0x00000230, 0x00000231, 0x00000230,
+ 0x00000232, 0x00000233, 0x00000232,
+ 0x00000386, 0x000003ac, 0x00000386,
+ 0x00000388, 0x000003ad, 0x00000388,
+ 0x00000389, 0x000003ae, 0x00000389,
+ 0x0000038a, 0x000003af, 0x0000038a,
+ 0x0000038c, 0x000003cc, 0x0000038c,
+ 0x0000038e, 0x000003cd, 0x0000038e,
+ 0x0000038f, 0x000003ce, 0x0000038f,
+ 0x00000391, 0x000003b1, 0x00000391,
+ 0x00000392, 0x000003b2, 0x00000392,
+ 0x00000393, 0x000003b3, 0x00000393,
+ 0x00000394, 0x000003b4, 0x00000394,
+ 0x00000395, 0x000003b5, 0x00000395,
+ 0x00000396, 0x000003b6, 0x00000396,
+ 0x00000397, 0x000003b7, 0x00000397,
+ 0x00000398, 0x000003b8, 0x00000398,
+ 0x00000399, 0x000003b9, 0x00000399,
+ 0x0000039a, 0x000003ba, 0x0000039a,
+ 0x0000039b, 0x000003bb, 0x0000039b,
+ 0x0000039c, 0x000003bc, 0x0000039c,
+ 0x0000039d, 0x000003bd, 0x0000039d,
+ 0x0000039e, 0x000003be, 0x0000039e,
+ 0x0000039f, 0x000003bf, 0x0000039f,
+ 0x000003a0, 0x000003c0, 0x000003a0,
+ 0x000003a1, 0x000003c1, 0x000003a1,
+ 0x000003a3, 0x000003c3, 0x000003a3,
+ 0x000003a4, 0x000003c4, 0x000003a4,
+ 0x000003a5, 0x000003c5, 0x000003a5,
+ 0x000003a6, 0x000003c6, 0x000003a6,
+ 0x000003a7, 0x000003c7, 0x000003a7,
+ 0x000003a8, 0x000003c8, 0x000003a8,
+ 0x000003a9, 0x000003c9, 0x000003a9,
+ 0x000003aa, 0x000003ca, 0x000003aa,
+ 0x000003ab, 0x000003cb, 0x000003ab,
+ 0x000003d8, 0x000003d9, 0x000003d8,
+ 0x000003da, 0x000003db, 0x000003da,
+ 0x000003dc, 0x000003dd, 0x000003dc,
+ 0x000003de, 0x000003df, 0x000003de,
+ 0x000003e0, 0x000003e1, 0x000003e0,
+ 0x000003e2, 0x000003e3, 0x000003e2,
+ 0x000003e4, 0x000003e5, 0x000003e4,
+ 0x000003e6, 0x000003e7, 0x000003e6,
+ 0x000003e8, 0x000003e9, 0x000003e8,
+ 0x000003ea, 0x000003eb, 0x000003ea,
+ 0x000003ec, 0x000003ed, 0x000003ec,
+ 0x000003ee, 0x000003ef, 0x000003ee,
+ 0x000003f4, 0x000003b8, 0x000003f4,
+ 0x00000400, 0x00000450, 0x00000400,
+ 0x00000401, 0x00000451, 0x00000401,
+ 0x00000402, 0x00000452, 0x00000402,
+ 0x00000403, 0x00000453, 0x00000403,
+ 0x00000404, 0x00000454, 0x00000404,
+ 0x00000405, 0x00000455, 0x00000405,
+ 0x00000406, 0x00000456, 0x00000406,
+ 0x00000407, 0x00000457, 0x00000407,
+ 0x00000408, 0x00000458, 0x00000408,
+ 0x00000409, 0x00000459, 0x00000409,
+ 0x0000040a, 0x0000045a, 0x0000040a,
+ 0x0000040b, 0x0000045b, 0x0000040b,
+ 0x0000040c, 0x0000045c, 0x0000040c,
+ 0x0000040d, 0x0000045d, 0x0000040d,
+ 0x0000040e, 0x0000045e, 0x0000040e,
+ 0x0000040f, 0x0000045f, 0x0000040f,
+ 0x00000410, 0x00000430, 0x00000410,
+ 0x00000411, 0x00000431, 0x00000411,
+ 0x00000412, 0x00000432, 0x00000412,
+ 0x00000413, 0x00000433, 0x00000413,
+ 0x00000414, 0x00000434, 0x00000414,
+ 0x00000415, 0x00000435, 0x00000415,
+ 0x00000416, 0x00000436, 0x00000416,
+ 0x00000417, 0x00000437, 0x00000417,
+ 0x00000418, 0x00000438, 0x00000418,
+ 0x00000419, 0x00000439, 0x00000419,
+ 0x0000041a, 0x0000043a, 0x0000041a,
+ 0x0000041b, 0x0000043b, 0x0000041b,
+ 0x0000041c, 0x0000043c, 0x0000041c,
+ 0x0000041d, 0x0000043d, 0x0000041d,
+ 0x0000041e, 0x0000043e, 0x0000041e,
+ 0x0000041f, 0x0000043f, 0x0000041f,
+ 0x00000420, 0x00000440, 0x00000420,
+ 0x00000421, 0x00000441, 0x00000421,
+ 0x00000422, 0x00000442, 0x00000422,
+ 0x00000423, 0x00000443, 0x00000423,
+ 0x00000424, 0x00000444, 0x00000424,
+ 0x00000425, 0x00000445, 0x00000425,
+ 0x00000426, 0x00000446, 0x00000426,
+ 0x00000427, 0x00000447, 0x00000427,
+ 0x00000428, 0x00000448, 0x00000428,
+ 0x00000429, 0x00000449, 0x00000429,
+ 0x0000042a, 0x0000044a, 0x0000042a,
+ 0x0000042b, 0x0000044b, 0x0000042b,
+ 0x0000042c, 0x0000044c, 0x0000042c,
+ 0x0000042d, 0x0000044d, 0x0000042d,
+ 0x0000042e, 0x0000044e, 0x0000042e,
+ 0x0000042f, 0x0000044f, 0x0000042f,
+ 0x00000460, 0x00000461, 0x00000460,
+ 0x00000462, 0x00000463, 0x00000462,
+ 0x00000464, 0x00000465, 0x00000464,
+ 0x00000466, 0x00000467, 0x00000466,
+ 0x00000468, 0x00000469, 0x00000468,
+ 0x0000046a, 0x0000046b, 0x0000046a,
+ 0x0000046c, 0x0000046d, 0x0000046c,
+ 0x0000046e, 0x0000046f, 0x0000046e,
+ 0x00000470, 0x00000471, 0x00000470,
+ 0x00000472, 0x00000473, 0x00000472,
+ 0x00000474, 0x00000475, 0x00000474,
+ 0x00000476, 0x00000477, 0x00000476,
+ 0x00000478, 0x00000479, 0x00000478,
+ 0x0000047a, 0x0000047b, 0x0000047a,
+ 0x0000047c, 0x0000047d, 0x0000047c,
+ 0x0000047e, 0x0000047f, 0x0000047e,
+ 0x00000480, 0x00000481, 0x00000480,
+ 0x0000048a, 0x0000048b, 0x0000048a,
+ 0x0000048c, 0x0000048d, 0x0000048c,
+ 0x0000048e, 0x0000048f, 0x0000048e,
+ 0x00000490, 0x00000491, 0x00000490,
+ 0x00000492, 0x00000493, 0x00000492,
+ 0x00000494, 0x00000495, 0x00000494,
+ 0x00000496, 0x00000497, 0x00000496,
+ 0x00000498, 0x00000499, 0x00000498,
+ 0x0000049a, 0x0000049b, 0x0000049a,
+ 0x0000049c, 0x0000049d, 0x0000049c,
+ 0x0000049e, 0x0000049f, 0x0000049e,
+ 0x000004a0, 0x000004a1, 0x000004a0,
+ 0x000004a2, 0x000004a3, 0x000004a2,
+ 0x000004a4, 0x000004a5, 0x000004a4,
+ 0x000004a6, 0x000004a7, 0x000004a6,
+ 0x000004a8, 0x000004a9, 0x000004a8,
+ 0x000004aa, 0x000004ab, 0x000004aa,
+ 0x000004ac, 0x000004ad, 0x000004ac,
+ 0x000004ae, 0x000004af, 0x000004ae,
+ 0x000004b0, 0x000004b1, 0x000004b0,
+ 0x000004b2, 0x000004b3, 0x000004b2,
+ 0x000004b4, 0x000004b5, 0x000004b4,
+ 0x000004b6, 0x000004b7, 0x000004b6,
+ 0x000004b8, 0x000004b9, 0x000004b8,
+ 0x000004ba, 0x000004bb, 0x000004ba,
+ 0x000004bc, 0x000004bd, 0x000004bc,
+ 0x000004be, 0x000004bf, 0x000004be,
+ 0x000004c1, 0x000004c2, 0x000004c1,
+ 0x000004c3, 0x000004c4, 0x000004c3,
+ 0x000004c5, 0x000004c6, 0x000004c5,
+ 0x000004c7, 0x000004c8, 0x000004c7,
+ 0x000004c9, 0x000004ca, 0x000004c9,
+ 0x000004cb, 0x000004cc, 0x000004cb,
+ 0x000004cd, 0x000004ce, 0x000004cd,
+ 0x000004d0, 0x000004d1, 0x000004d0,
+ 0x000004d2, 0x000004d3, 0x000004d2,
+ 0x000004d4, 0x000004d5, 0x000004d4,
+ 0x000004d6, 0x000004d7, 0x000004d6,
+ 0x000004d8, 0x000004d9, 0x000004d8,
+ 0x000004da, 0x000004db, 0x000004da,
+ 0x000004dc, 0x000004dd, 0x000004dc,
+ 0x000004de, 0x000004df, 0x000004de,
+ 0x000004e0, 0x000004e1, 0x000004e0,
+ 0x000004e2, 0x000004e3, 0x000004e2,
+ 0x000004e4, 0x000004e5, 0x000004e4,
+ 0x000004e6, 0x000004e7, 0x000004e6,
+ 0x000004e8, 0x000004e9, 0x000004e8,
+ 0x000004ea, 0x000004eb, 0x000004ea,
+ 0x000004ec, 0x000004ed, 0x000004ec,
+ 0x000004ee, 0x000004ef, 0x000004ee,
+ 0x000004f0, 0x000004f1, 0x000004f0,
+ 0x000004f2, 0x000004f3, 0x000004f2,
+ 0x000004f4, 0x000004f5, 0x000004f4,
+ 0x000004f8, 0x000004f9, 0x000004f8,
+ 0x00000500, 0x00000501, 0x00000500,
+ 0x00000502, 0x00000503, 0x00000502,
+ 0x00000504, 0x00000505, 0x00000504,
+ 0x00000506, 0x00000507, 0x00000506,
+ 0x00000508, 0x00000509, 0x00000508,
+ 0x0000050a, 0x0000050b, 0x0000050a,
+ 0x0000050c, 0x0000050d, 0x0000050c,
+ 0x0000050e, 0x0000050f, 0x0000050e,
+ 0x00000531, 0x00000561, 0x00000531,
+ 0x00000532, 0x00000562, 0x00000532,
+ 0x00000533, 0x00000563, 0x00000533,
+ 0x00000534, 0x00000564, 0x00000534,
+ 0x00000535, 0x00000565, 0x00000535,
+ 0x00000536, 0x00000566, 0x00000536,
+ 0x00000537, 0x00000567, 0x00000537,
+ 0x00000538, 0x00000568, 0x00000538,
+ 0x00000539, 0x00000569, 0x00000539,
+ 0x0000053a, 0x0000056a, 0x0000053a,
+ 0x0000053b, 0x0000056b, 0x0000053b,
+ 0x0000053c, 0x0000056c, 0x0000053c,
+ 0x0000053d, 0x0000056d, 0x0000053d,
+ 0x0000053e, 0x0000056e, 0x0000053e,
+ 0x0000053f, 0x0000056f, 0x0000053f,
+ 0x00000540, 0x00000570, 0x00000540,
+ 0x00000541, 0x00000571, 0x00000541,
+ 0x00000542, 0x00000572, 0x00000542,
+ 0x00000543, 0x00000573, 0x00000543,
+ 0x00000544, 0x00000574, 0x00000544,
+ 0x00000545, 0x00000575, 0x00000545,
+ 0x00000546, 0x00000576, 0x00000546,
+ 0x00000547, 0x00000577, 0x00000547,
+ 0x00000548, 0x00000578, 0x00000548,
+ 0x00000549, 0x00000579, 0x00000549,
+ 0x0000054a, 0x0000057a, 0x0000054a,
+ 0x0000054b, 0x0000057b, 0x0000054b,
+ 0x0000054c, 0x0000057c, 0x0000054c,
+ 0x0000054d, 0x0000057d, 0x0000054d,
+ 0x0000054e, 0x0000057e, 0x0000054e,
+ 0x0000054f, 0x0000057f, 0x0000054f,
+ 0x00000550, 0x00000580, 0x00000550,
+ 0x00000551, 0x00000581, 0x00000551,
+ 0x00000552, 0x00000582, 0x00000552,
+ 0x00000553, 0x00000583, 0x00000553,
+ 0x00000554, 0x00000584, 0x00000554,
+ 0x00000555, 0x00000585, 0x00000555,
+ 0x00000556, 0x00000586, 0x00000556,
+ 0x00001e00, 0x00001e01, 0x00001e00,
+ 0x00001e02, 0x00001e03, 0x00001e02,
+ 0x00001e04, 0x00001e05, 0x00001e04,
+ 0x00001e06, 0x00001e07, 0x00001e06,
+ 0x00001e08, 0x00001e09, 0x00001e08,
+ 0x00001e0a, 0x00001e0b, 0x00001e0a,
+ 0x00001e0c, 0x00001e0d, 0x00001e0c,
+ 0x00001e0e, 0x00001e0f, 0x00001e0e,
+ 0x00001e10, 0x00001e11, 0x00001e10,
+ 0x00001e12, 0x00001e13, 0x00001e12,
+ 0x00001e14, 0x00001e15, 0x00001e14,
+ 0x00001e16, 0x00001e17, 0x00001e16,
+ 0x00001e18, 0x00001e19, 0x00001e18,
+ 0x00001e1a, 0x00001e1b, 0x00001e1a,
+ 0x00001e1c, 0x00001e1d, 0x00001e1c,
+ 0x00001e1e, 0x00001e1f, 0x00001e1e,
+ 0x00001e20, 0x00001e21, 0x00001e20,
+ 0x00001e22, 0x00001e23, 0x00001e22,
+ 0x00001e24, 0x00001e25, 0x00001e24,
+ 0x00001e26, 0x00001e27, 0x00001e26,
+ 0x00001e28, 0x00001e29, 0x00001e28,
+ 0x00001e2a, 0x00001e2b, 0x00001e2a,
+ 0x00001e2c, 0x00001e2d, 0x00001e2c,
+ 0x00001e2e, 0x00001e2f, 0x00001e2e,
+ 0x00001e30, 0x00001e31, 0x00001e30,
+ 0x00001e32, 0x00001e33, 0x00001e32,
+ 0x00001e34, 0x00001e35, 0x00001e34,
+ 0x00001e36, 0x00001e37, 0x00001e36,
+ 0x00001e38, 0x00001e39, 0x00001e38,
+ 0x00001e3a, 0x00001e3b, 0x00001e3a,
+ 0x00001e3c, 0x00001e3d, 0x00001e3c,
+ 0x00001e3e, 0x00001e3f, 0x00001e3e,
+ 0x00001e40, 0x00001e41, 0x00001e40,
+ 0x00001e42, 0x00001e43, 0x00001e42,
+ 0x00001e44, 0x00001e45, 0x00001e44,
+ 0x00001e46, 0x00001e47, 0x00001e46,
+ 0x00001e48, 0x00001e49, 0x00001e48,
+ 0x00001e4a, 0x00001e4b, 0x00001e4a,
+ 0x00001e4c, 0x00001e4d, 0x00001e4c,
+ 0x00001e4e, 0x00001e4f, 0x00001e4e,
+ 0x00001e50, 0x00001e51, 0x00001e50,
+ 0x00001e52, 0x00001e53, 0x00001e52,
+ 0x00001e54, 0x00001e55, 0x00001e54,
+ 0x00001e56, 0x00001e57, 0x00001e56,
+ 0x00001e58, 0x00001e59, 0x00001e58,
+ 0x00001e5a, 0x00001e5b, 0x00001e5a,
+ 0x00001e5c, 0x00001e5d, 0x00001e5c,
+ 0x00001e5e, 0x00001e5f, 0x00001e5e,
+ 0x00001e60, 0x00001e61, 0x00001e60,
+ 0x00001e62, 0x00001e63, 0x00001e62,
+ 0x00001e64, 0x00001e65, 0x00001e64,
+ 0x00001e66, 0x00001e67, 0x00001e66,
+ 0x00001e68, 0x00001e69, 0x00001e68,
+ 0x00001e6a, 0x00001e6b, 0x00001e6a,
+ 0x00001e6c, 0x00001e6d, 0x00001e6c,
+ 0x00001e6e, 0x00001e6f, 0x00001e6e,
+ 0x00001e70, 0x00001e71, 0x00001e70,
+ 0x00001e72, 0x00001e73, 0x00001e72,
+ 0x00001e74, 0x00001e75, 0x00001e74,
+ 0x00001e76, 0x00001e77, 0x00001e76,
+ 0x00001e78, 0x00001e79, 0x00001e78,
+ 0x00001e7a, 0x00001e7b, 0x00001e7a,
+ 0x00001e7c, 0x00001e7d, 0x00001e7c,
+ 0x00001e7e, 0x00001e7f, 0x00001e7e,
+ 0x00001e80, 0x00001e81, 0x00001e80,
+ 0x00001e82, 0x00001e83, 0x00001e82,
+ 0x00001e84, 0x00001e85, 0x00001e84,
+ 0x00001e86, 0x00001e87, 0x00001e86,
+ 0x00001e88, 0x00001e89, 0x00001e88,
+ 0x00001e8a, 0x00001e8b, 0x00001e8a,
+ 0x00001e8c, 0x00001e8d, 0x00001e8c,
+ 0x00001e8e, 0x00001e8f, 0x00001e8e,
+ 0x00001e90, 0x00001e91, 0x00001e90,
+ 0x00001e92, 0x00001e93, 0x00001e92,
+ 0x00001e94, 0x00001e95, 0x00001e94,
+ 0x00001ea0, 0x00001ea1, 0x00001ea0,
+ 0x00001ea2, 0x00001ea3, 0x00001ea2,
+ 0x00001ea4, 0x00001ea5, 0x00001ea4,
+ 0x00001ea6, 0x00001ea7, 0x00001ea6,
+ 0x00001ea8, 0x00001ea9, 0x00001ea8,
+ 0x00001eaa, 0x00001eab, 0x00001eaa,
+ 0x00001eac, 0x00001ead, 0x00001eac,
+ 0x00001eae, 0x00001eaf, 0x00001eae,
+ 0x00001eb0, 0x00001eb1, 0x00001eb0,
+ 0x00001eb2, 0x00001eb3, 0x00001eb2,
+ 0x00001eb4, 0x00001eb5, 0x00001eb4,
+ 0x00001eb6, 0x00001eb7, 0x00001eb6,
+ 0x00001eb8, 0x00001eb9, 0x00001eb8,
+ 0x00001eba, 0x00001ebb, 0x00001eba,
+ 0x00001ebc, 0x00001ebd, 0x00001ebc,
+ 0x00001ebe, 0x00001ebf, 0x00001ebe,
+ 0x00001ec0, 0x00001ec1, 0x00001ec0,
+ 0x00001ec2, 0x00001ec3, 0x00001ec2,
+ 0x00001ec4, 0x00001ec5, 0x00001ec4,
+ 0x00001ec6, 0x00001ec7, 0x00001ec6,
+ 0x00001ec8, 0x00001ec9, 0x00001ec8,
+ 0x00001eca, 0x00001ecb, 0x00001eca,
+ 0x00001ecc, 0x00001ecd, 0x00001ecc,
+ 0x00001ece, 0x00001ecf, 0x00001ece,
+ 0x00001ed0, 0x00001ed1, 0x00001ed0,
+ 0x00001ed2, 0x00001ed3, 0x00001ed2,
+ 0x00001ed4, 0x00001ed5, 0x00001ed4,
+ 0x00001ed6, 0x00001ed7, 0x00001ed6,
+ 0x00001ed8, 0x00001ed9, 0x00001ed8,
+ 0x00001eda, 0x00001edb, 0x00001eda,
+ 0x00001edc, 0x00001edd, 0x00001edc,
+ 0x00001ede, 0x00001edf, 0x00001ede,
+ 0x00001ee0, 0x00001ee1, 0x00001ee0,
+ 0x00001ee2, 0x00001ee3, 0x00001ee2,
+ 0x00001ee4, 0x00001ee5, 0x00001ee4,
+ 0x00001ee6, 0x00001ee7, 0x00001ee6,
+ 0x00001ee8, 0x00001ee9, 0x00001ee8,
+ 0x00001eea, 0x00001eeb, 0x00001eea,
+ 0x00001eec, 0x00001eed, 0x00001eec,
+ 0x00001eee, 0x00001eef, 0x00001eee,
+ 0x00001ef0, 0x00001ef1, 0x00001ef0,
+ 0x00001ef2, 0x00001ef3, 0x00001ef2,
+ 0x00001ef4, 0x00001ef5, 0x00001ef4,
+ 0x00001ef6, 0x00001ef7, 0x00001ef6,
+ 0x00001ef8, 0x00001ef9, 0x00001ef8,
+ 0x00001f08, 0x00001f00, 0x00001f08,
+ 0x00001f09, 0x00001f01, 0x00001f09,
+ 0x00001f0a, 0x00001f02, 0x00001f0a,
+ 0x00001f0b, 0x00001f03, 0x00001f0b,
+ 0x00001f0c, 0x00001f04, 0x00001f0c,
+ 0x00001f0d, 0x00001f05, 0x00001f0d,
+ 0x00001f0e, 0x00001f06, 0x00001f0e,
+ 0x00001f0f, 0x00001f07, 0x00001f0f,
+ 0x00001f18, 0x00001f10, 0x00001f18,
+ 0x00001f19, 0x00001f11, 0x00001f19,
+ 0x00001f1a, 0x00001f12, 0x00001f1a,
+ 0x00001f1b, 0x00001f13, 0x00001f1b,
+ 0x00001f1c, 0x00001f14, 0x00001f1c,
+ 0x00001f1d, 0x00001f15, 0x00001f1d,
+ 0x00001f28, 0x00001f20, 0x00001f28,
+ 0x00001f29, 0x00001f21, 0x00001f29,
+ 0x00001f2a, 0x00001f22, 0x00001f2a,
+ 0x00001f2b, 0x00001f23, 0x00001f2b,
+ 0x00001f2c, 0x00001f24, 0x00001f2c,
+ 0x00001f2d, 0x00001f25, 0x00001f2d,
+ 0x00001f2e, 0x00001f26, 0x00001f2e,
+ 0x00001f2f, 0x00001f27, 0x00001f2f,
+ 0x00001f38, 0x00001f30, 0x00001f38,
+ 0x00001f39, 0x00001f31, 0x00001f39,
+ 0x00001f3a, 0x00001f32, 0x00001f3a,
+ 0x00001f3b, 0x00001f33, 0x00001f3b,
+ 0x00001f3c, 0x00001f34, 0x00001f3c,
+ 0x00001f3d, 0x00001f35, 0x00001f3d,
+ 0x00001f3e, 0x00001f36, 0x00001f3e,
+ 0x00001f3f, 0x00001f37, 0x00001f3f,
+ 0x00001f48, 0x00001f40, 0x00001f48,
+ 0x00001f49, 0x00001f41, 0x00001f49,
+ 0x00001f4a, 0x00001f42, 0x00001f4a,
+ 0x00001f4b, 0x00001f43, 0x00001f4b,
+ 0x00001f4c, 0x00001f44, 0x00001f4c,
+ 0x00001f4d, 0x00001f45, 0x00001f4d,
+ 0x00001f59, 0x00001f51, 0x00001f59,
+ 0x00001f5b, 0x00001f53, 0x00001f5b,
+ 0x00001f5d, 0x00001f55, 0x00001f5d,
+ 0x00001f5f, 0x00001f57, 0x00001f5f,
+ 0x00001f68, 0x00001f60, 0x00001f68,
+ 0x00001f69, 0x00001f61, 0x00001f69,
+ 0x00001f6a, 0x00001f62, 0x00001f6a,
+ 0x00001f6b, 0x00001f63, 0x00001f6b,
+ 0x00001f6c, 0x00001f64, 0x00001f6c,
+ 0x00001f6d, 0x00001f65, 0x00001f6d,
+ 0x00001f6e, 0x00001f66, 0x00001f6e,
+ 0x00001f6f, 0x00001f67, 0x00001f6f,
+ 0x00001fb8, 0x00001fb0, 0x00001fb8,
+ 0x00001fb9, 0x00001fb1, 0x00001fb9,
+ 0x00001fba, 0x00001f70, 0x00001fba,
+ 0x00001fbb, 0x00001f71, 0x00001fbb,
+ 0x00001fc8, 0x00001f72, 0x00001fc8,
+ 0x00001fc9, 0x00001f73, 0x00001fc9,
+ 0x00001fca, 0x00001f74, 0x00001fca,
+ 0x00001fcb, 0x00001f75, 0x00001fcb,
+ 0x00001fd8, 0x00001fd0, 0x00001fd8,
+ 0x00001fd9, 0x00001fd1, 0x00001fd9,
+ 0x00001fda, 0x00001f76, 0x00001fda,
+ 0x00001fdb, 0x00001f77, 0x00001fdb,
+ 0x00001fe8, 0x00001fe0, 0x00001fe8,
+ 0x00001fe9, 0x00001fe1, 0x00001fe9,
+ 0x00001fea, 0x00001f7a, 0x00001fea,
+ 0x00001feb, 0x00001f7b, 0x00001feb,
+ 0x00001fec, 0x00001fe5, 0x00001fec,
+ 0x00001ff8, 0x00001f78, 0x00001ff8,
+ 0x00001ff9, 0x00001f79, 0x00001ff9,
+ 0x00001ffa, 0x00001f7c, 0x00001ffa,
+ 0x00001ffb, 0x00001f7d, 0x00001ffb,
+ 0x00002126, 0x000003c9, 0x00002126,
+ 0x0000212a, 0x0000006b, 0x0000212a,
+ 0x0000212b, 0x000000e5, 0x0000212b,
+ 0x00002160, 0x00002170, 0x00002160,
+ 0x00002161, 0x00002171, 0x00002161,
+ 0x00002162, 0x00002172, 0x00002162,
+ 0x00002163, 0x00002173, 0x00002163,
+ 0x00002164, 0x00002174, 0x00002164,
+ 0x00002165, 0x00002175, 0x00002165,
+ 0x00002166, 0x00002176, 0x00002166,
+ 0x00002167, 0x00002177, 0x00002167,
+ 0x00002168, 0x00002178, 0x00002168,
+ 0x00002169, 0x00002179, 0x00002169,
+ 0x0000216a, 0x0000217a, 0x0000216a,
+ 0x0000216b, 0x0000217b, 0x0000216b,
+ 0x0000216c, 0x0000217c, 0x0000216c,
+ 0x0000216d, 0x0000217d, 0x0000216d,
+ 0x0000216e, 0x0000217e, 0x0000216e,
+ 0x0000216f, 0x0000217f, 0x0000216f,
+ 0x000024b6, 0x000024d0, 0x000024b6,
+ 0x000024b7, 0x000024d1, 0x000024b7,
+ 0x000024b8, 0x000024d2, 0x000024b8,
+ 0x000024b9, 0x000024d3, 0x000024b9,
+ 0x000024ba, 0x000024d4, 0x000024ba,
+ 0x000024bb, 0x000024d5, 0x000024bb,
+ 0x000024bc, 0x000024d6, 0x000024bc,
+ 0x000024bd, 0x000024d7, 0x000024bd,
+ 0x000024be, 0x000024d8, 0x000024be,
+ 0x000024bf, 0x000024d9, 0x000024bf,
+ 0x000024c0, 0x000024da, 0x000024c0,
+ 0x000024c1, 0x000024db, 0x000024c1,
+ 0x000024c2, 0x000024dc, 0x000024c2,
+ 0x000024c3, 0x000024dd, 0x000024c3,
+ 0x000024c4, 0x000024de, 0x000024c4,
+ 0x000024c5, 0x000024df, 0x000024c5,
+ 0x000024c6, 0x000024e0, 0x000024c6,
+ 0x000024c7, 0x000024e1, 0x000024c7,
+ 0x000024c8, 0x000024e2, 0x000024c8,
+ 0x000024c9, 0x000024e3, 0x000024c9,
+ 0x000024ca, 0x000024e4, 0x000024ca,
+ 0x000024cb, 0x000024e5, 0x000024cb,
+ 0x000024cc, 0x000024e6, 0x000024cc,
+ 0x000024cd, 0x000024e7, 0x000024cd,
+ 0x000024ce, 0x000024e8, 0x000024ce,
+ 0x000024cf, 0x000024e9, 0x000024cf,
+ 0x0000ff21, 0x0000ff41, 0x0000ff21,
+ 0x0000ff22, 0x0000ff42, 0x0000ff22,
+ 0x0000ff23, 0x0000ff43, 0x0000ff23,
+ 0x0000ff24, 0x0000ff44, 0x0000ff24,
+ 0x0000ff25, 0x0000ff45, 0x0000ff25,
+ 0x0000ff26, 0x0000ff46, 0x0000ff26,
+ 0x0000ff27, 0x0000ff47, 0x0000ff27,
+ 0x0000ff28, 0x0000ff48, 0x0000ff28,
+ 0x0000ff29, 0x0000ff49, 0x0000ff29,
+ 0x0000ff2a, 0x0000ff4a, 0x0000ff2a,
+ 0x0000ff2b, 0x0000ff4b, 0x0000ff2b,
+ 0x0000ff2c, 0x0000ff4c, 0x0000ff2c,
+ 0x0000ff2d, 0x0000ff4d, 0x0000ff2d,
+ 0x0000ff2e, 0x0000ff4e, 0x0000ff2e,
+ 0x0000ff2f, 0x0000ff4f, 0x0000ff2f,
+ 0x0000ff30, 0x0000ff50, 0x0000ff30,
+ 0x0000ff31, 0x0000ff51, 0x0000ff31,
+ 0x0000ff32, 0x0000ff52, 0x0000ff32,
+ 0x0000ff33, 0x0000ff53, 0x0000ff33,
+ 0x0000ff34, 0x0000ff54, 0x0000ff34,
+ 0x0000ff35, 0x0000ff55, 0x0000ff35,
+ 0x0000ff36, 0x0000ff56, 0x0000ff36,
+ 0x0000ff37, 0x0000ff57, 0x0000ff37,
+ 0x0000ff38, 0x0000ff58, 0x0000ff38,
+ 0x0000ff39, 0x0000ff59, 0x0000ff39,
+ 0x0000ff3a, 0x0000ff5a, 0x0000ff3a,
+ 0x00010400, 0x00010428, 0x00010400,
+ 0x00010401, 0x00010429, 0x00010401,
+ 0x00010402, 0x0001042a, 0x00010402,
+ 0x00010403, 0x0001042b, 0x00010403,
+ 0x00010404, 0x0001042c, 0x00010404,
+ 0x00010405, 0x0001042d, 0x00010405,
+ 0x00010406, 0x0001042e, 0x00010406,
+ 0x00010407, 0x0001042f, 0x00010407,
+ 0x00010408, 0x00010430, 0x00010408,
+ 0x00010409, 0x00010431, 0x00010409,
+ 0x0001040a, 0x00010432, 0x0001040a,
+ 0x0001040b, 0x00010433, 0x0001040b,
+ 0x0001040c, 0x00010434, 0x0001040c,
+ 0x0001040d, 0x00010435, 0x0001040d,
+ 0x0001040e, 0x00010436, 0x0001040e,
+ 0x0001040f, 0x00010437, 0x0001040f,
+ 0x00010410, 0x00010438, 0x00010410,
+ 0x00010411, 0x00010439, 0x00010411,
+ 0x00010412, 0x0001043a, 0x00010412,
+ 0x00010413, 0x0001043b, 0x00010413,
+ 0x00010414, 0x0001043c, 0x00010414,
+ 0x00010415, 0x0001043d, 0x00010415,
+ 0x00010416, 0x0001043e, 0x00010416,
+ 0x00010417, 0x0001043f, 0x00010417,
+ 0x00010418, 0x00010440, 0x00010418,
+ 0x00010419, 0x00010441, 0x00010419,
+ 0x0001041a, 0x00010442, 0x0001041a,
+ 0x0001041b, 0x00010443, 0x0001041b,
+ 0x0001041c, 0x00010444, 0x0001041c,
+ 0x0001041d, 0x00010445, 0x0001041d,
+ 0x0001041e, 0x00010446, 0x0001041e,
+ 0x0001041f, 0x00010447, 0x0001041f,
+ 0x00010420, 0x00010448, 0x00010420,
+ 0x00010421, 0x00010449, 0x00010421,
+ 0x00010422, 0x0001044a, 0x00010422,
+ 0x00010423, 0x0001044b, 0x00010423,
+ 0x00010424, 0x0001044c, 0x00010424,
+ 0x00010425, 0x0001044d, 0x00010425,
+ 0x00000061, 0x00000041, 0x00000041,
+ 0x00000062, 0x00000042, 0x00000042,
+ 0x00000063, 0x00000043, 0x00000043,
+ 0x00000064, 0x00000044, 0x00000044,
+ 0x00000065, 0x00000045, 0x00000045,
+ 0x00000066, 0x00000046, 0x00000046,
+ 0x00000067, 0x00000047, 0x00000047,
+ 0x00000068, 0x00000048, 0x00000048,
+ 0x00000069, 0x00000049, 0x00000049,
+ 0x0000006a, 0x0000004a, 0x0000004a,
+ 0x0000006b, 0x0000004b, 0x0000004b,
+ 0x0000006c, 0x0000004c, 0x0000004c,
+ 0x0000006d, 0x0000004d, 0x0000004d,
+ 0x0000006e, 0x0000004e, 0x0000004e,
+ 0x0000006f, 0x0000004f, 0x0000004f,
+ 0x00000070, 0x00000050, 0x00000050,
+ 0x00000071, 0x00000051, 0x00000051,
+ 0x00000072, 0x00000052, 0x00000052,
+ 0x00000073, 0x00000053, 0x00000053,
+ 0x00000074, 0x00000054, 0x00000054,
+ 0x00000075, 0x00000055, 0x00000055,
+ 0x00000076, 0x00000056, 0x00000056,
+ 0x00000077, 0x00000057, 0x00000057,
+ 0x00000078, 0x00000058, 0x00000058,
+ 0x00000079, 0x00000059, 0x00000059,
+ 0x0000007a, 0x0000005a, 0x0000005a,
+ 0x000000b5, 0x0000039c, 0x0000039c,
+ 0x000000e0, 0x000000c0, 0x000000c0,
+ 0x000000e1, 0x000000c1, 0x000000c1,
+ 0x000000e2, 0x000000c2, 0x000000c2,
+ 0x000000e3, 0x000000c3, 0x000000c3,
+ 0x000000e4, 0x000000c4, 0x000000c4,
+ 0x000000e5, 0x000000c5, 0x000000c5,
+ 0x000000e6, 0x000000c6, 0x000000c6,
+ 0x000000e7, 0x000000c7, 0x000000c7,
+ 0x000000e8, 0x000000c8, 0x000000c8,
+ 0x000000e9, 0x000000c9, 0x000000c9,
+ 0x000000ea, 0x000000ca, 0x000000ca,
+ 0x000000eb, 0x000000cb, 0x000000cb,
+ 0x000000ec, 0x000000cc, 0x000000cc,
+ 0x000000ed, 0x000000cd, 0x000000cd,
+ 0x000000ee, 0x000000ce, 0x000000ce,
+ 0x000000ef, 0x000000cf, 0x000000cf,
+ 0x000000f0, 0x000000d0, 0x000000d0,
+ 0x000000f1, 0x000000d1, 0x000000d1,
+ 0x000000f2, 0x000000d2, 0x000000d2,
+ 0x000000f3, 0x000000d3, 0x000000d3,
+ 0x000000f4, 0x000000d4, 0x000000d4,
+ 0x000000f5, 0x000000d5, 0x000000d5,
+ 0x000000f6, 0x000000d6, 0x000000d6,
+ 0x000000f8, 0x000000d8, 0x000000d8,
+ 0x000000f9, 0x000000d9, 0x000000d9,
+ 0x000000fa, 0x000000da, 0x000000da,
+ 0x000000fb, 0x000000db, 0x000000db,
+ 0x000000fc, 0x000000dc, 0x000000dc,
+ 0x000000fd, 0x000000dd, 0x000000dd,
+ 0x000000fe, 0x000000de, 0x000000de,
+ 0x000000ff, 0x00000178, 0x00000178,
+ 0x00000101, 0x00000100, 0x00000100,
+ 0x00000103, 0x00000102, 0x00000102,
+ 0x00000105, 0x00000104, 0x00000104,
+ 0x00000107, 0x00000106, 0x00000106,
+ 0x00000109, 0x00000108, 0x00000108,
+ 0x0000010b, 0x0000010a, 0x0000010a,
+ 0x0000010d, 0x0000010c, 0x0000010c,
+ 0x0000010f, 0x0000010e, 0x0000010e,
+ 0x00000111, 0x00000110, 0x00000110,
+ 0x00000113, 0x00000112, 0x00000112,
+ 0x00000115, 0x00000114, 0x00000114,
+ 0x00000117, 0x00000116, 0x00000116,
+ 0x00000119, 0x00000118, 0x00000118,
+ 0x0000011b, 0x0000011a, 0x0000011a,
+ 0x0000011d, 0x0000011c, 0x0000011c,
+ 0x0000011f, 0x0000011e, 0x0000011e,
+ 0x00000121, 0x00000120, 0x00000120,
+ 0x00000123, 0x00000122, 0x00000122,
+ 0x00000125, 0x00000124, 0x00000124,
+ 0x00000127, 0x00000126, 0x00000126,
+ 0x00000129, 0x00000128, 0x00000128,
+ 0x0000012b, 0x0000012a, 0x0000012a,
+ 0x0000012d, 0x0000012c, 0x0000012c,
+ 0x0000012f, 0x0000012e, 0x0000012e,
+ 0x00000131, 0x00000049, 0x00000049,
+ 0x00000133, 0x00000132, 0x00000132,
+ 0x00000135, 0x00000134, 0x00000134,
+ 0x00000137, 0x00000136, 0x00000136,
+ 0x0000013a, 0x00000139, 0x00000139,
+ 0x0000013c, 0x0000013b, 0x0000013b,
+ 0x0000013e, 0x0000013d, 0x0000013d,
+ 0x00000140, 0x0000013f, 0x0000013f,
+ 0x00000142, 0x00000141, 0x00000141,
+ 0x00000144, 0x00000143, 0x00000143,
+ 0x00000146, 0x00000145, 0x00000145,
+ 0x00000148, 0x00000147, 0x00000147,
+ 0x0000014b, 0x0000014a, 0x0000014a,
+ 0x0000014d, 0x0000014c, 0x0000014c,
+ 0x0000014f, 0x0000014e, 0x0000014e,
+ 0x00000151, 0x00000150, 0x00000150,
+ 0x00000153, 0x00000152, 0x00000152,
+ 0x00000155, 0x00000154, 0x00000154,
+ 0x00000157, 0x00000156, 0x00000156,
+ 0x00000159, 0x00000158, 0x00000158,
+ 0x0000015b, 0x0000015a, 0x0000015a,
+ 0x0000015d, 0x0000015c, 0x0000015c,
+ 0x0000015f, 0x0000015e, 0x0000015e,
+ 0x00000161, 0x00000160, 0x00000160,
+ 0x00000163, 0x00000162, 0x00000162,
+ 0x00000165, 0x00000164, 0x00000164,
+ 0x00000167, 0x00000166, 0x00000166,
+ 0x00000169, 0x00000168, 0x00000168,
+ 0x0000016b, 0x0000016a, 0x0000016a,
+ 0x0000016d, 0x0000016c, 0x0000016c,
+ 0x0000016f, 0x0000016e, 0x0000016e,
+ 0x00000171, 0x00000170, 0x00000170,
+ 0x00000173, 0x00000172, 0x00000172,
+ 0x00000175, 0x00000174, 0x00000174,
+ 0x00000177, 0x00000176, 0x00000176,
+ 0x0000017a, 0x00000179, 0x00000179,
+ 0x0000017c, 0x0000017b, 0x0000017b,
+ 0x0000017e, 0x0000017d, 0x0000017d,
+ 0x0000017f, 0x00000053, 0x00000053,
+ 0x00000183, 0x00000182, 0x00000182,
+ 0x00000185, 0x00000184, 0x00000184,
+ 0x00000188, 0x00000187, 0x00000187,
+ 0x0000018c, 0x0000018b, 0x0000018b,
+ 0x00000192, 0x00000191, 0x00000191,
+ 0x00000195, 0x000001f6, 0x000001f6,
+ 0x00000199, 0x00000198, 0x00000198,
+ 0x0000019e, 0x00000220, 0x00000220,
+ 0x000001a1, 0x000001a0, 0x000001a0,
+ 0x000001a3, 0x000001a2, 0x000001a2,
+ 0x000001a5, 0x000001a4, 0x000001a4,
+ 0x000001a8, 0x000001a7, 0x000001a7,
+ 0x000001ad, 0x000001ac, 0x000001ac,
+ 0x000001b0, 0x000001af, 0x000001af,
+ 0x000001b4, 0x000001b3, 0x000001b3,
+ 0x000001b6, 0x000001b5, 0x000001b5,
+ 0x000001b9, 0x000001b8, 0x000001b8,
+ 0x000001bd, 0x000001bc, 0x000001bc,
+ 0x000001bf, 0x000001f7, 0x000001f7,
+ 0x000001c6, 0x000001c4, 0x000001c5,
+ 0x000001c9, 0x000001c7, 0x000001c8,
+ 0x000001cc, 0x000001ca, 0x000001cb,
+ 0x000001ce, 0x000001cd, 0x000001cd,
+ 0x000001d0, 0x000001cf, 0x000001cf,
+ 0x000001d2, 0x000001d1, 0x000001d1,
+ 0x000001d4, 0x000001d3, 0x000001d3,
+ 0x000001d6, 0x000001d5, 0x000001d5,
+ 0x000001d8, 0x000001d7, 0x000001d7,
+ 0x000001da, 0x000001d9, 0x000001d9,
+ 0x000001dc, 0x000001db, 0x000001db,
+ 0x000001dd, 0x0000018e, 0x0000018e,
+ 0x000001df, 0x000001de, 0x000001de,
+ 0x000001e1, 0x000001e0, 0x000001e0,
+ 0x000001e3, 0x000001e2, 0x000001e2,
+ 0x000001e5, 0x000001e4, 0x000001e4,
+ 0x000001e7, 0x000001e6, 0x000001e6,
+ 0x000001e9, 0x000001e8, 0x000001e8,
+ 0x000001eb, 0x000001ea, 0x000001ea,
+ 0x000001ed, 0x000001ec, 0x000001ec,
+ 0x000001ef, 0x000001ee, 0x000001ee,
+ 0x000001f3, 0x000001f1, 0x000001f2,
+ 0x000001f5, 0x000001f4, 0x000001f4,
+ 0x000001f9, 0x000001f8, 0x000001f8,
+ 0x000001fb, 0x000001fa, 0x000001fa,
+ 0x000001fd, 0x000001fc, 0x000001fc,
+ 0x000001ff, 0x000001fe, 0x000001fe,
+ 0x00000201, 0x00000200, 0x00000200,
+ 0x00000203, 0x00000202, 0x00000202,
+ 0x00000205, 0x00000204, 0x00000204,
+ 0x00000207, 0x00000206, 0x00000206,
+ 0x00000209, 0x00000208, 0x00000208,
+ 0x0000020b, 0x0000020a, 0x0000020a,
+ 0x0000020d, 0x0000020c, 0x0000020c,
+ 0x0000020f, 0x0000020e, 0x0000020e,
+ 0x00000211, 0x00000210, 0x00000210,
+ 0x00000213, 0x00000212, 0x00000212,
+ 0x00000215, 0x00000214, 0x00000214,
+ 0x00000217, 0x00000216, 0x00000216,
+ 0x00000219, 0x00000218, 0x00000218,
+ 0x0000021b, 0x0000021a, 0x0000021a,
+ 0x0000021d, 0x0000021c, 0x0000021c,
+ 0x0000021f, 0x0000021e, 0x0000021e,
+ 0x00000223, 0x00000222, 0x00000222,
+ 0x00000225, 0x00000224, 0x00000224,
+ 0x00000227, 0x00000226, 0x00000226,
+ 0x00000229, 0x00000228, 0x00000228,
+ 0x0000022b, 0x0000022a, 0x0000022a,
+ 0x0000022d, 0x0000022c, 0x0000022c,
+ 0x0000022f, 0x0000022e, 0x0000022e,
+ 0x00000231, 0x00000230, 0x00000230,
+ 0x00000233, 0x00000232, 0x00000232,
+ 0x00000253, 0x00000181, 0x00000181,
+ 0x00000254, 0x00000186, 0x00000186,
+ 0x00000256, 0x00000189, 0x00000189,
+ 0x00000257, 0x0000018a, 0x0000018a,
+ 0x00000259, 0x0000018f, 0x0000018f,
+ 0x0000025b, 0x00000190, 0x00000190,
+ 0x00000260, 0x00000193, 0x00000193,
+ 0x00000263, 0x00000194, 0x00000194,
+ 0x00000268, 0x00000197, 0x00000197,
+ 0x00000269, 0x00000196, 0x00000196,
+ 0x0000026f, 0x0000019c, 0x0000019c,
+ 0x00000272, 0x0000019d, 0x0000019d,
+ 0x00000275, 0x0000019f, 0x0000019f,
+ 0x00000280, 0x000001a6, 0x000001a6,
+ 0x00000283, 0x000001a9, 0x000001a9,
+ 0x00000288, 0x000001ae, 0x000001ae,
+ 0x0000028a, 0x000001b1, 0x000001b1,
+ 0x0000028b, 0x000001b2, 0x000001b2,
+ 0x00000292, 0x000001b7, 0x000001b7,
+ 0x00000345, 0x00000399, 0x00000399,
+ 0x000003ac, 0x00000386, 0x00000386,
+ 0x000003ad, 0x00000388, 0x00000388,
+ 0x000003ae, 0x00000389, 0x00000389,
+ 0x000003af, 0x0000038a, 0x0000038a,
+ 0x000003b1, 0x00000391, 0x00000391,
+ 0x000003b2, 0x00000392, 0x00000392,
+ 0x000003b3, 0x00000393, 0x00000393,
+ 0x000003b4, 0x00000394, 0x00000394,
+ 0x000003b5, 0x00000395, 0x00000395,
+ 0x000003b6, 0x00000396, 0x00000396,
+ 0x000003b7, 0x00000397, 0x00000397,
+ 0x000003b8, 0x00000398, 0x00000398,
+ 0x000003b9, 0x00000399, 0x00000399,
+ 0x000003ba, 0x0000039a, 0x0000039a,
+ 0x000003bb, 0x0000039b, 0x0000039b,
+ 0x000003bc, 0x0000039c, 0x0000039c,
+ 0x000003bd, 0x0000039d, 0x0000039d,
+ 0x000003be, 0x0000039e, 0x0000039e,
+ 0x000003bf, 0x0000039f, 0x0000039f,
+ 0x000003c0, 0x000003a0, 0x000003a0,
+ 0x000003c1, 0x000003a1, 0x000003a1,
+ 0x000003c2, 0x000003a3, 0x000003a3,
+ 0x000003c3, 0x000003a3, 0x000003a3,
+ 0x000003c4, 0x000003a4, 0x000003a4,
+ 0x000003c5, 0x000003a5, 0x000003a5,
+ 0x000003c6, 0x000003a6, 0x000003a6,
+ 0x000003c7, 0x000003a7, 0x000003a7,
+ 0x000003c8, 0x000003a8, 0x000003a8,
+ 0x000003c9, 0x000003a9, 0x000003a9,
+ 0x000003ca, 0x000003aa, 0x000003aa,
+ 0x000003cb, 0x000003ab, 0x000003ab,
+ 0x000003cc, 0x0000038c, 0x0000038c,
+ 0x000003cd, 0x0000038e, 0x0000038e,
+ 0x000003ce, 0x0000038f, 0x0000038f,
+ 0x000003d0, 0x00000392, 0x00000392,
+ 0x000003d1, 0x00000398, 0x00000398,
+ 0x000003d5, 0x000003a6, 0x000003a6,
+ 0x000003d6, 0x000003a0, 0x000003a0,
+ 0x000003d9, 0x000003d8, 0x000003d8,
+ 0x000003db, 0x000003da, 0x000003da,
+ 0x000003dd, 0x000003dc, 0x000003dc,
+ 0x000003df, 0x000003de, 0x000003de,
+ 0x000003e1, 0x000003e0, 0x000003e0,
+ 0x000003e3, 0x000003e2, 0x000003e2,
+ 0x000003e5, 0x000003e4, 0x000003e4,
+ 0x000003e7, 0x000003e6, 0x000003e6,
+ 0x000003e9, 0x000003e8, 0x000003e8,
+ 0x000003eb, 0x000003ea, 0x000003ea,
+ 0x000003ed, 0x000003ec, 0x000003ec,
+ 0x000003ef, 0x000003ee, 0x000003ee,
+ 0x000003f0, 0x0000039a, 0x0000039a,
+ 0x000003f1, 0x000003a1, 0x000003a1,
+ 0x000003f2, 0x000003a3, 0x000003a3,
+ 0x000003f5, 0x00000395, 0x00000395,
+ 0x00000430, 0x00000410, 0x00000410,
+ 0x00000431, 0x00000411, 0x00000411,
+ 0x00000432, 0x00000412, 0x00000412,
+ 0x00000433, 0x00000413, 0x00000413,
+ 0x00000434, 0x00000414, 0x00000414,
+ 0x00000435, 0x00000415, 0x00000415,
+ 0x00000436, 0x00000416, 0x00000416,
+ 0x00000437, 0x00000417, 0x00000417,
+ 0x00000438, 0x00000418, 0x00000418,
+ 0x00000439, 0x00000419, 0x00000419,
+ 0x0000043a, 0x0000041a, 0x0000041a,
+ 0x0000043b, 0x0000041b, 0x0000041b,
+ 0x0000043c, 0x0000041c, 0x0000041c,
+ 0x0000043d, 0x0000041d, 0x0000041d,
+ 0x0000043e, 0x0000041e, 0x0000041e,
+ 0x0000043f, 0x0000041f, 0x0000041f,
+ 0x00000440, 0x00000420, 0x00000420,
+ 0x00000441, 0x00000421, 0x00000421,
+ 0x00000442, 0x00000422, 0x00000422,
+ 0x00000443, 0x00000423, 0x00000423,
+ 0x00000444, 0x00000424, 0x00000424,
+ 0x00000445, 0x00000425, 0x00000425,
+ 0x00000446, 0x00000426, 0x00000426,
+ 0x00000447, 0x00000427, 0x00000427,
+ 0x00000448, 0x00000428, 0x00000428,
+ 0x00000449, 0x00000429, 0x00000429,
+ 0x0000044a, 0x0000042a, 0x0000042a,
+ 0x0000044b, 0x0000042b, 0x0000042b,
+ 0x0000044c, 0x0000042c, 0x0000042c,
+ 0x0000044d, 0x0000042d, 0x0000042d,
+ 0x0000044e, 0x0000042e, 0x0000042e,
+ 0x0000044f, 0x0000042f, 0x0000042f,
+ 0x00000450, 0x00000400, 0x00000400,
+ 0x00000451, 0x00000401, 0x00000401,
+ 0x00000452, 0x00000402, 0x00000402,
+ 0x00000453, 0x00000403, 0x00000403,
+ 0x00000454, 0x00000404, 0x00000404,
+ 0x00000455, 0x00000405, 0x00000405,
+ 0x00000456, 0x00000406, 0x00000406,
+ 0x00000457, 0x00000407, 0x00000407,
+ 0x00000458, 0x00000408, 0x00000408,
+ 0x00000459, 0x00000409, 0x00000409,
+ 0x0000045a, 0x0000040a, 0x0000040a,
+ 0x0000045b, 0x0000040b, 0x0000040b,
+ 0x0000045c, 0x0000040c, 0x0000040c,
+ 0x0000045d, 0x0000040d, 0x0000040d,
+ 0x0000045e, 0x0000040e, 0x0000040e,
+ 0x0000045f, 0x0000040f, 0x0000040f,
+ 0x00000461, 0x00000460, 0x00000460,
+ 0x00000463, 0x00000462, 0x00000462,
+ 0x00000465, 0x00000464, 0x00000464,
+ 0x00000467, 0x00000466, 0x00000466,
+ 0x00000469, 0x00000468, 0x00000468,
+ 0x0000046b, 0x0000046a, 0x0000046a,
+ 0x0000046d, 0x0000046c, 0x0000046c,
+ 0x0000046f, 0x0000046e, 0x0000046e,
+ 0x00000471, 0x00000470, 0x00000470,
+ 0x00000473, 0x00000472, 0x00000472,
+ 0x00000475, 0x00000474, 0x00000474,
+ 0x00000477, 0x00000476, 0x00000476,
+ 0x00000479, 0x00000478, 0x00000478,
+ 0x0000047b, 0x0000047a, 0x0000047a,
+ 0x0000047d, 0x0000047c, 0x0000047c,
+ 0x0000047f, 0x0000047e, 0x0000047e,
+ 0x00000481, 0x00000480, 0x00000480,
+ 0x0000048b, 0x0000048a, 0x0000048a,
+ 0x0000048d, 0x0000048c, 0x0000048c,
+ 0x0000048f, 0x0000048e, 0x0000048e,
+ 0x00000491, 0x00000490, 0x00000490,
+ 0x00000493, 0x00000492, 0x00000492,
+ 0x00000495, 0x00000494, 0x00000494,
+ 0x00000497, 0x00000496, 0x00000496,
+ 0x00000499, 0x00000498, 0x00000498,
+ 0x0000049b, 0x0000049a, 0x0000049a,
+ 0x0000049d, 0x0000049c, 0x0000049c,
+ 0x0000049f, 0x0000049e, 0x0000049e,
+ 0x000004a1, 0x000004a0, 0x000004a0,
+ 0x000004a3, 0x000004a2, 0x000004a2,
+ 0x000004a5, 0x000004a4, 0x000004a4,
+ 0x000004a7, 0x000004a6, 0x000004a6,
+ 0x000004a9, 0x000004a8, 0x000004a8,
+ 0x000004ab, 0x000004aa, 0x000004aa,
+ 0x000004ad, 0x000004ac, 0x000004ac,
+ 0x000004af, 0x000004ae, 0x000004ae,
+ 0x000004b1, 0x000004b0, 0x000004b0,
+ 0x000004b3, 0x000004b2, 0x000004b2,
+ 0x000004b5, 0x000004b4, 0x000004b4,
+ 0x000004b7, 0x000004b6, 0x000004b6,
+ 0x000004b9, 0x000004b8, 0x000004b8,
+ 0x000004bb, 0x000004ba, 0x000004ba,
+ 0x000004bd, 0x000004bc, 0x000004bc,
+ 0x000004bf, 0x000004be, 0x000004be,
+ 0x000004c2, 0x000004c1, 0x000004c1,
+ 0x000004c4, 0x000004c3, 0x000004c3,
+ 0x000004c6, 0x000004c5, 0x000004c5,
+ 0x000004c8, 0x000004c7, 0x000004c7,
+ 0x000004ca, 0x000004c9, 0x000004c9,
+ 0x000004cc, 0x000004cb, 0x000004cb,
+ 0x000004ce, 0x000004cd, 0x000004cd,
+ 0x000004d1, 0x000004d0, 0x000004d0,
+ 0x000004d3, 0x000004d2, 0x000004d2,
+ 0x000004d5, 0x000004d4, 0x000004d4,
+ 0x000004d7, 0x000004d6, 0x000004d6,
+ 0x000004d9, 0x000004d8, 0x000004d8,
+ 0x000004db, 0x000004da, 0x000004da,
+ 0x000004dd, 0x000004dc, 0x000004dc,
+ 0x000004df, 0x000004de, 0x000004de,
+ 0x000004e1, 0x000004e0, 0x000004e0,
+ 0x000004e3, 0x000004e2, 0x000004e2,
+ 0x000004e5, 0x000004e4, 0x000004e4,
+ 0x000004e7, 0x000004e6, 0x000004e6,
+ 0x000004e9, 0x000004e8, 0x000004e8,
+ 0x000004eb, 0x000004ea, 0x000004ea,
+ 0x000004ed, 0x000004ec, 0x000004ec,
+ 0x000004ef, 0x000004ee, 0x000004ee,
+ 0x000004f1, 0x000004f0, 0x000004f0,
+ 0x000004f3, 0x000004f2, 0x000004f2,
+ 0x000004f5, 0x000004f4, 0x000004f4,
+ 0x000004f9, 0x000004f8, 0x000004f8,
+ 0x00000501, 0x00000500, 0x00000500,
+ 0x00000503, 0x00000502, 0x00000502,
+ 0x00000505, 0x00000504, 0x00000504,
+ 0x00000507, 0x00000506, 0x00000506,
+ 0x00000509, 0x00000508, 0x00000508,
+ 0x0000050b, 0x0000050a, 0x0000050a,
+ 0x0000050d, 0x0000050c, 0x0000050c,
+ 0x0000050f, 0x0000050e, 0x0000050e,
+ 0x00000561, 0x00000531, 0x00000531,
+ 0x00000562, 0x00000532, 0x00000532,
+ 0x00000563, 0x00000533, 0x00000533,
+ 0x00000564, 0x00000534, 0x00000534,
+ 0x00000565, 0x00000535, 0x00000535,
+ 0x00000566, 0x00000536, 0x00000536,
+ 0x00000567, 0x00000537, 0x00000537,
+ 0x00000568, 0x00000538, 0x00000538,
+ 0x00000569, 0x00000539, 0x00000539,
+ 0x0000056a, 0x0000053a, 0x0000053a,
+ 0x0000056b, 0x0000053b, 0x0000053b,
+ 0x0000056c, 0x0000053c, 0x0000053c,
+ 0x0000056d, 0x0000053d, 0x0000053d,
+ 0x0000056e, 0x0000053e, 0x0000053e,
+ 0x0000056f, 0x0000053f, 0x0000053f,
+ 0x00000570, 0x00000540, 0x00000540,
+ 0x00000571, 0x00000541, 0x00000541,
+ 0x00000572, 0x00000542, 0x00000542,
+ 0x00000573, 0x00000543, 0x00000543,
+ 0x00000574, 0x00000544, 0x00000544,
+ 0x00000575, 0x00000545, 0x00000545,
+ 0x00000576, 0x00000546, 0x00000546,
+ 0x00000577, 0x00000547, 0x00000547,
+ 0x00000578, 0x00000548, 0x00000548,
+ 0x00000579, 0x00000549, 0x00000549,
+ 0x0000057a, 0x0000054a, 0x0000054a,
+ 0x0000057b, 0x0000054b, 0x0000054b,
+ 0x0000057c, 0x0000054c, 0x0000054c,
+ 0x0000057d, 0x0000054d, 0x0000054d,
+ 0x0000057e, 0x0000054e, 0x0000054e,
+ 0x0000057f, 0x0000054f, 0x0000054f,
+ 0x00000580, 0x00000550, 0x00000550,
+ 0x00000581, 0x00000551, 0x00000551,
+ 0x00000582, 0x00000552, 0x00000552,
+ 0x00000583, 0x00000553, 0x00000553,
+ 0x00000584, 0x00000554, 0x00000554,
+ 0x00000585, 0x00000555, 0x00000555,
+ 0x00000586, 0x00000556, 0x00000556,
+ 0x00001e01, 0x00001e00, 0x00001e00,
+ 0x00001e03, 0x00001e02, 0x00001e02,
+ 0x00001e05, 0x00001e04, 0x00001e04,
+ 0x00001e07, 0x00001e06, 0x00001e06,
+ 0x00001e09, 0x00001e08, 0x00001e08,
+ 0x00001e0b, 0x00001e0a, 0x00001e0a,
+ 0x00001e0d, 0x00001e0c, 0x00001e0c,
+ 0x00001e0f, 0x00001e0e, 0x00001e0e,
+ 0x00001e11, 0x00001e10, 0x00001e10,
+ 0x00001e13, 0x00001e12, 0x00001e12,
+ 0x00001e15, 0x00001e14, 0x00001e14,
+ 0x00001e17, 0x00001e16, 0x00001e16,
+ 0x00001e19, 0x00001e18, 0x00001e18,
+ 0x00001e1b, 0x00001e1a, 0x00001e1a,
+ 0x00001e1d, 0x00001e1c, 0x00001e1c,
+ 0x00001e1f, 0x00001e1e, 0x00001e1e,
+ 0x00001e21, 0x00001e20, 0x00001e20,
+ 0x00001e23, 0x00001e22, 0x00001e22,
+ 0x00001e25, 0x00001e24, 0x00001e24,
+ 0x00001e27, 0x00001e26, 0x00001e26,
+ 0x00001e29, 0x00001e28, 0x00001e28,
+ 0x00001e2b, 0x00001e2a, 0x00001e2a,
+ 0x00001e2d, 0x00001e2c, 0x00001e2c,
+ 0x00001e2f, 0x00001e2e, 0x00001e2e,
+ 0x00001e31, 0x00001e30, 0x00001e30,
+ 0x00001e33, 0x00001e32, 0x00001e32,
+ 0x00001e35, 0x00001e34, 0x00001e34,
+ 0x00001e37, 0x00001e36, 0x00001e36,
+ 0x00001e39, 0x00001e38, 0x00001e38,
+ 0x00001e3b, 0x00001e3a, 0x00001e3a,
+ 0x00001e3d, 0x00001e3c, 0x00001e3c,
+ 0x00001e3f, 0x00001e3e, 0x00001e3e,
+ 0x00001e41, 0x00001e40, 0x00001e40,
+ 0x00001e43, 0x00001e42, 0x00001e42,
+ 0x00001e45, 0x00001e44, 0x00001e44,
+ 0x00001e47, 0x00001e46, 0x00001e46,
+ 0x00001e49, 0x00001e48, 0x00001e48,
+ 0x00001e4b, 0x00001e4a, 0x00001e4a,
+ 0x00001e4d, 0x00001e4c, 0x00001e4c,
+ 0x00001e4f, 0x00001e4e, 0x00001e4e,
+ 0x00001e51, 0x00001e50, 0x00001e50,
+ 0x00001e53, 0x00001e52, 0x00001e52,
+ 0x00001e55, 0x00001e54, 0x00001e54,
+ 0x00001e57, 0x00001e56, 0x00001e56,
+ 0x00001e59, 0x00001e58, 0x00001e58,
+ 0x00001e5b, 0x00001e5a, 0x00001e5a,
+ 0x00001e5d, 0x00001e5c, 0x00001e5c,
+ 0x00001e5f, 0x00001e5e, 0x00001e5e,
+ 0x00001e61, 0x00001e60, 0x00001e60,
+ 0x00001e63, 0x00001e62, 0x00001e62,
+ 0x00001e65, 0x00001e64, 0x00001e64,
+ 0x00001e67, 0x00001e66, 0x00001e66,
+ 0x00001e69, 0x00001e68, 0x00001e68,
+ 0x00001e6b, 0x00001e6a, 0x00001e6a,
+ 0x00001e6d, 0x00001e6c, 0x00001e6c,
+ 0x00001e6f, 0x00001e6e, 0x00001e6e,
+ 0x00001e71, 0x00001e70, 0x00001e70,
+ 0x00001e73, 0x00001e72, 0x00001e72,
+ 0x00001e75, 0x00001e74, 0x00001e74,
+ 0x00001e77, 0x00001e76, 0x00001e76,
+ 0x00001e79, 0x00001e78, 0x00001e78,
+ 0x00001e7b, 0x00001e7a, 0x00001e7a,
+ 0x00001e7d, 0x00001e7c, 0x00001e7c,
+ 0x00001e7f, 0x00001e7e, 0x00001e7e,
+ 0x00001e81, 0x00001e80, 0x00001e80,
+ 0x00001e83, 0x00001e82, 0x00001e82,
+ 0x00001e85, 0x00001e84, 0x00001e84,
+ 0x00001e87, 0x00001e86, 0x00001e86,
+ 0x00001e89, 0x00001e88, 0x00001e88,
+ 0x00001e8b, 0x00001e8a, 0x00001e8a,
+ 0x00001e8d, 0x00001e8c, 0x00001e8c,
+ 0x00001e8f, 0x00001e8e, 0x00001e8e,
+ 0x00001e91, 0x00001e90, 0x00001e90,
+ 0x00001e93, 0x00001e92, 0x00001e92,
+ 0x00001e95, 0x00001e94, 0x00001e94,
+ 0x00001e9b, 0x00001e60, 0x00001e60,
+ 0x00001ea1, 0x00001ea0, 0x00001ea0,
+ 0x00001ea3, 0x00001ea2, 0x00001ea2,
+ 0x00001ea5, 0x00001ea4, 0x00001ea4,
+ 0x00001ea7, 0x00001ea6, 0x00001ea6,
+ 0x00001ea9, 0x00001ea8, 0x00001ea8,
+ 0x00001eab, 0x00001eaa, 0x00001eaa,
+ 0x00001ead, 0x00001eac, 0x00001eac,
+ 0x00001eaf, 0x00001eae, 0x00001eae,
+ 0x00001eb1, 0x00001eb0, 0x00001eb0,
+ 0x00001eb3, 0x00001eb2, 0x00001eb2,
+ 0x00001eb5, 0x00001eb4, 0x00001eb4,
+ 0x00001eb7, 0x00001eb6, 0x00001eb6,
+ 0x00001eb9, 0x00001eb8, 0x00001eb8,
+ 0x00001ebb, 0x00001eba, 0x00001eba,
+ 0x00001ebd, 0x00001ebc, 0x00001ebc,
+ 0x00001ebf, 0x00001ebe, 0x00001ebe,
+ 0x00001ec1, 0x00001ec0, 0x00001ec0,
+ 0x00001ec3, 0x00001ec2, 0x00001ec2,
+ 0x00001ec5, 0x00001ec4, 0x00001ec4,
+ 0x00001ec7, 0x00001ec6, 0x00001ec6,
+ 0x00001ec9, 0x00001ec8, 0x00001ec8,
+ 0x00001ecb, 0x00001eca, 0x00001eca,
+ 0x00001ecd, 0x00001ecc, 0x00001ecc,
+ 0x00001ecf, 0x00001ece, 0x00001ece,
+ 0x00001ed1, 0x00001ed0, 0x00001ed0,
+ 0x00001ed3, 0x00001ed2, 0x00001ed2,
+ 0x00001ed5, 0x00001ed4, 0x00001ed4,
+ 0x00001ed7, 0x00001ed6, 0x00001ed6,
+ 0x00001ed9, 0x00001ed8, 0x00001ed8,
+ 0x00001edb, 0x00001eda, 0x00001eda,
+ 0x00001edd, 0x00001edc, 0x00001edc,
+ 0x00001edf, 0x00001ede, 0x00001ede,
+ 0x00001ee1, 0x00001ee0, 0x00001ee0,
+ 0x00001ee3, 0x00001ee2, 0x00001ee2,
+ 0x00001ee5, 0x00001ee4, 0x00001ee4,
+ 0x00001ee7, 0x00001ee6, 0x00001ee6,
+ 0x00001ee9, 0x00001ee8, 0x00001ee8,
+ 0x00001eeb, 0x00001eea, 0x00001eea,
+ 0x00001eed, 0x00001eec, 0x00001eec,
+ 0x00001eef, 0x00001eee, 0x00001eee,
+ 0x00001ef1, 0x00001ef0, 0x00001ef0,
+ 0x00001ef3, 0x00001ef2, 0x00001ef2,
+ 0x00001ef5, 0x00001ef4, 0x00001ef4,
+ 0x00001ef7, 0x00001ef6, 0x00001ef6,
+ 0x00001ef9, 0x00001ef8, 0x00001ef8,
+ 0x00001f00, 0x00001f08, 0x00001f08,
+ 0x00001f01, 0x00001f09, 0x00001f09,
+ 0x00001f02, 0x00001f0a, 0x00001f0a,
+ 0x00001f03, 0x00001f0b, 0x00001f0b,
+ 0x00001f04, 0x00001f0c, 0x00001f0c,
+ 0x00001f05, 0x00001f0d, 0x00001f0d,
+ 0x00001f06, 0x00001f0e, 0x00001f0e,
+ 0x00001f07, 0x00001f0f, 0x00001f0f,
+ 0x00001f10, 0x00001f18, 0x00001f18,
+ 0x00001f11, 0x00001f19, 0x00001f19,
+ 0x00001f12, 0x00001f1a, 0x00001f1a,
+ 0x00001f13, 0x00001f1b, 0x00001f1b,
+ 0x00001f14, 0x00001f1c, 0x00001f1c,
+ 0x00001f15, 0x00001f1d, 0x00001f1d,
+ 0x00001f20, 0x00001f28, 0x00001f28,
+ 0x00001f21, 0x00001f29, 0x00001f29,
+ 0x00001f22, 0x00001f2a, 0x00001f2a,
+ 0x00001f23, 0x00001f2b, 0x00001f2b,
+ 0x00001f24, 0x00001f2c, 0x00001f2c,
+ 0x00001f25, 0x00001f2d, 0x00001f2d,
+ 0x00001f26, 0x00001f2e, 0x00001f2e,
+ 0x00001f27, 0x00001f2f, 0x00001f2f,
+ 0x00001f30, 0x00001f38, 0x00001f38,
+ 0x00001f31, 0x00001f39, 0x00001f39,
+ 0x00001f32, 0x00001f3a, 0x00001f3a,
+ 0x00001f33, 0x00001f3b, 0x00001f3b,
+ 0x00001f34, 0x00001f3c, 0x00001f3c,
+ 0x00001f35, 0x00001f3d, 0x00001f3d,
+ 0x00001f36, 0x00001f3e, 0x00001f3e,
+ 0x00001f37, 0x00001f3f, 0x00001f3f,
+ 0x00001f40, 0x00001f48, 0x00001f48,
+ 0x00001f41, 0x00001f49, 0x00001f49,
+ 0x00001f42, 0x00001f4a, 0x00001f4a,
+ 0x00001f43, 0x00001f4b, 0x00001f4b,
+ 0x00001f44, 0x00001f4c, 0x00001f4c,
+ 0x00001f45, 0x00001f4d, 0x00001f4d,
+ 0x00001f51, 0x00001f59, 0x00001f59,
+ 0x00001f53, 0x00001f5b, 0x00001f5b,
+ 0x00001f55, 0x00001f5d, 0x00001f5d,
+ 0x00001f57, 0x00001f5f, 0x00001f5f,
+ 0x00001f60, 0x00001f68, 0x00001f68,
+ 0x00001f61, 0x00001f69, 0x00001f69,
+ 0x00001f62, 0x00001f6a, 0x00001f6a,
+ 0x00001f63, 0x00001f6b, 0x00001f6b,
+ 0x00001f64, 0x00001f6c, 0x00001f6c,
+ 0x00001f65, 0x00001f6d, 0x00001f6d,
+ 0x00001f66, 0x00001f6e, 0x00001f6e,
+ 0x00001f67, 0x00001f6f, 0x00001f6f,
+ 0x00001f70, 0x00001fba, 0x00001fba,
+ 0x00001f71, 0x00001fbb, 0x00001fbb,
+ 0x00001f72, 0x00001fc8, 0x00001fc8,
+ 0x00001f73, 0x00001fc9, 0x00001fc9,
+ 0x00001f74, 0x00001fca, 0x00001fca,
+ 0x00001f75, 0x00001fcb, 0x00001fcb,
+ 0x00001f76, 0x00001fda, 0x00001fda,
+ 0x00001f77, 0x00001fdb, 0x00001fdb,
+ 0x00001f78, 0x00001ff8, 0x00001ff8,
+ 0x00001f79, 0x00001ff9, 0x00001ff9,
+ 0x00001f7a, 0x00001fea, 0x00001fea,
+ 0x00001f7b, 0x00001feb, 0x00001feb,
+ 0x00001f7c, 0x00001ffa, 0x00001ffa,
+ 0x00001f7d, 0x00001ffb, 0x00001ffb,
+ 0x00001f80, 0x00001f88, 0x00001f88,
+ 0x00001f81, 0x00001f89, 0x00001f89,
+ 0x00001f82, 0x00001f8a, 0x00001f8a,
+ 0x00001f83, 0x00001f8b, 0x00001f8b,
+ 0x00001f84, 0x00001f8c, 0x00001f8c,
+ 0x00001f85, 0x00001f8d, 0x00001f8d,
+ 0x00001f86, 0x00001f8e, 0x00001f8e,
+ 0x00001f87, 0x00001f8f, 0x00001f8f,
+ 0x00001f90, 0x00001f98, 0x00001f98,
+ 0x00001f91, 0x00001f99, 0x00001f99,
+ 0x00001f92, 0x00001f9a, 0x00001f9a,
+ 0x00001f93, 0x00001f9b, 0x00001f9b,
+ 0x00001f94, 0x00001f9c, 0x00001f9c,
+ 0x00001f95, 0x00001f9d, 0x00001f9d,
+ 0x00001f96, 0x00001f9e, 0x00001f9e,
+ 0x00001f97, 0x00001f9f, 0x00001f9f,
+ 0x00001fa0, 0x00001fa8, 0x00001fa8,
+ 0x00001fa1, 0x00001fa9, 0x00001fa9,
+ 0x00001fa2, 0x00001faa, 0x00001faa,
+ 0x00001fa3, 0x00001fab, 0x00001fab,
+ 0x00001fa4, 0x00001fac, 0x00001fac,
+ 0x00001fa5, 0x00001fad, 0x00001fad,
+ 0x00001fa6, 0x00001fae, 0x00001fae,
+ 0x00001fa7, 0x00001faf, 0x00001faf,
+ 0x00001fb0, 0x00001fb8, 0x00001fb8,
+ 0x00001fb1, 0x00001fb9, 0x00001fb9,
+ 0x00001fb3, 0x00001fbc, 0x00001fbc,
+ 0x00001fbe, 0x00000399, 0x00000399,
+ 0x00001fc3, 0x00001fcc, 0x00001fcc,
+ 0x00001fd0, 0x00001fd8, 0x00001fd8,
+ 0x00001fd1, 0x00001fd9, 0x00001fd9,
+ 0x00001fe0, 0x00001fe8, 0x00001fe8,
+ 0x00001fe1, 0x00001fe9, 0x00001fe9,
+ 0x00001fe5, 0x00001fec, 0x00001fec,
+ 0x00001ff3, 0x00001ffc, 0x00001ffc,
+ 0x00002170, 0x00002160, 0x00002160,
+ 0x00002171, 0x00002161, 0x00002161,
+ 0x00002172, 0x00002162, 0x00002162,
+ 0x00002173, 0x00002163, 0x00002163,
+ 0x00002174, 0x00002164, 0x00002164,
+ 0x00002175, 0x00002165, 0x00002165,
+ 0x00002176, 0x00002166, 0x00002166,
+ 0x00002177, 0x00002167, 0x00002167,
+ 0x00002178, 0x00002168, 0x00002168,
+ 0x00002179, 0x00002169, 0x00002169,
+ 0x0000217a, 0x0000216a, 0x0000216a,
+ 0x0000217b, 0x0000216b, 0x0000216b,
+ 0x0000217c, 0x0000216c, 0x0000216c,
+ 0x0000217d, 0x0000216d, 0x0000216d,
+ 0x0000217e, 0x0000216e, 0x0000216e,
+ 0x0000217f, 0x0000216f, 0x0000216f,
+ 0x000024d0, 0x000024b6, 0x000024b6,
+ 0x000024d1, 0x000024b7, 0x000024b7,
+ 0x000024d2, 0x000024b8, 0x000024b8,
+ 0x000024d3, 0x000024b9, 0x000024b9,
+ 0x000024d4, 0x000024ba, 0x000024ba,
+ 0x000024d5, 0x000024bb, 0x000024bb,
+ 0x000024d6, 0x000024bc, 0x000024bc,
+ 0x000024d7, 0x000024bd, 0x000024bd,
+ 0x000024d8, 0x000024be, 0x000024be,
+ 0x000024d9, 0x000024bf, 0x000024bf,
+ 0x000024da, 0x000024c0, 0x000024c0,
+ 0x000024db, 0x000024c1, 0x000024c1,
+ 0x000024dc, 0x000024c2, 0x000024c2,
+ 0x000024dd, 0x000024c3, 0x000024c3,
+ 0x000024de, 0x000024c4, 0x000024c4,
+ 0x000024df, 0x000024c5, 0x000024c5,
+ 0x000024e0, 0x000024c6, 0x000024c6,
+ 0x000024e1, 0x000024c7, 0x000024c7,
+ 0x000024e2, 0x000024c8, 0x000024c8,
+ 0x000024e3, 0x000024c9, 0x000024c9,
+ 0x000024e4, 0x000024ca, 0x000024ca,
+ 0x000024e5, 0x000024cb, 0x000024cb,
+ 0x000024e6, 0x000024cc, 0x000024cc,
+ 0x000024e7, 0x000024cd, 0x000024cd,
+ 0x000024e8, 0x000024ce, 0x000024ce,
+ 0x000024e9, 0x000024cf, 0x000024cf,
+ 0x0000ff41, 0x0000ff21, 0x0000ff21,
+ 0x0000ff42, 0x0000ff22, 0x0000ff22,
+ 0x0000ff43, 0x0000ff23, 0x0000ff23,
+ 0x0000ff44, 0x0000ff24, 0x0000ff24,
+ 0x0000ff45, 0x0000ff25, 0x0000ff25,
+ 0x0000ff46, 0x0000ff26, 0x0000ff26,
+ 0x0000ff47, 0x0000ff27, 0x0000ff27,
+ 0x0000ff48, 0x0000ff28, 0x0000ff28,
+ 0x0000ff49, 0x0000ff29, 0x0000ff29,
+ 0x0000ff4a, 0x0000ff2a, 0x0000ff2a,
+ 0x0000ff4b, 0x0000ff2b, 0x0000ff2b,
+ 0x0000ff4c, 0x0000ff2c, 0x0000ff2c,
+ 0x0000ff4d, 0x0000ff2d, 0x0000ff2d,
+ 0x0000ff4e, 0x0000ff2e, 0x0000ff2e,
+ 0x0000ff4f, 0x0000ff2f, 0x0000ff2f,
+ 0x0000ff50, 0x0000ff30, 0x0000ff30,
+ 0x0000ff51, 0x0000ff31, 0x0000ff31,
+ 0x0000ff52, 0x0000ff32, 0x0000ff32,
+ 0x0000ff53, 0x0000ff33, 0x0000ff33,
+ 0x0000ff54, 0x0000ff34, 0x0000ff34,
+ 0x0000ff55, 0x0000ff35, 0x0000ff35,
+ 0x0000ff56, 0x0000ff36, 0x0000ff36,
+ 0x0000ff57, 0x0000ff37, 0x0000ff37,
+ 0x0000ff58, 0x0000ff38, 0x0000ff38,
+ 0x0000ff59, 0x0000ff39, 0x0000ff39,
+ 0x0000ff5a, 0x0000ff3a, 0x0000ff3a,
+ 0x00010428, 0x00010400, 0x00010400,
+ 0x00010429, 0x00010401, 0x00010401,
+ 0x0001042a, 0x00010402, 0x00010402,
+ 0x0001042b, 0x00010403, 0x00010403,
+ 0x0001042c, 0x00010404, 0x00010404,
+ 0x0001042d, 0x00010405, 0x00010405,
+ 0x0001042e, 0x00010406, 0x00010406,
+ 0x0001042f, 0x00010407, 0x00010407,
+ 0x00010430, 0x00010408, 0x00010408,
+ 0x00010431, 0x00010409, 0x00010409,
+ 0x00010432, 0x0001040a, 0x0001040a,
+ 0x00010433, 0x0001040b, 0x0001040b,
+ 0x00010434, 0x0001040c, 0x0001040c,
+ 0x00010435, 0x0001040d, 0x0001040d,
+ 0x00010436, 0x0001040e, 0x0001040e,
+ 0x00010437, 0x0001040f, 0x0001040f,
+ 0x00010438, 0x00010410, 0x00010410,
+ 0x00010439, 0x00010411, 0x00010411,
+ 0x0001043a, 0x00010412, 0x00010412,
+ 0x0001043b, 0x00010413, 0x00010413,
+ 0x0001043c, 0x00010414, 0x00010414,
+ 0x0001043d, 0x00010415, 0x00010415,
+ 0x0001043e, 0x00010416, 0x00010416,
+ 0x0001043f, 0x00010417, 0x00010417,
+ 0x00010440, 0x00010418, 0x00010418,
+ 0x00010441, 0x00010419, 0x00010419,
+ 0x00010442, 0x0001041a, 0x0001041a,
+ 0x00010443, 0x0001041b, 0x0001041b,
+ 0x00010444, 0x0001041c, 0x0001041c,
+ 0x00010445, 0x0001041d, 0x0001041d,
+ 0x00010446, 0x0001041e, 0x0001041e,
+ 0x00010447, 0x0001041f, 0x0001041f,
+ 0x00010448, 0x00010420, 0x00010420,
+ 0x00010449, 0x00010421, 0x00010421,
+ 0x0001044a, 0x00010422, 0x00010422,
+ 0x0001044b, 0x00010423, 0x00010423,
+ 0x0001044c, 0x00010424, 0x00010424,
+ 0x0001044d, 0x00010425, 0x00010425,
+ 0x000001c5, 0x000001c4, 0x000001c6,
+ 0x000001c8, 0x000001c7, 0x000001c9,
+ 0x000001cb, 0x000001ca, 0x000001cc,
+ 0x000001f2, 0x000001f1, 0x000001f3,
+ 0x00001f88, 0x00001f88, 0x00001f80,
+ 0x00001f89, 0x00001f89, 0x00001f81,
+ 0x00001f8a, 0x00001f8a, 0x00001f82,
+ 0x00001f8b, 0x00001f8b, 0x00001f83,
+ 0x00001f8c, 0x00001f8c, 0x00001f84,
+ 0x00001f8d, 0x00001f8d, 0x00001f85,
+ 0x00001f8e, 0x00001f8e, 0x00001f86,
+ 0x00001f8f, 0x00001f8f, 0x00001f87,
+ 0x00001f98, 0x00001f98, 0x00001f90,
+ 0x00001f99, 0x00001f99, 0x00001f91,
+ 0x00001f9a, 0x00001f9a, 0x00001f92,
+ 0x00001f9b, 0x00001f9b, 0x00001f93,
+ 0x00001f9c, 0x00001f9c, 0x00001f94,
+ 0x00001f9d, 0x00001f9d, 0x00001f95,
+ 0x00001f9e, 0x00001f9e, 0x00001f96,
+ 0x00001f9f, 0x00001f9f, 0x00001f97,
+ 0x00001fa8, 0x00001fa8, 0x00001fa0,
+ 0x00001fa9, 0x00001fa9, 0x00001fa1,
+ 0x00001faa, 0x00001faa, 0x00001fa2,
+ 0x00001fab, 0x00001fab, 0x00001fa3,
+ 0x00001fac, 0x00001fac, 0x00001fa4,
+ 0x00001fad, 0x00001fad, 0x00001fa5,
+ 0x00001fae, 0x00001fae, 0x00001fa6,
+ 0x00001faf, 0x00001faf, 0x00001fa7,
+ 0x00001fbc, 0x00001fbc, 0x00001fb3,
+ 0x00001fcc, 0x00001fcc, 0x00001fc3,
+ 0x00001ffc, 0x00001ffc, 0x00001ff3
+};
+
+static const ac_uint4 _uccomp_size = 3684;
+
+static const ac_uint4 _uccomp_data[] = {
+ 0x0000226e, 0x00000002, 0x0000003c, 0x00000338,
+ 0x00002260, 0x00000002, 0x0000003d, 0x00000338,
+ 0x0000226f, 0x00000002, 0x0000003e, 0x00000338,
+ 0x000000c0, 0x00000002, 0x00000041, 0x00000300,
+ 0x000000c1, 0x00000002, 0x00000041, 0x00000301,
+ 0x000000c2, 0x00000002, 0x00000041, 0x00000302,
+ 0x000000c3, 0x00000002, 0x00000041, 0x00000303,
+ 0x00000100, 0x00000002, 0x00000041, 0x00000304,
+ 0x00000102, 0x00000002, 0x00000041, 0x00000306,
+ 0x00000226, 0x00000002, 0x00000041, 0x00000307,
+ 0x000000c4, 0x00000002, 0x00000041, 0x00000308,
+ 0x00001ea2, 0x00000002, 0x00000041, 0x00000309,
+ 0x000000c5, 0x00000002, 0x00000041, 0x0000030a,
+ 0x000001cd, 0x00000002, 0x00000041, 0x0000030c,
+ 0x00000200, 0x00000002, 0x00000041, 0x0000030f,
+ 0x00000202, 0x00000002, 0x00000041, 0x00000311,
+ 0x00001ea0, 0x00000002, 0x00000041, 0x00000323,
+ 0x00001e00, 0x00000002, 0x00000041, 0x00000325,
+ 0x00000104, 0x00000002, 0x00000041, 0x00000328,
+ 0x00001e02, 0x00000002, 0x00000042, 0x00000307,
+ 0x00001e04, 0x00000002, 0x00000042, 0x00000323,
+ 0x00001e06, 0x00000002, 0x00000042, 0x00000331,
+ 0x00000106, 0x00000002, 0x00000043, 0x00000301,
+ 0x00000108, 0x00000002, 0x00000043, 0x00000302,
+ 0x0000010a, 0x00000002, 0x00000043, 0x00000307,
+ 0x0000010c, 0x00000002, 0x00000043, 0x0000030c,
+ 0x000000c7, 0x00000002, 0x00000043, 0x00000327,
+ 0x00001e0a, 0x00000002, 0x00000044, 0x00000307,
+ 0x0000010e, 0x00000002, 0x00000044, 0x0000030c,
+ 0x00001e0c, 0x00000002, 0x00000044, 0x00000323,
+ 0x00001e10, 0x00000002, 0x00000044, 0x00000327,
+ 0x00001e12, 0x00000002, 0x00000044, 0x0000032d,
+ 0x00001e0e, 0x00000002, 0x00000044, 0x00000331,
+ 0x000000c8, 0x00000002, 0x00000045, 0x00000300,
+ 0x000000c9, 0x00000002, 0x00000045, 0x00000301,
+ 0x000000ca, 0x00000002, 0x00000045, 0x00000302,
+ 0x00001ebc, 0x00000002, 0x00000045, 0x00000303,
+ 0x00000112, 0x00000002, 0x00000045, 0x00000304,
+ 0x00000114, 0x00000002, 0x00000045, 0x00000306,
+ 0x00000116, 0x00000002, 0x00000045, 0x00000307,
+ 0x000000cb, 0x00000002, 0x00000045, 0x00000308,
+ 0x00001eba, 0x00000002, 0x00000045, 0x00000309,
+ 0x0000011a, 0x00000002, 0x00000045, 0x0000030c,
+ 0x00000204, 0x00000002, 0x00000045, 0x0000030f,
+ 0x00000206, 0x00000002, 0x00000045, 0x00000311,
+ 0x00001eb8, 0x00000002, 0x00000045, 0x00000323,
+ 0x00000228, 0x00000002, 0x00000045, 0x00000327,
+ 0x00000118, 0x00000002, 0x00000045, 0x00000328,
+ 0x00001e18, 0x00000002, 0x00000045, 0x0000032d,
+ 0x00001e1a, 0x00000002, 0x00000045, 0x00000330,
+ 0x00001e1e, 0x00000002, 0x00000046, 0x00000307,
+ 0x000001f4, 0x00000002, 0x00000047, 0x00000301,
+ 0x0000011c, 0x00000002, 0x00000047, 0x00000302,
+ 0x00001e20, 0x00000002, 0x00000047, 0x00000304,
+ 0x0000011e, 0x00000002, 0x00000047, 0x00000306,
+ 0x00000120, 0x00000002, 0x00000047, 0x00000307,
+ 0x000001e6, 0x00000002, 0x00000047, 0x0000030c,
+ 0x00000122, 0x00000002, 0x00000047, 0x00000327,
+ 0x00000124, 0x00000002, 0x00000048, 0x00000302,
+ 0x00001e22, 0x00000002, 0x00000048, 0x00000307,
+ 0x00001e26, 0x00000002, 0x00000048, 0x00000308,
+ 0x0000021e, 0x00000002, 0x00000048, 0x0000030c,
+ 0x00001e24, 0x00000002, 0x00000048, 0x00000323,
+ 0x00001e28, 0x00000002, 0x00000048, 0x00000327,
+ 0x00001e2a, 0x00000002, 0x00000048, 0x0000032e,
+ 0x000000cc, 0x00000002, 0x00000049, 0x00000300,
+ 0x000000cd, 0x00000002, 0x00000049, 0x00000301,
+ 0x000000ce, 0x00000002, 0x00000049, 0x00000302,
+ 0x00000128, 0x00000002, 0x00000049, 0x00000303,
+ 0x0000012a, 0x00000002, 0x00000049, 0x00000304,
+ 0x0000012c, 0x00000002, 0x00000049, 0x00000306,
+ 0x00000130, 0x00000002, 0x00000049, 0x00000307,
+ 0x000000cf, 0x00000002, 0x00000049, 0x00000308,
+ 0x00001ec8, 0x00000002, 0x00000049, 0x00000309,
+ 0x000001cf, 0x00000002, 0x00000049, 0x0000030c,
+ 0x00000208, 0x00000002, 0x00000049, 0x0000030f,
+ 0x0000020a, 0x00000002, 0x00000049, 0x00000311,
+ 0x00001eca, 0x00000002, 0x00000049, 0x00000323,
+ 0x0000012e, 0x00000002, 0x00000049, 0x00000328,
+ 0x00001e2c, 0x00000002, 0x00000049, 0x00000330,
+ 0x00000134, 0x00000002, 0x0000004a, 0x00000302,
+ 0x00001e30, 0x00000002, 0x0000004b, 0x00000301,
+ 0x000001e8, 0x00000002, 0x0000004b, 0x0000030c,
+ 0x00001e32, 0x00000002, 0x0000004b, 0x00000323,
+ 0x00000136, 0x00000002, 0x0000004b, 0x00000327,
+ 0x00001e34, 0x00000002, 0x0000004b, 0x00000331,
+ 0x00000139, 0x00000002, 0x0000004c, 0x00000301,
+ 0x0000013d, 0x00000002, 0x0000004c, 0x0000030c,
+ 0x00001e36, 0x00000002, 0x0000004c, 0x00000323,
+ 0x0000013b, 0x00000002, 0x0000004c, 0x00000327,
+ 0x00001e3c, 0x00000002, 0x0000004c, 0x0000032d,
+ 0x00001e3a, 0x00000002, 0x0000004c, 0x00000331,
+ 0x00001e3e, 0x00000002, 0x0000004d, 0x00000301,
+ 0x00001e40, 0x00000002, 0x0000004d, 0x00000307,
+ 0x00001e42, 0x00000002, 0x0000004d, 0x00000323,
+ 0x000001f8, 0x00000002, 0x0000004e, 0x00000300,
+ 0x00000143, 0x00000002, 0x0000004e, 0x00000301,
+ 0x000000d1, 0x00000002, 0x0000004e, 0x00000303,
+ 0x00001e44, 0x00000002, 0x0000004e, 0x00000307,
+ 0x00000147, 0x00000002, 0x0000004e, 0x0000030c,
+ 0x00001e46, 0x00000002, 0x0000004e, 0x00000323,
+ 0x00000145, 0x00000002, 0x0000004e, 0x00000327,
+ 0x00001e4a, 0x00000002, 0x0000004e, 0x0000032d,
+ 0x00001e48, 0x00000002, 0x0000004e, 0x00000331,
+ 0x000000d2, 0x00000002, 0x0000004f, 0x00000300,
+ 0x000000d3, 0x00000002, 0x0000004f, 0x00000301,
+ 0x000000d4, 0x00000002, 0x0000004f, 0x00000302,
+ 0x000000d5, 0x00000002, 0x0000004f, 0x00000303,
+ 0x0000014c, 0x00000002, 0x0000004f, 0x00000304,
+ 0x0000014e, 0x00000002, 0x0000004f, 0x00000306,
+ 0x0000022e, 0x00000002, 0x0000004f, 0x00000307,
+ 0x000000d6, 0x00000002, 0x0000004f, 0x00000308,
+ 0x00001ece, 0x00000002, 0x0000004f, 0x00000309,
+ 0x00000150, 0x00000002, 0x0000004f, 0x0000030b,
+ 0x000001d1, 0x00000002, 0x0000004f, 0x0000030c,
+ 0x0000020c, 0x00000002, 0x0000004f, 0x0000030f,
+ 0x0000020e, 0x00000002, 0x0000004f, 0x00000311,
+ 0x000001a0, 0x00000002, 0x0000004f, 0x0000031b,
+ 0x00001ecc, 0x00000002, 0x0000004f, 0x00000323,
+ 0x000001ea, 0x00000002, 0x0000004f, 0x00000328,
+ 0x00001e54, 0x00000002, 0x00000050, 0x00000301,
+ 0x00001e56, 0x00000002, 0x00000050, 0x00000307,
+ 0x00000154, 0x00000002, 0x00000052, 0x00000301,
+ 0x00001e58, 0x00000002, 0x00000052, 0x00000307,
+ 0x00000158, 0x00000002, 0x00000052, 0x0000030c,
+ 0x00000210, 0x00000002, 0x00000052, 0x0000030f,
+ 0x00000212, 0x00000002, 0x00000052, 0x00000311,
+ 0x00001e5a, 0x00000002, 0x00000052, 0x00000323,
+ 0x00000156, 0x00000002, 0x00000052, 0x00000327,
+ 0x00001e5e, 0x00000002, 0x00000052, 0x00000331,
+ 0x0000015a, 0x00000002, 0x00000053, 0x00000301,
+ 0x0000015c, 0x00000002, 0x00000053, 0x00000302,
+ 0x00001e60, 0x00000002, 0x00000053, 0x00000307,
+ 0x00000160, 0x00000002, 0x00000053, 0x0000030c,
+ 0x00001e62, 0x00000002, 0x00000053, 0x00000323,
+ 0x00000218, 0x00000002, 0x00000053, 0x00000326,
+ 0x0000015e, 0x00000002, 0x00000053, 0x00000327,
+ 0x00001e6a, 0x00000002, 0x00000054, 0x00000307,
+ 0x00000164, 0x00000002, 0x00000054, 0x0000030c,
+ 0x00001e6c, 0x00000002, 0x00000054, 0x00000323,
+ 0x0000021a, 0x00000002, 0x00000054, 0x00000326,
+ 0x00000162, 0x00000002, 0x00000054, 0x00000327,
+ 0x00001e70, 0x00000002, 0x00000054, 0x0000032d,
+ 0x00001e6e, 0x00000002, 0x00000054, 0x00000331,
+ 0x000000d9, 0x00000002, 0x00000055, 0x00000300,
+ 0x000000da, 0x00000002, 0x00000055, 0x00000301,
+ 0x000000db, 0x00000002, 0x00000055, 0x00000302,
+ 0x00000168, 0x00000002, 0x00000055, 0x00000303,
+ 0x0000016a, 0x00000002, 0x00000055, 0x00000304,
+ 0x0000016c, 0x00000002, 0x00000055, 0x00000306,
+ 0x000000dc, 0x00000002, 0x00000055, 0x00000308,
+ 0x00001ee6, 0x00000002, 0x00000055, 0x00000309,
+ 0x0000016e, 0x00000002, 0x00000055, 0x0000030a,
+ 0x00000170, 0x00000002, 0x00000055, 0x0000030b,
+ 0x000001d3, 0x00000002, 0x00000055, 0x0000030c,
+ 0x00000214, 0x00000002, 0x00000055, 0x0000030f,
+ 0x00000216, 0x00000002, 0x00000055, 0x00000311,
+ 0x000001af, 0x00000002, 0x00000055, 0x0000031b,
+ 0x00001ee4, 0x00000002, 0x00000055, 0x00000323,
+ 0x00001e72, 0x00000002, 0x00000055, 0x00000324,
+ 0x00000172, 0x00000002, 0x00000055, 0x00000328,
+ 0x00001e76, 0x00000002, 0x00000055, 0x0000032d,
+ 0x00001e74, 0x00000002, 0x00000055, 0x00000330,
+ 0x00001e7c, 0x00000002, 0x00000056, 0x00000303,
+ 0x00001e7e, 0x00000002, 0x00000056, 0x00000323,
+ 0x00001e80, 0x00000002, 0x00000057, 0x00000300,
+ 0x00001e82, 0x00000002, 0x00000057, 0x00000301,
+ 0x00000174, 0x00000002, 0x00000057, 0x00000302,
+ 0x00001e86, 0x00000002, 0x00000057, 0x00000307,
+ 0x00001e84, 0x00000002, 0x00000057, 0x00000308,
+ 0x00001e88, 0x00000002, 0x00000057, 0x00000323,
+ 0x00001e8a, 0x00000002, 0x00000058, 0x00000307,
+ 0x00001e8c, 0x00000002, 0x00000058, 0x00000308,
+ 0x00001ef2, 0x00000002, 0x00000059, 0x00000300,
+ 0x000000dd, 0x00000002, 0x00000059, 0x00000301,
+ 0x00000176, 0x00000002, 0x00000059, 0x00000302,
+ 0x00001ef8, 0x00000002, 0x00000059, 0x00000303,
+ 0x00000232, 0x00000002, 0x00000059, 0x00000304,
+ 0x00001e8e, 0x00000002, 0x00000059, 0x00000307,
+ 0x00000178, 0x00000002, 0x00000059, 0x00000308,
+ 0x00001ef6, 0x00000002, 0x00000059, 0x00000309,
+ 0x00001ef4, 0x00000002, 0x00000059, 0x00000323,
+ 0x00000179, 0x00000002, 0x0000005a, 0x00000301,
+ 0x00001e90, 0x00000002, 0x0000005a, 0x00000302,
+ 0x0000017b, 0x00000002, 0x0000005a, 0x00000307,
+ 0x0000017d, 0x00000002, 0x0000005a, 0x0000030c,
+ 0x00001e92, 0x00000002, 0x0000005a, 0x00000323,
+ 0x00001e94, 0x00000002, 0x0000005a, 0x00000331,
+ 0x000000e0, 0x00000002, 0x00000061, 0x00000300,
+ 0x000000e1, 0x00000002, 0x00000061, 0x00000301,
+ 0x000000e2, 0x00000002, 0x00000061, 0x00000302,
+ 0x000000e3, 0x00000002, 0x00000061, 0x00000303,
+ 0x00000101, 0x00000002, 0x00000061, 0x00000304,
+ 0x00000103, 0x00000002, 0x00000061, 0x00000306,
+ 0x00000227, 0x00000002, 0x00000061, 0x00000307,
+ 0x000000e4, 0x00000002, 0x00000061, 0x00000308,
+ 0x00001ea3, 0x00000002, 0x00000061, 0x00000309,
+ 0x000000e5, 0x00000002, 0x00000061, 0x0000030a,
+ 0x000001ce, 0x00000002, 0x00000061, 0x0000030c,
+ 0x00000201, 0x00000002, 0x00000061, 0x0000030f,
+ 0x00000203, 0x00000002, 0x00000061, 0x00000311,
+ 0x00001ea1, 0x00000002, 0x00000061, 0x00000323,
+ 0x00001e01, 0x00000002, 0x00000061, 0x00000325,
+ 0x00000105, 0x00000002, 0x00000061, 0x00000328,
+ 0x00001e03, 0x00000002, 0x00000062, 0x00000307,
+ 0x00001e05, 0x00000002, 0x00000062, 0x00000323,
+ 0x00001e07, 0x00000002, 0x00000062, 0x00000331,
+ 0x00000107, 0x00000002, 0x00000063, 0x00000301,
+ 0x00000109, 0x00000002, 0x00000063, 0x00000302,
+ 0x0000010b, 0x00000002, 0x00000063, 0x00000307,
+ 0x0000010d, 0x00000002, 0x00000063, 0x0000030c,
+ 0x000000e7, 0x00000002, 0x00000063, 0x00000327,
+ 0x00001e0b, 0x00000002, 0x00000064, 0x00000307,
+ 0x0000010f, 0x00000002, 0x00000064, 0x0000030c,
+ 0x00001e0d, 0x00000002, 0x00000064, 0x00000323,
+ 0x00001e11, 0x00000002, 0x00000064, 0x00000327,
+ 0x00001e13, 0x00000002, 0x00000064, 0x0000032d,
+ 0x00001e0f, 0x00000002, 0x00000064, 0x00000331,
+ 0x000000e8, 0x00000002, 0x00000065, 0x00000300,
+ 0x000000e9, 0x00000002, 0x00000065, 0x00000301,
+ 0x000000ea, 0x00000002, 0x00000065, 0x00000302,
+ 0x00001ebd, 0x00000002, 0x00000065, 0x00000303,
+ 0x00000113, 0x00000002, 0x00000065, 0x00000304,
+ 0x00000115, 0x00000002, 0x00000065, 0x00000306,
+ 0x00000117, 0x00000002, 0x00000065, 0x00000307,
+ 0x000000eb, 0x00000002, 0x00000065, 0x00000308,
+ 0x00001ebb, 0x00000002, 0x00000065, 0x00000309,
+ 0x0000011b, 0x00000002, 0x00000065, 0x0000030c,
+ 0x00000205, 0x00000002, 0x00000065, 0x0000030f,
+ 0x00000207, 0x00000002, 0x00000065, 0x00000311,
+ 0x00001eb9, 0x00000002, 0x00000065, 0x00000323,
+ 0x00000229, 0x00000002, 0x00000065, 0x00000327,
+ 0x00000119, 0x00000002, 0x00000065, 0x00000328,
+ 0x00001e19, 0x00000002, 0x00000065, 0x0000032d,
+ 0x00001e1b, 0x00000002, 0x00000065, 0x00000330,
+ 0x00001e1f, 0x00000002, 0x00000066, 0x00000307,
+ 0x000001f5, 0x00000002, 0x00000067, 0x00000301,
+ 0x0000011d, 0x00000002, 0x00000067, 0x00000302,
+ 0x00001e21, 0x00000002, 0x00000067, 0x00000304,
+ 0x0000011f, 0x00000002, 0x00000067, 0x00000306,
+ 0x00000121, 0x00000002, 0x00000067, 0x00000307,
+ 0x000001e7, 0x00000002, 0x00000067, 0x0000030c,
+ 0x00000123, 0x00000002, 0x00000067, 0x00000327,
+ 0x00000125, 0x00000002, 0x00000068, 0x00000302,
+ 0x00001e23, 0x00000002, 0x00000068, 0x00000307,
+ 0x00001e27, 0x00000002, 0x00000068, 0x00000308,
+ 0x0000021f, 0x00000002, 0x00000068, 0x0000030c,
+ 0x00001e25, 0x00000002, 0x00000068, 0x00000323,
+ 0x00001e29, 0x00000002, 0x00000068, 0x00000327,
+ 0x00001e2b, 0x00000002, 0x00000068, 0x0000032e,
+ 0x00001e96, 0x00000002, 0x00000068, 0x00000331,
+ 0x000000ec, 0x00000002, 0x00000069, 0x00000300,
+ 0x000000ed, 0x00000002, 0x00000069, 0x00000301,
+ 0x000000ee, 0x00000002, 0x00000069, 0x00000302,
+ 0x00000129, 0x00000002, 0x00000069, 0x00000303,
+ 0x0000012b, 0x00000002, 0x00000069, 0x00000304,
+ 0x0000012d, 0x00000002, 0x00000069, 0x00000306,
+ 0x000000ef, 0x00000002, 0x00000069, 0x00000308,
+ 0x00001ec9, 0x00000002, 0x00000069, 0x00000309,
+ 0x000001d0, 0x00000002, 0x00000069, 0x0000030c,
+ 0x00000209, 0x00000002, 0x00000069, 0x0000030f,
+ 0x0000020b, 0x00000002, 0x00000069, 0x00000311,
+ 0x00001ecb, 0x00000002, 0x00000069, 0x00000323,
+ 0x0000012f, 0x00000002, 0x00000069, 0x00000328,
+ 0x00001e2d, 0x00000002, 0x00000069, 0x00000330,
+ 0x00000135, 0x00000002, 0x0000006a, 0x00000302,
+ 0x000001f0, 0x00000002, 0x0000006a, 0x0000030c,
+ 0x00001e31, 0x00000002, 0x0000006b, 0x00000301,
+ 0x000001e9, 0x00000002, 0x0000006b, 0x0000030c,
+ 0x00001e33, 0x00000002, 0x0000006b, 0x00000323,
+ 0x00000137, 0x00000002, 0x0000006b, 0x00000327,
+ 0x00001e35, 0x00000002, 0x0000006b, 0x00000331,
+ 0x0000013a, 0x00000002, 0x0000006c, 0x00000301,
+ 0x0000013e, 0x00000002, 0x0000006c, 0x0000030c,
+ 0x00001e37, 0x00000002, 0x0000006c, 0x00000323,
+ 0x0000013c, 0x00000002, 0x0000006c, 0x00000327,
+ 0x00001e3d, 0x00000002, 0x0000006c, 0x0000032d,
+ 0x00001e3b, 0x00000002, 0x0000006c, 0x00000331,
+ 0x00001e3f, 0x00000002, 0x0000006d, 0x00000301,
+ 0x00001e41, 0x00000002, 0x0000006d, 0x00000307,
+ 0x00001e43, 0x00000002, 0x0000006d, 0x00000323,
+ 0x000001f9, 0x00000002, 0x0000006e, 0x00000300,
+ 0x00000144, 0x00000002, 0x0000006e, 0x00000301,
+ 0x000000f1, 0x00000002, 0x0000006e, 0x00000303,
+ 0x00001e45, 0x00000002, 0x0000006e, 0x00000307,
+ 0x00000148, 0x00000002, 0x0000006e, 0x0000030c,
+ 0x00001e47, 0x00000002, 0x0000006e, 0x00000323,
+ 0x00000146, 0x00000002, 0x0000006e, 0x00000327,
+ 0x00001e4b, 0x00000002, 0x0000006e, 0x0000032d,
+ 0x00001e49, 0x00000002, 0x0000006e, 0x00000331,
+ 0x000000f2, 0x00000002, 0x0000006f, 0x00000300,
+ 0x000000f3, 0x00000002, 0x0000006f, 0x00000301,
+ 0x000000f4, 0x00000002, 0x0000006f, 0x00000302,
+ 0x000000f5, 0x00000002, 0x0000006f, 0x00000303,
+ 0x0000014d, 0x00000002, 0x0000006f, 0x00000304,
+ 0x0000014f, 0x00000002, 0x0000006f, 0x00000306,
+ 0x0000022f, 0x00000002, 0x0000006f, 0x00000307,
+ 0x000000f6, 0x00000002, 0x0000006f, 0x00000308,
+ 0x00001ecf, 0x00000002, 0x0000006f, 0x00000309,
+ 0x00000151, 0x00000002, 0x0000006f, 0x0000030b,
+ 0x000001d2, 0x00000002, 0x0000006f, 0x0000030c,
+ 0x0000020d, 0x00000002, 0x0000006f, 0x0000030f,
+ 0x0000020f, 0x00000002, 0x0000006f, 0x00000311,
+ 0x000001a1, 0x00000002, 0x0000006f, 0x0000031b,
+ 0x00001ecd, 0x00000002, 0x0000006f, 0x00000323,
+ 0x000001eb, 0x00000002, 0x0000006f, 0x00000328,
+ 0x00001e55, 0x00000002, 0x00000070, 0x00000301,
+ 0x00001e57, 0x00000002, 0x00000070, 0x00000307,
+ 0x00000155, 0x00000002, 0x00000072, 0x00000301,
+ 0x00001e59, 0x00000002, 0x00000072, 0x00000307,
+ 0x00000159, 0x00000002, 0x00000072, 0x0000030c,
+ 0x00000211, 0x00000002, 0x00000072, 0x0000030f,
+ 0x00000213, 0x00000002, 0x00000072, 0x00000311,
+ 0x00001e5b, 0x00000002, 0x00000072, 0x00000323,
+ 0x00000157, 0x00000002, 0x00000072, 0x00000327,
+ 0x00001e5f, 0x00000002, 0x00000072, 0x00000331,
+ 0x0000015b, 0x00000002, 0x00000073, 0x00000301,
+ 0x0000015d, 0x00000002, 0x00000073, 0x00000302,
+ 0x00001e61, 0x00000002, 0x00000073, 0x00000307,
+ 0x00000161, 0x00000002, 0x00000073, 0x0000030c,
+ 0x00001e63, 0x00000002, 0x00000073, 0x00000323,
+ 0x00000219, 0x00000002, 0x00000073, 0x00000326,
+ 0x0000015f, 0x00000002, 0x00000073, 0x00000327,
+ 0x00001e6b, 0x00000002, 0x00000074, 0x00000307,
+ 0x00001e97, 0x00000002, 0x00000074, 0x00000308,
+ 0x00000165, 0x00000002, 0x00000074, 0x0000030c,
+ 0x00001e6d, 0x00000002, 0x00000074, 0x00000323,
+ 0x0000021b, 0x00000002, 0x00000074, 0x00000326,
+ 0x00000163, 0x00000002, 0x00000074, 0x00000327,
+ 0x00001e71, 0x00000002, 0x00000074, 0x0000032d,
+ 0x00001e6f, 0x00000002, 0x00000074, 0x00000331,
+ 0x000000f9, 0x00000002, 0x00000075, 0x00000300,
+ 0x000000fa, 0x00000002, 0x00000075, 0x00000301,
+ 0x000000fb, 0x00000002, 0x00000075, 0x00000302,
+ 0x00000169, 0x00000002, 0x00000075, 0x00000303,
+ 0x0000016b, 0x00000002, 0x00000075, 0x00000304,
+ 0x0000016d, 0x00000002, 0x00000075, 0x00000306,
+ 0x000000fc, 0x00000002, 0x00000075, 0x00000308,
+ 0x00001ee7, 0x00000002, 0x00000075, 0x00000309,
+ 0x0000016f, 0x00000002, 0x00000075, 0x0000030a,
+ 0x00000171, 0x00000002, 0x00000075, 0x0000030b,
+ 0x000001d4, 0x00000002, 0x00000075, 0x0000030c,
+ 0x00000215, 0x00000002, 0x00000075, 0x0000030f,
+ 0x00000217, 0x00000002, 0x00000075, 0x00000311,
+ 0x000001b0, 0x00000002, 0x00000075, 0x0000031b,
+ 0x00001ee5, 0x00000002, 0x00000075, 0x00000323,
+ 0x00001e73, 0x00000002, 0x00000075, 0x00000324,
+ 0x00000173, 0x00000002, 0x00000075, 0x00000328,
+ 0x00001e77, 0x00000002, 0x00000075, 0x0000032d,
+ 0x00001e75, 0x00000002, 0x00000075, 0x00000330,
+ 0x00001e7d, 0x00000002, 0x00000076, 0x00000303,
+ 0x00001e7f, 0x00000002, 0x00000076, 0x00000323,
+ 0x00001e81, 0x00000002, 0x00000077, 0x00000300,
+ 0x00001e83, 0x00000002, 0x00000077, 0x00000301,
+ 0x00000175, 0x00000002, 0x00000077, 0x00000302,
+ 0x00001e87, 0x00000002, 0x00000077, 0x00000307,
+ 0x00001e85, 0x00000002, 0x00000077, 0x00000308,
+ 0x00001e98, 0x00000002, 0x00000077, 0x0000030a,
+ 0x00001e89, 0x00000002, 0x00000077, 0x00000323,
+ 0x00001e8b, 0x00000002, 0x00000078, 0x00000307,
+ 0x00001e8d, 0x00000002, 0x00000078, 0x00000308,
+ 0x00001ef3, 0x00000002, 0x00000079, 0x00000300,
+ 0x000000fd, 0x00000002, 0x00000079, 0x00000301,
+ 0x00000177, 0x00000002, 0x00000079, 0x00000302,
+ 0x00001ef9, 0x00000002, 0x00000079, 0x00000303,
+ 0x00000233, 0x00000002, 0x00000079, 0x00000304,
+ 0x00001e8f, 0x00000002, 0x00000079, 0x00000307,
+ 0x000000ff, 0x00000002, 0x00000079, 0x00000308,
+ 0x00001ef7, 0x00000002, 0x00000079, 0x00000309,
+ 0x00001e99, 0x00000002, 0x00000079, 0x0000030a,
+ 0x00001ef5, 0x00000002, 0x00000079, 0x00000323,
+ 0x0000017a, 0x00000002, 0x0000007a, 0x00000301,
+ 0x00001e91, 0x00000002, 0x0000007a, 0x00000302,
+ 0x0000017c, 0x00000002, 0x0000007a, 0x00000307,
+ 0x0000017e, 0x00000002, 0x0000007a, 0x0000030c,
+ 0x00001e93, 0x00000002, 0x0000007a, 0x00000323,
+ 0x00001e95, 0x00000002, 0x0000007a, 0x00000331,
+ 0x00001fed, 0x00000002, 0x000000a8, 0x00000300,
+ 0x00000385, 0x00000002, 0x000000a8, 0x00000301,
+ 0x00001fc1, 0x00000002, 0x000000a8, 0x00000342,
+ 0x00001ea6, 0x00000002, 0x000000c2, 0x00000300,
+ 0x00001ea4, 0x00000002, 0x000000c2, 0x00000301,
+ 0x00001eaa, 0x00000002, 0x000000c2, 0x00000303,
+ 0x00001ea8, 0x00000002, 0x000000c2, 0x00000309,
+ 0x000001de, 0x00000002, 0x000000c4, 0x00000304,
+ 0x000001fa, 0x00000002, 0x000000c5, 0x00000301,
+ 0x000001fc, 0x00000002, 0x000000c6, 0x00000301,
+ 0x000001e2, 0x00000002, 0x000000c6, 0x00000304,
+ 0x00001e08, 0x00000002, 0x000000c7, 0x00000301,
+ 0x00001ec0, 0x00000002, 0x000000ca, 0x00000300,
+ 0x00001ebe, 0x00000002, 0x000000ca, 0x00000301,
+ 0x00001ec4, 0x00000002, 0x000000ca, 0x00000303,
+ 0x00001ec2, 0x00000002, 0x000000ca, 0x00000309,
+ 0x00001e2e, 0x00000002, 0x000000cf, 0x00000301,
+ 0x00001ed2, 0x00000002, 0x000000d4, 0x00000300,
+ 0x00001ed0, 0x00000002, 0x000000d4, 0x00000301,
+ 0x00001ed6, 0x00000002, 0x000000d4, 0x00000303,
+ 0x00001ed4, 0x00000002, 0x000000d4, 0x00000309,
+ 0x00001e4c, 0x00000002, 0x000000d5, 0x00000301,
+ 0x0000022c, 0x00000002, 0x000000d5, 0x00000304,
+ 0x00001e4e, 0x00000002, 0x000000d5, 0x00000308,
+ 0x0000022a, 0x00000002, 0x000000d6, 0x00000304,
+ 0x000001fe, 0x00000002, 0x000000d8, 0x00000301,
+ 0x000001db, 0x00000002, 0x000000dc, 0x00000300,
+ 0x000001d7, 0x00000002, 0x000000dc, 0x00000301,
+ 0x000001d5, 0x00000002, 0x000000dc, 0x00000304,
+ 0x000001d9, 0x00000002, 0x000000dc, 0x0000030c,
+ 0x00001ea7, 0x00000002, 0x000000e2, 0x00000300,
+ 0x00001ea5, 0x00000002, 0x000000e2, 0x00000301,
+ 0x00001eab, 0x00000002, 0x000000e2, 0x00000303,
+ 0x00001ea9, 0x00000002, 0x000000e2, 0x00000309,
+ 0x000001df, 0x00000002, 0x000000e4, 0x00000304,
+ 0x000001fb, 0x00000002, 0x000000e5, 0x00000301,
+ 0x000001fd, 0x00000002, 0x000000e6, 0x00000301,
+ 0x000001e3, 0x00000002, 0x000000e6, 0x00000304,
+ 0x00001e09, 0x00000002, 0x000000e7, 0x00000301,
+ 0x00001ec1, 0x00000002, 0x000000ea, 0x00000300,
+ 0x00001ebf, 0x00000002, 0x000000ea, 0x00000301,
+ 0x00001ec5, 0x00000002, 0x000000ea, 0x00000303,
+ 0x00001ec3, 0x00000002, 0x000000ea, 0x00000309,
+ 0x00001e2f, 0x00000002, 0x000000ef, 0x00000301,
+ 0x00001ed3, 0x00000002, 0x000000f4, 0x00000300,
+ 0x00001ed1, 0x00000002, 0x000000f4, 0x00000301,
+ 0x00001ed7, 0x00000002, 0x000000f4, 0x00000303,
+ 0x00001ed5, 0x00000002, 0x000000f4, 0x00000309,
+ 0x00001e4d, 0x00000002, 0x000000f5, 0x00000301,
+ 0x0000022d, 0x00000002, 0x000000f5, 0x00000304,
+ 0x00001e4f, 0x00000002, 0x000000f5, 0x00000308,
+ 0x0000022b, 0x00000002, 0x000000f6, 0x00000304,
+ 0x000001ff, 0x00000002, 0x000000f8, 0x00000301,
+ 0x000001dc, 0x00000002, 0x000000fc, 0x00000300,
+ 0x000001d8, 0x00000002, 0x000000fc, 0x00000301,
+ 0x000001d6, 0x00000002, 0x000000fc, 0x00000304,
+ 0x000001da, 0x00000002, 0x000000fc, 0x0000030c,
+ 0x00001eb0, 0x00000002, 0x00000102, 0x00000300,
+ 0x00001eae, 0x00000002, 0x00000102, 0x00000301,
+ 0x00001eb4, 0x00000002, 0x00000102, 0x00000303,
+ 0x00001eb2, 0x00000002, 0x00000102, 0x00000309,
+ 0x00001eb1, 0x00000002, 0x00000103, 0x00000300,
+ 0x00001eaf, 0x00000002, 0x00000103, 0x00000301,
+ 0x00001eb5, 0x00000002, 0x00000103, 0x00000303,
+ 0x00001eb3, 0x00000002, 0x00000103, 0x00000309,
+ 0x00001e14, 0x00000002, 0x00000112, 0x00000300,
+ 0x00001e16, 0x00000002, 0x00000112, 0x00000301,
+ 0x00001e15, 0x00000002, 0x00000113, 0x00000300,
+ 0x00001e17, 0x00000002, 0x00000113, 0x00000301,
+ 0x00001e50, 0x00000002, 0x0000014c, 0x00000300,
+ 0x00001e52, 0x00000002, 0x0000014c, 0x00000301,
+ 0x00001e51, 0x00000002, 0x0000014d, 0x00000300,
+ 0x00001e53, 0x00000002, 0x0000014d, 0x00000301,
+ 0x00001e64, 0x00000002, 0x0000015a, 0x00000307,
+ 0x00001e65, 0x00000002, 0x0000015b, 0x00000307,
+ 0x00001e66, 0x00000002, 0x00000160, 0x00000307,
+ 0x00001e67, 0x00000002, 0x00000161, 0x00000307,
+ 0x00001e78, 0x00000002, 0x00000168, 0x00000301,
+ 0x00001e79, 0x00000002, 0x00000169, 0x00000301,
+ 0x00001e7a, 0x00000002, 0x0000016a, 0x00000308,
+ 0x00001e7b, 0x00000002, 0x0000016b, 0x00000308,
+ 0x00001e9b, 0x00000002, 0x0000017f, 0x00000307,
+ 0x00001edc, 0x00000002, 0x000001a0, 0x00000300,
+ 0x00001eda, 0x00000002, 0x000001a0, 0x00000301,
+ 0x00001ee0, 0x00000002, 0x000001a0, 0x00000303,
+ 0x00001ede, 0x00000002, 0x000001a0, 0x00000309,
+ 0x00001ee2, 0x00000002, 0x000001a0, 0x00000323,
+ 0x00001edd, 0x00000002, 0x000001a1, 0x00000300,
+ 0x00001edb, 0x00000002, 0x000001a1, 0x00000301,
+ 0x00001ee1, 0x00000002, 0x000001a1, 0x00000303,
+ 0x00001edf, 0x00000002, 0x000001a1, 0x00000309,
+ 0x00001ee3, 0x00000002, 0x000001a1, 0x00000323,
+ 0x00001eea, 0x00000002, 0x000001af, 0x00000300,
+ 0x00001ee8, 0x00000002, 0x000001af, 0x00000301,
+ 0x00001eee, 0x00000002, 0x000001af, 0x00000303,
+ 0x00001eec, 0x00000002, 0x000001af, 0x00000309,
+ 0x00001ef0, 0x00000002, 0x000001af, 0x00000323,
+ 0x00001eeb, 0x00000002, 0x000001b0, 0x00000300,
+ 0x00001ee9, 0x00000002, 0x000001b0, 0x00000301,
+ 0x00001eef, 0x00000002, 0x000001b0, 0x00000303,
+ 0x00001eed, 0x00000002, 0x000001b0, 0x00000309,
+ 0x00001ef1, 0x00000002, 0x000001b0, 0x00000323,
+ 0x000001ee, 0x00000002, 0x000001b7, 0x0000030c,
+ 0x000001ec, 0x00000002, 0x000001ea, 0x00000304,
+ 0x000001ed, 0x00000002, 0x000001eb, 0x00000304,
+ 0x000001e0, 0x00000002, 0x00000226, 0x00000304,
+ 0x000001e1, 0x00000002, 0x00000227, 0x00000304,
+ 0x00001e1c, 0x00000002, 0x00000228, 0x00000306,
+ 0x00001e1d, 0x00000002, 0x00000229, 0x00000306,
+ 0x00000230, 0x00000002, 0x0000022e, 0x00000304,
+ 0x00000231, 0x00000002, 0x0000022f, 0x00000304,
+ 0x000001ef, 0x00000002, 0x00000292, 0x0000030c,
+ 0x00000344, 0x00000002, 0x00000308, 0x00000301,
+ 0x00001fba, 0x00000002, 0x00000391, 0x00000300,
+ 0x00000386, 0x00000002, 0x00000391, 0x00000301,
+ 0x00001fb9, 0x00000002, 0x00000391, 0x00000304,
+ 0x00001fb8, 0x00000002, 0x00000391, 0x00000306,
+ 0x00001f08, 0x00000002, 0x00000391, 0x00000313,
+ 0x00001f09, 0x00000002, 0x00000391, 0x00000314,
+ 0x00001fbc, 0x00000002, 0x00000391, 0x00000345,
+ 0x00001fc8, 0x00000002, 0x00000395, 0x00000300,
+ 0x00000388, 0x00000002, 0x00000395, 0x00000301,
+ 0x00001f18, 0x00000002, 0x00000395, 0x00000313,
+ 0x00001f19, 0x00000002, 0x00000395, 0x00000314,
+ 0x00001fca, 0x00000002, 0x00000397, 0x00000300,
+ 0x00000389, 0x00000002, 0x00000397, 0x00000301,
+ 0x00001f28, 0x00000002, 0x00000397, 0x00000313,
+ 0x00001f29, 0x00000002, 0x00000397, 0x00000314,
+ 0x00001fcc, 0x00000002, 0x00000397, 0x00000345,
+ 0x00001fda, 0x00000002, 0x00000399, 0x00000300,
+ 0x0000038a, 0x00000002, 0x00000399, 0x00000301,
+ 0x00001fd9, 0x00000002, 0x00000399, 0x00000304,
+ 0x00001fd8, 0x00000002, 0x00000399, 0x00000306,
+ 0x000003aa, 0x00000002, 0x00000399, 0x00000308,
+ 0x00001f38, 0x00000002, 0x00000399, 0x00000313,
+ 0x00001f39, 0x00000002, 0x00000399, 0x00000314,
+ 0x00001ff8, 0x00000002, 0x0000039f, 0x00000300,
+ 0x0000038c, 0x00000002, 0x0000039f, 0x00000301,
+ 0x00001f48, 0x00000002, 0x0000039f, 0x00000313,
+ 0x00001f49, 0x00000002, 0x0000039f, 0x00000314,
+ 0x00001fec, 0x00000002, 0x000003a1, 0x00000314,
+ 0x00001fea, 0x00000002, 0x000003a5, 0x00000300,
+ 0x0000038e, 0x00000002, 0x000003a5, 0x00000301,
+ 0x00001fe9, 0x00000002, 0x000003a5, 0x00000304,
+ 0x00001fe8, 0x00000002, 0x000003a5, 0x00000306,
+ 0x000003ab, 0x00000002, 0x000003a5, 0x00000308,
+ 0x00001f59, 0x00000002, 0x000003a5, 0x00000314,
+ 0x00001ffa, 0x00000002, 0x000003a9, 0x00000300,
+ 0x0000038f, 0x00000002, 0x000003a9, 0x00000301,
+ 0x00001f68, 0x00000002, 0x000003a9, 0x00000313,
+ 0x00001f69, 0x00000002, 0x000003a9, 0x00000314,
+ 0x00001ffc, 0x00000002, 0x000003a9, 0x00000345,
+ 0x00001fb4, 0x00000002, 0x000003ac, 0x00000345,
+ 0x00001fc4, 0x00000002, 0x000003ae, 0x00000345,
+ 0x00001f70, 0x00000002, 0x000003b1, 0x00000300,
+ 0x000003ac, 0x00000002, 0x000003b1, 0x00000301,
+ 0x00001fb1, 0x00000002, 0x000003b1, 0x00000304,
+ 0x00001fb0, 0x00000002, 0x000003b1, 0x00000306,
+ 0x00001f00, 0x00000002, 0x000003b1, 0x00000313,
+ 0x00001f01, 0x00000002, 0x000003b1, 0x00000314,
+ 0x00001fb6, 0x00000002, 0x000003b1, 0x00000342,
+ 0x00001fb3, 0x00000002, 0x000003b1, 0x00000345,
+ 0x00001f72, 0x00000002, 0x000003b5, 0x00000300,
+ 0x000003ad, 0x00000002, 0x000003b5, 0x00000301,
+ 0x00001f10, 0x00000002, 0x000003b5, 0x00000313,
+ 0x00001f11, 0x00000002, 0x000003b5, 0x00000314,
+ 0x00001f74, 0x00000002, 0x000003b7, 0x00000300,
+ 0x000003ae, 0x00000002, 0x000003b7, 0x00000301,
+ 0x00001f20, 0x00000002, 0x000003b7, 0x00000313,
+ 0x00001f21, 0x00000002, 0x000003b7, 0x00000314,
+ 0x00001fc6, 0x00000002, 0x000003b7, 0x00000342,
+ 0x00001fc3, 0x00000002, 0x000003b7, 0x00000345,
+ 0x00001f76, 0x00000002, 0x000003b9, 0x00000300,
+ 0x000003af, 0x00000002, 0x000003b9, 0x00000301,
+ 0x00001fd1, 0x00000002, 0x000003b9, 0x00000304,
+ 0x00001fd0, 0x00000002, 0x000003b9, 0x00000306,
+ 0x000003ca, 0x00000002, 0x000003b9, 0x00000308,
+ 0x00001f30, 0x00000002, 0x000003b9, 0x00000313,
+ 0x00001f31, 0x00000002, 0x000003b9, 0x00000314,
+ 0x00001fd6, 0x00000002, 0x000003b9, 0x00000342,
+ 0x00001f78, 0x00000002, 0x000003bf, 0x00000300,
+ 0x000003cc, 0x00000002, 0x000003bf, 0x00000301,
+ 0x00001f40, 0x00000002, 0x000003bf, 0x00000313,
+ 0x00001f41, 0x00000002, 0x000003bf, 0x00000314,
+ 0x00001fe4, 0x00000002, 0x000003c1, 0x00000313,
+ 0x00001fe5, 0x00000002, 0x000003c1, 0x00000314,
+ 0x00001f7a, 0x00000002, 0x000003c5, 0x00000300,
+ 0x000003cd, 0x00000002, 0x000003c5, 0x00000301,
+ 0x00001fe1, 0x00000002, 0x000003c5, 0x00000304,
+ 0x00001fe0, 0x00000002, 0x000003c5, 0x00000306,
+ 0x000003cb, 0x00000002, 0x000003c5, 0x00000308,
+ 0x00001f50, 0x00000002, 0x000003c5, 0x00000313,
+ 0x00001f51, 0x00000002, 0x000003c5, 0x00000314,
+ 0x00001fe6, 0x00000002, 0x000003c5, 0x00000342,
+ 0x00001f7c, 0x00000002, 0x000003c9, 0x00000300,
+ 0x000003ce, 0x00000002, 0x000003c9, 0x00000301,
+ 0x00001f60, 0x00000002, 0x000003c9, 0x00000313,
+ 0x00001f61, 0x00000002, 0x000003c9, 0x00000314,
+ 0x00001ff6, 0x00000002, 0x000003c9, 0x00000342,
+ 0x00001ff3, 0x00000002, 0x000003c9, 0x00000345,
+ 0x00001fd2, 0x00000002, 0x000003ca, 0x00000300,
+ 0x00000390, 0x00000002, 0x000003ca, 0x00000301,
+ 0x00001fd7, 0x00000002, 0x000003ca, 0x00000342,
+ 0x00001fe2, 0x00000002, 0x000003cb, 0x00000300,
+ 0x000003b0, 0x00000002, 0x000003cb, 0x00000301,
+ 0x00001fe7, 0x00000002, 0x000003cb, 0x00000342,
+ 0x00001ff4, 0x00000002, 0x000003ce, 0x00000345,
+ 0x000003d3, 0x00000002, 0x000003d2, 0x00000301,
+ 0x000003d4, 0x00000002, 0x000003d2, 0x00000308,
+ 0x00000407, 0x00000002, 0x00000406, 0x00000308,
+ 0x000004d0, 0x00000002, 0x00000410, 0x00000306,
+ 0x000004d2, 0x00000002, 0x00000410, 0x00000308,
+ 0x00000403, 0x00000002, 0x00000413, 0x00000301,
+ 0x00000400, 0x00000002, 0x00000415, 0x00000300,
+ 0x000004d6, 0x00000002, 0x00000415, 0x00000306,
+ 0x00000401, 0x00000002, 0x00000415, 0x00000308,
+ 0x000004c1, 0x00000002, 0x00000416, 0x00000306,
+ 0x000004dc, 0x00000002, 0x00000416, 0x00000308,
+ 0x000004de, 0x00000002, 0x00000417, 0x00000308,
+ 0x0000040d, 0x00000002, 0x00000418, 0x00000300,
+ 0x000004e2, 0x00000002, 0x00000418, 0x00000304,
+ 0x00000419, 0x00000002, 0x00000418, 0x00000306,
+ 0x000004e4, 0x00000002, 0x00000418, 0x00000308,
+ 0x0000040c, 0x00000002, 0x0000041a, 0x00000301,
+ 0x000004e6, 0x00000002, 0x0000041e, 0x00000308,
+ 0x000004ee, 0x00000002, 0x00000423, 0x00000304,
+ 0x0000040e, 0x00000002, 0x00000423, 0x00000306,
+ 0x000004f0, 0x00000002, 0x00000423, 0x00000308,
+ 0x000004f2, 0x00000002, 0x00000423, 0x0000030b,
+ 0x000004f4, 0x00000002, 0x00000427, 0x00000308,
+ 0x000004f8, 0x00000002, 0x0000042b, 0x00000308,
+ 0x000004ec, 0x00000002, 0x0000042d, 0x00000308,
+ 0x000004d1, 0x00000002, 0x00000430, 0x00000306,
+ 0x000004d3, 0x00000002, 0x00000430, 0x00000308,
+ 0x00000453, 0x00000002, 0x00000433, 0x00000301,
+ 0x00000450, 0x00000002, 0x00000435, 0x00000300,
+ 0x000004d7, 0x00000002, 0x00000435, 0x00000306,
+ 0x00000451, 0x00000002, 0x00000435, 0x00000308,
+ 0x000004c2, 0x00000002, 0x00000436, 0x00000306,
+ 0x000004dd, 0x00000002, 0x00000436, 0x00000308,
+ 0x000004df, 0x00000002, 0x00000437, 0x00000308,
+ 0x0000045d, 0x00000002, 0x00000438, 0x00000300,
+ 0x000004e3, 0x00000002, 0x00000438, 0x00000304,
+ 0x00000439, 0x00000002, 0x00000438, 0x00000306,
+ 0x000004e5, 0x00000002, 0x00000438, 0x00000308,
+ 0x0000045c, 0x00000002, 0x0000043a, 0x00000301,
+ 0x000004e7, 0x00000002, 0x0000043e, 0x00000308,
+ 0x000004ef, 0x00000002, 0x00000443, 0x00000304,
+ 0x0000045e, 0x00000002, 0x00000443, 0x00000306,
+ 0x000004f1, 0x00000002, 0x00000443, 0x00000308,
+ 0x000004f3, 0x00000002, 0x00000443, 0x0000030b,
+ 0x000004f5, 0x00000002, 0x00000447, 0x00000308,
+ 0x000004f9, 0x00000002, 0x0000044b, 0x00000308,
+ 0x000004ed, 0x00000002, 0x0000044d, 0x00000308,
+ 0x00000457, 0x00000002, 0x00000456, 0x00000308,
+ 0x00000476, 0x00000002, 0x00000474, 0x0000030f,
+ 0x00000477, 0x00000002, 0x00000475, 0x0000030f,
+ 0x000004da, 0x00000002, 0x000004d8, 0x00000308,
+ 0x000004db, 0x00000002, 0x000004d9, 0x00000308,
+ 0x000004ea, 0x00000002, 0x000004e8, 0x00000308,
+ 0x000004eb, 0x00000002, 0x000004e9, 0x00000308,
+ 0x00000622, 0x00000002, 0x00000627, 0x00000653,
+ 0x00000623, 0x00000002, 0x00000627, 0x00000654,
+ 0x00000625, 0x00000002, 0x00000627, 0x00000655,
+ 0x00000624, 0x00000002, 0x00000648, 0x00000654,
+ 0x00000626, 0x00000002, 0x0000064a, 0x00000654,
+ 0x000006c2, 0x00000002, 0x000006c1, 0x00000654,
+ 0x000006d3, 0x00000002, 0x000006d2, 0x00000654,
+ 0x000006c0, 0x00000002, 0x000006d5, 0x00000654,
+ 0x00000929, 0x00000002, 0x00000928, 0x0000093c,
+ 0x00000931, 0x00000002, 0x00000930, 0x0000093c,
+ 0x00000934, 0x00000002, 0x00000933, 0x0000093c,
+ 0x000009cb, 0x00000002, 0x000009c7, 0x000009be,
+ 0x000009cc, 0x00000002, 0x000009c7, 0x000009d7,
+ 0x00000b4b, 0x00000002, 0x00000b47, 0x00000b3e,
+ 0x00000b48, 0x00000002, 0x00000b47, 0x00000b56,
+ 0x00000b4c, 0x00000002, 0x00000b47, 0x00000b57,
+ 0x00000b94, 0x00000002, 0x00000b92, 0x00000bd7,
+ 0x00000bca, 0x00000002, 0x00000bc6, 0x00000bbe,
+ 0x00000bcc, 0x00000002, 0x00000bc6, 0x00000bd7,
+ 0x00000bcb, 0x00000002, 0x00000bc7, 0x00000bbe,
+ 0x00000c48, 0x00000002, 0x00000c46, 0x00000c56,
+ 0x00000cc0, 0x00000002, 0x00000cbf, 0x00000cd5,
+ 0x00000cca, 0x00000002, 0x00000cc6, 0x00000cc2,
+ 0x00000cc7, 0x00000002, 0x00000cc6, 0x00000cd5,
+ 0x00000cc8, 0x00000002, 0x00000cc6, 0x00000cd6,
+ 0x00000ccb, 0x00000002, 0x00000cca, 0x00000cd5,
+ 0x00000d4a, 0x00000002, 0x00000d46, 0x00000d3e,
+ 0x00000d4c, 0x00000002, 0x00000d46, 0x00000d57,
+ 0x00000d4b, 0x00000002, 0x00000d47, 0x00000d3e,
+ 0x00000dda, 0x00000002, 0x00000dd9, 0x00000dca,
+ 0x00000ddc, 0x00000002, 0x00000dd9, 0x00000dcf,
+ 0x00000dde, 0x00000002, 0x00000dd9, 0x00000ddf,
+ 0x00000ddd, 0x00000002, 0x00000ddc, 0x00000dca,
+ 0x00000f73, 0x00000002, 0x00000f71, 0x00000f72,
+ 0x00000f75, 0x00000002, 0x00000f71, 0x00000f74,
+ 0x00000f81, 0x00000002, 0x00000f71, 0x00000f80,
+ 0x00001026, 0x00000002, 0x00001025, 0x0000102e,
+ 0x00001e38, 0x00000002, 0x00001e36, 0x00000304,
+ 0x00001e39, 0x00000002, 0x00001e37, 0x00000304,
+ 0x00001e5c, 0x00000002, 0x00001e5a, 0x00000304,
+ 0x00001e5d, 0x00000002, 0x00001e5b, 0x00000304,
+ 0x00001e68, 0x00000002, 0x00001e62, 0x00000307,
+ 0x00001e69, 0x00000002, 0x00001e63, 0x00000307,
+ 0x00001eac, 0x00000002, 0x00001ea0, 0x00000302,
+ 0x00001eb6, 0x00000002, 0x00001ea0, 0x00000306,
+ 0x00001ead, 0x00000002, 0x00001ea1, 0x00000302,
+ 0x00001eb7, 0x00000002, 0x00001ea1, 0x00000306,
+ 0x00001ec6, 0x00000002, 0x00001eb8, 0x00000302,
+ 0x00001ec7, 0x00000002, 0x00001eb9, 0x00000302,
+ 0x00001ed8, 0x00000002, 0x00001ecc, 0x00000302,
+ 0x00001ed9, 0x00000002, 0x00001ecd, 0x00000302,
+ 0x00001f02, 0x00000002, 0x00001f00, 0x00000300,
+ 0x00001f04, 0x00000002, 0x00001f00, 0x00000301,
+ 0x00001f06, 0x00000002, 0x00001f00, 0x00000342,
+ 0x00001f80, 0x00000002, 0x00001f00, 0x00000345,
+ 0x00001f03, 0x00000002, 0x00001f01, 0x00000300,
+ 0x00001f05, 0x00000002, 0x00001f01, 0x00000301,
+ 0x00001f07, 0x00000002, 0x00001f01, 0x00000342,
+ 0x00001f81, 0x00000002, 0x00001f01, 0x00000345,
+ 0x00001f82, 0x00000002, 0x00001f02, 0x00000345,
+ 0x00001f83, 0x00000002, 0x00001f03, 0x00000345,
+ 0x00001f84, 0x00000002, 0x00001f04, 0x00000345,
+ 0x00001f85, 0x00000002, 0x00001f05, 0x00000345,
+ 0x00001f86, 0x00000002, 0x00001f06, 0x00000345,
+ 0x00001f87, 0x00000002, 0x00001f07, 0x00000345,
+ 0x00001f0a, 0x00000002, 0x00001f08, 0x00000300,
+ 0x00001f0c, 0x00000002, 0x00001f08, 0x00000301,
+ 0x00001f0e, 0x00000002, 0x00001f08, 0x00000342,
+ 0x00001f88, 0x00000002, 0x00001f08, 0x00000345,
+ 0x00001f0b, 0x00000002, 0x00001f09, 0x00000300,
+ 0x00001f0d, 0x00000002, 0x00001f09, 0x00000301,
+ 0x00001f0f, 0x00000002, 0x00001f09, 0x00000342,
+ 0x00001f89, 0x00000002, 0x00001f09, 0x00000345,
+ 0x00001f8a, 0x00000002, 0x00001f0a, 0x00000345,
+ 0x00001f8b, 0x00000002, 0x00001f0b, 0x00000345,
+ 0x00001f8c, 0x00000002, 0x00001f0c, 0x00000345,
+ 0x00001f8d, 0x00000002, 0x00001f0d, 0x00000345,
+ 0x00001f8e, 0x00000002, 0x00001f0e, 0x00000345,
+ 0x00001f8f, 0x00000002, 0x00001f0f, 0x00000345,
+ 0x00001f12, 0x00000002, 0x00001f10, 0x00000300,
+ 0x00001f14, 0x00000002, 0x00001f10, 0x00000301,
+ 0x00001f13, 0x00000002, 0x00001f11, 0x00000300,
+ 0x00001f15, 0x00000002, 0x00001f11, 0x00000301,
+ 0x00001f1a, 0x00000002, 0x00001f18, 0x00000300,
+ 0x00001f1c, 0x00000002, 0x00001f18, 0x00000301,
+ 0x00001f1b, 0x00000002, 0x00001f19, 0x00000300,
+ 0x00001f1d, 0x00000002, 0x00001f19, 0x00000301,
+ 0x00001f22, 0x00000002, 0x00001f20, 0x00000300,
+ 0x00001f24, 0x00000002, 0x00001f20, 0x00000301,
+ 0x00001f26, 0x00000002, 0x00001f20, 0x00000342,
+ 0x00001f90, 0x00000002, 0x00001f20, 0x00000345,
+ 0x00001f23, 0x00000002, 0x00001f21, 0x00000300,
+ 0x00001f25, 0x00000002, 0x00001f21, 0x00000301,
+ 0x00001f27, 0x00000002, 0x00001f21, 0x00000342,
+ 0x00001f91, 0x00000002, 0x00001f21, 0x00000345,
+ 0x00001f92, 0x00000002, 0x00001f22, 0x00000345,
+ 0x00001f93, 0x00000002, 0x00001f23, 0x00000345,
+ 0x00001f94, 0x00000002, 0x00001f24, 0x00000345,
+ 0x00001f95, 0x00000002, 0x00001f25, 0x00000345,
+ 0x00001f96, 0x00000002, 0x00001f26, 0x00000345,
+ 0x00001f97, 0x00000002, 0x00001f27, 0x00000345,
+ 0x00001f2a, 0x00000002, 0x00001f28, 0x00000300,
+ 0x00001f2c, 0x00000002, 0x00001f28, 0x00000301,
+ 0x00001f2e, 0x00000002, 0x00001f28, 0x00000342,
+ 0x00001f98, 0x00000002, 0x00001f28, 0x00000345,
+ 0x00001f2b, 0x00000002, 0x00001f29, 0x00000300,
+ 0x00001f2d, 0x00000002, 0x00001f29, 0x00000301,
+ 0x00001f2f, 0x00000002, 0x00001f29, 0x00000342,
+ 0x00001f99, 0x00000002, 0x00001f29, 0x00000345,
+ 0x00001f9a, 0x00000002, 0x00001f2a, 0x00000345,
+ 0x00001f9b, 0x00000002, 0x00001f2b, 0x00000345,
+ 0x00001f9c, 0x00000002, 0x00001f2c, 0x00000345,
+ 0x00001f9d, 0x00000002, 0x00001f2d, 0x00000345,
+ 0x00001f9e, 0x00000002, 0x00001f2e, 0x00000345,
+ 0x00001f9f, 0x00000002, 0x00001f2f, 0x00000345,
+ 0x00001f32, 0x00000002, 0x00001f30, 0x00000300,
+ 0x00001f34, 0x00000002, 0x00001f30, 0x00000301,
+ 0x00001f36, 0x00000002, 0x00001f30, 0x00000342,
+ 0x00001f33, 0x00000002, 0x00001f31, 0x00000300,
+ 0x00001f35, 0x00000002, 0x00001f31, 0x00000301,
+ 0x00001f37, 0x00000002, 0x00001f31, 0x00000342,
+ 0x00001f3a, 0x00000002, 0x00001f38, 0x00000300,
+ 0x00001f3c, 0x00000002, 0x00001f38, 0x00000301,
+ 0x00001f3e, 0x00000002, 0x00001f38, 0x00000342,
+ 0x00001f3b, 0x00000002, 0x00001f39, 0x00000300,
+ 0x00001f3d, 0x00000002, 0x00001f39, 0x00000301,
+ 0x00001f3f, 0x00000002, 0x00001f39, 0x00000342,
+ 0x00001f42, 0x00000002, 0x00001f40, 0x00000300,
+ 0x00001f44, 0x00000002, 0x00001f40, 0x00000301,
+ 0x00001f43, 0x00000002, 0x00001f41, 0x00000300,
+ 0x00001f45, 0x00000002, 0x00001f41, 0x00000301,
+ 0x00001f4a, 0x00000002, 0x00001f48, 0x00000300,
+ 0x00001f4c, 0x00000002, 0x00001f48, 0x00000301,
+ 0x00001f4b, 0x00000002, 0x00001f49, 0x00000300,
+ 0x00001f4d, 0x00000002, 0x00001f49, 0x00000301,
+ 0x00001f52, 0x00000002, 0x00001f50, 0x00000300,
+ 0x00001f54, 0x00000002, 0x00001f50, 0x00000301,
+ 0x00001f56, 0x00000002, 0x00001f50, 0x00000342,
+ 0x00001f53, 0x00000002, 0x00001f51, 0x00000300,
+ 0x00001f55, 0x00000002, 0x00001f51, 0x00000301,
+ 0x00001f57, 0x00000002, 0x00001f51, 0x00000342,
+ 0x00001f5b, 0x00000002, 0x00001f59, 0x00000300,
+ 0x00001f5d, 0x00000002, 0x00001f59, 0x00000301,
+ 0x00001f5f, 0x00000002, 0x00001f59, 0x00000342,
+ 0x00001f62, 0x00000002, 0x00001f60, 0x00000300,
+ 0x00001f64, 0x00000002, 0x00001f60, 0x00000301,
+ 0x00001f66, 0x00000002, 0x00001f60, 0x00000342,
+ 0x00001fa0, 0x00000002, 0x00001f60, 0x00000345,
+ 0x00001f63, 0x00000002, 0x00001f61, 0x00000300,
+ 0x00001f65, 0x00000002, 0x00001f61, 0x00000301,
+ 0x00001f67, 0x00000002, 0x00001f61, 0x00000342,
+ 0x00001fa1, 0x00000002, 0x00001f61, 0x00000345,
+ 0x00001fa2, 0x00000002, 0x00001f62, 0x00000345,
+ 0x00001fa3, 0x00000002, 0x00001f63, 0x00000345,
+ 0x00001fa4, 0x00000002, 0x00001f64, 0x00000345,
+ 0x00001fa5, 0x00000002, 0x00001f65, 0x00000345,
+ 0x00001fa6, 0x00000002, 0x00001f66, 0x00000345,
+ 0x00001fa7, 0x00000002, 0x00001f67, 0x00000345,
+ 0x00001f6a, 0x00000002, 0x00001f68, 0x00000300,
+ 0x00001f6c, 0x00000002, 0x00001f68, 0x00000301,
+ 0x00001f6e, 0x00000002, 0x00001f68, 0x00000342,
+ 0x00001fa8, 0x00000002, 0x00001f68, 0x00000345,
+ 0x00001f6b, 0x00000002, 0x00001f69, 0x00000300,
+ 0x00001f6d, 0x00000002, 0x00001f69, 0x00000301,
+ 0x00001f6f, 0x00000002, 0x00001f69, 0x00000342,
+ 0x00001fa9, 0x00000002, 0x00001f69, 0x00000345,
+ 0x00001faa, 0x00000002, 0x00001f6a, 0x00000345,
+ 0x00001fab, 0x00000002, 0x00001f6b, 0x00000345,
+ 0x00001fac, 0x00000002, 0x00001f6c, 0x00000345,
+ 0x00001fad, 0x00000002, 0x00001f6d, 0x00000345,
+ 0x00001fae, 0x00000002, 0x00001f6e, 0x00000345,
+ 0x00001faf, 0x00000002, 0x00001f6f, 0x00000345,
+ 0x00001fb2, 0x00000002, 0x00001f70, 0x00000345,
+ 0x00001fc2, 0x00000002, 0x00001f74, 0x00000345,
+ 0x00001ff2, 0x00000002, 0x00001f7c, 0x00000345,
+ 0x00001fb7, 0x00000002, 0x00001fb6, 0x00000345,
+ 0x00001fcd, 0x00000002, 0x00001fbf, 0x00000300,
+ 0x00001fce, 0x00000002, 0x00001fbf, 0x00000301,
+ 0x00001fcf, 0x00000002, 0x00001fbf, 0x00000342,
+ 0x00001fc7, 0x00000002, 0x00001fc6, 0x00000345,
+ 0x00001ff7, 0x00000002, 0x00001ff6, 0x00000345,
+ 0x00001fdd, 0x00000002, 0x00001ffe, 0x00000300,
+ 0x00001fde, 0x00000002, 0x00001ffe, 0x00000301,
+ 0x00001fdf, 0x00000002, 0x00001ffe, 0x00000342,
+ 0x0000219a, 0x00000002, 0x00002190, 0x00000338,
+ 0x0000219b, 0x00000002, 0x00002192, 0x00000338,
+ 0x000021ae, 0x00000002, 0x00002194, 0x00000338,
+ 0x000021cd, 0x00000002, 0x000021d0, 0x00000338,
+ 0x000021cf, 0x00000002, 0x000021d2, 0x00000338,
+ 0x000021ce, 0x00000002, 0x000021d4, 0x00000338,
+ 0x00002204, 0x00000002, 0x00002203, 0x00000338,
+ 0x00002209, 0x00000002, 0x00002208, 0x00000338,
+ 0x0000220c, 0x00000002, 0x0000220b, 0x00000338,
+ 0x00002224, 0x00000002, 0x00002223, 0x00000338,
+ 0x00002226, 0x00000002, 0x00002225, 0x00000338,
+ 0x00002241, 0x00000002, 0x0000223c, 0x00000338,
+ 0x00002244, 0x00000002, 0x00002243, 0x00000338,
+ 0x00002247, 0x00000002, 0x00002245, 0x00000338,
+ 0x00002249, 0x00000002, 0x00002248, 0x00000338,
+ 0x0000226d, 0x00000002, 0x0000224d, 0x00000338,
+ 0x00002262, 0x00000002, 0x00002261, 0x00000338,
+ 0x00002270, 0x00000002, 0x00002264, 0x00000338,
+ 0x00002271, 0x00000002, 0x00002265, 0x00000338,
+ 0x00002274, 0x00000002, 0x00002272, 0x00000338,
+ 0x00002275, 0x00000002, 0x00002273, 0x00000338,
+ 0x00002278, 0x00000002, 0x00002276, 0x00000338,
+ 0x00002279, 0x00000002, 0x00002277, 0x00000338,
+ 0x00002280, 0x00000002, 0x0000227a, 0x00000338,
+ 0x00002281, 0x00000002, 0x0000227b, 0x00000338,
+ 0x000022e0, 0x00000002, 0x0000227c, 0x00000338,
+ 0x000022e1, 0x00000002, 0x0000227d, 0x00000338,
+ 0x00002284, 0x00000002, 0x00002282, 0x00000338,
+ 0x00002285, 0x00000002, 0x00002283, 0x00000338,
+ 0x00002288, 0x00000002, 0x00002286, 0x00000338,
+ 0x00002289, 0x00000002, 0x00002287, 0x00000338,
+ 0x000022e2, 0x00000002, 0x00002291, 0x00000338,
+ 0x000022e3, 0x00000002, 0x00002292, 0x00000338,
+ 0x000022ac, 0x00000002, 0x000022a2, 0x00000338,
+ 0x000022ad, 0x00000002, 0x000022a8, 0x00000338,
+ 0x000022ae, 0x00000002, 0x000022a9, 0x00000338,
+ 0x000022af, 0x00000002, 0x000022ab, 0x00000338,
+ 0x000022ea, 0x00000002, 0x000022b2, 0x00000338,
+ 0x000022eb, 0x00000002, 0x000022b3, 0x00000338,
+ 0x000022ec, 0x00000002, 0x000022b4, 0x00000338,
+ 0x000022ed, 0x00000002, 0x000022b5, 0x00000338,
+ 0x00003094, 0x00000002, 0x00003046, 0x00003099,
+ 0x0000304c, 0x00000002, 0x0000304b, 0x00003099,
+ 0x0000304e, 0x00000002, 0x0000304d, 0x00003099,
+ 0x00003050, 0x00000002, 0x0000304f, 0x00003099,
+ 0x00003052, 0x00000002, 0x00003051, 0x00003099,
+ 0x00003054, 0x00000002, 0x00003053, 0x00003099,
+ 0x00003056, 0x00000002, 0x00003055, 0x00003099,
+ 0x00003058, 0x00000002, 0x00003057, 0x00003099,
+ 0x0000305a, 0x00000002, 0x00003059, 0x00003099,
+ 0x0000305c, 0x00000002, 0x0000305b, 0x00003099,
+ 0x0000305e, 0x00000002, 0x0000305d, 0x00003099,
+ 0x00003060, 0x00000002, 0x0000305f, 0x00003099,
+ 0x00003062, 0x00000002, 0x00003061, 0x00003099,
+ 0x00003065, 0x00000002, 0x00003064, 0x00003099,
+ 0x00003067, 0x00000002, 0x00003066, 0x00003099,
+ 0x00003069, 0x00000002, 0x00003068, 0x00003099,
+ 0x00003070, 0x00000002, 0x0000306f, 0x00003099,
+ 0x00003071, 0x00000002, 0x0000306f, 0x0000309a,
+ 0x00003073, 0x00000002, 0x00003072, 0x00003099,
+ 0x00003074, 0x00000002, 0x00003072, 0x0000309a,
+ 0x00003076, 0x00000002, 0x00003075, 0x00003099,
+ 0x00003077, 0x00000002, 0x00003075, 0x0000309a,
+ 0x00003079, 0x00000002, 0x00003078, 0x00003099,
+ 0x0000307a, 0x00000002, 0x00003078, 0x0000309a,
+ 0x0000307c, 0x00000002, 0x0000307b, 0x00003099,
+ 0x0000307d, 0x00000002, 0x0000307b, 0x0000309a,
+ 0x0000309e, 0x00000002, 0x0000309d, 0x00003099,
+ 0x000030f4, 0x00000002, 0x000030a6, 0x00003099,
+ 0x000030ac, 0x00000002, 0x000030ab, 0x00003099,
+ 0x000030ae, 0x00000002, 0x000030ad, 0x00003099,
+ 0x000030b0, 0x00000002, 0x000030af, 0x00003099,
+ 0x000030b2, 0x00000002, 0x000030b1, 0x00003099,
+ 0x000030b4, 0x00000002, 0x000030b3, 0x00003099,
+ 0x000030b6, 0x00000002, 0x000030b5, 0x00003099,
+ 0x000030b8, 0x00000002, 0x000030b7, 0x00003099,
+ 0x000030ba, 0x00000002, 0x000030b9, 0x00003099,
+ 0x000030bc, 0x00000002, 0x000030bb, 0x00003099,
+ 0x000030be, 0x00000002, 0x000030bd, 0x00003099,
+ 0x000030c0, 0x00000002, 0x000030bf, 0x00003099,
+ 0x000030c2, 0x00000002, 0x000030c1, 0x00003099,
+ 0x000030c5, 0x00000002, 0x000030c4, 0x00003099,
+ 0x000030c7, 0x00000002, 0x000030c6, 0x00003099,
+ 0x000030c9, 0x00000002, 0x000030c8, 0x00003099,
+ 0x000030d0, 0x00000002, 0x000030cf, 0x00003099,
+ 0x000030d1, 0x00000002, 0x000030cf, 0x0000309a,
+ 0x000030d3, 0x00000002, 0x000030d2, 0x00003099,
+ 0x000030d4, 0x00000002, 0x000030d2, 0x0000309a,
+ 0x000030d6, 0x00000002, 0x000030d5, 0x00003099,
+ 0x000030d7, 0x00000002, 0x000030d5, 0x0000309a,
+ 0x000030d9, 0x00000002, 0x000030d8, 0x00003099,
+ 0x000030da, 0x00000002, 0x000030d8, 0x0000309a,
+ 0x000030dc, 0x00000002, 0x000030db, 0x00003099,
+ 0x000030dd, 0x00000002, 0x000030db, 0x0000309a,
+ 0x000030f7, 0x00000002, 0x000030ef, 0x00003099,
+ 0x000030f8, 0x00000002, 0x000030f0, 0x00003099,
+ 0x000030f9, 0x00000002, 0x000030f1, 0x00003099,
+ 0x000030fa, 0x00000002, 0x000030f2, 0x00003099,
+ 0x000030fe, 0x00000002, 0x000030fd, 0x00003099
+};
+
+static const ac_uint4 _ucdcmp_size = 3848;
+
+static const ac_uint4 _ucdcmp_nodes[] = {
+ 0x000000c0, 0x00000000,
+ 0x000000c1, 0x00000002,
+ 0x000000c2, 0x00000004,
+ 0x000000c3, 0x00000006,
+ 0x000000c4, 0x00000008,
+ 0x000000c5, 0x0000000a,
+ 0x000000c7, 0x0000000c,
+ 0x000000c8, 0x0000000e,
+ 0x000000c9, 0x00000010,
+ 0x000000ca, 0x00000012,
+ 0x000000cb, 0x00000014,
+ 0x000000cc, 0x00000016,
+ 0x000000cd, 0x00000018,
+ 0x000000ce, 0x0000001a,
+ 0x000000cf, 0x0000001c,
+ 0x000000d1, 0x0000001e,
+ 0x000000d2, 0x00000020,
+ 0x000000d3, 0x00000022,
+ 0x000000d4, 0x00000024,
+ 0x000000d5, 0x00000026,
+ 0x000000d6, 0x00000028,
+ 0x000000d9, 0x0000002a,
+ 0x000000da, 0x0000002c,
+ 0x000000db, 0x0000002e,
+ 0x000000dc, 0x00000030,
+ 0x000000dd, 0x00000032,
+ 0x000000e0, 0x00000034,
+ 0x000000e1, 0x00000036,
+ 0x000000e2, 0x00000038,
+ 0x000000e3, 0x0000003a,
+ 0x000000e4, 0x0000003c,
+ 0x000000e5, 0x0000003e,
+ 0x000000e7, 0x00000040,
+ 0x000000e8, 0x00000042,
+ 0x000000e9, 0x00000044,
+ 0x000000ea, 0x00000046,
+ 0x000000eb, 0x00000048,
+ 0x000000ec, 0x0000004a,
+ 0x000000ed, 0x0000004c,
+ 0x000000ee, 0x0000004e,
+ 0x000000ef, 0x00000050,
+ 0x000000f1, 0x00000052,
+ 0x000000f2, 0x00000054,
+ 0x000000f3, 0x00000056,
+ 0x000000f4, 0x00000058,
+ 0x000000f5, 0x0000005a,
+ 0x000000f6, 0x0000005c,
+ 0x000000f9, 0x0000005e,
+ 0x000000fa, 0x00000060,
+ 0x000000fb, 0x00000062,
+ 0x000000fc, 0x00000064,
+ 0x000000fd, 0x00000066,
+ 0x000000ff, 0x00000068,
+ 0x00000100, 0x0000006a,
+ 0x00000101, 0x0000006c,
+ 0x00000102, 0x0000006e,
+ 0x00000103, 0x00000070,
+ 0x00000104, 0x00000072,
+ 0x00000105, 0x00000074,
+ 0x00000106, 0x00000076,
+ 0x00000107, 0x00000078,
+ 0x00000108, 0x0000007a,
+ 0x00000109, 0x0000007c,
+ 0x0000010a, 0x0000007e,
+ 0x0000010b, 0x00000080,
+ 0x0000010c, 0x00000082,
+ 0x0000010d, 0x00000084,
+ 0x0000010e, 0x00000086,
+ 0x0000010f, 0x00000088,
+ 0x00000112, 0x0000008a,
+ 0x00000113, 0x0000008c,
+ 0x00000114, 0x0000008e,
+ 0x00000115, 0x00000090,
+ 0x00000116, 0x00000092,
+ 0x00000117, 0x00000094,
+ 0x00000118, 0x00000096,
+ 0x00000119, 0x00000098,
+ 0x0000011a, 0x0000009a,
+ 0x0000011b, 0x0000009c,
+ 0x0000011c, 0x0000009e,
+ 0x0000011d, 0x000000a0,
+ 0x0000011e, 0x000000a2,
+ 0x0000011f, 0x000000a4,
+ 0x00000120, 0x000000a6,
+ 0x00000121, 0x000000a8,
+ 0x00000122, 0x000000aa,
+ 0x00000123, 0x000000ac,
+ 0x00000124, 0x000000ae,
+ 0x00000125, 0x000000b0,
+ 0x00000128, 0x000000b2,
+ 0x00000129, 0x000000b4,
+ 0x0000012a, 0x000000b6,
+ 0x0000012b, 0x000000b8,
+ 0x0000012c, 0x000000ba,
+ 0x0000012d, 0x000000bc,
+ 0x0000012e, 0x000000be,
+ 0x0000012f, 0x000000c0,
+ 0x00000130, 0x000000c2,
+ 0x00000134, 0x000000c4,
+ 0x00000135, 0x000000c6,
+ 0x00000136, 0x000000c8,
+ 0x00000137, 0x000000ca,
+ 0x00000139, 0x000000cc,
+ 0x0000013a, 0x000000ce,
+ 0x0000013b, 0x000000d0,
+ 0x0000013c, 0x000000d2,
+ 0x0000013d, 0x000000d4,
+ 0x0000013e, 0x000000d6,
+ 0x00000143, 0x000000d8,
+ 0x00000144, 0x000000da,
+ 0x00000145, 0x000000dc,
+ 0x00000146, 0x000000de,
+ 0x00000147, 0x000000e0,
+ 0x00000148, 0x000000e2,
+ 0x0000014c, 0x000000e4,
+ 0x0000014d, 0x000000e6,
+ 0x0000014e, 0x000000e8,
+ 0x0000014f, 0x000000ea,
+ 0x00000150, 0x000000ec,
+ 0x00000151, 0x000000ee,
+ 0x00000154, 0x000000f0,
+ 0x00000155, 0x000000f2,
+ 0x00000156, 0x000000f4,
+ 0x00000157, 0x000000f6,
+ 0x00000158, 0x000000f8,
+ 0x00000159, 0x000000fa,
+ 0x0000015a, 0x000000fc,
+ 0x0000015b, 0x000000fe,
+ 0x0000015c, 0x00000100,
+ 0x0000015d, 0x00000102,
+ 0x0000015e, 0x00000104,
+ 0x0000015f, 0x00000106,
+ 0x00000160, 0x00000108,
+ 0x00000161, 0x0000010a,
+ 0x00000162, 0x0000010c,
+ 0x00000163, 0x0000010e,
+ 0x00000164, 0x00000110,
+ 0x00000165, 0x00000112,
+ 0x00000168, 0x00000114,
+ 0x00000169, 0x00000116,
+ 0x0000016a, 0x00000118,
+ 0x0000016b, 0x0000011a,
+ 0x0000016c, 0x0000011c,
+ 0x0000016d, 0x0000011e,
+ 0x0000016e, 0x00000120,
+ 0x0000016f, 0x00000122,
+ 0x00000170, 0x00000124,
+ 0x00000171, 0x00000126,
+ 0x00000172, 0x00000128,
+ 0x00000173, 0x0000012a,
+ 0x00000174, 0x0000012c,
+ 0x00000175, 0x0000012e,
+ 0x00000176, 0x00000130,
+ 0x00000177, 0x00000132,
+ 0x00000178, 0x00000134,
+ 0x00000179, 0x00000136,
+ 0x0000017a, 0x00000138,
+ 0x0000017b, 0x0000013a,
+ 0x0000017c, 0x0000013c,
+ 0x0000017d, 0x0000013e,
+ 0x0000017e, 0x00000140,
+ 0x000001a0, 0x00000142,
+ 0x000001a1, 0x00000144,
+ 0x000001af, 0x00000146,
+ 0x000001b0, 0x00000148,
+ 0x000001cd, 0x0000014a,
+ 0x000001ce, 0x0000014c,
+ 0x000001cf, 0x0000014e,
+ 0x000001d0, 0x00000150,
+ 0x000001d1, 0x00000152,
+ 0x000001d2, 0x00000154,
+ 0x000001d3, 0x00000156,
+ 0x000001d4, 0x00000158,
+ 0x000001d5, 0x0000015a,
+ 0x000001d6, 0x0000015d,
+ 0x000001d7, 0x00000160,
+ 0x000001d8, 0x00000163,
+ 0x000001d9, 0x00000166,
+ 0x000001da, 0x00000169,
+ 0x000001db, 0x0000016c,
+ 0x000001dc, 0x0000016f,
+ 0x000001de, 0x00000172,
+ 0x000001df, 0x00000175,
+ 0x000001e0, 0x00000178,
+ 0x000001e1, 0x0000017b,
+ 0x000001e2, 0x0000017e,
+ 0x000001e3, 0x00000180,
+ 0x000001e6, 0x00000182,
+ 0x000001e7, 0x00000184,
+ 0x000001e8, 0x00000186,
+ 0x000001e9, 0x00000188,
+ 0x000001ea, 0x0000018a,
+ 0x000001eb, 0x0000018c,
+ 0x000001ec, 0x0000018e,
+ 0x000001ed, 0x00000191,
+ 0x000001ee, 0x00000194,
+ 0x000001ef, 0x00000196,
+ 0x000001f0, 0x00000198,
+ 0x000001f4, 0x0000019a,
+ 0x000001f5, 0x0000019c,
+ 0x000001f8, 0x0000019e,
+ 0x000001f9, 0x000001a0,
+ 0x000001fa, 0x000001a2,
+ 0x000001fb, 0x000001a5,
+ 0x000001fc, 0x000001a8,
+ 0x000001fd, 0x000001aa,
+ 0x000001fe, 0x000001ac,
+ 0x000001ff, 0x000001ae,
+ 0x00000200, 0x000001b0,
+ 0x00000201, 0x000001b2,
+ 0x00000202, 0x000001b4,
+ 0x00000203, 0x000001b6,
+ 0x00000204, 0x000001b8,
+ 0x00000205, 0x000001ba,
+ 0x00000206, 0x000001bc,
+ 0x00000207, 0x000001be,
+ 0x00000208, 0x000001c0,
+ 0x00000209, 0x000001c2,
+ 0x0000020a, 0x000001c4,
+ 0x0000020b, 0x000001c6,
+ 0x0000020c, 0x000001c8,
+ 0x0000020d, 0x000001ca,
+ 0x0000020e, 0x000001cc,
+ 0x0000020f, 0x000001ce,
+ 0x00000210, 0x000001d0,
+ 0x00000211, 0x000001d2,
+ 0x00000212, 0x000001d4,
+ 0x00000213, 0x000001d6,
+ 0x00000214, 0x000001d8,
+ 0x00000215, 0x000001da,
+ 0x00000216, 0x000001dc,
+ 0x00000217, 0x000001de,
+ 0x00000218, 0x000001e0,
+ 0x00000219, 0x000001e2,
+ 0x0000021a, 0x000001e4,
+ 0x0000021b, 0x000001e6,
+ 0x0000021e, 0x000001e8,
+ 0x0000021f, 0x000001ea,
+ 0x00000226, 0x000001ec,
+ 0x00000227, 0x000001ee,
+ 0x00000228, 0x000001f0,
+ 0x00000229, 0x000001f2,
+ 0x0000022a, 0x000001f4,
+ 0x0000022b, 0x000001f7,
+ 0x0000022c, 0x000001fa,
+ 0x0000022d, 0x000001fd,
+ 0x0000022e, 0x00000200,
+ 0x0000022f, 0x00000202,
+ 0x00000230, 0x00000204,
+ 0x00000231, 0x00000207,
+ 0x00000232, 0x0000020a,
+ 0x00000233, 0x0000020c,
+ 0x00000340, 0x0000020e,
+ 0x00000341, 0x0000020f,
+ 0x00000343, 0x00000210,
+ 0x00000344, 0x00000211,
+ 0x00000374, 0x00000213,
+ 0x0000037e, 0x00000214,
+ 0x00000385, 0x00000215,
+ 0x00000386, 0x00000217,
+ 0x00000387, 0x00000219,
+ 0x00000388, 0x0000021a,
+ 0x00000389, 0x0000021c,
+ 0x0000038a, 0x0000021e,
+ 0x0000038c, 0x00000220,
+ 0x0000038e, 0x00000222,
+ 0x0000038f, 0x00000224,
+ 0x00000390, 0x00000226,
+ 0x000003aa, 0x00000229,
+ 0x000003ab, 0x0000022b,
+ 0x000003ac, 0x0000022d,
+ 0x000003ad, 0x0000022f,
+ 0x000003ae, 0x00000231,
+ 0x000003af, 0x00000233,
+ 0x000003b0, 0x00000235,
+ 0x000003ca, 0x00000238,
+ 0x000003cb, 0x0000023a,
+ 0x000003cc, 0x0000023c,
+ 0x000003cd, 0x0000023e,
+ 0x000003ce, 0x00000240,
+ 0x000003d3, 0x00000242,
+ 0x000003d4, 0x00000244,
+ 0x00000400, 0x00000246,
+ 0x00000401, 0x00000248,
+ 0x00000403, 0x0000024a,
+ 0x00000407, 0x0000024c,
+ 0x0000040c, 0x0000024e,
+ 0x0000040d, 0x00000250,
+ 0x0000040e, 0x00000252,
+ 0x00000419, 0x00000254,
+ 0x00000439, 0x00000256,
+ 0x00000450, 0x00000258,
+ 0x00000451, 0x0000025a,
+ 0x00000453, 0x0000025c,
+ 0x00000457, 0x0000025e,
+ 0x0000045c, 0x00000260,
+ 0x0000045d, 0x00000262,
+ 0x0000045e, 0x00000264,
+ 0x00000476, 0x00000266,
+ 0x00000477, 0x00000268,
+ 0x000004c1, 0x0000026a,
+ 0x000004c2, 0x0000026c,
+ 0x000004d0, 0x0000026e,
+ 0x000004d1, 0x00000270,
+ 0x000004d2, 0x00000272,
+ 0x000004d3, 0x00000274,
+ 0x000004d6, 0x00000276,
+ 0x000004d7, 0x00000278,
+ 0x000004da, 0x0000027a,
+ 0x000004db, 0x0000027c,
+ 0x000004dc, 0x0000027e,
+ 0x000004dd, 0x00000280,
+ 0x000004de, 0x00000282,
+ 0x000004df, 0x00000284,
+ 0x000004e2, 0x00000286,
+ 0x000004e3, 0x00000288,
+ 0x000004e4, 0x0000028a,
+ 0x000004e5, 0x0000028c,
+ 0x000004e6, 0x0000028e,
+ 0x000004e7, 0x00000290,
+ 0x000004ea, 0x00000292,
+ 0x000004eb, 0x00000294,
+ 0x000004ec, 0x00000296,
+ 0x000004ed, 0x00000298,
+ 0x000004ee, 0x0000029a,
+ 0x000004ef, 0x0000029c,
+ 0x000004f0, 0x0000029e,
+ 0x000004f1, 0x000002a0,
+ 0x000004f2, 0x000002a2,
+ 0x000004f3, 0x000002a4,
+ 0x000004f4, 0x000002a6,
+ 0x000004f5, 0x000002a8,
+ 0x000004f8, 0x000002aa,
+ 0x000004f9, 0x000002ac,
+ 0x00000622, 0x000002ae,
+ 0x00000623, 0x000002b0,
+ 0x00000624, 0x000002b2,
+ 0x00000625, 0x000002b4,
+ 0x00000626, 0x000002b6,
+ 0x000006c0, 0x000002b8,
+ 0x000006c2, 0x000002ba,
+ 0x000006d3, 0x000002bc,
+ 0x00000929, 0x000002be,
+ 0x00000931, 0x000002c0,
+ 0x00000934, 0x000002c2,
+ 0x00000958, 0x000002c4,
+ 0x00000959, 0x000002c6,
+ 0x0000095a, 0x000002c8,
+ 0x0000095b, 0x000002ca,
+ 0x0000095c, 0x000002cc,
+ 0x0000095d, 0x000002ce,
+ 0x0000095e, 0x000002d0,
+ 0x0000095f, 0x000002d2,
+ 0x000009cb, 0x000002d4,
+ 0x000009cc, 0x000002d6,
+ 0x000009dc, 0x000002d8,
+ 0x000009dd, 0x000002da,
+ 0x000009df, 0x000002dc,
+ 0x00000a33, 0x000002de,
+ 0x00000a36, 0x000002e0,
+ 0x00000a59, 0x000002e2,
+ 0x00000a5a, 0x000002e4,
+ 0x00000a5b, 0x000002e6,
+ 0x00000a5e, 0x000002e8,
+ 0x00000b48, 0x000002ea,
+ 0x00000b4b, 0x000002ec,
+ 0x00000b4c, 0x000002ee,
+ 0x00000b5c, 0x000002f0,
+ 0x00000b5d, 0x000002f2,
+ 0x00000b94, 0x000002f4,
+ 0x00000bca, 0x000002f6,
+ 0x00000bcb, 0x000002f8,
+ 0x00000bcc, 0x000002fa,
+ 0x00000c48, 0x000002fc,
+ 0x00000cc0, 0x000002fe,
+ 0x00000cc7, 0x00000300,
+ 0x00000cc8, 0x00000302,
+ 0x00000cca, 0x00000304,
+ 0x00000ccb, 0x00000306,
+ 0x00000d4a, 0x00000309,
+ 0x00000d4b, 0x0000030b,
+ 0x00000d4c, 0x0000030d,
+ 0x00000dda, 0x0000030f,
+ 0x00000ddc, 0x00000311,
+ 0x00000ddd, 0x00000313,
+ 0x00000dde, 0x00000316,
+ 0x00000f43, 0x00000318,
+ 0x00000f4d, 0x0000031a,
+ 0x00000f52, 0x0000031c,
+ 0x00000f57, 0x0000031e,
+ 0x00000f5c, 0x00000320,
+ 0x00000f69, 0x00000322,
+ 0x00000f73, 0x00000324,
+ 0x00000f75, 0x00000326,
+ 0x00000f76, 0x00000328,
+ 0x00000f78, 0x0000032a,
+ 0x00000f81, 0x0000032c,
+ 0x00000f93, 0x0000032e,
+ 0x00000f9d, 0x00000330,
+ 0x00000fa2, 0x00000332,
+ 0x00000fa7, 0x00000334,
+ 0x00000fac, 0x00000336,
+ 0x00000fb9, 0x00000338,
+ 0x00001026, 0x0000033a,
+ 0x00001e00, 0x0000033c,
+ 0x00001e01, 0x0000033e,
+ 0x00001e02, 0x00000340,
+ 0x00001e03, 0x00000342,
+ 0x00001e04, 0x00000344,
+ 0x00001e05, 0x00000346,
+ 0x00001e06, 0x00000348,
+ 0x00001e07, 0x0000034a,
+ 0x00001e08, 0x0000034c,
+ 0x00001e09, 0x0000034f,
+ 0x00001e0a, 0x00000352,
+ 0x00001e0b, 0x00000354,
+ 0x00001e0c, 0x00000356,
+ 0x00001e0d, 0x00000358,
+ 0x00001e0e, 0x0000035a,
+ 0x00001e0f, 0x0000035c,
+ 0x00001e10, 0x0000035e,
+ 0x00001e11, 0x00000360,
+ 0x00001e12, 0x00000362,
+ 0x00001e13, 0x00000364,
+ 0x00001e14, 0x00000366,
+ 0x00001e15, 0x00000369,
+ 0x00001e16, 0x0000036c,
+ 0x00001e17, 0x0000036f,
+ 0x00001e18, 0x00000372,
+ 0x00001e19, 0x00000374,
+ 0x00001e1a, 0x00000376,
+ 0x00001e1b, 0x00000378,
+ 0x00001e1c, 0x0000037a,
+ 0x00001e1d, 0x0000037d,
+ 0x00001e1e, 0x00000380,
+ 0x00001e1f, 0x00000382,
+ 0x00001e20, 0x00000384,
+ 0x00001e21, 0x00000386,
+ 0x00001e22, 0x00000388,
+ 0x00001e23, 0x0000038a,
+ 0x00001e24, 0x0000038c,
+ 0x00001e25, 0x0000038e,
+ 0x00001e26, 0x00000390,
+ 0x00001e27, 0x00000392,
+ 0x00001e28, 0x00000394,
+ 0x00001e29, 0x00000396,
+ 0x00001e2a, 0x00000398,
+ 0x00001e2b, 0x0000039a,
+ 0x00001e2c, 0x0000039c,
+ 0x00001e2d, 0x0000039e,
+ 0x00001e2e, 0x000003a0,
+ 0x00001e2f, 0x000003a3,
+ 0x00001e30, 0x000003a6,
+ 0x00001e31, 0x000003a8,
+ 0x00001e32, 0x000003aa,
+ 0x00001e33, 0x000003ac,
+ 0x00001e34, 0x000003ae,
+ 0x00001e35, 0x000003b0,
+ 0x00001e36, 0x000003b2,
+ 0x00001e37, 0x000003b4,
+ 0x00001e38, 0x000003b6,
+ 0x00001e39, 0x000003b9,
+ 0x00001e3a, 0x000003bc,
+ 0x00001e3b, 0x000003be,
+ 0x00001e3c, 0x000003c0,
+ 0x00001e3d, 0x000003c2,
+ 0x00001e3e, 0x000003c4,
+ 0x00001e3f, 0x000003c6,
+ 0x00001e40, 0x000003c8,
+ 0x00001e41, 0x000003ca,
+ 0x00001e42, 0x000003cc,
+ 0x00001e43, 0x000003ce,
+ 0x00001e44, 0x000003d0,
+ 0x00001e45, 0x000003d2,
+ 0x00001e46, 0x000003d4,
+ 0x00001e47, 0x000003d6,
+ 0x00001e48, 0x000003d8,
+ 0x00001e49, 0x000003da,
+ 0x00001e4a, 0x000003dc,
+ 0x00001e4b, 0x000003de,
+ 0x00001e4c, 0x000003e0,
+ 0x00001e4d, 0x000003e3,
+ 0x00001e4e, 0x000003e6,
+ 0x00001e4f, 0x000003e9,
+ 0x00001e50, 0x000003ec,
+ 0x00001e51, 0x000003ef,
+ 0x00001e52, 0x000003f2,
+ 0x00001e53, 0x000003f5,
+ 0x00001e54, 0x000003f8,
+ 0x00001e55, 0x000003fa,
+ 0x00001e56, 0x000003fc,
+ 0x00001e57, 0x000003fe,
+ 0x00001e58, 0x00000400,
+ 0x00001e59, 0x00000402,
+ 0x00001e5a, 0x00000404,
+ 0x00001e5b, 0x00000406,
+ 0x00001e5c, 0x00000408,
+ 0x00001e5d, 0x0000040b,
+ 0x00001e5e, 0x0000040e,
+ 0x00001e5f, 0x00000410,
+ 0x00001e60, 0x00000412,
+ 0x00001e61, 0x00000414,
+ 0x00001e62, 0x00000416,
+ 0x00001e63, 0x00000418,
+ 0x00001e64, 0x0000041a,
+ 0x00001e65, 0x0000041d,
+ 0x00001e66, 0x00000420,
+ 0x00001e67, 0x00000423,
+ 0x00001e68, 0x00000426,
+ 0x00001e69, 0x00000429,
+ 0x00001e6a, 0x0000042c,
+ 0x00001e6b, 0x0000042e,
+ 0x00001e6c, 0x00000430,
+ 0x00001e6d, 0x00000432,
+ 0x00001e6e, 0x00000434,
+ 0x00001e6f, 0x00000436,
+ 0x00001e70, 0x00000438,
+ 0x00001e71, 0x0000043a,
+ 0x00001e72, 0x0000043c,
+ 0x00001e73, 0x0000043e,
+ 0x00001e74, 0x00000440,
+ 0x00001e75, 0x00000442,
+ 0x00001e76, 0x00000444,
+ 0x00001e77, 0x00000446,
+ 0x00001e78, 0x00000448,
+ 0x00001e79, 0x0000044b,
+ 0x00001e7a, 0x0000044e,
+ 0x00001e7b, 0x00000451,
+ 0x00001e7c, 0x00000454,
+ 0x00001e7d, 0x00000456,
+ 0x00001e7e, 0x00000458,
+ 0x00001e7f, 0x0000045a,
+ 0x00001e80, 0x0000045c,
+ 0x00001e81, 0x0000045e,
+ 0x00001e82, 0x00000460,
+ 0x00001e83, 0x00000462,
+ 0x00001e84, 0x00000464,
+ 0x00001e85, 0x00000466,
+ 0x00001e86, 0x00000468,
+ 0x00001e87, 0x0000046a,
+ 0x00001e88, 0x0000046c,
+ 0x00001e89, 0x0000046e,
+ 0x00001e8a, 0x00000470,
+ 0x00001e8b, 0x00000472,
+ 0x00001e8c, 0x00000474,
+ 0x00001e8d, 0x00000476,
+ 0x00001e8e, 0x00000478,
+ 0x00001e8f, 0x0000047a,
+ 0x00001e90, 0x0000047c,
+ 0x00001e91, 0x0000047e,
+ 0x00001e92, 0x00000480,
+ 0x00001e93, 0x00000482,
+ 0x00001e94, 0x00000484,
+ 0x00001e95, 0x00000486,
+ 0x00001e96, 0x00000488,
+ 0x00001e97, 0x0000048a,
+ 0x00001e98, 0x0000048c,
+ 0x00001e99, 0x0000048e,
+ 0x00001e9b, 0x00000490,
+ 0x00001ea0, 0x00000492,
+ 0x00001ea1, 0x00000494,
+ 0x00001ea2, 0x00000496,
+ 0x00001ea3, 0x00000498,
+ 0x00001ea4, 0x0000049a,
+ 0x00001ea5, 0x0000049d,
+ 0x00001ea6, 0x000004a0,
+ 0x00001ea7, 0x000004a3,
+ 0x00001ea8, 0x000004a6,
+ 0x00001ea9, 0x000004a9,
+ 0x00001eaa, 0x000004ac,
+ 0x00001eab, 0x000004af,
+ 0x00001eac, 0x000004b2,
+ 0x00001ead, 0x000004b5,
+ 0x00001eae, 0x000004b8,
+ 0x00001eaf, 0x000004bb,
+ 0x00001eb0, 0x000004be,
+ 0x00001eb1, 0x000004c1,
+ 0x00001eb2, 0x000004c4,
+ 0x00001eb3, 0x000004c7,
+ 0x00001eb4, 0x000004ca,
+ 0x00001eb5, 0x000004cd,
+ 0x00001eb6, 0x000004d0,
+ 0x00001eb7, 0x000004d3,
+ 0x00001eb8, 0x000004d6,
+ 0x00001eb9, 0x000004d8,
+ 0x00001eba, 0x000004da,
+ 0x00001ebb, 0x000004dc,
+ 0x00001ebc, 0x000004de,
+ 0x00001ebd, 0x000004e0,
+ 0x00001ebe, 0x000004e2,
+ 0x00001ebf, 0x000004e5,
+ 0x00001ec0, 0x000004e8,
+ 0x00001ec1, 0x000004eb,
+ 0x00001ec2, 0x000004ee,
+ 0x00001ec3, 0x000004f1,
+ 0x00001ec4, 0x000004f4,
+ 0x00001ec5, 0x000004f7,
+ 0x00001ec6, 0x000004fa,
+ 0x00001ec7, 0x000004fd,
+ 0x00001ec8, 0x00000500,
+ 0x00001ec9, 0x00000502,
+ 0x00001eca, 0x00000504,
+ 0x00001ecb, 0x00000506,
+ 0x00001ecc, 0x00000508,
+ 0x00001ecd, 0x0000050a,
+ 0x00001ece, 0x0000050c,
+ 0x00001ecf, 0x0000050e,
+ 0x00001ed0, 0x00000510,
+ 0x00001ed1, 0x00000513,
+ 0x00001ed2, 0x00000516,
+ 0x00001ed3, 0x00000519,
+ 0x00001ed4, 0x0000051c,
+ 0x00001ed5, 0x0000051f,
+ 0x00001ed6, 0x00000522,
+ 0x00001ed7, 0x00000525,
+ 0x00001ed8, 0x00000528,
+ 0x00001ed9, 0x0000052b,
+ 0x00001eda, 0x0000052e,
+ 0x00001edb, 0x00000531,
+ 0x00001edc, 0x00000534,
+ 0x00001edd, 0x00000537,
+ 0x00001ede, 0x0000053a,
+ 0x00001edf, 0x0000053d,
+ 0x00001ee0, 0x00000540,
+ 0x00001ee1, 0x00000543,
+ 0x00001ee2, 0x00000546,
+ 0x00001ee3, 0x00000549,
+ 0x00001ee4, 0x0000054c,
+ 0x00001ee5, 0x0000054e,
+ 0x00001ee6, 0x00000550,
+ 0x00001ee7, 0x00000552,
+ 0x00001ee8, 0x00000554,
+ 0x00001ee9, 0x00000557,
+ 0x00001eea, 0x0000055a,
+ 0x00001eeb, 0x0000055d,
+ 0x00001eec, 0x00000560,
+ 0x00001eed, 0x00000563,
+ 0x00001eee, 0x00000566,
+ 0x00001eef, 0x00000569,
+ 0x00001ef0, 0x0000056c,
+ 0x00001ef1, 0x0000056f,
+ 0x00001ef2, 0x00000572,
+ 0x00001ef3, 0x00000574,
+ 0x00001ef4, 0x00000576,
+ 0x00001ef5, 0x00000578,
+ 0x00001ef6, 0x0000057a,
+ 0x00001ef7, 0x0000057c,
+ 0x00001ef8, 0x0000057e,
+ 0x00001ef9, 0x00000580,
+ 0x00001f00, 0x00000582,
+ 0x00001f01, 0x00000584,
+ 0x00001f02, 0x00000586,
+ 0x00001f03, 0x00000589,
+ 0x00001f04, 0x0000058c,
+ 0x00001f05, 0x0000058f,
+ 0x00001f06, 0x00000592,
+ 0x00001f07, 0x00000595,
+ 0x00001f08, 0x00000598,
+ 0x00001f09, 0x0000059a,
+ 0x00001f0a, 0x0000059c,
+ 0x00001f0b, 0x0000059f,
+ 0x00001f0c, 0x000005a2,
+ 0x00001f0d, 0x000005a5,
+ 0x00001f0e, 0x000005a8,
+ 0x00001f0f, 0x000005ab,
+ 0x00001f10, 0x000005ae,
+ 0x00001f11, 0x000005b0,
+ 0x00001f12, 0x000005b2,
+ 0x00001f13, 0x000005b5,
+ 0x00001f14, 0x000005b8,
+ 0x00001f15, 0x000005bb,
+ 0x00001f18, 0x000005be,
+ 0x00001f19, 0x000005c0,
+ 0x00001f1a, 0x000005c2,
+ 0x00001f1b, 0x000005c5,
+ 0x00001f1c, 0x000005c8,
+ 0x00001f1d, 0x000005cb,
+ 0x00001f20, 0x000005ce,
+ 0x00001f21, 0x000005d0,
+ 0x00001f22, 0x000005d2,
+ 0x00001f23, 0x000005d5,
+ 0x00001f24, 0x000005d8,
+ 0x00001f25, 0x000005db,
+ 0x00001f26, 0x000005de,
+ 0x00001f27, 0x000005e1,
+ 0x00001f28, 0x000005e4,
+ 0x00001f29, 0x000005e6,
+ 0x00001f2a, 0x000005e8,
+ 0x00001f2b, 0x000005eb,
+ 0x00001f2c, 0x000005ee,
+ 0x00001f2d, 0x000005f1,
+ 0x00001f2e, 0x000005f4,
+ 0x00001f2f, 0x000005f7,
+ 0x00001f30, 0x000005fa,
+ 0x00001f31, 0x000005fc,
+ 0x00001f32, 0x000005fe,
+ 0x00001f33, 0x00000601,
+ 0x00001f34, 0x00000604,
+ 0x00001f35, 0x00000607,
+ 0x00001f36, 0x0000060a,
+ 0x00001f37, 0x0000060d,
+ 0x00001f38, 0x00000610,
+ 0x00001f39, 0x00000612,
+ 0x00001f3a, 0x00000614,
+ 0x00001f3b, 0x00000617,
+ 0x00001f3c, 0x0000061a,
+ 0x00001f3d, 0x0000061d,
+ 0x00001f3e, 0x00000620,
+ 0x00001f3f, 0x00000623,
+ 0x00001f40, 0x00000626,
+ 0x00001f41, 0x00000628,
+ 0x00001f42, 0x0000062a,
+ 0x00001f43, 0x0000062d,
+ 0x00001f44, 0x00000630,
+ 0x00001f45, 0x00000633,
+ 0x00001f48, 0x00000636,
+ 0x00001f49, 0x00000638,
+ 0x00001f4a, 0x0000063a,
+ 0x00001f4b, 0x0000063d,
+ 0x00001f4c, 0x00000640,
+ 0x00001f4d, 0x00000643,
+ 0x00001f50, 0x00000646,
+ 0x00001f51, 0x00000648,
+ 0x00001f52, 0x0000064a,
+ 0x00001f53, 0x0000064d,
+ 0x00001f54, 0x00000650,
+ 0x00001f55, 0x00000653,
+ 0x00001f56, 0x00000656,
+ 0x00001f57, 0x00000659,
+ 0x00001f59, 0x0000065c,
+ 0x00001f5b, 0x0000065e,
+ 0x00001f5d, 0x00000661,
+ 0x00001f5f, 0x00000664,
+ 0x00001f60, 0x00000667,
+ 0x00001f61, 0x00000669,
+ 0x00001f62, 0x0000066b,
+ 0x00001f63, 0x0000066e,
+ 0x00001f64, 0x00000671,
+ 0x00001f65, 0x00000674,
+ 0x00001f66, 0x00000677,
+ 0x00001f67, 0x0000067a,
+ 0x00001f68, 0x0000067d,
+ 0x00001f69, 0x0000067f,
+ 0x00001f6a, 0x00000681,
+ 0x00001f6b, 0x00000684,
+ 0x00001f6c, 0x00000687,
+ 0x00001f6d, 0x0000068a,
+ 0x00001f6e, 0x0000068d,
+ 0x00001f6f, 0x00000690,
+ 0x00001f70, 0x00000693,
+ 0x00001f71, 0x00000695,
+ 0x00001f72, 0x00000697,
+ 0x00001f73, 0x00000699,
+ 0x00001f74, 0x0000069b,
+ 0x00001f75, 0x0000069d,
+ 0x00001f76, 0x0000069f,
+ 0x00001f77, 0x000006a1,
+ 0x00001f78, 0x000006a3,
+ 0x00001f79, 0x000006a5,
+ 0x00001f7a, 0x000006a7,
+ 0x00001f7b, 0x000006a9,
+ 0x00001f7c, 0x000006ab,
+ 0x00001f7d, 0x000006ad,
+ 0x00001f80, 0x000006af,
+ 0x00001f81, 0x000006b2,
+ 0x00001f82, 0x000006b5,
+ 0x00001f83, 0x000006b9,
+ 0x00001f84, 0x000006bd,
+ 0x00001f85, 0x000006c1,
+ 0x00001f86, 0x000006c5,
+ 0x00001f87, 0x000006c9,
+ 0x00001f88, 0x000006cd,
+ 0x00001f89, 0x000006d0,
+ 0x00001f8a, 0x000006d3,
+ 0x00001f8b, 0x000006d7,
+ 0x00001f8c, 0x000006db,
+ 0x00001f8d, 0x000006df,
+ 0x00001f8e, 0x000006e3,
+ 0x00001f8f, 0x000006e7,
+ 0x00001f90, 0x000006eb,
+ 0x00001f91, 0x000006ee,
+ 0x00001f92, 0x000006f1,
+ 0x00001f93, 0x000006f5,
+ 0x00001f94, 0x000006f9,
+ 0x00001f95, 0x000006fd,
+ 0x00001f96, 0x00000701,
+ 0x00001f97, 0x00000705,
+ 0x00001f98, 0x00000709,
+ 0x00001f99, 0x0000070c,
+ 0x00001f9a, 0x0000070f,
+ 0x00001f9b, 0x00000713,
+ 0x00001f9c, 0x00000717,
+ 0x00001f9d, 0x0000071b,
+ 0x00001f9e, 0x0000071f,
+ 0x00001f9f, 0x00000723,
+ 0x00001fa0, 0x00000727,
+ 0x00001fa1, 0x0000072a,
+ 0x00001fa2, 0x0000072d,
+ 0x00001fa3, 0x00000731,
+ 0x00001fa4, 0x00000735,
+ 0x00001fa5, 0x00000739,
+ 0x00001fa6, 0x0000073d,
+ 0x00001fa7, 0x00000741,
+ 0x00001fa8, 0x00000745,
+ 0x00001fa9, 0x00000748,
+ 0x00001faa, 0x0000074b,
+ 0x00001fab, 0x0000074f,
+ 0x00001fac, 0x00000753,
+ 0x00001fad, 0x00000757,
+ 0x00001fae, 0x0000075b,
+ 0x00001faf, 0x0000075f,
+ 0x00001fb0, 0x00000763,
+ 0x00001fb1, 0x00000765,
+ 0x00001fb2, 0x00000767,
+ 0x00001fb3, 0x0000076a,
+ 0x00001fb4, 0x0000076c,
+ 0x00001fb6, 0x0000076f,
+ 0x00001fb7, 0x00000771,
+ 0x00001fb8, 0x00000774,
+ 0x00001fb9, 0x00000776,
+ 0x00001fba, 0x00000778,
+ 0x00001fbb, 0x0000077a,
+ 0x00001fbc, 0x0000077c,
+ 0x00001fbe, 0x0000077e,
+ 0x00001fc1, 0x0000077f,
+ 0x00001fc2, 0x00000781,
+ 0x00001fc3, 0x00000784,
+ 0x00001fc4, 0x00000786,
+ 0x00001fc6, 0x00000789,
+ 0x00001fc7, 0x0000078b,
+ 0x00001fc8, 0x0000078e,
+ 0x00001fc9, 0x00000790,
+ 0x00001fca, 0x00000792,
+ 0x00001fcb, 0x00000794,
+ 0x00001fcc, 0x00000796,
+ 0x00001fcd, 0x00000798,
+ 0x00001fce, 0x0000079a,
+ 0x00001fcf, 0x0000079c,
+ 0x00001fd0, 0x0000079e,
+ 0x00001fd1, 0x000007a0,
+ 0x00001fd2, 0x000007a2,
+ 0x00001fd3, 0x000007a5,
+ 0x00001fd6, 0x000007a8,
+ 0x00001fd7, 0x000007aa,
+ 0x00001fd8, 0x000007ad,
+ 0x00001fd9, 0x000007af,
+ 0x00001fda, 0x000007b1,
+ 0x00001fdb, 0x000007b3,
+ 0x00001fdd, 0x000007b5,
+ 0x00001fde, 0x000007b7,
+ 0x00001fdf, 0x000007b9,
+ 0x00001fe0, 0x000007bb,
+ 0x00001fe1, 0x000007bd,
+ 0x00001fe2, 0x000007bf,
+ 0x00001fe3, 0x000007c2,
+ 0x00001fe4, 0x000007c5,
+ 0x00001fe5, 0x000007c7,
+ 0x00001fe6, 0x000007c9,
+ 0x00001fe7, 0x000007cb,
+ 0x00001fe8, 0x000007ce,
+ 0x00001fe9, 0x000007d0,
+ 0x00001fea, 0x000007d2,
+ 0x00001feb, 0x000007d4,
+ 0x00001fec, 0x000007d6,
+ 0x00001fed, 0x000007d8,
+ 0x00001fee, 0x000007da,
+ 0x00001fef, 0x000007dc,
+ 0x00001ff2, 0x000007dd,
+ 0x00001ff3, 0x000007e0,
+ 0x00001ff4, 0x000007e2,
+ 0x00001ff6, 0x000007e5,
+ 0x00001ff7, 0x000007e7,
+ 0x00001ff8, 0x000007ea,
+ 0x00001ff9, 0x000007ec,
+ 0x00001ffa, 0x000007ee,
+ 0x00001ffb, 0x000007f0,
+ 0x00001ffc, 0x000007f2,
+ 0x00001ffd, 0x000007f4,
+ 0x00002000, 0x000007f5,
+ 0x00002001, 0x000007f6,
+ 0x00002126, 0x000007f7,
+ 0x0000212a, 0x000007f8,
+ 0x0000212b, 0x000007f9,
+ 0x0000219a, 0x000007fb,
+ 0x0000219b, 0x000007fd,
+ 0x000021ae, 0x000007ff,
+ 0x000021cd, 0x00000801,
+ 0x000021ce, 0x00000803,
+ 0x000021cf, 0x00000805,
+ 0x00002204, 0x00000807,
+ 0x00002209, 0x00000809,
+ 0x0000220c, 0x0000080b,
+ 0x00002224, 0x0000080d,
+ 0x00002226, 0x0000080f,
+ 0x00002241, 0x00000811,
+ 0x00002244, 0x00000813,
+ 0x00002247, 0x00000815,
+ 0x00002249, 0x00000817,
+ 0x00002260, 0x00000819,
+ 0x00002262, 0x0000081b,
+ 0x0000226d, 0x0000081d,
+ 0x0000226e, 0x0000081f,
+ 0x0000226f, 0x00000821,
+ 0x00002270, 0x00000823,
+ 0x00002271, 0x00000825,
+ 0x00002274, 0x00000827,
+ 0x00002275, 0x00000829,
+ 0x00002278, 0x0000082b,
+ 0x00002279, 0x0000082d,
+ 0x00002280, 0x0000082f,
+ 0x00002281, 0x00000831,
+ 0x00002284, 0x00000833,
+ 0x00002285, 0x00000835,
+ 0x00002288, 0x00000837,
+ 0x00002289, 0x00000839,
+ 0x000022ac, 0x0000083b,
+ 0x000022ad, 0x0000083d,
+ 0x000022ae, 0x0000083f,
+ 0x000022af, 0x00000841,
+ 0x000022e0, 0x00000843,
+ 0x000022e1, 0x00000845,
+ 0x000022e2, 0x00000847,
+ 0x000022e3, 0x00000849,
+ 0x000022ea, 0x0000084b,
+ 0x000022eb, 0x0000084d,
+ 0x000022ec, 0x0000084f,
+ 0x000022ed, 0x00000851,
+ 0x00002329, 0x00000853,
+ 0x0000232a, 0x00000854,
+ 0x00002adc, 0x00000855,
+ 0x0000304c, 0x00000857,
+ 0x0000304e, 0x00000859,
+ 0x00003050, 0x0000085b,
+ 0x00003052, 0x0000085d,
+ 0x00003054, 0x0000085f,
+ 0x00003056, 0x00000861,
+ 0x00003058, 0x00000863,
+ 0x0000305a, 0x00000865,
+ 0x0000305c, 0x00000867,
+ 0x0000305e, 0x00000869,
+ 0x00003060, 0x0000086b,
+ 0x00003062, 0x0000086d,
+ 0x00003065, 0x0000086f,
+ 0x00003067, 0x00000871,
+ 0x00003069, 0x00000873,
+ 0x00003070, 0x00000875,
+ 0x00003071, 0x00000877,
+ 0x00003073, 0x00000879,
+ 0x00003074, 0x0000087b,
+ 0x00003076, 0x0000087d,
+ 0x00003077, 0x0000087f,
+ 0x00003079, 0x00000881,
+ 0x0000307a, 0x00000883,
+ 0x0000307c, 0x00000885,
+ 0x0000307d, 0x00000887,
+ 0x00003094, 0x00000889,
+ 0x0000309e, 0x0000088b,
+ 0x000030ac, 0x0000088d,
+ 0x000030ae, 0x0000088f,
+ 0x000030b0, 0x00000891,
+ 0x000030b2, 0x00000893,
+ 0x000030b4, 0x00000895,
+ 0x000030b6, 0x00000897,
+ 0x000030b8, 0x00000899,
+ 0x000030ba, 0x0000089b,
+ 0x000030bc, 0x0000089d,
+ 0x000030be, 0x0000089f,
+ 0x000030c0, 0x000008a1,
+ 0x000030c2, 0x000008a3,
+ 0x000030c5, 0x000008a5,
+ 0x000030c7, 0x000008a7,
+ 0x000030c9, 0x000008a9,
+ 0x000030d0, 0x000008ab,
+ 0x000030d1, 0x000008ad,
+ 0x000030d3, 0x000008af,
+ 0x000030d4, 0x000008b1,
+ 0x000030d6, 0x000008b3,
+ 0x000030d7, 0x000008b5,
+ 0x000030d9, 0x000008b7,
+ 0x000030da, 0x000008b9,
+ 0x000030dc, 0x000008bb,
+ 0x000030dd, 0x000008bd,
+ 0x000030f4, 0x000008bf,
+ 0x000030f7, 0x000008c1,
+ 0x000030f8, 0x000008c3,
+ 0x000030f9, 0x000008c5,
+ 0x000030fa, 0x000008c7,
+ 0x000030fe, 0x000008c9,
+ 0x0000f902, 0x000008cb,
+ 0x0000f903, 0x000008cc,
+ 0x0000f904, 0x000008cd,
+ 0x0000f905, 0x000008ce,
+ 0x0000f906, 0x000008cf,
+ 0x0000f907, 0x000008d0,
+ 0x0000f908, 0x000008d1,
+ 0x0000f909, 0x000008d2,
+ 0x0000f90a, 0x000008d3,
+ 0x0000f90b, 0x000008d4,
+ 0x0000f90c, 0x000008d5,
+ 0x0000f90d, 0x000008d6,
+ 0x0000f90e, 0x000008d7,
+ 0x0000f90f, 0x000008d8,
+ 0x0000f910, 0x000008d9,
+ 0x0000f911, 0x000008da,
+ 0x0000f912, 0x000008db,
+ 0x0000f913, 0x000008dc,
+ 0x0000f914, 0x000008dd,
+ 0x0000f915, 0x000008de,
+ 0x0000f916, 0x000008df,
+ 0x0000f917, 0x000008e0,
+ 0x0000f918, 0x000008e1,
+ 0x0000f919, 0x000008e2,
+ 0x0000f91a, 0x000008e3,
+ 0x0000f91b, 0x000008e4,
+ 0x0000f91c, 0x000008e5,
+ 0x0000f91d, 0x000008e6,
+ 0x0000f91e, 0x000008e7,
+ 0x0000f91f, 0x000008e8,
+ 0x0000f920, 0x000008e9,
+ 0x0000f921, 0x000008ea,
+ 0x0000f922, 0x000008eb,
+ 0x0000f923, 0x000008ec,
+ 0x0000f924, 0x000008ed,
+ 0x0000f925, 0x000008ee,
+ 0x0000f926, 0x000008ef,
+ 0x0000f927, 0x000008f0,
+ 0x0000f928, 0x000008f1,
+ 0x0000f929, 0x000008f2,
+ 0x0000f92a, 0x000008f3,
+ 0x0000f92b, 0x000008f4,
+ 0x0000f92c, 0x000008f5,
+ 0x0000f92d, 0x000008f6,
+ 0x0000f92e, 0x000008f7,
+ 0x0000f92f, 0x000008f8,
+ 0x0000f930, 0x000008f9,
+ 0x0000f931, 0x000008fa,
+ 0x0000f932, 0x000008fb,
+ 0x0000f933, 0x000008fc,
+ 0x0000f934, 0x000008fd,
+ 0x0000f935, 0x000008fe,
+ 0x0000f936, 0x000008ff,
+ 0x0000f937, 0x00000900,
+ 0x0000f938, 0x00000901,
+ 0x0000f939, 0x00000902,
+ 0x0000f93a, 0x00000903,
+ 0x0000f93b, 0x00000904,
+ 0x0000f93c, 0x00000905,
+ 0x0000f93d, 0x00000906,
+ 0x0000f93e, 0x00000907,
+ 0x0000f93f, 0x00000908,
+ 0x0000f940, 0x00000909,
+ 0x0000f941, 0x0000090a,
+ 0x0000f942, 0x0000090b,
+ 0x0000f943, 0x0000090c,
+ 0x0000f944, 0x0000090d,
+ 0x0000f945, 0x0000090e,
+ 0x0000f946, 0x0000090f,
+ 0x0000f947, 0x00000910,
+ 0x0000f948, 0x00000911,
+ 0x0000f949, 0x00000912,
+ 0x0000f94a, 0x00000913,
+ 0x0000f94b, 0x00000914,
+ 0x0000f94c, 0x00000915,
+ 0x0000f94d, 0x00000916,
+ 0x0000f94e, 0x00000917,
+ 0x0000f94f, 0x00000918,
+ 0x0000f950, 0x00000919,
+ 0x0000f951, 0x0000091a,
+ 0x0000f952, 0x0000091b,
+ 0x0000f953, 0x0000091c,
+ 0x0000f954, 0x0000091d,
+ 0x0000f955, 0x0000091e,
+ 0x0000f956, 0x0000091f,
+ 0x0000f957, 0x00000920,
+ 0x0000f958, 0x00000921,
+ 0x0000f959, 0x00000922,
+ 0x0000f95a, 0x00000923,
+ 0x0000f95b, 0x00000924,
+ 0x0000f95c, 0x00000925,
+ 0x0000f95d, 0x00000926,
+ 0x0000f95e, 0x00000927,
+ 0x0000f95f, 0x00000928,
+ 0x0000f960, 0x00000929,
+ 0x0000f961, 0x0000092a,
+ 0x0000f962, 0x0000092b,
+ 0x0000f963, 0x0000092c,
+ 0x0000f964, 0x0000092d,
+ 0x0000f965, 0x0000092e,
+ 0x0000f966, 0x0000092f,
+ 0x0000f967, 0x00000930,
+ 0x0000f968, 0x00000931,
+ 0x0000f969, 0x00000932,
+ 0x0000f96a, 0x00000933,
+ 0x0000f96b, 0x00000934,
+ 0x0000f96c, 0x00000935,
+ 0x0000f96d, 0x00000936,
+ 0x0000f96e, 0x00000937,
+ 0x0000f96f, 0x00000938,
+ 0x0000f970, 0x00000939,
+ 0x0000f971, 0x0000093a,
+ 0x0000f972, 0x0000093b,
+ 0x0000f973, 0x0000093c,
+ 0x0000f974, 0x0000093d,
+ 0x0000f975, 0x0000093e,
+ 0x0000f976, 0x0000093f,
+ 0x0000f977, 0x00000940,
+ 0x0000f978, 0x00000941,
+ 0x0000f979, 0x00000942,
+ 0x0000f97a, 0x00000943,
+ 0x0000f97b, 0x00000944,
+ 0x0000f97c, 0x00000945,
+ 0x0000f97d, 0x00000946,
+ 0x0000f97e, 0x00000947,
+ 0x0000f97f, 0x00000948,
+ 0x0000f980, 0x00000949,
+ 0x0000f981, 0x0000094a,
+ 0x0000f982, 0x0000094b,
+ 0x0000f983, 0x0000094c,
+ 0x0000f984, 0x0000094d,
+ 0x0000f985, 0x0000094e,
+ 0x0000f986, 0x0000094f,
+ 0x0000f987, 0x00000950,
+ 0x0000f988, 0x00000951,
+ 0x0000f989, 0x00000952,
+ 0x0000f98a, 0x00000953,
+ 0x0000f98b, 0x00000954,
+ 0x0000f98c, 0x00000955,
+ 0x0000f98d, 0x00000956,
+ 0x0000f98e, 0x00000957,
+ 0x0000f98f, 0x00000958,
+ 0x0000f990, 0x00000959,
+ 0x0000f991, 0x0000095a,
+ 0x0000f992, 0x0000095b,
+ 0x0000f993, 0x0000095c,
+ 0x0000f994, 0x0000095d,
+ 0x0000f995, 0x0000095e,
+ 0x0000f996, 0x0000095f,
+ 0x0000f997, 0x00000960,
+ 0x0000f998, 0x00000961,
+ 0x0000f999, 0x00000962,
+ 0x0000f99a, 0x00000963,
+ 0x0000f99b, 0x00000964,
+ 0x0000f99c, 0x00000965,
+ 0x0000f99d, 0x00000966,
+ 0x0000f99e, 0x00000967,
+ 0x0000f99f, 0x00000968,
+ 0x0000f9a0, 0x00000969,
+ 0x0000f9a1, 0x0000096a,
+ 0x0000f9a2, 0x0000096b,
+ 0x0000f9a3, 0x0000096c,
+ 0x0000f9a4, 0x0000096d,
+ 0x0000f9a5, 0x0000096e,
+ 0x0000f9a6, 0x0000096f,
+ 0x0000f9a7, 0x00000970,
+ 0x0000f9a8, 0x00000971,
+ 0x0000f9a9, 0x00000972,
+ 0x0000f9aa, 0x00000973,
+ 0x0000f9ab, 0x00000974,
+ 0x0000f9ac, 0x00000975,
+ 0x0000f9ad, 0x00000976,
+ 0x0000f9ae, 0x00000977,
+ 0x0000f9af, 0x00000978,
+ 0x0000f9b0, 0x00000979,
+ 0x0000f9b1, 0x0000097a,
+ 0x0000f9b2, 0x0000097b,
+ 0x0000f9b3, 0x0000097c,
+ 0x0000f9b4, 0x0000097d,
+ 0x0000f9b5, 0x0000097e,
+ 0x0000f9b6, 0x0000097f,
+ 0x0000f9b7, 0x00000980,
+ 0x0000f9b8, 0x00000981,
+ 0x0000f9b9, 0x00000982,
+ 0x0000f9ba, 0x00000983,
+ 0x0000f9bb, 0x00000984,
+ 0x0000f9bc, 0x00000985,
+ 0x0000f9bd, 0x00000986,
+ 0x0000f9be, 0x00000987,
+ 0x0000f9bf, 0x00000988,
+ 0x0000f9c0, 0x00000989,
+ 0x0000f9c1, 0x0000098a,
+ 0x0000f9c2, 0x0000098b,
+ 0x0000f9c3, 0x0000098c,
+ 0x0000f9c4, 0x0000098d,
+ 0x0000f9c5, 0x0000098e,
+ 0x0000f9c6, 0x0000098f,
+ 0x0000f9c7, 0x00000990,
+ 0x0000f9c8, 0x00000991,
+ 0x0000f9c9, 0x00000992,
+ 0x0000f9ca, 0x00000993,
+ 0x0000f9cb, 0x00000994,
+ 0x0000f9cc, 0x00000995,
+ 0x0000f9cd, 0x00000996,
+ 0x0000f9ce, 0x00000997,
+ 0x0000f9cf, 0x00000998,
+ 0x0000f9d0, 0x00000999,
+ 0x0000f9d1, 0x0000099a,
+ 0x0000f9d2, 0x0000099b,
+ 0x0000f9d3, 0x0000099c,
+ 0x0000f9d4, 0x0000099d,
+ 0x0000f9d5, 0x0000099e,
+ 0x0000f9d6, 0x0000099f,
+ 0x0000f9d7, 0x000009a0,
+ 0x0000f9d8, 0x000009a1,
+ 0x0000f9d9, 0x000009a2,
+ 0x0000f9da, 0x000009a3,
+ 0x0000f9db, 0x000009a4,
+ 0x0000f9dc, 0x000009a5,
+ 0x0000f9dd, 0x000009a6,
+ 0x0000f9de, 0x000009a7,
+ 0x0000f9df, 0x000009a8,
+ 0x0000f9e0, 0x000009a9,
+ 0x0000f9e1, 0x000009aa,
+ 0x0000f9e2, 0x000009ab,
+ 0x0000f9e3, 0x000009ac,
+ 0x0000f9e4, 0x000009ad,
+ 0x0000f9e5, 0x000009ae,
+ 0x0000f9e6, 0x000009af,
+ 0x0000f9e7, 0x000009b0,
+ 0x0000f9e8, 0x000009b1,
+ 0x0000f9e9, 0x000009b2,
+ 0x0000f9ea, 0x000009b3,
+ 0x0000f9eb, 0x000009b4,
+ 0x0000f9ec, 0x000009b5,
+ 0x0000f9ed, 0x000009b6,
+ 0x0000f9ee, 0x000009b7,
+ 0x0000f9ef, 0x000009b8,
+ 0x0000f9f0, 0x000009b9,
+ 0x0000f9f1, 0x000009ba,
+ 0x0000f9f2, 0x000009bb,
+ 0x0000f9f3, 0x000009bc,
+ 0x0000f9f4, 0x000009bd,
+ 0x0000f9f5, 0x000009be,
+ 0x0000f9f6, 0x000009bf,
+ 0x0000f9f7, 0x000009c0,
+ 0x0000f9f8, 0x000009c1,
+ 0x0000f9f9, 0x000009c2,
+ 0x0000f9fa, 0x000009c3,
+ 0x0000f9fb, 0x000009c4,
+ 0x0000f9fc, 0x000009c5,
+ 0x0000f9fd, 0x000009c6,
+ 0x0000f9fe, 0x000009c7,
+ 0x0000f9ff, 0x000009c8,
+ 0x0000fa00, 0x000009c9,
+ 0x0000fa01, 0x000009ca,
+ 0x0000fa02, 0x000009cb,
+ 0x0000fa03, 0x000009cc,
+ 0x0000fa04, 0x000009cd,
+ 0x0000fa05, 0x000009ce,
+ 0x0000fa06, 0x000009cf,
+ 0x0000fa07, 0x000009d0,
+ 0x0000fa08, 0x000009d1,
+ 0x0000fa09, 0x000009d2,
+ 0x0000fa0a, 0x000009d3,
+ 0x0000fa0b, 0x000009d4,
+ 0x0000fa0c, 0x000009d5,
+ 0x0000fa0d, 0x000009d6,
+ 0x0000fa10, 0x000009d7,
+ 0x0000fa12, 0x000009d8,
+ 0x0000fa15, 0x000009d9,
+ 0x0000fa16, 0x000009da,
+ 0x0000fa17, 0x000009db,
+ 0x0000fa18, 0x000009dc,
+ 0x0000fa19, 0x000009dd,
+ 0x0000fa1a, 0x000009de,
+ 0x0000fa1b, 0x000009df,
+ 0x0000fa1c, 0x000009e0,
+ 0x0000fa1d, 0x000009e1,
+ 0x0000fa1e, 0x000009e2,
+ 0x0000fa20, 0x000009e3,
+ 0x0000fa22, 0x000009e4,
+ 0x0000fa25, 0x000009e5,
+ 0x0000fa26, 0x000009e6,
+ 0x0000fa2a, 0x000009e7,
+ 0x0000fa2b, 0x000009e8,
+ 0x0000fa2c, 0x000009e9,
+ 0x0000fa2d, 0x000009ea,
+ 0x0000fa30, 0x000009eb,
+ 0x0000fa31, 0x000009ec,
+ 0x0000fa32, 0x000009ed,
+ 0x0000fa33, 0x000009ee,
+ 0x0000fa34, 0x000009ef,
+ 0x0000fa35, 0x000009f0,
+ 0x0000fa36, 0x000009f1,
+ 0x0000fa37, 0x000009f2,
+ 0x0000fa38, 0x000009f3,
+ 0x0000fa39, 0x000009f4,
+ 0x0000fa3a, 0x000009f5,
+ 0x0000fa3b, 0x000009f6,
+ 0x0000fa3c, 0x000009f7,
+ 0x0000fa3d, 0x000009f8,
+ 0x0000fa3e, 0x000009f9,
+ 0x0000fa3f, 0x000009fa,
+ 0x0000fa40, 0x000009fb,
+ 0x0000fa41, 0x000009fc,
+ 0x0000fa42, 0x000009fd,
+ 0x0000fa43, 0x000009fe,
+ 0x0000fa44, 0x000009ff,
+ 0x0000fa45, 0x00000a00,
+ 0x0000fa46, 0x00000a01,
+ 0x0000fa47, 0x00000a02,
+ 0x0000fa48, 0x00000a03,
+ 0x0000fa49, 0x00000a04,
+ 0x0000fa4a, 0x00000a05,
+ 0x0000fa4b, 0x00000a06,
+ 0x0000fa4c, 0x00000a07,
+ 0x0000fa4d, 0x00000a08,
+ 0x0000fa4e, 0x00000a09,
+ 0x0000fa4f, 0x00000a0a,
+ 0x0000fa50, 0x00000a0b,
+ 0x0000fa51, 0x00000a0c,
+ 0x0000fa52, 0x00000a0d,
+ 0x0000fa53, 0x00000a0e,
+ 0x0000fa54, 0x00000a0f,
+ 0x0000fa55, 0x00000a10,
+ 0x0000fa56, 0x00000a11,
+ 0x0000fa57, 0x00000a12,
+ 0x0000fa58, 0x00000a13,
+ 0x0000fa59, 0x00000a14,
+ 0x0000fa5a, 0x00000a15,
+ 0x0000fa5b, 0x00000a16,
+ 0x0000fa5c, 0x00000a17,
+ 0x0000fa5d, 0x00000a18,
+ 0x0000fa5e, 0x00000a19,
+ 0x0000fa5f, 0x00000a1a,
+ 0x0000fa60, 0x00000a1b,
+ 0x0000fa61, 0x00000a1c,
+ 0x0000fa62, 0x00000a1d,
+ 0x0000fa63, 0x00000a1e,
+ 0x0000fa64, 0x00000a1f,
+ 0x0000fa65, 0x00000a20,
+ 0x0000fa66, 0x00000a21,
+ 0x0000fa67, 0x00000a22,
+ 0x0000fa68, 0x00000a23,
+ 0x0000fa69, 0x00000a24,
+ 0x0000fa6a, 0x00000a25,
+ 0x0000fb1d, 0x00000a26,
+ 0x0000fb1f, 0x00000a28,
+ 0x0000fb2a, 0x00000a2a,
+ 0x0000fb2b, 0x00000a2c,
+ 0x0000fb2c, 0x00000a2e,
+ 0x0000fb2d, 0x00000a31,
+ 0x0000fb2e, 0x00000a34,
+ 0x0000fb2f, 0x00000a36,
+ 0x0000fb30, 0x00000a38,
+ 0x0000fb31, 0x00000a3a,
+ 0x0000fb32, 0x00000a3c,
+ 0x0000fb33, 0x00000a3e,
+ 0x0000fb34, 0x00000a40,
+ 0x0000fb35, 0x00000a42,
+ 0x0000fb36, 0x00000a44,
+ 0x0000fb38, 0x00000a46,
+ 0x0000fb39, 0x00000a48,
+ 0x0000fb3a, 0x00000a4a,
+ 0x0000fb3b, 0x00000a4c,
+ 0x0000fb3c, 0x00000a4e,
+ 0x0000fb3e, 0x00000a50,
+ 0x0000fb40, 0x00000a52,
+ 0x0000fb41, 0x00000a54,
+ 0x0000fb43, 0x00000a56,
+ 0x0000fb44, 0x00000a58,
+ 0x0000fb46, 0x00000a5a,
+ 0x0000fb47, 0x00000a5c,
+ 0x0000fb48, 0x00000a5e,
+ 0x0000fb49, 0x00000a60,
+ 0x0000fb4a, 0x00000a62,
+ 0x0000fb4b, 0x00000a64,
+ 0x0000fb4c, 0x00000a66,
+ 0x0000fb4d, 0x00000a68,
+ 0x0000fb4e, 0x00000a6a,
+ 0x0001d15e, 0x00000a6c,
+ 0x0001d15f, 0x00000a6e,
+ 0x0001d160, 0x00000a70,
+ 0x0001d161, 0x00000a73,
+ 0x0001d162, 0x00000a76,
+ 0x0001d163, 0x00000a79,
+ 0x0001d164, 0x00000a7c,
+ 0x0001d1bb, 0x00000a7f,
+ 0x0001d1bc, 0x00000a81,
+ 0x0001d1bd, 0x00000a83,
+ 0x0001d1be, 0x00000a86,
+ 0x0001d1bf, 0x00000a89,
+ 0x0001d1c0, 0x00000a8c,
+ 0x0002f800, 0x00000a8f,
+ 0x0002f801, 0x00000a90,
+ 0x0002f802, 0x00000a91,
+ 0x0002f803, 0x00000a92,
+ 0x0002f804, 0x00000a93,
+ 0x0002f805, 0x00000a94,
+ 0x0002f806, 0x00000a95,
+ 0x0002f807, 0x00000a96,
+ 0x0002f808, 0x00000a97,
+ 0x0002f809, 0x00000a98,
+ 0x0002f80a, 0x00000a99,
+ 0x0002f80b, 0x00000a9a,
+ 0x0002f80c, 0x00000a9b,
+ 0x0002f80d, 0x00000a9c,
+ 0x0002f80e, 0x00000a9d,
+ 0x0002f80f, 0x00000a9e,
+ 0x0002f810, 0x00000a9f,
+ 0x0002f811, 0x00000aa0,
+ 0x0002f812, 0x00000aa1,
+ 0x0002f813, 0x00000aa2,
+ 0x0002f814, 0x00000aa3,
+ 0x0002f815, 0x00000aa4,
+ 0x0002f816, 0x00000aa5,
+ 0x0002f817, 0x00000aa6,
+ 0x0002f818, 0x00000aa7,
+ 0x0002f819, 0x00000aa8,
+ 0x0002f81a, 0x00000aa9,
+ 0x0002f81b, 0x00000aaa,
+ 0x0002f81c, 0x00000aab,
+ 0x0002f81d, 0x00000aac,
+ 0x0002f81e, 0x00000aad,
+ 0x0002f81f, 0x00000aae,
+ 0x0002f820, 0x00000aaf,
+ 0x0002f821, 0x00000ab0,
+ 0x0002f822, 0x00000ab1,
+ 0x0002f823, 0x00000ab2,
+ 0x0002f824, 0x00000ab3,
+ 0x0002f825, 0x00000ab4,
+ 0x0002f826, 0x00000ab5,
+ 0x0002f827, 0x00000ab6,
+ 0x0002f828, 0x00000ab7,
+ 0x0002f829, 0x00000ab8,
+ 0x0002f82a, 0x00000ab9,
+ 0x0002f82b, 0x00000aba,
+ 0x0002f82c, 0x00000abb,
+ 0x0002f82d, 0x00000abc,
+ 0x0002f82e, 0x00000abd,
+ 0x0002f82f, 0x00000abe,
+ 0x0002f830, 0x00000abf,
+ 0x0002f831, 0x00000ac0,
+ 0x0002f832, 0x00000ac1,
+ 0x0002f833, 0x00000ac2,
+ 0x0002f834, 0x00000ac3,
+ 0x0002f835, 0x00000ac4,
+ 0x0002f836, 0x00000ac5,
+ 0x0002f837, 0x00000ac6,
+ 0x0002f838, 0x00000ac7,
+ 0x0002f839, 0x00000ac8,
+ 0x0002f83a, 0x00000ac9,
+ 0x0002f83b, 0x00000aca,
+ 0x0002f83c, 0x00000acb,
+ 0x0002f83d, 0x00000acc,
+ 0x0002f83e, 0x00000acd,
+ 0x0002f83f, 0x00000ace,
+ 0x0002f840, 0x00000acf,
+ 0x0002f841, 0x00000ad0,
+ 0x0002f842, 0x00000ad1,
+ 0x0002f843, 0x00000ad2,
+ 0x0002f844, 0x00000ad3,
+ 0x0002f845, 0x00000ad4,
+ 0x0002f846, 0x00000ad5,
+ 0x0002f847, 0x00000ad6,
+ 0x0002f848, 0x00000ad7,
+ 0x0002f849, 0x00000ad8,
+ 0x0002f84a, 0x00000ad9,
+ 0x0002f84b, 0x00000ada,
+ 0x0002f84c, 0x00000adb,
+ 0x0002f84d, 0x00000adc,
+ 0x0002f84e, 0x00000add,
+ 0x0002f84f, 0x00000ade,
+ 0x0002f850, 0x00000adf,
+ 0x0002f851, 0x00000ae0,
+ 0x0002f852, 0x00000ae1,
+ 0x0002f853, 0x00000ae2,
+ 0x0002f854, 0x00000ae3,
+ 0x0002f855, 0x00000ae4,
+ 0x0002f856, 0x00000ae5,
+ 0x0002f857, 0x00000ae6,
+ 0x0002f858, 0x00000ae7,
+ 0x0002f859, 0x00000ae8,
+ 0x0002f85a, 0x00000ae9,
+ 0x0002f85b, 0x00000aea,
+ 0x0002f85c, 0x00000aeb,
+ 0x0002f85d, 0x00000aec,
+ 0x0002f85e, 0x00000aed,
+ 0x0002f85f, 0x00000aee,
+ 0x0002f860, 0x00000aef,
+ 0x0002f861, 0x00000af0,
+ 0x0002f862, 0x00000af1,
+ 0x0002f863, 0x00000af2,
+ 0x0002f864, 0x00000af3,
+ 0x0002f865, 0x00000af4,
+ 0x0002f866, 0x00000af5,
+ 0x0002f867, 0x00000af6,
+ 0x0002f868, 0x00000af7,
+ 0x0002f869, 0x00000af8,
+ 0x0002f86a, 0x00000af9,
+ 0x0002f86b, 0x00000afa,
+ 0x0002f86c, 0x00000afb,
+ 0x0002f86d, 0x00000afc,
+ 0x0002f86e, 0x00000afd,
+ 0x0002f86f, 0x00000afe,
+ 0x0002f870, 0x00000aff,
+ 0x0002f871, 0x00000b00,
+ 0x0002f872, 0x00000b01,
+ 0x0002f873, 0x00000b02,
+ 0x0002f874, 0x00000b03,
+ 0x0002f875, 0x00000b04,
+ 0x0002f876, 0x00000b05,
+ 0x0002f877, 0x00000b06,
+ 0x0002f878, 0x00000b07,
+ 0x0002f879, 0x00000b08,
+ 0x0002f87a, 0x00000b09,
+ 0x0002f87b, 0x00000b0a,
+ 0x0002f87c, 0x00000b0b,
+ 0x0002f87d, 0x00000b0c,
+ 0x0002f87e, 0x00000b0d,
+ 0x0002f87f, 0x00000b0e,
+ 0x0002f880, 0x00000b0f,
+ 0x0002f881, 0x00000b10,
+ 0x0002f882, 0x00000b11,
+ 0x0002f883, 0x00000b12,
+ 0x0002f884, 0x00000b13,
+ 0x0002f885, 0x00000b14,
+ 0x0002f886, 0x00000b15,
+ 0x0002f887, 0x00000b16,
+ 0x0002f888, 0x00000b17,
+ 0x0002f889, 0x00000b18,
+ 0x0002f88a, 0x00000b19,
+ 0x0002f88b, 0x00000b1a,
+ 0x0002f88c, 0x00000b1b,
+ 0x0002f88d, 0x00000b1c,
+ 0x0002f88e, 0x00000b1d,
+ 0x0002f88f, 0x00000b1e,
+ 0x0002f890, 0x00000b1f,
+ 0x0002f891, 0x00000b20,
+ 0x0002f892, 0x00000b21,
+ 0x0002f893, 0x00000b22,
+ 0x0002f894, 0x00000b23,
+ 0x0002f895, 0x00000b24,
+ 0x0002f896, 0x00000b25,
+ 0x0002f897, 0x00000b26,
+ 0x0002f898, 0x00000b27,
+ 0x0002f899, 0x00000b28,
+ 0x0002f89a, 0x00000b29,
+ 0x0002f89b, 0x00000b2a,
+ 0x0002f89c, 0x00000b2b,
+ 0x0002f89d, 0x00000b2c,
+ 0x0002f89e, 0x00000b2d,
+ 0x0002f89f, 0x00000b2e,
+ 0x0002f8a0, 0x00000b2f,
+ 0x0002f8a1, 0x00000b30,
+ 0x0002f8a2, 0x00000b31,
+ 0x0002f8a3, 0x00000b32,
+ 0x0002f8a4, 0x00000b33,
+ 0x0002f8a5, 0x00000b34,
+ 0x0002f8a6, 0x00000b35,
+ 0x0002f8a7, 0x00000b36,
+ 0x0002f8a8, 0x00000b37,
+ 0x0002f8a9, 0x00000b38,
+ 0x0002f8aa, 0x00000b39,
+ 0x0002f8ab, 0x00000b3a,
+ 0x0002f8ac, 0x00000b3b,
+ 0x0002f8ad, 0x00000b3c,
+ 0x0002f8ae, 0x00000b3d,
+ 0x0002f8af, 0x00000b3e,
+ 0x0002f8b0, 0x00000b3f,
+ 0x0002f8b1, 0x00000b40,
+ 0x0002f8b2, 0x00000b41,
+ 0x0002f8b3, 0x00000b42,
+ 0x0002f8b4, 0x00000b43,
+ 0x0002f8b5, 0x00000b44,
+ 0x0002f8b6, 0x00000b45,
+ 0x0002f8b7, 0x00000b46,
+ 0x0002f8b8, 0x00000b47,
+ 0x0002f8b9, 0x00000b48,
+ 0x0002f8ba, 0x00000b49,
+ 0x0002f8bb, 0x00000b4a,
+ 0x0002f8bc, 0x00000b4b,
+ 0x0002f8bd, 0x00000b4c,
+ 0x0002f8be, 0x00000b4d,
+ 0x0002f8bf, 0x00000b4e,
+ 0x0002f8c0, 0x00000b4f,
+ 0x0002f8c1, 0x00000b50,
+ 0x0002f8c2, 0x00000b51,
+ 0x0002f8c3, 0x00000b52,
+ 0x0002f8c4, 0x00000b53,
+ 0x0002f8c5, 0x00000b54,
+ 0x0002f8c6, 0x00000b55,
+ 0x0002f8c7, 0x00000b56,
+ 0x0002f8c8, 0x00000b57,
+ 0x0002f8c9, 0x00000b58,
+ 0x0002f8ca, 0x00000b59,
+ 0x0002f8cb, 0x00000b5a,
+ 0x0002f8cc, 0x00000b5b,
+ 0x0002f8cd, 0x00000b5c,
+ 0x0002f8ce, 0x00000b5d,
+ 0x0002f8cf, 0x00000b5e,
+ 0x0002f8d0, 0x00000b5f,
+ 0x0002f8d1, 0x00000b60,
+ 0x0002f8d2, 0x00000b61,
+ 0x0002f8d3, 0x00000b62,
+ 0x0002f8d4, 0x00000b63,
+ 0x0002f8d5, 0x00000b64,
+ 0x0002f8d6, 0x00000b65,
+ 0x0002f8d7, 0x00000b66,
+ 0x0002f8d8, 0x00000b67,
+ 0x0002f8d9, 0x00000b68,
+ 0x0002f8da, 0x00000b69,
+ 0x0002f8db, 0x00000b6a,
+ 0x0002f8dc, 0x00000b6b,
+ 0x0002f8dd, 0x00000b6c,
+ 0x0002f8de, 0x00000b6d,
+ 0x0002f8df, 0x00000b6e,
+ 0x0002f8e0, 0x00000b6f,
+ 0x0002f8e1, 0x00000b70,
+ 0x0002f8e2, 0x00000b71,
+ 0x0002f8e3, 0x00000b72,
+ 0x0002f8e4, 0x00000b73,
+ 0x0002f8e5, 0x00000b74,
+ 0x0002f8e6, 0x00000b75,
+ 0x0002f8e7, 0x00000b76,
+ 0x0002f8e8, 0x00000b77,
+ 0x0002f8e9, 0x00000b78,
+ 0x0002f8ea, 0x00000b79,
+ 0x0002f8eb, 0x00000b7a,
+ 0x0002f8ec, 0x00000b7b,
+ 0x0002f8ed, 0x00000b7c,
+ 0x0002f8ee, 0x00000b7d,
+ 0x0002f8ef, 0x00000b7e,
+ 0x0002f8f0, 0x00000b7f,
+ 0x0002f8f1, 0x00000b80,
+ 0x0002f8f2, 0x00000b81,
+ 0x0002f8f3, 0x00000b82,
+ 0x0002f8f4, 0x00000b83,
+ 0x0002f8f5, 0x00000b84,
+ 0x0002f8f6, 0x00000b85,
+ 0x0002f8f7, 0x00000b86,
+ 0x0002f8f8, 0x00000b87,
+ 0x0002f8f9, 0x00000b88,
+ 0x0002f8fa, 0x00000b89,
+ 0x0002f8fb, 0x00000b8a,
+ 0x0002f8fc, 0x00000b8b,
+ 0x0002f8fd, 0x00000b8c,
+ 0x0002f8fe, 0x00000b8d,
+ 0x0002f8ff, 0x00000b8e,
+ 0x0002f900, 0x00000b8f,
+ 0x0002f901, 0x00000b90,
+ 0x0002f902, 0x00000b91,
+ 0x0002f903, 0x00000b92,
+ 0x0002f904, 0x00000b93,
+ 0x0002f905, 0x00000b94,
+ 0x0002f906, 0x00000b95,
+ 0x0002f907, 0x00000b96,
+ 0x0002f908, 0x00000b97,
+ 0x0002f909, 0x00000b98,
+ 0x0002f90a, 0x00000b99,
+ 0x0002f90b, 0x00000b9a,
+ 0x0002f90c, 0x00000b9b,
+ 0x0002f90d, 0x00000b9c,
+ 0x0002f90e, 0x00000b9d,
+ 0x0002f90f, 0x00000b9e,
+ 0x0002f910, 0x00000b9f,
+ 0x0002f911, 0x00000ba0,
+ 0x0002f912, 0x00000ba1,
+ 0x0002f913, 0x00000ba2,
+ 0x0002f914, 0x00000ba3,
+ 0x0002f915, 0x00000ba4,
+ 0x0002f916, 0x00000ba5,
+ 0x0002f917, 0x00000ba6,
+ 0x0002f918, 0x00000ba7,
+ 0x0002f919, 0x00000ba8,
+ 0x0002f91a, 0x00000ba9,
+ 0x0002f91b, 0x00000baa,
+ 0x0002f91c, 0x00000bab,
+ 0x0002f91d, 0x00000bac,
+ 0x0002f91e, 0x00000bad,
+ 0x0002f91f, 0x00000bae,
+ 0x0002f920, 0x00000baf,
+ 0x0002f921, 0x00000bb0,
+ 0x0002f922, 0x00000bb1,
+ 0x0002f923, 0x00000bb2,
+ 0x0002f924, 0x00000bb3,
+ 0x0002f925, 0x00000bb4,
+ 0x0002f926, 0x00000bb5,
+ 0x0002f927, 0x00000bb6,
+ 0x0002f928, 0x00000bb7,
+ 0x0002f929, 0x00000bb8,
+ 0x0002f92a, 0x00000bb9,
+ 0x0002f92b, 0x00000bba,
+ 0x0002f92c, 0x00000bbb,
+ 0x0002f92d, 0x00000bbc,
+ 0x0002f92e, 0x00000bbd,
+ 0x0002f92f, 0x00000bbe,
+ 0x0002f930, 0x00000bbf,
+ 0x0002f931, 0x00000bc0,
+ 0x0002f932, 0x00000bc1,
+ 0x0002f933, 0x00000bc2,
+ 0x0002f934, 0x00000bc3,
+ 0x0002f935, 0x00000bc4,
+ 0x0002f936, 0x00000bc5,
+ 0x0002f937, 0x00000bc6,
+ 0x0002f938, 0x00000bc7,
+ 0x0002f939, 0x00000bc8,
+ 0x0002f93a, 0x00000bc9,
+ 0x0002f93b, 0x00000bca,
+ 0x0002f93c, 0x00000bcb,
+ 0x0002f93d, 0x00000bcc,
+ 0x0002f93e, 0x00000bcd,
+ 0x0002f93f, 0x00000bce,
+ 0x0002f940, 0x00000bcf,
+ 0x0002f941, 0x00000bd0,
+ 0x0002f942, 0x00000bd1,
+ 0x0002f943, 0x00000bd2,
+ 0x0002f944, 0x00000bd3,
+ 0x0002f945, 0x00000bd4,
+ 0x0002f946, 0x00000bd5,
+ 0x0002f947, 0x00000bd6,
+ 0x0002f948, 0x00000bd7,
+ 0x0002f949, 0x00000bd8,
+ 0x0002f94a, 0x00000bd9,
+ 0x0002f94b, 0x00000bda,
+ 0x0002f94c, 0x00000bdb,
+ 0x0002f94d, 0x00000bdc,
+ 0x0002f94e, 0x00000bdd,
+ 0x0002f94f, 0x00000bde,
+ 0x0002f950, 0x00000bdf,
+ 0x0002f951, 0x00000be0,
+ 0x0002f952, 0x00000be1,
+ 0x0002f953, 0x00000be2,
+ 0x0002f954, 0x00000be3,
+ 0x0002f955, 0x00000be4,
+ 0x0002f956, 0x00000be5,
+ 0x0002f957, 0x00000be6,
+ 0x0002f958, 0x00000be7,
+ 0x0002f959, 0x00000be8,
+ 0x0002f95a, 0x00000be9,
+ 0x0002f95b, 0x00000bea,
+ 0x0002f95c, 0x00000beb,
+ 0x0002f95d, 0x00000bec,
+ 0x0002f95e, 0x00000bed,
+ 0x0002f95f, 0x00000bee,
+ 0x0002f960, 0x00000bef,
+ 0x0002f961, 0x00000bf0,
+ 0x0002f962, 0x00000bf1,
+ 0x0002f963, 0x00000bf2,
+ 0x0002f964, 0x00000bf3,
+ 0x0002f965, 0x00000bf4,
+ 0x0002f966, 0x00000bf5,
+ 0x0002f967, 0x00000bf6,
+ 0x0002f968, 0x00000bf7,
+ 0x0002f969, 0x00000bf8,
+ 0x0002f96a, 0x00000bf9,
+ 0x0002f96b, 0x00000bfa,
+ 0x0002f96c, 0x00000bfb,
+ 0x0002f96d, 0x00000bfc,
+ 0x0002f96e, 0x00000bfd,
+ 0x0002f96f, 0x00000bfe,
+ 0x0002f970, 0x00000bff,
+ 0x0002f971, 0x00000c00,
+ 0x0002f972, 0x00000c01,
+ 0x0002f973, 0x00000c02,
+ 0x0002f974, 0x00000c03,
+ 0x0002f975, 0x00000c04,
+ 0x0002f976, 0x00000c05,
+ 0x0002f977, 0x00000c06,
+ 0x0002f978, 0x00000c07,
+ 0x0002f979, 0x00000c08,
+ 0x0002f97a, 0x00000c09,
+ 0x0002f97b, 0x00000c0a,
+ 0x0002f97c, 0x00000c0b,
+ 0x0002f97d, 0x00000c0c,
+ 0x0002f97e, 0x00000c0d,
+ 0x0002f97f, 0x00000c0e,
+ 0x0002f980, 0x00000c0f,
+ 0x0002f981, 0x00000c10,
+ 0x0002f982, 0x00000c11,
+ 0x0002f983, 0x00000c12,
+ 0x0002f984, 0x00000c13,
+ 0x0002f985, 0x00000c14,
+ 0x0002f986, 0x00000c15,
+ 0x0002f987, 0x00000c16,
+ 0x0002f988, 0x00000c17,
+ 0x0002f989, 0x00000c18,
+ 0x0002f98a, 0x00000c19,
+ 0x0002f98b, 0x00000c1a,
+ 0x0002f98c, 0x00000c1b,
+ 0x0002f98d, 0x00000c1c,
+ 0x0002f98e, 0x00000c1d,
+ 0x0002f98f, 0x00000c1e,
+ 0x0002f990, 0x00000c1f,
+ 0x0002f991, 0x00000c20,
+ 0x0002f992, 0x00000c21,
+ 0x0002f993, 0x00000c22,
+ 0x0002f994, 0x00000c23,
+ 0x0002f995, 0x00000c24,
+ 0x0002f996, 0x00000c25,
+ 0x0002f997, 0x00000c26,
+ 0x0002f998, 0x00000c27,
+ 0x0002f999, 0x00000c28,
+ 0x0002f99a, 0x00000c29,
+ 0x0002f99b, 0x00000c2a,
+ 0x0002f99c, 0x00000c2b,
+ 0x0002f99d, 0x00000c2c,
+ 0x0002f99e, 0x00000c2d,
+ 0x0002f99f, 0x00000c2e,
+ 0x0002f9a0, 0x00000c2f,
+ 0x0002f9a1, 0x00000c30,
+ 0x0002f9a2, 0x00000c31,
+ 0x0002f9a3, 0x00000c32,
+ 0x0002f9a4, 0x00000c33,
+ 0x0002f9a5, 0x00000c34,
+ 0x0002f9a6, 0x00000c35,
+ 0x0002f9a7, 0x00000c36,
+ 0x0002f9a8, 0x00000c37,
+ 0x0002f9a9, 0x00000c38,
+ 0x0002f9aa, 0x00000c39,
+ 0x0002f9ab, 0x00000c3a,
+ 0x0002f9ac, 0x00000c3b,
+ 0x0002f9ad, 0x00000c3c,
+ 0x0002f9ae, 0x00000c3d,
+ 0x0002f9af, 0x00000c3e,
+ 0x0002f9b0, 0x00000c3f,
+ 0x0002f9b1, 0x00000c40,
+ 0x0002f9b2, 0x00000c41,
+ 0x0002f9b3, 0x00000c42,
+ 0x0002f9b4, 0x00000c43,
+ 0x0002f9b5, 0x00000c44,
+ 0x0002f9b6, 0x00000c45,
+ 0x0002f9b7, 0x00000c46,
+ 0x0002f9b8, 0x00000c47,
+ 0x0002f9b9, 0x00000c48,
+ 0x0002f9ba, 0x00000c49,
+ 0x0002f9bb, 0x00000c4a,
+ 0x0002f9bc, 0x00000c4b,
+ 0x0002f9bd, 0x00000c4c,
+ 0x0002f9be, 0x00000c4d,
+ 0x0002f9bf, 0x00000c4e,
+ 0x0002f9c0, 0x00000c4f,
+ 0x0002f9c1, 0x00000c50,
+ 0x0002f9c2, 0x00000c51,
+ 0x0002f9c3, 0x00000c52,
+ 0x0002f9c4, 0x00000c53,
+ 0x0002f9c5, 0x00000c54,
+ 0x0002f9c6, 0x00000c55,
+ 0x0002f9c7, 0x00000c56,
+ 0x0002f9c8, 0x00000c57,
+ 0x0002f9c9, 0x00000c58,
+ 0x0002f9ca, 0x00000c59,
+ 0x0002f9cb, 0x00000c5a,
+ 0x0002f9cc, 0x00000c5b,
+ 0x0002f9cd, 0x00000c5c,
+ 0x0002f9ce, 0x00000c5d,
+ 0x0002f9cf, 0x00000c5e,
+ 0x0002f9d0, 0x00000c5f,
+ 0x0002f9d1, 0x00000c60,
+ 0x0002f9d2, 0x00000c61,
+ 0x0002f9d3, 0x00000c62,
+ 0x0002f9d4, 0x00000c63,
+ 0x0002f9d5, 0x00000c64,
+ 0x0002f9d6, 0x00000c65,
+ 0x0002f9d7, 0x00000c66,
+ 0x0002f9d8, 0x00000c67,
+ 0x0002f9d9, 0x00000c68,
+ 0x0002f9da, 0x00000c69,
+ 0x0002f9db, 0x00000c6a,
+ 0x0002f9dc, 0x00000c6b,
+ 0x0002f9dd, 0x00000c6c,
+ 0x0002f9de, 0x00000c6d,
+ 0x0002f9df, 0x00000c6e,
+ 0x0002f9e0, 0x00000c6f,
+ 0x0002f9e1, 0x00000c70,
+ 0x0002f9e2, 0x00000c71,
+ 0x0002f9e3, 0x00000c72,
+ 0x0002f9e4, 0x00000c73,
+ 0x0002f9e5, 0x00000c74,
+ 0x0002f9e6, 0x00000c75,
+ 0x0002f9e7, 0x00000c76,
+ 0x0002f9e8, 0x00000c77,
+ 0x0002f9e9, 0x00000c78,
+ 0x0002f9ea, 0x00000c79,
+ 0x0002f9eb, 0x00000c7a,
+ 0x0002f9ec, 0x00000c7b,
+ 0x0002f9ed, 0x00000c7c,
+ 0x0002f9ee, 0x00000c7d,
+ 0x0002f9ef, 0x00000c7e,
+ 0x0002f9f0, 0x00000c7f,
+ 0x0002f9f1, 0x00000c80,
+ 0x0002f9f2, 0x00000c81,
+ 0x0002f9f3, 0x00000c82,
+ 0x0002f9f4, 0x00000c83,
+ 0x0002f9f5, 0x00000c84,
+ 0x0002f9f6, 0x00000c85,
+ 0x0002f9f7, 0x00000c86,
+ 0x0002f9f8, 0x00000c87,
+ 0x0002f9f9, 0x00000c88,
+ 0x0002f9fa, 0x00000c89,
+ 0x0002f9fb, 0x00000c8a,
+ 0x0002f9fc, 0x00000c8b,
+ 0x0002f9fd, 0x00000c8c,
+ 0x0002f9fe, 0x00000c8d,
+ 0x0002f9ff, 0x00000c8e,
+ 0x0002fa00, 0x00000c8f,
+ 0x0002fa01, 0x00000c90,
+ 0x0002fa02, 0x00000c91,
+ 0x0002fa03, 0x00000c92,
+ 0x0002fa04, 0x00000c93,
+ 0x0002fa05, 0x00000c94,
+ 0x0002fa06, 0x00000c95,
+ 0x0002fa07, 0x00000c96,
+ 0x0002fa08, 0x00000c97,
+ 0x0002fa09, 0x00000c98,
+ 0x0002fa0a, 0x00000c99,
+ 0x0002fa0b, 0x00000c9a,
+ 0x0002fa0c, 0x00000c9b,
+ 0x0002fa0d, 0x00000c9c,
+ 0x0002fa0e, 0x00000c9d,
+ 0x0002fa0f, 0x00000c9e,
+ 0x0002fa10, 0x00000c9f,
+ 0x0002fa11, 0x00000ca0,
+ 0x0002fa12, 0x00000ca1,
+ 0x0002fa13, 0x00000ca2,
+ 0x0002fa14, 0x00000ca3,
+ 0x0002fa15, 0x00000ca4,
+ 0x0002fa16, 0x00000ca5,
+ 0x0002fa17, 0x00000ca6,
+ 0x0002fa18, 0x00000ca7,
+ 0x0002fa19, 0x00000ca8,
+ 0x0002fa1a, 0x00000ca9,
+ 0x0002fa1b, 0x00000caa,
+ 0x0002fa1c, 0x00000cab,
+ 0x0002fa1d, 0x00000cac,
+ 0x00000cad
+};
+
+static const ac_uint4 _ucdcmp_decomp[] = {
+ 0x00000041, 0x00000300, 0x00000041, 0x00000301,
+ 0x00000041, 0x00000302, 0x00000041, 0x00000303,
+ 0x00000041, 0x00000308, 0x00000041, 0x0000030a,
+ 0x00000043, 0x00000327, 0x00000045, 0x00000300,
+ 0x00000045, 0x00000301, 0x00000045, 0x00000302,
+ 0x00000045, 0x00000308, 0x00000049, 0x00000300,
+ 0x00000049, 0x00000301, 0x00000049, 0x00000302,
+ 0x00000049, 0x00000308, 0x0000004e, 0x00000303,
+ 0x0000004f, 0x00000300, 0x0000004f, 0x00000301,
+ 0x0000004f, 0x00000302, 0x0000004f, 0x00000303,
+ 0x0000004f, 0x00000308, 0x00000055, 0x00000300,
+ 0x00000055, 0x00000301, 0x00000055, 0x00000302,
+ 0x00000055, 0x00000308, 0x00000059, 0x00000301,
+ 0x00000061, 0x00000300, 0x00000061, 0x00000301,
+ 0x00000061, 0x00000302, 0x00000061, 0x00000303,
+ 0x00000061, 0x00000308, 0x00000061, 0x0000030a,
+ 0x00000063, 0x00000327, 0x00000065, 0x00000300,
+ 0x00000065, 0x00000301, 0x00000065, 0x00000302,
+ 0x00000065, 0x00000308, 0x00000069, 0x00000300,
+ 0x00000069, 0x00000301, 0x00000069, 0x00000302,
+ 0x00000069, 0x00000308, 0x0000006e, 0x00000303,
+ 0x0000006f, 0x00000300, 0x0000006f, 0x00000301,
+ 0x0000006f, 0x00000302, 0x0000006f, 0x00000303,
+ 0x0000006f, 0x00000308, 0x00000075, 0x00000300,
+ 0x00000075, 0x00000301, 0x00000075, 0x00000302,
+ 0x00000075, 0x00000308, 0x00000079, 0x00000301,
+ 0x00000079, 0x00000308, 0x00000041, 0x00000304,
+ 0x00000061, 0x00000304, 0x00000041, 0x00000306,
+ 0x00000061, 0x00000306, 0x00000041, 0x00000328,
+ 0x00000061, 0x00000328, 0x00000043, 0x00000301,
+ 0x00000063, 0x00000301, 0x00000043, 0x00000302,
+ 0x00000063, 0x00000302, 0x00000043, 0x00000307,
+ 0x00000063, 0x00000307, 0x00000043, 0x0000030c,
+ 0x00000063, 0x0000030c, 0x00000044, 0x0000030c,
+ 0x00000064, 0x0000030c, 0x00000045, 0x00000304,
+ 0x00000065, 0x00000304, 0x00000045, 0x00000306,
+ 0x00000065, 0x00000306, 0x00000045, 0x00000307,
+ 0x00000065, 0x00000307, 0x00000045, 0x00000328,
+ 0x00000065, 0x00000328, 0x00000045, 0x0000030c,
+ 0x00000065, 0x0000030c, 0x00000047, 0x00000302,
+ 0x00000067, 0x00000302, 0x00000047, 0x00000306,
+ 0x00000067, 0x00000306, 0x00000047, 0x00000307,
+ 0x00000067, 0x00000307, 0x00000047, 0x00000327,
+ 0x00000067, 0x00000327, 0x00000048, 0x00000302,
+ 0x00000068, 0x00000302, 0x00000049, 0x00000303,
+ 0x00000069, 0x00000303, 0x00000049, 0x00000304,
+ 0x00000069, 0x00000304, 0x00000049, 0x00000306,
+ 0x00000069, 0x00000306, 0x00000049, 0x00000328,
+ 0x00000069, 0x00000328, 0x00000049, 0x00000307,
+ 0x0000004a, 0x00000302, 0x0000006a, 0x00000302,
+ 0x0000004b, 0x00000327, 0x0000006b, 0x00000327,
+ 0x0000004c, 0x00000301, 0x0000006c, 0x00000301,
+ 0x0000004c, 0x00000327, 0x0000006c, 0x00000327,
+ 0x0000004c, 0x0000030c, 0x0000006c, 0x0000030c,
+ 0x0000004e, 0x00000301, 0x0000006e, 0x00000301,
+ 0x0000004e, 0x00000327, 0x0000006e, 0x00000327,
+ 0x0000004e, 0x0000030c, 0x0000006e, 0x0000030c,
+ 0x0000004f, 0x00000304, 0x0000006f, 0x00000304,
+ 0x0000004f, 0x00000306, 0x0000006f, 0x00000306,
+ 0x0000004f, 0x0000030b, 0x0000006f, 0x0000030b,
+ 0x00000052, 0x00000301, 0x00000072, 0x00000301,
+ 0x00000052, 0x00000327, 0x00000072, 0x00000327,
+ 0x00000052, 0x0000030c, 0x00000072, 0x0000030c,
+ 0x00000053, 0x00000301, 0x00000073, 0x00000301,
+ 0x00000053, 0x00000302, 0x00000073, 0x00000302,
+ 0x00000053, 0x00000327, 0x00000073, 0x00000327,
+ 0x00000053, 0x0000030c, 0x00000073, 0x0000030c,
+ 0x00000054, 0x00000327, 0x00000074, 0x00000327,
+ 0x00000054, 0x0000030c, 0x00000074, 0x0000030c,
+ 0x00000055, 0x00000303, 0x00000075, 0x00000303,
+ 0x00000055, 0x00000304, 0x00000075, 0x00000304,
+ 0x00000055, 0x00000306, 0x00000075, 0x00000306,
+ 0x00000055, 0x0000030a, 0x00000075, 0x0000030a,
+ 0x00000055, 0x0000030b, 0x00000075, 0x0000030b,
+ 0x00000055, 0x00000328, 0x00000075, 0x00000328,
+ 0x00000057, 0x00000302, 0x00000077, 0x00000302,
+ 0x00000059, 0x00000302, 0x00000079, 0x00000302,
+ 0x00000059, 0x00000308, 0x0000005a, 0x00000301,
+ 0x0000007a, 0x00000301, 0x0000005a, 0x00000307,
+ 0x0000007a, 0x00000307, 0x0000005a, 0x0000030c,
+ 0x0000007a, 0x0000030c, 0x0000004f, 0x0000031b,
+ 0x0000006f, 0x0000031b, 0x00000055, 0x0000031b,
+ 0x00000075, 0x0000031b, 0x00000041, 0x0000030c,
+ 0x00000061, 0x0000030c, 0x00000049, 0x0000030c,
+ 0x00000069, 0x0000030c, 0x0000004f, 0x0000030c,
+ 0x0000006f, 0x0000030c, 0x00000055, 0x0000030c,
+ 0x00000075, 0x0000030c, 0x00000055, 0x00000308,
+ 0x00000304, 0x00000075, 0x00000308, 0x00000304,
+ 0x00000055, 0x00000308, 0x00000301, 0x00000075,
+ 0x00000308, 0x00000301, 0x00000055, 0x00000308,
+ 0x0000030c, 0x00000075, 0x00000308, 0x0000030c,
+ 0x00000055, 0x00000308, 0x00000300, 0x00000075,
+ 0x00000308, 0x00000300, 0x00000041, 0x00000308,
+ 0x00000304, 0x00000061, 0x00000308, 0x00000304,
+ 0x00000041, 0x00000307, 0x00000304, 0x00000061,
+ 0x00000307, 0x00000304, 0x000000c6, 0x00000304,
+ 0x000000e6, 0x00000304, 0x00000047, 0x0000030c,
+ 0x00000067, 0x0000030c, 0x0000004b, 0x0000030c,
+ 0x0000006b, 0x0000030c, 0x0000004f, 0x00000328,
+ 0x0000006f, 0x00000328, 0x0000004f, 0x00000328,
+ 0x00000304, 0x0000006f, 0x00000328, 0x00000304,
+ 0x000001b7, 0x0000030c, 0x00000292, 0x0000030c,
+ 0x0000006a, 0x0000030c, 0x00000047, 0x00000301,
+ 0x00000067, 0x00000301, 0x0000004e, 0x00000300,
+ 0x0000006e, 0x00000300, 0x00000041, 0x0000030a,
+ 0x00000301, 0x00000061, 0x0000030a, 0x00000301,
+ 0x000000c6, 0x00000301, 0x000000e6, 0x00000301,
+ 0x000000d8, 0x00000301, 0x000000f8, 0x00000301,
+ 0x00000041, 0x0000030f, 0x00000061, 0x0000030f,
+ 0x00000041, 0x00000311, 0x00000061, 0x00000311,
+ 0x00000045, 0x0000030f, 0x00000065, 0x0000030f,
+ 0x00000045, 0x00000311, 0x00000065, 0x00000311,
+ 0x00000049, 0x0000030f, 0x00000069, 0x0000030f,
+ 0x00000049, 0x00000311, 0x00000069, 0x00000311,
+ 0x0000004f, 0x0000030f, 0x0000006f, 0x0000030f,
+ 0x0000004f, 0x00000311, 0x0000006f, 0x00000311,
+ 0x00000052, 0x0000030f, 0x00000072, 0x0000030f,
+ 0x00000052, 0x00000311, 0x00000072, 0x00000311,
+ 0x00000055, 0x0000030f, 0x00000075, 0x0000030f,
+ 0x00000055, 0x00000311, 0x00000075, 0x00000311,
+ 0x00000053, 0x00000326, 0x00000073, 0x00000326,
+ 0x00000054, 0x00000326, 0x00000074, 0x00000326,
+ 0x00000048, 0x0000030c, 0x00000068, 0x0000030c,
+ 0x00000041, 0x00000307, 0x00000061, 0x00000307,
+ 0x00000045, 0x00000327, 0x00000065, 0x00000327,
+ 0x0000004f, 0x00000308, 0x00000304, 0x0000006f,
+ 0x00000308, 0x00000304, 0x0000004f, 0x00000303,
+ 0x00000304, 0x0000006f, 0x00000303, 0x00000304,
+ 0x0000004f, 0x00000307, 0x0000006f, 0x00000307,
+ 0x0000004f, 0x00000307, 0x00000304, 0x0000006f,
+ 0x00000307, 0x00000304, 0x00000059, 0x00000304,
+ 0x00000079, 0x00000304, 0x00000300, 0x00000301,
+ 0x00000313, 0x00000308, 0x00000301, 0x000002b9,
+ 0x0000003b, 0x000000a8, 0x00000301, 0x00000391,
+ 0x00000301, 0x000000b7, 0x00000395, 0x00000301,
+ 0x00000397, 0x00000301, 0x00000399, 0x00000301,
+ 0x0000039f, 0x00000301, 0x000003a5, 0x00000301,
+ 0x000003a9, 0x00000301, 0x000003b9, 0x00000308,
+ 0x00000301, 0x00000399, 0x00000308, 0x000003a5,
+ 0x00000308, 0x000003b1, 0x00000301, 0x000003b5,
+ 0x00000301, 0x000003b7, 0x00000301, 0x000003b9,
+ 0x00000301, 0x000003c5, 0x00000308, 0x00000301,
+ 0x000003b9, 0x00000308, 0x000003c5, 0x00000308,
+ 0x000003bf, 0x00000301, 0x000003c5, 0x00000301,
+ 0x000003c9, 0x00000301, 0x000003d2, 0x00000301,
+ 0x000003d2, 0x00000308, 0x00000415, 0x00000300,
+ 0x00000415, 0x00000308, 0x00000413, 0x00000301,
+ 0x00000406, 0x00000308, 0x0000041a, 0x00000301,
+ 0x00000418, 0x00000300, 0x00000423, 0x00000306,
+ 0x00000418, 0x00000306, 0x00000438, 0x00000306,
+ 0x00000435, 0x00000300, 0x00000435, 0x00000308,
+ 0x00000433, 0x00000301, 0x00000456, 0x00000308,
+ 0x0000043a, 0x00000301, 0x00000438, 0x00000300,
+ 0x00000443, 0x00000306, 0x00000474, 0x0000030f,
+ 0x00000475, 0x0000030f, 0x00000416, 0x00000306,
+ 0x00000436, 0x00000306, 0x00000410, 0x00000306,
+ 0x00000430, 0x00000306, 0x00000410, 0x00000308,
+ 0x00000430, 0x00000308, 0x00000415, 0x00000306,
+ 0x00000435, 0x00000306, 0x000004d8, 0x00000308,
+ 0x000004d9, 0x00000308, 0x00000416, 0x00000308,
+ 0x00000436, 0x00000308, 0x00000417, 0x00000308,
+ 0x00000437, 0x00000308, 0x00000418, 0x00000304,
+ 0x00000438, 0x00000304, 0x00000418, 0x00000308,
+ 0x00000438, 0x00000308, 0x0000041e, 0x00000308,
+ 0x0000043e, 0x00000308, 0x000004e8, 0x00000308,
+ 0x000004e9, 0x00000308, 0x0000042d, 0x00000308,
+ 0x0000044d, 0x00000308, 0x00000423, 0x00000304,
+ 0x00000443, 0x00000304, 0x00000423, 0x00000308,
+ 0x00000443, 0x00000308, 0x00000423, 0x0000030b,
+ 0x00000443, 0x0000030b, 0x00000427, 0x00000308,
+ 0x00000447, 0x00000308, 0x0000042b, 0x00000308,
+ 0x0000044b, 0x00000308, 0x00000627, 0x00000653,
+ 0x00000627, 0x00000654, 0x00000648, 0x00000654,
+ 0x00000627, 0x00000655, 0x0000064a, 0x00000654,
+ 0x000006d5, 0x00000654, 0x000006c1, 0x00000654,
+ 0x000006d2, 0x00000654, 0x00000928, 0x0000093c,
+ 0x00000930, 0x0000093c, 0x00000933, 0x0000093c,
+ 0x00000915, 0x0000093c, 0x00000916, 0x0000093c,
+ 0x00000917, 0x0000093c, 0x0000091c, 0x0000093c,
+ 0x00000921, 0x0000093c, 0x00000922, 0x0000093c,
+ 0x0000092b, 0x0000093c, 0x0000092f, 0x0000093c,
+ 0x000009c7, 0x000009be, 0x000009c7, 0x000009d7,
+ 0x000009a1, 0x000009bc, 0x000009a2, 0x000009bc,
+ 0x000009af, 0x000009bc, 0x00000a32, 0x00000a3c,
+ 0x00000a38, 0x00000a3c, 0x00000a16, 0x00000a3c,
+ 0x00000a17, 0x00000a3c, 0x00000a1c, 0x00000a3c,
+ 0x00000a2b, 0x00000a3c, 0x00000b47, 0x00000b56,
+ 0x00000b47, 0x00000b3e, 0x00000b47, 0x00000b57,
+ 0x00000b21, 0x00000b3c, 0x00000b22, 0x00000b3c,
+ 0x00000b92, 0x00000bd7, 0x00000bc6, 0x00000bbe,
+ 0x00000bc7, 0x00000bbe, 0x00000bc6, 0x00000bd7,
+ 0x00000c46, 0x00000c56, 0x00000cbf, 0x00000cd5,
+ 0x00000cc6, 0x00000cd5, 0x00000cc6, 0x00000cd6,
+ 0x00000cc6, 0x00000cc2, 0x00000cc6, 0x00000cc2,
+ 0x00000cd5, 0x00000d46, 0x00000d3e, 0x00000d47,
+ 0x00000d3e, 0x00000d46, 0x00000d57, 0x00000dd9,
+ 0x00000dca, 0x00000dd9, 0x00000dcf, 0x00000dd9,
+ 0x00000dcf, 0x00000dca, 0x00000dd9, 0x00000ddf,
+ 0x00000f42, 0x00000fb7, 0x00000f4c, 0x00000fb7,
+ 0x00000f51, 0x00000fb7, 0x00000f56, 0x00000fb7,
+ 0x00000f5b, 0x00000fb7, 0x00000f40, 0x00000fb5,
+ 0x00000f71, 0x00000f72, 0x00000f71, 0x00000f74,
+ 0x00000fb2, 0x00000f80, 0x00000fb3, 0x00000f80,
+ 0x00000f71, 0x00000f80, 0x00000f92, 0x00000fb7,
+ 0x00000f9c, 0x00000fb7, 0x00000fa1, 0x00000fb7,
+ 0x00000fa6, 0x00000fb7, 0x00000fab, 0x00000fb7,
+ 0x00000f90, 0x00000fb5, 0x00001025, 0x0000102e,
+ 0x00000041, 0x00000325, 0x00000061, 0x00000325,
+ 0x00000042, 0x00000307, 0x00000062, 0x00000307,
+ 0x00000042, 0x00000323, 0x00000062, 0x00000323,
+ 0x00000042, 0x00000331, 0x00000062, 0x00000331,
+ 0x00000043, 0x00000327, 0x00000301, 0x00000063,
+ 0x00000327, 0x00000301, 0x00000044, 0x00000307,
+ 0x00000064, 0x00000307, 0x00000044, 0x00000323,
+ 0x00000064, 0x00000323, 0x00000044, 0x00000331,
+ 0x00000064, 0x00000331, 0x00000044, 0x00000327,
+ 0x00000064, 0x00000327, 0x00000044, 0x0000032d,
+ 0x00000064, 0x0000032d, 0x00000045, 0x00000304,
+ 0x00000300, 0x00000065, 0x00000304, 0x00000300,
+ 0x00000045, 0x00000304, 0x00000301, 0x00000065,
+ 0x00000304, 0x00000301, 0x00000045, 0x0000032d,
+ 0x00000065, 0x0000032d, 0x00000045, 0x00000330,
+ 0x00000065, 0x00000330, 0x00000045, 0x00000327,
+ 0x00000306, 0x00000065, 0x00000327, 0x00000306,
+ 0x00000046, 0x00000307, 0x00000066, 0x00000307,
+ 0x00000047, 0x00000304, 0x00000067, 0x00000304,
+ 0x00000048, 0x00000307, 0x00000068, 0x00000307,
+ 0x00000048, 0x00000323, 0x00000068, 0x00000323,
+ 0x00000048, 0x00000308, 0x00000068, 0x00000308,
+ 0x00000048, 0x00000327, 0x00000068, 0x00000327,
+ 0x00000048, 0x0000032e, 0x00000068, 0x0000032e,
+ 0x00000049, 0x00000330, 0x00000069, 0x00000330,
+ 0x00000049, 0x00000308, 0x00000301, 0x00000069,
+ 0x00000308, 0x00000301, 0x0000004b, 0x00000301,
+ 0x0000006b, 0x00000301, 0x0000004b, 0x00000323,
+ 0x0000006b, 0x00000323, 0x0000004b, 0x00000331,
+ 0x0000006b, 0x00000331, 0x0000004c, 0x00000323,
+ 0x0000006c, 0x00000323, 0x0000004c, 0x00000323,
+ 0x00000304, 0x0000006c, 0x00000323, 0x00000304,
+ 0x0000004c, 0x00000331, 0x0000006c, 0x00000331,
+ 0x0000004c, 0x0000032d, 0x0000006c, 0x0000032d,
+ 0x0000004d, 0x00000301, 0x0000006d, 0x00000301,
+ 0x0000004d, 0x00000307, 0x0000006d, 0x00000307,
+ 0x0000004d, 0x00000323, 0x0000006d, 0x00000323,
+ 0x0000004e, 0x00000307, 0x0000006e, 0x00000307,
+ 0x0000004e, 0x00000323, 0x0000006e, 0x00000323,
+ 0x0000004e, 0x00000331, 0x0000006e, 0x00000331,
+ 0x0000004e, 0x0000032d, 0x0000006e, 0x0000032d,
+ 0x0000004f, 0x00000303, 0x00000301, 0x0000006f,
+ 0x00000303, 0x00000301, 0x0000004f, 0x00000303,
+ 0x00000308, 0x0000006f, 0x00000303, 0x00000308,
+ 0x0000004f, 0x00000304, 0x00000300, 0x0000006f,
+ 0x00000304, 0x00000300, 0x0000004f, 0x00000304,
+ 0x00000301, 0x0000006f, 0x00000304, 0x00000301,
+ 0x00000050, 0x00000301, 0x00000070, 0x00000301,
+ 0x00000050, 0x00000307, 0x00000070, 0x00000307,
+ 0x00000052, 0x00000307, 0x00000072, 0x00000307,
+ 0x00000052, 0x00000323, 0x00000072, 0x00000323,
+ 0x00000052, 0x00000323, 0x00000304, 0x00000072,
+ 0x00000323, 0x00000304, 0x00000052, 0x00000331,
+ 0x00000072, 0x00000331, 0x00000053, 0x00000307,
+ 0x00000073, 0x00000307, 0x00000053, 0x00000323,
+ 0x00000073, 0x00000323, 0x00000053, 0x00000301,
+ 0x00000307, 0x00000073, 0x00000301, 0x00000307,
+ 0x00000053, 0x0000030c, 0x00000307, 0x00000073,
+ 0x0000030c, 0x00000307, 0x00000053, 0x00000323,
+ 0x00000307, 0x00000073, 0x00000323, 0x00000307,
+ 0x00000054, 0x00000307, 0x00000074, 0x00000307,
+ 0x00000054, 0x00000323, 0x00000074, 0x00000323,
+ 0x00000054, 0x00000331, 0x00000074, 0x00000331,
+ 0x00000054, 0x0000032d, 0x00000074, 0x0000032d,
+ 0x00000055, 0x00000324, 0x00000075, 0x00000324,
+ 0x00000055, 0x00000330, 0x00000075, 0x00000330,
+ 0x00000055, 0x0000032d, 0x00000075, 0x0000032d,
+ 0x00000055, 0x00000303, 0x00000301, 0x00000075,
+ 0x00000303, 0x00000301, 0x00000055, 0x00000304,
+ 0x00000308, 0x00000075, 0x00000304, 0x00000308,
+ 0x00000056, 0x00000303, 0x00000076, 0x00000303,
+ 0x00000056, 0x00000323, 0x00000076, 0x00000323,
+ 0x00000057, 0x00000300, 0x00000077, 0x00000300,
+ 0x00000057, 0x00000301, 0x00000077, 0x00000301,
+ 0x00000057, 0x00000308, 0x00000077, 0x00000308,
+ 0x00000057, 0x00000307, 0x00000077, 0x00000307,
+ 0x00000057, 0x00000323, 0x00000077, 0x00000323,
+ 0x00000058, 0x00000307, 0x00000078, 0x00000307,
+ 0x00000058, 0x00000308, 0x00000078, 0x00000308,
+ 0x00000059, 0x00000307, 0x00000079, 0x00000307,
+ 0x0000005a, 0x00000302, 0x0000007a, 0x00000302,
+ 0x0000005a, 0x00000323, 0x0000007a, 0x00000323,
+ 0x0000005a, 0x00000331, 0x0000007a, 0x00000331,
+ 0x00000068, 0x00000331, 0x00000074, 0x00000308,
+ 0x00000077, 0x0000030a, 0x00000079, 0x0000030a,
+ 0x0000017f, 0x00000307, 0x00000041, 0x00000323,
+ 0x00000061, 0x00000323, 0x00000041, 0x00000309,
+ 0x00000061, 0x00000309, 0x00000041, 0x00000302,
+ 0x00000301, 0x00000061, 0x00000302, 0x00000301,
+ 0x00000041, 0x00000302, 0x00000300, 0x00000061,
+ 0x00000302, 0x00000300, 0x00000041, 0x00000302,
+ 0x00000309, 0x00000061, 0x00000302, 0x00000309,
+ 0x00000041, 0x00000302, 0x00000303, 0x00000061,
+ 0x00000302, 0x00000303, 0x00000041, 0x00000323,
+ 0x00000302, 0x00000061, 0x00000323, 0x00000302,
+ 0x00000041, 0x00000306, 0x00000301, 0x00000061,
+ 0x00000306, 0x00000301, 0x00000041, 0x00000306,
+ 0x00000300, 0x00000061, 0x00000306, 0x00000300,
+ 0x00000041, 0x00000306, 0x00000309, 0x00000061,
+ 0x00000306, 0x00000309, 0x00000041, 0x00000306,
+ 0x00000303, 0x00000061, 0x00000306, 0x00000303,
+ 0x00000041, 0x00000323, 0x00000306, 0x00000061,
+ 0x00000323, 0x00000306, 0x00000045, 0x00000323,
+ 0x00000065, 0x00000323, 0x00000045, 0x00000309,
+ 0x00000065, 0x00000309, 0x00000045, 0x00000303,
+ 0x00000065, 0x00000303, 0x00000045, 0x00000302,
+ 0x00000301, 0x00000065, 0x00000302, 0x00000301,
+ 0x00000045, 0x00000302, 0x00000300, 0x00000065,
+ 0x00000302, 0x00000300, 0x00000045, 0x00000302,
+ 0x00000309, 0x00000065, 0x00000302, 0x00000309,
+ 0x00000045, 0x00000302, 0x00000303, 0x00000065,
+ 0x00000302, 0x00000303, 0x00000045, 0x00000323,
+ 0x00000302, 0x00000065, 0x00000323, 0x00000302,
+ 0x00000049, 0x00000309, 0x00000069, 0x00000309,
+ 0x00000049, 0x00000323, 0x00000069, 0x00000323,
+ 0x0000004f, 0x00000323, 0x0000006f, 0x00000323,
+ 0x0000004f, 0x00000309, 0x0000006f, 0x00000309,
+ 0x0000004f, 0x00000302, 0x00000301, 0x0000006f,
+ 0x00000302, 0x00000301, 0x0000004f, 0x00000302,
+ 0x00000300, 0x0000006f, 0x00000302, 0x00000300,
+ 0x0000004f, 0x00000302, 0x00000309, 0x0000006f,
+ 0x00000302, 0x00000309, 0x0000004f, 0x00000302,
+ 0x00000303, 0x0000006f, 0x00000302, 0x00000303,
+ 0x0000004f, 0x00000323, 0x00000302, 0x0000006f,
+ 0x00000323, 0x00000302, 0x0000004f, 0x0000031b,
+ 0x00000301, 0x0000006f, 0x0000031b, 0x00000301,
+ 0x0000004f, 0x0000031b, 0x00000300, 0x0000006f,
+ 0x0000031b, 0x00000300, 0x0000004f, 0x0000031b,
+ 0x00000309, 0x0000006f, 0x0000031b, 0x00000309,
+ 0x0000004f, 0x0000031b, 0x00000303, 0x0000006f,
+ 0x0000031b, 0x00000303, 0x0000004f, 0x0000031b,
+ 0x00000323, 0x0000006f, 0x0000031b, 0x00000323,
+ 0x00000055, 0x00000323, 0x00000075, 0x00000323,
+ 0x00000055, 0x00000309, 0x00000075, 0x00000309,
+ 0x00000055, 0x0000031b, 0x00000301, 0x00000075,
+ 0x0000031b, 0x00000301, 0x00000055, 0x0000031b,
+ 0x00000300, 0x00000075, 0x0000031b, 0x00000300,
+ 0x00000055, 0x0000031b, 0x00000309, 0x00000075,
+ 0x0000031b, 0x00000309, 0x00000055, 0x0000031b,
+ 0x00000303, 0x00000075, 0x0000031b, 0x00000303,
+ 0x00000055, 0x0000031b, 0x00000323, 0x00000075,
+ 0x0000031b, 0x00000323, 0x00000059, 0x00000300,
+ 0x00000079, 0x00000300, 0x00000059, 0x00000323,
+ 0x00000079, 0x00000323, 0x00000059, 0x00000309,
+ 0x00000079, 0x00000309, 0x00000059, 0x00000303,
+ 0x00000079, 0x00000303, 0x000003b1, 0x00000313,
+ 0x000003b1, 0x00000314, 0x000003b1, 0x00000313,
+ 0x00000300, 0x000003b1, 0x00000314, 0x00000300,
+ 0x000003b1, 0x00000313, 0x00000301, 0x000003b1,
+ 0x00000314, 0x00000301, 0x000003b1, 0x00000313,
+ 0x00000342, 0x000003b1, 0x00000314, 0x00000342,
+ 0x00000391, 0x00000313, 0x00000391, 0x00000314,
+ 0x00000391, 0x00000313, 0x00000300, 0x00000391,
+ 0x00000314, 0x00000300, 0x00000391, 0x00000313,
+ 0x00000301, 0x00000391, 0x00000314, 0x00000301,
+ 0x00000391, 0x00000313, 0x00000342, 0x00000391,
+ 0x00000314, 0x00000342, 0x000003b5, 0x00000313,
+ 0x000003b5, 0x00000314, 0x000003b5, 0x00000313,
+ 0x00000300, 0x000003b5, 0x00000314, 0x00000300,
+ 0x000003b5, 0x00000313, 0x00000301, 0x000003b5,
+ 0x00000314, 0x00000301, 0x00000395, 0x00000313,
+ 0x00000395, 0x00000314, 0x00000395, 0x00000313,
+ 0x00000300, 0x00000395, 0x00000314, 0x00000300,
+ 0x00000395, 0x00000313, 0x00000301, 0x00000395,
+ 0x00000314, 0x00000301, 0x000003b7, 0x00000313,
+ 0x000003b7, 0x00000314, 0x000003b7, 0x00000313,
+ 0x00000300, 0x000003b7, 0x00000314, 0x00000300,
+ 0x000003b7, 0x00000313, 0x00000301, 0x000003b7,
+ 0x00000314, 0x00000301, 0x000003b7, 0x00000313,
+ 0x00000342, 0x000003b7, 0x00000314, 0x00000342,
+ 0x00000397, 0x00000313, 0x00000397, 0x00000314,
+ 0x00000397, 0x00000313, 0x00000300, 0x00000397,
+ 0x00000314, 0x00000300, 0x00000397, 0x00000313,
+ 0x00000301, 0x00000397, 0x00000314, 0x00000301,
+ 0x00000397, 0x00000313, 0x00000342, 0x00000397,
+ 0x00000314, 0x00000342, 0x000003b9, 0x00000313,
+ 0x000003b9, 0x00000314, 0x000003b9, 0x00000313,
+ 0x00000300, 0x000003b9, 0x00000314, 0x00000300,
+ 0x000003b9, 0x00000313, 0x00000301, 0x000003b9,
+ 0x00000314, 0x00000301, 0x000003b9, 0x00000313,
+ 0x00000342, 0x000003b9, 0x00000314, 0x00000342,
+ 0x00000399, 0x00000313, 0x00000399, 0x00000314,
+ 0x00000399, 0x00000313, 0x00000300, 0x00000399,
+ 0x00000314, 0x00000300, 0x00000399, 0x00000313,
+ 0x00000301, 0x00000399, 0x00000314, 0x00000301,
+ 0x00000399, 0x00000313, 0x00000342, 0x00000399,
+ 0x00000314, 0x00000342, 0x000003bf, 0x00000313,
+ 0x000003bf, 0x00000314, 0x000003bf, 0x00000313,
+ 0x00000300, 0x000003bf, 0x00000314, 0x00000300,
+ 0x000003bf, 0x00000313, 0x00000301, 0x000003bf,
+ 0x00000314, 0x00000301, 0x0000039f, 0x00000313,
+ 0x0000039f, 0x00000314, 0x0000039f, 0x00000313,
+ 0x00000300, 0x0000039f, 0x00000314, 0x00000300,
+ 0x0000039f, 0x00000313, 0x00000301, 0x0000039f,
+ 0x00000314, 0x00000301, 0x000003c5, 0x00000313,
+ 0x000003c5, 0x00000314, 0x000003c5, 0x00000313,
+ 0x00000300, 0x000003c5, 0x00000314, 0x00000300,
+ 0x000003c5, 0x00000313, 0x00000301, 0x000003c5,
+ 0x00000314, 0x00000301, 0x000003c5, 0x00000313,
+ 0x00000342, 0x000003c5, 0x00000314, 0x00000342,
+ 0x000003a5, 0x00000314, 0x000003a5, 0x00000314,
+ 0x00000300, 0x000003a5, 0x00000314, 0x00000301,
+ 0x000003a5, 0x00000314, 0x00000342, 0x000003c9,
+ 0x00000313, 0x000003c9, 0x00000314, 0x000003c9,
+ 0x00000313, 0x00000300, 0x000003c9, 0x00000314,
+ 0x00000300, 0x000003c9, 0x00000313, 0x00000301,
+ 0x000003c9, 0x00000314, 0x00000301, 0x000003c9,
+ 0x00000313, 0x00000342, 0x000003c9, 0x00000314,
+ 0x00000342, 0x000003a9, 0x00000313, 0x000003a9,
+ 0x00000314, 0x000003a9, 0x00000313, 0x00000300,
+ 0x000003a9, 0x00000314, 0x00000300, 0x000003a9,
+ 0x00000313, 0x00000301, 0x000003a9, 0x00000314,
+ 0x00000301, 0x000003a9, 0x00000313, 0x00000342,
+ 0x000003a9, 0x00000314, 0x00000342, 0x000003b1,
+ 0x00000300, 0x000003b1, 0x00000301, 0x000003b5,
+ 0x00000300, 0x000003b5, 0x00000301, 0x000003b7,
+ 0x00000300, 0x000003b7, 0x00000301, 0x000003b9,
+ 0x00000300, 0x000003b9, 0x00000301, 0x000003bf,
+ 0x00000300, 0x000003bf, 0x00000301, 0x000003c5,
+ 0x00000300, 0x000003c5, 0x00000301, 0x000003c9,
+ 0x00000300, 0x000003c9, 0x00000301, 0x000003b1,
+ 0x00000313, 0x00000345, 0x000003b1, 0x00000314,
+ 0x00000345, 0x000003b1, 0x00000313, 0x00000300,
+ 0x00000345, 0x000003b1, 0x00000314, 0x00000300,
+ 0x00000345, 0x000003b1, 0x00000313, 0x00000301,
+ 0x00000345, 0x000003b1, 0x00000314, 0x00000301,
+ 0x00000345, 0x000003b1, 0x00000313, 0x00000342,
+ 0x00000345, 0x000003b1, 0x00000314, 0x00000342,
+ 0x00000345, 0x00000391, 0x00000313, 0x00000345,
+ 0x00000391, 0x00000314, 0x00000345, 0x00000391,
+ 0x00000313, 0x00000300, 0x00000345, 0x00000391,
+ 0x00000314, 0x00000300, 0x00000345, 0x00000391,
+ 0x00000313, 0x00000301, 0x00000345, 0x00000391,
+ 0x00000314, 0x00000301, 0x00000345, 0x00000391,
+ 0x00000313, 0x00000342, 0x00000345, 0x00000391,
+ 0x00000314, 0x00000342, 0x00000345, 0x000003b7,
+ 0x00000313, 0x00000345, 0x000003b7, 0x00000314,
+ 0x00000345, 0x000003b7, 0x00000313, 0x00000300,
+ 0x00000345, 0x000003b7, 0x00000314, 0x00000300,
+ 0x00000345, 0x000003b7, 0x00000313, 0x00000301,
+ 0x00000345, 0x000003b7, 0x00000314, 0x00000301,
+ 0x00000345, 0x000003b7, 0x00000313, 0x00000342,
+ 0x00000345, 0x000003b7, 0x00000314, 0x00000342,
+ 0x00000345, 0x00000397, 0x00000313, 0x00000345,
+ 0x00000397, 0x00000314, 0x00000345, 0x00000397,
+ 0x00000313, 0x00000300, 0x00000345, 0x00000397,
+ 0x00000314, 0x00000300, 0x00000345, 0x00000397,
+ 0x00000313, 0x00000301, 0x00000345, 0x00000397,
+ 0x00000314, 0x00000301, 0x00000345, 0x00000397,
+ 0x00000313, 0x00000342, 0x00000345, 0x00000397,
+ 0x00000314, 0x00000342, 0x00000345, 0x000003c9,
+ 0x00000313, 0x00000345, 0x000003c9, 0x00000314,
+ 0x00000345, 0x000003c9, 0x00000313, 0x00000300,
+ 0x00000345, 0x000003c9, 0x00000314, 0x00000300,
+ 0x00000345, 0x000003c9, 0x00000313, 0x00000301,
+ 0x00000345, 0x000003c9, 0x00000314, 0x00000301,
+ 0x00000345, 0x000003c9, 0x00000313, 0x00000342,
+ 0x00000345, 0x000003c9, 0x00000314, 0x00000342,
+ 0x00000345, 0x000003a9, 0x00000313, 0x00000345,
+ 0x000003a9, 0x00000314, 0x00000345, 0x000003a9,
+ 0x00000313, 0x00000300, 0x00000345, 0x000003a9,
+ 0x00000314, 0x00000300, 0x00000345, 0x000003a9,
+ 0x00000313, 0x00000301, 0x00000345, 0x000003a9,
+ 0x00000314, 0x00000301, 0x00000345, 0x000003a9,
+ 0x00000313, 0x00000342, 0x00000345, 0x000003a9,
+ 0x00000314, 0x00000342, 0x00000345, 0x000003b1,
+ 0x00000306, 0x000003b1, 0x00000304, 0x000003b1,
+ 0x00000300, 0x00000345, 0x000003b1, 0x00000345,
+ 0x000003b1, 0x00000301, 0x00000345, 0x000003b1,
+ 0x00000342, 0x000003b1, 0x00000342, 0x00000345,
+ 0x00000391, 0x00000306, 0x00000391, 0x00000304,
+ 0x00000391, 0x00000300, 0x00000391, 0x00000301,
+ 0x00000391, 0x00000345, 0x000003b9, 0x000000a8,
+ 0x00000342, 0x000003b7, 0x00000300, 0x00000345,
+ 0x000003b7, 0x00000345, 0x000003b7, 0x00000301,
+ 0x00000345, 0x000003b7, 0x00000342, 0x000003b7,
+ 0x00000342, 0x00000345, 0x00000395, 0x00000300,
+ 0x00000395, 0x00000301, 0x00000397, 0x00000300,
+ 0x00000397, 0x00000301, 0x00000397, 0x00000345,
+ 0x00001fbf, 0x00000300, 0x00001fbf, 0x00000301,
+ 0x00001fbf, 0x00000342, 0x000003b9, 0x00000306,
+ 0x000003b9, 0x00000304, 0x000003b9, 0x00000308,
+ 0x00000300, 0x000003b9, 0x00000308, 0x00000301,
+ 0x000003b9, 0x00000342, 0x000003b9, 0x00000308,
+ 0x00000342, 0x00000399, 0x00000306, 0x00000399,
+ 0x00000304, 0x00000399, 0x00000300, 0x00000399,
+ 0x00000301, 0x00001ffe, 0x00000300, 0x00001ffe,
+ 0x00000301, 0x00001ffe, 0x00000342, 0x000003c5,
+ 0x00000306, 0x000003c5, 0x00000304, 0x000003c5,
+ 0x00000308, 0x00000300, 0x000003c5, 0x00000308,
+ 0x00000301, 0x000003c1, 0x00000313, 0x000003c1,
+ 0x00000314, 0x000003c5, 0x00000342, 0x000003c5,
+ 0x00000308, 0x00000342, 0x000003a5, 0x00000306,
+ 0x000003a5, 0x00000304, 0x000003a5, 0x00000300,
+ 0x000003a5, 0x00000301, 0x000003a1, 0x00000314,
+ 0x000000a8, 0x00000300, 0x000000a8, 0x00000301,
+ 0x00000060, 0x000003c9, 0x00000300, 0x00000345,
+ 0x000003c9, 0x00000345, 0x000003c9, 0x00000301,
+ 0x00000345, 0x000003c9, 0x00000342, 0x000003c9,
+ 0x00000342, 0x00000345, 0x0000039f, 0x00000300,
+ 0x0000039f, 0x00000301, 0x000003a9, 0x00000300,
+ 0x000003a9, 0x00000301, 0x000003a9, 0x00000345,
+ 0x000000b4, 0x00002002, 0x00002003, 0x000003a9,
+ 0x0000004b, 0x00000041, 0x0000030a, 0x00002190,
+ 0x00000338, 0x00002192, 0x00000338, 0x00002194,
+ 0x00000338, 0x000021d0, 0x00000338, 0x000021d4,
+ 0x00000338, 0x000021d2, 0x00000338, 0x00002203,
+ 0x00000338, 0x00002208, 0x00000338, 0x0000220b,
+ 0x00000338, 0x00002223, 0x00000338, 0x00002225,
+ 0x00000338, 0x0000223c, 0x00000338, 0x00002243,
+ 0x00000338, 0x00002245, 0x00000338, 0x00002248,
+ 0x00000338, 0x0000003d, 0x00000338, 0x00002261,
+ 0x00000338, 0x0000224d, 0x00000338, 0x0000003c,
+ 0x00000338, 0x0000003e, 0x00000338, 0x00002264,
+ 0x00000338, 0x00002265, 0x00000338, 0x00002272,
+ 0x00000338, 0x00002273, 0x00000338, 0x00002276,
+ 0x00000338, 0x00002277, 0x00000338, 0x0000227a,
+ 0x00000338, 0x0000227b, 0x00000338, 0x00002282,
+ 0x00000338, 0x00002283, 0x00000338, 0x00002286,
+ 0x00000338, 0x00002287, 0x00000338, 0x000022a2,
+ 0x00000338, 0x000022a8, 0x00000338, 0x000022a9,
+ 0x00000338, 0x000022ab, 0x00000338, 0x0000227c,
+ 0x00000338, 0x0000227d, 0x00000338, 0x00002291,
+ 0x00000338, 0x00002292, 0x00000338, 0x000022b2,
+ 0x00000338, 0x000022b3, 0x00000338, 0x000022b4,
+ 0x00000338, 0x000022b5, 0x00000338, 0x00003008,
+ 0x00003009, 0x00002add, 0x00000338, 0x0000304b,
+ 0x00003099, 0x0000304d, 0x00003099, 0x0000304f,
+ 0x00003099, 0x00003051, 0x00003099, 0x00003053,
+ 0x00003099, 0x00003055, 0x00003099, 0x00003057,
+ 0x00003099, 0x00003059, 0x00003099, 0x0000305b,
+ 0x00003099, 0x0000305d, 0x00003099, 0x0000305f,
+ 0x00003099, 0x00003061, 0x00003099, 0x00003064,
+ 0x00003099, 0x00003066, 0x00003099, 0x00003068,
+ 0x00003099, 0x0000306f, 0x00003099, 0x0000306f,
+ 0x0000309a, 0x00003072, 0x00003099, 0x00003072,
+ 0x0000309a, 0x00003075, 0x00003099, 0x00003075,
+ 0x0000309a, 0x00003078, 0x00003099, 0x00003078,
+ 0x0000309a, 0x0000307b, 0x00003099, 0x0000307b,
+ 0x0000309a, 0x00003046, 0x00003099, 0x0000309d,
+ 0x00003099, 0x000030ab, 0x00003099, 0x000030ad,
+ 0x00003099, 0x000030af, 0x00003099, 0x000030b1,
+ 0x00003099, 0x000030b3, 0x00003099, 0x000030b5,
+ 0x00003099, 0x000030b7, 0x00003099, 0x000030b9,
+ 0x00003099, 0x000030bb, 0x00003099, 0x000030bd,
+ 0x00003099, 0x000030bf, 0x00003099, 0x000030c1,
+ 0x00003099, 0x000030c4, 0x00003099, 0x000030c6,
+ 0x00003099, 0x000030c8, 0x00003099, 0x000030cf,
+ 0x00003099, 0x000030cf, 0x0000309a, 0x000030d2,
+ 0x00003099, 0x000030d2, 0x0000309a, 0x000030d5,
+ 0x00003099, 0x000030d5, 0x0000309a, 0x000030d8,
+ 0x00003099, 0x000030d8, 0x0000309a, 0x000030db,
+ 0x00003099, 0x000030db, 0x0000309a, 0x000030a6,
+ 0x00003099, 0x000030ef, 0x00003099, 0x000030f0,
+ 0x00003099, 0x000030f1, 0x00003099, 0x000030f2,
+ 0x00003099, 0x000030fd, 0x00003099, 0x00008eca,
+ 0x00008cc8, 0x00006ed1, 0x00004e32, 0x000053e5,
+ 0x00009f9c, 0x00009f9c, 0x00005951, 0x000091d1,
+ 0x00005587, 0x00005948, 0x000061f6, 0x00007669,
+ 0x00007f85, 0x0000863f, 0x000087ba, 0x000088f8,
+ 0x0000908f, 0x00006a02, 0x00006d1b, 0x000070d9,
+ 0x000073de, 0x0000843d, 0x0000916a, 0x000099f1,
+ 0x00004e82, 0x00005375, 0x00006b04, 0x0000721b,
+ 0x0000862d, 0x00009e1e, 0x00005d50, 0x00006feb,
+ 0x000085cd, 0x00008964, 0x000062c9, 0x000081d8,
+ 0x0000881f, 0x00005eca, 0x00006717, 0x00006d6a,
+ 0x000072fc, 0x000090ce, 0x00004f86, 0x000051b7,
+ 0x000052de, 0x000064c4, 0x00006ad3, 0x00007210,
+ 0x000076e7, 0x00008001, 0x00008606, 0x0000865c,
+ 0x00008def, 0x00009732, 0x00009b6f, 0x00009dfa,
+ 0x0000788c, 0x0000797f, 0x00007da0, 0x000083c9,
+ 0x00009304, 0x00009e7f, 0x00008ad6, 0x000058df,
+ 0x00005f04, 0x00007c60, 0x0000807e, 0x00007262,
+ 0x000078ca, 0x00008cc2, 0x000096f7, 0x000058d8,
+ 0x00005c62, 0x00006a13, 0x00006dda, 0x00006f0f,
+ 0x00007d2f, 0x00007e37, 0x0000964b, 0x000052d2,
+ 0x0000808b, 0x000051dc, 0x000051cc, 0x00007a1c,
+ 0x00007dbe, 0x000083f1, 0x00009675, 0x00008b80,
+ 0x000062cf, 0x00006a02, 0x00008afe, 0x00004e39,
+ 0x00005be7, 0x00006012, 0x00007387, 0x00007570,
+ 0x00005317, 0x000078fb, 0x00004fbf, 0x00005fa9,
+ 0x00004e0d, 0x00006ccc, 0x00006578, 0x00007d22,
+ 0x000053c3, 0x0000585e, 0x00007701, 0x00008449,
+ 0x00008aaa, 0x00006bba, 0x00008fb0, 0x00006c88,
+ 0x000062fe, 0x000082e5, 0x000063a0, 0x00007565,
+ 0x00004eae, 0x00005169, 0x000051c9, 0x00006881,
+ 0x00007ce7, 0x0000826f, 0x00008ad2, 0x000091cf,
+ 0x000052f5, 0x00005442, 0x00005973, 0x00005eec,
+ 0x000065c5, 0x00006ffe, 0x0000792a, 0x000095ad,
+ 0x00009a6a, 0x00009e97, 0x00009ece, 0x0000529b,
+ 0x000066c6, 0x00006b77, 0x00008f62, 0x00005e74,
+ 0x00006190, 0x00006200, 0x0000649a, 0x00006f23,
+ 0x00007149, 0x00007489, 0x000079ca, 0x00007df4,
+ 0x0000806f, 0x00008f26, 0x000084ee, 0x00009023,
+ 0x0000934a, 0x00005217, 0x000052a3, 0x000054bd,
+ 0x000070c8, 0x000088c2, 0x00008aaa, 0x00005ec9,
+ 0x00005ff5, 0x0000637b, 0x00006bae, 0x00007c3e,
+ 0x00007375, 0x00004ee4, 0x000056f9, 0x00005be7,
+ 0x00005dba, 0x0000601c, 0x000073b2, 0x00007469,
+ 0x00007f9a, 0x00008046, 0x00009234, 0x000096f6,
+ 0x00009748, 0x00009818, 0x00004f8b, 0x000079ae,
+ 0x000091b4, 0x000096b8, 0x000060e1, 0x00004e86,
+ 0x000050da, 0x00005bee, 0x00005c3f, 0x00006599,
+ 0x00006a02, 0x000071ce, 0x00007642, 0x000084fc,
+ 0x0000907c, 0x00009f8d, 0x00006688, 0x0000962e,
+ 0x00005289, 0x0000677b, 0x000067f3, 0x00006d41,
+ 0x00006e9c, 0x00007409, 0x00007559, 0x0000786b,
+ 0x00007d10, 0x0000985e, 0x0000516d, 0x0000622e,
+ 0x00009678, 0x0000502b, 0x00005d19, 0x00006dea,
+ 0x00008f2a, 0x00005f8b, 0x00006144, 0x00006817,
+ 0x00007387, 0x00009686, 0x00005229, 0x0000540f,
+ 0x00005c65, 0x00006613, 0x0000674e, 0x000068a8,
+ 0x00006ce5, 0x00007406, 0x000075e2, 0x00007f79,
+ 0x000088cf, 0x000088e1, 0x000091cc, 0x000096e2,
+ 0x0000533f, 0x00006eba, 0x0000541d, 0x000071d0,
+ 0x00007498, 0x000085fa, 0x000096a3, 0x00009c57,
+ 0x00009e9f, 0x00006797, 0x00006dcb, 0x000081e8,
+ 0x00007acb, 0x00007b20, 0x00007c92, 0x000072c0,
+ 0x00007099, 0x00008b58, 0x00004ec0, 0x00008336,
+ 0x0000523a, 0x00005207, 0x00005ea6, 0x000062d3,
+ 0x00007cd6, 0x00005b85, 0x00006d1e, 0x000066b4,
+ 0x00008f3b, 0x0000884c, 0x0000964d, 0x0000898b,
+ 0x00005ed3, 0x00005140, 0x000055c0, 0x0000585a,
+ 0x00006674, 0x000051de, 0x0000732a, 0x000076ca,
+ 0x0000793c, 0x0000795e, 0x00007965, 0x0000798f,
+ 0x00009756, 0x00007cbe, 0x00007fbd, 0x00008612,
+ 0x00008af8, 0x00009038, 0x000090fd, 0x000098ef,
+ 0x000098fc, 0x00009928, 0x00009db4, 0x00004fae,
+ 0x000050e7, 0x0000514d, 0x000052c9, 0x000052e4,
+ 0x00005351, 0x0000559d, 0x00005606, 0x00005668,
+ 0x00005840, 0x000058a8, 0x00005c64, 0x00005c6e,
+ 0x00006094, 0x00006168, 0x0000618e, 0x000061f2,
+ 0x0000654f, 0x000065e2, 0x00006691, 0x00006885,
+ 0x00006d77, 0x00006e1a, 0x00006f22, 0x0000716e,
+ 0x0000722b, 0x00007422, 0x00007891, 0x0000793e,
+ 0x00007949, 0x00007948, 0x00007950, 0x00007956,
+ 0x0000795d, 0x0000798d, 0x0000798e, 0x00007a40,
+ 0x00007a81, 0x00007bc0, 0x00007df4, 0x00007e09,
+ 0x00007e41, 0x00007f72, 0x00008005, 0x000081ed,
+ 0x00008279, 0x00008279, 0x00008457, 0x00008910,
+ 0x00008996, 0x00008b01, 0x00008b39, 0x00008cd3,
+ 0x00008d08, 0x00008fb6, 0x00009038, 0x000096e3,
+ 0x000097ff, 0x0000983b, 0x000005d9, 0x000005b4,
+ 0x000005f2, 0x000005b7, 0x000005e9, 0x000005c1,
+ 0x000005e9, 0x000005c2, 0x000005e9, 0x000005bc,
+ 0x000005c1, 0x000005e9, 0x000005bc, 0x000005c2,
+ 0x000005d0, 0x000005b7, 0x000005d0, 0x000005b8,
+ 0x000005d0, 0x000005bc, 0x000005d1, 0x000005bc,
+ 0x000005d2, 0x000005bc, 0x000005d3, 0x000005bc,
+ 0x000005d4, 0x000005bc, 0x000005d5, 0x000005bc,
+ 0x000005d6, 0x000005bc, 0x000005d8, 0x000005bc,
+ 0x000005d9, 0x000005bc, 0x000005da, 0x000005bc,
+ 0x000005db, 0x000005bc, 0x000005dc, 0x000005bc,
+ 0x000005de, 0x000005bc, 0x000005e0, 0x000005bc,
+ 0x000005e1, 0x000005bc, 0x000005e3, 0x000005bc,
+ 0x000005e4, 0x000005bc, 0x000005e6, 0x000005bc,
+ 0x000005e7, 0x000005bc, 0x000005e8, 0x000005bc,
+ 0x000005e9, 0x000005bc, 0x000005ea, 0x000005bc,
+ 0x000005d5, 0x000005b9, 0x000005d1, 0x000005bf,
+ 0x000005db, 0x000005bf, 0x000005e4, 0x000005bf,
+ 0x0001d157, 0x0001d165, 0x0001d158, 0x0001d165,
+ 0x0001d158, 0x0001d165, 0x0001d16e, 0x0001d158,
+ 0x0001d165, 0x0001d16f, 0x0001d158, 0x0001d165,
+ 0x0001d170, 0x0001d158, 0x0001d165, 0x0001d171,
+ 0x0001d158, 0x0001d165, 0x0001d172, 0x0001d1b9,
+ 0x0001d165, 0x0001d1ba, 0x0001d165, 0x0001d1b9,
+ 0x0001d165, 0x0001d16e, 0x0001d1ba, 0x0001d165,
+ 0x0001d16e, 0x0001d1b9, 0x0001d165, 0x0001d16f,
+ 0x0001d1ba, 0x0001d165, 0x0001d16f, 0x00004e3d,
+ 0x00004e38, 0x00004e41, 0x00020122, 0x00004f60,
+ 0x00004fae, 0x00004fbb, 0x00005002, 0x0000507a,
+ 0x00005099, 0x000050e7, 0x000050cf, 0x0000349e,
+ 0x0002063a, 0x0000514d, 0x00005154, 0x00005164,
+ 0x00005177, 0x0002051c, 0x000034b9, 0x00005167,
+ 0x0000518d, 0x0002054b, 0x00005197, 0x000051a4,
+ 0x00004ecc, 0x000051ac, 0x000051b5, 0x000291df,
+ 0x000051f5, 0x00005203, 0x000034df, 0x0000523b,
+ 0x00005246, 0x00005272, 0x00005277, 0x00003515,
+ 0x000052c7, 0x000052c9, 0x000052e4, 0x000052fa,
+ 0x00005305, 0x00005306, 0x00005317, 0x00005349,
+ 0x00005351, 0x0000535a, 0x00005373, 0x0000537d,
+ 0x0000537f, 0x0000537f, 0x0000537f, 0x00020a2c,
+ 0x00007070, 0x000053ca, 0x000053df, 0x00020b63,
+ 0x000053eb, 0x000053f1, 0x00005406, 0x0000549e,
+ 0x00005438, 0x00005448, 0x00005468, 0x000054a2,
+ 0x000054f6, 0x00005510, 0x00005553, 0x00005563,
+ 0x00005584, 0x00005584, 0x00005599, 0x000055ab,
+ 0x000055b3, 0x000055c2, 0x00005716, 0x00005606,
+ 0x00005717, 0x00005651, 0x00005674, 0x00005207,
+ 0x000058ee, 0x000057ce, 0x000057f4, 0x0000580d,
+ 0x0000578b, 0x00005832, 0x00005831, 0x000058ac,
+ 0x000214e4, 0x000058f2, 0x000058f7, 0x00005906,
+ 0x0000591a, 0x00005922, 0x00005962, 0x000216a8,
+ 0x000216ea, 0x000059ec, 0x00005a1b, 0x00005a27,
+ 0x000059d8, 0x00005a66, 0x000036ee, 0x0002136a,
+ 0x00005b08, 0x00005b3e, 0x00005b3e, 0x000219c8,
+ 0x00005bc3, 0x00005bd8, 0x00005be7, 0x00005bf3,
+ 0x00021b18, 0x00005bff, 0x00005c06, 0x00005f33,
+ 0x00005c22, 0x00003781, 0x00005c60, 0x00005c6e,
+ 0x00005cc0, 0x00005c8d, 0x00021de4, 0x00005d43,
+ 0x00021de6, 0x00005d6e, 0x00005d6b, 0x00005d7c,
+ 0x00005de1, 0x00005de2, 0x0000382f, 0x00005dfd,
+ 0x00005e28, 0x00005e3d, 0x00005e69, 0x00003862,
+ 0x00022183, 0x0000387c, 0x00005eb0, 0x00005eb3,
+ 0x00005eb6, 0x00005eca, 0x0002a392, 0x00005efe,
+ 0x00022331, 0x00022331, 0x00008201, 0x00005f22,
+ 0x00005f22, 0x000038c7, 0x000232b8, 0x000261da,
+ 0x00005f62, 0x00005f6b, 0x000038e3, 0x00005f9a,
+ 0x00005fcd, 0x00005fd7, 0x00005ff9, 0x00006081,
+ 0x0000393a, 0x0000391c, 0x00006094, 0x000226d4,
+ 0x000060c7, 0x00006148, 0x0000614c, 0x0000614e,
+ 0x0000614c, 0x0000617a, 0x0000618e, 0x000061b2,
+ 0x000061a4, 0x000061af, 0x000061de, 0x000061f2,
+ 0x000061f6, 0x00006210, 0x0000621b, 0x0000625d,
+ 0x000062b1, 0x000062d4, 0x00006350, 0x00022b0c,
+ 0x0000633d, 0x000062fc, 0x00006368, 0x00006383,
+ 0x000063e4, 0x00022bf1, 0x00006422, 0x000063c5,
+ 0x000063a9, 0x00003a2e, 0x00006469, 0x0000647e,
+ 0x0000649d, 0x00006477, 0x00003a6c, 0x0000654f,
+ 0x0000656c, 0x0002300a, 0x000065e3, 0x000066f8,
+ 0x00006649, 0x00003b19, 0x00006691, 0x00003b08,
+ 0x00003ae4, 0x00005192, 0x00005195, 0x00006700,
+ 0x0000669c, 0x000080ad, 0x000043d9, 0x00006717,
+ 0x0000671b, 0x00006721, 0x0000675e, 0x00006753,
+ 0x000233c3, 0x00003b49, 0x000067fa, 0x00006785,
+ 0x00006852, 0x00006885, 0x0002346d, 0x0000688e,
+ 0x0000681f, 0x00006914, 0x00003b9d, 0x00006942,
+ 0x000069a3, 0x000069ea, 0x00006aa8, 0x000236a3,
+ 0x00006adb, 0x00003c18, 0x00006b21, 0x000238a7,
+ 0x00006b54, 0x00003c4e, 0x00006b72, 0x00006b9f,
+ 0x00006bba, 0x00006bbb, 0x00023a8d, 0x00021d0b,
+ 0x00023afa, 0x00006c4e, 0x00023cbc, 0x00006cbf,
+ 0x00006ccd, 0x00006c67, 0x00006d16, 0x00006d3e,
+ 0x00006d77, 0x00006d41, 0x00006d69, 0x00006d78,
+ 0x00006d85, 0x00023d1e, 0x00006d34, 0x00006e2f,
+ 0x00006e6e, 0x00003d33, 0x00006ecb, 0x00006ec7,
+ 0x00023ed1, 0x00006df9, 0x00006f6e, 0x00023f5e,
+ 0x00023f8e, 0x00006fc6, 0x00007039, 0x0000701e,
+ 0x0000701b, 0x00003d96, 0x0000704a, 0x0000707d,
+ 0x00007077, 0x000070ad, 0x00020525, 0x00007145,
+ 0x00024263, 0x0000719c, 0x000043ab, 0x00007228,
+ 0x00007235, 0x00007250, 0x00024608, 0x00007280,
+ 0x00007295, 0x00024735, 0x00024814, 0x0000737a,
+ 0x0000738b, 0x00003eac, 0x000073a5, 0x00003eb8,
+ 0x00003eb8, 0x00007447, 0x0000745c, 0x00007471,
+ 0x00007485, 0x000074ca, 0x00003f1b, 0x00007524,
+ 0x00024c36, 0x0000753e, 0x00024c92, 0x00007570,
+ 0x0002219f, 0x00007610, 0x00024fa1, 0x00024fb8,
+ 0x00025044, 0x00003ffc, 0x00004008, 0x000076f4,
+ 0x000250f3, 0x000250f2, 0x00025119, 0x00025133,
+ 0x0000771e, 0x0000771f, 0x0000771f, 0x0000774a,
+ 0x00004039, 0x0000778b, 0x00004046, 0x00004096,
+ 0x0002541d, 0x0000784e, 0x0000788c, 0x000078cc,
+ 0x000040e3, 0x00025626, 0x00007956, 0x0002569a,
+ 0x000256c5, 0x0000798f, 0x000079eb, 0x0000412f,
+ 0x00007a40, 0x00007a4a, 0x00007a4f, 0x0002597c,
+ 0x00025aa7, 0x00025aa7, 0x00007aae, 0x00004202,
+ 0x00025bab, 0x00007bc6, 0x00007bc9, 0x00004227,
+ 0x00025c80, 0x00007cd2, 0x000042a0, 0x00007ce8,
+ 0x00007ce3, 0x00007d00, 0x00025f86, 0x00007d63,
+ 0x00004301, 0x00007dc7, 0x00007e02, 0x00007e45,
+ 0x00004334, 0x00026228, 0x00026247, 0x00004359,
+ 0x000262d9, 0x00007f7a, 0x0002633e, 0x00007f95,
+ 0x00007ffa, 0x00008005, 0x000264da, 0x00026523,
+ 0x00008060, 0x000265a8, 0x00008070, 0x0002335f,
+ 0x000043d5, 0x000080b2, 0x00008103, 0x0000440b,
+ 0x0000813e, 0x00005ab5, 0x000267a7, 0x000267b5,
+ 0x00023393, 0x0002339c, 0x00008201, 0x00008204,
+ 0x00008f9e, 0x0000446b, 0x00008291, 0x0000828b,
+ 0x0000829d, 0x000052b3, 0x000082b1, 0x000082b3,
+ 0x000082bd, 0x000082e6, 0x00026b3c, 0x000082e5,
+ 0x0000831d, 0x00008363, 0x000083ad, 0x00008323,
+ 0x000083bd, 0x000083e7, 0x00008457, 0x00008353,
+ 0x000083ca, 0x000083cc, 0x000083dc, 0x00026c36,
+ 0x00026d6b, 0x00026cd5, 0x0000452b, 0x000084f1,
+ 0x000084f3, 0x00008516, 0x000273ca, 0x00008564,
+ 0x00026f2c, 0x0000455d, 0x00004561, 0x00026fb1,
+ 0x000270d2, 0x0000456b, 0x00008650, 0x0000865c,
+ 0x00008667, 0x00008669, 0x000086a9, 0x00008688,
+ 0x0000870e, 0x000086e2, 0x00008779, 0x00008728,
+ 0x0000876b, 0x00008786, 0x00004d57, 0x000087e1,
+ 0x00008801, 0x000045f9, 0x00008860, 0x00008863,
+ 0x00027667, 0x000088d7, 0x000088de, 0x00004635,
+ 0x000088fa, 0x000034bb, 0x000278ae, 0x00027966,
+ 0x000046be, 0x000046c7, 0x00008aa0, 0x00008aed,
+ 0x00008b8a, 0x00008c55, 0x00027ca8, 0x00008cab,
+ 0x00008cc1, 0x00008d1b, 0x00008d77, 0x00027f2f,
+ 0x00020804, 0x00008dcb, 0x00008dbc, 0x00008df0,
+ 0x000208de, 0x00008ed4, 0x00008f38, 0x000285d2,
+ 0x000285ed, 0x00009094, 0x000090f1, 0x00009111,
+ 0x0002872e, 0x0000911b, 0x00009238, 0x000092d7,
+ 0x000092d8, 0x0000927c, 0x000093f9, 0x00009415,
+ 0x00028bfa, 0x0000958b, 0x00004995, 0x000095b7,
+ 0x00028d77, 0x000049e6, 0x000096c3, 0x00005db2,
+ 0x00009723, 0x00029145, 0x0002921a, 0x00004a6e,
+ 0x00004a76, 0x000097e0, 0x0002940a, 0x00004ab2,
+ 0x00029496, 0x0000980b, 0x0000980b, 0x00009829,
+ 0x000295b6, 0x000098e2, 0x00004b33, 0x00009929,
+ 0x000099a7, 0x000099c2, 0x000099fe, 0x00004bce,
+ 0x00029b30, 0x00009b12, 0x00009c40, 0x00009cfd,
+ 0x00004cce, 0x00004ced, 0x00009d67, 0x0002a0ce,
+ 0x00004cf8, 0x0002a105, 0x0002a20e, 0x0002a291,
+ 0x00009ebb, 0x00004d56, 0x00009ef9, 0x00009efe,
+ 0x00009f05, 0x00009f0f, 0x00009f16, 0x00009f3b,
+ 0x0002a600
+};
+
+static const ac_uint4 _uckdcmp_size = 10282;
+
+static const ac_uint4 _uckdcmp_nodes[] = {
+ 0x000000a0, 0x00000000,
+ 0x000000a8, 0x00000001,
+ 0x000000aa, 0x00000003,
+ 0x000000af, 0x00000004,
+ 0x000000b2, 0x00000006,
+ 0x000000b3, 0x00000007,
+ 0x000000b4, 0x00000008,
+ 0x000000b5, 0x0000000a,
+ 0x000000b8, 0x0000000b,
+ 0x000000b9, 0x0000000d,
+ 0x000000ba, 0x0000000e,
+ 0x000000bc, 0x0000000f,
+ 0x000000bd, 0x00000012,
+ 0x000000be, 0x00000015,
+ 0x000000c0, 0x00000018,
+ 0x000000c1, 0x0000001a,
+ 0x000000c2, 0x0000001c,
+ 0x000000c3, 0x0000001e,
+ 0x000000c4, 0x00000020,
+ 0x000000c5, 0x00000022,
+ 0x000000c7, 0x00000024,
+ 0x000000c8, 0x00000026,
+ 0x000000c9, 0x00000028,
+ 0x000000ca, 0x0000002a,
+ 0x000000cb, 0x0000002c,
+ 0x000000cc, 0x0000002e,
+ 0x000000cd, 0x00000030,
+ 0x000000ce, 0x00000032,
+ 0x000000cf, 0x00000034,
+ 0x000000d1, 0x00000036,
+ 0x000000d2, 0x00000038,
+ 0x000000d3, 0x0000003a,
+ 0x000000d4, 0x0000003c,
+ 0x000000d5, 0x0000003e,
+ 0x000000d6, 0x00000040,
+ 0x000000d9, 0x00000042,
+ 0x000000da, 0x00000044,
+ 0x000000db, 0x00000046,
+ 0x000000dc, 0x00000048,
+ 0x000000dd, 0x0000004a,
+ 0x000000e0, 0x0000004c,
+ 0x000000e1, 0x0000004e,
+ 0x000000e2, 0x00000050,
+ 0x000000e3, 0x00000052,
+ 0x000000e4, 0x00000054,
+ 0x000000e5, 0x00000056,
+ 0x000000e7, 0x00000058,
+ 0x000000e8, 0x0000005a,
+ 0x000000e9, 0x0000005c,
+ 0x000000ea, 0x0000005e,
+ 0x000000eb, 0x00000060,
+ 0x000000ec, 0x00000062,
+ 0x000000ed, 0x00000064,
+ 0x000000ee, 0x00000066,
+ 0x000000ef, 0x00000068,
+ 0x000000f1, 0x0000006a,
+ 0x000000f2, 0x0000006c,
+ 0x000000f3, 0x0000006e,
+ 0x000000f4, 0x00000070,
+ 0x000000f5, 0x00000072,
+ 0x000000f6, 0x00000074,
+ 0x000000f9, 0x00000076,
+ 0x000000fa, 0x00000078,
+ 0x000000fb, 0x0000007a,
+ 0x000000fc, 0x0000007c,
+ 0x000000fd, 0x0000007e,
+ 0x000000ff, 0x00000080,
+ 0x00000100, 0x00000082,
+ 0x00000101, 0x00000084,
+ 0x00000102, 0x00000086,
+ 0x00000103, 0x00000088,
+ 0x00000104, 0x0000008a,
+ 0x00000105, 0x0000008c,
+ 0x00000106, 0x0000008e,
+ 0x00000107, 0x00000090,
+ 0x00000108, 0x00000092,
+ 0x00000109, 0x00000094,
+ 0x0000010a, 0x00000096,
+ 0x0000010b, 0x00000098,
+ 0x0000010c, 0x0000009a,
+ 0x0000010d, 0x0000009c,
+ 0x0000010e, 0x0000009e,
+ 0x0000010f, 0x000000a0,
+ 0x00000112, 0x000000a2,
+ 0x00000113, 0x000000a4,
+ 0x00000114, 0x000000a6,
+ 0x00000115, 0x000000a8,
+ 0x00000116, 0x000000aa,
+ 0x00000117, 0x000000ac,
+ 0x00000118, 0x000000ae,
+ 0x00000119, 0x000000b0,
+ 0x0000011a, 0x000000b2,
+ 0x0000011b, 0x000000b4,
+ 0x0000011c, 0x000000b6,
+ 0x0000011d, 0x000000b8,
+ 0x0000011e, 0x000000ba,
+ 0x0000011f, 0x000000bc,
+ 0x00000120, 0x000000be,
+ 0x00000121, 0x000000c0,
+ 0x00000122, 0x000000c2,
+ 0x00000123, 0x000000c4,
+ 0x00000124, 0x000000c6,
+ 0x00000125, 0x000000c8,
+ 0x00000128, 0x000000ca,
+ 0x00000129, 0x000000cc,
+ 0x0000012a, 0x000000ce,
+ 0x0000012b, 0x000000d0,
+ 0x0000012c, 0x000000d2,
+ 0x0000012d, 0x000000d4,
+ 0x0000012e, 0x000000d6,
+ 0x0000012f, 0x000000d8,
+ 0x00000130, 0x000000da,
+ 0x00000132, 0x000000dc,
+ 0x00000133, 0x000000de,
+ 0x00000134, 0x000000e0,
+ 0x00000135, 0x000000e2,
+ 0x00000136, 0x000000e4,
+ 0x00000137, 0x000000e6,
+ 0x00000139, 0x000000e8,
+ 0x0000013a, 0x000000ea,
+ 0x0000013b, 0x000000ec,
+ 0x0000013c, 0x000000ee,
+ 0x0000013d, 0x000000f0,
+ 0x0000013e, 0x000000f2,
+ 0x0000013f, 0x000000f4,
+ 0x00000140, 0x000000f6,
+ 0x00000143, 0x000000f8,
+ 0x00000144, 0x000000fa,
+ 0x00000145, 0x000000fc,
+ 0x00000146, 0x000000fe,
+ 0x00000147, 0x00000100,
+ 0x00000148, 0x00000102,
+ 0x00000149, 0x00000104,
+ 0x0000014c, 0x00000106,
+ 0x0000014d, 0x00000108,
+ 0x0000014e, 0x0000010a,
+ 0x0000014f, 0x0000010c,
+ 0x00000150, 0x0000010e,
+ 0x00000151, 0x00000110,
+ 0x00000154, 0x00000112,
+ 0x00000155, 0x00000114,
+ 0x00000156, 0x00000116,
+ 0x00000157, 0x00000118,
+ 0x00000158, 0x0000011a,
+ 0x00000159, 0x0000011c,
+ 0x0000015a, 0x0000011e,
+ 0x0000015b, 0x00000120,
+ 0x0000015c, 0x00000122,
+ 0x0000015d, 0x00000124,
+ 0x0000015e, 0x00000126,
+ 0x0000015f, 0x00000128,
+ 0x00000160, 0x0000012a,
+ 0x00000161, 0x0000012c,
+ 0x00000162, 0x0000012e,
+ 0x00000163, 0x00000130,
+ 0x00000164, 0x00000132,
+ 0x00000165, 0x00000134,
+ 0x00000168, 0x00000136,
+ 0x00000169, 0x00000138,
+ 0x0000016a, 0x0000013a,
+ 0x0000016b, 0x0000013c,
+ 0x0000016c, 0x0000013e,
+ 0x0000016d, 0x00000140,
+ 0x0000016e, 0x00000142,
+ 0x0000016f, 0x00000144,
+ 0x00000170, 0x00000146,
+ 0x00000171, 0x00000148,
+ 0x00000172, 0x0000014a,
+ 0x00000173, 0x0000014c,
+ 0x00000174, 0x0000014e,
+ 0x00000175, 0x00000150,
+ 0x00000176, 0x00000152,
+ 0x00000177, 0x00000154,
+ 0x00000178, 0x00000156,
+ 0x00000179, 0x00000158,
+ 0x0000017a, 0x0000015a,
+ 0x0000017b, 0x0000015c,
+ 0x0000017c, 0x0000015e,
+ 0x0000017d, 0x00000160,
+ 0x0000017e, 0x00000162,
+ 0x0000017f, 0x00000164,
+ 0x000001a0, 0x00000165,
+ 0x000001a1, 0x00000167,
+ 0x000001af, 0x00000169,
+ 0x000001b0, 0x0000016b,
+ 0x000001c4, 0x0000016d,
+ 0x000001c5, 0x00000170,
+ 0x000001c6, 0x00000173,
+ 0x000001c7, 0x00000176,
+ 0x000001c8, 0x00000178,
+ 0x000001c9, 0x0000017a,
+ 0x000001ca, 0x0000017c,
+ 0x000001cb, 0x0000017e,
+ 0x000001cc, 0x00000180,
+ 0x000001cd, 0x00000182,
+ 0x000001ce, 0x00000184,
+ 0x000001cf, 0x00000186,
+ 0x000001d0, 0x00000188,
+ 0x000001d1, 0x0000018a,
+ 0x000001d2, 0x0000018c,
+ 0x000001d3, 0x0000018e,
+ 0x000001d4, 0x00000190,
+ 0x000001d5, 0x00000192,
+ 0x000001d6, 0x00000195,
+ 0x000001d7, 0x00000198,
+ 0x000001d8, 0x0000019b,
+ 0x000001d9, 0x0000019e,
+ 0x000001da, 0x000001a1,
+ 0x000001db, 0x000001a4,
+ 0x000001dc, 0x000001a7,
+ 0x000001de, 0x000001aa,
+ 0x000001df, 0x000001ad,
+ 0x000001e0, 0x000001b0,
+ 0x000001e1, 0x000001b3,
+ 0x000001e2, 0x000001b6,
+ 0x000001e3, 0x000001b8,
+ 0x000001e6, 0x000001ba,
+ 0x000001e7, 0x000001bc,
+ 0x000001e8, 0x000001be,
+ 0x000001e9, 0x000001c0,
+ 0x000001ea, 0x000001c2,
+ 0x000001eb, 0x000001c4,
+ 0x000001ec, 0x000001c6,
+ 0x000001ed, 0x000001c9,
+ 0x000001ee, 0x000001cc,
+ 0x000001ef, 0x000001ce,
+ 0x000001f0, 0x000001d0,
+ 0x000001f1, 0x000001d2,
+ 0x000001f2, 0x000001d4,
+ 0x000001f3, 0x000001d6,
+ 0x000001f4, 0x000001d8,
+ 0x000001f5, 0x000001da,
+ 0x000001f8, 0x000001dc,
+ 0x000001f9, 0x000001de,
+ 0x000001fa, 0x000001e0,
+ 0x000001fb, 0x000001e3,
+ 0x000001fc, 0x000001e6,
+ 0x000001fd, 0x000001e8,
+ 0x000001fe, 0x000001ea,
+ 0x000001ff, 0x000001ec,
+ 0x00000200, 0x000001ee,
+ 0x00000201, 0x000001f0,
+ 0x00000202, 0x000001f2,
+ 0x00000203, 0x000001f4,
+ 0x00000204, 0x000001f6,
+ 0x00000205, 0x000001f8,
+ 0x00000206, 0x000001fa,
+ 0x00000207, 0x000001fc,
+ 0x00000208, 0x000001fe,
+ 0x00000209, 0x00000200,
+ 0x0000020a, 0x00000202,
+ 0x0000020b, 0x00000204,
+ 0x0000020c, 0x00000206,
+ 0x0000020d, 0x00000208,
+ 0x0000020e, 0x0000020a,
+ 0x0000020f, 0x0000020c,
+ 0x00000210, 0x0000020e,
+ 0x00000211, 0x00000210,
+ 0x00000212, 0x00000212,
+ 0x00000213, 0x00000214,
+ 0x00000214, 0x00000216,
+ 0x00000215, 0x00000218,
+ 0x00000216, 0x0000021a,
+ 0x00000217, 0x0000021c,
+ 0x00000218, 0x0000021e,
+ 0x00000219, 0x00000220,
+ 0x0000021a, 0x00000222,
+ 0x0000021b, 0x00000224,
+ 0x0000021e, 0x00000226,
+ 0x0000021f, 0x00000228,
+ 0x00000226, 0x0000022a,
+ 0x00000227, 0x0000022c,
+ 0x00000228, 0x0000022e,
+ 0x00000229, 0x00000230,
+ 0x0000022a, 0x00000232,
+ 0x0000022b, 0x00000235,
+ 0x0000022c, 0x00000238,
+ 0x0000022d, 0x0000023b,
+ 0x0000022e, 0x0000023e,
+ 0x0000022f, 0x00000240,
+ 0x00000230, 0x00000242,
+ 0x00000231, 0x00000245,
+ 0x00000232, 0x00000248,
+ 0x00000233, 0x0000024a,
+ 0x000002b0, 0x0000024c,
+ 0x000002b1, 0x0000024d,
+ 0x000002b2, 0x0000024e,
+ 0x000002b3, 0x0000024f,
+ 0x000002b4, 0x00000250,
+ 0x000002b5, 0x00000251,
+ 0x000002b6, 0x00000252,
+ 0x000002b7, 0x00000253,
+ 0x000002b8, 0x00000254,
+ 0x000002d8, 0x00000255,
+ 0x000002d9, 0x00000257,
+ 0x000002da, 0x00000259,
+ 0x000002db, 0x0000025b,
+ 0x000002dc, 0x0000025d,
+ 0x000002dd, 0x0000025f,
+ 0x000002e0, 0x00000261,
+ 0x000002e1, 0x00000262,
+ 0x000002e2, 0x00000263,
+ 0x000002e3, 0x00000264,
+ 0x000002e4, 0x00000265,
+ 0x00000340, 0x00000266,
+ 0x00000341, 0x00000267,
+ 0x00000343, 0x00000268,
+ 0x00000344, 0x00000269,
+ 0x00000374, 0x0000026b,
+ 0x0000037a, 0x0000026c,
+ 0x0000037e, 0x0000026e,
+ 0x00000384, 0x0000026f,
+ 0x00000385, 0x00000271,
+ 0x00000386, 0x00000274,
+ 0x00000387, 0x00000276,
+ 0x00000388, 0x00000277,
+ 0x00000389, 0x00000279,
+ 0x0000038a, 0x0000027b,
+ 0x0000038c, 0x0000027d,
+ 0x0000038e, 0x0000027f,
+ 0x0000038f, 0x00000281,
+ 0x00000390, 0x00000283,
+ 0x000003aa, 0x00000286,
+ 0x000003ab, 0x00000288,
+ 0x000003ac, 0x0000028a,
+ 0x000003ad, 0x0000028c,
+ 0x000003ae, 0x0000028e,
+ 0x000003af, 0x00000290,
+ 0x000003b0, 0x00000292,
+ 0x000003ca, 0x00000295,
+ 0x000003cb, 0x00000297,
+ 0x000003cc, 0x00000299,
+ 0x000003cd, 0x0000029b,
+ 0x000003ce, 0x0000029d,
+ 0x000003d0, 0x0000029f,
+ 0x000003d1, 0x000002a0,
+ 0x000003d2, 0x000002a1,
+ 0x000003d3, 0x000002a2,
+ 0x000003d4, 0x000002a4,
+ 0x000003d5, 0x000002a6,
+ 0x000003d6, 0x000002a7,
+ 0x000003f0, 0x000002a8,
+ 0x000003f1, 0x000002a9,
+ 0x000003f2, 0x000002aa,
+ 0x000003f4, 0x000002ab,
+ 0x000003f5, 0x000002ac,
+ 0x00000400, 0x000002ad,
+ 0x00000401, 0x000002af,
+ 0x00000403, 0x000002b1,
+ 0x00000407, 0x000002b3,
+ 0x0000040c, 0x000002b5,
+ 0x0000040d, 0x000002b7,
+ 0x0000040e, 0x000002b9,
+ 0x00000419, 0x000002bb,
+ 0x00000439, 0x000002bd,
+ 0x00000450, 0x000002bf,
+ 0x00000451, 0x000002c1,
+ 0x00000453, 0x000002c3,
+ 0x00000457, 0x000002c5,
+ 0x0000045c, 0x000002c7,
+ 0x0000045d, 0x000002c9,
+ 0x0000045e, 0x000002cb,
+ 0x00000476, 0x000002cd,
+ 0x00000477, 0x000002cf,
+ 0x000004c1, 0x000002d1,
+ 0x000004c2, 0x000002d3,
+ 0x000004d0, 0x000002d5,
+ 0x000004d1, 0x000002d7,
+ 0x000004d2, 0x000002d9,
+ 0x000004d3, 0x000002db,
+ 0x000004d6, 0x000002dd,
+ 0x000004d7, 0x000002df,
+ 0x000004da, 0x000002e1,
+ 0x000004db, 0x000002e3,
+ 0x000004dc, 0x000002e5,
+ 0x000004dd, 0x000002e7,
+ 0x000004de, 0x000002e9,
+ 0x000004df, 0x000002eb,
+ 0x000004e2, 0x000002ed,
+ 0x000004e3, 0x000002ef,
+ 0x000004e4, 0x000002f1,
+ 0x000004e5, 0x000002f3,
+ 0x000004e6, 0x000002f5,
+ 0x000004e7, 0x000002f7,
+ 0x000004ea, 0x000002f9,
+ 0x000004eb, 0x000002fb,
+ 0x000004ec, 0x000002fd,
+ 0x000004ed, 0x000002ff,
+ 0x000004ee, 0x00000301,
+ 0x000004ef, 0x00000303,
+ 0x000004f0, 0x00000305,
+ 0x000004f1, 0x00000307,
+ 0x000004f2, 0x00000309,
+ 0x000004f3, 0x0000030b,
+ 0x000004f4, 0x0000030d,
+ 0x000004f5, 0x0000030f,
+ 0x000004f8, 0x00000311,
+ 0x000004f9, 0x00000313,
+ 0x00000587, 0x00000315,
+ 0x00000622, 0x00000317,
+ 0x00000623, 0x00000319,
+ 0x00000624, 0x0000031b,
+ 0x00000625, 0x0000031d,
+ 0x00000626, 0x0000031f,
+ 0x00000675, 0x00000321,
+ 0x00000676, 0x00000323,
+ 0x00000677, 0x00000325,
+ 0x00000678, 0x00000327,
+ 0x000006c0, 0x00000329,
+ 0x000006c2, 0x0000032b,
+ 0x000006d3, 0x0000032d,
+ 0x00000929, 0x0000032f,
+ 0x00000931, 0x00000331,
+ 0x00000934, 0x00000333,
+ 0x00000958, 0x00000335,
+ 0x00000959, 0x00000337,
+ 0x0000095a, 0x00000339,
+ 0x0000095b, 0x0000033b,
+ 0x0000095c, 0x0000033d,
+ 0x0000095d, 0x0000033f,
+ 0x0000095e, 0x00000341,
+ 0x0000095f, 0x00000343,
+ 0x000009cb, 0x00000345,
+ 0x000009cc, 0x00000347,
+ 0x000009dc, 0x00000349,
+ 0x000009dd, 0x0000034b,
+ 0x000009df, 0x0000034d,
+ 0x00000a33, 0x0000034f,
+ 0x00000a36, 0x00000351,
+ 0x00000a59, 0x00000353,
+ 0x00000a5a, 0x00000355,
+ 0x00000a5b, 0x00000357,
+ 0x00000a5e, 0x00000359,
+ 0x00000b48, 0x0000035b,
+ 0x00000b4b, 0x0000035d,
+ 0x00000b4c, 0x0000035f,
+ 0x00000b5c, 0x00000361,
+ 0x00000b5d, 0x00000363,
+ 0x00000b94, 0x00000365,
+ 0x00000bca, 0x00000367,
+ 0x00000bcb, 0x00000369,
+ 0x00000bcc, 0x0000036b,
+ 0x00000c48, 0x0000036d,
+ 0x00000cc0, 0x0000036f,
+ 0x00000cc7, 0x00000371,
+ 0x00000cc8, 0x00000373,
+ 0x00000cca, 0x00000375,
+ 0x00000ccb, 0x00000377,
+ 0x00000d4a, 0x0000037a,
+ 0x00000d4b, 0x0000037c,
+ 0x00000d4c, 0x0000037e,
+ 0x00000dda, 0x00000380,
+ 0x00000ddc, 0x00000382,
+ 0x00000ddd, 0x00000384,
+ 0x00000dde, 0x00000387,
+ 0x00000e33, 0x00000389,
+ 0x00000eb3, 0x0000038b,
+ 0x00000edc, 0x0000038d,
+ 0x00000edd, 0x0000038f,
+ 0x00000f0c, 0x00000391,
+ 0x00000f43, 0x00000392,
+ 0x00000f4d, 0x00000394,
+ 0x00000f52, 0x00000396,
+ 0x00000f57, 0x00000398,
+ 0x00000f5c, 0x0000039a,
+ 0x00000f69, 0x0000039c,
+ 0x00000f73, 0x0000039e,
+ 0x00000f75, 0x000003a0,
+ 0x00000f76, 0x000003a2,
+ 0x00000f77, 0x000003a4,
+ 0x00000f78, 0x000003a7,
+ 0x00000f79, 0x000003a9,
+ 0x00000f81, 0x000003ac,
+ 0x00000f93, 0x000003ae,
+ 0x00000f9d, 0x000003b0,
+ 0x00000fa2, 0x000003b2,
+ 0x00000fa7, 0x000003b4,
+ 0x00000fac, 0x000003b6,
+ 0x00000fb9, 0x000003b8,
+ 0x00001026, 0x000003ba,
+ 0x00001e00, 0x000003bc,
+ 0x00001e01, 0x000003be,
+ 0x00001e02, 0x000003c0,
+ 0x00001e03, 0x000003c2,
+ 0x00001e04, 0x000003c4,
+ 0x00001e05, 0x000003c6,
+ 0x00001e06, 0x000003c8,
+ 0x00001e07, 0x000003ca,
+ 0x00001e08, 0x000003cc,
+ 0x00001e09, 0x000003cf,
+ 0x00001e0a, 0x000003d2,
+ 0x00001e0b, 0x000003d4,
+ 0x00001e0c, 0x000003d6,
+ 0x00001e0d, 0x000003d8,
+ 0x00001e0e, 0x000003da,
+ 0x00001e0f, 0x000003dc,
+ 0x00001e10, 0x000003de,
+ 0x00001e11, 0x000003e0,
+ 0x00001e12, 0x000003e2,
+ 0x00001e13, 0x000003e4,
+ 0x00001e14, 0x000003e6,
+ 0x00001e15, 0x000003e9,
+ 0x00001e16, 0x000003ec,
+ 0x00001e17, 0x000003ef,
+ 0x00001e18, 0x000003f2,
+ 0x00001e19, 0x000003f4,
+ 0x00001e1a, 0x000003f6,
+ 0x00001e1b, 0x000003f8,
+ 0x00001e1c, 0x000003fa,
+ 0x00001e1d, 0x000003fd,
+ 0x00001e1e, 0x00000400,
+ 0x00001e1f, 0x00000402,
+ 0x00001e20, 0x00000404,
+ 0x00001e21, 0x00000406,
+ 0x00001e22, 0x00000408,
+ 0x00001e23, 0x0000040a,
+ 0x00001e24, 0x0000040c,
+ 0x00001e25, 0x0000040e,
+ 0x00001e26, 0x00000410,
+ 0x00001e27, 0x00000412,
+ 0x00001e28, 0x00000414,
+ 0x00001e29, 0x00000416,
+ 0x00001e2a, 0x00000418,
+ 0x00001e2b, 0x0000041a,
+ 0x00001e2c, 0x0000041c,
+ 0x00001e2d, 0x0000041e,
+ 0x00001e2e, 0x00000420,
+ 0x00001e2f, 0x00000423,
+ 0x00001e30, 0x00000426,
+ 0x00001e31, 0x00000428,
+ 0x00001e32, 0x0000042a,
+ 0x00001e33, 0x0000042c,
+ 0x00001e34, 0x0000042e,
+ 0x00001e35, 0x00000430,
+ 0x00001e36, 0x00000432,
+ 0x00001e37, 0x00000434,
+ 0x00001e38, 0x00000436,
+ 0x00001e39, 0x00000439,
+ 0x00001e3a, 0x0000043c,
+ 0x00001e3b, 0x0000043e,
+ 0x00001e3c, 0x00000440,
+ 0x00001e3d, 0x00000442,
+ 0x00001e3e, 0x00000444,
+ 0x00001e3f, 0x00000446,
+ 0x00001e40, 0x00000448,
+ 0x00001e41, 0x0000044a,
+ 0x00001e42, 0x0000044c,
+ 0x00001e43, 0x0000044e,
+ 0x00001e44, 0x00000450,
+ 0x00001e45, 0x00000452,
+ 0x00001e46, 0x00000454,
+ 0x00001e47, 0x00000456,
+ 0x00001e48, 0x00000458,
+ 0x00001e49, 0x0000045a,
+ 0x00001e4a, 0x0000045c,
+ 0x00001e4b, 0x0000045e,
+ 0x00001e4c, 0x00000460,
+ 0x00001e4d, 0x00000463,
+ 0x00001e4e, 0x00000466,
+ 0x00001e4f, 0x00000469,
+ 0x00001e50, 0x0000046c,
+ 0x00001e51, 0x0000046f,
+ 0x00001e52, 0x00000472,
+ 0x00001e53, 0x00000475,
+ 0x00001e54, 0x00000478,
+ 0x00001e55, 0x0000047a,
+ 0x00001e56, 0x0000047c,
+ 0x00001e57, 0x0000047e,
+ 0x00001e58, 0x00000480,
+ 0x00001e59, 0x00000482,
+ 0x00001e5a, 0x00000484,
+ 0x00001e5b, 0x00000486,
+ 0x00001e5c, 0x00000488,
+ 0x00001e5d, 0x0000048b,
+ 0x00001e5e, 0x0000048e,
+ 0x00001e5f, 0x00000490,
+ 0x00001e60, 0x00000492,
+ 0x00001e61, 0x00000494,
+ 0x00001e62, 0x00000496,
+ 0x00001e63, 0x00000498,
+ 0x00001e64, 0x0000049a,
+ 0x00001e65, 0x0000049d,
+ 0x00001e66, 0x000004a0,
+ 0x00001e67, 0x000004a3,
+ 0x00001e68, 0x000004a6,
+ 0x00001e69, 0x000004a9,
+ 0x00001e6a, 0x000004ac,
+ 0x00001e6b, 0x000004ae,
+ 0x00001e6c, 0x000004b0,
+ 0x00001e6d, 0x000004b2,
+ 0x00001e6e, 0x000004b4,
+ 0x00001e6f, 0x000004b6,
+ 0x00001e70, 0x000004b8,
+ 0x00001e71, 0x000004ba,
+ 0x00001e72, 0x000004bc,
+ 0x00001e73, 0x000004be,
+ 0x00001e74, 0x000004c0,
+ 0x00001e75, 0x000004c2,
+ 0x00001e76, 0x000004c4,
+ 0x00001e77, 0x000004c6,
+ 0x00001e78, 0x000004c8,
+ 0x00001e79, 0x000004cb,
+ 0x00001e7a, 0x000004ce,
+ 0x00001e7b, 0x000004d1,
+ 0x00001e7c, 0x000004d4,
+ 0x00001e7d, 0x000004d6,
+ 0x00001e7e, 0x000004d8,
+ 0x00001e7f, 0x000004da,
+ 0x00001e80, 0x000004dc,
+ 0x00001e81, 0x000004de,
+ 0x00001e82, 0x000004e0,
+ 0x00001e83, 0x000004e2,
+ 0x00001e84, 0x000004e4,
+ 0x00001e85, 0x000004e6,
+ 0x00001e86, 0x000004e8,
+ 0x00001e87, 0x000004ea,
+ 0x00001e88, 0x000004ec,
+ 0x00001e89, 0x000004ee,
+ 0x00001e8a, 0x000004f0,
+ 0x00001e8b, 0x000004f2,
+ 0x00001e8c, 0x000004f4,
+ 0x00001e8d, 0x000004f6,
+ 0x00001e8e, 0x000004f8,
+ 0x00001e8f, 0x000004fa,
+ 0x00001e90, 0x000004fc,
+ 0x00001e91, 0x000004fe,
+ 0x00001e92, 0x00000500,
+ 0x00001e93, 0x00000502,
+ 0x00001e94, 0x00000504,
+ 0x00001e95, 0x00000506,
+ 0x00001e96, 0x00000508,
+ 0x00001e97, 0x0000050a,
+ 0x00001e98, 0x0000050c,
+ 0x00001e99, 0x0000050e,
+ 0x00001e9a, 0x00000510,
+ 0x00001e9b, 0x00000512,
+ 0x00001ea0, 0x00000514,
+ 0x00001ea1, 0x00000516,
+ 0x00001ea2, 0x00000518,
+ 0x00001ea3, 0x0000051a,
+ 0x00001ea4, 0x0000051c,
+ 0x00001ea5, 0x0000051f,
+ 0x00001ea6, 0x00000522,
+ 0x00001ea7, 0x00000525,
+ 0x00001ea8, 0x00000528,
+ 0x00001ea9, 0x0000052b,
+ 0x00001eaa, 0x0000052e,
+ 0x00001eab, 0x00000531,
+ 0x00001eac, 0x00000534,
+ 0x00001ead, 0x00000537,
+ 0x00001eae, 0x0000053a,
+ 0x00001eaf, 0x0000053d,
+ 0x00001eb0, 0x00000540,
+ 0x00001eb1, 0x00000543,
+ 0x00001eb2, 0x00000546,
+ 0x00001eb3, 0x00000549,
+ 0x00001eb4, 0x0000054c,
+ 0x00001eb5, 0x0000054f,
+ 0x00001eb6, 0x00000552,
+ 0x00001eb7, 0x00000555,
+ 0x00001eb8, 0x00000558,
+ 0x00001eb9, 0x0000055a,
+ 0x00001eba, 0x0000055c,
+ 0x00001ebb, 0x0000055e,
+ 0x00001ebc, 0x00000560,
+ 0x00001ebd, 0x00000562,
+ 0x00001ebe, 0x00000564,
+ 0x00001ebf, 0x00000567,
+ 0x00001ec0, 0x0000056a,
+ 0x00001ec1, 0x0000056d,
+ 0x00001ec2, 0x00000570,
+ 0x00001ec3, 0x00000573,
+ 0x00001ec4, 0x00000576,
+ 0x00001ec5, 0x00000579,
+ 0x00001ec6, 0x0000057c,
+ 0x00001ec7, 0x0000057f,
+ 0x00001ec8, 0x00000582,
+ 0x00001ec9, 0x00000584,
+ 0x00001eca, 0x00000586,
+ 0x00001ecb, 0x00000588,
+ 0x00001ecc, 0x0000058a,
+ 0x00001ecd, 0x0000058c,
+ 0x00001ece, 0x0000058e,
+ 0x00001ecf, 0x00000590,
+ 0x00001ed0, 0x00000592,
+ 0x00001ed1, 0x00000595,
+ 0x00001ed2, 0x00000598,
+ 0x00001ed3, 0x0000059b,
+ 0x00001ed4, 0x0000059e,
+ 0x00001ed5, 0x000005a1,
+ 0x00001ed6, 0x000005a4,
+ 0x00001ed7, 0x000005a7,
+ 0x00001ed8, 0x000005aa,
+ 0x00001ed9, 0x000005ad,
+ 0x00001eda, 0x000005b0,
+ 0x00001edb, 0x000005b3,
+ 0x00001edc, 0x000005b6,
+ 0x00001edd, 0x000005b9,
+ 0x00001ede, 0x000005bc,
+ 0x00001edf, 0x000005bf,
+ 0x00001ee0, 0x000005c2,
+ 0x00001ee1, 0x000005c5,
+ 0x00001ee2, 0x000005c8,
+ 0x00001ee3, 0x000005cb,
+ 0x00001ee4, 0x000005ce,
+ 0x00001ee5, 0x000005d0,
+ 0x00001ee6, 0x000005d2,
+ 0x00001ee7, 0x000005d4,
+ 0x00001ee8, 0x000005d6,
+ 0x00001ee9, 0x000005d9,
+ 0x00001eea, 0x000005dc,
+ 0x00001eeb, 0x000005df,
+ 0x00001eec, 0x000005e2,
+ 0x00001eed, 0x000005e5,
+ 0x00001eee, 0x000005e8,
+ 0x00001eef, 0x000005eb,
+ 0x00001ef0, 0x000005ee,
+ 0x00001ef1, 0x000005f1,
+ 0x00001ef2, 0x000005f4,
+ 0x00001ef3, 0x000005f6,
+ 0x00001ef4, 0x000005f8,
+ 0x00001ef5, 0x000005fa,
+ 0x00001ef6, 0x000005fc,
+ 0x00001ef7, 0x000005fe,
+ 0x00001ef8, 0x00000600,
+ 0x00001ef9, 0x00000602,
+ 0x00001f00, 0x00000604,
+ 0x00001f01, 0x00000606,
+ 0x00001f02, 0x00000608,
+ 0x00001f03, 0x0000060b,
+ 0x00001f04, 0x0000060e,
+ 0x00001f05, 0x00000611,
+ 0x00001f06, 0x00000614,
+ 0x00001f07, 0x00000617,
+ 0x00001f08, 0x0000061a,
+ 0x00001f09, 0x0000061c,
+ 0x00001f0a, 0x0000061e,
+ 0x00001f0b, 0x00000621,
+ 0x00001f0c, 0x00000624,
+ 0x00001f0d, 0x00000627,
+ 0x00001f0e, 0x0000062a,
+ 0x00001f0f, 0x0000062d,
+ 0x00001f10, 0x00000630,
+ 0x00001f11, 0x00000632,
+ 0x00001f12, 0x00000634,
+ 0x00001f13, 0x00000637,
+ 0x00001f14, 0x0000063a,
+ 0x00001f15, 0x0000063d,
+ 0x00001f18, 0x00000640,
+ 0x00001f19, 0x00000642,
+ 0x00001f1a, 0x00000644,
+ 0x00001f1b, 0x00000647,
+ 0x00001f1c, 0x0000064a,
+ 0x00001f1d, 0x0000064d,
+ 0x00001f20, 0x00000650,
+ 0x00001f21, 0x00000652,
+ 0x00001f22, 0x00000654,
+ 0x00001f23, 0x00000657,
+ 0x00001f24, 0x0000065a,
+ 0x00001f25, 0x0000065d,
+ 0x00001f26, 0x00000660,
+ 0x00001f27, 0x00000663,
+ 0x00001f28, 0x00000666,
+ 0x00001f29, 0x00000668,
+ 0x00001f2a, 0x0000066a,
+ 0x00001f2b, 0x0000066d,
+ 0x00001f2c, 0x00000670,
+ 0x00001f2d, 0x00000673,
+ 0x00001f2e, 0x00000676,
+ 0x00001f2f, 0x00000679,
+ 0x00001f30, 0x0000067c,
+ 0x00001f31, 0x0000067e,
+ 0x00001f32, 0x00000680,
+ 0x00001f33, 0x00000683,
+ 0x00001f34, 0x00000686,
+ 0x00001f35, 0x00000689,
+ 0x00001f36, 0x0000068c,
+ 0x00001f37, 0x0000068f,
+ 0x00001f38, 0x00000692,
+ 0x00001f39, 0x00000694,
+ 0x00001f3a, 0x00000696,
+ 0x00001f3b, 0x00000699,
+ 0x00001f3c, 0x0000069c,
+ 0x00001f3d, 0x0000069f,
+ 0x00001f3e, 0x000006a2,
+ 0x00001f3f, 0x000006a5,
+ 0x00001f40, 0x000006a8,
+ 0x00001f41, 0x000006aa,
+ 0x00001f42, 0x000006ac,
+ 0x00001f43, 0x000006af,
+ 0x00001f44, 0x000006b2,
+ 0x00001f45, 0x000006b5,
+ 0x00001f48, 0x000006b8,
+ 0x00001f49, 0x000006ba,
+ 0x00001f4a, 0x000006bc,
+ 0x00001f4b, 0x000006bf,
+ 0x00001f4c, 0x000006c2,
+ 0x00001f4d, 0x000006c5,
+ 0x00001f50, 0x000006c8,
+ 0x00001f51, 0x000006ca,
+ 0x00001f52, 0x000006cc,
+ 0x00001f53, 0x000006cf,
+ 0x00001f54, 0x000006d2,
+ 0x00001f55, 0x000006d5,
+ 0x00001f56, 0x000006d8,
+ 0x00001f57, 0x000006db,
+ 0x00001f59, 0x000006de,
+ 0x00001f5b, 0x000006e0,
+ 0x00001f5d, 0x000006e3,
+ 0x00001f5f, 0x000006e6,
+ 0x00001f60, 0x000006e9,
+ 0x00001f61, 0x000006eb,
+ 0x00001f62, 0x000006ed,
+ 0x00001f63, 0x000006f0,
+ 0x00001f64, 0x000006f3,
+ 0x00001f65, 0x000006f6,
+ 0x00001f66, 0x000006f9,
+ 0x00001f67, 0x000006fc,
+ 0x00001f68, 0x000006ff,
+ 0x00001f69, 0x00000701,
+ 0x00001f6a, 0x00000703,
+ 0x00001f6b, 0x00000706,
+ 0x00001f6c, 0x00000709,
+ 0x00001f6d, 0x0000070c,
+ 0x00001f6e, 0x0000070f,
+ 0x00001f6f, 0x00000712,
+ 0x00001f70, 0x00000715,
+ 0x00001f71, 0x00000717,
+ 0x00001f72, 0x00000719,
+ 0x00001f73, 0x0000071b,
+ 0x00001f74, 0x0000071d,
+ 0x00001f75, 0x0000071f,
+ 0x00001f76, 0x00000721,
+ 0x00001f77, 0x00000723,
+ 0x00001f78, 0x00000725,
+ 0x00001f79, 0x00000727,
+ 0x00001f7a, 0x00000729,
+ 0x00001f7b, 0x0000072b,
+ 0x00001f7c, 0x0000072d,
+ 0x00001f7d, 0x0000072f,
+ 0x00001f80, 0x00000731,
+ 0x00001f81, 0x00000734,
+ 0x00001f82, 0x00000737,
+ 0x00001f83, 0x0000073b,
+ 0x00001f84, 0x0000073f,
+ 0x00001f85, 0x00000743,
+ 0x00001f86, 0x00000747,
+ 0x00001f87, 0x0000074b,
+ 0x00001f88, 0x0000074f,
+ 0x00001f89, 0x00000752,
+ 0x00001f8a, 0x00000755,
+ 0x00001f8b, 0x00000759,
+ 0x00001f8c, 0x0000075d,
+ 0x00001f8d, 0x00000761,
+ 0x00001f8e, 0x00000765,
+ 0x00001f8f, 0x00000769,
+ 0x00001f90, 0x0000076d,
+ 0x00001f91, 0x00000770,
+ 0x00001f92, 0x00000773,
+ 0x00001f93, 0x00000777,
+ 0x00001f94, 0x0000077b,
+ 0x00001f95, 0x0000077f,
+ 0x00001f96, 0x00000783,
+ 0x00001f97, 0x00000787,
+ 0x00001f98, 0x0000078b,
+ 0x00001f99, 0x0000078e,
+ 0x00001f9a, 0x00000791,
+ 0x00001f9b, 0x00000795,
+ 0x00001f9c, 0x00000799,
+ 0x00001f9d, 0x0000079d,
+ 0x00001f9e, 0x000007a1,
+ 0x00001f9f, 0x000007a5,
+ 0x00001fa0, 0x000007a9,
+ 0x00001fa1, 0x000007ac,
+ 0x00001fa2, 0x000007af,
+ 0x00001fa3, 0x000007b3,
+ 0x00001fa4, 0x000007b7,
+ 0x00001fa5, 0x000007bb,
+ 0x00001fa6, 0x000007bf,
+ 0x00001fa7, 0x000007c3,
+ 0x00001fa8, 0x000007c7,
+ 0x00001fa9, 0x000007ca,
+ 0x00001faa, 0x000007cd,
+ 0x00001fab, 0x000007d1,
+ 0x00001fac, 0x000007d5,
+ 0x00001fad, 0x000007d9,
+ 0x00001fae, 0x000007dd,
+ 0x00001faf, 0x000007e1,
+ 0x00001fb0, 0x000007e5,
+ 0x00001fb1, 0x000007e7,
+ 0x00001fb2, 0x000007e9,
+ 0x00001fb3, 0x000007ec,
+ 0x00001fb4, 0x000007ee,
+ 0x00001fb6, 0x000007f1,
+ 0x00001fb7, 0x000007f3,
+ 0x00001fb8, 0x000007f6,
+ 0x00001fb9, 0x000007f8,
+ 0x00001fba, 0x000007fa,
+ 0x00001fbb, 0x000007fc,
+ 0x00001fbc, 0x000007fe,
+ 0x00001fbd, 0x00000800,
+ 0x00001fbe, 0x00000802,
+ 0x00001fbf, 0x00000803,
+ 0x00001fc0, 0x00000805,
+ 0x00001fc1, 0x00000807,
+ 0x00001fc2, 0x0000080a,
+ 0x00001fc3, 0x0000080d,
+ 0x00001fc4, 0x0000080f,
+ 0x00001fc6, 0x00000812,
+ 0x00001fc7, 0x00000814,
+ 0x00001fc8, 0x00000817,
+ 0x00001fc9, 0x00000819,
+ 0x00001fca, 0x0000081b,
+ 0x00001fcb, 0x0000081d,
+ 0x00001fcc, 0x0000081f,
+ 0x00001fcd, 0x00000821,
+ 0x00001fce, 0x00000824,
+ 0x00001fcf, 0x00000827,
+ 0x00001fd0, 0x0000082a,
+ 0x00001fd1, 0x0000082c,
+ 0x00001fd2, 0x0000082e,
+ 0x00001fd3, 0x00000831,
+ 0x00001fd6, 0x00000834,
+ 0x00001fd7, 0x00000836,
+ 0x00001fd8, 0x00000839,
+ 0x00001fd9, 0x0000083b,
+ 0x00001fda, 0x0000083d,
+ 0x00001fdb, 0x0000083f,
+ 0x00001fdd, 0x00000841,
+ 0x00001fde, 0x00000844,
+ 0x00001fdf, 0x00000847,
+ 0x00001fe0, 0x0000084a,
+ 0x00001fe1, 0x0000084c,
+ 0x00001fe2, 0x0000084e,
+ 0x00001fe3, 0x00000851,
+ 0x00001fe4, 0x00000854,
+ 0x00001fe5, 0x00000856,
+ 0x00001fe6, 0x00000858,
+ 0x00001fe7, 0x0000085a,
+ 0x00001fe8, 0x0000085d,
+ 0x00001fe9, 0x0000085f,
+ 0x00001fea, 0x00000861,
+ 0x00001feb, 0x00000863,
+ 0x00001fec, 0x00000865,
+ 0x00001fed, 0x00000867,
+ 0x00001fee, 0x0000086a,
+ 0x00001fef, 0x0000086d,
+ 0x00001ff2, 0x0000086e,
+ 0x00001ff3, 0x00000871,
+ 0x00001ff4, 0x00000873,
+ 0x00001ff6, 0x00000876,
+ 0x00001ff7, 0x00000878,
+ 0x00001ff8, 0x0000087b,
+ 0x00001ff9, 0x0000087d,
+ 0x00001ffa, 0x0000087f,
+ 0x00001ffb, 0x00000881,
+ 0x00001ffc, 0x00000883,
+ 0x00001ffd, 0x00000885,
+ 0x00001ffe, 0x00000887,
+ 0x00002000, 0x00000889,
+ 0x00002001, 0x0000088a,
+ 0x00002002, 0x0000088b,
+ 0x00002003, 0x0000088c,
+ 0x00002004, 0x0000088d,
+ 0x00002005, 0x0000088e,
+ 0x00002006, 0x0000088f,
+ 0x00002007, 0x00000890,
+ 0x00002008, 0x00000891,
+ 0x00002009, 0x00000892,
+ 0x0000200a, 0x00000893,
+ 0x00002011, 0x00000894,
+ 0x00002017, 0x00000895,
+ 0x00002024, 0x00000897,
+ 0x00002025, 0x00000898,
+ 0x00002026, 0x0000089a,
+ 0x0000202f, 0x0000089d,
+ 0x00002033, 0x0000089e,
+ 0x00002034, 0x000008a0,
+ 0x00002036, 0x000008a3,
+ 0x00002037, 0x000008a5,
+ 0x0000203c, 0x000008a8,
+ 0x0000203e, 0x000008aa,
+ 0x00002047, 0x000008ac,
+ 0x00002048, 0x000008ae,
+ 0x00002049, 0x000008b0,
+ 0x00002057, 0x000008b2,
+ 0x0000205f, 0x000008b6,
+ 0x00002070, 0x000008b7,
+ 0x00002071, 0x000008b8,
+ 0x00002074, 0x000008b9,
+ 0x00002075, 0x000008ba,
+ 0x00002076, 0x000008bb,
+ 0x00002077, 0x000008bc,
+ 0x00002078, 0x000008bd,
+ 0x00002079, 0x000008be,
+ 0x0000207a, 0x000008bf,
+ 0x0000207b, 0x000008c0,
+ 0x0000207c, 0x000008c1,
+ 0x0000207d, 0x000008c2,
+ 0x0000207e, 0x000008c3,
+ 0x0000207f, 0x000008c4,
+ 0x00002080, 0x000008c5,
+ 0x00002081, 0x000008c6,
+ 0x00002082, 0x000008c7,
+ 0x00002083, 0x000008c8,
+ 0x00002084, 0x000008c9,
+ 0x00002085, 0x000008ca,
+ 0x00002086, 0x000008cb,
+ 0x00002087, 0x000008cc,
+ 0x00002088, 0x000008cd,
+ 0x00002089, 0x000008ce,
+ 0x0000208a, 0x000008cf,
+ 0x0000208b, 0x000008d0,
+ 0x0000208c, 0x000008d1,
+ 0x0000208d, 0x000008d2,
+ 0x0000208e, 0x000008d3,
+ 0x000020a8, 0x000008d4,
+ 0x00002100, 0x000008d6,
+ 0x00002101, 0x000008d9,
+ 0x00002102, 0x000008dc,
+ 0x00002103, 0x000008dd,
+ 0x00002105, 0x000008df,
+ 0x00002106, 0x000008e2,
+ 0x00002107, 0x000008e5,
+ 0x00002109, 0x000008e6,
+ 0x0000210a, 0x000008e8,
+ 0x0000210b, 0x000008e9,
+ 0x0000210c, 0x000008ea,
+ 0x0000210d, 0x000008eb,
+ 0x0000210e, 0x000008ec,
+ 0x0000210f, 0x000008ed,
+ 0x00002110, 0x000008ee,
+ 0x00002111, 0x000008ef,
+ 0x00002112, 0x000008f0,
+ 0x00002113, 0x000008f1,
+ 0x00002115, 0x000008f2,
+ 0x00002116, 0x000008f3,
+ 0x00002119, 0x000008f5,
+ 0x0000211a, 0x000008f6,
+ 0x0000211b, 0x000008f7,
+ 0x0000211c, 0x000008f8,
+ 0x0000211d, 0x000008f9,
+ 0x00002120, 0x000008fa,
+ 0x00002121, 0x000008fc,
+ 0x00002122, 0x000008ff,
+ 0x00002124, 0x00000901,
+ 0x00002126, 0x00000902,
+ 0x00002128, 0x00000903,
+ 0x0000212a, 0x00000904,
+ 0x0000212b, 0x00000905,
+ 0x0000212c, 0x00000907,
+ 0x0000212d, 0x00000908,
+ 0x0000212f, 0x00000909,
+ 0x00002130, 0x0000090a,
+ 0x00002131, 0x0000090b,
+ 0x00002133, 0x0000090c,
+ 0x00002134, 0x0000090d,
+ 0x00002135, 0x0000090e,
+ 0x00002136, 0x0000090f,
+ 0x00002137, 0x00000910,
+ 0x00002138, 0x00000911,
+ 0x00002139, 0x00000912,
+ 0x0000213d, 0x00000913,
+ 0x0000213e, 0x00000914,
+ 0x0000213f, 0x00000915,
+ 0x00002140, 0x00000916,
+ 0x00002145, 0x00000917,
+ 0x00002146, 0x00000918,
+ 0x00002147, 0x00000919,
+ 0x00002148, 0x0000091a,
+ 0x00002149, 0x0000091b,
+ 0x00002153, 0x0000091c,
+ 0x00002154, 0x0000091f,
+ 0x00002155, 0x00000922,
+ 0x00002156, 0x00000925,
+ 0x00002157, 0x00000928,
+ 0x00002158, 0x0000092b,
+ 0x00002159, 0x0000092e,
+ 0x0000215a, 0x00000931,
+ 0x0000215b, 0x00000934,
+ 0x0000215c, 0x00000937,
+ 0x0000215d, 0x0000093a,
+ 0x0000215e, 0x0000093d,
+ 0x0000215f, 0x00000940,
+ 0x00002160, 0x00000942,
+ 0x00002161, 0x00000943,
+ 0x00002162, 0x00000945,
+ 0x00002163, 0x00000948,
+ 0x00002164, 0x0000094a,
+ 0x00002165, 0x0000094b,
+ 0x00002166, 0x0000094d,
+ 0x00002167, 0x00000950,
+ 0x00002168, 0x00000954,
+ 0x00002169, 0x00000956,
+ 0x0000216a, 0x00000957,
+ 0x0000216b, 0x00000959,
+ 0x0000216c, 0x0000095c,
+ 0x0000216d, 0x0000095d,
+ 0x0000216e, 0x0000095e,
+ 0x0000216f, 0x0000095f,
+ 0x00002170, 0x00000960,
+ 0x00002171, 0x00000961,
+ 0x00002172, 0x00000963,
+ 0x00002173, 0x00000966,
+ 0x00002174, 0x00000968,
+ 0x00002175, 0x00000969,
+ 0x00002176, 0x0000096b,
+ 0x00002177, 0x0000096e,
+ 0x00002178, 0x00000972,
+ 0x00002179, 0x00000974,
+ 0x0000217a, 0x00000975,
+ 0x0000217b, 0x00000977,
+ 0x0000217c, 0x0000097a,
+ 0x0000217d, 0x0000097b,
+ 0x0000217e, 0x0000097c,
+ 0x0000217f, 0x0000097d,
+ 0x0000219a, 0x0000097e,
+ 0x0000219b, 0x00000980,
+ 0x000021ae, 0x00000982,
+ 0x000021cd, 0x00000984,
+ 0x000021ce, 0x00000986,
+ 0x000021cf, 0x00000988,
+ 0x00002204, 0x0000098a,
+ 0x00002209, 0x0000098c,
+ 0x0000220c, 0x0000098e,
+ 0x00002224, 0x00000990,
+ 0x00002226, 0x00000992,
+ 0x0000222c, 0x00000994,
+ 0x0000222d, 0x00000996,
+ 0x0000222f, 0x00000999,
+ 0x00002230, 0x0000099b,
+ 0x00002241, 0x0000099e,
+ 0x00002244, 0x000009a0,
+ 0x00002247, 0x000009a2,
+ 0x00002249, 0x000009a4,
+ 0x00002260, 0x000009a6,
+ 0x00002262, 0x000009a8,
+ 0x0000226d, 0x000009aa,
+ 0x0000226e, 0x000009ac,
+ 0x0000226f, 0x000009ae,
+ 0x00002270, 0x000009b0,
+ 0x00002271, 0x000009b2,
+ 0x00002274, 0x000009b4,
+ 0x00002275, 0x000009b6,
+ 0x00002278, 0x000009b8,
+ 0x00002279, 0x000009ba,
+ 0x00002280, 0x000009bc,
+ 0x00002281, 0x000009be,
+ 0x00002284, 0x000009c0,
+ 0x00002285, 0x000009c2,
+ 0x00002288, 0x000009c4,
+ 0x00002289, 0x000009c6,
+ 0x000022ac, 0x000009c8,
+ 0x000022ad, 0x000009ca,
+ 0x000022ae, 0x000009cc,
+ 0x000022af, 0x000009ce,
+ 0x000022e0, 0x000009d0,
+ 0x000022e1, 0x000009d2,
+ 0x000022e2, 0x000009d4,
+ 0x000022e3, 0x000009d6,
+ 0x000022ea, 0x000009d8,
+ 0x000022eb, 0x000009da,
+ 0x000022ec, 0x000009dc,
+ 0x000022ed, 0x000009de,
+ 0x00002329, 0x000009e0,
+ 0x0000232a, 0x000009e1,
+ 0x00002460, 0x000009e2,
+ 0x00002461, 0x000009e3,
+ 0x00002462, 0x000009e4,
+ 0x00002463, 0x000009e5,
+ 0x00002464, 0x000009e6,
+ 0x00002465, 0x000009e7,
+ 0x00002466, 0x000009e8,
+ 0x00002467, 0x000009e9,
+ 0x00002468, 0x000009ea,
+ 0x00002469, 0x000009eb,
+ 0x0000246a, 0x000009ed,
+ 0x0000246b, 0x000009ef,
+ 0x0000246c, 0x000009f1,
+ 0x0000246d, 0x000009f3,
+ 0x0000246e, 0x000009f5,
+ 0x0000246f, 0x000009f7,
+ 0x00002470, 0x000009f9,
+ 0x00002471, 0x000009fb,
+ 0x00002472, 0x000009fd,
+ 0x00002473, 0x000009ff,
+ 0x00002474, 0x00000a01,
+ 0x00002475, 0x00000a04,
+ 0x00002476, 0x00000a07,
+ 0x00002477, 0x00000a0a,
+ 0x00002478, 0x00000a0d,
+ 0x00002479, 0x00000a10,
+ 0x0000247a, 0x00000a13,
+ 0x0000247b, 0x00000a16,
+ 0x0000247c, 0x00000a19,
+ 0x0000247d, 0x00000a1c,
+ 0x0000247e, 0x00000a20,
+ 0x0000247f, 0x00000a24,
+ 0x00002480, 0x00000a28,
+ 0x00002481, 0x00000a2c,
+ 0x00002482, 0x00000a30,
+ 0x00002483, 0x00000a34,
+ 0x00002484, 0x00000a38,
+ 0x00002485, 0x00000a3c,
+ 0x00002486, 0x00000a40,
+ 0x00002487, 0x00000a44,
+ 0x00002488, 0x00000a48,
+ 0x00002489, 0x00000a4a,
+ 0x0000248a, 0x00000a4c,
+ 0x0000248b, 0x00000a4e,
+ 0x0000248c, 0x00000a50,
+ 0x0000248d, 0x00000a52,
+ 0x0000248e, 0x00000a54,
+ 0x0000248f, 0x00000a56,
+ 0x00002490, 0x00000a58,
+ 0x00002491, 0x00000a5a,
+ 0x00002492, 0x00000a5d,
+ 0x00002493, 0x00000a60,
+ 0x00002494, 0x00000a63,
+ 0x00002495, 0x00000a66,
+ 0x00002496, 0x00000a69,
+ 0x00002497, 0x00000a6c,
+ 0x00002498, 0x00000a6f,
+ 0x00002499, 0x00000a72,
+ 0x0000249a, 0x00000a75,
+ 0x0000249b, 0x00000a78,
+ 0x0000249c, 0x00000a7b,
+ 0x0000249d, 0x00000a7e,
+ 0x0000249e, 0x00000a81,
+ 0x0000249f, 0x00000a84,
+ 0x000024a0, 0x00000a87,
+ 0x000024a1, 0x00000a8a,
+ 0x000024a2, 0x00000a8d,
+ 0x000024a3, 0x00000a90,
+ 0x000024a4, 0x00000a93,
+ 0x000024a5, 0x00000a96,
+ 0x000024a6, 0x00000a99,
+ 0x000024a7, 0x00000a9c,
+ 0x000024a8, 0x00000a9f,
+ 0x000024a9, 0x00000aa2,
+ 0x000024aa, 0x00000aa5,
+ 0x000024ab, 0x00000aa8,
+ 0x000024ac, 0x00000aab,
+ 0x000024ad, 0x00000aae,
+ 0x000024ae, 0x00000ab1,
+ 0x000024af, 0x00000ab4,
+ 0x000024b0, 0x00000ab7,
+ 0x000024b1, 0x00000aba,
+ 0x000024b2, 0x00000abd,
+ 0x000024b3, 0x00000ac0,
+ 0x000024b4, 0x00000ac3,
+ 0x000024b5, 0x00000ac6,
+ 0x000024b6, 0x00000ac9,
+ 0x000024b7, 0x00000aca,
+ 0x000024b8, 0x00000acb,
+ 0x000024b9, 0x00000acc,
+ 0x000024ba, 0x00000acd,
+ 0x000024bb, 0x00000ace,
+ 0x000024bc, 0x00000acf,
+ 0x000024bd, 0x00000ad0,
+ 0x000024be, 0x00000ad1,
+ 0x000024bf, 0x00000ad2,
+ 0x000024c0, 0x00000ad3,
+ 0x000024c1, 0x00000ad4,
+ 0x000024c2, 0x00000ad5,
+ 0x000024c3, 0x00000ad6,
+ 0x000024c4, 0x00000ad7,
+ 0x000024c5, 0x00000ad8,
+ 0x000024c6, 0x00000ad9,
+ 0x000024c7, 0x00000ada,
+ 0x000024c8, 0x00000adb,
+ 0x000024c9, 0x00000adc,
+ 0x000024ca, 0x00000add,
+ 0x000024cb, 0x00000ade,
+ 0x000024cc, 0x00000adf,
+ 0x000024cd, 0x00000ae0,
+ 0x000024ce, 0x00000ae1,
+ 0x000024cf, 0x00000ae2,
+ 0x000024d0, 0x00000ae3,
+ 0x000024d1, 0x00000ae4,
+ 0x000024d2, 0x00000ae5,
+ 0x000024d3, 0x00000ae6,
+ 0x000024d4, 0x00000ae7,
+ 0x000024d5, 0x00000ae8,
+ 0x000024d6, 0x00000ae9,
+ 0x000024d7, 0x00000aea,
+ 0x000024d8, 0x00000aeb,
+ 0x000024d9, 0x00000aec,
+ 0x000024da, 0x00000aed,
+ 0x000024db, 0x00000aee,
+ 0x000024dc, 0x00000aef,
+ 0x000024dd, 0x00000af0,
+ 0x000024de, 0x00000af1,
+ 0x000024df, 0x00000af2,
+ 0x000024e0, 0x00000af3,
+ 0x000024e1, 0x00000af4,
+ 0x000024e2, 0x00000af5,
+ 0x000024e3, 0x00000af6,
+ 0x000024e4, 0x00000af7,
+ 0x000024e5, 0x00000af8,
+ 0x000024e6, 0x00000af9,
+ 0x000024e7, 0x00000afa,
+ 0x000024e8, 0x00000afb,
+ 0x000024e9, 0x00000afc,
+ 0x000024ea, 0x00000afd,
+ 0x00002a0c, 0x00000afe,
+ 0x00002a74, 0x00000b02,
+ 0x00002a75, 0x00000b05,
+ 0x00002a76, 0x00000b07,
+ 0x00002adc, 0x00000b0a,
+ 0x00002e9f, 0x00000b0c,
+ 0x00002ef3, 0x00000b0d,
+ 0x00002f00, 0x00000b0e,
+ 0x00002f01, 0x00000b0f,
+ 0x00002f02, 0x00000b10,
+ 0x00002f03, 0x00000b11,
+ 0x00002f04, 0x00000b12,
+ 0x00002f05, 0x00000b13,
+ 0x00002f06, 0x00000b14,
+ 0x00002f07, 0x00000b15,
+ 0x00002f08, 0x00000b16,
+ 0x00002f09, 0x00000b17,
+ 0x00002f0a, 0x00000b18,
+ 0x00002f0b, 0x00000b19,
+ 0x00002f0c, 0x00000b1a,
+ 0x00002f0d, 0x00000b1b,
+ 0x00002f0e, 0x00000b1c,
+ 0x00002f0f, 0x00000b1d,
+ 0x00002f10, 0x00000b1e,
+ 0x00002f11, 0x00000b1f,
+ 0x00002f12, 0x00000b20,
+ 0x00002f13, 0x00000b21,
+ 0x00002f14, 0x00000b22,
+ 0x00002f15, 0x00000b23,
+ 0x00002f16, 0x00000b24,
+ 0x00002f17, 0x00000b25,
+ 0x00002f18, 0x00000b26,
+ 0x00002f19, 0x00000b27,
+ 0x00002f1a, 0x00000b28,
+ 0x00002f1b, 0x00000b29,
+ 0x00002f1c, 0x00000b2a,
+ 0x00002f1d, 0x00000b2b,
+ 0x00002f1e, 0x00000b2c,
+ 0x00002f1f, 0x00000b2d,
+ 0x00002f20, 0x00000b2e,
+ 0x00002f21, 0x00000b2f,
+ 0x00002f22, 0x00000b30,
+ 0x00002f23, 0x00000b31,
+ 0x00002f24, 0x00000b32,
+ 0x00002f25, 0x00000b33,
+ 0x00002f26, 0x00000b34,
+ 0x00002f27, 0x00000b35,
+ 0x00002f28, 0x00000b36,
+ 0x00002f29, 0x00000b37,
+ 0x00002f2a, 0x00000b38,
+ 0x00002f2b, 0x00000b39,
+ 0x00002f2c, 0x00000b3a,
+ 0x00002f2d, 0x00000b3b,
+ 0x00002f2e, 0x00000b3c,
+ 0x00002f2f, 0x00000b3d,
+ 0x00002f30, 0x00000b3e,
+ 0x00002f31, 0x00000b3f,
+ 0x00002f32, 0x00000b40,
+ 0x00002f33, 0x00000b41,
+ 0x00002f34, 0x00000b42,
+ 0x00002f35, 0x00000b43,
+ 0x00002f36, 0x00000b44,
+ 0x00002f37, 0x00000b45,
+ 0x00002f38, 0x00000b46,
+ 0x00002f39, 0x00000b47,
+ 0x00002f3a, 0x00000b48,
+ 0x00002f3b, 0x00000b49,
+ 0x00002f3c, 0x00000b4a,
+ 0x00002f3d, 0x00000b4b,
+ 0x00002f3e, 0x00000b4c,
+ 0x00002f3f, 0x00000b4d,
+ 0x00002f40, 0x00000b4e,
+ 0x00002f41, 0x00000b4f,
+ 0x00002f42, 0x00000b50,
+ 0x00002f43, 0x00000b51,
+ 0x00002f44, 0x00000b52,
+ 0x00002f45, 0x00000b53,
+ 0x00002f46, 0x00000b54,
+ 0x00002f47, 0x00000b55,
+ 0x00002f48, 0x00000b56,
+ 0x00002f49, 0x00000b57,
+ 0x00002f4a, 0x00000b58,
+ 0x00002f4b, 0x00000b59,
+ 0x00002f4c, 0x00000b5a,
+ 0x00002f4d, 0x00000b5b,
+ 0x00002f4e, 0x00000b5c,
+ 0x00002f4f, 0x00000b5d,
+ 0x00002f50, 0x00000b5e,
+ 0x00002f51, 0x00000b5f,
+ 0x00002f52, 0x00000b60,
+ 0x00002f53, 0x00000b61,
+ 0x00002f54, 0x00000b62,
+ 0x00002f55, 0x00000b63,
+ 0x00002f56, 0x00000b64,
+ 0x00002f57, 0x00000b65,
+ 0x00002f58, 0x00000b66,
+ 0x00002f59, 0x00000b67,
+ 0x00002f5a, 0x00000b68,
+ 0x00002f5b, 0x00000b69,
+ 0x00002f5c, 0x00000b6a,
+ 0x00002f5d, 0x00000b6b,
+ 0x00002f5e, 0x00000b6c,
+ 0x00002f5f, 0x00000b6d,
+ 0x00002f60, 0x00000b6e,
+ 0x00002f61, 0x00000b6f,
+ 0x00002f62, 0x00000b70,
+ 0x00002f63, 0x00000b71,
+ 0x00002f64, 0x00000b72,
+ 0x00002f65, 0x00000b73,
+ 0x00002f66, 0x00000b74,
+ 0x00002f67, 0x00000b75,
+ 0x00002f68, 0x00000b76,
+ 0x00002f69, 0x00000b77,
+ 0x00002f6a, 0x00000b78,
+ 0x00002f6b, 0x00000b79,
+ 0x00002f6c, 0x00000b7a,
+ 0x00002f6d, 0x00000b7b,
+ 0x00002f6e, 0x00000b7c,
+ 0x00002f6f, 0x00000b7d,
+ 0x00002f70, 0x00000b7e,
+ 0x00002f71, 0x00000b7f,
+ 0x00002f72, 0x00000b80,
+ 0x00002f73, 0x00000b81,
+ 0x00002f74, 0x00000b82,
+ 0x00002f75, 0x00000b83,
+ 0x00002f76, 0x00000b84,
+ 0x00002f77, 0x00000b85,
+ 0x00002f78, 0x00000b86,
+ 0x00002f79, 0x00000b87,
+ 0x00002f7a, 0x00000b88,
+ 0x00002f7b, 0x00000b89,
+ 0x00002f7c, 0x00000b8a,
+ 0x00002f7d, 0x00000b8b,
+ 0x00002f7e, 0x00000b8c,
+ 0x00002f7f, 0x00000b8d,
+ 0x00002f80, 0x00000b8e,
+ 0x00002f81, 0x00000b8f,
+ 0x00002f82, 0x00000b90,
+ 0x00002f83, 0x00000b91,
+ 0x00002f84, 0x00000b92,
+ 0x00002f85, 0x00000b93,
+ 0x00002f86, 0x00000b94,
+ 0x00002f87, 0x00000b95,
+ 0x00002f88, 0x00000b96,
+ 0x00002f89, 0x00000b97,
+ 0x00002f8a, 0x00000b98,
+ 0x00002f8b, 0x00000b99,
+ 0x00002f8c, 0x00000b9a,
+ 0x00002f8d, 0x00000b9b,
+ 0x00002f8e, 0x00000b9c,
+ 0x00002f8f, 0x00000b9d,
+ 0x00002f90, 0x00000b9e,
+ 0x00002f91, 0x00000b9f,
+ 0x00002f92, 0x00000ba0,
+ 0x00002f93, 0x00000ba1,
+ 0x00002f94, 0x00000ba2,
+ 0x00002f95, 0x00000ba3,
+ 0x00002f96, 0x00000ba4,
+ 0x00002f97, 0x00000ba5,
+ 0x00002f98, 0x00000ba6,
+ 0x00002f99, 0x00000ba7,
+ 0x00002f9a, 0x00000ba8,
+ 0x00002f9b, 0x00000ba9,
+ 0x00002f9c, 0x00000baa,
+ 0x00002f9d, 0x00000bab,
+ 0x00002f9e, 0x00000bac,
+ 0x00002f9f, 0x00000bad,
+ 0x00002fa0, 0x00000bae,
+ 0x00002fa1, 0x00000baf,
+ 0x00002fa2, 0x00000bb0,
+ 0x00002fa3, 0x00000bb1,
+ 0x00002fa4, 0x00000bb2,
+ 0x00002fa5, 0x00000bb3,
+ 0x00002fa6, 0x00000bb4,
+ 0x00002fa7, 0x00000bb5,
+ 0x00002fa8, 0x00000bb6,
+ 0x00002fa9, 0x00000bb7,
+ 0x00002faa, 0x00000bb8,
+ 0x00002fab, 0x00000bb9,
+ 0x00002fac, 0x00000bba,
+ 0x00002fad, 0x00000bbb,
+ 0x00002fae, 0x00000bbc,
+ 0x00002faf, 0x00000bbd,
+ 0x00002fb0, 0x00000bbe,
+ 0x00002fb1, 0x00000bbf,
+ 0x00002fb2, 0x00000bc0,
+ 0x00002fb3, 0x00000bc1,
+ 0x00002fb4, 0x00000bc2,
+ 0x00002fb5, 0x00000bc3,
+ 0x00002fb6, 0x00000bc4,
+ 0x00002fb7, 0x00000bc5,
+ 0x00002fb8, 0x00000bc6,
+ 0x00002fb9, 0x00000bc7,
+ 0x00002fba, 0x00000bc8,
+ 0x00002fbb, 0x00000bc9,
+ 0x00002fbc, 0x00000bca,
+ 0x00002fbd, 0x00000bcb,
+ 0x00002fbe, 0x00000bcc,
+ 0x00002fbf, 0x00000bcd,
+ 0x00002fc0, 0x00000bce,
+ 0x00002fc1, 0x00000bcf,
+ 0x00002fc2, 0x00000bd0,
+ 0x00002fc3, 0x00000bd1,
+ 0x00002fc4, 0x00000bd2,
+ 0x00002fc5, 0x00000bd3,
+ 0x00002fc6, 0x00000bd4,
+ 0x00002fc7, 0x00000bd5,
+ 0x00002fc8, 0x00000bd6,
+ 0x00002fc9, 0x00000bd7,
+ 0x00002fca, 0x00000bd8,
+ 0x00002fcb, 0x00000bd9,
+ 0x00002fcc, 0x00000bda,
+ 0x00002fcd, 0x00000bdb,
+ 0x00002fce, 0x00000bdc,
+ 0x00002fcf, 0x00000bdd,
+ 0x00002fd0, 0x00000bde,
+ 0x00002fd1, 0x00000bdf,
+ 0x00002fd2, 0x00000be0,
+ 0x00002fd3, 0x00000be1,
+ 0x00002fd4, 0x00000be2,
+ 0x00002fd5, 0x00000be3,
+ 0x00003000, 0x00000be4,
+ 0x00003036, 0x00000be5,
+ 0x00003038, 0x00000be6,
+ 0x00003039, 0x00000be7,
+ 0x0000303a, 0x00000be8,
+ 0x0000304c, 0x00000be9,
+ 0x0000304e, 0x00000beb,
+ 0x00003050, 0x00000bed,
+ 0x00003052, 0x00000bef,
+ 0x00003054, 0x00000bf1,
+ 0x00003056, 0x00000bf3,
+ 0x00003058, 0x00000bf5,
+ 0x0000305a, 0x00000bf7,
+ 0x0000305c, 0x00000bf9,
+ 0x0000305e, 0x00000bfb,
+ 0x00003060, 0x00000bfd,
+ 0x00003062, 0x00000bff,
+ 0x00003065, 0x00000c01,
+ 0x00003067, 0x00000c03,
+ 0x00003069, 0x00000c05,
+ 0x00003070, 0x00000c07,
+ 0x00003071, 0x00000c09,
+ 0x00003073, 0x00000c0b,
+ 0x00003074, 0x00000c0d,
+ 0x00003076, 0x00000c0f,
+ 0x00003077, 0x00000c11,
+ 0x00003079, 0x00000c13,
+ 0x0000307a, 0x00000c15,
+ 0x0000307c, 0x00000c17,
+ 0x0000307d, 0x00000c19,
+ 0x00003094, 0x00000c1b,
+ 0x0000309b, 0x00000c1d,
+ 0x0000309c, 0x00000c1f,
+ 0x0000309e, 0x00000c21,
+ 0x0000309f, 0x00000c23,
+ 0x000030ac, 0x00000c25,
+ 0x000030ae, 0x00000c27,
+ 0x000030b0, 0x00000c29,
+ 0x000030b2, 0x00000c2b,
+ 0x000030b4, 0x00000c2d,
+ 0x000030b6, 0x00000c2f,
+ 0x000030b8, 0x00000c31,
+ 0x000030ba, 0x00000c33,
+ 0x000030bc, 0x00000c35,
+ 0x000030be, 0x00000c37,
+ 0x000030c0, 0x00000c39,
+ 0x000030c2, 0x00000c3b,
+ 0x000030c5, 0x00000c3d,
+ 0x000030c7, 0x00000c3f,
+ 0x000030c9, 0x00000c41,
+ 0x000030d0, 0x00000c43,
+ 0x000030d1, 0x00000c45,
+ 0x000030d3, 0x00000c47,
+ 0x000030d4, 0x00000c49,
+ 0x000030d6, 0x00000c4b,
+ 0x000030d7, 0x00000c4d,
+ 0x000030d9, 0x00000c4f,
+ 0x000030da, 0x00000c51,
+ 0x000030dc, 0x00000c53,
+ 0x000030dd, 0x00000c55,
+ 0x000030f4, 0x00000c57,
+ 0x000030f7, 0x00000c59,
+ 0x000030f8, 0x00000c5b,
+ 0x000030f9, 0x00000c5d,
+ 0x000030fa, 0x00000c5f,
+ 0x000030fe, 0x00000c61,
+ 0x000030ff, 0x00000c63,
+ 0x00003131, 0x00000c65,
+ 0x00003132, 0x00000c66,
+ 0x00003133, 0x00000c67,
+ 0x00003134, 0x00000c68,
+ 0x00003135, 0x00000c69,
+ 0x00003136, 0x00000c6a,
+ 0x00003137, 0x00000c6b,
+ 0x00003138, 0x00000c6c,
+ 0x00003139, 0x00000c6d,
+ 0x0000313a, 0x00000c6e,
+ 0x0000313b, 0x00000c6f,
+ 0x0000313c, 0x00000c70,
+ 0x0000313d, 0x00000c71,
+ 0x0000313e, 0x00000c72,
+ 0x0000313f, 0x00000c73,
+ 0x00003140, 0x00000c74,
+ 0x00003141, 0x00000c75,
+ 0x00003142, 0x00000c76,
+ 0x00003143, 0x00000c77,
+ 0x00003144, 0x00000c78,
+ 0x00003145, 0x00000c79,
+ 0x00003146, 0x00000c7a,
+ 0x00003147, 0x00000c7b,
+ 0x00003148, 0x00000c7c,
+ 0x00003149, 0x00000c7d,
+ 0x0000314a, 0x00000c7e,
+ 0x0000314b, 0x00000c7f,
+ 0x0000314c, 0x00000c80,
+ 0x0000314d, 0x00000c81,
+ 0x0000314e, 0x00000c82,
+ 0x0000314f, 0x00000c83,
+ 0x00003150, 0x00000c84,
+ 0x00003151, 0x00000c85,
+ 0x00003152, 0x00000c86,
+ 0x00003153, 0x00000c87,
+ 0x00003154, 0x00000c88,
+ 0x00003155, 0x00000c89,
+ 0x00003156, 0x00000c8a,
+ 0x00003157, 0x00000c8b,
+ 0x00003158, 0x00000c8c,
+ 0x00003159, 0x00000c8d,
+ 0x0000315a, 0x00000c8e,
+ 0x0000315b, 0x00000c8f,
+ 0x0000315c, 0x00000c90,
+ 0x0000315d, 0x00000c91,
+ 0x0000315e, 0x00000c92,
+ 0x0000315f, 0x00000c93,
+ 0x00003160, 0x00000c94,
+ 0x00003161, 0x00000c95,
+ 0x00003162, 0x00000c96,
+ 0x00003163, 0x00000c97,
+ 0x00003164, 0x00000c98,
+ 0x00003165, 0x00000c99,
+ 0x00003166, 0x00000c9a,
+ 0x00003167, 0x00000c9b,
+ 0x00003168, 0x00000c9c,
+ 0x00003169, 0x00000c9d,
+ 0x0000316a, 0x00000c9e,
+ 0x0000316b, 0x00000c9f,
+ 0x0000316c, 0x00000ca0,
+ 0x0000316d, 0x00000ca1,
+ 0x0000316e, 0x00000ca2,
+ 0x0000316f, 0x00000ca3,
+ 0x00003170, 0x00000ca4,
+ 0x00003171, 0x00000ca5,
+ 0x00003172, 0x00000ca6,
+ 0x00003173, 0x00000ca7,
+ 0x00003174, 0x00000ca8,
+ 0x00003175, 0x00000ca9,
+ 0x00003176, 0x00000caa,
+ 0x00003177, 0x00000cab,
+ 0x00003178, 0x00000cac,
+ 0x00003179, 0x00000cad,
+ 0x0000317a, 0x00000cae,
+ 0x0000317b, 0x00000caf,
+ 0x0000317c, 0x00000cb0,
+ 0x0000317d, 0x00000cb1,
+ 0x0000317e, 0x00000cb2,
+ 0x0000317f, 0x00000cb3,
+ 0x00003180, 0x00000cb4,
+ 0x00003181, 0x00000cb5,
+ 0x00003182, 0x00000cb6,
+ 0x00003183, 0x00000cb7,
+ 0x00003184, 0x00000cb8,
+ 0x00003185, 0x00000cb9,
+ 0x00003186, 0x00000cba,
+ 0x00003187, 0x00000cbb,
+ 0x00003188, 0x00000cbc,
+ 0x00003189, 0x00000cbd,
+ 0x0000318a, 0x00000cbe,
+ 0x0000318b, 0x00000cbf,
+ 0x0000318c, 0x00000cc0,
+ 0x0000318d, 0x00000cc1,
+ 0x0000318e, 0x00000cc2,
+ 0x00003192, 0x00000cc3,
+ 0x00003193, 0x00000cc4,
+ 0x00003194, 0x00000cc5,
+ 0x00003195, 0x00000cc6,
+ 0x00003196, 0x00000cc7,
+ 0x00003197, 0x00000cc8,
+ 0x00003198, 0x00000cc9,
+ 0x00003199, 0x00000cca,
+ 0x0000319a, 0x00000ccb,
+ 0x0000319b, 0x00000ccc,
+ 0x0000319c, 0x00000ccd,
+ 0x0000319d, 0x00000cce,
+ 0x0000319e, 0x00000ccf,
+ 0x0000319f, 0x00000cd0,
+ 0x00003200, 0x00000cd1,
+ 0x00003201, 0x00000cd4,
+ 0x00003202, 0x00000cd7,
+ 0x00003203, 0x00000cda,
+ 0x00003204, 0x00000cdd,
+ 0x00003205, 0x00000ce0,
+ 0x00003206, 0x00000ce3,
+ 0x00003207, 0x00000ce6,
+ 0x00003208, 0x00000ce9,
+ 0x00003209, 0x00000cec,
+ 0x0000320a, 0x00000cef,
+ 0x0000320b, 0x00000cf2,
+ 0x0000320c, 0x00000cf5,
+ 0x0000320d, 0x00000cf8,
+ 0x0000320e, 0x00000cfb,
+ 0x0000320f, 0x00000cff,
+ 0x00003210, 0x00000d03,
+ 0x00003211, 0x00000d07,
+ 0x00003212, 0x00000d0b,
+ 0x00003213, 0x00000d0f,
+ 0x00003214, 0x00000d13,
+ 0x00003215, 0x00000d17,
+ 0x00003216, 0x00000d1b,
+ 0x00003217, 0x00000d1f,
+ 0x00003218, 0x00000d23,
+ 0x00003219, 0x00000d27,
+ 0x0000321a, 0x00000d2b,
+ 0x0000321b, 0x00000d2f,
+ 0x0000321c, 0x00000d33,
+ 0x00003220, 0x00000d37,
+ 0x00003221, 0x00000d3a,
+ 0x00003222, 0x00000d3d,
+ 0x00003223, 0x00000d40,
+ 0x00003224, 0x00000d43,
+ 0x00003225, 0x00000d46,
+ 0x00003226, 0x00000d49,
+ 0x00003227, 0x00000d4c,
+ 0x00003228, 0x00000d4f,
+ 0x00003229, 0x00000d52,
+ 0x0000322a, 0x00000d55,
+ 0x0000322b, 0x00000d58,
+ 0x0000322c, 0x00000d5b,
+ 0x0000322d, 0x00000d5e,
+ 0x0000322e, 0x00000d61,
+ 0x0000322f, 0x00000d64,
+ 0x00003230, 0x00000d67,
+ 0x00003231, 0x00000d6a,
+ 0x00003232, 0x00000d6d,
+ 0x00003233, 0x00000d70,
+ 0x00003234, 0x00000d73,
+ 0x00003235, 0x00000d76,
+ 0x00003236, 0x00000d79,
+ 0x00003237, 0x00000d7c,
+ 0x00003238, 0x00000d7f,
+ 0x00003239, 0x00000d82,
+ 0x0000323a, 0x00000d85,
+ 0x0000323b, 0x00000d88,
+ 0x0000323c, 0x00000d8b,
+ 0x0000323d, 0x00000d8e,
+ 0x0000323e, 0x00000d91,
+ 0x0000323f, 0x00000d94,
+ 0x00003240, 0x00000d97,
+ 0x00003241, 0x00000d9a,
+ 0x00003242, 0x00000d9d,
+ 0x00003243, 0x00000da0,
+ 0x00003251, 0x00000da3,
+ 0x00003252, 0x00000da5,
+ 0x00003253, 0x00000da7,
+ 0x00003254, 0x00000da9,
+ 0x00003255, 0x00000dab,
+ 0x00003256, 0x00000dad,
+ 0x00003257, 0x00000daf,
+ 0x00003258, 0x00000db1,
+ 0x00003259, 0x00000db3,
+ 0x0000325a, 0x00000db5,
+ 0x0000325b, 0x00000db7,
+ 0x0000325c, 0x00000db9,
+ 0x0000325d, 0x00000dbb,
+ 0x0000325e, 0x00000dbd,
+ 0x0000325f, 0x00000dbf,
+ 0x00003260, 0x00000dc1,
+ 0x00003261, 0x00000dc2,
+ 0x00003262, 0x00000dc3,
+ 0x00003263, 0x00000dc4,
+ 0x00003264, 0x00000dc5,
+ 0x00003265, 0x00000dc6,
+ 0x00003266, 0x00000dc7,
+ 0x00003267, 0x00000dc8,
+ 0x00003268, 0x00000dc9,
+ 0x00003269, 0x00000dca,
+ 0x0000326a, 0x00000dcb,
+ 0x0000326b, 0x00000dcc,
+ 0x0000326c, 0x00000dcd,
+ 0x0000326d, 0x00000dce,
+ 0x0000326e, 0x00000dcf,
+ 0x0000326f, 0x00000dd1,
+ 0x00003270, 0x00000dd3,
+ 0x00003271, 0x00000dd5,
+ 0x00003272, 0x00000dd7,
+ 0x00003273, 0x00000dd9,
+ 0x00003274, 0x00000ddb,
+ 0x00003275, 0x00000ddd,
+ 0x00003276, 0x00000ddf,
+ 0x00003277, 0x00000de1,
+ 0x00003278, 0x00000de3,
+ 0x00003279, 0x00000de5,
+ 0x0000327a, 0x00000de7,
+ 0x0000327b, 0x00000de9,
+ 0x00003280, 0x00000deb,
+ 0x00003281, 0x00000dec,
+ 0x00003282, 0x00000ded,
+ 0x00003283, 0x00000dee,
+ 0x00003284, 0x00000def,
+ 0x00003285, 0x00000df0,
+ 0x00003286, 0x00000df1,
+ 0x00003287, 0x00000df2,
+ 0x00003288, 0x00000df3,
+ 0x00003289, 0x00000df4,
+ 0x0000328a, 0x00000df5,
+ 0x0000328b, 0x00000df6,
+ 0x0000328c, 0x00000df7,
+ 0x0000328d, 0x00000df8,
+ 0x0000328e, 0x00000df9,
+ 0x0000328f, 0x00000dfa,
+ 0x00003290, 0x00000dfb,
+ 0x00003291, 0x00000dfc,
+ 0x00003292, 0x00000dfd,
+ 0x00003293, 0x00000dfe,
+ 0x00003294, 0x00000dff,
+ 0x00003295, 0x00000e00,
+ 0x00003296, 0x00000e01,
+ 0x00003297, 0x00000e02,
+ 0x00003298, 0x00000e03,
+ 0x00003299, 0x00000e04,
+ 0x0000329a, 0x00000e05,
+ 0x0000329b, 0x00000e06,
+ 0x0000329c, 0x00000e07,
+ 0x0000329d, 0x00000e08,
+ 0x0000329e, 0x00000e09,
+ 0x0000329f, 0x00000e0a,
+ 0x000032a0, 0x00000e0b,
+ 0x000032a1, 0x00000e0c,
+ 0x000032a2, 0x00000e0d,
+ 0x000032a3, 0x00000e0e,
+ 0x000032a4, 0x00000e0f,
+ 0x000032a5, 0x00000e10,
+ 0x000032a6, 0x00000e11,
+ 0x000032a7, 0x00000e12,
+ 0x000032a8, 0x00000e13,
+ 0x000032a9, 0x00000e14,
+ 0x000032aa, 0x00000e15,
+ 0x000032ab, 0x00000e16,
+ 0x000032ac, 0x00000e17,
+ 0x000032ad, 0x00000e18,
+ 0x000032ae, 0x00000e19,
+ 0x000032af, 0x00000e1a,
+ 0x000032b0, 0x00000e1b,
+ 0x000032b1, 0x00000e1c,
+ 0x000032b2, 0x00000e1e,
+ 0x000032b3, 0x00000e20,
+ 0x000032b4, 0x00000e22,
+ 0x000032b5, 0x00000e24,
+ 0x000032b6, 0x00000e26,
+ 0x000032b7, 0x00000e28,
+ 0x000032b8, 0x00000e2a,
+ 0x000032b9, 0x00000e2c,
+ 0x000032ba, 0x00000e2e,
+ 0x000032bb, 0x00000e30,
+ 0x000032bc, 0x00000e32,
+ 0x000032bd, 0x00000e34,
+ 0x000032be, 0x00000e36,
+ 0x000032bf, 0x00000e38,
+ 0x000032c0, 0x00000e3a,
+ 0x000032c1, 0x00000e3c,
+ 0x000032c2, 0x00000e3e,
+ 0x000032c3, 0x00000e40,
+ 0x000032c4, 0x00000e42,
+ 0x000032c5, 0x00000e44,
+ 0x000032c6, 0x00000e46,
+ 0x000032c7, 0x00000e48,
+ 0x000032c8, 0x00000e4a,
+ 0x000032c9, 0x00000e4c,
+ 0x000032ca, 0x00000e4f,
+ 0x000032cb, 0x00000e52,
+ 0x000032d0, 0x00000e55,
+ 0x000032d1, 0x00000e56,
+ 0x000032d2, 0x00000e57,
+ 0x000032d3, 0x00000e58,
+ 0x000032d4, 0x00000e59,
+ 0x000032d5, 0x00000e5a,
+ 0x000032d6, 0x00000e5b,
+ 0x000032d7, 0x00000e5c,
+ 0x000032d8, 0x00000e5d,
+ 0x000032d9, 0x00000e5e,
+ 0x000032da, 0x00000e5f,
+ 0x000032db, 0x00000e60,
+ 0x000032dc, 0x00000e61,
+ 0x000032dd, 0x00000e62,
+ 0x000032de, 0x00000e63,
+ 0x000032df, 0x00000e64,
+ 0x000032e0, 0x00000e65,
+ 0x000032e1, 0x00000e66,
+ 0x000032e2, 0x00000e67,
+ 0x000032e3, 0x00000e68,
+ 0x000032e4, 0x00000e69,
+ 0x000032e5, 0x00000e6a,
+ 0x000032e6, 0x00000e6b,
+ 0x000032e7, 0x00000e6c,
+ 0x000032e8, 0x00000e6d,
+ 0x000032e9, 0x00000e6e,
+ 0x000032ea, 0x00000e6f,
+ 0x000032eb, 0x00000e70,
+ 0x000032ec, 0x00000e71,
+ 0x000032ed, 0x00000e72,
+ 0x000032ee, 0x00000e73,
+ 0x000032ef, 0x00000e74,
+ 0x000032f0, 0x00000e75,
+ 0x000032f1, 0x00000e76,
+ 0x000032f2, 0x00000e77,
+ 0x000032f3, 0x00000e78,
+ 0x000032f4, 0x00000e79,
+ 0x000032f5, 0x00000e7a,
+ 0x000032f6, 0x00000e7b,
+ 0x000032f7, 0x00000e7c,
+ 0x000032f8, 0x00000e7d,
+ 0x000032f9, 0x00000e7e,
+ 0x000032fa, 0x00000e7f,
+ 0x000032fb, 0x00000e80,
+ 0x000032fc, 0x00000e81,
+ 0x000032fd, 0x00000e82,
+ 0x000032fe, 0x00000e83,
+ 0x00003300, 0x00000e84,
+ 0x00003301, 0x00000e89,
+ 0x00003302, 0x00000e8d,
+ 0x00003303, 0x00000e92,
+ 0x00003304, 0x00000e95,
+ 0x00003305, 0x00000e9a,
+ 0x00003306, 0x00000e9d,
+ 0x00003307, 0x00000ea0,
+ 0x00003308, 0x00000ea6,
+ 0x00003309, 0x00000eaa,
+ 0x0000330a, 0x00000ead,
+ 0x0000330b, 0x00000eb0,
+ 0x0000330c, 0x00000eb3,
+ 0x0000330d, 0x00000eb7,
+ 0x0000330e, 0x00000ebb,
+ 0x0000330f, 0x00000ebf,
+ 0x00003310, 0x00000ec3,
+ 0x00003311, 0x00000ec7,
+ 0x00003312, 0x00000ecb,
+ 0x00003313, 0x00000ecf,
+ 0x00003314, 0x00000ed5,
+ 0x00003315, 0x00000ed7,
+ 0x00003316, 0x00000edd,
+ 0x00003317, 0x00000ee3,
+ 0x00003318, 0x00000ee8,
+ 0x00003319, 0x00000eec,
+ 0x0000331a, 0x00000ef2,
+ 0x0000331b, 0x00000ef8,
+ 0x0000331c, 0x00000efc,
+ 0x0000331d, 0x00000eff,
+ 0x0000331e, 0x00000f02,
+ 0x0000331f, 0x00000f06,
+ 0x00003320, 0x00000f0a,
+ 0x00003321, 0x00000f0f,
+ 0x00003322, 0x00000f14,
+ 0x00003323, 0x00000f17,
+ 0x00003324, 0x00000f1a,
+ 0x00003325, 0x00000f1e,
+ 0x00003326, 0x00000f21,
+ 0x00003327, 0x00000f24,
+ 0x00003328, 0x00000f26,
+ 0x00003329, 0x00000f28,
+ 0x0000332a, 0x00000f2b,
+ 0x0000332b, 0x00000f2e,
+ 0x0000332c, 0x00000f34,
+ 0x0000332d, 0x00000f38,
+ 0x0000332e, 0x00000f3d,
+ 0x0000332f, 0x00000f43,
+ 0x00003330, 0x00000f47,
+ 0x00003331, 0x00000f4a,
+ 0x00003332, 0x00000f4d,
+ 0x00003333, 0x00000f53,
+ 0x00003334, 0x00000f57,
+ 0x00003335, 0x00000f5d,
+ 0x00003336, 0x00000f60,
+ 0x00003337, 0x00000f65,
+ 0x00003338, 0x00000f68,
+ 0x00003339, 0x00000f6c,
+ 0x0000333a, 0x00000f6f,
+ 0x0000333b, 0x00000f73,
+ 0x0000333c, 0x00000f78,
+ 0x0000333d, 0x00000f7c,
+ 0x0000333e, 0x00000f81,
+ 0x0000333f, 0x00000f85,
+ 0x00003340, 0x00000f87,
+ 0x00003341, 0x00000f8c,
+ 0x00003342, 0x00000f8f,
+ 0x00003343, 0x00000f92,
+ 0x00003344, 0x00000f96,
+ 0x00003345, 0x00000f99,
+ 0x00003346, 0x00000f9c,
+ 0x00003347, 0x00000f9f,
+ 0x00003348, 0x00000fa4,
+ 0x00003349, 0x00000fa8,
+ 0x0000334a, 0x00000faa,
+ 0x0000334b, 0x00000fb0,
+ 0x0000334c, 0x00000fb3,
+ 0x0000334d, 0x00000fb8,
+ 0x0000334e, 0x00000fbc,
+ 0x0000334f, 0x00000fc0,
+ 0x00003350, 0x00000fc3,
+ 0x00003351, 0x00000fc6,
+ 0x00003352, 0x00000fca,
+ 0x00003353, 0x00000fcc,
+ 0x00003354, 0x00000fd0,
+ 0x00003355, 0x00000fd5,
+ 0x00003356, 0x00000fd7,
+ 0x00003357, 0x00000fdd,
+ 0x00003358, 0x00000fe0,
+ 0x00003359, 0x00000fe2,
+ 0x0000335a, 0x00000fe4,
+ 0x0000335b, 0x00000fe6,
+ 0x0000335c, 0x00000fe8,
+ 0x0000335d, 0x00000fea,
+ 0x0000335e, 0x00000fec,
+ 0x0000335f, 0x00000fee,
+ 0x00003360, 0x00000ff0,
+ 0x00003361, 0x00000ff2,
+ 0x00003362, 0x00000ff4,
+ 0x00003363, 0x00000ff7,
+ 0x00003364, 0x00000ffa,
+ 0x00003365, 0x00000ffd,
+ 0x00003366, 0x00001000,
+ 0x00003367, 0x00001003,
+ 0x00003368, 0x00001006,
+ 0x00003369, 0x00001009,
+ 0x0000336a, 0x0000100c,
+ 0x0000336b, 0x0000100f,
+ 0x0000336c, 0x00001012,
+ 0x0000336d, 0x00001015,
+ 0x0000336e, 0x00001018,
+ 0x0000336f, 0x0000101b,
+ 0x00003370, 0x0000101e,
+ 0x00003371, 0x00001021,
+ 0x00003372, 0x00001024,
+ 0x00003373, 0x00001026,
+ 0x00003374, 0x00001028,
+ 0x00003375, 0x0000102b,
+ 0x00003376, 0x0000102d,
+ 0x0000337b, 0x0000102f,
+ 0x0000337c, 0x00001031,
+ 0x0000337d, 0x00001033,
+ 0x0000337e, 0x00001035,
+ 0x0000337f, 0x00001037,
+ 0x00003380, 0x0000103b,
+ 0x00003381, 0x0000103d,
+ 0x00003382, 0x0000103f,
+ 0x00003383, 0x00001041,
+ 0x00003384, 0x00001043,
+ 0x00003385, 0x00001045,
+ 0x00003386, 0x00001047,
+ 0x00003387, 0x00001049,
+ 0x00003388, 0x0000104b,
+ 0x00003389, 0x0000104e,
+ 0x0000338a, 0x00001052,
+ 0x0000338b, 0x00001054,
+ 0x0000338c, 0x00001056,
+ 0x0000338d, 0x00001058,
+ 0x0000338e, 0x0000105a,
+ 0x0000338f, 0x0000105c,
+ 0x00003390, 0x0000105e,
+ 0x00003391, 0x00001060,
+ 0x00003392, 0x00001063,
+ 0x00003393, 0x00001066,
+ 0x00003394, 0x00001069,
+ 0x00003395, 0x0000106c,
+ 0x00003396, 0x0000106e,
+ 0x00003397, 0x00001070,
+ 0x00003398, 0x00001072,
+ 0x00003399, 0x00001074,
+ 0x0000339a, 0x00001076,
+ 0x0000339b, 0x00001078,
+ 0x0000339c, 0x0000107a,
+ 0x0000339d, 0x0000107c,
+ 0x0000339e, 0x0000107e,
+ 0x0000339f, 0x00001080,
+ 0x000033a0, 0x00001083,
+ 0x000033a1, 0x00001086,
+ 0x000033a2, 0x00001088,
+ 0x000033a3, 0x0000108b,
+ 0x000033a4, 0x0000108e,
+ 0x000033a5, 0x00001091,
+ 0x000033a6, 0x00001093,
+ 0x000033a7, 0x00001096,
+ 0x000033a8, 0x00001099,
+ 0x000033a9, 0x0000109d,
+ 0x000033aa, 0x0000109f,
+ 0x000033ab, 0x000010a2,
+ 0x000033ac, 0x000010a5,
+ 0x000033ad, 0x000010a8,
+ 0x000033ae, 0x000010ab,
+ 0x000033af, 0x000010b0,
+ 0x000033b0, 0x000010b6,
+ 0x000033b1, 0x000010b8,
+ 0x000033b2, 0x000010ba,
+ 0x000033b3, 0x000010bc,
+ 0x000033b4, 0x000010be,
+ 0x000033b5, 0x000010c0,
+ 0x000033b6, 0x000010c2,
+ 0x000033b7, 0x000010c4,
+ 0x000033b8, 0x000010c6,
+ 0x000033b9, 0x000010c8,
+ 0x000033ba, 0x000010ca,
+ 0x000033bb, 0x000010cc,
+ 0x000033bc, 0x000010ce,
+ 0x000033bd, 0x000010d0,
+ 0x000033be, 0x000010d2,
+ 0x000033bf, 0x000010d4,
+ 0x000033c0, 0x000010d6,
+ 0x000033c1, 0x000010d8,
+ 0x000033c2, 0x000010da,
+ 0x000033c3, 0x000010de,
+ 0x000033c4, 0x000010e0,
+ 0x000033c5, 0x000010e2,
+ 0x000033c6, 0x000010e4,
+ 0x000033c7, 0x000010e8,
+ 0x000033c8, 0x000010eb,
+ 0x000033c9, 0x000010ed,
+ 0x000033ca, 0x000010ef,
+ 0x000033cb, 0x000010f1,
+ 0x000033cc, 0x000010f3,
+ 0x000033cd, 0x000010f5,
+ 0x000033ce, 0x000010f7,
+ 0x000033cf, 0x000010f9,
+ 0x000033d0, 0x000010fb,
+ 0x000033d1, 0x000010fd,
+ 0x000033d2, 0x000010ff,
+ 0x000033d3, 0x00001102,
+ 0x000033d4, 0x00001104,
+ 0x000033d5, 0x00001106,
+ 0x000033d6, 0x00001109,
+ 0x000033d7, 0x0000110c,
+ 0x000033d8, 0x0000110e,
+ 0x000033d9, 0x00001112,
+ 0x000033da, 0x00001115,
+ 0x000033db, 0x00001117,
+ 0x000033dc, 0x00001119,
+ 0x000033dd, 0x0000111b,
+ 0x000033e0, 0x0000111d,
+ 0x000033e1, 0x0000111f,
+ 0x000033e2, 0x00001121,
+ 0x000033e3, 0x00001123,
+ 0x000033e4, 0x00001125,
+ 0x000033e5, 0x00001127,
+ 0x000033e6, 0x00001129,
+ 0x000033e7, 0x0000112b,
+ 0x000033e8, 0x0000112d,
+ 0x000033e9, 0x0000112f,
+ 0x000033ea, 0x00001132,
+ 0x000033eb, 0x00001135,
+ 0x000033ec, 0x00001138,
+ 0x000033ed, 0x0000113b,
+ 0x000033ee, 0x0000113e,
+ 0x000033ef, 0x00001141,
+ 0x000033f0, 0x00001144,
+ 0x000033f1, 0x00001147,
+ 0x000033f2, 0x0000114a,
+ 0x000033f3, 0x0000114d,
+ 0x000033f4, 0x00001150,
+ 0x000033f5, 0x00001153,
+ 0x000033f6, 0x00001156,
+ 0x000033f7, 0x00001159,
+ 0x000033f8, 0x0000115c,
+ 0x000033f9, 0x0000115f,
+ 0x000033fa, 0x00001162,
+ 0x000033fb, 0x00001165,
+ 0x000033fc, 0x00001168,
+ 0x000033fd, 0x0000116b,
+ 0x000033fe, 0x0000116e,
+ 0x0000f902, 0x00001171,
+ 0x0000f903, 0x00001172,
+ 0x0000f904, 0x00001173,
+ 0x0000f905, 0x00001174,
+ 0x0000f906, 0x00001175,
+ 0x0000f907, 0x00001176,
+ 0x0000f908, 0x00001177,
+ 0x0000f909, 0x00001178,
+ 0x0000f90a, 0x00001179,
+ 0x0000f90b, 0x0000117a,
+ 0x0000f90c, 0x0000117b,
+ 0x0000f90d, 0x0000117c,
+ 0x0000f90e, 0x0000117d,
+ 0x0000f90f, 0x0000117e,
+ 0x0000f910, 0x0000117f,
+ 0x0000f911, 0x00001180,
+ 0x0000f912, 0x00001181,
+ 0x0000f913, 0x00001182,
+ 0x0000f914, 0x00001183,
+ 0x0000f915, 0x00001184,
+ 0x0000f916, 0x00001185,
+ 0x0000f917, 0x00001186,
+ 0x0000f918, 0x00001187,
+ 0x0000f919, 0x00001188,
+ 0x0000f91a, 0x00001189,
+ 0x0000f91b, 0x0000118a,
+ 0x0000f91c, 0x0000118b,
+ 0x0000f91d, 0x0000118c,
+ 0x0000f91e, 0x0000118d,
+ 0x0000f91f, 0x0000118e,
+ 0x0000f920, 0x0000118f,
+ 0x0000f921, 0x00001190,
+ 0x0000f922, 0x00001191,
+ 0x0000f923, 0x00001192,
+ 0x0000f924, 0x00001193,
+ 0x0000f925, 0x00001194,
+ 0x0000f926, 0x00001195,
+ 0x0000f927, 0x00001196,
+ 0x0000f928, 0x00001197,
+ 0x0000f929, 0x00001198,
+ 0x0000f92a, 0x00001199,
+ 0x0000f92b, 0x0000119a,
+ 0x0000f92c, 0x0000119b,
+ 0x0000f92d, 0x0000119c,
+ 0x0000f92e, 0x0000119d,
+ 0x0000f92f, 0x0000119e,
+ 0x0000f930, 0x0000119f,
+ 0x0000f931, 0x000011a0,
+ 0x0000f932, 0x000011a1,
+ 0x0000f933, 0x000011a2,
+ 0x0000f934, 0x000011a3,
+ 0x0000f935, 0x000011a4,
+ 0x0000f936, 0x000011a5,
+ 0x0000f937, 0x000011a6,
+ 0x0000f938, 0x000011a7,
+ 0x0000f939, 0x000011a8,
+ 0x0000f93a, 0x000011a9,
+ 0x0000f93b, 0x000011aa,
+ 0x0000f93c, 0x000011ab,
+ 0x0000f93d, 0x000011ac,
+ 0x0000f93e, 0x000011ad,
+ 0x0000f93f, 0x000011ae,
+ 0x0000f940, 0x000011af,
+ 0x0000f941, 0x000011b0,
+ 0x0000f942, 0x000011b1,
+ 0x0000f943, 0x000011b2,
+ 0x0000f944, 0x000011b3,
+ 0x0000f945, 0x000011b4,
+ 0x0000f946, 0x000011b5,
+ 0x0000f947, 0x000011b6,
+ 0x0000f948, 0x000011b7,
+ 0x0000f949, 0x000011b8,
+ 0x0000f94a, 0x000011b9,
+ 0x0000f94b, 0x000011ba,
+ 0x0000f94c, 0x000011bb,
+ 0x0000f94d, 0x000011bc,
+ 0x0000f94e, 0x000011bd,
+ 0x0000f94f, 0x000011be,
+ 0x0000f950, 0x000011bf,
+ 0x0000f951, 0x000011c0,
+ 0x0000f952, 0x000011c1,
+ 0x0000f953, 0x000011c2,
+ 0x0000f954, 0x000011c3,
+ 0x0000f955, 0x000011c4,
+ 0x0000f956, 0x000011c5,
+ 0x0000f957, 0x000011c6,
+ 0x0000f958, 0x000011c7,
+ 0x0000f959, 0x000011c8,
+ 0x0000f95a, 0x000011c9,
+ 0x0000f95b, 0x000011ca,
+ 0x0000f95c, 0x000011cb,
+ 0x0000f95d, 0x000011cc,
+ 0x0000f95e, 0x000011cd,
+ 0x0000f95f, 0x000011ce,
+ 0x0000f960, 0x000011cf,
+ 0x0000f961, 0x000011d0,
+ 0x0000f962, 0x000011d1,
+ 0x0000f963, 0x000011d2,
+ 0x0000f964, 0x000011d3,
+ 0x0000f965, 0x000011d4,
+ 0x0000f966, 0x000011d5,
+ 0x0000f967, 0x000011d6,
+ 0x0000f968, 0x000011d7,
+ 0x0000f969, 0x000011d8,
+ 0x0000f96a, 0x000011d9,
+ 0x0000f96b, 0x000011da,
+ 0x0000f96c, 0x000011db,
+ 0x0000f96d, 0x000011dc,
+ 0x0000f96e, 0x000011dd,
+ 0x0000f96f, 0x000011de,
+ 0x0000f970, 0x000011df,
+ 0x0000f971, 0x000011e0,
+ 0x0000f972, 0x000011e1,
+ 0x0000f973, 0x000011e2,
+ 0x0000f974, 0x000011e3,
+ 0x0000f975, 0x000011e4,
+ 0x0000f976, 0x000011e5,
+ 0x0000f977, 0x000011e6,
+ 0x0000f978, 0x000011e7,
+ 0x0000f979, 0x000011e8,
+ 0x0000f97a, 0x000011e9,
+ 0x0000f97b, 0x000011ea,
+ 0x0000f97c, 0x000011eb,
+ 0x0000f97d, 0x000011ec,
+ 0x0000f97e, 0x000011ed,
+ 0x0000f97f, 0x000011ee,
+ 0x0000f980, 0x000011ef,
+ 0x0000f981, 0x000011f0,
+ 0x0000f982, 0x000011f1,
+ 0x0000f983, 0x000011f2,
+ 0x0000f984, 0x000011f3,
+ 0x0000f985, 0x000011f4,
+ 0x0000f986, 0x000011f5,
+ 0x0000f987, 0x000011f6,
+ 0x0000f988, 0x000011f7,
+ 0x0000f989, 0x000011f8,
+ 0x0000f98a, 0x000011f9,
+ 0x0000f98b, 0x000011fa,
+ 0x0000f98c, 0x000011fb,
+ 0x0000f98d, 0x000011fc,
+ 0x0000f98e, 0x000011fd,
+ 0x0000f98f, 0x000011fe,
+ 0x0000f990, 0x000011ff,
+ 0x0000f991, 0x00001200,
+ 0x0000f992, 0x00001201,
+ 0x0000f993, 0x00001202,
+ 0x0000f994, 0x00001203,
+ 0x0000f995, 0x00001204,
+ 0x0000f996, 0x00001205,
+ 0x0000f997, 0x00001206,
+ 0x0000f998, 0x00001207,
+ 0x0000f999, 0x00001208,
+ 0x0000f99a, 0x00001209,
+ 0x0000f99b, 0x0000120a,
+ 0x0000f99c, 0x0000120b,
+ 0x0000f99d, 0x0000120c,
+ 0x0000f99e, 0x0000120d,
+ 0x0000f99f, 0x0000120e,
+ 0x0000f9a0, 0x0000120f,
+ 0x0000f9a1, 0x00001210,
+ 0x0000f9a2, 0x00001211,
+ 0x0000f9a3, 0x00001212,
+ 0x0000f9a4, 0x00001213,
+ 0x0000f9a5, 0x00001214,
+ 0x0000f9a6, 0x00001215,
+ 0x0000f9a7, 0x00001216,
+ 0x0000f9a8, 0x00001217,
+ 0x0000f9a9, 0x00001218,
+ 0x0000f9aa, 0x00001219,
+ 0x0000f9ab, 0x0000121a,
+ 0x0000f9ac, 0x0000121b,
+ 0x0000f9ad, 0x0000121c,
+ 0x0000f9ae, 0x0000121d,
+ 0x0000f9af, 0x0000121e,
+ 0x0000f9b0, 0x0000121f,
+ 0x0000f9b1, 0x00001220,
+ 0x0000f9b2, 0x00001221,
+ 0x0000f9b3, 0x00001222,
+ 0x0000f9b4, 0x00001223,
+ 0x0000f9b5, 0x00001224,
+ 0x0000f9b6, 0x00001225,
+ 0x0000f9b7, 0x00001226,
+ 0x0000f9b8, 0x00001227,
+ 0x0000f9b9, 0x00001228,
+ 0x0000f9ba, 0x00001229,
+ 0x0000f9bb, 0x0000122a,
+ 0x0000f9bc, 0x0000122b,
+ 0x0000f9bd, 0x0000122c,
+ 0x0000f9be, 0x0000122d,
+ 0x0000f9bf, 0x0000122e,
+ 0x0000f9c0, 0x0000122f,
+ 0x0000f9c1, 0x00001230,
+ 0x0000f9c2, 0x00001231,
+ 0x0000f9c3, 0x00001232,
+ 0x0000f9c4, 0x00001233,
+ 0x0000f9c5, 0x00001234,
+ 0x0000f9c6, 0x00001235,
+ 0x0000f9c7, 0x00001236,
+ 0x0000f9c8, 0x00001237,
+ 0x0000f9c9, 0x00001238,
+ 0x0000f9ca, 0x00001239,
+ 0x0000f9cb, 0x0000123a,
+ 0x0000f9cc, 0x0000123b,
+ 0x0000f9cd, 0x0000123c,
+ 0x0000f9ce, 0x0000123d,
+ 0x0000f9cf, 0x0000123e,
+ 0x0000f9d0, 0x0000123f,
+ 0x0000f9d1, 0x00001240,
+ 0x0000f9d2, 0x00001241,
+ 0x0000f9d3, 0x00001242,
+ 0x0000f9d4, 0x00001243,
+ 0x0000f9d5, 0x00001244,
+ 0x0000f9d6, 0x00001245,
+ 0x0000f9d7, 0x00001246,
+ 0x0000f9d8, 0x00001247,
+ 0x0000f9d9, 0x00001248,
+ 0x0000f9da, 0x00001249,
+ 0x0000f9db, 0x0000124a,
+ 0x0000f9dc, 0x0000124b,
+ 0x0000f9dd, 0x0000124c,
+ 0x0000f9de, 0x0000124d,
+ 0x0000f9df, 0x0000124e,
+ 0x0000f9e0, 0x0000124f,
+ 0x0000f9e1, 0x00001250,
+ 0x0000f9e2, 0x00001251,
+ 0x0000f9e3, 0x00001252,
+ 0x0000f9e4, 0x00001253,
+ 0x0000f9e5, 0x00001254,
+ 0x0000f9e6, 0x00001255,
+ 0x0000f9e7, 0x00001256,
+ 0x0000f9e8, 0x00001257,
+ 0x0000f9e9, 0x00001258,
+ 0x0000f9ea, 0x00001259,
+ 0x0000f9eb, 0x0000125a,
+ 0x0000f9ec, 0x0000125b,
+ 0x0000f9ed, 0x0000125c,
+ 0x0000f9ee, 0x0000125d,
+ 0x0000f9ef, 0x0000125e,
+ 0x0000f9f0, 0x0000125f,
+ 0x0000f9f1, 0x00001260,
+ 0x0000f9f2, 0x00001261,
+ 0x0000f9f3, 0x00001262,
+ 0x0000f9f4, 0x00001263,
+ 0x0000f9f5, 0x00001264,
+ 0x0000f9f6, 0x00001265,
+ 0x0000f9f7, 0x00001266,
+ 0x0000f9f8, 0x00001267,
+ 0x0000f9f9, 0x00001268,
+ 0x0000f9fa, 0x00001269,
+ 0x0000f9fb, 0x0000126a,
+ 0x0000f9fc, 0x0000126b,
+ 0x0000f9fd, 0x0000126c,
+ 0x0000f9fe, 0x0000126d,
+ 0x0000f9ff, 0x0000126e,
+ 0x0000fa00, 0x0000126f,
+ 0x0000fa01, 0x00001270,
+ 0x0000fa02, 0x00001271,
+ 0x0000fa03, 0x00001272,
+ 0x0000fa04, 0x00001273,
+ 0x0000fa05, 0x00001274,
+ 0x0000fa06, 0x00001275,
+ 0x0000fa07, 0x00001276,
+ 0x0000fa08, 0x00001277,
+ 0x0000fa09, 0x00001278,
+ 0x0000fa0a, 0x00001279,
+ 0x0000fa0b, 0x0000127a,
+ 0x0000fa0c, 0x0000127b,
+ 0x0000fa0d, 0x0000127c,
+ 0x0000fa10, 0x0000127d,
+ 0x0000fa12, 0x0000127e,
+ 0x0000fa15, 0x0000127f,
+ 0x0000fa16, 0x00001280,
+ 0x0000fa17, 0x00001281,
+ 0x0000fa18, 0x00001282,
+ 0x0000fa19, 0x00001283,
+ 0x0000fa1a, 0x00001284,
+ 0x0000fa1b, 0x00001285,
+ 0x0000fa1c, 0x00001286,
+ 0x0000fa1d, 0x00001287,
+ 0x0000fa1e, 0x00001288,
+ 0x0000fa20, 0x00001289,
+ 0x0000fa22, 0x0000128a,
+ 0x0000fa25, 0x0000128b,
+ 0x0000fa26, 0x0000128c,
+ 0x0000fa2a, 0x0000128d,
+ 0x0000fa2b, 0x0000128e,
+ 0x0000fa2c, 0x0000128f,
+ 0x0000fa2d, 0x00001290,
+ 0x0000fa30, 0x00001291,
+ 0x0000fa31, 0x00001292,
+ 0x0000fa32, 0x00001293,
+ 0x0000fa33, 0x00001294,
+ 0x0000fa34, 0x00001295,
+ 0x0000fa35, 0x00001296,
+ 0x0000fa36, 0x00001297,
+ 0x0000fa37, 0x00001298,
+ 0x0000fa38, 0x00001299,
+ 0x0000fa39, 0x0000129a,
+ 0x0000fa3a, 0x0000129b,
+ 0x0000fa3b, 0x0000129c,
+ 0x0000fa3c, 0x0000129d,
+ 0x0000fa3d, 0x0000129e,
+ 0x0000fa3e, 0x0000129f,
+ 0x0000fa3f, 0x000012a0,
+ 0x0000fa40, 0x000012a1,
+ 0x0000fa41, 0x000012a2,
+ 0x0000fa42, 0x000012a3,
+ 0x0000fa43, 0x000012a4,
+ 0x0000fa44, 0x000012a5,
+ 0x0000fa45, 0x000012a6,
+ 0x0000fa46, 0x000012a7,
+ 0x0000fa47, 0x000012a8,
+ 0x0000fa48, 0x000012a9,
+ 0x0000fa49, 0x000012aa,
+ 0x0000fa4a, 0x000012ab,
+ 0x0000fa4b, 0x000012ac,
+ 0x0000fa4c, 0x000012ad,
+ 0x0000fa4d, 0x000012ae,
+ 0x0000fa4e, 0x000012af,
+ 0x0000fa4f, 0x000012b0,
+ 0x0000fa50, 0x000012b1,
+ 0x0000fa51, 0x000012b2,
+ 0x0000fa52, 0x000012b3,
+ 0x0000fa53, 0x000012b4,
+ 0x0000fa54, 0x000012b5,
+ 0x0000fa55, 0x000012b6,
+ 0x0000fa56, 0x000012b7,
+ 0x0000fa57, 0x000012b8,
+ 0x0000fa58, 0x000012b9,
+ 0x0000fa59, 0x000012ba,
+ 0x0000fa5a, 0x000012bb,
+ 0x0000fa5b, 0x000012bc,
+ 0x0000fa5c, 0x000012bd,
+ 0x0000fa5d, 0x000012be,
+ 0x0000fa5e, 0x000012bf,
+ 0x0000fa5f, 0x000012c0,
+ 0x0000fa60, 0x000012c1,
+ 0x0000fa61, 0x000012c2,
+ 0x0000fa62, 0x000012c3,
+ 0x0000fa63, 0x000012c4,
+ 0x0000fa64, 0x000012c5,
+ 0x0000fa65, 0x000012c6,
+ 0x0000fa66, 0x000012c7,
+ 0x0000fa67, 0x000012c8,
+ 0x0000fa68, 0x000012c9,
+ 0x0000fa69, 0x000012ca,
+ 0x0000fa6a, 0x000012cb,
+ 0x0000fb00, 0x000012cc,
+ 0x0000fb01, 0x000012ce,
+ 0x0000fb02, 0x000012d0,
+ 0x0000fb03, 0x000012d2,
+ 0x0000fb04, 0x000012d5,
+ 0x0000fb05, 0x000012d8,
+ 0x0000fb06, 0x000012da,
+ 0x0000fb13, 0x000012dc,
+ 0x0000fb14, 0x000012de,
+ 0x0000fb15, 0x000012e0,
+ 0x0000fb16, 0x000012e2,
+ 0x0000fb17, 0x000012e4,
+ 0x0000fb1d, 0x000012e6,
+ 0x0000fb1f, 0x000012e8,
+ 0x0000fb20, 0x000012ea,
+ 0x0000fb21, 0x000012eb,
+ 0x0000fb22, 0x000012ec,
+ 0x0000fb23, 0x000012ed,
+ 0x0000fb24, 0x000012ee,
+ 0x0000fb25, 0x000012ef,
+ 0x0000fb26, 0x000012f0,
+ 0x0000fb27, 0x000012f1,
+ 0x0000fb28, 0x000012f2,
+ 0x0000fb29, 0x000012f3,
+ 0x0000fb2a, 0x000012f4,
+ 0x0000fb2b, 0x000012f6,
+ 0x0000fb2c, 0x000012f8,
+ 0x0000fb2d, 0x000012fb,
+ 0x0000fb2e, 0x000012fe,
+ 0x0000fb2f, 0x00001300,
+ 0x0000fb30, 0x00001302,
+ 0x0000fb31, 0x00001304,
+ 0x0000fb32, 0x00001306,
+ 0x0000fb33, 0x00001308,
+ 0x0000fb34, 0x0000130a,
+ 0x0000fb35, 0x0000130c,
+ 0x0000fb36, 0x0000130e,
+ 0x0000fb38, 0x00001310,
+ 0x0000fb39, 0x00001312,
+ 0x0000fb3a, 0x00001314,
+ 0x0000fb3b, 0x00001316,
+ 0x0000fb3c, 0x00001318,
+ 0x0000fb3e, 0x0000131a,
+ 0x0000fb40, 0x0000131c,
+ 0x0000fb41, 0x0000131e,
+ 0x0000fb43, 0x00001320,
+ 0x0000fb44, 0x00001322,
+ 0x0000fb46, 0x00001324,
+ 0x0000fb47, 0x00001326,
+ 0x0000fb48, 0x00001328,
+ 0x0000fb49, 0x0000132a,
+ 0x0000fb4a, 0x0000132c,
+ 0x0000fb4b, 0x0000132e,
+ 0x0000fb4c, 0x00001330,
+ 0x0000fb4d, 0x00001332,
+ 0x0000fb4e, 0x00001334,
+ 0x0000fb4f, 0x00001336,
+ 0x0000fb50, 0x00001338,
+ 0x0000fb51, 0x00001339,
+ 0x0000fb52, 0x0000133a,
+ 0x0000fb53, 0x0000133b,
+ 0x0000fb54, 0x0000133c,
+ 0x0000fb55, 0x0000133d,
+ 0x0000fb56, 0x0000133e,
+ 0x0000fb57, 0x0000133f,
+ 0x0000fb58, 0x00001340,
+ 0x0000fb59, 0x00001341,
+ 0x0000fb5a, 0x00001342,
+ 0x0000fb5b, 0x00001343,
+ 0x0000fb5c, 0x00001344,
+ 0x0000fb5d, 0x00001345,
+ 0x0000fb5e, 0x00001346,
+ 0x0000fb5f, 0x00001347,
+ 0x0000fb60, 0x00001348,
+ 0x0000fb61, 0x00001349,
+ 0x0000fb62, 0x0000134a,
+ 0x0000fb63, 0x0000134b,
+ 0x0000fb64, 0x0000134c,
+ 0x0000fb65, 0x0000134d,
+ 0x0000fb66, 0x0000134e,
+ 0x0000fb67, 0x0000134f,
+ 0x0000fb68, 0x00001350,
+ 0x0000fb69, 0x00001351,
+ 0x0000fb6a, 0x00001352,
+ 0x0000fb6b, 0x00001353,
+ 0x0000fb6c, 0x00001354,
+ 0x0000fb6d, 0x00001355,
+ 0x0000fb6e, 0x00001356,
+ 0x0000fb6f, 0x00001357,
+ 0x0000fb70, 0x00001358,
+ 0x0000fb71, 0x00001359,
+ 0x0000fb72, 0x0000135a,
+ 0x0000fb73, 0x0000135b,
+ 0x0000fb74, 0x0000135c,
+ 0x0000fb75, 0x0000135d,
+ 0x0000fb76, 0x0000135e,
+ 0x0000fb77, 0x0000135f,
+ 0x0000fb78, 0x00001360,
+ 0x0000fb79, 0x00001361,
+ 0x0000fb7a, 0x00001362,
+ 0x0000fb7b, 0x00001363,
+ 0x0000fb7c, 0x00001364,
+ 0x0000fb7d, 0x00001365,
+ 0x0000fb7e, 0x00001366,
+ 0x0000fb7f, 0x00001367,
+ 0x0000fb80, 0x00001368,
+ 0x0000fb81, 0x00001369,
+ 0x0000fb82, 0x0000136a,
+ 0x0000fb83, 0x0000136b,
+ 0x0000fb84, 0x0000136c,
+ 0x0000fb85, 0x0000136d,
+ 0x0000fb86, 0x0000136e,
+ 0x0000fb87, 0x0000136f,
+ 0x0000fb88, 0x00001370,
+ 0x0000fb89, 0x00001371,
+ 0x0000fb8a, 0x00001372,
+ 0x0000fb8b, 0x00001373,
+ 0x0000fb8c, 0x00001374,
+ 0x0000fb8d, 0x00001375,
+ 0x0000fb8e, 0x00001376,
+ 0x0000fb8f, 0x00001377,
+ 0x0000fb90, 0x00001378,
+ 0x0000fb91, 0x00001379,
+ 0x0000fb92, 0x0000137a,
+ 0x0000fb93, 0x0000137b,
+ 0x0000fb94, 0x0000137c,
+ 0x0000fb95, 0x0000137d,
+ 0x0000fb96, 0x0000137e,
+ 0x0000fb97, 0x0000137f,
+ 0x0000fb98, 0x00001380,
+ 0x0000fb99, 0x00001381,
+ 0x0000fb9a, 0x00001382,
+ 0x0000fb9b, 0x00001383,
+ 0x0000fb9c, 0x00001384,
+ 0x0000fb9d, 0x00001385,
+ 0x0000fb9e, 0x00001386,
+ 0x0000fb9f, 0x00001387,
+ 0x0000fba0, 0x00001388,
+ 0x0000fba1, 0x00001389,
+ 0x0000fba2, 0x0000138a,
+ 0x0000fba3, 0x0000138b,
+ 0x0000fba4, 0x0000138c,
+ 0x0000fba5, 0x0000138e,
+ 0x0000fba6, 0x00001390,
+ 0x0000fba7, 0x00001391,
+ 0x0000fba8, 0x00001392,
+ 0x0000fba9, 0x00001393,
+ 0x0000fbaa, 0x00001394,
+ 0x0000fbab, 0x00001395,
+ 0x0000fbac, 0x00001396,
+ 0x0000fbad, 0x00001397,
+ 0x0000fbae, 0x00001398,
+ 0x0000fbaf, 0x00001399,
+ 0x0000fbb0, 0x0000139a,
+ 0x0000fbb1, 0x0000139c,
+ 0x0000fbd3, 0x0000139e,
+ 0x0000fbd4, 0x0000139f,
+ 0x0000fbd5, 0x000013a0,
+ 0x0000fbd6, 0x000013a1,
+ 0x0000fbd7, 0x000013a2,
+ 0x0000fbd8, 0x000013a3,
+ 0x0000fbd9, 0x000013a4,
+ 0x0000fbda, 0x000013a5,
+ 0x0000fbdb, 0x000013a6,
+ 0x0000fbdc, 0x000013a7,
+ 0x0000fbdd, 0x000013a8,
+ 0x0000fbde, 0x000013aa,
+ 0x0000fbdf, 0x000013ab,
+ 0x0000fbe0, 0x000013ac,
+ 0x0000fbe1, 0x000013ad,
+ 0x0000fbe2, 0x000013ae,
+ 0x0000fbe3, 0x000013af,
+ 0x0000fbe4, 0x000013b0,
+ 0x0000fbe5, 0x000013b1,
+ 0x0000fbe6, 0x000013b2,
+ 0x0000fbe7, 0x000013b3,
+ 0x0000fbe8, 0x000013b4,
+ 0x0000fbe9, 0x000013b5,
+ 0x0000fbea, 0x000013b6,
+ 0x0000fbeb, 0x000013b9,
+ 0x0000fbec, 0x000013bc,
+ 0x0000fbed, 0x000013bf,
+ 0x0000fbee, 0x000013c2,
+ 0x0000fbef, 0x000013c5,
+ 0x0000fbf0, 0x000013c8,
+ 0x0000fbf1, 0x000013cb,
+ 0x0000fbf2, 0x000013ce,
+ 0x0000fbf3, 0x000013d1,
+ 0x0000fbf4, 0x000013d4,
+ 0x0000fbf5, 0x000013d7,
+ 0x0000fbf6, 0x000013da,
+ 0x0000fbf7, 0x000013dd,
+ 0x0000fbf8, 0x000013e0,
+ 0x0000fbf9, 0x000013e3,
+ 0x0000fbfa, 0x000013e6,
+ 0x0000fbfb, 0x000013e9,
+ 0x0000fbfc, 0x000013ec,
+ 0x0000fbfd, 0x000013ed,
+ 0x0000fbfe, 0x000013ee,
+ 0x0000fbff, 0x000013ef,
+ 0x0000fc00, 0x000013f0,
+ 0x0000fc01, 0x000013f3,
+ 0x0000fc02, 0x000013f6,
+ 0x0000fc03, 0x000013f9,
+ 0x0000fc04, 0x000013fc,
+ 0x0000fc05, 0x000013ff,
+ 0x0000fc06, 0x00001401,
+ 0x0000fc07, 0x00001403,
+ 0x0000fc08, 0x00001405,
+ 0x0000fc09, 0x00001407,
+ 0x0000fc0a, 0x00001409,
+ 0x0000fc0b, 0x0000140b,
+ 0x0000fc0c, 0x0000140d,
+ 0x0000fc0d, 0x0000140f,
+ 0x0000fc0e, 0x00001411,
+ 0x0000fc0f, 0x00001413,
+ 0x0000fc10, 0x00001415,
+ 0x0000fc11, 0x00001417,
+ 0x0000fc12, 0x00001419,
+ 0x0000fc13, 0x0000141b,
+ 0x0000fc14, 0x0000141d,
+ 0x0000fc15, 0x0000141f,
+ 0x0000fc16, 0x00001421,
+ 0x0000fc17, 0x00001423,
+ 0x0000fc18, 0x00001425,
+ 0x0000fc19, 0x00001427,
+ 0x0000fc1a, 0x00001429,
+ 0x0000fc1b, 0x0000142b,
+ 0x0000fc1c, 0x0000142d,
+ 0x0000fc1d, 0x0000142f,
+ 0x0000fc1e, 0x00001431,
+ 0x0000fc1f, 0x00001433,
+ 0x0000fc20, 0x00001435,
+ 0x0000fc21, 0x00001437,
+ 0x0000fc22, 0x00001439,
+ 0x0000fc23, 0x0000143b,
+ 0x0000fc24, 0x0000143d,
+ 0x0000fc25, 0x0000143f,
+ 0x0000fc26, 0x00001441,
+ 0x0000fc27, 0x00001443,
+ 0x0000fc28, 0x00001445,
+ 0x0000fc29, 0x00001447,
+ 0x0000fc2a, 0x00001449,
+ 0x0000fc2b, 0x0000144b,
+ 0x0000fc2c, 0x0000144d,
+ 0x0000fc2d, 0x0000144f,
+ 0x0000fc2e, 0x00001451,
+ 0x0000fc2f, 0x00001453,
+ 0x0000fc30, 0x00001455,
+ 0x0000fc31, 0x00001457,
+ 0x0000fc32, 0x00001459,
+ 0x0000fc33, 0x0000145b,
+ 0x0000fc34, 0x0000145d,
+ 0x0000fc35, 0x0000145f,
+ 0x0000fc36, 0x00001461,
+ 0x0000fc37, 0x00001463,
+ 0x0000fc38, 0x00001465,
+ 0x0000fc39, 0x00001467,
+ 0x0000fc3a, 0x00001469,
+ 0x0000fc3b, 0x0000146b,
+ 0x0000fc3c, 0x0000146d,
+ 0x0000fc3d, 0x0000146f,
+ 0x0000fc3e, 0x00001471,
+ 0x0000fc3f, 0x00001473,
+ 0x0000fc40, 0x00001475,
+ 0x0000fc41, 0x00001477,
+ 0x0000fc42, 0x00001479,
+ 0x0000fc43, 0x0000147b,
+ 0x0000fc44, 0x0000147d,
+ 0x0000fc45, 0x0000147f,
+ 0x0000fc46, 0x00001481,
+ 0x0000fc47, 0x00001483,
+ 0x0000fc48, 0x00001485,
+ 0x0000fc49, 0x00001487,
+ 0x0000fc4a, 0x00001489,
+ 0x0000fc4b, 0x0000148b,
+ 0x0000fc4c, 0x0000148d,
+ 0x0000fc4d, 0x0000148f,
+ 0x0000fc4e, 0x00001491,
+ 0x0000fc4f, 0x00001493,
+ 0x0000fc50, 0x00001495,
+ 0x0000fc51, 0x00001497,
+ 0x0000fc52, 0x00001499,
+ 0x0000fc53, 0x0000149b,
+ 0x0000fc54, 0x0000149d,
+ 0x0000fc55, 0x0000149f,
+ 0x0000fc56, 0x000014a1,
+ 0x0000fc57, 0x000014a3,
+ 0x0000fc58, 0x000014a5,
+ 0x0000fc59, 0x000014a7,
+ 0x0000fc5a, 0x000014a9,
+ 0x0000fc5b, 0x000014ab,
+ 0x0000fc5c, 0x000014ad,
+ 0x0000fc5d, 0x000014af,
+ 0x0000fc5e, 0x000014b1,
+ 0x0000fc5f, 0x000014b4,
+ 0x0000fc60, 0x000014b7,
+ 0x0000fc61, 0x000014ba,
+ 0x0000fc62, 0x000014bd,
+ 0x0000fc63, 0x000014c0,
+ 0x0000fc64, 0x000014c3,
+ 0x0000fc65, 0x000014c6,
+ 0x0000fc66, 0x000014c9,
+ 0x0000fc67, 0x000014cc,
+ 0x0000fc68, 0x000014cf,
+ 0x0000fc69, 0x000014d2,
+ 0x0000fc6a, 0x000014d5,
+ 0x0000fc6b, 0x000014d7,
+ 0x0000fc6c, 0x000014d9,
+ 0x0000fc6d, 0x000014db,
+ 0x0000fc6e, 0x000014dd,
+ 0x0000fc6f, 0x000014df,
+ 0x0000fc70, 0x000014e1,
+ 0x0000fc71, 0x000014e3,
+ 0x0000fc72, 0x000014e5,
+ 0x0000fc73, 0x000014e7,
+ 0x0000fc74, 0x000014e9,
+ 0x0000fc75, 0x000014eb,
+ 0x0000fc76, 0x000014ed,
+ 0x0000fc77, 0x000014ef,
+ 0x0000fc78, 0x000014f1,
+ 0x0000fc79, 0x000014f3,
+ 0x0000fc7a, 0x000014f5,
+ 0x0000fc7b, 0x000014f7,
+ 0x0000fc7c, 0x000014f9,
+ 0x0000fc7d, 0x000014fb,
+ 0x0000fc7e, 0x000014fd,
+ 0x0000fc7f, 0x000014ff,
+ 0x0000fc80, 0x00001501,
+ 0x0000fc81, 0x00001503,
+ 0x0000fc82, 0x00001505,
+ 0x0000fc83, 0x00001507,
+ 0x0000fc84, 0x00001509,
+ 0x0000fc85, 0x0000150b,
+ 0x0000fc86, 0x0000150d,
+ 0x0000fc87, 0x0000150f,
+ 0x0000fc88, 0x00001511,
+ 0x0000fc89, 0x00001513,
+ 0x0000fc8a, 0x00001515,
+ 0x0000fc8b, 0x00001517,
+ 0x0000fc8c, 0x00001519,
+ 0x0000fc8d, 0x0000151b,
+ 0x0000fc8e, 0x0000151d,
+ 0x0000fc8f, 0x0000151f,
+ 0x0000fc90, 0x00001521,
+ 0x0000fc91, 0x00001523,
+ 0x0000fc92, 0x00001525,
+ 0x0000fc93, 0x00001527,
+ 0x0000fc94, 0x00001529,
+ 0x0000fc95, 0x0000152b,
+ 0x0000fc96, 0x0000152d,
+ 0x0000fc97, 0x0000152f,
+ 0x0000fc98, 0x00001532,
+ 0x0000fc99, 0x00001535,
+ 0x0000fc9a, 0x00001538,
+ 0x0000fc9b, 0x0000153b,
+ 0x0000fc9c, 0x0000153e,
+ 0x0000fc9d, 0x00001540,
+ 0x0000fc9e, 0x00001542,
+ 0x0000fc9f, 0x00001544,
+ 0x0000fca0, 0x00001546,
+ 0x0000fca1, 0x00001548,
+ 0x0000fca2, 0x0000154a,
+ 0x0000fca3, 0x0000154c,
+ 0x0000fca4, 0x0000154e,
+ 0x0000fca5, 0x00001550,
+ 0x0000fca6, 0x00001552,
+ 0x0000fca7, 0x00001554,
+ 0x0000fca8, 0x00001556,
+ 0x0000fca9, 0x00001558,
+ 0x0000fcaa, 0x0000155a,
+ 0x0000fcab, 0x0000155c,
+ 0x0000fcac, 0x0000155e,
+ 0x0000fcad, 0x00001560,
+ 0x0000fcae, 0x00001562,
+ 0x0000fcaf, 0x00001564,
+ 0x0000fcb0, 0x00001566,
+ 0x0000fcb1, 0x00001568,
+ 0x0000fcb2, 0x0000156a,
+ 0x0000fcb3, 0x0000156c,
+ 0x0000fcb4, 0x0000156e,
+ 0x0000fcb5, 0x00001570,
+ 0x0000fcb6, 0x00001572,
+ 0x0000fcb7, 0x00001574,
+ 0x0000fcb8, 0x00001576,
+ 0x0000fcb9, 0x00001578,
+ 0x0000fcba, 0x0000157a,
+ 0x0000fcbb, 0x0000157c,
+ 0x0000fcbc, 0x0000157e,
+ 0x0000fcbd, 0x00001580,
+ 0x0000fcbe, 0x00001582,
+ 0x0000fcbf, 0x00001584,
+ 0x0000fcc0, 0x00001586,
+ 0x0000fcc1, 0x00001588,
+ 0x0000fcc2, 0x0000158a,
+ 0x0000fcc3, 0x0000158c,
+ 0x0000fcc4, 0x0000158e,
+ 0x0000fcc5, 0x00001590,
+ 0x0000fcc6, 0x00001592,
+ 0x0000fcc7, 0x00001594,
+ 0x0000fcc8, 0x00001596,
+ 0x0000fcc9, 0x00001598,
+ 0x0000fcca, 0x0000159a,
+ 0x0000fccb, 0x0000159c,
+ 0x0000fccc, 0x0000159e,
+ 0x0000fccd, 0x000015a0,
+ 0x0000fcce, 0x000015a2,
+ 0x0000fccf, 0x000015a4,
+ 0x0000fcd0, 0x000015a6,
+ 0x0000fcd1, 0x000015a8,
+ 0x0000fcd2, 0x000015aa,
+ 0x0000fcd3, 0x000015ac,
+ 0x0000fcd4, 0x000015ae,
+ 0x0000fcd5, 0x000015b0,
+ 0x0000fcd6, 0x000015b2,
+ 0x0000fcd7, 0x000015b4,
+ 0x0000fcd8, 0x000015b6,
+ 0x0000fcd9, 0x000015b8,
+ 0x0000fcda, 0x000015ba,
+ 0x0000fcdb, 0x000015bc,
+ 0x0000fcdc, 0x000015be,
+ 0x0000fcdd, 0x000015c0,
+ 0x0000fcde, 0x000015c2,
+ 0x0000fcdf, 0x000015c4,
+ 0x0000fce0, 0x000015c7,
+ 0x0000fce1, 0x000015ca,
+ 0x0000fce2, 0x000015cc,
+ 0x0000fce3, 0x000015ce,
+ 0x0000fce4, 0x000015d0,
+ 0x0000fce5, 0x000015d2,
+ 0x0000fce6, 0x000015d4,
+ 0x0000fce7, 0x000015d6,
+ 0x0000fce8, 0x000015d8,
+ 0x0000fce9, 0x000015da,
+ 0x0000fcea, 0x000015dc,
+ 0x0000fceb, 0x000015de,
+ 0x0000fcec, 0x000015e0,
+ 0x0000fced, 0x000015e2,
+ 0x0000fcee, 0x000015e4,
+ 0x0000fcef, 0x000015e6,
+ 0x0000fcf0, 0x000015e8,
+ 0x0000fcf1, 0x000015ea,
+ 0x0000fcf2, 0x000015ec,
+ 0x0000fcf3, 0x000015ef,
+ 0x0000fcf4, 0x000015f2,
+ 0x0000fcf5, 0x000015f5,
+ 0x0000fcf6, 0x000015f7,
+ 0x0000fcf7, 0x000015f9,
+ 0x0000fcf8, 0x000015fb,
+ 0x0000fcf9, 0x000015fd,
+ 0x0000fcfa, 0x000015ff,
+ 0x0000fcfb, 0x00001601,
+ 0x0000fcfc, 0x00001603,
+ 0x0000fcfd, 0x00001605,
+ 0x0000fcfe, 0x00001607,
+ 0x0000fcff, 0x00001609,
+ 0x0000fd00, 0x0000160b,
+ 0x0000fd01, 0x0000160d,
+ 0x0000fd02, 0x0000160f,
+ 0x0000fd03, 0x00001611,
+ 0x0000fd04, 0x00001613,
+ 0x0000fd05, 0x00001615,
+ 0x0000fd06, 0x00001617,
+ 0x0000fd07, 0x00001619,
+ 0x0000fd08, 0x0000161b,
+ 0x0000fd09, 0x0000161d,
+ 0x0000fd0a, 0x0000161f,
+ 0x0000fd0b, 0x00001621,
+ 0x0000fd0c, 0x00001623,
+ 0x0000fd0d, 0x00001625,
+ 0x0000fd0e, 0x00001627,
+ 0x0000fd0f, 0x00001629,
+ 0x0000fd10, 0x0000162b,
+ 0x0000fd11, 0x0000162d,
+ 0x0000fd12, 0x0000162f,
+ 0x0000fd13, 0x00001631,
+ 0x0000fd14, 0x00001633,
+ 0x0000fd15, 0x00001635,
+ 0x0000fd16, 0x00001637,
+ 0x0000fd17, 0x00001639,
+ 0x0000fd18, 0x0000163b,
+ 0x0000fd19, 0x0000163d,
+ 0x0000fd1a, 0x0000163f,
+ 0x0000fd1b, 0x00001641,
+ 0x0000fd1c, 0x00001643,
+ 0x0000fd1d, 0x00001645,
+ 0x0000fd1e, 0x00001647,
+ 0x0000fd1f, 0x00001649,
+ 0x0000fd20, 0x0000164b,
+ 0x0000fd21, 0x0000164d,
+ 0x0000fd22, 0x0000164f,
+ 0x0000fd23, 0x00001651,
+ 0x0000fd24, 0x00001653,
+ 0x0000fd25, 0x00001655,
+ 0x0000fd26, 0x00001657,
+ 0x0000fd27, 0x00001659,
+ 0x0000fd28, 0x0000165b,
+ 0x0000fd29, 0x0000165d,
+ 0x0000fd2a, 0x0000165f,
+ 0x0000fd2b, 0x00001661,
+ 0x0000fd2c, 0x00001663,
+ 0x0000fd2d, 0x00001665,
+ 0x0000fd2e, 0x00001667,
+ 0x0000fd2f, 0x00001669,
+ 0x0000fd30, 0x0000166b,
+ 0x0000fd31, 0x0000166d,
+ 0x0000fd32, 0x0000166f,
+ 0x0000fd33, 0x00001671,
+ 0x0000fd34, 0x00001673,
+ 0x0000fd35, 0x00001675,
+ 0x0000fd36, 0x00001677,
+ 0x0000fd37, 0x00001679,
+ 0x0000fd38, 0x0000167b,
+ 0x0000fd39, 0x0000167d,
+ 0x0000fd3a, 0x0000167f,
+ 0x0000fd3b, 0x00001681,
+ 0x0000fd3c, 0x00001683,
+ 0x0000fd3d, 0x00001685,
+ 0x0000fd50, 0x00001687,
+ 0x0000fd51, 0x0000168a,
+ 0x0000fd52, 0x0000168d,
+ 0x0000fd53, 0x00001690,
+ 0x0000fd54, 0x00001693,
+ 0x0000fd55, 0x00001696,
+ 0x0000fd56, 0x00001699,
+ 0x0000fd57, 0x0000169c,
+ 0x0000fd58, 0x0000169f,
+ 0x0000fd59, 0x000016a2,
+ 0x0000fd5a, 0x000016a5,
+ 0x0000fd5b, 0x000016a8,
+ 0x0000fd5c, 0x000016ab,
+ 0x0000fd5d, 0x000016ae,
+ 0x0000fd5e, 0x000016b1,
+ 0x0000fd5f, 0x000016b4,
+ 0x0000fd60, 0x000016b7,
+ 0x0000fd61, 0x000016ba,
+ 0x0000fd62, 0x000016bd,
+ 0x0000fd63, 0x000016c0,
+ 0x0000fd64, 0x000016c3,
+ 0x0000fd65, 0x000016c6,
+ 0x0000fd66, 0x000016c9,
+ 0x0000fd67, 0x000016cc,
+ 0x0000fd68, 0x000016cf,
+ 0x0000fd69, 0x000016d2,
+ 0x0000fd6a, 0x000016d5,
+ 0x0000fd6b, 0x000016d8,
+ 0x0000fd6c, 0x000016db,
+ 0x0000fd6d, 0x000016de,
+ 0x0000fd6e, 0x000016e1,
+ 0x0000fd6f, 0x000016e4,
+ 0x0000fd70, 0x000016e7,
+ 0x0000fd71, 0x000016ea,
+ 0x0000fd72, 0x000016ed,
+ 0x0000fd73, 0x000016f0,
+ 0x0000fd74, 0x000016f3,
+ 0x0000fd75, 0x000016f6,
+ 0x0000fd76, 0x000016f9,
+ 0x0000fd77, 0x000016fc,
+ 0x0000fd78, 0x000016ff,
+ 0x0000fd79, 0x00001702,
+ 0x0000fd7a, 0x00001705,
+ 0x0000fd7b, 0x00001708,
+ 0x0000fd7c, 0x0000170b,
+ 0x0000fd7d, 0x0000170e,
+ 0x0000fd7e, 0x00001711,
+ 0x0000fd7f, 0x00001714,
+ 0x0000fd80, 0x00001717,
+ 0x0000fd81, 0x0000171a,
+ 0x0000fd82, 0x0000171d,
+ 0x0000fd83, 0x00001720,
+ 0x0000fd84, 0x00001723,
+ 0x0000fd85, 0x00001726,
+ 0x0000fd86, 0x00001729,
+ 0x0000fd87, 0x0000172c,
+ 0x0000fd88, 0x0000172f,
+ 0x0000fd89, 0x00001732,
+ 0x0000fd8a, 0x00001735,
+ 0x0000fd8b, 0x00001738,
+ 0x0000fd8c, 0x0000173b,
+ 0x0000fd8d, 0x0000173e,
+ 0x0000fd8e, 0x00001741,
+ 0x0000fd8f, 0x00001744,
+ 0x0000fd92, 0x00001747,
+ 0x0000fd93, 0x0000174a,
+ 0x0000fd94, 0x0000174d,
+ 0x0000fd95, 0x00001750,
+ 0x0000fd96, 0x00001753,
+ 0x0000fd97, 0x00001756,
+ 0x0000fd98, 0x00001759,
+ 0x0000fd99, 0x0000175c,
+ 0x0000fd9a, 0x0000175f,
+ 0x0000fd9b, 0x00001762,
+ 0x0000fd9c, 0x00001765,
+ 0x0000fd9d, 0x00001768,
+ 0x0000fd9e, 0x0000176b,
+ 0x0000fd9f, 0x0000176e,
+ 0x0000fda0, 0x00001771,
+ 0x0000fda1, 0x00001774,
+ 0x0000fda2, 0x00001777,
+ 0x0000fda3, 0x0000177a,
+ 0x0000fda4, 0x0000177d,
+ 0x0000fda5, 0x00001780,
+ 0x0000fda6, 0x00001783,
+ 0x0000fda7, 0x00001786,
+ 0x0000fda8, 0x00001789,
+ 0x0000fda9, 0x0000178c,
+ 0x0000fdaa, 0x0000178f,
+ 0x0000fdab, 0x00001792,
+ 0x0000fdac, 0x00001795,
+ 0x0000fdad, 0x00001798,
+ 0x0000fdae, 0x0000179b,
+ 0x0000fdaf, 0x0000179e,
+ 0x0000fdb0, 0x000017a1,
+ 0x0000fdb1, 0x000017a4,
+ 0x0000fdb2, 0x000017a7,
+ 0x0000fdb3, 0x000017aa,
+ 0x0000fdb4, 0x000017ad,
+ 0x0000fdb5, 0x000017b0,
+ 0x0000fdb6, 0x000017b3,
+ 0x0000fdb7, 0x000017b6,
+ 0x0000fdb8, 0x000017b9,
+ 0x0000fdb9, 0x000017bc,
+ 0x0000fdba, 0x000017bf,
+ 0x0000fdbb, 0x000017c2,
+ 0x0000fdbc, 0x000017c5,
+ 0x0000fdbd, 0x000017c8,
+ 0x0000fdbe, 0x000017cb,
+ 0x0000fdbf, 0x000017ce,
+ 0x0000fdc0, 0x000017d1,
+ 0x0000fdc1, 0x000017d4,
+ 0x0000fdc2, 0x000017d7,
+ 0x0000fdc3, 0x000017da,
+ 0x0000fdc4, 0x000017dd,
+ 0x0000fdc5, 0x000017e0,
+ 0x0000fdc6, 0x000017e3,
+ 0x0000fdc7, 0x000017e6,
+ 0x0000fdf0, 0x000017e9,
+ 0x0000fdf1, 0x000017ec,
+ 0x0000fdf2, 0x000017ef,
+ 0x0000fdf3, 0x000017f3,
+ 0x0000fdf4, 0x000017f7,
+ 0x0000fdf5, 0x000017fb,
+ 0x0000fdf6, 0x000017ff,
+ 0x0000fdf7, 0x00001803,
+ 0x0000fdf8, 0x00001807,
+ 0x0000fdf9, 0x0000180b,
+ 0x0000fdfa, 0x0000180e,
+ 0x0000fdfb, 0x00001820,
+ 0x0000fdfc, 0x00001828,
+ 0x0000fe30, 0x0000182c,
+ 0x0000fe31, 0x0000182e,
+ 0x0000fe32, 0x0000182f,
+ 0x0000fe33, 0x00001830,
+ 0x0000fe34, 0x00001831,
+ 0x0000fe35, 0x00001832,
+ 0x0000fe36, 0x00001833,
+ 0x0000fe37, 0x00001834,
+ 0x0000fe38, 0x00001835,
+ 0x0000fe39, 0x00001836,
+ 0x0000fe3a, 0x00001837,
+ 0x0000fe3b, 0x00001838,
+ 0x0000fe3c, 0x00001839,
+ 0x0000fe3d, 0x0000183a,
+ 0x0000fe3e, 0x0000183b,
+ 0x0000fe3f, 0x0000183c,
+ 0x0000fe40, 0x0000183d,
+ 0x0000fe41, 0x0000183e,
+ 0x0000fe42, 0x0000183f,
+ 0x0000fe43, 0x00001840,
+ 0x0000fe44, 0x00001841,
+ 0x0000fe49, 0x00001842,
+ 0x0000fe4a, 0x00001844,
+ 0x0000fe4b, 0x00001846,
+ 0x0000fe4c, 0x00001848,
+ 0x0000fe4d, 0x0000184a,
+ 0x0000fe4e, 0x0000184b,
+ 0x0000fe4f, 0x0000184c,
+ 0x0000fe50, 0x0000184d,
+ 0x0000fe51, 0x0000184e,
+ 0x0000fe52, 0x0000184f,
+ 0x0000fe54, 0x00001850,
+ 0x0000fe55, 0x00001851,
+ 0x0000fe56, 0x00001852,
+ 0x0000fe57, 0x00001853,
+ 0x0000fe58, 0x00001854,
+ 0x0000fe59, 0x00001855,
+ 0x0000fe5a, 0x00001856,
+ 0x0000fe5b, 0x00001857,
+ 0x0000fe5c, 0x00001858,
+ 0x0000fe5d, 0x00001859,
+ 0x0000fe5e, 0x0000185a,
+ 0x0000fe5f, 0x0000185b,
+ 0x0000fe60, 0x0000185c,
+ 0x0000fe61, 0x0000185d,
+ 0x0000fe62, 0x0000185e,
+ 0x0000fe63, 0x0000185f,
+ 0x0000fe64, 0x00001860,
+ 0x0000fe65, 0x00001861,
+ 0x0000fe66, 0x00001862,
+ 0x0000fe68, 0x00001863,
+ 0x0000fe69, 0x00001864,
+ 0x0000fe6a, 0x00001865,
+ 0x0000fe6b, 0x00001866,
+ 0x0000fe70, 0x00001867,
+ 0x0000fe71, 0x00001869,
+ 0x0000fe72, 0x0000186b,
+ 0x0000fe74, 0x0000186d,
+ 0x0000fe76, 0x0000186f,
+ 0x0000fe77, 0x00001871,
+ 0x0000fe78, 0x00001873,
+ 0x0000fe79, 0x00001875,
+ 0x0000fe7a, 0x00001877,
+ 0x0000fe7b, 0x00001879,
+ 0x0000fe7c, 0x0000187b,
+ 0x0000fe7d, 0x0000187d,
+ 0x0000fe7e, 0x0000187f,
+ 0x0000fe7f, 0x00001881,
+ 0x0000fe80, 0x00001883,
+ 0x0000fe81, 0x00001884,
+ 0x0000fe82, 0x00001886,
+ 0x0000fe83, 0x00001888,
+ 0x0000fe84, 0x0000188a,
+ 0x0000fe85, 0x0000188c,
+ 0x0000fe86, 0x0000188e,
+ 0x0000fe87, 0x00001890,
+ 0x0000fe88, 0x00001892,
+ 0x0000fe89, 0x00001894,
+ 0x0000fe8a, 0x00001896,
+ 0x0000fe8b, 0x00001898,
+ 0x0000fe8c, 0x0000189a,
+ 0x0000fe8d, 0x0000189c,
+ 0x0000fe8e, 0x0000189d,
+ 0x0000fe8f, 0x0000189e,
+ 0x0000fe90, 0x0000189f,
+ 0x0000fe91, 0x000018a0,
+ 0x0000fe92, 0x000018a1,
+ 0x0000fe93, 0x000018a2,
+ 0x0000fe94, 0x000018a3,
+ 0x0000fe95, 0x000018a4,
+ 0x0000fe96, 0x000018a5,
+ 0x0000fe97, 0x000018a6,
+ 0x0000fe98, 0x000018a7,
+ 0x0000fe99, 0x000018a8,
+ 0x0000fe9a, 0x000018a9,
+ 0x0000fe9b, 0x000018aa,
+ 0x0000fe9c, 0x000018ab,
+ 0x0000fe9d, 0x000018ac,
+ 0x0000fe9e, 0x000018ad,
+ 0x0000fe9f, 0x000018ae,
+ 0x0000fea0, 0x000018af,
+ 0x0000fea1, 0x000018b0,
+ 0x0000fea2, 0x000018b1,
+ 0x0000fea3, 0x000018b2,
+ 0x0000fea4, 0x000018b3,
+ 0x0000fea5, 0x000018b4,
+ 0x0000fea6, 0x000018b5,
+ 0x0000fea7, 0x000018b6,
+ 0x0000fea8, 0x000018b7,
+ 0x0000fea9, 0x000018b8,
+ 0x0000feaa, 0x000018b9,
+ 0x0000feab, 0x000018ba,
+ 0x0000feac, 0x000018bb,
+ 0x0000fead, 0x000018bc,
+ 0x0000feae, 0x000018bd,
+ 0x0000feaf, 0x000018be,
+ 0x0000feb0, 0x000018bf,
+ 0x0000feb1, 0x000018c0,
+ 0x0000feb2, 0x000018c1,
+ 0x0000feb3, 0x000018c2,
+ 0x0000feb4, 0x000018c3,
+ 0x0000feb5, 0x000018c4,
+ 0x0000feb6, 0x000018c5,
+ 0x0000feb7, 0x000018c6,
+ 0x0000feb8, 0x000018c7,
+ 0x0000feb9, 0x000018c8,
+ 0x0000feba, 0x000018c9,
+ 0x0000febb, 0x000018ca,
+ 0x0000febc, 0x000018cb,
+ 0x0000febd, 0x000018cc,
+ 0x0000febe, 0x000018cd,
+ 0x0000febf, 0x000018ce,
+ 0x0000fec0, 0x000018cf,
+ 0x0000fec1, 0x000018d0,
+ 0x0000fec2, 0x000018d1,
+ 0x0000fec3, 0x000018d2,
+ 0x0000fec4, 0x000018d3,
+ 0x0000fec5, 0x000018d4,
+ 0x0000fec6, 0x000018d5,
+ 0x0000fec7, 0x000018d6,
+ 0x0000fec8, 0x000018d7,
+ 0x0000fec9, 0x000018d8,
+ 0x0000feca, 0x000018d9,
+ 0x0000fecb, 0x000018da,
+ 0x0000fecc, 0x000018db,
+ 0x0000fecd, 0x000018dc,
+ 0x0000fece, 0x000018dd,
+ 0x0000fecf, 0x000018de,
+ 0x0000fed0, 0x000018df,
+ 0x0000fed1, 0x000018e0,
+ 0x0000fed2, 0x000018e1,
+ 0x0000fed3, 0x000018e2,
+ 0x0000fed4, 0x000018e3,
+ 0x0000fed5, 0x000018e4,
+ 0x0000fed6, 0x000018e5,
+ 0x0000fed7, 0x000018e6,
+ 0x0000fed8, 0x000018e7,
+ 0x0000fed9, 0x000018e8,
+ 0x0000feda, 0x000018e9,
+ 0x0000fedb, 0x000018ea,
+ 0x0000fedc, 0x000018eb,
+ 0x0000fedd, 0x000018ec,
+ 0x0000fede, 0x000018ed,
+ 0x0000fedf, 0x000018ee,
+ 0x0000fee0, 0x000018ef,
+ 0x0000fee1, 0x000018f0,
+ 0x0000fee2, 0x000018f1,
+ 0x0000fee3, 0x000018f2,
+ 0x0000fee4, 0x000018f3,
+ 0x0000fee5, 0x000018f4,
+ 0x0000fee6, 0x000018f5,
+ 0x0000fee7, 0x000018f6,
+ 0x0000fee8, 0x000018f7,
+ 0x0000fee9, 0x000018f8,
+ 0x0000feea, 0x000018f9,
+ 0x0000feeb, 0x000018fa,
+ 0x0000feec, 0x000018fb,
+ 0x0000feed, 0x000018fc,
+ 0x0000feee, 0x000018fd,
+ 0x0000feef, 0x000018fe,
+ 0x0000fef0, 0x000018ff,
+ 0x0000fef1, 0x00001900,
+ 0x0000fef2, 0x00001901,
+ 0x0000fef3, 0x00001902,
+ 0x0000fef4, 0x00001903,
+ 0x0000fef5, 0x00001904,
+ 0x0000fef6, 0x00001907,
+ 0x0000fef7, 0x0000190a,
+ 0x0000fef8, 0x0000190d,
+ 0x0000fef9, 0x00001910,
+ 0x0000fefa, 0x00001913,
+ 0x0000fefb, 0x00001916,
+ 0x0000fefc, 0x00001918,
+ 0x0000ff01, 0x0000191a,
+ 0x0000ff02, 0x0000191b,
+ 0x0000ff03, 0x0000191c,
+ 0x0000ff04, 0x0000191d,
+ 0x0000ff05, 0x0000191e,
+ 0x0000ff06, 0x0000191f,
+ 0x0000ff07, 0x00001920,
+ 0x0000ff08, 0x00001921,
+ 0x0000ff09, 0x00001922,
+ 0x0000ff0a, 0x00001923,
+ 0x0000ff0b, 0x00001924,
+ 0x0000ff0c, 0x00001925,
+ 0x0000ff0d, 0x00001926,
+ 0x0000ff0e, 0x00001927,
+ 0x0000ff0f, 0x00001928,
+ 0x0000ff10, 0x00001929,
+ 0x0000ff11, 0x0000192a,
+ 0x0000ff12, 0x0000192b,
+ 0x0000ff13, 0x0000192c,
+ 0x0000ff14, 0x0000192d,
+ 0x0000ff15, 0x0000192e,
+ 0x0000ff16, 0x0000192f,
+ 0x0000ff17, 0x00001930,
+ 0x0000ff18, 0x00001931,
+ 0x0000ff19, 0x00001932,
+ 0x0000ff1a, 0x00001933,
+ 0x0000ff1b, 0x00001934,
+ 0x0000ff1c, 0x00001935,
+ 0x0000ff1d, 0x00001936,
+ 0x0000ff1e, 0x00001937,
+ 0x0000ff1f, 0x00001938,
+ 0x0000ff20, 0x00001939,
+ 0x0000ff21, 0x0000193a,
+ 0x0000ff22, 0x0000193b,
+ 0x0000ff23, 0x0000193c,
+ 0x0000ff24, 0x0000193d,
+ 0x0000ff25, 0x0000193e,
+ 0x0000ff26, 0x0000193f,
+ 0x0000ff27, 0x00001940,
+ 0x0000ff28, 0x00001941,
+ 0x0000ff29, 0x00001942,
+ 0x0000ff2a, 0x00001943,
+ 0x0000ff2b, 0x00001944,
+ 0x0000ff2c, 0x00001945,
+ 0x0000ff2d, 0x00001946,
+ 0x0000ff2e, 0x00001947,
+ 0x0000ff2f, 0x00001948,
+ 0x0000ff30, 0x00001949,
+ 0x0000ff31, 0x0000194a,
+ 0x0000ff32, 0x0000194b,
+ 0x0000ff33, 0x0000194c,
+ 0x0000ff34, 0x0000194d,
+ 0x0000ff35, 0x0000194e,
+ 0x0000ff36, 0x0000194f,
+ 0x0000ff37, 0x00001950,
+ 0x0000ff38, 0x00001951,
+ 0x0000ff39, 0x00001952,
+ 0x0000ff3a, 0x00001953,
+ 0x0000ff3b, 0x00001954,
+ 0x0000ff3c, 0x00001955,
+ 0x0000ff3d, 0x00001956,
+ 0x0000ff3e, 0x00001957,
+ 0x0000ff3f, 0x00001958,
+ 0x0000ff40, 0x00001959,
+ 0x0000ff41, 0x0000195a,
+ 0x0000ff42, 0x0000195b,
+ 0x0000ff43, 0x0000195c,
+ 0x0000ff44, 0x0000195d,
+ 0x0000ff45, 0x0000195e,
+ 0x0000ff46, 0x0000195f,
+ 0x0000ff47, 0x00001960,
+ 0x0000ff48, 0x00001961,
+ 0x0000ff49, 0x00001962,
+ 0x0000ff4a, 0x00001963,
+ 0x0000ff4b, 0x00001964,
+ 0x0000ff4c, 0x00001965,
+ 0x0000ff4d, 0x00001966,
+ 0x0000ff4e, 0x00001967,
+ 0x0000ff4f, 0x00001968,
+ 0x0000ff50, 0x00001969,
+ 0x0000ff51, 0x0000196a,
+ 0x0000ff52, 0x0000196b,
+ 0x0000ff53, 0x0000196c,
+ 0x0000ff54, 0x0000196d,
+ 0x0000ff55, 0x0000196e,
+ 0x0000ff56, 0x0000196f,
+ 0x0000ff57, 0x00001970,
+ 0x0000ff58, 0x00001971,
+ 0x0000ff59, 0x00001972,
+ 0x0000ff5a, 0x00001973,
+ 0x0000ff5b, 0x00001974,
+ 0x0000ff5c, 0x00001975,
+ 0x0000ff5d, 0x00001976,
+ 0x0000ff5e, 0x00001977,
+ 0x0000ff5f, 0x00001978,
+ 0x0000ff60, 0x00001979,
+ 0x0000ff61, 0x0000197a,
+ 0x0000ff62, 0x0000197b,
+ 0x0000ff63, 0x0000197c,
+ 0x0000ff64, 0x0000197d,
+ 0x0000ff65, 0x0000197e,
+ 0x0000ff66, 0x0000197f,
+ 0x0000ff67, 0x00001980,
+ 0x0000ff68, 0x00001981,
+ 0x0000ff69, 0x00001982,
+ 0x0000ff6a, 0x00001983,
+ 0x0000ff6b, 0x00001984,
+ 0x0000ff6c, 0x00001985,
+ 0x0000ff6d, 0x00001986,
+ 0x0000ff6e, 0x00001987,
+ 0x0000ff6f, 0x00001988,
+ 0x0000ff70, 0x00001989,
+ 0x0000ff71, 0x0000198a,
+ 0x0000ff72, 0x0000198b,
+ 0x0000ff73, 0x0000198c,
+ 0x0000ff74, 0x0000198d,
+ 0x0000ff75, 0x0000198e,
+ 0x0000ff76, 0x0000198f,
+ 0x0000ff77, 0x00001990,
+ 0x0000ff78, 0x00001991,
+ 0x0000ff79, 0x00001992,
+ 0x0000ff7a, 0x00001993,
+ 0x0000ff7b, 0x00001994,
+ 0x0000ff7c, 0x00001995,
+ 0x0000ff7d, 0x00001996,
+ 0x0000ff7e, 0x00001997,
+ 0x0000ff7f, 0x00001998,
+ 0x0000ff80, 0x00001999,
+ 0x0000ff81, 0x0000199a,
+ 0x0000ff82, 0x0000199b,
+ 0x0000ff83, 0x0000199c,
+ 0x0000ff84, 0x0000199d,
+ 0x0000ff85, 0x0000199e,
+ 0x0000ff86, 0x0000199f,
+ 0x0000ff87, 0x000019a0,
+ 0x0000ff88, 0x000019a1,
+ 0x0000ff89, 0x000019a2,
+ 0x0000ff8a, 0x000019a3,
+ 0x0000ff8b, 0x000019a4,
+ 0x0000ff8c, 0x000019a5,
+ 0x0000ff8d, 0x000019a6,
+ 0x0000ff8e, 0x000019a7,
+ 0x0000ff8f, 0x000019a8,
+ 0x0000ff90, 0x000019a9,
+ 0x0000ff91, 0x000019aa,
+ 0x0000ff92, 0x000019ab,
+ 0x0000ff93, 0x000019ac,
+ 0x0000ff94, 0x000019ad,
+ 0x0000ff95, 0x000019ae,
+ 0x0000ff96, 0x000019af,
+ 0x0000ff97, 0x000019b0,
+ 0x0000ff98, 0x000019b1,
+ 0x0000ff99, 0x000019b2,
+ 0x0000ff9a, 0x000019b3,
+ 0x0000ff9b, 0x000019b4,
+ 0x0000ff9c, 0x000019b5,
+ 0x0000ff9d, 0x000019b6,
+ 0x0000ff9e, 0x000019b7,
+ 0x0000ff9f, 0x000019b8,
+ 0x0000ffa0, 0x000019b9,
+ 0x0000ffa1, 0x000019ba,
+ 0x0000ffa2, 0x000019bb,
+ 0x0000ffa3, 0x000019bc,
+ 0x0000ffa4, 0x000019bd,
+ 0x0000ffa5, 0x000019be,
+ 0x0000ffa6, 0x000019bf,
+ 0x0000ffa7, 0x000019c0,
+ 0x0000ffa8, 0x000019c1,
+ 0x0000ffa9, 0x000019c2,
+ 0x0000ffaa, 0x000019c3,
+ 0x0000ffab, 0x000019c4,
+ 0x0000ffac, 0x000019c5,
+ 0x0000ffad, 0x000019c6,
+ 0x0000ffae, 0x000019c7,
+ 0x0000ffaf, 0x000019c8,
+ 0x0000ffb0, 0x000019c9,
+ 0x0000ffb1, 0x000019ca,
+ 0x0000ffb2, 0x000019cb,
+ 0x0000ffb3, 0x000019cc,
+ 0x0000ffb4, 0x000019cd,
+ 0x0000ffb5, 0x000019ce,
+ 0x0000ffb6, 0x000019cf,
+ 0x0000ffb7, 0x000019d0,
+ 0x0000ffb8, 0x000019d1,
+ 0x0000ffb9, 0x000019d2,
+ 0x0000ffba, 0x000019d3,
+ 0x0000ffbb, 0x000019d4,
+ 0x0000ffbc, 0x000019d5,
+ 0x0000ffbd, 0x000019d6,
+ 0x0000ffbe, 0x000019d7,
+ 0x0000ffc2, 0x000019d8,
+ 0x0000ffc3, 0x000019d9,
+ 0x0000ffc4, 0x000019da,
+ 0x0000ffc5, 0x000019db,
+ 0x0000ffc6, 0x000019dc,
+ 0x0000ffc7, 0x000019dd,
+ 0x0000ffca, 0x000019de,
+ 0x0000ffcb, 0x000019df,
+ 0x0000ffcc, 0x000019e0,
+ 0x0000ffcd, 0x000019e1,
+ 0x0000ffce, 0x000019e2,
+ 0x0000ffcf, 0x000019e3,
+ 0x0000ffd2, 0x000019e4,
+ 0x0000ffd3, 0x000019e5,
+ 0x0000ffd4, 0x000019e6,
+ 0x0000ffd5, 0x000019e7,
+ 0x0000ffd6, 0x000019e8,
+ 0x0000ffd7, 0x000019e9,
+ 0x0000ffda, 0x000019ea,
+ 0x0000ffdb, 0x000019eb,
+ 0x0000ffdc, 0x000019ec,
+ 0x0000ffe0, 0x000019ed,
+ 0x0000ffe1, 0x000019ee,
+ 0x0000ffe2, 0x000019ef,
+ 0x0000ffe3, 0x000019f0,
+ 0x0000ffe4, 0x000019f2,
+ 0x0000ffe5, 0x000019f3,
+ 0x0000ffe6, 0x000019f4,
+ 0x0000ffe8, 0x000019f5,
+ 0x0000ffe9, 0x000019f6,
+ 0x0000ffea, 0x000019f7,
+ 0x0000ffeb, 0x000019f8,
+ 0x0000ffec, 0x000019f9,
+ 0x0000ffed, 0x000019fa,
+ 0x0000ffee, 0x000019fb,
+ 0x0001d15e, 0x000019fc,
+ 0x0001d15f, 0x000019fe,
+ 0x0001d160, 0x00001a00,
+ 0x0001d161, 0x00001a03,
+ 0x0001d162, 0x00001a06,
+ 0x0001d163, 0x00001a09,
+ 0x0001d164, 0x00001a0c,
+ 0x0001d1bb, 0x00001a0f,
+ 0x0001d1bc, 0x00001a11,
+ 0x0001d1bd, 0x00001a13,
+ 0x0001d1be, 0x00001a16,
+ 0x0001d1bf, 0x00001a19,
+ 0x0001d1c0, 0x00001a1c,
+ 0x0001d400, 0x00001a1f,
+ 0x0001d401, 0x00001a20,
+ 0x0001d402, 0x00001a21,
+ 0x0001d403, 0x00001a22,
+ 0x0001d404, 0x00001a23,
+ 0x0001d405, 0x00001a24,
+ 0x0001d406, 0x00001a25,
+ 0x0001d407, 0x00001a26,
+ 0x0001d408, 0x00001a27,
+ 0x0001d409, 0x00001a28,
+ 0x0001d40a, 0x00001a29,
+ 0x0001d40b, 0x00001a2a,
+ 0x0001d40c, 0x00001a2b,
+ 0x0001d40d, 0x00001a2c,
+ 0x0001d40e, 0x00001a2d,
+ 0x0001d40f, 0x00001a2e,
+ 0x0001d410, 0x00001a2f,
+ 0x0001d411, 0x00001a30,
+ 0x0001d412, 0x00001a31,
+ 0x0001d413, 0x00001a32,
+ 0x0001d414, 0x00001a33,
+ 0x0001d415, 0x00001a34,
+ 0x0001d416, 0x00001a35,
+ 0x0001d417, 0x00001a36,
+ 0x0001d418, 0x00001a37,
+ 0x0001d419, 0x00001a38,
+ 0x0001d41a, 0x00001a39,
+ 0x0001d41b, 0x00001a3a,
+ 0x0001d41c, 0x00001a3b,
+ 0x0001d41d, 0x00001a3c,
+ 0x0001d41e, 0x00001a3d,
+ 0x0001d41f, 0x00001a3e,
+ 0x0001d420, 0x00001a3f,
+ 0x0001d421, 0x00001a40,
+ 0x0001d422, 0x00001a41,
+ 0x0001d423, 0x00001a42,
+ 0x0001d424, 0x00001a43,
+ 0x0001d425, 0x00001a44,
+ 0x0001d426, 0x00001a45,
+ 0x0001d427, 0x00001a46,
+ 0x0001d428, 0x00001a47,
+ 0x0001d429, 0x00001a48,
+ 0x0001d42a, 0x00001a49,
+ 0x0001d42b, 0x00001a4a,
+ 0x0001d42c, 0x00001a4b,
+ 0x0001d42d, 0x00001a4c,
+ 0x0001d42e, 0x00001a4d,
+ 0x0001d42f, 0x00001a4e,
+ 0x0001d430, 0x00001a4f,
+ 0x0001d431, 0x00001a50,
+ 0x0001d432, 0x00001a51,
+ 0x0001d433, 0x00001a52,
+ 0x0001d434, 0x00001a53,
+ 0x0001d435, 0x00001a54,
+ 0x0001d436, 0x00001a55,
+ 0x0001d437, 0x00001a56,
+ 0x0001d438, 0x00001a57,
+ 0x0001d439, 0x00001a58,
+ 0x0001d43a, 0x00001a59,
+ 0x0001d43b, 0x00001a5a,
+ 0x0001d43c, 0x00001a5b,
+ 0x0001d43d, 0x00001a5c,
+ 0x0001d43e, 0x00001a5d,
+ 0x0001d43f, 0x00001a5e,
+ 0x0001d440, 0x00001a5f,
+ 0x0001d441, 0x00001a60,
+ 0x0001d442, 0x00001a61,
+ 0x0001d443, 0x00001a62,
+ 0x0001d444, 0x00001a63,
+ 0x0001d445, 0x00001a64,
+ 0x0001d446, 0x00001a65,
+ 0x0001d447, 0x00001a66,
+ 0x0001d448, 0x00001a67,
+ 0x0001d449, 0x00001a68,
+ 0x0001d44a, 0x00001a69,
+ 0x0001d44b, 0x00001a6a,
+ 0x0001d44c, 0x00001a6b,
+ 0x0001d44d, 0x00001a6c,
+ 0x0001d44e, 0x00001a6d,
+ 0x0001d44f, 0x00001a6e,
+ 0x0001d450, 0x00001a6f,
+ 0x0001d451, 0x00001a70,
+ 0x0001d452, 0x00001a71,
+ 0x0001d453, 0x00001a72,
+ 0x0001d454, 0x00001a73,
+ 0x0001d456, 0x00001a74,
+ 0x0001d457, 0x00001a75,
+ 0x0001d458, 0x00001a76,
+ 0x0001d459, 0x00001a77,
+ 0x0001d45a, 0x00001a78,
+ 0x0001d45b, 0x00001a79,
+ 0x0001d45c, 0x00001a7a,
+ 0x0001d45d, 0x00001a7b,
+ 0x0001d45e, 0x00001a7c,
+ 0x0001d45f, 0x00001a7d,
+ 0x0001d460, 0x00001a7e,
+ 0x0001d461, 0x00001a7f,
+ 0x0001d462, 0x00001a80,
+ 0x0001d463, 0x00001a81,
+ 0x0001d464, 0x00001a82,
+ 0x0001d465, 0x00001a83,
+ 0x0001d466, 0x00001a84,
+ 0x0001d467, 0x00001a85,
+ 0x0001d468, 0x00001a86,
+ 0x0001d469, 0x00001a87,
+ 0x0001d46a, 0x00001a88,
+ 0x0001d46b, 0x00001a89,
+ 0x0001d46c, 0x00001a8a,
+ 0x0001d46d, 0x00001a8b,
+ 0x0001d46e, 0x00001a8c,
+ 0x0001d46f, 0x00001a8d,
+ 0x0001d470, 0x00001a8e,
+ 0x0001d471, 0x00001a8f,
+ 0x0001d472, 0x00001a90,
+ 0x0001d473, 0x00001a91,
+ 0x0001d474, 0x00001a92,
+ 0x0001d475, 0x00001a93,
+ 0x0001d476, 0x00001a94,
+ 0x0001d477, 0x00001a95,
+ 0x0001d478, 0x00001a96,
+ 0x0001d479, 0x00001a97,
+ 0x0001d47a, 0x00001a98,
+ 0x0001d47b, 0x00001a99,
+ 0x0001d47c, 0x00001a9a,
+ 0x0001d47d, 0x00001a9b,
+ 0x0001d47e, 0x00001a9c,
+ 0x0001d47f, 0x00001a9d,
+ 0x0001d480, 0x00001a9e,
+ 0x0001d481, 0x00001a9f,
+ 0x0001d482, 0x00001aa0,
+ 0x0001d483, 0x00001aa1,
+ 0x0001d484, 0x00001aa2,
+ 0x0001d485, 0x00001aa3,
+ 0x0001d486, 0x00001aa4,
+ 0x0001d487, 0x00001aa5,
+ 0x0001d488, 0x00001aa6,
+ 0x0001d489, 0x00001aa7,
+ 0x0001d48a, 0x00001aa8,
+ 0x0001d48b, 0x00001aa9,
+ 0x0001d48c, 0x00001aaa,
+ 0x0001d48d, 0x00001aab,
+ 0x0001d48e, 0x00001aac,
+ 0x0001d48f, 0x00001aad,
+ 0x0001d490, 0x00001aae,
+ 0x0001d491, 0x00001aaf,
+ 0x0001d492, 0x00001ab0,
+ 0x0001d493, 0x00001ab1,
+ 0x0001d494, 0x00001ab2,
+ 0x0001d495, 0x00001ab3,
+ 0x0001d496, 0x00001ab4,
+ 0x0001d497, 0x00001ab5,
+ 0x0001d498, 0x00001ab6,
+ 0x0001d499, 0x00001ab7,
+ 0x0001d49a, 0x00001ab8,
+ 0x0001d49b, 0x00001ab9,
+ 0x0001d49c, 0x00001aba,
+ 0x0001d49e, 0x00001abb,
+ 0x0001d49f, 0x00001abc,
+ 0x0001d4a2, 0x00001abd,
+ 0x0001d4a5, 0x00001abe,
+ 0x0001d4a6, 0x00001abf,
+ 0x0001d4a9, 0x00001ac0,
+ 0x0001d4aa, 0x00001ac1,
+ 0x0001d4ab, 0x00001ac2,
+ 0x0001d4ac, 0x00001ac3,
+ 0x0001d4ae, 0x00001ac4,
+ 0x0001d4af, 0x00001ac5,
+ 0x0001d4b0, 0x00001ac6,
+ 0x0001d4b1, 0x00001ac7,
+ 0x0001d4b2, 0x00001ac8,
+ 0x0001d4b3, 0x00001ac9,
+ 0x0001d4b4, 0x00001aca,
+ 0x0001d4b5, 0x00001acb,
+ 0x0001d4b6, 0x00001acc,
+ 0x0001d4b7, 0x00001acd,
+ 0x0001d4b8, 0x00001ace,
+ 0x0001d4b9, 0x00001acf,
+ 0x0001d4bb, 0x00001ad0,
+ 0x0001d4bd, 0x00001ad1,
+ 0x0001d4be, 0x00001ad2,
+ 0x0001d4bf, 0x00001ad3,
+ 0x0001d4c0, 0x00001ad4,
+ 0x0001d4c2, 0x00001ad5,
+ 0x0001d4c3, 0x00001ad6,
+ 0x0001d4c5, 0x00001ad7,
+ 0x0001d4c6, 0x00001ad8,
+ 0x0001d4c7, 0x00001ad9,
+ 0x0001d4c8, 0x00001ada,
+ 0x0001d4c9, 0x00001adb,
+ 0x0001d4ca, 0x00001adc,
+ 0x0001d4cb, 0x00001add,
+ 0x0001d4cc, 0x00001ade,
+ 0x0001d4cd, 0x00001adf,
+ 0x0001d4ce, 0x00001ae0,
+ 0x0001d4cf, 0x00001ae1,
+ 0x0001d4d0, 0x00001ae2,
+ 0x0001d4d1, 0x00001ae3,
+ 0x0001d4d2, 0x00001ae4,
+ 0x0001d4d3, 0x00001ae5,
+ 0x0001d4d4, 0x00001ae6,
+ 0x0001d4d5, 0x00001ae7,
+ 0x0001d4d6, 0x00001ae8,
+ 0x0001d4d7, 0x00001ae9,
+ 0x0001d4d8, 0x00001aea,
+ 0x0001d4d9, 0x00001aeb,
+ 0x0001d4da, 0x00001aec,
+ 0x0001d4db, 0x00001aed,
+ 0x0001d4dc, 0x00001aee,
+ 0x0001d4dd, 0x00001aef,
+ 0x0001d4de, 0x00001af0,
+ 0x0001d4df, 0x00001af1,
+ 0x0001d4e0, 0x00001af2,
+ 0x0001d4e1, 0x00001af3,
+ 0x0001d4e2, 0x00001af4,
+ 0x0001d4e3, 0x00001af5,
+ 0x0001d4e4, 0x00001af6,
+ 0x0001d4e5, 0x00001af7,
+ 0x0001d4e6, 0x00001af8,
+ 0x0001d4e7, 0x00001af9,
+ 0x0001d4e8, 0x00001afa,
+ 0x0001d4e9, 0x00001afb,
+ 0x0001d4ea, 0x00001afc,
+ 0x0001d4eb, 0x00001afd,
+ 0x0001d4ec, 0x00001afe,
+ 0x0001d4ed, 0x00001aff,
+ 0x0001d4ee, 0x00001b00,
+ 0x0001d4ef, 0x00001b01,
+ 0x0001d4f0, 0x00001b02,
+ 0x0001d4f1, 0x00001b03,
+ 0x0001d4f2, 0x00001b04,
+ 0x0001d4f3, 0x00001b05,
+ 0x0001d4f4, 0x00001b06,
+ 0x0001d4f5, 0x00001b07,
+ 0x0001d4f6, 0x00001b08,
+ 0x0001d4f7, 0x00001b09,
+ 0x0001d4f8, 0x00001b0a,
+ 0x0001d4f9, 0x00001b0b,
+ 0x0001d4fa, 0x00001b0c,
+ 0x0001d4fb, 0x00001b0d,
+ 0x0001d4fc, 0x00001b0e,
+ 0x0001d4fd, 0x00001b0f,
+ 0x0001d4fe, 0x00001b10,
+ 0x0001d4ff, 0x00001b11,
+ 0x0001d500, 0x00001b12,
+ 0x0001d501, 0x00001b13,
+ 0x0001d502, 0x00001b14,
+ 0x0001d503, 0x00001b15,
+ 0x0001d504, 0x00001b16,
+ 0x0001d505, 0x00001b17,
+ 0x0001d507, 0x00001b18,
+ 0x0001d508, 0x00001b19,
+ 0x0001d509, 0x00001b1a,
+ 0x0001d50a, 0x00001b1b,
+ 0x0001d50d, 0x00001b1c,
+ 0x0001d50e, 0x00001b1d,
+ 0x0001d50f, 0x00001b1e,
+ 0x0001d510, 0x00001b1f,
+ 0x0001d511, 0x00001b20,
+ 0x0001d512, 0x00001b21,
+ 0x0001d513, 0x00001b22,
+ 0x0001d514, 0x00001b23,
+ 0x0001d516, 0x00001b24,
+ 0x0001d517, 0x00001b25,
+ 0x0001d518, 0x00001b26,
+ 0x0001d519, 0x00001b27,
+ 0x0001d51a, 0x00001b28,
+ 0x0001d51b, 0x00001b29,
+ 0x0001d51c, 0x00001b2a,
+ 0x0001d51e, 0x00001b2b,
+ 0x0001d51f, 0x00001b2c,
+ 0x0001d520, 0x00001b2d,
+ 0x0001d521, 0x00001b2e,
+ 0x0001d522, 0x00001b2f,
+ 0x0001d523, 0x00001b30,
+ 0x0001d524, 0x00001b31,
+ 0x0001d525, 0x00001b32,
+ 0x0001d526, 0x00001b33,
+ 0x0001d527, 0x00001b34,
+ 0x0001d528, 0x00001b35,
+ 0x0001d529, 0x00001b36,
+ 0x0001d52a, 0x00001b37,
+ 0x0001d52b, 0x00001b38,
+ 0x0001d52c, 0x00001b39,
+ 0x0001d52d, 0x00001b3a,
+ 0x0001d52e, 0x00001b3b,
+ 0x0001d52f, 0x00001b3c,
+ 0x0001d530, 0x00001b3d,
+ 0x0001d531, 0x00001b3e,
+ 0x0001d532, 0x00001b3f,
+ 0x0001d533, 0x00001b40,
+ 0x0001d534, 0x00001b41,
+ 0x0001d535, 0x00001b42,
+ 0x0001d536, 0x00001b43,
+ 0x0001d537, 0x00001b44,
+ 0x0001d538, 0x00001b45,
+ 0x0001d539, 0x00001b46,
+ 0x0001d53b, 0x00001b47,
+ 0x0001d53c, 0x00001b48,
+ 0x0001d53d, 0x00001b49,
+ 0x0001d53e, 0x00001b4a,
+ 0x0001d540, 0x00001b4b,
+ 0x0001d541, 0x00001b4c,
+ 0x0001d542, 0x00001b4d,
+ 0x0001d543, 0x00001b4e,
+ 0x0001d544, 0x00001b4f,
+ 0x0001d546, 0x00001b50,
+ 0x0001d54a, 0x00001b51,
+ 0x0001d54b, 0x00001b52,
+ 0x0001d54c, 0x00001b53,
+ 0x0001d54d, 0x00001b54,
+ 0x0001d54e, 0x00001b55,
+ 0x0001d54f, 0x00001b56,
+ 0x0001d550, 0x00001b57,
+ 0x0001d552, 0x00001b58,
+ 0x0001d553, 0x00001b59,
+ 0x0001d554, 0x00001b5a,
+ 0x0001d555, 0x00001b5b,
+ 0x0001d556, 0x00001b5c,
+ 0x0001d557, 0x00001b5d,
+ 0x0001d558, 0x00001b5e,
+ 0x0001d559, 0x00001b5f,
+ 0x0001d55a, 0x00001b60,
+ 0x0001d55b, 0x00001b61,
+ 0x0001d55c, 0x00001b62,
+ 0x0001d55d, 0x00001b63,
+ 0x0001d55e, 0x00001b64,
+ 0x0001d55f, 0x00001b65,
+ 0x0001d560, 0x00001b66,
+ 0x0001d561, 0x00001b67,
+ 0x0001d562, 0x00001b68,
+ 0x0001d563, 0x00001b69,
+ 0x0001d564, 0x00001b6a,
+ 0x0001d565, 0x00001b6b,
+ 0x0001d566, 0x00001b6c,
+ 0x0001d567, 0x00001b6d,
+ 0x0001d568, 0x00001b6e,
+ 0x0001d569, 0x00001b6f,
+ 0x0001d56a, 0x00001b70,
+ 0x0001d56b, 0x00001b71,
+ 0x0001d56c, 0x00001b72,
+ 0x0001d56d, 0x00001b73,
+ 0x0001d56e, 0x00001b74,
+ 0x0001d56f, 0x00001b75,
+ 0x0001d570, 0x00001b76,
+ 0x0001d571, 0x00001b77,
+ 0x0001d572, 0x00001b78,
+ 0x0001d573, 0x00001b79,
+ 0x0001d574, 0x00001b7a,
+ 0x0001d575, 0x00001b7b,
+ 0x0001d576, 0x00001b7c,
+ 0x0001d577, 0x00001b7d,
+ 0x0001d578, 0x00001b7e,
+ 0x0001d579, 0x00001b7f,
+ 0x0001d57a, 0x00001b80,
+ 0x0001d57b, 0x00001b81,
+ 0x0001d57c, 0x00001b82,
+ 0x0001d57d, 0x00001b83,
+ 0x0001d57e, 0x00001b84,
+ 0x0001d57f, 0x00001b85,
+ 0x0001d580, 0x00001b86,
+ 0x0001d581, 0x00001b87,
+ 0x0001d582, 0x00001b88,
+ 0x0001d583, 0x00001b89,
+ 0x0001d584, 0x00001b8a,
+ 0x0001d585, 0x00001b8b,
+ 0x0001d586, 0x00001b8c,
+ 0x0001d587, 0x00001b8d,
+ 0x0001d588, 0x00001b8e,
+ 0x0001d589, 0x00001b8f,
+ 0x0001d58a, 0x00001b90,
+ 0x0001d58b, 0x00001b91,
+ 0x0001d58c, 0x00001b92,
+ 0x0001d58d, 0x00001b93,
+ 0x0001d58e, 0x00001b94,
+ 0x0001d58f, 0x00001b95,
+ 0x0001d590, 0x00001b96,
+ 0x0001d591, 0x00001b97,
+ 0x0001d592, 0x00001b98,
+ 0x0001d593, 0x00001b99,
+ 0x0001d594, 0x00001b9a,
+ 0x0001d595, 0x00001b9b,
+ 0x0001d596, 0x00001b9c,
+ 0x0001d597, 0x00001b9d,
+ 0x0001d598, 0x00001b9e,
+ 0x0001d599, 0x00001b9f,
+ 0x0001d59a, 0x00001ba0,
+ 0x0001d59b, 0x00001ba1,
+ 0x0001d59c, 0x00001ba2,
+ 0x0001d59d, 0x00001ba3,
+ 0x0001d59e, 0x00001ba4,
+ 0x0001d59f, 0x00001ba5,
+ 0x0001d5a0, 0x00001ba6,
+ 0x0001d5a1, 0x00001ba7,
+ 0x0001d5a2, 0x00001ba8,
+ 0x0001d5a3, 0x00001ba9,
+ 0x0001d5a4, 0x00001baa,
+ 0x0001d5a5, 0x00001bab,
+ 0x0001d5a6, 0x00001bac,
+ 0x0001d5a7, 0x00001bad,
+ 0x0001d5a8, 0x00001bae,
+ 0x0001d5a9, 0x00001baf,
+ 0x0001d5aa, 0x00001bb0,
+ 0x0001d5ab, 0x00001bb1,
+ 0x0001d5ac, 0x00001bb2,
+ 0x0001d5ad, 0x00001bb3,
+ 0x0001d5ae, 0x00001bb4,
+ 0x0001d5af, 0x00001bb5,
+ 0x0001d5b0, 0x00001bb6,
+ 0x0001d5b1, 0x00001bb7,
+ 0x0001d5b2, 0x00001bb8,
+ 0x0001d5b3, 0x00001bb9,
+ 0x0001d5b4, 0x00001bba,
+ 0x0001d5b5, 0x00001bbb,
+ 0x0001d5b6, 0x00001bbc,
+ 0x0001d5b7, 0x00001bbd,
+ 0x0001d5b8, 0x00001bbe,
+ 0x0001d5b9, 0x00001bbf,
+ 0x0001d5ba, 0x00001bc0,
+ 0x0001d5bb, 0x00001bc1,
+ 0x0001d5bc, 0x00001bc2,
+ 0x0001d5bd, 0x00001bc3,
+ 0x0001d5be, 0x00001bc4,
+ 0x0001d5bf, 0x00001bc5,
+ 0x0001d5c0, 0x00001bc6,
+ 0x0001d5c1, 0x00001bc7,
+ 0x0001d5c2, 0x00001bc8,
+ 0x0001d5c3, 0x00001bc9,
+ 0x0001d5c4, 0x00001bca,
+ 0x0001d5c5, 0x00001bcb,
+ 0x0001d5c6, 0x00001bcc,
+ 0x0001d5c7, 0x00001bcd,
+ 0x0001d5c8, 0x00001bce,
+ 0x0001d5c9, 0x00001bcf,
+ 0x0001d5ca, 0x00001bd0,
+ 0x0001d5cb, 0x00001bd1,
+ 0x0001d5cc, 0x00001bd2,
+ 0x0001d5cd, 0x00001bd3,
+ 0x0001d5ce, 0x00001bd4,
+ 0x0001d5cf, 0x00001bd5,
+ 0x0001d5d0, 0x00001bd6,
+ 0x0001d5d1, 0x00001bd7,
+ 0x0001d5d2, 0x00001bd8,
+ 0x0001d5d3, 0x00001bd9,
+ 0x0001d5d4, 0x00001bda,
+ 0x0001d5d5, 0x00001bdb,
+ 0x0001d5d6, 0x00001bdc,
+ 0x0001d5d7, 0x00001bdd,
+ 0x0001d5d8, 0x00001bde,
+ 0x0001d5d9, 0x00001bdf,
+ 0x0001d5da, 0x00001be0,
+ 0x0001d5db, 0x00001be1,
+ 0x0001d5dc, 0x00001be2,
+ 0x0001d5dd, 0x00001be3,
+ 0x0001d5de, 0x00001be4,
+ 0x0001d5df, 0x00001be5,
+ 0x0001d5e0, 0x00001be6,
+ 0x0001d5e1, 0x00001be7,
+ 0x0001d5e2, 0x00001be8,
+ 0x0001d5e3, 0x00001be9,
+ 0x0001d5e4, 0x00001bea,
+ 0x0001d5e5, 0x00001beb,
+ 0x0001d5e6, 0x00001bec,
+ 0x0001d5e7, 0x00001bed,
+ 0x0001d5e8, 0x00001bee,
+ 0x0001d5e9, 0x00001bef,
+ 0x0001d5ea, 0x00001bf0,
+ 0x0001d5eb, 0x00001bf1,
+ 0x0001d5ec, 0x00001bf2,
+ 0x0001d5ed, 0x00001bf3,
+ 0x0001d5ee, 0x00001bf4,
+ 0x0001d5ef, 0x00001bf5,
+ 0x0001d5f0, 0x00001bf6,
+ 0x0001d5f1, 0x00001bf7,
+ 0x0001d5f2, 0x00001bf8,
+ 0x0001d5f3, 0x00001bf9,
+ 0x0001d5f4, 0x00001bfa,
+ 0x0001d5f5, 0x00001bfb,
+ 0x0001d5f6, 0x00001bfc,
+ 0x0001d5f7, 0x00001bfd,
+ 0x0001d5f8, 0x00001bfe,
+ 0x0001d5f9, 0x00001bff,
+ 0x0001d5fa, 0x00001c00,
+ 0x0001d5fb, 0x00001c01,
+ 0x0001d5fc, 0x00001c02,
+ 0x0001d5fd, 0x00001c03,
+ 0x0001d5fe, 0x00001c04,
+ 0x0001d5ff, 0x00001c05,
+ 0x0001d600, 0x00001c06,
+ 0x0001d601, 0x00001c07,
+ 0x0001d602, 0x00001c08,
+ 0x0001d603, 0x00001c09,
+ 0x0001d604, 0x00001c0a,
+ 0x0001d605, 0x00001c0b,
+ 0x0001d606, 0x00001c0c,
+ 0x0001d607, 0x00001c0d,
+ 0x0001d608, 0x00001c0e,
+ 0x0001d609, 0x00001c0f,
+ 0x0001d60a, 0x00001c10,
+ 0x0001d60b, 0x00001c11,
+ 0x0001d60c, 0x00001c12,
+ 0x0001d60d, 0x00001c13,
+ 0x0001d60e, 0x00001c14,
+ 0x0001d60f, 0x00001c15,
+ 0x0001d610, 0x00001c16,
+ 0x0001d611, 0x00001c17,
+ 0x0001d612, 0x00001c18,
+ 0x0001d613, 0x00001c19,
+ 0x0001d614, 0x00001c1a,
+ 0x0001d615, 0x00001c1b,
+ 0x0001d616, 0x00001c1c,
+ 0x0001d617, 0x00001c1d,
+ 0x0001d618, 0x00001c1e,
+ 0x0001d619, 0x00001c1f,
+ 0x0001d61a, 0x00001c20,
+ 0x0001d61b, 0x00001c21,
+ 0x0001d61c, 0x00001c22,
+ 0x0001d61d, 0x00001c23,
+ 0x0001d61e, 0x00001c24,
+ 0x0001d61f, 0x00001c25,
+ 0x0001d620, 0x00001c26,
+ 0x0001d621, 0x00001c27,
+ 0x0001d622, 0x00001c28,
+ 0x0001d623, 0x00001c29,
+ 0x0001d624, 0x00001c2a,
+ 0x0001d625, 0x00001c2b,
+ 0x0001d626, 0x00001c2c,
+ 0x0001d627, 0x00001c2d,
+ 0x0001d628, 0x00001c2e,
+ 0x0001d629, 0x00001c2f,
+ 0x0001d62a, 0x00001c30,
+ 0x0001d62b, 0x00001c31,
+ 0x0001d62c, 0x00001c32,
+ 0x0001d62d, 0x00001c33,
+ 0x0001d62e, 0x00001c34,
+ 0x0001d62f, 0x00001c35,
+ 0x0001d630, 0x00001c36,
+ 0x0001d631, 0x00001c37,
+ 0x0001d632, 0x00001c38,
+ 0x0001d633, 0x00001c39,
+ 0x0001d634, 0x00001c3a,
+ 0x0001d635, 0x00001c3b,
+ 0x0001d636, 0x00001c3c,
+ 0x0001d637, 0x00001c3d,
+ 0x0001d638, 0x00001c3e,
+ 0x0001d639, 0x00001c3f,
+ 0x0001d63a, 0x00001c40,
+ 0x0001d63b, 0x00001c41,
+ 0x0001d63c, 0x00001c42,
+ 0x0001d63d, 0x00001c43,
+ 0x0001d63e, 0x00001c44,
+ 0x0001d63f, 0x00001c45,
+ 0x0001d640, 0x00001c46,
+ 0x0001d641, 0x00001c47,
+ 0x0001d642, 0x00001c48,
+ 0x0001d643, 0x00001c49,
+ 0x0001d644, 0x00001c4a,
+ 0x0001d645, 0x00001c4b,
+ 0x0001d646, 0x00001c4c,
+ 0x0001d647, 0x00001c4d,
+ 0x0001d648, 0x00001c4e,
+ 0x0001d649, 0x00001c4f,
+ 0x0001d64a, 0x00001c50,
+ 0x0001d64b, 0x00001c51,
+ 0x0001d64c, 0x00001c52,
+ 0x0001d64d, 0x00001c53,
+ 0x0001d64e, 0x00001c54,
+ 0x0001d64f, 0x00001c55,
+ 0x0001d650, 0x00001c56,
+ 0x0001d651, 0x00001c57,
+ 0x0001d652, 0x00001c58,
+ 0x0001d653, 0x00001c59,
+ 0x0001d654, 0x00001c5a,
+ 0x0001d655, 0x00001c5b,
+ 0x0001d656, 0x00001c5c,
+ 0x0001d657, 0x00001c5d,
+ 0x0001d658, 0x00001c5e,
+ 0x0001d659, 0x00001c5f,
+ 0x0001d65a, 0x00001c60,
+ 0x0001d65b, 0x00001c61,
+ 0x0001d65c, 0x00001c62,
+ 0x0001d65d, 0x00001c63,
+ 0x0001d65e, 0x00001c64,
+ 0x0001d65f, 0x00001c65,
+ 0x0001d660, 0x00001c66,
+ 0x0001d661, 0x00001c67,
+ 0x0001d662, 0x00001c68,
+ 0x0001d663, 0x00001c69,
+ 0x0001d664, 0x00001c6a,
+ 0x0001d665, 0x00001c6b,
+ 0x0001d666, 0x00001c6c,
+ 0x0001d667, 0x00001c6d,
+ 0x0001d668, 0x00001c6e,
+ 0x0001d669, 0x00001c6f,
+ 0x0001d66a, 0x00001c70,
+ 0x0001d66b, 0x00001c71,
+ 0x0001d66c, 0x00001c72,
+ 0x0001d66d, 0x00001c73,
+ 0x0001d66e, 0x00001c74,
+ 0x0001d66f, 0x00001c75,
+ 0x0001d670, 0x00001c76,
+ 0x0001d671, 0x00001c77,
+ 0x0001d672, 0x00001c78,
+ 0x0001d673, 0x00001c79,
+ 0x0001d674, 0x00001c7a,
+ 0x0001d675, 0x00001c7b,
+ 0x0001d676, 0x00001c7c,
+ 0x0001d677, 0x00001c7d,
+ 0x0001d678, 0x00001c7e,
+ 0x0001d679, 0x00001c7f,
+ 0x0001d67a, 0x00001c80,
+ 0x0001d67b, 0x00001c81,
+ 0x0001d67c, 0x00001c82,
+ 0x0001d67d, 0x00001c83,
+ 0x0001d67e, 0x00001c84,
+ 0x0001d67f, 0x00001c85,
+ 0x0001d680, 0x00001c86,
+ 0x0001d681, 0x00001c87,
+ 0x0001d682, 0x00001c88,
+ 0x0001d683, 0x00001c89,
+ 0x0001d684, 0x00001c8a,
+ 0x0001d685, 0x00001c8b,
+ 0x0001d686, 0x00001c8c,
+ 0x0001d687, 0x00001c8d,
+ 0x0001d688, 0x00001c8e,
+ 0x0001d689, 0x00001c8f,
+ 0x0001d68a, 0x00001c90,
+ 0x0001d68b, 0x00001c91,
+ 0x0001d68c, 0x00001c92,
+ 0x0001d68d, 0x00001c93,
+ 0x0001d68e, 0x00001c94,
+ 0x0001d68f, 0x00001c95,
+ 0x0001d690, 0x00001c96,
+ 0x0001d691, 0x00001c97,
+ 0x0001d692, 0x00001c98,
+ 0x0001d693, 0x00001c99,
+ 0x0001d694, 0x00001c9a,
+ 0x0001d695, 0x00001c9b,
+ 0x0001d696, 0x00001c9c,
+ 0x0001d697, 0x00001c9d,
+ 0x0001d698, 0x00001c9e,
+ 0x0001d699, 0x00001c9f,
+ 0x0001d69a, 0x00001ca0,
+ 0x0001d69b, 0x00001ca1,
+ 0x0001d69c, 0x00001ca2,
+ 0x0001d69d, 0x00001ca3,
+ 0x0001d69e, 0x00001ca4,
+ 0x0001d69f, 0x00001ca5,
+ 0x0001d6a0, 0x00001ca6,
+ 0x0001d6a1, 0x00001ca7,
+ 0x0001d6a2, 0x00001ca8,
+ 0x0001d6a3, 0x00001ca9,
+ 0x0001d6a8, 0x00001caa,
+ 0x0001d6a9, 0x00001cab,
+ 0x0001d6aa, 0x00001cac,
+ 0x0001d6ab, 0x00001cad,
+ 0x0001d6ac, 0x00001cae,
+ 0x0001d6ad, 0x00001caf,
+ 0x0001d6ae, 0x00001cb0,
+ 0x0001d6af, 0x00001cb1,
+ 0x0001d6b0, 0x00001cb2,
+ 0x0001d6b1, 0x00001cb3,
+ 0x0001d6b2, 0x00001cb4,
+ 0x0001d6b3, 0x00001cb5,
+ 0x0001d6b4, 0x00001cb6,
+ 0x0001d6b5, 0x00001cb7,
+ 0x0001d6b6, 0x00001cb8,
+ 0x0001d6b7, 0x00001cb9,
+ 0x0001d6b8, 0x00001cba,
+ 0x0001d6b9, 0x00001cbb,
+ 0x0001d6ba, 0x00001cbc,
+ 0x0001d6bb, 0x00001cbd,
+ 0x0001d6bc, 0x00001cbe,
+ 0x0001d6bd, 0x00001cbf,
+ 0x0001d6be, 0x00001cc0,
+ 0x0001d6bf, 0x00001cc1,
+ 0x0001d6c0, 0x00001cc2,
+ 0x0001d6c1, 0x00001cc3,
+ 0x0001d6c2, 0x00001cc4,
+ 0x0001d6c3, 0x00001cc5,
+ 0x0001d6c4, 0x00001cc6,
+ 0x0001d6c5, 0x00001cc7,
+ 0x0001d6c6, 0x00001cc8,
+ 0x0001d6c7, 0x00001cc9,
+ 0x0001d6c8, 0x00001cca,
+ 0x0001d6c9, 0x00001ccb,
+ 0x0001d6ca, 0x00001ccc,
+ 0x0001d6cb, 0x00001ccd,
+ 0x0001d6cc, 0x00001cce,
+ 0x0001d6cd, 0x00001ccf,
+ 0x0001d6ce, 0x00001cd0,
+ 0x0001d6cf, 0x00001cd1,
+ 0x0001d6d0, 0x00001cd2,
+ 0x0001d6d1, 0x00001cd3,
+ 0x0001d6d2, 0x00001cd4,
+ 0x0001d6d3, 0x00001cd5,
+ 0x0001d6d4, 0x00001cd6,
+ 0x0001d6d5, 0x00001cd7,
+ 0x0001d6d6, 0x00001cd8,
+ 0x0001d6d7, 0x00001cd9,
+ 0x0001d6d8, 0x00001cda,
+ 0x0001d6d9, 0x00001cdb,
+ 0x0001d6da, 0x00001cdc,
+ 0x0001d6db, 0x00001cdd,
+ 0x0001d6dc, 0x00001cde,
+ 0x0001d6dd, 0x00001cdf,
+ 0x0001d6de, 0x00001ce0,
+ 0x0001d6df, 0x00001ce1,
+ 0x0001d6e0, 0x00001ce2,
+ 0x0001d6e1, 0x00001ce3,
+ 0x0001d6e2, 0x00001ce4,
+ 0x0001d6e3, 0x00001ce5,
+ 0x0001d6e4, 0x00001ce6,
+ 0x0001d6e5, 0x00001ce7,
+ 0x0001d6e6, 0x00001ce8,
+ 0x0001d6e7, 0x00001ce9,
+ 0x0001d6e8, 0x00001cea,
+ 0x0001d6e9, 0x00001ceb,
+ 0x0001d6ea, 0x00001cec,
+ 0x0001d6eb, 0x00001ced,
+ 0x0001d6ec, 0x00001cee,
+ 0x0001d6ed, 0x00001cef,
+ 0x0001d6ee, 0x00001cf0,
+ 0x0001d6ef, 0x00001cf1,
+ 0x0001d6f0, 0x00001cf2,
+ 0x0001d6f1, 0x00001cf3,
+ 0x0001d6f2, 0x00001cf4,
+ 0x0001d6f3, 0x00001cf5,
+ 0x0001d6f4, 0x00001cf6,
+ 0x0001d6f5, 0x00001cf7,
+ 0x0001d6f6, 0x00001cf8,
+ 0x0001d6f7, 0x00001cf9,
+ 0x0001d6f8, 0x00001cfa,
+ 0x0001d6f9, 0x00001cfb,
+ 0x0001d6fa, 0x00001cfc,
+ 0x0001d6fb, 0x00001cfd,
+ 0x0001d6fc, 0x00001cfe,
+ 0x0001d6fd, 0x00001cff,
+ 0x0001d6fe, 0x00001d00,
+ 0x0001d6ff, 0x00001d01,
+ 0x0001d700, 0x00001d02,
+ 0x0001d701, 0x00001d03,
+ 0x0001d702, 0x00001d04,
+ 0x0001d703, 0x00001d05,
+ 0x0001d704, 0x00001d06,
+ 0x0001d705, 0x00001d07,
+ 0x0001d706, 0x00001d08,
+ 0x0001d707, 0x00001d09,
+ 0x0001d708, 0x00001d0a,
+ 0x0001d709, 0x00001d0b,
+ 0x0001d70a, 0x00001d0c,
+ 0x0001d70b, 0x00001d0d,
+ 0x0001d70c, 0x00001d0e,
+ 0x0001d70d, 0x00001d0f,
+ 0x0001d70e, 0x00001d10,
+ 0x0001d70f, 0x00001d11,
+ 0x0001d710, 0x00001d12,
+ 0x0001d711, 0x00001d13,
+ 0x0001d712, 0x00001d14,
+ 0x0001d713, 0x00001d15,
+ 0x0001d714, 0x00001d16,
+ 0x0001d715, 0x00001d17,
+ 0x0001d716, 0x00001d18,
+ 0x0001d717, 0x00001d19,
+ 0x0001d718, 0x00001d1a,
+ 0x0001d719, 0x00001d1b,
+ 0x0001d71a, 0x00001d1c,
+ 0x0001d71b, 0x00001d1d,
+ 0x0001d71c, 0x00001d1e,
+ 0x0001d71d, 0x00001d1f,
+ 0x0001d71e, 0x00001d20,
+ 0x0001d71f, 0x00001d21,
+ 0x0001d720, 0x00001d22,
+ 0x0001d721, 0x00001d23,
+ 0x0001d722, 0x00001d24,
+ 0x0001d723, 0x00001d25,
+ 0x0001d724, 0x00001d26,
+ 0x0001d725, 0x00001d27,
+ 0x0001d726, 0x00001d28,
+ 0x0001d727, 0x00001d29,
+ 0x0001d728, 0x00001d2a,
+ 0x0001d729, 0x00001d2b,
+ 0x0001d72a, 0x00001d2c,
+ 0x0001d72b, 0x00001d2d,
+ 0x0001d72c, 0x00001d2e,
+ 0x0001d72d, 0x00001d2f,
+ 0x0001d72e, 0x00001d30,
+ 0x0001d72f, 0x00001d31,
+ 0x0001d730, 0x00001d32,
+ 0x0001d731, 0x00001d33,
+ 0x0001d732, 0x00001d34,
+ 0x0001d733, 0x00001d35,
+ 0x0001d734, 0x00001d36,
+ 0x0001d735, 0x00001d37,
+ 0x0001d736, 0x00001d38,
+ 0x0001d737, 0x00001d39,
+ 0x0001d738, 0x00001d3a,
+ 0x0001d739, 0x00001d3b,
+ 0x0001d73a, 0x00001d3c,
+ 0x0001d73b, 0x00001d3d,
+ 0x0001d73c, 0x00001d3e,
+ 0x0001d73d, 0x00001d3f,
+ 0x0001d73e, 0x00001d40,
+ 0x0001d73f, 0x00001d41,
+ 0x0001d740, 0x00001d42,
+ 0x0001d741, 0x00001d43,
+ 0x0001d742, 0x00001d44,
+ 0x0001d743, 0x00001d45,
+ 0x0001d744, 0x00001d46,
+ 0x0001d745, 0x00001d47,
+ 0x0001d746, 0x00001d48,
+ 0x0001d747, 0x00001d49,
+ 0x0001d748, 0x00001d4a,
+ 0x0001d749, 0x00001d4b,
+ 0x0001d74a, 0x00001d4c,
+ 0x0001d74b, 0x00001d4d,
+ 0x0001d74c, 0x00001d4e,
+ 0x0001d74d, 0x00001d4f,
+ 0x0001d74e, 0x00001d50,
+ 0x0001d74f, 0x00001d51,
+ 0x0001d750, 0x00001d52,
+ 0x0001d751, 0x00001d53,
+ 0x0001d752, 0x00001d54,
+ 0x0001d753, 0x00001d55,
+ 0x0001d754, 0x00001d56,
+ 0x0001d755, 0x00001d57,
+ 0x0001d756, 0x00001d58,
+ 0x0001d757, 0x00001d59,
+ 0x0001d758, 0x00001d5a,
+ 0x0001d759, 0x00001d5b,
+ 0x0001d75a, 0x00001d5c,
+ 0x0001d75b, 0x00001d5d,
+ 0x0001d75c, 0x00001d5e,
+ 0x0001d75d, 0x00001d5f,
+ 0x0001d75e, 0x00001d60,
+ 0x0001d75f, 0x00001d61,
+ 0x0001d760, 0x00001d62,
+ 0x0001d761, 0x00001d63,
+ 0x0001d762, 0x00001d64,
+ 0x0001d763, 0x00001d65,
+ 0x0001d764, 0x00001d66,
+ 0x0001d765, 0x00001d67,
+ 0x0001d766, 0x00001d68,
+ 0x0001d767, 0x00001d69,
+ 0x0001d768, 0x00001d6a,
+ 0x0001d769, 0x00001d6b,
+ 0x0001d76a, 0x00001d6c,
+ 0x0001d76b, 0x00001d6d,
+ 0x0001d76c, 0x00001d6e,
+ 0x0001d76d, 0x00001d6f,
+ 0x0001d76e, 0x00001d70,
+ 0x0001d76f, 0x00001d71,
+ 0x0001d770, 0x00001d72,
+ 0x0001d771, 0x00001d73,
+ 0x0001d772, 0x00001d74,
+ 0x0001d773, 0x00001d75,
+ 0x0001d774, 0x00001d76,
+ 0x0001d775, 0x00001d77,
+ 0x0001d776, 0x00001d78,
+ 0x0001d777, 0x00001d79,
+ 0x0001d778, 0x00001d7a,
+ 0x0001d779, 0x00001d7b,
+ 0x0001d77a, 0x00001d7c,
+ 0x0001d77b, 0x00001d7d,
+ 0x0001d77c, 0x00001d7e,
+ 0x0001d77d, 0x00001d7f,
+ 0x0001d77e, 0x00001d80,
+ 0x0001d77f, 0x00001d81,
+ 0x0001d780, 0x00001d82,
+ 0x0001d781, 0x00001d83,
+ 0x0001d782, 0x00001d84,
+ 0x0001d783, 0x00001d85,
+ 0x0001d784, 0x00001d86,
+ 0x0001d785, 0x00001d87,
+ 0x0001d786, 0x00001d88,
+ 0x0001d787, 0x00001d89,
+ 0x0001d788, 0x00001d8a,
+ 0x0001d789, 0x00001d8b,
+ 0x0001d78a, 0x00001d8c,
+ 0x0001d78b, 0x00001d8d,
+ 0x0001d78c, 0x00001d8e,
+ 0x0001d78d, 0x00001d8f,
+ 0x0001d78e, 0x00001d90,
+ 0x0001d78f, 0x00001d91,
+ 0x0001d790, 0x00001d92,
+ 0x0001d791, 0x00001d93,
+ 0x0001d792, 0x00001d94,
+ 0x0001d793, 0x00001d95,
+ 0x0001d794, 0x00001d96,
+ 0x0001d795, 0x00001d97,
+ 0x0001d796, 0x00001d98,
+ 0x0001d797, 0x00001d99,
+ 0x0001d798, 0x00001d9a,
+ 0x0001d799, 0x00001d9b,
+ 0x0001d79a, 0x00001d9c,
+ 0x0001d79b, 0x00001d9d,
+ 0x0001d79c, 0x00001d9e,
+ 0x0001d79d, 0x00001d9f,
+ 0x0001d79e, 0x00001da0,
+ 0x0001d79f, 0x00001da1,
+ 0x0001d7a0, 0x00001da2,
+ 0x0001d7a1, 0x00001da3,
+ 0x0001d7a2, 0x00001da4,
+ 0x0001d7a3, 0x00001da5,
+ 0x0001d7a4, 0x00001da6,
+ 0x0001d7a5, 0x00001da7,
+ 0x0001d7a6, 0x00001da8,
+ 0x0001d7a7, 0x00001da9,
+ 0x0001d7a8, 0x00001daa,
+ 0x0001d7a9, 0x00001dab,
+ 0x0001d7aa, 0x00001dac,
+ 0x0001d7ab, 0x00001dad,
+ 0x0001d7ac, 0x00001dae,
+ 0x0001d7ad, 0x00001daf,
+ 0x0001d7ae, 0x00001db0,
+ 0x0001d7af, 0x00001db1,
+ 0x0001d7b0, 0x00001db2,
+ 0x0001d7b1, 0x00001db3,
+ 0x0001d7b2, 0x00001db4,
+ 0x0001d7b3, 0x00001db5,
+ 0x0001d7b4, 0x00001db6,
+ 0x0001d7b5, 0x00001db7,
+ 0x0001d7b6, 0x00001db8,
+ 0x0001d7b7, 0x00001db9,
+ 0x0001d7b8, 0x00001dba,
+ 0x0001d7b9, 0x00001dbb,
+ 0x0001d7ba, 0x00001dbc,
+ 0x0001d7bb, 0x00001dbd,
+ 0x0001d7bc, 0x00001dbe,
+ 0x0001d7bd, 0x00001dbf,
+ 0x0001d7be, 0x00001dc0,
+ 0x0001d7bf, 0x00001dc1,
+ 0x0001d7c0, 0x00001dc2,
+ 0x0001d7c1, 0x00001dc3,
+ 0x0001d7c2, 0x00001dc4,
+ 0x0001d7c3, 0x00001dc5,
+ 0x0001d7c4, 0x00001dc6,
+ 0x0001d7c5, 0x00001dc7,
+ 0x0001d7c6, 0x00001dc8,
+ 0x0001d7c7, 0x00001dc9,
+ 0x0001d7c8, 0x00001dca,
+ 0x0001d7c9, 0x00001dcb,
+ 0x0001d7ce, 0x00001dcc,
+ 0x0001d7cf, 0x00001dcd,
+ 0x0001d7d0, 0x00001dce,
+ 0x0001d7d1, 0x00001dcf,
+ 0x0001d7d2, 0x00001dd0,
+ 0x0001d7d3, 0x00001dd1,
+ 0x0001d7d4, 0x00001dd2,
+ 0x0001d7d5, 0x00001dd3,
+ 0x0001d7d6, 0x00001dd4,
+ 0x0001d7d7, 0x00001dd5,
+ 0x0001d7d8, 0x00001dd6,
+ 0x0001d7d9, 0x00001dd7,
+ 0x0001d7da, 0x00001dd8,
+ 0x0001d7db, 0x00001dd9,
+ 0x0001d7dc, 0x00001dda,
+ 0x0001d7dd, 0x00001ddb,
+ 0x0001d7de, 0x00001ddc,
+ 0x0001d7df, 0x00001ddd,
+ 0x0001d7e0, 0x00001dde,
+ 0x0001d7e1, 0x00001ddf,
+ 0x0001d7e2, 0x00001de0,
+ 0x0001d7e3, 0x00001de1,
+ 0x0001d7e4, 0x00001de2,
+ 0x0001d7e5, 0x00001de3,
+ 0x0001d7e6, 0x00001de4,
+ 0x0001d7e7, 0x00001de5,
+ 0x0001d7e8, 0x00001de6,
+ 0x0001d7e9, 0x00001de7,
+ 0x0001d7ea, 0x00001de8,
+ 0x0001d7eb, 0x00001de9,
+ 0x0001d7ec, 0x00001dea,
+ 0x0001d7ed, 0x00001deb,
+ 0x0001d7ee, 0x00001dec,
+ 0x0001d7ef, 0x00001ded,
+ 0x0001d7f0, 0x00001dee,
+ 0x0001d7f1, 0x00001def,
+ 0x0001d7f2, 0x00001df0,
+ 0x0001d7f3, 0x00001df1,
+ 0x0001d7f4, 0x00001df2,
+ 0x0001d7f5, 0x00001df3,
+ 0x0001d7f6, 0x00001df4,
+ 0x0001d7f7, 0x00001df5,
+ 0x0001d7f8, 0x00001df6,
+ 0x0001d7f9, 0x00001df7,
+ 0x0001d7fa, 0x00001df8,
+ 0x0001d7fb, 0x00001df9,
+ 0x0001d7fc, 0x00001dfa,
+ 0x0001d7fd, 0x00001dfb,
+ 0x0001d7fe, 0x00001dfc,
+ 0x0001d7ff, 0x00001dfd,
+ 0x0002f800, 0x00001dfe,
+ 0x0002f801, 0x00001dff,
+ 0x0002f802, 0x00001e00,
+ 0x0002f803, 0x00001e01,
+ 0x0002f804, 0x00001e02,
+ 0x0002f805, 0x00001e03,
+ 0x0002f806, 0x00001e04,
+ 0x0002f807, 0x00001e05,
+ 0x0002f808, 0x00001e06,
+ 0x0002f809, 0x00001e07,
+ 0x0002f80a, 0x00001e08,
+ 0x0002f80b, 0x00001e09,
+ 0x0002f80c, 0x00001e0a,
+ 0x0002f80d, 0x00001e0b,
+ 0x0002f80e, 0x00001e0c,
+ 0x0002f80f, 0x00001e0d,
+ 0x0002f810, 0x00001e0e,
+ 0x0002f811, 0x00001e0f,
+ 0x0002f812, 0x00001e10,
+ 0x0002f813, 0x00001e11,
+ 0x0002f814, 0x00001e12,
+ 0x0002f815, 0x00001e13,
+ 0x0002f816, 0x00001e14,
+ 0x0002f817, 0x00001e15,
+ 0x0002f818, 0x00001e16,
+ 0x0002f819, 0x00001e17,
+ 0x0002f81a, 0x00001e18,
+ 0x0002f81b, 0x00001e19,
+ 0x0002f81c, 0x00001e1a,
+ 0x0002f81d, 0x00001e1b,
+ 0x0002f81e, 0x00001e1c,
+ 0x0002f81f, 0x00001e1d,
+ 0x0002f820, 0x00001e1e,
+ 0x0002f821, 0x00001e1f,
+ 0x0002f822, 0x00001e20,
+ 0x0002f823, 0x00001e21,
+ 0x0002f824, 0x00001e22,
+ 0x0002f825, 0x00001e23,
+ 0x0002f826, 0x00001e24,
+ 0x0002f827, 0x00001e25,
+ 0x0002f828, 0x00001e26,
+ 0x0002f829, 0x00001e27,
+ 0x0002f82a, 0x00001e28,
+ 0x0002f82b, 0x00001e29,
+ 0x0002f82c, 0x00001e2a,
+ 0x0002f82d, 0x00001e2b,
+ 0x0002f82e, 0x00001e2c,
+ 0x0002f82f, 0x00001e2d,
+ 0x0002f830, 0x00001e2e,
+ 0x0002f831, 0x00001e2f,
+ 0x0002f832, 0x00001e30,
+ 0x0002f833, 0x00001e31,
+ 0x0002f834, 0x00001e32,
+ 0x0002f835, 0x00001e33,
+ 0x0002f836, 0x00001e34,
+ 0x0002f837, 0x00001e35,
+ 0x0002f838, 0x00001e36,
+ 0x0002f839, 0x00001e37,
+ 0x0002f83a, 0x00001e38,
+ 0x0002f83b, 0x00001e39,
+ 0x0002f83c, 0x00001e3a,
+ 0x0002f83d, 0x00001e3b,
+ 0x0002f83e, 0x00001e3c,
+ 0x0002f83f, 0x00001e3d,
+ 0x0002f840, 0x00001e3e,
+ 0x0002f841, 0x00001e3f,
+ 0x0002f842, 0x00001e40,
+ 0x0002f843, 0x00001e41,
+ 0x0002f844, 0x00001e42,
+ 0x0002f845, 0x00001e43,
+ 0x0002f846, 0x00001e44,
+ 0x0002f847, 0x00001e45,
+ 0x0002f848, 0x00001e46,
+ 0x0002f849, 0x00001e47,
+ 0x0002f84a, 0x00001e48,
+ 0x0002f84b, 0x00001e49,
+ 0x0002f84c, 0x00001e4a,
+ 0x0002f84d, 0x00001e4b,
+ 0x0002f84e, 0x00001e4c,
+ 0x0002f84f, 0x00001e4d,
+ 0x0002f850, 0x00001e4e,
+ 0x0002f851, 0x00001e4f,
+ 0x0002f852, 0x00001e50,
+ 0x0002f853, 0x00001e51,
+ 0x0002f854, 0x00001e52,
+ 0x0002f855, 0x00001e53,
+ 0x0002f856, 0x00001e54,
+ 0x0002f857, 0x00001e55,
+ 0x0002f858, 0x00001e56,
+ 0x0002f859, 0x00001e57,
+ 0x0002f85a, 0x00001e58,
+ 0x0002f85b, 0x00001e59,
+ 0x0002f85c, 0x00001e5a,
+ 0x0002f85d, 0x00001e5b,
+ 0x0002f85e, 0x00001e5c,
+ 0x0002f85f, 0x00001e5d,
+ 0x0002f860, 0x00001e5e,
+ 0x0002f861, 0x00001e5f,
+ 0x0002f862, 0x00001e60,
+ 0x0002f863, 0x00001e61,
+ 0x0002f864, 0x00001e62,
+ 0x0002f865, 0x00001e63,
+ 0x0002f866, 0x00001e64,
+ 0x0002f867, 0x00001e65,
+ 0x0002f868, 0x00001e66,
+ 0x0002f869, 0x00001e67,
+ 0x0002f86a, 0x00001e68,
+ 0x0002f86b, 0x00001e69,
+ 0x0002f86c, 0x00001e6a,
+ 0x0002f86d, 0x00001e6b,
+ 0x0002f86e, 0x00001e6c,
+ 0x0002f86f, 0x00001e6d,
+ 0x0002f870, 0x00001e6e,
+ 0x0002f871, 0x00001e6f,
+ 0x0002f872, 0x00001e70,
+ 0x0002f873, 0x00001e71,
+ 0x0002f874, 0x00001e72,
+ 0x0002f875, 0x00001e73,
+ 0x0002f876, 0x00001e74,
+ 0x0002f877, 0x00001e75,
+ 0x0002f878, 0x00001e76,
+ 0x0002f879, 0x00001e77,
+ 0x0002f87a, 0x00001e78,
+ 0x0002f87b, 0x00001e79,
+ 0x0002f87c, 0x00001e7a,
+ 0x0002f87d, 0x00001e7b,
+ 0x0002f87e, 0x00001e7c,
+ 0x0002f87f, 0x00001e7d,
+ 0x0002f880, 0x00001e7e,
+ 0x0002f881, 0x00001e7f,
+ 0x0002f882, 0x00001e80,
+ 0x0002f883, 0x00001e81,
+ 0x0002f884, 0x00001e82,
+ 0x0002f885, 0x00001e83,
+ 0x0002f886, 0x00001e84,
+ 0x0002f887, 0x00001e85,
+ 0x0002f888, 0x00001e86,
+ 0x0002f889, 0x00001e87,
+ 0x0002f88a, 0x00001e88,
+ 0x0002f88b, 0x00001e89,
+ 0x0002f88c, 0x00001e8a,
+ 0x0002f88d, 0x00001e8b,
+ 0x0002f88e, 0x00001e8c,
+ 0x0002f88f, 0x00001e8d,
+ 0x0002f890, 0x00001e8e,
+ 0x0002f891, 0x00001e8f,
+ 0x0002f892, 0x00001e90,
+ 0x0002f893, 0x00001e91,
+ 0x0002f894, 0x00001e92,
+ 0x0002f895, 0x00001e93,
+ 0x0002f896, 0x00001e94,
+ 0x0002f897, 0x00001e95,
+ 0x0002f898, 0x00001e96,
+ 0x0002f899, 0x00001e97,
+ 0x0002f89a, 0x00001e98,
+ 0x0002f89b, 0x00001e99,
+ 0x0002f89c, 0x00001e9a,
+ 0x0002f89d, 0x00001e9b,
+ 0x0002f89e, 0x00001e9c,
+ 0x0002f89f, 0x00001e9d,
+ 0x0002f8a0, 0x00001e9e,
+ 0x0002f8a1, 0x00001e9f,
+ 0x0002f8a2, 0x00001ea0,
+ 0x0002f8a3, 0x00001ea1,
+ 0x0002f8a4, 0x00001ea2,
+ 0x0002f8a5, 0x00001ea3,
+ 0x0002f8a6, 0x00001ea4,
+ 0x0002f8a7, 0x00001ea5,
+ 0x0002f8a8, 0x00001ea6,
+ 0x0002f8a9, 0x00001ea7,
+ 0x0002f8aa, 0x00001ea8,
+ 0x0002f8ab, 0x00001ea9,
+ 0x0002f8ac, 0x00001eaa,
+ 0x0002f8ad, 0x00001eab,
+ 0x0002f8ae, 0x00001eac,
+ 0x0002f8af, 0x00001ead,
+ 0x0002f8b0, 0x00001eae,
+ 0x0002f8b1, 0x00001eaf,
+ 0x0002f8b2, 0x00001eb0,
+ 0x0002f8b3, 0x00001eb1,
+ 0x0002f8b4, 0x00001eb2,
+ 0x0002f8b5, 0x00001eb3,
+ 0x0002f8b6, 0x00001eb4,
+ 0x0002f8b7, 0x00001eb5,
+ 0x0002f8b8, 0x00001eb6,
+ 0x0002f8b9, 0x00001eb7,
+ 0x0002f8ba, 0x00001eb8,
+ 0x0002f8bb, 0x00001eb9,
+ 0x0002f8bc, 0x00001eba,
+ 0x0002f8bd, 0x00001ebb,
+ 0x0002f8be, 0x00001ebc,
+ 0x0002f8bf, 0x00001ebd,
+ 0x0002f8c0, 0x00001ebe,
+ 0x0002f8c1, 0x00001ebf,
+ 0x0002f8c2, 0x00001ec0,
+ 0x0002f8c3, 0x00001ec1,
+ 0x0002f8c4, 0x00001ec2,
+ 0x0002f8c5, 0x00001ec3,
+ 0x0002f8c6, 0x00001ec4,
+ 0x0002f8c7, 0x00001ec5,
+ 0x0002f8c8, 0x00001ec6,
+ 0x0002f8c9, 0x00001ec7,
+ 0x0002f8ca, 0x00001ec8,
+ 0x0002f8cb, 0x00001ec9,
+ 0x0002f8cc, 0x00001eca,
+ 0x0002f8cd, 0x00001ecb,
+ 0x0002f8ce, 0x00001ecc,
+ 0x0002f8cf, 0x00001ecd,
+ 0x0002f8d0, 0x00001ece,
+ 0x0002f8d1, 0x00001ecf,
+ 0x0002f8d2, 0x00001ed0,
+ 0x0002f8d3, 0x00001ed1,
+ 0x0002f8d4, 0x00001ed2,
+ 0x0002f8d5, 0x00001ed3,
+ 0x0002f8d6, 0x00001ed4,
+ 0x0002f8d7, 0x00001ed5,
+ 0x0002f8d8, 0x00001ed6,
+ 0x0002f8d9, 0x00001ed7,
+ 0x0002f8da, 0x00001ed8,
+ 0x0002f8db, 0x00001ed9,
+ 0x0002f8dc, 0x00001eda,
+ 0x0002f8dd, 0x00001edb,
+ 0x0002f8de, 0x00001edc,
+ 0x0002f8df, 0x00001edd,
+ 0x0002f8e0, 0x00001ede,
+ 0x0002f8e1, 0x00001edf,
+ 0x0002f8e2, 0x00001ee0,
+ 0x0002f8e3, 0x00001ee1,
+ 0x0002f8e4, 0x00001ee2,
+ 0x0002f8e5, 0x00001ee3,
+ 0x0002f8e6, 0x00001ee4,
+ 0x0002f8e7, 0x00001ee5,
+ 0x0002f8e8, 0x00001ee6,
+ 0x0002f8e9, 0x00001ee7,
+ 0x0002f8ea, 0x00001ee8,
+ 0x0002f8eb, 0x00001ee9,
+ 0x0002f8ec, 0x00001eea,
+ 0x0002f8ed, 0x00001eeb,
+ 0x0002f8ee, 0x00001eec,
+ 0x0002f8ef, 0x00001eed,
+ 0x0002f8f0, 0x00001eee,
+ 0x0002f8f1, 0x00001eef,
+ 0x0002f8f2, 0x00001ef0,
+ 0x0002f8f3, 0x00001ef1,
+ 0x0002f8f4, 0x00001ef2,
+ 0x0002f8f5, 0x00001ef3,
+ 0x0002f8f6, 0x00001ef4,
+ 0x0002f8f7, 0x00001ef5,
+ 0x0002f8f8, 0x00001ef6,
+ 0x0002f8f9, 0x00001ef7,
+ 0x0002f8fa, 0x00001ef8,
+ 0x0002f8fb, 0x00001ef9,
+ 0x0002f8fc, 0x00001efa,
+ 0x0002f8fd, 0x00001efb,
+ 0x0002f8fe, 0x00001efc,
+ 0x0002f8ff, 0x00001efd,
+ 0x0002f900, 0x00001efe,
+ 0x0002f901, 0x00001eff,
+ 0x0002f902, 0x00001f00,
+ 0x0002f903, 0x00001f01,
+ 0x0002f904, 0x00001f02,
+ 0x0002f905, 0x00001f03,
+ 0x0002f906, 0x00001f04,
+ 0x0002f907, 0x00001f05,
+ 0x0002f908, 0x00001f06,
+ 0x0002f909, 0x00001f07,
+ 0x0002f90a, 0x00001f08,
+ 0x0002f90b, 0x00001f09,
+ 0x0002f90c, 0x00001f0a,
+ 0x0002f90d, 0x00001f0b,
+ 0x0002f90e, 0x00001f0c,
+ 0x0002f90f, 0x00001f0d,
+ 0x0002f910, 0x00001f0e,
+ 0x0002f911, 0x00001f0f,
+ 0x0002f912, 0x00001f10,
+ 0x0002f913, 0x00001f11,
+ 0x0002f914, 0x00001f12,
+ 0x0002f915, 0x00001f13,
+ 0x0002f916, 0x00001f14,
+ 0x0002f917, 0x00001f15,
+ 0x0002f918, 0x00001f16,
+ 0x0002f919, 0x00001f17,
+ 0x0002f91a, 0x00001f18,
+ 0x0002f91b, 0x00001f19,
+ 0x0002f91c, 0x00001f1a,
+ 0x0002f91d, 0x00001f1b,
+ 0x0002f91e, 0x00001f1c,
+ 0x0002f91f, 0x00001f1d,
+ 0x0002f920, 0x00001f1e,
+ 0x0002f921, 0x00001f1f,
+ 0x0002f922, 0x00001f20,
+ 0x0002f923, 0x00001f21,
+ 0x0002f924, 0x00001f22,
+ 0x0002f925, 0x00001f23,
+ 0x0002f926, 0x00001f24,
+ 0x0002f927, 0x00001f25,
+ 0x0002f928, 0x00001f26,
+ 0x0002f929, 0x00001f27,
+ 0x0002f92a, 0x00001f28,
+ 0x0002f92b, 0x00001f29,
+ 0x0002f92c, 0x00001f2a,
+ 0x0002f92d, 0x00001f2b,
+ 0x0002f92e, 0x00001f2c,
+ 0x0002f92f, 0x00001f2d,
+ 0x0002f930, 0x00001f2e,
+ 0x0002f931, 0x00001f2f,
+ 0x0002f932, 0x00001f30,
+ 0x0002f933, 0x00001f31,
+ 0x0002f934, 0x00001f32,
+ 0x0002f935, 0x00001f33,
+ 0x0002f936, 0x00001f34,
+ 0x0002f937, 0x00001f35,
+ 0x0002f938, 0x00001f36,
+ 0x0002f939, 0x00001f37,
+ 0x0002f93a, 0x00001f38,
+ 0x0002f93b, 0x00001f39,
+ 0x0002f93c, 0x00001f3a,
+ 0x0002f93d, 0x00001f3b,
+ 0x0002f93e, 0x00001f3c,
+ 0x0002f93f, 0x00001f3d,
+ 0x0002f940, 0x00001f3e,
+ 0x0002f941, 0x00001f3f,
+ 0x0002f942, 0x00001f40,
+ 0x0002f943, 0x00001f41,
+ 0x0002f944, 0x00001f42,
+ 0x0002f945, 0x00001f43,
+ 0x0002f946, 0x00001f44,
+ 0x0002f947, 0x00001f45,
+ 0x0002f948, 0x00001f46,
+ 0x0002f949, 0x00001f47,
+ 0x0002f94a, 0x00001f48,
+ 0x0002f94b, 0x00001f49,
+ 0x0002f94c, 0x00001f4a,
+ 0x0002f94d, 0x00001f4b,
+ 0x0002f94e, 0x00001f4c,
+ 0x0002f94f, 0x00001f4d,
+ 0x0002f950, 0x00001f4e,
+ 0x0002f951, 0x00001f4f,
+ 0x0002f952, 0x00001f50,
+ 0x0002f953, 0x00001f51,
+ 0x0002f954, 0x00001f52,
+ 0x0002f955, 0x00001f53,
+ 0x0002f956, 0x00001f54,
+ 0x0002f957, 0x00001f55,
+ 0x0002f958, 0x00001f56,
+ 0x0002f959, 0x00001f57,
+ 0x0002f95a, 0x00001f58,
+ 0x0002f95b, 0x00001f59,
+ 0x0002f95c, 0x00001f5a,
+ 0x0002f95d, 0x00001f5b,
+ 0x0002f95e, 0x00001f5c,
+ 0x0002f95f, 0x00001f5d,
+ 0x0002f960, 0x00001f5e,
+ 0x0002f961, 0x00001f5f,
+ 0x0002f962, 0x00001f60,
+ 0x0002f963, 0x00001f61,
+ 0x0002f964, 0x00001f62,
+ 0x0002f965, 0x00001f63,
+ 0x0002f966, 0x00001f64,
+ 0x0002f967, 0x00001f65,
+ 0x0002f968, 0x00001f66,
+ 0x0002f969, 0x00001f67,
+ 0x0002f96a, 0x00001f68,
+ 0x0002f96b, 0x00001f69,
+ 0x0002f96c, 0x00001f6a,
+ 0x0002f96d, 0x00001f6b,
+ 0x0002f96e, 0x00001f6c,
+ 0x0002f96f, 0x00001f6d,
+ 0x0002f970, 0x00001f6e,
+ 0x0002f971, 0x00001f6f,
+ 0x0002f972, 0x00001f70,
+ 0x0002f973, 0x00001f71,
+ 0x0002f974, 0x00001f72,
+ 0x0002f975, 0x00001f73,
+ 0x0002f976, 0x00001f74,
+ 0x0002f977, 0x00001f75,
+ 0x0002f978, 0x00001f76,
+ 0x0002f979, 0x00001f77,
+ 0x0002f97a, 0x00001f78,
+ 0x0002f97b, 0x00001f79,
+ 0x0002f97c, 0x00001f7a,
+ 0x0002f97d, 0x00001f7b,
+ 0x0002f97e, 0x00001f7c,
+ 0x0002f97f, 0x00001f7d,
+ 0x0002f980, 0x00001f7e,
+ 0x0002f981, 0x00001f7f,
+ 0x0002f982, 0x00001f80,
+ 0x0002f983, 0x00001f81,
+ 0x0002f984, 0x00001f82,
+ 0x0002f985, 0x00001f83,
+ 0x0002f986, 0x00001f84,
+ 0x0002f987, 0x00001f85,
+ 0x0002f988, 0x00001f86,
+ 0x0002f989, 0x00001f87,
+ 0x0002f98a, 0x00001f88,
+ 0x0002f98b, 0x00001f89,
+ 0x0002f98c, 0x00001f8a,
+ 0x0002f98d, 0x00001f8b,
+ 0x0002f98e, 0x00001f8c,
+ 0x0002f98f, 0x00001f8d,
+ 0x0002f990, 0x00001f8e,
+ 0x0002f991, 0x00001f8f,
+ 0x0002f992, 0x00001f90,
+ 0x0002f993, 0x00001f91,
+ 0x0002f994, 0x00001f92,
+ 0x0002f995, 0x00001f93,
+ 0x0002f996, 0x00001f94,
+ 0x0002f997, 0x00001f95,
+ 0x0002f998, 0x00001f96,
+ 0x0002f999, 0x00001f97,
+ 0x0002f99a, 0x00001f98,
+ 0x0002f99b, 0x00001f99,
+ 0x0002f99c, 0x00001f9a,
+ 0x0002f99d, 0x00001f9b,
+ 0x0002f99e, 0x00001f9c,
+ 0x0002f99f, 0x00001f9d,
+ 0x0002f9a0, 0x00001f9e,
+ 0x0002f9a1, 0x00001f9f,
+ 0x0002f9a2, 0x00001fa0,
+ 0x0002f9a3, 0x00001fa1,
+ 0x0002f9a4, 0x00001fa2,
+ 0x0002f9a5, 0x00001fa3,
+ 0x0002f9a6, 0x00001fa4,
+ 0x0002f9a7, 0x00001fa5,
+ 0x0002f9a8, 0x00001fa6,
+ 0x0002f9a9, 0x00001fa7,
+ 0x0002f9aa, 0x00001fa8,
+ 0x0002f9ab, 0x00001fa9,
+ 0x0002f9ac, 0x00001faa,
+ 0x0002f9ad, 0x00001fab,
+ 0x0002f9ae, 0x00001fac,
+ 0x0002f9af, 0x00001fad,
+ 0x0002f9b0, 0x00001fae,
+ 0x0002f9b1, 0x00001faf,
+ 0x0002f9b2, 0x00001fb0,
+ 0x0002f9b3, 0x00001fb1,
+ 0x0002f9b4, 0x00001fb2,
+ 0x0002f9b5, 0x00001fb3,
+ 0x0002f9b6, 0x00001fb4,
+ 0x0002f9b7, 0x00001fb5,
+ 0x0002f9b8, 0x00001fb6,
+ 0x0002f9b9, 0x00001fb7,
+ 0x0002f9ba, 0x00001fb8,
+ 0x0002f9bb, 0x00001fb9,
+ 0x0002f9bc, 0x00001fba,
+ 0x0002f9bd, 0x00001fbb,
+ 0x0002f9be, 0x00001fbc,
+ 0x0002f9bf, 0x00001fbd,
+ 0x0002f9c0, 0x00001fbe,
+ 0x0002f9c1, 0x00001fbf,
+ 0x0002f9c2, 0x00001fc0,
+ 0x0002f9c3, 0x00001fc1,
+ 0x0002f9c4, 0x00001fc2,
+ 0x0002f9c5, 0x00001fc3,
+ 0x0002f9c6, 0x00001fc4,
+ 0x0002f9c7, 0x00001fc5,
+ 0x0002f9c8, 0x00001fc6,
+ 0x0002f9c9, 0x00001fc7,
+ 0x0002f9ca, 0x00001fc8,
+ 0x0002f9cb, 0x00001fc9,
+ 0x0002f9cc, 0x00001fca,
+ 0x0002f9cd, 0x00001fcb,
+ 0x0002f9ce, 0x00001fcc,
+ 0x0002f9cf, 0x00001fcd,
+ 0x0002f9d0, 0x00001fce,
+ 0x0002f9d1, 0x00001fcf,
+ 0x0002f9d2, 0x00001fd0,
+ 0x0002f9d3, 0x00001fd1,
+ 0x0002f9d4, 0x00001fd2,
+ 0x0002f9d5, 0x00001fd3,
+ 0x0002f9d6, 0x00001fd4,
+ 0x0002f9d7, 0x00001fd5,
+ 0x0002f9d8, 0x00001fd6,
+ 0x0002f9d9, 0x00001fd7,
+ 0x0002f9da, 0x00001fd8,
+ 0x0002f9db, 0x00001fd9,
+ 0x0002f9dc, 0x00001fda,
+ 0x0002f9dd, 0x00001fdb,
+ 0x0002f9de, 0x00001fdc,
+ 0x0002f9df, 0x00001fdd,
+ 0x0002f9e0, 0x00001fde,
+ 0x0002f9e1, 0x00001fdf,
+ 0x0002f9e2, 0x00001fe0,
+ 0x0002f9e3, 0x00001fe1,
+ 0x0002f9e4, 0x00001fe2,
+ 0x0002f9e5, 0x00001fe3,
+ 0x0002f9e6, 0x00001fe4,
+ 0x0002f9e7, 0x00001fe5,
+ 0x0002f9e8, 0x00001fe6,
+ 0x0002f9e9, 0x00001fe7,
+ 0x0002f9ea, 0x00001fe8,
+ 0x0002f9eb, 0x00001fe9,
+ 0x0002f9ec, 0x00001fea,
+ 0x0002f9ed, 0x00001feb,
+ 0x0002f9ee, 0x00001fec,
+ 0x0002f9ef, 0x00001fed,
+ 0x0002f9f0, 0x00001fee,
+ 0x0002f9f1, 0x00001fef,
+ 0x0002f9f2, 0x00001ff0,
+ 0x0002f9f3, 0x00001ff1,
+ 0x0002f9f4, 0x00001ff2,
+ 0x0002f9f5, 0x00001ff3,
+ 0x0002f9f6, 0x00001ff4,
+ 0x0002f9f7, 0x00001ff5,
+ 0x0002f9f8, 0x00001ff6,
+ 0x0002f9f9, 0x00001ff7,
+ 0x0002f9fa, 0x00001ff8,
+ 0x0002f9fb, 0x00001ff9,
+ 0x0002f9fc, 0x00001ffa,
+ 0x0002f9fd, 0x00001ffb,
+ 0x0002f9fe, 0x00001ffc,
+ 0x0002f9ff, 0x00001ffd,
+ 0x0002fa00, 0x00001ffe,
+ 0x0002fa01, 0x00001fff,
+ 0x0002fa02, 0x00002000,
+ 0x0002fa03, 0x00002001,
+ 0x0002fa04, 0x00002002,
+ 0x0002fa05, 0x00002003,
+ 0x0002fa06, 0x00002004,
+ 0x0002fa07, 0x00002005,
+ 0x0002fa08, 0x00002006,
+ 0x0002fa09, 0x00002007,
+ 0x0002fa0a, 0x00002008,
+ 0x0002fa0b, 0x00002009,
+ 0x0002fa0c, 0x0000200a,
+ 0x0002fa0d, 0x0000200b,
+ 0x0002fa0e, 0x0000200c,
+ 0x0002fa0f, 0x0000200d,
+ 0x0002fa10, 0x0000200e,
+ 0x0002fa11, 0x0000200f,
+ 0x0002fa12, 0x00002010,
+ 0x0002fa13, 0x00002011,
+ 0x0002fa14, 0x00002012,
+ 0x0002fa15, 0x00002013,
+ 0x0002fa16, 0x00002014,
+ 0x0002fa17, 0x00002015,
+ 0x0002fa18, 0x00002016,
+ 0x0002fa19, 0x00002017,
+ 0x0002fa1a, 0x00002018,
+ 0x0002fa1b, 0x00002019,
+ 0x0002fa1c, 0x0000201a,
+ 0x0002fa1d, 0x0000201b,
+ 0x0000201c
+};
+
+static const ac_uint4 _uckdcmp_decomp[] = {
+ 0x00000020, 0x00000020, 0x00000308, 0x00000061,
+ 0x00000020, 0x00000304, 0x00000032, 0x00000033,
+ 0x00000020, 0x00000301, 0x000003bc, 0x00000020,
+ 0x00000327, 0x00000031, 0x0000006f, 0x00000031,
+ 0x00002044, 0x00000034, 0x00000031, 0x00002044,
+ 0x00000032, 0x00000033, 0x00002044, 0x00000034,
+ 0x00000041, 0x00000300, 0x00000041, 0x00000301,
+ 0x00000041, 0x00000302, 0x00000041, 0x00000303,
+ 0x00000041, 0x00000308, 0x00000041, 0x0000030a,
+ 0x00000043, 0x00000327, 0x00000045, 0x00000300,
+ 0x00000045, 0x00000301, 0x00000045, 0x00000302,
+ 0x00000045, 0x00000308, 0x00000049, 0x00000300,
+ 0x00000049, 0x00000301, 0x00000049, 0x00000302,
+ 0x00000049, 0x00000308, 0x0000004e, 0x00000303,
+ 0x0000004f, 0x00000300, 0x0000004f, 0x00000301,
+ 0x0000004f, 0x00000302, 0x0000004f, 0x00000303,
+ 0x0000004f, 0x00000308, 0x00000055, 0x00000300,
+ 0x00000055, 0x00000301, 0x00000055, 0x00000302,
+ 0x00000055, 0x00000308, 0x00000059, 0x00000301,
+ 0x00000061, 0x00000300, 0x00000061, 0x00000301,
+ 0x00000061, 0x00000302, 0x00000061, 0x00000303,
+ 0x00000061, 0x00000308, 0x00000061, 0x0000030a,
+ 0x00000063, 0x00000327, 0x00000065, 0x00000300,
+ 0x00000065, 0x00000301, 0x00000065, 0x00000302,
+ 0x00000065, 0x00000308, 0x00000069, 0x00000300,
+ 0x00000069, 0x00000301, 0x00000069, 0x00000302,
+ 0x00000069, 0x00000308, 0x0000006e, 0x00000303,
+ 0x0000006f, 0x00000300, 0x0000006f, 0x00000301,
+ 0x0000006f, 0x00000302, 0x0000006f, 0x00000303,
+ 0x0000006f, 0x00000308, 0x00000075, 0x00000300,
+ 0x00000075, 0x00000301, 0x00000075, 0x00000302,
+ 0x00000075, 0x00000308, 0x00000079, 0x00000301,
+ 0x00000079, 0x00000308, 0x00000041, 0x00000304,
+ 0x00000061, 0x00000304, 0x00000041, 0x00000306,
+ 0x00000061, 0x00000306, 0x00000041, 0x00000328,
+ 0x00000061, 0x00000328, 0x00000043, 0x00000301,
+ 0x00000063, 0x00000301, 0x00000043, 0x00000302,
+ 0x00000063, 0x00000302, 0x00000043, 0x00000307,
+ 0x00000063, 0x00000307, 0x00000043, 0x0000030c,
+ 0x00000063, 0x0000030c, 0x00000044, 0x0000030c,
+ 0x00000064, 0x0000030c, 0x00000045, 0x00000304,
+ 0x00000065, 0x00000304, 0x00000045, 0x00000306,
+ 0x00000065, 0x00000306, 0x00000045, 0x00000307,
+ 0x00000065, 0x00000307, 0x00000045, 0x00000328,
+ 0x00000065, 0x00000328, 0x00000045, 0x0000030c,
+ 0x00000065, 0x0000030c, 0x00000047, 0x00000302,
+ 0x00000067, 0x00000302, 0x00000047, 0x00000306,
+ 0x00000067, 0x00000306, 0x00000047, 0x00000307,
+ 0x00000067, 0x00000307, 0x00000047, 0x00000327,
+ 0x00000067, 0x00000327, 0x00000048, 0x00000302,
+ 0x00000068, 0x00000302, 0x00000049, 0x00000303,
+ 0x00000069, 0x00000303, 0x00000049, 0x00000304,
+ 0x00000069, 0x00000304, 0x00000049, 0x00000306,
+ 0x00000069, 0x00000306, 0x00000049, 0x00000328,
+ 0x00000069, 0x00000328, 0x00000049, 0x00000307,
+ 0x00000049, 0x0000004a, 0x00000069, 0x0000006a,
+ 0x0000004a, 0x00000302, 0x0000006a, 0x00000302,
+ 0x0000004b, 0x00000327, 0x0000006b, 0x00000327,
+ 0x0000004c, 0x00000301, 0x0000006c, 0x00000301,
+ 0x0000004c, 0x00000327, 0x0000006c, 0x00000327,
+ 0x0000004c, 0x0000030c, 0x0000006c, 0x0000030c,
+ 0x0000004c, 0x000000b7, 0x0000006c, 0x000000b7,
+ 0x0000004e, 0x00000301, 0x0000006e, 0x00000301,
+ 0x0000004e, 0x00000327, 0x0000006e, 0x00000327,
+ 0x0000004e, 0x0000030c, 0x0000006e, 0x0000030c,
+ 0x000002bc, 0x0000006e, 0x0000004f, 0x00000304,
+ 0x0000006f, 0x00000304, 0x0000004f, 0x00000306,
+ 0x0000006f, 0x00000306, 0x0000004f, 0x0000030b,
+ 0x0000006f, 0x0000030b, 0x00000052, 0x00000301,
+ 0x00000072, 0x00000301, 0x00000052, 0x00000327,
+ 0x00000072, 0x00000327, 0x00000052, 0x0000030c,
+ 0x00000072, 0x0000030c, 0x00000053, 0x00000301,
+ 0x00000073, 0x00000301, 0x00000053, 0x00000302,
+ 0x00000073, 0x00000302, 0x00000053, 0x00000327,
+ 0x00000073, 0x00000327, 0x00000053, 0x0000030c,
+ 0x00000073, 0x0000030c, 0x00000054, 0x00000327,
+ 0x00000074, 0x00000327, 0x00000054, 0x0000030c,
+ 0x00000074, 0x0000030c, 0x00000055, 0x00000303,
+ 0x00000075, 0x00000303, 0x00000055, 0x00000304,
+ 0x00000075, 0x00000304, 0x00000055, 0x00000306,
+ 0x00000075, 0x00000306, 0x00000055, 0x0000030a,
+ 0x00000075, 0x0000030a, 0x00000055, 0x0000030b,
+ 0x00000075, 0x0000030b, 0x00000055, 0x00000328,
+ 0x00000075, 0x00000328, 0x00000057, 0x00000302,
+ 0x00000077, 0x00000302, 0x00000059, 0x00000302,
+ 0x00000079, 0x00000302, 0x00000059, 0x00000308,
+ 0x0000005a, 0x00000301, 0x0000007a, 0x00000301,
+ 0x0000005a, 0x00000307, 0x0000007a, 0x00000307,
+ 0x0000005a, 0x0000030c, 0x0000007a, 0x0000030c,
+ 0x00000073, 0x0000004f, 0x0000031b, 0x0000006f,
+ 0x0000031b, 0x00000055, 0x0000031b, 0x00000075,
+ 0x0000031b, 0x00000044, 0x0000005a, 0x0000030c,
+ 0x00000044, 0x0000007a, 0x0000030c, 0x00000064,
+ 0x0000007a, 0x0000030c, 0x0000004c, 0x0000004a,
+ 0x0000004c, 0x0000006a, 0x0000006c, 0x0000006a,
+ 0x0000004e, 0x0000004a, 0x0000004e, 0x0000006a,
+ 0x0000006e, 0x0000006a, 0x00000041, 0x0000030c,
+ 0x00000061, 0x0000030c, 0x00000049, 0x0000030c,
+ 0x00000069, 0x0000030c, 0x0000004f, 0x0000030c,
+ 0x0000006f, 0x0000030c, 0x00000055, 0x0000030c,
+ 0x00000075, 0x0000030c, 0x00000055, 0x00000308,
+ 0x00000304, 0x00000075, 0x00000308, 0x00000304,
+ 0x00000055, 0x00000308, 0x00000301, 0x00000075,
+ 0x00000308, 0x00000301, 0x00000055, 0x00000308,
+ 0x0000030c, 0x00000075, 0x00000308, 0x0000030c,
+ 0x00000055, 0x00000308, 0x00000300, 0x00000075,
+ 0x00000308, 0x00000300, 0x00000041, 0x00000308,
+ 0x00000304, 0x00000061, 0x00000308, 0x00000304,
+ 0x00000041, 0x00000307, 0x00000304, 0x00000061,
+ 0x00000307, 0x00000304, 0x000000c6, 0x00000304,
+ 0x000000e6, 0x00000304, 0x00000047, 0x0000030c,
+ 0x00000067, 0x0000030c, 0x0000004b, 0x0000030c,
+ 0x0000006b, 0x0000030c, 0x0000004f, 0x00000328,
+ 0x0000006f, 0x00000328, 0x0000004f, 0x00000328,
+ 0x00000304, 0x0000006f, 0x00000328, 0x00000304,
+ 0x000001b7, 0x0000030c, 0x00000292, 0x0000030c,
+ 0x0000006a, 0x0000030c, 0x00000044, 0x0000005a,
+ 0x00000044, 0x0000007a, 0x00000064, 0x0000007a,
+ 0x00000047, 0x00000301, 0x00000067, 0x00000301,
+ 0x0000004e, 0x00000300, 0x0000006e, 0x00000300,
+ 0x00000041, 0x0000030a, 0x00000301, 0x00000061,
+ 0x0000030a, 0x00000301, 0x000000c6, 0x00000301,
+ 0x000000e6, 0x00000301, 0x000000d8, 0x00000301,
+ 0x000000f8, 0x00000301, 0x00000041, 0x0000030f,
+ 0x00000061, 0x0000030f, 0x00000041, 0x00000311,
+ 0x00000061, 0x00000311, 0x00000045, 0x0000030f,
+ 0x00000065, 0x0000030f, 0x00000045, 0x00000311,
+ 0x00000065, 0x00000311, 0x00000049, 0x0000030f,
+ 0x00000069, 0x0000030f, 0x00000049, 0x00000311,
+ 0x00000069, 0x00000311, 0x0000004f, 0x0000030f,
+ 0x0000006f, 0x0000030f, 0x0000004f, 0x00000311,
+ 0x0000006f, 0x00000311, 0x00000052, 0x0000030f,
+ 0x00000072, 0x0000030f, 0x00000052, 0x00000311,
+ 0x00000072, 0x00000311, 0x00000055, 0x0000030f,
+ 0x00000075, 0x0000030f, 0x00000055, 0x00000311,
+ 0x00000075, 0x00000311, 0x00000053, 0x00000326,
+ 0x00000073, 0x00000326, 0x00000054, 0x00000326,
+ 0x00000074, 0x00000326, 0x00000048, 0x0000030c,
+ 0x00000068, 0x0000030c, 0x00000041, 0x00000307,
+ 0x00000061, 0x00000307, 0x00000045, 0x00000327,
+ 0x00000065, 0x00000327, 0x0000004f, 0x00000308,
+ 0x00000304, 0x0000006f, 0x00000308, 0x00000304,
+ 0x0000004f, 0x00000303, 0x00000304, 0x0000006f,
+ 0x00000303, 0x00000304, 0x0000004f, 0x00000307,
+ 0x0000006f, 0x00000307, 0x0000004f, 0x00000307,
+ 0x00000304, 0x0000006f, 0x00000307, 0x00000304,
+ 0x00000059, 0x00000304, 0x00000079, 0x00000304,
+ 0x00000068, 0x00000266, 0x0000006a, 0x00000072,
+ 0x00000279, 0x0000027b, 0x00000281, 0x00000077,
+ 0x00000079, 0x00000020, 0x00000306, 0x00000020,
+ 0x00000307, 0x00000020, 0x0000030a, 0x00000020,
+ 0x00000328, 0x00000020, 0x00000303, 0x00000020,
+ 0x0000030b, 0x00000263, 0x0000006c, 0x00000073,
+ 0x00000078, 0x00000295, 0x00000300, 0x00000301,
+ 0x00000313, 0x00000308, 0x00000301, 0x000002b9,
+ 0x00000020, 0x00000345, 0x0000003b, 0x00000020,
+ 0x00000301, 0x00000020, 0x00000308, 0x00000301,
+ 0x00000391, 0x00000301, 0x000000b7, 0x00000395,
+ 0x00000301, 0x00000397, 0x00000301, 0x00000399,
+ 0x00000301, 0x0000039f, 0x00000301, 0x000003a5,
+ 0x00000301, 0x000003a9, 0x00000301, 0x000003b9,
+ 0x00000308, 0x00000301, 0x00000399, 0x00000308,
+ 0x000003a5, 0x00000308, 0x000003b1, 0x00000301,
+ 0x000003b5, 0x00000301, 0x000003b7, 0x00000301,
+ 0x000003b9, 0x00000301, 0x000003c5, 0x00000308,
+ 0x00000301, 0x000003b9, 0x00000308, 0x000003c5,
+ 0x00000308, 0x000003bf, 0x00000301, 0x000003c5,
+ 0x00000301, 0x000003c9, 0x00000301, 0x000003b2,
+ 0x000003b8, 0x000003a5, 0x000003a5, 0x00000301,
+ 0x000003a5, 0x00000308, 0x000003c6, 0x000003c0,
+ 0x000003ba, 0x000003c1, 0x000003c2, 0x00000398,
+ 0x000003b5, 0x00000415, 0x00000300, 0x00000415,
+ 0x00000308, 0x00000413, 0x00000301, 0x00000406,
+ 0x00000308, 0x0000041a, 0x00000301, 0x00000418,
+ 0x00000300, 0x00000423, 0x00000306, 0x00000418,
+ 0x00000306, 0x00000438, 0x00000306, 0x00000435,
+ 0x00000300, 0x00000435, 0x00000308, 0x00000433,
+ 0x00000301, 0x00000456, 0x00000308, 0x0000043a,
+ 0x00000301, 0x00000438, 0x00000300, 0x00000443,
+ 0x00000306, 0x00000474, 0x0000030f, 0x00000475,
+ 0x0000030f, 0x00000416, 0x00000306, 0x00000436,
+ 0x00000306, 0x00000410, 0x00000306, 0x00000430,
+ 0x00000306, 0x00000410, 0x00000308, 0x00000430,
+ 0x00000308, 0x00000415, 0x00000306, 0x00000435,
+ 0x00000306, 0x000004d8, 0x00000308, 0x000004d9,
+ 0x00000308, 0x00000416, 0x00000308, 0x00000436,
+ 0x00000308, 0x00000417, 0x00000308, 0x00000437,
+ 0x00000308, 0x00000418, 0x00000304, 0x00000438,
+ 0x00000304, 0x00000418, 0x00000308, 0x00000438,
+ 0x00000308, 0x0000041e, 0x00000308, 0x0000043e,
+ 0x00000308, 0x000004e8, 0x00000308, 0x000004e9,
+ 0x00000308, 0x0000042d, 0x00000308, 0x0000044d,
+ 0x00000308, 0x00000423, 0x00000304, 0x00000443,
+ 0x00000304, 0x00000423, 0x00000308, 0x00000443,
+ 0x00000308, 0x00000423, 0x0000030b, 0x00000443,
+ 0x0000030b, 0x00000427, 0x00000308, 0x00000447,
+ 0x00000308, 0x0000042b, 0x00000308, 0x0000044b,
+ 0x00000308, 0x00000565, 0x00000582, 0x00000627,
+ 0x00000653, 0x00000627, 0x00000654, 0x00000648,
+ 0x00000654, 0x00000627, 0x00000655, 0x0000064a,
+ 0x00000654, 0x00000627, 0x00000674, 0x00000648,
+ 0x00000674, 0x000006c7, 0x00000674, 0x0000064a,
+ 0x00000674, 0x000006d5, 0x00000654, 0x000006c1,
+ 0x00000654, 0x000006d2, 0x00000654, 0x00000928,
+ 0x0000093c, 0x00000930, 0x0000093c, 0x00000933,
+ 0x0000093c, 0x00000915, 0x0000093c, 0x00000916,
+ 0x0000093c, 0x00000917, 0x0000093c, 0x0000091c,
+ 0x0000093c, 0x00000921, 0x0000093c, 0x00000922,
+ 0x0000093c, 0x0000092b, 0x0000093c, 0x0000092f,
+ 0x0000093c, 0x000009c7, 0x000009be, 0x000009c7,
+ 0x000009d7, 0x000009a1, 0x000009bc, 0x000009a2,
+ 0x000009bc, 0x000009af, 0x000009bc, 0x00000a32,
+ 0x00000a3c, 0x00000a38, 0x00000a3c, 0x00000a16,
+ 0x00000a3c, 0x00000a17, 0x00000a3c, 0x00000a1c,
+ 0x00000a3c, 0x00000a2b, 0x00000a3c, 0x00000b47,
+ 0x00000b56, 0x00000b47, 0x00000b3e, 0x00000b47,
+ 0x00000b57, 0x00000b21, 0x00000b3c, 0x00000b22,
+ 0x00000b3c, 0x00000b92, 0x00000bd7, 0x00000bc6,
+ 0x00000bbe, 0x00000bc7, 0x00000bbe, 0x00000bc6,
+ 0x00000bd7, 0x00000c46, 0x00000c56, 0x00000cbf,
+ 0x00000cd5, 0x00000cc6, 0x00000cd5, 0x00000cc6,
+ 0x00000cd6, 0x00000cc6, 0x00000cc2, 0x00000cc6,
+ 0x00000cc2, 0x00000cd5, 0x00000d46, 0x00000d3e,
+ 0x00000d47, 0x00000d3e, 0x00000d46, 0x00000d57,
+ 0x00000dd9, 0x00000dca, 0x00000dd9, 0x00000dcf,
+ 0x00000dd9, 0x00000dcf, 0x00000dca, 0x00000dd9,
+ 0x00000ddf, 0x00000e4d, 0x00000e32, 0x00000ecd,
+ 0x00000eb2, 0x00000eab, 0x00000e99, 0x00000eab,
+ 0x00000ea1, 0x00000f0b, 0x00000f42, 0x00000fb7,
+ 0x00000f4c, 0x00000fb7, 0x00000f51, 0x00000fb7,
+ 0x00000f56, 0x00000fb7, 0x00000f5b, 0x00000fb7,
+ 0x00000f40, 0x00000fb5, 0x00000f71, 0x00000f72,
+ 0x00000f71, 0x00000f74, 0x00000fb2, 0x00000f80,
+ 0x00000fb2, 0x00000f71, 0x00000f80, 0x00000fb3,
+ 0x00000f80, 0x00000fb3, 0x00000f71, 0x00000f80,
+ 0x00000f71, 0x00000f80, 0x00000f92, 0x00000fb7,
+ 0x00000f9c, 0x00000fb7, 0x00000fa1, 0x00000fb7,
+ 0x00000fa6, 0x00000fb7, 0x00000fab, 0x00000fb7,
+ 0x00000f90, 0x00000fb5, 0x00001025, 0x0000102e,
+ 0x00000041, 0x00000325, 0x00000061, 0x00000325,
+ 0x00000042, 0x00000307, 0x00000062, 0x00000307,
+ 0x00000042, 0x00000323, 0x00000062, 0x00000323,
+ 0x00000042, 0x00000331, 0x00000062, 0x00000331,
+ 0x00000043, 0x00000327, 0x00000301, 0x00000063,
+ 0x00000327, 0x00000301, 0x00000044, 0x00000307,
+ 0x00000064, 0x00000307, 0x00000044, 0x00000323,
+ 0x00000064, 0x00000323, 0x00000044, 0x00000331,
+ 0x00000064, 0x00000331, 0x00000044, 0x00000327,
+ 0x00000064, 0x00000327, 0x00000044, 0x0000032d,
+ 0x00000064, 0x0000032d, 0x00000045, 0x00000304,
+ 0x00000300, 0x00000065, 0x00000304, 0x00000300,
+ 0x00000045, 0x00000304, 0x00000301, 0x00000065,
+ 0x00000304, 0x00000301, 0x00000045, 0x0000032d,
+ 0x00000065, 0x0000032d, 0x00000045, 0x00000330,
+ 0x00000065, 0x00000330, 0x00000045, 0x00000327,
+ 0x00000306, 0x00000065, 0x00000327, 0x00000306,
+ 0x00000046, 0x00000307, 0x00000066, 0x00000307,
+ 0x00000047, 0x00000304, 0x00000067, 0x00000304,
+ 0x00000048, 0x00000307, 0x00000068, 0x00000307,
+ 0x00000048, 0x00000323, 0x00000068, 0x00000323,
+ 0x00000048, 0x00000308, 0x00000068, 0x00000308,
+ 0x00000048, 0x00000327, 0x00000068, 0x00000327,
+ 0x00000048, 0x0000032e, 0x00000068, 0x0000032e,
+ 0x00000049, 0x00000330, 0x00000069, 0x00000330,
+ 0x00000049, 0x00000308, 0x00000301, 0x00000069,
+ 0x00000308, 0x00000301, 0x0000004b, 0x00000301,
+ 0x0000006b, 0x00000301, 0x0000004b, 0x00000323,
+ 0x0000006b, 0x00000323, 0x0000004b, 0x00000331,
+ 0x0000006b, 0x00000331, 0x0000004c, 0x00000323,
+ 0x0000006c, 0x00000323, 0x0000004c, 0x00000323,
+ 0x00000304, 0x0000006c, 0x00000323, 0x00000304,
+ 0x0000004c, 0x00000331, 0x0000006c, 0x00000331,
+ 0x0000004c, 0x0000032d, 0x0000006c, 0x0000032d,
+ 0x0000004d, 0x00000301, 0x0000006d, 0x00000301,
+ 0x0000004d, 0x00000307, 0x0000006d, 0x00000307,
+ 0x0000004d, 0x00000323, 0x0000006d, 0x00000323,
+ 0x0000004e, 0x00000307, 0x0000006e, 0x00000307,
+ 0x0000004e, 0x00000323, 0x0000006e, 0x00000323,
+ 0x0000004e, 0x00000331, 0x0000006e, 0x00000331,
+ 0x0000004e, 0x0000032d, 0x0000006e, 0x0000032d,
+ 0x0000004f, 0x00000303, 0x00000301, 0x0000006f,
+ 0x00000303, 0x00000301, 0x0000004f, 0x00000303,
+ 0x00000308, 0x0000006f, 0x00000303, 0x00000308,
+ 0x0000004f, 0x00000304, 0x00000300, 0x0000006f,
+ 0x00000304, 0x00000300, 0x0000004f, 0x00000304,
+ 0x00000301, 0x0000006f, 0x00000304, 0x00000301,
+ 0x00000050, 0x00000301, 0x00000070, 0x00000301,
+ 0x00000050, 0x00000307, 0x00000070, 0x00000307,
+ 0x00000052, 0x00000307, 0x00000072, 0x00000307,
+ 0x00000052, 0x00000323, 0x00000072, 0x00000323,
+ 0x00000052, 0x00000323, 0x00000304, 0x00000072,
+ 0x00000323, 0x00000304, 0x00000052, 0x00000331,
+ 0x00000072, 0x00000331, 0x00000053, 0x00000307,
+ 0x00000073, 0x00000307, 0x00000053, 0x00000323,
+ 0x00000073, 0x00000323, 0x00000053, 0x00000301,
+ 0x00000307, 0x00000073, 0x00000301, 0x00000307,
+ 0x00000053, 0x0000030c, 0x00000307, 0x00000073,
+ 0x0000030c, 0x00000307, 0x00000053, 0x00000323,
+ 0x00000307, 0x00000073, 0x00000323, 0x00000307,
+ 0x00000054, 0x00000307, 0x00000074, 0x00000307,
+ 0x00000054, 0x00000323, 0x00000074, 0x00000323,
+ 0x00000054, 0x00000331, 0x00000074, 0x00000331,
+ 0x00000054, 0x0000032d, 0x00000074, 0x0000032d,
+ 0x00000055, 0x00000324, 0x00000075, 0x00000324,
+ 0x00000055, 0x00000330, 0x00000075, 0x00000330,
+ 0x00000055, 0x0000032d, 0x00000075, 0x0000032d,
+ 0x00000055, 0x00000303, 0x00000301, 0x00000075,
+ 0x00000303, 0x00000301, 0x00000055, 0x00000304,
+ 0x00000308, 0x00000075, 0x00000304, 0x00000308,
+ 0x00000056, 0x00000303, 0x00000076, 0x00000303,
+ 0x00000056, 0x00000323, 0x00000076, 0x00000323,
+ 0x00000057, 0x00000300, 0x00000077, 0x00000300,
+ 0x00000057, 0x00000301, 0x00000077, 0x00000301,
+ 0x00000057, 0x00000308, 0x00000077, 0x00000308,
+ 0x00000057, 0x00000307, 0x00000077, 0x00000307,
+ 0x00000057, 0x00000323, 0x00000077, 0x00000323,
+ 0x00000058, 0x00000307, 0x00000078, 0x00000307,
+ 0x00000058, 0x00000308, 0x00000078, 0x00000308,
+ 0x00000059, 0x00000307, 0x00000079, 0x00000307,
+ 0x0000005a, 0x00000302, 0x0000007a, 0x00000302,
+ 0x0000005a, 0x00000323, 0x0000007a, 0x00000323,
+ 0x0000005a, 0x00000331, 0x0000007a, 0x00000331,
+ 0x00000068, 0x00000331, 0x00000074, 0x00000308,
+ 0x00000077, 0x0000030a, 0x00000079, 0x0000030a,
+ 0x00000061, 0x000002be, 0x00000073, 0x00000307,
+ 0x00000041, 0x00000323, 0x00000061, 0x00000323,
+ 0x00000041, 0x00000309, 0x00000061, 0x00000309,
+ 0x00000041, 0x00000302, 0x00000301, 0x00000061,
+ 0x00000302, 0x00000301, 0x00000041, 0x00000302,
+ 0x00000300, 0x00000061, 0x00000302, 0x00000300,
+ 0x00000041, 0x00000302, 0x00000309, 0x00000061,
+ 0x00000302, 0x00000309, 0x00000041, 0x00000302,
+ 0x00000303, 0x00000061, 0x00000302, 0x00000303,
+ 0x00000041, 0x00000323, 0x00000302, 0x00000061,
+ 0x00000323, 0x00000302, 0x00000041, 0x00000306,
+ 0x00000301, 0x00000061, 0x00000306, 0x00000301,
+ 0x00000041, 0x00000306, 0x00000300, 0x00000061,
+ 0x00000306, 0x00000300, 0x00000041, 0x00000306,
+ 0x00000309, 0x00000061, 0x00000306, 0x00000309,
+ 0x00000041, 0x00000306, 0x00000303, 0x00000061,
+ 0x00000306, 0x00000303, 0x00000041, 0x00000323,
+ 0x00000306, 0x00000061, 0x00000323, 0x00000306,
+ 0x00000045, 0x00000323, 0x00000065, 0x00000323,
+ 0x00000045, 0x00000309, 0x00000065, 0x00000309,
+ 0x00000045, 0x00000303, 0x00000065, 0x00000303,
+ 0x00000045, 0x00000302, 0x00000301, 0x00000065,
+ 0x00000302, 0x00000301, 0x00000045, 0x00000302,
+ 0x00000300, 0x00000065, 0x00000302, 0x00000300,
+ 0x00000045, 0x00000302, 0x00000309, 0x00000065,
+ 0x00000302, 0x00000309, 0x00000045, 0x00000302,
+ 0x00000303, 0x00000065, 0x00000302, 0x00000303,
+ 0x00000045, 0x00000323, 0x00000302, 0x00000065,
+ 0x00000323, 0x00000302, 0x00000049, 0x00000309,
+ 0x00000069, 0x00000309, 0x00000049, 0x00000323,
+ 0x00000069, 0x00000323, 0x0000004f, 0x00000323,
+ 0x0000006f, 0x00000323, 0x0000004f, 0x00000309,
+ 0x0000006f, 0x00000309, 0x0000004f, 0x00000302,
+ 0x00000301, 0x0000006f, 0x00000302, 0x00000301,
+ 0x0000004f, 0x00000302, 0x00000300, 0x0000006f,
+ 0x00000302, 0x00000300, 0x0000004f, 0x00000302,
+ 0x00000309, 0x0000006f, 0x00000302, 0x00000309,
+ 0x0000004f, 0x00000302, 0x00000303, 0x0000006f,
+ 0x00000302, 0x00000303, 0x0000004f, 0x00000323,
+ 0x00000302, 0x0000006f, 0x00000323, 0x00000302,
+ 0x0000004f, 0x0000031b, 0x00000301, 0x0000006f,
+ 0x0000031b, 0x00000301, 0x0000004f, 0x0000031b,
+ 0x00000300, 0x0000006f, 0x0000031b, 0x00000300,
+ 0x0000004f, 0x0000031b, 0x00000309, 0x0000006f,
+ 0x0000031b, 0x00000309, 0x0000004f, 0x0000031b,
+ 0x00000303, 0x0000006f, 0x0000031b, 0x00000303,
+ 0x0000004f, 0x0000031b, 0x00000323, 0x0000006f,
+ 0x0000031b, 0x00000323, 0x00000055, 0x00000323,
+ 0x00000075, 0x00000323, 0x00000055, 0x00000309,
+ 0x00000075, 0x00000309, 0x00000055, 0x0000031b,
+ 0x00000301, 0x00000075, 0x0000031b, 0x00000301,
+ 0x00000055, 0x0000031b, 0x00000300, 0x00000075,
+ 0x0000031b, 0x00000300, 0x00000055, 0x0000031b,
+ 0x00000309, 0x00000075, 0x0000031b, 0x00000309,
+ 0x00000055, 0x0000031b, 0x00000303, 0x00000075,
+ 0x0000031b, 0x00000303, 0x00000055, 0x0000031b,
+ 0x00000323, 0x00000075, 0x0000031b, 0x00000323,
+ 0x00000059, 0x00000300, 0x00000079, 0x00000300,
+ 0x00000059, 0x00000323, 0x00000079, 0x00000323,
+ 0x00000059, 0x00000309, 0x00000079, 0x00000309,
+ 0x00000059, 0x00000303, 0x00000079, 0x00000303,
+ 0x000003b1, 0x00000313, 0x000003b1, 0x00000314,
+ 0x000003b1, 0x00000313, 0x00000300, 0x000003b1,
+ 0x00000314, 0x00000300, 0x000003b1, 0x00000313,
+ 0x00000301, 0x000003b1, 0x00000314, 0x00000301,
+ 0x000003b1, 0x00000313, 0x00000342, 0x000003b1,
+ 0x00000314, 0x00000342, 0x00000391, 0x00000313,
+ 0x00000391, 0x00000314, 0x00000391, 0x00000313,
+ 0x00000300, 0x00000391, 0x00000314, 0x00000300,
+ 0x00000391, 0x00000313, 0x00000301, 0x00000391,
+ 0x00000314, 0x00000301, 0x00000391, 0x00000313,
+ 0x00000342, 0x00000391, 0x00000314, 0x00000342,
+ 0x000003b5, 0x00000313, 0x000003b5, 0x00000314,
+ 0x000003b5, 0x00000313, 0x00000300, 0x000003b5,
+ 0x00000314, 0x00000300, 0x000003b5, 0x00000313,
+ 0x00000301, 0x000003b5, 0x00000314, 0x00000301,
+ 0x00000395, 0x00000313, 0x00000395, 0x00000314,
+ 0x00000395, 0x00000313, 0x00000300, 0x00000395,
+ 0x00000314, 0x00000300, 0x00000395, 0x00000313,
+ 0x00000301, 0x00000395, 0x00000314, 0x00000301,
+ 0x000003b7, 0x00000313, 0x000003b7, 0x00000314,
+ 0x000003b7, 0x00000313, 0x00000300, 0x000003b7,
+ 0x00000314, 0x00000300, 0x000003b7, 0x00000313,
+ 0x00000301, 0x000003b7, 0x00000314, 0x00000301,
+ 0x000003b7, 0x00000313, 0x00000342, 0x000003b7,
+ 0x00000314, 0x00000342, 0x00000397, 0x00000313,
+ 0x00000397, 0x00000314, 0x00000397, 0x00000313,
+ 0x00000300, 0x00000397, 0x00000314, 0x00000300,
+ 0x00000397, 0x00000313, 0x00000301, 0x00000397,
+ 0x00000314, 0x00000301, 0x00000397, 0x00000313,
+ 0x00000342, 0x00000397, 0x00000314, 0x00000342,
+ 0x000003b9, 0x00000313, 0x000003b9, 0x00000314,
+ 0x000003b9, 0x00000313, 0x00000300, 0x000003b9,
+ 0x00000314, 0x00000300, 0x000003b9, 0x00000313,
+ 0x00000301, 0x000003b9, 0x00000314, 0x00000301,
+ 0x000003b9, 0x00000313, 0x00000342, 0x000003b9,
+ 0x00000314, 0x00000342, 0x00000399, 0x00000313,
+ 0x00000399, 0x00000314, 0x00000399, 0x00000313,
+ 0x00000300, 0x00000399, 0x00000314, 0x00000300,
+ 0x00000399, 0x00000313, 0x00000301, 0x00000399,
+ 0x00000314, 0x00000301, 0x00000399, 0x00000313,
+ 0x00000342, 0x00000399, 0x00000314, 0x00000342,
+ 0x000003bf, 0x00000313, 0x000003bf, 0x00000314,
+ 0x000003bf, 0x00000313, 0x00000300, 0x000003bf,
+ 0x00000314, 0x00000300, 0x000003bf, 0x00000313,
+ 0x00000301, 0x000003bf, 0x00000314, 0x00000301,
+ 0x0000039f, 0x00000313, 0x0000039f, 0x00000314,
+ 0x0000039f, 0x00000313, 0x00000300, 0x0000039f,
+ 0x00000314, 0x00000300, 0x0000039f, 0x00000313,
+ 0x00000301, 0x0000039f, 0x00000314, 0x00000301,
+ 0x000003c5, 0x00000313, 0x000003c5, 0x00000314,
+ 0x000003c5, 0x00000313, 0x00000300, 0x000003c5,
+ 0x00000314, 0x00000300, 0x000003c5, 0x00000313,
+ 0x00000301, 0x000003c5, 0x00000314, 0x00000301,
+ 0x000003c5, 0x00000313, 0x00000342, 0x000003c5,
+ 0x00000314, 0x00000342, 0x000003a5, 0x00000314,
+ 0x000003a5, 0x00000314, 0x00000300, 0x000003a5,
+ 0x00000314, 0x00000301, 0x000003a5, 0x00000314,
+ 0x00000342, 0x000003c9, 0x00000313, 0x000003c9,
+ 0x00000314, 0x000003c9, 0x00000313, 0x00000300,
+ 0x000003c9, 0x00000314, 0x00000300, 0x000003c9,
+ 0x00000313, 0x00000301, 0x000003c9, 0x00000314,
+ 0x00000301, 0x000003c9, 0x00000313, 0x00000342,
+ 0x000003c9, 0x00000314, 0x00000342, 0x000003a9,
+ 0x00000313, 0x000003a9, 0x00000314, 0x000003a9,
+ 0x00000313, 0x00000300, 0x000003a9, 0x00000314,
+ 0x00000300, 0x000003a9, 0x00000313, 0x00000301,
+ 0x000003a9, 0x00000314, 0x00000301, 0x000003a9,
+ 0x00000313, 0x00000342, 0x000003a9, 0x00000314,
+ 0x00000342, 0x000003b1, 0x00000300, 0x000003b1,
+ 0x00000301, 0x000003b5, 0x00000300, 0x000003b5,
+ 0x00000301, 0x000003b7, 0x00000300, 0x000003b7,
+ 0x00000301, 0x000003b9, 0x00000300, 0x000003b9,
+ 0x00000301, 0x000003bf, 0x00000300, 0x000003bf,
+ 0x00000301, 0x000003c5, 0x00000300, 0x000003c5,
+ 0x00000301, 0x000003c9, 0x00000300, 0x000003c9,
+ 0x00000301, 0x000003b1, 0x00000313, 0x00000345,
+ 0x000003b1, 0x00000314, 0x00000345, 0x000003b1,
+ 0x00000313, 0x00000300, 0x00000345, 0x000003b1,
+ 0x00000314, 0x00000300, 0x00000345, 0x000003b1,
+ 0x00000313, 0x00000301, 0x00000345, 0x000003b1,
+ 0x00000314, 0x00000301, 0x00000345, 0x000003b1,
+ 0x00000313, 0x00000342, 0x00000345, 0x000003b1,
+ 0x00000314, 0x00000342, 0x00000345, 0x00000391,
+ 0x00000313, 0x00000345, 0x00000391, 0x00000314,
+ 0x00000345, 0x00000391, 0x00000313, 0x00000300,
+ 0x00000345, 0x00000391, 0x00000314, 0x00000300,
+ 0x00000345, 0x00000391, 0x00000313, 0x00000301,
+ 0x00000345, 0x00000391, 0x00000314, 0x00000301,
+ 0x00000345, 0x00000391, 0x00000313, 0x00000342,
+ 0x00000345, 0x00000391, 0x00000314, 0x00000342,
+ 0x00000345, 0x000003b7, 0x00000313, 0x00000345,
+ 0x000003b7, 0x00000314, 0x00000345, 0x000003b7,
+ 0x00000313, 0x00000300, 0x00000345, 0x000003b7,
+ 0x00000314, 0x00000300, 0x00000345, 0x000003b7,
+ 0x00000313, 0x00000301, 0x00000345, 0x000003b7,
+ 0x00000314, 0x00000301, 0x00000345, 0x000003b7,
+ 0x00000313, 0x00000342, 0x00000345, 0x000003b7,
+ 0x00000314, 0x00000342, 0x00000345, 0x00000397,
+ 0x00000313, 0x00000345, 0x00000397, 0x00000314,
+ 0x00000345, 0x00000397, 0x00000313, 0x00000300,
+ 0x00000345, 0x00000397, 0x00000314, 0x00000300,
+ 0x00000345, 0x00000397, 0x00000313, 0x00000301,
+ 0x00000345, 0x00000397, 0x00000314, 0x00000301,
+ 0x00000345, 0x00000397, 0x00000313, 0x00000342,
+ 0x00000345, 0x00000397, 0x00000314, 0x00000342,
+ 0x00000345, 0x000003c9, 0x00000313, 0x00000345,
+ 0x000003c9, 0x00000314, 0x00000345, 0x000003c9,
+ 0x00000313, 0x00000300, 0x00000345, 0x000003c9,
+ 0x00000314, 0x00000300, 0x00000345, 0x000003c9,
+ 0x00000313, 0x00000301, 0x00000345, 0x000003c9,
+ 0x00000314, 0x00000301, 0x00000345, 0x000003c9,
+ 0x00000313, 0x00000342, 0x00000345, 0x000003c9,
+ 0x00000314, 0x00000342, 0x00000345, 0x000003a9,
+ 0x00000313, 0x00000345, 0x000003a9, 0x00000314,
+ 0x00000345, 0x000003a9, 0x00000313, 0x00000300,
+ 0x00000345, 0x000003a9, 0x00000314, 0x00000300,
+ 0x00000345, 0x000003a9, 0x00000313, 0x00000301,
+ 0x00000345, 0x000003a9, 0x00000314, 0x00000301,
+ 0x00000345, 0x000003a9, 0x00000313, 0x00000342,
+ 0x00000345, 0x000003a9, 0x00000314, 0x00000342,
+ 0x00000345, 0x000003b1, 0x00000306, 0x000003b1,
+ 0x00000304, 0x000003b1, 0x00000300, 0x00000345,
+ 0x000003b1, 0x00000345, 0x000003b1, 0x00000301,
+ 0x00000345, 0x000003b1, 0x00000342, 0x000003b1,
+ 0x00000342, 0x00000345, 0x00000391, 0x00000306,
+ 0x00000391, 0x00000304, 0x00000391, 0x00000300,
+ 0x00000391, 0x00000301, 0x00000391, 0x00000345,
+ 0x00000020, 0x00000313, 0x000003b9, 0x00000020,
+ 0x00000313, 0x00000020, 0x00000342, 0x00000020,
+ 0x00000308, 0x00000342, 0x000003b7, 0x00000300,
+ 0x00000345, 0x000003b7, 0x00000345, 0x000003b7,
+ 0x00000301, 0x00000345, 0x000003b7, 0x00000342,
+ 0x000003b7, 0x00000342, 0x00000345, 0x00000395,
+ 0x00000300, 0x00000395, 0x00000301, 0x00000397,
+ 0x00000300, 0x00000397, 0x00000301, 0x00000397,
+ 0x00000345, 0x00000020, 0x00000313, 0x00000300,
+ 0x00000020, 0x00000313, 0x00000301, 0x00000020,
+ 0x00000313, 0x00000342, 0x000003b9, 0x00000306,
+ 0x000003b9, 0x00000304, 0x000003b9, 0x00000308,
+ 0x00000300, 0x000003b9, 0x00000308, 0x00000301,
+ 0x000003b9, 0x00000342, 0x000003b9, 0x00000308,
+ 0x00000342, 0x00000399, 0x00000306, 0x00000399,
+ 0x00000304, 0x00000399, 0x00000300, 0x00000399,
+ 0x00000301, 0x00000020, 0x00000314, 0x00000300,
+ 0x00000020, 0x00000314, 0x00000301, 0x00000020,
+ 0x00000314, 0x00000342, 0x000003c5, 0x00000306,
+ 0x000003c5, 0x00000304, 0x000003c5, 0x00000308,
+ 0x00000300, 0x000003c5, 0x00000308, 0x00000301,
+ 0x000003c1, 0x00000313, 0x000003c1, 0x00000314,
+ 0x000003c5, 0x00000342, 0x000003c5, 0x00000308,
+ 0x00000342, 0x000003a5, 0x00000306, 0x000003a5,
+ 0x00000304, 0x000003a5, 0x00000300, 0x000003a5,
+ 0x00000301, 0x000003a1, 0x00000314, 0x00000020,
+ 0x00000308, 0x00000300, 0x00000020, 0x00000308,
+ 0x00000301, 0x00000060, 0x000003c9, 0x00000300,
+ 0x00000345, 0x000003c9, 0x00000345, 0x000003c9,
+ 0x00000301, 0x00000345, 0x000003c9, 0x00000342,
+ 0x000003c9, 0x00000342, 0x00000345, 0x0000039f,
+ 0x00000300, 0x0000039f, 0x00000301, 0x000003a9,
+ 0x00000300, 0x000003a9, 0x00000301, 0x000003a9,
+ 0x00000345, 0x00000020, 0x00000301, 0x00000020,
+ 0x00000314, 0x00000020, 0x00000020, 0x00000020,
+ 0x00000020, 0x00000020, 0x00000020, 0x00000020,
+ 0x00000020, 0x00000020, 0x00000020, 0x00000020,
+ 0x00002010, 0x00000020, 0x00000333, 0x0000002e,
+ 0x0000002e, 0x0000002e, 0x0000002e, 0x0000002e,
+ 0x0000002e, 0x00000020, 0x00002032, 0x00002032,
+ 0x00002032, 0x00002032, 0x00002032, 0x00002035,
+ 0x00002035, 0x00002035, 0x00002035, 0x00002035,
+ 0x00000021, 0x00000021, 0x00000020, 0x00000305,
+ 0x0000003f, 0x0000003f, 0x0000003f, 0x00000021,
+ 0x00000021, 0x0000003f, 0x00002032, 0x00002032,
+ 0x00002032, 0x00002032, 0x00000020, 0x00000030,
+ 0x00000069, 0x00000034, 0x00000035, 0x00000036,
+ 0x00000037, 0x00000038, 0x00000039, 0x0000002b,
+ 0x00002212, 0x0000003d, 0x00000028, 0x00000029,
+ 0x0000006e, 0x00000030, 0x00000031, 0x00000032,
+ 0x00000033, 0x00000034, 0x00000035, 0x00000036,
+ 0x00000037, 0x00000038, 0x00000039, 0x0000002b,
+ 0x00002212, 0x0000003d, 0x00000028, 0x00000029,
+ 0x00000052, 0x00000073, 0x00000061, 0x0000002f,
+ 0x00000063, 0x00000061, 0x0000002f, 0x00000073,
+ 0x00000043, 0x000000b0, 0x00000043, 0x00000063,
+ 0x0000002f, 0x0000006f, 0x00000063, 0x0000002f,
+ 0x00000075, 0x00000190, 0x000000b0, 0x00000046,
+ 0x00000067, 0x00000048, 0x00000048, 0x00000048,
+ 0x00000068, 0x00000127, 0x00000049, 0x00000049,
+ 0x0000004c, 0x0000006c, 0x0000004e, 0x0000004e,
+ 0x0000006f, 0x00000050, 0x00000051, 0x00000052,
+ 0x00000052, 0x00000052, 0x00000053, 0x0000004d,
+ 0x00000054, 0x00000045, 0x0000004c, 0x00000054,
+ 0x0000004d, 0x0000005a, 0x000003a9, 0x0000005a,
+ 0x0000004b, 0x00000041, 0x0000030a, 0x00000042,
+ 0x00000043, 0x00000065, 0x00000045, 0x00000046,
+ 0x0000004d, 0x0000006f, 0x000005d0, 0x000005d1,
+ 0x000005d2, 0x000005d3, 0x00000069, 0x000003b3,
+ 0x00000393, 0x000003a0, 0x00002211, 0x00000044,
+ 0x00000064, 0x00000065, 0x00000069, 0x0000006a,
+ 0x00000031, 0x00002044, 0x00000033, 0x00000032,
+ 0x00002044, 0x00000033, 0x00000031, 0x00002044,
+ 0x00000035, 0x00000032, 0x00002044, 0x00000035,
+ 0x00000033, 0x00002044, 0x00000035, 0x00000034,
+ 0x00002044, 0x00000035, 0x00000031, 0x00002044,
+ 0x00000036, 0x00000035, 0x00002044, 0x00000036,
+ 0x00000031, 0x00002044, 0x00000038, 0x00000033,
+ 0x00002044, 0x00000038, 0x00000035, 0x00002044,
+ 0x00000038, 0x00000037, 0x00002044, 0x00000038,
+ 0x00000031, 0x00002044, 0x00000049, 0x00000049,
+ 0x00000049, 0x00000049, 0x00000049, 0x00000049,
+ 0x00000049, 0x00000056, 0x00000056, 0x00000056,
+ 0x00000049, 0x00000056, 0x00000049, 0x00000049,
+ 0x00000056, 0x00000049, 0x00000049, 0x00000049,
+ 0x00000049, 0x00000058, 0x00000058, 0x00000058,
+ 0x00000049, 0x00000058, 0x00000049, 0x00000049,
+ 0x0000004c, 0x00000043, 0x00000044, 0x0000004d,
+ 0x00000069, 0x00000069, 0x00000069, 0x00000069,
+ 0x00000069, 0x00000069, 0x00000069, 0x00000076,
+ 0x00000076, 0x00000076, 0x00000069, 0x00000076,
+ 0x00000069, 0x00000069, 0x00000076, 0x00000069,
+ 0x00000069, 0x00000069, 0x00000069, 0x00000078,
+ 0x00000078, 0x00000078, 0x00000069, 0x00000078,
+ 0x00000069, 0x00000069, 0x0000006c, 0x00000063,
+ 0x00000064, 0x0000006d, 0x00002190, 0x00000338,
+ 0x00002192, 0x00000338, 0x00002194, 0x00000338,
+ 0x000021d0, 0x00000338, 0x000021d4, 0x00000338,
+ 0x000021d2, 0x00000338, 0x00002203, 0x00000338,
+ 0x00002208, 0x00000338, 0x0000220b, 0x00000338,
+ 0x00002223, 0x00000338, 0x00002225, 0x00000338,
+ 0x0000222b, 0x0000222b, 0x0000222b, 0x0000222b,
+ 0x0000222b, 0x0000222e, 0x0000222e, 0x0000222e,
+ 0x0000222e, 0x0000222e, 0x0000223c, 0x00000338,
+ 0x00002243, 0x00000338, 0x00002245, 0x00000338,
+ 0x00002248, 0x00000338, 0x0000003d, 0x00000338,
+ 0x00002261, 0x00000338, 0x0000224d, 0x00000338,
+ 0x0000003c, 0x00000338, 0x0000003e, 0x00000338,
+ 0x00002264, 0x00000338, 0x00002265, 0x00000338,
+ 0x00002272, 0x00000338, 0x00002273, 0x00000338,
+ 0x00002276, 0x00000338, 0x00002277, 0x00000338,
+ 0x0000227a, 0x00000338, 0x0000227b, 0x00000338,
+ 0x00002282, 0x00000338, 0x00002283, 0x00000338,
+ 0x00002286, 0x00000338, 0x00002287, 0x00000338,
+ 0x000022a2, 0x00000338, 0x000022a8, 0x00000338,
+ 0x000022a9, 0x00000338, 0x000022ab, 0x00000338,
+ 0x0000227c, 0x00000338, 0x0000227d, 0x00000338,
+ 0x00002291, 0x00000338, 0x00002292, 0x00000338,
+ 0x000022b2, 0x00000338, 0x000022b3, 0x00000338,
+ 0x000022b4, 0x00000338, 0x000022b5, 0x00000338,
+ 0x00003008, 0x00003009, 0x00000031, 0x00000032,
+ 0x00000033, 0x00000034, 0x00000035, 0x00000036,
+ 0x00000037, 0x00000038, 0x00000039, 0x00000031,
+ 0x00000030, 0x00000031, 0x00000031, 0x00000031,
+ 0x00000032, 0x00000031, 0x00000033, 0x00000031,
+ 0x00000034, 0x00000031, 0x00000035, 0x00000031,
+ 0x00000036, 0x00000031, 0x00000037, 0x00000031,
+ 0x00000038, 0x00000031, 0x00000039, 0x00000032,
+ 0x00000030, 0x00000028, 0x00000031, 0x00000029,
+ 0x00000028, 0x00000032, 0x00000029, 0x00000028,
+ 0x00000033, 0x00000029, 0x00000028, 0x00000034,
+ 0x00000029, 0x00000028, 0x00000035, 0x00000029,
+ 0x00000028, 0x00000036, 0x00000029, 0x00000028,
+ 0x00000037, 0x00000029, 0x00000028, 0x00000038,
+ 0x00000029, 0x00000028, 0x00000039, 0x00000029,
+ 0x00000028, 0x00000031, 0x00000030, 0x00000029,
+ 0x00000028, 0x00000031, 0x00000031, 0x00000029,
+ 0x00000028, 0x00000031, 0x00000032, 0x00000029,
+ 0x00000028, 0x00000031, 0x00000033, 0x00000029,
+ 0x00000028, 0x00000031, 0x00000034, 0x00000029,
+ 0x00000028, 0x00000031, 0x00000035, 0x00000029,
+ 0x00000028, 0x00000031, 0x00000036, 0x00000029,
+ 0x00000028, 0x00000031, 0x00000037, 0x00000029,
+ 0x00000028, 0x00000031, 0x00000038, 0x00000029,
+ 0x00000028, 0x00000031, 0x00000039, 0x00000029,
+ 0x00000028, 0x00000032, 0x00000030, 0x00000029,
+ 0x00000031, 0x0000002e, 0x00000032, 0x0000002e,
+ 0x00000033, 0x0000002e, 0x00000034, 0x0000002e,
+ 0x00000035, 0x0000002e, 0x00000036, 0x0000002e,
+ 0x00000037, 0x0000002e, 0x00000038, 0x0000002e,
+ 0x00000039, 0x0000002e, 0x00000031, 0x00000030,
+ 0x0000002e, 0x00000031, 0x00000031, 0x0000002e,
+ 0x00000031, 0x00000032, 0x0000002e, 0x00000031,
+ 0x00000033, 0x0000002e, 0x00000031, 0x00000034,
+ 0x0000002e, 0x00000031, 0x00000035, 0x0000002e,
+ 0x00000031, 0x00000036, 0x0000002e, 0x00000031,
+ 0x00000037, 0x0000002e, 0x00000031, 0x00000038,
+ 0x0000002e, 0x00000031, 0x00000039, 0x0000002e,
+ 0x00000032, 0x00000030, 0x0000002e, 0x00000028,
+ 0x00000061, 0x00000029, 0x00000028, 0x00000062,
+ 0x00000029, 0x00000028, 0x00000063, 0x00000029,
+ 0x00000028, 0x00000064, 0x00000029, 0x00000028,
+ 0x00000065, 0x00000029, 0x00000028, 0x00000066,
+ 0x00000029, 0x00000028, 0x00000067, 0x00000029,
+ 0x00000028, 0x00000068, 0x00000029, 0x00000028,
+ 0x00000069, 0x00000029, 0x00000028, 0x0000006a,
+ 0x00000029, 0x00000028, 0x0000006b, 0x00000029,
+ 0x00000028, 0x0000006c, 0x00000029, 0x00000028,
+ 0x0000006d, 0x00000029, 0x00000028, 0x0000006e,
+ 0x00000029, 0x00000028, 0x0000006f, 0x00000029,
+ 0x00000028, 0x00000070, 0x00000029, 0x00000028,
+ 0x00000071, 0x00000029, 0x00000028, 0x00000072,
+ 0x00000029, 0x00000028, 0x00000073, 0x00000029,
+ 0x00000028, 0x00000074, 0x00000029, 0x00000028,
+ 0x00000075, 0x00000029, 0x00000028, 0x00000076,
+ 0x00000029, 0x00000028, 0x00000077, 0x00000029,
+ 0x00000028, 0x00000078, 0x00000029, 0x00000028,
+ 0x00000079, 0x00000029, 0x00000028, 0x0000007a,
+ 0x00000029, 0x00000041, 0x00000042, 0x00000043,
+ 0x00000044, 0x00000045, 0x00000046, 0x00000047,
+ 0x00000048, 0x00000049, 0x0000004a, 0x0000004b,
+ 0x0000004c, 0x0000004d, 0x0000004e, 0x0000004f,
+ 0x00000050, 0x00000051, 0x00000052, 0x00000053,
+ 0x00000054, 0x00000055, 0x00000056, 0x00000057,
+ 0x00000058, 0x00000059, 0x0000005a, 0x00000061,
+ 0x00000062, 0x00000063, 0x00000064, 0x00000065,
+ 0x00000066, 0x00000067, 0x00000068, 0x00000069,
+ 0x0000006a, 0x0000006b, 0x0000006c, 0x0000006d,
+ 0x0000006e, 0x0000006f, 0x00000070, 0x00000071,
+ 0x00000072, 0x00000073, 0x00000074, 0x00000075,
+ 0x00000076, 0x00000077, 0x00000078, 0x00000079,
+ 0x0000007a, 0x00000030, 0x0000222b, 0x0000222b,
+ 0x0000222b, 0x0000222b, 0x0000003a, 0x0000003a,
+ 0x0000003d, 0x0000003d, 0x0000003d, 0x0000003d,
+ 0x0000003d, 0x0000003d, 0x00002add, 0x00000338,
+ 0x00006bcd, 0x00009f9f, 0x00004e00, 0x00004e28,
+ 0x00004e36, 0x00004e3f, 0x00004e59, 0x00004e85,
+ 0x00004e8c, 0x00004ea0, 0x00004eba, 0x0000513f,
+ 0x00005165, 0x0000516b, 0x00005182, 0x00005196,
+ 0x000051ab, 0x000051e0, 0x000051f5, 0x00005200,
+ 0x0000529b, 0x000052f9, 0x00005315, 0x0000531a,
+ 0x00005338, 0x00005341, 0x0000535c, 0x00005369,
+ 0x00005382, 0x000053b6, 0x000053c8, 0x000053e3,
+ 0x000056d7, 0x0000571f, 0x000058eb, 0x00005902,
+ 0x0000590a, 0x00005915, 0x00005927, 0x00005973,
+ 0x00005b50, 0x00005b80, 0x00005bf8, 0x00005c0f,
+ 0x00005c22, 0x00005c38, 0x00005c6e, 0x00005c71,
+ 0x00005ddb, 0x00005de5, 0x00005df1, 0x00005dfe,
+ 0x00005e72, 0x00005e7a, 0x00005e7f, 0x00005ef4,
+ 0x00005efe, 0x00005f0b, 0x00005f13, 0x00005f50,
+ 0x00005f61, 0x00005f73, 0x00005fc3, 0x00006208,
+ 0x00006236, 0x0000624b, 0x0000652f, 0x00006534,
+ 0x00006587, 0x00006597, 0x000065a4, 0x000065b9,
+ 0x000065e0, 0x000065e5, 0x000066f0, 0x00006708,
+ 0x00006728, 0x00006b20, 0x00006b62, 0x00006b79,
+ 0x00006bb3, 0x00006bcb, 0x00006bd4, 0x00006bdb,
+ 0x00006c0f, 0x00006c14, 0x00006c34, 0x0000706b,
+ 0x0000722a, 0x00007236, 0x0000723b, 0x0000723f,
+ 0x00007247, 0x00007259, 0x0000725b, 0x000072ac,
+ 0x00007384, 0x00007389, 0x000074dc, 0x000074e6,
+ 0x00007518, 0x0000751f, 0x00007528, 0x00007530,
+ 0x0000758b, 0x00007592, 0x00007676, 0x0000767d,
+ 0x000076ae, 0x000076bf, 0x000076ee, 0x000077db,
+ 0x000077e2, 0x000077f3, 0x0000793a, 0x000079b8,
+ 0x000079be, 0x00007a74, 0x00007acb, 0x00007af9,
+ 0x00007c73, 0x00007cf8, 0x00007f36, 0x00007f51,
+ 0x00007f8a, 0x00007fbd, 0x00008001, 0x0000800c,
+ 0x00008012, 0x00008033, 0x0000807f, 0x00008089,
+ 0x000081e3, 0x000081ea, 0x000081f3, 0x000081fc,
+ 0x0000820c, 0x0000821b, 0x0000821f, 0x0000826e,
+ 0x00008272, 0x00008278, 0x0000864d, 0x0000866b,
+ 0x00008840, 0x0000884c, 0x00008863, 0x0000897e,
+ 0x0000898b, 0x000089d2, 0x00008a00, 0x00008c37,
+ 0x00008c46, 0x00008c55, 0x00008c78, 0x00008c9d,
+ 0x00008d64, 0x00008d70, 0x00008db3, 0x00008eab,
+ 0x00008eca, 0x00008f9b, 0x00008fb0, 0x00008fb5,
+ 0x00009091, 0x00009149, 0x000091c6, 0x000091cc,
+ 0x000091d1, 0x00009577, 0x00009580, 0x0000961c,
+ 0x000096b6, 0x000096b9, 0x000096e8, 0x00009751,
+ 0x0000975e, 0x00009762, 0x00009769, 0x000097cb,
+ 0x000097ed, 0x000097f3, 0x00009801, 0x000098a8,
+ 0x000098db, 0x000098df, 0x00009996, 0x00009999,
+ 0x000099ac, 0x00009aa8, 0x00009ad8, 0x00009adf,
+ 0x00009b25, 0x00009b2f, 0x00009b32, 0x00009b3c,
+ 0x00009b5a, 0x00009ce5, 0x00009e75, 0x00009e7f,
+ 0x00009ea5, 0x00009ebb, 0x00009ec3, 0x00009ecd,
+ 0x00009ed1, 0x00009ef9, 0x00009efd, 0x00009f0e,
+ 0x00009f13, 0x00009f20, 0x00009f3b, 0x00009f4a,
+ 0x00009f52, 0x00009f8d, 0x00009f9c, 0x00009fa0,
+ 0x00000020, 0x00003012, 0x00005341, 0x00005344,
+ 0x00005345, 0x0000304b, 0x00003099, 0x0000304d,
+ 0x00003099, 0x0000304f, 0x00003099, 0x00003051,
+ 0x00003099, 0x00003053, 0x00003099, 0x00003055,
+ 0x00003099, 0x00003057, 0x00003099, 0x00003059,
+ 0x00003099, 0x0000305b, 0x00003099, 0x0000305d,
+ 0x00003099, 0x0000305f, 0x00003099, 0x00003061,
+ 0x00003099, 0x00003064, 0x00003099, 0x00003066,
+ 0x00003099, 0x00003068, 0x00003099, 0x0000306f,
+ 0x00003099, 0x0000306f, 0x0000309a, 0x00003072,
+ 0x00003099, 0x00003072, 0x0000309a, 0x00003075,
+ 0x00003099, 0x00003075, 0x0000309a, 0x00003078,
+ 0x00003099, 0x00003078, 0x0000309a, 0x0000307b,
+ 0x00003099, 0x0000307b, 0x0000309a, 0x00003046,
+ 0x00003099, 0x00000020, 0x00003099, 0x00000020,
+ 0x0000309a, 0x0000309d, 0x00003099, 0x00003088,
+ 0x0000308a, 0x000030ab, 0x00003099, 0x000030ad,
+ 0x00003099, 0x000030af, 0x00003099, 0x000030b1,
+ 0x00003099, 0x000030b3, 0x00003099, 0x000030b5,
+ 0x00003099, 0x000030b7, 0x00003099, 0x000030b9,
+ 0x00003099, 0x000030bb, 0x00003099, 0x000030bd,
+ 0x00003099, 0x000030bf, 0x00003099, 0x000030c1,
+ 0x00003099, 0x000030c4, 0x00003099, 0x000030c6,
+ 0x00003099, 0x000030c8, 0x00003099, 0x000030cf,
+ 0x00003099, 0x000030cf, 0x0000309a, 0x000030d2,
+ 0x00003099, 0x000030d2, 0x0000309a, 0x000030d5,
+ 0x00003099, 0x000030d5, 0x0000309a, 0x000030d8,
+ 0x00003099, 0x000030d8, 0x0000309a, 0x000030db,
+ 0x00003099, 0x000030db, 0x0000309a, 0x000030a6,
+ 0x00003099, 0x000030ef, 0x00003099, 0x000030f0,
+ 0x00003099, 0x000030f1, 0x00003099, 0x000030f2,
+ 0x00003099, 0x000030fd, 0x00003099, 0x000030b3,
+ 0x000030c8, 0x00001100, 0x00001101, 0x000011aa,
+ 0x00001102, 0x000011ac, 0x000011ad, 0x00001103,
+ 0x00001104, 0x00001105, 0x000011b0, 0x000011b1,
+ 0x000011b2, 0x000011b3, 0x000011b4, 0x000011b5,
+ 0x0000111a, 0x00001106, 0x00001107, 0x00001108,
+ 0x00001121, 0x00001109, 0x0000110a, 0x0000110b,
+ 0x0000110c, 0x0000110d, 0x0000110e, 0x0000110f,
+ 0x00001110, 0x00001111, 0x00001112, 0x00001161,
+ 0x00001162, 0x00001163, 0x00001164, 0x00001165,
+ 0x00001166, 0x00001167, 0x00001168, 0x00001169,
+ 0x0000116a, 0x0000116b, 0x0000116c, 0x0000116d,
+ 0x0000116e, 0x0000116f, 0x00001170, 0x00001171,
+ 0x00001172, 0x00001173, 0x00001174, 0x00001175,
+ 0x00001160, 0x00001114, 0x00001115, 0x000011c7,
+ 0x000011c8, 0x000011cc, 0x000011ce, 0x000011d3,
+ 0x000011d7, 0x000011d9, 0x0000111c, 0x000011dd,
+ 0x000011df, 0x0000111d, 0x0000111e, 0x00001120,
+ 0x00001122, 0x00001123, 0x00001127, 0x00001129,
+ 0x0000112b, 0x0000112c, 0x0000112d, 0x0000112e,
+ 0x0000112f, 0x00001132, 0x00001136, 0x00001140,
+ 0x00001147, 0x0000114c, 0x000011f1, 0x000011f2,
+ 0x00001157, 0x00001158, 0x00001159, 0x00001184,
+ 0x00001185, 0x00001188, 0x00001191, 0x00001192,
+ 0x00001194, 0x0000119e, 0x000011a1, 0x00004e00,
+ 0x00004e8c, 0x00004e09, 0x000056db, 0x00004e0a,
+ 0x00004e2d, 0x00004e0b, 0x00007532, 0x00004e59,
+ 0x00004e19, 0x00004e01, 0x00005929, 0x00005730,
+ 0x00004eba, 0x00000028, 0x00001100, 0x00000029,
+ 0x00000028, 0x00001102, 0x00000029, 0x00000028,
+ 0x00001103, 0x00000029, 0x00000028, 0x00001105,
+ 0x00000029, 0x00000028, 0x00001106, 0x00000029,
+ 0x00000028, 0x00001107, 0x00000029, 0x00000028,
+ 0x00001109, 0x00000029, 0x00000028, 0x0000110b,
+ 0x00000029, 0x00000028, 0x0000110c, 0x00000029,
+ 0x00000028, 0x0000110e, 0x00000029, 0x00000028,
+ 0x0000110f, 0x00000029, 0x00000028, 0x00001110,
+ 0x00000029, 0x00000028, 0x00001111, 0x00000029,
+ 0x00000028, 0x00001112, 0x00000029, 0x00000028,
+ 0x00001100, 0x00001161, 0x00000029, 0x00000028,
+ 0x00001102, 0x00001161, 0x00000029, 0x00000028,
+ 0x00001103, 0x00001161, 0x00000029, 0x00000028,
+ 0x00001105, 0x00001161, 0x00000029, 0x00000028,
+ 0x00001106, 0x00001161, 0x00000029, 0x00000028,
+ 0x00001107, 0x00001161, 0x00000029, 0x00000028,
+ 0x00001109, 0x00001161, 0x00000029, 0x00000028,
+ 0x0000110b, 0x00001161, 0x00000029, 0x00000028,
+ 0x0000110c, 0x00001161, 0x00000029, 0x00000028,
+ 0x0000110e, 0x00001161, 0x00000029, 0x00000028,
+ 0x0000110f, 0x00001161, 0x00000029, 0x00000028,
+ 0x00001110, 0x00001161, 0x00000029, 0x00000028,
+ 0x00001111, 0x00001161, 0x00000029, 0x00000028,
+ 0x00001112, 0x00001161, 0x00000029, 0x00000028,
+ 0x0000110c, 0x0000116e, 0x00000029, 0x00000028,
+ 0x00004e00, 0x00000029, 0x00000028, 0x00004e8c,
+ 0x00000029, 0x00000028, 0x00004e09, 0x00000029,
+ 0x00000028, 0x000056db, 0x00000029, 0x00000028,
+ 0x00004e94, 0x00000029, 0x00000028, 0x0000516d,
+ 0x00000029, 0x00000028, 0x00004e03, 0x00000029,
+ 0x00000028, 0x0000516b, 0x00000029, 0x00000028,
+ 0x00004e5d, 0x00000029, 0x00000028, 0x00005341,
+ 0x00000029, 0x00000028, 0x00006708, 0x00000029,
+ 0x00000028, 0x0000706b, 0x00000029, 0x00000028,
+ 0x00006c34, 0x00000029, 0x00000028, 0x00006728,
+ 0x00000029, 0x00000028, 0x000091d1, 0x00000029,
+ 0x00000028, 0x0000571f, 0x00000029, 0x00000028,
+ 0x000065e5, 0x00000029, 0x00000028, 0x0000682a,
+ 0x00000029, 0x00000028, 0x00006709, 0x00000029,
+ 0x00000028, 0x0000793e, 0x00000029, 0x00000028,
+ 0x0000540d, 0x00000029, 0x00000028, 0x00007279,
+ 0x00000029, 0x00000028, 0x00008ca1, 0x00000029,
+ 0x00000028, 0x0000795d, 0x00000029, 0x00000028,
+ 0x000052b4, 0x00000029, 0x00000028, 0x00004ee3,
+ 0x00000029, 0x00000028, 0x0000547c, 0x00000029,
+ 0x00000028, 0x00005b66, 0x00000029, 0x00000028,
+ 0x000076e3, 0x00000029, 0x00000028, 0x00004f01,
+ 0x00000029, 0x00000028, 0x00008cc7, 0x00000029,
+ 0x00000028, 0x00005354, 0x00000029, 0x00000028,
+ 0x0000796d, 0x00000029, 0x00000028, 0x00004f11,
+ 0x00000029, 0x00000028, 0x000081ea, 0x00000029,
+ 0x00000028, 0x000081f3, 0x00000029, 0x00000032,
+ 0x00000031, 0x00000032, 0x00000032, 0x00000032,
+ 0x00000033, 0x00000032, 0x00000034, 0x00000032,
+ 0x00000035, 0x00000032, 0x00000036, 0x00000032,
+ 0x00000037, 0x00000032, 0x00000038, 0x00000032,
+ 0x00000039, 0x00000033, 0x00000030, 0x00000033,
+ 0x00000031, 0x00000033, 0x00000032, 0x00000033,
+ 0x00000033, 0x00000033, 0x00000034, 0x00000033,
+ 0x00000035, 0x00001100, 0x00001102, 0x00001103,
+ 0x00001105, 0x00001106, 0x00001107, 0x00001109,
+ 0x0000110b, 0x0000110c, 0x0000110e, 0x0000110f,
+ 0x00001110, 0x00001111, 0x00001112, 0x00001100,
+ 0x00001161, 0x00001102, 0x00001161, 0x00001103,
+ 0x00001161, 0x00001105, 0x00001161, 0x00001106,
+ 0x00001161, 0x00001107, 0x00001161, 0x00001109,
+ 0x00001161, 0x0000110b, 0x00001161, 0x0000110c,
+ 0x00001161, 0x0000110e, 0x00001161, 0x0000110f,
+ 0x00001161, 0x00001110, 0x00001161, 0x00001111,
+ 0x00001161, 0x00001112, 0x00001161, 0x00004e00,
+ 0x00004e8c, 0x00004e09, 0x000056db, 0x00004e94,
+ 0x0000516d, 0x00004e03, 0x0000516b, 0x00004e5d,
+ 0x00005341, 0x00006708, 0x0000706b, 0x00006c34,
+ 0x00006728, 0x000091d1, 0x0000571f, 0x000065e5,
+ 0x0000682a, 0x00006709, 0x0000793e, 0x0000540d,
+ 0x00007279, 0x00008ca1, 0x0000795d, 0x000052b4,
+ 0x000079d8, 0x00007537, 0x00005973, 0x00009069,
+ 0x0000512a, 0x00005370, 0x00006ce8, 0x00009805,
+ 0x00004f11, 0x00005199, 0x00006b63, 0x00004e0a,
+ 0x00004e2d, 0x00004e0b, 0x00005de6, 0x000053f3,
+ 0x0000533b, 0x00005b97, 0x00005b66, 0x000076e3,
+ 0x00004f01, 0x00008cc7, 0x00005354, 0x0000591c,
+ 0x00000033, 0x00000036, 0x00000033, 0x00000037,
+ 0x00000033, 0x00000038, 0x00000033, 0x00000039,
+ 0x00000034, 0x00000030, 0x00000034, 0x00000031,
+ 0x00000034, 0x00000032, 0x00000034, 0x00000033,
+ 0x00000034, 0x00000034, 0x00000034, 0x00000035,
+ 0x00000034, 0x00000036, 0x00000034, 0x00000037,
+ 0x00000034, 0x00000038, 0x00000034, 0x00000039,
+ 0x00000035, 0x00000030, 0x00000031, 0x00006708,
+ 0x00000032, 0x00006708, 0x00000033, 0x00006708,
+ 0x00000034, 0x00006708, 0x00000035, 0x00006708,
+ 0x00000036, 0x00006708, 0x00000037, 0x00006708,
+ 0x00000038, 0x00006708, 0x00000039, 0x00006708,
+ 0x00000031, 0x00000030, 0x00006708, 0x00000031,
+ 0x00000031, 0x00006708, 0x00000031, 0x00000032,
+ 0x00006708, 0x000030a2, 0x000030a4, 0x000030a6,
+ 0x000030a8, 0x000030aa, 0x000030ab, 0x000030ad,
+ 0x000030af, 0x000030b1, 0x000030b3, 0x000030b5,
+ 0x000030b7, 0x000030b9, 0x000030bb, 0x000030bd,
+ 0x000030bf, 0x000030c1, 0x000030c4, 0x000030c6,
+ 0x000030c8, 0x000030ca, 0x000030cb, 0x000030cc,
+ 0x000030cd, 0x000030ce, 0x000030cf, 0x000030d2,
+ 0x000030d5, 0x000030d8, 0x000030db, 0x000030de,
+ 0x000030df, 0x000030e0, 0x000030e1, 0x000030e2,
+ 0x000030e4, 0x000030e6, 0x000030e8, 0x000030e9,
+ 0x000030ea, 0x000030eb, 0x000030ec, 0x000030ed,
+ 0x000030ef, 0x000030f0, 0x000030f1, 0x000030f2,
+ 0x000030a2, 0x000030cf, 0x0000309a, 0x000030fc,
+ 0x000030c8, 0x000030a2, 0x000030eb, 0x000030d5,
+ 0x000030a1, 0x000030a2, 0x000030f3, 0x000030d8,
+ 0x0000309a, 0x000030a2, 0x000030a2, 0x000030fc,
+ 0x000030eb, 0x000030a4, 0x000030cb, 0x000030f3,
+ 0x000030af, 0x00003099, 0x000030a4, 0x000030f3,
+ 0x000030c1, 0x000030a6, 0x000030a9, 0x000030f3,
+ 0x000030a8, 0x000030b9, 0x000030af, 0x000030fc,
+ 0x000030c8, 0x00003099, 0x000030a8, 0x000030fc,
+ 0x000030ab, 0x000030fc, 0x000030aa, 0x000030f3,
+ 0x000030b9, 0x000030aa, 0x000030fc, 0x000030e0,
+ 0x000030ab, 0x000030a4, 0x000030ea, 0x000030ab,
+ 0x000030e9, 0x000030c3, 0x000030c8, 0x000030ab,
+ 0x000030ed, 0x000030ea, 0x000030fc, 0x000030ab,
+ 0x00003099, 0x000030ed, 0x000030f3, 0x000030ab,
+ 0x00003099, 0x000030f3, 0x000030de, 0x000030ad,
+ 0x00003099, 0x000030ab, 0x00003099, 0x000030ad,
+ 0x00003099, 0x000030cb, 0x000030fc, 0x000030ad,
+ 0x000030e5, 0x000030ea, 0x000030fc, 0x000030ad,
+ 0x00003099, 0x000030eb, 0x000030bf, 0x00003099,
+ 0x000030fc, 0x000030ad, 0x000030ed, 0x000030ad,
+ 0x000030ed, 0x000030af, 0x00003099, 0x000030e9,
+ 0x000030e0, 0x000030ad, 0x000030ed, 0x000030e1,
+ 0x000030fc, 0x000030c8, 0x000030eb, 0x000030ad,
+ 0x000030ed, 0x000030ef, 0x000030c3, 0x000030c8,
+ 0x000030af, 0x00003099, 0x000030e9, 0x000030e0,
+ 0x000030af, 0x00003099, 0x000030e9, 0x000030e0,
+ 0x000030c8, 0x000030f3, 0x000030af, 0x000030eb,
+ 0x000030bb, 0x00003099, 0x000030a4, 0x000030ed,
+ 0x000030af, 0x000030ed, 0x000030fc, 0x000030cd,
+ 0x000030b1, 0x000030fc, 0x000030b9, 0x000030b3,
+ 0x000030eb, 0x000030ca, 0x000030b3, 0x000030fc,
+ 0x000030db, 0x0000309a, 0x000030b5, 0x000030a4,
+ 0x000030af, 0x000030eb, 0x000030b5, 0x000030f3,
+ 0x000030c1, 0x000030fc, 0x000030e0, 0x000030b7,
+ 0x000030ea, 0x000030f3, 0x000030af, 0x00003099,
+ 0x000030bb, 0x000030f3, 0x000030c1, 0x000030bb,
+ 0x000030f3, 0x000030c8, 0x000030bf, 0x00003099,
+ 0x000030fc, 0x000030b9, 0x000030c6, 0x00003099,
+ 0x000030b7, 0x000030c8, 0x00003099, 0x000030eb,
+ 0x000030c8, 0x000030f3, 0x000030ca, 0x000030ce,
+ 0x000030ce, 0x000030c3, 0x000030c8, 0x000030cf,
+ 0x000030a4, 0x000030c4, 0x000030cf, 0x0000309a,
+ 0x000030fc, 0x000030bb, 0x000030f3, 0x000030c8,
+ 0x000030cf, 0x0000309a, 0x000030fc, 0x000030c4,
+ 0x000030cf, 0x00003099, 0x000030fc, 0x000030ec,
+ 0x000030eb, 0x000030d2, 0x0000309a, 0x000030a2,
+ 0x000030b9, 0x000030c8, 0x000030eb, 0x000030d2,
+ 0x0000309a, 0x000030af, 0x000030eb, 0x000030d2,
+ 0x0000309a, 0x000030b3, 0x000030d2, 0x00003099,
+ 0x000030eb, 0x000030d5, 0x000030a1, 0x000030e9,
+ 0x000030c3, 0x000030c8, 0x00003099, 0x000030d5,
+ 0x000030a3, 0x000030fc, 0x000030c8, 0x000030d5,
+ 0x00003099, 0x000030c3, 0x000030b7, 0x000030a7,
+ 0x000030eb, 0x000030d5, 0x000030e9, 0x000030f3,
+ 0x000030d8, 0x000030af, 0x000030bf, 0x000030fc,
+ 0x000030eb, 0x000030d8, 0x0000309a, 0x000030bd,
+ 0x000030d8, 0x0000309a, 0x000030cb, 0x000030d2,
+ 0x000030d8, 0x000030eb, 0x000030c4, 0x000030d8,
+ 0x0000309a, 0x000030f3, 0x000030b9, 0x000030d8,
+ 0x0000309a, 0x000030fc, 0x000030b7, 0x00003099,
+ 0x000030d8, 0x00003099, 0x000030fc, 0x000030bf,
+ 0x000030db, 0x0000309a, 0x000030a4, 0x000030f3,
+ 0x000030c8, 0x000030db, 0x00003099, 0x000030eb,
+ 0x000030c8, 0x000030db, 0x000030f3, 0x000030db,
+ 0x0000309a, 0x000030f3, 0x000030c8, 0x00003099,
+ 0x000030db, 0x000030fc, 0x000030eb, 0x000030db,
+ 0x000030fc, 0x000030f3, 0x000030de, 0x000030a4,
+ 0x000030af, 0x000030ed, 0x000030de, 0x000030a4,
+ 0x000030eb, 0x000030de, 0x000030c3, 0x000030cf,
+ 0x000030de, 0x000030eb, 0x000030af, 0x000030de,
+ 0x000030f3, 0x000030b7, 0x000030e7, 0x000030f3,
+ 0x000030df, 0x000030af, 0x000030ed, 0x000030f3,
+ 0x000030df, 0x000030ea, 0x000030df, 0x000030ea,
+ 0x000030cf, 0x00003099, 0x000030fc, 0x000030eb,
+ 0x000030e1, 0x000030ab, 0x00003099, 0x000030e1,
+ 0x000030ab, 0x00003099, 0x000030c8, 0x000030f3,
+ 0x000030e1, 0x000030fc, 0x000030c8, 0x000030eb,
+ 0x000030e4, 0x000030fc, 0x000030c8, 0x00003099,
+ 0x000030e4, 0x000030fc, 0x000030eb, 0x000030e6,
+ 0x000030a2, 0x000030f3, 0x000030ea, 0x000030c3,
+ 0x000030c8, 0x000030eb, 0x000030ea, 0x000030e9,
+ 0x000030eb, 0x000030d2, 0x0000309a, 0x000030fc,
+ 0x000030eb, 0x000030fc, 0x000030d5, 0x00003099,
+ 0x000030eb, 0x000030ec, 0x000030e0, 0x000030ec,
+ 0x000030f3, 0x000030c8, 0x000030b1, 0x00003099,
+ 0x000030f3, 0x000030ef, 0x000030c3, 0x000030c8,
+ 0x00000030, 0x000070b9, 0x00000031, 0x000070b9,
+ 0x00000032, 0x000070b9, 0x00000033, 0x000070b9,
+ 0x00000034, 0x000070b9, 0x00000035, 0x000070b9,
+ 0x00000036, 0x000070b9, 0x00000037, 0x000070b9,
+ 0x00000038, 0x000070b9, 0x00000039, 0x000070b9,
+ 0x00000031, 0x00000030, 0x000070b9, 0x00000031,
+ 0x00000031, 0x000070b9, 0x00000031, 0x00000032,
+ 0x000070b9, 0x00000031, 0x00000033, 0x000070b9,
+ 0x00000031, 0x00000034, 0x000070b9, 0x00000031,
+ 0x00000035, 0x000070b9, 0x00000031, 0x00000036,
+ 0x000070b9, 0x00000031, 0x00000037, 0x000070b9,
+ 0x00000031, 0x00000038, 0x000070b9, 0x00000031,
+ 0x00000039, 0x000070b9, 0x00000032, 0x00000030,
+ 0x000070b9, 0x00000032, 0x00000031, 0x000070b9,
+ 0x00000032, 0x00000032, 0x000070b9, 0x00000032,
+ 0x00000033, 0x000070b9, 0x00000032, 0x00000034,
+ 0x000070b9, 0x00000068, 0x00000050, 0x00000061,
+ 0x00000064, 0x00000061, 0x00000041, 0x00000055,
+ 0x00000062, 0x00000061, 0x00000072, 0x0000006f,
+ 0x00000056, 0x00000070, 0x00000063, 0x00005e73,
+ 0x00006210, 0x0000662d, 0x0000548c, 0x00005927,
+ 0x00006b63, 0x0000660e, 0x00006cbb, 0x0000682a,
+ 0x00005f0f, 0x00004f1a, 0x0000793e, 0x00000070,
+ 0x00000041, 0x0000006e, 0x00000041, 0x000003bc,
+ 0x00000041, 0x0000006d, 0x00000041, 0x0000006b,
+ 0x00000041, 0x0000004b, 0x00000042, 0x0000004d,
+ 0x00000042, 0x00000047, 0x00000042, 0x00000063,
+ 0x00000061, 0x0000006c, 0x0000006b, 0x00000063,
+ 0x00000061, 0x0000006c, 0x00000070, 0x00000046,
+ 0x0000006e, 0x00000046, 0x000003bc, 0x00000046,
+ 0x000003bc, 0x00000067, 0x0000006d, 0x00000067,
+ 0x0000006b, 0x00000067, 0x00000048, 0x0000007a,
+ 0x0000006b, 0x00000048, 0x0000007a, 0x0000004d,
+ 0x00000048, 0x0000007a, 0x00000047, 0x00000048,
+ 0x0000007a, 0x00000054, 0x00000048, 0x0000007a,
+ 0x000003bc, 0x0000006c, 0x0000006d, 0x0000006c,
+ 0x00000064, 0x0000006c, 0x0000006b, 0x0000006c,
+ 0x00000066, 0x0000006d, 0x0000006e, 0x0000006d,
+ 0x000003bc, 0x0000006d, 0x0000006d, 0x0000006d,
+ 0x00000063, 0x0000006d, 0x0000006b, 0x0000006d,
+ 0x0000006d, 0x0000006d, 0x00000032, 0x00000063,
+ 0x0000006d, 0x00000032, 0x0000006d, 0x00000032,
+ 0x0000006b, 0x0000006d, 0x00000032, 0x0000006d,
+ 0x0000006d, 0x00000033, 0x00000063, 0x0000006d,
+ 0x00000033, 0x0000006d, 0x00000033, 0x0000006b,
+ 0x0000006d, 0x00000033, 0x0000006d, 0x00002215,
+ 0x00000073, 0x0000006d, 0x00002215, 0x00000073,
+ 0x00000032, 0x00000050, 0x00000061, 0x0000006b,
+ 0x00000050, 0x00000061, 0x0000004d, 0x00000050,
+ 0x00000061, 0x00000047, 0x00000050, 0x00000061,
+ 0x00000072, 0x00000061, 0x00000064, 0x00000072,
+ 0x00000061, 0x00000064, 0x00002215, 0x00000073,
+ 0x00000072, 0x00000061, 0x00000064, 0x00002215,
+ 0x00000073, 0x00000032, 0x00000070, 0x00000073,
+ 0x0000006e, 0x00000073, 0x000003bc, 0x00000073,
+ 0x0000006d, 0x00000073, 0x00000070, 0x00000056,
+ 0x0000006e, 0x00000056, 0x000003bc, 0x00000056,
+ 0x0000006d, 0x00000056, 0x0000006b, 0x00000056,
+ 0x0000004d, 0x00000056, 0x00000070, 0x00000057,
+ 0x0000006e, 0x00000057, 0x000003bc, 0x00000057,
+ 0x0000006d, 0x00000057, 0x0000006b, 0x00000057,
+ 0x0000004d, 0x00000057, 0x0000006b, 0x000003a9,
+ 0x0000004d, 0x000003a9, 0x00000061, 0x0000002e,
+ 0x0000006d, 0x0000002e, 0x00000042, 0x00000071,
+ 0x00000063, 0x00000063, 0x00000063, 0x00000064,
+ 0x00000043, 0x00002215, 0x0000006b, 0x00000067,
+ 0x00000043, 0x0000006f, 0x0000002e, 0x00000064,
+ 0x00000042, 0x00000047, 0x00000079, 0x00000068,
+ 0x00000061, 0x00000048, 0x00000050, 0x00000069,
+ 0x0000006e, 0x0000004b, 0x0000004b, 0x0000004b,
+ 0x0000004d, 0x0000006b, 0x00000074, 0x0000006c,
+ 0x0000006d, 0x0000006c, 0x0000006e, 0x0000006c,
+ 0x0000006f, 0x00000067, 0x0000006c, 0x00000078,
+ 0x0000006d, 0x00000062, 0x0000006d, 0x00000069,
+ 0x0000006c, 0x0000006d, 0x0000006f, 0x0000006c,
+ 0x00000050, 0x00000048, 0x00000070, 0x0000002e,
+ 0x0000006d, 0x0000002e, 0x00000050, 0x00000050,
+ 0x0000004d, 0x00000050, 0x00000052, 0x00000073,
+ 0x00000072, 0x00000053, 0x00000076, 0x00000057,
+ 0x00000062, 0x00000031, 0x000065e5, 0x00000032,
+ 0x000065e5, 0x00000033, 0x000065e5, 0x00000034,
+ 0x000065e5, 0x00000035, 0x000065e5, 0x00000036,
+ 0x000065e5, 0x00000037, 0x000065e5, 0x00000038,
+ 0x000065e5, 0x00000039, 0x000065e5, 0x00000031,
+ 0x00000030, 0x000065e5, 0x00000031, 0x00000031,
+ 0x000065e5, 0x00000031, 0x00000032, 0x000065e5,
+ 0x00000031, 0x00000033, 0x000065e5, 0x00000031,
+ 0x00000034, 0x000065e5, 0x00000031, 0x00000035,
+ 0x000065e5, 0x00000031, 0x00000036, 0x000065e5,
+ 0x00000031, 0x00000037, 0x000065e5, 0x00000031,
+ 0x00000038, 0x000065e5, 0x00000031, 0x00000039,
+ 0x000065e5, 0x00000032, 0x00000030, 0x000065e5,
+ 0x00000032, 0x00000031, 0x000065e5, 0x00000032,
+ 0x00000032, 0x000065e5, 0x00000032, 0x00000033,
+ 0x000065e5, 0x00000032, 0x00000034, 0x000065e5,
+ 0x00000032, 0x00000035, 0x000065e5, 0x00000032,
+ 0x00000036, 0x000065e5, 0x00000032, 0x00000037,
+ 0x000065e5, 0x00000032, 0x00000038, 0x000065e5,
+ 0x00000032, 0x00000039, 0x000065e5, 0x00000033,
+ 0x00000030, 0x000065e5, 0x00000033, 0x00000031,
+ 0x000065e5, 0x00008eca, 0x00008cc8, 0x00006ed1,
+ 0x00004e32, 0x000053e5, 0x00009f9c, 0x00009f9c,
+ 0x00005951, 0x000091d1, 0x00005587, 0x00005948,
+ 0x000061f6, 0x00007669, 0x00007f85, 0x0000863f,
+ 0x000087ba, 0x000088f8, 0x0000908f, 0x00006a02,
+ 0x00006d1b, 0x000070d9, 0x000073de, 0x0000843d,
+ 0x0000916a, 0x000099f1, 0x00004e82, 0x00005375,
+ 0x00006b04, 0x0000721b, 0x0000862d, 0x00009e1e,
+ 0x00005d50, 0x00006feb, 0x000085cd, 0x00008964,
+ 0x000062c9, 0x000081d8, 0x0000881f, 0x00005eca,
+ 0x00006717, 0x00006d6a, 0x000072fc, 0x000090ce,
+ 0x00004f86, 0x000051b7, 0x000052de, 0x000064c4,
+ 0x00006ad3, 0x00007210, 0x000076e7, 0x00008001,
+ 0x00008606, 0x0000865c, 0x00008def, 0x00009732,
+ 0x00009b6f, 0x00009dfa, 0x0000788c, 0x0000797f,
+ 0x00007da0, 0x000083c9, 0x00009304, 0x00009e7f,
+ 0x00008ad6, 0x000058df, 0x00005f04, 0x00007c60,
+ 0x0000807e, 0x00007262, 0x000078ca, 0x00008cc2,
+ 0x000096f7, 0x000058d8, 0x00005c62, 0x00006a13,
+ 0x00006dda, 0x00006f0f, 0x00007d2f, 0x00007e37,
+ 0x0000964b, 0x000052d2, 0x0000808b, 0x000051dc,
+ 0x000051cc, 0x00007a1c, 0x00007dbe, 0x000083f1,
+ 0x00009675, 0x00008b80, 0x000062cf, 0x00006a02,
+ 0x00008afe, 0x00004e39, 0x00005be7, 0x00006012,
+ 0x00007387, 0x00007570, 0x00005317, 0x000078fb,
+ 0x00004fbf, 0x00005fa9, 0x00004e0d, 0x00006ccc,
+ 0x00006578, 0x00007d22, 0x000053c3, 0x0000585e,
+ 0x00007701, 0x00008449, 0x00008aaa, 0x00006bba,
+ 0x00008fb0, 0x00006c88, 0x000062fe, 0x000082e5,
+ 0x000063a0, 0x00007565, 0x00004eae, 0x00005169,
+ 0x000051c9, 0x00006881, 0x00007ce7, 0x0000826f,
+ 0x00008ad2, 0x000091cf, 0x000052f5, 0x00005442,
+ 0x00005973, 0x00005eec, 0x000065c5, 0x00006ffe,
+ 0x0000792a, 0x000095ad, 0x00009a6a, 0x00009e97,
+ 0x00009ece, 0x0000529b, 0x000066c6, 0x00006b77,
+ 0x00008f62, 0x00005e74, 0x00006190, 0x00006200,
+ 0x0000649a, 0x00006f23, 0x00007149, 0x00007489,
+ 0x000079ca, 0x00007df4, 0x0000806f, 0x00008f26,
+ 0x000084ee, 0x00009023, 0x0000934a, 0x00005217,
+ 0x000052a3, 0x000054bd, 0x000070c8, 0x000088c2,
+ 0x00008aaa, 0x00005ec9, 0x00005ff5, 0x0000637b,
+ 0x00006bae, 0x00007c3e, 0x00007375, 0x00004ee4,
+ 0x000056f9, 0x00005be7, 0x00005dba, 0x0000601c,
+ 0x000073b2, 0x00007469, 0x00007f9a, 0x00008046,
+ 0x00009234, 0x000096f6, 0x00009748, 0x00009818,
+ 0x00004f8b, 0x000079ae, 0x000091b4, 0x000096b8,
+ 0x000060e1, 0x00004e86, 0x000050da, 0x00005bee,
+ 0x00005c3f, 0x00006599, 0x00006a02, 0x000071ce,
+ 0x00007642, 0x000084fc, 0x0000907c, 0x00009f8d,
+ 0x00006688, 0x0000962e, 0x00005289, 0x0000677b,
+ 0x000067f3, 0x00006d41, 0x00006e9c, 0x00007409,
+ 0x00007559, 0x0000786b, 0x00007d10, 0x0000985e,
+ 0x0000516d, 0x0000622e, 0x00009678, 0x0000502b,
+ 0x00005d19, 0x00006dea, 0x00008f2a, 0x00005f8b,
+ 0x00006144, 0x00006817, 0x00007387, 0x00009686,
+ 0x00005229, 0x0000540f, 0x00005c65, 0x00006613,
+ 0x0000674e, 0x000068a8, 0x00006ce5, 0x00007406,
+ 0x000075e2, 0x00007f79, 0x000088cf, 0x000088e1,
+ 0x000091cc, 0x000096e2, 0x0000533f, 0x00006eba,
+ 0x0000541d, 0x000071d0, 0x00007498, 0x000085fa,
+ 0x000096a3, 0x00009c57, 0x00009e9f, 0x00006797,
+ 0x00006dcb, 0x000081e8, 0x00007acb, 0x00007b20,
+ 0x00007c92, 0x000072c0, 0x00007099, 0x00008b58,
+ 0x00004ec0, 0x00008336, 0x0000523a, 0x00005207,
+ 0x00005ea6, 0x000062d3, 0x00007cd6, 0x00005b85,
+ 0x00006d1e, 0x000066b4, 0x00008f3b, 0x0000884c,
+ 0x0000964d, 0x0000898b, 0x00005ed3, 0x00005140,
+ 0x000055c0, 0x0000585a, 0x00006674, 0x000051de,
+ 0x0000732a, 0x000076ca, 0x0000793c, 0x0000795e,
+ 0x00007965, 0x0000798f, 0x00009756, 0x00007cbe,
+ 0x00007fbd, 0x00008612, 0x00008af8, 0x00009038,
+ 0x000090fd, 0x000098ef, 0x000098fc, 0x00009928,
+ 0x00009db4, 0x00004fae, 0x000050e7, 0x0000514d,
+ 0x000052c9, 0x000052e4, 0x00005351, 0x0000559d,
+ 0x00005606, 0x00005668, 0x00005840, 0x000058a8,
+ 0x00005c64, 0x00005c6e, 0x00006094, 0x00006168,
+ 0x0000618e, 0x000061f2, 0x0000654f, 0x000065e2,
+ 0x00006691, 0x00006885, 0x00006d77, 0x00006e1a,
+ 0x00006f22, 0x0000716e, 0x0000722b, 0x00007422,
+ 0x00007891, 0x0000793e, 0x00007949, 0x00007948,
+ 0x00007950, 0x00007956, 0x0000795d, 0x0000798d,
+ 0x0000798e, 0x00007a40, 0x00007a81, 0x00007bc0,
+ 0x00007df4, 0x00007e09, 0x00007e41, 0x00007f72,
+ 0x00008005, 0x000081ed, 0x00008279, 0x00008279,
+ 0x00008457, 0x00008910, 0x00008996, 0x00008b01,
+ 0x00008b39, 0x00008cd3, 0x00008d08, 0x00008fb6,
+ 0x00009038, 0x000096e3, 0x000097ff, 0x0000983b,
+ 0x00000066, 0x00000066, 0x00000066, 0x00000069,
+ 0x00000066, 0x0000006c, 0x00000066, 0x00000066,
+ 0x00000069, 0x00000066, 0x00000066, 0x0000006c,
+ 0x00000073, 0x00000074, 0x00000073, 0x00000074,
+ 0x00000574, 0x00000576, 0x00000574, 0x00000565,
+ 0x00000574, 0x0000056b, 0x0000057e, 0x00000576,
+ 0x00000574, 0x0000056d, 0x000005d9, 0x000005b4,
+ 0x000005f2, 0x000005b7, 0x000005e2, 0x000005d0,
+ 0x000005d3, 0x000005d4, 0x000005db, 0x000005dc,
+ 0x000005dd, 0x000005e8, 0x000005ea, 0x0000002b,
+ 0x000005e9, 0x000005c1, 0x000005e9, 0x000005c2,
+ 0x000005e9, 0x000005bc, 0x000005c1, 0x000005e9,
+ 0x000005bc, 0x000005c2, 0x000005d0, 0x000005b7,
+ 0x000005d0, 0x000005b8, 0x000005d0, 0x000005bc,
+ 0x000005d1, 0x000005bc, 0x000005d2, 0x000005bc,
+ 0x000005d3, 0x000005bc, 0x000005d4, 0x000005bc,
+ 0x000005d5, 0x000005bc, 0x000005d6, 0x000005bc,
+ 0x000005d8, 0x000005bc, 0x000005d9, 0x000005bc,
+ 0x000005da, 0x000005bc, 0x000005db, 0x000005bc,
+ 0x000005dc, 0x000005bc, 0x000005de, 0x000005bc,
+ 0x000005e0, 0x000005bc, 0x000005e1, 0x000005bc,
+ 0x000005e3, 0x000005bc, 0x000005e4, 0x000005bc,
+ 0x000005e6, 0x000005bc, 0x000005e7, 0x000005bc,
+ 0x000005e8, 0x000005bc, 0x000005e9, 0x000005bc,
+ 0x000005ea, 0x000005bc, 0x000005d5, 0x000005b9,
+ 0x000005d1, 0x000005bf, 0x000005db, 0x000005bf,
+ 0x000005e4, 0x000005bf, 0x000005d0, 0x000005dc,
+ 0x00000671, 0x00000671, 0x0000067b, 0x0000067b,
+ 0x0000067b, 0x0000067b, 0x0000067e, 0x0000067e,
+ 0x0000067e, 0x0000067e, 0x00000680, 0x00000680,
+ 0x00000680, 0x00000680, 0x0000067a, 0x0000067a,
+ 0x0000067a, 0x0000067a, 0x0000067f, 0x0000067f,
+ 0x0000067f, 0x0000067f, 0x00000679, 0x00000679,
+ 0x00000679, 0x00000679, 0x000006a4, 0x000006a4,
+ 0x000006a4, 0x000006a4, 0x000006a6, 0x000006a6,
+ 0x000006a6, 0x000006a6, 0x00000684, 0x00000684,
+ 0x00000684, 0x00000684, 0x00000683, 0x00000683,
+ 0x00000683, 0x00000683, 0x00000686, 0x00000686,
+ 0x00000686, 0x00000686, 0x00000687, 0x00000687,
+ 0x00000687, 0x00000687, 0x0000068d, 0x0000068d,
+ 0x0000068c, 0x0000068c, 0x0000068e, 0x0000068e,
+ 0x00000688, 0x00000688, 0x00000698, 0x00000698,
+ 0x00000691, 0x00000691, 0x000006a9, 0x000006a9,
+ 0x000006a9, 0x000006a9, 0x000006af, 0x000006af,
+ 0x000006af, 0x000006af, 0x000006b3, 0x000006b3,
+ 0x000006b3, 0x000006b3, 0x000006b1, 0x000006b1,
+ 0x000006b1, 0x000006b1, 0x000006ba, 0x000006ba,
+ 0x000006bb, 0x000006bb, 0x000006bb, 0x000006bb,
+ 0x000006d5, 0x00000654, 0x000006d5, 0x00000654,
+ 0x000006c1, 0x000006c1, 0x000006c1, 0x000006c1,
+ 0x000006be, 0x000006be, 0x000006be, 0x000006be,
+ 0x000006d2, 0x000006d2, 0x000006d2, 0x00000654,
+ 0x000006d2, 0x00000654, 0x000006ad, 0x000006ad,
+ 0x000006ad, 0x000006ad, 0x000006c7, 0x000006c7,
+ 0x000006c6, 0x000006c6, 0x000006c8, 0x000006c8,
+ 0x000006c7, 0x00000674, 0x000006cb, 0x000006cb,
+ 0x000006c5, 0x000006c5, 0x000006c9, 0x000006c9,
+ 0x000006d0, 0x000006d0, 0x000006d0, 0x000006d0,
+ 0x00000649, 0x00000649, 0x0000064a, 0x00000654,
+ 0x00000627, 0x0000064a, 0x00000654, 0x00000627,
+ 0x0000064a, 0x00000654, 0x000006d5, 0x0000064a,
+ 0x00000654, 0x000006d5, 0x0000064a, 0x00000654,
+ 0x00000648, 0x0000064a, 0x00000654, 0x00000648,
+ 0x0000064a, 0x00000654, 0x000006c7, 0x0000064a,
+ 0x00000654, 0x000006c7, 0x0000064a, 0x00000654,
+ 0x000006c6, 0x0000064a, 0x00000654, 0x000006c6,
+ 0x0000064a, 0x00000654, 0x000006c8, 0x0000064a,
+ 0x00000654, 0x000006c8, 0x0000064a, 0x00000654,
+ 0x000006d0, 0x0000064a, 0x00000654, 0x000006d0,
+ 0x0000064a, 0x00000654, 0x000006d0, 0x0000064a,
+ 0x00000654, 0x00000649, 0x0000064a, 0x00000654,
+ 0x00000649, 0x0000064a, 0x00000654, 0x00000649,
+ 0x000006cc, 0x000006cc, 0x000006cc, 0x000006cc,
+ 0x0000064a, 0x00000654, 0x0000062c, 0x0000064a,
+ 0x00000654, 0x0000062d, 0x0000064a, 0x00000654,
+ 0x00000645, 0x0000064a, 0x00000654, 0x00000649,
+ 0x0000064a, 0x00000654, 0x0000064a, 0x00000628,
+ 0x0000062c, 0x00000628, 0x0000062d, 0x00000628,
+ 0x0000062e, 0x00000628, 0x00000645, 0x00000628,
+ 0x00000649, 0x00000628, 0x0000064a, 0x0000062a,
+ 0x0000062c, 0x0000062a, 0x0000062d, 0x0000062a,
+ 0x0000062e, 0x0000062a, 0x00000645, 0x0000062a,
+ 0x00000649, 0x0000062a, 0x0000064a, 0x0000062b,
+ 0x0000062c, 0x0000062b, 0x00000645, 0x0000062b,
+ 0x00000649, 0x0000062b, 0x0000064a, 0x0000062c,
+ 0x0000062d, 0x0000062c, 0x00000645, 0x0000062d,
+ 0x0000062c, 0x0000062d, 0x00000645, 0x0000062e,
+ 0x0000062c, 0x0000062e, 0x0000062d, 0x0000062e,
+ 0x00000645, 0x00000633, 0x0000062c, 0x00000633,
+ 0x0000062d, 0x00000633, 0x0000062e, 0x00000633,
+ 0x00000645, 0x00000635, 0x0000062d, 0x00000635,
+ 0x00000645, 0x00000636, 0x0000062c, 0x00000636,
+ 0x0000062d, 0x00000636, 0x0000062e, 0x00000636,
+ 0x00000645, 0x00000637, 0x0000062d, 0x00000637,
+ 0x00000645, 0x00000638, 0x00000645, 0x00000639,
+ 0x0000062c, 0x00000639, 0x00000645, 0x0000063a,
+ 0x0000062c, 0x0000063a, 0x00000645, 0x00000641,
+ 0x0000062c, 0x00000641, 0x0000062d, 0x00000641,
+ 0x0000062e, 0x00000641, 0x00000645, 0x00000641,
+ 0x00000649, 0x00000641, 0x0000064a, 0x00000642,
+ 0x0000062d, 0x00000642, 0x00000645, 0x00000642,
+ 0x00000649, 0x00000642, 0x0000064a, 0x00000643,
+ 0x00000627, 0x00000643, 0x0000062c, 0x00000643,
+ 0x0000062d, 0x00000643, 0x0000062e, 0x00000643,
+ 0x00000644, 0x00000643, 0x00000645, 0x00000643,
+ 0x00000649, 0x00000643, 0x0000064a, 0x00000644,
+ 0x0000062c, 0x00000644, 0x0000062d, 0x00000644,
+ 0x0000062e, 0x00000644, 0x00000645, 0x00000644,
+ 0x00000649, 0x00000644, 0x0000064a, 0x00000645,
+ 0x0000062c, 0x00000645, 0x0000062d, 0x00000645,
+ 0x0000062e, 0x00000645, 0x00000645, 0x00000645,
+ 0x00000649, 0x00000645, 0x0000064a, 0x00000646,
+ 0x0000062c, 0x00000646, 0x0000062d, 0x00000646,
+ 0x0000062e, 0x00000646, 0x00000645, 0x00000646,
+ 0x00000649, 0x00000646, 0x0000064a, 0x00000647,
+ 0x0000062c, 0x00000647, 0x00000645, 0x00000647,
+ 0x00000649, 0x00000647, 0x0000064a, 0x0000064a,
+ 0x0000062c, 0x0000064a, 0x0000062d, 0x0000064a,
+ 0x0000062e, 0x0000064a, 0x00000645, 0x0000064a,
+ 0x00000649, 0x0000064a, 0x0000064a, 0x00000630,
+ 0x00000670, 0x00000631, 0x00000670, 0x00000649,
+ 0x00000670, 0x00000020, 0x0000064c, 0x00000651,
+ 0x00000020, 0x0000064d, 0x00000651, 0x00000020,
+ 0x0000064e, 0x00000651, 0x00000020, 0x0000064f,
+ 0x00000651, 0x00000020, 0x00000650, 0x00000651,
+ 0x00000020, 0x00000651, 0x00000670, 0x0000064a,
+ 0x00000654, 0x00000631, 0x0000064a, 0x00000654,
+ 0x00000632, 0x0000064a, 0x00000654, 0x00000645,
+ 0x0000064a, 0x00000654, 0x00000646, 0x0000064a,
+ 0x00000654, 0x00000649, 0x0000064a, 0x00000654,
+ 0x0000064a, 0x00000628, 0x00000631, 0x00000628,
+ 0x00000632, 0x00000628, 0x00000645, 0x00000628,
+ 0x00000646, 0x00000628, 0x00000649, 0x00000628,
+ 0x0000064a, 0x0000062a, 0x00000631, 0x0000062a,
+ 0x00000632, 0x0000062a, 0x00000645, 0x0000062a,
+ 0x00000646, 0x0000062a, 0x00000649, 0x0000062a,
+ 0x0000064a, 0x0000062b, 0x00000631, 0x0000062b,
+ 0x00000632, 0x0000062b, 0x00000645, 0x0000062b,
+ 0x00000646, 0x0000062b, 0x00000649, 0x0000062b,
+ 0x0000064a, 0x00000641, 0x00000649, 0x00000641,
+ 0x0000064a, 0x00000642, 0x00000649, 0x00000642,
+ 0x0000064a, 0x00000643, 0x00000627, 0x00000643,
+ 0x00000644, 0x00000643, 0x00000645, 0x00000643,
+ 0x00000649, 0x00000643, 0x0000064a, 0x00000644,
+ 0x00000645, 0x00000644, 0x00000649, 0x00000644,
+ 0x0000064a, 0x00000645, 0x00000627, 0x00000645,
+ 0x00000645, 0x00000646, 0x00000631, 0x00000646,
+ 0x00000632, 0x00000646, 0x00000645, 0x00000646,
+ 0x00000646, 0x00000646, 0x00000649, 0x00000646,
+ 0x0000064a, 0x00000649, 0x00000670, 0x0000064a,
+ 0x00000631, 0x0000064a, 0x00000632, 0x0000064a,
+ 0x00000645, 0x0000064a, 0x00000646, 0x0000064a,
+ 0x00000649, 0x0000064a, 0x0000064a, 0x0000064a,
+ 0x00000654, 0x0000062c, 0x0000064a, 0x00000654,
+ 0x0000062d, 0x0000064a, 0x00000654, 0x0000062e,
+ 0x0000064a, 0x00000654, 0x00000645, 0x0000064a,
+ 0x00000654, 0x00000647, 0x00000628, 0x0000062c,
+ 0x00000628, 0x0000062d, 0x00000628, 0x0000062e,
+ 0x00000628, 0x00000645, 0x00000628, 0x00000647,
+ 0x0000062a, 0x0000062c, 0x0000062a, 0x0000062d,
+ 0x0000062a, 0x0000062e, 0x0000062a, 0x00000645,
+ 0x0000062a, 0x00000647, 0x0000062b, 0x00000645,
+ 0x0000062c, 0x0000062d, 0x0000062c, 0x00000645,
+ 0x0000062d, 0x0000062c, 0x0000062d, 0x00000645,
+ 0x0000062e, 0x0000062c, 0x0000062e, 0x00000645,
+ 0x00000633, 0x0000062c, 0x00000633, 0x0000062d,
+ 0x00000633, 0x0000062e, 0x00000633, 0x00000645,
+ 0x00000635, 0x0000062d, 0x00000635, 0x0000062e,
+ 0x00000635, 0x00000645, 0x00000636, 0x0000062c,
+ 0x00000636, 0x0000062d, 0x00000636, 0x0000062e,
+ 0x00000636, 0x00000645, 0x00000637, 0x0000062d,
+ 0x00000638, 0x00000645, 0x00000639, 0x0000062c,
+ 0x00000639, 0x00000645, 0x0000063a, 0x0000062c,
+ 0x0000063a, 0x00000645, 0x00000641, 0x0000062c,
+ 0x00000641, 0x0000062d, 0x00000641, 0x0000062e,
+ 0x00000641, 0x00000645, 0x00000642, 0x0000062d,
+ 0x00000642, 0x00000645, 0x00000643, 0x0000062c,
+ 0x00000643, 0x0000062d, 0x00000643, 0x0000062e,
+ 0x00000643, 0x00000644, 0x00000643, 0x00000645,
+ 0x00000644, 0x0000062c, 0x00000644, 0x0000062d,
+ 0x00000644, 0x0000062e, 0x00000644, 0x00000645,
+ 0x00000644, 0x00000647, 0x00000645, 0x0000062c,
+ 0x00000645, 0x0000062d, 0x00000645, 0x0000062e,
+ 0x00000645, 0x00000645, 0x00000646, 0x0000062c,
+ 0x00000646, 0x0000062d, 0x00000646, 0x0000062e,
+ 0x00000646, 0x00000645, 0x00000646, 0x00000647,
+ 0x00000647, 0x0000062c, 0x00000647, 0x00000645,
+ 0x00000647, 0x00000670, 0x0000064a, 0x0000062c,
+ 0x0000064a, 0x0000062d, 0x0000064a, 0x0000062e,
+ 0x0000064a, 0x00000645, 0x0000064a, 0x00000647,
+ 0x0000064a, 0x00000654, 0x00000645, 0x0000064a,
+ 0x00000654, 0x00000647, 0x00000628, 0x00000645,
+ 0x00000628, 0x00000647, 0x0000062a, 0x00000645,
+ 0x0000062a, 0x00000647, 0x0000062b, 0x00000645,
+ 0x0000062b, 0x00000647, 0x00000633, 0x00000645,
+ 0x00000633, 0x00000647, 0x00000634, 0x00000645,
+ 0x00000634, 0x00000647, 0x00000643, 0x00000644,
+ 0x00000643, 0x00000645, 0x00000644, 0x00000645,
+ 0x00000646, 0x00000645, 0x00000646, 0x00000647,
+ 0x0000064a, 0x00000645, 0x0000064a, 0x00000647,
+ 0x00000640, 0x0000064e, 0x00000651, 0x00000640,
+ 0x0000064f, 0x00000651, 0x00000640, 0x00000650,
+ 0x00000651, 0x00000637, 0x00000649, 0x00000637,
+ 0x0000064a, 0x00000639, 0x00000649, 0x00000639,
+ 0x0000064a, 0x0000063a, 0x00000649, 0x0000063a,
+ 0x0000064a, 0x00000633, 0x00000649, 0x00000633,
+ 0x0000064a, 0x00000634, 0x00000649, 0x00000634,
+ 0x0000064a, 0x0000062d, 0x00000649, 0x0000062d,
+ 0x0000064a, 0x0000062c, 0x00000649, 0x0000062c,
+ 0x0000064a, 0x0000062e, 0x00000649, 0x0000062e,
+ 0x0000064a, 0x00000635, 0x00000649, 0x00000635,
+ 0x0000064a, 0x00000636, 0x00000649, 0x00000636,
+ 0x0000064a, 0x00000634, 0x0000062c, 0x00000634,
+ 0x0000062d, 0x00000634, 0x0000062e, 0x00000634,
+ 0x00000645, 0x00000634, 0x00000631, 0x00000633,
+ 0x00000631, 0x00000635, 0x00000631, 0x00000636,
+ 0x00000631, 0x00000637, 0x00000649, 0x00000637,
+ 0x0000064a, 0x00000639, 0x00000649, 0x00000639,
+ 0x0000064a, 0x0000063a, 0x00000649, 0x0000063a,
+ 0x0000064a, 0x00000633, 0x00000649, 0x00000633,
+ 0x0000064a, 0x00000634, 0x00000649, 0x00000634,
+ 0x0000064a, 0x0000062d, 0x00000649, 0x0000062d,
+ 0x0000064a, 0x0000062c, 0x00000649, 0x0000062c,
+ 0x0000064a, 0x0000062e, 0x00000649, 0x0000062e,
+ 0x0000064a, 0x00000635, 0x00000649, 0x00000635,
+ 0x0000064a, 0x00000636, 0x00000649, 0x00000636,
+ 0x0000064a, 0x00000634, 0x0000062c, 0x00000634,
+ 0x0000062d, 0x00000634, 0x0000062e, 0x00000634,
+ 0x00000645, 0x00000634, 0x00000631, 0x00000633,
+ 0x00000631, 0x00000635, 0x00000631, 0x00000636,
+ 0x00000631, 0x00000634, 0x0000062c, 0x00000634,
+ 0x0000062d, 0x00000634, 0x0000062e, 0x00000634,
+ 0x00000645, 0x00000633, 0x00000647, 0x00000634,
+ 0x00000647, 0x00000637, 0x00000645, 0x00000633,
+ 0x0000062c, 0x00000633, 0x0000062d, 0x00000633,
+ 0x0000062e, 0x00000634, 0x0000062c, 0x00000634,
+ 0x0000062d, 0x00000634, 0x0000062e, 0x00000637,
+ 0x00000645, 0x00000638, 0x00000645, 0x00000627,
+ 0x0000064b, 0x00000627, 0x0000064b, 0x0000062a,
+ 0x0000062c, 0x00000645, 0x0000062a, 0x0000062d,
+ 0x0000062c, 0x0000062a, 0x0000062d, 0x0000062c,
+ 0x0000062a, 0x0000062d, 0x00000645, 0x0000062a,
+ 0x0000062e, 0x00000645, 0x0000062a, 0x00000645,
+ 0x0000062c, 0x0000062a, 0x00000645, 0x0000062d,
+ 0x0000062a, 0x00000645, 0x0000062e, 0x0000062c,
+ 0x00000645, 0x0000062d, 0x0000062c, 0x00000645,
+ 0x0000062d, 0x0000062d, 0x00000645, 0x0000064a,
+ 0x0000062d, 0x00000645, 0x00000649, 0x00000633,
+ 0x0000062d, 0x0000062c, 0x00000633, 0x0000062c,
+ 0x0000062d, 0x00000633, 0x0000062c, 0x00000649,
+ 0x00000633, 0x00000645, 0x0000062d, 0x00000633,
+ 0x00000645, 0x0000062d, 0x00000633, 0x00000645,
+ 0x0000062c, 0x00000633, 0x00000645, 0x00000645,
+ 0x00000633, 0x00000645, 0x00000645, 0x00000635,
+ 0x0000062d, 0x0000062d, 0x00000635, 0x0000062d,
+ 0x0000062d, 0x00000635, 0x00000645, 0x00000645,
+ 0x00000634, 0x0000062d, 0x00000645, 0x00000634,
+ 0x0000062d, 0x00000645, 0x00000634, 0x0000062c,
+ 0x0000064a, 0x00000634, 0x00000645, 0x0000062e,
+ 0x00000634, 0x00000645, 0x0000062e, 0x00000634,
+ 0x00000645, 0x00000645, 0x00000634, 0x00000645,
+ 0x00000645, 0x00000636, 0x0000062d, 0x00000649,
+ 0x00000636, 0x0000062e, 0x00000645, 0x00000636,
+ 0x0000062e, 0x00000645, 0x00000637, 0x00000645,
+ 0x0000062d, 0x00000637, 0x00000645, 0x0000062d,
+ 0x00000637, 0x00000645, 0x00000645, 0x00000637,
+ 0x00000645, 0x0000064a, 0x00000639, 0x0000062c,
+ 0x00000645, 0x00000639, 0x00000645, 0x00000645,
+ 0x00000639, 0x00000645, 0x00000645, 0x00000639,
+ 0x00000645, 0x00000649, 0x0000063a, 0x00000645,
+ 0x00000645, 0x0000063a, 0x00000645, 0x0000064a,
+ 0x0000063a, 0x00000645, 0x00000649, 0x00000641,
+ 0x0000062e, 0x00000645, 0x00000641, 0x0000062e,
+ 0x00000645, 0x00000642, 0x00000645, 0x0000062d,
+ 0x00000642, 0x00000645, 0x00000645, 0x00000644,
+ 0x0000062d, 0x00000645, 0x00000644, 0x0000062d,
+ 0x0000064a, 0x00000644, 0x0000062d, 0x00000649,
+ 0x00000644, 0x0000062c, 0x0000062c, 0x00000644,
+ 0x0000062c, 0x0000062c, 0x00000644, 0x0000062e,
+ 0x00000645, 0x00000644, 0x0000062e, 0x00000645,
+ 0x00000644, 0x00000645, 0x0000062d, 0x00000644,
+ 0x00000645, 0x0000062d, 0x00000645, 0x0000062d,
+ 0x0000062c, 0x00000645, 0x0000062d, 0x00000645,
+ 0x00000645, 0x0000062d, 0x0000064a, 0x00000645,
+ 0x0000062c, 0x0000062d, 0x00000645, 0x0000062c,
+ 0x00000645, 0x00000645, 0x0000062e, 0x0000062c,
+ 0x00000645, 0x0000062e, 0x00000645, 0x00000645,
+ 0x0000062c, 0x0000062e, 0x00000647, 0x00000645,
+ 0x0000062c, 0x00000647, 0x00000645, 0x00000645,
+ 0x00000646, 0x0000062d, 0x00000645, 0x00000646,
+ 0x0000062d, 0x00000649, 0x00000646, 0x0000062c,
+ 0x00000645, 0x00000646, 0x0000062c, 0x00000645,
+ 0x00000646, 0x0000062c, 0x00000649, 0x00000646,
+ 0x00000645, 0x0000064a, 0x00000646, 0x00000645,
+ 0x00000649, 0x0000064a, 0x00000645, 0x00000645,
+ 0x0000064a, 0x00000645, 0x00000645, 0x00000628,
+ 0x0000062e, 0x0000064a, 0x0000062a, 0x0000062c,
+ 0x0000064a, 0x0000062a, 0x0000062c, 0x00000649,
+ 0x0000062a, 0x0000062e, 0x0000064a, 0x0000062a,
+ 0x0000062e, 0x00000649, 0x0000062a, 0x00000645,
+ 0x0000064a, 0x0000062a, 0x00000645, 0x00000649,
+ 0x0000062c, 0x00000645, 0x0000064a, 0x0000062c,
+ 0x0000062d, 0x00000649, 0x0000062c, 0x00000645,
+ 0x00000649, 0x00000633, 0x0000062e, 0x00000649,
+ 0x00000635, 0x0000062d, 0x0000064a, 0x00000634,
+ 0x0000062d, 0x0000064a, 0x00000636, 0x0000062d,
+ 0x0000064a, 0x00000644, 0x0000062c, 0x0000064a,
+ 0x00000644, 0x00000645, 0x0000064a, 0x0000064a,
+ 0x0000062d, 0x0000064a, 0x0000064a, 0x0000062c,
+ 0x0000064a, 0x0000064a, 0x00000645, 0x0000064a,
+ 0x00000645, 0x00000645, 0x0000064a, 0x00000642,
+ 0x00000645, 0x0000064a, 0x00000646, 0x0000062d,
+ 0x0000064a, 0x00000642, 0x00000645, 0x0000062d,
+ 0x00000644, 0x0000062d, 0x00000645, 0x00000639,
+ 0x00000645, 0x0000064a, 0x00000643, 0x00000645,
+ 0x0000064a, 0x00000646, 0x0000062c, 0x0000062d,
+ 0x00000645, 0x0000062e, 0x0000064a, 0x00000644,
+ 0x0000062c, 0x00000645, 0x00000643, 0x00000645,
+ 0x00000645, 0x00000644, 0x0000062c, 0x00000645,
+ 0x00000646, 0x0000062c, 0x0000062d, 0x0000062c,
+ 0x0000062d, 0x0000064a, 0x0000062d, 0x0000062c,
+ 0x0000064a, 0x00000645, 0x0000062c, 0x0000064a,
+ 0x00000641, 0x00000645, 0x0000064a, 0x00000628,
+ 0x0000062d, 0x0000064a, 0x00000643, 0x00000645,
+ 0x00000645, 0x00000639, 0x0000062c, 0x00000645,
+ 0x00000635, 0x00000645, 0x00000645, 0x00000633,
+ 0x0000062e, 0x0000064a, 0x00000646, 0x0000062c,
+ 0x0000064a, 0x00000635, 0x00000644, 0x000006d2,
+ 0x00000642, 0x00000644, 0x000006d2, 0x00000627,
+ 0x00000644, 0x00000644, 0x00000647, 0x00000627,
+ 0x00000643, 0x00000628, 0x00000631, 0x00000645,
+ 0x0000062d, 0x00000645, 0x0000062f, 0x00000635,
+ 0x00000644, 0x00000639, 0x00000645, 0x00000631,
+ 0x00000633, 0x00000648, 0x00000644, 0x00000639,
+ 0x00000644, 0x0000064a, 0x00000647, 0x00000648,
+ 0x00000633, 0x00000644, 0x00000645, 0x00000635,
+ 0x00000644, 0x00000649, 0x00000635, 0x00000644,
+ 0x00000649, 0x00000020, 0x00000627, 0x00000644,
+ 0x00000644, 0x00000647, 0x00000020, 0x00000639,
+ 0x00000644, 0x0000064a, 0x00000647, 0x00000020,
+ 0x00000648, 0x00000633, 0x00000644, 0x00000645,
+ 0x0000062c, 0x00000644, 0x00000020, 0x0000062c,
+ 0x00000644, 0x00000627, 0x00000644, 0x00000647,
+ 0x00000631, 0x000006cc, 0x00000627, 0x00000644,
+ 0x0000002e, 0x0000002e, 0x00002014, 0x00002013,
+ 0x0000005f, 0x0000005f, 0x00000028, 0x00000029,
+ 0x0000007b, 0x0000007d, 0x00003014, 0x00003015,
+ 0x00003010, 0x00003011, 0x0000300a, 0x0000300b,
+ 0x00003008, 0x00003009, 0x0000300c, 0x0000300d,
+ 0x0000300e, 0x0000300f, 0x00000020, 0x00000305,
+ 0x00000020, 0x00000305, 0x00000020, 0x00000305,
+ 0x00000020, 0x00000305, 0x0000005f, 0x0000005f,
+ 0x0000005f, 0x0000002c, 0x00003001, 0x0000002e,
+ 0x0000003b, 0x0000003a, 0x0000003f, 0x00000021,
+ 0x00002014, 0x00000028, 0x00000029, 0x0000007b,
+ 0x0000007d, 0x00003014, 0x00003015, 0x00000023,
+ 0x00000026, 0x0000002a, 0x0000002b, 0x0000002d,
+ 0x0000003c, 0x0000003e, 0x0000003d, 0x0000005c,
+ 0x00000024, 0x00000025, 0x00000040, 0x00000020,
+ 0x0000064b, 0x00000640, 0x0000064b, 0x00000020,
+ 0x0000064c, 0x00000020, 0x0000064d, 0x00000020,
+ 0x0000064e, 0x00000640, 0x0000064e, 0x00000020,
+ 0x0000064f, 0x00000640, 0x0000064f, 0x00000020,
+ 0x00000650, 0x00000640, 0x00000650, 0x00000020,
+ 0x00000651, 0x00000640, 0x00000651, 0x00000020,
+ 0x00000652, 0x00000640, 0x00000652, 0x00000621,
+ 0x00000627, 0x00000653, 0x00000627, 0x00000653,
+ 0x00000627, 0x00000654, 0x00000627, 0x00000654,
+ 0x00000648, 0x00000654, 0x00000648, 0x00000654,
+ 0x00000627, 0x00000655, 0x00000627, 0x00000655,
+ 0x0000064a, 0x00000654, 0x0000064a, 0x00000654,
+ 0x0000064a, 0x00000654, 0x0000064a, 0x00000654,
+ 0x00000627, 0x00000627, 0x00000628, 0x00000628,
+ 0x00000628, 0x00000628, 0x00000629, 0x00000629,
+ 0x0000062a, 0x0000062a, 0x0000062a, 0x0000062a,
+ 0x0000062b, 0x0000062b, 0x0000062b, 0x0000062b,
+ 0x0000062c, 0x0000062c, 0x0000062c, 0x0000062c,
+ 0x0000062d, 0x0000062d, 0x0000062d, 0x0000062d,
+ 0x0000062e, 0x0000062e, 0x0000062e, 0x0000062e,
+ 0x0000062f, 0x0000062f, 0x00000630, 0x00000630,
+ 0x00000631, 0x00000631, 0x00000632, 0x00000632,
+ 0x00000633, 0x00000633, 0x00000633, 0x00000633,
+ 0x00000634, 0x00000634, 0x00000634, 0x00000634,
+ 0x00000635, 0x00000635, 0x00000635, 0x00000635,
+ 0x00000636, 0x00000636, 0x00000636, 0x00000636,
+ 0x00000637, 0x00000637, 0x00000637, 0x00000637,
+ 0x00000638, 0x00000638, 0x00000638, 0x00000638,
+ 0x00000639, 0x00000639, 0x00000639, 0x00000639,
+ 0x0000063a, 0x0000063a, 0x0000063a, 0x0000063a,
+ 0x00000641, 0x00000641, 0x00000641, 0x00000641,
+ 0x00000642, 0x00000642, 0x00000642, 0x00000642,
+ 0x00000643, 0x00000643, 0x00000643, 0x00000643,
+ 0x00000644, 0x00000644, 0x00000644, 0x00000644,
+ 0x00000645, 0x00000645, 0x00000645, 0x00000645,
+ 0x00000646, 0x00000646, 0x00000646, 0x00000646,
+ 0x00000647, 0x00000647, 0x00000647, 0x00000647,
+ 0x00000648, 0x00000648, 0x00000649, 0x00000649,
+ 0x0000064a, 0x0000064a, 0x0000064a, 0x0000064a,
+ 0x00000644, 0x00000627, 0x00000653, 0x00000644,
+ 0x00000627, 0x00000653, 0x00000644, 0x00000627,
+ 0x00000654, 0x00000644, 0x00000627, 0x00000654,
+ 0x00000644, 0x00000627, 0x00000655, 0x00000644,
+ 0x00000627, 0x00000655, 0x00000644, 0x00000627,
+ 0x00000644, 0x00000627, 0x00000021, 0x00000022,
+ 0x00000023, 0x00000024, 0x00000025, 0x00000026,
+ 0x00000027, 0x00000028, 0x00000029, 0x0000002a,
+ 0x0000002b, 0x0000002c, 0x0000002d, 0x0000002e,
+ 0x0000002f, 0x00000030, 0x00000031, 0x00000032,
+ 0x00000033, 0x00000034, 0x00000035, 0x00000036,
+ 0x00000037, 0x00000038, 0x00000039, 0x0000003a,
+ 0x0000003b, 0x0000003c, 0x0000003d, 0x0000003e,
+ 0x0000003f, 0x00000040, 0x00000041, 0x00000042,
+ 0x00000043, 0x00000044, 0x00000045, 0x00000046,
+ 0x00000047, 0x00000048, 0x00000049, 0x0000004a,
+ 0x0000004b, 0x0000004c, 0x0000004d, 0x0000004e,
+ 0x0000004f, 0x00000050, 0x00000051, 0x00000052,
+ 0x00000053, 0x00000054, 0x00000055, 0x00000056,
+ 0x00000057, 0x00000058, 0x00000059, 0x0000005a,
+ 0x0000005b, 0x0000005c, 0x0000005d, 0x0000005e,
+ 0x0000005f, 0x00000060, 0x00000061, 0x00000062,
+ 0x00000063, 0x00000064, 0x00000065, 0x00000066,
+ 0x00000067, 0x00000068, 0x00000069, 0x0000006a,
+ 0x0000006b, 0x0000006c, 0x0000006d, 0x0000006e,
+ 0x0000006f, 0x00000070, 0x00000071, 0x00000072,
+ 0x00000073, 0x00000074, 0x00000075, 0x00000076,
+ 0x00000077, 0x00000078, 0x00000079, 0x0000007a,
+ 0x0000007b, 0x0000007c, 0x0000007d, 0x0000007e,
+ 0x00002985, 0x00002986, 0x00003002, 0x0000300c,
+ 0x0000300d, 0x00003001, 0x000030fb, 0x000030f2,
+ 0x000030a1, 0x000030a3, 0x000030a5, 0x000030a7,
+ 0x000030a9, 0x000030e3, 0x000030e5, 0x000030e7,
+ 0x000030c3, 0x000030fc, 0x000030a2, 0x000030a4,
+ 0x000030a6, 0x000030a8, 0x000030aa, 0x000030ab,
+ 0x000030ad, 0x000030af, 0x000030b1, 0x000030b3,
+ 0x000030b5, 0x000030b7, 0x000030b9, 0x000030bb,
+ 0x000030bd, 0x000030bf, 0x000030c1, 0x000030c4,
+ 0x000030c6, 0x000030c8, 0x000030ca, 0x000030cb,
+ 0x000030cc, 0x000030cd, 0x000030ce, 0x000030cf,
+ 0x000030d2, 0x000030d5, 0x000030d8, 0x000030db,
+ 0x000030de, 0x000030df, 0x000030e0, 0x000030e1,
+ 0x000030e2, 0x000030e4, 0x000030e6, 0x000030e8,
+ 0x000030e9, 0x000030ea, 0x000030eb, 0x000030ec,
+ 0x000030ed, 0x000030ef, 0x000030f3, 0x00003099,
+ 0x0000309a, 0x00001160, 0x00001100, 0x00001101,
+ 0x000011aa, 0x00001102, 0x000011ac, 0x000011ad,
+ 0x00001103, 0x00001104, 0x00001105, 0x000011b0,
+ 0x000011b1, 0x000011b2, 0x000011b3, 0x000011b4,
+ 0x000011b5, 0x0000111a, 0x00001106, 0x00001107,
+ 0x00001108, 0x00001121, 0x00001109, 0x0000110a,
+ 0x0000110b, 0x0000110c, 0x0000110d, 0x0000110e,
+ 0x0000110f, 0x00001110, 0x00001111, 0x00001112,
+ 0x00001161, 0x00001162, 0x00001163, 0x00001164,
+ 0x00001165, 0x00001166, 0x00001167, 0x00001168,
+ 0x00001169, 0x0000116a, 0x0000116b, 0x0000116c,
+ 0x0000116d, 0x0000116e, 0x0000116f, 0x00001170,
+ 0x00001171, 0x00001172, 0x00001173, 0x00001174,
+ 0x00001175, 0x000000a2, 0x000000a3, 0x000000ac,
+ 0x00000020, 0x00000304, 0x000000a6, 0x000000a5,
+ 0x000020a9, 0x00002502, 0x00002190, 0x00002191,
+ 0x00002192, 0x00002193, 0x000025a0, 0x000025cb,
+ 0x0001d157, 0x0001d165, 0x0001d158, 0x0001d165,
+ 0x0001d158, 0x0001d165, 0x0001d16e, 0x0001d158,
+ 0x0001d165, 0x0001d16f, 0x0001d158, 0x0001d165,
+ 0x0001d170, 0x0001d158, 0x0001d165, 0x0001d171,
+ 0x0001d158, 0x0001d165, 0x0001d172, 0x0001d1b9,
+ 0x0001d165, 0x0001d1ba, 0x0001d165, 0x0001d1b9,
+ 0x0001d165, 0x0001d16e, 0x0001d1ba, 0x0001d165,
+ 0x0001d16e, 0x0001d1b9, 0x0001d165, 0x0001d16f,
+ 0x0001d1ba, 0x0001d165, 0x0001d16f, 0x00000041,
+ 0x00000042, 0x00000043, 0x00000044, 0x00000045,
+ 0x00000046, 0x00000047, 0x00000048, 0x00000049,
+ 0x0000004a, 0x0000004b, 0x0000004c, 0x0000004d,
+ 0x0000004e, 0x0000004f, 0x00000050, 0x00000051,
+ 0x00000052, 0x00000053, 0x00000054, 0x00000055,
+ 0x00000056, 0x00000057, 0x00000058, 0x00000059,
+ 0x0000005a, 0x00000061, 0x00000062, 0x00000063,
+ 0x00000064, 0x00000065, 0x00000066, 0x00000067,
+ 0x00000068, 0x00000069, 0x0000006a, 0x0000006b,
+ 0x0000006c, 0x0000006d, 0x0000006e, 0x0000006f,
+ 0x00000070, 0x00000071, 0x00000072, 0x00000073,
+ 0x00000074, 0x00000075, 0x00000076, 0x00000077,
+ 0x00000078, 0x00000079, 0x0000007a, 0x00000041,
+ 0x00000042, 0x00000043, 0x00000044, 0x00000045,
+ 0x00000046, 0x00000047, 0x00000048, 0x00000049,
+ 0x0000004a, 0x0000004b, 0x0000004c, 0x0000004d,
+ 0x0000004e, 0x0000004f, 0x00000050, 0x00000051,
+ 0x00000052, 0x00000053, 0x00000054, 0x00000055,
+ 0x00000056, 0x00000057, 0x00000058, 0x00000059,
+ 0x0000005a, 0x00000061, 0x00000062, 0x00000063,
+ 0x00000064, 0x00000065, 0x00000066, 0x00000067,
+ 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c,
+ 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070,
+ 0x00000071, 0x00000072, 0x00000073, 0x00000074,
+ 0x00000075, 0x00000076, 0x00000077, 0x00000078,
+ 0x00000079, 0x0000007a, 0x00000041, 0x00000042,
+ 0x00000043, 0x00000044, 0x00000045, 0x00000046,
+ 0x00000047, 0x00000048, 0x00000049, 0x0000004a,
+ 0x0000004b, 0x0000004c, 0x0000004d, 0x0000004e,
+ 0x0000004f, 0x00000050, 0x00000051, 0x00000052,
+ 0x00000053, 0x00000054, 0x00000055, 0x00000056,
+ 0x00000057, 0x00000058, 0x00000059, 0x0000005a,
+ 0x00000061, 0x00000062, 0x00000063, 0x00000064,
+ 0x00000065, 0x00000066, 0x00000067, 0x00000068,
+ 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c,
+ 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070,
+ 0x00000071, 0x00000072, 0x00000073, 0x00000074,
+ 0x00000075, 0x00000076, 0x00000077, 0x00000078,
+ 0x00000079, 0x0000007a, 0x00000041, 0x00000043,
+ 0x00000044, 0x00000047, 0x0000004a, 0x0000004b,
+ 0x0000004e, 0x0000004f, 0x00000050, 0x00000051,
+ 0x00000053, 0x00000054, 0x00000055, 0x00000056,
+ 0x00000057, 0x00000058, 0x00000059, 0x0000005a,
+ 0x00000061, 0x00000062, 0x00000063, 0x00000064,
+ 0x00000066, 0x00000068, 0x00000069, 0x0000006a,
+ 0x0000006b, 0x0000006d, 0x0000006e, 0x00000070,
+ 0x00000071, 0x00000072, 0x00000073, 0x00000074,
+ 0x00000075, 0x00000076, 0x00000077, 0x00000078,
+ 0x00000079, 0x0000007a, 0x00000041, 0x00000042,
+ 0x00000043, 0x00000044, 0x00000045, 0x00000046,
+ 0x00000047, 0x00000048, 0x00000049, 0x0000004a,
+ 0x0000004b, 0x0000004c, 0x0000004d, 0x0000004e,
+ 0x0000004f, 0x00000050, 0x00000051, 0x00000052,
+ 0x00000053, 0x00000054, 0x00000055, 0x00000056,
+ 0x00000057, 0x00000058, 0x00000059, 0x0000005a,
+ 0x00000061, 0x00000062, 0x00000063, 0x00000064,
+ 0x00000065, 0x00000066, 0x00000067, 0x00000068,
+ 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c,
+ 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070,
+ 0x00000071, 0x00000072, 0x00000073, 0x00000074,
+ 0x00000075, 0x00000076, 0x00000077, 0x00000078,
+ 0x00000079, 0x0000007a, 0x00000041, 0x00000042,
+ 0x00000044, 0x00000045, 0x00000046, 0x00000047,
+ 0x0000004a, 0x0000004b, 0x0000004c, 0x0000004d,
+ 0x0000004e, 0x0000004f, 0x00000050, 0x00000051,
+ 0x00000053, 0x00000054, 0x00000055, 0x00000056,
+ 0x00000057, 0x00000058, 0x00000059, 0x00000061,
+ 0x00000062, 0x00000063, 0x00000064, 0x00000065,
+ 0x00000066, 0x00000067, 0x00000068, 0x00000069,
+ 0x0000006a, 0x0000006b, 0x0000006c, 0x0000006d,
+ 0x0000006e, 0x0000006f, 0x00000070, 0x00000071,
+ 0x00000072, 0x00000073, 0x00000074, 0x00000075,
+ 0x00000076, 0x00000077, 0x00000078, 0x00000079,
+ 0x0000007a, 0x00000041, 0x00000042, 0x00000044,
+ 0x00000045, 0x00000046, 0x00000047, 0x00000049,
+ 0x0000004a, 0x0000004b, 0x0000004c, 0x0000004d,
+ 0x0000004f, 0x00000053, 0x00000054, 0x00000055,
+ 0x00000056, 0x00000057, 0x00000058, 0x00000059,
+ 0x00000061, 0x00000062, 0x00000063, 0x00000064,
+ 0x00000065, 0x00000066, 0x00000067, 0x00000068,
+ 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c,
+ 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070,
+ 0x00000071, 0x00000072, 0x00000073, 0x00000074,
+ 0x00000075, 0x00000076, 0x00000077, 0x00000078,
+ 0x00000079, 0x0000007a, 0x00000041, 0x00000042,
+ 0x00000043, 0x00000044, 0x00000045, 0x00000046,
+ 0x00000047, 0x00000048, 0x00000049, 0x0000004a,
+ 0x0000004b, 0x0000004c, 0x0000004d, 0x0000004e,
+ 0x0000004f, 0x00000050, 0x00000051, 0x00000052,
+ 0x00000053, 0x00000054, 0x00000055, 0x00000056,
+ 0x00000057, 0x00000058, 0x00000059, 0x0000005a,
+ 0x00000061, 0x00000062, 0x00000063, 0x00000064,
+ 0x00000065, 0x00000066, 0x00000067, 0x00000068,
+ 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c,
+ 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070,
+ 0x00000071, 0x00000072, 0x00000073, 0x00000074,
+ 0x00000075, 0x00000076, 0x00000077, 0x00000078,
+ 0x00000079, 0x0000007a, 0x00000041, 0x00000042,
+ 0x00000043, 0x00000044, 0x00000045, 0x00000046,
+ 0x00000047, 0x00000048, 0x00000049, 0x0000004a,
+ 0x0000004b, 0x0000004c, 0x0000004d, 0x0000004e,
+ 0x0000004f, 0x00000050, 0x00000051, 0x00000052,
+ 0x00000053, 0x00000054, 0x00000055, 0x00000056,
+ 0x00000057, 0x00000058, 0x00000059, 0x0000005a,
+ 0x00000061, 0x00000062, 0x00000063, 0x00000064,
+ 0x00000065, 0x00000066, 0x00000067, 0x00000068,
+ 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c,
+ 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070,
+ 0x00000071, 0x00000072, 0x00000073, 0x00000074,
+ 0x00000075, 0x00000076, 0x00000077, 0x00000078,
+ 0x00000079, 0x0000007a, 0x00000041, 0x00000042,
+ 0x00000043, 0x00000044, 0x00000045, 0x00000046,
+ 0x00000047, 0x00000048, 0x00000049, 0x0000004a,
+ 0x0000004b, 0x0000004c, 0x0000004d, 0x0000004e,
+ 0x0000004f, 0x00000050, 0x00000051, 0x00000052,
+ 0x00000053, 0x00000054, 0x00000055, 0x00000056,
+ 0x00000057, 0x00000058, 0x00000059, 0x0000005a,
+ 0x00000061, 0x00000062, 0x00000063, 0x00000064,
+ 0x00000065, 0x00000066, 0x00000067, 0x00000068,
+ 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c,
+ 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070,
+ 0x00000071, 0x00000072, 0x00000073, 0x00000074,
+ 0x00000075, 0x00000076, 0x00000077, 0x00000078,
+ 0x00000079, 0x0000007a, 0x00000041, 0x00000042,
+ 0x00000043, 0x00000044, 0x00000045, 0x00000046,
+ 0x00000047, 0x00000048, 0x00000049, 0x0000004a,
+ 0x0000004b, 0x0000004c, 0x0000004d, 0x0000004e,
+ 0x0000004f, 0x00000050, 0x00000051, 0x00000052,
+ 0x00000053, 0x00000054, 0x00000055, 0x00000056,
+ 0x00000057, 0x00000058, 0x00000059, 0x0000005a,
+ 0x00000061, 0x00000062, 0x00000063, 0x00000064,
+ 0x00000065, 0x00000066, 0x00000067, 0x00000068,
+ 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c,
+ 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070,
+ 0x00000071, 0x00000072, 0x00000073, 0x00000074,
+ 0x00000075, 0x00000076, 0x00000077, 0x00000078,
+ 0x00000079, 0x0000007a, 0x00000041, 0x00000042,
+ 0x00000043, 0x00000044, 0x00000045, 0x00000046,
+ 0x00000047, 0x00000048, 0x00000049, 0x0000004a,
+ 0x0000004b, 0x0000004c, 0x0000004d, 0x0000004e,
+ 0x0000004f, 0x00000050, 0x00000051, 0x00000052,
+ 0x00000053, 0x00000054, 0x00000055, 0x00000056,
+ 0x00000057, 0x00000058, 0x00000059, 0x0000005a,
+ 0x00000061, 0x00000062, 0x00000063, 0x00000064,
+ 0x00000065, 0x00000066, 0x00000067, 0x00000068,
+ 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c,
+ 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070,
+ 0x00000071, 0x00000072, 0x00000073, 0x00000074,
+ 0x00000075, 0x00000076, 0x00000077, 0x00000078,
+ 0x00000079, 0x0000007a, 0x00000041, 0x00000042,
+ 0x00000043, 0x00000044, 0x00000045, 0x00000046,
+ 0x00000047, 0x00000048, 0x00000049, 0x0000004a,
+ 0x0000004b, 0x0000004c, 0x0000004d, 0x0000004e,
+ 0x0000004f, 0x00000050, 0x00000051, 0x00000052,
+ 0x00000053, 0x00000054, 0x00000055, 0x00000056,
+ 0x00000057, 0x00000058, 0x00000059, 0x0000005a,
+ 0x00000061, 0x00000062, 0x00000063, 0x00000064,
+ 0x00000065, 0x00000066, 0x00000067, 0x00000068,
+ 0x00000069, 0x0000006a, 0x0000006b, 0x0000006c,
+ 0x0000006d, 0x0000006e, 0x0000006f, 0x00000070,
+ 0x00000071, 0x00000072, 0x00000073, 0x00000074,
+ 0x00000075, 0x00000076, 0x00000077, 0x00000078,
+ 0x00000079, 0x0000007a, 0x00000391, 0x00000392,
+ 0x00000393, 0x00000394, 0x00000395, 0x00000396,
+ 0x00000397, 0x00000398, 0x00000399, 0x0000039a,
+ 0x0000039b, 0x0000039c, 0x0000039d, 0x0000039e,
+ 0x0000039f, 0x000003a0, 0x000003a1, 0x00000398,
+ 0x000003a3, 0x000003a4, 0x000003a5, 0x000003a6,
+ 0x000003a7, 0x000003a8, 0x000003a9, 0x00002207,
+ 0x000003b1, 0x000003b2, 0x000003b3, 0x000003b4,
+ 0x000003b5, 0x000003b6, 0x000003b7, 0x000003b8,
+ 0x000003b9, 0x000003ba, 0x000003bb, 0x000003bc,
+ 0x000003bd, 0x000003be, 0x000003bf, 0x000003c0,
+ 0x000003c1, 0x000003c2, 0x000003c3, 0x000003c4,
+ 0x000003c5, 0x000003c6, 0x000003c7, 0x000003c8,
+ 0x000003c9, 0x00002202, 0x000003b5, 0x000003b8,
+ 0x000003ba, 0x000003c6, 0x000003c1, 0x000003c0,
+ 0x00000391, 0x00000392, 0x00000393, 0x00000394,
+ 0x00000395, 0x00000396, 0x00000397, 0x00000398,
+ 0x00000399, 0x0000039a, 0x0000039b, 0x0000039c,
+ 0x0000039d, 0x0000039e, 0x0000039f, 0x000003a0,
+ 0x000003a1, 0x00000398, 0x000003a3, 0x000003a4,
+ 0x000003a5, 0x000003a6, 0x000003a7, 0x000003a8,
+ 0x000003a9, 0x00002207, 0x000003b1, 0x000003b2,
+ 0x000003b3, 0x000003b4, 0x000003b5, 0x000003b6,
+ 0x000003b7, 0x000003b8, 0x000003b9, 0x000003ba,
+ 0x000003bb, 0x000003bc, 0x000003bd, 0x000003be,
+ 0x000003bf, 0x000003c0, 0x000003c1, 0x000003c2,
+ 0x000003c3, 0x000003c4, 0x000003c5, 0x000003c6,
+ 0x000003c7, 0x000003c8, 0x000003c9, 0x00002202,
+ 0x000003b5, 0x000003b8, 0x000003ba, 0x000003c6,
+ 0x000003c1, 0x000003c0, 0x00000391, 0x00000392,
+ 0x00000393, 0x00000394, 0x00000395, 0x00000396,
+ 0x00000397, 0x00000398, 0x00000399, 0x0000039a,
+ 0x0000039b, 0x0000039c, 0x0000039d, 0x0000039e,
+ 0x0000039f, 0x000003a0, 0x000003a1, 0x00000398,
+ 0x000003a3, 0x000003a4, 0x000003a5, 0x000003a6,
+ 0x000003a7, 0x000003a8, 0x000003a9, 0x00002207,
+ 0x000003b1, 0x000003b2, 0x000003b3, 0x000003b4,
+ 0x000003b5, 0x000003b6, 0x000003b7, 0x000003b8,
+ 0x000003b9, 0x000003ba, 0x000003bb, 0x000003bc,
+ 0x000003bd, 0x000003be, 0x000003bf, 0x000003c0,
+ 0x000003c1, 0x000003c2, 0x000003c3, 0x000003c4,
+ 0x000003c5, 0x000003c6, 0x000003c7, 0x000003c8,
+ 0x000003c9, 0x00002202, 0x000003b5, 0x000003b8,
+ 0x000003ba, 0x000003c6, 0x000003c1, 0x000003c0,
+ 0x00000391, 0x00000392, 0x00000393, 0x00000394,
+ 0x00000395, 0x00000396, 0x00000397, 0x00000398,
+ 0x00000399, 0x0000039a, 0x0000039b, 0x0000039c,
+ 0x0000039d, 0x0000039e, 0x0000039f, 0x000003a0,
+ 0x000003a1, 0x00000398, 0x000003a3, 0x000003a4,
+ 0x000003a5, 0x000003a6, 0x000003a7, 0x000003a8,
+ 0x000003a9, 0x00002207, 0x000003b1, 0x000003b2,
+ 0x000003b3, 0x000003b4, 0x000003b5, 0x000003b6,
+ 0x000003b7, 0x000003b8, 0x000003b9, 0x000003ba,
+ 0x000003bb, 0x000003bc, 0x000003bd, 0x000003be,
+ 0x000003bf, 0x000003c0, 0x000003c1, 0x000003c2,
+ 0x000003c3, 0x000003c4, 0x000003c5, 0x000003c6,
+ 0x000003c7, 0x000003c8, 0x000003c9, 0x00002202,
+ 0x000003b5, 0x000003b8, 0x000003ba, 0x000003c6,
+ 0x000003c1, 0x000003c0, 0x00000391, 0x00000392,
+ 0x00000393, 0x00000394, 0x00000395, 0x00000396,
+ 0x00000397, 0x00000398, 0x00000399, 0x0000039a,
+ 0x0000039b, 0x0000039c, 0x0000039d, 0x0000039e,
+ 0x0000039f, 0x000003a0, 0x000003a1, 0x00000398,
+ 0x000003a3, 0x000003a4, 0x000003a5, 0x000003a6,
+ 0x000003a7, 0x000003a8, 0x000003a9, 0x00002207,
+ 0x000003b1, 0x000003b2, 0x000003b3, 0x000003b4,
+ 0x000003b5, 0x000003b6, 0x000003b7, 0x000003b8,
+ 0x000003b9, 0x000003ba, 0x000003bb, 0x000003bc,
+ 0x000003bd, 0x000003be, 0x000003bf, 0x000003c0,
+ 0x000003c1, 0x000003c2, 0x000003c3, 0x000003c4,
+ 0x000003c5, 0x000003c6, 0x000003c7, 0x000003c8,
+ 0x000003c9, 0x00002202, 0x000003b5, 0x000003b8,
+ 0x000003ba, 0x000003c6, 0x000003c1, 0x000003c0,
+ 0x00000030, 0x00000031, 0x00000032, 0x00000033,
+ 0x00000034, 0x00000035, 0x00000036, 0x00000037,
+ 0x00000038, 0x00000039, 0x00000030, 0x00000031,
+ 0x00000032, 0x00000033, 0x00000034, 0x00000035,
+ 0x00000036, 0x00000037, 0x00000038, 0x00000039,
+ 0x00000030, 0x00000031, 0x00000032, 0x00000033,
+ 0x00000034, 0x00000035, 0x00000036, 0x00000037,
+ 0x00000038, 0x00000039, 0x00000030, 0x00000031,
+ 0x00000032, 0x00000033, 0x00000034, 0x00000035,
+ 0x00000036, 0x00000037, 0x00000038, 0x00000039,
+ 0x00000030, 0x00000031, 0x00000032, 0x00000033,
+ 0x00000034, 0x00000035, 0x00000036, 0x00000037,
+ 0x00000038, 0x00000039, 0x00004e3d, 0x00004e38,
+ 0x00004e41, 0x00020122, 0x00004f60, 0x00004fae,
+ 0x00004fbb, 0x00005002, 0x0000507a, 0x00005099,
+ 0x000050e7, 0x000050cf, 0x0000349e, 0x0002063a,
+ 0x0000514d, 0x00005154, 0x00005164, 0x00005177,
+ 0x0002051c, 0x000034b9, 0x00005167, 0x0000518d,
+ 0x0002054b, 0x00005197, 0x000051a4, 0x00004ecc,
+ 0x000051ac, 0x000051b5, 0x000291df, 0x000051f5,
+ 0x00005203, 0x000034df, 0x0000523b, 0x00005246,
+ 0x00005272, 0x00005277, 0x00003515, 0x000052c7,
+ 0x000052c9, 0x000052e4, 0x000052fa, 0x00005305,
+ 0x00005306, 0x00005317, 0x00005349, 0x00005351,
+ 0x0000535a, 0x00005373, 0x0000537d, 0x0000537f,
+ 0x0000537f, 0x0000537f, 0x00020a2c, 0x00007070,
+ 0x000053ca, 0x000053df, 0x00020b63, 0x000053eb,
+ 0x000053f1, 0x00005406, 0x0000549e, 0x00005438,
+ 0x00005448, 0x00005468, 0x000054a2, 0x000054f6,
+ 0x00005510, 0x00005553, 0x00005563, 0x00005584,
+ 0x00005584, 0x00005599, 0x000055ab, 0x000055b3,
+ 0x000055c2, 0x00005716, 0x00005606, 0x00005717,
+ 0x00005651, 0x00005674, 0x00005207, 0x000058ee,
+ 0x000057ce, 0x000057f4, 0x0000580d, 0x0000578b,
+ 0x00005832, 0x00005831, 0x000058ac, 0x000214e4,
+ 0x000058f2, 0x000058f7, 0x00005906, 0x0000591a,
+ 0x00005922, 0x00005962, 0x000216a8, 0x000216ea,
+ 0x000059ec, 0x00005a1b, 0x00005a27, 0x000059d8,
+ 0x00005a66, 0x000036ee, 0x0002136a, 0x00005b08,
+ 0x00005b3e, 0x00005b3e, 0x000219c8, 0x00005bc3,
+ 0x00005bd8, 0x00005be7, 0x00005bf3, 0x00021b18,
+ 0x00005bff, 0x00005c06, 0x00005f33, 0x00005c22,
+ 0x00003781, 0x00005c60, 0x00005c6e, 0x00005cc0,
+ 0x00005c8d, 0x00021de4, 0x00005d43, 0x00021de6,
+ 0x00005d6e, 0x00005d6b, 0x00005d7c, 0x00005de1,
+ 0x00005de2, 0x0000382f, 0x00005dfd, 0x00005e28,
+ 0x00005e3d, 0x00005e69, 0x00003862, 0x00022183,
+ 0x0000387c, 0x00005eb0, 0x00005eb3, 0x00005eb6,
+ 0x00005eca, 0x0002a392, 0x00005efe, 0x00022331,
+ 0x00022331, 0x00008201, 0x00005f22, 0x00005f22,
+ 0x000038c7, 0x000232b8, 0x000261da, 0x00005f62,
+ 0x00005f6b, 0x000038e3, 0x00005f9a, 0x00005fcd,
+ 0x00005fd7, 0x00005ff9, 0x00006081, 0x0000393a,
+ 0x0000391c, 0x00006094, 0x000226d4, 0x000060c7,
+ 0x00006148, 0x0000614c, 0x0000614e, 0x0000614c,
+ 0x0000617a, 0x0000618e, 0x000061b2, 0x000061a4,
+ 0x000061af, 0x000061de, 0x000061f2, 0x000061f6,
+ 0x00006210, 0x0000621b, 0x0000625d, 0x000062b1,
+ 0x000062d4, 0x00006350, 0x00022b0c, 0x0000633d,
+ 0x000062fc, 0x00006368, 0x00006383, 0x000063e4,
+ 0x00022bf1, 0x00006422, 0x000063c5, 0x000063a9,
+ 0x00003a2e, 0x00006469, 0x0000647e, 0x0000649d,
+ 0x00006477, 0x00003a6c, 0x0000654f, 0x0000656c,
+ 0x0002300a, 0x000065e3, 0x000066f8, 0x00006649,
+ 0x00003b19, 0x00006691, 0x00003b08, 0x00003ae4,
+ 0x00005192, 0x00005195, 0x00006700, 0x0000669c,
+ 0x000080ad, 0x000043d9, 0x00006717, 0x0000671b,
+ 0x00006721, 0x0000675e, 0x00006753, 0x000233c3,
+ 0x00003b49, 0x000067fa, 0x00006785, 0x00006852,
+ 0x00006885, 0x0002346d, 0x0000688e, 0x0000681f,
+ 0x00006914, 0x00003b9d, 0x00006942, 0x000069a3,
+ 0x000069ea, 0x00006aa8, 0x000236a3, 0x00006adb,
+ 0x00003c18, 0x00006b21, 0x000238a7, 0x00006b54,
+ 0x00003c4e, 0x00006b72, 0x00006b9f, 0x00006bba,
+ 0x00006bbb, 0x00023a8d, 0x00021d0b, 0x00023afa,
+ 0x00006c4e, 0x00023cbc, 0x00006cbf, 0x00006ccd,
+ 0x00006c67, 0x00006d16, 0x00006d3e, 0x00006d77,
+ 0x00006d41, 0x00006d69, 0x00006d78, 0x00006d85,
+ 0x00023d1e, 0x00006d34, 0x00006e2f, 0x00006e6e,
+ 0x00003d33, 0x00006ecb, 0x00006ec7, 0x00023ed1,
+ 0x00006df9, 0x00006f6e, 0x00023f5e, 0x00023f8e,
+ 0x00006fc6, 0x00007039, 0x0000701e, 0x0000701b,
+ 0x00003d96, 0x0000704a, 0x0000707d, 0x00007077,
+ 0x000070ad, 0x00020525, 0x00007145, 0x00024263,
+ 0x0000719c, 0x000043ab, 0x00007228, 0x00007235,
+ 0x00007250, 0x00024608, 0x00007280, 0x00007295,
+ 0x00024735, 0x00024814, 0x0000737a, 0x0000738b,
+ 0x00003eac, 0x000073a5, 0x00003eb8, 0x00003eb8,
+ 0x00007447, 0x0000745c, 0x00007471, 0x00007485,
+ 0x000074ca, 0x00003f1b, 0x00007524, 0x00024c36,
+ 0x0000753e, 0x00024c92, 0x00007570, 0x0002219f,
+ 0x00007610, 0x00024fa1, 0x00024fb8, 0x00025044,
+ 0x00003ffc, 0x00004008, 0x000076f4, 0x000250f3,
+ 0x000250f2, 0x00025119, 0x00025133, 0x0000771e,
+ 0x0000771f, 0x0000771f, 0x0000774a, 0x00004039,
+ 0x0000778b, 0x00004046, 0x00004096, 0x0002541d,
+ 0x0000784e, 0x0000788c, 0x000078cc, 0x000040e3,
+ 0x00025626, 0x00007956, 0x0002569a, 0x000256c5,
+ 0x0000798f, 0x000079eb, 0x0000412f, 0x00007a40,
+ 0x00007a4a, 0x00007a4f, 0x0002597c, 0x00025aa7,
+ 0x00025aa7, 0x00007aae, 0x00004202, 0x00025bab,
+ 0x00007bc6, 0x00007bc9, 0x00004227, 0x00025c80,
+ 0x00007cd2, 0x000042a0, 0x00007ce8, 0x00007ce3,
+ 0x00007d00, 0x00025f86, 0x00007d63, 0x00004301,
+ 0x00007dc7, 0x00007e02, 0x00007e45, 0x00004334,
+ 0x00026228, 0x00026247, 0x00004359, 0x000262d9,
+ 0x00007f7a, 0x0002633e, 0x00007f95, 0x00007ffa,
+ 0x00008005, 0x000264da, 0x00026523, 0x00008060,
+ 0x000265a8, 0x00008070, 0x0002335f, 0x000043d5,
+ 0x000080b2, 0x00008103, 0x0000440b, 0x0000813e,
+ 0x00005ab5, 0x000267a7, 0x000267b5, 0x00023393,
+ 0x0002339c, 0x00008201, 0x00008204, 0x00008f9e,
+ 0x0000446b, 0x00008291, 0x0000828b, 0x0000829d,
+ 0x000052b3, 0x000082b1, 0x000082b3, 0x000082bd,
+ 0x000082e6, 0x00026b3c, 0x000082e5, 0x0000831d,
+ 0x00008363, 0x000083ad, 0x00008323, 0x000083bd,
+ 0x000083e7, 0x00008457, 0x00008353, 0x000083ca,
+ 0x000083cc, 0x000083dc, 0x00026c36, 0x00026d6b,
+ 0x00026cd5, 0x0000452b, 0x000084f1, 0x000084f3,
+ 0x00008516, 0x000273ca, 0x00008564, 0x00026f2c,
+ 0x0000455d, 0x00004561, 0x00026fb1, 0x000270d2,
+ 0x0000456b, 0x00008650, 0x0000865c, 0x00008667,
+ 0x00008669, 0x000086a9, 0x00008688, 0x0000870e,
+ 0x000086e2, 0x00008779, 0x00008728, 0x0000876b,
+ 0x00008786, 0x00004d57, 0x000087e1, 0x00008801,
+ 0x000045f9, 0x00008860, 0x00008863, 0x00027667,
+ 0x000088d7, 0x000088de, 0x00004635, 0x000088fa,
+ 0x000034bb, 0x000278ae, 0x00027966, 0x000046be,
+ 0x000046c7, 0x00008aa0, 0x00008aed, 0x00008b8a,
+ 0x00008c55, 0x00027ca8, 0x00008cab, 0x00008cc1,
+ 0x00008d1b, 0x00008d77, 0x00027f2f, 0x00020804,
+ 0x00008dcb, 0x00008dbc, 0x00008df0, 0x000208de,
+ 0x00008ed4, 0x00008f38, 0x000285d2, 0x000285ed,
+ 0x00009094, 0x000090f1, 0x00009111, 0x0002872e,
+ 0x0000911b, 0x00009238, 0x000092d7, 0x000092d8,
+ 0x0000927c, 0x000093f9, 0x00009415, 0x00028bfa,
+ 0x0000958b, 0x00004995, 0x000095b7, 0x00028d77,
+ 0x000049e6, 0x000096c3, 0x00005db2, 0x00009723,
+ 0x00029145, 0x0002921a, 0x00004a6e, 0x00004a76,
+ 0x000097e0, 0x0002940a, 0x00004ab2, 0x00029496,
+ 0x0000980b, 0x0000980b, 0x00009829, 0x000295b6,
+ 0x000098e2, 0x00004b33, 0x00009929, 0x000099a7,
+ 0x000099c2, 0x000099fe, 0x00004bce, 0x00029b30,
+ 0x00009b12, 0x00009c40, 0x00009cfd, 0x00004cce,
+ 0x00004ced, 0x00009d67, 0x0002a0ce, 0x00004cf8,
+ 0x0002a105, 0x0002a20e, 0x0002a291, 0x00009ebb,
+ 0x00004d56, 0x00009ef9, 0x00009efe, 0x00009f05,
+ 0x00009f0f, 0x00009f16, 0x00009f3b, 0x0002a600
+};
+
+static const ac_uint4 _uccmcl_size = 489;
+
+static const ac_uint4 _uccmcl_nodes[] = {
+ 0x00000300, 0x00000314, 0x000000e6, 0x00000315,
+ 0x00000315, 0x000000e8, 0x00000316, 0x00000319,
+ 0x000000dc, 0x0000031a, 0x0000031a, 0x000000e8,
+ 0x0000031b, 0x0000031b, 0x000000d8, 0x0000031c,
+ 0x00000320, 0x000000dc, 0x00000321, 0x00000322,
+ 0x000000ca, 0x00000323, 0x00000326, 0x000000dc,
+ 0x00000327, 0x00000328, 0x000000ca, 0x00000329,
+ 0x00000333, 0x000000dc, 0x00000334, 0x00000338,
+ 0x00000001, 0x00000339, 0x0000033c, 0x000000dc,
+ 0x0000033d, 0x00000344, 0x000000e6, 0x00000345,
+ 0x00000345, 0x000000f0, 0x00000346, 0x00000346,
+ 0x000000e6, 0x00000347, 0x00000349, 0x000000dc,
+ 0x0000034a, 0x0000034c, 0x000000e6, 0x0000034d,
+ 0x0000034e, 0x000000dc, 0x00000360, 0x00000361,
+ 0x000000ea, 0x00000362, 0x00000362, 0x000000e9,
+ 0x00000363, 0x0000036f, 0x000000e6, 0x00000483,
+ 0x00000486, 0x000000e6, 0x00000591, 0x00000591,
+ 0x000000dc, 0x00000592, 0x00000595, 0x000000e6,
+ 0x00000596, 0x00000596, 0x000000dc, 0x00000597,
+ 0x00000599, 0x000000e6, 0x0000059a, 0x0000059a,
+ 0x000000de, 0x0000059b, 0x0000059b, 0x000000dc,
+ 0x0000059c, 0x000005a1, 0x000000e6, 0x000005a3,
+ 0x000005a7, 0x000000dc, 0x000005a8, 0x000005a9,
+ 0x000000e6, 0x000005aa, 0x000005aa, 0x000000dc,
+ 0x000005ab, 0x000005ac, 0x000000e6, 0x000005ad,
+ 0x000005ad, 0x000000de, 0x000005ae, 0x000005ae,
+ 0x000000e4, 0x000005af, 0x000005af, 0x000000e6,
+ 0x000005b0, 0x000005b0, 0x0000000a, 0x000005b1,
+ 0x000005b1, 0x0000000b, 0x000005b2, 0x000005b2,
+ 0x0000000c, 0x000005b3, 0x000005b3, 0x0000000d,
+ 0x000005b4, 0x000005b4, 0x0000000e, 0x000005b5,
+ 0x000005b5, 0x0000000f, 0x000005b6, 0x000005b6,
+ 0x00000010, 0x000005b7, 0x000005b7, 0x00000011,
+ 0x000005b8, 0x000005b8, 0x00000012, 0x000005b9,
+ 0x000005b9, 0x00000013, 0x000005bb, 0x000005bb,
+ 0x00000014, 0x000005bc, 0x000005bc, 0x00000015,
+ 0x000005bd, 0x000005bd, 0x00000016, 0x000005bf,
+ 0x000005bf, 0x00000017, 0x000005c1, 0x000005c1,
+ 0x00000018, 0x000005c2, 0x000005c2, 0x00000019,
+ 0x000005c4, 0x000005c4, 0x000000e6, 0x0000064b,
+ 0x0000064b, 0x0000001b, 0x0000064c, 0x0000064c,
+ 0x0000001c, 0x0000064d, 0x0000064d, 0x0000001d,
+ 0x0000064e, 0x0000064e, 0x0000001e, 0x0000064f,
+ 0x0000064f, 0x0000001f, 0x00000650, 0x00000650,
+ 0x00000020, 0x00000651, 0x00000651, 0x00000021,
+ 0x00000652, 0x00000652, 0x00000022, 0x00000653,
+ 0x00000654, 0x000000e6, 0x00000655, 0x00000655,
+ 0x000000dc, 0x00000670, 0x00000670, 0x00000023,
+ 0x000006d6, 0x000006dc, 0x000000e6, 0x000006df,
+ 0x000006e2, 0x000000e6, 0x000006e3, 0x000006e3,
+ 0x000000dc, 0x000006e4, 0x000006e4, 0x000000e6,
+ 0x000006e7, 0x000006e8, 0x000000e6, 0x000006ea,
+ 0x000006ea, 0x000000dc, 0x000006eb, 0x000006ec,
+ 0x000000e6, 0x000006ed, 0x000006ed, 0x000000dc,
+ 0x00000711, 0x00000711, 0x00000024, 0x00000730,
+ 0x00000730, 0x000000e6, 0x00000731, 0x00000731,
+ 0x000000dc, 0x00000732, 0x00000733, 0x000000e6,
+ 0x00000734, 0x00000734, 0x000000dc, 0x00000735,
+ 0x00000736, 0x000000e6, 0x00000737, 0x00000739,
+ 0x000000dc, 0x0000073a, 0x0000073a, 0x000000e6,
+ 0x0000073b, 0x0000073c, 0x000000dc, 0x0000073d,
+ 0x0000073d, 0x000000e6, 0x0000073e, 0x0000073e,
+ 0x000000dc, 0x0000073f, 0x00000741, 0x000000e6,
+ 0x00000742, 0x00000742, 0x000000dc, 0x00000743,
+ 0x00000743, 0x000000e6, 0x00000744, 0x00000744,
+ 0x000000dc, 0x00000745, 0x00000745, 0x000000e6,
+ 0x00000746, 0x00000746, 0x000000dc, 0x00000747,
+ 0x00000747, 0x000000e6, 0x00000748, 0x00000748,
+ 0x000000dc, 0x00000749, 0x0000074a, 0x000000e6,
+ 0x0000093c, 0x0000093c, 0x00000007, 0x0000094d,
+ 0x0000094d, 0x00000009, 0x00000951, 0x00000951,
+ 0x000000e6, 0x00000952, 0x00000952, 0x000000dc,
+ 0x00000953, 0x00000954, 0x000000e6, 0x000009bc,
+ 0x000009bc, 0x00000007, 0x000009cd, 0x000009cd,
+ 0x00000009, 0x00000a3c, 0x00000a3c, 0x00000007,
+ 0x00000a4d, 0x00000a4d, 0x00000009, 0x00000abc,
+ 0x00000abc, 0x00000007, 0x00000acd, 0x00000acd,
+ 0x00000009, 0x00000b3c, 0x00000b3c, 0x00000007,
+ 0x00000b4d, 0x00000b4d, 0x00000009, 0x00000bcd,
+ 0x00000bcd, 0x00000009, 0x00000c4d, 0x00000c4d,
+ 0x00000009, 0x00000c55, 0x00000c55, 0x00000054,
+ 0x00000c56, 0x00000c56, 0x0000005b, 0x00000ccd,
+ 0x00000ccd, 0x00000009, 0x00000d4d, 0x00000d4d,
+ 0x00000009, 0x00000dca, 0x00000dca, 0x00000009,
+ 0x00000e38, 0x00000e39, 0x00000067, 0x00000e3a,
+ 0x00000e3a, 0x00000009, 0x00000e48, 0x00000e4b,
+ 0x0000006b, 0x00000eb8, 0x00000eb9, 0x00000076,
+ 0x00000ec8, 0x00000ecb, 0x0000007a, 0x00000f18,
+ 0x00000f19, 0x000000dc, 0x00000f35, 0x00000f35,
+ 0x000000dc, 0x00000f37, 0x00000f37, 0x000000dc,
+ 0x00000f39, 0x00000f39, 0x000000d8, 0x00000f71,
+ 0x00000f71, 0x00000081, 0x00000f72, 0x00000f72,
+ 0x00000082, 0x00000f74, 0x00000f74, 0x00000084,
+ 0x00000f7a, 0x00000f7d, 0x00000082, 0x00000f80,
+ 0x00000f80, 0x00000082, 0x00000f82, 0x00000f83,
+ 0x000000e6, 0x00000f84, 0x00000f84, 0x00000009,
+ 0x00000f86, 0x00000f87, 0x000000e6, 0x00000fc6,
+ 0x00000fc6, 0x000000dc, 0x00001037, 0x00001037,
+ 0x00000007, 0x00001039, 0x00001039, 0x00000009,
+ 0x00001714, 0x00001714, 0x00000009, 0x00001734,
+ 0x00001734, 0x00000009, 0x000017d2, 0x000017d2,
+ 0x00000009, 0x000018a9, 0x000018a9, 0x000000e4,
+ 0x000020d0, 0x000020d1, 0x000000e6, 0x000020d2,
+ 0x000020d3, 0x00000001, 0x000020d4, 0x000020d7,
+ 0x000000e6, 0x000020d8, 0x000020da, 0x00000001,
+ 0x000020db, 0x000020dc, 0x000000e6, 0x000020e1,
+ 0x000020e1, 0x000000e6, 0x000020e5, 0x000020e6,
+ 0x00000001, 0x000020e7, 0x000020e7, 0x000000e6,
+ 0x000020e8, 0x000020e8, 0x000000dc, 0x000020e9,
+ 0x000020e9, 0x000000e6, 0x000020ea, 0x000020ea,
+ 0x00000001, 0x0000302a, 0x0000302a, 0x000000da,
+ 0x0000302b, 0x0000302b, 0x000000e4, 0x0000302c,
+ 0x0000302c, 0x000000e8, 0x0000302d, 0x0000302d,
+ 0x000000de, 0x0000302e, 0x0000302f, 0x000000e0,
+ 0x00003099, 0x0000309a, 0x00000008, 0x0000fb1e,
+ 0x0000fb1e, 0x0000001a, 0x0000fe20, 0x0000fe23,
+ 0x000000e6, 0x0001d165, 0x0001d166, 0x000000d8,
+ 0x0001d167, 0x0001d169, 0x00000001, 0x0001d16d,
+ 0x0001d16d, 0x000000e2, 0x0001d16e, 0x0001d172,
+ 0x000000d8, 0x0001d17b, 0x0001d182, 0x000000dc,
+ 0x0001d185, 0x0001d189, 0x000000e6, 0x0001d18a,
+ 0x0001d18b, 0x000000dc, 0x0001d1aa, 0x0001d1ad,
+ 0x000000e6
+};
+
+static const ac_uint4 _ucnum_size = 1066;
+
+static const ac_uint4 _ucnum_nodes[] = {
+ 0x00000030, 0x00000000, 0x00000031, 0x00000002,
+ 0x00000032, 0x00000004, 0x00000033, 0x00000006,
+ 0x00000034, 0x00000008, 0x00000035, 0x0000000a,
+ 0x00000036, 0x0000000c, 0x00000037, 0x0000000e,
+ 0x00000038, 0x00000010, 0x00000039, 0x00000012,
+ 0x000000b2, 0x00000004, 0x000000b3, 0x00000006,
+ 0x000000b9, 0x00000002, 0x000000bc, 0x00000014,
+ 0x000000bd, 0x00000016, 0x000000be, 0x00000018,
+ 0x00000660, 0x00000000, 0x00000661, 0x00000002,
+ 0x00000662, 0x00000004, 0x00000663, 0x00000006,
+ 0x00000664, 0x00000008, 0x00000665, 0x0000000a,
+ 0x00000666, 0x0000000c, 0x00000667, 0x0000000e,
+ 0x00000668, 0x00000010, 0x00000669, 0x00000012,
+ 0x000006f0, 0x00000000, 0x000006f1, 0x00000002,
+ 0x000006f2, 0x00000004, 0x000006f3, 0x00000006,
+ 0x000006f4, 0x00000008, 0x000006f5, 0x0000000a,
+ 0x000006f6, 0x0000000c, 0x000006f7, 0x0000000e,
+ 0x000006f8, 0x00000010, 0x000006f9, 0x00000012,
+ 0x00000966, 0x00000000, 0x00000967, 0x00000002,
+ 0x00000968, 0x00000004, 0x00000969, 0x00000006,
+ 0x0000096a, 0x00000008, 0x0000096b, 0x0000000a,
+ 0x0000096c, 0x0000000c, 0x0000096d, 0x0000000e,
+ 0x0000096e, 0x00000010, 0x0000096f, 0x00000012,
+ 0x000009e6, 0x00000000, 0x000009e7, 0x00000002,
+ 0x000009e8, 0x00000004, 0x000009e9, 0x00000006,
+ 0x000009ea, 0x00000008, 0x000009eb, 0x0000000a,
+ 0x000009ec, 0x0000000c, 0x000009ed, 0x0000000e,
+ 0x000009ee, 0x00000010, 0x000009ef, 0x00000012,
+ 0x000009f4, 0x00000002, 0x000009f5, 0x00000004,
+ 0x000009f6, 0x00000006, 0x000009f7, 0x00000008,
+ 0x000009f9, 0x0000001a, 0x00000a66, 0x00000000,
+ 0x00000a67, 0x00000002, 0x00000a68, 0x00000004,
+ 0x00000a69, 0x00000006, 0x00000a6a, 0x00000008,
+ 0x00000a6b, 0x0000000a, 0x00000a6c, 0x0000000c,
+ 0x00000a6d, 0x0000000e, 0x00000a6e, 0x00000010,
+ 0x00000a6f, 0x00000012, 0x00000ae6, 0x00000000,
+ 0x00000ae7, 0x00000002, 0x00000ae8, 0x00000004,
+ 0x00000ae9, 0x00000006, 0x00000aea, 0x00000008,
+ 0x00000aeb, 0x0000000a, 0x00000aec, 0x0000000c,
+ 0x00000aed, 0x0000000e, 0x00000aee, 0x00000010,
+ 0x00000aef, 0x00000012, 0x00000b66, 0x00000000,
+ 0x00000b67, 0x00000002, 0x00000b68, 0x00000004,
+ 0x00000b69, 0x00000006, 0x00000b6a, 0x00000008,
+ 0x00000b6b, 0x0000000a, 0x00000b6c, 0x0000000c,
+ 0x00000b6d, 0x0000000e, 0x00000b6e, 0x00000010,
+ 0x00000b6f, 0x00000012, 0x00000be7, 0x00000002,
+ 0x00000be8, 0x00000004, 0x00000be9, 0x00000006,
+ 0x00000bea, 0x00000008, 0x00000beb, 0x0000000a,
+ 0x00000bec, 0x0000000c, 0x00000bed, 0x0000000e,
+ 0x00000bee, 0x00000010, 0x00000bef, 0x00000012,
+ 0x00000bf0, 0x0000001c, 0x00000bf1, 0x0000001e,
+ 0x00000bf2, 0x00000020, 0x00000c66, 0x00000000,
+ 0x00000c67, 0x00000002, 0x00000c68, 0x00000004,
+ 0x00000c69, 0x00000006, 0x00000c6a, 0x00000008,
+ 0x00000c6b, 0x0000000a, 0x00000c6c, 0x0000000c,
+ 0x00000c6d, 0x0000000e, 0x00000c6e, 0x00000010,
+ 0x00000c6f, 0x00000012, 0x00000ce6, 0x00000000,
+ 0x00000ce7, 0x00000002, 0x00000ce8, 0x00000004,
+ 0x00000ce9, 0x00000006, 0x00000cea, 0x00000008,
+ 0x00000ceb, 0x0000000a, 0x00000cec, 0x0000000c,
+ 0x00000ced, 0x0000000e, 0x00000cee, 0x00000010,
+ 0x00000cef, 0x00000012, 0x00000d66, 0x00000000,
+ 0x00000d67, 0x00000002, 0x00000d68, 0x00000004,
+ 0x00000d69, 0x00000006, 0x00000d6a, 0x00000008,
+ 0x00000d6b, 0x0000000a, 0x00000d6c, 0x0000000c,
+ 0x00000d6d, 0x0000000e, 0x00000d6e, 0x00000010,
+ 0x00000d6f, 0x00000012, 0x00000e50, 0x00000000,
+ 0x00000e51, 0x00000002, 0x00000e52, 0x00000004,
+ 0x00000e53, 0x00000006, 0x00000e54, 0x00000008,
+ 0x00000e55, 0x0000000a, 0x00000e56, 0x0000000c,
+ 0x00000e57, 0x0000000e, 0x00000e58, 0x00000010,
+ 0x00000e59, 0x00000012, 0x00000ed0, 0x00000000,
+ 0x00000ed1, 0x00000002, 0x00000ed2, 0x00000004,
+ 0x00000ed3, 0x00000006, 0x00000ed4, 0x00000008,
+ 0x00000ed5, 0x0000000a, 0x00000ed6, 0x0000000c,
+ 0x00000ed7, 0x0000000e, 0x00000ed8, 0x00000010,
+ 0x00000ed9, 0x00000012, 0x00000f20, 0x00000000,
+ 0x00000f21, 0x00000002, 0x00000f22, 0x00000004,
+ 0x00000f23, 0x00000006, 0x00000f24, 0x00000008,
+ 0x00000f25, 0x0000000a, 0x00000f26, 0x0000000c,
+ 0x00000f27, 0x0000000e, 0x00000f28, 0x00000010,
+ 0x00000f29, 0x00000012, 0x00000f2a, 0x00000016,
+ 0x00000f2b, 0x00000022, 0x00000f2c, 0x00000024,
+ 0x00000f2d, 0x00000026, 0x00000f2e, 0x00000028,
+ 0x00000f2f, 0x0000002a, 0x00000f30, 0x0000002c,
+ 0x00000f31, 0x0000002e, 0x00000f32, 0x00000030,
+ 0x00000f33, 0x00000032, 0x00001040, 0x00000000,
+ 0x00001041, 0x00000002, 0x00001042, 0x00000004,
+ 0x00001043, 0x00000006, 0x00001044, 0x00000008,
+ 0x00001045, 0x0000000a, 0x00001046, 0x0000000c,
+ 0x00001047, 0x0000000e, 0x00001048, 0x00000010,
+ 0x00001049, 0x00000012, 0x00001369, 0x00000002,
+ 0x0000136a, 0x00000004, 0x0000136b, 0x00000006,
+ 0x0000136c, 0x00000008, 0x0000136d, 0x0000000a,
+ 0x0000136e, 0x0000000c, 0x0000136f, 0x0000000e,
+ 0x00001370, 0x00000010, 0x00001371, 0x00000012,
+ 0x00001372, 0x0000001c, 0x00001373, 0x00000034,
+ 0x00001374, 0x00000036, 0x00001375, 0x00000038,
+ 0x00001376, 0x0000003a, 0x00001377, 0x0000003c,
+ 0x00001378, 0x0000003e, 0x00001379, 0x00000040,
+ 0x0000137a, 0x00000042, 0x0000137b, 0x0000001e,
+ 0x0000137c, 0x00000044, 0x000016ee, 0x00000046,
+ 0x000016ef, 0x00000048, 0x000016f0, 0x0000004a,
+ 0x000017e0, 0x00000000, 0x000017e1, 0x00000002,
+ 0x000017e2, 0x00000004, 0x000017e3, 0x00000006,
+ 0x000017e4, 0x00000008, 0x000017e5, 0x0000000a,
+ 0x000017e6, 0x0000000c, 0x000017e7, 0x0000000e,
+ 0x000017e8, 0x00000010, 0x000017e9, 0x00000012,
+ 0x00001810, 0x00000000, 0x00001811, 0x00000002,
+ 0x00001812, 0x00000004, 0x00001813, 0x00000006,
+ 0x00001814, 0x00000008, 0x00001815, 0x0000000a,
+ 0x00001816, 0x0000000c, 0x00001817, 0x0000000e,
+ 0x00001818, 0x00000010, 0x00001819, 0x00000012,
+ 0x00002070, 0x00000000, 0x00002074, 0x00000008,
+ 0x00002075, 0x0000000a, 0x00002076, 0x0000000c,
+ 0x00002077, 0x0000000e, 0x00002078, 0x00000010,
+ 0x00002079, 0x00000012, 0x00002080, 0x00000000,
+ 0x00002081, 0x00000002, 0x00002082, 0x00000004,
+ 0x00002083, 0x00000006, 0x00002084, 0x00000008,
+ 0x00002085, 0x0000000a, 0x00002086, 0x0000000c,
+ 0x00002087, 0x0000000e, 0x00002088, 0x00000010,
+ 0x00002089, 0x00000012, 0x00002153, 0x0000004c,
+ 0x00002154, 0x0000004e, 0x00002155, 0x00000050,
+ 0x00002156, 0x00000052, 0x00002157, 0x00000054,
+ 0x00002158, 0x00000056, 0x00002159, 0x00000058,
+ 0x0000215a, 0x0000005a, 0x0000215b, 0x0000005c,
+ 0x0000215c, 0x0000005e, 0x0000215d, 0x00000060,
+ 0x0000215e, 0x00000062, 0x0000215f, 0x00000002,
+ 0x00002160, 0x00000002, 0x00002161, 0x00000004,
+ 0x00002162, 0x00000006, 0x00002163, 0x00000008,
+ 0x00002164, 0x0000000a, 0x00002165, 0x0000000c,
+ 0x00002166, 0x0000000e, 0x00002167, 0x00000010,
+ 0x00002168, 0x00000012, 0x00002169, 0x0000001c,
+ 0x0000216a, 0x00000064, 0x0000216b, 0x00000066,
+ 0x0000216c, 0x0000003a, 0x0000216d, 0x0000001e,
+ 0x0000216e, 0x00000068, 0x0000216f, 0x00000020,
+ 0x00002170, 0x00000002, 0x00002171, 0x00000004,
+ 0x00002172, 0x00000006, 0x00002173, 0x00000008,
+ 0x00002174, 0x0000000a, 0x00002175, 0x0000000c,
+ 0x00002176, 0x0000000e, 0x00002177, 0x00000010,
+ 0x00002178, 0x00000012, 0x00002179, 0x0000001c,
+ 0x0000217a, 0x00000064, 0x0000217b, 0x00000066,
+ 0x0000217c, 0x0000003a, 0x0000217d, 0x0000001e,
+ 0x0000217e, 0x00000068, 0x0000217f, 0x00000020,
+ 0x00002180, 0x00000020, 0x00002181, 0x0000006a,
+ 0x00002182, 0x00000044, 0x00002460, 0x00000002,
+ 0x00002461, 0x00000004, 0x00002462, 0x00000006,
+ 0x00002463, 0x00000008, 0x00002464, 0x0000000a,
+ 0x00002465, 0x0000000c, 0x00002466, 0x0000000e,
+ 0x00002467, 0x00000010, 0x00002468, 0x00000012,
+ 0x00002469, 0x0000001c, 0x0000246a, 0x00000064,
+ 0x0000246b, 0x00000066, 0x0000246c, 0x0000006c,
+ 0x0000246d, 0x0000006e, 0x0000246e, 0x00000070,
+ 0x0000246f, 0x0000001a, 0x00002470, 0x00000046,
+ 0x00002471, 0x00000048, 0x00002472, 0x0000004a,
+ 0x00002473, 0x00000034, 0x00002474, 0x00000002,
+ 0x00002475, 0x00000004, 0x00002476, 0x00000006,
+ 0x00002477, 0x00000008, 0x00002478, 0x0000000a,
+ 0x00002479, 0x0000000c, 0x0000247a, 0x0000000e,
+ 0x0000247b, 0x00000010, 0x0000247c, 0x00000012,
+ 0x0000247d, 0x0000001c, 0x0000247e, 0x00000064,
+ 0x0000247f, 0x00000066, 0x00002480, 0x0000006c,
+ 0x00002481, 0x0000006e, 0x00002482, 0x00000070,
+ 0x00002483, 0x0000001a, 0x00002484, 0x00000046,
+ 0x00002485, 0x00000048, 0x00002486, 0x0000004a,
+ 0x00002487, 0x00000034, 0x00002488, 0x00000002,
+ 0x00002489, 0x00000004, 0x0000248a, 0x00000006,
+ 0x0000248b, 0x00000008, 0x0000248c, 0x0000000a,
+ 0x0000248d, 0x0000000c, 0x0000248e, 0x0000000e,
+ 0x0000248f, 0x00000010, 0x00002490, 0x00000012,
+ 0x00002491, 0x0000001c, 0x00002492, 0x00000064,
+ 0x00002493, 0x00000066, 0x00002494, 0x0000006c,
+ 0x00002495, 0x0000006e, 0x00002496, 0x00000070,
+ 0x00002497, 0x0000001a, 0x00002498, 0x00000046,
+ 0x00002499, 0x00000048, 0x0000249a, 0x0000004a,
+ 0x0000249b, 0x00000034, 0x000024ea, 0x00000000,
+ 0x000024eb, 0x00000064, 0x000024ec, 0x00000066,
+ 0x000024ed, 0x0000006c, 0x000024ee, 0x0000006e,
+ 0x000024ef, 0x00000070, 0x000024f0, 0x0000001a,
+ 0x000024f1, 0x00000046, 0x000024f2, 0x00000048,
+ 0x000024f3, 0x0000004a, 0x000024f4, 0x00000034,
+ 0x000024f5, 0x00000002, 0x000024f6, 0x00000004,
+ 0x000024f7, 0x00000006, 0x000024f8, 0x00000008,
+ 0x000024f9, 0x0000000a, 0x000024fa, 0x0000000c,
+ 0x000024fb, 0x0000000e, 0x000024fc, 0x00000010,
+ 0x000024fd, 0x00000012, 0x000024fe, 0x0000001c,
+ 0x00002776, 0x00000002, 0x00002777, 0x00000004,
+ 0x00002778, 0x00000006, 0x00002779, 0x00000008,
+ 0x0000277a, 0x0000000a, 0x0000277b, 0x0000000c,
+ 0x0000277c, 0x0000000e, 0x0000277d, 0x00000010,
+ 0x0000277e, 0x00000012, 0x0000277f, 0x0000001c,
+ 0x00002780, 0x00000002, 0x00002781, 0x00000004,
+ 0x00002782, 0x00000006, 0x00002783, 0x00000008,
+ 0x00002784, 0x0000000a, 0x00002785, 0x0000000c,
+ 0x00002786, 0x0000000e, 0x00002787, 0x00000010,
+ 0x00002788, 0x00000012, 0x00002789, 0x0000001c,
+ 0x0000278a, 0x00000002, 0x0000278b, 0x00000004,
+ 0x0000278c, 0x00000006, 0x0000278d, 0x00000008,
+ 0x0000278e, 0x0000000a, 0x0000278f, 0x0000000c,
+ 0x00002790, 0x0000000e, 0x00002791, 0x00000010,
+ 0x00002792, 0x00000012, 0x00002793, 0x0000001c,
+ 0x00003007, 0x00000000, 0x00003021, 0x00000002,
+ 0x00003022, 0x00000004, 0x00003023, 0x00000006,
+ 0x00003024, 0x00000008, 0x00003025, 0x0000000a,
+ 0x00003026, 0x0000000c, 0x00003027, 0x0000000e,
+ 0x00003028, 0x00000010, 0x00003029, 0x00000012,
+ 0x00003038, 0x0000001c, 0x00003039, 0x00000034,
+ 0x0000303a, 0x00000036, 0x00003192, 0x00000002,
+ 0x00003193, 0x00000004, 0x00003194, 0x00000006,
+ 0x00003195, 0x00000008, 0x00003220, 0x00000002,
+ 0x00003221, 0x00000004, 0x00003222, 0x00000006,
+ 0x00003223, 0x00000008, 0x00003224, 0x0000000a,
+ 0x00003225, 0x0000000c, 0x00003226, 0x0000000e,
+ 0x00003227, 0x00000010, 0x00003228, 0x00000012,
+ 0x00003229, 0x0000001c, 0x00003251, 0x00000072,
+ 0x00003252, 0x00000074, 0x00003253, 0x00000076,
+ 0x00003254, 0x00000078, 0x00003255, 0x0000007a,
+ 0x00003256, 0x0000007c, 0x00003257, 0x0000007e,
+ 0x00003258, 0x00000080, 0x00003259, 0x00000082,
+ 0x0000325a, 0x00000036, 0x0000325b, 0x00000084,
+ 0x0000325c, 0x00000086, 0x0000325d, 0x00000088,
+ 0x0000325e, 0x0000008a, 0x0000325f, 0x0000008c,
+ 0x00003280, 0x00000002, 0x00003281, 0x00000004,
+ 0x00003282, 0x00000006, 0x00003283, 0x00000008,
+ 0x00003284, 0x0000000a, 0x00003285, 0x0000000c,
+ 0x00003286, 0x0000000e, 0x00003287, 0x00000010,
+ 0x00003288, 0x00000012, 0x00003289, 0x0000001c,
+ 0x000032b1, 0x0000008e, 0x000032b2, 0x00000090,
+ 0x000032b3, 0x00000092, 0x000032b4, 0x00000094,
+ 0x000032b5, 0x00000038, 0x000032b6, 0x00000096,
+ 0x000032b7, 0x00000098, 0x000032b8, 0x0000009a,
+ 0x000032b9, 0x0000009c, 0x000032ba, 0x0000009e,
+ 0x000032bb, 0x000000a0, 0x000032bc, 0x000000a2,
+ 0x000032bd, 0x000000a4, 0x000032be, 0x000000a6,
+ 0x000032bf, 0x0000003a, 0x0000ff10, 0x00000000,
+ 0x0000ff11, 0x00000002, 0x0000ff12, 0x00000004,
+ 0x0000ff13, 0x00000006, 0x0000ff14, 0x00000008,
+ 0x0000ff15, 0x0000000a, 0x0000ff16, 0x0000000c,
+ 0x0000ff17, 0x0000000e, 0x0000ff18, 0x00000010,
+ 0x0000ff19, 0x00000012, 0x00010320, 0x00000002,
+ 0x00010321, 0x0000000a, 0x00010322, 0x0000001c,
+ 0x00010323, 0x0000003a, 0x0001d7ce, 0x00000000,
+ 0x0001d7cf, 0x00000002, 0x0001d7d0, 0x00000004,
+ 0x0001d7d1, 0x00000006, 0x0001d7d2, 0x00000008,
+ 0x0001d7d3, 0x0000000a, 0x0001d7d4, 0x0000000c,
+ 0x0001d7d5, 0x0000000e, 0x0001d7d6, 0x00000010,
+ 0x0001d7d7, 0x00000012, 0x0001d7d8, 0x00000000,
+ 0x0001d7d9, 0x00000002, 0x0001d7da, 0x00000004,
+ 0x0001d7db, 0x00000006, 0x0001d7dc, 0x00000008,
+ 0x0001d7dd, 0x0000000a, 0x0001d7de, 0x0000000c,
+ 0x0001d7df, 0x0000000e, 0x0001d7e0, 0x00000010,
+ 0x0001d7e1, 0x00000012, 0x0001d7e2, 0x00000000,
+ 0x0001d7e3, 0x00000002, 0x0001d7e4, 0x00000004,
+ 0x0001d7e5, 0x00000006, 0x0001d7e6, 0x00000008,
+ 0x0001d7e7, 0x0000000a, 0x0001d7e8, 0x0000000c,
+ 0x0001d7e9, 0x0000000e, 0x0001d7ea, 0x00000010,
+ 0x0001d7eb, 0x00000012, 0x0001d7ec, 0x00000000,
+ 0x0001d7ed, 0x00000002, 0x0001d7ee, 0x00000004,
+ 0x0001d7ef, 0x00000006, 0x0001d7f0, 0x00000008,
+ 0x0001d7f1, 0x0000000a, 0x0001d7f2, 0x0000000c,
+ 0x0001d7f3, 0x0000000e, 0x0001d7f4, 0x00000010,
+ 0x0001d7f5, 0x00000012, 0x0001d7f6, 0x00000000,
+ 0x0001d7f7, 0x00000002, 0x0001d7f8, 0x00000004,
+ 0x0001d7f9, 0x00000006, 0x0001d7fa, 0x00000008,
+ 0x0001d7fb, 0x0000000a, 0x0001d7fc, 0x0000000c,
+ 0x0001d7fd, 0x0000000e, 0x0001d7fe, 0x00000010,
+ 0x0001d7ff, 0x00000012
+};
+
+static const short _ucnum_vals[] = {
+ 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0003, 0x0001,
+ 0x0004, 0x0001, 0x0005, 0x0001, 0x0006, 0x0001, 0x0007, 0x0001,
+ 0x0008, 0x0001, 0x0009, 0x0001, 0x0001, 0x0004, 0x0001, 0x0002,
+ 0x0003, 0x0004, 0x0010, 0x0001, 0x000a, 0x0001, 0x0064, 0x0001,
+ 0x03e8, 0x0001, 0x0003, 0x0002, 0x0005, 0x0002, 0x0007, 0x0002,
+ 0x0009, 0x0002, 0x000b, 0x0002, 0x000d, 0x0002, 0x000f, 0x0002,
+ 0x0011, 0x0002, -1, 0x0002, 0x0014, 0x0001, 0x001e, 0x0001,
+ 0x0028, 0x0001, 0x0032, 0x0001, 0x003c, 0x0001, 0x0046, 0x0001,
+ 0x0050, 0x0001, 0x005a, 0x0001, 0x2710, 0x0001, 0x0011, 0x0001,
+ 0x0012, 0x0001, 0x0013, 0x0001, 0x0001, 0x0003, 0x0002, 0x0003,
+ 0x0001, 0x0005, 0x0002, 0x0005, 0x0003, 0x0005, 0x0004, 0x0005,
+ 0x0001, 0x0006, 0x0005, 0x0006, 0x0001, 0x0008, 0x0003, 0x0008,
+ 0x0005, 0x0008, 0x0007, 0x0008, 0x000b, 0x0001, 0x000c, 0x0001,
+ 0x01f4, 0x0001, 0x1388, 0x0001, 0x000d, 0x0001, 0x000e, 0x0001,
+ 0x000f, 0x0001, 0x0015, 0x0001, 0x0016, 0x0001, 0x0017, 0x0001,
+ 0x0018, 0x0001, 0x0019, 0x0001, 0x001a, 0x0001, 0x001b, 0x0001,
+ 0x001c, 0x0001, 0x001d, 0x0001, 0x001f, 0x0001, 0x0020, 0x0001,
+ 0x0021, 0x0001, 0x0022, 0x0001, 0x0023, 0x0001, 0x0024, 0x0001,
+ 0x0025, 0x0001, 0x0026, 0x0001, 0x0027, 0x0001, 0x0029, 0x0001,
+ 0x002a, 0x0001, 0x002b, 0x0001, 0x002c, 0x0001, 0x002d, 0x0001,
+ 0x002e, 0x0001, 0x002f, 0x0001, 0x0030, 0x0001, 0x0031, 0x0001
+};
+
diff --git a/libraries/liblunicode/ucstr.c b/libraries/liblunicode/ucstr.c
new file mode 100644
index 0000000..e95db43
--- /dev/null
+++ b/libraries/liblunicode/ucstr.c
@@ -0,0 +1,459 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <ac/bytes.h>
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/stdlib.h>
+
+#include <lber_pvt.h>
+
+#include <ldap_utf8.h>
+#include <ldap_pvt_uc.h>
+
+#define malloc(x) ber_memalloc_x(x,ctx)
+#define realloc(x,y) ber_memrealloc_x(x,y,ctx)
+#define free(x) ber_memfree_x(x,ctx)
+
+int ucstrncmp(
+ const ldap_unicode_t *u1,
+ const ldap_unicode_t *u2,
+ ber_len_t n )
+{
+ for(; 0 < n; ++u1, ++u2, --n ) {
+ if( *u1 != *u2 ) {
+ return *u1 < *u2 ? -1 : +1;
+ }
+ if ( *u1 == 0 ) {
+ return 0;
+ }
+ }
+ return 0;
+}
+
+int ucstrncasecmp(
+ const ldap_unicode_t *u1,
+ const ldap_unicode_t *u2,
+ ber_len_t n )
+{
+ for(; 0 < n; ++u1, ++u2, --n ) {
+ ldap_unicode_t uu1 = uctolower( *u1 );
+ ldap_unicode_t uu2 = uctolower( *u2 );
+
+ if( uu1 != uu2 ) {
+ return uu1 < uu2 ? -1 : +1;
+ }
+ if ( uu1 == 0 ) {
+ return 0;
+ }
+ }
+ return 0;
+}
+
+ldap_unicode_t * ucstrnchr(
+ const ldap_unicode_t *u,
+ ber_len_t n,
+ ldap_unicode_t c )
+{
+ for(; 0 < n; ++u, --n ) {
+ if( *u == c ) {
+ return (ldap_unicode_t *) u;
+ }
+ }
+
+ return NULL;
+}
+
+ldap_unicode_t * ucstrncasechr(
+ const ldap_unicode_t *u,
+ ber_len_t n,
+ ldap_unicode_t c )
+{
+ c = uctolower( c );
+ for(; 0 < n; ++u, --n ) {
+ if( uctolower( *u ) == c ) {
+ return (ldap_unicode_t *) u;
+ }
+ }
+
+ return NULL;
+}
+
+void ucstr2upper(
+ ldap_unicode_t *u,
+ ber_len_t n )
+{
+ for(; 0 < n; ++u, --n ) {
+ *u = uctoupper( *u );
+ }
+}
+
+struct berval * UTF8bvnormalize(
+ struct berval *bv,
+ struct berval *newbv,
+ unsigned flags,
+ void *ctx )
+{
+ int i, j, len, clen, outpos, ucsoutlen, outsize, last;
+ int didnewbv = 0;
+ char *out, *outtmp, *s;
+ ac_uint4 *ucs, *p, *ucsout;
+
+ static unsigned char mask[] = {
+ 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 };
+
+ unsigned casefold = flags & LDAP_UTF8_CASEFOLD;
+ unsigned approx = flags & LDAP_UTF8_APPROX;
+
+ if ( bv == NULL ) {
+ return NULL;
+ }
+
+ s = bv->bv_val;
+ len = bv->bv_len;
+
+ if ( len == 0 ) {
+ return ber_dupbv_x( newbv, bv, ctx );
+ }
+
+ if ( !newbv ) {
+ newbv = ber_memalloc_x( sizeof(struct berval), ctx );
+ if ( !newbv ) return NULL;
+ didnewbv = 1;
+ }
+
+ /* Should first check to see if string is already in proper
+ * normalized form. This is almost as time consuming as
+ * the normalization though.
+ */
+
+ /* finish off everything up to character before first non-ascii */
+ if ( LDAP_UTF8_ISASCII( s ) ) {
+ if ( casefold ) {
+ outsize = len + 7;
+ out = (char *) ber_memalloc_x( outsize, ctx );
+ if ( out == NULL ) {
+fail:
+ if ( didnewbv )
+ ber_memfree_x( newbv, ctx );
+ return NULL;
+ }
+ outpos = 0;
+
+ for ( i = 1; (i < len) && LDAP_UTF8_ISASCII(s + i); i++ ) {
+ out[outpos++] = TOLOWER( s[i-1] );
+ }
+ if ( i == len ) {
+ out[outpos++] = TOLOWER( s[len-1] );
+ out[outpos] = '\0';
+ newbv->bv_val = out;
+ newbv->bv_len = outpos;
+ return newbv;
+ }
+ } else {
+ for ( i = 1; (i < len) && LDAP_UTF8_ISASCII(s + i); i++ ) {
+ /* empty */
+ }
+
+ if ( i == len ) {
+ return ber_str2bv_x( s, len, 1, newbv, ctx );
+ }
+
+ outsize = len + 7;
+ out = (char *) ber_memalloc_x( outsize, ctx );
+ if ( out == NULL ) {
+ goto fail;
+ }
+ outpos = i - 1;
+ memcpy(out, s, outpos);
+ }
+ } else {
+ outsize = len + 7;
+ out = (char *) ber_memalloc_x( outsize, ctx );
+ if ( out == NULL ) {
+ goto fail;
+ }
+ outpos = 0;
+ i = 0;
+ }
+
+ p = ucs = ber_memalloc_x( len * sizeof(*ucs), ctx );
+ if ( ucs == NULL ) {
+ ber_memfree_x(out, ctx);
+ goto fail;
+ }
+
+ /* convert character before first non-ascii to ucs-4 */
+ if ( i > 0 ) {
+ *p = casefold ? TOLOWER( s[i-1] ) : s[i-1];
+ p++;
+ }
+
+ /* s[i] is now first non-ascii character */
+ for (;;) {
+ /* s[i] is non-ascii */
+ /* convert everything up to next ascii to ucs-4 */
+ while ( i < len ) {
+ clen = LDAP_UTF8_CHARLEN2( s + i, clen );
+ if ( clen == 0 ) {
+ ber_memfree_x( ucs, ctx );
+ ber_memfree_x( out, ctx );
+ goto fail;
+ }
+ if ( clen == 1 ) {
+ /* ascii */
+ break;
+ }
+ *p = s[i] & mask[clen];
+ i++;
+ for( j = 1; j < clen; j++ ) {
+ if ( (s[i] & 0xc0) != 0x80 ) {
+ ber_memfree_x( ucs, ctx );
+ ber_memfree_x( out, ctx );
+ goto fail;
+ }
+ *p <<= 6;
+ *p |= s[i] & 0x3f;
+ i++;
+ }
+ if ( casefold ) {
+ *p = uctolower( *p );
+ }
+ p++;
+ }
+ /* normalize ucs of length p - ucs */
+ uccompatdecomp( ucs, p - ucs, &ucsout, &ucsoutlen, ctx );
+ if ( approx ) {
+ for ( j = 0; j < ucsoutlen; j++ ) {
+ if ( ucsout[j] < 0x80 ) {
+ out[outpos++] = ucsout[j];
+ }
+ }
+ } else {
+ ucsoutlen = uccanoncomp( ucsout, ucsoutlen );
+ /* convert ucs to utf-8 and store in out */
+ for ( j = 0; j < ucsoutlen; j++ ) {
+ /* allocate more space if not enough room for
+ 6 bytes and terminator */
+ if ( outsize - outpos < 7 ) {
+ outsize = ucsoutlen - j + outpos + 6;
+ outtmp = (char *) ber_memrealloc_x( out, outsize, ctx );
+ if ( outtmp == NULL ) {
+ ber_memfree_x( ucsout, ctx );
+ ber_memfree_x( ucs, ctx );
+ ber_memfree_x( out, ctx );
+ goto fail;
+ }
+ out = outtmp;
+ }
+ outpos += ldap_x_ucs4_to_utf8( ucsout[j], &out[outpos] );
+ }
+ }
+
+ ber_memfree_x( ucsout, ctx );
+ ucsout = NULL;
+
+ if ( i == len ) {
+ break;
+ }
+
+ last = i;
+
+ /* Allocate more space in out if necessary */
+ if (len - i >= outsize - outpos) {
+ outsize += 1 + ((len - i) - (outsize - outpos));
+ outtmp = (char *) ber_memrealloc_x(out, outsize, ctx);
+ if (outtmp == NULL) {
+ ber_memfree_x( ucs, ctx );
+ ber_memfree_x( out, ctx );
+ goto fail;
+ }
+ out = outtmp;
+ }
+
+ /* s[i] is ascii */
+ /* finish off everything up to char before next non-ascii */
+ for ( i++; (i < len) && LDAP_UTF8_ISASCII(s + i); i++ ) {
+ out[outpos++] = casefold ? TOLOWER( s[i-1] ) : s[i-1];
+ }
+ if ( i == len ) {
+ out[outpos++] = casefold ? TOLOWER( s[len-1] ) : s[len-1];
+ break;
+ }
+
+ /* convert character before next non-ascii to ucs-4 */
+ *ucs = casefold ? TOLOWER( s[i-1] ) : s[i-1];
+ p = ucs + 1;
+ }
+
+ ber_memfree_x( ucs, ctx );
+ out[outpos] = '\0';
+ newbv->bv_val = out;
+ newbv->bv_len = outpos;
+ return newbv;
+}
+
+/* compare UTF8-strings, optionally ignore casing */
+/* slow, should be optimized */
+int UTF8bvnormcmp(
+ struct berval *bv1,
+ struct berval *bv2,
+ unsigned flags,
+ void *ctx )
+{
+ int i, l1, l2, len, ulen, res = 0;
+ char *s1, *s2, *done;
+ ac_uint4 *ucs, *ucsout1, *ucsout2;
+
+ unsigned casefold = flags & LDAP_UTF8_CASEFOLD;
+ unsigned norm1 = flags & LDAP_UTF8_ARG1NFC;
+ unsigned norm2 = flags & LDAP_UTF8_ARG2NFC;
+
+ if (bv1 == NULL) {
+ return bv2 == NULL ? 0 : -1;
+
+ } else if (bv2 == NULL) {
+ return 1;
+ }
+
+ l1 = bv1->bv_len;
+ l2 = bv2->bv_len;
+
+ len = (l1 < l2) ? l1 : l2;
+ if (len == 0) {
+ return l1 == 0 ? (l2 == 0 ? 0 : -1) : 1;
+ }
+
+ s1 = bv1->bv_val;
+ s2 = bv2->bv_val;
+ done = s1 + len;
+
+ while ( (s1 < done) && LDAP_UTF8_ISASCII(s1) && LDAP_UTF8_ISASCII(s2) ) {
+ if (casefold) {
+ char c1 = TOLOWER(*s1);
+ char c2 = TOLOWER(*s2);
+ res = c1 - c2;
+ } else {
+ res = *s1 - *s2;
+ }
+ s1++;
+ s2++;
+ if (res) {
+ /* done unless next character in s1 or s2 is non-ascii */
+ if (s1 < done) {
+ if (!LDAP_UTF8_ISASCII(s1) || !LDAP_UTF8_ISASCII(s2)) {
+ break;
+ }
+ } else if (((len < l1) && !LDAP_UTF8_ISASCII(s1)) ||
+ ((len < l2) && !LDAP_UTF8_ISASCII(s2)))
+ {
+ break;
+ }
+ return res;
+ }
+ }
+
+ /* We have encountered non-ascii or strings equal up to len */
+
+ /* set i to number of iterations */
+ i = s1 - done + len;
+ /* passed through loop at least once? */
+ if (i > 0) {
+ if (!res && (s1 == done) &&
+ ((len == l1) || LDAP_UTF8_ISASCII(s1)) &&
+ ((len == l2) || LDAP_UTF8_ISASCII(s2))) {
+ /* all ascii and equal up to len */
+ return l1 - l2;
+ }
+
+ /* rewind one char, and do normalized compare from there */
+ s1--;
+ s2--;
+ l1 -= i - 1;
+ l2 -= i - 1;
+ }
+
+ /* Should first check to see if strings are already in
+ * proper normalized form.
+ */
+ ucs = malloc( ( ( norm1 || l1 > l2 ) ? l1 : l2 ) * sizeof(*ucs) );
+ if ( ucs == NULL ) {
+ return l1 > l2 ? 1 : -1; /* what to do??? */
+ }
+
+ /*
+ * XXYYZ: we convert to ucs4 even though -llunicode
+ * expects ucs2 in an ac_uint4
+ */
+
+ /* convert and normalize 1st string */
+ for ( i = 0, ulen = 0; i < l1; i += len, ulen++ ) {
+ ucs[ulen] = ldap_x_utf8_to_ucs4( s1 + i );
+ if ( ucs[ulen] == LDAP_UCS4_INVALID ) {
+ free( ucs );
+ return -1; /* what to do??? */
+ }
+ len = LDAP_UTF8_CHARLEN( s1 + i );
+ }
+
+ if ( norm1 ) {
+ ucsout1 = ucs;
+ l1 = ulen;
+ ucs = malloc( l2 * sizeof(*ucs) );
+ if ( ucs == NULL ) {
+ free( ucsout1 );
+ return l1 > l2 ? 1 : -1; /* what to do??? */
+ }
+ } else {
+ uccompatdecomp( ucs, ulen, &ucsout1, &l1, ctx );
+ l1 = uccanoncomp( ucsout1, l1 );
+ }
+
+ /* convert and normalize 2nd string */
+ for ( i = 0, ulen = 0; i < l2; i += len, ulen++ ) {
+ ucs[ulen] = ldap_x_utf8_to_ucs4( s2 + i );
+ if ( ucs[ulen] == LDAP_UCS4_INVALID ) {
+ free( ucsout1 );
+ free( ucs );
+ return 1; /* what to do??? */
+ }
+ len = LDAP_UTF8_CHARLEN( s2 + i );
+ }
+
+ if ( norm2 ) {
+ ucsout2 = ucs;
+ l2 = ulen;
+ } else {
+ uccompatdecomp( ucs, ulen, &ucsout2, &l2, ctx );
+ l2 = uccanoncomp( ucsout2, l2 );
+ free( ucs );
+ }
+
+ res = casefold
+ ? ucstrncasecmp( ucsout1, ucsout2, l1 < l2 ? l1 : l2 )
+ : ucstrncmp( ucsout1, ucsout2, l1 < l2 ? l1 : l2 );
+ free( ucsout1 );
+ free( ucsout2 );
+
+ if ( res != 0 ) {
+ return res;
+ }
+ if ( l1 == l2 ) {
+ return 0;
+ }
+ return l1 > l2 ? 1 : -1;
+}
diff --git a/libraries/liblunicode/ure/README b/libraries/liblunicode/ure/README
new file mode 100644
index 0000000..c9918f5
--- /dev/null
+++ b/libraries/liblunicode/ure/README
@@ -0,0 +1,212 @@
+#
+# $Id: README,v 1.3 1999/09/21 15:47:43 mleisher Exp $
+#
+# Copyright 1997, 1998, 1999 Computing Research Labs,
+# New Mexico State University
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+# OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+# THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+
+ Unicode and Regular Expressions
+ Version 0.5
+
+This is a simple regular expression package for matching against Unicode text
+in UCS2 form. The implementation of this URE package is a variation on the
+RE->DFA algorithm done by Mark Hopkins (markh@csd4.csd.uwm.edu). Mark
+Hopkins' algorithm had the virtue of being very simple, so it was used as a
+model.
+
+---------------------------------------------------------------------------
+
+Assumptions:
+
+ o Regular expression and text already normalized.
+
+ o Conversion to lower case assumes a 1-1 mapping.
+
+Definitions:
+
+ Separator - any one of U+2028, U+2029, '\n', '\r'.
+
+Operators:
+ . - match any character.
+ * - match zero or more of the last subexpression.
+ + - match one or more of the last subexpression.
+ ? - match zero or one of the last subexpression.
+ () - subexpression grouping.
+
+ Notes:
+
+ o The "." operator normally does not match separators, but a flag is
+ available for the ure_exec() function that will allow this operator to
+ match a separator.
+
+Literals and Constants:
+
+ c - literal UCS2 character.
+ \x.... - hexadecimal number of up to 4 digits.
+ \X.... - hexadecimal number of up to 4 digits.
+ \u.... - hexadecimal number of up to 4 digits.
+ \U.... - hexadecimal number of up to 4 digits.
+
+Character classes:
+
+ [...] - Character class.
+ [^...] - Negated character class.
+ \pN1,N2,...,Nn - Character properties class.
+ \PN1,N2,...,Nn - Negated character properties class.
+
+ POSIX character classes recognized:
+
+ :alnum:
+ :alpha:
+ :cntrl:
+ :digit:
+ :graph:
+ :lower:
+ :print:
+ :punct:
+ :space:
+ :upper:
+ :xdigit:
+
+ Notes:
+
+ o Character property classes are \p or \P followed by a comma separated
+ list of integers between 1 and 32. These integers are references to
+ the following character properties:
+
+ N Character Property
+ --------------------------
+ 1 _URE_NONSPACING
+ 2 _URE_COMBINING
+ 3 _URE_NUMDIGIT
+ 4 _URE_NUMOTHER
+ 5 _URE_SPACESEP
+ 6 _URE_LINESEP
+ 7 _URE_PARASEP
+ 8 _URE_CNTRL
+ 9 _URE_PUA
+ 10 _URE_UPPER
+ 11 _URE_LOWER
+ 12 _URE_TITLE
+ 13 _URE_MODIFIER
+ 14 _URE_OTHERLETTER
+ 15 _URE_DASHPUNCT
+ 16 _URE_OPENPUNCT
+ 17 _URE_CLOSEPUNCT
+ 18 _URE_OTHERPUNCT
+ 19 _URE_MATHSYM
+ 20 _URE_CURRENCYSYM
+ 21 _URE_OTHERSYM
+ 22 _URE_LTR
+ 23 _URE_RTL
+ 24 _URE_EURONUM
+ 25 _URE_EURONUMSEP
+ 26 _URE_EURONUMTERM
+ 27 _URE_ARABNUM
+ 28 _URE_COMMONSEP
+ 29 _URE_BLOCKSEP
+ 30 _URE_SEGMENTSEP
+ 31 _URE_WHITESPACE
+ 32 _URE_OTHERNEUT
+
+ o Character classes can contain literals, constants, and character
+ property classes. Example:
+
+ [abc\U10A\p1,3,4]
+
+---------------------------------------------------------------------------
+
+Before using URE
+----------------
+Before URE is used, two functions need to be created. One to check if a
+character matches a set of URE character properties, and one to convert a
+character to lower case.
+
+Stubs for these function are located in the urestubs.c file.
+
+Using URE
+---------
+
+Sample pseudo-code fragment.
+
+ ure_buffer_t rebuf;
+ ure_dfa_t dfa;
+ ucs2_t *re, *text;
+ unsigned long relen, textlen;
+ unsigned long match_start, match_end;
+
+ /*
+ * Allocate the dynamic storage needed to compile regular expressions.
+ */
+ rebuf = ure_buffer_create();
+
+ for each regular expression in a list {
+ re = next regular expression;
+ relen = length(re);
+
+ /*
+ * Compile the regular expression with the case insensitive flag
+ * turned on.
+ */
+ dfa = ure_compile(re, relen, 1, rebuf);
+
+ /*
+ * Look for the first match in some text. The matching will be done
+ * in a case insensitive manner because the expression was compiled
+ * with the case insensitive flag on.
+ */
+ if (ure_exec(dfa, 0, text, textlen, &match_start, &match_end))
+ printf("MATCH: %ld %ld\n", match_start, match_end);
+
+ /*
+ * Look for the first match in some text, ignoring non-spacing
+ * characters.
+ */
+ if (ure_exec(dfa, URE_IGNORE_NONSPACING, text, textlen,
+ &match_start, &match_end))
+ printf("MATCH: %ld %ld\n", match_start, match_end);
+
+ /*
+ * Free the DFA.
+ */
+ ure_free_dfa(dfa);
+ }
+
+ /*
+ * Free the dynamic storage used for compiling the expressions.
+ */
+ ure_free_buffer(rebuf);
+
+---------------------------------------------------------------------------
+
+Mark Leisher <mleisher@crl.nmsu.edu>
+29 March 1997
+
+===========================================================================
+
+CHANGES
+-------
+
+Version: 0.5
+Date : 21 September 1999
+==========================
+ 1. Added copyright stuff and put in CVS.
diff --git a/libraries/liblunicode/ure/ure.c b/libraries/liblunicode/ure/ure.c
new file mode 100644
index 0000000..db67b95
--- /dev/null
+++ b/libraries/liblunicode/ure/ure.c
@@ -0,0 +1,2131 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Copyright 1997, 1998, 1999 Computing Research Labs,
+ * New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+ * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* $Id: ure.c,v 1.2 1999/09/21 15:47:43 mleisher Exp $" */
+
+#include "portable.h"
+
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include "ure.h"
+
+/*
+ * Flags used internally in the DFA.
+ */
+#define _URE_DFA_CASEFOLD 0x01
+#define _URE_DFA_BLANKLINE 0x02
+
+static unsigned long cclass_flags[] = {
+ 0,
+ _URE_NONSPACING,
+ _URE_COMBINING,
+ _URE_NUMDIGIT,
+ _URE_NUMOTHER,
+ _URE_SPACESEP,
+ _URE_LINESEP,
+ _URE_PARASEP,
+ _URE_CNTRL,
+ _URE_PUA,
+ _URE_UPPER,
+ _URE_LOWER,
+ _URE_TITLE,
+ _URE_MODIFIER,
+ _URE_OTHERLETTER,
+ _URE_DASHPUNCT,
+ _URE_OPENPUNCT,
+ _URE_CLOSEPUNCT,
+ _URE_OTHERPUNCT,
+ _URE_MATHSYM,
+ _URE_CURRENCYSYM,
+ _URE_OTHERSYM,
+ _URE_LTR,
+ _URE_RTL,
+ _URE_EURONUM,
+ _URE_EURONUMSEP,
+ _URE_EURONUMTERM,
+ _URE_ARABNUM,
+ _URE_COMMONSEP,
+ _URE_BLOCKSEP,
+ _URE_SEGMENTSEP,
+ _URE_WHITESPACE,
+ _URE_OTHERNEUT,
+};
+
+/*
+ * Symbol types for the DFA.
+ */
+#define _URE_ANY_CHAR 1
+#define _URE_CHAR 2
+#define _URE_CCLASS 3
+#define _URE_NCCLASS 4
+#define _URE_BOL_ANCHOR 5
+#define _URE_EOL_ANCHOR 6
+
+/*
+ * Op codes for converting the NFA to a DFA.
+ */
+#define _URE_SYMBOL 10
+#define _URE_PAREN 11
+#define _URE_QUEST 12
+#define _URE_STAR 13
+#define _URE_PLUS 14
+#define _URE_ONE 15
+#define _URE_AND 16
+#define _URE_OR 17
+
+#define _URE_NOOP 0xffff
+
+#define _URE_REGSTART 0x8000
+#define _URE_REGEND 0x4000
+
+/*
+ * Structure used to handle a compacted range of characters.
+ */
+typedef struct {
+ ucs4_t min_code;
+ ucs4_t max_code;
+} _ure_range_t;
+
+typedef struct {
+ _ure_range_t *ranges;
+ ucs2_t ranges_used;
+ ucs2_t ranges_size;
+} _ure_ccl_t;
+
+typedef union {
+ ucs4_t chr;
+ _ure_ccl_t ccl;
+} _ure_sym_t;
+
+/*
+ * This is a general element structure used for expressions and stack
+ * elements.
+ */
+typedef struct {
+ ucs2_t reg;
+ ucs2_t onstack;
+ ucs2_t type;
+ ucs2_t lhs;
+ ucs2_t rhs;
+} _ure_elt_t;
+
+/*
+ * This is a structure used to track a list or a stack of states.
+ */
+typedef struct {
+ ucs2_t *slist;
+ ucs2_t slist_size;
+ ucs2_t slist_used;
+} _ure_stlist_t;
+
+/*
+ * Structure to track the list of unique states for a symbol
+ * during reduction.
+ */
+typedef struct {
+ ucs2_t id;
+ ucs2_t type;
+ unsigned long mods;
+ unsigned long props;
+ _ure_sym_t sym;
+ _ure_stlist_t states;
+} _ure_symtab_t;
+
+/*
+ * Structure to hold a single state.
+ */
+typedef struct {
+ ucs2_t id;
+ ucs2_t accepting;
+ ucs2_t pad;
+ _ure_stlist_t st;
+ _ure_elt_t *trans;
+ ucs2_t trans_size;
+ ucs2_t trans_used;
+} _ure_state_t;
+
+/*
+ * Structure used for keeping lists of states.
+ */
+typedef struct {
+ _ure_state_t *states;
+ ucs2_t states_size;
+ ucs2_t states_used;
+} _ure_statetable_t;
+
+/*
+ * Structure to track pairs of DFA states when equivalent states are
+ * merged.
+ */
+typedef struct {
+ ucs2_t l;
+ ucs2_t r;
+} _ure_equiv_t;
+
+/*
+ * Structure used for constructing the NFA and reducing to a minimal DFA.
+ */
+typedef struct _ure_buffer_t {
+ int reducing;
+ int error;
+ unsigned long flags;
+
+ _ure_stlist_t stack;
+
+ /*
+ * Table of unique symbols encountered.
+ */
+ _ure_symtab_t *symtab;
+ ucs2_t symtab_size;
+ ucs2_t symtab_used;
+
+ /*
+ * Tracks the unique expressions generated for the NFA and when the NFA is
+ * reduced.
+ */
+ _ure_elt_t *expr;
+ ucs2_t expr_used;
+ ucs2_t expr_size;
+
+ /*
+ * The reduced table of unique groups of NFA states.
+ */
+ _ure_statetable_t states;
+
+ /*
+ * Tracks states when equivalent states are merged.
+ */
+ _ure_equiv_t *equiv;
+ ucs2_t equiv_used;
+ ucs2_t equiv_size;
+} _ure_buffer_t;
+
+typedef struct {
+ ucs2_t symbol;
+ ucs2_t next_state;
+} _ure_trans_t;
+
+typedef struct {
+ ucs2_t accepting;
+ ucs2_t ntrans;
+ _ure_trans_t *trans;
+} _ure_dstate_t;
+
+typedef struct _ure_dfa_t {
+ unsigned long flags;
+
+ _ure_symtab_t *syms;
+ ucs2_t nsyms;
+
+ _ure_dstate_t *states;
+ ucs2_t nstates;
+
+ _ure_trans_t *trans;
+ ucs2_t ntrans;
+} _ure_dfa_t;
+
+/*************************************************************************
+ *
+ * Functions.
+ *
+ *************************************************************************/
+
+static void
+_ure_memmove(char *dest, char *src, unsigned long bytes)
+{
+ long i, j;
+
+ i = (long) bytes;
+ j = i & 7;
+ i = (i + 7) >> 3;
+
+ /*
+ * Do a memmove using Ye Olde Duff's Device for efficiency.
+ */
+ if (src < dest) {
+ src += bytes;
+ dest += bytes;
+
+ switch (j) {
+ case 0: do {
+ *--dest = *--src;
+ case 7: *--dest = *--src;
+ case 6: *--dest = *--src;
+ case 5: *--dest = *--src;
+ case 4: *--dest = *--src;
+ case 3: *--dest = *--src;
+ case 2: *--dest = *--src;
+ case 1: *--dest = *--src;
+ } while (--i > 0);
+ }
+ } else if (src > dest) {
+ switch (j) {
+ case 0: do {
+ *dest++ = *src++;
+ case 7: *dest++ = *src++;
+ case 6: *dest++ = *src++;
+ case 5: *dest++ = *src++;
+ case 4: *dest++ = *src++;
+ case 3: *dest++ = *src++;
+ case 2: *dest++ = *src++;
+ case 1: *dest++ = *src++;
+ } while (--i > 0);
+ }
+ }
+}
+
+static void
+_ure_push(ucs2_t v, _ure_buffer_t *b)
+{
+ _ure_stlist_t *s;
+
+ if (b == 0)
+ return;
+
+ /*
+ * If the `reducing' parameter is non-zero, check to see if the value
+ * passed is already on the stack.
+ */
+ if (b->reducing != 0 && b->expr[v].onstack != 0)
+ return;
+
+ s = &b->stack;
+ if (s->slist_used == s->slist_size) {
+ if (s->slist_size == 0)
+ s->slist = (ucs2_t *) malloc(sizeof(ucs2_t) << 3);
+ else
+ s->slist = (ucs2_t *) realloc((char *) s->slist,
+ sizeof(ucs2_t) * (s->slist_size + 8));
+ s->slist_size += 8;
+ }
+ s->slist[s->slist_used++] = v;
+
+ /*
+ * If the `reducing' parameter is non-zero, flag the element as being on
+ * the stack.
+ */
+ if (b->reducing != 0)
+ b->expr[v].onstack = 1;
+}
+
+static ucs2_t
+_ure_peek(_ure_buffer_t *b)
+{
+ if (b == 0 || b->stack.slist_used == 0)
+ return _URE_NOOP;
+
+ return b->stack.slist[b->stack.slist_used - 1];
+}
+
+static ucs2_t
+_ure_pop(_ure_buffer_t *b)
+{
+ ucs2_t v;
+
+ if (b == 0 || b->stack.slist_used == 0)
+ return _URE_NOOP;
+
+ v = b->stack.slist[--b->stack.slist_used];
+ if (b->reducing)
+ b->expr[v].onstack = 0;
+
+ return v;
+}
+
+/*************************************************************************
+ *
+ * Start symbol parse functions.
+ *
+ *************************************************************************/
+
+/*
+ * Parse a comma-separated list of integers that represent character
+ * properties. Combine them into a mask that is returned in the `mask'
+ * variable, and return the number of characters consumed.
+ */
+static unsigned long
+_ure_prop_list(ucs2_t *pp, unsigned long limit, unsigned long *mask,
+ _ure_buffer_t *b)
+{
+ unsigned long n, m;
+ ucs2_t *sp, *ep;
+
+ sp = pp;
+ ep = sp + limit;
+
+ for (m = n = 0; b->error == _URE_OK && sp < ep; sp++) {
+ if (*sp == ',') {
+ /*
+ * Encountered a comma, so select the next character property flag
+ * and reset the number.
+ */
+ m |= cclass_flags[n];
+ n = 0;
+ } else if (*sp >= '0' && *sp <= '9')
+ /*
+ * Encountered a digit, so start or continue building the cardinal
+ * that represents the character property flag.
+ */
+ n = (n * 10) + (*sp - '0');
+ else
+ /*
+ * Encountered something that is not part of the property list.
+ * Indicate that we are done.
+ */
+ break;
+
+ /*
+ * If a property number greater than 32 occurs, then there is a
+ * problem. Most likely a missing comma separator.
+ */
+ if (n > 32)
+ b->error = _URE_INVALID_PROPERTY;
+ }
+
+ if (b->error == _URE_OK && n != 0)
+ m |= cclass_flags[n];
+
+ /*
+ * Set the mask that represents the group of character properties.
+ */
+ *mask = m;
+
+ /*
+ * Return the number of characters consumed.
+ */
+ return sp - pp;
+}
+
+/*
+ * Collect a hex number with 1 to 4 digits and return the number
+ * of characters used.
+ */
+static unsigned long
+_ure_hex(ucs2_t *np, unsigned long limit, ucs4_t *n)
+{
+ ucs2_t i;
+ ucs2_t *sp, *ep;
+ ucs4_t nn;
+
+ sp = np;
+ ep = sp + limit;
+
+ for (nn = 0, i = 0; i < 4 && sp < ep; i++, sp++) {
+ if (*sp >= '0' && *sp <= '9')
+ nn = (nn << 4) + (*sp - '0');
+ else if (*sp >= 'A' && *sp <= 'F')
+ nn = (nn << 4) + ((*sp - 'A') + 10);
+ else if (*sp >= 'a' && *sp <= 'f')
+ nn = (nn << 4) + ((*sp - 'a') + 10);
+ else
+ /*
+ * Encountered something that is not a hex digit.
+ */
+ break;
+ }
+
+ /*
+ * Assign the character code collected and return the number of
+ * characters used.
+ */
+ *n = nn;
+
+ return sp - np;
+}
+
+/*
+ * Insert a range into a character class, removing duplicates and ordering
+ * them in increasing range-start order.
+ */
+static void
+_ure_add_range(_ure_ccl_t *ccl, _ure_range_t *r, _ure_buffer_t *b)
+{
+ ucs2_t i;
+ ucs4_t tmp;
+ _ure_range_t *rp;
+
+ /*
+ * If the `casefold' flag is set, then make sure both endpoints of the
+ * range are converted to lower case.
+ */
+ if (b->flags & _URE_DFA_CASEFOLD) {
+ r->min_code = _ure_tolower(r->min_code);
+ r->max_code = _ure_tolower(r->max_code);
+ }
+
+ /*
+ * Swap the range endpoints if they are not in increasing order.
+ */
+ if (r->min_code > r->max_code) {
+ tmp = r->min_code;
+ r->min_code = r->max_code;
+ r->max_code = tmp;
+ }
+
+ for (i = 0, rp = ccl->ranges;
+ i < ccl->ranges_used && r->min_code < rp->min_code; i++, rp++) ;
+
+ /*
+ * Check for a duplicate.
+ */
+ if (i < ccl->ranges_used &&
+ r->min_code == rp->min_code && r->max_code == rp->max_code)
+ return;
+
+ if (ccl->ranges_used == ccl->ranges_size) {
+ if (ccl->ranges_size == 0)
+ ccl->ranges = (_ure_range_t *) malloc(sizeof(_ure_range_t) << 3);
+ else
+ ccl->ranges = (_ure_range_t *)
+ realloc((char *) ccl->ranges,
+ sizeof(_ure_range_t) * (ccl->ranges_size + 8));
+ ccl->ranges_size += 8;
+ }
+
+ rp = ccl->ranges + ccl->ranges_used;
+
+ if (i < ccl->ranges_used)
+ _ure_memmove((char *) (rp + 1), (char *) rp,
+ sizeof(_ure_range_t) * (ccl->ranges_used - i));
+
+ ccl->ranges_used++;
+ rp->min_code = r->min_code;
+ rp->max_code = r->max_code;
+}
+
+#define _URE_ALPHA_MASK (_URE_UPPER|_URE_LOWER|_URE_OTHERLETTER|\
+_URE_MODIFIER|_URE_TITLE|_URE_NONSPACING|_URE_COMBINING)
+#define _URE_ALNUM_MASK (_URE_ALPHA_MASK|_URE_NUMDIGIT)
+#define _URE_PUNCT_MASK (_URE_DASHPUNCT|_URE_OPENPUNCT|_URE_CLOSEPUNCT|\
+_URE_OTHERPUNCT)
+#define _URE_GRAPH_MASK (_URE_NUMDIGIT|_URE_NUMOTHER|_URE_ALPHA_MASK|\
+_URE_MATHSYM|_URE_CURRENCYSYM|_URE_OTHERSYM)
+#define _URE_PRINT_MASK (_URE_GRAPH_MASK|_URE_SPACESEP)
+#define _URE_SPACE_MASK (_URE_SPACESEP|_URE_LINESEP|_URE_PARASEP)
+
+typedef void (*_ure_cclsetup_t)(
+ _ure_symtab_t *sym,
+ unsigned long mask,
+ _ure_buffer_t *b
+);
+
+typedef struct {
+ ucs2_t key;
+ unsigned long len;
+ unsigned long next;
+ _ure_cclsetup_t func;
+ unsigned long mask;
+} _ure_trie_t;
+
+static void
+_ure_ccl_setup(_ure_symtab_t *sym, unsigned long mask, _ure_buffer_t *b)
+{
+ sym->props |= mask;
+}
+
+static void
+_ure_space_setup(_ure_symtab_t *sym, unsigned long mask, _ure_buffer_t *b)
+{
+ _ure_range_t range;
+
+ sym->props |= mask;
+
+ /*
+ * Add the additional characters needed for handling isspace().
+ */
+ range.min_code = range.max_code = '\t';
+ _ure_add_range(&sym->sym.ccl, &range, b);
+ range.min_code = range.max_code = '\r';
+ _ure_add_range(&sym->sym.ccl, &range, b);
+ range.min_code = range.max_code = '\n';
+ _ure_add_range(&sym->sym.ccl, &range, b);
+ range.min_code = range.max_code = '\f';
+ _ure_add_range(&sym->sym.ccl, &range, b);
+ range.min_code = range.max_code = 0xfeff;
+ _ure_add_range(&sym->sym.ccl, &range, b);
+}
+
+static void
+_ure_xdigit_setup(_ure_symtab_t *sym, unsigned long mask, _ure_buffer_t *b)
+{
+ _ure_range_t range;
+
+ /*
+ * Add the additional characters needed for handling isxdigit().
+ */
+ range.min_code = '0';
+ range.max_code = '9';
+ _ure_add_range(&sym->sym.ccl, &range, b);
+ range.min_code = 'A';
+ range.max_code = 'F';
+ _ure_add_range(&sym->sym.ccl, &range, b);
+ range.min_code = 'a';
+ range.max_code = 'f';
+ _ure_add_range(&sym->sym.ccl, &range, b);
+}
+
+static _ure_trie_t cclass_trie[] = {
+ {0x003a, 1, 1, 0, 0},
+ {0x0061, 9, 10, 0, 0},
+ {0x0063, 8, 19, 0, 0},
+ {0x0064, 7, 24, 0, 0},
+ {0x0067, 6, 29, 0, 0},
+ {0x006c, 5, 34, 0, 0},
+ {0x0070, 4, 39, 0, 0},
+ {0x0073, 3, 49, 0, 0},
+ {0x0075, 2, 54, 0, 0},
+ {0x0078, 1, 59, 0, 0},
+ {0x006c, 1, 11, 0, 0},
+ {0x006e, 2, 13, 0, 0},
+ {0x0070, 1, 16, 0, 0},
+ {0x0075, 1, 14, 0, 0},
+ {0x006d, 1, 15, 0, 0},
+ {0x003a, 1, 16, _ure_ccl_setup, _URE_ALNUM_MASK},
+ {0x0068, 1, 17, 0, 0},
+ {0x0061, 1, 18, 0, 0},
+ {0x003a, 1, 19, _ure_ccl_setup, _URE_ALPHA_MASK},
+ {0x006e, 1, 20, 0, 0},
+ {0x0074, 1, 21, 0, 0},
+ {0x0072, 1, 22, 0, 0},
+ {0x006c, 1, 23, 0, 0},
+ {0x003a, 1, 24, _ure_ccl_setup, _URE_CNTRL},
+ {0x0069, 1, 25, 0, 0},
+ {0x0067, 1, 26, 0, 0},
+ {0x0069, 1, 27, 0, 0},
+ {0x0074, 1, 28, 0, 0},
+ {0x003a, 1, 29, _ure_ccl_setup, _URE_NUMDIGIT},
+ {0x0072, 1, 30, 0, 0},
+ {0x0061, 1, 31, 0, 0},
+ {0x0070, 1, 32, 0, 0},
+ {0x0068, 1, 33, 0, 0},
+ {0x003a, 1, 34, _ure_ccl_setup, _URE_GRAPH_MASK},
+ {0x006f, 1, 35, 0, 0},
+ {0x0077, 1, 36, 0, 0},
+ {0x0065, 1, 37, 0, 0},
+ {0x0072, 1, 38, 0, 0},
+ {0x003a, 1, 39, _ure_ccl_setup, _URE_LOWER},
+ {0x0072, 2, 41, 0, 0},
+ {0x0075, 1, 45, 0, 0},
+ {0x0069, 1, 42, 0, 0},
+ {0x006e, 1, 43, 0, 0},
+ {0x0074, 1, 44, 0, 0},
+ {0x003a, 1, 45, _ure_ccl_setup, _URE_PRINT_MASK},
+ {0x006e, 1, 46, 0, 0},
+ {0x0063, 1, 47, 0, 0},
+ {0x0074, 1, 48, 0, 0},
+ {0x003a, 1, 49, _ure_ccl_setup, _URE_PUNCT_MASK},
+ {0x0070, 1, 50, 0, 0},
+ {0x0061, 1, 51, 0, 0},
+ {0x0063, 1, 52, 0, 0},
+ {0x0065, 1, 53, 0, 0},
+ {0x003a, 1, 54, _ure_space_setup, _URE_SPACE_MASK},
+ {0x0070, 1, 55, 0, 0},
+ {0x0070, 1, 56, 0, 0},
+ {0x0065, 1, 57, 0, 0},
+ {0x0072, 1, 58, 0, 0},
+ {0x003a, 1, 59, _ure_ccl_setup, _URE_UPPER},
+ {0x0064, 1, 60, 0, 0},
+ {0x0069, 1, 61, 0, 0},
+ {0x0067, 1, 62, 0, 0},
+ {0x0069, 1, 63, 0, 0},
+ {0x0074, 1, 64, 0, 0},
+ {0x003a, 1, 65, _ure_xdigit_setup, 0},
+};
+
+/*
+ * Probe for one of the POSIX colon delimited character classes in the static
+ * trie.
+ */
+static unsigned long
+_ure_posix_ccl(ucs2_t *cp, unsigned long limit, _ure_symtab_t *sym,
+ _ure_buffer_t *b)
+{
+ int i;
+ unsigned long n;
+ _ure_trie_t *tp;
+ ucs2_t *sp, *ep;
+
+ /*
+ * If the number of characters left is less than 7, then this cannot be
+ * interpreted as one of the colon delimited classes.
+ */
+ if (limit < 7)
+ return 0;
+
+ sp = cp;
+ ep = sp + limit;
+ tp = cclass_trie;
+ for (i = 0; sp < ep && i < 8; i++, sp++) {
+ n = tp->len;
+
+ for (; n > 0 && tp->key != *sp; tp++, n--) ;
+
+ if (n == 0)
+ return 0;
+
+ if (*sp == ':' && (i == 6 || i == 7)) {
+ sp++;
+ break;
+ }
+ if (sp + 1 < ep)
+ tp = cclass_trie + tp->next;
+ }
+ if (tp->func == 0)
+ return 0;
+
+ (*tp->func)(sym, tp->mask, b);
+
+ return sp - cp;
+}
+
+/*
+ * Construct a list of ranges and return the number of characters consumed.
+ */
+static unsigned long
+_ure_cclass(ucs2_t *cp, unsigned long limit, _ure_symtab_t *symp,
+ _ure_buffer_t *b)
+{
+ int range_end;
+ unsigned long n;
+ ucs2_t *sp, *ep;
+ ucs4_t c, last;
+ _ure_ccl_t *cclp;
+ _ure_range_t range;
+
+ sp = cp;
+ ep = sp + limit;
+
+ if (*sp == '^') {
+ symp->type = _URE_NCCLASS;
+ sp++;
+ } else
+ symp->type = _URE_CCLASS;
+
+ for (last = 0, range_end = 0;
+ b->error == _URE_OK && sp < ep && *sp != ']'; ) {
+ c = *sp++;
+ if (c == '\\') {
+ if (sp == ep) {
+ /*
+ * The EOS was encountered when expecting the reverse solidus
+ * to be followed by the character it is escaping. Set an
+ * error code and return the number of characters consumed up
+ * to this point.
+ */
+ b->error = _URE_UNEXPECTED_EOS;
+ return sp - cp;
+ }
+
+ c = *sp++;
+ switch (c) {
+ case 'a':
+ c = 0x07;
+ break;
+ case 'b':
+ c = 0x08;
+ break;
+ case 'f':
+ c = 0x0c;
+ break;
+ case 'n':
+ c = 0x0a;
+ break;
+ case 'r':
+ c = 0x0d;
+ break;
+ case 't':
+ c = 0x09;
+ break;
+ case 'v':
+ c = 0x0b;
+ break;
+ case 'p':
+ case 'P':
+ sp += _ure_prop_list(sp, ep - sp, &symp->props, b);
+ /*
+ * Invert the bit mask of the properties if this is a negated
+ * character class or if 'P' is used to specify a list of
+ * character properties that should *not* match in a
+ * character class.
+ */
+ if (c == 'P')
+ symp->props = ~symp->props;
+ continue;
+ break;
+ case 'x':
+ case 'X':
+ case 'u':
+ case 'U':
+ if (sp < ep &&
+ ((*sp >= '0' && *sp <= '9') ||
+ (*sp >= 'A' && *sp <= 'F') ||
+ (*sp >= 'a' && *sp <= 'f')))
+ sp += _ure_hex(sp, ep - sp, &c);
+ }
+ } else if (c == ':') {
+ /*
+ * Probe for a POSIX colon delimited character class.
+ */
+ sp--;
+ if ((n = _ure_posix_ccl(sp, ep - sp, symp, b)) == 0)
+ sp++;
+ else {
+ sp += n;
+ continue;
+ }
+ }
+
+ cclp = &symp->sym.ccl;
+
+ /*
+ * Check to see if the current character is a low surrogate that needs
+ * to be combined with a preceding high surrogate.
+ */
+ if (last != 0) {
+ if (c >= 0xdc00 && c <= 0xdfff)
+ /*
+ * Construct the UTF16 character code.
+ */
+ c = 0x10000 + (((last & 0x03ff) << 10) | (c & 0x03ff));
+ else {
+ /*
+ * Add the isolated high surrogate to the range.
+ */
+ if (range_end == 1)
+ range.max_code = last & 0xffff;
+ else
+ range.min_code = range.max_code = last & 0xffff;
+
+ _ure_add_range(cclp, &range, b);
+ range_end = 0;
+ }
+ }
+
+ /*
+ * Clear the last character code.
+ */
+ last = 0;
+
+ /*
+ * This slightly awkward code handles the different cases needed to
+ * construct a range.
+ */
+ if (c >= 0xd800 && c <= 0xdbff) {
+ /*
+ * If the high surrogate is followed by a range indicator, simply
+ * add it as the range start. Otherwise, save it in case the next
+ * character is a low surrogate.
+ */
+ if (*sp == '-') {
+ sp++;
+ range.min_code = c;
+ range_end = 1;
+ } else
+ last = c;
+ } else if (range_end == 1) {
+ range.max_code = c;
+ _ure_add_range(cclp, &range, b);
+ range_end = 0;
+ } else {
+ range.min_code = range.max_code = c;
+ if (*sp == '-') {
+ sp++;
+ range_end = 1;
+ } else
+ _ure_add_range(cclp, &range, b);
+ }
+ }
+
+ if (sp < ep && *sp == ']')
+ sp++;
+ else
+ /*
+ * The parse was not terminated by the character class close symbol
+ * (']'), so set an error code.
+ */
+ b->error = _URE_CCLASS_OPEN;
+
+ return sp - cp;
+}
+
+/*
+ * Probe for a low surrogate hex code.
+ */
+static unsigned long
+_ure_probe_ls(ucs2_t *ls, unsigned long limit, ucs4_t *c)
+{
+ ucs4_t i, code;
+ ucs2_t *sp, *ep;
+
+ for (i = code = 0, sp = ls, ep = sp + limit; i < 4 && sp < ep; sp++) {
+ if (*sp >= '0' && *sp <= '9')
+ code = (code << 4) + (*sp - '0');
+ else if (*sp >= 'A' && *sp <= 'F')
+ code = (code << 4) + ((*sp - 'A') + 10);
+ else if (*sp >= 'a' && *sp <= 'f')
+ code = (code << 4) + ((*sp - 'a') + 10);
+ else
+ break;
+ }
+
+ *c = code;
+ return (0xdc00 <= code && code <= 0xdfff) ? sp - ls : 0;
+}
+
+static unsigned long
+_ure_compile_symbol(ucs2_t *sym, unsigned long limit, _ure_symtab_t *symp,
+ _ure_buffer_t *b)
+{
+ ucs4_t c;
+ ucs2_t *sp, *ep;
+
+ sp = sym;
+ ep = sym + limit;
+
+ if ((c = *sp++) == '\\') {
+
+ if (sp == ep) {
+ /*
+ * The EOS was encountered when expecting the reverse solidus to
+ * be followed by the character it is escaping. Set an error code
+ * and return the number of characters consumed up to this point.
+ */
+ b->error = _URE_UNEXPECTED_EOS;
+ return sp - sym;
+ }
+
+ c = *sp++;
+ switch (c) {
+ case 'p':
+ case 'P':
+ symp->type = (c == 'p') ? _URE_CCLASS : _URE_NCCLASS;
+ sp += _ure_prop_list(sp, ep - sp, &symp->props, b);
+ break;
+ case 'a':
+ symp->type = _URE_CHAR;
+ symp->sym.chr = 0x07;
+ break;
+ case 'b':
+ symp->type = _URE_CHAR;
+ symp->sym.chr = 0x08;
+ break;
+ case 'f':
+ symp->type = _URE_CHAR;
+ symp->sym.chr = 0x0c;
+ break;
+ case 'n':
+ symp->type = _URE_CHAR;
+ symp->sym.chr = 0x0a;
+ break;
+ case 'r':
+ symp->type = _URE_CHAR;
+ symp->sym.chr = 0x0d;
+ break;
+ case 't':
+ symp->type = _URE_CHAR;
+ symp->sym.chr = 0x09;
+ break;
+ case 'v':
+ symp->type = _URE_CHAR;
+ symp->sym.chr = 0x0b;
+ break;
+ case 'x':
+ case 'X':
+ case 'u':
+ case 'U':
+ /*
+ * Collect between 1 and 4 digits representing a UCS2 code. Fall
+ * through to the next case.
+ */
+ if (sp < ep &&
+ ((*sp >= '0' && *sp <= '9') ||
+ (*sp >= 'A' && *sp <= 'F') ||
+ (*sp >= 'a' && *sp <= 'f')))
+ sp += _ure_hex(sp, ep - sp, &c);
+ /* FALLTHROUGH */
+ default:
+ /*
+ * Simply add an escaped character here.
+ */
+ symp->type = _URE_CHAR;
+ symp->sym.chr = c;
+ }
+ } else if (c == '^' || c == '$')
+ /*
+ * Handle the BOL and EOL anchors. This actually consists simply of
+ * setting a flag that indicates that the user supplied anchor match
+ * function should be called. This needs to be done instead of simply
+ * matching line/paragraph separators because beginning-of-text and
+ * end-of-text tests are needed as well.
+ */
+ symp->type = (c == '^') ? _URE_BOL_ANCHOR : _URE_EOL_ANCHOR;
+ else if (c == '[')
+ /*
+ * Construct a character class.
+ */
+ sp += _ure_cclass(sp, ep - sp, symp, b);
+ else if (c == '.')
+ symp->type = _URE_ANY_CHAR;
+ else {
+ symp->type = _URE_CHAR;
+ symp->sym.chr = c;
+ }
+
+ /*
+ * If the symbol type happens to be a character and is a high surrogate,
+ * then probe forward to see if it is followed by a low surrogate that
+ * needs to be added.
+ */
+ if (sp < ep && symp->type == _URE_CHAR &&
+ 0xd800 <= symp->sym.chr && symp->sym.chr <= 0xdbff) {
+
+ if (0xdc00 <= *sp && *sp <= 0xdfff) {
+ symp->sym.chr = 0x10000 + (((symp->sym.chr & 0x03ff) << 10) |
+ (*sp & 0x03ff));
+ sp++;
+ } else if (*sp == '\\' && (*(sp + 1) == 'x' || *(sp + 1) == 'X' ||
+ *(sp + 1) == 'u' || *(sp + 1) == 'U')) {
+ sp += _ure_probe_ls(sp + 2, ep - (sp + 2), &c);
+ if (0xdc00 <= c && c <= 0xdfff) {
+ /*
+ * Take into account the \[xu] in front of the hex code.
+ */
+ sp += 2;
+ symp->sym.chr = 0x10000 + (((symp->sym.chr & 0x03ff) << 10) |
+ (c & 0x03ff));
+ }
+ }
+ }
+
+ /*
+ * Last, make sure any _URE_CHAR type symbols are changed to lower case if
+ * the `casefold' flag is set.
+ */
+ if ((b->flags & _URE_DFA_CASEFOLD) && symp->type == _URE_CHAR)
+ symp->sym.chr = _ure_tolower(symp->sym.chr);
+
+ /*
+ * If the symbol constructed is anything other than one of the anchors,
+ * make sure the _URE_DFA_BLANKLINE flag is removed.
+ */
+ if (symp->type != _URE_BOL_ANCHOR && symp->type != _URE_EOL_ANCHOR)
+ b->flags &= ~_URE_DFA_BLANKLINE;
+
+ /*
+ * Return the number of characters consumed.
+ */
+ return sp - sym;
+}
+
+static int
+_ure_sym_neq(_ure_symtab_t *a, _ure_symtab_t *b)
+{
+ if (a->type != b->type || a->mods != b->mods || a->props != b->props)
+ return 1;
+
+ if (a->type == _URE_CCLASS || a->type == _URE_NCCLASS) {
+ if (a->sym.ccl.ranges_used != b->sym.ccl.ranges_used)
+ return 1;
+ if (a->sym.ccl.ranges_used > 0 &&
+ memcmp((char *) a->sym.ccl.ranges, (char *) b->sym.ccl.ranges,
+ sizeof(_ure_range_t) * a->sym.ccl.ranges_used) != 0)
+ return 1;
+ } else if (a->type == _URE_CHAR && a->sym.chr != b->sym.chr)
+ return 1;
+ return 0;
+}
+
+/*
+ * Construct a symbol, but only keep unique symbols.
+ */
+static ucs2_t
+_ure_make_symbol(ucs2_t *sym, unsigned long limit, unsigned long *consumed,
+ _ure_buffer_t *b)
+{
+ ucs2_t i;
+ _ure_symtab_t *sp, symbol;
+
+ /*
+ * Build the next symbol so we can test to see if it is already in the
+ * symbol table.
+ */
+ (void) memset((char *) &symbol, '\0', sizeof(_ure_symtab_t));
+ *consumed = _ure_compile_symbol(sym, limit, &symbol, b);
+
+ /*
+ * Check to see if the symbol exists.
+ */
+ for (i = 0, sp = b->symtab;
+ i < b->symtab_used && _ure_sym_neq(&symbol, sp); i++, sp++) ;
+
+ if (i < b->symtab_used) {
+ /*
+ * Free up any ranges used for the symbol.
+ */
+ if ((symbol.type == _URE_CCLASS || symbol.type == _URE_NCCLASS) &&
+ symbol.sym.ccl.ranges_size > 0)
+ free((char *) symbol.sym.ccl.ranges);
+
+ return b->symtab[i].id;
+ }
+
+ /*
+ * Need to add the new symbol.
+ */
+ if (b->symtab_used == b->symtab_size) {
+ if (b->symtab_size == 0)
+ b->symtab = (_ure_symtab_t *) malloc(sizeof(_ure_symtab_t) << 3);
+ else
+ b->symtab = (_ure_symtab_t *)
+ realloc((char *) b->symtab,
+ sizeof(_ure_symtab_t) * (b->symtab_size + 8));
+ sp = b->symtab + b->symtab_size;
+ (void) memset((char *) sp, '\0', sizeof(_ure_symtab_t) << 3);
+ b->symtab_size += 8;
+ }
+
+ symbol.id = b->symtab_used++;
+ (void) AC_MEMCPY((char *) &b->symtab[symbol.id], (char *) &symbol,
+ sizeof(_ure_symtab_t));
+
+ return symbol.id;
+}
+
+/*************************************************************************
+ *
+ * End symbol parse functions.
+ *
+ *************************************************************************/
+
+static ucs2_t
+_ure_make_expr(ucs2_t type, ucs2_t lhs, ucs2_t rhs, _ure_buffer_t *b)
+{
+ ucs2_t i;
+
+ if (b == 0)
+ return _URE_NOOP;
+
+ /*
+ * Determine if the expression already exists or not.
+ */
+ for (i = 0; i < b->expr_used; i++) {
+ if (b->expr[i].type == type && b->expr[i].lhs == lhs &&
+ b->expr[i].rhs == rhs)
+ break;
+ }
+ if (i < b->expr_used)
+ return i;
+
+ /*
+ * Need to add a new expression.
+ */
+ if (b->expr_used == b->expr_size) {
+ if (b->expr_size == 0)
+ b->expr = (_ure_elt_t *) malloc(sizeof(_ure_elt_t) << 3);
+ else
+ b->expr = (_ure_elt_t *)
+ realloc((char *) b->expr,
+ sizeof(_ure_elt_t) * (b->expr_size + 8));
+ b->expr_size += 8;
+ }
+
+ b->expr[b->expr_used].onstack = 0;
+ b->expr[b->expr_used].type = type;
+ b->expr[b->expr_used].lhs = lhs;
+ b->expr[b->expr_used].rhs = rhs;
+
+ return b->expr_used++;
+}
+
+static unsigned char spmap[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+#define _ure_isspecial(cc) ((cc) > 0x20 && (cc) < 0x7f && \
+ (spmap[(cc) >> 3] & (1 << ((cc) & 7))))
+
+/*
+ * Convert the regular expression into an NFA in a form that will be easy to
+ * reduce to a DFA. The starting state for the reduction will be returned.
+ */
+static ucs2_t
+_ure_re2nfa(ucs2_t *re, unsigned long relen, _ure_buffer_t *b)
+{
+ ucs2_t c, state, top, sym, *sp, *ep;
+ unsigned long used;
+
+ state = _URE_NOOP;
+
+ sp = re;
+ ep = sp + relen;
+ while (b->error == _URE_OK && sp < ep) {
+ c = *sp++;
+ switch (c) {
+ case '(':
+ _ure_push(_URE_PAREN, b);
+ break;
+ case ')':
+ /*
+ * Check for the case of too many close parentheses.
+ */
+ if (_ure_peek(b) == _URE_NOOP) {
+ b->error = _URE_UNBALANCED_GROUP;
+ break;
+ }
+
+ while ((top = _ure_peek(b)) == _URE_AND || top == _URE_OR)
+ /*
+ * Make an expression with the AND or OR operator and its right
+ * hand side.
+ */
+ state = _ure_make_expr(_ure_pop(b), _ure_pop(b), state, b);
+
+ /*
+ * Remove the _URE_PAREN off the stack.
+ */
+ (void) _ure_pop(b);
+ break;
+ case '*':
+ state = _ure_make_expr(_URE_STAR, state, _URE_NOOP, b);
+ break;
+ case '+':
+ state = _ure_make_expr(_URE_PLUS, state, _URE_NOOP, b);
+ break;
+ case '?':
+ state = _ure_make_expr(_URE_QUEST, state, _URE_NOOP, b);
+ break;
+ case '|':
+ while ((top = _ure_peek(b)) == _URE_AND || top == _URE_OR)
+ /*
+ * Make an expression with the AND or OR operator and its right
+ * hand side.
+ */
+ state = _ure_make_expr(_ure_pop(b), _ure_pop(b), state, b);
+
+ _ure_push(state, b);
+ _ure_push(_URE_OR, b);
+ break;
+ default:
+ sp--;
+ sym = _ure_make_symbol(sp, ep - sp, &used, b);
+ sp += used;
+ state = _ure_make_expr(_URE_SYMBOL, sym, _URE_NOOP, b);
+ break;
+ }
+
+ if (c != '(' && c != '|' && sp < ep &&
+ (!_ure_isspecial(*sp) || *sp == '(')) {
+ _ure_push(state, b);
+ _ure_push(_URE_AND, b);
+ }
+ }
+ while ((top = _ure_peek(b)) == _URE_AND || top == _URE_OR)
+ /*
+ * Make an expression with the AND or OR operator and its right
+ * hand side.
+ */
+ state = _ure_make_expr(_ure_pop(b), _ure_pop(b), state, b);
+
+ if (b->stack.slist_used > 0)
+ b->error = _URE_UNBALANCED_GROUP;
+
+ return (b->error == _URE_OK) ? state : _URE_NOOP;
+}
+
+static void
+_ure_add_symstate(ucs2_t sym, ucs2_t state, _ure_buffer_t *b)
+{
+ ucs2_t i, *stp;
+ _ure_symtab_t *sp;
+
+ /*
+ * Locate the symbol in the symbol table so the state can be added.
+ * If the symbol doesn't exist, then a real problem exists.
+ */
+ for (i = 0, sp = b->symtab; i < b->symtab_used && sym != sp->id;
+ i++, sp++) ;
+
+ /*
+ * Now find out if the state exists in the symbol's state list.
+ */
+ for (i = 0, stp = sp->states.slist;
+ i < sp->states.slist_used && state > *stp; i++, stp++) ;
+
+ if (i == sp->states.slist_used || state < *stp) {
+ /*
+ * Need to add the state in order.
+ */
+ if (sp->states.slist_used == sp->states.slist_size) {
+ if (sp->states.slist_size == 0)
+ sp->states.slist = (ucs2_t *) malloc(sizeof(ucs2_t) << 3);
+ else
+ sp->states.slist = (ucs2_t *)
+ realloc((char *) sp->states.slist,
+ sizeof(ucs2_t) * (sp->states.slist_size + 8));
+ sp->states.slist_size += 8;
+ }
+ if (i < sp->states.slist_used)
+ (void) _ure_memmove((char *) (sp->states.slist + i + 1),
+ (char *) (sp->states.slist + i),
+ sizeof(ucs2_t) * (sp->states.slist_used - i));
+ sp->states.slist[i] = state;
+ sp->states.slist_used++;
+ }
+}
+
+static ucs2_t
+_ure_add_state(ucs2_t nstates, ucs2_t *states, _ure_buffer_t *b)
+{
+ ucs2_t i;
+ _ure_state_t *sp;
+
+ for (i = 0, sp = b->states.states; i < b->states.states_used; i++, sp++) {
+ if (sp->st.slist_used == nstates &&
+ memcmp((char *) states, (char *) sp->st.slist,
+ sizeof(ucs2_t) * nstates) == 0)
+ break;
+ }
+
+ if (i == b->states.states_used) {
+ /*
+ * Need to add a new DFA state (set of NFA states).
+ */
+ if (b->states.states_used == b->states.states_size) {
+ if (b->states.states_size == 0)
+ b->states.states = (_ure_state_t *)
+ malloc(sizeof(_ure_state_t) << 3);
+ else
+ b->states.states = (_ure_state_t *)
+ realloc((char *) b->states.states,
+ sizeof(_ure_state_t) * (b->states.states_size + 8));
+ sp = b->states.states + b->states.states_size;
+ (void) memset((char *) sp, '\0', sizeof(_ure_state_t) << 3);
+ b->states.states_size += 8;
+ }
+
+ sp = b->states.states + b->states.states_used++;
+ sp->id = i;
+
+ if (sp->st.slist_used + nstates > sp->st.slist_size) {
+ if (sp->st.slist_size == 0)
+ sp->st.slist = (ucs2_t *)
+ malloc(sizeof(ucs2_t) * (sp->st.slist_used + nstates));
+ else
+ sp->st.slist = (ucs2_t *)
+ realloc((char *) sp->st.slist,
+ sizeof(ucs2_t) * (sp->st.slist_used + nstates));
+ sp->st.slist_size = sp->st.slist_used + nstates;
+ }
+ sp->st.slist_used = nstates;
+ (void) AC_MEMCPY((char *) sp->st.slist, (char *) states,
+ sizeof(ucs2_t) * nstates);
+ }
+
+ /*
+ * Return the ID of the DFA state representing a group of NFA states.
+ */
+ return i;
+}
+
+static void
+_ure_reduce(ucs2_t start, _ure_buffer_t *b)
+{
+ ucs2_t i, j, state, eval, syms, rhs;
+ ucs2_t s1, s2, ns1, ns2;
+ _ure_state_t *sp;
+ _ure_symtab_t *smp;
+
+ b->reducing = 1;
+
+ /*
+ * Add the starting state for the reduction.
+ */
+ _ure_add_state(1, &start, b);
+
+ /*
+ * Process each set of NFA states that get created.
+ */
+ for (i = 0; i < b->states.states_used; i++) {
+ sp = b->states.states + i;
+
+ /*
+ * Push the current states on the stack.
+ */
+ for (j = 0; j < sp->st.slist_used; j++)
+ _ure_push(sp->st.slist[j], b);
+
+ /*
+ * Reduce the NFA states.
+ */
+ for (j = sp->accepting = syms = 0; j < b->stack.slist_used; j++) {
+ state = b->stack.slist[j];
+ eval = 1;
+
+ /*
+ * This inner loop is the iterative equivalent of recursively
+ * reducing subexpressions generated as a result of a reduction.
+ */
+ while (eval) {
+ switch (b->expr[state].type) {
+ case _URE_SYMBOL:
+ ns1 = _ure_make_expr(_URE_ONE, _URE_NOOP, _URE_NOOP, b);
+ _ure_add_symstate(b->expr[state].lhs, ns1, b);
+ syms++;
+ eval = 0;
+ break;
+ case _URE_ONE:
+ sp->accepting = 1;
+ eval = 0;
+ break;
+ case _URE_QUEST:
+ s1 = b->expr[state].lhs;
+ ns1 = _ure_make_expr(_URE_ONE, _URE_NOOP, _URE_NOOP, b);
+ state = _ure_make_expr(_URE_OR, ns1, s1, b);
+ break;
+ case _URE_PLUS:
+ s1 = b->expr[state].lhs;
+ ns1 = _ure_make_expr(_URE_STAR, s1, _URE_NOOP, b);
+ state = _ure_make_expr(_URE_AND, s1, ns1, b);
+ break;
+ case _URE_STAR:
+ s1 = b->expr[state].lhs;
+ ns1 = _ure_make_expr(_URE_ONE, _URE_NOOP, _URE_NOOP, b);
+ ns2 = _ure_make_expr(_URE_PLUS, s1, _URE_NOOP, b);
+ state = _ure_make_expr(_URE_OR, ns1, ns2, b);
+ break;
+ case _URE_OR:
+ s1 = b->expr[state].lhs;
+ s2 = b->expr[state].rhs;
+ _ure_push(s1, b);
+ _ure_push(s2, b);
+ eval = 0;
+ break;
+ case _URE_AND:
+ s1 = b->expr[state].lhs;
+ s2 = b->expr[state].rhs;
+ switch (b->expr[s1].type) {
+ case _URE_SYMBOL:
+ _ure_add_symstate(b->expr[s1].lhs, s2, b);
+ syms++;
+ eval = 0;
+ break;
+ case _URE_ONE:
+ state = s2;
+ break;
+ case _URE_QUEST:
+ ns1 = b->expr[s1].lhs;
+ ns2 = _ure_make_expr(_URE_AND, ns1, s2, b);
+ state = _ure_make_expr(_URE_OR, s2, ns2, b);
+ break;
+ case _URE_PLUS:
+ ns1 = b->expr[s1].lhs;
+ ns2 = _ure_make_expr(_URE_OR, s2, state, b);
+ state = _ure_make_expr(_URE_AND, ns1, ns2, b);
+ break;
+ case _URE_STAR:
+ ns1 = b->expr[s1].lhs;
+ ns2 = _ure_make_expr(_URE_AND, ns1, state, b);
+ state = _ure_make_expr(_URE_OR, s2, ns2, b);
+ break;
+ case _URE_OR:
+ ns1 = b->expr[s1].lhs;
+ ns2 = b->expr[s1].rhs;
+ ns1 = _ure_make_expr(_URE_AND, ns1, s2, b);
+ ns2 = _ure_make_expr(_URE_AND, ns2, s2, b);
+ state = _ure_make_expr(_URE_OR, ns1, ns2, b);
+ break;
+ case _URE_AND:
+ ns1 = b->expr[s1].lhs;
+ ns2 = b->expr[s1].rhs;
+ ns2 = _ure_make_expr(_URE_AND, ns2, s2, b);
+ state = _ure_make_expr(_URE_AND, ns1, ns2, b);
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ * Clear the state stack.
+ */
+ while (_ure_pop(b) != _URE_NOOP) ;
+
+ /*
+ * Reset the state pointer because the reduction may have moved it
+ * during a reallocation.
+ */
+ sp = b->states.states + i;
+
+ /*
+ * Generate the DFA states for the symbols collected during the
+ * current reduction.
+ */
+ if (sp->trans_used + syms > sp->trans_size) {
+ if (sp->trans_size == 0)
+ sp->trans = (_ure_elt_t *)
+ malloc(sizeof(_ure_elt_t) * (sp->trans_used + syms));
+ else
+ sp->trans = (_ure_elt_t *)
+ realloc((char *) sp->trans,
+ sizeof(_ure_elt_t) * (sp->trans_used + syms));
+ sp->trans_size = sp->trans_used + syms;
+ }
+
+ /*
+ * Go through the symbol table and generate the DFA state transitions
+ * for each symbol that has collected NFA states.
+ */
+ for (j = syms = 0, smp = b->symtab; j < b->symtab_used; j++, smp++) {
+ sp = b->states.states + i;
+
+ if (smp->states.slist_used > 0) {
+ sp->trans[syms].lhs = smp->id;
+ rhs = _ure_add_state(smp->states.slist_used,
+ smp->states.slist, b);
+ /*
+ * Reset the state pointer in case the reallocation moves it
+ * in memory.
+ */
+ sp = b->states.states + i;
+ sp->trans[syms].rhs = rhs;
+
+ smp->states.slist_used = 0;
+ syms++;
+ }
+ }
+
+ /*
+ * Set the number of transitions actually used.
+ */
+ sp->trans_used = syms;
+ }
+ b->reducing = 0;
+}
+
+static void
+_ure_add_equiv(ucs2_t l, ucs2_t r, _ure_buffer_t *b)
+{
+ ucs2_t tmp;
+
+ l = b->states.states[l].id;
+ r = b->states.states[r].id;
+
+ if (l == r)
+ return;
+
+ if (l > r) {
+ tmp = l;
+ l = r;
+ r = tmp;
+ }
+
+ /*
+ * Check to see if the equivalence pair already exists.
+ */
+ for (tmp = 0; tmp < b->equiv_used &&
+ (b->equiv[tmp].l != l || b->equiv[tmp].r != r);
+ tmp++) ;
+
+ if (tmp < b->equiv_used)
+ return;
+
+ if (b->equiv_used == b->equiv_size) {
+ if (b->equiv_size == 0)
+ b->equiv = (_ure_equiv_t *) malloc(sizeof(_ure_equiv_t) << 3);
+ else
+ b->equiv = (_ure_equiv_t *) realloc((char *) b->equiv,
+ sizeof(_ure_equiv_t) *
+ (b->equiv_size + 8));
+ b->equiv_size += 8;
+ }
+ b->equiv[b->equiv_used].l = l;
+ b->equiv[b->equiv_used].r = r;
+ b->equiv_used++;
+}
+
+/*
+ * Merge the DFA states that are equivalent.
+ */
+static void
+_ure_merge_equiv(_ure_buffer_t *b)
+{
+ ucs2_t i, j, k, eq, done;
+ _ure_state_t *sp1, *sp2, *ls, *rs;
+
+ for (i = 0; i < b->states.states_used; i++) {
+ sp1 = b->states.states + i;
+ if (sp1->id != i)
+ continue;
+ for (j = 0; j < i; j++) {
+ sp2 = b->states.states + j;
+ if (sp2->id != j)
+ continue;
+ b->equiv_used = 0;
+ _ure_add_equiv(i, j, b);
+ for (eq = 0, done = 0; eq < b->equiv_used; eq++) {
+ ls = b->states.states + b->equiv[eq].l;
+ rs = b->states.states + b->equiv[eq].r;
+ if (ls->accepting != rs->accepting ||
+ ls->trans_used != rs->trans_used) {
+ done = 1;
+ break;
+ }
+ for (k = 0; k < ls->trans_used &&
+ ls->trans[k].lhs == rs->trans[k].lhs; k++) ;
+ if (k < ls->trans_used) {
+ done = 1;
+ break;
+ }
+
+ for (k = 0; k < ls->trans_used; k++)
+ _ure_add_equiv(ls->trans[k].rhs, rs->trans[k].rhs, b);
+ }
+ if (done == 0)
+ break;
+ }
+ for (eq = 0; j < i && eq < b->equiv_used; eq++)
+ b->states.states[b->equiv[eq].r].id =
+ b->states.states[b->equiv[eq].l].id;
+ }
+
+ /*
+ * Renumber the states appropriately.
+ */
+ for (i = eq = 0, sp1 = b->states.states; i < b->states.states_used;
+ sp1++, i++)
+ sp1->id = (sp1->id == i) ? eq++ : b->states.states[sp1->id].id;
+}
+
+/*************************************************************************
+ *
+ * API.
+ *
+ *************************************************************************/
+
+ure_buffer_t
+ure_buffer_create(void)
+{
+ ure_buffer_t b;
+
+ b = (ure_buffer_t) calloc(1, sizeof(_ure_buffer_t));
+
+ return b;
+}
+
+void
+ure_buffer_free(ure_buffer_t buf)
+{
+ unsigned long i;
+
+ if (buf == 0)
+ return;
+
+ if (buf->stack.slist_size > 0)
+ free((char *) buf->stack.slist);
+
+ if (buf->expr_size > 0)
+ free((char *) buf->expr);
+
+ for (i = 0; i < buf->symtab_size; i++) {
+ if (buf->symtab[i].states.slist_size > 0)
+ free((char *) buf->symtab[i].states.slist);
+ }
+
+ if (buf->symtab_size > 0)
+ free((char *) buf->symtab);
+
+ for (i = 0; i < buf->states.states_size; i++) {
+ if (buf->states.states[i].trans_size > 0)
+ free((char *) buf->states.states[i].trans);
+ if (buf->states.states[i].st.slist_size > 0)
+ free((char *) buf->states.states[i].st.slist);
+ }
+
+ if (buf->states.states_size > 0)
+ free((char *) buf->states.states);
+
+ if (buf->equiv_size > 0)
+ free((char *) buf->equiv);
+
+ free((char *) buf);
+}
+
+ure_dfa_t
+ure_compile(ucs2_t *re, unsigned long relen, int casefold, ure_buffer_t buf)
+{
+ ucs2_t i, j, state;
+ _ure_state_t *sp;
+ _ure_dstate_t *dsp;
+ _ure_trans_t *tp;
+ ure_dfa_t dfa;
+
+ if (re == 0 || *re == 0 || relen == 0 || buf == 0)
+ return 0;
+
+ /*
+ * Reset the various fields of the compilation buffer. Default the flags
+ * to indicate the presence of the "^$" pattern. If any other pattern
+ * occurs, then this flag will be removed. This is done to catch this
+ * special pattern and handle it specially when matching.
+ */
+ buf->flags = _URE_DFA_BLANKLINE | ((casefold) ? _URE_DFA_CASEFOLD : 0);
+ buf->reducing = 0;
+ buf->stack.slist_used = 0;
+ buf->expr_used = 0;
+
+ for (i = 0; i < buf->symtab_used; i++)
+ buf->symtab[i].states.slist_used = 0;
+ buf->symtab_used = 0;
+
+ for (i = 0; i < buf->states.states_used; i++) {
+ buf->states.states[i].st.slist_used = 0;
+ buf->states.states[i].trans_used = 0;
+ }
+ buf->states.states_used = 0;
+
+ /*
+ * Construct the NFA. If this stage returns a 0, then an error occurred or
+ * an empty expression was passed.
+ */
+ if ((state = _ure_re2nfa(re, relen, buf)) == _URE_NOOP)
+ return 0;
+
+ /*
+ * Do the expression reduction to get the initial DFA.
+ */
+ _ure_reduce(state, buf);
+
+ /*
+ * Merge all the equivalent DFA states.
+ */
+ _ure_merge_equiv(buf);
+
+ /*
+ * Construct the minimal DFA.
+ */
+ dfa = (ure_dfa_t) malloc(sizeof(_ure_dfa_t));
+ (void) memset((char *) dfa, '\0', sizeof(_ure_dfa_t));
+
+ dfa->flags = buf->flags & (_URE_DFA_CASEFOLD|_URE_DFA_BLANKLINE);
+
+ /*
+ * Free up the NFA state groups and transfer the symbols from the buffer
+ * to the DFA.
+ */
+ for (i = 0; i < buf->symtab_size; i++) {
+ if (buf->symtab[i].states.slist_size > 0)
+ free((char *) buf->symtab[i].states.slist);
+ }
+ dfa->syms = buf->symtab;
+ dfa->nsyms = buf->symtab_used;
+
+ buf->symtab_used = buf->symtab_size = 0;
+
+ /*
+ * Collect the total number of states and transitions needed for the DFA.
+ */
+ for (i = state = 0, sp = buf->states.states; i < buf->states.states_used;
+ i++, sp++) {
+ if (sp->id == state) {
+ dfa->nstates++;
+ dfa->ntrans += sp->trans_used;
+ state++;
+ }
+ }
+
+ /*
+ * Allocate enough space for the states and transitions.
+ */
+ dfa->states = (_ure_dstate_t *) malloc(sizeof(_ure_dstate_t) *
+ dfa->nstates);
+ dfa->trans = (_ure_trans_t *) malloc(sizeof(_ure_trans_t) * dfa->ntrans);
+
+ /*
+ * Actually transfer the DFA states from the buffer.
+ */
+ dsp = dfa->states;
+ tp = dfa->trans;
+ for (i = state = 0, sp = buf->states.states; i < buf->states.states_used;
+ i++, sp++) {
+ if (sp->id == state) {
+ dsp->trans = tp;
+ dsp->ntrans = sp->trans_used;
+ dsp->accepting = sp->accepting;
+
+ /*
+ * Add the transitions for the state.
+ */
+ for (j = 0; j < dsp->ntrans; j++, tp++) {
+ tp->symbol = sp->trans[j].lhs;
+ tp->next_state = buf->states.states[sp->trans[j].rhs].id;
+ }
+
+ dsp++;
+ state++;
+ }
+ }
+
+ return dfa;
+}
+
+void
+ure_dfa_free(ure_dfa_t dfa)
+{
+ ucs2_t i;
+
+ if (dfa == 0)
+ return;
+
+ for (i = 0; i < dfa->nsyms; i++) {
+ if ((dfa->syms[i].type == _URE_CCLASS ||
+ dfa->syms[i].type == _URE_NCCLASS) &&
+ dfa->syms[i].sym.ccl.ranges_size > 0)
+ free((char *) dfa->syms[i].sym.ccl.ranges);
+ }
+ if (dfa->nsyms > 0)
+ free((char *) dfa->syms);
+
+ if (dfa->nstates > 0)
+ free((char *) dfa->states);
+ if (dfa->ntrans > 0)
+ free((char *) dfa->trans);
+ free((char *) dfa);
+}
+
+void
+ure_write_dfa(ure_dfa_t dfa, FILE *out)
+{
+ ucs2_t i, j, k, h, l;
+ _ure_dstate_t *sp;
+ _ure_symtab_t *sym;
+ _ure_range_t *rp;
+
+ if (dfa == 0 || out == 0)
+ return;
+
+ /*
+ * Write all the different character classes.
+ */
+ for (i = 0, sym = dfa->syms; i < dfa->nsyms; i++, sym++) {
+ if (sym->type == _URE_CCLASS || sym->type == _URE_NCCLASS) {
+ fprintf(out, "C%hd = ", sym->id);
+ if (sym->sym.ccl.ranges_used > 0) {
+ putc('[', out);
+ if (sym->type == _URE_NCCLASS)
+ putc('^', out);
+ }
+ if (sym->props != 0) {
+ if (sym->type == _URE_NCCLASS)
+ fprintf(out, "\\P");
+ else
+ fprintf(out, "\\p");
+ for (k = h = 0; k < 32; k++) {
+ if (sym->props & (1 << k)) {
+ if (h != 0)
+ putc(',', out);
+ fprintf(out, "%d", k + 1);
+ h = 1;
+ }
+ }
+ }
+ /*
+ * Dump the ranges.
+ */
+ for (k = 0, rp = sym->sym.ccl.ranges;
+ k < sym->sym.ccl.ranges_used; k++, rp++) {
+ /*
+ * Check for UTF16 characters.
+ */
+ if (0x10000 <= rp->min_code &&
+ rp->min_code <= 0x10ffff) {
+ h = (ucs2_t) (((rp->min_code - 0x10000) >> 10) + 0xd800);
+ l = (ucs2_t) (((rp->min_code - 0x10000) & 1023) + 0xdc00);
+ fprintf(out, "\\x%04hX\\x%04hX", h, l);
+ } else
+ fprintf(out, "\\x%04lX", rp->min_code & 0xffff);
+ if (rp->max_code != rp->min_code) {
+ putc('-', out);
+ if (rp->max_code >= 0x10000 &&
+ rp->max_code <= 0x10ffff) {
+ h = (ucs2_t) (((rp->max_code - 0x10000) >> 10) + 0xd800);
+ l = (ucs2_t) (((rp->max_code - 0x10000) & 1023) + 0xdc00);
+ fprintf(out, "\\x%04hX\\x%04hX", h, l);
+ } else
+ fprintf(out, "\\x%04lX", rp->max_code & 0xffff);
+ }
+ }
+ if (sym->sym.ccl.ranges_used > 0)
+ putc(']', out);
+ putc('\n', out);
+ }
+ }
+
+ for (i = 0, sp = dfa->states; i < dfa->nstates; i++, sp++) {
+ fprintf(out, "S%hd = ", i);
+ if (sp->accepting) {
+ fprintf(out, "1 ");
+ if (sp->ntrans)
+ fprintf(out, "| ");
+ }
+ for (j = 0; j < sp->ntrans; j++) {
+ if (j > 0)
+ fprintf(out, "| ");
+
+ sym = dfa->syms + sp->trans[j].symbol;
+ switch (sym->type) {
+ case _URE_CHAR:
+ if (0x10000 <= sym->sym.chr && sym->sym.chr <= 0x10ffff) {
+ /*
+ * Take care of UTF16 characters.
+ */
+ h = (ucs2_t) (((sym->sym.chr - 0x10000) >> 10) + 0xd800);
+ l = (ucs2_t) (((sym->sym.chr - 0x10000) & 1023) + 0xdc00);
+ fprintf(out, "\\x%04hX\\x%04hX ", h, l);
+ } else
+ fprintf(out, "\\x%04lX ", sym->sym.chr & 0xffff);
+ break;
+ case _URE_ANY_CHAR:
+ fprintf(out, "<any> ");
+ break;
+ case _URE_BOL_ANCHOR:
+ fprintf(out, "<bol-anchor> ");
+ break;
+ case _URE_EOL_ANCHOR:
+ fprintf(out, "<eol-anchor> ");
+ break;
+ case _URE_CCLASS:
+ case _URE_NCCLASS:
+ fprintf(out, "[C%hd] ", sym->id);
+ break;
+ }
+ fprintf(out, "S%hd", sp->trans[j].next_state);
+ if (j + 1 < sp->ntrans)
+ putc(' ', out);
+ }
+ putc('\n', out);
+ }
+}
+
+#define _ure_issep(cc) ((cc) == '\n' || (cc) == '\r' || (cc) == 0x2028 ||\
+ (cc) == 0x2029)
+
+int
+ure_exec(ure_dfa_t dfa, int flags, ucs2_t *text, unsigned long textlen,
+ unsigned long *match_start, unsigned long *match_end)
+{
+ int i, j, matched, found, skip;
+ unsigned long ms, me;
+ ucs4_t c;
+ ucs2_t *sp, *ep, *lp;
+ _ure_dstate_t *stp;
+ _ure_symtab_t *sym;
+ _ure_range_t *rp;
+
+ if (dfa == 0 || text == 0)
+ return 0;
+
+ /*
+ * Handle the special case of an empty string matching the "^$" pattern.
+ */
+ if (textlen == 0 && (dfa->flags & _URE_DFA_BLANKLINE)) {
+ *match_start = *match_end = 0;
+ return 1;
+ }
+
+ sp = text;
+ ep = sp + textlen;
+
+ ms = me = ~0;
+
+ stp = dfa->states;
+
+ for (found = skip = 0; found == 0 && sp < ep; ) {
+ lp = sp;
+ c = *sp++;
+
+ /*
+ * Check to see if this is a high surrogate that should be
+ * combined with a following low surrogate.
+ */
+ if (sp < ep && 0xd800 <= c && c <= 0xdbff &&
+ 0xdc00 <= *sp && *sp <= 0xdfff)
+ c = 0x10000 + (((c & 0x03ff) << 10) | (*sp++ & 0x03ff));
+
+ /*
+ * Determine if the character is non-spacing and should be skipped.
+ */
+ if (_ure_matches_properties(_URE_NONSPACING, c) &&
+ (flags & URE_IGNORE_NONSPACING)) {
+ sp++;
+ continue;
+ }
+
+ if (dfa->flags & _URE_DFA_CASEFOLD)
+ c = _ure_tolower(c);
+
+ /*
+ * See if one of the transitions matches.
+ */
+ for (i = 0, matched = 0; matched == 0 && i < stp->ntrans; i++) {
+ sym = dfa->syms + stp->trans[i].symbol;
+ switch (sym->type) {
+ case _URE_ANY_CHAR:
+ if ((flags & URE_DOT_MATCHES_SEPARATORS) ||
+ !_ure_issep(c))
+ matched = 1;
+ break;
+ case _URE_CHAR:
+ if (c == sym->sym.chr)
+ matched = 1;
+ break;
+ case _URE_BOL_ANCHOR:
+ if (lp == text) {
+ sp = lp;
+ matched = 1;
+ } else if (_ure_issep(c)) {
+ if (c == '\r' && sp < ep && *sp == '\n')
+ sp++;
+ lp = sp;
+ matched = 1;
+ }
+ break;
+ case _URE_EOL_ANCHOR:
+ if (_ure_issep(c)) {
+ /*
+ * Put the pointer back before the separator so the match
+ * end position will be correct. This case will also
+ * cause the `sp' pointer to be advanced over the current
+ * separator once the match end point has been recorded.
+ */
+ sp = lp;
+ matched = 1;
+ }
+ break;
+ case _URE_CCLASS:
+ case _URE_NCCLASS:
+ if (sym->props != 0)
+ matched = _ure_matches_properties(sym->props, c);
+ for (j = 0, rp = sym->sym.ccl.ranges;
+ j < sym->sym.ccl.ranges_used; j++, rp++) {
+ if (rp->min_code <= c && c <= rp->max_code)
+ matched = 1;
+ }
+ if (sym->type == _URE_NCCLASS)
+ matched = !matched;
+ break;
+ }
+
+ if (matched) {
+ if (ms == ~0UL)
+ ms = lp - text;
+ else
+ me = sp - text;
+ stp = dfa->states + stp->trans[i].next_state;
+
+ /*
+ * If the match was an EOL anchor, adjust the pointer past the
+ * separator that caused the match. The correct match
+ * position has been recorded already.
+ */
+ if (sym->type == _URE_EOL_ANCHOR) {
+ /*
+ * Skip the character that caused the match.
+ */
+ sp++;
+
+ /*
+ * Handle the infamous CRLF situation.
+ */
+ if (sp < ep && c == '\r' && *sp == '\n')
+ sp++;
+ }
+ }
+ }
+
+ if (matched == 0) {
+ if (stp->accepting == 0) {
+ /*
+ * If the last state was not accepting, then reset
+ * and start over.
+ */
+ stp = dfa->states;
+ ms = me = ~0;
+ } else
+ /*
+ * The last state was accepting, so terminate the matching
+ * loop to avoid more work.
+ */
+ found = 1;
+ } else if (sp == ep) {
+ if (!stp->accepting) {
+ /*
+ * This ugly hack is to make sure the end-of-line anchors
+ * match when the source text hits the end. This is only done
+ * if the last subexpression matches.
+ */
+ for (i = 0; found == 0 && i < stp->ntrans; i++) {
+ sym = dfa->syms + stp->trans[i].symbol;
+ if (sym->type ==_URE_EOL_ANCHOR) {
+ stp = dfa->states + stp->trans[i].next_state;
+ if (stp->accepting) {
+ me = sp - text;
+ found = 1;
+ } else
+ break;
+ }
+ }
+ } else {
+ /*
+ * Make sure any conditions that match all the way to the end
+ * of the string match.
+ */
+ found = 1;
+ me = sp - text;
+ }
+ }
+ }
+
+ if (found == 0)
+ ms = me = ~0;
+
+ *match_start = ms;
+ *match_end = me;
+
+ return (ms != ~0UL) ? 1 : 0;
+}
diff --git a/libraries/liblunicode/ure/ure.h b/libraries/liblunicode/ure/ure.h
new file mode 100644
index 0000000..391d3f3
--- /dev/null
+++ b/libraries/liblunicode/ure/ure.h
@@ -0,0 +1,154 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Copyright 1997, 1998, 1999 Computing Research Labs,
+ * New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+ * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* $Id: ure.h,v 1.2 1999/09/21 15:47:44 mleisher Exp $ */
+
+#ifndef _h_ure
+#define _h_ure
+
+#include "portable.h"
+
+
+#include <stdio.h>
+
+LDAP_BEGIN_DECL
+
+/*
+ * Set of character class flags.
+ */
+#define _URE_NONSPACING 0x00000001
+#define _URE_COMBINING 0x00000002
+#define _URE_NUMDIGIT 0x00000004
+#define _URE_NUMOTHER 0x00000008
+#define _URE_SPACESEP 0x00000010
+#define _URE_LINESEP 0x00000020
+#define _URE_PARASEP 0x00000040
+#define _URE_CNTRL 0x00000080
+#define _URE_PUA 0x00000100
+
+#define _URE_UPPER 0x00000200
+#define _URE_LOWER 0x00000400
+#define _URE_TITLE 0x00000800
+#define _URE_MODIFIER 0x00001000
+#define _URE_OTHERLETTER 0x00002000
+#define _URE_DASHPUNCT 0x00004000
+#define _URE_OPENPUNCT 0x00008000
+#define _URE_CLOSEPUNCT 0x00010000
+#define _URE_OTHERPUNCT 0x00020000
+#define _URE_MATHSYM 0x00040000
+#define _URE_CURRENCYSYM 0x00080000
+#define _URE_OTHERSYM 0x00100000
+
+#define _URE_LTR 0x00200000
+#define _URE_RTL 0x00400000
+
+#define _URE_EURONUM 0x00800000
+#define _URE_EURONUMSEP 0x01000000
+#define _URE_EURONUMTERM 0x02000000
+#define _URE_ARABNUM 0x04000000
+#define _URE_COMMONSEP 0x08000000
+
+#define _URE_BLOCKSEP 0x10000000
+#define _URE_SEGMENTSEP 0x20000000
+
+#define _URE_WHITESPACE 0x40000000
+#define _URE_OTHERNEUT 0x80000000
+
+/*
+ * Error codes.
+ */
+#define _URE_OK 0
+#define _URE_UNEXPECTED_EOS -1
+#define _URE_CCLASS_OPEN -2
+#define _URE_UNBALANCED_GROUP -3
+#define _URE_INVALID_PROPERTY -4
+
+/*
+ * Options that can be combined for searching.
+ */
+#define URE_IGNORE_NONSPACING 0x01
+#define URE_DOT_MATCHES_SEPARATORS 0x02
+
+typedef unsigned long ucs4_t;
+typedef unsigned short ucs2_t;
+
+/*
+ * Opaque type for memory used when compiling expressions.
+ */
+typedef struct _ure_buffer_t *ure_buffer_t;
+
+/*
+ * Opaque type for the minimal DFA used when matching.
+ */
+typedef struct _ure_dfa_t *ure_dfa_t;
+
+/*************************************************************************
+ *
+ * API.
+ *
+ *************************************************************************/
+
+LDAP_LUNICODE_F (ure_buffer_t) ure_buffer_create LDAP_P((void));
+
+LDAP_LUNICODE_F (void) ure_buffer_free LDAP_P((ure_buffer_t buf));
+
+LDAP_LUNICODE_F (ure_dfa_t)
+ure_compile LDAP_P((ucs2_t *re, unsigned long relen,
+ int casefold, ure_buffer_t buf));
+
+LDAP_LUNICODE_F (void) ure_dfa_free LDAP_P((ure_dfa_t dfa));
+
+LDAP_LUNICODE_F (void) ure_write_dfa LDAP_P((ure_dfa_t dfa, FILE *out));
+
+LDAP_LUNICODE_F (int)
+ure_exec LDAP_P((ure_dfa_t dfa, int flags, ucs2_t *text,
+ unsigned long textlen, unsigned long *match_start,
+ unsigned long *match_end));
+
+/*************************************************************************
+ *
+ * Prototypes for stub functions used for URE. These need to be rewritten to
+ * use the Unicode support available on the system.
+ *
+ *************************************************************************/
+
+LDAP_LUNICODE_F (ucs4_t) _ure_tolower LDAP_P((ucs4_t c));
+
+LDAP_LUNICODE_F (int)
+_ure_matches_properties LDAP_P((unsigned long props, ucs4_t c));
+
+LDAP_END_DECL
+
+#endif /* _h_ure */
diff --git a/libraries/liblunicode/ure/urestubs.c b/libraries/liblunicode/ure/urestubs.c
new file mode 100644
index 0000000..c0ca30d
--- /dev/null
+++ b/libraries/liblunicode/ure/urestubs.c
@@ -0,0 +1,127 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/*
+ * Copyright 1997, 1998, 1999 Computing Research Labs,
+ * New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+ * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* $Id: urestubs.c,v 1.2 1999/09/21 15:47:44 mleisher Exp $" */
+
+#include "portable.h"
+#include <ac/bytes.h>
+
+#include "ure.h"
+
+#ifdef _MSC_VER
+# include "../ucdata/ucdata.h"
+#else
+# include "ucdata.h"
+#endif
+
+/*
+ * This file contains stub routines needed by the URE package to test
+ * character properties and other Unicode implementation specific details.
+ */
+
+/*
+ * This routine should return the lower case equivalent for the character or,
+ * if there is no lower case quivalent, the character itself.
+ */
+ucs4_t _ure_tolower(ucs4_t c)
+{
+ return uctoupper(c);
+}
+
+static struct ucmaskmap {
+ unsigned long mask1;
+ unsigned long mask2;
+} masks[32] = {
+ { UC_MN, 0 }, /* _URE_NONSPACING */
+ { UC_MC, 0 }, /* _URE_COMBINING */
+ { UC_ND, 0 }, /* _URE_NUMDIGIT */
+ { UC_NL|UC_NO, 0 }, /* _URE_NUMOTHER */
+ { UC_ZS, 0 }, /* _URE_SPACESEP */
+ { UC_ZL, 0 }, /* _URE_LINESEP */
+ { UC_ZP, 0 }, /* _URE_PARASEP */
+ { UC_CC, 0 }, /* _URE_CNTRL */
+ { UC_CO, 0 }, /* _URE_PUA */
+
+ { UC_LU, 0 }, /* _URE_UPPER */
+ { UC_LL, 0 }, /* _URE_LOWER */
+ { UC_LT, 0 }, /* _URE_TITLE */
+ { UC_LM, 0 }, /* _URE_MODIFIER */
+ { UC_LO, 0 }, /* _URE_OTHERLETTER */
+ { UC_PD, 0 }, /* _URE_DASHPUNCT */
+ { UC_PS, 0 }, /* _URE_OPENPUNCT */
+ { UC_PC, 0 }, /* _URE_CLOSEPUNCT */
+ { UC_PO, 0 }, /* _URE_OTHERPUNCT */
+ { UC_SM, 0 }, /* _URE_MATHSYM */
+ { UC_SC, 0 }, /* _URE_CURRENCYSYM */
+ { UC_SO, 0 }, /* _URE_OTHERSYM */
+
+ { UC_L, 0 }, /* _URE_LTR */
+ { UC_R, 0 }, /* _URE_RTL */
+
+ { 0, UC_EN }, /* _URE_EURONUM */
+ { 0, UC_ES }, /* _URE_EURONUMSEP */
+ { 0, UC_ET }, /* _URE_EURONUMTERM */
+ { 0, UC_AN }, /* _URE_ARABNUM */
+ { 0, UC_CS }, /* _URE_COMMONSEP */
+
+ { 0, UC_B }, /* _URE_BLOCKSEP */
+ { 0, UC_S }, /* _URE_SEGMENTSEP */
+
+ { 0, UC_WS }, /* _URE_WHITESPACE */
+ { 0, UC_ON } /* _URE_OTHERNEUT */
+};
+
+
+/*
+ * This routine takes a set of URE character property flags (see ure.h) along
+ * with a character and tests to see if the character has one or more of those
+ * properties.
+ */
+int
+_ure_matches_properties(unsigned long props, ucs4_t c)
+{
+ int i;
+ unsigned long mask1=0, mask2=0;
+
+ for( i=0; i<32; i++ ) {
+ if( props & (1 << i) ) {
+ mask1 |= masks[i].mask1;
+ mask2 |= masks[i].mask2;
+ }
+ }
+
+ return ucisprop( c, mask1, mask2 );
+}
diff --git a/libraries/liblunicode/utbm/README b/libraries/liblunicode/utbm/README
new file mode 100644
index 0000000..2a62d3c
--- /dev/null
+++ b/libraries/liblunicode/utbm/README
@@ -0,0 +1,121 @@
+#
+# $Id: README,v 1.1 1999/09/21 15:45:17 mleisher Exp $
+#
+# Copyright 1997, 1998, 1999 Computing Research Labs,
+# New Mexico State University
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+# THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+# OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+# THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+ Unicode and Boyer-Moore Searching
+ Version 0.2
+
+UTBM (Unicode Tuned Boyer-Moore) is a simple package that provides tuned
+Boyer-Moore searches on Unicode UCS2 text (handles high and low surrogates).
+
+---------------------------------------------------------------------------
+
+Assumptions:
+
+ o Search pattern and text already normalized in some fashion.
+
+ o Upper, lower, and title case conversions are one-to-one.
+
+ o For conversions between upper, lower, and title case, UCS2 characters
+ always convert to other UCS2 characters, and UTF-16 characters always
+ convert to other UTF-16 characters.
+
+Flags:
+
+ UTBM provides three processing flags:
+
+ o UTBM_CASEFOLD - search in a case-insensitive manner.
+
+ o UTBM_IGNORE_NONSPACING - ignore non-spacing characters in the pattern and
+ the text.
+
+ o UTBM_SPACE_COMPRESS - view as a *single space*, sequential groups of
+ U+2028, U+2029, '\n', '\r', '\t', and any
+ character identified as a space by the Unicode
+ support on the platform.
+
+ This flag also causes all characters identified
+ as control by the Unicode support on the
+ platform to be ignored (except for '\n', '\r',
+ and '\t').
+
+---------------------------------------------------------------------------
+
+Before using UTBM
+-----------------
+Before UTBM is used, some functions need to be created. The "utbmstub.c" file
+contains stubs that need to be rewritten so they work with the Unicode support
+on the platform on which this package is being used.
+
+Using UTBM
+----------
+
+Sample pseudo-code fragment.
+
+ utbm_pattern_t pat;
+ ucs2_t *pattern, *text;
+ unsigned long patternlen, textlen;
+ unsigned long flags, match_start, match_end;
+
+ /*
+ * Allocate the dynamic storage needed for a search pattern.
+ */
+ pat = utbm_create_pattern();
+
+ /*
+ * Set the search flags desired.
+ */
+ flags = UTBM_CASEFOLD|UTBM_IGNORE_NONSPACING;
+
+ /*
+ * Compile the search pattern.
+ */
+ utbm_compile(pattern, patternlen, flags, pat);
+
+ /*
+ * Find the first occurrence of the search pattern in the text.
+ */
+ if (utbm_exec(pat, text, textlen, &match_start, &match_end))
+ printf("MATCH: %ld %ld\n", match_start, match_end);
+
+ /*
+ * Free the dynamic storage used for the search pattern.
+ */
+ ure_free_pattern(pat);
+
+---------------------------------------------------------------------------
+
+Mark Leisher <mleisher@crl.nmsu.edu>
+2 May 1997
+
+===========================================================================
+
+CHANGES
+-------
+
+Version: 0.2
+Date : 21 September 1999
+==========================
+ 1. Added copyright stuff and put in CVS.
+
diff --git a/libraries/liblunicode/utbm/utbm.c b/libraries/liblunicode/utbm/utbm.c
new file mode 100644
index 0000000..458305c
--- /dev/null
+++ b/libraries/liblunicode/utbm/utbm.c
@@ -0,0 +1,472 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Copyright 1997, 1998, 1999 Computing Research Labs,
+ * New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+ * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* $Id: utbm.c,v 1.1 1999/09/21 15:45:17 mleisher Exp $ */
+
+/*
+ * Assumptions:
+ * 1. Case conversions of UTF-16 characters must also be UTF-16 characters.
+ * 2. Case conversions are all one-to-one.
+ * 3. Text and pattern have already been normalized in some fashion.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "utbm.h"
+
+/*
+ * Single pattern character.
+ */
+typedef struct {
+ ucs4_t lc;
+ ucs4_t uc;
+ ucs4_t tc;
+} _utbm_char_t;
+
+typedef struct {
+ _utbm_char_t *ch;
+ unsigned long skip;
+} _utbm_skip_t;
+
+typedef struct _utbm_pattern_t {
+ unsigned long flags;
+
+ _utbm_char_t *pat;
+ unsigned long pat_used;
+ unsigned long pat_size;
+ unsigned long patlen;
+
+ _utbm_skip_t *skip;
+ unsigned long skip_used;
+ unsigned long skip_size;
+
+ unsigned long md4;
+} _utbm_pattern_t;
+
+/*************************************************************************
+ *
+ * Support functions.
+ *
+ *************************************************************************/
+
+/*
+ * Routine to look up the skip value for a character.
+ */
+static unsigned long
+_utbm_skip(utbm_pattern_t p, ucs2_t *start, ucs2_t *end)
+{
+ unsigned long i;
+ ucs4_t c1, c2;
+ _utbm_skip_t *sp;
+
+ if (start >= end)
+ return 0;
+
+ c1 = *start;
+ c2 = (start + 1 < end) ? *(start + 1) : ~0;
+ if (0xd800 <= c1 && c1 <= 0xdbff && 0xdc00 <= c2 && c2 <= 0xdfff)
+ c1 = 0x10000 + (((c1 & 0x03ff) << 10) | (c2 & 0x03ff));
+
+ for (i = 0, sp = p->skip; i < p->skip_used; i++, sp++) {
+ if (!((c1 ^ sp->ch->uc) & (c1 ^ sp->ch->lc) & (c1 ^ sp->ch->tc))) {
+ return ((unsigned long) (end - start) < sp->skip) ?
+ end - start : sp->skip;
+ }
+ }
+ return p->patlen;
+}
+
+static int
+_utbm_match(utbm_pattern_t pat, ucs2_t *text, ucs2_t *start, ucs2_t *end,
+ unsigned long *match_start, unsigned long *match_end)
+{
+ int check_space;
+ ucs4_t c1, c2;
+ unsigned long count;
+ _utbm_char_t *cp;
+
+ /*
+ * Set the potential match endpoint first.
+ */
+ *match_end = (start - text) + 1;
+
+ c1 = *start;
+ c2 = (start + 1 < end) ? *(start + 1) : ~0;
+ if (0xd800 <= c1 && c1 <= 0xdbff && 0xdc00 <= c2 && c2 <= 0xdfff) {
+ c1 = 0x10000 + (((c1 & 0x03ff) << 10) | (c2 & 0x03ff));
+ /*
+ * Adjust the match end point to occur after the UTF-16 character.
+ */
+ *match_end = *match_end + 1;
+ }
+
+ if (pat->pat_used == 1) {
+ *match_start = start - text;
+ return 1;
+ }
+
+ /*
+ * Compare backward.
+ */
+ cp = pat->pat + (pat->pat_used - 1);
+
+ for (count = pat->patlen; start > text && count > 0;) {
+ /*
+ * Ignore non-spacing characters if indicated.
+ */
+ if (pat->flags & UTBM_IGNORE_NONSPACING) {
+ while (start > text && _utbm_nonspacing(c1)) {
+ c2 = *--start;
+ c1 = (start - 1 > text) ? *(start - 1) : ~0;
+ if (0xdc00 <= c2 && c2 <= 0xdfff &&
+ 0xd800 <= c1 && c1 <= 0xdbff) {
+ c1 = 0x10000 + (((c1 & 0x03ff) << 10) | (c2 & 0x03ff));
+ start--;
+ } else
+ c1 = c2;
+ }
+ }
+
+ /*
+ * Handle space compression if indicated.
+ */
+ if (pat->flags & UTBM_SPACE_COMPRESS) {
+ check_space = 0;
+ while (start > text &&
+ (_utbm_isspace(c1, 1) || _utbm_iscntrl(c1))) {
+ check_space = _utbm_isspace(c1, 1);
+ c2 = *--start;
+ c1 = (start - 1 > text) ? *(start - 1) : ~0;
+ if (0xdc00 <= c2 && c2 <= 0xdfff &&
+ 0xd800 <= c1 && c1 <= 0xdbff) {
+ c1 = 0x10000 + (((c1 & 0x03ff) << 10) | (c2 & 0x03ff));
+ start--;
+ } else
+ c1 = c2;
+ }
+ /*
+ * Handle things if space compression was indicated and one or
+ * more member characters were found.
+ */
+ if (check_space) {
+ if (cp->uc != ' ')
+ return 0;
+ cp--;
+ count--;
+ }
+ }
+
+ /*
+ * Handle the normal comparison cases.
+ */
+ if (count > 0 && ((c1 ^ cp->uc) & (c1 ^ cp->lc) & (c1 ^ cp->tc)))
+ return 0;
+
+ count -= (c1 >= 0x10000) ? 2 : 1;
+ if (count > 0) {
+ cp--;
+
+ /*
+ * Get the next preceding character.
+ */
+ if (start > text) {
+ c2 = *--start;
+ c1 = (start - 1 > text) ? *(start - 1) : ~0;
+ if (0xdc00 <= c2 && c2 <= 0xdfff &&
+ 0xd800 <= c1 && c1 <= 0xdbff) {
+ c1 = 0x10000 + (((c1 & 0x03ff) << 10) | (c2 & 0x03ff));
+ start--;
+ } else
+ c1 = c2;
+ }
+ }
+ }
+
+ /*
+ * Set the match start position.
+ */
+ *match_start = start - text;
+ return 1;
+}
+
+/*************************************************************************
+ *
+ * API.
+ *
+ *************************************************************************/
+
+utbm_pattern_t
+utbm_create_pattern(void)
+{
+ utbm_pattern_t p;
+
+ p = (utbm_pattern_t) malloc(sizeof(_utbm_pattern_t));
+ (void) memset((char *) p, '\0', sizeof(_utbm_pattern_t));
+ return p;
+}
+
+void
+utbm_free_pattern(utbm_pattern_t pattern)
+{
+ if (pattern == 0)
+ return;
+
+ if (pattern->pat_size > 0)
+ free((char *) pattern->pat);
+
+ if (pattern->skip_size > 0)
+ free((char *) pattern->skip);
+
+ free((char *) pattern);
+}
+
+void
+utbm_compile(ucs2_t *pat, unsigned long patlen, unsigned long flags,
+ utbm_pattern_t p)
+{
+ int have_space;
+ unsigned long i, j, k, slen;
+ _utbm_char_t *cp;
+ _utbm_skip_t *sp;
+ ucs4_t c1, c2, sentinel;
+
+ if (p == 0 || pat == 0 || *pat == 0 || patlen == 0)
+ return;
+
+ /*
+ * Reset the pattern buffer.
+ */
+ p->patlen = p->pat_used = p->skip_used = 0;
+
+ /*
+ * Set the flags.
+ */
+ p->flags = flags;
+
+ /*
+ * Initialize the extra skip flag.
+ */
+ p->md4 = 1;
+
+ /*
+ * Allocate more storage if necessary.
+ */
+ if (patlen > p->pat_size) {
+ if (p->pat_size == 0) {
+ p->pat = (_utbm_char_t *) malloc(sizeof(_utbm_char_t) * patlen);
+ p->skip = (_utbm_skip_t *) malloc(sizeof(_utbm_skip_t) * patlen);
+ } else {
+ p->pat = (_utbm_char_t *)
+ realloc((char *) p->pat, sizeof(_utbm_char_t) * patlen);
+ p->skip = (_utbm_skip_t *)
+ realloc((char *) p->skip, sizeof(_utbm_skip_t) * patlen);
+ }
+ p->pat_size = p->skip_size = patlen;
+ }
+
+ /*
+ * Preprocess the pattern to remove controls (if specified) and determine
+ * case.
+ */
+ for (have_space = 0, cp = p->pat, i = 0; i < patlen; i++) {
+ c1 = pat[i];
+ c2 = (i + 1 < patlen) ? pat[i + 1] : ~0;
+ if (0xd800 <= c1 && c1 <= 0xdbff && 0xdc00 <= c2 && c2 <= 0xdfff)
+ c1 = 0x10000 + (((c1 & 0x03ff) << 10) | (c2 & 0x03ff));
+
+ /*
+ * Make sure the `have_space' flag is turned off if the character
+ * is not an appropriate one.
+ */
+ if (!_utbm_isspace(c1, flags & UTBM_SPACE_COMPRESS))
+ have_space = 0;
+
+ /*
+ * If non-spacing characters should be ignored, do it here.
+ */
+ if ((flags & UTBM_IGNORE_NONSPACING) && _utbm_nonspacing(c1))
+ continue;
+
+ /*
+ * Check if spaces and controls need to be compressed.
+ */
+ if (flags & UTBM_SPACE_COMPRESS) {
+ if (_utbm_isspace(c1, 1)) {
+ if (!have_space) {
+ /*
+ * Add a space and set the flag.
+ */
+ cp->uc = cp->lc = cp->tc = ' ';
+ cp++;
+
+ /*
+ * Increase the real pattern length.
+ */
+ p->patlen++;
+ sentinel = ' ';
+ have_space = 1;
+ }
+ continue;
+ }
+
+ /*
+ * Ignore all control characters.
+ */
+ if (_utbm_iscntrl(c1))
+ continue;
+ }
+
+ /*
+ * Add the character.
+ */
+ if (flags & UTBM_CASEFOLD) {
+ cp->uc = _utbm_toupper(c1);
+ cp->lc = _utbm_tolower(c1);
+ cp->tc = _utbm_totitle(c1);
+ } else
+ cp->uc = cp->lc = cp->tc = c1;
+
+ /*
+ * Set the sentinel character.
+ */
+ sentinel = cp->uc;
+
+ /*
+ * Move to the next character.
+ */
+ cp++;
+
+ /*
+ * Increase the real pattern length appropriately.
+ */
+ p->patlen += (c1 >= 0x10000) ? 2 : 1;
+
+ /*
+ * Increment the loop index for UTF-16 characters.
+ */
+ i += (c1 >= 0x10000) ? 1 : 0;
+
+ }
+
+ /*
+ * Set the number of characters actually used.
+ */
+ p->pat_used = cp - p->pat;
+
+ /*
+ * Go through and construct the skip array and determine the actual length
+ * of the pattern in UCS2 terms.
+ */
+ slen = p->patlen - 1;
+ cp = p->pat;
+ for (i = k = 0; i < p->pat_used; i++, cp++) {
+ /*
+ * Locate the character in the skip array.
+ */
+ for (sp = p->skip, j = 0;
+ j < p->skip_used && sp->ch->uc != cp->uc; j++, sp++) ;
+
+ /*
+ * If the character is not found, set the new skip element and
+ * increase the number of skip elements.
+ */
+ if (j == p->skip_used) {
+ sp->ch = cp;
+ p->skip_used++;
+ }
+
+ /*
+ * Set the updated skip value. If the character is UTF-16 and is
+ * not the last one in the pattern, add one to its skip value.
+ */
+ sp->skip = slen - k;
+ if (cp->uc >= 0x10000 && k + 2 < slen)
+ sp->skip++;
+
+ /*
+ * Set the new extra skip for the sentinel character.
+ */
+ if (((cp->uc >= 0x10000 && k + 2 <= slen) || k + 1 <= slen) &&
+ cp->uc == sentinel)
+ p->md4 = slen - k;
+
+ /*
+ * Increase the actual index.
+ */
+ k += (cp->uc >= 0x10000) ? 2 : 1;
+ }
+}
+
+int
+utbm_exec(utbm_pattern_t pat, ucs2_t *text, unsigned long textlen,
+ unsigned long *match_start, unsigned long *match_end)
+{
+ unsigned long k;
+ ucs2_t *start, *end;
+
+ if (pat == 0 || pat->pat_used == 0 || text == 0 || textlen == 0 ||
+ textlen < pat->patlen)
+ return 0;
+
+ start = text + pat->patlen;
+ end = text + textlen;
+
+ /*
+ * Adjust the start point if it points to a low surrogate.
+ */
+ if (0xdc00 <= *start && *start <= 0xdfff &&
+ 0xd800 <= *(start - 1) && *(start - 1) <= 0xdbff)
+ start--;
+
+ while (start < end) {
+ while ((k = _utbm_skip(pat, start, end))) {
+ start += k;
+ if (start < end && 0xdc00 <= *start && *start <= 0xdfff &&
+ 0xd800 <= *(start - 1) && *(start - 1) <= 0xdbff)
+ start--;
+ }
+
+ if (start < end &&
+ _utbm_match(pat, text, start, end, match_start, match_end))
+ return 1;
+
+ start += pat->md4;
+ if (start < end && 0xdc00 <= *start && *start <= 0xdfff &&
+ 0xd800 <= *(start - 1) && *(start - 1) <= 0xdbff)
+ start--;
+ }
+ return 0;
+}
diff --git a/libraries/liblunicode/utbm/utbm.h b/libraries/liblunicode/utbm/utbm.h
new file mode 100644
index 0000000..afdf2ce
--- /dev/null
+++ b/libraries/liblunicode/utbm/utbm.h
@@ -0,0 +1,114 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Copyright 1997, 1998, 1999 Computing Research Labs,
+ * New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+ * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* $Id: utbm.h,v 1.1 1999/09/21 15:45:18 mleisher Exp $ */
+
+#ifndef _h_utbm
+#define _h_utbm
+
+#include "portable.h"
+
+LDAP_BEGIN_DECL
+
+/*************************************************************************
+ *
+ * Types.
+ *
+ *************************************************************************/
+
+/*
+ * Fundamental character types.
+ */
+typedef unsigned long ucs4_t;
+typedef unsigned short ucs2_t;
+
+/*
+ * An opaque type used for the search pattern.
+ */
+typedef struct _utbm_pattern_t *utbm_pattern_t;
+
+/*************************************************************************
+ *
+ * Flags.
+ *
+ *************************************************************************/
+
+#define UTBM_CASEFOLD 0x01
+#define UTBM_IGNORE_NONSPACING 0x02
+#define UTBM_SPACE_COMPRESS 0x04
+
+/*************************************************************************
+ *
+ * API.
+ *
+ *************************************************************************/
+
+LDAP_LUNICODE_F (utbm_pattern_t) utbm_create_pattern LDAP_P((void));
+
+LDAP_LUNICODE_F (void) utbm_free_pattern LDAP_P((utbm_pattern_t pattern));
+
+LDAP_LUNICODE_F (void)
+utbm_compile LDAP_P((ucs2_t *pat, unsigned long patlen,
+ unsigned long flags, utbm_pattern_t pattern));
+
+LDAP_LUNICODE_F (int)
+utbm_exec LDAP_P((utbm_pattern_t pat, ucs2_t *text,
+ unsigned long textlen, unsigned long *match_start,
+ unsigned long *match_end));
+
+/*************************************************************************
+ *
+ * Prototypes for the stub functions needed.
+ *
+ *************************************************************************/
+
+LDAP_LUNICODE_F (int) _utbm_isspace LDAP_P((ucs4_t c, int compress));
+
+LDAP_LUNICODE_F (int) _utbm_iscntrl LDAP_P((ucs4_t c));
+
+LDAP_LUNICODE_F (int) _utbm_nonspacing LDAP_P((ucs4_t c));
+
+LDAP_LUNICODE_F (ucs4_t) _utbm_tolower LDAP_P((ucs4_t c));
+
+LDAP_LUNICODE_F (ucs4_t) _utbm_toupper LDAP_P((ucs4_t c));
+
+LDAP_LUNICODE_F (ucs4_t) _utbm_totitle LDAP_P((ucs4_t c));
+
+LDAP_END_DECL
+
+#endif
+
+
+#endif /* _h_utbm */
diff --git a/libraries/liblunicode/utbm/utbmstub.c b/libraries/liblunicode/utbm/utbmstub.c
new file mode 100644
index 0000000..f5713b0
--- /dev/null
+++ b/libraries/liblunicode/utbm/utbmstub.c
@@ -0,0 +1,105 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Copyright 1997, 1998, 1999 Computing Research Labs,
+ * New Mexico State University
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+ * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+/* $Id: utbmstub.c,v 1.1 1999/09/21 15:45:18 mleisher Exp $ */
+
+#include "utbm.h"
+
+/*
+ * This should be redefined to use the `isspace' function available in the
+ * Unicode support on the platform where this is being used.
+ */
+#define _platform_isspace(x) 0
+
+/*
+ * Return non-zero for any character that should be considered the equivalent
+ * of a space character. Return zero otherwise.
+ */
+int
+_utbm_isspace(ucs4_t c, int compress)
+{
+ if (compress)
+ return (c == 0x09 || c == 0x0a || c == 0x0d ||
+ c == 0x2028 || c == 0x2029 || _platform_isspace(c)) ? 1 : 0;
+
+ return _platform_isspace(c);
+
+}
+
+/*
+ * Return non-zero if the character is a control character, or zero otherwise.
+ */
+int
+_utbm_iscntrl(ucs4_t c)
+{
+ return 0;
+}
+
+/*
+ * Return non-zero if the character is a non-spacing character, or zero
+ * otherwise.
+ */
+int
+_utbm_nonspacing(ucs4_t c)
+{
+ return 0;
+}
+
+/*
+ * Convert a character to lower case.
+ */
+ucs4_t
+_utbm_tolower(ucs4_t c)
+{
+ return c;
+}
+
+/*
+ * Convert a character to upper case.
+ */
+ucs4_t
+_utbm_toupper(ucs4_t c)
+{
+ return c;
+}
+
+/*
+ * Convert a character to title case.
+ */
+ucs4_t
+_utbm_totitle(ucs4_t c)
+{
+ return c;
+}
diff --git a/libraries/liblutil/Makefile.in b/libraries/liblutil/Makefile.in
new file mode 100644
index 0000000..fff1c76
--- /dev/null
+++ b/libraries/liblutil/Makefile.in
@@ -0,0 +1,53 @@
+# Makefile for -llutil
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+LIBRARY = liblutil.a
+
+LDAP_INCDIR= ../../include
+LDAP_LIBDIR= ../../libraries
+
+NT_SRCS = ntservice.c
+NT_OBJS = ntservice.o slapdmsg.res
+
+UNIX_SRCS = detach.c
+UNIX_OBJS = detach.o
+
+XLIBS = $(LIBRARY) $(LDAP_LIBLBER_LA)
+
+SRCS = base64.c entropy.c sasl.c signal.c hash.c passfile.c \
+ md5.c passwd.c sha1.c getpass.c lockf.c utils.c uuid.c sockpair.c \
+ meter.c \
+ @LIBSRCS@ $(@PLAT@_SRCS)
+
+OBJS = base64.o entropy.o sasl.o signal.o hash.o passfile.o \
+ md5.o passwd.o sha1.o getpass.o lockf.o utils.o uuid.o sockpair.o \
+ meter.o \
+ @LIBOBJS@ $(@PLAT@_OBJS)
+
+# These rules are for a Mingw32 build, specifically.
+# It's ok for them to be here because the clean rule is harmless, and
+# slapdmsg.res won't get built unless it's declared in OBJS.
+
+RC = @RC@
+
+slapdmsg.bin: FORCE
+ @if [ ! -f $@ ]; then cp $(srcdir)/$@ .; fi
+
+slapdmsg.res: slapdmsg.rc slapdmsg.bin
+ $(RC) $< -O coff -o $@
+
+clean-local:
+ $(RM) *.res
+
diff --git a/libraries/liblutil/base64.c b/libraries/liblutil/base64.c
new file mode 100644
index 0000000..9c3e258
--- /dev/null
+++ b/libraries/liblutil/base64.c
@@ -0,0 +1,308 @@
+/* base64.c -- routines to encode/decode base64 data */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1998-2003 Kurt D. Zeilenga.
+ * Portions Copyright 1995 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1996, 1998 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+/* This work is based upon Base64 routines (developed by IBM) found
+ * Berkeley Internet Name Daemon (BIND) as distributed by ISC. They
+ * were adapted for inclusion in OpenLDAP Software by Kurt D. Zeilenga.
+ */
+
+#include "portable.h"
+
+#include <ac/assert.h>
+#include <ac/stdlib.h>
+#include <ac/ctype.h>
+#include <ac/string.h>
+
+/* include socket.h to get sys/types.h and/or winsock2.h */
+#include <ac/socket.h>
+
+#include "lutil.h"
+
+static const char Base64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char Pad64 = '=';
+
+/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt)
+ The following encoding technique is taken from RFC 1521 by Borenstein
+ and Freed. It is reproduced here in a slightly edited form for
+ convenience.
+
+ A 65-character subset of US-ASCII is used, enabling 6 bits to be
+ represented per printable character. (The extra 65th character, "=",
+ is used to signify a special processing function.)
+
+ The encoding process represents 24-bit groups of input bits as output
+ strings of 4 encoded characters. Proceeding from left to right, a
+ 24-bit input group is formed by concatenating 3 8-bit input groups.
+ These 24 bits are then treated as 4 concatenated 6-bit groups, each
+ of which is translated into a single digit in the base64 alphabet.
+
+ Each 6-bit group is used as an index into an array of 64 printable
+ characters. The character referenced by the index is placed in the
+ output string.
+
+ Table 1: The Base64 Alphabet
+
+ Value Encoding Value Encoding Value Encoding Value Encoding
+ 0 A 17 R 34 i 51 z
+ 1 B 18 S 35 j 52 0
+ 2 C 19 T 36 k 53 1
+ 3 D 20 U 37 l 54 2
+ 4 E 21 V 38 m 55 3
+ 5 F 22 W 39 n 56 4
+ 6 G 23 X 40 o 57 5
+ 7 H 24 Y 41 p 58 6
+ 8 I 25 Z 42 q 59 7
+ 9 J 26 a 43 r 60 8
+ 10 K 27 b 44 s 61 9
+ 11 L 28 c 45 t 62 +
+ 12 M 29 d 46 u 63 /
+ 13 N 30 e 47 v
+ 14 O 31 f 48 w (pad) =
+ 15 P 32 g 49 x
+ 16 Q 33 h 50 y
+
+ Special processing is performed if fewer than 24 bits are available
+ at the end of the data being encoded. A full encoding quantum is
+ always completed at the end of a quantity. When fewer than 24 input
+ bits are available in an input group, zero bits are added (on the
+ right) to form an integral number of 6-bit groups. Padding at the
+ end of the data is performed using the '=' character.
+
+ Since all base64 input is an integral number of octets, only the
+ -------------------------------------------------
+ following cases can arise:
+
+ (1) the final quantum of encoding input is an integral
+ multiple of 24 bits; here, the final unit of encoded
+ output will be an integral multiple of 4 characters
+ with no "=" padding,
+ (2) the final quantum of encoding input is exactly 8 bits;
+ here, the final unit of encoded output will be two
+ characters followed by two "=" padding characters, or
+ (3) the final quantum of encoding input is exactly 16 bits;
+ here, the final unit of encoded output will be three
+ characters followed by one "=" padding character.
+ */
+
+int
+lutil_b64_ntop(
+ u_char const *src,
+ size_t srclength,
+ char *target,
+ size_t targsize)
+{
+ size_t datalength = 0;
+ u_char input[3];
+ u_char output[4];
+ size_t i;
+
+ while (2 < srclength) {
+ input[0] = *src++;
+ input[1] = *src++;
+ input[2] = *src++;
+ srclength -= 3;
+
+ output[0] = input[0] >> 2;
+ output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
+ output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
+ output[3] = input[2] & 0x3f;
+ assert(output[0] < 64);
+ assert(output[1] < 64);
+ assert(output[2] < 64);
+ assert(output[3] < 64);
+
+ if (datalength + 4 > targsize)
+ return (-1);
+ target[datalength++] = Base64[output[0]];
+ target[datalength++] = Base64[output[1]];
+ target[datalength++] = Base64[output[2]];
+ target[datalength++] = Base64[output[3]];
+ }
+
+ /* Now we worry about padding. */
+ if (0 != srclength) {
+ /* Get what's left. */
+ input[0] = input[1] = input[2] = '\0';
+ for (i = 0; i < srclength; i++)
+ input[i] = *src++;
+
+ output[0] = input[0] >> 2;
+ output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
+ output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
+ assert(output[0] < 64);
+ assert(output[1] < 64);
+ assert(output[2] < 64);
+
+ if (datalength + 4 > targsize)
+ return (-1);
+ target[datalength++] = Base64[output[0]];
+ target[datalength++] = Base64[output[1]];
+ if (srclength == 1)
+ target[datalength++] = Pad64;
+ else
+ target[datalength++] = Base64[output[2]];
+ target[datalength++] = Pad64;
+ }
+ if (datalength >= targsize)
+ return (-1);
+ target[datalength] = '\0'; /* Returned value doesn't count \0. */
+ return (datalength);
+}
+
+/* skips all whitespace anywhere.
+ converts characters, four at a time, starting at (or after)
+ src from base - 64 numbers into three 8 bit bytes in the target area.
+ it returns the number of data bytes stored at the target, or -1 on error.
+ */
+
+int
+lutil_b64_pton(
+ char const *src,
+ u_char *target,
+ size_t targsize)
+{
+ int tarindex, state, ch;
+ char *pos;
+
+ state = 0;
+ tarindex = 0;
+
+ while ((ch = *src++) != '\0') {
+ if (isascii(ch) && isspace(ch)) /* Skip whitespace anywhere. */
+ continue;
+
+ if (ch == Pad64)
+ break;
+
+ pos = strchr(Base64, ch);
+ if (pos == 0) /* A non-base64 character. */
+ return (-1);
+
+ switch (state) {
+ case 0:
+ if (target) {
+ if ((size_t)tarindex >= targsize)
+ return (-1);
+ target[tarindex] = (pos - Base64) << 2;
+ }
+ state = 1;
+ break;
+ case 1:
+ if (target) {
+ if ((size_t)tarindex + 1 >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base64) >> 4;
+ target[tarindex+1] = ((pos - Base64) & 0x0f)
+ << 4 ;
+ }
+ tarindex++;
+ state = 2;
+ break;
+ case 2:
+ if (target) {
+ if ((size_t)tarindex + 1 >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base64) >> 2;
+ target[tarindex+1] = ((pos - Base64) & 0x03)
+ << 6;
+ }
+ tarindex++;
+ state = 3;
+ break;
+ case 3:
+ if (target) {
+ if ((size_t)tarindex >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base64);
+ }
+ tarindex++;
+ state = 0;
+ break;
+ default:
+ abort();
+ }
+ }
+
+ /*
+ * We are done decoding Base-64 chars. Let's see if we ended
+ * on a byte boundary, and/or with erroneous trailing characters.
+ */
+
+ if (ch == Pad64) { /* We got a pad char. */
+ ch = *src++; /* Skip it, get next. */
+ switch (state) {
+ case 0: /* Invalid = in first position */
+ case 1: /* Invalid = in second position */
+ return (-1);
+
+ case 2: /* Valid, means one byte of info */
+ /* Skip any number of spaces. */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (! (isascii(ch) && isspace(ch)))
+ break;
+ /* Make sure there is another trailing = sign. */
+ if (ch != Pad64)
+ return (-1);
+ ch = *src++; /* Skip the = */
+ /* Fall through to "single trailing =" case. */
+ /* FALLTHROUGH */
+
+ case 3: /* Valid, means two bytes of info */
+ /*
+ * We know this char is an =. Is there anything but
+ * whitespace after it?
+ */
+ for ((void)NULL; ch != '\0'; ch = *src++)
+ if (! (isascii(ch) && isspace(ch)))
+ return (-1);
+
+ /*
+ * Now make sure for cases 2 and 3 that the "extra"
+ * bits that slopped past the last full byte were
+ * zeros. If we don't check them, they become a
+ * subliminal channel.
+ */
+ if (target && target[tarindex] != 0)
+ return (-1);
+ }
+ } else {
+ /*
+ * We ended by seeing the end of the string. Make sure we
+ * have no partial bytes lying around.
+ */
+ if (state != 0)
+ return (-1);
+ }
+
+ return (tarindex);
+}
diff --git a/libraries/liblutil/detach.c b/libraries/liblutil/detach.c
new file mode 100644
index 0000000..e939e76
--- /dev/null
+++ b/libraries/liblutil/detach.c
@@ -0,0 +1,144 @@
+/* detach.c -- routines to daemonize a process */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/*
+ * Copyright (c) 1990, 1994 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+/* This work was originally developed by the University of Michigan
+ * and distributed as part of U-MICH LDAP.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/signal.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#include "lutil.h"
+
+int
+lutil_detach( int debug, int do_close )
+{
+ int i, sd, nbits, pid;
+
+#ifdef HAVE_SYSCONF
+ nbits = sysconf( _SC_OPEN_MAX );
+#elif defined(HAVE_GETDTABLESIZE)
+ nbits = getdtablesize();
+#else
+ nbits = FD_SETSIZE;
+#endif
+
+#ifdef FD_SETSIZE
+ if ( nbits > FD_SETSIZE ) {
+ nbits = FD_SETSIZE;
+ }
+#endif /* FD_SETSIZE */
+
+ if ( debug == 0 ) {
+ for ( i = 0; i < 5; i++ ) {
+#ifdef HAVE_THR
+ pid = fork1();
+#else
+ pid = fork();
+#endif
+ switch ( pid )
+ {
+ case -1:
+ sleep( 5 );
+ continue;
+
+ case 0:
+ break;
+
+ default:
+ return pid;
+ }
+ break;
+ }
+
+ if ( (sd = open( "/dev/null", O_RDWR )) == -1 &&
+ (sd = open( "/dev/null", O_RDONLY )) == -1 &&
+ /* Panic -- open *something* */
+ (sd = open( "/", O_RDONLY )) == -1 ) {
+ perror("/dev/null");
+ } else {
+ /* redirect stdin, stdout, stderr to /dev/null */
+ dup2( sd, STDIN_FILENO );
+ dup2( sd, STDOUT_FILENO );
+ dup2( sd, STDERR_FILENO );
+
+ switch( sd ) {
+ default:
+ close( sd );
+ case STDIN_FILENO:
+ case STDOUT_FILENO:
+ case STDERR_FILENO:
+ break;
+ }
+ }
+
+ if ( do_close ) {
+ /* close everything else */
+ for ( i = 0; i < nbits; i++ ) {
+ if( i != STDIN_FILENO &&
+ i != STDOUT_FILENO &&
+ i != STDERR_FILENO )
+ {
+ close( i );
+ }
+ }
+ }
+
+#ifdef CHDIR_TO_ROOT
+ (void) chdir( "/" );
+#endif
+
+#ifdef HAVE_SETSID
+ (void) setsid();
+#elif defined(TIOCNOTTY)
+ if ( (sd = open( "/dev/tty", O_RDWR )) != -1 ) {
+ (void) ioctl( sd, TIOCNOTTY, NULL );
+ (void) close( sd );
+ }
+#endif
+ }
+
+#ifdef SIGPIPE
+ (void) SIGNAL( SIGPIPE, SIG_IGN );
+#endif
+ return 0;
+}
diff --git a/libraries/liblutil/entropy.c b/libraries/liblutil/entropy.c
new file mode 100644
index 0000000..289aca4
--- /dev/null
+++ b/libraries/liblutil/entropy.c
@@ -0,0 +1,170 @@
+/* entropy.c -- routines for providing pseudo-random data */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Kurt D. Zeilenga.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* This work was initially developed by Kurt D. Zeilenga for
+ * inclusion in OpenLDAP Software based, in part, on publicly
+ * available works (as noted below).
+ */
+
+#include "portable.h"
+
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#ifdef HAVE_PROCESS_H
+#include <process.h>
+#endif
+
+#include <fcntl.h>
+
+#include <lutil.h>
+#include <lutil_md5.h>
+
+/*
+ * lutil_entropy() provides nbytes of entropy in buf.
+ * Quality offered is suitable for one-time uses, such as "once" keys.
+ * Values may not be suitable for multi-time uses.
+ *
+ * Note: Callers are encouraged to provide additional bytes of
+ * of entropy in the buf argument. This information is used in
+ * fallback mode to improve the quality of bytes returned.
+ *
+ * This routinue should be extended to support additional sources
+ * of entropy.
+ */
+int lutil_entropy( unsigned char *buf, ber_len_t nbytes )
+{
+ if( nbytes == 0 ) return 0;
+
+#ifdef URANDOM_DEVICE
+#define URANDOM_NREADS 4
+ /* Linux and *BSD offer a urandom device */
+ {
+ int rc, fd, n=0;
+
+ fd = open( URANDOM_DEVICE, O_RDONLY );
+
+ if( fd < 0 ) return -1;
+
+ do {
+ rc = read( fd, buf, nbytes );
+ if( rc <= 0 ) break;
+
+ buf+=rc;
+ nbytes-=rc;
+
+ if( ++n >= URANDOM_NREADS ) break;
+ } while( nbytes > 0 );
+
+ close(fd);
+ return nbytes > 0 ? -1 : 0;
+ }
+#elif defined(PROV_RSA_FULL)
+ {
+ /* Not used since _WIN32_WINNT not set... */
+ HCRYPTPROV hProv = 0;
+
+ /* Get handle to user default provider */
+ if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) {
+ return -1;
+ }
+
+ /* Generate random initialization vector */
+ if(!CryptGenRandom(hProv, (DWORD) nbytes, (BYTE *) buf)) {
+ return -1;
+ }
+
+ /* Release provider handle */
+ if(hProv != 0) CryptReleaseContext(hProv, 0);
+
+ return 0;
+ }
+#else
+ {
+ /* based upon Phil Karn's "practical randomness" idea
+ * but implementation 100% OpenLDAP. So don't blame Phil.
+ *
+ * Worse case is that this is a MD5 hash of a counter, if
+ * MD5 is a strong cryptographic hash, this should be fairly
+ * resistant to attack
+ */
+
+ /*
+ * the caller may need to provide external synchronization OR
+ * provide entropy (in buf) to ensure quality results as
+ * access to this counter may not be atomic.
+ */
+ static int counter = 0;
+ ber_len_t n;
+
+ struct rdata_s {
+ int counter;
+
+ unsigned char *buf;
+ struct rdata_s *stack;
+
+ pid_t pid;
+
+#ifdef HAVE_GETTIMEOFDAY
+ struct timeval tv;
+#else
+ time_t time;
+#endif
+
+ unsigned long junk; /* purposely not initialized */
+ } rdata;
+
+ /* make sure rdata differs for each process */
+ rdata.pid = getpid();
+
+ /* make sure rdata differs for each program */
+ rdata.buf = buf;
+ rdata.stack = &rdata;
+
+ for( n = 0; n < nbytes; n += 16 ) {
+ struct lutil_MD5Context ctx;
+ unsigned char digest[16];
+
+ /* poor resolution */
+#ifdef HAVE_GETTIMEOFDAY
+ (void) gettimeofday( &rdata.tv, NULL );
+#else
+ (void) time( &rdata.time );
+#endif
+
+ /* make sure rdata differs */
+ rdata.counter = ++counter;
+ rdata.pid++;
+ rdata.junk++;
+
+ lutil_MD5Init( &ctx );
+ lutil_MD5Update( &ctx, (unsigned char *) &rdata, sizeof( rdata ) );
+
+ /* allow caller to provided additional entropy */
+ lutil_MD5Update( &ctx, buf, nbytes );
+
+ lutil_MD5Final( digest, &ctx );
+
+ AC_MEMCPY( &buf[n], digest,
+ nbytes - n >= 16 ? 16 : nbytes - n );
+ }
+
+ return 0;
+ }
+#endif
+ return -1;
+}
diff --git a/libraries/liblutil/getopt.c b/libraries/liblutil/getopt.c
new file mode 100644
index 0000000..bc3feba
--- /dev/null
+++ b/libraries/liblutil/getopt.c
@@ -0,0 +1,136 @@
+/* getopt.c -- replacement getopt(3) routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1998-2003 Kurt D. Zeilenga.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* This work is based upon the public-domain getopt(3) routines
+ * developed by AT&T. Modified by Kurt D. Zeilenga for inclusion
+ * into OpenLDAP Software. Significant contributors include:
+ * Howard Chu
+ */
+
+#include "portable.h"
+
+#ifndef HAVE_GETOPT
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+
+#include "lutil.h"
+
+#ifndef STDERR_FILENO
+#define STDERR_FILENO 2
+#endif
+
+int opterr = 1;
+int optind = 1;
+int optopt;
+char * optarg;
+
+#ifdef HAVE_EBCDIC
+extern int _trans_argv;
+#endif
+
+static void ERR (char * const argv[], const char * s, char c)
+{
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: static void ERR () in getopt.c\n");
+#endif
+ if (opterr)
+ {
+ char *ptr, outbuf[4096];
+
+ ptr = lutil_strncopy(outbuf, argv[0], sizeof(outbuf) - 2);
+ ptr = lutil_strncopy(ptr, s, sizeof(outbuf)-2 -(ptr-outbuf));
+ *ptr++ = c;
+ *ptr++ = '\n';
+#ifdef HAVE_EBCDIC
+ __atoe_l(outbuf, ptr - outbuf);
+#endif
+ (void) write(STDERR_FILENO,outbuf,ptr - outbuf);
+ }
+}
+
+int getopt (int argc, char * const argv [], const char * opts)
+{
+ static int sp = 1, error = (int) '?';
+ static char sw = '-', eos = '\0', arg = ':';
+ register char c, * cp;
+
+#ifdef DF_TRACE_DEBUG
+printf("DF_TRACE_DEBUG: int getopt () in getopt.c\n");
+#endif
+
+#ifdef HAVE_EBCDIC
+ if (_trans_argv) {
+ int i;
+ for (i=0; i<argc; i++) __etoa(argv[i]);
+ _trans_argv = 0;
+ }
+#endif
+ if (sp == 1)
+ {
+ if (optind >= argc || argv[optind][0] != sw
+ || argv[optind][1] == eos)
+ return EOF;
+ else if (strcmp(argv[optind],"--") == 0)
+ {
+ optind++;
+ return EOF;
+ }
+ }
+ c = argv[optind][sp];
+ optopt = (int) c;
+ if (c == arg || (cp = strchr(opts,c)) == NULL)
+ {
+ ERR(argv,_(": illegal option--"),c);
+ if (argv[optind][++sp] == eos)
+ {
+ optind++;
+ sp = 1;
+ }
+ return error;
+ }
+ else if (*++cp == arg)
+ {
+ if (argv[optind][sp + 1] != eos)
+ optarg = &argv[optind++][sp + 1];
+ else if (++optind >= argc)
+ {
+ ERR(argv,_(": option requires an argument--"),c);
+ sp = 1;
+ return error;
+ }
+ else
+ optarg = argv[optind++];
+ sp = 1;
+ }
+ else
+ {
+ if (argv[optind][++sp] == eos)
+ {
+ sp = 1;
+ optind++;
+ }
+ optarg = NULL;
+ }
+ return (int) c;
+}
+#endif /* HAVE_GETOPT */
diff --git a/libraries/liblutil/getpass.c b/libraries/liblutil/getpass.c
new file mode 100644
index 0000000..e322723
--- /dev/null
+++ b/libraries/liblutil/getpass.c
@@ -0,0 +1,130 @@
+/* getpass.c -- get password from user */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1998-2003 Kurt D. Zeilenga.
+ * Portions Copyright 2009 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1992, 1993 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+/* This work was originally developed by the University of Michigan
+ * and distributed as part of U-MICH LDAP. It was adapted for use in
+ * -llutil by Kurt D. Zeilenga and subsequently rewritten by Howard Chu.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/signal.h>
+#include <ac/string.h>
+#include <ac/termios.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#ifndef HAVE_GETPASSPHRASE
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_CONIO_H
+#include <conio.h>
+#endif
+
+#include <lber.h>
+#include <ldap.h>
+
+#include "ldap_defaults.h"
+
+#define PBUF 512
+
+#ifdef HAVE_WINSOCK
+#define TTY "con:"
+#else
+#define TTY "/dev/tty"
+#endif
+
+char *
+lutil_getpass( const char *prompt )
+{
+ static char pbuf[PBUF];
+ FILE *fi;
+ int c;
+ unsigned i;
+#if defined(HAVE_TERMIOS_H) || defined(HAVE_SGTTY_H)
+ TERMIO_TYPE ttyb;
+ TERMFLAG_TYPE flags;
+ RETSIGTYPE (*sig)( int sig );
+#endif
+
+ if( prompt == NULL ) prompt = _("Password: ");
+
+#ifdef DEBUG
+ if (debug & D_TRACE)
+ printf("->getpass(%s)\n", prompt);
+#endif
+
+#if defined(HAVE_TERMIOS_H) || defined(HAVE_SGTTY_H)
+ if ((fi = fopen(TTY, "r")) == NULL)
+ fi = stdin;
+ else
+ setbuf(fi, (char *)NULL);
+ if (fi != stdin) {
+ if (GETATTR(fileno(fi), &ttyb) < 0)
+ perror("GETATTR");
+ sig = SIGNAL (SIGINT, SIG_IGN);
+ flags = GETFLAGS( ttyb );
+ SETFLAGS( ttyb, flags & ~ECHO );
+ if (SETATTR(fileno(fi), &ttyb) < 0)
+ perror("SETATTR");
+ }
+#else
+ fi = stdin;
+#endif
+ fprintf(stderr, "%s", prompt);
+ fflush(stderr);
+ i = 0;
+ while ( (c = getc(fi)) != EOF && c != '\n' && c != '\r' )
+ if ( i < (sizeof(pbuf)-1) )
+ pbuf[i++] = c;
+#if defined(HAVE_TERMIOS_H) || defined(HAVE_SGTTY_H)
+ /* tidy up */
+ if (fi != stdin) {
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ SETFLAGS( ttyb, flags );
+ if (SETATTR(fileno(fi), &ttyb) < 0)
+ perror("SETATTR");
+ (void) SIGNAL (SIGINT, sig);
+ (void) fclose(fi);
+ }
+#endif
+ if ( c == EOF )
+ return( NULL );
+ pbuf[i] = '\0';
+ return (pbuf);
+}
+
+#endif /* !NEED_GETPASSPHRASE */
diff --git a/libraries/liblutil/getpeereid.c b/libraries/liblutil/getpeereid.c
new file mode 100644
index 0000000..423fc7e
--- /dev/null
+++ b/libraries/liblutil/getpeereid.c
@@ -0,0 +1,220 @@
+/* getpeereid.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1 /* Needed for glibc struct ucred */
+#endif
+
+#include "portable.h"
+
+#ifndef HAVE_GETPEEREID
+
+#include <sys/types.h>
+#include <ac/unistd.h>
+
+#include <ac/socket.h>
+#include <ac/errno.h>
+
+#ifdef HAVE_GETPEERUCRED
+#include <ucred.h>
+#endif
+
+#ifdef LDAP_PF_LOCAL_SENDMSG
+#include <lber.h>
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+#include <sys/stat.h>
+#endif
+
+#ifdef HAVE_SYS_UCRED_H
+#ifdef HAVE_GRP_H
+#include <grp.h> /* for NGROUPS on Tru64 5.1 */
+#endif
+#include <sys/ucred.h>
+#endif
+
+#include <stdlib.h>
+
+int lutil_getpeereid( int s, uid_t *euid, gid_t *egid
+#ifdef LDAP_PF_LOCAL_SENDMSG
+ , struct berval *peerbv
+#endif
+ )
+{
+#ifdef LDAP_PF_LOCAL
+#if defined( HAVE_GETPEERUCRED )
+ ucred_t *uc = NULL;
+ if( getpeerucred( s, &uc ) == 0 ) {
+ *euid = ucred_geteuid( uc );
+ *egid = ucred_getegid( uc );
+ ucred_free( uc );
+ return 0;
+ }
+
+#elif defined( SO_PEERCRED )
+ struct ucred peercred;
+ ber_socklen_t peercredlen = sizeof peercred;
+
+ if(( getsockopt( s, SOL_SOCKET, SO_PEERCRED,
+ (void *)&peercred, &peercredlen ) == 0 )
+ && ( peercredlen == sizeof peercred ))
+ {
+ *euid = peercred.uid;
+ *egid = peercred.gid;
+ return 0;
+ }
+
+#elif defined( LOCAL_PEERCRED )
+ struct xucred peercred;
+ ber_socklen_t peercredlen = sizeof peercred;
+
+ if(( getsockopt( s, LOCAL_PEERCRED, 1,
+ (void *)&peercred, &peercredlen ) == 0 )
+ && ( peercred.cr_version == XUCRED_VERSION ))
+ {
+ *euid = peercred.cr_uid;
+ *egid = peercred.cr_gid;
+ return 0;
+ }
+#elif defined( LDAP_PF_LOCAL_SENDMSG ) && defined( MSG_WAITALL )
+ int err, fd;
+ struct iovec iov;
+ struct msghdr msg = {0};
+# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+# ifndef CMSG_SPACE
+# define CMSG_SPACE(len) (_CMSG_ALIGN(sizeof(struct cmsghdr)) + _CMSG_ALIGN(len))
+# endif
+# ifndef CMSG_LEN
+# define CMSG_LEN(len) (_CMSG_ALIGN(sizeof(struct cmsghdr)) + (len))
+# endif
+ struct {
+ struct cmsghdr cm;
+ int fd;
+ } control_st;
+ struct cmsghdr *cmsg;
+# endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */
+ struct stat st;
+ struct sockaddr_un lname, rname;
+ ber_socklen_t llen, rlen;
+
+ rlen = sizeof(rname);
+ llen = sizeof(lname);
+ memset( &lname, 0, sizeof( lname ));
+ getsockname(s, (struct sockaddr *)&lname, &llen);
+
+ iov.iov_base = peerbv->bv_val;
+ iov.iov_len = peerbv->bv_len;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ peerbv->bv_len = 0;
+
+# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+ msg.msg_control = &control_st;
+ msg.msg_controllen = sizeof( struct cmsghdr ) + sizeof( int ); /* no padding! */
+
+ cmsg = CMSG_FIRSTHDR( &msg );
+# else
+ msg.msg_accrights = (char *)&fd;
+ msg.msg_accrightslen = sizeof(fd);
+# endif
+
+ /*
+ * AIX returns a bogus file descriptor if recvmsg() is
+ * called with MSG_PEEK (is this a bug?). Hence we need
+ * to receive the Abandon PDU.
+ */
+ err = recvmsg( s, &msg, MSG_WAITALL );
+ if( err >= 0 &&
+# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+ cmsg->cmsg_len == CMSG_LEN( sizeof(int) ) &&
+ cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS
+# else
+ msg.msg_accrightslen == sizeof(int)
+# endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL*/
+ ) {
+ int mode = S_IFIFO|S_ISUID|S_IRWXU;
+
+ /* We must receive a valid descriptor, it must be a pipe,
+ * it must only be accessible by its owner, and it must
+ * have the name of our socket written on it.
+ */
+ peerbv->bv_len = err;
+# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+ fd = (*(int *)CMSG_DATA( cmsg ));
+# endif
+ err = fstat( fd, &st );
+ if ( err == 0 )
+ rlen = read(fd, &rname, rlen);
+ close(fd);
+ if( err == 0 && st.st_mode == mode &&
+ llen == rlen && !memcmp(&lname, &rname, llen))
+ {
+ *euid = st.st_uid;
+ *egid = st.st_gid;
+ return 0;
+ }
+ }
+#elif defined(SOCKCREDSIZE)
+ struct msghdr msg;
+ ber_socklen_t crmsgsize;
+ void *crmsg;
+ struct cmsghdr *cmp;
+ struct sockcred *sc;
+
+ memset(&msg, 0, sizeof msg);
+ crmsgsize = CMSG_SPACE(SOCKCREDSIZE(NGROUPS));
+ if (crmsgsize == 0) goto sc_err;
+ crmsg = malloc(crmsgsize);
+ if (crmsg == NULL) goto sc_err;
+ memset(crmsg, 0, crmsgsize);
+
+ msg.msg_control = crmsg;
+ msg.msg_controllen = crmsgsize;
+
+ if (recvmsg(s, &msg, 0) < 0) {
+ free(crmsg);
+ goto sc_err;
+ }
+
+ if (msg.msg_controllen == 0 || (msg.msg_flags & MSG_CTRUNC) != 0) {
+ free(crmsg);
+ goto sc_err;
+ }
+
+ cmp = CMSG_FIRSTHDR(&msg);
+ if (cmp->cmsg_level != SOL_SOCKET || cmp->cmsg_type != SCM_CREDS) {
+ printf("nocreds\n");
+ goto sc_err;
+ }
+
+ sc = (struct sockcred *)(void *)CMSG_DATA(cmp);
+
+ *euid = sc->sc_euid;
+ *egid = sc->sc_egid;
+
+ free(crmsg);
+ return 0;
+
+sc_err:
+#endif
+#endif /* LDAP_PF_LOCAL */
+
+ return -1;
+}
+
+#endif /* HAVE_GETPEEREID */
diff --git a/libraries/liblutil/hash.c b/libraries/liblutil/hash.c
new file mode 100644
index 0000000..10e56f0
--- /dev/null
+++ b/libraries/liblutil/hash.c
@@ -0,0 +1,141 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2000-2003 Kurt D. Zeilenga.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* This implements the Fowler / Noll / Vo (FNV-1) hash algorithm.
+ * A summary of the algorithm can be found at:
+ * http://www.isthe.com/chongo/tech/comp/fnv/index.html
+ */
+
+#include "portable.h"
+
+#include <lutil_hash.h>
+
+/* offset and prime for 32-bit FNV-1 */
+#define HASH_OFFSET 0x811c9dc5U
+#define HASH_PRIME 16777619
+
+
+/*
+ * Initialize context
+ */
+void
+lutil_HASHInit( lutil_HASH_CTX *ctx )
+{
+ ctx->hash = HASH_OFFSET;
+}
+
+/*
+ * Update hash
+ */
+void
+lutil_HASHUpdate(
+ lutil_HASH_CTX *ctx,
+ const unsigned char *buf,
+ ber_len_t len )
+{
+ const unsigned char *p, *e;
+ ber_uint_t h;
+
+ p = buf;
+ e = &buf[len];
+
+ h = ctx->hash;
+
+ while( p < e ) {
+ h *= HASH_PRIME;
+ h ^= *p++;
+ }
+
+ ctx->hash = h;
+}
+
+/*
+ * Save hash
+ */
+void
+lutil_HASHFinal( unsigned char *digest, lutil_HASH_CTX *ctx )
+{
+ ber_uint_t h = ctx->hash;
+
+ digest[0] = h & 0xffU;
+ digest[1] = (h>>8) & 0xffU;
+ digest[2] = (h>>16) & 0xffU;
+ digest[3] = (h>>24) & 0xffU;
+}
+
+#ifdef HAVE_LONG_LONG
+
+/* 64 bit Fowler/Noll/Vo-O FNV-1a hash code */
+
+#define HASH64_OFFSET 0xcbf29ce484222325ULL
+
+/*
+ * Initialize context
+ */
+void
+lutil_HASH64Init( lutil_HASH_CTX *ctx )
+{
+ ctx->hash64 = HASH64_OFFSET;
+}
+
+/*
+ * Update hash
+ */
+void
+lutil_HASH64Update(
+ lutil_HASH_CTX *ctx,
+ const unsigned char *buf,
+ ber_len_t len )
+{
+ const unsigned char *p, *e;
+ unsigned long long h;
+
+ p = buf;
+ e = &buf[len];
+
+ h = ctx->hash64;
+
+ while( p < e ) {
+ /* xor the bottom with the current octet */
+ h ^= *p++;
+
+ /* multiply by the 64 bit FNV magic prime mod 2^64 */
+ h += (h << 1) + (h << 4) + (h << 5) +
+ (h << 7) + (h << 8) + (h << 40);
+
+ }
+
+ ctx->hash64 = h;
+}
+
+/*
+ * Save hash
+ */
+void
+lutil_HASH64Final( unsigned char *digest, lutil_HASH_CTX *ctx )
+{
+ unsigned long long h = ctx->hash64;
+
+ digest[0] = h & 0xffU;
+ digest[1] = (h>>8) & 0xffU;
+ digest[2] = (h>>16) & 0xffU;
+ digest[3] = (h>>24) & 0xffU;
+ digest[4] = (h>>32) & 0xffU;
+ digest[5] = (h>>40) & 0xffU;
+ digest[6] = (h>>48) & 0xffU;
+ digest[7] = (h>>56) & 0xffU;
+}
+#endif /* HAVE_LONG_LONG */
diff --git a/libraries/liblutil/lockf.c b/libraries/liblutil/lockf.c
new file mode 100644
index 0000000..e939909
--- /dev/null
+++ b/libraries/liblutil/lockf.c
@@ -0,0 +1,118 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * File Locking Routines
+ *
+ * Implementations (in order of preference)
+ * - lockf
+ * - fcntl
+ * - flock
+ *
+ * Other implementations will be added as needed.
+ *
+ * NOTE: lutil_lockf() MUST block until an exclusive lock is acquired.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/unistd.h>
+
+#undef LOCK_API
+
+#if defined(HAVE_LOCKF) && defined(F_LOCK)
+# define USE_LOCKF 1
+# define LOCK_API "lockf"
+#endif
+
+#if !defined(LOCK_API) && defined(HAVE_FCNTL)
+# ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+# endif
+# ifdef F_WRLCK
+# define USE_FCNTL 1
+# define LOCK_API "fcntl"
+# endif
+#endif
+
+#if !defined(LOCK_API) && defined(HAVE_FLOCK)
+# ifdef HAVE_SYS_FILE_H
+# include <sys/file.h>
+# endif
+# define USE_FLOCK 1
+# define LOCK_API "flock"
+#endif
+
+#if !defined(USE_LOCKF) && !defined(USE_FCNTL) && !defined(USE_FLOCK)
+int lutil_lockf ( int fd ) {
+ fd = fd;
+ return 0;
+}
+
+int lutil_unlockf ( int fd ) {
+ fd = fd;
+ return 0;
+}
+#endif
+
+#ifdef USE_LOCKF
+int lutil_lockf ( int fd ) {
+ /* use F_LOCK instead of F_TLOCK, ie: block */
+ return lockf( fd, F_LOCK, 0 );
+}
+
+int lutil_unlockf ( int fd ) {
+ return lockf( fd, F_ULOCK, 0 );
+}
+#endif
+
+#ifdef USE_FCNTL
+int lutil_lockf ( int fd ) {
+ struct flock file_lock;
+
+ memset( &file_lock, '\0', sizeof( file_lock ) );
+ file_lock.l_type = F_WRLCK;
+ file_lock.l_whence = SEEK_SET;
+ file_lock.l_start = 0;
+ file_lock.l_len = 0;
+
+ /* use F_SETLKW instead of F_SETLK, ie: block */
+ return( fcntl( fd, F_SETLKW, &file_lock ) );
+}
+
+int lutil_unlockf ( int fd ) {
+ struct flock file_lock;
+
+ memset( &file_lock, '\0', sizeof( file_lock ) );
+ file_lock.l_type = F_UNLCK;
+ file_lock.l_whence = SEEK_SET;
+ file_lock.l_start = 0;
+ file_lock.l_len = 0;
+
+ return( fcntl ( fd, F_SETLKW, &file_lock ) );
+}
+#endif
+
+#ifdef USE_FLOCK
+int lutil_lockf ( int fd ) {
+ /* use LOCK_EX instead of LOCK_EX|LOCK_NB, ie: block */
+ return flock( fd, LOCK_EX );
+}
+
+int lutil_unlockf ( int fd ) {
+ return flock( fd, LOCK_UN );
+}
+#endif
diff --git a/libraries/liblutil/md5.c b/libraries/liblutil/md5.c
new file mode 100644
index 0000000..c895cb7
--- /dev/null
+++ b/libraries/liblutil/md5.c
@@ -0,0 +1,332 @@
+/* md5.c -- MD5 message-digest algorithm */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* This work was adapted for inclusion in OpenLDAP Software by
+ * Kurt D. Zeilenga based upon code developed by Colin Plumb
+ * and subsequently modified by Jim Kingdon.
+ */
+
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/* This code was modified in 1997 by Jim Kingdon of Cyclic Software to
+ not require an integer type which is exactly 32 bits. This work
+ draws on the changes for the same purpose by Tatu Ylonen
+ <ylo@cs.hut.fi> as part of SSH, but since I didn't actually use
+ that code, there is no copyright issue. I hereby disclaim
+ copyright in any changes I have made; this code remains in the
+ public domain. */
+
+#include "portable.h"
+
+#include <ac/string.h>
+
+/* include socket.h to get sys/types.h and/or winsock2.h */
+#include <ac/socket.h>
+
+#include <lutil_md5.h>
+
+/* Little-endian byte-swapping routines. Note that these do not
+ depend on the size of datatypes such as ber_uint_t, nor do they require
+ us to detect the endianness of the machine we are running on. It
+ is possible they should be macros for speed, but I would be
+ surprised if they were a performance bottleneck for MD5. */
+
+static ber_uint_t
+getu32( const unsigned char *addr )
+{
+ return (((((unsigned long)addr[3] << 8) | addr[2]) << 8)
+ | addr[1]) << 8 | addr[0];
+}
+
+static void
+putu32( ber_uint_t data, unsigned char *addr )
+{
+ addr[0] = (unsigned char)data;
+ addr[1] = (unsigned char)(data >> 8);
+ addr[2] = (unsigned char)(data >> 16);
+ addr[3] = (unsigned char)(data >> 24);
+}
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void
+lutil_MD5Init( struct lutil_MD5Context *ctx )
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void
+lutil_MD5Update(
+ struct lutil_MD5Context *ctx,
+ const unsigned char *buf,
+ ber_len_t len
+)
+{
+ ber_uint_t t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = (t + ((ber_uint_t)len << 3)) & 0xffffffff) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if ( t ) {
+ unsigned char *p = ctx->in + t;
+
+ t = 64-t;
+ if (len < t) {
+ AC_MEMCPY(p, buf, len);
+ return;
+ }
+ AC_MEMCPY(p, buf, t);
+ lutil_MD5Transform(ctx->buf, ctx->in);
+ buf += t;
+ len -= t;
+ }
+
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ AC_MEMCPY(ctx->in, buf, 64);
+ lutil_MD5Transform(ctx->buf, ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ AC_MEMCPY(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void
+lutil_MD5Final( unsigned char *digest, struct lutil_MD5Context *ctx )
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, '\0', count);
+ lutil_MD5Transform(ctx->buf, ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, '\0', 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, '\0', count-8);
+ }
+
+ /* Append length in bits and transform */
+ putu32(ctx->bits[0], ctx->in + 56);
+ putu32(ctx->bits[1], ctx->in + 60);
+
+ lutil_MD5Transform(ctx->buf, ctx->in);
+ putu32(ctx->buf[0], digest);
+ putu32(ctx->buf[1], digest + 4);
+ putu32(ctx->buf[2], digest + 8);
+ putu32(ctx->buf[3], digest + 12);
+ memset(ctx, '\0', sizeof(*ctx)); /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w &= 0xffffffff, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void
+lutil_MD5Transform( ber_uint_t *buf, const unsigned char *inraw )
+{
+ register ber_uint_t a, b, c, d;
+ ber_uint_t in[16];
+ int i;
+
+ for (i = 0; i < 16; ++i)
+ in[i] = getu32 (inraw + 4 * i);
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+#endif
+
+#ifdef TEST
+/* Simple test program. Can use it to manually run the tests from
+ RFC1321 for example. */
+#include <stdio.h>
+
+int
+main (int argc, char **argv )
+{
+ struct lutil_MD5Context context;
+ unsigned char checksum[LUTIL_MD5_BYTES];
+ int i;
+ int j;
+
+ if (argc < 2)
+ {
+ fprintf (stderr, "usage: %s string-to-hash\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ for (j = 1; j < argc; ++j)
+ {
+ printf ("MD5 (\"%s\") = ", argv[j]);
+ lutil_MD5Init (&context);
+ lutil_MD5Update (&context, argv[j], strlen (argv[j]));
+ lutil_MD5Final (checksum, &context);
+ for (i = 0; i < LUTIL_MD5_BYTES; i++)
+ {
+ printf ("%02x", (unsigned int) checksum[i]);
+ }
+ printf ("\n");
+ }
+ return EXIT_SUCCESS;
+}
+#endif /* TEST */
diff --git a/libraries/liblutil/memcmp.c b/libraries/liblutil/memcmp.c
new file mode 100644
index 0000000..8068de3
--- /dev/null
+++ b/libraries/liblutil/memcmp.c
@@ -0,0 +1,33 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <ac/string.h>
+
+/*
+ * Memory Compare
+ */
+int
+(lutil_memcmp)(const void *v1, const void *v2, size_t n)
+{
+ if (n != 0) {
+ const unsigned char *s1=v1, *s2=v2;
+ do {
+ if (*s1++ != *s2++) return *--s1 - *--s2;
+ } while (--n != 0);
+ }
+ return 0;
+}
diff --git a/libraries/liblutil/meter.c b/libraries/liblutil/meter.c
new file mode 100644
index 0000000..8ac592f
--- /dev/null
+++ b/libraries/liblutil/meter.c
@@ -0,0 +1,386 @@
+/* meter.c - lutil_meter meters */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright (c) 2009 by Emily Backes, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Emily Backes for inclusion
+ * in OpenLDAP software.
+ */
+
+#include "portable.h"
+#include "lutil_meter.h"
+
+#include <ac/assert.h>
+#include <ac/string.h>
+
+int
+lutil_time_string (
+ char *dest,
+ int duration,
+ int max_terms)
+{
+ static const int time_div[] = {31556952,
+ 604800,
+ 86400,
+ 3600,
+ 60,
+ 1,
+ 0};
+ const int * time_divp = time_div;
+ static const char * time_name_ch = "ywdhms";
+ const char * time_name_chp = time_name_ch;
+ int term_count = 0;
+ char *buf = dest;
+ int time_quot;
+
+ assert ( max_terms >= 2 ); /* room for "none" message */
+
+ if ( duration < 0 ) {
+ *dest = '\0';
+ return 1;
+ }
+ if ( duration == 0 ) {
+ strcpy( dest, "none" );
+ return 0;
+ }
+ while ( term_count < max_terms && duration > 0 ) {
+ if (duration > *time_divp) {
+ time_quot = duration / *time_divp;
+ duration %= *time_divp;
+ if (time_quot > 99) {
+ return 1;
+ } else {
+ *(buf++) = time_quot / 10 + '0';
+ *(buf++) = time_quot % 10 + '0';
+ *(buf++) = *time_name_chp;
+ ++term_count;
+ }
+ }
+ if ( *(++time_divp) == 0) duration = 0;
+ ++time_name_chp;
+ }
+ *buf = '\0';
+ return 0;
+}
+
+int
+lutil_get_now (double *now)
+{
+#ifdef HAVE_GETTIMEOFDAY
+ struct timeval tv;
+
+ assert( now );
+ gettimeofday( &tv, NULL );
+ *now = ((double) tv.tv_sec) + (((double) tv.tv_usec) / 1000000.0);
+ return 0;
+#else
+ time_t tm;
+
+ assert( now );
+ time( &tm );
+ *now = (double) tm;
+ return 0;
+#endif
+}
+
+int
+lutil_meter_open (
+ lutil_meter_t *meter,
+ const lutil_meter_display_t *display,
+ const lutil_meter_estimator_t *estimator,
+ size_t goal_value)
+{
+ int rc;
+
+ assert( meter != NULL );
+ assert( display != NULL );
+ assert( estimator != NULL );
+
+ if (goal_value < 1) return -1;
+
+ memset( (void*) meter, 0, sizeof( lutil_meter_t ));
+ meter->display = display;
+ meter->estimator = estimator;
+ lutil_get_now( &meter->start_time );
+ meter->last_update = meter->start_time;
+ meter->goal_value = goal_value;
+ meter->last_position = 0;
+
+ rc = meter->display->display_open( &meter->display_data );
+ if( rc != 0 ) return rc;
+
+ rc = meter->estimator->estimator_open( &meter->estimator_data );
+ if( rc != 0 ) {
+ meter->display->display_close( &meter->display_data );
+ return rc;
+ }
+
+ return 0;
+}
+
+int
+lutil_meter_update (
+ lutil_meter_t *meter,
+ size_t position,
+ int force)
+{
+ static const double display_rate = 0.5;
+ double frac, cycle_length, speed, now;
+ time_t remaining_time, elapsed;
+ int rc;
+
+ assert( meter != NULL );
+
+ lutil_get_now( &now );
+
+ if ( !force && now - meter->last_update < display_rate ) return 0;
+
+ frac = ((double)position) / ((double) meter->goal_value);
+ elapsed = now - meter->start_time;
+ if (frac <= 0.0 || elapsed == 0) return 0;
+ if (frac >= 1.0) {
+ rc = meter->display->display_update(
+ &meter->display_data,
+ 1.0,
+ 0,
+ (time_t) elapsed,
+ ((double)position) / elapsed);
+ } else {
+ rc = meter->estimator->estimator_update(
+ &meter->estimator_data,
+ meter->start_time,
+ frac,
+ &remaining_time );
+ if ( rc == 0 ) {
+ cycle_length = now - meter->last_update;
+ speed = cycle_length > 0.0 ?
+ ((double)(position - meter->last_position))
+ / cycle_length :
+ 0.0;
+ rc = meter->display->display_update(
+ &meter->display_data,
+ frac,
+ remaining_time,
+ (time_t) elapsed,
+ speed);
+ if ( rc == 0 ) {
+ meter->last_update = now;
+ meter->last_position = position;
+ }
+ }
+ }
+
+ return rc;
+}
+
+int
+lutil_meter_close (lutil_meter_t *meter)
+{
+ meter->estimator->estimator_close( &meter->estimator_data );
+ meter->display->display_close( &meter->display_data );
+
+ return 0;
+}
+
+/* Default display and estimator */
+typedef struct {
+ int buffer_length;
+ char * buffer;
+ int need_eol;
+ int phase;
+ FILE *output;
+} text_display_state_t;
+
+static int
+text_open (void ** display_datap)
+{
+ static const int default_buffer_length = 81;
+ text_display_state_t *data;
+
+ assert( display_datap != NULL );
+ data = calloc( 1, sizeof( text_display_state_t ));
+ assert( data != NULL );
+ data->buffer_length = default_buffer_length;
+ data->buffer = calloc( 1, default_buffer_length );
+ assert( data->buffer != NULL );
+ data->output = stderr;
+ *display_datap = data;
+ return 0;
+}
+
+static int
+text_update (
+ void **display_datap,
+ double frac,
+ time_t remaining_time,
+ time_t elapsed,
+ double byte_rate)
+{
+ text_display_state_t *data;
+ char *buf, *buf_end;
+
+ assert( display_datap != NULL );
+ assert( *display_datap != NULL );
+ data = (text_display_state_t*) *display_datap;
+
+ if ( data->output == NULL ) return 1;
+
+ buf = data->buffer;
+ buf_end = buf + data->buffer_length - 1;
+
+/* |#################### 100.00% eta 1d19h elapsed 23w 7d23h15m12s spd nnnn.n M/s */
+
+ {
+ /* spinner */
+ static const int phase_mod = 8;
+ static const char phase_char[] = "_.-*\"*-.";
+ *buf++ = phase_char[data->phase % phase_mod];
+ data->phase++;
+ }
+
+ {
+ /* bar */
+ static const int bar_length = 20;
+ static const double bar_lengthd = 20.0;
+ static const char fill_char = '#';
+ static const char blank_char = ' ';
+ char *bar_end = buf + bar_length;
+ char *bar_pos = frac < 0.0 ?
+ buf :
+ frac < 1.0 ?
+ buf + (int) (bar_lengthd * frac) :
+ bar_end;
+
+ assert( (buf_end - buf) > bar_length );
+ while ( buf < bar_end ) {
+ *buf = buf < bar_pos ?
+ fill_char : blank_char;
+ ++buf;
+ }
+ }
+
+ {
+ /* percent */
+ (void) snprintf( buf, buf_end-buf, "%7.2f%%", 100.0*frac );
+ buf += 8;
+ }
+
+ {
+ /* eta and elapsed */
+ char time_buffer[19];
+ int rc;
+ rc = lutil_time_string( time_buffer, remaining_time, 2);
+ if (rc == 0)
+ snprintf( buf, buf_end-buf, " eta %6s", time_buffer );
+ buf += 5+6;
+ rc = lutil_time_string( time_buffer, elapsed, 5);
+ if (rc == 0)
+ snprintf( buf, buf_end-buf, " elapsed %15s",
+ time_buffer );
+ buf += 9+15;
+ }
+
+ {
+ /* speed */
+ static const char prefixes[] = " kMGTPEZY";
+ const char *prefix_chp = prefixes;
+
+ while (*prefix_chp && byte_rate >= 1024.0) {
+ byte_rate /= 1024.0;
+ ++prefix_chp;
+ }
+ if ( byte_rate >= 1024.0 ) {
+ snprintf( buf, buf_end-buf, " fast!" );
+ buf += 6;
+ } else {
+ snprintf( buf, buf_end-buf, " spd %5.1f %c/s",
+ byte_rate,
+ *prefix_chp);
+ buf += 5+6+4;
+ }
+ }
+
+ (void) fprintf( data->output,
+ "\r%-79s",
+ data->buffer );
+ data->need_eol = 1;
+ return 0;
+}
+
+static int
+text_close (void ** display_datap)
+{
+ text_display_state_t *data;
+
+ if (display_datap) {
+ if (*display_datap) {
+ data = (text_display_state_t*) *display_datap;
+ if (data->output && data->need_eol)
+ fputs ("\n", data->output);
+ if (data->buffer)
+ free( data->buffer );
+ free( data );
+ }
+ *display_datap = NULL;
+ }
+ return 0;
+}
+
+static int
+null_open_close (void **datap)
+{
+ assert( datap );
+ *datap = NULL;
+ return 0;
+}
+
+static int
+linear_update (
+ void **estimator_datap,
+ double start,
+ double frac,
+ time_t *remaining)
+{
+ double now;
+ double elapsed;
+
+ assert( estimator_datap != NULL );
+ assert( *estimator_datap == NULL );
+ assert( start > 0.0 );
+ assert( frac >= 0.0 );
+ assert( frac <= 1.0 );
+ assert( remaining != NULL );
+ lutil_get_now( &now );
+
+ elapsed = now-start;
+ assert( elapsed >= 0.0 );
+
+ if ( frac == 0.0 ) {
+ return 1;
+ } else if ( frac >= 1.0 ) {
+ *remaining = 0;
+ return 0;
+ } else {
+ *remaining = (time_t) (elapsed/frac-elapsed+0.5);
+ return 0;
+ }
+}
+
+const lutil_meter_display_t lutil_meter_text_display = {
+ text_open, text_update, text_close
+};
+
+const lutil_meter_estimator_t lutil_meter_linear_estimator = {
+ null_open_close, linear_update, null_open_close
+};
diff --git a/libraries/liblutil/ntservice.c b/libraries/liblutil/ntservice.c
new file mode 100644
index 0000000..debc1c3
--- /dev/null
+++ b/libraries/liblutil/ntservice.c
@@ -0,0 +1,509 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * NT Service manager utilities for OpenLDAP services
+ */
+
+#include "portable.h"
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+
+#include <ac/stdlib.h>
+#include <ac/string.h>
+
+#include <stdio.h>
+
+#include <windows.h>
+#include <winsvc.h>
+
+#include <ldap.h>
+
+#include "ldap_pvt_thread.h"
+
+#include "ldap_defaults.h"
+
+#include "slapdmsg.h"
+
+#define SCM_NOTIFICATION_INTERVAL 5000
+#define THIRTY_SECONDS (30 * 1000)
+
+int is_NT_Service; /* is this is an NT service? */
+
+SERVICE_STATUS lutil_ServiceStatus;
+SERVICE_STATUS_HANDLE hlutil_ServiceStatus;
+
+ldap_pvt_thread_cond_t started_event, stopped_event;
+ldap_pvt_thread_t start_status_tid, stop_status_tid;
+
+void (*stopfunc)(int);
+
+static char *GetLastErrorString( void );
+
+int lutil_srv_install(LPCTSTR lpszServiceName, LPCTSTR lpszDisplayName,
+ LPCTSTR lpszBinaryPathName, int auto_start)
+{
+ HKEY hKey;
+ DWORD dwValue, dwDisposition;
+ SC_HANDLE schSCManager, schService;
+ char *sp = strrchr( lpszBinaryPathName, '\\');
+
+ if ( sp ) sp = strchr(sp, ' ');
+ if ( sp ) *sp = '\0';
+ fprintf( stderr, "The install path is %s.\n", lpszBinaryPathName );
+ if ( sp ) *sp = ' ';
+ if ((schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT|SC_MANAGER_CREATE_SERVICE ) ) != NULL )
+ {
+ if ((schService = CreateService(
+ schSCManager,
+ lpszServiceName,
+ lpszDisplayName,
+ SERVICE_ALL_ACCESS,
+ SERVICE_WIN32_OWN_PROCESS,
+ auto_start ? SERVICE_AUTO_START : SERVICE_DEMAND_START,
+ SERVICE_ERROR_NORMAL,
+ lpszBinaryPathName,
+ NULL, NULL, NULL, NULL, NULL)) != NULL)
+ {
+ char regpath[132];
+ CloseServiceHandle(schService);
+ CloseServiceHandle(schSCManager);
+
+ snprintf( regpath, sizeof regpath,
+ "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s",
+ lpszServiceName );
+ /* Create the registry key for event logging to the Windows NT event log. */
+ if ( RegCreateKeyEx(HKEY_LOCAL_MACHINE,
+ regpath, 0,
+ "REG_SZ", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
+ &dwDisposition) != ERROR_SUCCESS)
+ {
+ fprintf( stderr, "RegCreateKeyEx() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
+ RegCloseKey(hKey);
+ return(0);
+ }
+ if ( sp ) *sp = '\0';
+ if ( RegSetValueEx(hKey, "EventMessageFile", 0, REG_EXPAND_SZ, lpszBinaryPathName, strlen(lpszBinaryPathName) + 1) != ERROR_SUCCESS)
+ {
+ fprintf( stderr, "RegSetValueEx(EventMessageFile) failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
+ RegCloseKey(hKey);
+ return(0);
+ }
+
+ dwValue = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE;
+ if ( RegSetValueEx(hKey, "TypesSupported", 0, REG_DWORD, (LPBYTE) &dwValue, sizeof(DWORD)) != ERROR_SUCCESS)
+ {
+ fprintf( stderr, "RegCreateKeyEx(TypesSupported) failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
+ RegCloseKey(hKey);
+ return(0);
+ }
+ RegCloseKey(hKey);
+ return(1);
+ }
+ else
+ {
+ fprintf( stderr, "CreateService() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
+ CloseServiceHandle(schSCManager);
+ return(0);
+ }
+ }
+ else
+ fprintf( stderr, "OpenSCManager() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
+ return(0);
+}
+
+
+int lutil_srv_remove(LPCTSTR lpszServiceName, LPCTSTR lpszBinaryPathName)
+{
+ SC_HANDLE schSCManager, schService;
+
+ fprintf( stderr, "The installed path is %s.\n", lpszBinaryPathName );
+ if ((schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT|SC_MANAGER_CREATE_SERVICE)) != NULL )
+ {
+ if ((schService = OpenService(schSCManager, lpszServiceName, DELETE)) != NULL)
+ {
+ if ( DeleteService(schService) == TRUE)
+ {
+ CloseServiceHandle(schService);
+ CloseServiceHandle(schSCManager);
+ return(1);
+ } else {
+ fprintf( stderr, "DeleteService() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
+ fprintf( stderr, "The %s service has not been removed.\n", lpszBinaryPathName);
+ CloseServiceHandle(schService);
+ CloseServiceHandle(schSCManager);
+ return(0);
+ }
+ } else {
+ fprintf( stderr, "OpenService() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
+ CloseServiceHandle(schSCManager);
+ return(0);
+ }
+ }
+ else
+ fprintf( stderr, "OpenSCManager() failed. GetLastError=%lu (%s)\n", GetLastError(), GetLastErrorString() );
+ return(0);
+}
+
+
+#if 0 /* unused */
+DWORD
+svc_installed (LPTSTR lpszServiceName, LPTSTR lpszBinaryPathName)
+{
+ char buf[256];
+ HKEY key;
+ DWORD rc;
+ DWORD type;
+ long len;
+
+ strcpy(buf, TEXT("SYSTEM\\CurrentControlSet\\Services\\"));
+ strcat(buf, lpszServiceName);
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, buf, 0, KEY_QUERY_VALUE, &key) != ERROR_SUCCESS)
+ return(-1);
+
+ rc = 0;
+ if (lpszBinaryPathName) {
+ len = sizeof(buf);
+ if (RegQueryValueEx(key, "ImagePath", NULL, &type, buf, &len) == ERROR_SUCCESS) {
+ if (strcmp(lpszBinaryPathName, buf))
+ rc = -1;
+ }
+ }
+ RegCloseKey(key);
+ return(rc);
+}
+
+
+DWORD
+svc_running (LPTSTR lpszServiceName)
+{
+ SC_HANDLE service;
+ SC_HANDLE scm;
+ DWORD rc;
+ SERVICE_STATUS ss;
+
+ if (!(scm = OpenSCManager(NULL, NULL, GENERIC_READ)))
+ return(GetLastError());
+
+ rc = 1;
+ service = OpenService(scm, lpszServiceName, SERVICE_QUERY_STATUS);
+ if (service) {
+ if (!QueryServiceStatus(service, &ss))
+ rc = GetLastError();
+ else if (ss.dwCurrentState != SERVICE_STOPPED)
+ rc = 0;
+ CloseServiceHandle(service);
+ }
+ CloseServiceHandle(scm);
+ return(rc);
+}
+#endif
+
+static void *start_status_routine( void *ptr )
+{
+ DWORD wait_result;
+ int done = 0;
+
+ while ( !done )
+ {
+ wait_result = WaitForSingleObject( started_event, SCM_NOTIFICATION_INTERVAL );
+ switch ( wait_result )
+ {
+ case WAIT_ABANDONED:
+ case WAIT_OBJECT_0:
+ /* the object that we were waiting for has been destroyed (ABANDONED) or
+ * signalled (TIMEOUT_0). We can assume that the startup process is
+ * complete and tell the Service Control Manager that we are now runnng */
+ lutil_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
+ lutil_ServiceStatus.dwWin32ExitCode = NO_ERROR;
+ lutil_ServiceStatus.dwCheckPoint++;
+ lutil_ServiceStatus.dwWaitHint = 1000;
+ SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
+ done = 1;
+ break;
+ case WAIT_TIMEOUT:
+ /* We've waited for the required time, so send an update to the Service Control
+ * Manager saying to wait again. */
+ lutil_ServiceStatus.dwCheckPoint++;
+ lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2;
+ SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
+ break;
+ case WAIT_FAILED:
+ /* there's been some problem with WaitForSingleObject so tell the Service
+ * Control Manager to wait 30 seconds before deploying its assassin and
+ * then leave the thread. */
+ lutil_ServiceStatus.dwCheckPoint++;
+ lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS;
+ SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
+ done = 1;
+ break;
+ }
+ }
+ ldap_pvt_thread_exit(NULL);
+ return NULL;
+}
+
+
+
+static void *stop_status_routine( void *ptr )
+{
+ DWORD wait_result;
+ int done = 0;
+
+ while ( !done )
+ {
+ wait_result = WaitForSingleObject( stopped_event, SCM_NOTIFICATION_INTERVAL );
+ switch ( wait_result )
+ {
+ case WAIT_ABANDONED:
+ case WAIT_OBJECT_0:
+ /* the object that we were waiting for has been destroyed (ABANDONED) or
+ * signalled (TIMEOUT_0). The shutting down process is therefore complete
+ * and the final SERVICE_STOPPED message will be sent to the service control
+ * manager prior to the process terminating. */
+ done = 1;
+ break;
+ case WAIT_TIMEOUT:
+ /* We've waited for the required time, so send an update to the Service Control
+ * Manager saying to wait again. */
+ lutil_ServiceStatus.dwCheckPoint++;
+ lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2;
+ SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
+ break;
+ case WAIT_FAILED:
+ /* there's been some problem with WaitForSingleObject so tell the Service
+ * Control Manager to wait 30 seconds before deploying its assassin and
+ * then leave the thread. */
+ lutil_ServiceStatus.dwCheckPoint++;
+ lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS;
+ SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
+ done = 1;
+ break;
+ }
+ }
+ ldap_pvt_thread_exit(NULL);
+ return NULL;
+}
+
+
+
+static void WINAPI lutil_ServiceCtrlHandler( IN DWORD Opcode)
+{
+ switch (Opcode)
+ {
+ case SERVICE_CONTROL_STOP:
+ case SERVICE_CONTROL_SHUTDOWN:
+
+ lutil_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ lutil_ServiceStatus.dwCheckPoint++;
+ lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2;
+ SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
+
+ ldap_pvt_thread_cond_init( &stopped_event );
+ if ( stopped_event == NULL )
+ {
+ /* the event was not created. We will ask the service control manager for 30
+ * seconds to shutdown */
+ lutil_ServiceStatus.dwCheckPoint++;
+ lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS;
+ SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
+ }
+ else
+ {
+ /* start a thread to report the progress to the service control manager
+ * until the stopped_event is fired. */
+ if ( ldap_pvt_thread_create( &stop_status_tid, 0, stop_status_routine, NULL ) == 0 )
+ {
+
+ }
+ else {
+ /* failed to create the thread that tells the Service Control Manager that the
+ * service stopping is proceeding.
+ * tell the Service Control Manager to wait another 30 seconds before deploying its
+ * assassin. */
+ lutil_ServiceStatus.dwCheckPoint++;
+ lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS;
+ SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
+ }
+ }
+ stopfunc( -1 );
+ break;
+
+ case SERVICE_CONTROL_INTERROGATE:
+ SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
+ break;
+ }
+ return;
+}
+
+void *lutil_getRegParam( char *svc, char *value )
+{
+ HKEY hkey;
+ char path[255];
+ DWORD vType;
+ static char vValue[1024];
+ DWORD valLen = sizeof( vValue );
+
+ if ( svc != NULL )
+ snprintf ( path, sizeof path, "SOFTWARE\\%s", svc );
+ else
+ snprintf ( path, sizeof path, "SOFTWARE\\OpenLDAP\\Parameters" );
+
+ if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, path, 0, KEY_READ, &hkey ) != ERROR_SUCCESS )
+ {
+ return NULL;
+ }
+
+ if ( RegQueryValueEx( hkey, value, NULL, &vType, vValue, &valLen ) != ERROR_SUCCESS )
+ {
+ RegCloseKey( hkey );
+ return NULL;
+ }
+ RegCloseKey( hkey );
+
+ switch ( vType )
+ {
+ case REG_BINARY:
+ case REG_DWORD:
+ return (void*)&vValue;
+ case REG_SZ:
+ return (void*)&vValue;
+ }
+ return (void*)NULL;
+}
+
+void lutil_LogStartedEvent( char *svc, int slap_debug, char *configfile, char *urls )
+{
+ char *Inserts[5];
+ WORD i = 0, j;
+ HANDLE hEventLog;
+
+ hEventLog = RegisterEventSource( NULL, svc );
+
+ Inserts[i] = (char *)malloc( 20 );
+ itoa( slap_debug, Inserts[i++], 10 );
+ Inserts[i++] = strdup( configfile );
+ Inserts[i++] = strdup( urls ? urls : "ldap:///" );
+
+ ReportEvent( hEventLog, EVENTLOG_INFORMATION_TYPE, 0,
+ MSG_SVC_STARTED, NULL, i, 0, (LPCSTR *) Inserts, NULL );
+
+ for ( j = 0; j < i; j++ )
+ ldap_memfree( Inserts[j] );
+ DeregisterEventSource( hEventLog );
+}
+
+
+
+void lutil_LogStoppedEvent( char *svc )
+{
+ HANDLE hEventLog;
+
+ hEventLog = RegisterEventSource( NULL, svc );
+ ReportEvent( hEventLog, EVENTLOG_INFORMATION_TYPE, 0,
+ MSG_SVC_STOPPED, NULL, 0, 0, NULL, NULL );
+ DeregisterEventSource( hEventLog );
+}
+
+
+void lutil_CommenceStartupProcessing( char *lpszServiceName,
+ void (*stopper)(int) )
+{
+ hlutil_ServiceStatus = RegisterServiceCtrlHandler( lpszServiceName, (LPHANDLER_FUNCTION)lutil_ServiceCtrlHandler);
+
+ stopfunc = stopper;
+
+ /* initialize the Service Status structure */
+ lutil_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ lutil_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
+ lutil_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
+ lutil_ServiceStatus.dwWin32ExitCode = NO_ERROR;
+ lutil_ServiceStatus.dwServiceSpecificExitCode = 0;
+ lutil_ServiceStatus.dwCheckPoint = 1;
+ lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL * 2;
+
+ SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
+
+ /* start up a thread to keep sending SERVICE_START_PENDING to the Service Control Manager
+ * until the slapd listener is completed and listening. Only then should we send
+ * SERVICE_RUNNING to the Service Control Manager. */
+ ldap_pvt_thread_cond_init( &started_event );
+ if ( started_event == NULL)
+ {
+ /* failed to create the event to determine when the startup process is complete so
+ * tell the Service Control Manager to wait another 30 seconds before deploying its
+ * assassin */
+ lutil_ServiceStatus.dwCheckPoint++;
+ lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS;
+ SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
+ }
+ else
+ {
+ /* start a thread to report the progress to the service control manager
+ * until the started_event is fired. */
+ if ( ldap_pvt_thread_create( &start_status_tid, 0, start_status_routine, NULL ) == 0 )
+ {
+
+ }
+ else {
+ /* failed to create the thread that tells the Service Control Manager that the
+ * service startup is proceeding.
+ * tell the Service Control Manager to wait another 30 seconds before deploying its
+ * assassin. */
+ lutil_ServiceStatus.dwCheckPoint++;
+ lutil_ServiceStatus.dwWaitHint = THIRTY_SECONDS;
+ SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
+ }
+ }
+}
+
+void lutil_ReportShutdownComplete( )
+{
+ if ( is_NT_Service )
+ {
+ /* stop sending SERVICE_STOP_PENDING messages to the Service Control Manager */
+ ldap_pvt_thread_cond_signal( &stopped_event );
+ ldap_pvt_thread_cond_destroy( &stopped_event );
+
+ /* wait for the thread sending the SERVICE_STOP_PENDING messages to the Service Control Manager to die.
+ * if the wait fails then put ourselves to sleep for half the Service Control Manager update interval */
+ if (ldap_pvt_thread_join( stop_status_tid, (void *) NULL ) == -1)
+ ldap_pvt_thread_sleep( SCM_NOTIFICATION_INTERVAL / 2 );
+
+ lutil_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
+ lutil_ServiceStatus.dwCheckPoint++;
+ lutil_ServiceStatus.dwWaitHint = SCM_NOTIFICATION_INTERVAL;
+ SetServiceStatus(hlutil_ServiceStatus, &lutil_ServiceStatus);
+ }
+}
+
+static char *GetErrorString( int err )
+{
+ static char msgBuf[1024];
+
+ FormatMessage(
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ msgBuf, 1024, NULL );
+
+ return msgBuf;
+}
+
+static char *GetLastErrorString( void )
+{
+ return GetErrorString( GetLastError() );
+}
+#endif
diff --git a/libraries/liblutil/passfile.c b/libraries/liblutil/passfile.c
new file mode 100644
index 0000000..666b718
--- /dev/null
+++ b/libraries/liblutil/passfile.c
@@ -0,0 +1,110 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/ctype.h>
+#include <ac/string.h>
+
+#ifdef HAVE_FSTAT
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif /* HAVE_FSTAT */
+
+#include <lber.h>
+#include <lutil.h>
+
+/* Get a password from a file. */
+int
+lutil_get_filed_password(
+ const char *filename,
+ struct berval *passwd )
+{
+ size_t nread, nleft, nr;
+ FILE *f = fopen( filename, "r" );
+
+ if( f == NULL ) {
+ perror( filename );
+ return -1;
+ }
+
+ passwd->bv_val = NULL;
+ passwd->bv_len = 4096;
+
+#ifdef HAVE_FSTAT
+ {
+ struct stat sb;
+ if ( fstat( fileno( f ), &sb ) == 0 ) {
+ if( sb.st_mode & 006 ) {
+ fprintf( stderr, _("Warning: Password file %s"
+ " is publicly readable/writeable\n"),
+ filename );
+ }
+
+ if ( sb.st_size )
+ passwd->bv_len = sb.st_size;
+ }
+ }
+#endif /* HAVE_FSTAT */
+
+ passwd->bv_val = (char *) ber_memalloc( passwd->bv_len + 1 );
+ if( passwd->bv_val == NULL ) {
+ perror( filename );
+ fclose( f );
+ return -1;
+ }
+
+ nread = 0;
+ nleft = passwd->bv_len;
+ do {
+ if( nleft == 0 ) {
+ /* double the buffer size */
+ char *p = (char *) ber_memrealloc( passwd->bv_val,
+ 2 * passwd->bv_len + 1 );
+ if( p == NULL ) {
+ ber_memfree( passwd->bv_val );
+ passwd->bv_val = NULL;
+ passwd->bv_len = 0;
+ fclose( f );
+ return -1;
+ }
+ nleft = passwd->bv_len;
+ passwd->bv_len *= 2;
+ passwd->bv_val = p;
+ }
+
+ nr = fread( &passwd->bv_val[nread], 1, nleft, f );
+
+ if( nr < nleft && ferror( f ) ) {
+ ber_memfree( passwd->bv_val );
+ passwd->bv_val = NULL;
+ passwd->bv_len = 0;
+ fclose( f );
+ return -1;
+ }
+
+ nread += nr;
+ nleft -= nr;
+ } while ( !feof(f) );
+
+ passwd->bv_len = nread;
+ passwd->bv_val[nread] = '\0';
+
+ fclose( f );
+ return 0;
+}
diff --git a/libraries/liblutil/passwd.c b/libraries/liblutil/passwd.c
new file mode 100644
index 0000000..653cb5a
--- /dev/null
+++ b/libraries/liblutil/passwd.c
@@ -0,0 +1,935 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * int lutil_passwd(
+ * const struct berval *passwd,
+ * const struct berval *cred,
+ * const char **schemes )
+ *
+ * Returns true if user supplied credentials (cred) matches
+ * the stored password (passwd).
+ *
+ * Due to the use of the crypt(3) function
+ * this routine is NOT thread-safe.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+#include <ac/param.h>
+
+#ifdef SLAPD_CRYPT
+# include <ac/crypt.h>
+
+# if defined( HAVE_GETPWNAM ) && defined( HAVE_STRUCT_PASSWD_PW_PASSWD )
+# ifdef HAVE_SHADOW_H
+# include <shadow.h>
+# endif
+# ifdef HAVE_PWD_H
+# include <pwd.h>
+# endif
+# ifdef HAVE_AIX_SECURITY
+# include <userpw.h>
+# endif
+# endif
+#endif
+
+#include <lber.h>
+
+#include "ldap_pvt.h"
+#include "lber_pvt.h"
+
+#include "lutil_md5.h"
+#include "lutil_sha1.h"
+#include "lutil.h"
+
+static const unsigned char crypt64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890./";
+
+#ifdef SLAPD_CRYPT
+static char *salt_format = NULL;
+static lutil_cryptfunc lutil_crypt;
+lutil_cryptfunc *lutil_cryptptr = lutil_crypt;
+#endif
+
+/* KLUDGE:
+ * chk_fn is NULL iff name is {CLEARTEXT}
+ * otherwise, things will break
+ */
+struct pw_scheme {
+ struct berval name;
+ LUTIL_PASSWD_CHK_FUNC *chk_fn;
+ LUTIL_PASSWD_HASH_FUNC *hash_fn;
+};
+
+struct pw_slist {
+ struct pw_slist *next;
+ struct pw_scheme s;
+};
+
+/* password check routines */
+
+#define SALT_SIZE 4
+
+static LUTIL_PASSWD_CHK_FUNC chk_md5;
+static LUTIL_PASSWD_CHK_FUNC chk_smd5;
+static LUTIL_PASSWD_HASH_FUNC hash_smd5;
+static LUTIL_PASSWD_HASH_FUNC hash_md5;
+
+
+#ifdef LUTIL_SHA1_BYTES
+static LUTIL_PASSWD_CHK_FUNC chk_ssha1;
+static LUTIL_PASSWD_CHK_FUNC chk_sha1;
+static LUTIL_PASSWD_HASH_FUNC hash_sha1;
+static LUTIL_PASSWD_HASH_FUNC hash_ssha1;
+#endif
+
+
+#ifdef SLAPD_CRYPT
+static LUTIL_PASSWD_CHK_FUNC chk_crypt;
+static LUTIL_PASSWD_HASH_FUNC hash_crypt;
+
+#if defined( HAVE_GETPWNAM ) && defined( HAVE_STRUCT_PASSWD_PW_PASSWD )
+static LUTIL_PASSWD_CHK_FUNC chk_unix;
+#endif
+#endif
+
+/* password hash routines */
+
+#ifdef SLAPD_CLEARTEXT
+static LUTIL_PASSWD_HASH_FUNC hash_clear;
+#endif
+
+static struct pw_slist *pw_schemes;
+static int pw_inited;
+
+static const struct pw_scheme pw_schemes_default[] =
+{
+#ifdef LUTIL_SHA1_BYTES
+ { BER_BVC("{SSHA}"), chk_ssha1, hash_ssha1 },
+ { BER_BVC("{SHA}"), chk_sha1, hash_sha1 },
+#endif
+
+ { BER_BVC("{SMD5}"), chk_smd5, hash_smd5 },
+ { BER_BVC("{MD5}"), chk_md5, hash_md5 },
+
+#ifdef SLAPD_CRYPT
+ { BER_BVC("{CRYPT}"), chk_crypt, hash_crypt },
+# if defined( HAVE_GETPWNAM ) && defined( HAVE_STRUCT_PASSWD_PW_PASSWD )
+ { BER_BVC("{UNIX}"), chk_unix, NULL },
+# endif
+#endif
+
+#ifdef SLAPD_CLEARTEXT
+ /* pseudo scheme */
+ { BER_BVC("{CLEARTEXT}"), NULL, hash_clear },
+#endif
+
+ { BER_BVNULL, NULL, NULL }
+};
+
+int lutil_passwd_add(
+ struct berval *scheme,
+ LUTIL_PASSWD_CHK_FUNC *chk,
+ LUTIL_PASSWD_HASH_FUNC *hash )
+{
+ struct pw_slist *ptr;
+
+ if (!pw_inited) lutil_passwd_init();
+
+ ptr = ber_memalloc( sizeof( struct pw_slist ));
+ if (!ptr) return -1;
+ ptr->next = pw_schemes;
+ ptr->s.name = *scheme;
+ ptr->s.chk_fn = chk;
+ ptr->s.hash_fn = hash;
+ pw_schemes = ptr;
+ return 0;
+}
+
+void lutil_passwd_init()
+{
+ struct pw_scheme *s;
+
+ pw_inited = 1;
+
+ for( s=(struct pw_scheme *)pw_schemes_default; s->name.bv_val; s++) {
+ if ( lutil_passwd_add( &s->name, s->chk_fn, s->hash_fn ) ) break;
+ }
+}
+
+void lutil_passwd_destroy()
+{
+ struct pw_slist *ptr, *next;
+
+ for( ptr=pw_schemes; ptr; ptr=next ) {
+ next = ptr->next;
+ ber_memfree( ptr );
+ }
+}
+
+static const struct pw_scheme *get_scheme(
+ const char* scheme )
+{
+ struct pw_slist *pws;
+ struct berval bv;
+
+ if (!pw_inited) lutil_passwd_init();
+
+ bv.bv_val = strchr( scheme, '}' );
+ if ( !bv.bv_val )
+ return NULL;
+
+ bv.bv_len = bv.bv_val - scheme + 1;
+ bv.bv_val = (char *) scheme;
+
+ for( pws=pw_schemes; pws; pws=pws->next ) {
+ if ( ber_bvstrcasecmp(&bv, &pws->s.name ) == 0 ) {
+ return &(pws->s);
+ }
+ }
+
+ return NULL;
+}
+
+int lutil_passwd_scheme(
+ const char* scheme )
+{
+ if( scheme == NULL ) {
+ return 0;
+ }
+
+ return get_scheme(scheme) != NULL;
+}
+
+
+static int is_allowed_scheme(
+ const char* scheme,
+ const char** schemes )
+{
+ int i;
+
+ if( schemes == NULL ) return 1;
+
+ for( i=0; schemes[i] != NULL; i++ ) {
+ if( strcasecmp( scheme, schemes[i] ) == 0 ) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static struct berval *passwd_scheme(
+ const struct pw_scheme *scheme,
+ const struct berval * passwd,
+ struct berval *bv,
+ const char** allowed )
+{
+ if( !is_allowed_scheme( scheme->name.bv_val, allowed ) ) {
+ return NULL;
+ }
+
+ if( passwd->bv_len >= scheme->name.bv_len ) {
+ if( strncasecmp( passwd->bv_val, scheme->name.bv_val, scheme->name.bv_len ) == 0 ) {
+ bv->bv_val = &passwd->bv_val[scheme->name.bv_len];
+ bv->bv_len = passwd->bv_len - scheme->name.bv_len;
+
+ return bv;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Return 0 if creds are good.
+ */
+int
+lutil_passwd(
+ const struct berval *passwd, /* stored passwd */
+ const struct berval *cred, /* user cred */
+ const char **schemes,
+ const char **text )
+{
+ struct pw_slist *pws;
+
+ if ( text ) *text = NULL;
+
+ if (cred == NULL || cred->bv_len == 0 ||
+ passwd == NULL || passwd->bv_len == 0 )
+ {
+ return -1;
+ }
+
+ if (!pw_inited) lutil_passwd_init();
+
+ for( pws=pw_schemes; pws; pws=pws->next ) {
+ if( pws->s.chk_fn ) {
+ struct berval x;
+ struct berval *p = passwd_scheme( &(pws->s),
+ passwd, &x, schemes );
+
+ if( p != NULL ) {
+ return (pws->s.chk_fn)( &(pws->s.name), p, cred, text );
+ }
+ }
+ }
+
+#ifdef SLAPD_CLEARTEXT
+ /* Do we think there is a scheme specifier here that we
+ * didn't recognize? Assume a scheme name is at least 1 character.
+ */
+ if (( passwd->bv_val[0] == '{' ) &&
+ ( ber_bvchr( passwd, '}' ) > passwd->bv_val+1 ))
+ {
+ return 1;
+ }
+ if( is_allowed_scheme("{CLEARTEXT}", schemes ) ) {
+ return ( passwd->bv_len == cred->bv_len ) ?
+ memcmp( passwd->bv_val, cred->bv_val, passwd->bv_len )
+ : 1;
+ }
+#endif
+ return 1;
+}
+
+int lutil_passwd_generate( struct berval *pw, ber_len_t len )
+{
+
+ if( len < 1 ) return -1;
+
+ pw->bv_len = len;
+ pw->bv_val = ber_memalloc( len + 1 );
+
+ if( pw->bv_val == NULL ) {
+ return -1;
+ }
+
+ if( lutil_entropy( (unsigned char *) pw->bv_val, pw->bv_len) < 0 ) {
+ return -1;
+ }
+
+ for( len = 0; len < pw->bv_len; len++ ) {
+ pw->bv_val[len] = crypt64[
+ pw->bv_val[len] % (sizeof(crypt64)-1) ];
+ }
+
+ pw->bv_val[len] = '\0';
+
+ return 0;
+}
+
+int lutil_passwd_hash(
+ const struct berval * passwd,
+ const char * method,
+ struct berval *hash,
+ const char **text )
+{
+ const struct pw_scheme *sc = get_scheme( method );
+
+ hash->bv_val = NULL;
+ hash->bv_len = 0;
+
+ if( sc == NULL ) {
+ if( text ) *text = "scheme not recognized";
+ return -1;
+ }
+
+ if( ! sc->hash_fn ) {
+ if( text ) *text = "scheme provided no hash function";
+ return -1;
+ }
+
+ if( text ) *text = NULL;
+
+ return (sc->hash_fn)( &sc->name, passwd, hash, text );
+}
+
+/* pw_string is only called when SLAPD_CRYPT is defined */
+#if defined(SLAPD_CRYPT)
+static int pw_string(
+ const struct berval *sc,
+ struct berval *passwd )
+{
+ struct berval pw;
+
+ pw.bv_len = sc->bv_len + passwd->bv_len;
+ pw.bv_val = ber_memalloc( pw.bv_len + 1 );
+
+ if( pw.bv_val == NULL ) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ AC_MEMCPY( pw.bv_val, sc->bv_val, sc->bv_len );
+ AC_MEMCPY( &pw.bv_val[sc->bv_len], passwd->bv_val, passwd->bv_len );
+
+ pw.bv_val[pw.bv_len] = '\0';
+ *passwd = pw;
+
+ return LUTIL_PASSWD_OK;
+}
+#endif /* SLAPD_CRYPT */
+
+int lutil_passwd_string64(
+ const struct berval *sc,
+ const struct berval *hash,
+ struct berval *b64,
+ const struct berval *salt )
+{
+ int rc;
+ struct berval string;
+ size_t b64len;
+
+ if( salt ) {
+ /* need to base64 combined string */
+ string.bv_len = hash->bv_len + salt->bv_len;
+ string.bv_val = ber_memalloc( string.bv_len + 1 );
+
+ if( string.bv_val == NULL ) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ AC_MEMCPY( string.bv_val, hash->bv_val,
+ hash->bv_len );
+ AC_MEMCPY( &string.bv_val[hash->bv_len], salt->bv_val,
+ salt->bv_len );
+ string.bv_val[string.bv_len] = '\0';
+
+ } else {
+ string = *hash;
+ }
+
+ b64len = LUTIL_BASE64_ENCODE_LEN( string.bv_len ) + 1;
+ b64->bv_len = b64len + sc->bv_len;
+ b64->bv_val = ber_memalloc( b64->bv_len + 1 );
+
+ if( b64->bv_val == NULL ) {
+ if( salt ) ber_memfree( string.bv_val );
+ return LUTIL_PASSWD_ERR;
+ }
+
+ AC_MEMCPY(b64->bv_val, sc->bv_val, sc->bv_len);
+
+ rc = lutil_b64_ntop(
+ (unsigned char *) string.bv_val, string.bv_len,
+ &b64->bv_val[sc->bv_len], b64len );
+
+ if( salt ) ber_memfree( string.bv_val );
+
+ if( rc < 0 ) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* recompute length */
+ b64->bv_len = sc->bv_len + rc;
+ assert( strlen(b64->bv_val) == b64->bv_len );
+ return LUTIL_PASSWD_OK;
+}
+
+/* PASSWORD CHECK ROUTINES */
+
+#ifdef LUTIL_SHA1_BYTES
+static int chk_ssha1(
+ const struct berval *sc,
+ const struct berval * passwd,
+ const struct berval * cred,
+ const char **text )
+{
+ lutil_SHA1_CTX SHA1context;
+ unsigned char SHA1digest[LUTIL_SHA1_BYTES];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check -- must have some salt */
+ if (decode_len <= sizeof(SHA1digest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* decode base64 password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ /* safety check -- must have some salt */
+ if (rc <= (int)(sizeof(SHA1digest))) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ lutil_SHA1Init(&SHA1context);
+ lutil_SHA1Update(&SHA1context,
+ (const unsigned char *) cred->bv_val, cred->bv_len);
+ lutil_SHA1Update(&SHA1context,
+ (const unsigned char *) &orig_pass[sizeof(SHA1digest)],
+ rc - sizeof(SHA1digest));
+ lutil_SHA1Final(SHA1digest, &SHA1context);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)SHA1digest, sizeof(SHA1digest));
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+static int chk_sha1(
+ const struct berval *sc,
+ const struct berval * passwd,
+ const struct berval * cred,
+ const char **text )
+{
+ lutil_SHA1_CTX SHA1context;
+ unsigned char SHA1digest[LUTIL_SHA1_BYTES];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len < sizeof(SHA1digest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* base64 un-encode password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if( rc != sizeof(SHA1digest) ) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ lutil_SHA1Init(&SHA1context);
+ lutil_SHA1Update(&SHA1context,
+ (const unsigned char *) cred->bv_val, cred->bv_len);
+ lutil_SHA1Final(SHA1digest, &SHA1context);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)SHA1digest, sizeof(SHA1digest));
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+#endif
+
+static int chk_smd5(
+ const struct berval *sc,
+ const struct berval * passwd,
+ const struct berval * cred,
+ const char **text )
+{
+ lutil_MD5_CTX MD5context;
+ unsigned char MD5digest[LUTIL_MD5_BYTES];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len <= sizeof(MD5digest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* base64 un-encode password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if (rc <= (int)(sizeof(MD5digest))) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ lutil_MD5Init(&MD5context);
+ lutil_MD5Update(&MD5context,
+ (const unsigned char *) cred->bv_val,
+ cred->bv_len );
+ lutil_MD5Update(&MD5context,
+ &orig_pass[sizeof(MD5digest)],
+ rc - sizeof(MD5digest));
+ lutil_MD5Final(MD5digest, &MD5context);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)MD5digest, sizeof(MD5digest));
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+static int chk_md5(
+ const struct berval *sc,
+ const struct berval * passwd,
+ const struct berval * cred,
+ const char **text )
+{
+ lutil_MD5_CTX MD5context;
+ unsigned char MD5digest[LUTIL_MD5_BYTES];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len < sizeof(MD5digest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* base64 un-encode password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+ if ( rc != sizeof(MD5digest) ) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ lutil_MD5Init(&MD5context);
+ lutil_MD5Update(&MD5context,
+ (const unsigned char *) cred->bv_val,
+ cred->bv_len );
+ lutil_MD5Final(MD5digest, &MD5context);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)MD5digest, sizeof(MD5digest));
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+#ifdef SLAPD_CRYPT
+static int lutil_crypt(
+ const char *key,
+ const char *salt,
+ char **hash )
+{
+ char *cr = crypt( key, salt );
+ int rc;
+
+ if( cr == NULL || cr[0] == '\0' ) {
+ /* salt must have been invalid */
+ rc = LUTIL_PASSWD_ERR;
+ } else {
+ if ( hash ) {
+ *hash = ber_strdup( cr );
+ rc = LUTIL_PASSWD_OK;
+ } else {
+ rc = strcmp( salt, cr ) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+ }
+ }
+ return rc;
+}
+
+static int chk_crypt(
+ const struct berval *sc,
+ const struct berval * passwd,
+ const struct berval * cred,
+ const char **text )
+{
+ unsigned int i;
+
+ for( i=0; i<cred->bv_len; i++) {
+ if(cred->bv_val[i] == '\0') {
+ return LUTIL_PASSWD_ERR; /* NUL character in password */
+ }
+ }
+
+ if( cred->bv_val[i] != '\0' ) {
+ return LUTIL_PASSWD_ERR; /* cred must behave like a string */
+ }
+
+ if( passwd->bv_len < 2 ) {
+ return LUTIL_PASSWD_ERR; /* passwd must be at least two characters long */
+ }
+
+ for( i=0; i<passwd->bv_len; i++) {
+ if(passwd->bv_val[i] == '\0') {
+ return LUTIL_PASSWD_ERR; /* NUL character in password */
+ }
+ }
+
+ if( passwd->bv_val[i] != '\0' ) {
+ return LUTIL_PASSWD_ERR; /* passwd must behave like a string */
+ }
+
+ return lutil_cryptptr( cred->bv_val, passwd->bv_val, NULL );
+}
+
+# if defined( HAVE_GETPWNAM ) && defined( HAVE_STRUCT_PASSWD_PW_PASSWD )
+static int chk_unix(
+ const struct berval *sc,
+ const struct berval * passwd,
+ const struct berval * cred,
+ const char **text )
+{
+ unsigned int i;
+ char *pw;
+
+ for( i=0; i<cred->bv_len; i++) {
+ if(cred->bv_val[i] == '\0') {
+ return LUTIL_PASSWD_ERR; /* NUL character in password */
+ }
+ }
+ if( cred->bv_val[i] != '\0' ) {
+ return LUTIL_PASSWD_ERR; /* cred must behave like a string */
+ }
+
+ for( i=0; i<passwd->bv_len; i++) {
+ if(passwd->bv_val[i] == '\0') {
+ return LUTIL_PASSWD_ERR; /* NUL character in password */
+ }
+ }
+
+ if( passwd->bv_val[i] != '\0' ) {
+ return LUTIL_PASSWD_ERR; /* passwd must behave like a string */
+ }
+
+ {
+ struct passwd *pwd = getpwnam(passwd->bv_val);
+
+ if(pwd == NULL) {
+ return LUTIL_PASSWD_ERR; /* not found */
+ }
+
+ pw = pwd->pw_passwd;
+ }
+# ifdef HAVE_GETSPNAM
+ {
+ struct spwd *spwd = getspnam(passwd->bv_val);
+
+ if(spwd != NULL) {
+ pw = spwd->sp_pwdp;
+ }
+ }
+# endif
+# ifdef HAVE_AIX_SECURITY
+ {
+ struct userpw *upw = getuserpw(passwd->bv_val);
+
+ if (upw != NULL) {
+ pw = upw->upw_passwd;
+ }
+ }
+# endif
+
+ if( pw == NULL || pw[0] == '\0' || pw[1] == '\0' ) {
+ /* password must must be at least two characters long */
+ return LUTIL_PASSWD_ERR;
+ }
+
+ return lutil_cryptptr( cred->bv_val, pw, NULL );
+}
+# endif
+#endif
+
+/* PASSWORD GENERATION ROUTINES */
+
+#ifdef LUTIL_SHA1_BYTES
+static int hash_ssha1(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ lutil_SHA1_CTX SHA1context;
+ unsigned char SHA1digest[LUTIL_SHA1_BYTES];
+ char saltdata[SALT_SIZE];
+ struct berval digest;
+ struct berval salt;
+
+ digest.bv_val = (char *) SHA1digest;
+ digest.bv_len = sizeof(SHA1digest);
+ salt.bv_val = saltdata;
+ salt.bv_len = sizeof(saltdata);
+
+ if( lutil_entropy( (unsigned char *) salt.bv_val, salt.bv_len) < 0 ) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ lutil_SHA1Init( &SHA1context );
+ lutil_SHA1Update( &SHA1context,
+ (const unsigned char *)passwd->bv_val, passwd->bv_len );
+ lutil_SHA1Update( &SHA1context,
+ (const unsigned char *)salt.bv_val, salt.bv_len );
+ lutil_SHA1Final( SHA1digest, &SHA1context );
+
+ return lutil_passwd_string64( scheme, &digest, hash, &salt);
+}
+
+static int hash_sha1(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ lutil_SHA1_CTX SHA1context;
+ unsigned char SHA1digest[LUTIL_SHA1_BYTES];
+ struct berval digest;
+ digest.bv_val = (char *) SHA1digest;
+ digest.bv_len = sizeof(SHA1digest);
+
+ lutil_SHA1Init( &SHA1context );
+ lutil_SHA1Update( &SHA1context,
+ (const unsigned char *)passwd->bv_val, passwd->bv_len );
+ lutil_SHA1Final( SHA1digest, &SHA1context );
+
+ return lutil_passwd_string64( scheme, &digest, hash, NULL);
+}
+#endif
+
+static int hash_smd5(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ lutil_MD5_CTX MD5context;
+ unsigned char MD5digest[LUTIL_MD5_BYTES];
+ char saltdata[SALT_SIZE];
+ struct berval digest;
+ struct berval salt;
+
+ digest.bv_val = (char *) MD5digest;
+ digest.bv_len = sizeof(MD5digest);
+ salt.bv_val = saltdata;
+ salt.bv_len = sizeof(saltdata);
+
+ if( lutil_entropy( (unsigned char *) salt.bv_val, salt.bv_len) < 0 ) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ lutil_MD5Init( &MD5context );
+ lutil_MD5Update( &MD5context,
+ (const unsigned char *) passwd->bv_val, passwd->bv_len );
+ lutil_MD5Update( &MD5context,
+ (const unsigned char *) salt.bv_val, salt.bv_len );
+ lutil_MD5Final( MD5digest, &MD5context );
+
+ return lutil_passwd_string64( scheme, &digest, hash, &salt );
+}
+
+static int hash_md5(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ lutil_MD5_CTX MD5context;
+ unsigned char MD5digest[LUTIL_MD5_BYTES];
+
+ struct berval digest;
+
+ digest.bv_val = (char *) MD5digest;
+ digest.bv_len = sizeof(MD5digest);
+
+ lutil_MD5Init( &MD5context );
+ lutil_MD5Update( &MD5context,
+ (const unsigned char *) passwd->bv_val, passwd->bv_len );
+ lutil_MD5Final( MD5digest, &MD5context );
+
+ return lutil_passwd_string64( scheme, &digest, hash, NULL );
+;
+}
+
+#ifdef SLAPD_CRYPT
+static int hash_crypt(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ unsigned char salt[32]; /* salt suitable for most anything */
+ unsigned int i;
+ char *save;
+ int rc;
+
+ for( i=0; i<passwd->bv_len; i++) {
+ if(passwd->bv_val[i] == '\0') {
+ return LUTIL_PASSWD_ERR; /* NUL character in password */
+ }
+ }
+
+ if( passwd->bv_val[i] != '\0' ) {
+ return LUTIL_PASSWD_ERR; /* passwd must behave like a string */
+ }
+
+ if( lutil_entropy( salt, sizeof( salt ) ) < 0 ) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ for( i=0; i< ( sizeof(salt) - 1 ); i++ ) {
+ salt[i] = crypt64[ salt[i] % (sizeof(crypt64)-1) ];
+ }
+ salt[sizeof( salt ) - 1 ] = '\0';
+
+ if( salt_format != NULL ) {
+ /* copy the salt we made into entropy before snprintfing
+ it back into the salt */
+ char entropy[sizeof(salt)];
+ strcpy( entropy, (char *) salt );
+ snprintf( (char *) salt, sizeof(entropy), salt_format, entropy );
+ }
+
+ rc = lutil_cryptptr( passwd->bv_val, (char *) salt, &hash->bv_val );
+ if ( rc != LUTIL_PASSWD_OK ) return rc;
+
+ if( hash->bv_val == NULL ) return -1;
+
+ hash->bv_len = strlen( hash->bv_val );
+
+ save = hash->bv_val;
+
+ if( hash->bv_len == 0 ) {
+ rc = LUTIL_PASSWD_ERR;
+ } else {
+ rc = pw_string( scheme, hash );
+ }
+ ber_memfree( save );
+ return rc;
+}
+#endif
+
+int lutil_salt_format(const char *format)
+{
+#ifdef SLAPD_CRYPT
+ ber_memfree( salt_format );
+
+ salt_format = format != NULL ? ber_strdup( format ) : NULL;
+#endif
+
+ return 0;
+}
+
+#ifdef SLAPD_CLEARTEXT
+static int hash_clear(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ ber_dupbv( hash, (struct berval *)passwd );
+ return LUTIL_PASSWD_OK;
+}
+#endif
+
diff --git a/libraries/liblutil/ptest.c b/libraries/liblutil/ptest.c
new file mode 100644
index 0000000..5477007
--- /dev/null
+++ b/libraries/liblutil/ptest.c
@@ -0,0 +1,112 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/signal.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include <lber.h>
+
+#include "lutil.h"
+
+/*
+ * Password Test Program
+ */
+
+static char *hash[] = {
+#ifdef SLAP_AUTHPASSWD
+ "SHA1", "MD5",
+#else
+#ifdef SLAPD_CRYPT
+ "{CRYPT}",
+#endif
+ "{SSHA}", "{SMD5}",
+ "{SHA}", "{MD5}",
+ "{BOGUS}",
+#endif
+ NULL
+};
+
+static struct berval pw[] = {
+ { sizeof("secret")-1, "secret" },
+ { sizeof("binary\0secret")-1, "binary\0secret" },
+ { 0, NULL }
+};
+
+int
+main( int argc, char *argv[] )
+{
+ int i, j, rc;
+ struct berval *passwd;
+#ifdef SLAP_AUTHPASSWD
+ struct berval *salt;
+#endif
+ struct berval bad;
+ bad.bv_val = "bad password";
+ bad.bv_len = sizeof("bad password")-1;
+
+ for( i= 0; hash[i]; i++ ) {
+ for( j = 0; pw[j].bv_len; j++ ) {
+#ifdef SLAP_AUTHPASSWD
+ rc = lutil_authpasswd_hash( &pw[j],
+ &passwd, &salt, hash[i] );
+
+ if( rc )
+#else
+ passwd = lutil_passwd_hash( &pw[j], hash[i] );
+
+ if( passwd == NULL )
+#endif
+ {
+ printf("%s generate fail: %s (%d)\n",
+ hash[i], pw[j].bv_val, pw[j].bv_len );
+ continue;
+ }
+
+
+#ifdef SLAP_AUTHPASSWD
+ rc = lutil_authpasswd( &pw[j], passwd, salt, NULL );
+#else
+ rc = lutil_passwd( passwd, &pw[j], NULL );
+#endif
+
+ printf("%s (%d): %s (%d)\t(%d) %s\n",
+ pw[j].bv_val, pw[j].bv_len, passwd->bv_val, passwd->bv_len,
+ rc, rc == 0 ? "OKAY" : "BAD" );
+
+#ifdef SLAP_AUTHPASSWD
+ rc = lutil_authpasswd( passwd, salt, &bad, NULL );
+#else
+ rc = lutil_passwd( passwd, &bad, NULL );
+#endif
+
+ printf("%s (%d): %s (%d)\t(%d) %s\n",
+ bad.bv_val, bad.bv_len, passwd->bv_val, passwd->bv_len,
+ rc, rc != 0 ? "OKAY" : "BAD" );
+ }
+
+ printf("\n");
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/libraries/liblutil/sasl.c b/libraries/liblutil/sasl.c
new file mode 100644
index 0000000..b6a3f00
--- /dev/null
+++ b/libraries/liblutil/sasl.c
@@ -0,0 +1,232 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#ifdef HAVE_CYRUS_SASL
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#ifdef HAVE_SASL_SASL_H
+#include <sasl/sasl.h>
+#else
+#include <sasl.h>
+#endif
+
+#include <ldap.h>
+#include "ldap_pvt.h"
+#include "lutil_ldap.h"
+
+
+typedef struct lutil_sasl_defaults_s {
+ char *mech;
+ char *realm;
+ char *authcid;
+ char *passwd;
+ char *authzid;
+ char **resps;
+ int nresps;
+} lutilSASLdefaults;
+
+
+void
+lutil_sasl_freedefs(
+ void *defaults )
+{
+ lutilSASLdefaults *defs = defaults;
+
+ assert( defs != NULL );
+
+ if (defs->mech) ber_memfree(defs->mech);
+ if (defs->realm) ber_memfree(defs->realm);
+ if (defs->authcid) ber_memfree(defs->authcid);
+ if (defs->passwd) ber_memfree(defs->passwd);
+ if (defs->authzid) ber_memfree(defs->authzid);
+ if (defs->resps) ldap_charray_free(defs->resps);
+
+ ber_memfree(defs);
+}
+
+void *
+lutil_sasl_defaults(
+ LDAP *ld,
+ char *mech,
+ char *realm,
+ char *authcid,
+ char *passwd,
+ char *authzid )
+{
+ lutilSASLdefaults *defaults;
+
+ defaults = ber_memalloc( sizeof( lutilSASLdefaults ) );
+
+ if( defaults == NULL ) return NULL;
+
+ defaults->mech = mech ? ber_strdup(mech) : NULL;
+ defaults->realm = realm ? ber_strdup(realm) : NULL;
+ defaults->authcid = authcid ? ber_strdup(authcid) : NULL;
+ defaults->passwd = passwd ? ber_strdup(passwd) : NULL;
+ defaults->authzid = authzid ? ber_strdup(authzid) : NULL;
+
+ if( defaults->mech == NULL ) {
+ ldap_get_option( ld, LDAP_OPT_X_SASL_MECH, &defaults->mech );
+ }
+ if( defaults->realm == NULL ) {
+ ldap_get_option( ld, LDAP_OPT_X_SASL_REALM, &defaults->realm );
+ }
+ if( defaults->authcid == NULL ) {
+ ldap_get_option( ld, LDAP_OPT_X_SASL_AUTHCID, &defaults->authcid );
+ }
+ if( defaults->authzid == NULL ) {
+ ldap_get_option( ld, LDAP_OPT_X_SASL_AUTHZID, &defaults->authzid );
+ }
+ defaults->resps = NULL;
+ defaults->nresps = 0;
+
+ return defaults;
+}
+
+static int interaction(
+ unsigned flags,
+ sasl_interact_t *interact,
+ lutilSASLdefaults *defaults )
+{
+ const char *dflt = interact->defresult;
+ char input[1024];
+
+ int noecho=0;
+ int challenge=0;
+
+ switch( interact->id ) {
+ case SASL_CB_GETREALM:
+ if( defaults ) dflt = defaults->realm;
+ break;
+ case SASL_CB_AUTHNAME:
+ if( defaults ) dflt = defaults->authcid;
+ break;
+ case SASL_CB_PASS:
+ if( defaults ) dflt = defaults->passwd;
+ noecho = 1;
+ break;
+ case SASL_CB_USER:
+ if( defaults ) dflt = defaults->authzid;
+ break;
+ case SASL_CB_NOECHOPROMPT:
+ noecho = 1;
+ challenge = 1;
+ break;
+ case SASL_CB_ECHOPROMPT:
+ challenge = 1;
+ break;
+ }
+
+ if( dflt && !*dflt ) dflt = NULL;
+
+ if( flags != LDAP_SASL_INTERACTIVE &&
+ ( dflt || interact->id == SASL_CB_USER ) )
+ {
+ goto use_default;
+ }
+
+ if( flags == LDAP_SASL_QUIET ) {
+ /* don't prompt */
+ return LDAP_OTHER;
+ }
+
+ if( challenge ) {
+ if( interact->challenge ) {
+ fprintf( stderr, _("Challenge: %s\n"), interact->challenge );
+ }
+ }
+
+ if( dflt ) {
+ fprintf( stderr, _("Default: %s\n"), dflt );
+ }
+
+ snprintf( input, sizeof input, "%s: ",
+ interact->prompt ? interact->prompt : _("Interact") );
+
+ if( noecho ) {
+ interact->result = (char *) getpassphrase( input );
+ interact->len = interact->result
+ ? strlen( interact->result ) : 0;
+
+ } else {
+ /* prompt user */
+ fputs( input, stderr );
+
+ /* get input */
+ interact->result = fgets( input, sizeof(input), stdin );
+
+ if( interact->result == NULL ) {
+ interact->len = 0;
+ return LDAP_UNAVAILABLE;
+ }
+
+ /* len of input */
+ interact->len = strlen(input);
+
+ if( interact->len > 0 && input[interact->len - 1] == '\n' ) {
+ /* input includes '\n', trim it */
+ interact->len--;
+ input[interact->len] = '\0';
+ }
+ }
+
+
+ if( interact->len > 0 ) {
+ /* duplicate */
+ char *p = (char *)interact->result;
+ ldap_charray_add(&defaults->resps, interact->result);
+ interact->result = defaults->resps[defaults->nresps++];
+
+ /* zap */
+ memset( p, '\0', interact->len );
+
+ } else {
+use_default:
+ /* input must be empty */
+ interact->result = (dflt && *dflt) ? dflt : "";
+ interact->len = strlen( interact->result );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int lutil_sasl_interact(
+ LDAP *ld,
+ unsigned flags,
+ void *defaults,
+ void *in )
+{
+ sasl_interact_t *interact = in;
+
+ if( flags == LDAP_SASL_INTERACTIVE ) {
+ fputs( _("SASL Interaction\n"), stderr );
+ }
+
+ while( interact->id != SASL_CB_LIST_END ) {
+ int rc = interaction( flags, interact, defaults );
+
+ if( rc ) return rc;
+ interact++;
+ }
+
+ return LDAP_SUCCESS;
+}
+#endif
diff --git a/libraries/liblutil/sha1.c b/libraries/liblutil/sha1.c
new file mode 100644
index 0000000..08093d7
--- /dev/null
+++ b/libraries/liblutil/sha1.c
@@ -0,0 +1,288 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* This work was derived from code developed by Steve Reid and
+ * adapted for use in OpenLDAP by Kurt D. Zeilenga.
+ */
+
+
+/* Acquired from:
+ * $OpenBSD: sha1.c,v 1.9 1997/07/23 21:12:32 kstailey Exp $ */
+/*
+ * SHA-1 in C
+ * By Steve Reid <steve@edmweb.com>
+ * 100% Public Domain
+ *
+ * Test Vectors (from FIPS PUB 180-1)
+ * "abc"
+ * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+ * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+ * A million repetitions of "a"
+ * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+ */
+/*
+ * This code assumes uint32 is 32 bits and char is 8 bits
+ */
+
+#include "portable.h"
+#include <ac/param.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/bytes.h>
+
+#include "lutil_sha1.h"
+
+#ifdef LUTIL_SHA1_BYTES
+
+/* undefining this will cause pointer alignment errors */
+#define SHA1HANDSOFF /* Copies data before messing with it. */
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/*
+ * blk0() and blk() perform the initial expand.
+ * I got the idea of expanding during the round function from SSLeay
+ */
+#if BYTE_ORDER == LITTLE_ENDIAN
+# define blk0(i) (block[i] = (rol(block[i],24)&0xFF00FF00) \
+ |(rol(block[i],8)&0x00FF00FF))
+#else
+# define blk0(i) block[i]
+#endif
+#define blk(i) (block[i&15] = rol(block[(i+13)&15]^block[(i+8)&15] \
+ ^block[(i+2)&15]^block[i&15],1))
+
+/*
+ * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1
+ */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+/*
+ * Hash a single 512-bit block. This is the core of the algorithm.
+ */
+void
+lutil_SHA1Transform( uint32 *state, const unsigned char *buffer )
+{
+ uint32 a, b, c, d, e;
+
+#ifdef SHA1HANDSOFF
+ uint32 block[16];
+ (void)AC_MEMCPY(block, buffer, 64);
+#else
+ uint32 *block = (u_int32 *) buffer;
+#endif
+
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+
+ /* Wipe variables */
+ a = b = c = d = e = 0;
+}
+
+
+/*
+ * lutil_SHA1Init - Initialize new context
+ */
+void
+lutil_SHA1Init( lutil_SHA1_CTX *context )
+{
+
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+
+/*
+ * Run your data through this.
+ */
+void
+lutil_SHA1Update(
+ lutil_SHA1_CTX *context,
+ const unsigned char *data,
+ uint32 len
+)
+{
+ u_int i, j;
+
+ j = context->count[0];
+ if ((context->count[0] += len << 3) < j)
+ context->count[1] += (len>>29)+1;
+ j = (j >> 3) & 63;
+ if ((j + len) > 63) {
+ (void)AC_MEMCPY(&context->buffer[j], data, (i = 64-j));
+ lutil_SHA1Transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64)
+ lutil_SHA1Transform(context->state, &data[i]);
+ j = 0;
+ } else {
+ i = 0;
+ }
+ (void)AC_MEMCPY(&context->buffer[j], &data[i], len - i);
+}
+
+
+/*
+ * Add padding and return the message digest.
+ */
+void
+lutil_SHA1Final( unsigned char *digest, lutil_SHA1_CTX *context )
+{
+ u_int i;
+ unsigned char finalcount[8];
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+ >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
+ }
+ lutil_SHA1Update(context, (unsigned char *)"\200", 1);
+ while ((context->count[0] & 504) != 448)
+ lutil_SHA1Update(context, (unsigned char *)"\0", 1);
+ lutil_SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
+
+ if (digest) {
+ for (i = 0; i < 20; i++)
+ digest[i] = (unsigned char)
+ ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+ }
+}
+
+
+/* sha1hl.c
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dkuug.dk> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char rcsid[] = "$OpenBSD: sha1hl.c,v 1.1 1997/07/12 20:06:03 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <ac/errno.h>
+#include <ac/unistd.h>
+
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+
+/* ARGSUSED */
+char *
+lutil_SHA1End( lutil_SHA1_CTX *ctx, char *buf )
+{
+ int i;
+ char *p = buf;
+ unsigned char digest[20];
+ static const char hex[]="0123456789abcdef";
+
+ if (p == NULL && (p = malloc(41)) == NULL)
+ return 0;
+
+ lutil_SHA1Final(digest,ctx);
+ for (i = 0; i < 20; i++) {
+ p[i + i] = hex[digest[i] >> 4];
+ p[i + i + 1] = hex[digest[i] & 0x0f];
+ }
+ p[i + i] = '\0';
+ return(p);
+}
+
+char *
+lutil_SHA1File( char *filename, char *buf )
+{
+ unsigned char buffer[BUFSIZ];
+ lutil_SHA1_CTX ctx;
+ int fd, num, oerrno;
+
+ lutil_SHA1Init(&ctx);
+
+ if ((fd = open(filename,O_RDONLY)) < 0)
+ return(0);
+
+ while ((num = read(fd, buffer, sizeof(buffer))) > 0)
+ lutil_SHA1Update(&ctx, buffer, num);
+
+ oerrno = errno;
+ close(fd);
+ errno = oerrno;
+ return(num < 0 ? 0 : lutil_SHA1End(&ctx, buf));
+}
+
+char *
+lutil_SHA1Data( const unsigned char *data, size_t len, char *buf )
+{
+ lutil_SHA1_CTX ctx;
+
+ lutil_SHA1Init(&ctx);
+ lutil_SHA1Update(&ctx, data, len);
+ return(lutil_SHA1End(&ctx, buf));
+}
+
+#endif
diff --git a/libraries/liblutil/signal.c b/libraries/liblutil/signal.c
new file mode 100644
index 0000000..9d9da7a
--- /dev/null
+++ b/libraries/liblutil/signal.c
@@ -0,0 +1,41 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#ifdef HAVE_SIGACTION
+#include <ac/string.h>
+#include <ac/signal.h>
+
+lutil_sig_t
+lutil_sigaction(int sig, lutil_sig_t func)
+{
+ struct sigaction action, oaction;
+
+ memset( &action, '\0', sizeof(action) );
+
+ action.sa_handler = func;
+ sigemptyset( &action.sa_mask );
+#ifdef SA_RESTART
+ action.sa_flags |= SA_RESTART;
+#endif
+
+ if( sigaction( sig, &action, &oaction ) != 0 ) {
+ return NULL;
+ }
+
+ return oaction.sa_handler;
+}
+#endif
diff --git a/libraries/liblutil/slapdmsg.bin b/libraries/liblutil/slapdmsg.bin
new file mode 100644
index 0000000..d8ca806
--- /dev/null
+++ b/libraries/liblutil/slapdmsg.bin
Binary files differ
diff --git a/libraries/liblutil/slapdmsg.h b/libraries/liblutil/slapdmsg.h
new file mode 100644
index 0000000..a307603
--- /dev/null
+++ b/libraries/liblutil/slapdmsg.h
@@ -0,0 +1,65 @@
+//
+// This file contains message strings for the OpenLDAP slapd service.
+//
+// This file should be compiled as follows
+// mc -v slapdmsg.mc -r $(IntDir)
+// rc /v /r $(IntDir)\slapdmsg.rc
+// The mc (message compiler) command generates the .rc and .h files from this file. The
+// rc (resource compiler) takes the .rc file and produces a .res file that can be linked
+// with the final executable application. The application is then registered as a message
+// source with by creating the appropriate entries in the system registry.
+//
+//
+// Values are 32 bit values laid out as follows:
+//
+// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
+// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+// +---+-+-+-----------------------+-------------------------------+
+// |Sev|C|R| Facility | Code |
+// +---+-+-+-----------------------+-------------------------------+
+//
+// where
+//
+// Sev - is the severity code
+//
+// 00 - Success
+// 01 - Informational
+// 10 - Warning
+// 11 - Error
+//
+// C - is the Customer code flag
+//
+// R - is a reserved bit
+//
+// Facility - is the facility code
+//
+// Code - is the facility's status code
+//
+//
+// Define the facility codes
+//
+
+
+//
+// Define the severity codes
+//
+
+
+//
+// MessageId: MSG_SVC_STARTED
+//
+// MessageText:
+//
+// OpenLDAP service started. debuglevel=%1, conffile=%2, urls=%3
+//
+#define MSG_SVC_STARTED 0x40000500L
+
+//
+// MessageId: MSG_SVC_STOPPED
+//
+// MessageText:
+//
+// OpenLDAP service stopped.
+//
+#define MSG_SVC_STOPPED 0x40000501L
+
diff --git a/libraries/liblutil/slapdmsg.mc b/libraries/liblutil/slapdmsg.mc
new file mode 100644
index 0000000..53401f0
--- /dev/null
+++ b/libraries/liblutil/slapdmsg.mc
@@ -0,0 +1,28 @@
+;//
+;// This file contains message strings for the OpenLDAP slapd service.
+;//
+;// This file should be compiled as follows
+;// mc -v slapdmsg.mc -r $(IntDir)
+;// rc /v /r $(IntDir)\slapdmsg.rc
+;// The mc (message compiler) command generates the .rc and .h files from this file. The
+;// rc (resource compiler) takes the .rc file and produces a .res file that can be linked
+;// with the final executable application. The application is then registered as a message
+;// source with by creating the appropriate entries in the system registry.
+;//
+
+MessageID=0x500
+Severity=Informational
+SymbolicName=MSG_SVC_STARTED
+Facility=Application
+Language=English
+OpenLDAP service started. debuglevel=%1, conffile=%2, urls=%3
+.
+
+
+MessageID=0x501
+Severity=Informational
+SymbolicName=MSG_SVC_STOPPED
+Facility=Application
+Language=English
+OpenLDAP service stopped.
+.
diff --git a/libraries/liblutil/slapdmsg.rc b/libraries/liblutil/slapdmsg.rc
new file mode 100644
index 0000000..f967de2
--- /dev/null
+++ b/libraries/liblutil/slapdmsg.rc
@@ -0,0 +1,2 @@
+LANGUAGE 0x9,0x1
+1 11 slapdmsg.bin
diff --git a/libraries/liblutil/sockpair.c b/libraries/liblutil/sockpair.c
new file mode 100644
index 0000000..7be096d
--- /dev/null
+++ b/libraries/liblutil/sockpair.c
@@ -0,0 +1,78 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+#include <ac/socket.h>
+#include <ac/unistd.h>
+
+#include <lutil.h>
+
+/* Return a pair of socket descriptors that are connected to each other.
+ * The returned descriptors are suitable for use with select(). The two
+ * descriptors may or may not be identical; the function may return
+ * the same descriptor number in both slots. It is guaranteed that
+ * data written on sds[1] will be readable on sds[0]. The returned
+ * descriptors may be datagram oriented, so data should be written
+ * in reasonably small pieces and read all at once. On Unix systems
+ * this function is best implemented using a single pipe() call.
+ */
+
+int lutil_pair( ber_socket_t sds[2] )
+{
+#ifdef USE_PIPE
+ return pipe( sds );
+#else
+ struct sockaddr_in si;
+ int rc;
+ ber_socklen_t len = sizeof(si);
+ ber_socket_t sd;
+
+ sd = socket( AF_INET, SOCK_DGRAM, 0 );
+ if ( sd == AC_SOCKET_INVALID ) {
+ return sd;
+ }
+
+ (void) memset( (void*) &si, '\0', len );
+ si.sin_family = AF_INET;
+ si.sin_port = 0;
+ si.sin_addr.s_addr = htonl( INADDR_LOOPBACK );
+
+ rc = bind( sd, (struct sockaddr *)&si, len );
+ if ( rc == AC_SOCKET_ERROR ) {
+ tcp_close(sd);
+ return rc;
+ }
+
+ rc = getsockname( sd, (struct sockaddr *)&si, &len );
+ if ( rc == AC_SOCKET_ERROR ) {
+ tcp_close(sd);
+ return rc;
+ }
+
+ rc = connect( sd, (struct sockaddr *)&si, len );
+ if ( rc == AC_SOCKET_ERROR ) {
+ tcp_close(sd);
+ return rc;
+ }
+
+ sds[0] = sd;
+#if !HAVE_WINSOCK
+ sds[1] = dup( sds[0] );
+#else
+ sds[1] = sds[0];
+#endif
+ return 0;
+#endif
+}
diff --git a/libraries/liblutil/utils.c b/libraries/liblutil/utils.c
new file mode 100644
index 0000000..ea80659
--- /dev/null
+++ b/libraries/liblutil/utils.c
@@ -0,0 +1,1071 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/stdarg.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/unistd.h>
+#include <ac/time.h>
+#include <ac/errno.h>
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#include "lutil.h"
+#include "ldap_defaults.h"
+#include "ldap_pvt.h"
+#include "lber_pvt.h"
+
+#ifdef HAVE_EBCDIC
+int _trans_argv = 1;
+#endif
+
+#ifdef _WIN32
+/* Some Windows versions accept both forward and backslashes in
+ * directory paths, but we always use backslashes when generating
+ * and parsing...
+ */
+void lutil_slashpath( char *path )
+{
+ char *c, *p;
+
+ p = path;
+ while (( c=strchr( p, '/' ))) {
+ *c++ = '\\';
+ p = c;
+ }
+}
+#endif
+
+char* lutil_progname( const char* name, int argc, char *argv[] )
+{
+ char *progname;
+
+ if(argc == 0) {
+ return (char *)name;
+ }
+
+#ifdef HAVE_EBCDIC
+ if (_trans_argv) {
+ int i;
+ for (i=0; i<argc; i++) __etoa(argv[i]);
+ _trans_argv = 0;
+ }
+#endif
+ LUTIL_SLASHPATH( argv[0] );
+ progname = strrchr ( argv[0], *LDAP_DIRSEP );
+ progname = progname ? &progname[1] : argv[0];
+#ifdef _WIN32
+ {
+ size_t len = strlen( progname );
+ if ( len > 4 && strcasecmp( &progname[len - 4], ".exe" ) == 0 )
+ progname[len - 4] = '\0';
+ }
+#endif
+ return progname;
+}
+
+#if 0
+size_t lutil_gentime( char *s, size_t smax, const struct tm *tm )
+{
+ size_t ret;
+#ifdef HAVE_EBCDIC
+/* We've been compiling in ASCII so far, but we want EBCDIC now since
+ * strftime only understands EBCDIC input.
+ */
+#pragma convlit(suspend)
+#endif
+ ret = strftime( s, smax, "%Y%m%d%H%M%SZ", tm );
+#ifdef HAVE_EBCDIC
+#pragma convlit(resume)
+ __etoa( s );
+#endif
+ return ret;
+}
+#endif
+
+size_t lutil_localtime( char *s, size_t smax, const struct tm *tm, long delta )
+{
+ size_t ret;
+ char *p;
+
+ if ( smax < 16 ) { /* YYYYmmddHHMMSSZ */
+ return 0;
+ }
+
+#ifdef HAVE_EBCDIC
+/* We've been compiling in ASCII so far, but we want EBCDIC now since
+ * strftime only understands EBCDIC input.
+ */
+#pragma convlit(suspend)
+#endif
+ ret = strftime( s, smax, "%Y%m%d%H%M%SZ", tm );
+#ifdef HAVE_EBCDIC
+#pragma convlit(resume)
+ __etoa( s );
+#endif
+ if ( delta == 0 || ret == 0 ) {
+ return ret;
+ }
+
+ if ( smax < 20 ) { /* YYYYmmddHHMMSS+HHMM */
+ return 0;
+ }
+
+ p = s + 14;
+
+ if ( delta < 0 ) {
+ p[ 0 ] = '-';
+ delta = -delta;
+ } else {
+ p[ 0 ] = '+';
+ }
+ p++;
+
+ snprintf( p, smax - 15, "%02ld%02ld", delta / 3600,
+ ( delta % 3600 ) / 60 );
+
+ return ret + 4;
+}
+
+int lutil_tm2time( struct lutil_tm *tm, struct lutil_timet *tt )
+{
+ static int moffset[12] = {
+ 0, 31, 59, 90, 120,
+ 151, 181, 212, 243,
+ 273, 304, 334 };
+ int sec;
+
+ tt->tt_nsec = tm->tm_nsec;
+
+ /* special case 0000/01/01+00:00:00 is returned as zero */
+ if ( tm->tm_year == -1900 && tm->tm_mon == 0 && tm->tm_mday == 1 &&
+ tm->tm_hour == 0 && tm->tm_min == 0 && tm->tm_sec == 0 ) {
+ tt->tt_sec = 0;
+ tt->tt_gsec = 0;
+ return 0;
+ }
+
+ /* tm->tm_year is years since 1900 */
+ /* calculate days from years since 1970 (epoch) */
+ tt->tt_sec = tm->tm_year - 70;
+ tt->tt_sec *= 365L;
+
+ /* count leap days in preceding years */
+ tt->tt_sec += ((tm->tm_year -69) >> 2);
+
+ /* calculate days from months */
+ tt->tt_sec += moffset[tm->tm_mon];
+
+ /* add in this year's leap day, if any */
+ if (((tm->tm_year & 3) == 0) && (tm->tm_mon > 1)) {
+ tt->tt_sec ++;
+ }
+
+ /* add in days in this month */
+ tt->tt_sec += (tm->tm_mday - 1);
+
+ /* this function can handle a range of about 17408 years... */
+ /* 86400 seconds in a day, divided by 128 = 675 */
+ tt->tt_sec *= 675;
+
+ /* move high 7 bits into tt_gsec */
+ tt->tt_gsec = tt->tt_sec >> 25;
+ tt->tt_sec -= tt->tt_gsec << 25;
+
+ /* get hours */
+ sec = tm->tm_hour;
+
+ /* convert to minutes */
+ sec *= 60L;
+ sec += tm->tm_min;
+
+ /* convert to seconds */
+ sec *= 60L;
+ sec += tm->tm_sec;
+
+ /* add remaining seconds */
+ tt->tt_sec <<= 7;
+ tt->tt_sec += sec;
+
+ /* return success */
+ return 0;
+}
+
+/* Proleptic Gregorian Calendar, 1BCE = year 0 */
+
+int lutil_tm2gtime( struct lutil_tm *tm, struct lutil_timet *tt )
+{
+ static int moffset[12] = {
+ 0, 31, 59, 90, 120,
+ 151, 181, 212, 243,
+ 273, 304, 334 };
+ int sec, year;
+ long tmp;
+
+ tt->tt_nsec = tm->tm_nsec;
+
+ /* tm->tm_year is years since 1900 */
+ /* calculate days from 0000 */
+ year = tm->tm_year + 1900;
+ tmp = year * 365;
+
+ /* add in leap days */
+ sec = (year - 1) / 4;
+ tmp += sec;
+ sec /= 25;
+ tmp -= sec;
+ sec /= 4;
+ tmp += sec;
+ /* Year 0000 was a leap year */
+ if (year > 0)
+ tmp++;
+
+ /* calculate days from months */
+ tmp += moffset[tm->tm_mon];
+
+ /* add in this year's leap day, if any */
+ if (tm->tm_mon > 1) {
+ sec = (year % 4) ? 0 : (year % 100) ? 1 : (year % 400) ? 0 : 1;
+ tmp += sec;
+ }
+
+ /* add in days in this month */
+ tmp += (tm->tm_mday - 1);
+
+ /* this function can handle a range of about 17408 years... */
+ /* 86400 seconds in a day, divided by 128 = 675 */
+ tmp *= 675;
+
+ /* move high 7 bits into tt_gsec */
+ tt->tt_gsec = tmp >> 25;
+ tmp -= tt->tt_gsec << 25;
+
+ /* toggle sign bit, keep positive greater than negative */
+ tt->tt_gsec &= 0x7f;
+ tt->tt_gsec ^= 0x40;
+
+ /* get hours */
+ sec = tm->tm_hour;
+
+ /* convert to minutes */
+ sec *= 60L;
+ sec += tm->tm_min;
+
+ /* convert to seconds */
+ sec *= 60L;
+ sec += tm->tm_sec;
+
+ /* add remaining seconds */
+ tmp <<= 7;
+ tmp += sec;
+ tt->tt_sec = tmp;
+
+ /* return success */
+ return 0;
+}
+
+int lutil_parsetime( char *atm, struct lutil_tm *tm )
+{
+ while (atm && tm) {
+ char *ptr;
+ unsigned i, fracs;
+ int neg = 0;
+
+ if (*atm == '-') {
+ neg = 1;
+ atm++;
+ }
+ ptr = atm;
+
+ /* Is the stamp reasonably long? */
+ for (i=0; isdigit((unsigned char) atm[i]); i++);
+ if (i < sizeof("00000101000000")-1)
+ break;
+
+ /*
+ * parse the time into a struct tm
+ */
+ /* 4 digit year to year - 1900 */
+ tm->tm_year = *ptr++ - '0';
+ tm->tm_year *= 10; tm->tm_year += *ptr++ - '0';
+ tm->tm_year *= 10; tm->tm_year += *ptr++ - '0';
+ tm->tm_year *= 10; tm->tm_year += *ptr++ - '0';
+ if (neg)
+ tm->tm_year = -tm->tm_year;
+ tm->tm_year -= 1900;
+ /* month 01-12 to 0-11 */
+ tm->tm_mon = *ptr++ - '0';
+ tm->tm_mon *=10; tm->tm_mon += *ptr++ - '0';
+ if (tm->tm_mon < 1 || tm->tm_mon > 12) break;
+ tm->tm_mon--;
+
+ /* day of month 01-31 */
+ tm->tm_mday = *ptr++ - '0';
+ tm->tm_mday *=10; tm->tm_mday += *ptr++ - '0';
+ if (tm->tm_mday < 1 || tm->tm_mday > 31) break;
+
+ /* Hour 00-23 */
+ tm->tm_hour = *ptr++ - '0';
+ tm->tm_hour *=10; tm->tm_hour += *ptr++ - '0';
+ if (tm->tm_hour < 0 || tm->tm_hour > 23) break;
+
+ /* Minute 00-59 */
+ tm->tm_min = *ptr++ - '0';
+ tm->tm_min *=10; tm->tm_min += *ptr++ - '0';
+ if (tm->tm_min < 0 || tm->tm_min > 59) break;
+
+ /* Second 00-61 */
+ tm->tm_sec = *ptr++ - '0';
+ tm->tm_sec *=10; tm->tm_sec += *ptr++ - '0';
+ if (tm->tm_sec < 0 || tm->tm_sec > 61) break;
+
+ /* Fractions of seconds */
+ if ( *ptr == '.' ) {
+ ptr++;
+ for (i = 0, fracs = 0; isdigit((unsigned char) *ptr); ) {
+ i*=10; i+= *ptr++ - '0';
+ fracs++;
+ }
+ tm->tm_nsec = i;
+ if (i) {
+ for (i = fracs; i<9; i++)
+ tm->tm_nsec *= 10;
+ }
+ } else {
+ tm->tm_nsec = 0;
+ }
+ tm->tm_usub = 0;
+
+ /* Must be UTC */
+ if (*ptr != 'Z') break;
+
+ return 0;
+ }
+ return -1;
+}
+
+/* strcopy is like strcpy except it returns a pointer to the trailing NUL of
+ * the result string. This allows fast construction of catenated strings
+ * without the overhead of strlen/strcat.
+ */
+char *
+lutil_strcopy(
+ char *a,
+ const char *b
+)
+{
+ if (!a || !b)
+ return a;
+
+ while ((*a++ = *b++)) ;
+ return a-1;
+}
+
+/* strncopy is like strcpy except it returns a pointer to the trailing NUL of
+ * the result string. This allows fast construction of catenated strings
+ * without the overhead of strlen/strcat.
+ */
+char *
+lutil_strncopy(
+ char *a,
+ const char *b,
+ size_t n
+)
+{
+ if (!a || !b || n == 0)
+ return a;
+
+ while ((*a++ = *b++) && n-- > 0) ;
+ return a-1;
+}
+
+/* memcopy is like memcpy except it returns a pointer to the byte past
+ * the end of the result buffer, set to NULL. This allows fast construction
+ * of catenated buffers. Provided for API consistency with lutil_str*copy().
+ */
+char *
+lutil_memcopy(
+ char *a,
+ const char *b,
+ size_t n
+)
+{
+ AC_MEMCPY(a, b, n);
+ return a + n;
+}
+
+#ifndef HAVE_MKSTEMP
+int mkstemp( char * template )
+{
+#ifdef HAVE_MKTEMP
+ return open ( mktemp ( template ), O_RDWR|O_CREAT|O_EXCL, 0600 );
+#else
+ return -1;
+#endif
+}
+#endif
+
+#ifdef _MSC_VER
+/* Equivalent of MS CRT's _dosmaperr().
+ * @param lastError[in] Result of GetLastError().
+ */
+static errno_t win2errno(DWORD lastError)
+{
+ const struct {
+ DWORD windows_code;
+ errno_t errno_code;
+ } WIN2ERRNO_TABLE[] = {
+ { ERROR_SUCCESS, 0 },
+ { ERROR_FILE_NOT_FOUND, ENOENT },
+ { ERROR_PATH_NOT_FOUND, ENOENT },
+ { ERROR_TOO_MANY_OPEN_FILES, EMFILE },
+ { ERROR_ACCESS_DENIED, EACCES },
+ { ERROR_INVALID_HANDLE, EBADF },
+ { ERROR_NOT_ENOUGH_MEMORY, ENOMEM },
+ { ERROR_LOCK_VIOLATION, EACCES },
+ { ERROR_FILE_EXISTS, EEXIST },
+ { ERROR_INVALID_PARAMETER, EINVAL },
+ { ERROR_FILENAME_EXCED_RANGE, ENAMETOOLONG },
+ };
+ const unsigned int WIN2ERRNO_TABLE_SIZE = sizeof(WIN2ERRNO_TABLE) /
+sizeof(WIN2ERRNO_TABLE[0]);
+ const errno_t DEFAULT_ERRNO_ERROR = -1;
+ unsigned int i;
+
+ for (i = 0; i < WIN2ERRNO_TABLE_SIZE; ++i) {
+ if (WIN2ERRNO_TABLE[i].windows_code == lastError) {
+ return WIN2ERRNO_TABLE[i].errno_code;
+ }
+ }
+ return DEFAULT_ERRNO_ERROR;
+}
+
+struct dirent {
+ char *d_name;
+};
+typedef struct DIR {
+ HANDLE dir;
+ struct dirent data;
+ int first;
+ char buf[MAX_PATH+1];
+} DIR;
+DIR *opendir( char *path )
+{
+ char tmp[32768];
+ int len = strlen(path);
+ DIR *d;
+ HANDLE h;
+ WIN32_FIND_DATA data;
+
+ if (len+3 >= sizeof(tmp)) {
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+
+ strcpy(tmp, path);
+ tmp[len++] = '\\';
+ tmp[len++] = '*';
+ tmp[len] = '\0';
+
+ h = FindFirstFile( tmp, &data );
+
+ if ( h == INVALID_HANDLE_VALUE ) {
+ errno = win2errno( GetLastError());
+ return NULL;
+ }
+
+ d = ber_memalloc( sizeof(DIR) );
+ if ( !d )
+ return NULL;
+ d->dir = h;
+ d->data.d_name = d->buf;
+ d->first = 1;
+ strcpy(d->data.d_name, data.cFileName);
+ return d;
+}
+struct dirent *readdir(DIR *dir)
+{
+ WIN32_FIND_DATA data;
+
+ if (dir->first) {
+ dir->first = 0;
+ } else {
+ if (!FindNextFile(dir->dir, &data))
+ return NULL;
+ strcpy(dir->data.d_name, data.cFileName);
+ }
+ return &dir->data;
+}
+int closedir(DIR *dir)
+{
+ (void) FindClose(dir->dir);
+ ber_memfree(dir);
+ return 0;
+}
+#endif
+
+/*
+ * Memory Reverse Search
+ */
+void *
+(lutil_memrchr)(const void *b, int c, size_t n)
+{
+ if (n != 0) {
+ const unsigned char *s, *bb = b, cc = c;
+
+ for ( s = bb + n; s > bb; ) {
+ if ( *--s == cc ) {
+ return (void *) s;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+int
+lutil_atoix( int *v, const char *s, int x )
+{
+ char *next;
+ long i;
+
+ assert( s != NULL );
+ assert( v != NULL );
+
+ i = strtol( s, &next, x );
+ if ( next == s || next[ 0 ] != '\0' ) {
+ return -1;
+ }
+
+ if ( (long)(int)i != i ) {
+ return 1;
+ }
+
+ *v = (int)i;
+
+ return 0;
+}
+
+int
+lutil_atoux( unsigned *v, const char *s, int x )
+{
+ char *next;
+ unsigned long u;
+
+ assert( s != NULL );
+ assert( v != NULL );
+
+ /* strtoul() has an odd interface */
+ if ( s[ 0 ] == '-' ) {
+ return -1;
+ }
+
+ u = strtoul( s, &next, x );
+ if ( next == s || next[ 0 ] != '\0' ) {
+ return -1;
+ }
+
+ if ( (unsigned long)(unsigned)u != u ) {
+ return 1;
+ }
+
+ *v = u;
+
+ return 0;
+}
+
+int
+lutil_atolx( long *v, const char *s, int x )
+{
+ char *next;
+ long l;
+ int save_errno;
+
+ assert( s != NULL );
+ assert( v != NULL );
+
+ if ( isspace( s[ 0 ] ) ) {
+ return -1;
+ }
+
+ errno = 0;
+ l = strtol( s, &next, x );
+ save_errno = errno;
+ if ( next == s || next[ 0 ] != '\0' ) {
+ return -1;
+ }
+
+ if ( ( l == LONG_MIN || l == LONG_MAX ) && save_errno != 0 ) {
+ return -1;
+ }
+
+ *v = l;
+
+ return 0;
+}
+
+int
+lutil_atoulx( unsigned long *v, const char *s, int x )
+{
+ char *next;
+ unsigned long ul;
+ int save_errno;
+
+ assert( s != NULL );
+ assert( v != NULL );
+
+ /* strtoul() has an odd interface */
+ if ( s[ 0 ] == '-' || isspace( s[ 0 ] ) ) {
+ return -1;
+ }
+
+ errno = 0;
+ ul = strtoul( s, &next, x );
+ save_errno = errno;
+ if ( next == s || next[ 0 ] != '\0' ) {
+ return -1;
+ }
+
+ if ( ( ul == 0 || ul == ULONG_MAX ) && save_errno != 0 ) {
+ return -1;
+ }
+
+ *v = ul;
+
+ return 0;
+}
+
+#ifdef HAVE_LONG_LONG
+#if defined(HAVE_STRTOLL) || defined(HAVE_STRTOQ)
+int
+lutil_atollx( long long *v, const char *s, int x )
+{
+ char *next;
+ long long ll;
+ int save_errno;
+
+ assert( s != NULL );
+ assert( v != NULL );
+
+ if ( isspace( s[ 0 ] ) ) {
+ return -1;
+ }
+
+ errno = 0;
+#ifdef HAVE_STRTOLL
+ ll = strtoll( s, &next, x );
+#else /* HAVE_STRTOQ */
+ ll = (unsigned long long)strtoq( s, &next, x );
+#endif /* HAVE_STRTOQ */
+ save_errno = errno;
+ if ( next == s || next[ 0 ] != '\0' ) {
+ return -1;
+ }
+
+ /* LLONG_MIN, LLONG_MAX are C99 only */
+#if defined (LLONG_MIN) && defined(LLONG_MAX)
+ if ( ( ll == LLONG_MIN || ll == LLONG_MAX ) && save_errno != 0 ) {
+ return -1;
+ }
+#endif /* LLONG_MIN && LLONG_MAX */
+
+ *v = ll;
+
+ return 0;
+}
+#endif /* HAVE_STRTOLL || HAVE_STRTOQ */
+
+#if defined(HAVE_STRTOULL) || defined(HAVE_STRTOUQ)
+int
+lutil_atoullx( unsigned long long *v, const char *s, int x )
+{
+ char *next;
+ unsigned long long ull;
+ int save_errno;
+
+ assert( s != NULL );
+ assert( v != NULL );
+
+ /* strtoull() has an odd interface */
+ if ( s[ 0 ] == '-' || isspace( s[ 0 ] ) ) {
+ return -1;
+ }
+
+ errno = 0;
+#ifdef HAVE_STRTOULL
+ ull = strtoull( s, &next, x );
+#else /* HAVE_STRTOUQ */
+ ull = (unsigned long long)strtouq( s, &next, x );
+#endif /* HAVE_STRTOUQ */
+ save_errno = errno;
+ if ( next == s || next[ 0 ] != '\0' ) {
+ return -1;
+ }
+
+ /* ULLONG_MAX is C99 only */
+#if defined(ULLONG_MAX)
+ if ( ( ull == 0 || ull == ULLONG_MAX ) && save_errno != 0 ) {
+ return -1;
+ }
+#endif /* ULLONG_MAX */
+
+ *v = ull;
+
+ return 0;
+}
+#endif /* HAVE_STRTOULL || HAVE_STRTOUQ */
+#endif /* HAVE_LONG_LONG */
+
+/* Multiply an integer by 100000000 and add new */
+typedef struct lutil_int_decnum {
+ unsigned char *buf;
+ int bufsiz;
+ int beg;
+ int len;
+} lutil_int_decnum;
+
+#define FACTOR1 (100000000&0xffff)
+#define FACTOR2 (100000000>>16)
+
+static void
+scale( int new, lutil_int_decnum *prev, unsigned char *tmp )
+{
+ int i, j;
+ unsigned char *in = prev->buf+prev->beg;
+ unsigned int part;
+ unsigned char *out = tmp + prev->bufsiz - prev->len;
+
+ memset( tmp, 0, prev->bufsiz );
+ if ( prev->len ) {
+ for ( i = prev->len-1; i>=0; i-- ) {
+ part = in[i] * FACTOR1;
+ for ( j = i; part; j-- ) {
+ part += out[j];
+ out[j] = part & 0xff;
+ part >>= 8;
+ }
+ part = in[i] * FACTOR2;
+ for ( j = i-2; part; j-- ) {
+ part += out[j];
+ out[j] = part & 0xff;
+ part >>= 8;
+ }
+ }
+ j++;
+ prev->beg += j;
+ prev->len -= j;
+ }
+
+ out = tmp + prev->bufsiz;
+ i = 0;
+ do {
+ i--;
+ new += out[i];
+ out[i] = new & 0xff;
+ new >>= 8;
+ } while ( new );
+ i = -i;
+ if ( prev->len < i ) {
+ prev->beg = prev->bufsiz - i;
+ prev->len = i;
+ }
+ AC_MEMCPY( prev->buf+prev->beg, tmp+prev->beg, prev->len );
+}
+
+/* Convert unlimited length decimal or hex string to binary.
+ * Output buffer must be provided, bv_len must indicate buffer size
+ * Hex input can be "0x1234" or "'1234'H"
+ *
+ * Note: High bit of binary form is always the sign bit. If the number
+ * is supposed to be positive but has the high bit set, a zero byte
+ * is prepended. It is assumed that this has already been handled on
+ * any hex input.
+ */
+int
+lutil_str2bin( struct berval *in, struct berval *out, void *ctx )
+{
+ char *pin, *pout;
+ char *end;
+ int i, chunk, len, rc = 0, hex = 0;
+ if ( !out || !out->bv_val || out->bv_len < in->bv_len )
+ return -1;
+
+ pout = out->bv_val;
+ /* Leading "0x" for hex input */
+ if ( in->bv_len > 2 && in->bv_val[0] == '0' &&
+ ( in->bv_val[1] == 'x' || in->bv_val[1] == 'X' ) )
+ {
+ len = in->bv_len - 2;
+ pin = in->bv_val + 2;
+ hex = 1;
+ } else if ( in->bv_len > 3 && in->bv_val[0] == '\'' &&
+ in->bv_val[in->bv_len-2] == '\'' &&
+ in->bv_val[in->bv_len-1] == 'H' )
+ {
+ len = in->bv_len - 3;
+ pin = in->bv_val + 1;
+ hex = 1;
+ }
+ if ( hex ) {
+#define HEXMAX (2 * sizeof(long))
+ unsigned long l;
+ char tbuf[HEXMAX+1];
+
+ /* Convert a longword at a time, but handle leading
+ * odd bytes first
+ */
+ chunk = len % HEXMAX;
+ if ( !chunk )
+ chunk = HEXMAX;
+
+ while ( len ) {
+ int ochunk;
+ memcpy( tbuf, pin, chunk );
+ tbuf[chunk] = '\0';
+ errno = 0;
+ l = strtoul( tbuf, &end, 16 );
+ if ( errno )
+ return -1;
+ ochunk = (chunk + 1)/2;
+ for ( i = ochunk - 1; i >= 0; i-- ) {
+ pout[i] = l & 0xff;
+ l >>= 8;
+ }
+ pin += chunk;
+ pout += ochunk;
+ len -= chunk;
+ chunk = HEXMAX;
+ }
+ out->bv_len = pout - out->bv_val;
+ } else {
+ /* Decimal */
+#define DECMAX 8 /* 8 digits at a time */
+ char tmpbuf[64], *tmp;
+ lutil_int_decnum num;
+ int neg = 0;
+ long l;
+ char tbuf[DECMAX+1];
+
+ len = in->bv_len;
+ pin = in->bv_val;
+ num.buf = (unsigned char *)out->bv_val;
+ num.bufsiz = out->bv_len;
+ num.beg = num.bufsiz-1;
+ num.len = 0;
+ if ( pin[0] == '-' ) {
+ neg = 0xff;
+ len--;
+ pin++;
+ }
+
+ /* tmp must be at least as large as outbuf */
+ if ( out->bv_len > sizeof(tmpbuf)) {
+ tmp = ber_memalloc_x( out->bv_len, ctx );
+ } else {
+ tmp = tmpbuf;
+ }
+ chunk = len & (DECMAX-1);
+ if ( !chunk )
+ chunk = DECMAX;
+
+ while ( len ) {
+ memcpy( tbuf, pin, chunk );
+ tbuf[chunk] = '\0';
+ errno = 0;
+ l = strtol( tbuf, &end, 10 );
+ if ( errno ) {
+ rc = -1;
+ goto decfail;
+ }
+ scale( l, &num, (unsigned char *)tmp );
+ pin += chunk;
+ len -= chunk;
+ chunk = DECMAX;
+ }
+ /* Negate the result */
+ if ( neg ) {
+ unsigned char *ptr;
+
+ ptr = num.buf+num.beg;
+
+ /* flip all bits */
+ for ( i=0; i<num.len; i++ )
+ ptr[i] ^= 0xff;
+
+ /* add 1, with carry - overflow handled below */
+ while ( i-- && ! (ptr[i] = (ptr[i] + 1) & 0xff )) ;
+ }
+ /* Prepend sign byte if wrong sign bit */
+ if (( num.buf[num.beg] ^ neg ) & 0x80 ) {
+ num.beg--;
+ num.len++;
+ num.buf[num.beg] = neg;
+ }
+ if ( num.beg )
+ AC_MEMCPY( num.buf, num.buf+num.beg, num.len );
+ out->bv_len = num.len;
+decfail:
+ if ( tmp != tmpbuf ) {
+ ber_memfree_x( tmp, ctx );
+ }
+ }
+ return rc;
+}
+
+static char time_unit[] = "dhms";
+
+/* Used to parse and unparse time intervals, not timestamps */
+int
+lutil_parse_time(
+ const char *in,
+ unsigned long *tp )
+{
+ unsigned long t = 0;
+ char *s,
+ *next;
+ int sofar = -1,
+ scale[] = { 86400, 3600, 60, 1 };
+
+ *tp = 0;
+
+ for ( s = (char *)in; s[ 0 ] != '\0'; ) {
+ unsigned long u;
+ char *what;
+
+ /* strtoul() has an odd interface */
+ if ( s[ 0 ] == '-' ) {
+ return -1;
+ }
+
+ u = strtoul( s, &next, 10 );
+ if ( next == s ) {
+ return -1;
+ }
+
+ if ( next[ 0 ] == '\0' ) {
+ /* assume seconds */
+ t += u;
+ break;
+ }
+
+ what = strchr( time_unit, next[ 0 ] );
+ if ( what == NULL ) {
+ return -1;
+ }
+
+ if ( what - time_unit <= sofar ) {
+ return -1;
+ }
+
+ sofar = what - time_unit;
+ t += u * scale[ sofar ];
+
+ s = &next[ 1 ];
+ }
+
+ *tp = t;
+ return 0;
+}
+
+int
+lutil_unparse_time(
+ char *buf,
+ size_t buflen,
+ unsigned long t )
+{
+ int len, i;
+ unsigned long v[ 4 ];
+ char *ptr = buf;
+
+ v[ 0 ] = t/86400;
+ v[ 1 ] = (t%86400)/3600;
+ v[ 2 ] = (t%3600)/60;
+ v[ 3 ] = t%60;
+
+ for ( i = 0; i < 4; i++ ) {
+ if ( v[i] > 0 || ( i == 3 && ptr == buf ) ) {
+ len = snprintf( ptr, buflen, "%lu%c", v[ i ], time_unit[ i ] );
+ if ( len < 0 || (unsigned)len >= buflen ) {
+ return -1;
+ }
+ buflen -= len;
+ ptr += len;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * formatted print to string
+ *
+ * - if return code < 0, the error code returned by vsnprintf(3) is returned
+ *
+ * - if return code > 0, the buffer was not long enough;
+ * - if next is not NULL, *next will be set to buf + bufsize - 1
+ * - if len is not NULL, *len will contain the required buffer length
+ *
+ * - if return code == 0, the buffer was long enough;
+ * - if next is not NULL, *next will point to the end of the string printed so far
+ * - if len is not NULL, *len will contain the length of the string printed so far
+ */
+int
+lutil_snprintf( char *buf, ber_len_t bufsize, char **next, ber_len_t *len, LDAP_CONST char *fmt, ... )
+{
+ va_list ap;
+ int ret;
+
+ assert( buf != NULL );
+ assert( bufsize > 0 );
+ assert( fmt != NULL );
+
+ va_start( ap, fmt );
+ ret = vsnprintf( buf, bufsize, fmt, ap );
+ va_end( ap );
+
+ if ( ret < 0 ) {
+ return ret;
+ }
+
+ if ( len ) {
+ *len = ret;
+ }
+
+ if ( (unsigned) ret >= bufsize ) {
+ if ( next ) {
+ *next = &buf[ bufsize - 1 ];
+ }
+
+ return 1;
+ }
+
+ if ( next ) {
+ *next = &buf[ ret ];
+ }
+
+ return 0;
+}
diff --git a/libraries/liblutil/uuid.c b/libraries/liblutil/uuid.c
new file mode 100644
index 0000000..061b2a2
--- /dev/null
+++ b/libraries/liblutil/uuid.c
@@ -0,0 +1,460 @@
+/* uuid.c -- Universally Unique Identifier routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2000-2003 Kurt D. Zeilenga.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright 2000, John E. Schimmel, All rights reserved.
+ * This software is not subject to any license of Mirapoint, Inc.
+ *
+ * This is free software; you can redistribute and use it
+ * under the same terms as OpenLDAP itself.
+ */
+/* This work was initially developed by John E. Schimmel and adapted
+ * for inclusion in OpenLDAP Software by Kurt D. Zeilenga.
+ */
+
+/*
+ * Sorry this file is so scary, but it needs to run on a wide range of
+ * platforms. The only exported routine is lutil_uuidstr() which is all
+ * that LDAP cares about. It generates a new uuid and returns it in
+ * in string form.
+ */
+#include "portable.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <ac/stdlib.h>
+#include <ac/string.h> /* get memcmp() */
+
+#ifdef HAVE_UUID_TO_STR
+# include <sys/uuid.h>
+#elif defined( HAVE_UUID_GENERATE )
+# include <uuid/uuid.h>
+#elif defined( _WIN32 )
+# include <rpc.h>
+#else
+# include <ac/socket.h>
+# include <ac/time.h>
+# ifdef HAVE_SYS_SYSCTL_H
+# include <net/if.h>
+# include <sys/sysctl.h>
+# include <net/route.h>
+# endif
+#endif
+
+#include <lutil.h>
+
+/* not needed for Windows */
+#if !defined(HAVE_UUID_TO_STR) && !defined(HAVE_UUID_GENERATE) && !defined(_WIN32)
+static unsigned char *
+lutil_eaddr( void )
+{
+ static unsigned char zero[6];
+ static unsigned char eaddr[6];
+
+#ifdef HAVE_SYS_SYSCTL_H
+ size_t needed;
+ int mib[6];
+ char *buf, *next, *lim;
+ struct if_msghdr *ifm;
+ struct sockaddr_dl *sdl;
+
+ if (memcmp(eaddr, zero, sizeof(eaddr))) {
+ return eaddr;
+ }
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[3] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+
+ if (sysctl(mib, sizeof(mib), NULL, &needed, NULL, 0) < 0) {
+ return NULL;
+ }
+
+ buf = malloc(needed);
+ if( buf == NULL ) return NULL;
+
+ if (sysctl(mib, sizeof(mib), buf, &needed, NULL, 0) < 0) {
+ free(buf);
+ return NULL;
+ }
+
+ lim = buf + needed;
+ for (next = buf; next < lim; next += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)next;
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+
+ if ( sdl->sdl_family != AF_LINK || sdl->sdl_alen == 6 ) {
+ AC_MEMCPY(eaddr,
+ (unsigned char *)sdl->sdl_data + sdl->sdl_nlen,
+ sizeof(eaddr));
+ free(buf);
+ return eaddr;
+ }
+ }
+
+ free(buf);
+ return NULL;
+
+#elif defined( SIOCGIFADDR ) && defined( AFLINK )
+ char buf[sizeof(struct ifreq) * 32];
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ struct sockaddr *sa;
+ struct sockaddr_dl *sdl;
+ unsigned char *p;
+ int s, i;
+
+ if (memcmp(eaddr, zero, sizeof(eaddr))) {
+ return eaddr;
+ }
+
+ s = socket( AF_INET, SOCK_DGRAM, 0 );
+ if ( s < 0 ) {
+ return NULL;
+ }
+
+ ifc.ifc_len = sizeof( buf );
+ ifc.ifc_buf = buf;
+ memset( buf, 0, sizeof( buf ) );
+
+ i = ioctl( s, SIOCGIFCONF, (char *)&ifc );
+ close( s );
+
+ if( i < 0 ) {
+ return NULL;
+ }
+
+ for ( i = 0; i < ifc.ifc_len; ) {
+ ifr = (struct ifreq *)&ifc.ifc_buf[i];
+ sa = &ifr->ifr_addr;
+
+ if ( sa->sa_len > sizeof( ifr->ifr_addr ) ) {
+ i += sizeof( ifr->ifr_name ) + sa->sa_len;
+ } else {
+ i += sizeof( *ifr );
+ }
+
+ if ( sa->sa_family != AF_LINK ) {
+ continue;
+ }
+
+ sdl = (struct sockaddr_dl *)sa;
+
+ if ( sdl->sdl_alen == 6 ) {
+ AC_MEMCPY(eaddr,
+ (unsigned char *)sdl->sdl_data + sdl->sdl_nlen,
+ sizeof(eaddr));
+ return eaddr;
+ }
+ }
+
+ return NULL;
+
+#else
+ if (memcmp(eaddr, zero, sizeof(eaddr)) == 0) {
+ /* XXX - who knows? */
+ lutil_entropy( eaddr, sizeof(eaddr) );
+ eaddr[0] |= 0x01; /* turn it into a multicast address */
+ }
+
+ return eaddr;
+#endif
+}
+
+#if (ULONG_MAX >> 31 >> 31) > 1 || defined HAVE_LONG_LONG
+
+#if (ULONG_MAX >> 31 >> 31) > 1
+ typedef unsigned long UI64;
+ /* 100 usec intervals from 10/10/1582 to 1/1/1970 */
+# define UUID_TPLUS 0x01B21DD2138140ul
+#else
+ typedef unsigned long long UI64;
+# define UUID_TPLUS 0x01B21DD2138140ull
+#endif
+
+#define high32(i) ((unsigned long) ((i) >> 32))
+#define low32(i) ((unsigned long) (i) & 0xFFFFFFFFul)
+#define set_add64(res, i) ((res) += (i))
+#define set_add64l(res, i) ((res) += (i))
+#define mul64ll(i1, i2) ((UI64) (i1) * (i2))
+
+#else /* ! (ULONG_MAX >= 64 bits || HAVE_LONG_LONG) */
+
+typedef struct {
+ unsigned long high, low;
+} UI64;
+
+static const UI64 UUID_TPLUS = { 0x01B21Dul, 0xD2138140ul };
+
+#define high32(i) ((i).high)
+#define low32(i) ((i).low)
+
+/* res += ui64 */
+#define set_add64(res, ui64) \
+{ \
+ res.high += ui64.high; \
+ res.low = (res.low + ui64.low) & 0xFFFFFFFFul; \
+ if (res.low < ui64.low) res.high++; \
+}
+
+/* res += ul32 */
+#define set_add64l(res, ul32) \
+{ \
+ res.low = (res.low + ul32) & 0xFFFFFFFFul; \
+ if (res.low < ul32) res.high++; \
+}
+
+/* compute i1 * i2 */
+static UI64
+mul64ll(unsigned long i1, unsigned long i2)
+{
+ const unsigned int high1 = (i1 >> 16), low1 = (i1 & 0xffff);
+ const unsigned int high2 = (i2 >> 16), low2 = (i2 & 0xffff);
+
+ UI64 res;
+ unsigned long tmp;
+
+ res.high = (unsigned long) high1 * high2;
+ res.low = (unsigned long) low1 * low2;
+
+ tmp = (unsigned long) low1 * high2;
+ res.high += (tmp >> 16);
+ tmp = (tmp << 16) & 0xFFFFFFFFul;
+ res.low = (res.low + tmp) & 0xFFFFFFFFul;
+ if (res.low < tmp)
+ res.high++;
+
+ tmp = (unsigned long) low2 * high1;
+ res.high += (tmp >> 16);
+ tmp = (tmp << 16) & 0xFFFFFFFFul;
+ res.low = (res.low + tmp) & 0xFFFFFFFFul;
+ if (res.low < tmp)
+ res.high++;
+
+ return res;
+}
+
+#endif /* ULONG_MAX >= 64 bits || HAVE_LONG_LONG */
+
+#endif /* !HAVE_UUID_TO_STR && !HAVE_UUID_GENERATE && !_WIN32 */
+
+/*
+** All we really care about is an ISO UUID string. The format of a UUID is:
+** field octet note
+** time_low 0-3 low field of the timestamp
+** time_mid 4-5 middle field of timestamp
+** time_hi_and_version 6-7 high field of timestamp and
+** version number
+** clock_seq_hi_and_resv 8 high field of clock sequence
+** and variant
+** clock_seq_low 9 low field of clock sequence
+** node 10-15 spacially unique identifier
+**
+** We use DCE version one, and the DCE variant. Our unique identifier is
+** the first ethernet address on the system.
+*/
+size_t
+lutil_uuidstr( char *buf, size_t len )
+{
+#ifdef HAVE_UUID_TO_STR
+ uuid_t uu = {0};
+ unsigned rc;
+ char *s;
+ size_t l;
+
+ uuid_create( &uu, &rc );
+ if ( rc != uuid_s_ok ) {
+ return 0;
+ }
+
+ uuid_to_str( &uu, &s, &rc );
+ if ( rc != uuid_s_ok ) {
+ return 0;
+ }
+
+ l = strlen( s );
+ if ( l >= len ) {
+ free( s );
+ return 0;
+ }
+
+ strncpy( buf, s, len );
+ free( s );
+
+ return l;
+
+#elif defined( HAVE_UUID_GENERATE )
+ uuid_t uu;
+
+ uuid_generate( uu );
+ uuid_unparse_lower( uu, buf );
+ return strlen( buf );
+
+#elif defined( _WIN32 )
+ UUID uuid;
+ unsigned char *uuidstr;
+ size_t uuidlen;
+
+ if( UuidCreate( &uuid ) != RPC_S_OK ) {
+ return 0;
+ }
+
+ if( UuidToString( &uuid, &uuidstr ) != RPC_S_OK ) {
+ return 0;
+ }
+
+ uuidlen = strlen( uuidstr );
+ if( uuidlen >= len ) {
+ return 0;
+ }
+
+ strncpy( buf, uuidstr, len );
+ RpcStringFree( &uuidstr );
+
+ return uuidlen;
+
+#else
+ struct timeval tv;
+ UI64 tl;
+ unsigned char *nl;
+ unsigned short t2, t3, s1;
+ unsigned long t1, tl_high;
+ unsigned int rc;
+
+ /*
+ * Theoretically we should delay if seq wraps within 100usec but for now
+ * systems are not fast enough to worry about it.
+ */
+ static int inited = 0;
+ static unsigned short seq;
+
+ if (!inited) {
+ lutil_entropy( (unsigned char *) &seq, sizeof(seq) );
+ inited++;
+ }
+
+#ifdef HAVE_GETTIMEOFDAY
+ gettimeofday( &tv, 0 );
+#else
+ time( &tv.tv_sec );
+ tv.tv_usec = 0;
+#endif
+
+ tl = mul64ll(tv.tv_sec, 10000000UL);
+ set_add64l(tl, tv.tv_usec * 10UL);
+ set_add64(tl, UUID_TPLUS);
+
+ nl = lutil_eaddr();
+
+ t1 = low32(tl); /* time_low */
+ tl_high = high32(tl);
+ t2 = tl_high & 0xffff; /* time_mid */
+ t3 = ((tl_high >> 16) & 0x0fff) | 0x1000; /* time_hi_and_version */
+ s1 = ( ++seq & 0x1fff ) | 0x8000; /* clock_seq_and_reserved */
+
+ rc = snprintf( buf, len,
+ "%08lx-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x",
+ t1, (unsigned) t2, (unsigned) t3, (unsigned) s1,
+ (unsigned) nl[0], (unsigned) nl[1],
+ (unsigned) nl[2], (unsigned) nl[3],
+ (unsigned) nl[4], (unsigned) nl[5] );
+
+ return rc < len ? rc : 0;
+#endif
+}
+
+int
+lutil_uuidstr_from_normalized(
+ char *uuid,
+ size_t uuidlen,
+ char *buf,
+ size_t buflen )
+{
+ unsigned char nibble;
+ int i, d = 0;
+
+ assert( uuid != NULL );
+ assert( buf != NULL );
+
+ if ( uuidlen != 16 ) return -1;
+ if ( buflen < 36 ) return -1;
+
+ for ( i = 0; i < 16; i++ ) {
+ if ( i == 4 || i == 6 || i == 8 || i == 10 ) {
+ buf[(i<<1)+d] = '-';
+ d += 1;
+ }
+
+ nibble = (uuid[i] >> 4) & 0xF;
+ if ( nibble < 10 ) {
+ buf[(i<<1)+d] = nibble + '0';
+ } else {
+ buf[(i<<1)+d] = nibble - 10 + 'a';
+ }
+
+ nibble = (uuid[i]) & 0xF;
+ if ( nibble < 10 ) {
+ buf[(i<<1)+d+1] = nibble + '0';
+ } else {
+ buf[(i<<1)+d+1] = nibble - 10 + 'a';
+ }
+ }
+
+ if ( buflen > 36 ) buf[36] = '\0';
+ return 36;
+}
+
+#ifdef TEST
+int
+main(int argc, char **argv)
+{
+ char buf1[8], buf2[64];
+
+#ifndef HAVE_UUID_TO_STR
+ unsigned char *p = lutil_eaddr();
+
+ if( p ) {
+ printf( "Ethernet Address: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ (unsigned) p[0], (unsigned) p[1], (unsigned) p[2],
+ (unsigned) p[3], (unsigned) p[4], (unsigned) p[5]);
+ }
+#endif
+
+ if ( lutil_uuidstr( buf1, sizeof( buf1 ) ) ) {
+ printf( "UUID: %s\n", buf1 );
+ } else {
+ fprintf( stderr, "too short: %ld\n", (long) sizeof( buf1 ) );
+ }
+
+ if ( lutil_uuidstr( buf2, sizeof( buf2 ) ) ) {
+ printf( "UUID: %s\n", buf2 );
+ } else {
+ fprintf( stderr, "too short: %ld\n", (long) sizeof( buf2 ) );
+ }
+
+ if ( lutil_uuidstr( buf2, sizeof( buf2 ) ) ) {
+ printf( "UUID: %s\n", buf2 );
+ } else {
+ fprintf( stderr, "too short: %ld\n", (long) sizeof( buf2 ) );
+ }
+
+ return 0;
+}
+#endif
diff --git a/libraries/librewrite/Copyright b/libraries/librewrite/Copyright
new file mode 100644
index 0000000..64a25f5
--- /dev/null
+++ b/libraries/librewrite/Copyright
@@ -0,0 +1,23 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2000 Pierangelo Masarati, <ando@sys-net.it>
+ * All rights reserved.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Since few users ever read sources,
+ * credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software. Since few users
+ * ever read sources, credits should appear in the documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ ******************************************************************************/
diff --git a/libraries/librewrite/Makefile.in b/libraries/librewrite/Makefile.in
new file mode 100644
index 0000000..9e8dc3f
--- /dev/null
+++ b/libraries/librewrite/Makefile.in
@@ -0,0 +1,37 @@
+# LIBREWRITE
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## Copyright 2000-2001 Pierangelo Masarati <ando@sys-net.it>
+##
+
+SRCS = config.c context.c info.c ldapmap.c map.c params.c rule.c \
+ session.c subst.c var.c xmap.c \
+ parse.c rewrite.c
+XSRCS = version.c
+OBJS = config.o context.o info.o ldapmap.o map.o params.o rule.o \
+ session.o subst.o var.o xmap.o
+
+LDAP_INCDIR= ../../include
+LDAP_LIBDIR= ../../libraries
+
+LIBRARY = librewrite.a
+PROGRAMS = rewrite
+XLIBS = $(LIBRARY) $(LDAP_LIBLUTIL_A) \
+ $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+XXLIBS = $(SECURITY_LIBS) $(LUTIL_LIBS)
+XXXLIBS = $(LTHREAD_LIBS)
+
+rewrite: $(XLIBS) rewrite.o parse.o
+ $(LTLINK) -o $@ rewrite.o parse.o $(LIBS)
diff --git a/libraries/librewrite/RATIONALE b/libraries/librewrite/RATIONALE
new file mode 100644
index 0000000..c8fa386
--- /dev/null
+++ b/libraries/librewrite/RATIONALE
@@ -0,0 +1,2 @@
+The workings of the rewrite library are described in the
+REWRITING section of the slapd-meta(5) manual page.
diff --git a/libraries/librewrite/config.c b/libraries/librewrite/config.c
new file mode 100644
index 0000000..132c84e
--- /dev/null
+++ b/libraries/librewrite/config.c
@@ -0,0 +1,441 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#include "rewrite-int.h"
+#include "rewrite-map.h"
+
+/*
+ * Parses a plugin map
+ */
+static int
+rewrite_parse_builtin_map(
+ struct rewrite_info *info,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv
+);
+
+/*
+ * Parses a config line and takes actions to fit content in rewrite structure;
+ * lines handled are of the form:
+ *
+ * rewriteEngine {on|off}
+ * rewriteMaxPasses numPasses [numPassesPerRule]
+ * rewriteContext contextName [alias aliasedContextName]
+ * rewriteRule pattern substPattern [ruleFlags]
+ * rewriteMap mapType mapName [mapArgs]
+ * rewriteParam paramName paramValue
+ */
+int
+rewrite_parse(
+ struct rewrite_info *info,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv
+)
+{
+ int rc = -1;
+
+ assert( info != NULL );
+ assert( fname != NULL );
+ assert( argv != NULL );
+ assert( argc > 0 );
+
+ /*
+ * Switch on the rewrite engine
+ */
+ if ( strcasecmp( argv[ 0 ], "rewriteEngine" ) == 0 ) {
+ if ( argc < 2 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] rewriteEngine needs 'state'\n",
+ fname, lineno );
+ return -1;
+
+ } else if ( argc > 2 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] extra fields in rewriteEngine"
+ " will be discarded\n",
+ fname, lineno );
+ }
+
+ if ( strcasecmp( argv[ 1 ], "on" ) == 0 ) {
+ info->li_state = REWRITE_ON;
+
+ } else if ( strcasecmp( argv[ 1 ], "off" ) == 0 ) {
+ info->li_state = REWRITE_OFF;
+
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] unknown 'state' in rewriteEngine;"
+ " assuming 'on'\n",
+ fname, lineno );
+ info->li_state = REWRITE_ON;
+ }
+ rc = REWRITE_SUCCESS;
+
+ /*
+ * Alter max passes
+ */
+ } else if ( strcasecmp( argv[ 0 ], "rewriteMaxPasses" ) == 0 ) {
+ if ( argc < 2 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] rewriteMaxPasses needs 'value'\n",
+ fname, lineno );
+ return -1;
+ }
+
+ if ( lutil_atoi( &info->li_max_passes, argv[ 1 ] ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] unable to parse rewriteMaxPasses=\"%s\"\n",
+ fname, lineno, argv[ 1 ] );
+ return -1;
+ }
+
+ if ( info->li_max_passes <= 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] negative or null rewriteMaxPasses\n",
+ fname, lineno );
+ return -1;
+ }
+
+ if ( argc > 2 ) {
+ if ( lutil_atoi( &info->li_max_passes_per_rule, argv[ 2 ] ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] unable to parse rewriteMaxPassesPerRule=\"%s\"\n",
+ fname, lineno, argv[ 2 ] );
+ return -1;
+ }
+
+ if ( info->li_max_passes_per_rule <= 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] negative or null rewriteMaxPassesPerRule\n",
+ fname, lineno );
+ return -1;
+ }
+
+ } else {
+ info->li_max_passes_per_rule = info->li_max_passes;
+ }
+ rc = REWRITE_SUCCESS;
+
+ /*
+ * Start a new rewrite context and set current context
+ */
+ } else if ( strcasecmp( argv[ 0 ], "rewriteContext" ) == 0 ) {
+ if ( argc < 2 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] rewriteContext needs 'name'\n",
+ fname, lineno );
+ return -1;
+ }
+
+ /*
+ * Checks for existence (lots of contexts should be
+ * available by default ...)
+ */
+ rewrite_int_curr_context = rewrite_context_find( info, argv[ 1 ] );
+ if ( rewrite_int_curr_context == NULL ) {
+ rewrite_int_curr_context = rewrite_context_create( info,
+ argv[ 1 ] );
+ }
+ if ( rewrite_int_curr_context == NULL ) {
+ return -1;
+ }
+
+ if ( argc > 2 ) {
+
+ /*
+ * A context can alias another (e.g., the `builtin'
+ * contexts for backend operations, if not defined,
+ * alias the `default' rewrite context (with the
+ * notable exception of the searchResult context,
+ * which can be undefined)
+ */
+ if ( strcasecmp( argv[ 2 ], "alias" ) == 0 ) {
+ struct rewrite_context *aliased;
+
+ if ( argc == 3 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] rewriteContext"
+ " needs 'name' after"
+ " 'alias'\n",
+ fname, lineno );
+ return -1;
+
+ } else if ( argc > 4 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] extra fields in"
+ " rewriteContext"
+ " after aliased name"
+ " will be"
+ " discarded\n",
+ fname, lineno );
+ }
+
+ aliased = rewrite_context_find( info,
+ argv[ 3 ] );
+ if ( aliased == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] aliased"
+ " rewriteContext '%s'"
+ " does not exists\n",
+ fname, lineno,
+ argv[ 3 ] );
+ return -1;
+ }
+
+ rewrite_int_curr_context->lc_alias = aliased;
+ rewrite_int_curr_context = aliased;
+
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] extra fields"
+ " in rewriteContext"
+ " will be discarded\n",
+ fname, lineno );
+ }
+ }
+ rc = REWRITE_SUCCESS;
+
+ /*
+ * Compile a rule in current context
+ */
+ } else if ( strcasecmp( argv[ 0 ], "rewriteRule" ) == 0 ) {
+ if ( argc < 3 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] rewriteRule needs 'pattern'"
+ " 'subst' ['flags']\n",
+ fname, lineno );
+ return -1;
+
+ } else if ( argc > 4 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] extra fields in rewriteRule"
+ " will be discarded\n",
+ fname, lineno );
+ }
+
+ if ( rewrite_int_curr_context == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] rewriteRule outside a"
+ " context; will add to default\n",
+ fname, lineno );
+ rewrite_int_curr_context = rewrite_context_find( info,
+ REWRITE_DEFAULT_CONTEXT );
+
+ /*
+ * Default context MUST exist in a properly initialized
+ * struct rewrite_info
+ */
+ assert( rewrite_int_curr_context != NULL );
+ }
+
+ rc = rewrite_rule_compile( info, rewrite_int_curr_context, argv[ 1 ],
+ argv[ 2 ], ( argc == 4 ? argv[ 3 ] : "" ) );
+
+ /*
+ * Add a plugin map to the map tree
+ */
+ } else if ( strcasecmp( argv[ 0 ], "rewriteMap" ) == 0 ) {
+ if ( argc < 3 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] rewriteMap needs at least 'type'"
+ " and 'name' ['args']\n",
+ fname, lineno );
+ return -1;
+ }
+
+ rc = rewrite_parse_builtin_map( info, fname, lineno,
+ argc, argv );
+
+ /*
+ * Set the value of a global scope parameter
+ */
+ } else if ( strcasecmp( argv[ 0 ], "rewriteParam" ) == 0 ) {
+ if ( argc < 3 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] rewriteParam needs 'name'"
+ " and 'value'\n",
+ fname, lineno );
+ return -1;
+ }
+
+ rc = rewrite_param_set( info, argv[ 1 ], argv[ 2 ] );
+
+ /*
+ * Error
+ */
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] unknown command '%s'\n",
+ fname, lineno, argv[ 0 ] );
+ return -1;
+ }
+
+ return rc;
+}
+
+/*
+ * Compares two maps
+ */
+static int
+rewrite_builtin_map_cmp(
+ const void *c1,
+ const void *c2
+)
+{
+ const struct rewrite_builtin_map *m1, *m2;
+
+ m1 = ( const struct rewrite_builtin_map * )c1;
+ m2 = ( const struct rewrite_builtin_map * )c2;
+
+ assert( m1 != NULL );
+ assert( m2 != NULL );
+ assert( m1->lb_name != NULL );
+ assert( m2->lb_name != NULL );
+
+ return strcasecmp( m1->lb_name, m2->lb_name );
+}
+
+/*
+ * Duplicate map ?
+ */
+static int
+rewrite_builtin_map_dup(
+ void *c1,
+ void *c2
+)
+{
+ struct rewrite_builtin_map *m1, *m2;
+
+ m1 = ( struct rewrite_builtin_map * )c1;
+ m2 = ( struct rewrite_builtin_map * )c2;
+
+ assert( m1 != NULL );
+ assert( m2 != NULL );
+ assert( m1->lb_name != NULL );
+ assert( m2->lb_name != NULL );
+
+ return ( strcasecmp( m1->lb_name, m2->lb_name ) == 0 ? -1 : 0 );
+}
+
+/*
+ * Adds a map to the info map tree
+ */
+static int
+rewrite_builtin_map_insert(
+ struct rewrite_info *info,
+ struct rewrite_builtin_map *map
+)
+{
+ /*
+ * May need a mutex?
+ */
+ return ldap_avl_insert( &info->li_maps, ( caddr_t )map,
+ rewrite_builtin_map_cmp,
+ rewrite_builtin_map_dup );
+}
+
+/*
+ * Retrieves a map
+ */
+struct rewrite_builtin_map *
+rewrite_builtin_map_find(
+ struct rewrite_info *info,
+ const char *name
+)
+{
+ struct rewrite_builtin_map tmp;
+
+ assert( info != NULL );
+ assert( name != NULL );
+
+ tmp.lb_name = ( char * )name;
+
+ return ( struct rewrite_builtin_map * )ldap_avl_find( info->li_maps,
+ ( caddr_t )&tmp, rewrite_builtin_map_cmp );
+}
+
+/*
+ * Parses a plugin map
+ */
+static int
+rewrite_parse_builtin_map(
+ struct rewrite_info *info,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv
+)
+{
+ struct rewrite_builtin_map *map;
+
+#define MAP_TYPE 1
+#define MAP_NAME 2
+
+ assert( info != NULL );
+ assert( fname != NULL );
+ assert( argc > 2 );
+ assert( argv != NULL );
+ assert( strcasecmp( argv[ 0 ], "rewriteMap" ) == 0 );
+
+ map = calloc( sizeof( struct rewrite_builtin_map ), 1 );
+ if ( map == NULL ) {
+ return REWRITE_ERR;
+ }
+
+ map->lb_name = strdup( argv[ MAP_NAME ] );
+ if ( map->lb_name == NULL ) {
+ free( map );
+ return REWRITE_ERR;
+ }
+
+ /*
+ * Built-in ldap map
+ */
+ if (( map->lb_mapper = rewrite_mapper_find( argv[ MAP_TYPE ] ))) {
+ map->lb_type = REWRITE_BUILTIN_MAP;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ if ( ldap_pvt_thread_mutex_init( & map->lb_mutex ) ) {
+ free( map->lb_name );
+ free( map );
+ return REWRITE_ERR;
+ }
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ map->lb_private = map->lb_mapper->rm_config( fname, lineno,
+ argc - 3, argv + 3 );
+
+ /*
+ * Error
+ */
+ } else {
+ free( map );
+ Debug( LDAP_DEBUG_ANY, "[%s:%d] unknown map type\n",
+ fname, lineno );
+ return -1;
+ }
+
+ return rewrite_builtin_map_insert( info, map );
+}
diff --git a/libraries/librewrite/context.c b/libraries/librewrite/context.c
new file mode 100644
index 0000000..78ad6c9
--- /dev/null
+++ b/libraries/librewrite/context.c
@@ -0,0 +1,474 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#include "rewrite-int.h"
+
+/*
+ * Compares two struct rewrite_context based on the name;
+ * used by avl stuff
+ */
+static int
+rewrite_context_cmp(
+ const void *c1,
+ const void *c2
+)
+{
+ const struct rewrite_context *lc1, *lc2;
+
+ lc1 = (const struct rewrite_context *)c1;
+ lc2 = (const struct rewrite_context *)c2;
+
+ assert( c1 != NULL );
+ assert( c2 != NULL );
+ assert( lc1->lc_name != NULL );
+ assert( lc2->lc_name != NULL );
+
+ return strcasecmp( lc1->lc_name, lc2->lc_name );
+}
+
+/*
+ * Returns -1 in case a duplicate struct rewrite_context
+ * has been inserted; used by avl stuff
+ */
+static int
+rewrite_context_dup(
+ void *c1,
+ void *c2
+ )
+{
+ struct rewrite_context *lc1, *lc2;
+
+ lc1 = (struct rewrite_context *)c1;
+ lc2 = (struct rewrite_context *)c2;
+
+ assert( c1 != NULL );
+ assert( c2 != NULL );
+ assert( lc1->lc_name != NULL );
+ assert( lc2->lc_name != NULL );
+
+ return( strcasecmp( lc1->lc_name, lc2->lc_name) == 0 ? -1 : 0 );
+}
+
+/*
+ * Finds the context named rewriteContext in the context tree
+ */
+struct rewrite_context *
+rewrite_context_find(
+ struct rewrite_info *info,
+ const char *rewriteContext
+)
+{
+ struct rewrite_context *context, c;
+
+ assert( info != NULL );
+ assert( rewriteContext != NULL );
+
+ /*
+ * Fetches the required rewrite context
+ */
+ c.lc_name = (char *)rewriteContext;
+ context = (struct rewrite_context *)ldap_avl_find( info->li_context,
+ (caddr_t)&c, rewrite_context_cmp );
+ if ( context == NULL ) {
+ return NULL;
+ }
+
+ /*
+ * De-aliases the context if required
+ */
+ if ( context->lc_alias ) {
+ return context->lc_alias;
+ }
+
+ return context;
+}
+
+/*
+ * Creates a new context called rewriteContext and stores in into the tree
+ */
+struct rewrite_context *
+rewrite_context_create(
+ struct rewrite_info *info,
+ const char *rewriteContext
+)
+{
+ struct rewrite_context *context;
+ int rc;
+
+ assert( info != NULL );
+ assert( rewriteContext != NULL );
+
+ context = calloc( sizeof( struct rewrite_context ), 1 );
+ if ( context == NULL ) {
+ return NULL;
+ }
+
+ /*
+ * Context name
+ */
+ context->lc_name = strdup( rewriteContext );
+ if ( context->lc_name == NULL ) {
+ free( context );
+ return NULL;
+ }
+
+ /*
+ * The first, empty rule
+ */
+ context->lc_rule = calloc( sizeof( struct rewrite_rule ), 1 );
+ if ( context->lc_rule == NULL ) {
+ free( context->lc_name );
+ free( context );
+ return NULL;
+ }
+ memset( context->lc_rule, 0, sizeof( struct rewrite_rule ) );
+
+ /*
+ * Add context to tree
+ */
+ rc = ldap_avl_insert( &info->li_context, (caddr_t)context,
+ rewrite_context_cmp, rewrite_context_dup );
+ if ( rc == -1 ) {
+ free( context->lc_rule );
+ free( context->lc_name );
+ free( context );
+ return NULL;
+ }
+
+ return context;
+}
+
+/*
+ * Finds the next rule according to a goto action statement,
+ * or null in case of error.
+ * Helper for rewrite_context_apply.
+ */
+static struct rewrite_rule *
+rewrite_action_goto(
+ struct rewrite_action *action,
+ struct rewrite_rule *rule
+)
+{
+ int n;
+
+ assert( action != NULL );
+ assert( action->la_args != NULL );
+ assert( rule != NULL );
+
+ n = ((int *)action->la_args)[ 0 ];
+
+ if ( n > 0 ) {
+ for ( ; n > 1 && rule != NULL ; n-- ) {
+ rule = rule->lr_next;
+ }
+ } else if ( n <= 0 ) {
+ for ( ; n < 1 && rule != NULL ; n++ ) {
+ rule = rule->lr_prev;
+ }
+ }
+
+ return rule;
+}
+
+/*
+ * Rewrites string according to context; may return:
+ * OK: fine; if *result != NULL rule matched and rewrite succeeded.
+ * STOP: fine, rule matched; stop processing following rules
+ * UNWILL: rule matched; force 'unwilling to perform'
+ */
+int
+rewrite_context_apply(
+ struct rewrite_info *info,
+ struct rewrite_op *op,
+ struct rewrite_context *context,
+ const char *string,
+ char **result
+)
+{
+ struct rewrite_rule *rule;
+ char *s, *res = NULL;
+ int return_code = REWRITE_REGEXEC_OK;
+
+ assert( info != NULL );
+ assert( op != NULL );
+ assert( context != NULL );
+ assert( context->lc_rule != NULL );
+ assert( string != NULL );
+ assert( result != NULL );
+
+ op->lo_depth++;
+
+ Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply"
+ " [depth=%d] string='%s'\n",
+ op->lo_depth, string );
+ assert( op->lo_depth > 0 );
+
+ s = (char *)string;
+
+ for ( rule = context->lc_rule->lr_next;
+ rule != NULL && op->lo_num_passes < info->li_max_passes;
+ rule = rule->lr_next, op->lo_num_passes++ ) {
+ int rc;
+
+ /*
+ * Apply a single rule
+ */
+ rc = rewrite_rule_apply( info, op, rule, s, &res );
+
+ /*
+ * A rule may return:
+ * OK with result != NULL if matched
+ * ERR if anything was wrong
+ * UNWILLING if the server should drop the request
+ * the latter case in honored immediately;
+ * the other two may require some special actions to take
+ * place.
+ */
+ switch ( rc ) {
+
+ case REWRITE_REGEXEC_ERR:
+ Debug( LDAP_DEBUG_ANY, "==> rewrite_context_apply"
+ " error ...\n" );
+
+ /*
+ * Checks for special actions to be taken
+ * in case of error ...
+ */
+ if ( rule->lr_action != NULL ) {
+ struct rewrite_action *action;
+ int do_continue = 0;
+
+ for ( action = rule->lr_action;
+ action != NULL;
+ action = action->la_next ) {
+ switch ( action->la_type ) {
+
+ /*
+ * This action takes precedence
+ * over the others in case of failure
+ */
+ case REWRITE_ACTION_IGNORE_ERR:
+ Debug( LDAP_DEBUG_ANY,
+ "==> rewrite_context_apply"
+ " ignoring error ...\n" );
+ do_continue = 1;
+ break;
+
+ /*
+ * Goto is honored only if it comes
+ * after ignore error
+ */
+ case REWRITE_ACTION_GOTO:
+ if ( do_continue ) {
+ rule = rewrite_action_goto( action, rule );
+ if ( rule == NULL ) {
+ return_code = REWRITE_REGEXEC_ERR;
+ goto rc_end_of_context;
+ }
+ }
+ break;
+
+ /*
+ * Other actions are ignored
+ */
+ default:
+ break;
+ }
+ }
+
+ if ( do_continue ) {
+ if ( rule->lr_next == NULL ) {
+ res = s;
+ }
+ goto rc_continue;
+ }
+ }
+
+ /*
+ * Default behavior is to bail out ...
+ */
+ return_code = REWRITE_REGEXEC_ERR;
+ goto rc_end_of_context;
+
+ /*
+ * OK means there were no errors or special return codes;
+ * if res is defined, it means the rule matched and we
+ * got a successful rewriting
+ */
+ case REWRITE_REGEXEC_OK:
+
+ /*
+ * It matched! Check for actions ...
+ */
+ if ( res != NULL ) {
+ struct rewrite_action *action;
+
+ if ( s != string && s != res ) {
+ free( s );
+ }
+ s = res;
+
+ for ( action = rule->lr_action;
+ action != NULL;
+ action = action->la_next ) {
+
+ switch ( action->la_type ) {
+
+ /*
+ * This ends the rewrite context
+ * successfully
+ */
+ case REWRITE_ACTION_STOP:
+ goto rc_end_of_context;
+
+ /*
+ * This instructs the server to return
+ * an `unwilling to perform' error
+ * message
+ */
+ case REWRITE_ACTION_UNWILLING:
+ return_code = REWRITE_REGEXEC_UNWILLING;
+ goto rc_end_of_context;
+
+ /*
+ * This causes the processing to
+ * jump n rules back and forth
+ */
+ case REWRITE_ACTION_GOTO:
+ rule = rewrite_action_goto( action, rule );
+ if ( rule == NULL ) {
+ return_code = REWRITE_REGEXEC_ERR;
+ goto rc_end_of_context;
+ }
+ break;
+
+ /*
+ * This ends the rewrite context
+ * and returns a user-defined
+ * error code
+ */
+ case REWRITE_ACTION_USER:
+ return_code = ((int *)action->la_args)[ 0 ];
+ goto rc_end_of_context;
+
+ default:
+ /* ... */
+ break;
+ }
+ }
+
+ /*
+ * If result was OK and string didn't match,
+ * in case of last rule we need to set the
+ * result back to the string
+ */
+ } else if ( rule->lr_next == NULL ) {
+ res = s;
+ }
+
+ break;
+
+ /*
+ * A STOP has propagated ...
+ */
+ case REWRITE_REGEXEC_STOP:
+ goto rc_end_of_context;
+
+ /*
+ * This will instruct the server to return
+ * an `unwilling to perform' error message
+ */
+ case REWRITE_REGEXEC_UNWILLING:
+ return_code = REWRITE_REGEXEC_UNWILLING;
+ goto rc_end_of_context;
+
+ /*
+ * A user-defined error code has propagated ...
+ */
+ default:
+ assert( rc >= REWRITE_REGEXEC_USER );
+ goto rc_end_of_context;
+
+ }
+
+rc_continue:; /* sent here by actions that require to continue */
+
+ }
+
+rc_end_of_context:;
+ *result = res;
+
+ Debug( LDAP_DEBUG_TRACE, "==> rewrite_context_apply"
+ " [depth=%d] res={%d,'%s'}\n",
+ op->lo_depth, return_code, ( res ? res : "NULL" ) );
+
+ assert( op->lo_depth > 0 );
+ op->lo_depth--;
+
+ return return_code;
+}
+
+void
+rewrite_context_free(
+ void *tmp
+)
+{
+ struct rewrite_context *context = (struct rewrite_context *)tmp;
+
+ assert( tmp != NULL );
+
+ rewrite_context_destroy( &context );
+}
+
+int
+rewrite_context_destroy(
+ struct rewrite_context **pcontext
+)
+{
+ struct rewrite_context *context;
+ struct rewrite_rule *r;
+
+ assert( pcontext != NULL );
+ assert( *pcontext != NULL );
+
+ context = *pcontext;
+
+ assert( context->lc_rule != NULL );
+
+ for ( r = context->lc_rule->lr_next; r; ) {
+ struct rewrite_rule *cr = r;
+
+ r = r->lr_next;
+ rewrite_rule_destroy( &cr );
+ }
+
+ free( context->lc_rule );
+ context->lc_rule = NULL;
+
+ assert( context->lc_name != NULL );
+ free( context->lc_name );
+ context->lc_name = NULL;
+
+ free( context );
+ *pcontext = NULL;
+
+ return 0;
+}
diff --git a/libraries/librewrite/info.c b/libraries/librewrite/info.c
new file mode 100644
index 0000000..9db006d
--- /dev/null
+++ b/libraries/librewrite/info.c
@@ -0,0 +1,284 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#include "rewrite-int.h"
+
+/*
+ * Global data
+ */
+
+/*
+ * This becomes the running context for subsequent calls to
+ * rewrite_parse; it can be altered only by a
+ * rewriteContext config line or by a change in info.
+ */
+struct rewrite_context *rewrite_int_curr_context = NULL;
+
+/*
+ * Inits the info
+ */
+struct rewrite_info *
+rewrite_info_init(
+ int mode
+)
+{
+ struct rewrite_info *info;
+ struct rewrite_context *context;
+
+ switch ( mode ) {
+ case REWRITE_MODE_ERR:
+ case REWRITE_MODE_OK:
+ case REWRITE_MODE_COPY_INPUT:
+ case REWRITE_MODE_USE_DEFAULT:
+ break;
+ default:
+ mode = REWRITE_MODE_USE_DEFAULT;
+ break;
+ /* return NULL */
+ }
+
+ /*
+ * Resets the running context for parsing ...
+ */
+ rewrite_int_curr_context = NULL;
+
+ info = calloc( sizeof( struct rewrite_info ), 1 );
+ if ( info == NULL ) {
+ return NULL;
+ }
+
+ info->li_state = REWRITE_DEFAULT;
+ info->li_max_passes = REWRITE_MAX_PASSES;
+ info->li_max_passes_per_rule = REWRITE_MAX_PASSES;
+ info->li_rewrite_mode = mode;
+
+ /*
+ * Add the default (empty) rule
+ */
+ context = rewrite_context_create( info, REWRITE_DEFAULT_CONTEXT );
+ if ( context == NULL ) {
+ free( info );
+ return NULL;
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ if ( ldap_pvt_thread_rdwr_init( &info->li_cookies_mutex ) ) {
+ ldap_avl_free( info->li_context, rewrite_context_free );
+ free( info );
+ return NULL;
+ }
+ if ( ldap_pvt_thread_rdwr_init( &info->li_params_mutex ) ) {
+ ldap_pvt_thread_rdwr_destroy( &info->li_cookies_mutex );
+ ldap_avl_free( info->li_context, rewrite_context_free );
+ free( info );
+ return NULL;
+ }
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ return info;
+}
+
+/*
+ * Cleans up the info structure
+ */
+int
+rewrite_info_delete(
+ struct rewrite_info **pinfo
+)
+{
+ struct rewrite_info *info;
+
+ assert( pinfo != NULL );
+ assert( *pinfo != NULL );
+
+ info = *pinfo;
+
+ if ( info->li_context ) {
+ ldap_avl_free( info->li_context, rewrite_context_free );
+ }
+ info->li_context = NULL;
+
+ if ( info->li_maps ) {
+ ldap_avl_free( info->li_maps, rewrite_builtin_map_free );
+ }
+ info->li_maps = NULL;
+
+ rewrite_session_destroy( info );
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_destroy( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ rewrite_param_destroy( info );
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_destroy( &info->li_params_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ free( info );
+ *pinfo = NULL;
+
+ return REWRITE_SUCCESS;
+}
+
+/*
+ * Rewrites a string according to context.
+ * If the engine is off, OK is returned, but the return string will be NULL.
+ * In case of 'unwilling to perform', UNWILLING is returned, and the
+ * return string will also be null. The same in case of error.
+ * Otherwise, OK is returned, and result will hold a newly allocated string
+ * with the rewriting.
+ *
+ * What to do in case of non-existing rewrite context is still an issue.
+ * Four possibilities:
+ * - error,
+ * - ok with NULL result,
+ * - ok with copy of string as result,
+ * - use the default rewrite context.
+ */
+int
+rewrite(
+ struct rewrite_info *info,
+ const char *rewriteContext,
+ const char *string,
+ char **result
+)
+{
+ return rewrite_session( info, rewriteContext,
+ string, NULL, result );
+}
+
+int
+rewrite_session(
+ struct rewrite_info *info,
+ const char *rewriteContext,
+ const char *string,
+ const void *cookie,
+ char **result
+)
+{
+ struct rewrite_context *context;
+ struct rewrite_op op = { 0, 0, NULL, NULL, NULL };
+ int rc;
+
+ assert( info != NULL );
+ assert( rewriteContext != NULL );
+ assert( string != NULL );
+ assert( result != NULL );
+
+ /*
+ * cookie can be null; means: don't care about session stuff
+ */
+
+ *result = NULL;
+ op.lo_cookie = cookie;
+
+ /*
+ * Engine not on means no failure, but explicit no rewriting
+ */
+ if ( info->li_state != REWRITE_ON ) {
+ rc = REWRITE_REGEXEC_OK;
+ goto rc_return;
+ }
+
+ /*
+ * Undefined context means no rewriting also
+ * (conservative, are we sure it's what we want?)
+ */
+ context = rewrite_context_find( info, rewriteContext );
+ if ( context == NULL ) {
+ switch ( info->li_rewrite_mode ) {
+ case REWRITE_MODE_ERR:
+ rc = REWRITE_REGEXEC_ERR;
+ goto rc_return;
+
+ case REWRITE_MODE_OK:
+ rc = REWRITE_REGEXEC_OK;
+ goto rc_return;
+
+ case REWRITE_MODE_COPY_INPUT:
+ *result = strdup( string );
+ rc = ( *result != NULL ) ? REWRITE_REGEXEC_OK : REWRITE_REGEXEC_ERR;
+ goto rc_return;
+
+ case REWRITE_MODE_USE_DEFAULT:
+ context = rewrite_context_find( info,
+ REWRITE_DEFAULT_CONTEXT );
+ break;
+ }
+ }
+
+#if 0 /* FIXME: not used anywhere! (debug? then, why strdup?) */
+ op.lo_string = strdup( string );
+ if ( op.lo_string == NULL ) {
+ rc = REWRITE_REGEXEC_ERR;
+ goto rc_return;
+ }
+#endif
+
+ /*
+ * Applies rewrite context
+ */
+ rc = rewrite_context_apply( info, &op, context, string, result );
+ assert( op.lo_depth == 0 );
+
+#if 0 /* FIXME: not used anywhere! (debug? then, why strdup?) */
+ free( op.lo_string );
+#endif
+
+ switch ( rc ) {
+ /*
+ * Success
+ */
+ case REWRITE_REGEXEC_OK:
+ case REWRITE_REGEXEC_STOP:
+ /*
+ * If rewrite succeeded return OK regardless of how
+ * the successful rewriting was obtained!
+ */
+ rc = REWRITE_REGEXEC_OK;
+ break;
+
+
+ /*
+ * Internal or forced error, return = NULL; rc already OK.
+ */
+ case REWRITE_REGEXEC_UNWILLING:
+ case REWRITE_REGEXEC_ERR:
+ if ( *result != NULL ) {
+ if ( *result != string ) {
+ free( *result );
+ }
+ *result = NULL;
+ }
+
+ default:
+ break;
+ }
+
+rc_return:;
+ if ( op.lo_vars ) {
+ rewrite_var_delete( op.lo_vars );
+ }
+
+ return rc;
+}
+
diff --git a/libraries/librewrite/ldapmap.c b/libraries/librewrite/ldapmap.c
new file mode 100644
index 0000000..7d0252b
--- /dev/null
+++ b/libraries/librewrite/ldapmap.c
@@ -0,0 +1,454 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#define LDAP_DEPRECATED 1
+#include "rewrite-int.h"
+#include "rewrite-map.h"
+
+typedef enum {
+ MAP_LDAP_UNKNOWN,
+ MAP_LDAP_EVERYTIME,
+ MAP_LDAP_NOW,
+ MAP_LDAP_LATER
+} bindwhen_t;
+
+/*
+ * LDAP map data structure
+ */
+struct ldap_map_data {
+ char *lm_url;
+ LDAPURLDesc *lm_lud;
+ int lm_version;
+ char *lm_binddn;
+ struct berval lm_cred;
+
+ bindwhen_t lm_when;
+
+ LDAP *lm_ld;
+
+ int lm_wantdn;
+ char *lm_attrs[ 2 ];
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_t lm_mutex;
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+};
+
+static void
+map_ldap_free(
+ struct ldap_map_data *data
+)
+{
+ assert( data != NULL );
+
+ if ( data->lm_url != NULL ) {
+ free( data->lm_url );
+ }
+
+ if ( data->lm_lud != NULL ) {
+ ldap_free_urldesc( data->lm_lud );
+ }
+
+ if ( data->lm_binddn != NULL ) {
+ free( data->lm_binddn );
+ }
+
+ if ( data->lm_cred.bv_val != NULL ) {
+ memset( data->lm_cred.bv_val, 0, data->lm_cred.bv_len );
+ free( data->lm_cred.bv_val );
+ data->lm_cred.bv_val = NULL;
+ data->lm_cred.bv_len = 0;
+ }
+
+ if ( data->lm_when != MAP_LDAP_EVERYTIME && data->lm_ld != NULL ) {
+ ldap_unbind_ext( data->lm_ld, NULL, NULL );
+ }
+
+ free( data );
+}
+
+static void *
+map_ldap_parse(
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv
+)
+{
+ struct ldap_map_data *data;
+ char *p, *uri;
+
+ assert( fname != NULL );
+ assert( argv != NULL );
+
+ data = calloc( sizeof( struct ldap_map_data ), 1 );
+ if ( data == NULL ) {
+ return NULL;
+ }
+
+ if ( argc < 1 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] ldap map needs URI\n",
+ fname, lineno );
+ free( data );
+ return NULL;
+ }
+
+ uri = argv[ 0 ];
+ if ( strncasecmp( uri, "uri=", STRLENOF( "uri=" ) ) == 0 ) {
+ uri += STRLENOF( "uri=" );
+ }
+
+ data->lm_url = strdup( uri );
+ if ( data->lm_url == NULL ) {
+ map_ldap_free( data );
+ return NULL;
+ }
+
+ if ( ldap_url_parse( uri, &data->lm_lud ) != REWRITE_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] illegal URI '%s'\n",
+ fname, lineno, argv[ 0 ] );
+ map_ldap_free( data );
+ return NULL;
+ }
+
+ /* trim everything after [host][:port] */
+ p = strchr( data->lm_url, '/' );
+ assert( p[ 1 ] == '/' );
+ if ( ( p = strchr( p + 2, '/' ) ) != NULL ) {
+ p[ 0 ] = '\0';
+ }
+
+ if ( data->lm_lud->lud_attrs == NULL ) {
+ data->lm_attrs[ 0 ] = LDAP_NO_ATTRS;
+ data->lm_wantdn = 1;
+
+ } else {
+ if ( data->lm_lud->lud_attrs[ 1 ] != NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] only one attribute allowed in URI\n",
+ fname, lineno );
+ map_ldap_free( data );
+ return NULL;
+ }
+
+ if ( strcasecmp( data->lm_lud->lud_attrs[ 0 ], "dn" ) == 0
+ || strcasecmp( data->lm_lud->lud_attrs[ 0 ], "entryDN" ) == 0 )
+ {
+ ldap_memfree( data->lm_lud->lud_attrs[ 0 ] );
+ ldap_memfree( data->lm_lud->lud_attrs );
+ data->lm_lud->lud_attrs = NULL;
+ data->lm_attrs[ 0 ] = LDAP_NO_ATTRS;
+ data->lm_wantdn = 1;
+
+ } else {
+ data->lm_attrs[ 0 ] = data->lm_lud->lud_attrs[ 0 ];
+ }
+ }
+
+ data->lm_attrs[ 1 ] = NULL;
+
+ /* safe defaults */
+ data->lm_version = LDAP_VERSION3;
+
+ for ( argc--, argv++; argc > 0; argc--, argv++ ) {
+ if ( strncasecmp( argv[ 0 ], "binddn=", STRLENOF( "binddn=" ) ) == 0 ) {
+ char *p = argv[ 0 ] + STRLENOF( "binddn=" );
+ int l;
+
+ if ( p[ 0 ] == '\"' || p [ 0 ] == '\'' ) {
+ l = strlen( p ) - 2;
+ p++;
+ if ( p[ l ] != p[ 0 ] ) {
+ map_ldap_free( data );
+ return NULL;
+ }
+ } else {
+ l = strlen( p );
+ }
+
+ data->lm_binddn = strdup( p );
+ if ( data->lm_binddn == NULL ) {
+ map_ldap_free( data );
+ return NULL;
+ }
+
+ if ( data->lm_binddn[ l ] == '\"'
+ || data->lm_binddn[ l ] == '\'' ) {
+ data->lm_binddn[ l ] = '\0';
+ }
+
+ /* deprecated */
+ } else if ( strncasecmp( argv[ 0 ], "bindpw=", STRLENOF( "bindpw=" ) ) == 0 ) {
+ ber_str2bv( argv[ 0 ] + STRLENOF( "bindpw=" ), 0, 1, &data->lm_cred );
+ if ( data->lm_cred.bv_val == NULL ) {
+ map_ldap_free( data );
+ return NULL;
+ }
+
+ } else if ( strncasecmp( argv[ 0 ], "credentials=", STRLENOF( "credentials=" ) ) == 0 ) {
+ ber_str2bv( argv[ 0 ] + STRLENOF( "credentials=" ), 0, 1, &data->lm_cred );
+ if ( data->lm_cred.bv_val == NULL ) {
+ map_ldap_free( data );
+ return NULL;
+ }
+
+ } else if ( strncasecmp( argv[ 0 ], "bindwhen=", STRLENOF( "bindwhen=" ) ) == 0 ) {
+ char *p = argv[ 0 ] + STRLENOF( "bindwhen=" );
+
+ if ( strcasecmp( p, "now" ) == 0 ) {
+ int rc;
+
+ data->lm_when = MAP_LDAP_NOW;
+
+ /*
+ * Init LDAP handler ...
+ */
+ rc = ldap_initialize( &data->lm_ld, data->lm_url );
+ if ( rc != LDAP_SUCCESS ) {
+ map_ldap_free( data );
+ return NULL;
+ }
+
+ ldap_set_option( data->lm_ld,
+ LDAP_OPT_PROTOCOL_VERSION,
+ (void *)&data->lm_version );
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_init( &data->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ } else if ( strcasecmp( p, "later" ) == 0 ) {
+ data->lm_when = MAP_LDAP_LATER;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_init( &data->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ } else if ( strcasecmp( p, "everytime" ) == 0 ) {
+ data->lm_when = MAP_LDAP_EVERYTIME;
+ } else {
+ /* ignore ... */
+ }
+
+ } else if ( strncasecmp( argv[ 0 ], "version=", STRLENOF( "version=" ) ) == 0 ) {
+ if ( lutil_atoi( &data->lm_version, argv[ 0 ] + STRLENOF( "version=" ) ) ) {
+ map_ldap_free( data );
+ return NULL;
+ }
+
+ switch ( data->lm_version ) {
+ case LDAP_VERSION2:
+ case LDAP_VERSION3:
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] unknown version %s\n",
+ fname, lineno, p );
+ map_ldap_free( data );
+ return NULL;
+ }
+
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] unknown option %s (ignored)\n",
+ fname, lineno, argv[0] );
+ }
+ }
+
+ if ( data->lm_when == MAP_LDAP_UNKNOWN ) {
+ data->lm_when = MAP_LDAP_EVERYTIME;
+ }
+
+ return ( void * )data;
+}
+
+static int
+map_ldap_apply(
+ void *private,
+ const char *filter,
+ struct berval *val
+
+)
+{
+ LDAP *ld;
+ LDAPMessage *res = NULL, *entry;
+ int rc;
+ struct ldap_map_data *data = private;
+ LDAPURLDesc *lud = data->lm_lud;
+
+ int first_try = 1, set_version = 0;
+
+ assert( private != NULL );
+ assert( filter != NULL );
+ assert( val != NULL );
+
+ val->bv_val = NULL;
+ val->bv_len = 0;
+
+ if ( data->lm_when == MAP_LDAP_EVERYTIME ) {
+ rc = ldap_initialize( &ld, data->lm_url );
+ set_version = 1;
+
+ } else {
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_lock( &data->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ rc = LDAP_SUCCESS;
+
+ if ( data->lm_when == MAP_LDAP_LATER && data->lm_ld == NULL ) {
+ rc = ldap_initialize( &data->lm_ld, data->lm_url );
+ set_version = 1;
+ }
+
+ ld = data->lm_ld;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ rc = REWRITE_ERR;
+ goto rc_return;
+ }
+
+do_bind:;
+ if ( set_version ) {
+ ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION,
+ (void *)&data->lm_version );
+ set_version = 0;
+ }
+
+ if ( data->lm_binddn != NULL ) {
+ rc = ldap_sasl_bind_s( ld, data->lm_binddn,
+ LDAP_SASL_SIMPLE, &data->lm_cred,
+ NULL, NULL, NULL );
+ if ( rc == LDAP_SERVER_DOWN && first_try ) {
+ first_try = 0;
+ if ( ldap_initialize( &ld, data->lm_url ) != LDAP_SUCCESS ) {
+ rc = REWRITE_ERR;
+ goto rc_return;
+ }
+ set_version = 1;
+ goto do_bind;
+
+ } else if ( rc != REWRITE_SUCCESS ) {
+ rc = REWRITE_ERR;
+ goto rc_return;
+ }
+ }
+
+ rc = ldap_search_ext_s( ld, lud->lud_dn, lud->lud_scope, ( char * )filter,
+ data->lm_attrs, 0, NULL, NULL, NULL, 1, &res );
+ if ( rc == LDAP_SERVER_DOWN && first_try ) {
+ first_try = 0;
+ if ( ldap_initialize( &ld, data->lm_url ) != LDAP_SUCCESS ) {
+ rc = REWRITE_ERR;
+ goto rc_return;
+ }
+ set_version = 1;
+ goto do_bind;
+
+ } else if ( rc != LDAP_SUCCESS ) {
+ rc = REWRITE_ERR;
+ goto rc_return;
+ }
+
+ if ( ldap_count_entries( ld, res ) != 1 ) {
+ ldap_msgfree( res );
+ rc = REWRITE_ERR;
+ goto rc_return;
+ }
+
+ entry = ldap_first_entry( ld, res );
+ assert( entry != NULL );
+
+ if ( data->lm_wantdn == 1 ) {
+ /*
+ * dn is newly allocated, so there's no need to strdup it
+ */
+ val->bv_val = ldap_get_dn( ld, entry );
+ val->bv_len = strlen( val->bv_val );
+
+ } else {
+ struct berval **values;
+
+ values = ldap_get_values_len( ld, entry, data->lm_attrs[ 0 ] );
+ if ( values != NULL ) {
+ if ( values[ 0 ] != NULL && values[ 0 ]->bv_val != NULL ) {
+#if 0
+ /* NOTE: in principle, multiple values
+ * should not be acceptable according
+ * to the current API; ignore by now */
+ if ( values[ 1 ] != NULL ) {
+ /* error */
+ }
+#endif
+ ber_dupbv( val, values[ 0 ] );
+ }
+ ldap_value_free_len( values );
+ }
+ }
+
+ ldap_msgfree( res );
+
+ if ( val->bv_val == NULL ) {
+ rc = REWRITE_ERR;
+ goto rc_return;
+ }
+
+rc_return:;
+ if ( data->lm_when == MAP_LDAP_EVERYTIME ) {
+ if ( ld != NULL ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ }
+
+ } else {
+ data->lm_ld = ld;
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_unlock( &data->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+ }
+
+ return rc;
+}
+
+static int
+map_ldap_destroy(
+ void *private
+)
+{
+ struct ldap_map_data *data = private;
+
+ assert( private != NULL );
+
+ map_ldap_free( data );
+
+ return 0;
+}
+
+const rewrite_mapper rewrite_ldap_mapper = {
+ "ldap",
+ map_ldap_parse,
+ map_ldap_apply,
+ map_ldap_destroy
+};
+
diff --git a/libraries/librewrite/map.c b/libraries/librewrite/map.c
new file mode 100644
index 0000000..3fa5863
--- /dev/null
+++ b/libraries/librewrite/map.c
@@ -0,0 +1,582 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#include <stdio.h>
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#include "rewrite-int.h"
+#include "rewrite-map.h"
+
+static int num_mappers;
+static const rewrite_mapper **mappers;
+#define MAPPER_ALLOC 8
+
+struct rewrite_map *
+rewrite_map_parse(
+ struct rewrite_info *info,
+ const char *string,
+ const char **currpos
+)
+{
+ struct rewrite_map *map = NULL;
+ struct rewrite_subst *subst = NULL;
+ char *s, *begin = NULL, *end;
+ const char *p;
+ int l, cnt, mtx = 0, rc = 0;
+
+ assert( info != NULL );
+ assert( string != NULL );
+ assert( currpos != NULL );
+
+ *currpos = NULL;
+
+ /*
+ * Go to the end of the map invocation (the right closing brace)
+ */
+ for ( p = string, cnt = 1; p[ 0 ] != '\0' && cnt > 0; p++ ) {
+ if ( IS_REWRITE_SUBMATCH_ESCAPE( p[ 0 ] ) ) {
+ /*
+ * '%' marks the beginning of a new map
+ */
+ if ( p[ 1 ] == '{' ) {
+ cnt++;
+ /*
+ * '%' followed by a digit may mark the beginning
+ * of an old map
+ */
+ } else if ( isdigit( (unsigned char) p[ 1 ] ) && p[ 2 ] == '{' ) {
+ cnt++;
+ p++;
+ }
+
+ if ( p[ 1 ] != '\0' ) {
+ p++;
+ }
+
+ } else if ( p[ 0 ] == '}' ) {
+ cnt--;
+ }
+ }
+ if ( cnt != 0 ) {
+ return NULL;
+ }
+ *currpos = p;
+
+ /*
+ * Copy the map invocation
+ */
+ l = p - string - 1;
+ s = calloc( sizeof( char ), l + 1 );
+ if ( s == NULL ) {
+ return NULL;
+ }
+ AC_MEMCPY( s, string, l );
+ s[ l ] = 0;
+
+ /*
+ * Isolate the map name (except for variable deref)
+ */
+ switch ( s[ 0 ] ) {
+ case REWRITE_OPERATOR_VARIABLE_GET:
+ case REWRITE_OPERATOR_PARAM_GET:
+ break;
+
+ default:
+ begin = strchr( s, '(' );
+ if ( begin == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ begin[ 0 ] = '\0';
+ begin++;
+ break;
+ }
+
+ /*
+ * Check for special map types
+ */
+ p = s;
+ switch ( p[ 0 ] ) {
+ case REWRITE_OPERATOR_SUBCONTEXT:
+ case REWRITE_OPERATOR_COMMAND:
+ case REWRITE_OPERATOR_VARIABLE_SET:
+ case REWRITE_OPERATOR_VARIABLE_GET:
+ case REWRITE_OPERATOR_PARAM_GET:
+ p++;
+ break;
+ }
+
+ /*
+ * Variable set and get may be repeated to indicate session-wide
+ * instead of operation-wide variables
+ */
+ switch ( p[ 0 ] ) {
+ case REWRITE_OPERATOR_VARIABLE_SET:
+ case REWRITE_OPERATOR_VARIABLE_GET:
+ p++;
+ break;
+ }
+
+ /*
+ * Variable get token can be appended to variable set to mean store
+ * AND rewrite
+ */
+ if ( p[ 0 ] == REWRITE_OPERATOR_VARIABLE_GET ) {
+ p++;
+ }
+
+ /*
+ * Check the syntax of the variable name
+ */
+ if ( !isalpha( (unsigned char) p[ 0 ] ) ) {
+ rc = -1;
+ goto cleanup;
+ }
+ for ( p++; p[ 0 ] != '\0'; p++ ) {
+ if ( !isalnum( (unsigned char) p[ 0 ] ) ) {
+ rc = -1;
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Isolate the argument of the map (except for variable deref)
+ */
+ switch ( s[ 0 ] ) {
+ case REWRITE_OPERATOR_VARIABLE_GET:
+ case REWRITE_OPERATOR_PARAM_GET:
+ break;
+
+ default:
+ end = strrchr( begin, ')' );
+ if ( end == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ end[ 0 ] = '\0';
+
+ /*
+ * Compile the substitution pattern of the map argument
+ */
+ subst = rewrite_subst_compile( info, begin );
+ if ( subst == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ break;
+ }
+
+ /*
+ * Create the map
+ */
+ map = calloc( sizeof( struct rewrite_map ), 1 );
+ if ( map == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ memset( map, 0, sizeof( struct rewrite_map ) );
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ if ( ldap_pvt_thread_mutex_init( &map->lm_mutex ) ) {
+ rc = -1;
+ goto cleanup;
+ }
+ ++mtx;
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ /*
+ * No subst for variable deref
+ */
+ switch ( s[ 0 ] ) {
+ case REWRITE_OPERATOR_VARIABLE_GET:
+ case REWRITE_OPERATOR_PARAM_GET:
+ break;
+
+ default:
+ map->lm_subst = subst;
+ break;
+ }
+
+ /*
+ * Parses special map types
+ */
+ switch ( s[ 0 ] ) {
+
+ /*
+ * Subcontext
+ */
+ case REWRITE_OPERATOR_SUBCONTEXT: /* '>' */
+
+ /*
+ * Fetch the rewrite context
+ * it MUST have been defined previously
+ */
+ map->lm_type = REWRITE_MAP_SUBCONTEXT;
+ map->lm_name = strdup( s + 1 );
+ if ( map->lm_name == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ map->lm_data = rewrite_context_find( info, s + 1 );
+ if ( map->lm_data == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ break;
+
+ /*
+ * External command (not implemented yet)
+ */
+ case REWRITE_OPERATOR_COMMAND: /* '|' */
+ rc = -1;
+ goto cleanup;
+
+ /*
+ * Variable set
+ */
+ case REWRITE_OPERATOR_VARIABLE_SET: /* '&' */
+ if ( s[ 1 ] == REWRITE_OPERATOR_VARIABLE_SET ) {
+ if ( s[ 2 ] == REWRITE_OPERATOR_VARIABLE_GET ) {
+ map->lm_type = REWRITE_MAP_SETW_SESN_VAR;
+ map->lm_name = strdup( s + 3 );
+ } else {
+ map->lm_type = REWRITE_MAP_SET_SESN_VAR;
+ map->lm_name = strdup( s + 2 );
+ }
+ } else {
+ if ( s[ 1 ] == REWRITE_OPERATOR_VARIABLE_GET ) {
+ map->lm_type = REWRITE_MAP_SETW_OP_VAR;
+ map->lm_name = strdup( s + 2 );
+ } else {
+ map->lm_type = REWRITE_MAP_SET_OP_VAR;
+ map->lm_name = strdup( s + 1 );
+ }
+ }
+ if ( map->lm_name == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ break;
+
+ /*
+ * Variable dereference
+ */
+ case REWRITE_OPERATOR_VARIABLE_GET: /* '*' */
+ if ( s[ 1 ] == REWRITE_OPERATOR_VARIABLE_GET ) {
+ map->lm_type = REWRITE_MAP_GET_SESN_VAR;
+ map->lm_name = strdup( s + 2 );
+ } else {
+ map->lm_type = REWRITE_MAP_GET_OP_VAR;
+ map->lm_name = strdup( s + 1 );
+ }
+ if ( map->lm_name == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ break;
+
+ /*
+ * Parameter
+ */
+ case REWRITE_OPERATOR_PARAM_GET: /* '$' */
+ map->lm_type = REWRITE_MAP_GET_PARAM;
+ map->lm_name = strdup( s + 1 );
+ if ( map->lm_name == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ break;
+
+ /*
+ * Built-in map
+ */
+ default:
+ map->lm_type = REWRITE_MAP_BUILTIN;
+ map->lm_name = strdup( s );
+ if ( map->lm_name == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ map->lm_data = rewrite_builtin_map_find( info, s );
+ if ( map->lm_data == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ break;
+
+ }
+
+cleanup:
+ free( s );
+ if ( rc ) {
+ if ( subst != NULL ) {
+ free( subst );
+ }
+ if ( map ) {
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ if ( mtx ) {
+ ldap_pvt_thread_mutex_destroy( &map->lm_mutex );
+ }
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ if ( map->lm_name ) {
+ free( map->lm_name );
+ map->lm_name = NULL;
+ }
+ free( map );
+ map = NULL;
+ }
+ }
+
+ return map;
+}
+
+/*
+ * Applies the new map type
+ */
+int
+rewrite_map_apply(
+ struct rewrite_info *info,
+ struct rewrite_op *op,
+ struct rewrite_map *map,
+ struct berval *key,
+ struct berval *val
+)
+{
+ int rc = REWRITE_SUCCESS;
+
+ assert( info != NULL );
+ assert( op != NULL );
+ assert( map != NULL );
+ assert( key != NULL );
+ assert( val != NULL );
+
+ val->bv_val = NULL;
+ val->bv_len = 0;
+
+ switch ( map->lm_type ) {
+ case REWRITE_MAP_SUBCONTEXT:
+ rc = rewrite_context_apply( info, op,
+ ( struct rewrite_context * )map->lm_data,
+ key->bv_val, &val->bv_val );
+ if ( val->bv_val != NULL ) {
+ if ( val->bv_val == key->bv_val ) {
+ val->bv_len = key->bv_len;
+ key->bv_val = NULL;
+ } else {
+ val->bv_len = strlen( val->bv_val );
+ }
+ }
+ break;
+
+ case REWRITE_MAP_SET_OP_VAR:
+ case REWRITE_MAP_SETW_OP_VAR:
+ rc = rewrite_var_set( &op->lo_vars, map->lm_name,
+ key->bv_val, 1 )
+ ? REWRITE_SUCCESS : REWRITE_ERR;
+ if ( rc == REWRITE_SUCCESS ) {
+ if ( map->lm_type == REWRITE_MAP_SET_OP_VAR ) {
+ val->bv_val = strdup( "" );
+ } else {
+ val->bv_val = strdup( key->bv_val );
+ val->bv_len = key->bv_len;
+ }
+ if ( val->bv_val == NULL ) {
+ rc = REWRITE_ERR;
+ }
+ }
+ break;
+
+ case REWRITE_MAP_GET_OP_VAR: {
+ struct rewrite_var *var;
+
+ var = rewrite_var_find( op->lo_vars, map->lm_name );
+ if ( var == NULL ) {
+ rc = REWRITE_ERR;
+ } else {
+ val->bv_val = strdup( var->lv_value.bv_val );
+ val->bv_len = var->lv_value.bv_len;
+ if ( val->bv_val == NULL ) {
+ rc = REWRITE_ERR;
+ }
+ }
+ break;
+ }
+
+ case REWRITE_MAP_SET_SESN_VAR:
+ case REWRITE_MAP_SETW_SESN_VAR:
+ if ( op->lo_cookie == NULL ) {
+ rc = REWRITE_ERR;
+ break;
+ }
+ rc = rewrite_session_var_set( info, op->lo_cookie,
+ map->lm_name, key->bv_val );
+ if ( rc == REWRITE_SUCCESS ) {
+ if ( map->lm_type == REWRITE_MAP_SET_SESN_VAR ) {
+ val->bv_val = strdup( "" );
+ } else {
+ val->bv_val = strdup( key->bv_val );
+ val->bv_len = key->bv_len;
+ }
+ if ( val->bv_val == NULL ) {
+ rc = REWRITE_ERR;
+ }
+ }
+ break;
+
+ case REWRITE_MAP_GET_SESN_VAR:
+ rc = rewrite_session_var_get( info, op->lo_cookie,
+ map->lm_name, val );
+ break;
+
+ case REWRITE_MAP_GET_PARAM:
+ rc = rewrite_param_get( info, map->lm_name, val );
+ break;
+
+ case REWRITE_MAP_BUILTIN: {
+ struct rewrite_builtin_map *bmap = map->lm_data;
+
+ if ( bmap->lb_mapper && bmap->lb_mapper->rm_apply )
+ rc = bmap->lb_mapper->rm_apply( bmap->lb_private, key->bv_val,
+ val );
+ else
+ rc = REWRITE_ERR;
+ break;
+ }
+
+ default:
+ rc = REWRITE_ERR;
+ break;
+ }
+
+ return rc;
+}
+
+void
+rewrite_builtin_map_free(
+ void *tmp
+)
+{
+ struct rewrite_builtin_map *map = ( struct rewrite_builtin_map * )tmp;
+
+ assert( map != NULL );
+
+ if ( map->lb_mapper && map->lb_mapper->rm_destroy )
+ map->lb_mapper->rm_destroy( map->lb_private );
+
+ free( map->lb_name );
+ free( map );
+}
+
+int
+rewrite_map_destroy(
+ struct rewrite_map **pmap
+)
+{
+ struct rewrite_map *map;
+
+ assert( pmap != NULL );
+ assert( *pmap != NULL );
+
+ map = *pmap;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_lock( &map->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ if ( map->lm_name ) {
+ free( map->lm_name );
+ map->lm_name = NULL;
+ }
+
+ if ( map->lm_subst ) {
+ rewrite_subst_destroy( &map->lm_subst );
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_unlock( &map->lm_mutex );
+ ldap_pvt_thread_mutex_destroy( &map->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ free( map );
+ *pmap = NULL;
+
+ return 0;
+}
+
+/* ldapmap.c */
+extern const rewrite_mapper rewrite_ldap_mapper;
+
+const rewrite_mapper *
+rewrite_mapper_find(
+ const char *name
+)
+{
+ int i;
+
+ if ( !strcasecmp( name, "ldap" ))
+ return &rewrite_ldap_mapper;
+
+ for (i=0; i<num_mappers; i++)
+ if ( !strcasecmp( name, mappers[i]->rm_name ))
+ return mappers[i];
+ return NULL;
+}
+
+int
+rewrite_mapper_register(
+ const rewrite_mapper *map
+)
+{
+ if ( num_mappers % MAPPER_ALLOC == 0 ) {
+ const rewrite_mapper **mnew;
+ mnew = realloc( mappers, (num_mappers + MAPPER_ALLOC) *
+ sizeof( rewrite_mapper * ));
+ if ( mnew )
+ mappers = mnew;
+ else
+ return -1;
+ }
+ mappers[num_mappers++] = map;
+ return 0;
+}
+
+int
+rewrite_mapper_unregister(
+ const rewrite_mapper *map
+)
+{
+ int i;
+
+ for (i = 0; i<num_mappers; i++) {
+ if ( mappers[i] == map ) {
+ num_mappers--;
+ mappers[i] = mappers[num_mappers];
+ mappers[num_mappers] = NULL;
+ return 0;
+ }
+ }
+ /* not found */
+ return -1;
+}
diff --git a/libraries/librewrite/params.c b/libraries/librewrite/params.c
new file mode 100644
index 0000000..5da6e04
--- /dev/null
+++ b/libraries/librewrite/params.c
@@ -0,0 +1,147 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#include "rewrite-int.h"
+
+/*
+ * Defines and inits a variable with global scope
+ */
+int
+rewrite_param_set(
+ struct rewrite_info *info,
+ const char *name,
+ const char *value
+)
+{
+ struct rewrite_var *var;
+ int rc = REWRITE_SUCCESS;
+
+ assert( info != NULL );
+ assert( name != NULL );
+ assert( value != NULL );
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wlock( &info->li_params_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ var = rewrite_var_find( info->li_params, name );
+ if ( var != NULL ) {
+ assert( var->lv_value.bv_val != NULL );
+ free( var->lv_value.bv_val );
+ var->lv_value.bv_val = strdup( value );
+ var->lv_value.bv_len = strlen( value );
+
+ } else {
+ var = rewrite_var_insert( &info->li_params, name, value );
+ }
+
+ if ( var == NULL || var->lv_value.bv_val == NULL ) {
+ rc = REWRITE_ERR;
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wunlock( &info->li_params_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ return rc;
+}
+
+/*
+ * Gets a var with global scope
+ */
+int
+rewrite_param_get(
+ struct rewrite_info *info,
+ const char *name,
+ struct berval *value
+)
+{
+ struct rewrite_var *var;
+ int rc = REWRITE_SUCCESS;
+
+ assert( info != NULL );
+ assert( name != NULL );
+ assert( value != NULL );
+
+ value->bv_val = NULL;
+ value->bv_len = 0;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_rlock( &info->li_params_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ var = rewrite_var_find( info->li_params, name );
+ if ( var != NULL ) {
+ value->bv_val = strdup( var->lv_value.bv_val );
+ value->bv_len = var->lv_value.bv_len;
+ }
+
+ if ( var == NULL || value->bv_val == NULL ) {
+ rc = REWRITE_ERR;
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_runlock( &info->li_params_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ return rc;
+}
+
+static void
+rewrite_param_free(
+ void *tmp
+)
+{
+ struct rewrite_var *var = ( struct rewrite_var * )tmp;
+ assert( var != NULL );
+
+ assert( var->lv_name != NULL );
+ assert( var->lv_value.bv_val != NULL );
+
+ free( var->lv_name );
+ free( var->lv_value.bv_val );
+ free( var );
+}
+
+/*
+ * Destroys the parameter tree
+ */
+int
+rewrite_param_destroy(
+ struct rewrite_info *info
+)
+{
+ assert( info != NULL );
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wlock( &info->li_params_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ ldap_avl_free( info->li_params, rewrite_param_free );
+ info->li_params = NULL;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wunlock( &info->li_params_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ return REWRITE_SUCCESS;
+}
+
diff --git a/libraries/librewrite/parse.c b/libraries/librewrite/parse.c
new file mode 100644
index 0000000..0053a26
--- /dev/null
+++ b/libraries/librewrite/parse.c
@@ -0,0 +1,124 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#include <stdio.h>
+
+#include "rewrite-int.h"
+
+static int
+parse_line(
+ char **argv,
+ int *argc,
+ int maxargs,
+ char *buf
+)
+{
+ char *p, *begin;
+ int in_quoted_field = 0, cnt = 0;
+ char quote = '\0';
+
+ for ( p = buf; isspace( (unsigned char) p[ 0 ] ); p++ );
+
+ if ( p[ 0 ] == '#' ) {
+ return 0;
+ }
+
+ for ( begin = p; p[ 0 ] != '\0'; p++ ) {
+ if ( p[ 0 ] == '\\' && p[ 1 ] != '\0' ) {
+ p++;
+ } else if ( p[ 0 ] == '\'' || p[ 0 ] == '\"') {
+ if ( in_quoted_field && p[ 0 ] == quote ) {
+ in_quoted_field = 1 - in_quoted_field;
+ quote = '\0';
+ p[ 0 ] = '\0';
+ argv[ cnt ] = begin;
+ if ( ++cnt == maxargs ) {
+ *argc = cnt;
+ return 1;
+ }
+ for ( p++; isspace( (unsigned char) p[ 0 ] ); p++ );
+ begin = p;
+ p--;
+
+ } else if ( !in_quoted_field ) {
+ if ( p != begin ) {
+ return -1;
+ }
+ begin++;
+ in_quoted_field = 1 - in_quoted_field;
+ quote = p[ 0 ];
+ }
+ } else if ( isspace( (unsigned char) p[ 0 ] ) && !in_quoted_field ) {
+ p[ 0 ] = '\0';
+ argv[ cnt ] = begin;
+
+ if ( ++cnt == maxargs ) {
+ *argc = cnt;
+ return 1;
+ }
+
+ for ( p++; isspace( (unsigned char) p[ 0 ] ); p++ );
+ begin = p;
+ p--;
+ }
+ }
+
+ *argc = cnt;
+
+ return 1;
+}
+
+int
+rewrite_read(
+ FILE *fin,
+ struct rewrite_info *info
+)
+{
+ char buf[ 1024 ];
+ char *argv[11];
+ int argc, lineno;
+
+ /*
+ * Empty rule at the beginning of the context
+ */
+
+ for ( lineno = 0; fgets( buf, sizeof( buf ), fin ); lineno++ ) {
+ switch ( parse_line( argv, &argc, sizeof( argv ) - 1, buf ) ) {
+ case -1:
+ return REWRITE_ERR;
+ case 0:
+ break;
+ case 1:
+ if ( strncasecmp( argv[ 0 ], "rewrite", 7 ) == 0 ) {
+ int rc;
+ rc = rewrite_parse( info, "file", lineno,
+ argc, argv );
+ if ( rc != REWRITE_SUCCESS ) {
+ return rc;
+ }
+ }
+ break;
+ }
+ }
+
+ return REWRITE_SUCCESS;
+}
+
diff --git a/libraries/librewrite/rewrite-int.h b/libraries/librewrite/rewrite-int.h
new file mode 100644
index 0000000..441db51
--- /dev/null
+++ b/libraries/librewrite/rewrite-int.h
@@ -0,0 +1,628 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#ifndef REWRITE_INT_H
+#define REWRITE_INT_H
+
+/*
+ * These are required by every file of the library, so they're included here
+ */
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/syslog.h>
+#include <ac/regex.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+#include <ac/ctype.h>
+
+#include <lber.h>
+#include <ldap.h>
+#define LDAP_DEFINE_LDAP_DEBUG
+#include <ldap_log.h>
+#include <lutil.h>
+#include <ldap_avl.h>
+
+#include <rewrite.h>
+
+#ifndef NO_THREADS
+#define USE_REWRITE_LDAP_PVT_THREADS
+#include <ldap_pvt_thread.h>
+#endif
+
+#define malloc(x) ber_memalloc(x)
+#define calloc(x,y) ber_memcalloc(x,y)
+#define realloc(x,y) ber_memrealloc(x,y)
+#define free(x) ber_memfree(x)
+#undef strdup
+#define strdup(x) ber_strdup(x)
+
+/*
+ * For details, see RATIONALE.
+ */
+
+#define REWRITE_MAX_MATCH 11 /* 0: overall string; 1-9: submatches */
+#define REWRITE_MAX_PASSES 100
+
+/*
+ * Submatch escape char
+ */
+/* the '\' conflicts with slapd.conf parsing */
+/* #define REWRITE_SUBMATCH_ESCAPE '\\' */
+#define REWRITE_SUBMATCH_ESCAPE_ORIG '%'
+#define REWRITE_SUBMATCH_ESCAPE '$'
+#define IS_REWRITE_SUBMATCH_ESCAPE(c) \
+ ((c) == REWRITE_SUBMATCH_ESCAPE || (c) == REWRITE_SUBMATCH_ESCAPE_ORIG)
+
+/*
+ * REGEX flags
+ */
+
+#define REWRITE_FLAG_HONORCASE 'C'
+#define REWRITE_FLAG_BASICREGEX 'R'
+
+/*
+ * Action flags
+ */
+#define REWRITE_FLAG_EXECONCE ':'
+#define REWRITE_FLAG_STOP '@'
+#define REWRITE_FLAG_UNWILLING '#'
+#define REWRITE_FLAG_GOTO 'G' /* requires an arg */
+#define REWRITE_FLAG_USER 'U' /* requires an arg */
+#define REWRITE_FLAG_MAX_PASSES 'M' /* requires an arg */
+#define REWRITE_FLAG_IGNORE_ERR 'I'
+
+/*
+ * Map operators
+ */
+#define REWRITE_OPERATOR_SUBCONTEXT '>'
+#define REWRITE_OPERATOR_COMMAND '|'
+#define REWRITE_OPERATOR_VARIABLE_SET '&'
+#define REWRITE_OPERATOR_VARIABLE_GET '*'
+#define REWRITE_OPERATOR_PARAM_GET '$'
+
+
+/***********
+ * PRIVATE *
+ ***********/
+
+/*
+ * Action
+ */
+struct rewrite_action {
+ struct rewrite_action *la_next;
+
+#define REWRITE_ACTION_STOP 0x0001
+#define REWRITE_ACTION_UNWILLING 0x0002
+#define REWRITE_ACTION_GOTO 0x0003
+#define REWRITE_ACTION_IGNORE_ERR 0x0004
+#define REWRITE_ACTION_USER 0x0005
+ int la_type;
+ void *la_args;
+};
+
+/*
+ * Map
+ */
+struct rewrite_map {
+
+ /*
+ * Legacy stuff
+ */
+#define REWRITE_MAP_XFILEMAP 0x0001 /* Rough implementation! */
+#define REWRITE_MAP_XPWDMAP 0x0002 /* uid -> gecos */
+#define REWRITE_MAP_XLDAPMAP 0x0003 /* Not implemented yet! */
+
+ /*
+ * Maps with args
+ */
+#define REWRITE_MAP_SUBCONTEXT 0x0101
+
+#define REWRITE_MAP_SET_OP_VAR 0x0102
+#define REWRITE_MAP_SETW_OP_VAR 0x0103
+#define REWRITE_MAP_GET_OP_VAR 0x0104
+#define REWRITE_MAP_SET_SESN_VAR 0x0105
+#define REWRITE_MAP_SETW_SESN_VAR 0x0106
+#define REWRITE_MAP_GET_SESN_VAR 0x0107
+#define REWRITE_MAP_GET_PARAM 0x0108
+#define REWRITE_MAP_BUILTIN 0x0109
+ int lm_type;
+
+ char *lm_name;
+ void *lm_data;
+
+ /*
+ * Old maps store private data in _lm_args;
+ * new maps store the substitution pattern in _lm_subst
+ */
+ union {
+ void *_lm_args;
+ struct rewrite_subst *_lm_subst;
+ } lm_union;
+#define lm_args lm_union._lm_args
+#define lm_subst lm_union._lm_subst
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_t lm_mutex;
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+};
+
+/*
+ * Builtin maps
+ */
+struct rewrite_builtin_map {
+#define REWRITE_BUILTIN_MAP 0x0200
+ int lb_type;
+ char *lb_name;
+ void *lb_private;
+ const rewrite_mapper *lb_mapper;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_t lb_mutex;
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+};
+
+/*
+ * Submatch substitution
+ */
+struct rewrite_submatch {
+#define REWRITE_SUBMATCH_ASIS 0x0000
+#define REWRITE_SUBMATCH_XMAP 0x0001
+#define REWRITE_SUBMATCH_MAP_W_ARG 0x0002
+ int ls_type;
+ struct rewrite_map *ls_map;
+ int ls_submatch;
+ /*
+ * The first one represents the index of the submatch in case
+ * the map has single submatch as argument;
+ * the latter represents the map argument scheme in case
+ * the map has substitution string argument form
+ */
+};
+
+/*
+ * Pattern substitution
+ */
+struct rewrite_subst {
+ size_t lt_subs_len;
+ struct berval *lt_subs;
+
+ int lt_num_submatch;
+ struct rewrite_submatch *lt_submatch;
+};
+
+/*
+ * Rule
+ */
+struct rewrite_rule {
+ struct rewrite_rule *lr_next;
+ struct rewrite_rule *lr_prev;
+
+ char *lr_pattern;
+ char *lr_subststring;
+ char *lr_flagstring;
+ regex_t lr_regex;
+
+ /*
+ * I was thinking about some kind of per-rule mutex, but there's
+ * probably no need, because rules after compilation are only read;
+ * however, I need to check whether regexec is reentrant ...
+ */
+
+ struct rewrite_subst *lr_subst;
+
+#define REWRITE_REGEX_ICASE REG_ICASE
+#define REWRITE_REGEX_EXTENDED REG_EXTENDED
+ int lr_flags;
+
+#define REWRITE_RECURSE 0x0001
+#define REWRITE_EXEC_ONCE 0x0002
+ int lr_mode;
+ int lr_max_passes;
+
+ struct rewrite_action *lr_action;
+};
+
+/*
+ * Rewrite Context (set of rules)
+ */
+struct rewrite_context {
+ char *lc_name;
+ struct rewrite_context *lc_alias;
+ struct rewrite_rule *lc_rule;
+};
+
+/*
+ * Session
+ */
+struct rewrite_session {
+ void *ls_cookie;
+ Avlnode *ls_vars;
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_t ls_vars_mutex;
+ ldap_pvt_thread_mutex_t ls_mutex;
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+ int ls_count;
+};
+
+/*
+ * Variable
+ */
+struct rewrite_var {
+ char *lv_name;
+ int lv_flags;
+ struct berval lv_value;
+};
+
+/*
+ * Operation
+ */
+struct rewrite_op {
+ int lo_num_passes;
+ int lo_depth;
+#if 0 /* FIXME: not used anywhere! (debug? then, why strdup?) */
+ char *lo_string;
+#endif
+ char *lo_result;
+ Avlnode *lo_vars;
+ const void *lo_cookie;
+};
+
+
+/**********
+ * PUBLIC *
+ **********/
+
+/*
+ * Rewrite info
+ */
+struct rewrite_info {
+ Avlnode *li_context;
+ Avlnode *li_maps;
+ /*
+ * No global mutex because maps are read only at
+ * config time
+ */
+ Avlnode *li_params;
+ Avlnode *li_cookies;
+ int li_num_cookies;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_t li_params_mutex;
+ ldap_pvt_thread_rdwr_t li_cookies_mutex;
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ /*
+ * Default to `off';
+ * use `rewriteEngine {on|off}' directive to alter
+ */
+ int li_state;
+
+ /*
+ * Defaults to REWRITE_MAXPASSES;
+ * use `rewriteMaxPasses numPasses' directive to alter
+ */
+#define REWRITE_MAXPASSES 100
+ int li_max_passes;
+ int li_max_passes_per_rule;
+
+ /*
+ * Behavior in case a NULL or non-existent context is required
+ */
+ int li_rewrite_mode;
+};
+
+/***********
+ * PRIVATE *
+ ***********/
+
+LDAP_REWRITE_V (struct rewrite_context*) rewrite_int_curr_context;
+
+/*
+ * Maps
+ */
+
+/*
+ * Parses a map (also in legacy 'x' version)
+ */
+LDAP_REWRITE_F (struct rewrite_map *)
+rewrite_map_parse(
+ struct rewrite_info *info,
+ const char *s,
+ const char **end
+);
+
+LDAP_REWRITE_F (struct rewrite_map *)
+rewrite_xmap_parse(
+ struct rewrite_info *info,
+ const char *s,
+ const char **end
+);
+
+/*
+ * Resolves key in val by means of map (also in legacy 'x' version)
+ */
+LDAP_REWRITE_F (int)
+rewrite_map_apply(
+ struct rewrite_info *info,
+ struct rewrite_op *op,
+ struct rewrite_map *map,
+ struct berval *key,
+ struct berval *val
+);
+
+LDAP_REWRITE_F (int)
+rewrite_xmap_apply(
+ struct rewrite_info *info,
+ struct rewrite_op *op,
+ struct rewrite_map *map,
+ struct berval *key,
+ struct berval *val
+);
+
+LDAP_REWRITE_F (int)
+rewrite_map_destroy(
+ struct rewrite_map **map
+);
+
+LDAP_REWRITE_F (int)
+rewrite_xmap_destroy(
+ struct rewrite_map **map
+);
+
+LDAP_REWRITE_F (void)
+rewrite_builtin_map_free(
+ void *map
+);
+/*
+ * Submatch substitution
+ */
+
+/*
+ * Compiles a substitution pattern
+ */
+LDAP_REWRITE_F (struct rewrite_subst *)
+rewrite_subst_compile(
+ struct rewrite_info *info,
+ const char *result
+);
+
+/*
+ * Substitutes a portion of rewritten string according to substitution
+ * pattern using submatches
+ */
+LDAP_REWRITE_F (int)
+rewrite_subst_apply(
+ struct rewrite_info *info,
+ struct rewrite_op *op,
+ struct rewrite_subst *subst,
+ const char *string,
+ const regmatch_t *match,
+ struct berval *val
+);
+
+LDAP_REWRITE_F (int)
+rewrite_subst_destroy(
+ struct rewrite_subst **subst
+);
+
+
+/*
+ * Rules
+ */
+
+/*
+ * Compiles the rule and appends it at the running context
+ */
+LDAP_REWRITE_F (int)
+rewrite_rule_compile(
+ struct rewrite_info *info,
+ struct rewrite_context *context,
+ const char *pattern,
+ const char *result,
+ const char *flagstring
+);
+
+/*
+ * Rewrites string according to rule; may return:
+ * REWRITE_REGEXEC_OK: fine; if *result != NULL rule matched
+ * and rewrite succeeded.
+ * REWRITE_REGEXEC_STOP: fine, rule matched; stop processing
+ * following rules
+ * REWRITE_REGEXEC_UNWILL: rule matched; force 'unwilling to perform'
+ * REWRITE_REGEXEC_ERR: an error occurred
+ */
+LDAP_REWRITE_F (int)
+rewrite_rule_apply(
+ struct rewrite_info *info,
+ struct rewrite_op *op,
+ struct rewrite_rule *rule,
+ const char *string,
+ char **result
+);
+
+LDAP_REWRITE_F (int)
+rewrite_rule_destroy(
+ struct rewrite_rule **rule
+);
+
+/*
+ * Sessions
+ */
+
+/*
+ * Fetches a struct rewrite_session
+ */
+LDAP_REWRITE_F (struct rewrite_session *)
+rewrite_session_find(
+ struct rewrite_info *info,
+ const void *cookie
+);
+
+/*
+ * Defines and inits a variable with session scope
+ */
+LDAP_REWRITE_F (int)
+rewrite_session_var_set_f(
+ struct rewrite_info *info,
+ const void *cookie,
+ const char *name,
+ const char *value,
+ int flags
+);
+
+/*
+ * Gets a var with session scope
+ */
+LDAP_REWRITE_F (int)
+rewrite_session_var_get(
+ struct rewrite_info *info,
+ const void *cookie,
+ const char *name,
+ struct berval *val
+);
+
+/*
+ * Deletes a session
+ */
+LDAP_REWRITE_F (int)
+rewrite_session_delete(
+ struct rewrite_info *info,
+ const void *cookie
+);
+
+/*
+ * Destroys the cookie tree
+ */
+LDAP_REWRITE_F (int)
+rewrite_session_destroy(
+ struct rewrite_info *info
+);
+
+
+/*
+ * Vars
+ */
+
+/*
+ * Finds a var
+ */
+LDAP_REWRITE_F (struct rewrite_var *)
+rewrite_var_find(
+ Avlnode *tree,
+ const char *name
+);
+
+/*
+ * Replaces the value of a variable
+ */
+LDAP_REWRITE_F (int)
+rewrite_var_replace(
+ struct rewrite_var *var,
+ const char *value,
+ int flags
+);
+
+/*
+ * Inserts a newly created var
+ */
+LDAP_REWRITE_F (struct rewrite_var *)
+rewrite_var_insert_f(
+ Avlnode **tree,
+ const char *name,
+ const char *value,
+ int flags
+);
+
+#define rewrite_var_insert(tree, name, value) \
+ rewrite_var_insert_f((tree), (name), (value), \
+ REWRITE_VAR_UPDATE|REWRITE_VAR_COPY_NAME|REWRITE_VAR_COPY_VALUE)
+
+/*
+ * Sets/inserts a var
+ */
+LDAP_REWRITE_F (struct rewrite_var *)
+rewrite_var_set_f(
+ Avlnode **tree,
+ const char *name,
+ const char *value,
+ int flags
+);
+
+#define rewrite_var_set(tree, name, value, insert) \
+ rewrite_var_set_f((tree), (name), (value), \
+ REWRITE_VAR_UPDATE|REWRITE_VAR_COPY_NAME|REWRITE_VAR_COPY_VALUE|((insert)? REWRITE_VAR_INSERT : 0))
+
+/*
+ * Deletes a var tree
+ */
+LDAP_REWRITE_F (int)
+rewrite_var_delete(
+ Avlnode *tree
+);
+
+
+/*
+ * Contexts
+ */
+
+/*
+ * Finds the context named rewriteContext in the context tree
+ */
+LDAP_REWRITE_F (struct rewrite_context *)
+rewrite_context_find(
+ struct rewrite_info *info,
+ const char *rewriteContext
+);
+
+/*
+ * Creates a new context called rewriteContext and stores in into the tree
+ */
+LDAP_REWRITE_F (struct rewrite_context *)
+rewrite_context_create(
+ struct rewrite_info *info,
+ const char *rewriteContext
+);
+
+/*
+ * Rewrites string according to context; may return:
+ * OK: fine; if *result != NULL rule matched and rewrite succeeded.
+ * STOP: fine, rule matched; stop processing following rules
+ * UNWILL: rule matched; force 'unwilling to perform'
+ */
+LDAP_REWRITE_F (int)
+rewrite_context_apply(
+ struct rewrite_info *info,
+ struct rewrite_op *op,
+ struct rewrite_context *context,
+ const char *string,
+ char **result
+);
+
+LDAP_REWRITE_F (int)
+rewrite_context_destroy(
+ struct rewrite_context **context
+);
+
+LDAP_REWRITE_F (void)
+rewrite_context_free(
+ void *tmp
+);
+
+#endif /* REWRITE_INT_H */
+
diff --git a/libraries/librewrite/rewrite-map.h b/libraries/librewrite/rewrite-map.h
new file mode 100644
index 0000000..1a19679
--- /dev/null
+++ b/libraries/librewrite/rewrite-map.h
@@ -0,0 +1,32 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#ifndef MAP_H
+#define MAP_H
+
+/*
+ * Retrieves a builtin map
+ */
+LDAP_REWRITE_F (struct rewrite_builtin_map *)
+rewrite_builtin_map_find(
+ struct rewrite_info *info,
+ const char *name
+);
+
+#endif /* MAP_H */
diff --git a/libraries/librewrite/rewrite.c b/libraries/librewrite/rewrite.c
new file mode 100644
index 0000000..9c35975
--- /dev/null
+++ b/libraries/librewrite/rewrite.c
@@ -0,0 +1,195 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/syslog.h>
+#include <ac/regex.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <stdio.h>
+
+#include <rewrite.h>
+#include <lutil.h>
+#include <ldap.h>
+
+int ldap_debug;
+int ldap_syslog;
+int ldap_syslog_level;
+
+static void
+apply(
+ FILE *fin,
+ const char *rewriteContext,
+ const char *arg
+)
+{
+ struct rewrite_info *info;
+ char *string, *sep, *result = NULL;
+ int rc;
+ void *cookie = &info;
+
+ info = rewrite_info_init( REWRITE_MODE_ERR );
+
+ if ( rewrite_read( fin, info ) != 0 ) {
+ exit( EXIT_FAILURE );
+ }
+
+ rewrite_param_set( info, "prog", "rewrite" );
+
+ rewrite_session_init( info, cookie );
+
+ string = (char *)arg;
+ for ( sep = strchr( rewriteContext, ',' );
+ rewriteContext != NULL;
+ rewriteContext = sep,
+ sep ? sep = strchr( rewriteContext, ',' ) : NULL )
+ {
+ char *errmsg = "";
+
+ if ( sep != NULL ) {
+ sep[ 0 ] = '\0';
+ sep++;
+ }
+ /* rc = rewrite( info, rewriteContext, string, &result ); */
+ rc = rewrite_session( info, rewriteContext, string,
+ cookie, &result );
+
+ switch ( rc ) {
+ case REWRITE_REGEXEC_OK:
+ errmsg = "ok";
+ break;
+
+ case REWRITE_REGEXEC_ERR:
+ errmsg = "error";
+ break;
+
+ case REWRITE_REGEXEC_STOP:
+ errmsg = "stop";
+ break;
+
+ case REWRITE_REGEXEC_UNWILLING:
+ errmsg = "unwilling to perform";
+ break;
+
+ default:
+ if (rc >= REWRITE_REGEXEC_USER) {
+ errmsg = "user-defined";
+ } else {
+ errmsg = "unknown";
+ }
+ break;
+ }
+
+ fprintf( stdout, "%s -> %s [%d:%s]\n", string,
+ ( result ? result : "(null)" ),
+ rc, errmsg );
+ if ( result == NULL ) {
+ break;
+ }
+ if ( string != arg && string != result ) {
+ free( string );
+ }
+ string = result;
+ }
+
+ if ( result && result != arg ) {
+ free( result );
+ }
+
+ rewrite_session_delete( info, cookie );
+
+ rewrite_info_delete( &info );
+}
+
+int
+main( int argc, char *argv[] )
+{
+ FILE *fin = NULL;
+ char *rewriteContext = REWRITE_DEFAULT_CONTEXT;
+ int debug = 0;
+
+ while ( 1 ) {
+ int opt = getopt( argc, argv, "d:f:hr:" );
+
+ if ( opt == EOF ) {
+ break;
+ }
+
+ switch ( opt ) {
+ case 'd':
+ if ( lutil_atoi( &debug, optarg ) != 0 ) {
+ fprintf( stderr, "illegal log level '%s'\n",
+ optarg );
+ exit( EXIT_FAILURE );
+ }
+ break;
+
+ case 'f':
+ fin = fopen( optarg, "r" );
+ if ( fin == NULL ) {
+ fprintf( stderr, "unable to open file '%s'\n",
+ optarg );
+ exit( EXIT_FAILURE );
+ }
+ break;
+
+ case 'h':
+ fprintf( stderr,
+ "usage: rewrite [options] string\n"
+ "\n"
+ "\t\t-f file\t\tconfiguration file\n"
+ "\t\t-r rule[s]\tlist of comma-separated rules\n"
+ "\n"
+ "\tsyntax:\n"
+ "\t\trewriteEngine\t{on|off}\n"
+ "\t\trewriteContext\tcontextName [alias aliasedContextName]\n"
+ "\t\trewriteRule\tpattern subst [flags]\n"
+ "\n"
+ );
+ exit( EXIT_SUCCESS );
+
+ case 'r':
+ rewriteContext = optarg;
+ break;
+ }
+ }
+
+ if ( debug != 0 ) {
+ ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &debug);
+ ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &debug);
+ }
+
+ if ( optind >= argc ) {
+ return -1;
+ }
+
+ apply( ( fin ? fin : stdin ), rewriteContext, argv[ optind ] );
+
+ if ( fin ) {
+ fclose( fin );
+ }
+
+ return 0;
+}
+
diff --git a/libraries/librewrite/rule.c b/libraries/librewrite/rule.c
new file mode 100644
index 0000000..b4fe405
--- /dev/null
+++ b/libraries/librewrite/rule.c
@@ -0,0 +1,510 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#include "rewrite-int.h"
+
+/*
+ * Appends a rule to the double linked list of rules
+ * Helper for rewrite_rule_compile
+ */
+static int
+append_rule(
+ struct rewrite_context *context,
+ struct rewrite_rule *rule
+)
+{
+ struct rewrite_rule *r;
+
+ assert( context != NULL );
+ assert( context->lc_rule != NULL );
+ assert( rule != NULL );
+
+ for ( r = context->lc_rule; r->lr_next != NULL; r = r->lr_next );
+ r->lr_next = rule;
+ rule->lr_prev = r;
+
+ return REWRITE_SUCCESS;
+}
+
+/*
+ * Appends an action to the linked list of actions
+ * Helper for rewrite_rule_compile
+ */
+static int
+append_action(
+ struct rewrite_action **pbase,
+ struct rewrite_action *action
+)
+{
+ struct rewrite_action **pa;
+
+ assert( pbase != NULL );
+ assert( action != NULL );
+
+ for ( pa = pbase; *pa != NULL; pa = &(*pa)->la_next );
+ *pa = action;
+
+ return REWRITE_SUCCESS;
+}
+
+static int
+destroy_action(
+ struct rewrite_action **paction
+)
+{
+ struct rewrite_action *action;
+
+ assert( paction != NULL );
+ assert( *paction != NULL );
+
+ action = *paction;
+
+ /* do something */
+ switch ( action->la_type ) {
+ case REWRITE_FLAG_GOTO:
+ case REWRITE_FLAG_USER: {
+ int *pi = (int *)action->la_args;
+
+ if ( pi ) {
+ free( pi );
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ free( action );
+ *paction = NULL;
+
+ return 0;
+}
+
+static void
+destroy_actions(
+ struct rewrite_action *paction
+)
+{
+ struct rewrite_action *next;
+
+ for (; paction; paction = next) {
+ next = paction->la_next;
+ destroy_action( &paction );
+ }
+}
+
+/*
+ */
+int
+rewrite_rule_compile(
+ struct rewrite_info *info,
+ struct rewrite_context *context,
+ const char *pattern,
+ const char *result,
+ const char *flagstring
+)
+{
+ int flags = REWRITE_REGEX_EXTENDED | REWRITE_REGEX_ICASE;
+ int mode = REWRITE_RECURSE;
+ int max_passes;
+
+ struct rewrite_rule *rule = NULL;
+ struct rewrite_subst *subst = NULL;
+ struct rewrite_action *action = NULL, *first_action = NULL;
+
+ const char *p;
+
+ assert( info != NULL );
+ assert( context != NULL );
+ assert( pattern != NULL );
+ assert( result != NULL );
+ /*
+ * A null flagstring should be allowed
+ */
+
+ max_passes = info->li_max_passes_per_rule;
+
+ /*
+ * Take care of substitution string
+ */
+ subst = rewrite_subst_compile( info, result );
+ if ( subst == NULL ) {
+ return REWRITE_ERR;
+ }
+
+ /*
+ * Take care of flags
+ */
+ for ( p = flagstring; p[ 0 ] != '\0'; p++ ) {
+ switch( p[ 0 ] ) {
+
+ /*
+ * REGEX flags
+ */
+ case REWRITE_FLAG_HONORCASE: /* 'C' */
+ /*
+ * Honor case (default is case insensitive)
+ */
+ flags &= ~REWRITE_REGEX_ICASE;
+ break;
+
+ case REWRITE_FLAG_BASICREGEX: /* 'R' */
+ /*
+ * Use POSIX Basic Regular Expression syntax
+ * instead of POSIX Extended Regular Expression
+ * syntax (default)
+ */
+ flags &= ~REWRITE_REGEX_EXTENDED;
+ break;
+
+ /*
+ * Execution mode flags
+ */
+ case REWRITE_FLAG_EXECONCE: /* ':' */
+ /*
+ * Apply rule once only
+ */
+ mode &= ~REWRITE_RECURSE;
+ mode |= REWRITE_EXEC_ONCE;
+ break;
+
+ /*
+ * Special action flags
+ */
+ case REWRITE_FLAG_STOP: /* '@' */
+ /*
+ * Bail out after applying rule
+ */
+ action = calloc( sizeof( struct rewrite_action ), 1 );
+ if ( action == NULL ) {
+ goto fail;
+ }
+
+ action->la_type = REWRITE_ACTION_STOP;
+ break;
+
+ case REWRITE_FLAG_UNWILLING: /* '#' */
+ /*
+ * Matching objs will be marked as gone!
+ */
+ action = calloc( sizeof( struct rewrite_action ), 1 );
+ if ( action == NULL ) {
+ goto fail;
+ }
+
+ mode &= ~REWRITE_RECURSE;
+ mode |= REWRITE_EXEC_ONCE;
+ action->la_type = REWRITE_ACTION_UNWILLING;
+ break;
+
+ case REWRITE_FLAG_GOTO: /* 'G' */
+ /*
+ * After applying rule, jump N rules
+ */
+
+ case REWRITE_FLAG_USER: { /* 'U' */
+ /*
+ * After applying rule, return user-defined
+ * error code
+ */
+ char *next = NULL;
+ int *d;
+
+ if ( p[ 1 ] != '{' ) {
+ goto fail;
+ }
+
+ d = malloc( sizeof( int ) );
+ if ( d == NULL ) {
+ goto fail;
+ }
+
+ d[ 0 ] = strtol( &p[ 2 ], &next, 0 );
+ if ( next == &p[ 2 ] || next[0] != '}' ) {
+ free( d );
+ goto fail;
+ }
+
+ action = calloc( sizeof( struct rewrite_action ), 1 );
+ if ( action == NULL ) {
+ free( d );
+ goto fail;
+ }
+ switch ( p[ 0 ] ) {
+ case REWRITE_FLAG_GOTO:
+ action->la_type = REWRITE_ACTION_GOTO;
+ break;
+
+ case REWRITE_FLAG_USER:
+ action->la_type = REWRITE_ACTION_USER;
+ break;
+
+ default:
+ assert(0);
+ }
+
+ action->la_args = (void *)d;
+
+ p = next; /* p is incremented by the for ... */
+
+ break;
+ }
+
+ case REWRITE_FLAG_MAX_PASSES: { /* 'U' */
+ /*
+ * Set the number of max passes per rule
+ */
+ char *next = NULL;
+
+ if ( p[ 1 ] != '{' ) {
+ goto fail;
+ }
+
+ max_passes = strtol( &p[ 2 ], &next, 0 );
+ if ( next == &p[ 2 ] || next[0] != '}' ) {
+ goto fail;
+ }
+
+ if ( max_passes < 1 ) {
+ /* FIXME: nonsense ... */
+ max_passes = 1;
+ }
+
+ p = next; /* p is incremented by the for ... */
+
+ break;
+ }
+
+ case REWRITE_FLAG_IGNORE_ERR: /* 'I' */
+ /*
+ * Ignore errors!
+ */
+ action = calloc( sizeof( struct rewrite_action ), 1 );
+ if ( action == NULL ) {
+ goto fail;
+ }
+
+ action->la_type = REWRITE_ACTION_IGNORE_ERR;
+ break;
+
+ /*
+ * Other flags ...
+ */
+ default:
+ /*
+ * Unimplemented feature (complain only)
+ */
+ break;
+ }
+
+ /*
+ * Stupid way to append to a list ...
+ */
+ if ( action != NULL ) {
+ append_action( &first_action, action );
+ action = NULL;
+ }
+ }
+
+ /*
+ * Finally, rule allocation
+ */
+ rule = calloc( sizeof( struct rewrite_rule ), 1 );
+ if ( rule == NULL ) {
+ goto fail;
+ }
+
+ /*
+ * REGEX compilation (luckily I don't need to take care of this ...)
+ */
+ if ( regcomp( &rule->lr_regex, ( char * )pattern, flags ) != 0 ) {
+ goto fail;
+ }
+
+ /*
+ * Just to remember them ...
+ */
+ rule->lr_pattern = strdup( pattern );
+ rule->lr_subststring = strdup( result );
+ rule->lr_flagstring = strdup( flagstring );
+ if ( rule->lr_pattern == NULL
+ || rule->lr_subststring == NULL
+ || rule->lr_flagstring == NULL )
+ {
+ goto fail;
+ }
+
+ /*
+ * Load compiled data into rule
+ */
+ rule->lr_subst = subst;
+
+ /*
+ * Set various parameters
+ */
+ rule->lr_flags = flags; /* don't really need any longer ... */
+ rule->lr_mode = mode;
+ rule->lr_max_passes = max_passes;
+ rule->lr_action = first_action;
+
+ /*
+ * Append rule at the end of the rewrite context
+ */
+ append_rule( context, rule );
+
+ return REWRITE_SUCCESS;
+
+fail:
+ if ( rule ) {
+ if ( rule->lr_pattern ) free( rule->lr_pattern );
+ if ( rule->lr_subststring ) free( rule->lr_subststring );
+ if ( rule->lr_flagstring ) free( rule->lr_flagstring );
+ free( rule );
+ }
+ destroy_actions( first_action );
+ free( subst );
+ return REWRITE_ERR;
+}
+
+/*
+ * Rewrites string according to rule; may return:
+ * OK: fine; if *result != NULL rule matched and rewrite succeeded.
+ * STOP: fine, rule matched; stop processing following rules
+ * UNWILL: rule matched; force 'unwilling to perform'
+ */
+int
+rewrite_rule_apply(
+ struct rewrite_info *info,
+ struct rewrite_op *op,
+ struct rewrite_rule *rule,
+ const char *arg,
+ char **result
+ )
+{
+ size_t nmatch = REWRITE_MAX_MATCH;
+ regmatch_t match[ REWRITE_MAX_MATCH ];
+
+ int rc = REWRITE_SUCCESS;
+
+ char *string;
+ int strcnt = 0;
+ struct berval val = { 0, NULL };
+
+ assert( info != NULL );
+ assert( op != NULL );
+ assert( rule != NULL );
+ assert( arg != NULL );
+ assert( result != NULL );
+
+ *result = NULL;
+
+ string = (char *)arg;
+
+ /*
+ * In case recursive match is required (default)
+ */
+recurse:;
+
+ Debug( LDAP_DEBUG_TRACE, "==> rewrite_rule_apply"
+ " rule='%s' string='%s' [%d pass(es)]\n",
+ rule->lr_pattern, string, strcnt + 1 );
+
+ op->lo_num_passes++;
+
+ rc = regexec( &rule->lr_regex, string, nmatch, match, 0 );
+ if ( rc != 0 ) {
+ if ( *result == NULL && string != arg ) {
+ free( string );
+ }
+
+ /*
+ * No match is OK; *result = NULL means no match
+ */
+ return REWRITE_REGEXEC_OK;
+ }
+
+ rc = rewrite_subst_apply( info, op, rule->lr_subst, string,
+ match, &val );
+
+ *result = val.bv_val;
+ val.bv_val = NULL;
+ if ( string != arg ) {
+ free( string );
+ string = NULL;
+ }
+
+ if ( rc != REWRITE_REGEXEC_OK ) {
+ return rc;
+ }
+
+ if ( ( rule->lr_mode & REWRITE_RECURSE ) == REWRITE_RECURSE
+ && op->lo_num_passes < info->li_max_passes
+ && ++strcnt < rule->lr_max_passes ) {
+ string = *result;
+
+ goto recurse;
+ }
+
+ return REWRITE_REGEXEC_OK;
+}
+
+int
+rewrite_rule_destroy(
+ struct rewrite_rule **prule
+ )
+{
+ struct rewrite_rule *rule;
+
+ assert( prule != NULL );
+ assert( *prule != NULL );
+
+ rule = *prule;
+
+ if ( rule->lr_pattern ) {
+ free( rule->lr_pattern );
+ rule->lr_pattern = NULL;
+ }
+
+ if ( rule->lr_subststring ) {
+ free( rule->lr_subststring );
+ rule->lr_subststring = NULL;
+ }
+
+ if ( rule->lr_flagstring ) {
+ free( rule->lr_flagstring );
+ rule->lr_flagstring = NULL;
+ }
+
+ if ( rule->lr_subst ) {
+ rewrite_subst_destroy( &rule->lr_subst );
+ }
+
+ regfree( &rule->lr_regex );
+
+ destroy_actions( rule->lr_action );
+
+ free( rule );
+ *prule = NULL;
+
+ return 0;
+}
+
diff --git a/libraries/librewrite/session.c b/libraries/librewrite/session.c
new file mode 100644
index 0000000..f766159
--- /dev/null
+++ b/libraries/librewrite/session.c
@@ -0,0 +1,427 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#include "rewrite-int.h"
+
+/*
+ * Compares two cookies
+ */
+static int
+rewrite_cookie_cmp(
+ const void *c1,
+ const void *c2
+)
+{
+ const struct rewrite_session *s1, *s2;
+
+ s1 = ( const struct rewrite_session * )c1;
+ s2 = ( const struct rewrite_session * )c2;
+
+ assert( s1 != NULL );
+ assert( s2 != NULL );
+ assert( s1->ls_cookie != NULL );
+ assert( s2->ls_cookie != NULL );
+
+ return ( ( s1->ls_cookie < s2->ls_cookie ) ? -1 :
+ ( ( s1->ls_cookie > s2->ls_cookie ) ? 1 : 0 ) );
+}
+
+/*
+ * Duplicate cookies?
+ */
+static int
+rewrite_cookie_dup(
+ void *c1,
+ void *c2
+)
+{
+ struct rewrite_session *s1, *s2;
+
+ s1 = ( struct rewrite_session * )c1;
+ s2 = ( struct rewrite_session * )c2;
+
+ assert( s1 != NULL );
+ assert( s2 != NULL );
+ assert( s1->ls_cookie != NULL );
+ assert( s2->ls_cookie != NULL );
+
+ assert( s1->ls_cookie != s2->ls_cookie );
+
+ return ( ( s1->ls_cookie == s2->ls_cookie ) ? -1 : 0 );
+}
+
+/*
+ * Inits a session
+ */
+struct rewrite_session *
+rewrite_session_init(
+ struct rewrite_info *info,
+ const void *cookie
+)
+{
+ struct rewrite_session *session, tmp;
+ int rc;
+
+ assert( info != NULL );
+ assert( cookie != NULL );
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ tmp.ls_cookie = ( void * )cookie;
+ session = ( struct rewrite_session * )ldap_avl_find( info->li_cookies,
+ ( caddr_t )&tmp, rewrite_cookie_cmp );
+ if ( session ) {
+ session->ls_count++;
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+ return session;
+ }
+
+ session = calloc( sizeof( struct rewrite_session ), 1 );
+ if ( session == NULL ) {
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+ return NULL;
+ }
+ session->ls_cookie = ( void * )cookie;
+ session->ls_count = 1;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ if ( ldap_pvt_thread_mutex_init( &session->ls_mutex ) ) {
+ free( session );
+ ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex );
+ return NULL;
+ }
+ if ( ldap_pvt_thread_rdwr_init( &session->ls_vars_mutex ) ) {
+ ldap_pvt_thread_mutex_destroy( &session->ls_mutex );
+ free( session );
+ ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex );
+ return NULL;
+ }
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ rc = ldap_avl_insert( &info->li_cookies, ( caddr_t )session,
+ rewrite_cookie_cmp, rewrite_cookie_dup );
+ info->li_num_cookies++;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ if ( rc != 0 ) {
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_destroy( &session->ls_vars_mutex );
+ ldap_pvt_thread_mutex_destroy( &session->ls_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ free( session );
+ return NULL;
+ }
+
+ return session;
+}
+
+/*
+ * Fetches a session
+ */
+struct rewrite_session *
+rewrite_session_find(
+ struct rewrite_info *info,
+ const void *cookie
+)
+{
+ struct rewrite_session *session, tmp;
+
+ assert( info != NULL );
+ assert( cookie != NULL );
+
+ tmp.ls_cookie = ( void * )cookie;
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_rlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+ session = ( struct rewrite_session * )ldap_avl_find( info->li_cookies,
+ ( caddr_t )&tmp, rewrite_cookie_cmp );
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ if ( session ) {
+ ldap_pvt_thread_mutex_lock( &session->ls_mutex );
+ session->ls_count++;
+ }
+ ldap_pvt_thread_rdwr_runlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ return session;
+}
+
+/*
+ * Returns a session
+ */
+void
+rewrite_session_return(
+ struct rewrite_info *info,
+ struct rewrite_session *session
+)
+{
+ assert( session != NULL );
+ session->ls_count--;
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_unlock( &session->ls_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+}
+
+/*
+ * Defines and inits a var with session scope
+ */
+int
+rewrite_session_var_set_f(
+ struct rewrite_info *info,
+ const void *cookie,
+ const char *name,
+ const char *value,
+ int flags
+)
+{
+ struct rewrite_session *session;
+ struct rewrite_var *var;
+
+ assert( info != NULL );
+ assert( cookie != NULL );
+ assert( name != NULL );
+ assert( value != NULL );
+
+ session = rewrite_session_find( info, cookie );
+ if ( session == NULL ) {
+ session = rewrite_session_init( info, cookie );
+ if ( session == NULL ) {
+ return REWRITE_ERR;
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_lock( &session->ls_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wlock( &session->ls_vars_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ var = rewrite_var_find( session->ls_vars, name );
+ if ( var != NULL ) {
+ assert( var->lv_value.bv_val != NULL );
+
+ (void)rewrite_var_replace( var, value, flags );
+
+ } else {
+ var = rewrite_var_insert_f( &session->ls_vars, name, value, flags );
+ if ( var == NULL ) {
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wunlock( &session->ls_vars_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+ rewrite_session_return( info, session );
+ return REWRITE_ERR;
+ }
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wunlock( &session->ls_vars_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ rewrite_session_return( info, session );
+
+ return REWRITE_SUCCESS;
+}
+
+/*
+ * Gets a var with session scope
+ */
+int
+rewrite_session_var_get(
+ struct rewrite_info *info,
+ const void *cookie,
+ const char *name,
+ struct berval *value
+)
+{
+ struct rewrite_session *session;
+ struct rewrite_var *var;
+ int rc = REWRITE_SUCCESS;
+
+ assert( info != NULL );
+ assert( cookie != NULL );
+ assert( name != NULL );
+ assert( value != NULL );
+
+ value->bv_val = NULL;
+ value->bv_len = 0;
+
+ if ( cookie == NULL ) {
+ return REWRITE_ERR;
+ }
+
+ session = rewrite_session_find( info, cookie );
+ if ( session == NULL ) {
+ return REWRITE_ERR;
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_rlock( &session->ls_vars_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ var = rewrite_var_find( session->ls_vars, name );
+ if ( var != NULL ) {
+ value->bv_val = strdup( var->lv_value.bv_val );
+ value->bv_len = var->lv_value.bv_len;
+ }
+
+ if ( var == NULL || value->bv_val == NULL ) {
+ rc = REWRITE_ERR;
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_runlock( &session->ls_vars_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ rewrite_session_return( info, session );
+
+ return rc;
+}
+
+static void
+rewrite_session_clean( void *v_session )
+{
+ struct rewrite_session *session = (struct rewrite_session *)v_session;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wlock( &session->ls_vars_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ rewrite_var_delete( session->ls_vars );
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wunlock( &session->ls_vars_mutex );
+ ldap_pvt_thread_rdwr_destroy( &session->ls_vars_mutex );
+ ldap_pvt_thread_mutex_unlock( &session->ls_mutex );
+ ldap_pvt_thread_mutex_destroy( &session->ls_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+}
+
+static void
+rewrite_session_free( void *v_session )
+{
+ struct rewrite_session *session = (struct rewrite_session *)v_session;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_lock( &session->ls_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+ rewrite_session_clean( v_session );
+ free( v_session );
+}
+
+/*
+ * Deletes a session
+ */
+int
+rewrite_session_delete(
+ struct rewrite_info *info,
+ const void *cookie
+)
+{
+ struct rewrite_session *session, tmp = { 0 };
+
+ assert( info != NULL );
+ assert( cookie != NULL );
+
+ session = rewrite_session_find( info, cookie );
+
+ if ( session == NULL ) {
+ return REWRITE_SUCCESS;
+ }
+
+ if ( --session->ls_count > 0 ) {
+ rewrite_session_return( info, session );
+ return REWRITE_SUCCESS;
+ }
+
+ rewrite_session_clean( session );
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ assert( info->li_num_cookies > 0 );
+ info->li_num_cookies--;
+
+ /*
+ * There is nothing to delete in the return value
+ */
+ tmp.ls_cookie = ( void * )cookie;
+ ldap_avl_delete( &info->li_cookies, ( caddr_t )&tmp, rewrite_cookie_cmp );
+
+ free( session );
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ return REWRITE_SUCCESS;
+}
+
+/*
+ * Destroys the cookie tree
+ */
+int
+rewrite_session_destroy(
+ struct rewrite_info *info
+)
+{
+ int count;
+
+ assert( info != NULL );
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ /*
+ * Should call per-session destruction routine ...
+ */
+
+ count = ldap_avl_free( info->li_cookies, rewrite_session_free );
+ info->li_cookies = NULL;
+
+#if 0
+ fprintf( stderr, "count = %d; num_cookies = %d\n",
+ count, info->li_num_cookies );
+#endif
+
+ assert( count == info->li_num_cookies );
+ info->li_num_cookies = 0;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_rdwr_wunlock( &info->li_cookies_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ return REWRITE_SUCCESS;
+}
+
diff --git a/libraries/librewrite/subst.c b/libraries/librewrite/subst.c
new file mode 100644
index 0000000..16c13ec
--- /dev/null
+++ b/libraries/librewrite/subst.c
@@ -0,0 +1,513 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#include "rewrite-int.h"
+
+/*
+ * Compiles a substitution pattern
+ */
+struct rewrite_subst *
+rewrite_subst_compile(
+ struct rewrite_info *info,
+ const char *str
+)
+{
+ size_t subs_len;
+ struct berval *subs = NULL, *tmps;
+ struct rewrite_submatch *submatch = NULL, *tmpsm;
+
+ struct rewrite_subst *s = NULL;
+
+ char *result, *begin, *p;
+ int nsub = 0, l;
+
+ assert( info != NULL );
+ assert( str != NULL );
+
+ result = strdup( str );
+ if ( result == NULL ) {
+ return NULL;
+ }
+
+ /*
+ * Take care of substitution string
+ */
+ for ( p = begin = result, subs_len = 0; p[ 0 ] != '\0'; p++ ) {
+
+ /*
+ * Keep only single escapes '%'
+ */
+ if ( !IS_REWRITE_SUBMATCH_ESCAPE( p[ 0 ] ) ) {
+ continue;
+ }
+
+ if ( IS_REWRITE_SUBMATCH_ESCAPE( p[ 1 ] ) ) {
+ /* Pull &p[1] over p, including the trailing '\0' */
+ AC_MEMCPY((char *)p, &p[ 1 ], strlen( p ) );
+ continue;
+ }
+
+ tmps = ( struct berval * )realloc( subs,
+ sizeof( struct berval )*( nsub + 1 ) );
+ if ( tmps == NULL ) {
+ goto cleanup;
+ }
+ subs = tmps;
+ subs[ nsub ].bv_val = NULL;
+
+ tmpsm = ( struct rewrite_submatch * )realloc( submatch,
+ sizeof( struct rewrite_submatch )*( nsub + 1 ) );
+ if ( tmpsm == NULL ) {
+ goto cleanup;
+ }
+ submatch = tmpsm;
+ submatch[ nsub ].ls_map = NULL;
+
+ /*
+ * I think an `if l > 0' at runtime is better outside than
+ * inside a function call ...
+ */
+ l = p - begin;
+ if ( l > 0 ) {
+ subs_len += l;
+ subs[ nsub ].bv_len = l;
+ subs[ nsub ].bv_val = malloc( l + 1 );
+ if ( subs[ nsub ].bv_val == NULL ) {
+ goto cleanup;
+ }
+ AC_MEMCPY( subs[ nsub ].bv_val, begin, l );
+ subs[ nsub ].bv_val[ l ] = '\0';
+ } else {
+ subs[ nsub ].bv_val = NULL;
+ subs[ nsub ].bv_len = 0;
+ }
+
+ /*
+ * Substitution pattern
+ */
+ if ( isdigit( (unsigned char) p[ 1 ] ) ) {
+ int d = p[ 1 ] - '0';
+
+ /*
+ * Add a new value substitution scheme
+ */
+
+ submatch[ nsub ].ls_submatch = d;
+
+ /*
+ * If there is no argument, use default
+ * (substitute substring as is)
+ */
+ if ( p[ 2 ] != '{' ) {
+ submatch[ nsub ].ls_type =
+ REWRITE_SUBMATCH_ASIS;
+ submatch[ nsub ].ls_map = NULL;
+ begin = ++p + 1;
+
+ } else {
+ struct rewrite_map *map;
+
+ submatch[ nsub ].ls_type =
+ REWRITE_SUBMATCH_XMAP;
+
+ map = rewrite_xmap_parse( info,
+ p + 3, (const char **)&begin );
+ if ( map == NULL ) {
+ goto cleanup;
+ }
+ submatch[ nsub ].ls_map = map;
+ p = begin - 1;
+ }
+
+ /*
+ * Map with args ...
+ */
+ } else if ( p[ 1 ] == '{' ) {
+ struct rewrite_map *map;
+
+ map = rewrite_map_parse( info, p + 2,
+ (const char **)&begin );
+ if ( map == NULL ) {
+ goto cleanup;
+ }
+ p = begin - 1;
+
+ /*
+ * Add a new value substitution scheme
+ */
+ submatch[ nsub ].ls_type =
+ REWRITE_SUBMATCH_MAP_W_ARG;
+ submatch[ nsub ].ls_map = map;
+
+ /*
+ * Escape '%' ...
+ */
+ } else if ( p[ 1 ] == '%' ) {
+ AC_MEMCPY( &p[ 1 ], &p[ 2 ], strlen( &p[ 1 ] ) );
+ continue;
+
+ } else {
+ goto cleanup;
+ }
+
+ nsub++;
+ }
+
+ /*
+ * Last part of string
+ */
+ tmps = (struct berval * )realloc( subs, sizeof( struct berval )*( nsub + 1 ) );
+ if ( tmps == NULL ) {
+ /*
+ * XXX need to free the value subst stuff!
+ */
+ free( subs );
+ goto cleanup;
+ }
+ subs = tmps;
+ l = p - begin;
+ if ( l > 0 ) {
+ subs_len += l;
+ subs[ nsub ].bv_len = l;
+ subs[ nsub ].bv_val = malloc( l + 1 );
+ if ( subs[ nsub ].bv_val == NULL ) {
+ goto cleanup;
+ }
+ AC_MEMCPY( subs[ nsub ].bv_val, begin, l );
+ subs[ nsub ].bv_val[ l ] = '\0';
+ } else {
+ subs[ nsub ].bv_val = NULL;
+ subs[ nsub ].bv_len = 0;
+ }
+
+ s = calloc( sizeof( struct rewrite_subst ), 1 );
+ if ( s == NULL ) {
+ goto cleanup;
+ }
+
+ s->lt_subs_len = subs_len;
+ s->lt_subs = subs;
+ s->lt_num_submatch = nsub;
+ s->lt_submatch = submatch;
+ subs = NULL;
+ submatch = NULL;
+
+cleanup:;
+ if ( subs ) {
+ for ( l=0; l<nsub; l++ ) {
+ free( subs[nsub].bv_val );
+ }
+ free( subs );
+ }
+ if ( submatch ) {
+ for ( l=0; l<nsub; l++ ) {
+ free( submatch[nsub].ls_map );
+ }
+ free( submatch );
+ }
+ free( result );
+
+ return s;
+}
+
+/*
+ * Copies the match referred to by submatch and fetched in string by match.
+ * Helper for rewrite_rule_apply.
+ */
+static int
+submatch_copy(
+ struct rewrite_submatch *submatch,
+ const char *string,
+ const regmatch_t *match,
+ struct berval *val
+)
+{
+ int c, l;
+ const char *s;
+
+ assert( submatch != NULL );
+ assert( submatch->ls_type == REWRITE_SUBMATCH_ASIS
+ || submatch->ls_type == REWRITE_SUBMATCH_XMAP );
+ assert( string != NULL );
+ assert( match != NULL );
+ assert( val != NULL );
+ assert( val->bv_val == NULL );
+
+ c = submatch->ls_submatch;
+ s = string + match[ c ].rm_so;
+ l = match[ c ].rm_eo - match[ c ].rm_so;
+
+ val->bv_len = l;
+ val->bv_val = malloc( l + 1 );
+ if ( val->bv_val == NULL ) {
+ return REWRITE_ERR;
+ }
+
+ AC_MEMCPY( val->bv_val, s, l );
+ val->bv_val[ l ] = '\0';
+
+ return REWRITE_SUCCESS;
+}
+
+/*
+ * Substitutes a portion of rewritten string according to substitution
+ * pattern using submatches
+ */
+int
+rewrite_subst_apply(
+ struct rewrite_info *info,
+ struct rewrite_op *op,
+ struct rewrite_subst *subst,
+ const char *string,
+ const regmatch_t *match,
+ struct berval *val
+)
+{
+ struct berval *submatch = NULL;
+ char *res = NULL;
+ int n = 0, l, cl;
+ int rc = REWRITE_REGEXEC_OK;
+
+ assert( info != NULL );
+ assert( op != NULL );
+ assert( subst != NULL );
+ assert( string != NULL );
+ assert( match != NULL );
+ assert( val != NULL );
+
+ assert( val->bv_val == NULL );
+
+ val->bv_val = NULL;
+ val->bv_len = 0;
+
+ /*
+ * Prepare room for submatch expansion
+ */
+ if ( subst->lt_num_submatch > 0 ) {
+ submatch = calloc( sizeof( struct berval ),
+ subst->lt_num_submatch );
+ if ( submatch == NULL ) {
+ return REWRITE_REGEXEC_ERR;
+ }
+ }
+
+ /*
+ * Resolve submatches (simple subst, map expansion and so).
+ */
+ for ( n = 0, l = 0; n < subst->lt_num_submatch; n++ ) {
+ struct berval key = { 0, NULL };
+
+ submatch[ n ].bv_val = NULL;
+
+ /*
+ * Get key
+ */
+ switch ( subst->lt_submatch[ n ].ls_type ) {
+ case REWRITE_SUBMATCH_ASIS:
+ case REWRITE_SUBMATCH_XMAP:
+ rc = submatch_copy( &subst->lt_submatch[ n ],
+ string, match, &key );
+ if ( rc != REWRITE_SUCCESS ) {
+ rc = REWRITE_REGEXEC_ERR;
+ goto cleanup;
+ }
+ break;
+
+ case REWRITE_SUBMATCH_MAP_W_ARG:
+ switch ( subst->lt_submatch[ n ].ls_map->lm_type ) {
+ case REWRITE_MAP_GET_OP_VAR:
+ case REWRITE_MAP_GET_SESN_VAR:
+ case REWRITE_MAP_GET_PARAM:
+ rc = REWRITE_SUCCESS;
+ break;
+
+ default:
+ rc = rewrite_subst_apply( info, op,
+ subst->lt_submatch[ n ].ls_map->lm_subst,
+ string, match, &key);
+ }
+
+ if ( rc != REWRITE_SUCCESS ) {
+ goto cleanup;
+ }
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY, "Not Implemented\n" );
+ rc = REWRITE_ERR;
+ break;
+ }
+
+ if ( rc != REWRITE_SUCCESS ) {
+ rc = REWRITE_REGEXEC_ERR;
+ goto cleanup;
+ }
+
+ /*
+ * Resolve key
+ */
+ switch ( subst->lt_submatch[ n ].ls_type ) {
+ case REWRITE_SUBMATCH_ASIS:
+ submatch[ n ] = key;
+ rc = REWRITE_SUCCESS;
+ break;
+
+ case REWRITE_SUBMATCH_XMAP:
+ rc = rewrite_xmap_apply( info, op,
+ subst->lt_submatch[ n ].ls_map,
+ &key, &submatch[ n ] );
+ free( key.bv_val );
+ key.bv_val = NULL;
+ break;
+
+ case REWRITE_SUBMATCH_MAP_W_ARG:
+ rc = rewrite_map_apply( info, op,
+ subst->lt_submatch[ n ].ls_map,
+ &key, &submatch[ n ] );
+ free( key.bv_val );
+ key.bv_val = NULL;
+ break;
+
+ default:
+ /*
+ * When implemented, this might return the
+ * exit status of a rewrite context,
+ * which may include a stop, or an
+ * unwilling to perform
+ */
+ rc = REWRITE_ERR;
+ break;
+ }
+
+ if ( rc != REWRITE_SUCCESS ) {
+ rc = REWRITE_REGEXEC_ERR;
+ goto cleanup;
+ }
+
+ /*
+ * Increment the length of the resulting string
+ */
+ l += submatch[ n ].bv_len;
+ }
+
+ /*
+ * Alloc result buffer
+ */
+ l += subst->lt_subs_len;
+ res = malloc( l + 1 );
+ if ( res == NULL ) {
+ rc = REWRITE_REGEXEC_ERR;
+ goto cleanup;
+ }
+
+ /*
+ * Apply submatches (possibly resolved thru maps)
+ */
+ for ( n = 0, cl = 0; n < subst->lt_num_submatch; n++ ) {
+ if ( subst->lt_subs[ n ].bv_val != NULL ) {
+ AC_MEMCPY( res + cl, subst->lt_subs[ n ].bv_val,
+ subst->lt_subs[ n ].bv_len );
+ cl += subst->lt_subs[ n ].bv_len;
+ }
+ AC_MEMCPY( res + cl, submatch[ n ].bv_val,
+ submatch[ n ].bv_len );
+ cl += submatch[ n ].bv_len;
+ }
+ if ( subst->lt_subs[ n ].bv_val != NULL ) {
+ AC_MEMCPY( res + cl, subst->lt_subs[ n ].bv_val,
+ subst->lt_subs[ n ].bv_len );
+ cl += subst->lt_subs[ n ].bv_len;
+ }
+ res[ cl ] = '\0';
+
+ val->bv_val = res;
+ val->bv_len = l;
+
+cleanup:;
+ if ( submatch ) {
+ for ( ; --n >= 0; ) {
+ if ( submatch[ n ].bv_val ) {
+ free( submatch[ n ].bv_val );
+ }
+ }
+ free( submatch );
+ }
+
+ return rc;
+}
+
+/*
+ * frees data
+ */
+int
+rewrite_subst_destroy(
+ struct rewrite_subst **psubst
+)
+{
+ int n;
+ struct rewrite_subst *subst;
+
+ assert( psubst != NULL );
+ assert( *psubst != NULL );
+
+ subst = *psubst;
+
+ for ( n = 0; n < subst->lt_num_submatch; n++ ) {
+ if ( subst->lt_subs[ n ].bv_val ) {
+ free( subst->lt_subs[ n ].bv_val );
+ subst->lt_subs[ n ].bv_val = NULL;
+ }
+
+ switch ( subst->lt_submatch[ n ].ls_type ) {
+ case REWRITE_SUBMATCH_ASIS:
+ break;
+
+ case REWRITE_SUBMATCH_XMAP:
+ rewrite_xmap_destroy( &subst->lt_submatch[ n ].ls_map );
+ break;
+
+ case REWRITE_SUBMATCH_MAP_W_ARG:
+ rewrite_map_destroy( &subst->lt_submatch[ n ].ls_map );
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ free( subst->lt_submatch );
+ subst->lt_submatch = NULL;
+
+ /* last one */
+ if ( subst->lt_subs[ n ].bv_val ) {
+ free( subst->lt_subs[ n ].bv_val );
+ subst->lt_subs[ n ].bv_val = NULL;
+ }
+
+ free( subst->lt_subs );
+ subst->lt_subs = NULL;
+
+ free( subst );
+ *psubst = NULL;
+
+ return 0;
+}
+
diff --git a/libraries/librewrite/var.c b/libraries/librewrite/var.c
new file mode 100644
index 0000000..89a4b01
--- /dev/null
+++ b/libraries/librewrite/var.c
@@ -0,0 +1,273 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#include "rewrite-int.h"
+
+/*
+ * Compares two vars
+ */
+static int
+rewrite_var_cmp(
+ const void *c1,
+ const void *c2
+)
+{
+ const struct rewrite_var *v1, *v2;
+
+ v1 = ( const struct rewrite_var * )c1;
+ v2 = ( const struct rewrite_var * )c2;
+
+ assert( v1 != NULL );
+ assert( v2 != NULL );
+ assert( v1->lv_name != NULL );
+ assert( v2->lv_name != NULL );
+
+ return strcasecmp( v1->lv_name, v2->lv_name );
+}
+
+/*
+ * Duplicate var ?
+ */
+static int
+rewrite_var_dup(
+ void *c1,
+ void *c2
+)
+{
+ struct rewrite_var *v1, *v2;
+
+ v1 = ( struct rewrite_var * )c1;
+ v2 = ( struct rewrite_var * )c2;
+
+ assert( v1 != NULL );
+ assert( v2 != NULL );
+ assert( v1->lv_name != NULL );
+ assert( v2->lv_name != NULL );
+
+ return ( strcasecmp( v1->lv_name, v2->lv_name ) == 0 ? -1 : 0 );
+}
+
+/*
+ * Frees a var
+ */
+static void
+rewrite_var_free(
+ void *v_var
+)
+{
+ struct rewrite_var *var = v_var;
+ assert( var != NULL );
+
+ assert( var->lv_name != NULL );
+ assert( var->lv_value.bv_val != NULL );
+
+ if ( var->lv_flags & REWRITE_VAR_COPY_NAME )
+ free( var->lv_name );
+ if ( var->lv_flags & REWRITE_VAR_COPY_VALUE )
+ free( var->lv_value.bv_val );
+ free( var );
+}
+
+/*
+ * Deletes a var tree
+ */
+int
+rewrite_var_delete(
+ Avlnode *tree
+)
+{
+ ldap_avl_free( tree, rewrite_var_free );
+ return REWRITE_SUCCESS;
+}
+
+/*
+ * Finds a var
+ */
+struct rewrite_var *
+rewrite_var_find(
+ Avlnode *tree,
+ const char *name
+)
+{
+ struct rewrite_var var;
+
+ assert( name != NULL );
+
+ var.lv_name = ( char * )name;
+ return ( struct rewrite_var * )ldap_avl_find( tree,
+ ( caddr_t )&var, rewrite_var_cmp );
+}
+
+int
+rewrite_var_replace(
+ struct rewrite_var *var,
+ const char *value,
+ int flags
+)
+{
+ ber_len_t len;
+
+ assert( value != NULL );
+
+ len = strlen( value );
+
+ if ( var->lv_flags & REWRITE_VAR_COPY_VALUE ) {
+ if ( flags & REWRITE_VAR_COPY_VALUE ) {
+ if ( len <= var->lv_value.bv_len ) {
+ AC_MEMCPY(var->lv_value.bv_val, value, len + 1);
+
+ } else {
+ free( var->lv_value.bv_val );
+ var->lv_value.bv_val = strdup( value );
+ }
+
+ } else {
+ free( var->lv_value.bv_val );
+ var->lv_value.bv_val = (char *)value;
+ var->lv_flags &= ~REWRITE_VAR_COPY_VALUE;
+ }
+
+ } else {
+ if ( flags & REWRITE_VAR_COPY_VALUE ) {
+ var->lv_value.bv_val = strdup( value );
+ var->lv_flags |= REWRITE_VAR_COPY_VALUE;
+
+ } else {
+ var->lv_value.bv_val = (char *)value;
+ }
+ }
+
+ if ( var->lv_value.bv_val == NULL ) {
+ return -1;
+ }
+
+ var->lv_value.bv_len = len;
+
+ return 0;
+}
+
+/*
+ * Inserts a newly created var
+ */
+struct rewrite_var *
+rewrite_var_insert_f(
+ Avlnode **tree,
+ const char *name,
+ const char *value,
+ int flags
+)
+{
+ struct rewrite_var *var;
+ int rc = 0;
+
+ assert( tree != NULL );
+ assert( name != NULL );
+ assert( value != NULL );
+
+ var = rewrite_var_find( *tree, name );
+ if ( var != NULL ) {
+ if ( flags & REWRITE_VAR_UPDATE ) {
+ (void)rewrite_var_replace( var, value, flags );
+ goto cleanup;
+ }
+ rc = -1;
+ goto cleanup;
+ }
+
+ var = calloc( sizeof( struct rewrite_var ), 1 );
+ if ( var == NULL ) {
+ return NULL;
+ }
+
+ memset( var, 0, sizeof( struct rewrite_var ) );
+
+ if ( flags & REWRITE_VAR_COPY_NAME ) {
+ var->lv_name = strdup( name );
+ if ( var->lv_name == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ var->lv_flags |= REWRITE_VAR_COPY_NAME;
+
+ } else {
+ var->lv_name = (char *)name;
+ }
+
+ if ( flags & REWRITE_VAR_COPY_VALUE ) {
+ var->lv_value.bv_val = strdup( value );
+ if ( var->lv_value.bv_val == NULL ) {
+ rc = -1;
+ goto cleanup;
+ }
+ var->lv_flags |= REWRITE_VAR_COPY_VALUE;
+
+ } else {
+ var->lv_value.bv_val = (char *)value;
+ }
+ var->lv_value.bv_len = strlen( value );
+ rc = ldap_avl_insert( tree, ( caddr_t )var,
+ rewrite_var_cmp, rewrite_var_dup );
+
+cleanup:;
+ if ( rc != 0 && var ) {
+ ldap_avl_delete( tree, ( caddr_t )var, rewrite_var_cmp );
+ rewrite_var_free( var );
+ var = NULL;
+ }
+
+ return var;
+}
+
+/*
+ * Sets/inserts a var
+ */
+struct rewrite_var *
+rewrite_var_set_f(
+ Avlnode **tree,
+ const char *name,
+ const char *value,
+ int flags
+)
+{
+ struct rewrite_var *var;
+
+ assert( tree != NULL );
+ assert( name != NULL );
+ assert( value != NULL );
+
+ var = rewrite_var_find( *tree, name );
+ if ( var == NULL ) {
+ if ( flags & REWRITE_VAR_INSERT ) {
+ return rewrite_var_insert_f( tree, name, value, flags );
+
+ } else {
+ return NULL;
+ }
+
+ } else {
+ assert( var->lv_value.bv_val != NULL );
+
+ (void)rewrite_var_replace( var, value, flags );
+ }
+
+ return var;
+}
+
diff --git a/libraries/librewrite/xmap.c b/libraries/librewrite/xmap.c
new file mode 100644
index 0000000..728ed9a
--- /dev/null
+++ b/libraries/librewrite/xmap.c
@@ -0,0 +1,506 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Pierangelo Masarati for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include <portable.h>
+
+#include <stdio.h>
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#define LDAP_DEPRECATED 1
+#include "rewrite-int.h"
+#include "rewrite-map.h"
+
+/*
+ * Global data
+ */
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ldap_pvt_thread_mutex_t xpasswd_mutex;
+static int xpasswd_mutex_init = 0;
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+/*
+ * Map parsing
+ * NOTE: these are old-fashion maps; new maps will be parsed on separate
+ * config lines, and referred by name.
+ */
+struct rewrite_map *
+rewrite_xmap_parse(
+ struct rewrite_info *info,
+ const char *s,
+ const char **currpos
+)
+{
+ struct rewrite_map *map;
+
+ assert( info != NULL );
+ assert( s != NULL );
+ assert( currpos != NULL );
+
+ Debug( LDAP_DEBUG_ARGS, "rewrite_xmap_parse: %s\n",
+ s );
+
+ *currpos = NULL;
+
+ map = calloc( sizeof( struct rewrite_map ), 1 );
+ if ( map == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "rewrite_xmap_parse:"
+ " calloc failed\n" );
+ return NULL;
+ }
+
+ /*
+ * Experimental passwd map:
+ * replaces the uid with the matching gecos from /etc/passwd file
+ */
+ if ( strncasecmp(s, "xpasswd", 7 ) == 0 ) {
+ map->lm_type = REWRITE_MAP_XPWDMAP;
+ map->lm_name = strdup( "xpasswd" );
+ if ( map->lm_name == NULL ) {
+ free( map );
+ return NULL;
+ }
+
+ assert( s[7] == '}' );
+ *currpos = s + 8;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ if ( !xpasswd_mutex_init ) {
+ if ( ldap_pvt_thread_mutex_init( &xpasswd_mutex ) ) {
+ free( map );
+ return NULL;
+ }
+ }
+ ++xpasswd_mutex_init;
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ /* Don't really care if fails */
+ return map;
+
+ /*
+ * Experimental file map:
+ * looks up key in a `key value' ascii file
+ */
+ } else if ( strncasecmp( s, "xfile", 5 ) == 0 ) {
+ char *filename;
+ const char *p;
+ int l;
+ int c = 5;
+
+ map->lm_type = REWRITE_MAP_XFILEMAP;
+
+ if ( s[ c ] != '(' ) {
+ free( map );
+ return NULL;
+ }
+
+ /* Must start with '/' for security concerns */
+ c++;
+ if ( s[ c ] != '/' ) {
+ free( map );
+ return NULL;
+ }
+
+ for ( p = s + c; p[ 0 ] != '\0' && p[ 0 ] != ')'; p++ );
+ if ( p[ 0 ] != ')' ) {
+ free( map );
+ return NULL;
+ }
+
+ l = p - s - c;
+ filename = calloc( sizeof( char ), l + 1 );
+ if ( filename == NULL ) {
+ free( map );
+ return NULL;
+ }
+ AC_MEMCPY( filename, s + c, l );
+ filename[ l ] = '\0';
+
+ map->lm_args = ( void * )fopen( filename, "r" );
+ free( filename );
+
+ if ( map->lm_args == NULL ) {
+ free( map );
+ return NULL;
+ }
+
+ *currpos = p + 1;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ if ( ldap_pvt_thread_mutex_init( &map->lm_mutex ) ) {
+ fclose( ( FILE * )map->lm_args );
+ free( map );
+ return NULL;
+ }
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ return map;
+
+ /*
+ * Experimental ldap map:
+ * looks up key on the fly (not implemented!)
+ */
+ } else if ( strncasecmp(s, "xldap", 5 ) == 0 ) {
+ char *p;
+ char *url;
+ int l, rc;
+ int c = 5;
+ LDAPURLDesc *lud;
+
+ if ( s[ c ] != '(' ) {
+ free( map );
+ return NULL;
+ }
+ c++;
+
+ p = strchr( s, '}' );
+ if ( p == NULL ) {
+ free( map );
+ return NULL;
+ }
+ p--;
+
+ *currpos = p + 2;
+
+ /*
+ * Add two bytes for urlencoding of '%s'
+ */
+ l = p - s - c;
+ url = calloc( sizeof( char ), l + 3 );
+ if ( url == NULL ) {
+ free( map );
+ return NULL;
+ }
+ AC_MEMCPY( url, s + c, l );
+ url[ l ] = '\0';
+
+ /*
+ * Urlencodes the '%s' for ldap_url_parse
+ */
+ p = strchr( url, '%' );
+ if ( p != NULL ) {
+ AC_MEMCPY( p + 3, p + 1, strlen( p + 1 ) + 1 );
+ p[ 1 ] = '2';
+ p[ 2 ] = '5';
+ }
+
+ rc = ldap_url_parse( url, &lud );
+ free( url );
+
+ if ( rc != LDAP_SUCCESS ) {
+ free( map );
+ return NULL;
+ }
+ assert( lud != NULL );
+
+ map->lm_args = ( void * )lud;
+ map->lm_type = REWRITE_MAP_XLDAPMAP;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ if ( ldap_pvt_thread_mutex_init( &map->lm_mutex ) ) {
+ ldap_free_urldesc( lud );
+ free( map );
+ return NULL;
+ }
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ return map;
+
+ /* Unhandled map */
+ }
+
+ free( map );
+ return NULL;
+}
+
+/*
+ * Map key -> value resolution
+ * NOTE: these are old-fashion maps; new maps will be parsed on separate
+ * config lines, and referred by name.
+ */
+int
+rewrite_xmap_apply(
+ struct rewrite_info *info,
+ struct rewrite_op *op,
+ struct rewrite_map *map,
+ struct berval *key,
+ struct berval *val
+)
+{
+ int rc = REWRITE_SUCCESS;
+
+ assert( info != NULL );
+ assert( op != NULL );
+ assert( map != NULL );
+ assert( key != NULL );
+ assert( val != NULL );
+
+ val->bv_val = NULL;
+ val->bv_len = 0;
+
+ switch ( map->lm_type ) {
+#ifdef HAVE_GETPWNAM
+ case REWRITE_MAP_XPWDMAP: {
+ struct passwd *pwd;
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_lock( &xpasswd_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ pwd = getpwnam( key->bv_val );
+ if ( pwd == NULL ) {
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_unlock( &xpasswd_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ rc = LDAP_NO_SUCH_OBJECT;
+ break;
+ }
+
+#ifdef HAVE_STRUCT_PASSWD_PW_GECOS
+ if ( pwd->pw_gecos != NULL && pwd->pw_gecos[0] != '\0' ) {
+ int l = strlen( pwd->pw_gecos );
+
+ val->bv_val = strdup( pwd->pw_gecos );
+ val->bv_len = l;
+ } else
+#endif /* HAVE_STRUCT_PASSWD_PW_GECOS */
+ {
+ val->bv_val = strdup( key->bv_val );
+ val->bv_len = key->bv_len;
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_unlock( &xpasswd_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ if ( val->bv_val == NULL ) {
+ rc = REWRITE_ERR;
+ }
+ break;
+ }
+#endif /* HAVE_GETPWNAM*/
+
+ case REWRITE_MAP_XFILEMAP: {
+ char buf[1024];
+
+ if ( map->lm_args == NULL ) {
+ rc = REWRITE_ERR;
+ break;
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_lock( &map->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ rewind( ( FILE * )map->lm_args );
+
+ while ( fgets( buf, sizeof( buf ), ( FILE * )map->lm_args ) ) {
+ char *p;
+ int blen;
+
+ blen = strlen( buf );
+ if ( buf[ blen - 1 ] == '\n' ) {
+ buf[ blen - 1 ] = '\0';
+ }
+
+ p = strtok( buf, " " );
+ if ( p == NULL ) {
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_unlock( &map->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+ rc = REWRITE_ERR;
+ goto rc_return;
+ }
+ if ( strcasecmp( p, key->bv_val ) == 0
+ && ( p = strtok( NULL, "" ) ) ) {
+ val->bv_val = strdup( p );
+ if ( val->bv_val == NULL ) {
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_unlock( &map->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+ rc = REWRITE_ERR;
+ goto rc_return;
+ }
+
+ val->bv_len = strlen( p );
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_unlock( &map->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ goto rc_return;
+ }
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_unlock( &map->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ rc = REWRITE_ERR;
+
+ break;
+ }
+
+ case REWRITE_MAP_XLDAPMAP: {
+ LDAP *ld;
+ char filter[1024];
+ LDAPMessage *res = NULL, *entry;
+ LDAPURLDesc *lud = ( LDAPURLDesc * )map->lm_args;
+ int attrsonly = 0;
+ char **values;
+
+ assert( lud != NULL );
+
+ /*
+ * No mutex because there is no write on the map data
+ */
+
+ ld = ldap_init( lud->lud_host, lud->lud_port );
+ if ( ld == NULL ) {
+ rc = REWRITE_ERR;
+ goto rc_return;
+ }
+
+ snprintf( filter, sizeof( filter ), lud->lud_filter,
+ key->bv_val );
+
+ if ( strcasecmp( lud->lud_attrs[ 0 ], "dn" ) == 0 ) {
+ attrsonly = 1;
+ }
+ rc = ldap_search_s( ld, lud->lud_dn, lud->lud_scope,
+ filter, lud->lud_attrs, attrsonly, &res );
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_unbind( ld );
+ rc = REWRITE_ERR;
+ goto rc_return;
+ }
+
+ if ( ldap_count_entries( ld, res ) != 1 ) {
+ ldap_unbind( ld );
+ rc = REWRITE_ERR;
+ goto rc_return;
+ }
+
+ entry = ldap_first_entry( ld, res );
+ if ( entry == NULL ) {
+ ldap_msgfree( res );
+ ldap_unbind( ld );
+ rc = REWRITE_ERR;
+ goto rc_return;
+ }
+ if ( attrsonly == 1 ) {
+ val->bv_val = ldap_get_dn( ld, entry );
+
+ } else {
+ values = ldap_get_values( ld, entry,
+ lud->lud_attrs[0] );
+ if ( values != NULL ) {
+ val->bv_val = strdup( values[ 0 ] );
+ ldap_value_free( values );
+ }
+ }
+
+ ldap_msgfree( res );
+ ldap_unbind( ld );
+
+ if ( val->bv_val == NULL ) {
+ rc = REWRITE_ERR;
+ goto rc_return;
+ }
+ val->bv_len = strlen( val->bv_val );
+
+ rc = REWRITE_SUCCESS;
+ } break;
+ }
+
+rc_return:;
+ return rc;
+}
+
+int
+rewrite_xmap_destroy(
+ struct rewrite_map **pmap
+)
+{
+ struct rewrite_map *map;
+
+ assert( pmap != NULL );
+ assert( *pmap != NULL );
+
+ map = *pmap;
+
+ switch ( map->lm_type ) {
+ case REWRITE_MAP_XPWDMAP:
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ --xpasswd_mutex_init;
+ if ( !xpasswd_mutex_init ) {
+ ldap_pvt_thread_mutex_destroy( &xpasswd_mutex );
+ }
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ break;
+
+ case REWRITE_MAP_XFILEMAP:
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_lock( &map->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ if ( map->lm_args ) {
+ fclose( ( FILE * )map->lm_args );
+ map->lm_args = NULL;
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_unlock( &map->lm_mutex );
+ ldap_pvt_thread_mutex_destroy( &map->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+ break;
+
+ case REWRITE_MAP_XLDAPMAP:
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_lock( &map->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+
+ if ( map->lm_args ) {
+ ldap_free_urldesc( ( LDAPURLDesc * )map->lm_args );
+ map->lm_args = NULL;
+ }
+
+#ifdef USE_REWRITE_LDAP_PVT_THREADS
+ ldap_pvt_thread_mutex_unlock( &map->lm_mutex );
+ ldap_pvt_thread_mutex_destroy( &map->lm_mutex );
+#endif /* USE_REWRITE_LDAP_PVT_THREADS */
+ break;
+
+ default:
+ break;
+
+ }
+
+ free( map->lm_name );
+ free( map );
+ *pmap = NULL;
+
+ return 0;
+}
+
diff --git a/servers/Makefile.in b/servers/Makefile.in
new file mode 100644
index 0000000..31f57e7
--- /dev/null
+++ b/servers/Makefile.in
@@ -0,0 +1,17 @@
+# servers Makefile.in for OpenLDAP
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SUBDIRS= slapd lloadd
+
diff --git a/servers/lloadd/Makefile.in b/servers/lloadd/Makefile.in
new file mode 100644
index 0000000..add49ea
--- /dev/null
+++ b/servers/lloadd/Makefile.in
@@ -0,0 +1,48 @@
+# Makefile.in for Load Balancer
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+
+XSRCS = version.c
+
+NT_SRCS = nt_svc.c
+NT_OBJS = nt_svc.o ../../libraries/liblutil/slapdmsg.res
+
+SRCS = backend.c bind.c config.c connection.c client.c \
+ daemon.c epoch.c extended.c init.c operation.c \
+ upstream.c libevent_support.c \
+ $(@PLAT@_SRCS)
+
+
+LDAP_INCDIR= ../../include -I$(srcdir) -I$(srcdir)/../slapd
+LDAP_LIBDIR= ../../libraries
+
+
+# $(LTHREAD_LIBS) must be last!
+XLIBS = $(LLOADD_L)
+XXLIBS = $(LLOADD_LIBS) $(SECURITY_LIBS) $(LUTIL_LIBS)
+XXXLIBS = $(LTHREAD_LIBS)
+
+NT_DEPENDS = slapd.exp
+NT_OBJECTS = slapd.exp symdummy.o $(LLOADD_OBJS) version.o
+
+UNIX_DEPENDS = version.o $(LLOADD_L)
+UNIX_OBJECTS = $(OBJS) version.o
+
+LLOADD_DEPENDS = $(@PLAT@_DEPENDS)
+LLOADD_OBJECTS = $(@PLAT@_OBJECTS)
+
+include @BALANCER_INCLUDE@
+
+
diff --git a/servers/lloadd/Makefile_module.in b/servers/lloadd/Makefile_module.in
new file mode 100644
index 0000000..cd8cdb5
--- /dev/null
+++ b/servers/lloadd/Makefile_module.in
@@ -0,0 +1,45 @@
+# Makefile.in for Load Balancer
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+
+XSRCS = version.c
+
+NT_SRCS = ../slapd/nt_svc.c
+NT_OBJS = ../slapd/nt_svc.o ../../libraries/liblutil/slapdmsg.res
+
+SRCS += module_init.c monitor.c
+
+OBJS = $(patsubst %.c,%.lo,$(SRCS)) $(@PLAT@_OBJS)
+
+BUILD_OPT = "--enable-balancer=mod"
+BUILD_MOD = @BUILD_BALANCER@
+
+LIBBASE=lloadd
+
+# $(LTHREAD_LIBS) must be last!
+XLIBS = $(LLOADD_L)
+XXLIBS = $(LLOADD_LIBS) $(SECURITY_LIBS) $(LUTIL_LIBS)
+XXXLIBS = $(LTHREAD_LIBS)
+
+NT_DEPENDS = slapd.exp
+NT_OBJECTS = slapd.exp symdummy.o $(LLOADD_OBJS) version.o
+
+UNIX_DEPENDS = version.o $(LLOADD_L)
+UNIX_OBJECTS = $(OBJS) version.o
+
+LLOADD_DEPENDS = $(@PLAT@_DEPENDS)
+LLOADD_OBJECTS = $(@PLAT@_OBJECTS)
+LINK_LIBS=$(LLOADD_LIBS)
+MOD_DEFS = -DSLAPD_IMPORT -DBALANCER_MODULE
diff --git a/servers/lloadd/Makefile_server.in b/servers/lloadd/Makefile_server.in
new file mode 100644
index 0000000..dc9849f
--- /dev/null
+++ b/servers/lloadd/Makefile_server.in
@@ -0,0 +1,90 @@
+# Makefile.in for Load Balancer
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+PROGRAMS = lloadd
+XPROGRAMS = slloadd
+
+XSRCS = version.c
+
+NT_SRCS = ../slapd/nt_svc.c
+NT_OBJS = ../slapd/nt_svc.o ../../libraries/liblutil/slapdmsg.res
+
+SRCS += main.c value.c \
+ ../slapd/ch_malloc.c ../slapd/proxyp.c ../slapd/sl_malloc.c ../slapd/user.c
+
+OBJS = $(patsubst %.c,%.o,$(SRCS)) $(@PLAT@_OBJS)
+
+BUILD_OPT = "--enable-balancer"
+BUILD_SRV = @BUILD_BALANCER@
+
+all-local-srv: $(PROGRAMS) all-cffiles
+
+# $(LTHREAD_LIBS) must be last!
+XLIBS = $(LLOADD_L)
+XXLIBS = $(LLOADD_LIBS) $(SECURITY_LIBS) $(LUTIL_LIBS) $(SYSTEMD_LIBS)
+XXXLIBS = $(LTHREAD_LIBS)
+
+NT_DEPENDS = slapd.exp
+NT_OBJECTS = slapd.exp symdummy.o $(LLOADD_OBJS) version.o
+
+UNIX_DEPENDS = version.o $(LLOADD_L)
+UNIX_OBJECTS = $(OBJS) version.o
+
+LLOADD_DEPENDS = $(@PLAT@_DEPENDS)
+LLOADD_OBJECTS = $(@PLAT@_OBJECTS)
+
+
+lloadd: $(LLOADD_DEPENDS) version.o
+ $(LTLINK) -o $@ $(OBJS) version.o $(LIBS)
+
+slloadd: version.o
+ $(LTLINK) -static -o $@ $(OBJS) version.o $(LIBS)
+
+version.c: Makefile
+ @-$(RM) $@
+ $(MKVERSION) -s -n Versionstr lloadd > $@
+
+version.o: version.c $(OBJS) $(LLOADD_L)
+
+all-cffiles:
+ @if test -n "$(systemdsystemunitdir)"; then \
+ $(SED) -e "s;%LIBEXECDIR%;$(libexecdir);" \
+ $(srcdir)/lloadd.service > lloadd.service.tmp ; \
+ fi
+ touch all-cffiles
+
+clean-local-srv: FORCE
+ $(RM) *.tmp all-cffiles
+
+install-local-srv: install-lloadd install-conf
+
+install-lloadd: FORCE
+ -$(MKDIR) $(DESTDIR)$(libexecdir)
+ @-$(INSTALL) -m 700 -d $(DESTDIR)$(localstatedir)/openldap-lloadd
+ @( \
+ for prg in $(PROGRAMS); do \
+ $(LTINSTALL) $(INSTALLFLAGS) $(STRIP_OPTS) -m 755 $$prg$(EXEEXT) \
+ $(DESTDIR)$(libexecdir); \
+ done \
+ )
+
+install-conf: FORCE
+ @-$(MKDIR) $(DESTDIR)$(sysconfdir)
+ if test -n "$(systemdsystemunitdir)" && test ! -f $(DESTDIR)$(systemdsystemunitdir)/lloadd.service; then \
+ $(MKDIR) $(DESTDIR)$(systemdsystemunitdir); \
+ echo "installing lloadd.service in $(systemdsystemunitdir)"; \
+ echo "$(INSTALL) $(INSTALLFLAGS) -m 644 lloadd.service.tmp $(DESTDIR)$(systemdsystemunitdir)/lloadd.service"; \
+ $(INSTALL) $(INSTALLFLAGS) -m 644 lloadd.service.tmp $(DESTDIR)$(systemdsystemunitdir)/lloadd.service; \
+ fi
diff --git a/servers/lloadd/backend.c b/servers/lloadd/backend.c
new file mode 100644
index 0000000..ab0e932
--- /dev/null
+++ b/servers/lloadd/backend.c
@@ -0,0 +1,736 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <ac/socket.h>
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include <event2/event.h>
+#include <event2/dns.h>
+
+#include "lutil.h"
+#include "lload.h"
+
+static void
+upstream_connect_cb( evutil_socket_t s, short what, void *arg )
+{
+ LloadPendingConnection *conn = arg;
+ LloadBackend *b = conn->backend;
+ int error = 0, rc = -1;
+ epoch_t epoch;
+
+ checked_lock( &b->b_mutex );
+ Debug( LDAP_DEBUG_CONNS, "upstream_connect_cb: "
+ "fd=%d connection callback for backend uri='%s'\n",
+ s, b->b_uri.bv_val );
+
+ if ( s != conn->fd ) {
+ /* backend_reset has been here first */
+ goto preempted;
+ }
+
+ epoch = epoch_join();
+
+ if ( what == EV_WRITE ) {
+ socklen_t optlen = sizeof(error);
+
+ if ( getsockopt( conn->fd, SOL_SOCKET, SO_ERROR, (void *)&error,
+ &optlen ) < 0 ) {
+ goto done;
+ }
+ if ( error == EINTR || error == EINPROGRESS || error == EWOULDBLOCK ) {
+ checked_unlock( &b->b_mutex );
+ epoch_leave( epoch );
+ return;
+ } else if ( error ) {
+ goto done;
+ } else if ( upstream_init( s, conn->backend ) == NULL ) {
+ goto done;
+ }
+ rc = LDAP_SUCCESS;
+ }
+
+done:
+ epoch_leave( epoch );
+
+ LDAP_LIST_REMOVE( conn, next );
+ if ( rc ) {
+ evutil_closesocket( conn->fd );
+ b->b_opening--;
+ b->b_failed++;
+ if ( what & EV_TIMEOUT ) {
+ Debug( LDAP_DEBUG_ANY, "upstream_connect_cb: "
+ "fd=%d connection timed out\n",
+ s );
+ } else {
+ char ebuf[128];
+ Debug( LDAP_DEBUG_ANY, "upstream_connect_cb: "
+ "fd=%d connection set up failed%s%s\n",
+ s, error ? ": " : "",
+ error ? sock_errstr( error, ebuf, sizeof(ebuf) ) : "" );
+ }
+ backend_retry( b );
+ }
+preempted:
+ checked_unlock( &b->b_mutex );
+
+ event_free( conn->event );
+ ch_free( conn );
+}
+
+static void
+upstream_name_cb( int result, struct evutil_addrinfo *res, void *arg )
+{
+ LloadBackend *b = arg;
+ ber_socket_t s = AC_SOCKET_INVALID;
+ epoch_t epoch;
+ int rc;
+
+ if ( result == EVUTIL_EAI_CANCEL ) {
+ Debug( LDAP_DEBUG_ANY, "upstream_name_cb: "
+ "cancelled\n" );
+ return;
+ }
+
+ checked_lock( &b->b_mutex );
+ /* We were already running when backend_reset tried to cancel us, but were
+ * already stuck waiting for the mutex, nothing to do and b_opening has
+ * been decremented as well */
+ if ( b->b_dns_req == NULL ) {
+ checked_unlock( &b->b_mutex );
+ return;
+ }
+ b->b_dns_req = NULL;
+
+ epoch = epoch_join();
+ if ( result || !res ) {
+ Debug( LDAP_DEBUG_ANY, "upstream_name_cb: "
+ "name resolution failed for backend '%s': %s\n",
+ b->b_uri.bv_val, evutil_gai_strerror( result ) );
+ goto fail;
+ }
+
+ /* TODO: if we get failures, try the other addrinfos */
+ if ( (s = socket( res->ai_family, SOCK_STREAM, 0 )) ==
+ AC_SOCKET_INVALID ) {
+ goto fail;
+ }
+
+ if ( ber_pvt_socket_set_nonblock( s, 1 ) ) {
+ goto fail;
+ }
+
+#if defined(SO_KEEPALIVE) || defined(TCP_NODELAY)
+ if ( b->b_proto == LDAP_PROTO_TCP ) {
+ int dummy = 1;
+#ifdef SO_KEEPALIVE
+ if ( setsockopt( s, SOL_SOCKET, SO_KEEPALIVE, (char *)&dummy,
+ sizeof(dummy) ) == AC_SOCKET_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: "
+ "setsockopt(%d, SO_KEEPALIVE) failed (ignored).\n",
+ s );
+ }
+ if ( bindconf.sb_keepalive.sk_idle > 0 ) {
+#ifdef TCP_KEEPIDLE
+ if ( setsockopt( s, IPPROTO_TCP, TCP_KEEPIDLE,
+ (void *)&bindconf.sb_keepalive.sk_idle,
+ sizeof(bindconf.sb_keepalive.sk_idle) ) ==
+ AC_SOCKET_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: "
+ "setsockopt(%d, TCP_KEEPIDLE) failed (ignored).\n",
+ s );
+ }
+#else
+ Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: "
+ "sockopt TCP_KEEPIDLE not supported on this system.\n" );
+#endif /* TCP_KEEPIDLE */
+ }
+ if ( bindconf.sb_keepalive.sk_probes > 0 ) {
+#ifdef TCP_KEEPCNT
+ if ( setsockopt( s, IPPROTO_TCP, TCP_KEEPCNT,
+ (void *)&bindconf.sb_keepalive.sk_probes,
+ sizeof(bindconf.sb_keepalive.sk_probes) ) ==
+ AC_SOCKET_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: "
+ "setsockopt(%d, TCP_KEEPCNT) failed (ignored).\n",
+ s );
+ }
+#else
+ Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: "
+ "sockopt TCP_KEEPCNT not supported on this system.\n" );
+#endif /* TCP_KEEPCNT */
+ }
+ if ( bindconf.sb_keepalive.sk_interval > 0 ) {
+#ifdef TCP_KEEPINTVL
+ if ( setsockopt( s, IPPROTO_TCP, TCP_KEEPINTVL,
+ (void *)&bindconf.sb_keepalive.sk_interval,
+ sizeof(bindconf.sb_keepalive.sk_interval) ) ==
+ AC_SOCKET_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: "
+ "setsockopt(%d, TCP_KEEPINTVL) failed (ignored).\n",
+ s );
+ }
+#else
+ Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: "
+ "sockopt TCP_KEEPINTVL not supported on this system.\n" );
+#endif /* TCP_KEEPINTVL */
+ }
+#endif /* SO_KEEPALIVE */
+ if ( bindconf.sb_tcp_user_timeout > 0 ) {
+#ifdef TCP_USER_TIMEOUT
+ if ( setsockopt( s, IPPROTO_TCP, TCP_USER_TIMEOUT,
+ (void *)&bindconf.sb_tcp_user_timeout,
+ sizeof(bindconf.sb_tcp_user_timeout) ) ==
+ AC_SOCKET_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: "
+ "setsockopt(%d, TCP_USER_TIMEOUT) failed (ignored).\n",
+ s );
+ }
+#else
+ Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: "
+ "sockopt TCP_USER_TIMEOUT not supported on this "
+ "system.\n" );
+#endif /* TCP_USER_TIMEOUT */
+ }
+#ifdef TCP_NODELAY
+ if ( setsockopt( s, IPPROTO_TCP, TCP_NODELAY, (char *)&dummy,
+ sizeof(dummy) ) == AC_SOCKET_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "upstream_name_cb: "
+ "setsockopt(%d, TCP_NODELAY) failed (ignored).\n",
+ s );
+ }
+#endif /* TCP_NODELAY */
+ }
+#endif /* SO_KEEPALIVE || TCP_NODELAY */
+
+ if ( res->ai_family == PF_INET ) {
+ struct sockaddr_in *ai = (struct sockaddr_in *)res->ai_addr;
+ ai->sin_port = htons( b->b_port );
+ rc = connect( s, (struct sockaddr *)ai, res->ai_addrlen );
+ } else {
+ struct sockaddr_in6 *ai = (struct sockaddr_in6 *)res->ai_addr;
+ ai->sin6_port = htons( b->b_port );
+ rc = connect( s, (struct sockaddr *)ai, res->ai_addrlen );
+ }
+ /* Asynchronous connect */
+ if ( rc ) {
+ LloadPendingConnection *conn;
+
+ if ( errno != EINPROGRESS && errno != EWOULDBLOCK ) {
+ Debug( LDAP_DEBUG_ANY, "upstream_name_cb: "
+ "failed to connect to server '%s'\n",
+ b->b_uri.bv_val );
+ evutil_closesocket( s );
+ goto fail;
+ }
+
+ conn = ch_calloc( 1, sizeof(LloadPendingConnection) );
+ LDAP_LIST_ENTRY_INIT( conn, next );
+ conn->backend = b;
+ conn->fd = s;
+
+ conn->event = event_new( lload_get_base( s ), s, EV_WRITE|EV_PERSIST,
+ upstream_connect_cb, conn );
+ if ( !conn->event ) {
+ Debug( LDAP_DEBUG_ANY, "upstream_name_cb: "
+ "failed to acquire an event to finish upstream "
+ "connection setup.\n" );
+ ch_free( conn );
+ evutil_closesocket( s );
+ goto fail;
+ }
+
+ event_add( conn->event, lload_timeout_net );
+ LDAP_LIST_INSERT_HEAD( &b->b_connecting, conn, next );
+ Debug( LDAP_DEBUG_CONNS, "upstream_name_cb: "
+ "connection to backend uri=%s in progress\n",
+ b->b_uri.bv_val );
+ } else if ( upstream_init( s, b ) == NULL ) {
+ goto fail;
+ }
+
+ checked_unlock( &b->b_mutex );
+ evutil_freeaddrinfo( res );
+ epoch_leave( epoch );
+ return;
+
+fail:
+ if ( s != AC_SOCKET_INVALID ) {
+ evutil_closesocket( s );
+ }
+ b->b_opening--;
+ b->b_failed++;
+ backend_retry( b );
+ checked_unlock( &b->b_mutex );
+ if ( res ) {
+ evutil_freeaddrinfo( res );
+ }
+ epoch_leave( epoch );
+}
+
+LloadConnection *
+backend_select( LloadOperation *op, int *res )
+{
+ LloadBackend *b, *first, *next;
+
+ checked_lock( &backend_mutex );
+ first = b = current_backend;
+ checked_unlock( &backend_mutex );
+
+ *res = LDAP_UNAVAILABLE;
+
+ if ( !first ) {
+ return NULL;
+ }
+
+ /* TODO: Two runs, one with trylock, then one actually locked if we don't
+ * find anything? */
+ do {
+ lload_c_head *head;
+ LloadConnection *c;
+
+ checked_lock( &b->b_mutex );
+ next = LDAP_CIRCLEQ_LOOP_NEXT( &backend, b, b_next );
+
+ if ( b->b_max_pending && b->b_n_ops_executing >= b->b_max_pending ) {
+ Debug( LDAP_DEBUG_CONNS, "backend_select: "
+ "backend %s too busy\n",
+ b->b_uri.bv_val );
+ checked_unlock( &b->b_mutex );
+ b = next;
+ *res = LDAP_BUSY;
+ continue;
+ }
+
+ if ( op->o_tag == LDAP_REQ_BIND
+#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
+ && !(lload_features & LLOAD_FEATURE_VC)
+#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
+ ) {
+ head = &b->b_bindconns;
+ } else {
+ head = &b->b_conns;
+ }
+ if ( !LDAP_CIRCLEQ_EMPTY( head ) ) {
+ *res = LDAP_BUSY;
+ }
+
+ LDAP_CIRCLEQ_FOREACH ( c, head, c_next ) {
+ checked_lock( &c->c_io_mutex );
+ CONNECTION_LOCK(c);
+ if ( c->c_state == LLOAD_C_READY && !c->c_pendingber &&
+ ( b->b_max_conn_pending == 0 ||
+ c->c_n_ops_executing < b->b_max_conn_pending ) ) {
+ Debug( LDAP_DEBUG_CONNS, "backend_select: "
+ "selected connection connid=%lu for client "
+ "connid=%lu msgid=%d\n",
+ c->c_connid, op->o_client_connid, op->o_client_msgid );
+
+ /* c_state is DYING if we're about to be unlinked */
+ assert( IS_ALIVE( c, c_live ) );
+
+ /*
+ * Round-robin step:
+ * Rotate the queue to put this connection at the end, same for
+ * the backend.
+ */
+ LDAP_CIRCLEQ_MAKE_TAIL( head, c, c_next );
+
+ checked_lock( &backend_mutex );
+ current_backend = next;
+ checked_unlock( &backend_mutex );
+
+ b->b_n_ops_executing++;
+ if ( op->o_tag == LDAP_REQ_BIND ) {
+ b->b_counters[LLOAD_STATS_OPS_BIND].lc_ops_received++;
+ } else {
+ b->b_counters[LLOAD_STATS_OPS_OTHER].lc_ops_received++;
+ }
+ c->c_n_ops_executing++;
+ c->c_counters.lc_ops_received++;
+
+ checked_unlock( &b->b_mutex );
+ *res = LDAP_SUCCESS;
+ CONNECTION_ASSERT_LOCKED(c);
+ assert_locked( &c->c_io_mutex );
+ return c;
+ }
+ CONNECTION_UNLOCK(c);
+ checked_unlock( &c->c_io_mutex );
+ }
+ checked_unlock( &b->b_mutex );
+
+ b = next;
+ } while ( b != first );
+
+ return NULL;
+}
+
+/*
+ * Will schedule a connection attempt if there is a need for it. Need exclusive
+ * access to backend, its b_mutex is not touched here, though.
+ */
+void
+backend_retry( LloadBackend *b )
+{
+ int requested;
+
+ if ( slapd_shutdown ) {
+ Debug( LDAP_DEBUG_CONNS, "backend_retry: "
+ "shutting down\n" );
+ return;
+ }
+ assert_locked( &b->b_mutex );
+
+ requested = b->b_numconns;
+#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
+ if ( !(lload_features & LLOAD_FEATURE_VC) )
+#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
+ {
+ requested += b->b_numbindconns;
+ }
+
+ if ( b->b_active + b->b_bindavail + b->b_opening >= requested ) {
+ Debug( LDAP_DEBUG_CONNS, "backend_retry: "
+ "no more connections needed for this backend\n" );
+ assert_locked( &b->b_mutex );
+ return;
+ }
+
+ if ( b->b_opening > 0 ) {
+ Debug( LDAP_DEBUG_CONNS, "backend_retry: "
+ "retry in progress already\n" );
+ assert( b->b_opening == 1 );
+ assert_locked( &b->b_mutex );
+ return;
+ }
+
+ /* We incremented b_opening when we activated the event, so it can't be
+ * pending */
+ assert( !event_pending( b->b_retry_event, EV_TIMEOUT, NULL ) );
+ b->b_opening++;
+
+ if ( b->b_failed > 0 ) {
+ Debug( LDAP_DEBUG_CONNS, "backend_retry: "
+ "scheduling a retry in %d ms\n",
+ b->b_retry_timeout );
+ event_add( b->b_retry_event, &b->b_retry_tv );
+ assert_locked( &b->b_mutex );
+ return;
+ }
+
+ Debug( LDAP_DEBUG_CONNS, "backend_retry: "
+ "scheduling re-connection straight away\n" );
+
+ if ( ldap_pvt_thread_pool_submit2(
+ &connection_pool, backend_connect_task, b, &b->b_cookie ) ) {
+ Debug( LDAP_DEBUG_ANY, "backend_retry: "
+ "failed to submit retry task, scheduling a retry instead\n" );
+ /* The current implementation of ldap_pvt_thread_pool_submit2 can fail
+ * and still set (an invalid) cookie */
+ b->b_cookie = NULL;
+ b->b_failed++;
+ event_add( b->b_retry_event, &b->b_retry_tv );
+ }
+ assert_locked( &b->b_mutex );
+}
+
+void
+backend_connect( evutil_socket_t s, short what, void *arg )
+{
+ struct evutil_addrinfo hints = {};
+ LloadBackend *b = arg;
+ struct evdns_getaddrinfo_request *request, *placeholder;
+ char *hostname;
+ epoch_t epoch;
+
+ checked_lock( &b->b_mutex );
+ assert( b->b_dns_req == NULL );
+
+ if ( b->b_cookie ) {
+ b->b_cookie = NULL;
+ }
+
+ if ( slapd_shutdown ) {
+ Debug( LDAP_DEBUG_CONNS, "backend_connect: "
+ "doing nothing, shutdown in progress\n" );
+ b->b_opening--;
+ checked_unlock( &b->b_mutex );
+ return;
+ }
+
+ epoch = epoch_join();
+
+ Debug( LDAP_DEBUG_CONNS, "backend_connect: "
+ "%sattempting connection to %s\n",
+ (what & EV_TIMEOUT) ? "retry timeout finished, " : "",
+ b->b_host );
+
+#ifdef LDAP_PF_LOCAL
+ if ( b->b_proto == LDAP_PROTO_IPC ) {
+ struct sockaddr_un addr;
+ ber_socket_t s = socket( PF_LOCAL, SOCK_STREAM, 0 );
+ int rc;
+
+ if ( s == AC_SOCKET_INVALID ) {
+ goto fail;
+ }
+
+ rc = ber_pvt_socket_set_nonblock( s, 1 );
+ if ( rc ) {
+ evutil_closesocket( s );
+ goto fail;
+ }
+
+ if ( strlen( b->b_host ) > ( sizeof(addr.sun_path) - 1 ) ) {
+ evutil_closesocket( s );
+ goto fail;
+ }
+ memset( &addr, '\0', sizeof(addr) );
+ addr.sun_family = AF_LOCAL;
+ strcpy( addr.sun_path, b->b_host );
+
+ rc = connect(
+ s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un) );
+ /* Asynchronous connect */
+ if ( rc ) {
+ LloadPendingConnection *conn;
+
+ if ( errno != EINPROGRESS && errno != EWOULDBLOCK ) {
+ evutil_closesocket( s );
+ goto fail;
+ }
+
+ conn = ch_calloc( 1, sizeof(LloadPendingConnection) );
+ LDAP_LIST_ENTRY_INIT( conn, next );
+ conn->backend = b;
+ conn->fd = s;
+
+ conn->event = event_new( lload_get_base( s ), s,
+ EV_WRITE|EV_PERSIST, upstream_connect_cb, conn );
+ if ( !conn->event ) {
+ Debug( LDAP_DEBUG_ANY, "backend_connect: "
+ "failed to acquire an event to finish upstream "
+ "connection setup.\n" );
+ ch_free( conn );
+ evutil_closesocket( s );
+ goto fail;
+ }
+
+ event_add( conn->event, lload_timeout_net );
+ LDAP_LIST_INSERT_HEAD( &b->b_connecting, conn, next );
+ Debug( LDAP_DEBUG_CONNS, "backend_connect: "
+ "connection to backend uri=%s in progress\n",
+ b->b_uri.bv_val );
+ } else if ( upstream_init( s, b ) == NULL ) {
+ goto fail;
+ }
+
+ checked_unlock( &b->b_mutex );
+ epoch_leave( epoch );
+ return;
+ }
+#endif /* LDAP_PF_LOCAL */
+
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = EVUTIL_AI_CANONNAME;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ hostname = b->b_host;
+
+ /*
+ * Picking any value on the stack. This is unique to our thread without
+ * having to call ldap_pvt_thread_self.
+ * We might have to revert to using ldap_pvt_thread_self eventually since
+ * this betrays where exactly our stack lies - potentially weakening some
+ * protections like ASLR.
+ */
+ placeholder = (struct evdns_getaddrinfo_request *)&request;
+ b->b_dns_req = placeholder;
+ checked_unlock( &b->b_mutex );
+
+ request = evdns_getaddrinfo(
+ dnsbase, hostname, NULL, &hints, upstream_name_cb, b );
+
+ checked_lock( &b->b_mutex );
+ assert( request || b->b_dns_req != placeholder );
+
+ /* Record the request, unless upstream_name_cb or another thread
+ * cleared it. Another thread is usually backend_reset or backend_connect
+ * if upstream_name_cb finished and scheduled another one */
+ if ( b->b_dns_req == placeholder ) {
+ b->b_dns_req = request;
+ }
+ checked_unlock( &b->b_mutex );
+ epoch_leave( epoch );
+ return;
+
+fail:
+ b->b_opening--;
+ b->b_failed++;
+ backend_retry( b );
+ checked_unlock( &b->b_mutex );
+ epoch_leave( epoch );
+}
+
+void *
+backend_connect_task( void *ctx, void *arg )
+{
+ backend_connect( -1, 0, arg );
+ return NULL;
+}
+
+/*
+ * Needs exclusive access to the backend and no other thread is allowed to call
+ * backend_retry while we're handling this.
+ *
+ * If gentle == 0, a full pause must be in effect, else we risk deadlocking on
+ * event_free().
+ */
+void
+backend_reset( LloadBackend *b, int gentle )
+{
+ assert_locked( &b->b_mutex );
+ if ( b->b_cookie ) {
+ if ( ldap_pvt_thread_pool_retract( b->b_cookie ) ) {
+ b->b_cookie = NULL;
+ b->b_opening--;
+ } else {
+ /*
+ * The task might not be cancelable because it just started
+ * executing.
+ *
+ * Shutdown should be the only time when the thread pool is
+ * in that state. Keep the cookie in to keep an eye on whether
+ * it's finished yet.
+ */
+ assert( slapd_shutdown );
+ }
+ }
+ /* Not safe to hold our mutex and call event_del/free if the event's
+ * callback is running, relinquish the mutex while we do so. */
+ if ( b->b_retry_event &&
+ event_pending( b->b_retry_event, EV_TIMEOUT, NULL ) ) {
+ assert( b->b_failed );
+ checked_unlock( &b->b_mutex );
+ event_del( b->b_retry_event );
+ checked_lock( &b->b_mutex );
+ b->b_opening--;
+ }
+ if ( b->b_dns_req ) {
+ evdns_getaddrinfo_cancel( b->b_dns_req );
+ b->b_dns_req = NULL;
+ b->b_opening--;
+ }
+ while ( !LDAP_LIST_EMPTY( &b->b_connecting ) ) {
+ LloadPendingConnection *pending = LDAP_LIST_FIRST( &b->b_connecting );
+
+ Debug( LDAP_DEBUG_CONNS, "backend_reset: "
+ "destroying socket pending connect() fd=%d\n",
+ pending->fd );
+
+ event_active( pending->event, EV_WRITE, 0 );
+ evutil_closesocket( pending->fd );
+ pending->fd = -1;
+ LDAP_LIST_REMOVE( pending, next );
+
+ if ( !gentle ) {
+ /* None of the event bases are running, we're safe to free the
+ * event right now and potentially free the backend itself */
+ event_free( pending->event );
+ ch_free( pending );
+ }
+ /* else, just let the event dispose of the resources on its own later */
+ b->b_opening--;
+ }
+ connections_walk(
+ &b->b_mutex, &b->b_preparing, lload_connection_close, &gentle );
+ assert( LDAP_CIRCLEQ_EMPTY( &b->b_preparing ) );
+ assert( b->b_opening == ( b->b_cookie ? 1 : 0 ) );
+ b->b_failed = 0;
+
+ connections_walk_last( &b->b_mutex, &b->b_bindconns, b->b_last_bindconn,
+ lload_connection_close, &gentle );
+ assert( gentle || b->b_bindavail == 0 );
+
+ connections_walk_last( &b->b_mutex, &b->b_conns, b->b_last_conn,
+ lload_connection_close, &gentle );
+ assert( gentle || b->b_active == 0 );
+ assert_locked( &b->b_mutex );
+}
+
+void
+lload_backend_destroy( LloadBackend *b )
+{
+ LloadBackend *next = LDAP_CIRCLEQ_LOOP_NEXT( &backend, b, b_next );
+
+ Debug( LDAP_DEBUG_CONNS, "lload_backend_destroy: "
+ "destroying backend uri='%s', numconns=%d, numbindconns=%d\n",
+ b->b_uri.bv_val, b->b_numconns, b->b_numbindconns );
+
+ checked_lock( &b->b_mutex );
+ b->b_numconns = b->b_numbindconns = 0;
+ backend_reset( b, 0 );
+
+ LDAP_CIRCLEQ_REMOVE( &backend, b, b_next );
+ if ( b == next ) {
+ current_backend = NULL;
+ } else {
+ current_backend = next;
+ }
+
+#ifdef BALANCER_MODULE
+ if ( b->b_monitor ) {
+ BackendDB *be;
+ struct berval monitordn = BER_BVC("cn=monitor");
+ int rc;
+
+ be = select_backend( &monitordn, 0 );
+
+ /* FIXME: implement proper subsys shutdown in back-monitor or make
+ * backend just an entry, not a subsys */
+ rc = b->b_monitor->mss_destroy( be, b->b_monitor );
+ assert( rc == LDAP_SUCCESS );
+ }
+#endif /* BALANCER_MODULE */
+ checked_unlock( &b->b_mutex );
+ ldap_pvt_thread_mutex_destroy( &b->b_mutex );
+
+ if ( b->b_retry_event ) {
+ event_del( b->b_retry_event );
+ event_free( b->b_retry_event );
+ b->b_retry_event = NULL;
+ }
+
+ ch_free( b->b_host );
+ ch_free( b->b_uri.bv_val );
+ ch_free( b->b_name.bv_val );
+ ch_free( b );
+}
+
+void
+lload_backends_destroy( void )
+{
+ while ( !LDAP_CIRCLEQ_EMPTY( &backend ) ) {
+ LloadBackend *b = LDAP_CIRCLEQ_FIRST( &backend );
+
+ lload_backend_destroy( b );
+ }
+}
diff --git a/servers/lloadd/bind.c b/servers/lloadd/bind.c
new file mode 100644
index 0000000..2335cce
--- /dev/null
+++ b/servers/lloadd/bind.c
@@ -0,0 +1,992 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <ac/socket.h>
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include "lutil.h"
+#include "lload.h"
+
+struct berval mech_external = BER_BVC("EXTERNAL");
+
+int
+bind_mech_external(
+ LloadConnection *client,
+ LloadOperation *op,
+ struct berval *credentials )
+{
+ BerValue binddn;
+ void *ssl;
+ char *ptr, *message = "";
+ int result = LDAP_SUCCESS;
+
+ CONNECTION_ASSERT_LOCKED(client);
+ client->c_state = LLOAD_C_READY;
+ client->c_type = LLOAD_C_OPEN;
+
+ op->o_res = LLOAD_OP_COMPLETED;
+
+ /*
+ * We only support implicit assertion.
+ *
+ * Although RFC 4513 says the credentials field must be missing, RFC 4422
+ * doesn't and libsasl2 will pass a zero-length string to send. We have to
+ * allow that.
+ */
+ if ( !BER_BVISEMPTY( credentials ) ) {
+ result = LDAP_UNWILLING_TO_PERFORM;
+ message = "proxy authorization is not supported";
+ goto done;
+ }
+
+#ifdef HAVE_TLS
+ ssl = ldap_pvt_tls_sb_ctx( client->c_sb );
+ if ( !ssl || ldap_pvt_tls_get_peer_dn( ssl, &binddn, NULL, 0 ) ) {
+ result = LDAP_INVALID_CREDENTIALS;
+ message = "no externally negotiated identity";
+ goto done;
+ }
+ client->c_auth.bv_len = binddn.bv_len + STRLENOF("dn:");
+ client->c_auth.bv_val = ch_malloc( client->c_auth.bv_len + 1 );
+
+ ptr = lutil_strcopy( client->c_auth.bv_val, "dn:" );
+ ptr = lutil_strncopy( ptr, binddn.bv_val, binddn.bv_len );
+ *ptr = '\0';
+
+ ber_memfree( binddn.bv_val );
+
+ if ( !ber_bvstrcasecmp( &client->c_auth, &lloadd_identity ) ) {
+ client->c_type = LLOAD_C_PRIVILEGED;
+ }
+#else /* ! HAVE_TLS */
+ result = LDAP_AUTH_METHOD_NOT_SUPPORTED;
+ message = "requested SASL mechanism not supported";
+#endif /* ! HAVE_TLS */
+
+done:
+ CONNECTION_UNLOCK(client);
+ operation_send_reject( op, result, message, 1 );
+ return LDAP_SUCCESS;
+}
+
+static int
+client_bind(
+ LloadOperation *op,
+ LloadConnection *upstream,
+ struct berval *binddn,
+ ber_tag_t tag,
+ struct berval *auth )
+{
+ ber_printf( upstream->c_pendingber, "t{titOtO}", LDAP_TAG_MESSAGE,
+ LDAP_TAG_MSGID, op->o_upstream_msgid,
+ LDAP_REQ_BIND, &op->o_request,
+ LDAP_TAG_CONTROLS, BER_BV_OPTIONAL( &op->o_ctrls ) );
+
+ return 0;
+}
+
+#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
+static int
+client_bind_as_vc(
+ LloadOperation *op,
+ LloadConnection *upstream,
+ struct berval *binddn,
+ ber_tag_t tag,
+ struct berval *auth )
+{
+ CONNECTION_LOCK(upstream);
+ ber_printf( upstream->c_pendingber, "t{tit{tst{{tOOtOtO}}}}", LDAP_TAG_MESSAGE,
+ LDAP_TAG_MSGID, op->o_upstream_msgid,
+ LDAP_REQ_EXTENDED,
+ LDAP_TAG_EXOP_REQ_OID, LDAP_EXOP_VERIFY_CREDENTIALS,
+ LDAP_TAG_EXOP_REQ_VALUE,
+ LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE, BER_BV_OPTIONAL( &upstream->c_vc_cookie ),
+ &binddn, tag, &auth,
+ LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS, BER_BV_OPTIONAL( &op->o_ctrls ) );
+ CONNECTION_UNLOCK(upstream);
+ return 0;
+}
+#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
+
+/*
+ * The client connection can be in the following states:
+ * 1) there are between zero and many non-bind operations pending
+ * client->c_state == LLOAD_C_READY && client->c_pin_id == 0
+ * 2) there is one bind operation pending (waiting on an upstream response)
+ * a) It is a simple bind
+ * b) It is a SASL bind
+ * 3) there is one SASL bind in progress (received a LDAP_SASL_BIND_IN_PROGRESS
+ * response)
+ *
+ * In cases 2 and 3, client->c_state == LLOAD_C_BINDING, a SASL bind is in
+ * progress/pending if c_sasl_bind_mech is set.
+ *
+ * In the first case, client_reset abandons all operations on the respective
+ * upstreams, case 2a has client_reset send an anonymous bind to upstream to
+ * terminate the bind. In cases 2b and 3, c_pin_id is set and we retrieve the
+ * op. The rest is the same for both.
+ *
+ * If c_pin_id is unset, we request an upstream connection assigned, otherwise,
+ * we try to reuse the pinned upstream. In the case of no upstream, we reject
+ * the request. A SASL bind request means we acquire a new pin_id if we don't
+ * have one already.
+ *
+ * We have to reset c_auth (which holds the current or pending identity) and
+ * make sure we set it up eventually:
+ * - In the case of a simple bind, we already know the final identity being
+ * requested so we set it up immediately
+ * - In SASL binds, for mechanisms we implement ourselves (EXTERNAL), we set it
+ * up at some point
+ * - Otherwise, we have to ask the upstream what it thinks as the bind
+ * succeeds, we send an LDAP "Who Am I?" exop, this is one of the few
+ * requests we send on our own. If we implement the mechanism, we provide the
+ * identity (EXTERNAL uses the client certificate DN)
+ *
+ * At the end of the request processing, if nothing goes wrong, we're in state
+ * 2b (with c_pin_id set to the op's o_pin_id), or state 2a (we could reset
+ * c_pin_id/o_pin_id if we wanted but we don't always do that at the moment).
+ * If something does go wrong, we're either tearing down the client or we
+ * reject the request and switch to state 1 (clearing c_pin_id).
+ *
+ * As usual, we have to make any changes to the target connection before we've
+ * sent the PDU over it - while we are in charge of the read side and nothing
+ * happens there without our ceding control, the other read side could wake up
+ * at any time and preempt us.
+ *
+ * On a response (in handle_bind_response):
+ * - to a simple bind, clear c_auth on a failure otherwise keep it while we
+ * just reset the client to state 1
+ * - failure response to a SASL bind - reset client to state 1
+ * - LDAP_SASL_BIND_IN_PROGRESS - clear o_*_msgid from the op (have to
+ * remove+reinsert it from the respective c_ops!), we need it since it is the
+ * vessel maintaining the pin between client and upstream
+ * - all of the above forward the response immediately
+ * - LDAP_SUCCESS for a SASL bind - we send a "Who Am I?" request to retrieve
+ * the client's DN, only on receiving the response do we finalise the
+ * exchange by forwarding the successful bind response
+ *
+ * We can't do the same for VC Exop since the exchange is finished at the end
+ * and we need a change to the VC Exop spec to have the server (optionally?)
+ * respond with the final authzid (saving us a roundtrip as well).
+ */
+int
+request_bind( LloadConnection *client, LloadOperation *op )
+{
+ LloadConnection *upstream = NULL;
+ BerElement *ber, *copy;
+ struct berval binddn, auth, mech = BER_BVNULL;
+ ber_int_t version;
+ ber_tag_t tag;
+ unsigned long pin;
+ int res, rc = LDAP_SUCCESS;
+
+ CONNECTION_LOCK(client);
+ pin = client->c_pin_id;
+
+ if ( pin ) {
+ LloadOperation *pinned_op, needle = {
+ .o_client_connid = client->c_connid,
+ .o_client_msgid = 0,
+ .o_pin_id = client->c_pin_id,
+ };
+
+ Debug( LDAP_DEBUG_CONNS, "request_bind: "
+ "client connid=%lu is pinned pin=%lu\n",
+ client->c_connid, pin );
+
+ pinned_op =
+ ldap_tavl_delete( &client->c_ops, &needle, operation_client_cmp );
+ if ( pinned_op ) {
+ assert( op->o_tag == pinned_op->o_tag );
+
+ pinned_op->o_client_msgid = op->o_client_msgid;
+
+ /* Preserve the new BerElement and its pointers, reclaim the old
+ * one in operation_destroy_from_client if it's still there */
+ needle.o_ber = pinned_op->o_ber;
+ pinned_op->o_ber = op->o_ber;
+ op->o_ber = needle.o_ber;
+
+ pinned_op->o_request = op->o_request;
+ pinned_op->o_ctrls = op->o_ctrls;
+
+ /* No one has seen this operation yet, plant the pin back in its stead */
+ client->c_n_ops_executing--;
+ op->o_res = LLOAD_OP_COMPLETED;
+ ldap_tavl_delete( &client->c_ops, op, operation_client_cmp );
+ op->o_client = NULL;
+ assert( op->o_upstream == NULL );
+
+ rc = ldap_tavl_insert( &client->c_ops, pinned_op, operation_client_cmp,
+ ldap_avl_dup_error );
+ assert( rc == LDAP_SUCCESS );
+
+ /* No one has seen this operation yet */
+ op->o_refcnt--;
+ operation_destroy( op );
+
+ /* We didn't start a new operation, just continuing an existing one */
+ lload_stats.counters[LLOAD_STATS_OPS_BIND].lc_ops_received--;
+
+ op = pinned_op;
+ }
+ }
+
+ ldap_tavl_delete( &client->c_ops, op, operation_client_cmp );
+ client->c_n_ops_executing--;
+
+ client_reset( client );
+
+ client->c_state = LLOAD_C_BINDING;
+ client->c_type = LLOAD_C_OPEN;
+
+ if ( (copy = ber_alloc()) == NULL ) {
+ goto fail;
+ }
+ ber_init2( copy, &op->o_request, 0 );
+
+ tag = ber_get_int( copy, &version );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_PACKETS, "request_bind: "
+ "failed to parse version field\n" );
+ goto fail;
+ } else if ( version != LDAP_VERSION3 ) {
+ CONNECTION_UNLOCK(client);
+ operation_send_reject(
+ op, LDAP_PROTOCOL_ERROR, "LDAP version unsupported", 1 );
+ CONNECTION_LOCK(client);
+ goto fail;
+ }
+
+ tag = ber_get_stringbv( copy, &binddn, LBER_BV_NOTERM );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_PACKETS, "request_bind: "
+ "failed to parse bind name field\n" );
+ goto fail;
+ }
+
+ if ( !BER_BVISNULL( &client->c_auth ) ) {
+ ch_free( client->c_auth.bv_val );
+ BER_BVZERO( &client->c_auth );
+ }
+
+ tag = ber_skip_element( copy, &auth );
+ if ( tag == LDAP_AUTH_SIMPLE ) {
+ if ( !BER_BVISEMPTY( &binddn ) ) {
+ char *ptr;
+ client->c_auth.bv_len = STRLENOF("dn:") + binddn.bv_len;
+ client->c_auth.bv_val = ch_malloc( client->c_auth.bv_len + 1 );
+
+ ptr = lutil_strcopy( client->c_auth.bv_val, "dn:" );
+ ptr = lutil_strncopy( ptr, binddn.bv_val, binddn.bv_len );
+ *ptr = '\0';
+ }
+
+ if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
+ ber_memfree( client->c_sasl_bind_mech.bv_val );
+ BER_BVZERO( &client->c_sasl_bind_mech );
+ }
+ } else if ( tag == LDAP_AUTH_SASL ) {
+ ber_init2( copy, &auth, 0 );
+
+ if ( ber_get_stringbv( copy, &mech, LBER_BV_NOTERM ) == LBER_ERROR ) {
+ goto fail;
+ }
+ if ( !ber_bvcmp( &mech, &mech_external ) ) {
+ struct berval credentials = BER_BVNULL;
+
+ ber_get_stringbv( copy, &credentials, LBER_BV_NOTERM );
+ rc = bind_mech_external( client, op, &credentials );
+
+ /* terminate the upstream side if client switched mechanisms */
+ if ( pin ) {
+ operation_abandon( op );
+ }
+
+ ber_free( copy, 0 );
+ return rc;
+ } else if ( BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
+ ber_dupbv( &client->c_sasl_bind_mech, &mech );
+ } else if ( ber_bvcmp( &mech, &client->c_sasl_bind_mech ) ) {
+ ber_bvreplace( &client->c_sasl_bind_mech, &mech );
+ }
+ } else {
+ goto fail;
+ }
+
+ rc = ldap_tavl_insert( &client->c_ops, op, operation_client_cmp, ldap_avl_dup_error );
+ assert( rc == LDAP_SUCCESS );
+ client->c_n_ops_executing++;
+ CONNECTION_UNLOCK(client);
+
+ if ( pin ) {
+ checked_lock( &op->o_link_mutex );
+ upstream = op->o_upstream;
+ checked_unlock( &op->o_link_mutex );
+
+ if ( upstream ) {
+ checked_lock( &upstream->c_io_mutex );
+ CONNECTION_LOCK(upstream);
+ if ( !IS_ALIVE( upstream, c_live ) ) {
+ CONNECTION_UNLOCK(upstream);
+ checked_unlock( &upstream->c_io_mutex );
+ upstream = NULL;
+ }
+ }
+ }
+
+ /* If we were pinned but lost the link, don't look for a new upstream, we
+ * have to reject the op and clear pin */
+ if ( upstream ) {
+ /* No need to do anything */
+ } else if ( !pin ) {
+ upstream = backend_select( op, &res );
+ } else {
+ Debug( LDAP_DEBUG_STATS, "request_bind: "
+ "connid=%lu, msgid=%d pinned upstream lost\n",
+ op->o_client_connid, op->o_client_msgid );
+ operation_send_reject( op, LDAP_OTHER,
+ "connection to the remote server has been severed", 1 );
+ pin = 0;
+ goto done;
+ }
+
+ if ( !upstream ) {
+ Debug( LDAP_DEBUG_STATS, "request_bind: "
+ "connid=%lu, msgid=%d no available connection found\n",
+ op->o_client_connid, op->o_client_msgid );
+ operation_send_reject( op, res, "no connections available", 1 );
+ assert( client->c_pin_id == 0 );
+ goto done;
+ }
+ assert_locked( &upstream->c_io_mutex );
+ /*
+ * At this point, either:
+ * - upstream is READY and pin == 0
+ * - upstream is BINDING, pin != 0 and op->o_upstream_msgid == 0
+ *
+ * A pinned upstream we marked for closing at some point ago should have
+ * closed by now.
+ */
+
+ ber = upstream->c_pendingber;
+ if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
+ checked_unlock( &upstream->c_io_mutex );
+ if ( !pin ) {
+ LloadBackend *b = upstream->c_backend;
+
+ upstream->c_n_ops_executing--;
+ CONNECTION_UNLOCK(upstream);
+
+ checked_lock( &b->b_mutex );
+ b->b_n_ops_executing--;
+ operation_update_backend_counters( op, b );
+ checked_unlock( &b->b_mutex );
+ } else {
+ CONNECTION_UNLOCK(upstream);
+ }
+
+ Debug( LDAP_DEBUG_ANY, "request_bind: "
+ "ber_alloc failed\n" );
+
+ operation_unlink( op );
+
+ CONNECTION_LOCK(client);
+ goto fail;
+ }
+ upstream->c_pendingber = ber;
+
+ if ( !pin ) {
+ lload_stats.counters[LLOAD_STATS_OPS_BIND].lc_ops_forwarded++;
+ }
+
+ if ( pin ) {
+ ldap_tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
+ if ( tag == LDAP_AUTH_SIMPLE ) {
+ pin = op->o_pin_id = 0;
+ }
+ } else if ( tag == LDAP_AUTH_SASL && !op->o_pin_id ) {
+ checked_lock( &lload_pin_mutex );
+ pin = op->o_pin_id = lload_next_pin++;
+ Debug( LDAP_DEBUG_CONNS, "request_bind: "
+ "client connid=%lu allocated pin=%lu linking it to upstream "
+ "connid=%lu\n",
+ op->o_client_connid, pin, upstream->c_connid );
+ checked_unlock( &lload_pin_mutex );
+ }
+
+ op->o_upstream = upstream;
+ op->o_upstream_connid = upstream->c_connid;
+ op->o_upstream_msgid = upstream->c_next_msgid++;
+ op->o_res = LLOAD_OP_FAILED;
+
+ /* Was it unlinked in the meantime? No need to send a response since the
+ * client is dead */
+ if ( !IS_ALIVE( op, o_refcnt ) ) {
+ LloadBackend *b = upstream->c_backend;
+
+ upstream->c_n_ops_executing--;
+ checked_unlock( &upstream->c_io_mutex );
+ CONNECTION_UNLOCK(upstream);
+
+ checked_lock( &b->b_mutex );
+ b->b_n_ops_executing--;
+ checked_unlock( &b->b_mutex );
+
+ assert( !IS_ALIVE( client, c_live ) );
+ checked_lock( &op->o_link_mutex );
+ if ( op->o_upstream ) {
+ op->o_upstream = NULL;
+ }
+ checked_unlock( &op->o_link_mutex );
+ rc = -1;
+ goto done;
+ }
+
+ if ( BER_BVISNULL( &mech ) ) {
+ if ( !BER_BVISNULL( &upstream->c_sasl_bind_mech ) ) {
+ ber_memfree( upstream->c_sasl_bind_mech.bv_val );
+ BER_BVZERO( &upstream->c_sasl_bind_mech );
+ }
+ } else if ( ber_bvcmp( &upstream->c_sasl_bind_mech, &mech ) ) {
+ ber_bvreplace( &upstream->c_sasl_bind_mech, &mech );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "request_bind: "
+ "added bind from client connid=%lu to upstream connid=%lu "
+ "as msgid=%d\n",
+ op->o_client_connid, op->o_upstream_connid, op->o_upstream_msgid );
+ if ( ldap_tavl_insert( &upstream->c_ops, op, operation_upstream_cmp,
+ ldap_avl_dup_error ) ) {
+ assert(0);
+ }
+ upstream->c_state = LLOAD_C_BINDING;
+ CONNECTION_UNLOCK(upstream);
+
+#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
+ if ( lload_features & LLOAD_FEATURE_VC ) {
+ rc = client_bind_as_vc( op, upstream, &binddn, tag, &auth );
+ } else
+#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
+ {
+ rc = client_bind( op, upstream, &binddn, tag, &auth );
+ }
+ checked_unlock( &upstream->c_io_mutex );
+
+done:
+
+ CONNECTION_LOCK(client);
+ if ( rc == LDAP_SUCCESS ) {
+ client->c_pin_id = pin;
+ CONNECTION_UNLOCK(client);
+
+ if ( upstream ) {
+ connection_write_cb( -1, 0, upstream );
+ }
+ } else {
+fail:
+ rc = -1;
+
+ client->c_pin_id = 0;
+ CONNECTION_DESTROY(client);
+ }
+
+ ber_free( copy, 0 );
+ return rc;
+}
+
+/*
+ * Remember the response, but first ask the server what
+ * authorization identity has been negotiated.
+ *
+ * Also, this request will fail if the server thinks a SASL
+ * confidentiality/integrity layer has been negotiated so we catch
+ * it early and no other clients are affected.
+ */
+int
+finish_sasl_bind(
+ LloadConnection *upstream,
+ LloadOperation *op,
+ BerElement *ber )
+{
+ BerElement *output;
+ LloadOperation *removed;
+ ber_int_t msgid;
+ int rc;
+
+ CONNECTION_ASSERT_LOCKED(upstream);
+ removed = ldap_tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
+ if ( !removed ) {
+ assert( upstream->c_state != LLOAD_C_BINDING );
+ /* FIXME: has client replaced this bind since? */
+ assert(0);
+ }
+ assert( removed == op && upstream->c_state == LLOAD_C_BINDING );
+
+ CONNECTION_UNLOCK(upstream);
+
+ checked_lock( &upstream->c_io_mutex );
+ output = upstream->c_pendingber;
+ if ( output == NULL && (output = ber_alloc()) == NULL ) {
+ checked_unlock( &upstream->c_io_mutex );
+ CONNECTION_LOCK_DESTROY(upstream);
+ return -1;
+ }
+ upstream->c_pendingber = output;
+
+ msgid = upstream->c_next_msgid++;
+ ber_printf( output, "t{tit{ts}}", LDAP_TAG_MESSAGE,
+ LDAP_TAG_MSGID, msgid,
+ LDAP_REQ_EXTENDED,
+ LDAP_TAG_EXOP_REQ_OID, LDAP_EXOP_WHO_AM_I );
+
+ /* Make sure noone flushes the buffer before we re-insert the operation */
+ CONNECTION_LOCK(upstream);
+ checked_unlock( &upstream->c_io_mutex );
+
+ op->o_upstream_msgid = msgid;
+
+ /* remember the response for later */
+ ber_free( op->o_ber, 1 );
+ op->o_ber = ber;
+
+ /* Could we have been unlinked in the meantime? */
+ rc = ldap_tavl_insert(
+ &upstream->c_ops, op, operation_upstream_cmp, ldap_avl_dup_error );
+ assert( rc == LDAP_SUCCESS );
+
+ CONNECTION_UNLOCK(upstream);
+
+ Debug( LDAP_DEBUG_TRACE, "finish_sasl_bind: "
+ "SASL exchange in lieu of client connid=%lu to upstream "
+ "connid=%lu finished, resolving final authzid name msgid=%d\n",
+ op->o_client_connid, op->o_upstream_connid, op->o_upstream_msgid );
+
+ connection_write_cb( -1, 0, upstream );
+ return LDAP_SUCCESS;
+}
+
+int
+handle_bind_response(
+ LloadConnection *client,
+ LloadOperation *op,
+ BerElement *ber )
+{
+ LloadConnection *upstream;
+ BerValue response;
+ BerElement *copy;
+ LloadOperation *removed;
+ ber_int_t result;
+ ber_tag_t tag;
+ int rc = LDAP_SUCCESS;
+
+ if ( (copy = ber_alloc()) == NULL ) {
+ rc = -1;
+ goto done;
+ }
+
+ tag = ber_peek_element( ber, &response );
+ assert( tag == LDAP_RES_BIND );
+
+ ber_init2( copy, &response, 0 );
+
+ tag = ber_get_enum( copy, &result );
+ ber_free( copy, 0 );
+
+ if ( tag == LBER_ERROR ) {
+ rc = -1;
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_STATS, "handle_bind_response: "
+ "received response for bind request msgid=%d by client "
+ "connid=%lu, result=%d\n",
+ op->o_client_msgid, op->o_client_connid, result );
+
+ checked_lock( &op->o_link_mutex );
+ upstream = op->o_upstream;
+ checked_unlock( &op->o_link_mutex );
+ if ( !upstream ) {
+ return LDAP_SUCCESS;
+ }
+
+ CONNECTION_LOCK(upstream);
+ if ( !ldap_tavl_find( upstream->c_ops, op, operation_upstream_cmp ) ) {
+ /*
+ * operation might not be found because:
+ * - it has timed out (only happens when debugging/hung/...)
+ * a response has been sent for us, we must not send another
+ * - it has been abandoned (new bind, unbind)
+ * no response is expected
+ * - ???
+ */
+ CONNECTION_UNLOCK(upstream);
+ return LDAP_SUCCESS;
+ }
+
+ /*
+ * We might be marked for closing, forward the response if we can, but do
+ * no more if it's a SASL bind - just finish the operation and send failure
+ * in that case (since we can't resolve the bind identity correctly).
+ */
+ if ( upstream->c_state == LLOAD_C_CLOSING ) {
+ /* FIXME: this is too ad-hoc */
+ if ( ( result == LDAP_SUCCESS ||
+ result == LDAP_SASL_BIND_IN_PROGRESS ) &&
+ !BER_BVISNULL( &upstream->c_sasl_bind_mech ) ) {
+ CONNECTION_UNLOCK(upstream);
+ operation_send_reject(
+ op, LDAP_OTHER, "upstream connection is closing", 0 );
+
+ ber_free( ber, 1 );
+ return LDAP_SUCCESS;
+ }
+
+ assert( op->o_client_msgid && op->o_upstream_msgid );
+ op->o_pin_id = 0;
+
+ } else if ( result == LDAP_SASL_BIND_IN_PROGRESS ) {
+ ldap_tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
+ op->o_upstream_msgid = 0;
+ rc = ldap_tavl_insert(
+ &upstream->c_ops, op, operation_upstream_cmp, ldap_avl_dup_error );
+ assert( rc == LDAP_SUCCESS );
+ } else {
+ int sasl_finished = 0;
+ if ( !BER_BVISNULL( &upstream->c_sasl_bind_mech ) ) {
+ sasl_finished = 1;
+ ber_memfree( upstream->c_sasl_bind_mech.bv_val );
+ BER_BVZERO( &upstream->c_sasl_bind_mech );
+ }
+
+ assert( op->o_client_msgid && op->o_upstream_msgid );
+ op->o_pin_id = 0;
+
+ if ( (lload_features & LLOAD_FEATURE_PROXYAUTHZ) && sasl_finished &&
+ result == LDAP_SUCCESS ) {
+ return finish_sasl_bind( upstream, op, ber );
+ }
+ op->o_res = LLOAD_OP_COMPLETED;
+ }
+ CONNECTION_UNLOCK(upstream);
+
+ if ( !op->o_pin_id ) {
+ operation_unlink_upstream( op, upstream );
+ }
+
+ CONNECTION_LOCK(client);
+ removed = ldap_tavl_delete( &client->c_ops, op, operation_client_cmp );
+ assert( !removed || op == removed );
+
+ if ( client->c_state == LLOAD_C_BINDING ) {
+ assert( removed );
+ switch ( result ) {
+ case LDAP_SASL_BIND_IN_PROGRESS:
+ op->o_saved_msgid = op->o_client_msgid;
+ op->o_client_msgid = 0;
+ rc = ldap_tavl_insert( &client->c_ops, op, operation_client_cmp,
+ ldap_avl_dup_error );
+ assert( rc == LDAP_SUCCESS );
+ break;
+ case LDAP_SUCCESS:
+ default: {
+ client->c_state = LLOAD_C_READY;
+ client->c_type = LLOAD_C_OPEN;
+ client->c_pin_id = 0;
+ client->c_n_ops_executing--;
+ if ( !BER_BVISNULL( &client->c_auth ) ) {
+ if ( result != LDAP_SUCCESS ) {
+ ber_memfree( client->c_auth.bv_val );
+ BER_BVZERO( &client->c_auth );
+ } else if ( !ber_bvstrcasecmp(
+ &client->c_auth, &lloadd_identity ) ) {
+ client->c_type = LLOAD_C_PRIVILEGED;
+ }
+ }
+ if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
+ ber_memfree( client->c_sasl_bind_mech.bv_val );
+ BER_BVZERO( &client->c_sasl_bind_mech );
+ }
+ break;
+ }
+ }
+ } else {
+ if ( removed ) {
+ client->c_n_ops_executing--;
+ }
+ assert( client->c_state == LLOAD_C_DYING ||
+ client->c_state == LLOAD_C_CLOSING );
+ }
+ CONNECTION_UNLOCK(client);
+
+done:
+ if ( rc ) {
+ operation_send_reject( op, LDAP_OTHER, "internal error", 1 );
+
+ ber_free( ber, 1 );
+ return LDAP_SUCCESS;
+ }
+ return forward_final_response( client, op, ber );
+}
+
+int
+handle_whoami_response(
+ LloadConnection *client,
+ LloadOperation *op,
+ BerElement *ber )
+{
+ LloadConnection *upstream;
+ BerValue matched, diagmsg;
+ BerElement *saved_response = op->o_ber;
+ LloadOperation *removed;
+ ber_int_t result;
+ ber_tag_t tag;
+ ber_len_t len;
+
+ Debug( LDAP_DEBUG_TRACE, "handle_whoami_response: "
+ "connid=%ld received whoami response in lieu of connid=%ld\n",
+ op->o_upstream_connid, client->c_connid );
+
+ tag = ber_scanf( ber, "{emm" /* "}" */,
+ &result, &matched, &diagmsg );
+ if ( tag == LBER_ERROR ) {
+ operation_send_reject( op, LDAP_OTHER, "upstream protocol error", 0 );
+ return -1;
+ }
+
+ checked_lock( &op->o_link_mutex );
+ upstream = op->o_upstream;
+ checked_unlock( &op->o_link_mutex );
+ if ( !upstream ) {
+ return LDAP_SUCCESS;
+ }
+
+ op->o_res = LLOAD_OP_COMPLETED;
+ /* Clear upstream status */
+ operation_unlink_upstream( op, upstream );
+
+ if ( result == LDAP_PROTOCOL_ERROR ) {
+ LloadBackend *b;
+
+ CONNECTION_LOCK(upstream);
+ b = upstream->c_backend;
+ Debug( LDAP_DEBUG_ANY, "handle_whoami_response: "
+ "Who Am I? extended operation not supported on backend %s, "
+ "proxyauthz with clients that do SASL binds will not work "
+ "msg=%s!\n",
+ b->b_uri.bv_val, diagmsg.bv_val );
+ CONNECTION_UNLOCK(upstream);
+ operation_send_reject( op, LDAP_OTHER, "upstream protocol error", 0 );
+ return -1;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+
+ CONNECTION_LOCK(client);
+
+ assert( client->c_state == LLOAD_C_BINDING ||
+ client->c_state == LLOAD_C_CLOSING );
+
+ assert( BER_BVISNULL( &client->c_auth ) );
+ if ( !BER_BVISNULL( &client->c_auth ) ) {
+ ber_memfree( client->c_auth.bv_val );
+ BER_BVZERO( &client->c_auth );
+ }
+
+ if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
+ tag = ber_scanf( ber, "o", &client->c_auth );
+ if ( tag == LBER_ERROR ) {
+ CONNECTION_DESTROY(client);
+ return -1;
+ }
+ }
+
+ removed = ldap_tavl_delete( &client->c_ops, op, operation_client_cmp );
+ assert( !removed || op == removed );
+ op->o_pin_id = 0;
+ if ( removed ) {
+ client->c_n_ops_executing--;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "handle_whoami_response: "
+ "connid=%ld new authid=%s\n",
+ client->c_connid, client->c_auth.bv_val );
+
+ if ( client->c_state == LLOAD_C_BINDING ) {
+ client->c_state = LLOAD_C_READY;
+ client->c_type = LLOAD_C_OPEN;
+ client->c_pin_id = 0;
+ if ( !BER_BVISNULL( &client->c_auth ) &&
+ !ber_bvstrcasecmp( &client->c_auth, &lloadd_identity ) ) {
+ client->c_type = LLOAD_C_PRIVILEGED;
+ }
+ if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
+ ber_memfree( client->c_sasl_bind_mech.bv_val );
+ BER_BVZERO( &client->c_sasl_bind_mech );
+ }
+ }
+
+ CONNECTION_UNLOCK(client);
+
+ /* defer the disposal of ber to operation_destroy */
+ op->o_ber = ber;
+
+ return forward_final_response( client, op, saved_response );
+}
+
+#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
+int
+handle_vc_bind_response(
+ LloadConnection *client,
+ LloadOperation *op,
+ BerElement *ber )
+{
+ BerElement *output;
+ BerValue matched, diagmsg, creds = BER_BVNULL, controls = BER_BVNULL;
+ ber_int_t result;
+ ber_tag_t tag;
+ ber_len_t len;
+ int rc = 0;
+
+ tag = ber_scanf( ber, "{emm" /* "}" */,
+ &result, &matched, &diagmsg );
+ if ( tag == LBER_ERROR ) {
+ rc = -1;
+ goto done;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if ( result == LDAP_PROTOCOL_ERROR ) {
+ LloadConnection *upstream;
+
+ checked_lock( &op->o_link_mutex );
+ upstream = op->o_upstream;
+ checked_unlock( &op->o_link_mutex );
+ if ( upstream ) {
+ LloadBackend *b;
+
+ CONNECTION_LOCK(upstream);
+ b = upstream->c_backend;
+ Debug( LDAP_DEBUG_ANY, "handle_vc_bind_response: "
+ "VC extended operation not supported on backend %s\n",
+ b->b_uri.bv_val );
+ CONNECTION_UNLOCK(upstream);
+ }
+ }
+
+ Debug( LDAP_DEBUG_STATS, "handle_vc_bind_response: "
+ "received response for bind request msgid=%d by client "
+ "connid=%lu, result=%d\n",
+ op->o_client_msgid, op->o_client_connid, result );
+
+ CONNECTION_LOCK(client);
+
+ if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE ) {
+ if ( !BER_BVISNULL( &client->c_vc_cookie ) ) {
+ ber_memfree( client->c_vc_cookie.bv_val );
+ }
+ tag = ber_scanf( ber, "o", &client->c_vc_cookie );
+ if ( tag == LBER_ERROR ) {
+ rc = -1;
+ CONNECTION_UNLOCK(client);
+ goto done;
+ }
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_SCREDS ) {
+ tag = ber_scanf( ber, "m", &creds );
+ if ( tag == LBER_ERROR ) {
+ rc = -1;
+ CONNECTION_UNLOCK(client);
+ goto done;
+ }
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS ) {
+ tag = ber_scanf( ber, "m", &controls );
+ if ( tag == LBER_ERROR ) {
+ rc = -1;
+ CONNECTION_UNLOCK(client);
+ goto done;
+ }
+ }
+
+ if ( client->c_state == LLOAD_C_BINDING ) {
+ switch ( result ) {
+ case LDAP_SASL_BIND_IN_PROGRESS:
+ break;
+ case LDAP_SUCCESS:
+ default: {
+ client->c_state = LLOAD_C_READY;
+ client->c_type = LLOAD_C_OPEN;
+ client->c_pin_id = 0;
+ if ( result != LDAP_SUCCESS ) {
+ ber_memfree( client->c_auth.bv_val );
+ BER_BVZERO( &client->c_auth );
+ } else if ( !ber_bvstrcasecmp(
+ &client->c_auth, &lloadd_identity ) ) {
+ client->c_type = LLOAD_C_PRIVILEGED;
+ }
+ if ( !BER_BVISNULL( &client->c_vc_cookie ) ) {
+ ber_memfree( client->c_vc_cookie.bv_val );
+ BER_BVZERO( &client->c_vc_cookie );
+ }
+ if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
+ ber_memfree( client->c_sasl_bind_mech.bv_val );
+ BER_BVZERO( &client->c_sasl_bind_mech );
+ }
+ break;
+ }
+ }
+ } else {
+ assert( client->c_state == LLOAD_C_INVALID ||
+ client->c_state == LLOAD_C_CLOSING );
+ }
+ CONNECTION_UNLOCK(client);
+
+ checked_lock( &client->c_io_mutex );
+ output = client->c_pendingber;
+ if ( output == NULL && (output = ber_alloc()) == NULL ) {
+ rc = -1;
+ checked_unlock( &client->c_io_mutex );
+ goto done;
+ }
+ client->c_pendingber = output;
+
+ rc = ber_printf( output, "t{tit{eOOtO}tO}", LDAP_TAG_MESSAGE,
+ LDAP_TAG_MSGID, op->o_client_msgid, LDAP_RES_BIND,
+ result, &matched, &diagmsg,
+ LDAP_TAG_SASL_RES_CREDS, BER_BV_OPTIONAL( &creds ),
+ LDAP_TAG_CONTROLS, BER_BV_OPTIONAL( &controls ) );
+
+ checked_unlock( &client->c_io_mutex );
+ if ( rc >= 0 ) {
+ connection_write_cb( -1, 0, client );
+ rc = 0;
+ }
+
+done:
+ operation_unlink( op );
+ ber_free( ber, 1 );
+ return rc;
+}
+#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
diff --git a/servers/lloadd/client.c b/servers/lloadd/client.c
new file mode 100644
index 0000000..a716260
--- /dev/null
+++ b/servers/lloadd/client.c
@@ -0,0 +1,611 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <ac/socket.h>
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include "lutil.h"
+#include "lload.h"
+
+long lload_client_max_pending = 0;
+
+lload_c_head clients = LDAP_CIRCLEQ_HEAD_INITIALIZER( clients );
+
+ldap_pvt_thread_mutex_t clients_mutex;
+
+static void client_unlink( LloadConnection *upstream );
+
+int
+request_abandon( LloadConnection *c, LloadOperation *op )
+{
+ LloadOperation *request, needle = { .o_client_connid = c->c_connid };
+ int rc = LDAP_SUCCESS;
+
+ op->o_res = LLOAD_OP_COMPLETED;
+
+ if ( ber_decode_int( &op->o_request, &needle.o_client_msgid ) ) {
+ Debug( LDAP_DEBUG_STATS, "request_abandon: "
+ "connid=%lu msgid=%d invalid integer sent in abandon request\n",
+ c->c_connid, op->o_client_msgid );
+
+ operation_unlink( op );
+ CONNECTION_LOCK_DESTROY(c);
+ return -1;
+ }
+
+ CONNECTION_LOCK(c);
+ request = ldap_tavl_find( c->c_ops, &needle, operation_client_cmp );
+ if ( !request ) {
+ Debug( LDAP_DEBUG_STATS, "request_abandon: "
+ "connid=%lu msgid=%d requests abandon of an operation "
+ "msgid=%d not being processed anymore\n",
+ c->c_connid, op->o_client_msgid, needle.o_client_msgid );
+ CONNECTION_UNLOCK(c);
+ goto done;
+ } else if ( request->o_tag == LDAP_REQ_BIND ) {
+ /* RFC 4511 states we must not allow Abandon on Binds */
+ Debug( LDAP_DEBUG_STATS, "request_abandon: "
+ "connid=%lu msgid=%d requests abandon of a bind operation "
+ "msgid=%d\n",
+ c->c_connid, op->o_client_msgid, needle.o_client_msgid );
+ CONNECTION_UNLOCK(c);
+ goto done;
+ }
+ Debug( LDAP_DEBUG_STATS, "request_abandon: "
+ "connid=%lu msgid=%d abandoning %s msgid=%d\n",
+ c->c_connid, op->o_client_msgid,
+ lload_msgtype2str( request->o_tag ), needle.o_client_msgid );
+
+ if ( c->c_state == LLOAD_C_BINDING ) {
+ assert(0);
+ }
+
+ CONNECTION_UNLOCK(c);
+ operation_abandon( request );
+
+done:
+ operation_unlink( op );
+ return rc;
+}
+
+int
+request_process( LloadConnection *client, LloadOperation *op )
+{
+ BerElement *output;
+ LloadConnection *upstream;
+ ber_int_t msgid;
+ int res, rc = LDAP_SUCCESS;
+
+ upstream = backend_select( op, &res );
+ if ( !upstream ) {
+ Debug( LDAP_DEBUG_STATS, "request_process: "
+ "connid=%lu, msgid=%d no available connection found\n",
+ op->o_client_connid, op->o_client_msgid );
+
+ operation_send_reject( op, res, "no connections available", 1 );
+ goto fail;
+ }
+ CONNECTION_ASSERT_LOCKED(upstream);
+ assert_locked( &upstream->c_io_mutex );
+ op->o_upstream = upstream;
+ op->o_upstream_connid = upstream->c_connid;
+ op->o_res = LLOAD_OP_FAILED;
+
+ /* Was it unlinked in the meantime? No need to send a response since the
+ * client is dead */
+ if ( !IS_ALIVE( op, o_refcnt ) ) {
+ LloadBackend *b = upstream->c_backend;
+
+ upstream->c_n_ops_executing--;
+ checked_unlock( &upstream->c_io_mutex );
+ CONNECTION_UNLOCK(upstream);
+
+ checked_lock( &b->b_mutex );
+ b->b_n_ops_executing--;
+ checked_unlock( &b->b_mutex );
+
+ assert( !IS_ALIVE( client, c_live ) );
+ checked_lock( &op->o_link_mutex );
+ if ( op->o_upstream ) {
+ op->o_upstream = NULL;
+ }
+ checked_unlock( &op->o_link_mutex );
+ return -1;
+ }
+
+ output = upstream->c_pendingber;
+ if ( output == NULL && (output = ber_alloc()) == NULL ) {
+ LloadBackend *b = upstream->c_backend;
+
+ upstream->c_n_ops_executing--;
+ CONNECTION_UNLOCK(upstream);
+ checked_unlock( &upstream->c_io_mutex );
+
+ checked_lock( &b->b_mutex );
+ b->b_n_ops_executing--;
+ operation_update_backend_counters( op, b );
+ checked_unlock( &b->b_mutex );
+
+ Debug( LDAP_DEBUG_ANY, "request_process: "
+ "ber_alloc failed\n" );
+
+ rc = -1;
+ goto fail;
+ }
+ upstream->c_pendingber = output;
+
+ op->o_upstream_msgid = msgid = upstream->c_next_msgid++;
+ rc = ldap_tavl_insert(
+ &upstream->c_ops, op, operation_upstream_cmp, ldap_avl_dup_error );
+ CONNECTION_UNLOCK(upstream);
+
+ Debug( LDAP_DEBUG_TRACE, "request_process: "
+ "client connid=%lu added %s msgid=%d to upstream connid=%lu as "
+ "msgid=%d\n",
+ op->o_client_connid, lload_msgtype2str( op->o_tag ),
+ op->o_client_msgid, op->o_upstream_connid, op->o_upstream_msgid );
+ assert( rc == LDAP_SUCCESS );
+
+ lload_stats.counters[LLOAD_STATS_OPS_OTHER].lc_ops_forwarded++;
+
+ if ( (lload_features & LLOAD_FEATURE_PROXYAUTHZ) &&
+ client->c_type != LLOAD_C_PRIVILEGED ) {
+ CONNECTION_LOCK(client);
+ Debug( LDAP_DEBUG_TRACE, "request_process: "
+ "proxying identity %s to upstream\n",
+ client->c_auth.bv_val );
+ ber_printf( output, "t{titOt{{sbO}" /* "}}" */, LDAP_TAG_MESSAGE,
+ LDAP_TAG_MSGID, msgid,
+ op->o_tag, &op->o_request,
+ LDAP_TAG_CONTROLS,
+ LDAP_CONTROL_PROXY_AUTHZ, 1, &client->c_auth );
+ CONNECTION_UNLOCK(client);
+
+ if ( !BER_BVISNULL( &op->o_ctrls ) ) {
+ ber_write( output, op->o_ctrls.bv_val, op->o_ctrls.bv_len, 0 );
+ }
+
+ ber_printf( output, /* "{{" */ "}}" );
+ } else {
+ ber_printf( output, "t{titOtO}", LDAP_TAG_MESSAGE,
+ LDAP_TAG_MSGID, msgid,
+ op->o_tag, &op->o_request,
+ LDAP_TAG_CONTROLS, BER_BV_OPTIONAL( &op->o_ctrls ) );
+ }
+ checked_unlock( &upstream->c_io_mutex );
+
+ connection_write_cb( -1, 0, upstream );
+ return rc;
+
+fail:
+ if ( upstream ) {
+ CONNECTION_LOCK_DESTROY(upstream);
+
+ operation_send_reject( op, LDAP_OTHER, "internal error", 0 );
+ }
+
+ operation_unlink( op );
+ if ( rc ) {
+ CONNECTION_LOCK_DESTROY(client);
+ }
+ return rc;
+}
+
+int
+handle_one_request( LloadConnection *c )
+{
+ BerElement *ber;
+ LloadOperation *op = NULL;
+ RequestHandler handler = NULL;
+ int over_limit = 0;
+ enum sc_state state;
+ enum sc_io_state io_state;
+
+ ber = c->c_currentber;
+ c->c_currentber = NULL;
+
+ CONNECTION_LOCK(c);
+ op = operation_init( c, ber );
+ if ( !op ) {
+ Debug( LDAP_DEBUG_ANY, "handle_one_request: "
+ "connid=%lu, operation_init failed\n",
+ c->c_connid );
+ CONNECTION_DESTROY(c);
+ ber_free( ber, 1 );
+ return -1;
+ }
+ if ( lload_client_max_pending &&
+ c->c_n_ops_executing >= lload_client_max_pending ) {
+ over_limit = 1;
+ }
+
+ /*
+ * Remember the current state so we don't have to lock again,
+ * we're only screening whether we can keep going, e.g. noone can change
+ * state to LLOAD_C_BINDING from under us (would imply a new operation was
+ * received but that's us), but the opposite is possible - a Bind response
+ * could be received and processed in the meantime.
+ */
+ state = c->c_state;
+ CONNECTION_UNLOCK(c);
+
+ switch ( op->o_tag ) {
+ case LDAP_REQ_UNBIND:
+ /* There is never a response for this operation */
+ op->o_res = LLOAD_OP_COMPLETED;
+ operation_unlink( op );
+
+ Debug( LDAP_DEBUG_STATS, "handle_one_request: "
+ "received unbind, closing client connid=%lu\n",
+ c->c_connid );
+ CONNECTION_LOCK_DESTROY(c);
+ return -1;
+ case LDAP_REQ_BIND:
+ handler = request_bind;
+ break;
+ case LDAP_REQ_ABANDON:
+ /* We can't send a response to abandon requests even if a bind is
+ * currently in progress */
+ return request_abandon( c, op );
+ case LDAP_REQ_EXTENDED:
+ default:
+ if ( state == LLOAD_C_BINDING ) {
+ operation_send_reject(
+ op, LDAP_PROTOCOL_ERROR, "bind in progress", 0 );
+ return LDAP_SUCCESS;
+ }
+ if ( over_limit ) {
+ operation_send_reject( op, LDAP_BUSY,
+ "pending operation limit reached on this connection",
+ 0 );
+ return LDAP_SUCCESS;
+ }
+
+ checked_lock( &c->c_io_mutex );
+ io_state = c->c_io_state;
+ checked_unlock( &c->c_io_mutex );
+ if ( io_state & LLOAD_C_READ_PAUSE ) {
+ operation_send_reject( op, LDAP_BUSY,
+ "writing side backlogged, please keep reading", 0 );
+ return LDAP_SUCCESS;
+ }
+
+ if ( op->o_tag == LDAP_REQ_EXTENDED ) {
+ handler = request_extended;
+ } else {
+ handler = request_process;
+ }
+ break;
+ }
+
+ if ( state == LLOAD_C_CLOSING ) {
+ operation_send_reject(
+ op, LDAP_UNAVAILABLE, "connection is shutting down", 0 );
+ return LDAP_SUCCESS;
+ }
+
+ return handler( c, op );
+}
+
+#ifdef HAVE_TLS
+/*
+ * The connection has a token assigned to it when the callback is set up.
+ */
+void
+client_tls_handshake_cb( evutil_socket_t s, short what, void *arg )
+{
+ LloadConnection *c = arg;
+ epoch_t epoch;
+ int rc = 0;
+
+ if ( what & EV_TIMEOUT ) {
+ Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
+ "connid=%lu, timeout reached, destroying\n",
+ c->c_connid );
+ goto fail;
+ }
+
+ /*
+ * In case of StartTLS, make sure we flush the response first.
+ * Also before we try to read anything from the connection, it isn't
+ * permitted to Abandon a StartTLS exop per RFC4511 anyway.
+ */
+ checked_lock( &c->c_io_mutex );
+ if ( c->c_pendingber ) {
+ checked_unlock( &c->c_io_mutex );
+ connection_write_cb( s, what, arg );
+
+ if ( !IS_ALIVE( c, c_live ) ) {
+ goto fail;
+ }
+
+ /* Do we still have data pending? If so, connection_write_cb would
+ * already have arranged the write callback to trigger again */
+ checked_lock( &c->c_io_mutex );
+ if ( c->c_pendingber ) {
+ checked_unlock( &c->c_io_mutex );
+ return;
+ }
+ }
+
+ rc = ldap_pvt_tls_accept( c->c_sb, LLOAD_TLS_CTX );
+ checked_unlock( &c->c_io_mutex );
+ if ( rc < 0 ) {
+ goto fail;
+ }
+
+ if ( rc == 0 ) {
+ struct event_base *base = event_get_base( c->c_read_event );
+
+ /*
+ * We're finished, replace the callbacks
+ *
+ * This is deadlock-safe, since both share the same base - the one
+ * that's just running us.
+ */
+ CONNECTION_LOCK(c);
+ event_del( c->c_read_event );
+ event_del( c->c_write_event );
+
+ c->c_read_timeout = NULL;
+ event_assign( c->c_read_event, base, c->c_fd, EV_READ|EV_PERSIST,
+ connection_read_cb, c );
+ if ( IS_ALIVE( c, c_live ) ) {
+ event_add( c->c_read_event, c->c_read_timeout );
+ }
+
+ event_assign( c->c_write_event, base, c->c_fd, EV_WRITE,
+ connection_write_cb, c );
+ Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
+ "connid=%lu finished\n",
+ c->c_connid );
+
+ c->c_is_tls = LLOAD_TLS_ESTABLISHED;
+ CONNECTION_UNLOCK(c);
+ return;
+ } else if ( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_WRITE, NULL ) ) {
+ if ( IS_ALIVE( c, c_live ) ) {
+ CONNECTION_LOCK(c);
+ event_add( c->c_write_event, lload_write_timeout );
+ CONNECTION_UNLOCK(c);
+ }
+ Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
+ "connid=%lu need write rc=%d\n",
+ c->c_connid, rc );
+ }
+ return;
+
+fail:
+ Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
+ "connid=%lu failed rc=%d\n",
+ c->c_connid, rc );
+
+ assert( c->c_ops == NULL );
+ epoch = epoch_join();
+ CONNECTION_LOCK_DESTROY(c);
+ epoch_leave( epoch );
+}
+#endif /* HAVE_TLS */
+
+LloadConnection *
+client_init(
+ ber_socket_t s,
+ const char *peername,
+ struct event_base *base,
+ int flags )
+{
+ LloadConnection *c;
+ struct event *event;
+ event_callback_fn read_cb = connection_read_cb,
+ write_cb = connection_write_cb;
+
+ if ( (c = lload_connection_init( s, peername, flags) ) == NULL ) {
+ return NULL;
+ }
+
+ {
+ ber_len_t max = sockbuf_max_incoming_client;
+ ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_MAX_INCOMING, &max );
+ }
+
+ c->c_state = LLOAD_C_READY;
+
+ if ( flags & CONN_IS_TLS ) {
+#ifdef HAVE_TLS
+ int rc;
+
+ c->c_is_tls = LLOAD_LDAPS;
+
+ rc = ldap_pvt_tls_accept( c->c_sb, LLOAD_TLS_CTX );
+ if ( rc < 0 ) {
+ Debug( LDAP_DEBUG_CONNS, "client_init: "
+ "connid=%lu failed initial TLS accept rc=%d\n",
+ c->c_connid, rc );
+ CONNECTION_LOCK(c);
+ goto fail;
+ }
+
+ if ( rc ) {
+ c->c_read_timeout = lload_timeout_net;
+ read_cb = write_cb = client_tls_handshake_cb;
+ }
+#else /* ! HAVE_TLS */
+ assert(0);
+#endif /* ! HAVE_TLS */
+ }
+
+ event = event_new( base, s, EV_READ|EV_PERSIST, read_cb, c );
+ if ( !event ) {
+ Debug( LDAP_DEBUG_ANY, "client_init: "
+ "Read event could not be allocated\n" );
+ CONNECTION_LOCK(c);
+ goto fail;
+ }
+ c->c_read_event = event;
+
+ event = event_new( base, s, EV_WRITE, write_cb, c );
+ if ( !event ) {
+ Debug( LDAP_DEBUG_ANY, "client_init: "
+ "Write event could not be allocated\n" );
+ CONNECTION_LOCK(c);
+ goto fail;
+ }
+ c->c_write_event = event;
+
+ c->c_destroy = client_destroy;
+ c->c_unlink = client_unlink;
+ c->c_pdu_cb = handle_one_request;
+
+ CONNECTION_LOCK(c);
+ /* We only register the write event when we have data pending */
+ event_add( c->c_read_event, c->c_read_timeout );
+
+ checked_lock( &clients_mutex );
+ LDAP_CIRCLEQ_INSERT_TAIL( &clients, c, c_next );
+ checked_unlock( &clients_mutex );
+ CONNECTION_UNLOCK(c);
+
+ return c;
+fail:
+ if ( c->c_write_event ) {
+ event_free( c->c_write_event );
+ c->c_write_event = NULL;
+ }
+ if ( c->c_read_event ) {
+ event_free( c->c_read_event );
+ c->c_read_event = NULL;
+ }
+
+ c->c_state = LLOAD_C_INVALID;
+ c->c_live--;
+ c->c_refcnt--;
+ connection_destroy( c );
+ return NULL;
+}
+
+void
+client_reset( LloadConnection *c )
+{
+ TAvlnode *root;
+ long freed = 0, executing;
+
+ CONNECTION_ASSERT_LOCKED(c);
+ root = c->c_ops;
+ c->c_ops = NULL;
+ executing = c->c_n_ops_executing;
+ c->c_n_ops_executing = 0;
+
+ if ( !BER_BVISNULL( &c->c_auth ) ) {
+ ch_free( c->c_auth.bv_val );
+ BER_BVZERO( &c->c_auth );
+ }
+ if ( !BER_BVISNULL( &c->c_sasl_bind_mech ) ) {
+ ch_free( c->c_sasl_bind_mech.bv_val );
+ BER_BVZERO( &c->c_sasl_bind_mech );
+ }
+ CONNECTION_UNLOCK(c);
+
+ if ( root ) {
+ freed = ldap_tavl_free( root, (AVL_FREE)operation_abandon );
+ Debug( LDAP_DEBUG_TRACE, "client_reset: "
+ "dropped %ld operations\n",
+ freed );
+ }
+ assert( freed == executing );
+
+ CONNECTION_LOCK(c);
+ CONNECTION_ASSERT_LOCKED(c);
+}
+
+void
+client_unlink( LloadConnection *c )
+{
+ enum sc_state state;
+ struct event *read_event, *write_event;
+
+ Debug( LDAP_DEBUG_CONNS, "client_unlink: "
+ "removing client connid=%lu\n",
+ c->c_connid );
+
+ CONNECTION_ASSERT_LOCKED(c);
+ assert( c->c_state != LLOAD_C_INVALID );
+ assert( c->c_state != LLOAD_C_DYING );
+
+ state = c->c_state;
+ c->c_state = LLOAD_C_DYING;
+
+ read_event = c->c_read_event;
+ write_event = c->c_write_event;
+ CONNECTION_UNLOCK(c);
+
+ if ( read_event ) {
+ event_del( read_event );
+ }
+
+ if ( write_event ) {
+ event_del( write_event );
+ }
+
+ if ( state != LLOAD_C_DYING ) {
+ checked_lock( &clients_mutex );
+ LDAP_CIRCLEQ_REMOVE( &clients, c, c_next );
+ checked_unlock( &clients_mutex );
+ }
+
+ CONNECTION_LOCK(c);
+ client_reset( c );
+ CONNECTION_ASSERT_LOCKED(c);
+}
+
+void
+client_destroy( LloadConnection *c )
+{
+ Debug( LDAP_DEBUG_CONNS, "client_destroy: "
+ "destroying client connid=%lu\n",
+ c->c_connid );
+
+ CONNECTION_LOCK(c);
+ assert( c->c_state == LLOAD_C_DYING );
+ c->c_state = LLOAD_C_INVALID;
+
+ assert( c->c_ops == NULL );
+
+ if ( c->c_read_event ) {
+ event_free( c->c_read_event );
+ c->c_read_event = NULL;
+ }
+
+ if ( c->c_write_event ) {
+ event_free( c->c_write_event );
+ c->c_write_event = NULL;
+ }
+
+ assert( c->c_refcnt == 0 );
+ connection_destroy( c );
+}
+
+void
+clients_destroy( int gentle )
+{
+ checked_lock( &clients_mutex );
+ connections_walk(
+ &clients_mutex, &clients, lload_connection_close, &gentle );
+ checked_unlock( &clients_mutex );
+}
diff --git a/servers/lloadd/config.c b/servers/lloadd/config.c
new file mode 100644
index 0000000..12b4406
--- /dev/null
+++ b/servers/lloadd/config.c
@@ -0,0 +1,3824 @@
+/* config.c - configuration file handling routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/signal.h>
+#include <ac/socket.h>
+#include <ac/errno.h>
+#include <ac/unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef S_ISREG
+#define S_ISREG(m) ( ((m) & _S_IFMT ) == _S_IFREG )
+#endif
+
+#include "lload.h"
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "lload-config.h"
+
+#ifdef _WIN32
+#define LUTIL_ATOULX lutil_atoullx
+#define Z "I"
+#else
+#define LUTIL_ATOULX lutil_atoulx
+#define Z "z"
+#endif
+
+#define ARGS_STEP 512
+
+/*
+ * defaults for various global variables
+ */
+#ifdef BALANCER_MODULE
+char *listeners_list = NULL;
+#else /* !BALANCER_MODULE */
+slap_mask_t global_allows = 0;
+slap_mask_t global_disallows = 0;
+int global_gentlehup = 0;
+int global_idletimeout = 0;
+char *global_host = NULL;
+
+char *slapd_pid_file = NULL;
+char *slapd_args_file = NULL;
+#endif /* !BALANCER_MODULE */
+
+static FILE *logfile;
+static char *logfileName;
+
+static struct timeval timeout_api_tv, timeout_net_tv,
+ timeout_write_tv = { 10, 0 };
+
+lload_features_t lload_features;
+
+ber_len_t sockbuf_max_incoming_client = LLOAD_SB_MAX_INCOMING_CLIENT;
+ber_len_t sockbuf_max_incoming_upstream = LLOAD_SB_MAX_INCOMING_UPSTREAM;
+
+int lload_conn_max_pdus_per_cycle = LLOAD_CONN_MAX_PDUS_PER_CYCLE_DEFAULT;
+
+struct timeval *lload_timeout_api = NULL;
+struct timeval *lload_timeout_net = NULL;
+struct timeval *lload_write_timeout = &timeout_write_tv;
+
+static slap_verbmasks tlskey[];
+
+static int fp_getline( FILE *fp, ConfigArgs *c );
+static void fp_getline_init( ConfigArgs *c );
+
+static char *strtok_quote(
+ char *line,
+ char *sep,
+ char **quote_ptr,
+ int *inquote );
+
+typedef struct ConfigFile {
+ struct ConfigFile *c_sibs;
+ struct ConfigFile *c_kids;
+ struct berval c_file;
+ BerVarray c_dseFiles;
+} ConfigFile;
+
+static ConfigFile *cfn;
+
+static ConfigDriver config_fname;
+static ConfigDriver config_generic;
+static ConfigDriver config_backend;
+static ConfigDriver config_bindconf;
+#ifdef LDAP_TCP_BUFFER
+static ConfigDriver config_tcp_buffer;
+#endif /* LDAP_TCP_BUFFER */
+static ConfigDriver config_restrict;
+static ConfigDriver config_loglevel;
+static ConfigDriver config_include;
+static ConfigDriver config_feature;
+#ifdef HAVE_TLS
+static ConfigDriver config_tls_option;
+static ConfigDriver config_tls_config;
+#endif
+#ifdef BALANCER_MODULE
+static ConfigDriver config_share_tls_ctx;
+static ConfigDriver backend_cf_gen;
+#endif /* BALANCER_MODULE */
+
+lload_b_head backend = LDAP_CIRCLEQ_HEAD_INITIALIZER(backend);
+ldap_pvt_thread_mutex_t backend_mutex;
+LloadBackend *current_backend = NULL;
+
+struct slap_bindconf bindconf = {};
+struct berval lloadd_identity = BER_BVNULL;
+
+enum {
+ CFG_ACL = 1,
+ CFG_BACKEND,
+ CFG_BINDCONF,
+ CFG_LISTEN,
+ CFG_LISTEN_URI,
+ CFG_TLS_RAND,
+ CFG_TLS_CIPHER,
+ CFG_TLS_PROTOCOL_MIN,
+ CFG_TLS_CERT_FILE,
+ CFG_TLS_CERT_KEY,
+ CFG_TLS_CA_PATH,
+ CFG_TLS_CA_FILE,
+ CFG_TLS_DH_FILE,
+ CFG_TLS_VERIFY,
+ CFG_TLS_CRLCHECK,
+ CFG_TLS_CRL_FILE,
+ CFG_TLS_SHARE_CTX,
+ CFG_CONCUR,
+ CFG_THREADS,
+ CFG_LOGFILE,
+ CFG_MIRRORMODE,
+ CFG_IOTHREADS,
+ CFG_MAXBUF_CLIENT,
+ CFG_MAXBUF_UPSTREAM,
+ CFG_FEATURE,
+ CFG_THREADQS,
+ CFG_TLS_ECNAME,
+ CFG_TLS_CACERT,
+ CFG_TLS_CERT,
+ CFG_TLS_KEY,
+ CFG_RESCOUNT,
+ CFG_IOTIMEOUT,
+ CFG_URI,
+ CFG_NUMCONNS,
+ CFG_BINDCONNS,
+ CFG_RETRY,
+ CFG_MAX_PENDING_OPS,
+ CFG_MAX_PENDING_CONNS,
+ CFG_STARTTLS,
+ CFG_CLIENT_PENDING,
+
+ CFG_LAST
+};
+
+/* alphabetical ordering */
+
+static ConfigTable config_back_cf_table[] = {
+ /* This attr is read-only */
+ { "", "", 0, 0, 0,
+ ARG_MAGIC,
+ &config_fname,
+ NULL, NULL, NULL
+ },
+ { "argsfile", "file", 2, 2, 0,
+ ARG_STRING,
+ &slapd_args_file,
+ NULL, NULL, NULL
+ },
+ { "concurrency", "level", 2, 2, 0,
+ ARG_UINT|ARG_MAGIC|CFG_CONCUR,
+ &config_generic,
+ NULL, NULL, NULL
+ },
+ /* conf-file only option */
+ { "backend-server", "backend options", 2, 0, 0,
+ ARG_MAGIC|CFG_BACKEND,
+ &config_backend,
+ NULL, NULL, NULL
+ },
+ { "bindconf", "backend credentials", 2, 0, 0,
+ ARG_MAGIC|CFG_BINDCONF,
+ &config_bindconf,
+ "( OLcfgBkAt:13.2 "
+ "NAME 'olcBkLloadBindconf' "
+ "DESC 'Backend credentials' "
+ /* No EQUALITY since this is a compound attribute (and needs
+ * splitting up anyway - which is a TODO) */
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "gentlehup", "on|off", 2, 2, 0,
+#ifdef SIGHUP
+ ARG_ON_OFF,
+ &global_gentlehup,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ NULL, NULL, NULL
+ },
+ { "idletimeout", "timeout", 2, 2, 0,
+ ARG_UINT,
+ &global_idletimeout,
+ "( OLcfgBkAt:13.3 "
+ "NAME 'olcBkLloadIdleTimeout' "
+ "DESC 'Connection idle timeout' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "include", "file", 2, 2, 0,
+ ARG_MAGIC,
+ &config_include,
+ NULL, NULL, NULL
+ },
+ { "io-threads", "count", 2, 0, 0,
+ ARG_UINT|ARG_MAGIC|CFG_IOTHREADS,
+ &config_generic,
+ "( OLcfgBkAt:13.4 "
+ "NAME 'olcBkLloadIOThreads' "
+ "DESC 'I/O thread count' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+#ifdef BALANCER_MODULE
+ { "listen", "uri list", 2, 2, 0,
+ ARG_STRING|ARG_MAGIC|CFG_LISTEN,
+ &config_generic,
+ NULL, NULL, NULL
+ },
+ { "", "uri", 2, 2, 0,
+ ARG_MAGIC|CFG_LISTEN_URI,
+ &config_generic,
+ "( OLcfgBkAt:13.5 "
+ "NAME 'olcBkLloadListen' "
+ "DESC 'A listener adress' "
+ /* We don't handle adding/removing a value, so no EQUALITY yet */
+ "SYNTAX OMsDirectoryString )",
+ NULL, NULL
+ },
+#endif /* BALANCER_MODULE */
+ { "logfile", "file", 2, 2, 0,
+ ARG_STRING|ARG_MAGIC|CFG_LOGFILE,
+ &config_generic,
+ NULL, NULL, NULL
+ },
+ { "loglevel", "level", 2, 0, 0,
+ ARG_MAGIC,
+ &config_loglevel,
+ NULL, NULL, NULL
+ },
+ { "pidfile", "file", 2, 2, 0,
+ ARG_STRING,
+ &slapd_pid_file,
+ NULL, NULL, NULL
+ },
+ { "restrict", "op_list", 2, 0, 0,
+ ARG_MAGIC,
+ &config_restrict,
+ NULL, NULL, NULL
+ },
+ { "sockbuf_max_incoming_client", "max", 2, 2, 0,
+ ARG_BER_LEN_T|ARG_MAGIC|CFG_MAXBUF_CLIENT,
+ &config_generic,
+ "( OLcfgBkAt:13.6 "
+ "NAME 'olcBkLloadSockbufMaxClient' "
+ "DESC 'The maximum LDAP PDU size accepted coming from clients' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL,
+ { .v_ber_t = LLOAD_SB_MAX_INCOMING_CLIENT }
+ },
+ { "sockbuf_max_incoming_upstream", "max", 2, 2, 0,
+ ARG_BER_LEN_T|ARG_MAGIC|CFG_MAXBUF_UPSTREAM,
+ &config_generic,
+ "( OLcfgBkAt:13.7 "
+ "NAME 'olcBkLloadSockbufMaxUpstream' "
+ "DESC 'The maximum LDAP PDU size accepted coming from upstream' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL,
+ { .v_ber_t = LLOAD_SB_MAX_INCOMING_UPSTREAM }
+ },
+ { "tcp-buffer", "[listener=<listener>] [{read|write}=]size", 0, 0, 0,
+#ifdef LDAP_TCP_BUFFER
+ ARG_MAGIC,
+ &config_tcp_buffer,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ "( OLcfgBkAt:13.8 "
+ "NAME 'olcBkLloadTcpBuffer' "
+ "DESC 'TCP Buffer size' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "threads", "count", 2, 2, 0,
+ ARG_UINT|ARG_MAGIC|CFG_THREADS,
+ &config_generic,
+ NULL, NULL, NULL
+ },
+ { "threadqueues", "count", 2, 2, 0,
+ ARG_UINT|ARG_MAGIC|CFG_THREADQS,
+ &config_generic,
+ NULL, NULL, NULL
+ },
+ { "max_pdus_per_cycle", "count", 2, 2, 0,
+ ARG_UINT|ARG_MAGIC|CFG_RESCOUNT,
+ &config_generic,
+ "( OLcfgBkAt:13.9 "
+ "NAME 'olcBkLloadMaxPDUPerCycle' "
+ "DESC 'Maximum number of PDUs to handle in a single cycle' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "feature", "name", 2, 0, 0,
+ ARG_MAGIC|CFG_FEATURE,
+ &config_feature,
+ "( OLcfgBkAt:13.10 "
+ "NAME 'olcBkLloadFeature' "
+ "DESC 'Lload features enabled' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )",
+ NULL, NULL
+ },
+ { "TLSCACertificate", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_CACERT|ARG_BINARY|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ "( OLcfgBkAt:13.11 "
+ "NAME 'olcBkLloadTLSCACertificate' "
+ "DESC 'X.509 certificate, must use ;binary' "
+ "EQUALITY certificateExactMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.8 "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "TLSCACertificateFile", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_CA_FILE|ARG_STRING|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ "( OLcfgBkAt:13.12 "
+ "NAME 'olcBkLloadTLSCACertificateFile' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "TLSCACertificatePath", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_CA_PATH|ARG_STRING|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ "( OLcfgBkAt:13.13 "
+ "NAME 'olcBkLloadTLSCACertificatePath' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "TLSCertificate", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_CERT|ARG_BINARY|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ "( OLcfgBkAt:13.14 "
+ "NAME 'olcBkLloadTLSCertificate' "
+ "DESC 'X.509 certificate, must use ;binary' "
+ "EQUALITY certificateExactMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.8 "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "TLSCertificateFile", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_CERT_FILE|ARG_STRING|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ "( OLcfgBkAt:13.15 "
+ "NAME 'olcBkLloadTLSCertificateFile' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "TLSCertificateKey", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_KEY|ARG_BINARY|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ "( OLcfgBkAt:13.16 "
+ "NAME 'olcBkLloadTLSCertificateKey' "
+ "DESC 'X.509 privateKey, must use ;binary' "
+ "EQUALITY privateKeyMatch "
+ "SYNTAX 1.2.840.113549.1.8.1.1 "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "TLSCertificateKeyFile", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_CERT_KEY|ARG_STRING|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ "( OLcfgBkAt:13.17 "
+ "NAME 'olcBkLloadTLSCertificateKeyFile' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "TLSCipherSuite", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_CIPHER|ARG_STRING|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ "( OLcfgBkAt:13.18 "
+ "NAME 'olcBkLloadTLSCipherSuite' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "TLSCRLCheck", NULL, 2, 2, 0,
+#if defined(HAVE_TLS) && defined(HAVE_OPENSSL)
+ CFG_TLS_CRLCHECK|ARG_STRING|ARG_MAGIC,
+ &config_tls_config,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ "( OLcfgBkAt:13.19 "
+ "NAME 'olcBkLloadTLSCRLCheck' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "TLSCRLFile", NULL, 2, 2, 0,
+#if defined(HAVE_GNUTLS)
+ CFG_TLS_CRL_FILE|ARG_STRING|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ "( OLcfgBkAt:13.20 "
+ "NAME 'olcBkLloadTLSCRLFile' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "TLSRandFile", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_RAND|ARG_STRING|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ "( OLcfgBkAt:13.21 "
+ "NAME 'olcBkLloadTLSRandFile' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "TLSVerifyClient", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_VERIFY|ARG_STRING|ARG_MAGIC,
+ &config_tls_config,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ "( OLcfgBkAt:13.22 "
+ "NAME 'olcBkLloadVerifyClient' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "TLSDHParamFile", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_DH_FILE|ARG_STRING|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ "( OLcfgBkAt:13.23 "
+ "NAME 'olcBkLloadTLSDHParamFile' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "TLSECName", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_ECNAME|ARG_STRING|ARG_MAGIC,
+ &config_tls_option,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ "( OLcfgBkAt:13.24 "
+ "NAME 'olcBkLloadTLSECName' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "TLSProtocolMin", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_PROTOCOL_MIN|ARG_STRING|ARG_MAGIC,
+ &config_tls_config,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ "( OLcfgBkAt:13.25 "
+ "NAME 'olcBkLloadTLSProtocolMin' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "TLSShareSlapdCTX", NULL, 2, 2, 0,
+#if defined(HAVE_TLS) && defined(BALANCER_MODULE)
+ CFG_TLS_SHARE_CTX|ARG_ON_OFF|ARG_MAGIC,
+ &config_share_tls_ctx,
+#else
+ ARG_IGNORED,
+ NULL,
+#endif
+ "( OLcfgBkAt:13.33 "
+ "NAME 'olcBkLloadTLSShareSlapdCTX' "
+ "DESC 'Share slapd TLS context (all other lloadd TLS options cease to take effect)' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "iotimeout", "ms timeout", 2, 2, 0,
+ ARG_UINT|ARG_MAGIC|CFG_IOTIMEOUT,
+ &config_generic,
+ "( OLcfgBkAt:13.26 "
+ "NAME 'olcBkLloadIOTimeout' "
+ "DESC 'I/O timeout threshold in miliseconds' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "client_max_pending", NULL, 2, 2, 0,
+ ARG_MAGIC|ARG_UINT|CFG_CLIENT_PENDING,
+ &config_generic,
+ "( OLcfgBkAt:13.35 "
+ "NAME 'olcBkLloadClientMaxPending' "
+ "DESC 'Maximum pending operations per client connection' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL,
+ { .v_uint = 0 }
+ },
+
+ /* cn=config only options */
+#ifdef BALANCER_MODULE
+ { "", "uri", 2, 2, 0,
+ ARG_BERVAL|ARG_MAGIC|CFG_URI,
+ &backend_cf_gen,
+ "( OLcfgBkAt:13.27 "
+ "NAME 'olcBkLloadBackendUri' "
+ "DESC 'URI to contact the server on' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "", NULL, 2, 2, 0,
+ ARG_UINT|ARG_MAGIC|CFG_NUMCONNS,
+ &backend_cf_gen,
+ "( OLcfgBkAt:13.28 "
+ "NAME 'olcBkLloadNumconns' "
+ "DESC 'Number of regular connections to maintain' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "", NULL, 2, 2, 0,
+ ARG_UINT|ARG_MAGIC|CFG_BINDCONNS,
+ &backend_cf_gen,
+ "( OLcfgBkAt:13.29 "
+ "NAME 'olcBkLloadBindconns' "
+ "DESC 'Number of bind connections to maintain' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "", NULL, 2, 2, 0,
+ ARG_UINT|ARG_MAGIC|CFG_RETRY,
+ &backend_cf_gen,
+ "( OLcfgBkAt:13.30 "
+ "NAME 'olcBkLloadRetry' "
+ "DESC 'Number of seconds to wait before trying to reconnect' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "", NULL, 2, 2, 0,
+ ARG_UINT|ARG_MAGIC|CFG_MAX_PENDING_OPS,
+ &backend_cf_gen,
+ "( OLcfgBkAt:13.31 "
+ "NAME 'olcBkLloadMaxPendingOps' "
+ "DESC 'Maximum number of pending operations for this backend' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "", NULL, 2, 2, 0,
+ ARG_UINT|ARG_MAGIC|CFG_MAX_PENDING_CONNS,
+ &backend_cf_gen,
+ "( OLcfgBkAt:13.32 "
+ "NAME 'olcBkLloadMaxPendingConns' "
+ "DESC 'Maximum number of pending operations on each connection' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "", NULL, 2, 2, 0,
+ ARG_BERVAL|ARG_MAGIC|CFG_STARTTLS,
+ &backend_cf_gen,
+ "( OLcfgBkAt:13.34 "
+ "NAME 'olcBkLloadStartTLS' "
+ "DESC 'Whether StartTLS should be attempted on the connection' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+#endif /* BALANCER_MODULE */
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED, NULL }
+};
+
+#ifdef BALANCER_MODULE
+static ConfigCfAdd lload_cfadd;
+static ConfigLDAPadd lload_backend_ldadd;
+#ifdef SLAP_CONFIG_DELETE
+static ConfigLDAPdel lload_backend_lddel;
+#endif /* SLAP_CONFIG_DELETE */
+
+static ConfigOCs lloadocs[] = {
+ { "( OLcfgBkOc:13.1 "
+ "NAME 'olcBkLloadConfig' "
+ "DESC 'Lload backend configuration' "
+ "SUP olcBackendConfig "
+ "MUST ( olcBkLloadBindconf "
+ "$ olcBkLloadIOThreads "
+ "$ olcBkLloadListen "
+ "$ olcBkLloadSockbufMaxClient "
+ "$ olcBkLloadSockbufMaxUpstream "
+ "$ olcBkLloadMaxPDUPerCycle "
+ "$ olcBkLloadIOTimeout ) "
+ "MAY ( olcBkLloadFeature "
+ "$ olcBkLloadTcpBuffer "
+ "$ olcBkLloadTLSCACertificateFile "
+ "$ olcBkLloadTLSCACertificatePath "
+ "$ olcBkLloadTLSCertificateFile "
+ "$ olcBkLloadTLSCertificateKeyFile "
+ "$ olcBkLloadTLSCipherSuite "
+ "$ olcBkLloadTLSCRLCheck "
+ "$ olcBkLloadTLSRandFile "
+ "$ olcBkLloadVerifyClient "
+ "$ olcBkLloadTLSDHParamFile "
+ "$ olcBkLloadTLSECName "
+ "$ olcBkLloadTLSProtocolMin "
+ "$ olcBkLloadTLSCRLFile "
+ "$ olcBkLloadTLSShareSlapdCTX "
+ "$ olcBkLloadClientMaxPending "
+ ") )",
+ Cft_Backend, config_back_cf_table,
+ NULL,
+ lload_cfadd,
+ },
+ { "( OLcfgBkOc:13.2 "
+ "NAME 'olcBkLloadBackendConfig' "
+ "DESC 'Lload backend server configuration' "
+ "SUP olcConfig STRUCTURAL "
+ "MUST ( cn "
+ "$ olcBkLloadBackendUri "
+ "$ olcBkLloadNumconns "
+ "$ olcBkLloadBindconns "
+ "$ olcBkLloadRetry "
+ "$ olcBkLloadMaxPendingOps "
+ "$ olcBkLloadMaxPendingConns ) "
+ "MAY ( olcBkLloadStartTLS "
+ ") )",
+ Cft_Misc, config_back_cf_table,
+ lload_backend_ldadd,
+ NULL,
+#ifdef SLAP_CONFIG_DELETE
+ lload_backend_lddel,
+#endif /* SLAP_CONFIG_DELETE */
+ },
+ { NULL, 0, NULL }
+};
+#endif /* BALANCER_MODULE */
+
+static int
+config_generic( ConfigArgs *c )
+{
+ enum lcf_daemon flag = 0;
+ int rc = LDAP_SUCCESS;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ switch ( c->type ) {
+ case CFG_IOTHREADS:
+ c->value_uint = lload_daemon_threads;
+ break;
+ case CFG_LISTEN_URI: {
+ LloadListener **ll = lloadd_get_listeners();
+ struct berval bv = BER_BVNULL;
+
+ for ( ; ll && *ll; ll++ ) {
+ /* The same url could have spawned several consecutive
+ * listeners */
+ if ( !BER_BVISNULL( &bv ) &&
+ !ber_bvcmp( &bv, &(*ll)->sl_url ) ) {
+ continue;
+ }
+ ber_dupbv( &bv, &(*ll)->sl_url );
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ }
+ } break;
+ case CFG_MAXBUF_CLIENT:
+ c->value_uint = sockbuf_max_incoming_client;
+ break;
+ case CFG_MAXBUF_UPSTREAM:
+ c->value_uint = sockbuf_max_incoming_upstream;
+ break;
+ case CFG_RESCOUNT:
+ c->value_uint = lload_conn_max_pdus_per_cycle;
+ break;
+ case CFG_IOTIMEOUT:
+ c->value_uint = 1000 * lload_write_timeout->tv_sec +
+ lload_write_timeout->tv_usec / 1000;
+ break;
+ case CFG_CLIENT_PENDING:
+ c->value_uint = lload_client_max_pending;
+ break;
+ default:
+ rc = 1;
+ break;
+ }
+ return rc;
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ /* We only need to worry about deletions to multi-value or MAY
+ * attributes that belong to the lloadd module - we don't have any at
+ * the moment */
+ return rc;
+ }
+
+ lload_change.type = LLOAD_CHANGE_MODIFY;
+ lload_change.object = LLOAD_DAEMON;
+
+ switch ( c->type ) {
+ case CFG_CONCUR:
+ ldap_pvt_thread_set_concurrency( c->value_uint );
+ break;
+ case CFG_LISTEN:
+ if ( lloadd_inited ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "listen directive can only be specified once" );
+ ch_free( c->value_string );
+ return 1;
+ }
+ if ( lloadd_listeners_init( c->value_string ) ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "could not open one of the listener sockets: %s",
+ c->value_string );
+ ch_free( c->value_string );
+ return 1;
+ }
+ ch_free( c->value_string );
+ break;
+ case CFG_LISTEN_URI: {
+ LDAPURLDesc *lud;
+ LloadListener *l;
+
+ if ( ldap_url_parse_ext(
+ c->line, &lud, LDAP_PVT_URL_PARSE_DEF_PORT ) ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "string %s could not be parsed as an LDAP URL",
+ c->line );
+ goto fail;
+ }
+
+ /* A sanity check, although it will not catch everything */
+ if ( ( l = lload_config_check_my_url( c->line, lud ) ) ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "Load Balancer already configured to listen on %s "
+ "(while adding %s)",
+ l->sl_url.bv_val, c->line );
+ goto fail;
+ }
+
+ if ( !lloadd_inited ) {
+ if ( lload_open_new_listener( c->line, lud ) ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "could not open a listener for %s", c->line );
+ goto fail;
+ }
+ } else {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "listener changes will not take effect until restart: "
+ "%s",
+ c->line );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
+ }
+ } break;
+ case CFG_THREADS:
+ if ( c->value_uint < 2 ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "threads=%d smaller than minimum value 2",
+ c->value_uint );
+ goto fail;
+
+ } else if ( c->value_uint > 2 * SLAP_MAX_WORKER_THREADS ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "warning, threads=%d larger than twice the default "
+ "(2*%d=%d); YMMV",
+ c->value_uint, SLAP_MAX_WORKER_THREADS,
+ 2 * SLAP_MAX_WORKER_THREADS );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
+ }
+ if ( slapMode & SLAP_SERVER_MODE )
+ ldap_pvt_thread_pool_maxthreads(
+ &connection_pool, c->value_uint );
+ connection_pool_max = c->value_uint; /* save for reference */
+ break;
+
+ case CFG_THREADQS:
+ if ( c->value_uint < 1 ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "threadqueues=%d smaller than minimum value 1",
+ c->value_uint );
+ goto fail;
+ }
+ if ( slapMode & SLAP_SERVER_MODE )
+ ldap_pvt_thread_pool_queues( &connection_pool, c->value_uint );
+ connection_pool_queues = c->value_uint; /* save for reference */
+ break;
+
+ case CFG_IOTHREADS: {
+ int mask = 0;
+ /* use a power of two */
+ while ( c->value_uint > 1 ) {
+ c->value_uint >>= 1;
+ mask <<= 1;
+ mask |= 1;
+ }
+ if ( !lloadd_inited ) {
+ lload_daemon_mask = mask;
+ lload_daemon_threads = mask + 1;
+ flag = LLOAD_DAEMON_MOD_THREADS;
+ } else {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "io thread changes will not take effect until "
+ "restart" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
+ }
+ } break;
+
+ case CFG_LOGFILE: {
+ if ( logfileName ) ch_free( logfileName );
+ logfileName = c->value_string;
+ logfile = fopen( logfileName, "w" );
+ if ( logfile ) lutil_debug_file( logfile );
+ } break;
+
+ case CFG_RESCOUNT:
+ lload_conn_max_pdus_per_cycle = c->value_uint;
+ break;
+
+ case CFG_IOTIMEOUT:
+ if ( c->value_uint > 0 ) {
+ timeout_write_tv.tv_sec = c->value_uint / 1000;
+ timeout_write_tv.tv_usec = 1000 * ( c->value_uint % 1000 );
+ lload_write_timeout = &timeout_write_tv;
+ } else {
+ lload_write_timeout = NULL;
+ }
+ break;
+ case CFG_MAXBUF_CLIENT:
+ sockbuf_max_incoming_client = c->value_uint;
+ break;
+ case CFG_MAXBUF_UPSTREAM:
+ sockbuf_max_incoming_upstream = c->value_uint;
+ break;
+ case CFG_CLIENT_PENDING:
+ lload_client_max_pending = c->value_uint;
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY, "%s: unknown CFG_TYPE %d\n",
+ c->log, c->type );
+ return 1;
+ }
+
+ lload_change.flags.daemon |= flag;
+
+ return 0;
+
+fail:
+ if ( lload_change.type == LLOAD_CHANGE_ADD ) {
+ /* Abort the ADD */
+ lload_change.type = LLOAD_CHANGE_DEL;
+ }
+
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
+ return 1;
+}
+
+static int
+lload_backend_finish( ConfigArgs *ca )
+{
+ LloadBackend *b = ca->ca_private;
+
+ if ( ca->reply.err != LDAP_SUCCESS ) {
+ /* Not reached since cleanup is only called on success */
+ goto fail;
+ }
+
+ if ( b->b_numconns <= 0 || b->b_numbindconns <= 0 ) {
+ Debug( LDAP_DEBUG_ANY, "lload_backend_finish: "
+ "invalid connection pool configuration\n" );
+ goto fail;
+ }
+
+ if ( b->b_retry_timeout < 0 ) {
+ Debug( LDAP_DEBUG_ANY, "lload_backend_finish: "
+ "invalid retry timeout configuration\n" );
+ goto fail;
+ }
+
+ b->b_retry_tv.tv_sec = b->b_retry_timeout / 1000;
+ b->b_retry_tv.tv_usec = ( b->b_retry_timeout % 1000 ) * 1000;
+
+ /* daemon_base is only allocated after initial configuration happens, those
+ * events are allocated on startup, we only deal with online Adds */
+ if ( !b->b_retry_event && daemon_base ) {
+ struct event *event;
+ assert( CONFIG_ONLINE_ADD( ca ) );
+ event = evtimer_new( daemon_base, backend_connect, b );
+ if ( !event ) {
+ Debug( LDAP_DEBUG_ANY, "lload_backend_finish: "
+ "failed to allocate retry event\n" );
+ goto fail;
+ }
+ b->b_retry_event = event;
+ }
+
+ return LDAP_SUCCESS;
+
+fail:
+ if ( lload_change.type == LLOAD_CHANGE_ADD ) {
+ /* Abort the ADD */
+ lload_change.type = LLOAD_CHANGE_DEL;
+ }
+
+ lload_backend_destroy( b );
+ return -1;
+}
+
+static LloadBackend *
+backend_alloc( void )
+{
+ LloadBackend *b;
+
+ b = ch_calloc( 1, sizeof(LloadBackend) );
+
+ LDAP_CIRCLEQ_INIT( &b->b_conns );
+ LDAP_CIRCLEQ_INIT( &b->b_bindconns );
+ LDAP_CIRCLEQ_INIT( &b->b_preparing );
+
+ b->b_numconns = 1;
+ b->b_numbindconns = 1;
+
+ b->b_retry_timeout = 5000;
+
+ ldap_pvt_thread_mutex_init( &b->b_mutex );
+
+ LDAP_CIRCLEQ_INSERT_TAIL( &backend, b, b_next );
+ return b;
+}
+
+static int
+backend_config_url( LloadBackend *b, struct berval *uri )
+{
+ LDAPURLDesc *lud = NULL;
+ char *host = NULL;
+ int rc, proto, tls = b->b_tls_conf;
+
+ /* Effect no changes until we've checked everything */
+
+ rc = ldap_url_parse_ext( uri->bv_val, &lud, LDAP_PVT_URL_PARSE_DEF_PORT );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "backend_config_url: "
+ "listen URL \"%s\" parse error=%d\n",
+ uri->bv_val, rc );
+ return -1;
+ }
+
+ if ( ldap_pvt_url_scheme2tls( lud->lud_scheme ) ) {
+#ifdef HAVE_TLS
+ /* Specifying ldaps:// overrides starttls= settings */
+ tls = LLOAD_LDAPS;
+#else /* ! HAVE_TLS */
+
+ Debug( LDAP_DEBUG_ANY, "backend_config_url: "
+ "TLS not supported (%s)\n",
+ uri->bv_val );
+ rc = -1;
+ goto done;
+#endif /* ! HAVE_TLS */
+ }
+
+ proto = ldap_pvt_url_scheme2proto( lud->lud_scheme );
+ if ( proto == LDAP_PROTO_IPC ) {
+#ifdef LDAP_PF_LOCAL
+ if ( lud->lud_host == NULL || lud->lud_host[0] == '\0' ) {
+ host = LDAPI_SOCK;
+ }
+#else /* ! LDAP_PF_LOCAL */
+
+ Debug( LDAP_DEBUG_ANY, "backend_config_url: "
+ "URL scheme not supported: %s",
+ url );
+ rc = -1;
+ goto done;
+#endif /* ! LDAP_PF_LOCAL */
+ } else {
+ if ( lud->lud_host == NULL || lud->lud_host[0] == '\0' ) {
+ Debug( LDAP_DEBUG_ANY, "backend_config_url: "
+ "backend url missing hostname: '%s'\n",
+ uri->bv_val );
+ rc = -1;
+ goto done;
+ }
+ }
+ if ( !host ) {
+ host = lud->lud_host;
+ }
+
+ if ( b->b_host ) {
+ ch_free( b->b_host );
+ }
+
+ b->b_proto = proto;
+ b->b_tls = tls;
+ b->b_port = lud->lud_port;
+ b->b_host = ch_strdup( host );
+
+done:
+ ldap_free_urldesc( lud );
+ return rc;
+}
+
+static int
+config_backend( ConfigArgs *c )
+{
+ LloadBackend *b;
+ int i, rc = 0;
+
+ b = backend_alloc();
+
+ for ( i = 1; i < c->argc; i++ ) {
+ if ( lload_backend_parse( c->argv[i], b ) ) {
+ Debug( LDAP_DEBUG_ANY, "config_backend: "
+ "error parsing backend configuration item '%s'\n",
+ c->argv[i] );
+ return -1;
+ }
+ }
+
+ if ( BER_BVISNULL( &b->b_uri ) ) {
+ Debug( LDAP_DEBUG_ANY, "config_backend: "
+ "backend address not specified\n" );
+ rc = -1;
+ goto done;
+ }
+
+ if ( backend_config_url( b, &b->b_uri ) ) {
+ rc = -1;
+ goto done;
+ }
+
+ c->ca_private = b;
+ rc = lload_backend_finish( c );
+done:
+ if ( rc ) {
+ ch_free( b );
+ }
+ return rc;
+}
+
+static int
+config_bindconf( ConfigArgs *c )
+{
+ int i;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ struct berval bv;
+
+ lload_bindconf_unparse( &bindconf, &bv );
+
+ for ( i = 0; isspace( (unsigned char)bv.bv_val[i] ); i++ )
+ /* count spaces */;
+
+ if ( i ) {
+ bv.bv_len -= i;
+ AC_MEMCPY( bv.bv_val, &bv.bv_val[i], bv.bv_len + 1 );
+ }
+
+ value_add_one( &c->rvalue_vals, &bv );
+ ber_memfree( bv.bv_val );
+ return LDAP_SUCCESS;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ /* It's a MUST single-valued attribute, noop for now */
+ lload_bindconf_free( &bindconf );
+ return LDAP_SUCCESS;
+ }
+
+ lload_change.type = LLOAD_CHANGE_MODIFY;
+ lload_change.object = LLOAD_DAEMON;
+ lload_change.flags.daemon |= LLOAD_DAEMON_MOD_BINDCONF;
+
+ for ( i = 1; i < c->argc; i++ ) {
+ if ( lload_bindconf_parse( c->argv[i], &bindconf ) ) {
+ Debug( LDAP_DEBUG_ANY, "config_bindconf: "
+ "error parsing backend configuration item '%s'\n",
+ c->argv[i] );
+ return -1;
+ }
+ }
+
+ if ( bindconf.sb_method == LDAP_AUTH_SASL ) {
+#ifndef HAVE_CYRUS_SASL
+ Debug( LDAP_DEBUG_ANY, "config_bindconf: "
+ "no sasl support available\n" );
+ return -1;
+#endif
+ }
+
+ if ( !BER_BVISNULL( &bindconf.sb_authzId ) ) {
+ ber_dupbv( &lloadd_identity, &bindconf.sb_authzId );
+ } else if ( !BER_BVISNULL( &bindconf.sb_authcId ) ) {
+ ber_dupbv( &lloadd_identity, &bindconf.sb_authcId );
+ } else if ( !BER_BVISNULL( &bindconf.sb_binddn ) ) {
+ char *ptr;
+
+ lloadd_identity.bv_len = STRLENOF("dn:") + bindconf.sb_binddn.bv_len;
+ lloadd_identity.bv_val = ch_malloc( lloadd_identity.bv_len + 1 );
+
+ ptr = lutil_strcopy( lloadd_identity.bv_val, "dn:" );
+ ptr = lutil_strncopy(
+ ptr, bindconf.sb_binddn.bv_val, bindconf.sb_binddn.bv_len );
+ *ptr = '\0';
+ }
+
+ if ( bindconf.sb_timeout_api ) {
+ timeout_api_tv.tv_sec = bindconf.sb_timeout_api;
+ lload_timeout_api = &timeout_api_tv;
+ if ( lload_timeout_event ) {
+ event_add( lload_timeout_event, lload_timeout_api );
+ }
+ } else {
+ lload_timeout_api = NULL;
+ if ( lload_timeout_event ) {
+ event_del( lload_timeout_event );
+ }
+ }
+
+ if ( bindconf.sb_timeout_net ) {
+ timeout_net_tv.tv_sec = bindconf.sb_timeout_net;
+ lload_timeout_net = &timeout_net_tv;
+ } else {
+ lload_timeout_net = NULL;
+ }
+
+#ifdef HAVE_TLS
+ if ( bindconf.sb_tls_do_init ) {
+ lload_bindconf_tls_set( &bindconf, lload_tls_backend_ld );
+ }
+#endif /* HAVE_TLS */
+ return 0;
+}
+
+static int
+config_fname( ConfigArgs *c )
+{
+ return 0;
+}
+
+/*
+ * [listener=<listener>] [{read|write}=]<size>
+ */
+
+#ifdef LDAP_TCP_BUFFER
+static BerVarray tcp_buffer;
+int tcp_buffer_num;
+
+#define SLAP_TCP_RMEM ( 0x1U )
+#define SLAP_TCP_WMEM ( 0x2U )
+
+static int
+tcp_buffer_parse(
+ struct berval *val,
+ int argc,
+ char **argv,
+ int *size,
+ int *rw,
+ LloadListener **l )
+{
+ int i, rc = LDAP_SUCCESS;
+ LDAPURLDesc *lud = NULL;
+ char *ptr;
+
+ if ( val != NULL && argv == NULL ) {
+ char *s = val->bv_val;
+
+ argv = ldap_str2charray( s, " \t" );
+ if ( argv == NULL ) {
+ return LDAP_OTHER;
+ }
+ }
+
+ i = 0;
+ if ( strncasecmp( argv[i], "listener=", STRLENOF("listener=") ) == 0 ) {
+ char *url = argv[i] + STRLENOF("listener=");
+
+ if ( ldap_url_parse_ext( url, &lud, LDAP_PVT_URL_PARSE_DEF_PORT ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ *l = lload_config_check_my_url( url, lud );
+ if ( *l == NULL ) {
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ goto done;
+ }
+
+ i++;
+ }
+
+ ptr = argv[i];
+ if ( strncasecmp( ptr, "read=", STRLENOF("read=") ) == 0 ) {
+ *rw |= SLAP_TCP_RMEM;
+ ptr += STRLENOF("read=");
+
+ } else if ( strncasecmp( ptr, "write=", STRLENOF("write=") ) == 0 ) {
+ *rw |= SLAP_TCP_WMEM;
+ ptr += STRLENOF("write=");
+
+ } else {
+ *rw |= ( SLAP_TCP_RMEM | SLAP_TCP_WMEM );
+ }
+
+ /* accept any base */
+ if ( lutil_atoix( size, ptr, 0 ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+done:;
+ if ( val != NULL && argv != NULL ) {
+ ldap_charray_free( argv );
+ }
+
+ if ( lud != NULL ) {
+ ldap_free_urldesc( lud );
+ }
+
+ return rc;
+}
+
+#ifdef BALANCER_MODULE
+static int
+tcp_buffer_delete_one( struct berval *val )
+{
+ int rc = 0;
+ int size = -1, rw = 0;
+ LloadListener *l = NULL;
+
+ rc = tcp_buffer_parse( val, 0, NULL, &size, &rw, &l );
+ if ( rc != 0 ) {
+ return rc;
+ }
+
+ if ( l != NULL ) {
+ int i;
+ LloadListener **ll = lloadd_get_listeners();
+
+ for ( i = 0; ll[i] != NULL; i++ ) {
+ if ( ll[i] == l ) break;
+ }
+
+ if ( ll[i] == NULL ) {
+ return LDAP_NO_SUCH_ATTRIBUTE;
+ }
+
+ if ( rw & SLAP_TCP_RMEM ) l->sl_tcp_rmem = -1;
+ if ( rw & SLAP_TCP_WMEM ) l->sl_tcp_wmem = -1;
+
+ for ( i++; ll[i] != NULL && bvmatch( &l->sl_url, &ll[i]->sl_url );
+ i++ ) {
+ if ( rw & SLAP_TCP_RMEM ) ll[i]->sl_tcp_rmem = -1;
+ if ( rw & SLAP_TCP_WMEM ) ll[i]->sl_tcp_wmem = -1;
+ }
+
+ } else {
+ /* NOTE: this affects listeners without a specific setting,
+ * does not reset all listeners. If a listener without
+ * specific settings was assigned a buffer because of
+ * a global setting, it will not be reset. In any case,
+ * buffer changes will only take place at restart. */
+ if ( rw & SLAP_TCP_RMEM ) slapd_tcp_rmem = -1;
+ if ( rw & SLAP_TCP_WMEM ) slapd_tcp_wmem = -1;
+ }
+
+ return rc;
+}
+
+static int
+tcp_buffer_delete( BerVarray vals )
+{
+ int i;
+
+ for ( i = 0; !BER_BVISNULL( &vals[i] ); i++ ) {
+ tcp_buffer_delete_one( &vals[i] );
+ }
+
+ return 0;
+}
+#endif /* BALANCER_MODULE */
+
+static int
+tcp_buffer_unparse( int size, int rw, LloadListener *l, struct berval *val )
+{
+ char buf[sizeof("2147483648")], *ptr;
+
+ /* unparse for later use */
+ val->bv_len = snprintf( buf, sizeof(buf), "%d", size );
+ if ( l != NULL ) {
+ val->bv_len += STRLENOF( "listener="
+ " " ) +
+ l->sl_url.bv_len;
+ }
+
+ if ( rw != ( SLAP_TCP_RMEM | SLAP_TCP_WMEM ) ) {
+ if ( rw & SLAP_TCP_RMEM ) {
+ val->bv_len += STRLENOF("read=");
+ } else if ( rw & SLAP_TCP_WMEM ) {
+ val->bv_len += STRLENOF("write=");
+ }
+ }
+
+ val->bv_val = SLAP_MALLOC( val->bv_len + 1 );
+
+ ptr = val->bv_val;
+
+ if ( l != NULL ) {
+ ptr = lutil_strcopy( ptr, "listener=" );
+ ptr = lutil_strncopy( ptr, l->sl_url.bv_val, l->sl_url.bv_len );
+ *ptr++ = ' ';
+ }
+
+ if ( rw != ( SLAP_TCP_RMEM | SLAP_TCP_WMEM ) ) {
+ if ( rw & SLAP_TCP_RMEM ) {
+ ptr = lutil_strcopy( ptr, "read=" );
+ } else if ( rw & SLAP_TCP_WMEM ) {
+ ptr = lutil_strcopy( ptr, "write=" );
+ }
+ }
+
+ ptr = lutil_strcopy( ptr, buf );
+ *ptr = '\0';
+
+ assert( val->bv_val + val->bv_len == ptr );
+
+ return LDAP_SUCCESS;
+}
+
+static int
+tcp_buffer_add_one( int argc, char **argv )
+{
+ int rc = 0;
+ int size = -1, rw = 0;
+ LloadListener *l = NULL;
+
+ struct berval val;
+
+ /* parse */
+ rc = tcp_buffer_parse( NULL, argc, argv, &size, &rw, &l );
+ if ( rc != 0 ) {
+ return rc;
+ }
+
+ /* unparse for later use */
+ rc = tcp_buffer_unparse( size, rw, l, &val );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ /* use parsed values */
+ if ( l != NULL ) {
+ int i;
+ LloadListener **ll = lloadd_get_listeners();
+
+ for ( i = 0; ll[i] != NULL; i++ ) {
+ if ( ll[i] == l ) break;
+ }
+
+ if ( ll[i] == NULL ) {
+ return LDAP_NO_SUCH_ATTRIBUTE;
+ }
+
+ /* buffer only applies to TCP listeners;
+ * we do not do any check here, and delegate them
+ * to setsockopt(2) */
+ if ( rw & SLAP_TCP_RMEM ) l->sl_tcp_rmem = size;
+ if ( rw & SLAP_TCP_WMEM ) l->sl_tcp_wmem = size;
+
+ for ( i++; ll[i] != NULL && bvmatch( &l->sl_url, &ll[i]->sl_url );
+ i++ ) {
+ if ( rw & SLAP_TCP_RMEM ) ll[i]->sl_tcp_rmem = size;
+ if ( rw & SLAP_TCP_WMEM ) ll[i]->sl_tcp_wmem = size;
+ }
+
+ } else {
+ /* NOTE: this affects listeners without a specific setting,
+ * does not set all listeners */
+ if ( rw & SLAP_TCP_RMEM ) slapd_tcp_rmem = size;
+ if ( rw & SLAP_TCP_WMEM ) slapd_tcp_wmem = size;
+ }
+
+ tcp_buffer = SLAP_REALLOC(
+ tcp_buffer, sizeof(struct berval) * ( tcp_buffer_num + 2 ) );
+ /* append */
+ tcp_buffer[tcp_buffer_num] = val;
+
+ tcp_buffer_num++;
+ BER_BVZERO( &tcp_buffer[tcp_buffer_num] );
+
+ return rc;
+}
+
+static int
+config_tcp_buffer( ConfigArgs *c )
+{
+ int rc = LDAP_SUCCESS;
+
+#ifdef BALANCER_MODULE
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ if ( tcp_buffer == NULL || BER_BVISNULL( &tcp_buffer[0] ) ) {
+ return 1;
+ }
+ value_add( &c->rvalue_vals, tcp_buffer );
+ value_add( &c->rvalue_nvals, tcp_buffer );
+
+ return 0;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ if ( !c->line ) {
+ tcp_buffer_delete( tcp_buffer );
+ ber_bvarray_free( tcp_buffer );
+ tcp_buffer = NULL;
+ tcp_buffer_num = 0;
+
+ } else {
+ int size = -1, rw = 0;
+ LloadListener *l = NULL;
+
+ struct berval val = BER_BVNULL;
+
+ int i;
+
+ if ( tcp_buffer_num == 0 ) {
+ return 1;
+ }
+
+ /* parse */
+ rc = tcp_buffer_parse(
+ NULL, c->argc - 1, &c->argv[1], &size, &rw, &l );
+ if ( rc != 0 ) {
+ return 1;
+ }
+
+ /* unparse for later use */
+ rc = tcp_buffer_unparse( size, rw, l, &val );
+ if ( rc != LDAP_SUCCESS ) {
+ return 1;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &tcp_buffer[i] ); i++ ) {
+ if ( bvmatch( &tcp_buffer[i], &val ) ) {
+ break;
+ }
+ }
+
+ if ( BER_BVISNULL( &tcp_buffer[i] ) ) {
+ /* not found */
+ rc = 1;
+ goto done;
+ }
+
+ tcp_buffer_delete_one( &tcp_buffer[i] );
+ ber_memfree( tcp_buffer[i].bv_val );
+ for ( ; i < tcp_buffer_num; i++ ) {
+ tcp_buffer[i] = tcp_buffer[i + 1];
+ }
+ tcp_buffer_num--;
+
+done:;
+ if ( !BER_BVISNULL( &val ) ) {
+ SLAP_FREE(val.bv_val);
+ }
+ }
+
+ return rc;
+ }
+#endif /* BALANCER_MODULE */
+
+ rc = tcp_buffer_add_one( c->argc - 1, &c->argv[1] );
+ if ( rc ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> unable to add value #%d",
+ c->argv[0], tcp_buffer_num );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ return 0;
+}
+#endif /* LDAP_TCP_BUFFER */
+
+static int
+config_restrict( ConfigArgs *c )
+{
+ slap_mask_t restrictops = 0;
+ int i;
+ slap_verbmasks restrictable_ops[] = {
+ { BER_BVC("bind"), SLAP_RESTRICT_OP_BIND },
+ { BER_BVC("add"), SLAP_RESTRICT_OP_ADD },
+ { BER_BVC("modify"), SLAP_RESTRICT_OP_MODIFY },
+ { BER_BVC("rename"), SLAP_RESTRICT_OP_RENAME },
+ { BER_BVC("modrdn"), 0 },
+ { BER_BVC("delete"), SLAP_RESTRICT_OP_DELETE },
+ { BER_BVC("search"), SLAP_RESTRICT_OP_SEARCH },
+ { BER_BVC("compare"), SLAP_RESTRICT_OP_COMPARE },
+ { BER_BVC("read"), SLAP_RESTRICT_OP_READS },
+ { BER_BVC("write"), SLAP_RESTRICT_OP_WRITES },
+ { BER_BVC("extended"), SLAP_RESTRICT_OP_EXTENDED },
+ { BER_BVC("extended=" LDAP_EXOP_START_TLS), SLAP_RESTRICT_EXOP_START_TLS },
+ { BER_BVC("extended=" LDAP_EXOP_MODIFY_PASSWD), SLAP_RESTRICT_EXOP_MODIFY_PASSWD },
+ { BER_BVC("extended=" LDAP_EXOP_X_WHO_AM_I), SLAP_RESTRICT_EXOP_WHOAMI },
+ { BER_BVC("extended=" LDAP_EXOP_X_CANCEL), SLAP_RESTRICT_EXOP_CANCEL },
+ { BER_BVC("all"), SLAP_RESTRICT_OP_ALL },
+ { BER_BVNULL, 0 }
+ };
+
+ i = verbs_to_mask( c->argc, c->argv, restrictable_ops, &restrictops );
+ if ( i ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> unknown operation",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s %s\n",
+ c->log, c->cr_msg, c->argv[i] );
+ return 1;
+ }
+ if ( restrictops & SLAP_RESTRICT_OP_EXTENDED )
+ restrictops &= ~SLAP_RESTRICT_EXOP_MASK;
+ return 0;
+}
+
+static slap_verbmasks *loglevel_ops;
+
+static int
+loglevel_init( void )
+{
+ slap_verbmasks lo[] = {
+ { BER_BVC("Any"), (slap_mask_t)LDAP_DEBUG_ANY },
+ { BER_BVC("Trace"), LDAP_DEBUG_TRACE },
+ { BER_BVC("Packets"), LDAP_DEBUG_PACKETS },
+ { BER_BVC("Args"), LDAP_DEBUG_ARGS },
+ { BER_BVC("Conns"), LDAP_DEBUG_CONNS },
+ { BER_BVC("BER"), LDAP_DEBUG_BER },
+ { BER_BVC("Filter"), LDAP_DEBUG_FILTER },
+ { BER_BVC("Config"), LDAP_DEBUG_CONFIG },
+ { BER_BVC("ACL"), LDAP_DEBUG_ACL },
+ { BER_BVC("Stats"), LDAP_DEBUG_STATS },
+ { BER_BVC("Stats2"), LDAP_DEBUG_STATS2 },
+ { BER_BVC("Shell"), LDAP_DEBUG_SHELL },
+ { BER_BVC("Parse"), LDAP_DEBUG_PARSE },
+ { BER_BVC("Sync"), LDAP_DEBUG_SYNC },
+ { BER_BVC("None"), LDAP_DEBUG_NONE },
+ { BER_BVNULL, 0 }
+ };
+
+ return slap_verbmasks_init( &loglevel_ops, lo );
+}
+
+static void
+loglevel_destroy( void )
+{
+ if ( loglevel_ops ) {
+ (void)slap_verbmasks_destroy( loglevel_ops );
+ }
+ loglevel_ops = NULL;
+}
+
+int
+str2loglevel( const char *s, int *l )
+{
+ int i;
+
+ if ( loglevel_ops == NULL ) {
+ loglevel_init();
+ }
+
+ i = verb_to_mask( s, loglevel_ops );
+
+ if ( BER_BVISNULL( &loglevel_ops[i].word ) ) {
+ return -1;
+ }
+
+ *l = loglevel_ops[i].mask;
+
+ return 0;
+}
+
+int
+loglevel2bvarray( int l, BerVarray *bva )
+{
+ if ( loglevel_ops == NULL ) {
+ loglevel_init();
+ }
+
+ if ( l == 0 ) {
+ struct berval bv = BER_BVC("0");
+ return value_add_one( bva, &bv );
+ }
+
+ return mask_to_verbs( loglevel_ops, l, bva );
+}
+
+int
+loglevel_print( FILE *out )
+{
+ int i;
+
+ if ( loglevel_ops == NULL ) {
+ loglevel_init();
+ }
+
+ fprintf( out, "Installed log subsystems:\n\n" );
+ for ( i = 0; !BER_BVISNULL( &loglevel_ops[i].word ); i++ ) {
+ unsigned mask = loglevel_ops[i].mask & 0xffffffffUL;
+ fprintf( out,
+ ( mask == ( (slap_mask_t)-1 & 0xffffffffUL ) ?
+ "\t%-30s (-1, 0xffffffff)\n" :
+ "\t%-30s (%u, 0x%x)\n" ),
+ loglevel_ops[i].word.bv_val, mask, mask );
+ }
+
+ fprintf( out,
+ "\nNOTE: custom log subsystems may be later installed "
+ "by specific code\n\n" );
+
+ return 0;
+}
+
+static int config_syslog;
+
+static int
+config_loglevel( ConfigArgs *c )
+{
+ int i;
+
+ if ( loglevel_ops == NULL ) {
+ loglevel_init();
+ }
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ /* Get default or commandline slapd setting */
+ if ( ldap_syslog && !config_syslog ) config_syslog = ldap_syslog;
+ return loglevel2bvarray( config_syslog, &c->rvalue_vals );
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ if ( !c->line ) {
+ config_syslog = 0;
+ } else {
+ i = verb_to_mask( c->line, loglevel_ops );
+ config_syslog &= ~loglevel_ops[i].mask;
+ }
+ if ( slapMode & SLAP_SERVER_MODE ) {
+ ldap_syslog = config_syslog;
+ }
+ return 0;
+ }
+
+ for ( i = 1; i < c->argc; i++ ) {
+ int level;
+
+ if ( isdigit( (unsigned char)c->argv[i][0] ) || c->argv[i][0] == '-' ) {
+ if ( lutil_atoix( &level, c->argv[i], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "<%s> unable to parse level",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
+ c->log, c->cr_msg, c->argv[i] );
+ return 1;
+ }
+ } else {
+ if ( str2loglevel( c->argv[i], &level ) ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> unknown level",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
+ c->log, c->cr_msg, c->argv[i] );
+ return 1;
+ }
+ }
+ /* Explicitly setting a zero clears all the levels */
+ if ( level )
+ config_syslog |= level;
+ else
+ config_syslog = 0;
+ }
+ if ( slapMode & SLAP_SERVER_MODE ) {
+ ldap_syslog = config_syslog;
+ }
+ return 0;
+}
+
+static int
+config_include( ConfigArgs *c )
+{
+ int savelineno = c->lineno;
+ int rc;
+ ConfigFile *cf;
+ ConfigFile *cfsave = cfn;
+ ConfigFile *cf2 = NULL;
+
+ /* Leftover from RE23. No dynamic config for include files */
+ if ( c->op == SLAP_CONFIG_EMIT || c->op == LDAP_MOD_DELETE ) return 1;
+
+ cf = ch_calloc( 1, sizeof(ConfigFile) );
+ if ( cfn->c_kids ) {
+ for ( cf2 = cfn->c_kids; cf2 && cf2->c_sibs; cf2 = cf2->c_sibs )
+ /* empty */;
+ cf2->c_sibs = cf;
+ } else {
+ cfn->c_kids = cf;
+ }
+ cfn = cf;
+ ber_str2bv( c->argv[1], 0, 1, &cf->c_file );
+ rc = lload_read_config_file(
+ c->argv[1], c->depth + 1, c, config_back_cf_table );
+ c->lineno = savelineno - 1;
+ cfn = cfsave;
+ if ( rc ) {
+ if ( cf2 )
+ cf2->c_sibs = NULL;
+ else
+ cfn->c_kids = NULL;
+ ch_free( cf->c_file.bv_val );
+ ch_free( cf );
+ } else {
+ c->ca_private = cf;
+ }
+ return rc;
+}
+
+static int
+config_feature( ConfigArgs *c )
+{
+ slap_verbmasks features[] = {
+#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
+ { BER_BVC("vc"), LLOAD_FEATURE_VC },
+#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
+ { BER_BVC("proxyauthz"), LLOAD_FEATURE_PROXYAUTHZ },
+ { BER_BVC("read_pause"), LLOAD_FEATURE_PAUSE },
+ { BER_BVNULL, 0 }
+ };
+ slap_mask_t mask = 0;
+ int i;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ return mask_to_verbs( features, lload_features, &c->rvalue_vals );
+ }
+
+ lload_change.type = LLOAD_CHANGE_MODIFY;
+ lload_change.object = LLOAD_DAEMON;
+ lload_change.flags.daemon |= LLOAD_DAEMON_MOD_FEATURES;
+ if ( !lload_change.target ) {
+ lload_change.target = (void *)(uintptr_t)~lload_features;
+ }
+
+ if ( c->op == LDAP_MOD_DELETE ) {
+ if ( !c->line ) {
+ /* Last value has been deleted */
+ lload_features = 0;
+ } else {
+ i = verb_to_mask( c->line, features );
+ lload_features &= ~features[i].mask;
+ }
+ return 0;
+ }
+
+ i = verbs_to_mask( c->argc, c->argv, features, &mask );
+ if ( i ) {
+ Debug( LDAP_DEBUG_ANY, "%s: <%s> unknown feature %s\n", c->log,
+ c->argv[0], c->argv[i] );
+ return 1;
+ }
+
+ if ( mask & ~LLOAD_FEATURE_SUPPORTED_MASK ) {
+ for ( i = 1; i < c->argc; i++ ) {
+ int j = verb_to_mask( c->argv[i], features );
+ if ( features[j].mask & ~LLOAD_FEATURE_SUPPORTED_MASK ) {
+ Debug( LDAP_DEBUG_ANY, "%s: <%s> "
+ "experimental feature %s is undocumented, unsupported "
+ "and can change or disappear at any time!\n",
+ c->log, c->argv[0], c->argv[i] );
+ }
+ }
+ }
+
+ lload_features |= mask;
+ return 0;
+}
+
+#ifdef HAVE_TLS
+static int
+config_tls_cleanup( ConfigArgs *c )
+{
+ int rc = 0;
+
+ if ( lload_tls_ld ) {
+ int opt = 1;
+
+ ldap_pvt_tls_ctx_free( lload_tls_ctx );
+ lload_tls_ctx = NULL;
+
+ /* Force new ctx to be created */
+ rc = ldap_pvt_tls_set_option(
+ lload_tls_ld, LDAP_OPT_X_TLS_NEWCTX, &opt );
+ if ( rc == 0 ) {
+ /* The ctx's refcount is bumped up here */
+ ldap_pvt_tls_get_option(
+ lload_tls_ld, LDAP_OPT_X_TLS_CTX, &lload_tls_ctx );
+ } else {
+ if ( rc == LDAP_NOT_SUPPORTED )
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ else
+ rc = LDAP_OTHER;
+ }
+ }
+ return rc;
+}
+
+static int
+config_tls_option( ConfigArgs *c )
+{
+ int flag;
+ int berval = 0;
+ LDAP *ld = lload_tls_ld;
+
+ switch ( c->type ) {
+ case CFG_TLS_RAND:
+ flag = LDAP_OPT_X_TLS_RANDOM_FILE;
+ ld = NULL;
+ break;
+ case CFG_TLS_CIPHER:
+ flag = LDAP_OPT_X_TLS_CIPHER_SUITE;
+ break;
+ case CFG_TLS_CERT_FILE:
+ flag = LDAP_OPT_X_TLS_CERTFILE;
+ break;
+ case CFG_TLS_CERT_KEY:
+ flag = LDAP_OPT_X_TLS_KEYFILE;
+ break;
+ case CFG_TLS_CA_PATH:
+ flag = LDAP_OPT_X_TLS_CACERTDIR;
+ break;
+ case CFG_TLS_CA_FILE:
+ flag = LDAP_OPT_X_TLS_CACERTFILE;
+ break;
+ case CFG_TLS_DH_FILE:
+ flag = LDAP_OPT_X_TLS_DHFILE;
+ break;
+ case CFG_TLS_ECNAME:
+ flag = LDAP_OPT_X_TLS_ECNAME;
+ break;
+#ifdef HAVE_GNUTLS
+ case CFG_TLS_CRL_FILE:
+ flag = LDAP_OPT_X_TLS_CRLFILE;
+ break;
+#endif
+ case CFG_TLS_CACERT:
+ flag = LDAP_OPT_X_TLS_CACERT;
+ berval = 1;
+ break;
+ case CFG_TLS_CERT:
+ flag = LDAP_OPT_X_TLS_CERT;
+ berval = 1;
+ break;
+ case CFG_TLS_KEY:
+ flag = LDAP_OPT_X_TLS_KEY;
+ berval = 1;
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "unknown tls_option <0x%x>\n",
+ c->log, c->type );
+ return 1;
+ }
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ return ldap_pvt_tls_get_option( ld, flag,
+ berval ? (void *)&c->value_bv : (void *)&c->value_string );
+ }
+
+ lload_change.type = LLOAD_CHANGE_MODIFY;
+ lload_change.object = LLOAD_DAEMON;
+ lload_change.flags.daemon |= LLOAD_DAEMON_MOD_TLS;
+
+ config_push_cleanup( c, config_tls_cleanup );
+ if ( c->op == LDAP_MOD_DELETE ) {
+ return ldap_pvt_tls_set_option( ld, flag, NULL );
+ }
+ if ( !berval ) ch_free( c->value_string );
+ return ldap_pvt_tls_set_option(
+ ld, flag, berval ? (void *)&c->value_bv : (void *)c->argv[1] );
+}
+
+/* FIXME: this ought to be provided by libldap */
+static int
+config_tls_config( ConfigArgs *c )
+{
+ int i, flag;
+
+ switch ( c->type ) {
+ case CFG_TLS_CRLCHECK:
+ flag = LDAP_OPT_X_TLS_CRLCHECK;
+ break;
+ case CFG_TLS_VERIFY:
+ flag = LDAP_OPT_X_TLS_REQUIRE_CERT;
+ break;
+ case CFG_TLS_PROTOCOL_MIN:
+ flag = LDAP_OPT_X_TLS_PROTOCOL_MIN;
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "unknown tls_option <0x%x>\n",
+ c->log, c->type );
+ return 1;
+ }
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ return lload_tls_get_config( lload_tls_ld, flag, &c->value_string );
+ }
+
+ lload_change.type = LLOAD_CHANGE_MODIFY;
+ lload_change.object = LLOAD_DAEMON;
+ lload_change.flags.daemon |= LLOAD_DAEMON_MOD_TLS;
+
+ config_push_cleanup( c, config_tls_cleanup );
+ if ( c->op == LDAP_MOD_DELETE ) {
+ int i = 0;
+ return ldap_pvt_tls_set_option( lload_tls_ld, flag, &i );
+ }
+ ch_free( c->value_string );
+ if ( isdigit( (unsigned char)c->argv[1][0] ) &&
+ c->type != CFG_TLS_PROTOCOL_MIN ) {
+ if ( lutil_atoi( &i, c->argv[1] ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "unable to parse %s \"%s\"\n",
+ c->log, c->argv[0], c->argv[1] );
+ return 1;
+ }
+ return ldap_pvt_tls_set_option( lload_tls_ld, flag, &i );
+ } else {
+ return ldap_pvt_tls_config( lload_tls_ld, flag, c->argv[1] );
+ }
+}
+#endif
+
+#ifdef BALANCER_MODULE
+static int
+config_share_tls_ctx( ConfigArgs *c )
+{
+ int rc = LDAP_SUCCESS;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ c->value_int = lload_use_slap_tls_ctx;
+ return rc;
+ }
+
+ lload_change.type = LLOAD_CHANGE_MODIFY;
+ lload_change.object = LLOAD_DAEMON;
+ lload_change.flags.daemon |= LLOAD_DAEMON_MOD_TLS;
+
+ if ( c->op == LDAP_MOD_DELETE ) {
+ lload_use_slap_tls_ctx = 0;
+ return rc;
+ }
+
+ lload_use_slap_tls_ctx = c->value_int;
+ return rc;
+}
+#endif /* BALANCER_MODULE */
+
+void
+lload_init_config_argv( ConfigArgs *c )
+{
+ c->argv = ch_calloc( ARGS_STEP + 1, sizeof(*c->argv) );
+ c->argv_size = ARGS_STEP + 1;
+}
+
+ConfigTable *
+lload_config_find_keyword( ConfigTable *Conf, ConfigArgs *c )
+{
+ int i;
+
+ for ( i = 0; Conf[i].name; i++ )
+ if ( ( Conf[i].length &&
+ ( !strncasecmp(
+ c->argv[0], Conf[i].name, Conf[i].length ) ) ) ||
+ ( !strcasecmp( c->argv[0], Conf[i].name ) ) )
+ break;
+ if ( !Conf[i].name ) return NULL;
+ if ( (Conf[i].arg_type & ARGS_TYPES) == ARG_BINARY ) {
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN( c->linelen );
+ ch_free( c->tline );
+ c->tline = ch_malloc( decode_len + 1 );
+ c->linelen = lutil_b64_pton( c->line, c->tline, decode_len );
+ if ( c->linelen < 0 ) {
+ ch_free( c->tline );
+ c->tline = NULL;
+ return NULL;
+ }
+ c->line = c->tline;
+ }
+ return Conf + i;
+}
+
+int
+lload_config_check_vals( ConfigTable *Conf, ConfigArgs *c, int check_only )
+{
+ int arg_user, arg_type, arg_syn, iarg;
+ unsigned uiarg;
+ long larg;
+ unsigned long ularg;
+ ber_len_t barg;
+
+ if ( Conf->arg_type == ARG_IGNORED ) {
+ Debug( LDAP_DEBUG_CONFIG, "%s: keyword <%s> ignored\n",
+ c->log, Conf->name );
+ return 0;
+ }
+ arg_type = Conf->arg_type & ARGS_TYPES;
+ arg_user = Conf->arg_type & ARGS_USERLAND;
+ arg_syn = Conf->arg_type & ARGS_SYNTAX;
+
+ if ( Conf->min_args && ( c->argc < Conf->min_args ) ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> missing <%s> argument",
+ c->argv[0], Conf->what ? Conf->what : "" );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: keyword %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ if ( Conf->max_args && ( c->argc > Conf->max_args ) ) {
+ char *ignored = " ignored";
+
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> extra cruft after <%s>",
+ c->argv[0], Conf->what );
+
+ ignored = "";
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s%s\n",
+ c->log, c->cr_msg, ignored );
+ return ARG_BAD_CONF;
+ }
+ if ( (arg_syn & ARG_PAREN) && *c->argv[1] != '(' /*')'*/ ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> old format not supported",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ if ( arg_type && !Conf->arg_item && !(arg_syn & ARG_OFFSET) ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "<%s> invalid config_table, arg_item is NULL",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ c->type = arg_user;
+ memset( &c->values, 0, sizeof(c->values) );
+ if ( arg_type == ARG_STRING ) {
+ assert( c->argc == 2 );
+ if ( !check_only ) c->value_string = ch_strdup( c->argv[1] );
+ } else if ( arg_type == ARG_BERVAL ) {
+ assert( c->argc == 2 );
+ if ( !check_only ) ber_str2bv( c->argv[1], 0, 1, &c->value_bv );
+ } else if ( arg_type == ARG_BINARY ) {
+ assert( c->argc == 2 );
+ if ( !check_only ) {
+ c->value_bv.bv_len = c->linelen;
+ c->value_bv.bv_val = ch_malloc( c->linelen );
+ AC_MEMCPY( c->value_bv.bv_val, c->line, c->linelen );
+ }
+ } else { /* all numeric */
+ int j;
+ iarg = 0;
+ larg = 0;
+ barg = 0;
+ switch ( arg_type ) {
+ case ARG_INT:
+ assert( c->argc == 2 );
+ if ( lutil_atoix( &iarg, c->argv[1], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "<%s> unable to parse \"%s\" as int",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ break;
+ case ARG_UINT:
+ assert( c->argc == 2 );
+ if ( lutil_atoux( &uiarg, c->argv[1], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "<%s> unable to parse \"%s\" as unsigned int",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ break;
+ case ARG_LONG:
+ assert( c->argc == 2 );
+ if ( lutil_atolx( &larg, c->argv[1], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "<%s> unable to parse \"%s\" as long",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ break;
+ case ARG_ULONG:
+ assert( c->argc == 2 );
+ if ( LUTIL_ATOULX( &ularg, c->argv[1], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "<%s> unable to parse \"%s\" as unsigned long",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ break;
+ case ARG_BER_LEN_T: {
+ unsigned long l;
+ assert( c->argc == 2 );
+ if ( lutil_atoulx( &l, c->argv[1], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "<%s> unable to parse \"%s\" as ber_len_t",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ barg = (ber_len_t)l;
+ } break;
+ case ARG_ON_OFF:
+ /* note: this is an explicit exception
+ * to the "need exactly 2 args" rule */
+ if ( c->argc == 1 ) {
+ iarg = 1;
+ } else if ( !strcasecmp( c->argv[1], "on" ) ||
+ !strcasecmp( c->argv[1], "true" ) ||
+ !strcasecmp( c->argv[1], "yes" ) ) {
+ iarg = 1;
+ } else if ( !strcasecmp( c->argv[1], "off" ) ||
+ !strcasecmp( c->argv[1], "false" ) ||
+ !strcasecmp( c->argv[1], "no" ) ) {
+ iarg = 0;
+ } else {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "<%s> invalid value",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ break;
+ }
+ j = (arg_type & ARG_NONZERO) ? 1 : 0;
+ if ( iarg < j && larg < j && barg < (unsigned)j ) {
+ larg = larg ? larg : ( barg ? (long)barg : iarg );
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> invalid value",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ switch ( arg_type ) {
+ case ARG_ON_OFF:
+ case ARG_INT:
+ c->value_int = iarg;
+ break;
+ case ARG_UINT:
+ c->value_uint = uiarg;
+ break;
+ case ARG_LONG:
+ c->value_long = larg;
+ break;
+ case ARG_ULONG:
+ c->value_ulong = ularg;
+ break;
+ case ARG_BER_LEN_T:
+ c->value_ber_t = barg;
+ break;
+ }
+ }
+ return 0;
+}
+
+int
+lload_config_set_vals( ConfigTable *Conf, ConfigArgs *c )
+{
+ int rc, arg_type;
+ void *ptr = NULL;
+
+ arg_type = Conf->arg_type;
+ if ( arg_type & ARG_MAGIC ) {
+ c->cr_msg[0] = '\0';
+ rc = ( *( (ConfigDriver *)Conf->arg_item ) )( c );
+ if ( rc ) {
+ if ( !c->cr_msg[0] ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "<%s> handler exited with %d",
+ c->argv[0], rc );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s!\n", c->log, c->cr_msg );
+ }
+ return ARG_BAD_CONF;
+ }
+ return 0;
+ }
+ if ( arg_type & ARG_OFFSET ) {
+ {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "<%s> offset is missing base pointer",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s!\n", c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ ptr = (void *)( (char *)ptr + (long)Conf->arg_item );
+ } else if ( arg_type & ARGS_TYPES ) {
+ ptr = Conf->arg_item;
+ }
+ if ( arg_type & ARGS_TYPES ) switch ( arg_type & ARGS_TYPES ) {
+ case ARG_ON_OFF:
+ case ARG_INT:
+ *(int *)ptr = c->value_int;
+ break;
+ case ARG_UINT:
+ *(unsigned *)ptr = c->value_uint;
+ break;
+ case ARG_LONG:
+ *(long *)ptr = c->value_long;
+ break;
+ case ARG_ULONG:
+ *(size_t *)ptr = c->value_ulong;
+ break;
+ case ARG_BER_LEN_T:
+ *(ber_len_t *)ptr = c->value_ber_t;
+ break;
+ case ARG_STRING: {
+ char *cc = *(char **)ptr;
+ if ( cc ) {
+ if ( (arg_type & ARG_UNIQUE) &&
+ c->op == SLAP_CONFIG_ADD ) {
+ Debug( LDAP_DEBUG_CONFIG, "%s: already set %s!\n",
+ c->log, Conf->name );
+ return ARG_BAD_CONF;
+ }
+ ch_free( cc );
+ }
+ *(char **)ptr = c->value_string;
+ break;
+ }
+ case ARG_BERVAL:
+ case ARG_BINARY:
+ *(struct berval *)ptr = c->value_bv;
+ break;
+ }
+ return 0;
+}
+
+int
+lload_config_add_vals( ConfigTable *Conf, ConfigArgs *c )
+{
+ int rc, arg_type;
+
+ arg_type = Conf->arg_type;
+ if ( arg_type == ARG_IGNORED ) {
+ Debug( LDAP_DEBUG_CONFIG, "%s: keyword <%s> ignored\n",
+ c->log, Conf->name );
+ return 0;
+ }
+ rc = lload_config_check_vals( Conf, c, 0 );
+ if ( rc ) return rc;
+ return lload_config_set_vals( Conf, c );
+}
+
+int
+lload_read_config_file(
+ const char *fname,
+ int depth,
+ ConfigArgs *cf,
+ ConfigTable *cft )
+{
+ FILE *fp;
+ ConfigTable *ct;
+ ConfigArgs *c;
+ int rc;
+ struct stat s;
+
+ c = ch_calloc( 1, sizeof(ConfigArgs) );
+ if ( c == NULL ) {
+ return 1;
+ }
+
+ if ( depth ) {
+ memcpy( c, cf, sizeof(ConfigArgs) );
+ } else {
+ c->depth = depth; /* XXX */
+ }
+
+ c->valx = -1;
+ c->fname = fname;
+ lload_init_config_argv( c );
+
+ if ( stat( fname, &s ) != 0 ) {
+ char ebuf[128];
+ int saved_errno = errno;
+ ldap_syslog = 1;
+ Debug( LDAP_DEBUG_ANY, "could not stat config file \"%s\": %s (%d)\n",
+ fname, AC_STRERROR_R( saved_errno, ebuf, sizeof(ebuf) ),
+ saved_errno );
+ ch_free( c->argv );
+ ch_free( c );
+ return 1;
+ }
+
+ if ( !S_ISREG(s.st_mode) ) {
+ ldap_syslog = 1;
+ Debug( LDAP_DEBUG_ANY, "regular file expected, got \"%s\"\n", fname );
+ ch_free( c->argv );
+ ch_free( c );
+ return 1;
+ }
+
+ fp = fopen( fname, "r" );
+ if ( fp == NULL ) {
+ char ebuf[128];
+ int saved_errno = errno;
+ ldap_syslog = 1;
+ Debug( LDAP_DEBUG_ANY, "could not open config file \"%s\": %s (%d)\n",
+ fname, AC_STRERROR_R( saved_errno, ebuf, sizeof(ebuf) ),
+ saved_errno );
+ ch_free( c->argv );
+ ch_free( c );
+ return 1;
+ }
+
+ Debug( LDAP_DEBUG_CONFIG, "reading config file %s\n", fname );
+
+ fp_getline_init( c );
+
+ c->tline = NULL;
+
+ while ( fp_getline( fp, c ) ) {
+ /* skip comments and blank lines */
+ if ( c->line[0] == '#' || c->line[0] == '\0' ) {
+ continue;
+ }
+
+ snprintf( c->log, sizeof(c->log), "%s: line %d",
+ c->fname, c->lineno );
+
+ c->argc = 0;
+ ch_free( c->tline );
+ if ( lload_config_fp_parse_line( c ) ) {
+ rc = 1;
+ goto done;
+ }
+
+ if ( c->argc < 1 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: bad config line\n", c->log );
+ rc = 1;
+ goto done;
+ }
+
+ c->op = SLAP_CONFIG_ADD;
+
+ ct = lload_config_find_keyword( cft, c );
+ if ( ct ) {
+ c->table = Cft_Global;
+ rc = lload_config_add_vals( ct, c );
+ if ( !rc ) continue;
+
+ if ( rc & ARGS_USERLAND ) {
+ /* XXX a usertype would be opaque here */
+ Debug( LDAP_DEBUG_CONFIG, "%s: unknown user type <%s>\n",
+ c->log, c->argv[0] );
+ rc = 1;
+ goto done;
+
+ } else if ( rc == ARG_BAD_CONF ) {
+ rc = 1;
+ goto done;
+ }
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s: unknown directive "
+ "<%s> outside backend info and database definitions\n",
+ c->log, *c->argv );
+ rc = 1;
+ goto done;
+ }
+ }
+
+ rc = 0;
+
+done:
+ ch_free( c->tline );
+ fclose( fp );
+ ch_free( c->argv );
+ ch_free( c );
+ return rc;
+}
+
+int
+lload_read_config( const char *fname, const char *dir )
+{
+ if ( !fname ) fname = LLOADD_DEFAULT_CONFIGFILE;
+
+ cfn = ch_calloc( 1, sizeof(ConfigFile) );
+
+ return lload_read_config_file( fname, 0, NULL, config_back_cf_table );
+}
+
+/* restrictops, allows, disallows, requires, loglevel */
+
+int
+bverb_to_mask( struct berval *bword, slap_verbmasks *v )
+{
+ int i;
+ for ( i = 0; !BER_BVISNULL( &v[i].word ); i++ ) {
+ if ( !ber_bvstrcasecmp( bword, &v[i].word ) ) break;
+ }
+ return i;
+}
+
+int
+verb_to_mask( const char *word, slap_verbmasks *v )
+{
+ struct berval bword;
+ ber_str2bv( word, 0, 0, &bword );
+ return bverb_to_mask( &bword, v );
+}
+
+int
+verbs_to_mask( int argc, char *argv[], slap_verbmasks *v, slap_mask_t *m )
+{
+ int i, j;
+ for ( i = 1; i < argc; i++ ) {
+ j = verb_to_mask( argv[i], v );
+ if ( BER_BVISNULL( &v[j].word ) ) return i;
+ while ( !v[j].mask )
+ j--;
+ *m |= v[j].mask;
+ }
+ return 0;
+}
+
+/* Mask keywords that represent multiple bits should occur before single
+ * bit keywords in the verbmasks array.
+ */
+int
+mask_to_verbs( slap_verbmasks *v, slap_mask_t m, BerVarray *bva )
+{
+ int i, rc = 1;
+
+ if ( m ) {
+ for ( i = 0; !BER_BVISNULL( &v[i].word ); i++ ) {
+ if ( !v[i].mask ) continue;
+ if ( (m & v[i].mask) == v[i].mask ) {
+ value_add_one( bva, &v[i].word );
+ rc = 0;
+ m ^= v[i].mask;
+ if ( !m ) break;
+ }
+ }
+ }
+ return rc;
+}
+
+int
+slap_verbmasks_init( slap_verbmasks **vp, slap_verbmasks *v )
+{
+ int i;
+
+ assert( *vp == NULL );
+
+ for ( i = 0; !BER_BVISNULL( &v[i].word ); i++ ) /* EMPTY */;
+
+ *vp = ch_calloc( i + 1, sizeof(slap_verbmasks) );
+
+ for ( i = 0; !BER_BVISNULL( &v[i].word ); i++ ) {
+ ber_dupbv( &(*vp)[i].word, &v[i].word );
+ *( (slap_mask_t *)&(*vp)[i].mask ) = v[i].mask;
+ }
+
+ BER_BVZERO( &(*vp)[i].word );
+
+ return 0;
+}
+
+int
+slap_verbmasks_destroy( slap_verbmasks *v )
+{
+ int i;
+
+ assert( v != NULL );
+
+ for ( i = 0; !BER_BVISNULL( &v[i].word ); i++ ) {
+ ch_free( v[i].word.bv_val );
+ }
+
+ ch_free( v );
+
+ return 0;
+}
+
+#ifndef BALANCER_MODULE
+int
+config_push_cleanup( ConfigArgs *ca, ConfigDriver *cleanup )
+{
+ /* Stub, cleanups only run in online config */
+ return 0;
+}
+#endif /* !BALANCER_MODULE */
+
+static slap_verbmasks tlskey[] = {
+ { BER_BVC("no"), LLOAD_CLEARTEXT },
+ { BER_BVC("yes"), LLOAD_STARTTLS_OPTIONAL },
+ { BER_BVC("critical"), LLOAD_STARTTLS },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks crlkeys[] = {
+ { BER_BVC("none"), LDAP_OPT_X_TLS_CRL_NONE },
+ { BER_BVC("peer"), LDAP_OPT_X_TLS_CRL_PEER },
+ { BER_BVC("all"), LDAP_OPT_X_TLS_CRL_ALL },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks vfykeys[] = {
+ { BER_BVC("never"), LDAP_OPT_X_TLS_NEVER },
+ { BER_BVC("allow"), LDAP_OPT_X_TLS_ALLOW },
+ { BER_BVC("try"), LDAP_OPT_X_TLS_TRY },
+ { BER_BVC("demand"), LDAP_OPT_X_TLS_DEMAND },
+ { BER_BVC("hard"), LDAP_OPT_X_TLS_HARD },
+ { BER_BVC("true"), LDAP_OPT_X_TLS_HARD },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks methkey[] = {
+ { BER_BVC("none"), LDAP_AUTH_NONE },
+ { BER_BVC("simple"), LDAP_AUTH_SIMPLE },
+#ifdef HAVE_CYRUS_SASL
+ { BER_BVC("sasl"), LDAP_AUTH_SASL },
+#endif
+ { BER_BVNULL, 0 }
+};
+
+int
+lload_keepalive_parse(
+ struct berval *val,
+ void *bc,
+ slap_cf_aux_table *tab0,
+ const char *tabmsg,
+ int unparse )
+{
+ if ( unparse ) {
+ slap_keepalive *sk = (slap_keepalive *)bc;
+ int rc = snprintf( val->bv_val, val->bv_len, "%d:%d:%d",
+ sk->sk_idle, sk->sk_probes, sk->sk_interval );
+ if ( rc < 0 ) {
+ return -1;
+ }
+
+ if ( (unsigned)rc >= val->bv_len ) {
+ return -1;
+ }
+
+ val->bv_len = rc;
+
+ } else {
+ char *s = val->bv_val;
+ char *next;
+ slap_keepalive *sk = (slap_keepalive *)bc;
+ slap_keepalive sk2;
+
+ if ( s[0] == ':' ) {
+ sk2.sk_idle = 0;
+ s++;
+
+ } else {
+ sk2.sk_idle = strtol( s, &next, 10 );
+ if ( next == s || next[0] != ':' ) {
+ return -1;
+ }
+
+ if ( sk2.sk_idle < 0 ) {
+ return -1;
+ }
+
+ s = ++next;
+ }
+
+ if ( s[0] == ':' ) {
+ sk2.sk_probes = 0;
+ s++;
+
+ } else {
+ sk2.sk_probes = strtol( s, &next, 10 );
+ if ( next == s || next[0] != ':' ) {
+ return -1;
+ }
+
+ if ( sk2.sk_probes < 0 ) {
+ return -1;
+ }
+
+ s = ++next;
+ }
+
+ if ( *s == '\0' ) {
+ sk2.sk_interval = 0;
+
+ } else {
+ sk2.sk_interval = strtol( s, &next, 10 );
+ if ( next == s || next[0] != '\0' ) {
+ return -1;
+ }
+
+ if ( sk2.sk_interval < 0 ) {
+ return -1;
+ }
+ }
+
+ *sk = sk2;
+
+ ber_memfree( val->bv_val );
+ BER_BVZERO( val );
+ }
+
+ return 0;
+}
+
+static slap_cf_aux_table backendkey[] = {
+ { BER_BVC("uri="), offsetof(LloadBackend, b_uri), 'b', 1, NULL },
+
+ { BER_BVC("numconns="), offsetof(LloadBackend, b_numconns), 'i', 0, NULL },
+ { BER_BVC("bindconns="), offsetof(LloadBackend, b_numbindconns), 'i', 0, NULL },
+ { BER_BVC("retry="), offsetof(LloadBackend, b_retry_timeout), 'i', 0, NULL },
+
+ { BER_BVC("max-pending-ops="), offsetof(LloadBackend, b_max_pending), 'i', 0, NULL },
+ { BER_BVC("conn-max-pending="), offsetof(LloadBackend, b_max_conn_pending), 'i', 0, NULL },
+ { BER_BVC("starttls="), offsetof(LloadBackend, b_tls_conf), 'i', 0, tlskey },
+ { BER_BVNULL, 0, 0, 0, NULL }
+};
+
+static slap_cf_aux_table bindkey[] = {
+ { BER_BVC("bindmethod="), offsetof(slap_bindconf, sb_method), 'i', 0, methkey },
+ { BER_BVC("timeout="), offsetof(slap_bindconf, sb_timeout_api), 'i', 0, NULL },
+ { BER_BVC("network-timeout="), offsetof(slap_bindconf, sb_timeout_net), 'i', 0, NULL },
+ { BER_BVC("binddn="), offsetof(slap_bindconf, sb_binddn), 'b', 1, NULL },
+ { BER_BVC("credentials="), offsetof(slap_bindconf, sb_cred), 'b', 1, NULL },
+ { BER_BVC("saslmech="), offsetof(slap_bindconf, sb_saslmech), 'b', 0, NULL },
+ { BER_BVC("secprops="), offsetof(slap_bindconf, sb_secprops), 's', 0, NULL },
+ { BER_BVC("realm="), offsetof(slap_bindconf, sb_realm), 'b', 0, NULL },
+ { BER_BVC("authcID="), offsetof(slap_bindconf, sb_authcId), 'b', 1, NULL },
+ { BER_BVC("authzID="), offsetof(slap_bindconf, sb_authzId), 'b', 1, NULL },
+ { BER_BVC("keepalive="), offsetof(slap_bindconf, sb_keepalive), 'x', 0, (slap_verbmasks *)lload_keepalive_parse },
+ { BER_BVC("tcp-user-timeout="), offsetof(slap_bindconf, sb_tcp_user_timeout), 'u', 0, NULL },
+#ifdef HAVE_TLS
+ /* NOTE: replace "12" with the actual index
+ * of the first TLS-related line */
+#define aux_TLS (bindkey+12) /* beginning of TLS keywords */
+
+ { BER_BVC("tls_cert="), offsetof(slap_bindconf, sb_tls_cert), 's', 1, NULL },
+ { BER_BVC("tls_key="), offsetof(slap_bindconf, sb_tls_key), 's', 1, NULL },
+ { BER_BVC("tls_cacert="), offsetof(slap_bindconf, sb_tls_cacert), 's', 1, NULL },
+ { BER_BVC("tls_cacertdir="), offsetof(slap_bindconf, sb_tls_cacertdir), 's', 1, NULL },
+ { BER_BVC("tls_reqcert="), offsetof(slap_bindconf, sb_tls_reqcert), 's', 0, NULL },
+ { BER_BVC("tls_reqsan="), offsetof(slap_bindconf, sb_tls_reqsan), 's', 0, NULL },
+ { BER_BVC("tls_cipher_suite="), offsetof(slap_bindconf, sb_tls_cipher_suite), 's', 0, NULL },
+ { BER_BVC("tls_protocol_min="), offsetof(slap_bindconf, sb_tls_protocol_min), 's', 0, NULL },
+ { BER_BVC("tls_ecname="), offsetof(slap_bindconf, sb_tls_ecname), 's', 0, NULL },
+#ifdef HAVE_OPENSSL
+ { BER_BVC("tls_crlcheck="), offsetof(slap_bindconf, sb_tls_crlcheck), 's', 0, NULL },
+#endif
+#endif
+ { BER_BVNULL, 0, 0, 0, NULL }
+};
+
+/*
+ * 's': char *
+ * 'b': struct berval
+ * 'i': int; if !NULL, compute using ((slap_verbmasks *)aux)
+ * 'u': unsigned
+ * 'I': long
+ * 'U': unsigned long
+ */
+
+int
+lload_cf_aux_table_parse(
+ const char *word,
+ void *dst,
+ slap_cf_aux_table *tab0,
+ LDAP_CONST char *tabmsg )
+{
+ int rc = SLAP_CONF_UNKNOWN;
+ slap_cf_aux_table *tab;
+
+ for ( tab = tab0; !BER_BVISNULL( &tab->key ); tab++ ) {
+ if ( !strncasecmp( word, tab->key.bv_val, tab->key.bv_len ) ) {
+ char **cptr;
+ int *iptr, j;
+ unsigned *uptr;
+ long *lptr;
+ unsigned long *ulptr;
+ struct berval *bptr;
+ const char *val = word + tab->key.bv_len;
+
+ switch ( tab->type ) {
+ case 's':
+ cptr = (char **)( (char *)dst + tab->off );
+ *cptr = ch_strdup( val );
+ rc = 0;
+ break;
+
+ case 'b':
+ bptr = (struct berval *)( (char *)dst + tab->off );
+ assert( tab->aux == NULL );
+ ber_str2bv( val, 0, 1, bptr );
+ rc = 0;
+ break;
+
+ case 'i':
+ iptr = (int *)( (char *)dst + tab->off );
+
+ if ( tab->aux != NULL ) {
+ slap_verbmasks *aux = (slap_verbmasks *)tab->aux;
+
+ assert( aux != NULL );
+
+ rc = 1;
+ for ( j = 0; !BER_BVISNULL( &aux[j].word ); j++ ) {
+ if ( !strcasecmp( val, aux[j].word.bv_val ) ) {
+ *iptr = aux[j].mask;
+ rc = 0;
+ break;
+ }
+ }
+
+ } else {
+ rc = lutil_atoix( iptr, val, 0 );
+ }
+ break;
+
+ case 'u':
+ uptr = (unsigned *)( (char *)dst + tab->off );
+
+ rc = lutil_atoux( uptr, val, 0 );
+ break;
+
+ case 'I':
+ lptr = (long *)( (char *)dst + tab->off );
+
+ rc = lutil_atolx( lptr, val, 0 );
+ break;
+
+ case 'U':
+ ulptr = (unsigned long *)( (char *)dst + tab->off );
+
+ rc = lutil_atoulx( ulptr, val, 0 );
+ break;
+
+ case 'x':
+ if ( tab->aux != NULL ) {
+ struct berval value;
+ lload_cf_aux_table_parse_x *func =
+ (lload_cf_aux_table_parse_x *)tab->aux;
+
+ ber_str2bv( val, 0, 1, &value );
+
+ rc = func( &value, (void *)( (char *)dst + tab->off ),
+ tab, tabmsg, 0 );
+
+ } else {
+ rc = 1;
+ }
+ break;
+ }
+
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY, "invalid %s value %s\n", tabmsg, word );
+ }
+
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+int
+lload_cf_aux_table_unparse(
+ void *src,
+ struct berval *bv,
+ slap_cf_aux_table *tab0 )
+{
+ char buf[AC_LINE_MAX], *ptr;
+ slap_cf_aux_table *tab;
+ struct berval tmp;
+
+ ptr = buf;
+ for ( tab = tab0; !BER_BVISNULL( &tab->key ); tab++ ) {
+ char **cptr;
+ int *iptr, i;
+ unsigned *uptr;
+ long *lptr;
+ unsigned long *ulptr;
+ struct berval *bptr;
+
+ cptr = (char **)( (char *)src + tab->off );
+
+ switch ( tab->type ) {
+ case 'b':
+ bptr = (struct berval *)( (char *)src + tab->off );
+ cptr = &bptr->bv_val;
+
+ case 's':
+ if ( *cptr ) {
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, tab->key.bv_val );
+ if ( tab->quote ) *ptr++ = '"';
+ ptr = lutil_strcopy( ptr, *cptr );
+ if ( tab->quote ) *ptr++ = '"';
+ }
+ break;
+
+ case 'i':
+ iptr = (int *)( (char *)src + tab->off );
+
+ if ( tab->aux != NULL ) {
+ slap_verbmasks *aux = (slap_verbmasks *)tab->aux;
+
+ for ( i = 0; !BER_BVISNULL( &aux[i].word ); i++ ) {
+ if ( *iptr == aux[i].mask ) {
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, tab->key.bv_val );
+ ptr = lutil_strcopy( ptr, aux[i].word.bv_val );
+ break;
+ }
+ }
+
+ } else {
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, tab->key.bv_val );
+ ptr += snprintf( ptr, sizeof(buf) - ( ptr - buf ), "%d",
+ *iptr );
+ }
+ break;
+
+ case 'u':
+ uptr = (unsigned *)( (char *)src + tab->off );
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, tab->key.bv_val );
+ ptr += snprintf( ptr, sizeof(buf) - ( ptr - buf ), "%u",
+ *uptr );
+ break;
+
+ case 'I':
+ lptr = (long *)( (char *)src + tab->off );
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, tab->key.bv_val );
+ ptr += snprintf( ptr, sizeof(buf) - ( ptr - buf ), "%ld",
+ *lptr );
+ break;
+
+ case 'U':
+ ulptr = (unsigned long *)( (char *)src + tab->off );
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, tab->key.bv_val );
+ ptr += snprintf( ptr, sizeof(buf) - ( ptr - buf ), "%lu",
+ *ulptr );
+ break;
+
+ case 'x': {
+ char *saveptr = ptr;
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, tab->key.bv_val );
+ if ( tab->quote ) *ptr++ = '"';
+ if ( tab->aux != NULL ) {
+ struct berval value;
+ lload_cf_aux_table_parse_x *func =
+ (lload_cf_aux_table_parse_x *)tab->aux;
+ int rc;
+
+ value.bv_val = ptr;
+ value.bv_len = buf + sizeof(buf) - ptr;
+
+ rc = func( &value, (void *)( (char *)src + tab->off ), tab,
+ "(unparse)", 1 );
+ if ( rc == 0 ) {
+ if ( value.bv_len ) {
+ ptr += value.bv_len;
+ } else {
+ ptr = saveptr;
+ break;
+ }
+ }
+ }
+ if ( tab->quote ) *ptr++ = '"';
+ } break;
+
+ default:
+ assert(0);
+ }
+ }
+ tmp.bv_val = buf;
+ tmp.bv_len = ptr - buf;
+ ber_dupbv( bv, &tmp );
+ return 0;
+}
+
+int
+lload_tls_get_config( LDAP *ld, int opt, char **val )
+{
+#ifdef HAVE_TLS
+ slap_verbmasks *keys;
+ int i, ival;
+
+ *val = NULL;
+ switch ( opt ) {
+ case LDAP_OPT_X_TLS_CRLCHECK:
+ keys = crlkeys;
+ break;
+ case LDAP_OPT_X_TLS_REQUIRE_CERT:
+ keys = vfykeys;
+ break;
+ case LDAP_OPT_X_TLS_PROTOCOL_MIN: {
+ char buf[8];
+ ldap_pvt_tls_get_option( ld, opt, &ival );
+ snprintf( buf, sizeof(buf), "%d.%d",
+ ( ival >> 8 ) & 0xff, ival & 0xff );
+ *val = ch_strdup( buf );
+ return 0;
+ }
+ default:
+ return -1;
+ }
+ ldap_pvt_tls_get_option( ld, opt, &ival );
+ for ( i = 0; !BER_BVISNULL( &keys[i].word ); i++ ) {
+ if ( keys[i].mask == ival ) {
+ *val = ch_strdup( keys[i].word.bv_val );
+ return 0;
+ }
+ }
+#endif
+ return -1;
+}
+
+#ifdef HAVE_TLS
+static struct {
+ const char *key;
+ size_t offset;
+ int opt;
+} bindtlsopts[] = {
+ { "tls_cert", offsetof(slap_bindconf, sb_tls_cert), LDAP_OPT_X_TLS_CERTFILE },
+ { "tls_key", offsetof(slap_bindconf, sb_tls_key), LDAP_OPT_X_TLS_KEYFILE },
+ { "tls_cacert", offsetof(slap_bindconf, sb_tls_cacert), LDAP_OPT_X_TLS_CACERTFILE },
+ { "tls_cacertdir", offsetof(slap_bindconf, sb_tls_cacertdir), LDAP_OPT_X_TLS_CACERTDIR },
+ { "tls_cipher_suite", offsetof(slap_bindconf, sb_tls_cipher_suite), LDAP_OPT_X_TLS_CIPHER_SUITE },
+ { "tls_ecname", offsetof(slap_bindconf, sb_tls_ecname), LDAP_OPT_X_TLS_ECNAME },
+ { NULL, 0 }
+};
+
+int
+lload_bindconf_tls_set( slap_bindconf *bc, LDAP *ld )
+{
+ int i, rc, newctx = 0, res = 0;
+ char *ptr = (char *)bc, **word;
+
+ if ( bc->sb_tls_do_init ) {
+ for ( i = 0; bindtlsopts[i].opt; i++ ) {
+ word = (char **)( ptr + bindtlsopts[i].offset );
+ if ( *word ) {
+ rc = ldap_set_option( ld, bindtlsopts[i].opt, *word );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY, "lload_bindconf_tls_set: "
+ "failed to set %s to %s\n",
+ bindtlsopts[i].key, *word );
+ res = -1;
+ } else
+ newctx = 1;
+ }
+ }
+ if ( bc->sb_tls_reqcert ) {
+ rc = ldap_pvt_tls_config(
+ ld, LDAP_OPT_X_TLS_REQUIRE_CERT, bc->sb_tls_reqcert );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY, "lload_bindconf_tls_set: "
+ "failed to set tls_reqcert to %s\n",
+ bc->sb_tls_reqcert );
+ res = -1;
+ } else {
+ newctx = 1;
+ /* retrieve the parsed setting for later use */
+ ldap_get_option( ld, LDAP_OPT_X_TLS_REQUIRE_CERT,
+ &bc->sb_tls_int_reqcert );
+ }
+ }
+ if ( bc->sb_tls_reqsan ) {
+ rc = ldap_pvt_tls_config(
+ ld, LDAP_OPT_X_TLS_REQUIRE_SAN, bc->sb_tls_reqsan );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY, "lload_bindconf_tls_set: "
+ "failed to set tls_reqsan to %s\n",
+ bc->sb_tls_reqsan );
+ res = -1;
+ } else {
+ newctx = 1;
+ /* retrieve the parsed setting for later use */
+ ldap_get_option( ld, LDAP_OPT_X_TLS_REQUIRE_SAN,
+ &bc->sb_tls_int_reqsan );
+ }
+ }
+ if ( bc->sb_tls_protocol_min ) {
+ rc = ldap_pvt_tls_config(
+ ld, LDAP_OPT_X_TLS_PROTOCOL_MIN, bc->sb_tls_protocol_min );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY, "lload_bindconf_tls_set: "
+ "failed to set tls_protocol_min to %s\n",
+ bc->sb_tls_protocol_min );
+ res = -1;
+ } else
+ newctx = 1;
+ }
+#ifdef HAVE_OPENSSL
+ if ( bc->sb_tls_crlcheck ) {
+ rc = ldap_pvt_tls_config(
+ ld, LDAP_OPT_X_TLS_CRLCHECK, bc->sb_tls_crlcheck );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY, "lload_bindconf_tls_set: "
+ "failed to set tls_crlcheck to %s\n",
+ bc->sb_tls_crlcheck );
+ res = -1;
+ } else
+ newctx = 1;
+ }
+#endif
+ if ( !res ) bc->sb_tls_do_init = 0;
+ }
+
+ if ( newctx ) {
+ int opt = 0;
+
+ if ( bc->sb_tls_ctx ) {
+ ldap_pvt_tls_ctx_free( bc->sb_tls_ctx );
+ bc->sb_tls_ctx = NULL;
+ }
+ rc = ldap_set_option( ld, LDAP_OPT_X_TLS_NEWCTX, &opt );
+ if ( rc )
+ res = rc;
+ else
+ ldap_get_option( ld, LDAP_OPT_X_TLS_CTX, &bc->sb_tls_ctx );
+ } else if ( bc->sb_tls_ctx ) {
+ rc = ldap_set_option( ld, LDAP_OPT_X_TLS_CTX, bc->sb_tls_ctx );
+ if ( rc == LDAP_SUCCESS ) {
+ /* these options aren't actually inside the ctx, so have to be set again */
+ ldap_set_option(
+ ld, LDAP_OPT_X_TLS_REQUIRE_CERT, &bc->sb_tls_int_reqcert );
+ ldap_set_option(
+ ld, LDAP_OPT_X_TLS_REQUIRE_SAN, &bc->sb_tls_int_reqsan );
+ } else
+ res = rc;
+ }
+
+ return res;
+}
+#endif
+
+int
+lload_bindconf_tls_parse( const char *word, slap_bindconf *bc )
+{
+#ifdef HAVE_TLS
+ if ( lload_cf_aux_table_parse( word, bc, aux_TLS, "tls config" ) == 0 ) {
+ bc->sb_tls_do_init = 1;
+ return 0;
+ }
+#endif
+ return -1;
+}
+
+int
+lload_backend_parse( const char *word, LloadBackend *b )
+{
+ return lload_cf_aux_table_parse( word, b, backendkey, "backend config" );
+}
+
+int
+lload_bindconf_parse( const char *word, slap_bindconf *bc )
+{
+#ifdef HAVE_TLS
+ /* Detect TLS config changes explicitly */
+ if ( lload_bindconf_tls_parse( word, bc ) == 0 ) {
+ return 0;
+ }
+#endif
+ return lload_cf_aux_table_parse( word, bc, bindkey, "bind config" );
+}
+
+int
+lload_bindconf_unparse( slap_bindconf *bc, struct berval *bv )
+{
+ return lload_cf_aux_table_unparse( bc, bv, bindkey );
+}
+
+void
+lload_bindconf_free( slap_bindconf *bc )
+{
+ if ( !BER_BVISNULL( &bc->sb_uri ) ) {
+ ch_free( bc->sb_uri.bv_val );
+ BER_BVZERO( &bc->sb_uri );
+ }
+ if ( !BER_BVISNULL( &bc->sb_binddn ) ) {
+ ch_free( bc->sb_binddn.bv_val );
+ BER_BVZERO( &bc->sb_binddn );
+ }
+ if ( !BER_BVISNULL( &bc->sb_cred ) ) {
+ ch_free( bc->sb_cred.bv_val );
+ BER_BVZERO( &bc->sb_cred );
+ }
+ if ( !BER_BVISNULL( &bc->sb_saslmech ) ) {
+ ch_free( bc->sb_saslmech.bv_val );
+ BER_BVZERO( &bc->sb_saslmech );
+ }
+ if ( bc->sb_secprops ) {
+ ch_free( bc->sb_secprops );
+ bc->sb_secprops = NULL;
+ }
+ if ( !BER_BVISNULL( &bc->sb_realm ) ) {
+ ch_free( bc->sb_realm.bv_val );
+ BER_BVZERO( &bc->sb_realm );
+ }
+ if ( !BER_BVISNULL( &bc->sb_authcId ) ) {
+ ch_free( bc->sb_authcId.bv_val );
+ BER_BVZERO( &bc->sb_authcId );
+ }
+ if ( !BER_BVISNULL( &bc->sb_authzId ) ) {
+ ch_free( bc->sb_authzId.bv_val );
+ BER_BVZERO( &bc->sb_authzId );
+ }
+#ifdef HAVE_TLS
+ if ( bc->sb_tls_cert ) {
+ ch_free( bc->sb_tls_cert );
+ bc->sb_tls_cert = NULL;
+ }
+ if ( bc->sb_tls_key ) {
+ ch_free( bc->sb_tls_key );
+ bc->sb_tls_key = NULL;
+ }
+ if ( bc->sb_tls_cacert ) {
+ ch_free( bc->sb_tls_cacert );
+ bc->sb_tls_cacert = NULL;
+ }
+ if ( bc->sb_tls_cacertdir ) {
+ ch_free( bc->sb_tls_cacertdir );
+ bc->sb_tls_cacertdir = NULL;
+ }
+ if ( bc->sb_tls_reqcert ) {
+ ch_free( bc->sb_tls_reqcert );
+ bc->sb_tls_reqcert = NULL;
+ }
+ if ( bc->sb_tls_cipher_suite ) {
+ ch_free( bc->sb_tls_cipher_suite );
+ bc->sb_tls_cipher_suite = NULL;
+ }
+ if ( bc->sb_tls_protocol_min ) {
+ ch_free( bc->sb_tls_protocol_min );
+ bc->sb_tls_protocol_min = NULL;
+ }
+#ifdef HAVE_OPENSSL_CRL
+ if ( bc->sb_tls_crlcheck ) {
+ ch_free( bc->sb_tls_crlcheck );
+ bc->sb_tls_crlcheck = NULL;
+ }
+#endif
+ if ( bc->sb_tls_ctx ) {
+ ldap_pvt_tls_ctx_free( bc->sb_tls_ctx );
+ bc->sb_tls_ctx = NULL;
+ }
+#endif
+}
+
+void
+lload_bindconf_tls_defaults( slap_bindconf *bc )
+{
+#ifdef HAVE_TLS
+ if ( bc->sb_tls_do_init ) {
+ if ( !bc->sb_tls_cacert )
+ ldap_pvt_tls_get_option( lload_tls_ld, LDAP_OPT_X_TLS_CACERTFILE,
+ &bc->sb_tls_cacert );
+ if ( !bc->sb_tls_cacertdir )
+ ldap_pvt_tls_get_option( lload_tls_ld, LDAP_OPT_X_TLS_CACERTDIR,
+ &bc->sb_tls_cacertdir );
+ if ( !bc->sb_tls_cert )
+ ldap_pvt_tls_get_option(
+ lload_tls_ld, LDAP_OPT_X_TLS_CERTFILE, &bc->sb_tls_cert );
+ if ( !bc->sb_tls_key )
+ ldap_pvt_tls_get_option(
+ lload_tls_ld, LDAP_OPT_X_TLS_KEYFILE, &bc->sb_tls_key );
+ if ( !bc->sb_tls_cipher_suite )
+ ldap_pvt_tls_get_option( lload_tls_ld, LDAP_OPT_X_TLS_CIPHER_SUITE,
+ &bc->sb_tls_cipher_suite );
+ if ( !bc->sb_tls_reqcert ) bc->sb_tls_reqcert = ch_strdup( "demand" );
+#ifdef HAVE_OPENSSL_CRL
+ if ( !bc->sb_tls_crlcheck )
+ lload_tls_get_config( lload_tls_ld, LDAP_OPT_X_TLS_CRLCHECK,
+ &bc->sb_tls_crlcheck );
+#endif
+ }
+#endif
+}
+
+/* -------------------------------------- */
+
+static char *
+strtok_quote( char *line, char *sep, char **quote_ptr, int *iqp )
+{
+ int inquote;
+ char *tmp;
+ static char *next;
+
+ *quote_ptr = NULL;
+ if ( line != NULL ) {
+ next = line;
+ }
+ while ( *next && strchr( sep, *next ) ) {
+ next++;
+ }
+
+ if ( *next == '\0' ) {
+ next = NULL;
+ return NULL;
+ }
+ tmp = next;
+
+ for ( inquote = 0; *next; ) {
+ switch ( *next ) {
+ case '"':
+ if ( inquote ) {
+ inquote = 0;
+ } else {
+ inquote = 1;
+ }
+ AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
+ break;
+
+ case '\\':
+ if ( next[1] )
+ AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
+ next++; /* dont parse the escaped character */
+ break;
+
+ default:
+ if ( !inquote ) {
+ if ( strchr( sep, *next ) != NULL ) {
+ *quote_ptr = next;
+ *next++ = '\0';
+ return tmp;
+ }
+ }
+ next++;
+ break;
+ }
+ }
+ *iqp = inquote;
+
+ return tmp;
+}
+
+static char buf[AC_LINE_MAX];
+static char *line;
+static size_t lmax, lcur;
+
+#define CATLINE( buf ) \
+ do { \
+ size_t len = strlen( buf ); \
+ while ( lcur + len + 1 > lmax ) { \
+ lmax += AC_LINE_MAX; \
+ line = (char *)ch_realloc( line, lmax ); \
+ } \
+ strcpy( line + lcur, buf ); \
+ lcur += len; \
+ } while (0)
+
+static void
+fp_getline_init( ConfigArgs *c )
+{
+ c->lineno = -1;
+ buf[0] = '\0';
+}
+
+static int
+fp_getline( FILE *fp, ConfigArgs *c )
+{
+ char *p;
+
+ lcur = 0;
+ CATLINE( buf );
+ c->lineno++;
+
+ /* avoid stack of bufs */
+ if ( strncasecmp( line, "include", STRLENOF("include") ) == 0 ) {
+ buf[0] = '\0';
+ c->line = line;
+ return 1;
+ }
+
+ while ( fgets( buf, sizeof(buf), fp ) ) {
+ p = strchr( buf, '\n' );
+ if ( p ) {
+ if ( p > buf && p[-1] == '\r' ) {
+ --p;
+ }
+ *p = '\0';
+ }
+ /* XXX ugly */
+ c->line = line;
+ if ( line[0] && ( p = line + strlen( line ) - 1 )[0] == '\\' &&
+ p[-1] != '\\' ) {
+ p[0] = '\0';
+ lcur--;
+
+ } else {
+ if ( !isspace( (unsigned char)buf[0] ) ) {
+ return 1;
+ }
+ buf[0] = ' ';
+ }
+ CATLINE( buf );
+ c->lineno++;
+ }
+
+ buf[0] = '\0';
+ c->line = line;
+ return ( line[0] ? 1 : 0 );
+}
+
+int
+lload_config_fp_parse_line( ConfigArgs *c )
+{
+ char *token;
+ static char *const hide[] = { "bindconf", NULL };
+ static char *const raw[] = { NULL };
+ char *quote_ptr;
+ int i = (int)( sizeof(hide) / sizeof(hide[0]) ) - 1;
+ int inquote = 0;
+
+ c->tline = ch_strdup( c->line );
+ c->linelen = strlen( c->line );
+ token = strtok_quote( c->tline, " \t", &quote_ptr, &inquote );
+
+ if ( token )
+ for ( i = 0; hide[i]; i++ )
+ if ( !strcasecmp( token, hide[i] ) ) break;
+ if ( quote_ptr ) *quote_ptr = ' ';
+ Debug( LDAP_DEBUG_CONFIG, "%s (%s%s)\n",
+ c->log, hide[i] ? hide[i] : c->line, hide[i] ? " ***" : "" );
+ if ( quote_ptr ) *quote_ptr = '\0';
+
+ for ( ;; token = strtok_quote( NULL, " \t", &quote_ptr, &inquote ) ) {
+ if ( c->argc >= c->argv_size ) {
+ char **tmp;
+ tmp = ch_realloc( c->argv,
+ ( c->argv_size + ARGS_STEP ) * sizeof(*c->argv) );
+ if ( !tmp ) {
+ Debug( LDAP_DEBUG_ANY, "%s: out of memory\n", c->log );
+ return -1;
+ }
+ c->argv = tmp;
+ c->argv_size += ARGS_STEP;
+ }
+ if ( token == NULL ) break;
+ c->argv[c->argc++] = token;
+ }
+ c->argv[c->argc] = NULL;
+ if ( inquote ) {
+ /* these directives parse c->line independently of argv tokenizing */
+ for ( i = 0; raw[i]; i++ )
+ if ( !strcasecmp( c->argv[0], raw[i] ) ) return 0;
+
+ Debug( LDAP_DEBUG_ANY, "%s: unterminated quoted string \"%s\"\n",
+ c->log, c->argv[c->argc - 1] );
+ return -1;
+ }
+ return 0;
+}
+
+void
+lload_config_destroy( void )
+{
+ free( line );
+ if ( slapd_args_file ) free( slapd_args_file );
+ if ( slapd_pid_file ) free( slapd_pid_file );
+ loglevel_destroy();
+}
+
+/* See if the given URL (in plain and parsed form) matches
+ * any of the server's listener addresses. Return matching
+ * LloadListener or NULL for no match.
+ */
+LloadListener *
+lload_config_check_my_url( const char *url, LDAPURLDesc *lud )
+{
+ LloadListener **l = lloadd_get_listeners();
+ int i, isMe;
+
+ /* Try a straight compare with LloadListener strings */
+ for ( i = 0; l && l[i]; i++ ) {
+ if ( !strcasecmp( url, l[i]->sl_url.bv_val ) ) {
+ return l[i];
+ }
+ }
+
+ isMe = 0;
+ /* If hostname is empty, or is localhost, or matches
+ * our hostname, this url refers to this host.
+ * Compare it against listeners and ports.
+ */
+ if ( !lud->lud_host || !lud->lud_host[0] ||
+ !strncasecmp(
+ "localhost", lud->lud_host, STRLENOF("localhost") ) ||
+ !strcasecmp( global_host, lud->lud_host ) ) {
+ for ( i = 0; l && l[i]; i++ ) {
+ LDAPURLDesc *lu2;
+ ldap_url_parse_ext(
+ l[i]->sl_url.bv_val, &lu2, LDAP_PVT_URL_PARSE_DEF_PORT );
+ do {
+ if ( strcasecmp( lud->lud_scheme, lu2->lud_scheme ) ) break;
+ if ( lud->lud_port != lu2->lud_port ) break;
+ /* Listener on ANY address */
+ if ( !lu2->lud_host || !lu2->lud_host[0] ) {
+ isMe = 1;
+ break;
+ }
+ /* URL on ANY address */
+ if ( !lud->lud_host || !lud->lud_host[0] ) {
+ isMe = 1;
+ break;
+ }
+ /* Listener has specific host, must
+ * match it
+ */
+ if ( !strcasecmp( lud->lud_host, lu2->lud_host ) ) {
+ isMe = 1;
+ break;
+ }
+ } while (0);
+ ldap_free_urldesc( lu2 );
+ if ( isMe ) {
+ return l[i];
+ }
+ }
+ }
+ return NULL;
+}
+
+#ifdef BALANCER_MODULE
+static int
+backend_cf_gen( ConfigArgs *c )
+{
+ LloadBackend *b = c->ca_private;
+ enum lcf_backend flag = 0;
+ int rc = LDAP_SUCCESS;
+
+ assert( b != NULL );
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ switch ( c->type ) {
+ case CFG_URI:
+ c->value_bv = b->b_uri;
+ break;
+ case CFG_NUMCONNS:
+ c->value_uint = b->b_numconns;
+ break;
+ case CFG_BINDCONNS:
+ c->value_uint = b->b_numbindconns;
+ break;
+ case CFG_RETRY:
+ c->value_uint = b->b_retry_timeout;
+ break;
+ case CFG_MAX_PENDING_CONNS:
+ c->value_uint = b->b_max_conn_pending;
+ break;
+ case CFG_MAX_PENDING_OPS:
+ c->value_uint = b->b_max_pending;
+ break;
+ case CFG_STARTTLS:
+ enum_to_verb( tlskey, b->b_tls_conf, &c->value_bv );
+ break;
+ default:
+ rc = 1;
+ break;
+ }
+
+ return rc;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ /* We only need to worry about deletions to multi-value or MAY
+ * attributes */
+ switch ( c->type ) {
+ case CFG_STARTTLS:
+ b->b_tls_conf = LLOAD_CLEARTEXT;
+ break;
+ default:
+ break;
+ }
+ return rc;
+ }
+
+ switch ( c->type ) {
+ case CFG_URI:
+ rc = backend_config_url( b, &c->value_bv );
+ if ( rc ) {
+ backend_config_url( b, &b->b_uri );
+ goto fail;
+ }
+ if ( !BER_BVISNULL( &b->b_uri ) ) {
+ ch_free( b->b_uri.bv_val );
+ }
+ b->b_uri = c->value_bv;
+ flag = LLOAD_BACKEND_MOD_OTHER;
+ break;
+ case CFG_NUMCONNS:
+ if ( !c->value_uint ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "invalid connection pool configuration" );
+ goto fail;
+ }
+ b->b_numconns = c->value_uint;
+ flag = LLOAD_BACKEND_MOD_CONNS;
+ break;
+ case CFG_BINDCONNS:
+ if ( !c->value_uint ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "invalid connection pool configuration" );
+ goto fail;
+ }
+ b->b_numbindconns = c->value_uint;
+ flag = LLOAD_BACKEND_MOD_CONNS;
+ break;
+ case CFG_RETRY:
+ b->b_retry_timeout = c->value_uint;
+ break;
+ case CFG_MAX_PENDING_CONNS:
+ b->b_max_conn_pending = c->value_uint;
+ break;
+ case CFG_MAX_PENDING_OPS:
+ b->b_max_pending = c->value_uint;
+ break;
+ case CFG_STARTTLS: {
+ int i = bverb_to_mask( &c->value_bv, tlskey );
+ if ( BER_BVISNULL( &tlskey[i].word ) ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "invalid starttls configuration" );
+ goto fail;
+ }
+#ifndef HAVE_TLS
+ if ( tlskey[i].mask == LLOAD_STARTTLS_OPTIONAL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "lloadd compiled without TLS but starttls specified, "
+ "it will be ignored\n",
+ c->log );
+ } else if ( tlskey[i].mask != LLOAD_CLEARTEXT ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "invalid starttls configuration when compiled without "
+ "TLS support" );
+ goto fail;
+ }
+#endif /* ! HAVE_TLS */
+ b->b_tls_conf = tlskey[i].mask;
+ } break;
+ default:
+ rc = 1;
+ break;
+ }
+
+ /* do not set this if it has already been set by another callback, e.g.
+ * lload_backend_ldadd */
+ if ( lload_change.type == LLOAD_CHANGE_UNDEFINED ) {
+ lload_change.type = LLOAD_CHANGE_MODIFY;
+ }
+ lload_change.object = LLOAD_BACKEND;
+ lload_change.target = b;
+ lload_change.flags.backend |= flag;
+
+ config_push_cleanup( c, lload_backend_finish );
+ return rc;
+
+fail:
+ if ( lload_change.type == LLOAD_CHANGE_ADD ) {
+ /* Abort the ADD */
+ lload_change.type = LLOAD_CHANGE_DEL;
+ }
+
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
+ return 1;
+}
+
+int
+lload_back_init_cf( BackendInfo *bi )
+{
+ /* Make sure we don't exceed the bits reserved for userland */
+ config_check_userland( CFG_LAST );
+
+ bi->bi_cf_ocs = lloadocs;
+
+ return config_register_schema( config_back_cf_table, lloadocs );
+}
+
+static int
+lload_backend_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
+{
+ LloadBackend *b;
+ Attribute *a;
+ AttributeDescription *ad = NULL;
+ struct berval bv, type, rdn;
+ const char *text;
+ char *name;
+
+ Debug( LDAP_DEBUG_TRACE, "lload_backend_ldadd: "
+ "a new backend-server is being added\n" );
+
+ if ( p->ce_type != Cft_Backend || !p->ce_bi ||
+ p->ce_bi->bi_cf_ocs != lloadocs )
+ return LDAP_CONSTRAINT_VIOLATION;
+
+ dnRdn( &e->e_name, &rdn );
+ type.bv_len = strchr( rdn.bv_val, '=' ) - rdn.bv_val;
+ type.bv_val = rdn.bv_val;
+
+ /* Find attr */
+ slap_bv2ad( &type, &ad, &text );
+ if ( ad != slap_schema.si_ad_cn ) return LDAP_NAMING_VIOLATION;
+
+ a = attr_find( e->e_attrs, ad );
+ if ( !a || a->a_numvals != 1 ) return LDAP_NAMING_VIOLATION;
+ bv = a->a_vals[0];
+
+ if ( bv.bv_val[0] == '{' && ( name = strchr( bv.bv_val, '}' ) ) ) {
+ name++;
+ bv.bv_len -= name - bv.bv_val;
+ bv.bv_val = name;
+ }
+
+ b = backend_alloc();
+ ber_dupbv( &b->b_name, &bv );
+
+ ca->bi = p->ce_bi;
+ ca->ca_private = b;
+ config_push_cleanup( ca, lload_backend_finish );
+
+ /* ca cleanups are only run in the case of online config but we use it to
+ * save the new config when done with the entry */
+ ca->lineno = 0;
+
+ lload_change.type = LLOAD_CHANGE_ADD;
+ lload_change.object = LLOAD_BACKEND;
+ lload_change.target = b;
+
+ return LDAP_SUCCESS;
+}
+
+#ifdef SLAP_CONFIG_DELETE
+static int
+lload_backend_lddel( CfEntryInfo *ce, Operation *op )
+{
+ LloadBackend *b = ce->ce_private;
+
+ lload_change.type = LLOAD_CHANGE_DEL;
+ lload_change.object = LLOAD_BACKEND;
+ lload_change.target = b;
+
+ return LDAP_SUCCESS;
+}
+#endif /* SLAP_CONFIG_DELETE */
+
+static int
+lload_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *c )
+{
+ struct berval bv;
+ LloadBackend *b;
+ int i = 0;
+
+ bv.bv_val = c->cr_msg;
+ LDAP_CIRCLEQ_FOREACH ( b, &backend, b_next ) {
+ char buf[STRLENOF( "server 4294967295" ) + 1] = { 0 };
+
+ bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "cn=" SLAP_X_ORDERED_FMT "server %d", i, i + 1 );
+
+ snprintf( buf, sizeof(buf), "server %d", i + 1 );
+ ber_str2bv( buf, 0, 1, &b->b_name );
+
+ c->ca_private = b;
+ c->valx = i;
+
+ config_build_entry( op, rs, p->e_private, c, &bv, &lloadocs[1], NULL );
+
+ i++;
+ }
+ return LDAP_SUCCESS;
+}
+#endif /* BALANCER_MODULE */
diff --git a/servers/lloadd/connection.c b/servers/lloadd/connection.c
new file mode 100644
index 0000000..967c6c5
--- /dev/null
+++ b/servers/lloadd/connection.c
@@ -0,0 +1,620 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include <ac/socket.h>
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include "lload.h"
+
+#include "lutil.h"
+#include "lutil_ldap.h"
+
+static unsigned long conn_nextid = 0;
+
+static void
+lload_connection_assign_nextid( LloadConnection *conn )
+{
+ conn->c_connid = __atomic_fetch_add( &conn_nextid, 1, __ATOMIC_RELAXED );
+}
+
+/*
+ * We start off with the connection muted and c_currentber holding the pdu we
+ * received.
+ *
+ * We run c->c_pdu_cb for each pdu, stopping once we hit an error, have to wait
+ * on reading or after we process lload_conn_max_pdus_per_cycle pdus so as to
+ * maintain fairness and not hog the worker thread forever.
+ *
+ * If we've run out of pdus immediately available from the stream or hit the
+ * budget, we unmute the connection.
+ *
+ * c->c_pdu_cb might return an 'error' and not free the connection. That can
+ * happen when changing the state or when client is blocked on writing and
+ * already has a pdu pending on the same operation, it's their job to make sure
+ * we're woken up again.
+ */
+void *
+handle_pdus( void *ctx, void *arg )
+{
+ LloadConnection *c = arg;
+ int pdus_handled = 0;
+ epoch_t epoch;
+
+ /* A reference was passed on to us */
+ assert( IS_ALIVE( c, c_refcnt ) );
+
+ epoch = epoch_join();
+ for ( ;; ) {
+ BerElement *ber;
+ ber_tag_t tag;
+ ber_len_t len;
+
+ if ( c->c_pdu_cb( c ) ) {
+ /* Error/reset, get rid ouf our reference and bail */
+ goto done;
+ }
+
+ if ( !IS_ALIVE( c, c_live ) ) {
+ break;
+ }
+
+ if ( ++pdus_handled >= lload_conn_max_pdus_per_cycle ) {
+ /* Do not read now, re-enable read event instead */
+ break;
+ }
+
+ ber = c->c_currentber;
+ if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "handle_pdus: "
+ "connid=%lu, ber_alloc failed\n",
+ c->c_connid );
+ CONNECTION_LOCK_DESTROY(c);
+ goto done;
+ }
+ c->c_currentber = ber;
+
+ checked_lock( &c->c_io_mutex );
+ if ( (lload_features & LLOAD_FEATURE_PAUSE) &&
+ (c->c_io_state & LLOAD_C_READ_PAUSE) ) {
+ goto pause;
+ }
+ tag = ber_get_next( c->c_sb, &len, ber );
+ checked_unlock( &c->c_io_mutex );
+ if ( tag != LDAP_TAG_MESSAGE ) {
+ int err = sock_errno();
+
+ if ( err != EWOULDBLOCK && err != EAGAIN ) {
+ if ( err || tag == LBER_ERROR ) {
+ char ebuf[128];
+ Debug( LDAP_DEBUG_ANY, "handle_pdus: "
+ "ber_get_next on fd=%d failed errno=%d (%s)\n",
+ c->c_fd, err,
+ sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ } else {
+ Debug( LDAP_DEBUG_STATS, "handle_pdus: "
+ "ber_get_next on fd=%d connid=%lu received "
+ "a strange PDU tag=%lx\n",
+ c->c_fd, c->c_connid, tag );
+ }
+
+ c->c_currentber = NULL;
+ ber_free( ber, 1 );
+ CONNECTION_LOCK_DESTROY(c);
+ goto done;
+ }
+ break;
+ }
+
+ assert( IS_ALIVE( c, c_refcnt ) );
+ epoch_leave( epoch );
+ epoch = epoch_join();
+ assert( IS_ALIVE( c, c_refcnt ) );
+ }
+
+ checked_lock( &c->c_io_mutex );
+ if ( !(lload_features & LLOAD_FEATURE_PAUSE) ||
+ !(c->c_io_state & LLOAD_C_READ_PAUSE) ) {
+ event_add( c->c_read_event, c->c_read_timeout );
+ Debug( LDAP_DEBUG_CONNS, "handle_pdus: "
+ "re-enabled read event on connid=%lu\n",
+ c->c_connid );
+ }
+pause:
+ c->c_io_state &= ~LLOAD_C_READ_HANDOVER;
+ checked_unlock( &c->c_io_mutex );
+
+done:
+ RELEASE_REF( c, c_refcnt, c->c_destroy );
+ epoch_leave( epoch );
+ return NULL;
+}
+
+/*
+ * Initial read on the connection, if we get an LDAP PDU, submit the
+ * processing of this and successive ones to the work queue.
+ *
+ * If we can't submit it to the queue (overload), process this one and return
+ * to the event loop immediately after.
+ */
+void
+connection_read_cb( evutil_socket_t s, short what, void *arg )
+{
+ LloadConnection *c = arg;
+ BerElement *ber;
+ ber_tag_t tag;
+ ber_len_t len;
+ epoch_t epoch;
+ int pause;
+
+ if ( !IS_ALIVE( c, c_live ) ) {
+ event_del( c->c_read_event );
+ Debug( LDAP_DEBUG_CONNS, "connection_read_cb: "
+ "suspended read event on a dead connid=%lu\n",
+ c->c_connid );
+ return;
+ }
+
+ if ( what & EV_TIMEOUT ) {
+ Debug( LDAP_DEBUG_CONNS, "connection_read_cb: "
+ "connid=%lu, timeout reached, destroying\n",
+ c->c_connid );
+ /* Make sure the connection stays around for us to unlock it */
+ epoch = epoch_join();
+ CONNECTION_LOCK_DESTROY(c);
+ epoch_leave( epoch );
+ return;
+ }
+
+ if ( !acquire_ref( &c->c_refcnt ) ) {
+ return;
+ }
+ epoch = epoch_join();
+
+ Debug( LDAP_DEBUG_CONNS, "connection_read_cb: "
+ "connection connid=%lu ready to read\n",
+ c->c_connid );
+
+ ber = c->c_currentber;
+ if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "connection_read_cb: "
+ "connid=%lu, ber_alloc failed\n",
+ c->c_connid );
+ goto out;
+ }
+ c->c_currentber = ber;
+
+ checked_lock( &c->c_io_mutex );
+ assert( !(c->c_io_state & LLOAD_C_READ_HANDOVER) );
+ tag = ber_get_next( c->c_sb, &len, ber );
+ pause = c->c_io_state & LLOAD_C_READ_PAUSE;
+ checked_unlock( &c->c_io_mutex );
+
+ if ( tag != LDAP_TAG_MESSAGE ) {
+ int err = sock_errno();
+
+ if ( err != EWOULDBLOCK && err != EAGAIN ) {
+ if ( err || tag == LBER_ERROR ) {
+ char ebuf[128];
+ Debug( LDAP_DEBUG_STATS, "connection_read_cb: "
+ "ber_get_next on fd=%d failed errno=%d (%s)\n",
+ c->c_fd, err,
+ sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ } else {
+ Debug( LDAP_DEBUG_STATS, "connection_read_cb: "
+ "ber_get_next on fd=%d connid=%lu received "
+ "a strange PDU tag=%lx\n",
+ c->c_fd, c->c_connid, tag );
+ }
+
+ c->c_currentber = NULL;
+ ber_free( ber, 1 );
+
+ event_del( c->c_read_event );
+ Debug( LDAP_DEBUG_CONNS, "connection_read_cb: "
+ "suspended read event on dying connid=%lu\n",
+ c->c_connid );
+ CONNECTION_LOCK_DESTROY(c);
+ goto out;
+ }
+ if ( !(lload_features & LLOAD_FEATURE_PAUSE) || !pause ) {
+ event_add( c->c_read_event, c->c_read_timeout );
+ Debug( LDAP_DEBUG_CONNS, "connection_read_cb: "
+ "re-enabled read event on connid=%lu\n",
+ c->c_connid );
+ }
+ goto out;
+ }
+
+ checked_lock( &c->c_io_mutex );
+ c->c_io_state |= LLOAD_C_READ_HANDOVER;
+ checked_unlock( &c->c_io_mutex );
+ event_del( c->c_read_event );
+
+ if ( !lload_conn_max_pdus_per_cycle ||
+ ldap_pvt_thread_pool_submit( &connection_pool, handle_pdus, c ) ) {
+ /* If we're overloaded or configured as such, process one and resume in
+ * the next cycle. */
+ int rc = c->c_pdu_cb( c );
+
+ checked_lock( &c->c_io_mutex );
+ c->c_io_state &= ~LLOAD_C_READ_HANDOVER;
+ if ( rc == LDAP_SUCCESS &&
+ ( !(lload_features & LLOAD_FEATURE_PAUSE) ||
+ !(c->c_io_state & LLOAD_C_READ_PAUSE) ) ) {
+ event_add( c->c_read_event, c->c_read_timeout );
+ }
+ checked_unlock( &c->c_io_mutex );
+ goto out;
+ }
+
+ Debug( LDAP_DEBUG_CONNS, "connection_read_cb: "
+ "suspended read event on connid=%lu\n",
+ c->c_connid );
+
+ /*
+ * We have scheduled a call to handle_pdus to take care of handling this
+ * and further requests, its reference is now owned by that task.
+ */
+ epoch_leave( epoch );
+ return;
+
+out:
+ RELEASE_REF( c, c_refcnt, c->c_destroy );
+ epoch_leave( epoch );
+}
+
+void
+connection_write_cb( evutil_socket_t s, short what, void *arg )
+{
+ LloadConnection *c = arg;
+ epoch_t epoch;
+
+ Debug( LDAP_DEBUG_CONNS, "connection_write_cb: "
+ "considering writing to%s connid=%lu what=%hd\n",
+ c->c_live ? " live" : " dead", c->c_connid, what );
+ if ( !IS_ALIVE( c, c_live ) ) {
+ return;
+ }
+
+ if ( what & EV_TIMEOUT ) {
+ Debug( LDAP_DEBUG_CONNS, "connection_write_cb: "
+ "connid=%lu, timeout reached, destroying\n",
+ c->c_connid );
+ /* Make sure the connection stays around for us to unlock it */
+ epoch = epoch_join();
+ CONNECTION_LOCK_DESTROY(c);
+ epoch_leave( epoch );
+ return;
+ }
+
+ /* Before we acquire any locks */
+ event_del( c->c_write_event );
+
+ if ( !acquire_ref( &c->c_refcnt ) ) {
+ return;
+ }
+
+ /* If what == 0, we have a caller as opposed to being a callback */
+ if ( what ) {
+ epoch = epoch_join();
+ }
+
+ checked_lock( &c->c_io_mutex );
+ Debug( LDAP_DEBUG_CONNS, "connection_write_cb: "
+ "have something to write to connection connid=%lu\n",
+ c->c_connid );
+
+ /* We might have been beaten to flushing the data by another thread */
+ if ( c->c_pendingber && ber_flush( c->c_sb, c->c_pendingber, 1 ) ) {
+ int err = sock_errno();
+
+ if ( err != EWOULDBLOCK && err != EAGAIN ) {
+ char ebuf[128];
+ checked_unlock( &c->c_io_mutex );
+ Debug( LDAP_DEBUG_ANY, "connection_write_cb: "
+ "ber_flush on fd=%d failed errno=%d (%s)\n",
+ c->c_fd, err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ CONNECTION_LOCK_DESTROY(c);
+ goto done;
+ }
+
+ if ( !(c->c_io_state & LLOAD_C_READ_PAUSE) ) {
+ Debug( LDAP_DEBUG_CONNS, "connection_write_cb: "
+ "connection connid=%lu blocked on writing, marking "
+ "paused\n",
+ c->c_connid );
+ }
+ c->c_io_state |= LLOAD_C_READ_PAUSE;
+
+ /* TODO: Do not reset write timeout unless we wrote something */
+ event_add( c->c_write_event, lload_write_timeout );
+ } else {
+ c->c_pendingber = NULL;
+ if ( c->c_io_state & LLOAD_C_READ_PAUSE ) {
+ c->c_io_state ^= LLOAD_C_READ_PAUSE;
+ Debug( LDAP_DEBUG_CONNS, "connection_write_cb: "
+ "Unpausing connection connid=%lu\n",
+ c->c_connid );
+ if ( !(c->c_io_state & LLOAD_C_READ_HANDOVER) ) {
+ event_add( c->c_read_event, c->c_read_timeout );
+ }
+ }
+ }
+ checked_unlock( &c->c_io_mutex );
+
+done:
+ RELEASE_REF( c, c_refcnt, c->c_destroy );
+ if ( what ) {
+ epoch_leave( epoch );
+ }
+}
+
+void
+connection_destroy( LloadConnection *c )
+{
+ assert( c );
+ Debug( LDAP_DEBUG_CONNS, "connection_destroy: "
+ "destroying connection connid=%lu\n",
+ c->c_connid );
+
+ CONNECTION_ASSERT_LOCKED(c);
+ assert( c->c_live == 0 );
+ assert( c->c_refcnt == 0 );
+ assert( c->c_state == LLOAD_C_INVALID );
+
+ ber_sockbuf_free( c->c_sb );
+
+ if ( c->c_currentber ) {
+ ber_free( c->c_currentber, 1 );
+ c->c_currentber = NULL;
+ }
+ if ( c->c_pendingber ) {
+ ber_free( c->c_pendingber, 1 );
+ c->c_pendingber = NULL;
+ }
+
+ if ( !BER_BVISNULL( &c->c_sasl_bind_mech ) ) {
+ ber_memfree( c->c_sasl_bind_mech.bv_val );
+ BER_BVZERO( &c->c_sasl_bind_mech );
+ }
+#ifdef HAVE_CYRUS_SASL
+ if ( c->c_sasl_defaults ) {
+ lutil_sasl_freedefs( c->c_sasl_defaults );
+ c->c_sasl_defaults = NULL;
+ }
+ if ( c->c_sasl_authctx ) {
+#ifdef SASL_CHANNEL_BINDING /* 2.1.25+ */
+ if ( c->c_sasl_cbinding ) {
+ ch_free( c->c_sasl_cbinding );
+ }
+#endif
+ sasl_dispose( &c->c_sasl_authctx );
+ }
+#endif /* HAVE_CYRUS_SASL */
+
+ CONNECTION_UNLOCK(c);
+
+ ldap_pvt_thread_mutex_destroy( &c->c_io_mutex );
+ ldap_pvt_thread_mutex_destroy( &c->c_mutex );
+
+ ch_free( c );
+
+ listeners_reactivate();
+}
+
+/*
+ * Called holding mutex, will walk cq calling cb on all connections whose
+ * c_connid <= cq_last->c_connid that still exist at the time we get to them.
+ */
+void
+connections_walk_last(
+ ldap_pvt_thread_mutex_t *cq_mutex,
+ lload_c_head *cq,
+ LloadConnection *cq_last,
+ CONNCB cb,
+ void *arg )
+{
+ LloadConnection *c = cq_last;
+ uintptr_t last_connid;
+
+ if ( LDAP_CIRCLEQ_EMPTY( cq ) ) {
+ return;
+ }
+ assert_locked( cq_mutex );
+
+ last_connid = c->c_connid;
+ c = LDAP_CIRCLEQ_LOOP_NEXT( cq, c, c_next );
+
+ while ( !acquire_ref( &c->c_refcnt ) ) {
+ c = LDAP_CIRCLEQ_LOOP_NEXT( cq, c, c_next );
+ if ( c->c_connid >= last_connid ) {
+ assert_locked( cq_mutex );
+ return;
+ }
+ }
+
+ /*
+ * Notes:
+ * - we maintain the connections in the cq CIRCLEQ_ in ascending c_connid
+ * order
+ * - the connection with the highest c_connid is passed in cq_last
+ * - we can only use cq when we hold cq_mutex
+ * - connections might be added to or removed from cq while we're busy
+ * processing connections
+ * - we need a way to detect we've finished looping around cq for some
+ * definition of looping around
+ */
+ do {
+ int rc;
+
+ checked_unlock( cq_mutex );
+
+ rc = cb( c, arg );
+ RELEASE_REF( c, c_refcnt, c->c_destroy );
+
+ checked_lock( cq_mutex );
+ if ( rc || LDAP_CIRCLEQ_EMPTY( cq ) ) {
+ break;
+ }
+
+ do {
+ LloadConnection *old = c;
+ c = LDAP_CIRCLEQ_LOOP_NEXT( cq, c, c_next );
+ if ( c->c_connid <= old->c_connid || c->c_connid > last_connid ) {
+ assert_locked( cq_mutex );
+ return;
+ }
+ } while ( !acquire_ref( &c->c_refcnt ) );
+ } while ( c->c_connid <= last_connid );
+ assert_locked( cq_mutex );
+}
+
+void
+connections_walk(
+ ldap_pvt_thread_mutex_t *cq_mutex,
+ lload_c_head *cq,
+ CONNCB cb,
+ void *arg )
+{
+ LloadConnection *cq_last = LDAP_CIRCLEQ_LAST( cq );
+ return connections_walk_last( cq_mutex, cq, cq_last, cb, arg );
+}
+
+int
+lload_connection_close( LloadConnection *c, void *arg )
+{
+ int gentle = *(int *)arg;
+ LloadOperation *op;
+
+ Debug( LDAP_DEBUG_CONNS, "lload_connection_close: "
+ "marking connection connid=%lu closing\n",
+ c->c_connid );
+
+ /* We were approached from the connection list */
+ assert( IS_ALIVE( c, c_refcnt ) );
+
+ CONNECTION_LOCK(c);
+ if ( !gentle || !c->c_ops ) {
+ CONNECTION_DESTROY(c);
+ return LDAP_SUCCESS;
+ }
+
+ /* The first thing we do is make sure we don't get new Operations in */
+ c->c_state = LLOAD_C_CLOSING;
+
+ do {
+ TAvlnode *node = ldap_tavl_end( c->c_ops, TAVL_DIR_LEFT );
+ op = node->avl_data;
+
+ /* Close operations that would need client action to resolve,
+ * only SASL binds in progress do that right now */
+ if ( op->o_client_msgid || op->o_upstream_msgid ) {
+ break;
+ }
+
+ CONNECTION_UNLOCK(c);
+ operation_unlink( op );
+ CONNECTION_LOCK(c);
+ } while ( c->c_ops );
+
+ CONNECTION_UNLOCK(c);
+ return LDAP_SUCCESS;
+}
+
+LloadConnection *
+lload_connection_init( ber_socket_t s, const char *peername, int flags )
+{
+ LloadConnection *c;
+
+ assert( peername != NULL );
+
+ if ( s == AC_SOCKET_INVALID ) {
+ Debug( LDAP_DEBUG_ANY, "lload_connection_init: "
+ "init of socket fd=%ld invalid\n",
+ (long)s );
+ return NULL;
+ }
+
+ assert( s >= 0 );
+
+ c = ch_calloc( 1, sizeof(LloadConnection) );
+
+ c->c_fd = s;
+ c->c_sb = ber_sockbuf_alloc();
+ ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_FD, &s );
+
+#ifdef LDAP_PF_LOCAL
+ if ( flags & CONN_IS_IPC ) {
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)"ipc_" );
+#endif
+ ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_fd,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)&s );
+ } else
+#endif /* LDAP_PF_LOCAL */
+ {
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)"tcp_" );
+#endif
+ ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_tcp,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)&s );
+ }
+
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io(
+ c->c_sb, &ber_sockbuf_io_debug, INT_MAX, (void *)"lload_" );
+#endif
+
+ c->c_next_msgid = 1;
+ c->c_refcnt = c->c_live = 1;
+ c->c_destroy = connection_destroy;
+
+ LDAP_CIRCLEQ_ENTRY_INIT( c, c_next );
+
+ ldap_pvt_thread_mutex_init( &c->c_mutex );
+ ldap_pvt_thread_mutex_init( &c->c_io_mutex );
+
+ lload_connection_assign_nextid( c );
+
+ Debug( LDAP_DEBUG_CONNS, "lload_connection_init: "
+ "connection connid=%lu allocated for socket fd=%d peername=%s\n",
+ c->c_connid, s, peername );
+
+ c->c_state = LLOAD_C_ACTIVE;
+
+ return c;
+}
diff --git a/servers/lloadd/daemon.c b/servers/lloadd/daemon.c
new file mode 100644
index 0000000..48bcf6a
--- /dev/null
+++ b/servers/lloadd/daemon.c
@@ -0,0 +1,1886 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2007 by Howard Chu, Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include <event2/event.h>
+#include <event2/dns.h>
+#include <event2/listener.h>
+
+#include "lload.h"
+#include "ldap_pvt_thread.h"
+#include "lutil.h"
+
+#include "ldap_rq.h"
+
+#ifdef HAVE_SYSTEMD_SD_DAEMON_H
+#include <systemd/sd-daemon.h>
+#endif
+
+#ifdef LDAP_PF_LOCAL
+#include <sys/stat.h>
+/* this should go in <ldap.h> as soon as it is accepted */
+#define LDAPI_MOD_URLEXT "x-mod"
+#endif /* LDAP_PF_LOCAL */
+
+#ifndef BALANCER_MODULE
+#ifdef LDAP_PF_INET6
+int slap_inet4or6 = AF_UNSPEC;
+#else /* ! INETv6 */
+int slap_inet4or6 = AF_INET;
+#endif /* ! INETv6 */
+
+/* globals */
+time_t starttime;
+struct runqueue_s slapd_rq;
+
+#ifdef LDAP_TCP_BUFFER
+int slapd_tcp_rmem;
+int slapd_tcp_wmem;
+#endif /* LDAP_TCP_BUFFER */
+
+volatile sig_atomic_t slapd_shutdown = 0;
+volatile sig_atomic_t slapd_gentle_shutdown = 0;
+volatile sig_atomic_t slapd_abrupt_shutdown = 0;
+#endif /* !BALANCER_MODULE */
+
+static int emfile;
+
+ldap_pvt_thread_mutex_t lload_wait_mutex;
+ldap_pvt_thread_cond_t lload_wait_cond;
+ldap_pvt_thread_cond_t lload_pause_cond;
+
+#ifndef SLAPD_MAX_DAEMON_THREADS
+#define SLAPD_MAX_DAEMON_THREADS 16
+#endif
+int lload_daemon_threads = 1;
+int lload_daemon_mask;
+
+struct event_base *listener_base = NULL;
+LloadListener **lload_listeners = NULL;
+static ldap_pvt_thread_t listener_tid, *daemon_tid;
+
+struct event_base *daemon_base = NULL;
+struct evdns_base *dnsbase;
+
+struct event *lload_timeout_event;
+
+/*
+ * global lload statistics. Not mutex protected to preserve performance -
+ * increment is atomic, at most we risk a bit of inconsistency
+ */
+lload_global_stats_t lload_stats = {};
+
+#ifndef SLAPD_LISTEN_BACKLOG
+#define SLAPD_LISTEN_BACKLOG 1024
+#endif /* ! SLAPD_LISTEN_BACKLOG */
+
+#define DAEMON_ID(fd) ( fd & lload_daemon_mask )
+
+#ifdef HAVE_WINSOCK
+ldap_pvt_thread_mutex_t slapd_ws_mutex;
+SOCKET *slapd_ws_sockets;
+#define SD_READ 1
+#define SD_WRITE 2
+#define SD_ACTIVE 4
+#define SD_LISTENER 8
+#endif
+
+#ifdef HAVE_TCPD
+static ldap_pvt_thread_mutex_t sd_tcpd_mutex;
+#endif /* TCP Wrappers */
+
+typedef struct listener_item {
+ struct evconnlistener *listener;
+ ber_socket_t fd;
+} listener_item;
+
+typedef struct lload_daemon_st {
+ ldap_pvt_thread_mutex_t sd_mutex;
+
+ struct event_base *base;
+ struct event *wakeup_event;
+} lload_daemon_st;
+
+static lload_daemon_st lload_daemon[SLAPD_MAX_DAEMON_THREADS];
+
+static void daemon_wakeup_cb( evutil_socket_t sig, short what, void *arg );
+
+static void
+lloadd_close( ber_socket_t s )
+{
+ Debug( LDAP_DEBUG_CONNS, "lloadd_close: "
+ "closing fd=%ld\n",
+ (long)s );
+ tcp_close( s );
+}
+
+static void
+lload_free_listener_addresses( struct sockaddr **sal )
+{
+ struct sockaddr **sap;
+ if ( sal == NULL ) return;
+ for ( sap = sal; *sap != NULL; sap++ )
+ ch_free(*sap);
+ ch_free( sal );
+}
+
+#if defined(LDAP_PF_LOCAL) || defined(SLAP_X_LISTENER_MOD)
+static int
+get_url_perms( char **exts, mode_t *perms, int *crit )
+{
+ int i;
+
+ assert( exts != NULL );
+ assert( perms != NULL );
+ assert( crit != NULL );
+
+ *crit = 0;
+ for ( i = 0; exts[i]; i++ ) {
+ char *type = exts[i];
+ int c = 0;
+
+ if ( type[0] == '!' ) {
+ c = 1;
+ type++;
+ }
+
+ if ( strncasecmp( type, LDAPI_MOD_URLEXT "=",
+ sizeof(LDAPI_MOD_URLEXT "=") - 1 ) == 0 ) {
+ char *value = type + ( sizeof(LDAPI_MOD_URLEXT "=") - 1 );
+ mode_t p = 0;
+ int j;
+
+ switch ( strlen( value ) ) {
+ case 4:
+ /* skip leading '0' */
+ if ( value[0] != '0' ) return LDAP_OTHER;
+ value++;
+
+ case 3:
+ for ( j = 0; j < 3; j++ ) {
+ int v;
+
+ v = value[j] - '0';
+
+ if ( v < 0 || v > 7 ) return LDAP_OTHER;
+
+ p |= v << 3 * ( 2 - j );
+ }
+ break;
+
+ case 10:
+ for ( j = 1; j < 10; j++ ) {
+ static mode_t m[] = { 0, S_IRUSR, S_IWUSR, S_IXUSR,
+ S_IRGRP, S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH,
+ S_IXOTH };
+ static const char c[] = "-rwxrwxrwx";
+
+ if ( value[j] == c[j] ) {
+ p |= m[j];
+
+ } else if ( value[j] != '-' ) {
+ return LDAP_OTHER;
+ }
+ }
+ break;
+
+ default:
+ return LDAP_OTHER;
+ }
+
+ *crit = c;
+ *perms = p;
+
+ return LDAP_SUCCESS;
+ }
+ }
+
+ return LDAP_OTHER;
+}
+#endif /* LDAP_PF_LOCAL || SLAP_X_LISTENER_MOD */
+
+/* port = 0 indicates AF_LOCAL */
+static int
+lload_get_listener_addresses(
+ const char *host,
+ unsigned short port,
+ struct sockaddr ***sal )
+{
+ struct sockaddr **sap;
+
+#ifdef LDAP_PF_LOCAL
+ if ( port == 0 ) {
+ sap = *sal = ch_malloc( 2 * sizeof(void *) );
+
+ *sap = ch_calloc( 1, sizeof(struct sockaddr_un) );
+ sap[1] = NULL;
+
+ if ( strlen( host ) >
+ ( sizeof( ((struct sockaddr_un *)*sap)->sun_path ) - 1 ) ) {
+ Debug( LDAP_DEBUG_ANY, "lload_get_listener_addresses: "
+ "domain socket path (%s) too long in URL\n",
+ host );
+ goto errexit;
+ }
+
+ (*sap)->sa_family = AF_LOCAL;
+ strcpy( ((struct sockaddr_un *)*sap)->sun_path, host );
+ } else
+#endif /* LDAP_PF_LOCAL */
+ {
+#ifdef HAVE_GETADDRINFO
+ struct addrinfo hints, *res, *sai;
+ int n, err;
+ char serv[7];
+
+ memset( &hints, '\0', sizeof(hints) );
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = slap_inet4or6;
+ snprintf( serv, sizeof(serv), "%d", port );
+
+ if ( (err = getaddrinfo( host, serv, &hints, &res )) ) {
+ Debug( LDAP_DEBUG_ANY, "lload_get_listener_addresses: "
+ "getaddrinfo() failed: %s\n",
+ AC_GAI_STRERROR(err) );
+ return -1;
+ }
+
+ sai = res;
+ for ( n = 2; ( sai = sai->ai_next ) != NULL; n++ ) {
+ /* EMPTY */;
+ }
+ sap = *sal = ch_calloc( n, sizeof(void *) );
+
+ *sap = NULL;
+
+ for ( sai = res; sai; sai = sai->ai_next ) {
+ if ( sai->ai_addr == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "lload_get_listener_addresses: "
+ "getaddrinfo ai_addr is NULL?\n" );
+ freeaddrinfo( res );
+ goto errexit;
+ }
+
+ switch ( sai->ai_family ) {
+#ifdef LDAP_PF_INET6
+ case AF_INET6:
+ *sap = ch_malloc( sizeof(struct sockaddr_in6) );
+ *(struct sockaddr_in6 *)*sap =
+ *((struct sockaddr_in6 *)sai->ai_addr);
+ break;
+#endif /* LDAP_PF_INET6 */
+ case AF_INET:
+ *sap = ch_malloc( sizeof(struct sockaddr_in) );
+ *(struct sockaddr_in *)*sap =
+ *((struct sockaddr_in *)sai->ai_addr);
+ break;
+ default:
+ *sap = NULL;
+ break;
+ }
+
+ if ( *sap != NULL ) {
+ (*sap)->sa_family = sai->ai_family;
+ sap++;
+ *sap = NULL;
+ }
+ }
+
+ freeaddrinfo( res );
+
+#else /* ! HAVE_GETADDRINFO */
+ int i, n = 1;
+ struct in_addr in;
+ struct hostent *he = NULL;
+
+ if ( host == NULL ) {
+ in.s_addr = htonl( INADDR_ANY );
+
+ } else if ( !inet_aton( host, &in ) ) {
+ he = gethostbyname( host );
+ if ( he == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "lload_get_listener_addresses: "
+ "invalid host %s\n",
+ host );
+ return -1;
+ }
+ for ( n = 0; he->h_addr_list[n]; n++ ) /* empty */;
+ }
+
+ sap = *sal = ch_malloc( ( n + 1 ) * sizeof(void *) );
+
+ for ( i = 0; i < n; i++ ) {
+ sap[i] = ch_calloc( 1, sizeof(struct sockaddr_in) );
+ sap[i]->sa_family = AF_INET;
+ ((struct sockaddr_in *)sap[i])->sin_port = htons( port );
+ AC_MEMCPY( &((struct sockaddr_in *)sap[i])->sin_addr,
+ he ? (struct in_addr *)he->h_addr_list[i] : &in,
+ sizeof(struct in_addr) );
+ }
+ sap[i] = NULL;
+#endif /* ! HAVE_GETADDRINFO */
+ }
+
+ return 0;
+
+errexit:
+ lload_free_listener_addresses(*sal);
+ return -1;
+}
+
+static int
+lload_open_listener(
+ const char *url,
+ LDAPURLDesc *lud,
+ int *listeners,
+ int *cur )
+{
+ int num, tmp, rc;
+ LloadListener l;
+ LloadListener *li;
+ unsigned short port;
+ int err, addrlen = 0;
+ struct sockaddr **sal = NULL, **psal;
+ int socktype = SOCK_STREAM; /* default to COTS */
+ ber_socket_t s;
+ char ebuf[128];
+
+#if defined(LDAP_PF_LOCAL) || defined(SLAP_X_LISTENER_MOD)
+ /*
+ * use safe defaults
+ */
+ int crit = 1;
+#endif /* LDAP_PF_LOCAL || SLAP_X_LISTENER_MOD */
+
+ assert( url );
+ assert( lud );
+
+ l.sl_url.bv_val = NULL;
+ l.sl_mute = 0;
+ l.sl_busy = 0;
+
+#ifndef HAVE_TLS
+ if ( ldap_pvt_url_scheme2tls( lud->lud_scheme ) ) {
+ Debug( LDAP_DEBUG_ANY, "lload_open_listener: "
+ "TLS not supported (%s)\n",
+ url );
+ ldap_free_urldesc( lud );
+ return -1;
+ }
+
+ if ( !lud->lud_port ) lud->lud_port = LDAP_PORT;
+
+#else /* HAVE_TLS */
+ l.sl_is_tls = ldap_pvt_url_scheme2tls( lud->lud_scheme );
+#endif /* HAVE_TLS */
+
+ l.sl_is_proxied = ldap_pvt_url_scheme2proxied( lud->lud_scheme );
+
+#ifdef LDAP_TCP_BUFFER
+ l.sl_tcp_rmem = 0;
+ l.sl_tcp_wmem = 0;
+#endif /* LDAP_TCP_BUFFER */
+
+ port = (unsigned short)lud->lud_port;
+
+ tmp = ldap_pvt_url_scheme2proto( lud->lud_scheme );
+ if ( tmp == LDAP_PROTO_IPC ) {
+#ifdef LDAP_PF_LOCAL
+ if ( lud->lud_host == NULL || lud->lud_host[0] == '\0' ) {
+ err = lload_get_listener_addresses( LDAPI_SOCK, 0, &sal );
+ } else {
+ err = lload_get_listener_addresses( lud->lud_host, 0, &sal );
+ }
+#else /* ! LDAP_PF_LOCAL */
+
+ Debug( LDAP_DEBUG_ANY, "lload_open_listener: "
+ "URL scheme not supported: %s\n",
+ url );
+ ldap_free_urldesc( lud );
+ return -1;
+#endif /* ! LDAP_PF_LOCAL */
+ } else {
+ if ( lud->lud_host == NULL || lud->lud_host[0] == '\0' ||
+ strcmp( lud->lud_host, "*" ) == 0 ) {
+ err = lload_get_listener_addresses( NULL, port, &sal );
+ } else {
+ err = lload_get_listener_addresses( lud->lud_host, port, &sal );
+ }
+ }
+
+#if defined(LDAP_PF_LOCAL) || defined(SLAP_X_LISTENER_MOD)
+ if ( lud->lud_exts ) {
+ err = get_url_perms( lud->lud_exts, &l.sl_perms, &crit );
+ } else {
+ l.sl_perms = S_IRWXU | S_IRWXO;
+ }
+#endif /* LDAP_PF_LOCAL || SLAP_X_LISTENER_MOD */
+
+ ldap_free_urldesc( lud );
+ if ( err ) {
+ lload_free_listener_addresses( sal );
+ return -1;
+ }
+
+ /* If we got more than one address returned, we need to make space
+ * for it in the lload_listeners array.
+ */
+ for ( num = 0; sal[num]; num++ ) /* empty */;
+ if ( num > 1 ) {
+ *listeners += num - 1;
+ lload_listeners = ch_realloc( lload_listeners,
+ ( *listeners + 1 ) * sizeof(LloadListener *) );
+ }
+
+ psal = sal;
+ while ( *sal != NULL ) {
+ char *af;
+ switch ( (*sal)->sa_family ) {
+ case AF_INET:
+ af = "IPv4";
+ break;
+#ifdef LDAP_PF_INET6
+ case AF_INET6:
+ af = "IPv6";
+ break;
+#endif /* LDAP_PF_INET6 */
+#ifdef LDAP_PF_LOCAL
+ case AF_LOCAL:
+ af = "Local";
+ break;
+#endif /* LDAP_PF_LOCAL */
+ default:
+ sal++;
+ continue;
+ }
+
+ s = socket( (*sal)->sa_family, socktype, 0 );
+ if ( s == AC_SOCKET_INVALID ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "lload_open_listener: "
+ "%s socket() failed errno=%d (%s)\n",
+ af, err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ sal++;
+ continue;
+ }
+ ber_pvt_socket_set_nonblock( s, 1 );
+ l.sl_sd = s;
+
+#ifdef LDAP_PF_LOCAL
+ if ( (*sal)->sa_family == AF_LOCAL ) {
+ unlink( ((struct sockaddr_un *)*sal)->sun_path );
+ } else
+#endif /* LDAP_PF_LOCAL */
+ {
+#ifdef SO_REUSEADDR
+ /* enable address reuse */
+ tmp = 1;
+ rc = setsockopt(
+ s, SOL_SOCKET, SO_REUSEADDR, (char *)&tmp, sizeof(tmp) );
+ if ( rc == AC_SOCKET_ERROR ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "lload_open_listener(%ld): "
+ "setsockopt(SO_REUSEADDR) failed errno=%d (%s)\n",
+ (long)l.sl_sd, err,
+ sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ }
+#endif /* SO_REUSEADDR */
+ }
+
+ switch ( (*sal)->sa_family ) {
+ case AF_INET:
+ addrlen = sizeof(struct sockaddr_in);
+ break;
+#ifdef LDAP_PF_INET6
+ case AF_INET6:
+#ifdef IPV6_V6ONLY
+ /* Try to use IPv6 sockets for IPv6 only */
+ tmp = 1;
+ rc = setsockopt( s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&tmp,
+ sizeof(tmp) );
+ if ( rc == AC_SOCKET_ERROR ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "lload_open_listener(%ld): "
+ "setsockopt(IPV6_V6ONLY) failed errno=%d (%s)\n",
+ (long)l.sl_sd, err,
+ sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ }
+#endif /* IPV6_V6ONLY */
+ addrlen = sizeof(struct sockaddr_in6);
+ break;
+#endif /* LDAP_PF_INET6 */
+
+#ifdef LDAP_PF_LOCAL
+ case AF_LOCAL:
+#ifdef LOCAL_CREDS
+ {
+ int one = 1;
+ setsockopt( s, 0, LOCAL_CREDS, &one, sizeof(one) );
+ }
+#endif /* LOCAL_CREDS */
+
+ addrlen = sizeof(struct sockaddr_un);
+ break;
+#endif /* LDAP_PF_LOCAL */
+ }
+
+#ifdef LDAP_PF_LOCAL
+ /* create socket with all permissions set for those systems
+ * that honor permissions on sockets (e.g. Linux); typically,
+ * only write is required. To exploit filesystem permissions,
+ * place the socket in a directory and use directory's
+ * permissions. Need write perms to the directory to
+ * create/unlink the socket; likely need exec perms to access
+ * the socket (ITS#4709) */
+ {
+ mode_t old_umask = 0;
+
+ if ( (*sal)->sa_family == AF_LOCAL ) {
+ old_umask = umask( 0 );
+ }
+#endif /* LDAP_PF_LOCAL */
+ rc = bind( s, *sal, addrlen );
+#ifdef LDAP_PF_LOCAL
+ if ( old_umask != 0 ) {
+ umask( old_umask );
+ }
+ }
+#endif /* LDAP_PF_LOCAL */
+ if ( rc ) {
+ err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "lload_open_listener: "
+ "bind(%ld) failed errno=%d (%s)\n",
+ (long)l.sl_sd, err,
+ sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ tcp_close( s );
+ sal++;
+ continue;
+ }
+
+ switch ( (*sal)->sa_family ) {
+#ifdef LDAP_PF_LOCAL
+ case AF_LOCAL: {
+ char *path = ((struct sockaddr_un *)*sal)->sun_path;
+ l.sl_name.bv_len = strlen( path ) + STRLENOF("PATH=");
+ l.sl_name.bv_val = ch_malloc( l.sl_name.bv_len + 1 );
+ snprintf( l.sl_name.bv_val, l.sl_name.bv_len + 1, "PATH=%s",
+ path );
+ } break;
+#endif /* LDAP_PF_LOCAL */
+
+ case AF_INET: {
+ char addr[INET_ADDRSTRLEN];
+ const char *s;
+#if defined(HAVE_GETADDRINFO) && defined(HAVE_INET_NTOP)
+ s = inet_ntop( AF_INET,
+ &((struct sockaddr_in *)*sal)->sin_addr, addr,
+ sizeof(addr) );
+#else /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ s = inet_ntoa( ((struct sockaddr_in *)*sal)->sin_addr );
+#endif /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ if ( !s ) s = SLAP_STRING_UNKNOWN;
+ port = ntohs( ((struct sockaddr_in *)*sal)->sin_port );
+ l.sl_name.bv_val =
+ ch_malloc( sizeof("IP=255.255.255.255:65535") );
+ snprintf( l.sl_name.bv_val,
+ sizeof("IP=255.255.255.255:65535"), "IP=%s:%d", s,
+ port );
+ l.sl_name.bv_len = strlen( l.sl_name.bv_val );
+ } break;
+
+#ifdef LDAP_PF_INET6
+ case AF_INET6: {
+ char addr[INET6_ADDRSTRLEN];
+ const char *s;
+ s = inet_ntop( AF_INET6,
+ &((struct sockaddr_in6 *)*sal)->sin6_addr, addr,
+ sizeof(addr) );
+ if ( !s ) s = SLAP_STRING_UNKNOWN;
+ port = ntohs( ((struct sockaddr_in6 *)*sal)->sin6_port );
+ l.sl_name.bv_len = strlen( s ) + sizeof("IP=[]:65535");
+ l.sl_name.bv_val = ch_malloc( l.sl_name.bv_len );
+ snprintf( l.sl_name.bv_val, l.sl_name.bv_len, "IP=[%s]:%d", s,
+ port );
+ l.sl_name.bv_len = strlen( l.sl_name.bv_val );
+ } break;
+#endif /* LDAP_PF_INET6 */
+
+ default:
+ Debug( LDAP_DEBUG_ANY, "lload_open_listener: "
+ "unsupported address family (%d)\n",
+ (int)(*sal)->sa_family );
+ break;
+ }
+
+ AC_MEMCPY( &l.sl_sa, *sal, addrlen );
+ ber_str2bv( url, 0, 1, &l.sl_url );
+ li = ch_malloc( sizeof(LloadListener) );
+ *li = l;
+ lload_listeners[*cur] = li;
+ (*cur)++;
+ sal++;
+ }
+
+ lload_free_listener_addresses( psal );
+
+ if ( l.sl_url.bv_val == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "lload_open_listener: "
+ "failed on %s\n",
+ url );
+ return -1;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "lload_open_listener: "
+ "listener initialized %s\n",
+ l.sl_url.bv_val );
+
+ return 0;
+}
+
+int
+lload_open_new_listener( const char *url, LDAPURLDesc *lud )
+{
+ int rc, i, j = 0;
+
+ for ( i = 0; lload_listeners && lload_listeners[i] != NULL;
+ i++ ) /* count */
+ ;
+ j = i;
+
+ i++;
+ lload_listeners = ch_realloc(
+ lload_listeners, ( i + 1 ) * sizeof(LloadListener *) );
+
+ rc = lload_open_listener( url, lud, &i, &j );
+ lload_listeners[j] = NULL;
+ return rc;
+}
+
+int lloadd_inited = 0;
+
+int
+lloadd_listeners_init( const char *urls )
+{
+ int i, j, n;
+ char **u;
+ LDAPURLDesc *lud;
+
+ Debug( LDAP_DEBUG_ARGS, "lloadd_listeners_init: %s\n",
+ urls ? urls : "<null>" );
+
+#ifdef HAVE_TCPD
+ ldap_pvt_thread_mutex_init( &sd_tcpd_mutex );
+#endif /* TCP Wrappers */
+
+ if ( urls == NULL ) urls = "ldap:///";
+
+ u = ldap_str2charray( urls, " " );
+
+ if ( u == NULL || u[0] == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "lloadd_listeners_init: "
+ "no urls (%s) provided\n",
+ urls );
+ if ( u ) ldap_charray_free( u );
+ return -1;
+ }
+
+ for ( i = 0; u[i] != NULL; i++ ) {
+ Debug( LDAP_DEBUG_TRACE, "lloadd_listeners_init: "
+ "listen on %s\n",
+ u[i] );
+ }
+
+ if ( i == 0 ) {
+ Debug( LDAP_DEBUG_ANY, "lloadd_listeners_init: "
+ "no listeners to open (%s)\n",
+ urls );
+ ldap_charray_free( u );
+ return -1;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "lloadd_listeners_init: "
+ "%d listeners to open...\n",
+ i );
+ lload_listeners = ch_malloc( ( i + 1 ) * sizeof(LloadListener *) );
+
+ for ( n = 0, j = 0; u[n]; n++ ) {
+ if ( ldap_url_parse_ext( u[n], &lud, LDAP_PVT_URL_PARSE_DEF_PORT ) ) {
+ Debug( LDAP_DEBUG_ANY, "lloadd_listeners_init: "
+ "could not parse url %s\n",
+ u[n] );
+ ldap_charray_free( u );
+ return -1;
+ }
+
+ if ( lload_open_listener( u[n], lud, &i, &j ) ) {
+ ldap_charray_free( u );
+ return -1;
+ }
+ }
+ lload_listeners[j] = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "lloadd_listeners_init: "
+ "%d listeners opened\n",
+ i );
+
+ ldap_charray_free( u );
+
+ return !i;
+}
+
+int
+lloadd_daemon_destroy( void )
+{
+ epoch_shutdown();
+ if ( lloadd_inited ) {
+ int i;
+
+ for ( i = 0; i < lload_daemon_threads; i++ ) {
+ ldap_pvt_thread_mutex_destroy( &lload_daemon[i].sd_mutex );
+ if ( lload_daemon[i].wakeup_event ) {
+ event_free( lload_daemon[i].wakeup_event );
+ }
+ if ( lload_daemon[i].base ) {
+ event_base_free( lload_daemon[i].base );
+ }
+ }
+
+ event_base_free( daemon_base );
+ daemon_base = NULL;
+
+ lloadd_inited = 0;
+#ifdef HAVE_TCPD
+ ldap_pvt_thread_mutex_destroy( &sd_tcpd_mutex );
+#endif /* TCP Wrappers */
+ }
+
+ return 0;
+}
+
+static void
+destroy_listeners( void )
+{
+ LloadListener *lr, **ll = lload_listeners;
+
+ if ( ll == NULL ) return;
+
+ ldap_pvt_thread_join( listener_tid, (void *)NULL );
+
+ while ( (lr = *ll++) != NULL ) {
+ if ( lr->sl_url.bv_val ) {
+ ber_memfree( lr->sl_url.bv_val );
+ }
+
+ if ( lr->sl_name.bv_val ) {
+ ber_memfree( lr->sl_name.bv_val );
+ }
+
+#ifdef LDAP_PF_LOCAL
+ if ( lr->sl_sa.sa_addr.sa_family == AF_LOCAL ) {
+ unlink( lr->sl_sa.sa_un_addr.sun_path );
+ }
+#endif /* LDAP_PF_LOCAL */
+
+ evconnlistener_free( lr->listener );
+
+ free( lr );
+ }
+
+ free( lload_listeners );
+ lload_listeners = NULL;
+
+ if ( listener_base ) {
+ event_base_free( listener_base );
+ }
+}
+
+static void
+lload_listener(
+ struct evconnlistener *listener,
+ ber_socket_t s,
+ struct sockaddr *a,
+ int len,
+ void *arg )
+{
+ LloadListener *sl = arg;
+ LloadConnection *c;
+ Sockaddr *from = (Sockaddr *)a;
+ char peername[LDAP_IPADDRLEN];
+ struct berval peerbv = BER_BVC(peername);
+ int cflag;
+ int tid;
+ char ebuf[128];
+
+ Debug( LDAP_DEBUG_TRACE, ">>> lload_listener(%s)\n", sl->sl_url.bv_val );
+
+ peername[0] = '\0';
+
+ /* Resume the listener FD to allow concurrent-processing of
+ * additional incoming connections.
+ */
+ sl->sl_busy = 0;
+
+ tid = DAEMON_ID(s);
+
+ Debug( LDAP_DEBUG_CONNS, "lload_listener: "
+ "listen=%ld, new connection fd=%ld\n",
+ (long)sl->sl_sd, (long)s );
+
+#if defined(SO_KEEPALIVE) || defined(TCP_NODELAY)
+#ifdef LDAP_PF_LOCAL
+ /* for IPv4 and IPv6 sockets only */
+ if ( from->sa_addr.sa_family != AF_LOCAL )
+#endif /* LDAP_PF_LOCAL */
+ {
+ int rc;
+ int tmp;
+#ifdef SO_KEEPALIVE
+ /* enable keep alives */
+ tmp = 1;
+ rc = setsockopt(
+ s, SOL_SOCKET, SO_KEEPALIVE, (char *)&tmp, sizeof(tmp) );
+ if ( rc == AC_SOCKET_ERROR ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "lload_listener(%ld): "
+ "setsockopt(SO_KEEPALIVE) failed errno=%d (%s)\n",
+ (long)s, err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ }
+#endif /* SO_KEEPALIVE */
+#ifdef TCP_NODELAY
+ /* enable no delay */
+ tmp = 1;
+ rc = setsockopt(
+ s, IPPROTO_TCP, TCP_NODELAY, (char *)&tmp, sizeof(tmp) );
+ if ( rc == AC_SOCKET_ERROR ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "lload_listener(%ld): "
+ "setsockopt(TCP_NODELAY) failed errno=%d (%s)\n",
+ (long)s, err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ }
+#endif /* TCP_NODELAY */
+ }
+#endif /* SO_KEEPALIVE || TCP_NODELAY */
+
+ if ( sl->sl_is_proxied ) {
+ if ( !proxyp( s, from ) ) {
+ Debug( LDAP_DEBUG_ANY, "lload_listener: "
+ "proxyp(%ld) failed\n",
+ (long)s );
+ lloadd_close( s );
+ return;
+ }
+ }
+
+ cflag = 0;
+ switch ( from->sa_addr.sa_family ) {
+#ifdef LDAP_PF_LOCAL
+ case AF_LOCAL:
+ cflag |= CONN_IS_IPC;
+
+ /* FIXME: apparently accept doesn't fill the sun_path member */
+ sprintf( peername, "PATH=%s", sl->sl_sa.sa_un_addr.sun_path );
+ break;
+#endif /* LDAP_PF_LOCAL */
+
+#ifdef LDAP_PF_INET6
+ case AF_INET6:
+#endif /* LDAP_PF_INET6 */
+ case AF_INET:
+ ldap_pvt_sockaddrstr( from, &peerbv );
+ break;
+
+ default:
+ lloadd_close( s );
+ return;
+ }
+
+#ifdef HAVE_TLS
+ if ( sl->sl_is_tls ) cflag |= CONN_IS_TLS;
+#endif
+ c = client_init( s, peername, lload_daemon[tid].base, cflag );
+
+ if ( !c ) {
+ Debug( LDAP_DEBUG_ANY, "lload_listener: "
+ "client_init(%ld, %s, %s) failed\n",
+ (long)s, peername, sl->sl_name.bv_val );
+ lloadd_close( s );
+ }
+
+ return;
+}
+
+static void *
+lload_listener_thread( void *ctx )
+{
+ int rc = event_base_dispatch( listener_base );
+ Debug( LDAP_DEBUG_ANY, "lload_listener_thread: "
+ "event loop finished: rc=%d\n",
+ rc );
+
+ return (void *)NULL;
+}
+
+static void
+listener_error_cb( struct evconnlistener *lev, void *arg )
+{
+ LloadListener *l = arg;
+ int err = EVUTIL_SOCKET_ERROR();
+
+ assert( l->listener == lev );
+ if (
+#ifdef EMFILE
+ err == EMFILE ||
+#endif /* EMFILE */
+#ifdef ENFILE
+ err == ENFILE ||
+#endif /* ENFILE */
+ 0 ) {
+ ldap_pvt_thread_mutex_lock( &lload_daemon[0].sd_mutex );
+ emfile++;
+ /* Stop listening until an existing session closes */
+ l->sl_mute = 1;
+ evconnlistener_disable( lev );
+ ldap_pvt_thread_mutex_unlock( &lload_daemon[0].sd_mutex );
+ Debug( LDAP_DEBUG_ANY, "listener_error_cb: "
+ "too many open files, cannot accept new connections on "
+ "url=%s\n",
+ l->sl_url.bv_val );
+ } else {
+ char ebuf[128];
+ Debug( LDAP_DEBUG_ANY, "listener_error_cb: "
+ "received an error on a listener, shutting down: '%s'\n",
+ sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ event_base_loopexit( l->base, NULL );
+ }
+}
+
+void
+listeners_reactivate( void )
+{
+ int i;
+
+ ldap_pvt_thread_mutex_lock( &lload_daemon[0].sd_mutex );
+ for ( i = 0; emfile && lload_listeners[i] != NULL; i++ ) {
+ LloadListener *lr = lload_listeners[i];
+
+ if ( lr->sl_sd == AC_SOCKET_INVALID ) continue;
+ if ( lr->sl_mute ) {
+ emfile--;
+ evconnlistener_enable( lr->listener );
+ lr->sl_mute = 0;
+ Debug( LDAP_DEBUG_CONNS, "listeners_reactivate: "
+ "reactivated listener url=%s\n",
+ lr->sl_url.bv_val );
+ }
+ }
+ if ( emfile && lload_listeners[i] == NULL ) {
+ /* Walked the entire list without enabling anything; emfile
+ * counter is stale. Reset it. */
+ emfile = 0;
+ }
+ ldap_pvt_thread_mutex_unlock( &lload_daemon[0].sd_mutex );
+}
+
+static int
+lload_listener_activate( void )
+{
+ struct evconnlistener *listener;
+ int l, rc;
+ char ebuf[128];
+
+ listener_base = event_base_new();
+ if ( !listener_base ) return -1;
+
+ for ( l = 0; lload_listeners[l] != NULL; l++ ) {
+ if ( lload_listeners[l]->sl_sd == AC_SOCKET_INVALID ) continue;
+
+ /* FIXME: TCP-only! */
+#ifdef LDAP_TCP_BUFFER
+ if ( 1 ) {
+ int origsize, size, realsize, rc;
+ socklen_t optlen;
+
+ size = 0;
+ if ( lload_listeners[l]->sl_tcp_rmem > 0 ) {
+ size = lload_listeners[l]->sl_tcp_rmem;
+ } else if ( slapd_tcp_rmem > 0 ) {
+ size = slapd_tcp_rmem;
+ }
+
+ if ( size > 0 ) {
+ optlen = sizeof(origsize);
+ rc = getsockopt( lload_listeners[l]->sl_sd, SOL_SOCKET,
+ SO_RCVBUF, (void *)&origsize, &optlen );
+
+ if ( rc ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "lload_listener_activate: "
+ "getsockopt(SO_RCVBUF) failed errno=%d (%s)\n",
+ err, AC_STRERROR_R( err, ebuf, sizeof(ebuf) ) );
+ }
+
+ optlen = sizeof(size);
+ rc = setsockopt( lload_listeners[l]->sl_sd, SOL_SOCKET,
+ SO_RCVBUF, (const void *)&size, optlen );
+
+ if ( rc ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "lload_listener_activate: "
+ "setsockopt(SO_RCVBUF) failed errno=%d (%s)\n",
+ err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ }
+
+ optlen = sizeof(realsize);
+ rc = getsockopt( lload_listeners[l]->sl_sd, SOL_SOCKET,
+ SO_RCVBUF, (void *)&realsize, &optlen );
+
+ if ( rc ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "lload_listener_activate: "
+ "getsockopt(SO_RCVBUF) failed errno=%d (%s)\n",
+ err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ }
+
+ Debug( LDAP_DEBUG_ANY, "lload_listener_activate: "
+ "url=%s (#%d) RCVBUF original size=%d requested "
+ "size=%d real size=%d\n",
+ lload_listeners[l]->sl_url.bv_val, l, origsize, size,
+ realsize );
+ }
+
+ size = 0;
+ if ( lload_listeners[l]->sl_tcp_wmem > 0 ) {
+ size = lload_listeners[l]->sl_tcp_wmem;
+ } else if ( slapd_tcp_wmem > 0 ) {
+ size = slapd_tcp_wmem;
+ }
+
+ if ( size > 0 ) {
+ optlen = sizeof(origsize);
+ rc = getsockopt( lload_listeners[l]->sl_sd, SOL_SOCKET,
+ SO_SNDBUF, (void *)&origsize, &optlen );
+
+ if ( rc ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "lload_listener_activate: "
+ "getsockopt(SO_SNDBUF) failed errno=%d (%s)\n",
+ err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ }
+
+ optlen = sizeof(size);
+ rc = setsockopt( lload_listeners[l]->sl_sd, SOL_SOCKET,
+ SO_SNDBUF, (const void *)&size, optlen );
+
+ if ( rc ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "lload_listener_activate: "
+ "setsockopt(SO_SNDBUF) failed errno=%d (%s)\n",
+ err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ }
+
+ optlen = sizeof(realsize);
+ rc = getsockopt( lload_listeners[l]->sl_sd, SOL_SOCKET,
+ SO_SNDBUF, (void *)&realsize, &optlen );
+
+ if ( rc ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "lload_listener_activate: "
+ "getsockopt(SO_SNDBUF) failed errno=%d (%s)\n",
+ err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ }
+
+ Debug( LDAP_DEBUG_ANY, "lload_listener_activate: "
+ "url=%s (#%d) SNDBUF original size=%d requested "
+ "size=%d real size=%d\n",
+ lload_listeners[l]->sl_url.bv_val, l, origsize, size,
+ realsize );
+ }
+ }
+#endif /* LDAP_TCP_BUFFER */
+
+ lload_listeners[l]->sl_busy = 1;
+ listener = evconnlistener_new( listener_base, lload_listener,
+ lload_listeners[l],
+ LEV_OPT_THREADSAFE|LEV_OPT_DEFERRED_ACCEPT,
+ SLAPD_LISTEN_BACKLOG, lload_listeners[l]->sl_sd );
+ if ( !listener ) {
+ int err = sock_errno();
+
+#ifdef LDAP_PF_INET6
+ /* If error is EADDRINUSE, we are trying to listen to INADDR_ANY and
+ * we are already listening to in6addr_any, then we want to ignore
+ * this and continue.
+ */
+ if ( err == EADDRINUSE ) {
+ int i;
+ struct sockaddr_in sa = lload_listeners[l]->sl_sa.sa_in_addr;
+ struct sockaddr_in6 sa6;
+
+ if ( sa.sin_family == AF_INET &&
+ sa.sin_addr.s_addr == htonl( INADDR_ANY ) ) {
+ for ( i = 0; i < l; i++ ) {
+ sa6 = lload_listeners[i]->sl_sa.sa_in6_addr;
+ if ( sa6.sin6_family == AF_INET6 &&
+ !memcmp( &sa6.sin6_addr, &in6addr_any,
+ sizeof(struct in6_addr) ) ) {
+ break;
+ }
+ }
+
+ if ( i < l ) {
+ /* We are already listening to in6addr_any */
+ Debug( LDAP_DEBUG_CONNS, "lload_listener_activate: "
+ "Attempt to listen to 0.0.0.0 failed, "
+ "already listening on ::, assuming IPv4 "
+ "included\n" );
+ lloadd_close( lload_listeners[l]->sl_sd );
+ lload_listeners[l]->sl_sd = AC_SOCKET_INVALID;
+ continue;
+ }
+ }
+ }
+#endif /* LDAP_PF_INET6 */
+ Debug( LDAP_DEBUG_ANY, "lload_listener_activate: "
+ "listen(%s, 5) failed errno=%d (%s)\n",
+ lload_listeners[l]->sl_url.bv_val, err,
+ sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ return -1;
+ }
+
+ lload_listeners[l]->base = listener_base;
+ lload_listeners[l]->listener = listener;
+ evconnlistener_set_error_cb( listener, listener_error_cb );
+ }
+
+ rc = ldap_pvt_thread_create(
+ &listener_tid, 0, lload_listener_thread, lload_listeners[l] );
+
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "lload_listener_activate(%d): "
+ "submit failed (%d)\n",
+ lload_listeners[l]->sl_sd, rc );
+ }
+ return rc;
+}
+
+static void *
+lloadd_io_task( void *ptr )
+{
+ int rc;
+ int tid = (ldap_pvt_thread_t *)ptr - daemon_tid;
+ struct event_base *base = lload_daemon[tid].base;
+ struct event *event;
+
+ event = event_new( base, -1, EV_WRITE, daemon_wakeup_cb, ptr );
+ if ( !event ) {
+ Debug( LDAP_DEBUG_ANY, "lloadd_io_task: "
+ "failed to set up the wakeup event\n" );
+ return (void *)-1;
+ }
+ event_add( event, NULL );
+ lload_daemon[tid].wakeup_event = event;
+
+ /* run */
+ rc = event_base_dispatch( base );
+ Debug( LDAP_DEBUG_ANY, "lloadd_io_task: "
+ "Daemon %d, event loop finished: rc=%d\n",
+ tid, rc );
+
+ if ( !slapd_gentle_shutdown ) {
+ slapd_abrupt_shutdown = 1;
+ }
+
+ return NULL;
+}
+
+int
+lloadd_daemon( struct event_base *daemon_base )
+{
+ int i, rc;
+ LloadBackend *b;
+ struct event_base *base;
+ struct event *event;
+
+ assert( daemon_base != NULL );
+
+ dnsbase = evdns_base_new( daemon_base, EVDNS_BASE_INITIALIZE_NAMESERVERS );
+ if ( !dnsbase ) {
+ Debug( LDAP_DEBUG_ANY, "lloadd startup: "
+ "failed to set up for async name resolution\n" );
+ return -1;
+ }
+
+ if ( lload_daemon_threads > SLAPD_MAX_DAEMON_THREADS )
+ lload_daemon_threads = SLAPD_MAX_DAEMON_THREADS;
+
+ daemon_tid =
+ ch_malloc( lload_daemon_threads * sizeof(ldap_pvt_thread_t) );
+
+ for ( i = 0; i < lload_daemon_threads; i++ ) {
+ base = event_base_new();
+ if ( !base ) {
+ Debug( LDAP_DEBUG_ANY, "lloadd startup: "
+ "failed to acquire event base for an I/O thread\n" );
+ return -1;
+ }
+ lload_daemon[i].base = base;
+
+ ldap_pvt_thread_mutex_init( &lload_daemon[i].sd_mutex );
+ /* threads that handle client and upstream sockets */
+ rc = ldap_pvt_thread_create(
+ &daemon_tid[i], 0, lloadd_io_task, &daemon_tid[i] );
+
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "lloadd startup: "
+ "listener ldap_pvt_thread_create failed (%d)\n",
+ rc );
+ return rc;
+ }
+ }
+
+ if ( (rc = lload_listener_activate()) != 0 ) {
+ return rc;
+ }
+
+ if ( !LDAP_CIRCLEQ_EMPTY( &backend ) ) {
+ current_backend = LDAP_CIRCLEQ_FIRST( &backend );
+ LDAP_CIRCLEQ_FOREACH ( b, &backend, b_next ) {
+ event = evtimer_new( daemon_base, backend_connect, b );
+ if ( !event ) {
+ Debug( LDAP_DEBUG_ANY, "lloadd: "
+ "failed to allocate retry event\n" );
+ return -1;
+ }
+
+ checked_lock( &b->b_mutex );
+ b->b_retry_event = event;
+ backend_retry( b );
+ checked_unlock( &b->b_mutex );
+ }
+ }
+
+ event = evtimer_new( daemon_base, operations_timeout, event_self_cbarg() );
+ if ( !event ) {
+ Debug( LDAP_DEBUG_ANY, "lloadd: "
+ "failed to allocate timeout event\n" );
+ return -1;
+ }
+ lload_timeout_event = event;
+
+ /* TODO: should we just add it with any timeout and re-add when the timeout
+ * changes? */
+ if ( lload_timeout_api ) {
+ event_add( event, lload_timeout_api );
+ }
+
+ checked_lock( &lload_wait_mutex );
+ lloadd_inited = 1;
+ ldap_pvt_thread_cond_signal( &lload_wait_cond );
+ checked_unlock( &lload_wait_mutex );
+#if !defined(BALANCER_MODULE) && defined(HAVE_SYSTEMD)
+ rc = sd_notify( 1, "READY=1" );
+ if ( rc < 0 ) {
+ Debug( LDAP_DEBUG_ANY, "lloadd startup: "
+ "systemd sd_notify failed (%d)\n", rc );
+ }
+#endif /* !BALANCER_MODULE && HAVE_SYSTEMD */
+
+ rc = event_base_dispatch( daemon_base );
+ Debug( LDAP_DEBUG_ANY, "lloadd shutdown: "
+ "Main event loop finished: rc=%d\n",
+ rc );
+
+ /* shutdown */
+ event_base_loopexit( listener_base, 0 );
+
+ /* wait for the listener threads to complete */
+ destroy_listeners();
+
+ /* Mark upstream connections closing and prevent from opening new ones */
+ LDAP_CIRCLEQ_FOREACH ( b, &backend, b_next ) {
+ epoch_t epoch = epoch_join();
+
+ checked_lock( &b->b_mutex );
+ b->b_numconns = b->b_numbindconns = 0;
+ backend_reset( b, 1 );
+ checked_unlock( &b->b_mutex );
+
+ epoch_leave( epoch );
+ }
+
+ /* Do the same for clients */
+ clients_destroy( 1 );
+
+ for ( i = 0; i < lload_daemon_threads; i++ ) {
+ /*
+ * https://github.com/libevent/libevent/issues/623
+ * deleting the event doesn't notify the base, just activate it and
+ * let it delete itself
+ */
+ event_active( lload_daemon[i].wakeup_event, EV_READ, 0 );
+ }
+
+ for ( i = 0; i < lload_daemon_threads; i++ ) {
+ ldap_pvt_thread_join( daemon_tid[i], (void *)NULL );
+ }
+
+#ifndef BALANCER_MODULE
+ if ( LogTest( LDAP_DEBUG_ANY ) ) {
+ int t = ldap_pvt_thread_pool_backload( &connection_pool );
+ Debug( LDAP_DEBUG_ANY, "lloadd shutdown: "
+ "waiting for %d operations/tasks to finish\n",
+ t );
+ }
+ ldap_pvt_thread_pool_close( &connection_pool, 1 );
+#endif
+
+ lload_backends_destroy();
+ clients_destroy( 0 );
+ lload_bindconf_free( &bindconf );
+ evdns_base_free( dnsbase, 0 );
+
+ ch_free( daemon_tid );
+ daemon_tid = NULL;
+
+ lloadd_daemon_destroy();
+
+ /* If we're a slapd module, let the thread that initiated the shut down
+ * know we've finished */
+ checked_lock( &lload_wait_mutex );
+ ldap_pvt_thread_cond_signal( &lload_wait_cond );
+ checked_unlock( &lload_wait_mutex );
+
+ return 0;
+}
+
+static void
+daemon_wakeup_cb( evutil_socket_t sig, short what, void *arg )
+{
+ int tid = (ldap_pvt_thread_t *)arg - daemon_tid;
+
+ Debug( LDAP_DEBUG_TRACE, "daemon_wakeup_cb: "
+ "Daemon thread %d woken up\n",
+ tid );
+ event_del( lload_daemon[tid].wakeup_event );
+}
+
+LloadChange lload_change = { .type = LLOAD_CHANGE_UNDEFINED };
+
+#ifdef BALANCER_MODULE
+int
+backend_conn_cb( ldap_pvt_thread_start_t *start, void *startarg, void *arg )
+{
+ LloadConnection *c = startarg;
+ LloadBackend *b = arg;
+
+ if ( b == NULL || c->c_backend == b ) {
+ CONNECTION_LOCK_DESTROY(c);
+ return 1;
+ }
+ return 0;
+}
+
+#ifdef HAVE_TLS
+int
+client_tls_cb( ldap_pvt_thread_start_t *start, void *startarg, void *arg )
+{
+ LloadConnection *c = startarg;
+
+ if ( c->c_destroy == client_destroy &&
+ c->c_is_tls == LLOAD_TLS_ESTABLISHED ) {
+ CONNECTION_LOCK_DESTROY(c);
+ return 1;
+ }
+ return 0;
+}
+#endif /* HAVE_TLS */
+
+void
+lload_handle_backend_invalidation( LloadChange *change )
+{
+ LloadBackend *b = change->target;
+
+ assert( change->object == LLOAD_BACKEND );
+
+ if ( change->type == LLOAD_CHANGE_ADD ) {
+ BackendInfo *mi = backend_info( "monitor" );
+
+ if ( mi ) {
+ monitor_extra_t *mbe = mi->bi_extra;
+ if ( mbe->is_configured() ) {
+ lload_monitor_backend_init( mi, b );
+ }
+ }
+
+ if ( !current_backend ) {
+ current_backend = b;
+ }
+ checked_lock( &b->b_mutex );
+ backend_retry( b );
+ checked_unlock( &b->b_mutex );
+ return;
+ } else if ( change->type == LLOAD_CHANGE_DEL ) {
+ ldap_pvt_thread_pool_walk(
+ &connection_pool, handle_pdus, backend_conn_cb, b );
+ ldap_pvt_thread_pool_walk(
+ &connection_pool, upstream_bind, backend_conn_cb, b );
+ lload_backend_destroy( b );
+ return;
+ }
+ assert( change->type == LLOAD_CHANGE_MODIFY );
+
+ /*
+ * A change that can't be handled gracefully, terminate all connections and
+ * start over.
+ */
+ if ( change->flags.backend & LLOAD_BACKEND_MOD_OTHER ) {
+ ldap_pvt_thread_pool_walk(
+ &connection_pool, handle_pdus, backend_conn_cb, b );
+ ldap_pvt_thread_pool_walk(
+ &connection_pool, upstream_bind, backend_conn_cb, b );
+ checked_lock( &b->b_mutex );
+ backend_reset( b, 0 );
+ backend_retry( b );
+ checked_unlock( &b->b_mutex );
+ return;
+ }
+
+ /*
+ * Handle changes to number of connections:
+ * - a change might get the connection limit above the pool size:
+ * - consider closing (in order of priority?):
+ * - connections awaiting connect() completion
+ * - connections currently preparing
+ * - bind connections over limit (which is 0 if 'feature vc' is on
+ * - regular connections over limit
+ * - below pool size
+ * - call backend_retry if there are no opening connections
+ * - one pool size above and one below the configured size
+ * - still close the ones above limit, it should sort itself out
+ * the only issue is if a closing connection isn't guaranteed to do
+ * that at some point
+ */
+ if ( change->flags.backend & LLOAD_BACKEND_MOD_CONNS ) {
+ int bind_requested = 0, need_close = 0, need_open = 0;
+ LloadConnection *c;
+
+ bind_requested =
+#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
+ (lload_features & LLOAD_FEATURE_VC) ? 0 :
+#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
+ b->b_numbindconns;
+
+ if ( b->b_bindavail > bind_requested ) {
+ need_close += b->b_bindavail - bind_requested;
+ } else if ( b->b_bindavail < bind_requested ) {
+ need_open = 1;
+ }
+
+ if ( b->b_active > b->b_numconns ) {
+ need_close += b->b_active - b->b_numconns;
+ } else if ( b->b_active < b->b_numconns ) {
+ need_open = 1;
+ }
+
+ if ( !need_open ) {
+ need_close += b->b_opening;
+
+ while ( !LDAP_LIST_EMPTY( &b->b_connecting ) ) {
+ LloadPendingConnection *p = LDAP_LIST_FIRST( &b->b_connecting );
+
+ LDAP_LIST_REMOVE( p, next );
+ event_free( p->event );
+ evutil_closesocket( p->fd );
+ ch_free( p );
+ b->b_opening--;
+ need_close--;
+ }
+ }
+
+ if ( need_close || !need_open ) {
+ /* It might be too late to repurpose a preparing connection, just
+ * close them all */
+ while ( !LDAP_CIRCLEQ_EMPTY( &b->b_preparing ) ) {
+ c = LDAP_CIRCLEQ_FIRST( &b->b_preparing );
+
+ event_del( c->c_read_event );
+ CONNECTION_LOCK_DESTROY(c);
+ assert( c == NULL );
+ b->b_opening--;
+ need_close--;
+ }
+ if ( event_pending( b->b_retry_event, EV_TIMEOUT, NULL ) ) {
+ event_del( b->b_retry_event );
+ b->b_opening--;
+ }
+ assert( b->b_opening == 0 );
+ }
+
+ if ( b->b_bindavail > bind_requested ) {
+ int diff = b->b_bindavail - bind_requested;
+
+ assert( need_close >= diff );
+
+ LDAP_CIRCLEQ_FOREACH ( c, &b->b_bindconns, c_next ) {
+ int gentle = 1;
+
+ lload_connection_close( c, &gentle );
+ need_close--;
+ diff--;
+ if ( !diff ) {
+ break;
+ }
+ }
+ assert( diff == 0 );
+ }
+
+ if ( b->b_active > b->b_numconns ) {
+ int diff = b->b_active - b->b_numconns;
+
+ assert( need_close >= diff );
+
+ LDAP_CIRCLEQ_FOREACH ( c, &b->b_conns, c_next ) {
+ int gentle = 1;
+
+ lload_connection_close( c, &gentle );
+ need_close--;
+ diff--;
+ if ( !diff ) {
+ break;
+ }
+ }
+ assert( diff == 0 );
+ }
+ assert( need_close == 0 );
+
+ if ( need_open ) {
+ checked_lock( &b->b_mutex );
+ backend_retry( b );
+ checked_unlock( &b->b_mutex );
+ }
+ }
+}
+
+void
+lload_handle_global_invalidation( LloadChange *change )
+{
+ assert( change->type == LLOAD_CHANGE_MODIFY );
+ assert( change->object == LLOAD_DAEMON );
+
+ if ( change->flags.daemon & LLOAD_DAEMON_MOD_THREADS ) {
+ /* walk the task queue to remove any tasks belonging to us. */
+ /* TODO: initiate a full module restart, everything will fall into
+ * place at that point */
+ ldap_pvt_thread_pool_walk(
+ &connection_pool, handle_pdus, backend_conn_cb, NULL );
+ ldap_pvt_thread_pool_walk(
+ &connection_pool, upstream_bind, backend_conn_cb, NULL );
+ assert(0);
+ return;
+ }
+
+ if ( change->flags.daemon & LLOAD_DAEMON_MOD_FEATURES ) {
+ lload_features_t feature_diff =
+ lload_features ^ ( ~(uintptr_t)change->target );
+ /* Feature change handling:
+ * - VC (TODO):
+ * - on: terminate all bind connections
+ * - off: cancel all bind operations in progress, reopen bind connections
+ * - ProxyAuthz:
+ * - on: nothing needed
+ * - off: clear c_auth/privileged on each client
+ * - read pause (WIP):
+ * - nothing needed?
+ */
+
+ assert( change->target );
+ if ( feature_diff & LLOAD_FEATURE_VC ) {
+ assert(0);
+ feature_diff &= ~LLOAD_FEATURE_VC;
+ }
+ if ( feature_diff & LLOAD_FEATURE_PAUSE ) {
+ feature_diff &= ~LLOAD_FEATURE_PAUSE;
+ }
+ if ( feature_diff & LLOAD_FEATURE_PROXYAUTHZ ) {
+ if ( !(lload_features & LLOAD_FEATURE_PROXYAUTHZ) ) {
+ LloadConnection *c;
+ /* We switched proxyauthz off */
+ LDAP_CIRCLEQ_FOREACH ( c, &clients, c_next ) {
+ if ( !BER_BVISNULL( &c->c_auth ) ) {
+ ber_memfree( c->c_auth.bv_val );
+ BER_BVZERO( &c->c_auth );
+ }
+ if ( c->c_type == LLOAD_C_PRIVILEGED ) {
+ c->c_type = LLOAD_C_OPEN;
+ }
+ }
+ }
+ feature_diff &= ~LLOAD_FEATURE_PROXYAUTHZ;
+ }
+ assert( !feature_diff );
+ }
+
+#ifdef HAVE_TLS
+ if ( change->flags.daemon & LLOAD_DAEMON_MOD_TLS ) {
+ /* terminate all clients with TLS set up */
+ ldap_pvt_thread_pool_walk(
+ &connection_pool, handle_pdus, client_tls_cb, NULL );
+ if ( !LDAP_CIRCLEQ_EMPTY( &clients ) ) {
+ LloadConnection *c = LDAP_CIRCLEQ_FIRST( &clients );
+ unsigned long first_connid = c->c_connid;
+
+ while ( c ) {
+ LloadConnection *next =
+ LDAP_CIRCLEQ_LOOP_NEXT( &clients, c, c_next );
+ if ( c->c_is_tls ) {
+ CONNECTION_LOCK_DESTROY(c);
+ assert( c == NULL );
+ }
+ c = next;
+ if ( c->c_connid <= first_connid ) {
+ c = NULL;
+ }
+ }
+ }
+ }
+#endif /* HAVE_TLS */
+
+ if ( change->flags.daemon & LLOAD_DAEMON_MOD_BINDCONF ) {
+ LloadBackend *b;
+ LloadConnection *c;
+
+ /*
+ * Only timeout changes can be handled gracefully, terminate all
+ * connections and start over.
+ */
+ ldap_pvt_thread_pool_walk(
+ &connection_pool, handle_pdus, backend_conn_cb, NULL );
+ ldap_pvt_thread_pool_walk(
+ &connection_pool, upstream_bind, backend_conn_cb, NULL );
+
+ LDAP_CIRCLEQ_FOREACH ( b, &backend, b_next ) {
+ checked_lock( &b->b_mutex );
+ backend_reset( b, 0 );
+ backend_retry( b );
+ checked_unlock( &b->b_mutex );
+ }
+
+ /* Reconsider the PRIVILEGED flag on all clients */
+ LDAP_CIRCLEQ_FOREACH ( c, &clients, c_next ) {
+ int privileged = ber_bvstrcasecmp( &c->c_auth, &lloadd_identity );
+
+ /* We have just terminated all pending operations (even pins), there
+ * should be no connections still binding/closing */
+ assert( c->c_state == LLOAD_C_READY );
+
+ c->c_type = privileged ? LLOAD_C_PRIVILEGED : LLOAD_C_OPEN;
+ }
+ }
+}
+
+int
+lload_handle_invalidation( LloadChange *change )
+{
+ if ( (change->type == LLOAD_CHANGE_MODIFY) &&
+ change->flags.generic == 0 ) {
+ Debug( LDAP_DEBUG_ANY, "lload_handle_invalidation: "
+ "a modify where apparently nothing changed\n" );
+ }
+
+ switch ( change->object ) {
+ case LLOAD_BACKEND:
+ lload_handle_backend_invalidation( change );
+ break;
+ case LLOAD_DAEMON:
+ lload_handle_global_invalidation( change );
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY, "lload_handle_invalidation: "
+ "unrecognised change\n" );
+ assert(0);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static void
+lload_pause_event_cb( evutil_socket_t s, short what, void *arg )
+{
+ /*
+ * We are pausing, signal the pausing thread we've finished and
+ * wait until the thread pool resumes operation.
+ *
+ * Do this in lockstep with the pausing thread.
+ */
+ checked_lock( &lload_wait_mutex );
+ ldap_pvt_thread_cond_signal( &lload_wait_cond );
+
+ /* Now wait until we unpause, then we can resume operation */
+ ldap_pvt_thread_cond_wait( &lload_pause_cond, &lload_wait_mutex );
+ checked_unlock( &lload_wait_mutex );
+}
+
+/*
+ * Signal the event base to terminate processing as soon as it can and wait for
+ * lload_pause_event_cb to notify us this has happened.
+ */
+static int
+lload_pause_base( struct event_base *base )
+{
+ int rc;
+
+ checked_lock( &lload_wait_mutex );
+ event_base_once( base, -1, EV_TIMEOUT, lload_pause_event_cb, base, NULL );
+ rc = ldap_pvt_thread_cond_wait( &lload_wait_cond, &lload_wait_mutex );
+ checked_unlock( &lload_wait_mutex );
+
+ return rc;
+}
+
+void
+lload_pause_server( void )
+{
+ LloadChange ch = { .type = LLOAD_CHANGE_UNDEFINED };
+ int i;
+
+ lload_pause_base( listener_base );
+ lload_pause_base( daemon_base );
+
+ for ( i = 0; i < lload_daemon_threads; i++ ) {
+ lload_pause_base( lload_daemon[i].base );
+ }
+
+ lload_change = ch;
+}
+
+void
+lload_unpause_server( void )
+{
+ if ( lload_change.type != LLOAD_CHANGE_UNDEFINED ) {
+ lload_handle_invalidation( &lload_change );
+ }
+
+ /*
+ * Make sure lloadd is completely ready to unpause by now:
+ *
+ * After the broadcast, we handle I/O and begin filling the thread pool, in
+ * high load conditions, we might hit the pool limits and start processing
+ * operations in the I/O threads (one PDU per socket at a time for fairness
+ * sake) even before a pause has finished from slapd's point of view!
+ *
+ * When (max_pdus_per_cycle == 0) we don't use the pool for these at all and
+ * most lload processing starts immediately making this even more prominent.
+ */
+ ldap_pvt_thread_cond_broadcast( &lload_pause_cond );
+}
+#endif /* BALANCER_MODULE */
+
+void
+lload_sig_shutdown( evutil_socket_t sig, short what, void *arg )
+{
+ struct event_base *daemon_base = arg;
+ int save_errno = errno;
+ int i;
+
+ /*
+ * If the NT Service Manager is controlling the server, we don't
+ * want SIGBREAK to kill the server. For some strange reason,
+ * SIGBREAK is generated when a user logs out.
+ */
+
+#if defined(HAVE_NT_SERVICE_MANAGER) && defined(SIGBREAK)
+ if ( is_NT_Service && sig == SIGBREAK ) {
+ /* empty */;
+ } else
+#endif /* HAVE_NT_SERVICE_MANAGER && SIGBREAK */
+#ifdef SIGHUP
+ if ( sig == SIGHUP && global_gentlehup && slapd_gentle_shutdown == 0 ) {
+ slapd_gentle_shutdown = 1;
+ } else
+#endif /* SIGHUP */
+ {
+ slapd_shutdown = 1;
+ }
+
+ for ( i = 0; i < lload_daemon_threads; i++ ) {
+ event_base_loopexit( lload_daemon[i].base, NULL );
+ }
+ event_base_loopexit( daemon_base, NULL );
+
+ errno = save_errno;
+}
+
+struct event_base *
+lload_get_base( ber_socket_t s )
+{
+ int tid = DAEMON_ID(s);
+ return lload_daemon[tid].base;
+}
+
+LloadListener **
+lloadd_get_listeners( void )
+{
+ /* Could return array with no listeners if !listening, but current
+ * callers mostly look at the URLs. E.g. syncrepl uses this to
+ * identify the server, which means it wants the startup arguments.
+ */
+ return lload_listeners;
+}
+
+/* Reject all incoming requests */
+void
+lload_suspend_listeners( void )
+{
+ int i;
+ for ( i = 0; lload_listeners[i]; i++ ) {
+ lload_listeners[i]->sl_mute = 1;
+ evconnlistener_disable( lload_listeners[i]->listener );
+ listen( lload_listeners[i]->sl_sd, 0 );
+ }
+}
+
+/* Resume after a suspend */
+void
+lload_resume_listeners( void )
+{
+ int i;
+ for ( i = 0; lload_listeners[i]; i++ ) {
+ lload_listeners[i]->sl_mute = 0;
+ listen( lload_listeners[i]->sl_sd, SLAPD_LISTEN_BACKLOG );
+ evconnlistener_enable( lload_listeners[i]->listener );
+ }
+}
diff --git a/servers/lloadd/design.md b/servers/lloadd/design.md
new file mode 120000
index 0000000..757e340
--- /dev/null
+++ b/servers/lloadd/design.md
@@ -0,0 +1 @@
+../../doc/devel/lloadd/design.md \ No newline at end of file
diff --git a/servers/lloadd/epoch.c b/servers/lloadd/epoch.c
new file mode 100644
index 0000000..3574f0b
--- /dev/null
+++ b/servers/lloadd/epoch.c
@@ -0,0 +1,339 @@
+/* epoch.c - epoch based memory reclamation */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2018-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/** @file epoch.c
+ *
+ * Implementation of epoch based memory reclamation, in principle
+ * similar to the algorithm presented in
+ * https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-579.pdf
+ *
+ * Not completely lock-free at the moment.
+ *
+ * Also the problems with epoch based memory reclamation are still
+ * present - a thread actively observing an epoch getting stuck will
+ * prevent managed objects (in our case connections and operations)
+ * from being freed, potentially running out of memory.
+ */
+
+#include "portable.h"
+
+#include "lload.h"
+#include <epoch.h>
+
+/* Has to be >= 3 */
+#define EPOCH_MASK ( 1 << 2 )
+#define EPOCH_PREV(epoch) ( ( (epoch) + EPOCH_MASK - 1 ) % EPOCH_MASK )
+#define EPOCH_NEXT(epoch) ( ( (epoch) + 1 ) % EPOCH_MASK )
+
+struct pending_ref {
+ void *object;
+ dispose_cb *dispose;
+ struct pending_ref *next;
+};
+
+ldap_pvt_thread_rdwr_t epoch_mutex;
+
+static epoch_t current_epoch;
+static uintptr_t epoch_threads[EPOCH_MASK];
+static struct pending_ref *references[EPOCH_MASK];
+
+void
+epoch_init( void )
+{
+ epoch_t epoch;
+
+ current_epoch = 0;
+ for ( epoch = 0; epoch < EPOCH_MASK; epoch++ ) {
+ assert( !epoch_threads[epoch] );
+ assert( !references[epoch] );
+ }
+
+ ldap_pvt_thread_rdwr_init( &epoch_mutex );
+}
+
+void
+epoch_shutdown( void )
+{
+ epoch_t epoch;
+ struct pending_ref *old, *next;
+
+ for ( epoch = 0; epoch < EPOCH_MASK; epoch++ ) {
+ assert( !epoch_threads[epoch] );
+ }
+
+ /*
+ * Even with the work in epoch_leave(), shutdown code doesn't currently
+ * observe any epoch, so there might still be references left to free.
+ */
+ epoch = EPOCH_PREV(current_epoch);
+ next = references[epoch];
+ references[epoch] = NULL;
+ for ( old = next; old; old = next ) {
+ next = old->next;
+
+ old->dispose( old->object );
+ ch_free( old );
+ }
+
+ epoch = current_epoch;
+ next = references[epoch];
+ references[epoch] = NULL;
+ for ( old = next; old; old = next ) {
+ next = old->next;
+
+ old->dispose( old->object );
+ ch_free( old );
+ }
+
+ /* No references should exist anywhere now */
+ for ( epoch = 0; epoch < EPOCH_MASK; epoch++ ) {
+ assert( !references[epoch] );
+ }
+
+ ldap_pvt_thread_rdwr_destroy( &epoch_mutex );
+}
+
+epoch_t
+epoch_join( void )
+{
+ epoch_t epoch;
+ struct pending_ref *old, *ref = NULL;
+
+retry:
+ /* TODO: make this completely lock-free */
+ ldap_pvt_thread_rdwr_rlock( &epoch_mutex );
+ epoch = current_epoch;
+ __atomic_add_fetch( &epoch_threads[epoch], 1, __ATOMIC_ACQ_REL );
+ ldap_pvt_thread_rdwr_runlock( &epoch_mutex );
+
+ if ( __atomic_load_n(
+ &epoch_threads[EPOCH_PREV(epoch)], __ATOMIC_ACQUIRE ) ) {
+ return epoch;
+ }
+
+ __atomic_exchange(
+ &references[EPOCH_PREV(epoch)], &ref, &ref, __ATOMIC_ACQ_REL );
+
+ Debug( LDAP_DEBUG_TRACE, "epoch_join: "
+ "advancing epoch to %zu with %s objects to free\n",
+ EPOCH_NEXT(epoch), ref ? "some" : "no" );
+
+ ldap_pvt_thread_rdwr_wlock( &epoch_mutex );
+ current_epoch = EPOCH_NEXT(epoch);
+ ldap_pvt_thread_rdwr_wunlock( &epoch_mutex );
+
+ if ( !ref ) {
+ return epoch;
+ }
+
+ /*
+ * The below is now safe to free outside epochs and we don't want to make
+ * the current epoch last any longer than necessary.
+ *
+ * Looks like there might be fairness issues in massively parallel
+ * environments but they haven't been observed on 32-core machines.
+ */
+ epoch_leave( epoch );
+
+ for ( old = ref; old; old = ref ) {
+ ref = old->next;
+
+ old->dispose( old->object );
+ ch_free( old );
+ }
+
+ goto retry;
+}
+
+void
+epoch_leave( epoch_t epoch )
+{
+ struct pending_ref *p, *next, *old_refs = NULL, *current_refs = NULL;
+
+ /* Are there other threads observing our epoch? */
+ if ( __atomic_sub_fetch( &epoch_threads[epoch], 1, __ATOMIC_ACQ_REL ) ) {
+ return;
+ }
+
+ /*
+ * Optimisation for the case when we're mostly idle. Otherwise we won't
+ * release resources until another thread comes by and joins the epoch
+ * (twice), and there's no idea how soon (or late) that is going to happen.
+ *
+ * NB. There is no limit to the number of threads executing the following
+ * code in parallel.
+ */
+ ldap_pvt_thread_rdwr_rlock( &epoch_mutex );
+ /*
+ * Anything could happen between the subtract and the lock being acquired
+ * above, so check again. But once we hold this lock (and confirm no more
+ * threads still observe either prospective epoch), noone will be able to
+ * finish epoch_join until we've released epoch_mutex since it holds that:
+ *
+ * epoch_threads[EPOCH_PREV(current_epoch)] == 0
+ *
+ * and that leads epoch_join() to acquire a write lock on &epoch_mutex.
+ */
+ if ( __atomic_load_n( &epoch_threads[epoch], __ATOMIC_RELAXED ) ) {
+ /* Epoch counter has run full circle */
+ ldap_pvt_thread_rdwr_runlock( &epoch_mutex );
+ return;
+ } else if ( epoch == current_epoch ) {
+ if ( __atomic_load_n(
+ &epoch_threads[EPOCH_PREV(epoch)], __ATOMIC_RELAXED ) ) {
+ /* There is another (older) thread still running */
+ ldap_pvt_thread_rdwr_runlock( &epoch_mutex );
+ return;
+ }
+
+ /* We're all alone, it's safe to claim all references and free them. */
+ __atomic_exchange( &references[EPOCH_PREV(epoch)], &old_refs,
+ &old_refs, __ATOMIC_ACQ_REL );
+ __atomic_exchange( &references[epoch], &current_refs, &current_refs,
+ __ATOMIC_ACQ_REL );
+ } else if ( epoch == EPOCH_PREV(current_epoch) ) {
+ if ( __atomic_load_n(
+ &epoch_threads[EPOCH_NEXT(epoch)], __ATOMIC_RELAXED ) ) {
+ /* There is another (newer) thread still running */
+ ldap_pvt_thread_rdwr_runlock( &epoch_mutex );
+ return;
+ }
+
+ /* We're all alone, it's safe to claim all references and free them. */
+ __atomic_exchange(
+ &references[epoch], &old_refs, &old_refs, __ATOMIC_ACQ_REL );
+ __atomic_exchange( &references[EPOCH_NEXT(epoch)], &current_refs,
+ &current_refs, __ATOMIC_ACQ_REL );
+ }
+ /*
+ * Else the current_epoch has moved far enough that no references remain to
+ * be freed.
+ */
+ ldap_pvt_thread_rdwr_runlock( &epoch_mutex );
+
+ /*
+ * Trigger a memory-independent read fence to make sure we're reading the
+ * state after all threads actually finished - which might have happened
+ * after we acquired epoch_mutex so ldap_pvt_thread_rdwr_rlock would not
+ * catch everything.
+ *
+ * TODO is to confirm the below:
+ * It might be that the tests and exchanges above only enforce a fence for
+ * the locations affected, so we could still read stale memory for
+ * unrelated locations? At least that's the only explanation I've been able
+ * to establish for repeated crashes that seem to have gone away with this
+ * in place.
+ *
+ * But then that's contrary to the second example in Acquire/Release
+ * section here:
+ * https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync
+ */
+ __atomic_thread_fence( __ATOMIC_ACQUIRE );
+
+ for ( p = old_refs; p; p = next ) {
+ next = p->next;
+
+ p->dispose( p->object );
+ ch_free( p );
+ }
+
+ for ( p = current_refs; p; p = next ) {
+ next = p->next;
+
+ p->dispose( p->object );
+ ch_free( p );
+ }
+}
+
+/*
+ * Add the object to the "current global epoch", not the epoch our thread
+ * entered.
+ */
+void
+epoch_append( void *ptr, dispose_cb *cb )
+{
+ struct pending_ref *new;
+ epoch_t epoch = __atomic_load_n( &current_epoch, __ATOMIC_ACQUIRE );
+
+ /*
+ * BTW, the following is not appropriate here:
+ * assert( __atomic_load_n( &epoch_threads[epoch], __ATOMIC_RELAXED ) );
+ *
+ * We might be a thread lagging behind in the "previous epoch" with no
+ * other threads executing at all.
+ */
+
+ new = ch_malloc( sizeof(struct pending_ref) );
+ new->object = ptr;
+ new->dispose = cb;
+ new->next = __atomic_load_n( &references[epoch], __ATOMIC_ACQUIRE );
+
+ while ( !__atomic_compare_exchange( &references[epoch], &new->next, &new, 0,
+ __ATOMIC_RELEASE, __ATOMIC_RELAXED ) )
+ /* iterate until we succeed */;
+}
+
+int
+acquire_ref( uintptr_t *refp )
+{
+ uintptr_t refcnt, new_refcnt;
+
+ refcnt = __atomic_load_n( refp, __ATOMIC_ACQUIRE );
+
+ /*
+ * If we just incremented the refcnt and checked for zero after, another
+ * thread might falsely believe the object was going to stick around.
+ *
+ * Checking whether the object is still dead at disposal time might not be
+ * able to distinguish it from being freed in a later epoch.
+ */
+ do {
+ if ( !refcnt ) {
+ return refcnt;
+ }
+
+ new_refcnt = refcnt + 1;
+ } while ( !__atomic_compare_exchange( refp, &refcnt, &new_refcnt, 0,
+ __ATOMIC_RELEASE, __ATOMIC_RELAXED ) );
+ assert( new_refcnt == refcnt + 1 );
+
+ return refcnt;
+}
+
+int
+try_release_ref( uintptr_t *refp, void *object, dispose_cb *cb )
+{
+ uintptr_t refcnt, new_refcnt;
+
+ refcnt = __atomic_load_n( refp, __ATOMIC_ACQUIRE );
+
+ /* We promise the caller that we won't decrease refcnt below 0 */
+ do {
+ if ( !refcnt ) {
+ return refcnt;
+ }
+
+ new_refcnt = refcnt - 1;
+ } while ( !__atomic_compare_exchange( refp, &refcnt, &new_refcnt, 0,
+ __ATOMIC_RELEASE, __ATOMIC_RELAXED ) );
+ assert( new_refcnt == refcnt - 1 );
+
+ if ( !new_refcnt ) {
+ epoch_append( object, cb );
+ }
+
+ return refcnt;
+}
diff --git a/servers/lloadd/epoch.h b/servers/lloadd/epoch.h
new file mode 100644
index 0000000..c552ef0
--- /dev/null
+++ b/servers/lloadd/epoch.h
@@ -0,0 +1,144 @@
+/* epoch.h - epoch based memory reclamation */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2018-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef __LLOAD_EPOCH_H
+#define __LLOAD_EPOCH_H
+
+/** @file epoch.h
+ *
+ * Implementation of epoch based memory reclamation, in principle
+ * similar to the algorithm presented in
+ * https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-579.pdf
+ */
+
+typedef uintptr_t epoch_t;
+
+/** @brief A callback function used to free object and associated data */
+typedef void (dispose_cb)( void *object );
+
+/** @brief Initiate global state */
+void epoch_init( void );
+
+/** @brief Finalise global state and free any objects still pending */
+void epoch_shutdown( void );
+
+/** @brief Register thread as active
+ *
+ * In order to safely access managed objects, a thread should call
+ * this function or make sure no other thread is running (e.g. config
+ * pause, late shutdown). After calling this, it is guaranteed that no
+ * reachable objects will be freed before all threads have called
+ * `epoch_leave( current_epoch + 1 )` so it is essential that there
+ * is an upper limit to the amount of time between #epoch_join and
+ * corresponding #epoch_leave or the number of unfreed objects might
+ * grow without bounds.
+ *
+ * To simplify locking, memory is only freed when the current epoch
+ * is advanced rather than on leaving it.
+ *
+ * Can be safely called multiple times by the same thread as long as
+ * a matching #epoch_leave() call is made eventually.
+ *
+ * @return The observed epoch, to be passed to #epoch_leave()
+ */
+epoch_t epoch_join( void );
+
+/** @brief Register thread as inactive
+ *
+ * A thread should call this after they are finished with work
+ * performed since matching call to #epoch_join(). It is not safe
+ * to keep a local reference to managed objects after this call
+ * unless other precautions have been made to prevent it being
+ * released.
+ *
+ * @param[in] epoch Epoch identifier returned by a previous call to
+ * #epoch_join().
+ */
+void epoch_leave( epoch_t epoch );
+
+/** @brief Return an unreachable object to be freed
+ *
+ * The object should already be unreachable at the point of call and
+ * cb will be invoked when no other thread that could have seen it
+ * is active any more. This happens when we have advanced by two
+ * epochs.
+ *
+ * @param[in] ptr Object to be released/freed
+ * @param[in] cb Callback to invoke when safe to do so
+ */
+void epoch_append( void *ptr, dispose_cb *cb );
+
+/**
+ * \defgroup Reference counting helpers
+ */
+/**@{*/
+
+/** @brief Acquire a reference if possible
+ *
+ * Atomically, check reference count is non-zero and increment if so.
+ * Returns old reference count.
+ *
+ * @param[in] refp Pointer to a reference counter
+ * @return 0 if reference was already zero, non-zero if reference
+ * count was successfully incremented
+ */
+int acquire_ref( uintptr_t *refp );
+
+/** @brief Check reference count and try to decrement
+ *
+ * Atomically, decrement reference count if non-zero and register
+ * object if decremented to zero. Returning previous reference count.
+ *
+ * @param[in] refp Pointer to a reference counter
+ * @param[in] object The managed object
+ * @param[in] cb Callback to invoke when safe to do so
+ * @return 0 if reference was already zero, non-zero if reference
+ * count was non-zero at the time of call
+ */
+int try_release_ref( uintptr_t *refp, void *object, dispose_cb *cb );
+
+/** @brief Read reference count
+ *
+ * @param[in] object Pointer to the managed object
+ * @param[in] ref_field Member where reference count is stored in
+ * the object
+ * @return Current value of reference counter
+ */
+#define IS_ALIVE( object, ref_field ) \
+ __atomic_load_n( &(object)->ref_field, __ATOMIC_ACQUIRE )
+
+/** @brief Release reference
+ *
+ * A cheaper alternative to #try_release_ref(), safe only when we know
+ * reference count was already non-zero.
+ *
+ * @param[in] object The managed object
+ * @param[in] ref_field Member where reference count is stored in
+ * the object
+ * @param[in] cb Callback to invoke when safe to do so
+ */
+#define RELEASE_REF( object, ref_field, cb ) \
+ do { \
+ assert( IS_ALIVE( (object), ref_field ) ); \
+ if ( !__atomic_sub_fetch( \
+ &(object)->ref_field, 1, __ATOMIC_ACQ_REL ) ) { \
+ epoch_append( object, (dispose_cb *)cb ); \
+ } \
+ } while (0)
+
+/**@}*/
+
+#endif /* __LLOAD_EPOCH_H */
diff --git a/servers/lloadd/extended.c b/servers/lloadd/extended.c
new file mode 100644
index 0000000..43ea589
--- /dev/null
+++ b/servers/lloadd/extended.c
@@ -0,0 +1,203 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <ac/string.h>
+
+#include "lutil.h"
+#include "lload.h"
+
+Avlnode *lload_exop_handlers = NULL;
+
+#ifdef HAVE_TLS
+void *lload_tls_ctx;
+LDAP *lload_tls_ld, *lload_tls_backend_ld;
+#ifdef BALANCER_MODULE
+int lload_use_slap_tls_ctx = 0;
+#endif
+#endif /* HAVE_TLS */
+
+int
+handle_starttls( LloadConnection *c, LloadOperation *op )
+{
+ struct event_base *base = event_get_base( c->c_read_event );
+ LloadOperation *found;
+ BerElement *output;
+ char *msg = NULL;
+ int rc = LDAP_SUCCESS;
+
+ CONNECTION_LOCK(c);
+ found = ldap_tavl_delete( &c->c_ops, op, operation_client_cmp );
+ assert( op == found );
+ c->c_n_ops_executing--;
+
+#ifdef HAVE_TLS
+ if ( c->c_is_tls == LLOAD_TLS_ESTABLISHED ) {
+ rc = LDAP_OPERATIONS_ERROR;
+ msg = "TLS layer already in effect";
+ } else if ( c->c_state == LLOAD_C_BINDING ) {
+ rc = LDAP_OPERATIONS_ERROR;
+ msg = "bind in progress";
+ } else if ( c->c_ops ) {
+ rc = LDAP_OPERATIONS_ERROR;
+ msg = "cannot start TLS when operations are outstanding";
+ } else if ( !LLOAD_TLS_CTX ) {
+ rc = LDAP_UNAVAILABLE;
+ msg = "Could not initialize TLS";
+ }
+#else /* ! HAVE_TLS */
+ rc = LDAP_UNAVAILABLE;
+ msg = "Could not initialize TLS";
+#endif /* ! HAVE_TLS */
+
+ CONNECTION_UNLOCK(c);
+
+ Debug( LDAP_DEBUG_STATS, "handle_starttls: "
+ "handling StartTLS exop connid=%lu rc=%d msg=%s\n",
+ c->c_connid, rc, msg );
+
+ if ( rc ) {
+ /* We've already removed the operation from the queue */
+ operation_send_reject( op, rc, msg, 1 );
+ return LDAP_SUCCESS;
+ }
+
+#ifdef HAVE_TLS
+ event_del( c->c_read_event );
+ event_del( c->c_write_event );
+ /*
+ * At this point, we are the only thread handling the connection:
+ * - there are no upstream operations
+ * - the I/O callbacks have been successfully removed
+ *
+ * This means we can safely reconfigure both I/O events now.
+ */
+
+ checked_lock( &c->c_io_mutex );
+ output = c->c_pendingber;
+ if ( output == NULL && (output = ber_alloc()) == NULL ) {
+ checked_unlock( &c->c_io_mutex );
+ operation_unlink( op );
+ CONNECTION_LOCK_DESTROY(c);
+ return -1;
+ }
+ c->c_pendingber = output;
+ ber_printf( output, "t{tit{ess}}", LDAP_TAG_MESSAGE,
+ LDAP_TAG_MSGID, op->o_client_msgid,
+ LDAP_RES_EXTENDED, LDAP_SUCCESS, "", "" );
+ c->c_io_state &= ~LLOAD_C_READ_HANDOVER;
+ checked_unlock( &c->c_io_mutex );
+
+ CONNECTION_LOCK(c);
+ c->c_read_timeout = lload_timeout_net;
+ event_assign( c->c_read_event, base, c->c_fd, EV_READ|EV_PERSIST,
+ client_tls_handshake_cb, c );
+ event_add( c->c_read_event, c->c_read_timeout );
+
+ event_assign( c->c_write_event, base, c->c_fd, EV_WRITE,
+ client_tls_handshake_cb, c );
+ /* We already have something to write */
+ event_add( c->c_write_event, lload_write_timeout );
+
+ op->o_res = LLOAD_OP_COMPLETED;
+ CONNECTION_UNLOCK(c);
+
+ operation_unlink( op );
+
+ return -1;
+#endif /* HAVE_TLS */
+}
+
+int
+request_extended( LloadConnection *c, LloadOperation *op )
+{
+ ExopHandler *handler, needle = {};
+ BerElement *copy;
+ struct berval bv;
+ ber_tag_t tag;
+
+ if ( (copy = ber_alloc()) == NULL ) {
+ operation_send_reject( op, LDAP_OTHER, "internal error", 0 );
+ CONNECTION_LOCK_DESTROY(c);
+ return -1;
+ }
+
+ ber_init2( copy, &op->o_request, 0 );
+
+ tag = ber_skip_element( copy, &bv );
+ if ( tag != LDAP_TAG_EXOP_REQ_OID ) {
+ Debug( LDAP_DEBUG_STATS, "request_extended: "
+ "no OID present in extended request\n" );
+ operation_send_reject( op, LDAP_PROTOCOL_ERROR, "decoding error", 0 );
+ CONNECTION_LOCK_DESTROY(c);
+ return -1;
+ }
+
+ needle.oid = bv;
+
+ handler = ldap_avl_find( lload_exop_handlers, &needle, exop_handler_cmp );
+ if ( handler ) {
+ Debug( LDAP_DEBUG_TRACE, "request_extended: "
+ "handling exop OID %.*s internally\n",
+ (int)bv.bv_len, bv.bv_val );
+ ber_free( copy, 0 );
+ return handler->func( c, op );
+ }
+ ber_free( copy, 0 );
+
+ return request_process( c, op );
+}
+
+ExopHandler lload_exops[] = {
+ { BER_BVC(LDAP_EXOP_START_TLS), handle_starttls },
+ { BER_BVNULL }
+};
+
+int
+exop_handler_cmp( const void *left, const void *right )
+{
+ const struct lload_exop_handlers_t *l = left, *r = right;
+ return ber_bvcmp( &l->oid, &r->oid );
+}
+
+int
+lload_register_exop_handlers( struct lload_exop_handlers_t *handler )
+{
+ for ( ; !BER_BVISNULL( &handler->oid ); handler++ ) {
+ Debug( LDAP_DEBUG_TRACE, "lload_register_exop_handlers: "
+ "registering handler for exop oid=%s\n",
+ handler->oid.bv_val );
+ if ( ldap_avl_insert( &lload_exop_handlers, handler, exop_handler_cmp,
+ ldap_avl_dup_error ) ) {
+ Debug( LDAP_DEBUG_ANY, "lload_register_exop_handlers: "
+ "failed to register handler for exop oid=%s\n",
+ handler->oid.bv_val );
+ return -1;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int
+lload_exop_init( void )
+{
+ if ( lload_register_exop_handlers( lload_exops ) ) {
+ return -1;
+ }
+
+ return LDAP_SUCCESS;
+}
diff --git a/servers/lloadd/init.c b/servers/lloadd/init.c
new file mode 100644
index 0000000..7dbe58f
--- /dev/null
+++ b/servers/lloadd/init.c
@@ -0,0 +1,211 @@
+/* init.c - initialize various things */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "lload.h"
+#include "lber_pvt.h"
+
+#include "ldap_rq.h"
+
+#ifndef BALANCER_MODULE
+/*
+ * read-only global variables or variables only written by the listener
+ * thread (after they are initialized) - no need to protect them with a mutex.
+ */
+int slap_debug = 0;
+
+#ifdef LDAP_DEBUG
+int ldap_syslog = LDAP_DEBUG_STATS;
+#else
+int ldap_syslog;
+#endif
+
+#ifdef LOG_DEBUG
+int ldap_syslog_level = LOG_DEBUG;
+#endif
+
+/*
+ * global variables that need mutex protection
+ */
+ldap_pvt_thread_pool_t connection_pool;
+int connection_pool_max = SLAP_MAX_WORKER_THREADS;
+int connection_pool_queues = 1;
+int slap_tool_thread_max = 1;
+
+int slapMode = SLAP_UNDEFINED_MODE;
+#endif /* !BALANCER_MODULE */
+
+static const char *lload_name = NULL;
+
+int
+lload_global_init( void )
+{
+ int rc;
+
+ if ( lload_libevent_init() ) {
+ return -1;
+ }
+
+#ifdef HAVE_TLS
+ if ( ldap_create( &lload_tls_backend_ld ) ) {
+ return -1;
+ }
+ if ( ldap_create( &lload_tls_ld ) ) {
+ return -1;
+ }
+
+ /* Library defaults to full certificate checking. This is correct when
+ * a client is verifying a server because all servers should have a
+ * valid cert. But few clients have valid certs, so we want our default
+ * to be no checking. The config file can override this as usual.
+ */
+ rc = LDAP_OPT_X_TLS_NEVER;
+ (void)ldap_pvt_tls_set_option(
+ lload_tls_ld, LDAP_OPT_X_TLS_REQUIRE_CERT, &rc );
+#endif
+
+ ldap_pvt_thread_mutex_init( &lload_wait_mutex );
+ ldap_pvt_thread_cond_init( &lload_wait_cond );
+ ldap_pvt_thread_cond_init( &lload_pause_cond );
+
+ ldap_pvt_thread_mutex_init( &backend_mutex );
+ ldap_pvt_thread_mutex_init( &clients_mutex );
+ ldap_pvt_thread_mutex_init( &lload_pin_mutex );
+
+ if ( lload_exop_init() ) {
+ return -1;
+ }
+ return 0;
+}
+
+int
+lload_tls_init( void )
+{
+#ifdef HAVE_TLS
+ int rc, opt = 1;
+
+ /* Force new ctx to be created */
+ rc = ldap_pvt_tls_set_option( lload_tls_ld, LDAP_OPT_X_TLS_NEWCTX, &opt );
+ if ( rc == 0 ) {
+ /* The ctx's refcount is bumped up here */
+ ldap_pvt_tls_get_option(
+ lload_tls_ld, LDAP_OPT_X_TLS_CTX, &lload_tls_ctx );
+ } else if ( rc != LDAP_NOT_SUPPORTED ) {
+ Debug( LDAP_DEBUG_ANY, "lload_global_init: "
+ "TLS init def ctx failed: %d\n",
+ rc );
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+int
+lload_init( int mode, const char *name )
+{
+ int rc = LDAP_SUCCESS;
+
+ assert( mode );
+
+ if ( slapMode != SLAP_UNDEFINED_MODE ) {
+ /* Make sure we write something to stderr */
+ slap_debug |= LDAP_DEBUG_NONE;
+ Debug( LDAP_DEBUG_ANY, "%s init: "
+ "init called twice (old=%d, new=%d)\n",
+ name, slapMode, mode );
+
+ return 1;
+ }
+
+ slapMode = mode;
+
+ switch ( slapMode & SLAP_MODE ) {
+ case SLAP_SERVER_MODE:
+ Debug( LDAP_DEBUG_TRACE, "%s init: "
+ "initiated server.\n",
+ name );
+
+ lload_name = name;
+
+ ldap_pvt_thread_pool_init_q( &connection_pool, connection_pool_max,
+ 0, connection_pool_queues );
+
+ ldap_pvt_thread_mutex_init( &slapd_rq.rq_mutex );
+ LDAP_STAILQ_INIT( &slapd_rq.task_list );
+ LDAP_STAILQ_INIT( &slapd_rq.run_list );
+
+ rc = lload_global_init();
+ break;
+
+ default:
+ slap_debug |= LDAP_DEBUG_NONE;
+ Debug( LDAP_DEBUG_ANY, "%s init: "
+ "undefined mode (%d).\n",
+ name, mode );
+
+ rc = 1;
+ break;
+ }
+
+ return rc;
+}
+
+int
+lload_destroy( void )
+{
+ int rc = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_TRACE, "%s destroy: "
+ "freeing system resources.\n",
+ lload_name );
+
+ ldap_pvt_thread_pool_free( &connection_pool );
+
+ switch ( slapMode & SLAP_MODE ) {
+ case SLAP_SERVER_MODE:
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY, "lload_destroy(): "
+ "undefined mode (%d).\n",
+ slapMode );
+
+ rc = 1;
+ break;
+ }
+
+ ldap_pvt_thread_destroy();
+
+ /* should destroy the above mutex */
+ return rc;
+}
diff --git a/servers/lloadd/libevent_support.c b/servers/lloadd/libevent_support.c
new file mode 100644
index 0000000..2f94c5e
--- /dev/null
+++ b/servers/lloadd/libevent_support.c
@@ -0,0 +1,171 @@
+/* libevent_support.c - routines to bridge libldap and libevent */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2017-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <ac/time.h>
+
+#include <event2/event.h>
+#include <event2/thread.h>
+
+#include "lload.h"
+#include "ldap_pvt_thread.h"
+
+static void *
+lload_libevent_mutex_init( unsigned locktype )
+{
+ int rc;
+ ldap_pvt_thread_mutex_t *mutex =
+ ch_malloc( sizeof(ldap_pvt_thread_mutex_t) );
+
+ if ( locktype & EVTHREAD_LOCKTYPE_RECURSIVE ) {
+ rc = ldap_pvt_thread_mutex_recursive_init( mutex );
+ } else {
+ rc = ldap_pvt_thread_mutex_init( mutex );
+ }
+ if ( rc ) {
+ ch_free( mutex );
+ mutex = NULL;
+ }
+ return mutex;
+}
+
+static void
+lload_libevent_mutex_destroy( void *lock, unsigned locktype )
+{
+ int rc;
+ ldap_pvt_thread_mutex_t *mutex = lock;
+
+ rc = ldap_pvt_thread_mutex_destroy( mutex );
+ assert( rc == 0 );
+ ch_free( mutex );
+}
+
+static int
+lload_libevent_mutex_lock( unsigned mode, void *lock )
+{
+ ldap_pvt_thread_mutex_t *mutex = lock;
+
+ if ( mode & EVTHREAD_TRY ) {
+ return ldap_pvt_thread_mutex_trylock( mutex );
+ } else {
+ return ldap_pvt_thread_mutex_lock( mutex );
+ }
+}
+
+static int
+lload_libevent_mutex_unlock( unsigned mode, void *lock )
+{
+ ldap_pvt_thread_mutex_t *mutex = lock;
+
+ return ldap_pvt_thread_mutex_unlock( mutex );
+}
+
+static void *
+lload_libevent_cond_init( unsigned condtype )
+{
+ int rc;
+ ldap_pvt_thread_cond_t *cond =
+ ch_malloc( sizeof(ldap_pvt_thread_cond_t) );
+
+ assert( condtype == 0 );
+ rc = ldap_pvt_thread_cond_init( cond );
+ if ( rc ) {
+ ch_free( cond );
+ cond = NULL;
+ }
+ return cond;
+}
+
+static void
+lload_libevent_cond_destroy( void *c )
+{
+ int rc;
+ ldap_pvt_thread_cond_t *cond = c;
+
+ rc = ldap_pvt_thread_cond_destroy( cond );
+ assert( rc == 0 );
+ ch_free( c );
+}
+
+static int
+lload_libevent_cond_signal( void *c, int broadcast )
+{
+ ldap_pvt_thread_cond_t *cond = c;
+
+ if ( broadcast ) {
+ return ldap_pvt_thread_cond_broadcast( cond );
+ } else {
+ return ldap_pvt_thread_cond_signal( cond );
+ }
+}
+
+static int
+lload_libevent_cond_timedwait(
+ void *c,
+ void *lock,
+ const struct timeval *timeout )
+{
+ ldap_pvt_thread_cond_t *cond = c;
+ ldap_pvt_thread_mutex_t *mutex = lock;
+
+ /*
+ * libevent does not seem to request a timeout, this is true as of 2.1.8
+ * that has just been marked the first stable release of the 2.1 series
+ */
+ assert( timeout == NULL );
+
+ return ldap_pvt_thread_cond_wait( cond, mutex );
+}
+
+int
+lload_libevent_init( void )
+{
+ struct evthread_lock_callbacks cbs = {
+ EVTHREAD_LOCK_API_VERSION,
+ EVTHREAD_LOCKTYPE_RECURSIVE,
+ lload_libevent_mutex_init,
+ lload_libevent_mutex_destroy,
+ lload_libevent_mutex_lock,
+ lload_libevent_mutex_unlock
+ };
+ struct evthread_condition_callbacks cond_cbs = {
+ EVTHREAD_CONDITION_API_VERSION,
+ lload_libevent_cond_init,
+ lload_libevent_cond_destroy,
+ lload_libevent_cond_signal,
+ lload_libevent_cond_timedwait
+ };
+
+#ifndef BALANCER_MODULE
+ /* only necessary if lload is a server, slapd already calls
+ * ldap_pvt_thread_initialize() */
+ if ( ldap_pvt_thread_initialize() ) {
+ return -1;
+ }
+#endif
+
+ evthread_set_lock_callbacks( &cbs );
+ evthread_set_condition_callbacks( &cond_cbs );
+ evthread_set_id_callback( ldap_pvt_thread_self );
+ return 0;
+}
+
+void
+lload_libevent_destroy( void )
+{
+ libevent_global_shutdown();
+}
diff --git a/servers/lloadd/lload-config.h b/servers/lloadd/lload-config.h
new file mode 100644
index 0000000..e8ab431
--- /dev/null
+++ b/servers/lloadd/lload-config.h
@@ -0,0 +1,39 @@
+/* lload-config.h - configuration abstraction structure */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef LLOAD_CONFIG_H /* not CONFIG_H because it overlaps with the one from slapd */
+#define LLOAD_CONFIG_H
+
+#include <ac/string.h>
+#include "../slapd/slap-config.h"
+
+LDAP_BEGIN_DECL
+
+int lload_config_fp_parse_line( ConfigArgs *c );
+
+int lload_config_get_vals( ConfigTable *ct, ConfigArgs *c );
+int lload_config_add_vals( ConfigTable *ct, ConfigArgs *c );
+
+void lload_init_config_argv( ConfigArgs *c );
+int lload_read_config_file( const char *fname, int depth, ConfigArgs *cf, ConfigTable *cft );
+
+ConfigTable *lload_config_find_keyword( ConfigTable *ct, ConfigArgs *c );
+
+LloadListener *lload_config_check_my_url( const char *url, LDAPURLDesc *lud );
+
+LDAP_END_DECL
+
+#endif /* LLOAD_CONFIG_H */
diff --git a/servers/lloadd/lload.h b/servers/lloadd/lload.h
new file mode 100644
index 0000000..a9b98e0
--- /dev/null
+++ b/servers/lloadd/lload.h
@@ -0,0 +1,502 @@
+/* lload.h - load balancer include file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#ifndef _LLOAD_H_
+#define _LLOAD_H_
+
+#include "ldap_defaults.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <sys/types.h>
+#include <ac/syslog.h>
+#include <ac/regex.h>
+#include <ac/signal.h>
+#include <ac/socket.h>
+#include <ac/time.h>
+#include <ac/param.h>
+
+#include "ldap_avl.h"
+
+#include "../servers/slapd/slap.h"
+#include "../slapd/back-monitor/back-monitor.h"
+
+#ifndef ldap_debug
+#define ldap_debug slap_debug
+#endif
+
+#include "ldap_log.h"
+
+#include <ldap.h>
+#include <ldap_schema.h>
+
+#include "lber_pvt.h"
+#include "ldap_pvt.h"
+#include "ldap_pvt_thread.h"
+#include "ldap_queue.h"
+
+#include <event2/event.h>
+
+#ifdef HAVE_CYRUS_SASL
+#ifdef HAVE_SASL_SASL_H
+#include <sasl/sasl.h>
+#else
+#include <sasl.h>
+#endif
+#endif /* HAVE_CYRUS_SASL */
+
+LDAP_BEGIN_DECL
+
+#ifdef SERVICE_NAME
+#undef SERVICE_NAME
+#endif
+
+#define SERVICE_NAME OPENLDAP_PACKAGE "-lloadd"
+
+#define LLOAD_SB_MAX_INCOMING_CLIENT ( ( 1 << 24 ) - 1 )
+#define LLOAD_SB_MAX_INCOMING_UPSTREAM ( ( 1 << 24 ) - 1 )
+
+#define LLOAD_CONN_MAX_PDUS_PER_CYCLE_DEFAULT 10
+
+#define BER_BV_OPTIONAL( bv ) ( BER_BVISNULL( bv ) ? NULL : ( bv ) )
+
+#include <epoch.h>
+
+#define checked_lock( mutex ) \
+ if ( ldap_pvt_thread_mutex_lock( mutex ) != 0 ) assert(0)
+#define checked_unlock( mutex ) \
+ if ( ldap_pvt_thread_mutex_unlock( mutex ) != 0 ) assert(0)
+
+#ifdef LDAP_THREAD_DEBUG
+#define assert_locked( mutex ) \
+ if ( ldap_pvt_thread_mutex_trylock( mutex ) == 0 ) assert(0)
+#else
+#define assert_locked( mutex ) ( (void)0 )
+#endif
+
+typedef struct LloadBackend LloadBackend;
+typedef struct LloadPendingConnection LloadPendingConnection;
+typedef struct LloadConnection LloadConnection;
+typedef struct LloadOperation LloadOperation;
+typedef struct LloadChange LloadChange;
+/* end of forward declarations */
+
+typedef LDAP_CIRCLEQ_HEAD(BeSt, LloadBackend) lload_b_head;
+typedef LDAP_CIRCLEQ_HEAD(ConnSt, LloadConnection) lload_c_head;
+
+LDAP_SLAPD_V (lload_b_head) backend;
+LDAP_SLAPD_V (lload_c_head) clients;
+LDAP_SLAPD_V (ldap_pvt_thread_mutex_t) backend_mutex;
+LDAP_SLAPD_V (LloadBackend *) current_backend;
+LDAP_SLAPD_V (struct slap_bindconf) bindconf;
+LDAP_SLAPD_V (struct berval) lloadd_identity;
+
+/* Used to coordinate server (un)pause, shutdown */
+LDAP_SLAPD_V (ldap_pvt_thread_mutex_t) lload_wait_mutex;
+LDAP_SLAPD_V (ldap_pvt_thread_cond_t) lload_pause_cond;
+LDAP_SLAPD_V (ldap_pvt_thread_cond_t) lload_wait_cond;
+
+typedef int lload_cf_aux_table_parse_x( struct berval *val,
+ void *bc,
+ slap_cf_aux_table *tab0,
+ const char *tabmsg,
+ int unparse );
+
+typedef struct LloadListener LloadListener;
+
+enum lc_type {
+ LLOAD_CHANGE_UNDEFINED = 0,
+ LLOAD_CHANGE_MODIFY,
+ LLOAD_CHANGE_ADD,
+ LLOAD_CHANGE_DEL,
+};
+
+enum lc_object {
+ LLOAD_UNDEFINED = 0,
+ LLOAD_DAEMON,
+ /*
+ LLOAD_BINDCONF,
+ */
+ LLOAD_BACKEND,
+};
+
+enum lcf_daemon {
+ LLOAD_DAEMON_MOD_THREADS = 1 << 0,
+ LLOAD_DAEMON_MOD_FEATURES = 1 << 1,
+ LLOAD_DAEMON_MOD_TLS = 1 << 2,
+ LLOAD_DAEMON_MOD_LISTENER_ADD = 1 << 3,
+ LLOAD_DAEMON_MOD_LISTENER_REPLACE = 1 << 4,
+ LLOAD_DAEMON_MOD_BINDCONF = 1 << 5,
+};
+
+enum lcf_backend {
+ LLOAD_BACKEND_MOD_OTHER = 1 << 0,
+ LLOAD_BACKEND_MOD_CONNS = 1 << 1,
+};
+
+struct LloadChange {
+ enum lc_type type;
+ enum lc_object object;
+ union {
+ int generic;
+ enum lcf_daemon daemon;
+ enum lcf_backend backend;
+ } flags;
+ void *target;
+};
+
+typedef enum {
+#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
+ LLOAD_FEATURE_VC = 1 << 0,
+#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
+ LLOAD_FEATURE_PROXYAUTHZ = 1 << 1,
+ LLOAD_FEATURE_PAUSE = 1 << 2,
+} lload_features_t;
+
+#define LLOAD_FEATURE_SUPPORTED_MASK ( \
+ LLOAD_FEATURE_PROXYAUTHZ | \
+ 0 )
+
+#ifdef BALANCER_MODULE
+#define LLOAD_TLS_CTX ( lload_use_slap_tls_ctx ? slap_tls_ctx : lload_tls_ctx )
+#else
+#define LLOAD_TLS_CTX ( lload_tls_ctx )
+#endif
+
+enum lload_tls_type {
+ LLOAD_CLEARTEXT = 0,
+ LLOAD_LDAPS,
+ LLOAD_STARTTLS_OPTIONAL,
+ LLOAD_STARTTLS,
+ LLOAD_TLS_ESTABLISHED,
+};
+
+struct LloadPendingConnection {
+ LloadBackend *backend;
+
+ struct event *event;
+ ber_socket_t fd;
+
+ LDAP_LIST_ENTRY(LloadPendingConnection) next;
+};
+
+typedef struct lload_counters_t {
+ ldap_pvt_mp_t lc_ops_completed;
+ ldap_pvt_mp_t lc_ops_received;
+ ldap_pvt_mp_t lc_ops_forwarded;
+ ldap_pvt_mp_t lc_ops_rejected;
+ ldap_pvt_mp_t lc_ops_failed;
+} lload_counters_t;
+
+enum {
+ LLOAD_STATS_OPS_BIND = 0,
+ LLOAD_STATS_OPS_OTHER,
+ LLOAD_STATS_OPS_LAST
+};
+
+typedef struct lload_global_stats_t {
+ ldap_pvt_mp_t global_incoming;
+ ldap_pvt_mp_t global_outgoing;
+ lload_counters_t counters[LLOAD_STATS_OPS_LAST];
+} lload_global_stats_t;
+
+/* Can hold mutex when locking a linked connection */
+struct LloadBackend {
+ ldap_pvt_thread_mutex_t b_mutex;
+
+ struct berval b_name, b_uri;
+ int b_proto, b_port;
+ enum lload_tls_type b_tls, b_tls_conf;
+ char *b_host;
+
+ int b_retry_timeout, b_failed;
+ struct event *b_retry_event;
+ struct timeval b_retry_tv;
+
+ int b_numconns, b_numbindconns;
+ int b_bindavail, b_active, b_opening;
+ lload_c_head b_conns, b_bindconns, b_preparing;
+ LDAP_LIST_HEAD(ConnectingSt, LloadPendingConnection) b_connecting;
+ LloadConnection *b_last_conn, *b_last_bindconn;
+
+ long b_max_pending, b_max_conn_pending;
+ long b_n_ops_executing;
+
+ lload_counters_t b_counters[LLOAD_STATS_OPS_LAST];
+
+#ifdef BALANCER_MODULE
+ monitor_subsys_t *b_monitor;
+#endif /* BALANCER_MODULE */
+
+ struct evdns_getaddrinfo_request *b_dns_req;
+ void *b_cookie;
+
+ LDAP_CIRCLEQ_ENTRY(LloadBackend) b_next;
+};
+
+typedef int (*LloadOperationHandler)( LloadConnection *client,
+ LloadOperation *op,
+ BerElement *ber );
+typedef int (*RequestHandler)( LloadConnection *c, LloadOperation *op );
+typedef struct lload_exop_handlers_t {
+ struct berval oid;
+ RequestHandler func;
+} ExopHandler;
+
+typedef int (*CONNECTION_PDU_CB)( LloadConnection *c );
+typedef void (*CONNECTION_DESTROY_CB)( LloadConnection *c );
+
+/* connection state (protected by c_mutex) */
+enum sc_state {
+ LLOAD_C_INVALID = 0, /* MUST BE ZERO (0) */
+ LLOAD_C_READY, /* ready */
+ LLOAD_C_CLOSING, /* closing */
+ LLOAD_C_ACTIVE, /* exclusive operation (tls setup, ...) in progress */
+ LLOAD_C_BINDING, /* binding */
+ LLOAD_C_DYING, /* part-processed dead waiting to be freed, someone
+ * might still be observing it */
+};
+enum sc_type {
+ LLOAD_C_OPEN = 0, /* regular connection */
+ LLOAD_C_PREPARING, /* upstream connection not assigned yet */
+ LLOAD_C_BIND, /* connection used to handle bind client requests if VC not enabled */
+ LLOAD_C_PRIVILEGED, /* connection can override proxyauthz control */
+};
+enum sc_io_state {
+ LLOAD_C_OPERATIONAL = 0, /* all is good */
+ LLOAD_C_READ_HANDOVER = 1 << 0, /* A task to process PDUs is scheduled or
+ * running, do not re-enable c_read_event */
+ LLOAD_C_READ_PAUSE = 1 << 1, /* We want to pause reading until the client
+ * has sufficiently caught up with what we
+ * sent */
+};
+
+/*
+ * represents a connection from an ldap client/to ldap server
+ */
+struct LloadConnection {
+ enum sc_state c_state; /* connection state */
+ enum sc_type c_type;
+ enum sc_io_state c_io_state;
+ ber_socket_t c_fd;
+
+/*
+ * LloadConnection reference counting:
+ * - connection has a reference counter in c_refcnt
+ * - also a liveness/validity token is added to c_refcnt during
+ * lload_connection_init, its existence is tracked in c_live and is usually the
+ * only one that prevents it from being destroyed
+ * - anyone who needs to be able to relock the connection after unlocking it has
+ * to use acquire_ref(), they need to make sure a matching
+ * RELEASE_REF( c, c_refcnt, c->c_destroy ); is run eventually
+ * - when a connection is considered dead, use CONNECTION_DESTROY on a locked
+ * connection, it will be made unreachable from normal places and either
+ * scheduled for reclamation when safe to do so or if anyone still holds a
+ * reference, it just gets unlocked and reclaimed after the last ref is
+ * released
+ * - CONNECTION_LOCK_DESTROY is a shorthand for locking and CONNECTION_DESTROY
+ */
+ ldap_pvt_thread_mutex_t c_mutex; /* protect the connection */
+ uintptr_t c_refcnt, c_live;
+ CONNECTION_DESTROY_CB c_unlink;
+ CONNECTION_DESTROY_CB c_destroy;
+ CONNECTION_PDU_CB c_pdu_cb;
+#define CONNECTION_ASSERT_LOCKED(c) assert_locked( &(c)->c_mutex )
+#define CONNECTION_LOCK(c) \
+ do { \
+ checked_lock( &(c)->c_mutex ); \
+ } while (0)
+#define CONNECTION_UNLOCK(c) \
+ do { \
+ checked_unlock( &(c)->c_mutex ); \
+ } while (0)
+#define CONNECTION_UNLINK_(c) \
+ do { \
+ if ( __atomic_exchange_n( &(c)->c_live, 0, __ATOMIC_ACQ_REL ) ) { \
+ RELEASE_REF( (c), c_refcnt, c->c_destroy ); \
+ (c)->c_unlink( (c) ); \
+ } \
+ } while (0)
+#define CONNECTION_DESTROY(c) \
+ do { \
+ CONNECTION_UNLINK_(c); \
+ CONNECTION_UNLOCK(c); \
+ } while (0)
+#define CONNECTION_LOCK_DESTROY(c) \
+ do { \
+ CONNECTION_LOCK(c); \
+ CONNECTION_DESTROY(c); \
+ } while (0);
+
+ Sockbuf *c_sb; /* ber connection stuff */
+
+ /* set by connection_init */
+ unsigned long c_connid; /* unique id of this connection */
+ struct berval c_peer_name; /* peer name (trans=addr:port) */
+ time_t c_starttime; /* when the connection was opened */
+
+ time_t c_activitytime; /* when the connection was last used */
+ ber_int_t c_next_msgid; /* msgid of the next message */
+
+ /* must not be used while holding either mutex */
+ struct event *c_read_event, *c_write_event;
+ struct timeval *c_read_timeout;
+
+ /* can only be changed by binding thread */
+ struct berval c_sasl_bind_mech; /* mech in progress */
+ struct berval c_auth; /* authcDN (possibly in progress) */
+
+ unsigned long c_pin_id;
+
+#ifdef HAVE_CYRUS_SASL
+ sasl_conn_t *c_sasl_authctx;
+ void *c_sasl_defaults;
+#ifdef SASL_CHANNEL_BINDING /* 2.1.25+ */
+ sasl_channel_binding_t *c_sasl_cbinding; /* Else cyrus-sasl would happily
+ * leak it on sasl_dispose */
+#endif /* SASL_CHANNEL_BINDING */
+#endif /* HAVE_CYRUS_SASL */
+
+#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
+ struct berval c_vc_cookie;
+#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
+
+ /* Can be held while acquiring c_mutex to inject things into c_ops or
+ * destroy the connection */
+ ldap_pvt_thread_mutex_t c_io_mutex; /* only one pdu written at a time */
+
+ BerElement *c_currentber; /* ber we're attempting to read */
+ BerElement *c_pendingber; /* ber we're attempting to write */
+
+ TAvlnode *c_ops; /* Operations pending on the connection */
+
+#ifdef HAVE_TLS
+ enum lload_tls_type c_is_tls; /* true if this LDAP over raw TLS */
+#endif
+
+ long c_n_ops_executing; /* num of ops currently executing */
+ long c_n_ops_completed; /* num of ops completed */
+ lload_counters_t c_counters; /* per connection operation counters */
+
+ LloadBackend *c_backend;
+
+ /*
+ * Protected by the CIRCLEQ mutex:
+ * - Client: clients_mutex
+ * - Upstream: b->b_mutex
+ */
+ LDAP_CIRCLEQ_ENTRY(LloadConnection) c_next;
+};
+
+enum op_state {
+ LLOAD_OP_NOT_FREEING = 0,
+ LLOAD_OP_DETACHING_CLIENT = 1 << 1,
+ LLOAD_OP_DETACHING_UPSTREAM = 1 << 0,
+};
+
+#define LLOAD_OP_DETACHING_MASK \
+ ( LLOAD_OP_DETACHING_UPSTREAM | LLOAD_OP_DETACHING_CLIENT )
+
+/* operation result for monitoring purposes */
+enum op_result {
+ LLOAD_OP_REJECTED, /* operation was not forwarded */
+ LLOAD_OP_COMPLETED, /* operation sent and response received */
+ LLOAD_OP_FAILED, /* operation was forwarded, but no response was received */
+};
+
+/*
+ * Operation reference tracking:
+ * - o_refcnt is set to 1, never incremented
+ * - operation_unlink sets it to 0 and on transition from 1 clears both
+ * connection links (o_client, o_upstream)
+ */
+struct LloadOperation {
+ uintptr_t o_refcnt;
+
+ LloadConnection *o_client;
+ unsigned long o_client_connid;
+ ber_int_t o_client_msgid;
+ ber_int_t o_saved_msgid;
+
+ LloadConnection *o_upstream;
+ unsigned long o_upstream_connid;
+ ber_int_t o_upstream_msgid;
+ time_t o_last_response;
+
+ /* Protects o_client, o_upstream links */
+ ldap_pvt_thread_mutex_t o_link_mutex;
+
+ ber_tag_t o_tag;
+ time_t o_start;
+ unsigned long o_pin_id;
+
+ enum op_result o_res;
+ BerElement *o_ber;
+ BerValue o_request, o_ctrls;
+};
+
+/*
+ * listener; need to access it from monitor backend
+ */
+struct LloadListener {
+ struct berval sl_url;
+ struct berval sl_name;
+ mode_t sl_perms;
+#ifdef HAVE_TLS
+ int sl_is_tls;
+#endif
+ int sl_is_proxied;
+ struct event_base *base;
+ struct evconnlistener *listener;
+ int sl_mute; /* Listener is temporarily disabled due to emfile */
+ int sl_busy; /* Listener is busy (accept thread activated) */
+ ber_socket_t sl_sd;
+ Sockaddr sl_sa;
+#define sl_addr sl_sa.sa_in_addr
+#define LDAP_TCP_BUFFER
+#ifdef LDAP_TCP_BUFFER
+ int sl_tcp_rmem; /* custom TCP read buffer size */
+ int sl_tcp_wmem; /* custom TCP write buffer size */
+#endif
+};
+
+typedef int (*CONNCB)( LloadConnection *c, void *arg );
+
+struct lload_monitor_conn_arg {
+ Operation *op;
+ monitor_subsys_t *ms;
+ Entry **ep;
+};
+
+/* config requires a bi_private with configuration data - dummy for now */
+struct lload_conf_info {
+ int dummy;
+};
+LDAP_END_DECL
+
+#include "proto-lload.h"
+#endif /* _LLOAD_H_ */
diff --git a/servers/lloadd/lloadd.service b/servers/lloadd/lloadd.service
new file mode 100644
index 0000000..062b8ca
--- /dev/null
+++ b/servers/lloadd/lloadd.service
@@ -0,0 +1,13 @@
+[Unit]
+Description=LDAP Load Balancer Daemon
+After=syslog.target network-online.target
+Documentation=man:lloadd.conf
+
+[Service]
+Type=notify
+Environment="LLOADD_URLS=ldap:/// ldapi:///" "LLOADD_OPTIONS="
+EnvironmentFile=/etc/sysconfig/lloadd
+ExecStart=%LIBEXECDIR%/lloadd -d 0 -h ${LLOADD_URLS} $LLOADD_OPTIONS
+
+[Install]
+WantedBy=multi-user.target
diff --git a/servers/lloadd/main.c b/servers/lloadd/main.c
new file mode 100644
index 0000000..5f24cd4
--- /dev/null
+++ b/servers/lloadd/main.c
@@ -0,0 +1,949 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+#include <ac/wait.h>
+#include <ac/errno.h>
+
+#include <event2/event.h>
+
+#include "lload.h"
+#include "lutil.h"
+#include "ldif.h"
+
+#ifdef LDAP_SIGCHLD
+static void wait4child( evutil_socket_t sig, short what, void *arg );
+#endif
+
+#ifdef SIGPIPE
+static void sigpipe( evutil_socket_t sig, short what, void *arg );
+#endif
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+#define MAIN_RETURN(x) return
+static struct sockaddr_in bind_addr;
+
+#define SERVICE_EXIT( e, n ) \
+ do { \
+ if ( is_NT_Service ) { \
+ lutil_ServiceStatus.dwWin32ExitCode = (e); \
+ lutil_ServiceStatus.dwServiceSpecificExitCode = (n); \
+ } \
+ } while (0)
+
+#else
+#define SERVICE_EXIT( e, n )
+#define MAIN_RETURN(x) return (x)
+#endif
+
+struct signal_handler {
+ int signal;
+ event_callback_fn handler;
+ struct event *event;
+} signal_handlers[] = {
+ { LDAP_SIGUSR2, lload_sig_shutdown },
+
+#ifdef SIGPIPE
+ { SIGPIPE, sigpipe },
+#endif
+#ifdef SIGHUP
+ { SIGHUP, lload_sig_shutdown },
+#endif
+ { SIGINT, lload_sig_shutdown },
+ { SIGTERM, lload_sig_shutdown },
+#ifdef SIGTRAP
+ { SIGTRAP, lload_sig_shutdown },
+#endif
+#ifdef LDAP_SIGCHLD
+ { LDAP_SIGCHLD, wait4child },
+#endif
+#ifdef SIGBREAK
+ /* SIGBREAK is generated when Ctrl-Break is pressed. */
+ { SIGBREAK, lload_sig_shutdown },
+#endif
+ { 0, NULL }
+};
+
+/*
+ * when more than one lloadd is running on one machine, each one might have
+ * it's own LOCAL for syslogging and must have its own pid/args files
+ */
+
+#ifndef HAVE_MKVERSION
+const char Versionstr[] = OPENLDAP_PACKAGE
+ " " OPENLDAP_VERSION " LDAP Load Balancer Server (lloadd)";
+#endif
+
+#define CHECK_NONE 0x00
+#define CHECK_CONFIG 0x01
+#define CHECK_LOGLEVEL 0x02
+static int check = CHECK_NONE;
+static int version = 0;
+
+static int
+slapd_opt_slp( const char *val, void *arg )
+{
+#ifdef HAVE_SLP
+ /* NULL is default */
+ if ( val == NULL || *val == '(' || strcasecmp( val, "on" ) == 0 ) {
+ slapd_register_slp = 1;
+ slapd_slp_attrs = ( val != NULL && *val == '(' ) ? val : NULL;
+
+ } else if ( strcasecmp( val, "off" ) == 0 ) {
+ slapd_register_slp = 0;
+
+ /* NOTE: add support for URL specification? */
+
+ } else {
+ fprintf( stderr, "unrecognized value \"%s\" for SLP option\n", val );
+ return -1;
+ }
+
+ return 0;
+
+#else
+ fputs( "lloadd: SLP support is not available\n", stderr );
+ return 0;
+#endif
+}
+
+/*
+ * Option helper structure:
+ *
+ * oh_nam is left-hand part of <option>[=<value>]
+ * oh_fnc is handler function
+ * oh_arg is an optional arg to oh_fnc
+ * oh_usage is the one-line usage string related to the option,
+ * which is assumed to start with <option>[=<value>]
+ *
+ * please leave valid options in the structure, and optionally #ifdef
+ * their processing inside the helper, so that reasonable and helpful
+ * error messages can be generated if a disabled option is requested.
+ */
+struct option_helper {
+ struct berval oh_name;
+ int (*oh_fnc)( const char *val, void *arg );
+ void *oh_arg;
+ const char *oh_usage;
+} option_helpers[] = {
+ { BER_BVC("slp"), slapd_opt_slp, NULL,
+ "slp[={on|off|(attrs)}] enable/disable SLP using (attrs)" },
+ { BER_BVNULL, 0, NULL, NULL }
+};
+
+#if defined(LDAP_DEBUG) && defined(LDAP_SYSLOG)
+#ifdef LOG_LOCAL4
+int
+parse_syslog_user( const char *arg, int *syslogUser )
+{
+ static slap_verbmasks syslogUsers[] = {
+ { BER_BVC("LOCAL0"), LOG_LOCAL0 },
+ { BER_BVC("LOCAL1"), LOG_LOCAL1 },
+ { BER_BVC("LOCAL2"), LOG_LOCAL2 },
+ { BER_BVC("LOCAL3"), LOG_LOCAL3 },
+ { BER_BVC("LOCAL4"), LOG_LOCAL4 },
+ { BER_BVC("LOCAL5"), LOG_LOCAL5 },
+ { BER_BVC("LOCAL6"), LOG_LOCAL6 },
+ { BER_BVC("LOCAL7"), LOG_LOCAL7 },
+#ifdef LOG_USER
+ { BER_BVC("USER"), LOG_USER },
+#endif /* LOG_USER */
+#ifdef LOG_DAEMON
+ { BER_BVC("DAEMON"), LOG_DAEMON },
+#endif /* LOG_DAEMON */
+ { BER_BVNULL, 0 }
+};
+ int i = verb_to_mask( arg, syslogUsers );
+
+ if ( BER_BVISNULL( &syslogUsers[i].word ) ) {
+ Debug( LDAP_DEBUG_ANY, "unrecognized syslog user \"%s\".\n", arg );
+ return 1;
+ }
+
+ *syslogUser = syslogUsers[i].mask;
+
+ return 0;
+}
+#endif /* LOG_LOCAL4 */
+
+int
+parse_syslog_level( const char *arg, int *levelp )
+{
+ static slap_verbmasks str2syslog_level[] = {
+ { BER_BVC("EMERG"), LOG_EMERG },
+ { BER_BVC("ALERT"), LOG_ALERT },
+ { BER_BVC("CRIT"), LOG_CRIT },
+ { BER_BVC("ERR"), LOG_ERR },
+ { BER_BVC("WARNING"), LOG_WARNING },
+ { BER_BVC("NOTICE"), LOG_NOTICE },
+ { BER_BVC("INFO"), LOG_INFO },
+ { BER_BVC("DEBUG"), LOG_DEBUG },
+ { BER_BVNULL, 0 }
+};
+ int i = verb_to_mask( arg, str2syslog_level );
+ if ( BER_BVISNULL( &str2syslog_level[i].word ) ) {
+ Debug( LDAP_DEBUG_ANY, "unknown syslog level \"%s\".\n", arg );
+ return 1;
+ }
+
+ *levelp = str2syslog_level[i].mask;
+
+ return 0;
+}
+#endif /* LDAP_DEBUG && LDAP_SYSLOG */
+
+int
+parse_debug_unknowns( char **unknowns, int *levelp )
+{
+ int i, level, rc = 0;
+
+ for ( i = 0; unknowns[i] != NULL; i++ ) {
+ level = 0;
+ if ( str2loglevel( unknowns[i], &level ) ) {
+ fprintf( stderr, "unrecognized log level \"%s\"\n", unknowns[i] );
+ rc = 1;
+ } else {
+ *levelp |= level;
+ }
+ }
+ return rc;
+}
+
+int
+parse_debug_level( const char *arg, int *levelp, char ***unknowns )
+{
+ int level;
+
+ if ( arg && arg[0] != '-' && !isdigit( (unsigned char)arg[0] ) ) {
+ int i;
+ char **levels;
+
+ levels = ldap_str2charray( arg, "," );
+
+ for ( i = 0; levels[i] != NULL; i++ ) {
+ level = 0;
+
+ if ( str2loglevel( levels[i], &level ) ) {
+ /* remember this for later */
+ ldap_charray_add( unknowns, levels[i] );
+ fprintf( stderr, "unrecognized log level \"%s\" (deferred)\n",
+ levels[i] );
+ } else {
+ *levelp |= level;
+ }
+ }
+
+ ldap_charray_free( levels );
+
+ } else {
+ int rc;
+
+ if ( arg[0] == '-' ) {
+ rc = lutil_atoix( &level, arg, 0 );
+ } else {
+ unsigned ulevel;
+
+ rc = lutil_atoux( &ulevel, arg, 0 );
+ level = (int)ulevel;
+ }
+
+ if ( rc ) {
+ fprintf( stderr,
+ "unrecognized log level "
+ "\"%s\"\n",
+ arg );
+ return 1;
+ }
+
+ if ( level == 0 ) {
+ *levelp = 0;
+
+ } else {
+ *levelp |= level;
+ }
+ }
+
+ return 0;
+}
+
+static void
+usage( char *name )
+{
+ fprintf( stderr, "usage: %s options\n", name );
+ fprintf( stderr,
+ "\t-4\t\tIPv4 only\n"
+ "\t-6\t\tIPv6 only\n"
+ "\t-d level\tDebug level"
+ "\n"
+ "\t-f filename\tConfiguration file\n"
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+ "\t-g group\tGroup (id or name) to run as\n"
+#endif
+ "\t-h URLs\t\tList of URLs to serve\n"
+#ifdef SLAP_DEFAULT_SYSLOG_USER
+ "\t-l facility\tSyslog facility (default: LOCAL4)\n"
+#endif
+ "\t-n serverName\tService name\n"
+ "\t-o <opt>[=val] generic means to specify options" );
+ if ( !BER_BVISNULL( &option_helpers[0].oh_name ) ) {
+ int i;
+
+ fprintf( stderr, "; supported options:\n" );
+ for ( i = 0; !BER_BVISNULL( &option_helpers[i].oh_name ); i++ ) {
+ fprintf( stderr, "\t\t%s\n", option_helpers[i].oh_usage );
+ }
+ } else {
+ fprintf( stderr, "\n" );
+ }
+ fprintf( stderr,
+#ifdef HAVE_CHROOT
+ "\t-r directory\tSandbox directory to chroot to\n"
+#endif
+ "\t-s level\tSyslog level\n"
+ "\t-t\t\tCheck configuration file\n"
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+ "\t-u user\t\tUser (id or name) to run as\n"
+#endif
+ "\t-V\t\tprint version info (-VV exit afterwards)\n" );
+}
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+void WINAPI
+ServiceMain( DWORD argc, LPTSTR *argv )
+#else
+int
+main( int argc, char **argv )
+#endif
+{
+ int i, no_detach = 0;
+ int rc = 1;
+ char *urls = NULL;
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+ char *username = NULL;
+ char *groupname = NULL;
+#endif
+#if defined(HAVE_CHROOT)
+ char *sandbox = NULL;
+#endif
+#ifdef SLAP_DEFAULT_SYSLOG_USER
+ int syslogUser = SLAP_DEFAULT_SYSLOG_USER;
+#endif
+
+#ifndef HAVE_WINSOCK
+ int pid, waitfds[2];
+#endif
+ int g_argc = argc;
+ char **g_argv = argv;
+
+ char *configfile = NULL;
+ char *configdir = NULL;
+ char *serverName;
+ int serverMode = SLAP_SERVER_MODE;
+
+ char **debug_unknowns = NULL;
+ char **syslog_unknowns = NULL;
+
+ int slapd_pid_file_unlink = 0, slapd_args_file_unlink = 0;
+ int firstopt = 1;
+
+ slap_sl_mem_init();
+
+ serverName = lutil_progname( "lloadd", argc, argv );
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+ {
+ int *ip;
+ char *newConfigFile;
+ char *newConfigDir;
+ char *newUrls;
+ char *regService = NULL;
+
+ if ( is_NT_Service ) {
+ lutil_CommenceStartupProcessing( serverName, lload_sig_shutdown );
+ if ( strcmp( serverName, SERVICE_NAME ) ) regService = serverName;
+ }
+
+ ip = (int *)lutil_getRegParam( regService, "DebugLevel" );
+ if ( ip != NULL ) {
+ slap_debug = *ip;
+ Debug( LDAP_DEBUG_ANY, "new debug level from registry is: %d\n",
+ slap_debug );
+ }
+
+ newUrls = (char *)lutil_getRegParam( regService, "Urls" );
+ if ( newUrls ) {
+ if ( urls ) ch_free( urls );
+
+ urls = ch_strdup( newUrls );
+ Debug( LDAP_DEBUG_ANY, "new urls from registry: %s\n", urls );
+ }
+
+ newConfigFile = (char *)lutil_getRegParam( regService, "ConfigFile" );
+ if ( newConfigFile != NULL ) {
+ configfile = ch_strdup( newConfigFile );
+ Debug( LDAP_DEBUG_ANY, "new config file from registry is: %s\n",
+ configfile );
+ }
+
+ newConfigDir = (char *)lutil_getRegParam( regService, "ConfigDir" );
+ if ( newConfigDir != NULL ) {
+ configdir = ch_strdup( newConfigDir );
+ Debug( LDAP_DEBUG_ANY, "new config dir from registry is: %s\n",
+ configdir );
+ }
+ }
+#endif
+
+ epoch_init();
+
+ while ( (i = getopt( argc, argv,
+ "c:d:f:F:h:n:o:s:tV"
+#ifdef LDAP_PF_INET6
+ "46"
+#endif
+#ifdef HAVE_CHROOT
+ "r:"
+#endif
+#if defined(LDAP_DEBUG) && defined(LDAP_SYSLOG)
+ "S:"
+#ifdef LOG_LOCAL4
+ "l:"
+#endif
+#endif
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+ "u:g:"
+#endif
+ )) != EOF ) {
+ switch ( i ) {
+#ifdef LDAP_PF_INET6
+ case '4':
+ slap_inet4or6 = AF_INET;
+ break;
+ case '6':
+ slap_inet4or6 = AF_INET6;
+ break;
+#endif
+
+ case 'h': /* listen URLs */
+ if ( urls != NULL ) free( urls );
+ urls = ch_strdup( optarg );
+ break;
+
+ case 'd': { /* set debug level and 'do not detach' flag */
+ int level = 0;
+
+ if ( strcmp( optarg, "?" ) == 0 ) {
+ check |= CHECK_LOGLEVEL;
+ break;
+ }
+
+ no_detach = 1;
+ if ( parse_debug_level( optarg, &level, &debug_unknowns ) ) {
+ goto destroy;
+ }
+#ifdef LDAP_DEBUG
+ slap_debug |= level;
+#else
+ if ( level != 0 )
+ fputs( "must compile with LDAP_DEBUG for debugging\n",
+ stderr );
+#endif
+ } break;
+
+ case 'f': /* read config file */
+ configfile = ch_strdup( optarg );
+ break;
+
+ case 'o': {
+ char *val = strchr( optarg, '=' );
+ struct berval opt;
+
+ opt.bv_val = optarg;
+
+ if ( val ) {
+ opt.bv_len = ( val - optarg );
+ val++;
+
+ } else {
+ opt.bv_len = strlen( optarg );
+ }
+
+ for ( i = 0; !BER_BVISNULL( &option_helpers[i].oh_name );
+ i++ ) {
+ if ( ber_bvstrcasecmp( &option_helpers[i].oh_name, &opt ) ==
+ 0 ) {
+ assert( option_helpers[i].oh_fnc != NULL );
+ if ( (*option_helpers[i].oh_fnc)(
+ val, option_helpers[i].oh_arg ) == -1 ) {
+ /* we assume the option parsing helper
+ * issues appropriate and self-explanatory
+ * error messages... */
+ goto stop;
+ }
+ break;
+ }
+ }
+
+ if ( BER_BVISNULL( &option_helpers[i].oh_name ) ) {
+ goto unhandled_option;
+ }
+ break;
+ }
+
+ case 's': /* set syslog level */
+ if ( strcmp( optarg, "?" ) == 0 ) {
+ check |= CHECK_LOGLEVEL;
+ break;
+ }
+
+ if ( parse_debug_level(
+ optarg, &ldap_syslog, &syslog_unknowns ) ) {
+ goto destroy;
+ }
+ break;
+
+#if defined(LDAP_DEBUG) && defined(LDAP_SYSLOG)
+ case 'S':
+ if ( parse_syslog_level( optarg, &ldap_syslog_level ) ) {
+ goto destroy;
+ }
+ break;
+
+#ifdef LOG_LOCAL4
+ case 'l': /* set syslog local user */
+ if ( parse_syslog_user( optarg, &syslogUser ) ) {
+ goto destroy;
+ }
+ break;
+#endif
+#endif /* LDAP_DEBUG && LDAP_SYSLOG */
+
+#ifdef HAVE_CHROOT
+ case 'r':
+ if ( sandbox ) free( sandbox );
+ sandbox = ch_strdup( optarg );
+ break;
+#endif
+
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+ case 'u': /* user name */
+ if ( username ) free( username );
+ username = ch_strdup( optarg );
+ break;
+
+ case 'g': /* group name */
+ if ( groupname ) free( groupname );
+ groupname = ch_strdup( optarg );
+ break;
+#endif /* SETUID && GETUID */
+
+ case 'n': /* NT service name */
+ serverName = ch_strdup( optarg );
+ break;
+
+ case 't':
+ check |= CHECK_CONFIG;
+ break;
+
+ case 'V':
+ version++;
+ break;
+
+ default:
+unhandled_option:;
+ usage( argv[0] );
+ rc = 1;
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 15 );
+ goto stop;
+ }
+
+ if ( firstopt ) {
+ firstopt = 0;
+ }
+ }
+
+ if ( optind != argc ) goto unhandled_option;
+
+ ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &slap_debug );
+ ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &slap_debug );
+ ldif_debug = slap_debug;
+
+ if ( version ) {
+ fprintf( stderr, "%s\n", Versionstr );
+
+ if ( version > 1 ) goto stop;
+ }
+
+#if defined(LDAP_DEBUG) && defined(LDAP_SYSLOG)
+ {
+ char *logName;
+#ifdef HAVE_EBCDIC
+ logName = ch_strdup( serverName );
+ __atoe( logName );
+#else
+ logName = serverName;
+#endif
+
+#ifdef LOG_LOCAL4
+ openlog( logName, OPENLOG_OPTIONS, syslogUser );
+#elif defined LOG_DEBUG
+ openlog( logName, OPENLOG_OPTIONS );
+#endif
+#ifdef HAVE_EBCDIC
+ free( logName );
+#endif
+ }
+#endif /* LDAP_DEBUG && LDAP_SYSLOG */
+
+ Debug( LDAP_DEBUG_ANY, "%s", Versionstr );
+
+ global_host = ldap_pvt_get_fqdn( NULL );
+
+ if ( check == CHECK_NONE && lloadd_listeners_init( urls ) != 0 ) {
+ rc = 1;
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 16 );
+ goto stop;
+ }
+
+#if defined(HAVE_CHROOT)
+ if ( sandbox ) {
+ if ( chdir( sandbox ) ) {
+ perror( "chdir" );
+ rc = 1;
+ goto stop;
+ }
+ if ( chroot( sandbox ) ) {
+ perror( "chroot" );
+ rc = 1;
+ goto stop;
+ }
+ if ( chdir( "/" ) ) {
+ perror( "chdir" );
+ rc = 1;
+ goto stop;
+ }
+ }
+#endif
+
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+ if ( username != NULL || groupname != NULL ) {
+ slap_init_user( username, groupname );
+ }
+#endif
+
+ rc = lload_init( serverMode, serverName );
+ if ( rc ) {
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 18 );
+ goto destroy;
+ }
+
+ if ( lload_read_config( configfile, configdir ) != 0 ) {
+ rc = 1;
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 19 );
+
+ if ( check & CHECK_CONFIG ) {
+ fprintf( stderr, "config check failed\n" );
+ }
+
+ goto destroy;
+ }
+
+ if ( debug_unknowns ) {
+ rc = parse_debug_unknowns( debug_unknowns, &slap_debug );
+ ldap_charray_free( debug_unknowns );
+ debug_unknowns = NULL;
+ if ( rc ) goto destroy;
+ }
+ if ( syslog_unknowns ) {
+ rc = parse_debug_unknowns( syslog_unknowns, &ldap_syslog );
+ ldap_charray_free( syslog_unknowns );
+ syslog_unknowns = NULL;
+ if ( rc ) goto destroy;
+ }
+
+ if ( check & CHECK_LOGLEVEL ) {
+ rc = 0;
+ goto destroy;
+ }
+
+ if ( check & CHECK_CONFIG ) {
+ fprintf( stderr, "config check succeeded\n" );
+
+ check &= ~CHECK_CONFIG;
+ if ( check == CHECK_NONE ) {
+ rc = 0;
+ goto destroy;
+ }
+ }
+
+#ifdef HAVE_TLS
+ rc = ldap_pvt_tls_init( 1 );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "main: "
+ "TLS init failed: %d\n",
+ rc );
+ rc = 1;
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 20 );
+ goto destroy;
+ }
+
+ if ( lload_tls_init() ) {
+ rc = 1;
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 20 );
+ goto destroy;
+ }
+#endif
+
+ daemon_base = event_base_new();
+ if ( !daemon_base ) {
+ Debug( LDAP_DEBUG_ANY, "main: "
+ "main event base allocation failed\n" );
+ rc = 1;
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 21 );
+ goto destroy;
+ }
+
+ for ( i = 0; signal_handlers[i].signal; i++ ) {
+ struct event *event;
+ event = evsignal_new( daemon_base, signal_handlers[i].signal,
+ signal_handlers[i].handler, daemon_base );
+ if ( !event || event_add( event, NULL ) ) {
+ Debug( LDAP_DEBUG_ANY, "main: "
+ "failed to register a handler for signal %d\n",
+ signal_handlers[i].signal );
+ rc = 1;
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 21 );
+ goto destroy;
+ }
+ signal_handlers[i].event = event;
+ }
+
+#ifndef HAVE_WINSOCK
+ if ( !no_detach ) {
+ if ( lutil_pair( waitfds ) < 0 ) {
+ Debug( LDAP_DEBUG_ANY, "main: "
+ "lutil_pair failed\n" );
+ rc = 1;
+ goto destroy;
+ }
+ pid = lutil_detach( no_detach, 0 );
+ if ( pid ) {
+ char buf[4];
+ rc = EXIT_SUCCESS;
+ close( waitfds[1] );
+ if ( read( waitfds[0], buf, 1 ) != 1 ) rc = EXIT_FAILURE;
+ _exit( rc );
+ } else {
+ close( waitfds[0] );
+ }
+ }
+#endif /* HAVE_WINSOCK */
+
+ if ( slapd_pid_file != NULL ) {
+ FILE *fp = fopen( slapd_pid_file, "w" );
+
+ if ( fp == NULL ) {
+ char ebuf[128];
+ int save_errno = errno;
+
+ Debug( LDAP_DEBUG_ANY, "unable to open pid file "
+ "\"%s\": %d (%s)\n",
+ slapd_pid_file, save_errno,
+ AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+
+ free( slapd_pid_file );
+ slapd_pid_file = NULL;
+
+ rc = 1;
+ goto destroy;
+ }
+ fprintf( fp, "%d\n", (int)getpid() );
+ fclose( fp );
+ slapd_pid_file_unlink = 1;
+ }
+
+ if ( slapd_args_file != NULL ) {
+ FILE *fp = fopen( slapd_args_file, "w" );
+
+ if ( fp == NULL ) {
+ char ebuf[128];
+ int save_errno = errno;
+
+ Debug( LDAP_DEBUG_ANY, "unable to open args file "
+ "\"%s\": %d (%s)\n",
+ slapd_args_file, save_errno,
+ AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+
+ free( slapd_args_file );
+ slapd_args_file = NULL;
+
+ rc = 1;
+ goto destroy;
+ }
+
+ for ( i = 0; i < g_argc; i++ ) {
+ fprintf( fp, "%s ", g_argv[i] );
+ }
+ fprintf( fp, "\n" );
+ fclose( fp );
+ slapd_args_file_unlink = 1;
+ }
+
+ /*
+ * FIXME: moved here from lloadd_daemon_task()
+ * because back-monitor db_open() needs it
+ */
+ time( &starttime );
+
+ Debug( LDAP_DEBUG_ANY, "lloadd starting\n" );
+
+#ifndef HAVE_WINSOCK
+ if ( !no_detach ) {
+ write( waitfds[1], "1", 1 );
+ close( waitfds[1] );
+ }
+#endif
+
+#ifdef HAVE_NT_EVENT_LOG
+ if ( is_NT_Service )
+ lutil_LogStartedEvent( serverName, slap_debug,
+ configfile ? configfile : LLOADD_DEFAULT_CONFIGFILE, urls );
+#endif
+
+ rc = lloadd_daemon( daemon_base );
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+ /* Throw away the event that we used during the startup process. */
+ if ( is_NT_Service ) ldap_pvt_thread_cond_destroy( &started_event );
+#endif
+
+destroy:
+ if ( daemon_base ) {
+ for ( i = 0; signal_handlers[i].signal; i++ ) {
+ if ( signal_handlers[i].event ) {
+ event_del( signal_handlers[i].event );
+ event_free( signal_handlers[i].event );
+ }
+ }
+ event_base_free( daemon_base );
+ }
+
+ if ( check & CHECK_LOGLEVEL ) {
+ (void)loglevel_print( stdout );
+ }
+ /* remember an error during destroy */
+ rc |= lload_destroy();
+
+stop:
+#ifdef HAVE_NT_EVENT_LOG
+ if ( is_NT_Service ) lutil_LogStoppedEvent( serverName );
+#endif
+
+ Debug( LDAP_DEBUG_ANY, "lloadd stopped.\n" );
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+ lutil_ReportShutdownComplete();
+#endif
+
+#ifdef LOG_DEBUG
+ closelog();
+#endif
+ lloadd_daemon_destroy();
+
+#ifdef HAVE_TLS
+ if ( lload_tls_ld ) {
+ ldap_pvt_tls_ctx_free( lload_tls_ctx );
+ ldap_unbind_ext( lload_tls_ld, NULL, NULL );
+ }
+ ldap_pvt_tls_destroy();
+#endif
+
+ if ( slapd_pid_file_unlink ) {
+ unlink( slapd_pid_file );
+ }
+ if ( slapd_args_file_unlink ) {
+ unlink( slapd_args_file );
+ }
+
+ lload_config_destroy();
+
+ if ( configfile ) ch_free( configfile );
+ if ( configdir ) ch_free( configdir );
+ if ( urls ) ch_free( urls );
+ if ( global_host ) ch_free( global_host );
+
+ /* kludge, get symbols referenced */
+ ldap_tavl_free( NULL, NULL );
+
+ MAIN_RETURN(rc);
+}
+
+#ifdef SIGPIPE
+
+/*
+ * Catch and discard terminated child processes, to avoid zombies.
+ */
+
+static void
+sigpipe( evutil_socket_t sig, short what, void *arg )
+{
+}
+
+#endif /* SIGPIPE */
+
+#ifdef LDAP_SIGCHLD
+
+/*
+ * Catch and discard terminated child processes, to avoid zombies.
+ */
+
+static void
+wait4child( evutil_socket_t sig, short what, void *arg )
+{
+ int save_errno = errno;
+
+#ifdef WNOHANG
+ do
+ errno = 0;
+#ifdef HAVE_WAITPID
+ while ( waitpid( (pid_t)-1, NULL, WNOHANG ) > 0 || errno == EINTR );
+#else
+ while ( wait3( NULL, WNOHANG, NULL ) > 0 || errno == EINTR );
+#endif
+#else
+ (void)wait( NULL );
+#endif
+ errno = save_errno;
+}
+
+#endif /* LDAP_SIGCHLD */
diff --git a/servers/lloadd/module_init.c b/servers/lloadd/module_init.c
new file mode 100644
index 0000000..1475538
--- /dev/null
+++ b/servers/lloadd/module_init.c
@@ -0,0 +1,185 @@
+/* module_init.c - module initialization functions */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "../servers/slapd/slap.h"
+#include "../servers/slapd/slap-config.h"
+
+#include "lload.h"
+#include "lber_pvt.h"
+
+#include "ldap_rq.h"
+
+ldap_pvt_thread_t lloadd_main_thread;
+struct lload_conf_info lload_info;
+
+void *
+lload_start_daemon( void *arg )
+{
+ int rc = 0;
+
+ daemon_base = event_base_new();
+ if ( !daemon_base ) {
+ Debug( LDAP_DEBUG_ANY, "lload_start_daemon: "
+ "main event base allocation failed\n" );
+ rc = 1;
+ goto done;
+ }
+
+ rc = lloadd_daemon( daemon_base );
+done:
+ if ( rc != LDAP_SUCCESS ) {
+ assert( lloadd_inited == 0 );
+ checked_lock( &lload_wait_mutex );
+ ldap_pvt_thread_cond_signal( &lload_wait_cond );
+ checked_unlock( &lload_wait_mutex );
+ }
+ return (void *)(uintptr_t)rc;
+}
+
+static int
+lload_pause_cb( BackendInfo *bi )
+{
+ if ( daemon_base ) {
+ lload_pause_server();
+ }
+ return 0;
+}
+
+static int
+lload_unpause_cb( BackendInfo *bi )
+{
+ if ( daemon_base ) {
+ lload_unpause_server();
+ }
+ return 0;
+}
+
+int
+lload_back_open( BackendInfo *bi )
+{
+ int rc = 0;
+
+ if ( slapMode & SLAP_TOOL_MODE ) {
+ return 0;
+ }
+
+ /* This will fail if we ever try to instantiate more than one lloadd within
+ * the process */
+ epoch_init();
+
+ if ( lload_tls_init() != 0 ) {
+ return -1;
+ }
+
+ if ( lload_monitor_open() != 0 ) {
+ return -1;
+ }
+
+ assert( lloadd_get_listeners() );
+
+ checked_lock( &lload_wait_mutex );
+ rc = ldap_pvt_thread_create( &lloadd_main_thread,
+ 0, lload_start_daemon, NULL );
+ if ( !rc ) {
+ ldap_pvt_thread_cond_wait( &lload_wait_cond, &lload_wait_mutex );
+ if ( lloadd_inited != 1 ) {
+ ldap_pvt_thread_join( lloadd_main_thread, (void *)NULL );
+ rc = -1;
+ }
+ }
+ checked_unlock( &lload_wait_mutex );
+ return rc;
+}
+
+int
+lload_back_close( BackendInfo *bi )
+{
+ if ( slapMode & SLAP_TOOL_MODE ) {
+ return 0;
+ }
+
+ assert( lloadd_inited == 1 );
+
+ checked_lock( &lload_wait_mutex );
+ event_base_loopexit( daemon_base, NULL );
+ ldap_pvt_thread_cond_wait( &lload_wait_cond, &lload_wait_mutex );
+ checked_unlock( &lload_wait_mutex );
+ ldap_pvt_thread_join( lloadd_main_thread, (void *)NULL );
+
+ return 0;
+}
+
+int
+lload_back_initialize( BackendInfo *bi )
+{
+ bi->bi_flags = SLAP_BFLAG_STANDALONE;
+ bi->bi_open = lload_back_open;
+ bi->bi_config = config_generic_wrapper;
+ bi->bi_pause = lload_pause_cb;
+ bi->bi_unpause = lload_unpause_cb;
+ bi->bi_close = lload_back_close;
+ bi->bi_destroy = 0;
+
+ bi->bi_db_init = 0;
+ bi->bi_db_config = 0;
+ bi->bi_db_open = 0;
+ bi->bi_db_close = 0;
+ bi->bi_db_destroy = 0;
+
+ bi->bi_op_bind = 0;
+ bi->bi_op_unbind = 0;
+ bi->bi_op_search = 0;
+ bi->bi_op_compare = 0;
+ bi->bi_op_modify = 0;
+ bi->bi_op_modrdn = 0;
+ bi->bi_op_add = 0;
+ bi->bi_op_delete = 0;
+ bi->bi_op_abandon = 0;
+
+ bi->bi_extended = 0;
+
+ bi->bi_chk_referrals = 0;
+
+ bi->bi_connection_init = 0;
+ bi->bi_connection_destroy = 0;
+
+ if ( lload_global_init() ) {
+ return -1;
+ }
+
+ bi->bi_private = &lload_info;
+ return lload_back_init_cf( bi );
+}
+
+SLAP_BACKEND_INIT_MODULE( lload )
diff --git a/servers/lloadd/monitor.c b/servers/lloadd/monitor.c
new file mode 100644
index 0000000..d791648
--- /dev/null
+++ b/servers/lloadd/monitor.c
@@ -0,0 +1,1150 @@
+/* init.c - initialize various things */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "lload.h"
+#include "lber_pvt.h"
+
+#include "ldap_rq.h"
+#include "lload-config.h"
+#include "../slapd/back-monitor/back-monitor.h"
+
+#define LLOAD_MONITOR_BALANCER_NAME "Load Balancer"
+#define LLOAD_MONITOR_BALANCER_RDN \
+ SLAPD_MONITOR_AT "=" LLOAD_MONITOR_BALANCER_NAME
+#define LLOAD_MONITOR_BALANCER_DN \
+ LLOAD_MONITOR_BALANCER_RDN "," SLAPD_MONITOR_BACKEND_DN
+
+#define LLOAD_MONITOR_INCOMING_NAME "Incoming Connections"
+#define LLOAD_MONITOR_INCOMING_RDN \
+ SLAPD_MONITOR_AT "=" LLOAD_MONITOR_INCOMING_NAME
+#define LLOAD_MONITOR_INCOMING_DN \
+ LLOAD_MONITOR_INCOMING_RDN "," LLOAD_MONITOR_BALANCER_DN
+
+#define LLOAD_MONITOR_OPERATIONS_NAME "Operations"
+#define LLOAD_MONITOR_OPERATIONS_RDN \
+ SLAPD_MONITOR_AT "=" LLOAD_MONITOR_OPERATIONS_NAME
+#define LLOAD_MONITOR_OPERATIONS_DN \
+ LLOAD_MONITOR_OPERATIONS_RDN "," LLOAD_MONITOR_BALANCER_DN
+
+#define LLOAD_MONITOR_BACKENDS_NAME "Backend Servers"
+#define LLOAD_MONITOR_BACKENDS_RDN \
+ SLAPD_MONITOR_AT "=" LLOAD_MONITOR_BACKENDS_NAME
+#define LLOAD_MONITOR_BACKENDS_DN \
+ LLOAD_MONITOR_BACKENDS_RDN "," LLOAD_MONITOR_BALANCER_DN
+
+struct lload_monitor_ops_t {
+ struct berval rdn;
+} lload_monitor_op[] = {
+ { BER_BVC("cn=Bind") },
+ { BER_BVC("cn=Other") },
+
+ { BER_BVNULL }
+};
+
+static ObjectClass *oc_olmBalancer;
+static ObjectClass *oc_olmBalancerServer;
+static ObjectClass *oc_olmBalancerConnection;
+static ObjectClass *oc_olmBalancerOperation;
+
+static ObjectClass *oc_monitorContainer;
+static ObjectClass *oc_monitorCounterObject;
+
+static AttributeDescription *ad_olmServerURI;
+static AttributeDescription *ad_olmReceivedOps;
+static AttributeDescription *ad_olmForwardedOps;
+static AttributeDescription *ad_olmRejectedOps;
+static AttributeDescription *ad_olmCompletedOps;
+static AttributeDescription *ad_olmFailedOps;
+static AttributeDescription *ad_olmConnectionType;
+static AttributeDescription *ad_olmPendingOps;
+static AttributeDescription *ad_olmPendingConnections;
+static AttributeDescription *ad_olmActiveConnections;
+static AttributeDescription *ad_olmIncomingConnections;
+static AttributeDescription *ad_olmOutgoingConnections;
+
+static struct {
+ char *name;
+ char *oid;
+} s_oid[] = {
+ { "olmBalancerAttributes", "olmModuleAttributes:1" },
+ { "olmBalancerObjectClasses", "olmModuleObjectClasses:1" },
+
+ { NULL }
+};
+
+static struct {
+ char *desc;
+ AttributeDescription **ad;
+} s_at[] = {
+ { "( olmBalancerAttributes:1 "
+ "NAME ( 'olmServerURI' ) "
+ "DESC 'URI of a backend server' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
+ "EQUALITY caseIgnoreMatch "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmServerURI },
+ { "( olmBalancerAttributes:2 "
+ "NAME ( 'olmReceivedOps' ) "
+ "DESC 'monitor received operations' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmReceivedOps },
+ { "( olmBalancerAttributes:3 "
+ "NAME ( 'olmForwardedOps' ) "
+ "DESC 'monitor forwarded operations' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmForwardedOps },
+ { "( olmBalancerAttributes:4 "
+ "NAME ( 'olmRejectedOps' ) "
+ "DESC 'monitor rejected operations' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmRejectedOps },
+ { "( olmBalancerAttributes:5 "
+ "NAME ( 'olmCompletedOps' ) "
+ "DESC 'monitor completed operations' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmCompletedOps },
+ { "( olmBalancerAttributes:6 "
+ "NAME ( 'olmFailedOps' ) "
+ "DESC 'monitor failed operations' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmFailedOps },
+ { "( olmBalancerAttributes:7 "
+ "NAME ( 'olmPendingOps' ) "
+ "DESC 'monitor number of pending operations' "
+ "EQUALITY integerMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmPendingOps },
+ { "( olmBalancerAttributes:8 "
+ "NAME ( 'olmPendingConnections' ) "
+ "DESC 'monitor number of pending connections' "
+ "EQUALITY integerMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmPendingConnections },
+ { "( olmBalancerAttributes:9 "
+ "NAME ( 'olmActiveConnections' ) "
+ "DESC 'monitor number of active connections' "
+ "EQUALITY integerMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmActiveConnections },
+ { "( olmBalancerAttributes:10 "
+ "NAME ( 'olmConnectionType' ) "
+ "DESC 'Connection type' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmConnectionType },
+ { "( olmBalancerAttributes:11 "
+ "NAME ( 'olmIncomingConnections' ) "
+ "DESC 'monitor number of incoming connections' "
+ "EQUALITY integerMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmIncomingConnections },
+ { "( olmBalancerAttributes:12 "
+ "NAME ( 'olmOutgoingConnections' ) "
+ "DESC 'monitor number of active connections' "
+ "EQUALITY integerMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmOutgoingConnections },
+
+ { NULL }
+};
+
+static struct {
+ char *name;
+ ObjectClass **oc;
+} s_moc[] = {
+ { "monitorContainer", &oc_monitorContainer },
+ { "monitorCounterObject", &oc_monitorCounterObject },
+
+ { NULL }
+};
+
+static struct {
+ char *desc;
+ ObjectClass **oc;
+} s_oc[] = {
+ { "( olmBalancerObjectClasses:1 "
+ "NAME ( 'olmBalancer' ) "
+ "SUP top STRUCTURAL "
+ "MAY ( "
+ "olmIncomingConnections "
+ "$ olmOutgoingConnections "
+ ") )",
+ &oc_olmBalancer },
+ { "( olmBalancerObjectClasses:2 "
+ "NAME ( 'olmBalancerServer' ) "
+ "SUP top STRUCTURAL "
+ "MAY ( "
+ "olmServerURI "
+ "$ olmActiveConnections "
+ "$ olmPendingConnections "
+ "$ olmPendingOps"
+ "$ olmReceivedOps "
+ "$ olmCompletedOps "
+ "$ olmFailedOps "
+ ") )",
+ &oc_olmBalancerServer },
+
+ { "( olmBalancerObjectClasses:3 "
+ "NAME ( 'olmBalancerOperation' ) "
+ "SUP top STRUCTURAL "
+ "MAY ( "
+ "olmReceivedOps "
+ "$ olmForwardedOps "
+ "$ olmRejectedOps "
+ "$ olmCompletedOps "
+ "$ olmFailedOps "
+ ") )",
+ &oc_olmBalancerOperation },
+ { "( olmBalancerObjectClasses:4 "
+ "NAME ( 'olmBalancerConnection' ) "
+ "SUP top STRUCTURAL "
+ "MAY ( "
+ "olmConnectionType "
+ "$ olmPendingOps "
+ "$ olmReceivedOps "
+ "$ olmCompletedOps "
+ "$ olmFailedOps "
+ ") )",
+ &oc_olmBalancerConnection },
+ { NULL }
+};
+
+static int
+lload_monitor_subsystem_destroy( BackendDB *be, monitor_subsys_t *ms )
+{
+ return LDAP_SUCCESS;
+}
+
+static int
+lload_monitor_backend_destroy( BackendDB *be, monitor_subsys_t *ms )
+{
+ LloadBackend *b = ms->mss_private;
+ monitor_extra_t *mbe;
+ int rc = LDAP_SUCCESS;
+
+ mbe = (monitor_extra_t *)be->bd_info->bi_extra;
+ if ( b->b_monitor ) {
+ ms->mss_destroy = lload_monitor_subsystem_destroy;
+
+ assert( b->b_monitor == ms );
+ b->b_monitor = NULL;
+
+ rc = mbe->unregister_entry( &ms->mss_ndn );
+ ber_memfree( ms->mss_dn.bv_val );
+ ber_memfree( ms->mss_ndn.bv_val );
+ }
+
+ return rc;
+}
+
+static void
+lload_monitor_balancer_dispose( void **priv )
+{
+ return;
+}
+
+static int
+lload_monitor_balancer_free( Entry *e, void **priv )
+{
+ return LDAP_SUCCESS;
+}
+
+static int
+lload_monitor_balancer_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ void *priv )
+{
+ Attribute *a;
+
+ a = attr_find( e->e_attrs, ad_olmIncomingConnections );
+ assert( a != NULL );
+
+ UI2BV( &a->a_vals[0], lload_stats.global_incoming );
+
+ a = attr_find( e->e_attrs, ad_olmOutgoingConnections );
+ assert( a != NULL );
+
+ UI2BV( &a->a_vals[0], lload_stats.global_outgoing );
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+lload_monitor_ops_update( Operation *op, SlapReply *rs, Entry *e, void *priv )
+{
+ Attribute *a;
+ lload_counters_t *counters = (lload_counters_t *)priv;
+
+ a = attr_find( e->e_attrs, ad_olmReceivedOps );
+ assert( a != NULL );
+ UI2BV( &a->a_vals[0], counters->lc_ops_received );
+
+ a = attr_find( e->e_attrs, ad_olmForwardedOps );
+ assert( a != NULL );
+ UI2BV( &a->a_vals[0], counters->lc_ops_forwarded );
+
+ a = attr_find( e->e_attrs, ad_olmRejectedOps );
+ assert( a != NULL );
+ UI2BV( &a->a_vals[0], counters->lc_ops_rejected );
+
+ a = attr_find( e->e_attrs, ad_olmCompletedOps );
+ assert( a != NULL );
+ UI2BV( &a->a_vals[0], counters->lc_ops_completed );
+
+ a = attr_find( e->e_attrs, ad_olmFailedOps );
+ assert( a != NULL );
+ UI2BV( &a->a_vals[0], counters->lc_ops_failed );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static void
+lload_monitor_ops_dispose( void **priv )
+{
+ return;
+}
+
+static int
+lload_monitor_ops_free( Entry *e, void **priv )
+{
+ return LDAP_SUCCESS;
+}
+
+static int
+lload_monitor_balancer_init( BackendDB *be, monitor_subsys_t *ms )
+{
+ monitor_extra_t *mbe;
+ Entry *e;
+ int rc;
+ monitor_callback_t *cb;
+ struct berval value = BER_BVC("0");
+
+ assert( be != NULL );
+
+ mbe = (monitor_extra_t *)be->bd_info->bi_extra;
+
+ dnNormalize( 0, NULL, NULL, &ms->mss_dn, &ms->mss_ndn, NULL );
+
+ e = mbe->entry_stub( &ms->mss_dn, &ms->mss_ndn, &ms->mss_rdn,
+ oc_olmBalancer, NULL, NULL );
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "lload_monitor_balancer_init: "
+ "unable to create entry \"%s,%s\"\n",
+ ms->mss_rdn.bv_val, ms->mss_ndn.bv_val );
+ return -1;
+ }
+
+ ch_free( ms->mss_ndn.bv_val );
+ ber_dupbv( &ms->mss_dn, &e->e_name );
+ ber_dupbv( &ms->mss_ndn, &e->e_nname );
+
+ cb = ch_calloc( sizeof(monitor_callback_t), 1 );
+ cb->mc_update = lload_monitor_balancer_update;
+ cb->mc_free = lload_monitor_balancer_free;
+ cb->mc_dispose = lload_monitor_balancer_dispose;
+ cb->mc_private = NULL;
+
+ attr_merge_normalize_one( e, ad_olmIncomingConnections, &value, NULL );
+ attr_merge_normalize_one( e, ad_olmOutgoingConnections, &value, NULL );
+
+ rc = mbe->register_entry( e, cb, ms, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "lload_monitor_balancer_init: "
+ "unable to register entry \"%s\" for monitoring\n",
+ e->e_name.bv_val );
+ goto done;
+ }
+
+done:
+ entry_free( e );
+
+ return rc;
+}
+
+static int
+lload_monitor_ops_init( BackendDB *be, monitor_subsys_t *ms )
+{
+ monitor_extra_t *mbe;
+ Entry *e, *parent;
+ int rc;
+ int i;
+ struct berval value = BER_BVC("0");
+
+ assert( be != NULL );
+
+ mbe = (monitor_extra_t *)be->bd_info->bi_extra;
+
+ dnNormalize( 0, NULL, NULL, &ms->mss_dn, &ms->mss_ndn, NULL );
+ ms->mss_destroy = lload_monitor_subsystem_destroy;
+
+ parent = mbe->entry_stub( &ms->mss_dn, &ms->mss_ndn, &ms->mss_rdn,
+ oc_monitorContainer, NULL, NULL );
+ if ( parent == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "lload_monitor_ops_init: "
+ "unable to create entry \"%s,%s\"\n",
+ ms->mss_rdn.bv_val, ms->mss_ndn.bv_val );
+ return -1;
+ }
+ ch_free( ms->mss_ndn.bv_val );
+ ber_dupbv( &ms->mss_dn, &parent->e_name );
+ ber_dupbv( &ms->mss_ndn, &parent->e_nname );
+
+ rc = mbe->register_entry( parent, NULL, ms, MONITOR_F_PERSISTENT_CH );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "lload_monitor_ops_init: "
+ "unable to register entry \"%s\" for monitoring\n",
+ parent->e_name.bv_val );
+ goto done;
+ }
+
+ for ( i = 0; lload_monitor_op[i].rdn.bv_val != NULL; i++ ) {
+ monitor_callback_t *cb;
+ e = mbe->entry_stub( &parent->e_name, &parent->e_nname,
+ &lload_monitor_op[i].rdn, oc_olmBalancerOperation, NULL, NULL );
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "lload_monitor_ops_init: "
+ "unable to create entry \"%s,%s\"\n",
+ lload_monitor_op[i].rdn.bv_val, parent->e_nname.bv_val );
+ return -1;
+ }
+
+ /* attr_merge_normalize_one( e, ad_olmDbOperations, &value, NULL ); */
+
+ /*
+ * We cannot share a single callback between entries.
+ *
+ * monitor_cache_destroy() tries to free all callbacks and it's called
+ * before mss_destroy() so we have no chance of handling it ourselves
+ */
+ cb = ch_calloc( sizeof(monitor_callback_t), 1 );
+ cb->mc_update = lload_monitor_ops_update;
+ cb->mc_free = lload_monitor_ops_free;
+ cb->mc_dispose = lload_monitor_ops_dispose;
+ cb->mc_private = &lload_stats.counters[i];
+
+ attr_merge_normalize_one( e, ad_olmReceivedOps, &value, NULL );
+ attr_merge_normalize_one( e, ad_olmForwardedOps, &value, NULL );
+ attr_merge_normalize_one( e, ad_olmRejectedOps, &value, NULL );
+ attr_merge_normalize_one( e, ad_olmCompletedOps, &value, NULL );
+ attr_merge_normalize_one( e, ad_olmFailedOps, &value, NULL );
+
+ rc = mbe->register_entry( e, cb, ms, 0 );
+
+ entry_free( e );
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "lload_monitor_ops_init: "
+ "unable to register entry \"%s\" for monitoring\n",
+ e->e_name.bv_val );
+ ch_free( cb );
+ break;
+ }
+ }
+
+done:
+ entry_free( parent );
+ return rc;
+}
+
+static int
+lload_monitor_in_conn_entry( LloadConnection *conn, void *argv )
+{
+ Entry *e;
+ monitor_entry_t *mp;
+ struct lload_monitor_conn_arg *arg = argv;
+ monitor_extra_t *mbe = arg->op->o_bd->bd_info->bi_extra;
+ char buf[SLAP_TEXT_BUFLEN];
+ struct berval bv;
+
+ bv.bv_val = buf;
+ bv.bv_len = snprintf(
+ bv.bv_val, SLAP_TEXT_BUFLEN, "cn=Connection %lu", conn->c_connid );
+
+ e = mbe->entry_stub( &arg->ms->mss_dn, &arg->ms->mss_ndn, &bv,
+ oc_olmBalancerConnection, NULL, NULL );
+
+ mp = mbe->entrypriv_create();
+ e->e_private = mp;
+ mp->mp_info = arg->ms;
+ mp->mp_flags = MONITOR_F_SUB | MONITOR_F_VOLATILE;
+
+ *arg->ep = e;
+ arg->ep = &mp->mp_next;
+
+ return 0;
+}
+
+static int
+lload_monitor_in_conn_create(
+ Operation *op,
+ SlapReply *rs,
+ struct berval *ndn,
+ Entry *e_parent,
+ Entry **ep )
+{
+ monitor_entry_t *mp_parent;
+ struct lload_monitor_conn_arg arg = {
+ .op = op,
+ .ep = ep,
+ };
+
+ assert( e_parent->e_private != NULL );
+
+ mp_parent = e_parent->e_private;
+ arg.ms = (monitor_subsys_t *)mp_parent->mp_info;
+
+ checked_lock( &clients_mutex );
+ connections_walk(
+ &clients_mutex, &clients, lload_monitor_in_conn_entry, &arg );
+ checked_unlock( &clients_mutex );
+
+ return 0;
+}
+
+static int
+lload_monitor_up_conn_entry( LloadConnection *c, void *argv )
+{
+ Entry *e;
+ monitor_entry_t *mp;
+ struct lload_monitor_conn_arg *arg = argv;
+ monitor_extra_t *mbe = arg->op->o_bd->bd_info->bi_extra;
+ char buf[SLAP_TEXT_BUFLEN];
+ struct berval bv_rdn,
+ bv_type = BER_BVNULL,
+ bv_pending = BER_BVNULL,
+ bv_received = BER_BVNULL,
+ bv_completed = BER_BVNULL,
+ bv_failed = BER_BVNULL;
+
+ bv_rdn.bv_val = buf;
+ bv_rdn.bv_len = snprintf(
+ bv_rdn.bv_val, SLAP_TEXT_BUFLEN, "cn=Connection %lu", c->c_connid );
+
+ e = mbe->entry_stub( &arg->ms->mss_dn, &arg->ms->mss_ndn, &bv_rdn,
+ oc_olmBalancerConnection, NULL, NULL );
+
+ switch ( c->c_type ) {
+ case LLOAD_C_OPEN: {
+ struct berval bv = BER_BVC("regular");
+ bv_type = bv;
+ } break;
+ case LLOAD_C_PREPARING: {
+ struct berval bv = BER_BVC("preparing");
+ bv_type = bv;
+ } break;
+ case LLOAD_C_BIND: {
+ struct berval bv = BER_BVC("bind");
+ bv_type = bv;
+ } break;
+ case LLOAD_C_PRIVILEGED: {
+ struct berval bv = BER_BVC("privileged");
+ bv_type = bv;
+ } break;
+ default: {
+ struct berval bv = BER_BVC("unknown");
+ bv_type = bv;
+ } break;
+ }
+
+ UI2BV( &bv_pending, (long long unsigned int)c->c_n_ops_executing );
+ UI2BV( &bv_received, c->c_counters.lc_ops_received );
+ UI2BV( &bv_completed, c->c_counters.lc_ops_completed );
+ UI2BV( &bv_failed, c->c_counters.lc_ops_failed );
+
+ attr_merge_normalize_one( e, ad_olmConnectionType, &bv_type, NULL );
+ attr_merge_normalize_one( e, ad_olmPendingOps, &bv_pending, NULL );
+ attr_merge_normalize_one( e, ad_olmReceivedOps, &bv_received, NULL );
+ attr_merge_normalize_one( e, ad_olmCompletedOps, &bv_completed, NULL );
+ attr_merge_normalize_one( e, ad_olmFailedOps, &bv_failed, NULL );
+
+ ch_free( bv_pending.bv_val );
+ ch_free( bv_received.bv_val );
+ ch_free( bv_completed.bv_val );
+ ch_free( bv_failed.bv_val );
+ mp = mbe->entrypriv_create();
+ e->e_private = mp;
+ mp->mp_info = arg->ms;
+ mp->mp_flags = MONITOR_F_SUB | MONITOR_F_VOLATILE;
+
+ *arg->ep = e;
+ arg->ep = &mp->mp_next;
+
+ return 0;
+}
+
+static int
+lload_monitor_up_conn_create(
+ Operation *op,
+ SlapReply *rs,
+ struct berval *ndn,
+ Entry *e_parent,
+ Entry **ep )
+{
+ monitor_entry_t *mp_parent;
+ monitor_subsys_t *ms;
+ LloadBackend *b;
+ struct lload_monitor_conn_arg arg = {
+ .op = op,
+ .ep = ep,
+ };
+
+ assert( e_parent->e_private != NULL );
+
+ mp_parent = e_parent->e_private;
+ ms = (monitor_subsys_t *)mp_parent->mp_info;
+ b = ms->mss_private;
+
+ if ( !b ) {
+ return -1;
+ }
+
+ arg.ms = ms;
+
+ checked_lock( &b->b_mutex );
+ connections_walk_last( &b->b_mutex, &b->b_conns, b->b_last_conn,
+ lload_monitor_up_conn_entry, &arg );
+
+ connections_walk_last( &b->b_mutex, &b->b_bindconns, b->b_last_bindconn,
+ lload_monitor_up_conn_entry, &arg );
+ checked_unlock( &b->b_mutex );
+
+ return 0;
+}
+
+int
+lload_monitor_incoming_conn_init( BackendDB *be, monitor_subsys_t *ms )
+{
+ monitor_extra_t *mbe;
+ Entry *e;
+ int rc;
+
+ assert( be != NULL );
+ mbe = (monitor_extra_t *)be->bd_info->bi_extra;
+
+ ms->mss_create = lload_monitor_in_conn_create;
+ ms->mss_destroy = lload_monitor_subsystem_destroy;
+
+ dnNormalize( 0, NULL, NULL, &ms->mss_dn, &ms->mss_ndn, NULL );
+
+ e = mbe->entry_stub( &ms->mss_dn, &ms->mss_ndn, &ms->mss_rdn,
+ oc_monitorContainer, NULL, NULL );
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "lload_monitor_incoming_conn_init: "
+ "unable to create entry \"%s,%s\"\n",
+ ms->mss_rdn.bv_val, ms->mss_ndn.bv_val );
+ return -1;
+ }
+ ch_free( ms->mss_ndn.bv_val );
+ ber_dupbv( &ms->mss_dn, &e->e_name );
+ ber_dupbv( &ms->mss_ndn, &e->e_nname );
+
+ rc = mbe->register_entry( e, NULL, ms, MONITOR_F_VOLATILE_CH );
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "lload_monitor_incoming_conn_init: "
+ "unable to register entry \"%s\" for monitoring\n",
+ e->e_name.bv_val );
+ goto done;
+ }
+done:
+ entry_free( e );
+
+ return rc;
+}
+
+static int
+lload_monitor_server_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ void *priv )
+{
+ Attribute *a;
+ LloadBackend *b = priv;
+ LloadConnection *c;
+ LloadPendingConnection *pc;
+ ldap_pvt_mp_t active = 0, pending = 0, received = 0, completed = 0,
+ failed = 0;
+ int i;
+
+ checked_lock( &b->b_mutex );
+ active = b->b_active + b->b_bindavail;
+
+ LDAP_CIRCLEQ_FOREACH ( c, &b->b_preparing, c_next ) {
+ pending++;
+ }
+
+ LDAP_LIST_FOREACH( pc, &b->b_connecting, next ) {
+ pending++;
+ }
+
+ for ( i = 0; i < LLOAD_STATS_OPS_LAST; i++ ) {
+ received += b->b_counters[i].lc_ops_received;
+ completed += b->b_counters[i].lc_ops_completed;
+ failed += b->b_counters[i].lc_ops_failed;
+ }
+
+ a = attr_find( e->e_attrs, ad_olmPendingOps );
+ assert( a != NULL );
+ UI2BV( &a->a_vals[0], (long long unsigned int)b->b_n_ops_executing );
+
+ checked_unlock( &b->b_mutex );
+
+ /* Right now, there is no way to retrieve the entry from monitor's
+ * cache to replace URI at the moment it is modified */
+ a = attr_find( e->e_attrs, ad_olmServerURI );
+ assert( a != NULL );
+ ber_bvreplace( &a->a_vals[0], &b->b_uri );
+
+ a = attr_find( e->e_attrs, ad_olmActiveConnections );
+ assert( a != NULL );
+ UI2BV( &a->a_vals[0], active );
+
+ a = attr_find( e->e_attrs, ad_olmPendingConnections );
+ assert( a != NULL );
+ UI2BV( &a->a_vals[0], pending );
+
+ a = attr_find( e->e_attrs, ad_olmReceivedOps );
+ assert( a != NULL );
+ UI2BV( &a->a_vals[0], received );
+
+ a = attr_find( e->e_attrs, ad_olmCompletedOps );
+ assert( a != NULL );
+ UI2BV( &a->a_vals[0], completed );
+
+ a = attr_find( e->e_attrs, ad_olmFailedOps );
+ assert( a != NULL );
+ UI2BV( &a->a_vals[0], failed );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+lload_monitor_backend_open( BackendDB *be, monitor_subsys_t *ms )
+{
+ Entry *e;
+ struct berval value = BER_BVC("0");
+ monitor_extra_t *mbe;
+ monitor_callback_t *cb;
+ LloadBackend *b = ms->mss_private;
+ int rc;
+
+ assert( be != NULL );
+ mbe = (monitor_extra_t *)be->bd_info->bi_extra;
+
+ dnNormalize( 0, NULL, NULL, &ms->mss_dn, &ms->mss_ndn, NULL );
+ e = mbe->entry_stub( &ms->mss_dn, &ms->mss_ndn, &ms->mss_rdn,
+ oc_olmBalancerServer, NULL, NULL );
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "lload_monitor_backend_open: "
+ "unable to create entry \"%s,%s\"\n",
+ ms->mss_rdn.bv_val, ms->mss_ndn.bv_val );
+ return -1;
+ }
+
+ ch_free( ms->mss_ndn.bv_val );
+ ber_dupbv( &ms->mss_dn, &e->e_name );
+ ber_dupbv( &ms->mss_ndn, &e->e_nname );
+
+ cb = ch_calloc( sizeof(monitor_callback_t), 1 );
+ cb->mc_update = lload_monitor_server_update;
+ cb->mc_free = NULL;
+ cb->mc_dispose = NULL;
+ cb->mc_private = b;
+
+ attr_merge_normalize_one( e, ad_olmServerURI, &b->b_uri, NULL );
+ attr_merge_normalize_one( e, ad_olmActiveConnections, &value, NULL );
+ attr_merge_normalize_one( e, ad_olmPendingConnections, &value, NULL );
+ attr_merge_normalize_one( e, ad_olmPendingOps, &value, NULL );
+ attr_merge_normalize_one( e, ad_olmReceivedOps, &value, NULL );
+ attr_merge_normalize_one( e, ad_olmCompletedOps, &value, NULL );
+ attr_merge_normalize_one( e, ad_olmFailedOps, &value, NULL );
+
+ rc = mbe->register_entry( e, cb, ms, MONITOR_F_VOLATILE_CH );
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "lload_monitor_backend_open: "
+ "unable to register entry \"%s\" for monitoring\n",
+ e->e_name.bv_val );
+ goto done;
+ }
+
+ b->b_monitor = ms;
+ ms->mss_destroy = lload_monitor_backend_destroy;
+
+done:
+ entry_free( e );
+ return rc;
+}
+
+int
+lload_monitor_backend_init( BackendInfo *bi, LloadBackend *b )
+{
+ monitor_extra_t *mbe;
+ monitor_subsys_t *bk_mss;
+
+ mbe = (monitor_extra_t *)bi->bi_extra;
+
+ /* FIXME: With back-monitor as it works now, there is no way to know when
+ * this can be safely freed so we leak it on shutdown */
+ bk_mss = ch_calloc( 1, sizeof(monitor_subsys_t) );
+ bk_mss->mss_rdn.bv_len = sizeof("cn=") + b->b_name.bv_len;
+ bk_mss->mss_rdn.bv_val = ch_malloc( bk_mss->mss_rdn.bv_len );
+ bk_mss->mss_rdn.bv_len = snprintf( bk_mss->mss_rdn.bv_val,
+ bk_mss->mss_rdn.bv_len, "cn=%s", b->b_name.bv_val );
+
+ ber_str2bv( LLOAD_MONITOR_BACKENDS_DN, 0, 0, &bk_mss->mss_dn );
+ bk_mss->mss_name = b->b_name.bv_val;
+ bk_mss->mss_flags = MONITOR_F_VOLATILE_CH;
+ bk_mss->mss_open = lload_monitor_backend_open;
+ bk_mss->mss_create = lload_monitor_up_conn_create;
+ bk_mss->mss_destroy = lload_monitor_subsystem_destroy;
+ bk_mss->mss_update = NULL;
+ bk_mss->mss_private = b;
+
+ if ( mbe->register_subsys_late( bk_mss ) ) {
+ Debug( LDAP_DEBUG_ANY, "lload_monitor_backend_init: "
+ "failed to register backend %s\n",
+ bk_mss->mss_name );
+ return -1;
+ }
+ return LDAP_SUCCESS;
+}
+
+int
+lload_monitor_backends_init( BackendDB *be, monitor_subsys_t *ms )
+{
+ monitor_extra_t *mbe;
+ Entry *e;
+ int rc;
+ LloadBackend *b;
+
+ assert( be != NULL );
+ mbe = (monitor_extra_t *)be->bd_info->bi_extra;
+
+ dnNormalize( 0, NULL, NULL, &ms->mss_dn, &ms->mss_ndn, NULL );
+
+ e = mbe->entry_stub( &ms->mss_dn, &ms->mss_ndn, &ms->mss_rdn,
+ oc_monitorContainer, NULL, NULL );
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "lload_monitor_incoming_conn_init: "
+ "unable to create entry \"%s,%s\"\n",
+ ms->mss_rdn.bv_val, ms->mss_ndn.bv_val );
+ return -1;
+ }
+ ch_free( ms->mss_ndn.bv_val );
+ ber_dupbv( &ms->mss_dn, &e->e_name );
+ ber_dupbv( &ms->mss_ndn, &e->e_nname );
+
+ rc = mbe->register_entry( e, NULL, ms, MONITOR_F_PERSISTENT_CH );
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "lload_monitor_backends_init: "
+ "unable to register entry \"%s\" for monitoring\n",
+ e->e_name.bv_val );
+ goto done;
+ }
+
+ LDAP_CIRCLEQ_FOREACH ( b, &backend, b_next ) {
+ if ( (rc = lload_monitor_backend_init( be->bd_info, b )) ) {
+ break;
+ }
+ }
+done:
+ entry_free( e );
+
+ return rc;
+}
+
+static int
+lload_monitor_incoming_count( LloadConnection *conn, void *argv )
+{
+ lload_global_stats_t *tmp_stats = argv;
+ tmp_stats->global_incoming++;
+ return 0;
+}
+
+/*
+ * Update all global statistics other than rejected and received,
+ * which are updated in real time
+ */
+void *
+lload_monitor_update_global_stats( void *ctx, void *arg )
+{
+ struct re_s *rtask = arg;
+ lload_global_stats_t tmp_stats = {};
+ LloadBackend *b;
+ int i;
+
+ Debug( LDAP_DEBUG_TRACE, "lload_monitor_update_global_stats: "
+ "updating stats\n" );
+
+ /* count incoming connections */
+ checked_lock( &clients_mutex );
+ connections_walk( &clients_mutex, &clients, lload_monitor_incoming_count,
+ &tmp_stats );
+ checked_unlock( &clients_mutex );
+
+ LDAP_CIRCLEQ_FOREACH ( b, &backend, b_next ) {
+ checked_lock( &b->b_mutex );
+ tmp_stats.global_outgoing += b->b_active + b->b_bindavail;
+
+ /* merge completed and failed stats */
+ for ( i = 0; i < LLOAD_STATS_OPS_LAST; i++ ) {
+ tmp_stats.counters[i].lc_ops_completed +=
+ b->b_counters[i].lc_ops_completed;
+ tmp_stats.counters[i].lc_ops_failed +=
+ b->b_counters[i].lc_ops_failed;
+ }
+ checked_unlock( &b->b_mutex );
+ }
+
+ /* update lload_stats */
+ lload_stats.global_outgoing = tmp_stats.global_outgoing;
+ lload_stats.global_incoming = tmp_stats.global_incoming;
+ for ( i = 0; i < LLOAD_STATS_OPS_LAST; i++ ) {
+ lload_stats.counters[i].lc_ops_completed =
+ tmp_stats.counters[i].lc_ops_completed;
+ lload_stats.counters[i].lc_ops_failed =
+ tmp_stats.counters[i].lc_ops_failed;
+ }
+
+ /* reschedule */
+ checked_lock( &slapd_rq.rq_mutex );
+ ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
+ checked_unlock( &slapd_rq.rq_mutex );
+ return NULL;
+}
+
+static char *lload_subsys_rdn[] = {
+ LLOAD_MONITOR_BALANCER_RDN,
+ LLOAD_MONITOR_INCOMING_RDN,
+ LLOAD_MONITOR_OPERATIONS_RDN,
+ LLOAD_MONITOR_BACKENDS_RDN,
+ NULL
+};
+
+static struct monitor_subsys_t balancer_subsys[] = {
+ {
+ LLOAD_MONITOR_BALANCER_NAME,
+ BER_BVNULL,
+ BER_BVC(SLAPD_MONITOR_BACKEND_DN),
+ BER_BVNULL,
+ { BER_BVC("Load Balancer information"),
+ BER_BVNULL },
+ MONITOR_F_PERSISTENT_CH,
+ lload_monitor_balancer_init,
+ lload_monitor_subsystem_destroy, /* destroy */
+ NULL, /* update */
+ NULL, /* create */
+ NULL /* modify */
+ },
+ {
+ LLOAD_MONITOR_INCOMING_NAME,
+ BER_BVNULL,
+ BER_BVC(LLOAD_MONITOR_BALANCER_DN),
+ BER_BVNULL,
+ { BER_BVC("Load Balancer incoming connections"),
+ BER_BVNULL },
+ MONITOR_F_VOLATILE_CH,
+ lload_monitor_incoming_conn_init,
+ lload_monitor_subsystem_destroy, /* destroy */
+ NULL, /* update */
+ NULL, /* create */
+ NULL /* modify */
+ },
+ {
+ LLOAD_MONITOR_OPERATIONS_NAME,
+ BER_BVNULL,
+ BER_BVC(LLOAD_MONITOR_BALANCER_DN),
+ BER_BVNULL,
+ { BER_BVC("Load Balancer global operation statistics"),
+ BER_BVNULL },
+ MONITOR_F_PERSISTENT_CH,
+ lload_monitor_ops_init,
+ lload_monitor_subsystem_destroy, /* destroy */
+ NULL, /* update */
+ NULL, /* create */
+ NULL /* modify */
+ },
+ {
+ LLOAD_MONITOR_BACKENDS_NAME,
+ BER_BVNULL,
+ BER_BVC(LLOAD_MONITOR_BALANCER_DN),
+ BER_BVNULL,
+ { BER_BVC("Load Balancer Backends information"),
+ BER_BVNULL },
+ MONITOR_F_PERSISTENT_CH,
+ lload_monitor_backends_init,
+ lload_monitor_subsystem_destroy, /* destroy */
+ NULL, /* update */
+ NULL, /* create */
+ NULL /* modify */
+ },
+ { NULL }
+};
+
+int
+lload_monitor_open( void )
+{
+ static int lload_monitor_initialized_failure = 1;
+ static int lload_monitor_initialized = 0;
+ BackendInfo *mi;
+ monitor_extra_t *mbe;
+ monitor_subsys_t *mss;
+ ConfigArgs c;
+ char *argv[3], **rdn;
+ int i, rc;
+
+ /* check if monitor is configured and usable */
+ mi = backend_info( "monitor" );
+ if ( !mi || !mi->bi_extra ) {
+ Debug( LDAP_DEBUG_CONFIG, "lload_monitor_open: "
+ "monitor backend not available, monitoring disabled\n" );
+ return 0;
+ }
+ mbe = mi->bi_extra;
+
+ /* don't bother if monitor is not configured */
+ if ( !mbe->is_configured() ) {
+ static int warning = 0;
+
+ if ( warning++ == 0 ) {
+ Debug( LDAP_DEBUG_CONFIG, "lload_monitor_open: "
+ "monitoring disabled; "
+ "configure monitor database to enable\n" );
+ }
+
+ return 0;
+ }
+
+ if ( lload_monitor_initialized++ ) {
+ return lload_monitor_initialized_failure;
+ }
+
+ argv[0] = "lload monitor";
+ c.argv = argv;
+ c.argc = 3;
+ c.fname = argv[0];
+ for ( i = 0; s_oid[i].name; i++ ) {
+ argv[1] = s_oid[i].name;
+ argv[2] = s_oid[i].oid;
+
+ if ( parse_oidm( &c, 0, NULL ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "lload_monitor_open: "
+ "unable to add objectIdentifier \"%s=%s\"\n",
+ s_oid[i].name, s_oid[i].oid );
+ return 2;
+ }
+ }
+
+ for ( i = 0; s_at[i].desc != NULL; i++ ) {
+ rc = register_at( s_at[i].desc, s_at[i].ad, 1 );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "lload_monitor_open: "
+ "register_at failed for attributeType (%s)\n",
+ s_at[i].desc );
+ return 3;
+
+ } else {
+ (*s_at[i].ad)->ad_type->sat_flags |= SLAP_AT_HIDE;
+ }
+ }
+
+ for ( i = 0; s_oc[i].desc != NULL; i++ ) {
+ rc = register_oc( s_oc[i].desc, s_oc[i].oc, 1 );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "lload_monitor_open: "
+ "register_oc failed for objectClass (%s)\n",
+ s_oc[i].desc );
+ return 4;
+
+ } else {
+ (*s_oc[i].oc)->soc_flags |= SLAP_OC_HIDE;
+ }
+ }
+
+ for ( i = 0; s_moc[i].name != NULL; i++ ) {
+ *s_moc[i].oc = oc_find( s_moc[i].name );
+ if ( !*s_moc[i].oc ) {
+ Debug( LDAP_DEBUG_ANY, "lload_monitor_open: "
+ "failed to find objectClass (%s)\n",
+ s_moc[i].name );
+ return 5;
+ }
+ }
+
+ /* register the subsystems - Servers are registered in backends_init */
+ for ( mss = balancer_subsys, rdn = lload_subsys_rdn; mss->mss_name;
+ mss++, rdn++ ) {
+ ber_str2bv( *rdn, 0, 1, &mss->mss_rdn );
+ if ( mbe->register_subsys_late( mss ) ) {
+ Debug( LDAP_DEBUG_ANY, "lload_monitor_open: "
+ "failed to register %s subsystem\n",
+ mss->mss_name );
+ return -1;
+ }
+ }
+
+ checked_lock( &slapd_rq.rq_mutex );
+ ldap_pvt_runqueue_insert( &slapd_rq, 1, lload_monitor_update_global_stats,
+ NULL, "lload_monitor_update_global_stats", "lloadd" );
+ checked_unlock( &slapd_rq.rq_mutex );
+
+ return (lload_monitor_initialized_failure = LDAP_SUCCESS);
+}
diff --git a/servers/lloadd/nt_svc.c b/servers/lloadd/nt_svc.c
new file mode 120000
index 0000000..eb3ffdb
--- /dev/null
+++ b/servers/lloadd/nt_svc.c
@@ -0,0 +1 @@
+../slapd/nt_svc.c \ No newline at end of file
diff --git a/servers/lloadd/operation.c b/servers/lloadd/operation.c
new file mode 100644
index 0000000..9074404
--- /dev/null
+++ b/servers/lloadd/operation.c
@@ -0,0 +1,699 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include "lutil.h"
+#include "lload.h"
+
+ldap_pvt_thread_mutex_t lload_pin_mutex;
+unsigned long lload_next_pin = 1;
+
+ber_tag_t
+slap_req2res( ber_tag_t tag )
+{
+ switch ( tag ) {
+ case LDAP_REQ_ADD:
+ case LDAP_REQ_BIND:
+ case LDAP_REQ_COMPARE:
+ case LDAP_REQ_EXTENDED:
+ case LDAP_REQ_MODIFY:
+ case LDAP_REQ_MODRDN:
+ tag++;
+ break;
+
+ case LDAP_REQ_DELETE:
+ tag = LDAP_RES_DELETE;
+ break;
+
+ case LDAP_REQ_ABANDON:
+ case LDAP_REQ_UNBIND:
+ tag = LBER_SEQUENCE;
+ break;
+
+ case LDAP_REQ_SEARCH:
+ tag = LDAP_RES_SEARCH_RESULT;
+ break;
+
+ default:
+ tag = LBER_SEQUENCE;
+ }
+
+ return tag;
+}
+
+const char *
+lload_msgtype2str( ber_tag_t tag )
+{
+ switch ( tag ) {
+ case LDAP_REQ_ABANDON: return "abandon request";
+ case LDAP_REQ_ADD: return "add request";
+ case LDAP_REQ_BIND: return "bind request";
+ case LDAP_REQ_COMPARE: return "compare request";
+ case LDAP_REQ_DELETE: return "delete request";
+ case LDAP_REQ_EXTENDED: return "extended request";
+ case LDAP_REQ_MODIFY: return "modify request";
+ case LDAP_REQ_RENAME: return "rename request";
+ case LDAP_REQ_SEARCH: return "search request";
+ case LDAP_REQ_UNBIND: return "unbind request";
+
+ case LDAP_RES_ADD: return "add result";
+ case LDAP_RES_BIND: return "bind result";
+ case LDAP_RES_COMPARE: return "compare result";
+ case LDAP_RES_DELETE: return "delete result";
+ case LDAP_RES_EXTENDED: return "extended result";
+ case LDAP_RES_INTERMEDIATE: return "intermediate response";
+ case LDAP_RES_MODIFY: return "modify result";
+ case LDAP_RES_RENAME: return "rename result";
+ case LDAP_RES_SEARCH_ENTRY: return "search-entry response";
+ case LDAP_RES_SEARCH_REFERENCE: return "search-reference response";
+ case LDAP_RES_SEARCH_RESULT: return "search result";
+ }
+ return "unknown message";
+}
+
+int
+operation_client_cmp( const void *left, const void *right )
+{
+ const LloadOperation *l = left, *r = right;
+
+ assert( l->o_client_connid == r->o_client_connid );
+ if ( l->o_client_msgid || r->o_client_msgid ) {
+ return ( l->o_client_msgid < r->o_client_msgid ) ?
+ -1 :
+ ( l->o_client_msgid > r->o_client_msgid );
+ } else {
+ return ( l->o_pin_id < r->o_pin_id ) ? -1 :
+ ( l->o_pin_id > r->o_pin_id );
+ }
+}
+
+int
+operation_upstream_cmp( const void *left, const void *right )
+{
+ const LloadOperation *l = left, *r = right;
+
+ assert( l->o_upstream_connid == r->o_upstream_connid );
+ if ( l->o_upstream_msgid || r->o_upstream_msgid ) {
+ return ( l->o_upstream_msgid < r->o_upstream_msgid ) ?
+ -1 :
+ ( l->o_upstream_msgid > r->o_upstream_msgid );
+ } else {
+ return ( l->o_pin_id < r->o_pin_id ) ? -1 :
+ ( l->o_pin_id > r->o_pin_id );
+ }
+}
+
+/*
+ * Entered holding c_mutex for now.
+ */
+LloadOperation *
+operation_init( LloadConnection *c, BerElement *ber )
+{
+ LloadOperation *op;
+ ber_tag_t tag;
+ ber_len_t len;
+ int rc;
+
+ if ( !IS_ALIVE( c, c_live ) ) {
+ return NULL;
+ }
+
+ op = ch_calloc( 1, sizeof(LloadOperation) );
+ op->o_client = c;
+ op->o_client_connid = c->c_connid;
+ op->o_ber = ber;
+ op->o_start = slap_get_time();
+
+ ldap_pvt_thread_mutex_init( &op->o_link_mutex );
+
+ op->o_refcnt = 1;
+
+ tag = ber_get_int( ber, &op->o_client_msgid );
+ if ( tag != LDAP_TAG_MSGID ) {
+ goto fail;
+ }
+
+ if ( !op->o_client_msgid ) {
+ goto fail;
+ }
+
+ CONNECTION_ASSERT_LOCKED(c);
+ rc = ldap_tavl_insert( &c->c_ops, op, operation_client_cmp, ldap_avl_dup_error );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_PACKETS, "operation_init: "
+ "several operations with same msgid=%d in-flight "
+ "from client connid=%lu\n",
+ op->o_client_msgid, op->o_client_connid );
+ goto fail;
+ }
+
+ tag = op->o_tag = ber_skip_element( ber, &op->o_request );
+ switch ( tag ) {
+ case LBER_ERROR:
+ rc = -1;
+ break;
+ }
+ if ( rc ) {
+ ldap_tavl_delete( &c->c_ops, op, operation_client_cmp );
+ goto fail;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LDAP_TAG_CONTROLS ) {
+ ber_skip_element( ber, &op->o_ctrls );
+ }
+
+ switch ( op->o_tag ) {
+ case LDAP_REQ_BIND:
+ lload_stats.counters[LLOAD_STATS_OPS_BIND].lc_ops_received++;
+ break;
+ default:
+ lload_stats.counters[LLOAD_STATS_OPS_OTHER].lc_ops_received++;
+ break;
+ }
+
+ Debug( LDAP_DEBUG_STATS, "operation_init: "
+ "received a new operation, %s with msgid=%d for client "
+ "connid=%lu\n",
+ lload_msgtype2str( op->o_tag ), op->o_client_msgid,
+ op->o_client_connid );
+
+ c->c_n_ops_executing++;
+ return op;
+
+fail:
+ ch_free( op );
+ return NULL;
+}
+
+void
+operation_destroy( LloadOperation *op )
+{
+ Debug( LDAP_DEBUG_TRACE, "operation_destroy: "
+ "op=%p destroyed operation from client connid=%lu, "
+ "client msgid=%d\n",
+ op, op->o_client_connid, op->o_client_msgid );
+
+ assert( op->o_refcnt == 0 );
+ assert( op->o_client == NULL );
+ assert( op->o_upstream == NULL );
+
+ ber_free( op->o_ber, 1 );
+ ldap_pvt_thread_mutex_destroy( &op->o_link_mutex );
+ ch_free( op );
+}
+
+int
+operation_unlink( LloadOperation *op )
+{
+ LloadConnection *client, *upstream;
+ uintptr_t prev_refcnt;
+ int result = 0;
+
+ if ( !( prev_refcnt = try_release_ref(
+ &op->o_refcnt, op, (dispose_cb *)operation_destroy ) ) ) {
+ return result;
+ }
+
+ assert( prev_refcnt == 1 );
+
+ Debug( LDAP_DEBUG_TRACE, "operation_unlink: "
+ "unlinking operation between client connid=%lu and upstream "
+ "connid=%lu "
+ "client msgid=%d\n",
+ op->o_client_connid, op->o_upstream_connid, op->o_client_msgid );
+
+ checked_lock( &op->o_link_mutex );
+ client = op->o_client;
+ upstream = op->o_upstream;
+
+ op->o_client = NULL;
+ op->o_upstream = NULL;
+ checked_unlock( &op->o_link_mutex );
+
+ assert( client || upstream );
+
+ if ( client ) {
+ result |= operation_unlink_client( op, client );
+ operation_update_global_rejected( op );
+ }
+
+ if ( upstream ) {
+ result |= operation_unlink_upstream( op, upstream );
+ }
+
+ return result;
+}
+
+int
+operation_unlink_client( LloadOperation *op, LloadConnection *client )
+{
+ LloadOperation *removed;
+ int result = 0;
+
+ Debug( LDAP_DEBUG_TRACE, "operation_unlink_client: "
+ "unlinking operation op=%p msgid=%d client connid=%lu\n",
+ op, op->o_client_msgid, op->o_client_connid );
+
+ CONNECTION_LOCK(client);
+ if ( (removed = ldap_tavl_delete(
+ &client->c_ops, op, operation_client_cmp )) ) {
+ result = LLOAD_OP_DETACHING_CLIENT;
+
+ assert( op == removed );
+ client->c_n_ops_executing--;
+
+ if ( op->o_tag == LDAP_REQ_BIND &&
+ client->c_state == LLOAD_C_BINDING ) {
+ client->c_state = LLOAD_C_READY;
+ if ( !BER_BVISNULL( &client->c_auth ) ) {
+ ber_memfree( client->c_auth.bv_val );
+ BER_BVZERO( &client->c_auth );
+ }
+ if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
+ ber_memfree( client->c_sasl_bind_mech.bv_val );
+ BER_BVZERO( &client->c_sasl_bind_mech );
+ }
+ if ( op->o_pin_id ) {
+ client->c_pin_id = 0;
+ }
+ }
+ }
+ if ( client->c_state == LLOAD_C_CLOSING && !client->c_ops ) {
+ CONNECTION_DESTROY(client);
+ } else {
+ CONNECTION_UNLOCK(client);
+ }
+
+ return result;
+}
+
+int
+operation_unlink_upstream( LloadOperation *op, LloadConnection *upstream )
+{
+ LloadOperation *removed;
+ LloadBackend *b = NULL;
+ int result = 0;
+
+ Debug( LDAP_DEBUG_TRACE, "operation_unlink_upstream: "
+ "unlinking operation op=%p msgid=%d upstream connid=%lu\n",
+ op, op->o_upstream_msgid, op->o_upstream_connid );
+
+ CONNECTION_LOCK(upstream);
+ if ( (removed = ldap_tavl_delete(
+ &upstream->c_ops, op, operation_upstream_cmp )) ) {
+ result |= LLOAD_OP_DETACHING_UPSTREAM;
+
+ assert( op == removed );
+ upstream->c_n_ops_executing--;
+
+ if ( upstream->c_state == LLOAD_C_BINDING ) {
+ assert( op->o_tag == LDAP_REQ_BIND && upstream->c_ops == NULL );
+ upstream->c_state = LLOAD_C_READY;
+ if ( !BER_BVISNULL( &upstream->c_sasl_bind_mech ) ) {
+ ber_memfree( upstream->c_sasl_bind_mech.bv_val );
+ BER_BVZERO( &upstream->c_sasl_bind_mech );
+ }
+ }
+ operation_update_conn_counters( op, upstream );
+ b = upstream->c_backend;
+ }
+ if ( upstream->c_state == LLOAD_C_CLOSING && !upstream->c_ops ) {
+ CONNECTION_DESTROY(upstream);
+ } else {
+ CONNECTION_UNLOCK(upstream);
+ }
+
+ if ( b ) {
+ checked_lock( &b->b_mutex );
+ b->b_n_ops_executing--;
+ operation_update_backend_counters( op, b );
+ checked_unlock( &b->b_mutex );
+ }
+
+ return result;
+}
+
+int
+operation_send_abandon( LloadOperation *op, LloadConnection *upstream )
+{
+ BerElement *ber;
+ int rc = -1;
+
+ if ( !IS_ALIVE( upstream, c_live ) ) {
+ return rc;
+ }
+
+ checked_lock( &upstream->c_io_mutex );
+ ber = upstream->c_pendingber;
+ if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "operation_send_abandon: "
+ "ber_alloc failed\n" );
+ goto done;
+ }
+ upstream->c_pendingber = ber;
+
+ Debug( LDAP_DEBUG_TRACE, "operation_send_abandon: "
+ "abandoning %s msgid=%d on connid=%lu\n",
+ lload_msgtype2str( op->o_tag ), op->o_upstream_msgid,
+ op->o_upstream_connid );
+
+ if ( op->o_tag == LDAP_REQ_BIND ) {
+ rc = ber_printf( ber, "t{tit{ist{s}}}", LDAP_TAG_MESSAGE,
+ LDAP_TAG_MSGID, upstream->c_next_msgid++,
+ LDAP_REQ_BIND, LDAP_VERSION3, "", LDAP_AUTH_SASL, "" );
+ } else {
+ rc = ber_printf( ber, "t{titi}", LDAP_TAG_MESSAGE,
+ LDAP_TAG_MSGID, upstream->c_next_msgid++,
+ LDAP_REQ_ABANDON, op->o_upstream_msgid );
+ }
+
+ if ( rc < 0 ) {
+ ber_free( ber, 1 );
+ upstream->c_pendingber = NULL;
+ goto done;
+ }
+ rc = LDAP_SUCCESS;
+
+done:
+ checked_unlock( &upstream->c_io_mutex );
+ return rc;
+}
+
+/*
+ * Will remove the operation from its upstream and if it was still there,
+ * sends an abandon request.
+ *
+ * Being called from client_reset or request_abandon, the following hold:
+ * - noone else is processing the read part of the client connection (no new
+ * operations come in there - relevant for the c_state checks)
+ * - op->o_client_refcnt > op->o_client_live (and it follows that op->o_client != NULL)
+ */
+void
+operation_abandon( LloadOperation *op )
+{
+ LloadConnection *c;
+
+ checked_lock( &op->o_link_mutex );
+ c = op->o_upstream;
+ checked_unlock( &op->o_link_mutex );
+ if ( !c || !IS_ALIVE( c, c_live ) ) {
+ goto done;
+ }
+
+ /* for now consider all abandoned operations completed,
+ * perhaps add a separate counter later */
+ op->o_res = LLOAD_OP_COMPLETED;
+ if ( !operation_unlink_upstream( op, c ) ) {
+ /* The operation has already been abandoned or finished */
+ Debug( LDAP_DEBUG_TRACE, "operation_abandon: "
+ "%s from connid=%lu msgid=%d not present in connid=%lu any "
+ "more\n",
+ lload_msgtype2str( op->o_tag ), op->o_client_connid,
+ op->o_client_msgid, op->o_upstream_connid );
+ goto done;
+ }
+
+ if ( operation_send_abandon( op, c ) == LDAP_SUCCESS ) {
+ connection_write_cb( -1, 0, c );
+ }
+
+done:
+ operation_unlink( op );
+}
+
+void
+operation_send_reject(
+ LloadOperation *op,
+ int result,
+ const char *msg,
+ int send_anyway )
+{
+ LloadConnection *c;
+ BerElement *ber;
+ int found;
+
+ Debug( LDAP_DEBUG_TRACE, "operation_send_reject: "
+ "rejecting %s from client connid=%lu with message: \"%s\"\n",
+ lload_msgtype2str( op->o_tag ), op->o_client_connid, msg );
+
+ checked_lock( &op->o_link_mutex );
+ c = op->o_client;
+ checked_unlock( &op->o_link_mutex );
+ if ( !c || !IS_ALIVE( c, c_live ) ) {
+ Debug( LDAP_DEBUG_TRACE, "operation_send_reject: "
+ "not sending msgid=%d, client connid=%lu is dead\n",
+ op->o_client_msgid, op->o_client_connid );
+
+ goto done;
+ }
+
+ found = operation_unlink_client( op, c );
+ if ( !found && !send_anyway ) {
+ Debug( LDAP_DEBUG_TRACE, "operation_send_reject: "
+ "msgid=%d not scheduled for client connid=%lu anymore, "
+ "not sending\n",
+ op->o_client_msgid, c->c_connid );
+ goto done;
+ }
+
+ if ( op->o_client_msgid == 0 ) {
+ assert( op->o_saved_msgid == 0 && op->o_pin_id );
+ Debug( LDAP_DEBUG_TRACE, "operation_send_reject: "
+ "operation pin=%lu is just a pin, not sending\n",
+ op->o_pin_id );
+ goto done;
+ }
+
+ checked_lock( &c->c_io_mutex );
+ ber = c->c_pendingber;
+ if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
+ checked_unlock( &c->c_io_mutex );
+ Debug( LDAP_DEBUG_ANY, "operation_send_reject: "
+ "ber_alloc failed, closing connid=%lu\n",
+ c->c_connid );
+ CONNECTION_LOCK_DESTROY(c);
+ goto done;
+ }
+ c->c_pendingber = ber;
+
+ ber_printf( ber, "t{tit{ess}}", LDAP_TAG_MESSAGE,
+ LDAP_TAG_MSGID, op->o_client_msgid,
+ slap_req2res( op->o_tag ), result, "", msg );
+
+ checked_unlock( &c->c_io_mutex );
+
+ connection_write_cb( -1, 0, c );
+
+done:
+ operation_unlink( op );
+}
+
+/*
+ * Upstream is shutting down, signal the client if necessary, but we have to
+ * call operation_destroy_from_upstream ourselves to detach upstream from the
+ * op.
+ *
+ * Only called from upstream_destroy.
+ */
+void
+operation_lost_upstream( LloadOperation *op )
+{
+ operation_send_reject( op, LDAP_OTHER,
+ "connection to the remote server has been severed", 0 );
+}
+
+int
+connection_timeout( LloadConnection *upstream, void *arg )
+{
+ LloadOperation *op;
+ TAvlnode *ops = NULL, *node, *next;
+ LloadBackend *b = upstream->c_backend;
+ time_t threshold = *(time_t *)arg;
+ int rc, nops = 0;
+
+ CONNECTION_LOCK(upstream);
+ for ( node = ldap_tavl_end( upstream->c_ops, TAVL_DIR_LEFT ); node &&
+ ((LloadOperation *)node->avl_data)->o_start <
+ threshold; /* shortcut */
+ node = next ) {
+ LloadOperation *found_op;
+
+ next = ldap_tavl_next( node, TAVL_DIR_RIGHT );
+ op = node->avl_data;
+
+ /* Have we received another response since? */
+ if ( op->o_last_response && op->o_last_response >= threshold ) {
+ continue;
+ }
+
+ op->o_res = LLOAD_OP_FAILED;
+ found_op = ldap_tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
+ assert( op == found_op );
+
+ if ( upstream->c_state == LLOAD_C_BINDING ) {
+ assert( op->o_tag == LDAP_REQ_BIND && upstream->c_ops == NULL );
+ upstream->c_state = LLOAD_C_READY;
+ if ( !BER_BVISNULL( &upstream->c_sasl_bind_mech ) ) {
+ ber_memfree( upstream->c_sasl_bind_mech.bv_val );
+ BER_BVZERO( &upstream->c_sasl_bind_mech );
+ }
+ }
+
+ rc = ldap_tavl_insert( &ops, op, operation_upstream_cmp, ldap_avl_dup_error );
+ assert( rc == LDAP_SUCCESS );
+
+ Debug( LDAP_DEBUG_STATS2, "connection_timeout: "
+ "timing out %s from connid=%lu msgid=%d sent to connid=%lu as "
+ "msgid=%d\n",
+ lload_msgtype2str( op->o_tag ), op->o_client_connid,
+ op->o_client_msgid, op->o_upstream_connid,
+ op->o_upstream_msgid );
+ nops++;
+ }
+
+ if ( nops == 0 ) {
+ CONNECTION_UNLOCK(upstream);
+ return LDAP_SUCCESS;
+ }
+ upstream->c_n_ops_executing -= nops;
+ upstream->c_counters.lc_ops_failed += nops;
+ Debug( LDAP_DEBUG_STATS, "connection_timeout: "
+ "timing out %d operations for connid=%lu\n",
+ nops, upstream->c_connid );
+ CONNECTION_UNLOCK(upstream);
+
+ checked_lock( &b->b_mutex );
+ b->b_n_ops_executing -= nops;
+ checked_unlock( &b->b_mutex );
+
+ for ( node = ldap_tavl_end( ops, TAVL_DIR_LEFT ); node;
+ node = ldap_tavl_next( node, TAVL_DIR_RIGHT ) ) {
+ op = node->avl_data;
+
+ operation_send_reject( op,
+ op->o_tag == LDAP_REQ_SEARCH ? LDAP_TIMELIMIT_EXCEEDED :
+ LDAP_ADMINLIMIT_EXCEEDED,
+ "upstream did not respond in time", 0 );
+
+ if ( upstream->c_type != LLOAD_C_BIND && rc == LDAP_SUCCESS ) {
+ rc = operation_send_abandon( op, upstream );
+ }
+ operation_unlink( op );
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ connection_write_cb( -1, 0, upstream );
+ }
+
+ CONNECTION_LOCK(upstream);
+ /* ITS#9799: If a Bind timed out, connection is in an unknown state */
+ if ( upstream->c_type == LLOAD_C_BIND || rc != LDAP_SUCCESS ||
+ ( upstream->c_state == LLOAD_C_CLOSING && !upstream->c_ops ) ) {
+ CONNECTION_DESTROY(upstream);
+ } else {
+ CONNECTION_UNLOCK(upstream);
+ }
+
+ /* just dispose of the AVL, most operations should already be gone */
+ ldap_tavl_free( ops, NULL );
+ return LDAP_SUCCESS;
+}
+
+void
+operations_timeout( evutil_socket_t s, short what, void *arg )
+{
+ struct event *self = arg;
+ LloadBackend *b;
+ time_t threshold;
+
+ Debug( LDAP_DEBUG_TRACE, "operations_timeout: "
+ "running timeout task\n" );
+ if ( !lload_timeout_api ) goto done;
+
+ threshold = slap_get_time() - lload_timeout_api->tv_sec;
+
+ LDAP_CIRCLEQ_FOREACH ( b, &backend, b_next ) {
+ epoch_t epoch;
+
+ checked_lock( &b->b_mutex );
+ if ( b->b_n_ops_executing == 0 ) {
+ checked_unlock( &b->b_mutex );
+ continue;
+ }
+
+ epoch = epoch_join();
+
+ Debug( LDAP_DEBUG_TRACE, "operations_timeout: "
+ "timing out binds for backend uri=%s\n",
+ b->b_uri.bv_val );
+ connections_walk_last( &b->b_mutex, &b->b_bindconns, b->b_last_bindconn,
+ connection_timeout, &threshold );
+
+ Debug( LDAP_DEBUG_TRACE, "operations_timeout: "
+ "timing out other operations for backend uri=%s\n",
+ b->b_uri.bv_val );
+ connections_walk_last( &b->b_mutex, &b->b_conns, b->b_last_conn,
+ connection_timeout, &threshold );
+
+ epoch_leave( epoch );
+ checked_unlock( &b->b_mutex );
+ }
+done:
+ Debug( LDAP_DEBUG_TRACE, "operations_timeout: "
+ "timeout task finished\n" );
+ evtimer_add( self, lload_timeout_api );
+}
+
+void
+operation_update_global_rejected( LloadOperation *op )
+{
+ if ( op->o_res == LLOAD_OP_REJECTED ) {
+ assert( op->o_upstream_connid == 0 );
+ switch ( op->o_tag ) {
+ case LDAP_REQ_BIND:
+ lload_stats.counters[LLOAD_STATS_OPS_BIND].lc_ops_rejected++;
+ break;
+ default:
+ lload_stats.counters[LLOAD_STATS_OPS_OTHER].lc_ops_rejected++;
+ break;
+ }
+ }
+}
+
+void
+operation_update_conn_counters( LloadOperation *op, LloadConnection *upstream )
+{
+ if ( op->o_res == LLOAD_OP_COMPLETED ) {
+ upstream->c_counters.lc_ops_completed++;
+ } else {
+ upstream->c_counters.lc_ops_failed++;
+ }
+}
+
+void
+operation_update_backend_counters( LloadOperation *op, LloadBackend *b )
+{
+ int stat_type = op->o_tag == LDAP_REQ_BIND ? LLOAD_STATS_OPS_BIND :
+ LLOAD_STATS_OPS_OTHER;
+
+ assert( b != NULL );
+ if ( op->o_res == LLOAD_OP_COMPLETED ) {
+ b->b_counters[stat_type].lc_ops_completed++;
+ } else {
+ b->b_counters[stat_type].lc_ops_failed++;
+ }
+}
diff --git a/servers/lloadd/proto-lload.h b/servers/lloadd/proto-lload.h
new file mode 100644
index 0000000..253d272
--- /dev/null
+++ b/servers/lloadd/proto-lload.h
@@ -0,0 +1,225 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#ifndef PROTO_LLOAD_H
+#define PROTO_LLOAD_H
+
+#include <ldap_cdefs.h>
+#include "ldap_pvt.h"
+
+#include <event2/event.h>
+
+LDAP_BEGIN_DECL
+
+/*
+ * backend.c
+ */
+
+LDAP_SLAPD_F (void) backend_connect( evutil_socket_t s, short what, void *arg );
+LDAP_SLAPD_F (void *) backend_connect_task( void *ctx, void *arg );
+LDAP_SLAPD_F (void) backend_retry( LloadBackend *b );
+LDAP_SLAPD_F (LloadConnection *) backend_select( LloadOperation *op, int *res );
+LDAP_SLAPD_F (void) backend_reset( LloadBackend *b, int gentle );
+LDAP_SLAPD_F (void) lload_backend_destroy( LloadBackend *b );
+LDAP_SLAPD_F (void) lload_backends_destroy( void );
+
+/*
+ * bind.c
+ */
+LDAP_SLAPD_F (int) request_bind( LloadConnection *c, LloadOperation *op );
+LDAP_SLAPD_F (int) handle_bind_response( LloadConnection *client, LloadOperation *op, BerElement *ber );
+LDAP_SLAPD_F (int) handle_whoami_response( LloadConnection *client, LloadOperation *op, BerElement *ber );
+LDAP_SLAPD_F (int) handle_vc_bind_response( LloadConnection *client, LloadOperation *op, BerElement *ber );
+
+/*
+ * client.c
+ */
+LDAP_SLAPD_F (int) request_abandon( LloadConnection *c, LloadOperation *op );
+LDAP_SLAPD_F (int) request_process( LloadConnection *c, LloadOperation *op );
+LDAP_SLAPD_F (int) handle_one_request( LloadConnection *c );
+LDAP_SLAPD_F (void) client_tls_handshake_cb( evutil_socket_t s, short what, void *arg );
+LDAP_SLAPD_F (LloadConnection *) client_init( ber_socket_t s, const char *peername, struct event_base *base, int use_tls );
+LDAP_SLAPD_F (void) client_reset( LloadConnection *c );
+LDAP_SLAPD_F (void) client_destroy( LloadConnection *c );
+LDAP_SLAPD_F (void) clients_destroy( int gentle );
+LDAP_SLAPD_V (long) lload_client_max_pending;
+
+/*
+ * config.c
+ */
+LDAP_SLAPD_F (int) lload_read_config( const char *fname, const char *dir );
+LDAP_SLAPD_F (void) lload_config_destroy( void );
+LDAP_SLAPD_F (int) verb_to_mask( const char *word, slap_verbmasks *v );
+LDAP_SLAPD_F (int) lload_tls_get_config( LDAP *ld, int opt, char **val );
+LDAP_SLAPD_F (void) lload_bindconf_tls_defaults( slap_bindconf *bc );
+LDAP_SLAPD_F (int) lload_backend_parse( const char *word, LloadBackend *b );
+LDAP_SLAPD_F (int) lload_bindconf_parse( const char *word, slap_bindconf *bc );
+LDAP_SLAPD_F (int) lload_bindconf_unparse( slap_bindconf *bc, struct berval *bv );
+LDAP_SLAPD_F (int) lload_bindconf_tls_set( slap_bindconf *bc, LDAP *ld );
+LDAP_SLAPD_F (void) lload_bindconf_free( slap_bindconf *bc );
+#ifdef BALANCER_MODULE
+LDAP_SLAPD_F (int) lload_back_init_cf( BackendInfo *bi );
+#endif
+
+/*
+ * connection.c
+ */
+LDAP_SLAPD_V (ldap_pvt_thread_mutex_t) clients_mutex;
+LDAP_SLAPD_F (void *) handle_pdus( void *ctx, void *arg );
+LDAP_SLAPD_F (void) connection_write_cb( evutil_socket_t s, short what, void *arg );
+LDAP_SLAPD_F (void) connection_read_cb( evutil_socket_t s, short what, void *arg );
+LDAP_SLAPD_F (int) lload_connection_close( LloadConnection *c, void *arg );
+LDAP_SLAPD_F (LloadConnection *) lload_connection_init( ber_socket_t s, const char *peername, int use_tls );
+LDAP_SLAPD_F (void) connection_destroy( LloadConnection *c );
+LDAP_SLAPD_F (void) connections_walk_last( ldap_pvt_thread_mutex_t *cq_mutex,
+ lload_c_head *cq,
+ LloadConnection *cq_last,
+ CONNCB cb,
+ void *arg );
+LDAP_SLAPD_F (void) connections_walk( ldap_pvt_thread_mutex_t *cq_mutex, lload_c_head *cq, CONNCB cb, void *arg );
+
+/*
+ * daemon.c
+ */
+LDAP_SLAPD_F (int) lload_open_new_listener( const char *urls, LDAPURLDesc *lud );
+LDAP_SLAPD_F (int) lloadd_listeners_init( const char *urls );
+LDAP_SLAPD_F (int) lloadd_daemon_destroy( void );
+LDAP_SLAPD_F (int) lloadd_daemon( struct event_base *daemon_base );
+LDAP_SLAPD_F (LloadListener **) lloadd_get_listeners( void );
+LDAP_SLAPD_F (void) listeners_reactivate( void );
+LDAP_SLAPD_F (struct event_base *) lload_get_base( ber_socket_t s );
+LDAP_SLAPD_V (int) lload_daemon_threads;
+LDAP_SLAPD_V (int) lload_daemon_mask;
+
+LDAP_SLAPD_F (void) lload_sig_shutdown( evutil_socket_t sig, short what, void *arg );
+
+LDAP_SLAPD_F (void) lload_pause_server( void );
+LDAP_SLAPD_F (void) lload_unpause_server( void );
+
+LDAP_SLAPD_V (struct event_base *) daemon_base;
+LDAP_SLAPD_V (struct evdns_base *) dnsbase;
+LDAP_SLAPD_V (volatile sig_atomic_t) slapd_shutdown;
+LDAP_SLAPD_V (volatile sig_atomic_t) slapd_gentle_shutdown;
+LDAP_SLAPD_V (int) lloadd_inited;
+LDAP_SLAPD_V (struct LloadChange) lload_change;
+
+LDAP_SLAPD_V (struct event *) lload_timeout_event;
+
+LDAP_SLAPD_V (LDAP *) lload_tls_backend_ld;
+LDAP_SLAPD_V (LDAP *) lload_tls_ld;
+LDAP_SLAPD_V (void *) lload_tls_ctx;
+#ifdef BALANCER_MODULE
+LDAP_SLAPD_V (int) lload_use_slap_tls_ctx;
+#endif /* BALANCER_MODULE */
+
+/*
+ * extended.c
+ */
+LDAP_SLAPD_V (Avlnode *) lload_exop_handlers;
+LDAP_SLAPD_F (int) exop_handler_cmp( const void *l, const void *r );
+LDAP_SLAPD_F (int) request_extended( LloadConnection *c, LloadOperation *op );
+LDAP_SLAPD_F (int) lload_exop_init( void );
+
+/*
+ * init.c
+ */
+LDAP_SLAPD_F (int) lload_global_init( void );
+LDAP_SLAPD_F (int) lload_tls_init( void );
+LDAP_SLAPD_F (int) lload_init( int mode, const char *name );
+LDAP_SLAPD_F (int) lload_destroy( void );
+LDAP_SLAPD_F (void) lload_counters_init( void );
+
+/*
+ * libevent_support.c
+ */
+LDAP_SLAPD_F (int) lload_libevent_init( void );
+LDAP_SLAPD_F (void) lload_libevent_destroy( void );
+
+#ifdef BALANCER_MODULE
+/*
+ * monitor.c
+ */
+LDAP_SLAPD_F (int) lload_monitor_open( void );
+LDAP_SLAPD_F (int) lload_monitor_backend_init( BackendInfo *bi, LloadBackend *b );
+#endif /* BALANCER_MODULE */
+
+/*
+ * operation.c
+ */
+LDAP_SLAPD_V (ldap_pvt_thread_mutex_t) lload_pin_mutex;
+LDAP_SLAPD_V (unsigned long) lload_next_pin;
+LDAP_SLAPD_F (const char *) lload_msgtype2str( ber_tag_t tag );
+LDAP_SLAPD_F (int) operation_upstream_cmp( const void *l, const void *r );
+LDAP_SLAPD_F (int) operation_client_cmp( const void *l, const void *r );
+LDAP_SLAPD_F (LloadOperation *) operation_init( LloadConnection *c, BerElement *ber );
+LDAP_SLAPD_F (int) operation_send_abandon( LloadOperation *op, LloadConnection *c );
+LDAP_SLAPD_F (void) operation_abandon( LloadOperation *op );
+LDAP_SLAPD_F (void) operation_send_reject( LloadOperation *op, int result, const char *msg, int send_anyway );
+LDAP_SLAPD_F (int) operation_send_reject_locked( LloadOperation *op, int result, const char *msg, int send_anyway );
+LDAP_SLAPD_F (void) operation_lost_upstream( LloadOperation *op );
+LDAP_SLAPD_F (void) operation_destroy( LloadOperation *op );
+LDAP_SLAPD_F (int) operation_unlink( LloadOperation *op );
+LDAP_SLAPD_F (int) operation_unlink_client( LloadOperation *op, LloadConnection *client );
+LDAP_SLAPD_F (int) operation_unlink_upstream( LloadOperation *op, LloadConnection *upstream );
+LDAP_SLAPD_F (void) operations_timeout( evutil_socket_t s, short what, void *arg );
+LDAP_SLAPD_F (void) operation_update_conn_counters( LloadOperation *op, LloadConnection *upstream );
+LDAP_SLAPD_F (void) operation_update_backend_counters( LloadOperation *op, LloadBackend *b );
+LDAP_SLAPD_F (void) operation_update_global_rejected( LloadOperation *op );
+/*
+ * upstream.c
+ */
+LDAP_SLAPD_F (int) forward_final_response( LloadConnection *client, LloadOperation *op, BerElement *ber );
+LDAP_SLAPD_F (int) forward_response( LloadConnection *client, LloadOperation *op, BerElement *ber );
+LDAP_SLAPD_F (void *) upstream_bind( void *ctx, void *arg );
+LDAP_SLAPD_F (LloadConnection *) upstream_init( ber_socket_t s, LloadBackend *b );
+LDAP_SLAPD_F (void) upstream_destroy( LloadConnection *c );
+
+LDAP_SLAPD_V (ber_len_t) sockbuf_max_incoming_client;
+LDAP_SLAPD_V (ber_len_t) sockbuf_max_incoming_upstream;
+LDAP_SLAPD_V (int) lload_conn_max_pdus_per_cycle;
+
+LDAP_SLAPD_V (lload_features_t) lload_features;
+
+LDAP_SLAPD_V (slap_mask_t) global_allows;
+LDAP_SLAPD_V (slap_mask_t) global_disallows;
+
+LDAP_SLAPD_V (const char) Versionstr[];
+
+LDAP_SLAPD_V (int) global_gentlehup;
+LDAP_SLAPD_V (int) global_idletimeout;
+
+LDAP_SLAPD_V (struct timeval *) lload_timeout_api;
+LDAP_SLAPD_V (struct timeval *) lload_timeout_net;
+LDAP_SLAPD_V (struct timeval *) lload_write_timeout;
+
+LDAP_SLAPD_V (char *) global_host;
+LDAP_SLAPD_V (int) lber_debug;
+LDAP_SLAPD_V (int) ldap_syslog;
+
+LDAP_SLAPD_V (lload_global_stats_t) lload_stats;
+LDAP_SLAPD_V (char *) listeners_list;
+LDAP_END_DECL
+
+#endif /* PROTO_LLOAD_H */
diff --git a/servers/lloadd/upstream.c b/servers/lloadd/upstream.c
new file mode 100644
index 0000000..e85d6da
--- /dev/null
+++ b/servers/lloadd/upstream.c
@@ -0,0 +1,1103 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <ac/socket.h>
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include "lload.h"
+
+#include "lutil.h"
+#include "lutil_ldap.h"
+
+#ifdef HAVE_CYRUS_SASL
+static const sasl_callback_t client_callbacks[] = {
+#ifdef SASL_CB_GETREALM
+ { SASL_CB_GETREALM, NULL, NULL },
+#endif
+ { SASL_CB_USER, NULL, NULL },
+ { SASL_CB_AUTHNAME, NULL, NULL },
+ { SASL_CB_PASS, NULL, NULL },
+ { SASL_CB_LIST_END, NULL, NULL }
+};
+#endif /* HAVE_CYRUS_SASL */
+
+static void upstream_unlink( LloadConnection *upstream );
+
+int
+forward_response( LloadConnection *client, LloadOperation *op, BerElement *ber )
+{
+ BerElement *output;
+ BerValue response, controls = BER_BVNULL;
+ ber_int_t msgid;
+ ber_tag_t tag, response_tag;
+ ber_len_t len;
+
+ CONNECTION_LOCK(client);
+ if ( op->o_client_msgid ) {
+ msgid = op->o_client_msgid;
+ } else {
+ assert( op->o_pin_id );
+ msgid = op->o_saved_msgid;
+ op->o_saved_msgid = 0;
+ }
+ CONNECTION_UNLOCK(client);
+
+ response_tag = ber_skip_element( ber, &response );
+
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LDAP_TAG_CONTROLS ) {
+ ber_skip_element( ber, &controls );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "forward_response: "
+ "%s to client connid=%lu request msgid=%d\n",
+ lload_msgtype2str( response_tag ), op->o_client_connid, msgid );
+
+ checked_lock( &client->c_io_mutex );
+ output = client->c_pendingber;
+ if ( output == NULL && (output = ber_alloc()) == NULL ) {
+ ber_free( ber, 1 );
+ checked_unlock( &client->c_io_mutex );
+ return -1;
+ }
+ client->c_pendingber = output;
+
+ ber_printf( output, "t{titOtO}", LDAP_TAG_MESSAGE,
+ LDAP_TAG_MSGID, msgid,
+ response_tag, &response,
+ LDAP_TAG_CONTROLS, BER_BV_OPTIONAL( &controls ) );
+
+ checked_unlock( &client->c_io_mutex );
+
+ ber_free( ber, 1 );
+ connection_write_cb( -1, 0, client );
+ return 0;
+}
+
+int
+forward_final_response(
+ LloadConnection *client,
+ LloadOperation *op,
+ BerElement *ber )
+{
+ int rc;
+
+ Debug( LDAP_DEBUG_STATS, "forward_final_response: "
+ "connid=%lu msgid=%d finishing up with a request for "
+ "client connid=%lu\n",
+ op->o_upstream_connid, op->o_upstream_msgid, op->o_client_connid );
+
+ rc = forward_response( client, op, ber );
+
+ op->o_res = LLOAD_OP_COMPLETED;
+ if ( !op->o_pin_id ) {
+ operation_unlink( op );
+ }
+
+ return rc;
+}
+
+static int
+handle_unsolicited( LloadConnection *c, BerElement *ber )
+{
+ CONNECTION_ASSERT_LOCKED(c);
+ if ( c->c_state != LLOAD_C_PREPARING ) {
+ c->c_state = LLOAD_C_CLOSING;
+ }
+
+ Debug( LDAP_DEBUG_STATS, "handle_unsolicited: "
+ "teardown for upstream connection connid=%lu\n",
+ c->c_connid );
+
+ CONNECTION_DESTROY(c);
+ ber_free( ber, 1 );
+
+ return -1;
+}
+
+/*
+ * Pull c->c_currentber from the connection and try to look up the operation on
+ * the upstream.
+ *
+ * If it's a notice of disconnection, we won't find it and need to tear down
+ * the connection and tell the clients, if we can't find the operation, ignore
+ * the message (either client already disconnected/abandoned it or the upstream
+ * is pulling our leg).
+ *
+ * Some responses need special handling:
+ * - Bind response
+ * - VC response where the client requested a Bind (both need to update the
+ * client's bind status)
+ * - search entries/referrals and intermediate responses (will not trigger
+ * operation to be removed)
+ *
+ * If the worker pool is overloaded, we might be called directly from
+ * the read callback, at that point, the connection hasn't been muted.
+ *
+ * TODO: when the client already has data pending on write, we should mute the
+ * upstream.
+ * - should record the BerElement on the Op and the Op on the client
+ *
+ * The following hold on entering any of the handlers:
+ * - op->o_upstream_refcnt > 0
+ * - op->o_upstream->c_refcnt > 0
+ * - op->o_client->c_refcnt > 0
+ */
+static int
+handle_one_response( LloadConnection *c )
+{
+ BerElement *ber;
+ LloadOperation *op = NULL, needle = { .o_upstream_connid = c->c_connid };
+ LloadOperationHandler handler = NULL;
+ ber_tag_t tag;
+ ber_len_t len;
+ int rc = LDAP_SUCCESS;
+
+ ber = c->c_currentber;
+ c->c_currentber = NULL;
+
+ tag = ber_get_int( ber, &needle.o_upstream_msgid );
+ if ( tag != LDAP_TAG_MSGID ) {
+ rc = -1;
+ ber_free( ber, 1 );
+ goto fail;
+ }
+
+ CONNECTION_LOCK(c);
+ if ( needle.o_upstream_msgid == 0 ) {
+ return handle_unsolicited( c, ber );
+ } else if ( !( op = ldap_tavl_find(
+ c->c_ops, &needle, operation_upstream_cmp ) ) ) {
+ /* Already abandoned, do nothing */
+ CONNECTION_UNLOCK(c);
+ ber_free( ber, 1 );
+ return rc;
+ /*
+ } else if ( op->o_response_pending ) {
+ c->c_pendingop = op;
+ event_del( c->c_read_event );
+ */
+ } else {
+ CONNECTION_UNLOCK(c);
+ /*
+ op->o_response_pending = ber;
+ */
+
+ tag = ber_peek_tag( ber, &len );
+ switch ( tag ) {
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_REFERENCE:
+ case LDAP_RES_INTERMEDIATE:
+ handler = forward_response;
+ break;
+ case LDAP_RES_BIND:
+ handler = handle_bind_response;
+ break;
+ case LDAP_RES_EXTENDED:
+ if ( op->o_tag == LDAP_REQ_BIND ) {
+#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
+ if ( lload_features & LLOAD_FEATURE_VC ) {
+ handler = handle_vc_bind_response;
+ } else
+#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
+ {
+ handler = handle_whoami_response;
+ }
+ }
+ break;
+ }
+ if ( !handler ) {
+ handler = forward_final_response;
+ }
+ }
+ if ( op ) {
+ op->o_last_response = slap_get_time();
+ Debug( LDAP_DEBUG_STATS2, "handle_one_response: "
+ "upstream connid=%lu, processing response for "
+ "client connid=%lu, msgid=%d\n",
+ c->c_connid, op->o_client_connid, op->o_client_msgid );
+ } else {
+ tag = ber_peek_tag( ber, &len );
+ Debug( LDAP_DEBUG_STATS2, "handle_one_response: "
+ "upstream connid=%lu, %s, msgid=%d not for a pending "
+ "operation\n",
+ c->c_connid, lload_msgtype2str( tag ),
+ needle.o_upstream_msgid );
+ }
+
+ if ( handler ) {
+ LloadConnection *client;
+
+ checked_lock( &op->o_link_mutex );
+ client = op->o_client;
+ checked_unlock( &op->o_link_mutex );
+ if ( client && IS_ALIVE( client, c_live ) ) {
+ rc = handler( client, op, ber );
+ } else {
+ ber_free( ber, 1 );
+ }
+ } else {
+ assert(0);
+ ber_free( ber, 1 );
+ }
+
+fail:
+ if ( rc ) {
+ Debug( LDAP_DEBUG_STATS, "handle_one_response: "
+ "error on processing a response (%s) on upstream connection "
+ "connid=%ld, tag=%lx\n",
+ lload_msgtype2str( tag ), c->c_connid, tag );
+ CONNECTION_LOCK_DESTROY(c);
+ }
+ return rc;
+}
+
+#ifdef HAVE_CYRUS_SASL
+static int
+sasl_bind_step( LloadConnection *c, BerValue *scred, BerValue *ccred )
+{
+ LloadBackend *b = c->c_backend;
+ sasl_conn_t *ctx = c->c_sasl_authctx;
+ sasl_interact_t *prompts = NULL;
+ unsigned credlen;
+ int rc = -1;
+
+ if ( !ctx ) {
+ const char *mech = NULL;
+#ifdef HAVE_TLS
+ void *ssl;
+#endif /* HAVE_TLS */
+
+ if ( sasl_client_new( "ldap", b->b_host, NULL, NULL, client_callbacks,
+ 0, &ctx ) != SASL_OK ) {
+ goto done;
+ }
+ c->c_sasl_authctx = ctx;
+
+ assert( c->c_sasl_defaults == NULL );
+ c->c_sasl_defaults =
+ lutil_sasl_defaults( NULL, bindconf.sb_saslmech.bv_val,
+ bindconf.sb_realm.bv_val, bindconf.sb_authcId.bv_val,
+ bindconf.sb_cred.bv_val, bindconf.sb_authzId.bv_val );
+
+#ifdef HAVE_TLS
+ /* Check for TLS */
+ ssl = ldap_pvt_tls_sb_ctx( c->c_sb );
+ if ( ssl ) {
+ struct berval authid = BER_BVNULL;
+ ber_len_t ssf;
+
+ ssf = ldap_pvt_tls_get_strength( ssl );
+ (void)ldap_pvt_tls_get_my_dn( ssl, &authid, NULL, 0 );
+
+ sasl_setprop( ctx, SASL_SSF_EXTERNAL, &ssf );
+ sasl_setprop( ctx, SASL_AUTH_EXTERNAL, authid.bv_val );
+ ch_free( authid.bv_val );
+#ifdef SASL_CHANNEL_BINDING /* 2.1.25+ */
+ {
+ char cbinding[64];
+ struct berval cbv = { sizeof(cbinding), cbinding };
+ if ( ldap_pvt_tls_get_unique( ssl, &cbv, 0 ) ) {
+ sasl_channel_binding_t *cb =
+ ch_malloc( sizeof(*cb) + cbv.bv_len );
+ void *cb_data;
+ cb->name = "ldap";
+ cb->critical = 0;
+ cb->len = cbv.bv_len;
+ cb->data = cb_data = cb + 1;
+ memcpy( cb_data, cbv.bv_val, cbv.bv_len );
+ sasl_setprop( ctx, SASL_CHANNEL_BINDING, cb );
+ c->c_sasl_cbinding = cb;
+ }
+ }
+#endif
+ }
+#endif
+
+#if !defined(_WIN32)
+ /* Check for local */
+ if ( b->b_proto == LDAP_PROTO_IPC ) {
+ char authid[sizeof( "gidNumber=4294967295+uidNumber=4294967295,"
+ "cn=peercred,cn=external,cn=auth" )];
+ int ssf = LDAP_PVT_SASL_LOCAL_SSF;
+
+ sprintf( authid,
+ "gidNumber=%u+uidNumber=%u,"
+ "cn=peercred,cn=external,cn=auth",
+ getegid(), geteuid() );
+ sasl_setprop( ctx, SASL_SSF_EXTERNAL, &ssf );
+ sasl_setprop( ctx, SASL_AUTH_EXTERNAL, authid );
+ }
+#endif
+
+ do {
+ rc = sasl_client_start( ctx, bindconf.sb_saslmech.bv_val,
+ &prompts,
+ (const char **)&ccred->bv_val, &credlen,
+ &mech );
+
+ if ( rc == SASL_INTERACT ) {
+ if ( lutil_sasl_interact( NULL, LDAP_SASL_QUIET,
+ c->c_sasl_defaults, prompts ) ) {
+ break;
+ }
+ }
+ } while ( rc == SASL_INTERACT );
+
+ ber_str2bv( mech, 0, 0, &c->c_sasl_bind_mech );
+ } else {
+ assert( c->c_sasl_defaults );
+
+ do {
+ rc = sasl_client_step( ctx,
+ (scred == NULL) ? NULL : scred->bv_val,
+ (scred == NULL) ? 0 : scred->bv_len,
+ &prompts,
+ (const char **)&ccred->bv_val, &credlen);
+
+ if ( rc == SASL_INTERACT ) {
+ if ( lutil_sasl_interact( NULL, LDAP_SASL_QUIET,
+ c->c_sasl_defaults, prompts ) ) {
+ break;
+ }
+ }
+ } while ( rc == SASL_INTERACT );
+ }
+
+ if ( rc == SASL_OK ) {
+ sasl_ssf_t *ssf;
+ rc = sasl_getprop( ctx, SASL_SSF, (const void **)(char *)&ssf );
+ if ( rc == SASL_OK && ssf && *ssf ) {
+ Debug( LDAP_DEBUG_CONNS, "sasl_bind_step: "
+ "connid=%lu mech=%s setting up a new SASL security layer\n",
+ c->c_connid, c->c_sasl_bind_mech.bv_val );
+ ldap_pvt_sasl_install( c->c_sb, ctx );
+ }
+ }
+ ccred->bv_len = credlen;
+
+done:
+ Debug( LDAP_DEBUG_TRACE, "sasl_bind_step: "
+ "connid=%lu next step for SASL bind mech=%s rc=%d\n",
+ c->c_connid, c->c_sasl_bind_mech.bv_val, rc );
+ return rc;
+}
+#endif /* HAVE_CYRUS_SASL */
+
+int
+upstream_bind_cb( LloadConnection *c )
+{
+ BerElement *ber = c->c_currentber;
+ LloadBackend *b = c->c_backend;
+ BerValue matcheddn, message;
+ ber_tag_t tag;
+ ber_int_t msgid, result;
+
+ c->c_currentber = NULL;
+
+ if ( ber_scanf( ber, "it", &msgid, &tag ) == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, "upstream_bind_cb: "
+ "protocol violation from server\n" );
+ goto fail;
+ }
+
+ if ( msgid != ( c->c_next_msgid - 1 ) || tag != LDAP_RES_BIND ) {
+ Debug( LDAP_DEBUG_ANY, "upstream_bind_cb: "
+ "unexpected %s from server, msgid=%d\n",
+ lload_msgtype2str( tag ), msgid );
+ goto fail;
+ }
+
+ if ( ber_scanf( ber, "{emm" /* "}" */, &result, &matcheddn, &message ) ==
+ LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, "upstream_bind_cb: "
+ "response does not conform with a bind response\n" );
+ goto fail;
+ }
+
+ switch ( result ) {
+ case LDAP_SUCCESS:
+#ifdef HAVE_CYRUS_SASL
+ case LDAP_SASL_BIND_IN_PROGRESS:
+ if ( !BER_BVISNULL( &c->c_sasl_bind_mech ) ) {
+ BerValue scred = BER_BVNULL, ccred;
+ ber_len_t len;
+ int rc;
+
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SASL_RES_CREDS &&
+ ber_scanf( ber, "m", &scred ) == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, "upstream_bind_cb: "
+ "sasl bind response malformed\n" );
+ goto fail;
+ }
+
+ rc = sasl_bind_step( c, &scred, &ccred );
+ if ( rc != SASL_OK &&
+ ( rc != SASL_CONTINUE || result == LDAP_SUCCESS ) ) {
+ goto fail;
+ }
+
+ if ( result == LDAP_SASL_BIND_IN_PROGRESS ) {
+ BerElement *outber;
+
+ checked_lock( &c->c_io_mutex );
+ outber = c->c_pendingber;
+ if ( outber == NULL && (outber = ber_alloc()) == NULL ) {
+ checked_unlock( &c->c_io_mutex );
+ goto fail;
+ }
+ c->c_pendingber = outber;
+
+ msgid = c->c_next_msgid++;
+ ber_printf( outber, "{it{iOt{OON}N}}",
+ msgid, LDAP_REQ_BIND, LDAP_VERSION3,
+ &bindconf.sb_binddn, LDAP_AUTH_SASL,
+ &c->c_sasl_bind_mech, BER_BV_OPTIONAL( &ccred ) );
+ checked_unlock( &c->c_io_mutex );
+
+ connection_write_cb( -1, 0, c );
+
+ if ( rc == SASL_OK ) {
+ BER_BVZERO( &c->c_sasl_bind_mech );
+ }
+ break;
+ }
+ }
+ if ( result == LDAP_SASL_BIND_IN_PROGRESS ) {
+ goto fail;
+ }
+#endif /* HAVE_CYRUS_SASL */
+ CONNECTION_LOCK(c);
+ c->c_pdu_cb = handle_one_response;
+ c->c_state = LLOAD_C_READY;
+ c->c_type = LLOAD_C_OPEN;
+ c->c_read_timeout = NULL;
+ Debug( LDAP_DEBUG_CONNS, "upstream_bind_cb: "
+ "connection connid=%lu for backend server '%s' is ready "
+ "for use\n",
+ c->c_connid, b->b_name.bv_val );
+ CONNECTION_UNLOCK(c);
+ checked_lock( &b->b_mutex );
+ LDAP_CIRCLEQ_REMOVE( &b->b_preparing, c, c_next );
+ b->b_active++;
+ b->b_opening--;
+ b->b_failed = 0;
+ if ( b->b_last_conn ) {
+ LDAP_CIRCLEQ_INSERT_AFTER(
+ &b->b_conns, b->b_last_conn, c, c_next );
+ } else {
+ LDAP_CIRCLEQ_INSERT_HEAD( &b->b_conns, c, c_next );
+ }
+ b->b_last_conn = c;
+ backend_retry( b );
+ checked_unlock( &b->b_mutex );
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY, "upstream_bind_cb: "
+ "upstream bind failed, rc=%d, message='%s'\n",
+ result, message.bv_val );
+ goto fail;
+ }
+
+ checked_lock( &c->c_io_mutex );
+ c->c_io_state &= ~LLOAD_C_READ_HANDOVER;
+ checked_unlock( &c->c_io_mutex );
+ event_add( c->c_read_event, c->c_read_timeout );
+ ber_free( ber, 1 );
+ return -1;
+
+fail:
+ CONNECTION_LOCK_DESTROY(c);
+ ber_free( ber, 1 );
+ return -1;
+}
+
+void *
+upstream_bind( void *ctx, void *arg )
+{
+ LloadConnection *c = arg;
+ BerElement *ber;
+ ber_int_t msgid;
+
+ /* A reference was passed on to us */
+ assert( IS_ALIVE( c, c_refcnt ) );
+
+ if ( !IS_ALIVE( c, c_live ) ) {
+ RELEASE_REF( c, c_refcnt, c->c_destroy );
+ return NULL;
+ }
+
+ CONNECTION_LOCK(c);
+ assert( !event_pending( c->c_read_event, EV_READ, NULL ) );
+ c->c_pdu_cb = upstream_bind_cb;
+ CONNECTION_UNLOCK(c);
+
+ checked_lock( &c->c_io_mutex );
+ ber = c->c_pendingber;
+ if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
+ goto fail;
+ }
+ c->c_pendingber = ber;
+ msgid = c->c_next_msgid++;
+
+ if ( bindconf.sb_method == LDAP_AUTH_SIMPLE ) {
+ /* simple bind */
+ ber_printf( ber, "{it{iOtON}}",
+ msgid, LDAP_REQ_BIND, LDAP_VERSION3,
+ &bindconf.sb_binddn, LDAP_AUTH_SIMPLE,
+ &bindconf.sb_cred );
+
+#ifdef HAVE_CYRUS_SASL
+ } else {
+ BerValue cred;
+ int rc;
+
+ rc = sasl_bind_step( c, NULL, &cred );
+ if ( rc != SASL_OK && rc != SASL_CONTINUE ) {
+ goto fail;
+ }
+
+ ber_printf( ber, "{it{iOt{OON}N}}",
+ msgid, LDAP_REQ_BIND, LDAP_VERSION3,
+ &bindconf.sb_binddn, LDAP_AUTH_SASL,
+ &c->c_sasl_bind_mech, BER_BV_OPTIONAL( &cred ) );
+
+ if ( rc == SASL_OK ) {
+ BER_BVZERO( &c->c_sasl_bind_mech );
+ }
+#endif /* HAVE_CYRUS_SASL */
+ }
+ /* TODO: can we be paused at this point? Then we'd have to move this line
+ * after connection_write_cb */
+ c->c_io_state &= ~LLOAD_C_READ_HANDOVER;
+ checked_unlock( &c->c_io_mutex );
+
+ connection_write_cb( -1, 0, c );
+
+ CONNECTION_LOCK(c);
+ c->c_read_timeout = lload_timeout_net;
+ event_add( c->c_read_event, c->c_read_timeout );
+ CONNECTION_UNLOCK(c);
+
+ RELEASE_REF( c, c_refcnt, c->c_destroy );
+ return NULL;
+
+fail:
+ checked_unlock( &c->c_io_mutex );
+ CONNECTION_LOCK_DESTROY(c);
+ RELEASE_REF( c, c_refcnt, c->c_destroy );
+ return NULL;
+}
+
+/*
+ * The backend is already locked when entering the function.
+ */
+static int
+upstream_finish( LloadConnection *c )
+{
+ LloadBackend *b = c->c_backend;
+ int is_bindconn = 0;
+
+ assert_locked( &b->b_mutex );
+ CONNECTION_ASSERT_LOCKED(c);
+ assert( c->c_live );
+ c->c_pdu_cb = handle_one_response;
+
+ /* Unless we are configured to use the VC exop, consider allocating the
+ * connection into the bind conn pool. Start off by allocating one for
+ * general use, then one for binds, then we start filling up the general
+ * connection pool, finally the bind pool */
+ if (
+#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
+ !(lload_features & LLOAD_FEATURE_VC) &&
+#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
+ b->b_active && b->b_numbindconns ) {
+ if ( !b->b_bindavail ) {
+ is_bindconn = 1;
+ } else if ( b->b_active >= b->b_numconns &&
+ b->b_bindavail < b->b_numbindconns ) {
+ is_bindconn = 1;
+ }
+ }
+
+ if ( is_bindconn ) {
+ LDAP_CIRCLEQ_REMOVE( &b->b_preparing, c, c_next );
+ c->c_state = LLOAD_C_READY;
+ c->c_type = LLOAD_C_BIND;
+ b->b_bindavail++;
+ b->b_opening--;
+ b->b_failed = 0;
+ if ( b->b_last_bindconn ) {
+ LDAP_CIRCLEQ_INSERT_AFTER(
+ &b->b_bindconns, b->b_last_bindconn, c, c_next );
+ } else {
+ LDAP_CIRCLEQ_INSERT_HEAD( &b->b_bindconns, c, c_next );
+ }
+ b->b_last_bindconn = c;
+ } else if ( bindconf.sb_method == LDAP_AUTH_NONE ) {
+ LDAP_CIRCLEQ_REMOVE( &b->b_preparing, c, c_next );
+ c->c_state = LLOAD_C_READY;
+ c->c_type = LLOAD_C_OPEN;
+ b->b_active++;
+ b->b_opening--;
+ b->b_failed = 0;
+ if ( b->b_last_conn ) {
+ LDAP_CIRCLEQ_INSERT_AFTER( &b->b_conns, b->b_last_conn, c, c_next );
+ } else {
+ LDAP_CIRCLEQ_INSERT_HEAD( &b->b_conns, c, c_next );
+ }
+ b->b_last_conn = c;
+ } else {
+ if ( ldap_pvt_thread_pool_submit(
+ &connection_pool, upstream_bind, c ) ) {
+ Debug( LDAP_DEBUG_ANY, "upstream_finish: "
+ "failed to set up a bind callback for connid=%lu\n",
+ c->c_connid );
+ return -1;
+ }
+ /* keep a reference for upstream_bind */
+ acquire_ref( &c->c_refcnt );
+
+ Debug( LDAP_DEBUG_CONNS, "upstream_finish: "
+ "scheduled a bind callback for connid=%lu\n",
+ c->c_connid );
+ return LDAP_SUCCESS;
+ }
+ event_add( c->c_read_event, c->c_read_timeout );
+
+ Debug( LDAP_DEBUG_CONNS, "upstream_finish: "
+ "%sconnection connid=%lu for backend server '%s' is ready for "
+ "use\n",
+ is_bindconn ? "bind " : "", c->c_connid, b->b_name.bv_val );
+
+ backend_retry( b );
+ return LDAP_SUCCESS;
+}
+
+#ifdef HAVE_TLS
+static void
+upstream_tls_handshake_cb( evutil_socket_t s, short what, void *arg )
+{
+ LloadConnection *c = arg;
+ LloadBackend *b;
+ epoch_t epoch;
+ int rc = LDAP_SUCCESS;
+
+ CONNECTION_LOCK(c);
+ if ( what & EV_TIMEOUT ) {
+ Debug( LDAP_DEBUG_CONNS, "upstream_tls_handshake_cb: "
+ "connid=%lu, timeout reached, destroying\n",
+ c->c_connid );
+ goto fail;
+ }
+ b = c->c_backend;
+
+ rc = ldap_pvt_tls_connect( lload_tls_backend_ld, c->c_sb, b->b_host );
+ if ( rc < 0 ) {
+ goto fail;
+ }
+
+ if ( rc == 0 ) {
+ struct event_base *base = event_get_base( c->c_read_event );
+
+ /*
+ * We're finished, replace the callbacks
+ *
+ * This is deadlock-safe, since both share the same base - the one
+ * that's just running us.
+ */
+ event_del( c->c_read_event );
+ event_del( c->c_write_event );
+
+ c->c_read_timeout = NULL;
+ event_assign( c->c_read_event, base, c->c_fd, EV_READ|EV_PERSIST,
+ connection_read_cb, c );
+ event_assign( c->c_write_event, base, c->c_fd, EV_WRITE,
+ connection_write_cb, c );
+ Debug( LDAP_DEBUG_CONNS, "upstream_tls_handshake_cb: "
+ "connid=%lu finished\n",
+ c->c_connid );
+ c->c_is_tls = LLOAD_TLS_ESTABLISHED;
+
+ CONNECTION_UNLOCK(c);
+ checked_lock( &b->b_mutex );
+ CONNECTION_LOCK(c);
+
+ rc = upstream_finish( c );
+ checked_unlock( &b->b_mutex );
+
+ if ( rc ) {
+ goto fail;
+ }
+ } else if ( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_WRITE, NULL ) ) {
+ event_add( c->c_write_event, lload_write_timeout );
+ Debug( LDAP_DEBUG_CONNS, "upstream_tls_handshake_cb: "
+ "connid=%lu need write rc=%d\n",
+ c->c_connid, rc );
+ }
+ CONNECTION_UNLOCK(c);
+ return;
+
+fail:
+ Debug( LDAP_DEBUG_CONNS, "upstream_tls_handshake_cb: "
+ "connid=%lu failed rc=%d\n",
+ c->c_connid, rc );
+
+ assert( c->c_ops == NULL );
+ epoch = epoch_join();
+ CONNECTION_DESTROY(c);
+ epoch_leave( epoch );
+}
+
+static int
+upstream_starttls( LloadConnection *c )
+{
+ BerValue matcheddn, message, responseOid,
+ startTLSOid = BER_BVC(LDAP_EXOP_START_TLS);
+ BerElement *ber = c->c_currentber;
+ struct event_base *base;
+ ber_int_t msgid, result;
+ ber_tag_t tag;
+
+ c->c_currentber = NULL;
+ CONNECTION_LOCK(c);
+
+ if ( ber_scanf( ber, "it", &msgid, &tag ) == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, "upstream_starttls: "
+ "protocol violation from server\n" );
+ goto fail;
+ }
+
+ if ( msgid != ( c->c_next_msgid - 1 ) || tag != LDAP_RES_EXTENDED ) {
+ Debug( LDAP_DEBUG_ANY, "upstream_starttls: "
+ "unexpected %s from server, msgid=%d\n",
+ lload_msgtype2str( tag ), msgid );
+ goto fail;
+ }
+
+ if ( ber_scanf( ber, "{emm}", &result, &matcheddn, &message ) ==
+ LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, "upstream_starttls: "
+ "protocol violation on StartTLS response\n" );
+ goto fail;
+ }
+
+ if ( (tag = ber_get_tag( ber )) != LBER_DEFAULT ) {
+ if ( tag != LDAP_TAG_EXOP_RES_OID ||
+ ber_scanf( ber, "{m}", &responseOid ) == LBER_DEFAULT ) {
+ Debug( LDAP_DEBUG_ANY, "upstream_starttls: "
+ "protocol violation on StartTLS response\n" );
+ goto fail;
+ }
+
+ if ( ber_bvcmp( &responseOid, &startTLSOid ) ) {
+ Debug( LDAP_DEBUG_ANY, "upstream_starttls: "
+ "oid=%s not a StartTLS response\n",
+ responseOid.bv_val );
+ goto fail;
+ }
+ }
+
+ if ( result != LDAP_SUCCESS ) {
+ LloadBackend *b = c->c_backend;
+ int rc;
+
+ Debug( LDAP_DEBUG_STATS, "upstream_starttls: "
+ "server doesn't support StartTLS rc=%d message='%s'%s\n",
+ result, message.bv_val,
+ (c->c_is_tls == LLOAD_STARTTLS_OPTIONAL) ? ", ignored" : "" );
+ if ( c->c_is_tls != LLOAD_STARTTLS_OPTIONAL ) {
+ goto fail;
+ }
+ c->c_is_tls = LLOAD_CLEARTEXT;
+
+ CONNECTION_UNLOCK(c);
+ checked_lock( &b->b_mutex );
+ CONNECTION_LOCK(c);
+
+ rc = upstream_finish( c );
+ checked_unlock( &b->b_mutex );
+
+ if ( rc ) {
+ goto fail;
+ }
+
+ ber_free( ber, 1 );
+ CONNECTION_UNLOCK(c);
+
+ checked_lock( &c->c_io_mutex );
+ c->c_io_state &= ~LLOAD_C_READ_HANDOVER;
+ checked_unlock( &c->c_io_mutex );
+
+ /* Do not keep handle_pdus running, we have adjusted c_read_event as we
+ * need it. */
+ return -1;
+ }
+
+ base = event_get_base( c->c_read_event );
+
+ c->c_io_state &= ~LLOAD_C_READ_HANDOVER;
+ event_del( c->c_read_event );
+ event_del( c->c_write_event );
+
+ c->c_read_timeout = lload_timeout_net;
+ event_assign( c->c_read_event, base, c->c_fd, EV_READ|EV_PERSIST,
+ upstream_tls_handshake_cb, c );
+ event_assign( c->c_write_event, base, c->c_fd, EV_WRITE,
+ upstream_tls_handshake_cb, c );
+
+ event_add( c->c_read_event, c->c_read_timeout );
+ event_add( c->c_write_event, lload_write_timeout );
+
+ CONNECTION_UNLOCK(c);
+
+ ber_free( ber, 1 );
+ return -1;
+
+fail:
+ ber_free( ber, 1 );
+ CONNECTION_DESTROY(c);
+ return -1;
+}
+#endif /* HAVE_TLS */
+
+/*
+ * We must already hold b->b_mutex when called.
+ */
+LloadConnection *
+upstream_init( ber_socket_t s, LloadBackend *b )
+{
+ LloadConnection *c;
+ struct event_base *base = lload_get_base( s );
+ struct event *event;
+ int flags;
+
+ assert( b != NULL );
+
+ flags = (b->b_proto == LDAP_PROTO_IPC) ? CONN_IS_IPC : 0;
+ if ( (c = lload_connection_init( s, b->b_host, flags )) == NULL ) {
+ return NULL;
+ }
+
+ CONNECTION_LOCK(c);
+ c->c_backend = b;
+#ifdef HAVE_TLS
+ c->c_is_tls = b->b_tls;
+#endif
+ c->c_pdu_cb = handle_one_response;
+
+ LDAP_CIRCLEQ_INSERT_HEAD( &b->b_preparing, c, c_next );
+ c->c_type = LLOAD_C_PREPARING;
+
+ {
+ ber_len_t max = sockbuf_max_incoming_upstream;
+ ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_MAX_INCOMING, &max );
+ }
+
+ event = event_new( base, s, EV_READ|EV_PERSIST, connection_read_cb, c );
+ if ( !event ) {
+ Debug( LDAP_DEBUG_ANY, "upstream_init: "
+ "Read event could not be allocated\n" );
+ goto fail;
+ }
+ c->c_read_event = event;
+
+ event = event_new( base, s, EV_WRITE, connection_write_cb, c );
+ if ( !event ) {
+ Debug( LDAP_DEBUG_ANY, "upstream_init: "
+ "Write event could not be allocated\n" );
+ goto fail;
+ }
+ /* We only add the write event when we have data pending */
+ c->c_write_event = event;
+
+ c->c_destroy = upstream_destroy;
+ c->c_unlink = upstream_unlink;
+
+#ifdef HAVE_TLS
+ if ( c->c_is_tls == LLOAD_CLEARTEXT ) {
+#endif /* HAVE_TLS */
+ if ( upstream_finish( c ) ) {
+ goto fail;
+ }
+#ifdef HAVE_TLS
+ } else if ( c->c_is_tls == LLOAD_LDAPS ) {
+ event_assign( c->c_read_event, base, s, EV_READ|EV_PERSIST,
+ upstream_tls_handshake_cb, c );
+ event_add( c->c_read_event, c->c_read_timeout );
+ event_assign( c->c_write_event, base, s, EV_WRITE,
+ upstream_tls_handshake_cb, c );
+ event_add( c->c_write_event, lload_write_timeout );
+ } else if ( c->c_is_tls == LLOAD_STARTTLS ||
+ c->c_is_tls == LLOAD_STARTTLS_OPTIONAL ) {
+ BerElement *output;
+
+ checked_lock( &c->c_io_mutex );
+ if ( (output = c->c_pendingber = ber_alloc()) == NULL ) {
+ checked_unlock( &c->c_io_mutex );
+ goto fail;
+ }
+ ber_printf( output, "t{tit{ts}}", LDAP_TAG_MESSAGE,
+ LDAP_TAG_MSGID, c->c_next_msgid++,
+ LDAP_REQ_EXTENDED,
+ LDAP_TAG_EXOP_REQ_OID, LDAP_EXOP_START_TLS );
+ checked_unlock( &c->c_io_mutex );
+
+ c->c_pdu_cb = upstream_starttls;
+ CONNECTION_UNLOCK(c);
+ connection_write_cb( s, 0, c );
+ CONNECTION_LOCK(c);
+ if ( IS_ALIVE( c, c_live ) ) {
+ event_add( c->c_read_event, c->c_read_timeout );
+ }
+ }
+#endif /* HAVE_TLS */
+ CONNECTION_UNLOCK(c);
+
+ return c;
+
+fail:
+ if ( c->c_write_event ) {
+ event_del( c->c_write_event );
+ event_free( c->c_write_event );
+ }
+ if ( c->c_read_event ) {
+ event_del( c->c_read_event );
+ event_free( c->c_read_event );
+ }
+
+ c->c_state = LLOAD_C_INVALID;
+ c->c_live--;
+ c->c_refcnt--;
+ connection_destroy( c );
+
+ return NULL;
+}
+
+static void
+upstream_unlink( LloadConnection *c )
+{
+ LloadBackend *b = c->c_backend;
+ struct event *read_event, *write_event;
+ TAvlnode *root;
+ long freed, executing;
+
+ Debug( LDAP_DEBUG_CONNS, "upstream_unlink: "
+ "removing upstream connid=%lu\n",
+ c->c_connid );
+ CONNECTION_ASSERT_LOCKED(c);
+
+ assert( c->c_state != LLOAD_C_INVALID );
+ assert( c->c_state != LLOAD_C_DYING );
+
+ c->c_state = LLOAD_C_DYING;
+
+ read_event = c->c_read_event;
+ write_event = c->c_write_event;
+
+ root = c->c_ops;
+ c->c_ops = NULL;
+ executing = c->c_n_ops_executing;
+ c->c_n_ops_executing = 0;
+
+ CONNECTION_UNLOCK(c);
+
+ freed = ldap_tavl_free( root, (AVL_FREE)operation_lost_upstream );
+ assert( freed == executing );
+
+ /*
+ * Avoid a deadlock:
+ * event_del will block if the event is currently executing its callback,
+ * that callback might be waiting to lock c->c_mutex
+ */
+ if ( read_event ) {
+ event_del( read_event );
+ }
+
+ if ( write_event ) {
+ event_del( write_event );
+ }
+
+ checked_lock( &b->b_mutex );
+ if ( c->c_type == LLOAD_C_PREPARING ) {
+ LDAP_CIRCLEQ_REMOVE( &b->b_preparing, c, c_next );
+ b->b_opening--;
+ b->b_failed++;
+ } else if ( c->c_type == LLOAD_C_BIND ) {
+ if ( c == b->b_last_bindconn ) {
+ LloadConnection *prev =
+ LDAP_CIRCLEQ_LOOP_PREV( &b->b_bindconns, c, c_next );
+ if ( prev == c ) {
+ b->b_last_bindconn = NULL;
+ } else {
+ b->b_last_bindconn = prev;
+ }
+ }
+ LDAP_CIRCLEQ_REMOVE( &b->b_bindconns, c, c_next );
+ b->b_bindavail--;
+ } else {
+ if ( c == b->b_last_conn ) {
+ LloadConnection *prev =
+ LDAP_CIRCLEQ_LOOP_PREV( &b->b_conns, c, c_next );
+ if ( prev == c ) {
+ b->b_last_conn = NULL;
+ } else {
+ b->b_last_conn = prev;
+ }
+ }
+ LDAP_CIRCLEQ_REMOVE( &b->b_conns, c, c_next );
+ b->b_active--;
+ }
+ b->b_n_ops_executing -= executing;
+ backend_retry( b );
+ checked_unlock( &b->b_mutex );
+
+ CONNECTION_LOCK(c);
+ CONNECTION_ASSERT_LOCKED(c);
+}
+
+void
+upstream_destroy( LloadConnection *c )
+{
+ Debug( LDAP_DEBUG_CONNS, "upstream_destroy: "
+ "freeing connection connid=%lu\n",
+ c->c_connid );
+
+ CONNECTION_LOCK(c);
+ assert( c->c_state == LLOAD_C_DYING );
+ c->c_state = LLOAD_C_INVALID;
+
+ assert( c->c_ops == NULL );
+
+ if ( c->c_read_event ) {
+ event_free( c->c_read_event );
+ c->c_read_event = NULL;
+ }
+
+ if ( c->c_write_event ) {
+ event_free( c->c_write_event );
+ c->c_write_event = NULL;
+ }
+
+ if ( c->c_type != LLOAD_C_BIND ) {
+ BER_BVZERO( &c->c_sasl_bind_mech );
+ }
+ connection_destroy( c );
+}
diff --git a/servers/lloadd/value.c b/servers/lloadd/value.c
new file mode 100644
index 0000000..ec71444
--- /dev/null
+++ b/servers/lloadd/value.c
@@ -0,0 +1,67 @@
+/* value.c - routines for dealing with values */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/*
+ * Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include "lload.h"
+
+int
+value_add_one( BerVarray *vals, struct berval *addval )
+{
+ int n;
+ BerVarray v2;
+
+ if ( *vals == NULL ) {
+ *vals = (BerVarray)SLAP_MALLOC( 2 * sizeof(struct berval) );
+ if ( *vals == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "value_add_one: "
+ "SLAP_MALLOC failed.\n" );
+ return LBER_ERROR_MEMORY;
+ }
+ n = 0;
+
+ } else {
+ for ( n = 0; !BER_BVISNULL( &(*vals)[n] ); n++ ) {
+ ; /* Empty */
+ }
+ *vals = (BerVarray)SLAP_REALLOC(
+ (char *)*vals, ( n + 2 ) * sizeof(struct berval) );
+ if ( *vals == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "value_add_one: "
+ "SLAP_MALLOC failed.\n" );
+ return LBER_ERROR_MEMORY;
+ }
+ }
+
+ v2 = &(*vals)[n];
+ ber_dupbv( v2, addval );
+
+ v2++;
+ BER_BVZERO( v2 );
+
+ return LDAP_SUCCESS;
+}
diff --git a/servers/slapd/Makefile.in b/servers/slapd/Makefile.in
new file mode 100644
index 0000000..2fc46dd
--- /dev/null
+++ b/servers/slapd/Makefile.in
@@ -0,0 +1,467 @@
+## Makefile.in for slapd
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SLAPTOOLS=slapadd slapcat slapdn slapindex slapmodify slappasswd slaptest slapauth slapacl slapschema
+PROGRAMS=slapd $(SLAPTOOLS)
+XPROGRAMS=sslapd libbackends.a .backend liboverlays.a
+XSRCS=version.c
+
+SUBDIRS=back-* slapi overlays pwmods
+
+NT_SRCS = nt_svc.c
+NT_OBJS = nt_svc.o ../../libraries/liblutil/slapdmsg.res
+
+SRCS = main.c globals.c bconfig.c config.c daemon.c \
+ connection.c search.c filter.c add.c cr.c \
+ attr.c entry.c backend.c result.c operation.c \
+ dn.c compare.c modify.c delete.c modrdn.c ch_malloc.c \
+ value.c ava.c bind.c unbind.c abandon.c filterentry.c \
+ phonetic.c acl.c str2filter.c aclparse.c init.c user.c \
+ lock.c controls.c extended.c passwd.c proxyp.c \
+ schema.c schema_check.c schema_init.c schema_prep.c \
+ schemaparse.c ad.c at.c mr.c syntax.c oc.c saslauthz.c \
+ oidm.c starttls.c index.c sets.c referral.c root_dse.c \
+ sasl.c module.c mra.c mods.c sl_malloc.c zn_malloc.c limits.c \
+ operational.c matchedValues.c cancel.c syncrepl.c \
+ backglue.c backover.c ctxcsn.c ldapsync.c frontend.c \
+ slapadd.c slapcat.c slapcommon.c slapdn.c slapindex.c \
+ slappasswd.c slaptest.c slapauth.c slapacl.c component.c \
+ aci.c txn.c slapschema.c slapmodify.c \
+ $(@PLAT@_SRCS)
+
+OBJS = main.o globals.o bconfig.o config.o daemon.o \
+ connection.o search.o filter.o add.o cr.o \
+ attr.o entry.o backend.o backends.o result.o operation.o \
+ dn.o compare.o modify.o delete.o modrdn.o ch_malloc.o \
+ value.o ava.o bind.o unbind.o abandon.o filterentry.o \
+ phonetic.o acl.o str2filter.o aclparse.o init.o user.o \
+ lock.o controls.o extended.o passwd.o proxyp.o \
+ schema.o schema_check.o schema_init.o schema_prep.o \
+ schemaparse.o ad.o at.o mr.o syntax.o oc.o saslauthz.o \
+ oidm.o starttls.o index.o sets.o referral.o root_dse.o \
+ sasl.o module.o mra.o mods.o sl_malloc.o zn_malloc.o limits.o \
+ operational.o matchedValues.o cancel.o syncrepl.o \
+ backglue.o backover.o ctxcsn.o ldapsync.o frontend.o \
+ slapadd.o slapcat.o slapcommon.o slapdn.o slapindex.o \
+ slappasswd.o slaptest.o slapauth.o slapacl.o component.o \
+ aci.o txn.o slapschema.o slapmodify.o \
+ $(@PLAT@_OBJS)
+
+LDAP_INCDIR= ../../include -I$(srcdir) -I$(srcdir)/slapi -I.
+LDAP_LIBDIR= ../../libraries
+
+SLAP_DIR=
+SLAPD_STATIC_DEPENDS=@SLAPD_NO_STATIC@ libbackends.a liboverlays.a
+SLAPD_STATIC_BACKENDS=@SLAPD_STATIC_BACKENDS@
+SLAPD_DYNAMIC_BACKENDS=@SLAPD_DYNAMIC_BACKENDS@
+
+SLAPI_LIBS=@LIBSLAPI@ @SLAPI_LIBS@
+
+XDEFS = $(MODULES_CPPFLAGS)
+XLDFLAGS = $(MODULES_LDFLAGS)
+
+XLIBS = $(SLAPD_STATIC_DEPENDS) $(SLAPD_L) $(MODULES_LIBS)
+XXLIBS = $(SLAPD_LIBS) $(SECURITY_LIBS) $(LUTIL_LIBS)
+XXXLIBS = $(LTHREAD_LIBS) $(SLAPI_LIBS)
+
+BUILD_OPT = "--enable-slapd"
+BUILD_SRV = @BUILD_SLAPD@
+
+all-local-srv: all-cffiles
+
+NT_SLAPD_DEPENDS = slapd.exp
+NT_SLAPD_OBJECTS = slapd.exp symdummy.o $(OBJS) version.o
+
+UNIX_SLAPD_DEPENDS = $(SLAPD_STATIC_DEPENDS) version.o $(SLAPD_L)
+UNIX_SLAPD_OBJECTS = $(OBJS) version.o
+
+SLAPD_DEPENDS = $(@PLAT@_SLAPD_DEPENDS)
+SLAPD_OBJECTS = $(@PLAT@_SLAPD_OBJECTS)
+
+# Notes about slapd for Windows
+# =============================
+# slapd.exe must export all of its global symbols, just like a DLL.
+# The purpose of this is to allow dynamic modules (dynamic backends
+# or external dynamic modules) to bind with the symbols at run-time.
+#
+# Exporting symbols from an .EXE is a bit tricky and involves multiple
+# steps. First a .DEF file must be generated. The .DEF file indicates
+# the set of symbols that are to be exported. Many times, it's possible
+# to manually create this file with an editor. However, with slapd,
+# we want to export EVERY global symbol that it knows about (NOT including
+# symbols that are imported from other DLLs). The set of symbols to
+# export INCLUDES symbols from all static libraries that slapd gets
+# linked with, e.g. avl, lunicode, lutil, etc. This list
+# will also include liblber and libldap if they were built as static
+# libraries. ALSO included will be symbols from other STATIC libraries
+# outside the domain of the OpenLDAP source tree, e.g. regex, ltdl,
+# crypto, ssl, sasl, etc. (If these libraries are dynamic, we won't want
+# to include their symbols in the list). The correct set of symbols
+# CAN be determined at build time. The slapd.def target automatically
+# determines the correct set of symbols and generates the slapd.def file.
+#
+# The slapd.def file, serving multiple purposes, will:
+#
+# 1) be used to generate libslapd.a, the import library for slapd.exe.
+#
+# 2) be used to generate the symdummy.c file.
+#
+# 3) be used to help create slapd.exp, the binary-formated slapd export file.
+#
+# The import library is used by dynamic modules at link time. With this
+# library, dynamic modules indicate to the linker that it will resolve
+# these symbols from the slapd.exe binary at run-time. Of course, whenever
+# a module imports dynamic symbols, those symbols should be marked with
+# the __declspec(dllimport) directive in the header files that the dynamic
+# modules build with. In OpenLDAP, this is handled automatically in the
+# header files. (See ldap_cdefs.h for an explanation). Writers of
+# dynamic backend modules should keep in mind that slapd.exe might export
+# other global symbols that are not part of OpenLDAP (e.g. regex, ltdl,
+# crypto, ssl, sasl, etc.) When a writer actually uses (i.e. imports) these
+# symbols, he must verify that the header files from these external packages
+# include a mechanism to mark imported symbols with the __declspec(dllimport)
+# directive. Whether or not such a mechanism exists, the writer must be
+# able to include these directives appropriately when their symbols are
+# being imported from slapd.exe. The directive is not completely necessary
+# for functions, but it is required for variables.
+#
+# The symdummy.c file basically references EVERY symbol available to slapd.exe,
+# including symbols that slapd.exe never actually referenced. The file
+# is compiled and included at link time. Without this object file, slapd.exe
+# would NOT export symbols that it never referenced. The reason that these
+# symbols must still be exported is because a dynamic module may want to
+# use a symbol even if it had not been referenced by slapd.exe.
+#
+
+#
+# slapd.def REALLY depends upon all slapd objects and all static libraries
+# included in $(LIBS), including static libraries outside of OpenLDAP.
+# When slapd.def is built, the absolute paths to all static libraries
+# (both inside and outside of OpenLDAP) are generated. We don't have
+# any way to include this generated list as a dependency of slapd.def (sigh).
+# Thus, we do the best we can by depending on version.o, which depends
+# on its own very long list of dependencies.
+#
+slapd.def: libbackends.a liboverlays.a version.o
+ @for i in XX $(LDFLAGS) ; do \
+ path=`expr "$$i" : "-L\(.*\)"`; \
+ if test $$? != 0; then continue; fi; \
+ paths="$$paths $$path"; \
+ done; \
+ objs=""; \
+ for i in $(OBJS) version.o $(LIBS) ; do \
+ obj="" ; \
+ case $$i in \
+ -l*) \
+ done="" ;\
+ base=`expr "$$i" : "-l\(.*\)"`; \
+ for p in . $$paths ; do \
+ for ext in la dll dll.a a ; do \
+ path=$$p/lib$$base.$$ext; \
+ test ! -f $$path && continue; \
+ if test $$ext = la ; then \
+ for t in dlname old_library ; do \
+ line=`grep "^$$t=" $$path`; \
+ lib=`expr "$$line" : "[^']*'\(.*\)'"`; \
+ test -n "$$lib" && test -f $$p/$$lib && \
+ path=$$p/$$lib && break; \
+ done; \
+ test $$t = dlname && ext=dll; \
+ test $$t = old_library && ext=a; \
+ fi; \
+ if test $$ext = a ; then \
+ obj=$$path; \
+ fi; \
+ done=done; \
+ break; \
+ done; \
+ test -n "$$done" && break; \
+ done; \
+ test -z "$$obj" && continue; \
+ ;; \
+ *.la) \
+ if test -n "$(LTSTATIC)"; then \
+ base=`expr "$$i" : ".*/\(.*\).la"`; \
+ path=`expr "$$i" : "\(.*/\).*"`; \
+ obj=$$path.libs/$$base.a; \
+ fi; \
+ ;; \
+ *.dll.a) \
+ ;; \
+ *.o | *.a) \
+ obj=$$i; \
+ esac; \
+ objs="$$objs $$obj"; \
+ done; \
+ echo dlltool --exclude-symbols main,ServiceMain@8 --export-all-symbols \
+ --output-def $@.tmp $$objs; \
+ dlltool --exclude-symbols main,ServiceMain@8 --export-all-symbols \
+ --output-def $@.tmp $$objs;
+ echo EXPORTS > $@
+ $(SED) -e 1,2d -e 's/ @ [0-9][0-9]*//' -e '/\.refptr\./d' $@.tmp | sort >> $@
+ $(RM) $@.tmp
+
+symdummy.c: slapd.def
+ $(RM) $@
+ @echo "generating $@..."; \
+ echo "static void never_called() {" > $@.tmp; \
+ cat $< | while read line; \
+ do \
+ set dummy $$line; \
+ case $$# in \
+ 3) \
+ echo "int $$2();" >> $@; \
+ echo "$$2();" >> $@.tmp; \
+ ;; \
+ 4) \
+ echo "extern int $$2;" >> $@; \
+ echo "$$2 = 0;" >> $@.tmp; \
+ ;; \
+ esac; \
+ done; \
+ echo "" >> $@; \
+ echo "}" >> $@.tmp; \
+ cat $@.tmp >> $@; \
+ $(RM) $@.tmp
+
+libslapd.a: symdummy.o
+ dlltool --dllname slapd.exe --input-def slapd.def --output-lib $@
+
+slapd.exp: libslapd.a
+ @echo $(LTLINK) -Wl,--base-file,slapd.base -o slapd \
+ $(OBJS) symdummy.o version.o $(LIBS) $(WRAP_LIBS); \
+ $(LTLINK) -Wl,--base-file,slapd.base -o slapd \
+ $(OBJS) symdummy.o version.o $(LIBS) $(WRAP_LIBS)
+ $(RM) slapd.exe
+ @echo dlltool --dllname slapd.exe --input-def slapd.def \
+ --base-file slapd.base --output-exp $@; \
+ dlltool --dllname slapd.exe --input-def slapd.def \
+ --base-file slapd.base --output-exp $@; \
+ echo $(LTLINK) -Wl,--base-file,slapd.base -o slapd $@ \
+ $(OBJS) symdummy.o version.o $(LIBS) $(WRAP_LIBS); \
+ $(LTLINK) -Wl,--base-file,slapd.base -o slapd $@ \
+ $(OBJS) symdummy.o version.o $(LIBS) $(WRAP_LIBS)
+ $(RM) slapd.exe
+ @echo dlltool --dllname slapd.exe --input-def slapd.def \
+ --base-file slapd.base --output-exp $@; \
+ dlltool --dllname slapd.exe --input-def slapd.def \
+ --base-file slapd.base --output-exp $@
+
+slapi/libslapi.la: FORCE
+ cd slapi && $(MAKE) $(MFLAGS) all
+
+slapd: $(SLAPD_DEPENDS) @LIBSLAPI@
+ $(LTLINK) -o $@ $(SLAPD_OBJECTS) $(LIBS) \
+ $(WRAP_LIBS)
+ $(RM) $(SLAPTOOLS)
+ for i in $(SLAPTOOLS); do \
+ $(LN_S) slapd$(EXEEXT) $$i$(EXEEXT); done
+
+
+sslapd: version.o
+ $(LTLINK) -static -o $@ $(OBJS) version.o $(LIBS) $(WRAP_LIBS)
+
+dummy $(SLAPD_DYNAMIC_BACKENDS): slapd
+ cd $@ && $(MAKE) $(MFLAGS) all
+ @touch $@
+
+dynamic_overlays: slapd
+ cd overlays && $(MAKE) $(MFLAGS) dynamic
+
+dynamic_pwmods: slapd
+ cd pwmods && $(MAKE) $(MFLAGS) dynamic
+
+#
+# In Windows, dynamic backends have to be built after slapd. For this
+# reason, we only build static backends now and dynamic backends later.
+#
+.backend: FORCE
+ @if test -n "$(SLAPD_STATIC_BACKENDS)"; then \
+ echo "building static backends..."; \
+ for i in XX $(SLAPD_STATIC_BACKENDS); do \
+ if test $$i != XX; then \
+ echo " "; echo " cd $$i && $(MAKE) $(MFLAGS) all"; \
+ ( cd $$i && $(MAKE) $(MFLAGS) all ); \
+ if test $$? != 0; then exit 1; fi; \
+ fi; \
+ done; \
+ echo " "; \
+ fi
+
+libbackends.a: .backend
+ @$(RM) -r tmp
+ @$(MKDIR) tmp
+ @-for i in back-*/*.a; do \
+ ( \
+ cd tmp; \
+ $(AR) x ../$$i; \
+ pre=`echo $$i | $(SED) -e 's/\/.*$$//' -e 's/back-//'`; \
+ for j in *.o; do \
+ mv $$j $${pre}$$j; \
+ done; \
+ $(AR) ruv libbackends.a *.o 2>&1 | grep -v truncated; \
+ $(RM) *.o __.SYMDEF ________64ELEL_ ; \
+ echo "added backend library $$i"; \
+ echo ""; \
+ ); \
+ done
+ @mv -f tmp/libbackends.a ./libbackends.a
+ @$(RM) -r tmp
+ @if test ! -z "$(RANLIB)" ; then \
+ $(RANLIB) libbackends.a; \
+ fi
+ @ls -l libbackends.a; echo ""
+
+liboverlays.a: FORCE
+ cd overlays && $(MAKE) $(MFLAGS) static
+
+version.c: Makefile
+ @-$(RM) $@
+ $(MKVERSION) -s -n Versionstr slapd > $@
+
+version.o: version.c $(OBJS) $(SLAPD_LIBDEPEND)
+
+backends.o: backends.c $(srcdir)/slap.h
+
+depend-local-srv: FORCE
+ @for i in $(SUBDIRS); do \
+ if test -d $$i && test -f $$i/Makefile ; then \
+ echo; echo " cd $$i && $(MAKE) $(MFLAGS) depend"; \
+ ( cd $$i && $(MAKE) $(MFLAGS) depend ); \
+ if test $$? != 0 ; then exit 1; fi ; \
+ fi; \
+ done
+ @echo ""
+
+clean-local:
+ $(RM) *.exp *.def *.base *.a *.objs symdummy.c
+
+veryclean-local:
+ $(RM) backends.c
+
+clean-local-srv: FORCE
+ @for i in $(SUBDIRS); do \
+ if test -d $$i && test -f $$i/Makefile ; then \
+ echo; echo " cd $$i && $(MAKE) $(MFLAGS) clean"; \
+ ( cd $$i && $(MAKE) $(MFLAGS) clean ); \
+ if test $$? != 0 ; then exit 1; fi ; \
+ fi; \
+ done
+ $(RM) *.tmp all-cffiles
+
+veryclean-local-srv: FORCE
+ @for i in $(SUBDIRS); do \
+ if test -d $$i && test -f $$i/Makefile ; then \
+ echo; echo " cd $$i && $(MAKE) $(MFLAGS) clean"; \
+ ( cd $$i && $(MAKE) $(MFLAGS) veryclean ); \
+ fi; \
+ done
+
+install-local-srv: install-slapd install-tools \
+ install-conf install-schema install-tools
+
+install-slapd: FORCE
+ -$(MKDIR) $(DESTDIR)$(libexecdir)
+ -$(MKDIR) $(DESTDIR)$(localstatedir)/run
+ $(LTINSTALL) $(INSTALLFLAGS) $(STRIP_OPTS) -m 755 \
+ slapd$(EXEEXT) $(DESTDIR)$(libexecdir)
+ @for i in $(SUBDIRS); do \
+ if test -d $$i && test -f $$i/Makefile ; then \
+ echo; echo " cd $$i && $(MAKE) $(MFLAGS) install"; \
+ ( cd $$i && $(MAKE) $(MFLAGS) install ); \
+ if test $$? != 0 ; then exit 1; fi ; \
+ fi; \
+ done
+
+all-cffiles: slapd $(SLAPD_DYNAMIC_BACKENDS) dynamic_overlays dynamic_pwmods
+ @if test $(PLAT) = NT; then \
+ sysconfdir=`cygpath -w $(sysconfdir) | \
+ $(SED) -e 's/\\\\/\\\\\\\\\\\\\\\\/g'`; \
+ localstatedir=`cygpath -w $(localstatedir) | \
+ $(SED) -e 's/\\\\/\\\\\\\\\\\\\\\\/g'`; \
+ moduledir=`cygpath -w $(moduledir) | \
+ $(SED) -e 's/\\\\/\\\\\\\\\\\\\\\\/g'`; \
+ else \
+ sysconfdir=$(sysconfdir); \
+ localstatedir=$(localstatedir); \
+ moduledir=$(moduledir); \
+ fi; \
+ $(SED) -e "s;%SYSCONFDIR%;$$sysconfdir;" \
+ -e "s;%LOCALSTATEDIR%;$$localstatedir;" \
+ -e "s;%MODULEDIR%;$$moduledir;" \
+ $(srcdir)/slapd.conf > slapd.conf.tmp || exit $$? ; \
+ $(SED) -e "s;%SYSCONFDIR%;$$sysconfdir;" \
+ -e "s;%LOCALSTATEDIR%;$$localstatedir;" \
+ -e "s;%MODULEDIR%;$$moduledir;" \
+ $(srcdir)/slapd.ldif > slapd.ldif.tmp || exit $$? ;
+ @if test -n "$(systemdsystemunitdir)"; then \
+ $(SED) -e "s;%LIBEXECDIR%;$(libexecdir);" \
+ $(srcdir)/slapd.service > slapd.service.tmp ; \
+ fi
+ touch all-cffiles
+
+install-schema: FORCE
+ @if test -d $(DESTDIR)$(schemadir) ; then \
+ echo "MOVING EXISTING SCHEMA DIR to $(DESTDIR)$(schemadir).$$$$" ; \
+ mv $(DESTDIR)$(schemadir) $(DESTDIR)$(schemadir).$$$$ ; \
+ fi
+ $(MKDIR) $(DESTDIR)$(schemadir)
+ @SD=$(DESTDIR)$(schemadir) ; \
+ files=`cd $(srcdir)/schema ; echo README *.ldif *.schema` ; \
+ for i in $$files ; do \
+ echo $(INSTALL) $(INSTALLFLAGS) -m 444 schema/$$i $$SD/$$i ; \
+ $(INSTALL) $(INSTALLFLAGS) -m 444 $(srcdir)/schema/$$i $$SD/$$i ; \
+ done
+
+install-conf: FORCE
+ @-$(MKDIR) $(DESTDIR)$(sysconfdir)
+ $(INSTALL) $(INSTALLFLAGS) -m 600 slapd.conf.tmp $(DESTDIR)$(sysconfdir)/slapd.conf.default
+ if test ! -f $(DESTDIR)$(sysconfdir)/slapd.conf; then \
+ echo "installing slapd.conf in $(sysconfdir)"; \
+ echo "$(INSTALL) $(INSTALLFLAGS) -m 600 slapd.conf.tmp $(DESTDIR)$(sysconfdir)/slapd.conf"; \
+ $(INSTALL) $(INSTALLFLAGS) -m 600 slapd.conf.tmp $(DESTDIR)$(sysconfdir)/slapd.conf; \
+ else \
+ echo "PRESERVING EXISTING CONFIGURATION FILE $(DESTDIR)$(sysconfdir)/slapd.conf" ; \
+ fi
+ $(INSTALL) $(INSTALLFLAGS) -m 600 slapd.ldif.tmp $(DESTDIR)$(sysconfdir)/slapd.ldif.default
+ if test ! -f $(DESTDIR)$(sysconfdir)/slapd.ldif; then \
+ echo "installing slapd.ldif in $(sysconfdir)"; \
+ echo "$(INSTALL) $(INSTALLFLAGS) -m 600 slapd.ldif.tmp $(DESTDIR)$(sysconfdir)/slapd.ldif"; \
+ $(INSTALL) $(INSTALLFLAGS) -m 600 slapd.ldif.tmp $(DESTDIR)$(sysconfdir)/slapd.ldif; \
+ else \
+ echo "PRESERVING EXISTING CONFIGURATION FILE $(DESTDIR)$(sysconfdir)/slapd.ldif" ; \
+ fi
+ if test -n "$(systemdsystemunitdir)" && test ! -f $(DESTDIR)$(systemdsystemunitdir)/slapd.service; then \
+ $(MKDIR) $(DESTDIR)$(systemdsystemunitdir); \
+ echo "installing slapd.service in $(systemdsystemunitdir)"; \
+ echo "$(INSTALL) $(INSTALLFLAGS) -m 644 slapd.service.tmp $(DESTDIR)$(systemdsystemunitdir)/slapd.service"; \
+ $(INSTALL) $(INSTALLFLAGS) -m 644 slapd.service.tmp $(DESTDIR)$(systemdsystemunitdir)/slapd.service; \
+ fi
+
+install-db-config: FORCE
+ @-$(MKDIR) $(DESTDIR)$(localstatedir) $(DESTDIR)$(sysconfdir)
+ @-$(INSTALL) -m 700 -d $(DESTDIR)$(localstatedir)/openldap-data
+ $(INSTALL) $(INSTALLFLAGS) -m 600 $(srcdir)/DB_CONFIG \
+ $(DESTDIR)$(localstatedir)/openldap-data/DB_CONFIG.example
+ $(INSTALL) $(INSTALLFLAGS) -m 600 $(srcdir)/DB_CONFIG \
+ $(DESTDIR)$(sysconfdir)/DB_CONFIG.example
+
+install-tools: FORCE
+ -$(MKDIR) $(DESTDIR)$(sbindir)
+ for i in $(SLAPTOOLS); do \
+ $(RM) $(DESTDIR)$(sbindir)/$$i$(EXEEXT); \
+ $(LN_S) -f $(DESTDIR)$(libexecdir)/slapd$(EXEEXT) $(DESTDIR)$(sbindir)/$$i$(EXEEXT); \
+ done
+
diff --git a/servers/slapd/abandon.c b/servers/slapd/abandon.c
new file mode 100644
index 0000000..7eff9bc
--- /dev/null
+++ b/servers/slapd/abandon.c
@@ -0,0 +1,141 @@
+/* abandon.c - decode and handle an ldap abandon operation */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+int
+do_abandon( Operation *op, SlapReply *rs )
+{
+ ber_int_t id;
+ Operation *o;
+ const char *msg;
+
+ Debug( LDAP_DEBUG_TRACE, "%s do_abandon\n",
+ op->o_log_prefix );
+
+ /*
+ * Parse the abandon request. It looks like this:
+ *
+ * AbandonRequest := MessageID
+ */
+
+ if ( ber_scanf( op->o_ber, "i", &id ) == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_abandon: ber_scanf failed\n",
+ op->o_log_prefix );
+ send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
+ return SLAPD_DISCONNECT;
+ }
+
+ Debug( LDAP_DEBUG_STATS, "%s ABANDON msg=%ld\n",
+ op->o_log_prefix, (long) id );
+
+ if( get_ctrls( op, rs, 0 ) != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_abandon: get_ctrls failed\n",
+ op->o_log_prefix );
+ return rs->sr_err;
+ }
+
+ Debug( LDAP_DEBUG_ARGS, "%s do_abandon: id=%ld\n",
+ op->o_log_prefix, (long) id );
+
+ if( id <= 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_abandon: bad msgid %ld\n",
+ op->o_log_prefix, (long) id );
+ return LDAP_SUCCESS;
+ }
+
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+
+ /* Find the operation being abandoned. */
+ LDAP_STAILQ_FOREACH( o, &op->o_conn->c_ops, o_next ) {
+ if ( o->o_msgid == id ) {
+ break;
+ }
+ }
+
+ if ( o == NULL ) {
+ msg = "not found";
+ /* The operation is not active. Just discard it if found. */
+ LDAP_STAILQ_FOREACH( o, &op->o_conn->c_pending_ops, o_next ) {
+ if ( o->o_msgid == id ) {
+ msg = "discarded";
+ /* FIXME: This traverses c_pending_ops yet again. */
+ LDAP_STAILQ_REMOVE( &op->o_conn->c_pending_ops,
+ o, Operation, o_next );
+ LDAP_STAILQ_NEXT(o, o_next) = NULL;
+ op->o_conn->c_n_ops_pending--;
+ slap_op_free( o, NULL );
+ break;
+ }
+ }
+
+ } else if ( o->o_tag == LDAP_REQ_BIND
+ || o->o_tag == LDAP_REQ_UNBIND
+ || o->o_tag == LDAP_REQ_ABANDON ) {
+ msg = "cannot be abandoned";
+
+#if 0 /* Would break o_abandon used as "suppress response" flag, ITS#6138 */
+ } else if ( o->o_abandon ) {
+ msg = "already being abandoned";
+#endif
+
+ } else {
+ msg = "found";
+ /* Set the o_abandon flag in the to-be-abandoned operation.
+ * The backend can periodically check this flag and abort the
+ * operation at a convenient time. However it should "send"
+ * the response anyway, with result code SLAPD_ABANDON.
+ * The functions in result.c will intercept the message.
+ */
+ o->o_abandon = 1;
+ op->orn_msgid = id;
+ op->o_bd = frontendDB;
+ rs->sr_err = frontendDB->be_abandon( op, rs );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+
+ Debug( LDAP_DEBUG_TRACE, "%s do_abandon: op=%ld %s\n",
+ op->o_log_prefix, (long) id, msg );
+ return rs->sr_err;
+}
+
+int
+fe_op_abandon( Operation *op, SlapReply *rs )
+{
+ LDAP_STAILQ_FOREACH( op->o_bd, &backendDB, be_next ) {
+ if ( op->o_bd->be_abandon ) {
+ (void)op->o_bd->be_abandon( op, rs );
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
diff --git a/servers/slapd/aci.c b/servers/slapd/aci.c
new file mode 100644
index 0000000..33e6074
--- /dev/null
+++ b/servers/slapd/aci.c
@@ -0,0 +1,1834 @@
+/* aci.c - routines to parse and check acl's */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_ACI_ENABLED
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/regex.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include "slap.h"
+#include "lber_pvt.h"
+#include "lutil.h"
+
+/* use most appropriate size */
+#define ACI_BUF_SIZE 1024
+
+/* move to "stable" when no longer experimental */
+#define SLAPD_ACI_SYNTAX "1.3.6.1.4.1.4203.666.2.1"
+
+/* change this to "OpenLDAPset" */
+#define SLAPD_ACI_SET_ATTR "template"
+
+typedef enum slap_aci_scope_t {
+ SLAP_ACI_SCOPE_ENTRY = 0x1,
+ SLAP_ACI_SCOPE_CHILDREN = 0x2,
+ SLAP_ACI_SCOPE_SUBTREE = ( SLAP_ACI_SCOPE_ENTRY | SLAP_ACI_SCOPE_CHILDREN )
+} slap_aci_scope_t;
+
+enum {
+ ACI_BV_ENTRY,
+ ACI_BV_CHILDREN,
+ ACI_BV_ONELEVEL,
+ ACI_BV_SUBTREE,
+
+ ACI_BV_BR_ENTRY,
+ ACI_BV_BR_CHILDREN,
+ ACI_BV_BR_ALL,
+
+ ACI_BV_ACCESS_ID,
+ ACI_BV_PUBLIC,
+ ACI_BV_USERS,
+ ACI_BV_SELF,
+ ACI_BV_DNATTR,
+ ACI_BV_GROUP,
+ ACI_BV_ROLE,
+ ACI_BV_SET,
+ ACI_BV_SET_REF,
+
+ ACI_BV_GRANT,
+ ACI_BV_DENY,
+
+ ACI_BV_GROUP_CLASS,
+ ACI_BV_GROUP_ATTR,
+ ACI_BV_ROLE_CLASS,
+ ACI_BV_ROLE_ATTR,
+
+ ACI_BV_SET_ATTR,
+
+ ACI_BV_LAST
+};
+
+static const struct berval aci_bv[] = {
+ /* scope */
+ BER_BVC("entry"),
+ BER_BVC("children"),
+ BER_BVC("onelevel"),
+ BER_BVC("subtree"),
+
+ /* */
+ BER_BVC("[entry]"),
+ BER_BVC("[children]"),
+ BER_BVC("[all]"),
+
+ /* type */
+ BER_BVC("access-id"),
+ BER_BVC("public"),
+ BER_BVC("users"),
+ BER_BVC("self"),
+ BER_BVC("dnattr"),
+ BER_BVC("group"),
+ BER_BVC("role"),
+ BER_BVC("set"),
+ BER_BVC("set-ref"),
+
+ /* actions */
+ BER_BVC("grant"),
+ BER_BVC("deny"),
+
+ /* schema */
+ BER_BVC(SLAPD_GROUP_CLASS),
+ BER_BVC(SLAPD_GROUP_ATTR),
+ BER_BVC(SLAPD_ROLE_CLASS),
+ BER_BVC(SLAPD_ROLE_ATTR),
+
+ BER_BVC(SLAPD_ACI_SET_ATTR),
+
+ BER_BVNULL
+};
+
+static AttributeDescription *slap_ad_aci;
+
+static int
+OpenLDAPaciValidate(
+ Syntax *syntax,
+ struct berval *val );
+
+static int
+OpenLDAPaciPretty(
+ Syntax *syntax,
+ struct berval *val,
+ struct berval *out,
+ void *ctx );
+
+static int
+OpenLDAPaciNormalize(
+ slap_mask_t use,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *out,
+ void *ctx );
+
+#define OpenLDAPaciMatch octetStringMatch
+
+static int
+aci_list_map_rights(
+ struct berval *list )
+{
+ struct berval bv;
+ slap_access_t mask;
+ int i;
+
+ ACL_INIT( mask );
+ for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
+ if ( bv.bv_len <= 0 ) {
+ continue;
+ }
+
+ switch ( *bv.bv_val ) {
+ case 'x':
+ /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt does not
+ * define any equivalent to the AUTH right, so I've just used
+ * 'x' for now.
+ */
+ ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
+ break;
+ case 'd':
+ /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
+ * the right 'd' to mean "delete"; we hijack it to mean
+ * "disclose" for consistency wuith the rest of slapd.
+ */
+ ACL_PRIV_SET(mask, ACL_PRIV_DISCLOSE);
+ break;
+ case 'c':
+ ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
+ break;
+ case 's':
+ /* **** NOTE: draft-ietf-ldapext-aci-model-0.3.txt defines
+ * the right 's' to mean "set", but in the examples states
+ * that the right 's' means "search". The latter definition
+ * is used here.
+ */
+ ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
+ break;
+ case 'r':
+ ACL_PRIV_SET(mask, ACL_PRIV_READ);
+ break;
+ case 'w':
+ ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ return mask;
+}
+
+static int
+aci_list_has_attr(
+ struct berval *list,
+ const struct berval *attr,
+ struct berval *val )
+{
+ struct berval bv, left, right;
+ int i;
+
+ for ( i = 0; acl_get_part( list, i, ',', &bv ) >= 0; i++ ) {
+ if ( acl_get_part(&bv, 0, '=', &left ) < 0
+ || acl_get_part( &bv, 1, '=', &right ) < 0 )
+ {
+ if ( ber_bvstrcasecmp( attr, &bv ) == 0 ) {
+ return(1);
+ }
+
+ } else if ( val == NULL ) {
+ if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
+ return(1);
+ }
+
+ } else {
+ if ( ber_bvstrcasecmp( attr, &left ) == 0 ) {
+ /* FIXME: this is also totally undocumented! */
+ /* this is experimental code that implements a
+ * simple (prefix) match of the attribute value.
+ * the ACI draft does not provide for aci's that
+ * apply to specific values, but it would be
+ * nice to have. If the <attr> part of an aci's
+ * rights list is of the form <attr>=<value>,
+ * that means the aci applies only to attrs with
+ * the given value. Furthermore, if the attr is
+ * of the form <attr>=<value>*, then <value> is
+ * treated as a prefix, and the aci applies to
+ * any value with that prefix.
+ *
+ * Ideally, this would allow r.e. matches.
+ */
+ if ( acl_get_part( &right, 0, '*', &left ) < 0
+ || right.bv_len <= left.bv_len )
+ {
+ if ( ber_bvstrcasecmp( val, &right ) == 0 ) {
+ return 1;
+ }
+
+ } else if ( val->bv_len >= left.bv_len ) {
+ if ( strncasecmp( val->bv_val, left.bv_val, left.bv_len ) == 0 ) {
+ return(1);
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static slap_access_t
+aci_list_get_attr_rights(
+ struct berval *list,
+ const struct berval *attr,
+ struct berval *val )
+{
+ struct berval bv;
+ slap_access_t mask;
+ int i;
+
+ /* loop through each rights/attr pair, skip first part (action) */
+ ACL_INIT(mask);
+ for ( i = 1; acl_get_part( list, i + 1, ';', &bv ) >= 0; i += 2 ) {
+ if ( aci_list_has_attr( &bv, attr, val ) == 0 ) {
+ Debug( LDAP_DEBUG_ACL,
+ " <= aci_list_get_attr_rights "
+ "test %s for %s -> failed\n",
+ bv.bv_val, attr->bv_val );
+ continue;
+ }
+
+ Debug( LDAP_DEBUG_ACL,
+ " <= aci_list_get_attr_rights "
+ "test %s for %s -> ok\n",
+ bv.bv_val, attr->bv_val );
+
+ if ( acl_get_part( list, i, ';', &bv ) < 0 ) {
+ Debug( LDAP_DEBUG_ACL,
+ " <= aci_list_get_attr_rights "
+ "test no rights\n" );
+ continue;
+ }
+
+ mask |= aci_list_map_rights( &bv );
+ Debug( LDAP_DEBUG_ACL,
+ " <= aci_list_get_attr_rights "
+ "rights %s to mask 0x%x\n",
+ bv.bv_val, mask );
+ }
+
+ return mask;
+}
+
+static int
+aci_list_get_rights(
+ struct berval *list,
+ struct berval *attr,
+ struct berval *val,
+ slap_access_t *grant,
+ slap_access_t *deny )
+{
+ struct berval perm, actn, baseattr;
+ slap_access_t *mask;
+ int i, found;
+
+ if ( attr == NULL || BER_BVISEMPTY( attr ) ) {
+ attr = (struct berval *)&aci_bv[ ACI_BV_ENTRY ];
+
+ } else if ( acl_get_part( attr, 0, ';', &baseattr ) > 0 ) {
+ attr = &baseattr;
+ }
+ found = 0;
+ ACL_INIT(*grant);
+ ACL_INIT(*deny);
+ /* loop through each permissions clause */
+ for ( i = 0; acl_get_part( list, i, '$', &perm ) >= 0; i++ ) {
+ if ( acl_get_part( &perm, 0, ';', &actn ) < 0 ) {
+ continue;
+ }
+
+ if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_GRANT ], &actn ) == 0 ) {
+ mask = grant;
+
+ } else if ( ber_bvstrcasecmp( &aci_bv[ ACI_BV_DENY ], &actn ) == 0 ) {
+ mask = deny;
+
+ } else {
+ continue;
+ }
+
+ *mask |= aci_list_get_attr_rights( &perm, attr, val );
+ *mask |= aci_list_get_attr_rights( &perm, &aci_bv[ ACI_BV_BR_ALL ], NULL );
+
+ if ( *mask != ACL_PRIV_NONE ) {
+ found = 1;
+ }
+ }
+
+ return found;
+}
+
+static int
+aci_group_member (
+ struct berval *subj,
+ const struct berval *defgrpoc,
+ const struct berval *defgrpat,
+ Operation *op,
+ Entry *e,
+ int nmatch,
+ regmatch_t *matches
+)
+{
+ struct berval subjdn;
+ struct berval grpoc;
+ struct berval grpat;
+ ObjectClass *grp_oc = NULL;
+ AttributeDescription *grp_ad = NULL;
+ const char *text;
+ int rc;
+
+ /* format of string is "{group|role}/objectClassValue/groupAttrName" */
+ if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
+ return 0;
+ }
+
+ if ( acl_get_part( subj, 1, '/', &grpoc ) < 0 ) {
+ grpoc = *defgrpoc;
+ }
+
+ if ( acl_get_part( subj, 2, '/', &grpat ) < 0 ) {
+ grpat = *defgrpat;
+ }
+
+ rc = slap_bv2ad( &grpat, &grp_ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ rc = 0;
+ goto done;
+ }
+ rc = 0;
+
+ grp_oc = oc_bvfind( &grpoc );
+
+ if ( grp_oc != NULL && grp_ad != NULL ) {
+ char buf[ ACI_BUF_SIZE ];
+ struct berval bv, ndn;
+ AclRegexMatches amatches = { 0 };
+
+ amatches.dn_count = nmatch;
+ AC_MEMCPY( amatches.dn_data, matches, sizeof( amatches.dn_data ) );
+
+ bv.bv_len = sizeof( buf ) - 1;
+ bv.bv_val = (char *)&buf;
+ if ( acl_string_expand( &bv, &subjdn,
+ &e->e_nname, NULL, &amatches ) )
+ {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ if ( dnNormalize( 0, NULL, NULL, &bv, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS )
+ {
+ rc = ( backend_group( op, e, &ndn, &op->o_ndn,
+ grp_oc, grp_ad ) == 0 );
+ slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
+ }
+ }
+
+done:
+ return rc;
+}
+
+static int
+aci_mask(
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ struct berval *aci,
+ int nmatch,
+ regmatch_t *matches,
+ slap_access_t *grant,
+ slap_access_t *deny,
+ slap_aci_scope_t asserted_scope )
+{
+ struct berval bv,
+ scope,
+ perms,
+ type,
+ opts,
+ sdn;
+ int rc;
+
+ ACL_INIT( *grant );
+ ACL_INIT( *deny );
+
+ assert( !BER_BVISNULL( &desc->ad_cname ) );
+
+ /* parse an aci of the form:
+ oid # scope # action;rights;attr;rights;attr
+ $ action;rights;attr;rights;attr # type # subject
+
+ [NOTE: the following comment is very outdated,
+ as the draft version it refers to (Ando, 2004-11-20)].
+
+ See draft-ietf-ldapext-aci-model-04.txt section 9.1 for
+ a full description of the format for this attribute.
+ Differences: "this" in the draft is "self" here, and
+ "self" and "public" is in the position of type.
+
+ <scope> = {entry|children|subtree}
+ <type> = {public|users|access-id|subtree|onelevel|children|
+ self|dnattr|group|role|set|set-ref}
+
+ This routine now supports scope={ENTRY,CHILDREN}
+ with the semantics:
+ - ENTRY applies to "entry" and "subtree";
+ - CHILDREN applies to "children" and "subtree"
+ */
+
+ /* check that the aci has all 5 components */
+ if ( acl_get_part( aci, 4, '#', NULL ) < 0 ) {
+ return 0;
+ }
+
+ /* check that the aci family is supported */
+ /* FIXME: the OID is ignored? */
+ if ( acl_get_part( aci, 0, '#', &bv ) < 0 ) {
+ return 0;
+ }
+
+ /* check that the scope matches */
+ if ( acl_get_part( aci, 1, '#', &scope ) < 0 ) {
+ return 0;
+ }
+
+ /* note: scope can be either ENTRY or CHILDREN;
+ * they respectively match "entry" and "children" in bv
+ * both match "subtree" */
+ switch ( asserted_scope ) {
+ case SLAP_ACI_SCOPE_ENTRY:
+ if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_ENTRY ] ) != 0
+ && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
+ {
+ return 0;
+ }
+ break;
+
+ case SLAP_ACI_SCOPE_CHILDREN:
+ if ( ber_bvcmp( &scope, &aci_bv[ ACI_BV_CHILDREN ] ) != 0
+ && ber_bvstrcasecmp( &scope, &aci_bv[ ACI_BV_SUBTREE ] ) != 0 )
+ {
+ return 0;
+ }
+ break;
+
+ case SLAP_ACI_SCOPE_SUBTREE:
+ /* TODO: add assertion? */
+ return 0;
+ }
+
+ /* get the list of permissions clauses, bail if empty */
+ if ( acl_get_part( aci, 2, '#', &perms ) <= 0 ) {
+ assert( 0 );
+ return 0;
+ }
+
+ /* check if any permissions allow desired access */
+ if ( aci_list_get_rights( &perms, &desc->ad_cname, val, grant, deny ) == 0 ) {
+ return 0;
+ }
+
+ /* see if we have a DN match */
+ if ( acl_get_part( aci, 3, '#', &type ) < 0 ) {
+ assert( 0 );
+ return 0;
+ }
+
+ /* see if we have a public (i.e. anonymous) access */
+ if ( ber_bvcmp( &aci_bv[ ACI_BV_PUBLIC ], &type ) == 0 ) {
+ return 1;
+ }
+
+ /* otherwise require an identity */
+ if ( BER_BVISNULL( &op->o_ndn ) || BER_BVISEMPTY( &op->o_ndn ) ) {
+ return 0;
+ }
+
+ /* see if we have a users access */
+ if ( ber_bvcmp( &aci_bv[ ACI_BV_USERS ], &type ) == 0 ) {
+ return 1;
+ }
+
+ /* NOTE: this may fail if a DN contains a valid '#' (unescaped);
+ * just grab all the berval up to its end (ITS#3303).
+ * NOTE: the problem could be solved by providing the DN with
+ * the embedded '#' encoded as hexpairs: "cn=Foo#Bar" would
+ * become "cn=Foo\23Bar" and be safely used by aci_mask(). */
+#if 0
+ if ( acl_get_part( aci, 4, '#', &sdn ) < 0 ) {
+ return 0;
+ }
+#endif
+ sdn.bv_val = type.bv_val + type.bv_len + STRLENOF( "#" );
+ sdn.bv_len = aci->bv_len - ( sdn.bv_val - aci->bv_val );
+
+ /* get the type options, if any */
+ if ( acl_get_part( &type, 1, '/', &opts ) > 0 ) {
+ opts.bv_len = type.bv_len - ( opts.bv_val - type.bv_val );
+ type.bv_len = opts.bv_val - type.bv_val - 1;
+
+ } else {
+ BER_BVZERO( &opts );
+ }
+
+ if ( ber_bvcmp( &aci_bv[ ACI_BV_ACCESS_ID ], &type ) == 0 ) {
+ return dn_match( &op->o_ndn, &sdn );
+
+ } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SUBTREE ], &type ) == 0 ) {
+ return dnIsSuffix( &op->o_ndn, &sdn );
+
+ } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ONELEVEL ], &type ) == 0 ) {
+ struct berval pdn;
+
+ dnParent( &sdn, &pdn );
+
+ return dn_match( &op->o_ndn, &pdn );
+
+ } else if ( ber_bvcmp( &aci_bv[ ACI_BV_CHILDREN ], &type ) == 0 ) {
+ return ( !dn_match( &op->o_ndn, &sdn ) && dnIsSuffix( &op->o_ndn, &sdn ) );
+
+ } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SELF ], &type ) == 0 ) {
+ return dn_match( &op->o_ndn, &e->e_nname );
+
+ } else if ( ber_bvcmp( &aci_bv[ ACI_BV_DNATTR ], &type ) == 0 ) {
+ Attribute *at;
+ AttributeDescription *ad = NULL;
+ const char *text;
+
+ rc = slap_bv2ad( &sdn, &ad, &text );
+ assert( rc == LDAP_SUCCESS );
+
+ rc = 0;
+ for ( at = attrs_find( e->e_attrs, ad );
+ at != NULL;
+ at = attrs_find( at->a_next, ad ) )
+ {
+ if ( attr_valfind( at,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ &op->o_ndn, NULL, op->o_tmpmemctx ) == 0 )
+ {
+ rc = 1;
+ break;
+ }
+ }
+
+ return rc;
+
+ } else if ( ber_bvcmp( &aci_bv[ ACI_BV_GROUP ], &type ) == 0 ) {
+ struct berval oc,
+ at;
+
+ if ( BER_BVISNULL( &opts ) ) {
+ oc = aci_bv[ ACI_BV_GROUP_CLASS ];
+ at = aci_bv[ ACI_BV_GROUP_ATTR ];
+
+ } else {
+ if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) {
+ assert( 0 );
+ }
+
+ if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) {
+ at = aci_bv[ ACI_BV_GROUP_ATTR ];
+ }
+ }
+
+ if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) )
+ {
+ return 1;
+ }
+
+ } else if ( ber_bvcmp( &aci_bv[ ACI_BV_ROLE ], &type ) == 0 ) {
+ struct berval oc,
+ at;
+
+ if ( BER_BVISNULL( &opts ) ) {
+ oc = aci_bv[ ACI_BV_ROLE_CLASS ];
+ at = aci_bv[ ACI_BV_ROLE_ATTR ];
+
+ } else {
+ if ( acl_get_part( &opts, 0, '/', &oc ) < 0 ) {
+ assert( 0 );
+ }
+
+ if ( acl_get_part( &opts, 1, '/', &at ) < 0 ) {
+ at = aci_bv[ ACI_BV_ROLE_ATTR ];
+ }
+ }
+
+ if ( aci_group_member( &sdn, &oc, &at, op, e, nmatch, matches ) )
+ {
+ return 1;
+ }
+
+ } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET ], &type ) == 0 ) {
+ if ( acl_match_set( &sdn, op, e, NULL ) ) {
+ return 1;
+ }
+
+ } else if ( ber_bvcmp( &aci_bv[ ACI_BV_SET_REF ], &type ) == 0 ) {
+ if ( acl_match_set( &sdn, op, e, (struct berval *)&aci_bv[ ACI_BV_SET_ATTR ] ) ) {
+ return 1;
+ }
+
+ } else {
+ /* it passed normalization! */
+ assert( 0 );
+ }
+
+ return 0;
+}
+
+static int
+aci_init( void )
+{
+ /* OpenLDAP eXperimental Syntax */
+ static slap_syntax_defs_rec aci_syntax_def = {
+ "( 1.3.6.1.4.1.4203.666.2.1 DESC 'OpenLDAP Experimental ACI' )",
+ SLAP_SYNTAX_HIDE,
+ NULL,
+ OpenLDAPaciValidate,
+ OpenLDAPaciPretty
+ };
+ static slap_mrule_defs_rec aci_mr_def = {
+ "( 1.3.6.1.4.1.4203.666.4.2 NAME 'OpenLDAPaciMatch' "
+ "SYNTAX 1.3.6.1.4.1.4203.666.2.1 )",
+ SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
+ NULL, OpenLDAPaciNormalize, OpenLDAPaciMatch,
+ NULL, NULL,
+ NULL
+ };
+ static struct {
+ char *name;
+ char *desc;
+ slap_mask_t flags;
+ AttributeDescription **ad;
+ } aci_at = {
+ "OpenLDAPaci", "( 1.3.6.1.4.1.4203.666.1.5 "
+ "NAME 'OpenLDAPaci' "
+ "DESC 'OpenLDAP access control information (experimental)' "
+ "EQUALITY OpenLDAPaciMatch "
+ "SYNTAX 1.3.6.1.4.1.4203.666.2.1 "
+ "USAGE directoryOperation )",
+ SLAP_AT_HIDE,
+ &slap_ad_aci
+ };
+
+ int rc;
+
+ /* ACI syntax */
+ rc = register_syntax( &aci_syntax_def );
+ if ( rc != 0 ) {
+ return rc;
+ }
+
+ /* ACI equality rule */
+ rc = register_matching_rule( &aci_mr_def );
+ if ( rc != 0 ) {
+ return rc;
+ }
+
+ /* ACI attribute */
+ rc = register_at( aci_at.desc, aci_at.ad, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "aci_init: at_register failed\n" );
+ return rc;
+ }
+
+ /* install flags */
+ (*aci_at.ad)->ad_type->sat_flags |= aci_at.flags;
+
+ return rc;
+}
+
+static int
+dynacl_aci_parse(
+ const char *fname,
+ int lineno,
+ const char *opts,
+ slap_style_t sty,
+ const char *right,
+ void **privp )
+{
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+
+ if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
+ fprintf( stderr, "%s: line %d: "
+ "inappropriate style \"%s\" in \"aci\" by clause\n",
+ fname, lineno, style_strings[sty] );
+ return -1;
+ }
+
+ if ( right != NULL && *right != '\0' ) {
+ if ( slap_str2ad( right, &ad, &text ) != LDAP_SUCCESS ) {
+ fprintf( stderr,
+ "%s: line %d: aci \"%s\": %s\n",
+ fname, lineno, right, text );
+ return -1;
+ }
+
+ } else {
+ ad = slap_ad_aci;
+ }
+
+ if ( !is_at_syntax( ad->ad_type, SLAPD_ACI_SYNTAX) ) {
+ fprintf( stderr, "%s: line %d: "
+ "aci \"%s\": inappropriate syntax: %s\n",
+ fname, lineno, right,
+ ad->ad_type->sat_syntax_oid );
+ return -1;
+ }
+
+ *privp = (void *)ad;
+
+ return 0;
+}
+
+static int
+dynacl_aci_unparse( void *priv, struct berval *bv )
+{
+ AttributeDescription *ad = ( AttributeDescription * )priv;
+ char *ptr;
+
+ assert( ad != NULL );
+
+ bv->bv_val = ch_malloc( STRLENOF(" aci=") + ad->ad_cname.bv_len + 1 );
+ ptr = lutil_strcopy( bv->bv_val, " aci=" );
+ ptr = lutil_strcopy( ptr, ad->ad_cname.bv_val );
+ bv->bv_len = ptr - bv->bv_val;
+
+ return 0;
+}
+
+static int
+dynacl_aci_mask(
+ void *priv,
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ int nmatch,
+ regmatch_t *matches,
+ slap_access_t *grantp,
+ slap_access_t *denyp )
+{
+ AttributeDescription *ad = ( AttributeDescription * )priv;
+ Attribute *at;
+ slap_access_t tgrant, tdeny, grant, deny;
+#ifdef LDAP_DEBUG
+ char accessmaskbuf[ACCESSMASK_MAXLEN];
+ char accessmaskbuf1[ACCESSMASK_MAXLEN];
+#endif /* LDAP_DEBUG */
+
+ if ( BER_BVISEMPTY( &e->e_nname ) ) {
+ /* no ACIs in the root DSE */
+ return -1;
+ }
+
+ /* start out with nothing granted, nothing denied */
+ ACL_INIT(tgrant);
+ ACL_INIT(tdeny);
+
+ /* get the aci attribute */
+ at = attr_find( e->e_attrs, ad );
+ if ( at != NULL ) {
+ int i;
+
+ /* the aci is an multi-valued attribute. The
+ * rights are determined by OR'ing the individual
+ * rights given by the acis.
+ */
+ for ( i = 0; !BER_BVISNULL( &at->a_nvals[i] ); i++ ) {
+ if ( aci_mask( op, e, desc, val, &at->a_nvals[i],
+ nmatch, matches, &grant, &deny,
+ SLAP_ACI_SCOPE_ENTRY ) != 0 )
+ {
+ tgrant |= grant;
+ tdeny |= deny;
+ }
+ }
+
+ Debug( LDAP_DEBUG_ACL, " <= aci_mask grant %s deny %s\n",
+ accessmask2str( tgrant, accessmaskbuf, 1 ),
+ accessmask2str( tdeny, accessmaskbuf1, 1 ) );
+ }
+
+ /* If the entry level aci didn't contain anything valid for the
+ * current operation, climb up the tree and evaluate the
+ * acis with scope set to subtree
+ */
+ if ( tgrant == ACL_PRIV_NONE && tdeny == ACL_PRIV_NONE ) {
+ struct berval parent_ndn;
+
+ dnParent( &e->e_nname, &parent_ndn );
+ while ( !BER_BVISEMPTY( &parent_ndn ) ){
+ int i;
+ BerVarray bvals = NULL;
+ int ret, stop;
+
+ /* to solve the chicken'n'egg problem of accessing
+ * the OpenLDAPaci attribute, the direct access
+ * to the entry's attribute is unchecked; however,
+ * further accesses to OpenLDAPaci values in the
+ * ancestors occur through backend_attribute(), i.e.
+ * with the identity of the operation, requiring
+ * further access checking. For uniformity, this
+ * makes further requests occur as the rootdn, if
+ * any, i.e. searching for the OpenLDAPaci attribute
+ * is considered an internal search. If this is not
+ * acceptable, then the same check needs be performed
+ * when accessing the entry's attribute. */
+ struct berval save_o_dn, save_o_ndn;
+
+ if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
+ save_o_dn = op->o_dn;
+ save_o_ndn = op->o_ndn;
+
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ }
+
+ Debug( LDAP_DEBUG_ACL, " checking ACI of \"%s\"\n", parent_ndn.bv_val );
+ ret = backend_attribute( op, NULL, &parent_ndn, ad, &bvals, ACL_AUTH );
+
+ if ( !BER_BVISNULL( &op->o_bd->be_rootndn ) ) {
+ op->o_dn = save_o_dn;
+ op->o_ndn = save_o_ndn;
+ }
+
+ switch ( ret ) {
+ case LDAP_SUCCESS :
+ stop = 0;
+ if ( !bvals ) {
+ break;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &bvals[i] ); i++ ) {
+ if ( aci_mask( op, e, desc, val,
+ &bvals[i],
+ nmatch, matches,
+ &grant, &deny,
+ SLAP_ACI_SCOPE_CHILDREN ) != 0 )
+ {
+ tgrant |= grant;
+ tdeny |= deny;
+ /* evaluation stops as soon as either a "deny" or a
+ * "grant" directive matches.
+ */
+ if ( tgrant != ACL_PRIV_NONE || tdeny != ACL_PRIV_NONE ) {
+ stop = 1;
+ }
+ }
+ Debug( LDAP_DEBUG_ACL, "<= aci_mask grant %s deny %s\n",
+ accessmask2str( tgrant, accessmaskbuf, 1 ),
+ accessmask2str( tdeny, accessmaskbuf1, 1 ) );
+ }
+ break;
+
+ case LDAP_NO_SUCH_ATTRIBUTE:
+ /* just go on if the aci-Attribute is not present in
+ * the current entry
+ */
+ Debug( LDAP_DEBUG_ACL, "no such attribute\n" );
+ stop = 0;
+ break;
+
+ case LDAP_NO_SUCH_OBJECT:
+ /* We have reached the base object */
+ Debug( LDAP_DEBUG_ACL, "no such object\n" );
+ stop = 1;
+ break;
+
+ default:
+ stop = 1;
+ break;
+ }
+
+ if ( stop ) {
+ break;
+ }
+ dnParent( &parent_ndn, &parent_ndn );
+ }
+ }
+
+ *grantp = tgrant;
+ *denyp = tdeny;
+
+ return 0;
+}
+
+/* need to register this at some point */
+static slap_dynacl_t dynacl_aci = {
+ "aci",
+ dynacl_aci_parse,
+ dynacl_aci_unparse,
+ dynacl_aci_mask,
+ NULL,
+ NULL,
+ NULL
+};
+
+int
+dynacl_aci_init( void )
+{
+ int rc;
+
+ rc = aci_init();
+
+ if ( rc == 0 ) {
+ rc = slap_dynacl_register( &dynacl_aci );
+ }
+
+ return rc;
+}
+
+
+/* ACI syntax validation */
+
+/*
+ * Matches given berval to array of bervals
+ * Returns:
+ * >=0 if one if the array elements equals to this berval
+ * -1 if string was not found in array
+ */
+static int
+bv_getcaseidx(
+ struct berval *bv,
+ const struct berval *arr[] )
+{
+ int i;
+
+ if ( BER_BVISEMPTY( bv ) ) {
+ return -1;
+ }
+
+ for ( i = 0; arr[ i ] != NULL ; i++ ) {
+ if ( ber_bvstrcasecmp( bv, arr[ i ] ) == 0 ) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+
+/* Returns what have left in input berval after current sub */
+static void
+bv_get_tail(
+ struct berval *val,
+ struct berval *sub,
+ struct berval *tail )
+{
+ int head_len;
+
+ tail->bv_val = sub->bv_val + sub->bv_len;
+ head_len = (unsigned long) tail->bv_val - (unsigned long) val->bv_val;
+ tail->bv_len = val->bv_len - head_len;
+}
+
+
+/*
+ * aci is accepted in following form:
+ * oid#scope#rights#type#subject
+ * Where:
+ * oid := numeric OID (currently ignored)
+ * scope := entry|children|subtree
+ * rights := right[[$right]...]
+ * right := (grant|deny);action
+ * action := perms;attrs[[;perms;attrs]...]
+ * perms := perm[[,perm]...]
+ * perm := c|s|r|w|x
+ * attrs := attribute[[,attribute]..]|"[all]"
+ * attribute := attributeType|attributeType=attributeValue|attributeType=attributeValuePrefix*
+ * type := public|users|self|dnattr|group|role|set|set-ref|
+ * access_id|subtree|onelevel|children
+ */
+static int
+OpenLDAPaciValidatePerms(
+ struct berval *perms )
+{
+ ber_len_t i;
+
+ for ( i = 0; i < perms->bv_len; ) {
+ switch ( perms->bv_val[ i ] ) {
+ case 'x':
+ case 'd':
+ case 'c':
+ case 's':
+ case 'r':
+ case 'w':
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ACL, "aciValidatePerms: perms needs to be one of x,d,c,s,r,w in '%s'\n", perms->bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( ++i == perms->bv_len ) {
+ return LDAP_SUCCESS;
+ }
+
+ while ( i < perms->bv_len && perms->bv_val[ i ] == ' ' )
+ i++;
+
+ assert( i != perms->bv_len );
+
+ if ( perms->bv_val[ i ] != ',' ) {
+ Debug( LDAP_DEBUG_ACL, "aciValidatePerms: missing comma in '%s'\n", perms->bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ do {
+ i++;
+ } while ( perms->bv_val[ i ] == ' ' );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static const struct berval *ACIgrantdeny[] = {
+ &aci_bv[ ACI_BV_GRANT ],
+ &aci_bv[ ACI_BV_DENY ],
+ NULL
+};
+
+static int
+OpenLDAPaciValidateRight(
+ struct berval *action )
+{
+ struct berval bv = BER_BVNULL;
+ int i;
+
+ /* grant|deny */
+ if ( acl_get_part( action, 0, ';', &bv ) < 0 ||
+ bv_getcaseidx( &bv, ACIgrantdeny ) == -1 )
+ {
+ Debug( LDAP_DEBUG_ACL, "aciValidateRight: '%s' must be either 'grant' or 'deny'\n", bv.bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ for ( i = 0; acl_get_part( action, i + 1, ';', &bv ) >= 0; i++ ) {
+ if ( i & 1 ) {
+ /* perms */
+ if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
+ {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ } else {
+ /* attr */
+ AttributeDescription *ad;
+ const char *text;
+ struct berval attr, left, right;
+ int j;
+
+ /* could be "[all]" or an attribute description */
+ if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
+ continue;
+ }
+
+
+ for ( j = 0; acl_get_part( &bv, j, ',', &attr ) >= 0; j++ )
+ {
+ ad = NULL;
+ text = NULL;
+ if ( acl_get_part( &attr, 0, '=', &left ) < 0
+ || acl_get_part( &attr, 1, '=', &right ) < 0 )
+ {
+ if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS )
+ {
+ Debug( LDAP_DEBUG_ACL, "aciValidateRight: unknown attribute: '%s'\n", attr.bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+ } else {
+ if ( slap_bv2ad( &left, &ad, &text ) != LDAP_SUCCESS )
+ {
+ Debug( LDAP_DEBUG_ACL, "aciValidateRight: unknown attribute: '%s'\n", left.bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+ }
+ }
+ }
+
+ /* "perms;attr" go in pairs */
+ if ( i > 0 && ( i & 1 ) == 0 ) {
+ return LDAP_SUCCESS;
+
+ } else {
+ Debug( LDAP_DEBUG_ACL, "aciValidateRight: perms:attr need to be pairs in '%s'\n", action->bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+OpenLDAPaciNormalizeRight(
+ struct berval *action,
+ struct berval *naction,
+ void *ctx )
+{
+ struct berval grantdeny,
+ perms = BER_BVNULL,
+ bv = BER_BVNULL;
+ int idx,
+ i;
+
+ /* grant|deny */
+ if ( acl_get_part( action, 0, ';', &grantdeny ) < 0 ) {
+ Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: missing ';' in '%s'\n", action->bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+ idx = bv_getcaseidx( &grantdeny, ACIgrantdeny );
+ if ( idx == -1 ) {
+ Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: '%s' must be grant or deny\n", grantdeny.bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ ber_dupbv_x( naction, (struct berval *)ACIgrantdeny[ idx ], ctx );
+
+ for ( i = 1; acl_get_part( action, i, ';', &bv ) >= 0; i++ ) {
+ struct berval nattrs = BER_BVNULL;
+ int freenattrs = 1;
+ if ( i & 1 ) {
+ /* perms */
+ if ( OpenLDAPaciValidatePerms( &bv ) != LDAP_SUCCESS )
+ {
+ return LDAP_INVALID_SYNTAX;
+ }
+ perms = bv;
+
+ } else {
+ /* attr */
+ char *ptr;
+
+ /* could be "[all]" or an attribute description */
+ if ( ber_bvstrcasecmp( &bv, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
+ nattrs = aci_bv[ ACI_BV_BR_ALL ];
+ freenattrs = 0;
+
+ } else {
+ AttributeDescription *ad = NULL;
+ AttributeDescription adstatic= { 0 };
+ const char *text = NULL;
+ struct berval attr, left, right;
+ int j;
+ int len;
+
+ for ( j = 0; acl_get_part( &bv, j, ',', &attr ) >= 0; j++ )
+ {
+ ad = NULL;
+ text = NULL;
+ /* openldap 2.1 aci compatibility [entry] -> entry */
+ if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_ENTRY ] ) == 0 ) {
+ ad = &adstatic;
+ adstatic.ad_cname = aci_bv[ ACI_BV_ENTRY ];
+
+ /* openldap 2.1 aci compatibility [children] -> children */
+ } else if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_CHILDREN ] ) == 0 ) {
+ ad = &adstatic;
+ adstatic.ad_cname = aci_bv[ ACI_BV_CHILDREN ];
+
+ /* openldap 2.1 aci compatibility [all] -> only [all] */
+ } else if ( ber_bvstrcasecmp( &attr, &aci_bv[ ACI_BV_BR_ALL ] ) == 0 ) {
+ ber_memfree_x( nattrs.bv_val, ctx );
+ nattrs = aci_bv[ ACI_BV_BR_ALL ];
+ freenattrs = 0;
+ break;
+
+ } else if ( acl_get_part( &attr, 0, '=', &left ) < 0
+ || acl_get_part( &attr, 1, '=', &right ) < 0 )
+ {
+ if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS )
+ {
+ ber_memfree_x( nattrs.bv_val, ctx );
+ Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: unknown attribute: '%s'\n", attr.bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ } else {
+ if ( slap_bv2ad( &left, &ad, &text ) != LDAP_SUCCESS )
+ {
+ ber_memfree_x( nattrs.bv_val, ctx );
+ Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: unknown attribute: '%s'\n", left.bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+
+ len = nattrs.bv_len + ( !BER_BVISEMPTY( &nattrs ) ? STRLENOF( "," ) : 0 )
+ + ad->ad_cname.bv_len;
+ nattrs.bv_val = slap_sl_realloc( nattrs.bv_val, len + 1, ctx );
+ ptr = &nattrs.bv_val[ nattrs.bv_len ];
+ if ( !BER_BVISEMPTY( &nattrs ) ) {
+ *ptr++ = ',';
+ }
+ ptr = lutil_strncopy( ptr, ad->ad_cname.bv_val, ad->ad_cname.bv_len );
+ ptr[ 0 ] = '\0';
+ nattrs.bv_len = len;
+ }
+
+ }
+
+ naction->bv_val = slap_sl_realloc( naction->bv_val,
+ naction->bv_len + STRLENOF( ";" )
+ + perms.bv_len + STRLENOF( ";" )
+ + nattrs.bv_len + 1,
+ ctx );
+
+ ptr = &naction->bv_val[ naction->bv_len ];
+ ptr[ 0 ] = ';';
+ ptr++;
+ ptr = lutil_strncopy( ptr, perms.bv_val, perms.bv_len );
+ ptr[ 0 ] = ';';
+ ptr++;
+ ptr = lutil_strncopy( ptr, nattrs.bv_val, nattrs.bv_len );
+ ptr[ 0 ] = '\0';
+ naction->bv_len += STRLENOF( ";" ) + perms.bv_len
+ + STRLENOF( ";" ) + nattrs.bv_len;
+ if ( freenattrs ) {
+ ber_memfree_x( nattrs.bv_val, ctx );
+ }
+ }
+ }
+
+ /* perms;attr go in pairs */
+ if ( i > 1 && ( i & 1 ) ) {
+ return LDAP_SUCCESS;
+
+ } else {
+ Debug( LDAP_DEBUG_ACL, "aciNormalizeRight: perms:attr need to be pairs in '%s'\n", action->bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+}
+
+static int
+OpenLDAPaciValidateRights(
+ struct berval *actions )
+
+{
+ struct berval bv = BER_BVNULL;
+ int i;
+
+ for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
+ if ( OpenLDAPaciValidateRight( &bv ) != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+OpenLDAPaciNormalizeRights(
+ struct berval *actions,
+ struct berval *nactions,
+ void *ctx )
+
+{
+ struct berval bv = BER_BVNULL;
+ int i;
+
+ BER_BVZERO( nactions );
+ for ( i = 0; acl_get_part( actions, i, '$', &bv ) >= 0; i++ ) {
+ int rc;
+ struct berval nbv;
+
+ rc = OpenLDAPaciNormalizeRight( &bv, &nbv, ctx );
+ if ( rc != LDAP_SUCCESS ) {
+ ber_memfree_x( nactions->bv_val, ctx );
+ BER_BVZERO( nactions );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( i == 0 ) {
+ *nactions = nbv;
+
+ } else {
+ nactions->bv_val = slap_sl_realloc( nactions->bv_val,
+ nactions->bv_len + STRLENOF( "$" )
+ + nbv.bv_len + 1,
+ ctx );
+ nactions->bv_val[ nactions->bv_len ] = '$';
+ AC_MEMCPY( &nactions->bv_val[ nactions->bv_len + 1 ],
+ nbv.bv_val, nbv.bv_len + 1 );
+ ber_memfree_x( nbv.bv_val, ctx );
+ nactions->bv_len += STRLENOF( "$" ) + nbv.bv_len;
+ }
+ BER_BVZERO( &nbv );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static const struct berval *OpenLDAPaciscopes[] = {
+ &aci_bv[ ACI_BV_ENTRY ],
+ &aci_bv[ ACI_BV_CHILDREN ],
+ &aci_bv[ ACI_BV_SUBTREE ],
+
+ NULL
+};
+
+static const struct berval *OpenLDAPacitypes[] = {
+ /* DN-valued */
+ &aci_bv[ ACI_BV_GROUP ],
+ &aci_bv[ ACI_BV_ROLE ],
+
+/* set to one past the last DN-valued type with options (/) */
+#define LAST_OPTIONAL 2
+
+ &aci_bv[ ACI_BV_ACCESS_ID ],
+ &aci_bv[ ACI_BV_SUBTREE ],
+ &aci_bv[ ACI_BV_ONELEVEL ],
+ &aci_bv[ ACI_BV_CHILDREN ],
+
+/* set to one past the last DN-valued type */
+#define LAST_DNVALUED 6
+
+ /* non DN-valued */
+ &aci_bv[ ACI_BV_DNATTR ],
+ &aci_bv[ ACI_BV_PUBLIC ],
+ &aci_bv[ ACI_BV_USERS ],
+ &aci_bv[ ACI_BV_SELF ],
+ &aci_bv[ ACI_BV_SET ],
+ &aci_bv[ ACI_BV_SET_REF ],
+
+ NULL
+};
+
+static int
+OpenLDAPaciValidate(
+ Syntax *syntax,
+ struct berval *val )
+{
+ struct berval oid = BER_BVNULL,
+ scope = BER_BVNULL,
+ rights = BER_BVNULL,
+ type = BER_BVNULL,
+ subject = BER_BVNULL;
+ int idx;
+ int rc;
+
+ if ( BER_BVISEMPTY( val ) ) {
+ Debug( LDAP_DEBUG_ACL, "aciValidatet: value is empty\n" );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* oid */
+ if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
+ numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
+ {
+ /* NOTE: the numericoidValidate() is rather pedantic;
+ * I'd replace it with X-ORDERED VALUES so that
+ * it's guaranteed values are maintained and used
+ * in the desired order */
+ Debug( LDAP_DEBUG_ACL, "aciValidate: invalid oid '%s'\n", oid.bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* scope */
+ if ( acl_get_part( val, 1, '#', &scope ) < 0 ||
+ bv_getcaseidx( &scope, OpenLDAPaciscopes ) == -1 )
+ {
+ Debug( LDAP_DEBUG_ACL, "aciValidate: invalid scope '%s'\n", scope.bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* rights */
+ if ( acl_get_part( val, 2, '#', &rights ) < 0 ||
+ OpenLDAPaciValidateRights( &rights ) != LDAP_SUCCESS )
+ {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* type */
+ if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
+ Debug( LDAP_DEBUG_ACL, "aciValidate: missing type in '%s'\n", val->bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+ idx = bv_getcaseidx( &type, OpenLDAPacitypes );
+ if ( idx == -1 ) {
+ struct berval isgr;
+
+ if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
+ Debug( LDAP_DEBUG_ACL, "aciValidate: invalid type '%s'\n", type.bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
+ if ( idx == -1 || idx >= LAST_OPTIONAL ) {
+ Debug( LDAP_DEBUG_ACL, "aciValidate: invalid type '%s'\n", isgr.bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ /* subject */
+ bv_get_tail( val, &type, &subject );
+ if ( subject.bv_val[ 0 ] != '#' ) {
+ Debug( LDAP_DEBUG_ACL, "aciValidate: missing subject in '%s'\n", val->bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( idx >= LAST_DNVALUED ) {
+ if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+
+ rc = slap_bv2ad( &subject, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ACL, "aciValidate: unknown dn attribute '%s'\n", subject.bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
+ /* FIXME: allow nameAndOptionalUID? */
+ Debug( LDAP_DEBUG_ACL, "aciValidate: wrong syntax for dn attribute '%s'\n", subject.bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ /* not a DN */
+ return LDAP_SUCCESS;
+
+ } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
+ || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
+ {
+ /* do {group|role}/oc/at check */
+ struct berval ocbv = BER_BVNULL,
+ atbv = BER_BVNULL;
+
+ ocbv.bv_val = ber_bvchr( &type, '/' );
+ if ( ocbv.bv_val != NULL ) {
+ ocbv.bv_val++;
+ ocbv.bv_len = type.bv_len
+ - ( ocbv.bv_val - type.bv_val );
+
+ atbv.bv_val = ber_bvchr( &ocbv, '/' );
+ if ( atbv.bv_val != NULL ) {
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+ int rc;
+
+ atbv.bv_val++;
+ atbv.bv_len = type.bv_len
+ - ( atbv.bv_val - type.bv_val );
+ ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
+
+ rc = slap_bv2ad( &atbv, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ACL, "aciValidate: unknown group attribute '%s'\n", atbv.bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ if ( oc_bvfind( &ocbv ) == NULL ) {
+ Debug( LDAP_DEBUG_ACL, "aciValidate: unknown group '%s'\n", ocbv.bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+ }
+
+ if ( BER_BVISEMPTY( &subject ) ) {
+ /* empty DN invalid */
+ Debug( LDAP_DEBUG_ACL, "aciValidate: missing dn in '%s'\n", val->bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ subject.bv_val++;
+ subject.bv_len--;
+
+ /* FIXME: pass DN syntax? */
+ rc = dnValidate( NULL, &subject );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ACL, "aciValidate: invalid dn '%s'\n", subject.bv_val );
+ }
+ return rc;
+}
+
+static int
+OpenLDAPaciPrettyNormal(
+ struct berval *val,
+ struct berval *out,
+ void *ctx,
+ int normalize )
+{
+ struct berval oid = BER_BVNULL,
+ scope = BER_BVNULL,
+ rights = BER_BVNULL,
+ nrights = BER_BVNULL,
+ type = BER_BVNULL,
+ ntype = BER_BVNULL,
+ subject = BER_BVNULL,
+ nsubject = BER_BVNULL;
+ int idx,
+ rc = LDAP_SUCCESS,
+ freesubject = 0,
+ freetype = 0;
+ char *ptr;
+
+ BER_BVZERO( out );
+
+ if ( BER_BVISEMPTY( val ) ) {
+ Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: value is empty\n" );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* oid: if valid, it's already normalized */
+ if ( acl_get_part( val, 0, '#', &oid ) < 0 ||
+ numericoidValidate( NULL, &oid ) != LDAP_SUCCESS )
+ {
+ Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid oid '%s'\n", oid.bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* scope: normalize by replacing with OpenLDAPaciscopes */
+ if ( acl_get_part( val, 1, '#', &scope ) < 0 ) {
+ Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing scope in '%s'\n", val->bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+ idx = bv_getcaseidx( &scope, OpenLDAPaciscopes );
+ if ( idx == -1 ) {
+ Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid scope '%s'\n", scope.bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+ scope = *OpenLDAPaciscopes[ idx ];
+
+ /* rights */
+ if ( acl_get_part( val, 2, '#', &rights ) < 0 ) {
+ Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing rights in '%s'\n", val->bv_val );
+ return LDAP_INVALID_SYNTAX;
+ }
+ if ( OpenLDAPaciNormalizeRights( &rights, &nrights, ctx )
+ != LDAP_SUCCESS )
+ {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* type */
+ if ( acl_get_part( val, 3, '#', &type ) < 0 ) {
+ Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing type in '%s'\n", val->bv_val );
+ rc = LDAP_INVALID_SYNTAX;
+ goto cleanup;
+ }
+ idx = bv_getcaseidx( &type, OpenLDAPacitypes );
+ if ( idx == -1 ) {
+ struct berval isgr;
+
+ if ( acl_get_part( &type, 0, '/', &isgr ) < 0 ) {
+ Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid type '%s'\n", type.bv_val );
+ rc = LDAP_INVALID_SYNTAX;
+ goto cleanup;
+ }
+
+ idx = bv_getcaseidx( &isgr, OpenLDAPacitypes );
+ if ( idx == -1 || idx >= LAST_OPTIONAL ) {
+ Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid type '%s'\n", isgr.bv_val );
+ rc = LDAP_INVALID_SYNTAX;
+ goto cleanup;
+ }
+ }
+ ntype = *OpenLDAPacitypes[ idx ];
+
+ /* subject */
+ bv_get_tail( val, &type, &subject );
+
+ if ( BER_BVISEMPTY( &subject ) || subject.bv_val[ 0 ] != '#' ) {
+ Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: missing subject in '%s'\n", val->bv_val );
+ rc = LDAP_INVALID_SYNTAX;
+ goto cleanup;
+ }
+
+ subject.bv_val++;
+ subject.bv_len--;
+
+ if ( idx < LAST_DNVALUED ) {
+ /* FIXME: pass DN syntax? */
+ if ( normalize ) {
+ rc = dnNormalize( 0, NULL, NULL,
+ &subject, &nsubject, ctx );
+ } else {
+ rc = dnPretty( NULL, &subject, &nsubject, ctx );
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ freesubject = 1;
+
+ } else {
+ Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid subject dn '%s'\n", subject.bv_val );
+ goto cleanup;
+ }
+
+ if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_GROUP ]
+ || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_ROLE ] )
+ {
+ /* do {group|role}/oc/at check */
+ struct berval ocbv = BER_BVNULL,
+ atbv = BER_BVNULL;
+
+ ocbv.bv_val = ber_bvchr( &type, '/' );
+ if ( ocbv.bv_val != NULL ) {
+ ObjectClass *oc = NULL;
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+ int rc;
+ struct berval bv;
+
+ bv.bv_len = ntype.bv_len;
+
+ ocbv.bv_val++;
+ ocbv.bv_len = type.bv_len - ( ocbv.bv_val - type.bv_val );
+
+ atbv.bv_val = ber_bvchr( &ocbv, '/' );
+ if ( atbv.bv_val != NULL ) {
+ atbv.bv_val++;
+ atbv.bv_len = type.bv_len
+ - ( atbv.bv_val - type.bv_val );
+ ocbv.bv_len = atbv.bv_val - ocbv.bv_val - 1;
+
+ rc = slap_bv2ad( &atbv, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: unknown group attribute '%s'\n", atbv.bv_val );
+ rc = LDAP_INVALID_SYNTAX;
+ goto cleanup;
+ }
+
+ bv.bv_len += STRLENOF( "/" ) + ad->ad_cname.bv_len;
+ }
+
+ oc = oc_bvfind( &ocbv );
+ if ( oc == NULL ) {
+ Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: invalid group '%s'\n", ocbv.bv_val );
+ rc = LDAP_INVALID_SYNTAX;
+ goto cleanup;
+ }
+
+ bv.bv_len += STRLENOF( "/" ) + oc->soc_cname.bv_len;
+ bv.bv_val = slap_sl_malloc( bv.bv_len + 1, ctx );
+
+ ptr = bv.bv_val;
+ ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
+ ptr[ 0 ] = '/';
+ ptr++;
+ ptr = lutil_strncopy( ptr,
+ oc->soc_cname.bv_val,
+ oc->soc_cname.bv_len );
+ if ( ad != NULL ) {
+ ptr[ 0 ] = '/';
+ ptr++;
+ ptr = lutil_strncopy( ptr,
+ ad->ad_cname.bv_val,
+ ad->ad_cname.bv_len );
+ }
+ ptr[ 0 ] = '\0';
+
+ ntype = bv;
+ freetype = 1;
+ }
+ }
+
+ } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_DNATTR ] ) {
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+ int rc;
+
+ rc = slap_bv2ad( &subject, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: unknown dn attribute '%s'\n", subject.bv_val );
+ rc = LDAP_INVALID_SYNTAX;
+ goto cleanup;
+ }
+
+ if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
+ /* FIXME: allow nameAndOptionalUID? */
+ Debug( LDAP_DEBUG_ACL, "aciPrettyNormal: wrong syntax for dn attribute '%s'\n", subject.bv_val );
+ rc = LDAP_INVALID_SYNTAX;
+ goto cleanup;
+ }
+
+ nsubject = ad->ad_cname;
+
+ } else if ( OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_SET ]
+ || OpenLDAPacitypes[ idx ] == &aci_bv[ ACI_BV_SET_REF ] )
+ {
+ /* NOTE: dunno how to normalize it... */
+ nsubject = subject;
+ }
+
+
+ out->bv_len =
+ oid.bv_len + STRLENOF( "#" )
+ + scope.bv_len + STRLENOF( "#" )
+ + nrights.bv_len + STRLENOF( "#" )
+ + ntype.bv_len + STRLENOF( "#" )
+ + nsubject.bv_len;
+
+ out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
+ ptr = lutil_strncopy( out->bv_val, oid.bv_val, oid.bv_len );
+ ptr[ 0 ] = '#';
+ ptr++;
+ ptr = lutil_strncopy( ptr, scope.bv_val, scope.bv_len );
+ ptr[ 0 ] = '#';
+ ptr++;
+ ptr = lutil_strncopy( ptr, nrights.bv_val, nrights.bv_len );
+ ptr[ 0 ] = '#';
+ ptr++;
+ ptr = lutil_strncopy( ptr, ntype.bv_val, ntype.bv_len );
+ ptr[ 0 ] = '#';
+ ptr++;
+ if ( !BER_BVISNULL( &nsubject ) ) {
+ ptr = lutil_strncopy( ptr, nsubject.bv_val, nsubject.bv_len );
+ }
+ ptr[ 0 ] = '\0';
+
+cleanup:;
+ if ( freesubject ) {
+ ber_memfree_x( nsubject.bv_val, ctx );
+ }
+
+ if ( freetype ) {
+ ber_memfree_x( ntype.bv_val, ctx );
+ }
+
+ if ( !BER_BVISNULL( &nrights ) ) {
+ ber_memfree_x( nrights.bv_val, ctx );
+ }
+
+ return rc;
+}
+
+static int
+OpenLDAPaciPretty(
+ Syntax *syntax,
+ struct berval *val,
+ struct berval *out,
+ void *ctx )
+{
+ return OpenLDAPaciPrettyNormal( val, out, ctx, 0 );
+}
+
+static int
+OpenLDAPaciNormalize(
+ slap_mask_t use,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *out,
+ void *ctx )
+{
+ return OpenLDAPaciPrettyNormal( val, out, ctx, 1 );
+}
+
+#if SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC
+/*
+ * FIXME: need config and Makefile.am code to ease building
+ * as dynamic module
+ */
+int
+init_module( int argc, char *argv[] )
+{
+ return dynacl_aci_init();
+}
+#endif /* SLAPD_ACI_ENABLED == SLAPD_MOD_DYNAMIC */
+
+#endif /* SLAPD_ACI_ENABLED */
+
diff --git a/servers/slapd/acl.c b/servers/slapd/acl.c
new file mode 100644
index 0000000..4cfa172
--- /dev/null
+++ b/servers/slapd/acl.c
@@ -0,0 +1,2687 @@
+/* acl.c - routines to parse and check acl's */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/regex.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "sets.h"
+#include "lber_pvt.h"
+#include "lutil.h"
+
+#define ACL_BUF_SIZE 1024 /* use most appropriate size */
+
+static const struct berval acl_bv_ip_eq = BER_BVC( "IP=" );
+#ifdef LDAP_PF_INET6
+static const struct berval acl_bv_ipv6_eq = BER_BVC( "IP=[" );
+#endif /* LDAP_PF_INET6 */
+#ifdef LDAP_PF_LOCAL
+static const struct berval acl_bv_path_eq = BER_BVC("PATH=");
+#endif /* LDAP_PF_LOCAL */
+
+static AccessControl * slap_acl_get(
+ AccessControl *ac, int *count,
+ Operation *op, Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ AclRegexMatches *matches,
+ slap_mask_t *mask,
+ AccessControlState *state );
+
+static slap_control_t slap_acl_mask(
+ AccessControl *ac,
+ AccessControl *prev,
+ slap_mask_t *mask,
+ Operation *op, Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ AclRegexMatches *matches,
+ int count,
+ AccessControlState *state,
+ slap_access_t access );
+
+static int regex_matches(
+ struct berval *pat, char *str,
+ struct berval *dn_matches, struct berval *val_matches,
+ AclRegexMatches *matches);
+
+typedef struct AclSetCookie {
+ SetCookie asc_cookie;
+#define asc_op asc_cookie.set_op
+ Entry *asc_e;
+} AclSetCookie;
+
+
+SLAP_SET_GATHER acl_set_gather;
+SLAP_SET_GATHER acl_set_gather2;
+
+/*
+ * access_allowed - check whether op->o_ndn is allowed the requested access
+ * to entry e, attribute attr, value val. if val is null, access to
+ * the whole attribute is assumed (all values).
+ *
+ * This routine loops through all access controls and calls
+ * slap_acl_mask() on each applicable access control.
+ * The loop exits when a definitive answer is reached or
+ * or no more controls remain.
+ *
+ * returns:
+ * 0 access denied
+ * 1 access granted
+ *
+ * Notes:
+ * - can be legally called with op == NULL
+ * - can be legally called with op->o_bd == NULL
+ */
+
+int
+slap_access_always_allowed(
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ slap_access_t access,
+ AccessControlState *state,
+ slap_mask_t *maskp )
+{
+ assert( maskp != NULL );
+
+ /* assign all */
+ ACL_LVL_ASSIGN_MANAGE( *maskp );
+
+ return 1;
+}
+
+#define MATCHES_DNMAXCOUNT(m) \
+ ( sizeof ( (m)->dn_data ) / sizeof( *(m)->dn_data ) )
+#define MATCHES_VALMAXCOUNT(m) \
+ ( sizeof ( (m)->val_data ) / sizeof( *(m)->val_data ) )
+#define MATCHES_MEMSET(m) do { \
+ memset( (m)->dn_data, '\0', sizeof( (m)->dn_data ) ); \
+ memset( (m)->val_data, '\0', sizeof( (m)->val_data ) ); \
+ (m)->dn_count = MATCHES_DNMAXCOUNT( (m) ); \
+ (m)->val_count = MATCHES_VALMAXCOUNT( (m) ); \
+} while ( 0 /* CONSTCOND */ )
+
+int
+slap_access_allowed(
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ slap_access_t access,
+ AccessControlState *state,
+ slap_mask_t *maskp )
+{
+ int ret = 1;
+ int count;
+ AccessControl *a, *prev;
+
+#ifdef LDAP_DEBUG
+ char accessmaskbuf[ACCESSMASK_MAXLEN];
+#endif
+ slap_mask_t mask;
+ slap_control_t control;
+ slap_access_t access_level;
+ const char *attr;
+ AclRegexMatches matches;
+ AccessControlState acl_state = ACL_STATE_INIT;
+ static AccessControlState state_init = ACL_STATE_INIT;
+
+ assert( op != NULL );
+ assert( e != NULL );
+ assert( desc != NULL );
+ assert( maskp != NULL );
+
+ access_level = ACL_LEVEL( access );
+ attr = desc->ad_cname.bv_val;
+
+ assert( attr != NULL );
+
+ ACL_INIT( mask );
+
+ /* grant database root access */
+ if ( be_isroot( op ) ) {
+ Debug( LDAP_DEBUG_ACL, "<= root access granted\n" );
+ mask = ACL_LVL_MANAGE;
+ goto done;
+ }
+
+ /*
+ * no-user-modification operational attributes are ignored
+ * by ACL_WRITE checking as any found here are not provided
+ * by the user
+ *
+ * NOTE: but they are not ignored for ACL_MANAGE, because
+ * if we get here it means a non-root user is trying to
+ * manage data, so we need to check its privileges.
+ */
+ if ( access_level == ACL_WRITE_
+ && is_at_no_user_mod( desc->ad_type )
+ && desc != slap_schema.si_ad_entry
+ && desc != slap_schema.si_ad_children )
+ {
+ Debug( LDAP_DEBUG_ACL, "NoUserMod Operational attribute:"
+ " %s access granted\n",
+ attr );
+ goto done;
+ }
+
+ /* use backend default access if no backend acls */
+ if ( op->o_bd->be_acl == NULL && frontendDB->be_acl == NULL ) {
+ int i;
+
+ Debug( LDAP_DEBUG_ACL,
+ "=> slap_access_allowed: backend default %s "
+ "access %s to \"%s\"\n",
+ access2str( access ),
+ op->o_bd->be_dfltaccess >= access_level ? "granted" : "denied",
+ op->o_dn.bv_val ? op->o_dn.bv_val : "(anonymous)" );
+ ret = op->o_bd->be_dfltaccess >= access_level;
+
+ mask = ACL_PRIV_LEVEL;
+ for ( i = ACL_NONE; i <= op->o_bd->be_dfltaccess; i++ ) {
+ ACL_PRIV_SET( mask, ACL_ACCESS2PRIV( i ) );
+ }
+
+ goto done;
+ }
+
+ ret = 0;
+ control = ACL_BREAK;
+
+ if ( state == NULL )
+ state = &acl_state;
+ if ( state->as_desc == desc &&
+ state->as_access == access &&
+ state->as_vd_acl_present )
+ {
+ a = state->as_vd_acl;
+ count = state->as_vd_acl_count;
+ if ( state->as_fe_done )
+ state->as_fe_done--;
+ ACL_PRIV_ASSIGN( mask, state->as_vd_mask );
+ } else {
+ *state = state_init;
+
+ a = NULL;
+ count = 0;
+ ACL_PRIV_ASSIGN( mask, *maskp );
+ }
+
+ MATCHES_MEMSET( &matches );
+ prev = a;
+
+ while ( ( a = slap_acl_get( a, &count, op, e, desc, val,
+ &matches, &mask, state ) ) != NULL )
+ {
+ int i;
+ int dnmaxcount = MATCHES_DNMAXCOUNT( &matches );
+ int valmaxcount = MATCHES_VALMAXCOUNT( &matches );
+ regmatch_t *dn_data = matches.dn_data;
+ regmatch_t *val_data = matches.val_data;
+
+ /* DN matches */
+ for ( i = 0; i < dnmaxcount && dn_data[i].rm_eo > 0; i++ ) {
+ char *data = e->e_ndn;
+
+ Debug( LDAP_DEBUG_ACL, "=> match[dn%d]: %d %d ", i,
+ (int)dn_data[i].rm_so,
+ (int)dn_data[i].rm_eo );
+ if ( dn_data[i].rm_so <= dn_data[0].rm_eo ) {
+ int n;
+ for ( n = dn_data[i].rm_so;
+ n < dn_data[i].rm_eo; n++ ) {
+ Debug( LDAP_DEBUG_ACL, "%c",
+ data[n] );
+ }
+ }
+ Debug( LDAP_DEBUG_ACL, "\n" );
+ }
+
+ /* val matches */
+ for ( i = 0; i < valmaxcount && val_data[i].rm_eo > 0; i++ ) {
+ char *data = val->bv_val;
+
+ Debug( LDAP_DEBUG_ACL, "=> match[val%d]: %d %d ", i,
+ (int)val_data[i].rm_so,
+ (int)val_data[i].rm_eo );
+ if ( val_data[i].rm_so <= val_data[0].rm_eo ) {
+ int n;
+ for ( n = val_data[i].rm_so;
+ n < val_data[i].rm_eo; n++ ) {
+ Debug( LDAP_DEBUG_ACL, "%c",
+ data[n] );
+ }
+ }
+ Debug( LDAP_DEBUG_ACL, "\n" );
+ }
+
+ control = slap_acl_mask( a, prev, &mask, op,
+ e, desc, val, &matches, count, state, access );
+
+ if ( control != ACL_BREAK ) {
+ break;
+ }
+
+ MATCHES_MEMSET( &matches );
+ prev = a;
+ }
+
+ if ( ACL_IS_INVALID( mask ) ) {
+ Debug( LDAP_DEBUG_ACL,
+ "=> slap_access_allowed: \"%s\" (%s) invalid!\n",
+ e->e_dn, attr );
+ ACL_PRIV_ASSIGN( mask, *maskp );
+
+ } else if ( control == ACL_BREAK ) {
+ Debug( LDAP_DEBUG_ACL,
+ "=> slap_access_allowed: no more rules\n" );
+
+ goto done;
+ }
+
+ ret = ACL_GRANT( mask, access );
+
+ Debug( LDAP_DEBUG_ACL,
+ "=> slap_access_allowed: %s access %s by %s\n",
+ access2str( access ), ret ? "granted" : "denied",
+ accessmask2str( mask, accessmaskbuf, 1 ) );
+
+done:
+ ACL_PRIV_ASSIGN( *maskp, mask );
+ return ret;
+}
+
+int
+fe_access_allowed(
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ slap_access_t access,
+ AccessControlState *state,
+ slap_mask_t *maskp )
+{
+ BackendDB *be_orig;
+ int rc;
+
+ /*
+ * NOTE: control gets here if FIXME
+ * if an appropriate backend cannot be selected for the operation,
+ * we assume that the frontend should handle this
+ * FIXME: should select_backend() take care of this,
+ * and return frontendDB instead of NULL? maybe for some value
+ * of the flags?
+ */
+ be_orig = op->o_bd;
+
+ if ( op->o_bd == NULL ) {
+ op->o_bd = select_backend( &op->o_req_ndn, 0 );
+ if ( op->o_bd == NULL )
+ op->o_bd = frontendDB;
+ }
+ rc = slap_access_allowed( op, e, desc, val, access, state, maskp );
+ op->o_bd = be_orig;
+
+ return rc;
+}
+
+int
+access_allowed_mask(
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ slap_access_t access,
+ AccessControlState *state,
+ slap_mask_t *maskp )
+{
+ int ret = 1;
+ int be_null = 0;
+
+#ifdef LDAP_DEBUG
+ char accessmaskbuf[ACCESSMASK_MAXLEN];
+#endif
+ slap_mask_t mask;
+ slap_access_t access_level;
+ const char *attr;
+
+ assert( e != NULL );
+ assert( desc != NULL );
+
+ access_level = ACL_LEVEL( access );
+
+ assert( access_level > ACL_NONE );
+
+ ACL_INIT( mask );
+ if ( maskp ) ACL_INVALIDATE( *maskp );
+
+ attr = desc->ad_cname.bv_val;
+
+ assert( attr != NULL );
+
+ if ( op ) {
+ if ( op->o_acl_priv != ACL_NONE ) {
+ access = op->o_acl_priv;
+
+ } else if ( op->o_is_auth_check &&
+ ( access_level == ACL_SEARCH || access_level == ACL_READ ) )
+ {
+ access = ACL_AUTH;
+
+ } else if ( get_relax( op ) && access_level == ACL_WRITE_ &&
+ desc == slap_schema.si_ad_entry )
+ {
+ access = ACL_MANAGE;
+ }
+ }
+
+ if ( state != NULL ) {
+ if ( state->as_desc == desc &&
+ state->as_access == access &&
+ state->as_result != -1 &&
+ !state->as_vd_acl_present )
+ {
+ Debug( LDAP_DEBUG_ACL,
+ "=> access_allowed: result was in cache (%s)\n",
+ attr );
+ return state->as_result;
+ } else {
+ Debug( LDAP_DEBUG_ACL,
+ "=> access_allowed: result not in cache (%s)\n",
+ attr );
+ }
+ }
+
+ Debug( LDAP_DEBUG_ACL,
+ "=> access_allowed: %s access to \"%s\" \"%s\" requested\n",
+ access2str( access ), e->e_dn, attr );
+
+ if ( op == NULL ) {
+ /* no-op call */
+ goto done;
+ }
+
+ if ( op->o_bd == NULL ) {
+ op->o_bd = LDAP_STAILQ_FIRST( &backendDB );
+ be_null = 1;
+
+ /* FIXME: experimental; use first backend rules
+ * iff there is no global_acl (ITS#3100)
+ */
+ if ( frontendDB->be_acl != NULL ) {
+ op->o_bd = frontendDB;
+ }
+ }
+ assert( op->o_bd != NULL );
+
+ /* this is enforced in backend_add() */
+ if ( op->o_bd->bd_info->bi_access_allowed ) {
+ /* delegate to backend */
+ ret = op->o_bd->bd_info->bi_access_allowed( op, e,
+ desc, val, access, state, &mask );
+
+ } else {
+ /* use default (but pass through frontend
+ * for global ACL overlays) */
+ ret = frontendDB->bd_info->bi_access_allowed( op, e,
+ desc, val, access, state, &mask );
+ }
+
+ if ( !ret ) {
+ if ( ACL_IS_INVALID( mask ) ) {
+ Debug( LDAP_DEBUG_ACL,
+ "=> access_allowed: \"%s\" (%s) invalid!\n",
+ e->e_dn, attr );
+ ACL_INIT( mask );
+
+ } else {
+ Debug( LDAP_DEBUG_ACL,
+ "=> access_allowed: no more rules\n" );
+
+ goto done;
+ }
+ }
+
+ Debug( LDAP_DEBUG_ACL,
+ "=> access_allowed: %s access %s by %s\n",
+ access2str( access ), ret ? "granted" : "denied",
+ accessmask2str( mask, accessmaskbuf, 1 ) );
+
+done:
+ if ( state != NULL ) {
+ state->as_access = access;
+ state->as_result = ret;
+ state->as_desc = desc;
+ }
+ if ( be_null ) op->o_bd = NULL;
+ if ( maskp ) ACL_PRIV_ASSIGN( *maskp, mask );
+ return ret;
+}
+
+
+/*
+ * slap_acl_get - return the acl applicable to entry e, attribute
+ * attr. the acl returned is suitable for use in subsequent calls to
+ * acl_access_allowed().
+ */
+
+static AccessControl *
+slap_acl_get(
+ AccessControl *a,
+ int *count,
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ AclRegexMatches *matches,
+ slap_mask_t *mask,
+ AccessControlState *state )
+{
+ const char *attr;
+ ber_len_t dnlen;
+ AccessControl *prev;
+
+ assert( e != NULL );
+ assert( count != NULL );
+ assert( desc != NULL );
+ assert( state != NULL );
+
+ attr = desc->ad_cname.bv_val;
+
+ assert( attr != NULL );
+
+ if( a == NULL ) {
+ if( op->o_bd == NULL || op->o_bd->be_acl == NULL ) {
+ a = frontendDB->be_acl;
+ } else {
+ a = op->o_bd->be_acl;
+ }
+ prev = NULL;
+
+ assert( a != NULL );
+ if ( a == frontendDB->be_acl )
+ state->as_fe_done = 1;
+ } else {
+ prev = a;
+ a = a->acl_next;
+ }
+
+ dnlen = e->e_nname.bv_len;
+
+ retry:
+ for ( ; a != NULL; prev = a, a = a->acl_next ) {
+ (*count) ++;
+
+ if ( a != frontendDB->be_acl && state->as_fe_done )
+ state->as_fe_done++;
+
+ if ( a->acl_dn_pat.bv_len || ( a->acl_dn_style != ACL_STYLE_REGEX )) {
+ if ( a->acl_dn_style == ACL_STYLE_REGEX ) {
+ Debug( LDAP_DEBUG_ACL, "=> dnpat: [%d] %s nsub: %d\n",
+ *count, a->acl_dn_pat.bv_val, (int) a->acl_dn_re.re_nsub );
+ if ( regexec ( &a->acl_dn_re,
+ e->e_ndn,
+ matches->dn_count,
+ matches->dn_data, 0 ) )
+ continue;
+
+ } else {
+ ber_len_t patlen;
+
+ Debug( LDAP_DEBUG_ACL, "=> dn: [%d] %s\n",
+ *count, a->acl_dn_pat.bv_val );
+ patlen = a->acl_dn_pat.bv_len;
+ if ( dnlen < patlen )
+ continue;
+
+ if ( a->acl_dn_style == ACL_STYLE_BASE ) {
+ /* base dn -- entire object DN must match */
+ if ( dnlen != patlen )
+ continue;
+
+ } else if ( a->acl_dn_style == ACL_STYLE_ONE ) {
+ ber_len_t rdnlen = 0;
+ ber_len_t sep = 0;
+
+ if ( dnlen <= patlen )
+ continue;
+
+ if ( patlen > 0 ) {
+ if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
+ continue;
+ sep = 1;
+ }
+
+ rdnlen = dn_rdnlen( NULL, &e->e_nname );
+ if ( rdnlen + patlen + sep != dnlen )
+ continue;
+
+ } else if ( a->acl_dn_style == ACL_STYLE_SUBTREE ) {
+ if ( dnlen > patlen && !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
+ continue;
+
+ } else if ( a->acl_dn_style == ACL_STYLE_CHILDREN ) {
+ if ( dnlen <= patlen )
+ continue;
+ if ( !DN_SEPARATOR( e->e_ndn[dnlen - patlen - 1] ) )
+ continue;
+ }
+
+ if ( strcmp( a->acl_dn_pat.bv_val, e->e_ndn + dnlen - patlen ) != 0 )
+ continue;
+ }
+
+ Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] matched\n",
+ *count );
+ }
+
+ if ( a->acl_attrs && !ad_inlist( desc, a->acl_attrs ) ) {
+ matches->dn_data[0].rm_so = -1;
+ matches->dn_data[0].rm_eo = -1;
+ matches->val_data[0].rm_so = -1;
+ matches->val_data[0].rm_eo = -1;
+ continue;
+ }
+
+ /* Is this ACL only for a specific value? */
+ if ( a->acl_attrval.bv_val ) {
+ if ( val == NULL ) {
+ continue;
+ }
+
+ if ( !state->as_vd_acl_present ) {
+ state->as_vd_acl_present = 1;
+ state->as_vd_acl = prev;
+ state->as_vd_acl_count = *count - 1;
+ ACL_PRIV_ASSIGN ( state->as_vd_mask, *mask );
+ }
+
+ if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
+ Debug( LDAP_DEBUG_ACL,
+ "acl_get: valpat %s\n",
+ a->acl_attrval.bv_val );
+ if ( regexec ( &a->acl_attrval_re,
+ val->bv_val,
+ matches->val_count,
+ matches->val_data, 0 ) )
+ {
+ continue;
+ }
+
+ } else {
+ int match = 0;
+ const char *text;
+ Debug( LDAP_DEBUG_ACL,
+ "acl_get: val %s\n",
+ a->acl_attrval.bv_val );
+
+ if ( a->acl_attrs[0].an_desc->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName ) {
+ if (value_match( &match, desc,
+ a->acl_attrval_mr, 0,
+ val, &a->acl_attrval, &text ) != LDAP_SUCCESS ||
+ match )
+ continue;
+
+ } else {
+ ber_len_t patlen, vdnlen;
+
+ patlen = a->acl_attrval.bv_len;
+ vdnlen = val->bv_len;
+
+ if ( vdnlen < patlen )
+ continue;
+
+ if ( a->acl_attrval_style == ACL_STYLE_BASE ) {
+ if ( vdnlen > patlen )
+ continue;
+
+ } else if ( a->acl_attrval_style == ACL_STYLE_ONE ) {
+ ber_len_t rdnlen = 0;
+
+ if ( !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
+ continue;
+
+ rdnlen = dn_rdnlen( NULL, val );
+ if ( rdnlen + patlen + 1 != vdnlen )
+ continue;
+
+ } else if ( a->acl_attrval_style == ACL_STYLE_SUBTREE ) {
+ if ( vdnlen > patlen && !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
+ continue;
+
+ } else if ( a->acl_attrval_style == ACL_STYLE_CHILDREN ) {
+ if ( vdnlen <= patlen )
+ continue;
+
+ if ( !DN_SEPARATOR( val->bv_val[vdnlen - patlen - 1] ) )
+ continue;
+ }
+
+ if ( strcmp( a->acl_attrval.bv_val, val->bv_val + vdnlen - patlen ) )
+ continue;
+ }
+ }
+ }
+
+ if ( a->acl_filter != NULL ) {
+ ber_int_t rc = test_filter( NULL, e, a->acl_filter );
+ if ( rc != LDAP_COMPARE_TRUE ) {
+ continue;
+ }
+ }
+
+ Debug( LDAP_DEBUG_ACL, "=> acl_get: [%d] attr %s\n",
+ *count, attr );
+ return a;
+ }
+
+ if ( !state->as_fe_done ) {
+ state->as_fe_done = 1;
+ a = frontendDB->be_acl;
+ goto retry;
+ }
+
+ Debug( LDAP_DEBUG_ACL, "<= acl_get: done.\n" );
+ return( NULL );
+}
+
+/*
+ * Record value-dependent access control state
+ */
+#define ACL_RECORD_VALUE_STATE do { \
+ if( state && !state->as_vd_acl_present ) { \
+ state->as_vd_acl_present = 1; \
+ state->as_vd_acl = prev; \
+ state->as_vd_acl_count = count - 1; \
+ ACL_PRIV_ASSIGN( state->as_vd_mask, *mask ); \
+ } \
+ } while( 0 )
+
+static int
+acl_mask_dn(
+ Operation *op,
+ Entry *e,
+ struct berval *val,
+ AccessControl *a,
+ AclRegexMatches *matches,
+ slap_dn_access *bdn,
+ struct berval *opndn )
+{
+ /*
+ * if access applies to the entry itself, and the
+ * user is bound as somebody in the same namespace as
+ * the entry, OR the given dn matches the dn pattern
+ */
+ /*
+ * NOTE: styles "anonymous", "users" and "self"
+ * have been moved to enum slap_style_t, whose
+ * value is set in a_dn_style; however, the string
+ * is maintained in a_dn_pat.
+ */
+
+ if ( bdn->a_style == ACL_STYLE_ANONYMOUS ) {
+ if ( !BER_BVISEMPTY( opndn ) ) {
+ return 1;
+ }
+
+ } else if ( bdn->a_style == ACL_STYLE_USERS ) {
+ if ( BER_BVISEMPTY( opndn ) ) {
+ return 1;
+ }
+
+ } else if ( bdn->a_style == ACL_STYLE_SELF ) {
+ struct berval ndn, selfndn;
+ int level;
+
+ if ( BER_BVISEMPTY( opndn ) || BER_BVISNULL( &e->e_nname ) ) {
+ return 1;
+ }
+
+ level = bdn->a_self_level;
+ if ( level < 0 ) {
+ selfndn = *opndn;
+ ndn = e->e_nname;
+ level = -level;
+
+ } else {
+ ndn = *opndn;
+ selfndn = e->e_nname;
+ }
+
+ for ( ; level > 0; level-- ) {
+ if ( BER_BVISEMPTY( &ndn ) ) {
+ break;
+ }
+ dnParent( &ndn, &ndn );
+ }
+
+ if ( BER_BVISEMPTY( &ndn ) || !dn_match( &ndn, &selfndn ) )
+ {
+ return 1;
+ }
+
+ } else if ( bdn->a_style == ACL_STYLE_REGEX ) {
+ if ( !ber_bvccmp( &bdn->a_pat, '*' ) ) {
+ AclRegexMatches tmp_matches,
+ *tmp_matchesp = &tmp_matches;
+ int rc = 0;
+ regmatch_t *tmp_data;
+
+ MATCHES_MEMSET( &tmp_matches );
+ tmp_data = &tmp_matches.dn_data[0];
+
+ if ( a->acl_attrval_style == ACL_STYLE_REGEX )
+ tmp_matchesp = matches;
+ else switch ( a->acl_dn_style ) {
+ case ACL_STYLE_REGEX:
+ if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
+ tmp_matchesp = matches;
+ break;
+ }
+ /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
+
+ case ACL_STYLE_BASE:
+ tmp_data[0].rm_so = 0;
+ tmp_data[0].rm_eo = e->e_nname.bv_len;
+ tmp_matches.dn_count = 1;
+ break;
+
+ case ACL_STYLE_ONE:
+ case ACL_STYLE_SUBTREE:
+ case ACL_STYLE_CHILDREN:
+ tmp_data[0].rm_so = 0;
+ tmp_data[0].rm_eo = e->e_nname.bv_len;
+ tmp_data[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
+ tmp_data[1].rm_eo = e->e_nname.bv_len;
+ tmp_matches.dn_count = 2;
+ break;
+
+ default:
+ /* error */
+ rc = 1;
+ break;
+ }
+
+ if ( rc ) {
+ return 1;
+ }
+
+ if ( !regex_matches( &bdn->a_pat, opndn->bv_val,
+ &e->e_nname, NULL, tmp_matchesp ) )
+ {
+ return 1;
+ }
+ }
+
+ } else {
+ struct berval pat;
+ ber_len_t patlen, odnlen;
+ int got_match = 0;
+
+ if ( e->e_dn == NULL )
+ return 1;
+
+ if ( bdn->a_expand ) {
+ struct berval bv;
+ char buf[ACL_BUF_SIZE];
+
+ AclRegexMatches tmp_matches,
+ *tmp_matchesp = &tmp_matches;
+ int rc = 0;
+ regmatch_t *tmp_data;
+
+ MATCHES_MEMSET( &tmp_matches );
+ tmp_data = &tmp_matches.dn_data[0];
+
+ bv.bv_len = sizeof( buf ) - 1;
+ bv.bv_val = buf;
+
+ /* Expand value regex */
+ if ( a->acl_attrval_style == ACL_STYLE_REGEX )
+ tmp_matchesp = matches;
+ else switch ( a->acl_dn_style ) {
+ case ACL_STYLE_REGEX:
+ if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
+ tmp_matchesp = matches;
+ break;
+ }
+ /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
+
+ case ACL_STYLE_BASE:
+ tmp_data[0].rm_so = 0;
+ tmp_data[0].rm_eo = e->e_nname.bv_len;
+ tmp_matches.dn_count = 1;
+ break;
+
+ case ACL_STYLE_ONE:
+ case ACL_STYLE_SUBTREE:
+ case ACL_STYLE_CHILDREN:
+ tmp_data[0].rm_so = 0;
+ tmp_data[0].rm_eo = e->e_nname.bv_len;
+ tmp_data[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
+ tmp_data[1].rm_eo = e->e_nname.bv_len;
+ tmp_matches.dn_count = 2;
+ break;
+
+ default:
+ /* error */
+ rc = 1;
+ break;
+ }
+
+ if ( rc ) {
+ return 1;
+ }
+
+ if ( acl_string_expand( &bv, &bdn->a_pat,
+ &e->e_nname,
+ val, tmp_matchesp ) )
+ {
+ return 1;
+ }
+
+ if ( dnNormalize(0, NULL, NULL, &bv,
+ &pat, op->o_tmpmemctx )
+ != LDAP_SUCCESS )
+ {
+ /* did not expand to a valid dn */
+ return 1;
+ }
+
+ } else {
+ pat = bdn->a_pat;
+ }
+
+ patlen = pat.bv_len;
+ odnlen = opndn->bv_len;
+ if ( odnlen < patlen ) {
+ goto dn_match_cleanup;
+
+ }
+
+ if ( bdn->a_style == ACL_STYLE_BASE ) {
+ /* base dn -- entire object DN must match */
+ if ( odnlen != patlen ) {
+ goto dn_match_cleanup;
+ }
+
+ } else if ( bdn->a_style == ACL_STYLE_ONE ) {
+ ber_len_t rdnlen = 0;
+
+ if ( odnlen <= patlen ) {
+ goto dn_match_cleanup;
+ }
+
+ if ( !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) {
+ goto dn_match_cleanup;
+ }
+
+ rdnlen = dn_rdnlen( NULL, opndn );
+ if ( rdnlen - ( odnlen - patlen - 1 ) != 0 ) {
+ goto dn_match_cleanup;
+ }
+
+ } else if ( bdn->a_style == ACL_STYLE_SUBTREE ) {
+ if ( odnlen > patlen && !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) {
+ goto dn_match_cleanup;
+ }
+
+ } else if ( bdn->a_style == ACL_STYLE_CHILDREN ) {
+ if ( odnlen <= patlen ) {
+ goto dn_match_cleanup;
+ }
+
+ if ( !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) ) {
+ goto dn_match_cleanup;
+ }
+
+ } else if ( bdn->a_style == ACL_STYLE_LEVEL ) {
+ int level = bdn->a_level;
+ struct berval ndn;
+
+ if ( odnlen <= patlen ) {
+ goto dn_match_cleanup;
+ }
+
+ if ( level > 0 && !DN_SEPARATOR( opndn->bv_val[odnlen - patlen - 1] ) )
+ {
+ goto dn_match_cleanup;
+ }
+
+ ndn = *opndn;
+ for ( ; level > 0; level-- ) {
+ if ( BER_BVISEMPTY( &ndn ) ) {
+ goto dn_match_cleanup;
+ }
+ dnParent( &ndn, &ndn );
+ if ( ndn.bv_len < patlen ) {
+ goto dn_match_cleanup;
+ }
+ }
+
+ if ( ndn.bv_len != patlen ) {
+ goto dn_match_cleanup;
+ }
+ }
+
+ got_match = !strcmp( pat.bv_val, &opndn->bv_val[ odnlen - patlen ] );
+
+dn_match_cleanup:;
+ if ( pat.bv_val != bdn->a_pat.bv_val ) {
+ slap_sl_free( pat.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( !got_match ) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+acl_mask_dnattr(
+ Operation *op,
+ Entry *e,
+ struct berval *val,
+ AccessControl *a,
+ int count,
+ AccessControlState *state,
+ slap_mask_t *mask,
+ slap_dn_access *bdn,
+ struct berval *opndn )
+{
+ Attribute *at;
+ struct berval bv;
+ int rc, match = 0;
+ const char *text;
+ const char *attr = bdn->a_at->ad_cname.bv_val;
+
+ assert( attr != NULL );
+
+ if ( BER_BVISEMPTY( opndn ) ) {
+ return 1;
+ }
+
+ Debug( LDAP_DEBUG_ACL, "<= check a_dn_at: %s\n", attr );
+ bv = *opndn;
+
+ /* see if asker is listed in dnattr */
+ for ( at = attrs_find( e->e_attrs, bdn->a_at );
+ at != NULL;
+ at = attrs_find( at->a_next, bdn->a_at ) )
+ {
+ if ( attr_valfind( at,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ &bv, NULL, op->o_tmpmemctx ) == 0 )
+ {
+ /* found it */
+ match = 1;
+ break;
+ }
+ }
+
+ if ( match ) {
+ /* have a dnattr match. if this is a self clause then
+ * the target must also match the op dn.
+ */
+ if ( bdn->a_self ) {
+ /* check if the target is an attribute. */
+ if ( val == NULL ) return 1;
+
+ /* target is attribute, check if the attribute value
+ * is the op dn.
+ */
+ rc = value_match( &match, bdn->a_at,
+ bdn->a_at->ad_type->sat_equality, 0,
+ val, &bv, &text );
+ /* on match error or no match, fail the ACL clause */
+ if ( rc != LDAP_SUCCESS || match != 0 )
+ return 1;
+ }
+
+ } else {
+ /* no dnattr match, check if this is a self clause */
+ if ( ! bdn->a_self )
+ return 1;
+
+ /* this is a self clause, check if the target is an
+ * attribute.
+ */
+ if ( val == NULL )
+ return 1;
+
+ /* target is attribute, check if the attribute value
+ * is the op dn.
+ */
+ rc = value_match( &match, bdn->a_at,
+ bdn->a_at->ad_type->sat_equality, 0,
+ val, &bv, &text );
+
+ /* on match error or no match, fail the ACL clause */
+ if ( rc != LDAP_SUCCESS || match != 0 )
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * slap_acl_mask - modifies mask based upon the given acl and the
+ * requested access to entry e, attribute attr, value val. if val
+ * is null, access to the whole attribute is assumed (all values).
+ *
+ * returns 0 access NOT allowed
+ * 1 access allowed
+ */
+
+static slap_control_t
+slap_acl_mask(
+ AccessControl *a,
+ AccessControl *prev,
+ slap_mask_t *mask,
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ AclRegexMatches *matches,
+ int count,
+ AccessControlState *state,
+ slap_access_t access )
+{
+ int i;
+ Access *b;
+#ifdef LDAP_DEBUG
+ char accessmaskbuf[ACCESSMASK_MAXLEN];
+#endif /* DEBUG */
+ const char *attr;
+#ifdef SLAP_DYNACL
+ slap_mask_t a2pmask = ACL_ACCESS2PRIV( access );
+#endif /* SLAP_DYNACL */
+
+ assert( a != NULL );
+ assert( mask != NULL );
+ assert( desc != NULL );
+
+ attr = desc->ad_cname.bv_val;
+
+ assert( attr != NULL );
+
+ Debug( LDAP_DEBUG_ACL,
+ "=> acl_mask: access to entry \"%s\", attr \"%s\" requested\n",
+ e->e_dn, attr );
+
+ Debug( LDAP_DEBUG_ACL,
+ "=> acl_mask: to %s by \"%s\", (%s) \n",
+ val ? "value" : "all values",
+ op->o_ndn.bv_val ? op->o_ndn.bv_val : "",
+ accessmask2str( *mask, accessmaskbuf, 1 ) );
+
+
+ b = a->acl_access;
+ i = 1;
+
+ for ( ; b != NULL; b = b->a_next, i++ ) {
+ slap_mask_t oldmask, modmask;
+
+ ACL_INVALIDATE( modmask );
+
+ /* check for the "self" modifier in the <access> field */
+ if ( b->a_dn.a_self ) {
+ const char *dummy;
+ int rc, match = 0;
+
+ ACL_RECORD_VALUE_STATE;
+
+ /* must have DN syntax */
+ if ( desc->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName &&
+ !is_at_syntax( desc->ad_type, SLAPD_NAMEUID_SYNTAX )) continue;
+
+ /* check if the target is an attribute. */
+ if ( val == NULL ) continue;
+
+ /* a DN must be present */
+ if ( BER_BVISEMPTY( &op->o_ndn ) ) {
+ continue;
+ }
+
+ /* target is attribute, check if the attribute value
+ * is the op dn.
+ */
+ rc = value_match( &match, desc,
+ desc->ad_type->sat_equality, 0,
+ val, &op->o_ndn, &dummy );
+ /* on match error or no match, fail the ACL clause */
+ if ( rc != LDAP_SUCCESS || match != 0 )
+ continue;
+ }
+
+ /* AND <who> clauses */
+ if ( !BER_BVISEMPTY( &b->a_dn_pat ) ) {
+ Debug( LDAP_DEBUG_ACL, "<= check a_dn_pat: %s\n",
+ b->a_dn_pat.bv_val );
+ /*
+ * if access applies to the entry itself, and the
+ * user is bound as somebody in the same namespace as
+ * the entry, OR the given dn matches the dn pattern
+ */
+ /*
+ * NOTE: styles "anonymous", "users" and "self"
+ * have been moved to enum slap_style_t, whose
+ * value is set in a_dn_style; however, the string
+ * is maintained in a_dn_pat.
+ */
+
+ if ( acl_mask_dn( op, e, val, a, matches,
+ &b->a_dn, &op->o_ndn ) )
+ {
+ continue;
+ }
+ }
+
+ if ( !BER_BVISEMPTY( &b->a_realdn_pat ) ) {
+ struct berval ndn;
+
+ Debug( LDAP_DEBUG_ACL, "<= check a_realdn_pat: %s\n",
+ b->a_realdn_pat.bv_val );
+ /*
+ * if access applies to the entry itself, and the
+ * user is bound as somebody in the same namespace as
+ * the entry, OR the given dn matches the dn pattern
+ */
+ /*
+ * NOTE: styles "anonymous", "users" and "self"
+ * have been moved to enum slap_style_t, whose
+ * value is set in a_dn_style; however, the string
+ * is maintained in a_dn_pat.
+ */
+
+ if ( op->o_conn && !BER_BVISNULL( &op->o_conn->c_ndn ) )
+ {
+ ndn = op->o_conn->c_ndn;
+ } else {
+ ndn = op->o_ndn;
+ }
+
+ if ( acl_mask_dn( op, e, val, a, matches,
+ &b->a_realdn, &ndn ) )
+ {
+ continue;
+ }
+ }
+
+ if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
+ if ( ! op->o_conn->c_listener ) {
+ continue;
+ }
+ Debug( LDAP_DEBUG_ACL, "<= check a_sockurl_pat: %s\n",
+ b->a_sockurl_pat.bv_val );
+
+ if ( !ber_bvccmp( &b->a_sockurl_pat, '*' ) ) {
+ if ( b->a_sockurl_style == ACL_STYLE_REGEX) {
+ if ( !regex_matches( &b->a_sockurl_pat, op->o_conn->c_listener_url.bv_val,
+ &e->e_nname, val, matches ) )
+ {
+ continue;
+ }
+
+ } else if ( b->a_sockurl_style == ACL_STYLE_EXPAND ) {
+ struct berval bv;
+ char buf[ACL_BUF_SIZE];
+
+ bv.bv_len = sizeof( buf ) - 1;
+ bv.bv_val = buf;
+ if ( acl_string_expand( &bv, &b->a_sockurl_pat, &e->e_nname, val, matches ) )
+ {
+ continue;
+ }
+
+ if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_listener_url ) != 0 )
+ {
+ continue;
+ }
+
+ } else {
+ if ( ber_bvstrcasecmp( &b->a_sockurl_pat, &op->o_conn->c_listener_url ) != 0 )
+ {
+ continue;
+ }
+ }
+ }
+ }
+
+ if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
+ if ( !op->o_conn->c_peer_domain.bv_val ) {
+ continue;
+ }
+ Debug( LDAP_DEBUG_ACL, "<= check a_domain_pat: %s\n",
+ b->a_domain_pat.bv_val );
+ if ( !ber_bvccmp( &b->a_domain_pat, '*' ) ) {
+ if ( b->a_domain_style == ACL_STYLE_REGEX) {
+ if ( !regex_matches( &b->a_domain_pat, op->o_conn->c_peer_domain.bv_val,
+ &e->e_nname, val, matches ) )
+ {
+ continue;
+ }
+ } else {
+ char buf[ACL_BUF_SIZE];
+
+ struct berval cmp = op->o_conn->c_peer_domain;
+ struct berval pat = b->a_domain_pat;
+
+ if ( b->a_domain_expand ) {
+ struct berval bv;
+
+ bv.bv_len = sizeof(buf) - 1;
+ bv.bv_val = buf;
+
+ if ( acl_string_expand(&bv, &b->a_domain_pat, &e->e_nname, val, matches) )
+ {
+ continue;
+ }
+ pat = bv;
+ }
+
+ if ( b->a_domain_style == ACL_STYLE_SUBTREE ) {
+ int offset = cmp.bv_len - pat.bv_len;
+ if ( offset < 0 ) {
+ continue;
+ }
+
+ if ( offset == 1 || ( offset > 1 && cmp.bv_val[ offset - 1 ] != '.' ) ) {
+ continue;
+ }
+
+ /* trim the domain */
+ cmp.bv_val = &cmp.bv_val[ offset ];
+ cmp.bv_len -= offset;
+ }
+
+ if ( ber_bvstrcasecmp( &pat, &cmp ) != 0 ) {
+ continue;
+ }
+ }
+ }
+ }
+
+ if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
+ if ( !op->o_conn->c_peer_name.bv_val ) {
+ continue;
+ }
+ Debug( LDAP_DEBUG_ACL, "<= check a_peername_path: %s\n",
+ b->a_peername_pat.bv_val );
+ if ( !ber_bvccmp( &b->a_peername_pat, '*' ) ) {
+ if ( b->a_peername_style == ACL_STYLE_REGEX ) {
+ if ( !regex_matches( &b->a_peername_pat, op->o_conn->c_peer_name.bv_val,
+ &e->e_nname, val, matches ) )
+ {
+ continue;
+ }
+
+ } else {
+ /* try exact match */
+ if ( b->a_peername_style == ACL_STYLE_BASE ) {
+ if ( ber_bvstrcasecmp( &b->a_peername_pat, &op->o_conn->c_peer_name ) != 0 ) {
+ continue;
+ }
+
+ } else if ( b->a_peername_style == ACL_STYLE_EXPAND ) {
+ struct berval bv;
+ char buf[ACL_BUF_SIZE];
+
+ bv.bv_len = sizeof( buf ) - 1;
+ bv.bv_val = buf;
+ if ( acl_string_expand( &bv, &b->a_peername_pat, &e->e_nname, val, matches ) )
+ {
+ continue;
+ }
+
+ if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_peer_name ) != 0 ) {
+ continue;
+ }
+
+ /* extract IP and try exact match */
+ } else if ( b->a_peername_style == ACL_STYLE_IP ) {
+ char *port;
+ char buf[STRLENOF("255.255.255.255") + 1];
+ struct berval ip;
+ unsigned long addr;
+ int port_number = -1;
+
+ if ( strncasecmp( op->o_conn->c_peer_name.bv_val,
+ acl_bv_ip_eq.bv_val,
+ acl_bv_ip_eq.bv_len ) != 0 )
+ continue;
+
+ ip.bv_val = op->o_conn->c_peer_name.bv_val + acl_bv_ip_eq.bv_len;
+ ip.bv_len = op->o_conn->c_peer_name.bv_len - acl_bv_ip_eq.bv_len;
+
+ port = strrchr( ip.bv_val, ':' );
+ if ( port ) {
+ ip.bv_len = port - ip.bv_val;
+ ++port;
+ if ( lutil_atoi( &port_number, port ) != 0 )
+ continue;
+ }
+
+ /* the port check can be anticipated here */
+ if ( b->a_peername_port != -1 && port_number != b->a_peername_port )
+ continue;
+
+ /* address longer than expected? */
+ if ( ip.bv_len >= sizeof(buf) )
+ continue;
+
+ AC_MEMCPY( buf, ip.bv_val, ip.bv_len );
+ buf[ ip.bv_len ] = '\0';
+
+ addr = inet_addr( buf );
+
+ /* unable to convert? */
+ if ( addr == (unsigned long)(-1) )
+ continue;
+
+ if ( (addr & b->a_peername_mask) != b->a_peername_addr )
+ continue;
+
+#ifdef LDAP_PF_INET6
+ /* extract IPv6 and try exact match */
+ } else if ( b->a_peername_style == ACL_STYLE_IPV6 ) {
+ char *port;
+ char buf[STRLENOF("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF") + 1];
+ struct berval ip;
+ struct in6_addr addr;
+ int port_number = -1;
+
+ if ( strncasecmp( op->o_conn->c_peer_name.bv_val,
+ acl_bv_ipv6_eq.bv_val,
+ acl_bv_ipv6_eq.bv_len ) != 0 )
+ continue;
+
+ ip.bv_val = op->o_conn->c_peer_name.bv_val + acl_bv_ipv6_eq.bv_len;
+ ip.bv_len = op->o_conn->c_peer_name.bv_len - acl_bv_ipv6_eq.bv_len;
+
+ port = strrchr( ip.bv_val, ']' );
+ if ( port ) {
+ ip.bv_len = port - ip.bv_val;
+ ++port;
+ if ( port[0] == ':' && lutil_atoi( &port_number, ++port ) != 0 )
+ continue;
+ }
+
+ /* the port check can be anticipated here */
+ if ( b->a_peername_port != -1 && port_number != b->a_peername_port )
+ continue;
+
+ /* address longer than expected? */
+ if ( ip.bv_len >= sizeof(buf) )
+ continue;
+
+ AC_MEMCPY( buf, ip.bv_val, ip.bv_len );
+ buf[ ip.bv_len ] = '\0';
+
+ if ( inet_pton( AF_INET6, buf, &addr ) != 1 )
+ continue;
+
+ /* check mask */
+ if ( !slap_addr6_mask( &addr, &b->a_peername_mask6, &b->a_peername_addr6 ) )
+ continue;
+#endif /* LDAP_PF_INET6 */
+
+#ifdef LDAP_PF_LOCAL
+ /* extract path and try exact match */
+ } else if ( b->a_peername_style == ACL_STYLE_PATH ) {
+ struct berval path;
+
+ if ( strncmp( op->o_conn->c_peer_name.bv_val,
+ acl_bv_path_eq.bv_val,
+ acl_bv_path_eq.bv_len ) != 0 )
+ continue;
+
+ path.bv_val = op->o_conn->c_peer_name.bv_val
+ + acl_bv_path_eq.bv_len;
+ path.bv_len = op->o_conn->c_peer_name.bv_len
+ - acl_bv_path_eq.bv_len;
+
+ if ( ber_bvcmp( &b->a_peername_pat, &path ) != 0 )
+ continue;
+
+#endif /* LDAP_PF_LOCAL */
+
+ /* exact match (very unlikely...) */
+ } else if ( ber_bvcmp( &op->o_conn->c_peer_name, &b->a_peername_pat ) != 0 ) {
+ continue;
+ }
+ }
+ }
+ }
+
+ if ( !BER_BVISEMPTY( &b->a_sockname_pat ) ) {
+ if ( BER_BVISNULL( &op->o_conn->c_sock_name ) ) {
+ continue;
+ }
+ Debug( LDAP_DEBUG_ACL, "<= check a_sockname_path: %s\n",
+ b->a_sockname_pat.bv_val );
+ if ( !ber_bvccmp( &b->a_sockname_pat, '*' ) ) {
+ if ( b->a_sockname_style == ACL_STYLE_REGEX) {
+ if ( !regex_matches( &b->a_sockname_pat, op->o_conn->c_sock_name.bv_val,
+ &e->e_nname, val, matches ) )
+ {
+ continue;
+ }
+
+ } else if ( b->a_sockname_style == ACL_STYLE_EXPAND ) {
+ struct berval bv;
+ char buf[ACL_BUF_SIZE];
+
+ bv.bv_len = sizeof( buf ) - 1;
+ bv.bv_val = buf;
+ if ( acl_string_expand( &bv, &b->a_sockname_pat, &e->e_nname, val, matches ) )
+ {
+ continue;
+ }
+
+ if ( ber_bvstrcasecmp( &bv, &op->o_conn->c_sock_name ) != 0 ) {
+ continue;
+ }
+
+ } else {
+ if ( ber_bvstrcasecmp( &b->a_sockname_pat, &op->o_conn->c_sock_name ) != 0 ) {
+ continue;
+ }
+ }
+ }
+ }
+
+ if ( b->a_dn_at != NULL ) {
+ if ( acl_mask_dnattr( op, e, val, a,
+ count, state, mask,
+ &b->a_dn, &op->o_ndn ) )
+ {
+ continue;
+ }
+ }
+
+ if ( b->a_realdn_at != NULL ) {
+ struct berval ndn;
+
+ if ( op->o_conn && !BER_BVISNULL( &op->o_conn->c_ndn ) )
+ {
+ ndn = op->o_conn->c_ndn;
+ } else {
+ ndn = op->o_ndn;
+ }
+
+ if ( acl_mask_dnattr( op, e, val, a,
+ count, state, mask,
+ &b->a_realdn, &ndn ) )
+ {
+ continue;
+ }
+ }
+
+ if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
+ struct berval bv;
+ struct berval ndn = BER_BVNULL;
+ int rc;
+
+ if ( op->o_ndn.bv_len == 0 ) {
+ continue;
+ }
+
+ Debug( LDAP_DEBUG_ACL, "<= check a_group_pat: %s\n",
+ b->a_group_pat.bv_val );
+
+ /* b->a_group is an unexpanded entry name, expanded it should be an
+ * entry with objectclass group* and we test to see if odn is one of
+ * the values in the attribute group
+ */
+ /* see if asker is listed in dnattr */
+ if ( b->a_group_style == ACL_STYLE_EXPAND ) {
+ char buf[ACL_BUF_SIZE];
+ AclRegexMatches tmp_matches,
+ *tmp_matchesp = &tmp_matches;
+ regmatch_t *tmp_data;
+
+ MATCHES_MEMSET( &tmp_matches );
+ tmp_data = &tmp_matches.dn_data[0];
+
+ bv.bv_len = sizeof(buf) - 1;
+ bv.bv_val = buf;
+
+ rc = 0;
+
+ if ( a->acl_attrval_style == ACL_STYLE_REGEX )
+ tmp_matchesp = matches;
+ else switch ( a->acl_dn_style ) {
+ case ACL_STYLE_REGEX:
+ if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
+ tmp_matchesp = matches;
+ break;
+ }
+
+ /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
+ case ACL_STYLE_BASE:
+ tmp_data[0].rm_so = 0;
+ tmp_data[0].rm_eo = e->e_nname.bv_len;
+ tmp_matches.dn_count = 1;
+ break;
+
+ case ACL_STYLE_ONE:
+ case ACL_STYLE_SUBTREE:
+ case ACL_STYLE_CHILDREN:
+ tmp_data[0].rm_so = 0;
+ tmp_data[0].rm_eo = e->e_nname.bv_len;
+
+ tmp_data[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
+ tmp_data[1].rm_eo = e->e_nname.bv_len;
+ tmp_matches.dn_count = 2;
+ break;
+
+ default:
+ /* error */
+ rc = 1;
+ break;
+ }
+
+ if ( rc ) {
+ continue;
+ }
+
+ if ( acl_string_expand( &bv, &b->a_group_pat,
+ &e->e_nname, val,
+ tmp_matchesp ) )
+ {
+ continue;
+ }
+
+ if ( dnNormalize( 0, NULL, NULL, &bv, &ndn,
+ op->o_tmpmemctx ) != LDAP_SUCCESS )
+ {
+ /* did not expand to a valid dn */
+ continue;
+ }
+
+ bv = ndn;
+
+ } else {
+ bv = b->a_group_pat;
+ }
+
+ rc = backend_group( op, e, &bv, &op->o_ndn,
+ b->a_group_oc, b->a_group_at );
+
+ if ( ndn.bv_val ) {
+ slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( rc != 0 ) {
+ continue;
+ }
+ }
+
+ if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
+ struct berval bv;
+ char buf[ACL_BUF_SIZE];
+
+ Debug( LDAP_DEBUG_ACL, "<= check a_set_pat: %s\n",
+ b->a_set_pat.bv_val );
+
+ if ( b->a_set_style == ACL_STYLE_EXPAND ) {
+ AclRegexMatches tmp_matches,
+ *tmp_matchesp = &tmp_matches;
+ int rc = 0;
+ regmatch_t *tmp_data;
+
+ MATCHES_MEMSET( &tmp_matches );
+ tmp_data = &tmp_matches.dn_data[0];
+
+ bv.bv_len = sizeof( buf ) - 1;
+ bv.bv_val = buf;
+
+ rc = 0;
+
+ if ( a->acl_attrval_style == ACL_STYLE_REGEX )
+ tmp_matchesp = matches;
+ else switch ( a->acl_dn_style ) {
+ case ACL_STYLE_REGEX:
+ if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
+ tmp_matchesp = matches;
+ break;
+ }
+
+ /* FALLTHRU: applies also to ACL_STYLE_REGEX when pattern is "*" */
+ case ACL_STYLE_BASE:
+ tmp_data[0].rm_so = 0;
+ tmp_data[0].rm_eo = e->e_nname.bv_len;
+ tmp_matches.dn_count = 1;
+ break;
+
+ case ACL_STYLE_ONE:
+ case ACL_STYLE_SUBTREE:
+ case ACL_STYLE_CHILDREN:
+ tmp_data[0].rm_so = 0;
+ tmp_data[0].rm_eo = e->e_nname.bv_len;
+ tmp_data[1].rm_so = e->e_nname.bv_len - a->acl_dn_pat.bv_len;
+ tmp_data[1].rm_eo = e->e_nname.bv_len; tmp_matches.dn_count = 2;
+ break;
+
+ default:
+ /* error */
+ rc = 1;
+ break;
+ }
+
+ if ( rc ) {
+ continue;
+ }
+
+ if ( acl_string_expand( &bv, &b->a_set_pat,
+ &e->e_nname, val,
+ tmp_matchesp ) )
+ {
+ continue;
+ }
+
+ } else {
+ bv = b->a_set_pat;
+ }
+
+ if ( acl_match_set( &bv, op, e, NULL ) == 0 ) {
+ continue;
+ }
+ }
+
+ if ( b->a_authz.sai_ssf ) {
+ Debug( LDAP_DEBUG_ACL, "<= check a_authz.sai_ssf: ACL %u > OP %u\n",
+ b->a_authz.sai_ssf, op->o_ssf );
+ if ( b->a_authz.sai_ssf > op->o_ssf ) {
+ continue;
+ }
+ }
+
+ if ( b->a_authz.sai_transport_ssf ) {
+ Debug( LDAP_DEBUG_ACL,
+ "<= check a_authz.sai_transport_ssf: ACL %u > OP %u\n",
+ b->a_authz.sai_transport_ssf, op->o_transport_ssf );
+ if ( b->a_authz.sai_transport_ssf > op->o_transport_ssf ) {
+ continue;
+ }
+ }
+
+ if ( b->a_authz.sai_tls_ssf ) {
+ Debug( LDAP_DEBUG_ACL,
+ "<= check a_authz.sai_tls_ssf: ACL %u > OP %u\n",
+ b->a_authz.sai_tls_ssf, op->o_tls_ssf );
+ if ( b->a_authz.sai_tls_ssf > op->o_tls_ssf ) {
+ continue;
+ }
+ }
+
+ if ( b->a_authz.sai_sasl_ssf ) {
+ Debug( LDAP_DEBUG_ACL,
+ "<= check a_authz.sai_sasl_ssf: ACL %u > OP %u\n",
+ b->a_authz.sai_sasl_ssf, op->o_sasl_ssf );
+ if ( b->a_authz.sai_sasl_ssf > op->o_sasl_ssf ) {
+ continue;
+ }
+ }
+
+#ifdef SLAP_DYNACL
+ if ( b->a_dynacl ) {
+ slap_dynacl_t *da;
+ slap_access_t tgrant, tdeny;
+
+ Debug( LDAP_DEBUG_ACL, "<= check a_dynacl\n" );
+
+ /* this case works different from the others above.
+ * since dynamic ACL's themselves give permissions, we need
+ * to first check b->a_access_mask, the ACL's access level.
+ */
+ /* first check if the right being requested
+ * is allowed by the ACL clause.
+ */
+ if ( ! ACL_PRIV_ISSET( b->a_access_mask, a2pmask ) ) {
+ continue;
+ }
+
+ /* start out with nothing granted, nothing denied */
+ ACL_INVALIDATE(tgrant);
+ ACL_INVALIDATE(tdeny);
+
+ for ( da = b->a_dynacl; da; da = da->da_next ) {
+ slap_access_t grant,
+ deny;
+
+ ACL_INVALIDATE(grant);
+ ACL_INVALIDATE(deny);
+
+ Debug( LDAP_DEBUG_ACL, " <= check a_dynacl: %s\n",
+ da->da_name );
+
+ /*
+ * XXXmanu Only DN matches are supplied
+ * sending attribute values matches require
+ * an API update
+ */
+ (void)da->da_mask( da->da_private, op, e, desc,
+ val, matches->dn_count, matches->dn_data,
+ &grant, &deny );
+
+ tgrant |= grant;
+ tdeny |= deny;
+ }
+
+ /* remove anything that the ACL clause does not allow */
+ tgrant &= b->a_access_mask & ACL_PRIV_MASK;
+ tdeny &= ACL_PRIV_MASK;
+
+ /* see if we have anything to contribute */
+ if( ACL_IS_INVALID(tgrant) && ACL_IS_INVALID(tdeny) ) {
+ continue;
+ }
+
+ /* this could be improved by changing slap_acl_mask so that it can deal with
+ * by clauses that return grant/deny pairs. Right now, it does either
+ * additive or subtractive rights, but not both at the same time. So,
+ * we need to combine the grant/deny pair into a single rights mask in
+ * a smart way: if either grant or deny is "empty", then we use the
+ * opposite as is, otherwise we remove any denied rights from the grant
+ * rights mask and construct an additive mask.
+ */
+ if (ACL_IS_INVALID(tdeny)) {
+ modmask = tgrant | ACL_PRIV_ADDITIVE;
+
+ } else if (ACL_IS_INVALID(tgrant)) {
+ modmask = tdeny | ACL_PRIV_SUBSTRACTIVE;
+
+ } else {
+ modmask = (tgrant & ~tdeny) | ACL_PRIV_ADDITIVE;
+ }
+
+ } else
+#endif /* SLAP_DYNACL */
+ {
+ modmask = b->a_access_mask;
+ }
+
+ Debug( LDAP_DEBUG_ACL,
+ "<= acl_mask: [%d] applying %s (%s)\n",
+ i, accessmask2str( modmask, accessmaskbuf, 1 ),
+ b->a_type == ACL_CONTINUE
+ ? "continue"
+ : b->a_type == ACL_BREAK
+ ? "break"
+ : "stop" );
+ /* save old mask */
+ oldmask = *mask;
+
+ if( ACL_IS_ADDITIVE(modmask) ) {
+ /* add privs */
+ ACL_PRIV_SET( *mask, modmask );
+
+ /* cleanup */
+ ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
+
+ } else if( ACL_IS_SUBTRACTIVE(modmask) ) {
+ /* subtract privs */
+ ACL_PRIV_CLR( *mask, modmask );
+
+ /* cleanup */
+ ACL_PRIV_CLR( *mask, ~ACL_PRIV_MASK );
+
+ } else {
+ /* assign privs */
+ *mask = modmask;
+ }
+
+ Debug( LDAP_DEBUG_ACL,
+ "<= acl_mask: [%d] mask: %s\n",
+ i, accessmask2str(*mask, accessmaskbuf, 1) );
+
+ if( b->a_type == ACL_CONTINUE ) {
+ continue;
+
+ } else if ( b->a_type == ACL_BREAK ) {
+ return ACL_BREAK;
+
+ } else {
+ return ACL_STOP;
+ }
+ }
+
+ /* implicit "by * none" clause */
+ ACL_INIT(*mask);
+
+ Debug( LDAP_DEBUG_ACL,
+ "<= acl_mask: no more <who> clauses, returning %s (stop)\n",
+ accessmask2str(*mask, accessmaskbuf, 1) );
+ return ACL_STOP;
+}
+
+/*
+ * acl_check_modlist - check access control on the given entry to see if
+ * it allows the given modifications by the user associated with op.
+ * returns 1 if mods allowed ok
+ * 0 mods not allowed
+ */
+
+int
+acl_check_modlist(
+ Operation *op,
+ Entry *e,
+ Modifications *mlist )
+{
+ struct berval *bv;
+ AccessControlState state = ACL_STATE_INIT;
+ Backend *be;
+ int be_null = 0;
+ int ret = 1; /* default is access allowed */
+
+ be = op->o_bd;
+ if ( be == NULL ) {
+ be = LDAP_STAILQ_FIRST(&backendDB);
+ be_null = 1;
+ op->o_bd = be;
+ }
+ assert( be != NULL );
+
+ /* If ADD attribute checking is not enabled, just allow it */
+ if ( op->o_tag == LDAP_REQ_ADD && !SLAP_DBACL_ADD( be ))
+ return 1;
+
+ /* short circuit root database access */
+ if ( be_isroot( op ) ) {
+ Debug( LDAP_DEBUG_ACL,
+ "<= acl_access_allowed: granted to database root\n" );
+ goto done;
+ }
+
+ /* use backend default access if no backend acls */
+ if( op->o_bd != NULL && op->o_bd->be_acl == NULL && frontendDB->be_acl == NULL ) {
+ Debug( LDAP_DEBUG_ACL,
+ "=> access_allowed: backend default %s access %s to \"%s\"\n",
+ access2str( ACL_WRITE ),
+ op->o_bd->be_dfltaccess >= ACL_WRITE
+ ? "granted" : "denied",
+ op->o_dn.bv_val );
+ ret = (op->o_bd->be_dfltaccess >= ACL_WRITE);
+ goto done;
+ }
+
+ for ( ; mlist != NULL; mlist = mlist->sml_next ) {
+ /*
+ * Internal mods are ignored by ACL_WRITE checking
+ */
+ if ( mlist->sml_flags & SLAP_MOD_INTERNAL ) {
+ Debug( LDAP_DEBUG_ACL, "acl: internal mod %s:"
+ " modify access granted\n",
+ mlist->sml_desc->ad_cname.bv_val );
+ continue;
+ }
+
+ /*
+ * no-user-modification operational attributes are ignored
+ * by ACL_WRITE checking as any found here are not provided
+ * by the user
+ */
+ if ( is_at_no_user_mod( mlist->sml_desc->ad_type )
+ && ! ( mlist->sml_flags & SLAP_MOD_MANAGING ) )
+ {
+ Debug( LDAP_DEBUG_ACL, "acl: no-user-mod %s:"
+ " modify access granted\n",
+ mlist->sml_desc->ad_cname.bv_val );
+ continue;
+ }
+
+ switch ( mlist->sml_op ) {
+ case LDAP_MOD_REPLACE:
+ case LDAP_MOD_INCREMENT:
+ /*
+ * We must check both permission to delete the whole
+ * attribute and permission to add the specific attributes.
+ * This prevents abuse from selfwriters.
+ */
+ if ( ! access_allowed( op, e,
+ mlist->sml_desc, NULL,
+ ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WDEL,
+ &state ) )
+ {
+ ret = 0;
+ goto done;
+ }
+
+ if ( mlist->sml_values == NULL ) break;
+
+ /* fall thru to check value to add */
+
+ case LDAP_MOD_ADD:
+ case SLAP_MOD_ADD_IF_NOT_PRESENT:
+ assert( mlist->sml_values != NULL );
+
+ if ( mlist->sml_op == SLAP_MOD_ADD_IF_NOT_PRESENT
+ && attr_find( e->e_attrs, mlist->sml_desc ) )
+ {
+ break;
+ }
+
+ for ( bv = mlist->sml_nvalues
+ ? mlist->sml_nvalues : mlist->sml_values;
+ bv->bv_val != NULL; bv++ )
+ {
+ if ( ! access_allowed( op, e,
+ mlist->sml_desc, bv,
+ ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WADD,
+ &state ) )
+ {
+ ret = 0;
+ goto done;
+ }
+ }
+ break;
+
+ case LDAP_MOD_DELETE:
+ case SLAP_MOD_SOFTDEL:
+ if ( mlist->sml_values == NULL ) {
+ if ( ! access_allowed( op, e,
+ mlist->sml_desc, NULL,
+ ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WDEL,
+ &state ) )
+ {
+ ret = 0;
+ goto done;
+ }
+ break;
+ }
+ for ( bv = mlist->sml_nvalues
+ ? mlist->sml_nvalues : mlist->sml_values;
+ bv->bv_val != NULL; bv++ )
+ {
+ if ( ! access_allowed( op, e,
+ mlist->sml_desc, bv,
+ ( mlist->sml_flags & SLAP_MOD_MANAGING ) ? ACL_MANAGE : ACL_WDEL,
+ &state ) )
+ {
+ ret = 0;
+ goto done;
+ }
+ }
+ break;
+
+ case SLAP_MOD_SOFTADD:
+ /* allow adding attribute via modrdn thru */
+ break;
+
+ default:
+ assert( 0 );
+ /* not reached */
+ ret = 0;
+ break;
+ }
+ }
+
+done:
+ if (be_null) op->o_bd = NULL;
+ return( ret );
+}
+
+int
+acl_get_part(
+ struct berval *list,
+ int ix,
+ char sep,
+ struct berval *bv )
+{
+ int len;
+ char *p;
+
+ if ( bv ) {
+ BER_BVZERO( bv );
+ }
+ len = list->bv_len;
+ p = list->bv_val;
+ while ( len >= 0 && --ix >= 0 ) {
+ while ( --len >= 0 && *p++ != sep )
+ ;
+ }
+ while ( len >= 0 && *p == ' ' ) {
+ len--;
+ p++;
+ }
+ if ( len < 0 ) {
+ return -1;
+ }
+
+ if ( !bv ) {
+ return 0;
+ }
+
+ bv->bv_val = p;
+ while ( --len >= 0 && *p != sep ) {
+ bv->bv_len++;
+ p++;
+ }
+ while ( bv->bv_len > 0 && *--p == ' ' ) {
+ bv->bv_len--;
+ }
+
+ return bv->bv_len;
+}
+
+typedef struct acl_set_gather_t {
+ SetCookie *cookie;
+ BerVarray bvals;
+} acl_set_gather_t;
+
+static int
+acl_set_cb_gather( Operation *op, SlapReply *rs )
+{
+ acl_set_gather_t *p = (acl_set_gather_t *)op->o_callback->sc_private;
+
+ if ( rs->sr_type == REP_SEARCH ) {
+ BerValue bvals[ 2 ];
+ BerVarray bvalsp = NULL;
+ int j;
+
+ for ( j = 0; !BER_BVISNULL( &rs->sr_attrs[ j ].an_name ); j++ ) {
+ AttributeDescription *desc = rs->sr_attrs[ j ].an_desc;
+
+ if ( desc == NULL ) {
+ continue;
+ }
+
+ if ( desc == slap_schema.si_ad_entryDN ) {
+ bvalsp = bvals;
+ bvals[ 0 ] = rs->sr_entry->e_nname;
+ BER_BVZERO( &bvals[ 1 ] );
+
+ } else {
+ Attribute *a;
+
+ a = attr_find( rs->sr_entry->e_attrs, desc );
+ if ( a != NULL ) {
+ bvalsp = a->a_nvals;
+ }
+ }
+
+ if ( bvalsp ) {
+ p->bvals = slap_set_join( p->cookie, p->bvals,
+ ( '|' | SLAP_SET_RREF ), bvalsp );
+ }
+ }
+
+ } else {
+ switch ( rs->sr_type ) {
+ case REP_SEARCHREF:
+ case REP_INTERMEDIATE:
+ /* ignore */
+ break;
+
+ default:
+ assert( rs->sr_type == REP_RESULT );
+ break;
+ }
+ }
+
+ return 0;
+}
+
+BerVarray
+acl_set_gather( SetCookie *cookie, struct berval *name, AttributeDescription *desc )
+{
+ AclSetCookie *cp = (AclSetCookie *)cookie;
+ int rc = 0;
+ LDAPURLDesc *ludp = NULL;
+ Operation op2 = { 0 };
+ SlapReply rs = {REP_RESULT};
+ AttributeName anlist[ 2 ], *anlistp = NULL;
+ int nattrs = 0;
+ slap_callback cb = { NULL, acl_set_cb_gather, NULL, NULL };
+ acl_set_gather_t p = { 0 };
+
+ /* this routine needs to return the bervals instead of
+ * plain strings, since syntax is not known. It should
+ * also return the syntax or some "comparison cookie".
+ */
+ if ( strncasecmp( name->bv_val, "ldap:///", STRLENOF( "ldap:///" ) ) != 0 ) {
+ return acl_set_gather2( cookie, name, desc );
+ }
+
+ rc = ldap_url_parse( name->bv_val, &ludp );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "%s acl_set_gather: unable to parse URL=\"%s\"\n",
+ cp->asc_op->o_log_prefix, name->bv_val );
+
+ rc = LDAP_PROTOCOL_ERROR;
+ goto url_done;
+ }
+
+ if ( ( ludp->lud_host && ludp->lud_host[0] ) || ludp->lud_exts )
+ {
+ /* host part must be empty */
+ /* extensions parts must be empty */
+ Debug( LDAP_DEBUG_TRACE,
+ "%s acl_set_gather: host/exts must be absent in URL=\"%s\"\n",
+ cp->asc_op->o_log_prefix, name->bv_val );
+
+ rc = LDAP_PROTOCOL_ERROR;
+ goto url_done;
+ }
+
+ /* Grab the searchbase and see if an appropriate database can be found */
+ ber_str2bv( ludp->lud_dn, 0, 0, &op2.o_req_dn );
+ rc = dnNormalize( 0, NULL, NULL, &op2.o_req_dn,
+ &op2.o_req_ndn, cp->asc_op->o_tmpmemctx );
+ BER_BVZERO( &op2.o_req_dn );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "%s acl_set_gather: DN=\"%s\" normalize failed\n",
+ cp->asc_op->o_log_prefix, ludp->lud_dn );
+
+ goto url_done;
+ }
+
+ op2.o_bd = select_backend( &op2.o_req_ndn, 1 );
+ if ( ( op2.o_bd == NULL ) || ( op2.o_bd->be_search == NULL ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "%s acl_set_gather: no database could be selected for DN=\"%s\"\n",
+ cp->asc_op->o_log_prefix, op2.o_req_ndn.bv_val );
+
+ rc = LDAP_NO_SUCH_OBJECT;
+ goto url_done;
+ }
+
+ /* Grab the filter */
+ if ( ludp->lud_filter ) {
+ ber_str2bv_x( ludp->lud_filter, 0, 0, &op2.ors_filterstr,
+ cp->asc_op->o_tmpmemctx );
+ op2.ors_filter = str2filter_x( cp->asc_op, op2.ors_filterstr.bv_val );
+ if ( op2.ors_filter == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "%s acl_set_gather: unable to parse filter=\"%s\"\n",
+ cp->asc_op->o_log_prefix, op2.ors_filterstr.bv_val );
+
+ rc = LDAP_PROTOCOL_ERROR;
+ goto url_done;
+ }
+
+ } else {
+ op2.ors_filterstr = *slap_filterstr_objectClass_pres;
+ op2.ors_filter = (Filter *)slap_filter_objectClass_pres;
+ }
+
+
+ /* Grab the scope */
+ op2.ors_scope = ludp->lud_scope;
+
+ /* Grap the attributes */
+ if ( ludp->lud_attrs ) {
+ int i;
+
+ for ( ; ludp->lud_attrs[ nattrs ]; nattrs++ )
+ ;
+
+ anlistp = slap_sl_calloc( sizeof( AttributeName ), nattrs + 2,
+ cp->asc_op->o_tmpmemctx );
+
+ for ( i = 0, nattrs = 0; ludp->lud_attrs[ i ]; i++ ) {
+ struct berval name;
+ AttributeDescription *desc = NULL;
+ const char *text = NULL;
+
+ ber_str2bv( ludp->lud_attrs[ i ], 0, 0, &name );
+ rc = slap_bv2ad( &name, &desc, &text );
+ if ( rc == LDAP_SUCCESS ) {
+ anlistp[ nattrs ].an_name = name;
+ anlistp[ nattrs ].an_desc = desc;
+ nattrs++;
+ }
+ }
+
+ } else {
+ anlistp = anlist;
+ }
+
+ anlistp[ nattrs ].an_name = desc->ad_cname;
+ anlistp[ nattrs ].an_desc = desc;
+
+ BER_BVZERO( &anlistp[ nattrs + 1 ].an_name );
+
+ p.cookie = cookie;
+
+ op2.o_hdr = cp->asc_op->o_hdr;
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2.o_ndn = op2.o_bd->be_rootndn;
+ op2.o_callback = &cb;
+ slap_op_time( &op2.o_time, &op2.o_tincr );
+ op2.o_do_not_cache = 1;
+ op2.o_is_auth_check = 0;
+ ber_dupbv_x( &op2.o_req_dn, &op2.o_req_ndn, cp->asc_op->o_tmpmemctx );
+ op2.ors_slimit = SLAP_NO_LIMIT;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+ op2.ors_attrs = anlistp;
+ op2.ors_attrsonly = 0;
+ op2.o_private = cp->asc_op->o_private;
+ op2.o_extra = cp->asc_op->o_extra;
+
+ cb.sc_private = &p;
+
+ rc = op2.o_bd->be_search( &op2, &rs );
+ if ( rc != 0 ) {
+ goto url_done;
+ }
+
+url_done:;
+ if ( op2.ors_filter && op2.ors_filter != slap_filter_objectClass_pres ) {
+ filter_free_x( cp->asc_op, op2.ors_filter, 1 );
+ }
+ if ( !BER_BVISNULL( &op2.o_req_ndn ) ) {
+ slap_sl_free( op2.o_req_ndn.bv_val, cp->asc_op->o_tmpmemctx );
+ }
+ if ( !BER_BVISNULL( &op2.o_req_dn ) ) {
+ slap_sl_free( op2.o_req_dn.bv_val, cp->asc_op->o_tmpmemctx );
+ }
+ if ( ludp ) {
+ ldap_free_urldesc( ludp );
+ }
+ if ( anlistp && anlistp != anlist ) {
+ slap_sl_free( anlistp, cp->asc_op->o_tmpmemctx );
+ }
+
+ return p.bvals;
+}
+
+BerVarray
+acl_set_gather2( SetCookie *cookie, struct berval *name, AttributeDescription *desc )
+{
+ AclSetCookie *cp = (AclSetCookie *)cookie;
+ BerVarray bvals = NULL;
+ struct berval ndn;
+ int rc = 0;
+
+ /* this routine needs to return the bervals instead of
+ * plain strings, since syntax is not known. It should
+ * also return the syntax or some "comparison cookie".
+ */
+ rc = dnNormalize( 0, NULL, NULL, name, &ndn, cp->asc_op->o_tmpmemctx );
+ if ( rc == LDAP_SUCCESS ) {
+ if ( desc == slap_schema.si_ad_entryDN ) {
+ bvals = (BerVarray)slap_sl_malloc( sizeof( BerValue ) * 2,
+ cp->asc_op->o_tmpmemctx );
+ bvals[ 0 ] = ndn;
+ BER_BVZERO( &bvals[ 1 ] );
+ BER_BVZERO( &ndn );
+
+ } else {
+ backend_attribute( cp->asc_op,
+ cp->asc_e, &ndn, desc, &bvals, ACL_NONE );
+ }
+
+ if ( !BER_BVISNULL( &ndn ) ) {
+ slap_sl_free( ndn.bv_val, cp->asc_op->o_tmpmemctx );
+ }
+ }
+
+ return bvals;
+}
+
+int
+acl_match_set (
+ struct berval *subj,
+ Operation *op,
+ Entry *e,
+ struct berval *default_set_attribute )
+{
+ struct berval set = BER_BVNULL;
+ int rc = 0;
+ AclSetCookie cookie;
+
+ if ( default_set_attribute == NULL ) {
+ set = *subj;
+
+ } else {
+ struct berval subjdn, ndn = BER_BVNULL;
+ struct berval setat;
+ BerVarray bvals = NULL;
+ const char *text;
+ AttributeDescription *desc = NULL;
+
+ /* format of string is "entry/setAttrName" */
+ if ( acl_get_part( subj, 0, '/', &subjdn ) < 0 ) {
+ return 0;
+ }
+
+ if ( acl_get_part( subj, 1, '/', &setat ) < 0 ) {
+ setat = *default_set_attribute;
+ }
+
+ /*
+ * NOTE: dnNormalize honors the ber_len field
+ * as the length of the dn to be normalized
+ */
+ if ( slap_bv2ad( &setat, &desc, &text ) == LDAP_SUCCESS ) {
+ if ( dnNormalize( 0, NULL, NULL, &subjdn, &ndn, op->o_tmpmemctx ) == LDAP_SUCCESS )
+ {
+ backend_attribute( op, e, &ndn, desc, &bvals, ACL_NONE );
+ if ( bvals != NULL && !BER_BVISNULL( &bvals[0] ) ) {
+ int i;
+
+ set = bvals[0];
+ BER_BVZERO( &bvals[0] );
+ for ( i = 1; !BER_BVISNULL( &bvals[i] ); i++ )
+ /* count */ ;
+ bvals[0].bv_val = bvals[i-1].bv_val;
+ BER_BVZERO( &bvals[i-1] );
+ }
+ ber_bvarray_free_x( bvals, op->o_tmpmemctx );
+ slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
+ }
+ }
+ }
+
+ if ( !BER_BVISNULL( &set ) ) {
+ cookie.asc_op = op;
+ cookie.asc_e = e;
+ rc = ( slap_set_filter(
+ acl_set_gather,
+ (SetCookie *)&cookie, &set,
+ &op->o_ndn, &e->e_nname, NULL ) > 0 );
+ if ( set.bv_val != subj->bv_val ) {
+ slap_sl_free( set.bv_val, op->o_tmpmemctx );
+ }
+ }
+
+ return(rc);
+}
+
+#ifdef SLAP_DYNACL
+
+/*
+ * dynamic ACL infrastructure
+ */
+static slap_dynacl_t *da_list = NULL;
+
+int
+slap_dynacl_register( slap_dynacl_t *da )
+{
+ slap_dynacl_t *tmp;
+
+ for ( tmp = da_list; tmp; tmp = tmp->da_next ) {
+ if ( strcasecmp( da->da_name, tmp->da_name ) == 0 ) {
+ break;
+ }
+ }
+
+ if ( tmp != NULL ) {
+ return -1;
+ }
+
+ if ( da->da_mask == NULL ) {
+ return -1;
+ }
+
+ da->da_private = NULL;
+ da->da_next = da_list;
+ da_list = da;
+
+ return 0;
+}
+
+static slap_dynacl_t *
+slap_dynacl_next( slap_dynacl_t *da )
+{
+ if ( da ) {
+ return da->da_next;
+ }
+ return da_list;
+}
+
+slap_dynacl_t *
+slap_dynacl_get( const char *name )
+{
+ slap_dynacl_t *da;
+
+ for ( da = slap_dynacl_next( NULL ); da; da = slap_dynacl_next( da ) ) {
+ if ( strcasecmp( da->da_name, name ) == 0 ) {
+ break;
+ }
+ }
+
+ return da;
+}
+#endif /* SLAP_DYNACL */
+
+/*
+ * statically built-in dynamic ACL initialization
+ */
+static int (*acl_init_func[])( void ) = {
+#ifdef SLAP_DYNACL
+ /* TODO: remove when ACI will only be dynamic */
+#if SLAPD_ACI_ENABLED == SLAPD_MOD_STATIC
+ dynacl_aci_init,
+#endif /* SLAPD_ACI_ENABLED */
+#endif /* SLAP_DYNACL */
+
+ NULL
+};
+
+int
+acl_init( void )
+{
+ int i, rc;
+
+ for ( i = 0; acl_init_func[ i ] != NULL; i++ ) {
+ rc = (*(acl_init_func[ i ]))();
+ if ( rc != 0 ) {
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+int
+acl_string_expand(
+ struct berval *bv,
+ struct berval *pat,
+ struct berval *dn_matches,
+ struct berval *val_matches,
+ AclRegexMatches *matches)
+{
+ ber_len_t size;
+ char *sp;
+ char *dp;
+ int flag;
+ enum { DN_FLAG, VAL_FLAG } tflag;
+
+ size = 0;
+ bv->bv_val[0] = '\0';
+ bv->bv_len--; /* leave space for lone $ */
+
+ flag = 0;
+ tflag = DN_FLAG;
+ for ( dp = bv->bv_val, sp = pat->bv_val; size < bv->bv_len &&
+ sp < pat->bv_val + pat->bv_len ; sp++ )
+ {
+ /* did we previously see a $ */
+ if ( flag ) {
+ if ( flag == 1 && *sp == '$' ) {
+ *dp++ = '$';
+ size++;
+ flag = 0;
+ tflag = DN_FLAG;
+
+ } else if ( flag == 2 && *sp == 'v' /*'}'*/) {
+ tflag = VAL_FLAG;
+
+ } else if ( flag == 2 && *sp == 'd' /*'}'*/) {
+ tflag = DN_FLAG;
+
+ } else if ( flag == 1 && *sp == '{' /*'}'*/) {
+ flag = 2;
+
+ } else if ( *sp >= '0' && *sp <= '9' ) {
+ int nm;
+ regmatch_t *m;
+ char *data;
+ int n;
+ int i;
+ int l;
+
+ n = *sp - '0';
+
+ if ( flag == 2 ) {
+ for ( sp++; *sp != '\0' && *sp != /*'{'*/ '}'; sp++ ) {
+ if ( *sp >= '0' && *sp <= '9' ) {
+ n = 10*n + ( *sp - '0' );
+ }
+ }
+
+ if ( *sp != /*'{'*/ '}' ) {
+ /* FIXME: error */
+ return 1;
+ }
+ }
+
+ switch (tflag) {
+ case DN_FLAG:
+ nm = matches->dn_count;
+ m = matches->dn_data;
+ data = dn_matches ? dn_matches->bv_val : NULL;
+ break;
+ case VAL_FLAG:
+ nm = matches->val_count;
+ m = matches->val_data;
+ data = val_matches ? val_matches->bv_val : NULL;
+ break;
+ default:
+ assert( 0 );
+ }
+ if ( n >= nm ) {
+ /* FIXME: error */
+ return 1;
+ }
+ if ( data == NULL ) {
+ /* FIXME: error */
+ return 1;
+ }
+
+ *dp = '\0';
+ i = m[n].rm_so;
+ l = m[n].rm_eo;
+
+ for ( ; size < bv->bv_len && i < l; size++, i++ ) {
+ *dp++ = data[i];
+ }
+ *dp = '\0';
+
+ flag = 0;
+ tflag = DN_FLAG;
+ }
+ } else {
+ if (*sp == '$') {
+ flag = 1;
+ } else {
+ *dp++ = *sp;
+ size++;
+ }
+ }
+ }
+
+ if ( flag ) {
+ /* must have ended with a single $ */
+ *dp++ = '$';
+ size++;
+ }
+
+ *dp = '\0';
+ bv->bv_len = size;
+
+ Debug( LDAP_DEBUG_ACL, "=> acl_string_expand: pattern: %.*s\n", (int)pat->bv_len, pat->bv_val );
+ Debug( LDAP_DEBUG_ACL, "=> acl_string_expand: expanded: %s\n", bv->bv_val );
+
+ return 0;
+}
+
+static int
+regex_matches(
+ struct berval *pat, /* pattern to expand and match against */
+ char *str, /* string to match against pattern */
+ struct berval *dn_matches, /* buffer with $N expansion variables from DN */
+ struct berval *val_matches, /* buffer with $N expansion variables from val */
+ AclRegexMatches *matches /* offsets in buffer for $N expansion variables */
+)
+{
+ regex_t re;
+ char newbuf[ACL_BUF_SIZE];
+ struct berval bv;
+ int rc;
+
+ bv.bv_len = sizeof( newbuf ) - 1;
+ bv.bv_val = newbuf;
+
+ if (str == NULL) {
+ str = "";
+ };
+
+ if ( acl_string_expand( &bv, pat, dn_matches, val_matches, matches )) {
+ Debug( LDAP_DEBUG_TRACE,
+ "expand( \"%s\", \"%s\") failed\n",
+ pat->bv_val, str );
+ return( 0 );
+ }
+ rc = regcomp( &re, newbuf, REG_EXTENDED|REG_ICASE );
+ if ( rc ) {
+ char error[ACL_BUF_SIZE];
+ regerror( rc, &re, error, sizeof( error ) );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "compile( \"%s\", \"%s\") failed %s\n",
+ pat->bv_val, str, error );
+ return( 0 );
+ }
+
+ rc = regexec( &re, str, 0, NULL, 0 );
+ regfree( &re );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> regex_matches: string: %s\n", str );
+ Debug( LDAP_DEBUG_TRACE,
+ "=> regex_matches: rc: %d %s\n",
+ rc, !rc ? "matches" : "no matches" );
+ return( !rc );
+}
+
diff --git a/servers/slapd/aclparse.c b/servers/slapd/aclparse.c
new file mode 100644
index 0000000..60b74e3
--- /dev/null
+++ b/servers/slapd/aclparse.c
@@ -0,0 +1,2815 @@
+/* aclparse.c - routines to parse and check acl's */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/regex.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include "slap.h"
+#include "lber_pvt.h"
+#include "lutil.h"
+
+static const char style_base[] = "base";
+const char *style_strings[] = {
+ "regex",
+ "expand",
+ "exact",
+ "one",
+ "subtree",
+ "children",
+ "level",
+ "attrof",
+ "anonymous",
+ "users",
+ "self",
+ "ip",
+ "ipv6",
+ "path",
+ NULL
+};
+
+#define ACLBUF_CHUNKSIZE 8192
+static struct berval aclbuf;
+
+static void split(char *line, int splitchar, char **left, char **right);
+static void access_append(Access **l, Access *a);
+static void access_free( Access *a );
+static int acl_usage(void);
+
+static void acl_regex_normalized_dn(const char *src, struct berval *pat);
+
+#ifdef LDAP_DEBUG
+static void print_acl(Backend *be, AccessControl *a);
+#endif
+
+static int check_scope( BackendDB *be, AccessControl *a );
+
+#ifdef SLAP_DYNACL
+static int
+slap_dynacl_config(
+ const char *fname,
+ int lineno,
+ Access *b,
+ const char *name,
+ const char *opts,
+ slap_style_t sty,
+ const char *right )
+{
+ slap_dynacl_t *da, *tmp;
+ int rc = 0;
+
+ for ( da = b->a_dynacl; da; da = da->da_next ) {
+ if ( strcasecmp( da->da_name, name ) == 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: dynacl \"%s\" already specified.\n",
+ fname, lineno, name );
+ return acl_usage();
+ }
+ }
+
+ da = slap_dynacl_get( name );
+ if ( da == NULL ) {
+ return -1;
+ }
+
+ tmp = ch_malloc( sizeof( slap_dynacl_t ) );
+ *tmp = *da;
+
+ if ( tmp->da_parse ) {
+ rc = ( *tmp->da_parse )( fname, lineno, opts, sty, right, &tmp->da_private );
+ if ( rc ) {
+ ch_free( tmp );
+ return rc;
+ }
+ }
+
+ tmp->da_next = b->a_dynacl;
+ b->a_dynacl = tmp;
+
+ return 0;
+}
+#endif /* SLAP_DYNACL */
+
+static void
+regtest(const char *fname, int lineno, char *pat) {
+ int e;
+ regex_t re;
+
+ char buf[ SLAP_TEXT_BUFLEN ];
+ unsigned size;
+
+ char *sp;
+ char *dp;
+ int flag;
+
+ sp = pat;
+ dp = buf;
+ size = 0;
+ buf[0] = '\0';
+
+ for (size = 0, flag = 0; (size < sizeof(buf)) && *sp; sp++) {
+ if (flag) {
+ if (*sp == '$'|| (*sp >= '0' && *sp <= '9')) {
+ *dp++ = *sp;
+ size++;
+ }
+ flag = 0;
+
+ } else {
+ if (*sp == '$') {
+ flag = 1;
+ } else {
+ *dp++ = *sp;
+ size++;
+ }
+ }
+ }
+
+ *dp = '\0';
+ if ( size >= (sizeof(buf) - 1) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: regular expression \"%s\" too large\n",
+ fname, lineno, pat );
+ (void)acl_usage();
+ exit( EXIT_FAILURE );
+ }
+
+ if ((e = regcomp(&re, buf, REG_EXTENDED|REG_ICASE))) {
+ char error[ SLAP_TEXT_BUFLEN ];
+
+ regerror(e, &re, error, sizeof(error));
+
+ Debug(LDAP_DEBUG_ANY,
+ "%s: line %d: regular expression \"%s\" bad because of %s\n",
+ fname, lineno, pat, error );
+ acl_usage();
+ exit( EXIT_FAILURE );
+ }
+ regfree(&re);
+}
+
+/*
+ * Experimental
+ *
+ * Check if the pattern of an ACL, if any, matches the scope
+ * of the backend it is defined within.
+ */
+#define ACL_SCOPE_UNKNOWN (-2)
+#define ACL_SCOPE_ERR (-1)
+#define ACL_SCOPE_OK (0)
+#define ACL_SCOPE_PARTIAL (1)
+#define ACL_SCOPE_WARN (2)
+
+static int
+check_scope( BackendDB *be, AccessControl *a )
+{
+ ber_len_t patlen;
+ struct berval dn;
+
+ dn = be->be_nsuffix[0];
+
+ if ( BER_BVISEMPTY( &dn ) ) {
+ return ACL_SCOPE_OK;
+ }
+
+ if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
+ a->acl_dn_style != ACL_STYLE_REGEX )
+ {
+ slap_style_t style = a->acl_dn_style;
+
+ if ( style == ACL_STYLE_REGEX ) {
+ char dnbuf[SLAP_LDAPDN_MAXLEN + 2];
+ char rebuf[SLAP_LDAPDN_MAXLEN + 1];
+ ber_len_t rebuflen;
+ regex_t re;
+ int rc;
+
+ /* add trailing '$' to database suffix to form
+ * a simple trial regex pattern "<suffix>$" */
+ AC_MEMCPY( dnbuf, be->be_nsuffix[0].bv_val,
+ be->be_nsuffix[0].bv_len );
+ dnbuf[be->be_nsuffix[0].bv_len] = '$';
+ dnbuf[be->be_nsuffix[0].bv_len + 1] = '\0';
+
+ if ( regcomp( &re, dnbuf, REG_EXTENDED|REG_ICASE ) ) {
+ return ACL_SCOPE_WARN;
+ }
+
+ /* remove trailing ')$', if any, from original
+ * regex pattern */
+ rebuflen = a->acl_dn_pat.bv_len;
+ AC_MEMCPY( rebuf, a->acl_dn_pat.bv_val, rebuflen + 1 );
+ if ( rebuf[rebuflen - 1] == '$' ) {
+ rebuf[--rebuflen] = '\0';
+ }
+ while ( rebuflen > be->be_nsuffix[0].bv_len && rebuf[rebuflen - 1] == ')' ) {
+ rebuf[--rebuflen] = '\0';
+ }
+ if ( rebuflen == be->be_nsuffix[0].bv_len ) {
+ rc = ACL_SCOPE_WARN;
+ goto regex_done;
+ }
+
+ /* not a clear indication of scoping error, though */
+ rc = regexec( &re, rebuf, 0, NULL, 0 )
+ ? ACL_SCOPE_WARN : ACL_SCOPE_OK;
+
+regex_done:;
+ regfree( &re );
+ return rc;
+ }
+
+ patlen = a->acl_dn_pat.bv_len;
+ /* If backend suffix is longer than pattern,
+ * it is a potential mismatch (in the sense
+ * that a superior naming context could
+ * match */
+ if ( dn.bv_len > patlen ) {
+ /* base is blatantly wrong */
+ if ( style == ACL_STYLE_BASE ) return ACL_SCOPE_ERR;
+
+ /* a style of one can be wrong if there is
+ * more than one level between the suffix
+ * and the pattern */
+ if ( style == ACL_STYLE_ONE ) {
+ ber_len_t rdnlen = 0;
+ int sep = 0;
+
+ if ( patlen > 0 ) {
+ if ( !DN_SEPARATOR( dn.bv_val[dn.bv_len - patlen - 1] )) {
+ return ACL_SCOPE_ERR;
+ }
+ sep = 1;
+ }
+
+ rdnlen = dn_rdnlen( NULL, &dn );
+ if ( rdnlen != dn.bv_len - patlen - sep )
+ return ACL_SCOPE_ERR;
+ }
+
+ /* if the trailing part doesn't match,
+ * then it's an error */
+ if ( strcmp( a->acl_dn_pat.bv_val,
+ &dn.bv_val[dn.bv_len - patlen] ) != 0 )
+ {
+ return ACL_SCOPE_ERR;
+ }
+
+ return ACL_SCOPE_PARTIAL;
+ }
+
+ switch ( style ) {
+ case ACL_STYLE_BASE:
+ case ACL_STYLE_ONE:
+ case ACL_STYLE_CHILDREN:
+ case ACL_STYLE_SUBTREE:
+ break;
+
+ default:
+ assert( 0 );
+ break;
+ }
+
+ if ( dn.bv_len < patlen &&
+ !DN_SEPARATOR( a->acl_dn_pat.bv_val[patlen - dn.bv_len - 1] ))
+ {
+ return ACL_SCOPE_ERR;
+ }
+
+ if ( strcmp( &a->acl_dn_pat.bv_val[patlen - dn.bv_len], dn.bv_val )
+ != 0 )
+ {
+ return ACL_SCOPE_ERR;
+ }
+
+ return ACL_SCOPE_OK;
+ }
+
+ return ACL_SCOPE_UNKNOWN;
+}
+
+int
+parse_acl(
+ Backend *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv,
+ int pos )
+{
+ int i;
+ char *left, *right, *style;
+ struct berval bv;
+ AccessControl *a = NULL;
+ Access *b = NULL;
+ int rc;
+ const char *text;
+
+ for ( i = 1; i < argc; i++ ) {
+ /* to clause - select which entries are protected */
+ if ( strcasecmp( argv[i], "to" ) == 0 ) {
+ if ( a != NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "only one to clause allowed in access line\n",
+ fname, lineno );
+ goto fail;
+ }
+ a = (AccessControl *) ch_calloc( 1, sizeof(AccessControl) );
+ a->acl_attrval_style = ACL_STYLE_NONE;
+ for ( ++i; i < argc; i++ ) {
+ if ( strcasecmp( argv[i], "by" ) == 0 ) {
+ i--;
+ break;
+ }
+
+ if ( strcasecmp( argv[i], "*" ) == 0 ) {
+ if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
+ a->acl_dn_style != ACL_STYLE_REGEX )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: dn pattern"
+ " already specified in to clause.\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );
+ continue;
+ }
+
+ split( argv[i], '=', &left, &right );
+ split( left, '.', &left, &style );
+
+ if ( right == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "missing \"=\" in \"%s\" in to clause\n",
+ fname, lineno, left );
+ goto fail;
+ }
+
+ if ( strcasecmp( left, "dn" ) == 0 ) {
+ if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
+ a->acl_dn_style != ACL_STYLE_REGEX )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: dn pattern"
+ " already specified in to clause.\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ if ( style == NULL || *style == '\0' ||
+ strcasecmp( style, "baseObject" ) == 0 ||
+ strcasecmp( style, "base" ) == 0 ||
+ strcasecmp( style, "exact" ) == 0 )
+ {
+ a->acl_dn_style = ACL_STYLE_BASE;
+ ber_str2bv( right, 0, 1, &a->acl_dn_pat );
+
+ } else if ( strcasecmp( style, "oneLevel" ) == 0 ||
+ strcasecmp( style, "one" ) == 0 )
+ {
+ a->acl_dn_style = ACL_STYLE_ONE;
+ ber_str2bv( right, 0, 1, &a->acl_dn_pat );
+
+ } else if ( strcasecmp( style, "subtree" ) == 0 ||
+ strcasecmp( style, "sub" ) == 0 )
+ {
+ if( *right == '\0' ) {
+ ber_str2bv( "*", STRLENOF( "*" ), 1, &a->acl_dn_pat );
+
+ } else {
+ a->acl_dn_style = ACL_STYLE_SUBTREE;
+ ber_str2bv( right, 0, 1, &a->acl_dn_pat );
+ }
+
+ } else if ( strcasecmp( style, "children" ) == 0 ) {
+ a->acl_dn_style = ACL_STYLE_CHILDREN;
+ ber_str2bv( right, 0, 1, &a->acl_dn_pat );
+
+ } else if ( strcasecmp( style, "regex" ) == 0 ) {
+ a->acl_dn_style = ACL_STYLE_REGEX;
+
+ if ( *right == '\0' ) {
+ /* empty regex should match empty DN */
+ a->acl_dn_style = ACL_STYLE_BASE;
+ ber_str2bv( right, 0, 1, &a->acl_dn_pat );
+
+ } else if ( strcmp(right, "*") == 0
+ || strcmp(right, ".*") == 0
+ || strcmp(right, ".*$") == 0
+ || strcmp(right, "^.*") == 0
+ || strcmp(right, "^.*$") == 0
+ || strcmp(right, ".*$$") == 0
+ || strcmp(right, "^.*$$") == 0 )
+ {
+ ber_str2bv( "*", STRLENOF("*"), 1, &a->acl_dn_pat );
+
+ } else {
+ acl_regex_normalized_dn( right, &a->acl_dn_pat );
+ }
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "unknown dn style \"%s\" in to clause\n",
+ fname, lineno, style );
+ goto fail;
+ }
+
+ continue;
+ }
+
+ if ( strcasecmp( left, "filter" ) == 0 ) {
+ if ( (a->acl_filter = str2filter( right )) == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: bad filter \"%s\" in to clause\n",
+ fname, lineno, right );
+ goto fail;
+ }
+
+ } else if ( strcasecmp( left, "attr" ) == 0 /* TOLERATED */
+ || strcasecmp( left, "attrs" ) == 0 ) /* DOCUMENTED */
+ {
+ if ( strcasecmp( left, "attr" ) == 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: \"attr\" "
+ "is deprecated (and undocumented); "
+ "use \"attrs\" instead.\n",
+ fname, lineno );
+ }
+
+ a->acl_attrs = str2anlist( a->acl_attrs,
+ right, "," );
+ if ( a->acl_attrs == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: unknown attr \"%s\" in to clause\n",
+ fname, lineno, right );
+ goto fail;
+ }
+
+ } else if ( strncasecmp( left, "val", 3 ) == 0 ) {
+ struct berval bv;
+ char *mr;
+
+ if ( !BER_BVISEMPTY( &a->acl_attrval ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: attr val already specified in to clause.\n",
+ fname, lineno );
+ goto fail;
+ }
+ if ( a->acl_attrs == NULL || !BER_BVISEMPTY( &a->acl_attrs[1].an_name ) )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: attr val requires a single attribute.\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ ber_str2bv( right, 0, 0, &bv );
+ a->acl_attrval_style = ACL_STYLE_BASE;
+
+ mr = strchr( left, '/' );
+ if ( mr != NULL ) {
+ mr[ 0 ] = '\0';
+ mr++;
+
+ a->acl_attrval_mr = mr_find( mr );
+ if ( a->acl_attrval_mr == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "invalid matching rule \"%s\".\n",
+ fname, lineno, mr );
+ goto fail;
+ }
+
+ if( !mr_usable_with_at( a->acl_attrval_mr, a->acl_attrs[ 0 ].an_desc->ad_type ) )
+ {
+ Debug(LDAP_DEBUG_ANY,
+ "%s: line %d: matching rule \"%s\" use " "with attr \"%s\" not appropriate.\n",
+ fname, lineno,
+ mr,
+ a->acl_attrs[0].an_name.bv_val );
+ goto fail;
+ }
+ }
+
+ if ( style != NULL ) {
+ if ( strcasecmp( style, "regex" ) == 0 ) {
+ int e = regcomp( &a->acl_attrval_re, bv.bv_val,
+ REG_EXTENDED | REG_ICASE );
+ if ( e ) {
+ char err[SLAP_TEXT_BUFLEN];
+
+ regerror( e, &a->acl_attrval_re, err, sizeof( err ) );
+ Debug(LDAP_DEBUG_ANY,
+ "%s: line %d: regular expression \"%s\" bad because of %s\n",
+ fname, lineno, right, err );
+ goto fail;
+ }
+ a->acl_attrval_style = ACL_STYLE_REGEX;
+
+ } else {
+ /* FIXME: if the attribute has DN syntax, we might
+ * allow one, subtree and children styles as well */
+ if ( !strcasecmp( style, "base" ) ||
+ !strcasecmp( style, "exact" ) ) {
+ a->acl_attrval_style = ACL_STYLE_BASE;
+
+ } else if ( a->acl_attrs[0].an_desc->ad_type->
+ sat_syntax == slap_schema.si_syn_distinguishedName )
+ {
+ if ( !strcasecmp( style, "baseObject" ) ||
+ !strcasecmp( style, "base" ) )
+ {
+ a->acl_attrval_style = ACL_STYLE_BASE;
+ } else if ( !strcasecmp( style, "onelevel" ) ||
+ !strcasecmp( style, "one" ) )
+ {
+ a->acl_attrval_style = ACL_STYLE_ONE;
+ } else if ( !strcasecmp( style, "subtree" ) ||
+ !strcasecmp( style, "sub" ) )
+ {
+ a->acl_attrval_style = ACL_STYLE_SUBTREE;
+ } else if ( !strcasecmp( style, "children" ) ) {
+ a->acl_attrval_style = ACL_STYLE_CHILDREN;
+ } else {
+ Debug(LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
+ "%s: line %d: unknown val.<style> \"%s\" for attributeType \"%s\" " "with DN syntax.\n",
+ fname,
+ lineno,
+ style,
+ a->acl_attrs[0].an_desc->ad_cname.bv_val );
+ goto fail;
+ }
+
+ rc = dnNormalize( 0, NULL, NULL, &bv, &a->acl_attrval, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ANY,
+ "%s: line %d: unable to normalize DN \"%s\" " "for attributeType \"%s\" (%d).\n",
+ fname,
+ lineno,
+ bv.bv_val,
+ a->acl_attrs[0].an_desc->ad_cname.bv_val,
+ rc );
+ goto fail;
+ }
+
+ } else {
+ Debug(LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
+ "%s: line %d: unknown val.<style> \"%s\" for attributeType \"%s\".\n",
+ fname,
+ lineno,
+ style,
+ a->acl_attrs[0].an_desc->ad_cname.bv_val );
+ goto fail;
+ }
+ }
+ }
+
+ /* Check for appropriate matching rule */
+ if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
+ ber_dupbv( &a->acl_attrval, &bv );
+
+ } else if ( BER_BVISNULL( &a->acl_attrval ) ) {
+ int rc;
+ const char *text;
+
+ if ( a->acl_attrval_mr == NULL ) {
+ a->acl_attrval_mr = a->acl_attrs[ 0 ].an_desc->ad_type->sat_equality;
+ }
+
+ if ( a->acl_attrval_mr == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "attr \"%s\" does not have an EQUALITY matching rule.\n",
+ fname, lineno, a->acl_attrs[ 0 ].an_name.bv_val );
+ goto fail;
+ }
+
+ rc = asserted_value_validate_normalize(
+ a->acl_attrs[ 0 ].an_desc,
+ a->acl_attrval_mr,
+ SLAP_MR_EQUALITY|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
+ &bv,
+ &a->acl_attrval,
+ &text,
+ NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ANY,
+ "%s: line %d: %s: line %d: " " attr \"%s\" normalization failed (%d: %s).\n",
+ fname, lineno,
+ fname, lineno,
+ a->acl_attrs[0].an_name.bv_val,
+ rc, text );
+ goto fail;
+ }
+ }
+
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: expecting <what> got \"%s\"\n",
+ fname, lineno, left );
+ goto fail;
+ }
+ }
+
+ if ( !BER_BVISNULL( &a->acl_dn_pat ) &&
+ ber_bvccmp( &a->acl_dn_pat, '*' ) )
+ {
+ free( a->acl_dn_pat.bv_val );
+ BER_BVZERO( &a->acl_dn_pat );
+ a->acl_dn_style = ACL_STYLE_REGEX;
+ }
+
+ if ( !BER_BVISEMPTY( &a->acl_dn_pat ) ||
+ a->acl_dn_style != ACL_STYLE_REGEX )
+ {
+ if ( a->acl_dn_style != ACL_STYLE_REGEX ) {
+ struct berval bv;
+ rc = dnNormalize( 0, NULL, NULL, &a->acl_dn_pat, &bv, NULL);
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: bad DN \"%s\" in to DN clause\n",
+ fname, lineno, a->acl_dn_pat.bv_val );
+ goto fail;
+ }
+ free( a->acl_dn_pat.bv_val );
+ a->acl_dn_pat = bv;
+
+ } else {
+ int e = regcomp( &a->acl_dn_re, a->acl_dn_pat.bv_val,
+ REG_EXTENDED | REG_ICASE );
+ if ( e ) {
+ char err[ SLAP_TEXT_BUFLEN ];
+
+ regerror( e, &a->acl_dn_re, err, sizeof( err ) );
+ Debug(LDAP_DEBUG_ANY,
+ "%s: line %d: regular expression \"%s\" bad because of %s\n",
+ fname, lineno, right, err );
+ goto fail;
+ }
+ }
+ }
+
+ /* by clause - select who has what access to entries */
+ } else if ( strcasecmp( argv[i], "by" ) == 0 ) {
+ if ( a == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "to clause required before by clause in access line\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ /*
+ * by clause consists of <who> and <access>
+ */
+
+ if ( ++i == argc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: premature EOL: expecting <who>\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ b = (Access *) ch_calloc( 1, sizeof(Access) );
+
+ ACL_INVALIDATE( b->a_access_mask );
+
+ /* get <who> */
+ for ( ; i < argc; i++ ) {
+ slap_style_t sty = ACL_STYLE_REGEX;
+ char *style_modifier = NULL;
+ char *style_level = NULL;
+ int level = 0;
+ int expand = 0;
+ slap_dn_access *bdn = &b->a_dn;
+ int is_realdn = 0;
+
+ split( argv[i], '=', &left, &right );
+ split( left, '.', &left, &style );
+ if ( style ) {
+ split( style, ',', &style, &style_modifier );
+
+ if ( strncasecmp( style, "level", STRLENOF( "level" ) ) == 0 ) {
+ split( style, '{', &style, &style_level );
+ if ( style_level != NULL ) {
+ char *p = strchr( style_level, '}' );
+ if ( p == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: premature eol: "
+ "expecting closing '}' in \"level{n}\"\n",
+ fname, lineno );
+ goto fail;
+ } else if ( p == style_level ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: empty level "
+ "in \"level{n}\"\n",
+ fname, lineno );
+ goto fail;
+ }
+ p[0] = '\0';
+ }
+ }
+ }
+
+ if ( style == NULL || *style == '\0' ||
+ strcasecmp( style, "exact" ) == 0 ||
+ strcasecmp( style, "baseObject" ) == 0 ||
+ strcasecmp( style, "base" ) == 0 )
+ {
+ sty = ACL_STYLE_BASE;
+
+ } else if ( strcasecmp( style, "onelevel" ) == 0 ||
+ strcasecmp( style, "one" ) == 0 )
+ {
+ sty = ACL_STYLE_ONE;
+
+ } else if ( strcasecmp( style, "subtree" ) == 0 ||
+ strcasecmp( style, "sub" ) == 0 )
+ {
+ sty = ACL_STYLE_SUBTREE;
+
+ } else if ( strcasecmp( style, "children" ) == 0 ) {
+ sty = ACL_STYLE_CHILDREN;
+
+ } else if ( strcasecmp( style, "level" ) == 0 )
+ {
+ if ( lutil_atoi( &level, style_level ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: unable to parse level "
+ "in \"level{n}\"\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ sty = ACL_STYLE_LEVEL;
+
+ } else if ( strcasecmp( style, "regex" ) == 0 ) {
+ sty = ACL_STYLE_REGEX;
+
+ } else if ( strcasecmp( style, "expand" ) == 0 ) {
+ sty = ACL_STYLE_EXPAND;
+
+ } else if ( strcasecmp( style, "ip" ) == 0 ) {
+ sty = ACL_STYLE_IP;
+
+ } else if ( strcasecmp( style, "ipv6" ) == 0 ) {
+#ifndef LDAP_PF_INET6
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: IPv6 not supported\n",
+ fname, lineno );
+#endif /* ! LDAP_PF_INET6 */
+ sty = ACL_STYLE_IPV6;
+
+ } else if ( strcasecmp( style, "path" ) == 0 ) {
+ sty = ACL_STYLE_PATH;
+#ifndef LDAP_PF_LOCAL
+ Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
+ "%s: line %d: "
+ "\"path\" style modifier is useless without local.\n",
+ fname, lineno );
+ goto fail;
+#endif /* LDAP_PF_LOCAL */
+
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: unknown style \"%s\" in by clause\n",
+ fname, lineno, style );
+ goto fail;
+ }
+
+ if ( style_modifier &&
+ strcasecmp( style_modifier, "expand" ) == 0 )
+ {
+ switch ( sty ) {
+ case ACL_STYLE_REGEX:
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "\"regex\" style implies \"expand\" modifier.\n",
+ fname, lineno );
+ goto fail;
+ break;
+
+ case ACL_STYLE_EXPAND:
+ break;
+
+ default:
+ /* we'll see later if it's pertinent */
+ expand = 1;
+ break;
+ }
+ }
+
+ if ( strncasecmp( left, "real", STRLENOF( "real" ) ) == 0 ) {
+ is_realdn = 1;
+ bdn = &b->a_realdn;
+ left += STRLENOF( "real" );
+ }
+
+ if ( strcasecmp( left, "*" ) == 0 ) {
+ if ( is_realdn ) {
+ goto fail;
+ }
+
+ ber_str2bv( "*", STRLENOF( "*" ), 1, &bv );
+ sty = ACL_STYLE_REGEX;
+
+ } else if ( strcasecmp( left, "anonymous" ) == 0 ) {
+ ber_str2bv("anonymous", STRLENOF( "anonymous" ), 1, &bv);
+ sty = ACL_STYLE_ANONYMOUS;
+
+ } else if ( strcasecmp( left, "users" ) == 0 ) {
+ ber_str2bv("users", STRLENOF( "users" ), 1, &bv);
+ sty = ACL_STYLE_USERS;
+
+ } else if ( strcasecmp( left, "self" ) == 0 ) {
+ ber_str2bv("self", STRLENOF( "self" ), 1, &bv);
+ sty = ACL_STYLE_SELF;
+
+ } else if ( strcasecmp( left, "dn" ) == 0 ) {
+ if ( sty == ACL_STYLE_REGEX ) {
+ bdn->a_style = ACL_STYLE_REGEX;
+ if ( right == NULL ) {
+ /* no '=' */
+ ber_str2bv("users",
+ STRLENOF( "users" ),
+ 1, &bv);
+ bdn->a_style = ACL_STYLE_USERS;
+
+ } else if (*right == '\0' ) {
+ /* dn="" */
+ ber_str2bv("anonymous",
+ STRLENOF( "anonymous" ),
+ 1, &bv);
+ bdn->a_style = ACL_STYLE_ANONYMOUS;
+
+ } else if ( strcmp( right, "*" ) == 0 ) {
+ /* dn=* */
+ /* any or users? users for now */
+ ber_str2bv("users",
+ STRLENOF( "users" ),
+ 1, &bv);
+ bdn->a_style = ACL_STYLE_USERS;
+
+ } else if ( strcmp( right, ".+" ) == 0
+ || strcmp( right, "^.+" ) == 0
+ || strcmp( right, ".+$" ) == 0
+ || strcmp( right, "^.+$" ) == 0
+ || strcmp( right, ".+$$" ) == 0
+ || strcmp( right, "^.+$$" ) == 0 )
+ {
+ ber_str2bv("users",
+ STRLENOF( "users" ),
+ 1, &bv);
+ bdn->a_style = ACL_STYLE_USERS;
+
+ } else if ( strcmp( right, ".*" ) == 0
+ || strcmp( right, "^.*" ) == 0
+ || strcmp( right, ".*$" ) == 0
+ || strcmp( right, "^.*$" ) == 0
+ || strcmp( right, ".*$$" ) == 0
+ || strcmp( right, "^.*$$" ) == 0 )
+ {
+ ber_str2bv("*",
+ STRLENOF( "*" ),
+ 1, &bv);
+
+ } else {
+ acl_regex_normalized_dn( right, &bv );
+ if ( !ber_bvccmp( &bv, '*' ) ) {
+ regtest( fname, lineno, bv.bv_val );
+ }
+ }
+
+ } else if ( right == NULL || *right == '\0' ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "missing \"=\" in (or value after) \"%s\" "
+ "in by clause\n",
+ fname, lineno, left );
+ goto fail;
+
+ } else {
+ ber_str2bv( right, 0, 1, &bv );
+ }
+
+ } else {
+ BER_BVZERO( &bv );
+ }
+
+ if ( !BER_BVISNULL( &bv ) ) {
+ if ( !BER_BVISEMPTY( &bdn->a_pat ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: dn pattern already specified.\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ if ( sty != ACL_STYLE_REGEX &&
+ sty != ACL_STYLE_ANONYMOUS &&
+ sty != ACL_STYLE_USERS &&
+ sty != ACL_STYLE_SELF &&
+ expand == 0 )
+ {
+ rc = dnNormalize(0, NULL, NULL,
+ &bv, &bdn->a_pat, NULL);
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: bad DN \"%s\" in by DN clause\n",
+ fname, lineno, bv.bv_val );
+ goto fail;
+ }
+ free( bv.bv_val );
+ if ( sty == ACL_STYLE_BASE
+ && be != NULL
+ && !BER_BVISNULL( &be->be_rootndn )
+ && dn_match( &bdn->a_pat, &be->be_rootndn ) )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: rootdn is always granted "
+ "unlimited privileges.\n",
+ fname, lineno );
+ }
+
+ } else {
+ bdn->a_pat = bv;
+ }
+ bdn->a_style = sty;
+ if ( expand ) {
+ char *exp;
+ int gotit = 0;
+
+ for ( exp = strchr( bdn->a_pat.bv_val, '$' );
+ exp && (ber_len_t)(exp - bdn->a_pat.bv_val)
+ < bdn->a_pat.bv_len;
+ exp = strchr( exp, '$' ) )
+ {
+ if ( ( isdigit( (unsigned char) exp[ 1 ] ) ||
+ exp[ 1 ] == '{' ) ) {
+ gotit = 1;
+ break;
+ }
+ }
+
+ if ( gotit == 1 ) {
+ bdn->a_expand = expand;
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "\"expand\" used with no expansions in \"pattern\".\n",
+ fname, lineno );
+ goto fail;
+ }
+ }
+ if ( sty == ACL_STYLE_SELF ) {
+ bdn->a_self_level = level;
+
+ } else {
+ if ( level < 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: bad negative level \"%d\" "
+ "in by DN clause\n",
+ fname, lineno, level );
+ goto fail;
+ } else if ( level == 1 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: \"onelevel\" should be used "
+ "instead of \"level{1}\" in by DN clause\n",
+ fname, lineno );
+ } else if ( level == 0 && sty == ACL_STYLE_LEVEL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: \"base\" should be used "
+ "instead of \"level{0}\" in by DN clause\n",
+ fname, lineno );
+ }
+
+ bdn->a_level = level;
+ }
+ continue;
+ }
+
+ if ( strcasecmp( left, "dnattr" ) == 0 ) {
+ if ( right == NULL || right[0] == '\0' ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "missing \"=\" in (or value after) \"%s\" "
+ "in by clause\n",
+ fname, lineno, left );
+ goto fail;
+ }
+
+ if( bdn->a_at != NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: dnattr already specified.\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ rc = slap_str2ad( right, &bdn->a_at, &text );
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ANY,
+ "%s: line %d: dnattr \"%s\": %s\n",
+ fname, lineno, right,
+ text );
+ goto fail;
+ }
+
+
+ if( !is_at_syntax( bdn->a_at->ad_type,
+ SLAPD_DN_SYNTAX ) &&
+ !is_at_syntax( bdn->a_at->ad_type,
+ SLAPD_NAMEUID_SYNTAX ))
+ {
+ Debug(LDAP_DEBUG_ANY,
+ "%s: line %d: dnattr \"%s\": " "inappropriate syntax: %s\n\n",
+ fname, lineno, right,
+ bdn->a_at->ad_type->sat_syntax_oid );
+ goto fail;
+ }
+
+ if( bdn->a_at->ad_type->sat_equality == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: dnattr \"%s\": "
+ "inappropriate matching (no EQUALITY)\n",
+ fname, lineno, right );
+ goto fail;
+ }
+
+ continue;
+ }
+
+ if ( strncasecmp( left, "group", STRLENOF( "group" ) ) == 0 ) {
+ char *name = NULL;
+ char *value = NULL;
+ char *attr_name = SLAPD_GROUP_ATTR;
+
+ switch ( sty ) {
+ case ACL_STYLE_REGEX:
+ /* legacy, tolerated */
+ Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
+ "%s: line %d: "
+ "deprecated group style \"regex\"; "
+ "use \"expand\" instead.\n",
+ fname, lineno );
+ sty = ACL_STYLE_EXPAND;
+ break;
+
+ case ACL_STYLE_BASE:
+ /* legal, traditional */
+ case ACL_STYLE_EXPAND:
+ /* legal, substring expansion; supersedes regex */
+ break;
+
+ default:
+ /* unknown */
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: "
+ "inappropriate style \"%s\" in by clause.\n",
+ fname, lineno, style );
+ goto fail;
+ }
+
+ if ( right == NULL || right[0] == '\0' ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: "
+ "missing \"=\" in (or value after) \"%s\" "
+ "in by clause.\n",
+ fname, lineno, left );
+ goto fail;
+ }
+
+ if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: group pattern already specified.\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ /* format of string is
+ "group/objectClassValue/groupAttrName" */
+ if ( ( value = strchr(left, '/') ) != NULL ) {
+ *value++ = '\0';
+ if ( *value && ( name = strchr( value, '/' ) ) != NULL ) {
+ *name++ = '\0';
+ }
+ }
+
+ b->a_group_style = sty;
+ if ( sty == ACL_STYLE_EXPAND ) {
+ acl_regex_normalized_dn( right, &bv );
+ if ( !ber_bvccmp( &bv, '*' ) ) {
+ regtest( fname, lineno, bv.bv_val );
+ }
+ b->a_group_pat = bv;
+
+ } else {
+ ber_str2bv( right, 0, 0, &bv );
+ rc = dnNormalize( 0, NULL, NULL, &bv,
+ &b->a_group_pat, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: bad DN \"%s\".\n",
+ fname, lineno, right );
+ goto fail;
+ }
+ }
+
+ if ( value && *value ) {
+ b->a_group_oc = oc_find( value );
+ *--value = '/';
+
+ if ( b->a_group_oc == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: group objectclass "
+ "\"%s\" unknown.\n",
+ fname, lineno, value );
+ goto fail;
+ }
+
+ } else {
+ b->a_group_oc = oc_find( SLAPD_GROUP_CLASS );
+
+ if( b->a_group_oc == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: group default objectclass "
+ "\"%s\" unknown.\n",
+ fname, lineno, SLAPD_GROUP_CLASS );
+ goto fail;
+ }
+ }
+
+ if ( is_object_subclass( slap_schema.si_oc_referral,
+ b->a_group_oc ) )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: group objectclass \"%s\" "
+ "is subclass of referral.\n",
+ fname, lineno, value );
+ goto fail;
+ }
+
+ if ( is_object_subclass( slap_schema.si_oc_alias,
+ b->a_group_oc ) )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: group objectclass \"%s\" "
+ "is subclass of alias.\n",
+ fname, lineno, value );
+ goto fail;
+ }
+
+ if ( name && *name ) {
+ attr_name = name;
+ *--name = '/';
+
+ }
+
+ rc = slap_str2ad( attr_name, &b->a_group_at, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ANY,
+ "%s: line %d: group \"%s\": %s.\n",
+ fname, lineno, right,
+ text );
+ goto fail;
+ }
+
+ if ( !is_at_syntax( b->a_group_at->ad_type,
+ SLAPD_DN_SYNTAX ) /* e.g. "member" */
+ && !is_at_syntax( b->a_group_at->ad_type,
+ SLAPD_NAMEUID_SYNTAX ) /* e.g. memberUID */
+ && !is_at_subtype( b->a_group_at->ad_type,
+ slap_schema.si_ad_labeledURI->ad_type ) /* e.g. memberURL */ )
+ {
+ Debug(LDAP_DEBUG_ANY,
+ "%s: line %d: group \"%s\" attr \"%s\": inappropriate syntax: %s; " "must be " SLAPD_DN_SYNTAX " (DN), " SLAPD_NAMEUID_SYNTAX " (NameUID) " "or a subtype of labeledURI.\n",
+ fname, lineno, right,
+ attr_name,
+ at_syntax(b->a_group_at->ad_type) );
+ goto fail;
+ }
+
+
+ {
+ int rc;
+ ObjectClass *ocs[2];
+
+ ocs[0] = b->a_group_oc;
+ ocs[1] = NULL;
+
+ rc = oc_check_allowed( b->a_group_at->ad_type,
+ ocs, NULL );
+
+ if( rc != 0 ) {
+ Debug(LDAP_DEBUG_ANY,
+ "%s: line %d: group: \"%s\" not allowed by \"%s\".\n",
+ fname, lineno,
+ b->a_group_at->ad_cname.bv_val,
+ b->a_group_oc->soc_oid );
+ goto fail;
+ }
+ }
+ continue;
+ }
+
+ if ( strcasecmp( left, "peername" ) == 0 ) {
+ switch ( sty ) {
+ case ACL_STYLE_REGEX:
+ case ACL_STYLE_BASE:
+ /* legal, traditional */
+ case ACL_STYLE_EXPAND:
+ /* cheap replacement to regex for simple expansion */
+ case ACL_STYLE_IP:
+ case ACL_STYLE_IPV6:
+ case ACL_STYLE_PATH:
+ /* legal, peername specific */
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "inappropriate style \"%s\" in by clause.\n",
+ fname, lineno, style );
+ goto fail;
+ }
+
+ if ( right == NULL || right[0] == '\0' ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "missing \"=\" in (or value after) \"%s\" "
+ "in by clause.\n",
+ fname, lineno, left );
+ goto fail;
+ }
+
+ if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "peername pattern already specified.\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ b->a_peername_style = sty;
+ if ( sty == ACL_STYLE_REGEX ) {
+ acl_regex_normalized_dn( right, &bv );
+ if ( !ber_bvccmp( &bv, '*' ) ) {
+ regtest( fname, lineno, bv.bv_val );
+ }
+ b->a_peername_pat = bv;
+
+ } else {
+ ber_str2bv( right, 0, 1, &b->a_peername_pat );
+
+ if ( sty == ACL_STYLE_IP ) {
+ char *addr = NULL,
+ *mask = NULL,
+ *port = NULL;
+
+ split( right, '{', &addr, &port );
+ split( addr, '%', &addr, &mask );
+
+ b->a_peername_addr = inet_addr( addr );
+ if ( b->a_peername_addr == (unsigned long)(-1) ) {
+ /* illegal address */
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "illegal peername address \"%s\".\n",
+ fname, lineno, addr );
+ goto fail;
+ }
+
+ b->a_peername_mask = (unsigned long)(-1);
+ if ( mask != NULL ) {
+ b->a_peername_mask = inet_addr( mask );
+ if ( b->a_peername_mask ==
+ (unsigned long)(-1) )
+ {
+ /* illegal mask */
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "illegal peername address mask "
+ "\"%s\".\n",
+ fname, lineno, mask );
+ goto fail;
+ }
+ }
+
+ b->a_peername_port = -1;
+ if ( port ) {
+ char *end = NULL;
+
+ b->a_peername_port = strtol( port, &end, 10 );
+ if ( end == port || end[0] != '}' ) {
+ /* illegal port */
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "illegal peername port specification "
+ "\"{%s}\".\n",
+ fname, lineno, port );
+ goto fail;
+ }
+ }
+
+#ifdef LDAP_PF_INET6
+ } else if ( sty == ACL_STYLE_IPV6 ) {
+ char *addr = NULL,
+ *mask = NULL,
+ *port = NULL;
+
+ split( right, '{', &addr, &port );
+ split( addr, '%', &addr, &mask );
+
+ if ( inet_pton( AF_INET6, addr, &b->a_peername_addr6 ) != 1 ) {
+ /* illegal address */
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "illegal peername address \"%s\".\n",
+ fname, lineno, addr );
+ goto fail;
+ }
+
+ if ( mask == NULL ) {
+ mask = "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF";
+ }
+
+ if ( inet_pton( AF_INET6, mask, &b->a_peername_mask6 ) != 1 ) {
+ /* illegal mask */
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "illegal peername address mask "
+ "\"%s\".\n",
+ fname, lineno, mask );
+ goto fail;
+ }
+
+ b->a_peername_port = -1;
+ if ( port ) {
+ char *end = NULL;
+
+ b->a_peername_port = strtol( port, &end, 10 );
+ if ( end == port || end[0] != '}' ) {
+ /* illegal port */
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "illegal peername port specification "
+ "\"{%s}\".\n",
+ fname, lineno, port );
+ goto fail;
+ }
+ }
+#endif /* LDAP_PF_INET6 */
+ }
+ }
+ continue;
+ }
+
+ if ( strcasecmp( left, "sockname" ) == 0 ) {
+ switch ( sty ) {
+ case ACL_STYLE_REGEX:
+ case ACL_STYLE_BASE:
+ /* legal, traditional */
+ case ACL_STYLE_EXPAND:
+ /* cheap replacement to regex for simple expansion */
+ break;
+
+ default:
+ /* unknown */
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "inappropriate style \"%s\" in by clause\n",
+ fname, lineno, style );
+ goto fail;
+ }
+
+ if ( right == NULL || right[0] == '\0' ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "missing \"=\" in (or value after) \"%s\" "
+ "in by clause\n",
+ fname, lineno, left );
+ goto fail;
+ }
+
+ if ( !BER_BVISNULL( &b->a_sockname_pat ) ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "sockname pattern already specified.\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ b->a_sockname_style = sty;
+ if ( sty == ACL_STYLE_REGEX ) {
+ acl_regex_normalized_dn( right, &bv );
+ if ( !ber_bvccmp( &bv, '*' ) ) {
+ regtest( fname, lineno, bv.bv_val );
+ }
+ b->a_sockname_pat = bv;
+
+ } else {
+ ber_str2bv( right, 0, 1, &b->a_sockname_pat );
+ }
+ continue;
+ }
+
+ if ( strcasecmp( left, "domain" ) == 0 ) {
+ switch ( sty ) {
+ case ACL_STYLE_REGEX:
+ case ACL_STYLE_BASE:
+ case ACL_STYLE_SUBTREE:
+ /* legal, traditional */
+ break;
+
+ case ACL_STYLE_EXPAND:
+ /* tolerated: means exact,expand */
+ if ( expand ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: "
+ "\"expand\" modifier "
+ "with \"expand\" style.\n",
+ fname, lineno );
+ }
+ sty = ACL_STYLE_BASE;
+ expand = 1;
+ break;
+
+ default:
+ /* unknown */
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "inappropriate style \"%s\" in by clause.\n",
+ fname, lineno, style );
+ goto fail;
+ }
+
+ if ( right == NULL || right[0] == '\0' ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "missing \"=\" in (or value after) \"%s\" "
+ "in by clause.\n",
+ fname, lineno, left );
+ goto fail;
+ }
+
+ if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: domain pattern already specified.\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ b->a_domain_style = sty;
+ b->a_domain_expand = expand;
+ if ( sty == ACL_STYLE_REGEX ) {
+ acl_regex_normalized_dn( right, &bv );
+ if ( !ber_bvccmp( &bv, '*' ) ) {
+ regtest( fname, lineno, bv.bv_val );
+ }
+ b->a_domain_pat = bv;
+
+ } else {
+ ber_str2bv( right, 0, 1, &b->a_domain_pat );
+ }
+ continue;
+ }
+
+ if ( strcasecmp( left, "sockurl" ) == 0 ) {
+ switch ( sty ) {
+ case ACL_STYLE_REGEX:
+ case ACL_STYLE_BASE:
+ /* legal, traditional */
+ case ACL_STYLE_EXPAND:
+ /* cheap replacement to regex for simple expansion */
+ break;
+
+ default:
+ /* unknown */
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "inappropriate style \"%s\" in by clause.\n",
+ fname, lineno, style );
+ goto fail;
+ }
+
+ if ( right == NULL || right[0] == '\0' ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "missing \"=\" in (or value after) \"%s\" "
+ "in by clause.\n",
+ fname, lineno, left );
+ goto fail;
+ }
+
+ if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: sockurl pattern already specified.\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ b->a_sockurl_style = sty;
+ if ( sty == ACL_STYLE_REGEX ) {
+ acl_regex_normalized_dn( right, &bv );
+ if ( !ber_bvccmp( &bv, '*' ) ) {
+ regtest( fname, lineno, bv.bv_val );
+ }
+ b->a_sockurl_pat = bv;
+
+ } else {
+ ber_str2bv( right, 0, 1, &b->a_sockurl_pat );
+ }
+ continue;
+ }
+
+ if ( strcasecmp( left, "set" ) == 0 ) {
+ switch ( sty ) {
+ /* deprecated */
+ case ACL_STYLE_REGEX:
+ Debug( LDAP_DEBUG_CONFIG | LDAP_DEBUG_ACL,
+ "%s: line %d: "
+ "deprecated set style "
+ "\"regex\" in <by> clause; "
+ "use \"expand\" instead.\n",
+ fname, lineno );
+ sty = ACL_STYLE_EXPAND;
+ /* FALLTHRU */
+
+ case ACL_STYLE_BASE:
+ case ACL_STYLE_EXPAND:
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "inappropriate style \"%s\" in by clause.\n",
+ fname, lineno, style );
+ goto fail;
+ }
+
+ if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: set attribute already specified.\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ if ( right == NULL || *right == '\0' ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: no set is defined.\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ b->a_set_style = sty;
+ ber_str2bv( right, 0, 1, &b->a_set_pat );
+
+ continue;
+ }
+
+#ifdef SLAP_DYNACL
+ {
+ char *name = NULL,
+ *opts = NULL;
+
+#if 1 /* tolerate legacy "aci" <who> */
+ if ( strcasecmp( left, "aci" ) == 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "undocumented deprecated \"aci\" directive "
+ "is superseded by \"dynacl/aci\".\n",
+ fname, lineno );
+ name = "aci";
+
+ } else
+#endif /* tolerate legacy "aci" <who> */
+ if ( strncasecmp( left, "dynacl/", STRLENOF( "dynacl/" ) ) == 0 ) {
+ name = &left[ STRLENOF( "dynacl/" ) ];
+ opts = strchr( name, '/' );
+ if ( opts ) {
+ opts[ 0 ] = '\0';
+ opts++;
+ }
+ }
+
+ if ( name ) {
+ if ( slap_dynacl_config( fname, lineno, b, name, opts, sty, right ) ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "unable to configure dynacl \"%s\".\n",
+ fname, lineno, name );
+ goto fail;
+ }
+
+ continue;
+ }
+ }
+#endif /* SLAP_DYNACL */
+
+ if ( strcasecmp( left, "ssf" ) == 0 ) {
+ if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "inappropriate style \"%s\" in by clause.\n",
+ fname, lineno, style );
+ goto fail;
+ }
+
+ if ( b->a_authz.sai_ssf ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: ssf attribute already specified.\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ if ( right == NULL || *right == '\0' ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: no ssf is defined.\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ if ( lutil_atou( &b->a_authz.sai_ssf, right ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: unable to parse ssf value (%s).\n",
+ fname, lineno, right );
+ goto fail;
+ }
+
+ if ( !b->a_authz.sai_ssf ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: invalid ssf value (%s).\n",
+ fname, lineno, right );
+ goto fail;
+ }
+ continue;
+ }
+
+ if ( strcasecmp( left, "transport_ssf" ) == 0 ) {
+ if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "inappropriate style \"%s\" in by clause.\n",
+ fname, lineno, style );
+ goto fail;
+ }
+
+ if ( b->a_authz.sai_transport_ssf ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "transport_ssf attribute already specified.\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ if ( right == NULL || *right == '\0' ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: no transport_ssf is defined.\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ if ( lutil_atou( &b->a_authz.sai_transport_ssf, right ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "unable to parse transport_ssf value (%s).\n",
+ fname, lineno, right );
+ goto fail;
+ }
+
+ if ( !b->a_authz.sai_transport_ssf ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: invalid transport_ssf value (%s).\n",
+ fname, lineno, right );
+ goto fail;
+ }
+ continue;
+ }
+
+ if ( strcasecmp( left, "tls_ssf" ) == 0 ) {
+ if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "inappropriate style \"%s\" in by clause.\n",
+ fname, lineno, style );
+ goto fail;
+ }
+
+ if ( b->a_authz.sai_tls_ssf ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "tls_ssf attribute already specified.\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ if ( right == NULL || *right == '\0' ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: no tls_ssf is defined\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ if ( lutil_atou( &b->a_authz.sai_tls_ssf, right ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "unable to parse tls_ssf value (%s).\n",
+ fname, lineno, right );
+ goto fail;
+ }
+
+ if ( !b->a_authz.sai_tls_ssf ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: invalid tls_ssf value (%s).\n",
+ fname, lineno, right );
+ goto fail;
+ }
+ continue;
+ }
+
+ if ( strcasecmp( left, "sasl_ssf" ) == 0 ) {
+ if ( sty != ACL_STYLE_REGEX && sty != ACL_STYLE_BASE ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "inappropriate style \"%s\" in by clause.\n",
+ fname, lineno, style );
+ goto fail;
+ }
+
+ if ( b->a_authz.sai_sasl_ssf ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "sasl_ssf attribute already specified.\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ if ( right == NULL || *right == '\0' ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: no sasl_ssf is defined.\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ if ( lutil_atou( &b->a_authz.sai_sasl_ssf, right ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "unable to parse sasl_ssf value (%s).\n",
+ fname, lineno, right );
+ goto fail;
+ }
+
+ if ( !b->a_authz.sai_sasl_ssf ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: invalid sasl_ssf value (%s).\n",
+ fname, lineno, right );
+ goto fail;
+ }
+ continue;
+ }
+
+ if ( right != NULL ) {
+ /* unsplit */
+ right[-1] = '=';
+ }
+ break;
+ }
+
+ if ( i == argc || ( strcasecmp( left, "stop" ) == 0 ) ) {
+ /* out of arguments or plain stop */
+
+ ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
+ ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
+ b->a_type = ACL_STOP;
+
+ access_append( &a->acl_access, b );
+ continue;
+ }
+
+ if ( strcasecmp( left, "continue" ) == 0 ) {
+ /* plain continue */
+
+ ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
+ ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
+ b->a_type = ACL_CONTINUE;
+
+ access_append( &a->acl_access, b );
+ continue;
+ }
+
+ if ( strcasecmp( left, "break" ) == 0 ) {
+ /* plain continue */
+
+ ACL_PRIV_ASSIGN(b->a_access_mask, ACL_PRIV_ADDITIVE);
+ ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
+ b->a_type = ACL_BREAK;
+
+ access_append( &a->acl_access, b );
+ continue;
+ }
+
+ if ( strcasecmp( left, "by" ) == 0 ) {
+ /* we've gone too far */
+ --i;
+ ACL_PRIV_ASSIGN( b->a_access_mask, ACL_PRIV_ADDITIVE );
+ ACL_PRIV_SET( b->a_access_mask, ACL_PRIV_NONE);
+ b->a_type = ACL_STOP;
+
+ access_append( &a->acl_access, b );
+ continue;
+ }
+
+ /* get <access> */
+ {
+ char *lleft = left;
+
+ if ( strncasecmp( left, "self", STRLENOF( "self" ) ) == 0 ) {
+ b->a_dn_self = 1;
+ lleft = &left[ STRLENOF( "self" ) ];
+
+ } else if ( strncasecmp( left, "realself", STRLENOF( "realself" ) ) == 0 ) {
+ b->a_realdn_self = 1;
+ lleft = &left[ STRLENOF( "realself" ) ];
+ }
+
+ ACL_PRIV_ASSIGN( b->a_access_mask, str2accessmask( lleft ) );
+ }
+
+ if ( ACL_IS_INVALID( b->a_access_mask ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: expecting <access> got \"%s\".\n",
+ fname, lineno, left );
+ goto fail;
+ }
+
+ b->a_type = ACL_STOP;
+
+ if ( ++i == argc ) {
+ /* out of arguments or plain stop */
+ access_append( &a->acl_access, b );
+ continue;
+ }
+
+ if ( strcasecmp( argv[i], "continue" ) == 0 ) {
+ /* plain continue */
+ b->a_type = ACL_CONTINUE;
+
+ } else if ( strcasecmp( argv[i], "break" ) == 0 ) {
+ /* plain continue */
+ b->a_type = ACL_BREAK;
+
+ } else if ( strcasecmp( argv[i], "stop" ) != 0 ) {
+ /* gone to far */
+ i--;
+ }
+
+ access_append( &a->acl_access, b );
+ b = NULL;
+
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: expecting \"to\" "
+ "or \"by\" got \"%s\"\n",
+ fname, lineno, argv[i] );
+ goto fail;
+ }
+ }
+
+ /* if we have no real access clause, complain and do nothing */
+ if ( a == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "warning: no access clause(s) specified in access line.\n",
+ fname, lineno );
+ goto fail;
+
+ } else {
+#ifdef LDAP_DEBUG
+ if ( slap_debug & LDAP_DEBUG_ACL ) {
+ print_acl( be, a );
+ }
+#endif
+
+ if ( a->acl_access == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "warning: no by clause(s) specified in access line.\n",
+ fname, lineno );
+ goto fail;
+ }
+
+ if ( be != NULL ) {
+ if ( be->be_nsuffix == NULL ) {
+ Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
+ "scope checking needs suffix before ACLs.\n",
+ fname, lineno );
+ /* go ahead, since checking is not authoritative */
+ } else if ( !BER_BVISNULL( &be->be_nsuffix[ 1 ] ) ) {
+ Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
+ "scope checking only applies to single-valued "
+ "suffix databases\n",
+ fname, lineno );
+ /* go ahead, since checking is not authoritative */
+ } else {
+ switch ( check_scope( be, a ) ) {
+ case ACL_SCOPE_UNKNOWN:
+ Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
+ "cannot assess the validity of the ACL scope within "
+ "backend naming context\n",
+ fname, lineno );
+ break;
+
+ case ACL_SCOPE_WARN:
+ Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
+ "ACL could be out of scope within backend naming context\n",
+ fname, lineno );
+ break;
+
+ case ACL_SCOPE_PARTIAL:
+ Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
+ "ACL appears to be partially out of scope within "
+ "backend naming context\n",
+ fname, lineno );
+ break;
+
+ case ACL_SCOPE_ERR:
+ Debug( LDAP_DEBUG_ACL, "%s: line %d: warning: "
+ "ACL appears to be out of scope within "
+ "backend naming context\n",
+ fname, lineno );
+ break;
+
+ default:
+ break;
+ }
+ }
+ acl_append( &be->be_acl, a, pos );
+
+ } else {
+ acl_append( &frontendDB->be_acl, a, pos );
+ }
+ }
+
+ return 0;
+
+fail:
+ if ( b ) access_free( b );
+ if ( a ) acl_free( a );
+ return acl_usage();
+}
+
+char *
+accessmask2str( slap_mask_t mask, char *buf, int debug )
+{
+ int none = 1;
+ char *ptr = buf;
+
+ assert( buf != NULL );
+
+ if ( ACL_IS_INVALID( mask ) ) {
+ return "invalid";
+ }
+
+ buf[0] = '\0';
+
+ if ( ACL_IS_LEVEL( mask ) ) {
+ if ( ACL_LVL_IS_NONE(mask) ) {
+ ptr = lutil_strcopy( ptr, "none" );
+
+ } else if ( ACL_LVL_IS_DISCLOSE(mask) ) {
+ ptr = lutil_strcopy( ptr, "disclose" );
+
+ } else if ( ACL_LVL_IS_AUTH(mask) ) {
+ ptr = lutil_strcopy( ptr, "auth" );
+
+ } else if ( ACL_LVL_IS_COMPARE(mask) ) {
+ ptr = lutil_strcopy( ptr, "compare" );
+
+ } else if ( ACL_LVL_IS_SEARCH(mask) ) {
+ ptr = lutil_strcopy( ptr, "search" );
+
+ } else if ( ACL_LVL_IS_READ(mask) ) {
+ ptr = lutil_strcopy( ptr, "read" );
+
+ } else if ( ACL_LVL_IS_WRITE(mask) ) {
+ ptr = lutil_strcopy( ptr, "write" );
+
+ } else if ( ACL_LVL_IS_WADD(mask) ) {
+ ptr = lutil_strcopy( ptr, "add" );
+
+ } else if ( ACL_LVL_IS_WDEL(mask) ) {
+ ptr = lutil_strcopy( ptr, "delete" );
+
+ } else if ( ACL_LVL_IS_MANAGE(mask) ) {
+ ptr = lutil_strcopy( ptr, "manage" );
+
+ } else {
+ ptr = lutil_strcopy( ptr, "unknown" );
+ }
+
+ if ( !debug ) {
+ *ptr = '\0';
+ return buf;
+ }
+ *ptr++ = '(';
+ }
+
+ if( ACL_IS_ADDITIVE( mask ) ) {
+ *ptr++ = '+';
+
+ } else if( ACL_IS_SUBTRACTIVE( mask ) ) {
+ *ptr++ = '-';
+
+ } else {
+ *ptr++ = '=';
+ }
+
+ if ( ACL_PRIV_ISSET(mask, ACL_PRIV_MANAGE) ) {
+ none = 0;
+ *ptr++ = 'm';
+ }
+
+ if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WRITE) ) {
+ none = 0;
+ *ptr++ = 'w';
+
+ } else if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WADD) ) {
+ none = 0;
+ *ptr++ = 'a';
+
+ } else if ( ACL_PRIV_ISSET(mask, ACL_PRIV_WDEL) ) {
+ none = 0;
+ *ptr++ = 'z';
+ }
+
+ if ( ACL_PRIV_ISSET(mask, ACL_PRIV_READ) ) {
+ none = 0;
+ *ptr++ = 'r';
+ }
+
+ if ( ACL_PRIV_ISSET(mask, ACL_PRIV_SEARCH) ) {
+ none = 0;
+ *ptr++ = 's';
+ }
+
+ if ( ACL_PRIV_ISSET(mask, ACL_PRIV_COMPARE) ) {
+ none = 0;
+ *ptr++ = 'c';
+ }
+
+ if ( ACL_PRIV_ISSET(mask, ACL_PRIV_AUTH) ) {
+ none = 0;
+ *ptr++ = 'x';
+ }
+
+ if ( ACL_PRIV_ISSET(mask, ACL_PRIV_DISCLOSE) ) {
+ none = 0;
+ *ptr++ = 'd';
+ }
+
+ if ( none && ACL_PRIV_ISSET(mask, ACL_PRIV_NONE) ) {
+ none = 0;
+ *ptr++ = '0';
+ }
+
+ if ( none ) {
+ ptr = buf;
+ }
+
+ if ( ACL_IS_LEVEL( mask ) ) {
+ *ptr++ = ')';
+ }
+
+ *ptr = '\0';
+
+ return buf;
+}
+
+slap_mask_t
+str2accessmask( const char *str )
+{
+ slap_mask_t mask;
+
+ if( !ASCII_ALPHA(str[0]) ) {
+ int i;
+
+ if ( str[0] == '=' ) {
+ ACL_INIT(mask);
+
+ } else if( str[0] == '+' ) {
+ ACL_PRIV_ASSIGN(mask, ACL_PRIV_ADDITIVE);
+
+ } else if( str[0] == '-' ) {
+ ACL_PRIV_ASSIGN(mask, ACL_PRIV_SUBSTRACTIVE);
+
+ } else {
+ ACL_INVALIDATE(mask);
+ return mask;
+ }
+
+ for( i=1; str[i] != '\0'; i++ ) {
+ if( TOLOWER((unsigned char) str[i]) == 'm' ) {
+ ACL_PRIV_SET(mask, ACL_PRIV_MANAGE);
+
+ } else if( TOLOWER((unsigned char) str[i]) == 'w' ) {
+ ACL_PRIV_SET(mask, ACL_PRIV_WRITE);
+
+ } else if( TOLOWER((unsigned char) str[i]) == 'a' ) {
+ ACL_PRIV_SET(mask, ACL_PRIV_WADD);
+
+ } else if( TOLOWER((unsigned char) str[i]) == 'z' ) {
+ ACL_PRIV_SET(mask, ACL_PRIV_WDEL);
+
+ } else if( TOLOWER((unsigned char) str[i]) == 'r' ) {
+ ACL_PRIV_SET(mask, ACL_PRIV_READ);
+
+ } else if( TOLOWER((unsigned char) str[i]) == 's' ) {
+ ACL_PRIV_SET(mask, ACL_PRIV_SEARCH);
+
+ } else if( TOLOWER((unsigned char) str[i]) == 'c' ) {
+ ACL_PRIV_SET(mask, ACL_PRIV_COMPARE);
+
+ } else if( TOLOWER((unsigned char) str[i]) == 'x' ) {
+ ACL_PRIV_SET(mask, ACL_PRIV_AUTH);
+
+ } else if( TOLOWER((unsigned char) str[i]) == 'd' ) {
+ ACL_PRIV_SET(mask, ACL_PRIV_DISCLOSE);
+
+ } else if( str[i] == '0' ) {
+ ACL_PRIV_SET(mask, ACL_PRIV_NONE);
+
+ } else {
+ ACL_INVALIDATE(mask);
+ return mask;
+ }
+ }
+
+ return mask;
+ }
+
+ if ( strcasecmp( str, "none" ) == 0 ) {
+ ACL_LVL_ASSIGN_NONE(mask);
+
+ } else if ( strcasecmp( str, "disclose" ) == 0 ) {
+ ACL_LVL_ASSIGN_DISCLOSE(mask);
+
+ } else if ( strcasecmp( str, "auth" ) == 0 ) {
+ ACL_LVL_ASSIGN_AUTH(mask);
+
+ } else if ( strcasecmp( str, "compare" ) == 0 ) {
+ ACL_LVL_ASSIGN_COMPARE(mask);
+
+ } else if ( strcasecmp( str, "search" ) == 0 ) {
+ ACL_LVL_ASSIGN_SEARCH(mask);
+
+ } else if ( strcasecmp( str, "read" ) == 0 ) {
+ ACL_LVL_ASSIGN_READ(mask);
+
+ } else if ( strcasecmp( str, "add" ) == 0 ) {
+ ACL_LVL_ASSIGN_WADD(mask);
+
+ } else if ( strcasecmp( str, "delete" ) == 0 ) {
+ ACL_LVL_ASSIGN_WDEL(mask);
+
+ } else if ( strcasecmp( str, "write" ) == 0 ) {
+ ACL_LVL_ASSIGN_WRITE(mask);
+
+ } else if ( strcasecmp( str, "manage" ) == 0 ) {
+ ACL_LVL_ASSIGN_MANAGE(mask);
+
+ } else {
+ ACL_INVALIDATE( mask );
+ }
+
+ return mask;
+}
+
+static int
+acl_usage( void )
+{
+ char *access =
+ "<access clause> ::= access to <what> "
+ "[ by <who> [ <access> ] [ <control> ] ]+ \n";
+ char *what =
+ "<what> ::= * | dn[.<dnstyle>=<DN>] [filter=<filter>] [attrs=<attrspec>]\n"
+ "<attrspec> ::= <attrname> [val[/<matchingRule>][.<attrstyle>]=<value>] | <attrlist>\n"
+ "<attrlist> ::= <attr> [ , <attrlist> ]\n"
+ "<attr> ::= <attrname> | @<objectClass> | !<objectClass> | entry | children\n";
+
+ char *who =
+ "<who> ::= [ * | anonymous | users | self | dn[.<dnstyle>]=<DN> ]\n"
+ "\t[ realanonymous | realusers | realself | realdn[.<dnstyle>]=<DN> ]\n"
+ "\t[dnattr=<attrname>]\n"
+ "\t[realdnattr=<attrname>]\n"
+ "\t[group[/<objectclass>[/<attrname>]][.<style>]=<group>]\n"
+ "\t[peername[.<peernamestyle>]=<peer>] [sockname[.<style>]=<name>]\n"
+ "\t[domain[.<domainstyle>]=<domain>] [sockurl[.<style>]=<url>]\n"
+#ifdef SLAP_DYNACL
+ "\t[dynacl/<name>[/<options>][.<dynstyle>][=<pattern>]]\n"
+#endif /* SLAP_DYNACL */
+ "\t[ssf=<n>] [transport_ssf=<n>] [tls_ssf=<n>] [sasl_ssf=<n>]\n"
+ "<style> ::= exact | regex | base(Object)\n"
+ "<dnstyle> ::= base(Object) | one(level) | sub(tree) | children | "
+ "exact | regex\n"
+ "<attrstyle> ::= exact | regex | base(Object) | one(level) | "
+ "sub(tree) | children\n"
+ "<peernamestyle> ::= exact | regex | ip | ipv6 | path\n"
+ "<domainstyle> ::= exact | regex | base(Object) | sub(tree)\n"
+ "<access> ::= [[real]self]{<level>|<priv>}\n"
+ "<level> ::= none|disclose|auth|compare|search|read|{write|add|delete}|manage\n"
+ "<priv> ::= {=|+|-}{0|d|x|c|s|r|{w|a|z}|m}+\n"
+ "<control> ::= [ stop | continue | break ]\n"
+#ifdef SLAP_DYNACL
+#ifdef SLAPD_ACI_ENABLED
+ "dynacl:\n"
+ "\t<name>=ACI\t<pattern>=<attrname>\n"
+#endif /* SLAPD_ACI_ENABLED */
+#endif /* ! SLAP_DYNACL */
+ "";
+
+ Debug( LDAP_DEBUG_ANY, "%s%s%s\n", access, what, who );
+
+ return 1;
+}
+
+/*
+ * Set pattern to a "normalized" DN from src.
+ * At present it simply eats the (optional) space after
+ * a RDN separator (,)
+ * Eventually will evolve in a more complete normalization
+ */
+static void
+acl_regex_normalized_dn(
+ const char *src,
+ struct berval *pattern )
+{
+ char *str, *p;
+ ber_len_t len;
+
+ str = ch_strdup( src );
+ len = strlen( src );
+
+ for ( p = str; p && p[0]; p++ ) {
+ /* escape */
+ if ( p[0] == '\\' && p[1] ) {
+ /*
+ * if escaping a hex pair we should
+ * increment p twice; however, in that
+ * case the second hex number does
+ * no harm
+ */
+ p++;
+ }
+
+ if ( p[0] == ',' && p[1] == ' ' ) {
+ char *q;
+
+ /*
+ * too much space should be an error if we are pedantic
+ */
+ for ( q = &p[2]; q[0] == ' '; q++ ) {
+ /* DO NOTHING */ ;
+ }
+ AC_MEMCPY( p+1, q, len-(q-str)+1);
+ }
+ }
+ pattern->bv_val = str;
+ pattern->bv_len = p - str;
+
+ return;
+}
+
+static void
+split(
+ char *line,
+ int splitchar,
+ char **left,
+ char **right )
+{
+ *left = line;
+ if ( (*right = strchr( line, splitchar )) != NULL ) {
+ *((*right)++) = '\0';
+ }
+}
+
+static void
+access_append( Access **l, Access *a )
+{
+ for ( ; *l != NULL; l = &(*l)->a_next ) {
+ ; /* Empty */
+ }
+
+ *l = a;
+}
+
+void
+acl_append( AccessControl **l, AccessControl *a, int pos )
+{
+ int i;
+
+ for (i=0 ; i != pos && *l != NULL; l = &(*l)->acl_next, i++ ) {
+ ; /* Empty */
+ }
+ if ( *l && a )
+ a->acl_next = *l;
+ *l = a;
+}
+
+static void
+access_free( Access *a )
+{
+ if ( !BER_BVISNULL( &a->a_dn_pat ) ) {
+ free( a->a_dn_pat.bv_val );
+ }
+ if ( !BER_BVISNULL( &a->a_realdn_pat ) ) {
+ free( a->a_realdn_pat.bv_val );
+ }
+ if ( !BER_BVISNULL( &a->a_peername_pat ) ) {
+ free( a->a_peername_pat.bv_val );
+ }
+ if ( !BER_BVISNULL( &a->a_sockname_pat ) ) {
+ free( a->a_sockname_pat.bv_val );
+ }
+ if ( !BER_BVISNULL( &a->a_domain_pat ) ) {
+ free( a->a_domain_pat.bv_val );
+ }
+ if ( !BER_BVISNULL( &a->a_sockurl_pat ) ) {
+ free( a->a_sockurl_pat.bv_val );
+ }
+ if ( !BER_BVISNULL( &a->a_set_pat ) ) {
+ free( a->a_set_pat.bv_val );
+ }
+ if ( !BER_BVISNULL( &a->a_group_pat ) ) {
+ free( a->a_group_pat.bv_val );
+ }
+#ifdef SLAP_DYNACL
+ if ( a->a_dynacl != NULL ) {
+ slap_dynacl_t *da;
+ for ( da = a->a_dynacl; da; ) {
+ slap_dynacl_t *tmp = da;
+
+ da = da->da_next;
+
+ if ( tmp->da_destroy ) {
+ tmp->da_destroy( tmp->da_private );
+ }
+
+ ch_free( tmp );
+ }
+ }
+#endif /* SLAP_DYNACL */
+ free( a );
+}
+
+void
+acl_free( AccessControl *a )
+{
+ Access *n;
+ AttributeName *an;
+
+ if ( a->acl_filter ) {
+ filter_free( a->acl_filter );
+ }
+ if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
+ if ( a->acl_dn_style == ACL_STYLE_REGEX ) {
+ regfree( &a->acl_dn_re );
+ }
+ free ( a->acl_dn_pat.bv_val );
+ }
+ if ( a->acl_attrs ) {
+ for ( an = a->acl_attrs; !BER_BVISNULL( &an->an_name ); an++ ) {
+ free( an->an_name.bv_val );
+ }
+ free( a->acl_attrs );
+
+ if ( a->acl_attrval_style == ACL_STYLE_REGEX ) {
+ regfree( &a->acl_attrval_re );
+ }
+
+ if ( !BER_BVISNULL( &a->acl_attrval ) ) {
+ ber_memfree( a->acl_attrval.bv_val );
+ }
+ }
+ for ( ; a->acl_access; a->acl_access = n ) {
+ n = a->acl_access->a_next;
+ access_free( a->acl_access );
+ }
+ free( a );
+}
+
+void
+acl_destroy( AccessControl *a )
+{
+ AccessControl *n;
+
+ for ( ; a; a = n ) {
+ n = a->acl_next;
+ acl_free( a );
+ }
+
+ if ( !BER_BVISNULL( &aclbuf ) ) {
+ ch_free( aclbuf.bv_val );
+ BER_BVZERO( &aclbuf );
+ }
+}
+
+char *
+access2str( slap_access_t access )
+{
+ if ( access == ACL_NONE ) {
+ return "none";
+
+ } else if ( access == ACL_DISCLOSE ) {
+ return "disclose";
+
+ } else if ( access == ACL_AUTH ) {
+ return "auth";
+
+ } else if ( access == ACL_COMPARE ) {
+ return "compare";
+
+ } else if ( access == ACL_SEARCH ) {
+ return "search";
+
+ } else if ( access == ACL_READ ) {
+ return "read";
+
+ } else if ( access == ACL_WRITE ) {
+ return "write";
+
+ } else if ( access == ACL_WADD ) {
+ return "add";
+
+ } else if ( access == ACL_WDEL ) {
+ return "delete";
+
+ } else if ( access == ACL_MANAGE ) {
+ return "manage";
+
+ }
+
+ return "unknown";
+}
+
+slap_access_t
+str2access( const char *str )
+{
+ if ( strcasecmp( str, "none" ) == 0 ) {
+ return ACL_NONE;
+
+ } else if ( strcasecmp( str, "disclose" ) == 0 ) {
+ return ACL_DISCLOSE;
+
+ } else if ( strcasecmp( str, "auth" ) == 0 ) {
+ return ACL_AUTH;
+
+ } else if ( strcasecmp( str, "compare" ) == 0 ) {
+ return ACL_COMPARE;
+
+ } else if ( strcasecmp( str, "search" ) == 0 ) {
+ return ACL_SEARCH;
+
+ } else if ( strcasecmp( str, "read" ) == 0 ) {
+ return ACL_READ;
+
+ } else if ( strcasecmp( str, "write" ) == 0 ) {
+ return ACL_WRITE;
+
+ } else if ( strcasecmp( str, "add" ) == 0 ) {
+ return ACL_WADD;
+
+ } else if ( strcasecmp( str, "delete" ) == 0 ) {
+ return ACL_WDEL;
+
+ } else if ( strcasecmp( str, "manage" ) == 0 ) {
+ return ACL_MANAGE;
+ }
+
+ return( ACL_INVALID_ACCESS );
+}
+
+static char *
+safe_strncopy( char *ptr, const char *src, size_t n, struct berval *buf )
+{
+ while ( ptr + n >= buf->bv_val + buf->bv_len ) {
+ char *tmp = ch_realloc( buf->bv_val, 2*buf->bv_len );
+ if ( tmp == NULL ) {
+ return NULL;
+ }
+ ptr = tmp + (ptr - buf->bv_val);
+ buf->bv_val = tmp;
+ buf->bv_len *= 2;
+ }
+
+ return lutil_strncopy( ptr, src, n );
+}
+
+static char *
+safe_strcopy( char *ptr, const char *s, struct berval *buf )
+{
+ size_t n = strlen( s );
+
+ return safe_strncopy( ptr, s, n, buf );
+}
+
+static char *
+safe_strbvcopy( char *ptr, const struct berval *bv, struct berval *buf )
+{
+ return safe_strncopy( ptr, bv->bv_val, bv->bv_len, buf );
+}
+
+#define acl_safe_strcopy( ptr, s ) safe_strcopy( (ptr), (s), &aclbuf )
+#define acl_safe_strncopy( ptr, s, n ) safe_strncopy( (ptr), (s), (n), &aclbuf )
+#define acl_safe_strbvcopy( ptr, bv ) safe_strbvcopy( (ptr), (bv), &aclbuf )
+
+static char *
+dnaccess2text( slap_dn_access *bdn, char *ptr, int is_realdn )
+{
+ *ptr++ = ' ';
+
+ if ( is_realdn ) {
+ ptr = acl_safe_strcopy( ptr, "real" );
+ }
+
+ if ( ber_bvccmp( &bdn->a_pat, '*' ) ||
+ bdn->a_style == ACL_STYLE_ANONYMOUS ||
+ bdn->a_style == ACL_STYLE_USERS ||
+ bdn->a_style == ACL_STYLE_SELF )
+ {
+ if ( is_realdn ) {
+ assert( ! ber_bvccmp( &bdn->a_pat, '*' ) );
+ }
+
+ ptr = acl_safe_strbvcopy( ptr, &bdn->a_pat );
+ if ( bdn->a_style == ACL_STYLE_SELF && bdn->a_self_level != 0 ) {
+ char buf[SLAP_TEXT_BUFLEN];
+ int n = snprintf( buf, sizeof(buf), ".level{%d}", bdn->a_self_level );
+ if ( n > 0 ) {
+ ptr = acl_safe_strncopy( ptr, buf, n );
+ } /* else ? */
+ }
+
+ } else {
+ ptr = acl_safe_strcopy( ptr, "dn." );
+ if ( bdn->a_style == ACL_STYLE_BASE )
+ ptr = acl_safe_strcopy( ptr, style_base );
+ else
+ ptr = acl_safe_strcopy( ptr, style_strings[bdn->a_style] );
+ if ( bdn->a_style == ACL_STYLE_LEVEL ) {
+ char buf[SLAP_TEXT_BUFLEN];
+ int n = snprintf( buf, sizeof(buf), "{%d}", bdn->a_level );
+ if ( n > 0 ) {
+ ptr = acl_safe_strncopy( ptr, buf, n );
+ } /* else ? */
+ }
+ if ( bdn->a_expand ) {
+ ptr = acl_safe_strcopy( ptr, ",expand" );
+ }
+ ptr = acl_safe_strcopy( ptr, "=\"" );
+ ptr = acl_safe_strbvcopy( ptr, &bdn->a_pat );
+ ptr = acl_safe_strcopy( ptr, "\"" );
+ }
+ return ptr;
+}
+
+static char *
+access2text( Access *b, char *ptr )
+{
+ char maskbuf[ACCESSMASK_MAXLEN];
+
+ ptr = acl_safe_strcopy( ptr, "\tby" );
+
+ if ( !BER_BVISEMPTY( &b->a_dn_pat ) ) {
+ ptr = dnaccess2text( &b->a_dn, ptr, 0 );
+ }
+ if ( b->a_dn_at ) {
+ ptr = acl_safe_strcopy( ptr, " dnattr=" );
+ ptr = acl_safe_strbvcopy( ptr, &b->a_dn_at->ad_cname );
+ }
+
+ if ( !BER_BVISEMPTY( &b->a_realdn_pat ) ) {
+ ptr = dnaccess2text( &b->a_realdn, ptr, 1 );
+ }
+ if ( b->a_realdn_at ) {
+ ptr = acl_safe_strcopy( ptr, " realdnattr=" );
+ ptr = acl_safe_strbvcopy( ptr, &b->a_realdn_at->ad_cname );
+ }
+
+ if ( !BER_BVISEMPTY( &b->a_group_pat ) ) {
+ ptr = acl_safe_strcopy( ptr, " group/" );
+ ptr = acl_safe_strcopy( ptr, b->a_group_oc ?
+ b->a_group_oc->soc_cname.bv_val : SLAPD_GROUP_CLASS );
+ ptr = acl_safe_strcopy( ptr, "/" );
+ ptr = acl_safe_strcopy( ptr, b->a_group_at ?
+ b->a_group_at->ad_cname.bv_val : SLAPD_GROUP_ATTR );
+ ptr = acl_safe_strcopy( ptr, "." );
+ ptr = acl_safe_strcopy( ptr, style_strings[b->a_group_style] );
+ ptr = acl_safe_strcopy( ptr, "=\"" );
+ ptr = acl_safe_strbvcopy( ptr, &b->a_group_pat );
+ ptr = acl_safe_strcopy( ptr, "\"" );
+ }
+
+ if ( !BER_BVISEMPTY( &b->a_peername_pat ) ) {
+ ptr = acl_safe_strcopy( ptr, " peername" );
+ ptr = acl_safe_strcopy( ptr, "." );
+ ptr = acl_safe_strcopy( ptr, style_strings[b->a_peername_style] );
+ ptr = acl_safe_strcopy( ptr, "=\"" );
+ ptr = acl_safe_strbvcopy( ptr, &b->a_peername_pat );
+ ptr = acl_safe_strcopy( ptr, "\"" );
+ }
+
+ if ( !BER_BVISEMPTY( &b->a_sockname_pat ) ) {
+ ptr = acl_safe_strcopy( ptr, " sockname" );
+ ptr = acl_safe_strcopy( ptr, "." );
+ ptr = acl_safe_strcopy( ptr, style_strings[b->a_sockname_style] );
+ ptr = acl_safe_strcopy( ptr, "=\"" );
+ ptr = acl_safe_strbvcopy( ptr, &b->a_sockname_pat );
+ ptr = acl_safe_strcopy( ptr, "\"" );
+ }
+
+ if ( !BER_BVISEMPTY( &b->a_domain_pat ) ) {
+ ptr = acl_safe_strcopy( ptr, " domain" );
+ ptr = acl_safe_strcopy( ptr, "." );
+ ptr = acl_safe_strcopy( ptr, style_strings[b->a_domain_style] );
+ if ( b->a_domain_expand ) {
+ ptr = acl_safe_strcopy( ptr, ",expand" );
+ }
+ ptr = acl_safe_strcopy( ptr, "=" );
+ ptr = acl_safe_strbvcopy( ptr, &b->a_domain_pat );
+ }
+
+ if ( !BER_BVISEMPTY( &b->a_sockurl_pat ) ) {
+ ptr = acl_safe_strcopy( ptr, " sockurl" );
+ ptr = acl_safe_strcopy( ptr, "." );
+ ptr = acl_safe_strcopy( ptr, style_strings[b->a_sockurl_style] );
+ ptr = acl_safe_strcopy( ptr, "=\"" );
+ ptr = acl_safe_strbvcopy( ptr, &b->a_sockurl_pat );
+ ptr = acl_safe_strcopy( ptr, "\"" );
+ }
+
+ if ( !BER_BVISEMPTY( &b->a_set_pat ) ) {
+ ptr = acl_safe_strcopy( ptr, " set" );
+ ptr = acl_safe_strcopy( ptr, "." );
+ ptr = acl_safe_strcopy( ptr, style_strings[b->a_set_style] );
+ ptr = acl_safe_strcopy( ptr, "=\"" );
+ ptr = acl_safe_strbvcopy( ptr, &b->a_set_pat );
+ ptr = acl_safe_strcopy( ptr, "\"" );
+ }
+
+#ifdef SLAP_DYNACL
+ if ( b->a_dynacl ) {
+ slap_dynacl_t *da;
+
+ for ( da = b->a_dynacl; da; da = da->da_next ) {
+ if ( da->da_unparse ) {
+ struct berval bv = BER_BVNULL;
+ (void)( *da->da_unparse )( da->da_private, &bv );
+ assert( !BER_BVISNULL( &bv ) );
+ ptr = acl_safe_strbvcopy( ptr, &bv );
+ ch_free( bv.bv_val );
+ }
+ }
+ }
+#endif /* SLAP_DYNACL */
+
+ /* Security Strength Factors */
+ if ( b->a_authz.sai_ssf ) {
+ char buf[SLAP_TEXT_BUFLEN];
+ int n = snprintf( buf, sizeof(buf), " ssf=%u",
+ b->a_authz.sai_ssf );
+ ptr = acl_safe_strncopy( ptr, buf, n );
+ }
+ if ( b->a_authz.sai_transport_ssf ) {
+ char buf[SLAP_TEXT_BUFLEN];
+ int n = snprintf( buf, sizeof(buf), " transport_ssf=%u",
+ b->a_authz.sai_transport_ssf );
+ ptr = acl_safe_strncopy( ptr, buf, n );
+ }
+ if ( b->a_authz.sai_tls_ssf ) {
+ char buf[SLAP_TEXT_BUFLEN];
+ int n = snprintf( buf, sizeof(buf), " tls_ssf=%u",
+ b->a_authz.sai_tls_ssf );
+ ptr = acl_safe_strncopy( ptr, buf, n );
+ }
+ if ( b->a_authz.sai_sasl_ssf ) {
+ char buf[SLAP_TEXT_BUFLEN];
+ int n = snprintf( buf, sizeof(buf), " sasl_ssf=%u",
+ b->a_authz.sai_sasl_ssf );
+ ptr = acl_safe_strncopy( ptr, buf, n );
+ }
+
+ ptr = acl_safe_strcopy( ptr, " " );
+ if ( b->a_dn_self ) {
+ ptr = acl_safe_strcopy( ptr, "self" );
+ } else if ( b->a_realdn_self ) {
+ ptr = acl_safe_strcopy( ptr, "realself" );
+ }
+ ptr = acl_safe_strcopy( ptr, accessmask2str( b->a_access_mask, maskbuf, 0 ));
+ if ( !maskbuf[0] ) ptr--;
+
+ if( b->a_type == ACL_BREAK ) {
+ ptr = acl_safe_strcopy( ptr, " break" );
+
+ } else if( b->a_type == ACL_CONTINUE ) {
+ ptr = acl_safe_strcopy( ptr, " continue" );
+
+ } else if( b->a_type != ACL_STOP ) {
+ ptr = acl_safe_strcopy( ptr, " unknown-control" );
+ } else {
+ if ( !maskbuf[0] ) ptr = acl_safe_strcopy( ptr, " stop" );
+ }
+ ptr = acl_safe_strcopy( ptr, "\n" );
+
+ return ptr;
+}
+
+void
+acl_unparse( AccessControl *a, struct berval *bv )
+{
+ Access *b;
+ char *ptr;
+ int to = 0;
+
+ if ( BER_BVISNULL( &aclbuf ) ) {
+ aclbuf.bv_val = ch_malloc( ACLBUF_CHUNKSIZE );
+ aclbuf.bv_len = ACLBUF_CHUNKSIZE;
+ }
+
+ bv->bv_len = 0;
+
+ ptr = aclbuf.bv_val;
+
+ ptr = acl_safe_strcopy( ptr, "to" );
+ if ( !BER_BVISNULL( &a->acl_dn_pat ) ) {
+ to++;
+ ptr = acl_safe_strcopy( ptr, " dn." );
+ if ( a->acl_dn_style == ACL_STYLE_BASE )
+ ptr = acl_safe_strcopy( ptr, style_base );
+ else
+ ptr = acl_safe_strcopy( ptr, style_strings[a->acl_dn_style] );
+ ptr = acl_safe_strcopy( ptr, "=\"" );
+ ptr = acl_safe_strbvcopy( ptr, &a->acl_dn_pat );
+ ptr = acl_safe_strcopy( ptr, "\"\n" );
+ }
+
+ if ( a->acl_filter != NULL ) {
+ struct berval fbv = BER_BVNULL;
+
+ to++;
+ filter2bv( a->acl_filter, &fbv );
+ ptr = acl_safe_strcopy( ptr, " filter=\"" );
+ ptr = acl_safe_strbvcopy( ptr, &fbv );
+ ptr = acl_safe_strcopy( ptr, "\"\n" );
+ ch_free( fbv.bv_val );
+ }
+
+ if ( a->acl_attrs != NULL ) {
+ int first = 1;
+ AttributeName *an;
+ to++;
+
+ ptr = acl_safe_strcopy( ptr, " attrs=" );
+ for ( an = a->acl_attrs; an && !BER_BVISNULL( &an->an_name ); an++ ) {
+ if ( ! first ) ptr = acl_safe_strcopy( ptr, ",");
+ if (an->an_oc) {
+ ptr = acl_safe_strcopy( ptr, ( an->an_flags & SLAP_AN_OCEXCLUDE ) ? "!" : "@" );
+ ptr = acl_safe_strbvcopy( ptr, &an->an_oc->soc_cname );
+
+ } else {
+ ptr = acl_safe_strbvcopy( ptr, &an->an_name );
+ }
+ first = 0;
+ }
+ ptr = acl_safe_strcopy( ptr, "\n" );
+ }
+
+ if ( !BER_BVISNULL( &a->acl_attrval ) ) {
+ to++;
+ ptr = acl_safe_strcopy( ptr, " val." );
+ if ( a->acl_attrval_style == ACL_STYLE_BASE &&
+ a->acl_attrs[0].an_desc->ad_type->sat_syntax ==
+ slap_schema.si_syn_distinguishedName )
+ ptr = acl_safe_strcopy( ptr, style_base );
+ else
+ ptr = acl_safe_strcopy( ptr, style_strings[a->acl_attrval_style] );
+ ptr = acl_safe_strcopy( ptr, "=\"" );
+ ptr = acl_safe_strbvcopy( ptr, &a->acl_attrval );
+ ptr = acl_safe_strcopy( ptr, "\"\n" );
+ }
+
+ if ( !to ) {
+ ptr = acl_safe_strcopy( ptr, " *\n" );
+ }
+
+ for ( b = a->acl_access; b != NULL; b = b->a_next ) {
+ ptr = access2text( b, ptr );
+ }
+ *ptr = '\0';
+ bv->bv_val = aclbuf.bv_val;
+ bv->bv_len = ptr - bv->bv_val;
+}
+
+#ifdef LDAP_DEBUG
+static void
+print_acl( Backend *be, AccessControl *a )
+{
+ struct berval bv;
+
+ acl_unparse( a, &bv );
+ fprintf( stderr, "%s ACL: access %s\n",
+ be == NULL ? "Global" : "Backend", bv.bv_val );
+}
+#endif /* LDAP_DEBUG */
diff --git a/servers/slapd/ad.c b/servers/slapd/ad.c
new file mode 100644
index 0000000..6b643f5
--- /dev/null
+++ b/servers/slapd/ad.c
@@ -0,0 +1,1313 @@
+/* ad.c - routines for dealing with attribute descriptions */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "slap.h"
+#include "lutil.h"
+
+static struct berval bv_no_attrs = BER_BVC( LDAP_NO_ATTRS );
+static struct berval bv_all_user_attrs = BER_BVC( "*" );
+static struct berval bv_all_operational_attrs = BER_BVC( "+" );
+
+static AttributeName anlist_no_attrs[] = {
+ { BER_BVC( LDAP_NO_ATTRS ), NULL, 0, NULL },
+ { BER_BVNULL, NULL, 0, NULL }
+};
+
+static AttributeName anlist_all_user_attributes[] = {
+ { BER_BVC( LDAP_ALL_USER_ATTRIBUTES ), NULL, 0, NULL },
+ { BER_BVNULL, NULL, 0, NULL }
+};
+
+static AttributeName anlist_all_operational_attributes[] = {
+ { BER_BVC( LDAP_ALL_OPERATIONAL_ATTRIBUTES ), NULL, 0, NULL },
+ { BER_BVNULL, NULL, 0, NULL }
+};
+
+static AttributeName anlist_all_attributes[] = {
+ { BER_BVC( LDAP_ALL_USER_ATTRIBUTES ), NULL, 0, NULL },
+ { BER_BVC( LDAP_ALL_OPERATIONAL_ATTRIBUTES ), NULL, 0, NULL },
+ { BER_BVNULL, NULL, 0, NULL }
+};
+
+AttributeName *slap_anlist_no_attrs = anlist_no_attrs;
+AttributeName *slap_anlist_all_user_attributes = anlist_all_user_attributes;
+AttributeName *slap_anlist_all_operational_attributes = anlist_all_operational_attributes;
+AttributeName *slap_anlist_all_attributes = anlist_all_attributes;
+
+struct berval * slap_bv_no_attrs = &bv_no_attrs;
+struct berval * slap_bv_all_user_attrs = &bv_all_user_attrs;
+struct berval * slap_bv_all_operational_attrs = &bv_all_operational_attrs;
+
+typedef struct Attr_option {
+ struct berval name; /* option name or prefix */
+ int prefix; /* NAME is a tag and range prefix */
+} Attr_option;
+
+static Attr_option lang_option = { BER_BVC("lang-"), 1 };
+
+/* Options sorted by name, and number of options */
+static Attr_option *options = &lang_option;
+static int option_count = 1;
+
+static int msad_range_hack = 0;
+
+static int ad_count;
+
+static Attr_option *ad_find_option_definition( const char *opt, int optlen );
+
+int ad_keystring(
+ struct berval *bv )
+{
+ ber_len_t i;
+
+ if( !AD_LEADCHAR( bv->bv_val[0] ) ) {
+ return 1;
+ }
+
+ for( i=1; i<bv->bv_len; i++ ) {
+ if( !AD_CHAR( bv->bv_val[i] )) {
+ if ( msad_range_hack && bv->bv_val[i] == '=' )
+ continue;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void ad_destroy( AttributeDescription *ad )
+{
+ AttributeDescription *n;
+
+ for (; ad != NULL; ad = n) {
+ n = ad->ad_next;
+ ldap_memfree( ad );
+ }
+}
+
+/* Is there an AttributeDescription for this type that uses these tags? */
+AttributeDescription * ad_find_tags(
+ AttributeType *type,
+ struct berval *tags )
+{
+ AttributeDescription *ad;
+
+ ldap_pvt_thread_mutex_lock( &type->sat_ad_mutex );
+ for (ad = type->sat_ad; ad; ad=ad->ad_next)
+ {
+ if (ad->ad_tags.bv_len == tags->bv_len &&
+ !strcasecmp(ad->ad_tags.bv_val, tags->bv_val))
+ break;
+ }
+ ldap_pvt_thread_mutex_unlock( &type->sat_ad_mutex );
+ return ad;
+}
+
+int slap_str2ad(
+ const char *str,
+ AttributeDescription **ad,
+ const char **text )
+{
+ struct berval bv;
+ bv.bv_val = (char *) str;
+ bv.bv_len = strlen( str );
+
+ return slap_bv2ad( &bv, ad, text );
+}
+
+static char *strchrlen(
+ const char *beg,
+ const char *end,
+ const char ch,
+ int *len )
+{
+ const char *p;
+
+ for( p=beg; p < end && *p; p++ ) {
+ if( *p == ch ) {
+ *len = p - beg;
+ return (char *) p;
+ }
+ }
+
+ *len = p - beg;
+ return NULL;
+}
+
+int slap_bv2ad(
+ struct berval *bv,
+ AttributeDescription **ad,
+ const char **text )
+{
+ int rtn = LDAP_UNDEFINED_TYPE;
+ AttributeDescription desc, *d2;
+ char *name, *options, *optn;
+ char *opt, *next;
+ int ntags;
+ int tagslen;
+
+ /* hardcoded limits for speed */
+#define MAX_TAGGING_OPTIONS 128
+ struct berval tags[MAX_TAGGING_OPTIONS+1];
+#define MAX_TAGS_LEN 1024
+ char tagbuf[MAX_TAGS_LEN];
+
+ assert( ad != NULL );
+ assert( *ad == NULL ); /* temporary */
+
+ if( bv == NULL || BER_BVISNULL( bv ) || BER_BVISEMPTY( bv ) ) {
+ *text = "empty AttributeDescription";
+ return rtn;
+ }
+
+ /* make sure description is IA5 */
+ if( ad_keystring( bv ) ) {
+ *text = "AttributeDescription contains inappropriate characters";
+ return rtn;
+ }
+
+ /* find valid base attribute type; parse in place */
+ desc.ad_cname = *bv;
+ desc.ad_flags = 0;
+ BER_BVZERO( &desc.ad_tags );
+ name = bv->bv_val;
+ options = ber_bvchr( bv, ';' );
+ if ( options != NULL && (unsigned) ( options - name ) < bv->bv_len ) {
+ /* don't go past the end of the berval! */
+ desc.ad_cname.bv_len = options - name;
+ } else {
+ options = NULL;
+ }
+ desc.ad_type = at_bvfind( &desc.ad_cname );
+ if( desc.ad_type == NULL ) {
+ *text = "attribute type undefined";
+ return rtn;
+ }
+
+ if( is_at_operational( desc.ad_type ) && options != NULL ) {
+ *text = "operational attribute with options undefined";
+ return rtn;
+ }
+
+ /*
+ * parse options in place
+ */
+ ntags = 0;
+ tagslen = 0;
+ optn = bv->bv_val + bv->bv_len;
+
+ for( opt=options; opt != NULL; opt=next ) {
+ Attr_option *aopt;
+ int optlen;
+ opt++;
+ next = strchrlen( opt, optn, ';', &optlen );
+
+ if( optlen == 0 ) {
+ *text = "zero length option is invalid";
+ return rtn;
+
+ } else if ( optlen == STRLENOF("binary") &&
+ strncasecmp( opt, "binary", STRLENOF("binary") ) == 0 )
+ {
+ /* binary option */
+ if( slap_ad_is_binary( &desc ) ) {
+ *text = "option \"binary\" specified multiple times";
+ return rtn;
+ }
+
+ if( !slap_syntax_is_binary( desc.ad_type->sat_syntax )) {
+ /* not stored in binary, disallow option */
+ *text = "option \"binary\" not supported with type";
+ return rtn;
+ }
+
+ desc.ad_flags |= SLAP_DESC_BINARY;
+ continue;
+
+ } else if (( aopt = ad_find_option_definition( opt, optlen )) ) {
+ int i;
+
+ if( opt[optlen-1] == '-' ||
+ ( aopt->name.bv_val[aopt->name.bv_len-1] == '=' && msad_range_hack )) {
+ desc.ad_flags |= SLAP_DESC_TAG_RANGE;
+ }
+
+ if( ntags >= MAX_TAGGING_OPTIONS ) {
+ *text = "too many tagging options";
+ return rtn;
+ }
+
+ /*
+ * tags should be presented in sorted order,
+ * so run the array in reverse.
+ */
+ for( i=ntags-1; i>=0; i-- ) {
+ int rc;
+
+ rc = strncasecmp( opt, tags[i].bv_val,
+ (unsigned) optlen < tags[i].bv_len
+ ? (unsigned) optlen : tags[i].bv_len );
+
+ if( rc == 0 && (unsigned)optlen == tags[i].bv_len ) {
+ /* duplicate (ignore) */
+ ntags--;
+ goto done;
+
+ } else if ( rc > 0 ||
+ ( rc == 0 && (unsigned)optlen > tags[i].bv_len ))
+ {
+ AC_MEMCPY( &tags[i+2], &tags[i+1],
+ (ntags-i-1)*sizeof(struct berval) );
+ tags[i+1].bv_val = opt;
+ tags[i+1].bv_len = optlen;
+ goto done;
+ }
+ }
+
+ if( ntags ) {
+ AC_MEMCPY( &tags[1], &tags[0],
+ ntags*sizeof(struct berval) );
+ }
+ tags[0].bv_val = opt;
+ tags[0].bv_len = optlen;
+
+done:;
+ tagslen += optlen + 1;
+ ntags++;
+
+ } else {
+ *text = "unrecognized option";
+ return rtn;
+ }
+ }
+
+ if( ntags > 0 ) {
+ int i;
+
+ if( tagslen > MAX_TAGS_LEN ) {
+ *text = "tagging options too long";
+ return rtn;
+ }
+
+ desc.ad_tags.bv_val = tagbuf;
+ tagslen = 0;
+
+ for( i=0; i<ntags; i++ ) {
+ AC_MEMCPY( &desc.ad_tags.bv_val[tagslen],
+ tags[i].bv_val, tags[i].bv_len );
+
+ tagslen += tags[i].bv_len;
+ desc.ad_tags.bv_val[tagslen++] = ';';
+ }
+
+ desc.ad_tags.bv_val[--tagslen] = '\0';
+ desc.ad_tags.bv_len = tagslen;
+ }
+
+ /* see if a matching description is already cached */
+ for (d2 = desc.ad_type->sat_ad; d2; d2=d2->ad_next) {
+ if( d2->ad_flags != desc.ad_flags ) {
+ continue;
+ }
+ if( d2->ad_tags.bv_len != desc.ad_tags.bv_len ) {
+ continue;
+ }
+ if( d2->ad_tags.bv_len == 0 ) {
+ break;
+ }
+ if( strncasecmp( d2->ad_tags.bv_val, desc.ad_tags.bv_val,
+ desc.ad_tags.bv_len ) == 0 )
+ {
+ break;
+ }
+ }
+
+ /* Not found, add new one */
+ while (d2 == NULL) {
+ size_t dlen = 0;
+ ldap_pvt_thread_mutex_lock( &desc.ad_type->sat_ad_mutex );
+ /* check again now that we've locked */
+ for (d2 = desc.ad_type->sat_ad; d2; d2=d2->ad_next) {
+ if (d2->ad_flags != desc.ad_flags)
+ continue;
+ if (d2->ad_tags.bv_len != desc.ad_tags.bv_len)
+ continue;
+ if (d2->ad_tags.bv_len == 0)
+ break;
+ if (strncasecmp(d2->ad_tags.bv_val, desc.ad_tags.bv_val,
+ desc.ad_tags.bv_len) == 0)
+ break;
+ }
+ if (d2) {
+ ldap_pvt_thread_mutex_unlock( &desc.ad_type->sat_ad_mutex );
+ break;
+ }
+
+ /* Allocate a single contiguous block. If there are no
+ * options, we just need space for the AttrDesc structure.
+ * Otherwise, we need to tack on the full name length +
+ * options length, + maybe tagging options length again.
+ */
+ if (desc.ad_tags.bv_len || desc.ad_flags != SLAP_DESC_NONE) {
+ dlen = desc.ad_type->sat_cname.bv_len + 1;
+ if (desc.ad_tags.bv_len) {
+ dlen += 1 + desc.ad_tags.bv_len;
+ }
+ if ( slap_ad_is_binary( &desc ) ) {
+ dlen += 1 + STRLENOF(";binary") + desc.ad_tags.bv_len;
+ }
+ }
+
+ d2 = ch_malloc(sizeof(AttributeDescription) + dlen);
+ d2->ad_next = NULL;
+ d2->ad_type = desc.ad_type;
+ d2->ad_flags = desc.ad_flags;
+ d2->ad_cname.bv_len = desc.ad_type->sat_cname.bv_len;
+ d2->ad_tags.bv_len = desc.ad_tags.bv_len;
+ ldap_pvt_thread_mutex_lock( &ad_index_mutex );
+ d2->ad_index = ++ad_count;
+ ldap_pvt_thread_mutex_unlock( &ad_index_mutex );
+
+ if (dlen == 0) {
+ d2->ad_cname.bv_val = d2->ad_type->sat_cname.bv_val;
+ d2->ad_tags.bv_val = NULL;
+ } else {
+ char *cp, *op, *lp;
+ int j;
+ d2->ad_cname.bv_val = (char *)(d2+1);
+ strcpy(d2->ad_cname.bv_val, d2->ad_type->sat_cname.bv_val);
+ cp = d2->ad_cname.bv_val + d2->ad_cname.bv_len;
+ if( slap_ad_is_binary( &desc ) ) {
+ op = cp;
+ lp = NULL;
+ if( desc.ad_tags.bv_len ) {
+ lp = desc.ad_tags.bv_val;
+ while( strncasecmp(lp, "binary", STRLENOF("binary")) < 0
+ && (lp = strchr( lp, ';' )) != NULL )
+ ++lp;
+ if( lp != desc.ad_tags.bv_val ) {
+ *cp++ = ';';
+ j = (lp
+ ? (unsigned) (lp - desc.ad_tags.bv_val - 1)
+ : strlen( desc.ad_tags.bv_val ));
+ cp = lutil_strncopy(cp, desc.ad_tags.bv_val, j);
+ }
+ }
+ cp = lutil_strcopy(cp, ";binary");
+ if( lp != NULL ) {
+ *cp++ = ';';
+ cp = lutil_strcopy(cp, lp);
+ }
+ d2->ad_cname.bv_len = cp - d2->ad_cname.bv_val;
+ if( desc.ad_tags.bv_len )
+ ldap_pvt_str2lower(op);
+ j = 1;
+ } else {
+ j = 0;
+ }
+ if( desc.ad_tags.bv_len ) {
+ lp = d2->ad_cname.bv_val + d2->ad_cname.bv_len + j;
+ if ( j == 0 )
+ *lp++ = ';';
+ d2->ad_tags.bv_val = lp;
+ strcpy(lp, desc.ad_tags.bv_val);
+ ldap_pvt_str2lower(lp);
+ if( j == 0 )
+ d2->ad_cname.bv_len += 1 + desc.ad_tags.bv_len;
+ }
+ }
+ /* Add new desc to list. We always want the bare Desc with
+ * no options to stay at the head of the list, assuming
+ * that one will be used most frequently.
+ */
+ if (desc.ad_type->sat_ad == NULL || dlen == 0) {
+ d2->ad_next = desc.ad_type->sat_ad;
+ desc.ad_type->sat_ad = d2;
+ } else {
+ d2->ad_next = desc.ad_type->sat_ad->ad_next;
+ desc.ad_type->sat_ad->ad_next = d2;
+ }
+ ldap_pvt_thread_mutex_unlock( &desc.ad_type->sat_ad_mutex );
+ }
+
+ if( *ad == NULL ) {
+ *ad = d2;
+ } else {
+ **ad = *d2;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int is_ad_subtags(
+ struct berval *subtagsbv,
+ struct berval *suptagsbv )
+{
+ const char *suptags, *supp, *supdelimp, *supn;
+ const char *subtags, *subp, *subdelimp, *subn;
+ int suplen, sublen;
+
+ subtags =subtagsbv->bv_val;
+ suptags =suptagsbv->bv_val;
+ subn = subtags + subtagsbv->bv_len;
+ supn = suptags + suptagsbv->bv_len;
+
+ for( supp=suptags ; supp; supp=supdelimp ) {
+ supdelimp = strchrlen( supp, supn, ';', &suplen );
+ if( supdelimp ) supdelimp++;
+
+ for( subp=subtags ; subp; subp=subdelimp ) {
+ subdelimp = strchrlen( subp, subn, ';', &sublen );
+ if( subdelimp ) subdelimp++;
+
+ if ( suplen > sublen
+ ? ( suplen-1 == sublen && supp[suplen-1] == '-'
+ && strncmp( supp, subp, sublen ) == 0 )
+ : ( ( suplen == sublen || supp[suplen-1] == '-' )
+ && strncmp( supp, subp, suplen ) == 0 ) )
+ {
+ goto match;
+ }
+ }
+
+ return 0;
+match:;
+ }
+ return 1;
+}
+
+int is_ad_subtype(
+ AttributeDescription *sub,
+ AttributeDescription *super
+)
+{
+ AttributeType *a;
+ int lr;
+
+ for ( a = sub->ad_type; a; a=a->sat_sup ) {
+ if ( a == super->ad_type ) break;
+ }
+ if( !a ) {
+ return 0;
+ }
+
+ /* ensure sub does support all flags of super */
+ lr = sub->ad_tags.bv_len ? SLAP_DESC_TAG_RANGE : 0;
+ if(( super->ad_flags & ( sub->ad_flags | lr )) != super->ad_flags ) {
+ return 0;
+ }
+
+ /* check for tagging options */
+ if ( super->ad_tags.bv_len == 0 )
+ return 1;
+ if ( sub->ad_tags.bv_len == 0 )
+ return 0;
+
+ return is_ad_subtags( &sub->ad_tags, &super->ad_tags );
+}
+
+int ad_inlist(
+ AttributeDescription *desc,
+ AttributeName *attrs )
+{
+ if (! attrs ) return 0;
+
+ for( ; attrs->an_name.bv_val; attrs++ ) {
+ AttributeType *a;
+ ObjectClass *oc;
+
+ if ( attrs->an_desc ) {
+ int lr;
+
+ if ( desc == attrs->an_desc ) {
+ return 1;
+ }
+
+ /*
+ * EXTENSION: if requested description is preceded by
+ * a '-' character, do not match on subtypes.
+ */
+ if ( attrs->an_name.bv_val[0] == '-' ) {
+ continue;
+ }
+
+ /* Is this a subtype of the requested attr? */
+ for (a = desc->ad_type; a; a=a->sat_sup) {
+ if ( a == attrs->an_desc->ad_type )
+ break;
+ }
+ if ( !a ) {
+ continue;
+ }
+ /* Does desc support all the requested flags? */
+ lr = desc->ad_tags.bv_len ? SLAP_DESC_TAG_RANGE : 0;
+ if(( attrs->an_desc->ad_flags & (desc->ad_flags | lr))
+ != attrs->an_desc->ad_flags ) {
+ continue;
+ }
+ /* Do the descs have compatible tags? */
+ if ( attrs->an_desc->ad_tags.bv_len == 0 ) {
+ return 1;
+ }
+ if ( desc->ad_tags.bv_len == 0) {
+ continue;
+ }
+ if ( is_ad_subtags( &desc->ad_tags,
+ &attrs->an_desc->ad_tags ) ) {
+ return 1;
+ }
+ continue;
+ }
+
+ if ( ber_bvccmp( &attrs->an_name, '*' ) ) {
+ if ( !is_at_operational( desc->ad_type ) ) {
+ return 1;
+ }
+ continue;
+ }
+
+ if ( ber_bvccmp( &attrs->an_name, '+' ) ) {
+ if ( is_at_operational( desc->ad_type ) ) {
+ return 1;
+ }
+ continue;
+ }
+
+ /*
+ * EXTENSION: see if requested description is @objectClass
+ * if so, return attributes which the class requires/allows
+ * else if requested description is !objectClass, return
+ * attributes which the class does not require/allow
+ */
+ if ( !( attrs->an_flags & SLAP_AN_OCINITED )) {
+ if( attrs->an_name.bv_val ) {
+ switch( attrs->an_name.bv_val[0] ) {
+ case '@': /* @objectClass */
+ case '+': /* +objectClass (deprecated) */
+ case '!': { /* exclude */
+ struct berval ocname;
+ ocname.bv_len = attrs->an_name.bv_len - 1;
+ ocname.bv_val = &attrs->an_name.bv_val[1];
+ oc = oc_bvfind( &ocname );
+ if ( oc && attrs->an_name.bv_val[0] == '!' ) {
+ attrs->an_flags |= SLAP_AN_OCEXCLUDE;
+ } else {
+ attrs->an_flags &= ~SLAP_AN_OCEXCLUDE;
+ }
+ } break;
+
+ default: /* old (deprecated) way */
+ oc = oc_bvfind( &attrs->an_name );
+ }
+ attrs->an_oc = oc;
+ }
+ attrs->an_flags |= SLAP_AN_OCINITED;
+ }
+ oc = attrs->an_oc;
+ if( oc != NULL ) {
+ if ( attrs->an_flags & SLAP_AN_OCEXCLUDE ) {
+ if ( oc == slap_schema.si_oc_extensibleObject ) {
+ /* extensibleObject allows the return of anything */
+ return 0;
+ }
+
+ if( oc->soc_required ) {
+ /* allow return of required attributes */
+ int i;
+
+ for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
+ for (a = desc->ad_type; a; a=a->sat_sup) {
+ if ( a == oc->soc_required[i] ) {
+ return 0;
+ }
+ }
+ }
+ }
+
+ if( oc->soc_allowed ) {
+ /* allow return of allowed attributes */
+ int i;
+ for ( i = 0; oc->soc_allowed[i] != NULL; i++ ) {
+ for (a = desc->ad_type; a; a=a->sat_sup) {
+ if ( a == oc->soc_allowed[i] ) {
+ return 0;
+ }
+ }
+ }
+ }
+
+ return 1;
+ }
+
+ if ( oc == slap_schema.si_oc_extensibleObject ) {
+ /* extensibleObject allows the return of anything */
+ return 1;
+ }
+
+ if( oc->soc_required ) {
+ /* allow return of required attributes */
+ int i;
+
+ for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
+ for (a = desc->ad_type; a; a=a->sat_sup) {
+ if ( a == oc->soc_required[i] ) {
+ return 1;
+ }
+ }
+ }
+ }
+
+ if( oc->soc_allowed ) {
+ /* allow return of allowed attributes */
+ int i;
+ for ( i = 0; oc->soc_allowed[i] != NULL; i++ ) {
+ for (a = desc->ad_type; a; a=a->sat_sup) {
+ if ( a == oc->soc_allowed[i] ) {
+ return 1;
+ }
+ }
+ }
+ }
+
+ } else {
+ const char *text;
+
+ /* give it a chance of being retrieved by a proxy... */
+ (void)slap_bv2undef_ad( &attrs->an_name,
+ &attrs->an_desc, &text,
+ SLAP_AD_PROXIED|SLAP_AD_NOINSERT );
+ }
+ }
+
+ return 0;
+}
+
+
+int slap_str2undef_ad(
+ const char *str,
+ AttributeDescription **ad,
+ const char **text,
+ unsigned flags )
+{
+ struct berval bv;
+ bv.bv_val = (char *) str;
+ bv.bv_len = strlen( str );
+
+ return slap_bv2undef_ad( &bv, ad, text, flags );
+}
+
+int slap_bv2undef_ad(
+ struct berval *bv,
+ AttributeDescription **ad,
+ const char **text,
+ unsigned flags )
+{
+ AttributeDescription *desc;
+ AttributeType *at;
+
+ assert( ad != NULL );
+
+ if( bv == NULL || bv->bv_len == 0 ) {
+ *text = "empty AttributeDescription";
+ return LDAP_UNDEFINED_TYPE;
+ }
+
+ /* make sure description is IA5 */
+ if( ad_keystring( bv ) ) {
+ *text = "AttributeDescription contains inappropriate characters";
+ return LDAP_UNDEFINED_TYPE;
+ }
+
+ /* use the appropriate type */
+ if ( flags & SLAP_AD_PROXIED ) {
+ at = slap_schema.si_at_proxied;
+
+ } else {
+ at = slap_schema.si_at_undefined;
+ }
+
+ for( desc = at->sat_ad; desc; desc=desc->ad_next ) {
+ if( desc->ad_cname.bv_len == bv->bv_len &&
+ !strcasecmp( desc->ad_cname.bv_val, bv->bv_val ) )
+ {
+ break;
+ }
+ }
+
+ if( !desc ) {
+ if ( flags & SLAP_AD_NOINSERT ) {
+ *text = NULL;
+ return LDAP_UNDEFINED_TYPE;
+ }
+
+ desc = ch_malloc(sizeof(AttributeDescription) + 1 +
+ bv->bv_len);
+
+ desc->ad_flags = SLAP_DESC_NONE;
+ BER_BVZERO( &desc->ad_tags );
+
+ desc->ad_cname.bv_len = bv->bv_len;
+ desc->ad_cname.bv_val = (char *)(desc+1);
+ strncpy(desc->ad_cname.bv_val, bv->bv_val, bv->bv_len);
+ desc->ad_cname.bv_val[bv->bv_len] = '\0';
+
+ /* canonical to upper case */
+ ldap_pvt_str2upper( desc->ad_cname.bv_val );
+
+ /* shouldn't we protect this for concurrency? */
+ desc->ad_type = at;
+ desc->ad_index = 0;
+ ldap_pvt_thread_mutex_lock( &ad_undef_mutex );
+ desc->ad_next = desc->ad_type->sat_ad;
+ desc->ad_type->sat_ad = desc;
+ ldap_pvt_thread_mutex_unlock( &ad_undef_mutex );
+
+ Debug( LDAP_DEBUG_ANY,
+ "%s attributeDescription \"%s\" inserted.\n",
+ ( flags & SLAP_AD_PROXIED ) ? "PROXIED" : "UNKNOWN",
+ desc->ad_cname.bv_val );
+ }
+
+ if( !*ad ) {
+ *ad = desc;
+ } else {
+ **ad = *desc;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+AttributeDescription *
+slap_bv2tmp_ad(
+ struct berval *bv,
+ void *memctx )
+{
+ AttributeDescription *ad =
+ slap_sl_mfuncs.bmf_malloc( sizeof(AttributeDescription) +
+ bv->bv_len + 1, memctx );
+
+ ad->ad_cname.bv_val = (char *)(ad+1);
+ strncpy( ad->ad_cname.bv_val, bv->bv_val, bv->bv_len+1 );
+ ad->ad_cname.bv_len = bv->bv_len;
+ ad->ad_flags = SLAP_DESC_TEMPORARY;
+ ad->ad_type = slap_schema.si_at_undefined;
+
+ return ad;
+}
+
+static int
+undef_promote(
+ AttributeType *at,
+ char *name,
+ AttributeType *nat )
+{
+ AttributeDescription **u_ad, **n_ad;
+
+ /* Get to last ad on the new type */
+ for ( n_ad = &nat->sat_ad; *n_ad; n_ad = &(*n_ad)->ad_next ) ;
+
+ for ( u_ad = &at->sat_ad; *u_ad; ) {
+ struct berval bv;
+
+ ber_str2bv( name, 0, 0, &bv );
+
+ /* remove iff undef == name or undef == name;tag */
+ if ( (*u_ad)->ad_cname.bv_len >= bv.bv_len
+ && strncasecmp( (*u_ad)->ad_cname.bv_val, bv.bv_val, bv.bv_len ) == 0
+ && ( (*u_ad)->ad_cname.bv_val[ bv.bv_len ] == '\0'
+ || (*u_ad)->ad_cname.bv_val[ bv.bv_len ] == ';' ) )
+ {
+ AttributeDescription *tmp = *u_ad;
+
+ *u_ad = (*u_ad)->ad_next;
+
+ tmp->ad_type = nat;
+ tmp->ad_next = NULL;
+ /* ad_cname was contiguous, no leak here */
+ tmp->ad_cname = nat->sat_cname;
+ ldap_pvt_thread_mutex_lock( &ad_index_mutex );
+ tmp->ad_index = ++ad_count;
+ ldap_pvt_thread_mutex_unlock( &ad_index_mutex );
+ *n_ad = tmp;
+ n_ad = &tmp->ad_next;
+ } else {
+ u_ad = &(*u_ad)->ad_next;
+ }
+ }
+
+ return 0;
+}
+
+int
+slap_ad_undef_promote(
+ char *name,
+ AttributeType *at )
+{
+ int rc;
+
+ ldap_pvt_thread_mutex_lock( &ad_undef_mutex );
+
+ rc = undef_promote( slap_schema.si_at_undefined, name, at );
+ if ( rc == 0 ) {
+ rc = undef_promote( slap_schema.si_at_proxied, name, at );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &ad_undef_mutex );
+
+ return rc;
+}
+
+int
+an_find(
+ AttributeName *a,
+ struct berval *s
+)
+{
+ if( a == NULL ) return 0;
+
+ for ( ; a->an_name.bv_val; a++ ) {
+ if ( a->an_name.bv_len != s->bv_len) continue;
+ if ( strcasecmp( s->bv_val, a->an_name.bv_val ) == 0 ) {
+ return( 1 );
+ }
+ }
+
+ return( 0 );
+}
+
+/*
+ * Convert a delimited string into a list of AttributeNames; add
+ * on to an existing list if it was given. If the string is not
+ * a valid attribute name, if a '-' is prepended it is skipped
+ * and the remaining name is tried again; if a '@' (or '+') is
+ * prepended, an objectclass name is searched instead; if a '!'
+ * is prepended, the objectclass name is negated.
+ *
+ * NOTE: currently, if a valid attribute name is not found, the
+ * same string is also checked as valid objectclass name; however,
+ * this behavior is deprecated.
+ */
+AttributeName *
+str2anlist( AttributeName *an, char *in, const char *brkstr )
+{
+ char *str;
+ char *s;
+ char *lasts;
+ int i, j;
+ const char *text;
+ AttributeName *anew;
+
+ /* find last element in list */
+ i = 0;
+ if ( an != NULL ) {
+ for ( i = 0; !BER_BVISNULL( &an[ i ].an_name ) ; i++)
+ ;
+ }
+
+ /* protect the input string from strtok */
+ str = ch_strdup( in );
+
+ /* Count words in string */
+ j = 1;
+ for ( s = str; *s; s++ ) {
+ if ( strchr( brkstr, *s ) != NULL ) {
+ j++;
+ }
+ }
+
+ an = ch_realloc( an, ( i + j + 1 ) * sizeof( AttributeName ) );
+ anew = an + i;
+ for ( s = ldap_pvt_strtok( str, brkstr, &lasts );
+ s != NULL;
+ s = ldap_pvt_strtok( NULL, brkstr, &lasts ) )
+ {
+ /* put a stop mark */
+ BER_BVZERO( &anew[1].an_name );
+
+ anew->an_desc = NULL;
+ anew->an_oc = NULL;
+ anew->an_flags = 0;
+ ber_str2bv(s, 0, 1, &anew->an_name);
+ slap_bv2ad(&anew->an_name, &anew->an_desc, &text);
+ if ( !anew->an_desc ) {
+ switch( anew->an_name.bv_val[0] ) {
+ case '-': {
+ struct berval adname;
+ adname.bv_len = anew->an_name.bv_len - 1;
+ adname.bv_val = &anew->an_name.bv_val[1];
+ slap_bv2ad(&adname, &anew->an_desc, &text);
+ if ( !anew->an_desc ) {
+ goto reterr;
+ }
+ } break;
+
+ case '@':
+ case '+': /* (deprecated) */
+ case '!': {
+ struct berval ocname;
+ ocname.bv_len = anew->an_name.bv_len - 1;
+ ocname.bv_val = &anew->an_name.bv_val[1];
+ anew->an_oc = oc_bvfind( &ocname );
+ if ( !anew->an_oc ) {
+ goto reterr;
+ }
+
+ if ( anew->an_name.bv_val[0] == '!' ) {
+ anew->an_flags |= SLAP_AN_OCEXCLUDE;
+ }
+ } break;
+
+ default:
+ /* old (deprecated) way */
+ anew->an_oc = oc_bvfind( &anew->an_name );
+ if ( !anew->an_oc ) {
+ goto reterr;
+ }
+ }
+ }
+ anew->an_flags |= SLAP_AN_OCINITED;
+ anew++;
+ }
+
+ BER_BVZERO( &anew->an_name );
+ free( str );
+ return( an );
+
+reterr:
+ anlist_free( an, 1, NULL );
+
+ /*
+ * overwrites input string
+ * on error!
+ */
+ strcpy( in, s );
+ free( str );
+ return NULL;
+}
+
+void
+anlist_free( AttributeName *an, int freename, void *ctx )
+{
+ if ( an == NULL ) {
+ return;
+ }
+
+ if ( freename ) {
+ int i;
+
+ for ( i = 0; an[i].an_name.bv_val; i++ ) {
+ ber_memfree_x( an[i].an_name.bv_val, ctx );
+ }
+ }
+
+ ber_memfree_x( an, ctx );
+}
+
+char **anlist2charray_x( AttributeName *an, int dup, void *ctx )
+{
+ char **attrs;
+ int i;
+
+ if ( an != NULL ) {
+ for ( i = 0; !BER_BVISNULL( &an[i].an_name ); i++ )
+ ;
+ attrs = (char **) slap_sl_malloc( (i + 1) * sizeof(char *), ctx );
+ for ( i = 0; !BER_BVISNULL( &an[i].an_name ); i++ ) {
+ if ( dup )
+ attrs[i] = ch_strdup( an[i].an_name.bv_val );
+ else
+ attrs[i] = an[i].an_name.bv_val;
+ }
+ attrs[i] = NULL;
+ } else {
+ attrs = NULL;
+ }
+
+ return attrs;
+}
+
+char **anlist2charray( AttributeName *an, int dup )
+{
+ return anlist2charray_x( an, dup, NULL );
+}
+
+char**
+anlist2attrs( AttributeName * anlist )
+{
+ int i, j, k = 0;
+ int n;
+ char **attrs;
+ ObjectClass *oc;
+
+ if ( anlist == NULL )
+ return NULL;
+
+ for ( i = 0; anlist[i].an_name.bv_val; i++ ) {
+ if ( ( oc = anlist[i].an_oc ) ) {
+ for ( j = 0; oc->soc_required && oc->soc_required[j]; j++ ) ;
+ k += j;
+ for ( j = 0; oc->soc_allowed && oc->soc_allowed[j]; j++ ) ;
+ k += j;
+ }
+ }
+
+ if ( i == 0 )
+ return NULL;
+
+ attrs = anlist2charray( anlist, 1 );
+
+ n = i;
+
+ if ( k )
+ attrs = (char **) ch_realloc( attrs, (i + k + 1) * sizeof( char * ));
+
+ for ( i = 0; anlist[i].an_name.bv_val; i++ ) {
+ if ( ( oc = anlist[i].an_oc ) ) {
+ for ( j = 0; oc->soc_required && oc->soc_required[j]; j++ ) {
+ attrs[n++] = ch_strdup(
+ oc->soc_required[j]->sat_cname.bv_val );
+ }
+ for ( j = 0; oc->soc_allowed && oc->soc_allowed[j]; j++ ) {
+ attrs[n++] = ch_strdup(
+ oc->soc_allowed[j]->sat_cname.bv_val );
+ }
+ }
+ }
+
+ if ( attrs )
+ attrs[n] = NULL;
+
+ i = 0;
+ while ( attrs && attrs[i] ) {
+ if ( *attrs[i] == '@' ) {
+ ch_free( attrs[i] );
+ for ( j = i; attrs[j]; j++ ) {
+ attrs[j] = attrs[j+1];
+ }
+ } else {
+ i++;
+ }
+ }
+
+ for ( i = 0; attrs && attrs[i]; i++ ) {
+ j = i + 1;
+ while ( attrs && attrs[j] ) {
+ if ( !strcmp( attrs[i], attrs[j] )) {
+ ch_free( attrs[j] );
+ for ( k = j; attrs && attrs[k]; k++ ) {
+ attrs[k] = attrs[k+1];
+ }
+ } else {
+ j++;
+ }
+ }
+ }
+
+ if ( i != n )
+ attrs = (char **) ch_realloc( attrs, (i+1) * sizeof( char * ));
+
+ return attrs;
+}
+
+#define LBUFSIZ 80
+AttributeName*
+file2anlist( AttributeName *an, const char *fname, const char *brkstr )
+{
+ FILE *fp;
+ char *line = NULL;
+ char *lcur = NULL;
+ char *c;
+ size_t lmax = LBUFSIZ;
+
+ fp = fopen( fname, "r" );
+ if ( fp == NULL ) {
+ char ebuf[128];
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,
+ "get_attrs_from_file: failed to open attribute list file "
+ "\"%s\": %s\n", fname, AC_STRERROR_R( saved_errno, ebuf, sizeof(ebuf) ) );
+ return NULL;
+ }
+
+ lcur = line = (char *) ch_malloc( lmax );
+ if ( !line ) {
+ Debug( LDAP_DEBUG_ANY,
+ "get_attrs_from_file: could not allocate memory\n" );
+ fclose(fp);
+ return NULL;
+ }
+
+ while ( fgets( lcur, LBUFSIZ, fp ) != NULL ) {
+ if ( ( c = strchr( lcur, '\n' ) ) ) {
+ if ( c == line ) {
+ *c = '\0';
+ } else if ( *(c-1) == '\r' ) {
+ *(c-1) = '\0';
+ } else {
+ *c = '\0';
+ }
+ } else {
+ lmax += LBUFSIZ;
+ line = (char *) ch_realloc( line, lmax );
+ if ( !line ) {
+ Debug( LDAP_DEBUG_ANY,
+ "get_attrs_from_file: could not allocate memory\n" );
+ fclose(fp);
+ return NULL;
+ }
+ lcur = line + strlen( line );
+ continue;
+ }
+ an = str2anlist( an, line, brkstr );
+ if ( an == NULL )
+ break;
+ lcur = line;
+ }
+ ch_free( line );
+ fclose(fp);
+ return an;
+}
+#undef LBUFSIZ
+
+/* Define an attribute option. */
+int
+ad_define_option( const char *name, const char *fname, int lineno )
+{
+ int i;
+ unsigned int optlen;
+
+ if ( options == &lang_option ) {
+ options = NULL;
+ option_count = 0;
+ }
+ if ( name == NULL )
+ return 0;
+
+ optlen = 0;
+ do {
+ if ( !DESC_CHAR( name[optlen] ) ) {
+ /* allow trailing '=', same as '-' */
+ if ( name[optlen] == '=' && !name[optlen+1] ) {
+ msad_range_hack = 1;
+ continue;
+ }
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: illegal option name \"%s\"\n",
+ fname, lineno, name );
+ return 1;
+ }
+ } while ( name[++optlen] );
+
+ options = ch_realloc( options,
+ (option_count+1) * sizeof(Attr_option) );
+
+ if ( strcasecmp( name, "binary" ) == 0
+ || ad_find_option_definition( name, optlen ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: option \"%s\" is already defined\n",
+ fname, lineno, name );
+ return 1;
+ }
+
+ for ( i = option_count; i; --i ) {
+ if ( strcasecmp( name, options[i-1].name.bv_val ) >= 0 )
+ break;
+ options[i] = options[i-1];
+ }
+
+ options[i].name.bv_val = ch_strdup( name );
+ options[i].name.bv_len = optlen;
+ options[i].prefix = (name[optlen-1] == '-') ||
+ (name[optlen-1] == '=');
+
+ if ( i != option_count &&
+ options[i].prefix &&
+ optlen < options[i+1].name.bv_len &&
+ strncasecmp( name, options[i+1].name.bv_val, optlen ) == 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: option \"%s\" overrides previous option\n",
+ fname, lineno, name );
+ return 1;
+ }
+
+ option_count++;
+ return 0;
+}
+
+void
+ad_unparse_options( BerVarray *res )
+{
+ int i;
+ for ( i = 0; i < option_count; i++ ) {
+ value_add_one( res, &options[i].name );
+ }
+}
+
+/* Find the definition of the option name or prefix matching the arguments */
+static Attr_option *
+ad_find_option_definition( const char *opt, int optlen )
+{
+ int top = 0, bot = option_count;
+ while ( top < bot ) {
+ int mid = (top + bot) / 2;
+ int mlen = options[mid].name.bv_len;
+ char *mname = options[mid].name.bv_val;
+ int j;
+ if ( optlen < mlen ) {
+ j = strncasecmp( opt, mname, optlen ) - 1;
+ } else {
+ j = strncasecmp( opt, mname, mlen );
+ if ( j==0 && (optlen==mlen || options[mid].prefix) )
+ return &options[mid];
+ }
+ if ( j < 0 )
+ bot = mid;
+ else
+ top = mid + 1;
+ }
+ return NULL;
+}
+
+MatchingRule *ad_mr(
+ AttributeDescription *ad,
+ unsigned usage )
+{
+ switch( usage & SLAP_MR_TYPE_MASK ) {
+ case SLAP_MR_NONE:
+ case SLAP_MR_EQUALITY:
+ return ad->ad_type->sat_equality;
+ break;
+ case SLAP_MR_ORDERING:
+ return ad->ad_type->sat_ordering;
+ break;
+ case SLAP_MR_SUBSTR:
+ return ad->ad_type->sat_substr;
+ break;
+ case SLAP_MR_EXT:
+ default:
+ assert( 0 /* ad_mr: bad usage */);
+ }
+ return NULL;
+}
diff --git a/servers/slapd/add.c b/servers/slapd/add.c
new file mode 100644
index 0000000..2898d6c
--- /dev/null
+++ b/servers/slapd/add.c
@@ -0,0 +1,693 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/socket.h>
+
+#include "lutil.h"
+#include "slap.h"
+
+int
+do_add( Operation *op, SlapReply *rs )
+{
+ BerElement *ber = op->o_ber;
+ char *last;
+ struct berval dn = BER_BVNULL;
+ ber_len_t len;
+ ber_tag_t tag;
+ Modifications *modlist = NULL;
+ Modifications **modtail = &modlist;
+ Modifications tmp;
+ char textbuf[ SLAP_TEXT_BUFLEN ];
+ size_t textlen = sizeof( textbuf );
+ int rc = 0;
+ int freevals = 1;
+ OpExtraDB *oex;
+
+ Debug( LDAP_DEBUG_TRACE, "%s do_add\n",
+ op->o_log_prefix );
+
+ /*
+ * Parse the add request. It looks like this:
+ *
+ * AddRequest := [APPLICATION 14] SEQUENCE {
+ * name DistinguishedName,
+ * attrs SEQUENCE OF SEQUENCE {
+ * type AttributeType,
+ * values SET OF AttributeValue
+ * }
+ * }
+ */
+
+ /* get the name */
+ if ( ber_scanf( ber, "{m", /*}*/ &dn ) == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_add: ber_scanf failed\n",
+ op->o_log_prefix );
+ send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
+ return SLAPD_DISCONNECT;
+ }
+
+ Debug( LDAP_DEBUG_ARGS, "%s do_add: dn (%s)\n",
+ op->o_log_prefix, dn.bv_val );
+
+ /* get the attrs */
+ for ( tag = ber_first_element( ber, &len, &last ); tag != LBER_DEFAULT;
+ tag = ber_next_element( ber, &len, last ) )
+ {
+ Modifications *mod;
+ ber_tag_t rtag;
+
+ tmp.sml_nvalues = NULL;
+
+ rtag = ber_scanf( ber, "{m{W}}", &tmp.sml_type, &tmp.sml_values );
+
+ if ( rtag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_add: decoding error\n",
+ op->o_log_prefix );
+ send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
+ rs->sr_err = SLAPD_DISCONNECT;
+ goto done;
+ }
+
+ if ( tmp.sml_values == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_add: no values for type %s\n",
+ op->o_log_prefix, tmp.sml_type.bv_val );
+ send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR,
+ "no values for attribute type" );
+ goto done;
+ }
+
+ mod = (Modifications *) ch_malloc( sizeof(Modifications) );
+ mod->sml_op = LDAP_MOD_ADD;
+ mod->sml_flags = 0;
+ mod->sml_next = NULL;
+ mod->sml_desc = NULL;
+ mod->sml_type = tmp.sml_type;
+ mod->sml_values = tmp.sml_values;
+ mod->sml_nvalues = NULL;
+
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ }
+
+ if ( ber_scanf( ber, /*{*/ "}") == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_add: ber_scanf failed\n",
+ op->o_log_prefix );
+ send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
+ rs->sr_err = SLAPD_DISCONNECT;
+ goto done;
+ }
+
+ if ( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_add: get_ctrls failed\n",
+ op->o_log_prefix );
+ goto done;
+ }
+
+ rs->sr_err = dnPrettyNormal( NULL, &dn, &op->o_req_dn, &op->o_req_ndn,
+ op->o_tmpmemctx );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_add: invalid dn (%s)\n",
+ op->o_log_prefix, dn.bv_val );
+ send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid DN" );
+ goto done;
+ }
+
+ op->ora_e = entry_alloc();
+ ber_dupbv( &op->ora_e->e_name, &op->o_req_dn );
+ ber_dupbv( &op->ora_e->e_nname, &op->o_req_ndn );
+
+ Debug( LDAP_DEBUG_STATS, "%s ADD dn=\"%s\"\n",
+ op->o_log_prefix, op->o_req_dn.bv_val );
+
+ if ( modlist == NULL ) {
+ send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR,
+ "no attributes provided" );
+ goto done;
+ }
+
+ if ( dn_match( &op->ora_e->e_nname, &slap_empty_bv ) ) {
+ /* protocolError may be a more appropriate error */
+ send_ldap_error( op, rs, LDAP_ALREADY_EXISTS,
+ "root DSE already exists" );
+ goto done;
+
+ } else if ( dn_match( &op->ora_e->e_nname, &frontendDB->be_schemandn ) ) {
+ send_ldap_error( op, rs, LDAP_ALREADY_EXISTS,
+ "subschema subentry already exists" );
+ goto done;
+ }
+
+ rs->sr_err = slap_mods_check( op, modlist, &rs->sr_text,
+ textbuf, textlen, NULL );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ /* temporary; remove if not invoking backend function */
+ op->ora_modlist = modlist;
+
+ /* call this so global overlays/SLAPI have access to ora_e */
+ rs->sr_err = slap_mods2entry( op->ora_modlist, &op->ora_e,
+ 1, 0, &rs->sr_text, textbuf, textlen );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ freevals = 0;
+ oex = op->o_tmpalloc( sizeof(OpExtraDB), op->o_tmpmemctx );
+ oex->oe.oe_key = (void *)do_add;
+ oex->oe_db = NULL;
+ LDAP_SLIST_INSERT_HEAD(&op->o_extra, &oex->oe, oe_next);
+
+ op->o_bd = frontendDB;
+ rc = frontendDB->be_add( op, rs );
+
+ if ( rc == SLAPD_ASYNCOP ) {
+ /* skip cleanup */
+ return rc;
+ }
+
+ LDAP_SLIST_REMOVE(&op->o_extra, &oex->oe, OpExtra, oe_next);
+ if ( rc == LDAP_TXN_SPECIFY_OKAY ) {
+ /* skip cleanup */
+ return rc;
+ } else if ( rc == 0 ) {
+ if ( op->ora_e != NULL && oex->oe_db != NULL ) {
+ BackendDB *bd = op->o_bd;
+
+ op->o_bd = oex->oe_db;
+
+ be_entry_release_w( op, op->ora_e );
+
+ op->ora_e = NULL;
+ op->o_bd = bd;
+ }
+ }
+ op->o_tmpfree( oex, op->o_tmpmemctx );
+
+done:;
+ if ( modlist != NULL ) {
+ /* in case of error, free the values as well */
+ slap_mods_free( modlist, freevals );
+ }
+
+ if ( op->ora_e != NULL ) {
+ entry_free( op->ora_e );
+ }
+ op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
+
+ return rc;
+}
+
+int
+fe_op_add( Operation *op, SlapReply *rs )
+{
+ Modifications **modtail = &op->ora_modlist;
+ int rc = 0;
+ BackendDB *op_be, *bd = op->o_bd;
+ char textbuf[ SLAP_TEXT_BUFLEN ];
+ size_t textlen = sizeof( textbuf );
+
+ /*
+ * We could be serving multiple database backends. Select the
+ * appropriate one, or send a referral to our "referral server"
+ * if we don't hold it.
+ */
+ op->o_bd = select_backend( &op->ora_e->e_nname, 1 );
+ if ( op->o_bd == NULL ) {
+ op->o_bd = bd;
+ rs->sr_ref = referral_rewrite( default_referral,
+ NULL, &op->ora_e->e_name, LDAP_SCOPE_DEFAULT );
+ if ( !rs->sr_ref ) rs->sr_ref = default_referral;
+ if ( rs->sr_ref ) {
+ rs->sr_err = LDAP_REFERRAL;
+ send_ldap_result( op, rs );
+
+ if ( rs->sr_ref != default_referral ) {
+ ber_bvarray_free( rs->sr_ref );
+ }
+ } else {
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "no global superior knowledge" );
+ }
+ goto done;
+ }
+
+ /* If we've got a glued backend, check the real backend */
+ op_be = op->o_bd;
+ if ( SLAP_GLUE_INSTANCE( op->o_bd )) {
+ op->o_bd = select_backend( &op->ora_e->e_nname, 0 );
+ }
+
+ /* check restrictions */
+ if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ /* check for referrals */
+ if( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ rs->sr_err = slap_mods_obsolete_check( op, op->ora_modlist,
+ &rs->sr_text, textbuf, textlen );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ /*
+ * do the add if 1 && (2 || 3)
+ * 1) there is an add function implemented in this backend;
+ * 2) this backend is the provider for what it holds;
+ * 3) it's a replica and the dn supplied is the updatedn.
+ */
+ if ( op->o_bd->be_add ) {
+ /* do the update here */
+ int repl_user = be_isupdate( op );
+ if ( !SLAP_SINGLE_SHADOW(op->o_bd) || repl_user ) {
+ int update = !BER_BVISEMPTY( &op->o_bd->be_update_ndn );
+
+ if ( !update ) {
+ rs->sr_err = slap_mods_no_user_mod_check( op, op->ora_modlist,
+ &rs->sr_text, textbuf, textlen );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto done;
+ }
+ }
+
+ if ( !repl_user ) {
+ /* go to the last mod */
+ for ( modtail = &op->ora_modlist;
+ *modtail != NULL;
+ modtail = &(*modtail)->sml_next )
+ {
+ assert( (*modtail)->sml_op == LDAP_MOD_ADD );
+ assert( (*modtail)->sml_desc != NULL );
+ }
+
+
+ /* check for unmodifiable attributes */
+ rs->sr_err = slap_mods_no_repl_user_mod_check( op,
+ op->ora_modlist, &rs->sr_text, textbuf, textlen );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto done;
+ }
+ }
+
+ if ( op->o_txnSpec ) {
+ rc = txn_preop( op, rs );
+ goto done;
+ }
+
+ op->o_bd = op_be;
+ rc = op->o_bd->be_add( op, rs );
+ if ( rc == LDAP_SUCCESS ) {
+ OpExtra *oex;
+ /* NOTE: be_entry_release_w() is
+ * called by do_add(), so that global
+ * overlays on the way back can
+ * at least read the entry */
+ LDAP_SLIST_FOREACH(oex, &op->o_extra, oe_next) {
+ if ( oex->oe_key == (void *)do_add ) {
+ ((OpExtraDB *)oex)->oe_db = op->o_bd;
+ break;
+ }
+ }
+ }
+
+ } else {
+ BerVarray defref = NULL;
+
+ defref = op->o_bd->be_update_refs
+ ? op->o_bd->be_update_refs : default_referral;
+
+ if ( defref != NULL ) {
+ rs->sr_ref = referral_rewrite( defref,
+ NULL, &op->ora_e->e_name, LDAP_SCOPE_DEFAULT );
+ if ( rs->sr_ref == NULL ) rs->sr_ref = defref;
+ rs->sr_err = LDAP_REFERRAL;
+ if (!rs->sr_ref) rs->sr_ref = default_referral;
+ send_ldap_result( op, rs );
+
+ if ( rs->sr_ref != default_referral ) {
+ ber_bvarray_free( rs->sr_ref );
+ }
+ } else {
+ send_ldap_error( op, rs,
+ LDAP_UNWILLING_TO_PERFORM,
+ "shadow context; no update referral" );
+ }
+ }
+ } else {
+ Debug( LDAP_DEBUG_ARGS, "do_add: no backend support\n" );
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "operation not supported within namingContext" );
+ }
+
+done:;
+ op->o_bd = bd;
+ return rc;
+}
+
+int
+slap_mods2entry(
+ Modifications *mods,
+ Entry **e,
+ int initial,
+ int dup,
+ const char **text,
+ char *textbuf, size_t textlen )
+{
+ Attribute **tail;
+ int i;
+
+ if ( initial ) {
+ assert( (*e)->e_attrs == NULL );
+ }
+
+ for ( tail = &(*e)->e_attrs; *tail != NULL; tail = &(*tail)->a_next )
+ ;
+
+ *text = textbuf;
+
+ for( ; mods != NULL; mods = mods->sml_next ) {
+ Attribute *attr;
+
+ assert( mods->sml_desc != NULL );
+
+ attr = attr_find( (*e)->e_attrs, mods->sml_desc );
+
+ if( attr != NULL ) {
+#define SLURPD_FRIENDLY
+#ifdef SLURPD_FRIENDLY
+ int j;
+
+ if ( !initial ) {
+ /*
+ * This check allows overlays to override operational
+ * attributes by setting them directly in the entry.
+ * We assume slap_mods_no_user_mod_check() was called
+ * with the user modifications.
+ */
+ *text = NULL;
+ return LDAP_SUCCESS;
+ }
+
+ i = attr->a_numvals;
+ j = mods->sml_numvals;
+ attr->a_numvals += j;
+ j++; /* NULL */
+
+ attr->a_vals = ch_realloc( attr->a_vals,
+ sizeof( struct berval ) * (i+j) );
+
+ /* checked for duplicates in slap_mods_check */
+
+ if ( dup ) {
+ for ( j = 0; mods->sml_values[j].bv_val; j++ ) {
+ ber_dupbv( &attr->a_vals[i+j], &mods->sml_values[j] );
+ }
+ BER_BVZERO( &attr->a_vals[i+j] );
+ j++;
+ } else {
+ AC_MEMCPY( &attr->a_vals[i], mods->sml_values,
+ sizeof( struct berval ) * j );
+ }
+
+ if( mods->sml_nvalues ) {
+ attr->a_nvals = ch_realloc( attr->a_nvals,
+ sizeof( struct berval ) * (i+j) );
+ if ( dup ) {
+ for ( j = 0; mods->sml_nvalues[j].bv_val; j++ ) {
+ ber_dupbv( &attr->a_nvals[i+j], &mods->sml_nvalues[j] );
+ }
+ BER_BVZERO( &attr->a_nvals[i+j] );
+ } else {
+ AC_MEMCPY( &attr->a_nvals[i], mods->sml_nvalues,
+ sizeof( struct berval ) * j );
+ }
+ } else {
+ attr->a_nvals = attr->a_vals;
+ }
+
+ continue;
+#else
+ snprintf( textbuf, textlen,
+ "attribute '%s' provided more than once",
+ mods->sml_desc->ad_cname.bv_val );
+ *text = textbuf;
+ return LDAP_TYPE_OR_VALUE_EXISTS;
+#endif
+ }
+
+ attr = attr_alloc( mods->sml_desc );
+
+ /* move values to attr structure */
+ i = mods->sml_numvals;
+ attr->a_numvals = mods->sml_numvals;
+ if ( dup ) {
+ attr->a_vals = (BerVarray) ch_calloc( i+1, sizeof( BerValue ));
+ for ( i = 0; mods->sml_values[i].bv_val; i++ ) {
+ ber_dupbv( &attr->a_vals[i], &mods->sml_values[i] );
+ }
+ BER_BVZERO( &attr->a_vals[i] );
+ } else {
+ attr->a_vals = mods->sml_values;
+ }
+
+ if ( mods->sml_nvalues ) {
+ if ( dup ) {
+ i = mods->sml_numvals;
+ attr->a_nvals = (BerVarray) ch_calloc( i+1, sizeof( BerValue ));
+ for ( i = 0; mods->sml_nvalues[i].bv_val; i++ ) {
+ ber_dupbv( &attr->a_nvals[i], &mods->sml_nvalues[i] );
+ }
+ BER_BVZERO( &attr->a_nvals[i] );
+ } else {
+ attr->a_nvals = mods->sml_nvalues;
+ }
+ } else {
+ attr->a_nvals = attr->a_vals;
+ }
+
+ *tail = attr;
+ tail = &attr->a_next;
+ }
+
+ *text = NULL;
+
+ return LDAP_SUCCESS;
+}
+
+int
+slap_entry2mods(
+ Entry *e,
+ Modifications **mods,
+ const char **text,
+ char *textbuf, size_t textlen )
+{
+ Modifications *modhead = NULL;
+ Modifications *mod;
+ Modifications **modtail = &modhead;
+ Attribute *a_new;
+ AttributeDescription *a_new_desc;
+ int i, count;
+
+ a_new = e->e_attrs;
+
+ while ( a_new != NULL ) {
+ a_new_desc = a_new->a_desc;
+ mod = (Modifications *) ch_malloc( sizeof( Modifications ));
+
+ mod->sml_op = LDAP_MOD_REPLACE;
+ mod->sml_flags = 0;
+
+ mod->sml_type = a_new_desc->ad_cname;
+
+ count = a_new->a_numvals;
+ mod->sml_numvals = a_new->a_numvals;
+
+ mod->sml_values = (struct berval*) ch_malloc(
+ (count+1) * sizeof( struct berval) );
+
+ /* see slap_mods_check() comments...
+ * if a_vals == a_nvals, there is no normalizer.
+ * in this case, mod->sml_nvalues must be left NULL.
+ */
+ if ( a_new->a_vals != a_new->a_nvals ) {
+ mod->sml_nvalues = (struct berval*) ch_malloc(
+ (count+1) * sizeof( struct berval) );
+ } else {
+ mod->sml_nvalues = NULL;
+ }
+
+ for ( i = 0; i < count; i++ ) {
+ ber_dupbv(mod->sml_values+i, a_new->a_vals+i);
+ if ( mod->sml_nvalues ) {
+ ber_dupbv( mod->sml_nvalues+i, a_new->a_nvals+i );
+ }
+ }
+
+ mod->sml_values[count].bv_val = NULL;
+ mod->sml_values[count].bv_len = 0;
+
+ if ( mod->sml_nvalues ) {
+ mod->sml_nvalues[count].bv_val = NULL;
+ mod->sml_nvalues[count].bv_len = 0;
+ }
+
+ mod->sml_desc = a_new_desc;
+ mod->sml_next =NULL;
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ a_new = a_new->a_next;
+ }
+
+ *mods = modhead;
+
+ return LDAP_SUCCESS;
+}
+
+int slap_add_opattrs(
+ Operation *op,
+ const char **text,
+ char *textbuf,
+ size_t textlen,
+ int manage_ctxcsn )
+{
+ struct berval name, timestamp, csn = BER_BVNULL;
+ struct berval nname, tmp;
+ char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+ char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE ];
+ Attribute *a;
+
+ if ( SLAP_LASTMOD( op->o_bd ) ) {
+ char *ptr;
+ int gotcsn = 0;
+
+ timestamp.bv_val = timebuf;
+ a = attr_find( op->ora_e->e_attrs, slap_schema.si_ad_entryCSN );
+ if ( a ) {
+ gotcsn = 1;
+ csn = a->a_vals[0];
+ }
+ if ( BER_BVISEMPTY( &op->o_csn )) {
+ if ( !gotcsn ) {
+ csn.bv_val = csnbuf;
+ csn.bv_len = sizeof(csnbuf);
+ slap_get_csn( op, &csn, manage_ctxcsn );
+ } else {
+ if ( manage_ctxcsn )
+ slap_queue_csn( op, &csn );
+ }
+ } else {
+ csn = op->o_csn;
+ }
+ ptr = ber_bvchr( &csn, '#' );
+ if ( ptr ) {
+ timestamp.bv_len = STRLENOF("YYYYMMDDHHMMSSZ");
+ AC_MEMCPY( timebuf, csn.bv_val, timestamp.bv_len );
+ timebuf[timestamp.bv_len-1] = 'Z';
+ timebuf[timestamp.bv_len] = '\0';
+ } else {
+ time_t now = slap_get_time();
+
+ timestamp.bv_len = sizeof(timebuf);
+
+ slap_timestamp( &now, &timestamp );
+ }
+
+ if ( BER_BVISEMPTY( &op->o_dn ) ) {
+ BER_BVSTR( &name, SLAPD_ANONYMOUS );
+ nname = name;
+ } else {
+ name = op->o_dn;
+ nname = op->o_ndn;
+ }
+
+ a = attr_find( op->ora_e->e_attrs,
+ slap_schema.si_ad_entryUUID );
+ if ( !a ) {
+ char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
+
+ tmp.bv_len = lutil_uuidstr( uuidbuf, sizeof( uuidbuf ) );
+ tmp.bv_val = uuidbuf;
+
+ attr_merge_normalize_one( op->ora_e,
+ slap_schema.si_ad_entryUUID, &tmp, op->o_tmpmemctx );
+ }
+
+ a = attr_find( op->ora_e->e_attrs,
+ slap_schema.si_ad_creatorsName );
+ if ( !a ) {
+ attr_merge_one( op->ora_e,
+ slap_schema.si_ad_creatorsName, &name, &nname );
+ }
+
+ a = attr_find( op->ora_e->e_attrs,
+ slap_schema.si_ad_createTimestamp );
+ if ( !a ) {
+ attr_merge_one( op->ora_e,
+ slap_schema.si_ad_createTimestamp, &timestamp, NULL );
+ }
+
+ if ( !gotcsn ) {
+ attr_merge_one( op->ora_e,
+ slap_schema.si_ad_entryCSN, &csn, NULL );
+ }
+
+ a = attr_find( op->ora_e->e_attrs,
+ slap_schema.si_ad_modifiersName );
+ if ( !a ) {
+ attr_merge_one( op->ora_e,
+ slap_schema.si_ad_modifiersName, &name, &nname );
+ }
+
+ a = attr_find( op->ora_e->e_attrs,
+ slap_schema.si_ad_modifyTimestamp );
+ if ( !a ) {
+ attr_merge_one( op->ora_e,
+ slap_schema.si_ad_modifyTimestamp, &timestamp, NULL );
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
diff --git a/servers/slapd/at.c b/servers/slapd/at.c
new file mode 100644
index 0000000..63a9e9c
--- /dev/null
+++ b/servers/slapd/at.c
@@ -0,0 +1,1108 @@
+/* at.c - routines for dealing with attribute types */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "slap.h"
+
+
+const char *
+at_syntax(
+ AttributeType *at )
+{
+ for ( ; at != NULL; at = at->sat_sup ) {
+ if ( at->sat_syntax_oid ) {
+ return at->sat_syntax_oid;
+ }
+ }
+
+ assert( 0 );
+
+ return NULL;
+}
+
+int
+is_at_syntax(
+ AttributeType *at,
+ const char *oid )
+{
+ const char *syn_oid = at_syntax( at );
+
+ if ( syn_oid ) {
+ return strcmp( syn_oid, oid ) == 0;
+ }
+
+ return 0;
+}
+
+int is_at_subtype(
+ AttributeType *sub,
+ AttributeType *sup )
+{
+ for( ; sub != NULL; sub = sub->sat_sup ) {
+ if( sub == sup ) return 1;
+ }
+
+ return 0;
+}
+
+struct aindexrec {
+ struct berval air_name;
+ AttributeType *air_at;
+};
+
+static Avlnode *attr_index = NULL;
+static Avlnode *attr_cache = NULL;
+static LDAP_STAILQ_HEAD(ATList, AttributeType) attr_list
+ = LDAP_STAILQ_HEAD_INITIALIZER(attr_list);
+
+/* Last hardcoded attribute registered */
+AttributeType *at_sys_tail;
+
+int at_oc_cache;
+
+static int
+attr_index_cmp(
+ const void *v_air1,
+ const void *v_air2 )
+{
+ const struct aindexrec *air1 = v_air1;
+ const struct aindexrec *air2 = v_air2;
+ int i = air1->air_name.bv_len - air2->air_name.bv_len;
+ if (i) return i;
+ return (strcasecmp( air1->air_name.bv_val, air2->air_name.bv_val ));
+}
+
+static int
+attr_index_name_cmp(
+ const void *v_type,
+ const void *v_air )
+{
+ const struct berval *type = v_type;
+ const struct aindexrec *air = v_air;
+ int i = type->bv_len - air->air_name.bv_len;
+ if (i) return i;
+ return (strncasecmp( type->bv_val, air->air_name.bv_val, type->bv_len ));
+}
+
+AttributeType *
+at_find( const char *name )
+{
+ struct berval bv;
+
+ bv.bv_val = (char *)name;
+ bv.bv_len = strlen( name );
+
+ return at_bvfind( &bv );
+}
+
+AttributeType *
+at_bvfind( struct berval *name )
+{
+ struct aindexrec *air;
+
+ if ( attr_cache ) {
+ air = ldap_avl_find( attr_cache, name, attr_index_name_cmp );
+ if ( air ) return air->air_at;
+ }
+
+ air = ldap_avl_find( attr_index, name, attr_index_name_cmp );
+
+ if ( air ) {
+ if ( air->air_at->sat_flags & SLAP_AT_DELETED ) {
+ air = NULL;
+ } else if (( slapMode & SLAP_TOOL_MODE ) && at_oc_cache ) {
+ ldap_avl_insert( &attr_cache, (caddr_t) air,
+ attr_index_cmp, ldap_avl_dup_error );
+ }
+ }
+
+ return air != NULL ? air->air_at : NULL;
+}
+
+int
+at_append_to_list(
+ AttributeType *sat,
+ AttributeType ***listp )
+{
+ AttributeType **list;
+ AttributeType **list1;
+ int size;
+
+ list = *listp;
+ if ( !list ) {
+ size = 2;
+ list = ch_calloc(size, sizeof(AttributeType *));
+ if ( !list ) {
+ return -1;
+ }
+ } else {
+ size = 0;
+ list1 = *listp;
+ while ( *list1 ) {
+ size++;
+ list1++;
+ }
+ size += 2;
+ list1 = ch_realloc(list, size*sizeof(AttributeType *));
+ if ( !list1 ) {
+ return -1;
+ }
+ list = list1;
+ }
+ list[size-2] = sat;
+ list[size-1] = NULL;
+ *listp = list;
+ return 0;
+}
+
+int
+at_delete_from_list(
+ int pos,
+ AttributeType ***listp )
+{
+ AttributeType **list;
+ AttributeType **list1;
+ int i;
+ int j;
+
+ if ( pos < 0 ) {
+ return -2;
+ }
+ list = *listp;
+ for ( i=0; list[i]; i++ )
+ ;
+ if ( pos >= i ) {
+ return -2;
+ }
+ for ( i=pos, j=pos+1; list[j]; i++, j++ ) {
+ list[i] = list[j];
+ }
+ list[i] = NULL;
+ /* Tell the runtime this can be shrunk */
+ list1 = ch_realloc(list, (i+1)*sizeof(AttributeType **));
+ if ( !list1 ) {
+ return -1;
+ }
+ *listp = list1;
+ return 0;
+}
+
+int
+at_find_in_list(
+ AttributeType *sat,
+ AttributeType **list )
+{
+ int i;
+
+ if ( !list ) {
+ return -1;
+ }
+ for ( i=0; list[i]; i++ ) {
+ if ( sat == list[i] ) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static void
+at_delete_names( AttributeType *at )
+{
+ char **names = at->sat_names;
+
+ if (!names) return;
+
+ while (*names) {
+ struct aindexrec tmpair, *air;
+
+ ber_str2bv( *names, 0, 0, &tmpair.air_name );
+ tmpair.air_at = at;
+ air = (struct aindexrec *)ldap_avl_delete( &attr_index,
+ (caddr_t)&tmpair, attr_index_cmp );
+ assert( air != NULL );
+ ldap_memfree( air );
+ names++;
+ }
+}
+
+/* Mark the attribute as deleted, remove from list, and remove all its
+ * names from the AVL tree. Leave the OID in the tree.
+ */
+void
+at_delete( AttributeType *at )
+{
+ at->sat_flags |= SLAP_AT_DELETED;
+
+ LDAP_STAILQ_REMOVE(&attr_list, at, AttributeType, sat_next);
+
+ at_delete_names( at );
+}
+
+static void
+at_clean( AttributeType *a )
+{
+ if ( a->sat_equality ) {
+ MatchingRule *mr;
+
+ mr = mr_find( a->sat_equality->smr_oid );
+ assert( mr != NULL );
+ if ( mr != a->sat_equality ) {
+ ch_free( a->sat_equality );
+ a->sat_equality = NULL;
+ }
+ }
+
+ assert( a->sat_syntax != NULL );
+ if ( a->sat_syntax != NULL ) {
+ Syntax *syn;
+
+ syn = syn_find( a->sat_syntax->ssyn_oid );
+ assert( syn != NULL );
+ if ( syn != a->sat_syntax ) {
+ ch_free( a->sat_syntax );
+ a->sat_syntax = NULL;
+ }
+ }
+
+ if ( a->sat_oidmacro ) {
+ ldap_memfree( a->sat_oidmacro );
+ a->sat_oidmacro = NULL;
+ }
+ if ( a->sat_soidmacro ) {
+ ldap_memfree( a->sat_soidmacro );
+ a->sat_soidmacro = NULL;
+ }
+ if ( a->sat_subtypes ) {
+ ldap_memfree( a->sat_subtypes );
+ a->sat_subtypes = NULL;
+ }
+}
+
+static void
+at_destroy_one( void *v )
+{
+ struct aindexrec *air = v;
+ AttributeType *a = air->air_at;
+
+ at_clean( a );
+ ad_destroy(a->sat_ad);
+ ldap_pvt_thread_mutex_destroy(&a->sat_ad_mutex);
+ ldap_attributetype_free((LDAPAttributeType *)a);
+ ldap_memfree(air);
+}
+
+void
+at_destroy( void )
+{
+ AttributeType *a;
+
+ while( !LDAP_STAILQ_EMPTY(&attr_list) ) {
+ a = LDAP_STAILQ_FIRST(&attr_list);
+ LDAP_STAILQ_REMOVE_HEAD(&attr_list, sat_next);
+
+ at_delete_names( a );
+ }
+
+ ldap_avl_free(attr_index, at_destroy_one);
+
+ if ( slap_schema.si_at_undefined ) {
+ ad_destroy(slap_schema.si_at_undefined->sat_ad);
+ }
+
+ if ( slap_schema.si_at_proxied ) {
+ ad_destroy(slap_schema.si_at_proxied->sat_ad);
+ }
+}
+
+int
+at_start( AttributeType **at )
+{
+ assert( at != NULL );
+
+ *at = LDAP_STAILQ_FIRST(&attr_list);
+
+ return (*at != NULL);
+}
+
+int
+at_next( AttributeType **at )
+{
+ assert( at != NULL );
+
+#if 0 /* pedantic check: don't use this */
+ {
+ AttributeType *tmp = NULL;
+
+ LDAP_STAILQ_FOREACH(tmp,&attr_list,sat_next) {
+ if ( tmp == *at ) {
+ break;
+ }
+ }
+
+ assert( tmp != NULL );
+ }
+#endif
+
+ if ( *at == NULL ) {
+ return 0;
+ }
+
+ *at = LDAP_STAILQ_NEXT(*at,sat_next);
+
+ return (*at != NULL);
+}
+
+/*
+ * check whether the two attributeTypes actually __are__ identical,
+ * or rather inconsistent
+ */
+static int
+at_check_dup(
+ AttributeType *sat,
+ AttributeType *new_sat )
+{
+ if ( new_sat->sat_oid != NULL ) {
+ if ( sat->sat_oid == NULL ) {
+ return SLAP_SCHERR_ATTR_INCONSISTENT;
+ }
+
+ if ( strcmp( sat->sat_oid, new_sat->sat_oid ) != 0 ) {
+ return SLAP_SCHERR_ATTR_INCONSISTENT;
+ }
+
+ } else {
+ if ( sat->sat_oid != NULL ) {
+ return SLAP_SCHERR_ATTR_INCONSISTENT;
+ }
+ }
+
+ if ( new_sat->sat_names ) {
+ int i;
+
+ if ( sat->sat_names == NULL ) {
+ return SLAP_SCHERR_ATTR_INCONSISTENT;
+ }
+
+ for ( i = 0; new_sat->sat_names[ i ]; i++ ) {
+ if ( sat->sat_names[ i ] == NULL ) {
+ return SLAP_SCHERR_ATTR_INCONSISTENT;
+ }
+
+ if ( strcasecmp( sat->sat_names[ i ],
+ new_sat->sat_names[ i ] ) != 0 )
+ {
+ return SLAP_SCHERR_ATTR_INCONSISTENT;
+ }
+ }
+ } else {
+ if ( sat->sat_names != NULL ) {
+ return SLAP_SCHERR_ATTR_INCONSISTENT;
+ }
+ }
+
+ return SLAP_SCHERR_ATTR_DUP;
+}
+
+static struct aindexrec *air_old;
+
+static int
+at_dup_error( void *left, void *right )
+{
+ air_old = left;
+ return -1;
+}
+
+static int
+at_insert(
+ AttributeType **rat,
+ AttributeType *prev,
+ const char **err )
+{
+ struct aindexrec *air;
+ char **names = NULL;
+ AttributeType *sat = *rat;
+
+ if ( sat->sat_oid ) {
+ air = (struct aindexrec *)
+ ch_calloc( 1, sizeof(struct aindexrec) );
+ ber_str2bv( sat->sat_oid, 0, 0, &air->air_name );
+ air->air_at = sat;
+ air_old = NULL;
+
+ if ( ldap_avl_insert( &attr_index, (caddr_t) air,
+ attr_index_cmp, at_dup_error ) )
+ {
+ AttributeType *old_sat;
+ int rc;
+
+ *err = sat->sat_oid;
+
+ assert( air_old != NULL );
+ old_sat = air_old->air_at;
+
+ /* replacing a deleted definition? */
+ if ( old_sat->sat_flags & SLAP_AT_DELETED ) {
+ AttributeType tmp;
+ AttributeDescription *ad;
+
+ /* Keep old oid, free new oid;
+ * Keep old ads, free new ads;
+ * Keep old ad_mutex, free new ad_mutex;
+ * Keep new everything else, free old
+ */
+ tmp = *old_sat;
+ *old_sat = *sat;
+ old_sat->sat_oid = tmp.sat_oid;
+ tmp.sat_oid = sat->sat_oid;
+ old_sat->sat_ad = tmp.sat_ad;
+ tmp.sat_ad = sat->sat_ad;
+ old_sat->sat_ad_mutex = tmp.sat_ad_mutex;
+ tmp.sat_ad_mutex = sat->sat_ad_mutex;
+ *sat = tmp;
+
+ /* Check for basic ad pointing at old cname */
+ for ( ad = old_sat->sat_ad; ad; ad=ad->ad_next ) {
+ if ( ad->ad_cname.bv_val == sat->sat_cname.bv_val ) {
+ ad->ad_cname = old_sat->sat_cname;
+ break;
+ }
+ }
+
+ at_clean( sat );
+ at_destroy_one( air );
+
+ air = air_old;
+ sat = old_sat;
+ *rat = sat;
+ } else {
+ ldap_memfree( air );
+
+ rc = at_check_dup( old_sat, sat );
+
+ return rc;
+ }
+ }
+ /* FIX: temporal consistency check */
+ at_bvfind( &air->air_name );
+ }
+
+ names = sat->sat_names;
+ if ( names ) {
+ while ( *names ) {
+ air = (struct aindexrec *)
+ ch_calloc( 1, sizeof(struct aindexrec) );
+ ber_str2bv( *names, 0, 0, &air->air_name );
+ air->air_at = sat;
+ if ( ldap_avl_insert( &attr_index, (caddr_t) air,
+ attr_index_cmp, ldap_avl_dup_error ) )
+ {
+ AttributeType *old_sat;
+ int rc;
+
+ *err = *names;
+
+ old_sat = at_bvfind( &air->air_name );
+ assert( old_sat != NULL );
+ rc = at_check_dup( old_sat, sat );
+
+ ldap_memfree(air);
+
+ while ( names > sat->sat_names ) {
+ struct aindexrec tmpair;
+
+ names--;
+ ber_str2bv( *names, 0, 0, &tmpair.air_name );
+ tmpair.air_at = sat;
+ air = (struct aindexrec *)ldap_avl_delete( &attr_index,
+ (caddr_t)&tmpair, attr_index_cmp );
+ assert( air != NULL );
+ ldap_memfree( air );
+ }
+
+ if ( sat->sat_oid ) {
+ struct aindexrec tmpair;
+
+ ber_str2bv( sat->sat_oid, 0, 0, &tmpair.air_name );
+ tmpair.air_at = sat;
+ air = (struct aindexrec *)ldap_avl_delete( &attr_index,
+ (caddr_t)&tmpair, attr_index_cmp );
+ assert( air != NULL );
+ ldap_memfree( air );
+ }
+
+ return rc;
+ }
+ /* FIX: temporal consistency check */
+ at_bvfind(&air->air_name);
+ names++;
+ }
+ }
+
+ if ( sat->sat_oid ) {
+ slap_ad_undef_promote( sat->sat_oid, sat );
+ }
+
+ names = sat->sat_names;
+ if ( names ) {
+ while ( *names ) {
+ slap_ad_undef_promote( *names, sat );
+ names++;
+ }
+ }
+
+ if ( sat->sat_flags & SLAP_AT_HARDCODE ) {
+ prev = at_sys_tail;
+ at_sys_tail = sat;
+ }
+ if ( prev ) {
+ LDAP_STAILQ_INSERT_AFTER( &attr_list, prev, sat, sat_next );
+ } else {
+ LDAP_STAILQ_INSERT_TAIL( &attr_list, sat, sat_next );
+ }
+
+ return 0;
+}
+
+int
+at_add(
+ LDAPAttributeType *at,
+ int user,
+ AttributeType **rsat,
+ AttributeType *prev,
+ const char **err )
+{
+ AttributeType *sat = NULL;
+ MatchingRule *mr = NULL;
+ Syntax *syn = NULL;
+ int i;
+ int code = LDAP_SUCCESS;
+ char *cname = NULL;
+ char *oidm = NULL;
+ char *soidm = NULL;
+
+ if ( !at->at_oid ) {
+ *err = "";
+ return SLAP_SCHERR_ATTR_INCOMPLETE;
+ }
+
+ if ( !OID_LEADCHAR( at->at_oid[0] )) {
+ char *oid;
+
+ /* Expand OID macros */
+ oid = oidm_find( at->at_oid );
+ if ( !oid ) {
+ *err = at->at_oid;
+ return SLAP_SCHERR_OIDM;
+ }
+ if ( oid != at->at_oid ) {
+ oidm = at->at_oid;
+ at->at_oid = oid;
+ }
+ }
+
+ if ( at->at_syntax_oid && !OID_LEADCHAR( at->at_syntax_oid[0] )) {
+ char *oid;
+
+ /* Expand OID macros */
+ oid = oidm_find( at->at_syntax_oid );
+ if ( !oid ) {
+ *err = at->at_syntax_oid;
+ code = SLAP_SCHERR_OIDM;
+ goto error_return;
+ }
+ if ( oid != at->at_syntax_oid ) {
+ soidm = at->at_syntax_oid;
+ at->at_syntax_oid = oid;
+ }
+ }
+
+ if ( at->at_names && at->at_names[0] ) {
+ int i;
+
+ for( i=0; at->at_names[i]; i++ ) {
+ if( !slap_valid_descr( at->at_names[i] ) ) {
+ *err = at->at_names[i];
+ code = SLAP_SCHERR_BAD_DESCR;
+ goto error_return;
+ }
+ }
+
+ cname = at->at_names[0];
+
+ } else {
+ cname = at->at_oid;
+
+ }
+
+ *err = cname;
+
+ if ( !at->at_usage && at->at_no_user_mod ) {
+ /* user attribute must be modifiable */
+ code = SLAP_SCHERR_ATTR_BAD_USAGE;
+ goto error_return;
+ }
+
+ if ( at->at_collective ) {
+ if( at->at_usage ) {
+ /* collective attributes cannot be operational */
+ code = SLAP_SCHERR_ATTR_BAD_USAGE;
+ goto error_return;
+ }
+
+ if( at->at_single_value ) {
+ /* collective attributes cannot be single-valued */
+ code = SLAP_SCHERR_ATTR_BAD_USAGE;
+ goto error_return;
+ }
+ }
+
+ sat = (AttributeType *) ch_calloc( 1, sizeof(AttributeType) );
+ AC_MEMCPY( &sat->sat_atype, at, sizeof(LDAPAttributeType));
+
+ sat->sat_cname.bv_val = cname;
+ sat->sat_cname.bv_len = strlen( cname );
+ sat->sat_oidmacro = oidm;
+ sat->sat_soidmacro = soidm;
+ ldap_pvt_thread_mutex_init(&sat->sat_ad_mutex);
+
+ if ( at->at_sup_oid ) {
+ AttributeType *supsat = at_find(at->at_sup_oid);
+
+ if ( supsat == NULL ) {
+ *err = at->at_sup_oid;
+ code = SLAP_SCHERR_ATTR_NOT_FOUND;
+ goto error_return;
+ }
+
+ sat->sat_sup = supsat;
+
+ if ( at_append_to_list(sat, &supsat->sat_subtypes) ) {
+ code = SLAP_SCHERR_OUTOFMEM;
+ goto error_return;
+ }
+
+ if ( sat->sat_usage != supsat->sat_usage ) {
+ /* subtypes must have same usage as their SUP */
+ code = SLAP_SCHERR_ATTR_BAD_USAGE;
+ goto error_return;
+ }
+
+ if ( supsat->sat_obsolete && !sat->sat_obsolete ) {
+ /* subtypes must be obsolete if super is */
+ code = SLAP_SCHERR_ATTR_BAD_SUP;
+ goto error_return;
+ }
+
+ if ( sat->sat_flags & SLAP_AT_FINAL ) {
+ /* cannot subtype a "final" attribute type */
+ code = SLAP_SCHERR_ATTR_BAD_SUP;
+ goto error_return;
+ }
+ }
+
+ /*
+ * Inherit definitions from superiors. We only check the
+ * direct superior since that one has already inherited from
+ * its own superiors
+ */
+ if ( sat->sat_sup ) {
+ Syntax *syn = syn_find(sat->sat_sup->sat_syntax->ssyn_oid);
+ if ( syn != sat->sat_sup->sat_syntax ) {
+ sat->sat_syntax = ch_malloc( sizeof( Syntax ));
+ *sat->sat_syntax = *sat->sat_sup->sat_syntax;
+ } else {
+ sat->sat_syntax = sat->sat_sup->sat_syntax;
+ }
+ if ( sat->sat_sup->sat_equality ) {
+ MatchingRule *mr = mr_find( sat->sat_sup->sat_equality->smr_oid );
+ if ( mr != sat->sat_sup->sat_equality ) {
+ sat->sat_equality = ch_malloc( sizeof( MatchingRule ));
+ *sat->sat_equality = *sat->sat_sup->sat_equality;
+ } else {
+ sat->sat_equality = sat->sat_sup->sat_equality;
+ }
+ }
+ sat->sat_approx = sat->sat_sup->sat_approx;
+ sat->sat_ordering = sat->sat_sup->sat_ordering;
+ sat->sat_substr = sat->sat_sup->sat_substr;
+ }
+
+ /*
+ * check for X-ORDERED attributes
+ */
+ if ( sat->sat_extensions ) {
+ for (i=0; sat->sat_extensions[i]; i++) {
+ if (!strcasecmp( sat->sat_extensions[i]->lsei_name,
+ "X-ORDERED" ) && sat->sat_extensions[i]->lsei_values ) {
+ if ( !strcasecmp( sat->sat_extensions[i]->lsei_values[0],
+ "VALUES" )) {
+ sat->sat_flags |= SLAP_AT_ORDERED_VAL;
+ break;
+ } else if ( !strcasecmp( sat->sat_extensions[i]->lsei_values[0],
+ "SIBLINGS" )) {
+ sat->sat_flags |= SLAP_AT_ORDERED_SIB;
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !user )
+ sat->sat_flags |= SLAP_AT_HARDCODE;
+
+ if ( at->at_syntax_oid ) {
+ syn = syn_find(sat->sat_syntax_oid);
+ if ( syn == NULL ) {
+ *err = sat->sat_syntax_oid;
+ code = SLAP_SCHERR_SYN_NOT_FOUND;
+ goto error_return;
+ }
+
+ if ( sat->sat_syntax != NULL && sat->sat_syntax != syn ) {
+ /* BEWARE: no loop detection! */
+ if ( syn_is_sup( sat->sat_syntax, syn ) ) {
+ code = SLAP_SCHERR_ATTR_BAD_SUP;
+ goto error_return;
+ }
+ }
+
+ sat->sat_syntax = syn;
+
+ } else if ( sat->sat_syntax == NULL ) {
+ code = SLAP_SCHERR_ATTR_INCOMPLETE;
+ goto error_return;
+ }
+
+ if ( sat->sat_equality_oid ) {
+ mr = mr_find(sat->sat_equality_oid);
+
+ if( mr == NULL ) {
+ *err = sat->sat_equality_oid;
+ code = SLAP_SCHERR_MR_NOT_FOUND;
+ goto error_return;
+ }
+
+ if(( mr->smr_usage & SLAP_MR_EQUALITY ) != SLAP_MR_EQUALITY ) {
+ *err = sat->sat_equality_oid;
+ code = SLAP_SCHERR_ATTR_BAD_MR;
+ goto error_return;
+ }
+
+ if( sat->sat_syntax != mr->smr_syntax ) {
+ if( mr->smr_compat_syntaxes == NULL ) {
+ *err = sat->sat_equality_oid;
+ code = SLAP_SCHERR_ATTR_BAD_MR;
+ goto error_return;
+ }
+
+ for(i=0; mr->smr_compat_syntaxes[i]; i++) {
+ if( sat->sat_syntax == mr->smr_compat_syntaxes[i] ) {
+ i = -1;
+ break;
+ }
+ }
+
+ if( i >= 0 ) {
+ *err = sat->sat_equality_oid;
+ code = SLAP_SCHERR_ATTR_BAD_MR;
+ goto error_return;
+ }
+ }
+
+ sat->sat_equality = mr;
+ sat->sat_approx = mr->smr_associated;
+ }
+
+ if ( sat->sat_ordering_oid ) {
+ if( !sat->sat_equality ) {
+ *err = sat->sat_ordering_oid;
+ code = SLAP_SCHERR_ATTR_BAD_MR;
+ goto error_return;
+ }
+
+ mr = mr_find(sat->sat_ordering_oid);
+
+ if( mr == NULL ) {
+ *err = sat->sat_ordering_oid;
+ code = SLAP_SCHERR_MR_NOT_FOUND;
+ goto error_return;
+ }
+
+ if(( mr->smr_usage & SLAP_MR_ORDERING ) != SLAP_MR_ORDERING ) {
+ *err = sat->sat_ordering_oid;
+ code = SLAP_SCHERR_ATTR_BAD_MR;
+ goto error_return;
+ }
+
+ if( sat->sat_syntax != mr->smr_syntax ) {
+ if( mr->smr_compat_syntaxes == NULL ) {
+ *err = sat->sat_ordering_oid;
+ code = SLAP_SCHERR_ATTR_BAD_MR;
+ goto error_return;
+ }
+
+ for(i=0; mr->smr_compat_syntaxes[i]; i++) {
+ if( sat->sat_syntax == mr->smr_compat_syntaxes[i] ) {
+ i = -1;
+ break;
+ }
+ }
+
+ if( i >= 0 ) {
+ *err = sat->sat_ordering_oid;
+ code = SLAP_SCHERR_ATTR_BAD_MR;
+ goto error_return;
+ }
+ }
+
+ sat->sat_ordering = mr;
+ }
+
+ if ( sat->sat_substr_oid ) {
+ if( !sat->sat_equality ) {
+ *err = sat->sat_substr_oid;
+ code = SLAP_SCHERR_ATTR_BAD_MR;
+ goto error_return;
+ }
+
+ mr = mr_find(sat->sat_substr_oid);
+
+ if( mr == NULL ) {
+ *err = sat->sat_substr_oid;
+ code = SLAP_SCHERR_MR_NOT_FOUND;
+ goto error_return;
+ }
+
+ if(( mr->smr_usage & SLAP_MR_SUBSTR ) != SLAP_MR_SUBSTR ) {
+ *err = sat->sat_substr_oid;
+ code = SLAP_SCHERR_ATTR_BAD_MR;
+ goto error_return;
+ }
+
+ /* due to funky LDAP builtin substring rules,
+ * we check against the equality rule assertion
+ * syntax and compat syntaxes instead of those
+ * associated with the substrings rule.
+ */
+ if( sat->sat_syntax != sat->sat_equality->smr_syntax ) {
+ if( sat->sat_equality->smr_compat_syntaxes == NULL ) {
+ *err = sat->sat_substr_oid;
+ code = SLAP_SCHERR_ATTR_BAD_MR;
+ goto error_return;
+ }
+
+ for(i=0; sat->sat_equality->smr_compat_syntaxes[i]; i++) {
+ if( sat->sat_syntax ==
+ sat->sat_equality->smr_compat_syntaxes[i] )
+ {
+ i = -1;
+ break;
+ }
+ }
+
+ if( i >= 0 ) {
+ *err = sat->sat_substr_oid;
+ code = SLAP_SCHERR_ATTR_BAD_MR;
+ goto error_return;
+ }
+ }
+
+ sat->sat_substr = mr;
+ }
+
+ code = at_insert( &sat, prev, err );
+ if ( code != 0 ) {
+error_return:;
+ if ( sat ) {
+ ldap_pvt_thread_mutex_destroy( &sat->sat_ad_mutex );
+ ch_free( sat );
+ }
+
+ if ( oidm ) {
+ SLAP_FREE( at->at_oid );
+ at->at_oid = oidm;
+ }
+
+ if ( soidm ) {
+ SLAP_FREE( at->at_syntax_oid );
+ at->at_syntax_oid = soidm;
+ }
+
+ } else if ( rsat ) {
+ *rsat = sat;
+ }
+
+ return code;
+}
+
+#ifdef LDAP_DEBUG
+#ifdef SLAPD_UNUSED
+static int
+at_index_printnode( void *v_air, void *ignore )
+{
+ struct aindexrec *air = v_air;
+ printf("%s = %s\n",
+ air->air_name.bv_val,
+ ldap_attributetype2str(&air->air_at->sat_atype) );
+ return( 0 );
+}
+
+static void
+at_index_print( void )
+{
+ printf("Printing attribute type index:\n");
+ (void) ldap_avl_apply( attr_index, at_index_printnode, 0, -1, AVL_INORDER );
+}
+#endif
+#endif
+
+void
+at_unparse( BerVarray *res, AttributeType *start, AttributeType *end, int sys )
+{
+ AttributeType *at;
+ int i, num;
+ struct berval bv, *bva = NULL, idx;
+ char ibuf[32];
+
+ if ( !start )
+ start = LDAP_STAILQ_FIRST( &attr_list );
+
+ /* count the result size */
+ i = 0;
+ for ( at=start; at; at=LDAP_STAILQ_NEXT(at, sat_next)) {
+ if ( sys && !(at->sat_flags & SLAP_AT_HARDCODE)) break;
+ i++;
+ if ( at == end ) break;
+ }
+ if (!i) return;
+
+ num = i;
+ bva = ch_malloc( (num+1) * sizeof(struct berval) );
+ BER_BVZERO( bva );
+ idx.bv_val = ibuf;
+ if ( sys ) {
+ idx.bv_len = 0;
+ ibuf[0] = '\0';
+ }
+ i = 0;
+ for ( at=start; at; at=LDAP_STAILQ_NEXT(at, sat_next)) {
+ LDAPAttributeType lat, *latp;
+ if ( sys && !(at->sat_flags & SLAP_AT_HARDCODE)) break;
+ if ( at->sat_oidmacro || at->sat_soidmacro ) {
+ lat = at->sat_atype;
+ if ( at->sat_oidmacro )
+ lat.at_oid = at->sat_oidmacro;
+ if ( at->sat_soidmacro )
+ lat.at_syntax_oid = at->sat_soidmacro;
+ latp = &lat;
+ } else {
+ latp = &at->sat_atype;
+ }
+ if ( ldap_attributetype2bv( latp, &bv ) == NULL ) {
+ ber_bvarray_free( bva );
+ }
+ if ( !sys ) {
+ idx.bv_len = sprintf(idx.bv_val, "{%d}", i);
+ }
+ bva[i].bv_len = idx.bv_len + bv.bv_len;
+ bva[i].bv_val = ch_malloc( bva[i].bv_len + 1 );
+ strcpy( bva[i].bv_val, ibuf );
+ strcpy( bva[i].bv_val + idx.bv_len, bv.bv_val );
+ i++;
+ bva[i].bv_val = NULL;
+ ldap_memfree( bv.bv_val );
+ if ( at == end ) break;
+ }
+ *res = bva;
+}
+
+int
+at_schema_info( Entry *e )
+{
+ AttributeDescription *ad_attributeTypes = slap_schema.si_ad_attributeTypes;
+ AttributeType *at;
+ struct berval val;
+ struct berval nval;
+
+ LDAP_STAILQ_FOREACH(at,&attr_list,sat_next) {
+ if( at->sat_flags & SLAP_AT_HIDE ) continue;
+
+ if ( ldap_attributetype2bv( &at->sat_atype, &val ) == NULL ) {
+ return -1;
+ }
+
+ ber_str2bv( at->sat_oid, 0, 0, &nval );
+
+ if( attr_merge_one( e, ad_attributeTypes, &val, &nval ) )
+ {
+ return -1;
+ }
+ ldap_memfree( val.bv_val );
+ }
+ return 0;
+}
+
+int
+register_at( const char *def, AttributeDescription **rad, int dupok )
+{
+ LDAPAttributeType *at;
+ int code, freeit = 0;
+ const char *err;
+ AttributeDescription *ad = NULL;
+
+ at = ldap_str2attributetype( def, &code, &err, LDAP_SCHEMA_ALLOW_ALL );
+ if ( !at ) {
+ Debug( LDAP_DEBUG_ANY,
+ "register_at: AttributeType \"%s\": %s, %s\n",
+ def, ldap_scherr2str(code), err );
+ return code;
+ }
+
+ code = at_add( at, 0, NULL, NULL, &err );
+ if ( code ) {
+ if ( code == SLAP_SCHERR_ATTR_DUP && dupok ) {
+ freeit = 1;
+
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "register_at: AttributeType \"%s\": %s, %s\n",
+ def, scherr2str(code), err );
+ ldap_attributetype_free( at );
+ return code;
+ }
+ }
+ code = slap_str2ad( at->at_names[0], &ad, &err );
+ if ( freeit || code ) {
+ ldap_attributetype_free( at );
+ } else {
+ ldap_memfree( at );
+ }
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY, "register_at: AttributeType \"%s\": %s\n",
+ def, err );
+ }
+ if ( rad ) *rad = ad;
+ return code;
+}
diff --git a/servers/slapd/attr.c b/servers/slapd/attr.c
new file mode 100644
index 0000000..0828a76
--- /dev/null
+++ b/servers/slapd/attr.c
@@ -0,0 +1,722 @@
+/* attr.c - routines for dealing with attributes */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <ac/ctype.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "slap.h"
+
+/*
+ * Allocate in chunks, minimum of 1000 at a time.
+ */
+#define CHUNK_SIZE 1000
+typedef struct slap_list {
+ struct slap_list *next;
+} slap_list;
+static slap_list *attr_chunks;
+static Attribute *attrs_list;
+static ldap_pvt_thread_mutex_t attr_mutex;
+
+int
+attr_prealloc( int num )
+{
+ Attribute *a;
+ slap_list *s;
+
+ if (!num) return 0;
+
+ s = ch_calloc( 1, sizeof(slap_list) + num * sizeof(Attribute));
+ s->next = attr_chunks;
+ attr_chunks = s;
+
+ a = (Attribute *)(s+1);
+ for ( ;num>1; num--) {
+ a->a_next = a+1;
+ a++;
+ }
+ a->a_next = attrs_list;
+ attrs_list = (Attribute *)(s+1);
+
+ return 0;
+}
+
+Attribute *
+attr_alloc( AttributeDescription *ad )
+{
+ Attribute *a;
+
+ ldap_pvt_thread_mutex_lock( &attr_mutex );
+ if ( !attrs_list )
+ attr_prealloc( CHUNK_SIZE );
+ a = attrs_list;
+ attrs_list = a->a_next;
+ a->a_next = NULL;
+ ldap_pvt_thread_mutex_unlock( &attr_mutex );
+
+ a->a_desc = ad;
+ if ( ad && ( ad->ad_type->sat_flags & SLAP_AT_SORTED_VAL ))
+ a->a_flags |= SLAP_ATTR_SORTED_VALS;
+
+ return a;
+}
+
+/* Return a list of num attrs */
+Attribute *
+attrs_alloc( int num )
+{
+ Attribute *head = NULL;
+ Attribute **a;
+
+ ldap_pvt_thread_mutex_lock( &attr_mutex );
+ for ( a = &attrs_list; *a && num > 0; a = &(*a)->a_next ) {
+ if ( !head )
+ head = *a;
+ num--;
+ }
+ attrs_list = *a;
+ if ( num > 0 ) {
+ attr_prealloc( num > CHUNK_SIZE ? num : CHUNK_SIZE );
+ *a = attrs_list;
+ for ( ; *a && num > 0; a = &(*a)->a_next ) {
+ if ( !head )
+ head = *a;
+ num--;
+ }
+ attrs_list = *a;
+ }
+ *a = NULL;
+ ldap_pvt_thread_mutex_unlock( &attr_mutex );
+
+ return head;
+}
+
+
+void
+attr_clean( Attribute *a )
+{
+ if ( a->a_nvals && a->a_nvals != a->a_vals &&
+ !( a->a_flags & SLAP_ATTR_DONT_FREE_VALS )) {
+ if ( a->a_flags & SLAP_ATTR_DONT_FREE_DATA ) {
+ free( a->a_nvals );
+ } else {
+ ber_bvarray_free( a->a_nvals );
+ }
+ }
+ /* a_vals may be equal to slap_dummy_bv, a static empty berval;
+ * this is used as a placeholder for attributes that do not carry
+ * values, e.g. when proxying search entries with the "attrsonly"
+ * bit set. */
+ if ( a->a_vals != &slap_dummy_bv &&
+ !( a->a_flags & SLAP_ATTR_DONT_FREE_VALS )) {
+ if ( a->a_flags & SLAP_ATTR_DONT_FREE_DATA ) {
+ free( a->a_vals );
+ } else {
+ ber_bvarray_free( a->a_vals );
+ }
+ }
+ a->a_desc = NULL;
+ a->a_vals = NULL;
+ a->a_nvals = NULL;
+#ifdef LDAP_COMP_MATCH
+ a->a_comp_data = NULL;
+#endif
+ a->a_flags = 0;
+ a->a_numvals = 0;
+}
+
+void
+attr_free( Attribute *a )
+{
+ attr_clean( a );
+ ldap_pvt_thread_mutex_lock( &attr_mutex );
+ a->a_next = attrs_list;
+ attrs_list = a;
+ ldap_pvt_thread_mutex_unlock( &attr_mutex );
+}
+
+#ifdef LDAP_COMP_MATCH
+void
+comp_tree_free( Attribute *a )
+{
+ Attribute *next;
+
+ for( ; a != NULL ; a = next ) {
+ next = a->a_next;
+ if ( component_destructor && a->a_comp_data ) {
+ if ( a->a_comp_data->cd_mem_op )
+ component_destructor( a->a_comp_data->cd_mem_op );
+ free ( a->a_comp_data );
+ }
+ }
+}
+#endif
+
+void
+attrs_free( Attribute *a )
+{
+ if ( a ) {
+ Attribute *b = (Attribute *)0xBAD, *tail, *next;
+
+ /* save tail */
+ tail = a;
+ do {
+ next = a->a_next;
+ attr_clean( a );
+ a->a_next = b;
+ b = a;
+ a = next;
+ } while ( next );
+
+ ldap_pvt_thread_mutex_lock( &attr_mutex );
+ /* replace NULL with current attr list and let attr list
+ * start from last attribute returned to list */
+ tail->a_next = attrs_list;
+ attrs_list = b;
+ ldap_pvt_thread_mutex_unlock( &attr_mutex );
+ }
+}
+
+static void
+attr_dup2( Attribute *tmp, Attribute *a )
+{
+ tmp->a_flags = a->a_flags & SLAP_ATTR_PERSISTENT_FLAGS;
+ if ( a->a_vals != NULL ) {
+ unsigned i, j;
+
+ tmp->a_numvals = a->a_numvals;
+ tmp->a_vals = ch_malloc( (tmp->a_numvals + 1) * sizeof(struct berval) );
+ for ( i = 0; i < tmp->a_numvals; i++ ) {
+ ber_dupbv( &tmp->a_vals[i], &a->a_vals[i] );
+ if ( BER_BVISNULL( &tmp->a_vals[i] ) ) break;
+ /* FIXME: error? */
+ }
+ BER_BVZERO( &tmp->a_vals[i] );
+
+ /* a_nvals must be non null; it may be equal to a_vals */
+ assert( a->a_nvals != NULL );
+
+ if ( a->a_nvals != a->a_vals ) {
+
+ tmp->a_nvals = ch_malloc( (tmp->a_numvals + 1) * sizeof(struct berval) );
+ j = 0;
+ if ( i ) {
+ for ( ; !BER_BVISNULL( &a->a_nvals[j] ); j++ ) {
+ assert( j < i );
+ ber_dupbv( &tmp->a_nvals[j], &a->a_nvals[j] );
+ if ( BER_BVISNULL( &tmp->a_nvals[j] ) ) break;
+ /* FIXME: error? */
+ }
+ assert( j == i );
+ }
+ BER_BVZERO( &tmp->a_nvals[j] );
+
+ } else {
+ tmp->a_nvals = tmp->a_vals;
+ }
+ }
+}
+
+Attribute *
+attr_dup( Attribute *a )
+{
+ Attribute *tmp;
+
+ if ( a == NULL) return NULL;
+
+ tmp = attr_alloc( a->a_desc );
+ attr_dup2( tmp, a );
+ return tmp;
+}
+
+Attribute *
+attrs_dup( Attribute *a )
+{
+ int i;
+ Attribute *tmp, *anew;
+
+ if( a == NULL ) return NULL;
+
+ /* count them */
+ for( tmp=a,i=0; tmp; tmp=tmp->a_next ) {
+ i++;
+ }
+
+ anew = attrs_alloc( i );
+
+ for( tmp=anew; a; a=a->a_next ) {
+ tmp->a_desc = a->a_desc;
+ attr_dup2( tmp, a );
+ tmp=tmp->a_next;
+ }
+
+ return anew;
+}
+
+int
+attr_valfind(
+ Attribute *a,
+ unsigned flags,
+ struct berval *val,
+ unsigned *slot,
+ void *ctx )
+{
+ struct berval nval = BER_BVNULL, *cval;
+ MatchingRule *mr;
+ const char *text;
+ int match = -1, rc;
+ unsigned i, n;
+
+ if ( flags & SLAP_MR_ORDERING )
+ mr = a->a_desc->ad_type->sat_ordering;
+ else
+ mr = a->a_desc->ad_type->sat_equality;
+
+ if( !SLAP_IS_MR_ASSERTED_VALUE_NORMALIZED_MATCH( flags ) &&
+ mr->smr_normalize )
+ {
+ rc = (mr->smr_normalize)(
+ flags & (SLAP_MR_TYPE_MASK|SLAP_MR_SUBTYPE_MASK|SLAP_MR_VALUE_OF_SYNTAX),
+ a->a_desc->ad_type->sat_syntax,
+ mr, val, &nval, ctx );
+
+ if( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ cval = &nval;
+ } else {
+ cval = val;
+ }
+
+ n = a->a_numvals;
+ if ( (a->a_flags & SLAP_ATTR_SORTED_VALS) && n ) {
+ /* Binary search */
+ unsigned base = 0;
+
+ do {
+ unsigned pivot = n >> 1;
+ i = base + pivot;
+ rc = value_match( &match, a->a_desc, mr, flags,
+ &a->a_nvals[i], cval, &text );
+ if ( rc == LDAP_SUCCESS && match == 0 )
+ break;
+ if ( match < 0 ) {
+ base = i+1;
+ n -= pivot+1;
+ } else {
+ n = pivot;
+ }
+ } while ( n );
+ if ( match < 0 )
+ i++;
+ } else {
+ /* Linear search */
+ for ( i = 0; i < n; i++ ) {
+ const char *text;
+
+ rc = ordered_value_match( &match, a->a_desc, mr, flags,
+ &a->a_nvals[i], cval, &text );
+ if ( rc == LDAP_SUCCESS && match == 0 )
+ break;
+ }
+ }
+ if ( match )
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ if ( slot )
+ *slot = i;
+ if ( nval.bv_val )
+ slap_sl_free( nval.bv_val, ctx );
+
+ return rc;
+}
+
+int
+attr_valadd(
+ Attribute *a,
+ BerVarray vals,
+ BerVarray nvals,
+ int nn )
+{
+ int i;
+ BerVarray v2;
+
+ v2 = (BerVarray) SLAP_REALLOC( (char *) a->a_vals,
+ (a->a_numvals + nn + 1) * sizeof(struct berval) );
+ if( v2 == NULL ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "attr_valadd: SLAP_REALLOC failed.\n" );
+ return LBER_ERROR_MEMORY;
+ }
+ a->a_vals = v2;
+ if ( nvals ) {
+ v2 = (BerVarray) SLAP_REALLOC( (char *) a->a_nvals,
+ (a->a_numvals + nn + 1) * sizeof(struct berval) );
+ if( v2 == NULL ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "attr_valadd: SLAP_REALLOC failed.\n" );
+ return LBER_ERROR_MEMORY;
+ }
+ a->a_nvals = v2;
+ } else {
+ a->a_nvals = a->a_vals;
+ }
+
+ /* If sorted and old vals exist, must insert */
+ if (( a->a_flags & SLAP_ATTR_SORTED_VALS ) && a->a_numvals ) {
+ unsigned slot;
+ int j, rc;
+ v2 = nvals ? nvals : vals;
+ for ( i = 0; i < nn; i++ ) {
+ rc = attr_valfind( a, SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
+ &v2[i], &slot, NULL );
+ if ( rc != LDAP_NO_SUCH_ATTRIBUTE ) {
+ /* should never happen */
+ if ( rc == LDAP_SUCCESS )
+ rc = LDAP_TYPE_OR_VALUE_EXISTS;
+ return rc;
+ }
+ for ( j = a->a_numvals; j >= (int)slot; j-- ) {
+ a->a_vals[j+1] = a->a_vals[j];
+ if ( nvals )
+ a->a_nvals[j+1] = a->a_nvals[j];
+ }
+ ber_dupbv( &a->a_nvals[slot], &v2[i] );
+ if ( nvals )
+ ber_dupbv( &a->a_vals[slot], &vals[i] );
+ a->a_numvals++;
+ }
+ BER_BVZERO( &a->a_vals[a->a_numvals] );
+ if ( a->a_vals != a->a_nvals )
+ BER_BVZERO( &a->a_nvals[a->a_numvals] );
+ } else {
+ v2 = &a->a_vals[a->a_numvals];
+ for ( i = 0 ; i < nn; i++ ) {
+ ber_dupbv( &v2[i], &vals[i] );
+ if ( BER_BVISNULL( &v2[i] ) ) break;
+ }
+ BER_BVZERO( &v2[i] );
+
+ if ( nvals ) {
+ v2 = &a->a_nvals[a->a_numvals];
+ for ( i = 0 ; i < nn; i++ ) {
+ ber_dupbv( &v2[i], &nvals[i] );
+ if ( BER_BVISNULL( &v2[i] ) ) break;
+ }
+ BER_BVZERO( &v2[i] );
+ }
+ a->a_numvals += i;
+ }
+ return 0;
+}
+
+/*
+ * attr_merge - merge the given type and value with the list of
+ * attributes in attrs.
+ *
+ * nvals must be NULL if the attribute has no normalizer.
+ * In this case, a->a_nvals will be set equal to a->a_vals.
+ *
+ * returns 0 everything went ok
+ * -1 trouble
+ */
+
+int
+attr_merge(
+ Entry *e,
+ AttributeDescription *desc,
+ BerVarray vals,
+ BerVarray nvals )
+{
+ int i = 0;
+
+ Attribute **a;
+
+ for ( a = &e->e_attrs; *a != NULL; a = &(*a)->a_next ) {
+ if ( (*a)->a_desc == desc ) {
+ break;
+ }
+ }
+
+ if ( *a == NULL ) {
+ *a = attr_alloc( desc );
+ } else {
+ /*
+ * FIXME: if the attribute already exists, the presence
+ * of nvals and the value of (*a)->a_nvals must be consistent
+ */
+ assert( ( nvals == NULL && (*a)->a_nvals == (*a)->a_vals )
+ || ( nvals != NULL && (
+ ( (*a)->a_vals == NULL && (*a)->a_nvals == NULL )
+ || ( (*a)->a_nvals != (*a)->a_vals ) ) ) );
+ }
+
+ if ( vals != NULL ) {
+ for ( ; !BER_BVISNULL( &vals[i] ); i++ ) ;
+ }
+ return attr_valadd( *a, vals, nvals, i );
+}
+
+/*
+ * if a normalization function is defined for the equality matchingRule
+ * of desc, the value is normalized and stored in nval; otherwise nval
+ * is NULL
+ */
+int
+attr_normalize(
+ AttributeDescription *desc,
+ BerVarray vals,
+ BerVarray *nvalsp,
+ void *memctx )
+{
+ int rc = LDAP_SUCCESS;
+ BerVarray nvals = NULL;
+
+ *nvalsp = NULL;
+
+ if ( desc->ad_type->sat_equality &&
+ desc->ad_type->sat_equality->smr_normalize )
+ {
+ int i;
+
+ for ( i = 0; !BER_BVISNULL( &vals[i] ); i++ );
+
+ nvals = slap_sl_calloc( sizeof(struct berval), i + 1, memctx );
+ for ( i = 0; !BER_BVISNULL( &vals[i] ); i++ ) {
+ rc = desc->ad_type->sat_equality->smr_normalize(
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ desc->ad_type->sat_syntax,
+ desc->ad_type->sat_equality,
+ &vals[i], &nvals[i], memctx );
+
+ if ( rc != LDAP_SUCCESS ) {
+ BER_BVZERO( &nvals[i + 1] );
+ break;
+ }
+ }
+ BER_BVZERO( &nvals[i] );
+ *nvalsp = nvals;
+ }
+
+ if ( rc != LDAP_SUCCESS && nvals != NULL ) {
+ ber_bvarray_free_x( nvals, memctx );
+ }
+
+ return rc;
+}
+
+int
+attr_merge_normalize(
+ Entry *e,
+ AttributeDescription *desc,
+ BerVarray vals,
+ void *memctx )
+{
+ BerVarray nvals = NULL;
+ int rc;
+
+ rc = attr_normalize( desc, vals, &nvals, memctx );
+ if ( rc == LDAP_SUCCESS ) {
+ rc = attr_merge( e, desc, vals, nvals );
+ if ( nvals != NULL ) {
+ ber_bvarray_free_x( nvals, memctx );
+ }
+ }
+
+ return rc;
+}
+
+int
+attr_merge_one(
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ struct berval *nval )
+{
+ Attribute **a;
+
+ for ( a = &e->e_attrs; *a != NULL; a = &(*a)->a_next ) {
+ if ( (*a)->a_desc == desc ) {
+ break;
+ }
+ }
+
+ if ( *a == NULL ) {
+ *a = attr_alloc( desc );
+ }
+
+ return attr_valadd( *a, val, nval, 1 );
+}
+
+/*
+ * if a normalization function is defined for the equality matchingRule
+ * of desc, the value is normalized and stored in nval; otherwise nval
+ * is NULL
+ */
+int
+attr_normalize_one(
+ AttributeDescription *desc,
+ struct berval *val,
+ struct berval *nval,
+ void *memctx )
+{
+ int rc = LDAP_SUCCESS;
+
+ BER_BVZERO( nval );
+
+ if ( desc->ad_type->sat_equality &&
+ desc->ad_type->sat_equality->smr_normalize )
+ {
+ rc = desc->ad_type->sat_equality->smr_normalize(
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ desc->ad_type->sat_syntax,
+ desc->ad_type->sat_equality,
+ val, nval, memctx );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+int
+attr_merge_normalize_one(
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ void *memctx )
+{
+ struct berval nval = BER_BVNULL;
+ struct berval *nvalp = NULL;
+ int rc;
+
+ rc = attr_normalize_one( desc, val, &nval, memctx );
+ if ( rc == LDAP_SUCCESS && !BER_BVISNULL( &nval ) ) {
+ nvalp = &nval;
+ }
+
+ rc = attr_merge_one( e, desc, val, nvalp );
+ if ( nvalp != NULL ) {
+ slap_sl_free( nval.bv_val, memctx );
+ }
+ return rc;
+}
+
+/*
+ * attrs_find - find attribute(s) by AttributeDescription
+ * returns next attribute which is subtype of provided description.
+ */
+
+Attribute *
+attrs_find(
+ Attribute *a,
+ AttributeDescription *desc )
+{
+ for ( ; a != NULL; a = a->a_next ) {
+ if ( is_ad_subtype( a->a_desc, desc ) ) {
+ return( a );
+ }
+ }
+
+ return( NULL );
+}
+
+/*
+ * attr_find - find attribute by type
+ */
+
+Attribute *
+attr_find(
+ Attribute *a,
+ AttributeDescription *desc )
+{
+ for ( ; a != NULL; a = a->a_next ) {
+ if ( a->a_desc == desc ) {
+ return( a );
+ }
+ }
+
+ return( NULL );
+}
+
+/*
+ * attr_delete - delete the attribute type in list pointed to by attrs
+ * return 0 deleted ok
+ * 1 not found in list a
+ * -1 something bad happened
+ */
+
+int
+attr_delete(
+ Attribute **attrs,
+ AttributeDescription *desc )
+{
+ Attribute **a;
+
+ for ( a = attrs; *a != NULL; a = &(*a)->a_next ) {
+ if ( (*a)->a_desc == desc ) {
+ Attribute *save = *a;
+ *a = (*a)->a_next;
+ attr_free( save );
+
+ return LDAP_SUCCESS;
+ }
+ }
+
+ return LDAP_NO_SUCH_ATTRIBUTE;
+}
+
+int
+attr_init( void )
+{
+ ldap_pvt_thread_mutex_init( &attr_mutex );
+ return 0;
+}
+
+int
+attr_destroy( void )
+{
+ slap_list *a;
+
+ for ( a=attr_chunks; a; a=attr_chunks ) {
+ attr_chunks = a->next;
+ free( a );
+ }
+ ldap_pvt_thread_mutex_destroy( &attr_mutex );
+ return 0;
+}
diff --git a/servers/slapd/ava.c b/servers/slapd/ava.c
new file mode 100644
index 0000000..51d9cc1
--- /dev/null
+++ b/servers/slapd/ava.c
@@ -0,0 +1,133 @@
+/* ava.c - routines for dealing with attribute value assertions */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+#ifdef LDAP_COMP_MATCH
+#include "component.h"
+#endif
+
+void
+ava_free(
+ Operation *op,
+ AttributeAssertion *ava,
+ int freeit )
+{
+#ifdef LDAP_COMP_MATCH
+ if ( ava->aa_cf && ava->aa_cf->cf_ca->ca_comp_data.cd_mem_op )
+ nibble_mem_free ( ava->aa_cf->cf_ca->ca_comp_data.cd_mem_op );
+#endif
+ op->o_tmpfree( ava->aa_value.bv_val, op->o_tmpmemctx );
+ if ( ava->aa_desc->ad_flags & SLAP_DESC_TEMPORARY )
+ op->o_tmpfree( ava->aa_desc, op->o_tmpmemctx );
+ if ( freeit ) op->o_tmpfree( (char *) ava, op->o_tmpmemctx );
+}
+
+int
+get_ava(
+ Operation *op,
+ BerElement *ber,
+ Filter *f,
+ unsigned usage,
+ const char **text )
+{
+ int rc;
+ ber_tag_t rtag;
+ struct berval type, value;
+ AttributeAssertion *aa;
+#ifdef LDAP_COMP_MATCH
+ AttributeAliasing* a_alias = NULL;
+#endif
+
+ rtag = ber_scanf( ber, "{mm}", &type, &value );
+
+ if( rtag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, " get_ava ber_scanf\n" );
+ *text = "Error decoding attribute value assertion";
+ return SLAPD_DISCONNECT;
+ }
+
+ aa = op->o_tmpalloc( sizeof( AttributeAssertion ), op->o_tmpmemctx );
+ aa->aa_desc = NULL;
+ aa->aa_value.bv_val = NULL;
+#ifdef LDAP_COMP_MATCH
+ aa->aa_cf = NULL;
+#endif
+
+ rc = slap_bv2ad( &type, &aa->aa_desc, text );
+
+ if( rc != LDAP_SUCCESS ) {
+ f->f_choice |= SLAPD_FILTER_UNDEFINED;
+ *text = NULL;
+ rc = slap_bv2undef_ad( &type, &aa->aa_desc, text,
+ SLAP_AD_PROXIED|SLAP_AD_NOINSERT );
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_FILTER,
+ "get_ava: unknown attributeType %s\n", type.bv_val );
+ aa->aa_desc = slap_bv2tmp_ad( &type, op->o_tmpmemctx );
+ ber_dupbv_x( &aa->aa_value, &value, op->o_tmpmemctx );
+ f->f_ava = aa;
+ return LDAP_SUCCESS;
+ }
+ }
+
+ rc = asserted_value_validate_normalize(
+ aa->aa_desc, ad_mr(aa->aa_desc, usage),
+ usage, &value, &aa->aa_value, text, op->o_tmpmemctx );
+
+ if( rc != LDAP_SUCCESS ) {
+ f->f_choice |= SLAPD_FILTER_UNDEFINED;
+ Debug( LDAP_DEBUG_FILTER,
+ "get_ava: illegal value for attributeType %s\n", type.bv_val );
+ ber_dupbv_x( &aa->aa_value, &value, op->o_tmpmemctx );
+ *text = NULL;
+ rc = LDAP_SUCCESS;
+ }
+
+#ifdef LDAP_COMP_MATCH
+ if( is_aliased_attribute ) {
+ a_alias = is_aliased_attribute ( aa->aa_desc );
+ if ( a_alias ) {
+ rc = get_aliased_filter_aa ( op, aa, a_alias, text );
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_FILTER,
+ "get_ava: Invalid Attribute Aliasing\n" );
+ return rc;
+ }
+ }
+ }
+#endif
+ f->f_ava = aa;
+ return LDAP_SUCCESS;
+}
diff --git a/servers/slapd/back-asyncmeta/Makefile.in b/servers/slapd/back-asyncmeta/Makefile.in
new file mode 100644
index 0000000..c609458
--- /dev/null
+++ b/servers/slapd/back-asyncmeta/Makefile.in
@@ -0,0 +1,50 @@
+## Makefile.in for back-asyncmeta
+## $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2022 The OpenLDAP Foundation.
+## Portions Copyright 2016 Symas Corporation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This work was developed by Symas Corporation
+## based on back-meta module for inclusion in OpenLDAP Software.
+## This work was sponsored by Ericsson
+
+SRCS = init.c config.c search.c message_queue.c bind.c add.c compare.c \
+ delete.c modify.c modrdn.c map.c \
+ conn.c candidates.c dncache.c meta_result.c
+OBJS = init.lo config.lo search.lo message_queue.lo bind.lo add.lo compare.lo \
+ delete.lo modify.lo modrdn.lo map.lo \
+ conn.lo candidates.lo dncache.lo meta_result.lo
+
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-asyncmeta"
+BUILD_MOD = @BUILD_ASYNCMETA@
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = $(@BUILD_ASYNCMETA@_DEFS)
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(LIBS) $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+
+LIBBASE = back_asyncmeta
+
+XINCPATH = -I.. -I$(srcdir)/..
+XDEFS = $(MODULES_CPPFLAGS)
+
+all-local-lib: ../.backend
+
+../.backend: lib$(LIBBASE).a
+ @touch $@
diff --git a/servers/slapd/back-asyncmeta/add.c b/servers/slapd/back-asyncmeta/add.c
new file mode 100644
index 0000000..1f194ed
--- /dev/null
+++ b/servers/slapd/back-asyncmeta/add.c
@@ -0,0 +1,363 @@
+/* add.c - add request handler for back-asyncmeta */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2016 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Symas Corporation
+ * based on back-meta module for inclusion in OpenLDAP Software.
+ * This work was sponsored by Ericsson. */
+
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+#include "slap.h"
+#include "../../../libraries/liblber/lber-int.h"
+#include "../../../libraries/libldap/ldap-int.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-asyncmeta.h"
+#include "ldap_rq.h"
+
+
+int
+asyncmeta_error_cleanup(Operation *op,
+ SlapReply *rs,
+ bm_context_t *bc,
+ a_metaconn_t *mc,
+ int candidate)
+{
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ mc->mc_conns[candidate].msc_active--;
+ if (asyncmeta_bc_in_queue(mc,bc) == NULL || bc->bc_active > 1) {
+ bc->bc_active--;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ return LDAP_SUCCESS;
+ }
+ asyncmeta_drop_bc(mc, bc);
+ slap_sl_mem_setctx(op->o_threadctx, op->o_tmpmemctx);
+ operation_counter_init( op, op->o_threadctx );
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ send_ldap_result(op, rs);
+ return LDAP_SUCCESS;
+}
+
+meta_search_candidate_t
+asyncmeta_back_add_start(Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ bm_context_t *bc,
+ int candidate,
+ int do_lock)
+{
+ int isupdate;
+ Attribute *a;
+ int i;
+ LDAPMod **attrs;
+ a_dncookie dc;
+ a_metainfo_t *mi = mc->mc_info;
+ a_metatarget_t *mt = mi->mi_targets[ candidate ];
+ struct berval mdn = {0, NULL};
+ meta_search_candidate_t retcode = META_SEARCH_CANDIDATE;
+ BerElement *ber = NULL;
+ a_metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+ SlapReply *candidates = bc->candidates;
+ ber_int_t msgid;
+ LDAPControl **ctrls = NULL;
+ int rc;
+
+ dc.op = op;
+ dc.target = mt;
+ dc.memctx = op->o_tmpmemctx;
+ dc.to_from = MASSAGE_REQ;
+ asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn );
+
+ /* Count number of attributes in entry ( +1 ) */
+ for ( i = 1, a = op->ora_e->e_attrs; a; i++, a = a->a_next );
+
+ /* Create array of LDAPMods for ldap_add() */
+ attrs = op->o_tmpalloc(sizeof( LDAPMod * )*i, op->o_tmpmemctx);
+
+ isupdate = be_shadow_update( op );
+ for ( i = 0, a = op->ora_e->e_attrs; a; a = a->a_next ) {
+ int j;
+
+ if ( !isupdate && !get_relax( op ) && a->a_desc->ad_type->sat_no_user_mod )
+ {
+ continue;
+ }
+
+ attrs[ i ] = op->o_tmpalloc( sizeof( LDAPMod ), op->o_tmpmemctx );
+ if ( attrs[ i ] == NULL ) {
+ continue;
+ }
+ attrs[ i ]->mod_op = LDAP_MOD_BVALUES;
+ attrs[ i ]->mod_type = a->a_desc->ad_cname.bv_val;
+ j = a->a_numvals;
+ attrs[ i ]->mod_bvalues = op->o_tmpalloc( ( j + 1 ) * sizeof( struct berval * ), op->o_tmpmemctx );
+ for (j=0; j<a->a_numvals; j++) {
+ attrs[ i ]->mod_bvalues[ j ] = op->o_tmpalloc( sizeof( struct berval ), op->o_tmpmemctx );
+ if ( a->a_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName )
+ asyncmeta_dn_massage( &dc, &a->a_vals[ j ], attrs[ i ]->mod_bvalues[ j ] );
+ else
+ *attrs[ i ]->mod_bvalues[ j ] = a->a_vals[ j ];
+ }
+
+ attrs[ i ]->mod_bvalues[ j ] = NULL;
+ i++;
+ }
+ attrs[ i ] = NULL;
+
+ asyncmeta_set_msc_time(msc);
+
+ ctrls = op->o_ctrls;
+ if ( asyncmeta_controls_add( op, rs, mc, candidate, bc->is_root, &ctrls ) != LDAP_SUCCESS )
+ {
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ retcode = META_SEARCH_ERR;
+ goto done;
+ }
+ /* someone might have reset the connection */
+ if (!( LDAP_BACK_CONN_ISBOUND( msc )
+ || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+ Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+
+ ber = ldap_build_add_req( msc->msc_ld, mdn.bv_val, attrs, ctrls, NULL, &msgid);
+ if (!ber) {
+ Debug( asyncmeta_debug, "%s asyncmeta_back_add_start: Operation encoding failed with errno %d\n",
+ op->o_log_prefix, msc->msc_ld->ld_errno );
+ rs->sr_err = LDAP_OPERATIONS_ERROR;
+ rs->sr_text = "Failed to encode proxied request";
+ retcode = META_SEARCH_ERR;
+ goto done;
+ }
+
+ if (ber) {
+ struct timeval tv = {0, mt->mt_network_timeout*1000};
+ ber_socket_t s;
+ if (!( LDAP_BACK_CONN_ISBOUND( msc )
+ || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+ Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+ ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
+ if (s < 0) {
+ Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+
+ rc = ldap_int_poll( msc->msc_ld, s, &tv, 1);
+ if (rc < 0) {
+ Debug( asyncmeta_debug, "msc %p not writable within network timeout %s:%d\n", msc, __FILE__, __LINE__ );
+ if ((msc->msc_result_time + META_BACK_RESULT_INTERVAL) < slap_get_time()) {
+ rc = LDAP_SERVER_DOWN;
+ } else {
+ goto error_unavailable;
+ }
+ } else {
+ candidates[ candidate ].sr_msgid = msgid;
+ rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_ADD,
+ mdn.bv_val, ber, msgid );
+ if (rc == msgid)
+ rc = LDAP_SUCCESS;
+ else
+ rc = LDAP_SERVER_DOWN;
+ ber = NULL;
+ }
+
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ retcode = META_SEARCH_CANDIDATE;
+ asyncmeta_set_msc_time(msc);
+ goto done;
+
+ case LDAP_SERVER_DOWN:
+ /* do not lock if called from asyncmeta_handle_bind_result. Also do not reset the connection */
+ if (do_lock > 0) {
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ asyncmeta_reset_msc(NULL, mc, candidate, 0, __FUNCTION__);
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ }
+ /* fall though*/
+ default:
+ Debug( asyncmeta_debug, "msc %p ldap_send_initial_request failed. %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+ }
+
+error_unavailable:
+ if (ber)
+ ber_free(ber, 1);
+ switch (bc->nretries[candidate]) {
+ case -1: /* nretries = forever */
+ ldap_pvt_thread_yield();
+ retcode = META_SEARCH_NEED_BIND;
+ break;
+ case 0: /* no retries left */
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ rs->sr_err = LDAP_UNAVAILABLE;
+ rs->sr_text = "Unable to send add request to target";
+ retcode = META_SEARCH_ERR;
+ break;
+ default: /* more retries left - try to rebind and go again */
+ retcode = META_SEARCH_NEED_BIND;
+ bc->nretries[candidate]--;
+ ldap_pvt_thread_yield();
+ break;
+ }
+
+done:
+ (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+
+ if ( mdn.bv_val != op->o_req_dn.bv_val ) {
+ op->o_tmpfree( mdn.bv_val, op->o_tmpmemctx );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "%s <<< asyncmeta_back_add_start[%p]=%d\n", op->o_log_prefix, msc, candidates[candidate].sr_msgid );
+ return retcode;
+}
+
+
+int
+asyncmeta_back_add( Operation *op, SlapReply *rs )
+{
+ a_metainfo_t *mi = ( a_metainfo_t * )op->o_bd->be_private;
+ a_metatarget_t *mt;
+ a_metaconn_t *mc;
+ int rc, candidate = -1;
+ void *thrctx = op->o_threadctx;
+ bm_context_t *bc;
+ SlapReply *candidates;
+ time_t current_time = slap_get_time();
+ int max_pending_ops = (mi->mi_max_pending_ops == 0) ? META_BACK_CFG_MAX_PENDING_OPS : mi->mi_max_pending_ops;
+
+ Debug(LDAP_DEBUG_TRACE, "==> asyncmeta_back_add: %s\n",
+ op->o_req_dn.bv_val );
+
+ if (current_time > op->o_time) {
+ Debug(asyncmeta_debug, "==> asyncmeta_back_add[%s]: o_time:[%ld], current time: [%ld]\n",
+ op->o_log_prefix, op->o_time, current_time );
+ }
+
+ asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets, mi );
+ if (bc == NULL) {
+ rs->sr_err = LDAP_OTHER;
+ send_ldap_result(op, rs);
+ return rs->sr_err;
+ }
+
+ candidates = bc->candidates;
+ mc = asyncmeta_getconn( op, rs, candidates, &candidate, LDAP_BACK_DONTSEND, 0);
+ if ( !mc || rs->sr_err != LDAP_SUCCESS) {
+ send_ldap_result(op, rs);
+ return rs->sr_err;
+ }
+
+ mt = mi->mi_targets[ candidate ];
+ bc->timeout = mt->mt_timeout[ SLAP_OP_ADD ];
+ bc->retrying = LDAP_BACK_RETRYING;
+ bc->sendok = ( LDAP_BACK_SENDRESULT | bc->retrying );
+ bc->stoptime = op->o_time + bc->timeout;
+ bc->bc_active = 1;
+
+ if (mc->pending_ops >= max_pending_ops) {
+ rs->sr_err = LDAP_BUSY;
+ rs->sr_text = "Maximum pending ops limit exceeded";
+ send_ldap_result(op, rs);
+ return rs->sr_err;
+ }
+
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ rc = asyncmeta_add_message_queue(mc, bc);
+ mc->mc_conns[candidate].msc_active++;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+
+ if (rc != LDAP_SUCCESS) {
+ rs->sr_err = LDAP_BUSY;
+ rs->sr_text = "Maximum pending ops limit exceeded";
+ send_ldap_result(op, rs);
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ mc->mc_conns[candidate].msc_active--;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ goto finish;
+ }
+
+retry:
+ current_time = slap_get_time();
+ if (bc->timeout && bc->stoptime < current_time) {
+ int timeout_err;
+ timeout_err = op->o_protocol >= LDAP_VERSION3 ?
+ LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+ rs->sr_err = timeout_err;
+ rs->sr_text = "Operation timed out before it was sent to target";
+ asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+ goto finish;
+ }
+
+ rc = asyncmeta_dobind_init_with_retry(op, rs, bc, mc, candidate);
+ switch (rc)
+ {
+ case META_SEARCH_CANDIDATE:
+ /* target is already bound, just send the request */
+ Debug(LDAP_DEBUG_TRACE , "%s asyncmeta_back_add: "
+ "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+
+ rc = asyncmeta_back_add_start( op, rs, mc, bc, candidate, 1);
+ if (rc == META_SEARCH_ERR) {
+ asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+ goto finish;
+
+ } else if (rc == META_SEARCH_NEED_BIND) {
+ goto retry;
+ }
+ break;
+ case META_SEARCH_NOT_CANDIDATE:
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_add: NOT_CANDIDATE "
+ "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+ asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+ goto finish;
+
+ case META_SEARCH_NEED_BIND:
+ case META_SEARCH_BINDING:
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_add: BINDING "
+ "cnd=\"%d\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
+ /* add the context to the message queue but do not send the request
+ the receiver must send this when we are done binding */
+ break;
+
+ case META_SEARCH_ERR:
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_add: ERR "
+ "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+ asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+ goto finish;
+ default:
+ assert( 0 );
+ break;
+ }
+
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ mc->mc_conns[candidate].msc_active--;
+ asyncmeta_start_one_listener(mc, candidates, bc, candidate);
+ bc->bc_active--;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ rs->sr_err = SLAPD_ASYNCOP;
+finish:
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-asyncmeta/back-asyncmeta.h b/servers/slapd/back-asyncmeta/back-asyncmeta.h
new file mode 100644
index 0000000..f3ce06e
--- /dev/null
+++ b/servers/slapd/back-asyncmeta/back-asyncmeta.h
@@ -0,0 +1,782 @@
+/* back-asyncmeta.h - main header file for back-asyncmeta module */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2016 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Symas Corporation
+ * based on back-meta module for inclusion in OpenLDAP Software.
+ * This work was sponsored by Ericsson. */
+
+#ifndef SLAPD_LDAP_H
+#error "include servers/slapd/back-ldap/back-ldap.h before this file!"
+#endif /* SLAPD_LDAP_H */
+
+#ifndef SLAPD_ASYNCMETA_H
+#define SLAPD_ASYNCMETA_H
+
+#ifdef LDAP_DEVEL
+#define SLAPD_META_CLIENT_PR 1
+#endif /* LDAP_DEVEL */
+
+#include "proto-asyncmeta.h"
+
+#include "ldap_rq.h"
+
+LDAP_BEGIN_DECL
+
+/*
+ * Set META_BACK_PRINT_CONNTREE larger than 0 to dump the connection tree (debug only)
+ */
+#ifndef META_BACK_PRINT_CONNTREE
+#define META_BACK_PRINT_CONNTREE 0
+#endif /* !META_BACK_PRINT_CONNTREE */
+
+/*
+ * A a_metasingleconn_t can be in the following, mutually exclusive states:
+ *
+ * - none (0x0U)
+ * - creating META_BACK_FCONN_CREATING
+ * - initialized META_BACK_FCONN_INITED
+ * - binding LDAP_BACK_FCONN_BINDING
+ * - bound/anonymous LDAP_BACK_FCONN_ISBOUND/LDAP_BACK_FCONN_ISANON
+ *
+ * possible modifiers are:
+ *
+ * - privileged LDAP_BACK_FCONN_ISPRIV
+ * - privileged, TLS LDAP_BACK_FCONN_ISTLS
+ * - subjected to idassert LDAP_BACK_FCONN_ISIDASR
+ * - tainted LDAP_BACK_FCONN_TAINTED
+ */
+
+#define META_BACK_FCONN_INITED (0x00100000U)
+#define META_BACK_FCONN_CREATING (0x00200000U)
+#define META_BACK_FCONN_INVALID (0x00400000U)
+
+#define META_BACK_CONN_INITED(lc) LDAP_BACK_CONN_ISSET((lc), META_BACK_FCONN_INITED)
+#define META_BACK_CONN_INITED_SET(lc) LDAP_BACK_CONN_SET((lc), META_BACK_FCONN_INITED)
+#define META_BACK_CONN_INITED_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), META_BACK_FCONN_INITED)
+#define META_BACK_CONN_INITED_CPY(lc, mlc) LDAP_BACK_CONN_CPY((lc), META_BACK_FCONN_INITED, (mlc))
+#define META_BACK_CONN_CREATING(lc) LDAP_BACK_CONN_ISSET((lc), META_BACK_FCONN_CREATING)
+#define META_BACK_CONN_CREATING_SET(lc) LDAP_BACK_CONN_SET((lc), META_BACK_FCONN_CREATING)
+#define META_BACK_CONN_CREATING_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), META_BACK_FCONN_CREATING)
+#define META_BACK_CONN_CREATING_CPY(lc, mlc) LDAP_BACK_CONN_CPY((lc), META_BACK_FCONN_CREATING, (mlc))
+#define META_BACK_CONN_INVALID(lc) LDAP_BACK_CONN_ISSET((lc), META_BACK_FCONN_INVALID)
+#define META_BACK_CONN_INVALID_SET(lc) LDAP_BACK_CONN_SET((lc), META_BACK_FCONN_INVALID)
+#define META_BACK_CONN_INVALID_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), META_BACK_FCONN_INVALID)
+
+struct a_metainfo_t;
+struct a_metaconn_t;
+struct a_metatarget_t;
+#define META_NOT_CANDIDATE ((ber_tag_t)0x0)
+#define META_CANDIDATE ((ber_tag_t)0x1)
+#define META_BINDING ((ber_tag_t)0x2)
+#define META_RETRYING ((ber_tag_t)0x4)
+
+typedef struct bm_context_t {
+ LDAP_STAILQ_ENTRY(bm_context_t) bc_next;
+ struct a_metaconn_t *bc_mc;
+ time_t timeout;
+ time_t stoptime;
+ ldap_back_send_t sendok;
+ ldap_back_send_t retrying;
+ int candidate_match;
+ volatile int bc_active;
+ int searchtime; /* stoptime is a search timelimit */
+ int is_ok;
+ int is_root;
+ volatile sig_atomic_t bc_invalid;
+ SlapReply rs;
+ Operation *op;
+ Operation copy_op;
+ LDAPControl **ctrls;
+ int *msgids;
+ int *nretries; /* number of times to retry a failed send on an msc */
+ struct berval c_peer_name; /* peer name of original op->o_conn*/
+ SlapReply *candidates;
+} bm_context_t;
+
+typedef struct a_metasingleconn_t {
+#define META_CND_ISSET(rs,f) ( ( (rs)->sr_tag & (f) ) == (f) )
+#define META_CND_SET(rs,f) ( (rs)->sr_tag |= (f) )
+#define META_CND_CLEAR(rs,f) ( (rs)->sr_tag &= ~(f) )
+
+#define META_CANDIDATE_RESET(rs) ( (rs)->sr_tag = 0 )
+#define META_IS_CANDIDATE(rs) META_CND_ISSET( (rs), META_CANDIDATE )
+#define META_CANDIDATE_SET(rs) META_CND_SET( (rs), META_CANDIDATE )
+#define META_CANDIDATE_CLEAR(rs) META_CND_CLEAR( (rs), META_CANDIDATE )
+#define META_IS_BINDING(rs) META_CND_ISSET( (rs), META_BINDING )
+#define META_BINDING_SET(rs) META_CND_SET( (rs), META_BINDING )
+#define META_BINDING_CLEAR(rs) META_CND_CLEAR( (rs), META_BINDING )
+#define META_IS_RETRYING(rs) META_CND_ISSET( (rs), META_RETRYING )
+#define META_RETRYING_SET(rs) META_CND_SET( (rs), META_RETRYING )
+#define META_RETRYING_CLEAR(rs) META_CND_CLEAR( (rs), META_RETRYING )
+
+ LDAP *msc_ld;
+ LDAP *msc_ldr;
+ time_t msc_time;
+ time_t msc_binding_time;
+ time_t msc_result_time;
+ struct berval msc_bound_ndn;
+ struct berval msc_cred;
+ unsigned msc_mscflags;
+
+ /* NOTE: lc_lcflags is redefined to msc_mscflags to reuse the macros
+ * defined for back-ldap */
+#define lc_lcflags msc_mscflags
+ volatile int msc_active;
+ /* Connection for the select */
+ Connection *conn;
+} a_metasingleconn_t;
+
+typedef struct a_metaconn_t {
+ ldapconn_base_t lc_base;
+#define mc_base lc_base
+//#define mc_conn mc_base.lcb_conn
+//#define mc_local_ndn mc_base.lcb_local_ndn
+//#define mc_refcnt mc_base.lcb_refcnt
+//#define mc_create_time mc_base.lcb_create_time
+//#define mc_time mc_base.lcb_time
+
+ LDAP_TAILQ_ENTRY(a_metaconn_t) mc_q;
+
+ /* NOTE: msc_mscflags is used to recycle the #define
+ * in metasingleconn_t */
+ unsigned msc_mscflags;
+ int mc_active;
+
+ /*
+ * means that the connection is bound;
+ * of course only one target actually is ...
+ */
+ int mc_authz_target;
+#define META_BOUND_NONE (-1)
+#define META_BOUND_ALL (-2)
+
+ struct a_metainfo_t *mc_info;
+
+ int pending_ops;
+ ldap_pvt_thread_mutex_t mc_om_mutex;
+ /* queue for pending operations */
+ LDAP_STAILQ_HEAD(BCList, bm_context_t) mc_om_list;
+ /* supersedes the connection stuff */
+ a_metasingleconn_t *mc_conns;
+} a_metaconn_t;
+
+typedef enum meta_st_t {
+#if 0 /* todo */
+ META_ST_EXACT = LDAP_SCOPE_BASE,
+#endif
+ META_ST_SUBTREE = LDAP_SCOPE_SUBTREE,
+ META_ST_SUBORDINATE = LDAP_SCOPE_SUBORDINATE,
+ META_ST_REGEX /* last + 1 */
+} meta_st_t;
+
+typedef struct a_metasubtree_t {
+ meta_st_t ms_type;
+ union {
+ struct berval msu_dn;
+ struct {
+ struct berval msr_regex_pattern;
+ regex_t msr_regex;
+ } msu_regex;
+ } ms_un;
+#define ms_dn ms_un.msu_dn
+#define ms_regex ms_un.msu_regex.msr_regex
+#define ms_regex_pattern ms_un.msu_regex.msr_regex_pattern
+
+ struct a_metasubtree_t *ms_next;
+} a_metasubtree_t;
+
+typedef struct metafilter_t {
+ struct metafilter_t *mf_next;
+ struct berval mf_regex_pattern;
+ regex_t mf_regex;
+} metafilter_t;
+
+typedef struct a_metacommon_t {
+ int mc_version;
+ int mc_nretries;
+#define META_RETRY_UNDEFINED (-2)
+#define META_RETRY_FOREVER (-1)
+#define META_RETRY_NEVER (0)
+#define META_RETRY_DEFAULT (2)
+
+ unsigned mc_flags;
+#define META_BACK_CMN_ISSET(mc,f) ( ( (mc)->mc_flags & (f) ) == (f) )
+#define META_BACK_CMN_QUARANTINE(mc) META_BACK_CMN_ISSET( (mc), LDAP_BACK_F_QUARANTINE )
+#define META_BACK_CMN_CHASE_REFERRALS(mc) META_BACK_CMN_ISSET( (mc), LDAP_BACK_F_CHASE_REFERRALS )
+#define META_BACK_CMN_NOREFS(mc) META_BACK_CMN_ISSET( (mc), LDAP_BACK_F_NOREFS )
+#define META_BACK_CMN_NOUNDEFFILTER(mc) META_BACK_CMN_ISSET( (mc), LDAP_BACK_F_NOUNDEFFILTER )
+#define META_BACK_CMN_SAVECRED(mc) META_BACK_CMN_ISSET( (mc), LDAP_BACK_F_SAVECRED )
+#define META_BACK_CMN_ST_REQUEST(mc) META_BACK_CMN_ISSET( (mc), LDAP_BACK_F_ST_REQUEST )
+
+#ifdef SLAPD_META_CLIENT_PR
+ /*
+ * client-side paged results:
+ * -1: accept unsolicited paged results responses
+ * 0: off
+ * >0: always request paged results with size == mt_ps
+ */
+#define META_CLIENT_PR_DISABLE (0)
+#define META_CLIENT_PR_ACCEPT_UNSOLICITED (-1)
+ ber_int_t mc_ps;
+#endif /* SLAPD_META_CLIENT_PR */
+
+ slap_retry_info_t mc_quarantine;
+ time_t mc_network_timeout;
+ struct timeval mc_bind_timeout;
+#define META_BIND_TIMEOUT LDAP_BACK_RESULT_UTIMEOUT
+ time_t mc_timeout[ SLAP_OP_LAST ];
+} a_metacommon_t;
+
+typedef struct a_metatarget_t {
+ char *mt_uri;
+ ldap_pvt_thread_mutex_t mt_uri_mutex;
+
+ /* TODO: we might want to enable different strategies
+ * for different targets */
+ LDAP_REBIND_PROC *mt_rebind_f;
+ LDAP_URLLIST_PROC *mt_urllist_f;
+ void *mt_urllist_p;
+
+ metafilter_t *mt_filter;
+ a_metasubtree_t *mt_subtree;
+ /* F: subtree-include; T: subtree-exclude */
+ int mt_subtree_exclude;
+
+ int mt_scope;
+
+ struct berval mt_psuffix; /* pretty suffix */
+ struct berval mt_nsuffix; /* normalized suffix */
+
+ struct berval mt_lsuffixm; /* local suffix for massage */
+ struct berval mt_rsuffixm; /* remote suffix for massage */
+
+ struct berval mt_binddn;
+ struct berval mt_bindpw;
+
+ /* we only care about the TLS options here */
+ slap_bindconf mt_tls;
+
+ slap_idassert_t mt_idassert;
+#define mt_idassert_mode mt_idassert.si_mode
+#define mt_idassert_authcID mt_idassert.si_bc.sb_authcId
+#define mt_idassert_authcDN mt_idassert.si_bc.sb_binddn
+#define mt_idassert_passwd mt_idassert.si_bc.sb_cred
+#define mt_idassert_authzID mt_idassert.si_bc.sb_authzId
+#define mt_idassert_authmethod mt_idassert.si_bc.sb_method
+#define mt_idassert_sasl_mech mt_idassert.si_bc.sb_saslmech
+#define mt_idassert_sasl_realm mt_idassert.si_bc.sb_realm
+#define mt_idassert_secprops mt_idassert.si_bc.sb_secprops
+#define mt_idassert_tls mt_idassert.si_bc.sb_tls
+#define mt_idassert_flags mt_idassert.si_flags
+#define mt_idassert_authz mt_idassert.si_authz
+
+ sig_atomic_t mt_isquarantined;
+ ldap_pvt_thread_mutex_t mt_quarantine_mutex;
+
+ a_metacommon_t mt_mc;
+#define mt_nretries mt_mc.mc_nretries
+#define mt_flags mt_mc.mc_flags
+#define mt_version mt_mc.mc_version
+#define mt_ps mt_mc.mc_ps
+#define mt_network_timeout mt_mc.mc_network_timeout
+#define mt_bind_timeout mt_mc.mc_bind_timeout
+#define mt_timeout mt_mc.mc_timeout
+#define mt_quarantine mt_mc.mc_quarantine
+
+#define META_BACK_TGT_ISSET(mt,f) ( ( (mt)->mt_flags & (f) ) == (f) )
+#define META_BACK_TGT_ISMASK(mt,m,f) ( ( (mt)->mt_flags & (m) ) == (f) )
+
+#define META_BACK_TGT_SAVECRED(mt) META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_SAVECRED )
+
+#define META_BACK_TGT_USE_TLS(mt) META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_USE_TLS )
+#define META_BACK_TGT_PROPAGATE_TLS(mt) META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_PROPAGATE_TLS )
+#define META_BACK_TGT_TLS_CRITICAL(mt) META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_TLS_CRITICAL )
+
+#define META_BACK_TGT_CHASE_REFERRALS(mt) META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_CHASE_REFERRALS )
+
+#define META_BACK_TGT_T_F(mt) META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_T_F_MASK, LDAP_BACK_F_T_F )
+#define META_BACK_TGT_T_F_DISCOVER(mt) META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_T_F_MASK2, LDAP_BACK_F_T_F_DISCOVER )
+
+#define META_BACK_TGT_ABANDON(mt) META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_CANCEL_MASK, LDAP_BACK_F_CANCEL_ABANDON )
+#define META_BACK_TGT_IGNORE(mt) META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_CANCEL_MASK, LDAP_BACK_F_CANCEL_IGNORE )
+#define META_BACK_TGT_CANCEL(mt) META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_CANCEL_MASK, LDAP_BACK_F_CANCEL_EXOP )
+#define META_BACK_TGT_CANCEL_DISCOVER(mt) META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_CANCEL_MASK2, LDAP_BACK_F_CANCEL_EXOP_DISCOVER )
+#define META_BACK_TGT_QUARANTINE(mt) META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_QUARANTINE )
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+#define META_BACK_TGT_ST_REQUEST(mt) META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_ST_REQUEST )
+#define META_BACK_TGT_ST_RESPONSE(mt) META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_ST_RESPONSE )
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+#define META_BACK_TGT_NOREFS(mt) META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_NOREFS )
+#define META_BACK_TGT_NOUNDEFFILTER(mt) META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_NOUNDEFFILTER )
+
+#define META_BACK_CFG_MAX_PENDING_OPS 0x80
+#define META_BACK_CFG_MAX_TARGET_CONNS 0xFF
+#define META_BACK_CFG_DEFAULT_OPS_TIMEOUT 0x02
+
+/* the interval of the timeout checking loop in microseconds
+ * possibly make this configurable? */
+#define META_BACK_CFG_MAX_TIMEOUT_LOOP 0x70000
+ slap_mask_t mt_rep_flags;
+ int mt_timeout_ops;
+} a_metatarget_t;
+
+typedef struct a_metadncache_t {
+ ldap_pvt_thread_mutex_t mutex;
+ Avlnode *tree;
+
+#define META_DNCACHE_DISABLED (0)
+#define META_DNCACHE_FOREVER ((time_t)(-1))
+ time_t ttl; /* seconds; 0: no cache, -1: no expiry */
+} a_metadncache_t;
+
+typedef struct a_metacandidates_t {
+ int mc_ntargets;
+ SlapReply *mc_candidates;
+} a_metacandidates_t;
+
+/*
+ * Hook to allow mucking with a_metainfo_t/a_metatarget_t when quarantine is over
+ */
+typedef int (*asyncmeta_quarantine_f)( struct a_metainfo_t *, int target, void * );
+
+struct meta_out_message_t;
+
+typedef struct a_metainfo_t {
+ int mi_ntargets;
+ int mi_defaulttarget;
+#define META_DEFAULT_TARGET_NONE (-1)
+
+#define mi_nretries mi_mc.mc_nretries
+#define mi_flags mi_mc.mc_flags
+#define mi_version mi_mc.mc_version
+#define mi_ps mi_mc.mc_ps
+#define mi_network_timeout mi_mc.mc_network_timeout
+#define mi_bind_timeout mi_mc.mc_bind_timeout
+#define mi_timeout mi_mc.mc_timeout
+#define mi_quarantine mi_mc.mc_quarantine
+
+ a_metatarget_t **mi_targets;
+ a_metacandidates_t *mi_candidates;
+
+ LDAP_REBIND_PROC *mi_rebind_f;
+ LDAP_URLLIST_PROC *mi_urllist_f;
+
+ a_metadncache_t mi_cache;
+
+ struct {
+ int mic_num;
+ LDAP_TAILQ_HEAD(mc_conn_priv_q, a_metaconn_t) mic_priv;
+ } mi_conn_priv[ LDAP_BACK_PCONN_LAST ];
+ int mi_conn_priv_max;
+
+ /* NOTE: quarantine uses the connection mutex */
+ asyncmeta_quarantine_f mi_quarantine_f;
+ void *mi_quarantine_p;
+
+#define li_flags mi_flags
+/* uses flags as defined in <back-ldap/back-ldap.h> */
+#define META_BACK_F_ONERR_STOP LDAP_BACK_F_ONERR_STOP
+#define META_BACK_F_ONERR_REPORT (0x02000000U)
+#define META_BACK_F_ONERR_MASK (META_BACK_F_ONERR_STOP|META_BACK_F_ONERR_REPORT)
+#define META_BACK_F_DEFER_ROOTDN_BIND (0x04000000U)
+#define META_BACK_F_PROXYAUTHZ_ALWAYS (0x08000000U) /* users always proxyauthz */
+#define META_BACK_F_PROXYAUTHZ_ANON (0x10000000U) /* anonymous always proxyauthz */
+#define META_BACK_F_PROXYAUTHZ_NOANON (0x20000000U) /* anonymous remains anonymous */
+
+#define META_BACK_ONERR_STOP(mi) LDAP_BACK_ISSET( (mi), META_BACK_F_ONERR_STOP )
+#define META_BACK_ONERR_REPORT(mi) LDAP_BACK_ISSET( (mi), META_BACK_F_ONERR_REPORT )
+#define META_BACK_ONERR_CONTINUE(mi) ( !LDAP_BACK_ISSET( (mi), META_BACK_F_ONERR_MASK ) )
+
+#define META_BACK_DEFER_ROOTDN_BIND(mi) LDAP_BACK_ISSET( (mi), META_BACK_F_DEFER_ROOTDN_BIND )
+#define META_BACK_PROXYAUTHZ_ALWAYS(mi) LDAP_BACK_ISSET( (mi), META_BACK_F_PROXYAUTHZ_ALWAYS )
+#define META_BACK_PROXYAUTHZ_ANON(mi) LDAP_BACK_ISSET( (mi), META_BACK_F_PROXYAUTHZ_ANON )
+#define META_BACK_PROXYAUTHZ_NOANON(mi) LDAP_BACK_ISSET( (mi), META_BACK_F_PROXYAUTHZ_NOANON )
+
+#define META_BACK_QUARANTINE(mi) LDAP_BACK_ISSET( (mi), LDAP_BACK_F_QUARANTINE )
+
+ time_t mi_idle_timeout;
+ struct re_s *mi_task;
+
+ a_metacommon_t mi_mc;
+ ldap_extra_t *mi_ldap_extra;
+
+ int mi_max_timeout_ops;
+ int mi_max_pending_ops;
+ int mi_max_target_conns;
+ /* mutex for access to the connection structures */
+ ldap_pvt_thread_mutex_t mi_mc_mutex;
+ int mi_num_conns;
+ int mi_next_conn;
+ a_metaconn_t *mi_conns;
+
+ struct berval mi_suffix;
+} a_metainfo_t;
+
+typedef enum meta_op_type {
+ META_OP_ALLOW_MULTIPLE = 0,
+ META_OP_REQUIRE_SINGLE,
+ META_OP_REQUIRE_ALL
+} meta_op_type;
+
+/* Whatever context asyncmeta_dn_massage needs... */
+typedef struct a_dncookie {
+ Operation *op;
+ struct a_metatarget_t *target;
+ void *memctx;
+ int to_from;
+} a_dncookie;
+
+
+#define MASSAGE_REQ 0
+#define MASSAGE_REP 1
+
+extern void
+asyncmeta_dn_massage(a_dncookie *dc, struct berval *dn,
+ struct berval *res);
+
+extern void
+asyncmeta_filter_map_rewrite(
+ a_dncookie *dc,
+ Filter *f,
+ struct berval *fstr );
+
+extern void
+asyncmeta_back_referral_result_rewrite(
+ a_dncookie *dc,
+ BerVarray a_vals );
+
+extern a_metaconn_t *
+asyncmeta_getconn(
+ Operation *op,
+ SlapReply *rs,
+ SlapReply *candidates,
+ int *candidate,
+ ldap_back_send_t sendok,
+ int alloc_new);
+
+
+extern int
+asyncmeta_init_one_conn(
+ Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ int candidate,
+ int ispriv,
+ ldap_back_send_t sendok,
+ int dolock );
+
+extern void
+asyncmeta_quarantine(
+ Operation *op,
+ a_metainfo_t *mi,
+ SlapReply *rs,
+ int candidate );
+
+extern int
+asyncmeta_proxy_authz_cred(
+ a_metaconn_t *mc,
+ int candidate,
+ Operation *op,
+ SlapReply *rs,
+ ldap_back_send_t sendok,
+ struct berval *binddn,
+ struct berval *bindcred,
+ int *method );
+
+extern int
+asyncmeta_controls_add(
+ Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ int candidate,
+ int isroot,
+ LDAPControl ***pctrls );
+
+extern int
+asyncmeta_LTX_init_module(
+ int argc,
+ char *argv[] );
+
+/*
+ * Candidate stuff
+ */
+extern int
+asyncmeta_is_candidate(
+ a_metatarget_t *mt,
+ struct berval *ndn,
+ int scope );
+
+extern int
+asyncmeta_select_unique_candidate(
+ a_metainfo_t *mi,
+ struct berval *ndn );
+
+extern int
+asyncmeta_clear_unused_candidates(
+ Operation *op,
+ int candidate,
+ a_metaconn_t *mc,
+ SlapReply *candidates);
+
+/*
+ * Dn cache stuff (experimental)
+ */
+extern int
+asyncmeta_dncache_cmp(
+ const void *c1,
+ const void *c2 );
+
+extern int
+asyncmeta_dncache_dup(
+ void *c1,
+ void *c2 );
+
+#define META_TARGET_NONE (-1)
+#define META_TARGET_MULTIPLE (-2)
+extern int
+asyncmeta_dncache_get_target(
+ a_metadncache_t *cache,
+ struct berval *ndn );
+
+extern int
+meta_dncache_update_entry(
+ a_metadncache_t *cache,
+ struct berval *ndn,
+ int target );
+
+extern int
+asyncmeta_dncache_delete_entry(
+ a_metadncache_t *cache,
+ struct berval *ndn );
+
+extern void
+asyncmeta_dncache_free( void *entry );
+
+extern int
+asyncmeta_subtree_destroy( a_metasubtree_t *ms );
+
+extern void
+asyncmeta_filter_destroy( metafilter_t *mf );
+
+extern int
+asyncmeta_target_finish( a_metainfo_t *mi, a_metatarget_t *mt,
+ const char *log, char *msg, size_t msize
+);
+
+
+extern LDAP_REBIND_PROC asyncmeta_back_default_rebind;
+extern LDAP_URLLIST_PROC asyncmeta_back_default_urllist;
+
+/* IGNORE means that target does not (no longer) participate
+ * in the search;
+ * NOTREADY means the search on that target has not been initialized yet
+ */
+#define META_MSGID_IGNORE (-1)
+#define META_MSGID_NEED_BIND (-2)
+#define META_MSGID_CONNECTING (-3)
+#define META_MSGID_UNDEFINED (-4)
+#define META_MSGID_GOT_BIND (-5)
+
+typedef enum meta_search_candidate_t {
+ META_SEARCH_UNDEFINED = -2,
+ META_SEARCH_ERR = -1,
+ META_SEARCH_NOT_CANDIDATE,
+ META_SEARCH_CANDIDATE,
+ META_SEARCH_BINDING,
+ META_SEARCH_NEED_BIND,
+ META_SEARCH_CONNECTING
+} meta_search_candidate_t;
+
+Operation* asyncmeta_copy_op(Operation *op);
+void asyncmeta_clear_bm_context(bm_context_t *bc);
+
+int asyncmeta_add_message_queue(a_metaconn_t *mc, bm_context_t *bc);
+void asyncmeta_drop_bc(a_metaconn_t *mc, bm_context_t *bc);
+void asyncmeta_drop_bc_from_fconn(bm_context_t *bc);
+
+bm_context_t *
+asyncmeta_find_message(ber_int_t msgid, a_metaconn_t *mc, int candidate);
+
+void* asyncmeta_op_handle_result(void *ctx, void *arg);
+int asyncmeta_back_cleanup( Operation *op, SlapReply *rs, bm_context_t *bm );
+
+int
+asyncmeta_clear_one_msc(
+ Operation *op,
+ a_metaconn_t *msc,
+ int candidate,
+ int unbind,
+ const char * caller);
+
+a_metaconn_t *
+asyncmeta_get_next_mc( a_metainfo_t *mi );
+
+void* asyncmeta_timeout_loop(void *ctx, void *arg);
+
+int
+asyncmeta_start_timeout_loop(a_metatarget_t *mt, a_metainfo_t *mi);
+
+void asyncmeta_set_msc_time(a_metasingleconn_t *msc);
+
+int asyncmeta_back_cancel(
+ a_metaconn_t *mc,
+ Operation *op,
+ ber_int_t msgid,
+ int candidate );
+
+void
+asyncmeta_send_result(bm_context_t* bc, int error, char *text);
+
+int asyncmeta_new_bm_context(Operation *op,
+ SlapReply *rs,
+ bm_context_t **new_bc,
+ int ntargets,
+ a_metainfo_t *mi);
+
+int asyncmeta_start_listeners(a_metaconn_t *mc, SlapReply *candidates, bm_context_t *bc);
+int asyncmeta_start_one_listener(a_metaconn_t *mc, SlapReply *candidates, bm_context_t *bc, int candidate);
+
+meta_search_candidate_t
+asyncmeta_back_search_start(
+ Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ bm_context_t *bc,
+ int candidate,
+ struct berval *prcookie,
+ ber_int_t prsize,
+ int do_lock);
+
+meta_search_candidate_t
+asyncmeta_dobind_init(
+ Operation *op,
+ SlapReply *rs,
+ bm_context_t *bc,
+ a_metaconn_t *mc,
+ int candidate);
+
+meta_search_candidate_t
+asyncmeta_dobind_init_with_retry(
+ Operation *op,
+ SlapReply *rs,
+ bm_context_t *bc,
+ a_metaconn_t *mc,
+ int candidate);
+
+meta_search_candidate_t
+asyncmeta_back_add_start(Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ bm_context_t *bc,
+ int candidate,
+ int do_lock);
+meta_search_candidate_t
+asyncmeta_back_modify_start(Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ bm_context_t *bc,
+ int candidate,
+ int do_lock);
+
+meta_search_candidate_t
+asyncmeta_back_modrdn_start(Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ bm_context_t *bc,
+ int candidate,
+ int do_lock);
+meta_search_candidate_t
+asyncmeta_back_delete_start(Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ bm_context_t *bc,
+ int candidate,
+ int do_lock);
+
+meta_search_candidate_t
+asyncmeta_back_compare_start(Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ bm_context_t *bc,
+ int candidate,
+ int do_lock);
+
+bm_context_t *
+asyncmeta_bc_in_queue(a_metaconn_t *mc,
+ bm_context_t *bc);
+
+int
+asyncmeta_error_cleanup(Operation *op,
+ SlapReply *rs,
+ bm_context_t *bc,
+ a_metaconn_t *mc,
+ int candidate);
+
+int
+asyncmeta_reset_msc(Operation *op,
+ a_metaconn_t *mc,
+ int candidate,
+ int unbind,
+ const char *caller);
+
+
+void
+asyncmeta_back_conn_free(
+ void *v_mc );
+
+void asyncmeta_log_msc(a_metasingleconn_t *msc);
+void asyncmeta_log_conns(a_metainfo_t *mi);
+
+void asyncmeta_get_timestamp(char *buf);
+
+int
+asyncmeta_dncache_update_entry(a_metadncache_t *cache,
+ struct berval *ndn,
+ int target );
+
+void
+asyncmeta_dnattr_result_rewrite(a_dncookie *dc,
+ BerVarray a_vals);
+
+void
+asyncmeta_referral_result_rewrite(a_dncookie *dc,
+ BerVarray a_vals);
+
+meta_search_candidate_t
+asyncmeta_send_all_pending_ops(a_metaconn_t *mc,
+ int candidate,
+ void *ctx,
+ int dolock);
+meta_search_candidate_t
+asyncmeta_return_bind_errors(a_metaconn_t *mc,
+ int candidate,
+ SlapReply *bind_result,
+ void *ctx,
+ int dolock);
+
+/* The the maximum time in seconds after a result has been received on a connection,
+ * after which it can be reset if a sender error occurs. Should this be configurable? */
+#define META_BACK_RESULT_INTERVAL (2)
+
+extern int asyncmeta_debug;
+
+LDAP_END_DECL
+
+#endif /* SLAPD_ASYNCMETA_H */
diff --git a/servers/slapd/back-asyncmeta/bind.c b/servers/slapd/back-asyncmeta/bind.c
new file mode 100644
index 0000000..e290350
--- /dev/null
+++ b/servers/slapd/back-asyncmeta/bind.c
@@ -0,0 +1,1730 @@
+/* bind.c - bind request handler functions for binding
+ * to remote targets for back-asyncmeta */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2016 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Symas Corporation
+ * based on back-meta module for inclusion in OpenLDAP Software.
+ * This work was sponsored by Ericsson. */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include "slap.h"
+#include "../../../libraries/libldap/ldap-int.h"
+
+#define AVL_INTERNAL
+#include "../back-ldap/back-ldap.h"
+#include "back-asyncmeta.h"
+#include "lutil_ldap.h"
+
+#define LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ "2.16.840.1.113730.3.4.12"
+
+static int
+asyncmeta_proxy_authz_bind(
+ a_metaconn_t *mc,
+ int candidate,
+ Operation *op,
+ SlapReply *rs,
+ ldap_back_send_t sendok,
+ int dolock );
+
+static int
+asyncmeta_single_bind(
+ Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ int candidate );
+
+int
+asyncmeta_back_bind( Operation *op, SlapReply *rs )
+{
+ a_metainfo_t *mi = ( a_metainfo_t * )op->o_bd->be_private;
+ a_metaconn_t *mc = NULL;
+
+ int rc = LDAP_OTHER,
+ i,
+ gotit = 0,
+ isroot = 0;
+
+ SlapReply *candidates;
+
+ candidates = op->o_tmpcalloc(mi->mi_ntargets, sizeof(SlapReply),op->o_tmpmemctx);
+ rs->sr_err = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_ARGS, "%s asyncmeta_back_bind: dn=\"%s\".\n",
+ op->o_log_prefix, op->o_req_dn.bv_val );
+
+ /* the test on the bind method should be superfluous */
+ switch ( be_rootdn_bind( op, rs ) ) {
+ case LDAP_SUCCESS:
+ if ( META_BACK_DEFER_ROOTDN_BIND( mi ) ) {
+ /* frontend will return success */
+ return rs->sr_err;
+ }
+
+ isroot = 1;
+ /* fallthru */
+
+ case SLAP_CB_CONTINUE:
+ break;
+
+ default:
+ /* be_rootdn_bind() sent result */
+ return rs->sr_err;
+ }
+
+ /* we need asyncmeta_getconn() not send result even on error,
+ * because we want to intercept the error and make it
+ * invalidCredentials */
+ mc = asyncmeta_getconn( op, rs, candidates, NULL, LDAP_BACK_BIND_DONTSEND, 1 );
+ if ( !mc ) {
+ Debug(LDAP_DEBUG_ANY,
+ "%s asyncmeta_back_bind: no target " "for dn \"%s\" (%d%s%s).\n",
+ op->o_log_prefix, op->o_req_dn.bv_val,
+ rs->sr_err, rs->sr_text ? ". " : "",
+ rs->sr_text ? rs->sr_text : "" );
+
+ /* FIXME: there might be cases where we don't want
+ * to map the error onto invalidCredentials */
+ switch ( rs->sr_err ) {
+ case LDAP_NO_SUCH_OBJECT:
+ case LDAP_UNWILLING_TO_PERFORM:
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ rs->sr_text = NULL;
+ break;
+ }
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+
+ /*
+ * Each target is scanned ...
+ */
+ mc->mc_authz_target = META_BOUND_NONE;
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ a_metatarget_t *mt = mi->mi_targets[ i ];
+ int lerr;
+
+ /*
+ * Skip non-candidates
+ */
+ if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
+ continue;
+ }
+
+ if ( gotit == 0 ) {
+ /* set rc to LDAP_SUCCESS only if at least
+ * one candidate has been tried */
+ rc = LDAP_SUCCESS;
+ gotit = 1;
+
+ } else if ( !isroot ) {
+ /*
+ * A bind operation is expected to have
+ * ONE CANDIDATE ONLY!
+ */
+ Debug( LDAP_DEBUG_ANY,
+ "### %s asyncmeta_back_bind: more than one"
+ " candidate selected...\n",
+ op->o_log_prefix );
+ }
+
+ if ( isroot ) {
+ if ( mt->mt_idassert_authmethod == LDAP_AUTH_NONE
+ || BER_BVISNULL( &mt->mt_idassert_authcDN ) )
+ {
+ a_metasingleconn_t *msc = &mc->mc_conns[ i ];
+
+ if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
+ ch_free( msc->msc_bound_ndn.bv_val );
+ BER_BVZERO( &msc->msc_bound_ndn );
+ }
+
+ if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+ /* destroy sensitive data */
+ memset( msc->msc_cred.bv_val, 0,
+ msc->msc_cred.bv_len );
+ ch_free( msc->msc_cred.bv_val );
+ BER_BVZERO( &msc->msc_cred );
+ }
+
+ continue;
+ }
+
+
+ (void)asyncmeta_proxy_authz_bind( mc, i, op, rs, LDAP_BACK_DONTSEND, 1 );
+ lerr = rs->sr_err;
+
+ } else {
+ lerr = asyncmeta_single_bind( op, rs, mc, i );
+ }
+
+ if ( lerr != LDAP_SUCCESS ) {
+ rc = rs->sr_err = lerr;
+
+ /* FIXME: in some cases (e.g. unavailable)
+ * do not assume it's not candidate; rather
+ * mark this as an error to be eventually
+ * reported to client */
+ META_CANDIDATE_CLEAR( &candidates[ i ] );
+ break;
+ }
+ }
+
+ if ( mc != NULL ) {
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ a_metasingleconn_t *msc = &mc->mc_conns[ i ];
+ if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
+ ch_free( msc->msc_bound_ndn.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+ /* destroy sensitive data */
+ memset( msc->msc_cred.bv_val, 0,
+ msc->msc_cred.bv_len );
+ ch_free( msc->msc_cred.bv_val );
+ }
+ }
+ asyncmeta_back_conn_free( mc );
+ }
+
+ /*
+ * rc is LDAP_SUCCESS if at least one bind succeeded,
+ * err is the last error that occurred during a bind;
+ * if at least (and at most?) one bind succeeds, fine.
+ */
+ if ( rc != LDAP_SUCCESS ) {
+
+ /*
+ * deal with bind failure ...
+ */
+
+ /*
+ * no target was found within the naming context,
+ * so bind must fail with invalid credentials
+ */
+ if ( rs->sr_err == LDAP_SUCCESS && gotit == 0 ) {
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ } else {
+ rs->sr_err = slap_map_api2result( rs );
+ }
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+
+ }
+ return LDAP_SUCCESS;
+}
+
+static int
+asyncmeta_bind_op_result(
+ Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ int candidate,
+ int msgid,
+ ldap_back_send_t sendok,
+ int dolock )
+{
+ a_metainfo_t *mi = mc->mc_info;
+ a_metatarget_t *mt = mi->mi_targets[ candidate ];
+ a_metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+ LDAPMessage *res;
+ struct timeval tv;
+ int rc;
+ int nretries = mt->mt_nretries;
+
+ Debug( LDAP_DEBUG_TRACE,
+ ">>> %s asyncmeta_bind_op_result[%d]\n",
+ op->o_log_prefix, candidate );
+
+ /* make sure this is clean */
+ assert( rs->sr_ctrls == NULL );
+
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ time_t stoptime = (time_t)(-1),
+ timeout;
+ int timeout_err = op->o_protocol >= LDAP_VERSION3 ?
+ LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+ const char *timeout_text = "Operation timed out";
+ slap_op_t opidx = slap_req2op( op->o_tag );
+
+ /* since timeout is not specified, compute and use
+ * the one specific to the ongoing operation */
+ if ( opidx == LDAP_REQ_SEARCH ) {
+ if ( op->ors_tlimit <= 0 ) {
+ timeout = 0;
+
+ } else {
+ timeout = op->ors_tlimit;
+ timeout_err = LDAP_TIMELIMIT_EXCEEDED;
+ timeout_text = NULL;
+ }
+
+ } else {
+ timeout = mt->mt_timeout[ opidx ];
+ }
+
+ /* better than nothing :) */
+ if ( timeout == 0 ) {
+ if ( mi->mi_idle_timeout ) {
+ timeout = mi->mi_idle_timeout;
+
+ }
+ }
+
+ if ( timeout ) {
+ stoptime = op->o_time + timeout;
+ }
+
+ LDAP_BACK_TV_SET( &tv );
+
+ /*
+ * handle response!!!
+ */
+retry:;
+ rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
+ switch ( rc ) {
+ case 0:
+ if ( nretries != META_RETRY_NEVER
+ || ( timeout && slap_get_time() <= stoptime ) )
+ {
+ ldap_pvt_thread_yield();
+ if ( nretries > 0 ) {
+ nretries--;
+ }
+ tv = mt->mt_bind_timeout;
+ goto retry;
+ }
+
+ /* don't let anyone else use this handler,
+ * because there's a pending bind that will not
+ * be acknowledged */
+ assert( LDAP_BACK_CONN_BINDING( msc ) );
+
+#ifdef DEBUG_205
+ Debug( LDAP_DEBUG_ANY, "### %s asyncmeta_bind_op_result ldap_unbind_ext[%d] ld=%p\n",
+ op->o_log_prefix, candidate, (void *)msc->msc_ld );
+#endif /* DEBUG_205 */
+
+ rs->sr_err = timeout_err;
+ rs->sr_text = timeout_text;
+ break;
+
+ case -1:
+ ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER,
+ &rs->sr_err );
+
+ Debug( LDAP_DEBUG_ANY,
+ "### %s asyncmeta_bind_op_result[%d]: err=%d (%s) nretries=%d.\n",
+ op->o_log_prefix, candidate, rs->sr_err,
+ ldap_err2string(rs->sr_err), nretries );
+ break;
+
+ default:
+ /* only touch when activity actually took place... */
+ if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
+ msc->msc_time = op->o_time;
+ }
+
+ /* FIXME: matched? referrals? response controls? */
+ rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
+ NULL, NULL, NULL, NULL, 1 );
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_err = rc;
+ }
+ rs->sr_err = slap_map_api2result( rs );
+ break;
+ }
+ }
+
+ rs->sr_err = slap_map_api2result( rs );
+ Debug( LDAP_DEBUG_TRACE,
+ "<<< %s asyncmeta_bind_op_result[%d] err=%d\n",
+ op->o_log_prefix, candidate, rs->sr_err );
+
+ return rs->sr_err;
+}
+
+/*
+ * asyncmeta_single_bind
+ *
+ * attempts to perform a bind with creds
+ */
+static int
+asyncmeta_single_bind(
+ Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ int candidate )
+{
+ a_metainfo_t *mi = mc->mc_info;
+ a_metatarget_t *mt = mi->mi_targets[ candidate ];
+ struct berval mdn = BER_BVNULL;
+ a_metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+ int msgid;
+ a_dncookie dc;
+ struct berval save_o_dn;
+ int save_o_do_not_cache;
+ LDAPControl **ctrls = NULL;
+
+ if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
+ ch_free( msc->msc_bound_ndn.bv_val );
+ BER_BVZERO( &msc->msc_bound_ndn );
+ }
+
+ if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+ /* destroy sensitive data */
+ memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
+ ch_free( msc->msc_cred.bv_val );
+ BER_BVZERO( &msc->msc_cred );
+ }
+
+ /*
+ * Rewrite the bind dn if needed
+ */
+ dc.op = op;
+ dc.target = mt;
+ dc.memctx = op->o_tmpmemctx;
+ dc.to_from = MASSAGE_REQ;
+
+ asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn );
+
+ /* don't add proxyAuthz; set the bindDN */
+ save_o_dn = op->o_dn;
+ save_o_do_not_cache = op->o_do_not_cache;
+ op->o_do_not_cache = 1;
+ op->o_dn = op->o_req_dn;
+
+ ctrls = op->o_ctrls;
+ rs->sr_err = asyncmeta_controls_add( op, rs, mc, candidate, be_isroot(op), &ctrls );
+ op->o_dn = save_o_dn;
+ op->o_do_not_cache = save_o_do_not_cache;
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto return_results;
+ }
+
+ /* FIXME: this fixes the bind problem right now; we need
+ * to use the asynchronous version to get the "matched"
+ * and more in case of failure ... */
+ /* FIXME: should we check if at least some of the op->o_ctrls
+ * can/should be passed? */
+ for (;;) {
+ rs->sr_err = ldap_sasl_bind( msc->msc_ld, mdn.bv_val,
+ LDAP_SASL_SIMPLE, &op->orb_cred,
+ ctrls, NULL, &msgid );
+ if ( rs->sr_err != LDAP_X_CONNECTING ) {
+ break;
+ }
+ ldap_pvt_thread_yield();
+ }
+
+ mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+
+ asyncmeta_bind_op_result( op, rs, mc, candidate, msgid, LDAP_BACK_DONTSEND, 1 );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto return_results;
+ }
+
+ /* If defined, proxyAuthz will be used also when
+ * back-ldap is the authorizing backend; for this
+ * purpose, a successful bind is followed by a
+ * bind with the configured identity assertion */
+ /* NOTE: use with care */
+ if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
+ asyncmeta_proxy_authz_bind( mc, candidate, op, rs, LDAP_BACK_SENDERR, 1 );
+ if ( !LDAP_BACK_CONN_ISBOUND( msc ) ) {
+ goto return_results;
+ }
+ goto cache_refresh;
+ }
+
+ ber_bvreplace( &msc->msc_bound_ndn, &op->o_req_ndn );
+ LDAP_BACK_CONN_ISBOUND_SET( msc );
+ mc->mc_authz_target = candidate;
+
+ if ( META_BACK_TGT_SAVECRED( mt ) ) {
+ if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+ memset( msc->msc_cred.bv_val, 0,
+ msc->msc_cred.bv_len );
+ }
+ ber_bvreplace( &msc->msc_cred, &op->orb_cred );
+ ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc );
+ }
+
+cache_refresh:;
+ if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED
+ && !BER_BVISEMPTY( &op->o_req_ndn ) )
+ {
+ ( void )asyncmeta_dncache_update_entry( &mi->mi_cache,
+ &op->o_req_ndn, candidate );
+ }
+
+return_results:;
+ if ( mdn.bv_val != op->o_req_dn.bv_val ) {
+ op->o_tmpfree( mdn.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( META_BACK_TGT_QUARANTINE( mt ) ) {
+ asyncmeta_quarantine( op, mi, rs, candidate );
+ }
+ ldap_unbind_ext( msc->msc_ld, NULL, NULL );
+ msc->msc_ld = NULL;
+ ldap_ld_free( msc->msc_ldr, 0, NULL, NULL );
+ msc->msc_ldr = NULL;
+ return rs->sr_err;
+}
+
+
+/*
+ * asyncmeta_back_default_rebind
+ *
+ * This is a callback used for chasing referrals using the same
+ * credentials as the original user on this session.
+ */
+int
+asyncmeta_back_default_rebind(
+ LDAP *ld,
+ LDAP_CONST char *url,
+ ber_tag_t request,
+ ber_int_t msgid,
+ void *params )
+{
+ a_metasingleconn_t *msc = ( a_metasingleconn_t * )params;
+
+ return ldap_sasl_bind_s( ld, msc->msc_bound_ndn.bv_val,
+ LDAP_SASL_SIMPLE, &msc->msc_cred,
+ NULL, NULL, NULL );
+}
+
+/*
+ * meta_back_default_urllist
+ *
+ * This is a callback used for mucking with the urllist
+ */
+int
+asyncmeta_back_default_urllist(
+ LDAP *ld,
+ LDAPURLDesc **urllist,
+ LDAPURLDesc **url,
+ void *params )
+{
+ a_metatarget_t *mt = (a_metatarget_t *)params;
+ LDAPURLDesc **urltail;
+
+ if ( urllist == url ) {
+ return LDAP_SUCCESS;
+ }
+
+ for ( urltail = &(*url)->lud_next; *urltail; urltail = &(*urltail)->lud_next )
+ /* count */ ;
+
+ *urltail = *urllist;
+ *urllist = *url;
+ *url = NULL;
+
+ ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
+ if ( mt->mt_uri ) {
+ ch_free( mt->mt_uri );
+ }
+
+ ldap_get_option( ld, LDAP_OPT_URI, (void *)&mt->mt_uri );
+ ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
+
+ return LDAP_SUCCESS;
+}
+
+int
+asyncmeta_back_cancel(
+ a_metaconn_t *mc,
+ Operation *op,
+ ber_int_t msgid,
+ int candidate )
+{
+
+ a_metainfo_t *mi = mc->mc_info;
+ a_metatarget_t *mt = mi->mi_targets[ candidate ];
+ a_metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+
+ int rc = LDAP_OTHER;
+ struct timeval tv = { 0, 0 };
+ ber_socket_t s;
+
+ Debug( LDAP_DEBUG_TRACE, ">>> %s asyncmeta_back_cancel[%d] msgid=%d\n",
+ op->o_log_prefix, candidate, msgid );
+
+ if (!( LDAP_BACK_CONN_ISBOUND( msc )
+ || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, ">>> %s asyncmeta_back_cancel[%d] msgid=%d\n already reset",
+ op->o_log_prefix, candidate, msgid );
+ return LDAP_SUCCESS;
+ }
+
+ ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
+ if (s < 0) {
+ return rc;
+ }
+ rc = ldap_int_poll( msc->msc_ld, s, &tv, 1);
+ if (rc < 0) {
+ rc = LDAP_SERVER_DOWN;
+ return rc;
+ }
+ /* default behavior */
+ if ( META_BACK_TGT_ABANDON( mt ) ) {
+ rc = ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
+
+ } else if ( META_BACK_TGT_IGNORE( mt ) ) {
+ rc = ldap_pvt_discard( msc->msc_ld, msgid );
+
+ } else if ( META_BACK_TGT_CANCEL( mt ) ) {
+ rc = ldap_cancel_s( msc->msc_ld, msgid, NULL, NULL );
+
+ } else {
+ assert( 0 );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<<< %s asyncmeta_back_cancel[%d] err=%d\n",
+ op->o_log_prefix, candidate, rc );
+
+ return rc;
+}
+
+
+
+/*
+ * asyncmeta_back_proxy_authz_cred()
+ *
+ * prepares credentials & method for meta_back_proxy_authz_bind();
+ * or, if method is SASL, performs the SASL bind directly.
+ */
+int
+asyncmeta_back_proxy_authz_cred(
+ a_metaconn_t *mc,
+ int candidate,
+ Operation *op,
+ SlapReply *rs,
+ ldap_back_send_t sendok,
+ struct berval *binddn,
+ struct berval *bindcred,
+ int *method )
+{
+ a_metainfo_t *mi = mc->mc_info;
+ a_metatarget_t *mt = mi->mi_targets[ candidate ];
+ a_metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+ struct berval ndn;
+ int dobind = 0;
+ struct timeval old_tv = {0, 0};
+ struct timeval bind_tv = { mt->mt_timeout[ SLAP_OP_BIND ], 0};
+ /* don't proxyAuthz if protocol is not LDAPv3 */
+ switch ( mt->mt_version ) {
+ case LDAP_VERSION3:
+ break;
+
+ case 0:
+ if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
+ break;
+ }
+ /* fall thru */
+
+ default:
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+ goto done;
+ }
+
+ if ( op->o_tag == LDAP_REQ_BIND ) {
+ ndn = op->o_req_ndn;
+
+ } else if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
+ ndn = op->o_conn->c_ndn;
+
+ } else {
+ ndn = op->o_ndn;
+ }
+ rs->sr_err = LDAP_SUCCESS;
+
+ /*
+ * FIXME: we need to let clients use proxyAuthz
+ * otherwise we cannot do symmetric pools of servers;
+ * we have to live with the fact that a user can
+ * authorize itself as any ID that is allowed
+ * by the authzTo directive of the "proxyauthzdn".
+ */
+ /*
+ * NOTE: current Proxy Authorization specification
+ * and implementation do not allow proxy authorization
+ * control to be provided with Bind requests
+ */
+ /*
+ * if no bind took place yet, but the connection is bound
+ * and the "proxyauthzdn" is set, then bind as
+ * "proxyauthzdn" and explicitly add the proxyAuthz
+ * control to every operation with the dn bound
+ * to the connection as control value.
+ */
+
+ /* bind as proxyauthzdn only if no idassert mode
+ * is requested, or if the client's identity
+ * is authorized */
+ switch ( mt->mt_idassert_mode ) {
+ case LDAP_BACK_IDASSERT_LEGACY:
+ if ( !BER_BVISNULL( &ndn ) && !BER_BVISEMPTY( &ndn ) ) {
+ if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) && !BER_BVISEMPTY( &mt->mt_idassert_authcDN ) )
+ {
+ *binddn = mt->mt_idassert_authcDN;
+ *bindcred = mt->mt_idassert_passwd;
+ dobind = 1;
+ }
+ }
+ break;
+
+ default:
+ /* NOTE: rootdn can always idassert */
+ if ( BER_BVISNULL( &ndn )
+ && mt->mt_idassert_authz == NULL
+ && !( mt->mt_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) )
+ {
+ if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
+ rs->sr_err = LDAP_INAPPROPRIATE_AUTH;
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+ goto done;
+
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+ *binddn = slap_empty_bv;
+ *bindcred = slap_empty_bv;
+ break;
+
+ } else if ( mt->mt_idassert_authz && !be_isroot( op ) ) {
+ struct berval authcDN;
+
+ if ( BER_BVISNULL( &ndn ) ) {
+ authcDN = slap_empty_bv;
+
+ } else {
+ authcDN = ndn;
+ }
+ rs->sr_err = slap_sasl_matches( op, mt->mt_idassert_authz,
+ &authcDN, &authcDN );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+ goto done;
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+ *binddn = slap_empty_bv;
+ *bindcred = slap_empty_bv;
+ break;
+ }
+ }
+
+ *binddn = mt->mt_idassert_authcDN;
+ *bindcred = mt->mt_idassert_passwd;
+ dobind = 1;
+ break;
+ }
+
+ if ( dobind && mt->mt_idassert_authmethod == LDAP_AUTH_SASL ) {
+#ifdef HAVE_CYRUS_SASL
+ void *defaults = NULL;
+ struct berval authzID = BER_BVNULL;
+ int freeauthz = 0;
+
+ /* if SASL supports native authz, prepare for it */
+ if ( ( !op->o_do_not_cache || !op->o_is_auth_check ) &&
+ ( mt->mt_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
+ {
+ switch ( mt->mt_idassert_mode ) {
+ case LDAP_BACK_IDASSERT_OTHERID:
+ case LDAP_BACK_IDASSERT_OTHERDN:
+ authzID = mt->mt_idassert_authzID;
+ break;
+
+ case LDAP_BACK_IDASSERT_ANONYMOUS:
+ BER_BVSTR( &authzID, "dn:" );
+ break;
+
+ case LDAP_BACK_IDASSERT_SELF:
+ if ( BER_BVISNULL( &ndn ) ) {
+ /* connection is not authc'd, so don't idassert */
+ BER_BVSTR( &authzID, "dn:" );
+ break;
+ }
+ authzID.bv_len = STRLENOF( "dn:" ) + ndn.bv_len;
+ authzID.bv_val = slap_sl_malloc( authzID.bv_len + 1, op->o_tmpmemctx );
+ AC_MEMCPY( authzID.bv_val, "dn:", STRLENOF( "dn:" ) );
+ AC_MEMCPY( authzID.bv_val + STRLENOF( "dn:" ),
+ ndn.bv_val, ndn.bv_len + 1 );
+ freeauthz = 1;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if ( mt->mt_idassert_secprops != NULL ) {
+ rs->sr_err = ldap_set_option( msc->msc_ld,
+ LDAP_OPT_X_SASL_SECPROPS,
+ (void *)mt->mt_idassert_secprops );
+
+ if ( rs->sr_err != LDAP_OPT_SUCCESS ) {
+ rs->sr_err = LDAP_OTHER;
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+ goto done;
+ }
+ }
+
+ ldap_get_option( msc->msc_ld, LDAP_OPT_TIMEOUT, (void *)&old_tv);
+
+ if (mt->mt_timeout[ SLAP_OP_BIND ] > 0 ) {
+ rs->sr_err = ldap_set_option( msc->msc_ld,
+ LDAP_OPT_TIMEOUT,
+ (void *)&bind_tv );
+
+ if ( rs->sr_err != LDAP_OPT_SUCCESS ) {
+ rs->sr_err = LDAP_OTHER;
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+ goto done;
+ }
+ }
+ defaults = lutil_sasl_defaults( msc->msc_ld,
+ mt->mt_idassert_sasl_mech.bv_val,
+ mt->mt_idassert_sasl_realm.bv_val,
+ mt->mt_idassert_authcID.bv_val,
+ mt->mt_idassert_passwd.bv_val,
+ authzID.bv_val );
+ if ( defaults == NULL ) {
+ rs->sr_err = LDAP_OTHER;
+ LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ goto done;
+ }
+
+ rs->sr_err = ldap_sasl_interactive_bind_s( msc->msc_ld, binddn->bv_val,
+ mt->mt_idassert_sasl_mech.bv_val, NULL, NULL,
+ LDAP_SASL_QUIET, lutil_sasl_interact,
+ defaults );
+
+ /* restore the old timeout just in case */
+ ldap_set_option( msc->msc_ld, LDAP_OPT_TIMEOUT, (void *)&old_tv );
+
+ rs->sr_err = slap_map_api2result( rs );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ if ( LogTest( asyncmeta_debug ) ) {
+ char time_buf[ SLAP_TEXT_BUFLEN ];
+ asyncmeta_get_timestamp(time_buf);
+ Debug( asyncmeta_debug, "[%s] asyncmeta_back_proxy_authz_cred failed bind msc: %p\n",
+ time_buf, msc );
+ }
+ LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+
+ } else {
+ LDAP_BACK_CONN_ISBOUND_SET( msc );
+ }
+
+ lutil_sasl_freedefs( defaults );
+ if ( freeauthz ) {
+ slap_sl_free( authzID.bv_val, op->o_tmpmemctx );
+ }
+
+ goto done;
+#endif /* HAVE_CYRUS_SASL */
+ }
+
+ *method = mt->mt_idassert_authmethod;
+ switch ( mt->mt_idassert_authmethod ) {
+ case LDAP_AUTH_NONE:
+ BER_BVSTR( binddn, "" );
+ BER_BVSTR( bindcred, "" );
+ /* fallthru */
+
+ case LDAP_AUTH_SIMPLE:
+ break;
+
+ default:
+ /* unsupported! */
+ LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+ rs->sr_err = LDAP_AUTH_METHOD_NOT_SUPPORTED;
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ break;
+ }
+
+done:;
+
+ if ( !BER_BVISEMPTY( binddn ) ) {
+ LDAP_BACK_CONN_ISIDASSERT_SET( msc );
+ }
+
+ return rs->sr_err;
+}
+
+static int
+asyncmeta_proxy_authz_bind(
+ a_metaconn_t *mc,
+ int candidate,
+ Operation *op,
+ SlapReply *rs,
+ ldap_back_send_t sendok,
+ int dolock )
+{
+ a_metainfo_t *mi = mc->mc_info;
+ a_metatarget_t *mt = mi->mi_targets[ candidate ];
+ a_metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+ struct berval binddn = BER_BVC( "" ),
+ cred = BER_BVC( "" );
+ int method = LDAP_AUTH_NONE,
+ rc;
+
+ rc = asyncmeta_back_proxy_authz_cred( mc, candidate, op, rs, sendok, &binddn, &cred, &method );
+ if ( rc == LDAP_SUCCESS && !LDAP_BACK_CONN_ISBOUND( msc ) ) {
+ int msgid;
+
+ switch ( method ) {
+ case LDAP_AUTH_NONE:
+ case LDAP_AUTH_SIMPLE:
+ for (;;) {
+ rs->sr_err = ldap_sasl_bind( msc->msc_ld,
+ binddn.bv_val, LDAP_SASL_SIMPLE,
+ &cred, NULL, NULL, &msgid );
+ if ( rs->sr_err != LDAP_X_CONNECTING ) {
+ break;
+ }
+ ldap_pvt_thread_yield();
+ }
+
+ rc = asyncmeta_bind_op_result( op, rs, mc, candidate, msgid, sendok, dolock );
+ if ( rc == LDAP_SUCCESS ) {
+ /* set rebind stuff in case of successful proxyAuthz bind,
+ * so that referral chasing is attempted using the right
+ * identity */
+ LDAP_BACK_CONN_ISBOUND_SET( msc );
+ ber_bvreplace( &msc->msc_bound_ndn, &binddn );
+
+ if ( META_BACK_TGT_SAVECRED( mt ) ) {
+ if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+ memset( msc->msc_cred.bv_val, 0,
+ msc->msc_cred.bv_len );
+ }
+ ber_bvreplace( &msc->msc_cred, &cred );
+ ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc );
+ }
+ }
+ break;
+
+ default:
+ assert( 0 );
+ break;
+ }
+ }
+
+ return LDAP_BACK_CONN_ISBOUND( msc );
+}
+
+
+static int
+asyncmeta_back_proxy_authz_ctrl(Operation *op,
+ SlapReply *rs,
+ struct berval *bound_ndn,
+ int version,
+ int isroot,
+ slap_idassert_t *si,
+ LDAPControl *ctrl )
+{
+ slap_idassert_mode_t mode;
+ struct berval assertedID,
+ ndn;
+
+ rs->sr_err = SLAP_CB_CONTINUE;
+
+ /* FIXME: SASL/EXTERNAL over ldapi:// doesn't honor the authcID,
+ * but if it is not set this test fails. We need a different
+ * means to detect if idassert is enabled */
+ if ( ( BER_BVISNULL( &si->si_bc.sb_authcId ) || BER_BVISEMPTY( &si->si_bc.sb_authcId ) )
+ && ( BER_BVISNULL( &si->si_bc.sb_binddn ) || BER_BVISEMPTY( &si->si_bc.sb_binddn ) )
+ && BER_BVISNULL( &si->si_bc.sb_saslmech ) )
+ {
+ goto done;
+ }
+
+ if ( !op->o_conn || op->o_do_not_cache || ( isroot ) ) {
+ goto done;
+ }
+
+ if ( op->o_tag == LDAP_REQ_BIND ) {
+ ndn = op->o_req_ndn;
+
+#if 0
+ } else if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
+ ndn = op->o_conn->c_ndn;
+#endif
+ } else {
+ ndn = op->o_ndn;
+ }
+
+ if ( si->si_mode == LDAP_BACK_IDASSERT_LEGACY ) {
+ if ( op->o_proxy_authz ) {
+ /*
+ * FIXME: we do not want to perform proxyAuthz
+ * on behalf of the client, because this would
+ * be performed with "proxyauthzdn" privileges.
+ *
+ * This might actually be too strict, since
+ * the "proxyauthzdn" authzTo, and each entry's
+ * authzFrom attributes may be crafted
+ * to avoid unwanted proxyAuthz to take place.
+ */
+#if 0
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "proxyAuthz not allowed within namingContext";
+#endif
+ goto done;
+ }
+
+ if ( !BER_BVISNULL( bound_ndn ) ) {
+ goto done;
+ }
+
+ if ( BER_BVISNULL( &ndn ) ) {
+ goto done;
+ }
+
+ if ( BER_BVISNULL( &si->si_bc.sb_binddn ) ) {
+ goto done;
+ }
+
+ } else if ( si->si_bc.sb_method == LDAP_AUTH_SASL ) {
+ if ( ( si->si_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
+ {
+ /* already asserted in SASL via native authz */
+ goto done;
+ }
+
+ } else if ( si->si_authz && !isroot ) {
+ int rc;
+ struct berval authcDN;
+
+ if ( BER_BVISNULL( &ndn ) ) {
+ authcDN = slap_empty_bv;
+ } else {
+ authcDN = ndn;
+ }
+ rc = slap_sasl_matches( op, si->si_authz,
+ &authcDN, &authcDN );
+ if ( rc != LDAP_SUCCESS ) {
+ if ( si->si_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
+ /* ndn is not authorized
+ * to use idassert */
+ rs->sr_err = rc;
+ }
+ goto done;
+ }
+ }
+
+ if ( op->o_proxy_authz ) {
+ /*
+ * FIXME: we can:
+ * 1) ignore the already set proxyAuthz control
+ * 2) leave it in place, and don't set ours
+ * 3) add both
+ * 4) reject the operation
+ *
+ * option (4) is very drastic
+ * option (3) will make the remote server reject
+ * the operation, thus being equivalent to (4)
+ * option (2) will likely break the idassert
+ * assumptions, so we cannot accept it;
+ * option (1) means that we are contradicting
+ * the client's request.
+ *
+ * I think (4) is the only correct choice.
+ */
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "proxyAuthz not allowed within namingContext";
+ }
+
+ if ( op->o_is_auth_check ) {
+ mode = LDAP_BACK_IDASSERT_NOASSERT;
+
+ } else {
+ mode = si->si_mode;
+ }
+
+ switch ( mode ) {
+ case LDAP_BACK_IDASSERT_LEGACY:
+ /* original behavior:
+ * assert the client's identity */
+ case LDAP_BACK_IDASSERT_SELF:
+ assertedID = ndn;
+ break;
+
+ case LDAP_BACK_IDASSERT_ANONYMOUS:
+ /* assert "anonymous" */
+ assertedID = slap_empty_bv;
+ break;
+
+ case LDAP_BACK_IDASSERT_NOASSERT:
+ /* don't assert; bind as proxyauthzdn */
+ goto done;
+
+ case LDAP_BACK_IDASSERT_OTHERID:
+ case LDAP_BACK_IDASSERT_OTHERDN:
+ /* assert idassert DN */
+ assertedID = si->si_bc.sb_authzId;
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+ /* if we got here, "" is allowed to proxyAuthz */
+ if ( BER_BVISNULL( &assertedID ) ) {
+ assertedID = slap_empty_bv;
+ }
+
+ /* don't idassert the bound DN (ITS#4497) */
+ if ( dn_match( &assertedID, bound_ndn ) ) {
+ goto done;
+ }
+
+ ctrl->ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
+ ctrl->ldctl_iscritical = ( ( si->si_flags & LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL ) == LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL );
+
+ switch ( si->si_mode ) {
+ /* already in u:ID or dn:DN form */
+ case LDAP_BACK_IDASSERT_OTHERID:
+ case LDAP_BACK_IDASSERT_OTHERDN:
+ ber_dupbv_x( &ctrl->ldctl_value, &assertedID, op->o_tmpmemctx );
+ rs->sr_err = LDAP_SUCCESS;
+ break;
+
+ /* needs the dn: prefix */
+ default:
+ ctrl->ldctl_value.bv_len = assertedID.bv_len + STRLENOF( "dn:" );
+ ctrl->ldctl_value.bv_val = op->o_tmpalloc( ctrl->ldctl_value.bv_len + 1,
+ op->o_tmpmemctx );
+ AC_MEMCPY( ctrl->ldctl_value.bv_val, "dn:", STRLENOF( "dn:" ) );
+ AC_MEMCPY( &ctrl->ldctl_value.bv_val[ STRLENOF( "dn:" ) ],
+ assertedID.bv_val, assertedID.bv_len + 1 );
+ rs->sr_err = LDAP_SUCCESS;
+ break;
+ }
+
+ /* Older versions of <draft-weltman-ldapv3-proxy> required
+ * to encode the value of the authzID (and called it proxyDN);
+ * this hack provides compatibility with those DSAs that
+ * implement it this way */
+ if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) {
+ struct berval authzID = ctrl->ldctl_value;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+
+ ber_init2( ber, 0, LBER_USE_DER );
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+
+ tag = ber_printf( ber, "O", &authzID );
+ if ( tag == LBER_ERROR ) {
+ rs->sr_err = LDAP_OTHER;
+ goto free_ber;
+ }
+
+ if ( ber_flatten2( ber, &ctrl->ldctl_value, 1 ) == -1 ) {
+ rs->sr_err = LDAP_OTHER;
+ goto free_ber;
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+
+free_ber:;
+ op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx );
+ ber_free_buf( ber );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ } else if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) {
+ struct berval authzID = ctrl->ldctl_value,
+ tmp;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+
+ if ( strncasecmp( authzID.bv_val, "dn:", STRLENOF( "dn:" ) ) != 0 ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ tmp = authzID;
+ tmp.bv_val += STRLENOF( "dn:" );
+ tmp.bv_len -= STRLENOF( "dn:" );
+
+ ber_init2( ber, 0, LBER_USE_DER );
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+
+ /* apparently, Mozilla API encodes this
+ * as "SEQUENCE { LDAPDN }" */
+ tag = ber_printf( ber, "{O}", &tmp );
+ if ( tag == LBER_ERROR ) {
+ rs->sr_err = LDAP_OTHER;
+ goto free_ber2;
+ }
+
+ if ( ber_flatten2( ber, &ctrl->ldctl_value, 1 ) == -1 ) {
+ rs->sr_err = LDAP_OTHER;
+ goto free_ber2;
+ }
+
+ ctrl->ldctl_oid = LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ;
+ rs->sr_err = LDAP_SUCCESS;
+
+free_ber2:;
+ op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx );
+ ber_free_buf( ber );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto done;
+ }
+ }
+
+done:;
+
+ return rs->sr_err;
+}
+
+/*
+ * Add controls;
+ *
+ * if any needs to be added, it is prepended to existing ones,
+ * in a newly allocated array. The companion function
+ * mi->mi_ldap_extra->controls_free() must be used to restore the original
+ * status of op->o_ctrls.
+ */
+int
+asyncmeta_controls_add( Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ int candidate,
+ int isroot,
+ LDAPControl ***pctrls )
+{
+ a_metainfo_t *mi = mc->mc_info;
+ a_metatarget_t *mt = mi->mi_targets[ candidate ];
+ a_metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+
+ LDAPControl **ctrls = NULL;
+ /* set to the maximum number of controls this backend can add */
+ LDAPControl c[ 2 ] = {{ 0 }};
+ int n = 0, i, j1 = 0, j2 = 0, skipped = 0;
+
+ *pctrls = NULL;
+
+ rs->sr_err = LDAP_SUCCESS;
+
+ /* don't add controls if protocol is not LDAPv3 */
+ switch ( mt->mt_version ) {
+ case LDAP_VERSION3:
+ break;
+
+ case 0:
+ if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
+ break;
+ }
+ /* fall thru */
+
+ default:
+ goto done;
+ }
+
+ /* put controls that go __before__ existing ones here */
+
+ /* proxyAuthz for identity assertion */
+ switch ( asyncmeta_back_proxy_authz_ctrl( op, rs, &msc->msc_bound_ndn,
+ mt->mt_version, isroot, &mt->mt_idassert, &c[ j1 ] ) )
+ {
+ case SLAP_CB_CONTINUE:
+ break;
+
+ case LDAP_SUCCESS:
+ j1++;
+ break;
+
+ default:
+ goto done;
+ }
+
+ /* put controls that go __after__ existing ones here */
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ /* session tracking */
+ if ( META_BACK_TGT_ST_REQUEST( mt ) ) {
+ switch ( slap_ctrl_session_tracking_request_add( op, rs, &c[ j1 + j2 ] ) ) {
+ case SLAP_CB_CONTINUE:
+ break;
+
+ case LDAP_SUCCESS:
+ j2++;
+ break;
+
+ default:
+ goto done;
+ }
+ }
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+ if ( rs->sr_err == SLAP_CB_CONTINUE ) {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+
+ /* if nothing to do, just bail out */
+ if ( j1 == 0 && j2 == 0 ) {
+ goto done;
+ }
+
+ assert( j1 + j2 <= (int) (sizeof( c )/sizeof( c[0] )) );
+
+ if ( op->o_ctrls ) {
+ for ( n = 0; op->o_ctrls[ n ]; n++ )
+ /* just count ctrls */ ;
+ }
+
+ ctrls = op->o_tmpalloc( (n + j1 + j2 + 1) * sizeof( LDAPControl * ) + ( j1 + j2 ) * sizeof( LDAPControl ),
+ op->o_tmpmemctx );
+ if ( j1 ) {
+ ctrls[ 0 ] = (LDAPControl *)&ctrls[ n + j1 + j2 + 1 ];
+ *ctrls[ 0 ] = c[ 0 ];
+ for ( i = 1; i < j1; i++ ) {
+ ctrls[ i ] = &ctrls[ 0 ][ i ];
+ *ctrls[ i ] = c[ i ];
+ }
+ }
+
+ i = 0;
+ if ( op->o_ctrls ) {
+ LDAPControl *proxyauthz = ldap_control_find(
+ LDAP_CONTROL_PROXY_AUTHZ, op->o_ctrls, NULL );
+
+ for ( i = 0; op->o_ctrls[ i ]; i++ ) {
+ /* Only replace it if we generated one */
+ if ( j1 && proxyauthz && proxyauthz == op->o_ctrls[ i ] ) {
+ /* Frontend has already checked only one is present */
+ assert( skipped == 0 );
+ skipped++;
+ continue;
+ }
+ ctrls[ i + j1 - skipped ] = op->o_ctrls[ i ];
+ }
+ }
+
+ n += j1 - skipped;
+ if ( j2 ) {
+ ctrls[ n ] = (LDAPControl *)&ctrls[ n + j2 + 1 ] + j1;
+ *ctrls[ n ] = c[ j1 ];
+ for ( i = 1; i < j2; i++ ) {
+ ctrls[ n + i ] = &ctrls[ n ][ i ];
+ *ctrls[ n + i ] = c[ i ];
+ }
+ }
+
+ ctrls[ n + j2 ] = NULL;
+
+done:;
+ if ( ctrls == NULL ) {
+ ctrls = op->o_ctrls;
+ }
+
+ *pctrls = ctrls;
+
+ return rs->sr_err;
+}
+
+
+/*
+ * asyncmeta_dobind_init()
+ *
+ * initiates bind for a candidate target
+ */
+meta_search_candidate_t
+asyncmeta_dobind_init(Operation *op, SlapReply *rs, bm_context_t *bc, a_metaconn_t *mc, int candidate)
+{
+ SlapReply *candidates = bc->candidates;
+ a_metainfo_t *mi = ( a_metainfo_t * )mc->mc_info;
+ a_metatarget_t *mt = mi->mi_targets[ candidate ];
+ a_metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+ struct berval binddn = msc->msc_bound_ndn,
+ cred = msc->msc_cred;
+ int method;
+
+ int rc;
+ ber_int_t msgid;
+
+ meta_search_candidate_t retcode;
+
+ Debug( LDAP_DEBUG_TRACE, "%s >>> asyncmeta_dobind_init[%d] msc %p\n",
+ op->o_log_prefix, candidate, msc );
+
+ if ( mc->mc_authz_target == META_BOUND_ALL ) {
+ return META_SEARCH_CANDIDATE;
+ }
+
+ if ( slapd_shutdown ) {
+ rs->sr_err = LDAP_UNAVAILABLE;
+ return META_SEARCH_ERR;
+ }
+
+ retcode = META_SEARCH_BINDING;
+ if ( LDAP_BACK_CONN_ISBOUND( msc ) || LDAP_BACK_CONN_ISANON( msc ) ) {
+ /* already bound (or anonymous) */
+
+#ifdef DEBUG_205
+ char buf[ SLAP_TEXT_BUFLEN ] = { '\0' };
+ int bound = 0;
+
+ if ( LDAP_BACK_CONN_ISBOUND( msc ) ) {
+ bound = 1;
+ }
+
+ Debug( LDAP_DEBUG_ANY,
+ "### %s asyncmeta_dobind_init[%d] mc=%p ld=%p%s DN=\"%s\"\n",
+ op->o_log_prefix, candidate, (void *)mc,
+ (void *)msc->msc_ld, bound ? " bound" : " anonymous",
+ bound == 0 ? "" : msc->msc_bound_ndn.bv_val );
+#endif /* DEBUG_205 */
+
+ retcode = META_SEARCH_CANDIDATE;
+
+ } else if ( META_BACK_CONN_CREATING( msc ) || LDAP_BACK_CONN_BINDING( msc ) ) {
+ /* another thread is binding the target for this conn; wait */
+
+#ifdef DEBUG_205
+
+ Debug( LDAP_DEBUG_ANY,
+ "### %s asyncmeta_dobind_init[%d] mc=%p ld=%p needbind\n",
+ op->o_log_prefix, candidate, (void *)mc,
+ (void *)msc->msc_ld );
+#endif /* DEBUG_205 */
+
+ candidates[ candidate ].sr_msgid = META_MSGID_NEED_BIND;
+ retcode = META_SEARCH_NEED_BIND;
+ } else {
+ /* we'll need to bind the target for this conn */
+
+#ifdef DEBUG_205
+
+ Debug( LDAP_DEBUG_ANY,
+ "### %s asyncmeta_dobind_init[%d] mc=%p ld=%p binding\n",
+ op->o_log_prefix, candidate, (void *)mc,
+ (void *)msc->msc_ld );
+#endif /* DEBUG_205 */
+
+ if ( msc->msc_ld == NULL ) {
+ /* for some reason (e.g. because formerly in "binding"
+ * state, with eventual connection expiration or invalidation)
+ * it was not initialized as expected */
+
+ Debug( LDAP_DEBUG_ANY, "%s asyncmeta_dobind_init[%d] mc=%p ld=NULL\n",
+ op->o_log_prefix, candidate, (void *)mc );
+
+ rc = asyncmeta_init_one_conn( op, rs, mc, candidate,
+ LDAP_BACK_CONN_ISPRIV( mc ), LDAP_BACK_DONTSEND, 0 );
+
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ assert( msc->msc_ld != NULL );
+ break;
+
+ case LDAP_SERVER_DOWN:
+ case LDAP_UNAVAILABLE:
+ goto down;
+
+ default:
+ goto other;
+ }
+ }
+
+ LDAP_BACK_CONN_BINDING_SET( msc );
+ }
+
+ if ( retcode != META_SEARCH_BINDING ) {
+ return retcode;
+ }
+
+ if ( op->o_conn != NULL &&
+ !op->o_do_not_cache &&
+ ( BER_BVISNULL( &msc->msc_bound_ndn ) ||
+ BER_BVISEMPTY( &msc->msc_bound_ndn ) ||
+ ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) )
+ {
+ rc = asyncmeta_back_proxy_authz_cred( mc, candidate, op, rs, LDAP_BACK_DONTSEND, &binddn, &cred, &method );
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ break;
+ case LDAP_UNAVAILABLE:
+ goto down;
+ default:
+ goto other;
+ }
+
+ /* NOTE: we copy things here, even if bind didn't succeed yet,
+ * because the connection is not shared until bind is over */
+ if ( !BER_BVISNULL( &binddn ) ) {
+ ber_bvreplace( &msc->msc_bound_ndn, &binddn );
+ if ( META_BACK_TGT_SAVECRED( mt ) && !BER_BVISNULL( &cred ) ) {
+ if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+ memset( msc->msc_cred.bv_val, 0,
+ msc->msc_cred.bv_len );
+ }
+ ber_bvreplace( &msc->msc_cred, &cred );
+ }
+ }
+ if ( LDAP_BACK_CONN_ISBOUND( msc ) ) {
+ /* apparently, idassert was configured with SASL bind,
+ * so bind occurred inside meta_back_proxy_authz_cred() */
+ LDAP_BACK_CONN_BINDING_CLEAR( msc );
+ return META_SEARCH_CANDIDATE;
+ }
+
+ /* paranoid */
+ switch ( method ) {
+ case LDAP_AUTH_NONE:
+ case LDAP_AUTH_SIMPLE:
+ /* do a simple bind with binddn, cred */
+ break;
+
+ default:
+ assert( 0 );
+ break;
+ }
+ }
+
+ assert( msc->msc_ld != NULL );
+
+ if ( !BER_BVISEMPTY( &binddn ) && BER_BVISEMPTY( &cred ) ) {
+ /* bind anonymously? */
+ Debug( LDAP_DEBUG_ANY, "%s asyncmeta_dobind_init[%d] mc=%p: "
+ "non-empty dn with empty cred; binding anonymously\n",
+ op->o_log_prefix, candidate, (void *)mc );
+ cred = slap_empty_bv;
+
+ } else if ( BER_BVISEMPTY( &binddn ) && !BER_BVISEMPTY( &cred ) ) {
+ /* error */
+ Debug( LDAP_DEBUG_ANY, "%s asyncmeta_dobind_init[%d] mc=%p: "
+ "empty dn with non-empty cred: error\n",
+ op->o_log_prefix, candidate, (void *)mc );
+ rc = LDAP_OTHER;
+ goto other;
+ }
+retry_bind:
+ if ( LogTest( asyncmeta_debug ) ) {
+ char time_buf[ SLAP_TEXT_BUFLEN ];
+ asyncmeta_get_timestamp(time_buf);
+ Debug( asyncmeta_debug, "[%s] asyncmeta_dobind_init sending bind msc: %p\n",
+ time_buf, msc );
+ }
+ rc = ldap_sasl_bind( msc->msc_ld, binddn.bv_val, LDAP_SASL_SIMPLE, &cred,
+ NULL, NULL, &msgid );
+ ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE, &rc );
+ if ( LogTest( asyncmeta_debug ) ) {
+ char time_buf[ SLAP_TEXT_BUFLEN ];
+ asyncmeta_get_timestamp(time_buf);
+ Debug( asyncmeta_debug, "[%s] asyncmeta_dobind_init rc=%d msc: %p\n",
+ time_buf, rc, msc );
+ }
+ if ( LogTest( LDAP_DEBUG_TRACE )) {
+ ber_socket_t s;
+ char sockname[LDAP_IPADDRLEN];
+ struct berval sockbv = BER_BVC( sockname );
+ Sockaddr addr;
+ socklen_t len = sizeof( addr );
+
+ ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
+ getsockname( s, &addr.sa_addr, &len );
+ ldap_pvt_sockaddrstr( &addr, &sockbv );
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_dobind_init msc %p ld %p ldr %p fd %d addr %s\n",
+ op->o_log_prefix, msc, msc->msc_ld, msc->msc_ldr, s, sockname );
+ }
+
+ if (rc == LDAP_SERVER_DOWN ) {
+ goto down;
+ } else if (rc == LDAP_BUSY) {
+ if (rs->sr_text == NULL) {
+ rs->sr_text = "Unable to establish LDAP connection to target within the specified network timeout.";
+ }
+ LDAP_BACK_CONN_BINDING_CLEAR( msc );
+ goto other;
+ }
+ /* mark as need bind so it gets send when the bind response is received */
+ candidates[ candidate ].sr_msgid = META_MSGID_NEED_BIND;
+ asyncmeta_set_msc_time(msc);
+#ifdef DEBUG_205
+ Debug( LDAP_DEBUG_ANY,
+ "### %s asyncmeta_dobind_init[%d] mc=%p ld=%p rc=%d\n",
+ op->o_log_prefix, candidate, (void *)mc,
+ (void *)mc->mc_conns[candidate].msc_ld, rc );
+#endif /* DEBUG_205 */
+
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ assert( msgid >= 0 );
+ if ( LogTest( asyncmeta_debug ) ) {
+ char time_buf[ SLAP_TEXT_BUFLEN ];
+ asyncmeta_get_timestamp(time_buf);
+ Debug( asyncmeta_debug, "[%s] asyncmeta_dobind_init sending bind success msc: %p\n",
+ time_buf, msc );
+ }
+ META_BINDING_SET( &candidates[ candidate ] );
+ rs->sr_err = LDAP_SUCCESS;
+ msc->msc_binding_time = slap_get_time();
+ return META_SEARCH_BINDING;
+
+ case LDAP_X_CONNECTING:
+ /* must retry, same conn */
+ candidates[ candidate ].sr_msgid = META_MSGID_CONNECTING;
+ LDAP_BACK_CONN_BINDING_CLEAR( msc );
+ goto retry_bind;
+
+ case LDAP_SERVER_DOWN:
+down:;
+ retcode = META_SEARCH_ERR;
+ rs->sr_err = LDAP_UNAVAILABLE;
+ if (rs->sr_text == NULL) {
+ rs->sr_text = "Unable to bind to remote target - target down or unavailable";
+ }
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ LDAP_BACK_CONN_BINDING_CLEAR( msc );
+ break;
+
+ /* fall thru */
+
+ default:
+other:;
+ rs->sr_err = rc;
+ rc = slap_map_api2result( rs );
+ candidates[ candidate ].sr_err = rc;
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ retcode = META_SEARCH_ERR;
+
+ } else {
+ retcode = META_SEARCH_NOT_CANDIDATE;
+ }
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ LDAP_BACK_CONN_BINDING_CLEAR( msc );
+ break;
+ }
+
+ return retcode;
+}
+
+
+
+
+meta_search_candidate_t
+asyncmeta_dobind_init_with_retry(Operation *op, SlapReply *rs, bm_context_t *bc, a_metaconn_t *mc, int candidate)
+{
+
+ int rc;
+ a_metasingleconn_t *msc = &mc->mc_conns[candidate];
+ a_metainfo_t *mi = mc->mc_info;
+ a_metatarget_t *mt = mi->mi_targets[ candidate ];
+
+ if (META_BACK_CONN_INVALID(msc) || (LDAP_BACK_CONN_BINDING( msc ) && msc->msc_binding_time > 0
+ && (msc->msc_binding_time + mt->mt_timeout[ SLAP_OP_BIND ]) < slap_get_time())) {
+ char buf[ SLAP_TEXT_BUFLEN ];
+ snprintf( buf, sizeof( buf ), "called from %s:%d", __FILE__, __LINE__ );
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+ asyncmeta_reset_msc(NULL, mc, candidate, 0, buf);
+
+ rc = asyncmeta_init_one_conn( op, rs, mc, candidate,
+ LDAP_BACK_CONN_ISPRIV( mc ), LDAP_BACK_DONTSEND, 0 );
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+ }
+
+ if ( LDAP_BACK_CONN_ISBOUND( msc ) || LDAP_BACK_CONN_ISANON( msc ) ) {
+ if ( mc->pending_ops > 1 ) {
+ asyncmeta_send_all_pending_ops( mc, candidate, op->o_threadctx, 1 );
+ }
+ return META_SEARCH_CANDIDATE;
+ }
+
+retry_dobind:
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+ rc = asyncmeta_dobind_init(op, rs, bc, mc, candidate);
+ if (rs->sr_err != LDAP_UNAVAILABLE && rs->sr_err != LDAP_BUSY) {
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+ return rc;
+ } else if (bc->nretries[candidate] == 0) {
+ char buf[ SLAP_TEXT_BUFLEN ];
+ snprintf( buf, sizeof( buf ), "called from %s:%d", __FILE__, __LINE__ );
+ asyncmeta_reset_msc(NULL, mc, candidate, 0, buf);
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+ return rc;
+ }
+ /* need to retry */
+ bc->nretries[candidate]--;
+ if ( LogTest( LDAP_DEBUG_TRACE ) ) {
+ /* this lock is required; however,
+ * it's invoked only when logging is on */
+ ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
+ Debug( LDAP_DEBUG_ANY,
+ "%s asyncmeta_dobind_init_with_retry[%d]: retrying URI=\"%s\" DN=\"%s\".\n",
+ op->o_log_prefix, candidate, mt->mt_uri,
+ BER_BVISNULL(&msc->msc_bound_ndn) ? "" : msc->msc_bound_ndn.bv_val );
+ ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
+ }
+
+ asyncmeta_reset_msc(NULL, mc, candidate, 0, __FUNCTION__);
+ rc = asyncmeta_init_one_conn( op, rs, mc, candidate,
+ LDAP_BACK_CONN_ISPRIV( mc ), LDAP_BACK_DONTSEND, 0 );
+
+ if (rs->sr_err != LDAP_SUCCESS) {
+ asyncmeta_reset_msc(NULL, mc, candidate, 0, __FUNCTION__);
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+ return META_SEARCH_ERR;
+ }
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+ goto retry_dobind;
+ return rc;
+}
diff --git a/servers/slapd/back-asyncmeta/candidates.c b/servers/slapd/back-asyncmeta/candidates.c
new file mode 100644
index 0000000..5f3b228
--- /dev/null
+++ b/servers/slapd/back-asyncmeta/candidates.c
@@ -0,0 +1,239 @@
+/* candidates.c - candidate targets selection and processing for
+ * back-asyncmeta */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2016 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* ACKNOWLEDGEMENTS:
++ * This work was developed by Symas Corporation
++ * based on back-meta module for inclusion in OpenLDAP Software.
++ * This work was sponsored by Ericsson. */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include "ac/string.h"
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-asyncmeta.h"
+
+/*
+ * The meta-directory has one suffix, called <suffix>.
+ * It handles a pool of target servers, each with a branch suffix
+ * of the form <branch X>,<suffix>, where <branch X> may be empty.
+ *
+ * When the meta-directory receives a request with a request DN that belongs
+ * to a branch, the corresponding target is invoked. When the request DN
+ * does not belong to a specific branch, all the targets that
+ * are compatible with the request DN are selected as candidates, and
+ * the request is spawned to all the candidate targets
+ *
+ * A request is characterized by a request DN. The following cases are
+ * handled:
+ * - the request DN is the suffix: <dn> == <suffix>,
+ * all the targets are candidates (search ...)
+ * - the request DN is a branch suffix: <dn> == <branch X>,<suffix>, or
+ * - the request DN is a subtree of a branch suffix:
+ * <dn> == <rdn>,<branch X>,<suffix>,
+ * the target is the only candidate.
+ *
+ * A possible extension will include the handling of multiple suffixes
+ */
+
+static a_metasubtree_t *
+asyncmeta_subtree_match( a_metatarget_t *mt, struct berval *ndn, int scope )
+{
+ a_metasubtree_t *ms = mt->mt_subtree;
+
+ for ( ms = mt->mt_subtree; ms; ms = ms->ms_next ) {
+ switch ( ms->ms_type ) {
+ case META_ST_SUBTREE:
+ if ( dnIsSuffix( ndn, &ms->ms_dn ) ) {
+ return ms;
+ }
+ break;
+
+ case META_ST_SUBORDINATE:
+ if ( dnIsSuffix( ndn, &ms->ms_dn ) &&
+ ( ndn->bv_len > ms->ms_dn.bv_len || scope != LDAP_SCOPE_BASE ) )
+ {
+ return ms;
+ }
+ break;
+
+ case META_ST_REGEX:
+ /* NOTE: cannot handle scope */
+ if ( regexec( &ms->ms_regex, ndn->bv_val, 0, NULL, 0 ) == 0 ) {
+ return ms;
+ }
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * returns 1 if suffix is candidate for dn, otherwise 0
+ *
+ * Note: this function should never be called if dn is the <suffix>.
+ */
+int
+asyncmeta_is_candidate(
+ a_metatarget_t *mt,
+ struct berval *ndn,
+ int scope )
+{
+ struct berval rdn;
+ int d = ndn->bv_len - mt->mt_nsuffix.bv_len;
+
+ if ( d >= 0 ) {
+ if ( !dnIsSuffix( ndn, &mt->mt_nsuffix ) ) {
+ return META_NOT_CANDIDATE;
+ }
+
+ /*
+ * | match | exclude |
+ * +---------+---------+-------------------+
+ * | T | T | not candidate |
+ * | F | T | continue checking |
+ * +---------+---------+-------------------+
+ * | T | F | candidate |
+ * | F | F | not candidate |
+ * +---------+---------+-------------------+
+ */
+
+ if ( mt->mt_subtree ) {
+ int match = ( asyncmeta_subtree_match( mt, ndn, scope ) != NULL );
+
+ if ( !mt->mt_subtree_exclude ) {
+ return match ? META_CANDIDATE : META_NOT_CANDIDATE;
+ }
+
+ if ( match /* && mt->mt_subtree_exclude */ ) {
+ return META_NOT_CANDIDATE;
+ }
+ }
+
+ switch ( mt->mt_scope ) {
+ case LDAP_SCOPE_SUBTREE:
+ default:
+ return META_CANDIDATE;
+
+ case LDAP_SCOPE_SUBORDINATE:
+ if ( d > 0 ) {
+ return META_CANDIDATE;
+ }
+ break;
+
+ /* nearly useless; not allowed by config */
+ case LDAP_SCOPE_ONELEVEL:
+ if ( d > 0 ) {
+ rdn.bv_val = ndn->bv_val;
+ rdn.bv_len = (ber_len_t)d - STRLENOF( "," );
+ if ( dnIsOneLevelRDN( &rdn ) ) {
+ return META_CANDIDATE;
+ }
+ }
+ break;
+
+ /* nearly useless; not allowed by config */
+ case LDAP_SCOPE_BASE:
+ if ( d == 0 ) {
+ return META_CANDIDATE;
+ }
+ break;
+ }
+
+ } else /* if ( d < 0 ) */ {
+ if ( !dnIsSuffix( &mt->mt_nsuffix, ndn ) ) {
+ return META_NOT_CANDIDATE;
+ }
+
+ switch ( scope ) {
+ case LDAP_SCOPE_SUBTREE:
+ case LDAP_SCOPE_SUBORDINATE:
+ /*
+ * suffix longer than dn, but common part matches
+ */
+ return META_CANDIDATE;
+
+ case LDAP_SCOPE_ONELEVEL:
+ rdn.bv_val = mt->mt_nsuffix.bv_val;
+ rdn.bv_len = (ber_len_t)(-d) - STRLENOF( "," );
+ if ( dnIsOneLevelRDN( &rdn ) ) {
+ return META_CANDIDATE;
+ }
+ break;
+ }
+ }
+
+ return META_NOT_CANDIDATE;
+}
+
+/*
+ * meta_back_select_unique_candidate
+ *
+ * returns the index of the candidate in case it is unique, otherwise
+ * META_TARGET_NONE if none matches, or
+ * META_TARGET_MULTIPLE if more than one matches
+ * Note: ndn MUST be normalized.
+ */
+int
+asyncmeta_select_unique_candidate(
+ a_metainfo_t *mi,
+ struct berval *ndn )
+{
+ int i, candidate = META_TARGET_NONE;
+
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ a_metatarget_t *mt = mi->mi_targets[ i ];
+
+ if ( asyncmeta_is_candidate( mt, ndn, LDAP_SCOPE_BASE ) ) {
+ if ( candidate == META_TARGET_NONE ) {
+ candidate = i;
+
+ }
+ }
+ }
+
+ return candidate;
+}
+
+/*
+ * asyncmeta_clear_unused_candidates
+ *
+ * clears all candidates except candidate
+ */
+int
+asyncmeta_clear_unused_candidates(
+ Operation *op,
+ int candidate,
+ a_metaconn_t *mc,
+ SlapReply *candidates)
+{
+ a_metainfo_t *mi = mc->mc_info;
+ int i;
+
+ for ( i = 0; i < mi->mi_ntargets; ++i ) {
+ if ( i == candidate ) {
+ continue;
+ }
+ META_CANDIDATE_RESET( &candidates[ i ] );
+ }
+
+ return 0;
+}
diff --git a/servers/slapd/back-asyncmeta/compare.c b/servers/slapd/back-asyncmeta/compare.c
new file mode 100644
index 0000000..1349cac
--- /dev/null
+++ b/servers/slapd/back-asyncmeta/compare.c
@@ -0,0 +1,304 @@
+/* compare.c - compare exop handler for back-asyncmeta */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2016 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* ACKNOWLEDGEMENTS:
++ * This work was developed by Symas Corporation
++ * based on back-meta module for inclusion in OpenLDAP Software.
++ * This work was sponsored by Ericsson. */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+#include "slap.h"
+#include "../../../libraries/liblber/lber-int.h"
+#include "../../../libraries/libldap/ldap-int.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-asyncmeta.h"
+
+meta_search_candidate_t
+asyncmeta_back_compare_start(Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ bm_context_t *bc,
+ int candidate,
+ int do_lock)
+{
+ a_dncookie dc;
+ a_metainfo_t *mi = mc->mc_info;
+ a_metatarget_t *mt = mi->mi_targets[ candidate ];
+ struct berval c_attr = op->orc_ava->aa_desc->ad_cname;
+ struct berval mdn = BER_BVNULL;
+ struct berval mapped_value = op->orc_ava->aa_value;
+ int rc = 0;
+ LDAPControl **ctrls = NULL;
+ meta_search_candidate_t retcode = META_SEARCH_CANDIDATE;
+ BerElement *ber = NULL;
+ a_metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+ SlapReply *candidates = bc->candidates;
+ ber_int_t msgid;
+
+ dc.op = op;
+ dc.target = mt;
+ dc.memctx = op->o_tmpmemctx;
+ dc.to_from = MASSAGE_REQ;
+
+ asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn );
+
+ if ( op->orc_ava->aa_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName )
+ asyncmeta_dn_massage( &dc, &op->orc_ava->aa_value, &mapped_value );
+
+ asyncmeta_set_msc_time(msc);
+ ctrls = op->o_ctrls;
+ if ( asyncmeta_controls_add( op, rs, mc, candidate, bc->is_root,&ctrls ) != LDAP_SUCCESS )
+ {
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ retcode = META_SEARCH_ERR;
+ goto done;
+ }
+ /* someone might have reset the connection */
+ if (!( LDAP_BACK_CONN_ISBOUND( msc )
+ || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+ Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+
+ ber = ldap_build_compare_req( msc->msc_ld, mdn.bv_val, c_attr.bv_val, &mapped_value,
+ ctrls, NULL, &msgid);
+
+ if (!ber) {
+ Debug( asyncmeta_debug, "%s asyncmeta_back_compare_start: Operation encoding failed with errno %d\n",
+ op->o_log_prefix, msc->msc_ld->ld_errno );
+ rs->sr_err = LDAP_OPERATIONS_ERROR;
+ rs->sr_text = "Failed to encode proxied request";
+ retcode = META_SEARCH_ERR;
+ goto done;
+ }
+
+ if (ber) {
+ struct timeval tv = {0, mt->mt_network_timeout*1000};
+ ber_socket_t s;
+ if (!( LDAP_BACK_CONN_ISBOUND( msc )
+ || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+ Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+
+ ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
+ if (s < 0) {
+ Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+ rc = ldap_int_poll( msc->msc_ld, s, &tv, 1);
+ if (rc < 0) {
+ Debug( asyncmeta_debug, "msc %p not writable within network timeout %s:%d\n", msc, __FILE__, __LINE__ );
+ if ((msc->msc_result_time + META_BACK_RESULT_INTERVAL) < slap_get_time()) {
+ rc = LDAP_SERVER_DOWN;
+ } else {
+ goto error_unavailable;
+ }
+ } else {
+ candidates[ candidate ].sr_msgid = msgid;
+ rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_COMPARE,
+ mdn.bv_val, ber, msgid );
+ if (rc == msgid)
+ rc = LDAP_SUCCESS;
+ else
+ rc = LDAP_SERVER_DOWN;
+ ber = NULL;
+ }
+
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ retcode = META_SEARCH_CANDIDATE;
+ asyncmeta_set_msc_time(msc);
+ goto done;
+
+ case LDAP_SERVER_DOWN:
+ /* do not lock if called from asyncmeta_handle_bind_result. Also do not reset the connection */
+ if (do_lock > 0) {
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ asyncmeta_reset_msc(NULL, mc, candidate, 0, __FUNCTION__);
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ }
+ /* fall though*/
+ default:
+ Debug( asyncmeta_debug, "msc %p ldap_send_initial_request failed. %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+ }
+
+error_unavailable:
+ if (ber)
+ ber_free(ber, 1);
+ switch (bc->nretries[candidate]) {
+ case -1: /* nretries = forever */
+ retcode = META_SEARCH_NEED_BIND;
+ break;
+ case 0: /* no retries left */
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ rs->sr_err = LDAP_UNAVAILABLE;
+ rs->sr_text = "Unable to send compare request to target";
+ retcode = META_SEARCH_ERR;
+ break;
+ default: /* more retries left - try to rebind and go again */
+ retcode = META_SEARCH_NEED_BIND;
+ bc->nretries[candidate]--;
+ break;
+ }
+done:
+ (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+
+ if ( op->orc_ava->aa_value.bv_val != mapped_value.bv_val ) {
+ op->o_tmpfree( mapped_value.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( mdn.bv_val != op->o_req_dn.bv_val ) {
+ op->o_tmpfree( mdn.bv_val, op->o_tmpmemctx );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "%s <<< asyncmeta_back_compare_start[%p]=%d\n", op->o_log_prefix, msc, candidates[candidate].sr_msgid );
+ return retcode;
+}
+
+int
+asyncmeta_back_compare( Operation *op, SlapReply *rs )
+{
+ a_metainfo_t *mi = ( a_metainfo_t * )op->o_bd->be_private;
+ a_metatarget_t *mt;
+ a_metaconn_t *mc;
+ int rc, candidate = -1;
+ void *thrctx = op->o_threadctx;
+ bm_context_t *bc;
+ SlapReply *candidates;
+ time_t current_time = slap_get_time();
+ int max_pending_ops = (mi->mi_max_pending_ops == 0) ? META_BACK_CFG_MAX_PENDING_OPS : mi->mi_max_pending_ops;
+
+ Debug(LDAP_DEBUG_ARGS, "==> asyncmeta_back_compare: %s\n",
+ op->o_req_dn.bv_val );
+
+ if (current_time > op->o_time) {
+ Debug( asyncmeta_debug, "==> asyncmeta_back_compare[%s]: o_time:[%ld], current time: [%ld]\n",
+ op->o_log_prefix, op->o_time, current_time );
+ }
+ asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets, mi );
+ if (bc == NULL) {
+ rs->sr_err = LDAP_OTHER;
+ send_ldap_result(op, rs);
+ return rs->sr_err;
+ }
+
+ candidates = bc->candidates;
+ mc = asyncmeta_getconn( op, rs, candidates, &candidate, LDAP_BACK_DONTSEND, 0);
+ if ( !mc || rs->sr_err != LDAP_SUCCESS) {
+ send_ldap_result(op, rs);
+ return rs->sr_err;
+ }
+
+ mt = mi->mi_targets[ candidate ];
+ bc->timeout = mt->mt_timeout[ SLAP_OP_COMPARE ];
+ bc->retrying = LDAP_BACK_RETRYING;
+ bc->sendok = ( LDAP_BACK_SENDRESULT | bc->retrying );
+ bc->stoptime = op->o_time + bc->timeout;
+ bc->bc_active = 1;
+
+ if (mc->pending_ops >= max_pending_ops) {
+ rs->sr_err = LDAP_BUSY;
+ rs->sr_text = "Maximum pending ops limit exceeded";
+ send_ldap_result(op, rs);
+ return rs->sr_err;
+ }
+
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ rc = asyncmeta_add_message_queue(mc, bc);
+ mc->mc_conns[candidate].msc_active++;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+
+ if (rc != LDAP_SUCCESS) {
+ rs->sr_err = LDAP_BUSY;
+ rs->sr_text = "Maximum pending ops limit exceeded";
+ send_ldap_result(op, rs);
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ mc->mc_conns[candidate].msc_active--;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ goto finish;
+ }
+
+retry:
+ if (bc->timeout && bc->stoptime < slap_get_time()) {
+ int timeout_err;
+ timeout_err = op->o_protocol >= LDAP_VERSION3 ?
+ LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+ rs->sr_err = timeout_err;
+ rs->sr_text = "Operation timed out before it was sent to target";
+ asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+ goto finish;
+ }
+
+ rc = asyncmeta_dobind_init_with_retry(op, rs, bc, mc, candidate);
+ switch (rc)
+ {
+ case META_SEARCH_CANDIDATE:
+ /* target is already bound, just send the request */
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_compare: "
+ "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+
+ rc = asyncmeta_back_compare_start( op, rs, mc, bc, candidate, 1);
+ if (rc == META_SEARCH_ERR) {
+ asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+ goto finish;
+
+ } else if (rc == META_SEARCH_NEED_BIND) {
+ goto retry;
+ }
+ break;
+ case META_SEARCH_NOT_CANDIDATE:
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_compare: NOT_CANDIDATE "
+ "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+ asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+ goto finish;
+
+ case META_SEARCH_NEED_BIND:
+ case META_SEARCH_BINDING:
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_compare: BINDING "
+ "cnd=\"%d\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
+ /* Todo add the context to the message queue but do not send the request
+ the receiver must send this when we are done binding */
+ /* question - how would do receiver know to which targets??? */
+ break;
+
+ case META_SEARCH_ERR:
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_compare: ERR "
+ "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+ asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+ goto finish;
+ default:
+ assert( 0 );
+ break;
+ }
+
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ mc->mc_conns[candidate].msc_active--;
+ asyncmeta_start_one_listener(mc, candidates, bc, candidate);
+ bc->bc_active--;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ rs->sr_err = SLAPD_ASYNCOP;
+finish:
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-asyncmeta/config.c b/servers/slapd/back-asyncmeta/config.c
new file mode 100644
index 0000000..849ac01
--- /dev/null
+++ b/servers/slapd/back-asyncmeta/config.c
@@ -0,0 +1,2443 @@
+/* config.c - configuration parsing for back-asyncmeta */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2016 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Symas Corporation
+ * based on back-meta module for inclusion in OpenLDAP Software.
+ * This work was sponsored by Ericsson. */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+#include "ldif.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-asyncmeta.h"
+
+#ifdef LDAP_DEVEL
+#define SLAP_AUTH_DN 1
+#endif
+
+static ConfigDriver asyncmeta_back_cf_gen;
+static ConfigLDAPadd asyncmeta_ldadd;
+static ConfigCfAdd asyncmeta_cfadd;
+
+/* Three sets of enums:
+ * 1) attrs that are only valid in the base config
+ * 2) attrs that are valid in base or target
+ * 3) attrs that are only valid in a target
+ */
+
+/* Base attrs */
+enum {
+ LDAP_BACK_CFG_DNCACHE_TTL = 1,
+ LDAP_BACK_CFG_IDLE_TIMEOUT,
+ LDAP_BACK_CFG_ONERR,
+ LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER,
+ LDAP_BACK_CFG_CONNPOOLMAX,
+ LDAP_BACK_CFG_MAX_TIMEOUT_OPS,
+ LDAP_BACK_CFG_MAX_PENDING_OPS,
+ LDAP_BACK_CFG_MAX_TARGET_CONNS,
+ LDAP_BACK_CFG_LAST_BASE,
+};
+
+/* Base or target */
+enum {
+ LDAP_BACK_CFG_BIND_TIMEOUT = LDAP_BACK_CFG_LAST_BASE,
+ LDAP_BACK_CFG_CANCEL,
+ LDAP_BACK_CFG_CHASE,
+ LDAP_BACK_CFG_CLIENT_PR,
+ LDAP_BACK_CFG_DEFAULT_T,
+ LDAP_BACK_CFG_NETWORK_TIMEOUT,
+ LDAP_BACK_CFG_NOREFS,
+ LDAP_BACK_CFG_NOUNDEFFILTER,
+ LDAP_BACK_CFG_NRETRIES,
+ LDAP_BACK_CFG_QUARANTINE,
+ LDAP_BACK_CFG_REBIND,
+ LDAP_BACK_CFG_TIMEOUT,
+ LDAP_BACK_CFG_VERSION,
+ LDAP_BACK_CFG_ST_REQUEST,
+ LDAP_BACK_CFG_T_F,
+ LDAP_BACK_CFG_TLS,
+ LDAP_BACK_CFG_LAST_BOTH
+};
+
+/* Target attrs */
+enum {
+ LDAP_BACK_CFG_URI = LDAP_BACK_CFG_LAST_BOTH,
+ LDAP_BACK_CFG_IDASSERT_AUTHZFROM,
+ LDAP_BACK_CFG_IDASSERT_BIND,
+ LDAP_BACK_CFG_SUFFIXM,
+ LDAP_BACK_CFG_SUBTREE_EX,
+ LDAP_BACK_CFG_SUBTREE_IN,
+ LDAP_BACK_CFG_KEEPALIVE,
+ LDAP_BACK_CFG_FILTER,
+ LDAP_BACK_CFG_TCP_USER_TIMEOUT,
+ LDAP_BACK_CFG_LAST
+};
+
+static ConfigTable a_metacfg[] = {
+ { "uri", "uri", 2, 0, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_URI,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:0.14 "
+ "NAME 'olcDbURI' "
+ "DESC 'URI (list) for remote DSA' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "tls", "what", 2, 0, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_TLS,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.1 "
+ "NAME 'olcDbStartTLS' "
+ "DESC 'StartTLS' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "idassert-bind", "args", 2, 0, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_IDASSERT_BIND,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.7 "
+ "NAME 'olcDbIDAssertBind' "
+ "DESC 'Remote Identity Assertion administrative identity auth bind configuration' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "idassert-authzFrom", "authzRule", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_IDASSERT_AUTHZFROM,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.9 "
+ "NAME 'olcDbIDAssertAuthzFrom' "
+ "DESC 'Remote Identity Assertion authz rules' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "X-ORDERED 'VALUES' )",
+ NULL, NULL },
+ { "rebind-as-user", "true|FALSE", 1, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_REBIND,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.10 "
+ "NAME 'olcDbRebindAsUser' "
+ "DESC 'Rebind as user' "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "chase-referrals", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_CHASE,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.11 "
+ "NAME 'olcDbChaseReferrals' "
+ "DESC 'Chase referrals' "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "t-f-support", "true|FALSE|discover", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_T_F,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.12 "
+ "NAME 'olcDbTFSupport' "
+ "DESC 'Absolute filters support' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "timeout", "timeout(list)", 2, 0, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_TIMEOUT,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.14 "
+ "NAME 'olcDbTimeout' "
+ "DESC 'Per-operation timeouts' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "idle-timeout", "timeout", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_IDLE_TIMEOUT,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.15 "
+ "NAME 'olcDbIdleTimeout' "
+ "DESC 'connection idle timeout' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "network-timeout", "timeout", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_NETWORK_TIMEOUT,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.17 "
+ "NAME 'olcDbNetworkTimeout' "
+ "DESC 'connection network timeout' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "protocol-version", "version", 2, 2, 0,
+ ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_VERSION,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.18 "
+ "NAME 'olcDbProtocolVersion' "
+ "DESC 'protocol version' "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "cancel", "ABANDON|ignore|exop", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_CANCEL,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.20 "
+ "NAME 'olcDbCancel' "
+ "DESC 'abandon/ignore/exop operations when appropriate' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "quarantine", "retrylist", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_QUARANTINE,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.21 "
+ "NAME 'olcDbQuarantine' "
+ "DESC 'Quarantine database if connection fails and retry according to rule' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "conn-pool-max", "<n>", 2, 2, 0,
+ ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_CONNPOOLMAX,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.23 "
+ "NAME 'olcDbConnectionPoolMax' "
+ "DESC 'Max size of privileged connections pool' "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ { "session-tracking-request", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_ST_REQUEST,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.24 "
+ "NAME 'olcDbSessionTrackingRequest' "
+ "DESC 'Add session tracking control to proxied requests' "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+ { "norefs", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_NOREFS,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.25 "
+ "NAME 'olcDbNoRefs' "
+ "DESC 'Do not return search reference responses' "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "noundeffilter", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_NOUNDEFFILTER,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.26 "
+ "NAME 'olcDbNoUndefFilter' "
+ "DESC 'Do not propagate undefined search filters' "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "suffixmassage", "local> <remote", 2, 3, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_SUFFIXM,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.117 "
+ "NAME 'olcDbSuffixMassage' "
+ "DESC 'DN suffix massage' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "subtree-exclude", "pattern", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_SUBTREE_EX,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.103 "
+ "NAME 'olcDbSubtreeExclude' "
+ "DESC 'DN of subtree to exclude from target' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )",
+ NULL, NULL },
+ { "subtree-include", "pattern", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_SUBTREE_IN,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.104 "
+ "NAME 'olcDbSubtreeInclude' "
+ "DESC 'DN of subtree to include in target' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )",
+ NULL, NULL },
+ { "default-target", "[none|<target ID>]", 1, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_DEFAULT_T,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.105 "
+ "NAME 'olcDbDefaultTarget' "
+ "DESC 'Specify the default target' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "dncache-ttl", "ttl", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_DNCACHE_TTL,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.106 "
+ "NAME 'olcDbDnCacheTtl' "
+ "DESC 'dncache ttl' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "bind-timeout", "microseconds", 2, 2, 0,
+ ARG_MAGIC|ARG_ULONG|LDAP_BACK_CFG_BIND_TIMEOUT,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.107 "
+ "NAME 'olcDbBindTimeout' "
+ "DESC 'bind timeout' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "onerr", "CONTINUE|report|stop", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_ONERR,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.108 "
+ "NAME 'olcDbOnErr' "
+ "DESC 'error handling' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "pseudoroot-bind-defer", "TRUE|false", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.109 "
+ "NAME 'olcDbPseudoRootBindDefer' "
+ "DESC 'error handling' "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "root-bind-defer", "TRUE|false", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER,
+ asyncmeta_back_cf_gen, NULL, NULL, NULL },
+ { "nretries", "NEVER|forever|<number>", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_NRETRIES,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.110 "
+ "NAME 'olcDbNretries' "
+ "DESC 'retry handling' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "client-pr", "accept-unsolicited|disable|<size>", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_CLIENT_PR,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.111 "
+ "NAME 'olcDbClientPr' "
+ "DESC 'PagedResults handling' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "", "", 0, 0, 0, ARG_IGNORED,
+ NULL, "( OLcfgDbAt:3.116 NAME 'olcAsyncMetaSub' "
+ "DESC 'Placeholder to name a Target entry' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE X-ORDERED 'SIBLINGS' )", NULL, NULL },
+
+ { "keepalive", "keepalive", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_KEEPALIVE,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.29 "
+ "NAME 'olcDbKeepalive' "
+ "DESC 'TCP keepalive' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "tcp-user-timeout", "milliseconds", 2, 2, 0,
+ ARG_MAGIC|ARG_UINT|LDAP_BACK_CFG_TCP_USER_TIMEOUT,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.30 "
+ "NAME 'olcDbTcpUserTimeout' "
+ "DESC 'TCP User Timeout' "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "filter", "pattern", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_FILTER,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.112 "
+ "NAME 'olcDbFilter' "
+ "DESC 'Filter regex pattern to include in target' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString )",
+ NULL, NULL },
+
+ { "max-pending-ops", "<n>", 2, 2, 0,
+ ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_MAX_PENDING_OPS,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.113 "
+ "NAME 'olcDbMaxPendingOps' "
+ "DESC 'Maximum number of pending operations' "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "max-target-conns", "<n>", 2, 2, 0,
+ ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_MAX_TARGET_CONNS,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.114 "
+ "NAME 'olcDbMaxTargetConns' "
+ "DESC 'Maximum number of open connections per target' "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "max-timeout-ops", "<n>", 2, 2, 0,
+ ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_MAX_TIMEOUT_OPS,
+ asyncmeta_back_cf_gen, "( OLcfgDbAt:3.115 "
+ "NAME 'olcDbMaxTimeoutOps' "
+ "DESC 'Maximum number of consecutive timeout operations after which the connection is reset' "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED,
+ NULL, NULL, NULL, NULL }
+};
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+#define ST_ATTR "$ olcDbSessionTrackingRequest "
+#else
+#define ST_ATTR ""
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+#define COMMON_ATTRS \
+ "$ olcDbBindTimeout " \
+ "$ olcDbCancel " \
+ "$ olcDbChaseReferrals " \
+ "$ olcDbClientPr " \
+ "$ olcDbDefaultTarget " \
+ "$ olcDbNetworkTimeout " \
+ "$ olcDbNoRefs " \
+ "$ olcDbNoUndefFilter " \
+ "$ olcDbNretries " \
+ "$ olcDbProtocolVersion " \
+ "$ olcDbQuarantine " \
+ "$ olcDbRebindAsUser " \
+ ST_ATTR \
+ "$ olcDbStartTLS " \
+ "$ olcDbTFSupport "
+
+static ConfigOCs a_metaocs[] = {
+ { "( OLcfgDbOc:3.4 "
+ "NAME 'olcAsyncMetaConfig' "
+ "DESC 'Asyncmeta backend configuration' "
+ "SUP olcDatabaseConfig "
+ "MAY ( olcDbDnCacheTtl "
+ "$ olcDbIdleTimeout "
+ "$ olcDbOnErr "
+ "$ olcDbPseudoRootBindDefer "
+ "$ olcDbConnectionPoolMax "
+ "$ olcDbMaxTimeoutOps"
+ "$ olcDbMaxPendingOps "
+ "$ olcDbMaxTargetConns"
+ /* defaults, may be overridden per-target */
+ COMMON_ATTRS
+ ") )",
+ Cft_Database, a_metacfg, NULL, asyncmeta_cfadd },
+ { "( OLcfgDbOc:3.5 "
+ "NAME 'olcAsyncMetaTargetConfig' "
+ "DESC 'Asyncmeta target configuration' "
+ "SUP olcConfig STRUCTURAL "
+ "MUST ( olcAsyncMetaSub $ olcDbURI ) "
+ "MAY ( olcDbIDAssertAuthzFrom "
+ "$ olcDbIDAssertBind "
+ "$ olcDbSuffixMassage "
+ "$ olcDbSubtreeExclude "
+ "$ olcDbSubtreeInclude "
+ "$ olcDbTimeout "
+ "$ olcDbKeepalive "
+ "$ olcDbFilter "
+ "$ olcDbTcpUserTimeout "
+
+ /* defaults may be inherited */
+ COMMON_ATTRS
+ ") )",
+ Cft_Misc, a_metacfg, asyncmeta_ldadd },
+ { NULL, 0, NULL }
+};
+
+static int
+asyncmeta_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *c )
+{
+ if ( p->ce_type != Cft_Database || !p->ce_be ||
+ p->ce_be->be_cf_ocs != a_metaocs )
+ return LDAP_CONSTRAINT_VIOLATION;
+
+ c->be = p->ce_be;
+ return LDAP_SUCCESS;
+}
+
+static int
+asyncmeta_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *c )
+{
+ a_metainfo_t *mi = ( a_metainfo_t * )c->be->be_private;
+ struct berval bv;
+ int i;
+
+ bv.bv_val = c->cr_msg;
+ for ( i=0; i<mi->mi_ntargets; i++ ) {
+ bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "olcAsyncMetaSub=" SLAP_X_ORDERED_FMT "uri", i );
+ c->ca_private = mi->mi_targets[i];
+ c->valx = i;
+ config_build_entry( op, rs, p->e_private, c,
+ &bv, &a_metaocs[1], NULL );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+asyncmeta_back_new_target(
+ a_metatarget_t **mtp )
+{
+ a_metatarget_t *mt;
+
+ *mtp = NULL;
+
+ mt = ch_calloc( sizeof( a_metatarget_t ), 1 );
+
+ ldap_pvt_thread_mutex_init( &mt->mt_uri_mutex );
+
+ mt->mt_idassert_mode = LDAP_BACK_IDASSERT_LEGACY;
+ mt->mt_idassert_authmethod = LDAP_AUTH_NONE;
+ mt->mt_idassert_tls = SB_TLS_DEFAULT;
+ /* by default, use proxyAuthz control on each operation */
+ mt->mt_idassert_flags = LDAP_BACK_AUTH_PRESCRIPTIVE;
+
+ *mtp = mt;
+
+ return 0;
+}
+
+/* suffixmassage config */
+static int
+asyncmeta_suffixm_config(
+ ConfigArgs *c,
+ int argc,
+ char **argv,
+ a_metatarget_t *mt
+)
+{
+ BackendDB *tmp_bd;
+ struct berval dn, nvnc, pvnc, nrnc, prnc;
+ int j;
+
+ /*
+ * syntax:
+ *
+ * suffixmassage <local suffix> <remote suffix>
+ *
+ * the <local suffix> field must be defined as a valid suffix
+ * (or suffixAlias?) for the current database;
+ * the <remote suffix> shouldn't have already been
+ * defined as a valid suffix or suffixAlias for the
+ * current server
+ */
+
+ ber_str2bv( argv[ 1 ], 0, 0, &dn );
+ if ( dnPrettyNormal( NULL, &dn, &pvnc, &nvnc, NULL ) != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "suffix \"%s\" is invalid",
+ argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ for ( j = 0; !BER_BVISNULL( &c->be->be_nsuffix[ j ] ); j++ ) {
+ if ( dnIsSuffix( &nvnc, &c->be->be_nsuffix[ 0 ] ) ) {
+ break;
+ }
+ }
+
+ if ( BER_BVISNULL( &c->be->be_nsuffix[ j ] ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "suffix \"%s\" must be within the database naming context",
+ argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ free( pvnc.bv_val );
+ free( nvnc.bv_val );
+ return 1;
+ }
+
+ ber_str2bv( argv[ 2 ], 0, 0, &dn );
+ if ( dnPrettyNormal( NULL, &dn, &prnc, &nrnc, NULL ) != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "massaged suffix \"%s\" is invalid",
+ argv[2] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ free( pvnc.bv_val );
+ free( nvnc.bv_val );
+ return 1;
+ }
+
+ tmp_bd = select_backend( &nrnc, 0 );
+ if ( tmp_bd != NULL && tmp_bd->be_private == c->be->be_private ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: warning: <massaged suffix> \"%s\" resolves to this database, in "
+ "\"suffixMassage <suffix> <massaged suffix>\"\n",
+ c->log, prnc.bv_val );
+ }
+
+ mt->mt_lsuffixm = pvnc;
+ mt->mt_rsuffixm = prnc;
+
+ free( nvnc.bv_val );
+ free( nrnc.bv_val );
+
+ return 0;
+}
+
+int
+asyncmeta_subtree_free( a_metasubtree_t *ms )
+{
+ switch ( ms->ms_type ) {
+ case META_ST_SUBTREE:
+ case META_ST_SUBORDINATE:
+ ber_memfree( ms->ms_dn.bv_val );
+ break;
+
+ case META_ST_REGEX:
+ regfree( &ms->ms_regex );
+ ber_memfree( ms->ms_regex_pattern.bv_val );
+ break;
+
+ default:
+ return -1;
+ }
+
+ ch_free( ms );
+ return 0;
+}
+
+int
+asyncmeta_subtree_destroy( a_metasubtree_t *ms )
+{
+ if ( ms->ms_next ) {
+ asyncmeta_subtree_destroy( ms->ms_next );
+ }
+
+ return asyncmeta_subtree_free( ms );
+}
+
+static void
+asyncmeta_filter_free( metafilter_t *mf )
+{
+ regfree( &mf->mf_regex );
+ ber_memfree( mf->mf_regex_pattern.bv_val );
+ ch_free( mf );
+}
+
+void
+asyncmeta_filter_destroy( metafilter_t *mf )
+{
+ if ( mf->mf_next )
+ asyncmeta_filter_destroy( mf->mf_next );
+ asyncmeta_filter_free( mf );
+}
+
+static struct berval st_styles[] = {
+ BER_BVC("subtree"),
+ BER_BVC("children"),
+ BER_BVC("regex")
+};
+
+static int
+asyncmeta_subtree_unparse(
+ ConfigArgs *c,
+ a_metatarget_t *mt )
+{
+ a_metasubtree_t *ms;
+ struct berval bv, *style;
+
+ if ( !mt->mt_subtree )
+ return 1;
+
+ /* can only be one of exclude or include */
+ if (( c->type == LDAP_BACK_CFG_SUBTREE_EX ) ^ mt->mt_subtree_exclude )
+ return 1;
+
+ bv.bv_val = c->cr_msg;
+ for ( ms=mt->mt_subtree; ms; ms=ms->ms_next ) {
+ if (ms->ms_type == META_ST_SUBTREE)
+ style = &st_styles[0];
+ else if ( ms->ms_type == META_ST_SUBORDINATE )
+ style = &st_styles[1];
+ else if ( ms->ms_type == META_ST_REGEX )
+ style = &st_styles[2];
+ else {
+ assert(0);
+ continue;
+ }
+ bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "dn.%s:%s", style->bv_val, ms->ms_dn.bv_val );
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ return 0;
+}
+
+static int
+asyncmeta_subtree_config(
+ a_metatarget_t *mt,
+ ConfigArgs *c )
+{
+ meta_st_t type = META_ST_SUBTREE;
+ char *pattern;
+ struct berval ndn = BER_BVNULL;
+ a_metasubtree_t *ms = NULL;
+
+ if ( c->type == LDAP_BACK_CFG_SUBTREE_EX ) {
+ if ( mt->mt_subtree && !mt->mt_subtree_exclude ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "\"subtree-exclude\" incompatible with previous \"subtree-include\" directives" );
+ return 1;
+ }
+
+ mt->mt_subtree_exclude = 1;
+
+ } else {
+ if ( mt->mt_subtree && mt->mt_subtree_exclude ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "\"subtree-include\" incompatible with previous \"subtree-exclude\" directives" );
+ return 1;
+ }
+ }
+
+ pattern = c->argv[1];
+ if ( strncasecmp( pattern, "dn", STRLENOF( "dn" ) ) == 0 ) {
+ char *style;
+
+ pattern = &pattern[STRLENOF( "dn")];
+
+ if ( pattern[0] == '.' ) {
+ style = &pattern[1];
+
+ if ( strncasecmp( style, "subtree", STRLENOF( "subtree" ) ) == 0 ) {
+ type = META_ST_SUBTREE;
+ pattern = &style[STRLENOF( "subtree" )];
+
+ } else if ( strncasecmp( style, "children", STRLENOF( "children" ) ) == 0 ) {
+ type = META_ST_SUBORDINATE;
+ pattern = &style[STRLENOF( "children" )];
+
+ } else if ( strncasecmp( style, "sub", STRLENOF( "sub" ) ) == 0 ) {
+ type = META_ST_SUBTREE;
+ pattern = &style[STRLENOF( "sub" )];
+
+ } else if ( strncasecmp( style, "regex", STRLENOF( "regex" ) ) == 0 ) {
+ type = META_ST_REGEX;
+ pattern = &style[STRLENOF( "regex" )];
+
+ } else {
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "unknown style in \"dn.<style>\"" );
+ return 1;
+ }
+ }
+
+ if ( pattern[0] != ':' ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "missing colon after \"dn.<style>\"" );
+ return 1;
+ }
+ pattern++;
+ }
+
+ switch ( type ) {
+ case META_ST_SUBTREE:
+ case META_ST_SUBORDINATE: {
+ struct berval dn;
+
+ ber_str2bv( pattern, 0, 0, &dn );
+ if ( dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL )
+ != LDAP_SUCCESS )
+ {
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "DN=\"%s\" is invalid", pattern );
+ return 1;
+ }
+
+ if ( !dnIsSuffix( &ndn, &mt->mt_nsuffix ) ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "DN=\"%s\" is not a subtree of target \"%s\"",
+ pattern, mt->mt_nsuffix.bv_val );
+ ber_memfree( ndn.bv_val );
+ return( 1 );
+ }
+ } break;
+
+ default:
+ /* silence warnings */
+ break;
+ }
+
+ ms = ch_calloc( sizeof( a_metasubtree_t ), 1 );
+ ms->ms_type = type;
+
+ switch ( ms->ms_type ) {
+ case META_ST_SUBTREE:
+ case META_ST_SUBORDINATE:
+ ms->ms_dn = ndn;
+ break;
+
+ case META_ST_REGEX: {
+ int rc;
+
+ rc = regcomp( &ms->ms_regex, pattern, REG_EXTENDED|REG_ICASE );
+ if ( rc != 0 ) {
+ char regerr[ SLAP_TEXT_BUFLEN ];
+
+ regerror( rc, &ms->ms_regex, regerr, sizeof(regerr) );
+
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "regular expression \"%s\" bad because of %s",
+ pattern, regerr );
+ ch_free( ms );
+ return 1;
+ }
+ ber_str2bv( pattern, 0, 1, &ms->ms_regex_pattern );
+ } break;
+ }
+
+ if ( mt->mt_subtree == NULL ) {
+ mt->mt_subtree = ms;
+
+ } else {
+ a_metasubtree_t **msp;
+
+ for ( msp = &mt->mt_subtree; *msp; ) {
+ switch ( ms->ms_type ) {
+ case META_ST_SUBTREE:
+ switch ( (*msp)->ms_type ) {
+ case META_ST_SUBTREE:
+ if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) {
+ a_metasubtree_t *tmp = *msp;
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.subtree:%s\" is contained in rule \"dn.subtree:%s\" (replaced)\n",
+ c->log, pattern, (*msp)->ms_dn.bv_val );
+ *msp = (*msp)->ms_next;
+ tmp->ms_next = NULL;
+ asyncmeta_subtree_destroy( tmp );
+ continue;
+
+ } else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) ) {
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.subtree:%s\" contains rule \"dn.subtree:%s\" (ignored)\n",
+ c->log, (*msp)->ms_dn.bv_val, pattern );
+ asyncmeta_subtree_destroy( ms );
+ ms = NULL;
+ return( 0 );
+ }
+ break;
+
+ case META_ST_SUBORDINATE:
+ if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) {
+ a_metasubtree_t *tmp = *msp;
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.children:%s\" is contained in rule \"dn.subtree:%s\" (replaced)\n",
+ c->log, pattern, (*msp)->ms_dn.bv_val );
+ *msp = (*msp)->ms_next;
+ tmp->ms_next = NULL;
+ asyncmeta_subtree_destroy( tmp );
+ continue;
+
+ } else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) && ms->ms_dn.bv_len > (*msp)->ms_dn.bv_len ) {
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.children:%s\" contains rule \"dn.subtree:%s\" (ignored)\n",
+ c->log, (*msp)->ms_dn.bv_val, pattern );
+ asyncmeta_subtree_destroy( ms );
+ ms = NULL;
+ return( 0 );
+ }
+ break;
+
+ case META_ST_REGEX:
+ if ( regexec( &(*msp)->ms_regex, ms->ms_dn.bv_val, 0, NULL, 0 ) == 0 ) {
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.regex:%s\" may contain rule \"dn.subtree:%s\"\n",
+ c->log, (*msp)->ms_regex_pattern.bv_val, ms->ms_dn.bv_val );
+ }
+ break;
+ }
+ break;
+
+ case META_ST_SUBORDINATE:
+ switch ( (*msp)->ms_type ) {
+ case META_ST_SUBTREE:
+ if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) {
+ a_metasubtree_t *tmp = *msp;
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.children:%s\" is contained in rule \"dn.subtree:%s\" (replaced)\n",
+ c->log, pattern, (*msp)->ms_dn.bv_val );
+ *msp = (*msp)->ms_next;
+ tmp->ms_next = NULL;
+ asyncmeta_subtree_destroy( tmp );
+ continue;
+
+ } else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) && ms->ms_dn.bv_len > (*msp)->ms_dn.bv_len ) {
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.children:%s\" contains rule \"dn.subtree:%s\" (ignored)\n",
+ c->log, (*msp)->ms_dn.bv_val, pattern );
+ asyncmeta_subtree_destroy( ms );
+ ms = NULL;
+ return( 0 );
+ }
+ break;
+
+ case META_ST_SUBORDINATE:
+ if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) {
+ a_metasubtree_t *tmp = *msp;
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.children:%s\" is contained in rule \"dn.children:%s\" (replaced)\n",
+ c->log, pattern, (*msp)->ms_dn.bv_val );
+ *msp = (*msp)->ms_next;
+ tmp->ms_next = NULL;
+ asyncmeta_subtree_destroy( tmp );
+ continue;
+
+ } else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) ) {
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.children:%s\" contains rule \"dn.children:%s\" (ignored)\n",
+ c->log, (*msp)->ms_dn.bv_val, pattern );
+ asyncmeta_subtree_destroy( ms );
+ ms = NULL;
+ return( 0 );
+ }
+ break;
+
+ case META_ST_REGEX:
+ if ( regexec( &(*msp)->ms_regex, ms->ms_dn.bv_val, 0, NULL, 0 ) == 0 ) {
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.regex:%s\" may contain rule \"dn.subtree:%s\"\n",
+ c->log, (*msp)->ms_regex_pattern.bv_val, ms->ms_dn.bv_val );
+ }
+ break;
+ }
+ break;
+
+ case META_ST_REGEX:
+ switch ( (*msp)->ms_type ) {
+ case META_ST_SUBTREE:
+ case META_ST_SUBORDINATE:
+ if ( regexec( &ms->ms_regex, (*msp)->ms_dn.bv_val, 0, NULL, 0 ) == 0 ) {
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.subtree:%s\" may be contained in rule \"dn.regex:%s\"\n",
+ c->log, (*msp)->ms_dn.bv_val, ms->ms_regex_pattern.bv_val );
+ }
+ break;
+
+ case META_ST_REGEX:
+ /* no check possible */
+ break;
+ }
+ break;
+ }
+
+ msp = &(*msp)->ms_next;
+ }
+
+ *msp = ms;
+ }
+
+ return 0;
+}
+
+static slap_verbmasks idassert_mode[] = {
+ { BER_BVC("self"), LDAP_BACK_IDASSERT_SELF },
+ { BER_BVC("anonymous"), LDAP_BACK_IDASSERT_ANONYMOUS },
+ { BER_BVC("none"), LDAP_BACK_IDASSERT_NOASSERT },
+ { BER_BVC("legacy"), LDAP_BACK_IDASSERT_LEGACY },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks tls_mode[] = {
+ { BER_BVC( "propagate" ), LDAP_BACK_F_TLS_PROPAGATE_MASK },
+ { BER_BVC( "try-propagate" ), LDAP_BACK_F_PROPAGATE_TLS },
+ { BER_BVC( "start" ), LDAP_BACK_F_TLS_USE_MASK },
+ { BER_BVC( "try-start" ), LDAP_BACK_F_USE_TLS },
+ { BER_BVC( "ldaps" ), LDAP_BACK_F_TLS_LDAPS },
+ { BER_BVC( "none" ), LDAP_BACK_F_NONE },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks t_f_mode[] = {
+ { BER_BVC( "yes" ), LDAP_BACK_F_T_F },
+ { BER_BVC( "discover" ), LDAP_BACK_F_T_F_DISCOVER },
+ { BER_BVC( "no" ), LDAP_BACK_F_NONE },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks cancel_mode[] = {
+ { BER_BVC( "ignore" ), LDAP_BACK_F_CANCEL_IGNORE },
+ { BER_BVC( "exop" ), LDAP_BACK_F_CANCEL_EXOP },
+ { BER_BVC( "exop-discover" ), LDAP_BACK_F_CANCEL_EXOP_DISCOVER },
+ { BER_BVC( "abandon" ), LDAP_BACK_F_CANCEL_ABANDON },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks onerr_mode[] = {
+ { BER_BVC( "stop" ), META_BACK_F_ONERR_STOP },
+ { BER_BVC( "report" ), META_BACK_F_ONERR_REPORT },
+ { BER_BVC( "continue" ), LDAP_BACK_F_NONE },
+ { BER_BVNULL, 0 }
+};
+
+/* see enum in slap.h */
+static slap_cf_aux_table timeout_table[] = {
+ { BER_BVC("bind="), SLAP_OP_BIND * sizeof( time_t ), 'u', 0, NULL },
+ /* unbind makes no sense */
+ { BER_BVC("add="), SLAP_OP_ADD * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("delete="), SLAP_OP_DELETE * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("modrdn="), SLAP_OP_MODRDN * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("modify="), SLAP_OP_MODIFY * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("compare="), SLAP_OP_COMPARE * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("search="), SLAP_OP_SEARCH * sizeof( time_t ), 'u', 0, NULL },
+ /* abandon makes little sense */
+#if 0 /* not implemented yet */
+ { BER_BVC("extended="), SLAP_OP_EXTENDED * sizeof( time_t ), 'u', 0, NULL },
+#endif
+ { BER_BVNULL, 0, 0, 0, NULL }
+};
+
+static int
+asyncmeta_cf_cleanup( ConfigArgs *c )
+{
+ a_metainfo_t *mi = ( a_metainfo_t * )c->be->be_private;
+ a_metatarget_t *mt = c->ca_private;
+
+ return asyncmeta_target_finish( mi, mt, c->log, c->cr_msg, sizeof( c->cr_msg ));
+}
+
+static int
+asyncmeta_back_cf_gen( ConfigArgs *c )
+{
+ a_metainfo_t *mi = ( a_metainfo_t * )c->be->be_private;
+ a_metatarget_t *mt = NULL;
+ a_metacommon_t *mc = NULL;
+
+ int i, rc = 0;
+
+ assert( mi != NULL );
+
+ if ( c->op == SLAP_CONFIG_EMIT || c->op == LDAP_MOD_DELETE ) {
+ if ( !mi )
+ return 1;
+
+ if ( c->table == Cft_Database ) {
+ mt = NULL;
+ mc = &mi->mi_mc;
+ } else {
+ mt = c->ca_private;
+ mc = &mt->mt_mc;
+ }
+ }
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ struct berval bv = BER_BVNULL;
+
+ switch( c->type ) {
+ /* Base attrs */
+
+ case LDAP_BACK_CFG_DNCACHE_TTL:
+ if ( mi->mi_cache.ttl == META_DNCACHE_DISABLED ) {
+ return 1;
+ } else if ( mi->mi_cache.ttl == META_DNCACHE_FOREVER ) {
+ BER_BVSTR( &bv, "forever" );
+ } else {
+ char buf[ SLAP_TEXT_BUFLEN ];
+
+ lutil_unparse_time( buf, sizeof( buf ), mi->mi_cache.ttl );
+ ber_str2bv( buf, 0, 0, &bv );
+ }
+ value_add_one( &c->rvalue_vals, &bv );
+ break;
+
+ case LDAP_BACK_CFG_IDLE_TIMEOUT:
+ if ( mi->mi_idle_timeout == 0 ) {
+ return 1;
+ } else {
+ char buf[ SLAP_TEXT_BUFLEN ];
+
+ lutil_unparse_time( buf, sizeof( buf ), mi->mi_idle_timeout );
+ ber_str2bv( buf, 0, 0, &bv );
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
+
+ case LDAP_BACK_CFG_ONERR:
+ enum_to_verb( onerr_mode, mi->mi_flags & META_BACK_F_ONERR_MASK, &bv );
+ if ( BER_BVISNULL( &bv )) {
+ rc = 1;
+ } else {
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
+
+ case LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER:
+ c->value_int = META_BACK_DEFER_ROOTDN_BIND( mi );
+ break;
+
+ case LDAP_BACK_CFG_CONNPOOLMAX:
+ c->value_int = mi->mi_conn_priv_max;
+ break;
+
+ /* common attrs */
+ case LDAP_BACK_CFG_BIND_TIMEOUT:
+ if ( mc->mc_bind_timeout.tv_sec == 0 &&
+ mc->mc_bind_timeout.tv_usec == 0 ) {
+ return 1;
+ } else {
+ c->value_ulong = mc->mc_bind_timeout.tv_sec * 1000000UL +
+ mc->mc_bind_timeout.tv_usec;
+ }
+ break;
+
+ case LDAP_BACK_CFG_CANCEL: {
+ slap_mask_t mask = LDAP_BACK_F_CANCEL_MASK2;
+
+ if ( mt && META_BACK_TGT_CANCEL_DISCOVER( mt ) ) {
+ mask &= ~LDAP_BACK_F_CANCEL_EXOP;
+ }
+ enum_to_verb( cancel_mode, (mc->mc_flags & mask), &bv );
+ if ( BER_BVISNULL( &bv ) ) {
+ /* there's something wrong... */
+ assert( 0 );
+ rc = 1;
+
+ } else {
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ } break;
+
+ case LDAP_BACK_CFG_CHASE:
+ c->value_int = META_BACK_CMN_CHASE_REFERRALS(mc);
+ break;
+
+#ifdef SLAPD_META_CLIENT_PR
+ case LDAP_BACK_CFG_CLIENT_PR:
+ if ( mc->mc_ps == META_CLIENT_PR_DISABLE ) {
+ return 1;
+ } else if ( mc->mc_ps == META_CLIENT_PR_ACCEPT_UNSOLICITED ) {
+ BER_BVSTR( &bv, "accept-unsolicited" );
+ } else {
+ bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%d", mc->mc_ps );
+ bv.bv_val = c->cr_msg;
+ }
+ value_add_one( &c->rvalue_vals, &bv );
+ break;
+#endif /* SLAPD_META_CLIENT_PR */
+
+ case LDAP_BACK_CFG_DEFAULT_T:
+ if ( mt || mi->mi_defaulttarget == META_DEFAULT_TARGET_NONE )
+ return 1;
+ bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%d", mi->mi_defaulttarget );
+ bv.bv_val = c->cr_msg;
+ value_add_one( &c->rvalue_vals, &bv );
+ break;
+
+ case LDAP_BACK_CFG_NETWORK_TIMEOUT:
+ if ( mc->mc_network_timeout == 0 ) {
+ return 1;
+ }
+ bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%ld",
+ mc->mc_network_timeout );
+ bv.bv_val = c->cr_msg;
+ value_add_one( &c->rvalue_vals, &bv );
+ break;
+
+ case LDAP_BACK_CFG_NOREFS:
+ c->value_int = META_BACK_CMN_NOREFS(mc);
+ break;
+
+ case LDAP_BACK_CFG_NOUNDEFFILTER:
+ c->value_int = META_BACK_CMN_NOUNDEFFILTER(mc);
+ break;
+
+ case LDAP_BACK_CFG_NRETRIES:
+ if ( mc->mc_nretries == META_RETRY_FOREVER ) {
+ BER_BVSTR( &bv, "forever" );
+ } else if ( mc->mc_nretries == META_RETRY_NEVER ) {
+ BER_BVSTR( &bv, "never" );
+ } else {
+ bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%d",
+ mc->mc_nretries );
+ bv.bv_val = c->cr_msg;
+ }
+ value_add_one( &c->rvalue_vals, &bv );
+ break;
+
+ case LDAP_BACK_CFG_QUARANTINE:
+ if ( !META_BACK_CMN_QUARANTINE( mc )) {
+ rc = 1;
+ break;
+ }
+ rc = mi->mi_ldap_extra->retry_info_unparse( &mc->mc_quarantine, &bv );
+ if ( rc == 0 ) {
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ }
+ break;
+
+ case LDAP_BACK_CFG_REBIND:
+ c->value_int = META_BACK_CMN_SAVECRED(mc);
+ break;
+
+ case LDAP_BACK_CFG_TIMEOUT:
+ for ( i = 0; i < SLAP_OP_LAST; i++ ) {
+ if ( mc->mc_timeout[ i ] != META_BACK_CFG_DEFAULT_OPS_TIMEOUT ) {
+ break;
+ }
+ }
+
+ if ( i == SLAP_OP_LAST ) {
+ return 1;
+ }
+
+ BER_BVZERO( &bv );
+ slap_cf_aux_table_unparse( mc->mc_timeout, &bv, timeout_table );
+
+ if ( BER_BVISNULL( &bv ) ) {
+ return 1;
+ }
+
+ for ( i = 0; isspace( (unsigned char) bv.bv_val[ i ] ); i++ )
+ /* count spaces */ ;
+
+ if ( i ) {
+ bv.bv_len -= i;
+ AC_MEMCPY( bv.bv_val, &bv.bv_val[ i ],
+ bv.bv_len + 1 );
+ }
+
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ break;
+
+ case LDAP_BACK_CFG_VERSION:
+ if ( mc->mc_version == 0 )
+ return 1;
+ c->value_int = mc->mc_version;
+ break;
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ case LDAP_BACK_CFG_ST_REQUEST:
+ c->value_int = META_BACK_CMN_ST_REQUEST( mc );
+ break;
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+ case LDAP_BACK_CFG_T_F:
+ enum_to_verb( t_f_mode, (mc->mc_flags & LDAP_BACK_F_T_F_MASK2), &bv );
+ if ( BER_BVISNULL( &bv ) ) {
+ /* there's something wrong... */
+ assert( 0 );
+ rc = 1;
+
+ } else {
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
+
+ case LDAP_BACK_CFG_TLS: {
+ struct berval bc = BER_BVNULL, bv2;
+
+ if (( mc->mc_flags & LDAP_BACK_F_TLS_MASK ) == LDAP_BACK_F_NONE ) {
+ rc = 1;
+ break;
+ }
+ enum_to_verb( tls_mode, ( mc->mc_flags & LDAP_BACK_F_TLS_MASK ), &bv );
+ assert( !BER_BVISNULL( &bv ) );
+
+ if ( mt ) {
+ bindconf_tls_unparse( &mt->mt_tls, &bc );
+ }
+
+ if ( !BER_BVISEMPTY( &bc )) {
+ bv2.bv_len = bv.bv_len + bc.bv_len + 1;
+ bv2.bv_val = ch_malloc( bv2.bv_len + 1 );
+ strcpy( bv2.bv_val, bv.bv_val );
+ bv2.bv_val[bv.bv_len] = ' ';
+ strcpy( &bv2.bv_val[bv.bv_len + 1], bc.bv_val );
+ ber_memfree( bc.bv_val );
+ ber_bvarray_add( &c->rvalue_vals, &bv2 );
+ } else {
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ } break;
+
+ /* target attrs */
+ case LDAP_BACK_CFG_URI: {
+ char *p2, *p1 = strchr( mt->mt_uri, ' ' );
+ bv.bv_len = strlen( mt->mt_uri ) + 3 + mt->mt_psuffix.bv_len;
+ bv.bv_val = ch_malloc( bv.bv_len + 1 );
+ p2 = bv.bv_val;
+ *p2++ = '"';
+ if ( p1 ) {
+ p2 = lutil_strncopy( p2, mt->mt_uri, p1 - mt->mt_uri );
+ } else {
+ p2 = lutil_strcopy( p2, mt->mt_uri );
+ }
+ *p2++ = '/';
+ p2 = lutil_strcopy( p2, mt->mt_psuffix.bv_val );
+ *p2++ = '"';
+ if ( p1 ) {
+ strcpy( p2, p1 );
+ }
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ } break;
+
+ case LDAP_BACK_CFG_IDASSERT_AUTHZFROM: {
+ BerVarray *bvp;
+ int i;
+ struct berval bv = BER_BVNULL;
+ char buf[SLAP_TEXT_BUFLEN];
+
+ bvp = &mt->mt_idassert_authz;
+ if ( *bvp == NULL ) {
+ if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL )
+ {
+ BER_BVSTR( &bv, "*" );
+ value_add_one( &c->rvalue_vals, &bv );
+
+ } else {
+ rc = 1;
+ }
+ break;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &((*bvp)[ i ]) ); i++ ) {
+ char *ptr;
+ int len = snprintf( buf, sizeof( buf ), SLAP_X_ORDERED_FMT, i );
+ bv.bv_len = ((*bvp)[ i ]).bv_len + len;
+ bv.bv_val = ber_memrealloc( bv.bv_val, bv.bv_len + 1 );
+ ptr = bv.bv_val;
+ ptr = lutil_strcopy( ptr, buf );
+ ptr = lutil_strncopy( ptr, ((*bvp)[ i ]).bv_val, ((*bvp)[ i ]).bv_len );
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ if ( bv.bv_val ) {
+ ber_memfree( bv.bv_val );
+ }
+ break;
+ }
+
+ case LDAP_BACK_CFG_IDASSERT_BIND: {
+ int i;
+ struct berval bc = BER_BVNULL;
+ char *ptr;
+
+ if ( mt->mt_idassert_authmethod == LDAP_AUTH_NONE ) {
+ return 1;
+ } else {
+ ber_len_t len;
+
+ switch ( mt->mt_idassert_mode ) {
+ case LDAP_BACK_IDASSERT_OTHERID:
+ case LDAP_BACK_IDASSERT_OTHERDN:
+ break;
+
+ default: {
+ struct berval mode = BER_BVNULL;
+
+ enum_to_verb( idassert_mode, mt->mt_idassert_mode, &mode );
+ if ( BER_BVISNULL( &mode ) ) {
+ /* there's something wrong... */
+ assert( 0 );
+ rc = 1;
+
+ } else {
+ bv.bv_len = STRLENOF( "mode=" ) + mode.bv_len;
+ bv.bv_val = ch_malloc( bv.bv_len + 1 );
+
+ ptr = lutil_strcopy( bv.bv_val, "mode=" );
+ ptr = lutil_strcopy( ptr, mode.bv_val );
+ }
+ break;
+ }
+ }
+
+ if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) {
+ len = bv.bv_len + STRLENOF( "authz=native" );
+
+ if ( !BER_BVISEMPTY( &bv ) ) {
+ len += STRLENOF( " " );
+ }
+
+ bv.bv_val = ch_realloc( bv.bv_val, len + 1 );
+
+ ptr = &bv.bv_val[ bv.bv_len ];
+
+ if ( !BER_BVISEMPTY( &bv ) ) {
+ ptr = lutil_strcopy( ptr, " " );
+ }
+
+ (void)lutil_strcopy( ptr, "authz=native" );
+ }
+
+ len = bv.bv_len + STRLENOF( "flags=non-prescriptive,override,obsolete-encoding-workaround,proxy-authz-non-critical,dn-authzid" );
+ /* flags */
+ if ( !BER_BVISEMPTY( &bv ) ) {
+ len += STRLENOF( " " );
+ }
+
+ bv.bv_val = ch_realloc( bv.bv_val, len + 1 );
+
+ ptr = &bv.bv_val[ bv.bv_len ];
+
+ if ( !BER_BVISEMPTY( &bv ) ) {
+ ptr = lutil_strcopy( ptr, " " );
+ }
+
+ ptr = lutil_strcopy( ptr, "flags=" );
+
+ if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
+ ptr = lutil_strcopy( ptr, "prescriptive" );
+ } else {
+ ptr = lutil_strcopy( ptr, "non-prescriptive" );
+ }
+
+ if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
+ ptr = lutil_strcopy( ptr, ",override" );
+ }
+
+ if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) {
+ ptr = lutil_strcopy( ptr, ",obsolete-proxy-authz" );
+
+ } else if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) {
+ ptr = lutil_strcopy( ptr, ",obsolete-encoding-workaround" );
+ }
+
+ if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL ) {
+ ptr = lutil_strcopy( ptr, ",proxy-authz-critical" );
+
+ } else {
+ ptr = lutil_strcopy( ptr, ",proxy-authz-non-critical" );
+ }
+
+#ifdef SLAP_AUTH_DN
+ switch ( mt->mt_idassert_flags & LDAP_BACK_AUTH_DN_MASK ) {
+ case LDAP_BACK_AUTH_DN_AUTHZID:
+ ptr = lutil_strcopy( ptr, ",dn-authzid" );
+ break;
+
+ case LDAP_BACK_AUTH_DN_WHOAMI:
+ ptr = lutil_strcopy( ptr, ",dn-whoami" );
+ break;
+
+ default:
+#if 0 /* implicit */
+ ptr = lutil_strcopy( ptr, ",dn-none" );
+#endif
+ break;
+ }
+#endif
+
+ bv.bv_len = ( ptr - bv.bv_val );
+ /* end-of-flags */
+ }
+
+ bindconf_unparse( &mt->mt_idassert.si_bc, &bc );
+
+ if ( !BER_BVISNULL( &bv ) ) {
+ ber_len_t len = bv.bv_len + bc.bv_len;
+
+ bv.bv_val = ch_realloc( bv.bv_val, len + 1 );
+
+ assert( bc.bv_val[ 0 ] == ' ' );
+
+ ptr = lutil_strcopy( &bv.bv_val[ bv.bv_len ], bc.bv_val );
+ free( bc.bv_val );
+ bv.bv_len = ptr - bv.bv_val;
+
+ } else {
+ for ( i = 0; isspace( (unsigned char) bc.bv_val[ i ] ); i++ )
+ /* count spaces */ ;
+
+ if ( i ) {
+ bc.bv_len -= i;
+ AC_MEMCPY( bc.bv_val, &bc.bv_val[ i ], bc.bv_len + 1 );
+ }
+
+ bv = bc;
+ }
+
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+
+ break;
+ }
+
+ case LDAP_BACK_CFG_SUFFIXM:
+ if ( mt->mt_lsuffixm.bv_val ) {
+ struct berval bv;
+ char *ptr;
+ bv.bv_len = mt->mt_lsuffixm.bv_len + 2 + 1 + mt->mt_rsuffixm.bv_len + 2;
+ bv.bv_val = ch_malloc( bv.bv_len + 1 );
+ ptr = bv.bv_val;
+ *ptr++ = '"';
+ ptr = lutil_strcopy(ptr, mt->mt_lsuffixm.bv_val);
+ ptr = lutil_strcopy(ptr, "\" \"");
+ ptr = lutil_strcopy(ptr, mt->mt_rsuffixm.bv_val);
+ *ptr++ = '"';
+ *ptr = '\0';
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ rc = 0;
+ } else
+ rc = 1;
+ break;
+
+ case LDAP_BACK_CFG_SUBTREE_EX:
+ case LDAP_BACK_CFG_SUBTREE_IN:
+ rc = asyncmeta_subtree_unparse( c, mt );
+ break;
+
+ case LDAP_BACK_CFG_FILTER:
+ if ( mt->mt_filter == NULL ) {
+ rc = 1;
+ } else {
+ metafilter_t *mf;
+ for ( mf = mt->mt_filter; mf; mf = mf->mf_next )
+ value_add_one( &c->rvalue_vals, &mf->mf_regex_pattern );
+ }
+ break;
+ case LDAP_BACK_CFG_MAX_PENDING_OPS:
+ c->value_int = mi->mi_max_pending_ops;
+ break;
+
+ case LDAP_BACK_CFG_MAX_TARGET_CONNS:
+ c->value_int = mi->mi_max_target_conns;
+ break;
+ case LDAP_BACK_CFG_MAX_TIMEOUT_OPS:
+ c->value_int = mi->mi_max_timeout_ops;
+ break;
+
+ case LDAP_BACK_CFG_KEEPALIVE: {
+ struct berval bv;
+ char buf[AC_LINE_MAX];
+ bv.bv_len = AC_LINE_MAX;
+ bv.bv_val = &buf[0];
+ slap_keepalive_parse(&bv, &mt->mt_tls.sb_keepalive, 0, 0, 1);
+ value_add_one( &c->rvalue_vals, &bv );
+ break;
+ }
+
+ case LDAP_BACK_CFG_TCP_USER_TIMEOUT:
+ c->value_uint = mt->mt_tls.sb_tcp_user_timeout;
+ break;
+
+ default:
+ rc = 1;
+ }
+ return rc;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ switch( c->type ) {
+ /* Base attrs */
+ case LDAP_BACK_CFG_DNCACHE_TTL:
+ mi->mi_cache.ttl = META_DNCACHE_DISABLED;
+ break;
+
+ case LDAP_BACK_CFG_IDLE_TIMEOUT:
+ mi->mi_idle_timeout = 0;
+ break;
+
+ case LDAP_BACK_CFG_ONERR:
+ mi->mi_flags &= ~META_BACK_F_ONERR_MASK;
+ break;
+
+ case LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER:
+ mi->mi_flags &= ~META_BACK_F_DEFER_ROOTDN_BIND;
+ break;
+
+ case LDAP_BACK_CFG_CONNPOOLMAX:
+ mi->mi_conn_priv_max = LDAP_BACK_CONN_PRIV_MIN;
+ break;
+
+ /* common attrs */
+ case LDAP_BACK_CFG_BIND_TIMEOUT:
+ mc->mc_bind_timeout.tv_sec = 0;
+ mc->mc_bind_timeout.tv_usec = 0;
+ break;
+
+ case LDAP_BACK_CFG_CANCEL:
+ mc->mc_flags &= ~LDAP_BACK_F_CANCEL_MASK2;
+ break;
+
+ case LDAP_BACK_CFG_CHASE:
+ mc->mc_flags &= ~LDAP_BACK_F_CHASE_REFERRALS;
+ break;
+
+#ifdef SLAPD_META_CLIENT_PR
+ case LDAP_BACK_CFG_CLIENT_PR:
+ mc->mc_ps = META_CLIENT_PR_DISABLE;
+ break;
+#endif /* SLAPD_META_CLIENT_PR */
+
+ case LDAP_BACK_CFG_DEFAULT_T:
+ mi->mi_defaulttarget = META_DEFAULT_TARGET_NONE;
+ break;
+
+ case LDAP_BACK_CFG_NETWORK_TIMEOUT:
+ mc->mc_network_timeout = 0;
+ break;
+
+ case LDAP_BACK_CFG_NOREFS:
+ mc->mc_flags &= ~LDAP_BACK_F_NOREFS;
+ break;
+
+ case LDAP_BACK_CFG_NOUNDEFFILTER:
+ mc->mc_flags &= ~LDAP_BACK_F_NOUNDEFFILTER;
+ break;
+
+ case LDAP_BACK_CFG_NRETRIES:
+ mc->mc_nretries = META_RETRY_DEFAULT;
+ break;
+
+ case LDAP_BACK_CFG_QUARANTINE:
+ if ( META_BACK_CMN_QUARANTINE( mc )) {
+ mi->mi_ldap_extra->retry_info_destroy( &mc->mc_quarantine );
+ mc->mc_flags &= ~LDAP_BACK_F_QUARANTINE;
+ if ( mc == &mt->mt_mc ) {
+ ldap_pvt_thread_mutex_destroy( &mt->mt_quarantine_mutex );
+ mt->mt_isquarantined = 0;
+ }
+ }
+ break;
+
+ case LDAP_BACK_CFG_REBIND:
+ mc->mc_flags &= ~LDAP_BACK_F_SAVECRED;
+ break;
+
+ case LDAP_BACK_CFG_TIMEOUT:
+ for ( i = 0; i < SLAP_OP_LAST; i++ ) {
+ mc->mc_timeout[ i ] = 0;
+ }
+ break;
+
+ case LDAP_BACK_CFG_VERSION:
+ mc->mc_version = 0;
+ break;
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ case LDAP_BACK_CFG_ST_REQUEST:
+ mc->mc_flags &= ~LDAP_BACK_F_ST_REQUEST;
+ break;
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+ case LDAP_BACK_CFG_T_F:
+ mc->mc_flags &= ~LDAP_BACK_F_T_F_MASK2;
+ break;
+
+ case LDAP_BACK_CFG_TLS:
+ mc->mc_flags &= ~LDAP_BACK_F_TLS_MASK;
+ if ( mt )
+ bindconf_free( &mt->mt_tls );
+ break;
+
+ /* target attrs */
+ case LDAP_BACK_CFG_URI:
+ if ( mt->mt_uri ) {
+ ch_free( mt->mt_uri );
+ mt->mt_uri = NULL;
+ }
+ /* FIXME: should have a way to close all cached
+ * connections associated with this target.
+ */
+ break;
+
+ case LDAP_BACK_CFG_IDASSERT_AUTHZFROM: {
+ BerVarray *bvp;
+
+ bvp = &mt->mt_idassert_authz;
+ if ( c->valx < 0 ) {
+ if ( *bvp != NULL ) {
+ ber_bvarray_free( *bvp );
+ *bvp = NULL;
+ }
+
+ } else {
+ if ( *bvp == NULL ) {
+ rc = 1;
+ break;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &((*bvp)[ i ]) ); i++ )
+ ;
+
+ if ( i >= c->valx ) {
+ rc = 1;
+ break;
+ }
+ ber_memfree( ((*bvp)[ c->valx ]).bv_val );
+ for ( i = c->valx; !BER_BVISNULL( &((*bvp)[ i + 1 ]) ); i++ ) {
+ (*bvp)[ i ] = (*bvp)[ i + 1 ];
+ }
+ BER_BVZERO( &((*bvp)[ i ]) );
+ }
+ } break;
+
+ case LDAP_BACK_CFG_IDASSERT_BIND:
+ bindconf_free( &mt->mt_idassert.si_bc );
+ memset( &mt->mt_idassert, 0, sizeof( slap_idassert_t ) );
+ break;
+
+ case LDAP_BACK_CFG_SUFFIXM:
+ if ( mt->mt_lsuffixm.bv_val ) {
+ ch_free( mt->mt_lsuffixm.bv_val );
+ ch_free( mt->mt_rsuffixm.bv_val );
+ BER_BVZERO( &mt->mt_lsuffixm );
+ BER_BVZERO( &mt->mt_rsuffixm );
+ }
+ break;
+
+ case LDAP_BACK_CFG_SUBTREE_EX:
+ case LDAP_BACK_CFG_SUBTREE_IN:
+ /* can only be one of exclude or include */
+ if (( c->type == LDAP_BACK_CFG_SUBTREE_EX ) ^ mt->mt_subtree_exclude ) {
+ rc = 1;
+ break;
+ }
+ if ( c->valx < 0 ) {
+ asyncmeta_subtree_destroy( mt->mt_subtree );
+ mt->mt_subtree = NULL;
+ } else {
+ a_metasubtree_t *ms, **mprev;
+ for (i=0, mprev = &mt->mt_subtree, ms = *mprev; ms; ms = *mprev) {
+ if ( i == c->valx ) {
+ *mprev = ms->ms_next;
+ asyncmeta_subtree_free( ms );
+ break;
+ }
+ i++;
+ mprev = &ms->ms_next;
+ }
+ if ( i != c->valx )
+ rc = 1;
+ }
+ break;
+
+ case LDAP_BACK_CFG_FILTER:
+ if ( c->valx < 0 ) {
+ asyncmeta_filter_destroy( mt->mt_filter );
+ mt->mt_filter = NULL;
+ } else {
+ metafilter_t *mf, **mprev;
+ for (i=0, mprev = &mt->mt_filter, mf = *mprev; mf; mf = *mprev) {
+ if ( i == c->valx ) {
+ *mprev = mf->mf_next;
+ asyncmeta_filter_free( mf );
+ break;
+ }
+ i++;
+ mprev = &mf->mf_next;
+ }
+ if ( i != c->valx )
+ rc = 1;
+ }
+ break;
+ case LDAP_BACK_CFG_MAX_PENDING_OPS:
+ mi->mi_max_pending_ops = 0;
+ break;
+
+ case LDAP_BACK_CFG_MAX_TARGET_CONNS:
+ mi->mi_max_target_conns = 0;
+ break;
+
+ case LDAP_BACK_CFG_MAX_TIMEOUT_OPS:
+ mi->mi_max_timeout_ops = 0;
+ break;
+
+ case LDAP_BACK_CFG_KEEPALIVE:
+ mt->mt_tls.sb_keepalive.sk_idle = 0;
+ mt->mt_tls.sb_keepalive.sk_probes = 0;
+ mt->mt_tls.sb_keepalive.sk_interval = 0;
+ break;
+
+ case LDAP_BACK_CFG_TCP_USER_TIMEOUT:
+ mt->mt_tls.sb_tcp_user_timeout = 0;
+ break;
+
+ default:
+ rc = 1;
+ break;
+ }
+
+ return rc;
+ }
+
+ if ( c->op == SLAP_CONFIG_ADD ) {
+ if ( c->type >= LDAP_BACK_CFG_LAST_BASE ) {
+ /* exclude CFG_URI from this check */
+ if ( c->type > LDAP_BACK_CFG_LAST_BOTH ) {
+ if ( !mi->mi_ntargets ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "need \"uri\" directive first" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ }
+ if ( mi->mi_ntargets ) {
+ mt = mi->mi_targets[ mi->mi_ntargets-1 ];
+ mc = &mt->mt_mc;
+ } else {
+ mt = NULL;
+ mc = &mi->mi_mc;
+ }
+ }
+ } else {
+ if ( c->table == Cft_Database ) {
+ mt = NULL;
+ mc = &mi->mi_mc;
+ } else {
+ mt = c->ca_private;
+ if ( mt )
+ mc = &mt->mt_mc;
+ else
+ mc = NULL;
+ }
+ }
+
+ switch( c->type ) {
+ case LDAP_BACK_CFG_URI: {
+ LDAPURLDesc *ludp;
+ struct berval dn;
+ int j;
+
+ char **uris = NULL;
+
+ if ( c->be->be_nsuffix == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "the suffix must be defined before any target" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ i = mi->mi_ntargets++;
+
+ mi->mi_targets = ( a_metatarget_t ** )ch_realloc( mi->mi_targets,
+ sizeof( a_metatarget_t * ) * mi->mi_ntargets );
+ if ( mi->mi_targets == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "out of memory while storing server name"
+ " in \"%s <protocol>://<server>[:port]/<naming context>\"",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( asyncmeta_back_new_target( &mi->mi_targets[ i ] ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to init server"
+ " in \"%s <protocol>://<server>[:port]/<naming context>\"",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ mt = mi->mi_targets[ i ];
+
+ mt->mt_rebind_f = mi->mi_rebind_f;
+ mt->mt_urllist_f = mi->mi_urllist_f;
+ mt->mt_urllist_p = mt;
+
+ if ( META_BACK_QUARANTINE( mi ) ) {
+ ldap_pvt_thread_mutex_init( &mt->mt_quarantine_mutex );
+ }
+ mt->mt_mc = mi->mi_mc;
+
+ for ( j = 1; j < c->argc; j++ ) {
+ char **tmpuris = ldap_str2charray( c->argv[ j ], "\t" );
+
+ if ( tmpuris == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse URIs #%d"
+ " in \"%s <protocol>://<server>[:port]/<naming context>\"",
+ j-1, c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( j == 1 ) {
+ uris = tmpuris;
+
+ } else {
+ ldap_charray_merge( &uris, tmpuris );
+ ldap_charray_free( tmpuris );
+ }
+ }
+
+ for ( j = 0; uris[ j ] != NULL; j++ ) {
+ char *tmpuri = NULL;
+
+ /*
+ * uri MUST be legal!
+ */
+ if ( ldap_url_parselist_ext( &ludp, uris[ j ], "\t",
+ LDAP_PVT_URL_PARSE_NONE ) != LDAP_SUCCESS
+ || ludp->lud_next != NULL )
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse URI #%d"
+ " in \"%s <protocol>://<server>[:port]/<naming context>\"",
+ j-1, c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ ldap_charray_free( uris );
+ return 1;
+ }
+
+ if ( j == 0 ) {
+
+ /*
+ * uri MUST have the <dn> part!
+ */
+ if ( ludp->lud_dn == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "missing <naming context> "
+ " in \"%s <protocol>://<server>[:port]/<naming context>\"",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ ldap_free_urllist( ludp );
+ ldap_charray_free( uris );
+ return 1;
+ }
+
+ /*
+ * copies and stores uri and suffix
+ */
+ ber_str2bv( ludp->lud_dn, 0, 0, &dn );
+ rc = dnPrettyNormal( NULL, &dn, &mt->mt_psuffix,
+ &mt->mt_nsuffix, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "target DN is invalid \"%s\"",
+ c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ ldap_free_urllist( ludp );
+ ldap_charray_free( uris );
+ return( 1 );
+ }
+
+ ludp->lud_dn[ 0 ] = '\0';
+
+ switch ( ludp->lud_scope ) {
+ case LDAP_SCOPE_DEFAULT:
+ mt->mt_scope = LDAP_SCOPE_SUBTREE;
+ break;
+
+ case LDAP_SCOPE_SUBTREE:
+ case LDAP_SCOPE_SUBORDINATE:
+ mt->mt_scope = ludp->lud_scope;
+ break;
+
+ default:
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "invalid scope for target \"%s\"",
+ c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ ldap_free_urllist( ludp );
+ ldap_charray_free( uris );
+ return( 1 );
+ }
+
+ } else {
+ /* check all, to apply the scope check on the first one */
+ if ( ludp->lud_dn != NULL && ludp->lud_dn[ 0 ] != '\0' ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "multiple URIs must have no DN part" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ ldap_free_urllist( ludp );
+ ldap_charray_free( uris );
+ return( 1 );
+
+ }
+ }
+
+ tmpuri = ldap_url_list2urls( ludp );
+ ldap_free_urllist( ludp );
+ if ( tmpuri == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "no memory?" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ ldap_charray_free( uris );
+ return( 1 );
+ }
+ ldap_memfree( uris[ j ] );
+ uris[ j ] = tmpuri;
+ }
+
+ mt->mt_uri = ldap_charray2str( uris, " " );
+ ldap_charray_free( uris );
+ if ( mt->mt_uri == NULL) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "no memory?" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+
+ /*
+ * uri MUST be a branch of suffix!
+ */
+ for ( j = 0; !BER_BVISNULL( &c->be->be_nsuffix[ j ] ); j++ ) {
+ if ( dnIsSuffix( &mt->mt_nsuffix, &c->be->be_nsuffix[ j ] ) ) {
+ break;
+ }
+ }
+
+ if ( BER_BVISNULL( &c->be->be_nsuffix[ j ] ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "<naming context> of URI must be within the naming context of this database." );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ c->ca_private = mt;
+ config_push_cleanup( c, asyncmeta_cf_cleanup );
+ } break;
+ case LDAP_BACK_CFG_SUBTREE_EX:
+ case LDAP_BACK_CFG_SUBTREE_IN:
+ /* subtree-exclude */
+ if ( asyncmeta_subtree_config( mt, c )) {
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ break;
+
+ case LDAP_BACK_CFG_FILTER: {
+ metafilter_t *mf, **m2;
+ mf = ch_malloc( sizeof( metafilter_t ));
+ rc = regcomp( &mf->mf_regex, c->argv[1], REG_EXTENDED );
+ if ( rc ) {
+ char regerr[ SLAP_TEXT_BUFLEN ];
+ regerror( rc, &mf->mf_regex, regerr, sizeof(regerr) );
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "regular expression \"%s\" bad because of %s",
+ c->argv[1], regerr );
+ ch_free( mf );
+ return 1;
+ }
+ ber_str2bv( c->argv[1], 0, 1, &mf->mf_regex_pattern );
+ for ( m2 = &mt->mt_filter; *m2; m2 = &(*m2)->mf_next )
+ ;
+ *m2 = mf;
+ } break;
+ case LDAP_BACK_CFG_MAX_PENDING_OPS:
+ if (c->value_int < 0) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "max-pending-ops invalid value %d",
+ c->value_int);
+ return 1;
+ }
+ mi->mi_max_pending_ops = c->value_int;
+ break;
+ case LDAP_BACK_CFG_MAX_TARGET_CONNS:
+ {
+ if (c->value_int < 0) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "max-target-conns invalid value %d",
+ c->value_int);
+ return 1;
+ }
+ mi->mi_max_target_conns = c->value_int;
+ }
+ break;
+ case LDAP_BACK_CFG_MAX_TIMEOUT_OPS:
+ if (c->value_int < 0) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "max-timeout-ops invalid value %d",
+ c->value_int);
+ return 1;
+ }
+ mi->mi_max_timeout_ops = c->value_int;
+ break;
+
+ case LDAP_BACK_CFG_DEFAULT_T:
+ /* default target directive */
+ i = mi->mi_ntargets - 1;
+
+ if ( c->argc == 1 ) {
+ if ( i < 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s\" alone must be inside a \"uri\" directive",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ mi->mi_defaulttarget = i;
+
+ } else {
+ if ( strcasecmp( c->argv[ 1 ], "none" ) == 0 ) {
+ if ( i >= 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s none\" should go before uri definitions",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ }
+ mi->mi_defaulttarget = META_DEFAULT_TARGET_NONE;
+
+ } else {
+
+ if ( lutil_atoi( &mi->mi_defaulttarget, c->argv[ 1 ] ) != 0
+ || mi->mi_defaulttarget < 0
+ || mi->mi_defaulttarget >= i - 1 )
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "illegal target number %d",
+ mi->mi_defaulttarget );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ }
+ }
+ break;
+
+ case LDAP_BACK_CFG_DNCACHE_TTL:
+ /* ttl of dn cache */
+ if ( strcasecmp( c->argv[ 1 ], "forever" ) == 0 ) {
+ mi->mi_cache.ttl = META_DNCACHE_FOREVER;
+
+ } else if ( strcasecmp( c->argv[ 1 ], "disabled" ) == 0 ) {
+ mi->mi_cache.ttl = META_DNCACHE_DISABLED;
+
+ } else {
+ unsigned long t;
+
+ if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse dncache ttl \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ mi->mi_cache.ttl = (time_t)t;
+ }
+ break;
+
+ case LDAP_BACK_CFG_NETWORK_TIMEOUT: {
+ /* network timeout when connecting to ldap servers */
+ unsigned long t;
+
+ if ( lutil_parse_time( c->argv[ 1 ], &t ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse network timeout \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ mc->mc_network_timeout = (time_t)t;
+ } break;
+
+ case LDAP_BACK_CFG_IDLE_TIMEOUT: {
+ /* idle timeout when connecting to ldap servers */
+ unsigned long t;
+
+ if ( lutil_parse_time( c->argv[ 1 ], &t ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse idle timeout \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+
+ }
+ mi->mi_idle_timeout = (time_t)t;
+ } break;
+
+ case LDAP_BACK_CFG_BIND_TIMEOUT:
+ /* bind timeout when connecting to ldap servers */
+ mc->mc_bind_timeout.tv_sec = c->value_ulong/1000000;
+ mc->mc_bind_timeout.tv_usec = c->value_ulong%1000000;
+ break;
+
+ case LDAP_BACK_CFG_REBIND:
+ /* save bind creds for referral rebinds? */
+ if ( c->argc == 1 || c->value_int ) {
+ mc->mc_flags |= LDAP_BACK_F_SAVECRED;
+ } else {
+ mc->mc_flags &= ~LDAP_BACK_F_SAVECRED;
+ }
+ break;
+
+ case LDAP_BACK_CFG_CHASE:
+ if ( c->argc == 1 || c->value_int ) {
+ mc->mc_flags |= LDAP_BACK_F_CHASE_REFERRALS;
+ } else {
+ mc->mc_flags &= ~LDAP_BACK_F_CHASE_REFERRALS;
+ }
+ break;
+
+ case LDAP_BACK_CFG_TLS:
+ i = verb_to_mask( c->argv[1], tls_mode );
+ if ( BER_BVISNULL( &tls_mode[i].word ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s unknown argument \"%s\"",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ mc->mc_flags &= ~LDAP_BACK_F_TLS_MASK;
+ mc->mc_flags |= tls_mode[i].mask;
+
+ if ( c->argc > 2 ) {
+ if ( c->op == SLAP_CONFIG_ADD && mi->mi_ntargets == 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "need \"uri\" directive first" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ for ( i = 2; i < c->argc; i++ ) {
+ if ( bindconf_tls_parse( c->argv[i], &mt->mt_tls ))
+ return 1;
+ }
+ bindconf_tls_defaults( &mt->mt_tls );
+ }
+ break;
+
+ case LDAP_BACK_CFG_T_F:
+ i = verb_to_mask( c->argv[1], t_f_mode );
+ if ( BER_BVISNULL( &t_f_mode[i].word ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s unknown argument \"%s\"",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ mc->mc_flags &= ~LDAP_BACK_F_T_F_MASK2;
+ mc->mc_flags |= t_f_mode[i].mask;
+ break;
+
+ case LDAP_BACK_CFG_ONERR:
+ /* onerr? */
+ i = verb_to_mask( c->argv[1], onerr_mode );
+ if ( BER_BVISNULL( &onerr_mode[i].word ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s unknown argument \"%s\"",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ mi->mi_flags &= ~META_BACK_F_ONERR_MASK;
+ mi->mi_flags |= onerr_mode[i].mask;
+ break;
+
+ case LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER:
+ /* bind-defer? */
+ if ( c->argc == 1 || c->value_int ) {
+ mi->mi_flags |= META_BACK_F_DEFER_ROOTDN_BIND;
+ } else {
+ mi->mi_flags &= ~META_BACK_F_DEFER_ROOTDN_BIND;
+ }
+ break;
+
+ case LDAP_BACK_CFG_CONNPOOLMAX:
+ /* privileged connections pool max size ? */
+ if ( mi->mi_ntargets > 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s\" must appear before target definitions",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+
+ if ( c->value_int < LDAP_BACK_CONN_PRIV_MIN
+ || c->value_int > LDAP_BACK_CONN_PRIV_MAX )
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "invalid max size " "of privileged "
+ "connections pool \"%s\" "
+ "in \"conn-pool-max <n> "
+ "(must be between %d and %d)\"",
+ c->argv[ 1 ],
+ LDAP_BACK_CONN_PRIV_MIN,
+ LDAP_BACK_CONN_PRIV_MAX );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ mi->mi_conn_priv_max = c->value_int;
+ break;
+
+ case LDAP_BACK_CFG_CANCEL:
+ i = verb_to_mask( c->argv[1], cancel_mode );
+ if ( BER_BVISNULL( &cancel_mode[i].word ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s unknown argument \"%s\"",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ mc->mc_flags &= ~LDAP_BACK_F_CANCEL_MASK2;
+ mc->mc_flags |= cancel_mode[i].mask;
+ break;
+
+ case LDAP_BACK_CFG_TIMEOUT:
+ for ( i = 1; i < c->argc; i++ ) {
+ if ( isdigit( (unsigned char) c->argv[ i ][ 0 ] ) ) {
+ int j;
+ unsigned u;
+
+ if ( lutil_atoux( &u, c->argv[ i ], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "unable to parse timeout \"%s\"",
+ c->argv[ i ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ for ( j = 0; j < SLAP_OP_LAST; j++ ) {
+ mc->mc_timeout[ j ] = u;
+ }
+
+ continue;
+ }
+
+ if ( slap_cf_aux_table_parse( c->argv[ i ], mc->mc_timeout, timeout_table, "slapd-meta timeout" ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "unable to parse timeout \"%s\"",
+ c->argv[ i ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ }
+ break;
+
+ case LDAP_BACK_CFG_IDASSERT_BIND:
+ /* idassert-bind */
+ rc = mi->mi_ldap_extra->idassert_parse( c, &mt->mt_idassert );
+ break;
+
+ case LDAP_BACK_CFG_IDASSERT_AUTHZFROM:
+ /* idassert-authzFrom */
+ rc = mi->mi_ldap_extra->idassert_authzfrom_parse( c, &mt->mt_idassert );
+ break;
+
+ case LDAP_BACK_CFG_QUARANTINE:
+ /* quarantine */
+ if ( META_BACK_CMN_QUARANTINE( mc ) )
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "quarantine already defined" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( mt ) {
+ mc->mc_quarantine.ri_interval = NULL;
+ mc->mc_quarantine.ri_num = NULL;
+ if ( !META_BACK_QUARANTINE( mi ) ) {
+ ldap_pvt_thread_mutex_init( &mt->mt_quarantine_mutex );
+ }
+ }
+
+ if ( mi->mi_ldap_extra->retry_info_parse( c->argv[ 1 ], &mc->mc_quarantine, c->cr_msg, sizeof( c->cr_msg ) ) ) {
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ mc->mc_flags |= LDAP_BACK_F_QUARANTINE;
+ break;
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ case LDAP_BACK_CFG_ST_REQUEST:
+ /* session tracking request */
+ if ( c->value_int ) {
+ mc->mc_flags |= LDAP_BACK_F_ST_REQUEST;
+ } else {
+ mc->mc_flags &= ~LDAP_BACK_F_ST_REQUEST;
+ }
+ break;
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+ case LDAP_BACK_CFG_SUFFIXM:
+ rc = asyncmeta_suffixm_config( c, c->argc, c->argv, mt );
+ break;
+
+ case LDAP_BACK_CFG_NRETRIES: {
+ int nretries = META_RETRY_UNDEFINED;
+
+ if ( strcasecmp( c->argv[ 1 ], "forever" ) == 0 ) {
+ nretries = META_RETRY_FOREVER;
+
+ } else if ( strcasecmp( c->argv[ 1 ], "never" ) == 0 ) {
+ nretries = META_RETRY_NEVER;
+
+ } else {
+ if ( lutil_atoi( &nretries, c->argv[ 1 ] ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse nretries {never|forever|<retries>}: \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ }
+
+ mc->mc_nretries = nretries;
+ } break;
+
+ case LDAP_BACK_CFG_VERSION:
+ if ( c->value_int != 0 && ( c->value_int < LDAP_VERSION_MIN || c->value_int > LDAP_VERSION_MAX ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unsupported protocol version \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ mc->mc_version = c->value_int;
+ break;
+
+ case LDAP_BACK_CFG_NOREFS:
+ /* do not return search references */
+ if ( c->value_int ) {
+ mc->mc_flags |= LDAP_BACK_F_NOREFS;
+ } else {
+ mc->mc_flags &= ~LDAP_BACK_F_NOREFS;
+ }
+ break;
+
+ case LDAP_BACK_CFG_NOUNDEFFILTER:
+ /* do not propagate undefined search filters */
+ if ( c->value_int ) {
+ mc->mc_flags |= LDAP_BACK_F_NOUNDEFFILTER;
+ } else {
+ mc->mc_flags &= ~LDAP_BACK_F_NOUNDEFFILTER;
+ }
+ break;
+
+#ifdef SLAPD_META_CLIENT_PR
+ case LDAP_BACK_CFG_CLIENT_PR:
+ if ( strcasecmp( c->argv[ 1 ], "accept-unsolicited" ) == 0 ) {
+ mc->mc_ps = META_CLIENT_PR_ACCEPT_UNSOLICITED;
+
+ } else if ( strcasecmp( c->argv[ 1 ], "disable" ) == 0 ) {
+ mc->mc_ps = META_CLIENT_PR_DISABLE;
+
+ } else if ( lutil_atoi( &mc->mc_ps, c->argv[ 1 ] ) || mc->mc_ps < -1 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse client-pr {accept-unsolicited|disable|<size>}: \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+ break;
+#endif /* SLAPD_META_CLIENT_PR */
+
+ case LDAP_BACK_CFG_KEEPALIVE: {
+ struct berval bv;
+ ber_str2bv( c->argv[1], 0, 1, &bv );
+ slap_keepalive_parse( &bv, &mt->mt_tls.sb_keepalive, 0, 0, 0 );
+ }
+ break;
+
+ case LDAP_BACK_CFG_TCP_USER_TIMEOUT:
+ mt->mt_tls.sb_tcp_user_timeout = c->value_uint;
+ break;
+
+ /* anything else */
+ default:
+ return SLAP_CONF_UNKNOWN;
+ }
+
+ return rc;
+}
+
+int
+asyncmeta_back_init_cf( BackendInfo *bi )
+{
+ int rc;
+
+ /* Make sure we don't exceed the bits reserved for userland */
+ config_check_userland( LDAP_BACK_CFG_LAST );
+
+ bi->bi_cf_ocs = a_metaocs;
+
+ rc = config_register_schema( a_metacfg, a_metaocs );
+ if ( rc ) {
+ return rc;
+ }
+
+ return 0;
+}
diff --git a/servers/slapd/back-asyncmeta/conn.c b/servers/slapd/back-asyncmeta/conn.c
new file mode 100644
index 0000000..986d1ca
--- /dev/null
+++ b/servers/slapd/back-asyncmeta/conn.c
@@ -0,0 +1,1184 @@
+/* conn.c - handles connections to remote targets */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2016 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* ACKNOWLEDGEMENTS:
++ * This work was developed by Symas Corporation
++ * based on back-meta module for inclusion in OpenLDAP Software.
++ * This work was sponsored by Ericsson. */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include "slap.h"
+#include "../../../libraries/libldap/ldap-int.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-asyncmeta.h"
+
+/*
+ * asyncmeta_conn_alloc
+ *
+ * Allocates a connection structure, making room for all the referenced targets
+ */
+static a_metaconn_t *
+asyncmeta_conn_alloc(
+ a_metainfo_t *mi)
+{
+ a_metaconn_t *mc;
+ int ntargets = mi->mi_ntargets;
+
+ assert( ntargets > 0 );
+
+ /* malloc all in one */
+ mc = ( a_metaconn_t * )ch_calloc( 1, sizeof( a_metaconn_t ) + ntargets * sizeof( a_metasingleconn_t ));
+ if ( mc == NULL ) {
+ return NULL;
+ }
+
+ mc->mc_info = mi;
+ ldap_pvt_thread_mutex_init( &mc->mc_om_mutex);
+ mc->mc_authz_target = META_BOUND_NONE;
+ mc->mc_conns = (a_metasingleconn_t *)(mc+1);
+ return mc;
+}
+
+/*
+ * asyncmeta_init_one_conn
+ *
+ * Initializes one connection
+ */
+int
+asyncmeta_init_one_conn(
+ Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ int candidate,
+ int ispriv,
+ ldap_back_send_t sendok,
+ int dolock)
+{
+ a_metainfo_t *mi = mc->mc_info;
+ a_metatarget_t *mt = mi->mi_targets[ candidate ];
+ a_metasingleconn_t *msc = NULL;
+ int version;
+ a_dncookie dc;
+ int isauthz = ( candidate == mc->mc_authz_target );
+ int do_return = 0;
+#ifdef HAVE_TLS
+ int is_ldaps = 0;
+ int do_start_tls = 0;
+#endif /* HAVE_TLS */
+
+ /* if the server is quarantined, and
+ * - the current interval did not expire yet, or
+ * - no more retries should occur,
+ * don't return the connection */
+ if ( mt->mt_isquarantined ) {
+ slap_retry_info_t *ri = &mt->mt_quarantine;
+ int dont_retry = 0;
+
+ if ( mt->mt_quarantine.ri_interval ) {
+ ldap_pvt_thread_mutex_lock( &mt->mt_quarantine_mutex );
+ dont_retry = ( mt->mt_isquarantined > LDAP_BACK_FQ_NO );
+ if ( dont_retry ) {
+ dont_retry = ( ri->ri_num[ ri->ri_idx ] == SLAP_RETRYNUM_TAIL
+ || slap_get_time() < ri->ri_last + ri->ri_interval[ ri->ri_idx ] );
+ if ( !dont_retry ) {
+ Debug(LDAP_DEBUG_ANY,
+ "%s asyncmeta_init_one_conn[%d]: quarantine " "retry block #%d try #%d.\n",
+ op->o_log_prefix,
+ candidate, ri->ri_idx,
+ ri->ri_count );
+
+ mt->mt_isquarantined = LDAP_BACK_FQ_RETRYING;
+ }
+
+ }
+ ldap_pvt_thread_mutex_unlock( &mt->mt_quarantine_mutex );
+ }
+
+ if ( dont_retry ) {
+ rs->sr_err = LDAP_UNAVAILABLE;
+ rs->sr_text = "Target is quarantined";
+ Debug( LDAP_DEBUG_ANY, "%s asyncmeta_init_one_conn: Target is quarantined\n",
+ op->o_log_prefix );
+ if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
+ send_ldap_result( op, rs );
+ }
+ return rs->sr_err;
+ }
+ }
+ msc = &mc->mc_conns[candidate];
+ /*
+ * Already init'ed
+ */
+ if ( LDAP_BACK_CONN_ISBOUND( msc )
+ || LDAP_BACK_CONN_ISANON( msc ) )
+ {
+ assert( msc->msc_ld != NULL );
+ rs->sr_err = LDAP_SUCCESS;
+ do_return = 1;
+
+ } else if ( META_BACK_CONN_CREATING( msc )
+ || LDAP_BACK_CONN_BINDING( msc ) )
+ {
+ rs->sr_err = LDAP_SUCCESS;
+ do_return = 1;
+
+ } else if ( META_BACK_CONN_INITED( msc ) ) {
+ assert( msc->msc_ld != NULL );
+ rs->sr_err = LDAP_SUCCESS;
+ do_return = 1;
+
+ } else {
+ /*
+ * creating...
+ */
+ META_BACK_CONN_CREATING_SET( msc );
+ }
+
+ if ( do_return ) {
+ if ( rs->sr_err != LDAP_SUCCESS
+ && op->o_conn
+ && ( sendok & LDAP_BACK_SENDERR ) )
+ {
+ send_ldap_result( op, rs );
+ }
+
+ return rs->sr_err;
+ }
+
+ assert( msc->msc_ld == NULL );
+
+ /*
+ * Attempts to initialize the connection to the target ds
+ */
+ ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
+
+ rs->sr_err = ldap_initialize( &msc->msc_ld, mt->mt_uri );
+#ifdef HAVE_TLS
+ is_ldaps = ldap_is_ldaps_url( mt->mt_uri );
+#endif /* HAVE_TLS */
+ ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s asyncmeta_init_one_conn: ldap_initialize failed err=%d\n",
+ op->o_log_prefix, rs->sr_err );
+ goto error_return;
+ }
+
+ ldap_set_option( msc->msc_ld, LDAP_OPT_KEEPCONN, LDAP_OPT_ON);
+
+ msc->msc_ldr = ldap_dup(msc->msc_ld);
+ if (!msc->msc_ldr) {
+ ldap_ld_free(msc->msc_ld, 0, NULL, NULL);
+ rs->sr_err = LDAP_NO_MEMORY;
+ goto error_return;
+ }
+
+ /*
+ * Set LDAP version. This will always succeed: If the client
+ * bound with a particular version, then so can we.
+ */
+ if ( mt->mt_version != 0 ) {
+ version = mt->mt_version;
+
+ } else if ( op->o_conn->c_protocol != 0 ) {
+ version = op->o_conn->c_protocol;
+
+ } else {
+ version = LDAP_VERSION3;
+ }
+ ldap_set_option( msc->msc_ld, LDAP_OPT_PROTOCOL_VERSION, &version );
+ ldap_set_urllist_proc( msc->msc_ld, mt->mt_urllist_f, mt->mt_urllist_p );
+
+ /* automatically chase referrals ("chase-referrals [{yes|no}]" statement) */
+ ldap_set_option( msc->msc_ld, LDAP_OPT_REFERRALS,
+ META_BACK_TGT_CHASE_REFERRALS( mt ) ? LDAP_OPT_ON : LDAP_OPT_OFF );
+
+ slap_client_keepalive(msc->msc_ld, &mt->mt_tls.sb_keepalive);
+
+ if ( mt->mt_tls.sb_tcp_user_timeout > 0 ) {
+ ldap_set_option( msc->msc_ld, LDAP_OPT_TCP_USER_TIMEOUT,
+ &mt->mt_tls.sb_tcp_user_timeout );
+ }
+
+#ifdef HAVE_TLS
+ {
+ slap_bindconf *sb = NULL;
+
+ if ( ispriv ) {
+ sb = &mt->mt_idassert.si_bc;
+ } else {
+ sb = &mt->mt_tls;
+ }
+
+ bindconf_tls_set( sb, msc->msc_ld );
+
+ if ( !is_ldaps ) {
+ if ( META_BACK_TGT_USE_TLS( mt )
+ || ( op->o_conn->c_is_tls && META_BACK_TGT_PROPAGATE_TLS( mt ) ) )
+ {
+ do_start_tls = 1;
+ }
+ }
+ }
+
+ /* start TLS ("tls [try-]{start|propagate}" statement) */
+ if ( do_start_tls ) {
+#ifdef SLAP_STARTTLS_ASYNCHRONOUS
+ /*
+ * use asynchronous StartTLS; in case, chase referral
+ * FIXME: OpenLDAP does not return referral on StartTLS yet
+ */
+ int msgid;
+
+ rs->sr_err = ldap_start_tls( msc->msc_ld, NULL, NULL, &msgid );
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ LDAPMessage *res = NULL;
+ int rc, nretries = mt->mt_nretries;
+ struct timeval tv;
+
+ LDAP_BACK_TV_SET( &tv );
+
+retry:;
+ rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
+ switch ( rc ) {
+ case -1:
+ rs->sr_err = LDAP_OTHER;
+ break;
+
+ case 0:
+ if ( nretries != 0 ) {
+ if ( nretries > 0 ) {
+ nretries--;
+ }
+ LDAP_BACK_TV_SET( &tv );
+ goto retry;
+ }
+ rs->sr_err = LDAP_OTHER;
+ break;
+
+ default:
+ /* only touch when activity actually took place... */
+ if ( mi->mi_idle_timeout != 0 ) {
+ asyncmeta_set_msc_time(msc);
+ }
+ break;
+ }
+
+ if ( rc == LDAP_RES_EXTENDED ) {
+ struct berval *data = NULL;
+
+ /* NOTE: right now, data is unused, so don't get it */
+ rs->sr_err = ldap_parse_extended_result( msc->msc_ld,
+ res, NULL, NULL /* &data */ , 0 );
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ int err;
+
+ /* FIXME: matched? referrals? response controls? */
+ rs->sr_err = ldap_parse_result( msc->msc_ld,
+ res, &err, NULL, NULL, NULL, NULL, 1 );
+ res = NULL;
+
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+
+ rs->sr_err = err;
+ }
+ rs->sr_err = slap_map_api2result( rs );
+
+ /* FIXME: in case a referral
+ * is returned, should we try
+ * using it instead of the
+ * configured URI? */
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ rs->sr_err = ldap_install_tls( msc->msc_ld );
+
+ } else if ( rs->sr_err == LDAP_REFERRAL ) {
+ /* FIXME: LDAP_OPERATIONS_ERROR? */
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "Unwilling to chase referral "
+ "returned by Start TLS exop";
+ }
+
+ if ( data ) {
+ ber_bvfree( data );
+ }
+ }
+
+ } else {
+ rs->sr_err = LDAP_OTHER;
+ }
+
+ if ( res != NULL ) {
+ ldap_msgfree( res );
+ }
+ }
+#else /* ! SLAP_STARTTLS_ASYNCHRONOUS */
+ /*
+ * use synchronous StartTLS
+ */
+ rs->sr_err = ldap_start_tls_s( msc->msc_ld, NULL, NULL );
+#endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */
+ if (rs->sr_err != LDAP_SUCCESS) {
+ Debug( LDAP_DEBUG_ANY, "%s asyncmeta_init_one_conn: ldap_start_tls_s failed err=%d\n",
+ op->o_log_prefix, rs->sr_err );
+ }
+ /* if StartTLS is requested, only attempt it if the URL
+ * is not "ldaps://"; this may occur not only in case
+ * of misconfiguration, but also when used in the chain
+ * overlay, where the "uri" can be parsed out of a referral */
+ if ( rs->sr_err == LDAP_SERVER_DOWN
+ || ( rs->sr_err != LDAP_SUCCESS
+ && META_BACK_TGT_TLS_CRITICAL( mt ) ) )
+ {
+
+#ifdef DEBUG_205
+ Debug( LDAP_DEBUG_ANY,
+ "### %s asyncmeta_init_one_conn(TLS) "
+ "ldap_unbind_ext[%d] ld=%p\n",
+ op->o_log_prefix, candidate,
+ (void *)msc->msc_ld );
+#endif /* DEBUG_205 */
+
+ /* need to trash a failed Start TLS */
+ asyncmeta_clear_one_msc( op, mc, candidate, 1, __FUNCTION__ );
+ goto error_return;
+ }
+ }
+#endif /* HAVE_TLS */
+ /*
+ * Set the network timeout if set
+ */
+ if ( mt->mt_network_timeout != 0 ) {
+ struct timeval network_timeout;
+ network_timeout.tv_sec = 0;
+ network_timeout.tv_usec = mt->mt_network_timeout*1000;
+
+ ldap_set_option( msc->msc_ld, LDAP_OPT_NETWORK_TIMEOUT,
+ (void *)&network_timeout );
+ }
+
+ /*
+ * If the connection DN is not null, an attempt to rewrite it is made
+ */
+
+ if ( ispriv ) {
+ if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) ) {
+ ber_bvreplace( &msc->msc_bound_ndn, &mt->mt_idassert_authcDN );
+ if ( !BER_BVISNULL( &mt->mt_idassert_passwd ) ) {
+ if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+ memset( msc->msc_cred.bv_val, 0,
+ msc->msc_cred.bv_len );
+ }
+ ber_bvreplace( &msc->msc_cred, &mt->mt_idassert_passwd );
+ }
+ LDAP_BACK_CONN_ISIDASSERT_SET( msc );
+
+ } else {
+ ber_bvreplace( &msc->msc_bound_ndn, &slap_empty_bv );
+ }
+
+ } else {
+ if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+ memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
+ ber_memfree_x( msc->msc_cred.bv_val, NULL );
+ BER_BVZERO( &msc->msc_cred );
+ }
+ if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
+ ber_memfree_x( msc->msc_bound_ndn.bv_val, NULL );
+ BER_BVZERO( &msc->msc_bound_ndn );
+ }
+ if ( !BER_BVISEMPTY( &op->o_ndn )
+ && isauthz )
+ {
+ dc.op = op;
+ dc.target = mt;
+ dc.memctx = NULL;
+ dc.to_from = MASSAGE_REQ;
+
+ /*
+ * Rewrite the bind dn if needed
+ */
+ asyncmeta_dn_massage( &dc, &op->o_conn->c_dn, &msc->msc_bound_ndn );
+
+ /* copy the DN if needed */
+ if ( msc->msc_bound_ndn.bv_val == op->o_conn->c_dn.bv_val ) {
+ ber_dupbv( &msc->msc_bound_ndn, &op->o_conn->c_dn );
+ }
+ } else {
+ ber_dupbv( &msc->msc_bound_ndn, (struct berval *)&slap_empty_bv );
+ }
+ }
+ assert( !BER_BVISNULL( &msc->msc_bound_ndn ) );
+
+error_return:;
+
+ if (msc != NULL) {
+ META_BACK_CONN_CREATING_CLEAR( msc );
+ }
+ if ( rs->sr_err == LDAP_SUCCESS && msc != NULL) {
+ META_BACK_CONN_INITED_SET( msc );
+ }
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ rs->sr_err = slap_map_api2result( rs );
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ }
+ return rs->sr_err;
+}
+
+
+static int
+asyncmeta_get_candidate(
+ Operation *op,
+ SlapReply *rs,
+ struct berval *ndn )
+{
+ a_metainfo_t *mi = ( a_metainfo_t * )op->o_bd->be_private;
+ long candidate;
+
+ /*
+ * tries to get a unique candidate
+ * (takes care of default target)
+ */
+ candidate = asyncmeta_select_unique_candidate( mi, ndn );
+
+ /*
+ * if any is found, inits the connection
+ */
+ if ( candidate == META_TARGET_NONE ) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = "No suitable candidate target found";
+
+ } else {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+
+ return candidate;
+}
+
+
+/*
+ * asyncmeta_getconn
+ *
+ * Prepares the connection structure
+ *
+ * RATIONALE:
+ *
+ * - determine what DN is being requested:
+ *
+ * op requires candidate checks
+ *
+ * add unique parent of o_req_ndn
+ * bind unique^*[/all] o_req_ndn [no check]
+ * compare unique^+ o_req_ndn
+ * delete unique o_req_ndn
+ * modify unique o_req_ndn
+ * search any o_req_ndn
+ * modrdn unique[, unique] o_req_ndn[, orr_nnewSup]
+ *
+ * - for ops that require the candidate to be unique, in case of multiple
+ * occurrences an internal search with sizeLimit=1 is performed
+ * if a unique candidate can actually be determined. If none is found,
+ * the operation aborts; if multiple are found, the default target
+ * is used if defined and candidate; otherwise the operation aborts.
+ *
+ * *^note: actually, the bind operation is handled much like a search;
+ * i.e. the bind is broadcast to all candidate targets.
+ *
+ * +^note: actually, the compare operation is handled much like a search;
+ * i.e. the compare is broadcast to all candidate targets, while checking
+ * that exactly none (noSuchObject) or one (TRUE/FALSE/UNDEFINED) is
+ * returned.
+ */
+a_metaconn_t *
+asyncmeta_getconn(
+ Operation *op,
+ SlapReply *rs,
+ SlapReply *candidates,
+ int *candidate,
+ ldap_back_send_t sendok,
+ int alloc_new)
+{
+ a_metainfo_t *mi = ( a_metainfo_t * )op->o_bd->be_private;
+ a_metaconn_t *mc = NULL,
+ mc_curr = {{ 0 }};
+ int cached = META_TARGET_NONE,
+ i = META_TARGET_NONE,
+ err = LDAP_SUCCESS,
+ new_conn = 0,
+ ncandidates = 0;
+
+
+ meta_op_type op_type = META_OP_REQUIRE_SINGLE;
+ enum {
+ META_DNTYPE_ENTRY,
+ META_DNTYPE_PARENT,
+ META_DNTYPE_NEWPARENT
+ } dn_type = META_DNTYPE_ENTRY;
+ struct berval ndn = op->o_req_ndn,
+ pndn;
+
+ if (alloc_new > 0) {
+ mc = asyncmeta_conn_alloc(mi);
+ new_conn = 0;
+ } else {
+ mc = asyncmeta_get_next_mc(mi);
+ }
+
+ ldap_pvt_thread_mutex_lock(&mc->mc_om_mutex);
+ /* Internal searches are privileged and shared. So is root. */
+ if ( ( !BER_BVISEMPTY( &op->o_ndn ) && META_BACK_PROXYAUTHZ_ALWAYS( mi ) )
+ || ( BER_BVISEMPTY( &op->o_ndn ) && META_BACK_PROXYAUTHZ_ANON( mi ) )
+ || op->o_do_not_cache || be_isroot( op ) )
+ {
+ LDAP_BACK_CONN_ISPRIV_SET( &mc_curr );
+ LDAP_BACK_PCONN_ROOTDN_SET( &mc_curr, op );
+
+ } else if ( BER_BVISEMPTY( &op->o_ndn ) && META_BACK_PROXYAUTHZ_NOANON( mi ) )
+ {
+ LDAP_BACK_CONN_ISANON_SET( &mc_curr );
+ LDAP_BACK_PCONN_ANON_SET( &mc_curr, op );
+
+ } else {
+ /* Explicit binds must not be shared */
+ if ( !BER_BVISEMPTY( &op->o_ndn )
+ || op->o_tag == LDAP_REQ_BIND
+ || SLAP_IS_AUTHZ_BACKEND( op ) )
+ {
+ //mc_curr.mc_conn = op->o_conn;
+
+ } else {
+ LDAP_BACK_CONN_ISANON_SET( &mc_curr );
+ LDAP_BACK_PCONN_ANON_SET( &mc_curr, op );
+ }
+ }
+
+ switch ( op->o_tag ) {
+ case LDAP_REQ_ADD:
+ /* if we go to selection, the entry must not exist,
+ * and we must be able to resolve the parent */
+ dn_type = META_DNTYPE_PARENT;
+ dnParent( &ndn, &pndn );
+ break;
+
+ case LDAP_REQ_MODRDN:
+ /* if nnewSuperior is not NULL, it must resolve
+ * to the same candidate as the req_ndn */
+ if ( op->orr_nnewSup ) {
+ dn_type = META_DNTYPE_NEWPARENT;
+ }
+ break;
+
+ case LDAP_REQ_BIND:
+ /* if bound as rootdn, the backend must bind to all targets
+ * with the administrative identity
+ * (unless pseoudoroot-bind-defer is TRUE) */
+ if ( op->orb_method == LDAP_AUTH_SIMPLE && be_isroot_pw( op ) ) {
+ op_type = META_OP_REQUIRE_ALL;
+ }
+ break;
+
+ case LDAP_REQ_COMPARE:
+ case LDAP_REQ_DELETE:
+ case LDAP_REQ_MODIFY:
+ /* just a unique candidate */
+ break;
+
+ case LDAP_REQ_SEARCH:
+ /* allow multiple candidates for the searchBase */
+ op_type = META_OP_ALLOW_MULTIPLE;
+ break;
+
+ default:
+ /* right now, just break (exop?) */
+ break;
+ }
+
+ /*
+ * require all connections ...
+ */
+ if ( op_type == META_OP_REQUIRE_ALL ) {
+ if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
+ LDAP_BACK_CONN_ISPRIV_SET( mc );
+
+ } else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
+ LDAP_BACK_CONN_ISANON_SET( mc );
+ }
+
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ /*
+ * The target is activated; if needed, it is
+ * also init'd
+ */
+ candidates[ i ].sr_err = asyncmeta_init_one_conn( op,
+ rs, mc, i, LDAP_BACK_CONN_ISPRIV( &mc_curr ),
+ LDAP_BACK_DONTSEND, !new_conn );
+ if ( candidates[ i ].sr_err == LDAP_SUCCESS ) {
+ if ( new_conn && ( sendok & LDAP_BACK_BINDING ) ) {
+ LDAP_BACK_CONN_BINDING_SET( &mc->mc_conns[ i ] );
+ }
+ META_CANDIDATE_SET( &candidates[ i ] );
+ ncandidates++;
+
+ } else {
+
+ /*
+ * FIXME: in case one target cannot
+ * be init'd, should the other ones
+ * be tried?
+ */
+ META_CANDIDATE_RESET( &candidates[ i ] );
+ err = candidates[ i ].sr_err;
+ continue;
+ }
+ }
+
+ if ( ncandidates == 0 ) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = "Unable to select valid candidates";
+
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
+ rs->sr_matched = mi->mi_suffix.bv_val;
+ }
+ send_ldap_result( op, rs );
+ rs->sr_matched = NULL;
+ }
+ ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
+ if ( alloc_new > 0) {
+ asyncmeta_back_conn_free( mc );
+ }
+ return NULL;
+ }
+
+ goto done;
+ }
+
+ /*
+ * looks in cache, if any
+ */
+ if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
+ cached = i = asyncmeta_dncache_get_target( &mi->mi_cache, &op->o_req_ndn );
+ }
+
+ if ( op_type == META_OP_REQUIRE_SINGLE ) {
+ int j;
+
+ for ( j = 0; j < mi->mi_ntargets; j++ ) {
+ META_CANDIDATE_RESET( &candidates[ j ] );
+ }
+
+ /*
+ * tries to get a unique candidate
+ * (takes care of default target)
+ */
+ if ( i == META_TARGET_NONE ) {
+ i = asyncmeta_get_candidate( op, rs, &ndn );
+
+ if ( rs->sr_err == LDAP_NO_SUCH_OBJECT && dn_type == META_DNTYPE_PARENT ) {
+ i = asyncmeta_get_candidate( op, rs, &pndn );
+ }
+
+ if ( i < 0 || rs->sr_err != LDAP_SUCCESS ) {
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
+ rs->sr_matched = mi->mi_suffix.bv_val;
+ }
+ send_ldap_result( op, rs );
+ rs->sr_matched = NULL;
+ }
+ ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
+ if ( mc != NULL && alloc_new ) {
+ asyncmeta_back_conn_free( mc );
+ }
+ return NULL;
+ }
+ }
+
+ if ( dn_type == META_DNTYPE_NEWPARENT && asyncmeta_get_candidate( op, rs, op->orr_nnewSup ) != i )
+ {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "Cross-target rename not supported";
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
+ if ( mc != NULL && alloc_new > 0 ) {
+ asyncmeta_back_conn_free( mc );
+ }
+ return NULL;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "==>asyncmeta__getconn: got target=%d for ndn=\"%s\" from cache\n",
+ i, op->o_req_ndn.bv_val );
+ if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
+ LDAP_BACK_CONN_ISPRIV_SET( mc );
+
+ } else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
+ LDAP_BACK_CONN_ISANON_SET( mc );
+ }
+
+ /*
+ * Clear all other candidates
+ */
+ ( void )asyncmeta_clear_unused_candidates( op, i , mc, candidates);
+
+ /*
+ * The target is activated; if needed, it is
+ * also init'd. In case of error, asyncmeta_init_one_conn
+ * sends the appropriate result.
+ */
+ err = asyncmeta_init_one_conn( op, rs, mc, i,
+ LDAP_BACK_CONN_ISPRIV( &mc_curr ), sendok, !new_conn );
+ if ( err != LDAP_SUCCESS ) {
+ /*
+ * FIXME: in case one target cannot
+ * be init'd, should the other ones
+ * be tried?
+ */
+ META_CANDIDATE_RESET( &candidates[ i ] );
+ ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
+ if ( mc != NULL && alloc_new > 0 ) {
+ asyncmeta_back_conn_free( mc );
+ }
+ return NULL;
+ }
+
+ candidates[ i ].sr_err = LDAP_SUCCESS;
+ META_CANDIDATE_SET( &candidates[ i ] );
+ ncandidates++;
+
+ if ( candidate ) {
+ *candidate = i;
+ }
+
+ /*
+ * if no unique candidate ...
+ */
+ } else {
+ if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
+ LDAP_BACK_CONN_ISPRIV_SET( mc );
+
+ } else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
+ LDAP_BACK_CONN_ISANON_SET( mc );
+ }
+
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ a_metatarget_t *mt = mi->mi_targets[ i ];
+
+ META_CANDIDATE_RESET( &candidates[ i ] );
+
+ if ( i == cached
+ || asyncmeta_is_candidate( mt, &op->o_req_ndn,
+ op->o_tag == LDAP_REQ_SEARCH ? op->ors_scope : LDAP_SCOPE_SUBTREE ) )
+ {
+
+ /*
+ * The target is activated; if needed, it is
+ * also init'd
+ */
+ int lerr = asyncmeta_init_one_conn( op, rs, mc, i,
+ LDAP_BACK_CONN_ISPRIV( &mc_curr ),
+ LDAP_BACK_DONTSEND, !new_conn );
+ candidates[ i ].sr_err = lerr;
+ if ( lerr == LDAP_SUCCESS ) {
+ META_CANDIDATE_SET( &candidates[ i ] );
+ ncandidates++;
+
+ Debug( LDAP_DEBUG_TRACE, "%s: asyncmeta_getconn[%d]\n",
+ op->o_log_prefix, i );
+
+ } else if ( lerr == LDAP_UNAVAILABLE && !META_BACK_ONERR_STOP( mi ) ) {
+ META_CANDIDATE_SET( &candidates[ i ] );
+
+ Debug( LDAP_DEBUG_TRACE, "%s: asyncmeta_getconn[%d] %s\n",
+ op->o_log_prefix, i,
+ mt->mt_isquarantined != LDAP_BACK_FQ_NO ? "quarantined" : "unavailable" );
+
+ } else {
+
+ /*
+ * FIXME: in case one target cannot
+ * be init'd, should the other ones
+ * be tried?
+ */
+ /* leave the target candidate, but record the error for later use */
+ err = lerr;
+
+ if ( lerr == LDAP_UNAVAILABLE && mt->mt_isquarantined != LDAP_BACK_FQ_NO ) {
+ Debug( LDAP_DEBUG_TRACE, "%s: asyncmeta_getconn[%d] quarantined err=%d\n",
+ op->o_log_prefix, i, lerr );
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s: asyncmeta_getconn[%d] failed err=%d\n",
+ op->o_log_prefix, i, lerr );
+ }
+
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
+ if ( alloc_new > 0 ) {
+ asyncmeta_back_conn_free( mc );
+
+ }
+ return NULL;
+ }
+
+ continue;
+ }
+
+ }
+ }
+
+ if ( ncandidates == 0 ) {
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = "Unable to select valid candidates";
+ }
+
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
+ rs->sr_matched = mi->mi_suffix.bv_val;
+ }
+ send_ldap_result( op, rs );
+ rs->sr_matched = NULL;
+ }
+ if ( alloc_new > 0 ) {
+ asyncmeta_back_conn_free( mc );
+
+ }
+ ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
+ return NULL;
+ }
+ }
+
+done:;
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+
+ if ( new_conn ) {
+ if ( !LDAP_BACK_PCONN_ISPRIV( mc ) ) {
+ /*
+ * Err could be -1 in case a duplicate metaconn is inserted
+ */
+ switch ( err ) {
+ case 0:
+ break;
+ default:
+ LDAP_BACK_CONN_CACHED_CLEAR( mc );
+ if ( LogTest( LDAP_DEBUG_ANY ) ) {
+ char buf[STRLENOF("4294967295U") + 1] = { 0 };
+ mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
+
+ Debug( LDAP_DEBUG_ANY,
+ "%s asyncmeta_getconn: candidates=%d conn=%s insert failed\n",
+ op->o_log_prefix, ncandidates, buf );
+ }
+
+ asyncmeta_back_conn_free( mc );
+
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "Proxy bind collision";
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ return NULL;
+ }
+ }
+
+ if ( LogTest( LDAP_DEBUG_TRACE ) ) {
+ char buf[STRLENOF("4294967295U") + 1] = { 0 };
+ mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "%s asyncmeta_getconn: candidates=%d conn=%s inserted\n",
+ op->o_log_prefix, ncandidates, buf );
+ }
+
+ } else {
+ if ( LogTest( LDAP_DEBUG_TRACE ) ) {
+ char buf[STRLENOF("4294967295U") + 1] = { 0 };
+ mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "%s asyncmeta_getconn: candidates=%d conn=%s fetched\n",
+ op->o_log_prefix, ncandidates, buf );
+ }
+ }
+ ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
+ return mc;
+}
+
+void
+asyncmeta_quarantine(
+ Operation *op,
+ a_metainfo_t *mi,
+ SlapReply *rs,
+ int candidate )
+{
+ a_metatarget_t *mt = mi->mi_targets[ candidate ];
+
+ slap_retry_info_t *ri = &mt->mt_quarantine;
+
+ ldap_pvt_thread_mutex_lock( &mt->mt_quarantine_mutex );
+
+ if ( rs->sr_err == LDAP_UNAVAILABLE ) {
+ time_t new_last = slap_get_time();
+
+ switch ( mt->mt_isquarantined ) {
+ case LDAP_BACK_FQ_NO:
+ if ( ri->ri_last == new_last ) {
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_ANY,
+ "%s asyncmeta_quarantine[%d]: enter.\n",
+ op->o_log_prefix, candidate );
+
+ ri->ri_idx = 0;
+ ri->ri_count = 0;
+ break;
+
+ case LDAP_BACK_FQ_RETRYING:
+ Debug(LDAP_DEBUG_ANY,
+ "%s asyncmeta_quarantine[%d]: block #%d try #%d failed.\n",
+ op->o_log_prefix, candidate, ri->ri_idx,
+ ri->ri_count );
+
+ ++ri->ri_count;
+ if ( ri->ri_num[ ri->ri_idx ] != SLAP_RETRYNUM_FOREVER
+ && ri->ri_count == ri->ri_num[ ri->ri_idx ] )
+ {
+ ri->ri_count = 0;
+ ++ri->ri_idx;
+ }
+ break;
+
+ default:
+ goto done;
+ }
+
+ mt->mt_isquarantined = LDAP_BACK_FQ_YES;
+ ri->ri_last = new_last;
+
+ } else if ( mt->mt_isquarantined == LDAP_BACK_FQ_RETRYING ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s asyncmeta_quarantine[%d]: exit.\n",
+ op->o_log_prefix, candidate );
+
+ if ( mi->mi_quarantine_f ) {
+ (void)mi->mi_quarantine_f( mi, candidate,
+ mi->mi_quarantine_p );
+ }
+
+ ri->ri_count = 0;
+ ri->ri_idx = 0;
+ mt->mt_isquarantined = LDAP_BACK_FQ_NO;
+ mt->mt_timeout_ops = 0;
+ }
+
+done:;
+ ldap_pvt_thread_mutex_unlock( &mt->mt_quarantine_mutex );
+}
+
+a_metaconn_t *
+asyncmeta_get_next_mc( a_metainfo_t *mi )
+{
+ a_metaconn_t *mc = NULL;
+
+ ldap_pvt_thread_mutex_lock( &mi->mi_mc_mutex );
+ if (mi->mi_next_conn >= mi->mi_num_conns-1) {
+ mi->mi_next_conn = 0;
+ } else {
+ mi->mi_next_conn++;
+ }
+
+ mc = &mi->mi_conns[mi->mi_next_conn];
+ ldap_pvt_thread_mutex_unlock( &mi->mi_mc_mutex );
+ return mc;
+}
+
+int asyncmeta_start_listeners(a_metaconn_t *mc, SlapReply *candidates, bm_context_t *bc)
+{
+ int i;
+ for (i = 0; i < mc->mc_info->mi_ntargets; i++) {
+ asyncmeta_start_one_listener(mc, candidates, bc, i);
+ }
+ return LDAP_SUCCESS;
+}
+
+int asyncmeta_start_one_listener(a_metaconn_t *mc,
+ SlapReply *candidates,
+ bm_context_t *bc,
+ int candidate)
+{
+ a_metasingleconn_t *msc;
+ ber_socket_t s;
+
+ msc = &mc->mc_conns[candidate];
+ if ( slapd_shutdown || !META_BACK_CONN_INITED( msc ) || msc->msc_ld == NULL
+ || META_BACK_CONN_INVALID(msc) || !META_IS_CANDIDATE( &candidates[ candidate ] )) {
+ return LDAP_SUCCESS;
+ }
+ bc->msgids[candidate] = candidates[candidate].sr_msgid;
+ if ( msc->conn == NULL) {
+ ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
+ if (s < 0) {
+ /* Todo a meaningful log pls */
+ return LDAP_OTHER;
+ }
+ msc->conn = connection_client_setup( s, asyncmeta_op_handle_result, mc );
+ }
+ connection_client_enable( msc->conn );
+ return LDAP_SUCCESS;
+}
+
+int
+asyncmeta_clear_one_msc(
+ Operation *op,
+ a_metaconn_t *mc,
+ int candidate,
+ int unbind,
+ const char *caller)
+{
+ a_metasingleconn_t *msc;
+ if (mc == NULL) {
+ return 0;
+ }
+ msc = &mc->mc_conns[candidate];
+ if ( LogTest( asyncmeta_debug ) ) {
+ char time_buf[ SLAP_TEXT_BUFLEN ];
+ asyncmeta_get_timestamp(time_buf);
+ Debug( asyncmeta_debug, "[%s] Resetting msc: %p, msc_ld: %p, "
+ "msc_bound_ndn: %s, msc->conn: %p, %s \n",
+ time_buf, msc, msc->msc_ld, msc->msc_bound_ndn.bv_val,
+ msc->conn, caller ? caller : "" );
+ }
+ msc->msc_mscflags = 0;
+ if (msc->conn) {
+ connection_client_stop( msc->conn );
+ msc->conn = NULL;
+ }
+
+ if ( msc->msc_ld != NULL ) {
+
+#ifdef DEBUG_205
+ Debug( LDAP_DEBUG_ANY, "### %s asyncmeta_clear_one_msc ldap_unbind_ext[%d] ld=%p\n",
+ op ? op->o_log_prefix : "", candidate, (void *)msc->msc_ld );
+#endif /* DEBUG_205 */
+
+ ldap_unbind_ext( msc->msc_ld, NULL, NULL );
+ msc->msc_ld = NULL;
+ ldap_ld_free( msc->msc_ldr, 0, NULL, NULL );
+ msc->msc_ldr = NULL;
+ }
+
+ if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
+ ber_memfree_x( msc->msc_bound_ndn.bv_val, NULL );
+ BER_BVZERO( &msc->msc_bound_ndn );
+ }
+
+ if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+ memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
+ ber_memfree_x( msc->msc_cred.bv_val, NULL );
+ BER_BVZERO( &msc->msc_cred );
+ }
+ msc->msc_time = 0;
+ msc->msc_binding_time = 0;
+ msc->msc_result_time = 0;
+ return 0;
+}
+
+void asyncmeta_get_timestamp(char *buf)
+{
+ struct timespec tp;
+ struct tm *ttm;
+ clock_gettime(CLOCK_REALTIME, &tp);
+ ttm = gmtime(&tp.tv_sec);
+ sprintf(buf, "%d:%d:%d.%ld", ttm->tm_hour, ttm->tm_min, ttm->tm_sec, tp.tv_nsec/1000);
+}
+
+int
+asyncmeta_reset_msc(
+ Operation *op,
+ a_metaconn_t *mc,
+ int candidate,
+ int unbind,
+ const char *caller)
+{
+ a_metasingleconn_t *msc = &mc->mc_conns[candidate];
+ if ( LogTest( asyncmeta_debug ) ) {
+ char time_buf[ SLAP_TEXT_BUFLEN ];
+ asyncmeta_get_timestamp(time_buf);
+ Debug(asyncmeta_debug, "[%x] Will attempt to reset [%s] msc: %p, "
+ "msc->msc_binding_time: %x, msc->msc_flags:%x %s\n",
+ (unsigned int)slap_get_time(), time_buf, msc,
+ (unsigned int)msc->msc_binding_time, msc->msc_mscflags, caller );
+ }
+ if (msc->msc_active <= 1 && mc->mc_active < 1) {
+ bm_context_t *om;
+ asyncmeta_clear_one_msc(NULL, mc, candidate, 0, caller);
+ /* set whatever's in the queue to invalid, so the timeout loop cleans it up,
+ * but do not invalidate the current op*/
+ LDAP_STAILQ_FOREACH( om, &mc->mc_om_list, bc_next ) {
+ if (om->candidates[candidate].sr_msgid >= 0 && (om->op != op)) {
+ om->bc_invalid = 1;
+ }
+ }
+ return LDAP_SUCCESS;
+ } else {
+ META_BACK_CONN_INVALID_SET(msc);
+ Debug( asyncmeta_debug, "[%x] Failed to reset msc %p, msc_active=%d, mc_active=%d, %s\n",
+ (unsigned int)slap_get_time(), msc, msc->msc_active, mc->mc_active, caller );
+ }
+ return LDAP_OTHER;
+}
+
+
+void asyncmeta_log_msc(a_metasingleconn_t *msc)
+{
+ ber_socket_t s = 0;
+ if (msc->msc_ld) {
+ ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
+ }
+ Debug( asyncmeta_debug, "msc: %p, msc_ld: %p, msc_ld socket: %d, "
+ "msc_bound_ndn: %s, msc->conn: %p\n", msc, msc->msc_ld,
+ (int)s, msc->msc_bound_ndn.bv_val, msc->conn );
+}
+
+void asyncmeta_log_conns(a_metainfo_t *mi)
+{
+ a_metaconn_t *mc;
+ int i, j;
+ for (i = 0; i < mi->mi_num_conns; i++) {
+ mc = &mi->mi_conns[i];
+ Debug(asyncmeta_debug, "mc: %p, mc->pending_ops: %d\n", mc, mc->pending_ops);
+ for (j = 0; j < mi->mi_ntargets; j++ ) {
+ asyncmeta_log_msc(&mc->mc_conns[j]);
+ }
+
+ }
+}
diff --git a/servers/slapd/back-asyncmeta/delete.c b/servers/slapd/back-asyncmeta/delete.c
new file mode 100644
index 0000000..b91b1a5
--- /dev/null
+++ b/servers/slapd/back-asyncmeta/delete.c
@@ -0,0 +1,297 @@
+/* delete.c - delete request handler for back-asyncmeta */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2016 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* ACKNOWLEDGEMENTS:
++ * This work was developed by Symas Corporation
++ * based on back-meta module for inclusion in OpenLDAP Software.
++ * This work was sponsored by Ericsson. */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+#include "slap.h"
+#include "../../../libraries/liblber/lber-int.h"
+#include "../../../libraries/libldap/ldap-int.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-asyncmeta.h"
+
+meta_search_candidate_t
+asyncmeta_back_delete_start(Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ bm_context_t *bc,
+ int candidate,
+ int do_lock)
+{
+ a_metainfo_t *mi = mc->mc_info;
+ a_metatarget_t *mt = mi->mi_targets[ candidate ];
+ struct berval mdn = BER_BVNULL;
+ a_dncookie dc;
+ int rc = 0;
+ LDAPControl **ctrls = NULL;
+ meta_search_candidate_t retcode = META_SEARCH_CANDIDATE;
+ BerElement *ber = NULL;
+ a_metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+ SlapReply *candidates = bc->candidates;
+ ber_int_t msgid;
+
+ dc.op = op;
+ dc.target = mt;
+ dc.memctx = op->o_tmpmemctx;
+ dc.to_from = MASSAGE_REQ;
+
+ asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn );
+
+ asyncmeta_set_msc_time(msc);
+ ctrls = op->o_ctrls;
+ if ( asyncmeta_controls_add( op, rs, mc, candidate, bc->is_root, &ctrls ) != LDAP_SUCCESS )
+ {
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ retcode = META_SEARCH_ERR;
+ goto done;
+ }
+ /* someone might have reset the connection */
+ if (!( LDAP_BACK_CONN_ISBOUND( msc )
+ || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+ Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+ ber = ldap_build_delete_req( msc->msc_ld, mdn.bv_val, ctrls, NULL, &msgid);
+
+ if (!ber) {
+ Debug( asyncmeta_debug, "%s asyncmeta_back_delete_start: Operation encoding failed with errno %d\n",
+ op->o_log_prefix, msc->msc_ld->ld_errno );
+ rs->sr_err = LDAP_OPERATIONS_ERROR;
+ rs->sr_text = "Failed to encode proxied request";
+ retcode = META_SEARCH_ERR;
+ goto done;
+ }
+
+ if (ber) {
+ struct timeval tv = {0, mt->mt_network_timeout*1000};
+ ber_socket_t s;
+ if (!( LDAP_BACK_CONN_ISBOUND( msc )
+ || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+ Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+
+ ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
+ if (s < 0) {
+ Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+
+ rc = ldap_int_poll( msc->msc_ld, s, &tv, 1);
+ if (rc < 0) {
+ Debug( asyncmeta_debug, "msc %p not writable within network timeout %s:%d\n", msc, __FILE__, __LINE__ );
+ if ((msc->msc_result_time + META_BACK_RESULT_INTERVAL) < slap_get_time()) {
+ rc = LDAP_SERVER_DOWN;
+ } else {
+ goto error_unavailable;
+ }
+ } else {
+ candidates[ candidate ].sr_msgid = msgid;
+ rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_DELETE,
+ mdn.bv_val, ber, msgid );
+ if (rc == msgid)
+ rc = LDAP_SUCCESS;
+ else
+ rc = LDAP_SERVER_DOWN;
+ ber = NULL;
+ }
+
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ retcode = META_SEARCH_CANDIDATE;
+ asyncmeta_set_msc_time(msc);
+ goto done;
+
+ case LDAP_SERVER_DOWN:
+ /* do not lock if called from asyncmeta_handle_bind_result. Also do not reset the connection */
+ if (do_lock > 0) {
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ asyncmeta_reset_msc(NULL, mc, candidate, 0, __FUNCTION__);
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ }
+ /* fall though*/
+ default:
+ Debug( asyncmeta_debug, "msc %p ldap_send_initial_request failed. %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+ }
+
+error_unavailable:
+ if (ber)
+ ber_free(ber, 1);
+ switch (bc->nretries[candidate]) {
+ case -1: /* nretries = forever */
+ retcode = META_SEARCH_NEED_BIND;
+ ldap_pvt_thread_yield();
+ break;
+ case 0: /* no retries left */
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ rs->sr_err = LDAP_UNAVAILABLE;
+ rs->sr_text = "Unable to send delete request to target";
+ retcode = META_SEARCH_ERR;
+ break;
+ default: /* more retries left - try to rebind and go again */
+ retcode = META_SEARCH_NEED_BIND;
+ bc->nretries[candidate]--;
+ ldap_pvt_thread_yield();
+ break;
+ }
+done:
+ (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+
+ if ( mdn.bv_val != op->o_req_dn.bv_val ) {
+ op->o_tmpfree( mdn.bv_val, op->o_tmpmemctx );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "%s <<< asyncmeta_back_delete_start[%p]=%d\n", op->o_log_prefix, msc, candidates[candidate].sr_msgid );
+ return retcode;
+}
+
+int
+asyncmeta_back_delete( Operation *op, SlapReply *rs )
+{
+ a_metainfo_t *mi = ( a_metainfo_t * )op->o_bd->be_private;
+ a_metatarget_t *mt;
+ a_metaconn_t *mc;
+ int rc, candidate = -1;
+ void *thrctx = op->o_threadctx;
+ bm_context_t *bc;
+ SlapReply *candidates;
+ time_t current_time = slap_get_time();
+
+ int max_pending_ops = (mi->mi_max_pending_ops == 0) ? META_BACK_CFG_MAX_PENDING_OPS : mi->mi_max_pending_ops;
+
+ Debug(LDAP_DEBUG_TRACE, "==> asyncmeta_back_delete: %s\n",
+ op->o_req_dn.bv_val );
+
+ if (current_time > op->o_time) {
+ Debug(asyncmeta_debug, "==> asyncmeta_back_delete[%s]: o_time:[%ld], current time: [%ld]\n",
+ op->o_log_prefix, op->o_time, current_time );
+ }
+
+ asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets, mi );
+ if (bc == NULL) {
+ rs->sr_err = LDAP_OTHER;
+ send_ldap_result(op, rs);
+ return rs->sr_err;
+ }
+
+ candidates = bc->candidates;
+ mc = asyncmeta_getconn( op, rs, candidates, &candidate, LDAP_BACK_DONTSEND, 0);
+ if ( !mc || rs->sr_err != LDAP_SUCCESS) {
+ send_ldap_result(op, rs);
+ return rs->sr_err;
+ }
+
+ mt = mi->mi_targets[ candidate ];
+ bc->timeout = mt->mt_timeout[ SLAP_OP_DELETE ];
+ bc->retrying = LDAP_BACK_RETRYING;
+ bc->sendok = ( LDAP_BACK_SENDRESULT | bc->retrying );
+ bc->stoptime = op->o_time + bc->timeout;
+ bc->bc_active = 1;
+
+ if (mc->pending_ops >= max_pending_ops) {
+ rs->sr_err = LDAP_BUSY;
+ rs->sr_text = "Maximum pending ops limit exceeded";
+ send_ldap_result(op, rs);
+ return rs->sr_err;
+ }
+
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ rc = asyncmeta_add_message_queue(mc, bc);
+ mc->mc_conns[candidate].msc_active++;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+
+ if (rc != LDAP_SUCCESS) {
+ rs->sr_err = LDAP_BUSY;
+ rs->sr_text = "Maximum pending ops limit exceeded";
+ send_ldap_result(op, rs);
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ mc->mc_conns[candidate].msc_active--;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ goto finish;
+ }
+
+retry:
+ if (bc->timeout && bc->stoptime < slap_get_time()) {
+ int timeout_err;
+ timeout_err = op->o_protocol >= LDAP_VERSION3 ?
+ LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+ rs->sr_err = timeout_err;
+ rs->sr_text = "Operation timed out before it was sent to target";
+ asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+ goto finish;
+ }
+
+ rc = asyncmeta_dobind_init_with_retry(op, rs, bc, mc, candidate);
+ switch (rc)
+ {
+ case META_SEARCH_CANDIDATE:
+ /* target is already bound, just send the request */
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_delete: "
+ "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+
+ rc = asyncmeta_back_delete_start( op, rs, mc, bc, candidate, 1);
+ if (rc == META_SEARCH_ERR) {
+ asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+ goto finish;
+
+ } else if (rc == META_SEARCH_NEED_BIND) {
+ goto retry;
+ }
+ break;
+ case META_SEARCH_NOT_CANDIDATE:
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_delete: NOT_CANDIDATE "
+ "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+ asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+ goto finish;
+
+ case META_SEARCH_NEED_BIND:
+ case META_SEARCH_BINDING:
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_delete: BINDING "
+ "cnd=\"%d\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
+ /* Todo add the context to the message queue but do not send the request
+ the receiver must send this when we are done binding */
+ /* question - how would do receiver know to which targets??? */
+ break;
+
+ case META_SEARCH_ERR:
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_delete: ERR "
+ "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+ asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+ goto finish;
+ default:
+ assert( 0 );
+ break;
+ }
+
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ mc->mc_conns[candidate].msc_active--;
+ asyncmeta_start_one_listener(mc, candidates, bc, candidate);
+ bc->bc_active--;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ rs->sr_err = SLAPD_ASYNCOP;
+finish:
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-asyncmeta/dncache.c b/servers/slapd/back-asyncmeta/dncache.c
new file mode 100644
index 0000000..a588290
--- /dev/null
+++ b/servers/slapd/back-asyncmeta/dncache.c
@@ -0,0 +1,228 @@
+/* dncache.c - dn caching for back-asyncmeta */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2016 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Symas Corporation
+ * based on back-meta module for inclusion in OpenLDAP Software.
+ * This work was sponsored by Ericsson. */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-asyncmeta.h"
+
+/*
+ * The dncache, at present, maps an entry to the target that holds it.
+ */
+
+typedef struct metadncacheentry_t {
+ struct berval dn;
+ int target;
+
+ time_t lastupdated;
+} metadncacheentry_t;
+
+/*
+ * asyncmeta_dncache_cmp
+ *
+ * compares two struct metadncacheentry; used by avl stuff
+ * FIXME: modify avl stuff to delete an entry based on cmp
+ * (e.g. when ttl expired?)
+ */
+int
+asyncmeta_dncache_cmp(
+ const void *c1,
+ const void *c2 )
+{
+ metadncacheentry_t *cc1 = ( metadncacheentry_t * )c1;
+ metadncacheentry_t *cc2 = ( metadncacheentry_t * )c2;
+
+ /*
+ * case sensitive, because the dn MUST be normalized
+ */
+ return ber_bvcmp( &cc1->dn, &cc2->dn);
+}
+
+/*
+ * asyncmeta_dncache_dup
+ *
+ * returns -1 in case a duplicate struct metadncacheentry has been inserted;
+ * used by avl stuff
+ */
+int
+asyncmeta_dncache_dup(
+ void *c1,
+ void *c2 )
+{
+ metadncacheentry_t *cc1 = ( metadncacheentry_t * )c1;
+ metadncacheentry_t *cc2 = ( metadncacheentry_t * )c2;
+
+ /*
+ * case sensitive, because the dn MUST be normalized
+ */
+ return ( ber_bvcmp( &cc1->dn, &cc2->dn ) == 0 ) ? -1 : 0;
+}
+
+/*
+ * asyncmeta_dncache_get_target
+ *
+ * returns the target a dn belongs to, or -1 in case the dn is not
+ * in the cache
+ */
+int
+asyncmeta_dncache_get_target(
+ a_metadncache_t *cache,
+ struct berval *ndn )
+{
+ metadncacheentry_t tmp_entry,
+ *entry;
+ int target = META_TARGET_NONE;
+
+ assert( cache != NULL );
+ assert( ndn != NULL );
+
+ tmp_entry.dn = *ndn;
+ ldap_pvt_thread_mutex_lock( &cache->mutex );
+ entry = ( metadncacheentry_t * )ldap_avl_find( cache->tree,
+ ( caddr_t )&tmp_entry, asyncmeta_dncache_cmp );
+
+ if ( entry != NULL ) {
+
+ /*
+ * if cache->ttl < 0, cache never expires;
+ * if cache->ttl = 0 no cache is used; shouldn't get here
+ * else, cache is used with ttl
+ */
+ if ( cache->ttl < 0 ) {
+ target = entry->target;
+
+ } else {
+ if ( entry->lastupdated+cache->ttl > slap_get_time() ) {
+ target = entry->target;
+ }
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &cache->mutex );
+
+ return target;
+}
+
+/*
+ * asyncmeta_dncache_update_entry
+ *
+ * updates target and lastupdated of a struct metadncacheentry if exists,
+ * otherwise it gets created; returns -1 in case of error
+ */
+int
+asyncmeta_dncache_update_entry(
+ a_metadncache_t *cache,
+ struct berval *ndn,
+ int target )
+{
+ metadncacheentry_t *entry,
+ tmp_entry;
+ time_t curr_time = 0L;
+ int err = 0;
+
+ assert( cache != NULL );
+ assert( ndn != NULL );
+
+ /*
+ * if cache->ttl < 0, cache never expires;
+ * if cache->ttl = 0 no cache is used; shouldn't get here
+ * else, cache is used with ttl
+ */
+ if ( cache->ttl > 0 ) {
+ curr_time = slap_get_time();
+ }
+
+ tmp_entry.dn = *ndn;
+
+ ldap_pvt_thread_mutex_lock( &cache->mutex );
+ entry = ( metadncacheentry_t * )ldap_avl_find( cache->tree,
+ ( caddr_t )&tmp_entry, asyncmeta_dncache_cmp );
+
+ if ( entry != NULL ) {
+ entry->target = target;
+ entry->lastupdated = curr_time;
+
+ } else {
+ entry = ch_malloc( sizeof( metadncacheentry_t ) + ndn->bv_len + 1 );
+ if ( entry == NULL ) {
+ err = -1;
+ goto error_return;
+ }
+
+ entry->dn.bv_len = ndn->bv_len;
+ entry->dn.bv_val = (char *)&entry[ 1 ];
+ AC_MEMCPY( entry->dn.bv_val, ndn->bv_val, ndn->bv_len );
+ entry->dn.bv_val[ ndn->bv_len ] = '\0';
+
+ entry->target = target;
+ entry->lastupdated = curr_time;
+
+ err = ldap_avl_insert( &cache->tree, ( caddr_t )entry,
+ asyncmeta_dncache_cmp, asyncmeta_dncache_dup );
+ }
+
+error_return:;
+ ldap_pvt_thread_mutex_unlock( &cache->mutex );
+
+ return err;
+}
+
+int
+asyncmeta_dncache_delete_entry(
+ a_metadncache_t *cache,
+ struct berval *ndn )
+{
+ metadncacheentry_t *entry,
+ tmp_entry;
+
+ assert( cache != NULL );
+ assert( ndn != NULL );
+
+ tmp_entry.dn = *ndn;
+
+ ldap_pvt_thread_mutex_lock( &cache->mutex );
+ entry = ldap_avl_delete( &cache->tree, ( caddr_t )&tmp_entry,
+ asyncmeta_dncache_cmp );
+ ldap_pvt_thread_mutex_unlock( &cache->mutex );
+
+ if ( entry != NULL ) {
+ asyncmeta_dncache_free( ( void * )entry );
+ }
+
+ return 0;
+}
+
+/*
+ * meta_dncache_free
+ *
+ * frees an entry
+ *
+ */
+void
+asyncmeta_dncache_free(
+ void *e )
+{
+ free( e );
+}
diff --git a/servers/slapd/back-asyncmeta/init.c b/servers/slapd/back-asyncmeta/init.c
new file mode 100644
index 0000000..5324907
--- /dev/null
+++ b/servers/slapd/back-asyncmeta/init.c
@@ -0,0 +1,468 @@
+/* init.c - initialization of a back-asyncmeta database */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2016 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Symas Corporation
+ * based on back-meta module for inclusion in OpenLDAP Software.
+ * This work was sponsored by Ericsson. */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-asyncmeta.h"
+
+int asyncmeta_debug;
+
+int
+asyncmeta_back_open(
+ BackendInfo *bi )
+{
+ /* FIXME: need to remove the pagedResults, and likely more... */
+ bi->bi_controls = slap_known_controls;
+
+ return 0;
+}
+
+int
+asyncmeta_back_initialize(
+ BackendInfo *bi )
+{
+ int rc;
+ struct berval debugbv = BER_BVC("asyncmeta");
+
+ rc = slap_loglevel_get( &debugbv, &asyncmeta_debug );
+ if ( rc ) {
+ return rc;
+ }
+
+ bi->bi_flags =
+#if 0
+ /* this is not (yet) set essentially because back-meta does not
+ * directly support extended operations... */
+#ifdef LDAP_DYNAMIC_OBJECTS
+ /* this is set because all the support a proxy has to provide
+ * is the capability to forward the refresh exop, and to
+ * pass thru entries that contain the dynamicObject class
+ * and the entryTtl attribute */
+ SLAP_BFLAG_DYNAMIC |
+#endif /* LDAP_DYNAMIC_OBJECTS */
+#endif
+
+ /* back-meta recognizes RFC4525 increment;
+ * let the remote server complain, if needed (ITS#5912) */
+ SLAP_BFLAG_INCREMENT;
+
+ bi->bi_open = asyncmeta_back_open;
+ bi->bi_config = 0;
+ bi->bi_close = 0;
+ bi->bi_destroy = 0;
+
+ bi->bi_db_init = asyncmeta_back_db_init;
+ bi->bi_db_config = config_generic_wrapper;
+ bi->bi_db_open = asyncmeta_back_db_open;
+ bi->bi_db_close = asyncmeta_back_db_close;
+ bi->bi_db_destroy = asyncmeta_back_db_destroy;
+
+ bi->bi_op_bind = asyncmeta_back_bind;
+ bi->bi_op_unbind = 0;
+ bi->bi_op_search = asyncmeta_back_search;
+ bi->bi_op_compare = asyncmeta_back_compare;
+ bi->bi_op_modify = asyncmeta_back_modify;
+ bi->bi_op_modrdn = asyncmeta_back_modrdn;
+ bi->bi_op_add = asyncmeta_back_add;
+ bi->bi_op_delete = asyncmeta_back_delete;
+ bi->bi_op_abandon = 0;
+
+ bi->bi_extended = 0;
+
+ bi->bi_chk_referrals = 0;
+
+ bi->bi_connection_init = 0;
+ bi->bi_connection_destroy = 0 /* asyncmeta_back_conn_destroy */;
+
+ return asyncmeta_back_init_cf( bi );
+}
+
+int
+asyncmeta_back_db_init(
+ Backend *be,
+ ConfigReply *cr)
+{
+ a_metainfo_t *mi;
+ int i;
+ BackendInfo *bi;
+
+ bi = backend_info( "ldap" );
+ if ( !bi || !bi->bi_extra ) {
+ Debug( LDAP_DEBUG_ANY,
+ "asyncmeta_back_db_init: needs back-ldap\n" );
+ return 1;
+ }
+
+ mi = ch_calloc( 1, sizeof( a_metainfo_t ) );
+ if ( mi == NULL ) {
+ return -1;
+ }
+
+ /* set default flags */
+ mi->mi_flags =
+ META_BACK_F_DEFER_ROOTDN_BIND
+ | META_BACK_F_PROXYAUTHZ_ALWAYS
+ | META_BACK_F_PROXYAUTHZ_ANON
+ | META_BACK_F_PROXYAUTHZ_NOANON;
+
+ /*
+ * At present the default is no default target;
+ * this may change
+ */
+ mi->mi_defaulttarget = META_DEFAULT_TARGET_NONE;
+ mi->mi_bind_timeout.tv_sec = 0;
+ mi->mi_bind_timeout.tv_usec = META_BIND_TIMEOUT;
+
+ mi->mi_rebind_f = asyncmeta_back_default_rebind;
+ mi->mi_urllist_f = asyncmeta_back_default_urllist;
+
+ ldap_pvt_thread_mutex_init( &mi->mi_cache.mutex );
+
+ /* safe default */
+ mi->mi_nretries = META_RETRY_DEFAULT;
+ mi->mi_version = LDAP_VERSION3;
+
+ for ( i = 0; i < SLAP_OP_LAST; i++ ) {
+ mi->mi_timeout[ i ] = META_BACK_CFG_DEFAULT_OPS_TIMEOUT;
+ }
+
+ for ( i = LDAP_BACK_PCONN_FIRST; i < LDAP_BACK_PCONN_LAST; i++ ) {
+ mi->mi_conn_priv[ i ].mic_num = 0;
+ LDAP_TAILQ_INIT( &mi->mi_conn_priv[ i ].mic_priv );
+ }
+ mi->mi_conn_priv_max = LDAP_BACK_CONN_PRIV_DEFAULT;
+
+ mi->mi_ldap_extra = (ldap_extra_t *)bi->bi_extra;
+ ldap_pvt_thread_mutex_init( &mi->mi_mc_mutex);
+
+ be->be_private = mi;
+ be->be_cf_ocs = be->bd_info->bi_cf_ocs;
+
+ return 0;
+}
+
+int
+asyncmeta_target_finish(
+ a_metainfo_t *mi,
+ a_metatarget_t *mt,
+ const char *log,
+ char *msg,
+ size_t msize
+)
+{
+ slap_bindconf sb = { BER_BVNULL };
+ int rc;
+
+ ber_str2bv( mt->mt_uri, 0, 0, &sb.sb_uri );
+ sb.sb_version = mt->mt_version;
+ sb.sb_method = LDAP_AUTH_SIMPLE;
+ BER_BVSTR( &sb.sb_binddn, "" );
+
+ if ( META_BACK_TGT_T_F_DISCOVER( mt ) ) {
+ rc = slap_discover_feature( &sb,
+ slap_schema.si_ad_supportedFeatures->ad_cname.bv_val,
+ LDAP_FEATURE_ABSOLUTE_FILTERS );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ mt->mt_flags |= LDAP_BACK_F_T_F;
+ }
+ }
+
+ if ( META_BACK_TGT_CANCEL_DISCOVER( mt ) ) {
+ rc = slap_discover_feature( &sb,
+ slap_schema.si_ad_supportedExtension->ad_cname.bv_val,
+ LDAP_EXOP_CANCEL );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ mt->mt_flags |= LDAP_BACK_F_CANCEL_EXOP;
+ }
+ }
+
+ if ( !( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE )
+ || mt->mt_idassert_authz != NULL )
+ {
+ mi->mi_flags &= ~META_BACK_F_PROXYAUTHZ_ALWAYS;
+ }
+
+ if ( ( mt->mt_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL )
+ && !( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) )
+ {
+ Debug(LDAP_DEBUG_ANY,
+ "%s: inconsistent idassert configuration " "(likely authz=\"*\" used with \"non-prescriptive\" flag) (target %s)\n",
+ log, mt->mt_uri );
+ return 1;
+ }
+
+ if ( !( mt->mt_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) )
+ {
+ mi->mi_flags &= ~META_BACK_F_PROXYAUTHZ_ANON;
+ }
+
+ if ( ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) )
+ {
+ mi->mi_flags &= ~META_BACK_F_PROXYAUTHZ_NOANON;
+ }
+
+ return 0;
+}
+
+int
+asyncmeta_back_db_open(
+ Backend *be,
+ ConfigReply *cr )
+{
+ a_metainfo_t *mi = (a_metainfo_t *)be->be_private;
+ char msg[SLAP_TEXT_BUFLEN];
+ int i;
+
+ if ( mi->mi_ntargets == 0 ) {
+ /* Dynamically added, nothing to check here until
+ * some targets get added
+ */
+ if ( slapMode & SLAP_SERVER_RUNNING )
+ return 0;
+
+ Debug( LDAP_DEBUG_ANY,
+ "asyncmeta_back_db_open: no targets defined\n" );
+ return 1;
+ }
+ mi->mi_num_conns = 0;
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ a_metatarget_t *mt = mi->mi_targets[ i ];
+ if ( asyncmeta_target_finish( mi, mt,
+ "asyncmeta_back_db_open", msg, sizeof( msg )))
+ return 1;
+ }
+ mi->mi_num_conns = (mi->mi_max_target_conns == 0) ? META_BACK_CFG_MAX_TARGET_CONNS : mi->mi_max_target_conns;
+ assert(mi->mi_num_conns > 0);
+ mi->mi_conns = ch_calloc( mi->mi_num_conns, sizeof( a_metaconn_t ));
+ for (i = 0; i < mi->mi_num_conns; i++) {
+ a_metaconn_t *mc = &mi->mi_conns[i];
+ ldap_pvt_thread_mutex_init( &mc->mc_om_mutex);
+ mc->mc_authz_target = META_BOUND_NONE;
+ mc->mc_conns = ch_calloc( mi->mi_ntargets, sizeof( a_metasingleconn_t ));
+ mc->mc_info = mi;
+ LDAP_STAILQ_INIT( &mc->mc_om_list );
+ }
+ mi->mi_suffix = be->be_suffix[0];
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ mi->mi_task = ldap_pvt_runqueue_insert( &slapd_rq, 1,
+ asyncmeta_timeout_loop, mi, "asyncmeta_timeout_loop", mi->mi_suffix.bv_val );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ return 0;
+}
+
+/*
+ * asyncmeta_back_conn_free()
+ *
+ * actually frees a connection; the reference count must be 0,
+ * and it must not (or no longer) be in the cache.
+ */
+void
+asyncmeta_back_conn_free(
+ void *v_mc )
+{
+ a_metaconn_t *mc = v_mc;
+
+ assert( mc != NULL );
+ ldap_pvt_thread_mutex_destroy( &mc->mc_om_mutex );
+ free( mc );
+}
+
+static void
+asyncmeta_back_stop_miconns( a_metainfo_t *mi )
+{
+
+ /*Todo do any other mc cleanup here if necessary*/
+}
+
+static void
+asyncmeta_back_clear_miconns( a_metainfo_t *mi )
+{
+ int i, j;
+ a_metaconn_t *mc;
+ for (i = 0; i < mi->mi_num_conns; i++) {
+ mc = &mi->mi_conns[i];
+ /* todo clear the message queue */
+ for (j = 0; j < mi->mi_ntargets; j ++) {
+ asyncmeta_clear_one_msc(NULL, mc, j, 1, __FUNCTION__);
+ }
+ free(mc->mc_conns);
+ ldap_pvt_thread_mutex_destroy( &mc->mc_om_mutex );
+ }
+ free(mi->mi_conns);
+}
+
+static void
+asyncmeta_target_free(
+ a_metatarget_t *mt )
+{
+ if ( mt->mt_uri ) {
+ free( mt->mt_uri );
+ ldap_pvt_thread_mutex_destroy( &mt->mt_uri_mutex );
+ }
+ if ( mt->mt_subtree ) {
+ asyncmeta_subtree_destroy( mt->mt_subtree );
+ mt->mt_subtree = NULL;
+ }
+ if ( mt->mt_filter ) {
+ asyncmeta_filter_destroy( mt->mt_filter );
+ mt->mt_filter = NULL;
+ }
+ if ( !BER_BVISNULL( &mt->mt_psuffix ) ) {
+ free( mt->mt_psuffix.bv_val );
+ }
+ if ( !BER_BVISNULL( &mt->mt_nsuffix ) ) {
+ free( mt->mt_nsuffix.bv_val );
+ }
+ if ( !BER_BVISNULL( &mt->mt_binddn ) ) {
+ free( mt->mt_binddn.bv_val );
+ }
+ if ( !BER_BVISNULL( &mt->mt_bindpw ) ) {
+ free( mt->mt_bindpw.bv_val );
+ }
+ if ( !BER_BVISNULL( &mt->mt_idassert_authcID ) ) {
+ ch_free( mt->mt_idassert_authcID.bv_val );
+ }
+ if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) ) {
+ ch_free( mt->mt_idassert_authcDN.bv_val );
+ }
+ if ( !BER_BVISNULL( &mt->mt_idassert_passwd ) ) {
+ ch_free( mt->mt_idassert_passwd.bv_val );
+ }
+ if ( !BER_BVISNULL( &mt->mt_idassert_authzID ) ) {
+ ch_free( mt->mt_idassert_authzID.bv_val );
+ }
+ if ( !BER_BVISNULL( &mt->mt_idassert_sasl_mech ) ) {
+ ch_free( mt->mt_idassert_sasl_mech.bv_val );
+ }
+ if ( !BER_BVISNULL( &mt->mt_idassert_sasl_realm ) ) {
+ ch_free( mt->mt_idassert_sasl_realm.bv_val );
+ }
+ if ( mt->mt_idassert_authz != NULL ) {
+ ber_bvarray_free( mt->mt_idassert_authz );
+ }
+ if ( !BER_BVISNULL( &mt->mt_lsuffixm )) {
+ ch_free( mt->mt_lsuffixm.bv_val );
+ }
+ if ( !BER_BVISNULL( &mt->mt_rsuffixm )) {
+ ch_free( mt->mt_rsuffixm.bv_val );
+ }
+ free( mt );
+}
+
+int
+asyncmeta_back_db_close(
+ Backend *be,
+ ConfigReply *cr )
+{
+ a_metainfo_t *mi;
+
+ if ( be->be_private ) {
+ mi = ( a_metainfo_t * )be->be_private;
+ if ( mi->mi_task != NULL ) {
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, mi->mi_task )) {
+ ldap_pvt_runqueue_stoptask( &slapd_rq, mi->mi_task);
+ }
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ mi->mi_task = NULL;
+ }
+ ldap_pvt_thread_mutex_lock( &mi->mi_mc_mutex );
+ asyncmeta_back_stop_miconns( mi );
+ ldap_pvt_thread_mutex_unlock( &mi->mi_mc_mutex );
+ }
+ return 0;
+}
+
+int
+asyncmeta_back_db_destroy(
+ Backend *be,
+ ConfigReply *cr )
+{
+ a_metainfo_t *mi;
+
+ if ( be->be_private ) {
+ int i;
+
+ mi = ( a_metainfo_t * )be->be_private;
+ /*
+ * Destroy the per-target stuff (assuming there's at
+ * least one ...)
+ */
+ if ( mi->mi_targets != NULL ) {
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ a_metatarget_t *mt = mi->mi_targets[ i ];
+
+ if ( META_BACK_TGT_QUARANTINE( mt ) ) {
+ if ( mt->mt_quarantine.ri_num != mi->mi_quarantine.ri_num )
+ {
+ mi->mi_ldap_extra->retry_info_destroy( &mt->mt_quarantine );
+ }
+
+ ldap_pvt_thread_mutex_destroy( &mt->mt_quarantine_mutex );
+ }
+
+ asyncmeta_target_free( mt );
+ }
+
+ free( mi->mi_targets );
+ }
+
+ ldap_pvt_thread_mutex_lock( &mi->mi_cache.mutex );
+ if ( mi->mi_cache.tree ) {
+ ldap_avl_free( mi->mi_cache.tree, asyncmeta_dncache_free );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &mi->mi_cache.mutex );
+ ldap_pvt_thread_mutex_destroy( &mi->mi_cache.mutex );
+
+ if ( mi->mi_candidates != NULL ) {
+ ber_memfree_x( mi->mi_candidates, NULL );
+ }
+
+ if ( META_BACK_QUARANTINE( mi ) ) {
+ mi->mi_ldap_extra->retry_info_destroy( &mi->mi_quarantine );
+ }
+
+ ldap_pvt_thread_mutex_lock( &mi->mi_mc_mutex );
+ asyncmeta_back_clear_miconns(mi);
+ ldap_pvt_thread_mutex_unlock( &mi->mi_mc_mutex );
+ ldap_pvt_thread_mutex_destroy( &mi->mi_mc_mutex );
+
+ free( be->be_private );
+ }
+ return 0;
+}
+
+#if SLAPD_ASYNCMETA == SLAPD_MOD_DYNAMIC
+
+/* conditionally define the init_module() function */
+SLAP_BACKEND_INIT_MODULE( asyncmeta )
+
+#endif /* SLAPD_ASYNCMETA == SLAPD_MOD_DYNAMIC */
diff --git a/servers/slapd/back-asyncmeta/map.c b/servers/slapd/back-asyncmeta/map.c
new file mode 100644
index 0000000..b811708
--- /dev/null
+++ b/servers/slapd/back-asyncmeta/map.c
@@ -0,0 +1,214 @@
+/* map.c - ldap backend mapping routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2016 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Symas Corporation
+ * based on back-meta module for inclusion in OpenLDAP Software.
+ * This work was sponsored by Ericsson. */
+
+/* This is an altered version */
+/*
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Since few users ever read sources,
+ * credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software. Since few users
+ * ever read sources, credits should appear in the documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ *
+ *
+ * Copyright 2016, Symas Corporation
+ *
+ * This is based on the back-meta/map.c version by Pierangelo Masarati.
+ * The previously reported conditions apply to the modified code as well.
+ * Changes in the original code are highlighted where required.
+ * Credits for the original code go to the author, Howard Chu.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "lutil.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-asyncmeta.h"
+
+void
+asyncmeta_referral_result_rewrite(
+ a_dncookie *dc,
+ BerVarray a_vals
+)
+{
+ int i, last;
+
+ assert( dc != NULL );
+ assert( a_vals != NULL );
+
+ for ( last = 0; !BER_BVISNULL( &a_vals[ last ] ); last++ )
+ ;
+ last--;
+
+ for ( i = 0; !BER_BVISNULL( &a_vals[ i ] ); i++ ) {
+ struct berval dn,
+ olddn = BER_BVNULL;
+ int rc;
+ LDAPURLDesc *ludp;
+
+ rc = ldap_url_parse( a_vals[ i ].bv_val, &ludp );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ /* leave attr untouched if massage failed */
+ continue;
+ }
+
+ /* FIXME: URLs like "ldap:///dc=suffix" if passed
+ * thru ldap_url_parse() and ldap_url_desc2str()
+ * get rewritten as "ldap:///dc=suffix??base";
+ * we don't want this to occur... */
+ if ( ludp->lud_scope == LDAP_SCOPE_BASE ) {
+ ludp->lud_scope = LDAP_SCOPE_DEFAULT;
+ }
+
+ ber_str2bv( ludp->lud_dn, 0, 0, &olddn );
+
+ asyncmeta_dn_massage( dc, &olddn, &dn );
+ /* leave attr untouched if massage did nothing */
+ if ( olddn.bv_val != dn.bv_val )
+ {
+ char *newurl;
+
+ ludp->lud_dn = dn.bv_val;
+ newurl = ldap_url_desc2str( ludp );
+ dc->op->o_tmpfree( dn.bv_val, dc->memctx );
+ if ( newurl )
+ {
+ /* FIXME: leave attr untouched
+ * even if ldap_url_desc2str failed...
+ */
+
+ ber_memfree_x( a_vals[ i ].bv_val, dc->op->o_tmpmemctx );
+ ber_str2bv_x( newurl, 0, 1, &a_vals[ i ], dc->memctx );
+ ber_memfree( newurl );
+ ludp->lud_dn = olddn.bv_val;
+ }
+ }
+ ldap_free_urldesc( ludp );
+ }
+}
+
+void
+asyncmeta_dnattr_result_rewrite(
+ a_dncookie *dc,
+ BerVarray a_vals
+)
+{
+ struct berval bv;
+ int i;
+
+ assert( a_vals != NULL );
+
+ for ( i = 0; !BER_BVISNULL( &a_vals[i] ); i++ ) {
+ asyncmeta_dn_massage( dc, &a_vals[i], &bv );
+ if ( bv.bv_val != a_vals[i].bv_val ) {
+ ber_memfree_x( a_vals[i].bv_val, dc->memctx );
+ a_vals[i] = bv;
+ }
+ }
+}
+
+/*
+ * asyncmeta_dn_massage
+ *
+ * Aliases the suffix.
+ */
+void
+asyncmeta_dn_massage(
+ a_dncookie *dc,
+ struct berval *odn,
+ struct berval *res
+)
+{
+ struct berval pretty = {0,NULL}, *dn = odn;
+ struct berval *osuff, *nsuff;
+ int diff;
+
+ assert( res );
+
+ BER_BVZERO(res);
+ if ( dn == NULL )
+ return;
+
+ /* no suffix massage configured */
+ if ( !dc->target->mt_lsuffixm.bv_val ) {
+ *res = *dn;
+ return;
+ }
+
+ if ( dc->to_from == MASSAGE_REQ ) {
+ osuff = &dc->target->mt_lsuffixm;
+ nsuff = &dc->target->mt_rsuffixm;
+ } else {
+ osuff = &dc->target->mt_rsuffixm;
+ nsuff = &dc->target->mt_lsuffixm;
+ /* DN from remote server may be in arbitrary form.
+ * Pretty it so we can parse reliably.
+ */
+ dnPretty( NULL, dn, &pretty, dc->op->o_tmpmemctx );
+ if (pretty.bv_val) dn = &pretty;
+ }
+
+ diff = dn->bv_len - osuff->bv_len;
+ /* DN is shorter than suffix - ignore */
+ if ( diff < 0 ) {
+ignore:
+ *res = *odn;
+ if (pretty.bv_val)
+ dc->op->o_tmpfree( pretty.bv_val, dc->op->o_tmpmemctx );
+ return;
+ }
+
+ /* DN longer than our suffix and doesn't match */
+ if ( diff > 0 && !DN_SEPARATOR(dn->bv_val[diff-1]))
+ goto ignore;
+
+ /* suffix is same length as ours, but doesn't match */
+ if ( strcasecmp( osuff->bv_val, &dn->bv_val[diff] ))
+ goto ignore;
+
+ res->bv_len = diff + nsuff->bv_len;
+ res->bv_val = dc->op->o_tmpalloc( res->bv_len + 1, dc->memctx );
+ strncpy( res->bv_val, dn->bv_val, diff );
+ strcpy( &res->bv_val[diff], nsuff->bv_val );
+
+ if (pretty.bv_val)
+ dc->op->o_tmpfree( pretty.bv_val, dc->op->o_tmpmemctx );
+}
diff --git a/servers/slapd/back-asyncmeta/message_queue.c b/servers/slapd/back-asyncmeta/message_queue.c
new file mode 100644
index 0000000..f2e9c86
--- /dev/null
+++ b/servers/slapd/back-asyncmeta/message_queue.c
@@ -0,0 +1,236 @@
+/* message_queue.c - routines to maintain the per-connection lists
+ * of pending operations */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2016 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Symas Corporation
+ * based on back-meta module for inclusion in OpenLDAP Software.
+ * This work was sponsored by Ericsson. */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "lutil.h"
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-asyncmeta.h"
+#include "../../../libraries/liblber/lber-int.h"
+#include "lutil.h"
+
+
+typedef struct listptr {
+ void *reserved;
+ struct listptr *next;
+} listptr;
+
+typedef struct listhead {
+ struct listptr *list;
+ int cnt;
+} listhead;
+
+#ifndef LH_MAX
+#define LH_MAX 16
+#endif
+
+static void asyncmeta_memctx_put(void *threadctx, void *memctx)
+{
+ slap_sl_mem_setctx(threadctx, NULL);
+ slap_sl_mem_destroy((void *)1, memctx);
+}
+
+int asyncmeta_new_bm_context(Operation *op,
+ SlapReply *rs,
+ bm_context_t **new_bc,
+ int ntargets,
+ a_metainfo_t *mi)
+{
+ int i;
+ *new_bc = op->o_tmpcalloc( 1, sizeof( bm_context_t ), op->o_tmpmemctx );
+
+ (*new_bc)->op = op;
+ (*new_bc)->copy_op = *op;
+ (*new_bc)->candidates = op->o_tmpcalloc(ntargets, sizeof(SlapReply),op->o_tmpmemctx);
+ (*new_bc)->msgids = op->o_tmpcalloc(ntargets, sizeof(int),op->o_tmpmemctx);
+ (*new_bc)->nretries = op->o_tmpcalloc(ntargets, sizeof(int),op->o_tmpmemctx);
+ (*new_bc)->c_peer_name = op->o_conn->c_peer_name;
+ (*new_bc)->is_root = be_isroot( op );
+
+ switch(op->o_tag) {
+ case LDAP_REQ_COMPARE:
+ {
+ AttributeAssertion *ava = op->o_tmpcalloc( 1, sizeof(AttributeAssertion), op->o_tmpmemctx );
+ *ava = *op->orc_ava;
+ op->orc_ava = ava;
+ }
+ break;
+ case LDAP_REQ_MODRDN:
+ if (op->orr_newSup != NULL) {
+ struct berval *bv = op->o_tmpalloc( sizeof( struct berval ), op->o_tmpmemctx );
+ *bv = *op->orr_newSup;
+ op->orr_newSup = bv;
+ }
+
+ if (op->orr_nnewSup != NULL) {
+ struct berval *bv = op->o_tmpalloc( sizeof( struct berval ), op->o_tmpmemctx );
+ *bv = *op->orr_nnewSup;
+ op->orr_nnewSup = bv;
+ }
+ break;
+ default:
+ break;
+ }
+ for (i = 0; i < ntargets; i++) {
+ (*new_bc)->msgids[i] = META_MSGID_UNDEFINED;
+ }
+ for (i = 0; i < ntargets; i++) {
+ (*new_bc)->nretries[i] = mi->mi_targets[i]->mt_nretries;
+ }
+ return LDAP_SUCCESS;
+}
+
+void asyncmeta_free_op(Operation *op)
+{
+ assert (op != NULL);
+ switch (op->o_tag) {
+ case LDAP_REQ_SEARCH:
+ break;
+ case LDAP_REQ_ADD:
+ if ( op->ora_modlist != NULL ) {
+ slap_mods_free(op->ora_modlist, 0 );
+ }
+
+ if ( op->ora_e != NULL ) {
+ entry_free( op->ora_e );
+ }
+
+ break;
+ case LDAP_REQ_MODIFY:
+ if ( op->orm_modlist != NULL ) {
+ slap_mods_free(op->orm_modlist, 1 );
+ }
+ break;
+ case LDAP_REQ_MODRDN:
+ if ( op->orr_modlist != NULL ) {
+ slap_mods_free(op->orr_modlist, 1 );
+ }
+ break;
+ case LDAP_REQ_COMPARE:
+ break;
+ case LDAP_REQ_DELETE:
+ break;
+ default:
+ Debug( LDAP_DEBUG_TRACE, "==> asyncmeta_free_op : other message type" );
+ }
+
+ connection_op_finish( op );
+ slap_op_free( op, op->o_threadctx );
+}
+
+
+
+
+void asyncmeta_clear_bm_context(bm_context_t *bc)
+{
+
+ Operation *op = bc->op;
+ void *thrctx, *memctx;
+ int i;
+
+ if ( bc->bc_mc && bc->bc_mc->mc_info ) {
+ for (i = 0; i < bc->bc_mc->mc_info->mi_ntargets; i++) {
+ if (bc->candidates[ i ].sr_text != NULL) {
+ ch_free( (char *)bc->candidates[ i ].sr_text );
+ bc->candidates[ i ].sr_text = NULL;
+ }
+ }
+ }
+
+ if (op->o_conn->c_conn_idx == -1)
+ return;
+ memctx = op->o_tmpmemctx;
+ thrctx = op->o_threadctx;
+ while (op->o_bd == bc->copy_op.o_bd)
+ ldap_pvt_thread_yield();
+ asyncmeta_free_op(op);
+ asyncmeta_memctx_put(thrctx, memctx);
+}
+
+int asyncmeta_add_message_queue(a_metaconn_t *mc, bm_context_t *bc)
+{
+ a_metainfo_t *mi = mc->mc_info;
+ int max_pending_ops = (mi->mi_max_pending_ops == 0) ? META_BACK_CFG_MAX_PENDING_OPS : mi->mi_max_pending_ops;
+
+ Debug( LDAP_DEBUG_TRACE, "add_message_queue: mc %p, pending_ops %d, max_pending %d\n",
+ mc, mc->pending_ops, max_pending_ops );
+
+ assert(bc->bc_mc == NULL);
+ if (mc->pending_ops >= max_pending_ops) {
+ return LDAP_BUSY;
+ }
+ bc->bc_mc = mc;
+
+ slap_sl_mem_setctx(bc->op->o_threadctx, NULL);
+ LDAP_STAILQ_INSERT_TAIL( &mc->mc_om_list, bc, bc_next);
+ mc->pending_ops++;
+ return LDAP_SUCCESS;
+}
+
+
+void
+asyncmeta_drop_bc(a_metaconn_t *mc, bm_context_t *bc)
+{
+ bm_context_t *om;
+ LDAP_STAILQ_FOREACH( om, &mc->mc_om_list, bc_next ) {
+ if (om == bc) {
+ LDAP_STAILQ_REMOVE(&mc->mc_om_list, om, bm_context_t, bc_next);
+ mc->pending_ops--;
+ break;
+ }
+ }
+ assert(om == bc);
+ assert(bc->bc_mc == mc);
+}
+
+
+bm_context_t *
+asyncmeta_find_message(ber_int_t msgid, a_metaconn_t *mc, int candidate)
+{
+ bm_context_t *om;
+ LDAP_STAILQ_FOREACH( om, &mc->mc_om_list, bc_next ) {
+ if (om->candidates[candidate].sr_msgid == msgid && !om->bc_invalid) {
+ break;
+ }
+ }
+ return om;
+}
+
+bm_context_t *
+asyncmeta_bc_in_queue(a_metaconn_t *mc, bm_context_t *bc)
+{
+ bm_context_t *om;
+ LDAP_STAILQ_FOREACH( om, &mc->mc_om_list, bc_next ) {
+ if (om == bc) {
+ return bc;
+ }
+ }
+ return NULL;
+}
diff --git a/servers/slapd/back-asyncmeta/meta_result.c b/servers/slapd/back-asyncmeta/meta_result.c
new file mode 100644
index 0000000..0ce279a
--- /dev/null
+++ b/servers/slapd/back-asyncmeta/meta_result.c
@@ -0,0 +1,1825 @@
+/* meta_result.c - target responses processing */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2016 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Symas Corporation
+ * based on back-meta module for inclusion in OpenLDAP Software.
+ * This work was sponsored by Ericsson. */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-asyncmeta.h"
+#include "ldap_rq.h"
+#include "../../../libraries/liblber/lber-int.h"
+
+static void
+asyncmeta_send_ldap_result(bm_context_t *bc, Operation *op, SlapReply *rs)
+{
+ if (bc->c_peer_name.bv_val == op->o_conn->c_peer_name.bv_val && !bc->op->o_abandon ) {
+ send_ldap_result(&bc->copy_op, rs);
+ bc->op->o_callback = bc->copy_op.o_callback;
+ bc->op->o_extra = bc->copy_op.o_extra;
+ bc->op->o_ctrls = bc->copy_op.o_ctrls;
+ }
+}
+
+static int
+asyncmeta_is_last_result(a_metaconn_t *mc, bm_context_t *bc, int candidate)
+{
+ a_metainfo_t *mi = mc->mc_info;
+ int i;
+ SlapReply *candidates = bc->candidates;
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
+ continue;
+ }
+ if (candidates[ i ].sr_msgid != META_MSGID_IGNORE ||
+ candidates[ i ].sr_type != REP_RESULT) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+meta_search_candidate_t
+asyncmeta_dobind_result(
+ a_metaconn_t *mc,
+ int candidate,
+ SlapReply *bind_result,
+ LDAPMessage *res )
+{
+ a_metainfo_t *mi = mc->mc_info;
+ a_metatarget_t *mt = mi->mi_targets[ candidate ];
+ a_metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+
+ meta_search_candidate_t retcode = META_SEARCH_NOT_CANDIDATE;
+ int rc;
+
+ assert( msc->msc_ldr != NULL );
+
+ if ( mi->mi_idle_timeout != 0 ) {
+ asyncmeta_set_msc_time(msc);
+ }
+
+ if ( LogTest( asyncmeta_debug ) ) {
+ char time_buf[ SLAP_TEXT_BUFLEN ];
+ asyncmeta_get_timestamp(time_buf);
+ Debug( asyncmeta_debug, "[%x] [%s] asyncmeta_dobind_result msc: %p, "
+ "msc->msc_binding_time: %x, msc->msc_flags:%x\n ",
+ (unsigned int)slap_get_time(), time_buf, msc,
+ (unsigned int)msc->msc_binding_time, msc->msc_mscflags );
+ }
+ /* FIXME: matched? referrals? response controls? */
+ rc = ldap_parse_result( msc->msc_ldr, res,
+ &(bind_result->sr_err),
+ (char **)&(bind_result->sr_matched),
+ (char **)&(bind_result->sr_text),
+ NULL, NULL, 0 );
+
+ if ( LogTest( asyncmeta_debug ) ) {
+ char time_buf[ SLAP_TEXT_BUFLEN ];
+ asyncmeta_get_timestamp(time_buf);
+ Debug( asyncmeta_debug,
+ "[%s] asyncmeta_dobind_result error=%d msc: %p\n",
+ time_buf,bind_result->sr_err, msc );
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ bind_result->sr_err = rc;
+ }
+ rc = slap_map_api2result( bind_result );
+
+ LDAP_BACK_CONN_BINDING_CLEAR( msc );
+ if ( rc != LDAP_SUCCESS ) {
+ bind_result->sr_err = rc;
+ } else {
+ /* FIXME: check if bound as idassert authcDN! */
+ if ( BER_BVISNULL( &msc->msc_bound_ndn )
+ || BER_BVISEMPTY( &msc->msc_bound_ndn ) )
+ {
+ LDAP_BACK_CONN_ISANON_SET( msc );
+ if ( LogTest( asyncmeta_debug ) ) {
+ char time_buf[ SLAP_TEXT_BUFLEN ];
+ asyncmeta_get_timestamp(time_buf);
+ Debug( asyncmeta_debug, "[%s] asyncmeta_dobind_result anonymous msc: %p\n",
+ time_buf, msc );
+ }
+
+ } else {
+ if ( META_BACK_TGT_SAVECRED( mt ) &&
+ !BER_BVISNULL( &msc->msc_cred ) &&
+ !BER_BVISEMPTY( &msc->msc_cred ) )
+ {
+ ldap_set_rebind_proc( msc->msc_ldr, mt->mt_rebind_f, msc );
+ }
+ if ( LogTest( asyncmeta_debug ) ) {
+ char time_buf[ SLAP_TEXT_BUFLEN ];
+ asyncmeta_get_timestamp(time_buf);
+ Debug( asyncmeta_debug, "[%s] asyncmeta_dobind_result success msc: %p\n",
+ time_buf, msc );
+ }
+ LDAP_BACK_CONN_ISBOUND_SET( msc );
+ }
+ retcode = META_SEARCH_CANDIDATE;
+ }
+ return retcode;
+}
+
+static int
+asyncmeta_send_entry(
+ Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ int target,
+ LDAPMessage *e )
+{
+ a_metainfo_t *mi = mc->mc_info;
+ struct berval a, mapped = BER_BVNULL;
+ int check_sorted_attrs = 0;
+ Entry ent = {0};
+ BerElement ber = *ldap_get_message_ber( e );
+ Attribute *attr, **attrp;
+ struct berval bdn,
+ dn = BER_BVNULL;
+ const char *text;
+ a_dncookie dc;
+ ber_len_t len;
+ int rc;
+ void *mem_mark;
+
+ mem_mark = slap_sl_mark( op->o_tmpmemctx );
+ ber_set_option( &ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+
+ if ( ber_scanf( &ber, "l{", &len ) == LBER_ERROR ) {
+ return LDAP_DECODING_ERROR;
+ }
+
+ if ( ber_set_option( &ber, LBER_OPT_REMAINING_BYTES, &len ) != LBER_OPT_SUCCESS ) {
+ return LDAP_OTHER;
+ }
+
+ if ( ber_scanf( &ber, "m{", &bdn ) == LBER_ERROR ) {
+ return LDAP_DECODING_ERROR;
+ }
+
+ /*
+ * Rewrite the dn of the result, if needed
+ */
+ dc.op = op;
+ dc.target = mi->mi_targets[ target ];
+ dc.memctx = op->o_tmpmemctx;
+ dc.to_from = MASSAGE_REP;
+ asyncmeta_dn_massage( &dc, &bdn, &dn );
+
+ /*
+ * Note: this may fail if the target host(s) schema differs
+ * from the one known to the meta, and a DN with unknown
+ * attributes is returned.
+ *
+ * FIXME: should we log anything, or delegate to dnNormalize?
+ */
+ rc = dnPrettyNormal( NULL, &dn, &ent.e_name, &ent.e_nname,
+ op->o_tmpmemctx );
+ if ( dn.bv_val != bdn.bv_val ) {
+ op->o_tmpfree( dn.bv_val, op->o_tmpmemctx );
+ }
+ BER_BVZERO( &dn );
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s asyncmeta_send_entry(\"%s\"): "
+ "invalid DN syntax\n",
+ op->o_log_prefix, ent.e_name.bv_val );
+ rc = LDAP_INVALID_DN_SYNTAX;
+ goto done;
+ }
+
+ /*
+ * cache dn
+ */
+ if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
+ ( void )asyncmeta_dncache_update_entry( &mi->mi_cache,
+ &ent.e_nname, target );
+ }
+
+ attrp = &ent.e_attrs;
+
+ while ( ber_scanf( &ber, "{m", &a ) != LBER_ERROR ) {
+ int last = 0;
+ slap_syntax_validate_func *validate;
+ slap_syntax_transform_func *pretty;
+
+ if ( ber_pvt_ber_remaining( &ber ) < 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s asyncmeta_send_entry(\"%s\"): "
+ "unable to parse attr \"%s\".\n",
+ op->o_log_prefix, ent.e_name.bv_val, a.bv_val );
+
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ if ( ber_pvt_ber_remaining( &ber ) == 0 ) {
+ break;
+ }
+
+ attr = op->o_tmpcalloc( 1, sizeof(Attribute), op->o_tmpmemctx );
+ if ( slap_bv2ad( &a, &attr->a_desc, &text )
+ != LDAP_SUCCESS) {
+ if ( slap_bv2undef_ad( &a, &attr->a_desc, &text,
+ SLAP_AD_PROXIED ) != LDAP_SUCCESS )
+ {
+ Debug(LDAP_DEBUG_ANY,
+ "%s meta_send_entry(\"%s\"): " "slap_bv2undef_ad(%s): %s\n",
+ op->o_log_prefix, ent.e_name.bv_val,
+ mapped.bv_val, text );
+ ( void )ber_scanf( &ber, "x" /* [W] */ );
+ op->o_tmpfree( attr, op->o_tmpmemctx );
+ continue;
+ }
+ }
+
+ if ( attr->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL )
+ check_sorted_attrs = 1;
+
+ /* no subschemaSubentry */
+ if ( attr->a_desc == slap_schema.si_ad_subschemaSubentry
+ || attr->a_desc == slap_schema.si_ad_entryDN )
+ {
+
+ /*
+ * We eat target's subschemaSubentry because
+ * a search for this value is likely not
+ * to resolve to the appropriate backend;
+ * later, the local subschemaSubentry is
+ * added.
+ *
+ * We also eat entryDN because the frontend
+ * will reattach it without checking if already
+ * present...
+ */
+ ( void )ber_scanf( &ber, "x" /* [W] */ );
+ op->o_tmpfree( attr, op->o_tmpmemctx );
+ continue;
+ }
+
+ if ( ber_scanf( &ber, "[W]", &attr->a_vals ) == LBER_ERROR
+ || attr->a_vals == NULL )
+ {
+ attr->a_vals = (struct berval *)&slap_dummy_bv;
+
+ } else {
+ for ( last = 0; !BER_BVISNULL( &attr->a_vals[ last ] ); ++last )
+ ;
+ }
+ attr->a_numvals = last;
+
+ validate = attr->a_desc->ad_type->sat_syntax->ssyn_validate;
+ pretty = attr->a_desc->ad_type->sat_syntax->ssyn_pretty;
+
+ if ( !validate && !pretty ) {
+ ber_bvarray_free_x( attr->a_vals, op->o_tmpmemctx );
+ op->o_tmpfree( attr, op->o_tmpmemctx );
+ goto next_attr;
+ }
+
+ /*
+ * It is necessary to try to rewrite attributes with
+ * dn syntax because they might be used in ACLs as
+ * members of groups; since ACLs are applied to the
+ * rewritten stuff, no dn-based subecj clause could
+ * be used at the ldap backend side (see
+ * http://www.OpenLDAP.org/faq/data/cache/452.html)
+ * The problem can be overcome by moving the dn-based
+ * ACLs to the target directory server, and letting
+ * everything pass thru the ldap backend.
+ */
+ {
+ int i;
+
+ if ( attr->a_desc->ad_type->sat_syntax ==
+ slap_schema.si_syn_distinguishedName )
+ {
+ asyncmeta_dnattr_result_rewrite( &dc, attr->a_vals );
+
+ } else if ( attr->a_desc == slap_schema.si_ad_ref ) {
+ asyncmeta_referral_result_rewrite( &dc, attr->a_vals );
+
+ }
+
+ for ( i = 0; i < last; i++ ) {
+ struct berval pval;
+ int rc;
+
+ if ( pretty ) {
+ rc = ordered_value_pretty( attr->a_desc,
+ &attr->a_vals[i], &pval, op->o_tmpmemctx );
+
+ } else {
+ rc = ordered_value_validate( attr->a_desc,
+ &attr->a_vals[i], 0 );
+ }
+
+ if ( rc ) {
+ ber_memfree_x( attr->a_vals[i].bv_val, op->o_tmpmemctx );
+ if ( --last == i ) {
+ BER_BVZERO( &attr->a_vals[ i ] );
+ break;
+ }
+ attr->a_vals[i] = attr->a_vals[last];
+ BER_BVZERO( &attr->a_vals[last] );
+ i--;
+ continue;
+ }
+
+ if ( pretty ) {
+ ber_memfree_x( attr->a_vals[i].bv_val, op->o_tmpmemctx );
+ attr->a_vals[i] = pval;
+ }
+ }
+
+ if ( last == 0 && attr->a_vals != &slap_dummy_bv ) {
+ ber_bvarray_free_x( attr->a_vals, op->o_tmpmemctx );
+ op->o_tmpfree( attr, op->o_tmpmemctx );
+ goto next_attr;
+ }
+ }
+
+ if ( last && attr->a_desc->ad_type->sat_equality &&
+ attr->a_desc->ad_type->sat_equality->smr_normalize )
+ {
+ int i;
+
+ attr->a_nvals = op->o_tmpalloc( ( last + 1 ) * sizeof( struct berval ), op->o_tmpmemctx );
+ for ( i = 0; i<last; i++ ) {
+ /* if normalizer fails, drop this value */
+ if ( ordered_value_normalize(
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ attr->a_desc,
+ attr->a_desc->ad_type->sat_equality,
+ &attr->a_vals[i], &attr->a_nvals[i],
+ op->o_tmpmemctx )) {
+ ber_memfree_x( attr->a_vals[i].bv_val, op->o_tmpmemctx );
+ if ( --last == i ) {
+ BER_BVZERO( &attr->a_vals[ i ] );
+ break;
+ }
+ attr->a_vals[i] = attr->a_vals[last];
+ BER_BVZERO( &attr->a_vals[last] );
+ i--;
+ }
+ }
+ BER_BVZERO( &attr->a_nvals[i] );
+ if ( last == 0 ) {
+ ber_bvarray_free_x( attr->a_vals, op->o_tmpmemctx );
+ ber_bvarray_free_x( attr->a_nvals, op->o_tmpmemctx );
+ op->o_tmpfree( attr, op->o_tmpmemctx );
+ goto next_attr;
+ }
+
+ } else {
+ attr->a_nvals = attr->a_vals;
+ }
+
+ attr->a_numvals = last;
+ *attrp = attr;
+ attrp = &attr->a_next;
+next_attr:;
+ }
+
+ /* Check for sorted attributes */
+ if ( check_sorted_attrs ) {
+ for ( attr = ent.e_attrs; attr; attr = attr->a_next ) {
+ if ( attr->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL ) {
+ while ( attr->a_numvals > 1 ) {
+ int i;
+ int rc = slap_sort_vals( (Modifications *)attr, &text, &i, op->o_tmpmemctx );
+ if ( rc != LDAP_TYPE_OR_VALUE_EXISTS )
+ break;
+
+ /* Strip duplicate values */
+ if ( attr->a_nvals != attr->a_vals )
+ ber_memfree_x( attr->a_nvals[i].bv_val, op->o_tmpmemctx );
+ ber_memfree_x( attr->a_vals[i].bv_val, op->o_tmpmemctx );
+ attr->a_numvals--;
+ if ( (unsigned)i < attr->a_numvals ) {
+ attr->a_vals[i] = attr->a_vals[attr->a_numvals];
+ if ( attr->a_nvals != attr->a_vals )
+ attr->a_nvals[i] = attr->a_nvals[attr->a_numvals];
+ }
+ BER_BVZERO(&attr->a_vals[attr->a_numvals]);
+ if ( attr->a_nvals != attr->a_vals )
+ BER_BVZERO(&attr->a_nvals[attr->a_numvals]);
+ }
+ attr->a_flags |= SLAP_ATTR_SORTED_VALS;
+ }
+ }
+ }
+ Debug( LDAP_DEBUG_TRACE,
+ "%s asyncmeta_send_entry(\"%s\"): "
+ ".\n",
+ op->o_log_prefix, ent.e_name.bv_val );
+ ldap_get_entry_controls( mc->mc_conns[target].msc_ldr,
+ e, &rs->sr_ctrls );
+ rs->sr_entry = &ent;
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_operational_attrs = NULL;
+ rs->sr_flags = mi->mi_targets[ target ]->mt_rep_flags;
+ rs->sr_err = LDAP_SUCCESS;
+ rc = send_search_entry( op, rs );
+ switch ( rc ) {
+ case LDAP_UNAVAILABLE:
+ rc = LDAP_OTHER;
+ break;
+ }
+
+done:;
+ if ( rs->sr_ctrls != NULL ) {
+ ldap_controls_free( rs->sr_ctrls );
+ rs->sr_ctrls = NULL;
+ }
+#if 0
+ while ( ent.e_attrs ) {
+ attr = ent.e_attrs;
+ ent.e_attrs = attr->a_next;
+ if ( attr->a_nvals != attr->a_vals )
+ ber_bvarray_free_x( attr->a_nvals, op->o_tmpmemctx );
+ ber_bvarray_free_x( attr->a_vals, op->o_tmpmemctx );
+ op->o_tmpfree( attr, op->o_tmpmemctx );
+ }
+ if (ent.e_name.bv_val != NULL) {
+ op->o_tmpfree( ent.e_name.bv_val, op->o_tmpmemctx );
+ }
+
+ if (ent.e_nname.bv_val != NULL) {
+ op->o_tmpfree( ent.e_nname.bv_val, op->o_tmpmemctx );
+ }
+ if (rs->sr_entry && rs->sr_entry != &ent) {
+ entry_free( rs->sr_entry );
+ }
+#endif
+ slap_sl_release( mem_mark, op->o_tmpmemctx );
+ rs->sr_entry = NULL;
+ rs->sr_attrs = NULL;
+ return rc;
+}
+
+static void
+asyncmeta_search_last_result(a_metaconn_t *mc, bm_context_t *bc, int candidate, int sres)
+{
+ a_metainfo_t *mi = mc->mc_info;
+ Operation *op = bc->op;
+ SlapReply *rs = &bc->rs;
+ int i;
+ SlapReply *candidates = bc->candidates;
+ char *matched = NULL;
+
+ if ( bc->candidate_match > 0 ) {
+ struct berval pmatched = BER_BVNULL;
+
+ /* we use the first one */
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ if ( META_IS_CANDIDATE( &candidates[ i ] )
+ && candidates[ i ].sr_matched != NULL )
+ {
+ struct berval bv, pbv;
+ int rc;
+
+ /* if we got success, and this target
+ * returned noSuchObject, and its suffix
+ * is a superior of the searchBase,
+ * ignore the matchedDN */
+ if ( sres == LDAP_SUCCESS
+ && candidates[ i ].sr_err == LDAP_NO_SUCH_OBJECT
+ && op->o_req_ndn.bv_len > mi->mi_targets[ i ]->mt_nsuffix.bv_len )
+ {
+ free( (char *)candidates[ i ].sr_matched );
+ candidates[ i ].sr_matched = NULL;
+ continue;
+ }
+
+ ber_str2bv( candidates[ i ].sr_matched, 0, 0, &bv );
+ rc = dnPretty( NULL, &bv, &pbv, op->o_tmpmemctx );
+
+ if ( rc == LDAP_SUCCESS ) {
+
+ /* NOTE: if they all are superiors
+ * of the baseDN, the shorter is also
+ * superior of the longer... */
+ if ( pbv.bv_len > pmatched.bv_len ) {
+ if ( !BER_BVISNULL( &pmatched ) ) {
+ op->o_tmpfree( pmatched.bv_val, op->o_tmpmemctx );
+ }
+ pmatched = pbv;
+
+ } else {
+ op->o_tmpfree( pbv.bv_val, op->o_tmpmemctx );
+ }
+ }
+
+ if ( candidates[ i ].sr_matched != NULL ) {
+ free( (char *)candidates[ i ].sr_matched );
+ candidates[ i ].sr_matched = NULL;
+ }
+ }
+ }
+
+ if ( !BER_BVISNULL( &pmatched ) ) {
+ matched = pmatched.bv_val;
+ }
+
+ } else if ( sres == LDAP_NO_SUCH_OBJECT ) {
+ matched = mi->mi_suffix.bv_val;
+ }
+
+ /*
+ * In case we returned at least one entry, we return LDAP_SUCCESS
+ * otherwise, the latter error code we got
+ */
+
+ if ( sres == LDAP_SUCCESS ) {
+ if ( rs->sr_v2ref ) {
+ sres = LDAP_REFERRAL;
+ }
+
+ if ( META_BACK_ONERR_REPORT( mi ) ) {
+ /*
+ * Report errors, if any
+ *
+ * FIXME: we should handle error codes and return the more
+ * important/reasonable
+ */
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
+ continue;
+ }
+
+ if ( candidates[ i ].sr_err != LDAP_SUCCESS
+ && candidates[ i ].sr_err != LDAP_NO_SUCH_OBJECT )
+ {
+ sres = candidates[ i ].sr_err;
+ break;
+ }
+ }
+ }
+ }
+ Debug( LDAP_DEBUG_TRACE,
+ "%s asyncmeta_search_last_result(\"%d\"): "
+ ".\n",
+ op->o_log_prefix, candidate );
+ rs->sr_err = sres;
+ rs->sr_matched = ( sres == LDAP_SUCCESS ? NULL : matched );
+ rs->sr_text = ( sres == LDAP_SUCCESS ? NULL : candidates[candidate].sr_text );
+ rs->sr_ref = ( sres == LDAP_REFERRAL ? rs->sr_v2ref : NULL );
+ asyncmeta_send_ldap_result(bc, op, rs);
+ rs->sr_text = NULL;
+ rs->sr_matched = NULL;
+ rs->sr_ref = NULL;
+}
+
+static meta_search_candidate_t
+asyncmeta_send_pending_op(bm_context_t *bc, int candidate)
+{
+ meta_search_candidate_t retcode;
+ switch (bc->op->o_tag) {
+ case LDAP_REQ_SEARCH:
+ retcode = asyncmeta_back_search_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, NULL, 0 , 0);
+ break;
+ case LDAP_REQ_ADD:
+ retcode = asyncmeta_back_add_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0);
+ break;
+ case LDAP_REQ_MODIFY:
+ retcode = asyncmeta_back_modify_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0);
+ break;
+ case LDAP_REQ_MODRDN:
+ retcode = asyncmeta_back_modrdn_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0);
+ break;
+ case LDAP_REQ_COMPARE:
+ retcode = asyncmeta_back_compare_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0);
+ break;
+ case LDAP_REQ_DELETE:
+ retcode = asyncmeta_back_delete_start( &bc->copy_op, &bc->rs, bc->bc_mc, bc, candidate, 0);
+ break;
+ default:
+ retcode = META_SEARCH_NOT_CANDIDATE;
+ }
+ return retcode;
+}
+
+
+meta_search_candidate_t
+asyncmeta_send_all_pending_ops(a_metaconn_t *mc, int candidate, void *ctx, int dolock)
+{
+ a_metainfo_t *mi = mc->mc_info;
+ bm_context_t *bc, *onext;
+ a_metasingleconn_t *msc = &mc->mc_conns[candidate];
+
+ if ( dolock )
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+
+ msc->msc_active++;
+ for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) {
+ meta_search_candidate_t ret;
+ onext = LDAP_STAILQ_NEXT(bc, bc_next);
+ if (bc->candidates[candidate].sr_msgid == META_MSGID_NEED_BIND)
+ bc->candidates[candidate].sr_msgid = META_MSGID_GOT_BIND;
+ if (bc->candidates[candidate].sr_msgid != META_MSGID_GOT_BIND || bc->bc_active > 0 || bc->op->o_abandon > 0) {
+ continue;
+ }
+ bc->op->o_threadctx = ctx;
+ bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
+ slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
+ operation_counter_init( bc->op, ctx );
+ bc->bc_active++;
+ ret = asyncmeta_send_pending_op(bc, candidate);
+ if (ret != META_SEARCH_CANDIDATE) {
+ bc->candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ bc->candidates[ candidate ].sr_type = REP_RESULT;
+ bc->candidates[ candidate ].sr_err = bc->rs.sr_err;
+ if (bc->op->o_tag != LDAP_REQ_SEARCH || (META_BACK_ONERR_STOP( mi )) ||
+ (asyncmeta_is_last_result(mc, bc, candidate) == 0)) {
+ LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
+ mc->pending_ops--;
+ asyncmeta_send_ldap_result(bc, bc->op, &bc->rs);
+ asyncmeta_clear_bm_context(bc);
+ }
+ } else {
+ bc->bc_active--;
+ }
+ }
+ msc->msc_active--;
+
+ if ( dolock )
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+
+ return META_SEARCH_CANDIDATE;
+}
+
+meta_search_candidate_t
+asyncmeta_return_bind_errors(a_metaconn_t *mc, int candidate, SlapReply *bind_result, void *ctx, int dolock)
+{
+ a_metainfo_t *mi = mc->mc_info;
+ bm_context_t *bc, *onext;
+
+ if ( dolock )
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+
+ for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) {
+ onext = LDAP_STAILQ_NEXT(bc, bc_next);
+ if (bc->candidates[candidate].sr_msgid != META_MSGID_NEED_BIND
+ || bc->bc_active > 0 || bc->op->o_abandon > 0) {
+ continue;
+ }
+ bc->candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ bc->candidates[ candidate ].sr_type = REP_RESULT;
+ bc->candidates[ candidate ].sr_err = bind_result->sr_err;
+ if (bc->op->o_tag != LDAP_REQ_SEARCH || (META_BACK_ONERR_STOP( mi )) ||
+ (asyncmeta_is_last_result(mc, bc, candidate) == 0)) {
+ LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
+ bc->op->o_threadctx = ctx;
+ bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
+ slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
+ operation_counter_init( bc->op, ctx );
+ bc->rs.sr_err = bind_result->sr_err;
+ bc->rs.sr_text = bind_result->sr_text;
+ mc->pending_ops--;
+ asyncmeta_send_ldap_result(bc, bc->op, &bc->rs);
+ asyncmeta_clear_bm_context(bc);
+ }
+ }
+
+ if ( dolock )
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+
+ return META_SEARCH_CANDIDATE;
+}
+
+static meta_search_candidate_t
+asyncmeta_handle_bind_result(LDAPMessage *msg, a_metaconn_t *mc, int candidate, void *ctx)
+{
+ meta_search_candidate_t retcode;
+ SlapReply bind_result = {0};
+ /* could modify the msc, safer to lock it */
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+ retcode = asyncmeta_dobind_result( mc, candidate, &bind_result, msg );
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+ if ( retcode == META_SEARCH_CANDIDATE ) {
+ /* send the remaining pending ops */
+ asyncmeta_send_all_pending_ops(mc, candidate, ctx, 1);
+ } else {
+ asyncmeta_return_bind_errors(mc, candidate, &bind_result, ctx, 1);
+ }
+ return retcode;
+}
+
+int
+asyncmeta_handle_search_msg(LDAPMessage *res, a_metaconn_t *mc, bm_context_t *bc, int candidate)
+{
+ a_metainfo_t *mi;
+ a_metatarget_t *mt;
+ a_metasingleconn_t *msc;
+ Operation *op = bc->op;
+ SlapReply *rs;
+ int i, rc = LDAP_SUCCESS, sres;
+ SlapReply *candidates;
+ char **references = NULL;
+ LDAPControl **ctrls = NULL;
+ a_dncookie dc;
+ LDAPMessage *msg;
+ ber_int_t id;
+
+ rs = &bc->rs;
+ mi = mc->mc_info;
+ mt = mi->mi_targets[ candidate ];
+ msc = &mc->mc_conns[ candidate ];
+ dc.op = op;
+ dc.target = mt;
+ dc.to_from = MASSAGE_REP;
+ id = ldap_msgid(res);
+
+
+ candidates = bc->candidates;
+ i = candidate;
+
+ while (res && !META_BACK_CONN_INVALID(msc)) {
+ for (msg = ldap_first_message(msc->msc_ldr, res); msg; msg = ldap_next_message(msc->msc_ldr, msg)) {
+ switch(ldap_msgtype(msg)) {
+ case LDAP_RES_SEARCH_ENTRY:
+ Debug( LDAP_DEBUG_TRACE,
+ "%s asyncmeta_handle_search_msg: msc %p entry\n",
+ op->o_log_prefix, msc );
+ if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
+ /* don't retry any more... */
+ candidates[ i ].sr_type = REP_RESULT;
+ }
+ /* count entries returned by target */
+ candidates[ i ].sr_nentries++;
+ if (bc->c_peer_name.bv_val == op->o_conn->c_peer_name.bv_val && !op->o_abandon) {
+ rs->sr_err = asyncmeta_send_entry( &bc->copy_op, rs, mc, i, msg );
+ } else {
+ goto err_cleanup;
+ }
+ switch ( rs->sr_err ) {
+ case LDAP_SIZELIMIT_EXCEEDED:
+ asyncmeta_send_ldap_result(bc, op, rs);
+ rs->sr_err = LDAP_SUCCESS;
+ goto err_cleanup;
+ case LDAP_UNAVAILABLE:
+ rs->sr_err = LDAP_OTHER;
+ break;
+ default:
+ break;
+ }
+ bc->is_ok++;
+ break;
+
+ case LDAP_RES_SEARCH_REFERENCE:
+ if ( META_BACK_TGT_NOREFS( mt ) ) {
+ rs->sr_err = LDAP_OTHER;
+ asyncmeta_send_ldap_result(bc, op, rs);
+ goto err_cleanup;
+ }
+ if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
+ /* don't retry any more... */
+ candidates[ i ].sr_type = REP_RESULT;
+ }
+ bc->is_ok++;
+ rc = ldap_parse_reference( msc->msc_ldr, msg,
+ &references, &rs->sr_ctrls, 0 );
+
+ if ( rc != LDAP_SUCCESS || references == NULL ) {
+ rs->sr_err = LDAP_OTHER;
+ asyncmeta_send_ldap_result(bc, op, rs);
+ goto err_cleanup;
+ }
+
+ /* FIXME: merge all and return at the end */
+
+ {
+ int cnt;
+ for ( cnt = 0; references[ cnt ]; cnt++ )
+ ;
+
+ rs->sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( cnt + 1 ),
+ op->o_tmpmemctx );
+
+ for ( cnt = 0; references[ cnt ]; cnt++ ) {
+ ber_str2bv_x( references[ cnt ], 0, 1, &rs->sr_ref[ cnt ],
+ op->o_tmpmemctx );
+ }
+ BER_BVZERO( &rs->sr_ref[ cnt ] );
+ }
+
+ {
+ dc.memctx = op->o_tmpmemctx;
+ ( void )asyncmeta_referral_result_rewrite( &dc, rs->sr_ref );
+ }
+
+ if ( rs->sr_ref != NULL ) {
+ if (!BER_BVISNULL( &rs->sr_ref[ 0 ] ) ) {
+ /* ignore return value by now */
+ ( void )send_search_reference( op, rs );
+ }
+
+ ber_bvarray_free_x( rs->sr_ref, op->o_tmpmemctx );
+ rs->sr_ref = NULL;
+ }
+
+ /* cleanup */
+ if ( references ) {
+ ber_memvfree( (void **)references );
+ }
+
+ if ( rs->sr_ctrls ) {
+ ldap_controls_free( rs->sr_ctrls );
+ rs->sr_ctrls = NULL;
+ }
+ break;
+
+ case LDAP_RES_INTERMEDIATE:
+ if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
+ /* don't retry any more... */
+ candidates[ i ].sr_type = REP_RESULT;
+ }
+ bc->is_ok++;
+
+ /* FIXME: response controls
+ * are passed without checks */
+ rs->sr_err = ldap_parse_intermediate( msc->msc_ldr,
+ msg,
+ (char **)&rs->sr_rspoid,
+ &rs->sr_rspdata,
+ &rs->sr_ctrls,
+ 0 );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ candidates[ i ].sr_type = REP_RESULT;
+ rs->sr_err = LDAP_OTHER;
+ asyncmeta_send_ldap_result(bc, op, rs);
+ goto err_cleanup;
+ }
+
+ slap_send_ldap_intermediate( op, rs );
+
+ if ( rs->sr_rspoid != NULL ) {
+ ber_memfree( (char *)rs->sr_rspoid );
+ rs->sr_rspoid = NULL;
+ }
+
+ if ( rs->sr_rspdata != NULL ) {
+ ber_bvfree( rs->sr_rspdata );
+ rs->sr_rspdata = NULL;
+ }
+
+ if ( rs->sr_ctrls != NULL ) {
+ ldap_controls_free( rs->sr_ctrls );
+ rs->sr_ctrls = NULL;
+ }
+ break;
+
+ case LDAP_RES_SEARCH_RESULT:
+ if ( mi->mi_idle_timeout != 0 ) {
+ asyncmeta_set_msc_time(msc);
+ }
+ Debug( LDAP_DEBUG_TRACE,
+ "%s asyncmeta_handle_search_msg: msc %p result\n",
+ op->o_log_prefix, msc );
+ candidates[ i ].sr_type = REP_RESULT;
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ /* NOTE: ignores response controls
+ * (and intermediate response controls
+ * as well, except for those with search
+ * references); this may not be correct,
+ * but if they're not ignored then
+ * back-meta would need to merge them
+ * consistently (think of pagedResults...)
+ */
+ /* FIXME: response controls? */
+ rs->sr_err = ldap_parse_result( msc->msc_ldr,
+ msg,
+ &candidates[ i ].sr_err,
+ (char **)&candidates[ i ].sr_matched,
+ (char **)&candidates[ i ].sr_text,
+ &references,
+ &ctrls /* &candidates[ i ].sr_ctrls (unused) */ ,
+ 0 );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ candidates[ i ].sr_err = rs->sr_err;
+ sres = slap_map_api2result( &candidates[ i ] );
+ candidates[ i ].sr_type = REP_RESULT;
+ goto finish;
+ }
+
+ rs->sr_err = candidates[ i ].sr_err;
+
+ /* massage matchedDN if need be */
+ if ( candidates[ i ].sr_matched != NULL ) {
+ struct berval match, mmatch;
+
+ ber_str2bv( candidates[ i ].sr_matched,
+ 0, 0, &match );
+ candidates[ i ].sr_matched = NULL;
+
+ dc.memctx = NULL;
+ asyncmeta_dn_massage( &dc, &match, &mmatch );
+ if ( mmatch.bv_val == match.bv_val ) {
+ candidates[ i ].sr_matched
+ = ch_strdup( mmatch.bv_val );
+
+ } else {
+ candidates[ i ].sr_matched = mmatch.bv_val;
+ }
+
+ bc->candidate_match++;
+ ldap_memfree( match.bv_val );
+ }
+
+ /* add references to array */
+ /* RFC 4511: referrals can only appear
+ * if result code is LDAP_REFERRAL */
+ if ( references != NULL
+ && references[ 0 ] != NULL
+ && references[ 0 ][ 0 ] != '\0' )
+ {
+ if ( rs->sr_err != LDAP_REFERRAL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s asncmeta_search_result[%d]: "
+ "got referrals with err=%d\n",
+ op->o_log_prefix,
+ i, rs->sr_err );
+
+ } else {
+ BerVarray sr_ref;
+ int cnt;
+
+ for ( cnt = 0; references[ cnt ]; cnt++ )
+ ;
+
+ sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( cnt + 1 ),
+ op->o_tmpmemctx );
+
+ for ( cnt = 0; references[ cnt ]; cnt++ ) {
+ ber_str2bv_x( references[ cnt ], 0, 1, &sr_ref[ cnt ],
+ op->o_tmpmemctx );
+ }
+ BER_BVZERO( &sr_ref[ cnt ] );
+
+ dc.memctx = op->o_tmpmemctx;
+ ( void )asyncmeta_referral_result_rewrite( &dc, sr_ref );
+
+ if ( rs->sr_v2ref == NULL ) {
+ rs->sr_v2ref = sr_ref;
+
+ } else {
+ for ( cnt = 0; !BER_BVISNULL( &sr_ref[ cnt ] ); cnt++ ) {
+ ber_bvarray_add_x( &rs->sr_v2ref, &sr_ref[ cnt ],
+ op->o_tmpmemctx );
+ }
+ ber_memfree_x( sr_ref, op->o_tmpmemctx );
+ }
+ }
+
+ } else if ( rs->sr_err == LDAP_REFERRAL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "%s asyncmeta_search_result[%d]: "
+ "got err=%d with null "
+ "or empty referrals\n",
+ op->o_log_prefix,
+ i, rs->sr_err );
+
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ }
+
+ /* cleanup */
+ ber_memvfree( (void **)references );
+
+ sres = slap_map_api2result( rs );
+
+ if ( candidates[ i ].sr_err == LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_search_result[%d] "
+ "match=\"%s\" err=%ld\n",
+ op->o_log_prefix, i,
+ candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "",
+ (long) candidates[ i ].sr_err );
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s asyncmeta_search_result[%d] "
+ "match=\"%s\" err=%ld (%s)\n",
+ op->o_log_prefix, i,
+ candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "",
+ (long) candidates[ i ].sr_err, ldap_err2string( candidates[ i ].sr_err ) );
+ }
+
+ switch ( sres ) {
+ case LDAP_NO_SUCH_OBJECT:
+ /* is_ok is touched any time a valid
+ * (even intermediate) result is
+ * returned; as a consequence, if
+ * a candidate returns noSuchObject
+ * it is ignored and the candidate
+ * is simply demoted. */
+ if ( bc->is_ok ) {
+ sres = LDAP_SUCCESS;
+ }
+ break;
+
+ case LDAP_SUCCESS:
+ if ( ctrls != NULL && ctrls[0] != NULL ) {
+#ifdef SLAPD_META_CLIENT_PR
+ LDAPControl *pr_c;
+
+ pr_c = ldap_control_find( LDAP_CONTROL_PAGEDRESULTS, ctrls, NULL );
+ if ( pr_c != NULL ) {
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_int_t prsize;
+ struct berval prcookie;
+
+ /* unsolicited, do not accept */
+ if ( mt->mt_ps == 0 ) {
+ rs->sr_err = LDAP_OTHER;
+ goto err_pr;
+ }
+
+ ber_init2( ber, &pr_c->ldctl_value, LBER_USE_DER );
+
+ tag = ber_scanf( ber, "{im}", &prsize, &prcookie );
+ if ( tag == LBER_ERROR ) {
+ rs->sr_err = LDAP_OTHER;
+ goto err_pr;
+ }
+
+ /* more pages? new search request */
+ if ( !BER_BVISNULL( &prcookie ) && !BER_BVISEMPTY( &prcookie ) ) {
+ if ( mt->mt_ps > 0 ) {
+ /* ignore size if specified */
+ prsize = 0;
+
+ } else if ( prsize == 0 ) {
+ /* guess the page size from the entries returned so far */
+ prsize = candidates[ i ].sr_nentries;
+ }
+
+ candidates[ i ].sr_nentries = 0;
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ candidates[ i ].sr_type = REP_INTERMEDIATE;
+
+ assert( candidates[ i ].sr_matched == NULL );
+ assert( candidates[ i ].sr_text == NULL );
+ assert( candidates[ i ].sr_ref == NULL );
+
+ switch ( asyncmeta_back_search_start( &bc->copy_op, rs, mc, bc, i, &prcookie, prsize, 1 ) )
+ {
+ case META_SEARCH_CANDIDATE:
+ assert( candidates[ i ].sr_msgid >= 0 );
+ ldap_controls_free( ctrls );
+ // goto free_message;
+
+ case META_SEARCH_ERR:
+ case META_SEARCH_NEED_BIND:
+err_pr:;
+ candidates[ i ].sr_err = rs->sr_err;
+ candidates[ i ].sr_type = REP_RESULT;
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ asyncmeta_send_ldap_result(bc, op, rs);
+ ldap_controls_free( ctrls );
+ goto err_cleanup;
+ }
+ /* fallthru */
+
+ case META_SEARCH_NOT_CANDIDATE:
+ /* means that asyncmeta_back_search_start()
+ * failed but onerr == continue */
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ candidates[ i ].sr_type = REP_RESULT;
+ break;
+
+ default:
+ /* impossible */
+ assert( 0 );
+ break;
+ }
+ break;
+ }
+ }
+#endif /* SLAPD_META_CLIENT_PR */
+
+ ldap_controls_free( ctrls );
+ }
+ /* fallthru */
+
+ case LDAP_REFERRAL:
+ bc->is_ok++;
+ break;
+
+ case LDAP_SIZELIMIT_EXCEEDED:
+ /* if a target returned sizelimitExceeded
+ * and the entry count is equal to the
+ * proxy's limit, the target would have
+ * returned more, and the error must be
+ * propagated to the client; otherwise,
+ * the target enforced a limit lower
+ * than what requested by the proxy;
+ * ignore it */
+ candidates[ i ].sr_err = rs->sr_err;
+ if ( rs->sr_nentries == op->ors_slimit
+ || META_BACK_ONERR_STOP( mi ) )
+ {
+ const char *save_text;
+got_err:
+ save_text = rs->sr_text;
+ rs->sr_text = candidates[ i ].sr_text;
+ asyncmeta_send_ldap_result(bc, op, rs);
+ if (candidates[ i ].sr_text != NULL) {
+ ch_free( (char *)candidates[ i ].sr_text );
+ candidates[ i ].sr_text = NULL;
+ }
+ rs->sr_text = save_text;
+ ldap_controls_free( ctrls );
+ goto err_cleanup;
+ }
+ break;
+
+ default:
+ candidates[ i ].sr_err = rs->sr_err;
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ goto got_err;
+ }
+ break;
+ }
+ /* if this is the last result we will ever receive, send it back */
+ rc = rs->sr_err;
+ if (asyncmeta_is_last_result(mc, bc, i) == 0) {
+ Debug( LDAP_DEBUG_TRACE,
+ "%s asyncmeta_handle_search_msg: msc %p last result\n",
+ op->o_log_prefix, msc );
+ asyncmeta_search_last_result(mc, bc, i, sres);
+err_cleanup:
+ rc = rs->sr_err;
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+ asyncmeta_drop_bc( mc, bc);
+ asyncmeta_clear_bm_context(bc);
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+ ldap_msgfree(res);
+ return rc;
+ }
+finish:
+ break;
+
+ default:
+ continue;
+ }
+ }
+ ldap_msgfree(res);
+ res = NULL;
+ if (candidates[ i ].sr_type != REP_RESULT) {
+ struct timeval tv = {0};
+ rc = ldap_result( msc->msc_ldr, id, LDAP_MSG_RECEIVED, &tv, &res );
+ if (res != NULL) {
+ msc->msc_result_time = slap_get_time();
+ }
+ }
+ }
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+ bc->bc_active--;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+
+ return rc;
+}
+
+/* handles the received result for add, modify, modrdn, compare and delete ops */
+
+int asyncmeta_handle_common_result(LDAPMessage *msg, a_metaconn_t *mc, bm_context_t *bc, int candidate)
+{
+ a_metainfo_t *mi;
+ a_metatarget_t *mt;
+ a_metasingleconn_t *msc;
+ const char *save_text = NULL,
+ *save_matched = NULL;
+ BerVarray save_ref = NULL;
+ LDAPControl **save_ctrls = NULL;
+ void *matched_ctx = NULL;
+
+ char *matched = NULL;
+ char *text = NULL;
+ char **refs = NULL;
+ LDAPControl **ctrls = NULL;
+ Operation *op;
+ SlapReply *rs;
+ int rc;
+
+ mi = mc->mc_info;
+ mt = mi->mi_targets[ candidate ];
+ msc = &mc->mc_conns[ candidate ];
+
+ op = bc->op;
+ rs = &bc->rs;
+ save_text = rs->sr_text,
+ save_matched = rs->sr_matched;
+ save_ref = rs->sr_ref;
+ save_ctrls = rs->sr_ctrls;
+ rs->sr_text = NULL;
+ rs->sr_matched = NULL;
+ rs->sr_ref = NULL;
+ rs->sr_ctrls = NULL;
+
+ /* only touch when activity actually took place... */
+ if ( mi->mi_idle_timeout != 0 ) {
+ asyncmeta_set_msc_time(msc);
+ }
+
+ rc = ldap_parse_result( msc->msc_ldr, msg, &rs->sr_err,
+ &matched, &text, &refs, &ctrls, 0 );
+
+ if ( rc == LDAP_SUCCESS ) {
+ rs->sr_text = text;
+ } else {
+ rs->sr_err = rc;
+ }
+ rs->sr_err = slap_map_api2result( rs );
+
+ /* RFC 4511: referrals can only appear
+ * if result code is LDAP_REFERRAL */
+ if ( refs != NULL
+ && refs[ 0 ] != NULL
+ && refs[ 0 ][ 0 ] != '\0' )
+ {
+ if ( rs->sr_err != LDAP_REFERRAL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s asyncmeta_handle_common_result[%d]: "
+ "got referrals with err=%d\n",
+ op->o_log_prefix,
+ candidate, rs->sr_err );
+
+ } else {
+ int i;
+
+ for ( i = 0; refs[ i ] != NULL; i++ )
+ /* count */ ;
+ rs->sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( i + 1 ),
+ op->o_tmpmemctx );
+ for ( i = 0; refs[ i ] != NULL; i++ ) {
+ ber_str2bv( refs[ i ], 0, 0, &rs->sr_ref[ i ] );
+ }
+ BER_BVZERO( &rs->sr_ref[ i ] );
+ }
+
+ } else if ( rs->sr_err == LDAP_REFERRAL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s asyncmeta_handle_common_result[%d]: "
+ "got err=%d with null "
+ "or empty referrals\n",
+ op->o_log_prefix,
+ candidate, rs->sr_err );
+
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ }
+
+ if ( ctrls != NULL ) {
+ rs->sr_ctrls = ctrls;
+ }
+
+ /* if the error in the reply structure is not
+ * LDAP_SUCCESS, try to map it from client
+ * to server error */
+ if ( !LDAP_ERR_OK( rs->sr_err ) ) {
+ rs->sr_err = slap_map_api2result( rs );
+
+ /* internal ops ( op->o_conn == NULL )
+ * must not reply to client */
+ if ( op->o_conn && !op->o_do_not_cache && matched ) {
+
+ /* record the (massaged) matched
+ * DN into the reply structure */
+ rs->sr_matched = matched;
+ }
+ }
+
+ if ( META_BACK_TGT_QUARANTINE( mt ) ) {
+ asyncmeta_quarantine( op, mi, rs, candidate );
+ }
+
+ if ( matched != NULL ) {
+ struct berval dn, pdn;
+
+ ber_str2bv( matched, 0, 0, &dn );
+ if ( dnPretty( NULL, &dn, &pdn, op->o_tmpmemctx ) == LDAP_SUCCESS ) {
+ ldap_memfree( matched );
+ matched_ctx = op->o_tmpmemctx;
+ matched = pdn.bv_val;
+ }
+ rs->sr_matched = matched;
+ }
+
+ if ( rs->sr_err == LDAP_UNAVAILABLE || rs->sr_err == LDAP_SERVER_DOWN ) {
+ if ( rs->sr_text == NULL ) {
+ rs->sr_text = "Target is unavailable";
+ }
+ }
+
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+ asyncmeta_drop_bc( mc, bc);
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+
+ if ( op->o_conn ) {
+ asyncmeta_send_ldap_result(bc, op, rs);
+ }
+
+ if ( matched ) {
+ op->o_tmpfree( (char *)rs->sr_matched, matched_ctx );
+ }
+ if ( text ) {
+ ldap_memfree( text );
+ }
+ if ( rs->sr_ref ) {
+ op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
+ rs->sr_ref = NULL;
+ }
+ if ( refs ) {
+ ber_memvfree( (void **)refs );
+ }
+ if ( ctrls ) {
+ assert( rs->sr_ctrls != NULL );
+ ldap_controls_free( ctrls );
+ }
+
+ rs->sr_text = save_text;
+ rs->sr_matched = save_matched;
+ rs->sr_ref = save_ref;
+ rs->sr_ctrls = save_ctrls;
+ rc = (LDAP_ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err);
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+ asyncmeta_clear_bm_context(bc);
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+ return rc;
+}
+
+/* This takes care to clean out the outbound queue in case we have a read error
+ * sending back responses to the client */
+int
+asyncmeta_op_read_error(a_metaconn_t *mc, int candidate, int error, void* ctx)
+{
+ bm_context_t *bc, *onext;
+ int cleanup;
+ Operation *op;
+ SlapReply *rs;
+ SlapReply *candidates;
+
+ /* no outstanding ops, nothing to do but log */
+ Debug( LDAP_DEBUG_TRACE,
+ "asyncmeta_op_read_error: ldr=%p, err=%d\n",
+ mc->mc_conns[candidate].msc_ldr, error );
+
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+ /*someone may be trying to write */
+ if (mc->mc_conns[candidate].msc_active <= 1) {
+ asyncmeta_clear_one_msc(NULL, mc, candidate, 0, __FUNCTION__);
+ } else {
+ META_BACK_CONN_INVALID_SET(&mc->mc_conns[candidate]);
+ }
+
+ if (mc->pending_ops <= 0) {
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+ return LDAP_SUCCESS;
+ }
+
+ for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) {
+ onext = LDAP_STAILQ_NEXT(bc, bc_next);
+ cleanup = 0;
+ candidates = bc->candidates;
+ /* was this op affected? */
+ if ( !META_IS_CANDIDATE( &candidates[ candidate ] ) )
+ continue;
+
+ if (bc->op->o_abandon) {
+ bc->bc_invalid = 1;
+ continue;
+ }
+
+ if (bc->bc_active > 0) {
+ bc->bc_invalid = 1;
+ continue;
+ }
+
+ bc->op->o_threadctx = ctx;
+ bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
+ slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
+ operation_counter_init( bc->op, ctx );
+
+ op = bc->op;
+ rs = &bc->rs;
+ switch (op->o_tag) {
+ case LDAP_REQ_ADD:
+ case LDAP_REQ_MODIFY:
+ case LDAP_REQ_MODRDN:
+ case LDAP_REQ_COMPARE:
+ case LDAP_REQ_DELETE:
+ rs->sr_err = LDAP_UNAVAILABLE;
+ rs->sr_text = "Read error on connection to target";
+ asyncmeta_send_ldap_result( bc, op, rs );
+ cleanup = 1;
+ break;
+ case LDAP_REQ_SEARCH:
+ {
+ a_metainfo_t *mi = mc->mc_info;
+ rs->sr_err = LDAP_UNAVAILABLE;
+ rs->sr_text = "Read error on connection to target";
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ candidates[ candidate ].sr_type = REP_RESULT;
+ if ( (META_BACK_ONERR_STOP( mi ) ||
+ asyncmeta_is_last_result(mc, bc, candidate)) && op->o_conn) {
+ asyncmeta_send_ldap_result( bc, op, rs );
+ cleanup = 1;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (cleanup) {
+ int j;
+ a_metainfo_t *mi = mc->mc_info;
+ for (j=0; j<mi->mi_ntargets; j++) {
+ if (j != candidate && bc->candidates[j].sr_msgid >= 0
+ && mc->mc_conns[j].msc_ld != NULL) {
+ asyncmeta_back_cancel( mc, op,
+ bc->candidates[ j ].sr_msgid, j );
+ }
+ }
+ LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
+ mc->pending_ops--;
+ asyncmeta_clear_bm_context(bc);
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+ return LDAP_SUCCESS;
+}
+
+void *
+asyncmeta_op_handle_result(void *ctx, void *arg)
+{
+ a_metaconn_t *mc = arg;
+ int i, j, rc, ntargets;
+ struct timeval tv = {0};
+ LDAPMessage *msg;
+ a_metasingleconn_t *msc;
+ bm_context_t *bc;
+ void *oldctx;
+
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+ rc = ++mc->mc_active;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+ if (rc > 1)
+ return NULL;
+
+ ntargets = mc->mc_info->mi_ntargets;
+ i = ntargets;
+ oldctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 0); /* get existing memctx */
+
+again:
+ for (j=0; j<ntargets; j++) {
+ i++;
+ if (i >= ntargets) i = 0;
+ msc = &mc->mc_conns[i];
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+ if (!mc->mc_conns[i].msc_ldr ||
+ META_BACK_CONN_CREATING( &mc->mc_conns[i] ) ||
+ META_BACK_CONN_INVALID(&mc->mc_conns[i])) {
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+ continue;
+ }
+
+ msc->msc_active++;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+
+ rc = ldap_result( mc->mc_conns[i].msc_ldr, LDAP_RES_ANY, LDAP_MSG_RECEIVED, &tv, &msg );
+ if (rc < 1) {
+ if (rc < 0) {
+ ldap_get_option( mc->mc_conns[i].msc_ldr, LDAP_OPT_ERROR_NUMBER, &rc);
+ META_BACK_CONN_INVALID_SET(&mc->mc_conns[i]);
+ asyncmeta_op_read_error(mc, i, rc, ctx);
+ }
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+ msc->msc_active--;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+ continue;
+ }
+ rc = ldap_msgtype( msg );
+ if (rc == LDAP_RES_BIND) {
+ if ( LogTest( asyncmeta_debug ) ) {
+ char time_buf[ SLAP_TEXT_BUFLEN ];
+ asyncmeta_get_timestamp(time_buf);
+ Debug( asyncmeta_debug, "[%s] asyncmeta_op_handle_result received bind msgid=%d msc: %p\n",
+ time_buf, ldap_msgid(msg), msc );
+ }
+ asyncmeta_handle_bind_result(msg, mc, i, ctx);
+ mc->mc_info->mi_targets[i]->mt_timeout_ops = 0;
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+ msc->msc_result_time = slap_get_time();
+ msc->msc_active--;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+ if (msg)
+ ldap_msgfree(msg);
+
+ continue;
+ }
+retry_bc:
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+ bc = asyncmeta_find_message(ldap_msgid(msg), mc, i);
+/* The sender might not be yet done with the context. On error it might also remove it
+ * so it's best to try and find it again after a wait */
+ if (bc && bc->bc_active > 0) {
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+ ldap_pvt_thread_yield();
+ goto retry_bc;
+ }
+ if (bc) {
+ bc->bc_active++;
+ }
+
+ msc->msc_result_time = slap_get_time();
+ msc->msc_active--;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+ if (!bc) {
+ Debug( asyncmeta_debug,
+ "asyncmeta_op_handle_result: Unable to find bc for msguid %d, msc: %p\n", ldap_msgid(msg), msc );
+ ldap_msgfree(msg);
+ continue;
+ }
+
+ /* set our memctx */
+ bc->op->o_threadctx = ctx;
+ bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
+ slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
+ operation_counter_init( bc->op, ctx );
+ if (bc->op->o_abandon) {
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+ asyncmeta_drop_bc( mc, bc);
+ if ( bc->op->o_tag == LDAP_REQ_SEARCH ) {
+ int j;
+ for (j=0; j<ntargets; j++) {
+ if (bc->candidates[j].sr_msgid >= 0) {
+ a_metasingleconn_t *tmp_msc = &mc->mc_conns[j];
+ tmp_msc->msc_active++;
+ asyncmeta_back_cancel( mc, bc->op,
+ bc->candidates[ j ].sr_msgid, j );
+ tmp_msc->msc_active--;
+ }
+ }
+ }
+ asyncmeta_clear_bm_context(bc);
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+ if (msg)
+ ldap_msgfree(msg);
+ continue;
+ }
+
+ switch (rc) {
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_REFERENCE:
+ case LDAP_RES_SEARCH_RESULT:
+ case LDAP_RES_INTERMEDIATE:
+ asyncmeta_handle_search_msg(msg, mc, bc, i);
+ mc->mc_info->mi_targets[i]->mt_timeout_ops = 0;
+ msg = NULL;
+ break;
+ case LDAP_RES_ADD:
+ case LDAP_RES_DELETE:
+ case LDAP_RES_MODDN:
+ case LDAP_RES_COMPARE:
+ case LDAP_RES_MODIFY:
+ rc = asyncmeta_handle_common_result(msg, mc, bc, i);
+ mc->mc_info->mi_targets[i]->mt_timeout_ops = 0;
+ break;
+ default:
+ {
+ Debug( asyncmeta_debug,
+ "asyncmeta_op_handle_result: "
+ "unrecognized response message tag=%d\n",
+ rc );
+
+ }
+ }
+ if (msg)
+ ldap_msgfree(msg);
+ }
+
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+ rc = --mc->mc_active;
+ if (rc) {
+ i++;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+ goto again;
+ }
+ slap_sl_mem_setctx(ctx, oldctx);
+ if (mc->mc_conns) {
+ for (i=0; i<ntargets; i++) {
+ if (!slapd_shutdown && !META_BACK_CONN_INVALID(msc)
+ && mc->mc_conns[i].msc_ldr && mc->mc_conns[i].conn) {
+ connection_client_enable(mc->mc_conns[i].conn);
+ }
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+ return NULL;
+}
+
+void asyncmeta_set_msc_time(a_metasingleconn_t *msc)
+{
+ msc->msc_time = slap_get_time();
+}
+
+void* asyncmeta_timeout_loop(void *ctx, void *arg)
+{
+ struct re_s* rtask = arg;
+ a_metainfo_t *mi = rtask->arg;
+ bm_context_t *bc, *onext;
+ time_t current_time = slap_get_time();
+ int i, j;
+ LDAP_STAILQ_HEAD(BCList, bm_context_t) timeout_list;
+ LDAP_STAILQ_INIT( &timeout_list );
+
+ Debug( asyncmeta_debug, "asyncmeta_timeout_loop[%p] start at [%ld] \n", rtask, current_time );
+ void *oldctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 0);
+ for (i=0; i<mi->mi_num_conns; i++) {
+ a_metaconn_t * mc= &mi->mi_conns[i];
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+ for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) {
+ onext = LDAP_STAILQ_NEXT(bc, bc_next);
+ if (bc->bc_active > 0) {
+ continue;
+ }
+
+ if (bc->op->o_abandon ) {
+ Operation *op = bc->op;
+
+ /* set our memctx */
+ op->o_threadctx = ctx;
+ op->o_tid = ldap_pvt_thread_pool_tid( ctx );
+ slap_sl_mem_setctx(ctx, op->o_tmpmemctx);
+ operation_counter_init( op, ctx );
+
+ LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
+ mc->pending_ops--;
+ for (j=0; j<mi->mi_ntargets; j++) {
+ if (bc->candidates[j].sr_msgid >= 0) {
+ a_metasingleconn_t *msc = &mc->mc_conns[j];
+ if ( op->o_tag == LDAP_REQ_SEARCH ) {
+ msc->msc_active++;
+ asyncmeta_back_cancel( mc, op,
+ bc->candidates[ j ].sr_msgid, j );
+ msc->msc_active--;
+ }
+ }
+ }
+ asyncmeta_clear_bm_context(bc);
+ continue;
+ }
+ if (bc->bc_invalid) {
+ LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
+ mc->pending_ops--;
+ LDAP_STAILQ_INSERT_TAIL( &timeout_list, bc, bc_next);
+ continue;
+ }
+
+ if (bc->timeout && bc->stoptime < current_time) {
+ Operation *op = bc->op;
+ LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
+ mc->pending_ops--;
+ LDAP_STAILQ_INSERT_TAIL( &timeout_list, bc, bc_next);
+ for (j=0; j<mi->mi_ntargets; j++) {
+ if (bc->candidates[j].sr_msgid >= 0) {
+ a_metasingleconn_t *msc = &mc->mc_conns[j];
+ asyncmeta_set_msc_time(msc);
+ if ( op->o_tag == LDAP_REQ_SEARCH ) {
+ msc->msc_active++;
+ asyncmeta_back_cancel( mc, op,
+ bc->candidates[ j ].sr_msgid, j );
+ msc->msc_active--;
+ }
+ }
+ }
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+
+ for (bc = LDAP_STAILQ_FIRST(&timeout_list); bc; bc = onext) {
+ Operation *op = bc->op;
+ SlapReply *rs = &bc->rs;
+ int timeout_err;
+ const char *timeout_text;
+
+ onext = LDAP_STAILQ_NEXT(bc, bc_next);
+ LDAP_STAILQ_REMOVE(&timeout_list, bc, bm_context_t, bc_next);
+ /* set our memctx */
+ bc->op->o_threadctx = ctx;
+ bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
+ slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
+ operation_counter_init( bc->op, ctx );
+
+ if (bc->searchtime) {
+ timeout_err = LDAP_TIMELIMIT_EXCEEDED;
+ } else {
+ timeout_err = op->o_protocol >= LDAP_VERSION3 ?
+ LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+ }
+
+ if ( bc->bc_invalid ) {
+ timeout_text = "Operation is invalid - target connection has been reset";
+ } else {
+ a_metasingleconn_t *log_msc = &mc->mc_conns[0];
+ Debug( asyncmeta_debug,
+ "asyncmeta_timeout_loop:Timeout op %s loop[%p], "
+ "current_time:%ld, op->o_time:%ld msc: %p, "
+ "msc->msc_binding_time: %x, msc->msc_flags:%x \n",
+ bc->op->o_log_prefix, rtask, current_time, bc->op->o_time,
+ log_msc, (unsigned int)log_msc->msc_binding_time, log_msc->msc_mscflags );
+
+ if (bc->searchtime) {
+ timeout_text = NULL;
+ } else {
+ timeout_text = "Operation timed out";
+ }
+
+ for (j=0; j<mi->mi_ntargets; j++) {
+ if (bc->candidates[j].sr_msgid >= 0) {
+ a_metatarget_t *mt = mi->mi_targets[j];
+ if (!META_BACK_TGT_QUARANTINE( mt ) ||
+ bc->candidates[j].sr_type == REP_RESULT) {
+ continue;
+ }
+
+ if (mt->mt_isquarantined > LDAP_BACK_FQ_NO) {
+ timeout_err = LDAP_UNAVAILABLE;
+ } else {
+ mt->mt_timeout_ops++;
+ if ((mi->mi_max_timeout_ops > 0) &&
+ (mt->mt_timeout_ops > mi->mi_max_timeout_ops)) {
+ timeout_err = LDAP_UNAVAILABLE;
+ rs->sr_err = timeout_err;
+ if (mt->mt_isquarantined == LDAP_BACK_FQ_NO)
+ asyncmeta_quarantine(op, mi, rs, j);
+ }
+ }
+ }
+ }
+ }
+ rs->sr_err = timeout_err;
+ rs->sr_text = timeout_text;
+ if (!bc->op->o_abandon ) {
+ asyncmeta_send_ldap_result( bc, bc->op, &bc->rs );
+ }
+ asyncmeta_clear_bm_context(bc);
+ }
+
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
+ if (mi->mi_idle_timeout) {
+ for (j=0; j<mi->mi_ntargets; j++) {
+ a_metasingleconn_t *msc = &mc->mc_conns[j];
+ if ( msc->msc_active > 0 ) {
+ continue;
+ }
+ if (mc->pending_ops > 0) {
+ continue;
+ }
+ current_time = slap_get_time();
+ if (msc->msc_ld && msc->msc_time > 0 && msc->msc_time + mi->mi_idle_timeout < current_time) {
+ asyncmeta_clear_one_msc(NULL, mc, j, 1, __FUNCTION__);
+ }
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
+ }
+
+ slap_sl_mem_setctx(ctx, oldctx);
+ current_time = slap_get_time();
+ Debug( asyncmeta_debug, "asyncmeta_timeout_loop[%p] stop at [%ld] \n", rtask, current_time );
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
+ ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
+ }
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ return NULL;
+}
+
diff --git a/servers/slapd/back-asyncmeta/modify.c b/servers/slapd/back-asyncmeta/modify.c
new file mode 100644
index 0000000..a70bae8
--- /dev/null
+++ b/servers/slapd/back-asyncmeta/modify.c
@@ -0,0 +1,357 @@
+/* modify.c - modify request handler for back-asyncmeta */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2016 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Symas Corporation
+ * based on back-meta module for inclusion in OpenLDAP Software.
+ * This work was sponsored by Ericsson. */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+#include "slap.h"
+#include "../../../libraries/liblber/lber-int.h"
+#include "../../../libraries/libldap/ldap-int.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-asyncmeta.h"
+
+meta_search_candidate_t
+asyncmeta_back_modify_start(Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ bm_context_t *bc,
+ int candidate,
+ int do_lock)
+{
+ int i, isupdate, rc = 0;
+ a_dncookie dc;
+ a_metainfo_t *mi = mc->mc_info;
+ a_metatarget_t *mt = mi->mi_targets[ candidate ];
+ LDAPMod **modv = NULL;
+ LDAPMod *mods = NULL;
+ struct berval mdn;
+ Modifications *ml;
+ meta_search_candidate_t retcode = META_SEARCH_CANDIDATE;
+ BerElement *ber = NULL;
+ a_metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+ SlapReply *candidates = bc->candidates;
+ ber_int_t msgid;
+ LDAPControl **ctrls = NULL;
+
+ /*
+ * Rewrite the modify dn, if needed
+ */
+ dc.op = op;
+ dc.target = mt;
+ dc.memctx = op->o_tmpmemctx;
+ dc.to_from = MASSAGE_REQ;
+
+ asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn );
+
+ for ( i = 0, ml = op->orm_modlist; ml; i++ ,ml = ml->sml_next )
+ ;
+ if (i > 0) {
+ mods = op->o_tmpalloc( sizeof( LDAPMod )*i, op->o_tmpmemctx );
+ }
+
+ if ( mods == NULL ) {
+ rs->sr_err = LDAP_OTHER;
+ retcode = META_SEARCH_ERR;
+ goto doreturn;
+ }
+ modv = ( LDAPMod ** )op->o_tmpalloc( ( i + 1 )*sizeof( LDAPMod * ), op->o_tmpmemctx );
+ if ( modv == NULL ) {
+ rs->sr_err = LDAP_OTHER;
+ retcode = META_SEARCH_ERR;
+ goto doreturn;
+ }
+
+ isupdate = be_shadow_update( op );
+ for ( i = 0, ml = op->orm_modlist; ml; ml = ml->sml_next ) {
+ int j;
+
+ if ( !isupdate && !get_relax( op ) && ml->sml_desc->ad_type->sat_no_user_mod )
+ {
+ continue;
+ }
+
+ modv[ i ] = &mods[ i ];
+ mods[ i ].mod_op = ml->sml_op | LDAP_MOD_BVALUES;
+ mods[ i ].mod_type = ml->sml_desc->ad_cname.bv_val;
+
+ if ( ml->sml_values != NULL ) {
+ j = ml->sml_numvals;
+ mods[ i ].mod_bvalues =(struct berval **)op->o_tmpalloc( ( j + 1 ) *sizeof( struct berval * ), op->o_tmpmemctx );
+ for ( j = 0; !BER_BVISNULL( &ml->sml_values[ j ] ); j++ ) {
+ mods[ i ].mod_bvalues[ j ] = op->o_tmpalloc(sizeof( struct berval ), op->o_tmpmemctx );
+ if ( ml->sml_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName )
+ asyncmeta_dn_massage( &dc, &ml->sml_values[ j ], mods[ i ].mod_bvalues[ j ] );
+ else
+ *mods[ i ].mod_bvalues[ j ] = ml->sml_values[ j ];
+ }
+ mods[ i ].mod_bvalues[ j ] = NULL;
+
+ } else {
+ mods[ i ].mod_bvalues = NULL;
+ }
+
+ i++;
+ }
+ modv[ i ] = 0;
+
+ asyncmeta_set_msc_time(msc);
+ ctrls = op->o_ctrls;
+ if ( asyncmeta_controls_add( op, rs, mc, candidate, bc->is_root, &ctrls) != LDAP_SUCCESS )
+ {
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ retcode = META_SEARCH_ERR;
+ goto done;
+ }
+
+ /* someone reset the connection */
+ if (!( LDAP_BACK_CONN_ISBOUND( msc )
+ || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+ Debug( asyncmeta_debug , "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+
+ ber = ldap_build_modify_req( msc->msc_ld, mdn.bv_val, modv, ctrls, NULL, &msgid);
+
+ if (!ber) {
+ Debug( asyncmeta_debug, "%s asyncmeta_back_modify_start: Operation encoding failed with errno %d\n",
+ op->o_log_prefix, msc->msc_ld->ld_errno );
+ rs->sr_err = LDAP_OPERATIONS_ERROR;
+ rs->sr_text = "Failed to encode proxied request";
+ retcode = META_SEARCH_ERR;
+ goto done;
+ }
+
+ if (ber) {
+ struct timeval tv = {0, mt->mt_network_timeout*1000};
+ ber_socket_t s;
+ if (!( LDAP_BACK_CONN_ISBOUND( msc )
+ || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+ Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+
+ ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
+ if (s < 0) {
+ Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+
+ rc = ldap_int_poll( msc->msc_ld, s, &tv, 1);
+ if (rc < 0) {
+ Debug( asyncmeta_debug, "msc %p not writable within network timeout %s:%d\n", msc, __FILE__, __LINE__ );
+ if ((msc->msc_result_time + META_BACK_RESULT_INTERVAL) < slap_get_time()) {
+ rc = LDAP_SERVER_DOWN;
+ } else {
+ goto error_unavailable;
+ }
+ } else {
+ candidates[ candidate ].sr_msgid = msgid;
+ rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_MODIFY,
+ mdn.bv_val, ber, msgid );
+ if (rc == msgid)
+ rc = LDAP_SUCCESS;
+ else
+ rc = LDAP_SERVER_DOWN;
+ ber = NULL;
+ }
+
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ retcode = META_SEARCH_CANDIDATE;
+ asyncmeta_set_msc_time(msc);
+ goto done;
+
+ case LDAP_SERVER_DOWN:
+ /* do not lock if called from asyncmeta_handle_bind_result. Also do not reset the connection */
+ if (do_lock > 0) {
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ asyncmeta_reset_msc(NULL, mc, candidate, 0, __FUNCTION__);
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ }
+ /* fall though*/
+ default:
+ Debug( asyncmeta_debug, "msc %p ldap_send_initial_request failed. %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+ }
+
+error_unavailable:
+ if (ber)
+ ber_free(ber, 1);
+ switch (bc->nretries[candidate]) {
+ case -1: /* nretries = forever */
+ ldap_pvt_thread_yield();
+ retcode = META_SEARCH_NEED_BIND;
+ break;
+ case 0: /* no retries left */
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ rs->sr_err = LDAP_UNAVAILABLE;
+ rs->sr_text = "Unable to send modify request to target";
+ retcode = META_SEARCH_ERR;
+ break;
+ default: /* more retries left - try to rebind and go again */
+ retcode = META_SEARCH_NEED_BIND;
+ bc->nretries[candidate]--;
+ ldap_pvt_thread_yield();
+ break;
+ }
+done:
+ (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+
+ if ( mdn.bv_val != op->o_req_dn.bv_val ) {
+ op->o_tmpfree( mdn.bv_val, op->o_tmpmemctx );
+ }
+
+doreturn:;
+ Debug( LDAP_DEBUG_TRACE, "%s <<< asyncmeta_back_modify_start[%p]=%d\n", op->o_log_prefix, msc, candidates[candidate].sr_msgid );
+ return retcode;
+}
+
+int
+asyncmeta_back_modify( Operation *op, SlapReply *rs )
+{
+ a_metainfo_t *mi = ( a_metainfo_t * )op->o_bd->be_private;
+ a_metatarget_t *mt;
+ a_metaconn_t *mc;
+ int rc, candidate = -1;
+ void *thrctx = op->o_threadctx;
+ bm_context_t *bc;
+ SlapReply *candidates;
+ time_t current_time = slap_get_time();
+ int max_pending_ops = (mi->mi_max_pending_ops == 0) ? META_BACK_CFG_MAX_PENDING_OPS : mi->mi_max_pending_ops;
+
+ Debug(LDAP_DEBUG_ARGS, "==> asyncmeta_back_modify: %s\n",
+ op->o_req_dn.bv_val );
+
+ if (current_time > op->o_time) {
+ Debug(asyncmeta_debug, "==> asyncmeta_back_modify[%s]: o_time:[%ld], current time: [%ld]\n",
+ op->o_log_prefix, op->o_time, current_time );
+ }
+
+ asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets, mi );
+ if (bc == NULL) {
+ rs->sr_err = LDAP_OTHER;
+ send_ldap_result(op, rs);
+ return rs->sr_err;
+ }
+
+ candidates = bc->candidates;
+ mc = asyncmeta_getconn( op, rs, candidates, &candidate, LDAP_BACK_DONTSEND, 0);
+ if ( !mc || rs->sr_err != LDAP_SUCCESS) {
+ send_ldap_result(op, rs);
+ return rs->sr_err;
+ }
+
+ mt = mi->mi_targets[ candidate ];
+ bc->timeout = mt->mt_timeout[ SLAP_OP_MODIFY ];
+ bc->retrying = LDAP_BACK_RETRYING;
+ bc->sendok = ( LDAP_BACK_SENDRESULT | bc->retrying );
+ bc->stoptime = op->o_time + bc->timeout;
+ bc->bc_active = 1;
+
+ if (mc->pending_ops >= max_pending_ops) {
+ rs->sr_err = LDAP_BUSY;
+ rs->sr_text = "Maximum pending ops limit exceeded";
+ send_ldap_result(op, rs);
+ return rs->sr_err;
+ }
+
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ rc = asyncmeta_add_message_queue(mc, bc);
+ mc->mc_conns[candidate].msc_active++;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+
+ if (rc != LDAP_SUCCESS) {
+ rs->sr_err = LDAP_BUSY;
+ rs->sr_text = "Maximum pending ops limit exceeded";
+ send_ldap_result(op, rs);
+ ldap_pvt_thread_mutex_lock(&mc->mc_om_mutex);
+ mc->mc_conns[candidate].msc_active--;
+ ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
+ goto finish;
+ }
+
+retry:
+ if (bc->timeout && bc->stoptime < slap_get_time()) {
+ int timeout_err;
+ timeout_err = op->o_protocol >= LDAP_VERSION3 ?
+ LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+ rs->sr_err = timeout_err;
+ rs->sr_text = "Operation timed out before it was sent to target";
+ asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+ goto finish;
+ }
+
+ rc = asyncmeta_dobind_init_with_retry(op, rs, bc, mc, candidate);
+ switch (rc)
+ {
+ case META_SEARCH_CANDIDATE:
+ /* target is already bound, just send the request */
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modify: "
+ "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+
+ rc = asyncmeta_back_modify_start( op, rs, mc, bc, candidate, 1);
+ if (rc == META_SEARCH_ERR) {
+ asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+ goto finish;
+
+ } else if (rc == META_SEARCH_NEED_BIND) {
+ goto retry;
+ }
+ break;
+ case META_SEARCH_NOT_CANDIDATE:
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modify: NOT_CANDIDATE "
+ "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+ asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+ goto finish;
+
+ case META_SEARCH_NEED_BIND:
+ case META_SEARCH_BINDING:
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modify: BINDING "
+ "cnd=\"%d\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
+ /* Todo add the context to the message queue but do not send the request
+ the receiver must send this when we are done binding */
+ break;
+
+ case META_SEARCH_ERR:
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modify: ERR "
+ "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+ asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+ goto finish;
+ default:
+ assert( 0 );
+ break;
+ }
+
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ mc->mc_conns[candidate].msc_active--;
+ asyncmeta_start_one_listener(mc, candidates, bc, candidate);
+ bc->bc_active--;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ rs->sr_err = SLAPD_ASYNCOP;
+
+finish:
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-asyncmeta/modrdn.c b/servers/slapd/back-asyncmeta/modrdn.c
new file mode 100644
index 0000000..03dee11
--- /dev/null
+++ b/servers/slapd/back-asyncmeta/modrdn.c
@@ -0,0 +1,367 @@
+/* modrdn.c - modrdn request handler for back-syncmeta */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2016 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Symas Corporation
+ * based on back-meta module for inclusion in OpenLDAP Software.
+ * This work was sponsored by Ericsson. */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include "slap.h"
+#include "../../../libraries/liblber/lber-int.h"
+#include "../../../libraries/libldap/ldap-int.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-asyncmeta.h"
+
+meta_search_candidate_t
+asyncmeta_back_modrdn_start(Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ bm_context_t *bc,
+ int candidate,
+ int do_lock)
+{
+ a_dncookie dc;
+ a_metainfo_t *mi = mc->mc_info;
+ a_metatarget_t *mt = mi->mi_targets[ candidate ];
+ struct berval mdn = BER_BVNULL,
+ mnewSuperior = BER_BVNULL,
+ newrdn = BER_BVNULL;
+ int rc = 0;
+ LDAPControl **ctrls = NULL;
+ meta_search_candidate_t retcode = META_SEARCH_CANDIDATE;
+ BerElement *ber = NULL;
+ a_metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+ SlapReply *candidates = bc->candidates;
+ ber_int_t msgid;
+
+ dc.op = op;
+ dc.target = mt;
+ dc.memctx = op->o_tmpmemctx;
+ dc.to_from = MASSAGE_REQ;
+
+ if ( op->orr_newSup ) {
+
+ /*
+ * NOTE: the newParent, if defined, must be on the
+ * same target as the entry to be renamed. This check
+ * has been anticipated in meta_back_getconn()
+ */
+ /*
+ * FIXME: one possibility is to delete the entry
+ * from one target and add it to the other;
+ * unfortunately we'd need write access to both,
+ * which is nearly impossible; for administration
+ * needs, the rootdn of the metadirectory could
+ * be mapped to an administrative account on each
+ * target (the binddn?); we'll see.
+ */
+ /*
+ * NOTE: we need to port the identity assertion
+ * feature from back-ldap
+ */
+
+ /* needs LDAPv3 */
+ switch ( mt->mt_version ) {
+ case LDAP_VERSION3:
+ break;
+
+ case 0:
+ if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
+ break;
+ }
+ /* fall thru */
+
+ default:
+ /* op->o_protocol cannot be anything but LDAPv3,
+ * otherwise wouldn't be here */
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ retcode = META_SEARCH_ERR;
+ goto done;
+ }
+
+ /*
+ * Rewrite the new superior, if defined and required
+ */
+ asyncmeta_dn_massage( &dc, op->orr_newSup, &mnewSuperior );
+ }
+
+ /*
+ * Rewrite the modrdn dn, if required
+ */
+ asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn );
+
+ /* NOTE: we need to copy the newRDN in case it was formed
+ * from a DN by simply changing the length (ITS#5397) */
+ newrdn = op->orr_newrdn;
+ if ( newrdn.bv_val[ newrdn.bv_len ] != '\0' ) {
+ ber_dupbv_x( &newrdn, &op->orr_newrdn, op->o_tmpmemctx );
+ }
+
+ asyncmeta_set_msc_time(msc);
+ ctrls = op->o_ctrls;
+ if ( asyncmeta_controls_add( op, rs, mc, candidate, bc->is_root, &ctrls ) != LDAP_SUCCESS )
+ {
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ retcode = META_SEARCH_ERR;
+ goto done;
+ }
+ /* someone might have reset the connection */
+ if (!( LDAP_BACK_CONN_ISBOUND( msc )
+ || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+ Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+ ber = ldap_build_moddn_req( msc->msc_ld, mdn.bv_val, newrdn.bv_val,
+ mnewSuperior.bv_val, op->orr_deleteoldrdn, ctrls, NULL, &msgid);
+
+ if (!ber) {
+ Debug( asyncmeta_debug, "%s asyncmeta_back_modrdn_start: Operation encoding failed with errno %d\n",
+ op->o_log_prefix, msc->msc_ld->ld_errno );
+ rs->sr_err = LDAP_OPERATIONS_ERROR;
+ rs->sr_text = "Failed to encode proxied request";
+ retcode = META_SEARCH_ERR;
+ goto done;
+ }
+
+ if (ber) {
+ struct timeval tv = {0, mt->mt_network_timeout*1000};
+ ber_socket_t s;
+
+ if (!( LDAP_BACK_CONN_ISBOUND( msc )
+ || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+ Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+
+ ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
+ if (s < 0) {
+ Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+
+ rc = ldap_int_poll( msc->msc_ld, s, &tv, 1);
+ if (rc < 0) {
+ Debug( asyncmeta_debug, "msc %p not writable within network timeout %s:%d\n", msc, __FILE__, __LINE__ );
+ if ((msc->msc_result_time + META_BACK_RESULT_INTERVAL) < slap_get_time()) {
+ rc = LDAP_SERVER_DOWN;
+ } else {
+ goto error_unavailable;
+ }
+ } else {
+ candidates[ candidate ].sr_msgid = msgid;
+ rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_MODRDN,
+ mdn.bv_val, ber, msgid );
+ if (rc == msgid)
+ rc = LDAP_SUCCESS;
+ else
+ rc = LDAP_SERVER_DOWN;
+ ber = NULL;
+ }
+
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ retcode = META_SEARCH_CANDIDATE;
+ asyncmeta_set_msc_time(msc);
+ goto done;
+
+ case LDAP_SERVER_DOWN:
+ /* do not lock if called from asyncmeta_handle_bind_result. Also do not reset the connection */
+ if (do_lock > 0) {
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ asyncmeta_reset_msc(NULL, mc, candidate, 0, __FUNCTION__ );
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ }
+ /* fall though*/
+ default:
+ Debug( asyncmeta_debug, "msc %p ldap_send_initial_request failed. %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+ }
+
+error_unavailable:
+ if (ber)
+ ber_free(ber, 1);
+ switch (bc->nretries[candidate]) {
+ case -1: /* nretries = forever */
+ retcode = META_SEARCH_NEED_BIND;
+ ldap_pvt_thread_yield();
+ break;
+ case 0: /* no retries left */
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ rs->sr_err = LDAP_UNAVAILABLE;
+ rs->sr_text = "Unable to send modrdn request to target";
+ retcode = META_SEARCH_ERR;
+ break;
+ default: /* more retries left - try to rebind and go again */
+ retcode = META_SEARCH_NEED_BIND;
+ bc->nretries[candidate]--;
+ ldap_pvt_thread_yield();
+ break;
+ }
+done:
+ (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+
+ if ( mdn.bv_val != op->o_req_dn.bv_val ) {
+ op->o_tmpfree( mdn.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( !BER_BVISNULL( &mnewSuperior )
+ && mnewSuperior.bv_val != op->orr_newSup->bv_val )
+ {
+ op->o_tmpfree( mnewSuperior.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( newrdn.bv_val != op->orr_newrdn.bv_val ) {
+ op->o_tmpfree( newrdn.bv_val, op->o_tmpmemctx );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "%s <<< asyncmeta_back_modrdn_start[%p]=%d\n", op->o_log_prefix, msc, candidates[candidate].sr_msgid );
+ return retcode;
+}
+
+int
+asyncmeta_back_modrdn( Operation *op, SlapReply *rs )
+{
+ a_metainfo_t *mi = ( a_metainfo_t * )op->o_bd->be_private;
+ a_metatarget_t *mt;
+ a_metaconn_t *mc;
+ int rc, candidate = -1;
+ void *thrctx = op->o_threadctx;
+ bm_context_t *bc;
+ SlapReply *candidates;
+ time_t current_time = slap_get_time();
+ int max_pending_ops = (mi->mi_max_pending_ops == 0) ? META_BACK_CFG_MAX_PENDING_OPS : mi->mi_max_pending_ops;
+
+ Debug(LDAP_DEBUG_ARGS, "==> asyncmeta_back_modrdn: %s\n",
+ op->o_req_dn.bv_val );
+
+ if (current_time > op->o_time) {
+ Debug(asyncmeta_debug, "==> asyncmeta_back_modrdn[%s]: o_time:[%ld], current time: [%ld]\n",
+ op->o_log_prefix, op->o_time, current_time );
+ }
+ asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets, mi );
+ if (bc == NULL) {
+ rs->sr_err = LDAP_OTHER;
+ send_ldap_result(op, rs);
+ return rs->sr_err;
+ }
+
+ candidates = bc->candidates;
+ mc = asyncmeta_getconn( op, rs, candidates, &candidate, LDAP_BACK_DONTSEND, 0);
+ if ( !mc || rs->sr_err != LDAP_SUCCESS) {
+ send_ldap_result(op, rs);
+ return rs->sr_err;
+ }
+
+ mt = mi->mi_targets[ candidate ];
+ bc->timeout = mt->mt_timeout[ SLAP_OP_MODRDN ];
+ bc->retrying = LDAP_BACK_RETRYING;
+ bc->sendok = ( LDAP_BACK_SENDRESULT | bc->retrying );
+ bc->stoptime = op->o_time + bc->timeout;
+ bc->bc_active = 1;
+
+ if (mc->pending_ops >= max_pending_ops) {
+ rs->sr_err = LDAP_BUSY;
+ rs->sr_text = "Maximum pending ops limit exceeded";
+ send_ldap_result(op, rs);
+ return rs->sr_err;
+ }
+
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ rc = asyncmeta_add_message_queue(mc, bc);
+ mc->mc_conns[candidate].msc_active++;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+
+ if (rc != LDAP_SUCCESS) {
+ rs->sr_err = LDAP_BUSY;
+ rs->sr_text = "Maximum pending ops limit exceeded";
+ send_ldap_result(op, rs);
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ mc->mc_conns[candidate].msc_active--;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ goto finish;
+ }
+
+retry:
+ if (bc->timeout && bc->stoptime < slap_get_time()) {
+ int timeout_err;
+ timeout_err = op->o_protocol >= LDAP_VERSION3 ?
+ LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+ rs->sr_err = timeout_err;
+ rs->sr_text = "Operation timed out before it was sent to target";
+ asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+ goto finish;
+
+ }
+
+ rc = asyncmeta_dobind_init_with_retry(op, rs, bc, mc, candidate);
+ switch (rc)
+ {
+ case META_SEARCH_CANDIDATE:
+ /* target is already bound, just send the request */
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modrdn: "
+ "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+
+ rc = asyncmeta_back_modrdn_start( op, rs, mc, bc, candidate, 1);
+ if (rc == META_SEARCH_ERR) {
+ asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+ goto finish;
+
+ } else if (rc == META_SEARCH_NEED_BIND) {
+ goto retry;
+ }
+ break;
+ case META_SEARCH_NOT_CANDIDATE:
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modrdn: NOT_CANDIDATE "
+ "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+ asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+ goto finish;
+
+ case META_SEARCH_NEED_BIND:
+ case META_SEARCH_BINDING:
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modrdn: BINDING "
+ "cnd=\"%d\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
+ /* Todo add the context to the message queue but do not send the request
+ the receiver must send this when we are done binding */
+ /* question - how would do receiver know to which targets??? */
+ break;
+
+ case META_SEARCH_ERR:
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_modrdn: ERR "
+ "cnd=\"%d\"\n", op->o_log_prefix, candidate );
+ asyncmeta_error_cleanup(op, rs, bc, mc, candidate);
+ goto finish;
+ default:
+ assert( 0 );
+ break;
+ }
+
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ mc->mc_conns[candidate].msc_active--;
+ asyncmeta_start_one_listener(mc, candidates, bc, candidate);
+ bc->bc_active--;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ rs->sr_err = SLAPD_ASYNCOP;
+finish:
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-asyncmeta/proto-asyncmeta.h b/servers/slapd/back-asyncmeta/proto-asyncmeta.h
new file mode 100644
index 0000000..54041fa
--- /dev/null
+++ b/servers/slapd/back-asyncmeta/proto-asyncmeta.h
@@ -0,0 +1,53 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2016 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Symas Corporation
+ * based on back-meta module for inclusion in OpenLDAP Software.
+ * This work was sponsored by Ericsson. */
+
+#ifndef PROTO_ASYNCMETA_H
+#define PROTO_ASYNCMETA_H
+
+LDAP_BEGIN_DECL
+
+extern BI_init asyncmeta_back_initialize;
+
+extern BI_open asyncmeta_back_open;
+extern BI_close asyncmeta_back_close;
+extern BI_destroy asyncmeta_back_destroy;
+
+extern BI_db_init asyncmeta_back_db_init;
+extern BI_db_open asyncmeta_back_db_open;
+extern BI_db_destroy asyncmeta_back_db_destroy;
+extern BI_db_close asyncmeta_back_db_close;
+extern BI_db_config asyncmeta_back_db_config;
+
+extern BI_op_bind asyncmeta_back_bind;
+extern BI_op_search asyncmeta_back_search;
+extern BI_op_compare asyncmeta_back_compare;
+extern BI_op_modify asyncmeta_back_modify;
+extern BI_op_modrdn asyncmeta_back_modrdn;
+extern BI_op_add asyncmeta_back_add;
+extern BI_op_delete asyncmeta_back_delete;
+
+extern BI_connection_destroy asyncmeta_back_conn_destroy;
+
+int asyncmeta_back_init_cf( BackendInfo *bi );
+
+LDAP_END_DECL
+
+#endif /* PROTO_ASYNCMETA_H */
diff --git a/servers/slapd/back-asyncmeta/search.c b/servers/slapd/back-asyncmeta/search.c
new file mode 100644
index 0000000..0b0db82
--- /dev/null
+++ b/servers/slapd/back-asyncmeta/search.c
@@ -0,0 +1,963 @@
+/* search.c - search request handler for back-asyncmeta */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2016-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2016 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Symas Corporation
+ * based on back-meta module for inclusion in OpenLDAP Software.
+ * This work was sponsored by Ericsson. */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include "slap.h"
+#include "../../../libraries/liblber/lber-int.h"
+#include "../../../libraries/libldap/ldap-int.h"
+#include "lutil.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-asyncmeta.h"
+
+static void
+asyncmeta_handle_onerr_stop(Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ bm_context_t *bc,
+ int candidate)
+{
+ a_metainfo_t *mi = mc->mc_info;
+ int j;
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ if (asyncmeta_bc_in_queue(mc,bc) == NULL || bc->bc_active > 1) {
+ bc->bc_active--;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ return;
+ }
+ asyncmeta_drop_bc(mc, bc);
+ for (j=0; j<mi->mi_ntargets; j++) {
+ if (j != candidate && bc->candidates[j].sr_msgid >= 0
+ && mc->mc_conns[j].msc_ld != NULL && !META_BACK_CONN_CREATING( &mc->mc_conns[j] )) {
+ asyncmeta_back_cancel( mc, op,
+ bc->candidates[ j ].sr_msgid, j );
+ }
+ }
+ slap_sl_mem_setctx(op->o_threadctx, op->o_tmpmemctx);
+ operation_counter_init( op, op->o_threadctx );
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ send_ldap_result(op, rs);
+}
+
+static int
+asyncmeta_int_filter2bv( a_dncookie *dc,
+ Filter *f,
+ struct berval *fstr )
+{
+ int i;
+ Filter *p;
+ struct berval atmp,
+ vtmp,
+ ntmp,
+ *tmp;
+ static struct berval
+ /* better than nothing... */
+ ber_bvfalse = BER_BVC( "(!(objectClass=*))" ),
+ ber_bvtf_false = BER_BVC( "(|)" ),
+ /* better than nothing... */
+ ber_bvtrue = BER_BVC( "(objectClass=*)" ),
+ ber_bvtf_true = BER_BVC( "(&)" ),
+ ber_bverror = BER_BVC( "(?=error)" ),
+ ber_bvunknown = BER_BVC( "(?=unknown)" ),
+ ber_bvnone = BER_BVC( "(?=none)" );
+ ber_len_t len;
+ void *memctx = dc->memctx;
+
+ assert( fstr != NULL );
+ BER_BVZERO( fstr );
+
+ if ( f == NULL ) {
+ ber_dupbv_x( fstr, &ber_bvnone, memctx );
+ return LDAP_OTHER;
+ }
+
+ switch ( ( f->f_choice & SLAPD_FILTER_MASK ) ) {
+ case LDAP_FILTER_EQUALITY:
+ if ( f->f_av_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) {
+ asyncmeta_dn_massage( dc, &f->f_av_value, &vtmp );
+ } else {
+ vtmp = f->f_av_value;
+ }
+
+ filter_escape_value_x( &vtmp, &ntmp, memctx );
+ fstr->bv_len = f->f_av_desc->ad_cname.bv_len + ntmp.bv_len
+ + ( sizeof("(=)") - 1 );
+ fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 1, memctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=%s)",
+ f->f_av_desc->ad_cname.bv_val, ntmp.bv_len ? ntmp.bv_val : "" );
+
+ ber_memfree_x( ntmp.bv_val, memctx );
+ break;
+
+ case LDAP_FILTER_GE:
+ filter_escape_value_x( &f->f_av_value, &ntmp, memctx );
+ fstr->bv_len = f->f_av_desc->ad_cname.bv_len + ntmp.bv_len
+ + ( sizeof("(>=)") - 1 );
+ fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 1, memctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s>=%s)",
+ f->f_av_desc->ad_cname.bv_val, ntmp.bv_len ? ntmp.bv_val : "" );
+
+ ber_memfree_x( ntmp.bv_val, memctx );
+ break;
+
+ case LDAP_FILTER_LE:
+ filter_escape_value_x( &f->f_av_value, &ntmp, memctx );
+ fstr->bv_len = f->f_av_desc->ad_cname.bv_len + ntmp.bv_len
+ + ( sizeof("(<=)") - 1 );
+ fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 1, memctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s<=%s)",
+ f->f_av_desc->ad_cname.bv_val, ntmp.bv_len ? ntmp.bv_val : "" );
+
+ ber_memfree_x( ntmp.bv_val, memctx );
+ break;
+
+ case LDAP_FILTER_APPROX:
+ filter_escape_value_x( &f->f_av_value, &ntmp, memctx );
+ fstr->bv_len = f->f_av_desc->ad_cname.bv_len + ntmp.bv_len
+ + ( sizeof("(~=)") - 1 );
+ fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 1, memctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s~=%s)",
+ f->f_av_desc->ad_cname.bv_val, ntmp.bv_len ? ntmp.bv_val : "" );
+
+ ber_memfree_x( ntmp.bv_val, memctx );
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ fstr->bv_len = f->f_sub_desc->ad_cname.bv_len + ( STRLENOF( "(=*)" ) );
+ fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 128, memctx ); /* FIXME: why 128 ? */
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
+ f->f_sub_desc->ad_cname.bv_val );
+
+ if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
+ len = fstr->bv_len;
+
+ filter_escape_value_x( &f->f_sub_initial, &ntmp, memctx );
+
+ fstr->bv_len += ntmp.bv_len;
+ fstr->bv_val = dc->op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1, memctx );
+
+ snprintf( &fstr->bv_val[len - 2], ntmp.bv_len + 3,
+ /* "(attr=" */ "%s*)",
+ ntmp.bv_len ? ntmp.bv_val : "" );
+
+ ber_memfree_x( ntmp.bv_val, memctx );
+ }
+
+ if ( f->f_sub_any != NULL ) {
+ for ( i = 0; !BER_BVISNULL( &f->f_sub_any[i] ); i++ ) {
+ len = fstr->bv_len;
+ filter_escape_value_x( &f->f_sub_any[i], &ntmp, memctx );
+
+ fstr->bv_len += ntmp.bv_len + 1;
+ fstr->bv_val = dc->op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1, memctx );
+
+ snprintf( &fstr->bv_val[len - 1], ntmp.bv_len + 3,
+ /* "(attr=[init]*[any*]" */ "%s*)",
+ ntmp.bv_len ? ntmp.bv_val : "" );
+ ber_memfree_x( ntmp.bv_val, memctx );
+ }
+ }
+
+ if ( !BER_BVISNULL( &f->f_sub_final ) ) {
+ len = fstr->bv_len;
+
+ filter_escape_value_x( &f->f_sub_final, &ntmp, memctx );
+
+ fstr->bv_len += ntmp.bv_len;
+ fstr->bv_val = dc->op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1, memctx );
+
+ snprintf( &fstr->bv_val[len - 1], ntmp.bv_len + 3,
+ /* "(attr=[init*][any*]" */ "%s)",
+ ntmp.bv_len ? ntmp.bv_val : "" );
+
+ ber_memfree_x( ntmp.bv_val, memctx );
+ }
+
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ fstr->bv_len = f->f_desc->ad_cname.bv_len + ( STRLENOF( "(=*)" ) );
+ fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 1, memctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
+ f->f_desc->ad_cname.bv_val );
+ break;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ fstr->bv_len = STRLENOF( "(%)" );
+ fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 128, memctx ); /* FIXME: why 128? */
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%c)",
+ f->f_choice == LDAP_FILTER_AND ? '&' :
+ f->f_choice == LDAP_FILTER_OR ? '|' : '!' );
+
+ for ( p = f->f_list; p != NULL; p = p->f_next ) {
+ int rc;
+
+ len = fstr->bv_len;
+
+ rc = asyncmeta_int_filter2bv( dc, p, &vtmp );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ fstr->bv_len += vtmp.bv_len;
+ fstr->bv_val = dc->op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1, memctx );
+
+ snprintf( &fstr->bv_val[len-1], vtmp.bv_len + 2,
+ /*"("*/ "%s)", vtmp.bv_len ? vtmp.bv_val : "" );
+
+ ber_memfree_x( vtmp.bv_val, memctx );
+ }
+
+ break;
+
+ case LDAP_FILTER_EXT:
+ if ( f->f_mr_desc ) {
+ atmp = f->f_mr_desc->ad_cname;
+
+ } else {
+ BER_BVSTR( &atmp, "" );
+ }
+ filter_escape_value_x( &f->f_mr_value, &ntmp, memctx );
+
+ /* FIXME: cleanup (less ?: operators...) */
+ fstr->bv_len = atmp.bv_len +
+ ( f->f_mr_dnattrs ? STRLENOF( ":dn" ) : 0 ) +
+ ( !BER_BVISEMPTY( &f->f_mr_rule_text ) ? f->f_mr_rule_text.bv_len + 1 : 0 ) +
+ ntmp.bv_len + ( STRLENOF( "(:=)" ) );
+ fstr->bv_val = dc->op->o_tmpalloc( fstr->bv_len + 1, memctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s%s%s:=%s)",
+ atmp.bv_val,
+ f->f_mr_dnattrs ? ":dn" : "",
+ !BER_BVISEMPTY( &f->f_mr_rule_text ) ? ":" : "",
+ !BER_BVISEMPTY( &f->f_mr_rule_text ) ? f->f_mr_rule_text.bv_val : "",
+ ntmp.bv_len ? ntmp.bv_val : "" );
+ ber_memfree_x( ntmp.bv_val, memctx );
+ break;
+
+ case SLAPD_FILTER_COMPUTED:
+ switch ( f->f_result ) {
+ /* FIXME: treat UNDEFINED as FALSE */
+ case SLAPD_COMPARE_UNDEFINED:
+ if ( META_BACK_TGT_NOUNDEFFILTER( dc->target ) ) {
+ return LDAP_COMPARE_FALSE;
+ }
+ /* fallthru */
+
+ case LDAP_COMPARE_FALSE:
+ if ( META_BACK_TGT_T_F( dc->target ) ) {
+ tmp = &ber_bvtf_false;
+ break;
+ }
+ tmp = &ber_bvfalse;
+ break;
+
+ case LDAP_COMPARE_TRUE:
+ if ( META_BACK_TGT_T_F( dc->target ) ) {
+ tmp = &ber_bvtf_true;
+ break;
+ }
+
+ tmp = &ber_bvtrue;
+ break;
+
+ default:
+ tmp = &ber_bverror;
+ break;
+ }
+
+ ber_dupbv_x( fstr, tmp, memctx );
+ break;
+
+ default:
+ ber_dupbv_x( fstr, &ber_bvunknown, memctx );
+ break;
+ }
+
+ return 0;
+}
+meta_search_candidate_t
+asyncmeta_back_search_start(
+ Operation *op,
+ SlapReply *rs,
+ a_metaconn_t *mc,
+ bm_context_t *bc,
+ int candidate,
+ struct berval *prcookie,
+ ber_int_t prsize,
+ int do_lock)
+{
+ SlapReply *candidates = bc->candidates;
+ a_metainfo_t *mi = ( a_metainfo_t * )mc->mc_info;
+ a_metatarget_t *mt = mi->mi_targets[ candidate ];
+ a_metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+ a_dncookie dc;
+ struct berval realbase = op->o_req_dn;
+ char **attrs;
+ int realscope = op->ors_scope;
+ struct berval mbase = BER_BVNULL;
+ int rc;
+ struct berval filterbv = BER_BVNULL;
+ meta_search_candidate_t retcode;
+ int timelimit;
+ LDAPControl **ctrls = NULL;
+ BerElement *ber = NULL;
+ ber_int_t msgid;
+ ber_socket_t s = -1;
+#ifdef SLAPD_META_CLIENT_PR
+ LDAPControl **save_ctrls = NULL;
+#endif /* SLAPD_META_CLIENT_PR */
+
+ /* this should not happen; just in case... */
+ if ( msc->msc_ld == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: asyncmeta_back_search_start candidate=%d ld=NULL%s.\n",
+ op->o_log_prefix, candidate,
+ META_BACK_ONERR_STOP( mi ) ? "" : " (ignored)" );
+ candidates[ candidate ].sr_err = LDAP_OTHER;
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ return META_SEARCH_ERR;
+ }
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ return META_SEARCH_NOT_CANDIDATE;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "%s >>> asyncmeta_back_search_start: dn=%s filter=%s\n",
+ op->o_log_prefix, op->o_req_dn.bv_val, op->ors_filterstr.bv_val );
+ /*
+ * modifies the base according to the scope, if required
+ */
+ if ( mt->mt_nsuffix.bv_len > op->o_req_ndn.bv_len ) {
+ switch ( op->ors_scope ) {
+ case LDAP_SCOPE_SUBTREE:
+ /*
+ * make the target suffix the new base
+ * FIXME: this is very forgiving, because
+ * "illegal" searchBases may be turned
+ * into the suffix of the target; however,
+ * the requested searchBase already passed
+ * thru the candidate analyzer...
+ */
+ if ( dnIsSuffix( &mt->mt_nsuffix, &op->o_req_ndn ) ) {
+ realbase = mt->mt_nsuffix;
+ if ( mt->mt_scope == LDAP_SCOPE_SUBORDINATE ) {
+ realscope = LDAP_SCOPE_SUBORDINATE;
+ }
+
+ } else {
+ /*
+ * this target is no longer candidate
+ */
+ retcode = META_SEARCH_NOT_CANDIDATE;
+ goto doreturn;
+ }
+ break;
+
+ case LDAP_SCOPE_SUBORDINATE:
+ case LDAP_SCOPE_ONELEVEL:
+ {
+ struct berval rdn = mt->mt_nsuffix;
+ rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
+ if ( dnIsOneLevelRDN( &rdn )
+ && dnIsSuffix( &mt->mt_nsuffix, &op->o_req_ndn ) )
+ {
+ /*
+ * if there is exactly one level,
+ * make the target suffix the new
+ * base, and make scope "base"
+ */
+ realbase = mt->mt_nsuffix;
+ if ( op->ors_scope == LDAP_SCOPE_SUBORDINATE ) {
+ if ( mt->mt_scope == LDAP_SCOPE_SUBORDINATE ) {
+ realscope = LDAP_SCOPE_SUBORDINATE;
+ } else {
+ realscope = LDAP_SCOPE_SUBTREE;
+ }
+ } else {
+ realscope = LDAP_SCOPE_BASE;
+ }
+ break;
+ } /* else continue with the next case */
+ }
+
+ case LDAP_SCOPE_BASE:
+ /*
+ * this target is no longer candidate
+ */
+ retcode = META_SEARCH_NOT_CANDIDATE;
+ goto doreturn;
+ }
+ }
+
+ /* check filter expression */
+ if ( mt->mt_filter ) {
+ metafilter_t *mf;
+ for ( mf = mt->mt_filter; mf; mf = mf->mf_next ) {
+ if ( regexec( &mf->mf_regex, op->ors_filterstr.bv_val, 0, NULL, 0 ) == 0 )
+ break;
+ }
+ /* nothing matched, this target is no longer a candidate */
+ if ( !mf ) {
+ retcode = META_SEARCH_NOT_CANDIDATE;
+ goto doreturn;
+ }
+ }
+
+ /*
+ * Rewrite the search base, if required
+ */
+ dc.op = op;
+ dc.target = mt;
+ dc.memctx = op->o_tmpmemctx;
+ dc.to_from = MASSAGE_REQ;
+ asyncmeta_dn_massage( &dc, &realbase, &mbase );
+
+ attrs = anlist2charray_x( op->ors_attrs, 0, op->o_tmpmemctx );
+
+ if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
+ timelimit = op->ors_tlimit > 0 ? op->ors_tlimit : 1;
+ } else {
+ timelimit = -1; /* no limit */
+ }
+
+#ifdef SLAPD_META_CLIENT_PR
+ save_ctrls = op->o_ctrls;
+ {
+ LDAPControl *pr_c = NULL;
+ int i = 0, nc = 0;
+
+ if ( save_ctrls ) {
+ for ( ; save_ctrls[i] != NULL; i++ );
+ nc = i;
+ pr_c = ldap_control_find( LDAP_CONTROL_PAGEDRESULTS, save_ctrls, NULL );
+ }
+
+ if ( pr_c != NULL ) nc--;
+ if ( mt->mt_ps > 0 || prcookie != NULL ) nc++;
+
+ if ( mt->mt_ps > 0 || prcookie != NULL || pr_c != NULL ) {
+ int src = 0, dst = 0;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ struct berval val = BER_BVNULL;
+ ber_len_t len;
+
+ len = sizeof( LDAPControl * )*( nc + 1 ) + sizeof( LDAPControl );
+
+ if ( mt->mt_ps > 0 || prcookie != NULL ) {
+ struct berval nullcookie = BER_BVNULL;
+ ber_tag_t tag;
+
+ if ( prsize == 0 && mt->mt_ps > 0 ) prsize = mt->mt_ps;
+ if ( prcookie == NULL ) prcookie = &nullcookie;
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+ tag = ber_printf( ber, "{iO}", prsize, prcookie );
+ if ( tag == LBER_ERROR ) {
+ /* error */
+ (void) ber_free_buf( ber );
+ goto done_pr;
+ }
+
+ tag = ber_flatten2( ber, &val, 0 );
+ if ( tag == LBER_ERROR ) {
+ /* error */
+ (void) ber_free_buf( ber );
+ goto done_pr;
+ }
+
+ len += val.bv_len + 1;
+ }
+
+ op->o_ctrls = op->o_tmpalloc( len, op->o_tmpmemctx );
+ if ( save_ctrls ) {
+ for ( ; save_ctrls[ src ] != NULL; src++ ) {
+ if ( save_ctrls[ src ] != pr_c ) {
+ op->o_ctrls[ dst ] = save_ctrls[ src ];
+ dst++;
+ }
+ }
+ }
+
+ if ( mt->mt_ps > 0 || prcookie != NULL ) {
+ op->o_ctrls[ dst ] = (LDAPControl *)&op->o_ctrls[ nc + 1 ];
+
+ op->o_ctrls[ dst ]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
+ op->o_ctrls[ dst ]->ldctl_iscritical = 1;
+
+ op->o_ctrls[ dst ]->ldctl_value.bv_val = (char *)&op->o_ctrls[ dst ][ 1 ];
+ AC_MEMCPY( op->o_ctrls[ dst ]->ldctl_value.bv_val, val.bv_val, val.bv_len + 1 );
+ op->o_ctrls[ dst ]->ldctl_value.bv_len = val.bv_len;
+ dst++;
+
+ (void)ber_free_buf( ber );
+ }
+
+ op->o_ctrls[ dst ] = NULL;
+ }
+done_pr:;
+ }
+#endif /* SLAPD_META_CLIENT_PR */
+
+ asyncmeta_set_msc_time(msc);
+ ctrls = op->o_ctrls;
+
+ if ( asyncmeta_controls_add( op, rs, mc, candidate, bc->is_root, &ctrls )
+ != LDAP_SUCCESS )
+ {
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ retcode = META_SEARCH_NOT_CANDIDATE;
+ goto done;
+ }
+
+ /*
+ * Starts the search
+ */
+ /* someone reset the connection */
+ if (!( LDAP_BACK_CONN_ISBOUND( msc )
+ || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+ Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+ rc = asyncmeta_int_filter2bv( &dc, op->ors_filter, &filterbv );
+ if ( rc ) {
+ retcode = META_SEARCH_ERR;
+ goto done;
+ }
+
+ ber = ldap_build_search_req( msc->msc_ld,
+ mbase.bv_val, realscope, filterbv.bv_val,
+ attrs, op->ors_attrsonly,
+ ctrls, NULL, timelimit, op->ors_slimit, op->ors_deref,
+ &msgid );
+ if (!ber) {
+ Debug( asyncmeta_debug, "%s asyncmeta_back_search_start: Operation encoding failed with errno %d\n",
+ op->o_log_prefix, msc->msc_ld->ld_errno );
+ rs->sr_err = LDAP_OPERATIONS_ERROR;
+ rs->sr_text = "Failed to encode proxied request";
+ retcode = META_SEARCH_ERR;
+ goto done;
+ }
+
+ if (ber) {
+ struct timeval tv = {0, mt->mt_network_timeout*1000};
+
+ if (!( LDAP_BACK_CONN_ISBOUND( msc )
+ || LDAP_BACK_CONN_ISANON( msc )) || msc->msc_ld == NULL ) {
+ Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+
+ ldap_get_option( msc->msc_ld, LDAP_OPT_DESC, &s );
+ if (s < 0) {
+ Debug( asyncmeta_debug, "msc %p not initialized at %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+ }
+
+ rc = ldap_int_poll( msc->msc_ld, s, &tv, 1);
+ if (rc < 0) {
+ Debug( asyncmeta_debug, "msc %p not writable within network timeout %s:%d\n", msc, __FILE__, __LINE__ );
+ if ((msc->msc_result_time + META_BACK_RESULT_INTERVAL) < slap_get_time()) {
+ rc = LDAP_SERVER_DOWN;
+ } else {
+ goto error_unavailable;
+ }
+ } else {
+ candidates[ candidate ].sr_msgid = msgid;
+ rc = ldap_send_initial_request( msc->msc_ld, LDAP_REQ_SEARCH,
+ mbase.bv_val, ber, msgid );
+ if (rc == msgid)
+ rc = LDAP_SUCCESS;
+ else
+ rc = LDAP_SERVER_DOWN;
+ ber = NULL;
+ }
+
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ retcode = META_SEARCH_CANDIDATE;
+ asyncmeta_set_msc_time(msc);
+ goto done;
+
+ case LDAP_SERVER_DOWN:
+ /* do not lock if called from asyncmeta_handle_bind_result. Also do not reset the connection */
+ if (do_lock > 0) {
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ asyncmeta_reset_msc(NULL, mc, candidate, 0, __FUNCTION__);
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ }
+ Debug( asyncmeta_debug, "msc %p ldap_send_initial_request failed. %s:%d\n", msc, __FILE__, __LINE__ );
+ goto error_unavailable;
+
+ default:
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ retcode = META_SEARCH_NOT_CANDIDATE;
+ goto done;
+ }
+ }
+
+error_unavailable:
+ if (ber)
+ ber_free(ber, 1);
+ switch (bc->nretries[candidate]) {
+ case -1: /* nretries = forever */
+ retcode = META_SEARCH_NEED_BIND;
+ ldap_pvt_thread_yield();
+ break;
+ case 0: /* no retries left */
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ rs->sr_err = LDAP_UNAVAILABLE;
+ rs->sr_text = "Unable to send search request to target";
+ retcode = META_SEARCH_ERR;
+ break;
+ default: /* more retries left - try to rebind and go again */
+ retcode = META_SEARCH_NEED_BIND;
+ bc->nretries[candidate]--;
+ ldap_pvt_thread_yield();
+ break;
+ }
+done:;
+#if 0
+ (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+#endif
+#ifdef SLAPD_META_CLIENT_PR
+ if ( save_ctrls != op->o_ctrls ) {
+ op->o_tmpfree( op->o_ctrls, op->o_tmpmemctx );
+ op->o_ctrls = save_ctrls;
+ }
+#endif /* SLAPD_META_CLIENT_PR */
+
+ if ( mbase.bv_val != realbase.bv_val ) {
+ op->o_tmpfree( mbase.bv_val, op->o_tmpmemctx );
+ }
+
+doreturn:;
+ Debug( LDAP_DEBUG_TRACE, "%s <<< asyncmeta_back_search_start[%p] (fd %d)=%d\n", op->o_log_prefix, msc, s, candidates[candidate].sr_msgid );
+ return retcode;
+}
+
+int
+asyncmeta_back_search( Operation *op, SlapReply *rs )
+{
+ a_metainfo_t *mi = ( a_metainfo_t * )op->o_bd->be_private;
+ time_t timeout = 0;
+ int rc = 0;
+ int ncandidates = 0, initial_candidates = 0;
+ long i;
+ SlapReply *candidates = NULL;
+ void *thrctx = op->o_threadctx;
+ bm_context_t *bc;
+ a_metaconn_t *mc;
+ int msc_decr = 0;
+ int max_pending_ops = (mi->mi_max_pending_ops == 0) ? META_BACK_CFG_MAX_PENDING_OPS : mi->mi_max_pending_ops;
+ int check_bind = 0;
+
+ rs_assert_ready( rs );
+ rs->sr_flags &= ~REP_ENTRY_MASK; /* paranoia, we can set rs = non-entry */
+
+ /*
+ * controls are set in ldap_back_dobind()
+ *
+ * FIXME: in case of values return filter, we might want
+ * to map attrs and maybe rewrite value
+ */
+
+ asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets, mi );
+ if (bc == NULL) {
+ rs->sr_err = LDAP_OTHER;
+ send_ldap_result(op, rs);
+ return rs->sr_err;
+ }
+
+ candidates = bc->candidates;
+ mc = asyncmeta_getconn( op, rs, candidates, NULL, LDAP_BACK_DONTSEND, 0);
+ if ( !mc || rs->sr_err != LDAP_SUCCESS) {
+ send_ldap_result(op, rs);
+ return rs->sr_err;
+ }
+
+ /*
+ * Inits searches
+ */
+
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ /* reset sr_msgid; it is used in most loops
+ * to check if that target is still to be considered */
+ candidates[i].sr_msgid = META_MSGID_UNDEFINED;
+ /* a target is marked as candidate by asyncmeta_getconn();
+ * if for any reason (an error, it's over or so) it is
+ * no longer active, sr_msgid is set to META_MSGID_IGNORE
+ * but it remains candidate, which means it has been active
+ * at some point during the operation. This allows to
+ * use its response code and more to compute the final
+ * response */
+ if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
+ continue;
+ }
+
+ candidates[ i ].sr_matched = NULL;
+ candidates[ i ].sr_text = NULL;
+ candidates[ i ].sr_ref = NULL;
+ candidates[ i ].sr_ctrls = NULL;
+ candidates[ i ].sr_nentries = 0;
+ candidates[ i ].sr_type = -1;
+
+ /* get largest timeout among candidates */
+ if ( mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ]
+ && mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ] > timeout )
+ {
+ timeout = mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ];
+ }
+ }
+
+ if ( op->ors_tlimit != SLAP_NO_LIMIT && (timeout == 0 || op->ors_tlimit < timeout)) {
+ bc->searchtime = 1;
+ bc->timeout = op->ors_tlimit;
+ } else {
+ bc->timeout = timeout;
+ }
+
+ bc->stoptime = op->o_time + bc->timeout;
+ bc->bc_active = 1;
+
+ if (mc->pending_ops >= max_pending_ops) {
+ rs->sr_err = LDAP_BUSY;
+ rs->sr_text = "Maximum pending ops limit exceeded";
+ send_ldap_result(op, rs);
+ return rs->sr_err;
+ }
+
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ rc = asyncmeta_add_message_queue(mc, bc);
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ mc->mc_conns[i].msc_active++;
+ }
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+
+ if (rc != LDAP_SUCCESS) {
+ rs->sr_err = LDAP_BUSY;
+ rs->sr_text = "Maximum pending ops limit exceeded";
+ send_ldap_result(op, rs);
+ goto finish;
+ }
+
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ if ( !META_IS_CANDIDATE( &candidates[ i ] )
+ || candidates[ i ].sr_err != LDAP_SUCCESS )
+ {
+ continue;
+ }
+retry:
+ if (bc->timeout && bc->stoptime < slap_get_time() && META_BACK_ONERR_STOP( mi )) {
+ int timeout_err;
+ const char *timeout_text;
+ if (bc->searchtime) {
+ timeout_err = LDAP_TIMELIMIT_EXCEEDED;
+ timeout_text = NULL;
+ } else {
+ timeout_err = op->o_protocol >= LDAP_VERSION3 ?
+ LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+ timeout_text = "Operation timed out before it was sent to target";
+ }
+ rs->sr_err = timeout_err;
+ rs->sr_text = timeout_text;
+ asyncmeta_handle_onerr_stop(op,rs,mc,bc,i);
+ goto finish;
+
+ }
+
+ if (op->o_abandon) {
+ rs->sr_err = SLAPD_ABANDON;
+ asyncmeta_handle_onerr_stop(op,rs,mc,bc,i);
+ goto finish;
+ }
+
+ rc = asyncmeta_dobind_init_with_retry(op, rs, bc, mc, i);
+ switch (rc)
+ {
+ case META_SEARCH_CANDIDATE:
+ /* target is already bound, just send the search request */
+ ncandidates++;
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: IS_CANDIDATE "
+ "cnd=\"%ld\"\n", op->o_log_prefix, i );
+
+ rc = asyncmeta_back_search_start( op, rs, mc, bc, i, NULL, 0 , 1);
+ if (rc == META_SEARCH_ERR) {
+ META_CANDIDATE_CLEAR(&candidates[i]);
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ asyncmeta_handle_onerr_stop(op,rs,mc,bc,i);
+ goto finish;
+ }
+ else {
+ continue;
+ }
+ } else if (rc == META_SEARCH_NEED_BIND) {
+ goto retry;
+ }
+ break;
+ case META_SEARCH_NOT_CANDIDATE:
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: NOT_CANDIDATE "
+ "cnd=\"%ld\"\n", op->o_log_prefix, i );
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ break;
+
+ case META_SEARCH_NEED_BIND:
+ case META_SEARCH_BINDING:
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: BINDING "
+ "cnd=\"%ld\" mc %p msc %p\n", op->o_log_prefix, i , mc, &mc->mc_conns[i]);
+ check_bind++;
+ ncandidates++;
+ /* Todo add the context to the message queue but do not send the request
+ the receiver must send this when we are done binding */
+ /* question - how would do receiver know to which targets??? */
+ break;
+
+ case META_SEARCH_ERR:
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: SEARCH_ERR "
+ "cnd=\"%ldd\"\n", op->o_log_prefix, i );
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ candidates[ i ].sr_type = REP_RESULT;
+
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ asyncmeta_handle_onerr_stop(op,rs,mc,bc,i);
+ goto finish;
+ }
+ else {
+ continue;
+ }
+ break;
+
+ default:
+ assert( 0 );
+ break;
+ }
+ }
+
+ initial_candidates = ncandidates;
+
+ if ( LogTest( LDAP_DEBUG_TRACE ) ) {
+ char cnd[ SLAP_TEXT_BUFLEN ];
+ int c;
+
+ for ( c = 0; c < mi->mi_ntargets; c++ ) {
+ if ( META_IS_CANDIDATE( &candidates[ c ] ) ) {
+ cnd[ c ] = '*';
+ } else {
+ cnd[ c ] = ' ';
+ }
+ }
+ cnd[ c ] = '\0';
+
+ Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: ncandidates=%d "
+ "cnd=\"%s\"\n", op->o_log_prefix, ncandidates, cnd );
+ }
+
+ if ( initial_candidates == 0 ) {
+ /* NOTE: here we are not sending any matchedDN;
+ * this is intended, because if the back-meta
+ * is serving this search request, but no valid
+ * candidate could be looked up, it means that
+ * there is a hole in the mapping of the targets
+ * and thus no knowledge of any remote superior
+ * is available */
+ Debug( LDAP_DEBUG_ANY, "%s asyncmeta_back_search: "
+ "base=\"%s\" scope=%d: "
+ "no candidate could be selected\n",
+ op->o_log_prefix, op->o_req_dn.bv_val,
+ op->ors_scope );
+
+ /* FIXME: we're sending the first error we encounter;
+ * maybe we should pick the worst... */
+ rc = LDAP_NO_SUCH_OBJECT;
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ if ( META_IS_CANDIDATE( &candidates[ i ] )
+ && candidates[ i ].sr_err != LDAP_SUCCESS )
+ {
+ rc = candidates[ i ].sr_err;
+ break;
+ }
+ }
+ rs->sr_err = rc;
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ asyncmeta_drop_bc(mc, bc);
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ send_ldap_result(op, rs);
+ goto finish;
+ }
+
+ /* If we were processing many targets the result from a pending Bind
+ * on an earlier target may have arrived while we were sending to a
+ * later target. See if we can now send our pending request.
+ */
+ if ( check_bind ) {
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ if ( candidates[ i ].sr_msgid == META_MSGID_GOT_BIND ) {
+ rc = asyncmeta_back_search_start( op, rs, mc, bc, i, NULL, 0, 1 );
+ if ( rc == META_SEARCH_ERR ) {
+ META_CANDIDATE_CLEAR( &candidates[i] );
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ asyncmeta_handle_onerr_stop(op,rs,mc,bc,i);
+ goto finish;
+ }
+ }
+ }
+ }
+ }
+
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ mc->mc_conns[i].msc_active--;
+ }
+ msc_decr = 1;
+
+ asyncmeta_start_listeners(mc, candidates, bc);
+ bc->bc_active--;
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ rs->sr_err = SLAPD_ASYNCOP;
+
+finish:
+ /* we ended up straight here due to error and need to reset the msc_active*/
+ if (msc_decr == 0) {
+ ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ mc->mc_conns[i].msc_active--;
+ }
+ ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
+ }
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-dnssrv/Makefile.in b/servers/slapd/back-dnssrv/Makefile.in
new file mode 100644
index 0000000..cf571e0
--- /dev/null
+++ b/servers/slapd/back-dnssrv/Makefile.in
@@ -0,0 +1,46 @@
+# Makefile.in for back-dnssrv
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## Portions Copyright 1998-2003 Kurt D. Zeilenga.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+#
+# ACKNOWLEDGEMENTS:
+# The DNSSRV backend was written by Kurt D. Zeilenga.
+#
+
+SRCS = init.c bind.c search.c config.c referral.c
+OBJS = init.lo bind.lo search.lo config.lo referral.lo
+
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-dnssrv"
+BUILD_MOD = @BUILD_DNSSRV@
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = $(@BUILD_DNSSRV@_DEFS)
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+
+LIBBASE = back_dnssrv
+
+XINCPATH = -I.. -I$(srcdir)/..
+XDEFS = $(MODULES_CPPFLAGS)
+
+all-local-lib: ../.backend
+
+../.backend: lib$(LIBBASE).a
+ @touch $@
+
diff --git a/servers/slapd/back-dnssrv/bind.c b/servers/slapd/back-dnssrv/bind.c
new file mode 100644
index 0000000..705c503
--- /dev/null
+++ b/servers/slapd/back-dnssrv/bind.c
@@ -0,0 +1,79 @@
+/* bind.c - DNS SRV backend bind function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2000-2003 Kurt D. Zeilenga.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by Kurt D. Zeilenga for inclusion
+ * in OpenLDAP Software.
+ */
+
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "proto-dnssrv.h"
+
+int
+dnssrv_back_bind(
+ Operation *op,
+ SlapReply *rs )
+{
+ Debug( LDAP_DEBUG_TRACE, "DNSSRV: bind dn=\"%s\" (%d)\n",
+ BER_BVISNULL( &op->o_req_dn ) ? "" : op->o_req_dn.bv_val,
+ op->orb_method );
+
+ /* allow rootdn as a means to auth without the need to actually
+ * contact the proxied DSA */
+ switch ( be_rootdn_bind( op, NULL ) ) {
+ case LDAP_SUCCESS:
+ /* frontend will send result */
+ return rs->sr_err;
+
+ default:
+ /* treat failure and like any other bind, otherwise
+ * it could reveal the DN of the rootdn */
+ break;
+ }
+
+ if ( !BER_BVISNULL( &op->orb_cred ) &&
+ !BER_BVISEMPTY( &op->orb_cred ) )
+ {
+ /* simple bind */
+ Debug( LDAP_DEBUG_STATS,
+ "%s DNSSRV BIND dn=\"%s\" provided cleartext passwd\n",
+ op->o_log_prefix,
+ BER_BVISNULL( &op->o_req_dn ) ? "" : op->o_req_dn.bv_val );
+
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "you shouldn't send strangers your password" );
+
+ } else {
+ /* unauthenticated bind */
+ /* NOTE: we're not going to get here anyway:
+ * unauthenticated bind is dealt with by the frontend */
+ Debug( LDAP_DEBUG_TRACE, "DNSSRV: BIND dn=\"%s\"\n",
+ BER_BVISNULL( &op->o_req_dn ) ? "" : op->o_req_dn.bv_val );
+
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "anonymous bind expected" );
+ }
+
+ return 1;
+}
diff --git a/servers/slapd/back-dnssrv/compare.c b/servers/slapd/back-dnssrv/compare.c
new file mode 100644
index 0000000..28a0f6c
--- /dev/null
+++ b/servers/slapd/back-dnssrv/compare.c
@@ -0,0 +1,46 @@
+/* compare.c - DNS SRV backend compare function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2000-2003 Kurt D. Zeilenga.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by Kurt D. Zeilenga for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "proto-dnssrv.h"
+
+int
+dnssrv_back_compare(
+ Operation *op,
+ SlapReply *rs
+)
+{
+#if 0
+ assert( get_manageDSAit( op ) );
+#endif
+ send_ldap_error( op, rs, LDAP_OTHER,
+ "Operation not supported within naming context" );
+
+ /* not implemented */
+ return 1;
+}
diff --git a/servers/slapd/back-dnssrv/config.c b/servers/slapd/back-dnssrv/config.c
new file mode 100644
index 0000000..32e412e
--- /dev/null
+++ b/servers/slapd/back-dnssrv/config.c
@@ -0,0 +1,54 @@
+/* config.c - DNS SRV backend configuration file routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2000-2003 Kurt D. Zeilenga.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by Kurt D. Zeilenga for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "proto-dnssrv.h"
+
+#if 0
+int
+dnssrv_back_db_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv )
+{
+#if 0
+ struct ldapinfo *li = (struct ldapinfo *) be->be_private;
+
+ if ( li == NULL ) {
+ fprintf( stderr, "%s: line %d: DNSSRV backend info is null!\n",
+ fname, lineno );
+ return( 1 );
+ }
+#endif
+
+ /* no configuration options (yet) */
+ return SLAP_CONF_UNKNOWN;
+}
+#endif
diff --git a/servers/slapd/back-dnssrv/init.c b/servers/slapd/back-dnssrv/init.c
new file mode 100644
index 0000000..a253be7
--- /dev/null
+++ b/servers/slapd/back-dnssrv/init.c
@@ -0,0 +1,115 @@
+/* init.c - initialize ldap backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2000-2003 Kurt D. Zeilenga.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by Kurt D. Zeilenga for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/param.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "proto-dnssrv.h"
+
+int
+dnssrv_back_initialize(
+ BackendInfo *bi )
+{
+ static char *controls[] = {
+ LDAP_CONTROL_MANAGEDSAIT,
+ NULL
+ };
+
+ bi->bi_controls = controls;
+
+ bi->bi_open = dnssrv_back_open;
+ bi->bi_config = 0;
+ bi->bi_close = 0;
+ bi->bi_destroy = 0;
+
+ bi->bi_db_init = 0;
+ bi->bi_db_destroy = 0;
+ bi->bi_db_config = 0 /* dnssrv_back_db_config */;
+ bi->bi_db_open = 0;
+ bi->bi_db_close = 0;
+
+ bi->bi_chk_referrals = dnssrv_back_referrals;
+
+ bi->bi_op_bind = dnssrv_back_bind;
+ bi->bi_op_search = dnssrv_back_search;
+ bi->bi_op_compare = 0 /* dnssrv_back_compare */;
+ bi->bi_op_modify = 0;
+ bi->bi_op_modrdn = 0;
+ bi->bi_op_add = 0;
+ bi->bi_op_delete = 0;
+ bi->bi_op_abandon = 0;
+ bi->bi_op_unbind = 0;
+
+ bi->bi_extended = 0;
+
+ bi->bi_connection_init = 0;
+ bi->bi_connection_destroy = 0;
+
+ bi->bi_access_allowed = slap_access_always_allowed;
+
+ return 0;
+}
+
+AttributeDescription *ad_dc;
+AttributeDescription *ad_associatedDomain;
+
+int
+dnssrv_back_open(
+ BackendInfo *bi )
+{
+ const char *text;
+
+ (void)slap_str2ad( "dc", &ad_dc, &text );
+ (void)slap_str2ad( "associatedDomain", &ad_associatedDomain, &text );
+
+ return 0;
+}
+
+int
+dnssrv_back_db_init(
+ Backend *be,
+ ConfigReply *cr)
+{
+ return 0;
+}
+
+int
+dnssrv_back_db_destroy(
+ Backend *be,
+ ConfigReply *cr )
+{
+ return 0;
+}
+
+#if SLAPD_DNSSRV == SLAPD_MOD_DYNAMIC
+
+/* conditionally define the init_module() function */
+SLAP_BACKEND_INIT_MODULE( dnssrv )
+
+#endif /* SLAPD_DNSSRV == SLAPD_MOD_DYNAMIC */
+
diff --git a/servers/slapd/back-dnssrv/proto-dnssrv.h b/servers/slapd/back-dnssrv/proto-dnssrv.h
new file mode 100644
index 0000000..23b01aa
--- /dev/null
+++ b/servers/slapd/back-dnssrv/proto-dnssrv.h
@@ -0,0 +1,46 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by Kurt D. Zeilenga for inclusion
+ * in OpenLDAP Software.
+ */
+
+#ifndef PROTO_DNSSRV_H
+#define PROTO_DNSSRV_H
+
+LDAP_BEGIN_DECL
+
+extern BI_init dnssrv_back_initialize;
+
+extern BI_open dnssrv_back_open;
+extern BI_close dnssrv_back_close;
+extern BI_destroy dnssrv_back_destroy;
+
+extern BI_db_init dnssrv_back_db_init;
+extern BI_db_destroy dnssrv_back_db_destroy;
+extern BI_db_config dnssrv_back_db_config;
+
+extern BI_op_bind dnssrv_back_bind;
+extern BI_op_search dnssrv_back_search;
+extern BI_op_compare dnssrv_back_compare;
+
+extern BI_chk_referrals dnssrv_back_referrals;
+
+extern AttributeDescription *ad_dc;
+extern AttributeDescription *ad_associatedDomain;
+
+LDAP_END_DECL
+
+#endif /* PROTO_DNSSRV_H */
diff --git a/servers/slapd/back-dnssrv/referral.c b/servers/slapd/back-dnssrv/referral.c
new file mode 100644
index 0000000..c3b801a
--- /dev/null
+++ b/servers/slapd/back-dnssrv/referral.c
@@ -0,0 +1,129 @@
+/* referral.c - DNS SRV backend referral handler */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2000-2003 Kurt D. Zeilenga.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by Kurt D. Zeilenga for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "proto-dnssrv.h"
+
+int
+dnssrv_back_referrals(
+ Operation *op,
+ SlapReply *rs )
+{
+ int i;
+ int rc = LDAP_OTHER;
+ char *domain = NULL;
+ char *hostlist = NULL;
+ char **hosts = NULL;
+ BerVarray urls = NULL;
+
+ if ( BER_BVISEMPTY( &op->o_req_dn ) ) {
+ /* FIXME: need some means to determine whether the database
+ * is a glue instance */
+ if ( SLAP_GLUE_INSTANCE( op->o_bd ) ) {
+ return LDAP_SUCCESS;
+ }
+
+ rs->sr_text = "DNS SRV operation upon null (empty) DN disallowed";
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ if( get_manageDSAit( op ) ) {
+ if( op->o_tag == LDAP_REQ_SEARCH ) {
+ return LDAP_SUCCESS;
+ }
+
+ rs->sr_text = "DNS SRV problem processing manageDSAit control";
+ return LDAP_OTHER;
+ }
+
+ if( ldap_dn2domain( op->o_req_dn.bv_val, &domain ) || domain == NULL ) {
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_ref = default_referral;
+ send_ldap_result( op, rs );
+ rs->sr_ref = NULL;
+ return LDAP_REFERRAL;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "DNSSRV: dn=\"%s\" -> domain=\"%s\"\n",
+ op->o_req_dn.bv_val, domain );
+
+ i = ldap_domain2hostlist( domain, &hostlist );
+ if ( i ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "DNSSRV: domain2hostlist(%s) returned %d\n",
+ domain, i );
+ rs->sr_text = "no DNS SRV RR available for DN";
+ rc = LDAP_NO_SUCH_OBJECT;
+ goto done;
+ }
+
+ hosts = ldap_str2charray( hostlist, " " );
+
+ if( hosts == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "DNSSRV: str2charray error\n" );
+ rs->sr_text = "problem processing DNS SRV records for DN";
+ goto done;
+ }
+
+ for( i=0; hosts[i] != NULL; i++) {
+ struct berval url;
+
+ url.bv_len = STRLENOF( "ldap://" ) + strlen( hosts[i] );
+ url.bv_val = ch_malloc( url.bv_len + 1 );
+
+ strcpy( url.bv_val, "ldap://" );
+ strcpy( &url.bv_val[STRLENOF( "ldap://" )], hosts[i] );
+
+ if ( ber_bvarray_add( &urls, &url ) < 0 ) {
+ free( url.bv_val );
+ rs->sr_text = "problem processing DNS SRV records for DN";
+ goto done;
+ }
+ }
+
+ Debug( LDAP_DEBUG_STATS,
+ "%s DNSSRV p=%d dn=\"%s\" url=\"%s\"\n",
+ op->o_log_prefix, op->o_protocol,
+ op->o_req_dn.bv_val, urls[0].bv_val );
+
+ Debug( LDAP_DEBUG_TRACE, "DNSSRV: dn=\"%s\" -> url=\"%s\"\n",
+ op->o_req_dn.bv_val, urls[0].bv_val );
+
+ rs->sr_ref = urls;
+ send_ldap_error( op, rs, LDAP_REFERRAL,
+ "DNS SRV generated referrals" );
+ rs->sr_ref = NULL;
+ rc = LDAP_REFERRAL;
+
+done:
+ if( domain != NULL ) ch_free( domain );
+ if( hostlist != NULL ) ch_free( hostlist );
+ if( hosts != NULL ) ldap_charray_free( hosts );
+ ber_bvarray_free( urls );
+ return rc;
+}
diff --git a/servers/slapd/back-dnssrv/search.c b/servers/slapd/back-dnssrv/search.c
new file mode 100644
index 0000000..4403248
--- /dev/null
+++ b/servers/slapd/back-dnssrv/search.c
@@ -0,0 +1,239 @@
+/* search.c - DNS SRV backend search function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2000-2003 Kurt D. Zeilenga.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by Kurt D. Zeilenga for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "slap.h"
+#include "proto-dnssrv.h"
+
+int
+dnssrv_back_search(
+ Operation *op,
+ SlapReply *rs )
+{
+ int i;
+ int rc;
+ char *domain = NULL;
+ char *hostlist = NULL;
+ char **hosts = NULL;
+ char *refdn;
+ struct berval nrefdn = BER_BVNULL;
+ BerVarray urls = NULL;
+ int manageDSAit;
+
+ rs->sr_ref = NULL;
+
+ if ( BER_BVISEMPTY( &op->o_req_ndn ) ) {
+ /* FIXME: need some means to determine whether the database
+ * is a glue instance; if we got here with empty DN, then
+ * we passed this same test in dnssrv_back_referrals() */
+ if ( !SLAP_GLUE_INSTANCE( op->o_bd ) ) {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "DNS SRV operation upon null (empty) DN disallowed";
+
+ } else {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+ goto done;
+ }
+
+ manageDSAit = get_manageDSAit( op );
+ /*
+ * FIXME: we may return a referral if manageDSAit is not set
+ */
+ if ( !manageDSAit ) {
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "manageDSAit must be set" );
+ goto done;
+ }
+
+ if( ldap_dn2domain( op->o_req_dn.bv_val, &domain ) || domain == NULL ) {
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_ref = default_referral;
+ send_ldap_result( op, rs );
+ rs->sr_ref = NULL;
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "DNSSRV: dn=\"%s\" -> domain=\"%s\"\n",
+ op->o_req_dn.bv_len ? op->o_req_dn.bv_val : "", domain );
+
+ if( ( rc = ldap_domain2hostlist( domain, &hostlist ) ) ) {
+ Debug( LDAP_DEBUG_TRACE, "DNSSRV: domain2hostlist returned %d\n",
+ rc );
+ send_ldap_error( op, rs, LDAP_NO_SUCH_OBJECT,
+ "no DNS SRV RR available for DN" );
+ goto done;
+ }
+
+ hosts = ldap_str2charray( hostlist, " " );
+
+ if( hosts == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "DNSSRV: str2charray error\n" );
+ send_ldap_error( op, rs, LDAP_OTHER,
+ "problem processing DNS SRV records for DN" );
+ goto done;
+ }
+
+ for( i=0; hosts[i] != NULL; i++) {
+ struct berval url;
+
+ url.bv_len = STRLENOF( "ldap://" ) + strlen(hosts[i]);
+ url.bv_val = ch_malloc( url.bv_len + 1 );
+
+ strcpy( url.bv_val, "ldap://" );
+ strcpy( &url.bv_val[STRLENOF( "ldap://" )], hosts[i] );
+
+ if( ber_bvarray_add( &urls, &url ) < 0 ) {
+ free( url.bv_val );
+ send_ldap_error( op, rs, LDAP_OTHER,
+ "problem processing DNS SRV records for DN" );
+ goto done;
+ }
+ }
+
+ Debug( LDAP_DEBUG_STATS,
+ "%s DNSSRV p=%d dn=\"%s\" url=\"%s\"\n",
+ op->o_log_prefix, op->o_protocol,
+ op->o_req_dn.bv_len ? op->o_req_dn.bv_val : "", urls[0].bv_val );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "DNSSRV: ManageDSAit scope=%d dn=\"%s\" -> url=\"%s\"\n",
+ op->oq_search.rs_scope,
+ op->o_req_dn.bv_len ? op->o_req_dn.bv_val : "",
+ urls[0].bv_val );
+
+ rc = ldap_domain2dn(domain, &refdn);
+
+ if( rc != LDAP_SUCCESS ) {
+ send_ldap_error( op, rs, LDAP_OTHER,
+ "DNS SRV problem processing manageDSAit control" );
+ goto done;
+
+ } else {
+ struct berval bv;
+ bv.bv_val = refdn;
+ bv.bv_len = strlen( refdn );
+
+ rc = dnNormalize( 0, NULL, NULL, &bv, &nrefdn, op->o_tmpmemctx );
+ if( rc != LDAP_SUCCESS ) {
+ send_ldap_error( op, rs, LDAP_OTHER,
+ "DNS SRV problem processing manageDSAit control" );
+ goto done;
+ }
+ }
+
+ if( !dn_match( &nrefdn, &op->o_req_ndn ) ) {
+ /* requested dn is subordinate */
+
+ Debug( LDAP_DEBUG_TRACE,
+ "DNSSRV: dn=\"%s\" subordinate to refdn=\"%s\"\n",
+ op->o_req_dn.bv_len ? op->o_req_dn.bv_val : "",
+ refdn == NULL ? "" : refdn );
+
+ rs->sr_matched = refdn;
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ send_ldap_result( op, rs );
+ rs->sr_matched = NULL;
+
+ } else if ( op->oq_search.rs_scope == LDAP_SCOPE_ONELEVEL ) {
+ send_ldap_error( op, rs, LDAP_SUCCESS, NULL );
+
+ } else {
+ Entry e = { 0 };
+ AttributeDescription *ad_objectClass
+ = slap_schema.si_ad_objectClass;
+ AttributeDescription *ad_ref = slap_schema.si_ad_ref;
+ e.e_name.bv_val = ch_strdup( op->o_req_dn.bv_val );
+ e.e_name.bv_len = op->o_req_dn.bv_len;
+ e.e_nname.bv_val = ch_strdup( op->o_req_ndn.bv_val );
+ e.e_nname.bv_len = op->o_req_ndn.bv_len;
+
+ e.e_attrs = NULL;
+ e.e_private = NULL;
+
+ attr_merge_one( &e, ad_objectClass, &slap_schema.si_oc_referral->soc_cname, NULL );
+ attr_merge_one( &e, ad_objectClass, &slap_schema.si_oc_extensibleObject->soc_cname, NULL );
+
+ if ( ad_dc ) {
+ char *p;
+ struct berval bv;
+
+ bv.bv_val = domain;
+
+ p = strchr( bv.bv_val, '.' );
+
+ if ( p == bv.bv_val ) {
+ bv.bv_len = 1;
+
+ } else if ( p != NULL ) {
+ bv.bv_len = p - bv.bv_val;
+
+ } else {
+ bv.bv_len = strlen( bv.bv_val );
+ }
+
+ attr_merge_normalize_one( &e, ad_dc, &bv, NULL );
+ }
+
+ if ( ad_associatedDomain ) {
+ struct berval bv;
+
+ ber_str2bv( domain, 0, 0, &bv );
+ attr_merge_normalize_one( &e, ad_associatedDomain, &bv, NULL );
+ }
+
+ attr_merge_normalize_one( &e, ad_ref, urls, NULL );
+
+ rc = test_filter( op, &e, op->oq_search.rs_filter );
+
+ if( rc == LDAP_COMPARE_TRUE ) {
+ rs->sr_entry = &e;
+ rs->sr_attrs = op->oq_search.rs_attrs;
+ rs->sr_flags = REP_ENTRY_MODIFIABLE;
+ send_search_entry( op, rs );
+ rs->sr_entry = NULL;
+ rs->sr_attrs = NULL;
+ rs->sr_flags = 0;
+ }
+
+ entry_clean( &e );
+
+ rs->sr_err = LDAP_SUCCESS;
+ send_ldap_result( op, rs );
+ }
+
+ free( refdn );
+ if ( nrefdn.bv_val ) free( nrefdn.bv_val );
+
+done:
+ if( domain != NULL ) ch_free( domain );
+ if( hostlist != NULL ) ch_free( hostlist );
+ if( hosts != NULL ) ldap_charray_free( hosts );
+ if( urls != NULL ) ber_bvarray_free( urls );
+ return 0;
+}
diff --git a/servers/slapd/back-ldap/Makefile.in b/servers/slapd/back-ldap/Makefile.in
new file mode 100644
index 0000000..4b10d77
--- /dev/null
+++ b/servers/slapd/back-ldap/Makefile.in
@@ -0,0 +1,45 @@
+# Makefile.in for back-ldap
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SRCS = init.c config.c search.c bind.c unbind.c add.c compare.c \
+ delete.c modify.c modrdn.c extended.c chain.c \
+ distproc.c monitor.c pbind.c
+OBJS = init.lo config.lo search.lo bind.lo unbind.lo add.lo compare.lo \
+ delete.lo modify.lo modrdn.lo extended.lo chain.lo \
+ distproc.lo monitor.lo pbind.lo
+
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-ldap"
+BUILD_MOD = @BUILD_LDAP@
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = $(@BUILD_LDAP@_DEFS)
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+
+LIBBASE = back_ldap
+
+XINCPATH = -I.. -I$(srcdir)/..
+XDEFS = $(MODULES_CPPFLAGS)
+
+all-local-lib: ../.backend
+
+../.backend: lib$(LIBBASE).a
+ @touch $@
+
diff --git a/servers/slapd/back-ldap/TODO.proxy b/servers/slapd/back-ldap/TODO.proxy
new file mode 100644
index 0000000..01406c5
--- /dev/null
+++ b/servers/slapd/back-ldap/TODO.proxy
@@ -0,0 +1,101 @@
+back-proxy
+
+A proxy that handles a pool of URI associated to a unique suffix.
+Each request is spread over the different URIs and results are
+masqueraded to appear as coming from a unique server.
+
+Suppose a company has two branches, whose existing DS have URIs
+
+"ldap://ldap.branch1.com/o=Branch 1, c=US"
+"ldap://ldap.branch2.it/o=Branch 2, c=IT"
+
+and it wants to propose to the outer world as a unique URI
+
+"ldap://ldap.company.net/dc=company, dc=net"
+
+It could do some rewriting to map everything that comes in with a base DN
+of "o=Branch 1, dc=company, dc=net" as the URI of the Branch 1, and
+everything that comes in with a base DN of "o=Branch 2, dc=company, dc=net"
+as the URI of Branch 2, and by rewriting all the DNs back to the new, uniform
+base. Everything that comes in with a base DN of "dc=company, dc=net" should
+be handled locally and propagated to the two branch URIs if a subtree
+(or at least onelevel) search is required.
+
+Operations:
+
+- bind
+- unbind
+- search
+- compare
+- add
+- modify
+- modrdn
+- delete
+- abandon
+
+The input of each operation may be related to:
+
+ exact DN exact parent ancestor
+-------------------------------------------------------------
+bind x
+unbind
+search x x x
+compare x
+add x
+modify x
+modrdn x
+delete x
+abandon
+
+The backend must rely on a DN fetching mechanism. Each operation requires
+to determine as early as possible which URI will be able to satisfy it.
+Apart from searches, which by definition are usually allowed to return
+multiple results, and apart from unbind and abandon, which do not return any
+result, all the remaining operations require the related entry to be unique.
+
+A major problem isposed by the uniqueness of the DNs. As far as the suffixes
+are masqueraded by a common suffix, the DNs are no longer guaranteed to be
+unique. This backend relies on the assumption that the uniqueness of the
+DNs is guaranteed.
+
+Two layers of depth in DN fetching are envisaged.
+The first layer is provided by a backend-side cache made of previously
+retrieved entries. The cache relates each RDN (i.e. the DN apart from the
+common suffix) to the pool of URIs that are expected to contain a subset
+of its children.
+
+The second layer is provided by a fetching function that spawns a search for
+each URI in the pool determined by the cache if the correct URI has not been
+directly determined.
+
+Note that, as the remote servers may have been updated by some direct
+operation, this mechanism does not guarantee the uniqueness of the result.
+So write operations will require to skip the cache search and to perform
+the exhaustive search of all the URIs unless some hint mechanism is provided
+to the backend (e.g. a server is read-only).
+
+Again, the lag between the fetching of the required DN and the actual
+read/write may result in a failure; however, this applies to any LDAP
+operation AFAIK.
+
+- bind
+if updates are to be strictly honored, a bind operation is performed against
+each URI; otherwise, it is performed against the URIs resulting from a
+cache-level DN fetch.
+
+- unbind
+nothing to say; all the open handles related to the connection are reset.
+
+- search
+if updates are to be strictly honored, a search operation is performed against
+each URI. Note that this needs be performed also when the backend suffix
+is used as base. In case the base is stricter, the URI pool may be restricted
+by performing a cache DN fetch of the base first.
+
+- compare
+the same applies to the compare DN.
+
+- add
+this operation is delicate. Unless the DN up to the top-level part excluded
+can be uniquely associated to a URI, and unless its uniqueness can be trusted,
+no add operation should be allowed.
diff --git a/servers/slapd/back-ldap/add.c b/servers/slapd/back-ldap/add.c
new file mode 100644
index 0000000..32ceda2
--- /dev/null
+++ b/servers/slapd/back-ldap/add.c
@@ -0,0 +1,139 @@
+/* add.c - ldap backend add function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-ldap.h"
+
+int
+ldap_back_add(
+ Operation *op,
+ SlapReply *rs )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ ldapconn_t *lc = NULL;
+ int i = 0,
+ j = 0;
+ Attribute *a;
+ LDAPMod **attrs = NULL,
+ *attrs2 = NULL;
+ ber_int_t msgid;
+ int isupdate;
+ ldap_back_send_t retrying = LDAP_BACK_RETRYING;
+ LDAPControl **ctrls = NULL;
+
+ rs->sr_err = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_ARGS, "==> ldap_back_add(\"%s\")\n",
+ op->o_req_dn.bv_val );
+
+ if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ lc = NULL;
+ goto cleanup;
+ }
+
+ /* Count number of attributes in entry */
+ for ( i = 1, a = op->oq_add.rs_e->e_attrs; a; i++, a = a->a_next )
+ /* just count attrs */ ;
+
+ /* Create array of LDAPMods for ldap_add() */
+ attrs = (LDAPMod **)ch_malloc( sizeof( LDAPMod * )*i
+ + sizeof( LDAPMod )*( i - 1 ) );
+ attrs2 = ( LDAPMod * )&attrs[ i ];
+
+ isupdate = be_shadow_update( op );
+ for ( i = 0, a = op->oq_add.rs_e->e_attrs; a; a = a->a_next ) {
+ if ( !isupdate && !get_relax( op ) && a->a_desc->ad_type->sat_no_user_mod )
+ {
+ continue;
+ }
+
+ attrs[ i ] = &attrs2[ i ];
+ attrs[ i ]->mod_op = LDAP_MOD_BVALUES;
+ attrs[ i ]->mod_type = a->a_desc->ad_cname.bv_val;
+
+ for ( j = 0; a->a_vals[ j ].bv_val; j++ )
+ /* just count vals */ ;
+ attrs[i]->mod_vals.modv_bvals =
+ ch_malloc( ( j + 1 )*sizeof( struct berval * ) );
+ for ( j = 0; a->a_vals[ j ].bv_val; j++ ) {
+ attrs[ i ]->mod_vals.modv_bvals[ j ] = &a->a_vals[ j ];
+ }
+ attrs[ i ]->mod_vals.modv_bvals[ j ] = NULL;
+ i++;
+ }
+ attrs[ i ] = NULL;
+
+retry:
+ ctrls = op->o_ctrls;
+ rs->sr_err = ldap_back_controls_add( op, rs, lc, &ctrls );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ rs->sr_err = ldap_add_ext( lc->lc_ld, op->o_req_dn.bv_val, attrs,
+ ctrls, NULL, &msgid );
+ rs->sr_err = ldap_back_op_result( lc, op, rs, msgid,
+ li->li_timeout[ SLAP_OP_ADD ],
+ ( LDAP_BACK_SENDRESULT | retrying ) );
+ if ( rs->sr_err == LDAP_UNAVAILABLE && retrying ) {
+ retrying &= ~LDAP_BACK_RETRYING;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ /* if the identity changed, there might be need to re-authz */
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+ goto retry;
+ }
+ }
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_ADD ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+cleanup:
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+
+ if ( attrs ) {
+ for ( --i; i >= 0; --i ) {
+ ch_free( attrs[ i ]->mod_vals.modv_bvals );
+ }
+ ch_free( attrs );
+ }
+
+ if ( lc ) {
+ ldap_back_release_conn( li, lc );
+ }
+
+ Debug( LDAP_DEBUG_ARGS, "<== ldap_back_add(\"%s\"): %d\n",
+ op->o_req_dn.bv_val, rs->sr_err );
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/back-ldap/back-ldap.h b/servers/slapd/back-ldap/back-ldap.h
new file mode 100644
index 0000000..96bc6f3
--- /dev/null
+++ b/servers/slapd/back-ldap/back-ldap.h
@@ -0,0 +1,479 @@
+/* back-ldap.h - ldap backend header file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#ifndef SLAPD_LDAP_H
+#define SLAPD_LDAP_H
+
+#include "../back-monitor/back-monitor.h"
+
+LDAP_BEGIN_DECL
+
+struct ldapinfo_t;
+
+/* stuff required for monitoring */
+typedef struct ldap_monitor_info_t {
+ monitor_subsys_t lmi_mss[2];
+
+ struct berval lmi_ndn;
+ struct berval lmi_conn_rdn;
+ struct berval lmi_ops_rdn;
+} ldap_monitor_info_t;
+
+enum {
+ /* even numbers are connection types */
+ LDAP_BACK_PCONN_FIRST = 0,
+ LDAP_BACK_PCONN_ROOTDN = LDAP_BACK_PCONN_FIRST,
+ LDAP_BACK_PCONN_ANON = 2,
+ LDAP_BACK_PCONN_BIND = 4,
+
+ /* add the TLS bit */
+ LDAP_BACK_PCONN_TLS = 0x1U,
+
+ LDAP_BACK_PCONN_ROOTDN_TLS = (LDAP_BACK_PCONN_ROOTDN|LDAP_BACK_PCONN_TLS),
+ LDAP_BACK_PCONN_ANON_TLS = (LDAP_BACK_PCONN_ANON|LDAP_BACK_PCONN_TLS),
+ LDAP_BACK_PCONN_BIND_TLS = (LDAP_BACK_PCONN_BIND|LDAP_BACK_PCONN_TLS),
+
+ LDAP_BACK_PCONN_LAST
+};
+
+typedef struct ldapconn_base_t {
+ Connection *lcb_conn;
+#define LDAP_BACK_CONN2PRIV(lc) ((unsigned long)(lc)->lc_conn)
+#define LDAP_BACK_PCONN_ISPRIV(lc) (((void *)(lc)->lc_conn) >= ((void *)LDAP_BACK_PCONN_FIRST) \
+ && ((void *)(lc)->lc_conn) < ((void *)LDAP_BACK_PCONN_LAST))
+#define LDAP_BACK_PCONN_ISROOTDN(lc) (LDAP_BACK_PCONN_ISPRIV((lc)) \
+ && (LDAP_BACK_CONN2PRIV((lc)) < LDAP_BACK_PCONN_ANON))
+#define LDAP_BACK_PCONN_ISANON(lc) (LDAP_BACK_PCONN_ISPRIV((lc)) \
+ && (LDAP_BACK_CONN2PRIV((lc)) < LDAP_BACK_PCONN_BIND) \
+ && (LDAP_BACK_CONN2PRIV((lc)) >= LDAP_BACK_PCONN_ANON))
+#define LDAP_BACK_PCONN_ISBIND(lc) (LDAP_BACK_PCONN_ISPRIV((lc)) \
+ && (LDAP_BACK_CONN2PRIV((lc)) >= LDAP_BACK_PCONN_BIND))
+#define LDAP_BACK_PCONN_ISTLS(lc) (LDAP_BACK_PCONN_ISPRIV((lc)) \
+ && (LDAP_BACK_CONN2PRIV((lc)) & LDAP_BACK_PCONN_TLS))
+#ifdef HAVE_TLS
+#define LDAP_BACK_PCONN_ROOTDN_SET(lc, op) \
+ ((lc)->lc_conn = (void *)((op)->o_conn->c_is_tls ? (void *) LDAP_BACK_PCONN_ROOTDN_TLS : (void *) LDAP_BACK_PCONN_ROOTDN))
+#define LDAP_BACK_PCONN_ANON_SET(lc, op) \
+ ((lc)->lc_conn = (void *)((op)->o_conn->c_is_tls ? (void *) LDAP_BACK_PCONN_ANON_TLS : (void *) LDAP_BACK_PCONN_ANON))
+#define LDAP_BACK_PCONN_BIND_SET(lc, op) \
+ ((lc)->lc_conn = (void *)((op)->o_conn->c_is_tls ? (void *) LDAP_BACK_PCONN_BIND_TLS : (void *) LDAP_BACK_PCONN_BIND))
+#else /* ! HAVE_TLS */
+#define LDAP_BACK_PCONN_ROOTDN_SET(lc, op) \
+ ((lc)->lc_conn = (void *)LDAP_BACK_PCONN_ROOTDN)
+#define LDAP_BACK_PCONN_ANON_SET(lc, op) \
+ ((lc)->lc_conn = (void *)LDAP_BACK_PCONN_ANON)
+#define LDAP_BACK_PCONN_BIND_SET(lc, op) \
+ ((lc)->lc_conn = (void *)LDAP_BACK_PCONN_BIND)
+#endif /* ! HAVE_TLS */
+#define LDAP_BACK_PCONN_SET(lc, op) \
+ (BER_BVISEMPTY(&(op)->o_ndn) ? \
+ LDAP_BACK_PCONN_ANON_SET((lc), (op)) : LDAP_BACK_PCONN_ROOTDN_SET((lc), (op)))
+
+ struct ldapinfo_t *lcb_ldapinfo;
+ struct berval lcb_local_ndn;
+ unsigned lcb_refcnt;
+ time_t lcb_create_time;
+ time_t lcb_time;
+} ldapconn_base_t;
+
+typedef struct ldapconn_t {
+ ldapconn_base_t lc_base;
+#define lc_conn lc_base.lcb_conn
+#define lc_ldapinfo lc_base.lcb_ldapinfo
+#define lc_local_ndn lc_base.lcb_local_ndn
+#define lc_refcnt lc_base.lcb_refcnt
+#define lc_create_time lc_base.lcb_create_time
+#define lc_time lc_base.lcb_time
+
+ LDAP_TAILQ_ENTRY(ldapconn_t) lc_q;
+
+ unsigned lc_lcflags;
+#define LDAP_BACK_CONN_ISSET_F(fp,f) (*(fp) & (f))
+#define LDAP_BACK_CONN_SET_F(fp,f) (*(fp) |= (f))
+#define LDAP_BACK_CONN_CLEAR_F(fp,f) (*(fp) &= ~(f))
+#define LDAP_BACK_CONN_CPY_F(fp,f,mfp) \
+ do { \
+ if ( ((f) & *(mfp)) == (f) ) { \
+ *(fp) |= (f); \
+ } else { \
+ *(fp) &= ~(f); \
+ } \
+ } while ( 0 )
+
+#define LDAP_BACK_CONN_ISSET(lc,f) LDAP_BACK_CONN_ISSET_F(&(lc)->lc_lcflags, (f))
+#define LDAP_BACK_CONN_SET(lc,f) LDAP_BACK_CONN_SET_F(&(lc)->lc_lcflags, (f))
+#define LDAP_BACK_CONN_CLEAR(lc,f) LDAP_BACK_CONN_CLEAR_F(&(lc)->lc_lcflags, (f))
+#define LDAP_BACK_CONN_CPY(lc,f,mlc) LDAP_BACK_CONN_CPY_F(&(lc)->lc_lcflags, (f), &(mlc)->lc_lcflags)
+
+/* 0xFFF00000U are reserved for back-meta */
+
+#define LDAP_BACK_FCONN_ISBOUND (0x00000001U)
+#define LDAP_BACK_FCONN_ISANON (0x00000002U)
+#define LDAP_BACK_FCONN_ISBMASK (LDAP_BACK_FCONN_ISBOUND|LDAP_BACK_FCONN_ISANON)
+#define LDAP_BACK_FCONN_ISPRIV (0x00000004U)
+#define LDAP_BACK_FCONN_ISTLS (0x00000008U)
+#define LDAP_BACK_FCONN_BINDING (0x00000010U)
+#define LDAP_BACK_FCONN_TAINTED (0x00000020U)
+#define LDAP_BACK_FCONN_ABANDON (0x00000040U)
+#define LDAP_BACK_FCONN_ISIDASR (0x00000080U)
+#define LDAP_BACK_FCONN_CACHED (0x00000100U)
+
+#define LDAP_BACK_CONN_ISBOUND(lc) LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_ISBOUND)
+#define LDAP_BACK_CONN_ISBOUND_SET(lc) LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_ISBOUND)
+#define LDAP_BACK_CONN_ISBOUND_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), LDAP_BACK_FCONN_ISBMASK)
+#define LDAP_BACK_CONN_ISBOUND_CPY(lc, mlc) LDAP_BACK_CONN_CPY((lc), LDAP_BACK_FCONN_ISBOUND, (mlc))
+#define LDAP_BACK_CONN_ISANON(lc) LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_ISANON)
+#define LDAP_BACK_CONN_ISANON_SET(lc) LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_ISANON)
+#define LDAP_BACK_CONN_ISANON_CLEAR(lc) LDAP_BACK_CONN_ISBOUND_CLEAR((lc))
+#define LDAP_BACK_CONN_ISANON_CPY(lc, mlc) LDAP_BACK_CONN_CPY((lc), LDAP_BACK_FCONN_ISANON, (mlc))
+#define LDAP_BACK_CONN_ISPRIV(lc) LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_ISPRIV)
+#define LDAP_BACK_CONN_ISPRIV_SET(lc) LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_ISPRIV)
+#define LDAP_BACK_CONN_ISPRIV_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), LDAP_BACK_FCONN_ISPRIV)
+#define LDAP_BACK_CONN_ISPRIV_CPY(lc, mlc) LDAP_BACK_CONN_CPY((lc), LDAP_BACK_FCONN_ISPRIV, (mlc))
+#define LDAP_BACK_CONN_ISTLS(lc) LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_ISTLS)
+#define LDAP_BACK_CONN_ISTLS_SET(lc) LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_ISTLS)
+#define LDAP_BACK_CONN_ISTLS_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), LDAP_BACK_FCONN_ISTLS)
+#define LDAP_BACK_CONN_ISTLS_CPY(lc, mlc) LDAP_BACK_CONN_CPY((lc), LDAP_BACK_FCONN_ISTLS, (mlc))
+#define LDAP_BACK_CONN_BINDING(lc) LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_BINDING)
+#define LDAP_BACK_CONN_BINDING_SET(lc) LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_BINDING)
+#define LDAP_BACK_CONN_BINDING_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), LDAP_BACK_FCONN_BINDING)
+#define LDAP_BACK_CONN_TAINTED(lc) LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_TAINTED)
+#define LDAP_BACK_CONN_TAINTED_SET(lc) LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_TAINTED)
+#define LDAP_BACK_CONN_TAINTED_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), LDAP_BACK_FCONN_TAINTED)
+#define LDAP_BACK_CONN_ABANDON(lc) LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_ABANDON)
+#define LDAP_BACK_CONN_ABANDON_SET(lc) LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_ABANDON)
+#define LDAP_BACK_CONN_ABANDON_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), LDAP_BACK_FCONN_ABANDON)
+#define LDAP_BACK_CONN_ISIDASSERT(lc) LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_ISIDASR)
+#define LDAP_BACK_CONN_ISIDASSERT_SET(lc) LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_ISIDASR)
+#define LDAP_BACK_CONN_ISIDASSERT_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), LDAP_BACK_FCONN_ISIDASR)
+#define LDAP_BACK_CONN_ISIDASSERT_CPY(lc, mlc) LDAP_BACK_CONN_CPY((lc), LDAP_BACK_FCONN_ISIDASR, (mlc))
+#define LDAP_BACK_CONN_CACHED(lc) LDAP_BACK_CONN_ISSET((lc), LDAP_BACK_FCONN_CACHED)
+#define LDAP_BACK_CONN_CACHED_SET(lc) LDAP_BACK_CONN_SET((lc), LDAP_BACK_FCONN_CACHED)
+#define LDAP_BACK_CONN_CACHED_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), LDAP_BACK_FCONN_CACHED)
+
+ LDAP *lc_ld;
+ unsigned long lc_connid;
+ struct berval lc_cred;
+ struct berval lc_bound_ndn;
+ unsigned lc_flags;
+} ldapconn_t;
+
+typedef struct ldap_avl_info_t {
+ ldap_pvt_thread_mutex_t lai_mutex;
+ TAvlnode *lai_tree;
+} ldap_avl_info_t;
+
+typedef struct slap_retry_info_t {
+ time_t *ri_interval;
+ int *ri_num;
+ int ri_idx;
+ int ri_count;
+ time_t ri_last;
+
+#define SLAP_RETRYNUM_FOREVER (-1) /* retry forever */
+#define SLAP_RETRYNUM_TAIL (-2) /* end of retrynum array */
+#define SLAP_RETRYNUM_VALID(n) ((n) >= SLAP_RETRYNUM_FOREVER) /* valid retrynum */
+#define SLAP_RETRYNUM_FINITE(n) ((n) > SLAP_RETRYNUM_FOREVER) /* not forever */
+} slap_retry_info_t;
+
+/*
+ * identity assertion modes
+ */
+typedef enum {
+ LDAP_BACK_IDASSERT_LEGACY = 1,
+ LDAP_BACK_IDASSERT_NOASSERT,
+ LDAP_BACK_IDASSERT_ANONYMOUS,
+ LDAP_BACK_IDASSERT_SELF,
+ LDAP_BACK_IDASSERT_OTHERDN,
+ LDAP_BACK_IDASSERT_OTHERID
+} slap_idassert_mode_t;
+
+/* ID assert stuff */
+typedef struct slap_idassert_t {
+ slap_idassert_mode_t si_mode;
+#define li_idassert_mode li_idassert.si_mode
+
+ slap_bindconf si_bc;
+#define li_idassert_authcID li_idassert.si_bc.sb_authcId
+#define li_idassert_authcDN li_idassert.si_bc.sb_binddn
+#define li_idassert_passwd li_idassert.si_bc.sb_cred
+#define li_idassert_authzID li_idassert.si_bc.sb_authzId
+#define li_idassert_authmethod li_idassert.si_bc.sb_method
+#define li_idassert_sasl_mech li_idassert.si_bc.sb_saslmech
+#define li_idassert_sasl_realm li_idassert.si_bc.sb_realm
+#define li_idassert_secprops li_idassert.si_bc.sb_secprops
+#define li_idassert_tls li_idassert.si_bc.sb_tls
+
+ unsigned si_flags;
+#define LDAP_BACK_AUTH_NONE (0x00U)
+#define LDAP_BACK_AUTH_NATIVE_AUTHZ (0x01U)
+#define LDAP_BACK_AUTH_OVERRIDE (0x02U)
+#define LDAP_BACK_AUTH_PRESCRIPTIVE (0x04U)
+#define LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ (0x08U)
+#define LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND (0x10U)
+#define LDAP_BACK_AUTH_AUTHZ_ALL (0x20U)
+#define LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL (0x40U)
+#define LDAP_BACK_AUTH_DN_AUTHZID (0x100U)
+#define LDAP_BACK_AUTH_DN_WHOAMI (0x200U)
+#define LDAP_BACK_AUTH_DN_MASK (LDAP_BACK_AUTH_DN_AUTHZID|LDAP_BACK_AUTH_DN_WHOAMI)
+#define li_idassert_flags li_idassert.si_flags
+
+ BerVarray si_authz;
+#define li_idassert_authz li_idassert.si_authz
+
+ BerVarray si_passthru;
+#define li_idassert_passthru li_idassert.si_passthru
+} slap_idassert_t;
+
+/*
+ * Hook to allow mucking with ldapinfo_t when quarantine is over
+ */
+typedef int (*ldap_back_quarantine_f)( struct ldapinfo_t *, void * );
+
+typedef struct ldapinfo_t {
+ /* li_uri: the string that goes into ldap_initialize()
+ * TODO: use li_acl.sb_uri instead */
+ char *li_uri;
+ /* li_bvuri: an array of each single URI that is equivalent;
+ * to be checked for the presence of a certain item */
+ BerVarray li_bvuri;
+ ldap_pvt_thread_mutex_t li_uri_mutex;
+ /* hack because when TLS is used we need to lock and let
+ * the li_urllist_f function to know it's locked */
+ int li_uri_mutex_do_not_lock;
+
+ LDAP_REBIND_PROC *li_rebind_f;
+ LDAP_URLLIST_PROC *li_urllist_f;
+ void *li_urllist_p;
+
+ /* we only care about the TLS options here */
+ slap_bindconf li_tls;
+
+ slap_bindconf li_acl;
+#define li_acl_authcID li_acl.sb_authcId
+#define li_acl_authcDN li_acl.sb_binddn
+#define li_acl_passwd li_acl.sb_cred
+#define li_acl_authzID li_acl.sb_authzId
+#define li_acl_authmethod li_acl.sb_method
+#define li_acl_sasl_mech li_acl.sb_saslmech
+#define li_acl_sasl_realm li_acl.sb_realm
+#define li_acl_secprops li_acl.sb_secprops
+
+ /* ID assert stuff */
+ slap_idassert_t li_idassert;
+ /* end of ID assert stuff */
+
+ int li_nretries;
+#define LDAP_BACK_RETRY_UNDEFINED (-2)
+#define LDAP_BACK_RETRY_FOREVER (-1)
+#define LDAP_BACK_RETRY_NEVER (0)
+#define LDAP_BACK_RETRY_DEFAULT (3)
+
+ unsigned li_flags;
+
+/* 0xFF000000U are reserved for back-meta */
+
+#define LDAP_BACK_F_NONE (0x00000000U)
+#define LDAP_BACK_F_SAVECRED (0x00000001U)
+#define LDAP_BACK_F_USE_TLS (0x00000002U)
+#define LDAP_BACK_F_PROPAGATE_TLS (0x00000004U)
+#define LDAP_BACK_F_TLS_CRITICAL (0x00000008U)
+#define LDAP_BACK_F_TLS_LDAPS (0x00000010U)
+
+#define LDAP_BACK_F_TLS_USE_MASK (LDAP_BACK_F_USE_TLS|LDAP_BACK_F_TLS_CRITICAL)
+#define LDAP_BACK_F_TLS_PROPAGATE_MASK (LDAP_BACK_F_PROPAGATE_TLS|LDAP_BACK_F_TLS_CRITICAL)
+#define LDAP_BACK_F_TLS_MASK (LDAP_BACK_F_TLS_USE_MASK|LDAP_BACK_F_TLS_PROPAGATE_MASK|LDAP_BACK_F_TLS_LDAPS)
+#define LDAP_BACK_F_CHASE_REFERRALS (0x00000020U)
+#define LDAP_BACK_F_PROXY_WHOAMI (0x00000040U)
+
+#define LDAP_BACK_F_T_F (0x00000080U)
+#define LDAP_BACK_F_T_F_DISCOVER (0x00000100U)
+#define LDAP_BACK_F_T_F_MASK (LDAP_BACK_F_T_F)
+#define LDAP_BACK_F_T_F_MASK2 (LDAP_BACK_F_T_F_MASK|LDAP_BACK_F_T_F_DISCOVER)
+
+#define LDAP_BACK_F_MONITOR (0x00000200U)
+#define LDAP_BACK_F_SINGLECONN (0x00000400U)
+#define LDAP_BACK_F_USE_TEMPORARIES (0x00000800U)
+
+#define LDAP_BACK_F_ISOPEN (0x00001000U)
+
+#define LDAP_BACK_F_CANCEL_ABANDON (0x00000000U)
+#define LDAP_BACK_F_CANCEL_IGNORE (0x00002000U)
+#define LDAP_BACK_F_CANCEL_EXOP (0x00004000U)
+#define LDAP_BACK_F_CANCEL_EXOP_DISCOVER (0x00008000U)
+#define LDAP_BACK_F_CANCEL_MASK (LDAP_BACK_F_CANCEL_IGNORE|LDAP_BACK_F_CANCEL_EXOP)
+#define LDAP_BACK_F_CANCEL_MASK2 (LDAP_BACK_F_CANCEL_MASK|LDAP_BACK_F_CANCEL_EXOP_DISCOVER)
+
+#define LDAP_BACK_F_QUARANTINE (0x00010000U)
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+#define LDAP_BACK_F_ST_REQUEST (0x00020000U)
+#define LDAP_BACK_F_ST_RESPONSE (0x00040000U)
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+#define LDAP_BACK_F_NOREFS (0x00080000U)
+#define LDAP_BACK_F_NOUNDEFFILTER (0x00100000U)
+#define LDAP_BACK_F_OMIT_UNKNOWN_SCHEMA (0x00200000U)
+
+#define LDAP_BACK_F_ONERR_STOP (0x00400000U)
+
+#define LDAP_BACK_ISSET_F(ff,f) ( ( (ff) & (f) ) == (f) )
+#define LDAP_BACK_ISMASK_F(ff,m,f) ( ( (ff) & (m) ) == (f) )
+
+#define LDAP_BACK_ISSET(li,f) LDAP_BACK_ISSET_F( (li)->li_flags, (f) )
+#define LDAP_BACK_ISMASK(li,m,f) LDAP_BACK_ISMASK_F( (li)->li_flags, (m), (f) )
+
+#define LDAP_BACK_SAVECRED(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_SAVECRED )
+#define LDAP_BACK_USE_TLS(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_USE_TLS )
+#define LDAP_BACK_PROPAGATE_TLS(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_PROPAGATE_TLS )
+#define LDAP_BACK_TLS_CRITICAL(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_TLS_CRITICAL )
+#define LDAP_BACK_CHASE_REFERRALS(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_CHASE_REFERRALS )
+#define LDAP_BACK_PROXY_WHOAMI(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_PROXY_WHOAMI )
+
+#define LDAP_BACK_USE_TLS_F(ff) LDAP_BACK_ISSET_F( (ff), LDAP_BACK_F_USE_TLS )
+#define LDAP_BACK_PROPAGATE_TLS_F(ff) LDAP_BACK_ISSET_F( (ff), LDAP_BACK_F_PROPAGATE_TLS )
+#define LDAP_BACK_TLS_CRITICAL_F(ff) LDAP_BACK_ISSET_F( (ff), LDAP_BACK_F_TLS_CRITICAL )
+
+#define LDAP_BACK_T_F(li) LDAP_BACK_ISMASK( (li), LDAP_BACK_F_T_F_MASK, LDAP_BACK_F_T_F )
+#define LDAP_BACK_T_F_DISCOVER(li) LDAP_BACK_ISMASK( (li), LDAP_BACK_F_T_F_MASK2, LDAP_BACK_F_T_F_DISCOVER )
+
+#define LDAP_BACK_MONITOR(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_MONITOR )
+#define LDAP_BACK_SINGLECONN(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_SINGLECONN )
+#define LDAP_BACK_USE_TEMPORARIES(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_USE_TEMPORARIES)
+
+#define LDAP_BACK_ISOPEN(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_ISOPEN )
+
+#define LDAP_BACK_ABANDON(li) LDAP_BACK_ISMASK( (li), LDAP_BACK_F_CANCEL_MASK, LDAP_BACK_F_CANCEL_ABANDON )
+#define LDAP_BACK_IGNORE(li) LDAP_BACK_ISMASK( (li), LDAP_BACK_F_CANCEL_MASK, LDAP_BACK_F_CANCEL_IGNORE )
+#define LDAP_BACK_CANCEL(li) LDAP_BACK_ISMASK( (li), LDAP_BACK_F_CANCEL_MASK, LDAP_BACK_F_CANCEL_EXOP )
+#define LDAP_BACK_CANCEL_DISCOVER(li) LDAP_BACK_ISMASK( (li), LDAP_BACK_F_CANCEL_MASK2, LDAP_BACK_F_CANCEL_EXOP_DISCOVER )
+
+#define LDAP_BACK_QUARANTINE(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_QUARANTINE )
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+#define LDAP_BACK_ST_REQUEST(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_ST_REQUEST)
+#define LDAP_BACK_ST_RESPONSE(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_ST_RESPONSE)
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+#define LDAP_BACK_NOREFS(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_NOREFS)
+#define LDAP_BACK_NOUNDEFFILTER(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_NOUNDEFFILTER)
+#define LDAP_BACK_OMIT_UNKNOWN_SCHEMA(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_OMIT_UNKNOWN_SCHEMA)
+#define LDAP_BACK_ONERR_STOP(li) LDAP_BACK_ISSET( (li), LDAP_BACK_F_ONERR_STOP)
+
+ int li_version;
+
+ unsigned long li_conn_nextid;
+
+ /* cached connections;
+ * special conns are in tailq rather than in tree */
+ ldap_avl_info_t li_conninfo;
+ struct {
+ int lic_num;
+ LDAP_TAILQ_HEAD(lc_conn_priv_q, ldapconn_t) lic_priv;
+ } li_conn_priv[ LDAP_BACK_PCONN_LAST ];
+ int li_conn_priv_max;
+#define LDAP_BACK_CONN_PRIV_MIN (1)
+#define LDAP_BACK_CONN_PRIV_MAX (256)
+ /* must be between LDAP_BACK_CONN_PRIV_MIN
+ * and LDAP_BACK_CONN_PRIV_MAX ! */
+#define LDAP_BACK_CONN_PRIV_DEFAULT (16)
+
+ ldap_monitor_info_t li_monitor_info;
+
+ sig_atomic_t li_isquarantined;
+#define LDAP_BACK_FQ_NO (0)
+#define LDAP_BACK_FQ_YES (1)
+#define LDAP_BACK_FQ_RETRYING (2)
+
+ slap_retry_info_t li_quarantine;
+ ldap_pvt_thread_mutex_t li_quarantine_mutex;
+ ldap_back_quarantine_f li_quarantine_f;
+ void *li_quarantine_p;
+
+ time_t li_network_timeout;
+ time_t li_conn_ttl;
+ time_t li_idle_timeout;
+ time_t li_timeout[ SLAP_OP_LAST ];
+
+ ldap_pvt_thread_mutex_t li_counter_mutex;
+ ldap_pvt_mp_t li_ops_completed[SLAP_OP_LAST];
+ struct re_s* li_conn_expire_task;
+} ldapinfo_t;
+
+#define LDAP_ERR_OK(err) ((err) == LDAP_SUCCESS || (err) == LDAP_COMPARE_FALSE || (err) == LDAP_COMPARE_TRUE)
+
+typedef enum ldap_back_send_t {
+ LDAP_BACK_DONTSEND = 0x00,
+ LDAP_BACK_SENDOK = 0x01,
+ LDAP_BACK_SENDERR = 0x02,
+ LDAP_BACK_SENDRESULT = (LDAP_BACK_SENDOK|LDAP_BACK_SENDERR),
+ LDAP_BACK_BINDING = 0x04,
+
+ LDAP_BACK_BIND_DONTSEND = (LDAP_BACK_BINDING),
+ LDAP_BACK_BIND_SOK = (LDAP_BACK_BINDING|LDAP_BACK_SENDOK),
+ LDAP_BACK_BIND_SERR = (LDAP_BACK_BINDING|LDAP_BACK_SENDERR),
+ LDAP_BACK_BIND_SRES = (LDAP_BACK_BINDING|LDAP_BACK_SENDRESULT),
+
+ LDAP_BACK_RETRYING = 0x08,
+ LDAP_BACK_RETRY_DONTSEND = (LDAP_BACK_RETRYING),
+ LDAP_BACK_RETRY_SOK = (LDAP_BACK_RETRYING|LDAP_BACK_SENDOK),
+ LDAP_BACK_RETRY_SERR = (LDAP_BACK_RETRYING|LDAP_BACK_SENDERR),
+ LDAP_BACK_RETRY_SRES = (LDAP_BACK_RETRYING|LDAP_BACK_SENDRESULT),
+
+ LDAP_BACK_GETCONN = 0x10
+} ldap_back_send_t;
+
+/* define to use asynchronous StartTLS */
+#define SLAP_STARTTLS_ASYNCHRONOUS
+
+/* timeout to use when calling ldap_result() */
+#define LDAP_BACK_RESULT_TIMEOUT (0)
+#define LDAP_BACK_RESULT_UTIMEOUT (100000)
+#define LDAP_BACK_TV_SET(tv) \
+ do { \
+ (tv)->tv_sec = LDAP_BACK_RESULT_TIMEOUT; \
+ (tv)->tv_usec = LDAP_BACK_RESULT_UTIMEOUT; \
+ } while ( 0 )
+
+#ifndef LDAP_BACK_PRINT_CONNTREE
+#define LDAP_BACK_PRINT_CONNTREE 0
+#endif /* !LDAP_BACK_PRINT_CONNTREE */
+
+typedef struct ldap_extra_t {
+ int (*proxy_authz_ctrl)( Operation *op, SlapReply *rs, struct berval *bound_ndn,
+ int version, slap_idassert_t *si, LDAPControl *ctrl );
+ int (*controls_free)( Operation *op, SlapReply *rs, LDAPControl ***pctrls );
+ int (*idassert_authzfrom_parse)( struct config_args_s *ca, slap_idassert_t *si );
+ int (*idassert_passthru_parse_cf)( const char *fname, int lineno, const char *arg, slap_idassert_t *si );
+ int (*idassert_parse)( struct config_args_s *ca, slap_idassert_t *si );
+ void (*retry_info_destroy)( slap_retry_info_t *ri );
+ int (*retry_info_parse)( char *in, slap_retry_info_t *ri, char *buf, ber_len_t buflen );
+ int (*retry_info_unparse)( slap_retry_info_t *ri, struct berval *bvout );
+ int (*connid2str)( const ldapconn_base_t *lc, char *buf, ber_len_t buflen );
+} ldap_extra_t;
+
+LDAP_END_DECL
+
+#include "proto-ldap.h"
+
+#endif /* SLAPD_LDAP_H */
diff --git a/servers/slapd/back-ldap/bind.c b/servers/slapd/back-ldap/bind.c
new file mode 100644
index 0000000..2da66c4
--- /dev/null
+++ b/servers/slapd/back-ldap/bind.c
@@ -0,0 +1,3204 @@
+/* bind.c - ldap backend bind function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#define AVL_INTERNAL
+#include "slap.h"
+#include "back-ldap.h"
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "ldap_rq.h"
+
+#define LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ "2.16.840.1.113730.3.4.12"
+
+#ifdef LDAP_DEVEL
+#define SLAP_AUTH_DN 1
+#endif
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+
+static const struct {
+ slap_mask_t f;
+ char c;
+} flagsmap[] = {
+ { LDAP_BACK_FCONN_ISBOUND, 'B' },
+ { LDAP_BACK_FCONN_ISANON, 'A' },
+ { LDAP_BACK_FCONN_ISPRIV, 'P' },
+ { LDAP_BACK_FCONN_ISTLS, 'T' },
+ { LDAP_BACK_FCONN_BINDING, 'X' },
+ { LDAP_BACK_FCONN_TAINTED, 'E' },
+ { LDAP_BACK_FCONN_ABANDON, 'N' },
+ { LDAP_BACK_FCONN_ISIDASR, 'S' },
+ { LDAP_BACK_FCONN_CACHED, 'C' },
+ { 0, '\0' }
+};
+
+static void
+ldap_back_conn_print( ldapconn_t *lc )
+{
+ char buf[ SLAP_TEXT_BUFLEN ];
+ char fbuf[ sizeof("BAPTIENSC") ];
+ int i;
+
+ ldap_back_conn2str( &lc->lc_base, buf, sizeof( buf ) );
+ for ( i = 0; flagsmap[ i ].c != '\0'; i++ ) {
+ if ( lc->lc_lcflags & flagsmap[i].f ) {
+ fbuf[i] = flagsmap[i].c;
+
+ } else {
+ fbuf[i] = '.';
+ }
+ }
+ fbuf[i] = '\0';
+
+ fprintf( stderr, "lc=%p %s flags=0x%08x (%s)\n",
+ (void *)lc, buf, lc->lc_lcflags, fbuf );
+}
+
+
+static char* priv2str[] = {
+ "privileged",
+ "privileged/TLS",
+ "anonymous",
+ "anonymous/TLS",
+ "bind",
+ "bind/TLS",
+ NULL
+};
+
+void
+ldap_back_print_conntree( ldapinfo_t *li, char *msg )
+{
+ int c;
+
+ fprintf( stderr, "========> %s\n", msg );
+
+ for ( c = LDAP_BACK_PCONN_FIRST; c < LDAP_BACK_PCONN_LAST; c++ ) {
+ int i = 0;
+ ldapconn_t *lc;
+
+ fprintf( stderr, " %s[%d]\n", priv2str[ c ], li->li_conn_priv[ c ].lic_num );
+
+ LDAP_TAILQ_FOREACH( lc, &li->li_conn_priv[ c ].lic_priv, lc_q )
+ {
+ fprintf( stderr, " [%d] ", i );
+ ldap_back_conn_print( lc );
+ i++;
+ }
+ }
+
+ if ( li->li_conninfo.lai_tree == 0 ) {
+ fprintf( stderr, "\t(empty)\n" );
+
+ } else {
+ TAvlnode *edge = ldap_tavl_end( li->li_conninfo.lai_tree, TAVL_DIR_LEFT );
+ while ( edge ) {
+ ldap_back_conn_print( (ldapconn_t *)edge->avl_data );
+ edge = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
+ }
+ }
+
+ fprintf( stderr, "<======== %s\n", msg );
+}
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+static int
+ldap_back_freeconn( ldapinfo_t *li, ldapconn_t *lc, int dolock );
+
+static ldapconn_t *
+ldap_back_getconn( Operation *op, SlapReply *rs, ldap_back_send_t sendok,
+ struct berval *binddn, struct berval *bindcred );
+
+static int
+ldap_back_is_proxy_authz( Operation *op, SlapReply *rs, ldap_back_send_t sendok,
+ struct berval *binddn, struct berval *bindcred );
+
+static int
+ldap_back_proxy_authz_bind( ldapconn_t *lc, Operation *op, SlapReply *rs,
+ ldap_back_send_t sendok, struct berval *binddn, struct berval *bindcred );
+
+static int
+ldap_back_prepare_conn( ldapconn_t *lc, Operation *op, SlapReply *rs,
+ ldap_back_send_t sendok );
+
+static int
+ldap_back_conndnlc_cmp( const void *c1, const void *c2 );
+
+static void
+ldap_back_conn_prune( ldapinfo_t *li );
+
+static void
+ldap_back_schedule_conn_expiry( ldapinfo_t *li, ldapconn_t *lc );
+
+ldapconn_t *
+ldap_back_conn_delete( ldapinfo_t *li, ldapconn_t *lc )
+{
+ if ( LDAP_BACK_PCONN_ISPRIV( lc ) ) {
+ if ( LDAP_BACK_CONN_CACHED( lc ) ) {
+ assert( lc->lc_q.tqe_prev != NULL );
+ assert( li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num > 0 );
+ li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num--;
+ LDAP_TAILQ_REMOVE( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv, lc, lc_q );
+ LDAP_TAILQ_ENTRY_INIT( lc, lc_q );
+ LDAP_BACK_CONN_CACHED_CLEAR( lc );
+
+ } else {
+ assert( LDAP_BACK_CONN_TAINTED( lc ) );
+ assert( lc->lc_q.tqe_prev == NULL );
+ }
+
+ } else {
+ ldapconn_t *tmplc = NULL;
+
+ if ( LDAP_BACK_CONN_CACHED( lc ) ) {
+ assert( !LDAP_BACK_CONN_TAINTED( lc ) );
+ tmplc = ldap_tavl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc,
+ ldap_back_conndnlc_cmp );
+ assert( tmplc == lc );
+ LDAP_BACK_CONN_CACHED_CLEAR( lc );
+ }
+
+ assert( LDAP_BACK_CONN_TAINTED( lc ) || tmplc == lc );
+ }
+
+ return lc;
+}
+
+int
+ldap_back_bind( Operation *op, SlapReply *rs )
+{
+ ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private;
+ ldapconn_t *lc;
+
+ LDAPControl **ctrls = NULL;
+ struct berval save_o_dn;
+ int save_o_do_not_cache,
+ rc = 0;
+ ber_int_t msgid;
+ ldap_back_send_t retrying = LDAP_BACK_RETRYING;
+
+ /* allow rootdn as a means to auth without the need to actually
+ * contact the proxied DSA */
+ switch ( be_rootdn_bind( op, rs ) ) {
+ case SLAP_CB_CONTINUE:
+ break;
+
+ default:
+ return rs->sr_err;
+ }
+
+ lc = ldap_back_getconn( op, rs, LDAP_BACK_BIND_SERR, NULL, NULL );
+ if ( !lc ) {
+ return rs->sr_err;
+ }
+
+ /* we can do (almost) whatever we want with this conn,
+ * because either it's temporary, or it's marked as binding */
+ if ( !BER_BVISNULL( &lc->lc_bound_ndn ) ) {
+ ch_free( lc->lc_bound_ndn.bv_val );
+ BER_BVZERO( &lc->lc_bound_ndn );
+ }
+ if ( !BER_BVISNULL( &lc->lc_cred ) ) {
+ memset( lc->lc_cred.bv_val, 0, lc->lc_cred.bv_len );
+ ch_free( lc->lc_cred.bv_val );
+ BER_BVZERO( &lc->lc_cred );
+ }
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+
+ /* don't add proxyAuthz; set the bindDN */
+ save_o_dn = op->o_dn;
+ save_o_do_not_cache = op->o_do_not_cache;
+ op->o_dn = op->o_req_dn;
+ op->o_do_not_cache = 1;
+
+ ctrls = op->o_ctrls;
+ rc = ldap_back_controls_add( op, rs, lc, &ctrls );
+ op->o_dn = save_o_dn;
+ op->o_do_not_cache = save_o_do_not_cache;
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ ldap_back_release_conn( li, lc );
+ return( rc );
+ }
+
+retry:;
+ /* method is always LDAP_AUTH_SIMPLE if we got here */
+ rs->sr_err = ldap_sasl_bind( lc->lc_ld, op->o_req_dn.bv_val,
+ LDAP_SASL_SIMPLE,
+ &op->orb_cred, ctrls, NULL, &msgid );
+ /* FIXME: should we always retry, or only when piping the bind
+ * in the "override" connection pool? */
+ rc = ldap_back_op_result( lc, op, rs, msgid,
+ li->li_timeout[ SLAP_OP_BIND ],
+ LDAP_BACK_BIND_SERR | retrying );
+ if ( rc == LDAP_UNAVAILABLE && retrying ) {
+ retrying &= ~LDAP_BACK_RETRYING;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_BIND_SERR ) ) {
+ goto retry;
+ }
+ if ( !lc )
+ return( rc );
+ }
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_BIND ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+ ldap_back_controls_free( op, rs, &ctrls );
+
+ if ( rc == LDAP_SUCCESS ) {
+ op->o_conn->c_authz_cookie = op->o_bd->be_private;
+
+ /* If defined, proxyAuthz will be used also when
+ * back-ldap is the authorizing backend; for this
+ * purpose, after a successful bind the connection
+ * is left for further binds, and further operations
+ * on this client connection will use a default
+ * connection with identity assertion */
+ /* NOTE: use with care */
+ if ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
+ ldap_back_release_conn( li, lc );
+ return( rc );
+ }
+
+ /* rebind is now done inside ldap_back_proxy_authz_bind()
+ * in case of success */
+ LDAP_BACK_CONN_ISBOUND_SET( lc );
+ ber_dupbv( &lc->lc_bound_ndn, &op->o_req_ndn );
+
+ if ( !BER_BVISNULL( &lc->lc_cred ) ) {
+ memset( lc->lc_cred.bv_val, 0,
+ lc->lc_cred.bv_len );
+ }
+
+ if ( LDAP_BACK_SAVECRED( li ) ) {
+ ber_bvreplace( &lc->lc_cred, &op->orb_cred );
+ ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc );
+
+ } else {
+ lc->lc_cred.bv_len = 0;
+ }
+ }
+
+ /* must re-insert if local DN changed as result of bind */
+ if ( !LDAP_BACK_CONN_ISBOUND( lc )
+ || ( !dn_match( &op->o_req_ndn, &lc->lc_local_ndn )
+ && !LDAP_BACK_PCONN_ISPRIV( lc ) ) )
+ {
+ int lerr = -1;
+ ldapconn_t *tmplc;
+
+ /* wait for all other ops to release the connection */
+retry_lock:;
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+ if ( lc->lc_refcnt > 1 ) {
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ ldap_pvt_thread_yield();
+ goto retry_lock;
+ }
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, ">>> ldap_back_bind" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+ assert( lc->lc_refcnt == 1 );
+ ldap_back_conn_delete( li, lc );
+
+ /* delete all cached connections with the current connection */
+ if ( LDAP_BACK_SINGLECONN( li ) ) {
+ while ( ( tmplc = ldap_tavl_delete( &li->li_conninfo.lai_tree, (caddr_t)lc, ldap_back_conn_cmp ) ) != NULL )
+ {
+ assert( !LDAP_BACK_PCONN_ISPRIV( lc ) );
+ Debug( LDAP_DEBUG_TRACE,
+ "=>ldap_back_bind: destroying conn %lu (refcnt=%u)\n",
+ lc->lc_conn->c_connid, lc->lc_refcnt );
+
+ if ( tmplc->lc_refcnt != 0 ) {
+ /* taint it */
+ LDAP_BACK_CONN_TAINTED_SET( tmplc );
+ LDAP_BACK_CONN_CACHED_CLEAR( tmplc );
+
+ } else {
+ /*
+ * Needs a test because the handler may be corrupted,
+ * and calling ldap_unbind on a corrupted header results
+ * in a segmentation fault
+ */
+ ldap_back_conn_free( tmplc );
+ }
+ }
+ }
+
+ if ( LDAP_BACK_CONN_ISBOUND( lc ) ) {
+ ber_bvreplace( &lc->lc_local_ndn, &op->o_req_ndn );
+ if ( be_isroot_dn( op->o_bd, &op->o_req_ndn ) ) {
+ LDAP_BACK_PCONN_ROOTDN_SET( lc, op );
+ }
+ lerr = ldap_tavl_insert( &li->li_conninfo.lai_tree, (caddr_t)lc,
+ ldap_back_conndn_cmp, ldap_back_conndn_dup );
+ }
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, "<<< ldap_back_bind" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ switch ( lerr ) {
+ case 0:
+ LDAP_BACK_CONN_CACHED_SET( lc );
+ break;
+
+ case -1:
+ /* duplicate; someone else successfully bound
+ * on the same connection with the same identity;
+ * we can do this because lc_refcnt == 1 */
+ ldap_back_conn_free( lc );
+ lc = NULL;
+ }
+ }
+
+ if ( lc != NULL ) {
+ ldap_back_release_conn( li, lc );
+ }
+
+ return( rc );
+}
+
+/*
+ * ldap_back_conndn_cmp
+ *
+ * compares two ldapconn_t based on the value of the conn pointer
+ * and of the local DN; used by avl stuff for insert, lookup
+ * and direct delete
+ */
+int
+ldap_back_conndn_cmp( const void *c1, const void *c2 )
+{
+ const ldapconn_t *lc1 = (const ldapconn_t *)c1;
+ const ldapconn_t *lc2 = (const ldapconn_t *)c2;
+ int rc;
+
+ /* If local DNs don't match, it is definitely not a match */
+ /* For shared sessions, conn is NULL. Only explicitly
+ * bound sessions will have non-NULL conn.
+ */
+ rc = SLAP_PTRCMP( lc1->lc_conn, lc2->lc_conn );
+ if ( rc == 0 ) {
+ rc = ber_bvcmp( &lc1->lc_local_ndn, &lc2->lc_local_ndn );
+ }
+
+ return rc;
+}
+
+/*
+ * ldap_back_conndnlc_cmp
+ *
+ * compares two ldapconn_t based on the value of the conn pointer,
+ * the local DN and the lc pointer; used by avl stuff for insert, lookup
+ * and direct delete
+ */
+static int
+ldap_back_conndnlc_cmp( const void *c1, const void *c2 )
+{
+ const ldapconn_t *lc1 = (const ldapconn_t *)c1;
+ const ldapconn_t *lc2 = (const ldapconn_t *)c2;
+ int rc;
+
+ /* If local DNs don't match, it is definitely not a match */
+ /* For shared sessions, conn is NULL. Only explicitly
+ * bound sessions will have non-NULL conn.
+ */
+ rc = SLAP_PTRCMP( lc1->lc_conn, lc2->lc_conn );
+ if ( rc == 0 ) {
+ rc = ber_bvcmp( &lc1->lc_local_ndn, &lc2->lc_local_ndn );
+ if ( rc == 0 ) {
+ rc = SLAP_PTRCMP( lc1, lc2 );
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * ldap_back_conn_cmp
+ *
+ * compares two ldapconn_t based on the value of the conn pointer;
+ * used by avl stuff for delete of all conns with the same connid
+ */
+int
+ldap_back_conn_cmp( const void *c1, const void *c2 )
+{
+ const ldapconn_t *lc1 = (const ldapconn_t *)c1;
+ const ldapconn_t *lc2 = (const ldapconn_t *)c2;
+
+ /* For shared sessions, conn is NULL. Only explicitly
+ * bound sessions will have non-NULL conn.
+ */
+ return SLAP_PTRCMP( lc1->lc_conn, lc2->lc_conn );
+}
+
+/*
+ * ldap_back_conndn_dup
+ *
+ * returns -1 in case a duplicate ldapconn_t has been inserted;
+ * used by avl stuff
+ */
+int
+ldap_back_conndn_dup( void *c1, void *c2 )
+{
+ ldapconn_t *lc1 = (ldapconn_t *)c1;
+ ldapconn_t *lc2 = (ldapconn_t *)c2;
+
+ /* Cannot have more than one shared session with same DN */
+ if ( lc1->lc_conn == lc2->lc_conn &&
+ dn_match( &lc1->lc_local_ndn, &lc2->lc_local_ndn ) )
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+ldap_back_freeconn( ldapinfo_t *li, ldapconn_t *lc, int dolock )
+{
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+ }
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, ">>> ldap_back_freeconn" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+ (void)ldap_back_conn_delete( li, lc );
+
+ if ( lc->lc_refcnt == 0 ) {
+ ldap_back_conn_free( (void *)lc );
+ }
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, "<<< ldap_back_freeconn" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ }
+
+ return 0;
+}
+
+#ifdef HAVE_TLS
+static int
+ldap_back_start_tls(
+ LDAP *ld,
+ int protocol,
+ int *is_tls,
+ const char *url,
+ unsigned flags,
+ int timeout,
+ const char **text )
+{
+ int rc = LDAP_SUCCESS;
+
+ /* start TLS ("tls-[try-]{start,propagate}" statements) */
+ if ( ( LDAP_BACK_USE_TLS_F( flags ) || ( *is_tls && LDAP_BACK_PROPAGATE_TLS_F( flags ) ) )
+ && !ldap_is_ldaps_url( url ) )
+ {
+#ifdef SLAP_STARTTLS_ASYNCHRONOUS
+ /*
+ * use asynchronous StartTLS
+ * in case, chase referral (not implemented yet)
+ */
+ int msgid;
+
+ if ( protocol == 0 ) {
+ ldap_get_option( ld, LDAP_OPT_PROTOCOL_VERSION,
+ (void *)&protocol );
+ }
+
+ if ( protocol < LDAP_VERSION3 ) {
+ /* we should rather bail out... */
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ *text = "invalid protocol version";
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ rc = ldap_start_tls( ld, NULL, NULL, &msgid );
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ LDAPMessage *res = NULL;
+ struct timeval tv;
+
+ if ( timeout ) {
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ } else {
+ LDAP_BACK_TV_SET( &tv );
+ }
+ rc = ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res );
+ if ( rc <= 0 ) {
+ rc = LDAP_UNAVAILABLE;
+
+ } else if ( rc == LDAP_RES_EXTENDED ) {
+ struct berval *data = NULL;
+
+ rc = ldap_parse_extended_result( ld, res,
+ NULL, &data, 0 );
+ if ( rc == LDAP_SUCCESS ) {
+ SlapReply rs;
+ rc = ldap_parse_result( ld, res, &rs.sr_err,
+ NULL, NULL, NULL, NULL, 1 );
+ if ( rc != LDAP_SUCCESS ) {
+ rs.sr_err = rc;
+ }
+ rc = slap_map_api2result( &rs );
+ res = NULL;
+
+ /* FIXME: in case a referral
+ * is returned, should we try
+ * using it instead of the
+ * configured URI? */
+ if ( rc == LDAP_SUCCESS ) {
+ rc = ldap_install_tls( ld );
+
+ } else if ( rc == LDAP_REFERRAL ) {
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ *text = "unwilling to chase referral returned by Start TLS exop";
+ }
+
+ if ( data ) {
+ if ( data->bv_val ) {
+ ber_memfree( data->bv_val );
+ }
+ ber_memfree( data );
+ }
+ }
+
+ } else {
+ rc = LDAP_OTHER;
+ }
+
+ if ( res != NULL ) {
+ ldap_msgfree( res );
+ }
+ }
+#else /* ! SLAP_STARTTLS_ASYNCHRONOUS */
+ /*
+ * use synchronous StartTLS
+ */
+ rc = ldap_start_tls_s( ld, NULL, NULL );
+#endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */
+
+ /* if StartTLS is requested, only attempt it if the URL
+ * is not "ldaps://"; this may occur not only in case
+ * of misconfiguration, but also when used in the chain
+ * overlay, where the "uri" can be parsed out of a referral */
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ *is_tls = 1;
+ break;
+
+ case LDAP_SERVER_DOWN:
+ break;
+
+ default:
+ if ( LDAP_BACK_TLS_CRITICAL_F( flags ) ) {
+ *text = "could not start TLS";
+ break;
+ }
+
+ /* in case Start TLS is not critical */
+ *is_tls = 0;
+ rc = LDAP_SUCCESS;
+ break;
+ }
+
+ } else {
+ *is_tls = 0;
+ }
+
+ return rc;
+}
+#endif /* HAVE_TLS */
+
+static int
+ldap_back_prepare_conn( ldapconn_t *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+ int version;
+ LDAP *ld = NULL;
+#ifdef HAVE_TLS
+ int is_tls = op->o_conn->c_is_tls;
+ int flags = li->li_flags;
+ time_t lctime = (time_t)(-1);
+ slap_bindconf *sb;
+#endif /* HAVE_TLS */
+
+ ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
+ rs->sr_err = ldap_initialize( &ld, li->li_uri );
+ ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto error_return;
+ }
+
+ if ( li->li_urllist_f ) {
+ ldap_set_urllist_proc( ld, li->li_urllist_f, li->li_urllist_p );
+ }
+
+ /* Set LDAP version. This will always succeed: If the client
+ * bound with a particular version, then so can we.
+ */
+ if ( li->li_version != 0 ) {
+ version = li->li_version;
+
+ } else if ( op->o_protocol != 0 ) {
+ version = op->o_protocol;
+
+ } else {
+ /* assume it's an internal op; set to LDAPv3 */
+ version = LDAP_VERSION3;
+ }
+ ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, (const void *)&version );
+
+ /* automatically chase referrals ("chase-referrals [{yes|no}]" statement) */
+ ldap_set_option( ld, LDAP_OPT_REFERRALS,
+ LDAP_BACK_CHASE_REFERRALS( li ) ? LDAP_OPT_ON : LDAP_OPT_OFF );
+
+ if ( li->li_network_timeout > 0 ) {
+ struct timeval tv;
+
+ tv.tv_sec = li->li_network_timeout;
+ tv.tv_usec = 0;
+ ldap_set_option( ld, LDAP_OPT_NETWORK_TIMEOUT, (const void *)&tv );
+ }
+
+ /* turn on network keepalive, if configured so */
+ slap_client_keepalive(ld, &li->li_tls.sb_keepalive);
+
+ if ( li->li_tls.sb_tcp_user_timeout > 0 ) {
+ ldap_set_option( ld, LDAP_OPT_TCP_USER_TIMEOUT,
+ &li->li_tls.sb_tcp_user_timeout );
+ }
+
+#ifdef HAVE_TLS
+ if ( LDAP_BACK_CONN_ISPRIV( lc ) ) {
+ /* See "rationale" comment in ldap_back_getconn() */
+ if ( li->li_acl_authmethod == LDAP_AUTH_NONE &&
+ li->li_idassert_authmethod != LDAP_AUTH_NONE )
+ sb = &li->li_idassert.si_bc;
+ else
+ sb = &li->li_acl;
+
+ } else if ( LDAP_BACK_CONN_ISIDASSERT( lc ) ) {
+ sb = &li->li_idassert.si_bc;
+
+ } else {
+ sb = &li->li_tls;
+ }
+
+ bindconf_tls_set( sb, ld );
+
+ /* if required by the bindconf configuration, force TLS */
+ if ( ( sb == &li->li_acl || sb == &li->li_idassert.si_bc ) &&
+ sb->sb_tls_ctx )
+ {
+ flags |= LDAP_BACK_F_USE_TLS;
+ }
+
+ ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
+ assert( li->li_uri_mutex_do_not_lock == 0 );
+ li->li_uri_mutex_do_not_lock = 1;
+ rs->sr_err = ldap_back_start_tls( ld, op->o_protocol, &is_tls,
+ li->li_uri, flags, li->li_timeout[ SLAP_OP_BIND ], &rs->sr_text );
+ li->li_uri_mutex_do_not_lock = 0;
+ ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ rs->sr_text = "Start TLS failed";
+ goto error_return;
+
+ } else if ( li->li_idle_timeout ) {
+ /* only touch when activity actually took place... */
+ lctime = op->o_time;
+ }
+#endif /* HAVE_TLS */
+
+ lc->lc_ld = ld;
+ lc->lc_refcnt = 1;
+#ifdef HAVE_TLS
+ if ( is_tls ) {
+ LDAP_BACK_CONN_ISTLS_SET( lc );
+ } else {
+ LDAP_BACK_CONN_ISTLS_CLEAR( lc );
+ }
+ if ( lctime != (time_t)(-1) ) {
+ lc->lc_time = lctime;
+ }
+#endif /* HAVE_TLS */
+
+error_return:;
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ rs->sr_err = slap_map_api2result( rs );
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ if ( rs->sr_text == NULL ) {
+ rs->sr_text = "Proxy connection initialization failed";
+ }
+ send_ldap_result( op, rs );
+ }
+
+ } else {
+ if ( li->li_conn_ttl > 0 ) {
+ lc->lc_create_time = op->o_time;
+ }
+ }
+
+ return rs->sr_err;
+}
+
+static ldapconn_t *
+ldap_back_getconn(
+ Operation *op,
+ SlapReply *rs,
+ ldap_back_send_t sendok,
+ struct berval *binddn,
+ struct berval *bindcred )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+ ldapconn_t *lc = NULL,
+ lc_curr = {{ 0 }};
+ int refcnt = 1,
+ lookupconn = !( sendok & LDAP_BACK_BINDING );
+
+ /* if the server is quarantined, and
+ * - the current interval did not expire yet, or
+ * - no more retries should occur,
+ * don't return the connection */
+ if ( li->li_isquarantined ) {
+ slap_retry_info_t *ri = &li->li_quarantine;
+ int dont_retry = 1;
+
+ if ( li->li_quarantine.ri_interval ) {
+ ldap_pvt_thread_mutex_lock( &li->li_quarantine_mutex );
+ if ( li->li_isquarantined == LDAP_BACK_FQ_YES ) {
+ dont_retry = ( ri->ri_num[ ri->ri_idx ] == SLAP_RETRYNUM_TAIL
+ || slap_get_time() < ri->ri_last + ri->ri_interval[ ri->ri_idx ] );
+ if ( !dont_retry ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: ldap_back_getconn quarantine "
+ "retry block #%d try #%d.\n",
+ op->o_log_prefix, ri->ri_idx, ri->ri_count );
+ li->li_isquarantined = LDAP_BACK_FQ_RETRYING;
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &li->li_quarantine_mutex );
+ }
+
+ if ( dont_retry ) {
+ rs->sr_err = LDAP_UNAVAILABLE;
+ if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
+ rs->sr_text = "Target is quarantined";
+ send_ldap_result( op, rs );
+ }
+ return NULL;
+ }
+ }
+
+ /* Internal searches are privileged and shared. So is root. */
+ if ( op->o_do_not_cache || be_isroot( op ) ) {
+ LDAP_BACK_CONN_ISPRIV_SET( &lc_curr );
+ lc_curr.lc_local_ndn = op->o_bd->be_rootndn;
+ LDAP_BACK_PCONN_ROOTDN_SET( &lc_curr, op );
+
+ } else {
+ struct berval tmpbinddn,
+ tmpbindcred,
+ save_o_dn,
+ save_o_ndn;
+ int isproxyauthz;
+
+ /* need cleanup */
+ if ( binddn == NULL ) {
+ binddn = &tmpbinddn;
+ }
+ if ( bindcred == NULL ) {
+ bindcred = &tmpbindcred;
+ }
+ if ( op->o_tag == LDAP_REQ_BIND ) {
+ save_o_dn = op->o_dn;
+ save_o_ndn = op->o_ndn;
+ op->o_dn = op->o_req_dn;
+ op->o_ndn = op->o_req_ndn;
+ }
+ isproxyauthz = ldap_back_is_proxy_authz( op, rs, sendok, binddn, bindcred );
+ if ( op->o_tag == LDAP_REQ_BIND ) {
+ op->o_dn = save_o_dn;
+ op->o_ndn = save_o_ndn;
+ }
+ if ( isproxyauthz == -1 ) {
+ return NULL;
+ }
+
+ lc_curr.lc_local_ndn = op->o_ndn;
+ /* Explicit binds must not be shared;
+ * however, explicit binds are piped in a special connection
+ * when idassert is to occur with "override" set */
+ if ( op->o_tag == LDAP_REQ_BIND && !isproxyauthz ) {
+ lc_curr.lc_conn = op->o_conn;
+
+ } else {
+ if ( isproxyauthz && !( sendok & LDAP_BACK_BINDING ) ) {
+ lc_curr.lc_local_ndn = *binddn;
+ LDAP_BACK_PCONN_ROOTDN_SET( &lc_curr, op );
+ LDAP_BACK_CONN_ISIDASSERT_SET( &lc_curr );
+
+ } else if ( isproxyauthz && ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) {
+ lc_curr.lc_local_ndn = slap_empty_bv;
+ LDAP_BACK_PCONN_BIND_SET( &lc_curr, op );
+ LDAP_BACK_CONN_ISIDASSERT_SET( &lc_curr );
+ lookupconn = 1;
+
+ } else if ( SLAP_IS_AUTHZ_BACKEND( op ) ) {
+ lc_curr.lc_conn = op->o_conn;
+
+ } else {
+ LDAP_BACK_PCONN_ANON_SET( &lc_curr, op );
+ }
+ }
+ }
+
+ /* Explicit Bind requests always get their own conn */
+ if ( lookupconn ) {
+retry_lock:
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+ if ( LDAP_BACK_PCONN_ISPRIV( &lc_curr ) ) {
+ /* lookup a conn that's not binding */
+ LDAP_TAILQ_FOREACH( lc,
+ &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( &lc_curr ) ].lic_priv,
+ lc_q )
+ {
+ if ( !LDAP_BACK_CONN_BINDING( lc ) && lc->lc_refcnt == 0 ) {
+ break;
+ }
+ }
+
+ if ( lc != NULL ) {
+ if ( lc != LDAP_TAILQ_LAST( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv,
+ lc_conn_priv_q ) )
+ {
+ LDAP_TAILQ_REMOVE( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv,
+ lc, lc_q );
+ LDAP_TAILQ_ENTRY_INIT( lc, lc_q );
+ LDAP_TAILQ_INSERT_TAIL( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv,
+ lc, lc_q );
+ }
+
+ } else if ( !LDAP_BACK_USE_TEMPORARIES( li )
+ && li->li_conn_priv[ LDAP_BACK_CONN2PRIV( &lc_curr ) ].lic_num == li->li_conn_priv_max )
+ {
+ lc = LDAP_TAILQ_FIRST( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( &lc_curr ) ].lic_priv );
+ }
+
+ } else {
+
+ /* Searches for a ldapconn in the avl tree */
+ lc = (ldapconn_t *)ldap_tavl_find( li->li_conninfo.lai_tree,
+ (caddr_t)&lc_curr, ldap_back_conndn_cmp );
+ }
+
+ if ( lc != NULL ) {
+ /* Don't reuse connections while they're still binding */
+ if ( LDAP_BACK_CONN_BINDING( lc ) ) {
+ if ( !LDAP_BACK_USE_TEMPORARIES( li ) ) {
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+
+ ldap_pvt_thread_yield();
+ goto retry_lock;
+ }
+ lc = NULL;
+ }
+
+ if ( lc != NULL ) {
+ if ( op->o_tag == LDAP_REQ_BIND ) {
+ /* right now, this is the only possible case */
+ assert( ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) );
+ LDAP_BACK_CONN_BINDING_SET( lc );
+ }
+
+ refcnt = ++lc->lc_refcnt;
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ }
+
+ /* Looks like we didn't get a bind. Open a new session... */
+ if ( lc == NULL ) {
+ lc = (ldapconn_t *)ch_calloc( 1, sizeof( ldapconn_t ) );
+ lc->lc_flags = li->li_flags;
+ lc->lc_lcflags = lc_curr.lc_lcflags;
+ lc->lc_ldapinfo = li;
+ if ( ldap_back_prepare_conn( lc, op, rs, sendok ) != LDAP_SUCCESS ) {
+ ch_free( lc );
+ return NULL;
+ }
+
+ if ( sendok & LDAP_BACK_BINDING ) {
+ LDAP_BACK_CONN_BINDING_SET( lc );
+ }
+
+ lc->lc_conn = lc_curr.lc_conn;
+ ber_dupbv( &lc->lc_local_ndn, &lc_curr.lc_local_ndn );
+
+ /*
+ * the rationale is: connections as the rootdn are privileged,
+ * so li_acl is to be used; however, in some cases
+ * one already configured identity assertion with a highly
+ * privileged idassert_authcDN, so if li_acl is not configured
+ * and idassert is, use idassert instead.
+ *
+ * might change in the future, because it's preferable
+ * to make clear what identity is being used, since
+ * the only drawback is that one risks to configure
+ * the same identity twice...
+ */
+ if ( LDAP_BACK_CONN_ISPRIV( &lc_curr ) ) {
+ if ( li->li_acl_authmethod == LDAP_AUTH_NONE &&
+ li->li_idassert_authmethod != LDAP_AUTH_NONE ) {
+ ber_dupbv( &lc->lc_bound_ndn, &li->li_idassert_authcDN );
+ ber_dupbv( &lc->lc_cred, &li->li_idassert_passwd );
+
+ } else {
+ ber_dupbv( &lc->lc_bound_ndn, &li->li_acl_authcDN );
+ ber_dupbv( &lc->lc_cred, &li->li_acl_passwd );
+ }
+ LDAP_BACK_CONN_ISPRIV_SET( lc );
+
+ } else if ( LDAP_BACK_CONN_ISIDASSERT( &lc_curr ) ) {
+ if ( !LDAP_BACK_PCONN_ISBIND( &lc_curr ) ) {
+ ber_dupbv( &lc->lc_bound_ndn, &li->li_idassert_authcDN );
+ ber_dupbv( &lc->lc_cred, &li->li_idassert_passwd );
+ }
+ LDAP_BACK_CONN_ISIDASSERT_SET( lc );
+
+ } else {
+ BER_BVZERO( &lc->lc_cred );
+ BER_BVZERO( &lc->lc_bound_ndn );
+ if ( !BER_BVISEMPTY( &op->o_ndn )
+ && SLAP_IS_AUTHZ_BACKEND( op ) )
+ {
+ ber_dupbv( &lc->lc_bound_ndn, &op->o_ndn );
+ }
+ }
+
+#ifdef HAVE_TLS
+ /* if start TLS failed but it was not mandatory,
+ * check if the non-TLS connection was already
+ * in cache; in case, destroy the newly created
+ * connection and use the existing one */
+ if ( LDAP_BACK_PCONN_ISTLS( lc )
+ && !ldap_tls_inplace( lc->lc_ld ) )
+ {
+ ldapconn_t *tmplc = NULL;
+ int idx = LDAP_BACK_CONN2PRIV( &lc_curr ) - 1;
+
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+ LDAP_TAILQ_FOREACH( tmplc,
+ &li->li_conn_priv[ idx ].lic_priv,
+ lc_q )
+ {
+ if ( !LDAP_BACK_CONN_BINDING( tmplc ) ) {
+ break;
+ }
+ }
+
+ if ( tmplc != NULL ) {
+ refcnt = ++tmplc->lc_refcnt;
+ ldap_back_conn_free( lc );
+ lc = tmplc;
+ }
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+
+ if ( tmplc != NULL ) {
+ goto done;
+ }
+ }
+#endif /* HAVE_TLS */
+
+ /* Inserts the newly created ldapconn in the avl tree */
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+ lc->lc_connid = li->li_conn_nextid++;
+
+ assert( lc->lc_refcnt == 1 );
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, ">>> ldap_back_getconn(insert)" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+ if ( LDAP_BACK_PCONN_ISPRIV( lc ) ) {
+ if ( li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num < li->li_conn_priv_max ) {
+ LDAP_TAILQ_INSERT_TAIL( &li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_priv, lc, lc_q );
+ li->li_conn_priv[ LDAP_BACK_CONN2PRIV( lc ) ].lic_num++;
+ LDAP_BACK_CONN_CACHED_SET( lc );
+
+ } else {
+ LDAP_BACK_CONN_TAINTED_SET( lc );
+ }
+ rs->sr_err = 0;
+
+ } else {
+ rs->sr_err = ldap_tavl_insert( &li->li_conninfo.lai_tree, (caddr_t)lc,
+ ldap_back_conndn_cmp, ldap_back_conndn_dup );
+ LDAP_BACK_CONN_CACHED_SET( lc );
+ }
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, "<<< ldap_back_getconn(insert)" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+
+ Debug(LDAP_DEBUG_TRACE,
+ "=>ldap_back_getconn: %s: lc=%p inserted refcnt=%u rc=%d\n",
+ op->o_log_prefix, (void *)lc, refcnt,
+ rs->sr_err );
+
+ if ( !LDAP_BACK_PCONN_ISPRIV( lc ) ) {
+ /* Err could be -1 in case a duplicate ldapconn is inserted */
+ switch ( rs->sr_err ) {
+ case 0:
+ break;
+
+ case -1:
+ LDAP_BACK_CONN_CACHED_CLEAR( lc );
+ if ( !( sendok & LDAP_BACK_BINDING ) && !LDAP_BACK_USE_TEMPORARIES( li ) ) {
+ /* duplicate: free and try to get the newly created one */
+ ldap_back_conn_free( lc );
+ lc = NULL;
+ goto retry_lock;
+ }
+
+ /* taint connection, so that it'll be freed when released */
+ LDAP_BACK_CONN_TAINTED_SET( lc );
+ break;
+
+ default:
+ LDAP_BACK_CONN_CACHED_CLEAR( lc );
+ ldap_back_conn_free( lc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "Proxy bind collision";
+ if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
+ send_ldap_result( op, rs );
+ }
+ return NULL;
+ }
+ }
+ ldap_back_schedule_conn_expiry( li, lc );
+
+ } else {
+ int expiring = 0;
+
+ if ( ( li->li_idle_timeout != 0 && op->o_time > lc->lc_time + li->li_idle_timeout )
+ || ( li->li_conn_ttl != 0 && op->o_time > lc->lc_create_time + li->li_conn_ttl ) )
+ {
+ expiring = 1;
+
+ /* let it be used, but taint/delete it so that
+ * no-one else can look it up any further */
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, ">>> ldap_back_getconn(timeout)" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+ (void)ldap_back_conn_delete( li, lc );
+ LDAP_BACK_CONN_TAINTED_SET( lc );
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, "<<< ldap_back_getconn(timeout)" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ "=>ldap_back_getconn: conn %p fetched refcnt=%u%s.\n",
+ (void *)lc, refcnt, expiring ? " expiring" : "" );
+ }
+
+#ifdef HAVE_TLS
+done:;
+#endif /* HAVE_TLS */
+
+ return lc;
+}
+
+void
+ldap_back_release_conn_lock(
+ ldapinfo_t *li,
+ ldapconn_t **lcp,
+ int dolock )
+{
+
+ ldapconn_t *lc = *lcp;
+
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+ }
+ assert( lc->lc_refcnt > 0 );
+ LDAP_BACK_CONN_BINDING_CLEAR( lc );
+ lc->lc_refcnt--;
+ if ( LDAP_BACK_CONN_TAINTED( lc ) ) {
+ ldap_back_freeconn( li, lc, 0 );
+ *lcp = NULL;
+ }
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ }
+}
+
+void
+ldap_back_quarantine(
+ Operation *op,
+ SlapReply *rs )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ slap_retry_info_t *ri = &li->li_quarantine;
+
+ ldap_pvt_thread_mutex_lock( &li->li_quarantine_mutex );
+
+ if ( rs->sr_err == LDAP_UNAVAILABLE ) {
+ time_t new_last = slap_get_time();
+
+ switch ( li->li_isquarantined ) {
+ case LDAP_BACK_FQ_NO:
+ if ( ri->ri_last == new_last ) {
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_ANY,
+ "%s: ldap_back_quarantine enter.\n",
+ op->o_log_prefix );
+
+ ri->ri_idx = 0;
+ ri->ri_count = 0;
+ break;
+
+ case LDAP_BACK_FQ_RETRYING:
+ Debug( LDAP_DEBUG_ANY,
+ "%s: ldap_back_quarantine block #%d try #%d failed.\n",
+ op->o_log_prefix, ri->ri_idx, ri->ri_count );
+
+ ++ri->ri_count;
+ if ( ri->ri_num[ ri->ri_idx ] != SLAP_RETRYNUM_FOREVER
+ && ri->ri_count == ri->ri_num[ ri->ri_idx ] )
+ {
+ ri->ri_count = 0;
+ ++ri->ri_idx;
+ }
+ break;
+
+ default:
+ goto done;
+ }
+
+ li->li_isquarantined = LDAP_BACK_FQ_YES;
+ ri->ri_last = new_last;
+
+ } else if ( li->li_isquarantined != LDAP_BACK_FQ_NO ) {
+ if ( ri->ri_last == slap_get_time() ) {
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_ANY,
+ "%s: ldap_back_quarantine exit (%d) err=%d.\n",
+ op->o_log_prefix, li->li_isquarantined, rs->sr_err );
+
+ if ( li->li_quarantine_f ) {
+ (void)li->li_quarantine_f( li, li->li_quarantine_p );
+ }
+
+ ri->ri_count = 0;
+ ri->ri_idx = 0;
+ li->li_isquarantined = LDAP_BACK_FQ_NO;
+ }
+
+done:;
+ ldap_pvt_thread_mutex_unlock( &li->li_quarantine_mutex );
+}
+
+static int
+ldap_back_dobind_cb(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ ber_tag_t *tptr = op->o_callback->sc_private;
+ op->o_tag = *tptr;
+ rs->sr_tag = slap_req2res( op->o_tag );
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+ * ldap_back_dobind_int
+ *
+ * Note: dolock indicates whether li->li_conninfo.lai_mutex must be locked or not
+ */
+static int
+ldap_back_dobind_int(
+ ldapconn_t **lcp,
+ Operation *op,
+ SlapReply *rs,
+ ldap_back_send_t sendok,
+ int retries,
+ int dolock )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ ldapconn_t *lc;
+ struct berval binddn = slap_empty_bv,
+ bindcred = slap_empty_bv;
+
+ int rc = 0,
+ isbound,
+ binding = 0;
+ ber_int_t msgid;
+ ber_tag_t o_tag = op->o_tag;
+ slap_callback cb = {0};
+ char *tmp_dn;
+
+ assert( lcp != NULL );
+ assert( retries >= 0 );
+
+ if ( sendok & LDAP_BACK_GETCONN ) {
+ assert( *lcp == NULL );
+
+ lc = ldap_back_getconn( op, rs, sendok, &binddn, &bindcred );
+ if ( lc == NULL ) {
+ return 0;
+ }
+ *lcp = lc;
+
+ } else {
+ lc = *lcp;
+ }
+
+ assert( lc != NULL );
+
+retry_lock:;
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+ }
+
+ if ( binding == 0 ) {
+ /* check if already bound */
+ rc = isbound = LDAP_BACK_CONN_ISBOUND( lc );
+ if ( isbound ) {
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ }
+ return rc;
+ }
+
+ if ( LDAP_BACK_CONN_BINDING( lc ) ) {
+ /* if someone else is about to bind it, give up and retry */
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ }
+ ldap_pvt_thread_yield();
+ goto retry_lock;
+
+ } else {
+ /* otherwise this thread will bind it */
+ LDAP_BACK_CONN_BINDING_SET( lc );
+ binding = 1;
+ }
+ }
+
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ }
+
+ /*
+ * FIXME: we need to let clients use proxyAuthz
+ * otherwise we cannot do symmetric pools of servers;
+ * we have to live with the fact that a user can
+ * authorize itself as any ID that is allowed
+ * by the authzTo directive of the "proxyauthzdn".
+ */
+ /*
+ * NOTE: current Proxy Authorization specification
+ * and implementation do not allow proxy authorization
+ * control to be provided with Bind requests
+ */
+ /*
+ * if no bind took place yet, but the connection is bound
+ * and the "idassert-authcDN" (or other ID) is set,
+ * then bind as the asserting identity and explicitly
+ * add the proxyAuthz control to every operation with the
+ * dn bound to the connection as control value.
+ * This is done also if this is the authorizing backend,
+ * but the "override" flag is given to idassert.
+ * It allows to use SASL bind and yet proxyAuthz users
+ */
+ op->o_tag = LDAP_REQ_BIND;
+ cb.sc_next = op->o_callback;
+ cb.sc_private = &o_tag;
+ cb.sc_response = ldap_back_dobind_cb;
+ op->o_callback = &cb;
+
+ if ( LDAP_BACK_CONN_ISIDASSERT( lc ) ) {
+ if ( BER_BVISEMPTY( &binddn ) && BER_BVISEMPTY( &bindcred ) ) {
+ /* if we got here, it shouldn't return result */
+ rc = ldap_back_is_proxy_authz( op, rs,
+ LDAP_BACK_DONTSEND, &binddn, &bindcred );
+ if ( rc != 1 ) {
+ Debug( LDAP_DEBUG_ANY, "Error: ldap_back_is_proxy_authz "
+ "returned %d, misconfigured URI?\n", rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "misconfigured URI?";
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ goto done;
+ }
+ }
+ rc = ldap_back_proxy_authz_bind( lc, op, rs, sendok, &binddn, &bindcred );
+ goto done;
+ }
+
+#ifdef HAVE_CYRUS_SASL
+ if ( LDAP_BACK_CONN_ISPRIV( lc )) {
+ slap_bindconf *sb;
+ if ( li->li_acl_authmethod != LDAP_AUTH_NONE )
+ sb = &li->li_acl;
+ else
+ sb = &li->li_idassert.si_bc;
+
+ if ( sb->sb_method == LDAP_AUTH_SASL ) {
+ void *defaults = NULL;
+
+ if ( sb->sb_secprops != NULL ) {
+ rc = ldap_set_option( lc->lc_ld,
+ LDAP_OPT_X_SASL_SECPROPS, sb->sb_secprops );
+
+ if ( rc != LDAP_OPT_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "Error: ldap_set_option "
+ "(SECPROPS,\"%s\") failed!\n",
+ sb->sb_secprops );
+ goto done;
+ }
+ }
+
+ defaults = lutil_sasl_defaults( lc->lc_ld,
+ sb->sb_saslmech.bv_val,
+ sb->sb_realm.bv_val,
+ sb->sb_authcId.bv_val,
+ sb->sb_cred.bv_val,
+ NULL );
+ if ( defaults == NULL ) {
+ rs->sr_err = LDAP_OTHER;
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ goto done;
+ }
+
+ rs->sr_err = ldap_sasl_interactive_bind_s( lc->lc_ld,
+ sb->sb_binddn.bv_val,
+ sb->sb_saslmech.bv_val, NULL, NULL,
+ LDAP_SASL_QUIET, lutil_sasl_interact,
+ defaults );
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_BIND ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+ lutil_sasl_freedefs( defaults );
+
+ switch ( rs->sr_err ) {
+ case LDAP_SUCCESS:
+ LDAP_BACK_CONN_ISBOUND_SET( lc );
+ break;
+
+ case LDAP_LOCAL_ERROR:
+ /* list client API error codes that require
+ * to taint the connection */
+ /* FIXME: should actually retry? */
+ LDAP_BACK_CONN_TAINTED_SET( lc );
+
+ /* fallthru */
+
+ default:
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+ rs->sr_err = slap_map_api2result( rs );
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ break;
+ }
+
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs );
+ }
+
+ goto done;
+ }
+ }
+#endif /* HAVE_CYRUS_SASL */
+
+retry:;
+ if ( BER_BVISNULL( &lc->lc_cred ) ) {
+ tmp_dn = "";
+ if ( !BER_BVISNULL( &lc->lc_bound_ndn ) && !BER_BVISEMPTY( &lc->lc_bound_ndn ) ) {
+ Debug( LDAP_DEBUG_ANY, "%s ldap_back_dobind_int: DN=\"%s\" without creds, binding anonymously",
+ op->o_log_prefix, lc->lc_bound_ndn.bv_val );
+ }
+
+ } else {
+ tmp_dn = lc->lc_bound_ndn.bv_val;
+ }
+ rs->sr_err = ldap_sasl_bind( lc->lc_ld,
+ tmp_dn,
+ LDAP_SASL_SIMPLE, &lc->lc_cred,
+ NULL, NULL, &msgid );
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_BIND ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+ if ( rs->sr_err == LDAP_SERVER_DOWN ) {
+ if ( retries != LDAP_BACK_RETRY_NEVER ) {
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+ }
+
+ assert( lc->lc_refcnt > 0 );
+ if ( lc->lc_refcnt == 1 ) {
+ ldap_unbind_ext( lc->lc_ld, NULL, NULL );
+ lc->lc_ld = NULL;
+
+ /* lc here must be the regular lc, reset and ready for init */
+ rs->sr_err = ldap_back_prepare_conn( lc, op, rs, sendok );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ sendok &= ~LDAP_BACK_SENDERR;
+ lc->lc_refcnt = 0;
+ }
+ }
+
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ }
+
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ if ( retries > 0 ) {
+ retries--;
+ }
+ goto retry;
+ }
+ }
+
+ assert( lc->lc_refcnt == 1 );
+ lc->lc_refcnt = 0;
+ ldap_back_freeconn( li, lc, dolock );
+ *lcp = NULL;
+ rs->sr_err = slap_map_api2result( rs );
+
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs );
+ }
+
+ if ( rs->sr_err != LDAP_SUCCESS &&
+ ( sendok & LDAP_BACK_SENDERR ) )
+ {
+ if ( op->o_callback == &cb )
+ op->o_callback = cb.sc_next;
+ op->o_tag = o_tag;
+ rs->sr_text = "Proxy can't contact remote server";
+ send_ldap_result( op, rs );
+ /* if we originally bound and wanted rebind-as-user, must drop
+ * the connection now because we just discarded the credentials.
+ * ITS#7464, #8142
+ */
+ if ( LDAP_BACK_SAVECRED( li ) && SLAP_IS_AUTHZ_BACKEND( op ) )
+ rs->sr_err = SLAPD_DISCONNECT;
+ }
+
+ rc = 0;
+ goto func_leave;
+ }
+
+ rc = ldap_back_op_result( lc, op, rs, msgid,
+ -1, ( sendok | LDAP_BACK_BINDING ) );
+ if ( rc == LDAP_SUCCESS ) {
+ LDAP_BACK_CONN_ISBOUND_SET( lc );
+ }
+
+done:;
+ LDAP_BACK_CONN_BINDING_CLEAR( lc );
+ rc = LDAP_BACK_CONN_ISBOUND( lc );
+ if ( !rc ) {
+ ldap_back_release_conn_lock( li, lcp, dolock );
+
+ } else if ( LDAP_BACK_SAVECRED( li ) ) {
+ ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc );
+ }
+
+func_leave:;
+ if ( op->o_callback == &cb )
+ op->o_callback = cb.sc_next;
+ op->o_tag = o_tag;
+
+ return rc;
+}
+
+/*
+ * ldap_back_dobind
+ *
+ * Note: dolock indicates whether li->li_conninfo.lai_mutex must be locked or not
+ */
+int
+ldap_back_dobind( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ return ldap_back_dobind_int( lcp, op, rs,
+ ( sendok | LDAP_BACK_GETCONN ), li->li_nretries, 1 );
+}
+
+/*
+ * ldap_back_default_rebind
+ *
+ * This is a callback used for chasing referrals using the same
+ * credentials as the original user on this session.
+ */
+int
+ldap_back_default_rebind( LDAP *ld, LDAP_CONST char *url, ber_tag_t request,
+ ber_int_t msgid, void *params )
+{
+ ldapconn_t *lc = (ldapconn_t *)params;
+
+#ifdef HAVE_TLS
+ /* ... otherwise we couldn't get here */
+ assert( lc != NULL );
+
+ if ( !ldap_tls_inplace( ld ) ) {
+ int is_tls = LDAP_BACK_CONN_ISTLS( lc ),
+ rc;
+ const char *text = NULL;
+
+ rc = ldap_back_start_tls( ld, 0, &is_tls, url, lc->lc_flags,
+ lc->lc_ldapinfo->li_timeout[ SLAP_OP_BIND ], &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+ }
+#endif /* HAVE_TLS */
+
+ /* FIXME: add checks on the URL/identity? */
+ /* TODO: would like to count this bind operation for monitoring
+ * too, but where do we get the ldapinfo_t? */
+
+ return ldap_sasl_bind_s( ld,
+ BER_BVISNULL( &lc->lc_cred ) ? "" : lc->lc_bound_ndn.bv_val,
+ LDAP_SASL_SIMPLE, &lc->lc_cred, NULL, NULL, NULL );
+}
+
+/*
+ * ldap_back_default_urllist
+ */
+int
+ldap_back_default_urllist(
+ LDAP *ld,
+ LDAPURLDesc **urllist,
+ LDAPURLDesc **url,
+ void *params )
+{
+ ldapinfo_t *li = (ldapinfo_t *)params;
+ LDAPURLDesc **urltail;
+
+ if ( urllist == url ) {
+ return LDAP_SUCCESS;
+ }
+
+ for ( urltail = &(*url)->lud_next; *urltail; urltail = &(*urltail)->lud_next )
+ /* count */ ;
+
+ *urltail = *urllist;
+ *urllist = *url;
+ *url = NULL;
+
+ if ( !li->li_uri_mutex_do_not_lock ) {
+ ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
+ }
+
+ if ( li->li_uri ) {
+ ch_free( li->li_uri );
+ }
+
+ ldap_get_option( ld, LDAP_OPT_URI, (void *)&li->li_uri );
+
+ if ( !li->li_uri_mutex_do_not_lock ) {
+ ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int
+ldap_back_cancel(
+ ldapconn_t *lc,
+ Operation *op,
+ SlapReply *rs,
+ ber_int_t msgid,
+ ldap_back_send_t sendok )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ /* default behavior */
+ if ( LDAP_BACK_ABANDON( li ) ) {
+ return ldap_abandon_ext( lc->lc_ld, msgid, NULL, NULL );
+ }
+
+ if ( LDAP_BACK_IGNORE( li ) ) {
+ return ldap_pvt_discard( lc->lc_ld, msgid );
+ }
+
+ if ( LDAP_BACK_CANCEL( li ) ) {
+ /* FIXME: asynchronous? */
+ return ldap_cancel_s( lc->lc_ld, msgid, NULL, NULL );
+ }
+
+ assert( 0 );
+
+ return LDAP_OTHER;
+}
+
+int
+ldap_back_op_result(
+ ldapconn_t *lc,
+ Operation *op,
+ SlapReply *rs,
+ ber_int_t msgid,
+ time_t timeout,
+ ldap_back_send_t sendok )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ char *match = NULL;
+ char *text = NULL;
+ char **refs = NULL;
+ LDAPControl **ctrls = NULL;
+
+ rs->sr_text = NULL;
+ rs->sr_matched = NULL;
+ rs->sr_ref = NULL;
+ rs->sr_ctrls = NULL;
+
+ /* if the error recorded in the reply corresponds
+ * to a successful state, get the error from the
+ * remote server response */
+ if ( LDAP_ERR_OK( rs->sr_err ) ) {
+ int rc;
+ struct timeval tv;
+ LDAPMessage *res = NULL;
+ time_t stoptime = (time_t)(-1);
+ int timeout_err = op->o_protocol >= LDAP_VERSION3 ?
+ LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+ const char *timeout_text = "Operation timed out";
+
+ /* if timeout is not specified, compute and use
+ * the one specific to the ongoing operation */
+ if ( timeout == (time_t)(-1) ) {
+ slap_op_t opidx = slap_req2op( op->o_tag );
+
+ if ( opidx == SLAP_OP_SEARCH ) {
+ if ( op->ors_tlimit <= 0 ) {
+ timeout = 0;
+
+ } else {
+ timeout = op->ors_tlimit;
+ timeout_err = LDAP_TIMELIMIT_EXCEEDED;
+ timeout_text = NULL;
+ }
+
+ } else {
+ timeout = li->li_timeout[ opidx ];
+ }
+ }
+
+ /* better than nothing :) */
+ if ( timeout == 0 ) {
+ if ( li->li_idle_timeout ) {
+ timeout = li->li_idle_timeout;
+
+ } else if ( li->li_conn_ttl ) {
+ timeout = li->li_conn_ttl;
+ }
+ }
+
+ if ( timeout ) {
+ stoptime = op->o_time + timeout;
+ }
+
+ LDAP_BACK_TV_SET( &tv );
+
+retry:;
+ /* if result parsing fails, note the failure reason */
+ rc = ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
+ switch ( rc ) {
+ case 0:
+ if ( timeout && slap_get_time() > stoptime ) {
+ if ( sendok & LDAP_BACK_BINDING ) {
+ ldap_unbind_ext( lc->lc_ld, NULL, NULL );
+ lc->lc_ld = NULL;
+
+ /* let it be used, but taint/delete it so that
+ * no-one else can look it up any further */
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, ">>> ldap_back_getconn(timeout)" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+ (void)ldap_back_conn_delete( li, lc );
+ LDAP_BACK_CONN_TAINTED_SET( lc );
+
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, "<<< ldap_back_getconn(timeout)" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+
+ } else {
+ (void)ldap_back_cancel( lc, op, rs, msgid, sendok );
+ }
+ rs->sr_err = timeout_err;
+ rs->sr_text = timeout_text;
+ break;
+ }
+
+ /* timeout == 0 */
+ LDAP_BACK_TV_SET( &tv );
+ ldap_pvt_thread_yield();
+ goto retry;
+
+ case -1:
+ ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER,
+ &rs->sr_err );
+ break;
+
+
+ /* otherwise get the result; if it is not
+ * LDAP_SUCCESS, record it in the reply
+ * structure (this includes
+ * LDAP_COMPARE_{TRUE|FALSE}) */
+ default:
+ /* only touch when activity actually took place... */
+ if ( li->li_idle_timeout ) {
+ lc->lc_time = op->o_time;
+ }
+
+ rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
+ &match, &text, &refs, &ctrls, 1 );
+ if ( rc == LDAP_SUCCESS ) {
+ rs->sr_text = text;
+ } else {
+ rs->sr_err = rc;
+ }
+ rs->sr_err = slap_map_api2result( rs );
+
+ /* RFC 4511: referrals can only appear
+ * if result code is LDAP_REFERRAL */
+ if ( refs != NULL
+ && refs[ 0 ] != NULL
+ && refs[ 0 ][ 0 ] != '\0' )
+ {
+ if ( rs->sr_err != LDAP_REFERRAL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s ldap_back_op_result: "
+ "got referrals with err=%d\n",
+ op->o_log_prefix,
+ rs->sr_err );
+
+ } else {
+ int i;
+
+ for ( i = 0; refs[ i ] != NULL; i++ )
+ /* count */ ;
+ rs->sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( i + 1 ),
+ op->o_tmpmemctx );
+ for ( i = 0; refs[ i ] != NULL; i++ ) {
+ ber_str2bv( refs[ i ], 0, 0, &rs->sr_ref[ i ] );
+ }
+ BER_BVZERO( &rs->sr_ref[ i ] );
+ }
+
+ } else if ( rs->sr_err == LDAP_REFERRAL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s ldap_back_op_result: "
+ "got err=%d with null "
+ "or empty referrals\n",
+ op->o_log_prefix,
+ rs->sr_err );
+
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ }
+
+ if ( ctrls != NULL ) {
+ rs->sr_ctrls = ctrls;
+ }
+ }
+ }
+
+ /* if the error in the reply structure is not
+ * LDAP_SUCCESS, try to map it from client
+ * to server error */
+ if ( !LDAP_ERR_OK( rs->sr_err ) ) {
+ rs->sr_err = slap_map_api2result( rs );
+
+ /* internal ops ( op->o_conn == NULL )
+ * must not reply to client */
+ if ( op->o_conn && !op->o_do_not_cache && match ) {
+
+ /* record the (massaged) matched
+ * DN into the reply structure */
+ rs->sr_matched = match;
+ }
+ }
+
+ if ( rs->sr_err == LDAP_UNAVAILABLE ) {
+ if ( !( sendok & LDAP_BACK_RETRYING ) ) {
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs );
+ }
+ if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
+ if ( rs->sr_text == NULL ) rs->sr_text = "Proxy operation retry failed";
+ send_ldap_result( op, rs );
+ }
+ }
+
+ } else if ( op->o_conn &&
+ ( ( ( sendok & LDAP_BACK_SENDOK ) && LDAP_ERR_OK( rs->sr_err ) )
+ || ( ( sendok & LDAP_BACK_SENDERR ) && !LDAP_ERR_OK( rs->sr_err ) ) ) )
+ {
+ send_ldap_result( op, rs );
+ }
+
+ if ( text ) {
+ ldap_memfree( text );
+ }
+ rs->sr_text = NULL;
+
+ /* there can't be refs with a (successful) bind */
+ if ( rs->sr_ref ) {
+ op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
+ rs->sr_ref = NULL;
+ }
+
+ if ( refs ) {
+ ber_memvfree( (void **)refs );
+ }
+
+ /* match should not be possible with a successful bind */
+ if ( match ) {
+ if ( rs->sr_matched != match ) {
+ free( (char *)rs->sr_matched );
+ }
+ rs->sr_matched = NULL;
+ ldap_memfree( match );
+ }
+
+ if ( ctrls != NULL ) {
+ if ( op->o_tag == LDAP_REQ_BIND && rs->sr_err == LDAP_SUCCESS ) {
+ int i;
+
+ for ( i = 0; ctrls[i] != NULL; i++ );
+
+ rs->sr_ctrls = op->o_tmpalloc( sizeof( LDAPControl * )*( i + 1 ),
+ op->o_tmpmemctx );
+ for ( i = 0; ctrls[ i ] != NULL; i++ ) {
+ char *ptr;
+ ber_len_t oidlen = strlen( ctrls[i]->ldctl_oid );
+ ber_len_t size = sizeof( LDAPControl )
+ + oidlen + 1
+ + ctrls[i]->ldctl_value.bv_len + 1;
+
+ rs->sr_ctrls[ i ] = op->o_tmpalloc( size, op->o_tmpmemctx );
+ rs->sr_ctrls[ i ]->ldctl_oid = (char *)&rs->sr_ctrls[ i ][ 1 ];
+ lutil_strcopy( rs->sr_ctrls[ i ]->ldctl_oid, ctrls[i]->ldctl_oid );
+ rs->sr_ctrls[ i ]->ldctl_value.bv_val
+ = (char *)&rs->sr_ctrls[ i ]->ldctl_oid[oidlen + 1];
+ rs->sr_ctrls[ i ]->ldctl_value.bv_len
+ = ctrls[i]->ldctl_value.bv_len;
+ ptr = lutil_memcopy( rs->sr_ctrls[ i ]->ldctl_value.bv_val,
+ ctrls[i]->ldctl_value.bv_val, ctrls[i]->ldctl_value.bv_len );
+ *ptr = '\0';
+ }
+ rs->sr_ctrls[ i ] = NULL;
+ rs->sr_flags |= REP_CTRLS_MUSTBEFREED;
+
+ } else {
+ assert( rs->sr_ctrls != NULL );
+ rs->sr_ctrls = NULL;
+ }
+
+ ldap_controls_free( ctrls );
+ }
+
+ return( LDAP_ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err );
+}
+
+/* return true if bound, false if failed */
+int
+ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+ int rc = 0;
+
+ assert( lcp != NULL );
+ assert( *lcp != NULL );
+
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+
+ if ( (*lcp)->lc_refcnt == 1 ) {
+ int binding = LDAP_BACK_CONN_BINDING( *lcp );
+
+ ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
+ Debug( LDAP_DEBUG_ANY,
+ "%s ldap_back_retry: retrying URI=\"%s\" DN=\"%s\"\n",
+ op->o_log_prefix, li->li_uri,
+ BER_BVISNULL( &(*lcp)->lc_bound_ndn ) ?
+ "" : (*lcp)->lc_bound_ndn.bv_val );
+ ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
+
+ ldap_unbind_ext( (*lcp)->lc_ld, NULL, NULL );
+ (*lcp)->lc_ld = NULL;
+ LDAP_BACK_CONN_ISBOUND_CLEAR( (*lcp) );
+
+ /* lc here must be the regular lc, reset and ready for init */
+ rc = ldap_back_prepare_conn( *lcp, op, rs, sendok );
+ if ( rc != LDAP_SUCCESS ) {
+ /* freeit, because lc_refcnt == 1 */
+ (*lcp)->lc_refcnt = 0;
+ (void)ldap_back_freeconn( li, *lcp, 0 );
+ *lcp = NULL;
+ rc = 0;
+
+ } else if ( ( sendok & LDAP_BACK_BINDING ) ) {
+ if ( binding ) {
+ LDAP_BACK_CONN_BINDING_SET( *lcp );
+ }
+ rc = 1;
+
+ } else {
+ rc = ldap_back_dobind_int( lcp, op, rs, sendok, 0, 0 );
+ if ( rc == 0 && *lcp != NULL ) {
+ /* freeit, because lc_refcnt == 1 */
+ (*lcp)->lc_refcnt = 0;
+ (void)ldap_back_freeconn( li, *lcp, 0 );
+ *lcp = NULL;
+ }
+ }
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_back_retry: conn %p refcnt=%u unable to retry.\n",
+ (void *)(*lcp), (*lcp)->lc_refcnt );
+
+ LDAP_BACK_CONN_TAINTED_SET( *lcp );
+ ldap_back_release_conn_lock( li, lcp, 0 );
+ assert( *lcp == NULL );
+
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ rs->sr_err = LDAP_UNAVAILABLE;
+ rs->sr_text = "Unable to retry";
+ send_ldap_result( op, rs );
+ }
+ }
+
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+
+ return rc;
+}
+
+static int
+ldap_back_is_proxy_authz( Operation *op, SlapReply *rs, ldap_back_send_t sendok,
+ struct berval *binddn, struct berval *bindcred )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+ struct berval ndn;
+ int dobind = 0;
+
+ if ( op->o_conn == NULL || op->o_do_not_cache ) {
+ goto done;
+ }
+
+ /* don't proxyAuthz if protocol is not LDAPv3 */
+ switch ( li->li_version ) {
+ case LDAP_VERSION3:
+ break;
+
+ case 0:
+ if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
+ break;
+ }
+ /* fall thru */
+
+ default:
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ dobind = -1;
+ }
+ goto done;
+ }
+
+ /* safe default */
+ *binddn = slap_empty_bv;
+ *bindcred = slap_empty_bv;
+
+ if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
+ ndn = op->o_conn->c_ndn;
+
+ } else {
+ ndn = op->o_ndn;
+ }
+
+ if ( !( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE )) {
+ if ( op->o_tag == LDAP_REQ_BIND && ( sendok & LDAP_BACK_SENDERR )) {
+ if ( !BER_BVISEMPTY( &ndn )) {
+ dobind = 0;
+ goto done;
+ }
+ } else if ( SLAP_IS_AUTHZ_BACKEND( op )) {
+ dobind = 0;
+ goto done;
+ }
+ }
+
+ switch ( li->li_idassert_mode ) {
+ case LDAP_BACK_IDASSERT_LEGACY:
+ if ( !BER_BVISNULL( &ndn ) && !BER_BVISEMPTY( &ndn ) ) {
+ if ( !BER_BVISNULL( &li->li_idassert_authcDN ) && !BER_BVISEMPTY( &li->li_idassert_authcDN ) )
+ {
+ *binddn = li->li_idassert_authcDN;
+ *bindcred = li->li_idassert_passwd;
+ dobind = 1;
+ }
+ }
+ break;
+
+ default:
+ /* NOTE: rootdn can always idassert */
+ if ( BER_BVISNULL( &ndn )
+ && li->li_idassert_authz == NULL
+ && !( li->li_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) )
+ {
+ if ( li->li_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
+ rs->sr_err = LDAP_INAPPROPRIATE_AUTH;
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ dobind = -1;
+ }
+
+ } else {
+ rs->sr_err = LDAP_SUCCESS;
+ *binddn = slap_empty_bv;
+ *bindcred = slap_empty_bv;
+ break;
+ }
+
+ goto done;
+
+ } else if ( !be_isroot( op ) ) {
+ if ( li->li_idassert_passthru ) {
+ struct berval authcDN;
+
+ if ( BER_BVISNULL( &ndn ) ) {
+ authcDN = slap_empty_bv;
+
+ } else {
+ authcDN = ndn;
+ }
+ rs->sr_err = slap_sasl_matches( op, li->li_idassert_passthru,
+ &authcDN, &authcDN );
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ dobind = 0;
+ break;
+ }
+ }
+
+ if ( li->li_idassert_authz ) {
+ struct berval authcDN;
+
+ if ( BER_BVISNULL( &ndn ) ) {
+ authcDN = slap_empty_bv;
+
+ } else {
+ authcDN = ndn;
+ }
+ rs->sr_err = slap_sasl_matches( op, li->li_idassert_authz,
+ &authcDN, &authcDN );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ if ( li->li_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ dobind = -1;
+ }
+
+ } else {
+ rs->sr_err = LDAP_SUCCESS;
+ *binddn = slap_empty_bv;
+ *bindcred = slap_empty_bv;
+ break;
+ }
+
+ goto done;
+ }
+ }
+ }
+
+ *binddn = li->li_idassert_authcDN;
+ *bindcred = li->li_idassert_passwd;
+ dobind = 1;
+ break;
+ }
+
+done:;
+ return dobind;
+}
+
+static int
+ldap_back_proxy_authz_bind(
+ ldapconn_t *lc,
+ Operation *op,
+ SlapReply *rs,
+ ldap_back_send_t sendok,
+ struct berval *binddn,
+ struct berval *bindcred )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+ struct berval ndn;
+ int msgid;
+ int rc;
+
+ if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
+ ndn = op->o_conn->c_ndn;
+
+ } else {
+ ndn = op->o_ndn;
+ }
+
+ if ( li->li_idassert_authmethod == LDAP_AUTH_SASL ) {
+#ifdef HAVE_CYRUS_SASL
+ void *defaults = NULL;
+ struct berval authzID = BER_BVNULL;
+ int freeauthz = 0;
+ LDAPControl **ctrlsp = NULL;
+ LDAPMessage *result = NULL;
+ const char *rmech = NULL;
+ const char *save_text = rs->sr_text;
+
+#ifdef SLAP_AUTH_DN
+ LDAPControl ctrl, *ctrls[2];
+ int msgid;
+#endif /* SLAP_AUTH_DN */
+
+ /* if SASL supports native authz, prepare for it */
+ if ( ( !op->o_do_not_cache || !op->o_is_auth_check ) &&
+ ( li->li_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
+ {
+ switch ( li->li_idassert_mode ) {
+ case LDAP_BACK_IDASSERT_OTHERID:
+ case LDAP_BACK_IDASSERT_OTHERDN:
+ authzID = li->li_idassert_authzID;
+ break;
+
+ case LDAP_BACK_IDASSERT_ANONYMOUS:
+ BER_BVSTR( &authzID, "dn:" );
+ break;
+
+ case LDAP_BACK_IDASSERT_SELF:
+ if ( BER_BVISNULL( &ndn ) ) {
+ /* connection is not authc'd, so don't idassert */
+ BER_BVSTR( &authzID, "dn:" );
+ break;
+ }
+ authzID.bv_len = STRLENOF( "dn:" ) + ndn.bv_len;
+ authzID.bv_val = slap_sl_malloc( authzID.bv_len + 1, op->o_tmpmemctx );
+ AC_MEMCPY( authzID.bv_val, "dn:", STRLENOF( "dn:" ) );
+ AC_MEMCPY( authzID.bv_val + STRLENOF( "dn:" ),
+ ndn.bv_val, ndn.bv_len + 1 );
+ freeauthz = 1;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if ( li->li_idassert_secprops != NULL ) {
+ rs->sr_err = ldap_set_option( lc->lc_ld,
+ LDAP_OPT_X_SASL_SECPROPS,
+ (void *)li->li_idassert_secprops );
+
+ if ( rs->sr_err != LDAP_OPT_SUCCESS ) {
+ rs->sr_err = LDAP_OTHER;
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+ goto done;
+ }
+ }
+
+ defaults = lutil_sasl_defaults( lc->lc_ld,
+ li->li_idassert_sasl_mech.bv_val,
+ li->li_idassert_sasl_realm.bv_val,
+ li->li_idassert_authcID.bv_val,
+ li->li_idassert_passwd.bv_val,
+ authzID.bv_val );
+ if ( defaults == NULL ) {
+ rs->sr_err = LDAP_OTHER;
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ goto done;
+ }
+
+#ifdef SLAP_AUTH_DN
+ if ( li->li_idassert_flags & LDAP_BACK_AUTH_DN_AUTHZID ) {
+ assert( BER_BVISNULL( binddn ) );
+
+ ctrl.ldctl_oid = LDAP_CONTROL_AUTHZID_REQUEST;
+ ctrl.ldctl_iscritical = 0;
+ BER_BVZERO( &ctrl.ldctl_value );
+ ctrls[0] = &ctrl;
+ ctrls[1] = NULL;
+ ctrlsp = ctrls;
+ }
+#endif /* SLAP_AUTH_DN */
+
+ do {
+ rs->sr_err = ldap_sasl_interactive_bind( lc->lc_ld, binddn->bv_val,
+ li->li_idassert_sasl_mech.bv_val,
+ ctrlsp, NULL, LDAP_SASL_QUIET, lutil_sasl_interact, defaults,
+ result, &rmech, &msgid );
+
+ if ( rs->sr_err != LDAP_SASL_BIND_IN_PROGRESS )
+ break;
+
+ ldap_msgfree( result );
+
+ if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1 || !result ) {
+ ldap_get_option( lc->lc_ld, LDAP_OPT_RESULT_CODE, (void*)&rs->sr_err );
+ ldap_get_option( lc->lc_ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*)&rs->sr_text );
+ break;
+ }
+ } while ( rs->sr_err == LDAP_SASL_BIND_IN_PROGRESS );
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_BIND ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+ switch ( rs->sr_err ) {
+ case LDAP_SUCCESS:
+#ifdef SLAP_AUTH_DN
+ /* FIXME: right now, the only reason to check
+ * response controls is RFC 3829 authzid */
+ if ( li->li_idassert_flags & LDAP_BACK_AUTH_DN_AUTHZID ) {
+ ctrlsp = NULL;
+ rc = ldap_parse_result( lc->lc_ld, result, NULL, NULL, NULL, NULL,
+ &ctrlsp, 0 );
+ if ( rc == LDAP_SUCCESS && ctrlsp ) {
+ LDAPControl *ctrl;
+
+ ctrl = ldap_control_find( LDAP_CONTROL_AUTHZID_RESPONSE,
+ ctrlsp, NULL );
+ if ( ctrl ) {
+ Debug( LDAP_DEBUG_TRACE, "%s: ldap_back_proxy_authz_bind: authzID=\"%s\" (authzid)\n",
+ op->o_log_prefix, ctrl->ldctl_value.bv_val );
+ if ( ctrl->ldctl_value.bv_len > STRLENOF("dn:") &&
+ strncasecmp( ctrl->ldctl_value.bv_val, "dn:", STRLENOF("dn:") ) == 0 )
+ {
+ struct berval bv;
+ bv.bv_val = &ctrl->ldctl_value.bv_val[STRLENOF("dn:")];
+ bv.bv_len = ctrl->ldctl_value.bv_len - STRLENOF("dn:");
+ ber_bvreplace( &lc->lc_bound_ndn, &bv );
+ }
+ }
+ }
+
+ ldap_controls_free( ctrlsp );
+
+ } else if ( li->li_idassert_flags & LDAP_BACK_AUTH_DN_WHOAMI ) {
+ struct berval *val = NULL;
+ rc = ldap_whoami_s( lc->lc_ld, &val, NULL, NULL );
+ if ( rc == LDAP_SUCCESS && val != NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "%s: ldap_back_proxy_authz_bind: authzID=\"%s\" (whoami)\n",
+ op->o_log_prefix, val->bv_val );
+ if ( val->bv_len > STRLENOF("dn:") &&
+ strncasecmp( val->bv_val, "dn:", STRLENOF("dn:") ) == 0 )
+ {
+ struct berval bv;
+ bv.bv_val = &val->bv_val[STRLENOF("dn:")];
+ bv.bv_len = val->bv_len - STRLENOF("dn:");
+ ber_bvreplace( &lc->lc_bound_ndn, &bv );
+ }
+ ber_bvfree( val );
+ }
+ }
+
+ if ( ( li->li_idassert_flags & LDAP_BACK_AUTH_DN_MASK ) &&
+ BER_BVISNULL( &lc->lc_bound_ndn ) )
+ {
+ /* all in all, we only need it to be non-null */
+ /* FIXME: should this be configurable? */
+ static struct berval bv = BER_BVC("cn=authzdn");
+ ber_bvreplace( &lc->lc_bound_ndn, &bv );
+ }
+#endif /* SLAP_AUTH_DN */
+ LDAP_BACK_CONN_ISBOUND_SET( lc );
+ break;
+
+ case LDAP_LOCAL_ERROR:
+ /* list client API error codes that require
+ * to taint the connection */
+ /* FIXME: should actually retry? */
+ LDAP_BACK_CONN_TAINTED_SET( lc );
+
+ /* fallthru */
+
+ default:
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+ rs->sr_err = slap_map_api2result( rs );
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ break;
+ }
+
+ if ( save_text != rs->sr_text ) {
+ ldap_memfree( (char *)rs->sr_text );
+ rs->sr_text = save_text;
+ }
+
+ ldap_msgfree( result );
+
+ lutil_sasl_freedefs( defaults );
+ if ( freeauthz ) {
+ slap_sl_free( authzID.bv_val, op->o_tmpmemctx );
+ }
+
+ goto done;
+#endif /* HAVE_CYRUS_SASL */
+ }
+
+ switch ( li->li_idassert_authmethod ) {
+ case LDAP_AUTH_NONE:
+ /* FIXME: do we really need this? */
+ BER_BVSTR( binddn, "" );
+ BER_BVSTR( bindcred, "" );
+ /* fallthru */
+
+ case LDAP_AUTH_SIMPLE:
+ rs->sr_err = ldap_sasl_bind( lc->lc_ld,
+ binddn->bv_val, LDAP_SASL_SIMPLE,
+ bindcred, NULL, NULL, &msgid );
+ rc = ldap_back_op_result( lc, op, rs, msgid,
+ -1, ( sendok | LDAP_BACK_BINDING ) );
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_BIND ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+ break;
+
+ default:
+ /* unsupported! */
+ LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
+ rs->sr_err = LDAP_AUTH_METHOD_NOT_SUPPORTED;
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ goto done;
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ /* set rebind stuff in case of successful proxyAuthz bind,
+ * so that referral chasing is attempted using the right
+ * identity */
+ LDAP_BACK_CONN_ISBOUND_SET( lc );
+ if ( !BER_BVISNULL( binddn ) ) {
+ ber_bvreplace( &lc->lc_bound_ndn, binddn );
+ }
+
+ if ( !BER_BVISNULL( &lc->lc_cred ) ) {
+ memset( lc->lc_cred.bv_val, 0,
+ lc->lc_cred.bv_len );
+ }
+
+ if ( LDAP_BACK_SAVECRED( li ) ) {
+ if ( !BER_BVISNULL( bindcred ) ) {
+ ber_bvreplace( &lc->lc_cred, bindcred );
+ ldap_set_rebind_proc( lc->lc_ld, li->li_rebind_f, lc );
+ }
+
+ } else {
+ lc->lc_cred.bv_len = 0;
+ }
+ }
+
+done:;
+ return LDAP_BACK_CONN_ISBOUND( lc );
+}
+
+/*
+ * ldap_back_proxy_authz_ctrl() prepends a proxyAuthz control
+ * to existing server-side controls if required; if not,
+ * the existing server-side controls are placed in *pctrls.
+ * The caller, after using the controls in client API
+ * operations, if ( *pctrls != op->o_ctrls ), should
+ * free( (*pctrls)[ 0 ] ) and free( *pctrls ).
+ * The function returns success if the control could
+ * be added if required, or if it did nothing; in the future,
+ * it might return some error if it failed.
+ *
+ * if no bind took place yet, but the connection is bound
+ * and the "proxyauthzdn" is set, then bind as "proxyauthzdn"
+ * and explicitly add proxyAuthz the control to every operation
+ * with the dn bound to the connection as control value.
+ *
+ * If no server-side controls are defined for the operation,
+ * simply add the proxyAuthz control; otherwise, if the
+ * proxyAuthz control is not already set, add it as
+ * the first one
+ *
+ * FIXME: is controls order significant for security?
+ * ANSWER: controls ordering and interoperability
+ * must be indicated by the specs of each control; if none
+ * is specified, the order is irrelevant.
+ */
+int
+ldap_back_proxy_authz_ctrl(
+ Operation *op,
+ SlapReply *rs,
+ struct berval *bound_ndn,
+ int version,
+ slap_idassert_t *si,
+ LDAPControl *ctrl )
+{
+ slap_idassert_mode_t mode;
+ struct berval assertedID,
+ ndn;
+ int isroot = 0;
+
+ rs->sr_err = SLAP_CB_CONTINUE;
+
+ /* FIXME: SASL/EXTERNAL over ldapi:// doesn't honor the authcID,
+ * but if it is not set this test fails. We need a different
+ * means to detect if idassert is enabled */
+ if ( ( BER_BVISNULL( &si->si_bc.sb_authcId ) || BER_BVISEMPTY( &si->si_bc.sb_authcId ) )
+ && ( BER_BVISNULL( &si->si_bc.sb_binddn ) || BER_BVISEMPTY( &si->si_bc.sb_binddn ) )
+ && BER_BVISNULL( &si->si_bc.sb_saslmech ) )
+ {
+ goto done;
+ }
+
+ if ( !op->o_conn || op->o_do_not_cache || ( isroot = be_isroot( op ) ) ) {
+ goto done;
+ }
+
+ if ( op->o_tag == LDAP_REQ_BIND ) {
+ ndn = op->o_req_ndn;
+ } else {
+ ndn = op->o_ndn;
+ }
+
+ if ( si->si_mode == LDAP_BACK_IDASSERT_LEGACY ) {
+ if ( op->o_proxy_authz ) {
+ /*
+ * FIXME: we do not want to perform proxyAuthz
+ * on behalf of the client, because this would
+ * be performed with "proxyauthzdn" privileges.
+ *
+ * This might actually be too strict, since
+ * the "proxyauthzdn" authzTo, and each entry's
+ * authzFrom attributes may be crafted
+ * to avoid unwanted proxyAuthz to take place.
+ */
+#if 0
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "proxyAuthz not allowed within namingContext";
+#endif
+ goto done;
+ }
+
+ if ( !BER_BVISNULL( bound_ndn ) ) {
+ goto done;
+ }
+
+ if ( BER_BVISNULL( &ndn ) ) {
+ goto done;
+ }
+
+ if ( BER_BVISNULL( &si->si_bc.sb_binddn ) ) {
+ goto done;
+ }
+
+ } else if ( si->si_bc.sb_method == LDAP_AUTH_SASL ) {
+ if ( ( si->si_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
+ {
+ /* already asserted in SASL via native authz */
+ goto done;
+ }
+
+ } else if ( si->si_authz && !isroot ) {
+ int rc;
+ struct berval authcDN;
+
+ if ( BER_BVISNULL( &ndn ) ) {
+ authcDN = slap_empty_bv;
+ } else {
+ authcDN = ndn;
+ }
+ rc = slap_sasl_matches( op, si->si_authz,
+ &authcDN, &authcDN );
+ if ( rc != LDAP_SUCCESS ) {
+ if ( si->si_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
+ /* ndn is not authorized
+ * to use idassert */
+ rs->sr_err = rc;
+ }
+ goto done;
+ }
+ }
+
+ if ( op->o_proxy_authz ) {
+ /*
+ * FIXME: we can:
+ * 1) ignore the already set proxyAuthz control
+ * 2) leave it in place, and don't set ours
+ * 3) add both
+ * 4) reject the operation
+ *
+ * option (4) is very drastic
+ * option (3) will make the remote server reject
+ * the operation, thus being equivalent to (4)
+ * option (2) will likely break the idassert
+ * assumptions, so we cannot accept it;
+ * option (1) means that we are contradicting
+ * the client's request.
+ *
+ * I think (4) is the only correct choice.
+ */
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "proxyAuthz not allowed within namingContext";
+ }
+
+ if ( op->o_is_auth_check ) {
+ mode = LDAP_BACK_IDASSERT_NOASSERT;
+
+ } else {
+ mode = si->si_mode;
+ }
+
+ switch ( mode ) {
+ case LDAP_BACK_IDASSERT_LEGACY:
+ /* original behavior:
+ * assert the client's identity */
+ case LDAP_BACK_IDASSERT_SELF:
+ assertedID = ndn;
+ break;
+
+ case LDAP_BACK_IDASSERT_ANONYMOUS:
+ /* assert "anonymous" */
+ assertedID = slap_empty_bv;
+ break;
+
+ case LDAP_BACK_IDASSERT_NOASSERT:
+ /* don't assert; bind as proxyauthzdn */
+ goto done;
+
+ case LDAP_BACK_IDASSERT_OTHERID:
+ case LDAP_BACK_IDASSERT_OTHERDN:
+ /* assert idassert DN */
+ assertedID = si->si_bc.sb_authzId;
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+ /* if we got here, "" is allowed to proxyAuthz */
+ if ( BER_BVISNULL( &assertedID ) ) {
+ assertedID = slap_empty_bv;
+ }
+
+ /* don't idassert the bound DN (ITS#4497) */
+ if ( dn_match( &assertedID, bound_ndn ) ) {
+ goto done;
+ }
+
+ ctrl->ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
+ ctrl->ldctl_iscritical = ( ( si->si_flags & LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL ) == LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL );
+
+ switch ( si->si_mode ) {
+ /* already in u:ID or dn:DN form */
+ case LDAP_BACK_IDASSERT_OTHERID:
+ case LDAP_BACK_IDASSERT_OTHERDN:
+ ber_dupbv_x( &ctrl->ldctl_value, &assertedID, op->o_tmpmemctx );
+ rs->sr_err = LDAP_SUCCESS;
+ break;
+
+ /* needs the dn: prefix */
+ default:
+ ctrl->ldctl_value.bv_len = assertedID.bv_len + STRLENOF( "dn:" );
+ ctrl->ldctl_value.bv_val = op->o_tmpalloc( ctrl->ldctl_value.bv_len + 1,
+ op->o_tmpmemctx );
+ AC_MEMCPY( ctrl->ldctl_value.bv_val, "dn:", STRLENOF( "dn:" ) );
+ AC_MEMCPY( &ctrl->ldctl_value.bv_val[ STRLENOF( "dn:" ) ],
+ assertedID.bv_val, assertedID.bv_len + 1 );
+ rs->sr_err = LDAP_SUCCESS;
+ break;
+ }
+
+ /* Older versions of <draft-weltman-ldapv3-proxy> required
+ * to encode the value of the authzID (and called it proxyDN);
+ * this hack provides compatibility with those DSAs that
+ * implement it this way */
+ if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) {
+ struct berval authzID = ctrl->ldctl_value;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+
+ ber_init2( ber, 0, LBER_USE_DER );
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+
+ tag = ber_printf( ber, "O", &authzID );
+ if ( tag == LBER_ERROR ) {
+ rs->sr_err = LDAP_OTHER;
+ goto free_ber;
+ }
+
+ if ( ber_flatten2( ber, &ctrl->ldctl_value, 1 ) == -1 ) {
+ rs->sr_err = LDAP_OTHER;
+ goto free_ber;
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+
+free_ber:;
+ op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx );
+ ber_free_buf( ber );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ } else if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) {
+ struct berval authzID = ctrl->ldctl_value,
+ tmp;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+
+ if ( strncasecmp( authzID.bv_val, "dn:", STRLENOF( "dn:" ) ) != 0 ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ tmp = authzID;
+ tmp.bv_val += STRLENOF( "dn:" );
+ tmp.bv_len -= STRLENOF( "dn:" );
+
+ ber_init2( ber, 0, LBER_USE_DER );
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+
+ /* apparently, Mozilla API encodes this
+ * as "SEQUENCE { LDAPDN }" */
+ tag = ber_printf( ber, "{O}", &tmp );
+ if ( tag == LBER_ERROR ) {
+ rs->sr_err = LDAP_OTHER;
+ goto free_ber2;
+ }
+
+ if ( ber_flatten2( ber, &ctrl->ldctl_value, 1 ) == -1 ) {
+ rs->sr_err = LDAP_OTHER;
+ goto free_ber2;
+ }
+
+ ctrl->ldctl_oid = LDAP_CONTROL_OBSOLETE_PROXY_AUTHZ;
+ rs->sr_err = LDAP_SUCCESS;
+
+free_ber2:;
+ op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx );
+ ber_free_buf( ber );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto done;
+ }
+ }
+
+done:;
+
+ return rs->sr_err;
+}
+
+/*
+ * Add controls;
+ *
+ * if any needs to be added, it is prepended to existing ones,
+ * in a newly allocated array. The companion function
+ * ldap_back_controls_free() must be used to restore the original
+ * status of op->o_ctrls.
+ */
+int
+ldap_back_controls_add(
+ Operation *op,
+ SlapReply *rs,
+ ldapconn_t *lc,
+ LDAPControl ***pctrls )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ LDAPControl **ctrls = NULL;
+ /* set to the maximum number of controls this backend can add */
+ LDAPControl c[ 2 ] = { { 0 } };
+ int n = 0, i, j1 = 0, j2 = 0, skipped = 0;
+
+ *pctrls = NULL;
+
+ rs->sr_err = LDAP_SUCCESS;
+
+ /* don't add controls if protocol is not LDAPv3 */
+ switch ( li->li_version ) {
+ case LDAP_VERSION3:
+ break;
+
+ case 0:
+ if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
+ break;
+ }
+ /* fall thru */
+
+ default:
+ goto done;
+ }
+
+ /* put controls that go __before__ existing ones here */
+
+ /* proxyAuthz for identity assertion */
+ switch ( ldap_back_proxy_authz_ctrl( op, rs, &lc->lc_bound_ndn,
+ li->li_version, &li->li_idassert, &c[ j1 ] ) )
+ {
+ case SLAP_CB_CONTINUE:
+ break;
+
+ case LDAP_SUCCESS:
+ j1++;
+ break;
+
+ default:
+ goto done;
+ }
+
+ /* put controls that go __after__ existing ones here */
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ /* FIXME: according to <draft-wahl-ldap-session>,
+ * the server should check if the control can be added
+ * based on the identity of the client and so */
+
+ /* session tracking */
+ if ( LDAP_BACK_ST_REQUEST( li ) ) {
+ switch ( slap_ctrl_session_tracking_request_add( op, rs, &c[ j1 + j2 ] ) ) {
+ case SLAP_CB_CONTINUE:
+ break;
+
+ case LDAP_SUCCESS:
+ j2++;
+ break;
+
+ default:
+ goto done;
+ }
+ }
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+ if ( rs->sr_err == SLAP_CB_CONTINUE ) {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+
+ /* if nothing to do, just bail out */
+ if ( j1 == 0 && j2 == 0 ) {
+ goto done;
+ }
+
+ assert( j1 + j2 <= (int) (sizeof( c )/sizeof( c[0] )) );
+
+ if ( op->o_ctrls ) {
+ for ( n = 0; op->o_ctrls[ n ]; n++ )
+ /* just count ctrls */ ;
+ }
+
+ ctrls = op->o_tmpalloc( (n + j1 + j2 + 1) * sizeof( LDAPControl * ) + ( j1 + j2 ) * sizeof( LDAPControl ),
+ op->o_tmpmemctx );
+ if ( j1 ) {
+ ctrls[ 0 ] = (LDAPControl *)&ctrls[ n + j1 + j2 + 1 ];
+ *ctrls[ 0 ] = c[ 0 ];
+ for ( i = 1; i < j1; i++ ) {
+ ctrls[ i ] = &ctrls[ 0 ][ i ];
+ *ctrls[ i ] = c[ i ];
+ }
+ }
+
+ i = 0;
+ if ( op->o_ctrls ) {
+ LDAPControl *proxyauthz = ldap_control_find(
+ LDAP_CONTROL_PROXY_AUTHZ, op->o_ctrls, NULL );
+
+ for ( i = 0; op->o_ctrls[ i ]; i++ ) {
+ /* Only replace it if we generated one */
+ if ( j1 && proxyauthz && proxyauthz == op->o_ctrls[ i ] ) {
+ /* Frontend has already checked only one is present */
+ assert( skipped == 0 );
+ skipped++;
+ continue;
+ }
+ ctrls[ i + j1 - skipped ] = op->o_ctrls[ i ];
+ }
+ }
+
+ n += j1 - skipped;
+ if ( j2 ) {
+ ctrls[ n ] = (LDAPControl *)&ctrls[ n + j2 + 1 ] + j1;
+ *ctrls[ n ] = c[ j1 ];
+ for ( i = 1; i < j2; i++ ) {
+ ctrls[ n + i ] = &ctrls[ n ][ i ];
+ *ctrls[ n + i ] = c[ i ];
+ }
+ }
+
+ ctrls[ n + j2 ] = NULL;
+
+done:;
+ if ( ctrls == NULL ) {
+ ctrls = op->o_ctrls;
+ }
+
+ *pctrls = ctrls;
+
+ return rs->sr_err;
+}
+
+int
+ldap_back_controls_free( Operation *op, SlapReply *rs, LDAPControl ***pctrls )
+{
+ LDAPControl **ctrls = *pctrls;
+
+ /* we assume that the controls added by the proxy come first,
+ * so as soon as we find op->o_ctrls[ 0 ] we can stop */
+ if ( ctrls && ctrls != op->o_ctrls ) {
+ int i = 0, n = 0, n_added;
+ LDAPControl *lower, *upper;
+
+ assert( ctrls[ 0 ] != NULL );
+
+ for ( n = 0; ctrls[ n ] != NULL; n++ )
+ /* count 'em */ ;
+
+ if ( op->o_ctrls ) {
+ for ( i = 0; op->o_ctrls[ i ] != NULL; i++ )
+ /* count 'em */ ;
+ }
+
+ n_added = n - i;
+ lower = (LDAPControl *)&ctrls[ n ];
+ upper = &lower[ n_added ];
+
+ for ( i = 0; ctrls[ i ] != NULL; i++ ) {
+ if ( ctrls[ i ] < lower || ctrls[ i ] >= upper ) {
+ /* original; don't touch */
+ continue;
+ }
+
+ if ( !BER_BVISNULL( &ctrls[ i ]->ldctl_value ) ) {
+ op->o_tmpfree( ctrls[ i ]->ldctl_value.bv_val, op->o_tmpmemctx );
+ }
+ }
+
+ op->o_tmpfree( ctrls, op->o_tmpmemctx );
+ }
+
+ *pctrls = NULL;
+
+ return 0;
+}
+
+int
+ldap_back_conn2str( const ldapconn_base_t *lc, char *buf, ber_len_t buflen )
+{
+ char tbuf[ SLAP_TEXT_BUFLEN ];
+ char *ptr = buf, *end = buf + buflen;
+ int len;
+
+ if ( ptr + sizeof("conn=") > end ) return -1;
+ ptr = lutil_strcopy( ptr, "conn=" );
+
+ len = ldap_back_connid2str( lc, ptr, (ber_len_t)(end - ptr) );
+ ptr += len;
+ if ( ptr >= end ) return -1;
+
+ if ( !BER_BVISNULL( &lc->lcb_local_ndn ) ) {
+ if ( ptr + sizeof(" DN=\"\"") + lc->lcb_local_ndn.bv_len > end ) return -1;
+ ptr = lutil_strcopy( ptr, " DN=\"" );
+ ptr = lutil_strncopy( ptr, lc->lcb_local_ndn.bv_val, lc->lcb_local_ndn.bv_len );
+ *ptr++ = '"';
+ }
+
+ if ( lc->lcb_create_time != 0 ) {
+ len = snprintf( tbuf, sizeof(tbuf), "%ld", lc->lcb_create_time );
+ if ( ptr + sizeof(" created=") + len >= end ) return -1;
+ ptr = lutil_strcopy( ptr, " created=" );
+ ptr = lutil_strcopy( ptr, tbuf );
+ }
+
+ if ( lc->lcb_time != 0 ) {
+ len = snprintf( tbuf, sizeof(tbuf), "%ld", lc->lcb_time );
+ if ( ptr + sizeof(" modified=") + len >= end ) return -1;
+ ptr = lutil_strcopy( ptr, " modified=" );
+ ptr = lutil_strcopy( ptr, tbuf );
+ }
+
+ len = snprintf( tbuf, sizeof(tbuf), "%u", lc->lcb_refcnt );
+ if ( ptr + sizeof(" refcnt=") + len >= end ) return -1;
+ ptr = lutil_strcopy( ptr, " refcnt=" );
+ ptr = lutil_strcopy( ptr, tbuf );
+
+ return ptr - buf;
+}
+
+int
+ldap_back_connid2str( const ldapconn_base_t *lc, char *buf, ber_len_t buflen )
+{
+ static struct berval conns[] = {
+ BER_BVC("ROOTDN"),
+ BER_BVC("ROOTDN-TLS"),
+ BER_BVC("ANON"),
+ BER_BVC("ANON-TLS"),
+ BER_BVC("BIND"),
+ BER_BVC("BIND-TLS"),
+ BER_BVNULL
+ };
+
+ int len = 0;
+
+ if ( LDAP_BACK_PCONN_ISPRIV( (const ldapconn_t *)lc ) ) {
+ long cid;
+ struct berval *bv;
+
+ cid = (long)lc->lcb_conn;
+ assert( cid >= LDAP_BACK_PCONN_FIRST && cid < LDAP_BACK_PCONN_LAST );
+
+ bv = &conns[ cid ];
+
+ if ( bv->bv_len >= buflen ) {
+ return bv->bv_len + 1;
+ }
+
+ len = bv->bv_len;
+ lutil_strncopy( buf, bv->bv_val, bv->bv_len + 1 );
+
+ } else {
+ len = snprintf( buf, buflen, "%lu", lc->lcb_conn->c_connid );
+ }
+
+ return len;
+}
+
+void *
+ldap_back_conn_expire_fn( void *ctx, void *arg )
+{
+ struct re_s *rtask = arg;
+ ldapinfo_t *li = (ldapinfo_t *)rtask->arg;
+ ldap_back_conn_prune( li );
+
+ return NULL;
+}
+
+/* Pick which expires first: connection TTL or idle timeout */
+static time_t
+ldap_back_conn_expire_time( ldapinfo_t *li, ldapconn_t *lc) {
+ if ( li->li_conn_ttl != 0 && li->li_idle_timeout != 0 ) {
+ return ( lc->lc_create_time + li->li_conn_ttl ) < ( lc->lc_time + li->li_idle_timeout ) ?
+ ( lc->lc_create_time + li->li_conn_ttl ) : ( lc->lc_time + li->li_idle_timeout );
+ } else if ( li->li_conn_ttl != 0 ) {
+ return lc->lc_create_time + li->li_conn_ttl;
+ } else if ( li->li_idle_timeout != 0 ) {
+ return lc->lc_time + li->li_idle_timeout;
+ }
+ return -1;
+}
+
+static void
+ldap_back_conn_prune( ldapinfo_t *li )
+{
+ time_t now = slap_get_time();
+ time_t next_timeout = -1; /* -1 means uninitialized */
+ TAvlnode *edge;
+ int c;
+
+ /*
+ * Iterate though connections and close those that are pass the expiry time.
+ * Also calculate the time for next connection to to expire.
+ */
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+
+ for ( c = LDAP_BACK_PCONN_FIRST; c < LDAP_BACK_PCONN_LAST; c++ ) {
+ ldapconn_t *lc = LDAP_TAILQ_FIRST( &li->li_conn_priv[ c ].lic_priv );
+
+ while ( lc ) {
+ ldapconn_t *next = LDAP_TAILQ_NEXT( lc, lc_q );
+ time_t conn_expires = ldap_back_conn_expire_time( li, lc );
+
+ if ( now >= conn_expires ) {
+ if ( lc->lc_refcnt == 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_back_conn_prune: closing expired connection lc=%p\n",
+ lc );
+ ldap_back_freeconn( li, lc, 0 );
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_back_conn_prune: tainting expired connection lc=%p\n",
+ lc );
+ LDAP_BACK_CONN_TAINTED_SET( lc );
+ }
+ } else if ( next_timeout == -1 || conn_expires < next_timeout ) {
+ /* next_timeout was not yet initialized or current connection expires sooner */
+ next_timeout = conn_expires;
+ }
+
+ lc = next;
+ }
+ }
+
+ edge = ldap_tavl_end( li->li_conninfo.lai_tree, TAVL_DIR_LEFT );
+ while ( edge ) {
+ TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
+ ldapconn_t *lc = (ldapconn_t *)edge->avl_data;
+ time_t conn_expires = ldap_back_conn_expire_time( li, lc );
+
+ if ( now >= conn_expires ) {
+ if ( lc->lc_refcnt == 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_back_conn_prune: closing expired connection lc=%p\n",
+ lc );
+ ldap_back_freeconn( li, lc, 0 );
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_back_conn_prune: tainting expired connection lc=%p\n",
+ lc );
+ LDAP_BACK_CONN_TAINTED_SET( lc );
+ }
+ } else if ( next_timeout == -1 || conn_expires < next_timeout ) {
+ next_timeout = conn_expires;
+ }
+
+ edge = next;
+ }
+
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+
+ /* Reschedule for next timeout or cancel the task */
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( next_timeout > 0 ) {
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, li->li_conn_expire_task ) ) {
+ ldap_pvt_runqueue_stoptask( &slapd_rq, li->li_conn_expire_task );
+ }
+ li->li_conn_expire_task->interval.tv_sec = next_timeout - now;
+ ldap_pvt_runqueue_resched( &slapd_rq, li->li_conn_expire_task, 0 );
+
+ /*
+ * The thread that handles runqueue might have already processed all tasks
+ * before we insertered new task or rescheduled the existing task with new
+ * timeout period. Wake it up to ensure that the task will be picked up.
+ */
+ slap_wake_listener();
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_back_conn_prune: scheduled connection expiry timer to %ld sec\n",
+ li->li_conn_expire_task->interval.tv_sec );
+ } else if ( next_timeout == -1 && li->li_conn_expire_task != NULL ) {
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, li->li_conn_expire_task ) ) {
+ ldap_pvt_runqueue_stoptask( &slapd_rq, li->li_conn_expire_task );
+ }
+ ldap_pvt_runqueue_remove( &slapd_rq, li->li_conn_expire_task );
+ li->li_conn_expire_task = NULL;
+ }
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+
+ return;
+}
+
+static void
+ldap_back_schedule_conn_expiry( ldapinfo_t *li, ldapconn_t *lc ) {
+ /* Do nothing if timeouts are not set. */
+ if ( li->li_conn_ttl == 0 && li->li_idle_timeout == 0 ) {
+ return;
+ }
+
+ /*
+ * If connection expire task is not running, create it and schedule for
+ * timeout of this connection.
+ *
+ * If the task is already running, this connection cannot be next one
+ * to expire and therefore timeout does not need to be re-calculated.
+ */
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( li->li_conn_expire_task == NULL ) {
+ li->li_conn_expire_task = ldap_pvt_runqueue_insert( &slapd_rq,
+ ldap_back_conn_expire_time( li, lc ) - slap_get_time(),
+ ldap_back_conn_expire_fn, li, "ldap_back_conn_expire_fn",
+ "ldap_back_conn_expire_timer" );
+ slap_wake_listener();
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_back_conn_prune: scheduled connection expiry timer to %ld sec\n",
+ li->li_conn_expire_task->interval.tv_sec );
+ }
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+
+ return;
+} \ No newline at end of file
diff --git a/servers/slapd/back-ldap/chain.c b/servers/slapd/back-ldap/chain.c
new file mode 100644
index 0000000..d6ffd1a
--- /dev/null
+++ b/servers/slapd/back-ldap/chain.c
@@ -0,0 +1,2356 @@
+/* chain.c - chain LDAP operations */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2003 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software.
+ * This work was subsequently modified by Pierangelo Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "lutil.h"
+#include "slap.h"
+#include "back-ldap.h"
+#include "slap-config.h"
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+#define SLAP_CHAINING_DEFAULT LDAP_CHAINING_PREFERRED
+#define SLAP_CH_RESOLVE_SHIFT SLAP_CONTROL_SHIFT
+#define SLAP_CH_RESOLVE_MASK (0x3 << SLAP_CH_RESOLVE_SHIFT)
+#define SLAP_CH_RESOLVE_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
+#define SLAP_CH_RESOLVE_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
+#define SLAP_CH_RESOLVE_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
+#define SLAP_CH_RESOLVE_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
+#define SLAP_CH_RESOLVE_DEFAULT (SLAP_CHAINING_DEFAULT << SLAP_CH_RESOLVE_SHIFT)
+#define SLAP_CH_CONTINUATION_SHIFT (SLAP_CH_RESOLVE_SHIFT + 2)
+#define SLAP_CH_CONTINUATION_MASK (0x3 << SLAP_CH_CONTINUATION_SHIFT)
+#define SLAP_CH_CONTINUATION_CHAINING_PREFERRED (LDAP_CHAINING_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
+#define SLAP_CH_CONTINUATION_CHAINING_REQUIRED (LDAP_CHAINING_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
+#define SLAP_CH_CONTINUATION_REFERRALS_PREFERRED (LDAP_REFERRALS_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
+#define SLAP_CH_CONTINUATION_REFERRALS_REQUIRED (LDAP_REFERRALS_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
+#define SLAP_CH_CONTINUATION_DEFAULT (SLAP_CHAINING_DEFAULT << SLAP_CH_CONTINUATION_SHIFT)
+
+#define o_chaining o_ctrlflag[sc_chainingBehavior]
+#define get_chaining(op) ((op)->o_chaining & SLAP_CONTROL_MASK)
+#define get_chainingBehavior(op) ((op)->o_chaining & (SLAP_CH_RESOLVE_MASK|SLAP_CH_CONTINUATION_MASK))
+#define get_resolveBehavior(op) ((op)->o_chaining & SLAP_CH_RESOLVE_MASK)
+#define get_continuationBehavior(op) ((op)->o_chaining & SLAP_CH_CONTINUATION_MASK)
+
+static int sc_chainingBehavior;
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+typedef enum {
+ LDAP_CH_NONE = 0,
+ LDAP_CH_RES,
+ LDAP_CH_ERR
+} ldap_chain_status_t;
+
+static BackendInfo *lback;
+
+typedef struct ldap_chain_t {
+ /*
+ * A "template" ldapinfo_t gets all common configuration items;
+ * then, for each configured URI, an entry is created in the tree;
+ * all the specific configuration items get in the current URI
+ * structure.
+ *
+ * Then, for each referral, extract the URI and lookup the
+ * related structure. If configured to do so, allow URIs
+ * not found in the structure to create a temporary one
+ * that chains anonymously; maybe it can also be added to
+ * the tree? Should be all configurable.
+ */
+
+ /* "common" configuration info (anything occurring before an "uri") */
+ ldapinfo_t *lc_common_li;
+
+ /* current configuration info */
+ ldapinfo_t *lc_cfg_li;
+
+ /* tree of configured[/generated?] "uri" info */
+ ldap_avl_info_t lc_lai;
+
+ /* max depth in nested referrals chaining */
+ int lc_max_depth;
+
+ unsigned lc_flags;
+#define LDAP_CHAIN_F_NONE (0x00U)
+#define LDAP_CHAIN_F_CHAINING (0x01U)
+#define LDAP_CHAIN_F_CACHE_URI (0x02U)
+#define LDAP_CHAIN_F_RETURN_ERR (0x04U)
+
+#define LDAP_CHAIN_ISSET(lc, f) ( ( (lc)->lc_flags & (f) ) == (f) )
+#define LDAP_CHAIN_CHAINING( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CHAINING )
+#define LDAP_CHAIN_CACHE_URI( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CACHE_URI )
+#define LDAP_CHAIN_RETURN_ERR( lc ) LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_RETURN_ERR )
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ LDAPControl lc_chaining_ctrl;
+ char lc_chaining_ctrlflag;
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+} ldap_chain_t;
+
+static int ldap_chain_db_init_common( BackendDB *be );
+static int ldap_chain_db_init_one( BackendDB *be );
+static int ldap_chain_db_open_one( BackendDB *be );
+#define ldap_chain_db_close_one(be) (0)
+#define ldap_chain_db_destroy_one(be, rs) (lback)->bi_db_destroy( (be), (rs) )
+
+typedef struct ldap_chain_cb_t {
+ ldap_chain_status_t lb_status;
+ ldap_chain_t *lb_lc;
+ slap_operation_t lb_op_type;
+ char *lb_text;
+ int lb_depth;
+} ldap_chain_cb_t;
+
+static int
+ldap_chain_op(
+ Operation *op,
+ SlapReply *rs,
+ slap_operation_t op_type,
+ BerVarray ref,
+ int depth );
+
+static int
+ldap_chain_search(
+ Operation *op,
+ SlapReply *rs,
+ BerVarray ref,
+ int depth );
+
+static slap_overinst ldapchain;
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+static int
+chaining_control_add(
+ ldap_chain_t *lc,
+ Operation *op,
+ LDAPControl ***oldctrlsp )
+{
+ LDAPControl **ctrls = NULL;
+ int c = 0;
+
+ *oldctrlsp = op->o_ctrls;
+
+ /* default chaining control not defined */
+ if ( !LDAP_CHAIN_CHAINING( lc ) ) {
+ return 0;
+ }
+
+ /* already present */
+ if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
+ return 0;
+ }
+
+ /* FIXME: check other incompatibilities */
+
+ /* add to other controls */
+ if ( op->o_ctrls ) {
+ for ( c = 0; op->o_ctrls[ c ]; c++ )
+ /* count them */ ;
+ }
+
+ ctrls = ch_calloc( sizeof( LDAPControl *), c + 2 );
+ ctrls[ 0 ] = &lc->lc_chaining_ctrl;
+ if ( op->o_ctrls ) {
+ for ( c = 0; op->o_ctrls[ c ]; c++ ) {
+ ctrls[ c + 1 ] = op->o_ctrls[ c ];
+ }
+ }
+ ctrls[ c + 1 ] = NULL;
+
+ op->o_ctrls = ctrls;
+
+ op->o_chaining = lc->lc_chaining_ctrlflag;
+
+ return 0;
+}
+
+static int
+chaining_control_remove(
+ Operation *op,
+ LDAPControl ***oldctrlsp )
+{
+ LDAPControl **oldctrls = *oldctrlsp;
+
+ /* we assume that the first control is the chaining control
+ * added by the chain overlay, so it's the only one we explicitly
+ * free */
+ if ( op->o_ctrls != oldctrls ) {
+ if ( op->o_ctrls != NULL ) {
+ assert( op->o_ctrls[ 0 ] != NULL );
+
+ free( op->o_ctrls );
+
+ op->o_chaining = 0;
+ }
+ op->o_ctrls = oldctrls;
+ }
+
+ *oldctrlsp = NULL;
+
+ return 0;
+}
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+static int
+ldap_chain_uri_cmp( const void *c1, const void *c2 )
+{
+ const ldapinfo_t *li1 = (const ldapinfo_t *)c1;
+ const ldapinfo_t *li2 = (const ldapinfo_t *)c2;
+
+ assert( li1->li_bvuri != NULL );
+ assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
+ assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
+
+ assert( li2->li_bvuri != NULL );
+ assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
+ assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
+
+ return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] );
+}
+
+static int
+ldap_chain_uri_dup( void *c1, void *c2 )
+{
+ ldapinfo_t *li1 = (ldapinfo_t *)c1;
+ ldapinfo_t *li2 = (ldapinfo_t *)c2;
+
+ assert( li1->li_bvuri != NULL );
+ assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
+ assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
+
+ assert( li2->li_bvuri != NULL );
+ assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
+ assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
+
+ if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Search specific response that strips entryDN from entries
+ */
+static int
+ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
+{
+ ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
+
+ assert( op->o_tag == LDAP_REQ_SEARCH );
+
+ /* if in error, don't proceed any further */
+ if ( lb->lb_status == LDAP_CH_ERR ) {
+ return 0;
+ }
+
+ if ( rs->sr_type == REP_SEARCH ) {
+ Attribute **ap = &rs->sr_entry->e_attrs;
+
+ for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
+ /* will be generated later by frontend
+ * (a cleaner solution would be that
+ * the frontend checks if it already exists */
+ if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
+ {
+ Attribute *a = *ap;
+
+ *ap = (*ap)->a_next;
+ attr_free( a );
+
+ /* there SHOULD be one only! */
+ break;
+ }
+ }
+
+ /* tell the frontend not to add generated
+ * operational attributes */
+ rs->sr_flags |= REP_NO_OPERATIONALS;
+
+ return SLAP_CB_CONTINUE;
+
+ } else if ( rs->sr_type == REP_SEARCHREF ) {
+ /* if we get it here, it means the library was unable
+ * to chase the referral... */
+ if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
+ rs->sr_err = ldap_chain_search( op, rs, rs->sr_ref, lb->lb_depth );
+ }
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
+ switch ( get_continuationBehavior( op ) ) {
+ case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
+ lb->lb_status = LDAP_CH_ERR;
+ return rs->sr_err = LDAP_X_CANNOT_CHAIN;
+
+ default:
+ break;
+ }
+ }
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+ return SLAP_CB_CONTINUE;
+
+ } else if ( rs->sr_type == REP_RESULT ) {
+ if ( rs->sr_err == LDAP_REFERRAL
+ && lb->lb_depth < lb->lb_lc->lc_max_depth
+ && rs->sr_ref != NULL )
+ {
+ rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_type,
+ rs->sr_ref, lb->lb_depth );
+ }
+
+ /* back-ldap tried to send result */
+ lb->lb_status = LDAP_CH_RES;
+ /* don't let other callbacks run, this isn't
+ * the real result for this op.
+ */
+ op->o_callback->sc_next = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * Dummy response that simply traces if back-ldap tried to send
+ * anything to the client
+ */
+static int
+ldap_chain_cb_response( Operation *op, SlapReply *rs )
+{
+ ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
+
+ /* if in error, don't proceed any further */
+ if ( lb->lb_status == LDAP_CH_ERR ) {
+ return 0;
+ }
+
+ if ( rs->sr_type == REP_RESULT ) {
+retry:;
+ switch ( rs->sr_err ) {
+ case LDAP_COMPARE_TRUE:
+ case LDAP_COMPARE_FALSE:
+ if ( op->o_tag != LDAP_REQ_COMPARE ) {
+ return rs->sr_err;
+ }
+ /* fallthru */
+
+ case LDAP_SUCCESS:
+ lb->lb_status = LDAP_CH_RES;
+ break;
+
+ case LDAP_REFERRAL:
+ if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
+ rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_type,
+ rs->sr_ref, lb->lb_depth );
+ goto retry;
+ }
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
+ switch ( get_continuationBehavior( op ) ) {
+ case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
+ lb->lb_status = LDAP_CH_ERR;
+ return rs->sr_err = LDAP_X_CANNOT_CHAIN;
+
+ default:
+ break;
+ }
+ }
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+ break;
+
+ default:
+ /* remember the text before it's freed in ldap_back_op_result */
+ if ( lb->lb_text ) {
+ ber_memfree_x( lb->lb_text, op->o_tmpmemctx );
+ }
+ lb->lb_text = ber_strdup_x( rs->sr_text, op->o_tmpmemctx );
+ return rs->sr_err;
+ }
+
+ } else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
+ {
+ /* strip the entryDN attribute, but keep returning results */
+ (void)ldap_chain_cb_search_response( op, rs );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+ldap_chain_op(
+ Operation *op,
+ SlapReply *rs,
+ slap_operation_t op_type,
+ BerVarray ref,
+ int depth )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+ struct berval odn = op->o_req_dn,
+ ondn = op->o_req_ndn;
+ ldapinfo_t li = { 0 }, *lip = NULL;
+ struct berval bvuri[ 2 ] = { { 0 } };
+
+ /* NOTE: returned if ref is empty... */
+ int rc = LDAP_OTHER,
+ first_rc;
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ LDAPControl **ctrls = NULL;
+
+ (void)chaining_control_add( lc, op, &ctrls );
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+ li.li_bvuri = bvuri;
+ first_rc = -1;
+ for ( ; !BER_BVISNULL( ref ); ref++ ) {
+ SlapReply rs2 = { 0 };
+ LDAPURLDesc *srv = NULL;
+ req_search_s save_oq_search = op->oq_search,
+ tmp_oq_search = { 0 };
+ struct berval dn = BER_BVNULL,
+ pdn = odn,
+ ndn = ondn;
+ char *filter = NULL;
+ int temporary = 0;
+ int free_dn = 0;
+
+ /* We're setting the URI of the first referral;
+ * what if there are more?
+
+Document: RFC 4511
+
+4.1.10. Referral
+ ...
+ If the client wishes to progress the operation, it MUST follow the
+ referral by contacting one of the supported services. If multiple
+ URIs are present, the client assumes that any supported URI may be
+ used to progress the operation.
+
+ * so we actually need to follow exactly one,
+ * and we can assume any is fine.
+ */
+
+ /* parse reference and use
+ * proto://[host][:port]/ only */
+ rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: unable to parse ref=\"%s\"\n",
+ op->o_log_prefix, ref->bv_val );
+
+ /* try next */
+ rc = LDAP_OTHER;
+ continue;
+ }
+
+ if ( op->o_tag == LDAP_REQ_SEARCH ) {
+ if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
+ /* RFC 4511: if scope is present, use it */
+ tmp_oq_search.rs_scope = srv->lud_scope;
+
+ } else {
+ /* RFC 4511: if scope is absent, use original */
+ tmp_oq_search.rs_scope = op->ors_scope;
+ }
+ }
+
+ rc = LDAP_SUCCESS;
+ srv->lud_scope = LDAP_SCOPE_DEFAULT;
+ dn.bv_val = srv->lud_dn;
+ filter = srv->lud_filter;
+
+ /* normalize DN */
+ if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
+ if ( srv->lud_dn == NULL ) {
+ srv->lud_dn = "";
+ }
+
+ } else {
+ ber_str2bv( srv->lud_dn, 0, 0, &dn );
+ rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
+ if ( rc == LDAP_SUCCESS ) {
+ /* remove DN essentially because later on
+ * ldap_initialize() will parse the URL
+ * as a comma-separated URL list */
+ srv->lud_dn = "";
+ free_dn = 1;
+ }
+ }
+
+ /* prepare filter */
+ if ( rc == LDAP_SUCCESS && op->o_tag == LDAP_REQ_SEARCH ) {
+ /* filter */
+ if ( srv->lud_filter != NULL
+ && srv->lud_filter[0] != '\0'
+ && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
+ {
+ /* RFC 4511: if filter is present, use it;
+ * otherwise, use original */
+ tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
+ if ( tmp_oq_search.rs_filter != NULL ) {
+ filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": unable to parse filter=\"%s\"\n",
+ op->o_log_prefix, ref->bv_val, srv->lud_filter );
+ rc = LDAP_OTHER;
+ }
+ }
+ }
+ srv->lud_filter = NULL;
+
+ if ( rc == LDAP_SUCCESS ) {
+ li.li_uri = ldap_url_desc2str( srv );
+ }
+
+ srv->lud_dn = dn.bv_val;
+ srv->lud_filter = filter;
+ ldap_free_urldesc( srv );
+
+ if ( rc != LDAP_SUCCESS ) {
+ /* try next */
+ rc = LDAP_OTHER;
+ continue;
+ }
+
+ if ( li.li_uri == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to reconstruct URI\n",
+ op->o_log_prefix, ref->bv_val );
+
+ /* try next */
+ rc = LDAP_OTHER;
+ goto further_cleanup;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" -> \"%s\"\n",
+ op->o_log_prefix, ref->bv_val, li.li_uri );
+
+ op->o_req_dn = pdn;
+ op->o_req_ndn = ndn;
+
+ if ( op->o_tag == LDAP_REQ_SEARCH ) {
+ op->ors_scope = tmp_oq_search.rs_scope;
+ if ( tmp_oq_search.rs_filter != NULL ) {
+ op->ors_filter = tmp_oq_search.rs_filter;
+ op->ors_filterstr = tmp_oq_search.rs_filterstr;
+ }
+ }
+
+ ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
+
+ /* Searches for a ldapinfo in the avl tree */
+ ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
+ lip = (ldapinfo_t *)ldap_tavl_find( lc->lc_lai.lai_tree,
+ (caddr_t)&li, ldap_chain_uri_cmp );
+ ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
+
+ if ( lip != NULL ) {
+ op->o_bd->be_private = (void *)lip;
+
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": URI=\"%s\" found in cache\n",
+ op->o_log_prefix, ref->bv_val, li.li_uri );
+
+ } else {
+ rc = ldap_chain_db_init_one( op->o_bd );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
+ op->o_log_prefix, ref->bv_val, li.li_uri );
+ goto cleanup;
+ }
+ lip = (ldapinfo_t *)op->o_bd->be_private;
+ lip->li_uri = li.li_uri;
+ lip->li_bvuri = bvuri;
+ rc = ldap_chain_db_open_one( op->o_bd );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
+ op->o_log_prefix, ref->bv_val, li.li_uri );
+ lip->li_uri = NULL;
+ lip->li_bvuri = NULL;
+ (void)ldap_chain_db_destroy_one( op->o_bd, NULL);
+ goto cleanup;
+ }
+
+ if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
+ ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
+ if ( ldap_tavl_insert( &lc->lc_lai.lai_tree,
+ (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
+ {
+ /* someone just inserted another;
+ * don't bother, use this and then
+ * just free it */
+ temporary = 1;
+ }
+ ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
+
+ } else {
+ temporary = 1;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" %s\n",
+ op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
+ }
+
+ lb->lb_op_type = op_type;
+ lb->lb_depth = depth + 1;
+
+ rc = (&lback->bi_op_bind)[ op_type ]( op, &rs2 );
+
+ /* note the first error */
+ if ( first_rc == -1 ) {
+ first_rc = rc;
+ }
+
+cleanup:;
+ ldap_memfree( li.li_uri );
+ li.li_uri = NULL;
+
+ if ( temporary ) {
+ lip->li_uri = NULL;
+ lip->li_bvuri = NULL;
+ (void)ldap_chain_db_close_one( op->o_bd );
+ (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
+ }
+
+further_cleanup:;
+ if ( op->o_req_dn.bv_val == pdn.bv_val ) {
+ op->o_req_dn = odn;
+ op->o_req_ndn = ondn;
+ }
+
+ if ( free_dn ) {
+ op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( op->o_tag == LDAP_REQ_SEARCH ) {
+ if ( tmp_oq_search.rs_filter != NULL ) {
+ filter_free_x( op, tmp_oq_search.rs_filter, 1 );
+ }
+
+ if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
+ slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
+ }
+
+ op->oq_search = save_oq_search;
+ }
+
+ if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
+ *rs = rs2;
+ break;
+ }
+
+ rc = rs2.sr_err;
+ }
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ (void)chaining_control_remove( op, &ctrls );
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+ if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
+ rc = first_rc;
+ }
+
+ return rc;
+}
+
+static int
+ldap_chain_search(
+ Operation *op,
+ SlapReply *rs,
+ BerVarray ref,
+ int depth )
+
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ ldap_chain_cb_t *lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+ ldapinfo_t li = { 0 }, *lip = NULL;
+ struct berval bvuri[ 2 ] = { { 0 } };
+
+ struct berval odn = op->o_req_dn,
+ ondn = op->o_req_ndn;
+ Entry *save_entry = rs->sr_entry;
+ slap_mask_t save_flags = rs->sr_flags;
+
+ int rc = LDAP_OTHER,
+ first_rc = -1;
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ LDAPControl **ctrls = NULL;
+
+ (void)chaining_control_add( lc, op, &ctrls );
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+ assert( rs->sr_type == REP_SEARCHREF );
+
+ rs->sr_type = REP_SEARCH;
+
+ /* if we parse the URI then by no means
+ * we can cache stuff or reuse connections,
+ * because in back-ldap there's no caching
+ * based on the URI value, which is supposed
+ * to be set once for all (correct?) */
+ li.li_bvuri = bvuri;
+ for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
+ SlapReply rs2 = { REP_RESULT };
+ LDAPURLDesc *srv;
+ req_search_s save_oq_search = op->oq_search,
+ tmp_oq_search = { 0 };
+ struct berval dn,
+ pdn = op->o_req_dn,
+ ndn = op->o_req_ndn;
+ char *filter = NULL;
+ int temporary = 0;
+ int free_dn = 0;
+
+ /* parse reference and use
+ * proto://[host][:port]/ only */
+ rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: unable to parse ref=\"%s\"\n",
+ op->o_log_prefix, ref->bv_val );
+
+ /* try next */
+ rs->sr_err = LDAP_OTHER;
+ continue;
+ }
+
+ if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
+ /* RFC 4511: if scope is present, use it */
+ tmp_oq_search.rs_scope = srv->lud_scope;
+
+ } else {
+ /* RFC 4511: if scope is absent, use original */
+ /* Section 4.5.3: if scope is onelevel, use base */
+ if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
+ tmp_oq_search.rs_scope = LDAP_SCOPE_BASE;
+ else
+ tmp_oq_search.rs_scope = op->ors_scope;
+ }
+
+ rc = LDAP_SUCCESS;
+ srv->lud_scope = LDAP_SCOPE_DEFAULT;
+ dn.bv_val = srv->lud_dn;
+ filter = srv->lud_filter;
+
+ /* normalize DN */
+ if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
+ if ( srv->lud_dn == NULL ) {
+ srv->lud_dn = "";
+ }
+
+ if ( save_entry != NULL ) {
+ /* use the "right" DN, if available */
+ pdn = save_entry->e_name;
+ ndn = save_entry->e_nname;
+ } /* else leave the original req DN in place, if any RFC 4511 */
+
+ } else {
+ /* RFC 4511: if DN is present, use it */
+ ber_str2bv( srv->lud_dn, 0, 0, &dn );
+ rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
+ if ( rc == LDAP_SUCCESS ) {
+ /* remove DN essentially because later on
+ * ldap_initialize() will parse the URL
+ * as a comma-separated URL list */
+ srv->lud_dn = "";
+ free_dn = 1;
+ }
+ }
+
+ /* prepare filter */
+ if ( rc == LDAP_SUCCESS ) {
+ /* filter */
+ if ( srv->lud_filter != NULL
+ && srv->lud_filter[0] != '\0'
+ && strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
+ {
+ /* RFC 4511: if filter is present, use it;
+ * otherwise, use original */
+ tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
+ if ( tmp_oq_search.rs_filter != NULL ) {
+ filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": unable to parse filter=\"%s\"\n",
+ op->o_log_prefix, ref->bv_val, srv->lud_filter );
+ rc = LDAP_OTHER;
+ }
+ }
+ }
+ srv->lud_filter = NULL;
+
+ if ( rc == LDAP_SUCCESS ) {
+ li.li_uri = ldap_url_desc2str( srv );
+ }
+
+ srv->lud_dn = dn.bv_val;
+ srv->lud_filter = filter;
+ ldap_free_urldesc( srv );
+
+ if ( rc != LDAP_SUCCESS || li.li_uri == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to reconstruct URI\n",
+ op->o_log_prefix, ref->bv_val );
+
+ /* try next */
+ rc = LDAP_OTHER;
+ goto further_cleanup;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" -> \"%s\"\n",
+ op->o_log_prefix, ref->bv_val, li.li_uri );
+
+ op->o_req_dn = pdn;
+ op->o_req_ndn = ndn;
+ op->ors_scope = tmp_oq_search.rs_scope;
+ if ( tmp_oq_search.rs_filter != NULL ) {
+ op->ors_filter = tmp_oq_search.rs_filter;
+ op->ors_filterstr = tmp_oq_search.rs_filterstr;
+ }
+
+ ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
+
+ /* Searches for a ldapinfo in the avl tree */
+ ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
+ lip = (ldapinfo_t *)ldap_tavl_find( lc->lc_lai.lai_tree,
+ (caddr_t)&li, ldap_chain_uri_cmp );
+ ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
+
+ if ( lip != NULL ) {
+ op->o_bd->be_private = (void *)lip;
+
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": URI=\"%s\" found in cache\n",
+ op->o_log_prefix, ref->bv_val, li.li_uri );
+
+ } else {
+ /* if none is found, create a temporary... */
+ rc = ldap_chain_db_init_one( op->o_bd );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
+ op->o_log_prefix, ref->bv_val, li.li_uri );
+ goto cleanup;
+ }
+ lip = (ldapinfo_t *)op->o_bd->be_private;
+ lip->li_uri = li.li_uri;
+ lip->li_bvuri = bvuri;
+ rc = ldap_chain_db_open_one( op->o_bd );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
+ op->o_log_prefix, ref->bv_val, li.li_uri );
+ lip->li_uri = NULL;
+ lip->li_bvuri = NULL;
+ (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
+ goto cleanup;
+ }
+
+ if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
+ ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
+ if ( ldap_tavl_insert( &lc->lc_lai.lai_tree,
+ (caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
+ {
+ /* someone just inserted another;
+ * don't bother, use this and then
+ * just free it */
+ temporary = 1;
+ }
+ ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
+
+ } else {
+ temporary = 1;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" %s\n",
+ op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
+ }
+
+ lb->lb_op_type = op_search;
+ lb->lb_depth = depth + 1;
+
+ /* FIXME: should we also copy filter and scope?
+ * according to RFC3296, no */
+ rc = lback->bi_op_search( op, &rs2 );
+ if ( first_rc == -1 ) {
+ first_rc = rc;
+ }
+
+cleanup:;
+ ldap_memfree( li.li_uri );
+ li.li_uri = NULL;
+
+ if ( temporary ) {
+ lip->li_uri = NULL;
+ lip->li_bvuri = NULL;
+ (void)ldap_chain_db_close_one( op->o_bd );
+ (void)ldap_chain_db_destroy_one( op->o_bd, NULL );
+ }
+
+further_cleanup:;
+ if ( op->o_req_dn.bv_val == pdn.bv_val ) {
+ op->o_req_dn = odn;
+ op->o_req_ndn = ondn;
+ }
+
+ if ( free_dn ) {
+ op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( tmp_oq_search.rs_filter != NULL ) {
+ filter_free_x( op, tmp_oq_search.rs_filter, 1 );
+ }
+
+ if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
+ slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
+ }
+
+ op->oq_search = save_oq_search;
+
+ if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
+ *rs = rs2;
+ break;
+ }
+
+ rc = rs2.sr_err;
+ }
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ (void)chaining_control_remove( op, &ctrls );
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+ rs->sr_type = REP_SEARCHREF;
+ rs->sr_entry = save_entry;
+ rs->sr_flags = save_flags;
+
+ if ( rc != LDAP_SUCCESS ) {
+ /* couldn't chase any of the referrals */
+ if ( first_rc != -1 ) {
+ rc = first_rc;
+
+ } else {
+ rc = SLAP_CB_CONTINUE;
+ }
+ }
+
+ return rc;
+}
+
+static int
+ldap_chain_response( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+ BackendDB db, *bd = op->o_bd;
+ ldap_chain_cb_t lb = { 0 };
+ slap_callback *sc = op->o_callback,
+ sc2 = { 0 };
+ int rc = 0;
+ const char *text = NULL;
+ const char *matched;
+ BerVarray ref;
+ slap_mask_t flags = 0;
+ struct berval ndn = op->o_ndn;
+
+ int sr_err = rs->sr_err;
+ slap_reply_t sr_type = rs->sr_type;
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ slap_mask_t chain_mask = 0;
+ ber_len_t chain_shift = 0;
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+ if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
+ return SLAP_CB_CONTINUE;
+ }
+ if ( !rs->sr_ref ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
+ switch ( get_resolveBehavior( op ) ) {
+ case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
+ case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
+ return SLAP_CB_CONTINUE;
+
+ default:
+ chain_mask = SLAP_CH_RESOLVE_MASK;
+ chain_shift = SLAP_CH_RESOLVE_SHIFT;
+ break;
+ }
+
+ } else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
+ switch ( get_continuationBehavior( op ) ) {
+ case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
+ case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
+ return SLAP_CB_CONTINUE;
+
+ default:
+ chain_mask = SLAP_CH_CONTINUATION_MASK;
+ chain_shift = SLAP_CH_CONTINUATION_SHIFT;
+ break;
+ }
+ }
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+ /*
+ * TODO: add checks on who/when chain operations; e.g.:
+ * a) what identities are authorized
+ * b) what request DN (e.g. only chain requests rooted at <DN>)
+ * c) what referral URIs
+ * d) what protocol scheme (e.g. only ldaps://)
+ * e) what ssf
+ */
+
+ db = *op->o_bd;
+ SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
+ op->o_bd = &db;
+
+ text = rs->sr_text;
+ rs->sr_text = NULL;
+ matched = rs->sr_matched;
+ rs->sr_matched = NULL;
+ ref = rs->sr_ref;
+ rs->sr_ref = NULL;
+
+ flags = rs->sr_flags & (REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED);
+ rs->sr_flags &= ~flags;
+
+ /* we need this to know if back-ldap returned any result */
+ lb.lb_lc = lc;
+ sc2.sc_next = sc->sc_next;
+ sc2.sc_private = &lb;
+ sc2.sc_response = ldap_chain_cb_response;
+ op->o_callback = &sc2;
+
+ /* Chaining can be performed by a privileged user on behalf
+ * of normal users, using the ProxyAuthz control, by exploiting
+ * the identity assertion feature of back-ldap; see idassert-*
+ * directives in slapd-ldap(5).
+ *
+ * FIXME: the idassert-authcDN is one, will it be fine regardless
+ * of the URI we obtain from the referral?
+ */
+
+ switch ( op->o_tag ) {
+ case LDAP_REQ_BIND: {
+ struct berval rndn = op->o_req_ndn;
+ Connection *conn = op->o_conn;
+
+ /* FIXME: can we really get a referral for binds? */
+ op->o_req_ndn = slap_empty_bv;
+ op->o_conn = NULL;
+ rc = ldap_chain_op( op, rs, op_bind, ref, 0 );
+ op->o_req_ndn = rndn;
+ op->o_conn = conn;
+ }
+ break;
+
+ case LDAP_REQ_ADD:
+ rc = ldap_chain_op( op, rs, op_add, ref, 0 );
+ break;
+
+ case LDAP_REQ_DELETE:
+ rc = ldap_chain_op( op, rs, op_delete, ref, 0 );
+ break;
+
+ case LDAP_REQ_MODRDN:
+ rc = ldap_chain_op( op, rs, op_modrdn, ref, 0 );
+ break;
+
+ case LDAP_REQ_MODIFY:
+ rc = ldap_chain_op( op, rs, op_modify, ref, 0 );
+ break;
+
+ case LDAP_REQ_COMPARE:
+ rc = ldap_chain_op( op, rs, op_compare, ref, 0 );
+ if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
+ rc = LDAP_SUCCESS;
+ }
+ break;
+
+ case LDAP_REQ_SEARCH:
+ if ( rs->sr_type == REP_SEARCHREF ) {
+ sc2.sc_response = ldap_chain_cb_search_response;
+ rc = ldap_chain_search( op, rs, ref, 0 );
+
+ } else {
+ /* we might get here before any database actually
+ * performed a search; in those cases, we need
+ * to check limits, to make sure safe defaults
+ * are in place */
+ if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
+ rc = ldap_chain_op( op, rs, op_search, ref, 0 );
+ } else {
+ rc = SLAP_CB_CONTINUE;
+ }
+ }
+ break;
+
+ case LDAP_REQ_EXTENDED:
+ rc = ldap_chain_op( op, rs, op_extended, ref, 0 );
+ /* FIXME: ldap_back_extended() by design
+ * doesn't send result; frontend is expected
+ * to send it... */
+ /* FIXME: what about chaining? */
+ if ( rc != SLAPD_ABANDON ) {
+ rs->sr_err = rc;
+ send_ldap_extended( op, rs );
+ rc = LDAP_SUCCESS;
+ }
+ lb.lb_status = LDAP_CH_RES;
+ break;
+
+ default:
+ rc = SLAP_CB_CONTINUE;
+ break;
+ }
+
+ switch ( rc ) {
+ case SLAPD_ABANDON:
+ goto dont_chain;
+
+ case LDAP_SUCCESS:
+ case LDAP_REFERRAL:
+ sr_err = rs->sr_err;
+ /* slapd-ldap sent response */
+ if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
+ /* FIXME: should we send response? */
+ Debug( LDAP_DEBUG_ANY,
+ "%s: ldap_chain_response: "
+ "overlay should have sent result.\n",
+ op->o_log_prefix );
+ }
+ break;
+
+ default:
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
+ goto cannot_chain;
+ }
+
+ switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
+ case LDAP_CHAINING_REQUIRED:
+cannot_chain:;
+ op->o_callback = NULL;
+ send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
+ "operation cannot be completed without chaining" );
+ goto dont_chain;
+
+ default:
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+ if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
+ sr_err = rs->sr_err = rc;
+ rs->sr_text = lb.lb_text;
+ rs->sr_type = sr_type;
+
+ } else {
+ rc = SLAP_CB_CONTINUE;
+ rs->sr_err = sr_err;
+ rs->sr_type = sr_type;
+ rs->sr_text = text;
+ rs->sr_matched = matched;
+ rs->sr_ref = ref;
+ rs->sr_flags |= flags;
+ }
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ break;
+ }
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+ }
+
+ if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
+ /* give the remaining callbacks a chance */
+ op->o_callback = sc->sc_next;
+ rc = rs->sr_err = slap_map_api2result( rs );
+ send_ldap_result( op, rs );
+ }
+
+dont_chain:;
+ rs->sr_err = sr_err;
+ rs->sr_type = sr_type;
+ rs->sr_text = text;
+ rs->sr_matched = matched;
+ rs->sr_ref = ref;
+ rs->sr_flags |= flags;
+
+ op->o_bd = bd;
+ op->o_callback = sc;
+ op->o_ndn = ndn;
+
+ if ( rs->sr_text == lb.lb_text ) {
+ rs->sr_text = NULL;
+ }
+ if ( lb.lb_text ) {
+ ber_memfree_x( lb.lb_text, op->o_tmpmemctx );
+ }
+
+ return rc;
+}
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+static int
+ldap_chain_parse_ctrl(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl );
+
+static int
+str2chain( const char *s )
+{
+ if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
+ return LDAP_CHAINING_PREFERRED;
+
+ } else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
+ return LDAP_CHAINING_REQUIRED;
+
+ } else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
+ return LDAP_REFERRALS_PREFERRED;
+
+ } else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
+ return LDAP_REFERRALS_REQUIRED;
+ }
+
+ return -1;
+}
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+/*
+ * configuration...
+ */
+
+enum {
+ CH_CHAINING = 1,
+ CH_CACHE_URI,
+ CH_MAX_DEPTH,
+ CH_RETURN_ERR,
+
+ CH_LAST
+};
+
+static ConfigDriver chain_cf_gen;
+static ConfigCfAdd chain_cfadd;
+static ConfigLDAPadd chain_ldadd;
+#ifdef SLAP_CONFIG_DELETE
+static ConfigLDAPdel chain_lddel;
+#endif
+
+static ConfigTable chaincfg[] = {
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ { "chain-chaining", "args",
+ 2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
+ "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
+ "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+ { "chain-cache-uri", "TRUE/FALSE",
+ 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
+ "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
+ "DESC 'Enables caching of URIs not present in configuration' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "chain-max-depth", "args",
+ 2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
+ "( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
+ "DESC 'max referral depth' "
+ "SYNTAX OMsInteger "
+ "EQUALITY integerMatch "
+ "SINGLE-VALUE )", NULL, NULL },
+ { "chain-return-error", "TRUE/FALSE",
+ 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
+ "( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
+ "DESC 'Errors are returned instead of the original referral' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs chainocs[] = {
+ { "( OLcfgOvOc:3.1 "
+ "NAME 'olcChainConfig' "
+ "DESC 'Chain configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( "
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ "olcChainingBehavior $ "
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+ "olcChainCacheURI $ "
+ "olcChainMaxReferralDepth $ "
+ "olcChainReturnError "
+ ") )",
+ Cft_Overlay, chaincfg, NULL, chain_cfadd },
+ { "( OLcfgOvOc:3.2 "
+ "NAME 'olcChainDatabase' "
+ "DESC 'Chain remote server configuration' "
+ "AUXILIARY )",
+ Cft_Misc, NULL, chain_ldadd
+#ifdef SLAP_CONFIG_DELETE
+ , NULL, chain_lddel
+#endif
+ },
+ { NULL, 0, NULL }
+};
+
+static int
+chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
+{
+ slap_overinst *on;
+ ldap_chain_t *lc;
+
+ ldapinfo_t *li;
+
+ AttributeDescription *ad = NULL;
+ Attribute *at;
+ const char *text;
+
+ int rc;
+
+ if ( p->ce_type != Cft_Overlay
+ || !p->ce_bi
+ || p->ce_bi->bi_cf_ocs != chainocs )
+ {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ on = (slap_overinst *)p->ce_bi;
+ lc = (ldap_chain_t *)on->on_bi.bi_private;
+
+ assert( ca->be == NULL );
+ ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
+
+ ca->be->bd_info = (BackendInfo *)on;
+
+ rc = slap_str2ad( "olcDbURI", &ad, &text );
+ assert( rc == LDAP_SUCCESS );
+
+ at = attr_find( e->e_attrs, ad );
+#if 0
+ if ( lc->lc_common_li == NULL && at != NULL ) {
+ /* FIXME: we should generate an empty default entry
+ * if none is supplied */
+ Debug( LDAP_DEBUG_ANY, "slapd-chain: "
+ "first underlying database \"%s\" "
+ "cannot contain attribute \"%s\".\n",
+ e->e_name.bv_val, ad->ad_cname.bv_val );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+
+ } else
+#endif
+ if ( lc->lc_common_li != NULL && lc->lc_common_li != lc->lc_cfg_li && at == NULL ) {
+ /* FIXME: we should generate an empty default entry
+ * if none is supplied */
+ Debug( LDAP_DEBUG_ANY, "slapd-chain: "
+ "subsequent underlying database \"%s\" "
+ "must contain attribute \"%s\".\n",
+ e->e_name.bv_val, ad->ad_cname.bv_val );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+
+ if ( lc->lc_common_li == NULL ) {
+ rc = ldap_chain_db_init_common( ca->be );
+ if ( rc != 0 )
+ goto fail;
+ li = ca->be->be_private;
+ lc->lc_common_li = lc->lc_cfg_li = li;
+
+ }
+ rc = ldap_chain_db_init_one( ca->be );
+ lc->lc_cfg_li = NULL;
+
+ if ( rc != 0 ) {
+fail:
+ Debug( LDAP_DEBUG_ANY, "slapd-chain: "
+ "unable to init %sunderlying database \"%s\".\n",
+ lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val );
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ li = ca->be->be_private;
+
+ if ( at ) {
+ char **urls;
+
+ urls = ldap_str2charray( at->a_vals[ 0 ].bv_val, ", \t" );
+ if ( !urls || !urls[0] || urls[1] ) {
+ ldap_charray_free( urls );
+ Debug( LDAP_DEBUG_ANY, "slapd-chain: "
+ "olcDbURI must contain exactly one url, got %s\n",
+ at->a_vals[ 0 ].bv_val );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+ ldap_charray_free( urls );
+
+ li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
+ value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
+ if ( ldap_tavl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
+ ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
+ {
+ Debug( LDAP_DEBUG_ANY, "slapd-chain: "
+ "database \"%s\" insert failed.\n",
+ e->e_name.bv_val );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+ }
+
+ ca->ca_private = on;
+
+done:;
+ if ( rc != LDAP_SUCCESS ) {
+ (void)ldap_chain_db_destroy_one( ca->be, NULL );
+ ch_free( ca->be );
+ ca->be = NULL;
+ }
+
+ return rc;
+}
+
+static void
+ldap_chain_cfadd_apply(
+ ldapinfo_t *li,
+ Operation *op,
+ SlapReply *rs,
+ Entry *p,
+ ConfigArgs *ca,
+ int count )
+{
+ struct berval bv;
+
+ /* FIXME: should not hardcode "olcDatabase" here */
+ bv.bv_len = snprintf( ca->cr_msg, sizeof( ca->cr_msg ),
+ "olcDatabase={%d}%s", count, lback->bi_type );
+ bv.bv_val = ca->cr_msg;
+
+ ca->be->be_private = (void *)li;
+ config_build_entry( op, rs, p->e_private, ca,
+ &bv, lback->bi_cf_ocs, &chainocs[1] );
+
+ return;
+}
+
+static int
+chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
+{
+ CfEntryInfo *pe = p->e_private;
+ slap_overinst *on = (slap_overinst *)pe->ce_bi;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+ void *priv = (void *)ca->be->be_private;
+ TAvlnode *edge;
+ int count = 0;
+
+ if ( lback->bi_cf_ocs ) {
+
+ ldap_chain_cfadd_apply( lc->lc_common_li, op, rs, p, ca, count++ );
+
+ edge = ldap_tavl_end( lc->lc_lai.lai_tree, TAVL_DIR_LEFT );
+ while ( edge ) {
+ TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
+ ldapinfo_t *li = (ldapinfo_t *)edge->avl_data;
+ ldap_chain_cfadd_apply( li, op, rs, p, ca, count++ );
+ edge = next;
+ }
+
+ ca->be->be_private = priv;
+ }
+
+ lc->lc_cfg_li = NULL;
+
+ return 0;
+}
+
+#ifdef SLAP_CONFIG_DELETE
+static int
+chain_lddel( CfEntryInfo *ce, Operation *op )
+{
+ CfEntryInfo *pe = ce->ce_parent;
+ slap_overinst *on = (slap_overinst *)pe->ce_bi;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+ ldapinfo_t *li = (ldapinfo_t *) ce->ce_be->be_private;
+
+ if ( li != lc->lc_common_li ) {
+ if (! ldap_tavl_delete( &lc->lc_lai.lai_tree, li, ldap_chain_uri_cmp ) ) {
+ Debug( LDAP_DEBUG_ANY, "slapd-chain: ldap_avl_delete failed. "
+ "\"%s\" not found.\n", li->li_uri );
+ return -1;
+ }
+ } else if ( lc->lc_lai.lai_tree ) {
+ Debug( LDAP_DEBUG_ANY, "slapd-chain: cannot delete first underlying "
+ "LDAP database when other databases are still present.\n" );
+ return -1;
+ } else {
+ lc->lc_common_li = NULL;
+ }
+
+ ce->ce_be->bd_info = lback;
+
+ if ( ce->ce_be->bd_info->bi_db_close ) {
+ ce->ce_be->bd_info->bi_db_close( ce->ce_be, NULL );
+ }
+ if ( ce->ce_be->bd_info->bi_db_destroy ) {
+ ce->ce_be->bd_info->bi_db_destroy( ce->ce_be, NULL );
+ }
+
+ ch_free(ce->ce_be);
+ ce->ce_be = NULL;
+
+ return LDAP_SUCCESS;
+}
+#endif /* SLAP_CONFIG_DELETE */
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+static slap_verbmasks chaining_mode[] = {
+ { BER_BVC("referralsRequired"), LDAP_REFERRALS_REQUIRED },
+ { BER_BVC("referralsPreferred"), LDAP_REFERRALS_PREFERRED },
+ { BER_BVC("chainingRequired"), LDAP_CHAINING_REQUIRED },
+ { BER_BVC("chainingPreferred"), LDAP_CHAINING_PREFERRED },
+ { BER_BVNULL, 0 }
+};
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+static int
+chain_cf_gen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+
+ int rc = 0;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ switch( c->type ) {
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ case CH_CHAINING: {
+ struct berval resolve = BER_BVNULL,
+ continuation = BER_BVNULL;
+
+ if ( !LDAP_CHAIN_CHAINING( lc ) ) {
+ return 1;
+ }
+
+ enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
+ enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
+
+ c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
+ + STRLENOF( " " )
+ + STRLENOF( "continuation=" ) + continuation.bv_len;
+ c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
+ snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
+ "resolve=%s continuation=%s",
+ resolve.bv_val, continuation.bv_val );
+
+ if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
+ c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
+ c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
+ AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
+ " critical", STRLENOF( " critical" ) + 1 );
+ c->value_bv.bv_len += STRLENOF( " critical" );
+ }
+
+ break;
+ }
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+ case CH_CACHE_URI:
+ c->value_int = LDAP_CHAIN_CACHE_URI( lc );
+ break;
+
+ case CH_MAX_DEPTH:
+ c->value_int = lc->lc_max_depth;
+ break;
+
+ case CH_RETURN_ERR:
+ c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
+ break;
+
+ default:
+ assert( 0 );
+ rc = 1;
+ }
+ return rc;
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ switch( c->type ) {
+ case CH_CHAINING:
+ return 1;
+
+ case CH_CACHE_URI:
+ lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
+ break;
+
+ case CH_MAX_DEPTH:
+ c->value_int = 0;
+ break;
+
+ case CH_RETURN_ERR:
+ lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
+ break;
+
+ default:
+ return 1;
+ }
+ return rc;
+ }
+
+ switch( c->type ) {
+ case CH_CHAINING: {
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ char **argv = c->argv;
+ int argc = c->argc;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ int resolve = -1,
+ continuation = -1,
+ iscritical = 0;
+ Operation op = { 0 };
+ SlapReply rs = { 0 };
+
+ lc->lc_chaining_ctrlflag = 0;
+
+ for ( argc--, argv++; argc > 0; argc--, argv++ ) {
+ if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
+ resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
+ if ( resolve == -1 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "illegal <resolve> value %s "
+ "in \"chain-chaining>\".\n",
+ c->log, argv[ 0 ] );
+ return 1;
+ }
+
+ } else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
+ continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
+ if ( continuation == -1 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "illegal <continuation> value %s "
+ "in \"chain-chaining\".\n",
+ c->log, argv[ 0 ] );
+ return 1;
+ }
+
+ } else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
+ iscritical = 1;
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "unknown option in \"chain-chaining\".\n",
+ c->log );
+ return 1;
+ }
+ }
+
+ if ( resolve != -1 || continuation != -1 ) {
+ int err;
+
+ if ( resolve == -1 ) {
+ /* default */
+ resolve = SLAP_CHAINING_DEFAULT;
+ }
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+
+ err = ber_printf( ber, "{e" /* } */, resolve );
+ if ( err == -1 ) {
+ ber_free( ber, 1 );
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "chaining behavior control encoding error!\n",
+ c->log );
+ return 1;
+ }
+
+ if ( continuation > -1 ) {
+ err = ber_printf( ber, "e", continuation );
+ if ( err == -1 ) {
+ ber_free( ber, 1 );
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "chaining behavior control encoding error!\n",
+ c->log );
+ return 1;
+ }
+ }
+
+ err = ber_printf( ber, /* { */ "N}" );
+ if ( err == -1 ) {
+ ber_free( ber, 1 );
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "chaining behavior control encoding error!\n",
+ c->log );
+ return 1;
+ }
+
+ if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
+ exit( EXIT_FAILURE );
+ }
+
+ } else {
+ BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
+ }
+
+ lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
+ lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
+
+ if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
+ {
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "unable to parse chaining control%s%s.\n",
+ c->log, rs.sr_text ? ": " : "",
+ rs.sr_text ? rs.sr_text : "" );
+ return 1;
+ }
+
+ lc->lc_chaining_ctrlflag = op.o_chaining;
+
+ lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
+
+ rc = 0;
+#else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "\"chaining\" control unsupported (ignored).\n",
+ c->log );
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+ } break;
+
+ case CH_CACHE_URI:
+ if ( c->value_int ) {
+ lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
+ } else {
+ lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
+ }
+ break;
+
+ case CH_MAX_DEPTH:
+ if ( c->value_int < 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "<%s> invalid max referral depth %d",
+ c->argv[0], c->value_int );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ rc = 1;
+ break;
+ }
+ lc->lc_max_depth = c->value_int;
+
+ case CH_RETURN_ERR:
+ if ( c->value_int ) {
+ lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
+ } else {
+ lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
+ }
+ break;
+
+ default:
+ assert( 0 );
+ return 1;
+ }
+ return rc;
+}
+
+static int
+ldap_chain_db_init(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ ldap_chain_t *lc = NULL;
+
+ if ( lback == NULL ) {
+ lback = backend_info( "ldap" );
+
+ if ( lback == NULL ) {
+ return 1;
+ }
+ }
+
+ lc = ch_malloc( sizeof( ldap_chain_t ) );
+ if ( lc == NULL ) {
+ return 1;
+ }
+ memset( lc, 0, sizeof( ldap_chain_t ) );
+ lc->lc_max_depth = 1;
+ ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
+
+ on->on_bi.bi_private = (void *)lc;
+
+ return 0;
+}
+
+static int
+ldap_chain_db_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+
+ int rc = SLAP_CONF_UNKNOWN;
+
+ if ( lc->lc_common_li == NULL ) {
+ BackendDB db = *be;
+ ldap_chain_db_init_common( &db );
+ lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)db.be_private;
+ }
+
+ /* Something for the chain database? */
+ if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
+ char *save_argv0 = argv[ 0 ];
+ BackendDB db = *be;
+ static char *allowed_argv[] = {
+ /* special: put URI here, so in the meanwhile
+ * it detects whether a new URI is being provided */
+ "uri",
+ "nretries",
+ "timeout",
+ /* flags */
+ "tls",
+ /* FIXME: maybe rebind-as-user should be allowed
+ * only within known URIs... */
+ "rebind-as-user",
+ "chase-referrals",
+ "t-f-support",
+ "proxy-whoami",
+ NULL
+ };
+ int which_argv = -1;
+
+ argv[ 0 ] += STRLENOF( "chain-" );
+
+ for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
+ if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
+ break;
+ }
+ }
+
+ if ( allowed_argv[ which_argv ] == NULL ) {
+ which_argv = -1;
+
+ if ( lc->lc_cfg_li == lc->lc_common_li ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "\"%s\" only allowed within a URI directive.\n.",
+ fname, lineno, argv[ 0 ] );
+ return 1;
+ }
+ }
+
+ if ( which_argv == 0 ) {
+ rc = ldap_chain_db_init_one( &db );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "underlying slapd-ldap initialization failed.\n.",
+ fname, lineno );
+ return 1;
+ }
+ lc->lc_cfg_li = db.be_private;
+ }
+
+ /* TODO: add checks on what other slapd-ldap(5) args
+ * should be put in the template; this is not quite
+ * harmful, because attributes that shouldn't don't
+ * get actually used, but the user should at least
+ * be warned.
+ */
+
+ db.bd_info = lback;
+ db.be_private = (void *)lc->lc_cfg_li;
+ db.be_cf_ocs = lback->bi_cf_ocs;
+
+ rc = config_generic_wrapper( &db, fname, lineno, argc, argv );
+
+ argv[ 0 ] = save_argv0;
+
+ if ( which_argv == 0 ) {
+private_destroy:;
+ if ( rc != 0 ) {
+ db.bd_info = lback;
+ db.be_private = (void *)lc->lc_cfg_li;
+ ldap_chain_db_destroy_one( &db, NULL );
+ lc->lc_cfg_li = NULL;
+ } else {
+ if ( lc->lc_cfg_li->li_bvuri == NULL
+ || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
+ || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
+ {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "no URI list allowed in slapo-chain.\n",
+ fname, lineno );
+ rc = 1;
+ goto private_destroy;
+ }
+
+ if ( ldap_tavl_insert( &lc->lc_lai.lai_tree,
+ (caddr_t)lc->lc_cfg_li,
+ ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
+ {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "duplicate URI in slapo-chain.\n",
+ fname, lineno );
+ rc = 1;
+ goto private_destroy;
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+enum db_which {
+ db_open = 0,
+ db_close,
+ db_destroy,
+
+ db_last
+};
+
+static int
+ldap_chain_db_func(
+ BackendDB *be,
+ enum db_which which
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+
+ int rc = 0;
+
+ if ( lc ) {
+ BI_db_func *func = (&lback->bi_db_open)[ which ];
+
+ if ( func != NULL && lc->lc_common_li != NULL ) {
+ BackendDB db = *be;
+
+ db.bd_info = lback;
+ db.be_private = lc->lc_common_li;
+
+ rc = func( &db, NULL );
+
+ if ( rc != 0 ) {
+ return rc;
+ }
+
+ if ( lc->lc_lai.lai_tree != NULL ) {
+ TAvlnode *edge = ldap_tavl_end( lc->lc_lai.lai_tree, TAVL_DIR_LEFT );
+ while ( edge ) {
+ TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
+ ldapinfo_t *li = (ldapinfo_t *)edge->avl_data;
+ db.be_private = (void *)li;
+ rc = func( &db, NULL );
+ if ( rc == 1 ) {
+ break;
+ }
+ edge = next;
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+static int
+ldap_chain_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+ slap_mask_t monitoring;
+ int rc = 0;
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
+ if ( rc != 0 ) {
+ return rc;
+ }
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+ if ( lc->lc_common_li == NULL ) {
+ void *be_private = be->be_private;
+ ldap_chain_db_init_common( be );
+ lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
+ be->be_private = be_private;
+ }
+
+ /* filter out and restore monitoring */
+ monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
+ SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
+ rc = ldap_chain_db_func( be, db_open );
+ SLAP_DBFLAGS( be ) |= monitoring;
+
+ return rc;
+}
+
+static int
+ldap_chain_db_close(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+#ifdef SLAP_CONFIG_DELETE
+ overlay_unregister_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
+#endif /* SLAP_CONFIG_DELETE */
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+ return ldap_chain_db_func( be, db_close );
+}
+
+static int
+ldap_chain_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+
+ int rc;
+
+ rc = ldap_chain_db_func( be, db_destroy );
+
+ if ( lc ) {
+ ldap_tavl_free( lc->lc_lai.lai_tree, NULL );
+ ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
+ ch_free( lc );
+ }
+
+ return rc;
+}
+
+/*
+ * inits one instance of the slapd-ldap backend, and stores
+ * the private info in be_private of the arg
+ */
+static int
+ldap_chain_db_init_common(
+ BackendDB *be )
+{
+ BackendInfo *bi = be->bd_info;
+ ldapinfo_t *li;
+ int rc;
+
+ be->bd_info = lback;
+ be->be_private = NULL;
+ rc = lback->bi_db_init( be, NULL );
+ if ( rc != 0 ) {
+ return rc;
+ }
+ li = (ldapinfo_t *)be->be_private;
+ li->li_urllist_f = NULL;
+ li->li_urllist_p = NULL;
+
+ be->bd_info = bi;
+
+ return 0;
+}
+
+/*
+ * inits one instance of the slapd-ldap backend, stores
+ * the private info in be_private of the arg and fills
+ * selected fields with data from the template.
+ *
+ * NOTE: add checks about the other fields of the template,
+ * which are ignored and SHOULD NOT be configured by the user.
+ */
+static int
+ldap_chain_db_init_one(
+ BackendDB *be )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+
+ BackendInfo *bi = be->bd_info;
+ ldapinfo_t *li;
+
+ slap_op_t t;
+
+ be->bd_info = lback;
+ be->be_private = NULL;
+ t = lback->bi_db_init( be, NULL );
+ if ( t != 0 ) {
+ return t;
+ }
+ li = (ldapinfo_t *)be->be_private;
+ li->li_urllist_f = NULL;
+ li->li_urllist_p = NULL;
+
+ /* copy common data */
+ li->li_nretries = lc->lc_common_li->li_nretries;
+ li->li_flags = lc->lc_common_li->li_flags;
+ li->li_version = lc->lc_common_li->li_version;
+ for ( t = 0; t < SLAP_OP_LAST; t++ ) {
+ li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
+ }
+ be->bd_info = bi;
+
+ return 0;
+}
+
+static int
+ldap_chain_db_open_one(
+ BackendDB *be )
+{
+ if ( SLAP_DBMONITORING( be ) ) {
+ ldapinfo_t *li = (ldapinfo_t *)be->be_private;
+
+ if ( li->li_uri == NULL ) {
+ ber_str2bv( "cn=Common Connections", 0, 1,
+ &li->li_monitor_info.lmi_conn_rdn );
+ ber_str2bv( "cn=Operations on Common Connections", 0, 1,
+ &li->li_monitor_info.lmi_conn_rdn );
+
+ } else {
+ char *ptr;
+
+ li->li_monitor_info.lmi_conn_rdn.bv_len
+ = STRLENOF( "cn=" ) + strlen( li->li_uri );
+ ptr = li->li_monitor_info.lmi_conn_rdn.bv_val
+ = ch_malloc( li->li_monitor_info.lmi_conn_rdn.bv_len + 1 );
+ ptr = lutil_strcopy( ptr, "cn=" );
+ ptr = lutil_strcopy( ptr, li->li_uri );
+ ptr[ 0 ] = '\0';
+
+ li->li_monitor_info.lmi_ops_rdn.bv_len
+ = STRLENOF( "cn=Operations on " ) + strlen( li->li_uri );
+ ptr = li->li_monitor_info.lmi_ops_rdn.bv_val
+ = ch_malloc( li->li_monitor_info.lmi_ops_rdn.bv_len + 1 );
+ ptr = lutil_strcopy( ptr, "cn=Operations on " );
+ ptr = lutil_strcopy( ptr, li->li_uri );
+ ptr[ 0 ] = '\0';
+ }
+ }
+
+ return lback->bi_db_open( be, NULL );
+}
+
+static int
+ldap_chain_connection_destroy(
+ BackendDB *be,
+ Connection *conn
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ ldap_chain_t *lc = (ldap_chain_t *)on->on_bi.bi_private;
+ void *private = be->be_private;
+ TAvlnode *edge;
+ int rc;
+
+ be->be_private = NULL;
+ ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
+ edge = ldap_tavl_end( lc->lc_lai.lai_tree, TAVL_DIR_LEFT );
+ while ( edge ) {
+ TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
+ ldapinfo_t *li = (ldapinfo_t *)edge->avl_data;
+ be->be_private = (void *)li;
+ rc = lback->bi_connection_destroy( be, conn );
+ if ( rc == 1 ) {
+ break;
+ }
+ edge = next;
+ }
+
+
+ ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
+ be->be_private = private;
+
+ return rc;
+}
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+static int
+ldap_chain_parse_ctrl(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ ber_tag_t tag;
+ BerElement *ber;
+ ber_int_t mode,
+ behavior;
+
+ if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "Chaining behavior control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "Chaining behavior control specified with pagedResults control";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
+ mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
+
+ } else {
+ ber_len_t len;
+
+ /* Parse the control value
+ * ChainingBehavior ::= SEQUENCE {
+ * resolveBehavior Behavior OPTIONAL,
+ * continuationBehavior Behavior OPTIONAL }
+ *
+ * Behavior :: = ENUMERATED {
+ * chainingPreferred (0),
+ * chainingRequired (1),
+ * referralsPreferred (2),
+ * referralsRequired (3) }
+ */
+
+ ber = ber_init( &ctrl->ldctl_value );
+ if( ber == NULL ) {
+ rs->sr_text = "internal error";
+ return LDAP_OTHER;
+ }
+
+ tag = ber_scanf( ber, "{e" /* } */, &behavior );
+ /* FIXME: since the whole SEQUENCE is optional,
+ * should we accept no enumerations at all? */
+ if ( tag != LBER_ENUMERATED ) {
+ rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ switch ( behavior ) {
+ case LDAP_CHAINING_PREFERRED:
+ mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
+ break;
+
+ case LDAP_CHAINING_REQUIRED:
+ mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
+ break;
+
+ case LDAP_REFERRALS_PREFERRED:
+ mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
+ break;
+
+ case LDAP_REFERRALS_REQUIRED:
+ mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
+ break;
+
+ default:
+ rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LBER_ENUMERATED ) {
+ tag = ber_scanf( ber, "e", &behavior );
+ if ( tag == LBER_ERROR ) {
+ rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+
+ if ( tag == LBER_DEFAULT ) {
+ mode |= SLAP_CH_CONTINUATION_DEFAULT;
+
+ } else {
+ switch ( behavior ) {
+ case LDAP_CHAINING_PREFERRED:
+ mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
+ break;
+
+ case LDAP_CHAINING_REQUIRED:
+ mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
+ break;
+
+ case LDAP_REFERRALS_PREFERRED:
+ mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
+ break;
+
+ case LDAP_REFERRALS_REQUIRED:
+ mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
+ break;
+
+ default:
+ rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+
+ if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
+ rs->sr_text = "Chaining behavior control: decoding error";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ (void) ber_free( ber, 1 );
+ }
+
+ op->o_chaining = mode | ( ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL );
+
+ return LDAP_SUCCESS;
+}
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+int
+chain_initialize( void )
+{
+ int rc;
+
+ /* Make sure we don't exceed the bits reserved for userland */
+ config_check_userland( CH_LAST );
+
+ /* olcDatabaseDummy is defined in slapd, and Windows
+ will not let us initialize a struct element with a data pointer
+ from another library, so we have to initialize this element
+ "by hand". */
+ chainocs[1].co_table = olcDatabaseDummy;
+
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
+ /* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
+ ldap_chain_parse_ctrl, &sc_chainingBehavior );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "slapd-chain: "
+ "unable to register chaining behavior control: %d.\n",
+ rc );
+ return rc;
+ }
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+
+ ldapchain.on_bi.bi_type = "chain";
+ ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
+ ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
+ ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
+ ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
+ ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
+
+ ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
+
+ ldapchain.on_response = ldap_chain_response;
+
+ ldapchain.on_bi.bi_cf_ocs = chainocs;
+
+ rc = config_register_schema( chaincfg, chainocs );
+ if ( rc ) {
+ return rc;
+ }
+
+ return overlay_register( &ldapchain );
+}
+
diff --git a/servers/slapd/back-ldap/compare.c b/servers/slapd/back-ldap/compare.c
new file mode 100644
index 0000000..1e410db
--- /dev/null
+++ b/servers/slapd/back-ldap/compare.c
@@ -0,0 +1,88 @@
+/* compare.c - ldap backend compare function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-ldap.h"
+
+int
+ldap_back_compare(
+ Operation *op,
+ SlapReply *rs )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ ldapconn_t *lc = NULL;
+ ber_int_t msgid;
+ ldap_back_send_t retrying = LDAP_BACK_RETRYING;
+ LDAPControl **ctrls = NULL;
+ int rc = LDAP_SUCCESS;
+
+ if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ lc = NULL;
+ goto cleanup;
+ }
+
+retry:
+ ctrls = op->o_ctrls;
+ rc = ldap_back_controls_add( op, rs, lc, &ctrls );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ rs->sr_err = ldap_compare_ext( lc->lc_ld, op->o_req_dn.bv_val,
+ op->orc_ava->aa_desc->ad_cname.bv_val,
+ &op->orc_ava->aa_value,
+ ctrls, NULL, &msgid );
+ rc = ldap_back_op_result( lc, op, rs, msgid,
+ li->li_timeout[ SLAP_OP_COMPARE ],
+ ( LDAP_BACK_SENDRESULT | retrying ) );
+ if ( rc == LDAP_UNAVAILABLE && retrying ) {
+ retrying &= ~LDAP_BACK_RETRYING;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ /* if the identity changed, there might be need to re-authz */
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+ goto retry;
+ }
+ }
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_COMPARE ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+cleanup:
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+
+ if ( lc != NULL ) {
+ ldap_back_release_conn( li, lc );
+ }
+
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-ldap/config.c b/servers/slapd/back-ldap/config.c
new file mode 100644
index 0000000..fb97e8e
--- /dev/null
+++ b/servers/slapd/back-ldap/config.c
@@ -0,0 +1,2214 @@
+/* config.c - ldap backend configuration file routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "back-ldap.h"
+#include "lutil.h"
+#include "ldif.h"
+
+static SLAP_EXTOP_MAIN_FN ldap_back_exop_whoami;
+
+static ConfigDriver ldap_back_cf_gen;
+static ConfigDriver ldap_pbind_cf_gen;
+
+enum {
+ LDAP_BACK_CFG_URI = 1,
+ LDAP_BACK_CFG_TLS,
+ LDAP_BACK_CFG_ACL_BIND,
+ LDAP_BACK_CFG_IDASSERT_AUTHZFROM,
+ LDAP_BACK_CFG_IDASSERT_PASSTHRU,
+ LDAP_BACK_CFG_IDASSERT_BIND,
+ LDAP_BACK_CFG_REBIND,
+ LDAP_BACK_CFG_CHASE,
+ LDAP_BACK_CFG_T_F,
+ LDAP_BACK_CFG_WHOAMI,
+ LDAP_BACK_CFG_TIMEOUT,
+ LDAP_BACK_CFG_IDLE_TIMEOUT,
+ LDAP_BACK_CFG_CONN_TTL,
+ LDAP_BACK_CFG_NETWORK_TIMEOUT,
+ LDAP_BACK_CFG_VERSION,
+ LDAP_BACK_CFG_SINGLECONN,
+ LDAP_BACK_CFG_USETEMP,
+ LDAP_BACK_CFG_CONNPOOLMAX,
+ LDAP_BACK_CFG_CANCEL,
+ LDAP_BACK_CFG_QUARANTINE,
+ LDAP_BACK_CFG_ST_REQUEST,
+ LDAP_BACK_CFG_NOREFS,
+ LDAP_BACK_CFG_NOUNDEFFILTER,
+ LDAP_BACK_CFG_ONERR,
+
+ LDAP_BACK_CFG_KEEPALIVE,
+ LDAP_BACK_CFG_TCP_USER_TIMEOUT,
+
+ LDAP_BACK_CFG_OMIT_UNKNOWN_SCHEMA,
+
+ LDAP_BACK_CFG_LAST
+};
+
+static ConfigTable ldapcfg[] = {
+ { "uri", "uri", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_URI,
+ ldap_back_cf_gen, "( OLcfgDbAt:0.14 "
+ "NAME 'olcDbURI' "
+ "DESC 'URI (list) for remote DSA' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "tls", "what", 2, 0, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_TLS,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.1 "
+ "NAME 'olcDbStartTLS' "
+ "DESC 'StartTLS' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "acl-bind", "args", 2, 0, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_ACL_BIND,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.4 "
+ "NAME 'olcDbACLBind' "
+ "DESC 'Remote ACL administrative identity auth bind configuration' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "idassert-bind", "args", 2, 0, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_IDASSERT_BIND,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.7 "
+ "NAME 'olcDbIDAssertBind' "
+ "DESC 'Remote Identity Assertion administrative identity auth bind configuration' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "idassert-authzFrom", "authzRule", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_IDASSERT_AUTHZFROM,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.9 "
+ "NAME 'olcDbIDAssertAuthzFrom' "
+ "DESC 'Remote Identity Assertion authz rules' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "X-ORDERED 'VALUES' )",
+ NULL, NULL },
+ { "rebind-as-user", "true|FALSE", 1, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_REBIND,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.10 "
+ "NAME 'olcDbRebindAsUser' "
+ "DESC 'Rebind as user' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "chase-referrals", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_CHASE,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.11 "
+ "NAME 'olcDbChaseReferrals' "
+ "DESC 'Chase referrals' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "t-f-support", "true|FALSE|discover", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_T_F,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.12 "
+ "NAME 'olcDbTFSupport' "
+ "DESC 'Absolute filters support' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "proxy-whoami", "true|FALSE", 1, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_WHOAMI,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.13 "
+ "NAME 'olcDbProxyWhoAmI' "
+ "DESC 'Proxy whoAmI exop' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "timeout", "timeout(list)", 2, 0, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_TIMEOUT,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.14 "
+ "NAME 'olcDbTimeout' "
+ "DESC 'Per-operation timeouts' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "idle-timeout", "timeout", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_IDLE_TIMEOUT,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.15 "
+ "NAME 'olcDbIdleTimeout' "
+ "DESC 'connection idle timeout' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "conn-ttl", "ttl", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_CONN_TTL,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.16 "
+ "NAME 'olcDbConnTtl' "
+ "DESC 'connection ttl' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "network-timeout", "timeout", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_NETWORK_TIMEOUT,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.17 "
+ "NAME 'olcDbNetworkTimeout' "
+ "DESC 'connection network timeout' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "protocol-version", "version", 2, 2, 0,
+ ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_VERSION,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.18 "
+ "NAME 'olcDbProtocolVersion' "
+ "DESC 'protocol version' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "single-conn", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_SINGLECONN,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.19 "
+ "NAME 'olcDbSingleConn' "
+ "DESC 'cache a single connection per identity' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "cancel", "ABANDON|ignore|exop", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_CANCEL,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.20 "
+ "NAME 'olcDbCancel' "
+ "DESC 'abandon/ignore/exop operations when appropriate' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "quarantine", "retrylist", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_QUARANTINE,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.21 "
+ "NAME 'olcDbQuarantine' "
+ "DESC 'Quarantine database if connection fails and retry according to rule' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "use-temporary-conn", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_USETEMP,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.22 "
+ "NAME 'olcDbUseTemporaryConn' "
+ "DESC 'Use temporary connections if the cached one is busy' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "conn-pool-max", "<n>", 2, 2, 0,
+ ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_CONNPOOLMAX,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.23 "
+ "NAME 'olcDbConnectionPoolMax' "
+ "DESC 'Max size of privileged connections pool' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ { "session-tracking-request", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_ST_REQUEST,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.24 "
+ "NAME 'olcDbSessionTrackingRequest' "
+ "DESC 'Add session tracking control to proxied requests' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+ { "norefs", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_NOREFS,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.25 "
+ "NAME 'olcDbNoRefs' "
+ "DESC 'Do not return search reference responses' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "noundeffilter", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_NOUNDEFFILTER,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.26 "
+ "NAME 'olcDbNoUndefFilter' "
+ "DESC 'Do not propagate undefined search filters' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "onerr", "CONTINUE|report|stop", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_ONERR,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.108 "
+ "NAME 'olcDbOnErr' "
+ "DESC 'error handling' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "idassert-passThru", "authzRule", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_IDASSERT_PASSTHRU,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.27 "
+ "NAME 'olcDbIDAssertPassThru' "
+ "DESC 'Remote Identity Assertion passthru rules' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "X-ORDERED 'VALUES' )",
+ NULL, NULL },
+ { "omit-unknown-schema", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_OMIT_UNKNOWN_SCHEMA,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.28 "
+ "NAME 'olcDbRemoveUnknownSchema' "
+ "DESC 'Omit unknown schema when returning search results' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "keepalive", "keepalive", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_KEEPALIVE,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.29 "
+ "NAME 'olcDbKeepalive' "
+ "DESC 'TCP keepalive' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "tcp-user-timeout", "milliseconds", 2, 2, 0,
+ ARG_MAGIC|ARG_UINT|LDAP_BACK_CFG_TCP_USER_TIMEOUT,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.30 "
+ "NAME 'olcDbTcpUserTimeout' "
+ "DESC 'TCP User Timeout' "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED,
+ NULL, NULL, NULL, NULL }
+};
+
+static ConfigOCs ldapocs[] = {
+ { "( OLcfgDbOc:3.1 "
+ "NAME 'olcLDAPConfig' "
+ "DESC 'LDAP backend configuration' "
+ "SUP olcDatabaseConfig "
+ "MAY ( olcDbURI "
+ "$ olcDbStartTLS "
+ "$ olcDbACLBind "
+ "$ olcDbIDAssertBind "
+ "$ olcDbIDAssertAuthzFrom "
+ "$ olcDbIDAssertPassThru "
+ "$ olcDbRebindAsUser "
+ "$ olcDbChaseReferrals "
+ "$ olcDbTFSupport "
+ "$ olcDbProxyWhoAmI "
+ "$ olcDbTimeout "
+ "$ olcDbIdleTimeout "
+ "$ olcDbConnTtl "
+ "$ olcDbNetworkTimeout "
+ "$ olcDbProtocolVersion "
+ "$ olcDbSingleConn "
+ "$ olcDbCancel "
+ "$ olcDbQuarantine "
+ "$ olcDbUseTemporaryConn "
+ "$ olcDbConnectionPoolMax "
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ "$ olcDbSessionTrackingRequest "
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+ "$ olcDbNoRefs "
+ "$ olcDbNoUndefFilter "
+ "$ olcDbOnErr "
+ "$ olcDbKeepalive "
+ ") )",
+ Cft_Database, ldapcfg},
+ { NULL, 0, NULL }
+};
+
+static ConfigTable pbindcfg[] = {
+ { "uri", "uri", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_URI,
+ ldap_pbind_cf_gen, "( OLcfgDbAt:0.14 "
+ "NAME 'olcDbURI' "
+ "DESC 'URI (list) for remote DSA' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "tls", "what", 2, 0, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_TLS,
+ ldap_pbind_cf_gen, "( OLcfgDbAt:3.1 "
+ "NAME 'olcDbStartTLS' "
+ "DESC 'StartTLS' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "network-timeout", "timeout", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_NETWORK_TIMEOUT,
+ ldap_pbind_cf_gen, "( OLcfgDbAt:3.17 "
+ "NAME 'olcDbNetworkTimeout' "
+ "DESC 'connection network timeout' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "quarantine", "retrylist", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_QUARANTINE,
+ ldap_pbind_cf_gen, "( OLcfgDbAt:3.21 "
+ "NAME 'olcDbQuarantine' "
+ "DESC 'Quarantine database if connection fails and retry according to rule' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED,
+ NULL, NULL, NULL, NULL }
+};
+
+static ConfigOCs pbindocs[] = {
+ { "( OLcfgOvOc:3.3 "
+ "NAME 'olcPBindConfig' "
+ "DESC 'Proxy Bind configuration' "
+ "SUP olcOverlayConfig "
+ "MUST olcDbURI "
+ "MAY ( olcDbStartTLS "
+ "$ olcDbNetworkTimeout "
+ "$ olcDbQuarantine "
+ ") )",
+ Cft_Overlay, pbindcfg},
+ { NULL, 0, NULL }
+};
+
+static slap_verbmasks idassert_mode[] = {
+ { BER_BVC("self"), LDAP_BACK_IDASSERT_SELF },
+ { BER_BVC("anonymous"), LDAP_BACK_IDASSERT_ANONYMOUS },
+ { BER_BVC("none"), LDAP_BACK_IDASSERT_NOASSERT },
+ { BER_BVC("legacy"), LDAP_BACK_IDASSERT_LEGACY },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks tls_mode[] = {
+ { BER_BVC( "propagate" ), LDAP_BACK_F_TLS_PROPAGATE_MASK },
+ { BER_BVC( "try-propagate" ), LDAP_BACK_F_PROPAGATE_TLS },
+ { BER_BVC( "start" ), LDAP_BACK_F_TLS_USE_MASK },
+ { BER_BVC( "try-start" ), LDAP_BACK_F_USE_TLS },
+ { BER_BVC( "ldaps" ), LDAP_BACK_F_TLS_LDAPS },
+ { BER_BVC( "none" ), LDAP_BACK_F_NONE },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks t_f_mode[] = {
+ { BER_BVC( "yes" ), LDAP_BACK_F_T_F },
+ { BER_BVC( "discover" ), LDAP_BACK_F_T_F_DISCOVER },
+ { BER_BVC( "no" ), LDAP_BACK_F_NONE },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks cancel_mode[] = {
+ { BER_BVC( "ignore" ), LDAP_BACK_F_CANCEL_IGNORE },
+ { BER_BVC( "exop" ), LDAP_BACK_F_CANCEL_EXOP },
+ { BER_BVC( "exop-discover" ), LDAP_BACK_F_CANCEL_EXOP_DISCOVER },
+ { BER_BVC( "abandon" ), LDAP_BACK_F_CANCEL_ABANDON },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks onerr_mode[] = {
+ { BER_BVC( "stop" ), LDAP_BACK_F_ONERR_STOP },
+ { BER_BVC( "report" ), LDAP_BACK_F_ONERR_STOP }, /* same behavior */
+ { BER_BVC( "continue" ), LDAP_BACK_F_NONE },
+ { BER_BVNULL, 0 }
+};
+
+/* see enum in slap.h */
+static slap_cf_aux_table timeout_table[] = {
+ { BER_BVC("bind="), SLAP_OP_BIND * sizeof( time_t ), 'u', 0, NULL },
+ /* unbind makes no sense */
+ { BER_BVC("add="), SLAP_OP_ADD * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("delete="), SLAP_OP_DELETE * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("modrdn="), SLAP_OP_MODRDN * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("modify="), SLAP_OP_MODIFY * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("compare="), SLAP_OP_COMPARE * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("search="), SLAP_OP_SEARCH * sizeof( time_t ), 'u', 0, NULL },
+ /* abandon makes little sense */
+#if 0 /* not implemented yet */
+ { BER_BVC("extended="), SLAP_OP_EXTENDED * sizeof( time_t ), 'u', 0, NULL },
+#endif
+ { BER_BVNULL, 0, 0, 0, NULL }
+};
+
+int
+slap_retry_info_parse(
+ char *in,
+ slap_retry_info_t *ri,
+ char *buf,
+ ber_len_t buflen )
+{
+ char **retrylist = NULL;
+ int rc = 0;
+ int i;
+
+ slap_str2clist( &retrylist, in, " ;" );
+ if ( retrylist == NULL ) {
+ return 1;
+ }
+
+ for ( i = 0; retrylist[ i ] != NULL; i++ )
+ /* count */ ;
+
+ ri->ri_interval = ch_calloc( sizeof( time_t ), i + 1 );
+ ri->ri_num = ch_calloc( sizeof( int ), i + 1 );
+
+ for ( i = 0; retrylist[ i ] != NULL; i++ ) {
+ unsigned long t;
+ char *sep = strchr( retrylist[ i ], ',' );
+
+ if ( sep == NULL ) {
+ snprintf( buf, buflen,
+ "missing comma in retry pattern #%d \"%s\"",
+ i, retrylist[ i ] );
+ rc = 1;
+ goto done;
+ }
+
+ *sep++ = '\0';
+
+ if ( lutil_parse_time( retrylist[ i ], &t ) ) {
+ snprintf( buf, buflen,
+ "unable to parse interval #%d \"%s\"",
+ i, retrylist[ i ] );
+ rc = 1;
+ goto done;
+ }
+ ri->ri_interval[ i ] = (time_t)t;
+
+ if ( strcmp( sep, "+" ) == 0 ) {
+ if ( retrylist[ i + 1 ] != NULL ) {
+ snprintf( buf, buflen,
+ "extra cruft after retry pattern "
+ "#%d \"%s,+\" with \"forever\" mark",
+ i, retrylist[ i ] );
+ rc = 1;
+ goto done;
+ }
+ ri->ri_num[ i ] = SLAP_RETRYNUM_FOREVER;
+
+ } else if ( lutil_atoi( &ri->ri_num[ i ], sep ) ) {
+ snprintf( buf, buflen,
+ "unable to parse retry num #%d \"%s\"",
+ i, sep );
+ rc = 1;
+ goto done;
+ }
+ }
+
+ ri->ri_num[ i ] = SLAP_RETRYNUM_TAIL;
+
+ ri->ri_idx = 0;
+ ri->ri_count = 0;
+ ri->ri_last = (time_t)(-1);
+
+done:;
+ ldap_charray_free( retrylist );
+
+ if ( rc ) {
+ slap_retry_info_destroy( ri );
+ }
+
+ return rc;
+}
+
+int
+slap_retry_info_unparse(
+ slap_retry_info_t *ri,
+ struct berval *bvout )
+{
+ char buf[ BUFSIZ * 2 ],
+ *ptr = buf;
+ int i, len, restlen = (int) sizeof( buf );
+ struct berval bv;
+
+ assert( ri != NULL );
+ assert( bvout != NULL );
+
+ BER_BVZERO( bvout );
+
+ for ( i = 0; ri->ri_num[ i ] != SLAP_RETRYNUM_TAIL; i++ ) {
+ if ( i > 0 ) {
+ if ( --restlen <= 0 ) {
+ return 1;
+ }
+ *ptr++ = ';';
+ }
+
+ if ( lutil_unparse_time( ptr, restlen, ri->ri_interval[i] ) < 0 ) {
+ return 1;
+ }
+ len = (int) strlen( ptr );
+ if ( (restlen -= len + 1) <= 0 ) {
+ return 1;
+ }
+ ptr += len;
+ *ptr++ = ',';
+
+ if ( ri->ri_num[i] == SLAP_RETRYNUM_FOREVER ) {
+ if ( --restlen <= 0 ) {
+ return 1;
+ }
+ *ptr++ = '+';
+
+ } else {
+ len = snprintf( ptr, restlen, "%d", ri->ri_num[i] );
+ if ( (restlen -= len) <= 0 || len < 0 ) {
+ return 1;
+ }
+ ptr += len;
+ }
+ }
+
+ bv.bv_val = buf;
+ bv.bv_len = ptr - buf;
+ ber_dupbv( bvout, &bv );
+
+ return 0;
+}
+
+void
+slap_retry_info_destroy(
+ slap_retry_info_t *ri )
+{
+ assert( ri != NULL );
+
+ assert( ri->ri_interval != NULL );
+ ch_free( ri->ri_interval );
+ ri->ri_interval = NULL;
+
+ assert( ri->ri_num != NULL );
+ ch_free( ri->ri_num );
+ ri->ri_num = NULL;
+}
+
+int
+slap_idassert_authzfrom_parse( ConfigArgs *c, slap_idassert_t *si )
+{
+ struct berval bv;
+ struct berval in;
+ int rc;
+
+ if ( strcmp( c->argv[ 1 ], "*" ) == 0
+ || strcmp( c->argv[ 1 ], "dn:*" ) == 0
+ || strcasecmp( c->argv[ 1 ], "dn.regex:.*" ) == 0 )
+ {
+ if ( si->si_authz != NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <authz>\": "
+ "\"%s\" conflicts with existing authz rules",
+ c->argv[ 0 ], c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ si->si_flags |= LDAP_BACK_AUTH_AUTHZ_ALL;
+
+ return 0;
+
+ } else if ( ( si->si_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <authz>\": "
+ "\"<authz>\" conflicts with \"*\"", c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ ber_str2bv( c->argv[ 1 ], 0, 0, &in );
+ rc = authzNormalize( 0, NULL, NULL, &in, &bv, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <authz>\": "
+ "invalid syntax", c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( c->valx == -1 ) {
+ ber_bvarray_add( &si->si_authz, &bv );
+
+ } else {
+ int i = 0;
+ if ( si->si_authz != NULL ) {
+ for ( ; !BER_BVISNULL( &si->si_authz[ i ] ); i++ )
+ ;
+ }
+
+ if ( i <= c->valx ) {
+ ber_bvarray_add( &si->si_authz, &bv );
+
+ } else {
+ BerVarray tmp = ber_memrealloc( si->si_authz,
+ sizeof( struct berval )*( i + 2 ) );
+ if ( tmp == NULL ) {
+ return -1;
+ }
+ si->si_authz = tmp;
+ for ( ; i > c->valx; i-- ) {
+ si->si_authz[ i ] = si->si_authz[ i - 1 ];
+ }
+ si->si_authz[ c->valx ] = bv;
+ }
+ }
+
+ return 0;
+}
+
+static int
+slap_idassert_passthru_parse( ConfigArgs *c, slap_idassert_t *si )
+{
+ struct berval bv;
+ struct berval in;
+ int rc;
+
+ ber_str2bv( c->argv[ 1 ], 0, 0, &in );
+ rc = authzNormalize( 0, NULL, NULL, &in, &bv, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <authz>\": "
+ "invalid syntax", c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( c->valx == -1 ) {
+ ber_bvarray_add( &si->si_passthru, &bv );
+
+ } else {
+ int i = 0;
+ if ( si->si_passthru != NULL ) {
+ for ( ; !BER_BVISNULL( &si->si_passthru[ i ] ); i++ )
+ ;
+ }
+
+ if ( i <= c->valx ) {
+ ber_bvarray_add( &si->si_passthru, &bv );
+
+ } else {
+ BerVarray tmp = ber_memrealloc( si->si_passthru,
+ sizeof( struct berval )*( i + 2 ) );
+ if ( tmp == NULL ) {
+ return -1;
+ }
+ si->si_passthru = tmp;
+ for ( ; i > c->valx; i-- ) {
+ si->si_passthru[ i ] = si->si_passthru[ i - 1 ];
+ }
+ si->si_passthru[ c->valx ] = bv;
+ }
+ }
+
+ return 0;
+}
+
+int
+slap_idassert_parse( ConfigArgs *c, slap_idassert_t *si )
+{
+ int i;
+
+ /* set default */
+ si->si_mode = LDAP_BACK_IDASSERT_LEGACY;
+
+ for ( i = 1; i < c->argc; i++ ) {
+ if ( strncasecmp( c->argv[ i ], "mode=", STRLENOF( "mode=" ) ) == 0 ) {
+ char *argvi = c->argv[ i ] + STRLENOF( "mode=" );
+ int j;
+
+ j = verb_to_mask( argvi, idassert_mode );
+ if ( BER_BVISNULL( &idassert_mode[ j ].word ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <args>\": "
+ "unknown mode \"%s\"",
+ c->argv[0], argvi );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ si->si_mode = idassert_mode[ j ].mask;
+
+ } else if ( strncasecmp( c->argv[ i ], "authz=", STRLENOF( "authz=" ) ) == 0 ) {
+ char *argvi = c->argv[ i ] + STRLENOF( "authz=" );
+
+ if ( strcasecmp( argvi, "native" ) == 0 ) {
+ if ( si->si_bc.sb_method != LDAP_AUTH_SASL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <args>\": "
+ "authz=\"native\" incompatible "
+ "with auth method", c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ si->si_flags |= LDAP_BACK_AUTH_NATIVE_AUTHZ;
+
+ } else if ( strcasecmp( argvi, "proxyAuthz" ) == 0 ) {
+ si->si_flags &= ~LDAP_BACK_AUTH_NATIVE_AUTHZ;
+
+ } else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <args>\": "
+ "unknown authz \"%s\"",
+ c->argv[0], argvi );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ } else if ( strncasecmp( c->argv[ i ], "flags=", STRLENOF( "flags=" ) ) == 0 ) {
+ char *argvi = c->argv[ i ] + STRLENOF( "flags=" );
+ char **flags = ldap_str2charray( argvi, "," );
+ int j, err = 0;
+
+ if ( flags == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <args>\": "
+ "unable to parse flags \"%s\"",
+ c->argv[0], argvi );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ for ( j = 0; flags[ j ] != NULL; j++ ) {
+
+ if ( strcasecmp( flags[ j ], "override" ) == 0 ) {
+ si->si_flags |= LDAP_BACK_AUTH_OVERRIDE;
+
+ } else if ( strcasecmp( flags[ j ], "prescriptive" ) == 0 ) {
+ si->si_flags |= LDAP_BACK_AUTH_PRESCRIPTIVE;
+
+ } else if ( strcasecmp( flags[ j ], "non-prescriptive" ) == 0 ) {
+ si->si_flags &= ( ~LDAP_BACK_AUTH_PRESCRIPTIVE );
+
+ } else if ( strcasecmp( flags[ j ], "obsolete-proxy-authz" ) == 0 ) {
+ if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <args>\": "
+ "\"obsolete-proxy-authz\" flag "
+ "incompatible with previously issued \"obsolete-encoding-workaround\" flag.",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ err = 1;
+ break;
+
+ } else {
+ si->si_flags |= LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ;
+ }
+
+ } else if ( strcasecmp( flags[ j ], "obsolete-encoding-workaround" ) == 0 ) {
+ if ( si->si_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <args>\": "
+ "\"obsolete-encoding-workaround\" flag "
+ "incompatible with previously issued \"obsolete-proxy-authz\" flag.",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ err = 1;
+ break;
+
+ } else {
+ si->si_flags |= LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND;
+ }
+
+ } else if ( strcasecmp( flags[ j ], "proxy-authz-critical" ) == 0 ) {
+ si->si_flags |= LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL;
+
+ } else if ( strcasecmp( flags[ j ], "proxy-authz-non-critical" ) == 0 ) {
+ si->si_flags &= ~LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL;
+
+ } else if ( strcasecmp( flags[ j ], "dn-none" ) == 0 ) {
+ si->si_flags &= ~LDAP_BACK_AUTH_DN_MASK;
+
+ } else if ( strcasecmp( flags[ j ], "dn-authzid" ) == 0 ) {
+ si->si_flags &= ~LDAP_BACK_AUTH_DN_MASK;
+ si->si_flags |= LDAP_BACK_AUTH_DN_AUTHZID;
+
+ } else if ( strcasecmp( flags[ j ], "dn-whoami" ) == 0 ) {
+ si->si_flags &= ~LDAP_BACK_AUTH_DN_MASK;
+ si->si_flags |= LDAP_BACK_AUTH_DN_WHOAMI;
+
+ } else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <args>\": "
+ "unknown flag \"%s\"",
+ c->argv[0], flags[ j ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ err = 1;
+ break;
+ }
+ }
+
+ ldap_charray_free( flags );
+ if ( err ) {
+ return 1;
+ }
+
+ } else if ( bindconf_parse( c->argv[ i ], &si->si_bc ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <args>\": "
+ "unable to parse field \"%s\"",
+ c->argv[0], c->argv[ i ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ }
+
+ if ( si->si_bc.sb_method == LDAP_AUTH_SIMPLE ) {
+ if ( BER_BVISNULL( &si->si_bc.sb_binddn )
+ || BER_BVISNULL( &si->si_bc.sb_cred ) )
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s <args>\": "
+ "SIMPLE needs \"binddn\" and \"credentials\"", c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ } else if ( si->si_bc.sb_method == LDAP_AUTH_SASL ) {
+ if ( BER_BVISNULL( &si->si_bc.sb_binddn ) &&
+ !(si->si_flags & LDAP_BACK_AUTH_DN_MASK) )
+ {
+ static struct berval authid = BER_BVC("cn=auth");
+ ber_dupbv( &si->si_bc.sb_binddn, &authid );
+ }
+ }
+
+ bindconf_tls_defaults( &si->si_bc );
+#ifdef HAVE_TLS
+ if ( si->si_bc.sb_tls_ctx ) {
+ ldap_pvt_tls_ctx_free( si->si_bc.sb_tls_ctx );
+ si->si_bc.sb_tls_ctx = NULL;
+ }
+#endif
+
+ return 0;
+}
+
+/* NOTE: temporary, until back-meta is ported to back-config */
+int
+slap_idassert_passthru_parse_cf( const char *fname, int lineno, const char *arg, slap_idassert_t *si )
+{
+ ConfigArgs c = { 0 };
+ char *argv[ 3 ];
+
+ snprintf( c.log, sizeof( c.log ), "%s: line %d", fname, lineno );
+ c.argc = 2;
+ c.argv = argv;
+ argv[ 0 ] = "idassert-passThru";
+ argv[ 1 ] = (char *)arg;
+ argv[ 2 ] = NULL;
+
+ return slap_idassert_passthru_parse( &c, si );
+}
+
+static int
+ldap_back_cf_gen( ConfigArgs *c )
+{
+ ldapinfo_t *li = ( ldapinfo_t * )c->be->be_private;
+ int rc = 0;
+ int i;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ struct berval bv = BER_BVNULL;
+
+ if ( li == NULL ) {
+ return 1;
+ }
+
+ switch( c->type ) {
+ case LDAP_BACK_CFG_URI:
+ if ( li->li_uri != NULL ) {
+ struct berval bv, bv2;
+
+ ber_str2bv( li->li_uri, 0, 0, &bv );
+ bv2.bv_len = bv.bv_len + STRLENOF( "\"\"" );
+ bv2.bv_val = ch_malloc( bv2.bv_len + 1 );
+ snprintf( bv2.bv_val, bv2.bv_len + 1,
+ "\"%s\"", bv.bv_val );
+ ber_bvarray_add( &c->rvalue_vals, &bv2 );
+
+ } else {
+ rc = 1;
+ }
+ break;
+
+ case LDAP_BACK_CFG_TLS: {
+ struct berval bc = BER_BVNULL, bv2;
+ enum_to_verb( tls_mode, ( li->li_flags & LDAP_BACK_F_TLS_MASK ), &bv );
+ assert( !BER_BVISNULL( &bv ) );
+ bindconf_tls_unparse( &li->li_tls, &bc );
+
+ if ( !BER_BVISEMPTY( &bc )) {
+ bv2.bv_len = bv.bv_len + bc.bv_len + 1;
+ bv2.bv_val = ch_malloc( bv2.bv_len + 1 );
+ strcpy( bv2.bv_val, bv.bv_val );
+ bv2.bv_val[bv.bv_len] = ' ';
+ strcpy( &bv2.bv_val[bv.bv_len + 1], bc.bv_val );
+ ber_bvarray_add( &c->rvalue_vals, &bv2 );
+
+ } else {
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ ber_memfree( bc.bv_val );
+ }
+ break;
+
+ case LDAP_BACK_CFG_ACL_BIND: {
+ int i;
+
+ if ( li->li_acl_authmethod == LDAP_AUTH_NONE ) {
+ return 1;
+ }
+
+ bindconf_unparse( &li->li_acl, &bv );
+
+ for ( i = 0; isspace( (unsigned char) bv.bv_val[ i ] ); i++ )
+ /* count spaces */ ;
+
+ if ( i ) {
+ bv.bv_len -= i;
+ AC_MEMCPY( bv.bv_val, &bv.bv_val[ i ],
+ bv.bv_len + 1 );
+ }
+
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ break;
+ }
+
+ case LDAP_BACK_CFG_IDASSERT_AUTHZFROM:
+ case LDAP_BACK_CFG_IDASSERT_PASSTHRU: {
+ BerVarray *bvp;
+ int i;
+ struct berval bv = BER_BVNULL;
+ char buf[SLAP_TEXT_BUFLEN];
+
+ switch ( c->type ) {
+ case LDAP_BACK_CFG_IDASSERT_AUTHZFROM: bvp = &li->li_idassert_authz; break;
+ case LDAP_BACK_CFG_IDASSERT_PASSTHRU: bvp = &li->li_idassert_passthru; break;
+ default: assert( 0 ); break;
+ }
+
+ if ( *bvp == NULL ) {
+ if ( bvp == &li->li_idassert_authz
+ && ( li->li_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) )
+ {
+ BER_BVSTR( &bv, "*" );
+ value_add_one( &c->rvalue_vals, &bv );
+
+ } else {
+ rc = 1;
+ }
+ break;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &((*bvp)[ i ]) ); i++ ) {
+ char *ptr;
+ int len = snprintf( buf, sizeof( buf ), SLAP_X_ORDERED_FMT, i );
+ bv.bv_len = ((*bvp)[ i ]).bv_len + len;
+ bv.bv_val = ber_memrealloc( bv.bv_val, bv.bv_len + 1 );
+ ptr = bv.bv_val;
+ ptr = lutil_strcopy( ptr, buf );
+ ptr = lutil_strncopy( ptr, ((*bvp)[ i ]).bv_val, ((*bvp)[ i ]).bv_len );
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ if ( bv.bv_val ) {
+ ber_memfree( bv.bv_val );
+ }
+ break;
+ }
+
+ case LDAP_BACK_CFG_IDASSERT_BIND: {
+ int i;
+ struct berval bc = BER_BVNULL;
+ char *ptr;
+
+ if ( li->li_idassert_authmethod == LDAP_AUTH_NONE ) {
+ return 1;
+ }
+
+ if ( li->li_idassert_authmethod != LDAP_AUTH_NONE ) {
+ ber_len_t len;
+
+ switch ( li->li_idassert_mode ) {
+ case LDAP_BACK_IDASSERT_OTHERID:
+ case LDAP_BACK_IDASSERT_OTHERDN:
+ break;
+
+ default: {
+ struct berval mode = BER_BVNULL;
+
+ enum_to_verb( idassert_mode, li->li_idassert_mode, &mode );
+ if ( BER_BVISNULL( &mode ) ) {
+ /* there's something wrong... */
+ assert( 0 );
+ rc = 1;
+
+ } else {
+ bv.bv_len = STRLENOF( "mode=" ) + mode.bv_len;
+ bv.bv_val = ch_malloc( bv.bv_len + 1 );
+
+ ptr = lutil_strcopy( bv.bv_val, "mode=" );
+ ptr = lutil_strcopy( ptr, mode.bv_val );
+ }
+ break;
+ }
+ }
+
+ if ( li->li_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) {
+ len = bv.bv_len + STRLENOF( "authz=native" );
+
+ if ( !BER_BVISEMPTY( &bv ) ) {
+ len += STRLENOF( " " );
+ }
+
+ bv.bv_val = ch_realloc( bv.bv_val, len + 1 );
+
+ ptr = &bv.bv_val[ bv.bv_len ];
+
+ if ( !BER_BVISEMPTY( &bv ) ) {
+ ptr = lutil_strcopy( ptr, " " );
+ }
+
+ (void)lutil_strcopy( ptr, "authz=native" );
+ }
+
+ len = bv.bv_len + STRLENOF( "flags=non-prescriptive,override,obsolete-encoding-workaround,proxy-authz-non-critical,dn-authzid" );
+ /* flags */
+ if ( !BER_BVISEMPTY( &bv ) ) {
+ len += STRLENOF( " " );
+ }
+
+ bv.bv_val = ch_realloc( bv.bv_val, len + 1 );
+
+ ptr = &bv.bv_val[ bv.bv_len ];
+
+ if ( !BER_BVISEMPTY( &bv ) ) {
+ ptr = lutil_strcopy( ptr, " " );
+ }
+
+ ptr = lutil_strcopy( ptr, "flags=" );
+
+ if ( li->li_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
+ ptr = lutil_strcopy( ptr, "prescriptive" );
+ } else {
+ ptr = lutil_strcopy( ptr, "non-prescriptive" );
+ }
+
+ if ( li->li_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
+ ptr = lutil_strcopy( ptr, ",override" );
+ }
+
+ if ( li->li_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) {
+ ptr = lutil_strcopy( ptr, ",obsolete-proxy-authz" );
+
+ } else if ( li->li_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) {
+ ptr = lutil_strcopy( ptr, ",obsolete-encoding-workaround" );
+ }
+
+ if ( li->li_idassert_flags & LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL ) {
+ ptr = lutil_strcopy( ptr, ",proxy-authz-critical" );
+
+ } else {
+ ptr = lutil_strcopy( ptr, ",proxy-authz-non-critical" );
+ }
+
+ switch ( li->li_idassert_flags & LDAP_BACK_AUTH_DN_MASK ) {
+ case LDAP_BACK_AUTH_DN_AUTHZID:
+ ptr = lutil_strcopy( ptr, ",dn-authzid" );
+ break;
+
+ case LDAP_BACK_AUTH_DN_WHOAMI:
+ ptr = lutil_strcopy( ptr, ",dn-whoami" );
+ break;
+
+ default:
+#if 0 /* implicit */
+ ptr = lutil_strcopy( ptr, ",dn-none" );
+#endif
+ break;
+ }
+
+ bv.bv_len = ( ptr - bv.bv_val );
+ /* end-of-flags */
+ }
+
+ bindconf_unparse( &li->li_idassert.si_bc, &bc );
+
+ if ( !BER_BVISNULL( &bv ) ) {
+ ber_len_t len = bv.bv_len + bc.bv_len;
+
+ bv.bv_val = ch_realloc( bv.bv_val, len + 1 );
+
+ assert( bc.bv_val[ 0 ] == ' ' );
+
+ ptr = lutil_strcopy( &bv.bv_val[ bv.bv_len ], bc.bv_val );
+ free( bc.bv_val );
+ bv.bv_len = ptr - bv.bv_val;
+
+ } else {
+ for ( i = 0; isspace( (unsigned char) bc.bv_val[ i ] ); i++ )
+ /* count spaces */ ;
+
+ if ( i ) {
+ bc.bv_len -= i;
+ AC_MEMCPY( bc.bv_val, &bc.bv_val[ i ], bc.bv_len + 1 );
+ }
+
+ bv = bc;
+ }
+
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+
+ break;
+ }
+
+ case LDAP_BACK_CFG_REBIND:
+ c->value_int = LDAP_BACK_SAVECRED( li );
+ break;
+
+ case LDAP_BACK_CFG_CHASE:
+ c->value_int = LDAP_BACK_CHASE_REFERRALS( li );
+ break;
+
+ case LDAP_BACK_CFG_T_F:
+ enum_to_verb( t_f_mode, (li->li_flags & LDAP_BACK_F_T_F_MASK2), &bv );
+ if ( BER_BVISNULL( &bv ) ) {
+ /* there's something wrong... */
+ assert( 0 );
+ rc = 1;
+
+ } else {
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
+
+ case LDAP_BACK_CFG_WHOAMI:
+ c->value_int = LDAP_BACK_PROXY_WHOAMI( li );
+ break;
+
+ case LDAP_BACK_CFG_TIMEOUT:
+ BER_BVZERO( &bv );
+
+ for ( i = 0; i < SLAP_OP_LAST; i++ ) {
+ if ( li->li_timeout[ i ] != 0 ) {
+ break;
+ }
+ }
+
+ if ( i == SLAP_OP_LAST ) {
+ return 1;
+ }
+
+ slap_cf_aux_table_unparse( li->li_timeout, &bv, timeout_table );
+
+ if ( BER_BVISNULL( &bv ) ) {
+ return 1;
+ }
+
+ for ( i = 0; isspace( (unsigned char) bv.bv_val[ i ] ); i++ )
+ /* count spaces */ ;
+
+ if ( i ) {
+ bv.bv_len -= i;
+ AC_MEMCPY( bv.bv_val, &bv.bv_val[ i ],
+ bv.bv_len + 1 );
+ }
+
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ break;
+
+ case LDAP_BACK_CFG_IDLE_TIMEOUT: {
+ char buf[ SLAP_TEXT_BUFLEN ];
+
+ if ( li->li_idle_timeout == 0 ) {
+ return 1;
+ }
+
+ lutil_unparse_time( buf, sizeof( buf ), li->li_idle_timeout );
+ ber_str2bv( buf, 0, 0, &bv );
+ value_add_one( &c->rvalue_vals, &bv );
+ } break;
+
+ case LDAP_BACK_CFG_CONN_TTL: {
+ char buf[ SLAP_TEXT_BUFLEN ];
+
+ if ( li->li_conn_ttl == 0 ) {
+ return 1;
+ }
+
+ lutil_unparse_time( buf, sizeof( buf ), li->li_conn_ttl );
+ ber_str2bv( buf, 0, 0, &bv );
+ value_add_one( &c->rvalue_vals, &bv );
+ } break;
+
+ case LDAP_BACK_CFG_NETWORK_TIMEOUT: {
+ char buf[ SLAP_TEXT_BUFLEN ];
+
+ if ( li->li_network_timeout == 0 ) {
+ return 1;
+ }
+
+ lutil_unparse_time( buf, sizeof( buf ), li->li_network_timeout );
+ ber_str2bv( buf, 0, 0, &bv );
+ value_add_one( &c->rvalue_vals, &bv );
+ } break;
+
+ case LDAP_BACK_CFG_VERSION:
+ if ( li->li_version == 0 ) {
+ return 1;
+ }
+
+ c->value_int = li->li_version;
+ break;
+
+ case LDAP_BACK_CFG_SINGLECONN:
+ c->value_int = LDAP_BACK_SINGLECONN( li );
+ break;
+
+ case LDAP_BACK_CFG_USETEMP:
+ c->value_int = LDAP_BACK_USE_TEMPORARIES( li );
+ break;
+
+ case LDAP_BACK_CFG_CONNPOOLMAX:
+ c->value_int = li->li_conn_priv_max;
+ break;
+
+ case LDAP_BACK_CFG_CANCEL: {
+ slap_mask_t mask = LDAP_BACK_F_CANCEL_MASK2;
+
+ if ( LDAP_BACK_CANCEL_DISCOVER( li ) ) {
+ mask &= ~LDAP_BACK_F_CANCEL_EXOP;
+ }
+ enum_to_verb( cancel_mode, (li->li_flags & mask), &bv );
+ if ( BER_BVISNULL( &bv ) ) {
+ /* there's something wrong... */
+ assert( 0 );
+ rc = 1;
+
+ } else {
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ } break;
+
+ case LDAP_BACK_CFG_QUARANTINE:
+ if ( !LDAP_BACK_QUARANTINE( li ) ) {
+ rc = 1;
+ break;
+ }
+
+ rc = slap_retry_info_unparse( &li->li_quarantine, &bv );
+ if ( rc == 0 ) {
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ }
+ break;
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ case LDAP_BACK_CFG_ST_REQUEST:
+ c->value_int = LDAP_BACK_ST_REQUEST( li );
+ break;
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+ case LDAP_BACK_CFG_NOREFS:
+ c->value_int = LDAP_BACK_NOREFS( li );
+ break;
+
+ case LDAP_BACK_CFG_NOUNDEFFILTER:
+ c->value_int = LDAP_BACK_NOUNDEFFILTER( li );
+ break;
+
+ case LDAP_BACK_CFG_OMIT_UNKNOWN_SCHEMA:
+ c->value_int = LDAP_BACK_OMIT_UNKNOWN_SCHEMA( li );
+ break;
+
+ case LDAP_BACK_CFG_ONERR:
+ enum_to_verb( onerr_mode, li->li_flags & LDAP_BACK_F_ONERR_STOP, &bv );
+ if ( BER_BVISNULL( &bv )) {
+ rc = 1;
+ } else {
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
+
+ case LDAP_BACK_CFG_KEEPALIVE: {
+ struct berval bv;
+ char buf[AC_LINE_MAX];
+ bv.bv_len = AC_LINE_MAX;
+ bv.bv_val = &buf[0];
+ slap_keepalive_parse(&bv, &li->li_tls.sb_keepalive, 0, 0, 1);
+ value_add_one( &c->rvalue_vals, &bv );
+ break;
+ }
+
+ case LDAP_BACK_CFG_TCP_USER_TIMEOUT:
+ c->value_uint = li->li_tls.sb_tcp_user_timeout;
+ break;
+
+ default:
+ /* FIXME: we need to handle all... */
+ assert( 0 );
+ break;
+ }
+ return rc;
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ switch( c->type ) {
+ case LDAP_BACK_CFG_URI:
+ if ( li->li_uri != NULL ) {
+ ch_free( li->li_uri );
+ li->li_uri = NULL;
+
+ assert( li->li_bvuri != NULL );
+ ber_bvarray_free( li->li_bvuri );
+ li->li_bvuri = NULL;
+ }
+
+ /* better cleanup the cached connections... */
+ /* NOTE: don't worry about locking: if we got here,
+ * other threads are suspended. */
+ if ( li->li_conninfo.lai_tree != NULL ) {
+ ldap_tavl_free( li->li_conninfo.lai_tree, ldap_back_conn_free );
+ li->li_conninfo.lai_tree = NULL;
+ }
+
+ break;
+
+ case LDAP_BACK_CFG_TLS:
+ rc = 1;
+ break;
+
+ case LDAP_BACK_CFG_ACL_BIND:
+ bindconf_free( &li->li_acl );
+ break;
+
+ case LDAP_BACK_CFG_IDASSERT_AUTHZFROM:
+ case LDAP_BACK_CFG_IDASSERT_PASSTHRU: {
+ BerVarray *bvp;
+
+ switch ( c->type ) {
+ case LDAP_BACK_CFG_IDASSERT_AUTHZFROM: bvp = &li->li_idassert_authz; break;
+ case LDAP_BACK_CFG_IDASSERT_PASSTHRU: bvp = &li->li_idassert_passthru; break;
+ default: assert( 0 ); break;
+ }
+
+ if ( c->valx < 0 ) {
+ if ( *bvp != NULL ) {
+ ber_bvarray_free( *bvp );
+ *bvp = NULL;
+ }
+
+ } else {
+ int i;
+
+ if ( *bvp == NULL ) {
+ rc = 1;
+ break;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &((*bvp)[ i ]) ); i++ )
+ ;
+
+ if ( i >= c->valx ) {
+ rc = 1;
+ break;
+ }
+ ber_memfree( ((*bvp)[ c->valx ]).bv_val );
+ for ( i = c->valx; !BER_BVISNULL( &((*bvp)[ i + 1 ]) ); i++ ) {
+ (*bvp)[ i ] = (*bvp)[ i + 1 ];
+ }
+ BER_BVZERO( &((*bvp)[ i ]) );
+ }
+ } break;
+
+ case LDAP_BACK_CFG_IDASSERT_BIND:
+ bindconf_free( &li->li_idassert.si_bc );
+ memset( &li->li_idassert, 0, sizeof( slap_idassert_t ) );
+ break;
+
+ case LDAP_BACK_CFG_REBIND:
+ case LDAP_BACK_CFG_CHASE:
+ case LDAP_BACK_CFG_T_F:
+ case LDAP_BACK_CFG_WHOAMI:
+ case LDAP_BACK_CFG_CANCEL:
+ rc = 1;
+ break;
+
+ case LDAP_BACK_CFG_TIMEOUT:
+ for ( i = 0; i < SLAP_OP_LAST; i++ ) {
+ li->li_timeout[ i ] = 0;
+ }
+ break;
+
+ case LDAP_BACK_CFG_IDLE_TIMEOUT:
+ li->li_idle_timeout = 0;
+ break;
+
+ case LDAP_BACK_CFG_CONN_TTL:
+ li->li_conn_ttl = 0;
+ break;
+
+ case LDAP_BACK_CFG_NETWORK_TIMEOUT:
+ li->li_network_timeout = 0;
+ break;
+
+ case LDAP_BACK_CFG_VERSION:
+ li->li_version = 0;
+ break;
+
+ case LDAP_BACK_CFG_SINGLECONN:
+ li->li_flags &= ~LDAP_BACK_F_SINGLECONN;
+ break;
+
+ case LDAP_BACK_CFG_USETEMP:
+ li->li_flags &= ~LDAP_BACK_F_USE_TEMPORARIES;
+ break;
+
+ case LDAP_BACK_CFG_CONNPOOLMAX:
+ li->li_conn_priv_max = LDAP_BACK_CONN_PRIV_MIN;
+ break;
+
+ case LDAP_BACK_CFG_QUARANTINE:
+ if ( !LDAP_BACK_QUARANTINE( li ) ) {
+ break;
+ }
+
+ slap_retry_info_destroy( &li->li_quarantine );
+ ldap_pvt_thread_mutex_destroy( &li->li_quarantine_mutex );
+ li->li_isquarantined = 0;
+ li->li_flags &= ~LDAP_BACK_F_QUARANTINE;
+ break;
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ case LDAP_BACK_CFG_ST_REQUEST:
+ li->li_flags &= ~LDAP_BACK_F_ST_REQUEST;
+ break;
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+ case LDAP_BACK_CFG_NOREFS:
+ li->li_flags &= ~LDAP_BACK_F_NOREFS;
+ break;
+
+ case LDAP_BACK_CFG_NOUNDEFFILTER:
+ li->li_flags &= ~LDAP_BACK_F_NOUNDEFFILTER;
+ break;
+
+ case LDAP_BACK_CFG_OMIT_UNKNOWN_SCHEMA:
+ li->li_flags &= ~LDAP_BACK_F_OMIT_UNKNOWN_SCHEMA;
+ break;
+
+ case LDAP_BACK_CFG_ONERR:
+ li->li_flags &= ~LDAP_BACK_F_ONERR_STOP;
+ break;
+
+ case LDAP_BACK_CFG_KEEPALIVE:
+ li->li_tls.sb_keepalive.sk_idle = 0;
+ li->li_tls.sb_keepalive.sk_probes = 0;
+ li->li_tls.sb_keepalive.sk_interval = 0;
+ break;
+
+ case LDAP_BACK_CFG_TCP_USER_TIMEOUT:
+ li->li_tls.sb_tcp_user_timeout = 0;
+ break;
+
+ default:
+ /* FIXME: we need to handle all... */
+ assert( 0 );
+ break;
+ }
+ return rc;
+
+ }
+
+ switch( c->type ) {
+ case LDAP_BACK_CFG_URI: {
+ LDAPURLDesc *tmpludp, *lud;
+ char **urllist = NULL;
+ int urlrc = LDAP_URL_SUCCESS, i;
+
+ if ( li->li_uri != NULL ) {
+ ch_free( li->li_uri );
+ li->li_uri = NULL;
+
+ assert( li->li_bvuri != NULL );
+ ber_bvarray_free( li->li_bvuri );
+ li->li_bvuri = NULL;
+ }
+
+ /* PARANOID: DN and more are not required nor allowed */
+ urlrc = ldap_url_parselist_ext( &lud, c->argv[ 1 ], ", \t", LDAP_PVT_URL_PARSE_NONE );
+ if ( urlrc != LDAP_URL_SUCCESS ) {
+ char *why;
+
+ switch ( urlrc ) {
+ case LDAP_URL_ERR_MEM:
+ why = "no memory";
+ break;
+ case LDAP_URL_ERR_PARAM:
+ why = "parameter is bad";
+ break;
+ case LDAP_URL_ERR_BADSCHEME:
+ why = "URL doesn't begin with \"[c]ldap[si]://\"";
+ break;
+ case LDAP_URL_ERR_BADENCLOSURE:
+ why = "URL is missing trailing \">\"";
+ break;
+ case LDAP_URL_ERR_BADURL:
+ why = "URL is bad";
+ break;
+ case LDAP_URL_ERR_BADHOST:
+ why = "host/port is bad";
+ break;
+ case LDAP_URL_ERR_BADATTRS:
+ why = "bad (or missing) attributes";
+ break;
+ case LDAP_URL_ERR_BADSCOPE:
+ why = "scope string is invalid (or missing)";
+ break;
+ case LDAP_URL_ERR_BADFILTER:
+ why = "bad or missing filter";
+ break;
+ case LDAP_URL_ERR_BADEXTS:
+ why = "bad or missing extensions";
+ break;
+ default:
+ why = "unknown reason";
+ break;
+ }
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "unable to parse uri \"%s\" "
+ "in \"uri <uri>\" line: %s",
+ c->value_string, why );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ urlrc = 1;
+ goto done_url;
+ }
+
+ for ( i = 0, tmpludp = lud;
+ tmpludp;
+ i++, tmpludp = tmpludp->lud_next )
+ {
+ if ( ( tmpludp->lud_dn != NULL
+ && tmpludp->lud_dn[0] != '\0' )
+ || tmpludp->lud_attrs != NULL
+ /* || tmpludp->lud_scope != LDAP_SCOPE_DEFAULT */
+ || tmpludp->lud_filter != NULL
+ || tmpludp->lud_exts != NULL )
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "warning, only protocol, "
+ "host and port allowed "
+ "in \"uri <uri>\" statement "
+ "for uri #%d of \"%s\"",
+ i, c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ }
+ }
+
+ for ( i = 0, tmpludp = lud;
+ tmpludp;
+ i++, tmpludp = tmpludp->lud_next )
+ /* just count */
+ ;
+ urllist = ch_calloc( sizeof( char * ), i + 1 );
+
+ for ( i = 0, tmpludp = lud;
+ tmpludp;
+ i++, tmpludp = tmpludp->lud_next )
+ {
+ LDAPURLDesc tmplud;
+
+ tmplud = *tmpludp;
+ tmplud.lud_dn = "";
+ tmplud.lud_attrs = NULL;
+ tmplud.lud_filter = NULL;
+ if ( !ldap_is_ldapi_url( tmplud.lud_scheme ) ) {
+ tmplud.lud_exts = NULL;
+ tmplud.lud_crit_exts = 0;
+ }
+
+ urllist[ i ] = ldap_url_desc2str( &tmplud );
+
+ if ( urllist[ i ] == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "unable to rebuild uri "
+ "in \"uri <uri>\" statement "
+ "for \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ urlrc = 1;
+ goto done_url;
+ }
+ }
+
+ li->li_uri = ldap_charray2str( urllist, " " );
+ for ( i = 0; urllist[ i ] != NULL; i++ ) {
+ struct berval bv;
+
+ ber_str2bv( urllist[ i ], 0, 0, &bv );
+ ber_bvarray_add( &li->li_bvuri, &bv );
+ urllist[ i ] = NULL;
+ }
+ ldap_memfree( urllist );
+ urllist = NULL;
+
+done_url:;
+ if ( urllist ) {
+ ldap_charray_free( urllist );
+ }
+ if ( lud ) {
+ ldap_free_urllist( lud );
+ }
+ if ( urlrc != LDAP_URL_SUCCESS ) {
+ return 1;
+ }
+ break;
+ }
+
+ case LDAP_BACK_CFG_TLS:
+ i = verb_to_mask( c->argv[1], tls_mode );
+ if ( BER_BVISNULL( &tls_mode[i].word ) ) {
+ return 1;
+ }
+ li->li_flags &= ~LDAP_BACK_F_TLS_MASK;
+ li->li_flags |= tls_mode[i].mask;
+ if ( c->argc > 2 ) {
+ for ( i=2; i<c->argc; i++ ) {
+ if ( bindconf_tls_parse( c->argv[i], &li->li_tls ))
+ return 1;
+ }
+ bindconf_tls_defaults( &li->li_tls );
+ }
+#ifdef HAVE_TLS
+ if ( li->li_tls.sb_tls_ctx ) {
+ ldap_pvt_tls_ctx_free( li->li_tls.sb_tls_ctx );
+ li->li_tls.sb_tls_ctx = NULL;
+ }
+#endif
+ break;
+
+ case LDAP_BACK_CFG_ACL_BIND:
+ for ( i = 1; i < c->argc; i++ ) {
+ if ( bindconf_parse( c->argv[ i ], &li->li_acl ) ) {
+ return 1;
+ }
+ }
+ bindconf_tls_defaults( &li->li_acl );
+#ifdef HAVE_TLS
+ if ( li->li_acl.sb_tls_ctx ) {
+ ldap_pvt_tls_ctx_free( li->li_acl.sb_tls_ctx );
+ li->li_acl.sb_tls_ctx = NULL;
+ }
+#endif
+ break;
+
+ case LDAP_BACK_CFG_IDASSERT_AUTHZFROM:
+ rc = slap_idassert_authzfrom_parse( c, &li->li_idassert );
+ break;
+
+ case LDAP_BACK_CFG_IDASSERT_PASSTHRU:
+ rc = slap_idassert_passthru_parse( c, &li->li_idassert );
+ break;
+
+ case LDAP_BACK_CFG_IDASSERT_BIND:
+ rc = slap_idassert_parse( c, &li->li_idassert );
+ break;
+
+ case LDAP_BACK_CFG_REBIND:
+ if ( c->argc == 1 || c->value_int ) {
+ li->li_flags |= LDAP_BACK_F_SAVECRED;
+
+ } else {
+ li->li_flags &= ~LDAP_BACK_F_SAVECRED;
+ }
+ break;
+
+ case LDAP_BACK_CFG_CHASE:
+ if ( c->argc == 1 || c->value_int ) {
+ li->li_flags |= LDAP_BACK_F_CHASE_REFERRALS;
+
+ } else {
+ li->li_flags &= ~LDAP_BACK_F_CHASE_REFERRALS;
+ }
+ break;
+
+ case LDAP_BACK_CFG_T_F: {
+ slap_mask_t mask;
+
+ i = verb_to_mask( c->argv[1], t_f_mode );
+ if ( BER_BVISNULL( &t_f_mode[i].word ) ) {
+ return 1;
+ }
+
+ mask = t_f_mode[i].mask;
+
+ if ( LDAP_BACK_ISOPEN( li )
+ && mask == LDAP_BACK_F_T_F_DISCOVER
+ && !LDAP_BACK_T_F( li ) )
+ {
+ slap_bindconf sb = { BER_BVNULL };
+ int rc;
+
+ if ( li->li_uri == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "need URI to discover absolute filters support "
+ "in \"t-f-support discover\"" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ ber_str2bv( li->li_uri, 0, 0, &sb.sb_uri );
+ sb.sb_version = li->li_version;
+ sb.sb_method = LDAP_AUTH_SIMPLE;
+ BER_BVSTR( &sb.sb_binddn, "" );
+
+ rc = slap_discover_feature( &sb,
+ slap_schema.si_ad_supportedFeatures->ad_cname.bv_val,
+ LDAP_FEATURE_ABSOLUTE_FILTERS );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ mask |= LDAP_BACK_F_T_F;
+ }
+ }
+
+ li->li_flags &= ~LDAP_BACK_F_T_F_MASK2;
+ li->li_flags |= mask;
+ } break;
+
+ case LDAP_BACK_CFG_WHOAMI:
+ if ( c->argc == 1 || c->value_int ) {
+ li->li_flags |= LDAP_BACK_F_PROXY_WHOAMI;
+ load_extop( (struct berval *)&slap_EXOP_WHOAMI,
+ 0, ldap_back_exop_whoami );
+
+ } else {
+ li->li_flags &= ~LDAP_BACK_F_PROXY_WHOAMI;
+ }
+ break;
+
+ case LDAP_BACK_CFG_TIMEOUT:
+ for ( i = 1; i < c->argc; i++ ) {
+ if ( isdigit( (unsigned char) c->argv[ i ][ 0 ] ) ) {
+ int j;
+ unsigned u;
+
+ if ( lutil_atoux( &u, c->argv[ i ], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "unable to parse timeout \"%s\"",
+ c->argv[ i ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ for ( j = 0; j < SLAP_OP_LAST; j++ ) {
+ li->li_timeout[ j ] = u;
+ }
+
+ continue;
+ }
+
+ if ( slap_cf_aux_table_parse( c->argv[ i ], li->li_timeout, timeout_table, "slapd-ldap timeout" ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "unable to parse timeout \"%s\"",
+ c->argv[ i ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ }
+ break;
+
+ case LDAP_BACK_CFG_IDLE_TIMEOUT: {
+ unsigned long t;
+
+ if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "unable to parse idle timeout \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ li->li_idle_timeout = (time_t)t;
+ } break;
+
+ case LDAP_BACK_CFG_CONN_TTL: {
+ unsigned long t;
+
+ if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "unable to parse conn ttl\"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ li->li_conn_ttl = (time_t)t;
+ } break;
+
+ case LDAP_BACK_CFG_NETWORK_TIMEOUT: {
+ unsigned long t;
+
+ if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "unable to parse network timeout \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ li->li_network_timeout = (time_t)t;
+ } break;
+
+ case LDAP_BACK_CFG_VERSION:
+ if ( c->value_int != 0 && ( c->value_int < LDAP_VERSION_MIN || c->value_int > LDAP_VERSION_MAX ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unsupported version \"%s\" "
+ "in \"protocol-version <version>\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ li->li_version = c->value_int;
+ break;
+
+ case LDAP_BACK_CFG_SINGLECONN:
+ if ( c->value_int ) {
+ li->li_flags |= LDAP_BACK_F_SINGLECONN;
+
+ } else {
+ li->li_flags &= ~LDAP_BACK_F_SINGLECONN;
+ }
+ break;
+
+ case LDAP_BACK_CFG_USETEMP:
+ if ( c->value_int ) {
+ li->li_flags |= LDAP_BACK_F_USE_TEMPORARIES;
+
+ } else {
+ li->li_flags &= ~LDAP_BACK_F_USE_TEMPORARIES;
+ }
+ break;
+
+ case LDAP_BACK_CFG_CONNPOOLMAX:
+ if ( c->value_int < LDAP_BACK_CONN_PRIV_MIN
+ || c->value_int > LDAP_BACK_CONN_PRIV_MAX )
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "invalid max size " "of privileged "
+ "connections pool \"%s\" "
+ "in \"conn-pool-max <n> "
+ "(must be between %d and %d)\"",
+ c->argv[ 1 ],
+ LDAP_BACK_CONN_PRIV_MIN,
+ LDAP_BACK_CONN_PRIV_MAX );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ li->li_conn_priv_max = c->value_int;
+ break;
+
+ case LDAP_BACK_CFG_CANCEL: {
+ slap_mask_t mask;
+
+ i = verb_to_mask( c->argv[1], cancel_mode );
+ if ( BER_BVISNULL( &cancel_mode[i].word ) ) {
+ return 1;
+ }
+
+ mask = cancel_mode[i].mask;
+
+ if ( LDAP_BACK_ISOPEN( li )
+ && mask == LDAP_BACK_F_CANCEL_EXOP_DISCOVER
+ && !LDAP_BACK_CANCEL( li ) )
+ {
+ slap_bindconf sb = { BER_BVNULL };
+ int rc;
+
+ if ( li->li_uri == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "need URI to discover \"cancel\" support "
+ "in \"cancel exop-discover\"" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ ber_str2bv( li->li_uri, 0, 0, &sb.sb_uri );
+ sb.sb_version = li->li_version;
+ sb.sb_method = LDAP_AUTH_SIMPLE;
+ BER_BVSTR( &sb.sb_binddn, "" );
+
+ rc = slap_discover_feature( &sb,
+ slap_schema.si_ad_supportedExtension->ad_cname.bv_val,
+ LDAP_EXOP_CANCEL );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ mask |= LDAP_BACK_F_CANCEL_EXOP;
+ }
+ }
+
+ li->li_flags &= ~LDAP_BACK_F_CANCEL_MASK2;
+ li->li_flags |= mask;
+ } break;
+
+ case LDAP_BACK_CFG_QUARANTINE:
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "quarantine already defined" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ rc = slap_retry_info_parse( c->argv[1], &li->li_quarantine,
+ c->cr_msg, sizeof( c->cr_msg ) );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+
+ } else {
+ ldap_pvt_thread_mutex_init( &li->li_quarantine_mutex );
+ /* give it a chance to retry if the pattern gets reset
+ * via back-config */
+ li->li_isquarantined = 0;
+ li->li_flags |= LDAP_BACK_F_QUARANTINE;
+ }
+ break;
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ case LDAP_BACK_CFG_ST_REQUEST:
+ if ( c->value_int ) {
+ li->li_flags |= LDAP_BACK_F_ST_REQUEST;
+
+ } else {
+ li->li_flags &= ~LDAP_BACK_F_ST_REQUEST;
+ }
+ break;
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+ case LDAP_BACK_CFG_NOREFS:
+ if ( c->value_int ) {
+ li->li_flags |= LDAP_BACK_F_NOREFS;
+
+ } else {
+ li->li_flags &= ~LDAP_BACK_F_NOREFS;
+ }
+ break;
+
+ case LDAP_BACK_CFG_NOUNDEFFILTER:
+ if ( c->value_int ) {
+ li->li_flags |= LDAP_BACK_F_NOUNDEFFILTER;
+
+ } else {
+ li->li_flags &= ~LDAP_BACK_F_NOUNDEFFILTER;
+ }
+ break;
+
+ case LDAP_BACK_CFG_ONERR:
+ /* onerr? */
+ i = verb_to_mask( c->argv[1], onerr_mode );
+ if ( BER_BVISNULL( &onerr_mode[i].word ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s unknown argument \"%s\"",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ li->li_flags &= ~LDAP_BACK_F_ONERR_STOP;
+ li->li_flags |= onerr_mode[i].mask;
+ break;
+
+ case LDAP_BACK_CFG_OMIT_UNKNOWN_SCHEMA:
+ if ( c->value_int ) {
+ li->li_flags |= LDAP_BACK_F_OMIT_UNKNOWN_SCHEMA;
+
+ } else {
+ li->li_flags &= ~LDAP_BACK_F_OMIT_UNKNOWN_SCHEMA;
+ }
+ break;
+
+ case LDAP_BACK_CFG_KEEPALIVE: {
+ struct berval bv;
+ ber_str2bv( c->argv[1], 0, 1, &bv );
+ slap_keepalive_parse( &bv, &li->li_tls.sb_keepalive, 0, 0, 0 );
+ }
+ break;
+
+ case LDAP_BACK_CFG_TCP_USER_TIMEOUT:
+ li->li_tls.sb_tcp_user_timeout = c->value_uint;
+ break;
+
+ default:
+ /* FIXME: try to catch inconsistencies */
+ assert( 0 );
+ break;
+ }
+
+ return rc;
+}
+
+int
+ldap_back_init_cf( BackendInfo *bi )
+{
+ int rc;
+
+ /* Make sure we don't exceed the bits reserved for userland */
+ config_check_userland( LDAP_BACK_CFG_LAST );
+
+ bi->bi_cf_ocs = ldapocs;
+
+ rc = config_register_schema( ldapcfg, ldapocs );
+ if ( rc ) {
+ return rc;
+ }
+
+ return 0;
+}
+
+static int
+ldap_pbind_cf_gen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ void *private = c->be->be_private;
+ int rc;
+
+ c->be->be_private = on->on_bi.bi_private;
+ rc = ldap_back_cf_gen( c );
+ c->be->be_private = private;
+ return rc;
+}
+
+int
+ldap_pbind_init_cf( BackendInfo *bi )
+{
+ bi->bi_cf_ocs = pbindocs;
+
+ return config_register_schema( pbindcfg, pbindocs );
+}
+
+static int
+ldap_back_exop_whoami(
+ Operation *op,
+ SlapReply *rs )
+{
+ struct berval *bv = NULL;
+
+ if ( op->oq_extended.rs_reqdata != NULL ) {
+ /* no request data should be provided */
+ rs->sr_text = "no request data expected";
+ return rs->sr_err = LDAP_PROTOCOL_ERROR;
+ }
+
+ Debug( LDAP_DEBUG_STATS, "%s WHOAMI\n",
+ op->o_log_prefix );
+
+ rs->sr_err = backend_check_restrictions( op, rs,
+ (struct berval *)&slap_EXOP_WHOAMI );
+ if( rs->sr_err != LDAP_SUCCESS ) return rs->sr_err;
+
+ /* if auth'd by back-ldap and request is proxied, forward it */
+ if ( op->o_conn->c_authz_backend
+ && !strcmp( op->o_conn->c_authz_backend->be_type, "ldap" )
+ && !dn_match( &op->o_ndn, &op->o_conn->c_ndn ) )
+ {
+ ldapconn_t *lc = NULL;
+ LDAPControl c, *ctrls[2] = {NULL, NULL};
+ LDAPMessage *res;
+ Operation op2 = *op;
+ ber_int_t msgid;
+ int doretry = 1;
+ char *ptr;
+
+ ctrls[0] = &c;
+ op2.o_ndn = op->o_conn->c_ndn;
+ if ( !ldap_back_dobind( &lc, &op2, rs, LDAP_BACK_SENDERR ) ) {
+ return -1;
+ }
+ c.ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
+ c.ldctl_iscritical = 1;
+ c.ldctl_value.bv_val = op->o_tmpalloc(
+ op->o_ndn.bv_len + STRLENOF( "dn:" ) + 1,
+ op->o_tmpmemctx );
+ c.ldctl_value.bv_len = op->o_ndn.bv_len + 3;
+ ptr = c.ldctl_value.bv_val;
+ ptr = lutil_strcopy( ptr, "dn:" );
+ ptr = lutil_strncopy( ptr, op->o_ndn.bv_val, op->o_ndn.bv_len );
+ ptr[ 0 ] = '\0';
+
+retry:
+ rs->sr_err = ldap_whoami( lc->lc_ld, ctrls, NULL, &msgid );
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ /* by now, make sure no timeout is used (ITS#6282) */
+ struct timeval tv = { -1, 0 };
+ if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res ) == -1 ) {
+ ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER,
+ &rs->sr_err );
+ if ( rs->sr_err == LDAP_SERVER_DOWN && doretry ) {
+ doretry = 0;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ goto retry;
+ }
+ }
+
+ } else {
+ /* NOTE: are we sure "bv" will be malloc'ed
+ * with the appropriate memory? */
+ rs->sr_err = ldap_parse_whoami( lc->lc_ld, res, &bv );
+ ldap_msgfree(res);
+ }
+ }
+ op->o_tmpfree( c.ldctl_value.bv_val, op->o_tmpmemctx );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ rs->sr_err = slap_map_api2result( rs );
+ }
+
+ if ( lc != NULL ) {
+ ldap_back_release_conn( (ldapinfo_t *)op2.o_bd->be_private, lc );
+ }
+
+ } else {
+ /* else just do the same as before */
+ bv = (struct berval *) ch_malloc( sizeof( struct berval ) );
+ if ( !BER_BVISEMPTY( &op->o_dn ) ) {
+ bv->bv_len = op->o_dn.bv_len + STRLENOF( "dn:" );
+ bv->bv_val = ch_malloc( bv->bv_len + 1 );
+ AC_MEMCPY( bv->bv_val, "dn:", STRLENOF( "dn:" ) );
+ AC_MEMCPY( &bv->bv_val[ STRLENOF( "dn:" ) ], op->o_dn.bv_val,
+ op->o_dn.bv_len );
+ bv->bv_val[ bv->bv_len ] = '\0';
+
+ } else {
+ bv->bv_len = 0;
+ bv->bv_val = NULL;
+ }
+ }
+
+ rs->sr_rspdata = bv;
+ return rs->sr_err;
+}
+
+
diff --git a/servers/slapd/back-ldap/delete.c b/servers/slapd/back-ldap/delete.c
new file mode 100644
index 0000000..470aa0b
--- /dev/null
+++ b/servers/slapd/back-ldap/delete.c
@@ -0,0 +1,85 @@
+/* delete.c - ldap backend delete function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-ldap.h"
+
+int
+ldap_back_delete(
+ Operation *op,
+ SlapReply *rs )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ ldapconn_t *lc = NULL;
+ ber_int_t msgid;
+ LDAPControl **ctrls = NULL;
+ ldap_back_send_t retrying = LDAP_BACK_RETRYING;
+ int rc = LDAP_SUCCESS;
+
+ if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ return rs->sr_err;
+ }
+
+retry:
+ ctrls = op->o_ctrls;
+ rc = ldap_back_controls_add( op, rs, lc, &ctrls );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ rs->sr_err = ldap_delete_ext( lc->lc_ld, op->o_req_dn.bv_val,
+ ctrls, NULL, &msgid );
+ rc = ldap_back_op_result( lc, op, rs, msgid,
+ li->li_timeout[ SLAP_OP_DELETE ],
+ ( LDAP_BACK_SENDRESULT | retrying ) );
+ if ( rs->sr_err == LDAP_UNAVAILABLE && retrying ) {
+ retrying &= ~LDAP_BACK_RETRYING;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ /* if the identity changed, there might be need to re-authz */
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+ goto retry;
+ }
+ }
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_DELETE ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+cleanup:
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+
+ if ( lc != NULL ) {
+ ldap_back_release_conn( li, lc );
+ }
+
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-ldap/distproc.c b/servers/slapd/back-ldap/distproc.c
new file mode 100644
index 0000000..a2417a3
--- /dev/null
+++ b/servers/slapd/back-ldap/distproc.c
@@ -0,0 +1,998 @@
+/* distproc.c - implement distributed procedures */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2003 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ * Based on back-ldap and slapo-chain, developed by Howard Chu
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+#ifdef SLAP_DISTPROC
+
+#include "back-ldap.h"
+
+#include "slap-config.h"
+
+/*
+ * From <draft-sermersheim-ldap-distproc>
+ *
+
+ ContinuationReference ::= SET {
+ referralURI [0] SET SIZE (1..MAX) OF URI,
+ localReference [2] LDAPDN,
+ referenceType [3] ReferenceType,
+ remainingName [4] RelativeLDAPDN OPTIONAL,
+ searchScope [5] SearchScope OPTIONAL,
+ searchedSubtrees [6] SearchedSubtrees OPTIONAL,
+ failedName [7] LDAPDN OPTIONAL,
+ ... }
+
+ ReferenceType ::= ENUMERATED {
+ superior (0),
+ subordinate (1),
+ cross (2),
+ nonSpecificSubordinate (3),
+ supplier (4),
+ master (5),
+ immediateSuperior (6),
+ self (7),
+ ... }
+
+ SearchScope ::= ENUMERATED {
+ baseObject (0),
+ singleLevel (1),
+ wholeSubtree (2),
+ subordinateSubtree (3),
+ ... }
+
+ SearchedSubtrees ::= SET OF RelativeLDAPDN
+
+ LDAPDN, RelativeLDAPDN, and LDAPString, are defined in [RFC2251].
+
+ */
+
+typedef enum ReferenceType_t {
+ LDAP_DP_RT_UNKNOWN = -1,
+ LDAP_DP_RT_SUPERIOR = 0,
+ LDAP_DP_RT_SUBORDINATE = 1,
+ LDAP_DP_RT_CROSS = 2,
+ LDAP_DP_RT_NONSPECIFICSUBORDINATE = 3,
+ LDAP_DP_RT_SUPPLIER = 4,
+ LDAP_DP_RT_MASTER = 5,
+ LDAP_DP_RT_IMMEDIATESUPERIOR = 6,
+ LDAP_DP_RT_SELF = 7,
+ LDAP_DP_RT_LAST
+} ReferenceType_t;
+
+typedef enum SearchScope_t {
+ LDAP_DP_SS_UNKNOWN = -1,
+ LDAP_DP_SS_BASEOBJECT = 0,
+ LDAP_DP_SS_SINGLELEVEL = 1,
+ LDAP_DP_SS_WHOLESUBTREE = 2,
+ LDAP_DP_SS_SUBORDINATESUBTREE = 3,
+ LDAP_DP_SS_LAST
+} SearchScope_t;
+
+typedef struct ContinuationReference_t {
+ BerVarray cr_referralURI;
+ /* ? [1] ? */
+ struct berval cr_localReference;
+ ReferenceType_t cr_referenceType;
+ struct berval cr_remainingName;
+ SearchScope_t cr_searchScope;
+ BerVarray cr_searchedSubtrees;
+ struct berval cr_failedName;
+} ContinuationReference_t;
+#define CR_INIT { NULL, BER_BVNULL, LDAP_DP_RT_UNKNOWN, BER_BVNULL, LDAP_DP_SS_UNKNOWN, NULL, BER_BVNULL }
+
+#ifdef unused
+static struct berval bv2rt[] = {
+ BER_BVC( "superior" ),
+ BER_BVC( "subordinate" ),
+ BER_BVC( "cross" ),
+ BER_BVC( "nonSpecificSubordinate" ),
+ BER_BVC( "supplier" ),
+ BER_BVC( "master" ),
+ BER_BVC( "immediateSuperior" ),
+ BER_BVC( "self" ),
+ BER_BVNULL
+};
+
+static struct berval bv2ss[] = {
+ BER_BVC( "baseObject" ),
+ BER_BVC( "singleLevel" ),
+ BER_BVC( "wholeSubtree" ),
+ BER_BVC( "subordinateSubtree" ),
+ BER_BVNULL
+};
+
+static struct berval *
+ldap_distproc_rt2bv( ReferenceType_t rt )
+{
+ return &bv2rt[ rt ];
+}
+
+static const char *
+ldap_distproc_rt2str( ReferenceType_t rt )
+{
+ return bv2rt[ rt ].bv_val;
+}
+
+static ReferenceType_t
+ldap_distproc_bv2rt( struct berval *bv )
+{
+ ReferenceType_t rt;
+
+ for ( rt = 0; !BER_BVISNULL( &bv2rt[ rt ] ); rt++ ) {
+ if ( ber_bvstrcasecmp( bv, &bv2rt[ rt ] ) == 0 ) {
+ return rt;
+ }
+ }
+
+ return LDAP_DP_RT_UNKNOWN;
+}
+
+static ReferenceType_t
+ldap_distproc_str2rt( const char *s )
+{
+ struct berval bv;
+
+ ber_str2bv( s, 0, 0, &bv );
+ return ldap_distproc_bv2rt( &bv );
+}
+
+static struct berval *
+ldap_distproc_ss2bv( SearchScope_t ss )
+{
+ return &bv2ss[ ss ];
+}
+
+static const char *
+ldap_distproc_ss2str( SearchScope_t ss )
+{
+ return bv2ss[ ss ].bv_val;
+}
+
+static SearchScope_t
+ldap_distproc_bv2ss( struct berval *bv )
+{
+ ReferenceType_t ss;
+
+ for ( ss = 0; !BER_BVISNULL( &bv2ss[ ss ] ); ss++ ) {
+ if ( ber_bvstrcasecmp( bv, &bv2ss[ ss ] ) == 0 ) {
+ return ss;
+ }
+ }
+
+ return LDAP_DP_SS_UNKNOWN;
+}
+
+static SearchScope_t
+ldap_distproc_str2ss( const char *s )
+{
+ struct berval bv;
+
+ ber_str2bv( s, 0, 0, &bv );
+ return ldap_distproc_bv2ss( &bv );
+}
+#endif /* unused */
+
+/*
+ * NOTE: this overlay assumes that the chainingBehavior control
+ * is registered by the chain overlay; it may move here some time.
+ * This overlay provides support for that control as well.
+ */
+
+
+static int sc_returnContRef;
+#define o_returnContRef o_ctrlflag[sc_returnContRef]
+#define get_returnContRef(op) ((op)->o_returnContRef & SLAP_CONTROL_MASK)
+
+static struct berval slap_EXOP_CHAINEDREQUEST = BER_BVC( LDAP_EXOP_X_CHAINEDREQUEST );
+static struct berval slap_FEATURE_CANCHAINOPS = BER_BVC( LDAP_FEATURE_X_CANCHAINOPS );
+
+static BackendInfo *lback;
+
+typedef struct ldap_distproc_t {
+ /* "common" configuration info (anything occurring before an "uri") */
+ ldapinfo_t *lc_common_li;
+
+ /* current configuration info */
+ ldapinfo_t *lc_cfg_li;
+
+ /* tree of configured[/generated?] "uri" info */
+ ldap_avl_info_t lc_lai;
+
+ unsigned lc_flags;
+#define LDAP_DISTPROC_F_NONE (0x00U)
+#define LDAP_DISTPROC_F_CHAINING (0x01U)
+#define LDAP_DISTPROC_F_CACHE_URI (0x10U)
+
+#define LDAP_DISTPROC_CHAINING( lc ) ( ( (lc)->lc_flags & LDAP_DISTPROC_F_CHAINING ) == LDAP_DISTPROC_F_CHAINING )
+#define LDAP_DISTPROC_CACHE_URI( lc ) ( ( (lc)->lc_flags & LDAP_DISTPROC_F_CACHE_URI ) == LDAP_DISTPROC_F_CACHE_URI )
+
+} ldap_distproc_t;
+
+static int ldap_distproc_db_init_common( BackendDB *be );
+static int ldap_distproc_db_init_one( BackendDB *be );
+#define ldap_distproc_db_open_one(be) (lback)->bi_db_open( (be) )
+#define ldap_distproc_db_close_one(be) (0)
+#define ldap_distproc_db_destroy_one(be, ca) (lback)->bi_db_destroy( (be), (ca) )
+
+static int
+ldap_distproc_uri_cmp( const void *c1, const void *c2 )
+{
+ const ldapinfo_t *li1 = (const ldapinfo_t *)c1;
+ const ldapinfo_t *li2 = (const ldapinfo_t *)c2;
+
+ assert( li1->li_bvuri != NULL );
+ assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
+ assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
+
+ assert( li2->li_bvuri != NULL );
+ assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
+ assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
+
+ /* If local DNs don't match, it is definitely not a match */
+ return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] );
+}
+
+static int
+ldap_distproc_uri_dup( void *c1, void *c2 )
+{
+ ldapinfo_t *li1 = (ldapinfo_t *)c1;
+ ldapinfo_t *li2 = (ldapinfo_t *)c2;
+
+ assert( li1->li_bvuri != NULL );
+ assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
+ assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
+
+ assert( li2->li_bvuri != NULL );
+ assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
+ assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
+
+ /* Cannot have more than one shared session with same DN */
+ if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+ldap_distproc_operational( Operation *op, SlapReply *rs )
+{
+ /* Trap entries generated by back-ldap.
+ *
+ * FIXME: we need a better way to recognize them; a cleaner
+ * solution would be to be able to intercept the response
+ * of be_operational(), so that we can divert only those
+ * calls that fail because operational attributes were
+ * requested for entries that do not belong to the underlying
+ * database. This fix is likely to intercept also entries
+ * generated by back-perl and so. */
+ if ( rs->sr_entry->e_private == NULL ) {
+ return LDAP_SUCCESS;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+ldap_distproc_response( Operation *op, SlapReply *rs )
+{
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+ * configuration...
+ */
+
+enum {
+ /* NOTE: the chaining behavior control is registered
+ * by the chain overlay; it may move here some time */
+ DP_CHAINING = 1,
+ DP_CACHE_URI,
+
+ DP_LAST
+};
+
+static ConfigDriver distproc_cfgen;
+static ConfigCfAdd distproc_cfadd;
+static ConfigLDAPadd distproc_ldadd;
+
+static ConfigTable distproc_cfg[] = {
+ { "distproc-chaining", "args",
+ 2, 4, 0, ARG_MAGIC|ARG_BERVAL|DP_CHAINING, distproc_cfgen,
+ /* NOTE: using the same attributeTypes defined
+ * for the "chain" overlay */
+ "( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
+ "DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "distproc-cache-uri", "TRUE/FALSE",
+ 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|DP_CACHE_URI, distproc_cfgen,
+ "( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
+ "DESC 'Enables caching of URIs not present in configuration' "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs distproc_ocs[] = {
+ { "( OLcfgOvOc:7.1 "
+ "NAME 'olcDistProcConfig' "
+ "DESC 'Distributed procedures <draft-sermersheim-ldap-distproc> configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( "
+ "olcChainingBehavior $ "
+ "olcChainCacheURI "
+ ") )",
+ Cft_Overlay, distproc_cfg, NULL, distproc_cfadd },
+ { "( OLcfgOvOc:7.2 "
+ "NAME 'olcDistProcDatabase' "
+ "DESC 'Distributed procedure remote server configuration' "
+ "AUXILIARY )",
+ Cft_Misc, distproc_cfg, distproc_ldadd },
+ { NULL, 0, NULL }
+};
+
+static int
+distproc_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
+{
+ slap_overinst *on;
+ ldap_distproc_t *lc;
+
+ ldapinfo_t *li;
+
+ AttributeDescription *ad = NULL;
+ Attribute *at;
+ const char *text;
+
+ int rc;
+
+ if ( p->ce_type != Cft_Overlay
+ || !p->ce_bi
+ || p->ce_bi->bi_cf_ocs != distproc_ocs )
+ {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ on = (slap_overinst *)p->ce_bi;
+ lc = (ldap_distproc_t *)on->on_bi.bi_private;
+
+ assert( ca->be == NULL );
+ ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
+
+ ca->be->bd_info = (BackendInfo *)on;
+
+ rc = slap_str2ad( "olcDbURI", &ad, &text );
+ assert( rc == LDAP_SUCCESS );
+
+ at = attr_find( e->e_attrs, ad );
+ if ( lc->lc_common_li == NULL && at != NULL ) {
+ /* FIXME: we should generate an empty default entry
+ * if none is supplied */
+ Debug( LDAP_DEBUG_ANY, "slapd-distproc: "
+ "first underlying database \"%s\" "
+ "cannot contain attribute \"%s\".\n",
+ e->e_name.bv_val, ad->ad_cname.bv_val );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+
+ } else if ( lc->lc_common_li != NULL && at == NULL ) {
+ /* FIXME: we should generate an empty default entry
+ * if none is supplied */
+ Debug( LDAP_DEBUG_ANY, "slapd-distproc: "
+ "subsequent underlying database \"%s\" "
+ "must contain attribute \"%s\".\n",
+ e->e_name.bv_val, ad->ad_cname.bv_val );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+
+ if ( lc->lc_common_li == NULL ) {
+ rc = ldap_distproc_db_init_common( ca->be );
+
+ } else {
+ rc = ldap_distproc_db_init_one( ca->be );
+ }
+
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "slapd-distproc: "
+ "unable to init %sunderlying database \"%s\".\n",
+ lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+
+ li = ca->be->be_private;
+
+ if ( lc->lc_common_li == NULL ) {
+ lc->lc_common_li = li;
+
+ } else if ( ldap_tavl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
+ ldap_distproc_uri_cmp, ldap_distproc_uri_dup ) )
+ {
+ Debug( LDAP_DEBUG_ANY, "slapd-distproc: "
+ "database \"%s\" insert failed.\n",
+ e->e_name.bv_val );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+
+done:;
+ if ( rc != LDAP_SUCCESS ) {
+ (void)ldap_distproc_db_destroy_one( ca->be, NULL );
+ ch_free( ca->be );
+ ca->be = NULL;
+ }
+
+ return rc;
+}
+
+typedef struct ldap_distproc_cfadd_apply_t {
+ Operation *op;
+ SlapReply *rs;
+ Entry *p;
+ ConfigArgs *ca;
+ int count;
+} ldap_distproc_cfadd_apply_t;
+
+static void
+ldap_distproc_cfadd_apply(
+ ldapinfo_t *li,
+ Operation *op,
+ SlapReply *rs,
+ Entry *p,
+ ConfigArgs *ca,
+ int count )
+{
+ struct berval bv;
+
+ /* FIXME: should not hardcode "olcDatabase" here */
+ bv.bv_len = snprintf( ca->cr_msg, sizeof( ca->cr_msg ),
+ "olcDatabase={%d}%s", count, lback->bi_type );
+ bv.bv_val = ca->cr_msg;
+
+ ca->be->be_private = (void *)li;
+ config_build_entry( op, rs, p->e_private, ca,
+ &bv, lback->bi_cf_ocs, &distproc_ocs[ 1 ] );
+
+ return;
+}
+
+static int
+distproc_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
+{
+ CfEntryInfo *pe = p->e_private;
+ slap_overinst *on = (slap_overinst *)pe->ce_bi;
+ ldap_distproc_t *lc = (ldap_distproc_t *)on->on_bi.bi_private;
+ void *priv = (void *)ca->be->be_private;
+ TAvlnode *edge;
+ int count = 0;
+
+ if ( lback->bi_cf_ocs ) {
+ ldap_distproc_cfadd_apply_t lca = { 0 };
+
+ lca.op = op;
+ lca.rs = rs;
+ lca.p = p;
+ lca.ca = ca;
+ lca.count = 0;
+
+ ldap_distproc_cfadd_apply( lc->lc_common_li, op, rs, p, ca, count++ );
+
+ edge = ldap_tavl_end( lc->lc_lai.lai_tree, TAVL_DIR_LEFT );
+ while ( edge ) {
+ TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
+ ldapinfo_t *li = (ldapinfo_t *)edge->avl_data;
+ ldap_distproc_cfadd_apply( li, op, rs, p, ca, count++ );
+ edge = next;
+ }
+
+ ca->be->be_private = priv;
+ }
+
+ return 0;
+}
+
+static int
+distproc_cfgen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ ldap_distproc_t *lc = (ldap_distproc_t *)on->on_bi.bi_private;
+
+ int rc = 0;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ switch( c->type ) {
+ case DP_CACHE_URI:
+ c->value_int = LDAP_DISTPROC_CACHE_URI( lc );
+ break;
+
+ default:
+ assert( 0 );
+ rc = 1;
+ }
+ return rc;
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ switch( c->type ) {
+ case DP_CHAINING:
+ return 1;
+
+ case DP_CACHE_URI:
+ lc->lc_flags &= ~LDAP_DISTPROC_F_CACHE_URI;
+ break;
+
+ default:
+ return 1;
+ }
+ return rc;
+ }
+
+ switch( c->type ) {
+ case DP_CACHE_URI:
+ if ( c->value_int ) {
+ lc->lc_flags |= LDAP_DISTPROC_F_CACHE_URI;
+ } else {
+ lc->lc_flags &= ~LDAP_DISTPROC_F_CACHE_URI;
+ }
+ break;
+
+ default:
+ assert( 0 );
+ return 1;
+ }
+
+ return rc;
+}
+
+static int
+ldap_distproc_db_init(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ ldap_distproc_t *lc = NULL;
+
+ if ( lback == NULL ) {
+ lback = backend_info( "ldap" );
+
+ if ( lback == NULL ) {
+ return 1;
+ }
+ }
+
+ lc = ch_malloc( sizeof( ldap_distproc_t ) );
+ if ( lc == NULL ) {
+ return 1;
+ }
+ memset( lc, 0, sizeof( ldap_distproc_t ) );
+ ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
+
+ on->on_bi.bi_private = (void *)lc;
+
+ return 0;
+}
+
+static int
+ldap_distproc_db_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ ldap_distproc_t *lc = (ldap_distproc_t *)on->on_bi.bi_private;
+
+ int rc = SLAP_CONF_UNKNOWN;
+
+ if ( lc->lc_common_li == NULL ) {
+ void *be_private = be->be_private;
+ ldap_distproc_db_init_common( be );
+ lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
+ be->be_private = be_private;
+ }
+
+ /* Something for the distproc database? */
+ if ( strncasecmp( argv[ 0 ], "distproc-", STRLENOF( "distproc-" ) ) == 0 ) {
+ char *save_argv0 = argv[ 0 ];
+ BackendInfo *bd_info = be->bd_info;
+ void *be_private = be->be_private;
+ ConfigOCs *be_cf_ocs = be->be_cf_ocs;
+ int is_uri = 0;
+
+ argv[ 0 ] += STRLENOF( "distproc-" );
+
+ if ( strcasecmp( argv[ 0 ], "uri" ) == 0 ) {
+ rc = ldap_distproc_db_init_one( be );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "underlying slapd-ldap initialization failed.\n.",
+ fname, lineno );
+ return 1;
+ }
+ lc->lc_cfg_li = be->be_private;
+ is_uri = 1;
+ }
+
+ /* TODO: add checks on what other slapd-ldap(5) args
+ * should be put in the template; this is not quite
+ * harmful, because attributes that shouldn't don't
+ * get actually used, but the user should at least
+ * be warned.
+ */
+
+ be->bd_info = lback;
+ be->be_private = (void *)lc->lc_cfg_li;
+ be->be_cf_ocs = lback->bi_cf_ocs;
+
+ rc = config_generic_wrapper( be, fname, lineno, argc, argv );
+
+ argv[ 0 ] = save_argv0;
+ be->be_cf_ocs = be_cf_ocs;
+ be->be_private = be_private;
+ be->bd_info = bd_info;
+
+ if ( is_uri ) {
+private_destroy:;
+ if ( rc != 0 ) {
+ BackendDB db = *be;
+
+ db.bd_info = lback;
+ db.be_private = (void *)lc->lc_cfg_li;
+ ldap_distproc_db_destroy_one( &db, NULL );
+ lc->lc_cfg_li = NULL;
+
+ } else {
+ if ( lc->lc_cfg_li->li_bvuri == NULL
+ || BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
+ || !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
+ {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "no URI list allowed in slapo-distproc.\n",
+ fname, lineno );
+ rc = 1;
+ goto private_destroy;
+ }
+
+ if ( ldap_tavl_insert( &lc->lc_lai.lai_tree,
+ (caddr_t)lc->lc_cfg_li,
+ ldap_distproc_uri_cmp, ldap_distproc_uri_dup ) )
+ {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ "duplicate URI in slapo-distproc.\n",
+ fname, lineno );
+ rc = 1;
+ goto private_destroy;
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+enum db_which {
+ db_open = 0,
+ db_close,
+ db_destroy,
+
+ db_last
+};
+
+static int
+ldap_distproc_db_func(
+ BackendDB *be,
+ enum db_which which
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ ldap_distproc_t *lc = (ldap_distproc_t *)on->on_bi.bi_private;
+
+ int rc = 0;
+
+ if ( lc ) {
+ BI_db_func *func = (&lback->bi_db_open)[ which ];
+
+ if ( func != NULL && lc->lc_common_li != NULL ) {
+ BackendDB db = *be;
+
+ db.bd_info = lback;
+ db.be_private = lc->lc_common_li;
+
+ rc = func( &db, NULL );
+
+ if ( rc != 0 ) {
+ return rc;
+ }
+
+ if ( lc->lc_lai.lai_tree != NULL ) {
+ TAvlnode *edge = ldap_tavl_end( lc->lc_lai.lai_tree, TAVL_DIR_LEFT );
+ while ( edge ) {
+ TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
+ ldapinfo_t *li = (ldapinfo_t *)edge->avl_data;
+ be->be_private = (void *)li;
+ rc = func( &db, NULL );
+ if ( rc == 1 ) {
+ break;
+ }
+ edge = next;
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+static int
+ldap_distproc_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ return ldap_distproc_db_func( be, db_open );
+}
+
+static int
+ldap_distproc_db_close(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ return ldap_distproc_db_func( be, db_close );
+}
+
+static int
+ldap_distproc_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ ldap_distproc_t *lc = (ldap_distproc_t *)on->on_bi.bi_private;
+
+ int rc;
+
+ rc = ldap_distproc_db_func( be, db_destroy );
+
+ if ( lc ) {
+ ldap_tavl_free( lc->lc_lai.lai_tree, NULL );
+ ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
+ ch_free( lc );
+ }
+
+ return rc;
+}
+
+/*
+ * inits one instance of the slapd-ldap backend, and stores
+ * the private info in be_private of the arg
+ */
+static int
+ldap_distproc_db_init_common(
+ BackendDB *be )
+{
+ BackendInfo *bi = be->bd_info;
+ int t;
+
+ be->bd_info = lback;
+ be->be_private = NULL;
+ t = lback->bi_db_init( be, NULL );
+ if ( t != 0 ) {
+ return t;
+ }
+ be->bd_info = bi;
+
+ return 0;
+}
+
+/*
+ * inits one instance of the slapd-ldap backend, stores
+ * the private info in be_private of the arg and fills
+ * selected fields with data from the template.
+ *
+ * NOTE: add checks about the other fields of the template,
+ * which are ignored and SHOULD NOT be configured by the user.
+ */
+static int
+ldap_distproc_db_init_one(
+ BackendDB *be )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ ldap_distproc_t *lc = (ldap_distproc_t *)on->on_bi.bi_private;
+
+ BackendInfo *bi = be->bd_info;
+ ldapinfo_t *li;
+
+ slap_op_t t;
+
+ be->bd_info = lback;
+ be->be_private = NULL;
+ t = lback->bi_db_init( be, NULL );
+ if ( t != 0 ) {
+ return t;
+ }
+ li = (ldapinfo_t *)be->be_private;
+
+ /* copy common data */
+ li->li_nretries = lc->lc_common_li->li_nretries;
+ li->li_flags = lc->lc_common_li->li_flags;
+ li->li_version = lc->lc_common_li->li_version;
+ for ( t = 0; t < SLAP_OP_LAST; t++ ) {
+ li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
+ }
+ be->bd_info = bi;
+
+ return 0;
+}
+
+static int
+ldap_distproc_connection_destroy(
+ BackendDB *be,
+ Connection *conn
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ ldap_distproc_t *lc = (ldap_distproc_t *)on->on_bi.bi_private;
+ void *private = be->be_private;
+ int rc;
+ TAvlnode *edge;
+
+ be->be_private = NULL;
+ ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
+ edge = ldap_tavl_end( lc->lc_lai.lai_tree, TAVL_DIR_LEFT );
+ while ( edge ) {
+ TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
+ ldapinfo_t *li = (ldapinfo_t *)edge->avl_data;
+ be->be_private = (void *)li;
+ rc = lback->bi_connection_destroy( be, conn );
+ if ( rc == 1 ) {
+ break;
+ }
+ edge = next;
+ }
+ ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
+ be->be_private = private;
+
+ return rc;
+}
+
+static int
+ldap_distproc_parse_returnContRef_ctrl(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ if ( get_returnContRef( op ) != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "returnContinuationReference control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "returnContinuationReference control specified with pagedResults control";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( !BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "returnContinuationReference control: value must be NULL";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ op->o_returnContRef = ctrl->ldctl_iscritical ? SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+ldap_exop_chained_request(
+ Operation *op,
+ SlapReply *rs )
+{
+ Debug( LDAP_DEBUG_STATS, "%s CHAINED REQUEST\n",
+ op->o_log_prefix );
+
+ rs->sr_err = backend_check_restrictions( op, rs,
+ (struct berval *)&slap_EXOP_CHAINEDREQUEST );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ return rs->sr_err;
+ }
+
+ /* by now, just reject requests */
+ rs->sr_text = "under development";
+ return LDAP_UNWILLING_TO_PERFORM;
+}
+
+
+static slap_overinst distproc;
+
+int
+distproc_initialize( void )
+{
+ int rc;
+
+ /* Make sure we don't exceed the bits reserved for userland */
+ config_check_userland( DP_LAST );
+
+ rc = load_extop( (struct berval *)&slap_EXOP_CHAINEDREQUEST,
+ SLAP_EXOP_HIDE, ldap_exop_chained_request );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "slapd-distproc: "
+ "unable to register chainedRequest exop: %d.\n",
+ rc );
+ return rc;
+ }
+
+ rc = supported_feature_load( &slap_FEATURE_CANCHAINOPS );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "slapd-distproc: "
+ "unable to register canChainOperations supported feature: %d.\n",
+ rc );
+ return rc;
+ }
+
+ rc = register_supported_control( LDAP_CONTROL_X_RETURNCONTREF,
+ SLAP_CTRL_GLOBAL|SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
+ ldap_distproc_parse_returnContRef_ctrl, &sc_returnContRef );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "slapd-distproc: "
+ "unable to register returnContinuationReference control: %d.\n",
+ rc );
+ return rc;
+ }
+
+ distproc.on_bi.bi_type = "distproc";
+ distproc.on_bi.bi_db_init = ldap_distproc_db_init;
+ distproc.on_bi.bi_db_config = ldap_distproc_db_config;
+ distproc.on_bi.bi_db_open = ldap_distproc_db_open;
+ distproc.on_bi.bi_db_close = ldap_distproc_db_close;
+ distproc.on_bi.bi_db_destroy = ldap_distproc_db_destroy;
+
+ /* ... otherwise the underlying backend's function would be called,
+ * likely passing an invalid entry; on the contrary, the requested
+ * operational attributes should have been returned while chasing
+ * the referrals. This all in all is a bit messy, because part
+ * of the operational attributes are generated by the backend;
+ * part by the frontend; back-ldap should receive all the available
+ * ones from the remote server, but then, on its own, it strips those
+ * it assumes will be (re)generated by the frontend (e.g.
+ * subschemaSubentry, entryDN, ...) */
+ distproc.on_bi.bi_operational = ldap_distproc_operational;
+
+ distproc.on_bi.bi_connection_destroy = ldap_distproc_connection_destroy;
+
+ distproc.on_response = ldap_distproc_response;
+
+ distproc.on_bi.bi_cf_ocs = distproc_ocs;
+
+ rc = config_register_schema( distproc_cfg, distproc_ocs );
+ if ( rc ) {
+ return rc;
+ }
+
+ return overlay_register( &distproc );
+}
+
+#endif /* SLAP_DISTPROC */
diff --git a/servers/slapd/back-ldap/extended.c b/servers/slapd/back-ldap/extended.c
new file mode 100644
index 0000000..9b60cad
--- /dev/null
+++ b/servers/slapd/back-ldap/extended.c
@@ -0,0 +1,410 @@
+/* extended.c - ldap backend extended routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-ldap.h"
+#include "lber_pvt.h"
+
+typedef int (ldap_back_exop_f)( Operation *op, SlapReply *rs, ldapconn_t **lc );
+
+static ldap_back_exop_f ldap_back_exop_passwd;
+static ldap_back_exop_f ldap_back_exop_generic;
+
+static struct exop {
+ struct berval oid;
+ ldap_back_exop_f *extended;
+} exop_table[] = {
+ { BER_BVC(LDAP_EXOP_MODIFY_PASSWD), ldap_back_exop_passwd },
+ { BER_BVNULL, NULL }
+};
+
+static int
+ldap_back_extended_one( Operation *op, SlapReply *rs, ldap_back_exop_f exop )
+{
+ ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private;
+
+ ldapconn_t *lc = NULL;
+ LDAPControl **ctrls = NULL, **oldctrls = NULL;
+ int rc;
+
+ /* FIXME: this needs to be called here, so it is
+ * called twice; maybe we could avoid the
+ * ldap_back_dobind() call inside each extended()
+ * call ... */
+ if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ return -1;
+ }
+
+ ctrls = oldctrls = op->o_ctrls;
+ if ( ldap_back_controls_add( op, rs, lc, &ctrls ) )
+ {
+ op->o_ctrls = oldctrls;
+ send_ldap_extended( op, rs );
+ rs->sr_text = NULL;
+ /* otherwise frontend resends result */
+ rc = rs->sr_err = SLAPD_ABANDON;
+ goto done;
+ }
+
+ op->o_ctrls = ctrls;
+ rc = exop( op, rs, &lc );
+
+ op->o_ctrls = oldctrls;
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+
+done:;
+ if ( lc != NULL ) {
+ ldap_back_release_conn( li, lc );
+ }
+
+ return rc;
+}
+
+int
+ldap_back_extended(
+ Operation *op,
+ SlapReply *rs )
+{
+ int i;
+
+ RS_ASSERT( !(rs->sr_flags & REP_ENTRY_MASK) );
+ rs->sr_flags &= ~REP_ENTRY_MASK; /* paranoia */
+
+ for ( i = 0; exop_table[i].extended != NULL; i++ ) {
+ if ( bvmatch( &exop_table[i].oid, &op->oq_extended.rs_reqoid ) )
+ {
+ return ldap_back_extended_one( op, rs, exop_table[i].extended );
+ }
+ }
+
+ /* if we get here, the exop is known; the best that we can do
+ * is pass it thru as is */
+ /* FIXME: maybe a list of OIDs to pass thru would be safer */
+ return ldap_back_extended_one( op, rs, ldap_back_exop_generic );
+}
+
+static int
+ldap_back_exop_passwd(
+ Operation *op,
+ SlapReply *rs,
+ ldapconn_t **lcp )
+{
+ ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private;
+
+ ldapconn_t *lc = *lcp;
+ req_pwdexop_s *qpw = &op->oq_pwdexop;
+ LDAPMessage *res;
+ ber_int_t msgid;
+ int rc, isproxy, freedn = 0;
+ int do_retry = 1;
+ char *text = NULL;
+ struct berval dn = op->o_req_dn,
+ ndn = op->o_req_ndn;
+
+ assert( lc != NULL );
+ assert( rs->sr_ctrls == NULL );
+
+ if ( BER_BVISNULL( &ndn ) && op->ore_reqdata != NULL ) {
+ /* NOTE: most of this code is mutated
+ * from slap_passwd_parse();
+ * But here we only need
+ * the first berval... */
+
+ ber_tag_t tag;
+ ber_len_t len = -1;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+
+ struct berval tmpid = BER_BVNULL;
+
+ if ( op->ore_reqdata->bv_len == 0 ) {
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+ ber_init2( ber, op->ore_reqdata, 0 );
+
+ tag = ber_scanf( ber, "{" /*}*/ );
+
+ if ( tag == LBER_ERROR ) {
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_ID ) {
+ tag = ber_get_stringbv( ber, &tmpid, LBER_BV_NOTERM );
+
+ if ( tag == LBER_ERROR ) {
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+
+ if ( !BER_BVISEMPTY( &tmpid ) ) {
+ char idNull = tmpid.bv_val[tmpid.bv_len];
+ tmpid.bv_val[tmpid.bv_len] = '\0';
+ rs->sr_err = dnPrettyNormal( NULL, &tmpid, &dn,
+ &ndn, op->o_tmpmemctx );
+ tmpid.bv_val[tmpid.bv_len] = idNull;
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ /* should have been successfully parsed earlier! */
+ return rs->sr_err;
+ }
+ freedn = 1;
+
+ } else {
+ dn = op->o_dn;
+ ndn = op->o_ndn;
+ }
+ }
+
+ isproxy = ber_bvcmp( &ndn, &op->o_ndn );
+
+ Debug( LDAP_DEBUG_ARGS, "==> ldap_back_exop_passwd(\"%s\")%s\n",
+ dn.bv_val, isproxy ? " (proxy)" : "" );
+
+retry:
+ rc = ldap_passwd( lc->lc_ld, &dn,
+ qpw->rs_old.bv_val ? &qpw->rs_old : NULL,
+ qpw->rs_new.bv_val ? &qpw->rs_new : NULL,
+ op->o_ctrls, NULL, &msgid );
+
+ if ( rc == LDAP_SUCCESS ) {
+ /* TODO: set timeout? */
+ /* by now, make sure no timeout is used (ITS#6282) */
+ struct timeval tv = { -1, 0 };
+ if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res ) == -1 ) {
+ ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER, &rc );
+ rs->sr_err = rc;
+
+ } else {
+ /* only touch when activity actually took place... */
+ if ( li->li_idle_timeout ) {
+ lc->lc_time = op->o_time;
+ }
+
+ /* sigh. parse twice, because parse_passwd
+ * doesn't give us the err / match / msg info.
+ */
+ rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
+ (char **)&rs->sr_matched,
+ &text,
+ NULL, &rs->sr_ctrls, 0 );
+
+ if ( rc == LDAP_SUCCESS ) {
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ struct berval newpw;
+
+ /* this never happens because
+ * the frontend is generating
+ * the new password, so when
+ * the passwd exop is proxied,
+ * it never delegates password
+ * generation to the remote server
+ */
+ rc = ldap_parse_passwd( lc->lc_ld, res,
+ &newpw );
+ if ( rc == LDAP_SUCCESS &&
+ !BER_BVISNULL( &newpw ) )
+ {
+ rs->sr_type = REP_EXTENDED;
+ rs->sr_rspdata = slap_passwd_return( &newpw );
+ free( newpw.bv_val );
+ }
+
+ } else {
+ rc = rs->sr_err;
+ }
+ }
+ ldap_msgfree( res );
+ }
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_err = slap_map_api2result( rs );
+ if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
+ do_retry = 0;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ goto retry;
+ }
+ }
+
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs );
+ }
+
+ if ( text ) rs->sr_text = text;
+ send_ldap_extended( op, rs );
+ /* otherwise frontend resends result */
+ rc = rs->sr_err = SLAPD_ABANDON;
+
+ } else if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs );
+ }
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_EXTENDED ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+ if ( freedn ) {
+ op->o_tmpfree( dn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
+ }
+
+ /* these have to be freed anyway... */
+ if ( rs->sr_matched ) {
+ free( (char *)rs->sr_matched );
+ rs->sr_matched = NULL;
+ }
+
+ if ( rs->sr_ctrls ) {
+ ldap_controls_free( rs->sr_ctrls );
+ rs->sr_ctrls = NULL;
+ }
+
+ if ( text ) {
+ free( text );
+ rs->sr_text = NULL;
+ }
+
+ /* in case, cleanup handler */
+ if ( lc == NULL ) {
+ *lcp = NULL;
+ }
+
+ return rc;
+}
+
+static int
+ldap_back_exop_generic(
+ Operation *op,
+ SlapReply *rs,
+ ldapconn_t **lcp )
+{
+ ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private;
+
+ ldapconn_t *lc = *lcp;
+ LDAPMessage *res;
+ ber_int_t msgid;
+ int rc;
+ int do_retry = 1;
+ char *text = NULL;
+
+ Debug( LDAP_DEBUG_ARGS, "==> ldap_back_exop_generic(%s, \"%s\")\n",
+ op->ore_reqoid.bv_val, op->o_req_dn.bv_val );
+ assert( lc != NULL );
+ assert( rs->sr_ctrls == NULL );
+
+retry:
+ rc = ldap_extended_operation( lc->lc_ld,
+ op->ore_reqoid.bv_val, op->ore_reqdata,
+ op->o_ctrls, NULL, &msgid );
+
+ if ( rc == LDAP_SUCCESS ) {
+ /* TODO: set timeout? */
+ /* by now, make sure no timeout is used (ITS#6282) */
+ struct timeval tv = { -1, 0 };
+ if ( ldap_result( lc->lc_ld, msgid, LDAP_MSG_ALL, &tv, &res ) == -1 ) {
+ ldap_get_option( lc->lc_ld, LDAP_OPT_ERROR_NUMBER, &rc );
+ rs->sr_err = rc;
+
+ } else {
+ /* only touch when activity actually took place... */
+ if ( li->li_idle_timeout ) {
+ lc->lc_time = op->o_time;
+ }
+
+ /* sigh. parse twice, because parse_passwd
+ * doesn't give us the err / match / msg info.
+ */
+ rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
+ (char **)&rs->sr_matched,
+ &text,
+ NULL, &rs->sr_ctrls, 0 );
+ if ( rc == LDAP_SUCCESS ) {
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ rc = ldap_parse_extended_result( lc->lc_ld, res,
+ (char **)&rs->sr_rspoid, &rs->sr_rspdata, 0 );
+ if ( rc == LDAP_SUCCESS ) {
+ rs->sr_type = REP_EXTENDED;
+ }
+
+ } else {
+ rc = rs->sr_err;
+ }
+ }
+ ldap_msgfree( res );
+ }
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_err = slap_map_api2result( rs );
+ if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
+ do_retry = 0;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ goto retry;
+ }
+ }
+
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs );
+ }
+
+ if ( text ) rs->sr_text = text;
+ send_ldap_extended( op, rs );
+ /* otherwise frontend resends result */
+ rc = rs->sr_err = SLAPD_ABANDON;
+
+ } else if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs );
+ }
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_EXTENDED ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+ /* these have to be freed anyway... */
+ if ( rs->sr_matched ) {
+ free( (char *)rs->sr_matched );
+ rs->sr_matched = NULL;
+ }
+
+ if ( rs->sr_ctrls ) {
+ ldap_controls_free( rs->sr_ctrls );
+ rs->sr_ctrls = NULL;
+ }
+
+ if ( text ) {
+ free( text );
+ rs->sr_text = NULL;
+ }
+
+ /* in case, cleanup handler */
+ if ( lc == NULL ) {
+ *lcp = NULL;
+ }
+
+ return rc;
+}
diff --git a/servers/slapd/back-ldap/init.c b/servers/slapd/back-ldap/init.c
new file mode 100644
index 0000000..e2db2a0
--- /dev/null
+++ b/servers/slapd/back-ldap/init.c
@@ -0,0 +1,374 @@
+/* init.c - initialize ldap backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "back-ldap.h"
+#include "ldap_rq.h"
+
+static const ldap_extra_t ldap_extra = {
+ ldap_back_proxy_authz_ctrl,
+ ldap_back_controls_free,
+ slap_idassert_authzfrom_parse,
+ slap_idassert_passthru_parse_cf,
+ slap_idassert_parse,
+ slap_retry_info_destroy,
+ slap_retry_info_parse,
+ slap_retry_info_unparse,
+ ldap_back_connid2str
+};
+
+int
+ldap_back_open( BackendInfo *bi )
+{
+ bi->bi_controls = slap_known_controls;
+ return 0;
+}
+
+int
+ldap_back_initialize( BackendInfo *bi )
+{
+ int rc;
+
+ bi->bi_flags =
+#ifdef LDAP_DYNAMIC_OBJECTS
+ /* this is set because all the support a proxy has to provide
+ * is the capability to forward the refresh exop, and to
+ * pass thru entries that contain the dynamicObject class
+ * and the entryTtl attribute */
+ SLAP_BFLAG_DYNAMIC |
+#endif /* LDAP_DYNAMIC_OBJECTS */
+
+ /* back-ldap recognizes RFC4525 increment;
+ * let the remote server complain, if needed (ITS#5912) */
+ SLAP_BFLAG_INCREMENT;
+
+ bi->bi_open = ldap_back_open;
+ bi->bi_config = 0;
+ bi->bi_close = 0;
+ bi->bi_destroy = 0;
+
+ bi->bi_db_init = ldap_back_db_init;
+ bi->bi_db_config = config_generic_wrapper;
+ bi->bi_db_open = ldap_back_db_open;
+ bi->bi_db_close = ldap_back_db_close;
+ bi->bi_db_destroy = ldap_back_db_destroy;
+
+ bi->bi_op_bind = ldap_back_bind;
+ bi->bi_op_unbind = 0;
+ bi->bi_op_search = ldap_back_search;
+ bi->bi_op_compare = ldap_back_compare;
+ bi->bi_op_modify = ldap_back_modify;
+ bi->bi_op_modrdn = ldap_back_modrdn;
+ bi->bi_op_add = ldap_back_add;
+ bi->bi_op_delete = ldap_back_delete;
+ bi->bi_op_abandon = 0;
+
+ bi->bi_extended = ldap_back_extended;
+
+ bi->bi_chk_referrals = 0;
+ bi->bi_entry_get_rw = ldap_back_entry_get;
+
+ bi->bi_connection_init = 0;
+ bi->bi_connection_destroy = ldap_back_conn_destroy;
+
+ bi->bi_extra = (void *)&ldap_extra;
+
+ rc = ldap_back_init_cf( bi );
+ if ( rc ) {
+ return rc;
+ }
+
+ rc = chain_initialize();
+ if ( rc ) {
+ return rc;
+ }
+
+ rc = pbind_initialize();
+ if ( rc ) {
+ return rc;
+ }
+
+#ifdef SLAP_DISTPROC
+ rc = distproc_initialize();
+ if ( rc ) {
+ return rc;
+ }
+#endif
+ return rc;
+}
+
+int
+ldap_back_db_init( Backend *be, ConfigReply *cr )
+{
+ ldapinfo_t *li;
+ int rc;
+ unsigned i;
+
+ li = (ldapinfo_t *)ch_calloc( 1, sizeof( ldapinfo_t ) );
+ if ( li == NULL ) {
+ return -1;
+ }
+
+ li->li_rebind_f = ldap_back_default_rebind;
+ li->li_urllist_f = ldap_back_default_urllist;
+ li->li_urllist_p = li;
+ ldap_pvt_thread_mutex_init( &li->li_uri_mutex );
+
+ BER_BVZERO( &li->li_acl_authcID );
+ BER_BVZERO( &li->li_acl_authcDN );
+ BER_BVZERO( &li->li_acl_passwd );
+
+ li->li_acl_authmethod = LDAP_AUTH_NONE;
+ BER_BVZERO( &li->li_acl_sasl_mech );
+ li->li_acl.sb_tls = SB_TLS_DEFAULT;
+
+ li->li_idassert_mode = LDAP_BACK_IDASSERT_LEGACY;
+
+ BER_BVZERO( &li->li_idassert_authcID );
+ BER_BVZERO( &li->li_idassert_authcDN );
+ BER_BVZERO( &li->li_idassert_passwd );
+
+ BER_BVZERO( &li->li_idassert_authzID );
+
+ li->li_idassert_authmethod = LDAP_AUTH_NONE;
+ BER_BVZERO( &li->li_idassert_sasl_mech );
+ li->li_idassert_tls = SB_TLS_DEFAULT;
+
+ /* by default, use proxyAuthz control on each operation */
+ li->li_idassert_flags = LDAP_BACK_AUTH_PRESCRIPTIVE;
+
+ li->li_idassert_authz = NULL;
+
+ /* initialize flags */
+ li->li_flags = LDAP_BACK_F_CHASE_REFERRALS;
+
+ /* initialize version */
+ li->li_version = LDAP_VERSION3;
+
+ ldap_pvt_thread_mutex_init( &li->li_conninfo.lai_mutex );
+
+ for ( i = LDAP_BACK_PCONN_FIRST; i < LDAP_BACK_PCONN_LAST; i++ ) {
+ li->li_conn_priv[ i ].lic_num = 0;
+ LDAP_TAILQ_INIT( &li->li_conn_priv[ i ].lic_priv );
+ }
+ li->li_conn_priv_max = LDAP_BACK_CONN_PRIV_DEFAULT;
+
+ ldap_pvt_thread_mutex_init( &li->li_counter_mutex );
+ for ( i = 0; i < SLAP_OP_LAST; i++ ) {
+ ldap_pvt_mp_init( li->li_ops_completed[ i ] );
+ }
+
+ li->li_conn_expire_task = NULL;
+
+ be->be_private = li;
+ SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_NOLASTMOD;
+
+ be->be_cf_ocs = be->bd_info->bi_cf_ocs;
+
+ rc = ldap_back_monitor_db_init( be );
+ if ( rc != 0 ) {
+ /* ignore, by now */
+ rc = 0;
+ }
+
+ return rc;
+}
+
+int
+ldap_back_db_open( BackendDB *be, ConfigReply *cr )
+{
+ ldapinfo_t *li = (ldapinfo_t *)be->be_private;
+
+ slap_bindconf sb = { BER_BVNULL };
+ int rc = 0;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_back_db_open: URI=%s\n",
+ li->li_uri != NULL ? li->li_uri : "" );
+
+ /* by default, use proxyAuthz control on each operation */
+ switch ( li->li_idassert_mode ) {
+ case LDAP_BACK_IDASSERT_LEGACY:
+ case LDAP_BACK_IDASSERT_SELF:
+ /* however, since admin connections are pooled and shared,
+ * only static authzIDs can be native */
+ li->li_idassert_flags &= ~LDAP_BACK_AUTH_NATIVE_AUTHZ;
+ break;
+
+ default:
+ break;
+ }
+
+ ber_str2bv( li->li_uri, 0, 0, &sb.sb_uri );
+ sb.sb_version = li->li_version;
+ sb.sb_method = LDAP_AUTH_SIMPLE;
+ BER_BVSTR( &sb.sb_binddn, "" );
+
+ if ( LDAP_BACK_T_F_DISCOVER( li ) && !LDAP_BACK_T_F( li ) ) {
+ rc = slap_discover_feature( &sb,
+ slap_schema.si_ad_supportedFeatures->ad_cname.bv_val,
+ LDAP_FEATURE_ABSOLUTE_FILTERS );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ li->li_flags |= LDAP_BACK_F_T_F;
+ }
+ }
+
+ if ( LDAP_BACK_CANCEL_DISCOVER( li ) && !LDAP_BACK_CANCEL( li ) ) {
+ rc = slap_discover_feature( &sb,
+ slap_schema.si_ad_supportedExtension->ad_cname.bv_val,
+ LDAP_EXOP_CANCEL );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ li->li_flags |= LDAP_BACK_F_CANCEL_EXOP;
+ }
+ }
+
+ /* monitor setup */
+ rc = ldap_back_monitor_db_open( be );
+ if ( rc != 0 ) {
+ /* ignore by now */
+ rc = 0;
+ }
+
+ li->li_flags |= LDAP_BACK_F_ISOPEN;
+
+ return rc;
+}
+
+void
+ldap_back_conn_free( void *v_lc )
+{
+ ldapconn_t *lc = v_lc;
+
+ if ( lc->lc_ld != NULL ) {
+ ldap_unbind_ext( lc->lc_ld, NULL, NULL );
+ }
+ if ( !BER_BVISNULL( &lc->lc_bound_ndn ) ) {
+ ch_free( lc->lc_bound_ndn.bv_val );
+ }
+ if ( !BER_BVISNULL( &lc->lc_cred ) ) {
+ memset( lc->lc_cred.bv_val, 0, lc->lc_cred.bv_len );
+ ch_free( lc->lc_cred.bv_val );
+ }
+ if ( !BER_BVISNULL( &lc->lc_local_ndn ) ) {
+ ch_free( lc->lc_local_ndn.bv_val );
+ }
+ lc->lc_q.tqe_prev = NULL;
+ lc->lc_q.tqe_next = NULL;
+ ch_free( lc );
+}
+
+int
+ldap_back_db_close( Backend *be, ConfigReply *cr )
+{
+ int rc = 0;
+
+ if ( be->be_private ) {
+ rc = ldap_back_monitor_db_close( be );
+ }
+
+ return rc;
+}
+
+int
+ldap_back_db_destroy( Backend *be, ConfigReply *cr )
+{
+ if ( be->be_private ) {
+ ldapinfo_t *li = ( ldapinfo_t * )be->be_private;
+ unsigned i;
+
+ (void)ldap_back_monitor_db_destroy( be );
+
+ /* Stop and remove the task that prunes expired connections */
+ if ( li->li_conn_expire_task != NULL ) {
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, li->li_conn_expire_task ) ) {
+ ldap_pvt_runqueue_stoptask( &slapd_rq, li->li_conn_expire_task );
+ }
+ ldap_pvt_runqueue_remove( &slapd_rq, li->li_conn_expire_task );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+
+ if ( li->li_uri != NULL ) {
+ ch_free( li->li_uri );
+ li->li_uri = NULL;
+
+ assert( li->li_bvuri != NULL );
+ ber_bvarray_free( li->li_bvuri );
+ li->li_bvuri = NULL;
+ }
+
+ bindconf_free( &li->li_tls );
+ bindconf_free( &li->li_acl );
+ bindconf_free( &li->li_idassert.si_bc );
+
+ if ( li->li_idassert_authz != NULL ) {
+ ber_bvarray_free( li->li_idassert_authz );
+ li->li_idassert_authz = NULL;
+ }
+ if ( li->li_conninfo.lai_tree ) {
+ ldap_tavl_free( li->li_conninfo.lai_tree, ldap_back_conn_free );
+ }
+ for ( i = LDAP_BACK_PCONN_FIRST; i < LDAP_BACK_PCONN_LAST; i++ ) {
+ while ( !LDAP_TAILQ_EMPTY( &li->li_conn_priv[ i ].lic_priv ) ) {
+ ldapconn_t *lc = LDAP_TAILQ_FIRST( &li->li_conn_priv[ i ].lic_priv );
+
+ LDAP_TAILQ_REMOVE( &li->li_conn_priv[ i ].lic_priv, lc, lc_q );
+ ldap_back_conn_free( lc );
+ }
+ }
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ slap_retry_info_destroy( &li->li_quarantine );
+ ldap_pvt_thread_mutex_destroy( &li->li_quarantine_mutex );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ ldap_pvt_thread_mutex_destroy( &li->li_conninfo.lai_mutex );
+ ldap_pvt_thread_mutex_destroy( &li->li_uri_mutex );
+
+ for ( i = 0; i < SLAP_OP_LAST; i++ ) {
+ ldap_pvt_mp_clear( li->li_ops_completed[ i ] );
+ }
+ ldap_pvt_thread_mutex_destroy( &li->li_counter_mutex );
+ }
+
+ ch_free( be->be_private );
+
+ return 0;
+}
+
+#if SLAPD_LDAP == SLAPD_MOD_DYNAMIC
+
+/* conditionally define the init_module() function */
+SLAP_BACKEND_INIT_MODULE( ldap )
+
+#endif /* SLAPD_LDAP == SLAPD_MOD_DYNAMIC */
diff --git a/servers/slapd/back-ldap/modify.c b/servers/slapd/back-ldap/modify.c
new file mode 100644
index 0000000..53e8a68
--- /dev/null
+++ b/servers/slapd/back-ldap/modify.c
@@ -0,0 +1,136 @@
+/* modify.c - ldap backend modify function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-ldap.h"
+
+int
+ldap_back_modify(
+ Operation *op,
+ SlapReply *rs )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ ldapconn_t *lc = NULL;
+ LDAPMod **modv = NULL,
+ *mods = NULL;
+ Modifications *ml;
+ int i, j, rc;
+ ber_int_t msgid;
+ int isupdate;
+ ldap_back_send_t retrying = LDAP_BACK_RETRYING;
+ LDAPControl **ctrls = NULL;
+
+ if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ return rs->sr_err;
+ }
+
+ for ( i = 0, ml = op->orm_modlist; ml; i++, ml = ml->sml_next )
+ /* just count mods */ ;
+
+ modv = (LDAPMod **)ch_malloc( ( i + 1 )*sizeof( LDAPMod * )
+ + i*sizeof( LDAPMod ) );
+ if ( modv == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto cleanup;
+ }
+ mods = (LDAPMod *)&modv[ i + 1 ];
+
+ isupdate = be_shadow_update( op );
+ for ( i = 0, ml = op->orm_modlist; ml; ml = ml->sml_next ) {
+ if ( !isupdate && !get_relax( op ) && ml->sml_desc->ad_type->sat_no_user_mod )
+ {
+ continue;
+ }
+
+ modv[ i ] = &mods[ i ];
+ mods[ i ].mod_op = ( ml->sml_op | LDAP_MOD_BVALUES );
+ mods[ i ].mod_type = ml->sml_desc->ad_cname.bv_val;
+
+ if ( ml->sml_values != NULL ) {
+ for ( j = 0; !BER_BVISNULL( &ml->sml_values[ j ] ); j++ )
+ /* just count mods */ ;
+ mods[ i ].mod_bvalues =
+ (struct berval **)ch_malloc( ( j + 1 )*sizeof( struct berval * ) );
+ for ( j = 0; !BER_BVISNULL( &ml->sml_values[ j ] ); j++ )
+ {
+ mods[ i ].mod_bvalues[ j ] = &ml->sml_values[ j ];
+ }
+ mods[ i ].mod_bvalues[ j ] = NULL;
+
+ } else {
+ mods[ i ].mod_bvalues = NULL;
+ }
+
+ i++;
+ }
+ modv[ i ] = 0;
+
+retry:;
+ ctrls = op->o_ctrls;
+ rc = ldap_back_controls_add( op, rs, lc, &ctrls );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ rs->sr_err = ldap_modify_ext( lc->lc_ld, op->o_req_dn.bv_val, modv,
+ ctrls, NULL, &msgid );
+ rc = ldap_back_op_result( lc, op, rs, msgid,
+ li->li_timeout[ SLAP_OP_MODIFY ],
+ ( LDAP_BACK_SENDRESULT | retrying ) );
+ if ( rs->sr_err == LDAP_UNAVAILABLE && retrying ) {
+ retrying &= ~LDAP_BACK_RETRYING;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ /* if the identity changed, there might be need to re-authz */
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+ goto retry;
+ }
+ }
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_MODIFY ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+cleanup:;
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+
+ for ( i = 0; modv[ i ]; i++ ) {
+ ch_free( modv[ i ]->mod_bvalues );
+ }
+ ch_free( modv );
+
+ if ( lc != NULL ) {
+ ldap_back_release_conn( li, lc );
+ }
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/back-ldap/modrdn.c b/servers/slapd/back-ldap/modrdn.c
new file mode 100644
index 0000000..9c441eb
--- /dev/null
+++ b/servers/slapd/back-ldap/modrdn.c
@@ -0,0 +1,123 @@
+/* modrdn.c - ldap backend modrdn function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-ldap.h"
+
+int
+ldap_back_modrdn(
+ Operation *op,
+ SlapReply *rs )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ ldapconn_t *lc = NULL;
+ ber_int_t msgid;
+ LDAPControl **ctrls = NULL;
+ ldap_back_send_t retrying = LDAP_BACK_RETRYING;
+ int rc = LDAP_SUCCESS;
+ char *newSup = NULL;
+ struct berval newrdn = BER_BVNULL;
+
+ if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ return rs->sr_err;
+ }
+
+ if ( op->orr_newSup ) {
+ /* needs LDAPv3 */
+ switch ( li->li_version ) {
+ case LDAP_VERSION3:
+ break;
+
+ case 0:
+ if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
+ break;
+ }
+ /* fall thru */
+
+ default:
+ /* op->o_protocol cannot be anything but LDAPv3,
+ * otherwise wouldn't be here */
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ newSup = op->orr_newSup->bv_val;
+ }
+
+ /* NOTE: we need to copy the newRDN in case it was formed
+ * from a DN by simply changing the length (ITS#5397) */
+ newrdn = op->orr_newrdn;
+ if ( newrdn.bv_val[ newrdn.bv_len ] != '\0' ) {
+ ber_dupbv_x( &newrdn, &op->orr_newrdn, op->o_tmpmemctx );
+ }
+
+retry:
+ ctrls = op->o_ctrls;
+ rc = ldap_back_controls_add( op, rs, lc, &ctrls );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ rs->sr_err = ldap_rename( lc->lc_ld, op->o_req_dn.bv_val,
+ newrdn.bv_val, newSup,
+ op->orr_deleteoldrdn, ctrls, NULL, &msgid );
+ rc = ldap_back_op_result( lc, op, rs, msgid,
+ li->li_timeout[ SLAP_OP_MODRDN ],
+ ( LDAP_BACK_SENDRESULT | retrying ) );
+ if ( rs->sr_err == LDAP_UNAVAILABLE && retrying ) {
+ retrying &= ~LDAP_BACK_RETRYING;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ /* if the identity changed, there might be need to re-authz */
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+ goto retry;
+ }
+ }
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_MODRDN ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+cleanup:
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+
+ if ( newrdn.bv_val != op->orr_newrdn.bv_val ) {
+ op->o_tmpfree( newrdn.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( lc != NULL ) {
+ ldap_back_release_conn( li, lc );
+ }
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/back-ldap/monitor.c b/servers/slapd/back-ldap/monitor.c
new file mode 100644
index 0000000..16c2d6f
--- /dev/null
+++ b/servers/slapd/back-ldap/monitor.c
@@ -0,0 +1,1074 @@
+/* monitor.c - monitor ldap backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+#include <ac/stdlib.h>
+#include <ac/errno.h>
+#include <sys/stat.h>
+#include "lutil.h"
+#include "back-ldap.h"
+
+#include "slap-config.h"
+
+static ObjectClass *oc_olmLDAPDatabase;
+static ObjectClass *oc_olmLDAPConnection;
+
+static ObjectClass *oc_monitorContainer;
+static ObjectClass *oc_monitorCounterObject;
+
+static AttributeDescription *ad_olmDbURIList;
+static AttributeDescription *ad_olmDbOperations;
+static AttributeDescription *ad_olmDbBoundDN;
+static AttributeDescription *ad_olmDbConnFlags;
+static AttributeDescription *ad_olmDbConnURI;
+static AttributeDescription *ad_olmDbPeerAddress;
+
+/*
+ * Stolen from back-monitor/operations.c
+ * We don't need the normalized rdn's though.
+ */
+struct ldap_back_monitor_ops_t {
+ struct berval rdn;
+} ldap_back_monitor_op[] = {
+ { BER_BVC( "cn=Bind" ) },
+ { BER_BVC( "cn=Unbind" ) },
+ { BER_BVC( "cn=Search" ) },
+ { BER_BVC( "cn=Compare" ) },
+ { BER_BVC( "cn=Modify" ) },
+ { BER_BVC( "cn=Modrdn" ) },
+ { BER_BVC( "cn=Add" ) },
+ { BER_BVC( "cn=Delete" ) },
+ { BER_BVC( "cn=Abandon" ) },
+ { BER_BVC( "cn=Extended" ) },
+
+ { BER_BVNULL }
+};
+
+/* Corresponds to connection flags in back-ldap.h */
+static struct {
+ unsigned flag;
+ struct berval name;
+} s_flag[] = {
+ { LDAP_BACK_FCONN_ISBOUND, BER_BVC( "bound" ) },
+ { LDAP_BACK_FCONN_ISANON, BER_BVC( "anonymous" ) },
+ { LDAP_BACK_FCONN_ISPRIV, BER_BVC( "privileged" ) },
+ { LDAP_BACK_FCONN_ISTLS, BER_BVC( "TLS" ) },
+ { LDAP_BACK_FCONN_BINDING, BER_BVC( "binding" ) },
+ { LDAP_BACK_FCONN_TAINTED, BER_BVC( "tainted" ) },
+ { LDAP_BACK_FCONN_ABANDON, BER_BVC( "abandon" ) },
+ { LDAP_BACK_FCONN_ISIDASR, BER_BVC( "idassert" ) },
+ { LDAP_BACK_FCONN_CACHED, BER_BVC( "cached" ) },
+
+ { 0 }
+};
+
+
+/*
+ * NOTE: there's some confusion in monitor OID arc;
+ * by now, let's consider:
+ *
+ * Subsystems monitor attributes 1.3.6.1.4.1.4203.666.1.55.0
+ * Databases monitor attributes 1.3.6.1.4.1.4203.666.1.55.0.1
+ * LDAP database monitor attributes 1.3.6.1.4.1.4203.666.1.55.0.1.2
+ *
+ * Subsystems monitor objectclasses 1.3.6.1.4.1.4203.666.3.16.0
+ * Databases monitor objectclasses 1.3.6.1.4.1.4203.666.3.16.0.1
+ * LDAP database monitor objectclasses 1.3.6.1.4.1.4203.666.3.16.0.1.2
+ */
+
+static struct {
+ char *name;
+ char *oid;
+} s_oid[] = {
+ { "olmLDAPAttributes", "olmDatabaseAttributes:2" },
+ { "olmLDAPObjectClasses", "olmDatabaseObjectClasses:2" },
+
+ { NULL }
+};
+
+static struct {
+ char *desc;
+ AttributeDescription **ad;
+} s_at[] = {
+ { "( olmLDAPAttributes:1 "
+ "NAME ( 'olmDbURIList' ) "
+ "DESC 'List of URIs a proxy is serving; can be modified run-time' "
+ "SUP managedInfo )",
+ &ad_olmDbURIList },
+ { "( olmLDAPAttributes:2 "
+ "NAME ( 'olmDbOperation' ) "
+ "DESC 'monitor operations performed' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmDbOperations },
+ { "( olmLDAPAttributes:3 "
+ "NAME ( 'olmDbBoundDN' ) "
+ "DESC 'monitor connection authorization DN' "
+ "SUP monitorConnectionAuthzDN "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmDbBoundDN },
+ { "( olmLDAPAttributes:4 "
+ "NAME ( 'olmDbConnFlags' ) "
+ "DESC 'monitor connection flags' "
+ "SUP monitoredInfo "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmDbConnFlags },
+ { "( olmLDAPAttributes:5 "
+ "NAME ( 'olmDbConnURI' ) "
+ "DESC 'monitor connection URI' "
+ "SUP monitorConnectionPeerAddress "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmDbConnURI },
+ { "( olmLDAPAttributes:6 "
+ "NAME ( 'olmDbConnPeerAddress' ) "
+ "DESC 'monitor connection peer address' "
+ "SUP monitorConnectionPeerAddress "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmDbPeerAddress },
+
+ { NULL }
+};
+
+static struct {
+ char *name;
+ ObjectClass **oc;
+} s_moc[] = {
+ { "monitorContainer", &oc_monitorContainer },
+ { "monitorCounterObject", &oc_monitorCounterObject },
+
+ { NULL }
+};
+
+static struct {
+ char *desc;
+ ObjectClass **oc;
+} s_oc[] = {
+ /* augments an existing object, so it must be AUXILIARY
+ * FIXME: derive from some ABSTRACT "monitoredEntity"? */
+ { "( olmLDAPObjectClasses:1 "
+ "NAME ( 'olmLDAPDatabase' ) "
+ "SUP top AUXILIARY "
+ "MAY ( "
+ "olmDbURIList "
+ ") )",
+ &oc_olmLDAPDatabase },
+ { "( olmLDAPObjectClasses:2 "
+ "NAME ( 'olmLDAPConnection' ) "
+ "SUP monitorConnection STRUCTURAL "
+ "MAY ( "
+ "olmDbBoundDN "
+ "$ olmDbConnFlags "
+ "$ olmDbConnURI "
+ "$ olmDbConnPeerAddress "
+ ") )",
+ &oc_olmLDAPConnection },
+
+ { NULL }
+};
+
+static int
+ldap_back_monitor_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ void *priv )
+{
+ ldapinfo_t *li = (ldapinfo_t *)priv;
+
+ Attribute *a;
+
+ /* update olmDbURIList */
+ a = attr_find( e->e_attrs, ad_olmDbURIList );
+ if ( a != NULL ) {
+ struct berval bv;
+
+ assert( a->a_vals != NULL );
+ assert( !BER_BVISNULL( &a->a_vals[ 0 ] ) );
+ assert( BER_BVISNULL( &a->a_vals[ 1 ] ) );
+
+ ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
+ if ( li->li_uri ) {
+ ber_str2bv( li->li_uri, 0, 0, &bv );
+ if ( !bvmatch( &a->a_vals[ 0 ], &bv ) ) {
+ ber_bvreplace( &a->a_vals[ 0 ], &bv );
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+ldap_back_monitor_modify(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ void *priv )
+{
+ ldapinfo_t *li = (ldapinfo_t *) priv;
+
+ Attribute *save_attrs = NULL;
+ Modifications *ml,
+ *ml_olmDbURIList = NULL;
+ struct berval ul = BER_BVNULL;
+ int got = 0;
+
+ for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
+ if ( ml->sml_desc == ad_olmDbURIList ) {
+ if ( ml_olmDbURIList != NULL ) {
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ rs->sr_text = "conflicting modifications";
+ goto done;
+ }
+
+ if ( ml->sml_op != LDAP_MOD_REPLACE ) {
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ rs->sr_text = "modification not allowed";
+ goto done;
+ }
+
+ ml_olmDbURIList = ml;
+ got++;
+ continue;
+ }
+ }
+
+ if ( got == 0 ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ save_attrs = attrs_dup( e->e_attrs );
+
+ if ( ml_olmDbURIList != NULL ) {
+ Attribute *a = NULL;
+ LDAPURLDesc *ludlist = NULL;
+ int rc;
+
+ ml = ml_olmDbURIList;
+ assert( ml->sml_nvalues != NULL );
+
+ if ( BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ rs->sr_text = "no value provided";
+ goto done;
+ }
+
+ if ( !BER_BVISNULL( &ml->sml_nvalues[ 1 ] ) ) {
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ rs->sr_text = "multiple values provided";
+ goto done;
+ }
+
+ rc = ldap_url_parselist_ext( &ludlist,
+ ml->sml_nvalues[ 0 ].bv_val, NULL,
+ LDAP_PVT_URL_PARSE_NOEMPTY_HOST
+ | LDAP_PVT_URL_PARSE_DEF_PORT );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ rs->sr_err = LDAP_INVALID_SYNTAX;
+ rs->sr_text = "unable to parse URI list";
+ goto done;
+ }
+
+ ul.bv_val = ldap_url_list2urls( ludlist );
+ ldap_free_urllist( ludlist );
+ if ( ul.bv_val == NULL ) {
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+ ul.bv_len = strlen( ul.bv_val );
+
+ a = attr_find( e->e_attrs, ad_olmDbURIList );
+ if ( a != NULL ) {
+ if ( a->a_nvals == a->a_vals ) {
+ a->a_nvals = ch_calloc( sizeof( struct berval ), 2 );
+ }
+
+ ber_bvreplace( &a->a_vals[ 0 ], &ul );
+ ber_bvreplace( &a->a_nvals[ 0 ], &ul );
+
+ } else {
+ attr_merge_normalize_one( e, ad_olmDbURIList, &ul, NULL );
+ }
+ }
+
+ /* apply changes */
+ if ( !BER_BVISNULL( &ul ) ) {
+ ldap_pvt_thread_mutex_lock( &li->li_uri_mutex );
+ if ( li->li_uri ) {
+ ch_free( li->li_uri );
+ }
+ li->li_uri = ul.bv_val;
+ ldap_pvt_thread_mutex_unlock( &li->li_uri_mutex );
+
+ BER_BVZERO( &ul );
+ }
+
+done:;
+ if ( !BER_BVISNULL( &ul ) ) {
+ ldap_memfree( ul.bv_val );
+ }
+
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ attrs_free( save_attrs );
+ return SLAP_CB_CONTINUE;
+ }
+
+ attrs_free( e->e_attrs );
+ e->e_attrs = save_attrs;
+
+ return rs->sr_err;
+}
+
+static int
+ldap_back_monitor_free(
+ Entry *e,
+ void **priv )
+{
+ ldapinfo_t *li = (ldapinfo_t *)(*priv);
+
+ *priv = NULL;
+
+ if ( !slapd_shutdown ) {
+ memset( &li->li_monitor_info, 0, sizeof( li->li_monitor_info ) );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+ldap_back_monitor_subsystem_destroy(
+ BackendDB *be,
+ monitor_subsys_t *ms)
+{
+ free(ms->mss_dn.bv_val);
+ BER_BVZERO(&ms->mss_dn);
+
+ free(ms->mss_ndn.bv_val);
+ BER_BVZERO(&ms->mss_ndn);
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Connection monitoring subsystem:
+ * Tries to mimic what the cn=connections,cn=monitor subsystem does
+ * by creating volatile entries for each connection and populating them
+ * according to the information attached to the connection.
+ * At this moment the only exposed information is the DN used to bind it.
+ * Also note that the connection IDs are not and probably never will be
+ * stable.
+ */
+
+struct ldap_back_monitor_conn_arg {
+ Operation *op;
+ monitor_subsys_t *ms;
+ Entry **ep;
+};
+
+/* code stolen from daemon.c */
+static int
+ldap_back_monitor_conn_peername(
+ LDAP *ld,
+ struct berval *bv)
+{
+ Sockbuf *sockbuf;
+ ber_socket_t socket;
+ Sockaddr sa;
+ socklen_t salen = sizeof(sa);
+ const char *peeraddr = NULL;
+ /* we assume INET6_ADDRSTRLEN > INET_ADDRSTRLEN */
+ char addr[INET6_ADDRSTRLEN];
+#ifdef LDAP_PF_LOCAL
+ char peername[MAXPATHLEN + sizeof("PATH=")];
+#elif defined(LDAP_PF_INET6)
+ char peername[sizeof("IP=[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535")];
+#else /* ! LDAP_PF_LOCAL && ! LDAP_PF_INET6 */
+ char peername[sizeof("IP=255.255.255.255:65336")];
+#endif /* LDAP_PF_LOCAL */
+
+ assert( bv != NULL );
+
+ ldap_get_option( ld, LDAP_OPT_SOCKBUF, (void **)&sockbuf );
+ ber_sockbuf_ctrl( sockbuf, LBER_SB_OPT_GET_FD, &socket );
+ getpeername( socket, (struct sockaddr *)&sa, &salen );
+
+ switch ( sa.sa_addr.sa_family ) {
+#ifdef LDAP_PF_LOCAL
+ case AF_LOCAL:
+ sprintf( peername, "PATH=%s", sa.sa_un_addr.sun_path );
+ break;
+#endif /* LDAP_PF_LOCAL */
+
+#ifdef LDAP_PF_INET6
+ case AF_INET6:
+ if ( IN6_IS_ADDR_V4MAPPED(&sa.sa_in6_addr.sin6_addr) ) {
+#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
+ peeraddr = inet_ntop( AF_INET,
+ ((struct in_addr *)&sa.sa_in6_addr.sin6_addr.s6_addr[12]),
+ addr, sizeof(addr) );
+#else /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ peeraddr = inet_ntoa( *((struct in_addr *)
+ &sa.sa_in6_addr.sin6_addr.s6_addr[12]) );
+#endif /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ if ( !peeraddr ) peeraddr = SLAP_STRING_UNKNOWN;
+ sprintf( peername, "IP=%s:%d", peeraddr,
+ (unsigned) ntohs( sa.sa_in6_addr.sin6_port ) );
+ } else {
+ peeraddr = inet_ntop( AF_INET6,
+ &sa.sa_in6_addr.sin6_addr,
+ addr, sizeof addr );
+ if ( !peeraddr ) peeraddr = SLAP_STRING_UNKNOWN;
+ sprintf( peername, "IP=[%s]:%d", peeraddr,
+ (unsigned) ntohs( sa.sa_in6_addr.sin6_port ) );
+ }
+ break;
+#endif /* LDAP_PF_INET6 */
+
+ case AF_INET: {
+#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
+ peeraddr = inet_ntop( AF_INET, &sa.sa_in_addr.sin_addr,
+ addr, sizeof(addr) );
+#else /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ peeraddr = inet_ntoa( sa.sa_in_addr.sin_addr );
+#endif /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ if ( !peeraddr ) peeraddr = SLAP_STRING_UNKNOWN;
+ sprintf( peername, "IP=%s:%d", peeraddr,
+ (unsigned) ntohs( sa.sa_in_addr.sin_port ) );
+ } break;
+
+ default:
+ sprintf( peername, SLAP_STRING_UNKNOWN );
+ }
+
+ ber_str2bv( peername, 0, 1, bv );
+ return LDAP_SUCCESS;
+}
+
+static int
+ldap_back_monitor_conn_entry(
+ ldapconn_t *lc,
+ struct ldap_back_monitor_conn_arg *arg )
+{
+ Entry *e;
+ monitor_entry_t *mp;
+ monitor_extra_t *mbe = arg->op->o_bd->bd_info->bi_extra;
+ char buf[SLAP_TEXT_BUFLEN];
+ char *ptr;
+ struct berval bv;
+ int i;
+
+ bv.bv_val = buf;
+ bv.bv_len = snprintf( bv.bv_val, SLAP_TEXT_BUFLEN,
+ "cn=Connection %lu", lc->lc_connid );
+
+ e = mbe->entry_stub( &arg->ms->mss_dn, &arg->ms->mss_ndn, &bv,
+ oc_monitorContainer, NULL, NULL );
+
+ attr_merge_normalize_one( e, ad_olmDbBoundDN, &lc->lc_bound_ndn, NULL );
+
+ for ( i = 0; s_flag[i].flag; i++ )
+ {
+ if ( lc->lc_flags & s_flag[i].flag )
+ {
+ attr_merge_normalize_one( e, ad_olmDbConnFlags, &s_flag[i].name, NULL );
+ }
+ }
+
+ ldap_get_option( lc->lc_ld, LDAP_OPT_URI, &bv.bv_val );
+ ptr = strchr( bv.bv_val, ' ' );
+ bv.bv_len = ptr ? ptr - bv.bv_val : strlen(bv.bv_val);
+ attr_merge_normalize_one( e, ad_olmDbConnURI, &bv, NULL );
+ ch_free( bv.bv_val );
+
+ ldap_back_monitor_conn_peername( lc->lc_ld, &bv );
+ attr_merge_normalize_one( e, ad_olmDbPeerAddress, &bv, NULL );
+ ch_free( bv.bv_val );
+
+ mp = mbe->entrypriv_create();
+ e->e_private = mp;
+ mp->mp_info = arg->ms;
+ mp->mp_flags = MONITOR_F_SUB | MONITOR_F_VOLATILE;
+
+ *arg->ep = e;
+ arg->ep = &mp->mp_next;
+
+ return 0;
+}
+
+static int
+ldap_back_monitor_conn_create(
+ Operation *op,
+ SlapReply *rs,
+ struct berval *ndn,
+ Entry *e_parent,
+ Entry **ep )
+{
+ monitor_entry_t *mp_parent;
+ monitor_subsys_t *ms;
+ ldapinfo_t *li;
+ ldapconn_t *lc;
+
+ struct ldap_back_monitor_conn_arg *arg;
+ int conn_type;
+ TAvlnode *edge;
+
+ assert( e_parent->e_private != NULL );
+
+ mp_parent = e_parent->e_private;
+ ms = (monitor_subsys_t *)mp_parent->mp_info;
+ li = (ldapinfo_t *)ms->mss_private;
+
+ arg = ch_calloc( 1, sizeof(struct ldap_back_monitor_conn_arg) );
+ arg->op = op;
+ arg->ep = ep;
+ arg->ms = ms;
+
+ for ( conn_type = LDAP_BACK_PCONN_FIRST;
+ conn_type < LDAP_BACK_PCONN_LAST;
+ conn_type++ )
+ {
+ LDAP_TAILQ_FOREACH( lc,
+ &li->li_conn_priv[ conn_type ].lic_priv,
+ lc_q )
+ {
+ ldap_back_monitor_conn_entry( lc, arg );
+ }
+ }
+
+ edge = ldap_tavl_end( li->li_conninfo.lai_tree, TAVL_DIR_LEFT );
+ while ( edge ) {
+ TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
+ ldapconn_t *lc = (ldapconn_t *)edge->avl_data;
+ ldap_back_monitor_conn_entry( lc, arg );
+ edge = next;
+ }
+
+ ch_free( arg );
+
+ return 0;
+}
+
+static int
+ldap_back_monitor_conn_init(
+ BackendDB *be,
+ monitor_subsys_t *ms )
+{
+ ldapinfo_t *li = (ldapinfo_t *) ms->mss_private;
+ monitor_extra_t *mbe;
+
+ Entry *e;
+ int rc;
+
+ assert( be != NULL );
+ mbe = (monitor_extra_t *) be->bd_info->bi_extra;
+
+ ms->mss_dn = ms->mss_ndn = li->li_monitor_info.lmi_ndn;
+ ms->mss_rdn = li->li_monitor_info.lmi_conn_rdn;
+ ms->mss_create = ldap_back_monitor_conn_create;
+ ms->mss_destroy = ldap_back_monitor_subsystem_destroy;
+
+ e = mbe->entry_stub( &ms->mss_dn, &ms->mss_ndn,
+ &ms->mss_rdn, oc_monitorContainer, NULL, NULL );
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_conn_init: "
+ "unable to create entry \"%s,%s\"\n",
+ li->li_monitor_info.lmi_conn_rdn.bv_val,
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ ber_dupbv( &ms->mss_dn, &e->e_name );
+ ber_dupbv( &ms->mss_ndn, &e->e_nname );
+
+ rc = mbe->register_entry( e, NULL, ms, MONITOR_F_VOLATILE_CH );
+
+ /* add labeledURI and special, modifiable URI value */
+ if ( rc == LDAP_SUCCESS && li->li_uri != NULL ) {
+ struct berval bv;
+ Attribute *a;
+ LDAPURLDesc *ludlist = NULL;
+ monitor_callback_t *cb = NULL;
+
+ a = attr_alloc( ad_olmDbURIList );
+
+ ber_str2bv( li->li_uri, 0, 0, &bv );
+ attr_valadd( a, &bv, NULL, 1 );
+ attr_normalize( a->a_desc, a->a_vals, &a->a_nvals, NULL );
+
+ rc = ldap_url_parselist_ext( &ludlist,
+ li->li_uri, NULL,
+ LDAP_PVT_URL_PARSE_NOEMPTY_HOST
+ | LDAP_PVT_URL_PARSE_DEF_PORT );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_db_open: "
+ "unable to parse URI list (ignored)\n" );
+ } else {
+ Attribute *a2 = attr_alloc( slap_schema.si_ad_labeledURI );
+
+ a->a_next = a2;
+
+ for ( ; ludlist != NULL; ) {
+ LDAPURLDesc *next = ludlist->lud_next;
+
+ bv.bv_val = ldap_url_desc2str( ludlist );
+ assert( bv.bv_val != NULL );
+ ldap_free_urldesc( ludlist );
+ bv.bv_len = strlen( bv.bv_val );
+ attr_valadd( a2, &bv, NULL, 1 );
+ ch_free( bv.bv_val );
+
+ ludlist = next;
+ }
+
+ attr_normalize( a2->a_desc, a2->a_vals, &a2->a_nvals, NULL );
+ }
+
+ cb = ch_calloc( sizeof( monitor_callback_t ), 1 );
+ cb->mc_update = ldap_back_monitor_update;
+ cb->mc_modify = ldap_back_monitor_modify;
+ cb->mc_free = ldap_back_monitor_free;
+ cb->mc_private = (void *)li;
+
+ rc = mbe->register_entry_attrs( &ms->mss_ndn, a, cb, NULL, -1, NULL );
+
+ attr_free( a->a_next );
+ attr_free( a );
+
+ if ( rc != LDAP_SUCCESS )
+ {
+ ch_free( cb );
+ }
+ }
+
+ entry_free( e );
+
+ return rc;
+}
+
+/*
+ * Operation monitoring subsystem:
+ * Looks a lot like the cn=operations,cn=monitor subsystem except that at this
+ * moment, only completed operations are counted. Each entry has a separate
+ * callback with all the needed information linked there in the structure
+ * below so that the callback need not locate it over and over again.
+ */
+
+struct ldap_back_monitor_op_counter {
+ ldap_pvt_mp_t *data;
+ ldap_pvt_thread_mutex_t *mutex;
+};
+
+static void
+ldap_back_monitor_ops_dispose(
+ void **priv)
+{
+ struct ldap_back_monitor_op_counter *counter = *priv;
+
+ ch_free( counter );
+ counter = NULL;
+}
+
+static int
+ldap_back_monitor_ops_free(
+ Entry *e,
+ void **priv)
+{
+ ldap_back_monitor_ops_dispose( priv );
+ return LDAP_SUCCESS;
+}
+
+static int
+ldap_back_monitor_ops_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ void *priv )
+{
+ struct ldap_back_monitor_op_counter *counter = priv;
+ Attribute *a;
+
+ /*TODO
+ * what about initiated/completed?
+ */
+ a = attr_find( e->e_attrs, ad_olmDbOperations );
+ assert( a != NULL );
+
+ ldap_pvt_thread_mutex_lock( counter->mutex );
+ UI2BV( &a->a_vals[ 0 ], *counter->data );
+ ldap_pvt_thread_mutex_unlock( counter->mutex );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+ldap_back_monitor_ops_init(
+ BackendDB *be,
+ monitor_subsys_t *ms )
+{
+ ldapinfo_t *li = (ldapinfo_t *) ms->mss_private;
+
+ monitor_extra_t *mbe;
+ Entry *e, *parent;
+ int rc;
+ slap_op_t op;
+ struct berval value = BER_BVC( "0" );
+
+ assert( be != NULL );
+
+ mbe = (monitor_extra_t *) be->bd_info->bi_extra;
+
+ ms->mss_dn = ms->mss_ndn = li->li_monitor_info.lmi_ndn;
+ ms->mss_rdn = li->li_monitor_info.lmi_ops_rdn;
+ ms->mss_destroy = ldap_back_monitor_subsystem_destroy;
+
+ parent = mbe->entry_stub( &ms->mss_dn, &ms->mss_ndn,
+ &ms->mss_rdn, oc_monitorContainer, NULL, NULL );
+ if ( parent == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_ops_init: "
+ "unable to create entry \"%s,%s\"\n",
+ li->li_monitor_info.lmi_ops_rdn.bv_val,
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ ber_dupbv( &ms->mss_dn, &parent->e_name );
+ ber_dupbv( &ms->mss_ndn, &parent->e_nname );
+
+ rc = mbe->register_entry( parent, NULL, ms, MONITOR_F_PERSISTENT_CH );
+ if ( rc != LDAP_SUCCESS )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_ops_init: "
+ "unable to register entry \"%s\" for monitoring\n",
+ parent->e_name.bv_val );
+ goto done;
+ }
+
+ for ( op = 0; op < SLAP_OP_LAST; op++ )
+ {
+ monitor_callback_t *cb;
+ struct ldap_back_monitor_op_counter *counter;
+
+ e = mbe->entry_stub( &parent->e_name, &parent->e_nname,
+ &ldap_back_monitor_op[op].rdn,
+ oc_monitorCounterObject, NULL, NULL );
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_ops_init: "
+ "unable to create entry \"%s,%s\"\n",
+ ldap_back_monitor_op[op].rdn.bv_val,
+ parent->e_nname.bv_val );
+ return( -1 );
+ }
+
+ attr_merge_normalize_one( e, ad_olmDbOperations, &value, NULL );
+
+ counter = ch_malloc( sizeof( struct ldap_back_monitor_op_counter ) );
+ counter->data = &li->li_ops_completed[ op ];
+ counter->mutex = &li->li_counter_mutex;
+
+ /*
+ * We cannot share a single callback between entries.
+ *
+ * monitor_cache_destroy() tries to free all callbacks and it's called
+ * before mss_destroy() so we have no chance of handling it ourselves
+ */
+ cb = ch_calloc( sizeof( monitor_callback_t ), 1 );
+ cb->mc_update = ldap_back_monitor_ops_update;
+ cb->mc_free = ldap_back_monitor_ops_free;
+ cb->mc_dispose = ldap_back_monitor_ops_dispose;
+ cb->mc_private = (void *)counter;
+
+ rc = mbe->register_entry( e, cb, ms, 0 );
+
+ /* TODO: register_entry has stored a duplicate so we might actually reuse it
+ * instead of recreating it every time... */
+ entry_free( e );
+
+ if ( rc != LDAP_SUCCESS )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_ops_init: "
+ "unable to register entry \"%s\" for monitoring\n",
+ e->e_name.bv_val );
+ ch_free( cb );
+ break;
+ }
+ }
+
+done:
+ entry_free( parent );
+
+ return rc;
+}
+
+/*
+ * call from within ldap_back_initialize()
+ */
+static int
+ldap_back_monitor_initialize( void )
+{
+ int i, code;
+ ConfigArgs c;
+ char *argv[ 3 ];
+
+ static int ldap_back_monitor_initialized = 0;
+
+ /* set to 0 when successfully initialized; otherwise, remember failure */
+ static int ldap_back_monitor_initialized_failure = 1;
+
+ /* register schema here */
+
+ if ( ldap_back_monitor_initialized++ ) {
+ return ldap_back_monitor_initialized_failure;
+ }
+
+ if ( backend_info( "monitor" ) == NULL ) {
+ return -1;
+ }
+
+ argv[ 0 ] = "back-ldap monitor";
+ c.argv = argv;
+ c.argc = 3;
+ c.fname = argv[0];
+ for ( i = 0; s_oid[ i ].name; i++ ) {
+
+ argv[ 1 ] = s_oid[ i ].name;
+ argv[ 2 ] = s_oid[ i ].oid;
+
+ if ( parse_oidm( &c, 0, NULL ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_initialize: unable to add "
+ "objectIdentifier \"%s=%s\"\n",
+ s_oid[ i ].name, s_oid[ i ].oid );
+ return 2;
+ }
+ }
+
+ for ( i = 0; s_at[ i ].desc != NULL; i++ ) {
+ code = register_at( s_at[ i ].desc, s_at[ i ].ad, 1 );
+ if ( code != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_initialize: register_at failed for attributeType (%s)\n",
+ s_at[ i ].desc );
+ return 3;
+
+ } else {
+ (*s_at[ i ].ad)->ad_type->sat_flags |= SLAP_AT_HIDE;
+ }
+ }
+
+ for ( i = 0; s_oc[ i ].desc != NULL; i++ ) {
+ code = register_oc( s_oc[ i ].desc, s_oc[ i ].oc, 1 );
+ if ( code != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_initialize: register_oc failed for objectClass (%s)\n",
+ s_oc[ i ].desc );
+ return 4;
+
+ } else {
+ (*s_oc[ i ].oc)->soc_flags |= SLAP_OC_HIDE;
+ }
+ }
+
+ for ( i = 0; s_moc[ i ].name != NULL; i++ ) {
+ *s_moc[i].oc = oc_find( s_moc[ i ].name );
+ if ( ! *s_moc[i].oc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_initialize: failed to find objectClass (%s)\n",
+ s_moc[ i ].name );
+ return 5;
+
+ }
+ }
+
+ return ( ldap_back_monitor_initialized_failure = LDAP_SUCCESS );
+}
+
+/*
+ * call from within ldap_back_db_init()
+ */
+int
+ldap_back_monitor_db_init( BackendDB *be )
+{
+ int rc;
+
+ rc = ldap_back_monitor_initialize();
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+#if 0 /* uncomment to turn monitoring on by default */
+ SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_MONITORING;
+#endif
+
+ return 0;
+}
+
+/*
+ * call from within ldap_back_db_open()
+ */
+int
+ldap_back_monitor_db_open( BackendDB *be )
+{
+ ldapinfo_t *li = (ldapinfo_t *) be->be_private;
+ monitor_subsys_t *mss = li->li_monitor_info.lmi_mss;
+ int rc = 0;
+ BackendInfo *mi;
+ monitor_extra_t *mbe;
+
+ if ( !SLAP_DBMONITORING( be ) ) {
+ return 0;
+ }
+
+ /* check if monitor is configured and usable */
+ mi = backend_info( "monitor" );
+ if ( !mi || !mi->bi_extra ) {
+ SLAP_DBFLAGS( be ) ^= SLAP_DBFLAG_MONITORING;
+ return 0;
+ }
+ mbe = mi->bi_extra;
+
+ /* don't bother if monitor is not configured */
+ if ( !mbe->is_configured() ) {
+ static int warning = 0;
+
+ if ( warning++ == 0 ) {
+ Debug( LDAP_DEBUG_CONFIG, "ldap_back_monitor_db_open: "
+ "monitoring disabled; "
+ "configure monitor database to enable\n" );
+ }
+
+ return 0;
+ }
+
+ /* caller (e.g. an overlay based on back-ldap) may want to use
+ * a different DN and RDNs... */
+ if ( BER_BVISNULL( &li->li_monitor_info.lmi_ndn ) ) {
+ rc = mbe->register_database( be, &li->li_monitor_info.lmi_ndn );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "ldap_back_monitor_db_open: "
+ "failed to register the database with back-monitor\n" );
+ }
+ }
+ if ( BER_BVISNULL( &li->li_monitor_info.lmi_conn_rdn ) ) {
+ ber_str2bv( "cn=Connections", 0, 1,
+ &li->li_monitor_info.lmi_conn_rdn );
+ }
+ if ( BER_BVISNULL( &li->li_monitor_info.lmi_ops_rdn ) ) {
+ ber_str2bv( "cn=Operations", 0, 1,
+ &li->li_monitor_info.lmi_ops_rdn );
+ }
+
+ /* set up the subsystems used to create the operation and
+ * volatile connection entries */
+
+ mss->mss_name = "back-ldap connections";
+ mss->mss_flags = MONITOR_F_VOLATILE_CH;
+ mss->mss_open = ldap_back_monitor_conn_init;
+ mss->mss_private = li;
+
+ if ( mbe->register_subsys_late( mss ) )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_db_open: "
+ "failed to register connection subsystem" );
+ return -1;
+ }
+
+ mss++;
+
+ mss->mss_name = "back-ldap operations";
+ mss->mss_flags = MONITOR_F_PERSISTENT_CH;
+ mss->mss_open = ldap_back_monitor_ops_init;
+ mss->mss_private = li;
+
+ if ( mbe->register_subsys_late( mss ) )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_back_monitor_db_open: "
+ "failed to register operation subsystem" );
+ return -1;
+ }
+
+ return rc;
+}
+
+/*
+ * call from within ldap_back_db_close()
+ */
+int
+ldap_back_monitor_db_close( BackendDB *be )
+{
+ ldapinfo_t *li = (ldapinfo_t *) be->be_private;
+
+ if ( li && !BER_BVISNULL( &li->li_monitor_info.lmi_ndn ) ) {
+ BackendInfo *mi;
+ monitor_extra_t *mbe;
+
+ /* check if monitor is configured and usable */
+ mi = backend_info( "monitor" );
+ if ( mi && mi->bi_extra ) {
+ mbe = mi->bi_extra;
+
+ /*TODO
+ * Unregister all entries our subsystems have created.
+ * Will only really be necessary when
+ * SLAPD_CONFIG_DELETE is enabled.
+ *
+ * Might need a way to unregister subsystems instead.
+ */
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * call from within ldap_back_db_destroy()
+ */
+int
+ldap_back_monitor_db_destroy( BackendDB *be )
+{
+ ldapinfo_t *li = (ldapinfo_t *) be->be_private;
+
+ if ( li ) {
+ memset( &li->li_monitor_info, 0, sizeof( li->li_monitor_info ) );
+ }
+
+ return 0;
+}
+
diff --git a/servers/slapd/back-ldap/pbind.c b/servers/slapd/back-ldap/pbind.c
new file mode 100644
index 0000000..f5841e9
--- /dev/null
+++ b/servers/slapd/back-ldap/pbind.c
@@ -0,0 +1,173 @@
+/* pbind.c - passthru Bind overlay */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2003-2010 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "lutil.h"
+#include "slap.h"
+#include "back-ldap.h"
+#include "slap-config.h"
+
+static BackendInfo *lback;
+
+static slap_overinst ldappbind;
+
+static int
+ldap_pbind_bind(
+ Operation *op,
+ SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ void *private = op->o_bd->be_private;
+ void *bi = op->o_bd->bd_info;
+ int rc;
+
+ op->o_bd->bd_info = lback;
+ op->o_bd->be_private = on->on_bi.bi_private;
+ rc = lback->bi_op_bind( op, rs );
+ op->o_bd->be_private = private;
+ op->o_bd->bd_info = bi;
+
+ return rc;
+}
+
+static int
+ldap_pbind_db_init(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ ConfigOCs *be_cf_ocs = be->be_cf_ocs;
+ void *private = be->be_private;
+ int rc;
+
+ if ( lback == NULL ) {
+ lback = backend_info( "ldap" );
+
+ if ( lback == NULL ) {
+ return 1;
+ }
+ }
+
+ rc = lback->bi_db_init( be, cr );
+ on->on_bi.bi_private = be->be_private;
+ be->be_cf_ocs = be_cf_ocs;
+ be->be_private = private;
+
+ return rc;
+}
+
+static int
+ldap_pbind_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ void *private = be->be_private;
+ int rc;
+ int monitoring;
+
+ be->be_private = on->on_bi.bi_private;
+ monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
+ SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
+ rc = lback->bi_db_open( be, cr );
+ SLAP_DBFLAGS( be ) |= monitoring;
+ be->be_private = private;
+
+ return rc;
+}
+
+static int
+ldap_pbind_db_close(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ void *private = be->be_private;
+ int rc;
+
+ be->be_private = on->on_bi.bi_private;
+ rc = lback->bi_db_close( be, cr );
+ be->be_private = private;
+
+ return rc;
+}
+
+static int
+ldap_pbind_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ void *private = be->be_private;
+ int rc;
+
+ be->be_private = on->on_bi.bi_private;
+ rc = lback->bi_db_close( be, cr );
+ on->on_bi.bi_private = be->be_private;
+ be->be_private = private;
+
+ return rc;
+}
+
+static int
+ldap_pbind_connection_destroy(
+ BackendDB *be,
+ Connection *conn
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ void *private = be->be_private;
+ int rc;
+
+ be->be_private = on->on_bi.bi_private;
+ rc = lback->bi_connection_destroy( be, conn );
+ be->be_private = private;
+
+ return rc;
+}
+
+int
+pbind_initialize( void )
+{
+ int rc;
+
+ ldappbind.on_bi.bi_type = "pbind";
+ ldappbind.on_bi.bi_db_init = ldap_pbind_db_init;
+ ldappbind.on_bi.bi_db_open = ldap_pbind_db_open;
+ ldappbind.on_bi.bi_db_close = ldap_pbind_db_close;
+ ldappbind.on_bi.bi_db_destroy = ldap_pbind_db_destroy;
+
+ ldappbind.on_bi.bi_op_bind = ldap_pbind_bind;
+ ldappbind.on_bi.bi_connection_destroy = ldap_pbind_connection_destroy;
+
+ rc = ldap_pbind_init_cf( &ldappbind.on_bi );
+ if ( rc ) {
+ return rc;
+ }
+
+ return overlay_register( &ldappbind );
+}
diff --git a/servers/slapd/back-ldap/proto-ldap.h b/servers/slapd/back-ldap/proto-ldap.h
new file mode 100644
index 0000000..445d551
--- /dev/null
+++ b/servers/slapd/back-ldap/proto-ldap.h
@@ -0,0 +1,124 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#ifndef PROTO_LDAP_H
+#define PROTO_LDAP_H
+
+LDAP_BEGIN_DECL
+
+extern BI_init ldap_back_initialize;
+extern BI_open ldap_back_open;
+
+extern BI_db_init ldap_back_db_init;
+extern BI_db_open ldap_back_db_open;
+extern BI_db_close ldap_back_db_close;
+extern BI_db_destroy ldap_back_db_destroy;
+
+extern BI_op_bind ldap_back_bind;
+extern BI_op_search ldap_back_search;
+extern BI_op_compare ldap_back_compare;
+extern BI_op_modify ldap_back_modify;
+extern BI_op_modrdn ldap_back_modrdn;
+extern BI_op_add ldap_back_add;
+extern BI_op_delete ldap_back_delete;
+extern BI_op_abandon ldap_back_abandon;
+extern BI_op_extended ldap_back_extended;
+
+extern BI_connection_destroy ldap_back_conn_destroy;
+
+extern BI_entry_get_rw ldap_back_entry_get;
+
+void ldap_back_release_conn_lock( ldapinfo_t *li, ldapconn_t **lcp, int dolock );
+#define ldap_back_release_conn(li, lc) ldap_back_release_conn_lock((li), &(lc), 1)
+int ldap_back_dobind( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok );
+int ldap_back_retry( ldapconn_t **lcp, Operation *op, SlapReply *rs, ldap_back_send_t sendok );
+int ldap_back_map_result( SlapReply *rs );
+int ldap_back_op_result( ldapconn_t *lc, Operation *op, SlapReply *rs,
+ ber_int_t msgid, time_t timeout, ldap_back_send_t sendok );
+int ldap_back_cancel( ldapconn_t *lc, Operation *op, SlapReply *rs, ber_int_t msgid, ldap_back_send_t sendok );
+
+int ldap_back_init_cf( BackendInfo *bi );
+int ldap_pbind_init_cf( BackendInfo *bi );
+
+extern int ldap_back_conndn_cmp( const void *c1, const void *c2);
+extern int ldap_back_conn_cmp( const void *c1, const void *c2);
+extern int ldap_back_conndn_dup( void *c1, void *c2 );
+extern void ldap_back_conn_free( void *c );
+
+extern ldapconn_t * ldap_back_conn_delete( ldapinfo_t *li, ldapconn_t *lc );
+
+extern int ldap_back_conn2str( const ldapconn_base_t *lc, char *buf, ber_len_t buflen );
+extern int ldap_back_connid2str( const ldapconn_base_t *lc, char *buf, ber_len_t buflen );
+
+extern int
+ldap_back_proxy_authz_ctrl(
+ Operation *op,
+ SlapReply *rs,
+ struct berval *bound_ndn,
+ int version,
+ slap_idassert_t *si,
+ LDAPControl *ctrl );
+
+extern int
+ldap_back_controls_add(
+ Operation *op,
+ SlapReply *rs,
+ ldapconn_t *lc,
+ LDAPControl ***pctrls );
+
+extern int
+ldap_back_controls_free( Operation *op, SlapReply *rs, LDAPControl ***pctrls );
+
+extern void
+ldap_back_quarantine(
+ Operation *op,
+ SlapReply *rs );
+
+#ifdef LDAP_BACK_PRINT_CONNTREE
+extern void
+ldap_back_print_conntree( ldapinfo_t *li, char *msg );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+
+extern void slap_retry_info_destroy( slap_retry_info_t *ri );
+extern int slap_retry_info_parse( char *in, slap_retry_info_t *ri,
+ char *buf, ber_len_t buflen );
+extern int slap_retry_info_unparse( slap_retry_info_t *ri, struct berval *bvout );
+
+extern int slap_idassert_authzfrom_parse( struct config_args_s *ca, slap_idassert_t *si );
+extern int slap_idassert_passthru_parse_cf( const char *fname, int lineno, const char *arg, slap_idassert_t *si );
+extern int slap_idassert_parse( struct config_args_s *ca, slap_idassert_t *si );
+
+extern int chain_initialize( void );
+extern int pbind_initialize( void );
+#ifdef SLAP_DISTPROC
+extern int distproc_initialize( void );
+#endif
+
+extern int ldap_back_monitor_db_init( BackendDB *be );
+extern int ldap_back_monitor_db_open( BackendDB *be );
+extern int ldap_back_monitor_db_close( BackendDB *be );
+extern int ldap_back_monitor_db_destroy( BackendDB *be );
+
+extern LDAP_REBIND_PROC ldap_back_default_rebind;
+extern LDAP_URLLIST_PROC ldap_back_default_urllist;
+
+LDAP_END_DECL
+
+#endif /* PROTO_LDAP_H */
diff --git a/servers/slapd/back-ldap/search.c b/servers/slapd/back-ldap/search.c
new file mode 100644
index 0000000..90b5b65
--- /dev/null
+++ b/servers/slapd/back-ldap/search.c
@@ -0,0 +1,1042 @@
+/* search.c - ldap backend search function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "slap.h"
+#include "back-ldap.h"
+#include "../../../libraries/liblber/lber-int.h"
+
+#include "lutil.h"
+
+static int
+ldap_build_entry( Operation *op, LDAPMessage *e, Entry *ent,
+ struct berval *bdn, int remove_unknown_schema );
+
+
+static ObjectClass *
+oc_bvfind_undef_ex( struct berval *ocname, int flag )
+{
+ ObjectClass *oc = oc_bvfind( ocname );
+
+ if ( oc || flag ) {
+ /* oc defined or remove-unknown-schema flag set */
+ return oc;
+ }
+
+ return oc_bvfind_undef( ocname );
+}
+
+
+/*
+ * replaces (&) with (objectClass=*) and (|) with (!(objectClass=*))
+ * as the best replacement for RFC 4526 absolute true/absolute false
+ * filters; the only difference (AFAIK) is that they require search
+ * access to objectClass.
+ *
+ * filter->bv_val may be alloc'd on the thread's slab, if equal to
+ * op->ors_filterstr.bv_val, or realloc'd on the thread's slab otherwise.
+ */
+static int
+ldap_back_munge_filter(
+ Operation *op,
+ struct berval *filter )
+{
+ char *ptr;
+ int gotit = 0;
+
+ Debug( LDAP_DEBUG_ARGS, "=> ldap_back_munge_filter \"%s\"\n",
+ filter->bv_val );
+
+ for ( ptr = strchr( filter->bv_val, '(' );
+ ptr;
+ ptr = strchr( ptr, '(' ) )
+ {
+ static struct berval
+ bv_t = BER_BVC( "(&)" ),
+ bv_f = BER_BVC( "(|)" ),
+ bv_T = BER_BVC( "(objectClass=*)" ),
+ bv_F = BER_BVC( "(!(objectClass=*))" );
+ struct berval *oldbv = NULL,
+ *newbv = NULL,
+ oldfilter = BER_BVNULL;
+
+ if ( ptr[2] != ')' ) {
+ ptr++;
+ continue;
+ }
+
+ switch ( ptr[1] ) {
+ case '&':
+ oldbv = &bv_t;
+ newbv = &bv_T;
+ break;
+
+ case '|':
+ oldbv = &bv_f;
+ newbv = &bv_F;
+ break;
+
+ default:
+ /* should be an error */
+ continue;
+ }
+
+ oldfilter = *filter;
+ filter->bv_len += newbv->bv_len - oldbv->bv_len;
+ if ( filter->bv_val == op->ors_filterstr.bv_val ) {
+ filter->bv_val = op->o_tmpalloc( filter->bv_len + 1,
+ op->o_tmpmemctx );
+
+ AC_MEMCPY( filter->bv_val, op->ors_filterstr.bv_val,
+ ptr - oldfilter.bv_val );
+
+ } else {
+ filter->bv_val = op->o_tmprealloc( filter->bv_val,
+ filter->bv_len + 1, op->o_tmpmemctx );
+ }
+
+ ptr = filter->bv_val + ( ptr - oldfilter.bv_val );
+
+ AC_MEMCPY( &ptr[ newbv->bv_len ],
+ &ptr[ oldbv->bv_len ],
+ oldfilter.bv_len - ( ptr - filter->bv_val ) - oldbv->bv_len + 1 );
+ AC_MEMCPY( ptr, newbv->bv_val, newbv->bv_len );
+
+ ptr += newbv->bv_len;
+
+ gotit++;
+ }
+
+ Debug( LDAP_DEBUG_ARGS, "<= ldap_back_munge_filter \"%s\" (%d)\n",
+ filter->bv_val, gotit );
+
+ return gotit;
+}
+
+int
+ldap_back_search(
+ Operation *op,
+ SlapReply *rs )
+{
+ ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private;
+
+ ldapconn_t *lc = NULL;
+ struct timeval tv;
+ time_t stoptime = (time_t)(-1);
+ LDAPMessage *res,
+ *e;
+ int rc = 0,
+ msgid;
+ struct berval match = BER_BVNULL,
+ filter = BER_BVNULL;
+ int i, x;
+ char **attrs = NULL;
+ int freetext = 0, filter_undef = 0;
+ int do_retry = 1, dont_retry = 0;
+ LDAPControl **ctrls = NULL;
+ char **references = NULL;
+ int remove_unknown_schema =
+ LDAP_BACK_OMIT_UNKNOWN_SCHEMA (li);
+
+ rs_assert_ready( rs );
+ rs->sr_flags &= ~REP_ENTRY_MASK; /* paranoia, we can set rs = non-entry */
+
+ if ( !ldap_back_dobind( &lc, op, rs, LDAP_BACK_SENDERR ) ) {
+ return rs->sr_err;
+ }
+
+ /*
+ * FIXME: in case of values return filter, we might want
+ * to map attrs and maybe rewrite value
+ */
+
+ if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
+ tv.tv_sec = op->ors_tlimit;
+ tv.tv_usec = 0;
+ stoptime = op->o_time + op->ors_tlimit;
+
+ } else {
+ LDAP_BACK_TV_SET( &tv );
+ }
+
+ i = 0;
+ if ( op->ors_attrs ) {
+ for ( ; !BER_BVISNULL( &op->ors_attrs[i].an_name ); i++ )
+ /* just count attrs */ ;
+ }
+
+ x = 0;
+ if ( op->o_bd->be_extra_anlist ) {
+ for ( ; !BER_BVISNULL( &op->o_bd->be_extra_anlist[x].an_name ); x++ )
+ /* just count attrs */ ;
+ }
+
+ if ( i > 0 || x > 0 ) {
+ int j = 0;
+
+ attrs = op->o_tmpalloc( ( i + x + 1 )*sizeof( char * ),
+ op->o_tmpmemctx );
+ if ( attrs == NULL ) {
+ rs->sr_err = LDAP_NO_MEMORY;
+ rc = -1;
+ goto finish;
+ }
+
+ if ( i > 0 ) {
+ for ( i = 0; !BER_BVISNULL( &op->ors_attrs[i].an_name ); i++, j++ ) {
+ attrs[ j ] = op->ors_attrs[i].an_name.bv_val;
+ }
+ }
+
+ if ( x > 0 ) {
+ for ( x = 0; !BER_BVISNULL( &op->o_bd->be_extra_anlist[x].an_name ); x++, j++ ) {
+ if ( op->o_bd->be_extra_anlist[x].an_desc &&
+ ad_inlist( op->o_bd->be_extra_anlist[x].an_desc, op->ors_attrs ) )
+ {
+ continue;
+ }
+
+ attrs[ j ] = op->o_bd->be_extra_anlist[x].an_name.bv_val;
+ }
+ }
+
+ attrs[ j ] = NULL;
+ }
+
+ ctrls = op->o_ctrls;
+ rc = ldap_back_controls_add( op, rs, lc, &ctrls );
+ if ( rc != LDAP_SUCCESS ) {
+ goto finish;
+ }
+
+ /* deal with <draft-zeilenga-ldap-t-f> filters */
+ filter = op->ors_filterstr;
+retry:
+ /* this goes after retry because ldap_back_munge_filter()
+ * optionally replaces RFC 4526 T-F filters (&) (|)
+ * if already computed, they will be re-installed
+ * by filter2bv_undef_x() later */
+ if ( !LDAP_BACK_T_F( li ) ) {
+ ldap_back_munge_filter( op, &filter );
+ }
+
+ rs->sr_err = ldap_pvt_search( lc->lc_ld, op->o_req_dn.bv_val,
+ op->ors_scope, filter.bv_val,
+ attrs, op->ors_attrsonly, ctrls, NULL,
+ tv.tv_sec ? &tv : NULL,
+ op->ors_slimit, op->ors_deref, &msgid );
+
+ ldap_pvt_thread_mutex_lock( &li->li_counter_mutex );
+ ldap_pvt_mp_add( li->li_ops_completed[ SLAP_OP_SEARCH ], 1 );
+ ldap_pvt_thread_mutex_unlock( &li->li_counter_mutex );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ switch ( rs->sr_err ) {
+ case LDAP_SERVER_DOWN:
+ if ( do_retry ) {
+ do_retry = 0;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_DONTSEND ) ) {
+ goto retry;
+ }
+ }
+
+ if ( lc == NULL ) {
+ /* reset by ldap_back_retry ... */
+ rs->sr_err = slap_map_api2result( rs );
+
+ } else {
+ rc = ldap_back_op_result( lc, op, rs, msgid, 0, LDAP_BACK_DONTSEND );
+ }
+
+ goto finish;
+
+ case LDAP_FILTER_ERROR:
+ /* first try? */
+ if ( !filter_undef &&
+ strstr( filter.bv_val, "(?" ) &&
+ !LDAP_BACK_NOUNDEFFILTER( li ) )
+ {
+ BER_BVZERO( &filter );
+ filter2bv_undef_x( op, op->ors_filter, 1, &filter );
+ filter_undef = 1;
+ goto retry;
+ }
+
+ /* invalid filters return success with no data */
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ goto finish;
+
+ default:
+ rs->sr_err = slap_map_api2result( rs );
+ rs->sr_text = NULL;
+ goto finish;
+ }
+ }
+
+ /* if needed, initialize timeout */
+ if ( li->li_timeout[ SLAP_OP_SEARCH ] ) {
+ if ( tv.tv_sec == 0 || tv.tv_sec > li->li_timeout[ SLAP_OP_SEARCH ] ) {
+ tv.tv_sec = li->li_timeout[ SLAP_OP_SEARCH ];
+ tv.tv_usec = 0;
+ }
+ }
+
+ /* We pull apart the ber result, stuff it into a slapd entry, and
+ * let send_search_entry stuff it back into ber format. Slow & ugly,
+ * but this is necessary for version matching, and for ACL processing.
+ */
+
+ for ( rc = -2; rc != -1; rc = ldap_result( lc->lc_ld, msgid, LDAP_MSG_ONE, &tv, &res ) )
+ {
+ /* check for abandon */
+ if ( op->o_abandon || LDAP_BACK_CONN_ABANDON( lc ) ) {
+ if ( rc > 0 ) {
+ ldap_msgfree( res );
+ }
+ (void)ldap_back_cancel( lc, op, rs, msgid, LDAP_BACK_DONTSEND );
+ rc = SLAPD_ABANDON;
+ goto finish;
+ }
+
+ if ( rc == 0 || rc == -2 ) {
+ ldap_pvt_thread_yield();
+
+ /* check timeout */
+ if ( li->li_timeout[ SLAP_OP_SEARCH ] ) {
+ if ( rc == 0 ) {
+ (void)ldap_back_cancel( lc, op, rs, msgid, LDAP_BACK_DONTSEND );
+ rs->sr_text = "Operation timed out";
+ rc = rs->sr_err = op->o_protocol >= LDAP_VERSION3 ?
+ LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+ goto finish;
+ }
+
+ } else {
+ LDAP_BACK_TV_SET( &tv );
+ }
+
+ /* check time limit */
+ if ( op->ors_tlimit != SLAP_NO_LIMIT
+ && slap_get_time() > stoptime )
+ {
+ (void)ldap_back_cancel( lc, op, rs, msgid, LDAP_BACK_DONTSEND );
+ rc = rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
+ goto finish;
+ }
+ continue;
+
+ } else {
+ /* only touch when activity actually took place... */
+ if ( li->li_idle_timeout ) {
+ lc->lc_time = op->o_time;
+ }
+
+ /* don't retry any more */
+ dont_retry = 1;
+ }
+
+
+ if ( rc == LDAP_RES_SEARCH_ENTRY ) {
+ Entry ent = { 0 };
+ struct berval bdn = BER_BVNULL;
+
+ do_retry = 0;
+
+ e = ldap_first_entry( lc->lc_ld, res );
+ rc = ldap_build_entry( op, e, &ent, &bdn,
+ remove_unknown_schema);
+ if ( rc == LDAP_SUCCESS ) {
+ ldap_get_entry_controls( lc->lc_ld, res, &rs->sr_ctrls );
+ rs->sr_entry = &ent;
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_operational_attrs = NULL;
+ rs->sr_flags = 0;
+ rs->sr_err = LDAP_SUCCESS;
+ rc = rs->sr_err = send_search_entry( op, rs );
+ if ( rs->sr_ctrls ) {
+ ldap_controls_free( rs->sr_ctrls );
+ rs->sr_ctrls = NULL;
+ }
+ rs->sr_entry = NULL;
+ rs->sr_flags = 0;
+ if ( !BER_BVISNULL( &ent.e_name ) ) {
+ assert( ent.e_name.bv_val != bdn.bv_val );
+ op->o_tmpfree( ent.e_name.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &ent.e_name );
+ }
+ if ( !BER_BVISNULL( &ent.e_nname ) ) {
+ op->o_tmpfree( ent.e_nname.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &ent.e_nname );
+ }
+ entry_clean( &ent );
+ }
+ ldap_msgfree( res );
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ case LDAP_INSUFFICIENT_ACCESS:
+ break;
+
+ default:
+ if ( rc == LDAP_UNAVAILABLE ) {
+ rc = rs->sr_err = LDAP_OTHER;
+ } else {
+ (void)ldap_back_cancel( lc, op, rs, msgid, LDAP_BACK_DONTSEND );
+ }
+ goto finish;
+ }
+
+ } else if ( rc == LDAP_RES_SEARCH_REFERENCE ) {
+ if ( LDAP_BACK_NOREFS( li ) ) {
+ ldap_msgfree( res );
+ continue;
+ }
+
+ do_retry = 0;
+ rc = ldap_parse_reference( lc->lc_ld, res,
+ &references, &rs->sr_ctrls, 1 );
+
+ if ( rc != LDAP_SUCCESS ) {
+ continue;
+ }
+
+ /* FIXME: there MUST be at least one */
+ if ( references && references[ 0 ] && references[ 0 ][ 0 ] ) {
+ int cnt;
+
+ for ( cnt = 0; references[ cnt ]; cnt++ )
+ /* NO OP */ ;
+
+ /* FIXME: there MUST be at least one */
+ rs->sr_ref = op->o_tmpalloc( ( cnt + 1 ) * sizeof( struct berval ),
+ op->o_tmpmemctx );
+
+ for ( cnt = 0; references[ cnt ]; cnt++ ) {
+ ber_str2bv( references[ cnt ], 0, 0, &rs->sr_ref[ cnt ] );
+ }
+ BER_BVZERO( &rs->sr_ref[ cnt ] );
+
+ /* ignore return value by now */
+ RS_ASSERT( !(rs->sr_flags & REP_ENTRY_MASK) );
+ rs->sr_entry = NULL;
+ ( void )send_search_reference( op, rs );
+
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "%s ldap_back_search: "
+ "got SEARCH_REFERENCE "
+ "with no referrals\n",
+ op->o_log_prefix );
+ }
+
+ /* cleanup */
+ if ( references ) {
+ ber_memvfree( (void **)references );
+ op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
+ rs->sr_ref = NULL;
+ references = NULL;
+ }
+
+ if ( rs->sr_ctrls ) {
+ ldap_controls_free( rs->sr_ctrls );
+ rs->sr_ctrls = NULL;
+ }
+
+ } else if ( rc == LDAP_RES_INTERMEDIATE ) {
+ /* FIXME: response controls
+ * are passed without checks */
+ rc = ldap_parse_intermediate( lc->lc_ld,
+ res,
+ (char **)&rs->sr_rspoid,
+ &rs->sr_rspdata,
+ &rs->sr_ctrls,
+ 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ continue;
+ }
+
+ slap_send_ldap_intermediate( op, rs );
+
+ if ( rs->sr_rspoid != NULL ) {
+ ber_memfree( (char *)rs->sr_rspoid );
+ rs->sr_rspoid = NULL;
+ }
+
+ if ( rs->sr_rspdata != NULL ) {
+ ber_bvfree( rs->sr_rspdata );
+ rs->sr_rspdata = NULL;
+ }
+
+ if ( rs->sr_ctrls != NULL ) {
+ ldap_controls_free( rs->sr_ctrls );
+ rs->sr_ctrls = NULL;
+ }
+
+ } else {
+ char *err = NULL;
+
+ rc = ldap_parse_result( lc->lc_ld, res, &rs->sr_err,
+ &match.bv_val, &err,
+ &references, &rs->sr_ctrls, 1 );
+ if ( rc == LDAP_SUCCESS ) {
+ if ( err ) {
+ rs->sr_text = err;
+ freetext = 1;
+ }
+ } else {
+ rs->sr_err = rc;
+ }
+ rs->sr_err = slap_map_api2result( rs );
+
+ /* RFC 4511: referrals can only appear
+ * if result code is LDAP_REFERRAL */
+ if ( references
+ && references[ 0 ]
+ && references[ 0 ][ 0 ] )
+ {
+ if ( rs->sr_err != LDAP_REFERRAL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s ldap_back_search: "
+ "got referrals with err=%d\n",
+ op->o_log_prefix,
+ rs->sr_err );
+
+ } else {
+ int cnt;
+
+ for ( cnt = 0; references[ cnt ]; cnt++ )
+ /* NO OP */ ;
+
+ rs->sr_ref = op->o_tmpalloc( ( cnt + 1 ) * sizeof( struct berval ),
+ op->o_tmpmemctx );
+
+ for ( cnt = 0; references[ cnt ]; cnt++ ) {
+ /* duplicating ...*/
+ ber_str2bv( references[ cnt ], 0, 0, &rs->sr_ref[ cnt ] );
+ }
+ BER_BVZERO( &rs->sr_ref[ cnt ] );
+ }
+
+ } else if ( rs->sr_err == LDAP_REFERRAL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s ldap_back_search: "
+ "got err=%d with null "
+ "or empty referrals\n",
+ op->o_log_prefix,
+ rs->sr_err );
+
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ }
+
+ if ( match.bv_val != NULL ) {
+ match.bv_len = strlen( match.bv_val );
+ }
+
+ rc = 0;
+ break;
+ }
+
+ /* if needed, restore timeout */
+ if ( li->li_timeout[ SLAP_OP_SEARCH ] ) {
+ if ( tv.tv_sec == 0 || tv.tv_sec > li->li_timeout[ SLAP_OP_SEARCH ] ) {
+ tv.tv_sec = li->li_timeout[ SLAP_OP_SEARCH ];
+ tv.tv_usec = 0;
+ }
+ }
+ }
+
+ if ( rc == -1 ) {
+ if ( dont_retry == 0 ) {
+ if ( do_retry ) {
+ do_retry = 0;
+ if ( ldap_back_retry( &lc, op, rs, LDAP_BACK_DONTSEND ) ) {
+ goto retry;
+ }
+ }
+
+ rs->sr_err = LDAP_SERVER_DOWN;
+ rs->sr_err = slap_map_api2result( rs );
+ goto finish;
+
+ } else if ( LDAP_BACK_ONERR_STOP( li ) ) {
+ /* if onerr == STOP */
+ rs->sr_err = LDAP_SERVER_DOWN;
+ rs->sr_err = slap_map_api2result( rs );
+ goto finish;
+ }
+ }
+
+ /*
+ * Rewrite the matched portion of the search base, if required
+ */
+ if ( !BER_BVISNULL( &match ) && !BER_BVISEMPTY( &match ) ) {
+ struct berval pmatch;
+
+ if ( dnPretty( NULL, &match, &pmatch, op->o_tmpmemctx ) != LDAP_SUCCESS ) {
+ pmatch.bv_val = match.bv_val;
+ match.bv_val = NULL;
+ }
+ rs->sr_matched = pmatch.bv_val;
+ rs->sr_flags |= REP_MATCHED_MUSTBEFREED;
+ }
+
+finish:;
+ if ( !BER_BVISNULL( &match ) ) {
+ ber_memfree( match.bv_val );
+ }
+
+ if ( rs->sr_v2ref ) {
+ rs->sr_err = LDAP_REFERRAL;
+ }
+
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs );
+ }
+
+ if ( filter.bv_val != op->ors_filterstr.bv_val ) {
+ op->o_tmpfree( filter.bv_val, op->o_tmpmemctx );
+ }
+
+#if 0
+ /* let send_ldap_result play cleanup handlers (ITS#4645) */
+ if ( rc != SLAPD_ABANDON )
+#endif
+ {
+ send_ldap_result( op, rs );
+ }
+
+ (void)ldap_back_controls_free( op, rs, &ctrls );
+
+ if ( rs->sr_ctrls ) {
+ ldap_controls_free( rs->sr_ctrls );
+ rs->sr_ctrls = NULL;
+ }
+
+ if ( rs->sr_text ) {
+ if ( freetext ) {
+ ber_memfree( (char *)rs->sr_text );
+ }
+ rs->sr_text = NULL;
+ }
+
+ if ( rs->sr_ref ) {
+ op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
+ rs->sr_ref = NULL;
+ }
+
+ if ( references ) {
+ ber_memvfree( (void **)references );
+ }
+
+ if ( attrs ) {
+ op->o_tmpfree( attrs, op->o_tmpmemctx );
+ }
+
+ if ( lc != NULL ) {
+ ldap_back_release_conn( li, lc );
+ }
+
+ if ( rs->sr_err == LDAP_UNAVAILABLE &&
+ /* if we originally bound and wanted rebind-as-user, must drop
+ * the connection now because we just discarded the credentials.
+ * ITS#7464, #8142
+ */
+ LDAP_BACK_SAVECRED( li ) && SLAP_IS_AUTHZ_BACKEND( op ) )
+ rs->sr_err = SLAPD_DISCONNECT;
+ return rs->sr_err;
+}
+
+static int
+ldap_build_entry(
+ Operation *op,
+ LDAPMessage *e,
+ Entry *ent,
+ struct berval *bdn,
+ int remove_unknown_schema)
+{
+ struct berval a;
+ BerElement ber = *ldap_get_message_ber( e );
+ Attribute *attr, **attrp;
+ const char *text;
+ int last;
+ char *lastb;
+ ber_len_t len;
+
+ /* safe assumptions ... */
+ assert( ent != NULL );
+ BER_BVZERO( &ent->e_bv );
+
+ if ( ber_scanf( &ber, "{m", bdn ) == LBER_ERROR ) {
+ return LDAP_DECODING_ERROR;
+ }
+
+ /*
+ * Note: this may fail if the target host(s) schema differs
+ * from the one known to the meta, and a DN with unknown
+ * attributes is returned.
+ *
+ * FIXME: should we log anything, or delegate to dnNormalize?
+ */
+ /* Note: if the distinguished values or the naming attributes
+ * change, should we massage them as well?
+ */
+ if ( dnPrettyNormal( NULL, bdn, &ent->e_name, &ent->e_nname,
+ op->o_tmpmemctx ) != LDAP_SUCCESS )
+ {
+ return LDAP_INVALID_DN_SYNTAX;
+ }
+
+ ent->e_attrs = NULL;
+ if ( ber_first_element( &ber, &len, &lastb ) != LBER_SEQUENCE ) {
+ return LDAP_SUCCESS;
+ }
+
+ attrp = &ent->e_attrs;
+ while ( ber_next_element( &ber, &len, lastb ) == LBER_SEQUENCE &&
+ ber_scanf( &ber, "{m", &a ) != LBER_ERROR ) {
+ int i;
+ slap_syntax_validate_func *validate;
+ slap_syntax_transform_func *pretty;
+
+ attr = attr_alloc( NULL );
+ if ( attr == NULL ) {
+ return LDAP_OTHER;
+ }
+ if ( slap_bv2ad( &a, &attr->a_desc, &text )
+ != LDAP_SUCCESS )
+ {
+ if ( slap_bv2undef_ad( &a, &attr->a_desc, &text,
+ (remove_unknown_schema ? SLAP_AD_NOINSERT : SLAP_AD_PROXIED )) != LDAP_SUCCESS )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "%s ldap_build_entry: "
+ "slap_bv2undef_ad(%s): %s\n",
+ op->o_log_prefix, a.bv_val, text );
+
+ ( void )ber_scanf( &ber, "x" /* [W] */ );
+ attr_free( attr );
+ continue;
+ }
+ }
+
+ /* no subschemaSubentry */
+ if ( attr->a_desc == slap_schema.si_ad_subschemaSubentry
+ || attr->a_desc == slap_schema.si_ad_entryDN )
+ {
+
+ /*
+ * We eat target's subschemaSubentry because
+ * a search for this value is likely not
+ * to resolve to the appropriate backend;
+ * later, the local subschemaSubentry is
+ * added.
+ *
+ * We also eat entryDN because the frontend
+ * will reattach it without checking if already
+ * present...
+ */
+ ( void )ber_scanf( &ber, "x" /* [W] */ );
+ attr_free( attr );
+ continue;
+ }
+
+ if ( ber_scanf( &ber, "[W]", &attr->a_vals ) == LBER_ERROR
+ || attr->a_vals == NULL )
+ {
+ /*
+ * Note: attr->a_vals can be null when using
+ * values result filter
+ */
+ attr->a_vals = (struct berval *)&slap_dummy_bv;
+ }
+
+ validate = attr->a_desc->ad_type->sat_syntax->ssyn_validate;
+ pretty = attr->a_desc->ad_type->sat_syntax->ssyn_pretty;
+
+ if ( !validate && !pretty ) {
+ attr->a_nvals = NULL;
+ attr_free( attr );
+ goto next_attr;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &attr->a_vals[i] ); i++ ) ;
+ last = i;
+
+ /*
+ * check that each value is valid per syntax
+ * and pretty if appropriate
+ */
+ for ( i = 0; i<last; i++ ) {
+ struct berval pval;
+ int rc;
+
+ if ( pretty ) {
+ rc = ordered_value_pretty( attr->a_desc,
+ &attr->a_vals[i], &pval, NULL );
+
+ } else {
+ rc = ordered_value_validate( attr->a_desc,
+ &attr->a_vals[i], 0 );
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ ObjectClass *oc;
+
+ /* check if, by chance, it's an undefined objectClass */
+ if ( attr->a_desc == slap_schema.si_ad_objectClass &&
+ ( oc = oc_bvfind_undef_ex( &attr->a_vals[i],
+ remove_unknown_schema ) ) != NULL )
+ {
+ ber_dupbv( &pval, &oc->soc_cname );
+ rc = LDAP_SUCCESS;
+
+ } else {
+ ber_memfree( attr->a_vals[i].bv_val );
+ if ( --last == i ) {
+ BER_BVZERO( &attr->a_vals[i] );
+ break;
+ }
+ attr->a_vals[i] = attr->a_vals[last];
+ BER_BVZERO( &attr->a_vals[last] );
+ i--;
+ }
+ }
+
+ if ( rc == LDAP_SUCCESS && pretty ) {
+ ber_memfree( attr->a_vals[i].bv_val );
+ attr->a_vals[i] = pval;
+ }
+ }
+ attr->a_numvals = last = i;
+ if ( last == 0 && attr->a_vals != &slap_dummy_bv ) {
+ attr->a_nvals = NULL;
+ attr_free( attr );
+ goto next_attr;
+ }
+
+ if ( last && attr->a_desc->ad_type->sat_equality &&
+ attr->a_desc->ad_type->sat_equality->smr_normalize )
+ {
+ attr->a_nvals = ch_malloc( ( last + 1 )*sizeof( struct berval ) );
+ for ( i = 0; i < last; i++ ) {
+ int rc;
+
+ rc = ordered_value_normalize(
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ attr->a_desc,
+ attr->a_desc->ad_type->sat_equality,
+ &attr->a_vals[i], &attr->a_nvals[i],
+ NULL );
+
+ if ( rc != LDAP_SUCCESS ) {
+ ber_memfree( attr->a_vals[i].bv_val );
+ if ( --last == i ) {
+ BER_BVZERO( &attr->a_vals[i] );
+ break;
+ }
+ attr->a_vals[i] = attr->a_vals[last];
+ BER_BVZERO( &attr->a_vals[last] );
+ i--;
+ }
+ }
+ BER_BVZERO( &attr->a_nvals[i] );
+ if ( last == 0 ) {
+ attr_free( attr );
+ goto next_attr;
+ }
+
+ } else {
+ attr->a_nvals = attr->a_vals;
+ }
+
+ attr->a_numvals = last;
+
+ /* Handle sorted vals, strip dups but keep the attr */
+ if ( attr->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL ) {
+ while ( attr->a_numvals > 1 ) {
+ int rc = slap_sort_vals( (Modifications *)attr, &text, &i, op->o_tmpmemctx );
+ if ( rc != LDAP_TYPE_OR_VALUE_EXISTS )
+ break;
+
+ /* Strip duplicate values */
+ if ( attr->a_nvals != attr->a_vals )
+ ber_memfree( attr->a_nvals[i].bv_val );
+ ber_memfree( attr->a_vals[i].bv_val );
+ attr->a_numvals--;
+
+ assert( i >= 0 );
+ if ( (unsigned)i < attr->a_numvals ) {
+ attr->a_vals[i] = attr->a_vals[attr->a_numvals];
+ if ( attr->a_nvals != attr->a_vals )
+ attr->a_nvals[i] = attr->a_nvals[attr->a_numvals];
+ }
+ BER_BVZERO(&attr->a_vals[attr->a_numvals]);
+ if ( attr->a_nvals != attr->a_vals )
+ BER_BVZERO(&attr->a_nvals[attr->a_numvals]);
+ }
+ attr->a_flags |= SLAP_ATTR_SORTED_VALS;
+ }
+
+ *attrp = attr;
+ attrp = &attr->a_next;
+
+next_attr:;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/* return 0 IFF we can retrieve the entry with ndn
+ */
+int
+ldap_back_entry_get(
+ Operation *op,
+ struct berval *ndn,
+ ObjectClass *oc,
+ AttributeDescription *at,
+ int rw,
+ Entry **ent )
+{
+ ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private;
+
+ ldapconn_t *lc = NULL;
+ int rc;
+ struct berval bdn;
+ LDAPMessage *result = NULL,
+ *e = NULL;
+ char *attr[3], **attrp = NULL;
+ char *filter = NULL;
+ SlapReply rs;
+ int do_retry = 1;
+ LDAPControl **ctrls = NULL;
+ Operation op2 = *op;
+
+ int remove_unknown_schema =
+ LDAP_BACK_OMIT_UNKNOWN_SCHEMA (li);
+ *ent = NULL;
+
+ /* Tell getconn this is a privileged op */
+ op2.o_do_not_cache = 1;
+ /* use rootdn to be doubly explicit this is privileged */
+ op2.o_dn = op->o_bd->be_rootdn;
+ op2.o_ndn = op->o_bd->be_rootndn;
+ /* ldap_back_entry_get() is an entry lookup, so it does not need
+ * to know what the entry is being looked up for */
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2.o_ctrls = NULL;
+ rc = ldap_back_dobind( &lc, &op2, &rs, LDAP_BACK_DONTSEND );
+ if ( !rc ) {
+ return rs.sr_err;
+ }
+
+ if ( at ) {
+ attrp = attr;
+ if ( oc && at != slap_schema.si_ad_objectClass ) {
+ attr[0] = slap_schema.si_ad_objectClass->ad_cname.bv_val;
+ attr[1] = at->ad_cname.bv_val;
+ attr[2] = NULL;
+
+ } else {
+ attr[0] = at->ad_cname.bv_val;
+ attr[1] = NULL;
+ }
+ }
+
+ if ( oc ) {
+ char *ptr;
+
+ filter = op->o_tmpalloc( STRLENOF( "(objectClass=" ")" )
+ + oc->soc_cname.bv_len + 1, op->o_tmpmemctx );
+ ptr = lutil_strcopy( filter, "(objectClass=" );
+ ptr = lutil_strcopy( ptr, oc->soc_cname.bv_val );
+ *ptr++ = ')';
+ *ptr++ = '\0';
+ }
+
+retry:
+ ctrls = NULL;
+ rc = ldap_back_controls_add( &op2, &rs, lc, &ctrls );
+ if ( rc != LDAP_SUCCESS ) {
+ goto cleanup;
+ }
+
+ /* TODO: timeout? */
+ rc = ldap_pvt_search_s( lc->lc_ld, ndn->bv_val, LDAP_SCOPE_BASE, filter,
+ attrp, LDAP_DEREF_NEVER, ctrls, NULL,
+ NULL, LDAP_NO_LIMIT, 0, &result );
+ if ( rc != LDAP_SUCCESS ) {
+ if ( rc == LDAP_SERVER_DOWN && do_retry ) {
+ do_retry = 0;
+ if ( ldap_back_retry( &lc, &op2, &rs, LDAP_BACK_DONTSEND ) ) {
+ /* if the identity changed, there might be need to re-authz */
+ (void)ldap_back_controls_free( &op2, &rs, &ctrls );
+ goto retry;
+ }
+ }
+ goto cleanup;
+ }
+
+ e = ldap_first_entry( lc->lc_ld, result );
+ if ( e == NULL ) {
+ /* the entry exists, but it doesn't match the filter? */
+ rc = LDAP_NO_RESULTS_RETURNED;
+ goto cleanup;
+ }
+
+ *ent = entry_alloc();
+ if ( *ent == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto cleanup;
+ }
+
+ rc = ldap_build_entry( op, e, *ent, &bdn, remove_unknown_schema );
+
+ if ( rc != LDAP_SUCCESS ) {
+ entry_free( *ent );
+ *ent = NULL;
+ }
+
+cleanup:
+ (void)ldap_back_controls_free( &op2, &rs, &ctrls );
+
+ if ( result ) {
+ ldap_msgfree( result );
+ }
+
+ if ( filter ) {
+ op->o_tmpfree( filter, op->o_tmpmemctx );
+ }
+
+ if ( lc != NULL ) {
+ ldap_back_release_conn( li, lc );
+ }
+
+ return rc;
+}
diff --git a/servers/slapd/back-ldap/unbind.c b/servers/slapd/back-ldap/unbind.c
new file mode 100644
index 0000000..071380e
--- /dev/null
+++ b/servers/slapd/back-ldap/unbind.c
@@ -0,0 +1,78 @@
+/* unbind.c - ldap backend unbind function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-ldap.h"
+
+int
+ldap_back_conn_destroy(
+ Backend *be,
+ Connection *conn
+)
+{
+ ldapinfo_t *li = (ldapinfo_t *) be->be_private;
+ ldapconn_t *lc = NULL, lc_curr;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=>ldap_back_conn_destroy: fetching conn %ld\n",
+ conn->c_connid );
+
+ lc_curr.lc_conn = conn;
+
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, ">>> ldap_back_conn_destroy" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+ while ( ( lc = ldap_tavl_delete( &li->li_conninfo.lai_tree, (caddr_t)&lc_curr, ldap_back_conn_cmp ) ) != NULL )
+ {
+ assert( !LDAP_BACK_PCONN_ISPRIV( lc ) );
+ Debug( LDAP_DEBUG_TRACE,
+ "=>ldap_back_conn_destroy: destroying conn %lu "
+ "refcnt=%d flags=0x%08x\n",
+ lc->lc_conn->c_connid, lc->lc_refcnt, lc->lc_lcflags );
+
+ if ( lc->lc_refcnt > 0 ) {
+ /* someone else might be accessing the connection;
+ * mark for deletion */
+ LDAP_BACK_CONN_CACHED_CLEAR( lc );
+ LDAP_BACK_CONN_TAINTED_SET( lc );
+
+ } else {
+ ldap_back_conn_free( lc );
+ }
+ }
+#if LDAP_BACK_PRINT_CONNTREE > 0
+ ldap_back_print_conntree( li, "<<< ldap_back_conn_destroy" );
+#endif /* LDAP_BACK_PRINT_CONNTREE */
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+
+ return 0;
+}
diff --git a/servers/slapd/back-ldif/Makefile.in b/servers/slapd/back-ldif/Makefile.in
new file mode 100644
index 0000000..50f2553
--- /dev/null
+++ b/servers/slapd/back-ldif/Makefile.in
@@ -0,0 +1,41 @@
+# Makefile.in for back-ldif
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2005-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SRCS = ldif.c
+OBJS = ldif.lo
+
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-ldif"
+BUILD_MOD = yes
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = $(yes_DEFS)
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+
+LIBBASE = back_ldif
+
+XINCPATH = -I.. -I$(srcdir)/..
+XDEFS = $(MODULES_CPPFLAGS)
+
+all-local-lib: ../.backend
+
+../.backend: lib$(LIBBASE).a
+ @touch $@
+
diff --git a/servers/slapd/back-ldif/ldif.c b/servers/slapd/back-ldif/ldif.c
new file mode 100644
index 0000000..bca2472
--- /dev/null
+++ b/servers/slapd/back-ldif/ldif.c
@@ -0,0 +1,2054 @@
+/* ldif.c - the ldif backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by Eric Stokes for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+#include <stdio.h>
+#include <ac/string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ac/dirent.h>
+#include <fcntl.h>
+#include <ac/errno.h>
+#include <ac/unistd.h>
+#include "slap.h"
+#include "lutil.h"
+#include "slap-config.h"
+
+struct ldif_tool {
+ Entry **entries; /* collected by bi_tool_entry_first() */
+ ID elen; /* length of entries[] array */
+ ID ecount; /* number of entries */
+ ID ecurrent; /* bi_tool_entry_next() position */
+# define ENTRY_BUFF_INCREMENT 500 /* initial entries[] length */
+ struct berval *tl_base;
+ int tl_scope;
+ Filter *tl_filter;
+};
+
+/* Per-database data */
+struct ldif_info {
+ struct berval li_base_path; /* database directory */
+ struct ldif_tool li_tool; /* for slap tools */
+ /*
+ * Read-only LDAP requests readlock li_rdwr for filesystem input.
+ * Update requests first lock li_modop_mutex for filesystem I/O,
+ * and then writelock li_rdwr as well for filesystem output.
+ * This allows update requests to do callbacks that acquire
+ * read locks, e.g. access controls that inspect entries.
+ * (An alternative would be recursive read/write locks.)
+ */
+ ldap_pvt_thread_mutex_t li_modop_mutex; /* serialize update requests */
+ ldap_pvt_thread_rdwr_t li_rdwr; /* no other I/O when writing */
+};
+
+static int write_data( int fd, const char *spew, int len, int *save_errno );
+
+#ifdef _WIN32
+#define mkdir(a,b) mkdir(a)
+#define move_file(from, to) (!MoveFileEx(from, to, MOVEFILE_REPLACE_EXISTING))
+#else
+#define move_file(from, to) rename(from, to)
+#endif
+#define move_dir(from, to) rename(from, to)
+
+
+#define LDIF ".ldif"
+#define LDIF_FILETYPE_SEP '.' /* LDIF[0] */
+
+/*
+ * Unsafe/translated characters in the filesystem.
+ *
+ * LDIF_UNSAFE_CHAR(c) returns true if the character c is not to be used
+ * in relative filenames, except it should accept '\\', '{' and '}' even
+ * if unsafe. The value should be a constant expression.
+ *
+ * If '\\' is unsafe, #define LDIF_ESCAPE_CHAR as a safe character.
+ * If '{' and '}' are unsafe, #define IX_FSL/IX_FSR as safe characters.
+ * (Not digits, '-' or '+'. IX_FSL == IX_FSR is allowed.)
+ *
+ * Characters are escaped as LDIF_ESCAPE_CHAR followed by two hex digits,
+ * except '\\' is replaced with LDIF_ESCAPE_CHAR and {} with IX_FS[LR].
+ * Also some LDIF special chars are hex-escaped.
+ *
+ * Thus an LDIF filename is a valid normalized RDN (or suffix DN)
+ * followed by ".ldif", except with '\\' replaced with LDIF_ESCAPE_CHAR.
+ */
+
+#ifndef _WIN32
+
+/*
+ * Unix/MacOSX version. ':' vs '/' can cause confusion on MacOSX so we
+ * escape both. We escape them on Unix so both OS variants get the same
+ * filenames.
+ */
+#define LDIF_ESCAPE_CHAR '\\'
+#define LDIF_UNSAFE_CHAR(c) ((c) == '/' || (c) == ':')
+
+#else /* _WIN32 */
+
+/* Windows version - Microsoft's list of unsafe characters, except '\\' */
+#define LDIF_ESCAPE_CHAR '^' /* Not '\\' (unsafe on Windows) */
+#define LDIF_UNSAFE_CHAR(c) \
+ ((c) == '/' || (c) == ':' || \
+ (c) == '<' || (c) == '>' || (c) == '"' || \
+ (c) == '|' || (c) == '?' || (c) == '*')
+
+#endif /* !_WIN32 */
+
+/*
+ * Left and Right "{num}" prefix to ordered RDNs ("olcDatabase={1}mdb").
+ * IX_DN* are for LDAP RDNs, IX_FS* for their .ldif filenames.
+ */
+#define IX_DNL '{'
+#define IX_DNR '}'
+#ifndef IX_FSL
+#define IX_FSL IX_DNL
+#define IX_FSR IX_DNR
+#endif
+
+/*
+ * Test for unsafe chars, as well as chars handled specially by back-ldif:
+ * - If the escape char is not '\\', it must itself be escaped. Otherwise
+ * '\\' and the escape char would map to the same character.
+ * - Escape the '.' in ".ldif", so the directory for an RDN that actually
+ * ends with ".ldif" can not conflict with a file of the same name. And
+ * since some OSes/programs choke on multiple '.'s, escape all of them.
+ * - If '{' and '}' are translated to some other characters, those
+ * characters must in turn be escaped when they occur in an RDN.
+ */
+#ifndef LDIF_NEED_ESCAPE
+#define LDIF_NEED_ESCAPE(c) \
+ ((LDIF_UNSAFE_CHAR(c)) || \
+ LDIF_MAYBE_UNSAFE(c, LDIF_ESCAPE_CHAR) || \
+ LDIF_MAYBE_UNSAFE(c, LDIF_FILETYPE_SEP) || \
+ LDIF_MAYBE_UNSAFE(c, IX_FSL) || \
+ (IX_FSR != IX_FSL && LDIF_MAYBE_UNSAFE(c, IX_FSR)))
+#endif
+/*
+ * Helper macro for LDIF_NEED_ESCAPE(): Treat character x as unsafe if
+ * back-ldif does not already treat is specially.
+ */
+#define LDIF_MAYBE_UNSAFE(c, x) \
+ (!(LDIF_UNSAFE_CHAR(x) || (x) == '\\' || (x) == IX_DNL || (x) == IX_DNR) \
+ && (c) == (x))
+
+/* Collect other "safe char" tests here, until someone needs a fix. */
+enum {
+ eq_unsafe = LDIF_UNSAFE_CHAR('='),
+ safe_filenames = STRLENOF("" LDAP_DIRSEP "") == 1 && !(
+ LDIF_UNSAFE_CHAR('-') || /* for "{-1}frontend" in bconfig.c */
+ LDIF_UNSAFE_CHAR(LDIF_ESCAPE_CHAR) ||
+ LDIF_UNSAFE_CHAR(IX_FSL) || LDIF_UNSAFE_CHAR(IX_FSR))
+};
+/* Sanity check: Try to force a compilation error if !safe_filenames */
+typedef struct {
+ int assert_safe_filenames : safe_filenames ? 2 : -2;
+} assert_safe_filenames[safe_filenames ? 2 : -2];
+
+
+static ConfigTable ldifcfg[] = {
+ { "directory", "dir", 2, 2, 0, ARG_BERVAL|ARG_OFFSET,
+ (void *)offsetof(struct ldif_info, li_base_path),
+ "( OLcfgDbAt:0.1 NAME 'olcDbDirectory' "
+ "DESC 'Directory for database content' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED,
+ NULL, NULL, NULL, NULL }
+};
+
+static ConfigOCs ldifocs[] = {
+ { "( OLcfgDbOc:2.1 "
+ "NAME 'olcLdifConfig' "
+ "DESC 'LDIF backend configuration' "
+ "SUP olcDatabaseConfig "
+ "MUST ( olcDbDirectory ) )", Cft_Database, ldifcfg },
+ { NULL, 0, NULL }
+};
+
+
+/*
+ * Handle file/directory names.
+ */
+
+/* Set *res = LDIF filename path for the normalized DN */
+static int
+ndn2path( Operation *op, struct berval *dn, struct berval *res, int empty_ok )
+{
+ BackendDB *be = op->o_bd;
+ struct ldif_info *li = (struct ldif_info *) be->be_private;
+ struct berval *suffixdn = &be->be_nsuffix[0];
+ const char *start, *end, *next, *p;
+ char ch, *ptr;
+ ber_len_t len;
+ static const char hex[] = "0123456789ABCDEF";
+
+ assert( dn != NULL );
+ assert( !BER_BVISNULL( dn ) );
+ assert( suffixdn != NULL );
+ assert( !BER_BVISNULL( suffixdn ) );
+ assert( dnIsSuffix( dn, suffixdn ) );
+
+ if ( dn->bv_len == 0 && !empty_ok ) {
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ start = dn->bv_val;
+ end = start + dn->bv_len;
+
+ /* Room for dir, dirsep, dn, LDIF, "\hexpair"-escaping of unsafe chars */
+ len = li->li_base_path.bv_len + dn->bv_len + (1 + STRLENOF( LDIF ));
+ for ( p = start; p < end; ) {
+ ch = *p++;
+ if ( LDIF_NEED_ESCAPE( ch ) )
+ len += 2;
+ }
+ res->bv_val = ch_malloc( len + 1 );
+
+ ptr = lutil_strcopy( res->bv_val, li->li_base_path.bv_val );
+ for ( next = end - suffixdn->bv_len; end > start; end = next ) {
+ /* Set p = start of DN component, next = &',' or start of DN */
+ while ( (p = next) > start ) {
+ --next;
+ if ( DN_SEPARATOR( *next ) )
+ break;
+ }
+ /* Append <dirsep> <p..end-1: RDN or database-suffix> */
+ for ( *ptr++ = LDAP_DIRSEP[0]; p < end; *ptr++ = ch ) {
+ ch = *p++;
+ if ( LDIF_ESCAPE_CHAR != '\\' && ch == '\\' ) {
+ ch = LDIF_ESCAPE_CHAR;
+ } else if ( IX_FSL != IX_DNL && ch == IX_DNL ) {
+ ch = IX_FSL;
+ } else if ( IX_FSR != IX_DNR && ch == IX_DNR ) {
+ ch = IX_FSR;
+ } else if ( LDIF_NEED_ESCAPE( ch ) ) {
+ *ptr++ = LDIF_ESCAPE_CHAR;
+ *ptr++ = hex[(ch & 0xFFU) >> 4];
+ ch = hex[ch & 0x0FU];
+ }
+ }
+ }
+ ptr = lutil_strcopy( ptr, LDIF );
+ res->bv_len = ptr - res->bv_val;
+
+ assert( res->bv_len <= len );
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * *dest = dupbv(<dir + LDAP_DIRSEP>), plus room for <more>-sized filename.
+ * Return pointer past the dirname.
+ */
+static char *
+fullpath_alloc( struct berval *dest, const struct berval *dir, ber_len_t more )
+{
+ char *s = SLAP_MALLOC( dir->bv_len + more + 2 );
+
+ dest->bv_val = s;
+ if ( s == NULL ) {
+ dest->bv_len = 0;
+ Debug( LDAP_DEBUG_ANY, "back-ldif: out of memory\n" );
+ } else {
+ s = lutil_strcopy( dest->bv_val, dir->bv_val );
+ *s++ = LDAP_DIRSEP[0];
+ *s = '\0';
+ dest->bv_len = s - dest->bv_val;
+ }
+ return s;
+}
+
+/*
+ * Append filename to fullpath_alloc() dirname or replace previous filename.
+ * dir_end = fullpath_alloc() return value.
+ */
+#define FILL_PATH(fpath, dir_end, filename) \
+ ((fpath)->bv_len = lutil_strcopy(dir_end, filename) - (fpath)->bv_val)
+
+
+/* .ldif entry filename length <-> subtree dirname length. */
+#define ldif2dir_len(bv) ((bv).bv_len -= STRLENOF(LDIF))
+#define dir2ldif_len(bv) ((bv).bv_len += STRLENOF(LDIF))
+/* .ldif entry filename <-> subtree dirname, both with dirname length. */
+#define ldif2dir_name(bv) ((bv).bv_val[(bv).bv_len] = '\0')
+#define dir2ldif_name(bv) ((bv).bv_val[(bv).bv_len] = LDIF_FILETYPE_SEP)
+
+/* Get the parent directory path, plus the LDIF suffix overwritten by a \0. */
+static int
+get_parent_path( struct berval *dnpath, struct berval *res )
+{
+ ber_len_t i = dnpath->bv_len;
+
+ while ( i > 0 && dnpath->bv_val[ --i ] != LDAP_DIRSEP[0] ) ;
+ if ( res == NULL ) {
+ res = dnpath;
+ } else {
+ res->bv_val = SLAP_MALLOC( i + 1 + STRLENOF(LDIF) );
+ if ( res->bv_val == NULL )
+ return LDAP_OTHER;
+ AC_MEMCPY( res->bv_val, dnpath->bv_val, i );
+ }
+ res->bv_len = i;
+ strcpy( res->bv_val + i, LDIF );
+ res->bv_val[i] = '\0';
+ return LDAP_SUCCESS;
+}
+
+/* Make temporary filename pattern for mkstemp() based on dnpath. */
+static char *
+ldif_tempname( const struct berval *dnpath )
+{
+ static const char suffix[] = ".XXXXXX";
+ ber_len_t len = dnpath->bv_len - STRLENOF( LDIF );
+ char *name = SLAP_MALLOC( len + sizeof( suffix ) );
+
+ if ( name != NULL ) {
+ AC_MEMCPY( name, dnpath->bv_val, len );
+ strcpy( name + len, suffix );
+ }
+ return name;
+}
+
+/* CRC-32 table for the polynomial:
+ * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.
+ *
+ * As used by zlib
+ */
+
+static const ber_uint_t crctab[256] = {
+ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+ 0x2d02ef8dL
+};
+
+#define CRC1 crc = crctab[(crc ^ *buf++) & 0xff] ^ (crc >> 8)
+#define CRC8 CRC1; CRC1; CRC1; CRC1; CRC1; CRC1; CRC1; CRC1
+unsigned int
+crc32(const void *vbuf, int len)
+{
+ const unsigned char *buf = vbuf;
+ ber_uint_t crc = 0xffffffff;
+
+ while (len > 7) {
+ CRC8;
+ len -= 8;
+ }
+ while (len) {
+ CRC1;
+ len--;
+ }
+
+ return crc ^ 0xffffffff;
+}
+
+/*
+ * Read a file, or stat() it if datap == NULL. Allocate and fill *datap.
+ * Return LDAP_SUCCESS, LDAP_NO_SUCH_OBJECT (no such file), or another error.
+ */
+static int
+ldif_read_file( const char *path, char **datap )
+{
+ int rc = LDAP_SUCCESS, fd, len;
+ int res = -1; /* 0:success, <0:error, >0:file too big/growing. */
+ struct stat st;
+ char *data = NULL, *ptr = NULL;
+ const char *msg;
+
+ if ( datap == NULL ) {
+ res = stat( path, &st );
+ goto done;
+ }
+ fd = open( path, O_RDONLY );
+ if ( fd >= 0 ) {
+ if ( fstat( fd, &st ) == 0 ) {
+ if ( st.st_size > INT_MAX - 2 ) {
+ res = 1;
+ } else {
+ len = st.st_size + 1; /* +1 detects file size > st.st_size */
+ *datap = data = ptr = SLAP_MALLOC( len + 1 );
+ if ( ptr != NULL ) {
+ while ( len && (res = read( fd, ptr, len )) ) {
+ if ( res > 0 ) {
+ len -= res;
+ ptr += res;
+ } else if ( errno != EINTR ) {
+ break;
+ }
+ }
+ *ptr = '\0';
+ }
+ }
+ }
+ if ( close( fd ) < 0 )
+ res = -1;
+ }
+
+ done:
+ if ( res == 0 ) {
+#ifdef LDAP_DEBUG
+ msg = "entry file exists";
+ if ( datap ) {
+ msg = "read entry file";
+ len = ptr - data;
+ ptr = strstr( data, "\n# CRC32" );
+ if (!ptr) {
+ msg = "read entry file without checksum";
+ } else {
+ unsigned int crc1 = 0, crc2 = 1;
+ if ( sscanf( ptr + 9, "%08x", &crc1) == 1) {
+ ptr = strchr(ptr+1, '\n');
+ if ( ptr ) {
+ ptr++;
+ len -= (ptr - data);
+ crc2 = crc32( ptr, len );
+ }
+ }
+ if ( crc1 != crc2 ) {
+ Debug( LDAP_DEBUG_ANY, "ldif_read_file: checksum error on \"%s\"\n",
+ path );
+ return rc;
+ }
+ }
+ }
+ Debug( LDAP_DEBUG_TRACE, "ldif_read_file: %s: \"%s\"\n", msg, path );
+#endif /* LDAP_DEBUG */
+ } else {
+ char ebuf[128];
+ if ( res < 0 && errno == ENOENT ) {
+ Debug( LDAP_DEBUG_TRACE, "ldif_read_file: "
+ "no entry file \"%s\"\n", path );
+ rc = LDAP_NO_SUCH_OBJECT;
+ } else {
+ msg = res < 0 ? AC_STRERROR_R( errno, ebuf, sizeof(ebuf) ) : "bad stat() size";
+ Debug( LDAP_DEBUG_ANY, "ldif_read_file: %s for \"%s\"\n",
+ msg, path );
+ rc = LDAP_OTHER;
+ }
+ if ( data != NULL )
+ SLAP_FREE( data );
+ }
+ return rc;
+}
+
+/*
+ * return nonnegative for success or -1 for error
+ * do not return numbers less than -1
+ */
+static int
+spew_file( int fd, const char *spew, int len, int *save_errno )
+{
+ int writeres;
+#define HEADER "# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.\n"
+ char header[sizeof(HEADER "# CRC32 12345678\n")];
+
+ sprintf(header, HEADER "# CRC32 %08x\n", crc32(spew, len));
+ writeres = write_data(fd, header, sizeof(header)-1, save_errno);
+ return writeres < 0 ? writeres : write_data(fd, spew, len, save_errno);
+}
+
+static int
+write_data( int fd, const char *spew, int len, int *save_errno )
+{
+ int writeres = 0;
+ while(len > 0) {
+ writeres = write(fd, spew, len);
+ if(writeres == -1) {
+ *save_errno = errno;
+ if (*save_errno != EINTR)
+ break;
+ }
+ else {
+ spew += writeres;
+ len -= writeres;
+ }
+ }
+ return writeres;
+}
+
+/* Write an entry LDIF file. Create parentdir first if non-NULL. */
+static int
+ldif_write_entry(
+ Operation *op,
+ Entry *e,
+ const struct berval *path,
+ const char *parentdir,
+ const char **text )
+{
+ int rc = LDAP_OTHER, res, save_errno = 0;
+ int fd, entry_length;
+ char *entry_as_string, *tmpfname;
+ char ebuf[128];
+
+ if ( op->o_abandon )
+ return SLAPD_ABANDON;
+
+ if ( parentdir != NULL && mkdir( parentdir, 0750 ) < 0 ) {
+ save_errno = errno;
+ Debug( LDAP_DEBUG_ANY, "ldif_write_entry: %s \"%s\": %s\n",
+ "cannot create parent directory",
+ parentdir, AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+ *text = "internal error (cannot create parent directory)";
+ return rc;
+ }
+
+ tmpfname = ldif_tempname( path );
+ fd = tmpfname == NULL ? -1 : mkstemp( tmpfname );
+ if ( fd < 0 ) {
+ save_errno = errno;
+ Debug( LDAP_DEBUG_ANY, "ldif_write_entry: %s for \"%s\": %s\n",
+ "cannot create file", e->e_dn, AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+ *text = "internal error (cannot create file)";
+
+ } else {
+ ber_len_t dn_len = e->e_name.bv_len;
+ struct berval rdn;
+
+ /* Only save the RDN onto disk */
+ dnRdn( &e->e_name, &rdn );
+ if ( rdn.bv_len != dn_len ) {
+ e->e_name.bv_val[rdn.bv_len] = '\0';
+ e->e_name.bv_len = rdn.bv_len;
+ }
+
+ res = -2;
+ ldap_pvt_thread_mutex_lock( &entry2str_mutex );
+ entry_as_string = entry2str( e, &entry_length );
+ if ( entry_as_string != NULL )
+ res = spew_file( fd, entry_as_string, entry_length, &save_errno );
+ ldap_pvt_thread_mutex_unlock( &entry2str_mutex );
+
+ /* Restore full DN */
+ if ( rdn.bv_len != dn_len ) {
+ e->e_name.bv_val[rdn.bv_len] = ',';
+ e->e_name.bv_len = dn_len;
+ }
+
+ if ( close( fd ) < 0 && res >= 0 ) {
+ res = -1;
+ save_errno = errno;
+ }
+
+ if ( res >= 0 ) {
+ if ( move_file( tmpfname, path->bv_val ) == 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "ldif_write_entry: "
+ "wrote entry \"%s\"\n", e->e_name.bv_val );
+ rc = LDAP_SUCCESS;
+ } else {
+ save_errno = errno;
+ Debug( LDAP_DEBUG_ANY, "ldif_write_entry: "
+ "could not put entry file for \"%s\" in place: %s\n",
+ e->e_name.bv_val, AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+ *text = "internal error (could not put entry file in place)";
+ }
+ } else if ( res == -1 ) {
+ Debug( LDAP_DEBUG_ANY, "ldif_write_entry: %s \"%s\": %s\n",
+ "write error to", tmpfname, AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+ *text = "internal error (write error to entry file)";
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ unlink( tmpfname );
+ }
+ }
+
+ if ( tmpfname )
+ SLAP_FREE( tmpfname );
+ return rc;
+}
+
+/*
+ * Read the entry at path, or if entryp==NULL just see if it exists.
+ * pdn and pndn are the parent's DN and normalized DN, or both NULL.
+ * Return an LDAP result code.
+ */
+static int
+ldif_read_entry(
+ Operation *op,
+ const char *path,
+ struct berval *pdn,
+ struct berval *pndn,
+ Entry **entryp,
+ const char **text )
+{
+ int rc;
+ Entry *entry;
+ char *entry_as_string;
+ struct berval rdn;
+
+ /* TODO: Does slapd prevent Abandon of Bind as per rfc4511?
+ * If so we need not check for LDAP_REQ_BIND here.
+ */
+ if ( op->o_abandon && op->o_tag != LDAP_REQ_BIND )
+ return SLAPD_ABANDON;
+
+ rc = ldif_read_file( path, entryp ? &entry_as_string : NULL );
+
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ if ( entryp == NULL )
+ break;
+ *entryp = entry = str2entry( entry_as_string );
+ SLAP_FREE( entry_as_string );
+ if ( entry == NULL ) {
+ rc = LDAP_OTHER;
+ if ( text != NULL )
+ *text = "internal error (cannot parse some entry file)";
+ break;
+ }
+ if ( pdn == NULL || BER_BVISEMPTY( pdn ) )
+ break;
+ /* Append parent DN to DN from LDIF file */
+ rdn = entry->e_name;
+ build_new_dn( &entry->e_name, pdn, &rdn, NULL );
+ SLAP_FREE( rdn.bv_val );
+ rdn = entry->e_nname;
+ build_new_dn( &entry->e_nname, pndn, &rdn, NULL );
+ SLAP_FREE( rdn.bv_val );
+ break;
+
+ case LDAP_OTHER:
+ if ( text != NULL )
+ *text = entryp
+ ? "internal error (cannot read some entry file)"
+ : "internal error (cannot stat some entry file)";
+ break;
+ }
+
+ return rc;
+}
+
+/*
+ * Read the operation's entry, or if entryp==NULL just see if it exists.
+ * Return an LDAP result code. May set *text to a message on failure.
+ * If pathp is non-NULL, set it to the entry filename on success.
+ */
+static int
+get_entry(
+ Operation *op,
+ Entry **entryp,
+ struct berval *pathp,
+ const char **text )
+{
+ int rc;
+ struct berval path, pdn, pndn;
+
+ dnParent( &op->o_req_dn, &pdn );
+ dnParent( &op->o_req_ndn, &pndn );
+ rc = ndn2path( op, &op->o_req_ndn, &path, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ rc = ldif_read_entry( op, path.bv_val, &pdn, &pndn, entryp, text );
+
+ if ( rc == LDAP_SUCCESS && pathp != NULL ) {
+ *pathp = path;
+ } else {
+ SLAP_FREE( path.bv_val );
+ }
+ done:
+ return rc;
+}
+
+
+/*
+ * RDN-named directory entry, with special handling of "attr={num}val" RDNs.
+ * For sorting, filename "attr=val.ldif" is truncated to "attr="val\0ldif",
+ * and filename "attr={num}val.ldif" to "attr={\0um}val.ldif".
+ * Does not sort escaped chars correctly, would need to un-escape them.
+ */
+typedef struct bvlist {
+ struct bvlist *next;
+ char *trunc; /* filename was truncated here */
+ int inum; /* num from "attr={num}" in filename, or INT_MIN */
+ char savech; /* original char at *trunc */
+ /* BVL_NAME(&bvlist) is the filename, allocated after the struct: */
+# define BVL_NAME(bvl) ((char *) ((bvl) + 1))
+# define BVL_SIZE(namelen) (sizeof(bvlist) + (namelen) + 1)
+} bvlist;
+
+static int
+ldif_send_entry( Operation *op, SlapReply *rs, Entry *e, int scope )
+{
+ int rc = LDAP_SUCCESS;
+
+ if ( scope == LDAP_SCOPE_BASE || scope == LDAP_SCOPE_SUBTREE ) {
+ if ( rs == NULL ) {
+ /* Save the entry for tool mode */
+ struct ldif_tool *tl =
+ &((struct ldif_info *) op->o_bd->be_private)->li_tool;
+
+ if ( tl->ecount >= tl->elen ) {
+ /* Allocate/grow entries */
+ ID elen = tl->elen ? tl->elen * 2 : ENTRY_BUFF_INCREMENT;
+ Entry **entries = (Entry **) SLAP_REALLOC( tl->entries,
+ sizeof(Entry *) * elen );
+ if ( entries == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ldif_send_entry: out of memory\n" );
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ tl->elen = elen;
+ tl->entries = entries;
+ }
+ tl->entries[tl->ecount++] = e;
+ e->e_id = tl->ecount;
+ return rc;
+ }
+
+ else if ( !get_manageDSAit( op ) && is_entry_referral( e ) ) {
+ /* Send a continuation reference.
+ * (ldif_back_referrals() handles baseobject referrals.)
+ * Don't check the filter since it's only a candidate.
+ */
+ BerVarray refs = get_entry_referrals( op, e );
+ rs->sr_ref = referral_rewrite( refs, &e->e_name, NULL, scope );
+ rs->sr_entry = e;
+ rc = send_search_reference( op, rs );
+ ber_bvarray_free( rs->sr_ref );
+ ber_bvarray_free( refs );
+ rs->sr_ref = NULL;
+ rs->sr_entry = NULL;
+ }
+
+ else if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE ) {
+ rs->sr_entry = e;
+ rs->sr_attrs = op->ors_attrs;
+ /* Could set REP_ENTRY_MUSTBEFREED too for efficiency,
+ * but refraining lets us test unFREEable MODIFIABLE
+ * entries. Like entries built on the stack.
+ */
+ rs->sr_flags = REP_ENTRY_MODIFIABLE;
+ rc = send_search_entry( op, rs );
+ rs->sr_entry = NULL;
+ rs->sr_attrs = NULL;
+ }
+ }
+
+ done:
+ entry_free( e );
+ return rc;
+}
+
+/* Read LDIF directory <path> into <listp>. Set *fname_maxlenp. */
+static int
+ldif_readdir(
+ Operation *op,
+ SlapReply *rs,
+ const struct berval *path,
+ bvlist **listp,
+ ber_len_t *fname_maxlenp )
+{
+ int rc = LDAP_SUCCESS;
+ DIR *dir_of_path;
+ char ebuf[128];
+
+ *listp = NULL;
+ *fname_maxlenp = 0;
+
+ dir_of_path = opendir( path->bv_val );
+ if ( dir_of_path == NULL ) {
+ int save_errno = errno;
+ struct ldif_info *li = (struct ldif_info *) op->o_bd->be_private;
+ int is_rootDSE = (path->bv_len == li->li_base_path.bv_len);
+
+ /* Absent directory is OK (leaf entry), except the database dir */
+ if ( is_rootDSE || save_errno != ENOENT ) {
+ Debug( LDAP_DEBUG_ANY,
+ "=> ldif_search_entry: failed to opendir \"%s\": %s\n",
+ path->bv_val, AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+ rc = LDAP_OTHER;
+ if ( rs != NULL )
+ rs->sr_text =
+ save_errno != ENOENT ? "internal error (bad directory)"
+ : "internal error (database directory does not exist)";
+ }
+
+ } else {
+ bvlist *ptr;
+ struct dirent *dir;
+ int save_errno = 0;
+
+ while ( (dir = readdir( dir_of_path )) != NULL ) {
+ size_t fname_len;
+ bvlist *bvl, **prev;
+ char *trunc, *idxp, *endp, *endp2;
+
+ fname_len = strlen( dir->d_name );
+ if ( fname_len < STRLENOF( "x=" LDIF )) /* min filename size */
+ continue;
+ if ( strcmp( dir->d_name + fname_len - STRLENOF(LDIF), LDIF ))
+ continue;
+
+ if ( *fname_maxlenp < fname_len )
+ *fname_maxlenp = fname_len;
+
+ bvl = SLAP_MALLOC( BVL_SIZE( fname_len ) );
+ if ( bvl == NULL ) {
+ rc = LDAP_OTHER;
+ save_errno = errno;
+ break;
+ }
+ strcpy( BVL_NAME( bvl ), dir->d_name );
+
+ /* Make it sortable by ("attr=val" or <preceding {num}, num>) */
+ trunc = BVL_NAME( bvl ) + fname_len - STRLENOF( LDIF );
+ if ( (idxp = strchr( BVL_NAME( bvl ) + 2, IX_FSL )) != NULL &&
+ (endp = strchr( ++idxp, IX_FSR )) != NULL && endp > idxp &&
+ (eq_unsafe || idxp[-2] == '=' || endp + 1 == trunc) )
+ {
+ /* attr={n}val or bconfig.c's "pseudo-indexed" attr=val{n} */
+ bvl->inum = strtol( idxp, &endp2, 10 );
+ if ( endp2 == endp ) {
+ trunc = idxp;
+ goto truncate;
+ }
+ }
+ bvl->inum = INT_MIN;
+ truncate:
+ bvl->trunc = trunc;
+ bvl->savech = *trunc;
+ *trunc = '\0';
+
+ /* Insertion sort */
+ for ( prev = listp; (ptr = *prev) != NULL; prev = &ptr->next ) {
+ int cmp = strcmp( BVL_NAME( bvl ), BVL_NAME( ptr ));
+ if ( cmp < 0 || (cmp == 0 && bvl->inum < ptr->inum) )
+ break;
+ }
+ *prev = bvl;
+ bvl->next = ptr;
+ }
+
+ if ( closedir( dir_of_path ) < 0 ) {
+ save_errno = errno;
+ rc = LDAP_OTHER;
+ if ( rs != NULL )
+ rs->sr_text = "internal error (bad directory)";
+ }
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "ldif_search_entry: %s \"%s\": %s\n",
+ "error reading directory", path->bv_val,
+ AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * Send an entry, recursively search its children, and free or save it.
+ * Return an LDAP result code. Parameters:
+ * op, rs operation and reply. rs == NULL for slap tools.
+ * e entry to search, or NULL for rootDSE.
+ * scope scope for the part of the search from this entry.
+ * path LDIF filename -- bv_len and non-directory part are overwritten.
+ */
+static int
+ldif_search_entry(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ int scope,
+ struct berval *path )
+{
+ int rc = LDAP_SUCCESS;
+ struct berval dn = BER_BVC( "" ), ndn = BER_BVC( "" );
+
+ if ( scope != LDAP_SCOPE_BASE && e != NULL ) {
+ /* Copy DN/NDN since we send the entry with REP_ENTRY_MODIFIABLE,
+ * which bconfig.c seems to need. (TODO: see config_rename_one.)
+ */
+ if ( ber_dupbv( &dn, &e->e_name ) == NULL ||
+ ber_dupbv( &ndn, &e->e_nname ) == NULL )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "ldif_search_entry: out of memory\n" );
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ }
+
+ /* Send the entry if appropriate, and free or save it */
+ if ( e != NULL )
+ rc = ldif_send_entry( op, rs, e, scope );
+
+ /* Search the children */
+ if ( scope != LDAP_SCOPE_BASE && rc == LDAP_SUCCESS ) {
+ bvlist *list, *ptr;
+ struct berval fpath; /* becomes child pathname */
+ char *dir_end; /* will point past dirname in fpath */
+
+ ldif2dir_len( *path );
+ ldif2dir_name( *path );
+ rc = ldif_readdir( op, rs, path, &list, &fpath.bv_len );
+
+ if ( list != NULL ) {
+ const char **text = rs == NULL ? NULL : &rs->sr_text;
+
+ if ( scope == LDAP_SCOPE_ONELEVEL )
+ scope = LDAP_SCOPE_BASE;
+ else if ( scope == LDAP_SCOPE_SUBORDINATE )
+ scope = LDAP_SCOPE_SUBTREE;
+
+ /* Allocate fpath and fill in directory part */
+ dir_end = fullpath_alloc( &fpath, path, fpath.bv_len );
+ if ( dir_end == NULL )
+ rc = LDAP_OTHER;
+
+ do {
+ ptr = list;
+
+ if ( rc == LDAP_SUCCESS ) {
+ *ptr->trunc = ptr->savech;
+ FILL_PATH( &fpath, dir_end, BVL_NAME( ptr ));
+
+ rc = ldif_read_entry( op, fpath.bv_val, &dn, &ndn,
+ &e, text );
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ rc = ldif_search_entry( op, rs, e, scope, &fpath );
+ break;
+ case LDAP_NO_SUCH_OBJECT:
+ /* Only the search baseDN may produce noSuchObject. */
+ rc = LDAP_OTHER;
+ if ( rs != NULL )
+ rs->sr_text = "internal error "
+ "(did someone just remove an entry file?)";
+ Debug( LDAP_DEBUG_ANY, "ldif_search_entry: "
+ "file listed in parent directory does not exist: "
+ "\"%s\"\n", fpath.bv_val );
+ break;
+ }
+ }
+
+ list = ptr->next;
+ SLAP_FREE( ptr );
+ } while ( list != NULL );
+
+ if ( !BER_BVISNULL( &fpath ) )
+ SLAP_FREE( fpath.bv_val );
+ }
+ }
+
+ done:
+ if ( !BER_BVISEMPTY( &dn ) )
+ ber_memfree( dn.bv_val );
+ if ( !BER_BVISEMPTY( &ndn ) )
+ ber_memfree( ndn.bv_val );
+ return rc;
+}
+
+static int
+search_tree( Operation *op, SlapReply *rs )
+{
+ int rc = LDAP_SUCCESS;
+ Entry *e = NULL;
+ struct berval path;
+ struct berval pdn, pndn;
+
+ (void) ndn2path( op, &op->o_req_ndn, &path, 1 );
+ if ( !BER_BVISEMPTY( &op->o_req_ndn ) ) {
+ /* Read baseObject */
+ dnParent( &op->o_req_dn, &pdn );
+ dnParent( &op->o_req_ndn, &pndn );
+ rc = ldif_read_entry( op, path.bv_val, &pdn, &pndn, &e,
+ rs == NULL ? NULL : &rs->sr_text );
+ }
+ if ( rc == LDAP_SUCCESS )
+ rc = ldif_search_entry( op, rs, e, op->ors_scope, &path );
+
+ ch_free( path.bv_val );
+ return rc;
+}
+
+
+/*
+ * Prepare to create or rename an entry:
+ * Check that the entry does not already exist.
+ * Check that the parent entry exists and can have subordinates,
+ * unless need_dir is NULL or adding the suffix entry.
+ *
+ * Return an LDAP result code. May set *text to a message on failure.
+ * If success, set *dnpath to LDIF entry path and *need_dir to
+ * (directory must be created ? dirname : NULL).
+ */
+static int
+ldif_prepare_create(
+ Operation *op,
+ Entry *e,
+ struct berval *dnpath,
+ char **need_dir,
+ const char **text )
+{
+ struct ldif_info *li = (struct ldif_info *) op->o_bd->be_private;
+ struct berval *ndn = &e->e_nname;
+ struct berval ppath = BER_BVNULL;
+ struct stat st;
+ Entry *parent = NULL;
+ int rc;
+ char ebuf[128];
+
+ if ( op->o_abandon )
+ return SLAPD_ABANDON;
+
+ rc = ndn2path( op, ndn, dnpath, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( stat( dnpath->bv_val, &st ) == 0 ) { /* entry .ldif file */
+ rc = LDAP_ALREADY_EXISTS;
+
+ } else if ( errno != ENOENT ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ldif_prepare_create: cannot stat \"%s\": %s\n",
+ dnpath->bv_val, AC_STRERROR_R( errno, ebuf, sizeof(ebuf) ) );
+ rc = LDAP_OTHER;
+ *text = "internal error (cannot check entry file)";
+
+ } else if ( need_dir != NULL ) {
+ *need_dir = NULL;
+ rc = get_parent_path( dnpath, &ppath );
+ /* If parent dir exists, so does parent .ldif:
+ * The directory gets created after and removed before the .ldif.
+ * Except with the database directory, which has no matching entry.
+ */
+ if ( rc == LDAP_SUCCESS && stat( ppath.bv_val, &st ) < 0 ) {
+ rc = errno == ENOENT && ppath.bv_len > li->li_base_path.bv_len
+ ? LDAP_NO_SUCH_OBJECT : LDAP_OTHER;
+ }
+ switch ( rc ) {
+ case LDAP_NO_SUCH_OBJECT:
+ /* No parent dir, check parent .ldif */
+ dir2ldif_name( ppath );
+ rc = ldif_read_entry( op, ppath.bv_val, NULL, NULL,
+ (op->o_tag != LDAP_REQ_ADD || get_manageDSAit( op )
+ ? &parent : NULL),
+ text );
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ /* Check that parent is not a referral, unless
+ * ldif_back_referrals() already checked.
+ */
+ if ( parent != NULL ) {
+ int is_ref = is_entry_referral( parent );
+ entry_free( parent );
+ if ( is_ref ) {
+ rc = LDAP_AFFECTS_MULTIPLE_DSAS;
+ *text = op->o_tag == LDAP_REQ_MODDN
+ ? "newSuperior is a referral object"
+ : "parent is a referral object";
+ break;
+ }
+ }
+ /* Must create parent directory. */
+ ldif2dir_name( ppath );
+ *need_dir = ppath.bv_val;
+ break;
+ case LDAP_NO_SUCH_OBJECT:
+ *text = op->o_tag == LDAP_REQ_MODDN
+ ? "newSuperior object does not exist"
+ : "parent does not exist";
+ break;
+ }
+ break;
+ case LDAP_OTHER:
+ Debug( LDAP_DEBUG_ANY,
+ "ldif_prepare_create: cannot stat \"%s\" parent dir: %s\n",
+ ndn->bv_val, AC_STRERROR_R( errno, ebuf, sizeof(ebuf) ) );
+ *text = "internal error (cannot stat parent dir)";
+ break;
+ }
+ if ( *need_dir == NULL && ppath.bv_val != NULL )
+ SLAP_FREE( ppath.bv_val );
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ SLAP_FREE( dnpath->bv_val );
+ BER_BVZERO( dnpath );
+ }
+ return rc;
+}
+
+static int
+apply_modify_to_entry(
+ Entry *entry,
+ Modifications *modlist,
+ Operation *op,
+ SlapReply *rs,
+ char *textbuf )
+{
+ int rc = modlist ? LDAP_UNWILLING_TO_PERFORM : LDAP_SUCCESS;
+ int is_oc = 0;
+ Modification *mods;
+
+ if (!acl_check_modlist(op, entry, modlist)) {
+ return LDAP_INSUFFICIENT_ACCESS;
+ }
+
+ for (; modlist != NULL; modlist = modlist->sml_next) {
+ mods = &modlist->sml_mod;
+
+ if ( mods->sm_desc == slap_schema.si_ad_objectClass ) {
+ is_oc = 1;
+ }
+ switch (mods->sm_op) {
+ case LDAP_MOD_ADD:
+ rc = modify_add_values(entry, mods,
+ get_permissiveModify(op),
+ &rs->sr_text, textbuf,
+ SLAP_TEXT_BUFLEN );
+ break;
+
+ case LDAP_MOD_DELETE:
+ rc = modify_delete_values(entry, mods,
+ get_permissiveModify(op),
+ &rs->sr_text, textbuf,
+ SLAP_TEXT_BUFLEN );
+ break;
+
+ case LDAP_MOD_REPLACE:
+ rc = modify_replace_values(entry, mods,
+ get_permissiveModify(op),
+ &rs->sr_text, textbuf,
+ SLAP_TEXT_BUFLEN );
+ break;
+
+ case LDAP_MOD_INCREMENT:
+ rc = modify_increment_values( entry,
+ mods, get_permissiveModify(op),
+ &rs->sr_text, textbuf,
+ SLAP_TEXT_BUFLEN );
+ break;
+
+ case SLAP_MOD_SOFTADD:
+ mods->sm_op = LDAP_MOD_ADD;
+ rc = modify_add_values(entry, mods,
+ get_permissiveModify(op),
+ &rs->sr_text, textbuf,
+ SLAP_TEXT_BUFLEN );
+ mods->sm_op = SLAP_MOD_SOFTADD;
+ if (rc == LDAP_TYPE_OR_VALUE_EXISTS) {
+ rc = LDAP_SUCCESS;
+ }
+ break;
+
+ case SLAP_MOD_SOFTDEL:
+ mods->sm_op = LDAP_MOD_DELETE;
+ rc = modify_delete_values(entry, mods,
+ get_permissiveModify(op),
+ &rs->sr_text, textbuf,
+ SLAP_TEXT_BUFLEN );
+ mods->sm_op = SLAP_MOD_SOFTDEL;
+ if (rc == LDAP_NO_SUCH_ATTRIBUTE) {
+ rc = LDAP_SUCCESS;
+ }
+ break;
+
+ case SLAP_MOD_ADD_IF_NOT_PRESENT:
+ if ( attr_find( entry->e_attrs, mods->sm_desc ) ) {
+ rc = LDAP_SUCCESS;
+ break;
+ }
+ mods->sm_op = LDAP_MOD_ADD;
+ rc = modify_add_values(entry, mods,
+ get_permissiveModify(op),
+ &rs->sr_text, textbuf,
+ SLAP_TEXT_BUFLEN );
+ mods->sm_op = SLAP_MOD_ADD_IF_NOT_PRESENT;
+ break;
+ }
+ if(rc != LDAP_SUCCESS) break;
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ rs->sr_text = NULL; /* Needed at least with SLAP_MOD_SOFTADD */
+ if ( is_oc ) {
+ entry->e_ocflags = 0;
+ }
+ /* check that the entry still obeys the schema */
+ rc = entry_schema_check( op, entry, NULL, 0, 0, NULL,
+ &rs->sr_text, textbuf, SLAP_TEXT_BUFLEN );
+ }
+
+ return rc;
+}
+
+
+static int
+ldif_back_referrals( Operation *op, SlapReply *rs )
+{
+ struct ldif_info *li = (struct ldif_info *) op->o_bd->be_private;
+ struct berval path, dn = op->o_req_dn, ndn = op->o_req_ndn;
+ ber_len_t min_dnlen;
+ Entry *entry = NULL, **entryp;
+ BerVarray ref;
+ int rc;
+
+ min_dnlen = op->o_bd->be_nsuffix[0].bv_len;
+ if ( min_dnlen == 0 ) {
+ /* Catch root DSE (empty DN), it is not a referral */
+ min_dnlen = 1;
+ }
+ if ( ndn2path( op, &ndn, &path, 0 ) != LDAP_SUCCESS ) {
+ return LDAP_SUCCESS; /* Root DSE again */
+ }
+
+ entryp = get_manageDSAit( op ) ? NULL : &entry;
+ ldap_pvt_thread_rdwr_rlock( &li->li_rdwr );
+
+ for (;;) {
+ dnParent( &dn, &dn );
+ dnParent( &ndn, &ndn );
+ rc = ldif_read_entry( op, path.bv_val, &dn, &ndn,
+ entryp, &rs->sr_text );
+ if ( rc != LDAP_NO_SUCH_OBJECT )
+ break;
+
+ rc = LDAP_SUCCESS;
+ if ( ndn.bv_len < min_dnlen )
+ break;
+ (void) get_parent_path( &path, NULL );
+ dir2ldif_name( path );
+ entryp = &entry;
+ }
+
+ ldap_pvt_thread_rdwr_runlock( &li->li_rdwr );
+ SLAP_FREE( path.bv_val );
+
+ if ( entry != NULL ) {
+ if ( is_entry_referral( entry ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "ldif_back_referrals: tag=%lu target=\"%s\" matched=\"%s\"\n",
+ (unsigned long) op->o_tag, op->o_req_dn.bv_val, entry->e_dn );
+
+ ref = get_entry_referrals( op, entry );
+ rs->sr_ref = referral_rewrite( ref, &entry->e_name, &op->o_req_dn,
+ op->o_tag == LDAP_REQ_SEARCH ?
+ op->ors_scope : LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+
+ if ( rs->sr_ref != NULL ) {
+ /* send referral */
+ rc = rs->sr_err = LDAP_REFERRAL;
+ rs->sr_matched = entry->e_dn;
+ send_ldap_result( op, rs );
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ } else {
+ rc = LDAP_OTHER;
+ rs->sr_text = "bad referral object";
+ }
+ rs->sr_matched = NULL;
+ }
+
+ entry_free( entry );
+ }
+
+ return rc;
+}
+
+
+/* LDAP operations */
+
+static int
+ldif_back_bind( Operation *op, SlapReply *rs )
+{
+ struct ldif_info *li;
+ Attribute *a;
+ AttributeDescription *password = slap_schema.si_ad_userPassword;
+ int return_val;
+ Entry *entry = NULL;
+
+ switch ( be_rootdn_bind( op, rs ) ) {
+ case SLAP_CB_CONTINUE:
+ break;
+
+ default:
+ /* in case of success, front end will send result;
+ * otherwise, be_rootdn_bind() did */
+ return rs->sr_err;
+ }
+
+ li = (struct ldif_info *) op->o_bd->be_private;
+ ldap_pvt_thread_rdwr_rlock(&li->li_rdwr);
+ return_val = get_entry(op, &entry, NULL, NULL);
+
+ /* no object is found for them */
+ if(return_val != LDAP_SUCCESS) {
+ rs->sr_err = return_val = LDAP_INVALID_CREDENTIALS;
+ goto return_result;
+ }
+
+ /* they don't have userpassword */
+ if((a = attr_find(entry->e_attrs, password)) == NULL) {
+ rs->sr_err = LDAP_INAPPROPRIATE_AUTH;
+ return_val = 1;
+ goto return_result;
+ }
+
+ /* authentication actually failed */
+ if(slap_passwd_check(op, entry, a, &op->oq_bind.rb_cred,
+ &rs->sr_text) != 0) {
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ return_val = 1;
+ goto return_result;
+ }
+
+ /* let the front-end send success */
+ return_val = LDAP_SUCCESS;
+
+ return_result:
+ ldap_pvt_thread_rdwr_runlock(&li->li_rdwr);
+ if(return_val != LDAP_SUCCESS)
+ send_ldap_result( op, rs );
+ if(entry != NULL)
+ entry_free(entry);
+ return return_val;
+}
+
+static int
+ldif_back_search( Operation *op, SlapReply *rs )
+{
+ struct ldif_info *li = (struct ldif_info *) op->o_bd->be_private;
+
+ ldap_pvt_thread_rdwr_rlock(&li->li_rdwr);
+ rs->sr_err = search_tree( op, rs );
+ ldap_pvt_thread_rdwr_runlock(&li->li_rdwr);
+ rs->sr_ctrls = NULL;
+ send_ldap_result(op, rs);
+
+ return rs->sr_err;
+}
+
+static int
+ldif_back_add( Operation *op, SlapReply *rs )
+{
+ struct ldif_info *li = (struct ldif_info *) op->o_bd->be_private;
+ Entry * e = op->ora_e;
+ struct berval path;
+ char *parentdir;
+ char textbuf[SLAP_TEXT_BUFLEN];
+ int rc;
+
+ Debug( LDAP_DEBUG_TRACE, "ldif_back_add: \"%s\"\n", e->e_dn );
+
+ rc = entry_schema_check( op, e, NULL, 0, 1, NULL,
+ &rs->sr_text, textbuf, sizeof( textbuf ) );
+ if ( rc != LDAP_SUCCESS )
+ goto send_res;
+
+ rc = slap_add_opattrs( op, &rs->sr_text, textbuf, sizeof( textbuf ), 1 );
+ if ( rc != LDAP_SUCCESS )
+ goto send_res;
+
+ ldap_pvt_thread_mutex_lock( &li->li_modop_mutex );
+
+ rc = ldif_prepare_create( op, e, &path, &parentdir, &rs->sr_text );
+ if ( rc == LDAP_SUCCESS ) {
+ ldap_pvt_thread_rdwr_wlock( &li->li_rdwr );
+ rc = ldif_write_entry( op, e, &path, parentdir, &rs->sr_text );
+ ldap_pvt_thread_rdwr_wunlock( &li->li_rdwr );
+
+ SLAP_FREE( path.bv_val );
+ if ( parentdir != NULL )
+ SLAP_FREE( parentdir );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &li->li_modop_mutex );
+
+ send_res:
+ rs->sr_err = rc;
+ Debug( LDAP_DEBUG_TRACE, "ldif_back_add: err: %d text: %s\n",
+ rc, rs->sr_text ? rs->sr_text : "" );
+ send_ldap_result( op, rs );
+ slap_graduate_commit_csn( op );
+ rs->sr_text = NULL; /* remove possible pointer to textbuf */
+ return rs->sr_err;
+}
+
+static int
+ldif_back_modify( Operation *op, SlapReply *rs )
+{
+ struct ldif_info *li = (struct ldif_info *) op->o_bd->be_private;
+ Modifications * modlst = op->orm_modlist;
+ struct berval path;
+ Entry *entry;
+ char textbuf[SLAP_TEXT_BUFLEN];
+ int rc;
+
+ slap_mods_opattrs( op, &op->orm_modlist, 1 );
+
+ ldap_pvt_thread_mutex_lock( &li->li_modop_mutex );
+
+ rc = get_entry( op, &entry, &path, &rs->sr_text );
+ if ( rc == LDAP_SUCCESS ) {
+ rc = apply_modify_to_entry( entry, modlst, op, rs, textbuf );
+ if ( rc == LDAP_SUCCESS ) {
+ ldap_pvt_thread_rdwr_wlock( &li->li_rdwr );
+ rc = ldif_write_entry( op, entry, &path, NULL, &rs->sr_text );
+ ldap_pvt_thread_rdwr_wunlock( &li->li_rdwr );
+ }
+
+ entry_free( entry );
+ SLAP_FREE( path.bv_val );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &li->li_modop_mutex );
+
+ rs->sr_err = rc;
+ send_ldap_result( op, rs );
+ slap_graduate_commit_csn( op );
+ rs->sr_text = NULL; /* remove possible pointer to textbuf */
+ return rs->sr_err;
+}
+
+static int
+ldif_back_delete( Operation *op, SlapReply *rs )
+{
+ struct ldif_info *li = (struct ldif_info *) op->o_bd->be_private;
+ struct berval path;
+ int rc = LDAP_SUCCESS;
+ char ebuf[128];
+
+ if ( BER_BVISEMPTY( &op->o_csn )) {
+ struct berval csn;
+ char csnbuf[LDAP_PVT_CSNSTR_BUFSIZE];
+
+ csn.bv_val = csnbuf;
+ csn.bv_len = sizeof( csnbuf );
+ slap_get_csn( op, &csn, 1 );
+ }
+
+ ldap_pvt_thread_mutex_lock( &li->li_modop_mutex );
+ ldap_pvt_thread_rdwr_wlock( &li->li_rdwr );
+ if ( op->o_abandon ) {
+ rc = SLAPD_ABANDON;
+ goto done;
+ }
+
+ rc = ndn2path( op, &op->o_req_ndn, &path, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ ldif2dir_len( path );
+ ldif2dir_name( path );
+ if ( rmdir( path.bv_val ) < 0 ) {
+ switch ( errno ) {
+ case ENOTEMPTY:
+ rc = LDAP_NOT_ALLOWED_ON_NONLEAF;
+ break;
+ case ENOENT:
+ /* is leaf, go on */
+ break;
+ default:
+ rc = LDAP_OTHER;
+ rs->sr_text = "internal error (cannot delete subtree directory)";
+ break;
+ }
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ dir2ldif_name( path );
+ if ( unlink( path.bv_val ) < 0 ) {
+ rc = LDAP_NO_SUCH_OBJECT;
+ if ( errno != ENOENT ) {
+ rc = LDAP_OTHER;
+ rs->sr_text = "internal error (cannot delete entry file)";
+ }
+ }
+ }
+
+ if ( rc == LDAP_OTHER ) {
+ Debug( LDAP_DEBUG_ANY, "ldif_back_delete: %s \"%s\": %s\n",
+ "cannot delete", path.bv_val, AC_STRERROR_R( errno, ebuf, sizeof(ebuf) ) );
+ }
+
+ SLAP_FREE( path.bv_val );
+ done:
+ ldap_pvt_thread_rdwr_wunlock( &li->li_rdwr );
+ ldap_pvt_thread_mutex_unlock( &li->li_modop_mutex );
+ rs->sr_err = rc;
+ send_ldap_result( op, rs );
+ slap_graduate_commit_csn( op );
+ return rs->sr_err;
+}
+
+
+static int
+ldif_move_entry(
+ Operation *op,
+ Entry *entry,
+ int same_ndn,
+ struct berval *oldpath,
+ const char **text )
+{
+ struct ldif_info *li = (struct ldif_info *) op->o_bd->be_private;
+ struct berval newpath;
+ char *parentdir = NULL, *trash;
+ int rc, rename_res;
+ char ebuf[128];
+
+ if ( same_ndn ) {
+ rc = LDAP_SUCCESS;
+ newpath = *oldpath;
+ } else {
+ rc = ldif_prepare_create( op, entry, &newpath,
+ op->orr_newSup ? &parentdir : NULL, text );
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ ldap_pvt_thread_rdwr_wlock( &li->li_rdwr );
+
+ rc = ldif_write_entry( op, entry, &newpath, parentdir, text );
+ if ( rc == LDAP_SUCCESS && !same_ndn ) {
+ trash = oldpath->bv_val; /* will be .ldif file to delete */
+ ldif2dir_len( newpath );
+ ldif2dir_len( *oldpath );
+ /* Move subdir before deleting old entry,
+ * so .ldif always exists if subdir does.
+ */
+ ldif2dir_name( newpath );
+ ldif2dir_name( *oldpath );
+ rename_res = move_dir( oldpath->bv_val, newpath.bv_val );
+ if ( rename_res != 0 && errno != ENOENT ) {
+ rc = LDAP_OTHER;
+ *text = "internal error (cannot move this subtree)";
+ trash = newpath.bv_val;
+ }
+
+ /* Delete old entry, or if error undo change */
+ for (;;) {
+ dir2ldif_name( newpath );
+ dir2ldif_name( *oldpath );
+ if ( unlink( trash ) == 0 )
+ break;
+ if ( rc == LDAP_SUCCESS ) {
+ /* Prepare to undo change and return failure */
+ rc = LDAP_OTHER;
+ *text = "internal error (cannot move this entry)";
+ trash = newpath.bv_val;
+ if ( rename_res != 0 )
+ continue;
+ /* First move subdirectory back */
+ ldif2dir_name( newpath );
+ ldif2dir_name( *oldpath );
+ if ( move_dir( newpath.bv_val, oldpath->bv_val ) == 0 )
+ continue;
+ }
+ *text = "added new but couldn't delete old entry!";
+ break;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ANY,
+ "ldif_move_entry: %s (%s): \"%s\" -> \"%s\"\n",
+ *text, AC_STRERROR_R(errno, ebuf, sizeof(ebuf)),
+ op->o_req_dn.bv_val, entry->e_dn );
+ }
+ }
+
+ ldap_pvt_thread_rdwr_wunlock( &li->li_rdwr );
+ if ( !same_ndn )
+ SLAP_FREE( newpath.bv_val );
+ if ( parentdir != NULL )
+ SLAP_FREE( parentdir );
+ }
+
+ return rc;
+}
+
+static int
+ldif_back_modrdn( Operation *op, SlapReply *rs )
+{
+ struct ldif_info *li = (struct ldif_info *) op->o_bd->be_private;
+ struct berval new_dn = BER_BVNULL, new_ndn = BER_BVNULL;
+ struct berval p_dn, old_path;
+ Entry *entry;
+ char textbuf[SLAP_TEXT_BUFLEN];
+ int rc, same_ndn;
+
+ slap_mods_opattrs( op, &op->orr_modlist, 1 );
+
+ ldap_pvt_thread_mutex_lock( &li->li_modop_mutex );
+
+ rc = get_entry( op, &entry, &old_path, &rs->sr_text );
+ if ( rc == LDAP_SUCCESS ) {
+ /* build new dn, and new ndn for the entry */
+ if ( op->oq_modrdn.rs_newSup != NULL ) {
+ p_dn = *op->oq_modrdn.rs_newSup;
+ } else {
+ dnParent( &entry->e_name, &p_dn );
+ }
+ build_new_dn( &new_dn, &p_dn, &op->oq_modrdn.rs_newrdn, NULL );
+ dnNormalize( 0, NULL, NULL, &new_dn, &new_ndn, NULL );
+ same_ndn = !ber_bvcmp( &entry->e_nname, &new_ndn );
+ ber_memfree_x( entry->e_name.bv_val, NULL );
+ ber_memfree_x( entry->e_nname.bv_val, NULL );
+ entry->e_name = new_dn;
+ entry->e_nname = new_ndn;
+
+ /* perform the modifications */
+ rc = apply_modify_to_entry( entry, op->orr_modlist, op, rs, textbuf );
+ if ( rc == LDAP_SUCCESS )
+ rc = ldif_move_entry( op, entry, same_ndn, &old_path,
+ &rs->sr_text );
+
+ entry_free( entry );
+ SLAP_FREE( old_path.bv_val );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &li->li_modop_mutex );
+ rs->sr_err = rc;
+ send_ldap_result( op, rs );
+ slap_graduate_commit_csn( op );
+ rs->sr_text = NULL; /* remove possible pointer to textbuf */
+ return rs->sr_err;
+}
+
+
+/* Return LDAP_SUCCESS IFF we retrieve the specified entry. */
+static int
+ldif_back_entry_get(
+ Operation *op,
+ struct berval *ndn,
+ ObjectClass *oc,
+ AttributeDescription *at,
+ int rw,
+ Entry **e )
+{
+ struct ldif_info *li = (struct ldif_info *) op->o_bd->be_private;
+ struct berval op_dn = op->o_req_dn, op_ndn = op->o_req_ndn;
+ int rc;
+
+ assert( ndn != NULL );
+ assert( !BER_BVISNULL( ndn ) );
+
+ ldap_pvt_thread_rdwr_rlock( &li->li_rdwr );
+ op->o_req_dn = *ndn;
+ op->o_req_ndn = *ndn;
+ rc = get_entry( op, e, NULL, NULL );
+ op->o_req_dn = op_dn;
+ op->o_req_ndn = op_ndn;
+ ldap_pvt_thread_rdwr_runlock( &li->li_rdwr );
+
+ if ( rc == LDAP_SUCCESS && oc && !is_entry_objectclass_or_sub( *e, oc ) ) {
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ entry_free( *e );
+ *e = NULL;
+ }
+
+ return rc;
+}
+
+static int
+ldif_back_entry_release_rw (
+ Operation *op,
+ Entry *e,
+ int rw )
+{
+ ID id = e->e_id;
+
+ /* only tool mode assigns valid IDs */
+ if ( id != 0 && id != NOID )
+ {
+ struct ldif_tool *tl = &((struct ldif_info *) op->o_bd->be_private)->li_tool;
+
+ id--;
+
+ assert( id < tl->ecount );
+ assert( e == tl->entries[id] );
+ tl->entries[id] = NULL;
+ }
+
+ entry_free( e );
+ return 0;
+}
+
+
+/* Slap tools */
+
+static int
+ldif_tool_entry_open( BackendDB *be, int mode )
+{
+ struct ldif_tool *tl = &((struct ldif_info *) be->be_private)->li_tool;
+
+ tl->ecurrent = 0;
+ return 0;
+}
+
+static int
+ldif_tool_entry_close( BackendDB *be )
+{
+ struct ldif_tool *tl = &((struct ldif_info *) be->be_private)->li_tool;
+ Entry **entries = tl->entries;
+ ID i;
+
+ for ( i = tl->ecount; i--; )
+ if ( entries[i] )
+ entry_free( entries[i] );
+ SLAP_FREE( entries );
+ tl->entries = NULL;
+ tl->ecount = tl->elen = 0;
+ return 0;
+}
+
+static ID
+ldif_tool_entry_next( BackendDB *be )
+{
+ struct ldif_tool *tl = &((struct ldif_info *) be->be_private)->li_tool;
+
+ do {
+ Entry *e = tl->entries[ tl->ecurrent ];
+
+ if ( tl->ecurrent >= tl->ecount ) {
+ return NOID;
+ }
+
+ ++tl->ecurrent;
+
+ if ( tl->tl_base && !dnIsSuffixScope( &e->e_nname, tl->tl_base, tl->tl_scope ) ) {
+ continue;
+ }
+
+ if ( tl->tl_filter && test_filter( NULL, e, tl->tl_filter ) != LDAP_COMPARE_TRUE ) {
+ continue;
+ }
+
+ break;
+ } while ( 1 );
+
+ return tl->ecurrent;
+}
+
+static ID
+ldif_tool_entry_first_x( BackendDB *be, struct berval *base, int scope, Filter *f )
+{
+ struct ldif_tool *tl = &((struct ldif_info *) be->be_private)->li_tool;
+
+ tl->tl_base = base;
+ tl->tl_scope = scope;
+ tl->tl_filter = f;
+
+ if ( tl->entries == NULL ) {
+ Operation op = {0};
+
+ op.o_bd = be;
+ op.o_req_dn = *be->be_suffix;
+ op.o_req_ndn = *be->be_nsuffix;
+ op.ors_scope = LDAP_SCOPE_SUBTREE;
+ if ( search_tree( &op, NULL ) != LDAP_SUCCESS ) {
+ tl->ecurrent = tl->ecount; /* fail ldif_tool_entry_next() */
+ return NOID; /* fail ldif_tool_entry_get() */
+ }
+ }
+ return ldif_tool_entry_next( be );
+}
+
+static ID
+ldif_tool_dn2id_get( BackendDB *be, struct berval *dn )
+{
+ struct ldif_tool *tl = &((struct ldif_info *) be->be_private)->li_tool;
+
+ Operation op = {0};
+
+ op.o_bd = be;
+ op.o_req_dn = *dn;
+ op.o_req_ndn = *dn;
+ op.ors_scope = LDAP_SCOPE_BASE;
+ if ( search_tree( &op, NULL ) != LDAP_SUCCESS ) {
+ return NOID;
+ }
+ return tl->ecount;
+}
+
+static Entry *
+ldif_tool_entry_get( BackendDB *be, ID id )
+{
+ struct ldif_tool *tl = &((struct ldif_info *) be->be_private)->li_tool;
+ Entry *e = NULL;
+
+ --id;
+ if ( id < tl->ecount ) {
+ e = tl->entries[id];
+ }
+ return e;
+}
+
+static ID
+ldif_tool_entry_put( BackendDB *be, Entry *e, struct berval *text )
+{
+ int rc;
+ const char *errmsg = NULL;
+ struct berval path;
+ char *parentdir;
+ Operation op = {0};
+
+ op.o_bd = be;
+ rc = ldif_prepare_create( &op, e, &path, &parentdir, &errmsg );
+ if ( rc == LDAP_SUCCESS ) {
+ rc = ldif_write_entry( &op, e, &path, parentdir, &errmsg );
+
+ SLAP_FREE( path.bv_val );
+ if ( parentdir != NULL )
+ SLAP_FREE( parentdir );
+ if ( rc == LDAP_SUCCESS )
+ return 1;
+ }
+
+ if ( errmsg == NULL && rc != LDAP_OTHER )
+ errmsg = ldap_err2string( rc );
+ if ( errmsg != NULL )
+ snprintf( text->bv_val, text->bv_len, "%s", errmsg );
+ return NOID;
+}
+
+static ID
+ldif_tool_entry_modify( BackendDB *be, Entry *e, struct berval *text )
+{
+ int rc;
+ const char *errmsg = NULL;
+ struct berval path;
+ Operation op = {0};
+
+ op.o_bd = be;
+ ndn2path( &op, &e->e_nname, &path, 0 );
+ rc = ldif_write_entry( &op, e, &path, NULL, &errmsg );
+ SLAP_FREE( path.bv_val );
+ if ( rc == LDAP_SUCCESS )
+ return 1;
+
+ if ( errmsg == NULL && rc != LDAP_OTHER )
+ errmsg = ldap_err2string( rc );
+ if ( errmsg != NULL )
+ snprintf( text->bv_val, text->bv_len, "%s", errmsg );
+ return NOID;
+}
+
+static int
+ldif_tool_entry_delete( BackendDB *be, struct berval *ndn, struct berval *text )
+{
+ int rc = LDAP_SUCCESS;
+ const char *errmsg = NULL;
+ struct berval path;
+ Operation op = {0};
+
+ op.o_bd = be;
+ ndn2path( &op, ndn, &path, 0 );
+
+ ldif2dir_len( path );
+ ldif2dir_name( path );
+ if ( rmdir( path.bv_val ) < 0 ) {
+ switch ( errno ) {
+ case ENOTEMPTY:
+ rc = LDAP_NOT_ALLOWED_ON_NONLEAF;
+ break;
+ case ENOENT:
+ /* is leaf, go on */
+ break;
+ default:
+ rc = LDAP_OTHER;
+ errmsg = "internal error (cannot delete subtree directory)";
+ break;
+ }
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ dir2ldif_name( path );
+ if ( unlink( path.bv_val ) < 0 ) {
+ rc = LDAP_NO_SUCH_OBJECT;
+ if ( errno != ENOENT ) {
+ rc = LDAP_OTHER;
+ errmsg = "internal error (cannot delete entry file)";
+ }
+ }
+ }
+
+ SLAP_FREE( path.bv_val );
+
+ if ( errmsg == NULL && rc != LDAP_OTHER )
+ errmsg = ldap_err2string( rc );
+ if ( errmsg != NULL )
+ snprintf( text->bv_val, text->bv_len, "%s", errmsg );
+ return rc;
+}
+
+
+/* Setup */
+
+static int
+ldif_back_db_init( BackendDB *be, ConfigReply *cr )
+{
+ struct ldif_info *li;
+
+ li = ch_calloc( 1, sizeof(struct ldif_info) );
+ be->be_private = li;
+ be->be_cf_ocs = ldifocs;
+ ldap_pvt_thread_mutex_init( &li->li_modop_mutex );
+ ldap_pvt_thread_rdwr_init( &li->li_rdwr );
+ SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_ONE_SUFFIX;
+ return 0;
+}
+
+static int
+ldif_back_db_destroy( Backend *be, ConfigReply *cr )
+{
+ struct ldif_info *li = be->be_private;
+
+ ch_free( li->li_base_path.bv_val );
+ ldap_pvt_thread_rdwr_destroy( &li->li_rdwr );
+ ldap_pvt_thread_mutex_destroy( &li->li_modop_mutex );
+ free( be->be_private );
+ return 0;
+}
+
+static int
+ldif_back_db_open( Backend *be, ConfigReply *cr )
+{
+ struct ldif_info *li = (struct ldif_info *) be->be_private;
+ if( BER_BVISEMPTY(&li->li_base_path)) {/* missing base path */
+ Debug( LDAP_DEBUG_ANY, "missing base path for back-ldif\n" );
+ return 1;
+ }
+ return 0;
+}
+
+int
+ldif_back_initialize( BackendInfo *bi )
+{
+ static char *controls[] = {
+ LDAP_CONTROL_MANAGEDSAIT,
+ NULL
+ };
+ int rc;
+
+ bi->bi_flags |=
+ SLAP_BFLAG_INCREMENT |
+ SLAP_BFLAG_REFERRALS;
+
+ bi->bi_controls = controls;
+
+ bi->bi_open = 0;
+ bi->bi_close = 0;
+ bi->bi_config = 0;
+ bi->bi_destroy = 0;
+
+ bi->bi_db_init = ldif_back_db_init;
+ bi->bi_db_config = config_generic_wrapper;
+ bi->bi_db_open = ldif_back_db_open;
+ bi->bi_db_close = 0;
+ bi->bi_db_destroy = ldif_back_db_destroy;
+
+ bi->bi_op_bind = ldif_back_bind;
+ bi->bi_op_unbind = 0;
+ bi->bi_op_search = ldif_back_search;
+ bi->bi_op_compare = 0;
+ bi->bi_op_modify = ldif_back_modify;
+ bi->bi_op_modrdn = ldif_back_modrdn;
+ bi->bi_op_add = ldif_back_add;
+ bi->bi_op_delete = ldif_back_delete;
+ bi->bi_op_abandon = 0;
+
+ bi->bi_extended = 0;
+
+ bi->bi_chk_referrals = ldif_back_referrals;
+
+ bi->bi_connection_init = 0;
+ bi->bi_connection_destroy = 0;
+
+ bi->bi_entry_get_rw = ldif_back_entry_get;
+ bi->bi_entry_release_rw = ldif_back_entry_release_rw;
+
+#if 0 /* NOTE: uncomment to completely disable access control */
+ bi->bi_access_allowed = slap_access_always_allowed;
+#endif
+
+ bi->bi_tool_entry_open = ldif_tool_entry_open;
+ bi->bi_tool_entry_close = ldif_tool_entry_close;
+ bi->bi_tool_entry_first = backend_tool_entry_first;
+ bi->bi_tool_entry_first_x = ldif_tool_entry_first_x;
+ bi->bi_tool_entry_next = ldif_tool_entry_next;
+ bi->bi_tool_dn2id_get = ldif_tool_dn2id_get;
+ bi->bi_tool_entry_get = ldif_tool_entry_get;
+ bi->bi_tool_entry_put = ldif_tool_entry_put;
+ bi->bi_tool_entry_modify = ldif_tool_entry_modify;
+ bi->bi_tool_entry_delete = ldif_tool_entry_delete;
+ bi->bi_tool_entry_reindex = 0;
+ bi->bi_tool_sync = 0;
+
+ bi->bi_cf_ocs = ldifocs;
+
+ rc = config_register_schema( ldifcfg, ldifocs );
+ if ( rc ) return rc;
+ return 0;
+}
diff --git a/servers/slapd/back-mdb/Makefile.in b/servers/slapd/back-mdb/Makefile.in
new file mode 100644
index 0000000..ad38048
--- /dev/null
+++ b/servers/slapd/back-mdb/Makefile.in
@@ -0,0 +1,62 @@
+# Makefile.in for back-mdb
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2011-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SRCS = init.c tools.c config.c \
+ add.c bind.c compare.c delete.c modify.c modrdn.c search.c \
+ extended.c operational.c \
+ attr.c index.c key.c filterindex.c \
+ dn2entry.c dn2id.c id2entry.c idl.c \
+ nextid.c monitor.c
+
+OBJS = init.lo tools.lo config.lo \
+ add.lo bind.lo compare.lo delete.lo modify.lo modrdn.lo search.lo \
+ extended.lo operational.lo \
+ attr.lo index.lo key.lo filterindex.lo \
+ dn2entry.lo dn2id.lo id2entry.lo idl.lo \
+ nextid.lo monitor.lo mdb.lo midl.lo
+
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+MDB_SUBDIR = $(srcdir)/$(LDAP_LIBDIR)/liblmdb
+
+BUILD_OPT = "--enable-mdb"
+BUILD_MOD = @BUILD_MDB@
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = $(@BUILD_MDB@_DEFS)
+MOD_LIBS = $(MDB_LIBS)
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+
+LIBBASE = back_mdb
+
+XINCPATH = -I.. -I$(srcdir)/.. -I$(MDB_SUBDIR)
+XDEFS = $(MODULES_CPPFLAGS)
+
+all-local-lib: ../.backend
+
+../.backend: lib$(LIBBASE).a
+ @touch $@
+
+mdb.lo: $(MDB_SUBDIR)/mdb.c
+ $(LTCOMPILE_MOD) $(MDB_SUBDIR)/mdb.c
+
+midl.lo: $(MDB_SUBDIR)/midl.c
+ $(LTCOMPILE_MOD) $(MDB_SUBDIR)/midl.c
+
+veryclean-local-lib: FORCE
+ $(RM) $(XXHEADERS) $(XXSRCS) .links
diff --git a/servers/slapd/back-mdb/add.c b/servers/slapd/back-mdb/add.c
new file mode 100644
index 0000000..f1632e2
--- /dev/null
+++ b/servers/slapd/back-mdb/add.c
@@ -0,0 +1,419 @@
+/* add.c - ldap mdb back-end add routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+
+int
+mdb_add(Operation *op, SlapReply *rs )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ struct berval pdn;
+ Entry *p = NULL, *oe = op->ora_e;
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof textbuf;
+ AttributeDescription *children = slap_schema.si_ad_children;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+ MDB_txn *txn = NULL;
+ MDB_cursor *mc = NULL;
+ MDB_cursor *mcd;
+ ID eid, pid = 0;
+ mdb_op_info opinfo = {{{ 0 }}}, *moi = &opinfo;
+ int subentry;
+ int numads = mdb->mi_numads;
+
+ int success;
+
+ LDAPControl **postread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+
+ Debug(LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(mdb_add) ": %s\n",
+ op->ora_e->e_name.bv_val );
+
+ ctrls[num_ctrls] = 0;
+
+ /* check entry's schema */
+ rs->sr_err = entry_schema_check( op, op->ora_e, NULL,
+ get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": entry failed schema check: "
+ "%s (%d)\n", rs->sr_text, rs->sr_err );
+ goto return_results;
+ }
+
+ /* begin transaction */
+ rs->sr_err = mdb_opinfo_get( op, mdb, 0, &moi );
+ rs->sr_text = NULL;
+ if( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": txn_begin failed: %s (%d)\n",
+ mdb_strerror(rs->sr_err), rs->sr_err );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ txn = moi->moi_txn;
+
+ /* add opattrs to shadow as well, only missing attrs will actually
+ * be added; helps compatibility with older OL versions */
+ rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": entry failed op attrs add: "
+ "%s (%d)\n", rs->sr_text, rs->sr_err );
+ goto return_results;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, op->ora_e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+ subentry = is_entry_subentry( op->ora_e );
+
+ /*
+ * Get the parent dn and see if the corresponding entry exists.
+ */
+ if ( be_issuffix( op->o_bd, &op->ora_e->e_nname ) ) {
+ pdn = slap_empty_bv;
+ } else {
+ dnParent( &op->ora_e->e_nname, &pdn );
+ }
+
+ rs->sr_err = mdb_cursor_open( txn, mdb->mi_dn2id, &mcd );
+ if( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": mdb_cursor_open failed (%d)\n",
+ rs->sr_err );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* get entry or parent */
+ rs->sr_err = mdb_dn2entry( op, txn, mcd, &op->ora_e->e_nname, &p, NULL, 1 );
+ switch( rs->sr_err ) {
+ case 0:
+ rs->sr_err = LDAP_ALREADY_EXISTS;
+ mdb_entry_return( op, p );
+ p = NULL;
+ goto return_results;
+ case MDB_NOTFOUND:
+ break;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ if ( !p )
+ p = (Entry *)&slap_entry_root;
+
+ if ( !bvmatch( &pdn, &p->e_nname ) ) {
+ rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
+ op->o_tmpmemctx );
+ if ( p != (Entry *)&slap_entry_root && is_entry_referral( p )) {
+ BerVarray ref = get_entry_referrals( op, p );
+ rs->sr_ref = referral_rewrite( ref, &p->e_name,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ } else {
+ rs->sr_ref = NULL;
+ }
+ if ( p != (Entry *)&slap_entry_root )
+ mdb_entry_return( op, p );
+ p = NULL;
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": parent "
+ "does not exist\n" );
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+
+ rs->sr_err = access_allowed( op, p,
+ children, NULL, ACL_WADD, NULL );
+
+ if ( ! rs->sr_err ) {
+ if ( p != (Entry *)&slap_entry_root )
+ mdb_entry_return( op, p );
+ p = NULL;
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": no write access to parent\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to parent";
+ goto return_results;;
+ }
+
+ if ( p != (Entry *)&slap_entry_root ) {
+ if ( is_entry_subentry( p ) ) {
+ mdb_entry_return( op, p );
+ p = NULL;
+ /* parent is a subentry, don't allow add */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": parent is subentry\n" );
+ rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
+ rs->sr_text = "parent is a subentry";
+ goto return_results;;
+ }
+
+ if ( is_entry_alias( p ) ) {
+ mdb_entry_return( op, p );
+ p = NULL;
+ /* parent is an alias, don't allow add */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": parent is alias\n" );
+ rs->sr_err = LDAP_ALIAS_PROBLEM;
+ rs->sr_text = "parent is an alias";
+ goto return_results;;
+ }
+
+ if ( is_entry_referral( p ) ) {
+ BerVarray ref = get_entry_referrals( op, p );
+ /* parent is a referral, don't allow add */
+ rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
+ op->o_tmpmemctx );
+ rs->sr_ref = referral_rewrite( ref, &p->e_name,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ mdb_entry_return( op, p );
+ p = NULL;
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": parent is referral\n" );
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+
+ }
+
+ if ( subentry ) {
+ /* FIXME: */
+ /* parent must be an administrative point of the required kind */
+ }
+
+ /* free parent */
+ if ( p != (Entry *)&slap_entry_root ) {
+ pid = p->e_id;
+ if ( p->e_nname.bv_len ) {
+ struct berval ppdn;
+
+ /* ITS#5326: use parent's DN if differs from provided one */
+ dnParent( &op->ora_e->e_name, &ppdn );
+ if ( !dn_match( &p->e_name, &ppdn ) ) {
+ struct berval rdn;
+ struct berval newdn;
+
+ dnRdn( &op->ora_e->e_name, &rdn );
+
+ build_new_dn( &newdn, &p->e_name, &rdn, NULL );
+ if ( op->ora_e->e_name.bv_val != op->o_req_dn.bv_val )
+ ber_memfree( op->ora_e->e_name.bv_val );
+ op->ora_e->e_name = newdn;
+
+ /* FIXME: should check whether
+ * dnNormalize(newdn) == e->e_nname ... */
+ }
+ }
+
+ mdb_entry_return( op, p );
+ }
+ p = NULL;
+
+ rs->sr_err = access_allowed( op, op->ora_e,
+ entry, NULL, ACL_WADD, NULL );
+
+ if ( ! rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": no write access to entry\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to entry";
+ goto return_results;;
+ }
+
+ /*
+ * Check ACL for attribute write access
+ */
+ if (!acl_check_modlist(op, oe, op->ora_modlist)) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": no write access to attribute\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to attribute";
+ goto return_results;;
+ }
+
+ rs->sr_err = mdb_cursor_open( txn, mdb->mi_id2entry, &mc );
+ if( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": mdb_cursor_open failed (%d)\n",
+ rs->sr_err );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ rs->sr_err = mdb_next_id( op->o_bd, mc, &eid );
+ if( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": next_id failed (%d)\n",
+ rs->sr_err );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ op->ora_e->e_id = eid;
+
+ /* dn2id index */
+ rs->sr_err = mdb_dn2id_add( op, mcd, mcd, pid, 1, 1, op->ora_e );
+ mdb_cursor_close( mcd );
+ if ( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": dn2id_add failed: %s (%d)\n",
+ mdb_strerror(rs->sr_err), rs->sr_err );
+
+ switch( rs->sr_err ) {
+ case MDB_KEYEXIST:
+ rs->sr_err = LDAP_ALREADY_EXISTS;
+ break;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ }
+ goto return_results;
+ }
+
+ /* attribute indexes */
+ rs->sr_err = mdb_index_entry_add( op, txn, op->ora_e );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": index_entry_add failed\n" );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "index generation failed";
+ goto return_results;
+ }
+
+ /* id2entry index */
+ rs->sr_err = mdb_id2entry_add( op, txn, mc, op->ora_e );
+ if ( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": id2entry_add failed\n" );
+ if ( rs->sr_err == LDAP_ADMINLIMIT_EXCEEDED ) {
+ rs->sr_text = "entry is too big";
+ } else {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "entry store failed";
+ }
+ goto return_results;
+ }
+
+ /* post-read */
+ if( op->o_postread ) {
+ if( postread_ctrl == NULL ) {
+ postread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if ( slap_read_controls( op, rs, op->ora_e,
+ &slap_post_read_bv, postread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_add) ": post-read "
+ "failed!\n" );
+ if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ if ( moi == &opinfo ) {
+ LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
+ opinfo.moi_oe.oe_key = NULL;
+ if ( op->o_noop ) {
+ mdb->mi_numads = numads;
+ mdb_txn_abort( txn );
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ txn = NULL;
+ goto return_results;
+ }
+
+ rs->sr_err = mdb_txn_commit( txn );
+ txn = NULL;
+ if ( rs->sr_err != 0 ) {
+ mdb->mi_numads = numads;
+ rs->sr_text = "txn_commit failed";
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_add) ": %s : %s (%d)\n",
+ rs->sr_text, mdb_strerror(rs->sr_err), rs->sr_err );
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_add) ": added%s id=%08lx dn=\"%s\"\n",
+ op->o_noop ? " (no-op)" : "",
+ op->ora_e->e_id, op->ora_e->e_dn );
+
+ rs->sr_text = NULL;
+ if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+ success = rs->sr_err;
+ send_ldap_result( op, rs );
+
+ if( moi == &opinfo ) {
+ if( txn != NULL ) {
+ mdb->mi_numads = numads;
+ mdb_txn_abort( txn );
+ }
+ if ( opinfo.moi_oe.oe_key ) {
+ LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
+ }
+ } else {
+ moi->moi_ref--;
+ }
+
+ if( success == LDAP_SUCCESS ) {
+#if 0
+ if ( mdb->bi_txn_cp_kbyte ) {
+ TXN_CHECKPOINT( mdb->bi_dbenv,
+ mdb->bi_txn_cp_kbyte, mdb->bi_txn_cp_min, 0 );
+ }
+#endif
+ }
+
+ slap_graduate_commit_csn( op );
+
+ if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
+ slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
+ }
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-mdb/attr.c b/servers/slapd/back-mdb/attr.c
new file mode 100644
index 0000000..9567fb5
--- /dev/null
+++ b/servers/slapd/back-mdb/attr.c
@@ -0,0 +1,824 @@
+/* attr.c - backend routines for dealing with attributes */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-mdb.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+/* Find the ad, return -1 if not found,
+ * set point for insertion if ins is non-NULL
+ */
+int
+mdb_attr_slot( struct mdb_info *mdb, AttributeDescription *ad, int *ins )
+{
+ unsigned base = 0, cursor = 0;
+ unsigned n = mdb->mi_nattrs;
+ int val = 0;
+
+ while ( 0 < n ) {
+ unsigned pivot = n >> 1;
+ cursor = base + pivot;
+
+ val = SLAP_PTRCMP( ad, mdb->mi_attrs[cursor]->ai_desc );
+ if ( val < 0 ) {
+ n = pivot;
+ } else if ( val > 0 ) {
+ base = cursor + 1;
+ n -= pivot + 1;
+ } else {
+ return cursor;
+ }
+ }
+ if ( ins ) {
+ if ( val > 0 )
+ ++cursor;
+ *ins = cursor;
+ }
+ return -1;
+}
+
+static int
+ainfo_insert( struct mdb_info *mdb, AttrInfo *a )
+{
+ int x;
+ int i = mdb_attr_slot( mdb, a->ai_desc, &x );
+
+ /* Is it a dup? */
+ if ( i >= 0 )
+ return -1;
+
+ mdb->mi_attrs = ch_realloc( mdb->mi_attrs, ( mdb->mi_nattrs+1 ) *
+ sizeof( AttrInfo * ));
+ if ( x < mdb->mi_nattrs )
+ AC_MEMCPY( &mdb->mi_attrs[x+1], &mdb->mi_attrs[x],
+ ( mdb->mi_nattrs - x ) * sizeof( AttrInfo *));
+ mdb->mi_attrs[x] = a;
+ mdb->mi_nattrs++;
+ return 0;
+}
+
+AttrInfo *
+mdb_attr_mask(
+ struct mdb_info *mdb,
+ AttributeDescription *desc )
+{
+ int i = mdb_attr_slot( mdb, desc, NULL );
+ return i < 0 ? NULL : mdb->mi_attrs[i];
+}
+
+/* Open all un-opened index DB handles */
+int
+mdb_attr_dbs_open(
+ BackendDB *be, MDB_txn *tx0, ConfigReply *cr )
+{
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+ MDB_txn *txn;
+ MDB_dbi *dbis = NULL;
+ int i, flags;
+ int rc;
+
+ txn = tx0;
+ if ( txn == NULL ) {
+ rc = mdb_txn_begin( mdb->mi_dbenv, NULL, 0, &txn );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
+ "txn_begin failed: %s (%d).",
+ be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
+ cr->msg );
+ return rc;
+ }
+ dbis = ch_calloc( 1, mdb->mi_nattrs * sizeof(MDB_dbi) );
+ } else {
+ rc = 0;
+ }
+
+ flags = MDB_DUPSORT|MDB_DUPFIXED|MDB_INTEGERDUP;
+ if ( !(slapMode & SLAP_TOOL_READONLY) )
+ flags |= MDB_CREATE;
+
+ for ( i=0; i<mdb->mi_nattrs; i++ ) {
+ if ( mdb->mi_attrs[i]->ai_dbi ) /* already open */
+ continue;
+ if ( !( mdb->mi_attrs[i]->ai_indexmask || mdb->mi_attrs[i]->ai_newmask )) /* not an index record */
+ continue;
+ rc = mdb_dbi_open( txn, mdb->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val,
+ flags, &mdb->mi_attrs[i]->ai_dbi );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
+ "mdb_dbi_open(%s) failed: %s (%d).",
+ be->be_suffix[0].bv_val,
+ mdb->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val,
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
+ cr->msg );
+ break;
+ }
+ /* Remember newly opened DBI handles */
+ if ( dbis )
+ dbis[i] = mdb->mi_attrs[i]->ai_dbi;
+ }
+
+ /* Only commit if this is our txn */
+ if ( tx0 == NULL ) {
+ if ( !rc ) {
+ rc = mdb_txn_commit( txn );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
+ "txn_commit failed: %s (%d).",
+ be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
+ cr->msg );
+ }
+ } else {
+ mdb_txn_abort( txn );
+ }
+ /* Something failed, forget anything we just opened */
+ if ( rc ) {
+ for ( i=0; i<mdb->mi_nattrs; i++ ) {
+ if ( dbis[i] ) {
+ mdb->mi_attrs[i]->ai_dbi = 0;
+ mdb->mi_attrs[i]->ai_indexmask |= MDB_INDEX_DELETING;
+ }
+ }
+ mdb_attr_flush( mdb );
+ }
+ ch_free( dbis );
+ }
+
+ return rc;
+}
+
+void
+mdb_attr_dbs_close(
+ struct mdb_info *mdb
+)
+{
+ int i;
+ for ( i=0; i<mdb->mi_nattrs; i++ )
+ if ( mdb->mi_attrs[i]->ai_dbi ) {
+ mdb_dbi_close( mdb->mi_dbenv, mdb->mi_attrs[i]->ai_dbi );
+ mdb->mi_attrs[i]->ai_dbi = 0;
+ }
+}
+
+int
+mdb_attr_index_config(
+ struct mdb_info *mdb,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv,
+ struct config_reply_s *c_reply)
+{
+ int rc = 0;
+ int i;
+ slap_mask_t mask;
+ char **attrs;
+ char **indexes = NULL;
+
+ attrs = ldap_str2charray( argv[0], "," );
+
+ if( attrs == NULL ) {
+ fprintf( stderr, "%s: line %d: "
+ "no attributes specified: %s\n",
+ fname, lineno, argv[0] );
+ return LDAP_PARAM_ERROR;
+ }
+
+ if ( argc > 1 ) {
+ indexes = ldap_str2charray( argv[1], "," );
+
+ if( indexes == NULL ) {
+ fprintf( stderr, "%s: line %d: "
+ "no indexes specified: %s\n",
+ fname, lineno, argv[1] );
+ rc = LDAP_PARAM_ERROR;
+ goto done;
+ }
+ }
+
+ if( indexes == NULL ) {
+ mask = mdb->mi_defaultmask;
+
+ } else {
+ mask = 0;
+
+ for ( i = 0; indexes[i] != NULL; i++ ) {
+ slap_mask_t index;
+ rc = slap_str2index( indexes[i], &index );
+
+ if( rc != LDAP_SUCCESS ) {
+ if ( c_reply )
+ {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "index type \"%s\" undefined", indexes[i] );
+
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ rc = LDAP_PARAM_ERROR;
+ goto done;
+ }
+
+ mask |= index;
+ }
+ }
+
+ if( !mask ) {
+ if ( c_reply )
+ {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "no indexes selected" );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ rc = LDAP_PARAM_ERROR;
+ goto done;
+ }
+
+ for ( i = 0; attrs[i] != NULL; i++ ) {
+ AttrInfo *a;
+ AttributeDescription *ad;
+ const char *text;
+#ifdef LDAP_COMP_MATCH
+ ComponentReference* cr = NULL;
+ AttrInfo *a_cr = NULL;
+#endif
+
+ if( strcasecmp( attrs[i], "default" ) == 0 ) {
+ mdb->mi_defaultmask |= mask;
+ continue;
+ }
+
+#ifdef LDAP_COMP_MATCH
+ if ( is_component_reference( attrs[i] ) ) {
+ rc = extract_component_reference( attrs[i], &cr );
+ if ( rc != LDAP_SUCCESS ) {
+ if ( c_reply )
+ {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "index component reference\"%s\" undefined",
+ attrs[i] );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ goto done;
+ }
+ cr->cr_indexmask = mask;
+ /*
+ * After extracting a component reference
+ * only the name of a attribute will be remaining
+ */
+ } else {
+ cr = NULL;
+ }
+#endif
+ ad = NULL;
+ rc = slap_str2ad( attrs[i], &ad, &text );
+
+ if( rc != LDAP_SUCCESS ) {
+ if ( c_reply )
+ {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "index attribute \"%s\" undefined",
+ attrs[i] );
+
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+fail:
+#ifdef LDAP_COMP_MATCH
+ ch_free( cr );
+#endif
+ goto done;
+ }
+
+ if( ad == slap_schema.si_ad_entryDN || slap_ad_is_binary( ad ) ) {
+ if (c_reply) {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "index of attribute \"%s\" disallowed", attrs[i] );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto fail;
+ }
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) && !(
+ ad->ad_type->sat_approx
+ && ad->ad_type->sat_approx->smr_indexer
+ && ad->ad_type->sat_approx->smr_filter ) )
+ {
+ if (c_reply) {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "approx index of attribute \"%s\" disallowed", attrs[i] );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ rc = LDAP_INAPPROPRIATE_MATCHING;
+ goto fail;
+ }
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) && !(
+ ad->ad_type->sat_equality
+ && ad->ad_type->sat_equality->smr_indexer
+ && ad->ad_type->sat_equality->smr_filter ) )
+ {
+ if (c_reply) {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "equality index of attribute \"%s\" disallowed", attrs[i] );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ rc = LDAP_INAPPROPRIATE_MATCHING;
+ goto fail;
+ }
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) && !(
+ ad->ad_type->sat_substr
+ && ad->ad_type->sat_substr->smr_indexer
+ && ad->ad_type->sat_substr->smr_filter ) )
+ {
+ if (c_reply) {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "substr index of attribute \"%s\" disallowed", attrs[i] );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ rc = LDAP_INAPPROPRIATE_MATCHING;
+ goto fail;
+ }
+
+ Debug( LDAP_DEBUG_CONFIG, "index %s 0x%04lx\n",
+ ad->ad_cname.bv_val, mask );
+
+ a = (AttrInfo *) ch_malloc( sizeof(AttrInfo) );
+
+#ifdef LDAP_COMP_MATCH
+ a->ai_cr = NULL;
+#endif
+ a->ai_cursor = NULL;
+ a->ai_root = NULL;
+ a->ai_desc = ad;
+ a->ai_dbi = 0;
+ a->ai_multi_hi = UINT_MAX;
+ a->ai_multi_lo = UINT_MAX;
+
+ if ( mdb->mi_flags & MDB_IS_OPEN ) {
+ a->ai_indexmask = 0;
+ a->ai_newmask = mask;
+ } else {
+ a->ai_indexmask = mask;
+ a->ai_newmask = 0;
+ }
+
+#ifdef LDAP_COMP_MATCH
+ if ( cr ) {
+ a_cr = mdb_attr_mask( mdb, ad );
+ if ( a_cr ) {
+ /*
+ * AttrInfo is already in AVL
+ * just add the extracted component reference
+ * in the AttrInfo
+ */
+ ch_free( a );
+ rc = insert_component_reference( cr, &a_cr->ai_cr );
+ if ( rc != LDAP_SUCCESS) {
+ fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
+ rc = LDAP_PARAM_ERROR;
+ goto fail;
+ }
+ continue;
+ } else {
+ rc = insert_component_reference( cr, &a->ai_cr );
+ if ( rc != LDAP_SUCCESS) {
+ fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
+ rc = LDAP_PARAM_ERROR;
+ ch_free( a );
+ goto fail;
+ }
+ }
+ }
+#endif
+ rc = ainfo_insert( mdb, a );
+ if( rc ) {
+ AttrInfo *b = mdb_attr_mask( mdb, ad );
+ /* If this is just a multival record, reuse it for index info */
+ if ( !( b->ai_indexmask || b->ai_newmask ) && b->ai_multi_lo < UINT_MAX ) {
+ b->ai_indexmask = a->ai_indexmask;
+ b->ai_newmask = a->ai_newmask;
+ ch_free( a );
+ rc = 0;
+ continue;
+ }
+ if ( mdb->mi_flags & MDB_IS_OPEN ) {
+ /* If there is already an index defined for this attribute
+ * it must be replaced. Otherwise we end up with multiple
+ * olcIndex values for the same attribute */
+ if ( b->ai_indexmask & MDB_INDEX_DELETING ) {
+ /* If we were editing this attr, reset it */
+ b->ai_indexmask &= ~MDB_INDEX_DELETING;
+ /* If this is leftover from a previous add, commit it */
+ if ( b->ai_newmask )
+ b->ai_indexmask = b->ai_newmask;
+ b->ai_newmask = a->ai_newmask;
+ ch_free( a );
+ rc = 0;
+ continue;
+ }
+ }
+ if (c_reply) {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "duplicate index definition for attr \"%s\"",
+ attrs[i] );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+
+ rc = LDAP_PARAM_ERROR;
+ goto done;
+ }
+ }
+
+done:
+ ldap_charray_free( attrs );
+ if ( indexes != NULL ) ldap_charray_free( indexes );
+
+ return rc;
+}
+
+static int
+mdb_attr_index_unparser( void *v1, void *v2 )
+{
+ AttrInfo *ai = v1;
+ BerVarray *bva = v2;
+ struct berval bv;
+ char *ptr;
+
+ slap_index2bvlen( ai->ai_indexmask, &bv );
+ if ( bv.bv_len ) {
+ bv.bv_len += ai->ai_desc->ad_cname.bv_len + 1;
+ ptr = ch_malloc( bv.bv_len+1 );
+ bv.bv_val = lutil_strcopy( ptr, ai->ai_desc->ad_cname.bv_val );
+ *bv.bv_val++ = ' ';
+ slap_index2bv( ai->ai_indexmask, &bv );
+ bv.bv_val = ptr;
+ ber_bvarray_add( bva, &bv );
+ }
+ return 0;
+}
+
+static AttributeDescription addef = { NULL, NULL, BER_BVC("default") };
+static AttrInfo aidef = { &addef };
+
+void
+mdb_attr_index_unparse( struct mdb_info *mdb, BerVarray *bva )
+{
+ int i;
+
+ if ( mdb->mi_defaultmask ) {
+ aidef.ai_indexmask = mdb->mi_defaultmask;
+ mdb_attr_index_unparser( &aidef, bva );
+ }
+ for ( i=0; i<mdb->mi_nattrs; i++ )
+ if ( mdb->mi_attrs[i]->ai_indexmask )
+ mdb_attr_index_unparser( mdb->mi_attrs[i], bva );
+}
+
+int
+mdb_attr_multi_config(
+ struct mdb_info *mdb,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv,
+ struct config_reply_s *c_reply)
+{
+ int rc = 0;
+ int i;
+ unsigned hi,lo;
+ char **attrs, *next, *s;
+
+ attrs = ldap_str2charray( argv[0], "," );
+
+ if( attrs == NULL ) {
+ fprintf( stderr, "%s: line %d: "
+ "no attributes specified: %s\n",
+ fname, lineno, argv[0] );
+ return LDAP_PARAM_ERROR;
+ }
+
+ hi = strtoul( argv[1], &next, 10 );
+ if ( next == argv[1] || next[0] != ',' )
+ goto badval;
+ s = next+1;
+ lo = strtoul( s, &next, 10 );
+ if ( next == s || next[0] != '\0' )
+ goto badval;
+
+ if ( lo > hi ) {
+badval:
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "invalid hi/lo thresholds" );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ return LDAP_PARAM_ERROR;
+ }
+
+ for ( i = 0; attrs[i] != NULL; i++ ) {
+ AttrInfo *a;
+ AttributeDescription *ad;
+ const char *text;
+
+ if( strcasecmp( attrs[i], "default" ) == 0 ) {
+ mdb->mi_multi_hi = hi;
+ mdb->mi_multi_lo = lo;
+ continue;
+ }
+
+ ad = NULL;
+ rc = slap_str2ad( attrs[i], &ad, &text );
+
+ if( rc != LDAP_SUCCESS ) {
+ if ( c_reply )
+ {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "multival attribute \"%s\" undefined",
+ attrs[i] );
+
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+fail:
+ goto done;
+ }
+
+ a = (AttrInfo *) ch_calloc( 1, sizeof(AttrInfo) );
+
+ a->ai_desc = ad;
+ a->ai_multi_hi = hi;
+ a->ai_multi_lo = lo;
+
+ rc = ainfo_insert( mdb, a );
+ if( rc ) {
+ AttrInfo *b = mdb_attr_mask( mdb, ad );
+ /* If this is just an index record, reuse it for multival info */
+ if ( b->ai_multi_lo == UINT_MAX ) {
+ b->ai_multi_hi = a->ai_multi_hi;
+ b->ai_multi_lo = a->ai_multi_lo;
+ ch_free( a );
+ rc = 0;
+ continue;
+ }
+ if (c_reply) {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "duplicate multival definition for attr \"%s\"",
+ attrs[i] );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+
+ rc = LDAP_PARAM_ERROR;
+ goto done;
+ }
+ }
+
+done:
+ ldap_charray_free( attrs );
+
+ return rc;
+}
+
+static int
+mdb_attr_multi_unparser( void *v1, void *v2 )
+{
+ AttrInfo *ai = v1;
+ BerVarray *bva = v2;
+ struct berval bv;
+ char digbuf[sizeof("4294967296,4294967296")];
+ char *ptr;
+
+ bv.bv_len = snprintf( digbuf, sizeof(digbuf), "%u,%u",
+ ai->ai_multi_hi, ai->ai_multi_lo );
+ if ( bv.bv_len ) {
+ bv.bv_len += ai->ai_desc->ad_cname.bv_len + 1;
+ ptr = ch_malloc( bv.bv_len+1 );
+ bv.bv_val = lutil_strcopy( ptr, ai->ai_desc->ad_cname.bv_val );
+ *bv.bv_val++ = ' ';
+ strcpy(bv.bv_val, digbuf);
+ bv.bv_val = ptr;
+ ber_bvarray_add( bva, &bv );
+ }
+ return 0;
+}
+
+void
+mdb_attr_multi_unparse( struct mdb_info *mdb, BerVarray *bva )
+{
+ int i;
+
+ if ( mdb->mi_multi_hi < UINT_MAX ) {
+ aidef.ai_multi_hi = mdb->mi_multi_hi;
+ aidef.ai_multi_lo = mdb->mi_multi_lo;
+ mdb_attr_multi_unparser( &aidef, bva );
+ }
+ for ( i=0; i<mdb->mi_nattrs; i++ )
+ if ( mdb->mi_attrs[i]->ai_multi_hi < UINT_MAX )
+ mdb_attr_multi_unparser( mdb->mi_attrs[i], bva );
+}
+
+void
+mdb_attr_multi_thresh( struct mdb_info *mdb, AttributeDescription *ad, unsigned *hi, unsigned *lo )
+{
+ AttrInfo *ai = mdb_attr_mask( mdb, ad );
+ if ( ai && ai->ai_multi_hi < UINT_MAX )
+ {
+ if ( hi )
+ *hi = ai->ai_multi_hi;
+ if ( lo )
+ *lo = ai->ai_multi_lo;
+ } else
+ {
+ if ( hi )
+ *hi = mdb->mi_multi_hi;
+ if ( lo )
+ *lo = mdb->mi_multi_lo;
+ }
+}
+
+void
+mdb_attr_info_free( AttrInfo *ai )
+{
+#ifdef LDAP_COMP_MATCH
+ free( ai->ai_cr );
+#endif
+ free( ai );
+}
+
+void
+mdb_attr_index_destroy( struct mdb_info *mdb )
+{
+ int i;
+
+ for ( i=0; i<mdb->mi_nattrs; i++ )
+ mdb_attr_info_free( mdb->mi_attrs[i] );
+
+ free( mdb->mi_attrs );
+}
+
+void mdb_attr_index_free( struct mdb_info *mdb, AttributeDescription *ad )
+{
+ int i;
+
+ i = mdb_attr_slot( mdb, ad, NULL );
+ if ( i >= 0 ) {
+ mdb_attr_info_free( mdb->mi_attrs[i] );
+ mdb->mi_nattrs--;
+ for (; i<mdb->mi_nattrs; i++)
+ mdb->mi_attrs[i] = mdb->mi_attrs[i+1];
+ }
+}
+
+void mdb_attr_flush( struct mdb_info *mdb )
+{
+ int i;
+
+ for ( i=0; i<mdb->mi_nattrs; i++ ) {
+ if ( mdb->mi_attrs[i]->ai_indexmask & MDB_INDEX_DELETING ) {
+ /* if this is also a multival rec, just clear index */
+ if ( mdb->mi_attrs[i]->ai_multi_lo < UINT_MAX ) {
+ mdb->mi_attrs[i]->ai_indexmask = 0;
+ mdb->mi_attrs[i]->ai_newmask = 0;
+ } else {
+ int j;
+ mdb_attr_info_free( mdb->mi_attrs[i] );
+ mdb->mi_nattrs--;
+ for (j=i; j<mdb->mi_nattrs; j++)
+ mdb->mi_attrs[j] = mdb->mi_attrs[j+1];
+ i--;
+ }
+ }
+ }
+}
+
+int mdb_ad_read( struct mdb_info *mdb, MDB_txn *txn )
+{
+ int i, rc;
+ MDB_cursor *mc;
+ MDB_val key, data;
+ struct berval bdata;
+ const char *text;
+ AttributeDescription *ad;
+
+ rc = mdb_cursor_open( txn, mdb->mi_ad2id, &mc );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "mdb_ad_read: cursor_open failed %s(%d)\n",
+ mdb_strerror(rc), rc );
+ return rc;
+ }
+
+ /* our array is 1-based, an index of 0 means no data */
+ i = mdb->mi_numads+1;
+ key.mv_size = sizeof(int);
+ key.mv_data = &i;
+
+ rc = mdb_cursor_get( mc, &key, &data, MDB_SET );
+
+ while ( rc == MDB_SUCCESS ) {
+ bdata.bv_len = data.mv_size;
+ bdata.bv_val = data.mv_data;
+ ad = NULL;
+ rc = slap_bv2ad( &bdata, &ad, &text );
+ if ( rc ) {
+ rc = slap_bv2undef_ad( &bdata, &mdb->mi_ads[i], &text, 0 );
+ } else {
+ if ( ad->ad_index >= MDB_MAXADS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "mdb_adb_read: too many AttributeDescriptions in use\n" );
+ return LDAP_OTHER;
+ }
+ mdb->mi_adxs[ad->ad_index] = i;
+ mdb->mi_ads[i] = ad;
+ }
+ i++;
+ rc = mdb_cursor_get( mc, &key, &data, MDB_NEXT );
+ }
+ mdb->mi_numads = i-1;
+
+done:
+ if ( rc == MDB_NOTFOUND )
+ rc = 0;
+
+ mdb_cursor_close( mc );
+
+ return rc;
+}
+
+int mdb_ad_get( struct mdb_info *mdb, MDB_txn *txn, AttributeDescription *ad )
+{
+ int i, rc;
+ MDB_val key, val;
+
+ rc = mdb_ad_read( mdb, txn );
+ if (rc)
+ return rc;
+
+ if ( mdb->mi_adxs[ad->ad_index] )
+ return 0;
+
+ i = mdb->mi_numads+1;
+ key.mv_size = sizeof(int);
+ key.mv_data = &i;
+ val.mv_size = ad->ad_cname.bv_len;
+ val.mv_data = ad->ad_cname.bv_val;
+
+ rc = mdb_put( txn, mdb->mi_ad2id, &key, &val, 0 );
+ if ( rc == MDB_SUCCESS ) {
+ mdb->mi_adxs[ad->ad_index] = i;
+ mdb->mi_ads[i] = ad;
+ mdb->mi_numads = i;
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "mdb_ad_get: mdb_put failed %s(%d)\n",
+ mdb_strerror(rc), rc );
+ }
+
+ return rc;
+}
+
+void mdb_ad_unwind( struct mdb_info *mdb, int prev_ads )
+{
+ int i;
+
+ for (i=mdb->mi_numads; i>prev_ads; i--) {
+ mdb->mi_adxs[mdb->mi_ads[i]->ad_index] = 0;
+ mdb->mi_ads[i] = NULL;
+ }
+ mdb->mi_numads = i;
+}
diff --git a/servers/slapd/back-mdb/back-mdb.h b/servers/slapd/back-mdb/back-mdb.h
new file mode 100644
index 0000000..2b48aaa
--- /dev/null
+++ b/servers/slapd/back-mdb/back-mdb.h
@@ -0,0 +1,207 @@
+/* back-mdb.h - mdb back-end header file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _BACK_MDB_H_
+#define _BACK_MDB_H_
+
+#include <portable.h>
+#include "slap.h"
+#include "lmdb.h"
+
+LDAP_BEGIN_DECL
+
+#undef MDB_TOOL_IDL_CACHING /* currently no perf gain */
+
+#define DN_BASE_PREFIX SLAP_INDEX_EQUALITY_PREFIX
+#define DN_ONE_PREFIX '%'
+#define DN_SUBTREE_PREFIX '@'
+
+#define MDB_AD2ID 0
+#define MDB_DN2ID 1
+#define MDB_ID2ENTRY 2
+#define MDB_ID2VAL 3
+#define MDB_NDB 4
+
+/* The default search IDL stack cache depth */
+#define DEFAULT_SEARCH_STACK_DEPTH 16
+
+/* The minimum we can function with */
+#define MINIMUM_SEARCH_STACK_DEPTH 8
+
+#define MDB_INDICES 128
+
+#define MDB_MAXADS 65536
+
+/* Default to 10MB max */
+#define DEFAULT_MAPSIZE (10*1048576)
+
+/* Most users will never see this */
+#define DEFAULT_RTXN_SIZE 10000
+
+#ifdef LDAP_DEVEL
+#define MDB_MONITOR_IDX
+#endif
+
+typedef struct mdb_monitor_t {
+ void *mdm_cb;
+ struct berval mdm_ndn;
+} mdb_monitor_t;
+
+/* From ldap_rq.h */
+struct re_s;
+
+struct mdb_info {
+ MDB_env *mi_dbenv;
+
+ /* DB_ENV parameters */
+ char *mi_dbenv_home;
+ unsigned mi_dbenv_flags;
+ int mi_dbenv_mode;
+
+ size_t mi_mapsize;
+ ID mi_nextid;
+ size_t mi_maxentrysize;
+
+ slap_mask_t mi_defaultmask;
+ int mi_nattrs;
+ struct mdb_attrinfo **mi_attrs;
+ void *mi_search_stack;
+ int mi_search_stack_depth;
+ int mi_readers;
+
+ unsigned mi_rtxn_size;
+ int mi_txn_cp;
+ unsigned mi_txn_cp_min;
+ unsigned mi_txn_cp_kbyte;
+
+ struct re_s *mi_txn_cp_task;
+ struct re_s *mi_index_task;
+
+ mdb_monitor_t mi_monitor;
+
+#ifdef MDB_MONITOR_IDX
+ ldap_pvt_thread_mutex_t mi_idx_mutex;
+ Avlnode *mi_idx;
+#endif /* MDB_MONITOR_IDX */
+
+ int mi_flags;
+#define MDB_IS_OPEN 0x01
+#define MDB_OPEN_INDEX 0x02
+#define MDB_DEL_INDEX 0x08
+#define MDB_RE_OPEN 0x10
+#define MDB_NEED_UPGRADE 0x20
+
+ int mi_numads;
+
+ unsigned mi_multi_hi;
+ /* more than this many values in an attr goes
+ * into a separate DB */
+ unsigned mi_multi_lo;
+ /* less than this many values in an attr goes
+ * back into main blob */
+
+ MDB_dbi mi_dbis[MDB_NDB];
+ AttributeDescription *mi_ads[MDB_MAXADS];
+ int mi_adxs[MDB_MAXADS];
+};
+
+#define mi_id2entry mi_dbis[MDB_ID2ENTRY]
+#define mi_dn2id mi_dbis[MDB_DN2ID]
+#define mi_ad2id mi_dbis[MDB_AD2ID]
+#define mi_id2val mi_dbis[MDB_ID2VAL]
+
+typedef struct mdb_op_info {
+ OpExtra moi_oe;
+ MDB_txn* moi_txn;
+ int moi_ref;
+ char moi_flag;
+} mdb_op_info;
+#define MOI_READER 0x01
+#define MOI_FREEIT 0x02
+#define MOI_KEEPER 0x04
+
+LDAP_END_DECL
+
+/* for the cache of attribute information (which are indexed, etc.) */
+typedef struct mdb_attrinfo {
+ AttributeDescription *ai_desc; /* attribute description cn;lang-en */
+ slap_mask_t ai_indexmask; /* how the attr is indexed */
+ slap_mask_t ai_newmask; /* new settings to replace old mask */
+#ifdef LDAP_COMP_MATCH
+ ComponentReference* ai_cr; /*component indexing*/
+#endif
+ TAvlnode *ai_root; /* for tools */
+ MDB_cursor *ai_cursor; /* for tools */
+ int ai_idx; /* position in AI array */
+ MDB_dbi ai_dbi;
+ unsigned ai_multi_hi;
+ unsigned ai_multi_lo;
+} AttrInfo;
+
+/* tool threaded indexer state */
+typedef struct mdb_attrixinfo {
+ OpExtra ai_oe;
+ void *ai_flist;
+ void *ai_clist;
+ AttrInfo *ai_ai;
+} AttrIxInfo;
+
+/* These flags must not clash with SLAP_INDEX flags or ops in slap.h! */
+#define MDB_INDEX_DELETING 0x8000U /* index is being modified */
+#define MDB_INDEX_UPDATE_OP 0x03 /* performing an index update */
+
+/* For slapindex to record which attrs in an entry belong to which
+ * index database
+ */
+typedef struct AttrList {
+ struct AttrList *next;
+ Attribute *attr;
+} AttrList;
+
+#ifndef CACHELINE
+#define CACHELINE 64
+#endif
+
+#if defined(__i386) || defined(__x86_64)
+#define MISALIGNED_OK 1
+#else
+#define ALIGNER (sizeof(size_t)-1)
+#endif
+
+typedef struct IndexRbody {
+ AttrInfo *ai;
+ AttrList *attrs;
+ void *tptr;
+ int i;
+} IndexRbody;
+
+typedef struct IndexRec {
+ union {
+ IndexRbody irb;
+#define ir_ai iru.irb.ai
+#define ir_attrs iru.irb.attrs
+#define ir_tptr iru.irb.tptr
+#define ir_i iru.irb.i
+ /* cache line alignment */
+ char pad[(sizeof(IndexRbody)+CACHELINE-1) & (!CACHELINE-1)];
+ } iru;
+} IndexRec;
+
+#define MAXRDNS SLAP_LDAPDN_MAXLEN/4
+
+#include "proto-mdb.h"
+
+#endif /* _BACK_MDB_H_ */
diff --git a/servers/slapd/back-mdb/bind.c b/servers/slapd/back-mdb/bind.c
new file mode 100644
index 0000000..6df3f2d
--- /dev/null
+++ b/servers/slapd/back-mdb/bind.c
@@ -0,0 +1,156 @@
+/* bind.c - mdb backend bind routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include "back-mdb.h"
+
+int
+mdb_bind( Operation *op, SlapReply *rs )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ Entry *e;
+ Attribute *a;
+
+ AttributeDescription *password = slap_schema.si_ad_userPassword;
+
+ MDB_txn *rtxn;
+ mdb_op_info opinfo = {{{0}}}, *moi = &opinfo;
+
+ Debug( LDAP_DEBUG_ARGS,
+ "==> " LDAP_XSTRING(mdb_bind) ": dn: %s\n",
+ op->o_req_dn.bv_val );
+
+ /* allow noauth binds */
+ switch ( be_rootdn_bind( op, NULL ) ) {
+ case LDAP_SUCCESS:
+ /* frontend will send result */
+ return rs->sr_err = LDAP_SUCCESS;
+
+ default:
+ /* give the database a chance */
+ /* NOTE: this behavior departs from that of other backends,
+ * since the others, in case of password checking failure
+ * do not give the database a chance. If an entry with
+ * rootdn's name does not exist in the database the result
+ * will be the same. See ITS#4962 for discussion. */
+ break;
+ }
+
+ rs->sr_err = mdb_opinfo_get(op, mdb, 1, &moi);
+ switch(rs->sr_err) {
+ case 0:
+ break;
+ default:
+ rs->sr_text = "internal error";
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+
+ rtxn = moi->moi_txn;
+
+ /* get entry with reader lock */
+ rs->sr_err = mdb_dn2entry( op, rtxn, NULL, &op->o_req_ndn, &e, NULL, 0 );
+
+ switch(rs->sr_err) {
+ case MDB_NOTFOUND:
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ case 0:
+ break;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap_server_busy";
+ goto done;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto done;
+ }
+
+ ber_dupbv( &op->oq_bind.rb_edn, &e->e_name );
+
+ /* check for deleted */
+ if ( is_entry_subentry( e ) ) {
+ /* entry is an subentry, don't allow bind */
+ Debug( LDAP_DEBUG_TRACE, "entry is subentry\n" );
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ if ( is_entry_alias( e ) ) {
+ /* entry is an alias, don't allow bind */
+ Debug( LDAP_DEBUG_TRACE, "entry is alias\n" );
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ if ( is_entry_referral( e ) ) {
+ Debug( LDAP_DEBUG_TRACE, "entry is referral\n" );
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ switch ( op->oq_bind.rb_method ) {
+ case LDAP_AUTH_SIMPLE:
+ a = attr_find( e->e_attrs, password );
+ if ( a == NULL ) {
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ if ( slap_passwd_check( op, e, a, &op->oq_bind.rb_cred,
+ &rs->sr_text ) != 0 )
+ {
+ /* failure; stop front end from sending result */
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ rs->sr_err = 0;
+ break;
+
+ default:
+ assert( 0 ); /* should not be reachable */
+ rs->sr_err = LDAP_STRONG_AUTH_NOT_SUPPORTED;
+ rs->sr_text = "authentication method not supported";
+ }
+
+done:
+ if ( moi == &opinfo ) {
+ mdb_txn_reset( moi->moi_txn );
+ LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
+ } else {
+ moi->moi_ref--;
+ }
+ /* free entry and reader lock */
+ if( e != NULL ) {
+ mdb_entry_return( op, e );
+ }
+
+ if ( rs->sr_err ) {
+ send_ldap_result( op, rs );
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+ }
+ /* front end will send result on success (rs->sr_err==0) */
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-mdb/compare.c b/servers/slapd/back-mdb/compare.c
new file mode 100644
index 0000000..10a6ccd
--- /dev/null
+++ b/servers/slapd/back-mdb/compare.c
@@ -0,0 +1,142 @@
+/* compare.c - mdb backend compare routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+
+int
+mdb_compare( Operation *op, SlapReply *rs )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ Entry *e = NULL;
+ int manageDSAit = get_manageDSAit( op );
+
+ MDB_txn *rtxn;
+ mdb_op_info opinfo = {{{0}}}, *moi = &opinfo;
+
+ rs->sr_err = mdb_opinfo_get(op, mdb, 1, &moi);
+ switch(rs->sr_err) {
+ case 0:
+ break;
+ default:
+ send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
+ return rs->sr_err;
+ }
+
+ rtxn = moi->moi_txn;
+
+ /* get entry */
+ rs->sr_err = mdb_dn2entry( op, rtxn, NULL, &op->o_req_ndn, &e, NULL, 1 );
+ switch( rs->sr_err ) {
+ case MDB_NOTFOUND:
+ case 0:
+ break;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ if ( rs->sr_err == MDB_NOTFOUND ) {
+ if ( e != NULL ) {
+ /* return referral only if "disclose" is granted on the object */
+ if ( ! access_allowed( op, e, slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL ) )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+
+ } else {
+ rs->sr_matched = ch_strdup( e->e_dn );
+ if ( is_entry_referral( e )) {
+ BerVarray ref = get_entry_referrals( op, e );
+ rs->sr_ref = referral_rewrite( ref, &e->e_name,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ } else {
+ rs->sr_ref = NULL;
+ }
+ rs->sr_err = LDAP_REFERRAL;
+ }
+ mdb_entry_return( op, e );
+ e = NULL;
+
+ } else {
+ rs->sr_ref = referral_rewrite( default_referral,
+ NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ rs->sr_err = rs->sr_ref ? LDAP_REFERRAL : LDAP_NO_SUCH_OBJECT;
+ }
+
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ if (!manageDSAit && is_entry_referral( e ) ) {
+ /* return referral only if "disclose" is granted on the object */
+ if ( !access_allowed( op, e, slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL ) )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ } else {
+ /* entry is a referral, don't allow compare */
+ rs->sr_ref = get_entry_referrals( op, e );
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_matched = e->e_name.bv_val;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "entry is referral\n" );
+
+ send_ldap_result( op, rs );
+
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ rs->sr_matched = NULL;
+ goto done;
+ }
+
+ rs->sr_err = slap_compare_entry( op, e, op->orc_ava );
+
+return_results:
+ send_ldap_result( op, rs );
+
+ switch ( rs->sr_err ) {
+ case LDAP_COMPARE_FALSE:
+ case LDAP_COMPARE_TRUE:
+ rs->sr_err = LDAP_SUCCESS;
+ break;
+ }
+
+done:
+ if ( moi == &opinfo ) {
+ mdb_txn_reset( moi->moi_txn );
+ LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
+ } else {
+ moi->moi_ref--;
+ }
+ /* free entry */
+ if ( e != NULL ) {
+ mdb_entry_return( op, e );
+ }
+
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-mdb/config.c b/servers/slapd/back-mdb/config.c
new file mode 100644
index 0000000..ce6d78a
--- /dev/null
+++ b/servers/slapd/back-mdb/config.c
@@ -0,0 +1,828 @@
+/* config.c - mdb backend configuration file routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/errno.h>
+
+#include "back-mdb.h"
+#include "idl.h"
+
+#include "slap-config.h"
+
+#include "lutil.h"
+#include "ldap_rq.h"
+
+
+static ConfigDriver mdb_cf_gen;
+static ConfigDriver mdb_bk_cfg;
+
+enum {
+ MDB_CHKPT = 1,
+ MDB_DIRECTORY,
+ MDB_DBNOSYNC,
+ MDB_ENVFLAGS,
+ MDB_INDEX,
+ MDB_MAXREADERS,
+ MDB_MAXSIZE,
+ MDB_MODE,
+ MDB_SSTACK,
+ MDB_MULTIVAL,
+ MDB_IDLEXP,
+};
+
+static ConfigTable mdbcfg[] = {
+ { "idlexp", "log", 2, 2, 0, ARG_UINT|ARG_MAGIC|MDB_IDLEXP,
+ mdb_bk_cfg, "( OLcfgBkAt:12.1 NAME 'olcBkMdbIdlExp' "
+ "DESC 'Power of 2 used to set IDL size' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "directory", "dir", 2, 2, 0, ARG_STRING|ARG_MAGIC|MDB_DIRECTORY,
+ mdb_cf_gen, "( OLcfgDbAt:0.1 NAME 'olcDbDirectory' "
+ "DESC 'Directory for database content' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "checkpoint", "kbyte> <min", 3, 3, 0, ARG_MAGIC|MDB_CHKPT,
+ mdb_cf_gen, "( OLcfgDbAt:1.2 NAME 'olcDbCheckpoint' "
+ "DESC 'Database checkpoint interval in kbytes and minutes' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )",NULL, NULL },
+ { "dbnosync", NULL, 1, 2, 0, ARG_ON_OFF|ARG_MAGIC|MDB_DBNOSYNC,
+ mdb_cf_gen, "( OLcfgDbAt:1.4 NAME 'olcDbNoSync' "
+ "DESC 'Disable synchronous database writes' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "envflags", "flags", 2, 0, 0, ARG_MAGIC|MDB_ENVFLAGS,
+ mdb_cf_gen, "( OLcfgDbAt:12.3 NAME 'olcDbEnvFlags' "
+ "DESC 'Database environment flags' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "index", "attr> <[pres,eq,approx,sub]", 2, 3, 0, ARG_MAGIC|MDB_INDEX,
+ mdb_cf_gen, "( OLcfgDbAt:0.2 NAME 'olcDbIndex' "
+ "DESC 'Attribute index parameters' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "maxentrysize", "size", 2, 2, 0, ARG_ULONG|ARG_OFFSET,
+ (void *)offsetof(struct mdb_info, mi_maxentrysize),
+ "( OLcfgDbAt:12.4 NAME 'olcDbMaxEntrySize' "
+ "DESC 'Maximum size of an entry in bytes' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "maxreaders", "num", 2, 2, 0, ARG_UINT|ARG_MAGIC|MDB_MAXREADERS,
+ mdb_cf_gen, "( OLcfgDbAt:12.1 NAME 'olcDbMaxReaders' "
+ "DESC 'Maximum number of threads that may access the DB concurrently' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "maxsize", "size", 2, 2, 0, ARG_ULONG|ARG_MAGIC|MDB_MAXSIZE,
+ mdb_cf_gen, "( OLcfgDbAt:12.2 NAME 'olcDbMaxSize' "
+ "DESC 'Maximum size of DB in bytes' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "mode", "mode", 2, 2, 0, ARG_MAGIC|MDB_MODE,
+ mdb_cf_gen, "( OLcfgDbAt:0.3 NAME 'olcDbMode' "
+ "DESC 'Unix permissions of database files' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "multival", "attr> <hi,lo", 3, 3, 0, ARG_MAGIC|MDB_MULTIVAL,
+ mdb_cf_gen,
+ "( OLcfgDbAt:12.6 NAME 'olcDbMultival' "
+ "DESC 'Hi/Lo thresholds for splitting multivalued attr out of main blob' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "rtxnsize", "entries", 2, 2, 0, ARG_UINT|ARG_OFFSET,
+ (void *)offsetof(struct mdb_info, mi_rtxn_size),
+ "( OLcfgDbAt:12.5 NAME 'olcDbRtxnSize' "
+ "DESC 'Number of entries to process in one read transaction' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL,
+ { .v_uint = DEFAULT_RTXN_SIZE } },
+ { "searchstack", "depth", 2, 2, 0, ARG_INT|ARG_MAGIC|MDB_SSTACK,
+ mdb_cf_gen, "( OLcfgDbAt:1.9 NAME 'olcDbSearchStack' "
+ "DESC 'Depth of search stack in IDLs' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED,
+ NULL, NULL, NULL, NULL }
+};
+
+static ConfigOCs mdbocs[] = {
+ {
+ "( OLcfgBkOc:12.1 "
+ "NAME 'olcMdbBkConfig' "
+ "DESC 'MDB backend configuration' "
+ "SUP olcBackendConfig "
+ "MAY olcBkMdbIdlExp )",
+ Cft_Backend, mdbcfg },
+ {
+ "( OLcfgDbOc:12.1 "
+ "NAME 'olcMdbConfig' "
+ "DESC 'MDB database configuration' "
+ "SUP olcDatabaseConfig "
+ "MUST olcDbDirectory "
+ "MAY ( olcDbCheckpoint $ olcDbEnvFlags $ "
+ "olcDbNoSync $ olcDbIndex $ olcDbMaxReaders $ olcDbMaxSize $ "
+ "olcDbMode $ olcDbSearchStack $ olcDbMaxEntrySize $ olcDbRtxnSize $ "
+ "olcDbMultival ) )",
+ Cft_Database, mdbcfg+1 },
+ { NULL, 0, NULL }
+};
+
+static slap_verbmasks mdb_envflags[] = {
+ { BER_BVC("nosync"), MDB_NOSYNC },
+ { BER_BVC("nometasync"), MDB_NOMETASYNC },
+ { BER_BVC("writemap"), MDB_WRITEMAP },
+ { BER_BVC("mapasync"), MDB_MAPASYNC },
+ { BER_BVC("nordahead"), MDB_NORDAHEAD },
+ { BER_BVNULL, 0 }
+};
+
+static int
+mdb_bk_cfg( ConfigArgs *c )
+{
+ int rc = 0;
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ if ( MDB_idl_logn != MDB_IDL_LOGN )
+ c->value_int = MDB_idl_logn;
+ else
+ rc = 1;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ /* We expect to immediately be followed by an Add, but */
+ MDB_idl_logn = MDB_IDL_LOGN; /* return to default for safety */
+ mdb_idl_reset();
+ c->bi->bi_private = 0;
+ } else {
+ /* with 32 bit ints, db_size max is 2^30 and um_size max is 2^31 */
+ if ( c->value_int >= MDB_IDL_LOGN && ( c->value_int < sizeof(int) * CHAR_BIT - 1 )) {
+ MDB_idl_logn = c->value_int;
+ mdb_idl_reset();
+ c->bi->bi_private = (void *)8; /* non-NULL to show we're using it */
+ } else {
+ rc = 1;
+ }
+ }
+ return rc;
+}
+
+/* perform periodic syncs */
+static void *
+mdb_checkpoint( void *ctx, void *arg )
+{
+ struct re_s *rtask = arg;
+ struct mdb_info *mdb = rtask->arg;
+
+ mdb_env_sync( mdb->mi_dbenv, 1 );
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ return NULL;
+}
+
+/* reindex entries on the fly */
+static void *
+mdb_online_index( void *ctx, void *arg )
+{
+ struct re_s *rtask = arg;
+ BackendDB *be = rtask->arg;
+ struct mdb_info *mdb = be->be_private;
+
+ Connection conn = {0};
+ OperationBuffer opbuf;
+ Operation *op;
+
+ MDB_cursor *curs;
+ MDB_val key, data;
+ MDB_txn *txn;
+ ID id;
+ Entry *e;
+ int rc, getnext = 1;
+ int i;
+
+ connection_fake_init( &conn, &opbuf, ctx );
+ op = &opbuf.ob_op;
+
+ op->o_bd = be;
+
+ id = 1;
+ key.mv_size = sizeof(ID);
+
+ while ( 1 ) {
+ if ( slapd_shutdown )
+ break;
+
+ rc = mdb_txn_begin( mdb->mi_dbenv, NULL, 0, &txn );
+ if ( rc )
+ break;
+ rc = mdb_cursor_open( txn, mdb->mi_id2entry, &curs );
+ if ( rc ) {
+ mdb_txn_abort( txn );
+ break;
+ }
+ if ( getnext ) {
+ getnext = 0;
+ key.mv_data = &id;
+ rc = mdb_cursor_get( curs, &key, &data, MDB_SET_RANGE );
+ if ( rc ) {
+ mdb_txn_abort( txn );
+ if ( rc == MDB_NOTFOUND )
+ rc = 0;
+ break;
+ }
+ memcpy( &id, key.mv_data, sizeof( id ));
+ }
+
+ rc = mdb_id2entry( op, curs, id, &e );
+ mdb_cursor_close( curs );
+ if ( rc ) {
+ mdb_txn_abort( txn );
+ if ( rc == MDB_NOTFOUND ) {
+ id++;
+ getnext = 1;
+ continue;
+ }
+ break;
+ }
+ rc = mdb_index_entry( op, txn, MDB_INDEX_UPDATE_OP, e );
+ mdb_entry_return( op, e );
+ if ( rc == 0 ) {
+ rc = mdb_txn_commit( txn );
+ txn = NULL;
+ } else {
+ mdb_txn_abort( txn );
+ txn = NULL;
+ }
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_online_index) ": database %s: "
+ "txn_commit failed: %s (%d)\n",
+ be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
+ break;
+ }
+ id++;
+ getnext = 1;
+ }
+
+ for ( i = 0; i < mdb->mi_nattrs; i++ ) {
+ if ( mdb->mi_attrs[ i ]->ai_indexmask & MDB_INDEX_DELETING
+ || mdb->mi_attrs[ i ]->ai_newmask == 0 )
+ {
+ continue;
+ }
+ mdb->mi_attrs[ i ]->ai_indexmask = mdb->mi_attrs[ i ]->ai_newmask;
+ mdb->mi_attrs[ i ]->ai_newmask = 0;
+ }
+
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
+ mdb->mi_index_task = NULL;
+ ldap_pvt_runqueue_remove( &slapd_rq, rtask );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+
+ return NULL;
+}
+
+/* Cleanup loose ends after Modify completes */
+static int
+mdb_cf_cleanup( ConfigArgs *c )
+{
+ struct mdb_info *mdb = c->be->be_private;
+ int rc = 0;
+
+ if ( mdb->mi_flags & MDB_DEL_INDEX ) {
+ mdb_attr_flush( mdb );
+ mdb->mi_flags ^= MDB_DEL_INDEX;
+ }
+
+ if ( mdb->mi_flags & MDB_RE_OPEN ) {
+ mdb->mi_flags ^= MDB_RE_OPEN;
+ rc = c->be->bd_info->bi_db_close( c->be, &c->reply );
+ if ( rc == 0 )
+ rc = c->be->bd_info->bi_db_open( c->be, &c->reply );
+ /* If this fails, we need to restart */
+ if ( rc ) {
+ slapd_shutdown = 2;
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "failed to reopen database, rc=%d", rc );
+ Debug( LDAP_DEBUG_ANY, LDAP_XSTRING(mdb_cf_cleanup)
+ ": %s\n", c->cr_msg );
+ rc = LDAP_OTHER;
+ }
+ }
+
+ if ( mdb->mi_flags & MDB_OPEN_INDEX ) {
+ mdb->mi_flags ^= MDB_OPEN_INDEX;
+ rc = mdb_attr_dbs_open( c->be, NULL, &c->reply );
+ if ( rc )
+ rc = LDAP_OTHER;
+ }
+ return rc;
+}
+
+static int
+mdb_cf_gen( ConfigArgs *c )
+{
+ struct mdb_info *mdb = c->be->be_private;
+ int rc;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ rc = 0;
+ switch( c->type ) {
+ case MDB_MODE: {
+ char buf[64];
+ struct berval bv;
+ bv.bv_len = snprintf( buf, sizeof(buf), "0%o", mdb->mi_dbenv_mode );
+ if ( bv.bv_len > 0 && bv.bv_len < sizeof(buf) ) {
+ bv.bv_val = buf;
+ value_add_one( &c->rvalue_vals, &bv );
+ } else {
+ rc = 1;
+ }
+ } break;
+
+ case MDB_CHKPT:
+ if ( mdb->mi_txn_cp ) {
+ char buf[64];
+ struct berval bv;
+ bv.bv_len = snprintf( buf, sizeof(buf), "%ld %ld",
+ (long) mdb->mi_txn_cp_kbyte, (long) mdb->mi_txn_cp_min );
+ if ( bv.bv_len > 0 && bv.bv_len < sizeof(buf) ) {
+ bv.bv_val = buf;
+ value_add_one( &c->rvalue_vals, &bv );
+ } else {
+ rc = 1;
+ }
+ } else {
+ rc = 1;
+ }
+ break;
+
+ case MDB_DIRECTORY:
+ if ( mdb->mi_dbenv_home ) {
+ c->value_string = ch_strdup( mdb->mi_dbenv_home );
+ } else {
+ rc = 1;
+ }
+ break;
+
+ case MDB_DBNOSYNC:
+ if ( mdb->mi_dbenv_flags & MDB_NOSYNC )
+ c->value_int = 1;
+ break;
+
+ case MDB_ENVFLAGS:
+ if ( mdb->mi_dbenv_flags ) {
+ mask_to_verbs( mdb_envflags, mdb->mi_dbenv_flags, &c->rvalue_vals );
+ }
+ if ( !c->rvalue_vals ) rc = 1;
+ break;
+
+ case MDB_INDEX:
+ mdb_attr_index_unparse( mdb, &c->rvalue_vals );
+ if ( !c->rvalue_vals ) rc = 1;
+ break;
+
+ case MDB_SSTACK:
+ c->value_int = mdb->mi_search_stack_depth;
+ break;
+
+ case MDB_MAXREADERS:
+ c->value_int = mdb->mi_readers;
+ break;
+
+ case MDB_MAXSIZE:
+ c->value_ulong = mdb->mi_mapsize;
+ break;
+
+ case MDB_MULTIVAL:
+ mdb_attr_multi_unparse( mdb, &c->rvalue_vals );
+ if ( !c->rvalue_vals ) rc = 1;
+ break;
+ }
+ return rc;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ rc = 0;
+ switch( c->type ) {
+ case MDB_MODE:
+#if 0
+ /* FIXME: does it make any sense to change the mode,
+ * if we don't exec a chmod()? */
+ mdb->bi_dbenv_mode = SLAPD_DEFAULT_DB_MODE;
+ break;
+#endif
+
+ /* single-valued no-ops */
+ case MDB_SSTACK:
+ case MDB_MAXREADERS:
+ case MDB_MAXSIZE:
+ break;
+
+ case MDB_CHKPT:
+ if ( mdb->mi_txn_cp_task ) {
+ struct re_s *re = mdb->mi_txn_cp_task;
+ mdb->mi_txn_cp_task = NULL;
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, re ) )
+ ldap_pvt_runqueue_stoptask( &slapd_rq, re );
+ ldap_pvt_runqueue_remove( &slapd_rq, re );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+ mdb->mi_txn_cp = 0;
+ break;
+ case MDB_DIRECTORY:
+ mdb->mi_flags |= MDB_RE_OPEN;
+ ch_free( mdb->mi_dbenv_home );
+ mdb->mi_dbenv_home = NULL;
+ config_push_cleanup( c, mdb_cf_cleanup );
+ ldap_pvt_thread_pool_purgekey( mdb->mi_dbenv );
+ break;
+ case MDB_DBNOSYNC:
+ mdb_env_set_flags( mdb->mi_dbenv, MDB_NOSYNC, 0 );
+ mdb->mi_dbenv_flags &= ~MDB_NOSYNC;
+ break;
+
+ case MDB_ENVFLAGS:
+ if ( c->valx == -1 ) {
+ int i;
+ for ( i=0; mdb_envflags[i].mask; i++) {
+ if ( mdb->mi_dbenv_flags & mdb_envflags[i].mask ) {
+ /* not all flags are runtime resettable */
+ rc = mdb_env_set_flags( mdb->mi_dbenv, mdb_envflags[i].mask, 0 );
+ if ( rc ) {
+ mdb->mi_flags |= MDB_RE_OPEN;
+ config_push_cleanup( c, mdb_cf_cleanup );
+ rc = 0;
+ }
+ mdb->mi_dbenv_flags ^= mdb_envflags[i].mask;
+ }
+ }
+ } else {
+ int i = verb_to_mask( c->line, mdb_envflags );
+ if ( mdb_envflags[i].mask & mdb->mi_dbenv_flags ) {
+ rc = mdb_env_set_flags( mdb->mi_dbenv, mdb_envflags[i].mask, 0 );
+ if ( rc ) {
+ mdb->mi_flags |= MDB_RE_OPEN;
+ config_push_cleanup( c, mdb_cf_cleanup );
+ rc = 0;
+ }
+ mdb->mi_dbenv_flags ^= mdb_envflags[i].mask;
+ } else {
+ /* unknown keyword */
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: unknown keyword \"%s\"",
+ c->argv[0], c->argv[i] );
+ Debug( LDAP_DEBUG_CONFIG, "%s %s\n", c->log, c->cr_msg );
+ rc = 1;
+ }
+ }
+ break;
+
+ case MDB_INDEX:
+ if ( c->valx == -1 ) {
+ int i;
+
+ /* delete all */
+ for ( i = 0; i < mdb->mi_nattrs; i++ ) {
+ mdb->mi_attrs[i]->ai_indexmask |= MDB_INDEX_DELETING;
+ }
+ mdb->mi_defaultmask = 0;
+ mdb->mi_flags |= MDB_DEL_INDEX;
+ config_push_cleanup( c, mdb_cf_cleanup );
+
+ } else {
+ struct berval bv, def = BER_BVC("default");
+ char *ptr;
+
+ for (ptr = c->line; !isspace( (unsigned char) *ptr ); ptr++);
+
+ bv.bv_val = c->line;
+ bv.bv_len = ptr - bv.bv_val;
+ if ( bvmatch( &bv, &def )) {
+ mdb->mi_defaultmask = 0;
+
+ } else {
+ int i;
+ char **attrs;
+ char sep;
+
+ sep = bv.bv_val[ bv.bv_len ];
+ bv.bv_val[ bv.bv_len ] = '\0';
+ attrs = ldap_str2charray( bv.bv_val, "," );
+
+ for ( i = 0; attrs[ i ]; i++ ) {
+ AttributeDescription *ad = NULL;
+ const char *text;
+ AttrInfo *ai;
+
+ slap_str2ad( attrs[ i ], &ad, &text );
+ /* if we got here... */
+ assert( ad != NULL );
+
+ ai = mdb_attr_mask( mdb, ad );
+ /* if we got here... */
+ assert( ai != NULL );
+
+ ai->ai_indexmask |= MDB_INDEX_DELETING;
+ mdb->mi_flags |= MDB_DEL_INDEX;
+ config_push_cleanup( c, mdb_cf_cleanup );
+ }
+
+ bv.bv_val[ bv.bv_len ] = sep;
+ ldap_charray_free( attrs );
+ }
+ }
+ break;
+ case MDB_MULTIVAL:
+ if ( c->valx == -1 ) {
+ int i;
+
+ /* delete all */
+ for ( i = 0; i < mdb->mi_nattrs; i++ ) {
+ mdb->mi_attrs[i]->ai_multi_hi = UINT_MAX;
+ mdb->mi_attrs[i]->ai_multi_lo = UINT_MAX;
+ }
+ mdb->mi_multi_hi = UINT_MAX;
+ mdb->mi_multi_lo = UINT_MAX;
+
+ } else {
+ struct berval bv, def = BER_BVC("default");
+ char *ptr;
+
+ for (ptr = c->line; !isspace( (unsigned char) *ptr ); ptr++);
+
+ bv.bv_val = c->line;
+ bv.bv_len = ptr - bv.bv_val;
+ if ( bvmatch( &bv, &def )) {
+ mdb->mi_multi_hi = UINT_MAX;
+ mdb->mi_multi_lo = UINT_MAX;
+
+ } else {
+ int i;
+ char **attrs;
+ char sep;
+
+ sep = bv.bv_val[ bv.bv_len ];
+ bv.bv_val[ bv.bv_len ] = '\0';
+ attrs = ldap_str2charray( bv.bv_val, "," );
+
+ for ( i = 0; attrs[ i ]; i++ ) {
+ AttributeDescription *ad = NULL;
+ const char *text;
+ AttrInfo *ai;
+
+ slap_str2ad( attrs[ i ], &ad, &text );
+ /* if we got here... */
+ assert( ad != NULL );
+
+ ai = mdb_attr_mask( mdb, ad );
+ /* if we got here... */
+ assert( ai != NULL );
+
+ ai->ai_multi_hi = UINT_MAX;
+ ai->ai_multi_lo = UINT_MAX;
+ }
+
+ bv.bv_val[ bv.bv_len ] = sep;
+ ldap_charray_free( attrs );
+ }
+ }
+ break;
+ }
+ return rc;
+ }
+
+ switch( c->type ) {
+ case MDB_MODE:
+ if ( ASCII_DIGIT( c->argv[1][0] ) ) {
+ long mode;
+ char *next;
+ errno = 0;
+ mode = strtol( c->argv[1], &next, 0 );
+ if ( errno != 0 || next == c->argv[1] || next[0] != '\0' ) {
+ fprintf( stderr, "%s: "
+ "unable to parse mode=\"%s\".\n",
+ c->log, c->argv[1] );
+ return 1;
+ }
+ mdb->mi_dbenv_mode = mode;
+
+ } else {
+ char *m = c->argv[1];
+ int who, what, mode = 0;
+
+ if ( strlen( m ) != STRLENOF("-rwxrwxrwx") ) {
+ return 1;
+ }
+
+ if ( m[0] != '-' ) {
+ return 1;
+ }
+
+ m++;
+ for ( who = 0; who < 3; who++ ) {
+ for ( what = 0; what < 3; what++, m++ ) {
+ if ( m[0] == '-' ) {
+ continue;
+ } else if ( m[0] != "rwx"[what] ) {
+ return 1;
+ }
+ mode += ((1 << (2 - what)) << 3*(2 - who));
+ }
+ }
+ mdb->mi_dbenv_mode = mode;
+ }
+ break;
+ case MDB_CHKPT: {
+ unsigned cp_kbyte, cp_min;
+ if ( lutil_atoux( &cp_kbyte, c->argv[1], 0 ) != 0 ) {
+ fprintf( stderr, "%s: "
+ "invalid kbyte \"%s\" in \"checkpoint\".\n",
+ c->log, c->argv[1] );
+ return 1;
+ }
+ if ( lutil_atoux( &cp_min, c->argv[2], 0 ) != 0 ) {
+ fprintf( stderr, "%s: "
+ "invalid minutes \"%s\" in \"checkpoint\".\n",
+ c->log, c->argv[2] );
+ return 1;
+ }
+ mdb->mi_txn_cp = 1;
+ mdb->mi_txn_cp_kbyte = cp_kbyte;
+ mdb->mi_txn_cp_min = cp_min;
+ /* If we're in server mode and time-based checkpointing is enabled,
+ * submit a task to perform periodic checkpoints.
+ */
+ if ((slapMode & SLAP_SERVER_MODE) && mdb->mi_txn_cp_min ) {
+ struct re_s *re = mdb->mi_txn_cp_task;
+ if ( re ) {
+ re->interval.tv_sec = mdb->mi_txn_cp_min * 60;
+ } else {
+ if ( c->be->be_suffix == NULL || BER_BVISNULL( &c->be->be_suffix[0] ) ) {
+ fprintf( stderr, "%s: "
+ "\"checkpoint\" must occur after \"suffix\".\n",
+ c->log );
+ return 1;
+ }
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ mdb->mi_txn_cp_task = ldap_pvt_runqueue_insert( &slapd_rq,
+ mdb->mi_txn_cp_min * 60, mdb_checkpoint, mdb,
+ LDAP_XSTRING(mdb_checkpoint), c->be->be_suffix[0].bv_val );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+ }
+ } break;
+
+ case MDB_DIRECTORY: {
+ FILE *f;
+ char *ptr, *testpath;
+ int len;
+
+ len = strlen( c->value_string );
+ testpath = ch_malloc( len + STRLENOF(LDAP_DIRSEP) + STRLENOF("DUMMY") + 1 );
+ ptr = lutil_strcopy( testpath, c->value_string );
+ *ptr++ = LDAP_DIRSEP[0];
+ strcpy( ptr, "DUMMY" );
+ f = fopen( testpath, "w" );
+ if ( f ) {
+ fclose( f );
+ unlink( testpath );
+ }
+ ch_free( testpath );
+ if ( !f ) {
+ char ebuf[128];
+ int saved_errno = errno;
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: invalid path: %s",
+ c->log, AC_STRERROR_R( saved_errno, ebuf, sizeof(ebuf) ) );
+ Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg );
+ return -1;
+ }
+
+ if ( mdb->mi_dbenv_home )
+ ch_free( mdb->mi_dbenv_home );
+ mdb->mi_dbenv_home = c->value_string;
+
+ }
+ break;
+
+ case MDB_DBNOSYNC:
+ if ( c->value_int )
+ mdb->mi_dbenv_flags |= MDB_NOSYNC;
+ else
+ mdb->mi_dbenv_flags &= ~MDB_NOSYNC;
+ if ( mdb->mi_flags & MDB_IS_OPEN ) {
+ mdb_env_set_flags( mdb->mi_dbenv, MDB_NOSYNC,
+ c->value_int );
+ }
+ break;
+
+ case MDB_ENVFLAGS: {
+ int i, j;
+ for ( i=1; i<c->argc; i++ ) {
+ j = verb_to_mask( c->argv[i], mdb_envflags );
+ if ( mdb_envflags[j].mask ) {
+ if ( mdb->mi_flags & MDB_IS_OPEN )
+ rc = mdb_env_set_flags( mdb->mi_dbenv, mdb_envflags[j].mask, 1 );
+ else
+ rc = 0;
+ if ( rc ) {
+ mdb->mi_flags |= MDB_RE_OPEN;
+ config_push_cleanup( c, mdb_cf_cleanup );
+ rc = 0;
+ }
+ mdb->mi_dbenv_flags |= mdb_envflags[j].mask;
+ } else {
+ /* unknown keyword */
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: unknown keyword \"%s\"",
+ c->argv[0], c->argv[i] );
+ Debug( LDAP_DEBUG_ANY, "%s %s\n", c->log, c->cr_msg );
+ return 1;
+ }
+ }
+ }
+ break;
+
+ case MDB_INDEX:
+ rc = mdb_attr_index_config( mdb, c->fname, c->lineno,
+ c->argc - 1, &c->argv[1], &c->reply);
+
+ if( rc != LDAP_SUCCESS ) return 1;
+ if ( mdb->mi_flags & MDB_IS_OPEN ) {
+ mdb->mi_flags |= MDB_OPEN_INDEX;
+ config_push_cleanup( c, mdb_cf_cleanup );
+ if ( !mdb->mi_index_task ) {
+ /* Start the task as soon as we finish here. Set a long
+ * interval (10 hours) so that it only gets scheduled once.
+ */
+ if ( c->be->be_suffix == NULL || BER_BVISNULL( &c->be->be_suffix[0] ) ) {
+ fprintf( stderr, "%s: "
+ "\"index\" must occur after \"suffix\".\n",
+ c->log );
+ return 1;
+ }
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ mdb->mi_index_task = ldap_pvt_runqueue_insert( &slapd_rq, 36000,
+ mdb_online_index, c->be,
+ LDAP_XSTRING(mdb_online_index), c->be->be_suffix[0].bv_val );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+ }
+ break;
+
+ case MDB_SSTACK:
+ if ( c->value_int < MINIMUM_SEARCH_STACK_DEPTH ) {
+ fprintf( stderr,
+ "%s: depth %d too small, using %d\n",
+ c->log, c->value_int, MINIMUM_SEARCH_STACK_DEPTH );
+ c->value_int = MINIMUM_SEARCH_STACK_DEPTH;
+ }
+ mdb->mi_search_stack_depth = c->value_int;
+ break;
+
+ case MDB_MAXREADERS:
+ mdb->mi_readers = c->value_int;
+ if ( mdb->mi_flags & MDB_IS_OPEN ) {
+ mdb->mi_flags |= MDB_RE_OPEN;
+ config_push_cleanup( c, mdb_cf_cleanup );
+ }
+ break;
+
+ case MDB_MAXSIZE:
+ mdb->mi_mapsize = c->value_ulong;
+ if ( mdb->mi_flags & MDB_IS_OPEN ) {
+ mdb->mi_flags |= MDB_RE_OPEN;
+ config_push_cleanup( c, mdb_cf_cleanup );
+ }
+ break;
+
+ case MDB_MULTIVAL:
+ rc = mdb_attr_multi_config( mdb, c->fname, c->lineno,
+ c->argc - 1, &c->argv[1], &c->reply);
+
+ if( rc != LDAP_SUCCESS ) return 1;
+ break;
+ }
+ return 0;
+}
+
+int mdb_back_init_cf( BackendInfo *bi )
+{
+ int rc;
+ bi->bi_cf_ocs = mdbocs;
+
+ rc = config_register_schema( mdbcfg, mdbocs );
+ if ( rc ) return rc;
+ return 0;
+}
diff --git a/servers/slapd/back-mdb/delete.c b/servers/slapd/back-mdb/delete.c
new file mode 100644
index 0000000..d4e6b8d
--- /dev/null
+++ b/servers/slapd/back-mdb/delete.c
@@ -0,0 +1,436 @@
+/* delete.c - mdb backend delete routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "lutil.h"
+#include "back-mdb.h"
+
+int
+mdb_delete( Operation *op, SlapReply *rs )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ struct berval pdn = {0, NULL};
+ Entry *e = NULL;
+ Entry *p = NULL;
+ int manageDSAit = get_manageDSAit( op );
+ AttributeDescription *children = slap_schema.si_ad_children;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+ MDB_txn *txn = NULL;
+ MDB_cursor *mc;
+ mdb_op_info opinfo = {{{ 0 }}}, *moi = &opinfo;
+
+ LDAPControl **preread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+
+ int parent_is_glue = 0;
+ int parent_is_leaf = 0;
+
+ Debug( LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(mdb_delete) ": %s\n",
+ op->o_req_dn.bv_val );
+
+ ctrls[num_ctrls] = 0;
+
+ /* begin transaction */
+ rs->sr_err = mdb_opinfo_get( op, mdb, 0, &moi );
+ rs->sr_text = NULL;
+ if( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_delete) ": txn_begin failed: "
+ "%s (%d)\n", mdb_strerror(rs->sr_err), rs->sr_err );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ txn = moi->moi_txn;
+
+ /* allocate CSN */
+ if ( BER_BVISNULL( &op->o_csn ) ) {
+ struct berval csn;
+ char csnbuf[LDAP_PVT_CSNSTR_BUFSIZE];
+
+ csn.bv_val = csnbuf;
+ csn.bv_len = sizeof(csnbuf);
+ slap_get_csn( op, &csn, 1 );
+ }
+
+ rs->sr_err = mdb_cursor_open( txn, mdb->mi_dn2id, &mc );
+ if ( rs->sr_err ) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ if ( !be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
+ dnParent( &op->o_req_ndn, &pdn );
+
+ /* get parent */
+ rs->sr_err = mdb_dn2entry( op, txn, mc, &pdn, &p, NULL, 1 );
+ switch( rs->sr_err ) {
+ case 0:
+ case MDB_NOTFOUND:
+ break;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ if ( rs->sr_err == MDB_NOTFOUND ) {
+ Debug( LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(mdb_delete) ": no such object %s\n",
+ op->o_req_dn.bv_val );
+
+ if ( p && !BER_BVISEMPTY( &p->e_name )) {
+ rs->sr_matched = ch_strdup( p->e_name.bv_val );
+ if ( is_entry_referral( p )) {
+ BerVarray ref = get_entry_referrals( op, p );
+ rs->sr_ref = referral_rewrite( ref, &p->e_name,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ } else {
+ rs->sr_ref = NULL;
+ }
+ } else {
+ rs->sr_ref = referral_rewrite( default_referral, NULL,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ }
+ if ( p ) {
+ mdb_entry_return( op, p );
+ p = NULL;
+ }
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+ }
+
+ /* get entry */
+ rs->sr_err = mdb_dn2entry( op, txn, mc, &op->o_req_ndn, &e, NULL, 0 );
+ switch( rs->sr_err ) {
+ case MDB_NOTFOUND:
+ e = p;
+ p = NULL;
+ case 0:
+ break;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* FIXME : dn2entry() should return non-glue entry */
+ if ( rs->sr_err == MDB_NOTFOUND || ( !manageDSAit && is_entry_glue( e ))) {
+ Debug( LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(mdb_delete) ": no such object %s\n",
+ op->o_req_dn.bv_val );
+
+ rs->sr_matched = ch_strdup( e->e_dn );
+ if ( is_entry_referral( e )) {
+ BerVarray ref = get_entry_referrals( op, e );
+ rs->sr_ref = referral_rewrite( ref, &e->e_name,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ } else {
+ rs->sr_ref = NULL;
+ }
+ mdb_entry_return( op, e );
+ e = NULL;
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+
+ if ( pdn.bv_len != 0 ) {
+ /* check parent for "children" acl */
+ rs->sr_err = access_allowed( op, p,
+ children, NULL, ACL_WDEL, NULL );
+
+ if ( !rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_delete) ": no write "
+ "access to parent\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to parent";
+ goto return_results;
+ }
+
+ } else {
+ /* no parent, must be root to delete */
+ if( ! be_isroot( op ) ) {
+ if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv )
+ || be_shadow_update( op ) ) {
+ p = (Entry *)&slap_entry_root;
+
+ /* check parent for "children" acl */
+ rs->sr_err = access_allowed( op, p,
+ children, NULL, ACL_WDEL, NULL );
+
+ p = NULL;
+
+ if ( !rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_delete)
+ ": no access to parent\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to parent";
+ goto return_results;
+ }
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_delete)
+ ": no parent and not root\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto return_results;
+ }
+ }
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+ rs->sr_err = access_allowed( op, e,
+ entry, NULL, ACL_WDEL, NULL );
+
+ if ( !rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_delete) ": no write access "
+ "to entry\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to entry";
+ goto return_results;
+ }
+
+ if ( !manageDSAit && is_entry_referral( e ) ) {
+ /* entry is a referral, don't allow delete */
+ rs->sr_ref = get_entry_referrals( op, e );
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_delete) ": entry is referral\n" );
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_matched = ch_strdup( e->e_name.bv_val );
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+
+ /* pre-read */
+ if( op->o_preread ) {
+ if( preread_ctrl == NULL ) {
+ preread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if( slap_read_controls( op, rs, e,
+ &slap_pre_read_bv, preread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_delete) ": pre-read "
+ "failed!\n" );
+ if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ rs->sr_text = NULL;
+
+ /* Can't do it if we have kids */
+ rs->sr_err = mdb_dn2id_children( op, txn, e );
+ if( rs->sr_err != MDB_NOTFOUND ) {
+ switch( rs->sr_err ) {
+ case 0:
+ Debug(LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(mdb_delete)
+ ": non-leaf %s\n",
+ op->o_req_dn.bv_val );
+ rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
+ rs->sr_text = "subordinate objects must be deleted first";
+ break;
+ default:
+ Debug(LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(mdb_delete)
+ ": has_children failed: %s (%d)\n",
+ mdb_strerror(rs->sr_err), rs->sr_err );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ }
+ goto return_results;
+ }
+
+ /* delete from dn2id */
+ rs->sr_err = mdb_dn2id_delete( op, mc, e->e_id, 1 );
+ mdb_cursor_close( mc );
+ if ( rs->sr_err != 0 ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_delete) ": dn2id failed: "
+ "%s (%d)\n", mdb_strerror(rs->sr_err), rs->sr_err );
+ rs->sr_text = "DN index delete failed";
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+
+ /* delete indices for old attributes */
+ rs->sr_err = mdb_index_entry_del( op, txn, e );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_delete) ": index failed: "
+ "%s (%d)\n", mdb_strerror(rs->sr_err), rs->sr_err );
+ rs->sr_text = "entry index delete failed";
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+
+ /* fixup delete CSN */
+ if ( !SLAP_SHADOW( op->o_bd )) {
+ struct berval vals[2];
+
+ assert( !BER_BVISNULL( &op->o_csn ) );
+ vals[0] = op->o_csn;
+ BER_BVZERO( &vals[1] );
+ rs->sr_err = mdb_index_values( op, txn, slap_schema.si_ad_entryCSN,
+ vals, 0, SLAP_INDEX_ADD_OP );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ rs->sr_text = "entryCSN index update failed";
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+ }
+
+ /* delete from id2entry */
+ rs->sr_err = mdb_id2entry_delete( op->o_bd, txn, e );
+ if ( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_delete) ": id2entry failed: "
+ "%s (%d)\n", mdb_strerror(rs->sr_err), rs->sr_err );
+ rs->sr_text = "entry delete failed";
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+
+ if ( pdn.bv_len != 0 ) {
+ parent_is_glue = is_entry_glue(p);
+ rs->sr_err = mdb_dn2id_children( op, txn, p );
+ if ( rs->sr_err != MDB_NOTFOUND ) {
+ switch( rs->sr_err ) {
+ case 0:
+ break;
+ default:
+ Debug(LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(mdb_delete)
+ ": has_children failed: %s (%d)\n",
+ mdb_strerror(rs->sr_err), rs->sr_err );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ parent_is_leaf = 1;
+ }
+ mdb_entry_return( op, p );
+ p = NULL;
+ }
+
+ if( moi == &opinfo ) {
+ LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
+ opinfo.moi_oe.oe_key = NULL;
+ if( op->o_noop ) {
+ mdb_txn_abort( txn );
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ txn = NULL;
+ goto return_results;
+ } else {
+ rs->sr_err = mdb_txn_commit( txn );
+ }
+ txn = NULL;
+ }
+
+ if( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_delete) ": txn_%s failed: %s (%d)\n",
+ op->o_noop ? "abort (no-op)" : "commit",
+ mdb_strerror(rs->sr_err), rs->sr_err );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "commit failed";
+
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_delete) ": deleted%s id=%08lx dn=\"%s\"\n",
+ op->o_noop ? " (no-op)" : "",
+ e->e_id, op->o_req_dn.bv_val );
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+ if ( rs->sr_err == LDAP_SUCCESS && parent_is_glue && parent_is_leaf ) {
+ op->o_delete_glue_parent = 1;
+ }
+
+ if ( p != NULL ) {
+ mdb_entry_return( op, p );
+ }
+
+ /* free entry */
+ if( e != NULL ) {
+ mdb_entry_return( op, e );
+ }
+
+ if( moi == &opinfo ) {
+ if( txn != NULL ) {
+ mdb_txn_abort( txn );
+ }
+ if ( opinfo.moi_oe.oe_key ) {
+ LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
+ }
+ } else {
+ moi->moi_ref--;
+ }
+
+ send_ldap_result( op, rs );
+ slap_graduate_commit_csn( op );
+
+ if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
+ slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
+ }
+
+#if 0
+ if( rs->sr_err == LDAP_SUCCESS && mdb->bi_txn_cp_kbyte ) {
+ TXN_CHECKPOINT( mdb->bi_dbenv,
+ mdb->bi_txn_cp_kbyte, mdb->bi_txn_cp_min, 0 );
+ }
+#endif
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-mdb/dn2entry.c b/servers/slapd/back-mdb/dn2entry.c
new file mode 100644
index 0000000..2262147
--- /dev/null
+++ b/servers/slapd/back-mdb/dn2entry.c
@@ -0,0 +1,79 @@
+/* dn2entry.c - routines to deal with the dn2id / id2entry glue */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+
+/*
+ * dn2entry - look up dn in the cache/indexes and return the corresponding
+ * entry. If the requested DN is not found and matched is TRUE, return info
+ * for the closest ancestor of the DN. Otherwise e is NULL.
+ */
+
+int
+mdb_dn2entry(
+ Operation *op,
+ MDB_txn *tid,
+ MDB_cursor *m2,
+ struct berval *dn,
+ Entry **e,
+ ID *nsubs,
+ int matched )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ int rc, rc2;
+ ID id = NOID;
+ struct berval mbv, nmbv;
+ MDB_cursor *mc;
+
+ Debug(LDAP_DEBUG_TRACE, "mdb_dn2entry(\"%s\")\n",
+ dn->bv_val ? dn->bv_val : "" );
+
+ *e = NULL;
+
+ rc = mdb_dn2id( op, tid, m2, dn, &id, nsubs, &mbv, &nmbv );
+ if ( rc ) {
+ if ( matched ) {
+ rc2 = mdb_cursor_open( tid, mdb->mi_id2entry, &mc );
+ if ( rc2 == MDB_SUCCESS ) {
+ rc2 = mdb_id2entry( op, mc, id, e );
+ mdb_cursor_close( mc );
+ }
+ }
+
+ } else {
+ rc = mdb_cursor_open( tid, mdb->mi_id2entry, &mc );
+ if ( rc == MDB_SUCCESS ) {
+ rc = mdb_id2entry( op, mc, id, e );
+ mdb_cursor_close(mc);
+ }
+ }
+ if ( *e ) {
+ (*e)->e_name = mbv;
+ if ( rc == MDB_SUCCESS )
+ ber_dupbv_x( &(*e)->e_nname, dn, op->o_tmpmemctx );
+ else
+ ber_dupbv_x( &(*e)->e_nname, &nmbv, op->o_tmpmemctx );
+ } else {
+ op->o_tmpfree( mbv.bv_val, op->o_tmpmemctx );
+ }
+
+ return rc;
+}
diff --git a/servers/slapd/back-mdb/dn2id.c b/servers/slapd/back-mdb/dn2id.c
new file mode 100644
index 0000000..68000ca
--- /dev/null
+++ b/servers/slapd/back-mdb/dn2id.c
@@ -0,0 +1,981 @@
+/* dn2id.c - routines to deal with the dn2id index */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+#include "idl.h"
+#include "lutil.h"
+
+/* Management routines for a hierarchically structured database.
+ *
+ * Instead of a ldbm-style dn2id database, we use a hierarchical one. Each
+ * entry in this database is a struct diskNode, keyed by entryID and with
+ * the data containing the RDN and entryID of the node's children. We use
+ * a B-Tree with sorted duplicates to store all the children of a node under
+ * the same key. Also, the first item under the key contains the entry's own
+ * rdn and the ID of the node's parent, to allow bottom-up tree traversal as
+ * well as top-down. To keep this info first in the list, the high bit of all
+ * subsequent nrdnlen's is always set. This means we can only accommodate
+ * RDNs up to length 32767, but that's fine since full DNs are already
+ * restricted to 8192.
+ *
+ * Also each child node contains a count of the number of entries in
+ * its subtree, appended after its entryID.
+ *
+ * The diskNode is a variable length structure. This definition is not
+ * directly usable for in-memory manipulation.
+ */
+typedef struct diskNode {
+ unsigned char nrdnlen[2];
+ char nrdn[1];
+ char rdn[1]; /* variable placement */
+ unsigned char entryID[sizeof(ID)]; /* variable placement */
+ /* unsigned char nsubs[sizeof(ID)]; in child nodes only */
+} diskNode;
+
+/* Sort function for the sorted duplicate data items of a dn2id key.
+ * Sorts based on normalized RDN, in length order.
+ */
+int
+mdb_dup_compare(
+ const MDB_val *usrkey,
+ const MDB_val *curkey
+)
+{
+ diskNode *un, *cn;
+ int rc, nrlen;
+
+ un = (diskNode *)usrkey->mv_data;
+ cn = (diskNode *)curkey->mv_data;
+
+ /* data is not aligned, cannot compare directly */
+ rc = un->nrdnlen[0] - cn->nrdnlen[0];
+ if ( rc ) return rc;
+ rc = un->nrdnlen[1] - cn->nrdnlen[1];
+ if ( rc ) return rc;
+
+ nrlen = ((un->nrdnlen[0] & 0x7f) << 8) | un->nrdnlen[1];
+ return strncmp( un->nrdn, cn->nrdn, nrlen );
+}
+
+/* We add two elements to the DN2ID database - a data item under the parent's
+ * entryID containing the child's RDN and entryID, and an item under the
+ * child's entryID containing the parent's entryID.
+ */
+int
+mdb_dn2id_add(
+ Operation *op,
+ MDB_cursor *mcp,
+ MDB_cursor *mcd,
+ ID pid,
+ ID nsubs,
+ int upsub,
+ Entry *e )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_val key, data;
+ ID nid;
+ int rc, rlen, nrlen;
+ diskNode *d;
+ char *ptr;
+
+ Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2id_add 0x%lx: \"%s\"\n",
+ e->e_id, e->e_ndn ? e->e_ndn : "" );
+
+ nrlen = dn_rdnlen( op->o_bd, &e->e_nname );
+ if (nrlen) {
+ rlen = dn_rdnlen( op->o_bd, &e->e_name );
+ } else {
+ nrlen = e->e_nname.bv_len;
+ rlen = e->e_name.bv_len;
+ }
+
+ d = op->o_tmpalloc(sizeof(diskNode) + rlen + nrlen + sizeof(ID), op->o_tmpmemctx);
+ d->nrdnlen[1] = nrlen & 0xff;
+ d->nrdnlen[0] = (nrlen >> 8) | 0x80;
+ ptr = lutil_strncopy( d->nrdn, e->e_nname.bv_val, nrlen );
+ *ptr++ = '\0';
+ ptr = lutil_strncopy( ptr, e->e_name.bv_val, rlen );
+ *ptr++ = '\0';
+ memcpy( ptr, &e->e_id, sizeof( ID ));
+ ptr += sizeof( ID );
+ memcpy( ptr, &nsubs, sizeof( ID ));
+
+ key.mv_size = sizeof(ID);
+ key.mv_data = &nid;
+
+ nid = pid;
+
+ /* Need to make dummy root node once. Subsequent attempts
+ * will fail harmlessly.
+ */
+ if ( pid == 0 ) {
+ diskNode dummy = {{0, 0}, "", "", ""};
+ data.mv_data = &dummy;
+ data.mv_size = sizeof(diskNode);
+
+ mdb_cursor_put( mcp, &key, &data, MDB_NODUPDATA );
+ }
+
+ data.mv_data = d;
+ data.mv_size = sizeof(diskNode) + rlen + nrlen + sizeof( ID );
+
+ /* Add our child node under parent's key */
+ rc = mdb_cursor_put( mcp, &key, &data, MDB_NODUPDATA );
+
+ /* Add our own node */
+ if (rc == 0) {
+ int flag = MDB_NODUPDATA;
+ nid = e->e_id;
+ /* drop subtree count */
+ data.mv_size -= sizeof( ID );
+ ptr -= sizeof( ID );
+ memcpy( ptr, &pid, sizeof( ID ));
+ d->nrdnlen[0] ^= 0x80;
+
+ if ((slapMode & SLAP_TOOL_MODE) || (e->e_id == mdb->mi_nextid))
+ flag |= MDB_APPEND;
+ rc = mdb_cursor_put( mcd, &key, &data, flag );
+ }
+ op->o_tmpfree( d, op->o_tmpmemctx );
+
+ /* Add our subtree count to all superiors */
+ if ( rc == 0 && upsub && pid ) {
+ ID subs;
+ nid = pid;
+ do {
+ /* Get parent's RDN */
+ rc = mdb_cursor_get( mcp, &key, &data, MDB_SET );
+ if ( !rc ) {
+ char *p2;
+ ptr = (char *)data.mv_data + data.mv_size - sizeof( ID );
+ memcpy( &nid, ptr, sizeof( ID ));
+ /* Get parent's node under grandparent */
+ d = data.mv_data;
+ rlen = ( d->nrdnlen[0] << 8 ) | d->nrdnlen[1];
+ p2 = op->o_tmpalloc( rlen + 2, op->o_tmpmemctx );
+ memcpy( p2, data.mv_data, rlen+2 );
+ *p2 ^= 0x80;
+ data.mv_data = p2;
+ rc = mdb_cursor_get( mcp, &key, &data, MDB_GET_BOTH );
+ op->o_tmpfree( p2, op->o_tmpmemctx );
+ if ( !rc ) {
+ /* Get parent's subtree count */
+ ptr = (char *)data.mv_data + data.mv_size - sizeof( ID );
+ memcpy( &subs, ptr, sizeof( ID ));
+ subs += nsubs;
+ p2 = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx );
+ memcpy( p2, data.mv_data, data.mv_size - sizeof( ID ));
+ memcpy( p2+data.mv_size - sizeof( ID ), &subs, sizeof( ID ));
+ data.mv_data = p2;
+ rc = mdb_cursor_put( mcp, &key, &data, MDB_CURRENT );
+ op->o_tmpfree( p2, op->o_tmpmemctx );
+ }
+ }
+ if ( rc )
+ break;
+ } while ( nid );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id_add 0x%lx: %d\n", e->e_id, rc );
+
+ return rc;
+}
+
+/* mc must have been set by mdb_dn2id */
+int
+mdb_dn2id_delete(
+ Operation *op,
+ MDB_cursor *mc,
+ ID id,
+ ID nsubs )
+{
+ ID nid;
+ char *ptr;
+ int rc;
+
+ Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2id_delete 0x%lx\n",
+ id );
+
+ /* Delete our ID from the parent's list */
+ rc = mdb_cursor_del( mc, 0 );
+
+ /* Delete our ID from the tree. With sorted duplicates, this
+ * will leave any child nodes still hanging around. This is OK
+ * for modrdn, which will add our info back in later.
+ */
+ if ( rc == 0 ) {
+ MDB_val key, data;
+ if ( nsubs ) {
+ mdb_cursor_get( mc, &key, NULL, MDB_GET_CURRENT );
+ memcpy( &nid, key.mv_data, sizeof( ID ));
+ }
+ key.mv_size = sizeof(ID);
+ key.mv_data = &id;
+ rc = mdb_cursor_get( mc, &key, &data, MDB_SET );
+ if ( rc == 0 )
+ rc = mdb_cursor_del( mc, 0 );
+ }
+
+ /* Delete our subtree count from all superiors */
+ if ( rc == 0 && nsubs && nid ) {
+ MDB_val key, data;
+ ID subs;
+ key.mv_data = &nid;
+ key.mv_size = sizeof( ID );
+ do {
+ rc = mdb_cursor_get( mc, &key, &data, MDB_SET );
+ if ( !rc ) {
+ char *p2;
+ diskNode *d;
+ int rlen;
+ ptr = (char *)data.mv_data + data.mv_size - sizeof( ID );
+ memcpy( &nid, ptr, sizeof( ID ));
+ /* Get parent's node under grandparent */
+ d = data.mv_data;
+ rlen = ( d->nrdnlen[0] << 8 ) | d->nrdnlen[1];
+ p2 = op->o_tmpalloc( rlen + 2, op->o_tmpmemctx );
+ memcpy( p2, data.mv_data, rlen+2 );
+ *p2 ^= 0x80;
+ data.mv_data = p2;
+ rc = mdb_cursor_get( mc, &key, &data, MDB_GET_BOTH );
+ op->o_tmpfree( p2, op->o_tmpmemctx );
+ if ( !rc ) {
+ /* Get parent's subtree count */
+ ptr = (char *)data.mv_data + data.mv_size - sizeof( ID );
+ memcpy( &subs, ptr, sizeof( ID ));
+ subs -= nsubs;
+ p2 = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx );
+ memcpy( p2, data.mv_data, data.mv_size - sizeof( ID ));
+ memcpy( p2+data.mv_size - sizeof( ID ), &subs, sizeof( ID ));
+ data.mv_data = p2;
+ rc = mdb_cursor_put( mc, &key, &data, MDB_CURRENT );
+ op->o_tmpfree( p2, op->o_tmpmemctx );
+ }
+
+ }
+ if ( rc )
+ break;
+ } while ( nid );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id_delete 0x%lx: %d\n", id, rc );
+ return rc;
+}
+
+/* return last found ID in *id if no match
+ * If mc is provided, it will be left pointing to the RDN's
+ * record under the parent's ID. If nsubs is provided, return
+ * the number of entries in this entry's subtree.
+ */
+int
+mdb_dn2id(
+ Operation *op,
+ MDB_txn *txn,
+ MDB_cursor *mc,
+ struct berval *in,
+ ID *id,
+ ID *nsubs,
+ struct berval *matched,
+ struct berval *nmatched )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_cursor *cursor;
+ MDB_dbi dbi = mdb->mi_dn2id;
+ MDB_val key, data;
+ int rc = 0, nrlen;
+ diskNode *d;
+ char *ptr;
+ char dn[SLAP_LDAPDN_MAXLEN];
+ ID pid, nid;
+ struct berval tmp;
+
+ Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2id(\"%s\")\n", in->bv_val ? in->bv_val : "" );
+
+ if ( matched ) {
+ matched->bv_val = dn + sizeof(dn) - 1;
+ matched->bv_len = 0;
+ *matched->bv_val-- = '\0';
+ }
+ if ( nmatched ) {
+ nmatched->bv_len = 0;
+ nmatched->bv_val = 0;
+ }
+
+ if ( !in->bv_len ) {
+ *id = 0;
+ nid = 0;
+ goto done;
+ }
+
+ tmp = *in;
+
+ if ( op->o_bd->be_nsuffix[0].bv_len ) {
+ nrlen = tmp.bv_len - op->o_bd->be_nsuffix[0].bv_len;
+ tmp.bv_val += nrlen;
+ tmp.bv_len = op->o_bd->be_nsuffix[0].bv_len;
+ } else {
+ for ( ptr = tmp.bv_val + tmp.bv_len - 1; ptr >= tmp.bv_val; ptr-- )
+ if (DN_SEPARATOR(*ptr))
+ break;
+ ptr++;
+ tmp.bv_len -= ptr - tmp.bv_val;
+ tmp.bv_val = ptr;
+ }
+ nid = 0;
+ key.mv_size = sizeof(ID);
+
+ if ( mc ) {
+ cursor = mc;
+ } else {
+ rc = mdb_cursor_open( txn, dbi, &cursor );
+ if ( rc ) goto done;
+ }
+
+ for (;;) {
+ key.mv_data = &pid;
+ pid = nid;
+
+ data.mv_size = sizeof(diskNode) + tmp.bv_len;
+ d = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx );
+ d->nrdnlen[1] = tmp.bv_len & 0xff;
+ d->nrdnlen[0] = (tmp.bv_len >> 8) | 0x80;
+ ptr = lutil_strncopy( d->nrdn, tmp.bv_val, tmp.bv_len );
+ *ptr = '\0';
+ data.mv_data = d;
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_GET_BOTH );
+ op->o_tmpfree( d, op->o_tmpmemctx );
+ if ( rc )
+ break;
+ ptr = (char *) data.mv_data + data.mv_size - 2*sizeof(ID);
+ memcpy( &nid, ptr, sizeof(ID));
+
+ /* grab the non-normalized RDN */
+ if ( matched ) {
+ int rlen;
+ d = data.mv_data;
+ rlen = data.mv_size - sizeof(diskNode) - tmp.bv_len - sizeof(ID);
+ matched->bv_len += rlen;
+ matched->bv_val -= rlen + 1;
+ ptr = lutil_strcopy( matched->bv_val, d->rdn + tmp.bv_len );
+ if ( pid ) {
+ *ptr = ',';
+ matched->bv_len++;
+ }
+ }
+ if ( nmatched ) {
+ nmatched->bv_val = tmp.bv_val;
+ }
+
+ if ( tmp.bv_val > in->bv_val ) {
+ for (ptr = tmp.bv_val - 2; ptr > in->bv_val &&
+ !DN_SEPARATOR(*ptr); ptr--) /* empty */;
+ if ( ptr >= in->bv_val ) {
+ if (DN_SEPARATOR(*ptr)) ptr++;
+ tmp.bv_len = tmp.bv_val - ptr - 1;
+ tmp.bv_val = ptr;
+ }
+ } else {
+ break;
+ }
+ }
+ *id = nid;
+ /* return subtree count if requested */
+ if ( !rc && nsubs ) {
+ ptr = (char *)data.mv_data + data.mv_size - sizeof(ID);
+ memcpy( nsubs, ptr, sizeof( ID ));
+ }
+ if ( !mc )
+ mdb_cursor_close( cursor );
+done:
+ if ( matched ) {
+ if ( matched->bv_len ) {
+ ptr = op->o_tmpalloc( matched->bv_len+1, op->o_tmpmemctx );
+ strcpy( ptr, matched->bv_val );
+ matched->bv_val = ptr;
+ } else {
+ if ( BER_BVISEMPTY( &op->o_bd->be_nsuffix[0] ) && !nid ) {
+ ber_dupbv( matched, (struct berval *)&slap_empty_bv );
+ } else {
+ matched->bv_val = NULL;
+ }
+ }
+ }
+ if ( nmatched ) {
+ if ( nmatched->bv_val ) {
+ nmatched->bv_len = in->bv_len - (nmatched->bv_val - in->bv_val);
+ } else {
+ *nmatched = slap_empty_bv;
+ }
+ }
+
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id: get failed: %s (%d)\n",
+ mdb_strerror( rc ), rc );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2id: got id=0x%lx\n",
+ nid );
+ }
+
+ return rc;
+}
+
+/* return IDs from root to parent of DN */
+int
+mdb_dn2sups(
+ Operation *op,
+ MDB_txn *txn,
+ struct berval *in,
+ ID *ids )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_cursor *cursor;
+ MDB_dbi dbi = mdb->mi_dn2id;
+ MDB_val key, data;
+ int rc = 0, nrlen;
+ diskNode *d;
+ char *ptr;
+ ID pid, nid;
+ struct berval tmp;
+
+ Debug( LDAP_DEBUG_TRACE, "=> mdb_dn2sups(\"%s\")\n", in->bv_val );
+
+ if ( !in->bv_len ) {
+ goto done;
+ }
+
+ tmp = *in;
+
+ nrlen = tmp.bv_len - op->o_bd->be_nsuffix[0].bv_len;
+ tmp.bv_val += nrlen;
+ tmp.bv_len = op->o_bd->be_nsuffix[0].bv_len;
+ nid = 0;
+ key.mv_size = sizeof(ID);
+
+ rc = mdb_cursor_open( txn, dbi, &cursor );
+ if ( rc ) goto done;
+
+ for (;;) {
+ key.mv_data = &pid;
+ pid = nid;
+
+ data.mv_size = sizeof(diskNode) + tmp.bv_len;
+ d = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx );
+ d->nrdnlen[1] = tmp.bv_len & 0xff;
+ d->nrdnlen[0] = (tmp.bv_len >> 8) | 0x80;
+ ptr = lutil_strncopy( d->nrdn, tmp.bv_val, tmp.bv_len );
+ *ptr = '\0';
+ data.mv_data = d;
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_GET_BOTH );
+ op->o_tmpfree( d, op->o_tmpmemctx );
+ if ( rc )
+ break;
+ ptr = (char *) data.mv_data + data.mv_size - 2*sizeof(ID);
+ memcpy( &nid, ptr, sizeof(ID));
+
+ if ( pid )
+ mdb_idl_insert( ids, pid );
+
+ if ( tmp.bv_val > in->bv_val ) {
+ for (ptr = tmp.bv_val - 2; ptr > in->bv_val &&
+ !DN_SEPARATOR(*ptr); ptr--) /* empty */;
+ if ( ptr >= in->bv_val ) {
+ if (DN_SEPARATOR(*ptr)) ptr++;
+ tmp.bv_len = tmp.bv_val - ptr - 1;
+ tmp.bv_val = ptr;
+ }
+ } else {
+ break;
+ }
+ }
+ mdb_cursor_close( cursor );
+done:
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "<= mdb_dn2sups: get failed: %s (%d)\n",
+ mdb_strerror( rc ), rc );
+ }
+
+ return rc;
+}
+
+int
+mdb_dn2id_children(
+ Operation *op,
+ MDB_txn *txn,
+ Entry *e )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_dbi dbi = mdb->mi_dn2id;
+ MDB_val key, data;
+ MDB_cursor *cursor;
+ int rc;
+ ID id;
+
+ key.mv_size = sizeof(ID);
+ key.mv_data = &id;
+ id = e->e_id;
+
+ rc = mdb_cursor_open( txn, dbi, &cursor );
+ if ( rc ) return rc;
+
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
+ if ( rc == 0 ) {
+ size_t dkids;
+ rc = mdb_cursor_count( cursor, &dkids );
+ if ( rc == 0 ) {
+ if ( dkids < 2 ) rc = MDB_NOTFOUND;
+ }
+ }
+ mdb_cursor_close( cursor );
+ return rc;
+}
+
+int
+mdb_id2name(
+ Operation *op,
+ MDB_txn *txn,
+ MDB_cursor **cursp,
+ ID id,
+ struct berval *name,
+ struct berval *nname )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_dbi dbi = mdb->mi_dn2id;
+ MDB_val key, data;
+ MDB_cursor *cursor;
+ int rc, len, nlen;
+ char dn[SLAP_LDAPDN_MAXLEN], ndn[SLAP_LDAPDN_MAXLEN], *ptr;
+ char *dptr, *nptr;
+ diskNode *d;
+
+ key.mv_size = sizeof(ID);
+
+ if ( !*cursp ) {
+ rc = mdb_cursor_open( txn, dbi, cursp );
+ if ( rc ) return rc;
+ }
+ cursor = *cursp;
+
+ len = 0;
+ nlen = 0;
+ dptr = dn;
+ nptr = ndn;
+ while (id) {
+ unsigned int nrlen, rlen;
+ key.mv_data = &id;
+ data.mv_size = 0;
+ data.mv_data = "";
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
+ if ( rc ) break;
+ ptr = data.mv_data;
+ ptr += data.mv_size - sizeof(ID);
+ memcpy( &id, ptr, sizeof(ID) );
+ d = data.mv_data;
+ nrlen = (d->nrdnlen[0] << 8) | d->nrdnlen[1];
+ rlen = data.mv_size - sizeof(diskNode) - nrlen;
+ assert( nrlen < 1024 && rlen < 1024 ); /* FIXME: Sanity check */
+ if (nptr > ndn) {
+ *nptr++ = ',';
+ *dptr++ = ',';
+ }
+ /* copy name and trailing NUL */
+ memcpy( nptr, d->nrdn, nrlen+1 );
+ memcpy( dptr, d->nrdn+nrlen+1, rlen+1 );
+ nptr += nrlen;
+ dptr += rlen;
+ }
+ if ( rc == 0 ) {
+ name->bv_len = dptr - dn;
+ nname->bv_len = nptr - ndn;
+ name->bv_val = op->o_tmpalloc( name->bv_len + 1, op->o_tmpmemctx );
+ nname->bv_val = op->o_tmpalloc( nname->bv_len + 1, op->o_tmpmemctx );
+ memcpy( name->bv_val, dn, name->bv_len );
+ name->bv_val[name->bv_len] = '\0';
+ memcpy( nname->bv_val, ndn, nname->bv_len );
+ nname->bv_val[nname->bv_len] = '\0';
+ }
+ return rc;
+}
+
+/* Find each id in ids that is a child of base and move it to res.
+ */
+int
+mdb_idscope(
+ Operation *op,
+ MDB_txn *txn,
+ ID base,
+ ID *ids,
+ ID *res )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_dbi dbi = mdb->mi_dn2id;
+ MDB_val key, data;
+ MDB_cursor *cursor;
+ ID ida, id, cid = 0, ci0 = 0, idc = 0;
+ char *ptr;
+ int rc, copy;
+
+ key.mv_size = sizeof(ID);
+
+ MDB_IDL_ZERO( res );
+
+ rc = mdb_cursor_open( txn, dbi, &cursor );
+ if ( rc ) return rc;
+
+ /* first see if base has any children at all */
+ key.mv_data = &base;
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
+ if ( rc ) {
+ goto leave;
+ }
+ {
+ size_t dkids;
+ rc = mdb_cursor_count( cursor, &dkids );
+ if ( rc == 0 ) {
+ if ( dkids < 2 ) {
+ goto leave;
+ }
+ }
+ }
+
+ ida = mdb_idl_first( ids, &cid );
+
+ /* Don't bother moving out of ids if it's a range */
+ if (!MDB_IDL_IS_RANGE(ids)) {
+ idc = ids[0];
+ ci0 = cid;
+ }
+
+ while (ida != NOID) {
+ copy = 1;
+ id = ida;
+ while (id) {
+ key.mv_data = &id;
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
+ if ( rc ) {
+ /* not found, drop this from ids */
+ copy = 0;
+ break;
+ }
+ ptr = data.mv_data;
+ ptr += data.mv_size - sizeof(ID);
+ memcpy( &id, ptr, sizeof(ID) );
+ if ( id == base ) {
+ if ( res[0] >= MDB_idl_db_max ) {
+ /* too many aliases in scope. Fallback to range */
+ MDB_IDL_RANGE( res, MDB_IDL_FIRST( ids ), MDB_IDL_LAST( ids ));
+ goto leave;
+ }
+ res[0]++;
+ res[res[0]] = ida;
+ copy = 0;
+ break;
+ }
+ if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
+ break;
+ }
+ if (idc) {
+ if (copy) {
+ if (ci0 != cid)
+ ids[ci0] = ids[cid];
+ ci0++;
+ } else
+ idc--;
+ }
+ ida = mdb_idl_next( ids, &cid );
+ }
+ if (!MDB_IDL_IS_RANGE( ids ))
+ ids[0] = idc;
+
+leave:
+ mdb_cursor_close( cursor );
+ return rc;
+}
+
+/* See if base is a child of any of the scopes
+ */
+int
+mdb_idscopes(
+ Operation *op,
+ IdScopes *isc )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_dbi dbi = mdb->mi_dn2id;
+ MDB_val key, data;
+ ID id, prev;
+ ID2 id2;
+ char *ptr;
+ int rc = 0;
+ unsigned int x;
+ unsigned int nrlen, rlen;
+ diskNode *d;
+
+ key.mv_size = sizeof(ID);
+
+ if ( !isc->mc ) {
+ rc = mdb_cursor_open( isc->mt, dbi, &isc->mc );
+ if ( rc ) return rc;
+ }
+
+ id = isc->id;
+
+ /* Catch entries from deref'd aliases */
+ x = mdb_id2l_search( isc->scopes, id );
+ if ( x <= isc->scopes[0].mid && isc->scopes[x].mid == id ) {
+ isc->nscope = x;
+ return MDB_SUCCESS;
+ }
+
+ isc->sctmp[0].mid = 0;
+ while (id) {
+ if ( !rc ) {
+ key.mv_data = &id;
+ rc = mdb_cursor_get( isc->mc, &key, &data, MDB_SET );
+ if ( rc )
+ return rc;
+
+ /* save RDN info */
+ }
+ d = data.mv_data;
+ nrlen = (d->nrdnlen[0] << 8) | d->nrdnlen[1];
+ rlen = data.mv_size - sizeof(diskNode) - nrlen;
+ isc->nrdns[isc->numrdns].bv_len = nrlen;
+ isc->nrdns[isc->numrdns].bv_val = d->nrdn;
+ isc->rdns[isc->numrdns].bv_len = rlen;
+ isc->rdns[isc->numrdns].bv_val = d->nrdn+nrlen+1;
+ isc->numrdns++;
+
+ if (!rc && id != isc->id) {
+ /* remember our chain of parents */
+ id2.mid = id;
+ id2.mval = data;
+ mdb_id2l_insert( isc->sctmp, &id2 );
+ }
+ ptr = data.mv_data;
+ ptr += data.mv_size - sizeof(ID);
+ prev = id;
+ memcpy( &id, ptr, sizeof(ID) );
+ /* If we didn't advance, some parent is missing */
+ if ( id == prev )
+ return MDB_NOTFOUND;
+
+ x = mdb_id2l_search( isc->scopes, id );
+ if ( x <= isc->scopes[0].mid && isc->scopes[x].mid == id ) {
+ if ( !isc->scopes[x].mval.mv_data ) {
+ /* This node is in scope, add parent chain to scope */
+ int i;
+ for ( i = 1; i <= isc->sctmp[0].mid; i++ ) {
+ rc = mdb_id2l_insert( isc->scopes, &isc->sctmp[i] );
+ if ( rc )
+ break;
+ }
+ /* check id again since inserts may have changed its position */
+ if ( isc->scopes[x].mid != id )
+ x = mdb_id2l_search( isc->scopes, id );
+ isc->nscope = x;
+ return MDB_SUCCESS;
+ }
+ data = isc->scopes[x].mval;
+ rc = 1;
+ }
+ if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
+ break;
+ }
+ return MDB_SUCCESS;
+}
+
+/* See if ID is a child of any of the scopes,
+ * return MDB_KEYEXIST if so.
+ */
+int
+mdb_idscopechk(
+ Operation *op,
+ IdScopes *isc )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_val key, data;
+ ID id, prev;
+ char *ptr;
+ int rc = 0;
+ unsigned int x;
+
+ key.mv_size = sizeof(ID);
+
+ if ( !isc->mc ) {
+ rc = mdb_cursor_open( isc->mt, mdb->mi_dn2id, &isc->mc );
+ if ( rc ) return rc;
+ }
+
+ id = isc->id;
+
+ while (id) {
+ if ( !rc ) {
+ key.mv_data = &id;
+ rc = mdb_cursor_get( isc->mc, &key, &data, MDB_SET );
+ if ( rc )
+ return rc;
+ }
+
+ ptr = data.mv_data;
+ ptr += data.mv_size - sizeof(ID);
+ prev = id;
+ memcpy( &id, ptr, sizeof(ID) );
+ /* If we didn't advance, some parent is missing */
+ if ( id == prev )
+ return MDB_NOTFOUND;
+
+ x = mdb_id2l_search( isc->scopes, id );
+ if ( x <= isc->scopes[0].mid && isc->scopes[x].mid == id )
+ return MDB_KEYEXIST;
+ }
+ return MDB_SUCCESS;
+}
+
+int
+mdb_dn2id_walk(
+ Operation *op,
+ IdScopes *isc
+)
+{
+ MDB_val key, data;
+ diskNode *d;
+ char *ptr;
+ int rc, n;
+ ID nsubs;
+
+ if ( !isc->numrdns ) {
+ key.mv_data = &isc->id;
+ key.mv_size = sizeof(ID);
+ rc = mdb_cursor_get( isc->mc, &key, &data, MDB_SET );
+ isc->scopes[0].mid = isc->id;
+ isc->numrdns++;
+ isc->nscope = 0;
+ /* skip base if not a subtree walk */
+ if ( isc->oscope == LDAP_SCOPE_SUBTREE ||
+ isc->oscope == LDAP_SCOPE_BASE )
+ return rc;
+ }
+ if ( isc->oscope == LDAP_SCOPE_BASE )
+ return MDB_NOTFOUND;
+
+ for (;;) {
+ /* Get next sibling */
+ rc = mdb_cursor_get( isc->mc, &key, &data, MDB_NEXT_DUP );
+ if ( !rc ) {
+ ptr = (char *)data.mv_data + data.mv_size - 2*sizeof(ID);
+ d = data.mv_data;
+ memcpy( &isc->id, ptr, sizeof(ID));
+
+ /* If we're pushing down, see if there's any children to find */
+ if ( isc->nscope ) {
+ ptr += sizeof(ID);
+ memcpy( &nsubs, ptr, sizeof(ID));
+ /* No children, go to next sibling */
+ if ( nsubs < 2 )
+ continue;
+ }
+ n = isc->numrdns;
+ isc->scopes[n].mid = isc->id;
+ n--;
+ isc->nrdns[n].bv_len = ((d->nrdnlen[0] & 0x7f) << 8) | d->nrdnlen[1];
+ isc->nrdns[n].bv_val = d->nrdn;
+ isc->rdns[n].bv_val = d->nrdn+isc->nrdns[n].bv_len+1;
+ isc->rdns[n].bv_len = data.mv_size - sizeof(diskNode) - isc->nrdns[n].bv_len - sizeof(ID);
+ /* return this ID to caller */
+ if ( !isc->nscope )
+ break;
+
+ /* push down to child */
+ key.mv_data = &isc->id;
+ mdb_cursor_get( isc->mc, &key, &data, MDB_SET );
+ isc->nscope = 0;
+ isc->numrdns++;
+ continue;
+
+ } else if ( rc == MDB_NOTFOUND ) {
+ if ( !isc->nscope && isc->oscope != LDAP_SCOPE_ONELEVEL ) {
+ /* reset to first dup */
+ mdb_cursor_get( isc->mc, &key, NULL, MDB_GET_CURRENT );
+ mdb_cursor_get( isc->mc, &key, &data, MDB_SET );
+ isc->nscope = 1;
+ continue;
+ } else {
+ isc->numrdns--;
+ /* stack is empty? */
+ if ( !isc->numrdns )
+ break;
+ /* pop up to prev node */
+ n = isc->numrdns - 1;
+ key.mv_data = &isc->scopes[n].mid;
+ key.mv_size = sizeof(ID);
+ data.mv_data = isc->nrdns[n].bv_val - 2;
+ data.mv_size = 1; /* just needs to be non-zero, mdb_dup_compare doesn't care */
+ mdb_cursor_get( isc->mc, &key, &data, MDB_GET_BOTH );
+ continue;
+ }
+ } else {
+ break;
+ }
+ }
+ return rc;
+}
+
+/* restore the nrdn/rdn pointers after a txn reset */
+void mdb_dn2id_wrestore (
+ Operation *op,
+ IdScopes *isc
+)
+{
+ MDB_val key, data;
+ diskNode *d;
+ int rc, n, nrlen;
+ char *ptr;
+
+ /* We only need to restore up to the n-1th element,
+ * the nth element will be replaced anyway
+ */
+ key.mv_size = sizeof(ID);
+ for ( n=0; n<isc->numrdns-1; n++ ) {
+ key.mv_data = &isc->scopes[n+1].mid;
+ rc = mdb_cursor_get( isc->mc, &key, &data, MDB_SET );
+ if ( rc )
+ continue;
+ /* we can't use this data directly since its nrlen
+ * is missing the high bit setting, so copy it and
+ * set it properly. we just copy enough to satisfy
+ * mdb_dup_compare.
+ */
+ d = data.mv_data;
+ nrlen = ((d->nrdnlen[0] & 0x7f) << 8) | d->nrdnlen[1];
+ ptr = op->o_tmpalloc( nrlen+2, op->o_tmpmemctx );
+ memcpy( ptr, data.mv_data, nrlen+2 );
+ key.mv_data = &isc->scopes[n].mid;
+ data.mv_data = ptr;
+ data.mv_size = 1;
+ *ptr |= 0x80;
+ mdb_cursor_get( isc->mc, &key, &data, MDB_GET_BOTH );
+ op->o_tmpfree( ptr, op->o_tmpmemctx );
+
+ /* now we're back to where we wanted to be */
+ d = data.mv_data;
+ isc->nrdns[n].bv_val = d->nrdn;
+ isc->rdns[n].bv_val = d->nrdn+isc->nrdns[n].bv_len+1;
+ }
+}
diff --git a/servers/slapd/back-mdb/extended.c b/servers/slapd/back-mdb/extended.c
new file mode 100644
index 0000000..9ede8b7
--- /dev/null
+++ b/servers/slapd/back-mdb/extended.c
@@ -0,0 +1,54 @@
+/* extended.c - mdb backend extended routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+#include "lber_pvt.h"
+
+static struct exop {
+ struct berval *oid;
+ BI_op_extended *extended;
+} exop_table[] = {
+ { NULL, NULL }
+};
+
+int
+mdb_extended( Operation *op, SlapReply *rs )
+/* struct berval *reqoid,
+ struct berval *reqdata,
+ char **rspoid,
+ struct berval **rspdata,
+ LDAPControl *** rspctrls,
+ const char** text,
+ BerVarray *refs
+) */
+{
+ int i;
+
+ for( i=0; exop_table[i].extended != NULL; i++ ) {
+ if( ber_bvcmp( exop_table[i].oid, &op->oq_extended.rs_reqoid ) == 0 ) {
+ return (exop_table[i].extended)( op, rs );
+ }
+ }
+
+ rs->sr_text = "not supported within naming context";
+ return rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+}
+
diff --git a/servers/slapd/back-mdb/filterindex.c b/servers/slapd/back-mdb/filterindex.c
new file mode 100644
index 0000000..6d2c487
--- /dev/null
+++ b/servers/slapd/back-mdb/filterindex.c
@@ -0,0 +1,1173 @@
+/* filterindex.c - generate the list of candidate entries from a filter */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+#include "idl.h"
+#ifdef LDAP_COMP_MATCH
+#include <component.h>
+#endif
+
+static int presence_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ AttributeDescription *desc,
+ ID *ids );
+
+static int equality_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ AttributeAssertion *ava,
+ ID *ids,
+ ID *tmp );
+static int inequality_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ AttributeAssertion *ava,
+ ID *ids,
+ ID *tmp,
+ int gtorlt );
+static int approx_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ AttributeAssertion *ava,
+ ID *ids,
+ ID *tmp );
+static int substring_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ SubstringsAssertion *sub,
+ ID *ids,
+ ID *tmp );
+
+static int list_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ Filter *flist,
+ int ftype,
+ ID *ids,
+ ID *tmp,
+ ID *stack );
+
+static int
+ext_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ MatchingRuleAssertion *mra,
+ ID *ids,
+ ID *tmp,
+ ID *stack);
+
+#ifdef LDAP_COMP_MATCH
+static int
+comp_candidates (
+ Operation *op,
+ MDB_txn *rtxn,
+ MatchingRuleAssertion *mra,
+ ComponentFilter *f,
+ ID *ids,
+ ID *tmp,
+ ID *stack);
+
+static int
+ava_comp_candidates (
+ Operation *op,
+ MDB_txn *rtxn,
+ AttributeAssertion *ava,
+ AttributeAliasing *aa,
+ ID *ids,
+ ID *tmp,
+ ID *stack);
+#endif
+
+int
+mdb_filter_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ Filter *f,
+ ID *ids,
+ ID *tmp,
+ ID *stack )
+{
+ int rc = 0;
+#ifdef LDAP_COMP_MATCH
+ AttributeAliasing *aa;
+#endif
+ Debug( LDAP_DEBUG_FILTER, "=> mdb_filter_candidates\n" );
+
+ if ( f->f_choice & SLAPD_FILTER_UNDEFINED ) {
+ MDB_IDL_ZERO( ids );
+ goto out;
+ }
+
+ switch ( f->f_choice ) {
+ case SLAPD_FILTER_COMPUTED:
+ switch( f->f_result ) {
+ case SLAPD_COMPARE_UNDEFINED:
+ /* This technically is not the same as FALSE, but it
+ * certainly will produce no matches.
+ */
+ /* FALL THRU */
+ case LDAP_COMPARE_FALSE:
+ MDB_IDL_ZERO( ids );
+ break;
+ case LDAP_COMPARE_TRUE:
+ MDB_IDL_ALL( ids );
+ break;
+ case LDAP_SUCCESS:
+ /* this is a pre-computed scope, leave it alone */
+ break;
+ }
+ break;
+ case LDAP_FILTER_PRESENT:
+ Debug( LDAP_DEBUG_FILTER, "\tPRESENT\n" );
+ rc = presence_candidates( op, rtxn, f->f_desc, ids );
+ break;
+
+ case LDAP_FILTER_EQUALITY:
+ Debug( LDAP_DEBUG_FILTER, "\tEQUALITY\n" );
+#ifdef LDAP_COMP_MATCH
+ if ( is_aliased_attribute && ( aa = is_aliased_attribute ( f->f_ava->aa_desc ) ) ) {
+ rc = ava_comp_candidates ( op, rtxn, f->f_ava, aa, ids, tmp, stack );
+ }
+ else
+#endif
+ {
+ rc = equality_candidates( op, rtxn, f->f_ava, ids, tmp );
+ }
+ break;
+
+ case LDAP_FILTER_APPROX:
+ Debug( LDAP_DEBUG_FILTER, "\tAPPROX\n" );
+ rc = approx_candidates( op, rtxn, f->f_ava, ids, tmp );
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ Debug( LDAP_DEBUG_FILTER, "\tSUBSTRINGS\n" );
+ rc = substring_candidates( op, rtxn, f->f_sub, ids, tmp );
+ break;
+
+ case LDAP_FILTER_GE:
+ /* if no GE index, use pres */
+ Debug( LDAP_DEBUG_FILTER, "\tGE\n" );
+ if( f->f_ava->aa_desc->ad_type->sat_ordering &&
+ ( f->f_ava->aa_desc->ad_type->sat_ordering->smr_usage & SLAP_MR_ORDERED_INDEX ) )
+ rc = inequality_candidates( op, rtxn, f->f_ava, ids, tmp, LDAP_FILTER_GE );
+ else
+ rc = presence_candidates( op, rtxn, f->f_ava->aa_desc, ids );
+ break;
+
+ case LDAP_FILTER_LE:
+ /* if no LE index, use pres */
+ Debug( LDAP_DEBUG_FILTER, "\tLE\n" );
+ if( f->f_ava->aa_desc->ad_type->sat_ordering &&
+ ( f->f_ava->aa_desc->ad_type->sat_ordering->smr_usage & SLAP_MR_ORDERED_INDEX ) )
+ rc = inequality_candidates( op, rtxn, f->f_ava, ids, tmp, LDAP_FILTER_LE );
+ else
+ rc = presence_candidates( op, rtxn, f->f_ava->aa_desc, ids );
+ break;
+
+ case LDAP_FILTER_NOT:
+ /* no indexing to support NOT filters */
+ Debug( LDAP_DEBUG_FILTER, "\tNOT\n" );
+ MDB_IDL_ALL( ids );
+ break;
+
+ case LDAP_FILTER_AND:
+ Debug( LDAP_DEBUG_FILTER, "\tAND\n" );
+ rc = list_candidates( op, rtxn,
+ f->f_and, LDAP_FILTER_AND, ids, tmp, stack );
+ break;
+
+ case LDAP_FILTER_OR:
+ Debug( LDAP_DEBUG_FILTER, "\tOR\n" );
+ rc = list_candidates( op, rtxn,
+ f->f_or, LDAP_FILTER_OR, ids, tmp, stack );
+ break;
+ case LDAP_FILTER_EXT:
+ Debug( LDAP_DEBUG_FILTER, "\tEXT\n" );
+ rc = ext_candidates( op, rtxn, f->f_mra, ids, tmp, stack );
+ break;
+ default:
+ Debug( LDAP_DEBUG_FILTER, "\tUNKNOWN %lu\n",
+ (unsigned long) f->f_choice );
+ /* Must not return NULL, otherwise extended filters break */
+ MDB_IDL_ALL( ids );
+ }
+ if ( ids[2] == NOID && MDB_IDL_IS_RANGE( ids )) {
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ ID last;
+
+ if ( mdb->mi_nextid ) {
+ last = mdb->mi_nextid;
+ } else {
+ MDB_cursor *mc;
+ MDB_val key;
+
+ last = 0;
+ rc = mdb_cursor_open( rtxn, mdb->mi_id2entry, &mc );
+ if ( !rc ) {
+ rc = mdb_cursor_get( mc, &key, NULL, MDB_LAST );
+ if ( !rc )
+ memcpy( &last, key.mv_data, sizeof( last ));
+ mdb_cursor_close( mc );
+ }
+ }
+ if ( last ) {
+ ids[2] = last;
+ } else {
+ MDB_IDL_ZERO( ids );
+ }
+ }
+
+out:
+ Debug( LDAP_DEBUG_FILTER,
+ "<= mdb_filter_candidates: id=%ld first=%ld last=%ld\n",
+ (long) ids[0],
+ (long) MDB_IDL_FIRST( ids ),
+ (long) MDB_IDL_LAST( ids ) );
+
+ return rc;
+}
+
+#ifdef LDAP_COMP_MATCH
+static int
+comp_list_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ MatchingRuleAssertion* mra,
+ ComponentFilter *flist,
+ int ftype,
+ ID *ids,
+ ID *tmp,
+ ID *save )
+{
+ int rc = 0;
+ ComponentFilter *f;
+
+ Debug( LDAP_DEBUG_FILTER, "=> comp_list_candidates 0x%x\n", ftype );
+ for ( f = flist; f != NULL; f = f->cf_next ) {
+ /* ignore precomputed scopes */
+ if ( f->cf_choice == SLAPD_FILTER_COMPUTED &&
+ f->cf_result == LDAP_SUCCESS ) {
+ continue;
+ }
+ MDB_IDL_ZERO( save );
+ rc = comp_candidates( op, rtxn, mra, f, save, tmp, save+MDB_idl_um_size );
+
+ if ( rc != 0 ) {
+ if ( ftype == LDAP_COMP_FILTER_AND ) {
+ rc = 0;
+ continue;
+ }
+ break;
+ }
+
+ if ( ftype == LDAP_COMP_FILTER_AND ) {
+ if ( f == flist ) {
+ MDB_IDL_CPY( ids, save );
+ } else {
+ mdb_idl_intersection( ids, save );
+ }
+ if( MDB_IDL_IS_ZERO( ids ) )
+ break;
+ } else {
+ if ( f == flist ) {
+ MDB_IDL_CPY( ids, save );
+ } else {
+ mdb_idl_union( ids, save );
+ }
+ }
+ }
+
+ if( rc == LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= comp_list_candidates: id=%ld first=%ld last=%ld\n",
+ (long) ids[0],
+ (long) MDB_IDL_FIRST(ids),
+ (long) MDB_IDL_LAST(ids) );
+
+ } else {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= comp_list_candidates: undefined rc=%d\n",
+ rc );
+ }
+
+ return rc;
+}
+
+static int
+comp_equality_candidates (
+ Operation *op,
+ MDB_txn *rtxn,
+ MatchingRuleAssertion *mra,
+ ComponentAssertion *ca,
+ ID *ids,
+ ID *tmp,
+ ID *stack)
+{
+ MDB_dbi dbi;
+ int i;
+ int rc;
+ slap_mask_t mask;
+ struct berval prefix = {0, NULL};
+ struct berval *keys = NULL;
+ MatchingRule *mr = mra->ma_rule;
+ Syntax *sat_syntax;
+ ComponentReference* cr_list, *cr;
+ AttrInfo *ai;
+
+ MDB_IDL_ALL( ids );
+
+ if ( !ca->ca_comp_ref )
+ return 0;
+
+ ai = mdb_attr_mask( op->o_bd->be_private, mra->ma_desc );
+ if( ai ) {
+ cr_list = ai->ai_cr;
+ }
+ else {
+ return 0;
+ }
+ /* find a component reference to be indexed */
+ sat_syntax = ca->ca_ma_rule->smr_syntax;
+ for ( cr = cr_list ; cr ; cr = cr->cr_next ) {
+ if ( cr->cr_string.bv_len == ca->ca_comp_ref->cr_string.bv_len &&
+ strncmp( cr->cr_string.bv_val, ca->ca_comp_ref->cr_string.bv_val,cr->cr_string.bv_len ) == 0 )
+ break;
+ }
+
+ if ( !cr )
+ return 0;
+
+ rc = mdb_index_param( op->o_bd, mra->ma_desc, LDAP_FILTER_EQUALITY,
+ &dbi, &mask, &prefix );
+
+ if( rc != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ if( !mr ) {
+ return 0;
+ }
+
+ if( !mr->smr_filter ) {
+ return 0;
+ }
+
+ rc = (ca->ca_ma_rule->smr_filter)(
+ LDAP_FILTER_EQUALITY,
+ cr->cr_indexmask,
+ sat_syntax,
+ ca->ca_ma_rule,
+ &prefix,
+ &ca->ca_ma_value,
+ &keys, op->o_tmpmemctx );
+
+ if( rc != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ if( keys == NULL ) {
+ return 0;
+ }
+ for ( i= 0; keys[i].bv_val != NULL; i++ ) {
+ rc = mdb_key_read( op->o_bd, rtxn, dbi, &keys[i], tmp, NULL, 0 );
+
+ if( rc == MDB_NOTFOUND ) {
+ MDB_IDL_ZERO( ids );
+ rc = 0;
+ break;
+ } else if( rc != LDAP_SUCCESS ) {
+ break;
+ }
+
+ if( MDB_IDL_IS_ZERO( tmp ) ) {
+ MDB_IDL_ZERO( ids );
+ break;
+ }
+
+ if ( i == 0 ) {
+ MDB_IDL_CPY( ids, tmp );
+ } else {
+ mdb_idl_intersection( ids, tmp );
+ }
+
+ if( MDB_IDL_IS_ZERO( ids ) )
+ break;
+ }
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<= comp_equality_candidates: id=%ld, first=%ld, last=%ld\n",
+ (long) ids[0],
+ (long) MDB_IDL_FIRST(ids),
+ (long) MDB_IDL_LAST(ids) );
+ return( rc );
+}
+
+static int
+ava_comp_candidates (
+ Operation *op,
+ MDB_txn *rtxn,
+ AttributeAssertion *ava,
+ AttributeAliasing *aa,
+ ID *ids,
+ ID *tmp,
+ ID *stack )
+{
+ MatchingRuleAssertion mra;
+
+ mra.ma_rule = ava->aa_desc->ad_type->sat_equality;
+ if ( !mra.ma_rule ) {
+ MDB_IDL_ALL( ids );
+ return 0;
+ }
+ mra.ma_desc = aa->aa_aliased_ad;
+ mra.ma_rule = ava->aa_desc->ad_type->sat_equality;
+
+ return comp_candidates ( op, rtxn, &mra, ava->aa_cf, ids, tmp, stack );
+}
+
+static int
+comp_candidates (
+ Operation *op,
+ MDB_txn *rtxn,
+ MatchingRuleAssertion *mra,
+ ComponentFilter *f,
+ ID *ids,
+ ID *tmp,
+ ID *stack)
+{
+ int rc;
+
+ if ( !f ) return LDAP_PROTOCOL_ERROR;
+
+ Debug( LDAP_DEBUG_FILTER, "comp_candidates\n" );
+ switch ( f->cf_choice ) {
+ case SLAPD_FILTER_COMPUTED:
+ rc = f->cf_result;
+ break;
+ case LDAP_COMP_FILTER_AND:
+ rc = comp_list_candidates( op, rtxn, mra, f->cf_and, LDAP_COMP_FILTER_AND, ids, tmp, stack );
+ break;
+ case LDAP_COMP_FILTER_OR:
+ rc = comp_list_candidates( op, rtxn, mra, f->cf_or, LDAP_COMP_FILTER_OR, ids, tmp, stack );
+ break;
+ case LDAP_COMP_FILTER_NOT:
+ /* No component indexing supported for NOT filter */
+ Debug( LDAP_DEBUG_FILTER, "\tComponent NOT\n" );
+ MDB_IDL_ALL( ids );
+ rc = LDAP_PROTOCOL_ERROR;
+ break;
+ case LDAP_COMP_FILTER_ITEM:
+ rc = comp_equality_candidates( op, rtxn, mra, f->cf_ca, ids, tmp, stack );
+ break;
+ default:
+ MDB_IDL_ALL( ids );
+ rc = LDAP_PROTOCOL_ERROR;
+ }
+
+ return( rc );
+}
+#endif
+
+static int
+ext_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ MatchingRuleAssertion *mra,
+ ID *ids,
+ ID *tmp,
+ ID *stack)
+{
+#ifdef LDAP_COMP_MATCH
+ /*
+ * Currently Only Component Indexing for componentFilterMatch is supported
+ * Indexing for an extensible filter is not supported yet
+ */
+ if ( mra->ma_cf ) {
+ return comp_candidates ( op, rtxn, mra, mra->ma_cf, ids, tmp, stack);
+ }
+#endif
+ if ( mra->ma_desc == slap_schema.si_ad_entryDN ) {
+ int rc;
+ ID id;
+
+ MDB_IDL_ZERO( ids );
+ if ( mra->ma_rule == slap_schema.si_mr_distinguishedNameMatch ) {
+base:
+ rc = mdb_dn2id( op, rtxn, NULL, &mra->ma_value, &id, NULL, NULL, NULL );
+ if ( rc == MDB_SUCCESS ) {
+ mdb_idl_insert( ids, id );
+ }
+ return 0;
+ } else if ( mra->ma_rule && mra->ma_rule->smr_match ==
+ dnRelativeMatch && dnIsSuffix( &mra->ma_value,
+ op->o_bd->be_nsuffix )) {
+ int scope;
+ if ( mra->ma_rule == slap_schema.si_mr_dnSuperiorMatch ) {
+ mdb_dn2sups( op, rtxn, &mra->ma_value, ids );
+ return 0;
+ }
+ if ( mra->ma_rule == slap_schema.si_mr_dnSubtreeMatch )
+ scope = LDAP_SCOPE_SUBTREE;
+ else if ( mra->ma_rule == slap_schema.si_mr_dnOneLevelMatch )
+ scope = LDAP_SCOPE_ONELEVEL;
+ else if ( mra->ma_rule == slap_schema.si_mr_dnSubordinateMatch )
+ scope = LDAP_SCOPE_SUBORDINATE;
+ else
+ goto base; /* scope = LDAP_SCOPE_BASE; */
+#if 0
+ if ( scope > LDAP_SCOPE_BASE ) {
+ ei = NULL;
+ rc = mdb_cache_find_ndn( op, rtxn, &mra->ma_value, &ei );
+ if ( ei )
+ mdb_cache_entryinfo_unlock( ei );
+ if ( rc == LDAP_SUCCESS ) {
+ int sc = op->ors_scope;
+ op->ors_scope = scope;
+ rc = mdb_dn2idl( op, rtxn, &mra->ma_value, ei, ids,
+ stack );
+ op->ors_scope = sc;
+ }
+ return 0;
+ }
+#endif
+ }
+ }
+
+ MDB_IDL_ALL( ids );
+ return 0;
+}
+
+static int
+list_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ Filter *flist,
+ int ftype,
+ ID *ids,
+ ID *tmp,
+ ID *save )
+{
+ int rc = 0;
+ Filter *f;
+
+ Debug( LDAP_DEBUG_FILTER, "=> mdb_list_candidates 0x%x\n", ftype );
+ for ( f = flist; f != NULL; f = f->f_next ) {
+ /* ignore precomputed scopes */
+ if ( f->f_choice == SLAPD_FILTER_COMPUTED &&
+ f->f_result == LDAP_SUCCESS ) {
+ continue;
+ }
+ MDB_IDL_ZERO( save );
+ rc = mdb_filter_candidates( op, rtxn, f, save, tmp,
+ save+MDB_idl_um_size );
+
+ if ( rc != 0 ) {
+ if ( ftype == LDAP_FILTER_AND ) {
+ rc = 0;
+ continue;
+ }
+ break;
+ }
+
+
+ if ( ftype == LDAP_FILTER_AND ) {
+ if ( f == flist ) {
+ MDB_IDL_CPY( ids, save );
+ } else {
+ mdb_idl_intersection( ids, save );
+ }
+ if( MDB_IDL_IS_ZERO( ids ) )
+ break;
+ } else {
+ if ( f == flist ) {
+ MDB_IDL_CPY( ids, save );
+ } else {
+ mdb_idl_union( ids, save );
+ }
+ }
+ }
+
+ if( rc == LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= mdb_list_candidates: id=%ld first=%ld last=%ld\n",
+ (long) ids[0],
+ (long) MDB_IDL_FIRST(ids),
+ (long) MDB_IDL_LAST(ids) );
+
+ } else {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= mdb_list_candidates: undefined rc=%d\n",
+ rc );
+ }
+
+ return rc;
+}
+
+static int
+presence_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ AttributeDescription *desc,
+ ID *ids )
+{
+ MDB_dbi dbi;
+ int rc;
+ slap_mask_t mask;
+ struct berval prefix = {0, NULL};
+
+ Debug( LDAP_DEBUG_TRACE, "=> mdb_presence_candidates (%s)\n",
+ desc->ad_cname.bv_val );
+
+ MDB_IDL_ALL( ids );
+
+ if( desc == slap_schema.si_ad_objectClass ) {
+ return 0;
+ }
+
+ rc = mdb_index_param( op->o_bd, desc, LDAP_FILTER_PRESENT,
+ &dbi, &mask, &prefix );
+
+ if( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+ /* not indexed */
+ Debug( LDAP_DEBUG_FILTER,
+ "<= mdb_presence_candidates: (%s) not indexed\n",
+ desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_presence_candidates: (%s) index_param "
+ "returned=%d\n",
+ desc->ad_cname.bv_val, rc );
+ return 0;
+ }
+
+ if( prefix.bv_val == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_presence_candidates: (%s) no prefix\n",
+ desc->ad_cname.bv_val );
+ return -1;
+ }
+
+ rc = mdb_key_read( op->o_bd, rtxn, dbi, &prefix, ids, NULL, 0 );
+
+ if( rc == MDB_NOTFOUND ) {
+ MDB_IDL_ZERO( ids );
+ rc = 0;
+ } else if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_presense_candidates: (%s) "
+ "key read failed (%d)\n",
+ desc->ad_cname.bv_val, rc );
+ goto done;
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ "<= mdb_presence_candidates: id=%ld first=%ld last=%ld\n",
+ (long) ids[0],
+ (long) MDB_IDL_FIRST(ids),
+ (long) MDB_IDL_LAST(ids) );
+
+done:
+ return rc;
+}
+
+static int
+equality_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ AttributeAssertion *ava,
+ ID *ids,
+ ID *tmp )
+{
+ MDB_dbi dbi;
+ int i;
+ int rc;
+ slap_mask_t mask;
+ struct berval prefix = {0, NULL};
+ struct berval *keys = NULL;
+ MatchingRule *mr;
+
+ Debug( LDAP_DEBUG_TRACE, "=> mdb_equality_candidates (%s)\n",
+ ava->aa_desc->ad_cname.bv_val );
+
+ if ( ava->aa_desc == slap_schema.si_ad_entryDN ) {
+ ID id;
+ rc = mdb_dn2id( op, rtxn, NULL, &ava->aa_value, &id, NULL, NULL, NULL );
+ if ( rc == LDAP_SUCCESS ) {
+ /* exactly one ID can match */
+ ids[0] = 1;
+ ids[1] = id;
+ }
+ if ( rc == MDB_NOTFOUND ) {
+ MDB_IDL_ZERO( ids );
+ rc = 0;
+ }
+ return rc;
+ }
+
+ MDB_IDL_ALL( ids );
+
+ rc = mdb_index_param( op->o_bd, ava->aa_desc, LDAP_FILTER_EQUALITY,
+ &dbi, &mask, &prefix );
+
+ if ( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= mdb_equality_candidates: (%s) not indexed\n",
+ ava->aa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= mdb_equality_candidates: (%s) "
+ "index_param failed (%d)\n",
+ ava->aa_desc->ad_cname.bv_val, rc );
+ return 0;
+ }
+
+ mr = ava->aa_desc->ad_type->sat_equality;
+ if( !mr ) {
+ return 0;
+ }
+
+ if( !mr->smr_filter ) {
+ return 0;
+ }
+
+ rc = (mr->smr_filter)(
+ LDAP_FILTER_EQUALITY,
+ mask,
+ ava->aa_desc->ad_type->sat_syntax,
+ mr,
+ &prefix,
+ &ava->aa_value,
+ &keys, op->o_tmpmemctx );
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_equality_candidates: (%s, %s) "
+ "MR filter failed (%d)\n",
+ prefix.bv_val, ava->aa_desc->ad_cname.bv_val, rc );
+ return 0;
+ }
+
+ if( keys == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_equality_candidates: (%s) no keys\n",
+ ava->aa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ for ( i= 0; keys[i].bv_val != NULL; i++ ) {
+ rc = mdb_key_read( op->o_bd, rtxn, dbi, &keys[i], tmp, NULL, 0 );
+
+ if( rc == MDB_NOTFOUND ) {
+ MDB_IDL_ZERO( ids );
+ rc = 0;
+ break;
+ } else if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_equality_candidates: (%s) "
+ "key read failed (%d)\n",
+ ava->aa_desc->ad_cname.bv_val, rc );
+ break;
+ }
+
+ if( MDB_IDL_IS_ZERO( tmp ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_equality_candidates: (%s) NULL\n",
+ ava->aa_desc->ad_cname.bv_val );
+ MDB_IDL_ZERO( ids );
+ break;
+ }
+
+ if ( i == 0 ) {
+ MDB_IDL_CPY( ids, tmp );
+ } else {
+ mdb_idl_intersection( ids, tmp );
+ }
+
+ if( MDB_IDL_IS_ZERO( ids ) )
+ break;
+ }
+
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_equality_candidates: id=%ld, first=%ld, last=%ld\n",
+ (long) ids[0],
+ (long) MDB_IDL_FIRST(ids),
+ (long) MDB_IDL_LAST(ids) );
+ return( rc );
+}
+
+
+static int
+approx_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ AttributeAssertion *ava,
+ ID *ids,
+ ID *tmp )
+{
+ MDB_dbi dbi;
+ int i;
+ int rc;
+ slap_mask_t mask;
+ struct berval prefix = {0, NULL};
+ struct berval *keys = NULL;
+ MatchingRule *mr;
+
+ Debug( LDAP_DEBUG_TRACE, "=> mdb_approx_candidates (%s)\n",
+ ava->aa_desc->ad_cname.bv_val );
+
+ MDB_IDL_ALL( ids );
+
+ rc = mdb_index_param( op->o_bd, ava->aa_desc, LDAP_FILTER_APPROX,
+ &dbi, &mask, &prefix );
+
+ if ( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= mdb_approx_candidates: (%s) not indexed\n",
+ ava->aa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= mdb_approx_candidates: (%s) "
+ "index_param failed (%d)\n",
+ ava->aa_desc->ad_cname.bv_val, rc );
+ return 0;
+ }
+
+ mr = ava->aa_desc->ad_type->sat_approx;
+ if( !mr ) {
+ /* no approx matching rule, try equality matching rule */
+ mr = ava->aa_desc->ad_type->sat_equality;
+ }
+
+ if( !mr ) {
+ return 0;
+ }
+
+ if( !mr->smr_filter ) {
+ return 0;
+ }
+
+ rc = (mr->smr_filter)(
+ LDAP_FILTER_APPROX,
+ mask,
+ ava->aa_desc->ad_type->sat_syntax,
+ mr,
+ &prefix,
+ &ava->aa_value,
+ &keys, op->o_tmpmemctx );
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_approx_candidates: (%s, %s) "
+ "MR filter failed (%d)\n",
+ prefix.bv_val, ava->aa_desc->ad_cname.bv_val, rc );
+ return 0;
+ }
+
+ if( keys == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_approx_candidates: (%s) no keys (%s)\n",
+ prefix.bv_val, ava->aa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ for ( i= 0; keys[i].bv_val != NULL; i++ ) {
+ rc = mdb_key_read( op->o_bd, rtxn, dbi, &keys[i], tmp, NULL, 0 );
+
+ if( rc == MDB_NOTFOUND ) {
+ MDB_IDL_ZERO( ids );
+ rc = 0;
+ break;
+ } else if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_approx_candidates: (%s) "
+ "key read failed (%d)\n",
+ ava->aa_desc->ad_cname.bv_val, rc );
+ break;
+ }
+
+ if( MDB_IDL_IS_ZERO( tmp ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_approx_candidates: (%s) NULL\n",
+ ava->aa_desc->ad_cname.bv_val );
+ MDB_IDL_ZERO( ids );
+ break;
+ }
+
+ if ( i == 0 ) {
+ MDB_IDL_CPY( ids, tmp );
+ } else {
+ mdb_idl_intersection( ids, tmp );
+ }
+
+ if( MDB_IDL_IS_ZERO( ids ) )
+ break;
+ }
+
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+
+ Debug( LDAP_DEBUG_TRACE, "<= mdb_approx_candidates %ld, first=%ld, last=%ld\n",
+ (long) ids[0],
+ (long) MDB_IDL_FIRST(ids),
+ (long) MDB_IDL_LAST(ids) );
+ return( rc );
+}
+
+static int
+substring_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ SubstringsAssertion *sub,
+ ID *ids,
+ ID *tmp )
+{
+ MDB_dbi dbi;
+ int i;
+ int rc;
+ slap_mask_t mask;
+ struct berval prefix = {0, NULL};
+ struct berval *keys = NULL;
+ MatchingRule *mr;
+
+ Debug( LDAP_DEBUG_TRACE, "=> mdb_substring_candidates (%s)\n",
+ sub->sa_desc->ad_cname.bv_val );
+
+ MDB_IDL_ALL( ids );
+
+ rc = mdb_index_param( op->o_bd, sub->sa_desc, LDAP_FILTER_SUBSTRINGS,
+ &dbi, &mask, &prefix );
+
+ if ( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= mdb_substring_candidates: (%s) not indexed\n",
+ sub->sa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= mdb_substring_candidates: (%s) "
+ "index_param failed (%d)\n",
+ sub->sa_desc->ad_cname.bv_val, rc );
+ return 0;
+ }
+
+ mr = sub->sa_desc->ad_type->sat_substr;
+
+ if( !mr ) {
+ return 0;
+ }
+
+ if( !mr->smr_filter ) {
+ return 0;
+ }
+
+ rc = (mr->smr_filter)(
+ LDAP_FILTER_SUBSTRINGS,
+ mask,
+ sub->sa_desc->ad_type->sat_syntax,
+ mr,
+ &prefix,
+ sub,
+ &keys, op->o_tmpmemctx );
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_substring_candidates: (%s) "
+ "MR filter failed (%d)\n",
+ sub->sa_desc->ad_cname.bv_val, rc );
+ return 0;
+ }
+
+ if( keys == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_substring_candidates: (0x%04lx) no keys (%s)\n",
+ mask, sub->sa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ for ( i= 0; keys[i].bv_val != NULL; i++ ) {
+ rc = mdb_key_read( op->o_bd, rtxn, dbi, &keys[i], tmp, NULL, 0 );
+
+ if( rc == MDB_NOTFOUND ) {
+ MDB_IDL_ZERO( ids );
+ rc = 0;
+ break;
+ } else if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_substring_candidates: (%s) "
+ "key read failed (%d)\n",
+ sub->sa_desc->ad_cname.bv_val, rc );
+ break;
+ }
+
+ if( MDB_IDL_IS_ZERO( tmp ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_substring_candidates: (%s) NULL\n",
+ sub->sa_desc->ad_cname.bv_val );
+ MDB_IDL_ZERO( ids );
+ break;
+ }
+
+ if ( i == 0 ) {
+ MDB_IDL_CPY( ids, tmp );
+ } else {
+ mdb_idl_intersection( ids, tmp );
+ }
+
+ if( MDB_IDL_IS_ZERO( ids ) )
+ break;
+ }
+
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+
+ Debug( LDAP_DEBUG_TRACE, "<= mdb_substring_candidates: %ld, first=%ld, last=%ld\n",
+ (long) ids[0],
+ (long) MDB_IDL_FIRST(ids),
+ (long) MDB_IDL_LAST(ids) );
+ return( rc );
+}
+
+static int
+inequality_candidates(
+ Operation *op,
+ MDB_txn *rtxn,
+ AttributeAssertion *ava,
+ ID *ids,
+ ID *tmp,
+ int gtorlt )
+{
+ MDB_dbi dbi;
+ int rc;
+ slap_mask_t mask;
+ struct berval prefix = {0, NULL};
+ struct berval *keys = NULL;
+ MatchingRule *mr;
+ MDB_cursor *cursor = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "=> mdb_inequality_candidates (%s)\n",
+ ava->aa_desc->ad_cname.bv_val );
+
+ MDB_IDL_ALL( ids );
+
+ rc = mdb_index_param( op->o_bd, ava->aa_desc, LDAP_FILTER_EQUALITY,
+ &dbi, &mask, &prefix );
+
+ if ( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= mdb_inequality_candidates: (%s) not indexed\n",
+ ava->aa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= mdb_inequality_candidates: (%s) "
+ "index_param failed (%d)\n",
+ ava->aa_desc->ad_cname.bv_val, rc );
+ return 0;
+ }
+
+ mr = ava->aa_desc->ad_type->sat_equality;
+ if( !mr ) {
+ return 0;
+ }
+
+ if( !mr->smr_filter ) {
+ return 0;
+ }
+
+ rc = (mr->smr_filter)(
+ LDAP_FILTER_EQUALITY,
+ mask,
+ ava->aa_desc->ad_type->sat_syntax,
+ mr,
+ &prefix,
+ &ava->aa_value,
+ &keys, op->o_tmpmemctx );
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_inequality_candidates: (%s, %s) "
+ "MR filter failed (%d)\n",
+ prefix.bv_val, ava->aa_desc->ad_cname.bv_val, rc );
+ return 0;
+ }
+
+ if( keys == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_inequality_candidates: (%s) no keys\n",
+ ava->aa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ MDB_IDL_ZERO( ids );
+ while(1) {
+ rc = mdb_key_read( op->o_bd, rtxn, dbi, &keys[0], tmp, &cursor, gtorlt );
+
+ if( rc == MDB_NOTFOUND ) {
+ rc = 0;
+ break;
+ } else if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_inequality_candidates: (%s) "
+ "key read failed (%d)\n",
+ ava->aa_desc->ad_cname.bv_val, rc );
+ break;
+ }
+
+ if( MDB_IDL_IS_ZERO( tmp ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_inequality_candidates: (%s) NULL\n",
+ ava->aa_desc->ad_cname.bv_val );
+ break;
+ }
+
+ mdb_idl_union( ids, tmp );
+
+ if( op->ors_limit && op->ors_limit->lms_s_unchecked != -1 &&
+ MDB_IDL_N( ids ) >= (unsigned) op->ors_limit->lms_s_unchecked ) {
+ mdb_cursor_close( cursor );
+ break;
+ }
+ }
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<= mdb_inequality_candidates: id=%ld, first=%ld, last=%ld\n",
+ (long) ids[0],
+ (long) MDB_IDL_FIRST(ids),
+ (long) MDB_IDL_LAST(ids) );
+ return( rc );
+}
diff --git a/servers/slapd/back-mdb/id2entry.c b/servers/slapd/back-mdb/id2entry.c
new file mode 100644
index 0000000..aa6067a
--- /dev/null
+++ b/servers/slapd/back-mdb/id2entry.c
@@ -0,0 +1,1151 @@
+/* id2entry.c - routines to deal with the id2entry database */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/errno.h>
+
+#include "back-mdb.h"
+
+typedef struct Ecount {
+ ber_len_t len; /* total entry size */
+ ber_len_t dlen; /* contiguous data size */
+ int nattrs;
+ int nvals;
+ int offset;
+ Attribute *multi;
+} Ecount;
+
+static int mdb_entry_partsize(struct mdb_info *mdb, MDB_txn *txn, Entry *e,
+ Ecount *eh);
+static int mdb_entry_encode(Operation *op, Entry *e, MDB_val *data,
+ Ecount *ec);
+static Entry *mdb_entry_alloc( Operation *op, int nattrs, int nvals );
+
+#define ID2VKSZ (sizeof(ID)+2)
+
+int
+mdb_id2v_compare(
+ const MDB_val *usrkey,
+ const MDB_val *curkey
+)
+{
+ unsigned short *uv, *cv;
+ ID ui, ci;
+ int rc;
+
+ memcpy(&ui, usrkey->mv_data, sizeof(ID));
+ memcpy(&ci, curkey->mv_data, sizeof(ID));
+ if (ui < ci)
+ return -1;
+ if (ui > ci)
+ return 1;
+ uv = usrkey->mv_data;
+ cv = curkey->mv_data;
+ return uv[sizeof(ID)/2] - cv[sizeof(ID)/2];
+}
+
+/* usrkey[0] is the key in DB format, as described at mdb_mval_put.
+ * usrkey[1] is the value we'll actually match against.
+ * usrkey[2] is the attributeDescription for this value.
+ */
+int
+mdb_id2v_dupsort(
+ const MDB_val *usrkey,
+ const MDB_val *curkey
+)
+{
+ AttributeDescription *ad = usrkey[2].mv_data;
+ struct berval bv1, bv2;
+ int rc, match, olen;
+ unsigned short s;
+ char *ptr;
+
+ ptr = curkey->mv_data + curkey->mv_size - 2;
+ memcpy(&s, ptr, 2);
+ bv2.bv_val = curkey->mv_data;
+ bv2.bv_len = curkey->mv_size - 3;
+ if (s)
+ bv2.bv_len -= (s+1);
+
+ bv1.bv_val = usrkey[1].mv_data;
+ bv1.bv_len = usrkey[1].mv_size;
+
+ if (ad && ad->ad_type->sat_equality) {
+ MatchingRule *mr = ad->ad_type->sat_equality;
+ rc = mr->smr_match(&match, SLAP_MR_EQUALITY
+ | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX
+ | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
+ | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
+ ad->ad_type->sat_syntax, mr, &bv1, &bv2);
+ } else {
+ match = ber_bvcmp(&bv1, &bv2);
+ }
+
+ return match;
+}
+
+/* Values are stored as
+ * [normalized-value NUL ] original-value NUL 2-byte-len
+ * The trailing 2-byte-len is zero if there is no normalized value.
+ * Otherwise, it is the length of the original-value.
+ */
+int mdb_mval_put(Operation *op, MDB_cursor *mc, ID id, Attribute *a)
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_val key, data[3];
+ char *buf;
+ char ivk[ID2VKSZ];
+ unsigned i;
+ unsigned short s;
+ int rc, len;
+
+ memcpy(ivk, &id, sizeof(id));
+ s = mdb->mi_adxs[a->a_desc->ad_index];
+ memcpy(ivk+sizeof(ID), &s, 2);
+ key.mv_data = &ivk;
+ key.mv_size = sizeof(ivk);
+ if ((a->a_desc->ad_type->sat_flags & SLAP_AT_ORDERED) || a->a_desc == slap_schema.si_ad_objectClass)
+ data[2].mv_data = NULL;
+ else
+ data[2].mv_data = a->a_desc;
+
+ for (i=0; i<a->a_numvals; i++) {
+ len = a->a_nvals[i].bv_len + 1 + 2;
+ if (a->a_nvals != a->a_vals) {
+ len += a->a_vals[i].bv_len + 1;
+ data[1].mv_data = a->a_nvals[i].bv_val;
+ data[1].mv_size = a->a_nvals[i].bv_len;
+ } else {
+ data[1].mv_data = a->a_vals[i].bv_val;
+ data[1].mv_size = a->a_vals[i].bv_len;
+ }
+ data[0].mv_size = len;
+ buf = op->o_tmpalloc( len, op->o_tmpmemctx );
+ data[0].mv_data = buf;
+ memcpy(buf, a->a_nvals[i].bv_val, a->a_nvals[i].bv_len);
+ buf += a->a_nvals[i].bv_len;
+ *buf++ = 0;
+ if (a->a_nvals != a->a_vals) {
+ s = a->a_vals[i].bv_len;
+ memcpy(buf, a->a_vals[i].bv_val, a->a_vals[i].bv_len);
+ buf += a->a_vals[i].bv_len;
+ *buf++ = 0;
+ memcpy(buf, &s, 2);
+ } else {
+ *buf++ = 0;
+ *buf++ = 0;
+ }
+ rc = mdb_cursor_put(mc, &key, data, 0);
+ op->o_tmpfree( data[0].mv_data, op->o_tmpmemctx );
+ if (rc)
+ return rc;
+ }
+ return 0;
+}
+
+int mdb_mval_del(Operation *op, MDB_cursor *mc, ID id, Attribute *a)
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_val key, data[3];
+ char *ptr;
+ char ivk[ID2VKSZ];
+ unsigned i;
+ int rc;
+ unsigned short s;
+
+ memcpy(ivk, &id, sizeof(id));
+ s = mdb->mi_adxs[a->a_desc->ad_index];
+ memcpy(ivk+sizeof(ID), &s, 2);
+ key.mv_data = &ivk;
+ key.mv_size = sizeof(ivk);
+ if ((a->a_desc->ad_type->sat_flags & SLAP_AT_ORDERED) || a->a_desc == slap_schema.si_ad_objectClass)
+ data[2].mv_data = NULL;
+ else
+ data[2].mv_data = a->a_desc;
+
+ if (a->a_numvals) {
+ for (i=0; i<a->a_numvals; i++) {
+ data[0].mv_data = a->a_nvals[i].bv_val;
+ data[0].mv_size = a->a_nvals[i].bv_len+1;
+ if (a->a_nvals != a->a_vals) {
+ data[1].mv_data = a->a_nvals[i].bv_val;
+ data[1].mv_size = a->a_nvals[i].bv_len;
+ } else {
+ data[1].mv_data = a->a_vals[i].bv_val;
+ data[1].mv_size = a->a_vals[i].bv_len;
+ }
+ rc = mdb_cursor_get(mc, &key, data, MDB_GET_BOTH_RANGE);
+ if (rc)
+ return rc;
+ rc = mdb_cursor_del(mc, 0);
+ if (rc)
+ return rc;
+ }
+ } else {
+ rc = mdb_cursor_get(mc, &key, data, MDB_SET);
+ if (rc)
+ return rc;
+ rc = mdb_cursor_del(mc, MDB_NODUPDATA);
+ }
+ return rc;
+}
+
+static int mdb_mval_get(Operation *op, MDB_cursor *mc, ID id, Attribute *a, int have_nvals)
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_val key, data[3];
+ char *ptr;
+ char ivk[ID2VKSZ];
+ unsigned i;
+ int rc = 0;
+ unsigned short s;
+
+ memcpy(ivk, &id, sizeof(id));
+ s = mdb->mi_adxs[a->a_desc->ad_index];
+ memcpy(ivk+sizeof(ID), &s, 2);
+ key.mv_data = &ivk;
+ key.mv_size = sizeof(ivk);
+
+ /* not needed */
+ if ((a->a_desc->ad_type->sat_flags & SLAP_AT_ORDERED) || a->a_desc == slap_schema.si_ad_objectClass)
+ data[2].mv_data = NULL;
+ else
+ data[2].mv_data = a->a_desc;
+
+ if (have_nvals)
+ a->a_nvals = a->a_vals + a->a_numvals + 1;
+ else
+ a->a_nvals = a->a_vals;
+ for (i=0; i<a->a_numvals; i++) {
+ if (!i)
+ rc = mdb_cursor_get(mc, &key, data, MDB_SET);
+ else
+ rc = mdb_cursor_get(mc, &key, data, MDB_NEXT_DUP);
+ if (rc)
+ break;
+ ptr = (char*)data[0].mv_data + data[0].mv_size - 2;
+ memcpy(&s, ptr, 2);
+ if (have_nvals) {
+ a->a_nvals[i].bv_val = data[0].mv_data;
+ a->a_vals[i].bv_len = s;
+ a->a_vals[i].bv_val = ptr - a->a_vals[i].bv_len - 1;
+ a->a_nvals[i].bv_len = a->a_vals[i].bv_val - a->a_nvals[i].bv_val - 1;
+ } else {
+ assert(!s);
+ a->a_vals[i].bv_val = data[0].mv_data;
+ a->a_vals[i].bv_len = data[0].mv_size - 3;
+ }
+ }
+ a->a_numvals = i;
+ BER_BVZERO(&a->a_vals[i]);
+ if (have_nvals) {
+ BER_BVZERO(&a->a_nvals[i]);
+ }
+ return rc;
+}
+
+#define ADD_FLAGS (MDB_NOOVERWRITE|MDB_APPEND)
+
+static int mdb_id2entry_put(
+ Operation *op,
+ MDB_txn *txn,
+ MDB_cursor *mc,
+ Entry *e,
+ int flag )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ Ecount ec;
+ MDB_val key, data;
+ int rc, adding = flag, prev_ads = mdb->mi_numads;
+
+ /* We only store rdns, and they go in the dn2id database. */
+
+ key.mv_data = &e->e_id;
+ key.mv_size = sizeof(ID);
+
+ rc = mdb_entry_partsize( mdb, txn, e, &ec );
+ if (rc) {
+ rc = LDAP_OTHER;
+ goto fail;
+ }
+
+ flag |= MDB_RESERVE;
+
+ if (e->e_id < mdb->mi_nextid)
+ flag &= ~MDB_APPEND;
+
+ if (mdb->mi_maxentrysize && ec.len > mdb->mi_maxentrysize) {
+ rc = LDAP_ADMINLIMIT_EXCEEDED;
+ goto fail;
+ }
+
+again:
+ data.mv_size = ec.dlen;
+ if ( mc )
+ rc = mdb_cursor_put( mc, &key, &data, flag );
+ else
+ rc = mdb_put( txn, mdb->mi_id2entry, &key, &data, flag );
+ if (rc == MDB_SUCCESS) {
+ rc = mdb_entry_encode( op, e, &data, &ec );
+ if( rc != LDAP_SUCCESS )
+ goto fail;
+ /* Handle adds of large multi-valued attrs here.
+ * Modifies handle them directly.
+ */
+ if (adding && ec.multi) {
+ MDB_cursor *mvc;
+ Attribute *a;
+ rc = mdb_cursor_open( txn, mdb->mi_dbis[MDB_ID2VAL], &mvc );
+ if( !rc ) {
+ for ( a = ec.multi; a; a=a->a_next ) {
+ if (!(a->a_flags & SLAP_ATTR_BIG_MULTI))
+ continue;
+ rc = mdb_mval_put( op, mvc, e->e_id, a );
+ if( rc )
+ break;
+ }
+ mdb_cursor_close( mvc );
+ }
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "mdb_id2entry_put: mdb_mval_put failed: %s(%d) \"%s\"\n",
+ mdb_strerror(rc), rc,
+ e->e_nname.bv_val );
+ rc = LDAP_OTHER;
+ goto fail;
+ }
+ }
+ }
+ if (rc) {
+ /* Was there a hole from slapadd? */
+ if ( (flag & MDB_NOOVERWRITE) && data.mv_size == 0 ) {
+ flag &= ~ADD_FLAGS;
+ goto again;
+ }
+ Debug( LDAP_DEBUG_ANY,
+ "mdb_id2entry_put: mdb_put failed: %s(%d) \"%s\"\n",
+ mdb_strerror(rc), rc,
+ e->e_nname.bv_val );
+ if ( rc != MDB_KEYEXIST )
+ rc = LDAP_OTHER;
+ }
+fail:
+ if (rc) {
+ mdb_ad_unwind( mdb, prev_ads );
+ }
+ return rc;
+}
+
+/*
+ * This routine adds (or updates) an entry on disk.
+ */
+int mdb_id2entry_add(
+ Operation *op,
+ MDB_txn *txn,
+ MDB_cursor *mc,
+ Entry *e )
+{
+ return mdb_id2entry_put(op, txn, mc, e, ADD_FLAGS);
+}
+
+int mdb_id2entry_update(
+ Operation *op,
+ MDB_txn *txn,
+ MDB_cursor *mc,
+ Entry *e )
+{
+ return mdb_id2entry_put(op, txn, mc, e, 0);
+}
+
+int mdb_id2edata(
+ Operation *op,
+ MDB_cursor *mc,
+ ID id,
+ MDB_val *data )
+{
+ MDB_val key;
+ int rc;
+
+ key.mv_data = &id;
+ key.mv_size = sizeof(ID);
+
+ /* fetch it */
+ rc = mdb_cursor_get( mc, &key, data, MDB_SET );
+ /* stubs from missing parents - DB is actually invalid */
+ if ( rc == MDB_SUCCESS && !data->mv_size )
+ rc = MDB_NOTFOUND;
+ return rc;
+}
+
+int mdb_id2entry(
+ Operation *op,
+ MDB_cursor *mc,
+ ID id,
+ Entry **e )
+{
+ MDB_val key, data;
+ int rc = 0;
+
+ *e = NULL;
+
+ key.mv_data = &id;
+ key.mv_size = sizeof(ID);
+
+ /* fetch it */
+ rc = mdb_cursor_get( mc, &key, &data, MDB_SET );
+ if ( rc == MDB_NOTFOUND ) {
+ /* Looking for root entry on an empty-dn suffix? */
+ if ( !id && BER_BVISEMPTY( &op->o_bd->be_nsuffix[0] )) {
+ struct berval gluebv = BER_BVC("glue");
+ Entry *r = mdb_entry_alloc(op, 2, 4);
+ Attribute *a = r->e_attrs;
+ struct berval *bptr;
+
+ r->e_id = 0;
+ r->e_ocflags = SLAP_OC_GLUE|SLAP_OC__END;
+ bptr = a->a_vals;
+ a->a_flags = SLAP_ATTR_DONT_FREE_DATA | SLAP_ATTR_DONT_FREE_VALS;
+ a->a_desc = slap_schema.si_ad_objectClass;
+ a->a_nvals = a->a_vals;
+ a->a_numvals = 1;
+ *bptr++ = gluebv;
+ BER_BVZERO(bptr);
+ bptr++;
+ a->a_next = a+1;
+ a = a->a_next;
+ a->a_flags = SLAP_ATTR_DONT_FREE_DATA | SLAP_ATTR_DONT_FREE_VALS;
+ a->a_desc = slap_schema.si_ad_structuralObjectClass;
+ a->a_vals = bptr;
+ a->a_nvals = a->a_vals;
+ a->a_numvals = 1;
+ *bptr++ = gluebv;
+ BER_BVZERO(bptr);
+ a->a_next = NULL;
+ *e = r;
+ return MDB_SUCCESS;
+ }
+ }
+ /* stubs from missing parents - DB is actually invalid */
+ if ( rc == MDB_SUCCESS && !data.mv_size )
+ rc = MDB_NOTFOUND;
+ if ( rc ) return rc;
+
+ rc = mdb_entry_decode( op, mdb_cursor_txn( mc ), &data, id, e );
+ if ( rc ) return rc;
+
+ (*e)->e_id = id;
+ (*e)->e_name.bv_val = NULL;
+ (*e)->e_nname.bv_val = NULL;
+
+ return rc;
+}
+
+int mdb_id2entry_delete(
+ BackendDB *be,
+ MDB_txn *tid,
+ Entry *e )
+{
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+ MDB_dbi dbi = mdb->mi_id2entry;
+ MDB_val key;
+ MDB_cursor *mvc;
+ char kbuf[sizeof(ID) + sizeof(unsigned short)];
+ int rc;
+
+ memcpy( kbuf, &e->e_id, sizeof(ID) );
+ memset( kbuf+sizeof(ID), 0, sizeof(unsigned short) );
+ key.mv_data = kbuf;
+ key.mv_size = sizeof(kbuf);
+
+ /* delete from database */
+ rc = mdb_del( tid, dbi, &key, NULL );
+ if (rc)
+ return rc;
+ rc = mdb_cursor_open( tid, mdb->mi_dbis[MDB_ID2VAL], &mvc );
+ if (rc)
+ return rc;
+
+ rc = mdb_cursor_get( mvc, &key, NULL, MDB_SET_RANGE );
+ if (rc) {
+ if (rc == MDB_NOTFOUND)
+ rc = MDB_SUCCESS;
+ return rc;
+ }
+ while (*(ID *)key.mv_data == e->e_id ) {
+ rc = mdb_cursor_del( mvc, MDB_NODUPDATA );
+ if (rc)
+ return rc;
+ rc = mdb_cursor_get( mvc, &key, NULL, MDB_GET_CURRENT );
+ if (rc) {
+ /* no record or DB is empty */
+ if (rc == MDB_NOTFOUND || rc == EINVAL)
+ rc = MDB_SUCCESS;
+ break;
+ }
+ }
+ return rc;
+}
+
+static Entry * mdb_entry_alloc(
+ Operation *op,
+ int nattrs,
+ int nvals )
+{
+ Entry *e = op->o_tmpalloc( sizeof(Entry) +
+ nattrs * sizeof(Attribute) +
+ nvals * sizeof(struct berval), op->o_tmpmemctx );
+ BER_BVZERO(&e->e_bv);
+ e->e_private = e;
+ if (nattrs) {
+ e->e_attrs = (Attribute *)(e+1);
+ e->e_attrs->a_vals = (struct berval *)(e->e_attrs+nattrs);
+ } else {
+ e->e_attrs = NULL;
+ }
+
+ return e;
+}
+
+int mdb_entry_return(
+ Operation *op,
+ Entry *e
+)
+{
+ if ( !e )
+ return 0;
+ if ( e->e_private ) {
+ if ( op->o_hdr && op->o_tmpmfuncs ) {
+ op->o_tmpfree( e->e_nname.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( e->e_name.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( e, op->o_tmpmemctx );
+ } else {
+ ch_free( e->e_nname.bv_val );
+ ch_free( e->e_name.bv_val );
+ ch_free( e );
+ }
+ } else {
+ entry_free( e );
+ }
+ return 0;
+}
+
+int mdb_entry_release(
+ Operation *op,
+ Entry *e,
+ int rw )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ struct mdb_op_info *moi = NULL;
+
+ /* slapMode : SLAP_SERVER_MODE, SLAP_TOOL_MODE,
+ SLAP_TRUNCATE_MODE, SLAP_UNDEFINED_MODE */
+
+ int release = 1;
+ if ( slapMode & SLAP_SERVER_MODE ) {
+ OpExtra *oex;
+ LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
+ release = 0;
+ if ( oex->oe_key == mdb ) {
+ mdb_entry_return( op, e );
+ moi = (mdb_op_info *)oex;
+ /* If it was setup by entry_get we should probably free it */
+ if (( moi->moi_flag & (MOI_FREEIT|MOI_KEEPER)) == MOI_FREEIT ) {
+ moi->moi_ref--;
+ if ( moi->moi_ref < 1 ) {
+ mdb_txn_reset( moi->moi_txn );
+ moi->moi_ref = 0;
+ LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
+ op->o_tmpfree( moi, op->o_tmpmemctx );
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ if (release)
+ mdb_entry_return( op, e );
+
+ return 0;
+}
+
+/* return LDAP_SUCCESS IFF we can retrieve the specified entry.
+ */
+int mdb_entry_get(
+ Operation *op,
+ struct berval *ndn,
+ ObjectClass *oc,
+ AttributeDescription *at,
+ int rw,
+ Entry **ent )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ struct mdb_op_info *moi = NULL;
+ MDB_txn *txn = NULL;
+ Entry *e = NULL;
+ int rc;
+ const char *at_name = at ? at->ad_cname.bv_val : "(null)";
+
+ Debug( LDAP_DEBUG_ARGS,
+ "=> mdb_entry_get: ndn: \"%s\"\n", ndn->bv_val );
+ Debug( LDAP_DEBUG_ARGS,
+ "=> mdb_entry_get: oc: \"%s\", at: \"%s\"\n",
+ oc ? oc->soc_cname.bv_val : "(null)", at_name );
+
+ rc = mdb_opinfo_get( op, mdb, rw == 0, &moi );
+ if ( rc )
+ return LDAP_OTHER;
+ txn = moi->moi_txn;
+
+ /* can we find entry */
+ rc = mdb_dn2entry( op, txn, NULL, ndn, &e, NULL, 0 );
+ switch( rc ) {
+ case MDB_NOTFOUND:
+ case 0:
+ break;
+ default:
+ return (rc != LDAP_BUSY) ? LDAP_OTHER : LDAP_BUSY;
+ }
+ if (e == NULL) {
+ Debug( LDAP_DEBUG_ACL,
+ "=> mdb_entry_get: cannot find entry: \"%s\"\n",
+ ndn->bv_val );
+ rc = LDAP_NO_SUCH_OBJECT;
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_ACL,
+ "=> mdb_entry_get: found entry: \"%s\"\n",
+ ndn->bv_val );
+
+ if ( oc && !is_entry_objectclass( e, oc, 0 )) {
+ Debug( LDAP_DEBUG_ACL,
+ "<= mdb_entry_get: failed to find objectClass %s\n",
+ oc->soc_cname.bv_val );
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ goto return_results;
+ }
+
+ /* NOTE: attr_find() or attrs_find()? */
+ if ( at && attr_find( e->e_attrs, at ) == NULL ) {
+ Debug( LDAP_DEBUG_ACL,
+ "<= mdb_entry_get: failed to find attribute %s\n",
+ at->ad_cname.bv_val );
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ goto return_results;
+ }
+
+return_results:
+ if( rc != LDAP_SUCCESS ) {
+ /* free entry */
+ mdb_entry_release( op, e, rw );
+ } else {
+ *ent = e;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "mdb_entry_get: rc=%d\n",
+ rc );
+ return(rc);
+}
+
+static void
+mdb_reader_free( void *key, void *data )
+{
+ MDB_txn *txn = data;
+
+ if ( txn ) mdb_txn_abort( txn );
+}
+
+/* free up any keys used by the main thread */
+void
+mdb_reader_flush( MDB_env *env )
+{
+ void *data;
+ void *ctx = ldap_pvt_thread_pool_context();
+
+ if ( !ldap_pvt_thread_pool_getkey( ctx, env, &data, NULL ) ) {
+ ldap_pvt_thread_pool_setkey( ctx, env, NULL, 0, NULL, NULL );
+ mdb_reader_free( env, data );
+ }
+}
+
+extern MDB_txn *mdb_tool_txn;
+
+int
+mdb_opinfo_get( Operation *op, struct mdb_info *mdb, int rdonly, mdb_op_info **moip )
+{
+ int rc, renew = 0;
+ void *data;
+ void *ctx;
+ mdb_op_info *moi = NULL;
+ OpExtra *oex;
+
+ assert( op != NULL );
+
+ if ( !mdb || !moip ) return -1;
+
+ /* If no op was provided, try to find the ctx anyway... */
+ if ( op ) {
+ ctx = op->o_threadctx;
+ } else {
+ ctx = ldap_pvt_thread_pool_context();
+ }
+
+ if ( op ) {
+ LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
+ if ( oex->oe_key == mdb ) break;
+ }
+ moi = (mdb_op_info *)oex;
+ }
+
+ if ( !moi ) {
+ moi = *moip;
+
+ if ( !moi ) {
+ if ( op ) {
+ moi = op->o_tmpalloc(sizeof(struct mdb_op_info),op->o_tmpmemctx);
+ } else {
+ moi = ch_malloc(sizeof(mdb_op_info));
+ }
+ moi->moi_flag = MOI_FREEIT;
+ *moip = moi;
+ }
+ LDAP_SLIST_INSERT_HEAD( &op->o_extra, &moi->moi_oe, oe_next );
+ moi->moi_oe.oe_key = mdb;
+ moi->moi_ref = 0;
+ moi->moi_txn = NULL;
+ }
+
+ if ( !rdonly ) {
+ /* This op started as a reader, but now wants to write. */
+ if ( moi->moi_flag & MOI_READER ) {
+ moi = *moip;
+ LDAP_SLIST_INSERT_HEAD( &op->o_extra, &moi->moi_oe, oe_next );
+ } else {
+ /* This op is continuing an existing write txn */
+ *moip = moi;
+ }
+ moi->moi_ref++;
+ if ( !moi->moi_txn ) {
+ if (( slapMode & SLAP_TOOL_MODE ) && mdb_tool_txn ) {
+ moi->moi_txn = mdb_tool_txn;
+ } else {
+ int flag = 0;
+#ifdef SLAP_CONTROL_X_LAZY_COMMIT
+ if ( get_lazyCommit( op ))
+ flag |= MDB_NOMETASYNC;
+#endif
+ rc = mdb_txn_begin( mdb->mi_dbenv, NULL, flag, &moi->moi_txn );
+ if (rc) {
+ Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: err %s(%d)\n",
+ mdb_strerror(rc), rc );
+ }
+ return rc;
+ }
+ }
+ return 0;
+ }
+
+ /* OK, this is a reader */
+ if ( !moi->moi_txn ) {
+ if (( slapMode & SLAP_TOOL_MODE ) && mdb_tool_txn ) {
+ moi->moi_txn = mdb_tool_txn;
+ goto ok;
+ }
+ if ( !ctx ) {
+ /* Shouldn't happen unless we're single-threaded */
+ rc = mdb_txn_begin( mdb->mi_dbenv, NULL, MDB_RDONLY, &moi->moi_txn );
+ if (rc) {
+ Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: err %s(%d)\n",
+ mdb_strerror(rc), rc );
+ }
+ return rc;
+ }
+ if ( ldap_pvt_thread_pool_getkey( ctx, mdb->mi_dbenv, &data, NULL ) ) {
+ int retried = 0;
+retry:
+ rc = mdb_txn_begin( mdb->mi_dbenv, NULL, MDB_RDONLY, &moi->moi_txn );
+ if (rc == MDB_READERS_FULL && !retried) {
+ int dead;
+ /* if any stale readers were cleared, a slot should be available */
+ if (!mdb_reader_check( mdb->mi_dbenv, &dead ) && dead) {
+ retried = 1;
+ goto retry;
+ }
+ }
+ if (rc) {
+ Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: err %s(%d)\n",
+ mdb_strerror(rc), rc );
+ return rc;
+ }
+ data = moi->moi_txn;
+ if ( ( rc = ldap_pvt_thread_pool_setkey( ctx, mdb->mi_dbenv,
+ data, mdb_reader_free, NULL, NULL ) ) ) {
+ mdb_txn_abort( moi->moi_txn );
+ moi->moi_txn = NULL;
+ Debug( LDAP_DEBUG_ANY, "mdb_opinfo_get: thread_pool_setkey failed err (%d)\n",
+ rc );
+ return rc;
+ }
+ } else {
+ moi->moi_txn = data;
+ renew = 1;
+ }
+ moi->moi_flag |= MOI_READER;
+ }
+ok:
+ if ( moi->moi_ref < 1 ) {
+ moi->moi_ref = 0;
+ }
+ if ( renew ) {
+ rc = mdb_txn_renew( moi->moi_txn );
+ assert(!rc);
+ }
+ moi->moi_ref++;
+ if ( *moip != moi )
+ *moip = moi;
+
+ return 0;
+}
+
+int mdb_txn( Operation *op, int txnop, OpExtra **ptr )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ mdb_op_info **moip = (mdb_op_info **)ptr, *moi = *moip;
+ int rc;
+
+ switch( txnop ) {
+ case SLAP_TXN_BEGIN:
+ rc = mdb_opinfo_get( op, mdb, 0, moip );
+ if ( !rc ) {
+ moi = *moip;
+ moi->moi_flag |= MOI_KEEPER;
+ }
+ return rc;
+ case SLAP_TXN_COMMIT:
+ rc = mdb_txn_commit( moi->moi_txn );
+ if ( rc )
+ mdb->mi_numads = 0;
+ op->o_tmpfree( moi, op->o_tmpmemctx );
+ return rc;
+ case SLAP_TXN_ABORT:
+ mdb->mi_numads = 0;
+ mdb_txn_abort( moi->moi_txn );
+ op->o_tmpfree( moi, op->o_tmpmemctx );
+ return 0;
+ }
+ return LDAP_OTHER;
+}
+
+/* Count up the sizes of the components of an entry */
+static int mdb_entry_partsize(struct mdb_info *mdb, MDB_txn *txn, Entry *e,
+ Ecount *eh)
+{
+ ber_len_t len, dlen;
+ int i, nat = 0, nval = 0, nnval = 0, doff = 0;
+ Attribute *a;
+ unsigned hi;
+
+ eh->multi = NULL;
+ len = 4*sizeof(int); /* nattrs, nvals, ocflags, offset */
+ dlen = len;
+ for (a=e->e_attrs; a; a=a->a_next) {
+ /* For AttributeDesc, we only store the attr index */
+ nat++;
+ if (a->a_desc->ad_index >= MDB_MAXADS) {
+ Debug( LDAP_DEBUG_ANY, "mdb_entry_partsize: too many AttributeDescriptions used\n" );
+ return LDAP_OTHER;
+ }
+ if (!mdb->mi_adxs[a->a_desc->ad_index]) {
+ int rc = mdb_ad_get(mdb, txn, a->a_desc);
+ if (rc)
+ return rc;
+ }
+ len += 2*sizeof(int); /* AD index, numvals */
+ dlen += 2*sizeof(int);
+ nval += a->a_numvals + 1; /* empty berval at end */
+ mdb_attr_multi_thresh( mdb, a->a_desc, &hi, NULL );
+ if (a->a_numvals > hi)
+ a->a_flags |= SLAP_ATTR_BIG_MULTI;
+ if (a->a_flags & SLAP_ATTR_BIG_MULTI)
+ doff += a->a_numvals;
+ for (i=0; i<a->a_numvals; i++) {
+ int alen = a->a_vals[i].bv_len + 1 + sizeof(int); /* len */
+ len += alen;
+ if (a->a_flags & SLAP_ATTR_BIG_MULTI) {
+ if (!eh->multi)
+ eh->multi = a;
+ } else {
+ dlen += alen;
+ }
+ }
+ if (a->a_nvals != a->a_vals) {
+ nval += a->a_numvals + 1;
+ nnval++;
+ if (a->a_flags & SLAP_ATTR_BIG_MULTI)
+ doff += a->a_numvals;
+ for (i=0; i<a->a_numvals; i++) {
+ int alen = a->a_nvals[i].bv_len + 1 + sizeof(int);
+ len += alen;
+ if (!(a->a_flags & SLAP_ATTR_BIG_MULTI))
+ dlen += alen;
+ }
+ }
+ }
+ /* padding */
+ dlen = (dlen + sizeof(ID)-1) & ~(sizeof(ID)-1);
+ eh->len = len;
+ eh->dlen = dlen;
+ eh->nattrs = nat;
+ eh->nvals = nval;
+ eh->offset = nat + nval - nnval - doff;
+ return 0;
+}
+
+/* Flag bits for an encoded attribute */
+#define MDB_AT_SORTED (1U<<(sizeof(unsigned int)*CHAR_BIT-1))
+ /* the values are in sorted order */
+#define MDB_AT_MULTI (1<<(sizeof(unsigned int)*CHAR_BIT-2))
+ /* the values of this multi-valued attr are stored separately */
+
+#define MDB_AT_NVALS (1U<<(sizeof(unsigned int)*CHAR_BIT-1))
+ /* this attribute has normalized values */
+
+/* Flatten an Entry into a buffer. The buffer starts with the count of the
+ * number of attributes in the entry, the total number of values in the
+ * entry, and the e_ocflags. It then contains a list of integers for each
+ * attribute. For each attribute the first integer gives the index of the
+ * matching AttributeDescription, followed by the number of values in the
+ * attribute. If the MDB_AT_SORTED bit of the attr index is set, the
+ * attribute's values are already sorted. If the MDB_AT_MULTI bit of the
+ * attr index is set, the values are stored separately.
+ *
+ * If the MDB_AT_NVALS bit of numvals is set, the attribute also has
+ * normalized values present. (Note - a_numvals is an unsigned int, so this
+ * means it's possible to receive an attribute that we can't encode due
+ * to size overflow. In practice, this should not be an issue.)
+ *
+ * Then the length of each value is listed. If there are normalized values,
+ * their lengths come next. This continues for each attribute. After all
+ * of the lengths for the last attribute, the actual values are copied,
+ * with a NUL terminator after each value.
+ * The buffer is padded to the sizeof(ID). The entire buffer size is
+ * precomputed so that a single malloc can be performed.
+ */
+static int mdb_entry_encode(Operation *op, Entry *e, MDB_val *data, Ecount *eh)
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ ber_len_t i;
+ Attribute *a;
+ unsigned char *ptr;
+ unsigned int *lp, l;
+
+ Debug( LDAP_DEBUG_TRACE, "=> mdb_entry_encode(0x%08lx): %s\n",
+ (long) e->e_id, e->e_dn );
+
+ /* make sure e->e_ocflags is set */
+ if (is_entry_referral(e))
+ ; /* empty */
+
+ lp = (unsigned int *)data->mv_data;
+ *lp++ = eh->nattrs;
+ *lp++ = eh->nvals;
+ *lp++ = (unsigned int)e->e_ocflags;
+ *lp++ = eh->offset;
+ ptr = (unsigned char *)(lp + eh->offset);
+
+ for (a=e->e_attrs; a; a=a->a_next) {
+ if (!a->a_desc->ad_index)
+ return LDAP_UNDEFINED_TYPE;
+ l = mdb->mi_adxs[a->a_desc->ad_index];
+ if (a->a_flags & SLAP_ATTR_BIG_MULTI)
+ l |= MDB_AT_MULTI;
+ if (a->a_flags & SLAP_ATTR_SORTED_VALS)
+ l |= MDB_AT_SORTED;
+ *lp++ = l;
+ l = a->a_numvals;
+ if (a->a_nvals != a->a_vals)
+ l |= MDB_AT_NVALS;
+ *lp++ = l;
+ if (a->a_flags & SLAP_ATTR_BIG_MULTI) {
+ continue;
+ } else {
+ if (a->a_vals) {
+ for (i=0; a->a_vals[i].bv_val; i++);
+ assert( i == a->a_numvals );
+ for (i=0; i<a->a_numvals; i++) {
+ *lp++ = a->a_vals[i].bv_len;
+ memcpy(ptr, a->a_vals[i].bv_val,
+ a->a_vals[i].bv_len);
+ ptr += a->a_vals[i].bv_len;
+ *ptr++ = '\0';
+ }
+ if (a->a_nvals != a->a_vals) {
+ for (i=0; i<a->a_numvals; i++) {
+ *lp++ = a->a_nvals[i].bv_len;
+ memcpy(ptr, a->a_nvals[i].bv_val,
+ a->a_nvals[i].bv_len);
+ ptr += a->a_nvals[i].bv_len;
+ *ptr++ = '\0';
+ }
+ }
+ }
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<= mdb_entry_encode(0x%08lx): %s\n",
+ (long) e->e_id, e->e_dn );
+
+ return 0;
+}
+
+/* Retrieve an Entry that was stored using entry_encode above.
+ *
+ * Note: everything is stored in a single contiguous block, so
+ * you can not free individual attributes or names from this
+ * structure. Attempting to do so will likely corrupt memory.
+ */
+
+int mdb_entry_decode(Operation *op, MDB_txn *txn, MDB_val *data, ID id, Entry **e)
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ int i, j, nattrs, nvals;
+ int rc;
+ Attribute *a;
+ Entry *x;
+ const char *text;
+ unsigned int *lp = (unsigned int *)data->mv_data;
+ unsigned char *ptr;
+ BerVarray bptr;
+ MDB_cursor *mvc = NULL;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> mdb_entry_decode:\n" );
+
+ nattrs = *lp++;
+ nvals = *lp++;
+ x = mdb_entry_alloc(op, nattrs, nvals);
+ x->e_ocflags = *lp++;
+ if (!nvals) {
+ goto done;
+ }
+ a = x->e_attrs;
+ bptr = a->a_vals;
+ i = *lp++;
+ ptr = (unsigned char *)(lp + i);
+
+ for (;nattrs>0; nattrs--) {
+ int have_nval = 0, multi = 0;
+ a->a_flags = SLAP_ATTR_DONT_FREE_DATA | SLAP_ATTR_DONT_FREE_VALS;
+ i = *lp++;
+ if (i & MDB_AT_SORTED) {
+ i ^= MDB_AT_SORTED;
+ a->a_flags |= SLAP_ATTR_SORTED_VALS;
+ }
+ if (i & MDB_AT_MULTI) {
+ i ^= MDB_AT_MULTI;
+ a->a_flags |= SLAP_ATTR_BIG_MULTI;
+ multi = 1;
+ }
+ if (i > mdb->mi_numads) {
+ rc = mdb_ad_read(mdb, txn);
+ if (rc)
+ goto leave;
+ if (i > mdb->mi_numads) {
+ Debug( LDAP_DEBUG_ANY,
+ "mdb_entry_decode: attribute index %d not recognized\n",
+ i );
+ rc = LDAP_OTHER;
+ goto leave;
+ }
+ }
+ a->a_desc = mdb->mi_ads[i];
+ a->a_numvals = *lp++;
+ if (a->a_numvals & MDB_AT_NVALS) {
+ a->a_numvals ^= MDB_AT_NVALS;
+ have_nval = 1;
+ }
+ a->a_vals = bptr;
+ if (multi) {
+ if (!mvc) {
+ rc = mdb_cursor_open(txn, mdb->mi_dbis[MDB_ID2VAL], &mvc);
+ if (rc)
+ goto leave;
+ }
+ i = a->a_numvals;
+ mdb_mval_get(op, mvc, id, a, have_nval);
+ bptr += i + 1;
+ if (have_nval)
+ bptr += i + 1;
+ } else {
+ for (i=0; i<a->a_numvals; i++) {
+ bptr->bv_len = *lp++;
+ bptr->bv_val = (char *)ptr;
+ ptr += bptr->bv_len+1;
+ bptr++;
+ }
+ bptr->bv_val = NULL;
+ bptr->bv_len = 0;
+ bptr++;
+
+ if (have_nval) {
+ a->a_nvals = bptr;
+ for (i=0; i<a->a_numvals; i++) {
+ bptr->bv_len = *lp++;
+ bptr->bv_val = (char *)ptr;
+ ptr += bptr->bv_len+1;
+ bptr++;
+ }
+ bptr->bv_val = NULL;
+ bptr->bv_len = 0;
+ bptr++;
+ } else {
+ a->a_nvals = a->a_vals;
+ }
+ }
+
+ /* FIXME: This is redundant once a sorted entry is saved into the DB */
+ if (( a->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL )
+ && !(a->a_flags & SLAP_ATTR_SORTED_VALS)) {
+ rc = slap_sort_vals( (Modifications *)a, &text, &j, NULL );
+ if ( rc == LDAP_SUCCESS ) {
+ a->a_flags |= SLAP_ATTR_SORTED_VALS;
+ } else if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
+ /* should never happen */
+ Debug( LDAP_DEBUG_ANY,
+ "mdb_entry_decode: attributeType %s value #%d provided more than once\n",
+ a->a_desc->ad_cname.bv_val, j );
+ goto leave;
+ }
+ }
+ a->a_next = a+1;
+ a = a->a_next;
+ }
+ a[-1].a_next = NULL;
+done:
+ Debug(LDAP_DEBUG_TRACE, "<= mdb_entry_decode\n" );
+ *e = x;
+ rc = 0;
+
+leave:
+ if (mvc)
+ mdb_cursor_close(mvc);
+ return rc;
+}
diff --git a/servers/slapd/back-mdb/idl.c b/servers/slapd/back-mdb/idl.c
new file mode 100644
index 0000000..c7ffbfa
--- /dev/null
+++ b/servers/slapd/back-mdb/idl.c
@@ -0,0 +1,1293 @@
+/* idl.c - ldap id list handling routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+#include "idl.h"
+
+unsigned int MDB_idl_logn = MDB_IDL_LOGN;
+unsigned int MDB_idl_db_size = 1 << MDB_IDL_LOGN;
+unsigned int MDB_idl_um_size = 1 << (MDB_IDL_LOGN+1);
+unsigned int MDB_idl_db_max = (1 << MDB_IDL_LOGN) - 1;
+unsigned int MDB_idl_um_max = (1 << (MDB_IDL_LOGN+1)) - 1;
+
+#define IDL_MAX(x,y) ( (x) > (y) ? (x) : (y) )
+#define IDL_MIN(x,y) ( (x) < (y) ? (x) : (y) )
+#define IDL_CMP(x,y) ( (x) < (y) ? -1 : (x) > (y) )
+
+#if IDL_DEBUG > 0
+static void idl_check( ID *ids )
+{
+ if( MDB_IDL_IS_RANGE( ids ) ) {
+ assert( MDB_IDL_RANGE_FIRST(ids) <= MDB_IDL_RANGE_LAST(ids) );
+ } else {
+ ID i;
+ for( i=1; i < ids[0]; i++ ) {
+ assert( ids[i+1] > ids[i] );
+ }
+ }
+}
+
+#if IDL_DEBUG > 1
+static void idl_dump( ID *ids )
+{
+ if( MDB_IDL_IS_RANGE( ids ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "IDL: range ( %ld - %ld )\n",
+ (long) MDB_IDL_RANGE_FIRST( ids ),
+ (long) MDB_IDL_RANGE_LAST( ids ) );
+
+ } else {
+ ID i;
+ Debug( LDAP_DEBUG_ANY, "IDL: size %ld", (long) ids[0] );
+
+ for( i=1; i<=ids[0]; i++ ) {
+ if( i % 16 == 1 ) {
+ Debug( LDAP_DEBUG_ANY, "\n" );
+ }
+ Debug( LDAP_DEBUG_ANY, " %02lx", (long) ids[i] );
+ }
+
+ Debug( LDAP_DEBUG_ANY, "\n" );
+ }
+
+ idl_check( ids );
+}
+#endif /* IDL_DEBUG > 1 */
+#endif /* IDL_DEBUG > 0 */
+
+void mdb_idl_reset()
+{
+ if ( !MDB_idl_logn )
+ MDB_idl_logn = MDB_IDL_LOGN;
+
+ MDB_idl_db_size = 1 << MDB_idl_logn;
+ MDB_idl_um_size = 1 << (MDB_idl_logn+1);
+ MDB_idl_db_max = MDB_idl_db_size - 1;
+ MDB_idl_um_max = MDB_idl_um_size - 1;
+}
+
+unsigned mdb_idl_search( ID *ids, ID id )
+{
+#define IDL_BINARY_SEARCH 1
+#ifdef IDL_BINARY_SEARCH
+ /*
+ * binary search of id in ids
+ * if found, returns position of id
+ * if not found, returns first position greater than id
+ */
+ unsigned base = 0;
+ unsigned cursor = 1;
+ int val = 0;
+ unsigned n = ids[0];
+
+#if IDL_DEBUG > 0
+ idl_check( ids );
+#endif
+
+ while( 0 < n ) {
+ unsigned pivot = n >> 1;
+ cursor = base + pivot + 1;
+ val = IDL_CMP( id, ids[cursor] );
+
+ if( val < 0 ) {
+ n = pivot;
+
+ } else if ( val > 0 ) {
+ base = cursor;
+ n -= pivot + 1;
+
+ } else {
+ return cursor;
+ }
+ }
+
+ if( val > 0 ) {
+ ++cursor;
+ }
+ return cursor;
+
+#else
+ /* (reverse) linear search */
+ int i;
+
+#if IDL_DEBUG > 0
+ idl_check( ids );
+#endif
+
+ for( i=ids[0]; i; i-- ) {
+ if( id > ids[i] ) {
+ break;
+ }
+ }
+
+ return i+1;
+#endif
+}
+
+int mdb_idl_insert( ID *ids, ID id )
+{
+ unsigned x;
+
+#if IDL_DEBUG > 1
+ Debug( LDAP_DEBUG_ANY, "insert: %04lx at %d\n", (long) id, x );
+ idl_dump( ids );
+#elif IDL_DEBUG > 0
+ idl_check( ids );
+#endif
+
+ if (MDB_IDL_IS_RANGE( ids )) {
+ /* if already in range, treat as a dup */
+ if (id >= MDB_IDL_RANGE_FIRST(ids) && id <= MDB_IDL_RANGE_LAST(ids))
+ return -1;
+ if (id < MDB_IDL_RANGE_FIRST(ids))
+ ids[1] = id;
+ else if (id > MDB_IDL_RANGE_LAST(ids))
+ ids[2] = id;
+ return 0;
+ }
+
+ x = mdb_idl_search( ids, id );
+ assert( x > 0 );
+
+ if( x < 1 ) {
+ /* internal error */
+ return -2;
+ }
+
+ if ( x <= ids[0] && ids[x] == id ) {
+ /* duplicate */
+ return -1;
+ }
+
+ if ( ++ids[0] >= MDB_idl_db_max ) {
+ if( id < ids[1] ) {
+ ids[1] = id;
+ ids[2] = ids[ids[0]-1];
+ } else if ( ids[ids[0]-1] < id ) {
+ ids[2] = id;
+ } else {
+ ids[2] = ids[ids[0]-1];
+ }
+ ids[0] = NOID;
+
+ } else {
+ /* insert id */
+ AC_MEMCPY( &ids[x+1], &ids[x], (ids[0]-x) * sizeof(ID) );
+ ids[x] = id;
+ }
+
+#if IDL_DEBUG > 1
+ idl_dump( ids );
+#elif IDL_DEBUG > 0
+ idl_check( ids );
+#endif
+
+ return 0;
+}
+
+static int mdb_idl_delete( ID *ids, ID id )
+{
+ unsigned x;
+
+#if IDL_DEBUG > 1
+ Debug( LDAP_DEBUG_ANY, "delete: %04lx at %d\n", (long) id, x );
+ idl_dump( ids );
+#elif IDL_DEBUG > 0
+ idl_check( ids );
+#endif
+
+ if (MDB_IDL_IS_RANGE( ids )) {
+ /* If deleting a range boundary, adjust */
+ if ( ids[1] == id )
+ ids[1]++;
+ else if ( ids[2] == id )
+ ids[2]--;
+ /* deleting from inside a range is a no-op */
+
+ /* If the range has collapsed, re-adjust */
+ if ( ids[1] > ids[2] )
+ ids[0] = 0;
+ else if ( ids[1] == ids[2] )
+ ids[1] = 1;
+ return 0;
+ }
+
+ x = mdb_idl_search( ids, id );
+ assert( x > 0 );
+
+ if( x <= 0 ) {
+ /* internal error */
+ return -2;
+ }
+
+ if( x > ids[0] || ids[x] != id ) {
+ /* not found */
+ return -1;
+
+ } else if ( --ids[0] == 0 ) {
+ if( x != 1 ) {
+ return -3;
+ }
+
+ } else {
+ AC_MEMCPY( &ids[x], &ids[x+1], (1+ids[0]-x) * sizeof(ID) );
+ }
+
+#if IDL_DEBUG > 1
+ idl_dump( ids );
+#elif IDL_DEBUG > 0
+ idl_check( ids );
+#endif
+
+ return 0;
+}
+
+static char *
+mdb_show_key(
+ char *buf,
+ void *val,
+ size_t len )
+{
+ if ( len == 4 /* LUTIL_HASH_BYTES */ ) {
+ unsigned char *c = val;
+ sprintf( buf, "[%02x%02x%02x%02x]", c[0], c[1], c[2], c[3] );
+ return buf;
+ } else {
+ return val;
+ }
+}
+
+int
+mdb_idl_fetch_key(
+ BackendDB *be,
+ MDB_txn *txn,
+ MDB_dbi dbi,
+ MDB_val *key,
+ ID *ids,
+ MDB_cursor **saved_cursor,
+ int get_flag )
+{
+ MDB_val data, key2, *kptr;
+ MDB_cursor *cursor;
+ ID *i;
+ size_t len;
+ int rc;
+ MDB_cursor_op opflag;
+
+ char keybuf[16];
+
+ Debug( LDAP_DEBUG_ARGS,
+ "mdb_idl_fetch_key: %s\n",
+ mdb_show_key( keybuf, key->mv_data, key->mv_size ) );
+
+ assert( ids != NULL );
+
+ if ( saved_cursor && *saved_cursor ) {
+ opflag = MDB_NEXT;
+ } else if ( get_flag == LDAP_FILTER_GE ) {
+ opflag = MDB_SET_RANGE;
+ } else if ( get_flag == LDAP_FILTER_LE ) {
+ opflag = MDB_FIRST;
+ } else {
+ opflag = MDB_SET;
+ }
+
+ /* If we're not reusing an existing cursor, get a new one */
+ if( opflag != MDB_NEXT ) {
+ rc = mdb_cursor_open( txn, dbi, &cursor );
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "=> mdb_idl_fetch_key: "
+ "cursor failed: %s (%d)\n", mdb_strerror(rc), rc );
+ return rc;
+ }
+ } else {
+ cursor = *saved_cursor;
+ }
+
+ /* If this is a LE lookup, save original key so we can determine
+ * when to stop. If this is a GE lookup, save the key since it
+ * will be overwritten.
+ */
+ if ( get_flag == LDAP_FILTER_LE || get_flag == LDAP_FILTER_GE ) {
+ key2.mv_data = keybuf;
+ key2.mv_size = key->mv_size;
+ AC_MEMCPY( keybuf, key->mv_data, key->mv_size );
+ kptr = &key2;
+ } else {
+ kptr = key;
+ }
+ len = key->mv_size;
+ rc = mdb_cursor_get( cursor, kptr, &data, opflag );
+
+ /* skip presence key on range inequality lookups */
+ while (rc == 0 && kptr->mv_size != len) {
+ rc = mdb_cursor_get( cursor, kptr, &data, MDB_NEXT_NODUP );
+ }
+ /* If we're doing a LE compare and the new key is greater than
+ * our search key, we're done
+ */
+ if (rc == 0 && get_flag == LDAP_FILTER_LE && memcmp( kptr->mv_data,
+ key->mv_data, key->mv_size ) > 0 ) {
+ rc = MDB_NOTFOUND;
+ }
+ if (rc == 0) {
+ i = ids+1;
+ rc = mdb_cursor_get( cursor, key, &data, MDB_GET_MULTIPLE );
+ while (rc == 0) {
+ memcpy( i, data.mv_data, data.mv_size );
+ i += data.mv_size / sizeof(ID);
+ rc = mdb_cursor_get( cursor, key, &data, MDB_NEXT_MULTIPLE );
+ }
+ if ( rc == MDB_NOTFOUND ) rc = 0;
+ ids[0] = i - &ids[1];
+ /* On disk, a range is denoted by 0 in the first element */
+ if (ids[1] == 0) {
+ if (ids[0] != MDB_IDL_RANGE_SIZE) {
+ Debug( LDAP_DEBUG_ANY, "=> mdb_idl_fetch_key: "
+ "range size mismatch: expected %d, got %ld\n",
+ MDB_IDL_RANGE_SIZE, ids[0] );
+ mdb_cursor_close( cursor );
+ return -1;
+ }
+ MDB_IDL_RANGE( ids, ids[2], ids[3] );
+ }
+ data.mv_size = MDB_IDL_SIZEOF(ids);
+ }
+
+ if ( saved_cursor && rc == 0 ) {
+ if ( !*saved_cursor )
+ *saved_cursor = cursor;
+ }
+ else
+ mdb_cursor_close( cursor );
+
+ if( rc == MDB_NOTFOUND ) {
+ return rc;
+
+ } else if( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "=> mdb_idl_fetch_key: "
+ "get failed: %s (%d)\n",
+ mdb_strerror(rc), rc );
+ return rc;
+
+ } else if ( data.mv_size == 0 || data.mv_size % sizeof( ID ) ) {
+ /* size not multiple of ID size */
+ Debug( LDAP_DEBUG_ANY, "=> mdb_idl_fetch_key: "
+ "odd size: expected %ld multiple, got %ld\n",
+ (long) sizeof( ID ), (long) data.mv_size );
+ return -1;
+
+ } else if ( data.mv_size != MDB_IDL_SIZEOF(ids) ) {
+ /* size mismatch */
+ Debug( LDAP_DEBUG_ANY, "=> mdb_idl_fetch_key: "
+ "get size mismatch: expected %ld, got %ld\n",
+ (long) ((1 + ids[0]) * sizeof( ID )), (long) data.mv_size );
+ return -1;
+ }
+
+ return rc;
+}
+
+int
+mdb_idl_insert_keys(
+ BackendDB *be,
+ MDB_cursor *cursor,
+ struct berval *keys,
+ ID id )
+{
+ struct mdb_info *mdb = be->be_private;
+ MDB_val key, data;
+ ID lo, hi, *i;
+ char *err;
+ int rc = 0, k;
+ unsigned int flag = MDB_NODUPDATA;
+#ifndef MISALIGNED_OK
+ int kbuf[2];
+#endif
+
+ {
+ char buf[16];
+ Debug( LDAP_DEBUG_ARGS,
+ "mdb_idl_insert_keys: %lx %s\n",
+ (long) id, mdb_show_key( buf, keys->bv_val, keys->bv_len ) );
+ }
+
+ assert( id != NOID );
+
+#ifndef MISALIGNED_OK
+ if (keys[0].bv_len & ALIGNER)
+ kbuf[1] = 0;
+#endif
+ for ( k=0; keys[k].bv_val; k++ ) {
+ /* Fetch the first data item for this key, to see if it
+ * exists and if it's a range.
+ */
+#ifndef MISALIGNED_OK
+ if (keys[k].bv_len & ALIGNER) {
+ key.mv_size = sizeof(kbuf);
+ key.mv_data = kbuf;
+ memcpy(key.mv_data, keys[k].bv_val, keys[k].bv_len);
+ } else
+#endif
+ {
+ key.mv_size = keys[k].bv_len;
+ key.mv_data = keys[k].bv_val;
+ }
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
+ err = "c_get";
+ if ( rc == 0 ) {
+ i = data.mv_data;
+ memcpy(&lo, data.mv_data, sizeof(ID));
+ if ( lo != 0 ) {
+ /* not a range, count the number of items */
+ size_t count;
+ rc = mdb_cursor_count( cursor, &count );
+ if ( rc != 0 ) {
+ err = "c_count";
+ goto fail;
+ }
+ if ( count >= MDB_idl_db_max ) {
+ /* No room, convert to a range */
+ lo = *i;
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_LAST_DUP );
+ if ( rc != 0 && rc != MDB_NOTFOUND ) {
+ err = "c_get last_dup";
+ goto fail;
+ }
+ i = data.mv_data;
+ hi = *i;
+ /* Update hi/lo if needed */
+ if ( id < lo ) {
+ lo = id;
+ } else if ( id > hi ) {
+ hi = id;
+ }
+ /* delete the old key */
+ rc = mdb_cursor_del( cursor, MDB_NODUPDATA );
+ if ( rc != 0 ) {
+ err = "c_del dups";
+ goto fail;
+ }
+ /* Store the range */
+ data.mv_size = sizeof(ID);
+ data.mv_data = &id;
+ id = 0;
+ rc = mdb_cursor_put( cursor, &key, &data, 0 );
+ if ( rc != 0 ) {
+ err = "c_put range";
+ goto fail;
+ }
+ id = lo;
+ rc = mdb_cursor_put( cursor, &key, &data, 0 );
+ if ( rc != 0 ) {
+ err = "c_put lo";
+ goto fail;
+ }
+ id = hi;
+ rc = mdb_cursor_put( cursor, &key, &data, 0 );
+ if ( rc != 0 ) {
+ err = "c_put hi";
+ goto fail;
+ }
+ } else {
+ /* There's room, just store it */
+ if (id == mdb->mi_nextid)
+ flag |= MDB_APPENDDUP;
+ goto put1;
+ }
+ } else {
+ /* It's a range, see if we need to rewrite
+ * the boundaries
+ */
+ lo = i[1];
+ hi = i[2];
+ if ( id < lo || id > hi ) {
+ /* position on lo */
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_NEXT_DUP );
+ if ( rc != 0 ) {
+ err = "c_get lo";
+ goto fail;
+ }
+ if ( id > hi ) {
+ /* position on hi */
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_NEXT_DUP );
+ if ( rc != 0 ) {
+ err = "c_get hi";
+ goto fail;
+ }
+ }
+ data.mv_size = sizeof(ID);
+ data.mv_data = &id;
+ /* Replace the current lo/hi */
+ rc = mdb_cursor_put( cursor, &key, &data, MDB_CURRENT );
+ if ( rc != 0 ) {
+ err = "c_put lo/hi";
+ goto fail;
+ }
+ }
+ }
+ } else if ( rc == MDB_NOTFOUND ) {
+ flag &= ~MDB_APPENDDUP;
+put1: data.mv_data = &id;
+ data.mv_size = sizeof(ID);
+ rc = mdb_cursor_put( cursor, &key, &data, flag );
+ /* Don't worry if it's already there */
+ if ( rc == MDB_KEYEXIST )
+ rc = 0;
+ if ( rc ) {
+ err = "c_put id";
+ goto fail;
+ }
+ } else {
+ /* initial c_get failed, nothing was done */
+fail:
+ Debug( LDAP_DEBUG_ANY, "=> mdb_idl_insert_keys: "
+ "%s failed: %s (%d)\n", err, mdb_strerror(rc), rc );
+ break;
+ }
+ }
+ return rc;
+}
+
+int
+mdb_idl_delete_keys(
+ BackendDB *be,
+ MDB_cursor *cursor,
+ struct berval *keys,
+ ID id )
+{
+ int rc = 0, k;
+ MDB_val key, data;
+ ID lo, hi, tmp, *i;
+ char *err;
+#ifndef MISALIGNED_OK
+ int kbuf[2];
+#endif
+
+ {
+ char buf[16];
+ Debug( LDAP_DEBUG_ARGS,
+ "mdb_idl_delete_keys: %lx %s\n",
+ (long) id, mdb_show_key( buf, keys->bv_val, keys->bv_len ) );
+ }
+ assert( id != NOID );
+
+#ifndef MISALIGNED_OK
+ if (keys[0].bv_len & ALIGNER)
+ kbuf[1] = 0;
+#endif
+ for ( k=0; keys[k].bv_val; k++) {
+ /* Fetch the first data item for this key, to see if it
+ * exists and if it's a range.
+ */
+#ifndef MISALIGNED_OK
+ if (keys[k].bv_len & ALIGNER) {
+ key.mv_size = sizeof(kbuf);
+ key.mv_data = kbuf;
+ memcpy(key.mv_data, keys[k].bv_val, keys[k].bv_len);
+ } else
+#endif
+ {
+ key.mv_size = keys[k].bv_len;
+ key.mv_data = keys[k].bv_val;
+ }
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
+ err = "c_get";
+ if ( rc == 0 ) {
+ memcpy( &tmp, data.mv_data, sizeof(ID) );
+ i = data.mv_data;
+ if ( tmp != 0 ) {
+ /* Not a range, just delete it */
+ data.mv_data = &id;
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_GET_BOTH );
+ if ( rc != 0 ) {
+ err = "c_get id";
+ goto fail;
+ }
+ rc = mdb_cursor_del( cursor, 0 );
+ if ( rc != 0 ) {
+ err = "c_del id";
+ goto fail;
+ }
+ } else {
+ /* It's a range, see if we need to rewrite
+ * the boundaries
+ */
+ lo = i[1];
+ hi = i[2];
+ if ( id == lo || id == hi ) {
+ ID lo2 = lo, hi2 = hi;
+ if ( id == lo ) {
+ lo2++;
+ } else if ( id == hi ) {
+ hi2--;
+ }
+ if ( lo2 >= hi2 ) {
+ /* The range has collapsed... */
+ /* delete the range marker */
+ rc = mdb_cursor_del( cursor, 0 );
+ if ( rc != 0 ) {
+ err = "c_del dup1";
+ goto fail;
+ }
+ /* skip past deleted marker */
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_NEXT_DUP );
+ if ( rc != 0 ) {
+ err = "c_get dup1";
+ goto fail;
+ }
+ /* delete the requested id */
+ if ( id == hi ) {
+ /* skip lo */
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_NEXT_DUP );
+ if ( rc != 0 ) {
+ err = "c_get dup2";
+ goto fail;
+ }
+ }
+ rc = mdb_cursor_del( cursor, 0 );
+ if ( rc != 0 ) {
+ err = "c_del dup2";
+ goto fail;
+ }
+ } else {
+ /* position on lo */
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_NEXT_DUP );
+ if ( id == lo )
+ data.mv_data = &lo2;
+ else {
+ /* position on hi */
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_NEXT_DUP );
+ data.mv_data = &hi2;
+ }
+ /* Replace the current lo/hi */
+ data.mv_size = sizeof(ID);
+ rc = mdb_cursor_put( cursor, &key, &data, MDB_CURRENT );
+ if ( rc != 0 ) {
+ err = "c_put lo/hi";
+ goto fail;
+ }
+ }
+ }
+ }
+ } else {
+ /* initial c_get failed, nothing was done */
+fail:
+ if ( rc == MDB_NOTFOUND )
+ rc = 0;
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY, "=> mdb_idl_delete_key: "
+ "%s failed: %s (%d)\n", err, mdb_strerror(rc), rc );
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+
+/*
+ * idl_intersection - return a = a intersection b
+ */
+int
+mdb_idl_intersection(
+ ID *a,
+ ID *b )
+{
+ ID ida, idb;
+ ID idmax, idmin;
+ ID cursora = 0, cursorb = 0, cursorc;
+ int swap = 0;
+
+ if ( MDB_IDL_IS_ZERO( a ) || MDB_IDL_IS_ZERO( b ) ) {
+ a[0] = 0;
+ return 0;
+ }
+
+ idmin = IDL_MAX( MDB_IDL_FIRST(a), MDB_IDL_FIRST(b) );
+ idmax = IDL_MIN( MDB_IDL_LAST(a), MDB_IDL_LAST(b) );
+ if ( idmin > idmax ) {
+ a[0] = 0;
+ return 0;
+ } else if ( idmin == idmax ) {
+ a[0] = 1;
+ a[1] = idmin;
+ return 0;
+ }
+
+ if ( MDB_IDL_IS_RANGE( a ) ) {
+ if ( MDB_IDL_IS_RANGE(b) ) {
+ /* If both are ranges, just shrink the boundaries */
+ a[1] = idmin;
+ a[2] = idmax;
+ return 0;
+ } else {
+ /* Else swap so that b is the range, a is a list */
+ ID *tmp = a;
+ a = b;
+ b = tmp;
+ swap = 1;
+ }
+ }
+
+ /* If a range completely covers the list, the result is
+ * just the list.
+ */
+ if ( MDB_IDL_IS_RANGE( b )
+ && MDB_IDL_RANGE_FIRST( b ) <= MDB_IDL_FIRST( a )
+ && MDB_IDL_RANGE_LAST( b ) >= MDB_IDL_LLAST( a ) ) {
+ goto done;
+ }
+
+ /* Fine, do the intersection one element at a time.
+ * First advance to idmin in both IDLs.
+ */
+ cursora = cursorb = idmin;
+ ida = mdb_idl_first( a, &cursora );
+ idb = mdb_idl_first( b, &cursorb );
+ cursorc = 0;
+
+ while( ida <= idmax || idb <= idmax ) {
+ if( ida == idb ) {
+ a[++cursorc] = ida;
+ ida = mdb_idl_next( a, &cursora );
+ idb = mdb_idl_next( b, &cursorb );
+ } else if ( ida < idb ) {
+ ida = mdb_idl_next( a, &cursora );
+ } else {
+ idb = mdb_idl_next( b, &cursorb );
+ }
+ }
+ a[0] = cursorc;
+done:
+ if (swap)
+ MDB_IDL_CPY( b, a );
+
+ return 0;
+}
+
+
+/*
+ * idl_union - return a = a union b
+ */
+int
+mdb_idl_union(
+ ID *a,
+ ID *b )
+{
+ ID ida, idb;
+ ID cursora = 0, cursorb = 0, cursorc;
+
+ if ( MDB_IDL_IS_ZERO( b ) ) {
+ return 0;
+ }
+
+ if ( MDB_IDL_IS_ZERO( a ) ) {
+ MDB_IDL_CPY( a, b );
+ return 0;
+ }
+
+ if ( MDB_IDL_IS_RANGE( a ) || MDB_IDL_IS_RANGE(b) ) {
+over: ida = IDL_MIN( MDB_IDL_FIRST(a), MDB_IDL_FIRST(b) );
+ idb = IDL_MAX( MDB_IDL_LAST(a), MDB_IDL_LAST(b) );
+ a[0] = NOID;
+ a[1] = ida;
+ a[2] = idb;
+ return 0;
+ }
+
+ ida = mdb_idl_first( a, &cursora );
+ idb = mdb_idl_first( b, &cursorb );
+
+ cursorc = b[0];
+
+ /* The distinct elements of a are cat'd to b */
+ while( ida != NOID || idb != NOID ) {
+ if ( ida < idb ) {
+ if( ++cursorc > MDB_idl_um_max ) {
+ goto over;
+ }
+ b[cursorc] = ida;
+ ida = mdb_idl_next( a, &cursora );
+
+ } else {
+ if ( ida == idb )
+ ida = mdb_idl_next( a, &cursora );
+ idb = mdb_idl_next( b, &cursorb );
+ }
+ }
+
+ /* b is copied back to a in sorted order */
+ a[0] = cursorc;
+ cursora = 1;
+ cursorb = 1;
+ cursorc = b[0]+1;
+ while (cursorb <= b[0] || cursorc <= a[0]) {
+ if (cursorc > a[0])
+ idb = NOID;
+ else
+ idb = b[cursorc];
+ if (cursorb <= b[0] && b[cursorb] < idb)
+ a[cursora++] = b[cursorb++];
+ else {
+ a[cursora++] = idb;
+ cursorc++;
+ }
+ }
+
+ return 0;
+}
+
+
+#if 0
+/*
+ * mdb_idl_notin - return a intersection ~b (or a minus b)
+ */
+int
+mdb_idl_notin(
+ ID *a,
+ ID *b,
+ ID *ids )
+{
+ ID ida, idb;
+ ID cursora = 0, cursorb = 0;
+
+ if( MDB_IDL_IS_ZERO( a ) ||
+ MDB_IDL_IS_ZERO( b ) ||
+ MDB_IDL_IS_RANGE( b ) )
+ {
+ MDB_IDL_CPY( ids, a );
+ return 0;
+ }
+
+ if( MDB_IDL_IS_RANGE( a ) ) {
+ MDB_IDL_CPY( ids, a );
+ return 0;
+ }
+
+ ida = mdb_idl_first( a, &cursora ),
+ idb = mdb_idl_first( b, &cursorb );
+
+ ids[0] = 0;
+
+ while( ida != NOID ) {
+ if ( idb == NOID ) {
+ /* we could shortcut this */
+ ids[++ids[0]] = ida;
+ ida = mdb_idl_next( a, &cursora );
+
+ } else if ( ida < idb ) {
+ ids[++ids[0]] = ida;
+ ida = mdb_idl_next( a, &cursora );
+
+ } else if ( ida > idb ) {
+ idb = mdb_idl_next( b, &cursorb );
+
+ } else {
+ ida = mdb_idl_next( a, &cursora );
+ idb = mdb_idl_next( b, &cursorb );
+ }
+ }
+
+ return 0;
+}
+#endif
+
+ID mdb_idl_first( ID *ids, ID *cursor )
+{
+ ID pos;
+
+ if ( ids[0] == 0 ) {
+ *cursor = NOID;
+ return NOID;
+ }
+
+ if ( MDB_IDL_IS_RANGE( ids ) ) {
+ if( *cursor < ids[1] ) {
+ *cursor = ids[1];
+ }
+ return *cursor;
+ }
+
+ if ( *cursor == 0 )
+ pos = 1;
+ else
+ pos = mdb_idl_search( ids, *cursor );
+
+ if( pos > ids[0] ) {
+ return NOID;
+ }
+
+ *cursor = pos;
+ return ids[pos];
+}
+
+ID mdb_idl_next( ID *ids, ID *cursor )
+{
+ if ( MDB_IDL_IS_RANGE( ids ) ) {
+ if( ids[2] < ++(*cursor) ) {
+ return NOID;
+ }
+ return *cursor;
+ }
+
+ if ( ++(*cursor) <= ids[0] ) {
+ return ids[*cursor];
+ }
+
+ return NOID;
+}
+
+/* Add one ID to an unsorted list. We ensure that the first element is the
+ * minimum and the last element is the maximum, for fast range compaction.
+ * this means IDLs up to length 3 are always sorted...
+ */
+int mdb_idl_append_one( ID *ids, ID id )
+{
+ if (MDB_IDL_IS_RANGE( ids )) {
+ /* if already in range, treat as a dup */
+ if (id >= MDB_IDL_RANGE_FIRST(ids) && id <= MDB_IDL_RANGE_LAST(ids))
+ return -1;
+ if (id < MDB_IDL_RANGE_FIRST(ids))
+ ids[1] = id;
+ else if (id > MDB_IDL_RANGE_LAST(ids))
+ ids[2] = id;
+ return 0;
+ }
+ if ( ids[0] ) {
+ ID tmp;
+
+ if (id < ids[1]) {
+ tmp = ids[1];
+ ids[1] = id;
+ id = tmp;
+ }
+ if ( ids[0] > 1 && id < ids[ids[0]] ) {
+ tmp = ids[ids[0]];
+ ids[ids[0]] = id;
+ id = tmp;
+ }
+ }
+ ids[0]++;
+ if ( ids[0] >= MDB_idl_um_max ) {
+ ids[0] = NOID;
+ ids[2] = id;
+ } else {
+ ids[ids[0]] = id;
+ }
+ return 0;
+}
+
+/* Append sorted list b to sorted list a. The result is unsorted but
+ * a[1] is the min of the result and a[a[0]] is the max.
+ */
+int mdb_idl_append( ID *a, ID *b )
+{
+ ID ida, idb, tmp, swap = 0;
+
+ if ( MDB_IDL_IS_ZERO( b ) ) {
+ return 0;
+ }
+
+ if ( MDB_IDL_IS_ZERO( a ) ) {
+ MDB_IDL_CPY( a, b );
+ return 0;
+ }
+
+ ida = MDB_IDL_LAST( a );
+ idb = MDB_IDL_LAST( b );
+ if ( MDB_IDL_IS_RANGE( a ) || MDB_IDL_IS_RANGE(b) ||
+ a[0] + b[0] >= MDB_idl_um_max ) {
+ a[2] = IDL_MAX( ida, idb );
+ a[1] = IDL_MIN( a[1], b[1] );
+ a[0] = NOID;
+ return 0;
+ }
+
+ if ( b[0] > 1 && ida > idb ) {
+ swap = idb;
+ a[a[0]] = idb;
+ b[b[0]] = ida;
+ }
+
+ if ( b[1] < a[1] ) {
+ tmp = a[1];
+ a[1] = b[1];
+ } else {
+ tmp = b[1];
+ }
+ a[0]++;
+ a[a[0]] = tmp;
+
+ if ( b[0] > 1 ) {
+ int i = b[0] - 1;
+ AC_MEMCPY(a+a[0]+1, b+2, i * sizeof(ID));
+ a[0] += i;
+ }
+ if ( swap ) {
+ b[b[0]] = swap;
+ }
+ return 0;
+}
+
+#if 1
+
+/* Quicksort + Insertion sort for small arrays */
+
+#define SMALL 8
+#define SWAP(a,b) itmp=(a);(a)=(b);(b)=itmp
+
+void
+mdb_idl_sort( ID *ids, ID *tmp )
+{
+ int *istack = (int *)tmp; /* Private stack, not used by caller */
+ int i,j,k,l,ir,jstack;
+ ID a, itmp;
+
+ if ( MDB_IDL_IS_RANGE( ids ))
+ return;
+
+ ir = ids[0];
+ l = 1;
+ jstack = 0;
+ for(;;) {
+ if (ir - l < SMALL) { /* Insertion sort */
+ for (j=l+1;j<=ir;j++) {
+ a = ids[j];
+ for (i=j-1;i>=1;i--) {
+ if (ids[i] <= a) break;
+ ids[i+1] = ids[i];
+ }
+ ids[i+1] = a;
+ }
+ if (jstack == 0) break;
+ ir = istack[jstack--];
+ l = istack[jstack--];
+ } else {
+ k = (l + ir) >> 1; /* Choose median of left, center, right */
+ SWAP(ids[k], ids[l+1]);
+ if (ids[l] > ids[ir]) {
+ SWAP(ids[l], ids[ir]);
+ }
+ if (ids[l+1] > ids[ir]) {
+ SWAP(ids[l+1], ids[ir]);
+ }
+ if (ids[l] > ids[l+1]) {
+ SWAP(ids[l], ids[l+1]);
+ }
+ i = l+1;
+ j = ir;
+ a = ids[l+1];
+ for(;;) {
+ do i++; while(ids[i] < a);
+ do j--; while(ids[j] > a);
+ if (j < i) break;
+ SWAP(ids[i],ids[j]);
+ }
+ ids[l+1] = ids[j];
+ ids[j] = a;
+ jstack += 2;
+ if (ir-i+1 >= j-l) {
+ istack[jstack] = ir;
+ istack[jstack-1] = i;
+ ir = j-1;
+ } else {
+ istack[jstack] = j-1;
+ istack[jstack-1] = l;
+ l = i;
+ }
+ }
+ }
+}
+
+#else
+
+/* 8 bit Radix sort + insertion sort
+ *
+ * based on code from http://www.cubic.org/docs/radix.htm
+ * with improvements by ebackes@symas.com and hyc@symas.com
+ *
+ * This code is O(n) but has a relatively high constant factor. For lists
+ * up to ~50 Quicksort is slightly faster; up to ~100 they are even.
+ * Much faster than quicksort for lists longer than ~100. Insertion
+ * sort is actually superior for lists <50.
+ */
+
+#define BUCKETS (1<<8)
+#define SMALL 50
+
+void
+mdb_idl_sort( ID *ids, ID *tmp )
+{
+ int count, soft_limit, phase = 0, size = ids[0];
+ ID *idls[2];
+ unsigned char *maxv = (unsigned char *)&ids[size];
+
+ if ( MDB_IDL_IS_RANGE( ids ))
+ return;
+
+ /* Use insertion sort for small lists */
+ if ( size <= SMALL ) {
+ int i,j;
+ ID a;
+
+ for (j=1;j<=size;j++) {
+ a = ids[j];
+ for (i=j-1;i>=1;i--) {
+ if (ids[i] <= a) break;
+ ids[i+1] = ids[i];
+ }
+ ids[i+1] = a;
+ }
+ return;
+ }
+
+ tmp[0] = size;
+ idls[0] = ids;
+ idls[1] = tmp;
+
+#if BYTE_ORDER == BIG_ENDIAN
+ for (soft_limit = 0; !maxv[soft_limit]; soft_limit++);
+#else
+ for (soft_limit = sizeof(ID)-1; !maxv[soft_limit]; soft_limit--);
+#endif
+
+ for (
+#if BYTE_ORDER == BIG_ENDIAN
+ count = sizeof(ID)-1; count >= soft_limit; --count
+#else
+ count = 0; count <= soft_limit; ++count
+#endif
+ ) {
+ unsigned int num[BUCKETS], * np, n, sum;
+ int i;
+ ID *sp, *source, *dest;
+ unsigned char *bp, *source_start;
+
+ source = idls[phase]+1;
+ dest = idls[phase^1]+1;
+ source_start = ((unsigned char *) source) + count;
+
+ np = num;
+ for ( i = BUCKETS; i > 0; --i ) *np++ = 0;
+
+ /* count occurrences of every byte value */
+ bp = source_start;
+ for ( i = size; i > 0; --i, bp += sizeof(ID) )
+ num[*bp]++;
+
+ /* transform count into index by summing elements and storing
+ * into same array
+ */
+ sum = 0;
+ np = num;
+ for ( i = BUCKETS; i > 0; --i ) {
+ n = *np;
+ *np++ = sum;
+ sum += n;
+ }
+
+ /* fill dest with the right values in the right place */
+ bp = source_start;
+ sp = source;
+ for ( i = size; i > 0; --i, bp += sizeof(ID) ) {
+ np = num + *bp;
+ dest[*np] = *sp++;
+ ++(*np);
+ }
+ phase ^= 1;
+ }
+
+ /* copy back from temp if needed */
+ if ( phase ) {
+ ids++; tmp++;
+ for ( count = 0; count < size; ++count )
+ *ids++ = *tmp++;
+ }
+}
+#endif /* Quick vs Radix */
+
+unsigned mdb_id2l_search( ID2L ids, ID id )
+{
+ /*
+ * binary search of id in ids
+ * if found, returns position of id
+ * if not found, returns first position greater than id
+ */
+ unsigned base = 0;
+ unsigned cursor = 1;
+ int val = 0;
+ unsigned n = ids[0].mid;
+
+ while( 0 < n ) {
+ unsigned pivot = n >> 1;
+ cursor = base + pivot + 1;
+ val = IDL_CMP( id, ids[cursor].mid );
+
+ if( val < 0 ) {
+ n = pivot;
+
+ } else if ( val > 0 ) {
+ base = cursor;
+ n -= pivot + 1;
+
+ } else {
+ return cursor;
+ }
+ }
+
+ if( val > 0 ) {
+ ++cursor;
+ }
+ return cursor;
+}
+
+int mdb_id2l_insert( ID2L ids, ID2 *id )
+{
+ unsigned x, i;
+
+ x = mdb_id2l_search( ids, id->mid );
+ assert( x > 0 );
+
+ if( x < 1 ) {
+ /* internal error */
+ return -2;
+ }
+
+ if ( x <= ids[0].mid && ids[x].mid == id->mid ) {
+ /* duplicate */
+ return -1;
+ }
+
+ if ( ids[0].mid >= MDB_idl_um_max ) {
+ /* too big */
+ return -2;
+
+ } else {
+ /* insert id */
+ ids[0].mid++;
+ for (i=ids[0].mid; i>x; i--)
+ ids[i] = ids[i-1];
+ ids[x] = *id;
+ }
+
+ return 0;
+}
diff --git a/servers/slapd/back-mdb/idl.h b/servers/slapd/back-mdb/idl.h
new file mode 100644
index 0000000..1405571
--- /dev/null
+++ b/servers/slapd/back-mdb/idl.h
@@ -0,0 +1,118 @@
+/* idl.h - ldap mdb back-end ID list header file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _MDB_IDL_H_
+#define _MDB_IDL_H_
+
+/* IDL sizes - likely should be even bigger
+ * limiting factors: sizeof(ID), thread stack size
+ */
+#define MDB_IDL_LOGN 16 /* DB_SIZE is 2^16, UM_SIZE is 2^17 */
+extern unsigned int MDB_idl_logn;
+extern unsigned int MDB_idl_db_size;
+extern unsigned int MDB_idl_um_size;
+extern unsigned int MDB_idl_db_max;
+extern unsigned int MDB_idl_um_max;
+
+#define MDB_IDL_IS_RANGE(ids) ((ids)[0] == NOID)
+#define MDB_IDL_RANGE_SIZE (3)
+#define MDB_IDL_RANGE_SIZEOF (MDB_IDL_RANGE_SIZE * sizeof(ID))
+#define MDB_IDL_SIZEOF(ids) ((MDB_IDL_IS_RANGE(ids) \
+ ? MDB_IDL_RANGE_SIZE : ((ids)[0]+1)) * sizeof(ID))
+
+#define MDB_IDL_RANGE_FIRST(ids) ((ids)[1])
+#define MDB_IDL_RANGE_LAST(ids) ((ids)[2])
+
+#define MDB_IDL_RANGE( ids, f, l ) \
+ do { \
+ (ids)[0] = NOID; \
+ (ids)[1] = (f); \
+ (ids)[2] = (l); \
+ } while(0)
+
+#define MDB_IDL_ZERO(ids) \
+ do { \
+ (ids)[0] = 0; \
+ (ids)[1] = 0; \
+ (ids)[2] = 0; \
+ } while(0)
+
+#define MDB_IDL_IS_ZERO(ids) ( (ids)[0] == 0 )
+#define MDB_IDL_IS_ALL( range, ids ) ( (ids)[0] == NOID \
+ && (ids)[1] <= (range)[1] && (range)[2] <= (ids)[2] )
+
+#define MDB_IDL_CPY( dst, src ) (AC_MEMCPY( dst, src, MDB_IDL_SIZEOF( src ) ))
+
+#define MDB_IDL_ID( mdb, ids, id ) MDB_IDL_RANGE( ids, id, NOID )
+#define MDB_IDL_ALL( ids ) MDB_IDL_RANGE( ids, 1, NOID )
+
+#define MDB_IDL_FIRST( ids ) ( (ids)[1] )
+#define MDB_IDL_LLAST( ids ) ( (ids)[(ids)[0]] )
+#define MDB_IDL_LAST( ids ) ( MDB_IDL_IS_RANGE(ids) \
+ ? (ids)[2] : (ids)[(ids)[0]] )
+
+#define MDB_IDL_N( ids ) ( MDB_IDL_IS_RANGE(ids) \
+ ? ((ids)[2]-(ids)[1])+1 : (ids)[0] )
+
+ /** An ID2 is an ID/value pair.
+ */
+typedef struct ID2 {
+ ID mid; /**< The ID */
+ MDB_val mval; /**< The value */
+} ID2;
+
+ /** An ID2L is an ID2 List, a sorted array of ID2s.
+ * The first element's \b mid member is a count of how many actual
+ * elements are in the array. The \b mptr member of the first element is unused.
+ * The array is sorted in ascending order by \b mid.
+ */
+typedef ID2 *ID2L;
+
+typedef struct IdScopes {
+ MDB_txn *mt;
+ MDB_cursor *mc;
+ ID id;
+ ID2L scopes;
+ ID2L sctmp;
+ int numrdns;
+ int nscope;
+ int oscope;
+ struct berval rdns[MAXRDNS];
+ struct berval nrdns[MAXRDNS];
+} IdScopes;
+
+LDAP_BEGIN_DECL
+ /** Reset IDL params after changing logn */
+void mdb_idl_reset();
+
+
+ /** Search for an ID in an ID2L.
+ * @param[in] ids The ID2L to search.
+ * @param[in] id The ID to search for.
+ * @return The index of the first ID2 whose \b mid member is greater than or equal to \b id.
+ */
+unsigned mdb_id2l_search( ID2L ids, ID id );
+
+
+ /** Insert an ID2 into a ID2L.
+ * @param[in,out] ids The ID2L to insert into.
+ * @param[in] id The ID2 to insert.
+ * @return 0 on success, -1 if the ID was already present in the MIDL2.
+ */
+int mdb_id2l_insert( ID2L ids, ID2 *id );
+LDAP_END_DECL
+
+#endif
diff --git a/servers/slapd/back-mdb/index.c b/servers/slapd/back-mdb/index.c
new file mode 100644
index 0000000..fa9006c
--- /dev/null
+++ b/servers/slapd/back-mdb/index.c
@@ -0,0 +1,577 @@
+/* index.c - routines for dealing with attribute indexes */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-mdb.h"
+#include "lutil_hash.h"
+
+static char presence_keyval[] = {0,0,0,0,0};
+static struct berval presence_key[2] = {BER_BVC(presence_keyval), BER_BVNULL};
+
+AttrInfo *mdb_index_mask(
+ Backend *be,
+ AttributeDescription *desc,
+ struct berval *atname )
+{
+ AttributeType *at;
+ AttrInfo *ai = mdb_attr_mask( be->be_private, desc );
+
+ if( ai ) {
+ *atname = desc->ad_cname;
+ return ai;
+ }
+
+ /* If there is a tagging option, did we ever index the base
+ * type? If so, check for mask, otherwise it's not there.
+ */
+ if( slap_ad_is_tagged( desc ) && desc != desc->ad_type->sat_ad ) {
+ /* has tagging option */
+ ai = mdb_attr_mask( be->be_private, desc->ad_type->sat_ad );
+
+ if ( ai && !( ai->ai_indexmask & SLAP_INDEX_NOTAGS ) ) {
+ *atname = desc->ad_type->sat_cname;
+ return ai;
+ }
+ }
+
+ /* see if supertype defined mask for its subtypes */
+ for( at = desc->ad_type; at != NULL ; at = at->sat_sup ) {
+ /* If no AD, we've never indexed this type */
+ if ( !at->sat_ad ) continue;
+
+ ai = mdb_attr_mask( be->be_private, at->sat_ad );
+
+ if ( ai && !( ai->ai_indexmask & SLAP_INDEX_NOSUBTYPES ) ) {
+ *atname = at->sat_cname;
+ return ai;
+ }
+ }
+
+ return 0;
+}
+
+/* This function is only called when evaluating search filters.
+ */
+int mdb_index_param(
+ Backend *be,
+ AttributeDescription *desc,
+ int ftype,
+ MDB_dbi *dbip,
+ slap_mask_t *maskp,
+ struct berval *prefixp )
+{
+ AttrInfo *ai;
+ slap_mask_t mask, type = 0;
+
+ ai = mdb_index_mask( be, desc, prefixp );
+
+ if ( !ai ) {
+#ifdef MDB_MONITOR_IDX
+ switch ( ftype ) {
+ case LDAP_FILTER_PRESENT:
+ type = SLAP_INDEX_PRESENT;
+ break;
+ case LDAP_FILTER_APPROX:
+ type = SLAP_INDEX_APPROX;
+ break;
+ case LDAP_FILTER_EQUALITY:
+ type = SLAP_INDEX_EQUALITY;
+ break;
+ case LDAP_FILTER_SUBSTRINGS:
+ type = SLAP_INDEX_SUBSTR;
+ break;
+ default:
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+ mdb_monitor_idx_add( be->be_private, desc, type );
+#endif /* MDB_MONITOR_IDX */
+
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+ mask = ai->ai_indexmask;
+
+ switch( ftype ) {
+ case LDAP_FILTER_PRESENT:
+ type = SLAP_INDEX_PRESENT;
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_PRESENT ) ) {
+ *prefixp = presence_key[0];
+ goto done;
+ }
+ break;
+
+ case LDAP_FILTER_APPROX:
+ type = SLAP_INDEX_APPROX;
+ if ( desc->ad_type->sat_approx ) {
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) ) {
+ goto done;
+ }
+ break;
+ }
+
+ /* Use EQUALITY rule and index for approximate match */
+ /* fall thru */
+
+ case LDAP_FILTER_EQUALITY:
+ type = SLAP_INDEX_EQUALITY;
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) ) {
+ goto done;
+ }
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ type = SLAP_INDEX_SUBSTR;
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) ) {
+ goto done;
+ }
+ break;
+
+ default:
+ return LDAP_OTHER;
+ }
+
+#ifdef MDB_MONITOR_IDX
+ mdb_monitor_idx_add( be->be_private, desc, type );
+#endif /* MDB_MONITOR_IDX */
+
+ return LDAP_INAPPROPRIATE_MATCHING;
+
+done:
+ *dbip = ai->ai_dbi;
+ *maskp = mask;
+ return LDAP_SUCCESS;
+}
+
+static int indexer(
+ Operation *op,
+ MDB_txn *txn,
+ struct mdb_attrinfo *ai,
+ AttributeDescription *ad,
+ struct berval *atname,
+ BerVarray vals,
+ ID id,
+ int opid,
+ slap_mask_t mask )
+{
+ int rc;
+ struct berval *keys;
+ MDB_cursor *mc = ai->ai_cursor;
+ mdb_idl_keyfunc *keyfunc;
+ char *err;
+
+ assert( mask != 0 );
+
+ if ( !mc ) {
+ err = "c_open";
+ rc = mdb_cursor_open( txn, ai->ai_dbi, &mc );
+ if ( rc ) goto done;
+ if ( slapMode & SLAP_TOOL_QUICK )
+ ai->ai_cursor = mc;
+ }
+
+ if ( opid == SLAP_INDEX_ADD_OP ) {
+#ifdef MDB_TOOL_IDL_CACHING
+ if (( slapMode & SLAP_TOOL_QUICK ) && slap_tool_thread_max > 2 ) {
+ AttrIxInfo *ax = (AttrIxInfo *)LDAP_SLIST_FIRST(&op->o_extra);
+ ax->ai_ai = ai;
+ keyfunc = mdb_tool_idl_add;
+ mc = (MDB_cursor *)ax;
+ } else
+#endif
+ keyfunc = mdb_idl_insert_keys;
+ } else
+ keyfunc = mdb_idl_delete_keys;
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_PRESENT ) ) {
+ rc = keyfunc( op->o_bd, mc, presence_key, id );
+ if( rc ) {
+ err = "presence";
+ goto done;
+ }
+ }
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) ) {
+ rc = ad->ad_type->sat_equality->smr_indexer(
+ LDAP_FILTER_EQUALITY,
+ mask,
+ ad->ad_type->sat_syntax,
+ ad->ad_type->sat_equality,
+ atname, vals, &keys, op->o_tmpmemctx );
+
+ if( rc == LDAP_SUCCESS && keys != NULL ) {
+ rc = keyfunc( op->o_bd, mc, keys, id );
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+ if ( rc ) {
+ err = "equality";
+ goto done;
+ }
+ }
+ rc = LDAP_SUCCESS;
+ }
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) ) {
+ rc = ad->ad_type->sat_approx->smr_indexer(
+ LDAP_FILTER_APPROX,
+ mask,
+ ad->ad_type->sat_syntax,
+ ad->ad_type->sat_approx,
+ atname, vals, &keys, op->o_tmpmemctx );
+
+ if( rc == LDAP_SUCCESS && keys != NULL ) {
+ rc = keyfunc( op->o_bd, mc, keys, id );
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+ if ( rc ) {
+ err = "approx";
+ goto done;
+ }
+ }
+
+ rc = LDAP_SUCCESS;
+ }
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) ) {
+ rc = ad->ad_type->sat_substr->smr_indexer(
+ LDAP_FILTER_SUBSTRINGS,
+ mask,
+ ad->ad_type->sat_syntax,
+ ad->ad_type->sat_substr,
+ atname, vals, &keys, op->o_tmpmemctx );
+
+ if( rc == LDAP_SUCCESS && keys != NULL ) {
+ rc = keyfunc( op->o_bd, mc, keys, id );
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+ if( rc ) {
+ err = "substr";
+ goto done;
+ }
+ }
+
+ rc = LDAP_SUCCESS;
+ }
+
+done:
+ if ( !(slapMode & SLAP_TOOL_QUICK))
+ mdb_cursor_close( mc );
+ switch( rc ) {
+ /* The callers all know how to deal with these results */
+ case 0:
+ break;
+ /* Anything else is bad news */
+ default:
+ rc = LDAP_OTHER;
+ }
+ return rc;
+}
+
+static int index_at_values(
+ Operation *op,
+ MDB_txn *txn,
+ AttributeDescription *ad,
+ AttributeType *type,
+ struct berval *tags,
+ BerVarray vals,
+ ID id,
+ int opid )
+{
+ int rc;
+ slap_mask_t mask = 0;
+ int ixop = opid;
+ AttrInfo *ai = NULL;
+
+ if ( opid == MDB_INDEX_UPDATE_OP )
+ ixop = SLAP_INDEX_ADD_OP;
+
+ if( type->sat_sup ) {
+ /* recurse */
+ rc = index_at_values( op, txn, NULL,
+ type->sat_sup, tags,
+ vals, id, opid );
+
+ if( rc ) return rc;
+ }
+
+ /* If this type has no AD, we've never used it before */
+ if( type->sat_ad ) {
+ ai = mdb_attr_mask( op->o_bd->be_private, type->sat_ad );
+ if ( ai && ( ai->ai_indexmask || ai->ai_newmask )) {
+#ifdef LDAP_COMP_MATCH
+ /* component indexing */
+ if ( ai->ai_cr ) {
+ ComponentReference *cr;
+ for( cr = ai->ai_cr ; cr ; cr = cr->cr_next ) {
+ rc = indexer( op, txn, ai, cr->cr_ad, &type->sat_cname,
+ cr->cr_nvals, id, ixop,
+ cr->cr_indexmask );
+ }
+ }
+#endif
+ ad = type->sat_ad;
+ /* If we're updating the index, just set the new bits that aren't
+ * already in the old mask.
+ */
+ if ( opid == MDB_INDEX_UPDATE_OP )
+ mask = ai->ai_newmask & ~ai->ai_indexmask;
+ else
+ /* For regular updates, if there is a newmask use it. Otherwise
+ * just use the old mask.
+ */
+ mask = ai->ai_newmask ? ai->ai_newmask : ai->ai_indexmask;
+ if( mask ) {
+ rc = indexer( op, txn, ai, ad, &type->sat_cname,
+ vals, id, ixop, mask );
+
+ if( rc ) return rc;
+ }
+ }
+ }
+
+ if( tags->bv_len ) {
+ AttributeDescription *desc;
+
+ desc = ad_find_tags( type, tags );
+ if( desc ) {
+ ai = mdb_attr_mask( op->o_bd->be_private, desc );
+
+ if( ai && ( ai->ai_indexmask || ai->ai_newmask )) {
+ if ( opid == MDB_INDEX_UPDATE_OP )
+ mask = ai->ai_newmask & ~ai->ai_indexmask;
+ else
+ mask = ai->ai_newmask ? ai->ai_newmask : ai->ai_indexmask;
+ if ( mask ) {
+ rc = indexer( op, txn, ai, desc, &desc->ad_cname,
+ vals, id, ixop, mask );
+
+ if( rc ) {
+ return rc;
+ }
+ }
+ }
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int mdb_index_values(
+ Operation *op,
+ MDB_txn *txn,
+ AttributeDescription *desc,
+ BerVarray vals,
+ ID id,
+ int opid )
+{
+ int rc;
+
+ /* Never index ID 0 */
+ if ( id == 0 )
+ return 0;
+
+ rc = index_at_values( op, txn, desc,
+ desc->ad_type, &desc->ad_tags,
+ vals, id, opid );
+
+ return rc;
+}
+
+/* Get the list of which indices apply to this attr */
+int
+mdb_index_recset(
+ struct mdb_info *mdb,
+ Attribute *a,
+ AttributeType *type,
+ struct berval *tags,
+ IndexRec *ir )
+{
+ int rc, slot;
+ AttrList *al;
+
+ if( type->sat_sup ) {
+ /* recurse */
+ rc = mdb_index_recset( mdb, a, type->sat_sup, tags, ir );
+ if( rc ) return rc;
+ }
+ /* If this type has no AD, we've never used it before */
+ if( type->sat_ad ) {
+ slot = mdb_attr_slot( mdb, type->sat_ad, NULL );
+ if ( slot >= 0 ) {
+ ir[slot].ir_ai = mdb->mi_attrs[slot];
+ al = ch_malloc( sizeof( AttrList ));
+ al->attr = a;
+ al->next = ir[slot].ir_attrs;
+ ir[slot].ir_attrs = al;
+ }
+ }
+ if( tags->bv_len ) {
+ AttributeDescription *desc;
+
+ desc = ad_find_tags( type, tags );
+ if( desc ) {
+ slot = mdb_attr_slot( mdb, desc, NULL );
+ if ( slot >= 0 ) {
+ ir[slot].ir_ai = mdb->mi_attrs[slot];
+ al = ch_malloc( sizeof( AttrList ));
+ al->attr = a;
+ al->next = ir[slot].ir_attrs;
+ ir[slot].ir_attrs = al;
+ }
+ }
+ }
+ return LDAP_SUCCESS;
+}
+
+/* Apply the indices for the recset */
+int mdb_index_recrun(
+ Operation *op,
+ MDB_txn *txn,
+ struct mdb_info *mdb,
+ IndexRec *ir0,
+ ID id,
+ int base )
+{
+ IndexRec *ir;
+ AttrList *al;
+ int i, rc = 0;
+
+ /* Never index ID 0 */
+ if ( id == 0 )
+ return 0;
+
+ for (i=base; i<mdb->mi_nattrs; i+=slap_tool_thread_max-1) {
+ ir = ir0 + i;
+ if ( !ir->ir_ai ) continue;
+ while (( al = ir->ir_attrs )) {
+ ir->ir_attrs = al->next;
+ rc = indexer( op, txn, ir->ir_ai, ir->ir_ai->ai_desc,
+ &ir->ir_ai->ai_desc->ad_type->sat_cname,
+ al->attr->a_nvals, id, SLAP_INDEX_ADD_OP,
+ ir->ir_ai->ai_indexmask );
+ free( al );
+ if ( rc ) break;
+ }
+ }
+ return rc;
+}
+
+int
+mdb_index_entry(
+ Operation *op,
+ MDB_txn *txn,
+ int opid,
+ Entry *e )
+{
+ int rc;
+ Attribute *ap = e->e_attrs;
+#if 0 /* ifdef LDAP_COMP_MATCH */
+ ComponentReference *cr_list = NULL;
+ ComponentReference *cr = NULL, *dupped_cr = NULL;
+ void* decoded_comp;
+ ComponentSyntaxInfo* csi_attr;
+ Syntax* syn;
+ AttributeType* at;
+ int i, num_attr;
+ void* mem_op;
+ struct berval value = {0};
+#endif
+
+ /* Never index ID 0 */
+ if ( e->e_id == 0 )
+ return 0;
+
+ Debug( LDAP_DEBUG_TRACE, "=> index_entry_%s( %ld, \"%s\" )\n",
+ opid == SLAP_INDEX_DELETE_OP ? "del" : "add",
+ (long) e->e_id, e->e_dn ? e->e_dn : "" );
+
+ /* add each attribute to the indexes */
+ for ( ; ap != NULL; ap = ap->a_next ) {
+#if 0 /* ifdef LDAP_COMP_MATCH */
+ AttrInfo *ai;
+ /* see if attribute has components to be indexed */
+ ai = mdb_attr_mask( op->o_bd->be_private, ap->a_desc->ad_type->sat_ad );
+ if ( !ai ) continue;
+ cr_list = ai->ai_cr;
+ if ( attr_converter && cr_list ) {
+ syn = ap->a_desc->ad_type->sat_syntax;
+ ap->a_comp_data = op->o_tmpalloc( sizeof( ComponentData ), op->o_tmpmemctx );
+ /* Memory chunk(nibble) pre-allocation for decoders */
+ mem_op = nibble_mem_allocator ( 1024*16, 1024*4 );
+ ap->a_comp_data->cd_mem_op = mem_op;
+ for( cr = cr_list ; cr ; cr = cr->cr_next ) {
+ /* count how many values in an attribute */
+ for( num_attr=0; ap->a_vals[num_attr].bv_val != NULL; num_attr++ );
+ num_attr++;
+ cr->cr_nvals = (BerVarray)op->o_tmpalloc( sizeof( struct berval )*num_attr, op->o_tmpmemctx );
+ for( i=0; ap->a_vals[i].bv_val != NULL; i++ ) {
+ /* decoding attribute value */
+ decoded_comp = attr_converter ( ap, syn, &ap->a_vals[i] );
+ if ( !decoded_comp )
+ return LDAP_DECODING_ERROR;
+ /* extracting the referenced component */
+ dupped_cr = dup_comp_ref( op, cr );
+ csi_attr = ((ComponentSyntaxInfo*)decoded_comp)->csi_comp_desc->cd_extract_i( mem_op, dupped_cr, decoded_comp );
+ if ( !csi_attr )
+ return LDAP_DECODING_ERROR;
+ cr->cr_asn_type_id = csi_attr->csi_comp_desc->cd_type_id;
+ cr->cr_ad = (AttributeDescription*)get_component_description ( cr->cr_asn_type_id );
+ if ( !cr->cr_ad )
+ return LDAP_INVALID_SYNTAX;
+ at = cr->cr_ad->ad_type;
+ /* encoding the value of component in GSER */
+ rc = component_encoder( mem_op, csi_attr, &value );
+ if ( rc != LDAP_SUCCESS )
+ return LDAP_ENCODING_ERROR;
+ /* Normalize the encoded component values */
+ if ( at->sat_equality && at->sat_equality->smr_normalize ) {
+ rc = at->sat_equality->smr_normalize (
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ at->sat_syntax, at->sat_equality,
+ &value, &cr->cr_nvals[i], op->o_tmpmemctx );
+ } else {
+ cr->cr_nvals[i] = value;
+ }
+ }
+ /* The end of BerVarray */
+ cr->cr_nvals[num_attr-1].bv_val = NULL;
+ cr->cr_nvals[num_attr-1].bv_len = 0;
+ }
+ op->o_tmpfree( ap->a_comp_data, op->o_tmpmemctx );
+ nibble_mem_free ( mem_op );
+ ap->a_comp_data = NULL;
+ }
+#endif
+ rc = mdb_index_values( op, txn, ap->a_desc,
+ ap->a_nvals, e->e_id, opid );
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= index_entry_%s( %ld, \"%s\" ) failure\n",
+ opid == SLAP_INDEX_ADD_OP ? "add" : "del",
+ (long) e->e_id, e->e_dn );
+ return rc;
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<= index_entry_%s( %ld, \"%s\" ) success\n",
+ opid == SLAP_INDEX_DELETE_OP ? "del" : "add",
+ (long) e->e_id, e->e_dn ? e->e_dn : "" );
+
+ return LDAP_SUCCESS;
+}
diff --git a/servers/slapd/back-mdb/init.c b/servers/slapd/back-mdb/init.c
new file mode 100644
index 0000000..8aa9cbb
--- /dev/null
+++ b/servers/slapd/back-mdb/init.c
@@ -0,0 +1,508 @@
+/* init.c - initialize mdb backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+#include <ac/stdlib.h>
+#include <ac/errno.h>
+#include <sys/stat.h>
+#include "back-mdb.h"
+#include <lutil.h>
+#include <ldap_rq.h>
+#include "slap-config.h"
+
+static const struct berval mdmi_databases[] = {
+ BER_BVC("ad2i"),
+ BER_BVC("dn2i"),
+ BER_BVC("id2e"),
+ BER_BVC("id2v"),
+ BER_BVNULL
+};
+
+static int
+mdb_id_compare( const MDB_val *a, const MDB_val *b )
+{
+ return *(ID *)a->mv_data < *(ID *)b->mv_data ? -1 : *(ID *)a->mv_data > *(ID *)b->mv_data;
+}
+
+static int
+mdb_db_init( BackendDB *be, ConfigReply *cr )
+{
+ struct mdb_info *mdb;
+ int rc;
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_db_init) ": Initializing mdb database\n" );
+
+ /* allocate backend-database-specific stuff */
+ mdb = (struct mdb_info *) ch_calloc( 1, sizeof(struct mdb_info) );
+
+ /* DBEnv parameters */
+ mdb->mi_dbenv_home = ch_strdup( SLAPD_DEFAULT_DB_DIR );
+ mdb->mi_dbenv_flags = 0;
+ mdb->mi_dbenv_mode = SLAPD_DEFAULT_DB_MODE;
+
+ mdb->mi_search_stack_depth = DEFAULT_SEARCH_STACK_DEPTH;
+ mdb->mi_search_stack = NULL;
+
+ mdb->mi_mapsize = DEFAULT_MAPSIZE;
+ mdb->mi_rtxn_size = DEFAULT_RTXN_SIZE;
+ mdb->mi_multi_hi = UINT_MAX;
+ mdb->mi_multi_lo = UINT_MAX;
+
+ be->be_private = mdb;
+ be->be_cf_ocs = be->bd_info->bi_cf_ocs+1;
+
+#ifndef MDB_MULTIPLE_SUFFIXES
+ SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_ONE_SUFFIX;
+#endif
+
+ rc = mdb_monitor_db_init( be );
+
+ return rc;
+}
+
+static int
+mdb_db_close( BackendDB *be, ConfigReply *cr );
+
+static int
+mdb_db_open( BackendDB *be, ConfigReply *cr )
+{
+ int rc, i;
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+ struct stat stat1;
+ unsigned flags;
+ char *dbhome;
+ MDB_txn *txn;
+
+ if ( be->be_suffix == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_db_open) ": need suffix.\n" );
+ return -1;
+ }
+
+ Debug( LDAP_DEBUG_ARGS,
+ LDAP_XSTRING(mdb_db_open) ": \"%s\"\n",
+ be->be_suffix[0].bv_val );
+
+ /* Check existence of dbenv_home. Any error means trouble */
+ rc = stat( mdb->mi_dbenv_home, &stat1 );
+ if( rc != 0 ) {
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_db_open) ": database \"%s\": "
+ "cannot access database directory \"%s\" (%d).\n",
+ be->be_suffix[0].bv_val, mdb->mi_dbenv_home, saved_errno );
+ return -1;
+ }
+
+ /* mdb is always clean */
+ be->be_flags |= SLAP_DBFLAG_CLEAN;
+
+ rc = mdb_env_create( &mdb->mi_dbenv );
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_db_open) ": database \"%s\": "
+ "mdb_env_create failed: %s (%d).\n",
+ be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
+ goto fail;
+ }
+
+ if ( mdb->mi_readers ) {
+ rc = mdb_env_set_maxreaders( mdb->mi_dbenv, mdb->mi_readers );
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_db_open) ": database \"%s\": "
+ "mdb_env_set_maxreaders failed: %s (%d).\n",
+ be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
+ goto fail;
+ }
+ }
+
+ rc = mdb_env_set_mapsize( mdb->mi_dbenv, mdb->mi_mapsize );
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_db_open) ": database \"%s\": "
+ "mdb_env_set_mapsize failed: %s (%d).\n",
+ be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
+ goto fail;
+ }
+
+ rc = mdb_env_set_maxdbs( mdb->mi_dbenv, MDB_INDICES );
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_db_open) ": database \"%s\": "
+ "mdb_env_set_maxdbs failed: %s (%d).\n",
+ be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
+ goto fail;
+ }
+
+#ifdef HAVE_EBCDIC
+ strcpy( path, mdb->mi_dbenv_home );
+ __atoe( path );
+ dbhome = path;
+#else
+ dbhome = mdb->mi_dbenv_home;
+#endif
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_db_open) ": database \"%s\": "
+ "dbenv_open(%s).\n",
+ be->be_suffix[0].bv_val, mdb->mi_dbenv_home );
+
+ flags = mdb->mi_dbenv_flags;
+
+ if ( slapMode & SLAP_TOOL_QUICK )
+ flags |= MDB_NOSYNC|MDB_WRITEMAP;
+
+ if ( slapMode & SLAP_TOOL_READONLY)
+ flags |= MDB_RDONLY;
+
+ rc = mdb_env_open( mdb->mi_dbenv, dbhome,
+ flags, mdb->mi_dbenv_mode );
+
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_db_open) ": database \"%s\" cannot be opened: %s (%d). "
+ "Restore from backup!\n",
+ be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
+ goto fail;
+ }
+
+ rc = mdb_txn_begin( mdb->mi_dbenv, NULL, flags & MDB_RDONLY, &txn );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_db_open) ": database \"%s\" cannot be opened: %s (%d). "
+ "Restore from backup!\n",
+ be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
+ goto fail;
+ }
+
+ /* open (and create) main databases */
+ for( i = 0; mdmi_databases[i].bv_val; i++ ) {
+ flags = MDB_INTEGERKEY;
+ if( i == MDB_ID2ENTRY ) {
+ if ( !(slapMode & (SLAP_TOOL_READMAIN|SLAP_TOOL_READONLY) ))
+ flags |= MDB_CREATE;
+ } else {
+ if ( i == MDB_DN2ID )
+ flags |= MDB_DUPSORT;
+ if ( i == MDB_ID2VAL )
+ flags ^= MDB_INTEGERKEY|MDB_DUPSORT;
+ if ( !(slapMode & SLAP_TOOL_READONLY) )
+ flags |= MDB_CREATE;
+ }
+
+ rc = mdb_dbi_open( txn,
+ mdmi_databases[i].bv_val,
+ flags,
+ &mdb->mi_dbis[i] );
+
+ if ( rc != 0 ) {
+ /* when read-only, it's ok for ID2VAL or IDXCKP to not exist */
+ if (( flags & MDB_CREATE ) || ( i < MDB_ID2VAL )) {
+ snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
+ "mdb_dbi_open(%s/%s) failed: %s (%d).",
+ be->be_suffix[0].bv_val,
+ mdb->mi_dbenv_home, mdmi_databases[i].bv_val,
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_db_open) ": %s\n",
+ cr->msg );
+ goto fail;
+ }
+ }
+
+ if ( i == MDB_ID2ENTRY )
+ mdb_set_compare( txn, mdb->mi_dbis[i], mdb_id_compare );
+ else if ( i == MDB_ID2VAL ) {
+ mdb_set_compare( txn, mdb->mi_dbis[i], mdb_id2v_compare );
+ mdb_set_dupsort( txn, mdb->mi_dbis[i], mdb_id2v_dupsort );
+ } else if ( i == MDB_DN2ID ) {
+ MDB_cursor *mc;
+ MDB_val key, data;
+ mdb_set_dupsort( txn, mdb->mi_dbis[i], mdb_dup_compare );
+ /* check for old dn2id format */
+ rc = mdb_cursor_open( txn, mdb->mi_dbis[i], &mc );
+ /* first record is always ID 0 */
+ rc = mdb_cursor_get( mc, &key, &data, MDB_FIRST );
+ if ( rc == 0 ) {
+ rc = mdb_cursor_get( mc, &key, &data, MDB_NEXT );
+ if ( rc == 0 ) {
+ int len;
+ unsigned char *ptr;
+ ptr = data.mv_data;
+ len = (ptr[0] & 0x7f) << 8 | ptr[1];
+ if (data.mv_size < 2*len + 4 + 2*sizeof(ID)) {
+ snprintf( cr->msg, sizeof(cr->msg),
+ "database \"%s\": DN index needs upgrade, "
+ "run \"slapindex entryDN\".",
+ be->be_suffix[0].bv_val );
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_db_open) ": %s\n",
+ cr->msg );
+ if ( !(slapMode & SLAP_TOOL_READMAIN ))
+ rc = LDAP_OTHER;
+ mdb->mi_flags |= MDB_NEED_UPGRADE;
+ }
+ }
+ }
+ mdb_cursor_close( mc );
+ if ( rc == LDAP_OTHER )
+ goto fail;
+ }
+ }
+
+ rc = mdb_ad_read( mdb, txn );
+ if ( rc ) {
+ mdb_txn_abort( txn );
+ goto fail;
+ }
+
+ /* slapcat doesn't need indexes. avoid a failure if
+ * a configured index wasn't created yet.
+ */
+ if ( !(slapMode & SLAP_TOOL_READONLY) ) {
+ rc = mdb_attr_dbs_open( be, txn, cr );
+ if ( rc ) {
+ mdb_txn_abort( txn );
+ goto fail;
+ }
+ }
+
+ rc = mdb_txn_commit(txn);
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_db_open) ": database %s: "
+ "txn_commit failed: %s (%d)\n",
+ be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
+ goto fail;
+ }
+
+ /* monitor setup */
+ rc = mdb_monitor_db_open( be );
+ if ( rc != 0 ) {
+ goto fail;
+ }
+
+ mdb->mi_flags |= MDB_IS_OPEN;
+
+ return 0;
+
+fail:
+ mdb_db_close( be, NULL );
+ return rc;
+}
+
+static int
+mdb_db_close( BackendDB *be, ConfigReply *cr )
+{
+ int rc;
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+
+ /* monitor handling */
+ (void)mdb_monitor_db_close( be );
+
+ mdb->mi_flags &= ~MDB_IS_OPEN;
+
+ if( mdb->mi_dbenv ) {
+ mdb_reader_flush( mdb->mi_dbenv );
+ }
+
+ if ( mdb->mi_dbenv ) {
+ if ( mdb->mi_dbis[0] ) {
+ int i;
+
+ mdb_attr_dbs_close( mdb );
+ for ( i=0; i<MDB_NDB; i++ )
+ mdb_dbi_close( mdb->mi_dbenv, mdb->mi_dbis[i] );
+
+ /* force a sync, but not if we were ReadOnly,
+ * and not in Quick mode.
+ */
+ if (!(slapMode & (SLAP_TOOL_QUICK|SLAP_TOOL_READONLY))) {
+ rc = mdb_env_sync( mdb->mi_dbenv, 1 );
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "mdb_db_close: database \"%s\": "
+ "mdb_env_sync failed: %s (%d).\n",
+ be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
+ }
+ }
+ }
+
+ mdb_env_close( mdb->mi_dbenv );
+ mdb->mi_dbenv = NULL;
+ }
+
+ return 0;
+}
+
+static int
+mdb_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+
+ /* stop and remove checkpoint task */
+ if ( mdb->mi_txn_cp_task ) {
+ struct re_s *re = mdb->mi_txn_cp_task;
+ mdb->mi_txn_cp_task = NULL;
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, re ) )
+ ldap_pvt_runqueue_stoptask( &slapd_rq, re );
+ ldap_pvt_runqueue_remove( &slapd_rq, re );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+
+ /* monitor handling */
+ (void)mdb_monitor_db_destroy( be );
+
+ if( mdb->mi_dbenv_home ) ch_free( mdb->mi_dbenv_home );
+
+ mdb_attr_index_destroy( mdb );
+
+ ch_free( mdb );
+ be->be_private = NULL;
+
+ return 0;
+}
+
+int
+mdb_back_initialize(
+ BackendInfo *bi )
+{
+ int rc;
+
+ static char *controls[] = {
+ LDAP_CONTROL_ASSERT,
+ LDAP_CONTROL_MANAGEDSAIT,
+ LDAP_CONTROL_NOOP,
+ LDAP_CONTROL_PAGEDRESULTS,
+ LDAP_CONTROL_PRE_READ,
+ LDAP_CONTROL_POST_READ,
+ LDAP_CONTROL_SUBENTRIES,
+ LDAP_CONTROL_X_PERMISSIVE_MODIFY,
+ LDAP_CONTROL_TXN_SPEC,
+ NULL
+ };
+
+ /* initialize the underlying database system */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_back_initialize) ": initialize "
+ MDB_UCTYPE " backend\n" );
+
+ bi->bi_flags |=
+ SLAP_BFLAG_INCREMENT |
+ SLAP_BFLAG_SUBENTRIES |
+ SLAP_BFLAG_ALIASES |
+ SLAP_BFLAG_REFERRALS |
+ SLAP_BFLAG_TXNS;
+
+ bi->bi_controls = controls;
+
+ { /* version check */
+ int major, minor, patch, ver;
+ char *version = mdb_version( &major, &minor, &patch );
+#ifdef HAVE_EBCDIC
+ char v2[1024];
+
+ /* All our stdio does an ASCII to EBCDIC conversion on
+ * the output. Strings from the MDB library are already
+ * in EBCDIC; we have to go back and forth...
+ */
+ strcpy( v2, version );
+ __etoa( v2 );
+ version = v2;
+#endif
+ ver = (major << 24) | (minor << 16) | patch;
+ if( ver != MDB_VERSION_FULL ) {
+ /* fail if a versions don't match */
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_back_initialize) ": "
+ "MDB library version mismatch:"
+ " expected " MDB_VERSION_STRING ","
+ " got %s\n", version );
+ return -1;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(mdb_back_initialize)
+ ": %s\n", version );
+ }
+
+ bi->bi_open = 0;
+ bi->bi_close = 0;
+ bi->bi_config = 0;
+ bi->bi_destroy = 0;
+
+ bi->bi_db_init = mdb_db_init;
+ bi->bi_db_config = config_generic_wrapper;
+ bi->bi_db_open = mdb_db_open;
+ bi->bi_db_close = mdb_db_close;
+ bi->bi_db_destroy = mdb_db_destroy;
+
+ bi->bi_op_add = mdb_add;
+ bi->bi_op_bind = mdb_bind;
+ bi->bi_op_compare = mdb_compare;
+ bi->bi_op_delete = mdb_delete;
+ bi->bi_op_modify = mdb_modify;
+ bi->bi_op_modrdn = mdb_modrdn;
+ bi->bi_op_search = mdb_search;
+
+ bi->bi_op_unbind = 0;
+ bi->bi_op_txn = mdb_txn;
+
+ bi->bi_extended = mdb_extended;
+
+ bi->bi_chk_referrals = 0;
+ bi->bi_operational = mdb_operational;
+
+ bi->bi_has_subordinates = mdb_hasSubordinates;
+ bi->bi_entry_release_rw = mdb_entry_release;
+ bi->bi_entry_get_rw = mdb_entry_get;
+
+ /*
+ * hooks for slap tools
+ */
+ bi->bi_tool_entry_open = mdb_tool_entry_open;
+ bi->bi_tool_entry_close = mdb_tool_entry_close;
+ bi->bi_tool_entry_first = backend_tool_entry_first;
+ bi->bi_tool_entry_first_x = mdb_tool_entry_first_x;
+ bi->bi_tool_entry_next = mdb_tool_entry_next;
+ bi->bi_tool_entry_get = mdb_tool_entry_get;
+ bi->bi_tool_entry_put = mdb_tool_entry_put;
+ bi->bi_tool_entry_reindex = mdb_tool_entry_reindex;
+ bi->bi_tool_sync = 0;
+ bi->bi_tool_dn2id_get = mdb_tool_dn2id_get;
+ bi->bi_tool_entry_modify = mdb_tool_entry_modify;
+ bi->bi_tool_entry_delete = mdb_tool_entry_delete;
+
+ bi->bi_connection_init = 0;
+ bi->bi_connection_destroy = 0;
+
+ rc = mdb_back_init_cf( bi );
+
+ return rc;
+}
+
+#if (SLAPD_MDB == SLAPD_MOD_DYNAMIC)
+
+SLAP_BACKEND_INIT_MODULE( mdb )
+
+#endif /* SLAPD_MDB == SLAPD_MOD_DYNAMIC */
+
diff --git a/servers/slapd/back-mdb/key.c b/servers/slapd/back-mdb/key.c
new file mode 100644
index 0000000..b0b453b
--- /dev/null
+++ b/servers/slapd/back-mdb/key.c
@@ -0,0 +1,72 @@
+/* index.c - routines for dealing with attribute indexes */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-mdb.h"
+#include "idl.h"
+
+/* read a key */
+int
+mdb_key_read(
+ Backend *be,
+ MDB_txn *txn,
+ MDB_dbi dbi,
+ struct berval *k,
+ ID *ids,
+ MDB_cursor **saved_cursor,
+ int get_flag
+)
+{
+ int rc;
+ MDB_val key;
+#ifndef MISALIGNED_OK
+ int kbuf[2];
+#endif
+
+ Debug( LDAP_DEBUG_TRACE, "=> key_read\n" );
+
+#ifndef MISALIGNED_OK
+ if (k->bv_len & ALIGNER) {
+ key.mv_size = sizeof(kbuf);
+ key.mv_data = kbuf;
+ kbuf[1] = 0;
+ memcpy(kbuf, k->bv_val, k->bv_len);
+ } else
+#endif
+ {
+ key.mv_size = k->bv_len;
+ key.mv_data = k->bv_val;
+ }
+
+ rc = mdb_idl_fetch_key( be, txn, dbi, &key, ids, saved_cursor, get_flag );
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "<= mdb_index_read: failed (%d)\n",
+ rc );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "<= mdb_index_read %ld candidates\n",
+ (long) MDB_IDL_N(ids) );
+ }
+
+ return rc;
+}
diff --git a/servers/slapd/back-mdb/modify.c b/servers/slapd/back-mdb/modify.c
new file mode 100644
index 0000000..f2233e8
--- /dev/null
+++ b/servers/slapd/back-mdb/modify.c
@@ -0,0 +1,843 @@
+/* modify.c - mdb backend modify routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "back-mdb.h"
+
+static struct berval scbva[] = {
+ BER_BVC("glue"),
+ BER_BVNULL
+};
+
+#define CHECK_ADD 1
+#define CHECK_DEL 2
+
+static void
+mdb_modify_idxflags(
+ Operation *op,
+ AttributeDescription *desc,
+ int ixcheck,
+ Attribute *newattrs,
+ Attribute *oldattrs )
+{
+ struct berval ix_at;
+ AttrInfo *ai;
+
+ /* check if modified attribute was indexed
+ * but not in case of NOOP... */
+ ai = mdb_index_mask( op->o_bd, desc, &ix_at );
+ if ( ai ) {
+ if ( ixcheck & CHECK_DEL ) {
+ Attribute *ap;
+ struct berval ix2;
+
+ ap = attr_find( oldattrs, desc );
+ if ( ap ) ap->a_flags |= SLAP_ATTR_IXDEL;
+
+ /* ITS#8678 FIXME
+ * If using 32bit hashes, or substring index, must account for
+ * possible index collisions. If no substring index, and using
+ * 64bit hashes, assume we don't need to check for collisions.
+ *
+ * In 2.5 use refcounts and avoid all of this mess.
+ */
+ if (!slap_hash64(-1) || (ai->ai_indexmask & SLAP_INDEX_SUBSTR)) {
+ /* Find all other attrs that index to same slot */
+ for ( ap = newattrs; ap; ap = ap->a_next ) {
+ ai = mdb_index_mask( op->o_bd, ap->a_desc, &ix2 );
+ if ( ai && ix2.bv_val == ix_at.bv_val )
+ ap->a_flags |= SLAP_ATTR_IXADD;
+ }
+ }
+
+ }
+ if ( ixcheck & CHECK_ADD ) {
+ Attribute *ap;
+
+ ap = attr_find( newattrs, desc );
+ if ( ap ) ap->a_flags |= SLAP_ATTR_IXADD;
+ }
+ }
+}
+
+int mdb_modify_internal(
+ Operation *op,
+ MDB_txn *tid,
+ Modifications *modlist,
+ Entry *e,
+ const char **text,
+ char *textbuf,
+ size_t textlen )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ int rc, err;
+ Modification *mod;
+ Modifications *ml;
+ Attribute *save_attrs;
+ Attribute *ap, *aold, *anew;
+ int glue_attr_delete = 0;
+ int softop, chkpresent;
+ int ixcheck;
+ int a_flags;
+ MDB_cursor *mvc = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "mdb_modify_internal: 0x%08lx: %s\n",
+ e->e_id, e->e_dn );
+
+ if ( !acl_check_modlist( op, e, modlist )) {
+ return LDAP_INSUFFICIENT_ACCESS;
+ }
+
+ /* save_attrs will be disposed of by caller */
+ save_attrs = e->e_attrs;
+ e->e_attrs = attrs_dup( e->e_attrs );
+
+ for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
+ int match;
+ mod = &ml->sml_mod;
+ switch( mod->sm_op ) {
+ case LDAP_MOD_ADD:
+ case LDAP_MOD_REPLACE:
+ if ( mod->sm_desc == slap_schema.si_ad_structuralObjectClass ) {
+ value_match( &match, slap_schema.si_ad_structuralObjectClass,
+ slap_schema.si_ad_structuralObjectClass->
+ ad_type->sat_equality,
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ &mod->sm_values[0], &scbva[0], text );
+ if ( !match ) glue_attr_delete = 1;
+ }
+ }
+ if ( glue_attr_delete )
+ break;
+ }
+
+ if ( glue_attr_delete ) {
+ Attribute **app = &e->e_attrs;
+ while ( *app != NULL ) {
+ if ( !is_at_operational( (*app)->a_desc->ad_type )) {
+ Attribute *save = *app;
+ *app = (*app)->a_next;
+ attr_free( save );
+ continue;
+ }
+ app = &(*app)->a_next;
+ }
+ }
+
+ for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
+ mod = &ml->sml_mod;
+ ixcheck = 0;
+
+ aold = attr_find( e->e_attrs, mod->sm_desc );
+ if (aold)
+ a_flags = aold->a_flags;
+ else
+ a_flags = 0;
+
+ switch ( mod->sm_op ) {
+ case LDAP_MOD_ADD:
+ softop = 0;
+ chkpresent = 0;
+ Debug(LDAP_DEBUG_ARGS,
+ "mdb_modify_internal: add %s\n",
+ mod->sm_desc->ad_cname.bv_val );
+
+do_add:
+ err = modify_add_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+
+ if( softop ) {
+ mod->sm_op = SLAP_MOD_SOFTADD;
+ if ( err == LDAP_TYPE_OR_VALUE_EXISTS )
+ err = LDAP_SUCCESS;
+ }
+ if( chkpresent ) {
+ mod->sm_op = SLAP_MOD_ADD_IF_NOT_PRESENT;
+ }
+
+ if( err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "mdb_modify_internal: %d %s\n",
+ err, *text );
+ } else {
+ unsigned hi;
+ if (!aold)
+ anew = attr_find( e->e_attrs, mod->sm_desc );
+ else
+ anew = aold;
+ mdb_attr_multi_thresh( mdb, mod->sm_desc, &hi, NULL );
+ /* check for big multivalued attrs */
+ if ( anew->a_numvals > hi )
+ anew->a_flags |= SLAP_ATTR_BIG_MULTI;
+ if ( anew->a_flags & SLAP_ATTR_BIG_MULTI ) {
+ if (!mvc) {
+ err = mdb_cursor_open( tid, mdb->mi_dbis[MDB_ID2VAL], &mvc );
+ if (err) {
+mval_fail: strncpy( textbuf, mdb_strerror( err ), textlen );
+ err = LDAP_OTHER;
+ break;
+ }
+ }
+ /* if prev was set, just add new values */
+ if (a_flags & SLAP_ATTR_BIG_MULTI ) {
+ anew = (Attribute *)mod;
+ /* Tweak nvals */
+ if (!anew->a_nvals)
+ anew->a_nvals = anew->a_vals;
+ }
+ err = mdb_mval_put(op, mvc, e->e_id, anew);
+ if (a_flags & SLAP_ATTR_BIG_MULTI ) {
+ /* Undo nvals tweak */
+ if (anew->a_nvals == anew->a_vals)
+ anew->a_nvals = NULL;
+ }
+ if ( err )
+ goto mval_fail;
+ }
+ ixcheck |= CHECK_ADD;
+ }
+ break;
+
+ case LDAP_MOD_DELETE:
+ if ( glue_attr_delete ) {
+ err = LDAP_SUCCESS;
+ break;
+ }
+
+ softop = 0;
+ Debug(LDAP_DEBUG_ARGS,
+ "mdb_modify_internal: delete %s\n",
+ mod->sm_desc->ad_cname.bv_val );
+do_del:
+ err = modify_delete_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+
+ if (softop) {
+ mod->sm_op = SLAP_MOD_SOFTDEL;
+ if ( err == LDAP_NO_SUCH_ATTRIBUTE ) {
+ err = LDAP_SUCCESS;
+ softop = 2;
+ }
+ }
+
+ if( err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "mdb_modify_internal: %d %s\n",
+ err, *text );
+ } else {
+ if (softop != 2)
+ ixcheck |= CHECK_DEL;
+ /* check for big multivalued attrs */
+ if (a_flags & SLAP_ATTR_BIG_MULTI) {
+ Attribute a_dummy;
+ if (!mvc) {
+ err = mdb_cursor_open( tid, mdb->mi_dbis[MDB_ID2VAL], &mvc );
+ if (err)
+ goto mval_fail;
+ }
+ if ( mod->sm_numvals ) {
+ anew = attr_find( e->e_attrs, mod->sm_desc );
+ if ( anew ) {
+ unsigned lo;
+ mdb_attr_multi_thresh( mdb, mod->sm_desc, NULL, &lo );
+ if ( anew->a_numvals < lo ) {
+ anew->a_flags ^= SLAP_ATTR_BIG_MULTI;
+ anew = NULL;
+ } else {
+ anew = (Attribute *)mod;
+ }
+ }
+ } else {
+ anew = NULL;
+ }
+ if (!anew) {
+ /* delete all values */
+ anew = &a_dummy;
+ anew->a_desc = mod->sm_desc;
+ anew->a_numvals = 0;
+ }
+ err = mdb_mval_del( op, mvc, e->e_id, anew );
+ if ( err )
+ goto mval_fail;
+ }
+ }
+ break;
+
+ case LDAP_MOD_REPLACE:
+ Debug(LDAP_DEBUG_ARGS,
+ "mdb_modify_internal: replace %s\n",
+ mod->sm_desc->ad_cname.bv_val );
+ err = modify_replace_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+ if( err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "mdb_modify_internal: %d %s\n",
+ err, *text );
+ } else {
+ unsigned hi;
+ ixcheck = CHECK_DEL;
+ if ( mod->sm_numvals )
+ ixcheck |= CHECK_ADD;
+ if (a_flags & SLAP_ATTR_BIG_MULTI) {
+ Attribute a_dummy;
+ if (!mvc) {
+ err = mdb_cursor_open( tid, mdb->mi_dbis[MDB_ID2VAL], &mvc );
+ if (err)
+ goto mval_fail;
+ }
+ /* delete all values */
+ anew = &a_dummy;
+ anew->a_desc = mod->sm_desc;
+ anew->a_numvals = 0;
+ err = mdb_mval_del( op, mvc, e->e_id, anew );
+ if (err)
+ goto mval_fail;
+ }
+ anew = attr_find( e->e_attrs, mod->sm_desc );
+ mdb_attr_multi_thresh( mdb, mod->sm_desc, &hi, NULL );
+ if (mod->sm_numvals > hi) {
+ anew->a_flags |= SLAP_ATTR_BIG_MULTI;
+ if (!mvc) {
+ err = mdb_cursor_open( tid, mdb->mi_dbis[MDB_ID2VAL], &mvc );
+ if (err)
+ goto mval_fail;
+ }
+ err = mdb_mval_put(op, mvc, e->e_id, anew);
+ if (err)
+ goto mval_fail;
+ } else if (anew) {
+ /* revert back to normal attr */
+ anew->a_flags &= ~SLAP_ATTR_BIG_MULTI;
+ }
+ }
+ break;
+
+ case LDAP_MOD_INCREMENT:
+ Debug(LDAP_DEBUG_ARGS,
+ "mdb_modify_internal: increment %s\n",
+ mod->sm_desc->ad_cname.bv_val );
+ err = modify_increment_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+ if( err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS,
+ "mdb_modify_internal: %d %s\n",
+ err, *text );
+ } else {
+ ixcheck = CHECK_ADD|CHECK_DEL;
+ }
+ break;
+
+ case SLAP_MOD_SOFTADD:
+ Debug(LDAP_DEBUG_ARGS,
+ "mdb_modify_internal: softadd %s\n",
+ mod->sm_desc->ad_cname.bv_val );
+ /* Avoid problems in index_add_mods()
+ * We need to add index if necessary.
+ */
+ mod->sm_op = LDAP_MOD_ADD;
+ softop = 1;
+ chkpresent = 0;
+ goto do_add;
+
+ case SLAP_MOD_SOFTDEL:
+ Debug(LDAP_DEBUG_ARGS,
+ "mdb_modify_internal: softdel %s\n",
+ mod->sm_desc->ad_cname.bv_val );
+ /* Avoid problems in index_delete_mods()
+ * We need to add index if necessary.
+ */
+ mod->sm_op = LDAP_MOD_DELETE;
+ softop = 1;
+ goto do_del;
+
+ case SLAP_MOD_ADD_IF_NOT_PRESENT:
+ if ( attr_find( e->e_attrs, mod->sm_desc ) != NULL ) {
+ /* skip */
+ err = LDAP_SUCCESS;
+ break;
+ }
+
+ Debug(LDAP_DEBUG_ARGS,
+ "mdb_modify_internal: add_if_not_present %s\n",
+ mod->sm_desc->ad_cname.bv_val );
+ /* Avoid problems in index_add_mods()
+ * We need to add index if necessary.
+ */
+ mod->sm_op = LDAP_MOD_ADD;
+ softop = 0;
+ chkpresent = 1;
+ goto do_add;
+
+ default:
+ Debug(LDAP_DEBUG_ANY, "mdb_modify_internal: invalid op %d\n",
+ mod->sm_op );
+ *text = "Invalid modify operation";
+ err = LDAP_OTHER;
+ Debug(LDAP_DEBUG_ARGS, "mdb_modify_internal: %d %s\n",
+ err, *text );
+ }
+
+ if ( err != LDAP_SUCCESS ) {
+ attrs_free( e->e_attrs );
+ e->e_attrs = save_attrs;
+ /* unlock entry, delete from cache */
+ return err;
+ }
+
+ /* If objectClass was modified, reset the flags */
+ if ( mod->sm_desc == slap_schema.si_ad_objectClass ) {
+ e->e_ocflags = 0;
+ }
+
+ if ( glue_attr_delete ) e->e_ocflags = 0;
+
+
+ /* check if modified attribute was indexed
+ * but not in case of NOOP... */
+ if ( !op->o_noop ) {
+ mdb_modify_idxflags( op, mod->sm_desc, ixcheck, e->e_attrs, save_attrs );
+ }
+ }
+
+ /* check that the entry still obeys the schema */
+ ap = NULL;
+ rc = entry_schema_check( op, e, save_attrs, get_relax(op), 0, &ap,
+ text, textbuf, textlen );
+ if ( rc != LDAP_SUCCESS || op->o_noop ) {
+ attrs_free( e->e_attrs );
+ /* clear the indexing flags */
+ for ( ap = save_attrs; ap != NULL; ap = ap->a_next ) {
+ ap->a_flags &= ~(SLAP_ATTR_IXADD|SLAP_ATTR_IXDEL);
+ }
+ e->e_attrs = save_attrs;
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "entry failed schema check: %s\n",
+ *text );
+ }
+
+ /* if NOOP then silently revert to saved attrs */
+ return rc;
+ }
+
+ /* structuralObjectClass modified! */
+ if ( ap ) {
+ assert( ap->a_desc == slap_schema.si_ad_structuralObjectClass );
+ if ( !op->o_noop ) {
+ mdb_modify_idxflags( op, slap_schema.si_ad_structuralObjectClass,
+ CHECK_ADD|CHECK_DEL, e->e_attrs, save_attrs );
+ }
+ }
+
+ /* update the indices of the modified attributes */
+
+ /* start with deleting the old index entries */
+ for ( ap = save_attrs; ap != NULL; ap = ap->a_next ) {
+ if ( ap->a_flags & SLAP_ATTR_IXDEL ) {
+ struct berval *vals;
+ Attribute *a2;
+ ap->a_flags &= ~SLAP_ATTR_IXDEL;
+ a2 = attr_find( e->e_attrs, ap->a_desc );
+ if ( a2 ) {
+ /* need to detect which values were deleted */
+ int i, j, k;
+ /* let add know there were deletes */
+ if ( a2->a_flags & SLAP_ATTR_IXADD )
+ a2->a_flags |= SLAP_ATTR_IXDEL;
+ vals = op->o_tmpalloc( (ap->a_numvals + 1) *
+ sizeof(struct berval), op->o_tmpmemctx );
+ j = 0;
+ for ( i=k=0; i < ap->a_numvals; i++ ) {
+ char found = 0;
+ BerValue* current = &ap->a_nvals[i];
+ int k2 = k;
+ for (k2 = k ; k2 < a2->a_numvals; k2 ++) {
+ int match = -1, rc;
+ const char *text;
+
+ rc = ordered_value_match( &match, a2->a_desc,
+ ap->a_desc->ad_type->sat_equality, 0,
+ &a2->a_nvals[k2], current, &text );
+ if ( rc == LDAP_SUCCESS && match == 0 ) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ vals[j++] = *current;
+ } else {
+ k = k2 + 1;
+ }
+ }
+ BER_BVZERO(vals+j);
+ } else {
+ /* attribute was completely deleted */
+ vals = ap->a_nvals;
+ }
+ rc = 0;
+ if ( !BER_BVISNULL( vals )) {
+ rc = mdb_index_values( op, tid, ap->a_desc,
+ vals, e->e_id, SLAP_INDEX_DELETE_OP );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: attribute \"%s\" index delete failure\n",
+ op->o_log_prefix, ap->a_desc->ad_cname.bv_val );
+ attrs_free( e->e_attrs );
+ e->e_attrs = save_attrs;
+ }
+ }
+ if ( vals != ap->a_nvals )
+ op->o_tmpfree( vals, op->o_tmpmemctx );
+ if ( rc ) return rc;
+ }
+ }
+
+ /* add the new index entries */
+ for ( ap = e->e_attrs; ap != NULL; ap = ap->a_next ) {
+ if (ap->a_flags & SLAP_ATTR_IXADD) {
+ ap->a_flags &= ~SLAP_ATTR_IXADD;
+ if ( ap->a_flags & SLAP_ATTR_IXDEL ) {
+ /* if any values were deleted, we must readd index
+ * for all remaining values.
+ */
+ ap->a_flags &= ~SLAP_ATTR_IXDEL;
+ rc = mdb_index_values( op, tid, ap->a_desc,
+ ap->a_nvals,
+ e->e_id, SLAP_INDEX_ADD_OP );
+ } else {
+ int found = 0;
+ /* if this was only an add, we only need to index
+ * the added values.
+ */
+ for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
+ struct berval *vals;
+ if ( ml->sml_desc != ap->a_desc || !ml->sml_numvals )
+ continue;
+ found = 1;
+ switch( ml->sml_op ) {
+ case LDAP_MOD_ADD:
+ case LDAP_MOD_REPLACE:
+ case LDAP_MOD_INCREMENT:
+ case SLAP_MOD_SOFTADD:
+ case SLAP_MOD_ADD_IF_NOT_PRESENT:
+ if ( ml->sml_op == LDAP_MOD_INCREMENT )
+ vals = ap->a_nvals;
+ else if ( ml->sml_nvalues )
+ vals = ml->sml_nvalues;
+ else
+ vals = ml->sml_values;
+ rc = mdb_index_values( op, tid, ap->a_desc,
+ vals, e->e_id, SLAP_INDEX_ADD_OP );
+ break;
+ }
+ if ( rc )
+ break;
+ }
+ /* This attr was affected by a modify of a subtype, so
+ * there was no direct match in the modlist. Just readd
+ * all of its values.
+ */
+ if ( !found ) {
+ rc = mdb_index_values( op, tid, ap->a_desc,
+ ap->a_nvals,
+ e->e_id, SLAP_INDEX_ADD_OP );
+ }
+ }
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: attribute \"%s\" index add failure\n",
+ op->o_log_prefix, ap->a_desc->ad_cname.bv_val );
+ attrs_free( e->e_attrs );
+ e->e_attrs = save_attrs;
+ return rc;
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+int
+mdb_modify( Operation *op, SlapReply *rs )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ Entry *e = NULL;
+ int manageDSAit = get_manageDSAit( op );
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof textbuf;
+ MDB_txn *txn = NULL;
+ mdb_op_info opinfo = {{{ 0 }}}, *moi = &opinfo;
+ Entry dummy = {0};
+
+ LDAPControl **preread_ctrl = NULL;
+ LDAPControl **postread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+ int numads = mdb->mi_numads;
+
+ Debug( LDAP_DEBUG_ARGS, LDAP_XSTRING(mdb_modify) ": %s\n",
+ op->o_req_dn.bv_val );
+
+ ctrls[num_ctrls] = NULL;
+
+ /* begin transaction */
+ rs->sr_err = mdb_opinfo_get( op, mdb, 0, &moi );
+ rs->sr_text = NULL;
+ if( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modify) ": txn_begin failed: "
+ "%s (%d)\n", mdb_strerror(rs->sr_err), rs->sr_err );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ txn = moi->moi_txn;
+
+ /* Don't touch the opattrs, if this is a contextCSN update
+ * initiated from updatedn */
+ if ( !be_isupdate(op) || !op->orm_modlist || op->orm_modlist->sml_next ||
+ op->orm_modlist->sml_desc != slap_schema.si_ad_contextCSN ) {
+
+ slap_mods_opattrs( op, &op->orm_modlist, 1 );
+ }
+
+ /* get entry or ancestor */
+ rs->sr_err = mdb_dn2entry( op, txn, NULL, &op->o_req_ndn, &e, NULL, 1 );
+
+ if ( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modify) ": dn2entry failed (%d)\n",
+ rs->sr_err );
+ switch( rs->sr_err ) {
+ case MDB_NOTFOUND:
+ break;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ }
+
+ /* acquire and lock entry */
+ /* FIXME: dn2entry() should return non-glue entry */
+ if (( rs->sr_err == MDB_NOTFOUND ) ||
+ ( !manageDSAit && e && is_entry_glue( e )))
+ {
+ if ( e != NULL ) {
+ rs->sr_matched = ch_strdup( e->e_dn );
+ if ( is_entry_referral( e )) {
+ BerVarray ref = get_entry_referrals( op, e );
+ rs->sr_ref = referral_rewrite( ref, &e->e_name,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ } else {
+ rs->sr_ref = NULL;
+ }
+ mdb_entry_return( op, e );
+ e = NULL;
+
+ } else {
+ rs->sr_ref = referral_rewrite( default_referral, NULL,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ }
+
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ rs->sr_err = LDAP_REFERRAL;
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ if ( !manageDSAit && is_entry_referral( e ) ) {
+ /* entry is a referral, don't allow modify */
+ rs->sr_ref = get_entry_referrals( op, e );
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modify) ": entry is referral\n" );
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_matched = e->e_name.bv_val;
+ rs->sr_flags = REP_REF_MUSTBEFREED;
+ send_ldap_result( op, rs );
+ rs->sr_matched = NULL;
+ goto done;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+ if( op->o_preread ) {
+ if( preread_ctrl == NULL ) {
+ preread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if ( slap_read_controls( op, rs, e,
+ &slap_pre_read_bv, preread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_modify) ": pre-read "
+ "failed!\n" );
+ if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ /* Modify the entry */
+ dummy = *e;
+ rs->sr_err = mdb_modify_internal( op, txn, op->orm_modlist,
+ &dummy, &rs->sr_text, textbuf, textlen );
+
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modify) ": modify failed (%d)\n",
+ rs->sr_err );
+ /* Only free attrs if they were dup'd. */
+ if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
+ goto return_results;
+ }
+
+ /* change the entry itself */
+ rs->sr_err = mdb_id2entry_update( op, txn, NULL, &dummy );
+ if ( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modify) ": id2entry update failed " "(%d)\n",
+ rs->sr_err );
+ if ( rs->sr_err == LDAP_ADMINLIMIT_EXCEEDED ) {
+ rs->sr_text = "entry too big";
+ } else {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "entry update failed";
+ }
+ goto return_results;
+ }
+
+ if( op->o_postread ) {
+ if( postread_ctrl == NULL ) {
+ postread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if( slap_read_controls( op, rs, &dummy,
+ &slap_post_read_bv, postread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_modify)
+ ": post-read failed!\n" );
+ if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ /* Only free attrs if they were dup'd. */
+ if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
+ if( moi == &opinfo ) {
+ LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
+ opinfo.moi_oe.oe_key = NULL;
+ if( op->o_noop ) {
+ mdb->mi_numads = numads;
+ mdb_txn_abort( txn );
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ txn = NULL;
+ goto return_results;
+ } else {
+ rs->sr_err = mdb_txn_commit( txn );
+ if ( rs->sr_err )
+ mdb->mi_numads = numads;
+ txn = NULL;
+ }
+ }
+
+ if( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_modify) ": txn_%s failed: %s (%d)\n",
+ op->o_noop ? "abort (no-op)" : "commit",
+ mdb_strerror(rs->sr_err), rs->sr_err );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "commit failed";
+
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modify) ": updated%s id=%08lx dn=\"%s\"\n",
+ op->o_noop ? " (no-op)" : "",
+ dummy.e_id, op->o_req_dn.bv_val );
+
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+ if( dummy.e_attrs ) {
+ attrs_free( dummy.e_attrs );
+ }
+ send_ldap_result( op, rs );
+
+#if 0
+ if( rs->sr_err == LDAP_SUCCESS && mdb->bi_txn_cp_kbyte ) {
+ TXN_CHECKPOINT( mdb->bi_dbenv,
+ mdb->bi_txn_cp_kbyte, mdb->bi_txn_cp_min, 0 );
+ }
+#endif
+
+done:
+ slap_graduate_commit_csn( op );
+
+ if( moi == &opinfo ) {
+ if( txn != NULL ) {
+ mdb->mi_numads = numads;
+ mdb_txn_abort( txn );
+ }
+ if ( opinfo.moi_oe.oe_key ) {
+ LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
+ }
+ } else {
+ moi->moi_ref--;
+ }
+
+ if( e != NULL ) {
+ mdb_entry_return( op, e );
+ }
+
+ if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
+ slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
+ }
+ if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
+ slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
+ }
+
+ rs->sr_text = NULL;
+
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-mdb/modrdn.c b/servers/slapd/back-mdb/modrdn.c
new file mode 100644
index 0000000..5b234ba
--- /dev/null
+++ b/servers/slapd/back-mdb/modrdn.c
@@ -0,0 +1,624 @@
+/* modrdn.c - mdb backend modrdn routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+
+int
+mdb_modrdn( Operation *op, SlapReply *rs )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ AttributeDescription *children = slap_schema.si_ad_children;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+ struct berval p_dn, p_ndn;
+ struct berval new_dn = {0, NULL}, new_ndn = {0, NULL};
+ Entry *e = NULL;
+ Entry *p = NULL;
+ /* LDAP v2 supporting correct attribute handling. */
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof textbuf;
+ MDB_txn *txn = NULL;
+ MDB_cursor *mc;
+ struct mdb_op_info opinfo = {{{ 0 }}}, *moi = &opinfo;
+ Entry dummy = {0};
+
+ Entry *np = NULL; /* newSuperior Entry */
+ struct berval *np_dn = NULL; /* newSuperior dn */
+ struct berval *np_ndn = NULL; /* newSuperior ndn */
+ struct berval *new_parent_dn = NULL; /* np_dn, p_dn, or NULL */
+
+ int manageDSAit = get_manageDSAit( op );
+
+ ID nid, nsubs;
+ LDAPControl **preread_ctrl = NULL;
+ LDAPControl **postread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+
+ int parent_is_glue = 0;
+ int parent_is_leaf = 0;
+
+ Debug( LDAP_DEBUG_TRACE, "==>" LDAP_XSTRING(mdb_modrdn) "(%s,%s,%s)\n",
+ op->o_req_dn.bv_val,op->oq_modrdn.rs_newrdn.bv_val,
+ op->oq_modrdn.rs_newSup ? op->oq_modrdn.rs_newSup->bv_val : "NULL" );
+
+ ctrls[num_ctrls] = NULL;
+
+ /* begin transaction */
+ rs->sr_err = mdb_opinfo_get( op, mdb, 0, &moi );
+ rs->sr_text = NULL;
+ if( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn) ": txn_begin failed: "
+ "%s (%d)\n", mdb_strerror(rs->sr_err), rs->sr_err );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ txn = moi->moi_txn;
+
+ slap_mods_opattrs( op, &op->orr_modlist, 1 );
+
+ if ( be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
+#ifdef MDB_MULTIPLE_SUFFIXES
+ /* Allow renaming one suffix entry to another */
+ p_ndn = slap_empty_bv;
+#else
+ /* There can only be one suffix entry */
+ rs->sr_err = LDAP_NAMING_VIOLATION;
+ rs->sr_text = "cannot rename suffix entry";
+ goto return_results;
+#endif
+ } else {
+ dnParent( &op->o_req_ndn, &p_ndn );
+ }
+ np_ndn = &p_ndn;
+ /* Make sure parent entry exist and we can write its
+ * children.
+ */
+ rs->sr_err = mdb_cursor_open( txn, mdb->mi_dn2id, &mc );
+ if ( rs->sr_err != 0 ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_modrdn)
+ ": cursor_open failed: %s (%d)\n",
+ mdb_strerror(rs->sr_err), rs->sr_err );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "DN cursor_open failed";
+ goto return_results;
+ }
+ rs->sr_err = mdb_dn2entry( op, txn, mc, &p_ndn, &p, NULL, 0 );
+ switch( rs->sr_err ) {
+ case MDB_NOTFOUND:
+ Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(mdb_modrdn)
+ ": parent does not exist\n" );
+ rs->sr_ref = referral_rewrite( default_referral, NULL,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ rs->sr_err = LDAP_REFERRAL;
+
+ send_ldap_result( op, rs );
+
+ ber_bvarray_free( rs->sr_ref );
+ goto done;
+ case 0:
+ break;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* check parent for "children" acl */
+ rs->sr_err = access_allowed( op, p,
+ children, NULL,
+ op->oq_modrdn.rs_newSup == NULL ?
+ ACL_WRITE : ACL_WDEL,
+ NULL );
+
+ if ( ! rs->sr_err ) {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ Debug( LDAP_DEBUG_TRACE, "no access to parent\n" );
+ rs->sr_text = "no write access to parent's children";
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn) ": wr to children "
+ "of entry %s OK\n", p_ndn.bv_val );
+
+ if ( p_ndn.bv_val == slap_empty_bv.bv_val ) {
+ p_dn = slap_empty_bv;
+ } else {
+ dnParent( &op->o_req_dn, &p_dn );
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn) ": parent dn=%s\n",
+ p_dn.bv_val );
+
+ /* get entry */
+ rs->sr_err = mdb_dn2entry( op, txn, mc, &op->o_req_ndn, &e, &nsubs, 0 );
+ switch( rs->sr_err ) {
+ case MDB_NOTFOUND:
+ e = p;
+ p = NULL;
+ case 0:
+ break;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* FIXME: dn2entry() should return non-glue entry */
+ if (( rs->sr_err == MDB_NOTFOUND ) ||
+ ( !manageDSAit && e && is_entry_glue( e )))
+ {
+ if( e != NULL ) {
+ rs->sr_matched = ch_strdup( e->e_dn );
+ if ( is_entry_referral( e )) {
+ BerVarray ref = get_entry_referrals( op, e );
+ rs->sr_ref = referral_rewrite( ref, &e->e_name,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ } else {
+ rs->sr_ref = NULL;
+ }
+ mdb_entry_return( op, e );
+ e = NULL;
+
+ } else {
+ rs->sr_ref = referral_rewrite( default_referral, NULL,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ }
+
+ rs->sr_err = LDAP_REFERRAL;
+ send_ldap_result( op, rs );
+
+ ber_bvarray_free( rs->sr_ref );
+ free( (char *)rs->sr_matched );
+ rs->sr_ref = NULL;
+ rs->sr_matched = NULL;
+
+ goto done;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+ /* check write on old entry */
+ rs->sr_err = access_allowed( op, e, entry, NULL, ACL_WRITE, NULL );
+ if ( ! rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE, "no access to entry\n" );
+ rs->sr_text = "no write access to old entry";
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto return_results;
+ }
+
+ if (!manageDSAit && is_entry_referral( e ) ) {
+ /* entry is a referral, don't allow rename */
+ rs->sr_ref = get_entry_referrals( op, e );
+
+ Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(mdb_modrdn)
+ ": entry %s is referral\n", e->e_dn );
+
+ rs->sr_err = LDAP_REFERRAL,
+ rs->sr_matched = e->e_name.bv_val;
+ send_ldap_result( op, rs );
+
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ rs->sr_matched = NULL;
+ goto done;
+ }
+
+ new_parent_dn = &p_dn; /* New Parent unless newSuperior given */
+
+ if ( op->oq_modrdn.rs_newSup != NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn)
+ ": new parent \"%s\" requested...\n",
+ op->oq_modrdn.rs_newSup->bv_val );
+
+ /* newSuperior == oldParent? */
+ if( dn_match( &p_ndn, op->oq_modrdn.rs_nnewSup ) ) {
+ Debug( LDAP_DEBUG_TRACE, "mdb_back_modrdn: "
+ "new parent \"%s\" same as the old parent \"%s\"\n",
+ op->oq_modrdn.rs_newSup->bv_val, p_dn.bv_val );
+ op->oq_modrdn.rs_newSup = NULL; /* ignore newSuperior */
+ }
+ }
+
+ /* There's a MDB_MULTIPLE_SUFFIXES case here that this code doesn't
+ * support. E.g., two suffixes dc=foo,dc=com and dc=bar,dc=net.
+ * We do not allow modDN
+ * dc=foo,dc=com
+ * newrdn dc=bar
+ * newsup dc=net
+ * and we probably should. But since MULTIPLE_SUFFIXES is deprecated
+ * I'm ignoring this problem for now.
+ */
+ if ( op->oq_modrdn.rs_newSup != NULL ) {
+ if ( op->oq_modrdn.rs_newSup->bv_len ) {
+ np_dn = op->oq_modrdn.rs_newSup;
+ np_ndn = op->oq_modrdn.rs_nnewSup;
+
+ /* newSuperior == oldParent? - checked above */
+ /* newSuperior == entry being moved?, if so ==> ERROR */
+ if ( dnIsSuffix( np_ndn, &e->e_nname )) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = "new superior not found";
+ goto return_results;
+ }
+ /* Get Entry with dn=newSuperior. Does newSuperior exist? */
+ rs->sr_err = mdb_dn2entry( op, txn, NULL, np_ndn, &np, NULL, 0 );
+
+ switch( rs->sr_err ) {
+ case 0:
+ break;
+ case MDB_NOTFOUND:
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn)
+ ": newSup(ndn=%s) not here!\n",
+ np_ndn->bv_val );
+ rs->sr_text = "new superior not found";
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto return_results;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* check newSuperior for "children" acl */
+ rs->sr_err = access_allowed( op, np, children,
+ NULL, ACL_WADD, NULL );
+
+ if( ! rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn)
+ ": no wr to newSup children\n" );
+ rs->sr_text = "no write access to new superior's children";
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn)
+ ": wr to new parent OK np=%p, id=%ld\n",
+ (void *) np, (long) np->e_id );
+
+ if ( is_entry_alias( np ) ) {
+ /* parent is an alias, don't allow add */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn)
+ ": entry is alias\n" );
+ rs->sr_text = "new superior is an alias";
+ rs->sr_err = LDAP_ALIAS_PROBLEM;
+ goto return_results;
+ }
+
+ if ( is_entry_referral( np ) ) {
+ /* parent is a referral, don't allow add */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn)
+ ": entry is referral\n" );
+ rs->sr_text = "new superior is a referral";
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+ np_dn = &np->e_name;
+
+ } else {
+ np_dn = NULL;
+
+ /* no parent, modrdn entry directly under root */
+ if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv )
+ || be_isupdate( op ) ) {
+ np = (Entry *)&slap_entry_root;
+
+ /* check parent for "children" acl */
+ rs->sr_err = access_allowed( op, np,
+ children, NULL, ACL_WADD, NULL );
+
+ np = NULL;
+
+ if ( ! rs->sr_err ) {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ Debug( LDAP_DEBUG_TRACE,
+ "no access to new superior\n" );
+ rs->sr_text =
+ "no write access to new superior's children";
+ goto return_results;
+ }
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn)
+ ": wr to new parent's children OK\n" );
+
+ new_parent_dn = np_dn;
+ }
+
+ /* Build target dn and make sure target entry doesn't exist already. */
+ if (!new_dn.bv_val) {
+ build_new_dn( &new_dn, new_parent_dn, &op->oq_modrdn.rs_newrdn, op->o_tmpmemctx );
+ }
+
+ if (!new_ndn.bv_val) {
+ dnNormalize( 0, NULL, NULL, &new_dn, &new_ndn, op->o_tmpmemctx );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(mdb_modrdn) ": new ndn=%s\n",
+ new_ndn.bv_val );
+
+ /* Shortcut the search */
+ rs->sr_err = mdb_dn2id ( op, txn, NULL, &new_ndn, &nid, NULL, NULL, NULL );
+ switch( rs->sr_err ) {
+ case MDB_NOTFOUND:
+ break;
+ case 0:
+ /* Allow rename to same DN */
+ if ( nid == e->e_id )
+ break;
+ rs->sr_err = LDAP_ALREADY_EXISTS;
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ if( op->o_preread ) {
+ if( preread_ctrl == NULL ) {
+ preread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if( slap_read_controls( op, rs, e,
+ &slap_pre_read_bv, preread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_modrdn)
+ ": pre-read failed!\n" );
+ if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ /* delete old DN
+ * If moving to a new parent, must delete current subtree count,
+ * otherwise leave it unchanged since we'll be adding it right back.
+ */
+ rs->sr_err = mdb_dn2id_delete( op, mc, e->e_id, np ? nsubs : 0 );
+ if ( rs->sr_err != 0 ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_modrdn)
+ ": dn2id del failed: %s (%d)\n",
+ mdb_strerror(rs->sr_err), rs->sr_err );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "DN index delete fail";
+ goto return_results;
+ }
+
+ /* copy the entry, then override some fields */
+ dummy = *e;
+ dummy.e_name = new_dn;
+ dummy.e_nname = new_ndn;
+ dummy.e_attrs = NULL;
+
+ /* add new DN */
+ rs->sr_err = mdb_dn2id_add( op, mc, mc, np ? np->e_id : p->e_id,
+ nsubs, np != NULL, &dummy );
+ if ( rs->sr_err != 0 ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_modrdn)
+ ": dn2id add failed: %s (%d)\n",
+ mdb_strerror(rs->sr_err), rs->sr_err );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "DN index add failed";
+ goto return_results;
+ }
+
+ dummy.e_attrs = e->e_attrs;
+
+ if ( op->orr_modlist != NULL ) {
+ /* modify entry */
+ rs->sr_err = mdb_modify_internal( op, txn, op->orr_modlist, &dummy,
+ &rs->sr_text, textbuf, textlen );
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_modrdn)
+ ": modify failed: %s (%d)\n",
+ mdb_strerror(rs->sr_err), rs->sr_err );
+ goto return_results;
+ }
+ }
+
+ /* id2entry index */
+ rs->sr_err = mdb_id2entry_update( op, txn, NULL, &dummy );
+ if ( rs->sr_err != 0 ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_modrdn)
+ ": id2entry failed: %s (%d)\n",
+ mdb_strerror(rs->sr_err), rs->sr_err );
+ if ( rs->sr_err == LDAP_ADMINLIMIT_EXCEEDED ) {
+ rs->sr_text = "entry too big";
+ } else {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "entry update failed";
+ }
+ goto return_results;
+ }
+
+ if ( p_ndn.bv_len != 0 ) {
+ if ((parent_is_glue = is_entry_glue(p))) {
+ rs->sr_err = mdb_dn2id_children( op, txn, p );
+ if ( rs->sr_err != MDB_NOTFOUND ) {
+ switch( rs->sr_err ) {
+ case 0:
+ break;
+ default:
+ Debug(LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(mdb_modrdn)
+ ": has_children failed: %s (%d)\n",
+ mdb_strerror(rs->sr_err), rs->sr_err );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ } else {
+ parent_is_leaf = 1;
+ }
+ }
+ mdb_entry_return( op, p );
+ p = NULL;
+ }
+
+ if( op->o_postread ) {
+ if( postread_ctrl == NULL ) {
+ postread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if( slap_read_controls( op, rs, &dummy,
+ &slap_post_read_bv, postread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(mdb_modrdn)
+ ": post-read failed!\n" );
+ if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ if( moi == &opinfo ) {
+ LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
+ opinfo.moi_oe.oe_key = NULL;
+ if( op->o_noop ) {
+ mdb_txn_abort( txn );
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ txn = NULL;
+ goto return_results;
+
+ } else {
+ if(( rs->sr_err=mdb_txn_commit( txn )) != 0 ) {
+ rs->sr_text = "txn_commit failed";
+ } else {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+ txn = NULL;
+ }
+ }
+
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_modrdn) ": %s : %s (%d)\n",
+ rs->sr_text, mdb_strerror(rs->sr_err), rs->sr_err );
+ rs->sr_err = LDAP_OTHER;
+
+ goto return_results;
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_modrdn)
+ ": rdn modified%s id=%08lx dn=\"%s\"\n",
+ op->o_noop ? " (no-op)" : "",
+ dummy.e_id, op->o_req_dn.bv_val );
+ rs->sr_text = NULL;
+ if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+ if ( e != NULL && dummy.e_attrs != e->e_attrs ) {
+ attrs_free( dummy.e_attrs );
+ }
+ send_ldap_result( op, rs );
+
+#if 0
+ if( rs->sr_err == LDAP_SUCCESS && mdb->bi_txn_cp_kbyte ) {
+ TXN_CHECKPOINT( mdb->bi_dbenv,
+ mdb->bi_txn_cp_kbyte, mdb->bi_txn_cp_min, 0 );
+ }
+#endif
+
+ if ( rs->sr_err == LDAP_SUCCESS && parent_is_glue && parent_is_leaf ) {
+ op->o_delete_glue_parent = 1;
+ }
+
+done:
+ slap_graduate_commit_csn( op );
+
+ if( new_ndn.bv_val != NULL ) op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
+ if( new_dn.bv_val != NULL ) op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
+
+ /* LDAP v3 Support */
+ if( np != NULL ) {
+ /* free new parent */
+ mdb_entry_return( op, np );
+ }
+
+ if( p != NULL ) {
+ /* free parent */
+ mdb_entry_return( op, p );
+ }
+
+ /* free entry */
+ if( e != NULL ) {
+ mdb_entry_return( op, e );
+ }
+
+ if( moi == &opinfo ) {
+ if( txn != NULL ) {
+ mdb_txn_abort( txn );
+ }
+ if ( opinfo.moi_oe.oe_key ) {
+ LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
+ }
+ } else {
+ moi->moi_ref--;
+ }
+
+ if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
+ slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
+ }
+ if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
+ slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
+ }
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-mdb/monitor.c b/servers/slapd/back-mdb/monitor.c
new file mode 100644
index 0000000..fc77bc6
--- /dev/null
+++ b/servers/slapd/back-mdb/monitor.c
@@ -0,0 +1,807 @@
+/* monitor.c - monitor mdb backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+#include <ac/stdlib.h>
+#include <ac/errno.h>
+#include <sys/stat.h>
+#include "lutil.h"
+#include "back-mdb.h"
+
+#include "../back-monitor/back-monitor.h"
+
+#include "slap-config.h"
+
+static ObjectClass *oc_olmMDBDatabase;
+
+static AttributeDescription *ad_olmDbDirectory;
+
+#ifdef MDB_MONITOR_IDX
+static int
+mdb_monitor_idx_entry_add(
+ struct mdb_info *mdb,
+ Entry *e );
+
+static AttributeDescription *ad_olmDbNotIndexed;
+#endif /* MDB_MONITOR_IDX */
+
+static AttributeDescription *ad_olmMDBPagesMax,
+ *ad_olmMDBPagesUsed, *ad_olmMDBPagesFree;
+
+static AttributeDescription *ad_olmMDBReadersMax,
+ *ad_olmMDBReadersUsed;
+
+static AttributeDescription *ad_olmMDBEntries;
+
+/*
+ * NOTE: there's some confusion in monitor OID arc;
+ * by now, let's consider:
+ *
+ * Subsystems monitor attributes 1.3.6.1.4.1.4203.666.1.55.0
+ * Databases monitor attributes 1.3.6.1.4.1.4203.666.1.55.0.1
+ * MDB database monitor attributes 1.3.6.1.4.1.4203.666.1.55.0.1.3
+ *
+ * Subsystems monitor objectclasses 1.3.6.1.4.1.4203.666.3.16.0
+ * Databases monitor objectclasses 1.3.6.1.4.1.4203.666.3.16.0.1
+ * MDB database monitor objectclasses 1.3.6.1.4.1.4203.666.3.16.0.1.3
+ */
+
+static struct {
+ char *name;
+ char *oid;
+} s_oid[] = {
+ { "olmMDBAttributes", "olmDatabaseAttributes:1" },
+ { "olmMDBObjectClasses", "olmDatabaseObjectClasses:1" },
+
+ { NULL }
+};
+
+static struct {
+ char *desc;
+ AttributeDescription **ad;
+} s_at[] = {
+ { "( olmDatabaseAttributes:1 "
+ "NAME ( 'olmDbDirectory' ) "
+ "DESC 'Path name of the directory "
+ "where the database environment resides' "
+ "SUP monitoredInfo "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmDbDirectory },
+
+#ifdef MDB_MONITOR_IDX
+ { "( olmDatabaseAttributes:2 "
+ "NAME ( 'olmDbNotIndexed' ) "
+ "DESC 'Missing indexes resulting from candidate selection' "
+ "SUP monitoredInfo "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmDbNotIndexed },
+#endif /* MDB_MONITOR_IDX */
+
+ { "( olmMDBAttributes:1 "
+ "NAME ( 'olmMDBPagesMax' ) "
+ "DESC 'Maximum number of pages' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmMDBPagesMax },
+
+ { "( olmMDBAttributes:2 "
+ "NAME ( 'olmMDBPagesUsed' ) "
+ "DESC 'Number of pages in use' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmMDBPagesUsed },
+
+ { "( olmMDBAttributes:3 "
+ "NAME ( 'olmMDBPagesFree' ) "
+ "DESC 'Number of free pages' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmMDBPagesFree },
+
+ { "( olmMDBAttributes:4 "
+ "NAME ( 'olmMDBReadersMax' ) "
+ "DESC 'Maximum number of readers' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmMDBReadersMax },
+
+ { "( olmMDBAttributes:5 "
+ "NAME ( 'olmMDBReadersUsed' ) "
+ "DESC 'Number of readers in use' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmMDBReadersUsed },
+
+ { "( olmMDBAttributes:6 "
+ "NAME ( 'olmMDBEntries' ) "
+ "DESC 'Number of entries in DB' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmMDBEntries },
+ { NULL }
+};
+
+static struct {
+ char *desc;
+ ObjectClass **oc;
+} s_oc[] = {
+ /* augments an existing object, so it must be AUXILIARY
+ * FIXME: derive from some ABSTRACT "monitoredEntity"? */
+ { "( olmMDBObjectClasses:2 "
+ "NAME ( 'olmMDBDatabase' ) "
+ "SUP top AUXILIARY "
+ "MAY ( "
+ "olmDbDirectory "
+#ifdef MDB_MONITOR_IDX
+ "$ olmDbNotIndexed "
+#endif /* MDB_MONITOR_IDX */
+ "$ olmMDBPagesMax $ olmMDBPagesUsed $ olmMDBPagesFree "
+ "$ olmMDBReadersMax $ olmMDBReadersUsed $ olmMDBEntries "
+ ") )",
+ &oc_olmMDBDatabase },
+
+ { NULL }
+};
+
+static int
+mdb_monitor_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ void *priv )
+{
+ struct mdb_info *mdb = (struct mdb_info *) priv;
+ Attribute *a;
+ char buf[ BUFSIZ ];
+ struct berval bv;
+ MDB_stat mst;
+ MDB_envinfo mei;
+ MDB_txn *txn;
+ int rc;
+
+#ifdef MDB_MONITOR_IDX
+
+ mdb_monitor_idx_entry_add( mdb, e );
+#endif /* MDB_MONITOR_IDX */
+
+ mdb_env_stat( mdb->mi_dbenv, &mst );
+ mdb_env_info( mdb->mi_dbenv, &mei );
+
+ a = attr_find( e->e_attrs, ad_olmMDBPagesMax );
+ assert( a != NULL );
+ bv.bv_val = buf;
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%lu", mei.me_mapsize / mst.ms_psize );
+ ber_bvreplace( &a->a_vals[ 0 ], &bv );
+
+ a = attr_find( e->e_attrs, ad_olmMDBPagesUsed );
+ assert( a != NULL );
+ bv.bv_val = buf;
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%lu", mei.me_last_pgno+1 );
+ ber_bvreplace( &a->a_vals[ 0 ], &bv );
+
+ a = attr_find( e->e_attrs, ad_olmMDBReadersMax );
+ assert( a != NULL );
+ bv.bv_val = buf;
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%u", mei.me_maxreaders );
+ ber_bvreplace( &a->a_vals[ 0 ], &bv );
+
+ a = attr_find( e->e_attrs, ad_olmMDBReadersUsed );
+ assert( a != NULL );
+ bv.bv_val = buf;
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%u", mei.me_numreaders );
+ ber_bvreplace( &a->a_vals[ 0 ], &bv );
+
+ rc = mdb_txn_begin( mdb->mi_dbenv, NULL, MDB_RDONLY, &txn );
+ if ( !rc ) {
+ MDB_cursor *cursor;
+ MDB_val key, data;
+ size_t pages = 0, *iptr;
+
+ rc = mdb_cursor_open( txn, 0, &cursor );
+ if ( !rc ) {
+ while (( rc = mdb_cursor_get( cursor, &key, &data, MDB_NEXT )) == 0 ) {
+ iptr = data.mv_data;
+ pages += *iptr;
+ }
+ mdb_cursor_close( cursor );
+ }
+
+ mdb_stat( txn, mdb->mi_id2entry, &mst );
+ a = attr_find( e->e_attrs, ad_olmMDBEntries );
+ assert( a != NULL );
+ bv.bv_val = buf;
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%lu", mst.ms_entries );
+ ber_bvreplace( &a->a_vals[ 0 ], &bv );
+
+ mdb_txn_abort( txn );
+
+ a = attr_find( e->e_attrs, ad_olmMDBPagesFree );
+ assert( a != NULL );
+ bv.bv_val = buf;
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%lu", pages );
+ ber_bvreplace( &a->a_vals[ 0 ], &bv );
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+#if 0 /* uncomment if required */
+static int
+mdb_monitor_modify(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ void *priv )
+{
+ return SLAP_CB_CONTINUE;
+}
+#endif
+
+static int
+mdb_monitor_free(
+ Entry *e,
+ void **priv )
+{
+ struct berval values[ 2 ];
+ Modification mod = { 0 };
+
+ const char *text;
+ char textbuf[ SLAP_TEXT_BUFLEN ];
+
+ int i, rc;
+
+ /* NOTE: if slap_shutdown != 0, priv might have already been freed */
+ *priv = NULL;
+
+ /* Remove objectClass */
+ mod.sm_op = LDAP_MOD_DELETE;
+ mod.sm_desc = slap_schema.si_ad_objectClass;
+ mod.sm_values = values;
+ mod.sm_numvals = 1;
+ values[ 0 ] = oc_olmMDBDatabase->soc_cname;
+ BER_BVZERO( &values[ 1 ] );
+
+ rc = modify_delete_values( e, &mod, 1, &text,
+ textbuf, sizeof( textbuf ) );
+ /* don't care too much about return code... */
+
+ /* remove attrs */
+ mod.sm_values = NULL;
+ mod.sm_numvals = 0;
+ for ( i = 0; s_at[ i ].desc != NULL; i++ ) {
+ mod.sm_desc = *s_at[ i ].ad;
+ rc = modify_delete_values( e, &mod, 1, &text,
+ textbuf, sizeof( textbuf ) );
+ /* don't care too much about return code... */
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+ * call from within mdb_initialize()
+ */
+static int
+mdb_monitor_initialize( void )
+{
+ int i, code;
+ ConfigArgs c;
+ char *argv[ 3 ];
+
+ static int mdb_monitor_initialized = 0;
+
+ /* set to 0 when successfully initialized; otherwise, remember failure */
+ static int mdb_monitor_initialized_failure = 1;
+
+ if ( mdb_monitor_initialized++ ) {
+ return mdb_monitor_initialized_failure;
+ }
+
+ if ( backend_info( "monitor" ) == NULL ) {
+ return -1;
+ }
+
+ /* register schema here */
+
+ argv[ 0 ] = "back-mdb monitor";
+ c.argv = argv;
+ c.argc = 3;
+ c.fname = argv[0];
+
+ for ( i = 0; s_oid[ i ].name; i++ ) {
+ c.lineno = i;
+ argv[ 1 ] = s_oid[ i ].name;
+ argv[ 2 ] = s_oid[ i ].oid;
+
+ if ( parse_oidm( &c, 0, NULL ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, LDAP_XSTRING(mdb_monitor_initialize)
+ ": unable to add "
+ "objectIdentifier \"%s=%s\"\n",
+ s_oid[ i ].name, s_oid[ i ].oid );
+ return 2;
+ }
+ }
+
+ for ( i = 0; s_at[ i ].desc != NULL; i++ ) {
+ code = register_at( s_at[ i ].desc, s_at[ i ].ad, 1 );
+ if ( code != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, LDAP_XSTRING(mdb_monitor_initialize)
+ ": register_at failed for attributeType (%s)\n",
+ s_at[ i ].desc );
+ return 3;
+
+ } else {
+ (*s_at[ i ].ad)->ad_type->sat_flags |= SLAP_AT_HIDE;
+ }
+ }
+
+ for ( i = 0; s_oc[ i ].desc != NULL; i++ ) {
+ code = register_oc( s_oc[ i ].desc, s_oc[ i ].oc, 1 );
+ if ( code != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, LDAP_XSTRING(mdb_monitor_initialize)
+ ": register_oc failed for objectClass (%s)\n",
+ s_oc[ i ].desc );
+ return 4;
+
+ } else {
+ (*s_oc[ i ].oc)->soc_flags |= SLAP_OC_HIDE;
+ }
+ }
+
+ return ( mdb_monitor_initialized_failure = LDAP_SUCCESS );
+}
+
+/*
+ * call from within mdb_db_init()
+ */
+int
+mdb_monitor_db_init( BackendDB *be )
+{
+#ifdef MDB_MONITOR_IDX
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+#endif /* MDB_MONITOR_IDX */
+
+ if ( mdb_monitor_initialize() == LDAP_SUCCESS ) {
+ /* monitoring in back-mdb is on by default */
+ SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_MONITORING;
+ }
+
+#ifdef MDB_MONITOR_IDX
+ mdb->mi_idx = NULL;
+ ldap_pvt_thread_mutex_init( &mdb->mi_idx_mutex );
+#endif /* MDB_MONITOR_IDX */
+
+ return 0;
+}
+
+/*
+ * call from within mdb_db_open()
+ */
+int
+mdb_monitor_db_open( BackendDB *be )
+{
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+ Attribute *a, *next;
+ monitor_callback_t *cb = NULL;
+ int rc = 0;
+ BackendInfo *mi;
+ monitor_extra_t *mbe;
+
+ if ( !SLAP_DBMONITORING( be ) ) {
+ return 0;
+ }
+
+ mi = backend_info( "monitor" );
+ if ( !mi || !mi->bi_extra ) {
+ SLAP_DBFLAGS( be ) ^= SLAP_DBFLAG_MONITORING;
+ return 0;
+ }
+ mbe = mi->bi_extra;
+
+ /* don't bother if monitor is not configured */
+ if ( !mbe->is_configured() ) {
+ static int warning = 0;
+
+ if ( warning++ == 0 ) {
+ Debug( LDAP_DEBUG_CONFIG, LDAP_XSTRING(mdb_monitor_db_open)
+ ": monitoring disabled; "
+ "configure monitor database to enable\n" );
+ }
+
+ return 0;
+ }
+
+ /* alloc as many as required (plus 1 for objectClass) */
+ a = attrs_alloc( 1 + 7 );
+ if ( a == NULL ) {
+ rc = 1;
+ goto cleanup;
+ }
+
+ a->a_desc = slap_schema.si_ad_objectClass;
+ attr_valadd( a, &oc_olmMDBDatabase->soc_cname, NULL, 1 );
+ next = a->a_next;
+
+ {
+ struct berval bv = BER_BVC( "0" );
+
+ next->a_desc = ad_olmMDBPagesMax;
+ attr_valadd( next, &bv, NULL, 1 );
+ next = next->a_next;
+
+ next->a_desc = ad_olmMDBPagesUsed;
+ attr_valadd( next, &bv, NULL, 1 );
+ next = next->a_next;
+
+ next->a_desc = ad_olmMDBPagesFree;
+ attr_valadd( next, &bv, NULL, 1 );
+ next = next->a_next;
+
+ next->a_desc = ad_olmMDBReadersMax;
+ attr_valadd( next, &bv, NULL, 1 );
+ next = next->a_next;
+
+ next->a_desc = ad_olmMDBReadersUsed;
+ attr_valadd( next, &bv, NULL, 1 );
+ next = next->a_next;
+
+ next->a_desc = ad_olmMDBEntries;
+ attr_valadd( next, &bv, NULL, 1 );
+ next = next->a_next;
+ }
+
+ {
+ struct berval bv, nbv;
+ ber_len_t pathlen = 0, len = 0;
+ char path[ MAXPATHLEN ] = { '\0' };
+ char *fname = mdb->mi_dbenv_home,
+ *ptr;
+
+ len = strlen( fname );
+ if ( fname[ 0 ] != '/' ) {
+ /* get full path name */
+ getcwd( path, sizeof( path ) );
+ pathlen = strlen( path );
+
+ if ( fname[ 0 ] == '.' && fname[ 1 ] == '/' ) {
+ fname += 2;
+ len -= 2;
+ }
+ }
+
+ bv.bv_len = pathlen + STRLENOF( "/" ) + len;
+ ptr = bv.bv_val = ch_malloc( bv.bv_len + STRLENOF( "/" ) + 1 );
+ if ( pathlen ) {
+ ptr = lutil_strncopy( ptr, path, pathlen );
+ ptr[ 0 ] = '/';
+ ptr++;
+ }
+ ptr = lutil_strncopy( ptr, fname, len );
+ if ( ptr[ -1 ] != '/' ) {
+ ptr[ 0 ] = '/';
+ ptr++;
+ }
+ ptr[ 0 ] = '\0';
+
+ attr_normalize_one( ad_olmDbDirectory, &bv, &nbv, NULL );
+
+ next->a_desc = ad_olmDbDirectory;
+ next->a_vals = ch_calloc( sizeof( struct berval ), 2 );
+ next->a_vals[ 0 ] = bv;
+ next->a_numvals = 1;
+
+ if ( BER_BVISNULL( &nbv ) ) {
+ next->a_nvals = next->a_vals;
+
+ } else {
+ next->a_nvals = ch_calloc( sizeof( struct berval ), 2 );
+ next->a_nvals[ 0 ] = nbv;
+ }
+
+ next = next->a_next;
+ }
+
+ cb = ch_calloc( sizeof( monitor_callback_t ), 1 );
+ cb->mc_update = mdb_monitor_update;
+#if 0 /* uncomment if required */
+ cb->mc_modify = mdb_monitor_modify;
+#endif
+ cb->mc_free = mdb_monitor_free;
+ cb->mc_private = (void *)mdb;
+
+ /* make sure the database is registered; then add monitor attributes */
+ rc = mbe->register_database( be, &mdb->mi_monitor.mdm_ndn );
+ if ( rc == 0 ) {
+ rc = mbe->register_entry_attrs( &mdb->mi_monitor.mdm_ndn, a, cb,
+ NULL, -1, NULL );
+ }
+
+cleanup:;
+ if ( rc != 0 ) {
+ if ( cb != NULL ) {
+ ch_free( cb );
+ cb = NULL;
+ }
+
+ if ( a != NULL ) {
+ attrs_free( a );
+ a = NULL;
+ }
+ }
+
+ /* store for cleanup */
+ mdb->mi_monitor.mdm_cb = (void *)cb;
+
+ /* we don't need to keep track of the attributes, because
+ * mdb_monitor_free() takes care of everything */
+ if ( a != NULL ) {
+ attrs_free( a );
+ }
+
+ return rc;
+}
+
+/*
+ * call from within mdb_db_close()
+ */
+int
+mdb_monitor_db_close( BackendDB *be )
+{
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+
+ if ( !BER_BVISNULL( &mdb->mi_monitor.mdm_ndn ) ) {
+ BackendInfo *mi = backend_info( "monitor" );
+ monitor_extra_t *mbe;
+
+ if ( mi && mi->bi_extra ) {
+ struct berval dummy = BER_BVNULL;
+ mbe = mi->bi_extra;
+ mbe->unregister_entry_callback( &mdb->mi_monitor.mdm_ndn,
+ (monitor_callback_t *)mdb->mi_monitor.mdm_cb,
+ &dummy, 0, &dummy );
+ }
+
+ memset( &mdb->mi_monitor, 0, sizeof( mdb->mi_monitor ) );
+ }
+
+ return 0;
+}
+
+/*
+ * call from within mdb_db_destroy()
+ */
+int
+mdb_monitor_db_destroy( BackendDB *be )
+{
+#ifdef MDB_MONITOR_IDX
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+
+ /* TODO: free tree */
+ ldap_pvt_thread_mutex_destroy( &mdb->mi_idx_mutex );
+ ldap_avl_free( mdb->mi_idx, ch_free );
+#endif /* MDB_MONITOR_IDX */
+
+ return 0;
+}
+
+#ifdef MDB_MONITOR_IDX
+
+#define MDB_MONITOR_IDX_TYPES (4)
+
+typedef struct monitor_idx_t monitor_idx_t;
+
+struct monitor_idx_t {
+ AttributeDescription *idx_ad;
+ unsigned long idx_count[MDB_MONITOR_IDX_TYPES];
+};
+
+static int
+mdb_monitor_bitmask2key( slap_mask_t bitmask )
+{
+ int key;
+
+ for ( key = 0; key < 8 * (int)sizeof(slap_mask_t) && !( bitmask & 0x1U );
+ key++ )
+ bitmask >>= 1;
+
+ return key;
+}
+
+static struct berval idxbv[] = {
+ BER_BVC( "present=" ),
+ BER_BVC( "equality=" ),
+ BER_BVC( "approx=" ),
+ BER_BVC( "substr=" ),
+ BER_BVNULL
+};
+
+static ber_len_t
+mdb_monitor_idx2len( monitor_idx_t *idx )
+{
+ int i;
+ ber_len_t len = 0;
+
+ for ( i = 0; i < MDB_MONITOR_IDX_TYPES; i++ ) {
+ if ( idx->idx_count[ i ] != 0 ) {
+ len += idxbv[i].bv_len;
+ }
+ }
+
+ return len;
+}
+
+static int
+monitor_idx_cmp( const void *p1, const void *p2 )
+{
+ const monitor_idx_t *idx1 = (const monitor_idx_t *)p1;
+ const monitor_idx_t *idx2 = (const monitor_idx_t *)p2;
+
+ return SLAP_PTRCMP( idx1->idx_ad, idx2->idx_ad );
+}
+
+static int
+monitor_idx_dup( void *p1, void *p2 )
+{
+ monitor_idx_t *idx1 = (monitor_idx_t *)p1;
+ monitor_idx_t *idx2 = (monitor_idx_t *)p2;
+
+ return SLAP_PTRCMP( idx1->idx_ad, idx2->idx_ad ) == 0 ? -1 : 0;
+}
+
+int
+mdb_monitor_idx_add(
+ struct mdb_info *mdb,
+ AttributeDescription *desc,
+ slap_mask_t type )
+{
+ monitor_idx_t idx_dummy = { 0 },
+ *idx;
+ int rc = 0, key;
+
+ idx_dummy.idx_ad = desc;
+ key = mdb_monitor_bitmask2key( type ) - 1;
+ if ( key >= MDB_MONITOR_IDX_TYPES ) {
+ /* invalid index type */
+ return -1;
+ }
+
+ ldap_pvt_thread_mutex_lock( &mdb->mi_idx_mutex );
+
+ idx = (monitor_idx_t *)ldap_avl_find( mdb->mi_idx,
+ (caddr_t)&idx_dummy, monitor_idx_cmp );
+ if ( idx == NULL ) {
+ idx = (monitor_idx_t *)ch_calloc( sizeof( monitor_idx_t ), 1 );
+ idx->idx_ad = desc;
+ idx->idx_count[ key ] = 1;
+
+ switch ( ldap_avl_insert( &mdb->mi_idx, (caddr_t)idx,
+ monitor_idx_cmp, monitor_idx_dup ) )
+ {
+ case 0:
+ break;
+
+ default:
+ ch_free( idx );
+ rc = -1;
+ }
+
+ } else {
+ idx->idx_count[ key ]++;
+ }
+
+ ldap_pvt_thread_mutex_unlock( &mdb->mi_idx_mutex );
+
+ return rc;
+}
+
+static int
+mdb_monitor_idx_apply( void *v_idx, void *v_valp )
+{
+ monitor_idx_t *idx = (monitor_idx_t *)v_idx;
+ BerVarray *valp = (BerVarray *)v_valp;
+
+ struct berval bv;
+ char *ptr;
+ char count_buf[ MDB_MONITOR_IDX_TYPES ][ SLAP_TEXT_BUFLEN ];
+ ber_len_t count_len[ MDB_MONITOR_IDX_TYPES ],
+ idx_len;
+ int i, num = 0;
+
+ idx_len = mdb_monitor_idx2len( idx );
+
+ bv.bv_len = 0;
+ for ( i = 0; i < MDB_MONITOR_IDX_TYPES; i++ ) {
+ if ( idx->idx_count[ i ] == 0 ) {
+ continue;
+ }
+
+ count_len[ i ] = snprintf( count_buf[ i ],
+ sizeof( count_buf[ i ] ), "%lu", idx->idx_count[ i ] );
+ bv.bv_len += count_len[ i ];
+ num++;
+ }
+
+ bv.bv_len += idx->idx_ad->ad_cname.bv_len
+ + num
+ + idx_len;
+ ptr = bv.bv_val = ch_malloc( bv.bv_len + 1 );
+ ptr = lutil_strcopy( ptr, idx->idx_ad->ad_cname.bv_val );
+ for ( i = 0; i < MDB_MONITOR_IDX_TYPES; i++ ) {
+ if ( idx->idx_count[ i ] == 0 ) {
+ continue;
+ }
+
+ ptr[ 0 ] = '#';
+ ++ptr;
+ ptr = lutil_strcopy( ptr, idxbv[ i ].bv_val );
+ ptr = lutil_strcopy( ptr, count_buf[ i ] );
+ }
+
+ ber_bvarray_add( valp, &bv );
+
+ return 0;
+}
+
+static int
+mdb_monitor_idx_entry_add(
+ struct mdb_info *mdb,
+ Entry *e )
+{
+ BerVarray vals = NULL;
+ Attribute *a;
+
+ a = attr_find( e->e_attrs, ad_olmDbNotIndexed );
+
+ ldap_pvt_thread_mutex_lock( &mdb->mi_idx_mutex );
+
+ ldap_avl_apply( mdb->mi_idx, mdb_monitor_idx_apply,
+ &vals, -1, AVL_INORDER );
+
+ ldap_pvt_thread_mutex_unlock( &mdb->mi_idx_mutex );
+
+ if ( vals != NULL ) {
+ if ( a != NULL ) {
+ assert( a->a_nvals == a->a_vals );
+
+ ber_bvarray_free( a->a_vals );
+
+ } else {
+ Attribute **ap;
+
+ for ( ap = &e->e_attrs; *ap != NULL; ap = &(*ap)->a_next )
+ ;
+ *ap = attr_alloc( ad_olmDbNotIndexed );
+ a = *ap;
+ }
+ a->a_vals = vals;
+ a->a_nvals = a->a_vals;
+ }
+
+ return 0;
+}
+
+#endif /* MDB_MONITOR_IDX */
diff --git a/servers/slapd/back-mdb/nextid.c b/servers/slapd/back-mdb/nextid.c
new file mode 100644
index 0000000..b342b48
--- /dev/null
+++ b/servers/slapd/back-mdb/nextid.c
@@ -0,0 +1,53 @@
+/* init.c - initialize mdb backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+
+int mdb_next_id( BackendDB *be, MDB_cursor *mc, ID *out )
+{
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+ int rc;
+ ID id = 0;
+ MDB_val key;
+
+ rc = mdb_cursor_get(mc, &key, NULL, MDB_LAST);
+
+ switch(rc) {
+ case MDB_NOTFOUND:
+ rc = 0;
+ *out = 1;
+ break;
+ case 0:
+ memcpy( &id, key.mv_data, sizeof( id ));
+ *out = ++id;
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "=> mdb_next_id: get failed: %s (%d)\n",
+ mdb_strerror(rc), rc );
+ goto done;
+ }
+ mdb->mi_nextid = *out;
+
+done:
+ return rc;
+}
diff --git a/servers/slapd/back-mdb/operational.c b/servers/slapd/back-mdb/operational.c
new file mode 100644
index 0000000..e2d4495
--- /dev/null
+++ b/servers/slapd/back-mdb/operational.c
@@ -0,0 +1,121 @@
+/* operational.c - mdb backend operational attributes function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-mdb.h"
+
+/*
+ * sets *hasSubordinates to LDAP_COMPARE_TRUE/LDAP_COMPARE_FALSE
+ * if the entry has children or not.
+ */
+int
+mdb_hasSubordinates(
+ Operation *op,
+ Entry *e,
+ int *hasSubordinates )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ MDB_txn *rtxn;
+ mdb_op_info opinfo = {{{0}}}, *moi = &opinfo;
+ int rc;
+
+ assert( e != NULL );
+
+ rc = mdb_opinfo_get(op, mdb, 1, &moi);
+ switch(rc) {
+ case 0:
+ break;
+ default:
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ rtxn = moi->moi_txn;
+
+ rc = mdb_dn2id_children( op, rtxn, e );
+
+ switch( rc ) {
+ case 0:
+ *hasSubordinates = LDAP_COMPARE_TRUE;
+ break;
+
+ case MDB_NOTFOUND:
+ *hasSubordinates = LDAP_COMPARE_FALSE;
+ rc = LDAP_SUCCESS;
+ break;
+
+ default:
+ Debug(LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(mdb_hasSubordinates)
+ ": has_children failed: %s (%d)\n",
+ mdb_strerror(rc), rc );
+ rc = LDAP_OTHER;
+ }
+
+done:;
+ if ( moi == &opinfo ) {
+ mdb_txn_reset( moi->moi_txn );
+ LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
+ } else {
+ moi->moi_ref--;
+ }
+ return rc;
+}
+
+/*
+ * sets the supported operational attributes (if required)
+ */
+int
+mdb_operational(
+ Operation *op,
+ SlapReply *rs )
+{
+ Attribute **ap;
+
+ assert( rs->sr_entry != NULL );
+
+ for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next ) {
+ if ( (*ap)->a_desc == slap_schema.si_ad_hasSubordinates ) {
+ break;
+ }
+ }
+
+ if ( *ap == NULL &&
+ attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_hasSubordinates ) == NULL &&
+ ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
+ ad_inlist( slap_schema.si_ad_hasSubordinates, rs->sr_attrs ) ) )
+ {
+ int hasSubordinates, rc;
+
+ rc = mdb_hasSubordinates( op, rs->sr_entry, &hasSubordinates );
+ if ( rc == LDAP_SUCCESS ) {
+ *ap = slap_operational_hasSubordinate( hasSubordinates == LDAP_COMPARE_TRUE );
+ assert( *ap != NULL );
+
+ ap = &(*ap)->a_next;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
diff --git a/servers/slapd/back-mdb/proto-mdb.h b/servers/slapd/back-mdb/proto-mdb.h
new file mode 100644
index 0000000..25fdee7
--- /dev/null
+++ b/servers/slapd/back-mdb/proto-mdb.h
@@ -0,0 +1,411 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _PROTO_MDB_H
+#define _PROTO_MDB_H
+
+LDAP_BEGIN_DECL
+
+#define MDB_UCTYPE "MDB"
+
+/*
+ * attr.c
+ */
+
+AttrInfo *mdb_attr_mask( struct mdb_info *mdb,
+ AttributeDescription *desc );
+
+void mdb_attr_flush( struct mdb_info *mdb );
+
+int mdb_attr_slot( struct mdb_info *mdb,
+ AttributeDescription *desc, int *insert );
+
+int mdb_attr_dbs_open( BackendDB *be, MDB_txn *txn, struct config_reply_s *cr );
+void mdb_attr_dbs_close( struct mdb_info *mdb );
+
+int mdb_attr_index_config LDAP_P(( struct mdb_info *mdb,
+ const char *fname, int lineno,
+ int argc, char **argv, struct config_reply_s *cr ));
+
+void mdb_attr_index_unparse LDAP_P(( struct mdb_info *mdb, BerVarray *bva ));
+void mdb_attr_index_destroy LDAP_P(( struct mdb_info *mdb ));
+void mdb_attr_index_free LDAP_P(( struct mdb_info *mdb,
+ AttributeDescription *ad ));
+
+int mdb_attr_multi_config LDAP_P(( struct mdb_info *mdb,
+ const char *fname, int lineno,
+ int argc, char **argv, struct config_reply_s *cr ));
+
+void mdb_attr_multi_unparse LDAP_P(( struct mdb_info *mdb, BerVarray *bva ));
+
+void mdb_attr_multi_thresh LDAP_P(( struct mdb_info *mdb, AttributeDescription *ad,
+ unsigned *hi, unsigned *lo ));
+
+void mdb_attr_info_free( AttrInfo *ai );
+
+int mdb_ad_read( struct mdb_info *mdb, MDB_txn *txn );
+int mdb_ad_get( struct mdb_info *mdb, MDB_txn *txn, AttributeDescription *ad );
+void mdb_ad_unwind( struct mdb_info *mdb, int prev_ads );
+
+/*
+ * config.c
+ */
+
+int mdb_back_init_cf( BackendInfo *bi );
+
+/*
+ * dn2entry.c
+ */
+
+int mdb_dn2entry LDAP_P(( Operation *op, MDB_txn *tid, MDB_cursor *mc,
+ struct berval *dn, Entry **e, ID *nsubs, int matched ));
+
+/*
+ * dn2id.c
+ */
+
+int mdb_dn2id(
+ Operation *op,
+ MDB_txn *txn,
+ MDB_cursor *mc,
+ struct berval *ndn,
+ ID *id,
+ ID *nsubs,
+ struct berval *matched,
+ struct berval *nmatched );
+
+int mdb_dn2id_add(
+ Operation *op,
+ MDB_cursor *mcp,
+ MDB_cursor *mcd,
+ ID pid,
+ ID nsubs,
+ int upsub,
+ Entry *e );
+
+int mdb_dn2id_delete(
+ Operation *op,
+ MDB_cursor *mc,
+ ID id,
+ ID nsubs );
+
+int mdb_dn2id_children(
+ Operation *op,
+ MDB_txn *tid,
+ Entry *e );
+
+int mdb_dn2sups (
+ Operation *op,
+ MDB_txn *tid,
+ struct berval *dn,
+ ID *sups
+ );
+
+int mdb_dn2idl(
+ Operation *op,
+ MDB_txn *txn,
+ struct berval *ndn,
+ ID eid,
+ ID *ids,
+ ID *stack );
+
+int mdb_dn2id_parent(
+ Operation *op,
+ MDB_txn *txn,
+ ID eid,
+ ID *idp );
+
+int mdb_id2name(
+ Operation *op,
+ MDB_txn *txn,
+ MDB_cursor **cursp,
+ ID eid,
+ struct berval *name,
+ struct berval *nname);
+
+int mdb_idscope(
+ Operation *op,
+ MDB_txn *txn,
+ ID base,
+ ID *ids,
+ ID *res );
+
+struct IdScopes;
+
+int mdb_idscopes(
+ Operation *op,
+ struct IdScopes *isc );
+
+int mdb_idscopechk(
+ Operation *op,
+ struct IdScopes *isc );
+
+int mdb_dn2id_walk(
+ Operation *op,
+ struct IdScopes *isc );
+
+void mdb_dn2id_wrestore(
+ Operation *op,
+ struct IdScopes *isc );
+
+MDB_cmp_func mdb_dup_compare;
+
+/*
+ * filterentry.c
+ */
+
+int mdb_filter_candidates(
+ Operation *op,
+ MDB_txn *txn,
+ Filter *f,
+ ID *ids,
+ ID *tmp,
+ ID *stack );
+
+/*
+ * id2entry.c
+ */
+
+MDB_cmp_func mdb_id2v_compare;
+MDB_cmp_func mdb_id2v_dupsort;
+
+int mdb_id2entry_add(
+ Operation *op,
+ MDB_txn *tid,
+ MDB_cursor *mc,
+ Entry *e );
+
+int mdb_id2entry_update(
+ Operation *op,
+ MDB_txn *tid,
+ MDB_cursor *mc,
+ Entry *e );
+
+int mdb_id2entry_delete(
+ BackendDB *be,
+ MDB_txn *tid,
+ Entry *e);
+
+int mdb_id2entry(
+ Operation *op,
+ MDB_cursor *mc,
+ ID id,
+ Entry **e);
+
+int mdb_id2edata(
+ Operation *op,
+ MDB_cursor *mc,
+ ID id,
+ MDB_val *data);
+
+int mdb_entry_return( Operation *op, Entry *e );
+BI_entry_release_rw mdb_entry_release;
+BI_entry_get_rw mdb_entry_get;
+BI_op_txn mdb_txn;
+
+int mdb_entry_decode( Operation *op, MDB_txn *txn, MDB_val *data, ID id, Entry **e );
+
+void mdb_reader_flush( MDB_env *env );
+int mdb_opinfo_get( Operation *op, struct mdb_info *mdb, int rdonly, mdb_op_info **moi );
+
+int mdb_mval_put(Operation *op, MDB_cursor *mc, ID id, Attribute *a);
+int mdb_mval_del(Operation *op, MDB_cursor *mc, ID id, Attribute *a);
+
+/*
+ * idl.c
+ */
+
+unsigned mdb_idl_search( ID *ids, ID id );
+
+int mdb_idl_fetch_key(
+ BackendDB *be,
+ MDB_txn *txn,
+ MDB_dbi dbi,
+ MDB_val *key,
+ ID *ids,
+ MDB_cursor **saved_cursor,
+ int get_flag );
+
+int mdb_idl_insert( ID *ids, ID id );
+
+typedef int (mdb_idl_keyfunc)(
+ BackendDB *be,
+ MDB_cursor *mc,
+ struct berval *key,
+ ID id );
+
+mdb_idl_keyfunc mdb_idl_insert_keys;
+mdb_idl_keyfunc mdb_idl_delete_keys;
+
+int
+mdb_idl_intersection(
+ ID *a,
+ ID *b );
+
+int
+mdb_idl_union(
+ ID *a,
+ ID *b );
+
+ID mdb_idl_first( ID *ids, ID *cursor );
+ID mdb_idl_next( ID *ids, ID *cursor );
+
+void mdb_idl_sort( ID *ids, ID *tmp );
+int mdb_idl_append( ID *a, ID *b );
+int mdb_idl_append_one( ID *ids, ID id );
+
+
+/*
+ * index.c
+ */
+
+extern AttrInfo *
+mdb_index_mask LDAP_P((
+ Backend *be,
+ AttributeDescription *desc,
+ struct berval *name ));
+
+extern int
+mdb_index_param LDAP_P((
+ Backend *be,
+ AttributeDescription *desc,
+ int ftype,
+ MDB_dbi *dbi,
+ slap_mask_t *mask,
+ struct berval *prefix ));
+
+extern int
+mdb_index_values LDAP_P((
+ Operation *op,
+ MDB_txn *txn,
+ AttributeDescription *desc,
+ BerVarray vals,
+ ID id,
+ int opid ));
+
+extern int
+mdb_index_recset LDAP_P((
+ struct mdb_info *mdb,
+ Attribute *a,
+ AttributeType *type,
+ struct berval *tags,
+ IndexRec *ir ));
+
+extern int
+mdb_index_recrun LDAP_P((
+ Operation *op,
+ MDB_txn *txn,
+ struct mdb_info *mdb,
+ IndexRec *ir,
+ ID id,
+ int base ));
+
+int mdb_index_entry LDAP_P(( Operation *op, MDB_txn *t, int r, Entry *e ));
+
+#define mdb_index_entry_add(op,t,e) \
+ mdb_index_entry((op),(t),SLAP_INDEX_ADD_OP,(e))
+#define mdb_index_entry_del(op,t,e) \
+ mdb_index_entry((op),(t),SLAP_INDEX_DELETE_OP,(e))
+
+/*
+ * key.c
+ */
+
+extern int
+mdb_key_read(
+ Backend *be,
+ MDB_txn *txn,
+ MDB_dbi dbi,
+ struct berval *k,
+ ID *ids,
+ MDB_cursor **saved_cursor,
+ int get_flags );
+
+/*
+ * nextid.c
+ */
+
+int mdb_next_id( BackendDB *be, MDB_cursor *mc, ID *id );
+
+/*
+ * modify.c
+ */
+
+int mdb_modify_internal(
+ Operation *op,
+ MDB_txn *tid,
+ Modifications *modlist,
+ Entry *e,
+ const char **text,
+ char *textbuf,
+ size_t textlen );
+
+/*
+ * monitor.c
+ */
+
+int mdb_monitor_db_init( BackendDB *be );
+int mdb_monitor_db_open( BackendDB *be );
+int mdb_monitor_db_close( BackendDB *be );
+int mdb_monitor_db_destroy( BackendDB *be );
+
+#ifdef MDB_MONITOR_IDX
+int
+mdb_monitor_idx_add(
+ struct mdb_info *mdb,
+ AttributeDescription *desc,
+ slap_mask_t type );
+#endif /* MDB_MONITOR_IDX */
+
+/*
+ * former external.h
+ */
+
+extern BI_init mdb_back_initialize;
+
+extern BI_db_config mdb_db_config;
+
+extern BI_op_add mdb_add;
+extern BI_op_bind mdb_bind;
+extern BI_op_compare mdb_compare;
+extern BI_op_delete mdb_delete;
+extern BI_op_modify mdb_modify;
+extern BI_op_modrdn mdb_modrdn;
+extern BI_op_search mdb_search;
+extern BI_op_extended mdb_extended;
+
+extern BI_chk_referrals mdb_referrals;
+
+extern BI_operational mdb_operational;
+
+extern BI_has_subordinates mdb_hasSubordinates;
+
+/* tools.c */
+extern BI_tool_entry_open mdb_tool_entry_open;
+extern BI_tool_entry_close mdb_tool_entry_close;
+extern BI_tool_entry_first_x mdb_tool_entry_first_x;
+extern BI_tool_entry_next mdb_tool_entry_next;
+extern BI_tool_entry_get mdb_tool_entry_get;
+extern BI_tool_entry_put mdb_tool_entry_put;
+extern BI_tool_entry_reindex mdb_tool_entry_reindex;
+extern BI_tool_dn2id_get mdb_tool_dn2id_get;
+extern BI_tool_entry_modify mdb_tool_entry_modify;
+extern BI_tool_entry_delete mdb_tool_entry_delete;
+
+extern mdb_idl_keyfunc mdb_tool_idl_add;
+
+LDAP_END_DECL
+
+#endif /* _PROTO_MDB_H */
diff --git a/servers/slapd/back-mdb/referral.c b/servers/slapd/back-mdb/referral.c
new file mode 100644
index 0000000..3164890
--- /dev/null
+++ b/servers/slapd/back-mdb/referral.c
@@ -0,0 +1,151 @@
+/* referral.c - MDB backend referral handler */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+
+int
+mdb_referrals( Operation *op, SlapReply *rs )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ Entry *e = NULL;
+ int rc = LDAP_SUCCESS;
+
+ MDB_txn *rtxn;
+ mdb_op_info opinfo = {0}, *moi = &opinfo;
+
+ if( op->o_tag == LDAP_REQ_SEARCH ) {
+ /* let search take care of itself */
+ return rc;
+ }
+
+ if( get_manageDSAit( op ) ) {
+ /* let op take care of DSA management */
+ return rc;
+ }
+
+ rc = mdb_opinfo_get(op, mdb, 1, &moi);
+ switch(rc) {
+ case 0:
+ break;
+ default:
+ return LDAP_OTHER;
+ }
+
+ rtxn = moi->moi_txn;
+
+ /* get entry */
+ rc = mdb_dn2entry( op, rtxn, &op->o_req_ndn, &e, 1 );
+
+ switch(rc) {
+ case MDB_NOTFOUND:
+ case 0:
+ break;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto done;
+ default:
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_referrals)
+ ": dn2entry failed: %s (%d)\n",
+ mdb_strerror(rc), rc );
+ rs->sr_text = "internal error";
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ if ( rc == MDB_NOTFOUND ) {
+ rc = LDAP_SUCCESS;
+ rs->sr_matched = NULL;
+ if ( e != NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_referrals)
+ ": tag=%lu target=\"%s\" matched=\"%s\"\n",
+ (unsigned long)op->o_tag, op->o_req_dn.bv_val, e->e_name.bv_val );
+
+ if( is_entry_referral( e ) ) {
+ BerVarray ref = get_entry_referrals( op, e );
+ rc = LDAP_OTHER;
+ rs->sr_ref = referral_rewrite( ref, &e->e_name,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ if ( rs->sr_ref ) {
+ rs->sr_matched = ber_strdup_x(
+ e->e_name.bv_val, op->o_tmpmemctx );
+ }
+ }
+
+ mdb_entry_return( op, e );
+ e = NULL;
+ }
+
+ if( rs->sr_ref != NULL ) {
+ /* send referrals */
+ rc = rs->sr_err = LDAP_REFERRAL;
+ send_ldap_result( op, rs );
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ } else if ( rc != LDAP_SUCCESS ) {
+ rs->sr_text = rs->sr_matched ? "bad referral object" : NULL;
+ }
+
+ if (rs->sr_matched) {
+ op->o_tmpfree( (char *)rs->sr_matched, op->o_tmpmemctx );
+ rs->sr_matched = NULL;
+ }
+ goto done;
+ }
+
+ if ( is_entry_referral( e ) ) {
+ /* entry is a referral */
+ BerVarray refs = get_entry_referrals( op, e );
+ rs->sr_ref = referral_rewrite(
+ refs, &e->e_name, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_referrals)
+ ": tag=%lu target=\"%s\" matched=\"%s\"\n",
+ (unsigned long)op->o_tag, op->o_req_dn.bv_val, e->e_name.bv_val );
+
+ rs->sr_matched = e->e_name.bv_val;
+ if( rs->sr_ref != NULL ) {
+ rc = rs->sr_err = LDAP_REFERRAL;
+ send_ldap_result( op, rs );
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ } else {
+ rc = LDAP_OTHER;
+ rs->sr_text = "bad referral object";
+ }
+
+ rs->sr_matched = NULL;
+ ber_bvarray_free( refs );
+ }
+
+done:
+ if ( moi == &opinfo ) {
+ mdb_txn_reset( moi->moi_txn );
+ LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
+ } else {
+ moi->moi_ref--;
+ }
+ if ( e )
+ mdb_entry_return( op, e );
+ return rc;
+}
diff --git a/servers/slapd/back-mdb/search.c b/servers/slapd/back-mdb/search.c
new file mode 100644
index 0000000..0889597
--- /dev/null
+++ b/servers/slapd/back-mdb/search.c
@@ -0,0 +1,1543 @@
+/* search.c - search operation */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-mdb.h"
+#include "idl.h"
+
+static int base_candidate(
+ BackendDB *be,
+ Entry *e,
+ ID *ids );
+
+static int search_candidates(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ IdScopes *isc,
+ MDB_cursor *mci,
+ ID *ids,
+ ID *stack );
+
+static int parse_paged_cookie( Operation *op, SlapReply *rs );
+
+static void send_paged_response(
+ Operation *op,
+ SlapReply *rs,
+ ID *lastid,
+ int tentries );
+
+/* Dereference aliases for a single alias entry. Return the final
+ * dereferenced entry on success, NULL on any failure.
+ */
+static Entry * deref_base (
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ Entry **matched,
+ MDB_txn *txn,
+ ID *tmp,
+ ID *visited )
+{
+ struct berval ndn;
+
+ rs->sr_err = LDAP_ALIAS_DEREF_PROBLEM;
+ rs->sr_text = "maximum deref depth exceeded";
+
+ for (;;) {
+ /* Remember the last entry we looked at, so we can
+ * report broken links
+ */
+ *matched = e;
+
+ if (MDB_IDL_N(tmp) >= op->o_bd->be_max_deref_depth) {
+ e = NULL;
+ break;
+ }
+
+ /* If this is part of a subtree or onelevel search,
+ * have we seen this ID before? If so, quit.
+ */
+ if ( visited && mdb_idl_insert( visited, e->e_id ) ) {
+ e = NULL;
+ break;
+ }
+
+ /* If we've seen this ID during this deref iteration,
+ * we've hit a loop.
+ */
+ if ( mdb_idl_insert( tmp, e->e_id ) ) {
+ rs->sr_err = LDAP_ALIAS_PROBLEM;
+ rs->sr_text = "circular alias";
+ e = NULL;
+ break;
+ }
+
+ /* If there was a problem getting the aliasedObjectName,
+ * get_alias_dn will have set the error status.
+ */
+ if ( get_alias_dn(e, &ndn, &rs->sr_err, &rs->sr_text) ) {
+ e = NULL;
+ break;
+ }
+
+ rs->sr_err = mdb_dn2entry( op, txn, NULL, &ndn, &e, NULL, 0 );
+ if (rs->sr_err) {
+ rs->sr_err = LDAP_ALIAS_PROBLEM;
+ rs->sr_text = "aliasedObject not found";
+ break;
+ }
+
+ /* Free the previous entry, continue to work with the
+ * one we just retrieved.
+ */
+ mdb_entry_return( op, *matched );
+
+ /* We found a regular entry. Return this to the caller.
+ */
+ if (!is_entry_alias(e)) {
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ break;
+ }
+ }
+ return e;
+}
+
+/* Look for and dereference all aliases within the search scope.
+ * Requires "stack" to be able to hold 6 levels of DB_SIZE IDLs.
+ * Of course we're hardcoded to require a minimum of 8 UM_SIZE
+ * IDLs so this is never a problem.
+ */
+static int search_aliases(
+ Operation *op,
+ SlapReply *rs,
+ ID e_id,
+ IdScopes *isc,
+ MDB_cursor *mci,
+ ID *stack )
+{
+ ID *aliases, *curscop, *visited, *newsubs, *oldsubs, *tmp;
+ ID cursora, ida, cursoro, ido;
+ Entry *matched, *a;
+ struct berval bv_alias = BER_BVC( "alias" );
+ AttributeAssertion aa_alias = ATTRIBUTEASSERTION_INIT;
+ Filter af;
+
+ aliases = stack; /* IDL of all aliases in the database */
+ curscop = aliases + MDB_idl_db_size; /* Aliases in the current scope */
+ visited = curscop + MDB_idl_db_size; /* IDs we've seen in this search */
+ newsubs = visited + MDB_idl_db_size; /* New subtrees we've added */
+ oldsubs = newsubs + MDB_idl_db_size; /* Subtrees added previously */
+ tmp = oldsubs + MDB_idl_db_size; /* Scratch space for deref_base() */
+
+ af.f_choice = LDAP_FILTER_EQUALITY;
+ af.f_ava = &aa_alias;
+ af.f_av_desc = slap_schema.si_ad_objectClass;
+ af.f_av_value = bv_alias;
+ af.f_next = NULL;
+
+ /* Find all aliases in database */
+ MDB_IDL_ZERO( aliases );
+ rs->sr_err = mdb_filter_candidates( op, isc->mt, &af, aliases,
+ curscop, visited );
+ if (rs->sr_err != LDAP_SUCCESS || MDB_IDL_IS_ZERO( aliases )) {
+ return rs->sr_err;
+ }
+ if ( op->ors_limit /* isroot == FALSE */ &&
+ op->ors_limit->lms_s_unchecked != -1 &&
+ MDB_IDL_N( aliases ) > (unsigned) op->ors_limit->lms_s_unchecked )
+ {
+ return LDAP_ADMINLIMIT_EXCEEDED;
+ }
+ oldsubs[0] = 1;
+ oldsubs[1] = e_id;
+
+ MDB_IDL_ZERO( visited );
+ MDB_IDL_ZERO( newsubs );
+
+ cursoro = 0;
+ ido = mdb_idl_first( oldsubs, &cursoro );
+
+ for (;;) {
+ /* Set curscop to only the aliases in the current scope. Start with
+ * all the aliases, then get the intersection with the scope.
+ */
+ rs->sr_err = mdb_idscope( op, isc->mt, e_id, aliases, curscop );
+
+ /* Dereference all of the aliases in the current scope. */
+ cursora = 0;
+ for (ida = mdb_idl_first(curscop, &cursora); ida != NOID;
+ ida = mdb_idl_next(curscop, &cursora))
+ {
+ rs->sr_err = mdb_id2entry(op, mci, ida, &a);
+ if (rs->sr_err != LDAP_SUCCESS) {
+ continue;
+ }
+
+ /* This should only happen if the curscop IDL has maxed out and
+ * turned into a range that spans IDs indiscriminately
+ */
+ if (!is_entry_alias(a)) {
+ mdb_entry_return(op, a);
+ continue;
+ }
+
+ /* Actually dereference the alias */
+ MDB_IDL_ZERO(tmp);
+ a = deref_base( op, rs, a, &matched, isc->mt,
+ tmp, visited );
+ if (a) {
+ /* If the target was not already in our current scopes,
+ * make note of it in the newsubs list.
+ */
+ ID2 mid;
+ mid.mid = a->e_id;
+ mid.mval.mv_data = NULL;
+ if (op->ors_scope == LDAP_SCOPE_SUBTREE) {
+ isc->id = a->e_id;
+ /* if ID is a child of any of our current scopes,
+ * ignore it, it's already included.
+ */
+ if (mdb_idscopechk(op, isc))
+ goto skip;
+ }
+ if (mdb_id2l_insert(isc->scopes, &mid) == 0) {
+ mdb_idl_insert(newsubs, a->e_id);
+ }
+skip: mdb_entry_return( op, a );
+
+ } else if (matched) {
+ /* Alias could not be dereferenced, or it deref'd to
+ * an ID we've already seen. Ignore it.
+ */
+ mdb_entry_return( op, matched );
+ rs->sr_text = NULL;
+ rs->sr_err = 0;
+ }
+ }
+ /* If this is a OneLevel search, we're done; oldsubs only had one
+ * ID in it. For a Subtree search, oldsubs may be a list of scope IDs.
+ */
+ if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ) break;
+nextido:
+ ido = mdb_idl_next( oldsubs, &cursoro );
+
+ /* If we're done processing the old scopes, did we add any new
+ * scopes in this iteration? If so, go back and do those now.
+ */
+ if (ido == NOID) {
+ if (MDB_IDL_IS_ZERO(newsubs)) break;
+ MDB_IDL_CPY(oldsubs, newsubs);
+ MDB_IDL_ZERO(newsubs);
+ cursoro = 0;
+ ido = mdb_idl_first( oldsubs, &cursoro );
+ }
+
+ /* Find the entry corresponding to the next scope. If it can't
+ * be found, ignore it and move on. This should never happen;
+ * we should never see the ID of an entry that doesn't exist.
+ */
+ {
+ MDB_val edata;
+ rs->sr_err = mdb_id2edata(op, mci, ido, &edata);
+ if ( rs->sr_err != MDB_SUCCESS ) {
+ goto nextido;
+ }
+ e_id = ido;
+ }
+ }
+ return rs->sr_err;
+}
+
+/* Get the next ID from the DB. Used if the candidate list is
+ * a range and simple iteration hits missing entryIDs
+ */
+static int
+mdb_get_nextid(MDB_cursor *mci, ID *cursor)
+{
+ MDB_val key;
+ ID id;
+ int rc;
+
+ id = *cursor + 1;
+ key.mv_data = &id;
+ key.mv_size = sizeof(ID);
+ rc = mdb_cursor_get( mci, &key, NULL, MDB_SET_RANGE );
+ if ( rc )
+ return rc;
+ memcpy( cursor, key.mv_data, sizeof(ID));
+ return 0;
+}
+
+static void scope_chunk_free( void *key, void *data )
+{
+ ID2 *p1, *p2;
+ for (p1 = data; p1; p1 = p2) {
+ p2 = p1[0].mval.mv_data;
+ ber_memfree_x(p1, NULL);
+ }
+}
+
+static ID2 *scope_chunk_get( Operation *op )
+{
+ ID2 *ret = NULL;
+
+ ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)scope_chunk_get,
+ (void *)&ret, NULL );
+ if ( !ret ) {
+ ret = ch_malloc( MDB_idl_um_size * sizeof( ID2 ));
+ } else {
+ void *r2 = ret[0].mval.mv_data;
+ ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)scope_chunk_get,
+ r2, scope_chunk_free, NULL, NULL );
+ }
+ return ret;
+}
+
+static void scope_chunk_ret( Operation *op, ID2 *scopes )
+{
+ void *ret = NULL;
+
+ ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)scope_chunk_get,
+ &ret, NULL );
+ scopes[0].mval.mv_data = ret;
+ ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)scope_chunk_get,
+ (void *)scopes, scope_chunk_free, NULL, NULL );
+}
+
+static void *search_stack( Operation *op );
+
+typedef struct ww_ctx {
+ MDB_txn *txn;
+ MDB_cursor *mcd; /* if set, save cursor context */
+ ID key;
+ MDB_val data;
+ int flag;
+ unsigned nentries;
+} ww_ctx;
+
+/* ITS#7904 if we get blocked while writing results to client,
+ * release the current reader txn and reacquire it after we
+ * unblock.
+ * Slight problem - if we're doing a scope-based walk (mdb_dn2id_walk)
+ * to return results, we need to remember the state of the mcd cursor.
+ * If the node that cursor was pointing to gets deleted while we're
+ * blocked, we may be unable to restore the cursor position. In that
+ * case return an LDAP_BUSY error - let the client know this search
+ * couldn't succeed, but might succeed on a retry.
+ */
+static void
+mdb_rtxn_snap( Operation *op, ww_ctx *ww )
+{
+ /* save cursor position and release read txn */
+ if ( ww->mcd ) {
+ MDB_val key, data;
+ mdb_cursor_get( ww->mcd, &key, &data, MDB_GET_CURRENT );
+ memcpy( &ww->key, key.mv_data, sizeof(ID) );
+ ww->data.mv_size = data.mv_size;
+ ww->data.mv_data = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx );
+ memcpy(ww->data.mv_data, data.mv_data, data.mv_size);
+ }
+ mdb_txn_reset( ww->txn );
+ ww->flag = 1;
+}
+
+static void
+mdb_writewait( Operation *op, slap_callback *sc )
+{
+ ww_ctx *ww = sc->sc_private;
+ if ( !ww->flag ) {
+ mdb_rtxn_snap( op, ww );
+ }
+}
+
+static int
+mdb_waitfixup( Operation *op, ww_ctx *ww, MDB_cursor *mci, MDB_cursor *mcd, IdScopes *isc )
+{
+ MDB_val key;
+ int rc = 0;
+ ww->flag = 0;
+ mdb_txn_renew( ww->txn );
+ mdb_cursor_renew( ww->txn, mci );
+ mdb_cursor_renew( ww->txn, mcd );
+
+ key.mv_size = sizeof(ID);
+ if ( ww->mcd ) { /* scope-based search using dn2id_walk */
+ MDB_val data;
+
+ if ( isc->numrdns )
+ mdb_dn2id_wrestore( op, isc );
+
+ key.mv_data = &ww->key;
+ data = ww->data;
+ rc = mdb_cursor_get( mcd, &key, &data, MDB_GET_BOTH );
+ if ( rc == MDB_NOTFOUND ) {
+ data = ww->data;
+ rc = mdb_cursor_get( mcd, &key, &data, MDB_GET_BOTH_RANGE );
+ /* the loop will skip this node using NEXT_DUP but we want it
+ * sent, so go back one space first
+ */
+ if ( rc == MDB_SUCCESS )
+ mdb_cursor_get( mcd, &key, &data, MDB_PREV_DUP );
+ else
+ rc = LDAP_BUSY;
+ } else if ( rc ) {
+ rc = LDAP_OTHER;
+ }
+ op->o_tmpfree( ww->data.mv_data, op->o_tmpmemctx );
+ ww->data.mv_data = NULL;
+ } else if ( isc->scopes[0].mid > 1 ) { /* candidate-based search */
+ int i;
+ for ( i=1; i<=isc->scopes[0].mid; i++ ) {
+ if ( !isc->scopes[i].mval.mv_data )
+ continue;
+ key.mv_data = &isc->scopes[i].mid;
+ mdb_cursor_get( mcd, &key, &isc->scopes[i].mval, MDB_SET );
+ }
+ }
+ return rc;
+}
+
+int
+mdb_search( Operation *op, SlapReply *rs )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ ID id, cursor, nsubs, ncand, cscope;
+ ID lastid = NOID;
+ ID *candidates, *iscopes, *c0;
+ ID2 *scopes;
+ void *stack;
+ Entry *e = NULL, *base = NULL;
+ Entry *matched = NULL;
+ AttributeName *attrs;
+ slap_mask_t mask;
+ time_t stoptime;
+ int manageDSAit;
+ int tentries = 0;
+ int admincheck = 0;
+ IdScopes isc;
+ MDB_cursor *mci, *mcd;
+ ww_ctx wwctx;
+ slap_callback cb = { 0 };
+
+ mdb_op_info opinfo = {{{0}}}, *moi = &opinfo;
+ MDB_txn *ltid = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(mdb_search) "\n" );
+ attrs = op->oq_search.rs_attrs;
+
+ manageDSAit = get_manageDSAit( op );
+
+ rs->sr_err = mdb_opinfo_get( op, mdb, 1, &moi );
+ switch(rs->sr_err) {
+ case 0:
+ break;
+ default:
+ send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
+ return rs->sr_err;
+ }
+
+ ltid = moi->moi_txn;
+
+ rs->sr_err = mdb_cursor_open( ltid, mdb->mi_id2entry, &mci );
+ if ( rs->sr_err ) {
+ send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
+ return rs->sr_err;
+ }
+
+ rs->sr_err = mdb_cursor_open( ltid, mdb->mi_dn2id, &mcd );
+ if ( rs->sr_err ) {
+ mdb_cursor_close( mci );
+ send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
+ return rs->sr_err;
+ }
+
+ scopes = scope_chunk_get( op );
+ candidates = c0 = search_stack( op );
+ iscopes = candidates + MDB_idl_um_size;
+ stack = iscopes + MDB_idl_db_size;
+ /* if candidates already in use, alloc a new array */
+ if ( c0[0] ) {
+ candidates = ch_malloc(( MDB_idl_um_size + MDB_idl_db_size ) * sizeof ( ID ));
+ iscopes = candidates + MDB_idl_um_size;
+ }
+ isc.mt = ltid;
+ isc.mc = mcd;
+ isc.scopes = scopes;
+ isc.oscope = op->ors_scope;
+ isc.sctmp = stack;
+
+ if ( op->ors_deref & LDAP_DEREF_FINDING ) {
+ MDB_IDL_ZERO(candidates);
+ }
+dn2entry_retry:
+ /* get entry with reader lock */
+ rs->sr_err = mdb_dn2entry( op, ltid, mcd, &op->o_req_ndn, &e, &nsubs, 1 );
+
+ switch(rs->sr_err) {
+ case MDB_NOTFOUND:
+ matched = e;
+ e = NULL;
+ break;
+ case 0:
+ break;
+ case LDAP_BUSY:
+ send_ldap_error( op, rs, LDAP_BUSY, "ldap server busy" );
+ goto done;
+ default:
+ send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
+ goto done;
+ }
+
+ if ( op->ors_deref & LDAP_DEREF_FINDING ) {
+ if ( matched && is_entry_alias( matched )) {
+ struct berval stub;
+
+ stub.bv_val = op->o_req_ndn.bv_val;
+ stub.bv_len = op->o_req_ndn.bv_len - matched->e_nname.bv_len - 1;
+ e = deref_base( op, rs, matched, &matched, ltid,
+ candidates, NULL );
+ if ( e ) {
+ build_new_dn( &op->o_req_ndn, &e->e_nname, &stub,
+ op->o_tmpmemctx );
+ mdb_entry_return(op, e);
+ matched = NULL;
+ goto dn2entry_retry;
+ }
+ } else if ( e && is_entry_alias( e )) {
+ e = deref_base( op, rs, e, &matched, ltid,
+ candidates, NULL );
+ }
+ }
+
+ if ( e == NULL ) {
+ struct berval matched_dn = BER_BVNULL;
+
+ if ( matched != NULL ) {
+ BerVarray erefs = NULL;
+
+ /* return referral only if "disclose"
+ * is granted on the object */
+ if ( ! access_allowed( op, matched,
+ slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL ) )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+
+ } else {
+ ber_dupbv( &matched_dn, &matched->e_name );
+
+ erefs = is_entry_referral( matched )
+ ? get_entry_referrals( op, matched )
+ : NULL;
+ if ( rs->sr_err == MDB_NOTFOUND )
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_matched = matched_dn.bv_val;
+ }
+
+ mdb_entry_return(op, matched);
+ matched = NULL;
+
+ if ( erefs ) {
+ rs->sr_ref = referral_rewrite( erefs, &matched_dn,
+ &op->o_req_dn, op->oq_search.rs_scope );
+ ber_bvarray_free( erefs );
+ }
+
+ } else {
+ rs->sr_ref = referral_rewrite( default_referral,
+ NULL, &op->o_req_dn, op->oq_search.rs_scope );
+ rs->sr_err = rs->sr_ref != NULL ? LDAP_REFERRAL : LDAP_NO_SUCH_OBJECT;
+ }
+
+ send_ldap_result( op, rs );
+
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+ if ( !BER_BVISNULL( &matched_dn ) ) {
+ ber_memfree( matched_dn.bv_val );
+ rs->sr_matched = NULL;
+ }
+ goto done;
+ }
+
+ /* NOTE: __NEW__ "search" access is required
+ * on searchBase object */
+ if ( ! access_allowed_mask( op, e, slap_schema.si_ad_entry,
+ NULL, ACL_SEARCH, NULL, &mask ) )
+ {
+ if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ } else {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ }
+
+ mdb_entry_return( op,e);
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ if ( !manageDSAit && is_entry_referral( e ) ) {
+ /* entry is a referral */
+ struct berval matched_dn = BER_BVNULL;
+ BerVarray erefs = NULL;
+
+ ber_dupbv( &matched_dn, &e->e_name );
+ erefs = get_entry_referrals( op, e );
+
+ rs->sr_err = LDAP_REFERRAL;
+
+ mdb_entry_return( op, e );
+ e = NULL;
+
+ if ( erefs ) {
+ rs->sr_ref = referral_rewrite( erefs, &matched_dn,
+ &op->o_req_dn, op->oq_search.rs_scope );
+ ber_bvarray_free( erefs );
+
+ if ( !rs->sr_ref ) {
+ rs->sr_text = "bad_referral object";
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_search) ": entry is referral\n" );
+
+ rs->sr_matched = matched_dn.bv_val;
+ send_ldap_result( op, rs );
+
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ ber_memfree( matched_dn.bv_val );
+ rs->sr_matched = NULL;
+ goto done;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ mdb_entry_return( op,e);
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ /* compute it anyway; root does not use it */
+ stoptime = op->o_time + op->ors_tlimit;
+
+ base = e;
+
+ e = NULL;
+
+ /* select candidates */
+ if ( op->oq_search.rs_scope == LDAP_SCOPE_BASE ) {
+ rs->sr_err = base_candidate( op->o_bd, base, candidates );
+ scopes[0].mid = 0;
+ ncand = 1;
+ } else {
+ if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ) {
+ size_t nkids;
+ MDB_val key, data;
+ key.mv_data = &base->e_id;
+ key.mv_size = sizeof( ID );
+ mdb_cursor_get( mcd, &key, &data, MDB_SET );
+ mdb_cursor_count( mcd, &nkids );
+ nsubs = nkids - 1;
+ } else if ( !base->e_id ) {
+ /* we don't maintain nsubs for entryID 0.
+ * just grab entry count from id2entry stat
+ */
+ MDB_stat ms;
+ mdb_stat( ltid, mdb->mi_id2entry, &ms );
+ nsubs = ms.ms_entries;
+ }
+ MDB_IDL_ZERO( candidates );
+ scopes[0].mid = 1;
+ scopes[1].mid = base->e_id;
+ scopes[1].mval.mv_data = NULL;
+ rs->sr_err = search_candidates( op, rs, base,
+ &isc, mci, candidates, stack );
+
+ if ( rs->sr_err == LDAP_ADMINLIMIT_EXCEEDED ) {
+adminlimit:
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ goto done;
+ }
+
+ ncand = MDB_IDL_N( candidates );
+ if ( !base->e_id || ncand == NOID ) {
+ /* grab entry count from id2entry stat
+ */
+ MDB_stat ms;
+ mdb_stat( ltid, mdb->mi_id2entry, &ms );
+ if ( !base->e_id )
+ nsubs = ms.ms_entries;
+ if ( ncand == NOID )
+ ncand = ms.ms_entries;
+ }
+ }
+
+ /* start cursor at beginning of candidates.
+ */
+ cursor = 0;
+
+ if ( candidates[0] == 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_search) ": no candidates\n" );
+
+ goto nochange;
+ }
+
+ /* if not root and candidates exceed to-be-checked entries, abort */
+ if ( op->ors_limit /* isroot == FALSE */ &&
+ op->ors_limit->lms_s_unchecked != -1 &&
+ ncand > (unsigned) op->ors_limit->lms_s_unchecked )
+ {
+ admincheck = 1;
+ }
+
+ if ( op->ors_limit == NULL /* isroot == TRUE */ ||
+ !op->ors_limit->lms_s_pr_hide )
+ {
+ tentries = ncand;
+ }
+
+ wwctx.flag = 0;
+ wwctx.nentries = 0;
+ /* If we're running in our own read txn */
+ if ( moi == &opinfo ) {
+ cb.sc_writewait = mdb_writewait;
+ cb.sc_private = &wwctx;
+ wwctx.txn = ltid;
+ wwctx.mcd = NULL;
+ cb.sc_next = op->o_callback;
+ op->o_callback = &cb;
+ }
+
+ if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
+ PagedResultsState *ps = op->o_pagedresults_state;
+ /* deferred cookie parsing */
+ rs->sr_err = parse_paged_cookie( op, rs );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ cursor = (ID) ps->ps_cookie;
+ if ( cursor && ps->ps_size == 0 ) {
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = "search abandoned by pagedResult size=0";
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ if ( admincheck )
+ goto adminlimit;
+
+ id = mdb_idl_first( candidates, &cursor );
+ if ( id == NOID ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_search)
+ ": no paged results candidates\n" );
+ send_paged_response( op, rs, &lastid, 0 );
+
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+ if ( id == (ID)ps->ps_cookie )
+ id = mdb_idl_next( candidates, &cursor );
+ nsubs = ncand; /* always bypass scope'd search */
+ goto loop_begin;
+ }
+ if ( nsubs < ncand ) {
+ int rc;
+ /* Do scope-based search */
+ if ( admincheck && nsubs > (unsigned) op->ors_limit->lms_s_unchecked )
+ goto adminlimit;
+
+ /* if any alias scopes were set, save them */
+ if (scopes[0].mid > 1) {
+ cursor = 1;
+ for (cscope = 1; cscope <= scopes[0].mid; cscope++) {
+ /* Ignore the original base */
+ if (scopes[cscope].mid == base->e_id)
+ continue;
+ iscopes[cursor++] = scopes[cscope].mid;
+ }
+ iscopes[0] = scopes[0].mid - 1;
+ } else {
+ iscopes[0] = 0;
+ }
+
+ wwctx.mcd = mcd;
+ isc.id = base->e_id;
+ isc.numrdns = 0;
+ rc = mdb_dn2id_walk( op, &isc );
+ if ( rc )
+ id = NOID;
+ else
+ id = isc.id;
+ cscope = 0;
+ } else {
+ if ( admincheck )
+ goto adminlimit;
+ id = mdb_idl_first( candidates, &cursor );
+ }
+
+ while (id != NOID)
+ {
+ int scopeok;
+ MDB_val edata;
+
+loop_begin:
+
+ /* check for abandon */
+ if ( op->o_abandon ) {
+ rs->sr_err = SLAPD_ABANDON;
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ /* mostly needed by internal searches,
+ * e.g. related to syncrepl, for whom
+ * abandon does not get set... */
+ if ( slapd_shutdown ) {
+ rs->sr_err = LDAP_UNAVAILABLE;
+ send_ldap_disconnect( op, rs );
+ goto done;
+ }
+
+ /* check time limit */
+ if ( op->ors_tlimit != SLAP_NO_LIMIT
+ && slap_get_time() > stoptime )
+ {
+ rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
+ rs->sr_ref = rs->sr_v2ref;
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ goto done;
+ }
+
+
+ if ( nsubs < ncand ) {
+ unsigned i;
+ /* Is this entry in the candidate list? */
+ scopeok = 0;
+ if (MDB_IDL_IS_RANGE( candidates )) {
+ if ( id >= MDB_IDL_RANGE_FIRST( candidates ) &&
+ id <= MDB_IDL_RANGE_LAST( candidates ))
+ scopeok = 1;
+ } else {
+ i = mdb_idl_search( candidates, id );
+ if (i <= candidates[0] && candidates[i] == id )
+ scopeok = 1;
+ }
+ if ( scopeok )
+ goto scopeok;
+ goto loop_continue;
+ }
+
+ /* Does this candidate actually satisfy the search scope?
+ */
+ scopeok = 0;
+ isc.numrdns = 0;
+ switch( op->ors_scope ) {
+ case LDAP_SCOPE_BASE:
+ /* This is always true, yes? */
+ if ( id == base->e_id ) scopeok = 1;
+ break;
+
+#ifdef LDAP_SCOPE_CHILDREN
+ case LDAP_SCOPE_CHILDREN:
+ if ( id == base->e_id ) break;
+ /* Fall-thru */
+#endif
+ case LDAP_SCOPE_SUBTREE:
+ if ( id == base->e_id ) {
+ scopeok = 1;
+ break;
+ }
+ /* Fall-thru */
+ case LDAP_SCOPE_ONELEVEL:
+ if ( id == base->e_id ) break;
+ isc.id = id;
+ isc.nscope = 0;
+ rs->sr_err = mdb_idscopes( op, &isc );
+ if ( rs->sr_err == MDB_SUCCESS ) {
+ if ( isc.nscope )
+ scopeok = 1;
+ } else {
+ if ( rs->sr_err == MDB_NOTFOUND )
+ goto notfound;
+ }
+ break;
+ }
+
+ /* Not in scope, ignore it */
+ if ( !scopeok )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_search)
+ ": %ld scope not okay\n",
+ (long) id );
+ goto loop_continue;
+ }
+
+scopeok:
+ if ( id == base->e_id ) {
+ e = base;
+ } else {
+
+ /* get the entry */
+ rs->sr_err = mdb_id2edata( op, mci, id, &edata );
+ if ( rs->sr_err == MDB_NOTFOUND ) {
+notfound:
+ if( nsubs < ncand )
+ goto loop_continue;
+
+ if( !MDB_IDL_IS_RANGE(candidates) ) {
+ /* only complain for non-range IDLs */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_search)
+ ": candidate %ld not found\n",
+ (long) id );
+ } else {
+ /* get the next ID from the DB */
+ rs->sr_err = mdb_get_nextid( mci, &cursor );
+ if ( rs->sr_err == MDB_NOTFOUND ) {
+ break;
+ }
+ if ( rs->sr_err ) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error in get_nextid";
+ send_ldap_result( op, rs );
+ goto done;
+ }
+ cursor--;
+ }
+
+ goto loop_continue;
+ } else if ( rs->sr_err ) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error in mdb_id2edata";
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ rs->sr_err = mdb_entry_decode( op, ltid, &edata, id, &e );
+ if ( rs->sr_err ) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error in mdb_entry_decode";
+ send_ldap_result( op, rs );
+ goto done;
+ }
+ e->e_id = id;
+ e->e_name.bv_val = NULL;
+ e->e_nname.bv_val = NULL;
+ }
+
+ if ( is_entry_subentry( e ) ) {
+ if( op->oq_search.rs_scope != LDAP_SCOPE_BASE ) {
+ if(!get_subentries_visibility( op )) {
+ /* only subentries are visible */
+ goto loop_continue;
+ }
+
+ } else if ( get_subentries( op ) &&
+ !get_subentries_visibility( op ))
+ {
+ /* only subentries are visible */
+ goto loop_continue;
+ }
+
+ } else if ( get_subentries_visibility( op )) {
+ /* only subentries are visible */
+ goto loop_continue;
+ }
+
+ /* aliases were already dereferenced in candidate list */
+ if ( op->ors_deref & LDAP_DEREF_SEARCHING ) {
+ /* but if the search base is an alias, and we didn't
+ * deref it when finding, return it.
+ */
+ if ( is_entry_alias(e) &&
+ ((op->ors_deref & LDAP_DEREF_FINDING) || e != base ))
+ {
+ goto loop_continue;
+ }
+ }
+
+ if ( !manageDSAit && is_entry_glue( e )) {
+ goto loop_continue;
+ }
+
+ if (e != base) {
+ struct berval pdn, pndn;
+ char *d, *n;
+ int i;
+
+ /* child of base, just append RDNs to base->e_name */
+ if ( nsubs < ncand || isc.scopes[isc.nscope].mid == base->e_id ) {
+ pdn = base->e_name;
+ pndn = base->e_nname;
+ } else {
+ mdb_id2name( op, ltid, &isc.mc, scopes[isc.nscope].mid, &pdn, &pndn );
+ }
+ e->e_name.bv_len = pdn.bv_len;
+ e->e_nname.bv_len = pndn.bv_len;
+ for (i=0; i<isc.numrdns; i++) {
+ e->e_name.bv_len += isc.rdns[i].bv_len + 1;
+ e->e_nname.bv_len += isc.nrdns[i].bv_len + 1;
+ }
+ e->e_name.bv_val = op->o_tmpalloc(e->e_name.bv_len + 1, op->o_tmpmemctx);
+ e->e_nname.bv_val = op->o_tmpalloc(e->e_nname.bv_len + 1, op->o_tmpmemctx);
+ d = e->e_name.bv_val;
+ n = e->e_nname.bv_val;
+ if (nsubs < ncand) {
+ /* RDNs are in top-down order */
+ for (i=isc.numrdns-1; i>=0; i--) {
+ memcpy(d, isc.rdns[i].bv_val, isc.rdns[i].bv_len);
+ d += isc.rdns[i].bv_len;
+ *d++ = ',';
+ memcpy(n, isc.nrdns[i].bv_val, isc.nrdns[i].bv_len);
+ n += isc.nrdns[i].bv_len;
+ *n++ = ',';
+ }
+ } else {
+ /* RDNs are in bottom-up order */
+ for (i=0; i<isc.numrdns; i++) {
+ memcpy(d, isc.rdns[i].bv_val, isc.rdns[i].bv_len);
+ d += isc.rdns[i].bv_len;
+ *d++ = ',';
+ memcpy(n, isc.nrdns[i].bv_val, isc.nrdns[i].bv_len);
+ n += isc.nrdns[i].bv_len;
+ *n++ = ',';
+ }
+ }
+
+ if (pdn.bv_len) {
+ memcpy(d, pdn.bv_val, pdn.bv_len+1);
+ memcpy(n, pndn.bv_val, pndn.bv_len+1);
+ } else {
+ *--d = '\0';
+ *--n = '\0';
+ e->e_name.bv_len--;
+ e->e_nname.bv_len--;
+ }
+ if (pndn.bv_val != base->e_nname.bv_val) {
+ op->o_tmpfree(pndn.bv_val, op->o_tmpmemctx);
+ op->o_tmpfree(pdn.bv_val, op->o_tmpmemctx);
+ }
+ }
+
+ /*
+ * if it's a referral, add it to the list of referrals. only do
+ * this for non-base searches, and don't check the filter
+ * explicitly here since it's only a candidate anyway.
+ */
+ if ( !manageDSAit && op->oq_search.rs_scope != LDAP_SCOPE_BASE
+ && is_entry_referral( e ) )
+ {
+ BerVarray erefs = get_entry_referrals( op, e );
+ rs->sr_ref = referral_rewrite( erefs, &e->e_name, NULL,
+ op->oq_search.rs_scope == LDAP_SCOPE_ONELEVEL
+ ? LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
+
+ rs->sr_entry = e;
+ rs->sr_flags = 0;
+
+ send_search_reference( op, rs );
+
+ if (e != base)
+ mdb_entry_return( op, e );
+ rs->sr_entry = NULL;
+ e = NULL;
+
+ ber_bvarray_free( rs->sr_ref );
+ ber_bvarray_free( erefs );
+ rs->sr_ref = NULL;
+
+ goto loop_continue;
+ }
+
+ /* if it matches the filter and scope, send it */
+ rs->sr_err = test_filter( op, e, op->oq_search.rs_filter );
+
+ if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
+ /* check size limit */
+ if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
+ if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size ) {
+ if (e != base)
+ mdb_entry_return( op, e );
+ e = NULL;
+ send_paged_response( op, rs, &lastid, tentries );
+ goto done;
+ }
+ lastid = id;
+ }
+
+ if (e) {
+ /* safe default */
+ rs->sr_attrs = op->oq_search.rs_attrs;
+ rs->sr_operational_attrs = NULL;
+ rs->sr_ctrls = NULL;
+ rs->sr_entry = e;
+ RS_ASSERT( e->e_private != NULL );
+ rs->sr_flags = 0;
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_err = send_search_entry( op, rs );
+ rs->sr_attrs = NULL;
+ rs->sr_entry = NULL;
+ if (e != base)
+ mdb_entry_return( op, e );
+ e = NULL;
+
+ switch ( rs->sr_err ) {
+ case LDAP_SUCCESS: /* entry sent ok */
+ break;
+ default: /* entry not sent */
+ break;
+ case LDAP_BUSY:
+ send_ldap_result( op, rs );
+ goto done;
+ case LDAP_UNAVAILABLE:
+ case LDAP_SIZELIMIT_EXCEEDED:
+ if ( rs->sr_err == LDAP_SIZELIMIT_EXCEEDED ) {
+ rs->sr_ref = rs->sr_v2ref;
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+
+ } else {
+ rs->sr_err = LDAP_OTHER;
+ }
+ goto done;
+ }
+ }
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(mdb_search)
+ ": %ld does not match filter\n",
+ (long) id );
+ }
+
+loop_continue:
+ if ( moi == &opinfo && !wwctx.flag && mdb->mi_rtxn_size ) {
+ wwctx.nentries++;
+ if ( wwctx.nentries >= mdb->mi_rtxn_size ) {
+ MDB_envinfo ei;
+ wwctx.nentries = 0;
+ mdb_env_info(mdb->mi_dbenv, &ei);
+ if ( ei.me_last_txnid > mdb_txn_id( ltid ))
+ mdb_rtxn_snap( op, &wwctx );
+ }
+ }
+ if ( wwctx.flag ) {
+ rs->sr_err = mdb_waitfixup( op, &wwctx, mci, mcd, &isc );
+ if ( rs->sr_err ) {
+ send_ldap_result( op, rs );
+ goto done;
+ }
+ }
+
+ if( e != NULL ) {
+ if ( e != base )
+ mdb_entry_return( op, e );
+ RS_ASSERT( rs->sr_entry == NULL );
+ e = NULL;
+ rs->sr_entry = NULL;
+ }
+
+ if ( nsubs < ncand ) {
+ int rc = mdb_dn2id_walk( op, &isc );
+ if (rc) {
+ id = NOID;
+ /* We got to the end of a subtree. If there are any
+ * alias scopes left, search them too.
+ */
+ while (iscopes[0] && cscope < iscopes[0]) {
+ cscope++;
+ isc.id = iscopes[cscope];
+ if ( base )
+ mdb_entry_return( op, base );
+ rs->sr_err = mdb_id2entry(op, mci, isc.id, &base);
+ if ( !rs->sr_err ) {
+ mdb_id2name( op, ltid, &isc.mc, isc.id, &base->e_name, &base->e_nname );
+ isc.numrdns = 0;
+ if (isc.oscope == LDAP_SCOPE_ONELEVEL)
+ isc.oscope = LDAP_SCOPE_BASE;
+ rc = mdb_dn2id_walk( op, &isc );
+ if ( !rc ) {
+ id = isc.id;
+ break;
+ }
+ }
+ }
+ } else
+ id = isc.id;
+ } else {
+ id = mdb_idl_next( candidates, &cursor );
+ }
+ }
+
+nochange:
+ rs->sr_ctrls = NULL;
+ rs->sr_ref = rs->sr_v2ref;
+ rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS : LDAP_REFERRAL;
+ rs->sr_rspoid = NULL;
+ if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
+ send_paged_response( op, rs, NULL, 0 );
+ } else {
+ send_ldap_result( op, rs );
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+
+done:
+ if ( cb.sc_private ) {
+ /* remove our writewait callback */
+ slap_callback **scp = &op->o_callback;
+ while ( *scp ) {
+ if ( *scp == &cb ) {
+ *scp = cb.sc_next;
+ cb.sc_private = NULL;
+ break;
+ }
+ }
+ }
+ mdb_cursor_close( mcd );
+ mdb_cursor_close( mci );
+ if ( moi == &opinfo ) {
+ mdb_txn_reset( moi->moi_txn );
+ LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
+ } else {
+ moi->moi_ref--;
+ }
+ if( rs->sr_v2ref ) {
+ ber_bvarray_free( rs->sr_v2ref );
+ rs->sr_v2ref = NULL;
+ }
+ if (base)
+ mdb_entry_return( op, base );
+ scope_chunk_ret( op, scopes );
+ if ( candidates != c0 ) {
+ ch_free( candidates );
+ } else {
+ MDB_IDL_ZERO( candidates );
+ }
+
+ return rs->sr_err;
+}
+
+
+static int base_candidate(
+ BackendDB *be,
+ Entry *e,
+ ID *ids )
+{
+ Debug(LDAP_DEBUG_ARGS, "base_candidates: base: \"%s\" (0x%08lx)\n",
+ e->e_nname.bv_val, (long) e->e_id );
+
+ ids[0] = 1;
+ ids[1] = e->e_id;
+ return 0;
+}
+
+/* Look for "objectClass Present" in this filter.
+ * Also count depth of filter tree while we're at it.
+ */
+static int oc_filter(
+ Filter *f,
+ int cur,
+ int *max )
+{
+ int rc = 0;
+
+ assert( f != NULL );
+
+ if( cur > *max ) *max = cur;
+
+ switch( f->f_choice ) {
+ case LDAP_FILTER_PRESENT:
+ if (f->f_desc == slap_schema.si_ad_objectClass) {
+ rc = 1;
+ }
+ break;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ cur++;
+ for ( f=f->f_and; f; f=f->f_next ) {
+ (void) oc_filter(f, cur, max);
+ }
+ break;
+
+ default:
+ break;
+ }
+ return rc;
+}
+
+typedef struct IDLchunk {
+ unsigned int logn;
+ unsigned int pad;
+} IDLchunk;
+
+static void search_stack_free( void *key, void *data )
+{
+ ber_memfree_x(data, NULL);
+}
+
+static void *search_stack( Operation *op )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ IDLchunk *ic = NULL;
+
+ if ( op->o_threadctx ) {
+ ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)search_stack,
+ (void **)&ic, NULL );
+ } else {
+ ic = mdb->mi_search_stack;
+ }
+
+ if ( ic && ic->logn != MDB_idl_logn ) {
+ ber_memfree_x( ic, NULL );
+ ic = NULL;
+ }
+
+ if ( !ic ) {
+ ic = ch_malloc(( mdb->mi_search_stack_depth + 2 ) * (size_t)MDB_idl_um_size
+ * sizeof( ID ) + sizeof( IDLchunk ) );
+ ic->logn = MDB_idl_logn;
+ if ( op->o_threadctx ) {
+ ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)search_stack,
+ ic, search_stack_free, NULL, NULL );
+ } else {
+ mdb->mi_search_stack = ic;
+ }
+ ID *idl = (ID *)( ic+1 );
+ MDB_IDL_ZERO( idl );
+ }
+ return ic+1;
+}
+
+static int search_candidates(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ IdScopes *isc,
+ MDB_cursor *mci,
+ ID *ids,
+ ID *stack )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+ int rc, depth = 1;
+ Filter *f, rf, xf, nf, sf;
+ AttributeAssertion aa_ref = ATTRIBUTEASSERTION_INIT;
+ AttributeAssertion aa_subentry = ATTRIBUTEASSERTION_INIT;
+
+ /*
+ * This routine takes as input a filter (user-filter)
+ * and rewrites it as follows:
+ * (&(scope=DN)[(objectClass=subentry)]
+ * (|[(objectClass=referral)](user-filter))
+ */
+
+ Debug(LDAP_DEBUG_TRACE,
+ "search_candidates: base=\"%s\" (0x%08lx) scope=%d\n",
+ e->e_nname.bv_val, (long) e->e_id, op->oq_search.rs_scope );
+
+ f = op->oq_search.rs_filter;
+
+ /* If the user's filter uses objectClass=*,
+ * these clauses are redundant.
+ */
+ if (!oc_filter(op->oq_search.rs_filter, 1, &depth)
+ && !get_subentries_visibility(op)) {
+ if( !get_manageDSAit(op) && !get_domainScope(op) ) {
+ /* match referral objects */
+ struct berval bv_ref = BER_BVC( "referral" );
+ rf.f_choice = LDAP_FILTER_EQUALITY;
+ rf.f_ava = &aa_ref;
+ rf.f_av_desc = slap_schema.si_ad_objectClass;
+ rf.f_av_value = bv_ref;
+ rf.f_next = f;
+ xf.f_or = &rf;
+ xf.f_choice = LDAP_FILTER_OR;
+ xf.f_next = NULL;
+ f = &xf;
+ depth++;
+ }
+ }
+
+ if( get_subentries_visibility( op ) ) {
+ struct berval bv_subentry = BER_BVC( "subentry" );
+ sf.f_choice = LDAP_FILTER_EQUALITY;
+ sf.f_ava = &aa_subentry;
+ sf.f_av_desc = slap_schema.si_ad_objectClass;
+ sf.f_av_value = bv_subentry;
+ sf.f_next = f;
+ nf.f_choice = LDAP_FILTER_AND;
+ nf.f_and = &sf;
+ nf.f_next = NULL;
+ f = &nf;
+ depth++;
+ }
+
+ /* Allocate IDL stack, plus 1 more for former tmp */
+ if ( depth+1 > mdb->mi_search_stack_depth ) {
+ stack = ch_malloc( (depth + 1) * MDB_idl_um_size * sizeof( ID ) );
+ }
+
+ if( op->ors_deref & LDAP_DEREF_SEARCHING ) {
+ rc = search_aliases( op, rs, e->e_id, isc, mci, stack );
+ } else {
+ rc = LDAP_SUCCESS;
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ rc = mdb_filter_candidates( op, isc->mt, f, ids,
+ stack, stack+MDB_idl_um_size );
+ }
+
+ if ( depth+1 > mdb->mi_search_stack_depth ) {
+ ch_free( stack );
+ }
+
+ if( rc ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "mdb_search_candidates: failed (rc=%d)\n",
+ rc );
+
+ } else {
+ Debug(LDAP_DEBUG_TRACE,
+ "mdb_search_candidates: id=%ld first=%ld last=%ld\n",
+ (long) ids[0],
+ (long) MDB_IDL_FIRST(ids),
+ (long) MDB_IDL_LAST(ids) );
+ }
+
+ return rc;
+}
+
+static int
+parse_paged_cookie( Operation *op, SlapReply *rs )
+{
+ int rc = LDAP_SUCCESS;
+ PagedResultsState *ps = op->o_pagedresults_state;
+
+ /* this function must be invoked only if the pagedResults
+ * control has been detected, parsed and partially checked
+ * by the frontend */
+ assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
+
+ /* cookie decoding/checks deferred to backend... */
+ if ( ps->ps_cookieval.bv_len ) {
+ PagedResultsCookie reqcookie;
+ if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) {
+ /* bad cookie */
+ rs->sr_text = "paged results cookie is invalid";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ AC_MEMCPY( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie ));
+
+ if ( reqcookie > ps->ps_cookie ) {
+ /* bad cookie */
+ rs->sr_text = "paged results cookie is invalid";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+
+ } else if ( reqcookie < ps->ps_cookie ) {
+ rs->sr_text = "paged results cookie is invalid or old";
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ } else {
+ /* we're going to use ps_cookie */
+ op->o_conn->c_pagedresults_state.ps_cookie = 0;
+ }
+
+done:;
+
+ return rc;
+}
+
+static void
+send_paged_response(
+ Operation *op,
+ SlapReply *rs,
+ ID *lastid,
+ int tentries )
+{
+ LDAPControl *ctrls[2];
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ PagedResultsCookie respcookie;
+ struct berval cookie;
+
+ Debug(LDAP_DEBUG_ARGS,
+ "send_paged_response: lastid=0x%08lx nentries=%d\n",
+ lastid ? *lastid : 0, rs->sr_nentries );
+
+ ctrls[1] = NULL;
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+
+ if ( lastid ) {
+ respcookie = ( PagedResultsCookie )(*lastid);
+ cookie.bv_len = sizeof( respcookie );
+ cookie.bv_val = (char *)&respcookie;
+
+ } else {
+ respcookie = ( PagedResultsCookie )0;
+ BER_BVSTR( &cookie, "" );
+ }
+
+ op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
+ op->o_conn->c_pagedresults_state.ps_count =
+ ((PagedResultsState *)op->o_pagedresults_state)->ps_count +
+ rs->sr_nentries;
+
+ /* return size of 0 -- no estimate */
+ ber_printf( ber, "{iO}", 0, &cookie );
+
+ ctrls[0] = op->o_tmpalloc( sizeof(LDAPControl), op->o_tmpmemctx );
+ if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
+ goto done;
+ }
+
+ ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
+ ctrls[0]->ldctl_iscritical = 0;
+
+ slap_add_ctrls( op, rs, ctrls );
+ rs->sr_err = LDAP_SUCCESS;
+ send_ldap_result( op, rs );
+
+done:
+ (void) ber_free_buf( ber );
+}
diff --git a/servers/slapd/back-mdb/tools.c b/servers/slapd/back-mdb/tools.c
new file mode 100644
index 0000000..ddf6a29
--- /dev/null
+++ b/servers/slapd/back-mdb/tools.c
@@ -0,0 +1,1712 @@
+/* tools.c - tools for slap tools */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2011-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/errno.h>
+
+#define AVL_INTERNAL
+#include "back-mdb.h"
+#include "idl.h"
+
+#ifdef MDB_TOOL_IDL_CACHING
+static int mdb_tool_idl_flush( BackendDB *be, MDB_txn *txn );
+
+#define IDBLOCK 1024
+
+typedef struct mdb_tool_idl_cache_entry {
+ struct mdb_tool_idl_cache_entry *next;
+ ID ids[IDBLOCK];
+} mdb_tool_idl_cache_entry;
+
+typedef struct mdb_tool_idl_cache {
+ struct berval kstr;
+ mdb_tool_idl_cache_entry *head, *tail;
+ ID first, last;
+ int count;
+ short offset;
+ short flags;
+} mdb_tool_idl_cache;
+#define WAS_FOUND 0x01
+#define WAS_RANGE 0x02
+
+#define MDB_TOOL_IDL_FLUSH(be, txn) mdb_tool_idl_flush(be, txn)
+#else
+#define MDB_TOOL_IDL_FLUSH(be, txn)
+#endif /* MDB_TOOL_IDL_CACHING */
+
+MDB_txn *mdb_tool_txn = NULL;
+
+static MDB_txn *txi = NULL;
+static MDB_cursor *cursor = NULL, *idcursor = NULL;
+static MDB_cursor *mcp = NULL, *mcd = NULL;
+static MDB_val key, data;
+static ID previd = NOID;
+
+typedef struct dn_id {
+ ID id;
+ struct berval dn;
+} dn_id;
+
+#define HOLE_SIZE 4096
+static dn_id hbuf[HOLE_SIZE], *holes = hbuf;
+static unsigned nhmax = HOLE_SIZE;
+static unsigned nholes;
+
+static struct berval *tool_base;
+static int tool_scope;
+static Filter *tool_filter;
+static Entry *tool_next_entry;
+
+static ID mdb_tool_ix_id;
+static BackendDB *mdb_tool_ix_be;
+static MDB_txn *mdb_tool_ix_txn;
+static int mdb_tool_index_tcount, mdb_tool_threads;
+static IndexRec *mdb_tool_index_rec;
+static AttrIxInfo **mdb_tool_axinfo;
+static struct mdb_info *mdb_tool_info;
+static ldap_pvt_thread_mutex_t mdb_tool_index_mutex;
+static ldap_pvt_thread_cond_t mdb_tool_index_cond_main;
+static ldap_pvt_thread_cond_t mdb_tool_index_cond_work;
+static void * mdb_tool_index_task( void *ctx, void *ptr );
+
+static int mdb_writes, mdb_writes_per_commit;
+
+/* Number of ops per commit in Quick mode.
+ * Batching speeds writes overall, but too large a
+ * batch will fail with MDB_TXN_FULL.
+ */
+#ifndef MDB_WRITES_PER_COMMIT
+#define MDB_WRITES_PER_COMMIT 500
+#endif
+
+static int
+mdb_tool_entry_get_int( BackendDB *be, ID id, Entry **ep );
+
+int mdb_tool_entry_open(
+ BackendDB *be, int mode )
+{
+ /* In Quick mode, commit once per 500 entries */
+ mdb_writes = 0;
+ if ( slapMode & SLAP_TOOL_QUICK )
+ mdb_writes_per_commit = MDB_WRITES_PER_COMMIT;
+ else
+ mdb_writes_per_commit = 1;
+
+#ifdef MDB_TOOL_IDL_CACHING /* threaded indexing has no performance advantage */
+ /* Set up for threaded slapindex */
+ if (( slapMode & (SLAP_TOOL_QUICK|SLAP_TOOL_READONLY)) == SLAP_TOOL_QUICK ) {
+ if ( !mdb_tool_info ) {
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+ ldap_pvt_thread_mutex_init( &mdb_tool_index_mutex );
+ ldap_pvt_thread_cond_init( &mdb_tool_index_cond_main );
+ ldap_pvt_thread_cond_init( &mdb_tool_index_cond_work );
+ if ( mdb->mi_nattrs ) {
+ int i;
+ mdb_tool_threads = slap_tool_thread_max - 1;
+ if ( mdb_tool_threads > 1 ) {
+ mdb_tool_index_rec = ch_calloc( mdb->mi_nattrs, sizeof( IndexRec ));
+ mdb_tool_axinfo = ch_calloc( mdb_tool_threads, sizeof( AttrIxInfo* ) +
+ sizeof( AttrIxInfo ));
+ mdb_tool_axinfo[0] = (AttrIxInfo *)(mdb_tool_axinfo + mdb_tool_threads);
+ for (i=1; i<mdb_tool_threads; i++)
+ mdb_tool_axinfo[i] = mdb_tool_axinfo[i-1]+1;
+ mdb_tool_index_tcount = mdb_tool_threads - 1;
+ mdb_tool_ix_be = be;
+ for (i=1; i<mdb_tool_threads; i++) {
+ int *ptr = ch_malloc( sizeof( int ));
+ *ptr = i;
+ ldap_pvt_thread_pool_submit( &connection_pool,
+ mdb_tool_index_task, ptr );
+ }
+ mdb_tool_info = mdb;
+ }
+ }
+ }
+ }
+#endif
+
+ return 0;
+}
+
+int mdb_tool_entry_close(
+ BackendDB *be )
+{
+#ifdef MDB_TOOL_IDL_CACHING
+ if ( mdb_tool_info ) {
+ int i;
+ slapd_shutdown = 1;
+ ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
+
+ /* There might still be some threads starting */
+ while ( mdb_tool_index_tcount > 0 ) {
+ ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
+ &mdb_tool_index_mutex );
+ }
+
+ mdb_tool_index_tcount = mdb_tool_threads - 1;
+ ldap_pvt_thread_cond_broadcast( &mdb_tool_index_cond_work );
+
+ /* Make sure all threads are stopped */
+ while ( mdb_tool_index_tcount > 0 ) {
+ ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
+ &mdb_tool_index_mutex );
+ }
+ ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
+
+ mdb_tool_info = NULL;
+ slapd_shutdown = 0;
+ ch_free( mdb_tool_index_rec );
+ mdb_tool_index_tcount = mdb_tool_threads - 1;
+ if (mdb_tool_txn)
+ MDB_TOOL_IDL_FLUSH( be, mdb_tool_txn );
+ for (i=0; i<mdb_tool_threads; i++) {
+ mdb_tool_idl_cache *ic;
+ mdb_tool_idl_cache_entry *ice;
+ while ((ic = mdb_tool_axinfo[i]->ai_clist)) {
+ mdb_tool_axinfo[i]->ai_clist = ic->head;
+ free(ic);
+ }
+ while ((ice = mdb_tool_axinfo[i]->ai_flist)) {
+ mdb_tool_axinfo[i]->ai_flist = ice->next;
+ free(ice);
+ }
+ }
+ }
+#endif
+
+ if( idcursor ) {
+ mdb_cursor_close( idcursor );
+ idcursor = NULL;
+ }
+ if( cursor ) {
+ mdb_cursor_close( cursor );
+ cursor = NULL;
+ }
+ {
+ struct mdb_info *mdb = be->be_private;
+ if ( mdb ) {
+ int i;
+ for (i=0; i<mdb->mi_nattrs; i++)
+ mdb->mi_attrs[i]->ai_cursor = NULL;
+ }
+ }
+ if( mdb_tool_txn ) {
+ int rc;
+ if (( rc = mdb_txn_commit( mdb_tool_txn ))) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_tool_entry_close) ": database %s: "
+ "txn_commit failed: %s (%d)\n",
+ be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
+ return -1;
+ }
+ mdb_tool_txn = NULL;
+ }
+ if( txi ) {
+ int rc;
+ if (( rc = mdb_txn_commit( txi ))) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_tool_entry_close) ": database %s: "
+ "txn_commit failed: %s (%d)\n",
+ be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
+ return -1;
+ }
+ txi = NULL;
+ }
+
+ if( nholes ) {
+ unsigned i;
+ fprintf( stderr, "Error, entries missing!\n");
+ for (i=0; i<nholes; i++) {
+ fprintf(stderr, " entry %ld: %s\n",
+ holes[i].id, holes[i].dn.bv_val);
+ }
+ nholes = 0;
+ return -1;
+ }
+
+ return 0;
+}
+
+ID
+mdb_tool_entry_first_x(
+ BackendDB *be,
+ struct berval *base,
+ int scope,
+ Filter *f )
+{
+ tool_base = base;
+ tool_scope = scope;
+ tool_filter = f;
+
+ return mdb_tool_entry_next( be );
+}
+
+ID mdb_tool_entry_next(
+ BackendDB *be )
+{
+ int rc;
+ ID id;
+ struct mdb_info *mdb;
+
+ assert( be != NULL );
+ assert( slapMode & SLAP_TOOL_MODE );
+
+ mdb = (struct mdb_info *) be->be_private;
+ assert( mdb != NULL );
+
+ if ( !mdb_tool_txn ) {
+ rc = mdb_txn_begin( mdb->mi_dbenv, NULL, MDB_RDONLY, &mdb_tool_txn );
+ if ( rc )
+ return NOID;
+ rc = mdb_cursor_open( mdb_tool_txn, mdb->mi_id2entry, &cursor );
+ if ( rc ) {
+ mdb_txn_abort( mdb_tool_txn );
+ return NOID;
+ }
+ }
+
+next:;
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_NEXT );
+
+ if( rc ) {
+ return NOID;
+ }
+
+ previd = *(ID *)key.mv_data;
+ id = previd;
+
+ if ( !data.mv_size )
+ goto next;
+
+ if ( tool_filter || tool_base ) {
+ static Operation op = {0};
+ static Opheader ohdr = {0};
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ if ( tool_next_entry ) {
+ mdb_entry_release( &op, tool_next_entry, 0 );
+ tool_next_entry = NULL;
+ }
+
+ rc = mdb_tool_entry_get_int( be, id, &tool_next_entry );
+ if ( rc == LDAP_NO_SUCH_OBJECT ) {
+ goto next;
+ }
+
+ assert( tool_next_entry != NULL );
+
+ if ( tool_filter && test_filter( NULL, tool_next_entry, tool_filter ) != LDAP_COMPARE_TRUE )
+ {
+ mdb_entry_release( &op, tool_next_entry, 0 );
+ tool_next_entry = NULL;
+ goto next;
+ }
+ }
+
+ return id;
+}
+
+ID mdb_tool_dn2id_get(
+ Backend *be,
+ struct berval *dn
+)
+{
+ struct mdb_info *mdb;
+ Operation op = {0};
+ Opheader ohdr = {0};
+ ID id;
+ int rc;
+
+ if ( BER_BVISEMPTY(dn) )
+ return 0;
+
+ mdb = (struct mdb_info *) be->be_private;
+
+ if ( !mdb_tool_txn ) {
+ rc = mdb_txn_begin( mdb->mi_dbenv, NULL, (slapMode & SLAP_TOOL_READONLY) != 0 ?
+ MDB_RDONLY : 0, &mdb_tool_txn );
+ if ( rc )
+ return NOID;
+ }
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ rc = mdb_dn2id( &op, mdb_tool_txn, NULL, dn, &id, NULL, NULL, NULL );
+ if ( rc == MDB_NOTFOUND )
+ return NOID;
+
+ return id;
+}
+
+static int
+mdb_tool_entry_get_int( BackendDB *be, ID id, Entry **ep )
+{
+ Operation op = {0};
+ Opheader ohdr = {0};
+
+ Entry *e = NULL;
+ struct berval dn = BER_BVNULL, ndn = BER_BVNULL;
+ int rc;
+
+ assert( be != NULL );
+ assert( slapMode & SLAP_TOOL_MODE );
+
+ if ( ( tool_filter || tool_base ) && id == previd && tool_next_entry != NULL ) {
+ *ep = tool_next_entry;
+ tool_next_entry = NULL;
+ return LDAP_SUCCESS;
+ }
+
+ if ( id != previd ) {
+ key.mv_size = sizeof(ID);
+ key.mv_data = &id;
+ rc = mdb_cursor_get( cursor, &key, &data, MDB_SET );
+ if ( rc ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ }
+ if ( !data.mv_size ) {
+ rc = LDAP_NO_SUCH_OBJECT;
+ goto done;
+ }
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+ if ( slapMode & SLAP_TOOL_READONLY ) {
+ rc = mdb_id2name( &op, mdb_tool_txn, &idcursor, id, &dn, &ndn );
+ if ( rc ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ if ( tool_base != NULL ) {
+ if ( !dnIsSuffixScope( &ndn, tool_base, tool_scope ) ) {
+ ch_free( dn.bv_val );
+ ch_free( ndn.bv_val );
+ rc = LDAP_NO_SUCH_OBJECT;
+ goto done;
+ }
+ }
+ }
+ rc = mdb_entry_decode( &op, mdb_tool_txn, &data, id, &e );
+ e->e_id = id;
+ if ( !BER_BVISNULL( &dn )) {
+ e->e_name = dn;
+ e->e_nname = ndn;
+ } else {
+ e->e_name.bv_val = NULL;
+ e->e_nname.bv_val = NULL;
+ }
+
+done:
+ if ( e != NULL ) {
+ *ep = e;
+ }
+
+ return rc;
+}
+
+Entry*
+mdb_tool_entry_get( BackendDB *be, ID id )
+{
+ Entry *e = NULL;
+ int rc;
+
+ if ( !mdb_tool_txn ) {
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+ rc = mdb_txn_begin( mdb->mi_dbenv, NULL,
+ (slapMode & SLAP_TOOL_READONLY) ? MDB_RDONLY : 0, &mdb_tool_txn );
+ if ( rc )
+ return NULL;
+ }
+ if ( !cursor ) {
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+ rc = mdb_cursor_open( mdb_tool_txn, mdb->mi_id2entry, &cursor );
+ if ( rc ) {
+ mdb_txn_abort( mdb_tool_txn );
+ mdb_tool_txn = NULL;
+ return NULL;
+ }
+ }
+ (void)mdb_tool_entry_get_int( be, id, &e );
+ return e;
+}
+
+static int mdb_tool_next_id(
+ Operation *op,
+ MDB_txn *tid,
+ Entry *e,
+ struct berval *text,
+ int hole )
+{
+ struct berval dn = e->e_name;
+ struct berval ndn = e->e_nname;
+ struct berval pdn, npdn, nmatched;
+ ID id, pid = 0;
+ int rc;
+
+ if (ndn.bv_len == 0) {
+ e->e_id = 0;
+ return 0;
+ }
+
+ rc = mdb_dn2id( op, tid, mcp, &ndn, &id, NULL, NULL, &nmatched );
+ if ( rc == MDB_NOTFOUND ) {
+ if ( !be_issuffix( op->o_bd, &ndn ) ) {
+ ID eid = e->e_id;
+ dnParent( &ndn, &npdn );
+ if ( nmatched.bv_len != npdn.bv_len ) {
+ dnParent( &dn, &pdn );
+ e->e_name = pdn;
+ e->e_nname = npdn;
+ rc = mdb_tool_next_id( op, tid, e, text, 1 );
+ e->e_name = dn;
+ e->e_nname = ndn;
+ if ( rc ) {
+ return rc;
+ }
+ /* If parent didn't exist, it was created just now
+ * and its ID is now in e->e_id. Make sure the current
+ * entry gets added under the new parent ID.
+ */
+ if ( eid != e->e_id ) {
+ pid = e->e_id;
+ }
+ } else {
+ pid = id;
+ }
+ }
+ rc = mdb_next_id( op->o_bd, idcursor, &e->e_id );
+ if ( rc ) {
+ snprintf( text->bv_val, text->bv_len,
+ "next_id failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> mdb_tool_next_id: %s\n", text->bv_val );
+ return rc;
+ }
+ rc = mdb_dn2id_add( op, mcp, mcd, pid, 1, 1, e );
+ if ( rc ) {
+ snprintf( text->bv_val, text->bv_len,
+ "dn2id_add failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> mdb_tool_next_id: %s\n", text->bv_val );
+ } else if ( hole ) {
+ MDB_val key, data;
+ if ( nholes == nhmax - 1 ) {
+ if ( holes == hbuf ) {
+ holes = ch_malloc( nhmax * sizeof(dn_id) * 2 );
+ AC_MEMCPY( holes, hbuf, sizeof(hbuf) );
+ } else {
+ holes = ch_realloc( holes, nhmax * sizeof(dn_id) * 2 );
+ }
+ nhmax *= 2;
+ }
+ ber_dupbv( &holes[nholes].dn, &ndn );
+ holes[nholes++].id = e->e_id;
+ key.mv_size = sizeof(ID);
+ key.mv_data = &e->e_id;
+ data.mv_size = 0;
+ data.mv_data = NULL;
+ rc = mdb_cursor_put( idcursor, &key, &data, MDB_NOOVERWRITE );
+ if ( rc == MDB_KEYEXIST )
+ rc = 0;
+ if ( rc ) {
+ snprintf( text->bv_val, text->bv_len,
+ "dummy id2entry add failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> mdb_tool_next_id: %s\n", text->bv_val );
+ }
+ }
+ } else if ( !hole ) {
+ unsigned i, j;
+
+ e->e_id = id;
+
+ for ( i=0; i<nholes; i++) {
+ if ( holes[i].id == e->e_id ) {
+ free(holes[i].dn.bv_val);
+ for (j=i;j<nholes;j++) holes[j] = holes[j+1];
+ holes[j].id = 0;
+ nholes--;
+ break;
+ } else if ( holes[i].id > e->e_id ) {
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+static int
+mdb_tool_index_add(
+ Operation *op,
+ MDB_txn *txn,
+ Entry *e )
+{
+ struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
+
+ if ( !mdb->mi_nattrs )
+ return 0;
+
+ if ( mdb_tool_threads > 1 ) {
+ IndexRec *ir;
+ int i, rc;
+ Attribute *a;
+
+ ir = mdb_tool_index_rec;
+ for (i=0; i<mdb->mi_nattrs; i++)
+ ir[i].ir_attrs = NULL;
+
+ for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
+ rc = mdb_index_recset( mdb, a, a->a_desc->ad_type,
+ &a->a_desc->ad_tags, ir );
+ if ( rc )
+ return rc;
+ }
+ for (i=0; i<mdb->mi_nattrs; i++) {
+ if ( !ir[i].ir_ai )
+ break;
+ rc = mdb_cursor_open( txn, ir[i].ir_ai->ai_dbi,
+ &ir[i].ir_ai->ai_cursor );
+ if ( rc )
+ return rc;
+ }
+ mdb_tool_ix_id = e->e_id;
+ mdb_tool_ix_txn = txn;
+ ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
+ /* Wait for all threads to be ready */
+ while ( mdb_tool_index_tcount ) {
+ ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
+ &mdb_tool_index_mutex );
+ }
+
+ for ( i=1; i<mdb_tool_threads; i++ )
+ mdb_tool_index_rec[i].ir_i = LDAP_BUSY;
+ mdb_tool_index_tcount = mdb_tool_threads - 1;
+ ldap_pvt_thread_cond_broadcast( &mdb_tool_index_cond_work );
+ ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
+
+ return mdb_index_recrun( op, txn, mdb, ir, e->e_id, 0 );
+ } else
+ {
+ return mdb_index_entry_add( op, txn, e );
+ }
+}
+
+static int
+mdb_tool_index_finish()
+{
+ int i, rc = 0;
+ ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
+ for ( i=1; i<mdb_tool_threads; i++ ) {
+ if ( mdb_tool_index_rec[i].ir_i == LDAP_BUSY ) {
+ ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_main,
+ &mdb_tool_index_mutex );
+ i--;
+ continue;
+ }
+ if ( mdb_tool_index_rec[i].ir_i ) {
+ rc = mdb_tool_index_rec[i].ir_i;
+ break;
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
+ return rc;
+}
+
+ID mdb_tool_entry_put(
+ BackendDB *be,
+ Entry *e,
+ struct berval *text )
+{
+ int rc;
+ struct mdb_info *mdb;
+ Operation op = {0};
+ Opheader ohdr = {0};
+
+ assert( be != NULL );
+ assert( slapMode & SLAP_TOOL_MODE );
+
+ assert( text != NULL );
+ assert( text->bv_val != NULL );
+ assert( text->bv_val[0] == '\0' ); /* overconservative? */
+
+ Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(mdb_tool_entry_put)
+ "( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn );
+
+ mdb = (struct mdb_info *) be->be_private;
+
+ if ( !mdb_tool_txn ) {
+ rc = mdb_txn_begin( mdb->mi_dbenv, NULL, 0, &mdb_tool_txn );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "txn_begin failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
+ text->bv_val );
+ return NOID;
+ }
+ }
+ if ( !idcursor ) {
+ rc = mdb_cursor_open( mdb_tool_txn, mdb->mi_id2entry, &idcursor );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "cursor_open failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
+ text->bv_val );
+ return NOID;
+ }
+ if ( !mdb->mi_nextid ) {
+ ID dummy;
+ mdb_next_id( be, idcursor, &dummy );
+ }
+ rc = mdb_cursor_open( mdb_tool_txn, mdb->mi_dn2id, &mcp );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "cursor_open failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
+ text->bv_val );
+ return NOID;
+ }
+ rc = mdb_cursor_open( mdb_tool_txn, mdb->mi_dn2id, &mcd );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "cursor_open failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
+ text->bv_val );
+ return NOID;
+ }
+ }
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ /* add dn2id indices */
+ rc = mdb_tool_next_id( &op, mdb_tool_txn, e, text, 0 );
+ if( rc != 0 ) {
+ goto done;
+ }
+
+ if ( mdb_tool_threads > 1 ) {
+ LDAP_SLIST_INSERT_HEAD( &op.o_extra, &mdb_tool_axinfo[0]->ai_oe, oe_next );
+ }
+ rc = mdb_tool_index_add( &op, mdb_tool_txn, e );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "index_entry_add failed: err=%d", rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
+ text->bv_val );
+ goto done;
+ }
+
+
+ /* id2entry index */
+ rc = mdb_id2entry_add( &op, mdb_tool_txn, idcursor, e );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "id2entry_add failed: err=%d", rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
+ text->bv_val );
+ goto done;
+ }
+
+ if( mdb->mi_nattrs && mdb_tool_threads > 1 )
+ rc = mdb_tool_index_finish();
+
+done:
+ if( rc == 0 ) {
+ mdb_writes++;
+ if ( mdb_writes >= mdb_writes_per_commit ) {
+ unsigned i;
+ MDB_TOOL_IDL_FLUSH( be, mdb_tool_txn );
+ rc = mdb_txn_commit( mdb_tool_txn );
+ for ( i=0; i<mdb->mi_nattrs; i++ )
+ mdb->mi_attrs[i]->ai_cursor = NULL;
+ mdb_writes = 0;
+ mdb_tool_txn = NULL;
+ idcursor = NULL;
+ if( rc != 0 ) {
+ mdb->mi_numads = 0;
+ snprintf( text->bv_val, text->bv_len,
+ "txn_commit failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
+ text->bv_val );
+ e->e_id = NOID;
+ }
+ }
+
+ } else {
+ unsigned i;
+ mdb_txn_abort( mdb_tool_txn );
+ mdb_tool_txn = NULL;
+ idcursor = NULL;
+ for ( i=0; i<mdb->mi_nattrs; i++ )
+ mdb->mi_attrs[i]->ai_cursor = NULL;
+ mdb_writes = 0;
+ snprintf( text->bv_val, text->bv_len,
+ "txn_aborted! %s (%d)",
+ rc == LDAP_OTHER ? "Internal error" :
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_put) ": %s\n",
+ text->bv_val );
+ e->e_id = NOID;
+ }
+
+ return e->e_id;
+}
+
+static int mdb_dn2id_upgrade( BackendDB *be );
+
+int mdb_tool_entry_reindex(
+ BackendDB *be,
+ ID id,
+ AttributeDescription **adv )
+{
+ struct mdb_info *mi = (struct mdb_info *) be->be_private;
+ int rc;
+ Entry *e;
+ Operation op = {0};
+ Opheader ohdr = {0};
+
+ Debug( LDAP_DEBUG_ARGS,
+ "=> " LDAP_XSTRING(mdb_tool_entry_reindex) "( %ld )\n",
+ (long) id );
+ assert( tool_base == NULL );
+ assert( tool_filter == NULL );
+
+ /* Special: do a dn2id upgrade */
+ if ( adv && adv[0] == slap_schema.si_ad_entryDN ) {
+ /* short-circuit tool_entry_next() */
+ mdb_cursor_get( cursor, &key, &data, MDB_LAST );
+ return mdb_dn2id_upgrade( be );
+ }
+
+ /* No indexes configured, nothing to do. Could return an
+ * error here to shortcut things.
+ */
+ if (!mi->mi_attrs) {
+ return 0;
+ }
+
+ /* Check for explicit list of attrs to index */
+ if ( adv ) {
+ int i, j, n;
+
+ if ( mi->mi_attrs[0]->ai_desc != adv[0] ) {
+ /* count */
+ for ( n = 0; adv[n]; n++ ) ;
+
+ /* insertion sort */
+ for ( i = 0; i < n; i++ ) {
+ AttributeDescription *ad = adv[i];
+ for ( j = i-1; j>=0; j--) {
+ if ( SLAP_PTRCMP( adv[j], ad ) <= 0 ) break;
+ adv[j+1] = adv[j];
+ }
+ adv[j+1] = ad;
+ }
+ }
+
+ for ( i = 0; adv[i]; i++ ) {
+ if ( mi->mi_attrs[i]->ai_desc != adv[i] ) {
+ for ( j = i+1; j < mi->mi_nattrs; j++ ) {
+ if ( mi->mi_attrs[j]->ai_desc == adv[i] ) {
+ AttrInfo *ai = mi->mi_attrs[i];
+ mi->mi_attrs[i] = mi->mi_attrs[j];
+ mi->mi_attrs[j] = ai;
+ break;
+ }
+ }
+ if ( j == mi->mi_nattrs ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_tool_entry_reindex)
+ ": no index configured for %s\n",
+ adv[i]->ad_cname.bv_val );
+ return -1;
+ }
+ }
+ }
+ mi->mi_nattrs = i;
+ }
+
+ e = mdb_tool_entry_get( be, id );
+
+ if( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_tool_entry_reindex)
+ ": could not locate id=%ld\n",
+ (long) id );
+ return -1;
+ }
+
+ if ( !txi ) {
+ rc = mdb_txn_begin( mi->mi_dbenv, NULL, 0, &txi );
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_reindex) ": "
+ "txn_begin failed: %s (%d)\n",
+ mdb_strerror(rc), rc );
+ goto done;
+ }
+ }
+
+ if ( slapMode & SLAP_TRUNCATE_MODE ) {
+ int i;
+ for ( i=0; i < mi->mi_nattrs; i++ ) {
+ rc = mdb_drop( txi, mi->mi_attrs[i]->ai_dbi, 0 );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ LDAP_XSTRING(mdb_tool_entry_reindex)
+ ": (Truncate) mdb_drop(%s) failed: %s (%d)\n",
+ mi->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val,
+ mdb_strerror(rc), rc );
+ return -1;
+ }
+ }
+ slapMode ^= SLAP_TRUNCATE_MODE;
+ }
+
+ /*
+ * just (re)add them for now
+ * Use truncate mode to empty/reset index databases
+ */
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> " LDAP_XSTRING(mdb_tool_entry_reindex) "( %ld )\n",
+ (long) id );
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ rc = mdb_tool_index_add( &op, txi, e );
+
+done:
+ if( rc == 0 ) {
+ mdb_writes++;
+ if ( mdb_writes >= mdb_writes_per_commit ) {
+ MDB_val key;
+ unsigned i;
+ MDB_TOOL_IDL_FLUSH( be, txi );
+ rc = mdb_txn_commit( txi );
+ mdb_writes = 0;
+ for ( i=0; i<mi->mi_nattrs; i++ )
+ mi->mi_attrs[i]->ai_cursor = NULL;
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_reindex)
+ ": txn_commit failed: %s (%d)\n",
+ mdb_strerror(rc), rc );
+ e->e_id = NOID;
+ }
+ mdb_cursor_close( cursor );
+ txi = NULL;
+ /* Must close the read txn to allow old pages to be reclaimed. */
+ mdb_txn_abort( mdb_tool_txn );
+ /* and then reopen it so that tool_entry_next still works. */
+ mdb_txn_begin( mi->mi_dbenv, NULL, MDB_RDONLY, &mdb_tool_txn );
+ mdb_cursor_open( mdb_tool_txn, mi->mi_id2entry, &cursor );
+ key.mv_data = &id;
+ key.mv_size = sizeof(ID);
+ mdb_cursor_get( cursor, &key, NULL, MDB_SET );
+ }
+
+ } else {
+ unsigned i;
+ mdb_writes = 0;
+ mdb_cursor_close( cursor );
+ cursor = NULL;
+ mdb_txn_abort( txi );
+ for ( i=0; i<mi->mi_nattrs; i++ )
+ mi->mi_attrs[i]->ai_cursor = NULL;
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_reindex)
+ ": txn_aborted! err=%d\n",
+ rc );
+ e->e_id = NOID;
+ txi = NULL;
+ }
+ mdb_entry_release( &op, e, 0 );
+
+ return rc;
+}
+
+ID mdb_tool_entry_modify(
+ BackendDB *be,
+ Entry *e,
+ struct berval *text )
+{
+ int rc;
+ struct mdb_info *mdb;
+ Operation op = {0};
+ Opheader ohdr = {0};
+
+ assert( be != NULL );
+ assert( slapMode & SLAP_TOOL_MODE );
+
+ assert( text != NULL );
+ assert( text->bv_val != NULL );
+ assert( text->bv_val[0] == '\0' ); /* overconservative? */
+
+ assert ( e->e_id != NOID );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> " LDAP_XSTRING(mdb_tool_entry_modify) "( %ld, \"%s\" )\n",
+ (long) e->e_id, e->e_dn );
+
+ mdb = (struct mdb_info *) be->be_private;
+
+ if( cursor ) {
+ mdb_cursor_close( cursor );
+ cursor = NULL;
+ }
+ if ( !mdb_tool_txn ) {
+ rc = mdb_txn_begin( mdb->mi_dbenv, NULL, 0, &mdb_tool_txn );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "txn_begin failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_modify) ": %s\n",
+ text->bv_val );
+ return NOID;
+ }
+ }
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ /* id2entry index */
+ rc = mdb_id2entry_update( &op, mdb_tool_txn, NULL, e );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "id2entry_update failed: err=%d", rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_modify) ": %s\n",
+ text->bv_val );
+ goto done;
+ }
+
+done:
+ if( rc == 0 ) {
+ rc = mdb_txn_commit( mdb_tool_txn );
+ if( rc != 0 ) {
+ mdb->mi_numads = 0;
+ snprintf( text->bv_val, text->bv_len,
+ "txn_commit failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_modify) ": "
+ "%s\n", text->bv_val );
+ e->e_id = NOID;
+ }
+
+ } else {
+ mdb_txn_abort( mdb_tool_txn );
+ snprintf( text->bv_val, text->bv_len,
+ "txn_aborted! %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_modify) ": %s\n",
+ text->bv_val );
+ e->e_id = NOID;
+ }
+ mdb_tool_txn = NULL;
+
+ return e->e_id;
+}
+
+int mdb_tool_entry_delete(
+ BackendDB *be,
+ struct berval *ndn,
+ struct berval *text )
+{
+ int rc;
+ struct mdb_info *mdb;
+ Operation op = {0};
+ Opheader ohdr = {0};
+ Entry *e;
+
+ assert( be != NULL );
+ assert( slapMode & SLAP_TOOL_MODE );
+
+ assert( text != NULL );
+ assert( text->bv_val != NULL );
+ assert( text->bv_val[0] == '\0' ); /* overconservative? */
+
+ assert ( ndn != NULL );
+ assert ( ndn->bv_val != NULL );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> " LDAP_XSTRING(mdb_tool_entry_delete) "( %s )\n",
+ ndn->bv_val );
+
+ mdb = (struct mdb_info *) be->be_private;
+
+ assert( cursor == NULL );
+ if( cursor ) {
+ mdb_cursor_close( cursor );
+ cursor = NULL;
+ }
+ if( !mdb_tool_txn ) {
+ rc = mdb_txn_begin( mdb->mi_dbenv, NULL, 0, &mdb_tool_txn );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "txn_begin failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_delete) ": %s\n",
+ text->bv_val );
+ return LDAP_OTHER;
+ }
+ }
+
+ rc = mdb_cursor_open( mdb_tool_txn, mdb->mi_dn2id, &cursor );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "cursor_open failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_delete) ": %s\n",
+ text->bv_val );
+ return LDAP_OTHER;
+ }
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ rc = mdb_dn2entry( &op, mdb_tool_txn, cursor, ndn, &e, NULL, 0 );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "dn2entry failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_delete) ": %s\n",
+ text->bv_val );
+ goto done;
+ }
+
+ /* check that we wouldn't orphan any children */
+ rc = mdb_dn2id_children( &op, mdb_tool_txn, e );
+ if( rc != MDB_NOTFOUND ) {
+ switch( rc ) {
+ case 0:
+ snprintf( text->bv_val, text->bv_len,
+ "delete failed:"
+ " subordinate objects must be deleted first");
+ break;
+ default:
+ snprintf( text->bv_val, text->bv_len,
+ "has_children failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ break;
+ }
+ rc = -1;
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_delete) ": %s\n",
+ text->bv_val );
+ goto done;
+ }
+
+ /* delete from dn2id */
+ rc = mdb_dn2id_delete( &op, cursor, e->e_id, 1 );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "dn2id_delete failed: err=%d", rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_delete) ": %s\n",
+ text->bv_val );
+ goto done;
+ }
+
+ /* deindex values */
+ rc = mdb_index_entry_del( &op, mdb_tool_txn, e );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "entry_delete failed: err=%d", rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_delete) ": %s\n",
+ text->bv_val );
+ goto done;
+ }
+
+ /* do the deletion */
+ rc = mdb_id2entry_delete( be, mdb_tool_txn, e );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "id2entry_update failed: err=%d", rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_delete) ": %s\n",
+ text->bv_val );
+ goto done;
+ }
+
+done:
+ /* free entry */
+ if( e != NULL ) {
+ mdb_entry_return( &op, e );
+ }
+
+ if( rc == 0 ) {
+ rc = mdb_txn_commit( mdb_tool_txn );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "txn_commit failed: %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_delete) ": "
+ "%s\n", text->bv_val );
+ }
+
+ } else {
+ mdb_txn_abort( mdb_tool_txn );
+ snprintf( text->bv_val, text->bv_len,
+ "txn_aborted! %s (%d)",
+ mdb_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(mdb_tool_entry_delete) ": %s\n",
+ text->bv_val );
+ }
+ mdb_tool_txn = NULL;
+ cursor = NULL;
+
+ return rc;
+}
+
+static void *
+mdb_tool_index_task( void *ctx, void *ptr )
+{
+ int base = *(int *)ptr;
+ Operation op = {0};
+ Opheader ohdr = {0};
+ AttrIxInfo ai = {0}, *aio;
+
+ free( ptr );
+ op.o_hdr = &ohdr;
+ op.o_bd = mdb_tool_ix_be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+ aio = mdb_tool_axinfo[base];
+ mdb_tool_axinfo[base] = &ai;
+ LDAP_SLIST_INSERT_HEAD( &op.o_extra, &ai.ai_oe, oe_next );
+ while ( 1 ) {
+ ldap_pvt_thread_mutex_lock( &mdb_tool_index_mutex );
+ mdb_tool_index_tcount--;
+ if ( !mdb_tool_index_tcount )
+ ldap_pvt_thread_cond_signal( &mdb_tool_index_cond_main );
+ ldap_pvt_thread_cond_wait( &mdb_tool_index_cond_work,
+ &mdb_tool_index_mutex );
+ if ( slapd_shutdown ) {
+ mdb_tool_index_tcount--;
+ if ( !mdb_tool_index_tcount )
+ ldap_pvt_thread_cond_signal( &mdb_tool_index_cond_main );
+ *aio = ai;
+ mdb_tool_axinfo[base] = aio;
+ ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
+ break;
+ }
+ ldap_pvt_thread_mutex_unlock( &mdb_tool_index_mutex );
+ mdb_tool_index_rec[base].ir_i = mdb_index_recrun( &op,
+ mdb_tool_ix_txn,
+ mdb_tool_info, mdb_tool_index_rec, mdb_tool_ix_id, base );
+ }
+
+ return NULL;
+}
+
+#ifdef MDB_TOOL_IDL_CACHING
+static int
+mdb_tool_idl_cmp( const void *v1, const void *v2 )
+{
+ const mdb_tool_idl_cache *c1 = v1, *c2 = v2;
+ int rc;
+
+ if (( rc = c1->kstr.bv_len - c2->kstr.bv_len )) return rc;
+ return memcmp( c1->kstr.bv_val, c2->kstr.bv_val, c1->kstr.bv_len );
+}
+
+static int
+mdb_tool_idl_flush_one( MDB_cursor *mc, AttrIxInfo *ai, mdb_tool_idl_cache *ic )
+{
+ mdb_tool_idl_cache_entry *ice;
+ MDB_val key, data[2];
+ int i, rc;
+ ID id, nid;
+
+ /* Freshly allocated, ignore it */
+ if ( !ic->head && ic->count <= MDB_idl_db_size ) {
+ return 0;
+ }
+
+ key.mv_data = ic->kstr.bv_val;
+ key.mv_size = ic->kstr.bv_len;
+
+ if ( ic->count > MDB_idl_db_size ) {
+ while ( ic->flags & WAS_FOUND ) {
+ rc = mdb_cursor_get( mc, &key, data, MDB_SET );
+ if ( rc ) {
+ /* FIXME: find out why this happens */
+ ic->flags = 0;
+ break;
+ }
+ if ( ic->flags & WAS_RANGE ) {
+ /* Skip lo */
+ rc = mdb_cursor_get( mc, &key, data, MDB_NEXT_DUP );
+
+ /* Get hi */
+ rc = mdb_cursor_get( mc, &key, data, MDB_NEXT_DUP );
+
+ /* Store range hi */
+ data[0].mv_data = &ic->last;
+ rc = mdb_cursor_put( mc, &key, data, MDB_CURRENT );
+ } else {
+ /* Delete old data, replace with range */
+ ic->first = *(ID *)data[0].mv_data;
+ mdb_cursor_del( mc, MDB_NODUPDATA );
+ }
+ break;
+ }
+ if ( !(ic->flags & WAS_RANGE)) {
+ /* range, didn't exist before */
+ nid = 0;
+ data[0].mv_size = sizeof(ID);
+ data[0].mv_data = &nid;
+ rc = mdb_cursor_put( mc, &key, data, 0 );
+ if ( rc == 0 ) {
+ data[0].mv_data = &ic->first;
+ rc = mdb_cursor_put( mc, &key, data, 0 );
+ if ( rc == 0 ) {
+ data[0].mv_data = &ic->last;
+ rc = mdb_cursor_put( mc, &key, data, 0 );
+ }
+ }
+ if ( rc ) {
+ rc = -1;
+ }
+ }
+ } else {
+ /* Normal write */
+ int n;
+
+ data[0].mv_size = sizeof(ID);
+ rc = 0;
+ for ( ice = ic->head, n=0; ice; ice = ice->next, n++ ) {
+ int end;
+ if ( ice->next ) {
+ end = IDBLOCK;
+ } else {
+ end = (ic->count-ic->offset) & (IDBLOCK-1);
+ if ( !end )
+ end = IDBLOCK;
+ }
+ data[1].mv_size = end;
+ data[0].mv_data = ice->ids;
+ rc = mdb_cursor_put( mc, &key, data, MDB_APPENDDUP|MDB_MULTIPLE );
+ if ( rc ) {
+ rc = -1;
+ break;
+ }
+ }
+ if ( ic->head ) {
+ ic->tail->next = ai->ai_flist;
+ ai->ai_flist = ic->head;
+ }
+ }
+ ic->head = ai->ai_clist;
+ ai->ai_clist = ic;
+ return rc;
+}
+
+static int
+mdb_tool_idl_flush_db( MDB_txn *txn, AttrInfo *ai, AttrIxInfo *ax )
+{
+ MDB_cursor *mc;
+ Avlnode *root;
+ int rc;
+
+ mdb_cursor_open( txn, ai->ai_dbi, &mc );
+ root = ldap_tavl_end( ai->ai_root, TAVL_DIR_LEFT );
+ do {
+ rc = mdb_tool_idl_flush_one( mc, ax, root->avl_data );
+ if ( rc != -1 )
+ rc = 0;
+ } while ((root = ldap_tavl_next(root, TAVL_DIR_RIGHT)));
+ mdb_cursor_close( mc );
+
+ return rc;
+}
+
+static int
+mdb_tool_idl_flush( BackendDB *be, MDB_txn *txn )
+{
+ struct mdb_info *mdb = (struct mdb_info *) be->be_private;
+ int rc = 0;
+ unsigned int i, dbi;
+
+ for ( i=0; i < mdb->mi_nattrs; i++ ) {
+ if ( !mdb->mi_attrs[i]->ai_root ) continue;
+ rc = mdb_tool_idl_flush_db( txn, mdb->mi_attrs[i], mdb_tool_axinfo[i % mdb_tool_threads] );
+ ldap_tavl_free(mdb->mi_attrs[i]->ai_root, NULL);
+ mdb->mi_attrs[i]->ai_root = NULL;
+ if ( rc )
+ break;
+ }
+ return rc;
+}
+
+int mdb_tool_idl_add(
+ BackendDB *be,
+ MDB_cursor *mc,
+ struct berval *keys,
+ ID id )
+{
+ MDB_dbi dbi;
+ mdb_tool_idl_cache *ic, itmp;
+ mdb_tool_idl_cache_entry *ice;
+ int i, rc, lcount;
+ AttrIxInfo *ax = (AttrIxInfo *)mc;
+ AttrInfo *ai = (AttrInfo *)ax->ai_ai;
+ mc = ai->ai_cursor;
+
+ dbi = ai->ai_dbi;
+ for (i=0; keys[i].bv_val; i++) {
+ itmp.kstr = keys[i];
+ ic = ldap_tavl_find( ai->ai_root, &itmp, mdb_tool_idl_cmp );
+
+ /* No entry yet, create one */
+ if ( !ic ) {
+ MDB_val key, data;
+ ID nid;
+ int rc;
+
+ if ( ax->ai_clist ) {
+ ic = ax->ai_clist;
+ ax->ai_clist = ic->head;
+ } else {
+ ic = ch_malloc( sizeof( mdb_tool_idl_cache ) + itmp.kstr.bv_len + 4 );
+ }
+ ic->kstr.bv_len = itmp.kstr.bv_len;
+ ic->kstr.bv_val = (char *)(ic+1);
+ memcpy( ic->kstr.bv_val, itmp.kstr.bv_val, ic->kstr.bv_len );
+ ic->head = ic->tail = NULL;
+ ic->last = 0;
+ ic->count = 0;
+ ic->offset = 0;
+ ic->flags = 0;
+ ldap_tavl_insert( &ai->ai_root, ic, mdb_tool_idl_cmp,
+ ldap_avl_dup_error );
+
+ /* load existing key count here */
+ key.mv_size = keys[i].bv_len;
+ key.mv_data = keys[i].bv_val;
+ rc = mdb_cursor_get( mc, &key, &data, MDB_SET );
+ if ( rc == 0 ) {
+ ic->flags |= WAS_FOUND;
+ nid = *(ID *)data.mv_data;
+ if ( nid == 0 ) {
+ ic->count = MDB_idl_db_size+1;
+ ic->flags |= WAS_RANGE;
+ } else {
+ size_t count;
+
+ mdb_cursor_count( mc, &count );
+ ic->count = count;
+ ic->first = nid;
+ ic->offset = count & (IDBLOCK-1);
+ }
+ }
+ }
+ /* are we a range already? */
+ if ( ic->count > MDB_idl_db_size ) {
+ ic->last = id;
+ continue;
+ /* Are we at the limit, and converting to a range? */
+ } else if ( ic->count == MDB_idl_db_size ) {
+ if ( ic->head ) {
+ ic->tail->next = ax->ai_flist;
+ ax->ai_flist = ic->head;
+ }
+ ic->head = ic->tail = NULL;
+ ic->last = id;
+ ic->count++;
+ continue;
+ }
+ /* No free block, create that too */
+ lcount = (ic->count-ic->offset) & (IDBLOCK-1);
+ if ( !ic->tail || lcount == 0) {
+ if ( ax->ai_flist ) {
+ ice = ax->ai_flist;
+ ax->ai_flist = ice->next;
+ } else {
+ ice = ch_malloc( sizeof( mdb_tool_idl_cache_entry ));
+ }
+ ice->next = NULL;
+ if ( !ic->head ) {
+ ic->head = ice;
+ } else {
+ ic->tail->next = ice;
+ }
+ ic->tail = ice;
+ if ( lcount )
+ ice->ids[lcount-1] = 0;
+ if ( !ic->count )
+ ic->first = id;
+ }
+ ice = ic->tail;
+ if (!lcount || ice->ids[lcount-1] != id) {
+ ice->ids[lcount] = id;
+ ic->count++;
+ }
+ }
+
+ return 0;
+}
+#endif /* MDB_TOOL_IDL_CACHING */
+
+/* Upgrade from pre 2.4.34 dn2id format */
+
+#include <ac/unistd.h>
+#include <lutil_meter.h>
+
+#define STACKSIZ 2048
+
+typedef struct rec {
+ ID id;
+ size_t len;
+ char rdn[512];
+} rec;
+
+static int
+mdb_dn2id_upgrade( BackendDB *be ) {
+ struct mdb_info *mi = (struct mdb_info *) be->be_private;
+ MDB_txn *mt;
+ MDB_cursor *mc = NULL;
+ MDB_val key, data;
+ int rc, writes=0, depth=0;
+ int enable_meter = 0;
+ ID id = 0, *num, count = 0;
+ rec *stack;
+ lutil_meter_t meter;
+
+ if (!(mi->mi_flags & MDB_NEED_UPGRADE)) {
+ Debug( LDAP_DEBUG_ANY, "database %s: No upgrade needed.\n",
+ be->be_suffix[0].bv_val );
+ return 0;
+ }
+
+ {
+ MDB_stat st;
+
+ mdb_stat(mdb_cursor_txn(cursor), mi->mi_dbis[MDB_ID2ENTRY], &st);
+ if (!st.ms_entries) {
+ /* Empty DB, nothing to upgrade? */
+ return 0;
+ }
+ if (isatty(2))
+ enable_meter = !lutil_meter_open(&meter,
+ &lutil_meter_text_display,
+ &lutil_meter_linear_estimator,
+ st.ms_entries);
+ }
+
+ num = ch_malloc(STACKSIZ * (sizeof(ID) + sizeof(rec)));
+ stack = (rec *)(num + STACKSIZ);
+
+ rc = mdb_txn_begin(mi->mi_dbenv, NULL, 0, &mt);
+ if (rc) {
+ Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_txn_begin failed, %s (%d)\n",
+ mdb_strerror(rc), rc );
+ goto leave;
+ }
+ rc = mdb_cursor_open(mt, mi->mi_dbis[MDB_DN2ID], &mc);
+ if (rc) {
+ Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_cursor_open failed, %s (%d)\n",
+ mdb_strerror(rc), rc );
+ goto leave;
+ }
+
+ key.mv_size = sizeof(ID);
+ /* post-order depth-first update */
+ for(;;) {
+ size_t dkids;
+ unsigned char *ptr;
+
+ /* visit */
+ key.mv_data = &id;
+ stack[depth].id = id;
+ rc = mdb_cursor_get(mc, &key, &data, MDB_SET);
+ if (rc) {
+ Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_cursor_get failed, %s (%d)\n",
+ mdb_strerror(rc), rc );
+ goto leave;
+ }
+ num[depth] = 1;
+
+ rc = mdb_cursor_count(mc, &dkids);
+ if (rc) {
+ Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_cursor_count failed, %s (%d)\n",
+ mdb_strerror(rc), rc );
+ goto leave;
+ }
+ if (dkids > 1) {
+ rc = mdb_cursor_get(mc, &key, &data, MDB_NEXT_DUP);
+down:
+ ptr = (unsigned char *)data.mv_data + data.mv_size - sizeof(ID);
+ memcpy(&id, ptr, sizeof(ID));
+ depth++;
+ memcpy(stack[depth].rdn, data.mv_data, data.mv_size);
+ stack[depth].len = data.mv_size;
+ continue;
+ }
+
+
+ /* pop: write updated count, advance to next node */
+pop:
+ /* update superior counts */
+ if (depth)
+ num[depth-1] += num[depth];
+
+ key.mv_data = &id;
+ id = stack[depth-1].id;
+ data.mv_data = stack[depth].rdn;
+ data.mv_size = stack[depth].len;
+ rc = mdb_cursor_get(mc, &key, &data, MDB_GET_BOTH);
+ if (rc) {
+ Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_cursor_get(BOTH) failed, %s (%d)\n",
+ mdb_strerror(rc), rc );
+ goto leave;
+ }
+ data.mv_data = stack[depth].rdn;
+ ptr = (unsigned char *)data.mv_data + data.mv_size;
+ memcpy(ptr, &num[depth], sizeof(ID));
+ data.mv_size += sizeof(ID);
+ rc = mdb_cursor_del(mc, 0);
+ if (rc) {
+ Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_cursor_del failed, %s (%d)\n",
+ mdb_strerror(rc), rc );
+ goto leave;
+ }
+ rc = mdb_cursor_put(mc, &key, &data, 0);
+ if (rc) {
+ Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_cursor_put failed, %s (%d)\n",
+ mdb_strerror(rc), rc );
+ goto leave;
+ }
+ count++;
+#if 1
+ if (enable_meter)
+ lutil_meter_update(&meter, count, 0);
+#else
+ {
+ int len;
+ ptr = data.mv_data;
+ len = (ptr[0] & 0x7f) << 8 | ptr[1];
+ printf("ID: %zu, %zu, %.*s\n", stack[depth].id, num[depth], len, ptr+2);
+ }
+#endif
+ writes++;
+ if (writes == 1000) {
+ mdb_cursor_close(mc);
+ rc = mdb_txn_commit(mt);
+ if (rc) {
+ Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_txn_commit failed, %s (%d)\n",
+ mdb_strerror(rc), rc );
+ goto leave;
+ }
+ rc = mdb_txn_begin(mi->mi_dbenv, NULL, 0, &mt);
+ if (rc) {
+ Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_txn_begin(2) failed, %s (%d)\n",
+ mdb_strerror(rc), rc );
+ goto leave;
+ }
+ rc = mdb_cursor_open(mt, mi->mi_dbis[MDB_DN2ID], &mc);
+ if (rc) {
+ Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_cursor_open(2) failed, %s (%d)\n",
+ mdb_strerror(rc), rc );
+ goto leave;
+ }
+ rc = mdb_cursor_get(mc, &key, &data, MDB_GET_BOTH);
+ if (rc) {
+ Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_cursor_get(2) failed, %s (%d)\n",
+ mdb_strerror(rc), rc );
+ goto leave;
+ }
+ writes = 0;
+ }
+ depth--;
+
+ rc = mdb_cursor_get(mc, &key, &data, MDB_NEXT_DUP);
+ if (rc == 0)
+ goto down;
+ rc = 0;
+ if (depth)
+ goto pop;
+ else
+ break;
+ }
+leave:
+ mdb_cursor_close(mc);
+ if (mt) {
+ int r2;
+ r2 = mdb_txn_commit(mt);
+ if (r2) {
+ Debug(LDAP_DEBUG_ANY, "mdb_dn2id_upgrade: mdb_txn_commit(2) failed, %s (%d)\n",
+ mdb_strerror(r2), r2 );
+ if (!rc)
+ rc = r2;
+ }
+ }
+ ch_free(num);
+ if (enable_meter) {
+ lutil_meter_update(&meter, count, 1);
+ lutil_meter_close(&meter);
+ }
+ return rc;
+}
diff --git a/servers/slapd/back-meta/Makefile.in b/servers/slapd/back-meta/Makefile.in
new file mode 100644
index 0000000..05b7ec7
--- /dev/null
+++ b/servers/slapd/back-meta/Makefile.in
@@ -0,0 +1,45 @@
+# Makefile.in for back-meta
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SRCS = init.c config.c search.c bind.c unbind.c add.c compare.c \
+ delete.c modify.c modrdn.c suffixmassage.c map.c \
+ conn.c candidates.c dncache.c
+OBJS = init.lo config.lo search.lo bind.lo unbind.lo add.lo compare.lo \
+ delete.lo modify.lo modrdn.lo suffixmassage.lo map.lo \
+ conn.lo candidates.lo dncache.lo
+
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-meta"
+BUILD_MOD = @BUILD_META@
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = $(@BUILD_META@_DEFS)
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+
+LIBBASE = back_meta
+
+XINCPATH = -I.. -I$(srcdir)/..
+XDEFS = $(MODULES_CPPFLAGS)
+
+all-local-lib: ../.backend
+
+../.backend: lib$(LIBBASE).a
+ @touch $@
+
diff --git a/servers/slapd/back-meta/add.c b/servers/slapd/back-meta/add.c
new file mode 100644
index 0000000..ec75db1
--- /dev/null
+++ b/servers/slapd/back-meta/add.c
@@ -0,0 +1,211 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+int
+meta_back_add( Operation *op, SlapReply *rs )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ metatarget_t *mt;
+ metaconn_t *mc;
+ int i, candidate = -1;
+ int isupdate;
+ Attribute *a;
+ LDAPMod **attrs;
+ struct berval mdn = BER_BVNULL, mapped;
+ dncookie dc;
+ int msgid;
+ ldap_back_send_t retrying = LDAP_BACK_RETRYING;
+ LDAPControl **ctrls = NULL;
+
+ Debug(LDAP_DEBUG_ARGS, "==> meta_back_add: %s\n",
+ op->o_req_dn.bv_val );
+
+ /*
+ * get the current connection
+ */
+ mc = meta_back_getconn( op, rs, &candidate, LDAP_BACK_SENDERR );
+ if ( !mc || !meta_back_dobind( op, rs, mc, LDAP_BACK_SENDERR ) ) {
+ return rs->sr_err;
+ }
+
+ assert( mc->mc_conns[ candidate ].msc_ld != NULL );
+
+ /*
+ * Rewrite the add dn, if needed
+ */
+ mt = mi->mi_targets[ candidate ];
+ dc.target = mt;
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+ dc.ctx = "addDN";
+
+ if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ /* Count number of attributes in entry ( +1 ) */
+ for ( i = 1, a = op->ora_e->e_attrs; a; i++, a = a->a_next );
+
+ /* Create array of LDAPMods for ldap_add() */
+ attrs = ch_malloc( sizeof( LDAPMod * )*i );
+
+ dc.ctx = "addAttrDN";
+ isupdate = be_shadow_update( op );
+ for ( i = 0, a = op->ora_e->e_attrs; a; a = a->a_next ) {
+ int j, is_oc = 0;
+
+ if ( !isupdate && !get_relax( op ) && a->a_desc->ad_type->sat_no_user_mod )
+ {
+ continue;
+ }
+
+ if ( a->a_desc == slap_schema.si_ad_objectClass
+ || a->a_desc == slap_schema.si_ad_structuralObjectClass )
+ {
+ is_oc = 1;
+ mapped = a->a_desc->ad_cname;
+
+ } else {
+ ldap_back_map( &mt->mt_rwmap.rwm_at,
+ &a->a_desc->ad_cname, &mapped, BACKLDAP_MAP );
+ if ( BER_BVISNULL( &mapped ) || BER_BVISEMPTY( &mapped ) ) {
+ continue;
+ }
+ }
+
+ attrs[ i ] = ch_malloc( sizeof( LDAPMod ) );
+ if ( attrs[ i ] == NULL ) {
+ continue;
+ }
+ attrs[ i ]->mod_op = LDAP_MOD_BVALUES;
+ attrs[ i ]->mod_type = mapped.bv_val;
+
+ if ( is_oc ) {
+ for ( j = 0; !BER_BVISNULL( &a->a_vals[ j ] ); j++ )
+ ;
+
+ attrs[ i ]->mod_bvalues =
+ (struct berval **)ch_malloc( ( j + 1 ) *
+ sizeof( struct berval * ) );
+
+ for ( j = 0; !BER_BVISNULL( &a->a_vals[ j ] ); ) {
+ struct ldapmapping *mapping;
+
+ ldap_back_mapping( &mt->mt_rwmap.rwm_oc,
+ &a->a_vals[ j ], &mapping, BACKLDAP_MAP );
+
+ if ( mapping == NULL ) {
+ if ( mt->mt_rwmap.rwm_oc.drop_missing ) {
+ continue;
+ }
+ attrs[ i ]->mod_bvalues[ j ] = &a->a_vals[ j ];
+
+ } else {
+ attrs[ i ]->mod_bvalues[ j ] = &mapping->dst;
+ }
+ j++;
+ }
+ attrs[ i ]->mod_bvalues[ j ] = NULL;
+
+ } else {
+ /*
+ * FIXME: dn-valued attrs should be rewritten
+ * to allow their use in ACLs at the back-ldap
+ * level.
+ */
+ if ( a->a_desc->ad_type->sat_syntax ==
+ slap_schema.si_syn_distinguishedName )
+ {
+ (void)ldap_dnattr_rewrite( &dc, a->a_vals );
+ if ( a->a_vals == NULL ) {
+ continue;
+ }
+ }
+
+ for ( j = 0; !BER_BVISNULL( &a->a_vals[ j ] ); j++ )
+ ;
+
+ attrs[ i ]->mod_bvalues = ch_malloc( ( j + 1 ) * sizeof( struct berval * ) );
+ for ( j = 0; !BER_BVISNULL( &a->a_vals[ j ] ); j++ ) {
+ attrs[ i ]->mod_bvalues[ j ] = &a->a_vals[ j ];
+ }
+ attrs[ i ]->mod_bvalues[ j ] = NULL;
+ }
+ i++;
+ }
+ attrs[ i ] = NULL;
+
+retry:;
+ ctrls = op->o_ctrls;
+ if ( meta_back_controls_add( op, rs, mc, candidate, &ctrls ) != LDAP_SUCCESS )
+ {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ rs->sr_err = ldap_add_ext( mc->mc_conns[ candidate ].msc_ld, mdn.bv_val,
+ attrs, ctrls, NULL, &msgid );
+ rs->sr_err = meta_back_op_result( mc, op, rs, candidate, msgid,
+ mt->mt_timeout[ SLAP_OP_ADD ], ( LDAP_BACK_SENDRESULT | retrying ) );
+ if ( rs->sr_err == LDAP_UNAVAILABLE && retrying ) {
+ retrying &= ~LDAP_BACK_RETRYING;
+ if ( meta_back_retry( op, rs, &mc, candidate, LDAP_BACK_SENDERR ) ) {
+ /* if the identity changed, there might be need to re-authz */
+ (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+ goto retry;
+ }
+ }
+
+cleanup:;
+ (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+
+ for ( --i; i >= 0; --i ) {
+ free( attrs[ i ]->mod_bvalues );
+ free( attrs[ i ] );
+ }
+ free( attrs );
+ if ( mdn.bv_val != op->ora_e->e_dn ) {
+ free( mdn.bv_val );
+ BER_BVZERO( &mdn );
+ }
+
+done:;
+ if ( mc ) {
+ meta_back_release_conn( mi, mc );
+ }
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/back-meta/back-meta.h b/servers/slapd/back-meta/back-meta.h
new file mode 100644
index 0000000..82b2105
--- /dev/null
+++ b/servers/slapd/back-meta/back-meta.h
@@ -0,0 +1,690 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#ifndef SLAPD_LDAP_H
+#error "include servers/slapd/back-ldap/back-ldap.h before this file!"
+#endif /* SLAPD_LDAP_H */
+
+#ifndef SLAPD_META_H
+#define SLAPD_META_H
+
+#define SLAPD_META_CLIENT_PR 1
+
+#include "proto-meta.h"
+
+/* String rewrite library */
+#include "rewrite.h"
+
+LDAP_BEGIN_DECL
+
+/*
+ * Set META_BACK_PRINT_CONNTREE larger than 0 to dump the connection tree (debug only)
+ */
+#ifndef META_BACK_PRINT_CONNTREE
+#define META_BACK_PRINT_CONNTREE 0
+#endif /* !META_BACK_PRINT_CONNTREE */
+
+/* from back-ldap.h before rwm removal */
+struct ldapmap {
+ int drop_missing;
+
+ Avlnode *map;
+ Avlnode *remap;
+};
+
+struct ldapmapping {
+ struct berval src;
+ struct berval dst;
+};
+
+struct ldaprwmap {
+ /*
+ * DN rewriting
+ */
+ struct rewrite_info *rwm_rw;
+ BerVarray rwm_bva_rewrite;
+
+ /*
+ * Attribute/objectClass mapping
+ */
+ struct ldapmap rwm_oc;
+ struct ldapmap rwm_at;
+ BerVarray rwm_bva_map;
+};
+
+/* Whatever context ldap_back_dn_massage needs... */
+typedef struct dncookie {
+ struct metatarget_t *target;
+
+ Connection *conn;
+ char *ctx;
+ SlapReply *rs;
+} dncookie;
+
+int ldap_back_dn_massage(dncookie *dc, struct berval *dn,
+ struct berval *res);
+
+extern int ldap_back_conn_cmp( const void *c1, const void *c2);
+extern int ldap_back_conn_dup( void *c1, void *c2 );
+extern void ldap_back_conn_free( void *c );
+
+/* attributeType/objectClass mapping */
+int mapping_cmp (const void *, const void *);
+int mapping_dup (void *, void *);
+
+void ldap_back_map_init ( struct ldapmap *lm, struct ldapmapping ** );
+int ldap_back_mapping ( struct ldapmap *map, struct berval *s,
+ struct ldapmapping **m, int remap );
+void ldap_back_map ( struct ldapmap *map, struct berval *s, struct berval *m,
+ int remap );
+#define BACKLDAP_MAP 0
+#define BACKLDAP_REMAP 1
+char *
+ldap_back_map_filter(
+ struct ldapmap *at_map,
+ struct ldapmap *oc_map,
+ struct berval *f,
+ int remap );
+
+int
+ldap_back_map_attrs(
+ Operation *op,
+ struct ldapmap *at_map,
+ AttributeName *a,
+ int remap,
+ char ***mapped_attrs );
+
+extern int
+ldap_back_filter_map_rewrite(
+ dncookie *dc,
+ Filter *f,
+ struct berval *fstr,
+ int remap,
+ void *memctx );
+
+/* suffix massaging by means of librewrite */
+extern int
+suffix_massage_config( struct rewrite_info *info,
+ struct berval *pvnc,
+ struct berval *nvnc,
+ struct berval *prnc,
+ struct berval *nrnc );
+extern int
+ldap_back_referral_result_rewrite(
+ dncookie *dc,
+ BerVarray a_vals,
+ void *memctx );
+extern int
+ldap_dnattr_rewrite(
+ dncookie *dc,
+ BerVarray a_vals );
+extern int
+ldap_dnattr_result_rewrite(
+ dncookie *dc,
+ BerVarray a_vals );
+
+/* (end of) from back-ldap.h before rwm removal */
+
+/*
+ * A metasingleconn_t can be in the following, mutually exclusive states:
+ *
+ * - none (0x0U)
+ * - creating META_BACK_FCONN_CREATING
+ * - initialized META_BACK_FCONN_INITED
+ * - binding LDAP_BACK_FCONN_BINDING
+ * - bound/anonymous LDAP_BACK_FCONN_ISBOUND/LDAP_BACK_FCONN_ISANON
+ *
+ * possible modifiers are:
+ *
+ * - privileged LDAP_BACK_FCONN_ISPRIV
+ * - privileged, TLS LDAP_BACK_FCONN_ISTLS
+ * - subjected to idassert LDAP_BACK_FCONN_ISIDASR
+ * - tainted LDAP_BACK_FCONN_TAINTED
+ */
+
+#define META_BACK_FCONN_INITED (0x00100000U)
+#define META_BACK_FCONN_CREATING (0x00200000U)
+
+#define META_BACK_CONN_INITED(lc) LDAP_BACK_CONN_ISSET((lc), META_BACK_FCONN_INITED)
+#define META_BACK_CONN_INITED_SET(lc) LDAP_BACK_CONN_SET((lc), META_BACK_FCONN_INITED)
+#define META_BACK_CONN_INITED_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), META_BACK_FCONN_INITED)
+#define META_BACK_CONN_INITED_CPY(lc, mlc) LDAP_BACK_CONN_CPY((lc), META_BACK_FCONN_INITED, (mlc))
+#define META_BACK_CONN_CREATING(lc) LDAP_BACK_CONN_ISSET((lc), META_BACK_FCONN_CREATING)
+#define META_BACK_CONN_CREATING_SET(lc) LDAP_BACK_CONN_SET((lc), META_BACK_FCONN_CREATING)
+#define META_BACK_CONN_CREATING_CLEAR(lc) LDAP_BACK_CONN_CLEAR((lc), META_BACK_FCONN_CREATING)
+#define META_BACK_CONN_CREATING_CPY(lc, mlc) LDAP_BACK_CONN_CPY((lc), META_BACK_FCONN_CREATING, (mlc))
+
+struct metainfo_t;
+
+#define META_NOT_CANDIDATE ((ber_tag_t)0x0)
+#define META_CANDIDATE ((ber_tag_t)0x1)
+#define META_BINDING ((ber_tag_t)0x2)
+#define META_RETRYING ((ber_tag_t)0x4)
+
+typedef struct metasingleconn_t {
+#define META_CND_ISSET(rs,f) ( ( (rs)->sr_tag & (f) ) == (f) )
+#define META_CND_SET(rs,f) ( (rs)->sr_tag |= (f) )
+#define META_CND_CLEAR(rs,f) ( (rs)->sr_tag &= ~(f) )
+
+#define META_CANDIDATE_RESET(rs) ( (rs)->sr_tag = 0 )
+#define META_IS_CANDIDATE(rs) META_CND_ISSET( (rs), META_CANDIDATE )
+#define META_CANDIDATE_SET(rs) META_CND_SET( (rs), META_CANDIDATE )
+#define META_CANDIDATE_CLEAR(rs) META_CND_CLEAR( (rs), META_CANDIDATE )
+#define META_IS_BINDING(rs) META_CND_ISSET( (rs), META_BINDING )
+#define META_BINDING_SET(rs) META_CND_SET( (rs), META_BINDING )
+#define META_BINDING_CLEAR(rs) META_CND_CLEAR( (rs), META_BINDING )
+#define META_IS_RETRYING(rs) META_CND_ISSET( (rs), META_RETRYING )
+#define META_RETRYING_SET(rs) META_CND_SET( (rs), META_RETRYING )
+#define META_RETRYING_CLEAR(rs) META_CND_CLEAR( (rs), META_RETRYING )
+
+ LDAP *msc_ld;
+ time_t msc_time;
+ struct berval msc_bound_ndn;
+ struct berval msc_cred;
+ unsigned msc_mscflags;
+ /* NOTE: lc_lcflags is redefined to msc_mscflags to reuse the macros
+ * defined for back-ldap */
+#define lc_lcflags msc_mscflags
+} metasingleconn_t;
+
+typedef struct metaconn_t {
+ ldapconn_base_t lc_base;
+#define mc_base lc_base
+#define mc_conn mc_base.lcb_conn
+#define mc_local_ndn mc_base.lcb_local_ndn
+#define mc_refcnt mc_base.lcb_refcnt
+#define mc_create_time mc_base.lcb_create_time
+#define mc_time mc_base.lcb_time
+
+ LDAP_TAILQ_ENTRY(metaconn_t) mc_q;
+
+ /* NOTE: msc_mscflags is used to recycle the #define
+ * in metasingleconn_t */
+ unsigned msc_mscflags;
+
+ /*
+ * means that the connection is bound;
+ * of course only one target actually is ...
+ */
+ int mc_authz_target;
+#define META_BOUND_NONE (-1)
+#define META_BOUND_ALL (-2)
+
+ struct metainfo_t *mc_info;
+
+ /* supersedes the connection stuff */
+ metasingleconn_t mc_conns[ 1 ];
+ /* NOTE: mc_conns must be last, because
+ * the required number of conns is malloc'ed
+ * in one block with the metaconn_t structure */
+} metaconn_t;
+
+typedef enum meta_st_t {
+#if 0 /* todo */
+ META_ST_EXACT = LDAP_SCOPE_BASE,
+#endif
+ META_ST_SUBTREE = LDAP_SCOPE_SUBTREE,
+ META_ST_SUBORDINATE = LDAP_SCOPE_SUBORDINATE,
+ META_ST_REGEX /* last + 1 */
+} meta_st_t;
+
+typedef struct metasubtree_t {
+ meta_st_t ms_type;
+ union {
+ struct berval msu_dn;
+ struct {
+ struct berval msr_regex_pattern;
+ regex_t msr_regex;
+ } msu_regex;
+ } ms_un;
+#define ms_dn ms_un.msu_dn
+#define ms_regex ms_un.msu_regex.msr_regex
+#define ms_regex_pattern ms_un.msu_regex.msr_regex_pattern
+
+ struct metasubtree_t *ms_next;
+} metasubtree_t;
+
+typedef struct metafilter_t {
+ struct metafilter_t *mf_next;
+ struct berval mf_regex_pattern;
+ regex_t mf_regex;
+} metafilter_t;
+
+typedef struct metacommon_t {
+ int mc_version;
+ int mc_nretries;
+#define META_RETRY_UNDEFINED (-2)
+#define META_RETRY_FOREVER (-1)
+#define META_RETRY_NEVER (0)
+#define META_RETRY_DEFAULT (10)
+
+ unsigned mc_flags;
+#define META_BACK_CMN_ISSET(mc,f) ( ( (mc)->mc_flags & (f) ) == (f) )
+#define META_BACK_CMN_QUARANTINE(mc) META_BACK_CMN_ISSET( (mc), LDAP_BACK_F_QUARANTINE )
+#define META_BACK_CMN_CHASE_REFERRALS(mc) META_BACK_CMN_ISSET( (mc), LDAP_BACK_F_CHASE_REFERRALS )
+#define META_BACK_CMN_NOREFS(mc) META_BACK_CMN_ISSET( (mc), LDAP_BACK_F_NOREFS )
+#define META_BACK_CMN_NOUNDEFFILTER(mc) META_BACK_CMN_ISSET( (mc), LDAP_BACK_F_NOUNDEFFILTER )
+#define META_BACK_CMN_SAVECRED(mc) META_BACK_CMN_ISSET( (mc), LDAP_BACK_F_SAVECRED )
+#define META_BACK_CMN_ST_REQUEST(mc) META_BACK_CMN_ISSET( (mc), LDAP_BACK_F_ST_REQUEST )
+
+#ifdef SLAPD_META_CLIENT_PR
+ /*
+ * client-side paged results:
+ * -1: accept unsolicited paged results responses
+ * 0: off
+ * >0: always request paged results with size == mt_ps
+ */
+#define META_CLIENT_PR_DISABLE (0)
+#define META_CLIENT_PR_ACCEPT_UNSOLICITED (-1)
+ ber_int_t mc_ps;
+#endif /* SLAPD_META_CLIENT_PR */
+
+ slap_retry_info_t mc_quarantine;
+ time_t mc_network_timeout;
+ struct timeval mc_bind_timeout;
+#define META_BIND_TIMEOUT LDAP_BACK_RESULT_UTIMEOUT
+ time_t mc_timeout[ SLAP_OP_LAST ];
+} metacommon_t;
+
+typedef struct metatarget_t {
+ char *mt_uri;
+ ldap_pvt_thread_mutex_t mt_uri_mutex;
+
+ /* TODO: we might want to enable different strategies
+ * for different targets */
+ LDAP_REBIND_PROC *mt_rebind_f;
+ LDAP_URLLIST_PROC *mt_urllist_f;
+ void *mt_urllist_p;
+
+ metafilter_t *mt_filter;
+ metasubtree_t *mt_subtree;
+ /* F: subtree-include; T: subtree-exclude */
+ int mt_subtree_exclude;
+
+ int mt_scope;
+
+ struct berval mt_psuffix; /* pretty suffix */
+ struct berval mt_nsuffix; /* normalized suffix */
+
+ struct berval mt_binddn;
+ struct berval mt_bindpw;
+
+ /* we only care about the TLS options here */
+ slap_bindconf mt_tls;
+
+ slap_idassert_t mt_idassert;
+#define mt_idassert_mode mt_idassert.si_mode
+#define mt_idassert_authcID mt_idassert.si_bc.sb_authcId
+#define mt_idassert_authcDN mt_idassert.si_bc.sb_binddn
+#define mt_idassert_passwd mt_idassert.si_bc.sb_cred
+#define mt_idassert_authzID mt_idassert.si_bc.sb_authzId
+#define mt_idassert_authmethod mt_idassert.si_bc.sb_method
+#define mt_idassert_sasl_mech mt_idassert.si_bc.sb_saslmech
+#define mt_idassert_sasl_realm mt_idassert.si_bc.sb_realm
+#define mt_idassert_secprops mt_idassert.si_bc.sb_secprops
+#define mt_idassert_tls mt_idassert.si_bc.sb_tls
+#define mt_idassert_flags mt_idassert.si_flags
+#define mt_idassert_authz mt_idassert.si_authz
+
+ struct ldaprwmap mt_rwmap;
+
+ sig_atomic_t mt_isquarantined;
+ ldap_pvt_thread_mutex_t mt_quarantine_mutex;
+
+ metacommon_t mt_mc;
+#define mt_nretries mt_mc.mc_nretries
+#define mt_flags mt_mc.mc_flags
+#define mt_version mt_mc.mc_version
+#define mt_ps mt_mc.mc_ps
+#define mt_network_timeout mt_mc.mc_network_timeout
+#define mt_bind_timeout mt_mc.mc_bind_timeout
+#define mt_timeout mt_mc.mc_timeout
+#define mt_quarantine mt_mc.mc_quarantine
+
+#define META_BACK_TGT_ISSET(mt,f) ( ( (mt)->mt_flags & (f) ) == (f) )
+#define META_BACK_TGT_ISMASK(mt,m,f) ( ( (mt)->mt_flags & (m) ) == (f) )
+
+#define META_BACK_TGT_SAVECRED(mt) META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_SAVECRED )
+
+#define META_BACK_TGT_USE_TLS(mt) META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_USE_TLS )
+#define META_BACK_TGT_PROPAGATE_TLS(mt) META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_PROPAGATE_TLS )
+#define META_BACK_TGT_TLS_CRITICAL(mt) META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_TLS_CRITICAL )
+
+#define META_BACK_TGT_CHASE_REFERRALS(mt) META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_CHASE_REFERRALS )
+
+#define META_BACK_TGT_T_F(mt) META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_T_F_MASK, LDAP_BACK_F_T_F )
+#define META_BACK_TGT_T_F_DISCOVER(mt) META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_T_F_MASK2, LDAP_BACK_F_T_F_DISCOVER )
+
+#define META_BACK_TGT_ABANDON(mt) META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_CANCEL_MASK, LDAP_BACK_F_CANCEL_ABANDON )
+#define META_BACK_TGT_IGNORE(mt) META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_CANCEL_MASK, LDAP_BACK_F_CANCEL_IGNORE )
+#define META_BACK_TGT_CANCEL(mt) META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_CANCEL_MASK, LDAP_BACK_F_CANCEL_EXOP )
+#define META_BACK_TGT_CANCEL_DISCOVER(mt) META_BACK_TGT_ISMASK( (mt), LDAP_BACK_F_CANCEL_MASK2, LDAP_BACK_F_CANCEL_EXOP_DISCOVER )
+#define META_BACK_TGT_QUARANTINE(mt) META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_QUARANTINE )
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+#define META_BACK_TGT_ST_REQUEST(mt) META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_ST_REQUEST )
+#define META_BACK_TGT_ST_RESPONSE(mt) META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_ST_RESPONSE )
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+#define META_BACK_TGT_NOREFS(mt) META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_NOREFS )
+#define META_BACK_TGT_NOUNDEFFILTER(mt) META_BACK_TGT_ISSET( (mt), LDAP_BACK_F_NOUNDEFFILTER )
+
+ slap_mask_t mt_rep_flags;
+
+} metatarget_t;
+
+typedef struct metadncache_t {
+ ldap_pvt_thread_mutex_t mutex;
+ Avlnode *tree;
+
+#define META_DNCACHE_DISABLED (0)
+#define META_DNCACHE_FOREVER ((time_t)(-1))
+ time_t ttl; /* seconds; 0: no cache, -1: no expiry */
+} metadncache_t;
+
+typedef struct metacandidates_t {
+ int mc_ntargets;
+ SlapReply *mc_candidates;
+} metacandidates_t;
+
+/*
+ * Hook to allow mucking with metainfo_t/metatarget_t when quarantine is over
+ */
+typedef int (*meta_back_quarantine_f)( struct metainfo_t *, int target, void * );
+
+typedef struct metainfo_t {
+ int mi_ntargets;
+ int mi_defaulttarget;
+#define META_DEFAULT_TARGET_NONE (-1)
+
+#define mi_nretries mi_mc.mc_nretries
+#define mi_flags mi_mc.mc_flags
+#define mi_version mi_mc.mc_version
+#define mi_ps mi_mc.mc_ps
+#define mi_network_timeout mi_mc.mc_network_timeout
+#define mi_bind_timeout mi_mc.mc_bind_timeout
+#define mi_timeout mi_mc.mc_timeout
+#define mi_quarantine mi_mc.mc_quarantine
+
+ metatarget_t **mi_targets;
+ metacandidates_t *mi_candidates;
+
+ LDAP_REBIND_PROC *mi_rebind_f;
+ LDAP_URLLIST_PROC *mi_urllist_f;
+
+ metadncache_t mi_cache;
+
+ /* cached connections;
+ * special conns are in tailq rather than in tree */
+ ldap_avl_info_t mi_conninfo;
+ struct {
+ int mic_num;
+ LDAP_TAILQ_HEAD(mc_conn_priv_q, metaconn_t) mic_priv;
+ } mi_conn_priv[ LDAP_BACK_PCONN_LAST ];
+ int mi_conn_priv_max;
+
+ /* NOTE: quarantine uses the connection mutex */
+ meta_back_quarantine_f mi_quarantine_f;
+ void *mi_quarantine_p;
+
+#define li_flags mi_flags
+/* uses flags as defined in <back-ldap/back-ldap.h> */
+#define META_BACK_F_ONERR_STOP LDAP_BACK_F_ONERR_STOP
+#define META_BACK_F_ONERR_REPORT (0x02000000U)
+#define META_BACK_F_ONERR_MASK (META_BACK_F_ONERR_STOP|META_BACK_F_ONERR_REPORT)
+#define META_BACK_F_DEFER_ROOTDN_BIND (0x04000000U)
+#define META_BACK_F_PROXYAUTHZ_ALWAYS (0x08000000U) /* users always proxyauthz */
+#define META_BACK_F_PROXYAUTHZ_ANON (0x10000000U) /* anonymous always proxyauthz */
+#define META_BACK_F_PROXYAUTHZ_NOANON (0x20000000U) /* anonymous remains anonymous */
+
+#define META_BACK_ONERR_STOP(mi) LDAP_BACK_ISSET( (mi), META_BACK_F_ONERR_STOP )
+#define META_BACK_ONERR_REPORT(mi) LDAP_BACK_ISSET( (mi), META_BACK_F_ONERR_REPORT )
+#define META_BACK_ONERR_CONTINUE(mi) ( !LDAP_BACK_ISSET( (mi), META_BACK_F_ONERR_MASK ) )
+
+#define META_BACK_DEFER_ROOTDN_BIND(mi) LDAP_BACK_ISSET( (mi), META_BACK_F_DEFER_ROOTDN_BIND )
+#define META_BACK_PROXYAUTHZ_ALWAYS(mi) LDAP_BACK_ISSET( (mi), META_BACK_F_PROXYAUTHZ_ALWAYS )
+#define META_BACK_PROXYAUTHZ_ANON(mi) LDAP_BACK_ISSET( (mi), META_BACK_F_PROXYAUTHZ_ANON )
+#define META_BACK_PROXYAUTHZ_NOANON(mi) LDAP_BACK_ISSET( (mi), META_BACK_F_PROXYAUTHZ_NOANON )
+
+#define META_BACK_QUARANTINE(mi) LDAP_BACK_ISSET( (mi), LDAP_BACK_F_QUARANTINE )
+
+ time_t mi_conn_ttl;
+ time_t mi_idle_timeout;
+
+ metacommon_t mi_mc;
+ ldap_extra_t *mi_ldap_extra;
+
+} metainfo_t;
+
+typedef enum meta_op_type {
+ META_OP_ALLOW_MULTIPLE = 0,
+ META_OP_REQUIRE_SINGLE,
+ META_OP_REQUIRE_ALL
+} meta_op_type;
+
+SlapReply *
+meta_back_candidates_get( Operation *op );
+
+extern metaconn_t *
+meta_back_getconn(
+ Operation *op,
+ SlapReply *rs,
+ int *candidate,
+ ldap_back_send_t sendok );
+
+extern void
+meta_back_release_conn_lock(
+ metainfo_t *mi,
+ metaconn_t *mc,
+ int dolock );
+#define meta_back_release_conn(mi, mc) meta_back_release_conn_lock( (mi), (mc), 1 )
+
+extern int
+meta_back_retry(
+ Operation *op,
+ SlapReply *rs,
+ metaconn_t **mcp,
+ int candidate,
+ ldap_back_send_t sendok );
+
+extern void
+meta_back_conn_free(
+ void *v_mc );
+
+#if META_BACK_PRINT_CONNTREE > 0
+extern void
+meta_back_print_conntree(
+ metainfo_t *mi,
+ char *msg );
+#endif
+
+extern int
+meta_back_init_one_conn(
+ Operation *op,
+ SlapReply *rs,
+ metaconn_t *mc,
+ int candidate,
+ int ispriv,
+ ldap_back_send_t sendok,
+ int dolock );
+
+extern void
+meta_back_quarantine(
+ Operation *op,
+ SlapReply *rs,
+ int candidate );
+
+extern int
+meta_back_dobind(
+ Operation *op,
+ SlapReply *rs,
+ metaconn_t *mc,
+ ldap_back_send_t sendok );
+
+extern int
+meta_back_single_dobind(
+ Operation *op,
+ SlapReply *rs,
+ metaconn_t **mcp,
+ int candidate,
+ ldap_back_send_t sendok,
+ int retries,
+ int dolock );
+
+extern int
+meta_back_proxy_authz_cred(
+ metaconn_t *mc,
+ int candidate,
+ Operation *op,
+ SlapReply *rs,
+ ldap_back_send_t sendok,
+ struct berval *binddn,
+ struct berval *bindcred,
+ int *method );
+
+extern int
+meta_back_cancel(
+ metaconn_t *mc,
+ Operation *op,
+ SlapReply *rs,
+ ber_int_t msgid,
+ int candidate,
+ ldap_back_send_t sendok );
+
+extern int
+meta_back_op_result(
+ metaconn_t *mc,
+ Operation *op,
+ SlapReply *rs,
+ int candidate,
+ ber_int_t msgid,
+ time_t timeout,
+ ldap_back_send_t sendok );
+
+extern int
+meta_back_controls_add(
+ Operation *op,
+ SlapReply *rs,
+ metaconn_t *mc,
+ int candidate,
+ LDAPControl ***pctrls );
+
+extern int
+back_meta_LTX_init_module(
+ int argc,
+ char *argv[] );
+
+extern int
+meta_back_conn_cmp(
+ const void *c1,
+ const void *c2 );
+
+extern int
+meta_back_conndn_cmp(
+ const void *c1,
+ const void *c2 );
+
+extern int
+meta_back_conndn_dup(
+ void *c1,
+ void *c2 );
+
+/*
+ * Candidate stuff
+ */
+extern int
+meta_back_is_candidate(
+ metatarget_t *mt,
+ struct berval *ndn,
+ int scope );
+
+extern int
+meta_back_select_unique_candidate(
+ metainfo_t *mi,
+ struct berval *ndn );
+
+extern int
+meta_clear_unused_candidates(
+ Operation *op,
+ int candidate );
+
+extern int
+meta_clear_one_candidate(
+ Operation *op,
+ metaconn_t *mc,
+ int candidate );
+
+/*
+ * Dn cache stuff (experimental)
+ */
+extern int
+meta_dncache_cmp(
+ const void *c1,
+ const void *c2 );
+
+extern int
+meta_dncache_dup(
+ void *c1,
+ void *c2 );
+
+#define META_TARGET_NONE (-1)
+#define META_TARGET_MULTIPLE (-2)
+extern int
+meta_dncache_get_target(
+ metadncache_t *cache,
+ struct berval *ndn );
+
+extern int
+meta_dncache_update_entry(
+ metadncache_t *cache,
+ struct berval *ndn,
+ int target );
+
+extern int
+meta_dncache_delete_entry(
+ metadncache_t *cache,
+ struct berval *ndn );
+
+extern void
+meta_dncache_free( void *entry );
+
+extern void
+meta_back_map_free( struct ldapmap *lm );
+
+extern int
+meta_subtree_destroy( metasubtree_t *ms );
+
+extern void
+meta_filter_destroy( metafilter_t *mf );
+
+extern int
+meta_target_finish( metainfo_t *mi, metatarget_t *mt,
+ const char *log, char *msg, size_t msize
+);
+
+extern LDAP_REBIND_PROC meta_back_default_rebind;
+extern LDAP_URLLIST_PROC meta_back_default_urllist;
+
+LDAP_END_DECL
+
+#endif /* SLAPD_META_H */
+
diff --git a/servers/slapd/back-meta/bind.c b/servers/slapd/back-meta/bind.c
new file mode 100644
index 0000000..edfabbb
--- /dev/null
+++ b/servers/slapd/back-meta/bind.c
@@ -0,0 +1,1758 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+
+
+#define AVL_INTERNAL
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+#include "lutil_ldap.h"
+
+static int
+meta_back_proxy_authz_bind(
+ metaconn_t *mc,
+ int candidate,
+ Operation *op,
+ SlapReply *rs,
+ ldap_back_send_t sendok,
+ int dolock );
+
+static int
+meta_back_single_bind(
+ Operation *op,
+ SlapReply *rs,
+ metaconn_t *mc,
+ int candidate );
+
+int
+meta_back_bind( Operation *op, SlapReply *rs )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ metaconn_t *mc = NULL;
+
+ int rc = LDAP_OTHER,
+ i,
+ gotit = 0,
+ isroot = 0;
+
+ SlapReply *candidates;
+
+ rs->sr_err = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_ARGS, "%s meta_back_bind: dn=\"%s\".\n",
+ op->o_log_prefix, op->o_req_dn.bv_val );
+
+ /* the test on the bind method should be superfluous */
+ switch ( be_rootdn_bind( op, rs ) ) {
+ case LDAP_SUCCESS:
+ if ( META_BACK_DEFER_ROOTDN_BIND( mi ) ) {
+ /* frontend will return success */
+ return rs->sr_err;
+ }
+
+ isroot = 1;
+ /* fallthru */
+
+ case SLAP_CB_CONTINUE:
+ break;
+
+ default:
+ /* be_rootdn_bind() sent result */
+ return rs->sr_err;
+ }
+
+ /* we need meta_back_getconn() not send result even on error,
+ * because we want to intercept the error and make it
+ * invalidCredentials */
+ mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_BIND_DONTSEND );
+ if ( !mc ) {
+ Debug(LDAP_DEBUG_ANY,
+ "%s meta_back_bind: no target " "for dn \"%s\" (%d%s%s).\n",
+ op->o_log_prefix, op->o_req_dn.bv_val,
+ rs->sr_err, rs->sr_text ? ". " : "",
+ rs->sr_text ? rs->sr_text : "" );
+
+ /* FIXME: there might be cases where we don't want
+ * to map the error onto invalidCredentials */
+ switch ( rs->sr_err ) {
+ case LDAP_NO_SUCH_OBJECT:
+ case LDAP_UNWILLING_TO_PERFORM:
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ rs->sr_text = NULL;
+ break;
+ }
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+
+ candidates = meta_back_candidates_get( op );
+
+ /*
+ * Each target is scanned ...
+ */
+ mc->mc_authz_target = META_BOUND_NONE;
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ metatarget_t *mt = mi->mi_targets[ i ];
+ int lerr;
+
+ /*
+ * Skip non-candidates
+ */
+ if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
+ continue;
+ }
+
+ if ( gotit == 0 ) {
+ /* set rc to LDAP_SUCCESS only if at least
+ * one candidate has been tried */
+ rc = LDAP_SUCCESS;
+ gotit = 1;
+
+ } else if ( !isroot ) {
+ /*
+ * A bind operation is expected to have
+ * ONE CANDIDATE ONLY!
+ */
+ Debug( LDAP_DEBUG_ANY,
+ "### %s meta_back_bind: more than one"
+ " candidate selected...\n",
+ op->o_log_prefix );
+ }
+
+ if ( isroot ) {
+ if ( mt->mt_idassert_authmethod == LDAP_AUTH_NONE
+ || BER_BVISNULL( &mt->mt_idassert_authcDN ) )
+ {
+ metasingleconn_t *msc = &mc->mc_conns[ i ];
+
+ /* skip the target if no pseudorootdn is provided */
+ if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
+ ch_free( msc->msc_bound_ndn.bv_val );
+ BER_BVZERO( &msc->msc_bound_ndn );
+ }
+
+ if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+ /* destroy sensitive data */
+ memset( msc->msc_cred.bv_val, 0,
+ msc->msc_cred.bv_len );
+ ch_free( msc->msc_cred.bv_val );
+ BER_BVZERO( &msc->msc_cred );
+ }
+
+ continue;
+ }
+
+
+ (void)meta_back_proxy_authz_bind( mc, i, op, rs, LDAP_BACK_DONTSEND, 1 );
+ lerr = rs->sr_err;
+
+ } else {
+ lerr = meta_back_single_bind( op, rs, mc, i );
+ }
+
+ if ( lerr != LDAP_SUCCESS ) {
+ rc = rs->sr_err = lerr;
+
+ /* FIXME: in some cases (e.g. unavailable)
+ * do not assume it's not candidate; rather
+ * mark this as an error to be eventually
+ * reported to client */
+ META_CANDIDATE_CLEAR( &candidates[ i ] );
+ break;
+ }
+ }
+
+ /* must re-insert if local DN changed as result of bind */
+ if ( rc == LDAP_SUCCESS ) {
+ if ( isroot ) {
+ mc->mc_authz_target = META_BOUND_ALL;
+ }
+
+ if ( !LDAP_BACK_PCONN_ISPRIV( mc )
+ && !dn_match( &op->o_req_ndn, &mc->mc_local_ndn ) )
+ {
+ int lerr;
+
+ /* wait for all other ops to release the connection */
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ assert( mc->mc_refcnt == 1 );
+#if META_BACK_PRINT_CONNTREE > 0
+ meta_back_print_conntree( mi, ">>> meta_back_bind" );
+#endif /* META_BACK_PRINT_CONNTREE */
+
+ /* delete all cached connections with the current connection */
+ if ( LDAP_BACK_SINGLECONN( mi ) ) {
+ metaconn_t *tmpmc;
+
+ while ( ( tmpmc = ldap_tavl_delete( &mi->mi_conninfo.lai_tree, (caddr_t)mc, meta_back_conn_cmp ) ) != NULL )
+ {
+ assert( !LDAP_BACK_PCONN_ISPRIV( mc ) );
+ Debug( LDAP_DEBUG_TRACE,
+ "=>meta_back_bind: destroying conn %lu (refcnt=%u)\n",
+ mc->mc_conn->c_connid, mc->mc_refcnt );
+
+ if ( tmpmc->mc_refcnt != 0 ) {
+ /* taint it */
+ LDAP_BACK_CONN_TAINTED_SET( tmpmc );
+
+ } else {
+ /*
+ * Needs a test because the handler may be corrupted,
+ * and calling ldap_unbind on a corrupted header results
+ * in a segmentation fault
+ */
+ meta_back_conn_free( tmpmc );
+ }
+ }
+ }
+
+ ber_bvreplace( &mc->mc_local_ndn, &op->o_req_ndn );
+ lerr = ldap_tavl_insert( &mi->mi_conninfo.lai_tree, (caddr_t)mc,
+ meta_back_conndn_cmp, meta_back_conndn_dup );
+#if META_BACK_PRINT_CONNTREE > 0
+ meta_back_print_conntree( mi, "<<< meta_back_bind" );
+#endif /* META_BACK_PRINT_CONNTREE */
+ if ( lerr == 0 ) {
+#if 0
+ /* NOTE: a connection cannot be privileged
+ * and be in the avl tree at the same time
+ */
+ if ( isroot ) {
+ LDAP_BACK_CONN_ISPRIV_SET( mc );
+ LDAP_BACK_PCONN_SET( mc, op );
+ }
+#endif
+ LDAP_BACK_CONN_CACHED_SET( mc );
+
+ } else {
+ LDAP_BACK_CONN_CACHED_CLEAR( mc );
+ }
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ }
+ }
+
+ if ( mc != NULL ) {
+ meta_back_release_conn( mi, mc );
+ }
+
+ /*
+ * rc is LDAP_SUCCESS if at least one bind succeeded,
+ * err is the last error that occurred during a bind;
+ * if at least (and at most?) one bind succeeds, fine.
+ */
+ if ( rc != LDAP_SUCCESS ) {
+
+ /*
+ * deal with bind failure ...
+ */
+
+ /*
+ * no target was found within the naming context,
+ * so bind must fail with invalid credentials
+ */
+ if ( rs->sr_err == LDAP_SUCCESS && gotit == 0 ) {
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ } else {
+ rs->sr_err = slap_map_api2result( rs );
+ }
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+meta_back_bind_op_result(
+ Operation *op,
+ SlapReply *rs,
+ metaconn_t *mc,
+ int candidate,
+ int msgid,
+ ldap_back_send_t sendok,
+ int dolock )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ metatarget_t *mt = mi->mi_targets[ candidate ];
+ metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+ LDAPMessage *res;
+ struct timeval tv;
+ int rc;
+ int nretries = mt->mt_nretries;
+
+ Debug( LDAP_DEBUG_TRACE,
+ ">>> %s meta_back_bind_op_result[%d]\n",
+ op->o_log_prefix, candidate );
+
+ /* make sure this is clean */
+ assert( rs->sr_ctrls == NULL );
+
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ time_t stoptime = (time_t)(-1),
+ timeout;
+ int timeout_err = op->o_protocol >= LDAP_VERSION3 ?
+ LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+ const char *timeout_text = "Operation timed out";
+ slap_op_t opidx = slap_req2op( op->o_tag );
+
+ /* since timeout is not specified, compute and use
+ * the one specific to the ongoing operation */
+ if ( opidx == LDAP_REQ_SEARCH ) {
+ if ( op->ors_tlimit <= 0 ) {
+ timeout = 0;
+
+ } else {
+ timeout = op->ors_tlimit;
+ timeout_err = LDAP_TIMELIMIT_EXCEEDED;
+ timeout_text = NULL;
+ }
+
+ } else {
+ timeout = mt->mt_timeout[ opidx ];
+ }
+
+ /* better than nothing :) */
+ if ( timeout == 0 ) {
+ if ( mi->mi_idle_timeout ) {
+ timeout = mi->mi_idle_timeout;
+
+ } else if ( mi->mi_conn_ttl ) {
+ timeout = mi->mi_conn_ttl;
+ }
+ }
+
+ if ( timeout ) {
+ stoptime = op->o_time + timeout;
+ }
+
+ LDAP_BACK_TV_SET( &tv );
+
+ /*
+ * handle response!!!
+ */
+retry:;
+ rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
+ switch ( rc ) {
+ case 0:
+ if ( nretries != META_RETRY_NEVER
+ || ( timeout && slap_get_time() <= stoptime ) )
+ {
+ ldap_pvt_thread_yield();
+ if ( nretries > 0 ) {
+ nretries--;
+ }
+ tv = mt->mt_bind_timeout;
+ goto retry;
+ }
+
+ /* don't let anyone else use this handler,
+ * because there's a pending bind that will not
+ * be acknowledged */
+ if ( dolock) {
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ }
+ assert( LDAP_BACK_CONN_BINDING( msc ) );
+
+#ifdef DEBUG_205
+ Debug( LDAP_DEBUG_ANY, "### %s meta_back_bind_op_result ldap_unbind_ext[%d] ld=%p\n",
+ op->o_log_prefix, candidate, (void *)msc->msc_ld );
+#endif /* DEBUG_205 */
+
+ meta_clear_one_candidate( op, mc, candidate );
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ }
+
+ rs->sr_err = timeout_err;
+ rs->sr_text = timeout_text;
+ break;
+
+ case -1:
+ ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER,
+ &rs->sr_err );
+
+ Debug(LDAP_DEBUG_ANY,
+ "### %s meta_back_bind_op_result[%d]: err=%d (%s) nretries=%d.\n",
+ op->o_log_prefix, candidate, rs->sr_err,
+ ldap_err2string(rs->sr_err), nretries );
+ break;
+
+ default:
+ /* only touch when activity actually took place... */
+ if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
+ msc->msc_time = op->o_time;
+ }
+
+ /* FIXME: matched? referrals? response controls? */
+ rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
+ NULL, NULL, NULL, NULL, 1 );
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_err = rc;
+ }
+ rs->sr_err = slap_map_api2result( rs );
+ break;
+ }
+ }
+
+ rs->sr_err = slap_map_api2result( rs );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<<< %s meta_back_bind_op_result[%d] err=%d\n",
+ op->o_log_prefix, candidate, rs->sr_err );
+
+ return rs->sr_err;
+}
+
+/*
+ * meta_back_single_bind
+ *
+ * attempts to perform a bind with creds
+ */
+static int
+meta_back_single_bind(
+ Operation *op,
+ SlapReply *rs,
+ metaconn_t *mc,
+ int candidate )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ metatarget_t *mt = mi->mi_targets[ candidate ];
+ struct berval mdn = BER_BVNULL;
+ metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+ int msgid;
+ dncookie dc;
+ struct berval save_o_dn;
+ int save_o_do_not_cache;
+ LDAPControl **ctrls = NULL;
+
+ if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
+ ch_free( msc->msc_bound_ndn.bv_val );
+ BER_BVZERO( &msc->msc_bound_ndn );
+ }
+
+ if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+ /* destroy sensitive data */
+ memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
+ ch_free( msc->msc_cred.bv_val );
+ BER_BVZERO( &msc->msc_cred );
+ }
+
+ /*
+ * Rewrite the bind dn if needed
+ */
+ dc.target = mt;
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+ dc.ctx = "bindDN";
+
+ if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
+ rs->sr_text = "DN rewrite error";
+ rs->sr_err = LDAP_OTHER;
+ return rs->sr_err;
+ }
+
+ /* don't add proxyAuthz; set the bindDN */
+ save_o_dn = op->o_dn;
+ save_o_do_not_cache = op->o_do_not_cache;
+ op->o_do_not_cache = 1;
+ op->o_dn = op->o_req_dn;
+
+ ctrls = op->o_ctrls;
+ rs->sr_err = meta_back_controls_add( op, rs, mc, candidate, &ctrls );
+ op->o_dn = save_o_dn;
+ op->o_do_not_cache = save_o_do_not_cache;
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto return_results;
+ }
+
+ /* FIXME: this fixes the bind problem right now; we need
+ * to use the asynchronous version to get the "matched"
+ * and more in case of failure ... */
+ /* FIXME: should we check if at least some of the op->o_ctrls
+ * can/should be passed? */
+ for (;;) {
+ rs->sr_err = ldap_sasl_bind( msc->msc_ld, mdn.bv_val,
+ LDAP_SASL_SIMPLE, &op->orb_cred,
+ ctrls, NULL, &msgid );
+ if ( rs->sr_err != LDAP_X_CONNECTING ) {
+ break;
+ }
+ ldap_pvt_thread_yield();
+ }
+
+ mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+
+ meta_back_bind_op_result( op, rs, mc, candidate, msgid, LDAP_BACK_DONTSEND, 1 );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto return_results;
+ }
+
+ /* If defined, proxyAuthz will be used also when
+ * back-ldap is the authorizing backend; for this
+ * purpose, a successful bind is followed by a
+ * bind with the configured identity assertion */
+ /* NOTE: use with care */
+ if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
+ meta_back_proxy_authz_bind( mc, candidate, op, rs, LDAP_BACK_SENDERR, 1 );
+ if ( !LDAP_BACK_CONN_ISBOUND( msc ) ) {
+ goto return_results;
+ }
+ goto cache_refresh;
+ }
+
+ ber_bvreplace( &msc->msc_bound_ndn, &op->o_req_ndn );
+ LDAP_BACK_CONN_ISBOUND_SET( msc );
+ mc->mc_authz_target = candidate;
+
+ if ( META_BACK_TGT_SAVECRED( mt ) ) {
+ if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+ memset( msc->msc_cred.bv_val, 0,
+ msc->msc_cred.bv_len );
+ }
+ ber_bvreplace( &msc->msc_cred, &op->orb_cred );
+ ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc );
+ }
+
+cache_refresh:;
+ if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED
+ && !BER_BVISEMPTY( &op->o_req_ndn ) )
+ {
+ ( void )meta_dncache_update_entry( &mi->mi_cache,
+ &op->o_req_ndn, candidate );
+ }
+
+return_results:;
+ if ( mdn.bv_val != op->o_req_dn.bv_val ) {
+ free( mdn.bv_val );
+ }
+
+ if ( META_BACK_TGT_QUARANTINE( mt ) ) {
+ meta_back_quarantine( op, rs, candidate );
+ }
+
+ return rs->sr_err;
+}
+
+/*
+ * meta_back_single_dobind
+ */
+int
+meta_back_single_dobind(
+ Operation *op,
+ SlapReply *rs,
+ metaconn_t **mcp,
+ int candidate,
+ ldap_back_send_t sendok,
+ int nretries,
+ int dolock )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ metatarget_t *mt = mi->mi_targets[ candidate ];
+ metaconn_t *mc = *mcp;
+ metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+ int msgid;
+
+ assert( !LDAP_BACK_CONN_ISBOUND( msc ) );
+
+ /* NOTE: this obsoletes pseudorootdn */
+ if ( op->o_conn != NULL &&
+ !op->o_do_not_cache &&
+ ( BER_BVISNULL( &msc->msc_bound_ndn ) ||
+ BER_BVISEMPTY( &msc->msc_bound_ndn ) ||
+ ( LDAP_BACK_CONN_ISPRIV( mc ) && dn_match( &msc->msc_bound_ndn, &mt->mt_idassert_authcDN ) ) ||
+ ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) )
+ {
+ (void)meta_back_proxy_authz_bind( mc, candidate, op, rs, sendok, dolock );
+
+ } else {
+ char *binddn = "";
+ struct berval cred = BER_BVC( "" );
+
+ /* use credentials if available */
+ if ( !BER_BVISNULL( &msc->msc_bound_ndn )
+ && !BER_BVISNULL( &msc->msc_cred ) )
+ {
+ binddn = msc->msc_bound_ndn.bv_val;
+ cred = msc->msc_cred;
+ }
+
+ /* FIXME: should we check if at least some of the op->o_ctrls
+ * can/should be passed? */
+ if(!dolock) {
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ }
+
+ for (;;) {
+ rs->sr_err = ldap_sasl_bind( msc->msc_ld,
+ binddn, LDAP_SASL_SIMPLE, &cred,
+ NULL, NULL, &msgid );
+ if ( rs->sr_err != LDAP_X_CONNECTING ) {
+ break;
+ }
+ ldap_pvt_thread_yield();
+ }
+
+ if(!dolock) {
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ }
+
+ rs->sr_err = meta_back_bind_op_result( op, rs, mc, candidate, msgid, sendok, dolock );
+
+ /* if bind succeeded, but anonymous, clear msc_bound_ndn */
+ if ( rs->sr_err != LDAP_SUCCESS || binddn[0] == '\0' ) {
+ if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
+ ber_memfree( msc->msc_bound_ndn.bv_val );
+ BER_BVZERO( &msc->msc_bound_ndn );
+ }
+
+ if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+ memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
+ ber_memfree( msc->msc_cred.bv_val );
+ BER_BVZERO( &msc->msc_cred );
+ }
+ }
+ }
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ }
+ LDAP_BACK_CONN_BINDING_CLEAR( msc );
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ LDAP_BACK_CONN_TAINTED_SET( mc );
+ meta_back_release_conn_lock( mi, mc, 0 );
+ *mcp = NULL;
+ }
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ }
+ }
+
+ if ( META_BACK_TGT_QUARANTINE( mt ) ) {
+ meta_back_quarantine( op, rs, candidate );
+ }
+
+ return rs->sr_err;
+}
+
+/*
+ * meta_back_dobind
+ */
+int
+meta_back_dobind(
+ Operation *op,
+ SlapReply *rs,
+ metaconn_t *mc,
+ ldap_back_send_t sendok )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+
+ int bound = 0,
+ i,
+ isroot = 0;
+
+ SlapReply *candidates;
+
+ if ( be_isroot( op ) ) {
+ isroot = 1;
+ }
+
+ if ( LogTest( LDAP_DEBUG_TRACE ) ) {
+ char buf[STRLENOF("4294967295U") + 1] = { 0 };
+ mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "%s meta_back_dobind: conn=%s%s\n",
+ op->o_log_prefix, buf,
+ isroot ? " (isroot)" : "" );
+ }
+
+ /*
+ * all the targets are bound as pseudoroot
+ */
+ if ( mc->mc_authz_target == META_BOUND_ALL ) {
+ bound = 1;
+ goto done;
+ }
+
+ candidates = meta_back_candidates_get( op );
+
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ metatarget_t *mt = mi->mi_targets[ i ];
+ metasingleconn_t *msc = &mc->mc_conns[ i ];
+ int rc;
+
+ /*
+ * Not a candidate
+ */
+ if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
+ continue;
+ }
+
+ assert( msc->msc_ld != NULL );
+
+ /*
+ * If the target is already bound it is skipped
+ */
+
+retry_binding:;
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ if ( LDAP_BACK_CONN_ISBOUND( msc )
+ || ( LDAP_BACK_CONN_ISANON( msc )
+ && mt->mt_idassert_authmethod == LDAP_AUTH_NONE ) )
+ {
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ ++bound;
+ continue;
+
+ } else if ( META_BACK_CONN_CREATING( msc ) || LDAP_BACK_CONN_BINDING( msc ) )
+ {
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ ldap_pvt_thread_yield();
+ goto retry_binding;
+
+ }
+
+ LDAP_BACK_CONN_BINDING_SET( msc );
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+
+ rc = meta_back_single_dobind( op, rs, &mc, i,
+ LDAP_BACK_DONTSEND, mt->mt_nretries, 1 );
+ /*
+ * NOTE: meta_back_single_dobind() already retries;
+ * in case of failure, it resets mc...
+ */
+ if ( rc != LDAP_SUCCESS ) {
+ if ( mc == NULL ) {
+ /* meta_back_single_dobind() already sent
+ * response and released connection */
+ goto send_err;
+ }
+
+ if ( rc == LDAP_UNAVAILABLE ) {
+ /* FIXME: meta_back_retry() already re-calls
+ * meta_back_single_dobind() */
+ if ( meta_back_retry( op, rs, &mc, i, sendok ) ) {
+ goto retry_ok;
+ }
+
+ if ( mc != NULL ) {
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ LDAP_BACK_CONN_BINDING_CLEAR( msc );
+ meta_back_release_conn_lock( mi, mc, 0 );
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ }
+
+ return 0;
+ }
+
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ LDAP_BACK_CONN_BINDING_CLEAR( msc );
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+
+ Debug(LDAP_DEBUG_ANY,
+ "%s meta_back_dobind[%d]: (%s) err=%d (%s).\n",
+ op->o_log_prefix, i,
+ isroot ? op->o_bd->be_rootdn.bv_val : "anonymous",
+ rc, ldap_err2string(rc) );
+
+ /*
+ * null cred bind should always succeed
+ * as anonymous, so a failure means
+ * the target is no longer candidate possibly
+ * due to technical reasons (remote host down?)
+ * so better clear the handle
+ */
+ /* leave the target candidate, but record the error for later use */
+ candidates[ i ].sr_err = rc;
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ bound = 0;
+ goto done;
+ }
+
+ continue;
+ } /* else */
+
+retry_ok:;
+ Debug( LDAP_DEBUG_TRACE,
+ "%s meta_back_dobind[%d]: "
+ "(%s)\n",
+ op->o_log_prefix, i,
+ isroot ? op->o_bd->be_rootdn.bv_val : "anonymous" );
+
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ LDAP_BACK_CONN_BINDING_CLEAR( msc );
+ if ( isroot ) {
+ LDAP_BACK_CONN_ISBOUND_SET( msc );
+ } else {
+ LDAP_BACK_CONN_ISANON_SET( msc );
+ }
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ ++bound;
+ }
+
+done:;
+ if ( LogTest( LDAP_DEBUG_TRACE ) ) {
+ char buf[STRLENOF("4294967295U") + 1] = { 0 };
+ mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "%s meta_back_dobind: conn=%s bound=%d\n",
+ op->o_log_prefix, buf, bound );
+ }
+
+ if ( bound == 0 ) {
+ meta_back_release_conn( mi, mc );
+
+send_err:;
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ rs->sr_err = LDAP_BUSY;
+ }
+ send_ldap_result( op, rs );
+ }
+
+ return 0;
+ }
+
+ return ( bound > 0 );
+}
+
+/*
+ * meta_back_default_rebind
+ *
+ * This is a callback used for chasing referrals using the same
+ * credentials as the original user on this session.
+ */
+int
+meta_back_default_rebind(
+ LDAP *ld,
+ LDAP_CONST char *url,
+ ber_tag_t request,
+ ber_int_t msgid,
+ void *params )
+{
+ metasingleconn_t *msc = ( metasingleconn_t * )params;
+
+ return ldap_sasl_bind_s( ld, msc->msc_bound_ndn.bv_val,
+ LDAP_SASL_SIMPLE, &msc->msc_cred,
+ NULL, NULL, NULL );
+}
+
+/*
+ * meta_back_default_urllist
+ *
+ * This is a callback used for mucking with the urllist
+ */
+int
+meta_back_default_urllist(
+ LDAP *ld,
+ LDAPURLDesc **urllist,
+ LDAPURLDesc **url,
+ void *params )
+{
+ metatarget_t *mt = (metatarget_t *)params;
+ LDAPURLDesc **urltail;
+
+ if ( urllist == url ) {
+ return LDAP_SUCCESS;
+ }
+
+ for ( urltail = &(*url)->lud_next; *urltail; urltail = &(*urltail)->lud_next )
+ /* count */ ;
+
+ *urltail = *urllist;
+ *urllist = *url;
+ *url = NULL;
+
+ ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
+ if ( mt->mt_uri ) {
+ ch_free( mt->mt_uri );
+ }
+
+ ldap_get_option( ld, LDAP_OPT_URI, (void *)&mt->mt_uri );
+ ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
+
+ return LDAP_SUCCESS;
+}
+
+int
+meta_back_cancel(
+ metaconn_t *mc,
+ Operation *op,
+ SlapReply *rs,
+ ber_int_t msgid,
+ int candidate,
+ ldap_back_send_t sendok )
+{
+ metainfo_t *mi = (metainfo_t *)op->o_bd->be_private;
+
+ metatarget_t *mt = mi->mi_targets[ candidate ];
+ metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+
+ int rc = LDAP_OTHER;
+
+ Debug( LDAP_DEBUG_TRACE, ">>> %s meta_back_cancel[%d] msgid=%d\n",
+ op->o_log_prefix, candidate, msgid );
+
+ /* default behavior */
+ if ( META_BACK_TGT_ABANDON( mt ) ) {
+ rc = ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
+
+ } else if ( META_BACK_TGT_IGNORE( mt ) ) {
+ rc = ldap_pvt_discard( msc->msc_ld, msgid );
+
+ } else if ( META_BACK_TGT_CANCEL( mt ) ) {
+ rc = ldap_cancel_s( msc->msc_ld, msgid, NULL, NULL );
+
+ } else {
+ assert( 0 );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<<< %s meta_back_cancel[%d] err=%d\n",
+ op->o_log_prefix, candidate, rc );
+
+ return rc;
+}
+
+
+
+/*
+ * FIXME: error return must be handled in a cleaner way ...
+ */
+int
+meta_back_op_result(
+ metaconn_t *mc,
+ Operation *op,
+ SlapReply *rs,
+ int candidate,
+ ber_int_t msgid,
+ time_t timeout,
+ ldap_back_send_t sendok )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+
+ const char *save_text = rs->sr_text,
+ *save_matched = rs->sr_matched;
+ BerVarray save_ref = rs->sr_ref;
+ LDAPControl **save_ctrls = rs->sr_ctrls;
+ void *matched_ctx = NULL;
+
+ char *matched = NULL;
+ char *text = NULL;
+ char **refs = NULL;
+ LDAPControl **ctrls = NULL;
+
+ assert( mc != NULL );
+
+ rs->sr_text = NULL;
+ rs->sr_matched = NULL;
+ rs->sr_ref = NULL;
+ rs->sr_ctrls = NULL;
+
+ if ( candidate != META_TARGET_NONE ) {
+ metatarget_t *mt = mi->mi_targets[ candidate ];
+ metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+
+ if ( LDAP_ERR_OK( rs->sr_err ) ) {
+ int rc;
+ struct timeval tv;
+ LDAPMessage *res = NULL;
+ time_t stoptime = (time_t)(-1);
+ int timeout_err = op->o_protocol >= LDAP_VERSION3 ?
+ LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+ const char *timeout_text = "Operation timed out";
+
+ /* if timeout is not specified, compute and use
+ * the one specific to the ongoing operation */
+ if ( timeout == (time_t)(-1) ) {
+ slap_op_t opidx = slap_req2op( op->o_tag );
+
+ if ( opidx == SLAP_OP_SEARCH ) {
+ if ( op->ors_tlimit <= 0 ) {
+ timeout = 0;
+
+ } else {
+ timeout = op->ors_tlimit;
+ timeout_err = LDAP_TIMELIMIT_EXCEEDED;
+ timeout_text = NULL;
+ }
+
+ } else {
+ timeout = mt->mt_timeout[ opidx ];
+ }
+ }
+
+ /* better than nothing :) */
+ if ( timeout == 0 ) {
+ if ( mi->mi_idle_timeout ) {
+ timeout = mi->mi_idle_timeout;
+
+ } else if ( mi->mi_conn_ttl ) {
+ timeout = mi->mi_conn_ttl;
+ }
+ }
+
+ if ( timeout ) {
+ stoptime = op->o_time + timeout;
+ }
+
+ LDAP_BACK_TV_SET( &tv );
+
+retry:;
+ rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
+ switch ( rc ) {
+ case 0:
+ if ( timeout && slap_get_time() > stoptime ) {
+ (void)meta_back_cancel( mc, op, rs, msgid, candidate, sendok );
+ rs->sr_err = timeout_err;
+ rs->sr_text = timeout_text;
+ break;
+ }
+
+ LDAP_BACK_TV_SET( &tv );
+ ldap_pvt_thread_yield();
+ goto retry;
+
+ case -1:
+ ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE,
+ &rs->sr_err );
+ break;
+
+
+ /* otherwise get the result; if it is not
+ * LDAP_SUCCESS, record it in the reply
+ * structure (this includes
+ * LDAP_COMPARE_{TRUE|FALSE}) */
+ default:
+ /* only touch when activity actually took place... */
+ if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
+ msc->msc_time = op->o_time;
+ }
+
+ rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
+ &matched, &text, &refs, &ctrls, 1 );
+ res = NULL;
+ if ( rc == LDAP_SUCCESS ) {
+ rs->sr_text = text;
+ } else {
+ rs->sr_err = rc;
+ }
+ rs->sr_err = slap_map_api2result( rs );
+
+ /* RFC 4511: referrals can only appear
+ * if result code is LDAP_REFERRAL */
+ if ( refs != NULL
+ && refs[ 0 ] != NULL
+ && refs[ 0 ][ 0 ] != '\0' )
+ {
+ if ( rs->sr_err != LDAP_REFERRAL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s meta_back_op_result[%d]: "
+ "got referrals with err=%d\n",
+ op->o_log_prefix,
+ candidate, rs->sr_err );
+
+ } else {
+ int i;
+
+ for ( i = 0; refs[ i ] != NULL; i++ )
+ /* count */ ;
+ rs->sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( i + 1 ),
+ op->o_tmpmemctx );
+ for ( i = 0; refs[ i ] != NULL; i++ ) {
+ ber_str2bv( refs[ i ], 0, 0, &rs->sr_ref[ i ] );
+ }
+ BER_BVZERO( &rs->sr_ref[ i ] );
+ }
+
+ } else if ( rs->sr_err == LDAP_REFERRAL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s meta_back_op_result[%d]: "
+ "got err=%d with null "
+ "or empty referrals\n",
+ op->o_log_prefix,
+ candidate, rs->sr_err );
+
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ }
+
+ if ( ctrls != NULL ) {
+ rs->sr_ctrls = ctrls;
+ }
+ }
+
+ assert( res == NULL );
+ }
+
+ /* if the error in the reply structure is not
+ * LDAP_SUCCESS, try to map it from client
+ * to server error */
+ if ( !LDAP_ERR_OK( rs->sr_err ) ) {
+ rs->sr_err = slap_map_api2result( rs );
+
+ /* internal ops ( op->o_conn == NULL )
+ * must not reply to client */
+ if ( op->o_conn && !op->o_do_not_cache && matched ) {
+
+ /* record the (massaged) matched
+ * DN into the reply structure */
+ rs->sr_matched = matched;
+ }
+ }
+
+ if ( META_BACK_TGT_QUARANTINE( mt ) ) {
+ meta_back_quarantine( op, rs, candidate );
+ }
+
+ } else {
+ int i,
+ err = rs->sr_err;
+
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ metasingleconn_t *msc = &mc->mc_conns[ i ];
+ char *xtext = NULL;
+ char *xmatched = NULL;
+
+ if ( msc->msc_ld == NULL ) {
+ continue;
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+
+ ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE, &rs->sr_err );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ /*
+ * better check the type of error. In some cases
+ * (search ?) it might be better to return a
+ * success if at least one of the targets gave
+ * positive result ...
+ */
+ ldap_get_option( msc->msc_ld,
+ LDAP_OPT_DIAGNOSTIC_MESSAGE, &xtext );
+ if ( xtext != NULL && xtext [ 0 ] == '\0' ) {
+ ldap_memfree( xtext );
+ xtext = NULL;
+ }
+
+ ldap_get_option( msc->msc_ld,
+ LDAP_OPT_MATCHED_DN, &xmatched );
+ if ( xmatched != NULL && xmatched[ 0 ] == '\0' ) {
+ ldap_memfree( xmatched );
+ xmatched = NULL;
+ }
+
+ rs->sr_err = slap_map_api2result( rs );
+
+ Debug(LDAP_DEBUG_ANY,
+ "%s meta_back_op_result[%d] " "err=%d text=\"%s\" matched=\"%s\".\n",
+ op->o_log_prefix, i, rs->sr_err,
+ (xtext ? xtext : ""),
+ (xmatched ? xmatched : "") );
+
+ /*
+ * FIXME: need to rewrite "match" (need rwinfo)
+ */
+ switch ( rs->sr_err ) {
+ default:
+ err = rs->sr_err;
+ if ( xtext != NULL ) {
+ if ( text ) {
+ ldap_memfree( text );
+ }
+ text = xtext;
+ xtext = NULL;
+ }
+ if ( xmatched != NULL ) {
+ if ( matched ) {
+ ldap_memfree( matched );
+ }
+ matched = xmatched;
+ xmatched = NULL;
+ }
+ break;
+ }
+
+ if ( xtext ) {
+ ldap_memfree( xtext );
+ }
+
+ if ( xmatched ) {
+ ldap_memfree( xmatched );
+ }
+ }
+
+ if ( META_BACK_TGT_QUARANTINE( mi->mi_targets[ i ] ) ) {
+ meta_back_quarantine( op, rs, i );
+ }
+ }
+
+ if ( err != LDAP_SUCCESS ) {
+ rs->sr_err = err;
+ }
+ }
+
+ if ( matched != NULL ) {
+ struct berval dn, pdn;
+
+ ber_str2bv( matched, 0, 0, &dn );
+ if ( dnPretty( NULL, &dn, &pdn, op->o_tmpmemctx ) == LDAP_SUCCESS ) {
+ ldap_memfree( matched );
+ matched_ctx = op->o_tmpmemctx;
+ matched = pdn.bv_val;
+ }
+ rs->sr_matched = matched;
+ }
+
+ if ( rs->sr_err == LDAP_UNAVAILABLE ) {
+ if ( !( sendok & LDAP_BACK_RETRYING ) ) {
+ if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
+ if ( rs->sr_text == NULL ) rs->sr_text = "Proxy operation retry failed";
+ send_ldap_result( op, rs );
+ }
+ }
+
+ } else if ( op->o_conn &&
+ ( ( ( sendok & LDAP_BACK_SENDOK ) && LDAP_ERR_OK( rs->sr_err ) )
+ || ( ( sendok & LDAP_BACK_SENDERR ) && !LDAP_ERR_OK( rs->sr_err ) ) ) )
+ {
+ send_ldap_result( op, rs );
+ }
+ if ( matched ) {
+ op->o_tmpfree( (char *)rs->sr_matched, matched_ctx );
+ }
+ if ( text ) {
+ ldap_memfree( text );
+ }
+ if ( rs->sr_ref ) {
+ op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
+ rs->sr_ref = NULL;
+ }
+ if ( refs ) {
+ ber_memvfree( (void **)refs );
+ }
+ if ( ctrls ) {
+ assert( rs->sr_ctrls != NULL );
+ ldap_controls_free( ctrls );
+ }
+
+ rs->sr_text = save_text;
+ rs->sr_matched = save_matched;
+ rs->sr_ref = save_ref;
+ rs->sr_ctrls = save_ctrls;
+
+ return( LDAP_ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err );
+}
+
+/*
+ * meta_back_proxy_authz_cred()
+ *
+ * prepares credentials & method for meta_back_proxy_authz_bind();
+ * or, if method is SASL, performs the SASL bind directly.
+ */
+int
+meta_back_proxy_authz_cred(
+ metaconn_t *mc,
+ int candidate,
+ Operation *op,
+ SlapReply *rs,
+ ldap_back_send_t sendok,
+ struct berval *binddn,
+ struct berval *bindcred,
+ int *method )
+{
+ metainfo_t *mi = (metainfo_t *)op->o_bd->be_private;
+ metatarget_t *mt = mi->mi_targets[ candidate ];
+ metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+ struct berval ndn;
+ int dobind = 0;
+
+ /* don't proxyAuthz if protocol is not LDAPv3 */
+ switch ( mt->mt_version ) {
+ case LDAP_VERSION3:
+ break;
+
+ case 0:
+ if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
+ break;
+ }
+ /* fall thru */
+
+ default:
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+ goto done;
+ }
+
+ if ( op->o_tag == LDAP_REQ_BIND ) {
+ ndn = op->o_req_ndn;
+
+ } else if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
+ ndn = op->o_conn->c_ndn;
+
+ } else {
+ ndn = op->o_ndn;
+ }
+ rs->sr_err = LDAP_SUCCESS;
+
+ /*
+ * FIXME: we need to let clients use proxyAuthz
+ * otherwise we cannot do symmetric pools of servers;
+ * we have to live with the fact that a user can
+ * authorize itself as any ID that is allowed
+ * by the authzTo directive of the "proxyauthzdn".
+ */
+ /*
+ * NOTE: current Proxy Authorization specification
+ * and implementation do not allow proxy authorization
+ * control to be provided with Bind requests
+ */
+ /*
+ * if no bind took place yet, but the connection is bound
+ * and the "proxyauthzdn" is set, then bind as
+ * "proxyauthzdn" and explicitly add the proxyAuthz
+ * control to every operation with the dn bound
+ * to the connection as control value.
+ */
+
+ /* bind as proxyauthzdn only if no idassert mode
+ * is requested, or if the client's identity
+ * is authorized */
+ switch ( mt->mt_idassert_mode ) {
+ case LDAP_BACK_IDASSERT_LEGACY:
+ if ( !BER_BVISNULL( &ndn ) && !BER_BVISEMPTY( &ndn ) ) {
+ if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) && !BER_BVISEMPTY( &mt->mt_idassert_authcDN ) )
+ {
+ *binddn = mt->mt_idassert_authcDN;
+ *bindcred = mt->mt_idassert_passwd;
+ dobind = 1;
+ }
+ }
+ break;
+
+ default:
+ /* NOTE: rootdn can always idassert */
+ if ( BER_BVISNULL( &ndn )
+ && mt->mt_idassert_authz == NULL
+ && !( mt->mt_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) )
+ {
+ if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
+ rs->sr_err = LDAP_INAPPROPRIATE_AUTH;
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+ goto done;
+
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+ *binddn = slap_empty_bv;
+ *bindcred = slap_empty_bv;
+ break;
+
+ } else if ( mt->mt_idassert_authz && !be_isroot( op ) ) {
+ struct berval authcDN;
+
+ if ( BER_BVISNULL( &ndn ) ) {
+ authcDN = slap_empty_bv;
+
+ } else {
+ authcDN = ndn;
+ }
+ rs->sr_err = slap_sasl_matches( op, mt->mt_idassert_authz,
+ &authcDN, &authcDN );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+ goto done;
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+ *binddn = slap_empty_bv;
+ *bindcred = slap_empty_bv;
+ break;
+ }
+ }
+
+ *binddn = mt->mt_idassert_authcDN;
+ *bindcred = mt->mt_idassert_passwd;
+ dobind = 1;
+ break;
+ }
+
+ if ( dobind && mt->mt_idassert_authmethod == LDAP_AUTH_SASL ) {
+#ifdef HAVE_CYRUS_SASL
+ void *defaults = NULL;
+ struct berval authzID = BER_BVNULL;
+ int freeauthz = 0;
+
+ /* if SASL supports native authz, prepare for it */
+ if ( ( !op->o_do_not_cache || !op->o_is_auth_check ) &&
+ ( mt->mt_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
+ {
+ switch ( mt->mt_idassert_mode ) {
+ case LDAP_BACK_IDASSERT_OTHERID:
+ case LDAP_BACK_IDASSERT_OTHERDN:
+ authzID = mt->mt_idassert_authzID;
+ break;
+
+ case LDAP_BACK_IDASSERT_ANONYMOUS:
+ BER_BVSTR( &authzID, "dn:" );
+ break;
+
+ case LDAP_BACK_IDASSERT_SELF:
+ if ( BER_BVISNULL( &ndn ) ) {
+ /* connection is not authc'd, so don't idassert */
+ BER_BVSTR( &authzID, "dn:" );
+ break;
+ }
+ authzID.bv_len = STRLENOF( "dn:" ) + ndn.bv_len;
+ authzID.bv_val = slap_sl_malloc( authzID.bv_len + 1, op->o_tmpmemctx );
+ AC_MEMCPY( authzID.bv_val, "dn:", STRLENOF( "dn:" ) );
+ AC_MEMCPY( authzID.bv_val + STRLENOF( "dn:" ),
+ ndn.bv_val, ndn.bv_len + 1 );
+ freeauthz = 1;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if ( mt->mt_idassert_secprops != NULL ) {
+ rs->sr_err = ldap_set_option( msc->msc_ld,
+ LDAP_OPT_X_SASL_SECPROPS,
+ (void *)mt->mt_idassert_secprops );
+
+ if ( rs->sr_err != LDAP_OPT_SUCCESS ) {
+ rs->sr_err = LDAP_OTHER;
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+ goto done;
+ }
+ }
+
+ defaults = lutil_sasl_defaults( msc->msc_ld,
+ mt->mt_idassert_sasl_mech.bv_val,
+ mt->mt_idassert_sasl_realm.bv_val,
+ mt->mt_idassert_authcID.bv_val,
+ mt->mt_idassert_passwd.bv_val,
+ authzID.bv_val );
+ if ( defaults == NULL ) {
+ rs->sr_err = LDAP_OTHER;
+ LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ goto done;
+ }
+
+ rs->sr_err = ldap_sasl_interactive_bind_s( msc->msc_ld, binddn->bv_val,
+ mt->mt_idassert_sasl_mech.bv_val, NULL, NULL,
+ LDAP_SASL_QUIET, lutil_sasl_interact,
+ defaults );
+
+ rs->sr_err = slap_map_api2result( rs );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+
+ } else {
+ LDAP_BACK_CONN_ISBOUND_SET( msc );
+ }
+
+ lutil_sasl_freedefs( defaults );
+ if ( freeauthz ) {
+ slap_sl_free( authzID.bv_val, op->o_tmpmemctx );
+ }
+
+ goto done;
+#endif /* HAVE_CYRUS_SASL */
+ }
+
+ *method = mt->mt_idassert_authmethod;
+ switch ( mt->mt_idassert_authmethod ) {
+ case LDAP_AUTH_NONE:
+ BER_BVSTR( binddn, "" );
+ BER_BVSTR( bindcred, "" );
+ /* fallthru */
+
+ case LDAP_AUTH_SIMPLE:
+ break;
+
+ default:
+ /* unsupported! */
+ LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+ rs->sr_err = LDAP_AUTH_METHOD_NOT_SUPPORTED;
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ break;
+ }
+
+done:;
+
+ if ( !BER_BVISEMPTY( binddn ) ) {
+ LDAP_BACK_CONN_ISIDASSERT_SET( msc );
+ }
+
+ return rs->sr_err;
+}
+
+static int
+meta_back_proxy_authz_bind(
+ metaconn_t *mc,
+ int candidate,
+ Operation *op,
+ SlapReply *rs,
+ ldap_back_send_t sendok,
+ int dolock )
+{
+ metainfo_t *mi = (metainfo_t *)op->o_bd->be_private;
+ metatarget_t *mt = mi->mi_targets[ candidate ];
+ metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+ struct berval binddn = BER_BVC( "" ),
+ cred = BER_BVC( "" );
+ int method = LDAP_AUTH_NONE,
+ rc;
+
+ rc = meta_back_proxy_authz_cred( mc, candidate, op, rs, sendok, &binddn, &cred, &method );
+ if ( rc == LDAP_SUCCESS && !LDAP_BACK_CONN_ISBOUND( msc ) ) {
+ int msgid;
+
+ switch ( method ) {
+ case LDAP_AUTH_NONE:
+ case LDAP_AUTH_SIMPLE:
+
+ if(!dolock) {
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ }
+
+ for (;;) {
+ rs->sr_err = ldap_sasl_bind( msc->msc_ld,
+ binddn.bv_val, LDAP_SASL_SIMPLE,
+ &cred, NULL, NULL, &msgid );
+ if ( rs->sr_err != LDAP_X_CONNECTING ) {
+ break;
+ }
+ ldap_pvt_thread_yield();
+ }
+
+ if(!dolock) {
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ }
+
+ rc = meta_back_bind_op_result( op, rs, mc, candidate, msgid, sendok, dolock );
+ if ( rc == LDAP_SUCCESS ) {
+ /* set rebind stuff in case of successful proxyAuthz bind,
+ * so that referral chasing is attempted using the right
+ * identity */
+ LDAP_BACK_CONN_ISBOUND_SET( msc );
+ ber_bvreplace( &msc->msc_bound_ndn, &binddn );
+
+ if ( META_BACK_TGT_SAVECRED( mt ) ) {
+ if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+ memset( msc->msc_cred.bv_val, 0,
+ msc->msc_cred.bv_len );
+ }
+ ber_bvreplace( &msc->msc_cred, &cred );
+ ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc );
+ }
+ }
+ break;
+
+ default:
+ assert( 0 );
+ break;
+ }
+ }
+
+ return LDAP_BACK_CONN_ISBOUND( msc );
+}
+
+/*
+ * Add controls;
+ *
+ * if any needs to be added, it is prepended to existing ones,
+ * in a newly allocated array. The companion function
+ * mi->mi_ldap_extra->controls_free() must be used to restore the original
+ * status of op->o_ctrls.
+ */
+int
+meta_back_controls_add(
+ Operation *op,
+ SlapReply *rs,
+ metaconn_t *mc,
+ int candidate,
+ LDAPControl ***pctrls )
+{
+ metainfo_t *mi = (metainfo_t *)op->o_bd->be_private;
+ metatarget_t *mt = mi->mi_targets[ candidate ];
+ metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+
+ LDAPControl **ctrls = NULL;
+ /* set to the maximum number of controls this backend can add */
+ LDAPControl c[ 2 ] = {{ 0 }};
+ int n = 0, i, j1 = 0, j2 = 0, skipped = 0;
+
+ *pctrls = NULL;
+
+ rs->sr_err = LDAP_SUCCESS;
+
+ /* don't add controls if protocol is not LDAPv3 */
+ switch ( mt->mt_version ) {
+ case LDAP_VERSION3:
+ break;
+
+ case 0:
+ if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
+ break;
+ }
+ /* fall thru */
+
+ default:
+ goto done;
+ }
+
+ /* put controls that go __before__ existing ones here */
+
+ /* proxyAuthz for identity assertion */
+ switch ( mi->mi_ldap_extra->proxy_authz_ctrl( op, rs, &msc->msc_bound_ndn,
+ mt->mt_version, &mt->mt_idassert, &c[ j1 ] ) )
+ {
+ case SLAP_CB_CONTINUE:
+ break;
+
+ case LDAP_SUCCESS:
+ j1++;
+ break;
+
+ default:
+ goto done;
+ }
+
+ /* put controls that go __after__ existing ones here */
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ /* session tracking */
+ if ( META_BACK_TGT_ST_REQUEST( mt ) ) {
+ switch ( slap_ctrl_session_tracking_request_add( op, rs, &c[ j1 + j2 ] ) ) {
+ case SLAP_CB_CONTINUE:
+ break;
+
+ case LDAP_SUCCESS:
+ j2++;
+ break;
+
+ default:
+ goto done;
+ }
+ }
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+ if ( rs->sr_err == SLAP_CB_CONTINUE ) {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+
+ /* if nothing to do, just bail out */
+ if ( j1 == 0 && j2 == 0 ) {
+ goto done;
+ }
+
+ assert( j1 + j2 <= (int) (sizeof( c )/sizeof( c[0] )) );
+
+ if ( op->o_ctrls ) {
+ for ( n = 0; op->o_ctrls[ n ]; n++ )
+ /* just count ctrls */ ;
+ }
+
+ ctrls = op->o_tmpalloc( (n + j1 + j2 + 1) * sizeof( LDAPControl * ) + ( j1 + j2 ) * sizeof( LDAPControl ),
+ op->o_tmpmemctx );
+ if ( j1 ) {
+ ctrls[ 0 ] = (LDAPControl *)&ctrls[ n + j1 + j2 + 1 ];
+ *ctrls[ 0 ] = c[ 0 ];
+ for ( i = 1; i < j1; i++ ) {
+ ctrls[ i ] = &ctrls[ 0 ][ i ];
+ *ctrls[ i ] = c[ i ];
+ }
+ }
+
+ i = 0;
+ if ( op->o_ctrls ) {
+ LDAPControl *proxyauthz = ldap_control_find(
+ LDAP_CONTROL_PROXY_AUTHZ, op->o_ctrls, NULL );
+
+ for ( i = 0; op->o_ctrls[ i ]; i++ ) {
+ /* Only replace it if we generated one */
+ if ( j1 && proxyauthz && proxyauthz == op->o_ctrls[ i ] ) {
+ /* Frontend has already checked only one is present */
+ assert( skipped == 0 );
+ skipped++;
+ continue;
+ }
+ ctrls[ i + j1 - skipped ] = op->o_ctrls[ i ];
+ }
+ }
+
+ n += j1 - skipped;
+ if ( j2 ) {
+ ctrls[ n ] = (LDAPControl *)&ctrls[ n + j2 + 1 ] + j1;
+ *ctrls[ n ] = c[ j1 ];
+ for ( i = 1; i < j2; i++ ) {
+ ctrls[ n + i ] = &ctrls[ n ][ i ];
+ *ctrls[ n + i ] = c[ i ];
+ }
+ }
+
+ ctrls[ n + j2 ] = NULL;
+
+done:;
+ if ( ctrls == NULL ) {
+ ctrls = op->o_ctrls;
+ }
+
+ *pctrls = ctrls;
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/back-meta/candidates.c b/servers/slapd/back-meta/candidates.c
new file mode 100644
index 0000000..ffb5992
--- /dev/null
+++ b/servers/slapd/back-meta/candidates.c
@@ -0,0 +1,282 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include "ac/string.h"
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+/*
+ * The meta-directory has one suffix, called <suffix>.
+ * It handles a pool of target servers, each with a branch suffix
+ * of the form <branch X>,<suffix>, where <branch X> may be empty.
+ *
+ * When the meta-directory receives a request with a request DN that belongs
+ * to a branch, the corresponding target is invoked. When the request DN
+ * does not belong to a specific branch, all the targets that
+ * are compatible with the request DN are selected as candidates, and
+ * the request is spawned to all the candidate targets
+ *
+ * A request is characterized by a request DN. The following cases are
+ * handled:
+ * - the request DN is the suffix: <dn> == <suffix>,
+ * all the targets are candidates (search ...)
+ * - the request DN is a branch suffix: <dn> == <branch X>,<suffix>, or
+ * - the request DN is a subtree of a branch suffix:
+ * <dn> == <rdn>,<branch X>,<suffix>,
+ * the target is the only candidate.
+ *
+ * A possible extension will include the handling of multiple suffixes
+ */
+
+static metasubtree_t *
+meta_subtree_match( metatarget_t *mt, struct berval *ndn, int scope )
+{
+ metasubtree_t *ms = mt->mt_subtree;
+
+ for ( ms = mt->mt_subtree; ms; ms = ms->ms_next ) {
+ switch ( ms->ms_type ) {
+ case META_ST_SUBTREE:
+ if ( dnIsSuffix( ndn, &ms->ms_dn ) ) {
+ return ms;
+ }
+ break;
+
+ case META_ST_SUBORDINATE:
+ if ( dnIsSuffix( ndn, &ms->ms_dn ) &&
+ ( ndn->bv_len > ms->ms_dn.bv_len || scope != LDAP_SCOPE_BASE ) )
+ {
+ return ms;
+ }
+ break;
+
+ case META_ST_REGEX:
+ /* NOTE: cannot handle scope */
+ if ( regexec( &ms->ms_regex, ndn->bv_val, 0, NULL, 0 ) == 0 ) {
+ return ms;
+ }
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * returns 1 if suffix is candidate for dn, otherwise 0
+ *
+ * Note: this function should never be called if dn is the <suffix>.
+ */
+int
+meta_back_is_candidate(
+ metatarget_t *mt,
+ struct berval *ndn,
+ int scope )
+{
+ struct berval rdn;
+ int d = ndn->bv_len - mt->mt_nsuffix.bv_len;
+
+ if ( d >= 0 ) {
+ if ( !dnIsSuffix( ndn, &mt->mt_nsuffix ) ) {
+ return META_NOT_CANDIDATE;
+ }
+
+ /*
+ * | match | exclude |
+ * +---------+---------+-------------------+
+ * | T | T | not candidate |
+ * | F | T | continue checking |
+ * +---------+---------+-------------------+
+ * | T | F | candidate |
+ * | F | F | not candidate |
+ * +---------+---------+-------------------+
+ */
+
+ if ( mt->mt_subtree ) {
+ int match = ( meta_subtree_match( mt, ndn, scope ) != NULL );
+
+ if ( !mt->mt_subtree_exclude ) {
+ return match ? META_CANDIDATE : META_NOT_CANDIDATE;
+ }
+
+ if ( match /* && mt->mt_subtree_exclude */ ) {
+ return META_NOT_CANDIDATE;
+ }
+ }
+
+ switch ( mt->mt_scope ) {
+ case LDAP_SCOPE_SUBTREE:
+ default:
+ return META_CANDIDATE;
+
+ case LDAP_SCOPE_SUBORDINATE:
+ if ( d > 0 ) {
+ return META_CANDIDATE;
+ }
+ break;
+
+ /* nearly useless; not allowed by config */
+ case LDAP_SCOPE_ONELEVEL:
+ if ( d > 0 ) {
+ rdn.bv_val = ndn->bv_val;
+ rdn.bv_len = (ber_len_t)d - STRLENOF( "," );
+ if ( dnIsOneLevelRDN( &rdn ) ) {
+ return META_CANDIDATE;
+ }
+ }
+ break;
+
+ /* nearly useless; not allowed by config */
+ case LDAP_SCOPE_BASE:
+ if ( d == 0 ) {
+ return META_CANDIDATE;
+ }
+ break;
+ }
+
+ } else /* if ( d < 0 ) */ {
+ if ( !dnIsSuffix( &mt->mt_nsuffix, ndn ) ) {
+ return META_NOT_CANDIDATE;
+ }
+
+ switch ( scope ) {
+ case LDAP_SCOPE_SUBTREE:
+ case LDAP_SCOPE_SUBORDINATE:
+ /*
+ * suffix longer than dn, but common part matches
+ */
+ return META_CANDIDATE;
+
+ case LDAP_SCOPE_ONELEVEL:
+ rdn.bv_val = mt->mt_nsuffix.bv_val;
+ rdn.bv_len = (ber_len_t)(-d) - STRLENOF( "," );
+ if ( dnIsOneLevelRDN( &rdn ) ) {
+ return META_CANDIDATE;
+ }
+ break;
+ }
+ }
+
+ return META_NOT_CANDIDATE;
+}
+
+/*
+ * meta_back_select_unique_candidate
+ *
+ * returns the index of the candidate in case it is unique, otherwise
+ * META_TARGET_NONE if none matches, or
+ * META_TARGET_MULTIPLE if more than one matches
+ * Note: ndn MUST be normalized.
+ */
+int
+meta_back_select_unique_candidate(
+ metainfo_t *mi,
+ struct berval *ndn )
+{
+ int i, candidate = META_TARGET_NONE;
+
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ metatarget_t *mt = mi->mi_targets[ i ];
+
+ if ( meta_back_is_candidate( mt, ndn, LDAP_SCOPE_BASE ) ) {
+ if ( candidate == META_TARGET_NONE ) {
+ candidate = i;
+
+ } else {
+ return META_TARGET_MULTIPLE;
+ }
+ }
+ }
+
+ return candidate;
+}
+
+/*
+ * meta_clear_unused_candidates
+ *
+ * clears all candidates except candidate
+ */
+int
+meta_clear_unused_candidates(
+ Operation *op,
+ int candidate )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ int i;
+ SlapReply *candidates = meta_back_candidates_get( op );
+
+ for ( i = 0; i < mi->mi_ntargets; ++i ) {
+ if ( i == candidate ) {
+ continue;
+ }
+ META_CANDIDATE_RESET( &candidates[ i ] );
+ }
+
+ return 0;
+}
+
+/*
+ * meta_clear_one_candidate
+ *
+ * clears the selected candidate
+ */
+int
+meta_clear_one_candidate(
+ Operation *op,
+ metaconn_t *mc,
+ int candidate )
+{
+ metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+
+ if ( msc->msc_ld != NULL ) {
+
+#ifdef DEBUG_205
+ Debug(LDAP_DEBUG_ANY,
+ "### %s meta_clear_one_candidate ldap_unbind_ext[%d] mc=%p ld=%p\n",
+ op ? op->o_log_prefix : "", candidate, (void *)mc,
+ (void *)msc->msc_ld );
+#endif /* DEBUG_205 */
+
+ ldap_unbind_ext( msc->msc_ld, NULL, NULL );
+ msc->msc_ld = NULL;
+ }
+
+ if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
+ ber_memfree_x( msc->msc_bound_ndn.bv_val, NULL );
+ BER_BVZERO( &msc->msc_bound_ndn );
+ }
+
+ if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+ memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
+ ber_memfree_x( msc->msc_cred.bv_val, NULL );
+ BER_BVZERO( &msc->msc_cred );
+ }
+
+ msc->msc_mscflags = 0;
+
+ return 0;
+}
+
diff --git a/servers/slapd/back-meta/compare.c b/servers/slapd/back-meta/compare.c
new file mode 100644
index 0000000..f6fd54d
--- /dev/null
+++ b/servers/slapd/back-meta/compare.c
@@ -0,0 +1,154 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+int
+meta_back_compare( Operation *op, SlapReply *rs )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ metatarget_t *mt;
+ metaconn_t *mc;
+ int rc = 0;
+ int candidate = -1;
+ struct berval mdn = BER_BVNULL;
+ dncookie dc;
+ struct berval mapped_attr = op->orc_ava->aa_desc->ad_cname;
+ struct berval mapped_value = op->orc_ava->aa_value;
+ int msgid;
+ ldap_back_send_t retrying = LDAP_BACK_RETRYING;
+ LDAPControl **ctrls = NULL;
+
+ mc = meta_back_getconn( op, rs, &candidate, LDAP_BACK_SENDERR );
+ if ( !mc || !meta_back_dobind( op, rs, mc, LDAP_BACK_SENDERR ) ) {
+ return rs->sr_err;
+ }
+
+ assert( mc->mc_conns[ candidate ].msc_ld != NULL );
+
+ /*
+ * Rewrite the modify dn, if needed
+ */
+ mt = mi->mi_targets[ candidate ];
+ dc.target = mt;
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+ dc.ctx = "compareDN";
+
+ switch ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
+ case LDAP_UNWILLING_TO_PERFORM:
+ rc = 1;
+ goto cleanup;
+
+ default:
+ break;
+ }
+
+ /*
+ * if attr is objectClass, try to remap the value
+ */
+ if ( op->orc_ava->aa_desc == slap_schema.si_ad_objectClass ) {
+ ldap_back_map( &mt->mt_rwmap.rwm_oc,
+ &op->orc_ava->aa_value,
+ &mapped_value, BACKLDAP_MAP );
+
+ if ( BER_BVISNULL( &mapped_value ) || BER_BVISEMPTY( &mapped_value ) ) {
+ goto cleanup;
+ }
+
+ /*
+ * else try to remap the attribute
+ */
+ } else {
+ ldap_back_map( &mt->mt_rwmap.rwm_at,
+ &op->orc_ava->aa_desc->ad_cname,
+ &mapped_attr, BACKLDAP_MAP );
+ if ( BER_BVISNULL( &mapped_attr ) || BER_BVISEMPTY( &mapped_attr ) ) {
+ goto cleanup;
+ }
+
+ if ( op->orc_ava->aa_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName )
+ {
+ dc.ctx = "compareAttrDN";
+
+ switch ( ldap_back_dn_massage( &dc, &op->orc_ava->aa_value, &mapped_value ) )
+ {
+ case LDAP_UNWILLING_TO_PERFORM:
+ rc = 1;
+ goto cleanup;
+
+ default:
+ break;
+ }
+ }
+ }
+
+retry:;
+ ctrls = op->o_ctrls;
+ rc = meta_back_controls_add( op, rs, mc, candidate, &ctrls );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ rs->sr_err = ldap_compare_ext( mc->mc_conns[ candidate ].msc_ld, mdn.bv_val,
+ mapped_attr.bv_val, &mapped_value,
+ ctrls, NULL, &msgid );
+
+ rs->sr_err = meta_back_op_result( mc, op, rs, candidate, msgid,
+ mt->mt_timeout[ SLAP_OP_COMPARE ], ( LDAP_BACK_SENDRESULT | retrying ) );
+ if ( rs->sr_err == LDAP_UNAVAILABLE && retrying ) {
+ retrying &= ~LDAP_BACK_RETRYING;
+ if ( meta_back_retry( op, rs, &mc, candidate, LDAP_BACK_SENDERR ) ) {
+ /* if the identity changed, there might be need to re-authz */
+ (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+ goto retry;
+ }
+ }
+
+cleanup:;
+ (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+
+ if ( mdn.bv_val != op->o_req_dn.bv_val ) {
+ free( mdn.bv_val );
+ }
+
+ if ( op->orc_ava->aa_value.bv_val != mapped_value.bv_val ) {
+ free( mapped_value.bv_val );
+ }
+
+ if ( mc ) {
+ meta_back_release_conn( mi, mc );
+ }
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/back-meta/config.c b/servers/slapd/back-meta/config.c
new file mode 100644
index 0000000..6b1e607
--- /dev/null
+++ b/servers/slapd/back-meta/config.c
@@ -0,0 +1,3300 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+#include "ldif.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+#ifdef LDAP_DEVEL
+#define SLAP_AUTH_DN 1
+#endif
+
+static ConfigDriver meta_back_cf_gen;
+static ConfigLDAPadd meta_ldadd;
+static ConfigCfAdd meta_cfadd;
+
+static int ldap_back_map_config(
+ ConfigArgs *c,
+ struct ldapmap *oc_map,
+ struct ldapmap *at_map );
+
+/* Three sets of enums:
+ * 1) attrs that are only valid in the base config
+ * 2) attrs that are valid in base or target
+ * 3) attrs that are only valid in a target
+ */
+
+/* Base attrs */
+enum {
+ LDAP_BACK_CFG_CONN_TTL = 1,
+ LDAP_BACK_CFG_DNCACHE_TTL,
+ LDAP_BACK_CFG_IDLE_TIMEOUT,
+ LDAP_BACK_CFG_ONERR,
+ LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER,
+ LDAP_BACK_CFG_SINGLECONN,
+ LDAP_BACK_CFG_USETEMP,
+ LDAP_BACK_CFG_CONNPOOLMAX,
+ LDAP_BACK_CFG_LAST_BASE
+};
+
+/* Base or target */
+enum {
+ LDAP_BACK_CFG_BIND_TIMEOUT = LDAP_BACK_CFG_LAST_BASE,
+ LDAP_BACK_CFG_CANCEL,
+ LDAP_BACK_CFG_CHASE,
+ LDAP_BACK_CFG_CLIENT_PR,
+ LDAP_BACK_CFG_DEFAULT_T,
+ LDAP_BACK_CFG_NETWORK_TIMEOUT,
+ LDAP_BACK_CFG_NOREFS,
+ LDAP_BACK_CFG_NOUNDEFFILTER,
+ LDAP_BACK_CFG_NRETRIES,
+ LDAP_BACK_CFG_QUARANTINE,
+ LDAP_BACK_CFG_REBIND,
+ LDAP_BACK_CFG_TIMEOUT,
+ LDAP_BACK_CFG_VERSION,
+ LDAP_BACK_CFG_ST_REQUEST,
+ LDAP_BACK_CFG_T_F,
+ LDAP_BACK_CFG_TLS,
+ LDAP_BACK_CFG_LAST_BOTH
+};
+
+/* Target attrs */
+enum {
+ LDAP_BACK_CFG_URI = LDAP_BACK_CFG_LAST_BOTH,
+ LDAP_BACK_CFG_IDASSERT_AUTHZFROM,
+ LDAP_BACK_CFG_IDASSERT_BIND,
+ LDAP_BACK_CFG_REWRITE,
+ LDAP_BACK_CFG_SUFFIXM,
+ LDAP_BACK_CFG_MAP,
+ LDAP_BACK_CFG_SUBTREE_EX,
+ LDAP_BACK_CFG_SUBTREE_IN,
+ LDAP_BACK_CFG_PSEUDOROOTDN,
+ LDAP_BACK_CFG_PSEUDOROOTPW,
+ LDAP_BACK_CFG_KEEPALIVE,
+ LDAP_BACK_CFG_TCP_USER_TIMEOUT,
+ LDAP_BACK_CFG_FILTER,
+
+ LDAP_BACK_CFG_LAST
+};
+
+static ConfigTable metacfg[] = {
+ { "uri", "uri", 2, 0, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_URI,
+ meta_back_cf_gen, "( OLcfgDbAt:0.14 "
+ "NAME 'olcDbURI' "
+ "DESC 'URI (list) for remote DSA' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "tls", "what", 2, 0, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_TLS,
+ meta_back_cf_gen, "( OLcfgDbAt:3.1 "
+ "NAME 'olcDbStartTLS' "
+ "DESC 'StartTLS' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "idassert-bind", "args", 2, 0, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_IDASSERT_BIND,
+ meta_back_cf_gen, "( OLcfgDbAt:3.7 "
+ "NAME 'olcDbIDAssertBind' "
+ "DESC 'Remote Identity Assertion administrative identity auth bind configuration' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "idassert-authzFrom", "authzRule", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_IDASSERT_AUTHZFROM,
+ meta_back_cf_gen, "( OLcfgDbAt:3.9 "
+ "NAME 'olcDbIDAssertAuthzFrom' "
+ "DESC 'Remote Identity Assertion authz rules' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "X-ORDERED 'VALUES' )",
+ NULL, NULL },
+ { "rebind-as-user", "true|FALSE", 1, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_REBIND,
+ meta_back_cf_gen, "( OLcfgDbAt:3.10 "
+ "NAME 'olcDbRebindAsUser' "
+ "DESC 'Rebind as user' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "chase-referrals", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_CHASE,
+ meta_back_cf_gen, "( OLcfgDbAt:3.11 "
+ "NAME 'olcDbChaseReferrals' "
+ "DESC 'Chase referrals' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "t-f-support", "true|FALSE|discover", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_T_F,
+ meta_back_cf_gen, "( OLcfgDbAt:3.12 "
+ "NAME 'olcDbTFSupport' "
+ "DESC 'Absolute filters support' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "timeout", "timeout(list)", 2, 0, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_TIMEOUT,
+ meta_back_cf_gen, "( OLcfgDbAt:3.14 "
+ "NAME 'olcDbTimeout' "
+ "DESC 'Per-operation timeouts' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "idle-timeout", "timeout", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_IDLE_TIMEOUT,
+ meta_back_cf_gen, "( OLcfgDbAt:3.15 "
+ "NAME 'olcDbIdleTimeout' "
+ "DESC 'connection idle timeout' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "conn-ttl", "ttl", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_CONN_TTL,
+ meta_back_cf_gen, "( OLcfgDbAt:3.16 "
+ "NAME 'olcDbConnTtl' "
+ "DESC 'connection ttl' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "network-timeout", "timeout", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_NETWORK_TIMEOUT,
+ meta_back_cf_gen, "( OLcfgDbAt:3.17 "
+ "NAME 'olcDbNetworkTimeout' "
+ "DESC 'connection network timeout' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "protocol-version", "version", 2, 2, 0,
+ ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_VERSION,
+ meta_back_cf_gen, "( OLcfgDbAt:3.18 "
+ "NAME 'olcDbProtocolVersion' "
+ "DESC 'protocol version' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "single-conn", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_SINGLECONN,
+ meta_back_cf_gen, "( OLcfgDbAt:3.19 "
+ "NAME 'olcDbSingleConn' "
+ "DESC 'cache a single connection per identity' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "cancel", "ABANDON|ignore|exop", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_CANCEL,
+ meta_back_cf_gen, "( OLcfgDbAt:3.20 "
+ "NAME 'olcDbCancel' "
+ "DESC 'abandon/ignore/exop operations when appropriate' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "quarantine", "retrylist", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_QUARANTINE,
+ meta_back_cf_gen, "( OLcfgDbAt:3.21 "
+ "NAME 'olcDbQuarantine' "
+ "DESC 'Quarantine database if connection fails and retry according to rule' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "use-temporary-conn", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_USETEMP,
+ meta_back_cf_gen, "( OLcfgDbAt:3.22 "
+ "NAME 'olcDbUseTemporaryConn' "
+ "DESC 'Use temporary connections if the cached one is busy' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "conn-pool-max", "<n>", 2, 2, 0,
+ ARG_MAGIC|ARG_INT|LDAP_BACK_CFG_CONNPOOLMAX,
+ meta_back_cf_gen, "( OLcfgDbAt:3.23 "
+ "NAME 'olcDbConnectionPoolMax' "
+ "DESC 'Max size of privileged connections pool' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ { "session-tracking-request", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_ST_REQUEST,
+ meta_back_cf_gen, "( OLcfgDbAt:3.24 "
+ "NAME 'olcDbSessionTrackingRequest' "
+ "DESC 'Add session tracking control to proxied requests' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+ { "norefs", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_NOREFS,
+ meta_back_cf_gen, "( OLcfgDbAt:3.25 "
+ "NAME 'olcDbNoRefs' "
+ "DESC 'Do not return search reference responses' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "noundeffilter", "true|FALSE", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_NOUNDEFFILTER,
+ meta_back_cf_gen, "( OLcfgDbAt:3.26 "
+ "NAME 'olcDbNoUndefFilter' "
+ "DESC 'Do not propagate undefined search filters' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "rewrite", "arglist", 2, 0, STRLENOF( "rewrite" ),
+ ARG_MAGIC|LDAP_BACK_CFG_REWRITE,
+ meta_back_cf_gen, "( OLcfgDbAt:3.101 "
+ "NAME 'olcDbRewrite' "
+ "DESC 'DN rewriting rules' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "X-ORDERED 'VALUES' )",
+ NULL, NULL },
+ { "suffixmassage", "virtual> <real", 2, 3, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_SUFFIXM,
+ meta_back_cf_gen, NULL, NULL, NULL },
+
+ { "map", "attribute|objectClass> [*|<local>] *|<remote", 3, 4, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_MAP,
+ meta_back_cf_gen, "( OLcfgDbAt:3.102 "
+ "NAME 'olcDbMap' "
+ "DESC 'Map attribute and objectclass names' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "X-ORDERED 'VALUES' )",
+ NULL, NULL },
+
+ { "subtree-exclude", "pattern", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_SUBTREE_EX,
+ meta_back_cf_gen, "( OLcfgDbAt:3.103 "
+ "NAME 'olcDbSubtreeExclude' "
+ "DESC 'DN of subtree to exclude from target' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )",
+ NULL, NULL },
+ { "subtree-include", "pattern", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_SUBTREE_IN,
+ meta_back_cf_gen, "( OLcfgDbAt:3.104 "
+ "NAME 'olcDbSubtreeInclude' "
+ "DESC 'DN of subtree to include in target' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )",
+ NULL, NULL },
+ { "default-target", "[none|<target ID>]", 1, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_DEFAULT_T,
+ meta_back_cf_gen, "( OLcfgDbAt:3.105 "
+ "NAME 'olcDbDefaultTarget' "
+ "DESC 'Specify the default target' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "dncache-ttl", "ttl", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_DNCACHE_TTL,
+ meta_back_cf_gen, "( OLcfgDbAt:3.106 "
+ "NAME 'olcDbDnCacheTtl' "
+ "DESC 'dncache ttl' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "bind-timeout", "microseconds", 2, 2, 0,
+ ARG_MAGIC|ARG_ULONG|LDAP_BACK_CFG_BIND_TIMEOUT,
+ meta_back_cf_gen, "( OLcfgDbAt:3.107 "
+ "NAME 'olcDbBindTimeout' "
+ "DESC 'bind timeout' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "onerr", "CONTINUE|report|stop", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_ONERR,
+ meta_back_cf_gen, "( OLcfgDbAt:3.108 "
+ "NAME 'olcDbOnErr' "
+ "DESC 'error handling' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "pseudoroot-bind-defer", "TRUE|false", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER,
+ meta_back_cf_gen, "( OLcfgDbAt:3.109 "
+ "NAME 'olcDbPseudoRootBindDefer' "
+ "DESC 'error handling' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "root-bind-defer", "TRUE|false", 2, 2, 0,
+ ARG_MAGIC|ARG_ON_OFF|LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER,
+ meta_back_cf_gen, NULL, NULL, NULL },
+ { "pseudorootdn", "dn", 2, 2, 0,
+ ARG_MAGIC|ARG_DN|ARG_QUOTE|LDAP_BACK_CFG_PSEUDOROOTDN,
+ meta_back_cf_gen, NULL, NULL, NULL },
+ { "pseudorootpw", "password", 2, 2, 0,
+ ARG_MAGIC|ARG_STRING|LDAP_BACK_CFG_PSEUDOROOTPW,
+ meta_back_cf_gen, NULL, NULL, NULL },
+ { "nretries", "NEVER|forever|<number>", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_NRETRIES,
+ meta_back_cf_gen, "( OLcfgDbAt:3.110 "
+ "NAME 'olcDbNretries' "
+ "DESC 'retry handling' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { "client-pr", "accept-unsolicited|disable|<size>", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_CLIENT_PR,
+ meta_back_cf_gen, "( OLcfgDbAt:3.111 "
+ "NAME 'olcDbClientPr' "
+ "DESC 'PagedResults handling' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "", "", 0, 0, 0, ARG_IGNORED,
+ NULL, "( OLcfgDbAt:3.100 NAME 'olcMetaSub' "
+ "DESC 'Placeholder to name a Target entry' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE X-ORDERED 'SIBLINGS' )", NULL, NULL },
+
+ { "keepalive", "keepalive", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_KEEPALIVE,
+ meta_back_cf_gen, "( OLcfgDbAt:3.29 "
+ "NAME 'olcDbKeepalive' "
+ "DESC 'TCP keepalive' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "tcp-user-timeout", "milliseconds", 2, 2, 0,
+ ARG_MAGIC|ARG_UINT|LDAP_BACK_CFG_TCP_USER_TIMEOUT,
+ meta_back_cf_gen, "( OLcfgDbAt:3.30 "
+ "NAME 'olcDbTcpUserTimeout' "
+ "DESC 'TCP User Timeout' "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "filter", "pattern", 2, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_FILTER,
+ meta_back_cf_gen, "( OLcfgDbAt:3.112 "
+ "NAME 'olcDbFilter' "
+ "DESC 'Filter regex pattern to include in target' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString )",
+ NULL, NULL },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED,
+ NULL, NULL, NULL, NULL }
+};
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+#define ST_ATTR "$ olcDbSessionTrackingRequest "
+#else
+#define ST_ATTR ""
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+#define COMMON_ATTRS \
+ "$ olcDbBindTimeout " \
+ "$ olcDbCancel " \
+ "$ olcDbChaseReferrals " \
+ "$ olcDbClientPr " \
+ "$ olcDbDefaultTarget " \
+ "$ olcDbNetworkTimeout " \
+ "$ olcDbNoRefs " \
+ "$ olcDbNoUndefFilter " \
+ "$ olcDbNretries " \
+ "$ olcDbProtocolVersion " \
+ "$ olcDbQuarantine " \
+ "$ olcDbRebindAsUser " \
+ ST_ATTR \
+ "$ olcDbStartTLS " \
+ "$ olcDbTFSupport "
+
+static ConfigOCs metaocs[] = {
+ { "( OLcfgDbOc:3.2 "
+ "NAME 'olcMetaConfig' "
+ "DESC 'Meta backend configuration' "
+ "SUP olcDatabaseConfig "
+ "MAY ( olcDbConnTtl "
+ "$ olcDbDnCacheTtl "
+ "$ olcDbIdleTimeout "
+ "$ olcDbOnErr "
+ "$ olcDbPseudoRootBindDefer "
+ "$ olcDbSingleConn "
+ "$ olcDbUseTemporaryConn "
+ "$ olcDbConnectionPoolMax "
+
+ /* defaults, may be overridden per-target */
+ COMMON_ATTRS
+ ") )",
+ Cft_Database, metacfg, NULL, meta_cfadd },
+ { "( OLcfgDbOc:3.3 "
+ "NAME 'olcMetaTargetConfig' "
+ "DESC 'Meta target configuration' "
+ "SUP olcConfig STRUCTURAL "
+ "MUST ( olcMetaSub $ olcDbURI ) "
+ "MAY ( olcDbIDAssertAuthzFrom "
+ "$ olcDbIDAssertBind "
+ "$ olcDbMap "
+ "$ olcDbRewrite "
+ "$ olcDbSubtreeExclude "
+ "$ olcDbSubtreeInclude "
+ "$ olcDbTimeout "
+ "$ olcDbKeepalive "
+ "$ olcDbTcpUserTimeout "
+ "$ olcDbFilter "
+
+ /* defaults may be inherited */
+ COMMON_ATTRS
+ ") )",
+ Cft_Misc, metacfg, meta_ldadd },
+ { NULL, 0, NULL }
+};
+
+static int
+meta_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *c )
+{
+ if ( p->ce_type != Cft_Database || !p->ce_be ||
+ p->ce_be->be_cf_ocs != metaocs )
+ return LDAP_CONSTRAINT_VIOLATION;
+
+ c->be = p->ce_be;
+ return LDAP_SUCCESS;
+}
+
+static int
+meta_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *c )
+{
+ metainfo_t *mi = ( metainfo_t * )c->be->be_private;
+ struct berval bv;
+ int i;
+
+ bv.bv_val = c->cr_msg;
+ for ( i=0; i<mi->mi_ntargets; i++ ) {
+ bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "olcMetaSub=" SLAP_X_ORDERED_FMT "uri", i );
+ c->ca_private = mi->mi_targets[i];
+ c->valx = i;
+ config_build_entry( op, rs, p->e_private, c,
+ &bv, &metaocs[1], NULL );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+meta_rwi_init( struct rewrite_info **rwm_rw )
+{
+ char *rargv[ 3 ];
+
+ *rwm_rw = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
+ if ( *rwm_rw == NULL ) {
+ return -1;
+ }
+ /*
+ * the filter rewrite as a string must be disabled
+ * by default; it can be re-enabled by adding rules;
+ * this creates an empty rewriteContext
+ */
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "searchFilter";
+ rargv[ 2 ] = NULL;
+ rewrite_parse( *rwm_rw, "<suffix massage>", 1, 2, rargv );
+
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "default";
+ rargv[ 2 ] = NULL;
+ rewrite_parse( *rwm_rw, "<suffix massage>", 1, 2, rargv );
+
+ return 0;
+}
+
+static int
+meta_back_new_target(
+ metatarget_t **mtp )
+{
+ metatarget_t *mt;
+
+ *mtp = NULL;
+
+ mt = ch_calloc( sizeof( metatarget_t ), 1 );
+
+ if ( meta_rwi_init( &mt->mt_rwmap.rwm_rw )) {
+ ch_free( mt );
+ return -1;
+ }
+
+ ldap_pvt_thread_mutex_init( &mt->mt_uri_mutex );
+
+ mt->mt_idassert_mode = LDAP_BACK_IDASSERT_LEGACY;
+ mt->mt_idassert_authmethod = LDAP_AUTH_NONE;
+ mt->mt_idassert_tls = SB_TLS_DEFAULT;
+
+ /* by default, use proxyAuthz control on each operation */
+ mt->mt_idassert_flags = LDAP_BACK_AUTH_PRESCRIPTIVE;
+
+ *mtp = mt;
+
+ return 0;
+}
+
+/* Validation for suffixmassage_config */
+static int
+meta_suffixm_config(
+ ConfigArgs *c,
+ int argc,
+ char **argv,
+ metatarget_t *mt
+)
+{
+ BackendDB *tmp_bd;
+ struct berval dn, nvnc, pvnc, nrnc, prnc;
+ int j, rc;
+
+ /*
+ * syntax:
+ *
+ * suffixmassage <suffix> <massaged suffix>
+ *
+ * the <suffix> field must be defined as a valid suffix
+ * (or suffixAlias?) for the current database;
+ * the <massaged suffix> shouldn't have already been
+ * defined as a valid suffix or suffixAlias for the
+ * current server
+ */
+
+ ber_str2bv( argv[ 1 ], 0, 0, &dn );
+ if ( dnPrettyNormal( NULL, &dn, &pvnc, &nvnc, NULL ) != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "suffix \"%s\" is invalid",
+ argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ for ( j = 0; !BER_BVISNULL( &c->be->be_nsuffix[ j ] ); j++ ) {
+ if ( dnIsSuffix( &nvnc, &c->be->be_nsuffix[ 0 ] ) ) {
+ break;
+ }
+ }
+
+ if ( BER_BVISNULL( &c->be->be_nsuffix[ j ] ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "suffix \"%s\" must be within the database naming context",
+ argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ free( pvnc.bv_val );
+ free( nvnc.bv_val );
+ return 1;
+ }
+
+ ber_str2bv( argv[ 2 ], 0, 0, &dn );
+ if ( dnPrettyNormal( NULL, &dn, &prnc, &nrnc, NULL ) != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "massaged suffix \"%s\" is invalid",
+ argv[2] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ free( pvnc.bv_val );
+ free( nvnc.bv_val );
+ return 1;
+ }
+
+ tmp_bd = select_backend( &nrnc, 0 );
+ if ( tmp_bd != NULL && tmp_bd->be_private == c->be->be_private ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: warning: <massaged suffix> \"%s\" resolves to this database, in "
+ "\"suffixMassage <suffix> <massaged suffix>\"\n",
+ c->log, prnc.bv_val );
+ }
+
+ /*
+ * The suffix massaging is emulated by means of the
+ * rewrite capabilities
+ */
+ rc = suffix_massage_config( mt->mt_rwmap.rwm_rw,
+ &pvnc, &nvnc, &prnc, &nrnc );
+
+ free( pvnc.bv_val );
+ free( nvnc.bv_val );
+ free( prnc.bv_val );
+ free( nrnc.bv_val );
+
+ return rc;
+}
+
+int
+meta_subtree_free( metasubtree_t *ms )
+{
+ switch ( ms->ms_type ) {
+ case META_ST_SUBTREE:
+ case META_ST_SUBORDINATE:
+ ber_memfree( ms->ms_dn.bv_val );
+ break;
+
+ case META_ST_REGEX:
+ regfree( &ms->ms_regex );
+ ber_memfree( ms->ms_regex_pattern.bv_val );
+ break;
+
+ default:
+ return -1;
+ }
+
+ ch_free( ms );
+ return 0;
+}
+
+int
+meta_subtree_destroy( metasubtree_t *ms )
+{
+ if ( ms->ms_next ) {
+ meta_subtree_destroy( ms->ms_next );
+ }
+
+ return meta_subtree_free( ms );
+}
+
+static void
+meta_filter_free( metafilter_t *mf )
+{
+ regfree( &mf->mf_regex );
+ ber_memfree( mf->mf_regex_pattern.bv_val );
+ ch_free( mf );
+}
+
+void
+meta_filter_destroy( metafilter_t *mf )
+{
+ if ( mf->mf_next )
+ meta_filter_destroy( mf->mf_next );
+ meta_filter_free( mf );
+}
+
+static struct berval st_styles[] = {
+ BER_BVC("subtree"),
+ BER_BVC("children"),
+ BER_BVC("regex")
+};
+
+static int
+meta_subtree_unparse(
+ ConfigArgs *c,
+ metatarget_t *mt )
+{
+ metasubtree_t *ms;
+ struct berval bv, *style;
+
+ if ( !mt->mt_subtree )
+ return 1;
+
+ /* can only be one of exclude or include */
+ if (( c->type == LDAP_BACK_CFG_SUBTREE_EX ) ^ mt->mt_subtree_exclude )
+ return 1;
+
+ bv.bv_val = c->cr_msg;
+ for ( ms=mt->mt_subtree; ms; ms=ms->ms_next ) {
+ if (ms->ms_type == META_ST_SUBTREE)
+ style = &st_styles[0];
+ else if ( ms->ms_type == META_ST_SUBORDINATE )
+ style = &st_styles[1];
+ else if ( ms->ms_type == META_ST_REGEX )
+ style = &st_styles[2];
+ else {
+ assert(0);
+ continue;
+ }
+ bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "dn.%s:%s", style->bv_val, ms->ms_dn.bv_val );
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ return 0;
+}
+
+static int
+meta_subtree_config(
+ metatarget_t *mt,
+ ConfigArgs *c )
+{
+ meta_st_t type = META_ST_SUBTREE;
+ char *pattern;
+ struct berval ndn = BER_BVNULL;
+ metasubtree_t *ms = NULL;
+
+ if ( c->type == LDAP_BACK_CFG_SUBTREE_EX ) {
+ if ( mt->mt_subtree && !mt->mt_subtree_exclude ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "\"subtree-exclude\" incompatible with previous \"subtree-include\" directives" );
+ return 1;
+ }
+
+ mt->mt_subtree_exclude = 1;
+
+ } else {
+ if ( mt->mt_subtree && mt->mt_subtree_exclude ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "\"subtree-include\" incompatible with previous \"subtree-exclude\" directives" );
+ return 1;
+ }
+ }
+
+ pattern = c->argv[1];
+ if ( strncasecmp( pattern, "dn", STRLENOF( "dn" ) ) == 0 ) {
+ char *style;
+
+ pattern = &pattern[STRLENOF( "dn")];
+
+ if ( pattern[0] == '.' ) {
+ style = &pattern[1];
+
+ if ( strncasecmp( style, "subtree", STRLENOF( "subtree" ) ) == 0 ) {
+ type = META_ST_SUBTREE;
+ pattern = &style[STRLENOF( "subtree" )];
+
+ } else if ( strncasecmp( style, "children", STRLENOF( "children" ) ) == 0 ) {
+ type = META_ST_SUBORDINATE;
+ pattern = &style[STRLENOF( "children" )];
+
+ } else if ( strncasecmp( style, "sub", STRLENOF( "sub" ) ) == 0 ) {
+ type = META_ST_SUBTREE;
+ pattern = &style[STRLENOF( "sub" )];
+
+ } else if ( strncasecmp( style, "regex", STRLENOF( "regex" ) ) == 0 ) {
+ type = META_ST_REGEX;
+ pattern = &style[STRLENOF( "regex" )];
+
+ } else {
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "unknown style in \"dn.<style>\"" );
+ return 1;
+ }
+ }
+
+ if ( pattern[0] != ':' ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "missing colon after \"dn.<style>\"" );
+ return 1;
+ }
+ pattern++;
+ }
+
+ switch ( type ) {
+ case META_ST_SUBTREE:
+ case META_ST_SUBORDINATE: {
+ struct berval dn;
+
+ ber_str2bv( pattern, 0, 0, &dn );
+ if ( dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL )
+ != LDAP_SUCCESS )
+ {
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "DN=\"%s\" is invalid", pattern );
+ return 1;
+ }
+
+ if ( !dnIsSuffix( &ndn, &mt->mt_nsuffix ) ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "DN=\"%s\" is not a subtree of target \"%s\"",
+ pattern, mt->mt_nsuffix.bv_val );
+ ber_memfree( ndn.bv_val );
+ return( 1 );
+ }
+ } break;
+
+ default:
+ /* silence warnings */
+ break;
+ }
+
+ ms = ch_calloc( sizeof( metasubtree_t ), 1 );
+ ms->ms_type = type;
+
+ switch ( ms->ms_type ) {
+ case META_ST_SUBTREE:
+ case META_ST_SUBORDINATE:
+ ms->ms_dn = ndn;
+ break;
+
+ case META_ST_REGEX: {
+ int rc;
+
+ rc = regcomp( &ms->ms_regex, pattern, REG_EXTENDED|REG_ICASE );
+ if ( rc != 0 ) {
+ char regerr[ SLAP_TEXT_BUFLEN ];
+
+ regerror( rc, &ms->ms_regex, regerr, sizeof(regerr) );
+
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "regular expression \"%s\" bad because of %s",
+ pattern, regerr );
+ ch_free( ms );
+ return 1;
+ }
+ ber_str2bv( pattern, 0, 1, &ms->ms_regex_pattern );
+ } break;
+ }
+
+ if ( mt->mt_subtree == NULL ) {
+ mt->mt_subtree = ms;
+
+ } else {
+ metasubtree_t **msp;
+
+ for ( msp = &mt->mt_subtree; *msp; ) {
+ switch ( ms->ms_type ) {
+ case META_ST_SUBTREE:
+ switch ( (*msp)->ms_type ) {
+ case META_ST_SUBTREE:
+ if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) {
+ metasubtree_t *tmp = *msp;
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.subtree:%s\" is contained in rule \"dn.subtree:%s\" (replaced)\n",
+ c->log, pattern, (*msp)->ms_dn.bv_val );
+ *msp = (*msp)->ms_next;
+ tmp->ms_next = NULL;
+ meta_subtree_destroy( tmp );
+ continue;
+
+ } else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) ) {
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.subtree:%s\" contains rule \"dn.subtree:%s\" (ignored)\n",
+ c->log, (*msp)->ms_dn.bv_val, pattern );
+ meta_subtree_destroy( ms );
+ ms = NULL;
+ return( 0 );
+ }
+ break;
+
+ case META_ST_SUBORDINATE:
+ if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) {
+ metasubtree_t *tmp = *msp;
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.children:%s\" is contained in rule \"dn.subtree:%s\" (replaced)\n",
+ c->log, pattern, (*msp)->ms_dn.bv_val );
+ *msp = (*msp)->ms_next;
+ tmp->ms_next = NULL;
+ meta_subtree_destroy( tmp );
+ continue;
+
+ } else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) && ms->ms_dn.bv_len > (*msp)->ms_dn.bv_len ) {
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.children:%s\" contains rule \"dn.subtree:%s\" (ignored)\n",
+ c->log, (*msp)->ms_dn.bv_val, pattern );
+ meta_subtree_destroy( ms );
+ ms = NULL;
+ return( 0 );
+ }
+ break;
+
+ case META_ST_REGEX:
+ if ( regexec( &(*msp)->ms_regex, ms->ms_dn.bv_val, 0, NULL, 0 ) == 0 ) {
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.regex:%s\" may contain rule \"dn.subtree:%s\"\n",
+ c->log, (*msp)->ms_regex_pattern.bv_val, ms->ms_dn.bv_val );
+ }
+ break;
+ }
+ break;
+
+ case META_ST_SUBORDINATE:
+ switch ( (*msp)->ms_type ) {
+ case META_ST_SUBTREE:
+ if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) {
+ metasubtree_t *tmp = *msp;
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.children:%s\" is contained in rule \"dn.subtree:%s\" (replaced)\n",
+ c->log, pattern, (*msp)->ms_dn.bv_val );
+ *msp = (*msp)->ms_next;
+ tmp->ms_next = NULL;
+ meta_subtree_destroy( tmp );
+ continue;
+
+ } else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) && ms->ms_dn.bv_len > (*msp)->ms_dn.bv_len ) {
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.children:%s\" contains rule \"dn.subtree:%s\" (ignored)\n",
+ c->log, (*msp)->ms_dn.bv_val, pattern );
+ meta_subtree_destroy( ms );
+ ms = NULL;
+ return( 0 );
+ }
+ break;
+
+ case META_ST_SUBORDINATE:
+ if ( dnIsSuffix( &(*msp)->ms_dn, &ms->ms_dn ) ) {
+ metasubtree_t *tmp = *msp;
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.children:%s\" is contained in rule \"dn.children:%s\" (replaced)\n",
+ c->log, pattern, (*msp)->ms_dn.bv_val );
+ *msp = (*msp)->ms_next;
+ tmp->ms_next = NULL;
+ meta_subtree_destroy( tmp );
+ continue;
+
+ } else if ( dnIsSuffix( &ms->ms_dn, &(*msp)->ms_dn ) ) {
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.children:%s\" contains rule \"dn.children:%s\" (ignored)\n",
+ c->log, (*msp)->ms_dn.bv_val, pattern );
+ meta_subtree_destroy( ms );
+ ms = NULL;
+ return( 0 );
+ }
+ break;
+
+ case META_ST_REGEX:
+ if ( regexec( &(*msp)->ms_regex, ms->ms_dn.bv_val, 0, NULL, 0 ) == 0 ) {
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.regex:%s\" may contain rule \"dn.subtree:%s\"\n",
+ c->log, (*msp)->ms_regex_pattern.bv_val, ms->ms_dn.bv_val );
+ }
+ break;
+ }
+ break;
+
+ case META_ST_REGEX:
+ switch ( (*msp)->ms_type ) {
+ case META_ST_SUBTREE:
+ case META_ST_SUBORDINATE:
+ if ( regexec( &ms->ms_regex, (*msp)->ms_dn.bv_val, 0, NULL, 0 ) == 0 ) {
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: previous rule \"dn.subtree:%s\" may be contained in rule \"dn.regex:%s\"\n",
+ c->log, (*msp)->ms_dn.bv_val, ms->ms_regex_pattern.bv_val );
+ }
+ break;
+
+ case META_ST_REGEX:
+ /* no check possible */
+ break;
+ }
+ break;
+ }
+
+ msp = &(*msp)->ms_next;
+ }
+
+ *msp = ms;
+ }
+
+ return 0;
+}
+
+static slap_verbmasks idassert_mode[] = {
+ { BER_BVC("self"), LDAP_BACK_IDASSERT_SELF },
+ { BER_BVC("anonymous"), LDAP_BACK_IDASSERT_ANONYMOUS },
+ { BER_BVC("none"), LDAP_BACK_IDASSERT_NOASSERT },
+ { BER_BVC("legacy"), LDAP_BACK_IDASSERT_LEGACY },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks tls_mode[] = {
+ { BER_BVC( "propagate" ), LDAP_BACK_F_TLS_PROPAGATE_MASK },
+ { BER_BVC( "try-propagate" ), LDAP_BACK_F_PROPAGATE_TLS },
+ { BER_BVC( "start" ), LDAP_BACK_F_TLS_USE_MASK },
+ { BER_BVC( "try-start" ), LDAP_BACK_F_USE_TLS },
+ { BER_BVC( "ldaps" ), LDAP_BACK_F_TLS_LDAPS },
+ { BER_BVC( "none" ), LDAP_BACK_F_NONE },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks t_f_mode[] = {
+ { BER_BVC( "yes" ), LDAP_BACK_F_T_F },
+ { BER_BVC( "discover" ), LDAP_BACK_F_T_F_DISCOVER },
+ { BER_BVC( "no" ), LDAP_BACK_F_NONE },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks cancel_mode[] = {
+ { BER_BVC( "ignore" ), LDAP_BACK_F_CANCEL_IGNORE },
+ { BER_BVC( "exop" ), LDAP_BACK_F_CANCEL_EXOP },
+ { BER_BVC( "exop-discover" ), LDAP_BACK_F_CANCEL_EXOP_DISCOVER },
+ { BER_BVC( "abandon" ), LDAP_BACK_F_CANCEL_ABANDON },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks onerr_mode[] = {
+ { BER_BVC( "stop" ), META_BACK_F_ONERR_STOP },
+ { BER_BVC( "report" ), META_BACK_F_ONERR_REPORT },
+ { BER_BVC( "continue" ), LDAP_BACK_F_NONE },
+ { BER_BVNULL, 0 }
+};
+
+/* see enum in slap.h */
+static slap_cf_aux_table timeout_table[] = {
+ { BER_BVC("bind="), SLAP_OP_BIND * sizeof( time_t ), 'u', 0, NULL },
+ /* unbind makes no sense */
+ { BER_BVC("add="), SLAP_OP_ADD * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("delete="), SLAP_OP_DELETE * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("modrdn="), SLAP_OP_MODRDN * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("modify="), SLAP_OP_MODIFY * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("compare="), SLAP_OP_COMPARE * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("search="), SLAP_OP_SEARCH * sizeof( time_t ), 'u', 0, NULL },
+ /* abandon makes little sense */
+#if 0 /* not implemented yet */
+ { BER_BVC("extended="), SLAP_OP_EXTENDED * sizeof( time_t ), 'u', 0, NULL },
+#endif
+ { BER_BVNULL, 0, 0, 0, NULL }
+};
+
+static int
+meta_cf_cleanup( ConfigArgs *c )
+{
+ metainfo_t *mi = ( metainfo_t * )c->be->be_private;
+ metatarget_t *mt = c->ca_private;
+
+ return meta_target_finish( mi, mt, c->log, c->cr_msg, sizeof( c->cr_msg ));
+}
+
+static int
+meta_back_cf_gen( ConfigArgs *c )
+{
+ metainfo_t *mi = ( metainfo_t * )c->be->be_private;
+ metatarget_t *mt;
+ metacommon_t *mc;
+
+ int i, rc = 0;
+
+ assert( mi != NULL );
+
+ if ( c->op == SLAP_CONFIG_EMIT || c->op == LDAP_MOD_DELETE ) {
+ if ( !mi )
+ return 1;
+
+ if ( c->table == Cft_Database ) {
+ mt = NULL;
+ mc = &mi->mi_mc;
+ } else {
+ mt = c->ca_private;
+ mc = &mt->mt_mc;
+ }
+ }
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ struct berval bv = BER_BVNULL;
+
+ switch( c->type ) {
+ /* Base attrs */
+ case LDAP_BACK_CFG_CONN_TTL:
+ if ( mi->mi_conn_ttl == 0 ) {
+ return 1;
+ } else {
+ char buf[ SLAP_TEXT_BUFLEN ];
+
+ lutil_unparse_time( buf, sizeof( buf ), mi->mi_conn_ttl );
+ ber_str2bv( buf, 0, 0, &bv );
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
+
+ case LDAP_BACK_CFG_DNCACHE_TTL:
+ if ( mi->mi_cache.ttl == META_DNCACHE_DISABLED ) {
+ return 1;
+ } else if ( mi->mi_cache.ttl == META_DNCACHE_FOREVER ) {
+ BER_BVSTR( &bv, "forever" );
+ } else {
+ char buf[ SLAP_TEXT_BUFLEN ];
+
+ lutil_unparse_time( buf, sizeof( buf ), mi->mi_cache.ttl );
+ ber_str2bv( buf, 0, 0, &bv );
+ }
+ value_add_one( &c->rvalue_vals, &bv );
+ break;
+
+ case LDAP_BACK_CFG_IDLE_TIMEOUT:
+ if ( mi->mi_idle_timeout == 0 ) {
+ return 1;
+ } else {
+ char buf[ SLAP_TEXT_BUFLEN ];
+
+ lutil_unparse_time( buf, sizeof( buf ), mi->mi_idle_timeout );
+ ber_str2bv( buf, 0, 0, &bv );
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
+
+ case LDAP_BACK_CFG_ONERR:
+ enum_to_verb( onerr_mode, mi->mi_flags & META_BACK_F_ONERR_MASK, &bv );
+ if ( BER_BVISNULL( &bv )) {
+ rc = 1;
+ } else {
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
+
+ case LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER:
+ c->value_int = META_BACK_DEFER_ROOTDN_BIND( mi );
+ break;
+
+ case LDAP_BACK_CFG_SINGLECONN:
+ c->value_int = LDAP_BACK_SINGLECONN( mi );
+ break;
+
+ case LDAP_BACK_CFG_USETEMP:
+ c->value_int = LDAP_BACK_USE_TEMPORARIES( mi );
+ break;
+
+ case LDAP_BACK_CFG_CONNPOOLMAX:
+ c->value_int = mi->mi_conn_priv_max;
+ break;
+
+ /* common attrs */
+ case LDAP_BACK_CFG_BIND_TIMEOUT:
+ if ( mc->mc_bind_timeout.tv_sec == 0 &&
+ mc->mc_bind_timeout.tv_usec == 0 ) {
+ return 1;
+ } else {
+ c->value_ulong = mc->mc_bind_timeout.tv_sec * 1000000UL +
+ mc->mc_bind_timeout.tv_usec;
+ }
+ break;
+
+ case LDAP_BACK_CFG_CANCEL: {
+ slap_mask_t mask = LDAP_BACK_F_CANCEL_MASK2;
+
+ if ( mt && META_BACK_TGT_CANCEL_DISCOVER( mt ) ) {
+ mask &= ~LDAP_BACK_F_CANCEL_EXOP;
+ }
+ enum_to_verb( cancel_mode, (mc->mc_flags & mask), &bv );
+ if ( BER_BVISNULL( &bv ) ) {
+ /* there's something wrong... */
+ assert( 0 );
+ rc = 1;
+
+ } else {
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ } break;
+
+ case LDAP_BACK_CFG_CHASE:
+ c->value_int = META_BACK_CMN_CHASE_REFERRALS(mc);
+ break;
+
+#ifdef SLAPD_META_CLIENT_PR
+ case LDAP_BACK_CFG_CLIENT_PR:
+ if ( mc->mc_ps == META_CLIENT_PR_DISABLE ) {
+ return 1;
+ } else if ( mc->mc_ps == META_CLIENT_PR_ACCEPT_UNSOLICITED ) {
+ BER_BVSTR( &bv, "accept-unsolicited" );
+ } else {
+ bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%d", mc->mc_ps );
+ bv.bv_val = c->cr_msg;
+ }
+ value_add_one( &c->rvalue_vals, &bv );
+ break;
+#endif /* SLAPD_META_CLIENT_PR */
+
+ case LDAP_BACK_CFG_DEFAULT_T:
+ if ( mt || mi->mi_defaulttarget == META_DEFAULT_TARGET_NONE )
+ return 1;
+ bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%d", mi->mi_defaulttarget );
+ bv.bv_val = c->cr_msg;
+ value_add_one( &c->rvalue_vals, &bv );
+ break;
+
+ case LDAP_BACK_CFG_NETWORK_TIMEOUT:
+ if ( mc->mc_network_timeout == 0 ) {
+ return 1;
+ } else {
+ char buf[ SLAP_TEXT_BUFLEN ];
+ lutil_unparse_time( buf, sizeof( buf ), mc->mc_network_timeout );
+ ber_str2bv( buf, 0, 0, &bv );
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
+
+ case LDAP_BACK_CFG_NOREFS:
+ c->value_int = META_BACK_CMN_NOREFS(mc);
+ break;
+
+ case LDAP_BACK_CFG_NOUNDEFFILTER:
+ c->value_int = META_BACK_CMN_NOUNDEFFILTER(mc);
+ break;
+
+ case LDAP_BACK_CFG_NRETRIES:
+ if ( mc->mc_nretries == META_RETRY_FOREVER ) {
+ BER_BVSTR( &bv, "forever" );
+ } else if ( mc->mc_nretries == META_RETRY_NEVER ) {
+ BER_BVSTR( &bv, "never" );
+ } else {
+ bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%d",
+ mc->mc_nretries );
+ bv.bv_val = c->cr_msg;
+ }
+ value_add_one( &c->rvalue_vals, &bv );
+ break;
+
+ case LDAP_BACK_CFG_QUARANTINE:
+ if ( !META_BACK_CMN_QUARANTINE( mc )) {
+ rc = 1;
+ break;
+ }
+ rc = mi->mi_ldap_extra->retry_info_unparse( &mc->mc_quarantine, &bv );
+ if ( rc == 0 ) {
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ }
+ break;
+
+ case LDAP_BACK_CFG_REBIND:
+ c->value_int = META_BACK_CMN_SAVECRED(mc);
+ break;
+
+ case LDAP_BACK_CFG_TIMEOUT:
+ for ( i = 0; i < SLAP_OP_LAST; i++ ) {
+ if ( mc->mc_timeout[ i ] != 0 ) {
+ break;
+ }
+ }
+
+ if ( i == SLAP_OP_LAST ) {
+ return 1;
+ }
+
+ BER_BVZERO( &bv );
+ slap_cf_aux_table_unparse( mc->mc_timeout, &bv, timeout_table );
+
+ if ( BER_BVISNULL( &bv ) ) {
+ return 1;
+ }
+
+ for ( i = 0; isspace( (unsigned char) bv.bv_val[ i ] ); i++ )
+ /* count spaces */ ;
+
+ if ( i ) {
+ bv.bv_len -= i;
+ AC_MEMCPY( bv.bv_val, &bv.bv_val[ i ],
+ bv.bv_len + 1 );
+ }
+
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ break;
+
+ case LDAP_BACK_CFG_VERSION:
+ if ( mc->mc_version == 0 )
+ return 1;
+ c->value_int = mc->mc_version;
+ break;
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ case LDAP_BACK_CFG_ST_REQUEST:
+ c->value_int = META_BACK_CMN_ST_REQUEST( mc );
+ break;
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+ case LDAP_BACK_CFG_T_F:
+ enum_to_verb( t_f_mode, (mc->mc_flags & LDAP_BACK_F_T_F_MASK2), &bv );
+ if ( BER_BVISNULL( &bv ) ) {
+ /* there's something wrong... */
+ assert( 0 );
+ rc = 1;
+
+ } else {
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
+
+ case LDAP_BACK_CFG_TLS: {
+ struct berval bc = BER_BVNULL, bv2;
+
+ if (( mc->mc_flags & LDAP_BACK_F_TLS_MASK ) == LDAP_BACK_F_NONE ) {
+ rc = 1;
+ break;
+ }
+ enum_to_verb( tls_mode, ( mc->mc_flags & LDAP_BACK_F_TLS_MASK ), &bv );
+ assert( !BER_BVISNULL( &bv ) );
+
+ if ( mt ) {
+ bindconf_tls_unparse( &mt->mt_tls, &bc );
+ }
+
+ if ( !BER_BVISEMPTY( &bc )) {
+ bv2.bv_len = bv.bv_len + bc.bv_len + 1;
+ bv2.bv_val = ch_malloc( bv2.bv_len + 1 );
+ strcpy( bv2.bv_val, bv.bv_val );
+ bv2.bv_val[bv.bv_len] = ' ';
+ strcpy( &bv2.bv_val[bv.bv_len + 1], bc.bv_val );
+ ber_memfree( bc.bv_val );
+ ber_bvarray_add( &c->rvalue_vals, &bv2 );
+ } else {
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ } break;
+
+ /* target attrs */
+ case LDAP_BACK_CFG_URI: {
+ char *p2, *p1 = strchr( mt->mt_uri, ' ' );
+ bv.bv_len = strlen( mt->mt_uri ) + 3 + mt->mt_psuffix.bv_len;
+ bv.bv_val = ch_malloc( bv.bv_len + 1 );
+ p2 = bv.bv_val;
+ *p2++ = '"';
+ if ( p1 ) {
+ p2 = lutil_strncopy( p2, mt->mt_uri, p1 - mt->mt_uri );
+ } else {
+ p2 = lutil_strcopy( p2, mt->mt_uri );
+ }
+ *p2++ = '/';
+ p2 = lutil_strcopy( p2, mt->mt_psuffix.bv_val );
+ *p2++ = '"';
+ if ( p1 ) {
+ strcpy( p2, p1 );
+ }
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ } break;
+
+ case LDAP_BACK_CFG_IDASSERT_AUTHZFROM: {
+ BerVarray *bvp;
+ int i;
+ struct berval bv = BER_BVNULL;
+ char buf[SLAP_TEXT_BUFLEN];
+
+ bvp = &mt->mt_idassert_authz;
+ if ( *bvp == NULL ) {
+ if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL )
+ {
+ BER_BVSTR( &bv, "*" );
+ value_add_one( &c->rvalue_vals, &bv );
+
+ } else {
+ rc = 1;
+ }
+ break;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &((*bvp)[ i ]) ); i++ ) {
+ char *ptr;
+ int len = snprintf( buf, sizeof( buf ), SLAP_X_ORDERED_FMT, i );
+ bv.bv_len = ((*bvp)[ i ]).bv_len + len;
+ bv.bv_val = ch_realloc( bv.bv_val, bv.bv_len + 1 );
+ ptr = bv.bv_val;
+ ptr = lutil_strcopy( ptr, buf );
+ ptr = lutil_strncopy( ptr, ((*bvp)[ i ]).bv_val, ((*bvp)[ i ]).bv_len );
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ if ( bv.bv_val ) {
+ ber_memfree( bv.bv_val );
+ }
+ break;
+ }
+
+ case LDAP_BACK_CFG_IDASSERT_BIND: {
+ int i;
+ struct berval bc = BER_BVNULL;
+ char *ptr;
+
+ if ( mt->mt_idassert_authmethod == LDAP_AUTH_NONE ) {
+ return 1;
+ } else {
+ ber_len_t len;
+
+ switch ( mt->mt_idassert_mode ) {
+ case LDAP_BACK_IDASSERT_OTHERID:
+ case LDAP_BACK_IDASSERT_OTHERDN:
+ break;
+
+ default: {
+ struct berval mode = BER_BVNULL;
+
+ enum_to_verb( idassert_mode, mt->mt_idassert_mode, &mode );
+ if ( BER_BVISNULL( &mode ) ) {
+ /* there's something wrong... */
+ assert( 0 );
+ rc = 1;
+
+ } else {
+ bv.bv_len = STRLENOF( "mode=" ) + mode.bv_len;
+ bv.bv_val = ch_malloc( bv.bv_len + 1 );
+
+ ptr = lutil_strcopy( bv.bv_val, "mode=" );
+ ptr = lutil_strcopy( ptr, mode.bv_val );
+ }
+ break;
+ }
+ }
+
+ if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) {
+ len = bv.bv_len + STRLENOF( "authz=native" );
+
+ if ( !BER_BVISEMPTY( &bv ) ) {
+ len += STRLENOF( " " );
+ }
+
+ bv.bv_val = ch_realloc( bv.bv_val, len + 1 );
+
+ ptr = &bv.bv_val[ bv.bv_len ];
+
+ if ( !BER_BVISEMPTY( &bv ) ) {
+ ptr = lutil_strcopy( ptr, " " );
+ }
+
+ (void)lutil_strcopy( ptr, "authz=native" );
+ }
+
+ len = bv.bv_len + STRLENOF( "flags=non-prescriptive,override,obsolete-encoding-workaround,proxy-authz-non-critical,dn-authzid" );
+ /* flags */
+ if ( !BER_BVISEMPTY( &bv ) ) {
+ len += STRLENOF( " " );
+ }
+
+ bv.bv_val = ch_realloc( bv.bv_val, len + 1 );
+
+ ptr = &bv.bv_val[ bv.bv_len ];
+
+ if ( !BER_BVISEMPTY( &bv ) ) {
+ ptr = lutil_strcopy( ptr, " " );
+ }
+
+ ptr = lutil_strcopy( ptr, "flags=" );
+
+ if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
+ ptr = lutil_strcopy( ptr, "prescriptive" );
+ } else {
+ ptr = lutil_strcopy( ptr, "non-prescriptive" );
+ }
+
+ if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
+ ptr = lutil_strcopy( ptr, ",override" );
+ }
+
+ if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) {
+ ptr = lutil_strcopy( ptr, ",obsolete-proxy-authz" );
+
+ } else if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) {
+ ptr = lutil_strcopy( ptr, ",obsolete-encoding-workaround" );
+ }
+
+ if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL ) {
+ ptr = lutil_strcopy( ptr, ",proxy-authz-critical" );
+
+ } else {
+ ptr = lutil_strcopy( ptr, ",proxy-authz-non-critical" );
+ }
+
+#ifdef SLAP_AUTH_DN
+ switch ( mt->mt_idassert_flags & LDAP_BACK_AUTH_DN_MASK ) {
+ case LDAP_BACK_AUTH_DN_AUTHZID:
+ ptr = lutil_strcopy( ptr, ",dn-authzid" );
+ break;
+
+ case LDAP_BACK_AUTH_DN_WHOAMI:
+ ptr = lutil_strcopy( ptr, ",dn-whoami" );
+ break;
+
+ default:
+#if 0 /* implicit */
+ ptr = lutil_strcopy( ptr, ",dn-none" );
+#endif
+ break;
+ }
+#endif
+
+ bv.bv_len = ( ptr - bv.bv_val );
+ /* end-of-flags */
+ }
+
+ bindconf_unparse( &mt->mt_idassert.si_bc, &bc );
+
+ if ( !BER_BVISNULL( &bv ) ) {
+ ber_len_t len = bv.bv_len + bc.bv_len;
+
+ bv.bv_val = ch_realloc( bv.bv_val, len + 1 );
+
+ assert( bc.bv_val[ 0 ] == ' ' );
+
+ ptr = lutil_strcopy( &bv.bv_val[ bv.bv_len ], bc.bv_val );
+ free( bc.bv_val );
+ bv.bv_len = ptr - bv.bv_val;
+
+ } else {
+ for ( i = 0; isspace( (unsigned char) bc.bv_val[ i ] ); i++ )
+ /* count spaces */ ;
+
+ if ( i ) {
+ bc.bv_len -= i;
+ AC_MEMCPY( bc.bv_val, &bc.bv_val[ i ], bc.bv_len + 1 );
+ }
+
+ bv = bc;
+ }
+
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+
+ break;
+ }
+
+ case LDAP_BACK_CFG_SUFFIXM: /* unused */
+ case LDAP_BACK_CFG_REWRITE:
+ if ( mt->mt_rwmap.rwm_bva_rewrite == NULL ) {
+ rc = 1;
+ } else {
+ rc = slap_bv_x_ordered_unparse( mt->mt_rwmap.rwm_bva_rewrite, &c->rvalue_vals );
+ }
+ break;
+
+ case LDAP_BACK_CFG_MAP:
+ if ( mt->mt_rwmap.rwm_bva_map == NULL ) {
+ rc = 1;
+ } else {
+ rc = slap_bv_x_ordered_unparse( mt->mt_rwmap.rwm_bva_map, &c->rvalue_vals );
+ }
+ break;
+
+ case LDAP_BACK_CFG_SUBTREE_EX:
+ case LDAP_BACK_CFG_SUBTREE_IN:
+ rc = meta_subtree_unparse( c, mt );
+ break;
+
+ case LDAP_BACK_CFG_FILTER:
+ if ( mt->mt_filter == NULL ) {
+ rc = 1;
+ } else {
+ metafilter_t *mf;
+ for ( mf = mt->mt_filter; mf; mf = mf->mf_next )
+ value_add_one( &c->rvalue_vals, &mf->mf_regex_pattern );
+ }
+ break;
+
+ /* replaced by idassert */
+ case LDAP_BACK_CFG_PSEUDOROOTDN:
+ case LDAP_BACK_CFG_PSEUDOROOTPW:
+ rc = 1;
+ break;
+
+ case LDAP_BACK_CFG_KEEPALIVE: {
+ struct berval bv;
+ char buf[AC_LINE_MAX];
+ bv.bv_len = AC_LINE_MAX;
+ bv.bv_val = &buf[0];
+ slap_keepalive_parse(&bv, &mt->mt_tls.sb_keepalive, 0, 0, 1);
+ value_add_one( &c->rvalue_vals, &bv );
+ break;
+ }
+
+ case LDAP_BACK_CFG_TCP_USER_TIMEOUT:
+ c->value_uint = mt->mt_tls.sb_tcp_user_timeout;
+ break;
+
+
+ default:
+ rc = 1;
+ }
+ return rc;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ switch( c->type ) {
+ /* Base attrs */
+ case LDAP_BACK_CFG_CONN_TTL:
+ mi->mi_conn_ttl = 0;
+ break;
+
+ case LDAP_BACK_CFG_DNCACHE_TTL:
+ mi->mi_cache.ttl = META_DNCACHE_DISABLED;
+ break;
+
+ case LDAP_BACK_CFG_IDLE_TIMEOUT:
+ mi->mi_idle_timeout = 0;
+ break;
+
+ case LDAP_BACK_CFG_ONERR:
+ mi->mi_flags &= ~META_BACK_F_ONERR_MASK;
+ break;
+
+ case LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER:
+ mi->mi_flags &= ~META_BACK_F_DEFER_ROOTDN_BIND;
+ break;
+
+ case LDAP_BACK_CFG_SINGLECONN:
+ mi->mi_flags &= ~LDAP_BACK_F_SINGLECONN;
+ break;
+
+ case LDAP_BACK_CFG_USETEMP:
+ mi->mi_flags &= ~LDAP_BACK_F_USE_TEMPORARIES;
+ break;
+
+ case LDAP_BACK_CFG_CONNPOOLMAX:
+ mi->mi_conn_priv_max = LDAP_BACK_CONN_PRIV_MIN;
+ break;
+
+ /* common attrs */
+ case LDAP_BACK_CFG_BIND_TIMEOUT:
+ mc->mc_bind_timeout.tv_sec = 0;
+ mc->mc_bind_timeout.tv_usec = 0;
+ break;
+
+ case LDAP_BACK_CFG_CANCEL:
+ mc->mc_flags &= ~LDAP_BACK_F_CANCEL_MASK2;
+ break;
+
+ case LDAP_BACK_CFG_CHASE:
+ mc->mc_flags &= ~LDAP_BACK_F_CHASE_REFERRALS;
+ break;
+
+#ifdef SLAPD_META_CLIENT_PR
+ case LDAP_BACK_CFG_CLIENT_PR:
+ mc->mc_ps = META_CLIENT_PR_DISABLE;
+ break;
+#endif /* SLAPD_META_CLIENT_PR */
+
+ case LDAP_BACK_CFG_DEFAULT_T:
+ mi->mi_defaulttarget = META_DEFAULT_TARGET_NONE;
+ break;
+
+ case LDAP_BACK_CFG_NETWORK_TIMEOUT:
+ mc->mc_network_timeout = 0;
+ break;
+
+ case LDAP_BACK_CFG_NOREFS:
+ mc->mc_flags &= ~LDAP_BACK_F_NOREFS;
+ break;
+
+ case LDAP_BACK_CFG_NOUNDEFFILTER:
+ mc->mc_flags &= ~LDAP_BACK_F_NOUNDEFFILTER;
+ break;
+
+ case LDAP_BACK_CFG_NRETRIES:
+ mc->mc_nretries = META_RETRY_DEFAULT;
+ break;
+
+ case LDAP_BACK_CFG_QUARANTINE:
+ if ( META_BACK_CMN_QUARANTINE( mc )) {
+ mi->mi_ldap_extra->retry_info_destroy( &mc->mc_quarantine );
+ mc->mc_flags &= ~LDAP_BACK_F_QUARANTINE;
+ if ( mc == &mt->mt_mc ) {
+ ldap_pvt_thread_mutex_destroy( &mt->mt_quarantine_mutex );
+ mt->mt_isquarantined = 0;
+ }
+ }
+ break;
+
+ case LDAP_BACK_CFG_REBIND:
+ mc->mc_flags &= ~LDAP_BACK_F_SAVECRED;
+ break;
+
+ case LDAP_BACK_CFG_TIMEOUT:
+ for ( i = 0; i < SLAP_OP_LAST; i++ ) {
+ mc->mc_timeout[ i ] = 0;
+ }
+ break;
+
+ case LDAP_BACK_CFG_VERSION:
+ mc->mc_version = 0;
+ break;
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ case LDAP_BACK_CFG_ST_REQUEST:
+ mc->mc_flags &= ~LDAP_BACK_F_ST_REQUEST;
+ break;
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+ case LDAP_BACK_CFG_T_F:
+ mc->mc_flags &= ~LDAP_BACK_F_T_F_MASK2;
+ break;
+
+ case LDAP_BACK_CFG_TLS:
+ mc->mc_flags &= ~LDAP_BACK_F_TLS_MASK;
+ if ( mt )
+ bindconf_free( &mt->mt_tls );
+ break;
+
+ /* target attrs */
+ case LDAP_BACK_CFG_URI:
+ if ( mt->mt_uri ) {
+ ch_free( mt->mt_uri );
+ mt->mt_uri = NULL;
+ }
+ /* FIXME: should have a way to close all cached
+ * connections associated with this target.
+ */
+ break;
+
+ case LDAP_BACK_CFG_IDASSERT_AUTHZFROM: {
+ BerVarray *bvp;
+
+ bvp = &mt->mt_idassert_authz;
+ if ( c->valx < 0 ) {
+ if ( *bvp != NULL ) {
+ ber_bvarray_free( *bvp );
+ *bvp = NULL;
+ }
+
+ } else {
+ if ( *bvp == NULL ) {
+ rc = 1;
+ break;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &((*bvp)[ i ]) ); i++ )
+ ;
+
+ if ( i >= c->valx ) {
+ rc = 1;
+ break;
+ }
+ ber_memfree( ((*bvp)[ c->valx ]).bv_val );
+ for ( i = c->valx; !BER_BVISNULL( &((*bvp)[ i + 1 ]) ); i++ ) {
+ (*bvp)[ i ] = (*bvp)[ i + 1 ];
+ }
+ BER_BVZERO( &((*bvp)[ i ]) );
+ }
+ } break;
+
+ case LDAP_BACK_CFG_IDASSERT_BIND:
+ bindconf_free( &mt->mt_idassert.si_bc );
+ memset( &mt->mt_idassert, 0, sizeof( slap_idassert_t ) );
+ break;
+
+ case LDAP_BACK_CFG_SUFFIXM: /* unused */
+ case LDAP_BACK_CFG_REWRITE:
+ {
+ if ( c->valx >= 0 ) {
+ int i;
+
+ for ( i = 0; !BER_BVISNULL( &mt->mt_rwmap.rwm_bva_rewrite[ i ] ); i++ );
+
+ if ( c->valx >= i ) {
+ rc = 1;
+ break;
+ }
+
+ ber_memfree( mt->mt_rwmap.rwm_bva_rewrite[ c->valx ].bv_val );
+ for ( i = c->valx; !BER_BVISNULL( &mt->mt_rwmap.rwm_bva_rewrite[ i + 1 ] ); i++ )
+ {
+ mt->mt_rwmap.rwm_bva_rewrite[ i ] = mt->mt_rwmap.rwm_bva_rewrite[ i + 1 ];
+ }
+ BER_BVZERO( &mt->mt_rwmap.rwm_bva_rewrite[ i ] );
+
+ rewrite_info_delete( &mt->mt_rwmap.rwm_rw );
+ assert( mt->mt_rwmap.rwm_rw == NULL );
+
+ rc = meta_rwi_init( &mt->mt_rwmap.rwm_rw );
+
+ for ( i = 0; !BER_BVISNULL( &mt->mt_rwmap.rwm_bva_rewrite[ i ] ); i++ )
+ {
+ ConfigArgs ca = { 0 };
+
+ ca.line = mt->mt_rwmap.rwm_bva_rewrite[ i ].bv_val;
+ init_config_argv( &ca );
+ config_parse_ldif( &ca );
+
+ if ( !strcasecmp( ca.argv[0], "suffixmassage" )) {
+ rc = meta_suffixm_config( &ca, ca.argc, ca.argv, mt );
+ } else {
+ rc = rewrite_parse( mt->mt_rwmap.rwm_rw,
+ c->fname, c->lineno, ca.argc, ca.argv );
+ }
+
+
+ ch_free( ca.tline );
+ ch_free( ca.argv );
+
+ assert( rc == 0 );
+ }
+
+ } else if ( mt->mt_rwmap.rwm_rw != NULL ) {
+ if ( mt->mt_rwmap.rwm_bva_rewrite ) {
+ ber_bvarray_free( mt->mt_rwmap.rwm_bva_rewrite );
+ mt->mt_rwmap.rwm_bva_rewrite = NULL;
+ }
+ if ( mt->mt_rwmap.rwm_rw )
+ rewrite_info_delete( &mt->mt_rwmap.rwm_rw );
+
+ meta_rwi_init( &mt->mt_rwmap.rwm_rw );
+ }
+ }
+ break;
+
+ case LDAP_BACK_CFG_MAP:
+ if ( mt->mt_rwmap.rwm_bva_map ) {
+ ber_bvarray_free( mt->mt_rwmap.rwm_bva_map );
+ mt->mt_rwmap.rwm_bva_map = NULL;
+ }
+ meta_back_map_free( &mt->mt_rwmap.rwm_oc );
+ meta_back_map_free( &mt->mt_rwmap.rwm_at );
+ mt->mt_rwmap.rwm_oc.drop_missing = 0;
+ mt->mt_rwmap.rwm_at.drop_missing = 0;
+ break;
+
+ case LDAP_BACK_CFG_SUBTREE_EX:
+ case LDAP_BACK_CFG_SUBTREE_IN:
+ /* can only be one of exclude or include */
+ if (( c->type == LDAP_BACK_CFG_SUBTREE_EX ) ^ mt->mt_subtree_exclude ) {
+ rc = 1;
+ break;
+ }
+ if ( c->valx < 0 ) {
+ meta_subtree_destroy( mt->mt_subtree );
+ mt->mt_subtree = NULL;
+ } else {
+ metasubtree_t *ms, **mprev;
+ for (i=0, mprev = &mt->mt_subtree, ms = *mprev; ms; ms = *mprev) {
+ if ( i == c->valx ) {
+ *mprev = ms->ms_next;
+ meta_subtree_free( ms );
+ break;
+ }
+ i++;
+ mprev = &ms->ms_next;
+ }
+ if ( i != c->valx )
+ rc = 1;
+ }
+ break;
+
+ case LDAP_BACK_CFG_FILTER:
+ if ( c->valx < 0 ) {
+ meta_filter_destroy( mt->mt_filter );
+ mt->mt_filter = NULL;
+ } else {
+ metafilter_t *mf, **mprev;
+ for (i=0, mprev = &mt->mt_filter, mf = *mprev; mf; mf = *mprev) {
+ if ( i == c->valx ) {
+ *mprev = mf->mf_next;
+ meta_filter_free( mf );
+ break;
+ }
+ i++;
+ mprev = &mf->mf_next;
+ }
+ if ( i != c->valx )
+ rc = 1;
+ }
+ break;
+
+ case LDAP_BACK_CFG_KEEPALIVE:
+ mt->mt_tls.sb_keepalive.sk_idle = 0;
+ mt->mt_tls.sb_keepalive.sk_probes = 0;
+ mt->mt_tls.sb_keepalive.sk_interval = 0;
+ break;
+
+ case LDAP_BACK_CFG_TCP_USER_TIMEOUT:
+ mt->mt_tls.sb_tcp_user_timeout = 0;
+ break;
+
+ default:
+ rc = 1;
+ break;
+ }
+
+ return rc;
+ }
+
+ if ( c->op == SLAP_CONFIG_ADD ) {
+ if ( c->type >= LDAP_BACK_CFG_LAST_BASE ) {
+ /* exclude CFG_URI from this check */
+ if ( c->type > LDAP_BACK_CFG_LAST_BOTH ) {
+ if ( !mi->mi_ntargets ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "need \"uri\" directive first" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ }
+ if ( mi->mi_ntargets ) {
+ mt = mi->mi_targets[ mi->mi_ntargets-1 ];
+ mc = &mt->mt_mc;
+ } else {
+ mt = NULL;
+ mc = &mi->mi_mc;
+ }
+ }
+ } else {
+ if ( c->table == Cft_Database ) {
+ mt = NULL;
+ mc = &mi->mi_mc;
+ } else {
+ mt = c->ca_private;
+ if ( mt )
+ mc = &mt->mt_mc;
+ else
+ mc = NULL;
+ }
+ }
+
+ switch( c->type ) {
+ case LDAP_BACK_CFG_URI: {
+ LDAPURLDesc *ludp;
+ struct berval dn;
+ int j;
+
+ char **uris = NULL;
+
+ if ( c->be->be_nsuffix == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "the suffix must be defined before any target" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ i = mi->mi_ntargets++;
+
+ mi->mi_targets = ( metatarget_t ** )ch_realloc( mi->mi_targets,
+ sizeof( metatarget_t * ) * mi->mi_ntargets );
+ if ( mi->mi_targets == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "out of memory while storing server name"
+ " in \"%s <protocol>://<server>[:port]/<naming context>\"",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( meta_back_new_target( &mi->mi_targets[ i ] ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to init server"
+ " in \"%s <protocol>://<server>[:port]/<naming context>\"",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ mt = mi->mi_targets[ i ];
+
+ mt->mt_rebind_f = mi->mi_rebind_f;
+ mt->mt_urllist_f = mi->mi_urllist_f;
+ mt->mt_urllist_p = mt;
+
+ if ( META_BACK_QUARANTINE( mi ) ) {
+ ldap_pvt_thread_mutex_init( &mt->mt_quarantine_mutex );
+ }
+ mt->mt_mc = mi->mi_mc;
+
+ for ( j = 1; j < c->argc; j++ ) {
+ char **tmpuris = ldap_str2charray( c->argv[ j ], "\t" );
+
+ if ( tmpuris == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse URIs #%d"
+ " in \"%s <protocol>://<server>[:port]/<naming context>\"",
+ j-1, c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( j == 1 ) {
+ uris = tmpuris;
+
+ } else {
+ ldap_charray_merge( &uris, tmpuris );
+ ldap_charray_free( tmpuris );
+ }
+ }
+
+ for ( j = 0; uris[ j ] != NULL; j++ ) {
+ char *tmpuri = NULL;
+
+ /*
+ * uri MUST be legal!
+ */
+ if ( ldap_url_parselist_ext( &ludp, uris[ j ], "\t",
+ LDAP_PVT_URL_PARSE_NONE ) != LDAP_SUCCESS
+ || ludp->lud_next != NULL )
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse URI #%d"
+ " in \"%s <protocol>://<server>[:port]/<naming context>\"",
+ j-1, c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ ldap_charray_free( uris );
+ return 1;
+ }
+
+ if ( j == 0 ) {
+
+ /*
+ * uri MUST have the <dn> part!
+ */
+ if ( ludp->lud_dn == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "missing <naming context> "
+ " in \"%s <protocol>://<server>[:port]/<naming context>\"",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ ldap_free_urllist( ludp );
+ ldap_charray_free( uris );
+ return 1;
+ }
+
+ /*
+ * copies and stores uri and suffix
+ */
+ ber_str2bv( ludp->lud_dn, 0, 0, &dn );
+ rc = dnPrettyNormal( NULL, &dn, &mt->mt_psuffix,
+ &mt->mt_nsuffix, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "target DN is invalid \"%s\"",
+ c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ ldap_free_urllist( ludp );
+ ldap_charray_free( uris );
+ return( 1 );
+ }
+
+ ludp->lud_dn[ 0 ] = '\0';
+
+ switch ( ludp->lud_scope ) {
+ case LDAP_SCOPE_DEFAULT:
+ mt->mt_scope = LDAP_SCOPE_SUBTREE;
+ break;
+
+ case LDAP_SCOPE_SUBTREE:
+ case LDAP_SCOPE_SUBORDINATE:
+ mt->mt_scope = ludp->lud_scope;
+ break;
+
+ default:
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "invalid scope for target \"%s\"",
+ c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ ldap_free_urllist( ludp );
+ ldap_charray_free( uris );
+ return( 1 );
+ }
+
+ } else {
+ /* check all, to apply the scope check on the first one */
+ if ( ludp->lud_dn != NULL && ludp->lud_dn[ 0 ] != '\0' ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "multiple URIs must have no DN part" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ ldap_free_urllist( ludp );
+ ldap_charray_free( uris );
+ return( 1 );
+
+ }
+ }
+
+ tmpuri = ldap_url_list2urls( ludp );
+ ldap_free_urllist( ludp );
+ if ( tmpuri == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "no memory?" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ ldap_charray_free( uris );
+ return( 1 );
+ }
+ ldap_memfree( uris[ j ] );
+ uris[ j ] = tmpuri;
+ }
+
+ mt->mt_uri = ldap_charray2str( uris, " " );
+ ldap_charray_free( uris );
+ if ( mt->mt_uri == NULL) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "no memory?" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+
+ /*
+ * uri MUST be a branch of suffix!
+ */
+ for ( j = 0; !BER_BVISNULL( &c->be->be_nsuffix[ j ] ); j++ ) {
+ if ( dnIsSuffix( &mt->mt_nsuffix, &c->be->be_nsuffix[ j ] ) ) {
+ break;
+ }
+ }
+
+ if ( BER_BVISNULL( &c->be->be_nsuffix[ j ] ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "<naming context> of URI must be within the naming context of this database." );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ c->ca_private = mt;
+ config_push_cleanup( c, meta_cf_cleanup );
+ } break;
+ case LDAP_BACK_CFG_SUBTREE_EX:
+ case LDAP_BACK_CFG_SUBTREE_IN:
+ /* subtree-exclude */
+ if ( meta_subtree_config( mt, c )) {
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ break;
+
+ case LDAP_BACK_CFG_FILTER: {
+ metafilter_t *mf, **m2;
+ mf = ch_calloc( 1, sizeof( metafilter_t ));
+ rc = regcomp( &mf->mf_regex, c->argv[1], REG_EXTENDED );
+ if ( rc ) {
+ char regerr[ SLAP_TEXT_BUFLEN ];
+ regerror( rc, &mf->mf_regex, regerr, sizeof(regerr) );
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "regular expression \"%s\" bad because of %s",
+ c->argv[1], regerr );
+ ch_free( mf );
+ return 1;
+ }
+ ber_str2bv( c->argv[1], 0, 1, &mf->mf_regex_pattern );
+ for ( m2 = &mt->mt_filter; *m2; m2 = &(*m2)->mf_next )
+ ;
+ *m2 = mf;
+ } break;
+
+ case LDAP_BACK_CFG_DEFAULT_T:
+ /* default target directive */
+ i = mi->mi_ntargets - 1;
+
+ if ( c->argc == 1 ) {
+ if ( i < 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s\" alone must be inside a \"uri\" directive",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ mi->mi_defaulttarget = i;
+
+ } else {
+ if ( strcasecmp( c->argv[ 1 ], "none" ) == 0 ) {
+ if ( i >= 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s none\" should go before uri definitions",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ }
+ mi->mi_defaulttarget = META_DEFAULT_TARGET_NONE;
+
+ } else {
+
+ if ( lutil_atoi( &mi->mi_defaulttarget, c->argv[ 1 ] ) != 0
+ || mi->mi_defaulttarget < 0
+ || mi->mi_defaulttarget >= i - 1 )
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "illegal target number %d",
+ mi->mi_defaulttarget );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ }
+ }
+ break;
+
+ case LDAP_BACK_CFG_DNCACHE_TTL:
+ /* ttl of dn cache */
+ if ( strcasecmp( c->argv[ 1 ], "forever" ) == 0 ) {
+ mi->mi_cache.ttl = META_DNCACHE_FOREVER;
+
+ } else if ( strcasecmp( c->argv[ 1 ], "disabled" ) == 0 ) {
+ mi->mi_cache.ttl = META_DNCACHE_DISABLED;
+
+ } else {
+ unsigned long t;
+
+ if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse dncache ttl \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ mi->mi_cache.ttl = (time_t)t;
+ }
+ break;
+
+ case LDAP_BACK_CFG_NETWORK_TIMEOUT: {
+ /* network timeout when connecting to ldap servers */
+ unsigned long t;
+
+ if ( lutil_parse_time( c->argv[ 1 ], &t ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse network timeout \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ mc->mc_network_timeout = (time_t)t;
+ } break;
+
+ case LDAP_BACK_CFG_IDLE_TIMEOUT: {
+ /* idle timeout when connecting to ldap servers */
+ unsigned long t;
+
+ if ( lutil_parse_time( c->argv[ 1 ], &t ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse idle timeout \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+
+ }
+ mi->mi_idle_timeout = (time_t)t;
+ } break;
+
+ case LDAP_BACK_CFG_CONN_TTL: {
+ /* conn ttl */
+ unsigned long t;
+
+ if ( lutil_parse_time( c->argv[ 1 ], &t ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse conn ttl \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+
+ }
+ mi->mi_conn_ttl = (time_t)t;
+ } break;
+
+ case LDAP_BACK_CFG_BIND_TIMEOUT:
+ /* bind timeout when connecting to ldap servers */
+ mc->mc_bind_timeout.tv_sec = c->value_ulong/1000000;
+ mc->mc_bind_timeout.tv_usec = c->value_ulong%1000000;
+ break;
+
+ case LDAP_BACK_CFG_REBIND:
+ /* save bind creds for referral rebinds? */
+ if ( c->argc == 1 || c->value_int ) {
+ mc->mc_flags |= LDAP_BACK_F_SAVECRED;
+ } else {
+ mc->mc_flags &= ~LDAP_BACK_F_SAVECRED;
+ }
+ break;
+
+ case LDAP_BACK_CFG_CHASE:
+ if ( c->argc == 1 || c->value_int ) {
+ mc->mc_flags |= LDAP_BACK_F_CHASE_REFERRALS;
+ } else {
+ mc->mc_flags &= ~LDAP_BACK_F_CHASE_REFERRALS;
+ }
+ break;
+
+ case LDAP_BACK_CFG_TLS:
+ i = verb_to_mask( c->argv[1], tls_mode );
+ if ( BER_BVISNULL( &tls_mode[i].word ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s unknown argument \"%s\"",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ mc->mc_flags &= ~LDAP_BACK_F_TLS_MASK;
+ mc->mc_flags |= tls_mode[i].mask;
+
+ if ( c->argc > 2 ) {
+ if ( c->op == SLAP_CONFIG_ADD && mi->mi_ntargets == 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "need \"uri\" directive first" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ for ( i = 2; i < c->argc; i++ ) {
+ if ( bindconf_tls_parse( c->argv[i], &mt->mt_tls ))
+ return 1;
+ }
+ bindconf_tls_defaults( &mt->mt_tls );
+ }
+ break;
+
+ case LDAP_BACK_CFG_T_F:
+ i = verb_to_mask( c->argv[1], t_f_mode );
+ if ( BER_BVISNULL( &t_f_mode[i].word ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s unknown argument \"%s\"",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ mc->mc_flags &= ~LDAP_BACK_F_T_F_MASK2;
+ mc->mc_flags |= t_f_mode[i].mask;
+ break;
+
+ case LDAP_BACK_CFG_ONERR:
+ /* onerr? */
+ i = verb_to_mask( c->argv[1], onerr_mode );
+ if ( BER_BVISNULL( &onerr_mode[i].word ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s unknown argument \"%s\"",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ mi->mi_flags &= ~META_BACK_F_ONERR_MASK;
+ mi->mi_flags |= onerr_mode[i].mask;
+ break;
+
+ case LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER:
+ /* bind-defer? */
+ if ( c->argc == 1 || c->value_int ) {
+ mi->mi_flags |= META_BACK_F_DEFER_ROOTDN_BIND;
+ } else {
+ mi->mi_flags &= ~META_BACK_F_DEFER_ROOTDN_BIND;
+ }
+ break;
+
+ case LDAP_BACK_CFG_SINGLECONN:
+ /* single-conn? */
+ if ( mi->mi_ntargets > 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s\" must appear before target definitions",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+ if ( c->value_int ) {
+ mi->mi_flags |= LDAP_BACK_F_SINGLECONN;
+ } else {
+ mi->mi_flags &= ~LDAP_BACK_F_SINGLECONN;
+ }
+ break;
+
+ case LDAP_BACK_CFG_USETEMP:
+ /* use-temporaries? */
+ if ( mi->mi_ntargets > 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s\" must appear before target definitions",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+ if ( c->value_int ) {
+ mi->mi_flags |= LDAP_BACK_F_USE_TEMPORARIES;
+ } else {
+ mi->mi_flags &= ~LDAP_BACK_F_USE_TEMPORARIES;
+ }
+ break;
+
+ case LDAP_BACK_CFG_CONNPOOLMAX:
+ /* privileged connections pool max size ? */
+ if ( mi->mi_ntargets > 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"%s\" must appear before target definitions",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+
+ if ( c->value_int < LDAP_BACK_CONN_PRIV_MIN
+ || c->value_int > LDAP_BACK_CONN_PRIV_MAX )
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "invalid max size " "of privileged "
+ "connections pool \"%s\" "
+ "in \"conn-pool-max <n> "
+ "(must be between %d and %d)\"",
+ c->argv[ 1 ],
+ LDAP_BACK_CONN_PRIV_MIN,
+ LDAP_BACK_CONN_PRIV_MAX );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ mi->mi_conn_priv_max = c->value_int;
+ break;
+
+ case LDAP_BACK_CFG_CANCEL:
+ i = verb_to_mask( c->argv[1], cancel_mode );
+ if ( BER_BVISNULL( &cancel_mode[i].word ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s unknown argument \"%s\"",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ mc->mc_flags &= ~LDAP_BACK_F_CANCEL_MASK2;
+ mc->mc_flags |= cancel_mode[i].mask;
+ break;
+
+ case LDAP_BACK_CFG_TIMEOUT:
+ for ( i = 1; i < c->argc; i++ ) {
+ if ( isdigit( (unsigned char) c->argv[ i ][ 0 ] ) ) {
+ int j;
+ unsigned u;
+
+ if ( lutil_atoux( &u, c->argv[ i ], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "unable to parse timeout \"%s\"",
+ c->argv[ i ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ for ( j = 0; j < SLAP_OP_LAST; j++ ) {
+ mc->mc_timeout[ j ] = u;
+ }
+
+ continue;
+ }
+
+ if ( slap_cf_aux_table_parse( c->argv[ i ], mc->mc_timeout, timeout_table, "slapd-meta timeout" ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "unable to parse timeout \"%s\"",
+ c->argv[ i ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ }
+ break;
+
+ case LDAP_BACK_CFG_PSEUDOROOTDN:
+ /* name to use as pseudo-root dn */
+ /*
+ * exact replacement:
+ *
+
+idassert-bind bindmethod=simple
+ binddn=<pseudorootdn>
+ credentials=<pseudorootpw>
+ mode=none
+ flags=non-prescriptive
+idassert-authzFrom "dn:<rootdn>"
+
+ * so that only when authc'd as <rootdn> the proxying occurs
+ * rebinding as the <pseudorootdn> without proxyAuthz.
+ */
+
+ Debug( LDAP_DEBUG_ANY,
+ "%s: \"pseudorootdn\", \"pseudorootpw\" are no longer supported; "
+ "use \"idassert-bind\" and \"idassert-authzFrom\" instead.\n",
+ c->log );
+
+ {
+ char binddn[ SLAP_TEXT_BUFLEN ];
+ char *cargv[] = {
+ "idassert-bind",
+ "bindmethod=simple",
+ NULL,
+ "mode=none",
+ "flags=non-prescriptive",
+ NULL
+ };
+ char **oargv;
+ int oargc;
+ int cargc = 5;
+ int rc;
+
+
+ if ( BER_BVISNULL( &c->be->be_rootndn ) ) {
+ Debug( LDAP_DEBUG_ANY, "%s: \"pseudorootpw\": \"rootdn\" must be defined first.\n",
+ c->log );
+ return 1;
+ }
+
+ if ( sizeof( binddn ) <= (unsigned) snprintf( binddn,
+ sizeof( binddn ), "binddn=%s", c->argv[ 1 ] ))
+ {
+ Debug( LDAP_DEBUG_ANY, "%s: \"pseudorootdn\" too long.\n",
+ c->log );
+ return 1;
+ }
+ cargv[ 2 ] = binddn;
+
+ oargv = c->argv;
+ oargc = c->argc;
+ c->argv = cargv;
+ c->argc = cargc;
+ rc = mi->mi_ldap_extra->idassert_parse( c, &mt->mt_idassert );
+ c->argv = oargv;
+ c->argc = oargc;
+ if ( rc == 0 ) {
+ struct berval bv;
+
+ if ( mt->mt_idassert_authz != NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: \"idassert-authzFrom\" already defined (discarded).\n",
+ c->log );
+ ber_bvarray_free( mt->mt_idassert_authz );
+ mt->mt_idassert_authz = NULL;
+ }
+
+ assert( !BER_BVISNULL( &mt->mt_idassert_authcDN ) );
+
+ bv.bv_len = STRLENOF( "dn:" ) + c->be->be_rootndn.bv_len;
+ bv.bv_val = ch_malloc( bv.bv_len + 1 );
+ AC_MEMCPY( bv.bv_val, "dn:", STRLENOF( "dn:" ) );
+ AC_MEMCPY( &bv.bv_val[ STRLENOF( "dn:" ) ], c->be->be_rootndn.bv_val, c->be->be_rootndn.bv_len + 1 );
+
+ ber_bvarray_add( &mt->mt_idassert_authz, &bv );
+ }
+
+ return rc;
+ }
+ break;
+
+ case LDAP_BACK_CFG_PSEUDOROOTPW:
+ /* password to use as pseudo-root */
+ Debug( LDAP_DEBUG_ANY,
+ "%s: \"pseudorootdn\", \"pseudorootpw\" are no longer supported; "
+ "use \"idassert-bind\" and \"idassert-authzFrom\" instead.\n",
+ c->log );
+
+ if ( BER_BVISNULL( &mt->mt_idassert_authcDN ) ) {
+ Debug( LDAP_DEBUG_ANY, "%s: \"pseudorootpw\": \"pseudorootdn\" must be defined first.\n",
+ c->log );
+ return 1;
+ }
+
+ if ( !BER_BVISNULL( &mt->mt_idassert_passwd ) ) {
+ memset( mt->mt_idassert_passwd.bv_val, 0,
+ mt->mt_idassert_passwd.bv_len );
+ ber_memfree( mt->mt_idassert_passwd.bv_val );
+ }
+ ber_str2bv( c->argv[ 1 ], 0, 1, &mt->mt_idassert_passwd );
+ break;
+
+ case LDAP_BACK_CFG_IDASSERT_BIND:
+ /* idassert-bind */
+ rc = mi->mi_ldap_extra->idassert_parse( c, &mt->mt_idassert );
+ break;
+
+ case LDAP_BACK_CFG_IDASSERT_AUTHZFROM:
+ /* idassert-authzFrom */
+ rc = mi->mi_ldap_extra->idassert_authzfrom_parse( c, &mt->mt_idassert );
+ break;
+
+ case LDAP_BACK_CFG_QUARANTINE:
+ /* quarantine */
+ if ( META_BACK_CMN_QUARANTINE( mc ) )
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "quarantine already defined" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( mt ) {
+ mc->mc_quarantine.ri_interval = NULL;
+ mc->mc_quarantine.ri_num = NULL;
+ if ( !META_BACK_QUARANTINE( mi ) ) {
+ ldap_pvt_thread_mutex_init( &mt->mt_quarantine_mutex );
+ }
+ }
+
+ if ( mi->mi_ldap_extra->retry_info_parse( c->argv[ 1 ], &mc->mc_quarantine, c->cr_msg, sizeof( c->cr_msg ) ) ) {
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ mc->mc_flags |= LDAP_BACK_F_QUARANTINE;
+ break;
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ case LDAP_BACK_CFG_ST_REQUEST:
+ /* session tracking request */
+ if ( c->value_int ) {
+ mc->mc_flags |= LDAP_BACK_F_ST_REQUEST;
+ } else {
+ mc->mc_flags &= ~LDAP_BACK_F_ST_REQUEST;
+ }
+ break;
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+ case LDAP_BACK_CFG_SUFFIXM: /* FALLTHRU */
+ case LDAP_BACK_CFG_REWRITE: {
+ /* rewrite stuff ... */
+ ConfigArgs ca = { 0 };
+ char *line, **argv;
+ struct rewrite_info *rwi;
+ int cnt = 0, argc, ix = c->valx;
+
+ if ( mt->mt_rwmap.rwm_bva_rewrite ) {
+ for ( ; !BER_BVISNULL( &mt->mt_rwmap.rwm_bva_rewrite[ cnt ] ); cnt++ )
+ /* count */ ;
+ }
+
+ if ( ix >= cnt || ix < 0 ) {
+ ix = cnt;
+ } else {
+ rwi = mt->mt_rwmap.rwm_rw;
+
+ mt->mt_rwmap.rwm_rw = NULL;
+ rc = meta_rwi_init( &mt->mt_rwmap.rwm_rw );
+
+ /* re-parse all rewrite rules, up to the one
+ * that needs to be added */
+ ca.be = c->be;
+ ca.fname = c->fname;
+ ca.lineno = c->lineno;
+ for ( i = 0; i < ix; i++ ) {
+ ca.line = mt->mt_rwmap.rwm_bva_rewrite[ i ].bv_val;
+ ca.argc = 0;
+ config_fp_parse_line( &ca );
+
+ if ( !strcasecmp( ca.argv[0], "suffixmassage" )) {
+ rc = meta_suffixm_config( &ca, ca.argc, ca.argv, mt );
+ } else {
+ rc = rewrite_parse( mt->mt_rwmap.rwm_rw,
+ c->fname, c->lineno, ca.argc, ca.argv );
+ }
+ assert( rc == 0 );
+ ch_free( ca.tline );
+ }
+ }
+ argc = c->argc;
+ argv = c->argv;
+ if ( c->op != SLAP_CONFIG_ADD ) {
+ argc--;
+ argv++;
+ }
+ /* add the new rule */
+ if ( !strcasecmp( argv[0], "suffixmassage" )) {
+ rc = meta_suffixm_config( c, argc, argv, mt );
+ } else {
+ rc = rewrite_parse( mt->mt_rwmap.rwm_rw,
+ c->fname, c->lineno, argc, argv );
+ }
+ if ( rc ) {
+ if ( ix < cnt ) {
+ rewrite_info_delete( &mt->mt_rwmap.rwm_rw );
+ mt->mt_rwmap.rwm_rw = rwi;
+ }
+ return 1;
+ }
+ if ( ix < cnt ) {
+ for ( ; i < cnt; i++ ) {
+ ca.line = mt->mt_rwmap.rwm_bva_rewrite[ i ].bv_val;
+ ca.argc = 0;
+ config_fp_parse_line( &ca );
+
+ if ( !strcasecmp( ca.argv[0], "suffixmassage" )) {
+ rc = meta_suffixm_config( &ca, ca.argc, ca.argv, mt );
+ } else {
+ rc = rewrite_parse( mt->mt_rwmap.rwm_rw,
+ c->fname, c->lineno, ca.argc, argv );
+ }
+ assert( rc == 0 );
+ ch_free( ca.tline );
+ }
+ ch_free( ca.argv );
+ }
+
+ /* save the rule info */
+ line = ldap_charray2str( argv, "\" \"" );
+ if ( line != NULL ) {
+ struct berval bv;
+ int len = strlen( argv[ 0 ] );
+
+ ber_str2bv( line, 0, 0, &bv );
+ AC_MEMCPY( &bv.bv_val[ len ], &bv.bv_val[ len + 1 ],
+ bv.bv_len - ( len + 1 ));
+ bv.bv_val[ bv.bv_len - 1] = '"';
+ ber_bvarray_add( &mt->mt_rwmap.rwm_bva_rewrite, &bv );
+ /* move it to the right slot */
+ if ( ix < cnt ) {
+ for ( i=cnt; i>ix; i-- )
+ mt->mt_rwmap.rwm_bva_rewrite[i] = mt->mt_rwmap.rwm_bva_rewrite[i-1];
+ mt->mt_rwmap.rwm_bva_rewrite[i] = bv;
+
+ /* destroy old rules */
+ rewrite_info_delete( &rwi );
+ }
+ }
+ } break;
+
+ case LDAP_BACK_CFG_MAP: {
+ /* objectclass/attribute mapping */
+ ConfigArgs ca = { 0 };
+ char *argv[5], **argvp;
+ struct ldapmap rwm_oc;
+ struct ldapmap rwm_at;
+ int cnt = 0, ix = c->valx;
+
+ if ( mt->mt_rwmap.rwm_bva_map ) {
+ for ( ; !BER_BVISNULL( &mt->mt_rwmap.rwm_bva_map[ cnt ] ); cnt++ )
+ /* count */ ;
+ }
+
+ if ( ix >= cnt || ix < 0 ) {
+ ix = cnt;
+ } else {
+ rwm_oc = mt->mt_rwmap.rwm_oc;
+ rwm_at = mt->mt_rwmap.rwm_at;
+
+ memset( &mt->mt_rwmap.rwm_oc, 0, sizeof( mt->mt_rwmap.rwm_oc ) );
+ memset( &mt->mt_rwmap.rwm_at, 0, sizeof( mt->mt_rwmap.rwm_at ) );
+
+ /* re-parse all mappings, up to the one
+ * that needs to be added */
+ argv[0] = c->argv[0];
+ ca.fname = c->fname;
+ ca.lineno = c->lineno;
+ for ( i = 0; i < ix; i++ ) {
+ ca.line = mt->mt_rwmap.rwm_bva_map[ i ].bv_val;
+ ca.argc = 0;
+ config_fp_parse_line( &ca );
+
+ argv[1] = ca.argv[0];
+ argv[2] = ca.argv[1];
+ argv[3] = ca.argv[2];
+ argv[4] = ca.argv[3];
+
+ argvp = ca.argv;
+ ca.argv = argv;
+ ca.argc++;
+ rc = ldap_back_map_config( &ca, &mt->mt_rwmap.rwm_oc,
+ &mt->mt_rwmap.rwm_at );
+
+ ch_free( ca.tline );
+ ca.tline = NULL;
+ ca.argv = argvp;
+
+ /* in case of failure, restore
+ * the existing mapping */
+ if ( rc ) {
+ goto map_fail;
+ }
+ }
+ }
+ /* add the new mapping */
+ rc = ldap_back_map_config( c, &mt->mt_rwmap.rwm_oc,
+ &mt->mt_rwmap.rwm_at );
+ if ( rc ) {
+ goto map_fail;
+ }
+
+ if ( ix < cnt ) {
+ for ( ; i<cnt ; i++ ) {
+ ca.line = mt->mt_rwmap.rwm_bva_map[ i ].bv_val;
+ ca.argc = 0;
+ config_fp_parse_line( &ca );
+
+ argv[1] = ca.argv[0];
+ argv[2] = ca.argv[1];
+ argv[3] = ca.argv[2];
+ argv[4] = ca.argv[3];
+
+ argvp = ca.argv;
+ ca.argv = argv;
+ ca.argc++;
+ rc = ldap_back_map_config( &ca, &mt->mt_rwmap.rwm_oc,
+ &mt->mt_rwmap.rwm_at );
+
+ ch_free( ca.tline );
+ ca.tline = NULL;
+ ca.argv = argvp;
+
+ /* in case of failure, restore
+ * the existing mapping */
+ if ( rc ) {
+ goto map_fail;
+ }
+ }
+ ch_free( ca.argv );
+ }
+
+ /* save the map info */
+ argv[0] = ldap_charray2str( &c->argv[ 1 ], " " );
+ if ( argv[0] != NULL ) {
+ struct berval bv;
+ ber_str2bv( argv[0], 0, 0, &bv );
+ ber_bvarray_add( &mt->mt_rwmap.rwm_bva_map, &bv );
+ /* move it to the right slot */
+ if ( ix < cnt ) {
+ for ( i=cnt; i>ix; i-- )
+ mt->mt_rwmap.rwm_bva_map[i] = mt->mt_rwmap.rwm_bva_map[i-1];
+ mt->mt_rwmap.rwm_bva_map[i] = bv;
+
+ /* destroy old mapping */
+ meta_back_map_free( &rwm_oc );
+ meta_back_map_free( &rwm_at );
+ }
+ }
+ break;
+
+map_fail:;
+ if ( ix < cnt ) {
+ meta_back_map_free( &mt->mt_rwmap.rwm_oc );
+ meta_back_map_free( &mt->mt_rwmap.rwm_at );
+ mt->mt_rwmap.rwm_oc = rwm_oc;
+ mt->mt_rwmap.rwm_at = rwm_at;
+ ch_free( ca.argv );
+ }
+ } break;
+
+ case LDAP_BACK_CFG_NRETRIES: {
+ int nretries = META_RETRY_UNDEFINED;
+
+ if ( strcasecmp( c->argv[ 1 ], "forever" ) == 0 ) {
+ nretries = META_RETRY_FOREVER;
+
+ } else if ( strcasecmp( c->argv[ 1 ], "never" ) == 0 ) {
+ nretries = META_RETRY_NEVER;
+
+ } else {
+ if ( lutil_atoi( &nretries, c->argv[ 1 ] ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse nretries {never|forever|<retries>}: \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ }
+
+ mc->mc_nretries = nretries;
+ } break;
+
+ case LDAP_BACK_CFG_VERSION:
+ if ( c->value_int != 0 && ( c->value_int < LDAP_VERSION_MIN || c->value_int > LDAP_VERSION_MAX ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unsupported protocol version \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ mc->mc_version = c->value_int;
+ break;
+
+ case LDAP_BACK_CFG_NOREFS:
+ /* do not return search references */
+ if ( c->value_int ) {
+ mc->mc_flags |= LDAP_BACK_F_NOREFS;
+ } else {
+ mc->mc_flags &= ~LDAP_BACK_F_NOREFS;
+ }
+ break;
+
+ case LDAP_BACK_CFG_NOUNDEFFILTER:
+ /* do not propagate undefined search filters */
+ if ( c->value_int ) {
+ mc->mc_flags |= LDAP_BACK_F_NOUNDEFFILTER;
+ } else {
+ mc->mc_flags &= ~LDAP_BACK_F_NOUNDEFFILTER;
+ }
+ break;
+
+#ifdef SLAPD_META_CLIENT_PR
+ case LDAP_BACK_CFG_CLIENT_PR:
+ if ( strcasecmp( c->argv[ 1 ], "accept-unsolicited" ) == 0 ) {
+ mc->mc_ps = META_CLIENT_PR_ACCEPT_UNSOLICITED;
+
+ } else if ( strcasecmp( c->argv[ 1 ], "disable" ) == 0 ) {
+ mc->mc_ps = META_CLIENT_PR_DISABLE;
+
+ } else if ( lutil_atoi( &mc->mc_ps, c->argv[ 1 ] ) || mc->mc_ps < -1 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse client-pr {accept-unsolicited|disable|<size>}: \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+ break;
+#endif /* SLAPD_META_CLIENT_PR */
+
+ case LDAP_BACK_CFG_KEEPALIVE: {
+ struct berval bv;
+ ber_str2bv( c->argv[ 1 ], 0, 1, &bv );
+ slap_keepalive_parse( &bv, &mt->mt_tls.sb_keepalive, 0, 0, 0 );
+ }
+ break;
+
+ case LDAP_BACK_CFG_TCP_USER_TIMEOUT:
+ mt->mt_tls.sb_tcp_user_timeout = c->value_uint;
+ break;
+
+ /* anything else */
+ default:
+ return SLAP_CONF_UNKNOWN;
+ }
+
+ return rc;
+}
+
+int
+meta_back_init_cf( BackendInfo *bi )
+{
+ int rc;
+
+ /* Make sure we don't exceed the bits reserved for userland */
+ config_check_userland( LDAP_BACK_CFG_LAST );
+
+ bi->bi_cf_ocs = metaocs;
+
+ rc = config_register_schema( metacfg, metaocs );
+ if ( rc ) {
+ return rc;
+ }
+
+ return 0;
+}
+
+static int
+ldap_back_map_config(
+ ConfigArgs *c,
+ struct ldapmap *oc_map,
+ struct ldapmap *at_map )
+{
+ struct ldapmap *map;
+ struct ldapmapping *mapping;
+ char *src, *dst;
+ int is_oc = 0;
+
+ if ( strcasecmp( c->argv[ 1 ], "objectclass" ) == 0 ) {
+ map = oc_map;
+ is_oc = 1;
+
+ } else if ( strcasecmp( c->argv[ 1 ], "attribute" ) == 0 ) {
+ map = at_map;
+
+ } else {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "%s unknown argument \"%s\"",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( !is_oc && map->map == NULL ) {
+ /* only init if required */
+ ldap_back_map_init( map, &mapping );
+ }
+
+ if ( strcmp( c->argv[ 2 ], "*" ) == 0 ) {
+ if ( c->argc < 4 || strcmp( c->argv[ 3 ], "*" ) == 0 ) {
+ map->drop_missing = ( c->argc < 4 );
+ goto success_return;
+ }
+ src = dst = c->argv[ 3 ];
+
+ } else if ( c->argc < 4 ) {
+ src = "";
+ dst = c->argv[ 2 ];
+
+ } else {
+ src = c->argv[ 2 ];
+ dst = ( strcmp( c->argv[ 3 ], "*" ) == 0 ? src : c->argv[ 3 ] );
+ }
+
+ if ( ( map == at_map )
+ && ( strcasecmp( src, "objectclass" ) == 0
+ || strcasecmp( dst, "objectclass" ) == 0 ) )
+ {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "objectclass attribute cannot be mapped" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ mapping = (struct ldapmapping *)ch_calloc( 2,
+ sizeof(struct ldapmapping) );
+ if ( mapping == NULL ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "out of memory" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ ber_str2bv( src, 0, 1, &mapping[ 0 ].src );
+ ber_str2bv( dst, 0, 1, &mapping[ 0 ].dst );
+ mapping[ 1 ].src = mapping[ 0 ].dst;
+ mapping[ 1 ].dst = mapping[ 0 ].src;
+
+ /*
+ * schema check
+ */
+ if ( is_oc ) {
+ if ( src[ 0 ] != '\0' ) {
+ if ( oc_bvfind( &mapping[ 0 ].src ) == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: warning, source objectClass '%s' should be defined in schema\n",
+ c->log, src );
+
+ /*
+ * FIXME: this should become an err
+ */
+ goto error_return;
+ }
+ }
+
+ if ( oc_bvfind( &mapping[ 0 ].dst ) == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: warning, destination objectClass '%s' is not defined in schema\n",
+ c->log, dst );
+ }
+ } else {
+ int rc;
+ const char *text = NULL;
+ AttributeDescription *ad = NULL;
+
+ if ( src[ 0 ] != '\0' ) {
+ rc = slap_bv2ad( &mapping[ 0 ].src, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: warning, source attributeType '%s' should be defined in schema\n",
+ c->log, src );
+
+ /*
+ * FIXME: this should become an err
+ */
+ /*
+ * we create a fake "proxied" ad
+ * and add it here.
+ */
+
+ rc = slap_bv2undef_ad( &mapping[ 0 ].src,
+ &ad, &text, SLAP_AD_PROXIED );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "source attributeType \"%s\": %d (%s)",
+ src, rc, text ? text : "" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ goto error_return;
+ }
+ }
+
+ ad = NULL;
+ }
+
+ rc = slap_bv2ad( &mapping[ 0 ].dst, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: warning, destination attributeType '%s' is not defined in schema\n",
+ c->log, dst );
+
+ /*
+ * we create a fake "proxied" ad
+ * and add it here.
+ */
+
+ rc = slap_bv2undef_ad( &mapping[ 0 ].dst,
+ &ad, &text, SLAP_AD_PROXIED );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "destination attributeType \"%s\": %d (%s)\n",
+ dst, rc, text ? text : "" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ }
+ }
+
+ if ( (src[ 0 ] != '\0' && ldap_avl_find( map->map, (caddr_t)&mapping[ 0 ], mapping_cmp ) != NULL)
+ || ldap_avl_find( map->remap, (caddr_t)&mapping[ 1 ], mapping_cmp ) != NULL)
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "duplicate mapping found." );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ goto error_return;
+ }
+
+ if ( src[ 0 ] != '\0' ) {
+ ldap_avl_insert( &map->map, (caddr_t)&mapping[ 0 ],
+ mapping_cmp, mapping_dup );
+ }
+ ldap_avl_insert( &map->remap, (caddr_t)&mapping[ 1 ],
+ mapping_cmp, mapping_dup );
+
+success_return:;
+ return 0;
+
+error_return:;
+ if ( mapping ) {
+ ch_free( mapping[ 0 ].src.bv_val );
+ ch_free( mapping[ 0 ].dst.bv_val );
+ ch_free( mapping );
+ }
+
+ return 1;
+}
+
+
+static char *
+suffix_massage_regexize( const char *s )
+{
+ char *res, *ptr;
+ const char *p, *r;
+ int i;
+
+ if ( s[ 0 ] == '\0' ) {
+ return ch_strdup( "^(.+)$" );
+ }
+
+ for ( i = 0, p = s;
+ ( r = strchr( p, ',' ) ) != NULL;
+ p = r + 1, i++ )
+ ;
+
+ res = ch_calloc( sizeof( char ),
+ strlen( s )
+ + STRLENOF( "((.+),)?" )
+ + STRLENOF( "[ ]?" ) * i
+ + STRLENOF( "$" ) + 1 );
+
+ ptr = lutil_strcopy( res, "((.+),)?" );
+ for ( i = 0, p = s;
+ ( r = strchr( p, ',' ) ) != NULL;
+ p = r + 1 , i++ ) {
+ ptr = lutil_strncopy( ptr, p, r - p + 1 );
+ ptr = lutil_strcopy( ptr, "[ ]?" );
+
+ if ( r[ 1 ] == ' ' ) {
+ r++;
+ }
+ }
+ ptr = lutil_strcopy( ptr, p );
+ ptr[ 0 ] = '$';
+ ptr++;
+ ptr[ 0 ] = '\0';
+
+ return res;
+}
+
+static char *
+suffix_massage_patternize( const char *s, const char *p )
+{
+ ber_len_t len;
+ char *res, *ptr;
+
+ len = strlen( p );
+
+ if ( s[ 0 ] == '\0' ) {
+ len++;
+ }
+
+ res = ch_calloc( sizeof( char ), len + STRLENOF( "%1" ) + 1 );
+ if ( res == NULL ) {
+ return NULL;
+ }
+
+ ptr = lutil_strcopy( res, ( p[ 0 ] == '\0' ? "%2" : "%1" ) );
+ if ( s[ 0 ] == '\0' ) {
+ ptr[ 0 ] = ',';
+ ptr++;
+ }
+ lutil_strcopy( ptr, p );
+
+ return res;
+}
+
+int
+suffix_massage_config(
+ struct rewrite_info *info,
+ struct berval *pvnc,
+ struct berval *nvnc,
+ struct berval *prnc,
+ struct berval *nrnc
+)
+{
+ char *rargv[ 5 ];
+ int line = 0;
+
+ rargv[ 0 ] = "rewriteEngine";
+ rargv[ 1 ] = "on";
+ rargv[ 2 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
+
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "default";
+ rargv[ 2 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
+
+ rargv[ 0 ] = "rewriteRule";
+ rargv[ 1 ] = suffix_massage_regexize( pvnc->bv_val );
+ rargv[ 2 ] = suffix_massage_patternize( pvnc->bv_val, prnc->bv_val );
+ rargv[ 3 ] = ":";
+ rargv[ 4 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
+ ch_free( rargv[ 1 ] );
+ ch_free( rargv[ 2 ] );
+
+ if ( BER_BVISEMPTY( pvnc ) ) {
+ rargv[ 0 ] = "rewriteRule";
+ rargv[ 1 ] = "^$";
+ rargv[ 2 ] = prnc->bv_val;
+ rargv[ 3 ] = ":";
+ rargv[ 4 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
+ }
+
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "searchEntryDN";
+ rargv[ 2 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
+
+ rargv[ 0 ] = "rewriteRule";
+ rargv[ 1 ] = suffix_massage_regexize( prnc->bv_val );
+ rargv[ 2 ] = suffix_massage_patternize( prnc->bv_val, pvnc->bv_val );
+ rargv[ 3 ] = ":";
+ rargv[ 4 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
+ ch_free( rargv[ 1 ] );
+ ch_free( rargv[ 2 ] );
+
+ if ( BER_BVISEMPTY( prnc ) ) {
+ rargv[ 0 ] = "rewriteRule";
+ rargv[ 1 ] = "^$";
+ rargv[ 2 ] = pvnc->bv_val;
+ rargv[ 3 ] = ":";
+ rargv[ 4 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
+ }
+
+ /* backward compatibility */
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "searchResult";
+ rargv[ 2 ] = "alias";
+ rargv[ 3 ] = "searchEntryDN";
+ rargv[ 4 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
+
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "matchedDN";
+ rargv[ 2 ] = "alias";
+ rargv[ 3 ] = "searchEntryDN";
+ rargv[ 4 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
+
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "searchAttrDN";
+ rargv[ 2 ] = "alias";
+ rargv[ 3 ] = "searchEntryDN";
+ rargv[ 4 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
+
+ /* NOTE: this corresponds to #undef'ining RWM_REFERRAL_REWRITE;
+ * see servers/slapd/overlays/rwm.h for details */
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "referralAttrDN";
+ rargv[ 2 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
+
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "referralDN";
+ rargv[ 2 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
+
+ return 0;
+}
diff --git a/servers/slapd/back-meta/conn.c b/servers/slapd/back-meta/conn.c
new file mode 100644
index 0000000..91fdad3
--- /dev/null
+++ b/servers/slapd/back-meta/conn.c
@@ -0,0 +1,1893 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+
+
+#define AVL_INTERNAL
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+/*
+ * meta_back_conndn_cmp
+ *
+ * compares two struct metaconn based on the value of the conn pointer
+ * and of the local DN; used by avl stuff
+ */
+int
+meta_back_conndn_cmp(
+ const void *c1,
+ const void *c2 )
+{
+ metaconn_t *mc1 = ( metaconn_t * )c1;
+ metaconn_t *mc2 = ( metaconn_t * )c2;
+ int rc;
+
+ /* If local DNs don't match, it is definitely not a match */
+ /* For shared sessions, conn is NULL. Only explicitly
+ * bound sessions will have non-NULL conn.
+ */
+ rc = SLAP_PTRCMP( mc1->mc_conn, mc2->mc_conn );
+ if ( rc == 0 ) {
+ rc = ber_bvcmp( &mc1->mc_local_ndn, &mc2->mc_local_ndn );
+ }
+
+ return rc;
+}
+
+/*
+ * meta_back_conndnmc_cmp
+ *
+ * compares two struct metaconn based on the value of the conn pointer,
+ * the local DN and the struct pointer; used by avl stuff
+ */
+static int
+meta_back_conndnmc_cmp(
+ const void *c1,
+ const void *c2 )
+{
+ metaconn_t *mc1 = ( metaconn_t * )c1;
+ metaconn_t *mc2 = ( metaconn_t * )c2;
+ int rc;
+
+ /* If local DNs don't match, it is definitely not a match */
+ /* For shared sessions, conn is NULL. Only explicitly
+ * bound sessions will have non-NULL conn.
+ */
+ rc = SLAP_PTRCMP( mc1->mc_conn, mc2->mc_conn );
+ if ( rc == 0 ) {
+ rc = ber_bvcmp( &mc1->mc_local_ndn, &mc2->mc_local_ndn );
+ if ( rc == 0 ) {
+ rc = SLAP_PTRCMP( mc1, mc2 );
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * meta_back_conn_cmp
+ *
+ * compares two struct metaconn based on the value of the conn pointer;
+ * used by avl stuff
+ */
+int
+meta_back_conn_cmp(
+ const void *c1,
+ const void *c2 )
+{
+ metaconn_t *mc1 = ( metaconn_t * )c1;
+ metaconn_t *mc2 = ( metaconn_t * )c2;
+
+ /* For shared sessions, conn is NULL. Only explicitly
+ * bound sessions will have non-NULL conn.
+ */
+ return SLAP_PTRCMP( mc1->mc_conn, mc2->mc_conn );
+}
+
+/*
+ * meta_back_conndn_dup
+ *
+ * returns -1 in case a duplicate struct metaconn has been inserted;
+ * used by avl stuff
+ */
+int
+meta_back_conndn_dup(
+ void *c1,
+ void *c2 )
+{
+ metaconn_t *mc1 = ( metaconn_t * )c1;
+ metaconn_t *mc2 = ( metaconn_t * )c2;
+
+ /* Cannot have more than one shared session with same DN */
+ if ( mc1->mc_conn == mc2->mc_conn &&
+ dn_match( &mc1->mc_local_ndn, &mc2->mc_local_ndn ) )
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Debug stuff (got it from libavl)
+ */
+#if META_BACK_PRINT_CONNTREE > 0
+static void
+meta_back_print( metaconn_t *mc, char *avlstr )
+{
+ int i;
+
+ fputs( "targets=[", stderr );
+ for ( i = 0; i < mc->mc_info->mi_ntargets; i++ ) {
+ fputc( mc->mc_conns[ i ].msc_ld ? '*' : 'o', stderr);
+ }
+ fputc( ']', stderr );
+
+ fprintf( stderr, " mc=%p local=\"%s\" conn=%p refcnt=%d%s %s\n",
+ (void *)mc,
+ mc->mc_local_ndn.bv_val ? mc->mc_local_ndn.bv_val : "",
+ (void *)mc->mc_conn,
+ mc->mc_refcnt,
+ LDAP_BACK_CONN_TAINTED( mc ) ? " tainted" : "",
+ avlstr );
+}
+
+static void
+meta_back_ravl_print( TAvlnode *root, int depth )
+{
+ int i;
+
+ if ( root == 0 ) {
+ return;
+ }
+
+ meta_back_ravl_print( root->avl_right, depth + 1 );
+
+ for ( i = 0; i < depth; i++ ) {
+ fprintf( stderr, "-" );
+ }
+ fputc( ' ', stderr );
+
+ meta_back_print( (metaconn_t *)root->avl_data,
+ avl_bf2str( root->avl_bf ) );
+
+ meta_back_ravl_print( root->avl_left, depth + 1 );
+}
+
+/* NOTE: duplicate from back-ldap/bind.c */
+static char* priv2str[] = {
+ "privileged",
+ "privileged/TLS",
+ "anonymous",
+ "anonymous/TLS",
+ "bind",
+ "bind/TLS",
+ NULL
+};
+
+void
+meta_back_print_conntree( metainfo_t *mi, char *msg )
+{
+ int c;
+
+ fprintf( stderr, "========> %s\n", msg );
+
+ for ( c = LDAP_BACK_PCONN_FIRST; c < LDAP_BACK_PCONN_LAST; c++ ) {
+ int i = 0;
+ metaconn_t *mc;
+
+ fprintf( stderr, " %s[%d]\n", priv2str[ c ], mi->mi_conn_priv[ c ].mic_num );
+
+ LDAP_TAILQ_FOREACH( mc, &mi->mi_conn_priv[ c ].mic_priv, mc_q )
+ {
+ fprintf( stderr, " [%d] ", i );
+ meta_back_print( mc, "" );
+ i++;
+ }
+ }
+
+ if ( mi->mi_conninfo.lai_tree == NULL ) {
+ fprintf( stderr, "\t(empty)\n" );
+
+ } else {
+ meta_back_ravl_print( mi->mi_conninfo.lai_tree, 0 );
+ }
+
+ fprintf( stderr, "<======== %s\n", msg );
+}
+#endif /* META_BACK_PRINT_CONNTREE */
+/*
+ * End of debug stuff
+ */
+
+/*
+ * metaconn_alloc
+ *
+ * Allocates a connection structure, making room for all the referenced targets
+ */
+static metaconn_t *
+metaconn_alloc(
+ Operation *op )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ metaconn_t *mc;
+ int ntargets = mi->mi_ntargets;
+
+ assert( ntargets > 0 );
+
+ /* malloc all in one */
+ mc = ( metaconn_t * )ch_calloc( 1, sizeof( metaconn_t )
+ + sizeof( metasingleconn_t ) * ( ntargets - 1 ) );
+ if ( mc == NULL ) {
+ return NULL;
+ }
+
+ mc->mc_info = mi;
+
+ mc->mc_authz_target = META_BOUND_NONE;
+ mc->mc_refcnt = 1;
+
+ return mc;
+}
+
+/*
+ * meta_back_init_one_conn
+ *
+ * Initializes one connection
+ */
+int
+meta_back_init_one_conn(
+ Operation *op,
+ SlapReply *rs,
+ metaconn_t *mc,
+ int candidate,
+ int ispriv,
+ ldap_back_send_t sendok,
+ int dolock )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ metatarget_t *mt = mi->mi_targets[ candidate ];
+ metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+ int version;
+ dncookie dc;
+ int isauthz = ( candidate == mc->mc_authz_target );
+ int do_return = 0;
+#ifdef HAVE_TLS
+ int is_ldaps = 0;
+ int do_start_tls = 0;
+#endif /* HAVE_TLS */
+
+ /* if the server is quarantined, and
+ * - the current interval did not expire yet, or
+ * - no more retries should occur,
+ * don't return the connection */
+ if ( mt->mt_isquarantined ) {
+ slap_retry_info_t *ri = &mt->mt_quarantine;
+ int dont_retry = 0;
+
+ if ( mt->mt_quarantine.ri_interval ) {
+ ldap_pvt_thread_mutex_lock( &mt->mt_quarantine_mutex );
+ dont_retry = ( mt->mt_isquarantined > LDAP_BACK_FQ_NO );
+ if ( dont_retry ) {
+ dont_retry = ( ri->ri_num[ ri->ri_idx ] == SLAP_RETRYNUM_TAIL
+ || slap_get_time() < ri->ri_last + ri->ri_interval[ ri->ri_idx ] );
+ if ( !dont_retry ) {
+ Debug(LDAP_DEBUG_ANY,
+ "%s meta_back_init_one_conn[%d]: quarantine " "retry block #%d try #%d.\n",
+ op->o_log_prefix,
+ candidate, ri->ri_idx,
+ ri->ri_count );
+
+ mt->mt_isquarantined = LDAP_BACK_FQ_RETRYING;
+ }
+
+ }
+ ldap_pvt_thread_mutex_unlock( &mt->mt_quarantine_mutex );
+ }
+
+ if ( dont_retry ) {
+ rs->sr_err = LDAP_UNAVAILABLE;
+ if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
+ rs->sr_text = "Target is quarantined";
+ send_ldap_result( op, rs );
+ }
+ return rs->sr_err;
+ }
+ }
+
+retry_lock:;
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ }
+
+ /*
+ * Already init'ed
+ */
+ if ( LDAP_BACK_CONN_ISBOUND( msc )
+ || LDAP_BACK_CONN_ISANON( msc ) )
+ {
+ assert( msc->msc_ld != NULL );
+ rs->sr_err = LDAP_SUCCESS;
+ do_return = 1;
+
+ } else if ( META_BACK_CONN_CREATING( msc )
+ || LDAP_BACK_CONN_BINDING( msc ) )
+ {
+ if ( !LDAP_BACK_USE_TEMPORARIES( mi ) ) {
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ }
+
+ ldap_pvt_thread_yield();
+ goto retry_lock;
+ }
+
+ /* sounds more appropriate */
+ rs->sr_err = LDAP_BUSY;
+ rs->sr_text = "No connections to target are available";
+ do_return = 1;
+
+ } else if ( META_BACK_CONN_INITED( msc ) ) {
+ assert( msc->msc_ld != NULL );
+ rs->sr_err = LDAP_SUCCESS;
+ do_return = 1;
+
+ } else {
+ /*
+ * creating...
+ */
+ META_BACK_CONN_CREATING_SET( msc );
+ }
+
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ }
+
+ if ( do_return ) {
+ if ( rs->sr_err != LDAP_SUCCESS
+ && op->o_conn
+ && ( sendok & LDAP_BACK_SENDERR ) )
+ {
+ send_ldap_result( op, rs );
+ }
+
+ return rs->sr_err;
+ }
+
+ assert( msc->msc_ld == NULL );
+
+ /*
+ * Attempts to initialize the connection to the target ds
+ */
+ ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
+ rs->sr_err = ldap_initialize( &msc->msc_ld, mt->mt_uri );
+#ifdef HAVE_TLS
+ is_ldaps = ldap_is_ldaps_url( mt->mt_uri );
+#endif /* HAVE_TLS */
+ ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto error_return;
+ }
+
+ /*
+ * Set LDAP version. This will always succeed: If the client
+ * bound with a particular version, then so can we.
+ */
+ if ( mt->mt_version != 0 ) {
+ version = mt->mt_version;
+
+ } else if ( op->o_conn->c_protocol != 0 ) {
+ version = op->o_conn->c_protocol;
+
+ } else {
+ version = LDAP_VERSION3;
+ }
+ ldap_set_option( msc->msc_ld, LDAP_OPT_PROTOCOL_VERSION, &version );
+ ldap_set_urllist_proc( msc->msc_ld, mt->mt_urllist_f, mt->mt_urllist_p );
+
+ /* automatically chase referrals ("chase-referrals [{yes|no}]" statement) */
+ ldap_set_option( msc->msc_ld, LDAP_OPT_REFERRALS,
+ META_BACK_TGT_CHASE_REFERRALS( mt ) ? LDAP_OPT_ON : LDAP_OPT_OFF );
+
+ slap_client_keepalive(msc->msc_ld, &mt->mt_tls.sb_keepalive);
+
+ if ( mt->mt_tls.sb_tcp_user_timeout > 0 ) {
+ ldap_set_option( msc->msc_ld, LDAP_OPT_TCP_USER_TIMEOUT,
+ &mt->mt_tls.sb_tcp_user_timeout );
+ }
+
+
+
+#ifdef HAVE_TLS
+ {
+ slap_bindconf *sb = NULL;
+
+ if ( ispriv ) {
+ sb = &mt->mt_idassert.si_bc;
+ } else {
+ sb = &mt->mt_tls;
+ }
+
+ bindconf_tls_set( sb, msc->msc_ld );
+
+ if ( !is_ldaps ) {
+ if ( META_BACK_TGT_USE_TLS( mt )
+ || ( op->o_conn->c_is_tls && META_BACK_TGT_PROPAGATE_TLS( mt ) ) )
+ {
+ do_start_tls = 1;
+ }
+ }
+ }
+
+ /* start TLS ("tls [try-]{start|propagate}" statement) */
+ if ( do_start_tls ) {
+#ifdef SLAP_STARTTLS_ASYNCHRONOUS
+ /*
+ * use asynchronous StartTLS; in case, chase referral
+ * FIXME: OpenLDAP does not return referral on StartTLS yet
+ */
+ int msgid;
+
+ rs->sr_err = ldap_start_tls( msc->msc_ld, NULL, NULL, &msgid );
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ LDAPMessage *res = NULL;
+ int rc, nretries = mt->mt_nretries;
+ struct timeval tv;
+
+ LDAP_BACK_TV_SET( &tv );
+
+retry:;
+ rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
+ switch ( rc ) {
+ case -1:
+ rs->sr_err = LDAP_UNAVAILABLE;
+ rs->sr_text = "Remote server down";
+ break;
+
+ case 0:
+ if ( nretries != 0 ) {
+ if ( nretries > 0 ) {
+ nretries--;
+ }
+ LDAP_BACK_TV_SET( &tv );
+ goto retry;
+ }
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "Timeout, no more retries";
+ break;
+
+ default:
+ /* only touch when activity actually took place... */
+ if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
+ msc->msc_time = op->o_time;
+ }
+ break;
+ }
+
+ if ( rc == LDAP_RES_EXTENDED ) {
+ struct berval *data = NULL;
+
+ /* NOTE: right now, data is unused, so don't get it */
+ rs->sr_err = ldap_parse_extended_result( msc->msc_ld,
+ res, NULL, NULL /* &data */ , 0 );
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ int err;
+
+ /* FIXME: matched? referrals? response controls? */
+ rs->sr_err = ldap_parse_result( msc->msc_ld,
+ res, &err, NULL, NULL, NULL, NULL, 1 );
+ res = NULL;
+
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ rs->sr_err = err;
+ }
+ rs->sr_err = slap_map_api2result( rs );
+
+ /* FIXME: in case a referral
+ * is returned, should we try
+ * using it instead of the
+ * configured URI? */
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ rs->sr_err = ldap_install_tls( msc->msc_ld );
+
+ } else if ( rs->sr_err == LDAP_REFERRAL ) {
+ /* FIXME: LDAP_OPERATIONS_ERROR? */
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "Unwilling to chase referral "
+ "returned by Start TLS exop";
+ }
+
+ if ( data ) {
+ ber_bvfree( data );
+ }
+ }
+
+ } else {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "Unknown response to StartTLS request :"
+ " an ExtendedResponse is expected";
+ }
+
+ if ( res != NULL ) {
+ ldap_msgfree( res );
+ }
+ }
+#else /* ! SLAP_STARTTLS_ASYNCHRONOUS */
+ /*
+ * use synchronous StartTLS
+ */
+ rs->sr_err = ldap_start_tls_s( msc->msc_ld, NULL, NULL );
+#endif /* ! SLAP_STARTTLS_ASYNCHRONOUS */
+
+ /* if StartTLS is requested, only attempt it if the URL
+ * is not "ldaps://"; this may occur not only in case
+ * of misconfiguration, but also when used in the chain
+ * overlay, where the "uri" can be parsed out of a referral */
+ if ( rs->sr_err == LDAP_SERVER_DOWN
+ || ( rs->sr_err != LDAP_SUCCESS
+ && META_BACK_TGT_TLS_CRITICAL( mt ) ) )
+ {
+
+#ifdef DEBUG_205
+ Debug( LDAP_DEBUG_ANY,
+ "### %s meta_back_init_one_conn(TLS) "
+ "ldap_unbind_ext[%d] ld=%p\n",
+ op->o_log_prefix, candidate,
+ (void *)msc->msc_ld );
+#endif /* DEBUG_205 */
+
+ /* need to trash a failed Start TLS */
+ meta_clear_one_candidate( op, mc, candidate );
+ goto error_return;
+ }
+ }
+#endif /* HAVE_TLS */
+
+ /*
+ * Set the network timeout if set
+ */
+ if ( mt->mt_network_timeout != 0 ) {
+ struct timeval network_timeout;
+
+ network_timeout.tv_usec = 0;
+ network_timeout.tv_sec = mt->mt_network_timeout;
+
+ ldap_set_option( msc->msc_ld, LDAP_OPT_NETWORK_TIMEOUT,
+ (void *)&network_timeout );
+ }
+
+ /*
+ * If the connection DN is not null, an attempt to rewrite it is made
+ */
+
+ if ( ispriv ) {
+ if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) ) {
+ ber_bvreplace( &msc->msc_bound_ndn, &mt->mt_idassert_authcDN );
+ if ( !BER_BVISNULL( &mt->mt_idassert_passwd ) ) {
+ if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+ memset( msc->msc_cred.bv_val, 0,
+ msc->msc_cred.bv_len );
+ }
+ ber_bvreplace( &msc->msc_cred, &mt->mt_idassert_passwd );
+ }
+ LDAP_BACK_CONN_ISIDASSERT_SET( msc );
+
+ } else {
+ ber_bvreplace( &msc->msc_bound_ndn, &slap_empty_bv );
+ }
+
+ } else {
+ if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+ memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
+ ber_memfree_x( msc->msc_cred.bv_val, NULL );
+ BER_BVZERO( &msc->msc_cred );
+ }
+ if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
+ ber_memfree_x( msc->msc_bound_ndn.bv_val, NULL );
+ BER_BVZERO( &msc->msc_bound_ndn );
+ }
+ if ( !BER_BVISEMPTY( &op->o_ndn )
+ && SLAP_IS_AUTHZ_BACKEND( op )
+ && isauthz )
+ {
+ dc.target = mt;
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+ dc.ctx = "bindDN";
+
+ /*
+ * Rewrite the bind dn if needed
+ */
+ if ( ldap_back_dn_massage( &dc, &op->o_conn->c_dn,
+ &msc->msc_bound_ndn ) )
+ {
+
+#ifdef DEBUG_205
+ Debug( LDAP_DEBUG_ANY,
+ "### %s meta_back_init_one_conn(rewrite) "
+ "ldap_unbind_ext[%d] ld=%p\n",
+ op->o_log_prefix, candidate,
+ (void *)msc->msc_ld );
+#endif /* DEBUG_205 */
+
+ /* need to trash a connection not fully established */
+ meta_clear_one_candidate( op, mc, candidate );
+ goto error_return;
+ }
+
+ /* copy the DN if needed */
+ if ( msc->msc_bound_ndn.bv_val == op->o_conn->c_dn.bv_val ) {
+ ber_dupbv( &msc->msc_bound_ndn, &op->o_conn->c_dn );
+ }
+
+ assert( !BER_BVISNULL( &msc->msc_bound_ndn ) );
+
+ } else {
+ ber_dupbv( &msc->msc_bound_ndn, (struct berval *)&slap_empty_bv );
+ }
+ }
+
+ assert( !BER_BVISNULL( &msc->msc_bound_ndn ) );
+
+error_return:;
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ }
+ META_BACK_CONN_CREATING_CLEAR( msc );
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ /*
+ * Sets a cookie for the rewrite session
+ */
+ ( void )rewrite_session_init( mt->mt_rwmap.rwm_rw, op->o_conn );
+ META_BACK_CONN_INITED_SET( msc );
+ }
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ }
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ /* Get the error message and print it in TRACE mode */
+ if ( LogTest( LDAP_DEBUG_TRACE ) ) {
+ Log( LDAP_DEBUG_TRACE, ldap_syslog_level, "%s: meta_back_init_one_conn[%d] failed err=%d text=%s\n",
+ op->o_log_prefix, candidate, rs->sr_err, rs->sr_text );
+ }
+
+ rs->sr_err = slap_map_api2result( rs );
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ }
+
+ return rs->sr_err;
+}
+
+/*
+ * meta_back_retry
+ *
+ * Retries one connection
+ */
+int
+meta_back_retry(
+ Operation *op,
+ SlapReply *rs,
+ metaconn_t **mcp,
+ int candidate,
+ ldap_back_send_t sendok )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ metatarget_t *mt = mi->mi_targets[ candidate ];
+ metaconn_t *mc = *mcp;
+ metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+ int rc = LDAP_UNAVAILABLE,
+ binding,
+ quarantine = 1;
+
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+
+ assert( !META_BACK_CONN_CREATING( msc ) );
+ binding = LDAP_BACK_CONN_BINDING( msc );
+ LDAP_BACK_CONN_BINDING_CLEAR( msc );
+
+ assert( mc->mc_refcnt > 0 );
+ if ( mc->mc_refcnt == 1 ) {
+ struct berval save_cred;
+
+ if ( LogTest( LDAP_DEBUG_ANY ) ) {
+ /* this lock is required; however,
+ * it's invoked only when logging is on */
+ ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
+ Debug(LDAP_DEBUG_ANY,
+ "%s meta_back_retry[%d]: retrying URI=\"%s\" DN=\"%s\".\n",
+ op->o_log_prefix, candidate, mt->mt_uri,
+ BER_BVISNULL(&msc->msc_bound_ndn) ? "" : msc->msc_bound_ndn.bv_val );
+ ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
+ }
+
+ /* save credentials, if any, for later use;
+ * meta_clear_one_candidate() would free them */
+ save_cred = msc->msc_cred;
+ BER_BVZERO( &msc->msc_cred );
+
+ meta_clear_one_candidate( op, mc, candidate );
+ LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+
+ ( void )rewrite_session_delete( mt->mt_rwmap.rwm_rw, op->o_conn );
+
+ /* mc here must be the regular mc, reset and ready for init */
+ rc = meta_back_init_one_conn( op, rs, mc, candidate,
+ LDAP_BACK_CONN_ISPRIV( mc ), sendok, 0 );
+
+ /* restore credentials, if any and if needed;
+ * meta_back_init_one_conn() restores msc_bound_ndn, if any;
+ * if no msc_bound_ndn is restored, destroy credentials */
+ if ( !BER_BVISNULL( &msc->msc_bound_ndn )
+ && BER_BVISNULL( &msc->msc_cred ) )
+ {
+ msc->msc_cred = save_cred;
+
+ } else if ( !BER_BVISNULL( &save_cred ) ) {
+ memset( save_cred.bv_val, 0, save_cred.bv_len );
+ ber_memfree_x( save_cred.bv_val, NULL );
+ }
+
+ /* restore the "binding" flag, in case */
+ if ( binding ) {
+ LDAP_BACK_CONN_BINDING_SET( msc );
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ quarantine = 0;
+ LDAP_BACK_CONN_BINDING_SET( msc ); binding = 1;
+ rc = meta_back_single_dobind( op, rs, mcp, candidate,
+ sendok, mt->mt_nretries, 0 );
+
+ Debug( LDAP_DEBUG_ANY,
+ "%s meta_back_retry[%d]: "
+ "meta_back_single_dobind=%d\n",
+ op->o_log_prefix, candidate, rc );
+ if ( rc == LDAP_SUCCESS ) {
+ if ( !BER_BVISNULL( &msc->msc_bound_ndn ) &&
+ !BER_BVISEMPTY( &msc->msc_bound_ndn ) )
+ {
+ LDAP_BACK_CONN_ISBOUND_SET( msc );
+
+ } else {
+ LDAP_BACK_CONN_ISANON_SET( msc );
+ }
+
+ /* when bound, dispose of the "binding" flag */
+ if ( binding ) {
+ LDAP_BACK_CONN_BINDING_CLEAR( msc );
+ }
+ }
+ }
+
+#if 0 /* ITS#7591, following stmt drops needed result msgs */
+ /* don't send twice */
+ sendok &= ~LDAP_BACK_SENDERR;
+#endif
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ SlapReply *candidates = meta_back_candidates_get( op );
+
+ candidates[ candidate ].sr_err = rc;
+
+ if ( *mcp != NULL ) {
+ if ( mc->mc_refcnt == 1 ) {
+ if ( binding ) {
+ LDAP_BACK_CONN_BINDING_CLEAR( msc );
+ }
+ (void)meta_clear_one_candidate( op, mc, candidate );
+ }
+
+ LDAP_BACK_CONN_TAINTED_SET( mc );
+ /* only release if mandatory; otherwise
+ * let the caller do what's best before
+ * releasing */
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ meta_back_release_conn_lock( mi, mc, 0 );
+ *mcp = NULL;
+
+ } else {
+#if META_BACK_PRINT_CONNTREE > 0
+ meta_back_print_conntree( mi, ">>> meta_back_retry" );
+#endif /* META_BACK_PRINT_CONNTREE */
+
+ /* FIXME: could be done better, reworking meta_back_release_conn_lock() */
+ if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) {
+ if ( mc->mc_q.tqe_prev != NULL ) {
+ assert( LDAP_BACK_CONN_CACHED( mc ) );
+ assert( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num > 0 );
+ LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
+ mc, mc_q );
+ mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num--;
+ LDAP_TAILQ_ENTRY_INIT( mc, mc_q );
+
+ } else {
+ assert( !LDAP_BACK_CONN_CACHED( mc ) );
+ }
+
+ } else {
+ /* FIXME: check if in tree, for consistency? */
+ (void)ldap_tavl_delete( &mi->mi_conninfo.lai_tree,
+ ( caddr_t )mc, meta_back_conndnmc_cmp );
+ }
+ LDAP_BACK_CONN_CACHED_CLEAR( mc );
+
+#if META_BACK_PRINT_CONNTREE > 0
+ meta_back_print_conntree( mi, "<<< meta_back_retry" );
+#endif /* META_BACK_PRINT_CONNTREE */
+ }
+ }
+
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ rs->sr_err = rc;
+ rs->sr_text = "Unable to retry";
+ send_ldap_result( op, rs );
+ }
+ }
+
+ if ( quarantine && META_BACK_TGT_QUARANTINE( mt ) ) {
+ meta_back_quarantine( op, rs, candidate );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+
+ return rc == LDAP_SUCCESS ? 1 : 0;
+}
+
+/*
+ * callback for unique candidate selection
+ */
+static int
+meta_back_conn_cb( Operation *op, SlapReply *rs )
+{
+ assert( op->o_tag == LDAP_REQ_SEARCH );
+
+ switch ( rs->sr_type ) {
+ case REP_SEARCH:
+ ((long *)op->o_callback->sc_private)[0] = (long)op->o_private;
+ break;
+
+ case REP_SEARCHREF:
+ case REP_RESULT:
+ break;
+
+ default:
+ return rs->sr_err;
+ }
+
+ return 0;
+}
+
+
+static int
+meta_back_get_candidate(
+ Operation *op,
+ SlapReply *rs,
+ struct berval *ndn )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ long candidate;
+
+ /*
+ * tries to get a unique candidate
+ * (takes care of default target)
+ */
+ candidate = meta_back_select_unique_candidate( mi, ndn );
+
+ /*
+ * if any is found, inits the connection
+ */
+ if ( candidate == META_TARGET_NONE ) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = "No suitable candidate target found";
+
+ } else if ( candidate == META_TARGET_MULTIPLE ) {
+ Operation op2 = *op;
+ SlapReply rs2 = { REP_RESULT };
+ slap_callback cb2 = { 0 };
+ int rc;
+
+ /* try to get a unique match for the request ndn
+ * among the multiple candidates available */
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2.o_req_dn = *ndn;
+ op2.o_req_ndn = *ndn;
+ op2.ors_scope = LDAP_SCOPE_BASE;
+ op2.ors_deref = LDAP_DEREF_NEVER;
+ op2.ors_attrs = slap_anlist_no_attrs;
+ op2.ors_attrsonly = 0;
+ op2.ors_limit = NULL;
+ op2.ors_slimit = 1;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+
+ op2.ors_filter = (Filter *)slap_filter_objectClass_pres;
+ op2.ors_filterstr = *slap_filterstr_objectClass_pres;
+
+ op2.o_callback = &cb2;
+ cb2.sc_response = meta_back_conn_cb;
+ cb2.sc_private = (void *)&candidate;
+
+ rc = op->o_bd->be_search( &op2, &rs2 );
+
+ switch ( rs2.sr_err ) {
+ case LDAP_SUCCESS:
+ default:
+ rs->sr_err = rs2.sr_err;
+ break;
+
+ case LDAP_SIZELIMIT_EXCEEDED:
+ /* if multiple candidates can serve the operation,
+ * and a default target is defined, and it is
+ * a candidate, try using it (FIXME: YMMV) */
+ if ( mi->mi_defaulttarget != META_DEFAULT_TARGET_NONE
+ && meta_back_is_candidate( mi->mi_targets[ mi->mi_defaulttarget ],
+ ndn, op->o_tag == LDAP_REQ_SEARCH ? op->ors_scope : LDAP_SCOPE_BASE ) )
+ {
+ candidate = mi->mi_defaulttarget;
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+
+ } else {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "Unable to select unique candidate target";
+ }
+ break;
+ }
+
+ } else {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+
+ return candidate;
+}
+
+static void *meta_back_candidates_dummy;
+
+static void
+meta_back_candidates_keyfree(
+ void *key,
+ void *data )
+{
+ metacandidates_t *mc = (metacandidates_t *)data;
+
+ ber_memfree_x( mc->mc_candidates, NULL );
+ ber_memfree_x( data, NULL );
+}
+
+SlapReply *
+meta_back_candidates_get( Operation *op )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ metacandidates_t *mc;
+
+ if ( op->o_threadctx ) {
+ void *data = NULL;
+
+ ldap_pvt_thread_pool_getkey( op->o_threadctx,
+ &meta_back_candidates_dummy, &data, NULL );
+ mc = (metacandidates_t *)data;
+
+ } else {
+ mc = mi->mi_candidates;
+ }
+
+ if ( mc == NULL ) {
+ mc = ch_calloc( sizeof( metacandidates_t ), 1 );
+ mc->mc_ntargets = mi->mi_ntargets;
+ mc->mc_candidates = ch_calloc( sizeof( SlapReply ), mc->mc_ntargets );
+ if ( op->o_threadctx ) {
+ void *data = NULL;
+
+ data = (void *)mc;
+ ldap_pvt_thread_pool_setkey( op->o_threadctx,
+ &meta_back_candidates_dummy, data,
+ meta_back_candidates_keyfree,
+ NULL, NULL );
+
+ } else {
+ mi->mi_candidates = mc;
+ }
+
+ } else if ( mc->mc_ntargets < mi->mi_ntargets ) {
+ /* NOTE: in the future, may want to allow back-config
+ * to add/remove targets from back-meta... */
+ mc->mc_candidates = ch_realloc( mc->mc_candidates,
+ sizeof( SlapReply ) * mi->mi_ntargets );
+ memset( &mc->mc_candidates[ mc->mc_ntargets ], 0,
+ sizeof( SlapReply ) * ( mi->mi_ntargets - mc->mc_ntargets ) );
+ mc->mc_ntargets = mi->mi_ntargets;
+ }
+
+ return mc->mc_candidates;
+}
+
+/*
+ * meta_back_getconn
+ *
+ * Prepares the connection structure
+ *
+ * RATIONALE:
+ *
+ * - determine what DN is being requested:
+ *
+ * op requires candidate checks
+ *
+ * add unique parent of o_req_ndn
+ * bind unique^*[/all] o_req_ndn [no check]
+ * compare unique^+ o_req_ndn
+ * delete unique o_req_ndn
+ * modify unique o_req_ndn
+ * search any o_req_ndn
+ * modrdn unique[, unique] o_req_ndn[, orr_nnewSup]
+ *
+ * - for ops that require the candidate to be unique, in case of multiple
+ * occurrences an internal search with sizeLimit=1 is performed
+ * if a unique candidate can actually be determined. If none is found,
+ * the operation aborts; if multiple are found, the default target
+ * is used if defined and candidate; otherwise the operation aborts.
+ *
+ * *^note: actually, the bind operation is handled much like a search;
+ * i.e. the bind is broadcast to all candidate targets.
+ *
+ * +^note: actually, the compare operation is handled much like a search;
+ * i.e. the compare is broadcast to all candidate targets, while checking
+ * that exactly none (noSuchObject) or one (TRUE/FALSE/UNDEFINED) is
+ * returned.
+ */
+metaconn_t *
+meta_back_getconn(
+ Operation *op,
+ SlapReply *rs,
+ int *candidate,
+ ldap_back_send_t sendok )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ metaconn_t *mc = NULL,
+ mc_curr = {{ 0 }};
+ int cached = META_TARGET_NONE,
+ i = META_TARGET_NONE,
+ err = LDAP_SUCCESS,
+ new_conn = 0,
+ ncandidates = 0;
+
+
+ meta_op_type op_type = META_OP_REQUIRE_SINGLE;
+ enum {
+ META_DNTYPE_ENTRY,
+ META_DNTYPE_PARENT,
+ META_DNTYPE_NEWPARENT
+ } dn_type = META_DNTYPE_ENTRY;
+ struct berval ndn = op->o_req_ndn,
+ pndn;
+
+ SlapReply *candidates = meta_back_candidates_get( op );
+
+ /* Internal searches are privileged and shared. So is root. */
+ if ( ( !BER_BVISEMPTY( &op->o_ndn ) && META_BACK_PROXYAUTHZ_ALWAYS( mi ) )
+ || ( BER_BVISEMPTY( &op->o_ndn ) && META_BACK_PROXYAUTHZ_ANON( mi ) )
+ || op->o_do_not_cache || be_isroot( op ) )
+ {
+ LDAP_BACK_CONN_ISPRIV_SET( &mc_curr );
+ mc_curr.mc_local_ndn = op->o_bd->be_rootndn;
+ LDAP_BACK_PCONN_ROOTDN_SET( &mc_curr, op );
+
+ } else if ( BER_BVISEMPTY( &op->o_ndn ) && META_BACK_PROXYAUTHZ_NOANON( mi ) )
+ {
+ LDAP_BACK_CONN_ISANON_SET( &mc_curr );
+ BER_BVSTR( &mc_curr.mc_local_ndn, "" );
+ LDAP_BACK_PCONN_ANON_SET( &mc_curr, op );
+
+ } else {
+ mc_curr.mc_local_ndn = op->o_ndn;
+
+ /* Explicit binds must not be shared */
+ if ( !BER_BVISEMPTY( &op->o_ndn )
+ || op->o_tag == LDAP_REQ_BIND
+ || SLAP_IS_AUTHZ_BACKEND( op ) )
+ {
+ mc_curr.mc_conn = op->o_conn;
+
+ } else {
+ LDAP_BACK_CONN_ISANON_SET( &mc_curr );
+ LDAP_BACK_PCONN_ANON_SET( &mc_curr, op );
+ }
+ }
+
+ /* Explicit Bind requests always get their own conn */
+ if ( sendok & LDAP_BACK_BINDING ) {
+ mc_curr.mc_conn = op->o_conn;
+
+ } else {
+ /* Searches for a metaconn in the avl tree */
+retry_lock:;
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ if ( LDAP_BACK_PCONN_ISPRIV( &mc_curr ) ) {
+ /* lookup a conn that's not binding */
+ LDAP_TAILQ_FOREACH( mc,
+ &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( &mc_curr ) ].mic_priv,
+ mc_q )
+ {
+ if ( !LDAP_BACK_CONN_BINDING( mc ) && mc->mc_refcnt == 0 ) {
+ break;
+ }
+ }
+
+ if ( mc != NULL ) {
+ /* move to tail of queue */
+ if ( mc != LDAP_TAILQ_LAST( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
+ mc_conn_priv_q ) )
+ {
+ LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
+ mc, mc_q );
+ LDAP_TAILQ_ENTRY_INIT( mc, mc_q );
+ LDAP_TAILQ_INSERT_TAIL( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
+ mc, mc_q );
+ }
+
+ } else if ( !LDAP_BACK_USE_TEMPORARIES( mi )
+ && mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( &mc_curr ) ].mic_num == mi->mi_conn_priv_max )
+ {
+ mc = LDAP_TAILQ_FIRST( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( &mc_curr ) ].mic_priv );
+ }
+
+
+ } else {
+ mc = (metaconn_t *)ldap_tavl_find( mi->mi_conninfo.lai_tree,
+ (caddr_t)&mc_curr, meta_back_conndn_cmp );
+ }
+
+ if ( mc ) {
+ /* catch taint errors */
+ assert( !LDAP_BACK_CONN_TAINTED( mc ) );
+
+ /* Don't reuse connections while they're still binding
+ * NOTE: only makes sense for binds */
+ if ( LDAP_BACK_CONN_BINDING( mc ) ) {
+ if ( !LDAP_BACK_USE_TEMPORARIES( mi ) ) {
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+
+ ldap_pvt_thread_yield();
+ goto retry_lock;
+ }
+
+ /* release conn, and create a temporary */
+ mc = NULL;
+
+ } else {
+ if ( mc->mc_refcnt == 0 && (( mi->mi_conn_ttl != 0 && op->o_time > mc->mc_create_time + mi->mi_conn_ttl )
+ || ( mi->mi_idle_timeout != 0 && op->o_time > mc->mc_time + mi->mi_idle_timeout )) )
+ {
+#if META_BACK_PRINT_CONNTREE > 0
+ meta_back_print_conntree( mi,
+ ">>> meta_back_getconn(expired)" );
+#endif /* META_BACK_PRINT_CONNTREE */
+
+ /* don't let anyone else use this expired connection */
+ if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) {
+ if ( mc->mc_q.tqe_prev != NULL ) {
+ assert( LDAP_BACK_CONN_CACHED( mc ) );
+ assert( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num > 0 );
+ LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv,
+ mc, mc_q );
+ mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num--;
+ LDAP_TAILQ_ENTRY_INIT( mc, mc_q );
+
+ } else {
+ assert( !LDAP_BACK_CONN_CACHED( mc ) );
+ }
+
+ } else {
+ (void)ldap_tavl_delete( &mi->mi_conninfo.lai_tree,
+ (caddr_t)mc, meta_back_conndnmc_cmp );
+ }
+
+#if META_BACK_PRINT_CONNTREE > 0
+ meta_back_print_conntree( mi,
+ "<<< meta_back_getconn(expired)" );
+#endif /* META_BACK_PRINT_CONNTREE */
+ LDAP_BACK_CONN_TAINTED_SET( mc );
+ LDAP_BACK_CONN_CACHED_CLEAR( mc );
+
+ if ( LogTest( LDAP_DEBUG_TRACE ) ) {
+ char buf[STRLENOF("4294967295U") + 1] = { 0 };
+ mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "%s meta_back_getconn: mc=%p conn=%s expired (tainted).\n",
+ op->o_log_prefix, (void *)mc, buf );
+ }
+ }
+
+ mc->mc_refcnt++;
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ }
+
+ switch ( op->o_tag ) {
+ case LDAP_REQ_ADD:
+ /* if we go to selection, the entry must not exist,
+ * and we must be able to resolve the parent */
+ dn_type = META_DNTYPE_PARENT;
+ dnParent( &ndn, &pndn );
+ break;
+
+ case LDAP_REQ_MODRDN:
+ /* if nnewSuperior is not NULL, it must resolve
+ * to the same candidate as the req_ndn */
+ if ( op->orr_nnewSup ) {
+ dn_type = META_DNTYPE_NEWPARENT;
+ }
+ break;
+
+ case LDAP_REQ_BIND:
+ /* if bound as rootdn, the backend must bind to all targets
+ * with the administrative identity
+ * (unless pseoudoroot-bind-defer is TRUE) */
+ if ( op->orb_method == LDAP_AUTH_SIMPLE && be_isroot_pw( op ) ) {
+ op_type = META_OP_REQUIRE_ALL;
+ }
+ break;
+
+ case LDAP_REQ_COMPARE:
+ case LDAP_REQ_DELETE:
+ case LDAP_REQ_MODIFY:
+ /* just a unique candidate */
+ break;
+
+ case LDAP_REQ_SEARCH:
+ /* allow multiple candidates for the searchBase */
+ op_type = META_OP_ALLOW_MULTIPLE;
+ break;
+
+ default:
+ /* right now, just break (exop?) */
+ break;
+ }
+
+ /*
+ * require all connections ...
+ */
+ if ( op_type == META_OP_REQUIRE_ALL ) {
+
+ /* Looks like we didn't get a bind. Open a new session... */
+ if ( mc == NULL ) {
+ assert( new_conn == 0 );
+ mc = metaconn_alloc( op );
+ mc->mc_conn = mc_curr.mc_conn;
+ ber_dupbv( &mc->mc_local_ndn, &mc_curr.mc_local_ndn );
+ new_conn = 1;
+ if ( sendok & LDAP_BACK_BINDING ) {
+ LDAP_BACK_CONN_BINDING_SET( mc );
+ }
+ if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
+ LDAP_BACK_CONN_ISPRIV_SET( mc );
+
+ } else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
+ LDAP_BACK_CONN_ISANON_SET( mc );
+ }
+
+ } else if ( 0 ) {
+ /* TODO: if any of the connections is binding,
+ * release mc and create a new one */
+ }
+
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ /*
+ * The target is activated; if needed, it is
+ * also init'd
+ */
+ candidates[ i ].sr_err = meta_back_init_one_conn( op,
+ rs, mc, i, LDAP_BACK_CONN_ISPRIV( &mc_curr ),
+ LDAP_BACK_DONTSEND, !new_conn );
+ if ( candidates[ i ].sr_err == LDAP_SUCCESS ) {
+ if ( new_conn && ( sendok & LDAP_BACK_BINDING ) ) {
+ LDAP_BACK_CONN_BINDING_SET( &mc->mc_conns[ i ] );
+ }
+ META_CANDIDATE_SET( &candidates[ i ] );
+ ncandidates++;
+
+ } else {
+
+ /*
+ * FIXME: in case one target cannot
+ * be init'd, should the other ones
+ * be tried?
+ */
+ META_CANDIDATE_RESET( &candidates[ i ] );
+ err = candidates[ i ].sr_err;
+ continue;
+ }
+ }
+
+ if ( ncandidates == 0 ) {
+ if ( new_conn ) {
+ mc->mc_refcnt = 0;
+ meta_back_conn_free( mc );
+
+ } else {
+ meta_back_release_conn( mi, mc );
+ }
+
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = "Unable to select valid candidates";
+
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
+ rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
+ }
+ send_ldap_result( op, rs );
+ rs->sr_matched = NULL;
+ }
+
+ return NULL;
+ }
+
+ goto done;
+ }
+
+ /*
+ * looks in cache, if any
+ */
+ if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
+ cached = i = meta_dncache_get_target( &mi->mi_cache, &op->o_req_ndn );
+ }
+
+ if ( op_type == META_OP_REQUIRE_SINGLE ) {
+ metatarget_t *mt = NULL;
+ metasingleconn_t *msc = NULL;
+
+ int j;
+
+ for ( j = 0; j < mi->mi_ntargets; j++ ) {
+ META_CANDIDATE_RESET( &candidates[ j ] );
+ }
+
+ /*
+ * tries to get a unique candidate
+ * (takes care of default target)
+ */
+ if ( i == META_TARGET_NONE ) {
+ i = meta_back_get_candidate( op, rs, &ndn );
+
+ if ( rs->sr_err == LDAP_NO_SUCH_OBJECT && dn_type == META_DNTYPE_PARENT ) {
+ i = meta_back_get_candidate( op, rs, &pndn );
+ }
+
+ if ( i < 0 || rs->sr_err != LDAP_SUCCESS ) {
+ if ( mc != NULL ) {
+ meta_back_release_conn( mi, mc );
+ }
+
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
+ rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
+ }
+ send_ldap_result( op, rs );
+ rs->sr_matched = NULL;
+ }
+
+ return NULL;
+ }
+ }
+
+ if ( dn_type == META_DNTYPE_NEWPARENT && meta_back_get_candidate( op, rs, op->orr_nnewSup ) != i )
+ {
+ if ( mc != NULL ) {
+ meta_back_release_conn( mi, mc );
+ }
+
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "Cross-target rename not supported";
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+
+ return NULL;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "==>meta_back_getconn: got target=%d for ndn=\"%s\" from cache\n",
+ i, op->o_req_ndn.bv_val );
+
+ if ( mc == NULL ) {
+ /* Retries searching for a metaconn in the avl tree
+ * the reason is that the connection might have been
+ * created by meta_back_get_candidate() */
+ if ( !( sendok & LDAP_BACK_BINDING ) ) {
+retry_lock2:;
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ mc = (metaconn_t *)ldap_tavl_find( mi->mi_conninfo.lai_tree,
+ (caddr_t)&mc_curr, meta_back_conndn_cmp );
+ if ( mc != NULL ) {
+ /* catch taint errors */
+ assert( !LDAP_BACK_CONN_TAINTED( mc ) );
+
+ /* Don't reuse connections while they're still binding */
+ if ( META_BACK_CONN_CREATING( &mc->mc_conns[ i ] )
+ || LDAP_BACK_CONN_BINDING( &mc->mc_conns[ i ] ) )
+ {
+ if ( !LDAP_BACK_USE_TEMPORARIES( mi ) ) {
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ ldap_pvt_thread_yield();
+ goto retry_lock2;
+ }
+
+ mc = NULL;
+
+ } else {
+ mc->mc_refcnt++;
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ }
+
+ /* Looks like we didn't get a bind. Open a new session... */
+ if ( mc == NULL ) {
+ assert( new_conn == 0 );
+ mc = metaconn_alloc( op );
+ mc->mc_conn = mc_curr.mc_conn;
+ ber_dupbv( &mc->mc_local_ndn, &mc_curr.mc_local_ndn );
+ new_conn = 1;
+ if ( sendok & LDAP_BACK_BINDING ) {
+ LDAP_BACK_CONN_BINDING_SET( mc );
+ }
+ if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
+ LDAP_BACK_CONN_ISPRIV_SET( mc );
+
+ } else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
+ LDAP_BACK_CONN_ISANON_SET( mc );
+ }
+ }
+ }
+
+ /*
+ * Clear all other candidates
+ */
+ ( void )meta_clear_unused_candidates( op, i );
+
+ mt = mi->mi_targets[ i ];
+ msc = &mc->mc_conns[ i ];
+
+ /*
+ * The target is activated; if needed, it is
+ * also init'd. In case of error, meta_back_init_one_conn
+ * sends the appropriate result.
+ */
+ err = meta_back_init_one_conn( op, rs, mc, i,
+ LDAP_BACK_CONN_ISPRIV( &mc_curr ), sendok, !new_conn );
+ if ( err != LDAP_SUCCESS ) {
+ /*
+ * FIXME: in case one target cannot
+ * be init'd, should the other ones
+ * be tried?
+ */
+ META_CANDIDATE_RESET( &candidates[ i ] );
+ if ( new_conn ) {
+ mc->mc_refcnt = 0;
+ meta_back_conn_free( mc );
+
+ } else {
+ meta_back_release_conn( mi, mc );
+ }
+ return NULL;
+ }
+
+ if ( new_conn && ( sendok & LDAP_BACK_BINDING ) ) {
+ LDAP_BACK_CONN_BINDING_SET( &mc->mc_conns[ i ] );
+ }
+
+ candidates[ i ].sr_err = LDAP_SUCCESS;
+ META_CANDIDATE_SET( &candidates[ i ] );
+ ncandidates++;
+
+ if ( candidate ) {
+ *candidate = i;
+ }
+
+ /*
+ * if no unique candidate ...
+ */
+ } else {
+
+ /* Looks like we didn't get a bind. Open a new session... */
+ if ( mc == NULL ) {
+ assert( new_conn == 0 );
+ mc = metaconn_alloc( op );
+ mc->mc_conn = mc_curr.mc_conn;
+ ber_dupbv( &mc->mc_local_ndn, &mc_curr.mc_local_ndn );
+ new_conn = 1;
+ if ( LDAP_BACK_CONN_ISPRIV( &mc_curr ) ) {
+ LDAP_BACK_CONN_ISPRIV_SET( mc );
+
+ } else if ( LDAP_BACK_CONN_ISANON( &mc_curr ) ) {
+ LDAP_BACK_CONN_ISANON_SET( mc );
+ }
+ }
+
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ metatarget_t *mt = mi->mi_targets[ i ];
+
+ META_CANDIDATE_RESET( &candidates[ i ] );
+
+ if ( i == cached
+ || meta_back_is_candidate( mt, &op->o_req_ndn,
+ op->o_tag == LDAP_REQ_SEARCH ? op->ors_scope : LDAP_SCOPE_SUBTREE ) )
+ {
+
+ /*
+ * The target is activated; if needed, it is
+ * also init'd
+ */
+ int lerr = meta_back_init_one_conn( op, rs, mc, i,
+ LDAP_BACK_CONN_ISPRIV( &mc_curr ),
+ LDAP_BACK_DONTSEND, !new_conn );
+ candidates[ i ].sr_err = lerr;
+ if ( lerr == LDAP_SUCCESS ) {
+ META_CANDIDATE_SET( &candidates[ i ] );
+ ncandidates++;
+
+ Debug( LDAP_DEBUG_TRACE, "%s: meta_back_getconn[%d]\n",
+ op->o_log_prefix, i );
+
+ } else if ( lerr == LDAP_UNAVAILABLE && !META_BACK_ONERR_STOP( mi ) ) {
+ META_CANDIDATE_SET( &candidates[ i ] );
+
+ Debug( LDAP_DEBUG_TRACE, "%s: meta_back_getconn[%d] %s\n",
+ op->o_log_prefix, i,
+ mt->mt_isquarantined != LDAP_BACK_FQ_NO ? "quarantined" : "unavailable" );
+
+ } else {
+
+ /*
+ * FIXME: in case one target cannot
+ * be init'd, should the other ones
+ * be tried?
+ */
+ if ( new_conn ) {
+ ( void )meta_clear_one_candidate( op, mc, i );
+ }
+ /* leave the target candidate, but record the error for later use */
+ err = lerr;
+
+ if ( lerr == LDAP_UNAVAILABLE && mt->mt_isquarantined != LDAP_BACK_FQ_NO ) {
+ Log( LDAP_DEBUG_TRACE, ldap_syslog_level, "%s: meta_back_getconn[%d] quarantined err=%d text=%s\n",
+ op->o_log_prefix, i, lerr, rs->sr_text );
+
+ } else {
+ Log( LDAP_DEBUG_ANY, ldap_syslog, "%s: meta_back_getconn[%d] failed err=%d text=%s\n",
+ op->o_log_prefix, i, lerr, rs->sr_text );
+ }
+
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ if ( new_conn ) {
+ mc->mc_refcnt = 0;
+ meta_back_conn_free( mc );
+
+ } else {
+ meta_back_release_conn( mi, mc );
+ }
+
+ return NULL;
+ }
+
+ continue;
+ }
+
+ } else {
+ if ( new_conn ) {
+ ( void )meta_clear_one_candidate( op, mc, i );
+ }
+ }
+ }
+
+ if ( ncandidates == 0 ) {
+ if ( new_conn ) {
+ mc->mc_refcnt = 0;
+ meta_back_conn_free( mc );
+
+ } else {
+ meta_back_release_conn( mi, mc );
+ }
+
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = "Unable to select valid candidates";
+ }
+
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
+ rs->sr_matched = op->o_bd->be_suffix[ 0 ].bv_val;
+ }
+ send_ldap_result( op, rs );
+ rs->sr_matched = NULL;
+ }
+
+ return NULL;
+ }
+ }
+
+done:;
+ /* clear out meta_back_init_one_conn non-fatal errors */
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+
+ /* touch the timestamp */
+ if ( mi->mi_idle_timeout != 0 ) {
+ mc->mc_time = op->o_time;
+ }
+
+ if ( new_conn ) {
+ if ( mi->mi_conn_ttl ) {
+ mc->mc_create_time = op->o_time;
+ }
+
+ /*
+ * Inserts the newly created metaconn in the avl tree
+ */
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+#if META_BACK_PRINT_CONNTREE > 0
+ meta_back_print_conntree( mi, ">>> meta_back_getconn" );
+#endif /* META_BACK_PRINT_CONNTREE */
+
+ err = 0;
+ if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) {
+ if ( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num < mi->mi_conn_priv_max ) {
+ LDAP_TAILQ_INSERT_TAIL( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv, mc, mc_q );
+ mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num++;
+ LDAP_BACK_CONN_CACHED_SET( mc );
+
+ } else {
+ LDAP_BACK_CONN_TAINTED_SET( mc );
+ }
+ rs->sr_err = 0;
+
+ } else if ( !( sendok & LDAP_BACK_BINDING ) ) {
+ err = ldap_tavl_insert( &mi->mi_conninfo.lai_tree, ( caddr_t )mc,
+ meta_back_conndn_cmp, meta_back_conndn_dup );
+ LDAP_BACK_CONN_CACHED_SET( mc );
+ }
+
+#if META_BACK_PRINT_CONNTREE > 0
+ meta_back_print_conntree( mi, "<<< meta_back_getconn" );
+#endif /* META_BACK_PRINT_CONNTREE */
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+
+ if ( !LDAP_BACK_PCONN_ISPRIV( mc ) ) {
+ /*
+ * Err could be -1 in case a duplicate metaconn is inserted
+ */
+ switch ( err ) {
+ case 0:
+ break;
+
+ case -1:
+ LDAP_BACK_CONN_CACHED_CLEAR( mc );
+ /* duplicate: free and try to get the newly created one */
+ if ( !( sendok & LDAP_BACK_BINDING ) && !LDAP_BACK_USE_TEMPORARIES( mi ) ) {
+ mc->mc_refcnt = 0;
+ meta_back_conn_free( mc );
+
+ new_conn = 0;
+ goto retry_lock;
+ }
+
+ LDAP_BACK_CONN_TAINTED_SET( mc );
+ break;
+
+ default:
+ LDAP_BACK_CONN_CACHED_CLEAR( mc );
+ if ( LogTest( LDAP_DEBUG_ANY ) ) {
+ char buf[STRLENOF("4294967295U") + 1] = { 0 };
+ mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
+
+ Debug( LDAP_DEBUG_ANY,
+ "%s meta_back_getconn: candidates=%d conn=%s insert failed\n",
+ op->o_log_prefix, ncandidates, buf );
+ }
+
+ mc->mc_refcnt = 0;
+ meta_back_conn_free( mc );
+
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "Proxy bind collision";
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ }
+ return NULL;
+ }
+ }
+
+ if ( LogTest( LDAP_DEBUG_TRACE ) ) {
+ char buf[STRLENOF("4294967295U") + 1] = { 0 };
+ mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "%s meta_back_getconn: candidates=%d conn=%s inserted\n",
+ op->o_log_prefix, ncandidates, buf );
+ }
+
+ } else {
+ if ( LogTest( LDAP_DEBUG_TRACE ) ) {
+ char buf[STRLENOF("4294967295U") + 1] = { 0 };
+ mi->mi_ldap_extra->connid2str( &mc->mc_base, buf, sizeof(buf) );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "%s meta_back_getconn: candidates=%d conn=%s fetched\n",
+ op->o_log_prefix, ncandidates, buf );
+ }
+ }
+
+ return mc;
+}
+
+void
+meta_back_release_conn_lock(
+ metainfo_t *mi,
+ metaconn_t *mc,
+ int dolock )
+{
+ assert( mc != NULL );
+
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ }
+ assert( mc->mc_refcnt > 0 );
+ mc->mc_refcnt--;
+ /* NOTE: the connection is removed if either it is tainted
+ * or if it is shared and no one else is using it. This needs
+ * to occur because for intrinsic reasons cached connections
+ * that are not privileged would live forever and pollute
+ * the connection space (and eat up resources). Maybe this
+ * should be configurable... */
+ if ( LDAP_BACK_CONN_TAINTED( mc ) || !LDAP_BACK_CONN_CACHED( mc ) ) {
+#if META_BACK_PRINT_CONNTREE > 0
+ meta_back_print_conntree( mi, ">>> meta_back_release_conn" );
+#endif /* META_BACK_PRINT_CONNTREE */
+
+ if ( LDAP_BACK_PCONN_ISPRIV( mc ) ) {
+ if ( mc->mc_q.tqe_prev != NULL ) {
+ assert( LDAP_BACK_CONN_CACHED( mc ) );
+ assert( mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num > 0 );
+ LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_priv, mc, mc_q );
+ mi->mi_conn_priv[ LDAP_BACK_CONN2PRIV( mc ) ].mic_num--;
+ LDAP_TAILQ_ENTRY_INIT( mc, mc_q );
+
+ } else {
+ assert( !LDAP_BACK_CONN_CACHED( mc ) );
+ }
+
+ } else if ( LDAP_BACK_CONN_CACHED( mc ) ) {
+ metaconn_t *tmpmc;
+
+ tmpmc = ldap_tavl_delete( &mi->mi_conninfo.lai_tree,
+ ( caddr_t )mc, meta_back_conndnmc_cmp );
+
+ /* Overparanoid, but useful... */
+ assert( tmpmc == NULL || tmpmc == mc );
+ }
+
+ LDAP_BACK_CONN_CACHED_CLEAR( mc );
+
+#if META_BACK_PRINT_CONNTREE > 0
+ meta_back_print_conntree( mi, "<<< meta_back_release_conn" );
+#endif /* META_BACK_PRINT_CONNTREE */
+
+ if ( mc->mc_refcnt == 0 ) {
+ meta_back_conn_free( mc );
+ mc = NULL;
+ }
+ }
+
+ if ( mc != NULL && LDAP_BACK_CONN_BINDING( mc ) ) {
+ LDAP_BACK_CONN_BINDING_CLEAR( mc );
+ }
+
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ }
+}
+
+void
+meta_back_quarantine(
+ Operation *op,
+ SlapReply *rs,
+ int candidate )
+{
+ metainfo_t *mi = (metainfo_t *)op->o_bd->be_private;
+ metatarget_t *mt = mi->mi_targets[ candidate ];
+
+ slap_retry_info_t *ri = &mt->mt_quarantine;
+
+ ldap_pvt_thread_mutex_lock( &mt->mt_quarantine_mutex );
+
+ if ( rs->sr_err == LDAP_UNAVAILABLE ) {
+ time_t new_last = slap_get_time();
+
+ switch ( mt->mt_isquarantined ) {
+ case LDAP_BACK_FQ_NO:
+ if ( ri->ri_last == new_last ) {
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_ANY,
+ "%s meta_back_quarantine[%d]: enter.\n",
+ op->o_log_prefix, candidate );
+
+ ri->ri_idx = 0;
+ ri->ri_count = 0;
+ break;
+
+ case LDAP_BACK_FQ_RETRYING:
+ Debug(LDAP_DEBUG_ANY,
+ "%s meta_back_quarantine[%d]: block #%d try #%d failed.\n",
+ op->o_log_prefix, candidate, ri->ri_idx,
+ ri->ri_count );
+
+ ++ri->ri_count;
+ if ( ri->ri_num[ ri->ri_idx ] != SLAP_RETRYNUM_FOREVER
+ && ri->ri_count == ri->ri_num[ ri->ri_idx ] )
+ {
+ ri->ri_count = 0;
+ ++ri->ri_idx;
+ }
+ break;
+
+ default:
+ goto done;
+ }
+
+ mt->mt_isquarantined = LDAP_BACK_FQ_YES;
+ ri->ri_last = new_last;
+
+ } else if ( mt->mt_isquarantined == LDAP_BACK_FQ_RETRYING ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s meta_back_quarantine[%d]: exit.\n",
+ op->o_log_prefix, candidate );
+
+ if ( mi->mi_quarantine_f ) {
+ (void)mi->mi_quarantine_f( mi, candidate,
+ mi->mi_quarantine_p );
+ }
+
+ ri->ri_count = 0;
+ ri->ri_idx = 0;
+ mt->mt_isquarantined = LDAP_BACK_FQ_NO;
+ }
+
+done:;
+ ldap_pvt_thread_mutex_unlock( &mt->mt_quarantine_mutex );
+}
diff --git a/servers/slapd/back-meta/delete.c b/servers/slapd/back-meta/delete.c
new file mode 100644
index 0000000..bec437a
--- /dev/null
+++ b/servers/slapd/back-meta/delete.c
@@ -0,0 +1,103 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+int
+meta_back_delete( Operation *op, SlapReply *rs )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ metatarget_t *mt;
+ metaconn_t *mc = NULL;
+ int candidate = -1;
+ struct berval mdn = BER_BVNULL;
+ dncookie dc;
+ int msgid;
+ ldap_back_send_t retrying = LDAP_BACK_RETRYING;
+ LDAPControl **ctrls = NULL;
+
+ mc = meta_back_getconn( op, rs, &candidate, LDAP_BACK_SENDERR );
+ if ( !mc || !meta_back_dobind( op, rs, mc, LDAP_BACK_SENDERR ) ) {
+ return rs->sr_err;
+ }
+
+ assert( mc->mc_conns[ candidate ].msc_ld != NULL );
+
+ /*
+ * Rewrite the compare dn, if needed
+ */
+ mt = mi->mi_targets[ candidate ];
+ dc.target = mt;
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+ dc.ctx = "deleteDN";
+
+ if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+retry:;
+ ctrls = op->o_ctrls;
+ if ( meta_back_controls_add( op, rs, mc, candidate, &ctrls ) != LDAP_SUCCESS )
+ {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ rs->sr_err = ldap_delete_ext( mc->mc_conns[ candidate ].msc_ld,
+ mdn.bv_val, ctrls, NULL, &msgid );
+ rs->sr_err = meta_back_op_result( mc, op, rs, candidate, msgid,
+ mt->mt_timeout[ SLAP_OP_DELETE ], ( LDAP_BACK_SENDRESULT | retrying ) );
+ if ( rs->sr_err == LDAP_UNAVAILABLE && retrying ) {
+ retrying &= ~LDAP_BACK_RETRYING;
+ if ( meta_back_retry( op, rs, &mc, candidate, LDAP_BACK_SENDERR ) ) {
+ /* if the identity changed, there might be need to re-authz */
+ (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+ goto retry;
+ }
+ }
+
+cleanup:;
+ (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+
+ if ( mdn.bv_val != op->o_req_dn.bv_val ) {
+ free( mdn.bv_val );
+ BER_BVZERO( &mdn );
+ }
+
+ if ( mc ) {
+ meta_back_release_conn( mi, mc );
+ }
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/back-meta/dncache.c b/servers/slapd/back-meta/dncache.c
new file mode 100644
index 0000000..a3e7958
--- /dev/null
+++ b/servers/slapd/back-meta/dncache.c
@@ -0,0 +1,235 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+/*
+ * The dncache, at present, maps an entry to the target that holds it.
+ */
+
+typedef struct metadncacheentry_t {
+ struct berval dn;
+ int target;
+
+ time_t lastupdated;
+} metadncacheentry_t;
+
+/*
+ * meta_dncache_cmp
+ *
+ * compares two struct metadncacheentry; used by avl stuff
+ * FIXME: modify avl stuff to delete an entry based on cmp
+ * (e.g. when ttl expired?)
+ */
+int
+meta_dncache_cmp(
+ const void *c1,
+ const void *c2 )
+{
+ metadncacheentry_t *cc1 = ( metadncacheentry_t * )c1;
+ metadncacheentry_t *cc2 = ( metadncacheentry_t * )c2;
+
+ /*
+ * case sensitive, because the dn MUST be normalized
+ */
+ return ber_bvcmp( &cc1->dn, &cc2->dn);
+}
+
+/*
+ * meta_dncache_dup
+ *
+ * returns -1 in case a duplicate struct metadncacheentry has been inserted;
+ * used by avl stuff
+ */
+int
+meta_dncache_dup(
+ void *c1,
+ void *c2 )
+{
+ metadncacheentry_t *cc1 = ( metadncacheentry_t * )c1;
+ metadncacheentry_t *cc2 = ( metadncacheentry_t * )c2;
+
+ /*
+ * case sensitive, because the dn MUST be normalized
+ */
+ return ( ber_bvcmp( &cc1->dn, &cc2->dn ) == 0 ) ? -1 : 0;
+}
+
+/*
+ * meta_dncache_get_target
+ *
+ * returns the target a dn belongs to, or -1 in case the dn is not
+ * in the cache
+ */
+int
+meta_dncache_get_target(
+ metadncache_t *cache,
+ struct berval *ndn )
+{
+ metadncacheentry_t tmp_entry,
+ *entry;
+ int target = META_TARGET_NONE;
+
+ assert( cache != NULL );
+ assert( ndn != NULL );
+
+ tmp_entry.dn = *ndn;
+ ldap_pvt_thread_mutex_lock( &cache->mutex );
+ entry = ( metadncacheentry_t * )ldap_avl_find( cache->tree,
+ ( caddr_t )&tmp_entry, meta_dncache_cmp );
+
+ if ( entry != NULL ) {
+
+ /*
+ * if cache->ttl < 0, cache never expires;
+ * if cache->ttl = 0 no cache is used; shouldn't get here
+ * else, cache is used with ttl
+ */
+ if ( cache->ttl < 0 ) {
+ target = entry->target;
+
+ } else {
+ if ( entry->lastupdated+cache->ttl > slap_get_time() ) {
+ target = entry->target;
+ }
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &cache->mutex );
+
+ return target;
+}
+
+/*
+ * meta_dncache_update_entry
+ *
+ * updates target and lastupdated of a struct metadncacheentry if exists,
+ * otherwise it gets created; returns -1 in case of error
+ */
+int
+meta_dncache_update_entry(
+ metadncache_t *cache,
+ struct berval *ndn,
+ int target )
+{
+ metadncacheentry_t *entry,
+ tmp_entry;
+ time_t curr_time = 0L;
+ int err = 0;
+
+ assert( cache != NULL );
+ assert( ndn != NULL );
+
+ /*
+ * if cache->ttl < 0, cache never expires;
+ * if cache->ttl = 0 no cache is used; shouldn't get here
+ * else, cache is used with ttl
+ */
+ if ( cache->ttl > 0 ) {
+ curr_time = slap_get_time();
+ }
+
+ tmp_entry.dn = *ndn;
+
+ ldap_pvt_thread_mutex_lock( &cache->mutex );
+ entry = ( metadncacheentry_t * )ldap_avl_find( cache->tree,
+ ( caddr_t )&tmp_entry, meta_dncache_cmp );
+
+ if ( entry != NULL ) {
+ entry->target = target;
+ entry->lastupdated = curr_time;
+
+ } else {
+ entry = ch_malloc( sizeof( metadncacheentry_t ) + ndn->bv_len + 1 );
+ if ( entry == NULL ) {
+ err = -1;
+ goto error_return;
+ }
+
+ entry->dn.bv_len = ndn->bv_len;
+ entry->dn.bv_val = (char *)&entry[ 1 ];
+ AC_MEMCPY( entry->dn.bv_val, ndn->bv_val, ndn->bv_len );
+ entry->dn.bv_val[ ndn->bv_len ] = '\0';
+
+ entry->target = target;
+ entry->lastupdated = curr_time;
+
+ err = ldap_avl_insert( &cache->tree, ( caddr_t )entry,
+ meta_dncache_cmp, meta_dncache_dup );
+ }
+
+error_return:;
+ ldap_pvt_thread_mutex_unlock( &cache->mutex );
+
+ return err;
+}
+
+/*
+ * meta_dncache_update_entry
+ *
+ * updates target and lastupdated of a struct metadncacheentry if exists,
+ * otherwise it gets created; returns -1 in case of error
+ */
+int
+meta_dncache_delete_entry(
+ metadncache_t *cache,
+ struct berval *ndn )
+{
+ metadncacheentry_t *entry,
+ tmp_entry;
+
+ assert( cache != NULL );
+ assert( ndn != NULL );
+
+ tmp_entry.dn = *ndn;
+
+ ldap_pvt_thread_mutex_lock( &cache->mutex );
+ entry = ldap_avl_delete( &cache->tree, ( caddr_t )&tmp_entry,
+ meta_dncache_cmp );
+ ldap_pvt_thread_mutex_unlock( &cache->mutex );
+
+ if ( entry != NULL ) {
+ meta_dncache_free( ( void * )entry );
+ }
+
+ return 0;
+}
+
+/*
+ * meta_dncache_free
+ *
+ * frees an entry
+ *
+ */
+void
+meta_dncache_free(
+ void *e )
+{
+ free( e );
+}
+
diff --git a/servers/slapd/back-meta/init.c b/servers/slapd/back-meta/init.c
new file mode 100644
index 0000000..8639109
--- /dev/null
+++ b/servers/slapd/back-meta/init.c
@@ -0,0 +1,473 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+int
+meta_back_open(
+ BackendInfo *bi )
+{
+ /* FIXME: need to remove the pagedResults, and likely more... */
+ bi->bi_controls = slap_known_controls;
+
+ return 0;
+}
+
+int
+meta_back_initialize(
+ BackendInfo *bi )
+{
+ bi->bi_flags =
+#if 0
+ /* this is not (yet) set essentially because back-meta does not
+ * directly support extended operations... */
+#ifdef LDAP_DYNAMIC_OBJECTS
+ /* this is set because all the support a proxy has to provide
+ * is the capability to forward the refresh exop, and to
+ * pass thru entries that contain the dynamicObject class
+ * and the entryTtl attribute */
+ SLAP_BFLAG_DYNAMIC |
+#endif /* LDAP_DYNAMIC_OBJECTS */
+#endif
+
+ /* back-meta recognizes RFC4525 increment;
+ * let the remote server complain, if needed (ITS#5912) */
+ SLAP_BFLAG_INCREMENT;
+
+ bi->bi_open = meta_back_open;
+ bi->bi_config = 0;
+ bi->bi_close = 0;
+ bi->bi_destroy = 0;
+
+ bi->bi_db_init = meta_back_db_init;
+ bi->bi_db_config = config_generic_wrapper;
+ bi->bi_db_open = meta_back_db_open;
+ bi->bi_db_close = 0;
+ bi->bi_db_destroy = meta_back_db_destroy;
+
+ bi->bi_op_bind = meta_back_bind;
+ bi->bi_op_unbind = 0;
+ bi->bi_op_search = meta_back_search;
+ bi->bi_op_compare = meta_back_compare;
+ bi->bi_op_modify = meta_back_modify;
+ bi->bi_op_modrdn = meta_back_modrdn;
+ bi->bi_op_add = meta_back_add;
+ bi->bi_op_delete = meta_back_delete;
+ bi->bi_op_abandon = 0;
+
+ bi->bi_extended = 0;
+
+ bi->bi_chk_referrals = 0;
+
+ bi->bi_connection_init = 0;
+ bi->bi_connection_destroy = meta_back_conn_destroy;
+
+ return meta_back_init_cf( bi );
+}
+
+int
+meta_back_db_init(
+ Backend *be,
+ ConfigReply *cr)
+{
+ metainfo_t *mi;
+ int i;
+ BackendInfo *bi;
+
+ bi = backend_info( "ldap" );
+ if ( !bi || !bi->bi_extra ) {
+ Debug( LDAP_DEBUG_ANY,
+ "meta_back_db_init: needs back-ldap\n" );
+ return 1;
+ }
+
+ mi = ch_calloc( 1, sizeof( metainfo_t ) );
+ if ( mi == NULL ) {
+ return -1;
+ }
+
+ /* set default flags */
+ mi->mi_flags =
+ META_BACK_F_DEFER_ROOTDN_BIND
+ | META_BACK_F_PROXYAUTHZ_ALWAYS
+ | META_BACK_F_PROXYAUTHZ_ANON
+ | META_BACK_F_PROXYAUTHZ_NOANON;
+
+ /*
+ * At present the default is no default target;
+ * this may change
+ */
+ mi->mi_defaulttarget = META_DEFAULT_TARGET_NONE;
+ mi->mi_bind_timeout.tv_sec = 0;
+ mi->mi_bind_timeout.tv_usec = META_BIND_TIMEOUT;
+
+ mi->mi_rebind_f = meta_back_default_rebind;
+ mi->mi_urllist_f = meta_back_default_urllist;
+
+ ldap_pvt_thread_mutex_init( &mi->mi_conninfo.lai_mutex );
+ ldap_pvt_thread_mutex_init( &mi->mi_cache.mutex );
+
+ /* safe default */
+ mi->mi_nretries = META_RETRY_DEFAULT;
+ mi->mi_version = LDAP_VERSION3;
+
+ for ( i = LDAP_BACK_PCONN_FIRST; i < LDAP_BACK_PCONN_LAST; i++ ) {
+ mi->mi_conn_priv[ i ].mic_num = 0;
+ LDAP_TAILQ_INIT( &mi->mi_conn_priv[ i ].mic_priv );
+ }
+ mi->mi_conn_priv_max = LDAP_BACK_CONN_PRIV_DEFAULT;
+
+ mi->mi_ldap_extra = (ldap_extra_t *)bi->bi_extra;
+
+ be->be_private = mi;
+ be->be_cf_ocs = be->bd_info->bi_cf_ocs;
+
+ return 0;
+}
+
+int
+meta_target_finish(
+ metainfo_t *mi,
+ metatarget_t *mt,
+ const char *log,
+ char *msg,
+ size_t msize
+)
+{
+ slap_bindconf sb = { BER_BVNULL };
+ struct berval mapped;
+ int rc;
+
+ ber_str2bv( mt->mt_uri, 0, 0, &sb.sb_uri );
+ sb.sb_version = mt->mt_version;
+ sb.sb_method = LDAP_AUTH_SIMPLE;
+ BER_BVSTR( &sb.sb_binddn, "" );
+
+ if ( META_BACK_TGT_T_F_DISCOVER( mt ) ) {
+ rc = slap_discover_feature( &sb,
+ slap_schema.si_ad_supportedFeatures->ad_cname.bv_val,
+ LDAP_FEATURE_ABSOLUTE_FILTERS );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ mt->mt_flags |= LDAP_BACK_F_T_F;
+ }
+ }
+
+ if ( META_BACK_TGT_CANCEL_DISCOVER( mt ) ) {
+ rc = slap_discover_feature( &sb,
+ slap_schema.si_ad_supportedExtension->ad_cname.bv_val,
+ LDAP_EXOP_CANCEL );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ mt->mt_flags |= LDAP_BACK_F_CANCEL_EXOP;
+ }
+ }
+
+ if ( !( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE )
+ || mt->mt_idassert_authz != NULL )
+ {
+ mi->mi_flags &= ~META_BACK_F_PROXYAUTHZ_ALWAYS;
+ }
+
+ if ( ( mt->mt_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL )
+ && !( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) )
+ {
+ snprintf( msg, msize,
+ "%s: inconsistent idassert configuration "
+ "(likely authz=\"*\" used with \"non-prescriptive\" flag)",
+ log );
+ Debug( LDAP_DEBUG_ANY, "%s (target %s)\n",
+ msg, mt->mt_uri );
+ return 1;
+ }
+
+ if ( !( mt->mt_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) )
+ {
+ mi->mi_flags &= ~META_BACK_F_PROXYAUTHZ_ANON;
+ }
+
+ if ( ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) )
+ {
+ mi->mi_flags &= ~META_BACK_F_PROXYAUTHZ_NOANON;
+ }
+
+ BER_BVZERO( &mapped );
+ ldap_back_map( &mt->mt_rwmap.rwm_at,
+ &slap_schema.si_ad_entryDN->ad_cname, &mapped,
+ BACKLDAP_REMAP );
+ if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0' ) {
+ mt->mt_rep_flags |= REP_NO_ENTRYDN;
+ }
+
+ BER_BVZERO( &mapped );
+ ldap_back_map( &mt->mt_rwmap.rwm_at,
+ &slap_schema.si_ad_subschemaSubentry->ad_cname, &mapped,
+ BACKLDAP_REMAP );
+ if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0' ) {
+ mt->mt_rep_flags |= REP_NO_SUBSCHEMA;
+ }
+
+ return 0;
+}
+
+int
+meta_back_db_open(
+ Backend *be,
+ ConfigReply *cr )
+{
+ metainfo_t *mi = (metainfo_t *)be->be_private;
+ char msg[SLAP_TEXT_BUFLEN];
+
+ int i, rc;
+
+ if ( mi->mi_ntargets == 0 ) {
+ /* Dynamically added, nothing to check here until
+ * some targets get added
+ */
+ if ( slapMode & SLAP_SERVER_RUNNING )
+ return 0;
+
+ Debug( LDAP_DEBUG_ANY,
+ "meta_back_db_open: no targets defined\n" );
+ return 1;
+ }
+
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ metatarget_t *mt = mi->mi_targets[ i ];
+
+ if ( meta_target_finish( mi, mt,
+ "meta_back_db_open", msg, sizeof( msg )))
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * meta_back_conn_free()
+ *
+ * actually frees a connection; the reference count must be 0,
+ * and it must not (or no longer) be in the cache.
+ */
+void
+meta_back_conn_free(
+ void *v_mc )
+{
+ metaconn_t *mc = v_mc;
+ int ntargets;
+
+ assert( mc != NULL );
+ assert( mc->mc_refcnt == 0 );
+
+ /* at least one must be present... */
+ ntargets = mc->mc_info->mi_ntargets;
+ assert( ntargets > 0 );
+
+ for ( ; ntargets--; ) {
+ (void)meta_clear_one_candidate( NULL, mc, ntargets );
+ }
+
+ if ( !BER_BVISNULL( &mc->mc_local_ndn ) ) {
+ free( mc->mc_local_ndn.bv_val );
+ }
+
+ free( mc );
+}
+
+static void
+mapping_free(
+ void *v_mapping )
+{
+ struct ldapmapping *mapping = v_mapping;
+ ch_free( mapping->src.bv_val );
+ ch_free( mapping->dst.bv_val );
+ ch_free( mapping );
+}
+
+static void
+mapping_dst_free(
+ void *v_mapping )
+{
+ struct ldapmapping *mapping = v_mapping;
+
+ if ( BER_BVISEMPTY( &mapping->dst ) ) {
+ mapping_free( &mapping[ -1 ] );
+ }
+}
+
+void
+meta_back_map_free( struct ldapmap *lm )
+{
+ ldap_avl_free( lm->remap, mapping_dst_free );
+ ldap_avl_free( lm->map, mapping_free );
+ lm->remap = NULL;
+ lm->map = NULL;
+}
+
+static void
+target_free(
+ metatarget_t *mt )
+{
+ if ( mt->mt_uri ) {
+ free( mt->mt_uri );
+ ldap_pvt_thread_mutex_destroy( &mt->mt_uri_mutex );
+ }
+ if ( mt->mt_subtree ) {
+ meta_subtree_destroy( mt->mt_subtree );
+ mt->mt_subtree = NULL;
+ }
+ if ( mt->mt_filter ) {
+ meta_filter_destroy( mt->mt_filter );
+ mt->mt_filter = NULL;
+ }
+ if ( !BER_BVISNULL( &mt->mt_psuffix ) ) {
+ free( mt->mt_psuffix.bv_val );
+ }
+ if ( !BER_BVISNULL( &mt->mt_nsuffix ) ) {
+ free( mt->mt_nsuffix.bv_val );
+ }
+ if ( !BER_BVISNULL( &mt->mt_binddn ) ) {
+ free( mt->mt_binddn.bv_val );
+ }
+ if ( !BER_BVISNULL( &mt->mt_bindpw ) ) {
+ free( mt->mt_bindpw.bv_val );
+ }
+ if ( !BER_BVISNULL( &mt->mt_idassert_authcID ) ) {
+ ch_free( mt->mt_idassert_authcID.bv_val );
+ }
+ if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) ) {
+ ch_free( mt->mt_idassert_authcDN.bv_val );
+ }
+ if ( !BER_BVISNULL( &mt->mt_idassert_passwd ) ) {
+ ch_free( mt->mt_idassert_passwd.bv_val );
+ }
+ if ( !BER_BVISNULL( &mt->mt_idassert_authzID ) ) {
+ ch_free( mt->mt_idassert_authzID.bv_val );
+ }
+ if ( !BER_BVISNULL( &mt->mt_idassert_sasl_mech ) ) {
+ ch_free( mt->mt_idassert_sasl_mech.bv_val );
+ }
+ if ( !BER_BVISNULL( &mt->mt_idassert_sasl_realm ) ) {
+ ch_free( mt->mt_idassert_sasl_realm.bv_val );
+ }
+ if ( mt->mt_idassert_authz != NULL ) {
+ ber_bvarray_free( mt->mt_idassert_authz );
+ }
+ if ( mt->mt_rwmap.rwm_rw ) {
+ rewrite_info_delete( &mt->mt_rwmap.rwm_rw );
+ if ( mt->mt_rwmap.rwm_bva_rewrite )
+ ber_bvarray_free( mt->mt_rwmap.rwm_bva_rewrite );
+ }
+ meta_back_map_free( &mt->mt_rwmap.rwm_oc );
+ meta_back_map_free( &mt->mt_rwmap.rwm_at );
+ ber_bvarray_free( mt->mt_rwmap.rwm_bva_map );
+
+ free( mt );
+}
+
+int
+meta_back_db_destroy(
+ Backend *be,
+ ConfigReply *cr )
+{
+ metainfo_t *mi;
+
+ if ( be->be_private ) {
+ int i;
+
+ mi = ( metainfo_t * )be->be_private;
+
+ /*
+ * Destroy the connection tree
+ */
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+
+ if ( mi->mi_conninfo.lai_tree ) {
+ ldap_tavl_free( mi->mi_conninfo.lai_tree, meta_back_conn_free );
+ }
+ for ( i = LDAP_BACK_PCONN_FIRST; i < LDAP_BACK_PCONN_LAST; i++ ) {
+ while ( !LDAP_TAILQ_EMPTY( &mi->mi_conn_priv[ i ].mic_priv ) ) {
+ metaconn_t *mc = LDAP_TAILQ_FIRST( &mi->mi_conn_priv[ i ].mic_priv );
+
+ LDAP_TAILQ_REMOVE( &mi->mi_conn_priv[ i ].mic_priv, mc, mc_q );
+ meta_back_conn_free( mc );
+ }
+ }
+
+ /*
+ * Destroy the per-target stuff (assuming there's at
+ * least one ...)
+ */
+ if ( mi->mi_targets != NULL ) {
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ metatarget_t *mt = mi->mi_targets[ i ];
+
+ if ( META_BACK_TGT_QUARANTINE( mt ) ) {
+ if ( mt->mt_quarantine.ri_num != mi->mi_quarantine.ri_num )
+ {
+ mi->mi_ldap_extra->retry_info_destroy( &mt->mt_quarantine );
+ }
+
+ ldap_pvt_thread_mutex_destroy( &mt->mt_quarantine_mutex );
+ }
+
+ target_free( mt );
+ }
+
+ free( mi->mi_targets );
+ }
+
+ ldap_pvt_thread_mutex_lock( &mi->mi_cache.mutex );
+ if ( mi->mi_cache.tree ) {
+ ldap_avl_free( mi->mi_cache.tree, meta_dncache_free );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &mi->mi_cache.mutex );
+ ldap_pvt_thread_mutex_destroy( &mi->mi_cache.mutex );
+
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ ldap_pvt_thread_mutex_destroy( &mi->mi_conninfo.lai_mutex );
+
+ if ( mi->mi_candidates != NULL ) {
+ ber_memfree_x( mi->mi_candidates, NULL );
+ }
+
+ if ( META_BACK_QUARANTINE( mi ) ) {
+ mi->mi_ldap_extra->retry_info_destroy( &mi->mi_quarantine );
+ }
+ }
+
+ free( be->be_private );
+ return 0;
+}
+
+#if SLAPD_META == SLAPD_MOD_DYNAMIC
+
+/* conditionally define the init_module() function */
+SLAP_BACKEND_INIT_MODULE( meta )
+
+#endif /* SLAPD_META == SLAPD_MOD_DYNAMIC */
+
+
diff --git a/servers/slapd/back-meta/map.c b/servers/slapd/back-meta/map.c
new file mode 100644
index 0000000..6591341
--- /dev/null
+++ b/servers/slapd/back-meta/map.c
@@ -0,0 +1,924 @@
+/* map.c - ldap backend mapping routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+/* This is an altered version */
+/*
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Since few users ever read sources,
+ * credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software. Since few users
+ * ever read sources, credits should appear in the documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ *
+ *
+ *
+ * Copyright 2000, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+ *
+ * This software is being modified by Pierangelo Masarati.
+ * The previously reported conditions apply to the modified code as well.
+ * Changes in the original code are highlighted where required.
+ * Credits for the original code go to the author, Howard Chu.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "lutil.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+int
+mapping_cmp ( const void *c1, const void *c2 )
+{
+ struct ldapmapping *map1 = (struct ldapmapping *)c1;
+ struct ldapmapping *map2 = (struct ldapmapping *)c2;
+ int rc = map1->src.bv_len - map2->src.bv_len;
+ if (rc) return rc;
+ return ( strcasecmp( map1->src.bv_val, map2->src.bv_val ) );
+}
+
+int
+mapping_dup ( void *c1, void *c2 )
+{
+ struct ldapmapping *map1 = (struct ldapmapping *)c1;
+ struct ldapmapping *map2 = (struct ldapmapping *)c2;
+
+ return ( ( strcasecmp( map1->src.bv_val, map2->src.bv_val ) == 0 ) ? -1 : 0 );
+}
+
+void
+ldap_back_map_init ( struct ldapmap *lm, struct ldapmapping **m )
+{
+ struct ldapmapping *mapping;
+
+ assert( m != NULL );
+
+ *m = NULL;
+
+ mapping = (struct ldapmapping *)ch_calloc( 2,
+ sizeof( struct ldapmapping ) );
+ if ( mapping == NULL ) {
+ return;
+ }
+
+ ber_str2bv( "objectclass", STRLENOF("objectclass"), 1, &mapping[0].src);
+ ber_dupbv( &mapping[0].dst, &mapping[0].src );
+ mapping[1].src = mapping[0].src;
+ mapping[1].dst = mapping[0].dst;
+
+ ldap_avl_insert( &lm->map, (caddr_t)&mapping[0],
+ mapping_cmp, mapping_dup );
+ ldap_avl_insert( &lm->remap, (caddr_t)&mapping[1],
+ mapping_cmp, mapping_dup );
+ *m = mapping;
+}
+
+int
+ldap_back_mapping ( struct ldapmap *map, struct berval *s, struct ldapmapping **m,
+ int remap )
+{
+ Avlnode *tree;
+ struct ldapmapping fmapping;
+
+ assert( m != NULL );
+
+ /* let special attrnames slip through (ITS#5760) */
+ if ( bvmatch( s, slap_bv_no_attrs )
+ || bvmatch( s, slap_bv_all_user_attrs )
+ || bvmatch( s, slap_bv_all_operational_attrs ) )
+ {
+ *m = NULL;
+ return 0;
+ }
+
+ if ( remap == BACKLDAP_REMAP ) {
+ tree = map->remap;
+
+ } else {
+ tree = map->map;
+ }
+
+ fmapping.src = *s;
+ *m = (struct ldapmapping *)ldap_avl_find( tree, (caddr_t)&fmapping, mapping_cmp );
+ if ( *m == NULL ) {
+ return map->drop_missing;
+ }
+
+ return 0;
+}
+
+void
+ldap_back_map ( struct ldapmap *map, struct berval *s, struct berval *bv,
+ int remap )
+{
+ struct ldapmapping *mapping;
+ int drop_missing;
+
+ /* map->map may be NULL when mapping is configured,
+ * but map->remap can't */
+ if ( map->remap == NULL ) {
+ *bv = *s;
+ return;
+ }
+
+ BER_BVZERO( bv );
+ drop_missing = ldap_back_mapping( map, s, &mapping, remap );
+ if ( mapping != NULL ) {
+ if ( !BER_BVISNULL( &mapping->dst ) ) {
+ *bv = mapping->dst;
+ }
+ return;
+ }
+
+ if ( !drop_missing ) {
+ *bv = *s;
+ }
+}
+
+int
+ldap_back_map_attrs(
+ Operation *op,
+ struct ldapmap *at_map,
+ AttributeName *an,
+ int remap,
+ char ***mapped_attrs )
+{
+ int i, x, j;
+ char **na;
+ struct berval mapped;
+
+ if ( an == NULL && op->o_bd->be_extra_anlist == NULL ) {
+ *mapped_attrs = NULL;
+ return LDAP_SUCCESS;
+ }
+
+ i = 0;
+ if ( an != NULL ) {
+ for ( ; !BER_BVISNULL( &an[i].an_name ); i++ )
+ /* */ ;
+ }
+
+ x = 0;
+ if ( op->o_bd->be_extra_anlist != NULL ) {
+ for ( ; !BER_BVISNULL( &op->o_bd->be_extra_anlist[x].an_name ); x++ )
+ /* */ ;
+ }
+
+ assert( i > 0 || x > 0 );
+
+ na = (char **)ber_memcalloc_x( i + x + 1, sizeof(char *), op->o_tmpmemctx );
+ if ( na == NULL ) {
+ *mapped_attrs = NULL;
+ return LDAP_NO_MEMORY;
+ }
+
+ j = 0;
+ if ( i > 0 ) {
+ for ( i = 0; !BER_BVISNULL( &an[i].an_name ); i++ ) {
+ ldap_back_map( at_map, &an[i].an_name, &mapped, remap );
+ if ( !BER_BVISNULL( &mapped ) && !BER_BVISEMPTY( &mapped ) ) {
+ na[j++] = mapped.bv_val;
+ }
+ }
+ }
+
+ if ( x > 0 ) {
+ for ( x = 0; !BER_BVISNULL( &op->o_bd->be_extra_anlist[x].an_name ); x++ ) {
+ if ( op->o_bd->be_extra_anlist[x].an_desc &&
+ ad_inlist( op->o_bd->be_extra_anlist[x].an_desc, an ) )
+ {
+ continue;
+ }
+
+ ldap_back_map( at_map, &op->o_bd->be_extra_anlist[x].an_name, &mapped, remap );
+ if ( !BER_BVISNULL( &mapped ) && !BER_BVISEMPTY( &mapped ) ) {
+ na[j++] = mapped.bv_val;
+ }
+ }
+ }
+
+ if ( j == 0 && ( i > 0 || x > 0 ) ) {
+ na[j++] = LDAP_NO_ATTRS;
+ }
+ na[j] = NULL;
+
+ *mapped_attrs = na;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+map_attr_value(
+ dncookie *dc,
+ AttributeDescription *ad,
+ struct berval *mapped_attr,
+ struct berval *value,
+ struct berval *mapped_value,
+ int remap,
+ void *memctx )
+{
+ struct berval vtmp;
+ int freeval = 0;
+
+ ldap_back_map( &dc->target->mt_rwmap.rwm_at, &ad->ad_cname, mapped_attr, remap );
+ if ( BER_BVISNULL( mapped_attr ) || BER_BVISEMPTY( mapped_attr ) ) {
+#if 0
+ /*
+ * FIXME: are we sure we need to search oc_map if at_map fails?
+ */
+ ldap_back_map( &dc->target->mt_rwmap.rwm_oc, &ad->ad_cname, mapped_attr, remap );
+ if ( BER_BVISNULL( mapped_attr ) || BER_BVISEMPTY( mapped_attr ) ) {
+ *mapped_attr = ad->ad_cname;
+ }
+#endif
+ if ( dc->target->mt_rwmap.rwm_at.drop_missing ) {
+ return -1;
+ }
+
+ *mapped_attr = ad->ad_cname;
+ }
+
+ if ( value == NULL ) {
+ return 0;
+ }
+
+ if ( ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName )
+ {
+ dncookie fdc = *dc;
+
+ fdc.ctx = "searchFilterAttrDN";
+
+ switch ( ldap_back_dn_massage( &fdc, value, &vtmp ) ) {
+ case LDAP_SUCCESS:
+ if ( vtmp.bv_val != value->bv_val ) {
+ freeval = 1;
+ }
+ break;
+
+ case LDAP_UNWILLING_TO_PERFORM:
+ return -1;
+
+ case LDAP_OTHER:
+ return -1;
+ }
+
+ } else if ( ad->ad_type->sat_equality &&
+ ad->ad_type->sat_equality->smr_usage & SLAP_MR_MUTATION_NORMALIZER )
+ {
+ if ( ad->ad_type->sat_equality->smr_normalize(
+ (SLAP_MR_DENORMALIZE|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX),
+ NULL, NULL, value, &vtmp, memctx ) )
+ {
+ return -1;
+ }
+ freeval = 2;
+
+ } else if ( ad == slap_schema.si_ad_objectClass || ad == slap_schema.si_ad_structuralObjectClass ) {
+ ldap_back_map( &dc->target->mt_rwmap.rwm_oc, value, &vtmp, remap );
+ if ( BER_BVISNULL( &vtmp ) || BER_BVISEMPTY( &vtmp ) ) {
+ vtmp = *value;
+ }
+
+ } else {
+ vtmp = *value;
+ }
+
+ filter_escape_value_x( &vtmp, mapped_value, memctx );
+
+ switch ( freeval ) {
+ case 1:
+ ber_memfree( vtmp.bv_val );
+ break;
+ case 2:
+ ber_memfree_x( vtmp.bv_val, memctx );
+ break;
+ }
+
+ return 0;
+}
+
+static int
+ldap_back_int_filter_map_rewrite(
+ dncookie *dc,
+ Filter *f,
+ struct berval *fstr,
+ int remap,
+ void *memctx )
+{
+ int i;
+ Filter *p;
+ struct berval atmp,
+ vtmp,
+ *tmp;
+ static struct berval
+ /* better than nothing... */
+ ber_bvfalse = BER_BVC( "(!(objectClass=*))" ),
+ ber_bvtf_false = BER_BVC( "(|)" ),
+ /* better than nothing... */
+ ber_bvtrue = BER_BVC( "(objectClass=*)" ),
+ ber_bvtf_true = BER_BVC( "(&)" ),
+#if 0
+ /* no longer needed; preserved for completeness */
+ ber_bvundefined = BER_BVC( "(?=undefined)" ),
+#endif
+ ber_bverror = BER_BVC( "(?=error)" ),
+ ber_bvunknown = BER_BVC( "(?=unknown)" ),
+ ber_bvnone = BER_BVC( "(?=none)" );
+ ber_len_t len;
+
+ assert( fstr != NULL );
+ BER_BVZERO( fstr );
+
+ if ( f == NULL ) {
+ ber_dupbv_x( fstr, &ber_bvnone, memctx );
+ return LDAP_OTHER;
+ }
+
+ switch ( ( f->f_choice & SLAPD_FILTER_MASK ) ) {
+ case LDAP_FILTER_EQUALITY:
+ if ( map_attr_value( dc, f->f_av_desc, &atmp,
+ &f->f_av_value, &vtmp, remap, memctx ) )
+ {
+ goto computed;
+ }
+
+ fstr->bv_len = atmp.bv_len + vtmp.bv_len
+ + ( sizeof("(=)") - 1 );
+ fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
+ if ( !fstr->bv_val ) {
+ ber_memfree_x( vtmp.bv_val, memctx );
+ return LDAP_NO_MEMORY;
+ }
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=%s)",
+ atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
+
+ ber_memfree_x( vtmp.bv_val, memctx );
+ break;
+
+ case LDAP_FILTER_GE:
+ if ( map_attr_value( dc, f->f_av_desc, &atmp,
+ &f->f_av_value, &vtmp, remap, memctx ) )
+ {
+ goto computed;
+ }
+
+ fstr->bv_len = atmp.bv_len + vtmp.bv_len
+ + ( sizeof("(>=)") - 1 );
+ fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
+ if ( !fstr->bv_val ) {
+ ber_memfree_x( vtmp.bv_val, memctx );
+ return LDAP_NO_MEMORY;
+ }
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s>=%s)",
+ atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
+
+ ber_memfree_x( vtmp.bv_val, memctx );
+ break;
+
+ case LDAP_FILTER_LE:
+ if ( map_attr_value( dc, f->f_av_desc, &atmp,
+ &f->f_av_value, &vtmp, remap, memctx ) )
+ {
+ goto computed;
+ }
+
+ fstr->bv_len = atmp.bv_len + vtmp.bv_len
+ + ( sizeof("(<=)") - 1 );
+ fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
+ if ( !fstr->bv_val ) {
+ ber_memfree_x( vtmp.bv_val, memctx );
+ return LDAP_NO_MEMORY;
+ }
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s<=%s)",
+ atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
+
+ ber_memfree_x( vtmp.bv_val, memctx );
+ break;
+
+ case LDAP_FILTER_APPROX:
+ if ( map_attr_value( dc, f->f_av_desc, &atmp,
+ &f->f_av_value, &vtmp, remap, memctx ) )
+ {
+ goto computed;
+ }
+
+ fstr->bv_len = atmp.bv_len + vtmp.bv_len
+ + ( sizeof("(~=)") - 1 );
+ fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
+ if ( !fstr->bv_val ) {
+ ber_memfree_x( vtmp.bv_val, memctx );
+ return LDAP_NO_MEMORY;
+ }
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s~=%s)",
+ atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
+
+ ber_memfree_x( vtmp.bv_val, memctx );
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ if ( map_attr_value( dc, f->f_sub_desc, &atmp,
+ NULL, NULL, remap, memctx ) )
+ {
+ goto computed;
+ }
+
+ /* cannot be a DN ... */
+
+ fstr->bv_len = atmp.bv_len + ( STRLENOF( "(=*)" ) );
+ fstr->bv_val = ber_memalloc_x( fstr->bv_len + 128, memctx ); /* FIXME: why 128 ? */
+ if ( !fstr->bv_val ) {
+ return LDAP_NO_MEMORY;
+ }
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
+ atmp.bv_val );
+
+ if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
+ char *tmp;
+
+ len = fstr->bv_len;
+
+ filter_escape_value_x( &f->f_sub_initial, &vtmp, memctx );
+
+ fstr->bv_len += vtmp.bv_len;
+ tmp = ber_memrealloc_x( fstr->bv_val, fstr->bv_len + 1, memctx );
+ if ( !tmp ) {
+ ber_memfree_x( vtmp.bv_val, memctx );
+ return LDAP_NO_MEMORY;
+ }
+ fstr->bv_val = tmp;
+
+ snprintf( &fstr->bv_val[len - 2], vtmp.bv_len + 3,
+ /* "(attr=" */ "%s*)",
+ vtmp.bv_len ? vtmp.bv_val : "" );
+
+ ber_memfree_x( vtmp.bv_val, memctx );
+ }
+
+ if ( f->f_sub_any != NULL ) {
+ for ( i = 0; !BER_BVISNULL( &f->f_sub_any[i] ); i++ ) {
+ char *tmp;
+
+ len = fstr->bv_len;
+ filter_escape_value_x( &f->f_sub_any[i], &vtmp, memctx );
+
+ fstr->bv_len += vtmp.bv_len + 1;
+ tmp = ber_memrealloc_x( fstr->bv_val, fstr->bv_len + 1, memctx );
+ if ( !tmp ) {
+ ber_memfree_x( vtmp.bv_val, memctx );
+ return LDAP_NO_MEMORY;
+ }
+ fstr->bv_val = tmp;
+
+ snprintf( &fstr->bv_val[len - 1], vtmp.bv_len + 3,
+ /* "(attr=[init]*[any*]" */ "%s*)",
+ vtmp.bv_len ? vtmp.bv_val : "" );
+ ber_memfree_x( vtmp.bv_val, memctx );
+ }
+ }
+
+ if ( !BER_BVISNULL( &f->f_sub_final ) ) {
+ char *tmp;
+
+ len = fstr->bv_len;
+
+ filter_escape_value_x( &f->f_sub_final, &vtmp, memctx );
+
+ fstr->bv_len += vtmp.bv_len;
+ tmp = ber_memrealloc_x( fstr->bv_val, fstr->bv_len + 1, memctx );
+ if ( !tmp ) {
+ ber_memfree_x( vtmp.bv_val, memctx );
+ return LDAP_NO_MEMORY;
+ }
+ fstr->bv_val = tmp;
+
+ snprintf( &fstr->bv_val[len - 1], vtmp.bv_len + 3,
+ /* "(attr=[init*][any*]" */ "%s)",
+ vtmp.bv_len ? vtmp.bv_val : "" );
+
+ ber_memfree_x( vtmp.bv_val, memctx );
+ }
+
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ if ( map_attr_value( dc, f->f_desc, &atmp,
+ NULL, NULL, remap, memctx ) )
+ {
+ goto computed;
+ }
+
+ fstr->bv_len = atmp.bv_len + ( STRLENOF( "(=*)" ) );
+ fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
+ if ( !fstr->bv_val ) {
+ return LDAP_NO_MEMORY;
+ }
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
+ atmp.bv_val );
+ break;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ fstr->bv_len = STRLENOF( "(%)" );
+ fstr->bv_val = ber_memalloc_x( fstr->bv_len + 128, memctx ); /* FIXME: why 128? */
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%c)",
+ f->f_choice == LDAP_FILTER_AND ? '&' :
+ f->f_choice == LDAP_FILTER_OR ? '|' : '!' );
+
+ for ( p = f->f_list; p != NULL; p = p->f_next ) {
+ int rc;
+
+ len = fstr->bv_len;
+
+ rc = ldap_back_int_filter_map_rewrite( dc, p, &vtmp, remap, memctx );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ fstr->bv_len += vtmp.bv_len;
+ fstr->bv_val = ber_memrealloc_x( fstr->bv_val, fstr->bv_len + 1, memctx );
+ if ( !fstr->bv_val ) {
+ ber_memfree_x( vtmp.bv_val, memctx );
+ return LDAP_NO_MEMORY;
+ }
+
+ snprintf( &fstr->bv_val[len-1], vtmp.bv_len + 2,
+ /*"("*/ "%s)", vtmp.bv_len ? vtmp.bv_val : "" );
+
+ ber_memfree_x( vtmp.bv_val, memctx );
+ }
+
+ break;
+
+ case LDAP_FILTER_EXT:
+ if ( f->f_mr_desc ) {
+ if ( map_attr_value( dc, f->f_mr_desc, &atmp,
+ &f->f_mr_value, &vtmp, remap, memctx ) )
+ {
+ goto computed;
+ }
+
+ } else {
+ BER_BVSTR( &atmp, "" );
+ filter_escape_value_x( &f->f_mr_value, &vtmp, memctx );
+ }
+
+ /* FIXME: cleanup (less ?: operators...) */
+ fstr->bv_len = atmp.bv_len +
+ ( f->f_mr_dnattrs ? STRLENOF( ":dn" ) : 0 ) +
+ ( !BER_BVISEMPTY( &f->f_mr_rule_text ) ? f->f_mr_rule_text.bv_len + 1 : 0 ) +
+ vtmp.bv_len + ( STRLENOF( "(:=)" ) );
+ fstr->bv_val = ber_memalloc_x( fstr->bv_len + 1, memctx );
+ if ( !fstr->bv_val ) {
+ ber_memfree_x( vtmp.bv_val, memctx );
+ return LDAP_NO_MEMORY;
+ }
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s%s%s:=%s)",
+ atmp.bv_val,
+ f->f_mr_dnattrs ? ":dn" : "",
+ !BER_BVISEMPTY( &f->f_mr_rule_text ) ? ":" : "",
+ !BER_BVISEMPTY( &f->f_mr_rule_text ) ? f->f_mr_rule_text.bv_val : "",
+ vtmp.bv_len ? vtmp.bv_val : "" );
+ ber_memfree_x( vtmp.bv_val, memctx );
+ break;
+
+ case SLAPD_FILTER_COMPUTED:
+ switch ( f->f_result ) {
+ /* FIXME: treat UNDEFINED as FALSE */
+ case SLAPD_COMPARE_UNDEFINED:
+computed:;
+ if ( META_BACK_TGT_NOUNDEFFILTER( dc->target ) ) {
+ return LDAP_COMPARE_FALSE;
+ }
+ /* fallthru */
+
+ case LDAP_COMPARE_FALSE:
+ if ( META_BACK_TGT_T_F( dc->target ) ) {
+ tmp = &ber_bvtf_false;
+ break;
+ }
+ tmp = &ber_bvfalse;
+ break;
+
+ case LDAP_COMPARE_TRUE:
+ if ( META_BACK_TGT_T_F( dc->target ) ) {
+ tmp = &ber_bvtf_true;
+ break;
+ }
+
+ tmp = &ber_bvtrue;
+ break;
+
+ default:
+ tmp = &ber_bverror;
+ break;
+ }
+
+ ber_dupbv_x( fstr, tmp, memctx );
+ break;
+
+ default:
+ ber_dupbv_x( fstr, &ber_bvunknown, memctx );
+ break;
+ }
+
+ return 0;
+}
+
+int
+ldap_back_filter_map_rewrite(
+ dncookie *dc,
+ Filter *f,
+ struct berval *fstr,
+ int remap,
+ void *memctx )
+{
+ int rc;
+ dncookie fdc;
+ struct berval ftmp;
+ static char *dmy = "";
+
+ rc = ldap_back_int_filter_map_rewrite( dc, f, fstr, remap, memctx );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ fdc = *dc;
+ ftmp = *fstr;
+
+ fdc.ctx = "searchFilter";
+
+ switch ( rewrite_session( fdc.target->mt_rwmap.rwm_rw, fdc.ctx,
+ ( !BER_BVISEMPTY( &ftmp ) ? ftmp.bv_val : dmy ),
+ fdc.conn, &fstr->bv_val ) )
+ {
+ case REWRITE_REGEXEC_OK:
+ if ( !BER_BVISNULL( fstr ) ) {
+ fstr->bv_len = strlen( fstr->bv_val );
+
+ } else {
+ *fstr = ftmp;
+ }
+ Debug( LDAP_DEBUG_ARGS,
+ "[rw] %s: \"%s\" -> \"%s\"\n",
+ fdc.ctx, BER_BVISNULL( &ftmp ) ? "" : ftmp.bv_val,
+ BER_BVISNULL( fstr ) ? "" : fstr->bv_val );
+ rc = LDAP_SUCCESS;
+ break;
+
+ case REWRITE_REGEXEC_UNWILLING:
+ if ( fdc.rs ) {
+ fdc.rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ fdc.rs->sr_text = "Operation not allowed";
+ }
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ break;
+
+ case REWRITE_REGEXEC_ERR:
+ if ( fdc.rs ) {
+ fdc.rs->sr_err = LDAP_OTHER;
+ fdc.rs->sr_text = "Rewrite error";
+ }
+ rc = LDAP_OTHER;
+ break;
+ }
+
+ if ( fstr->bv_val == dmy ) {
+ BER_BVZERO( fstr );
+
+ } else if ( fstr->bv_val != ftmp.bv_val ) {
+ /* NOTE: need to realloc mapped filter on slab
+ * and free the original one, until librewrite
+ * becomes slab-aware
+ */
+ ber_dupbv_x( &ftmp, fstr, memctx );
+ ch_free( fstr->bv_val );
+ *fstr = ftmp;
+ }
+
+ return rc;
+}
+
+int
+ldap_back_referral_result_rewrite(
+ dncookie *dc,
+ BerVarray a_vals,
+ void *memctx
+)
+{
+ int i, last;
+
+ assert( dc != NULL );
+ assert( a_vals != NULL );
+
+ for ( last = 0; !BER_BVISNULL( &a_vals[ last ] ); last++ )
+ ;
+ last--;
+
+ for ( i = 0; !BER_BVISNULL( &a_vals[ i ] ); i++ ) {
+ struct berval dn,
+ olddn = BER_BVNULL;
+ int rc;
+ LDAPURLDesc *ludp;
+
+ rc = ldap_url_parse( a_vals[ i ].bv_val, &ludp );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ /* leave attr untouched if massage failed */
+ continue;
+ }
+
+ /* FIXME: URLs like "ldap:///dc=suffix" if passed
+ * thru ldap_url_parse() and ldap_url_desc2str()
+ * get rewritten as "ldap:///dc=suffix??base";
+ * we don't want this to occur... */
+ if ( ludp->lud_scope == LDAP_SCOPE_BASE ) {
+ ludp->lud_scope = LDAP_SCOPE_DEFAULT;
+ }
+
+ ber_str2bv( ludp->lud_dn, 0, 0, &olddn );
+
+ rc = ldap_back_dn_massage( dc, &olddn, &dn );
+ switch ( rc ) {
+ case LDAP_UNWILLING_TO_PERFORM:
+ /*
+ * FIXME: need to check if it may be considered
+ * legal to trim values when adding/modifying;
+ * it should be when searching (e.g. ACLs).
+ */
+ ber_memfree( a_vals[ i ].bv_val );
+ if ( last > i ) {
+ a_vals[ i ] = a_vals[ last ];
+ }
+ BER_BVZERO( &a_vals[ last ] );
+ last--;
+ i--;
+ break;
+
+ default:
+ /* leave attr untouched if massage failed */
+ if ( !BER_BVISNULL( &dn ) && olddn.bv_val != dn.bv_val )
+ {
+ char *newurl;
+
+ ludp->lud_dn = dn.bv_val;
+ newurl = ldap_url_desc2str( ludp );
+ free( dn.bv_val );
+ if ( newurl == NULL ) {
+ /* FIXME: leave attr untouched
+ * even if ldap_url_desc2str failed...
+ */
+ break;
+ }
+
+ ber_memfree_x( a_vals[ i ].bv_val, memctx );
+ ber_str2bv_x( newurl, 0, 1, &a_vals[ i ], memctx );
+ ber_memfree( newurl );
+ ludp->lud_dn = olddn.bv_val;
+ }
+ break;
+ }
+
+ ldap_free_urldesc( ludp );
+ }
+
+ return 0;
+}
+
+/*
+ * I don't like this much, but we need two different
+ * functions because different heap managers may be
+ * in use in back-ldap/meta to reduce the amount of
+ * calls to malloc routines, and some of the free()
+ * routines may be macros with args
+ */
+int
+ldap_dnattr_rewrite(
+ dncookie *dc,
+ BerVarray a_vals
+)
+{
+ struct berval bv;
+ int i, last;
+
+ assert( a_vals != NULL );
+
+ for ( last = 0; !BER_BVISNULL( &a_vals[last] ); last++ )
+ ;
+ last--;
+
+ for ( i = 0; !BER_BVISNULL( &a_vals[i] ); i++ ) {
+ switch ( ldap_back_dn_massage( dc, &a_vals[i], &bv ) ) {
+ case LDAP_UNWILLING_TO_PERFORM:
+ /*
+ * FIXME: need to check if it may be considered
+ * legal to trim values when adding/modifying;
+ * it should be when searching (e.g. ACLs).
+ */
+ ch_free( a_vals[i].bv_val );
+ if ( last > i ) {
+ a_vals[i] = a_vals[last];
+ }
+ BER_BVZERO( &a_vals[last] );
+ last--;
+ break;
+
+ default:
+ /* leave attr untouched if massage failed */
+ if ( !BER_BVISNULL( &bv ) && bv.bv_val != a_vals[i].bv_val ) {
+ ch_free( a_vals[i].bv_val );
+ a_vals[i] = bv;
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int
+ldap_dnattr_result_rewrite(
+ dncookie *dc,
+ BerVarray a_vals
+)
+{
+ struct berval bv;
+ int i, last;
+
+ assert( a_vals != NULL );
+
+ for ( last = 0; !BER_BVISNULL( &a_vals[last] ); last++ )
+ ;
+ last--;
+
+ for ( i = 0; !BER_BVISNULL( &a_vals[i] ); i++ ) {
+ switch ( ldap_back_dn_massage( dc, &a_vals[i], &bv ) ) {
+ case LDAP_UNWILLING_TO_PERFORM:
+ /*
+ * FIXME: need to check if it may be considered
+ * legal to trim values when adding/modifying;
+ * it should be when searching (e.g. ACLs).
+ */
+ ber_memfree( a_vals[i].bv_val );
+ if ( last > i ) {
+ a_vals[i] = a_vals[last];
+ }
+ BER_BVZERO( &a_vals[last] );
+ last--;
+ break;
+
+ default:
+ /* leave attr untouched if massage failed */
+ if ( !BER_BVISNULL( &bv ) && a_vals[i].bv_val != bv.bv_val ) {
+ ber_memfree( a_vals[i].bv_val );
+ a_vals[i] = bv;
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
diff --git a/servers/slapd/back-meta/modify.c b/servers/slapd/back-meta/modify.c
new file mode 100644
index 0000000..0a78e76
--- /dev/null
+++ b/servers/slapd/back-meta/modify.c
@@ -0,0 +1,221 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+int
+meta_back_modify( Operation *op, SlapReply *rs )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ metatarget_t *mt;
+ metaconn_t *mc;
+ int rc = 0;
+ LDAPMod **modv = NULL;
+ LDAPMod *mods = NULL;
+ Modifications *ml;
+ int candidate = -1, i;
+ int isupdate;
+ struct berval mdn = BER_BVNULL;
+ struct berval mapped;
+ dncookie dc;
+ int msgid;
+ ldap_back_send_t retrying = LDAP_BACK_RETRYING;
+ LDAPControl **ctrls = NULL;
+
+ mc = meta_back_getconn( op, rs, &candidate, LDAP_BACK_SENDERR );
+ if ( !mc || !meta_back_dobind( op, rs, mc, LDAP_BACK_SENDERR ) ) {
+ return rs->sr_err;
+ }
+
+ assert( mc->mc_conns[ candidate ].msc_ld != NULL );
+
+ /*
+ * Rewrite the modify dn, if needed
+ */
+ mt = mi->mi_targets[ candidate ];
+ dc.target = mt;
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+ dc.ctx = "modifyDN";
+
+ if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ for ( i = 0, ml = op->orm_modlist; ml; i++ ,ml = ml->sml_next )
+ ;
+
+ mods = ch_malloc( sizeof( LDAPMod )*i );
+ if ( mods == NULL ) {
+ rs->sr_err = LDAP_OTHER;
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+ modv = ( LDAPMod ** )ch_malloc( ( i + 1 )*sizeof( LDAPMod * ) );
+ if ( modv == NULL ) {
+ rs->sr_err = LDAP_OTHER;
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ dc.ctx = "modifyAttrDN";
+ isupdate = be_shadow_update( op );
+ for ( i = 0, ml = op->orm_modlist; ml; ml = ml->sml_next ) {
+ int j, is_oc = 0;
+
+ if ( !isupdate && !get_relax( op ) && ml->sml_desc->ad_type->sat_no_user_mod )
+ {
+ continue;
+ }
+
+ if ( ml->sml_desc == slap_schema.si_ad_objectClass
+ || ml->sml_desc == slap_schema.si_ad_structuralObjectClass )
+ {
+ is_oc = 1;
+ mapped = ml->sml_desc->ad_cname;
+
+ } else {
+ ldap_back_map( &mt->mt_rwmap.rwm_at,
+ &ml->sml_desc->ad_cname, &mapped,
+ BACKLDAP_MAP );
+ if ( BER_BVISNULL( &mapped ) || BER_BVISEMPTY( &mapped ) ) {
+ continue;
+ }
+ }
+
+ modv[ i ] = &mods[ i ];
+ mods[ i ].mod_op = ml->sml_op | LDAP_MOD_BVALUES;
+ mods[ i ].mod_type = mapped.bv_val;
+
+ /*
+ * FIXME: dn-valued attrs should be rewritten
+ * to allow their use in ACLs at the back-ldap
+ * level.
+ */
+ if ( ml->sml_values != NULL ) {
+ if ( is_oc ) {
+ for ( j = 0; !BER_BVISNULL( &ml->sml_values[ j ] ); j++ )
+ ;
+ mods[ i ].mod_bvalues =
+ (struct berval **)ch_malloc( ( j + 1 ) *
+ sizeof( struct berval * ) );
+ for ( j = 0; !BER_BVISNULL( &ml->sml_values[ j ] ); ) {
+ struct ldapmapping *mapping;
+
+ ldap_back_mapping( &mt->mt_rwmap.rwm_oc,
+ &ml->sml_values[ j ], &mapping, BACKLDAP_MAP );
+
+ if ( mapping == NULL ) {
+ if ( mt->mt_rwmap.rwm_oc.drop_missing ) {
+ continue;
+ }
+ mods[ i ].mod_bvalues[ j ] = &ml->sml_values[ j ];
+
+ } else {
+ mods[ i ].mod_bvalues[ j ] = &mapping->dst;
+ }
+ j++;
+ }
+ mods[ i ].mod_bvalues[ j ] = NULL;
+
+ } else {
+ if ( ml->sml_desc->ad_type->sat_syntax ==
+ slap_schema.si_syn_distinguishedName )
+ {
+ ( void )ldap_dnattr_rewrite( &dc, ml->sml_values );
+ if ( ml->sml_values == NULL ) {
+ continue;
+ }
+ }
+
+ for ( j = 0; !BER_BVISNULL( &ml->sml_values[ j ] ); j++ )
+ ;
+ mods[ i ].mod_bvalues =
+ (struct berval **)ch_malloc( ( j + 1 ) *
+ sizeof( struct berval * ) );
+ for ( j = 0; !BER_BVISNULL( &ml->sml_values[ j ] ); j++ ) {
+ mods[ i ].mod_bvalues[ j ] = &ml->sml_values[ j ];
+ }
+ mods[ i ].mod_bvalues[ j ] = NULL;
+ }
+
+ } else {
+ mods[ i ].mod_bvalues = NULL;
+ }
+
+ i++;
+ }
+ modv[ i ] = 0;
+
+retry:;
+ ctrls = op->o_ctrls;
+ rc = meta_back_controls_add( op, rs, mc, candidate, &ctrls );
+ if ( rc != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ rs->sr_err = ldap_modify_ext( mc->mc_conns[ candidate ].msc_ld, mdn.bv_val,
+ modv, ctrls, NULL, &msgid );
+ rs->sr_err = meta_back_op_result( mc, op, rs, candidate, msgid,
+ mt->mt_timeout[ SLAP_OP_MODIFY ], ( LDAP_BACK_SENDRESULT | retrying ) );
+ if ( rs->sr_err == LDAP_UNAVAILABLE && retrying ) {
+ retrying &= ~LDAP_BACK_RETRYING;
+ if ( meta_back_retry( op, rs, &mc, candidate, LDAP_BACK_SENDERR ) ) {
+ /* if the identity changed, there might be need to re-authz */
+ (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+ goto retry;
+ }
+ }
+
+cleanup:;
+ (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+
+ if ( mdn.bv_val != op->o_req_dn.bv_val ) {
+ free( mdn.bv_val );
+ BER_BVZERO( &mdn );
+ }
+ if ( modv != NULL ) {
+ for ( i = 0; modv[ i ]; i++ ) {
+ free( modv[ i ]->mod_bvalues );
+ }
+ }
+ free( mods );
+ free( modv );
+
+ if ( mc ) {
+ meta_back_release_conn( mi, mc );
+ }
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/back-meta/modrdn.c b/servers/slapd/back-meta/modrdn.c
new file mode 100644
index 0000000..cf6e1be
--- /dev/null
+++ b/servers/slapd/back-meta/modrdn.c
@@ -0,0 +1,177 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+int
+meta_back_modrdn( Operation *op, SlapReply *rs )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ metatarget_t *mt;
+ metaconn_t *mc;
+ int candidate = -1;
+ struct berval mdn = BER_BVNULL,
+ mnewSuperior = BER_BVNULL;
+ dncookie dc;
+ int msgid;
+ ldap_back_send_t retrying = LDAP_BACK_RETRYING;
+ LDAPControl **ctrls = NULL;
+ struct berval newrdn = BER_BVNULL;
+
+ mc = meta_back_getconn( op, rs, &candidate, LDAP_BACK_SENDERR );
+ if ( !mc || !meta_back_dobind( op, rs, mc, LDAP_BACK_SENDERR ) ) {
+ return rs->sr_err;
+ }
+
+ assert( mc->mc_conns[ candidate ].msc_ld != NULL );
+
+ mt = mi->mi_targets[ candidate ];
+ dc.target = mt;
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+
+ if ( op->orr_newSup ) {
+
+ /*
+ * NOTE: the newParent, if defined, must be on the
+ * same target as the entry to be renamed. This check
+ * has been anticipated in meta_back_getconn()
+ */
+ /*
+ * FIXME: one possibility is to delete the entry
+ * from one target and add it to the other;
+ * unfortunately we'd need write access to both,
+ * which is nearly impossible; for administration
+ * needs, the rootdn of the metadirectory could
+ * be mapped to an administrative account on each
+ * target (the binddn?); we'll see.
+ */
+ /*
+ * NOTE: we need to port the identity assertion
+ * feature from back-ldap
+ */
+
+ /* needs LDAPv3 */
+ switch ( mt->mt_version ) {
+ case LDAP_VERSION3:
+ break;
+
+ case 0:
+ if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
+ break;
+ }
+ /* fall thru */
+
+ default:
+ /* op->o_protocol cannot be anything but LDAPv3,
+ * otherwise wouldn't be here */
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ /*
+ * Rewrite the new superior, if defined and required
+ */
+ dc.ctx = "newSuperiorDN";
+ if ( ldap_back_dn_massage( &dc, op->orr_newSup, &mnewSuperior ) ) {
+ rs->sr_err = LDAP_OTHER;
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Rewrite the modrdn dn, if required
+ */
+ dc.ctx = "modrDN";
+ if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
+ rs->sr_err = LDAP_OTHER;
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ /* NOTE: we need to copy the newRDN in case it was formed
+ * from a DN by simply changing the length (ITS#5397) */
+ newrdn = op->orr_newrdn;
+ if ( newrdn.bv_val[ newrdn.bv_len ] != '\0' ) {
+ ber_dupbv_x( &newrdn, &op->orr_newrdn, op->o_tmpmemctx );
+ }
+
+retry:;
+ ctrls = op->o_ctrls;
+ if ( meta_back_controls_add( op, rs, mc, candidate, &ctrls ) != LDAP_SUCCESS )
+ {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ rs->sr_err = ldap_rename( mc->mc_conns[ candidate ].msc_ld,
+ mdn.bv_val, newrdn.bv_val,
+ mnewSuperior.bv_val, op->orr_deleteoldrdn,
+ ctrls, NULL, &msgid );
+ rs->sr_err = meta_back_op_result( mc, op, rs, candidate, msgid,
+ mt->mt_timeout[ SLAP_OP_MODRDN ], ( LDAP_BACK_SENDRESULT | retrying ) );
+ if ( rs->sr_err == LDAP_UNAVAILABLE && retrying ) {
+ retrying &= ~LDAP_BACK_RETRYING;
+ if ( meta_back_retry( op, rs, &mc, candidate, LDAP_BACK_SENDERR ) ) {
+ /* if the identity changed, there might be need to re-authz */
+ (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+ goto retry;
+ }
+ }
+
+cleanup:;
+ (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+
+ if ( mdn.bv_val != op->o_req_dn.bv_val ) {
+ free( mdn.bv_val );
+ BER_BVZERO( &mdn );
+ }
+
+ if ( !BER_BVISNULL( &mnewSuperior )
+ && mnewSuperior.bv_val != op->orr_newSup->bv_val )
+ {
+ free( mnewSuperior.bv_val );
+ BER_BVZERO( &mnewSuperior );
+ }
+
+ if ( newrdn.bv_val != op->orr_newrdn.bv_val ) {
+ op->o_tmpfree( newrdn.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( mc ) {
+ meta_back_release_conn( mi, mc );
+ }
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/back-meta/proto-meta.h b/servers/slapd/back-meta/proto-meta.h
new file mode 100644
index 0000000..f6c16b2
--- /dev/null
+++ b/servers/slapd/back-meta/proto-meta.h
@@ -0,0 +1,54 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#ifndef PROTO_META_H
+#define PROTO_META_H
+
+LDAP_BEGIN_DECL
+
+extern BI_init meta_back_initialize;
+
+extern BI_open meta_back_open;
+extern BI_close meta_back_close;
+extern BI_destroy meta_back_destroy;
+
+extern BI_db_init meta_back_db_init;
+extern BI_db_open meta_back_db_open;
+extern BI_db_destroy meta_back_db_destroy;
+extern BI_db_config meta_back_db_config;
+
+extern BI_op_bind meta_back_bind;
+extern BI_op_search meta_back_search;
+extern BI_op_compare meta_back_compare;
+extern BI_op_modify meta_back_modify;
+extern BI_op_modrdn meta_back_modrdn;
+extern BI_op_add meta_back_add;
+extern BI_op_delete meta_back_delete;
+extern BI_op_abandon meta_back_abandon;
+
+extern BI_connection_destroy meta_back_conn_destroy;
+
+int meta_back_init_cf( BackendInfo *bi );
+
+LDAP_END_DECL
+
+#endif /* PROTO_META_H */
diff --git a/servers/slapd/back-meta/search.c b/servers/slapd/back-meta/search.c
new file mode 100644
index 0000000..cfd2f4b
--- /dev/null
+++ b/servers/slapd/back-meta/search.c
@@ -0,0 +1,2431 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "lutil.h"
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+#include "../../../libraries/liblber/lber-int.h"
+
+/* IGNORE means that target does not (no longer) participate
+ * in the search;
+ * NOTREADY means the search on that target has not been initialized yet
+ */
+#define META_MSGID_IGNORE (-1)
+#define META_MSGID_NEED_BIND (-2)
+#define META_MSGID_CONNECTING (-3)
+
+static int
+meta_send_entry(
+ Operation *op,
+ SlapReply *rs,
+ metaconn_t *mc,
+ int i,
+ LDAPMessage *e );
+
+typedef enum meta_search_candidate_t {
+ META_SEARCH_UNDEFINED = -2,
+ META_SEARCH_ERR = -1,
+ META_SEARCH_NOT_CANDIDATE,
+ META_SEARCH_CANDIDATE,
+ META_SEARCH_BINDING,
+ META_SEARCH_NEED_BIND,
+ META_SEARCH_CONNECTING
+} meta_search_candidate_t;
+
+/*
+ * meta_search_dobind_init()
+ *
+ * initiates bind for a candidate target of a search.
+ */
+static meta_search_candidate_t
+meta_search_dobind_init(
+ Operation *op,
+ SlapReply *rs,
+ metaconn_t **mcp,
+ int candidate,
+ SlapReply *candidates )
+{
+ metaconn_t *mc = *mcp;
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ metatarget_t *mt = mi->mi_targets[ candidate ];
+ metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+
+ struct berval binddn = msc->msc_bound_ndn,
+ cred = msc->msc_cred;
+ int method;
+
+ int rc;
+
+ meta_search_candidate_t retcode;
+
+ Debug( LDAP_DEBUG_TRACE, "%s >>> meta_search_dobind_init[%d]\n",
+ op->o_log_prefix, candidate );
+
+ /*
+ * all the targets are already bound as pseudoroot
+ */
+ if ( mc->mc_authz_target == META_BOUND_ALL ) {
+ return META_SEARCH_CANDIDATE;
+ }
+
+ retcode = META_SEARCH_BINDING;
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ if ( LDAP_BACK_CONN_ISBOUND( msc ) || LDAP_BACK_CONN_ISANON( msc ) ) {
+ /* already bound (or anonymous) */
+
+#ifdef DEBUG_205
+ int bound = 0;
+
+ if ( LDAP_BACK_CONN_ISBOUND( msc ) ) {
+ bound = 1;
+ }
+
+ Debug(LDAP_DEBUG_ANY,
+ "### %s meta_search_dobind_init[%d] mc=%p ld=%p%s DN=\"%s\"\n",
+ op->o_log_prefix, candidate, (void *)mc,
+ (void *)msc->msc_ld, bound ? " bound" : " anonymous",
+ bound == 0 ? "" : msc->msc_bound_ndn.bv_val );
+#endif /* DEBUG_205 */
+
+ retcode = META_SEARCH_CANDIDATE;
+
+ } else if ( META_BACK_CONN_CREATING( msc ) || LDAP_BACK_CONN_BINDING( msc ) ) {
+ /* another thread is binding the target for this conn; wait */
+
+#ifdef DEBUG_205
+ Debug(LDAP_DEBUG_ANY,
+ "### %s meta_search_dobind_init[%d] mc=%p ld=%p needbind\n",
+ op->o_log_prefix, candidate, (void *)mc,
+ (void *)msc->msc_ld );
+#endif /* DEBUG_205 */
+
+ candidates[ candidate ].sr_msgid = META_MSGID_NEED_BIND;
+ retcode = META_SEARCH_NEED_BIND;
+
+ } else {
+ /* we'll need to bind the target for this conn */
+
+#ifdef DEBUG_205
+ Debug(LDAP_DEBUG_ANY,
+ "### %s meta_search_dobind_init[%d] mc=%p ld=%p binding\n",
+ op->o_log_prefix, candidate, (void *)mc,
+ (void *)msc->msc_ld );
+#endif /* DEBUG_205 */
+
+ if ( msc->msc_ld == NULL ) {
+ /* for some reason (e.g. because formerly in "binding"
+ * state, with eventual connection expiration or invalidation)
+ * it was not initialized as expected */
+
+ Debug( LDAP_DEBUG_ANY, "%s meta_search_dobind_init[%d] mc=%p ld=NULL\n",
+ op->o_log_prefix, candidate, (void *)mc );
+
+ rc = meta_back_init_one_conn( op, rs, *mcp, candidate,
+ LDAP_BACK_CONN_ISPRIV( *mcp ), LDAP_BACK_DONTSEND, 0 );
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ assert( msc->msc_ld != NULL );
+ break;
+
+ case LDAP_SERVER_DOWN:
+ case LDAP_UNAVAILABLE:
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ goto down;
+
+ default:
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ goto other;
+ }
+ }
+
+ LDAP_BACK_CONN_BINDING_SET( msc );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+
+ if ( retcode != META_SEARCH_BINDING ) {
+ return retcode;
+ }
+
+ /* NOTE: this obsoletes pseudorootdn */
+ if ( op->o_conn != NULL &&
+ !op->o_do_not_cache &&
+ ( BER_BVISNULL( &msc->msc_bound_ndn ) ||
+ BER_BVISEMPTY( &msc->msc_bound_ndn ) ||
+ ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) )
+ {
+ rc = meta_back_proxy_authz_cred( mc, candidate, op, rs, LDAP_BACK_DONTSEND, &binddn, &cred, &method );
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ break;
+ case LDAP_UNAVAILABLE:
+ goto down;
+ default:
+ goto other;
+ }
+
+ /* NOTE: we copy things here, even if bind didn't succeed yet,
+ * because the connection is not shared until bind is over */
+ if ( !BER_BVISNULL( &binddn ) ) {
+ ber_bvreplace( &msc->msc_bound_ndn, &binddn );
+ if ( META_BACK_TGT_SAVECRED( mt ) && !BER_BVISNULL( &cred ) ) {
+ if ( !BER_BVISNULL( &msc->msc_cred ) ) {
+ memset( msc->msc_cred.bv_val, 0,
+ msc->msc_cred.bv_len );
+ }
+ ber_bvreplace( &msc->msc_cred, &cred );
+ }
+ }
+
+ if ( LDAP_BACK_CONN_ISBOUND( msc ) ) {
+ /* apparently, idassert was configured with SASL bind,
+ * so bind occurred inside meta_back_proxy_authz_cred() */
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ LDAP_BACK_CONN_BINDING_CLEAR( msc );
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ return META_SEARCH_CANDIDATE;
+ }
+
+ /* paranoid */
+ switch ( method ) {
+ case LDAP_AUTH_NONE:
+ case LDAP_AUTH_SIMPLE:
+ /* do a simple bind with binddn, cred */
+ break;
+
+ default:
+ assert( 0 );
+ break;
+ }
+ }
+
+ assert( msc->msc_ld != NULL );
+
+ /* connect must be async only the first time... */
+ ldap_set_option( msc->msc_ld, LDAP_OPT_CONNECT_ASYNC, LDAP_OPT_ON );
+
+retry:;
+ if ( !BER_BVISEMPTY( &binddn ) && BER_BVISEMPTY( &cred ) ) {
+ /* bind anonymously? */
+ Debug( LDAP_DEBUG_ANY, "%s meta_search_dobind_init[%d] mc=%p: "
+ "non-empty dn with empty cred; binding anonymously\n",
+ op->o_log_prefix, candidate, (void *)mc );
+ cred = slap_empty_bv;
+
+ } else if ( BER_BVISEMPTY( &binddn ) && !BER_BVISEMPTY( &cred ) ) {
+ /* error */
+ Debug( LDAP_DEBUG_ANY, "%s meta_search_dobind_init[%d] mc=%p: "
+ "empty dn with non-empty cred: error\n",
+ op->o_log_prefix, candidate, (void *)mc );
+ rc = LDAP_OTHER;
+ goto other;
+ }
+
+ rc = ldap_sasl_bind( msc->msc_ld, binddn.bv_val, LDAP_SASL_SIMPLE, &cred,
+ NULL, NULL, &candidates[ candidate ].sr_msgid );
+
+#ifdef DEBUG_205
+ Debug(LDAP_DEBUG_ANY,
+ "### %s meta_search_dobind_init[%d] mc=%p ld=%p rc=%d\n",
+ op->o_log_prefix, candidate, (void *)mc,
+ (void *)mc->mc_conns[candidate].msc_ld, rc );
+#endif /* DEBUG_205 */
+
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ assert( candidates[ candidate ].sr_msgid >= 0 );
+ META_BINDING_SET( &candidates[ candidate ] );
+ return META_SEARCH_BINDING;
+
+ case LDAP_X_CONNECTING:
+ /* must retry, same conn */
+ candidates[ candidate ].sr_msgid = META_MSGID_CONNECTING;
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ LDAP_BACK_CONN_BINDING_CLEAR( msc );
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ return META_SEARCH_CONNECTING;
+
+ case LDAP_SERVER_DOWN:
+down:;
+ /* This is the worst thing that could happen:
+ * the search will wait until the retry is over. */
+ if ( !META_IS_RETRYING( &candidates[ candidate ] ) ) {
+ META_RETRYING_SET( &candidates[ candidate ] );
+
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+
+ assert( mc->mc_refcnt > 0 );
+ if ( LogTest( LDAP_DEBUG_ANY ) ) {
+ /* this lock is required; however,
+ * it's invoked only when logging is on */
+ ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
+ Debug(LDAP_DEBUG_ANY,
+ "%s meta_search_dobind_init[%d]: retrying URI=\"%s\" DN=\"%s\".\n",
+ op->o_log_prefix, candidate, mt->mt_uri,
+ BER_BVISNULL(&msc->msc_bound_ndn) ? "" : msc->msc_bound_ndn.bv_val );
+ ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
+ }
+
+ meta_clear_one_candidate( op, mc, candidate );
+ LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
+
+ ( void )rewrite_session_delete( mt->mt_rwmap.rwm_rw, op->o_conn );
+
+ /* mc here must be the regular mc, reset and ready for init */
+ rc = meta_back_init_one_conn( op, rs, mc, candidate,
+ LDAP_BACK_CONN_ISPRIV( mc ), LDAP_BACK_DONTSEND, 0 );
+
+ if ( rc == LDAP_SUCCESS ) {
+ LDAP_BACK_CONN_BINDING_SET( msc );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+
+ if ( rc == LDAP_SUCCESS ) {
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ binddn = msc->msc_bound_ndn;
+ cred = msc->msc_cred;
+ goto retry;
+ }
+ }
+
+ if ( *mcp == NULL ) {
+ retcode = META_SEARCH_ERR;
+ rc = LDAP_UNAVAILABLE;
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ break;
+ }
+ /* fall thru */
+
+ default:
+other:;
+ /* convert rc to the correct LDAP error and send it back to the client:
+ assign the error to rs, so we can use it as argument to slap_map_api2result
+ and then assign the output back to rs->sr_err */
+ rs->sr_err = rc;
+ rs->sr_err = slap_map_api2result( rs );
+
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ meta_clear_one_candidate( op, mc, candidate );
+ candidates[ candidate ].sr_err = rs->sr_err;
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ LDAP_BACK_CONN_TAINTED_SET( mc );
+ meta_back_release_conn_lock( mi, mc, 0 );
+ *mcp = NULL;
+
+ retcode = META_SEARCH_ERR;
+
+ } else {
+ retcode = META_SEARCH_NOT_CANDIDATE;
+ }
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ break;
+ }
+
+ return retcode;
+}
+
+static meta_search_candidate_t
+meta_search_dobind_result(
+ Operation *op,
+ SlapReply *rs,
+ metaconn_t **mcp,
+ int candidate,
+ SlapReply *candidates,
+ LDAPMessage *res )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ metatarget_t *mt = mi->mi_targets[ candidate ];
+ metaconn_t *mc = *mcp;
+ metasingleconn_t *msc = &mc->mc_conns[ candidate ];
+
+ meta_search_candidate_t retcode = META_SEARCH_NOT_CANDIDATE;
+ int rc;
+
+ assert( msc->msc_ld != NULL );
+
+ /* FIXME: matched? referrals? response controls? */
+ rc = ldap_parse_result( msc->msc_ld, res,
+ &candidates[ candidate ].sr_err,
+ NULL, NULL, NULL, NULL, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ candidates[ candidate ].sr_err = rc;
+ }
+ rc = slap_map_api2result( &candidates[ candidate ] );
+
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ LDAP_BACK_CONN_BINDING_CLEAR( msc );
+ if ( rc != LDAP_SUCCESS ) {
+ meta_clear_one_candidate( op, mc, candidate );
+ candidates[ candidate ].sr_err = rc;
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ LDAP_BACK_CONN_TAINTED_SET( mc );
+ meta_back_release_conn_lock( mi, mc, 0 );
+ *mcp = NULL;
+ retcode = META_SEARCH_ERR;
+ rs->sr_err = rc;
+ }
+
+ } else {
+ /* FIXME: check if bound as idassert authcDN! */
+ if ( BER_BVISNULL( &msc->msc_bound_ndn )
+ || BER_BVISEMPTY( &msc->msc_bound_ndn ) )
+ {
+ LDAP_BACK_CONN_ISANON_SET( msc );
+
+ } else {
+ if ( META_BACK_TGT_SAVECRED( mt ) &&
+ !BER_BVISNULL( &msc->msc_cred ) &&
+ !BER_BVISEMPTY( &msc->msc_cred ) )
+ {
+ ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc );
+ }
+ LDAP_BACK_CONN_ISBOUND_SET( msc );
+ }
+ retcode = META_SEARCH_CANDIDATE;
+
+ /* connect must be async */
+ ldap_set_option( msc->msc_ld, LDAP_OPT_CONNECT_ASYNC, LDAP_OPT_OFF );
+ }
+
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ META_BINDING_CLEAR( &candidates[ candidate ] );
+
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+
+ return retcode;
+}
+
+static meta_search_candidate_t
+meta_back_search_start(
+ Operation *op,
+ SlapReply *rs,
+ dncookie *dc,
+ metaconn_t **mcp,
+ int candidate,
+ SlapReply *candidates,
+ struct berval *prcookie,
+ ber_int_t prsize )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ metatarget_t *mt = mi->mi_targets[ candidate ];
+ metasingleconn_t *msc = &(*mcp)->mc_conns[ candidate ];
+ struct berval realbase = op->o_req_dn;
+ int realscope = op->ors_scope;
+ struct berval mbase = BER_BVNULL;
+ struct berval mfilter = BER_BVNULL;
+ char **mapped_attrs = NULL;
+ int rc;
+ meta_search_candidate_t retcode;
+ struct timeval tv, *tvp = NULL;
+ int nretries = 1;
+ LDAPControl **ctrls = NULL;
+#ifdef SLAPD_META_CLIENT_PR
+ LDAPControl **save_ctrls = NULL;
+#endif /* SLAPD_META_CLIENT_PR */
+
+ /* this should not happen; just in case... */
+ if ( msc->msc_ld == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: meta_back_search_start candidate=%d ld=NULL%s.\n",
+ op->o_log_prefix, candidate,
+ META_BACK_ONERR_STOP( mi ) ? "" : " (ignored)" );
+ candidates[ candidate ].sr_err = LDAP_OTHER;
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ return META_SEARCH_ERR;
+ }
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ return META_SEARCH_NOT_CANDIDATE;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "%s >>> meta_back_search_start[%d]\n", op->o_log_prefix, candidate );
+
+ /*
+ * modifies the base according to the scope, if required
+ */
+ if ( mt->mt_nsuffix.bv_len > op->o_req_ndn.bv_len ) {
+ switch ( op->ors_scope ) {
+ case LDAP_SCOPE_SUBTREE:
+ /*
+ * make the target suffix the new base
+ * FIXME: this is very forgiving, because
+ * "illegal" searchBases may be turned
+ * into the suffix of the target; however,
+ * the requested searchBase already passed
+ * thru the candidate analyzer...
+ */
+ if ( dnIsSuffix( &mt->mt_nsuffix, &op->o_req_ndn ) ) {
+ realbase = mt->mt_nsuffix;
+ if ( mt->mt_scope == LDAP_SCOPE_SUBORDINATE ) {
+ realscope = LDAP_SCOPE_SUBORDINATE;
+ }
+
+ } else {
+ /*
+ * this target is no longer candidate
+ */
+ retcode = META_SEARCH_NOT_CANDIDATE;
+ goto doreturn;
+ }
+ break;
+
+ case LDAP_SCOPE_SUBORDINATE:
+ case LDAP_SCOPE_ONELEVEL:
+ {
+ struct berval rdn = mt->mt_nsuffix;
+ rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
+ if ( dnIsOneLevelRDN( &rdn )
+ && dnIsSuffix( &mt->mt_nsuffix, &op->o_req_ndn ) )
+ {
+ /*
+ * if there is exactly one level,
+ * make the target suffix the new
+ * base, and make scope "base"
+ */
+ realbase = mt->mt_nsuffix;
+ if ( op->ors_scope == LDAP_SCOPE_SUBORDINATE ) {
+ if ( mt->mt_scope == LDAP_SCOPE_SUBORDINATE ) {
+ realscope = LDAP_SCOPE_SUBORDINATE;
+ } else {
+ realscope = LDAP_SCOPE_SUBTREE;
+ }
+ } else {
+ realscope = LDAP_SCOPE_BASE;
+ }
+ break;
+ } /* else continue with the next case */
+ }
+
+ case LDAP_SCOPE_BASE:
+ /*
+ * this target is no longer candidate
+ */
+ retcode = META_SEARCH_NOT_CANDIDATE;
+ goto doreturn;
+ }
+ }
+
+ /* check filter expression */
+ if ( mt->mt_filter ) {
+ metafilter_t *mf;
+ for ( mf = mt->mt_filter; mf; mf = mf->mf_next ) {
+ if ( regexec( &mf->mf_regex, op->ors_filterstr.bv_val, 0, NULL, 0 ) == 0 )
+ break;
+ }
+ /* nothing matched, this target is no longer a candidate */
+ if ( !mf ) {
+ retcode = META_SEARCH_NOT_CANDIDATE;
+ goto doreturn;
+ }
+ }
+
+ /* initiate dobind */
+ retcode = meta_search_dobind_init( op, rs, mcp, candidate, candidates );
+
+ Debug( LDAP_DEBUG_TRACE, "%s <<< meta_search_dobind_init[%d]=%d\n", op->o_log_prefix, candidate, retcode );
+
+ if ( retcode != META_SEARCH_CANDIDATE ) {
+ goto doreturn;
+ }
+
+ /*
+ * Rewrite the search base, if required
+ */
+ dc->target = mt;
+ dc->ctx = "searchBase";
+ switch ( ldap_back_dn_massage( dc, &realbase, &mbase ) ) {
+ case LDAP_SUCCESS:
+ break;
+
+ case LDAP_UNWILLING_TO_PERFORM:
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "Operation not allowed";
+ send_ldap_result( op, rs );
+ retcode = META_SEARCH_ERR;
+ goto doreturn;
+
+ default:
+
+ /*
+ * this target is no longer candidate
+ */
+ retcode = META_SEARCH_NOT_CANDIDATE;
+ goto doreturn;
+ }
+
+ /*
+ * Maps filter
+ */
+ rc = ldap_back_filter_map_rewrite( dc, op->ors_filter,
+ &mfilter, BACKLDAP_MAP, op->o_tmpmemctx );
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ break;
+
+ case LDAP_COMPARE_FALSE:
+ default:
+ /*
+ * this target is no longer candidate
+ */
+ retcode = META_SEARCH_NOT_CANDIDATE;
+ goto done;
+ }
+
+ /*
+ * Maps required attributes
+ */
+ rc = ldap_back_map_attrs( op, &mt->mt_rwmap.rwm_at,
+ op->ors_attrs, BACKLDAP_MAP, &mapped_attrs );
+ if ( rc != LDAP_SUCCESS ) {
+ /*
+ * this target is no longer candidate
+ */
+ retcode = META_SEARCH_NOT_CANDIDATE;
+ goto done;
+ }
+
+ if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
+ tv.tv_sec = op->ors_tlimit > 0 ? op->ors_tlimit : 1;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ }
+
+#ifdef SLAPD_META_CLIENT_PR
+ save_ctrls = op->o_ctrls;
+ {
+ LDAPControl *pr_c = NULL;
+ int i = 0, nc = 0;
+
+ if ( save_ctrls ) {
+ for ( ; save_ctrls[i] != NULL; i++ );
+ nc = i;
+ pr_c = ldap_control_find( LDAP_CONTROL_PAGEDRESULTS, save_ctrls, NULL );
+ }
+
+ if ( pr_c != NULL ) nc--;
+ if ( mt->mt_ps > 0 || prcookie != NULL ) nc++;
+
+ if ( mt->mt_ps > 0 || prcookie != NULL || pr_c != NULL ) {
+ int src = 0, dst = 0;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ struct berval val = BER_BVNULL;
+ ber_len_t len;
+
+ len = sizeof( LDAPControl * )*( nc + 1 ) + sizeof( LDAPControl );
+
+ if ( mt->mt_ps > 0 || prcookie != NULL ) {
+ struct berval nullcookie = BER_BVNULL;
+ ber_tag_t tag;
+
+ if ( prsize == 0 && mt->mt_ps > 0 ) prsize = mt->mt_ps;
+ if ( prcookie == NULL ) prcookie = &nullcookie;
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+ tag = ber_printf( ber, "{iO}", prsize, prcookie );
+ if ( tag == LBER_ERROR ) {
+ /* error */
+ (void) ber_free_buf( ber );
+ goto done_pr;
+ }
+
+ tag = ber_flatten2( ber, &val, 0 );
+ if ( tag == LBER_ERROR ) {
+ /* error */
+ (void) ber_free_buf( ber );
+ goto done_pr;
+ }
+
+ len += val.bv_len + 1;
+ }
+
+ op->o_ctrls = op->o_tmpalloc( len, op->o_tmpmemctx );
+ if ( save_ctrls ) {
+ for ( ; save_ctrls[ src ] != NULL; src++ ) {
+ if ( save_ctrls[ src ] != pr_c ) {
+ op->o_ctrls[ dst ] = save_ctrls[ src ];
+ dst++;
+ }
+ }
+ }
+
+ if ( mt->mt_ps > 0 || prcookie != NULL ) {
+ op->o_ctrls[ dst ] = (LDAPControl *)&op->o_ctrls[ nc + 1 ];
+
+ op->o_ctrls[ dst ]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
+ op->o_ctrls[ dst ]->ldctl_iscritical = 1;
+
+ op->o_ctrls[ dst ]->ldctl_value.bv_val = (char *)&op->o_ctrls[ dst ][ 1 ];
+ AC_MEMCPY( op->o_ctrls[ dst ]->ldctl_value.bv_val, val.bv_val, val.bv_len + 1 );
+ op->o_ctrls[ dst ]->ldctl_value.bv_len = val.bv_len;
+ dst++;
+
+ (void)ber_free_buf( ber );
+ }
+
+ op->o_ctrls[ dst ] = NULL;
+ }
+done_pr:;
+ }
+#endif /* SLAPD_META_CLIENT_PR */
+
+retry:;
+ ctrls = op->o_ctrls;
+ if ( meta_back_controls_add( op, rs, *mcp, candidate, &ctrls )
+ != LDAP_SUCCESS )
+ {
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ retcode = META_SEARCH_NOT_CANDIDATE;
+ goto done;
+ }
+
+ /*
+ * Starts the search
+ */
+ assert( msc->msc_ld != NULL );
+ rc = ldap_pvt_search( msc->msc_ld,
+ mbase.bv_val, realscope, mfilter.bv_val,
+ mapped_attrs, op->ors_attrsonly,
+ ctrls, NULL, tvp, op->ors_slimit, op->ors_deref,
+ &candidates[ candidate ].sr_msgid );
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ retcode = META_SEARCH_CANDIDATE;
+ break;
+
+ case LDAP_SERVER_DOWN:
+ if ( nretries && meta_back_retry( op, rs, mcp, candidate, LDAP_BACK_DONTSEND ) ) {
+ nretries = 0;
+ /* if the identity changed, there might be need to re-authz */
+ (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+ goto retry;
+ }
+
+ if ( *mcp == NULL ) {
+ retcode = META_SEARCH_ERR;
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ break;
+ }
+ /* fall thru */
+
+ default:
+ candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
+ retcode = META_SEARCH_NOT_CANDIDATE;
+ }
+
+done:;
+ (void)mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
+#ifdef SLAPD_META_CLIENT_PR
+ if ( save_ctrls != op->o_ctrls ) {
+ op->o_tmpfree( op->o_ctrls, op->o_tmpmemctx );
+ op->o_ctrls = save_ctrls;
+ }
+#endif /* SLAPD_META_CLIENT_PR */
+
+ if ( mapped_attrs ) {
+ ber_memfree_x( mapped_attrs, op->o_tmpmemctx );
+ }
+ if ( mfilter.bv_val != op->ors_filterstr.bv_val ) {
+ ber_memfree_x( mfilter.bv_val, op->o_tmpmemctx );
+ }
+ if ( mbase.bv_val != realbase.bv_val ) {
+ free( mbase.bv_val );
+ }
+
+doreturn:;
+ Debug( LDAP_DEBUG_TRACE, "%s <<< meta_back_search_start[%d]=%d\n", op->o_log_prefix, candidate, retcode );
+
+ return retcode;
+}
+
+int
+meta_back_search( Operation *op, SlapReply *rs )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ metaconn_t *mc;
+ struct timeval save_tv = { 0, 0 },
+ tv;
+ time_t stoptime = (time_t)(-1),
+ lastres_time = slap_get_time(),
+ timeout = 0;
+ int rc = 0, sres = LDAP_SUCCESS;
+ char *matched = NULL;
+ int last = 0, ncandidates = 0,
+ initial_candidates = 0, candidate_match = 0,
+ needbind = 0;
+ ldap_back_send_t sendok = LDAP_BACK_SENDERR;
+ long i;
+ dncookie dc;
+ int is_ok = 0;
+ void *savepriv;
+ SlapReply *candidates = NULL;
+ int do_taint = 0;
+
+ rs_assert_ready( rs );
+ rs->sr_flags &= ~REP_ENTRY_MASK; /* paranoia, we can set rs = non-entry */
+
+ /*
+ * controls are set in ldap_back_dobind()
+ *
+ * FIXME: in case of values return filter, we might want
+ * to map attrs and maybe rewrite value
+ */
+getconn:;
+ mc = meta_back_getconn( op, rs, NULL, sendok );
+ if ( !mc ) {
+ return rs->sr_err;
+ }
+
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+
+ if ( candidates == NULL ) candidates = meta_back_candidates_get( op );
+ /*
+ * Inits searches
+ */
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ /* reset sr_msgid; it is used in most loops
+ * to check if that target is still to be considered */
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+
+ /* a target is marked as candidate by meta_back_getconn();
+ * if for any reason (an error, it's over or so) it is
+ * no longer active, sr_msgid is set to META_MSGID_IGNORE
+ * but it remains candidate, which means it has been active
+ * at some point during the operation. This allows to
+ * use its response code and more to compute the final
+ * response */
+ if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
+ continue;
+ }
+
+ candidates[ i ].sr_matched = NULL;
+ candidates[ i ].sr_text = NULL;
+ candidates[ i ].sr_ref = NULL;
+ candidates[ i ].sr_ctrls = NULL;
+ candidates[ i ].sr_nentries = 0;
+
+ /* get largest timeout among candidates */
+ if ( mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ]
+ && mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ] > timeout )
+ {
+ timeout = mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ];
+ }
+ }
+
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ if ( !META_IS_CANDIDATE( &candidates[ i ] )
+ || candidates[ i ].sr_err != LDAP_SUCCESS )
+ {
+ continue;
+ }
+
+ switch ( meta_back_search_start( op, rs, &dc, &mc, i, candidates, NULL, 0 ) )
+ {
+ case META_SEARCH_NOT_CANDIDATE:
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ break;
+
+ case META_SEARCH_NEED_BIND:
+ ++needbind;
+ /* fallthru */
+
+ case META_SEARCH_CONNECTING:
+ case META_SEARCH_CANDIDATE:
+ case META_SEARCH_BINDING:
+ candidates[ i ].sr_type = REP_INTERMEDIATE;
+ ++ncandidates;
+ break;
+
+ case META_SEARCH_ERR:
+ savepriv = op->o_private;
+ op->o_private = (void *)i;
+ send_ldap_result( op, rs );
+ op->o_private = savepriv;
+ rc = -1;
+ goto finish;
+
+ default:
+ assert( 0 );
+ break;
+ }
+ }
+
+ if ( ncandidates > 0 && needbind == ncandidates ) {
+ /*
+ * give up the second time...
+ *
+ * NOTE: this should not occur the second time, since a fresh
+ * connection has ben created; however, targets may also
+ * need bind because the bind timed out or so.
+ */
+ if ( sendok & LDAP_BACK_BINDING ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s meta_back_search: unable to initialize conn\n",
+ op->o_log_prefix );
+ rs->sr_err = LDAP_UNAVAILABLE;
+ rs->sr_text = "unable to initialize connection to remote targets";
+ send_ldap_result( op, rs );
+ rc = -1;
+ goto finish;
+ }
+
+ /* FIXME: better create a separate connection? */
+ sendok |= LDAP_BACK_BINDING;
+
+#ifdef DEBUG_205
+ Debug( LDAP_DEBUG_ANY, "*** %s drop mc=%p create new connection\n",
+ op->o_log_prefix, (void *)mc );
+#endif /* DEBUG_205 */
+
+ meta_back_release_conn( mi, mc );
+ mc = NULL;
+
+ needbind = 0;
+ ncandidates = 0;
+
+ goto getconn;
+ }
+
+ initial_candidates = ncandidates;
+
+ if ( LogTest( LDAP_DEBUG_TRACE ) ) {
+ char cnd[ SLAP_TEXT_BUFLEN ];
+ int c;
+
+ for ( c = 0; c < mi->mi_ntargets; c++ ) {
+ if ( META_IS_CANDIDATE( &candidates[ c ] ) ) {
+ cnd[ c ] = '*';
+ } else {
+ cnd[ c ] = ' ';
+ }
+ }
+ cnd[ c ] = '\0';
+
+ Debug( LDAP_DEBUG_TRACE, "%s meta_back_search: ncandidates=%d "
+ "cnd=\"%s\"\n", op->o_log_prefix, ncandidates, cnd );
+ }
+
+ if ( initial_candidates == 0 ) {
+ /* NOTE: here we are not sending any matchedDN;
+ * this is intended, because if the back-meta
+ * is serving this search request, but no valid
+ * candidate could be looked up, it means that
+ * there is a hole in the mapping of the targets
+ * and thus no knowledge of any remote superior
+ * is available */
+ Debug( LDAP_DEBUG_ANY, "%s meta_back_search: "
+ "base=\"%s\" scope=%d: "
+ "no candidate could be selected\n",
+ op->o_log_prefix, op->o_req_dn.bv_val,
+ op->ors_scope );
+
+ /* FIXME: we're sending the first error we encounter;
+ * maybe we should pick the worst... */
+ rc = LDAP_NO_SUCH_OBJECT;
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ if ( META_IS_CANDIDATE( &candidates[ i ] )
+ && candidates[ i ].sr_err != LDAP_SUCCESS )
+ {
+ rc = candidates[ i ].sr_err;
+ break;
+ }
+ }
+
+ send_ldap_error( op, rs, rc, NULL );
+
+ goto finish;
+ }
+
+ /* We pull apart the ber result, stuff it into a slapd entry, and
+ * let send_search_entry stuff it back into ber format. Slow & ugly,
+ * but this is necessary for version matching, and for ACL processing.
+ */
+
+ if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
+ stoptime = op->o_time + op->ors_tlimit;
+ }
+
+ /*
+ * In case there are no candidates, no cycle takes place...
+ *
+ * FIXME: we might use a queue, to better balance the load
+ * among the candidates
+ */
+ for ( rc = 0; ncandidates > 0; ) {
+ int gotit = 0,
+ doabandon = 0,
+ alreadybound = ncandidates;
+
+ /* check timeout */
+ if ( timeout && lastres_time > 0
+ && ( slap_get_time() - lastres_time ) > timeout )
+ {
+ doabandon = 1;
+ rs->sr_text = "Operation timed out";
+ rc = rs->sr_err = op->o_protocol >= LDAP_VERSION3 ?
+ LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
+ savepriv = op->o_private;
+ op->o_private = (void *)i;
+ send_ldap_result( op, rs );
+ op->o_private = savepriv;
+ goto finish;
+ }
+
+ /* check time limit */
+ if ( op->ors_tlimit != SLAP_NO_LIMIT
+ && slap_get_time() > stoptime )
+ {
+ doabandon = 1;
+ rc = rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
+ savepriv = op->o_private;
+ op->o_private = (void *)i;
+ send_ldap_result( op, rs );
+ op->o_private = savepriv;
+ goto finish;
+ }
+
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ meta_search_candidate_t retcode = META_SEARCH_UNDEFINED;
+ metasingleconn_t *msc = &mc->mc_conns[ i ];
+ LDAPMessage *res = NULL, *msg;
+
+ /* if msgid is invalid, don't ldap_result() */
+ if ( candidates[ i ].sr_msgid == META_MSGID_IGNORE ) {
+ continue;
+ }
+
+ /* if target still needs bind, retry */
+ if ( candidates[ i ].sr_msgid == META_MSGID_NEED_BIND
+ || candidates[ i ].sr_msgid == META_MSGID_CONNECTING )
+ {
+ /* initiate dobind */
+ retcode = meta_search_dobind_init( op, rs, &mc, i, candidates );
+
+ Debug( LDAP_DEBUG_TRACE, "%s <<< meta_search_dobind_init[%ld]=%d\n",
+ op->o_log_prefix, i, retcode );
+
+ switch ( retcode ) {
+ case META_SEARCH_NEED_BIND:
+ alreadybound--;
+ /* fallthru */
+
+ case META_SEARCH_CONNECTING:
+ case META_SEARCH_BINDING:
+ break;
+
+ case META_SEARCH_ERR:
+ candidates[ i ].sr_err = rs->sr_err;
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ savepriv = op->o_private;
+ op->o_private = (void *)i;
+ send_ldap_result( op, rs );
+ op->o_private = savepriv;
+ goto finish;
+ }
+ /* fallthru */
+
+ case META_SEARCH_NOT_CANDIDATE:
+ /*
+ * When no candidates are left,
+ * the outer cycle finishes
+ */
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ assert( ncandidates > 0 );
+ --ncandidates;
+ break;
+
+ case META_SEARCH_CANDIDATE:
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ switch ( meta_back_search_start( op, rs, &dc, &mc, i, candidates, NULL, 0 ) )
+ {
+ case META_SEARCH_CANDIDATE:
+ assert( candidates[ i ].sr_msgid >= 0 );
+ break;
+
+ case META_SEARCH_ERR:
+ candidates[ i ].sr_err = rs->sr_err;
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ savepriv = op->o_private;
+ op->o_private = (void *)i;
+ send_ldap_result( op, rs );
+ op->o_private = savepriv;
+ goto finish;
+ }
+ /* fallthru */
+
+ case META_SEARCH_NOT_CANDIDATE:
+ /* means that meta_back_search_start()
+ * failed but onerr == continue */
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ assert( ncandidates > 0 );
+ --ncandidates;
+ break;
+
+ default:
+ /* impossible */
+ assert( 0 );
+ break;
+ }
+ break;
+
+ default:
+ /* impossible */
+ assert( 0 );
+ break;
+ }
+ continue;
+ }
+
+ /* check for abandon */
+ if ( op->o_abandon || LDAP_BACK_CONN_ABANDON( mc ) ) {
+ break;
+ }
+
+#ifdef DEBUG_205
+ if ( msc->msc_ld == NULL ) {
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ Debug(LDAP_DEBUG_ANY,
+ "!!! %s meta_back_search[%ld] mc=%p msgid=%d%s%s%s\n\n",
+ op->o_log_prefix, (long)i, (void *)mc,
+ candidates[i].sr_msgid,
+ META_IS_BINDING(&candidates[i]) ? " binding" : "",
+ LDAP_BACK_CONN_BINDING(&mc->mc_conns[i]) ? " connbinding" : "",
+ META_BACK_CONN_CREATING(&mc->mc_conns[i]) ? " conncreating" : "" );
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ }
+#endif /* DEBUG_205 */
+
+ /*
+ * FIXME: handle time limit as well?
+ * Note that target servers are likely
+ * to handle it, so at some time we'll
+ * get a LDAP_TIMELIMIT_EXCEEDED from
+ * one of them ...
+ */
+ tv = save_tv;
+ rc = ldap_result( msc->msc_ld, candidates[ i ].sr_msgid,
+ LDAP_MSG_RECEIVED, &tv, &res );
+ switch ( rc ) {
+ case 0:
+ /* FIXME: res should not need to be freed */
+ assert( res == NULL );
+ continue;
+
+ case -1:
+really_bad:;
+ /* something REALLY bad happened! */
+ if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
+ candidates[ i ].sr_type = REP_RESULT;
+
+ if ( meta_back_retry( op, rs, &mc, i, LDAP_BACK_DONTSEND ) ) {
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ switch ( meta_back_search_start( op, rs, &dc, &mc, i, candidates, NULL, 0 ) )
+ {
+ /* means that failed but onerr == continue */
+ case META_SEARCH_NOT_CANDIDATE:
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+
+ assert( ncandidates > 0 );
+ --ncandidates;
+
+ candidates[ i ].sr_err = rs->sr_err;
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ savepriv = op->o_private;
+ op->o_private = (void *)i;
+ send_ldap_result( op, rs );
+ op->o_private = savepriv;
+ goto finish;
+ }
+ /* fall thru */
+
+ case META_SEARCH_CANDIDATE:
+ /* get back into business... */
+ continue;
+
+ case META_SEARCH_BINDING:
+ case META_SEARCH_CONNECTING:
+ case META_SEARCH_NEED_BIND:
+ case META_SEARCH_UNDEFINED:
+ assert( 0 );
+
+ default:
+ /* unrecoverable error */
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ rc = rs->sr_err = LDAP_OTHER;
+ goto finish;
+ }
+ }
+
+ candidates[ i ].sr_err = rs->sr_err;
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ savepriv = op->o_private;
+ op->o_private = (void *)i;
+ send_ldap_result( op, rs );
+ op->o_private = savepriv;
+ goto finish;
+ }
+ }
+
+ /*
+ * When no candidates are left,
+ * the outer cycle finishes
+ */
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ assert( ncandidates > 0 );
+ --ncandidates;
+ rs->sr_err = candidates[ i ].sr_err;
+ continue;
+
+ default:
+ lastres_time = slap_get_time();
+
+ /* only touch when activity actually took place... */
+ if ( mi->mi_idle_timeout != 0 && msc->msc_time < lastres_time ) {
+ msc->msc_time = lastres_time;
+ }
+ break;
+ }
+
+ for ( msg = ldap_first_message( msc->msc_ld, res );
+ msg != NULL;
+ msg = ldap_next_message( msc->msc_ld, msg ) )
+ {
+ rc = ldap_msgtype( msg );
+ if ( rc == LDAP_RES_SEARCH_ENTRY ) {
+ LDAPMessage *e;
+
+ if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
+ /* don't retry any more... */
+ candidates[ i ].sr_type = REP_RESULT;
+ }
+
+ /* count entries returned by target */
+ candidates[ i ].sr_nentries++;
+
+ is_ok++;
+
+ e = ldap_first_entry( msc->msc_ld, msg );
+ savepriv = op->o_private;
+ op->o_private = (void *)i;
+ rs->sr_err = meta_send_entry( op, rs, mc, i, e );
+
+ switch ( rs->sr_err ) {
+ case LDAP_SIZELIMIT_EXCEEDED:
+ savepriv = op->o_private;
+ op->o_private = (void *)i;
+ send_ldap_result( op, rs );
+ op->o_private = savepriv;
+ rs->sr_err = LDAP_SUCCESS;
+ ldap_msgfree( res );
+ res = NULL;
+ goto finish;
+
+ case LDAP_UNAVAILABLE:
+ rs->sr_err = LDAP_OTHER;
+ ldap_msgfree( res );
+ res = NULL;
+ goto finish;
+ }
+ op->o_private = savepriv;
+
+ /* don't wait any longer... */
+ gotit = 1;
+ save_tv.tv_sec = 0;
+ save_tv.tv_usec = 0;
+
+ } else if ( rc == LDAP_RES_SEARCH_REFERENCE ) {
+ char **references = NULL;
+ int cnt;
+
+ if ( META_BACK_TGT_NOREFS( mi->mi_targets[ i ] ) ) {
+ continue;
+ }
+
+ if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
+ /* don't retry any more... */
+ candidates[ i ].sr_type = REP_RESULT;
+ }
+
+ is_ok++;
+
+ rc = ldap_parse_reference( msc->msc_ld, msg,
+ &references, &rs->sr_ctrls, 0 );
+
+ if ( rc != LDAP_SUCCESS ) {
+ continue;
+ }
+
+ if ( references == NULL ) {
+ continue;
+ }
+
+ dc.ctx = "referralDN";
+
+ /* FIXME: merge all and return at the end */
+
+ for ( cnt = 0; references[ cnt ]; cnt++ )
+ ;
+
+ rs->sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( cnt + 1 ),
+ op->o_tmpmemctx );
+
+ for ( cnt = 0; references[ cnt ]; cnt++ ) {
+ ber_str2bv_x( references[ cnt ], 0, 1, &rs->sr_ref[ cnt ],
+ op->o_tmpmemctx );
+ }
+ BER_BVZERO( &rs->sr_ref[ cnt ] );
+
+ ( void )ldap_back_referral_result_rewrite( &dc, rs->sr_ref,
+ op->o_tmpmemctx );
+
+ if ( rs->sr_ref != NULL && !BER_BVISNULL( &rs->sr_ref[ 0 ] ) ) {
+ /* ignore return value by now */
+ savepriv = op->o_private;
+ op->o_private = (void *)i;
+ ( void )send_search_reference( op, rs );
+ op->o_private = savepriv;
+
+ ber_bvarray_free_x( rs->sr_ref, op->o_tmpmemctx );
+ rs->sr_ref = NULL;
+ }
+
+ /* cleanup */
+ if ( references ) {
+ ber_memvfree( (void **)references );
+ }
+
+ if ( rs->sr_ctrls ) {
+ ldap_controls_free( rs->sr_ctrls );
+ rs->sr_ctrls = NULL;
+ }
+
+ } else if ( rc == LDAP_RES_INTERMEDIATE ) {
+ if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
+ /* don't retry any more... */
+ candidates[ i ].sr_type = REP_RESULT;
+ }
+
+ /* FIXME: response controls
+ * are passed without checks */
+ rs->sr_err = ldap_parse_intermediate( msc->msc_ld,
+ msg,
+ (char **)&rs->sr_rspoid,
+ &rs->sr_rspdata,
+ &rs->sr_ctrls,
+ 0 );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ candidates[ i ].sr_type = REP_RESULT;
+ ldap_msgfree( res );
+ res = NULL;
+ goto really_bad;
+ }
+
+ slap_send_ldap_intermediate( op, rs );
+
+ if ( rs->sr_rspoid != NULL ) {
+ ber_memfree( (char *)rs->sr_rspoid );
+ rs->sr_rspoid = NULL;
+ }
+
+ if ( rs->sr_rspdata != NULL ) {
+ ber_bvfree( rs->sr_rspdata );
+ rs->sr_rspdata = NULL;
+ }
+
+ if ( rs->sr_ctrls != NULL ) {
+ ldap_controls_free( rs->sr_ctrls );
+ rs->sr_ctrls = NULL;
+ }
+
+ } else if ( rc == LDAP_RES_SEARCH_RESULT ) {
+ char **references = NULL;
+ LDAPControl **ctrls = NULL;
+
+ if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
+ /* don't retry any more... */
+ candidates[ i ].sr_type = REP_RESULT;
+ }
+
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+
+ /* NOTE: ignores response controls
+ * (and intermediate response controls
+ * as well, except for those with search
+ * references); this may not be correct,
+ * but if they're not ignored then
+ * back-meta would need to merge them
+ * consistently (think of pagedResults...)
+ */
+ /* FIXME: response controls? */
+ rs->sr_err = ldap_parse_result( msc->msc_ld,
+ msg,
+ &candidates[ i ].sr_err,
+ (char **)&candidates[ i ].sr_matched,
+ (char **)&candidates[ i ].sr_text,
+ &references,
+ &ctrls /* &candidates[ i ].sr_ctrls (unused) */ ,
+ 0 );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ candidates[ i ].sr_err = rs->sr_err;
+ sres = slap_map_api2result( &candidates[ i ] );
+ candidates[ i ].sr_type = REP_RESULT;
+ ldap_msgfree( res );
+ res = NULL;
+ goto really_bad;
+ }
+
+ rs->sr_err = candidates[ i ].sr_err;
+
+ /* massage matchedDN if need be */
+ if ( candidates[ i ].sr_matched != NULL ) {
+ struct berval match, mmatch;
+
+ ber_str2bv( candidates[ i ].sr_matched,
+ 0, 0, &match );
+ candidates[ i ].sr_matched = NULL;
+
+ dc.ctx = "matchedDN";
+ dc.target = mi->mi_targets[ i ];
+ if ( !ldap_back_dn_massage( &dc, &match, &mmatch ) ) {
+ if ( mmatch.bv_val == match.bv_val ) {
+ candidates[ i ].sr_matched
+ = ch_strdup( mmatch.bv_val );
+
+ } else {
+ candidates[ i ].sr_matched = mmatch.bv_val;
+ }
+
+ candidate_match++;
+ }
+ ldap_memfree( match.bv_val );
+ }
+
+ /* add references to array */
+ /* RFC 4511: referrals can only appear
+ * if result code is LDAP_REFERRAL */
+ if ( references != NULL
+ && references[ 0 ] != NULL
+ && references[ 0 ][ 0 ] != '\0' )
+ {
+ if ( rs->sr_err != LDAP_REFERRAL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s meta_back_search[%ld]: "
+ "got referrals with err=%d\n",
+ op->o_log_prefix,
+ i, rs->sr_err );
+
+ } else {
+ BerVarray sr_ref;
+ int cnt;
+
+ for ( cnt = 0; references[ cnt ]; cnt++ )
+ ;
+
+ sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( cnt + 1 ),
+ op->o_tmpmemctx );
+
+ for ( cnt = 0; references[ cnt ]; cnt++ ) {
+ ber_str2bv_x( references[ cnt ], 0, 1, &sr_ref[ cnt ],
+ op->o_tmpmemctx );
+ }
+ BER_BVZERO( &sr_ref[ cnt ] );
+
+ ( void )ldap_back_referral_result_rewrite( &dc, sr_ref,
+ op->o_tmpmemctx );
+
+ if ( rs->sr_v2ref == NULL ) {
+ rs->sr_v2ref = sr_ref;
+
+ } else {
+ for ( cnt = 0; !BER_BVISNULL( &sr_ref[ cnt ] ); cnt++ ) {
+ ber_bvarray_add_x( &rs->sr_v2ref, &sr_ref[ cnt ],
+ op->o_tmpmemctx );
+ }
+ ber_memfree_x( sr_ref, op->o_tmpmemctx );
+ }
+ }
+
+ } else if ( rs->sr_err == LDAP_REFERRAL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s meta_back_search[%ld]: "
+ "got err=%d with null "
+ "or empty referrals\n",
+ op->o_log_prefix,
+ i, rs->sr_err );
+
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ }
+
+ /* cleanup */
+ ber_memvfree( (void **)references );
+
+ sres = slap_map_api2result( rs );
+
+ if ( LogTest( LDAP_DEBUG_TRACE | LDAP_DEBUG_ANY ) ) {
+ char buf[ SLAP_TEXT_BUFLEN ];
+ snprintf( buf, sizeof( buf ),
+ "%s meta_back_search[%ld] "
+ "match=\"%s\" err=%ld",
+ op->o_log_prefix, i,
+ candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "",
+ (long) candidates[ i ].sr_err );
+ if ( candidates[ i ].sr_err == LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "%s.\n", buf );
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s (%s) text=\"%s\".\n",
+ buf, ldap_err2string( candidates[ i ].sr_err ),
+ candidates[ i ].sr_text ? candidates[i].sr_text : "" );
+ }
+ }
+
+ switch ( sres ) {
+ case LDAP_NO_SUCH_OBJECT:
+ /* is_ok is touched any time a valid
+ * (even intermediate) result is
+ * returned; as a consequence, if
+ * a candidate returns noSuchObject
+ * it is ignored and the candidate
+ * is simply demoted. */
+ if ( is_ok ) {
+ sres = LDAP_SUCCESS;
+ }
+ break;
+
+ case LDAP_SUCCESS:
+ if ( ctrls != NULL && ctrls[0] != NULL ) {
+#ifdef SLAPD_META_CLIENT_PR
+ LDAPControl *pr_c;
+
+ pr_c = ldap_control_find( LDAP_CONTROL_PAGEDRESULTS, ctrls, NULL );
+ if ( pr_c != NULL ) {
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_int_t prsize;
+ struct berval prcookie;
+
+ /* unsolicited, do not accept */
+ if ( mi->mi_targets[i]->mt_ps == 0 ) {
+ rs->sr_err = LDAP_OTHER;
+ goto err_pr;
+ }
+
+ ber_init2( ber, &pr_c->ldctl_value, LBER_USE_DER );
+
+ tag = ber_scanf( ber, "{im}", &prsize, &prcookie );
+ if ( tag == LBER_ERROR ) {
+ rs->sr_err = LDAP_OTHER;
+ goto err_pr;
+ }
+
+ /* more pages? new search request */
+ if ( !BER_BVISNULL( &prcookie ) && !BER_BVISEMPTY( &prcookie ) ) {
+ if ( mi->mi_targets[i]->mt_ps > 0 ) {
+ /* ignore size if specified */
+ prsize = 0;
+
+ } else if ( prsize == 0 ) {
+ /* guess the page size from the entries returned so far */
+ prsize = candidates[ i ].sr_nentries;
+ }
+
+ candidates[ i ].sr_nentries = 0;
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ candidates[ i ].sr_type = REP_INTERMEDIATE;
+
+ assert( candidates[ i ].sr_matched == NULL );
+ assert( candidates[ i ].sr_text == NULL );
+ assert( candidates[ i ].sr_ref == NULL );
+
+ switch ( meta_back_search_start( op, rs, &dc, &mc, i, candidates, &prcookie, prsize ) )
+ {
+ case META_SEARCH_CANDIDATE:
+ assert( candidates[ i ].sr_msgid >= 0 );
+ ldap_controls_free( ctrls );
+ goto free_message;
+
+ case META_SEARCH_ERR:
+err_pr:;
+ candidates[ i ].sr_err = rs->sr_err;
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ savepriv = op->o_private;
+ op->o_private = (void *)i;
+ send_ldap_result( op, rs );
+ op->o_private = savepriv;
+ ldap_controls_free( ctrls );
+ goto finish;
+ }
+ /* fallthru */
+
+ case META_SEARCH_NOT_CANDIDATE:
+ /* means that meta_back_search_start()
+ * failed but onerr == continue */
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ assert( ncandidates > 0 );
+ --ncandidates;
+ break;
+
+ default:
+ /* impossible */
+ assert( 0 );
+ break;
+ }
+ break;
+ }
+ }
+#endif /* SLAPD_META_CLIENT_PR */
+ }
+ /* fallthru */
+
+ case LDAP_REFERRAL:
+ is_ok++;
+ break;
+
+ case LDAP_SIZELIMIT_EXCEEDED:
+ /* if a target returned sizelimitExceeded
+ * and the entry count is equal to the
+ * proxy's limit, the target would have
+ * returned more, and the error must be
+ * propagated to the client; otherwise,
+ * the target enforced a limit lower
+ * than what requested by the proxy;
+ * ignore it */
+ candidates[ i ].sr_err = rs->sr_err;
+ if ( rs->sr_nentries == op->ors_slimit
+ || META_BACK_ONERR_STOP( mi ) )
+ {
+ const char *save_text;
+got_err:
+ save_text = rs->sr_text;
+ savepriv = op->o_private;
+ op->o_private = (void *)i;
+ rs->sr_text = candidates[ i ].sr_text;
+ send_ldap_result( op, rs );
+ rs->sr_text = save_text;
+ op->o_private = savepriv;
+ ldap_msgfree( res );
+ res = NULL;
+ ldap_controls_free( ctrls );
+ goto finish;
+ }
+ break;
+
+ default:
+ candidates[ i ].sr_err = rs->sr_err;
+ if ( META_BACK_ONERR_STOP( mi ) )
+ goto got_err;
+ break;
+ }
+
+ ldap_controls_free( ctrls );
+ last = i;
+ rc = 0;
+
+ /*
+ * When no candidates are left,
+ * the outer cycle finishes
+ */
+ assert( ncandidates > 0 );
+ --ncandidates;
+
+ } else if ( rc == LDAP_RES_BIND ) {
+ meta_search_candidate_t retcode;
+
+ retcode = meta_search_dobind_result( op, rs, &mc, i, candidates, msg );
+ if ( retcode == META_SEARCH_CANDIDATE ) {
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ retcode = meta_back_search_start( op, rs, &dc, &mc, i, candidates, NULL, 0 );
+ }
+
+ switch ( retcode ) {
+ case META_SEARCH_CANDIDATE:
+ break;
+
+ /* means that failed but onerr == continue */
+ case META_SEARCH_NOT_CANDIDATE:
+ case META_SEARCH_ERR:
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ assert( ncandidates > 0 );
+ --ncandidates;
+
+ candidates[ i ].sr_err = rs->sr_err;
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ savepriv = op->o_private;
+ op->o_private = (void *)i;
+ send_ldap_result( op, rs );
+ op->o_private = savepriv;
+ ldap_msgfree( res );
+ res = NULL;
+ goto finish;
+ }
+ goto free_message;
+
+ default:
+ assert( 0 );
+ break;
+ }
+
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "%s meta_back_search[%ld]: "
+ "unrecognized response message tag=%d\n",
+ op->o_log_prefix,
+ i, rc );
+
+ ldap_msgfree( res );
+ res = NULL;
+ goto really_bad;
+ }
+ }
+
+free_message:;
+ ldap_msgfree( res );
+ res = NULL;
+ }
+
+ /* check for abandon */
+ if ( op->o_abandon || LDAP_BACK_CONN_ABANDON( mc ) ) {
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ if ( candidates[ i ].sr_msgid >= 0
+ || candidates[ i ].sr_msgid == META_MSGID_CONNECTING )
+ {
+ if ( META_IS_BINDING( &candidates[ i ] )
+ || candidates[ i ].sr_msgid == META_MSGID_CONNECTING )
+ {
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ if ( LDAP_BACK_CONN_BINDING( &mc->mc_conns[ i ] )
+ || candidates[ i ].sr_msgid == META_MSGID_CONNECTING )
+ {
+ /* if still binding, destroy */
+
+#ifdef DEBUG_205
+ Debug(LDAP_DEBUG_ANY,
+ "### %s meta_back_search(abandon) " "ldap_unbind_ext[%ld] mc=%p ld=%p\n",
+ op->o_log_prefix,
+ i, (void *)mc,
+ (void *)mc->mc_conns[i].msc_ld );
+#endif /* DEBUG_205 */
+
+ meta_clear_one_candidate( op, mc, i );
+ }
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ META_BINDING_CLEAR( &candidates[ i ] );
+
+ } else {
+ (void)meta_back_cancel( mc, op, rs,
+ candidates[ i ].sr_msgid, i,
+ LDAP_BACK_DONTSEND );
+ }
+
+ candidates[ i ].sr_msgid = META_MSGID_IGNORE;
+ assert( ncandidates > 0 );
+ --ncandidates;
+ }
+ }
+
+ if ( op->o_abandon ) {
+ rc = SLAPD_ABANDON;
+ }
+
+ /* let send_ldap_result play cleanup handlers (ITS#4645) */
+ break;
+ }
+
+ /* if no entry was found during this loop,
+ * set a minimal timeout */
+ if ( ncandidates > 0 && gotit == 0 ) {
+ if ( save_tv.tv_sec == 0 && save_tv.tv_usec == 0 ) {
+ save_tv.tv_usec = LDAP_BACK_RESULT_UTIMEOUT/initial_candidates;
+
+ /* arbitrarily limit to something between 1 and 2 minutes */
+ } else if ( ( stoptime == -1 && save_tv.tv_sec < 60 )
+ || save_tv.tv_sec < ( stoptime - slap_get_time() ) / ( 2 * ncandidates ) )
+ {
+ /* double the timeout */
+ lutil_timermul( &save_tv, 2, &save_tv );
+ }
+
+ if ( alreadybound == 0 ) {
+ tv = save_tv;
+ (void)select( 0, NULL, NULL, NULL, &tv );
+
+ } else {
+ ldap_pvt_thread_yield();
+ }
+ }
+ }
+
+ if ( rc == -1 ) {
+ /*
+ * FIXME: need a better strategy to handle errors
+ */
+ if ( mc ) {
+ rc = meta_back_op_result( mc, op, rs, META_TARGET_NONE,
+ -1, stoptime != -1 ? (stoptime - slap_get_time()) : 0,
+ LDAP_BACK_SENDERR );
+ } else {
+ rc = rs->sr_err;
+ }
+ goto finish;
+ }
+
+ /*
+ * Rewrite the matched portion of the search base, if required
+ *
+ * FIXME: only the last one gets caught!
+ */
+ savepriv = op->o_private;
+ op->o_private = (void *)(long)mi->mi_ntargets;
+ if ( candidate_match > 0 ) {
+ struct berval pmatched = BER_BVNULL;
+
+ /* we use the first one */
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ if ( META_IS_CANDIDATE( &candidates[ i ] )
+ && candidates[ i ].sr_matched != NULL )
+ {
+ struct berval bv, pbv;
+ int rc;
+
+ /* if we got success, and this target
+ * returned noSuchObject, and its suffix
+ * is a superior of the searchBase,
+ * ignore the matchedDN */
+ if ( sres == LDAP_SUCCESS
+ && candidates[ i ].sr_err == LDAP_NO_SUCH_OBJECT
+ && op->o_req_ndn.bv_len > mi->mi_targets[ i ]->mt_nsuffix.bv_len )
+ {
+ free( (char *)candidates[ i ].sr_matched );
+ candidates[ i ].sr_matched = NULL;
+ continue;
+ }
+
+ ber_str2bv( candidates[ i ].sr_matched, 0, 0, &bv );
+ rc = dnPretty( NULL, &bv, &pbv, op->o_tmpmemctx );
+
+ if ( rc == LDAP_SUCCESS ) {
+
+ /* NOTE: if they all are superiors
+ * of the baseDN, the shorter is also
+ * superior of the longer... */
+ if ( pbv.bv_len > pmatched.bv_len ) {
+ if ( !BER_BVISNULL( &pmatched ) ) {
+ op->o_tmpfree( pmatched.bv_val, op->o_tmpmemctx );
+ }
+ pmatched = pbv;
+ op->o_private = (void *)i;
+
+ } else {
+ op->o_tmpfree( pbv.bv_val, op->o_tmpmemctx );
+ }
+ }
+
+ if ( candidates[ i ].sr_matched != NULL ) {
+ free( (char *)candidates[ i ].sr_matched );
+ candidates[ i ].sr_matched = NULL;
+ }
+ }
+ }
+
+ if ( !BER_BVISNULL( &pmatched ) ) {
+ matched = pmatched.bv_val;
+ }
+
+ } else if ( sres == LDAP_NO_SUCH_OBJECT ) {
+ matched = op->o_bd->be_suffix[ 0 ].bv_val;
+ }
+
+ /*
+ * In case we returned at least one entry, we return LDAP_SUCCESS
+ * otherwise, the latter error code we got
+ */
+
+ if ( sres == LDAP_SUCCESS ) {
+ if ( rs->sr_v2ref ) {
+ sres = LDAP_REFERRAL;
+ }
+
+ if ( META_BACK_ONERR_REPORT( mi ) ) {
+ /*
+ * Report errors, if any
+ *
+ * FIXME: we should handle error codes and return the more
+ * important/reasonable
+ */
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
+ continue;
+ }
+
+ if ( candidates[ i ].sr_err != LDAP_SUCCESS
+ && candidates[ i ].sr_err != LDAP_NO_SUCH_OBJECT )
+ {
+ sres = candidates[ i ].sr_err;
+ break;
+ }
+ }
+ }
+ }
+
+ rs->sr_err = sres;
+ rs->sr_matched = ( sres == LDAP_SUCCESS ? NULL : matched );
+ rs->sr_ref = ( sres == LDAP_REFERRAL ? rs->sr_v2ref : NULL );
+ send_ldap_result( op, rs );
+ op->o_private = savepriv;
+ rs->sr_matched = NULL;
+ rs->sr_ref = NULL;
+
+finish:;
+ if ( matched && matched != op->o_bd->be_suffix[ 0 ].bv_val ) {
+ op->o_tmpfree( matched, op->o_tmpmemctx );
+ }
+
+ if ( rs->sr_v2ref ) {
+ ber_bvarray_free_x( rs->sr_v2ref, op->o_tmpmemctx );
+ }
+
+ for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
+ continue;
+ }
+
+ if ( mc ) {
+ if ( META_IS_BINDING( &candidates[ i ] )
+ || candidates[ i ].sr_msgid == META_MSGID_CONNECTING )
+ {
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ if ( LDAP_BACK_CONN_BINDING( &mc->mc_conns[ i ] )
+ || candidates[ i ].sr_msgid == META_MSGID_CONNECTING )
+ {
+ assert( candidates[ i ].sr_msgid >= 0
+ || candidates[ i ].sr_msgid == META_MSGID_CONNECTING );
+ assert( mc->mc_conns[ i ].msc_ld != NULL );
+
+#ifdef DEBUG_205
+ Debug( LDAP_DEBUG_ANY, "### %s meta_back_search(cleanup) "
+ "ldap_unbind_ext[%ld] ld=%p\n",
+ op->o_log_prefix, i, (void *)mc->mc_conns[i].msc_ld );
+#endif /* DEBUG_205 */
+
+ /* if still binding, destroy */
+ meta_clear_one_candidate( op, mc, i );
+ }
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ META_BINDING_CLEAR( &candidates[ i ] );
+
+ } else if ( candidates[ i ].sr_msgid >= 0 ) {
+ (void)meta_back_cancel( mc, op, rs,
+ candidates[ i ].sr_msgid, i,
+ LDAP_BACK_DONTSEND );
+ }
+ }
+
+ if ( candidates[ i ].sr_matched ) {
+ free( (char *)candidates[ i ].sr_matched );
+ candidates[ i ].sr_matched = NULL;
+ }
+
+ if ( candidates[ i ].sr_text ) {
+ ldap_memfree( (char *)candidates[ i ].sr_text );
+ candidates[ i ].sr_text = NULL;
+ }
+
+ if ( candidates[ i ].sr_ref ) {
+ ber_bvarray_free( candidates[ i ].sr_ref );
+ candidates[ i ].sr_ref = NULL;
+ }
+
+ if ( candidates[ i ].sr_ctrls ) {
+ ldap_controls_free( candidates[ i ].sr_ctrls );
+ candidates[ i ].sr_ctrls = NULL;
+ }
+
+ if ( META_BACK_TGT_QUARANTINE( mi->mi_targets[ i ] ) ) {
+ meta_back_quarantine( op, &candidates[ i ], i );
+ }
+
+ /* only in case of timelimit exceeded, if the timelimit exceeded because
+ * one contacted target never responded, invalidate the connection
+ * NOTE: should we quarantine the target as well? right now, the connection
+ * is invalidated; the next time it will be recreated and the target
+ * will be quarantined if it cannot be contacted */
+ if ( mi->mi_idle_timeout != 0
+ && rs->sr_err == LDAP_TIMELIMIT_EXCEEDED
+ && op->o_time > mc->mc_conns[ i ].msc_time )
+ {
+ /* don't let anyone else use this expired connection */
+ do_taint++;
+ }
+ }
+
+ if ( mc ) {
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ if ( do_taint ) {
+ LDAP_BACK_CONN_TAINTED_SET( mc );
+ }
+ meta_back_release_conn_lock( mi, mc, 0 );
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ }
+
+ return rs->sr_err;
+}
+
+static int
+meta_send_entry(
+ Operation *op,
+ SlapReply *rs,
+ metaconn_t *mc,
+ int target,
+ LDAPMessage *e )
+{
+ metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ struct berval a, mapped;
+ int check_duplicate_attrs = 0;
+ int check_sorted_attrs = 0;
+ Entry ent = { 0 };
+ BerElement ber = *ldap_get_message_ber( e );
+ Attribute *attr, **attrp;
+ struct berval bdn,
+ dn = BER_BVNULL;
+ const char *text;
+ dncookie dc;
+ ber_len_t len;
+ int rc;
+
+ if ( ber_scanf( &ber, "l{", &len ) == LBER_ERROR ) {
+ return LDAP_DECODING_ERROR;
+ }
+
+ if ( ber_set_option( &ber, LBER_OPT_REMAINING_BYTES, &len ) != LBER_OPT_SUCCESS ) {
+ return LDAP_OTHER;
+ }
+
+ if ( ber_scanf( &ber, "m{", &bdn ) == LBER_ERROR ) {
+ return LDAP_DECODING_ERROR;
+ }
+
+ /*
+ * Rewrite the dn of the result, if needed
+ */
+ dc.target = mi->mi_targets[ target ];
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+ dc.ctx = "searchResult";
+
+ rs->sr_err = ldap_back_dn_massage( &dc, &bdn, &dn );
+ if ( rs->sr_err != LDAP_SUCCESS) {
+ return rs->sr_err;
+ }
+
+ /*
+ * Note: this may fail if the target host(s) schema differs
+ * from the one known to the meta, and a DN with unknown
+ * attributes is returned.
+ *
+ * FIXME: should we log anything, or delegate to dnNormalize?
+ */
+ rc = dnPrettyNormal( NULL, &dn, &ent.e_name, &ent.e_nname,
+ op->o_tmpmemctx );
+ if ( dn.bv_val != bdn.bv_val ) {
+ free( dn.bv_val );
+ }
+ BER_BVZERO( &dn );
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s meta_send_entry(\"%s\"): "
+ "invalid DN syntax\n",
+ op->o_log_prefix, ent.e_name.bv_val );
+ rc = LDAP_INVALID_DN_SYNTAX;
+ goto done;
+ }
+
+ /*
+ * cache dn
+ */
+ if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
+ ( void )meta_dncache_update_entry( &mi->mi_cache,
+ &ent.e_nname, target );
+ }
+
+ attrp = &ent.e_attrs;
+
+ dc.ctx = "searchAttrDN";
+ while ( ber_scanf( &ber, "{m", &a ) != LBER_ERROR ) {
+ int last = 0;
+ slap_syntax_validate_func *validate;
+ slap_syntax_transform_func *pretty;
+
+ if ( ber_pvt_ber_remaining( &ber ) < 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s meta_send_entry(\"%s\"): "
+ "unable to parse attr \"%s\".\n",
+ op->o_log_prefix, ent.e_name.bv_val, a.bv_val );
+
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ if ( ber_pvt_ber_remaining( &ber ) == 0 ) {
+ break;
+ }
+
+ ldap_back_map( &mi->mi_targets[ target ]->mt_rwmap.rwm_at,
+ &a, &mapped, BACKLDAP_REMAP );
+ if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0' ) {
+ ( void )ber_scanf( &ber, "x" /* [W] */ );
+ continue;
+ }
+ if ( mapped.bv_val != a.bv_val ) {
+ /* will need to check for duplicate attrs */
+ check_duplicate_attrs++;
+ }
+ attr = attr_alloc( NULL );
+ if ( attr == NULL ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ if ( slap_bv2ad( &mapped, &attr->a_desc, &text )
+ != LDAP_SUCCESS) {
+ if ( slap_bv2undef_ad( &mapped, &attr->a_desc, &text,
+ SLAP_AD_PROXIED ) != LDAP_SUCCESS )
+ {
+ Debug(LDAP_DEBUG_ANY,
+ "%s meta_send_entry(\"%s\"): " "slap_bv2undef_ad(%s): %s\n",
+ op->o_log_prefix, ent.e_name.bv_val,
+ mapped.bv_val, text );
+ ( void )ber_scanf( &ber, "x" /* [W] */ );
+ attr_free( attr );
+ continue;
+ }
+ }
+
+ if ( attr->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL )
+ check_sorted_attrs = 1;
+
+ /* no subschemaSubentry */
+ if ( attr->a_desc == slap_schema.si_ad_subschemaSubentry
+ || attr->a_desc == slap_schema.si_ad_entryDN )
+ {
+
+ /*
+ * We eat target's subschemaSubentry because
+ * a search for this value is likely not
+ * to resolve to the appropriate backend;
+ * later, the local subschemaSubentry is
+ * added.
+ *
+ * We also eat entryDN because the frontend
+ * will reattach it without checking if already
+ * present...
+ */
+ ( void )ber_scanf( &ber, "x" /* [W] */ );
+ attr_free(attr);
+ continue;
+ }
+
+ if ( ber_scanf( &ber, "[W]", &attr->a_vals ) == LBER_ERROR
+ || attr->a_vals == NULL )
+ {
+ attr->a_vals = (struct berval *)&slap_dummy_bv;
+
+ } else {
+ for ( last = 0; !BER_BVISNULL( &attr->a_vals[ last ] ); ++last )
+ ;
+ }
+ attr->a_numvals = last;
+
+ validate = attr->a_desc->ad_type->sat_syntax->ssyn_validate;
+ pretty = attr->a_desc->ad_type->sat_syntax->ssyn_pretty;
+
+ if ( !validate && !pretty ) {
+ attr_free( attr );
+ goto next_attr;
+ }
+
+ if ( attr->a_desc == slap_schema.si_ad_objectClass
+ || attr->a_desc == slap_schema.si_ad_structuralObjectClass )
+ {
+ struct berval *bv;
+
+ for ( bv = attr->a_vals; !BER_BVISNULL( bv ); bv++ ) {
+ ObjectClass *oc;
+
+ ldap_back_map( &mi->mi_targets[ target ]->mt_rwmap.rwm_oc,
+ bv, &mapped, BACKLDAP_REMAP );
+ if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0') {
+remove_oc:;
+ free( bv->bv_val );
+ BER_BVZERO( bv );
+ if ( --last < 0 ) {
+ break;
+ }
+ *bv = attr->a_vals[ last ];
+ BER_BVZERO( &attr->a_vals[ last ] );
+ bv--;
+
+ } else if ( mapped.bv_val != bv->bv_val ) {
+ int i;
+
+ for ( i = 0; !BER_BVISNULL( &attr->a_vals[ i ] ); i++ ) {
+ if ( &attr->a_vals[ i ] == bv ) {
+ continue;
+ }
+
+ if ( ber_bvstrcasecmp( &mapped, &attr->a_vals[ i ] ) == 0 ) {
+ break;
+ }
+ }
+
+ if ( !BER_BVISNULL( &attr->a_vals[ i ] ) ) {
+ goto remove_oc;
+ }
+
+ ber_bvreplace( bv, &mapped );
+
+ } else if ( ( oc = oc_bvfind_undef( bv ) ) == NULL ) {
+ goto remove_oc;
+
+ } else {
+ ber_bvreplace( bv, &oc->soc_cname );
+ }
+ }
+ /*
+ * It is necessary to try to rewrite attributes with
+ * dn syntax because they might be used in ACLs as
+ * members of groups; since ACLs are applied to the
+ * rewritten stuff, no dn-based subecj clause could
+ * be used at the ldap backend side (see
+ * http://www.OpenLDAP.org/faq/data/cache/452.html)
+ * The problem can be overcome by moving the dn-based
+ * ACLs to the target directory server, and letting
+ * everything pass thru the ldap backend.
+ */
+ } else {
+ int i;
+
+ if ( attr->a_desc->ad_type->sat_syntax ==
+ slap_schema.si_syn_distinguishedName )
+ {
+ ldap_dnattr_result_rewrite( &dc, attr->a_vals );
+
+ } else if ( attr->a_desc == slap_schema.si_ad_ref ) {
+ ldap_back_referral_result_rewrite( &dc, attr->a_vals, NULL );
+
+ }
+
+ for ( i = 0; i < last; i++ ) {
+ struct berval pval;
+ int rc;
+
+ if ( pretty ) {
+ rc = ordered_value_pretty( attr->a_desc,
+ &attr->a_vals[i], &pval, NULL );
+
+ } else {
+ rc = ordered_value_validate( attr->a_desc,
+ &attr->a_vals[i], 0 );
+ }
+
+ if ( rc ) {
+ ber_memfree( attr->a_vals[i].bv_val );
+ if ( --last == i ) {
+ BER_BVZERO( &attr->a_vals[ i ] );
+ break;
+ }
+ attr->a_vals[i] = attr->a_vals[last];
+ BER_BVZERO( &attr->a_vals[last] );
+ i--;
+ continue;
+ }
+
+ if ( pretty ) {
+ ber_memfree( attr->a_vals[i].bv_val );
+ attr->a_vals[i] = pval;
+ }
+ }
+
+ if ( last == 0 && attr->a_vals != &slap_dummy_bv ) {
+ attr_free( attr );
+ goto next_attr;
+ }
+ }
+
+ if ( last && attr->a_desc->ad_type->sat_equality &&
+ attr->a_desc->ad_type->sat_equality->smr_normalize )
+ {
+ int i;
+
+ attr->a_nvals = ch_malloc( ( last + 1 ) * sizeof( struct berval ) );
+ for ( i = 0; i<last; i++ ) {
+ /* if normalizer fails, drop this value */
+ if ( ordered_value_normalize(
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ attr->a_desc,
+ attr->a_desc->ad_type->sat_equality,
+ &attr->a_vals[i], &attr->a_nvals[i],
+ NULL )) {
+ ber_memfree( attr->a_vals[i].bv_val );
+ if ( --last == i ) {
+ BER_BVZERO( &attr->a_vals[ i ] );
+ break;
+ }
+ attr->a_vals[i] = attr->a_vals[last];
+ BER_BVZERO( &attr->a_vals[last] );
+ i--;
+ }
+ }
+ BER_BVZERO( &attr->a_nvals[i] );
+ if ( last == 0 ) {
+ attr_free( attr );
+ goto next_attr;
+ }
+
+ } else {
+ attr->a_nvals = attr->a_vals;
+ }
+
+ attr->a_numvals = last;
+ *attrp = attr;
+ attrp = &attr->a_next;
+next_attr:;
+ }
+
+ /* only check if some mapping occurred */
+ if ( check_duplicate_attrs ) {
+ Attribute **ap;
+
+ for ( ap = &ent.e_attrs; *ap != NULL; ap = &(*ap)->a_next ) {
+ Attribute **tap;
+
+ for ( tap = &(*ap)->a_next; *tap != NULL; ) {
+ if ( (*tap)->a_desc == (*ap)->a_desc ) {
+ Entry e = { 0 };
+ Modification mod = { 0 };
+ const char *text = NULL;
+ char textbuf[ SLAP_TEXT_BUFLEN ];
+ Attribute *next = (*tap)->a_next;
+
+ BER_BVSTR( &e.e_name, "" );
+ BER_BVSTR( &e.e_nname, "" );
+ e.e_attrs = *ap;
+ mod.sm_op = LDAP_MOD_ADD;
+ mod.sm_desc = (*ap)->a_desc;
+ mod.sm_type = mod.sm_desc->ad_cname;
+ mod.sm_numvals = (*ap)->a_numvals;
+ mod.sm_values = (*tap)->a_vals;
+ if ( (*tap)->a_nvals != (*tap)->a_vals ) {
+ mod.sm_nvalues = (*tap)->a_nvals;
+ }
+
+ (void)modify_add_values( &e, &mod,
+ /* permissive */ 1,
+ &text, textbuf, sizeof( textbuf ) );
+
+ /* should not insert new attrs! */
+ assert( e.e_attrs == *ap );
+
+ attr_free( *tap );
+ *tap = next;
+
+ } else {
+ tap = &(*tap)->a_next;
+ }
+ }
+ }
+ }
+
+ /* Check for sorted attributes */
+ if ( check_sorted_attrs ) {
+ for ( attr = ent.e_attrs; attr; attr = attr->a_next ) {
+ if ( attr->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL ) {
+ while ( attr->a_numvals > 1 ) {
+ int i;
+ int rc = slap_sort_vals( (Modifications *)attr, &text, &i, op->o_tmpmemctx );
+ if ( rc != LDAP_TYPE_OR_VALUE_EXISTS )
+ break;
+
+ /* Strip duplicate values */
+ if ( attr->a_nvals != attr->a_vals )
+ ber_memfree( attr->a_nvals[i].bv_val );
+ ber_memfree( attr->a_vals[i].bv_val );
+ attr->a_numvals--;
+ if ( (unsigned)i < attr->a_numvals ) {
+ attr->a_vals[i] = attr->a_vals[attr->a_numvals];
+ if ( attr->a_nvals != attr->a_vals )
+ attr->a_nvals[i] = attr->a_nvals[attr->a_numvals];
+ }
+ BER_BVZERO(&attr->a_vals[attr->a_numvals]);
+ if ( attr->a_nvals != attr->a_vals )
+ BER_BVZERO(&attr->a_nvals[attr->a_numvals]);
+ }
+ attr->a_flags |= SLAP_ATTR_SORTED_VALS;
+ }
+ }
+ }
+
+ ldap_get_entry_controls( mc->mc_conns[target].msc_ld,
+ e, &rs->sr_ctrls );
+ rs->sr_entry = &ent;
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_operational_attrs = NULL;
+ rs->sr_flags = mi->mi_targets[ target ]->mt_rep_flags;
+ rs->sr_err = LDAP_SUCCESS;
+ rc = send_search_entry( op, rs );
+ switch ( rc ) {
+ case LDAP_UNAVAILABLE:
+ rc = LDAP_OTHER;
+ break;
+ }
+
+done:;
+ rs->sr_entry = NULL;
+ rs->sr_attrs = NULL;
+ if ( rs->sr_ctrls != NULL ) {
+ ldap_controls_free( rs->sr_ctrls );
+ rs->sr_ctrls = NULL;
+ }
+ if ( !BER_BVISNULL( &ent.e_name ) ) {
+ free( ent.e_name.bv_val );
+ BER_BVZERO( &ent.e_name );
+ }
+ if ( !BER_BVISNULL( &ent.e_nname ) ) {
+ free( ent.e_nname.bv_val );
+ BER_BVZERO( &ent.e_nname );
+ }
+ entry_clean( &ent );
+
+ return rc;
+}
+
diff --git a/servers/slapd/back-meta/suffixmassage.c b/servers/slapd/back-meta/suffixmassage.c
new file mode 100644
index 0000000..8d0b5e7
--- /dev/null
+++ b/servers/slapd/back-meta/suffixmassage.c
@@ -0,0 +1,110 @@
+/* suffixmassage.c - massages ldap backend dns */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+/* This is an altered version */
+
+/*
+ * Copyright 1999, Howard Chu, All rights reserved. <hyc@highlandsun.com>
+ * Copyright 2000, Pierangelo Masarati, All rights reserved. <ando@sys-net.it>
+ *
+ * Module back-ldap, originally developed by Howard Chu
+ *
+ * has been modified by Pierangelo Masarati. The original copyright
+ * notice has been maintained.
+ *
+ * Permission is granted to anyone to use this software for any purpose
+ * on any computer system, and to alter it and redistribute it, subject
+ * to the following restrictions:
+ *
+ * 1. The author is not responsible for the consequences of use of this
+ * software, no matter how awful, even if they arise from flaws in it.
+ *
+ * 2. The origin of this software must not be misrepresented, either by
+ * explicit claim or by omission. Since few users ever read sources,
+ * credits should appear in the documentation.
+ *
+ * 3. Altered versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software. Since few users
+ * ever read sources, credits should appear in the documentation.
+ *
+ * 4. This notice may not be removed or altered.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+int
+ldap_back_dn_massage(
+ dncookie *dc,
+ struct berval *dn,
+ struct berval *res )
+{
+ int rc = 0;
+ static char *dmy = "";
+
+ switch ( rewrite_session( dc->target->mt_rwmap.rwm_rw, dc->ctx,
+ ( dn->bv_val ? dn->bv_val : dmy ),
+ dc->conn, &res->bv_val ) )
+ {
+ case REWRITE_REGEXEC_OK:
+ if ( res->bv_val != NULL ) {
+ res->bv_len = strlen( res->bv_val );
+ } else {
+ *res = *dn;
+ }
+ Debug( LDAP_DEBUG_ARGS,
+ "[rw] %s: \"%s\" -> \"%s\"\n",
+ dc->ctx,
+ BER_BVISNULL( dn ) ? "" : dn->bv_val,
+ BER_BVISNULL( res ) ? "" : res->bv_val );
+ rc = LDAP_SUCCESS;
+ break;
+
+ case REWRITE_REGEXEC_UNWILLING:
+ if ( dc->rs ) {
+ dc->rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ dc->rs->sr_text = "Operation not allowed";
+ }
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ break;
+
+ case REWRITE_REGEXEC_ERR:
+ if ( dc->rs ) {
+ dc->rs->sr_err = LDAP_OTHER;
+ dc->rs->sr_text = "Rewrite error";
+ }
+ rc = LDAP_OTHER;
+ break;
+ }
+
+ if ( res->bv_val == dmy ) {
+ BER_BVZERO( res );
+ }
+
+ return rc;
+}
diff --git a/servers/slapd/back-meta/unbind.c b/servers/slapd/back-meta/unbind.c
new file mode 100644
index 0000000..6c2928d
--- /dev/null
+++ b/servers/slapd/back-meta/unbind.c
@@ -0,0 +1,89 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "../back-ldap/back-ldap.h"
+#include "back-meta.h"
+
+int
+meta_back_conn_destroy(
+ Backend *be,
+ Connection *conn )
+{
+ metainfo_t *mi = ( metainfo_t * )be->be_private;
+ metaconn_t *mc,
+ mc_curr = {{ 0 }};
+ int i;
+
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=>meta_back_conn_destroy: fetching conn=%ld DN=\"%s\"\n",
+ conn->c_connid,
+ BER_BVISNULL( &conn->c_ndn ) ? "" : conn->c_ndn.bv_val );
+
+ mc_curr.mc_conn = conn;
+
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+#if META_BACK_PRINT_CONNTREE > 0
+ meta_back_print_conntree( mi, ">>> meta_back_conn_destroy" );
+#endif /* META_BACK_PRINT_CONNTREE */
+ while ( ( mc = ldap_tavl_delete( &mi->mi_conninfo.lai_tree, ( caddr_t )&mc_curr, meta_back_conn_cmp ) ) != NULL )
+ {
+ assert( !LDAP_BACK_PCONN_ISPRIV( mc ) );
+ Debug( LDAP_DEBUG_TRACE,
+ "=>meta_back_conn_destroy: destroying conn %lu "
+ "refcnt=%d flags=0x%08x\n",
+ mc->mc_conn->c_connid, mc->mc_refcnt, mc->msc_mscflags );
+
+ if ( mc->mc_refcnt > 0 ) {
+ /* someone else might be accessing the connection;
+ * mark for deletion */
+ LDAP_BACK_CONN_CACHED_CLEAR( mc );
+ LDAP_BACK_CONN_TAINTED_SET( mc );
+
+ } else {
+ meta_back_conn_free( mc );
+ }
+ }
+#if META_BACK_PRINT_CONNTREE > 0
+ meta_back_print_conntree( mi, "<<< meta_back_conn_destroy" );
+#endif /* META_BACK_PRINT_CONNTREE */
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+
+ /*
+ * Cleanup rewrite session
+ */
+ for ( i = 0; i < mi->mi_ntargets; ++i ) {
+ rewrite_session_delete( mi->mi_targets[ i ]->mt_rwmap.rwm_rw, conn );
+ }
+
+ return 0;
+}
+
diff --git a/servers/slapd/back-monitor/Makefile.in b/servers/slapd/back-monitor/Makefile.in
new file mode 100644
index 0000000..e3b30a0
--- /dev/null
+++ b/servers/slapd/back-monitor/Makefile.in
@@ -0,0 +1,49 @@
+# Makefile.in for back-monitor
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SRCS = init.c search.c compare.c modify.c bind.c \
+ operational.c \
+ cache.c entry.c \
+ backend.c database.c thread.c conn.c rww.c log.c \
+ operation.c sent.c listener.c time.c overlay.c
+OBJS = init.lo search.lo compare.lo modify.lo bind.lo \
+ operational.lo \
+ cache.lo entry.lo \
+ backend.lo database.lo thread.lo conn.lo rww.lo log.lo \
+ operation.lo sent.lo listener.lo time.lo overlay.lo
+
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-monitor"
+BUILD_MOD = yes
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = $(yes_DEFS)
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+
+LIBBASE = back_monitor
+
+XINCPATH = -I.. -I$(srcdir)/.. -I$(srcdir)/../slapi
+XDEFS = $(MODULES_CPPFLAGS)
+
+all-local-lib: ../.backend
+
+../.backend: lib$(LIBBASE).a
+ @touch $@
+
diff --git a/servers/slapd/back-monitor/README b/servers/slapd/back-monitor/README
new file mode 100644
index 0000000..38dc360
--- /dev/null
+++ b/servers/slapd/back-monitor/README
@@ -0,0 +1,243 @@
+MONITOR BACKEND
+
+ NAME: back-monitor
+
+ Backend for monitoring the server's activity.
+
+
+
+COMPILE AND CONFIGURATION OPTIONS
+
+It must be explicitly enabled by configuring with
+
+ --enable-monitor
+
+set; then it must be activated by placing in slapd.conf the database
+configure directive
+
+ database monitor
+
+The suffix "cn=Monitor" is implicitly activated (it cannot be given
+as a suffix of the database as usually done for conventional backends).
+Note that the "cn=Monitor" naming context appears in the rootDSE
+in the attribute monitorContext
+
+A bind operation is provided; at present it allows to bind as the
+backend rootdn. As a result, the backend supports the rootdn/rootpw
+directives (only simple bind at present).
+
+
+
+NAMING CONTEXT AND TREE STRUCTURE
+
+The backend naming context is "cn=Monitor"; the first level entries
+represent the monitored subsystems. It is implemented in a modular way,
+to ease the addition of new subsystems.
+
+
+
+SCHEMA
+
+All the subsystems get a default "cn" attribute, represented by the
+subsystem's name, and they all have "top", "monitor" and "extensibleObject"
+objectclasses.
+"extensibleObject" is used, and the "description" attribute
+is used to hold the monitor information of each entry.
+
+
+
+FUNCTIONALITY
+
+Most of the subsystems contain an additional depth level, represented
+by detailed item monitoring.
+All the entries undergo an update operation, if a related method is
+defined, prior to being returned. Moreover, there's a mechanism to
+allow volatile entries to be defined, and generated on the fly when
+requested. As an instance, the connection statistics are updated
+at each request, while each active connection data is created on the
+fly.
+
+One nice feature of this solution is that granular ACLs can be applied
+to each entry.
+
+
+
+OPERATIONS
+
+The backend currently supports:
+
+ bind
+ compare
+ modify
+ search
+
+
+
+SUBSYSTEMS
+
+Currently some subsystems are partially supported. "Partially"
+means their entries are correctly generated, but sometimes only
+partially useful information is provided.
+
+The subsystems are:
+
+ Backends
+ Connections
+ Databases
+ Listener
+ Log
+ Operations
+ Overlays
+ SASL
+ Statistics
+ Threads
+ Time
+ TLS
+ Read/Write Waiters
+
+
+
+BACKENDS SUBSYSTEMS
+
+The main entry contains the type of backends enabled at compile time;
+the subentries, for each backend, contain the type of the backend.
+It should also contain the modules that have been loaded if dynamic
+backends are enabled.
+
+
+
+CONNECTIONS
+
+The main entry is empty; it should contain some statistics on the number
+of connections.
+Dynamic subentries are created for each open connection, with stats on
+the activity on that connection (the format will be detailed later).
+There are two special subentries that show the number of total and
+current connections respectively.
+
+
+
+DATABASES SUBSYSTEM
+
+The main entry contains the naming context of each configured database;
+the subentries contain, for each database, the type and the naming
+context.
+
+
+
+LISTENER SUBSYSTEM
+
+It contains the description of the devices the server is currently
+listening on
+
+
+
+LOG SUBSYSTEM
+
+It contains the currently active log items. The "Log" subsystem allows
+user modify operations on the "description" attribute, whose values MUST
+be in the list of admittable log switches:
+
+ Trace
+ Packets
+ Args
+ Conns
+ BER
+ Filter
+ Config (useless)
+ ACL
+ Stats
+ Stats2
+ Shell
+ Parse
+ Cache (deprecated)
+ Index
+
+These values can be added, replaced or deleted; they affect what
+messages are sent to the syslog device.
+
+
+
+OPERATIONS SUBSYSTEM
+
+It shows some statistics on the operations performed by the server:
+
+ Initiated
+ Completed
+
+and for each operation type, i.e.:
+
+ Bind
+ Unbind
+ Add
+ Delete
+ Modrdn
+ Modify
+ Compare
+ Search
+ Abandon
+ Extended
+
+
+
+OVERLAYS SUBSYSTEM
+
+The main entry contains the type of overlays available at run-time;
+the subentries, for each overlay, contain the type of the overlay.
+It should also contain the modules that have been loaded if dynamic
+overlays are enabled.
+
+
+
+SASL
+
+Currently empty.
+
+
+
+STATISTICS SUBSYSTEM
+
+It shows some statistics on the data sent by the server:
+
+ Bytes
+ PDU
+ Entries
+ Referrals
+
+
+
+THREADS SUBSYSTEM
+
+It contains the maximum number of threads enabled at startup and the
+current backload.
+
+
+
+TIME SUBSYSTEM
+
+It contains two subentries with the start time and the current time
+of the server.
+
+
+
+TLS
+
+Currently empty.
+
+
+
+READ/WRITE WAITERS SUBSYSTEM
+
+It contains the number of current read waiters.
+
+
+
+NOTES
+
+This document is in a very early stage of maturity and will
+probably be rewritten many times before the monitor backend is released.
+
+
+
+AUTHOR: Pierangelo Masarati <ando@OpenLDAP.org>
+
diff --git a/servers/slapd/back-monitor/back-monitor.h b/servers/slapd/back-monitor/back-monitor.h
new file mode 100644
index 0000000..272a551
--- /dev/null
+++ b/servers/slapd/back-monitor/back-monitor.h
@@ -0,0 +1,327 @@
+/* back-monitor.h - ldap monitor back-end header file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#ifndef _BACK_MONITOR_H_
+#define _BACK_MONITOR_H_
+
+#include <ldap_pvt.h>
+#include <ldap_pvt_thread.h>
+#include <ldap_avl.h>
+#include <slap.h>
+
+LDAP_BEGIN_DECL
+
+/* define if si_ad_labeledURI is removed from slap_schema */
+#undef MONITOR_DEFINE_LABELEDURI
+
+typedef struct monitor_callback_t {
+ int (*mc_update)( Operation *op, SlapReply *rs, Entry *e, void *priv );
+ /* update callback
+ for user-defined entries */
+ int (*mc_modify)( Operation *op, SlapReply *rs, Entry *e, void *priv );
+ /* modify callback
+ for user-defined entries */
+ int (*mc_free)( Entry *e, void **priv );
+ /* delete callback
+ for user-defined entries */
+ void (*mc_dispose)( void **priv );
+ /* dispose callback
+ to dispose of the callback
+ private data itself */
+ void *mc_private; /* opaque pointer to
+ private data */
+ struct monitor_callback_t *mc_next;
+} monitor_callback_t;
+
+
+typedef struct monitor_entry_t {
+ ldap_pvt_thread_mutex_t mp_mutex; /* entry mutex */
+ Entry *mp_next; /* pointer to next sibling */
+ Entry *mp_children; /* pointer to first child */
+ struct monitor_subsys_t *mp_info; /* subsystem info */
+#define mp_type mp_info->mss_type
+ unsigned long mp_flags; /* flags */
+
+#define MONITOR_F_NONE 0x0000U
+#define MONITOR_F_SUB 0x0001U /* subentry of subsystem */
+#define MONITOR_F_PERSISTENT 0x0010U /* persistent entry */
+#define MONITOR_F_PERSISTENT_CH 0x0020U /* subsystem generates
+ persistent entries */
+#define MONITOR_F_VOLATILE 0x0040U /* volatile entry */
+#define MONITOR_F_VOLATILE_CH 0x0080U /* subsystem generates
+ volatile entries */
+#define MONITOR_F_EXTERNAL 0x0100U /* externally added - don't free */
+/* NOTE: flags with 0xF0000000U mask are reserved for subsystem internals */
+
+ struct monitor_callback_t *mp_cb; /* callback sequence */
+ void *mp_private;
+} monitor_entry_t;
+
+struct entry_limbo_t; /* in init.c */
+
+typedef struct monitor_info_t {
+
+ /*
+ * Internal data
+ */
+ Avlnode *mi_cache;
+ ldap_pvt_thread_mutex_t mi_cache_mutex;
+
+ /*
+ * Config parameters
+ */
+ struct berval mi_startTime; /* don't free it! */
+ struct berval mi_creatorsName; /* don't free it! */
+ struct berval mi_ncreatorsName; /* don't free it! */
+
+ /*
+ * Specific schema entities
+ */
+ ObjectClass *mi_oc_monitor;
+ ObjectClass *mi_oc_monitorServer;
+ ObjectClass *mi_oc_monitorContainer;
+ ObjectClass *mi_oc_monitorCounterObject;
+ ObjectClass *mi_oc_monitorOperation;
+ ObjectClass *mi_oc_monitorConnection;
+ ObjectClass *mi_oc_managedObject;
+ ObjectClass *mi_oc_monitoredObject;
+
+ AttributeDescription *mi_ad_monitoredInfo;
+ AttributeDescription *mi_ad_managedInfo;
+ AttributeDescription *mi_ad_monitorCounter;
+ AttributeDescription *mi_ad_monitorOpCompleted;
+ AttributeDescription *mi_ad_monitorOpInitiated;
+ AttributeDescription *mi_ad_monitorConnectionNumber;
+ AttributeDescription *mi_ad_monitorConnectionAuthzDN;
+ AttributeDescription *mi_ad_monitorConnectionLocalAddress;
+ AttributeDescription *mi_ad_monitorConnectionPeerAddress;
+ AttributeDescription *mi_ad_monitorTimestamp;
+ AttributeDescription *mi_ad_monitorOverlay;
+ AttributeDescription *mi_ad_monitorConnectionProtocol;
+ AttributeDescription *mi_ad_monitorConnectionOpsReceived;
+ AttributeDescription *mi_ad_monitorConnectionOpsExecuting;
+ AttributeDescription *mi_ad_monitorConnectionOpsPending;
+ AttributeDescription *mi_ad_monitorConnectionOpsCompleted;
+ AttributeDescription *mi_ad_monitorConnectionGet;
+ AttributeDescription *mi_ad_monitorConnectionRead;
+ AttributeDescription *mi_ad_monitorConnectionWrite;
+ AttributeDescription *mi_ad_monitorConnectionMask;
+ AttributeDescription *mi_ad_monitorConnectionListener;
+ AttributeDescription *mi_ad_monitorConnectionPeerDomain;
+ AttributeDescription *mi_ad_monitorConnectionStartTime;
+ AttributeDescription *mi_ad_monitorConnectionActivityTime;
+ AttributeDescription *mi_ad_monitorIsShadow;
+ AttributeDescription *mi_ad_monitorUpdateRef;
+ AttributeDescription *mi_ad_monitorRuntimeConfig;
+ AttributeDescription *mi_ad_monitorSuperiorDN;
+ AttributeDescription *mi_ad_monitorConnectionOpsAsync;
+
+ /*
+ * Generic description attribute
+ */
+ AttributeDescription *mi_ad_readOnly;
+ AttributeDescription *mi_ad_restrictedOperation;
+
+ struct entry_limbo_t *mi_entry_limbo;
+} monitor_info_t;
+
+/*
+ * DNs
+ */
+
+enum {
+ SLAPD_MONITOR_BACKEND = 0,
+ SLAPD_MONITOR_CONN,
+ SLAPD_MONITOR_DATABASE,
+ SLAPD_MONITOR_LISTENER,
+ SLAPD_MONITOR_LOG,
+ SLAPD_MONITOR_OPS,
+ SLAPD_MONITOR_OVERLAY,
+ SLAPD_MONITOR_SASL,
+ SLAPD_MONITOR_SENT,
+ SLAPD_MONITOR_THREAD,
+ SLAPD_MONITOR_TIME,
+ SLAPD_MONITOR_TLS,
+ SLAPD_MONITOR_RWW,
+
+ SLAPD_MONITOR_LAST
+};
+
+#define SLAPD_MONITOR_AT "cn"
+
+#define SLAPD_MONITOR_BACKEND_NAME "Backends"
+#define SLAPD_MONITOR_BACKEND_RDN \
+ SLAPD_MONITOR_AT "=" SLAPD_MONITOR_BACKEND_NAME
+#define SLAPD_MONITOR_BACKEND_DN \
+ SLAPD_MONITOR_BACKEND_RDN "," SLAPD_MONITOR_DN
+
+#define SLAPD_MONITOR_CONN_NAME "Connections"
+#define SLAPD_MONITOR_CONN_RDN \
+ SLAPD_MONITOR_AT "=" SLAPD_MONITOR_CONN_NAME
+#define SLAPD_MONITOR_CONN_DN \
+ SLAPD_MONITOR_CONN_RDN "," SLAPD_MONITOR_DN
+
+#define SLAPD_MONITOR_DATABASE_NAME "Databases"
+#define SLAPD_MONITOR_DATABASE_RDN \
+ SLAPD_MONITOR_AT "=" SLAPD_MONITOR_DATABASE_NAME
+#define SLAPD_MONITOR_DATABASE_DN \
+ SLAPD_MONITOR_DATABASE_RDN "," SLAPD_MONITOR_DN
+
+#define SLAPD_MONITOR_LISTENER_NAME "Listeners"
+#define SLAPD_MONITOR_LISTENER_RDN \
+ SLAPD_MONITOR_AT "=" SLAPD_MONITOR_LISTENER_NAME
+#define SLAPD_MONITOR_LISTENER_DN \
+ SLAPD_MONITOR_LISTENER_RDN "," SLAPD_MONITOR_DN
+
+#define SLAPD_MONITOR_LOG_NAME "Log"
+#define SLAPD_MONITOR_LOG_RDN \
+ SLAPD_MONITOR_AT "=" SLAPD_MONITOR_LOG_NAME
+#define SLAPD_MONITOR_LOG_DN \
+ SLAPD_MONITOR_LOG_RDN "," SLAPD_MONITOR_DN
+
+#define SLAPD_MONITOR_OPS_NAME "Operations"
+#define SLAPD_MONITOR_OPS_RDN \
+ SLAPD_MONITOR_AT "=" SLAPD_MONITOR_OPS_NAME
+#define SLAPD_MONITOR_OPS_DN \
+ SLAPD_MONITOR_OPS_RDN "," SLAPD_MONITOR_DN
+
+#define SLAPD_MONITOR_OVERLAY_NAME "Overlays"
+#define SLAPD_MONITOR_OVERLAY_RDN \
+ SLAPD_MONITOR_AT "=" SLAPD_MONITOR_OVERLAY_NAME
+#define SLAPD_MONITOR_OVERLAY_DN \
+ SLAPD_MONITOR_OVERLAY_RDN "," SLAPD_MONITOR_DN
+
+#define SLAPD_MONITOR_SASL_NAME "SASL"
+#define SLAPD_MONITOR_SASL_RDN \
+ SLAPD_MONITOR_AT "=" SLAPD_MONITOR_SASL_NAME
+#define SLAPD_MONITOR_SASL_DN \
+ SLAPD_MONITOR_SASL_RDN "," SLAPD_MONITOR_DN
+
+#define SLAPD_MONITOR_SENT_NAME "Statistics"
+#define SLAPD_MONITOR_SENT_RDN \
+ SLAPD_MONITOR_AT "=" SLAPD_MONITOR_SENT_NAME
+#define SLAPD_MONITOR_SENT_DN \
+ SLAPD_MONITOR_SENT_RDN "," SLAPD_MONITOR_DN
+
+#define SLAPD_MONITOR_THREAD_NAME "Threads"
+#define SLAPD_MONITOR_THREAD_RDN \
+ SLAPD_MONITOR_AT "=" SLAPD_MONITOR_THREAD_NAME
+#define SLAPD_MONITOR_THREAD_DN \
+ SLAPD_MONITOR_THREAD_RDN "," SLAPD_MONITOR_DN
+
+#define SLAPD_MONITOR_TIME_NAME "Time"
+#define SLAPD_MONITOR_TIME_RDN \
+ SLAPD_MONITOR_AT "=" SLAPD_MONITOR_TIME_NAME
+#define SLAPD_MONITOR_TIME_DN \
+ SLAPD_MONITOR_TIME_RDN "," SLAPD_MONITOR_DN
+
+#define SLAPD_MONITOR_TLS_NAME "TLS"
+#define SLAPD_MONITOR_TLS_RDN \
+ SLAPD_MONITOR_AT "=" SLAPD_MONITOR_TLS_NAME
+#define SLAPD_MONITOR_TLS_DN \
+ SLAPD_MONITOR_TLS_RDN "," SLAPD_MONITOR_DN
+
+#define SLAPD_MONITOR_RWW_NAME "Waiters"
+#define SLAPD_MONITOR_RWW_RDN \
+ SLAPD_MONITOR_AT "=" SLAPD_MONITOR_RWW_NAME
+#define SLAPD_MONITOR_RWW_DN \
+ SLAPD_MONITOR_RWW_RDN "," SLAPD_MONITOR_DN
+
+typedef struct monitor_subsys_t {
+ char *mss_name;
+ struct berval mss_rdn;
+ struct berval mss_dn;
+ struct berval mss_ndn;
+ struct berval mss_desc[ 3 ];
+ int mss_flags;
+#define MONITOR_F_OPENED 0x10000000U
+
+#define MONITOR_HAS_VOLATILE_CH( mp ) \
+ ( ( mp )->mp_flags & MONITOR_F_VOLATILE_CH )
+#define MONITOR_HAS_CHILDREN( mp ) \
+ ( ( mp )->mp_children || MONITOR_HAS_VOLATILE_CH( mp ) )
+
+ /* initialize entry and subentries */
+ int ( *mss_open )( BackendDB *, struct monitor_subsys_t *ms );
+ /* destroy structure */
+ int ( *mss_destroy )( BackendDB *, struct monitor_subsys_t *ms );
+ /* update existing dynamic entry and subentries */
+ int ( *mss_update )( Operation *, SlapReply *, Entry * );
+ /* create new dynamic subentries */
+ int ( *mss_create )( Operation *, SlapReply *,
+ struct berval *ndn, Entry *, Entry ** );
+ /* modify entry and subentries */
+ int ( *mss_modify )( Operation *, SlapReply *, Entry * );
+
+ void *mss_private;
+} monitor_subsys_t;
+
+extern BackendDB *be_monitor;
+
+/* increase this bufsize if entries in string form get too big */
+#define BACKMONITOR_BUFSIZE 8192
+
+typedef int (monitor_cbfunc)( struct berval *ndn, monitor_callback_t *cb,
+ struct berval *base, int scope, struct berval *filter );
+
+typedef int (monitor_cbafunc)( struct berval *ndn, Attribute *a,
+ monitor_callback_t *cb,
+ struct berval *base, int scope, struct berval *filter );
+
+typedef struct monitor_extra_t {
+ int (*is_configured)(void);
+ monitor_subsys_t * (*get_subsys)( const char *name );
+ monitor_subsys_t * (*get_subsys_by_dn)( struct berval *ndn, int sub );
+
+ int (*register_subsys)( monitor_subsys_t *ms );
+ int (*register_backend)( BackendInfo *bi );
+ int (*register_database)( BackendDB *be, struct berval *ndn_out );
+ int (*register_overlay_info)( slap_overinst *on );
+ int (*register_overlay)( BackendDB *be, slap_overinst *on, struct berval *ndn_out );
+ int (*register_entry)( Entry *e, monitor_callback_t *cb,
+ monitor_subsys_t *ms, unsigned long flags );
+ int (*register_entry_parent)( Entry *e, monitor_callback_t *cb,
+ monitor_subsys_t *ms, unsigned long flags,
+ struct berval *base, int scope, struct berval *filter );
+ monitor_cbafunc *register_entry_attrs;
+ monitor_cbfunc *register_entry_callback;
+
+ int (*unregister_entry)( struct berval *ndn );
+ monitor_cbfunc *unregister_entry_parent;
+ monitor_cbafunc *unregister_entry_attrs;
+ monitor_cbfunc *unregister_entry_callback;
+ Entry * (*entry_stub)( struct berval *pdn,
+ struct berval *pndn,
+ struct berval *rdn,
+ ObjectClass *oc,
+ struct berval *create,
+ struct berval *modify );
+ monitor_entry_t * (*entrypriv_create)( void );
+ int (*register_subsys_late)( monitor_subsys_t *ms );
+ Entry * (*entry_get_unlocked)( struct berval *ndn );
+} monitor_extra_t;
+
+LDAP_END_DECL
+
+#include "proto-back-monitor.h"
+
+#endif /* _back_monitor_h_ */
+
diff --git a/servers/slapd/back-monitor/backend.c b/servers/slapd/back-monitor/backend.c
new file mode 100644
index 0000000..c84dc38
--- /dev/null
+++ b/servers/slapd/back-monitor/backend.c
@@ -0,0 +1,159 @@
+/* backend.c - deals with backend subsystem */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-monitor.h"
+
+/*
+ * initializes backend subentries
+ */
+int
+monitor_subsys_backend_init(
+ BackendDB *be,
+ monitor_subsys_t *ms
+)
+{
+ monitor_info_t *mi;
+ Entry *e_backend, **ep;
+ int i;
+ monitor_entry_t *mp;
+ monitor_subsys_t *ms_database;
+ BackendInfo *bi;
+
+ mi = ( monitor_info_t * )be->be_private;
+
+ ms_database = monitor_back_get_subsys( SLAPD_MONITOR_DATABASE_NAME );
+ if ( ms_database == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_backend_init: "
+ "unable to get "
+ "\"" SLAPD_MONITOR_DATABASE_NAME "\" "
+ "subsystem\n" );
+ return -1;
+ }
+
+ if ( monitor_cache_get( mi, &ms->mss_ndn, &e_backend ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_backend_init: "
+ "unable to get entry \"%s\"\n",
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ mp = ( monitor_entry_t * )e_backend->e_private;
+ mp->mp_children = NULL;
+ ep = &mp->mp_children;
+
+ i = -1;
+ LDAP_STAILQ_FOREACH( bi, &backendInfo, bi_next ) {
+ char buf[ BACKMONITOR_BUFSIZE ];
+ BackendDB *be;
+ struct berval bv;
+ int j;
+ Entry *e;
+
+ i++;
+
+ bv.bv_len = snprintf( buf, sizeof( buf ), "cn=Backend %d", i );
+ bv.bv_val = buf;
+
+ e = monitor_entry_stub( &ms->mss_dn, &ms->mss_ndn, &bv,
+ mi->mi_oc_monitoredObject, NULL, NULL );
+
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_backend_init: "
+ "unable to create entry \"cn=Backend %d,%s\"\n",
+ i, ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ ber_str2bv( bi->bi_type, 0, 0, &bv );
+ attr_merge_normalize_one( e, mi->mi_ad_monitoredInfo,
+ &bv, NULL );
+ attr_merge_normalize_one( e_backend, mi->mi_ad_monitoredInfo,
+ &bv, NULL );
+
+ attr_merge_normalize_one( e, mi->mi_ad_monitorRuntimeConfig,
+ bi->bi_cf_ocs == NULL ? (struct berval *)&slap_false_bv :
+ (struct berval *)&slap_true_bv, NULL );
+
+ if ( bi->bi_controls ) {
+ int j;
+
+ for ( j = 0; bi->bi_controls[ j ]; j++ ) {
+ ber_str2bv( bi->bi_controls[ j ], 0, 0, &bv );
+ attr_merge_one( e, slap_schema.si_ad_supportedControl,
+ &bv, &bv );
+ }
+ }
+
+ j = -1;
+ LDAP_STAILQ_FOREACH( be, &backendDB, be_next ) {
+ char buf[ SLAP_LDAPDN_MAXLEN ];
+ struct berval dn;
+
+ j++;
+
+ if ( be->bd_info != bi ) {
+ continue;
+ }
+
+ snprintf( buf, sizeof( buf ), "cn=Database %d,%s",
+ j, ms_database->mss_dn.bv_val );
+
+ ber_str2bv( buf, 0, 0, &dn );
+ attr_merge_normalize_one( e, slap_schema.si_ad_seeAlso,
+ &dn, NULL );
+ }
+
+ mp = monitor_entrypriv_create();
+ if ( mp == NULL ) {
+ return -1;
+ }
+ e->e_private = ( void * )mp;
+ mp->mp_info = ms;
+ mp->mp_flags = ms->mss_flags | MONITOR_F_SUB;
+
+ if ( monitor_cache_add( mi, e ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_backend_init: "
+ "unable to add entry \"cn=Backend %d,%s\"\n",
+ i,
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ *ep = e;
+ ep = &mp->mp_next;
+ }
+
+ monitor_cache_release( mi, e_backend );
+
+ return( 0 );
+}
+
diff --git a/servers/slapd/back-monitor/bind.c b/servers/slapd/back-monitor/bind.c
new file mode 100644
index 0000000..718c001
--- /dev/null
+++ b/servers/slapd/back-monitor/bind.c
@@ -0,0 +1,48 @@
+/* bind.c - monitor backend bind routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <slap.h>
+#include "back-monitor.h"
+
+/*
+ * At present, only rootdn can bind with simple bind
+ */
+
+int
+monitor_back_bind( Operation *op, SlapReply *rs )
+{
+ Debug(LDAP_DEBUG_ARGS, "==> monitor_back_bind: dn: %s\n",
+ op->o_req_dn.bv_val );
+
+ if ( be_isroot_pw( op ) ) {
+ return LDAP_SUCCESS;
+ }
+
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ send_ldap_result( op, rs );
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/back-monitor/cache.c b/servers/slapd/back-monitor/cache.c
new file mode 100644
index 0000000..97bf39f
--- /dev/null
+++ b/servers/slapd/back-monitor/cache.c
@@ -0,0 +1,446 @@
+/* cache.c - routines to maintain an in-core cache of entries */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include "ac/string.h"
+
+#include "slap.h"
+
+#include "back-monitor.h"
+
+/*
+ * The cache maps DNs to Entries.
+ * Each entry, on turn, holds the list of its children in the e_private field.
+ * This is used by search operation to perform onelevel and subtree candidate
+ * selection.
+ */
+typedef struct monitor_cache_t {
+ struct berval mc_ndn;
+ Entry *mc_e;
+} monitor_cache_t;
+
+/*
+ * compares entries based on the dn
+ */
+int
+monitor_cache_cmp(
+ const void *c1,
+ const void *c2 )
+{
+ monitor_cache_t *cc1 = ( monitor_cache_t * )c1;
+ monitor_cache_t *cc2 = ( monitor_cache_t * )c2;
+
+ /*
+ * case sensitive, because the dn MUST be normalized
+ */
+ return ber_bvcmp( &cc1->mc_ndn, &cc2->mc_ndn );
+}
+
+/*
+ * checks for duplicate entries
+ */
+int
+monitor_cache_dup(
+ void *c1,
+ void *c2 )
+{
+ monitor_cache_t *cc1 = ( monitor_cache_t * )c1;
+ monitor_cache_t *cc2 = ( monitor_cache_t * )c2;
+
+ /*
+ * case sensitive, because the dn MUST be normalized
+ */
+ return ber_bvcmp( &cc1->mc_ndn, &cc2->mc_ndn ) == 0 ? -1 : 0;
+}
+
+/*
+ * adds an entry to the cache and inits the mutex
+ */
+int
+monitor_cache_add(
+ monitor_info_t *mi,
+ Entry *e )
+{
+ monitor_cache_t *mc;
+ int rc;
+
+ assert( mi != NULL );
+ assert( e != NULL );
+
+ mc = ( monitor_cache_t * )ch_malloc( sizeof( monitor_cache_t ) );
+ mc->mc_ndn = e->e_nname;
+ mc->mc_e = e;
+ ldap_pvt_thread_mutex_lock( &mi->mi_cache_mutex );
+ rc = ldap_avl_insert( &mi->mi_cache, ( caddr_t )mc,
+ monitor_cache_cmp, monitor_cache_dup );
+ ldap_pvt_thread_mutex_unlock( &mi->mi_cache_mutex );
+
+ return rc;
+}
+
+/*
+ * locks the entry (no r/w)
+ */
+int
+monitor_cache_lock(
+ Entry *e )
+{
+ monitor_entry_t *mp;
+
+ assert( e != NULL );
+ assert( e->e_private != NULL );
+
+ mp = ( monitor_entry_t * )e->e_private;
+ ldap_pvt_thread_mutex_lock( &mp->mp_mutex );
+
+ return( 0 );
+}
+
+/*
+ * tries to lock the entry (no r/w)
+ */
+int
+monitor_cache_trylock(
+ Entry *e )
+{
+ monitor_entry_t *mp;
+
+ assert( e != NULL );
+ assert( e->e_private != NULL );
+
+ mp = ( monitor_entry_t * )e->e_private;
+ return ldap_pvt_thread_mutex_trylock( &mp->mp_mutex );
+}
+
+/*
+ * gets an entry from the cache based on the normalized dn
+ * with mutex locked
+ */
+int
+monitor_cache_get(
+ monitor_info_t *mi,
+ struct berval *ndn,
+ Entry **ep )
+{
+ monitor_cache_t tmp_mc, *mc;
+
+ assert( mi != NULL );
+ assert( ndn != NULL );
+ assert( ep != NULL );
+
+ *ep = NULL;
+
+ tmp_mc.mc_ndn = *ndn;
+retry:;
+ ldap_pvt_thread_mutex_lock( &mi->mi_cache_mutex );
+ mc = ( monitor_cache_t * )ldap_avl_find( mi->mi_cache,
+ ( caddr_t )&tmp_mc, monitor_cache_cmp );
+
+ if ( mc != NULL ) {
+ /* entry is returned with mutex locked */
+ if ( monitor_cache_trylock( mc->mc_e ) ) {
+ ldap_pvt_thread_mutex_unlock( &mi->mi_cache_mutex );
+ ldap_pvt_thread_yield();
+ goto retry;
+ }
+ *ep = mc->mc_e;
+ }
+
+ ldap_pvt_thread_mutex_unlock( &mi->mi_cache_mutex );
+
+ return ( *ep == NULL ? -1 : 0 );
+}
+
+/*
+ * gets an entry from the cache based on the normalized dn
+ * with mutex locked
+ */
+int
+monitor_cache_remove(
+ monitor_info_t *mi,
+ struct berval *ndn,
+ Entry **ep )
+{
+ monitor_cache_t tmp_mc, *mc;
+ struct berval pndn;
+
+ assert( mi != NULL );
+ assert( ndn != NULL );
+ assert( ep != NULL );
+
+ *ep = NULL;
+
+ dnParent( ndn, &pndn );
+
+retry:;
+ ldap_pvt_thread_mutex_lock( &mi->mi_cache_mutex );
+
+ tmp_mc.mc_ndn = *ndn;
+ mc = ( monitor_cache_t * )ldap_avl_find( mi->mi_cache,
+ ( caddr_t )&tmp_mc, monitor_cache_cmp );
+
+ if ( mc != NULL ) {
+ monitor_cache_t *pmc;
+
+ if ( monitor_cache_trylock( mc->mc_e ) ) {
+ ldap_pvt_thread_mutex_unlock( &mi->mi_cache_mutex );
+ goto retry;
+ }
+
+ tmp_mc.mc_ndn = pndn;
+ pmc = ( monitor_cache_t * )ldap_avl_find( mi->mi_cache,
+ ( caddr_t )&tmp_mc, monitor_cache_cmp );
+ if ( pmc != NULL ) {
+ monitor_entry_t *mp = (monitor_entry_t *)mc->mc_e->e_private,
+ *pmp = (monitor_entry_t *)pmc->mc_e->e_private;
+ Entry **entryp;
+
+ if ( monitor_cache_trylock( pmc->mc_e ) ) {
+ monitor_cache_release( mi, mc->mc_e );
+ ldap_pvt_thread_mutex_unlock( &mi->mi_cache_mutex );
+ goto retry;
+ }
+
+ for ( entryp = &pmp->mp_children; *entryp != NULL; ) {
+ monitor_entry_t *next = (monitor_entry_t *)(*entryp)->e_private;
+ if ( next == mp ) {
+ *entryp = next->mp_next;
+ entryp = NULL;
+ break;
+ }
+
+ entryp = &next->mp_next;
+ }
+
+ if ( entryp != NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_cache_remove(\"%s\"): "
+ "not in parent's list\n",
+ ndn->bv_val );
+ }
+
+ /* either succeeded, and the entry is no longer
+ * in its parent's list, or failed, and the
+ * entry is neither mucked with nor returned */
+ monitor_cache_release( mi, pmc->mc_e );
+
+ if ( entryp == NULL ) {
+ monitor_cache_t *tmpmc;
+
+ tmp_mc.mc_ndn = *ndn;
+ tmpmc = ldap_avl_delete( &mi->mi_cache,
+ ( caddr_t )&tmp_mc, monitor_cache_cmp );
+ assert( tmpmc == mc );
+
+ *ep = mc->mc_e;
+ ch_free( mc );
+ mc = NULL;
+
+ /* NOTE: we destroy the mutex, but otherwise
+ * leave the private data around; specifically,
+ * callbacks need be freed by someone else */
+
+ ldap_pvt_thread_mutex_destroy( &mp->mp_mutex );
+ mp->mp_next = NULL;
+ mp->mp_children = NULL;
+ }
+
+ }
+
+ if ( mc ) {
+ monitor_cache_release( mi, mc->mc_e );
+ }
+ }
+
+ ldap_pvt_thread_mutex_unlock( &mi->mi_cache_mutex );
+
+ return ( *ep == NULL ? -1 : 0 );
+}
+
+/*
+ * If the entry exists in cache, it is returned in locked status;
+ * otherwise, if the parent exists, if it may generate volatile
+ * descendants an attempt to generate the required entry is
+ * performed and, if successful, the entry is returned
+ */
+int
+monitor_cache_dn2entry(
+ Operation *op,
+ SlapReply *rs,
+ struct berval *ndn,
+ Entry **ep,
+ Entry **matched )
+{
+ monitor_info_t *mi = (monitor_info_t *)op->o_bd->be_private;
+ int rc;
+ struct berval p_ndn = BER_BVNULL;
+ Entry *e_parent;
+ monitor_entry_t *mp;
+
+ assert( mi != NULL );
+ assert( ndn != NULL );
+ assert( ep != NULL );
+ assert( matched != NULL );
+
+ *matched = NULL;
+
+ if ( !dnIsSuffix( ndn, &op->o_bd->be_nsuffix[ 0 ] ) ) {
+ return( -1 );
+ }
+
+ rc = monitor_cache_get( mi, ndn, ep );
+ if ( !rc && *ep != NULL ) {
+ return( 0 );
+ }
+
+ /* try with parent/ancestors */
+ if ( BER_BVISNULL( ndn ) ) {
+ BER_BVSTR( &p_ndn, "" );
+
+ } else {
+ dnParent( ndn, &p_ndn );
+ }
+
+ rc = monitor_cache_dn2entry( op, rs, &p_ndn, &e_parent, matched );
+ if ( rc || e_parent == NULL ) {
+ return( -1 );
+ }
+
+ mp = ( monitor_entry_t * )e_parent->e_private;
+ rc = -1;
+ if ( mp->mp_flags & MONITOR_F_VOLATILE_CH ) {
+ /* parent entry generates volatile children */
+ rc = monitor_entry_create( op, rs, ndn, e_parent, ep );
+ }
+
+ if ( !rc ) {
+ monitor_cache_lock( *ep );
+ monitor_cache_release( mi, e_parent );
+
+ } else {
+ *matched = e_parent;
+ }
+
+ return( rc );
+}
+
+/*
+ * releases the lock of the entry; if it is marked as volatile, it is
+ * destroyed.
+ */
+int
+monitor_cache_release(
+ monitor_info_t *mi,
+ Entry *e )
+{
+ monitor_entry_t *mp;
+
+ assert( mi != NULL );
+ assert( e != NULL );
+ assert( e->e_private != NULL );
+
+ mp = ( monitor_entry_t * )e->e_private;
+
+ if ( mp->mp_flags & MONITOR_F_VOLATILE ) {
+ monitor_cache_t *mc, tmp_mc;
+
+ /* volatile entries do not return to cache */
+ ldap_pvt_thread_mutex_lock( &mi->mi_cache_mutex );
+ tmp_mc.mc_ndn = e->e_nname;
+ mc = ldap_avl_delete( &mi->mi_cache,
+ ( caddr_t )&tmp_mc, monitor_cache_cmp );
+ ldap_pvt_thread_mutex_unlock( &mi->mi_cache_mutex );
+ if ( mc != NULL ) {
+ ch_free( mc );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &mp->mp_mutex );
+ ldap_pvt_thread_mutex_destroy( &mp->mp_mutex );
+ ch_free( mp );
+ e->e_private = NULL;
+ entry_free( e );
+
+ return( 0 );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &mp->mp_mutex );
+
+ return( 0 );
+}
+
+static void
+monitor_entry_destroy( void *v_mc )
+{
+ monitor_cache_t *mc = (monitor_cache_t *)v_mc;
+
+ if ( mc->mc_e != NULL ) {
+ monitor_entry_t *mp;
+
+ assert( mc->mc_e->e_private != NULL );
+
+ mp = ( monitor_entry_t * )mc->mc_e->e_private;
+
+ if ( mp->mp_cb ) {
+ monitor_callback_t *cb;
+
+ for ( cb = mp->mp_cb; cb != NULL; ) {
+ monitor_callback_t *next = cb->mc_next;
+
+ if ( cb->mc_free ) {
+ (void)cb->mc_free( mc->mc_e, &cb->mc_private );
+ }
+ ch_free( mp->mp_cb );
+
+ cb = next;
+ }
+ }
+
+ ldap_pvt_thread_mutex_destroy( &mp->mp_mutex );
+
+ ch_free( mp );
+ mc->mc_e->e_private = NULL;
+ entry_free( mc->mc_e );
+ }
+
+ ch_free( mc );
+}
+
+int
+monitor_cache_destroy(
+ monitor_info_t *mi )
+{
+ if ( mi->mi_cache ) {
+ ldap_avl_free( mi->mi_cache, monitor_entry_destroy );
+ }
+
+ return 0;
+}
+
+int monitor_back_release(
+ Operation *op,
+ Entry *e,
+ int rw )
+{
+ monitor_info_t *mi = ( monitor_info_t * )op->o_bd->be_private;
+ return monitor_cache_release( mi, e );
+}
diff --git a/servers/slapd/back-monitor/compare.c b/servers/slapd/back-monitor/compare.c
new file mode 100644
index 0000000..e17b4f1
--- /dev/null
+++ b/servers/slapd/back-monitor/compare.c
@@ -0,0 +1,76 @@
+/* compare.c - monitor backend compare routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <slap.h>
+#include "back-monitor.h"
+
+int
+monitor_back_compare( Operation *op, SlapReply *rs )
+{
+ monitor_info_t *mi = ( monitor_info_t * ) op->o_bd->be_private;
+ Entry *e, *matched = NULL;
+ int rc;
+
+ /* get entry with reader lock */
+ monitor_cache_dn2entry( op, rs, &op->o_req_ndn, &e, &matched );
+ if ( e == NULL ) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ if ( matched ) {
+ if ( !access_allowed_mask( op, matched,
+ slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL, NULL ) )
+ {
+ /* do nothing */ ;
+ } else {
+ rs->sr_matched = matched->e_dn;
+ }
+ }
+ send_ldap_result( op, rs );
+ if ( matched ) {
+ monitor_cache_release( mi, matched );
+ rs->sr_matched = NULL;
+ }
+
+ return rs->sr_err;
+ }
+
+ monitor_entry_update( op, rs, e );
+ rs->sr_err = slap_compare_entry( op, e, op->orc_ava );
+ rc = rs->sr_err;
+ switch ( rc ) {
+ case LDAP_COMPARE_FALSE:
+ case LDAP_COMPARE_TRUE:
+ rc = LDAP_SUCCESS;
+ break;
+ }
+
+ send_ldap_result( op, rs );
+ rs->sr_err = rc;
+
+ monitor_cache_release( mi, e );
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/back-monitor/conn.c b/servers/slapd/back-monitor/conn.c
new file mode 100644
index 0000000..fd3e014
--- /dev/null
+++ b/servers/slapd/back-monitor/conn.c
@@ -0,0 +1,537 @@
+/* conn.c - deal with connection subsystem */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "lutil.h"
+#include "back-monitor.h"
+
+static int
+monitor_subsys_conn_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e );
+
+static int
+monitor_subsys_conn_create(
+ Operation *op,
+ SlapReply *rs,
+ struct berval *ndn,
+ Entry *e_parent,
+ Entry **ep );
+
+int
+monitor_subsys_conn_init(
+ BackendDB *be,
+ monitor_subsys_t *ms )
+{
+ monitor_info_t *mi;
+ Entry *e, **ep, *e_conn;
+ monitor_entry_t *mp;
+ char buf[ BACKMONITOR_BUFSIZE ];
+ struct berval bv;
+
+ assert( be != NULL );
+
+ ms->mss_update = monitor_subsys_conn_update;
+ ms->mss_create = monitor_subsys_conn_create;
+
+ mi = ( monitor_info_t * )be->be_private;
+
+ if ( monitor_cache_get( mi, &ms->mss_ndn, &e_conn ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_conn_init: "
+ "unable to get entry \"%s\"\n",
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ mp = ( monitor_entry_t * )e_conn->e_private;
+ mp->mp_children = NULL;
+ ep = &mp->mp_children;
+
+ /*
+ * Max file descriptors
+ */
+ BER_BVSTR( &bv, "cn=Max File Descriptors" );
+ e = monitor_entry_stub( &ms->mss_dn, &ms->mss_ndn, &bv,
+ mi->mi_oc_monitorCounterObject, NULL, NULL );
+
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_conn_init: "
+ "unable to create entry \"%s,%s\"\n",
+ bv.bv_val, ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ if ( dtblsize ) {
+ bv.bv_val = buf;
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%d", dtblsize );
+
+ } else {
+ BER_BVSTR( &bv, "0" );
+ }
+ attr_merge_one( e, mi->mi_ad_monitorCounter, &bv, NULL );
+
+ mp = monitor_entrypriv_create();
+ if ( mp == NULL ) {
+ return -1;
+ }
+ e->e_private = ( void * )mp;
+ mp->mp_info = ms;
+ mp->mp_flags = ms->mss_flags \
+ | MONITOR_F_SUB | MONITOR_F_PERSISTENT;
+ mp->mp_flags &= ~MONITOR_F_VOLATILE_CH;
+
+ if ( monitor_cache_add( mi, e ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_conn_init: "
+ "unable to add entry \"cn=Total,%s\"\n",
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ *ep = e;
+ ep = &mp->mp_next;
+
+ /*
+ * Total conns
+ */
+ BER_BVSTR( &bv, "cn=Total" );
+ e = monitor_entry_stub( &ms->mss_dn, &ms->mss_ndn, &bv,
+ mi->mi_oc_monitorCounterObject, NULL, NULL );
+
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_conn_init: "
+ "unable to create entry \"cn=Total,%s\"\n",
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ BER_BVSTR( &bv, "-1" );
+ attr_merge_one( e, mi->mi_ad_monitorCounter, &bv, NULL );
+
+ mp = monitor_entrypriv_create();
+ if ( mp == NULL ) {
+ return -1;
+ }
+ e->e_private = ( void * )mp;
+ mp->mp_info = ms;
+ mp->mp_flags = ms->mss_flags \
+ | MONITOR_F_SUB | MONITOR_F_PERSISTENT;
+ mp->mp_flags &= ~MONITOR_F_VOLATILE_CH;
+
+ if ( monitor_cache_add( mi, e ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_conn_init: "
+ "unable to add entry \"cn=Total,%s\"\n",
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ *ep = e;
+ ep = &mp->mp_next;
+
+ /*
+ * Current conns
+ */
+ BER_BVSTR( &bv, "cn=Current" );
+ e = monitor_entry_stub( &ms->mss_dn, &ms->mss_ndn, &bv,
+ mi->mi_oc_monitorCounterObject, NULL, NULL );
+
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_conn_init: "
+ "unable to create entry \"cn=Current,%s\"\n",
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ BER_BVSTR( &bv, "0" );
+ attr_merge_one( e, mi->mi_ad_monitorCounter, &bv, NULL );
+
+ mp = monitor_entrypriv_create();
+ if ( mp == NULL ) {
+ return -1;
+ }
+ e->e_private = ( void * )mp;
+ mp->mp_info = ms;
+ mp->mp_flags = ms->mss_flags \
+ | MONITOR_F_SUB | MONITOR_F_PERSISTENT;
+ mp->mp_flags &= ~MONITOR_F_VOLATILE_CH;
+
+ if ( monitor_cache_add( mi, e ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_conn_init: "
+ "unable to add entry \"cn=Current,%s\"\n",
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ *ep = e;
+ ep = &mp->mp_next;
+
+ monitor_cache_release( mi, e_conn );
+
+ return( 0 );
+}
+
+static int
+monitor_subsys_conn_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e )
+{
+ monitor_info_t *mi = ( monitor_info_t * )op->o_bd->be_private;
+
+ long n = -1;
+ static struct berval total_bv = BER_BVC( "cn=total" ),
+ current_bv = BER_BVC( "cn=current" );
+ struct berval rdn;
+
+ assert( mi != NULL );
+ assert( e != NULL );
+
+ dnRdn( &e->e_nname, &rdn );
+
+ if ( dn_match( &rdn, &total_bv ) ) {
+ n = connections_nextid() - SLAPD_SYNC_SYNCCONN_OFFSET;
+
+ } else if ( dn_match( &rdn, &current_bv ) ) {
+ Connection *c;
+ ber_socket_t connindex;
+
+ for ( n = 0, c = connection_first( &connindex );
+ c != NULL;
+ n++, c = connection_next( c, &connindex ) )
+ {
+ /* Ignore outbound connections */
+ if ( c->c_conn_state == SLAP_C_CLIENT )
+ n--;
+ }
+ connection_done( c );
+ }
+
+ if ( n != -1 ) {
+ Attribute *a;
+ char buf[LDAP_PVT_INTTYPE_CHARS(long)];
+ ber_len_t len;
+
+ a = attr_find( e->e_attrs, mi->mi_ad_monitorCounter );
+ if ( a == NULL ) {
+ return( -1 );
+ }
+
+ snprintf( buf, sizeof( buf ), "%ld", n );
+ len = strlen( buf );
+ if ( len > a->a_vals[ 0 ].bv_len ) {
+ a->a_vals[ 0 ].bv_val = ber_memrealloc( a->a_vals[ 0 ].bv_val, len + 1 );
+ }
+ a->a_vals[ 0 ].bv_len = len;
+ AC_MEMCPY( a->a_vals[ 0 ].bv_val, buf, len + 1 );
+
+ /* FIXME: touch modifyTimestamp? */
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+conn_create(
+ monitor_info_t *mi,
+ Connection *c,
+ Entry **ep,
+ monitor_subsys_t *ms )
+{
+ monitor_entry_t *mp;
+ struct tm tm;
+ char buf[ BACKMONITOR_BUFSIZE ];
+ char buf2[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+ char buf3[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+
+ struct berval bv, ctmbv, mtmbv;
+ struct berval bv_unknown= BER_BVC("unknown");
+
+ Entry *e;
+
+ assert( c != NULL );
+ assert( ep != NULL );
+
+ ldap_pvt_gmtime( &c->c_starttime, &tm );
+
+ ctmbv.bv_len = lutil_gentime( buf2, sizeof( buf2 ), &tm );
+ ctmbv.bv_val = buf2;
+
+ ldap_pvt_gmtime( &c->c_activitytime, &tm );
+ mtmbv.bv_len = lutil_gentime( buf3, sizeof( buf3 ), &tm );
+ mtmbv.bv_val = buf3;
+
+ bv.bv_len = snprintf( buf, sizeof( buf ),
+ "cn=Connection %ld", c->c_connid );
+ bv.bv_val = buf;
+ e = monitor_entry_stub( &ms->mss_dn, &ms->mss_ndn, &bv,
+ mi->mi_oc_monitorConnection, &ctmbv, &mtmbv );
+
+ if ( e == NULL) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_conn_create: "
+ "unable to create entry "
+ "\"cn=Connection %ld,%s\"\n",
+ c->c_connid,
+ ms->mss_dn.bv_val );
+ return( -1 );
+ }
+
+#ifdef MONITOR_LEGACY_CONN
+ /* NOTE: this will disappear, as the exploded data
+ * has been moved to dedicated attributes */
+ bv.bv_len = snprintf( buf, sizeof( buf ),
+ "%ld "
+ ": %ld "
+ ": %ld/%ld/%ld/%ld "
+ ": %ld/%ld/%ld "
+ ": %s%s%s%s%s%s "
+ ": %s "
+ ": %s "
+ ": %s "
+ ": %s "
+ ": %s "
+ ": %s "
+ ": %s",
+ c->c_connid,
+ (long) c->c_protocol,
+ c->c_n_ops_received, c->c_n_ops_executing,
+ c->c_n_ops_pending, c->c_n_ops_completed,
+
+ /* add low-level counters here */
+ c->c_n_get, c->c_n_read, c->c_n_write,
+
+ c->c_currentber ? "r" : "",
+ c->c_writewaiter ? "w" : "",
+ LDAP_STAILQ_EMPTY( &c->c_ops ) ? "" : "x",
+ LDAP_STAILQ_EMPTY( &c->c_pending_ops ) ? "" : "p",
+ connection_state2str( c->c_conn_state ),
+ c->c_sasl_bind_in_progress ? "S" : "",
+
+ c->c_dn.bv_len ? c->c_dn.bv_val : SLAPD_ANONYMOUS,
+
+ c->c_listener_url.bv_val,
+ BER_BVISNULL( &c->c_peer_domain )
+ ? "" : c->c_peer_domain.bv_val,
+ BER_BVISNULL( &c->c_peer_name )
+ ? "" : c->c_peer_name.bv_val,
+ c->c_sock_name.bv_val,
+
+ buf2,
+ buf3 );
+ attr_merge_normalize_one( e, mi->mi_ad_monitoredInfo, &bv, NULL );
+#endif /* MONITOR_LEGACY_CONN */
+
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%lu", c->c_connid );
+ attr_merge_one( e, mi->mi_ad_monitorConnectionNumber, &bv, NULL );
+
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%ld", (long) c->c_protocol );
+ attr_merge_normalize_one( e, mi->mi_ad_monitorConnectionProtocol, &bv, NULL );
+
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%ld", c->c_n_ops_received );
+ attr_merge_one( e, mi->mi_ad_monitorConnectionOpsReceived, &bv, NULL );
+
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%ld", c->c_n_ops_executing );
+ attr_merge_one( e, mi->mi_ad_monitorConnectionOpsExecuting, &bv, NULL );
+
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%ld", c->c_n_ops_pending );
+ attr_merge_one( e, mi->mi_ad_monitorConnectionOpsPending, &bv, NULL );
+
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%ld", c->c_n_ops_completed );
+ attr_merge_one( e, mi->mi_ad_monitorConnectionOpsCompleted, &bv, NULL );
+
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%ld", c->c_n_ops_async );
+ attr_merge_one( e, mi->mi_ad_monitorConnectionOpsAsync, &bv, NULL );
+
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%ld", c->c_n_get );
+ attr_merge_one( e, mi->mi_ad_monitorConnectionGet, &bv, NULL );
+
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%ld", c->c_n_read );
+ attr_merge_one( e, mi->mi_ad_monitorConnectionRead, &bv, NULL );
+
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%ld", c->c_n_write );
+ attr_merge_one( e, mi->mi_ad_monitorConnectionWrite, &bv, NULL );
+
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%s%s%s%s%s%s",
+ c->c_currentber ? "r" : "",
+ c->c_writewaiter ? "w" : "",
+ LDAP_STAILQ_EMPTY( &c->c_ops ) ? "" : "x",
+ LDAP_STAILQ_EMPTY( &c->c_pending_ops ) ? "" : "p",
+ connection_state2str( c->c_conn_state ),
+ c->c_sasl_bind_in_progress ? "S" : "" );
+ attr_merge_normalize_one( e, mi->mi_ad_monitorConnectionMask, &bv, NULL );
+
+ attr_merge_one( e, mi->mi_ad_monitorConnectionAuthzDN,
+ &c->c_dn, &c->c_ndn );
+
+ /* NOTE: client connections leave the c_peer_* fields NULL */
+ assert( !BER_BVISNULL( &c->c_listener_url ) );
+ attr_merge_normalize_one( e, mi->mi_ad_monitorConnectionListener,
+ &c->c_listener_url, NULL );
+
+ attr_merge_normalize_one( e, mi->mi_ad_monitorConnectionPeerDomain,
+ BER_BVISNULL( &c->c_peer_domain ) ? &bv_unknown : &c->c_peer_domain,
+ NULL );
+
+ attr_merge_normalize_one( e, mi->mi_ad_monitorConnectionPeerAddress,
+ BER_BVISNULL( &c->c_peer_name ) ? &bv_unknown : &c->c_peer_name,
+ NULL );
+
+ assert( !BER_BVISNULL( &c->c_sock_name ) );
+ attr_merge_normalize_one( e, mi->mi_ad_monitorConnectionLocalAddress,
+ &c->c_sock_name, NULL );
+
+ attr_merge_normalize_one( e, mi->mi_ad_monitorConnectionStartTime, &ctmbv, NULL );
+
+ attr_merge_normalize_one( e, mi->mi_ad_monitorConnectionActivityTime, &mtmbv, NULL );
+
+ mp = monitor_entrypriv_create();
+ if ( mp == NULL ) {
+ return LDAP_OTHER;
+ }
+ e->e_private = ( void * )mp;
+ mp->mp_info = ms;
+ mp->mp_flags = MONITOR_F_SUB | MONITOR_F_VOLATILE;
+
+ *ep = e;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+monitor_subsys_conn_create(
+ Operation *op,
+ SlapReply *rs,
+ struct berval *ndn,
+ Entry *e_parent,
+ Entry **ep )
+{
+ monitor_info_t *mi = ( monitor_info_t * )op->o_bd->be_private;
+
+ int rc = SLAP_CB_CONTINUE;
+ monitor_subsys_t *ms;
+
+ assert( mi != NULL );
+ assert( e_parent != NULL );
+ assert( ep != NULL );
+
+ ms = (( monitor_entry_t *)e_parent->e_private)->mp_info;
+
+ *ep = NULL;
+
+ if ( ndn == NULL ) {
+ Connection *c;
+ ber_socket_t connindex;
+ Entry *e = NULL,
+ *e_tmp = NULL;
+
+ /* create all the children of e_parent */
+ for ( c = connection_first( &connindex );
+ c != NULL;
+ c = connection_next( c, &connindex ) )
+ {
+ monitor_entry_t *mp;
+
+ /* ignore outbound for now, nothing to show */
+ if ( c->c_conn_state == SLAP_C_CLIENT )
+ continue;
+
+ if ( conn_create( mi, c, &e, ms ) != SLAP_CB_CONTINUE
+ || e == NULL )
+ {
+ for ( ; e_tmp != NULL; ) {
+ mp = ( monitor_entry_t * )e_tmp->e_private;
+ e = mp->mp_next;
+
+ ch_free( mp );
+ e_tmp->e_private = NULL;
+ entry_free( e_tmp );
+
+ e_tmp = e;
+ }
+ rc = rs->sr_err = LDAP_OTHER;
+ break;
+ }
+ mp = ( monitor_entry_t * )e->e_private;
+ mp->mp_next = e_tmp;
+ e_tmp = e;
+ }
+ connection_done( c );
+ *ep = e;
+
+ } else {
+ Connection *c;
+ ber_socket_t connindex;
+ unsigned long connid;
+ char *next = NULL;
+ static struct berval nconn_bv = BER_BVC( "cn=connection " );
+
+ rc = LDAP_NO_SUCH_OBJECT;
+
+ /* create exactly the required entry;
+ * the normalized DN must start with "cn=connection ",
+ * followed by the connection id, followed by
+ * the RDN separator "," */
+ if ( ndn->bv_len <= nconn_bv.bv_len
+ || strncmp( ndn->bv_val, nconn_bv.bv_val, nconn_bv.bv_len ) != 0 )
+ {
+ return -1;
+ }
+
+ connid = strtol( &ndn->bv_val[ nconn_bv.bv_len ], &next, 10 );
+ if ( next[ 0 ] != ',' ) {
+ return ( rs->sr_err = LDAP_OTHER );
+ }
+
+ for ( c = connection_first( &connindex );
+ c != NULL;
+ c = connection_next( c, &connindex ) )
+ {
+ if ( c->c_connid == connid ) {
+ rc = conn_create( mi, c, ep, ms );
+ if ( rc != SLAP_CB_CONTINUE ) {
+ rs->sr_err = rc;
+
+ } else if ( *ep == NULL ) {
+ rc = rs->sr_err = LDAP_OTHER;
+ }
+
+ break;
+ }
+ }
+
+ connection_done( c );
+ }
+
+ return rc;
+}
+
diff --git a/servers/slapd/back-monitor/database.c b/servers/slapd/back-monitor/database.c
new file mode 100644
index 0000000..5cc6df3
--- /dev/null
+++ b/servers/slapd/back-monitor/database.c
@@ -0,0 +1,1027 @@
+/* database.c - deals with database subsystem */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include "slap.h"
+#include "back-monitor.h"
+
+#if defined(LDAP_SLAPI)
+#include "slapi.h"
+static int monitor_back_add_plugin( monitor_info_t *mi, Backend *be, Entry *e );
+#endif /* defined(LDAP_SLAPI) */
+
+static int
+monitor_subsys_database_modify(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e );
+
+static struct restricted_ops_t {
+ struct berval op;
+ unsigned int tag;
+} restricted_ops[] = {
+ { BER_BVC( "add" ), SLAP_RESTRICT_OP_ADD },
+ { BER_BVC( "bind" ), SLAP_RESTRICT_OP_BIND },
+ { BER_BVC( "compare" ), SLAP_RESTRICT_OP_COMPARE },
+ { BER_BVC( "delete" ), SLAP_RESTRICT_OP_DELETE },
+ { BER_BVC( "extended" ), SLAP_RESTRICT_OP_EXTENDED },
+ { BER_BVC( "modify" ), SLAP_RESTRICT_OP_MODIFY },
+ { BER_BVC( "rename" ), SLAP_RESTRICT_OP_RENAME },
+ { BER_BVC( "search" ), SLAP_RESTRICT_OP_SEARCH },
+ { BER_BVNULL, 0 }
+}, restricted_exops[] = {
+ { BER_BVC( LDAP_EXOP_START_TLS ), SLAP_RESTRICT_EXOP_START_TLS },
+ { BER_BVC( LDAP_EXOP_MODIFY_PASSWD ), SLAP_RESTRICT_EXOP_MODIFY_PASSWD },
+ { BER_BVC( LDAP_EXOP_WHO_AM_I ), SLAP_RESTRICT_EXOP_WHOAMI },
+ { BER_BVC( LDAP_EXOP_CANCEL ), SLAP_RESTRICT_EXOP_CANCEL },
+ { BER_BVNULL, 0 }
+};
+
+static int
+init_readOnly( monitor_info_t *mi, Entry *e, slap_mask_t restrictops )
+{
+ struct berval *tf = ( ( restrictops & SLAP_RESTRICT_OP_MASK ) == SLAP_RESTRICT_OP_WRITES ) ?
+ (struct berval *)&slap_true_bv : (struct berval *)&slap_false_bv;
+
+ return attr_merge_one( e, mi->mi_ad_readOnly, tf, NULL );
+}
+
+static int
+init_restrictedOperation( monitor_info_t *mi, Entry *e, slap_mask_t restrictops )
+{
+ int i, rc;
+
+ for ( i = 0; restricted_ops[ i ].op.bv_val; i++ ) {
+ if ( restrictops & restricted_ops[ i ].tag ) {
+ rc = attr_merge_one( e, mi->mi_ad_restrictedOperation,
+ &restricted_ops[ i ].op,
+ &restricted_ops[ i ].op );
+ if ( rc ) {
+ return rc;
+ }
+ }
+ }
+
+ for ( i = 0; restricted_exops[ i ].op.bv_val; i++ ) {
+ if ( restrictops & restricted_exops[ i ].tag ) {
+ rc = attr_merge_one( e, mi->mi_ad_restrictedOperation,
+ &restricted_exops[ i ].op,
+ &restricted_exops[ i ].op );
+ if ( rc ) {
+ return rc;
+ }
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+monitor_subsys_overlay_init_one(
+ monitor_info_t *mi,
+ BackendDB *be,
+ monitor_subsys_t *ms,
+ monitor_subsys_t *ms_overlay,
+ slap_overinst *on,
+ Entry *e_database,
+ Entry **ep_overlay )
+{
+ char buf[ BACKMONITOR_BUFSIZE ];
+ int j, o;
+ Entry *e_overlay;
+ slap_overinst *on2;
+ slap_overinfo *oi = NULL;
+ BackendInfo *bi;
+ monitor_entry_t *mp_overlay;
+ struct berval bv;
+
+ assert( overlay_is_over( be ) );
+
+ oi = (slap_overinfo *)be->bd_info->bi_private;
+ bi = oi->oi_orig;
+
+ /* find the overlay number, o */
+ for ( o = 0, on2 = oi->oi_list; on2 && on2 != on; on2 = on2->on_next, o++ )
+ ;
+
+ if ( on2 == NULL ) {
+ return -1;
+ }
+
+ /* find the overlay type number, j */
+ for ( on2 = overlay_next( NULL ), j = 0; on2; on2 = overlay_next( on2 ), j++ ) {
+ if ( on2->on_bi.bi_type == on->on_bi.bi_type ) {
+ break;
+ }
+ }
+ assert( on2 != NULL );
+
+ bv.bv_len = snprintf( buf, sizeof( buf ), "cn=Overlay %d", o );
+ bv.bv_val = buf;
+
+ e_overlay = monitor_entry_stub( &e_database->e_name, &e_database->e_nname, &bv,
+ mi->mi_oc_monitoredObject, NULL, NULL );
+
+ if ( e_overlay == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_overlay_init_one: "
+ "unable to create entry "
+ "\"cn=Overlay %d,%s\"\n",
+ o, e_database->e_name.bv_val );
+ return( -1 );
+ }
+ ber_str2bv( on->on_bi.bi_type, 0, 0, &bv );
+ attr_merge_normalize_one( e_overlay, mi->mi_ad_monitoredInfo, &bv, NULL );
+
+ bv.bv_len = snprintf( buf, sizeof( buf ), "cn=Overlay %d,%s",
+ j, ms_overlay->mss_dn.bv_val );
+ bv.bv_val = buf;
+ attr_merge_normalize_one( e_overlay, slap_schema.si_ad_seeAlso,
+ &bv, NULL );
+
+ if ( SLAP_MONITOR( be ) ) {
+ attr_merge( e_overlay, slap_schema.si_ad_monitorContext,
+ be->be_suffix, be->be_nsuffix );
+
+ } else {
+ attr_merge( e_overlay, slap_schema.si_ad_namingContexts,
+ be->be_suffix, be->be_nsuffix );
+ }
+
+ mp_overlay = monitor_entrypriv_create();
+ if ( mp_overlay == NULL ) {
+ return -1;
+ }
+ e_overlay->e_private = ( void * )mp_overlay;
+ mp_overlay->mp_info = ms;
+ mp_overlay->mp_flags = ms->mss_flags | MONITOR_F_SUB;
+
+ if ( monitor_cache_add( mi, e_overlay ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_overlay_init_one: "
+ "unable to add entry "
+ "\"cn=Overlay %d,%s\"\n",
+ o, e_database->e_name.bv_val );
+ return -1;
+ }
+
+ *ep_overlay = e_overlay;
+ ep_overlay = &mp_overlay->mp_next;
+
+ return 0;
+}
+
+static int
+monitor_subsys_database_init_one(
+ monitor_info_t *mi,
+ BackendDB *be,
+ monitor_subsys_t *ms,
+ monitor_subsys_t *ms_backend,
+ monitor_subsys_t *ms_overlay,
+ struct berval *rdn,
+ Entry *e_database,
+ Entry ***epp )
+{
+ char buf[ BACKMONITOR_BUFSIZE ];
+ int j;
+ slap_overinfo *oi = NULL;
+ BackendInfo *bi, *bi2;
+ Entry *e;
+ monitor_entry_t *mp;
+ char *rdnval = strchr( rdn->bv_val, '=' ) + 1;
+ struct berval bv;
+
+ bi = be->bd_info;
+
+ if ( be->be_suffix == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_database_init_one: "
+ "missing suffix for %s\n",
+ rdnval );
+ return( -1 );
+ }
+
+ if ( overlay_is_over( be ) ) {
+ oi = (slap_overinfo *)be->bd_info->bi_private;
+ bi = oi->oi_orig;
+ }
+
+ e = monitor_entry_stub( &ms->mss_dn, &ms->mss_ndn, rdn,
+ mi->mi_oc_monitoredObject, NULL, NULL );
+
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_database_init_one: "
+ "unable to create entry \"%s,%s\"\n",
+ rdn->bv_val, ms->mss_dn.bv_val );
+ return( -1 );
+ }
+
+ ber_str2bv( bi->bi_type, 0, 0, &bv );
+ attr_merge_normalize_one( e, mi->mi_ad_monitoredInfo, &bv, NULL );
+ attr_merge_one( e, mi->mi_ad_monitorIsShadow,
+ SLAP_SHADOW( be ) ? (struct berval *)&slap_true_bv :
+ (struct berval *)&slap_false_bv, NULL );
+
+ if ( SLAP_MONITOR( be ) ) {
+ attr_merge( e, slap_schema.si_ad_monitorContext,
+ be->be_suffix, be->be_nsuffix );
+ attr_merge( e_database, slap_schema.si_ad_monitorContext,
+ be->be_suffix, be->be_nsuffix );
+
+ } else {
+ attr_merge( e, slap_schema.si_ad_namingContexts,
+ be->be_suffix, be->be_nsuffix );
+ attr_merge( e_database, slap_schema.si_ad_namingContexts,
+ be->be_suffix, be->be_nsuffix );
+
+ if ( SLAP_GLUE_SUBORDINATE( be ) ) {
+ BackendDB *sup_be = select_backend( &be->be_nsuffix[ 0 ], 1 );
+ if ( sup_be == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_database_init: "
+ "unable to get superior for %s\n",
+ be->be_suffix[ 0 ].bv_val );
+
+ } else {
+ attr_merge( e, mi->mi_ad_monitorSuperiorDN,
+ sup_be->be_suffix, sup_be->be_nsuffix );
+ }
+ }
+ }
+
+ (void)init_readOnly( mi, e, be->be_restrictops );
+ (void)init_restrictedOperation( mi, e, be->be_restrictops );
+
+ if ( SLAP_SHADOW( be ) && be->be_update_refs ) {
+ attr_merge_normalize( e, mi->mi_ad_monitorUpdateRef,
+ be->be_update_refs, NULL );
+ }
+
+ if ( oi != NULL ) {
+ slap_overinst *on = oi->oi_list,
+ *on1 = on;
+
+ for ( ; on; on = on->on_next ) {
+ slap_overinst *on2;
+
+ for ( on2 = on1; on2 != on; on2 = on2->on_next ) {
+ if ( on2->on_bi.bi_type == on->on_bi.bi_type ) {
+ break;
+ }
+ }
+
+ if ( on2 != on ) {
+ break;
+ }
+
+ ber_str2bv( on->on_bi.bi_type, 0, 0, &bv );
+ attr_merge_normalize_one( e, mi->mi_ad_monitorOverlay,
+ &bv, NULL );
+
+ /* find the overlay number, j */
+ for ( on2 = overlay_next( NULL ), j = 0; on2; on2 = overlay_next( on2 ), j++ ) {
+ if ( on2->on_bi.bi_type == on->on_bi.bi_type ) {
+ break;
+ }
+ }
+ assert( on2 != NULL );
+
+ snprintf( buf, sizeof( buf ),
+ "cn=Overlay %d,%s",
+ j, ms_overlay->mss_dn.bv_val );
+ ber_str2bv( buf, 0, 0, &bv );
+ attr_merge_normalize_one( e,
+ slap_schema.si_ad_seeAlso,
+ &bv, NULL );
+ }
+ }
+
+ j = -1;
+ LDAP_STAILQ_FOREACH( bi2, &backendInfo, bi_next ) {
+ j++;
+ if ( bi2->bi_type == bi->bi_type ) {
+ snprintf( buf, sizeof( buf ),
+ "cn=Backend %d,%s",
+ j, ms_backend->mss_dn.bv_val );
+ bv.bv_val = buf;
+ bv.bv_len = strlen( buf );
+ attr_merge_normalize_one( e,
+ slap_schema.si_ad_seeAlso,
+ &bv, NULL );
+ break;
+ }
+ }
+ /* we must find it! */
+ assert( j >= 0 );
+
+ mp = monitor_entrypriv_create();
+ if ( mp == NULL ) {
+ return -1;
+ }
+ e->e_private = ( void * )mp;
+ mp->mp_info = ms;
+ mp->mp_flags = ms->mss_flags
+ | MONITOR_F_SUB;
+ mp->mp_private = be;
+
+ if ( monitor_cache_add( mi, e ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_database_init_one: "
+ "unable to add entry \"%s,%s\"\n",
+ rdn->bv_val, ms->mss_dn.bv_val );
+ return( -1 );
+ }
+
+#if defined(LDAP_SLAPI)
+ monitor_back_add_plugin( mi, be, e );
+#endif /* defined(LDAP_SLAPI) */
+
+ if ( oi != NULL ) {
+ Entry **ep_overlay = &mp->mp_children;
+ slap_overinst *on = oi->oi_list;
+
+ for ( ; on; on = on->on_next ) {
+ monitor_subsys_overlay_init_one( mi, be,
+ ms, ms_overlay, on, e, ep_overlay );
+ }
+ }
+
+ **epp = e;
+ *epp = &mp->mp_next;
+
+ return 0;
+}
+
+static int
+monitor_back_register_database_and_overlay(
+ BackendDB *be,
+ struct slap_overinst *on,
+ struct berval *ndn_out )
+{
+ monitor_info_t *mi;
+ Entry *e_database, **ep;
+ int i, rc;
+ monitor_entry_t *mp;
+ monitor_subsys_t *ms_backend,
+ *ms_database,
+ *ms_overlay;
+ struct berval bv;
+ char buf[ BACKMONITOR_BUFSIZE ];
+
+ assert( be_monitor != NULL );
+
+ if ( !monitor_subsys_is_opened() ) {
+ if ( on ) {
+ return monitor_back_register_overlay_limbo( be, on, ndn_out );
+
+ } else {
+ return monitor_back_register_database_limbo( be, ndn_out );
+ }
+ }
+
+ mi = ( monitor_info_t * )be_monitor->be_private;
+
+ ms_backend = monitor_back_get_subsys( SLAPD_MONITOR_BACKEND_NAME );
+ if ( ms_backend == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_database: "
+ "unable to get "
+ "\"" SLAPD_MONITOR_BACKEND_NAME "\" "
+ "subsystem\n" );
+ return -1;
+ }
+
+ ms_database = monitor_back_get_subsys( SLAPD_MONITOR_DATABASE_NAME );
+ if ( ms_database == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_database: "
+ "unable to get "
+ "\"" SLAPD_MONITOR_DATABASE_NAME "\" "
+ "subsystem\n" );
+ return -1;
+ }
+
+ ms_overlay = monitor_back_get_subsys( SLAPD_MONITOR_OVERLAY_NAME );
+ if ( ms_overlay == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_database: "
+ "unable to get "
+ "\"" SLAPD_MONITOR_OVERLAY_NAME "\" "
+ "subsystem\n" );
+ return -1;
+ }
+
+ if ( monitor_cache_get( mi, &ms_database->mss_ndn, &e_database ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_database_init: "
+ "unable to get entry \"%s\"\n",
+ ms_database->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ mp = ( monitor_entry_t * )e_database->e_private;
+ for ( i = -1, ep = &mp->mp_children; *ep; i++ ) {
+ mp = ( monitor_entry_t * )(*ep)->e_private;
+
+ assert( mp != NULL );
+ if ( mp->mp_private == be->bd_self ) {
+ rc = 0;
+ goto done;
+ }
+ ep = &mp->mp_next;
+ }
+
+ bv.bv_val = buf;
+ bv.bv_len = snprintf( buf, sizeof( buf ), "cn=Database %d", i );
+ if ( bv.bv_len >= sizeof( buf ) ) {
+ rc = -1;
+ goto done;
+ }
+
+ rc = monitor_subsys_database_init_one( mi, be,
+ ms_database, ms_backend, ms_overlay, &bv, e_database, &ep );
+ if ( rc != 0 ) {
+ goto done;
+ }
+ /* database_init_one advanced ep past where we want.
+ * But it stored the entry we want in mp->mp_next.
+ */
+ ep = &mp->mp_next;
+
+done:;
+ monitor_cache_release( mi, e_database );
+ if ( rc == 0 && ndn_out && ep && *ep ) {
+ if ( on ) {
+ Entry *e_ov;
+ struct berval ov_type;
+
+ ber_str2bv( on->on_bi.bi_type, 0, 0, &ov_type );
+
+ mp = ( monitor_entry_t * ) (*ep)->e_private;
+ for ( e_ov = mp->mp_children; e_ov; ) {
+ Attribute *a = attr_find( e_ov->e_attrs, mi->mi_ad_monitoredInfo );
+
+ if ( a != NULL && bvmatch( &a->a_nvals[ 0 ], &ov_type ) ) {
+ *ndn_out = e_ov->e_nname;
+ break;
+ }
+
+ mp = ( monitor_entry_t * ) e_ov->e_private;
+ e_ov = mp->mp_next;
+ }
+
+ } else {
+ *ndn_out = (*ep)->e_nname;
+ }
+ }
+
+ return rc;
+}
+
+int
+monitor_back_register_database(
+ BackendDB *be,
+ struct berval *ndn_out )
+{
+ return monitor_back_register_database_and_overlay( be, NULL, ndn_out );
+}
+
+int
+monitor_back_register_overlay(
+ BackendDB *be,
+ struct slap_overinst *on,
+ struct berval *ndn_out )
+{
+ return monitor_back_register_database_and_overlay( be, on, ndn_out );
+}
+
+int
+monitor_subsys_database_init(
+ BackendDB *be,
+ monitor_subsys_t *ms )
+{
+ monitor_info_t *mi;
+ Entry *e_database, **ep;
+ int i, rc;
+ monitor_entry_t *mp;
+ monitor_subsys_t *ms_backend,
+ *ms_overlay;
+ struct berval bv;
+
+ assert( be != NULL );
+
+ ms->mss_modify = monitor_subsys_database_modify;
+
+ mi = ( monitor_info_t * )be->be_private;
+
+ ms_backend = monitor_back_get_subsys( SLAPD_MONITOR_BACKEND_NAME );
+ if ( ms_backend == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_database_init: "
+ "unable to get "
+ "\"" SLAPD_MONITOR_BACKEND_NAME "\" "
+ "subsystem\n" );
+ return -1;
+ }
+
+ ms_overlay = monitor_back_get_subsys( SLAPD_MONITOR_OVERLAY_NAME );
+ if ( ms_overlay == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_database_init: "
+ "unable to get "
+ "\"" SLAPD_MONITOR_OVERLAY_NAME "\" "
+ "subsystem\n" );
+ return -1;
+ }
+
+ if ( monitor_cache_get( mi, &ms->mss_ndn, &e_database ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_database_init: "
+ "unable to get entry \"%s\"\n",
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ (void)init_readOnly( mi, e_database, frontendDB->be_restrictops );
+ (void)init_restrictedOperation( mi, e_database, frontendDB->be_restrictops );
+
+ mp = ( monitor_entry_t * )e_database->e_private;
+ mp->mp_children = NULL;
+ ep = &mp->mp_children;
+
+ BER_BVSTR( &bv, "cn=Frontend" );
+ rc = monitor_subsys_database_init_one( mi, frontendDB,
+ ms, ms_backend, ms_overlay, &bv, e_database, &ep );
+ if ( rc != 0 ) {
+ return rc;
+ }
+
+ i = -1;
+ LDAP_STAILQ_FOREACH( be, &backendDB, be_next ) {
+ char buf[ BACKMONITOR_BUFSIZE ];
+
+ bv.bv_val = buf;
+ bv.bv_len = snprintf( buf, sizeof( buf ), "cn=Database %d", ++i );
+ if ( bv.bv_len >= sizeof( buf ) ) {
+ return -1;
+ }
+
+ rc = monitor_subsys_database_init_one( mi, be,
+ ms, ms_backend, ms_overlay, &bv, e_database, &ep );
+ if ( rc != 0 ) {
+ return rc;
+ }
+ }
+
+ monitor_cache_release( mi, e_database );
+
+ return( 0 );
+}
+
+/*
+ * v: array of values
+ * cur: must not contain the tags corresponding to the values in v
+ * delta: will contain the tags corresponding to the values in v
+ */
+static int
+value_mask( BerVarray v, slap_mask_t cur, slap_mask_t *delta )
+{
+ for ( ; !BER_BVISNULL( v ); v++ ) {
+ struct restricted_ops_t *rops;
+ int i;
+
+ if ( OID_LEADCHAR( v->bv_val[ 0 ] ) ) {
+ rops = restricted_exops;
+
+ } else {
+ rops = restricted_ops;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &rops[ i ].op ); i++ ) {
+ if ( ber_bvstrcasecmp( v, &rops[ i ].op ) != 0 ) {
+ continue;
+ }
+
+ if ( rops[ i ].tag & *delta ) {
+ return LDAP_OTHER;
+ }
+
+ if ( rops[ i ].tag & cur ) {
+ return LDAP_OTHER;
+ }
+
+ cur |= rops[ i ].tag;
+ *delta |= rops[ i ].tag;
+
+ break;
+ }
+
+ if ( BER_BVISNULL( &rops[ i ].op ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+monitor_subsys_database_modify(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e )
+{
+ monitor_info_t *mi = (monitor_info_t *)op->o_bd->be_private;
+ int rc = LDAP_OTHER;
+ Attribute *save_attrs, *a;
+ Modifications *ml;
+ Backend *be;
+ int ro_gotval = 1, i, n;
+ slap_mask_t rp_add = 0, rp_delete = 0, rp_cur;
+ struct berval *tf;
+
+ i = sscanf( e->e_nname.bv_val, "cn=database %d,", &n );
+ if ( i != 1 ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( n < 0 || n >= nBackendDB ) {
+ rs->sr_text = "invalid database index";
+ return ( rs->sr_err = LDAP_NO_SUCH_OBJECT );
+ }
+
+ LDAP_STAILQ_FOREACH( be, &backendDB, be_next ) {
+ if ( n == 0 ) {
+ break;
+ }
+ n--;
+ }
+ /* do not allow some changes on back-monitor (needs work)... */
+ if ( SLAP_MONITOR( be ) ) {
+ rs->sr_text = "no modifications allowed to monitor database entry";
+ return ( rs->sr_err = LDAP_UNWILLING_TO_PERFORM );
+ }
+
+ rp_cur = be->be_restrictops;
+
+ save_attrs = e->e_attrs;
+ e->e_attrs = attrs_dup( e->e_attrs );
+
+ for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
+ Modification *mod = &ml->sml_mod;
+
+ if ( mod->sm_desc == mi->mi_ad_readOnly ) {
+ int val = -1;
+
+ if ( mod->sm_values ) {
+ if ( !BER_BVISNULL( &mod->sm_values[ 1 ] ) ) {
+ rs->sr_text = "attempting to modify multiple values of single-valued attribute";
+ rc = rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+
+ if ( bvmatch( &slap_true_bv, mod->sm_values )) {
+ val = 1;
+
+ } else if ( bvmatch( &slap_false_bv, mod->sm_values )) {
+ val = 0;
+
+ } else {
+ assert( 0 );
+ rc = rs->sr_err = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+ }
+
+ switch ( mod->sm_op ) {
+ case LDAP_MOD_DELETE:
+ if ( ro_gotval < 1 ) {
+ rc = rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+ ro_gotval--;
+
+ if ( val == 0 && ( rp_cur & SLAP_RESTRICT_OP_WRITES ) == SLAP_RESTRICT_OP_WRITES ) {
+ rc = rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
+ goto done;
+ }
+
+ if ( val == 1 && ( rp_cur & SLAP_RESTRICT_OP_WRITES ) != SLAP_RESTRICT_OP_WRITES ) {
+ rc = rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
+ goto done;
+ }
+
+ break;
+
+ case LDAP_MOD_REPLACE:
+ ro_gotval = 0;
+ /* fall thru */
+
+ case LDAP_MOD_ADD:
+ if ( ro_gotval > 0 ) {
+ rc = rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+ ro_gotval++;
+
+ if ( val == 1 ) {
+ rp_add |= (~rp_cur) & SLAP_RESTRICT_OP_WRITES;
+ rp_cur |= SLAP_RESTRICT_OP_WRITES;
+ rp_delete &= ~SLAP_RESTRICT_OP_WRITES;
+
+ } else if ( val == 0 ) {
+ rp_delete |= rp_cur & SLAP_RESTRICT_OP_WRITES;
+ rp_cur &= ~SLAP_RESTRICT_OP_WRITES;
+ rp_add &= ~SLAP_RESTRICT_OP_WRITES;
+ }
+ break;
+
+ default:
+ rc = rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+
+ } else if ( mod->sm_desc == mi->mi_ad_restrictedOperation ) {
+ slap_mask_t mask = 0;
+
+ switch ( mod->sm_op ) {
+ case LDAP_MOD_DELETE:
+ if ( mod->sm_values == NULL ) {
+ rp_delete = rp_cur;
+ rp_cur = 0;
+ rp_add = 0;
+ break;
+ }
+ rc = value_mask( mod->sm_values, ~rp_cur, &mask );
+ if ( rc == LDAP_SUCCESS ) {
+ rp_delete |= mask;
+ rp_add &= ~mask;
+ rp_cur &= ~mask;
+
+ } else if ( rc == LDAP_OTHER ) {
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ }
+ break;
+
+ case LDAP_MOD_REPLACE:
+ rp_delete = rp_cur;
+ rp_cur = 0;
+ rp_add = 0;
+ /* fall thru */
+
+ case LDAP_MOD_ADD:
+ rc = value_mask( mod->sm_values, rp_cur, &mask );
+ if ( rc == LDAP_SUCCESS ) {
+ rp_add |= mask;
+ rp_cur |= mask;
+ rp_delete &= ~mask;
+
+ } else if ( rc == LDAP_OTHER ) {
+ rc = rs->sr_err = LDAP_TYPE_OR_VALUE_EXISTS;
+ }
+ break;
+
+ default:
+ rc = rs->sr_err = LDAP_OTHER;
+ break;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ } else if ( is_at_operational( mod->sm_desc->ad_type )) {
+ /* accept all operational attributes */
+ attr_delete( &e->e_attrs, mod->sm_desc );
+ rc = attr_merge( e, mod->sm_desc, mod->sm_values,
+ mod->sm_nvalues );
+ if ( rc ) {
+ rc = rs->sr_err = LDAP_OTHER;
+ break;
+ }
+
+ } else {
+ rc = rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ break;
+ }
+ }
+
+ /* sanity checks: */
+ if ( ro_gotval < 1 ) {
+ rc = rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+
+ if ( ( rp_cur & SLAP_RESTRICT_OP_EXTENDED ) && ( rp_cur & SLAP_RESTRICT_EXOP_MASK ) ) {
+ rc = rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+
+ if ( rp_delete & rp_add ) {
+ rc = rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+
+ /* check current value of readOnly */
+ if ( ( rp_cur & SLAP_RESTRICT_OP_WRITES ) == SLAP_RESTRICT_OP_WRITES ) {
+ tf = (struct berval *)&slap_true_bv;
+
+ } else {
+ tf = (struct berval *)&slap_false_bv;
+ }
+
+ a = attr_find( e->e_attrs, mi->mi_ad_readOnly );
+ if ( a == NULL ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ if ( !bvmatch( &a->a_vals[ 0 ], tf ) ) {
+ attr_delete( &e->e_attrs, mi->mi_ad_readOnly );
+ rc = attr_merge_one( e, mi->mi_ad_readOnly, tf, tf );
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ if ( rp_delete ) {
+ if ( rp_delete == be->be_restrictops ) {
+ attr_delete( &e->e_attrs, mi->mi_ad_restrictedOperation );
+
+ } else {
+ a = attr_find( e->e_attrs, mi->mi_ad_restrictedOperation );
+ if ( a == NULL ) {
+ rc = rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &restricted_ops[ i ].op ); i++ ) {
+ if ( rp_delete & restricted_ops[ i ].tag ) {
+ int j;
+
+ for ( j = 0; !BER_BVISNULL( &a->a_nvals[ j ] ); j++ ) {
+ int k;
+
+ if ( !bvmatch( &a->a_nvals[ j ], &restricted_ops[ i ].op ) ) {
+ continue;
+ }
+
+ ch_free( a->a_vals[ j ].bv_val );
+ ch_free( a->a_nvals[ j ].bv_val );
+
+ for ( k = j + 1; !BER_BVISNULL( &a->a_nvals[ k ] ); k++ ) {
+ a->a_vals[ k - 1 ] = a->a_vals[ k ];
+ a->a_nvals[ k - 1 ] = a->a_nvals[ k ];
+ }
+
+ BER_BVZERO( &a->a_vals[ k - 1 ] );
+ BER_BVZERO( &a->a_nvals[ k - 1 ] );
+ a->a_numvals--;
+ }
+ }
+ }
+
+ for ( i = 0; !BER_BVISNULL( &restricted_exops[ i ].op ); i++ ) {
+ if ( rp_delete & restricted_exops[ i ].tag ) {
+ int j;
+
+ for ( j = 0; !BER_BVISNULL( &a->a_nvals[ j ] ); j++ ) {
+ int k;
+
+ if ( !bvmatch( &a->a_nvals[ j ], &restricted_exops[ i ].op ) ) {
+ continue;
+ }
+
+ ch_free( a->a_vals[ j ].bv_val );
+ ch_free( a->a_nvals[ j ].bv_val );
+
+ for ( k = j + 1; !BER_BVISNULL( &a->a_nvals[ k ] ); k++ ) {
+ a->a_vals[ k - 1 ] = a->a_vals[ k ];
+ a->a_nvals[ k - 1 ] = a->a_nvals[ k ];
+ }
+
+ BER_BVZERO( &a->a_vals[ k - 1 ] );
+ BER_BVZERO( &a->a_nvals[ k - 1 ] );
+ a->a_numvals--;
+ }
+ }
+ }
+
+ if ( a->a_vals == NULL ) {
+ assert( a->a_numvals == 0 );
+
+ attr_delete( &e->e_attrs, mi->mi_ad_restrictedOperation );
+ }
+ }
+ }
+
+ if ( rp_add ) {
+ for ( i = 0; !BER_BVISNULL( &restricted_ops[ i ].op ); i++ ) {
+ if ( rp_add & restricted_ops[ i ].tag ) {
+ attr_merge_one( e, mi->mi_ad_restrictedOperation,
+ &restricted_ops[ i ].op,
+ &restricted_ops[ i ].op );
+ }
+ }
+
+ for ( i = 0; !BER_BVISNULL( &restricted_exops[ i ].op ); i++ ) {
+ if ( rp_add & restricted_exops[ i ].tag ) {
+ attr_merge_one( e, mi->mi_ad_restrictedOperation,
+ &restricted_exops[ i ].op,
+ &restricted_exops[ i ].op );
+ }
+ }
+ }
+ }
+
+ be->be_restrictops = rp_cur;
+
+done:;
+ if ( rc == LDAP_SUCCESS ) {
+ attrs_free( save_attrs );
+ rc = SLAP_CB_CONTINUE;
+
+ } else {
+ Attribute *tmp = e->e_attrs;
+ e->e_attrs = save_attrs;
+ attrs_free( tmp );
+ }
+ return rc;
+}
+
+#if defined(LDAP_SLAPI)
+static int
+monitor_back_add_plugin( monitor_info_t *mi, Backend *be, Entry *e_database )
+{
+ Slapi_PBlock *pCurrentPB;
+ int i, rc = LDAP_SUCCESS;
+
+ if ( slapi_int_pblock_get_first( be, &pCurrentPB ) != LDAP_SUCCESS ) {
+ /*
+ * LDAP_OTHER is returned if no plugins are installed
+ */
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ i = 0;
+ do {
+ Slapi_PluginDesc *srchdesc;
+ char buf[ BACKMONITOR_BUFSIZE ];
+ struct berval bv;
+
+ rc = slapi_pblock_get( pCurrentPB, SLAPI_PLUGIN_DESCRIPTION,
+ &srchdesc );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ if ( srchdesc ) {
+ snprintf( buf, sizeof(buf),
+ "plugin %d name: %s; "
+ "vendor: %s; "
+ "version: %s; "
+ "description: %s",
+ i,
+ srchdesc->spd_id,
+ srchdesc->spd_vendor,
+ srchdesc->spd_version,
+ srchdesc->spd_description );
+ } else {
+ snprintf( buf, sizeof(buf),
+ "plugin %d name: <no description available>", i );
+ }
+
+ ber_str2bv( buf, 0, 0, &bv );
+ attr_merge_normalize_one( e_database,
+ mi->mi_ad_monitoredInfo, &bv, NULL );
+
+ i++;
+
+ } while ( ( slapi_int_pblock_get_next( &pCurrentPB ) == LDAP_SUCCESS )
+ && ( pCurrentPB != NULL ) );
+
+done:
+ return rc;
+}
+#endif /* defined(LDAP_SLAPI) */
diff --git a/servers/slapd/back-monitor/entry.c b/servers/slapd/back-monitor/entry.c
new file mode 100644
index 0000000..027dcc3
--- /dev/null
+++ b/servers/slapd/back-monitor/entry.c
@@ -0,0 +1,236 @@
+/* entry.c - monitor backend entry handling routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <slap.h>
+#include "back-monitor.h"
+
+int
+monitor_entry_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e
+)
+{
+ monitor_info_t *mi = ( monitor_info_t * )op->o_bd->be_private;
+ monitor_entry_t *mp;
+
+ int rc = SLAP_CB_CONTINUE;
+
+ assert( mi != NULL );
+ assert( e != NULL );
+ assert( e->e_private != NULL );
+
+ mp = ( monitor_entry_t * )e->e_private;
+
+ if ( mp->mp_cb ) {
+ struct monitor_callback_t *mc;
+
+ for ( mc = mp->mp_cb; mc; mc = mc->mc_next ) {
+ if ( mc->mc_update ) {
+ rc = mc->mc_update( op, rs, e, mc->mc_private );
+ if ( rc != SLAP_CB_CONTINUE ) {
+ break;
+ }
+ }
+ }
+ }
+
+ if ( rc == SLAP_CB_CONTINUE && mp->mp_info && mp->mp_info->mss_update ) {
+ rc = mp->mp_info->mss_update( op, rs, e );
+ }
+
+ if ( rc == SLAP_CB_CONTINUE ) {
+ rc = LDAP_SUCCESS;
+ }
+
+ return rc;
+}
+
+int
+monitor_entry_create(
+ Operation *op,
+ SlapReply *rs,
+ struct berval *ndn,
+ Entry *e_parent,
+ Entry **ep )
+{
+ monitor_info_t *mi = ( monitor_info_t * )op->o_bd->be_private;
+ monitor_entry_t *mp;
+
+ int rc = SLAP_CB_CONTINUE;
+
+ assert( mi != NULL );
+ assert( e_parent != NULL );
+ assert( e_parent->e_private != NULL );
+ assert( ep != NULL );
+
+ mp = ( monitor_entry_t * )e_parent->e_private;
+
+ if ( mp->mp_info && mp->mp_info->mss_create ) {
+ rc = mp->mp_info->mss_create( op, rs, ndn, e_parent, ep );
+ }
+
+ if ( rc == SLAP_CB_CONTINUE ) {
+ rc = LDAP_SUCCESS;
+ }
+
+ return rc;
+}
+
+int
+monitor_entry_modify(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e
+)
+{
+ monitor_info_t *mi = ( monitor_info_t * )op->o_bd->be_private;
+ monitor_entry_t *mp;
+
+ int rc = SLAP_CB_CONTINUE;
+
+ assert( mi != NULL );
+ assert( e != NULL );
+ assert( e->e_private != NULL );
+
+ mp = ( monitor_entry_t * )e->e_private;
+
+ if ( mp->mp_cb ) {
+ struct monitor_callback_t *mc;
+
+ for ( mc = mp->mp_cb; mc; mc = mc->mc_next ) {
+ if ( mc->mc_modify ) {
+ rc = mc->mc_modify( op, rs, e, mc->mc_private );
+ if ( rc != SLAP_CB_CONTINUE ) {
+ break;
+ }
+ }
+ }
+ }
+
+ if ( rc == SLAP_CB_CONTINUE && mp->mp_info && mp->mp_info->mss_modify ) {
+ rc = mp->mp_info->mss_modify( op, rs, e );
+ }
+
+ if ( rc == SLAP_CB_CONTINUE ) {
+ rc = LDAP_SUCCESS;
+ }
+
+ return rc;
+}
+
+int
+monitor_entry_test_flags(
+ monitor_entry_t *mp,
+ int cond
+)
+{
+ assert( mp != NULL );
+
+ return( ( mp->mp_flags & cond ) || ( mp->mp_info->mss_flags & cond ) );
+}
+
+monitor_entry_t *
+monitor_back_entrypriv_create( void )
+{
+ monitor_entry_t *mp;
+
+ mp = ( monitor_entry_t * )ch_calloc( sizeof( monitor_entry_t ), 1 );
+
+ mp->mp_next = NULL;
+ mp->mp_children = NULL;
+ mp->mp_info = NULL;
+ mp->mp_flags = MONITOR_F_NONE;
+ mp->mp_cb = NULL;
+
+ ldap_pvt_thread_mutex_init( &mp->mp_mutex );
+
+ return mp;
+}
+
+Entry *
+monitor_entry_stub(
+ struct berval *pdn,
+ struct berval *pndn,
+ struct berval *rdn,
+ ObjectClass *oc,
+ struct berval *create,
+ struct berval *modify
+)
+{
+ monitor_info_t *mi;
+ AttributeDescription *nad = NULL;
+ Entry *e;
+ struct berval nat;
+ char *ptr;
+ const char *text;
+ int rc;
+
+ mi = ( monitor_info_t * )be_monitor->be_private;
+
+ nat = *rdn;
+ ptr = strchr( nat.bv_val, '=' );
+ nat.bv_len = ptr - nat.bv_val;
+ rc = slap_bv2ad( &nat, &nad, &text );
+ if ( rc )
+ return NULL;
+
+ e = entry_alloc();
+ if ( e ) {
+ struct berval nrdn;
+
+ rdnNormalize( 0, NULL, NULL, rdn, &nrdn, NULL );
+ build_new_dn( &e->e_name, pdn, rdn, NULL );
+ build_new_dn( &e->e_nname, pndn, &nrdn, NULL );
+ ber_memfree( nrdn.bv_val );
+ nat.bv_val = ptr + 1;
+ nat.bv_len = rdn->bv_len - ( nat.bv_val - rdn->bv_val );
+ attr_merge_normalize_one( e, slap_schema.si_ad_objectClass,
+ &oc->soc_cname, NULL );
+ attr_merge_normalize_one( e, slap_schema.si_ad_structuralObjectClass,
+ &oc->soc_cname, NULL );
+ attr_merge_normalize_one( e, nad, &nat, NULL );
+ attr_merge_one( e, slap_schema.si_ad_creatorsName, &mi->mi_creatorsName,
+ &mi->mi_ncreatorsName );
+ attr_merge_one( e, slap_schema.si_ad_modifiersName, &mi->mi_creatorsName,
+ &mi->mi_ncreatorsName );
+ attr_merge_normalize_one( e, slap_schema.si_ad_createTimestamp,
+ create ? create : &mi->mi_startTime, NULL );
+ attr_merge_normalize_one( e, slap_schema.si_ad_modifyTimestamp,
+ modify ? modify : &mi->mi_startTime, NULL );
+ }
+ return e;
+}
+
+Entry *
+monitor_entry_get_unlocked(
+ struct berval *ndn
+)
+{
+ monitor_info_t *mi = ( monitor_info_t * )be_monitor->be_private;
+ Entry *ret = NULL;
+
+ if ( !monitor_cache_get( mi, ndn, &ret ))
+ monitor_cache_release( mi, ret );
+ return ret;
+}
diff --git a/servers/slapd/back-monitor/init.c b/servers/slapd/back-monitor/init.c
new file mode 100644
index 0000000..43749dd
--- /dev/null
+++ b/servers/slapd/back-monitor/init.c
@@ -0,0 +1,2573 @@
+/* init.c - initialize monitor backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include <lutil.h>
+#include "slap.h"
+#include "slap-config.h"
+#include "lber_pvt.h"
+#include "back-monitor.h"
+
+#include "slap-config.h"
+
+#undef INTEGRATE_CORE_SCHEMA
+
+/*
+ * used by many functions to add description to entries
+ *
+ * WARNING: be_monitor may change as new databases are added,
+ * so it should not be used outside monitor_back_db_init()
+ * until monitor_back_db_open is called.
+ */
+BackendDB *be_monitor;
+
+static struct monitor_subsys_t **monitor_subsys;
+static int monitor_subsys_opened;
+static monitor_info_t monitor_info;
+static const monitor_extra_t monitor_extra = {
+ monitor_back_is_configured,
+ monitor_back_get_subsys,
+ monitor_back_get_subsys_by_dn,
+
+ monitor_back_register_subsys,
+ monitor_back_register_backend,
+ monitor_back_register_database,
+ monitor_back_register_overlay_info,
+ monitor_back_register_overlay,
+ monitor_back_register_entry,
+ monitor_back_register_entry_parent,
+ monitor_back_register_entry_attrs,
+ monitor_back_register_entry_callback,
+
+ monitor_back_unregister_entry,
+ monitor_back_unregister_entry_parent,
+ monitor_back_unregister_entry_attrs,
+ monitor_back_unregister_entry_callback,
+
+ monitor_back_entry_stub,
+ monitor_back_entrypriv_create,
+ monitor_back_register_subsys_late,
+ monitor_back_entry_get_unlocked
+};
+
+
+/*
+ * subsystem data
+ *
+ * the known subsystems are added to the subsystems
+ * array at backend initialization; other subsystems
+ * may be added by calling monitor_back_register_subsys()
+ * before the database is opened (e.g. by other backends
+ * or by overlays or modules).
+ */
+static struct monitor_subsys_t known_monitor_subsys[] = {
+ {
+ SLAPD_MONITOR_BACKEND_NAME,
+ BER_BVNULL, BER_BVNULL, BER_BVNULL,
+ { BER_BVC( "This subsystem contains information about available backends." ),
+ BER_BVNULL },
+ MONITOR_F_PERSISTENT_CH,
+ monitor_subsys_backend_init,
+ NULL, /* destroy */
+ NULL, /* update */
+ NULL, /* create */
+ NULL /* modify */
+ }, {
+ SLAPD_MONITOR_CONN_NAME,
+ BER_BVNULL, BER_BVNULL, BER_BVNULL,
+ { BER_BVC( "This subsystem contains information about connections." ),
+ BER_BVNULL },
+ MONITOR_F_VOLATILE_CH,
+ monitor_subsys_conn_init,
+ NULL, /* destroy */
+ NULL, /* update */
+ NULL, /* create */
+ NULL /* modify */
+ }, {
+ SLAPD_MONITOR_DATABASE_NAME,
+ BER_BVNULL, BER_BVNULL, BER_BVNULL,
+ { BER_BVC( "This subsystem contains information about configured databases." ),
+ BER_BVNULL },
+ MONITOR_F_PERSISTENT_CH,
+ monitor_subsys_database_init,
+ NULL, /* destroy */
+ NULL, /* update */
+ NULL, /* create */
+ NULL /* modify */
+ }, {
+ SLAPD_MONITOR_LISTENER_NAME,
+ BER_BVNULL, BER_BVNULL, BER_BVNULL,
+ { BER_BVC( "This subsystem contains information about active listeners." ),
+ BER_BVNULL },
+ MONITOR_F_PERSISTENT_CH,
+ monitor_subsys_listener_init,
+ NULL, /* destroy */
+ NULL, /* update */
+ NULL, /* create */
+ NULL /* modify */
+ }, {
+ SLAPD_MONITOR_LOG_NAME,
+ BER_BVNULL, BER_BVNULL, BER_BVNULL,
+ { BER_BVC( "This subsystem contains information about logging." ),
+ BER_BVC( "Set the attribute \"managedInfo\" to the desired log levels." ),
+ BER_BVNULL },
+ MONITOR_F_NONE,
+ monitor_subsys_log_init,
+ NULL, /* destroy */
+ NULL, /* update */
+ NULL, /* create */
+ NULL, /* modify */
+ }, {
+ SLAPD_MONITOR_OPS_NAME,
+ BER_BVNULL, BER_BVNULL, BER_BVNULL,
+ { BER_BVC( "This subsystem contains information about performed operations." ),
+ BER_BVNULL },
+ MONITOR_F_PERSISTENT_CH,
+ monitor_subsys_ops_init,
+ NULL, /* destroy */
+ NULL, /* update */
+ NULL, /* create */
+ NULL, /* modify */
+ }, {
+ SLAPD_MONITOR_OVERLAY_NAME,
+ BER_BVNULL, BER_BVNULL, BER_BVNULL,
+ { BER_BVC( "This subsystem contains information about available overlays." ),
+ BER_BVNULL },
+ MONITOR_F_PERSISTENT_CH,
+ monitor_subsys_overlay_init,
+ NULL, /* destroy */
+ NULL, /* update */
+ NULL, /* create */
+ NULL, /* modify */
+ }, {
+ SLAPD_MONITOR_SASL_NAME,
+ BER_BVNULL, BER_BVNULL, BER_BVNULL,
+ { BER_BVC( "This subsystem contains information about SASL." ),
+ BER_BVNULL },
+ MONITOR_F_NONE,
+ NULL, /* init */
+ NULL, /* destroy */
+ NULL, /* update */
+ NULL, /* create */
+ NULL /* modify */
+ }, {
+ SLAPD_MONITOR_SENT_NAME,
+ BER_BVNULL, BER_BVNULL, BER_BVNULL,
+ { BER_BVC( "This subsystem contains statistics." ),
+ BER_BVNULL },
+ MONITOR_F_PERSISTENT_CH,
+ monitor_subsys_sent_init,
+ NULL, /* destroy */
+ NULL, /* update */
+ NULL, /* create */
+ NULL, /* modify */
+ }, {
+ SLAPD_MONITOR_THREAD_NAME,
+ BER_BVNULL, BER_BVNULL, BER_BVNULL,
+ { BER_BVC( "This subsystem contains information about threads." ),
+ BER_BVNULL },
+ MONITOR_F_PERSISTENT_CH,
+ monitor_subsys_thread_init,
+ NULL, /* destroy */
+ NULL, /* update */
+ NULL, /* create */
+ NULL /* modify */
+ }, {
+ SLAPD_MONITOR_TIME_NAME,
+ BER_BVNULL, BER_BVNULL, BER_BVNULL,
+ { BER_BVC( "This subsystem contains information about time." ),
+ BER_BVNULL },
+ MONITOR_F_PERSISTENT_CH,
+ monitor_subsys_time_init,
+ NULL, /* destroy */
+ NULL, /* update */
+ NULL, /* create */
+ NULL, /* modify */
+ }, {
+ SLAPD_MONITOR_TLS_NAME,
+ BER_BVNULL, BER_BVNULL, BER_BVNULL,
+ { BER_BVC( "This subsystem contains information about TLS." ),
+ BER_BVNULL },
+ MONITOR_F_NONE,
+ NULL, /* init */
+ NULL, /* destroy */
+ NULL, /* update */
+ NULL, /* create */
+ NULL /* modify */
+ }, {
+ SLAPD_MONITOR_RWW_NAME,
+ BER_BVNULL, BER_BVNULL, BER_BVNULL,
+ { BER_BVC( "This subsystem contains information about read/write waiters." ),
+ BER_BVNULL },
+ MONITOR_F_PERSISTENT_CH,
+ monitor_subsys_rww_init,
+ NULL, /* destroy */
+ NULL, /* update */
+ NULL, /* create */
+ NULL /* modify */
+ }, { NULL }
+};
+
+int
+monitor_subsys_is_opened( void )
+{
+ return monitor_subsys_opened;
+}
+
+int
+monitor_back_register_subsys(
+ monitor_subsys_t *ms )
+{
+ int i = 0;
+
+ if ( monitor_subsys ) {
+ for ( ; monitor_subsys[ i ] != NULL; i++ )
+ /* just count'em */ ;
+ }
+
+ monitor_subsys = ch_realloc( monitor_subsys,
+ ( 2 + i ) * sizeof( monitor_subsys_t * ) );
+
+ if ( monitor_subsys == NULL ) {
+ return -1;
+ }
+
+ monitor_subsys[ i ] = ms;
+ monitor_subsys[ i + 1 ] = NULL;
+
+ /* if a subsystem is registered __AFTER__ subsystem
+ * initialization (depending on the sequence the databases
+ * are listed in slapd.conf), init it */
+ if ( monitor_subsys_is_opened() ) {
+
+ /* FIXME: this should only be possible
+ * if be_monitor is already initialized */
+ assert( be_monitor != NULL );
+
+ if ( ms->mss_open && ( *ms->mss_open )( be_monitor, ms ) ) {
+ return -1;
+ }
+
+ ms->mss_flags |= MONITOR_F_OPENED;
+ }
+
+ return 0;
+}
+
+enum {
+ LIMBO_ENTRY,
+ LIMBO_ENTRY_PARENT,
+ LIMBO_ATTRS,
+ LIMBO_CB,
+ LIMBO_BACKEND,
+ LIMBO_DATABASE,
+ LIMBO_OVERLAY_INFO,
+ LIMBO_OVERLAY,
+ LIMBO_SUBSYS,
+
+ LIMBO_LAST
+};
+
+typedef struct entry_limbo_t {
+ int el_type;
+ BackendInfo *el_bi;
+ BackendDB *el_be;
+ slap_overinst *el_on;
+ Entry *el_e;
+ Attribute *el_a;
+ struct berval *el_ndn;
+ struct berval el_nbase;
+ int el_scope;
+ struct berval el_filter;
+ monitor_callback_t *el_cb;
+ monitor_subsys_t *el_mss;
+ unsigned long el_flags;
+ struct entry_limbo_t *el_next;
+} entry_limbo_t;
+
+int
+monitor_back_is_configured( void )
+{
+ return be_monitor != NULL;
+}
+
+int
+monitor_back_register_subsys_late(
+ monitor_subsys_t *ms )
+{
+ entry_limbo_t **elpp, el = { 0 };
+ monitor_info_t *mi;
+
+ if ( be_monitor == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_subsys_late: "
+ "monitor database not configured.\n" );
+ return -1;
+ }
+
+ /* everything is ready, can register already */
+ if ( monitor_subsys_is_opened() ) {
+ return monitor_back_register_subsys( ms );
+ }
+
+ mi = ( monitor_info_t * )be_monitor->be_private;
+
+
+ el.el_type = LIMBO_SUBSYS;
+
+ el.el_mss = ms;
+
+ for ( elpp = &mi->mi_entry_limbo;
+ *elpp;
+ elpp = &(*elpp)->el_next )
+ /* go to last */;
+
+ *elpp = (entry_limbo_t *)ch_malloc( sizeof( entry_limbo_t ) );
+
+ el.el_next = NULL;
+ **elpp = el;
+
+ return 0;
+}
+
+int
+monitor_back_register_backend(
+ BackendInfo *bi )
+{
+ return -1;
+}
+
+int
+monitor_back_register_overlay_info(
+ slap_overinst *on )
+{
+ return -1;
+}
+
+int
+monitor_back_register_backend_limbo(
+ BackendInfo *bi )
+{
+ return -1;
+}
+
+int
+monitor_back_register_database_limbo(
+ BackendDB *be,
+ struct berval *ndn_out )
+{
+ entry_limbo_t **elpp, el = { 0 };
+ monitor_info_t *mi;
+
+ if ( be_monitor == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_database_limbo: "
+ "monitor database not configured.\n" );
+ return -1;
+ }
+
+ mi = ( monitor_info_t * )be_monitor->be_private;
+
+
+ el.el_type = LIMBO_DATABASE;
+
+ el.el_be = be->bd_self;
+ el.el_ndn = ndn_out;
+
+ for ( elpp = &mi->mi_entry_limbo;
+ *elpp;
+ elpp = &(*elpp)->el_next )
+ /* go to last */;
+
+ *elpp = (entry_limbo_t *)ch_malloc( sizeof( entry_limbo_t ) );
+
+ el.el_next = NULL;
+ **elpp = el;
+
+ return 0;
+}
+
+int
+monitor_back_register_overlay_info_limbo(
+ slap_overinst *on )
+{
+ return -1;
+}
+
+int
+monitor_back_register_overlay_limbo(
+ BackendDB *be,
+ struct slap_overinst *on,
+ struct berval *ndn_out )
+{
+ entry_limbo_t **elpp, el = { 0 };
+ monitor_info_t *mi;
+
+ if ( be_monitor == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_overlay_limbo: "
+ "monitor database not configured.\n" );
+ return -1;
+ }
+
+ mi = ( monitor_info_t * )be_monitor->be_private;
+
+
+ el.el_type = LIMBO_OVERLAY;
+
+ el.el_be = be->bd_self;
+ el.el_on = on;
+ el.el_ndn = ndn_out;
+
+ for ( elpp = &mi->mi_entry_limbo;
+ *elpp;
+ elpp = &(*elpp)->el_next )
+ /* go to last */;
+
+ *elpp = (entry_limbo_t *)ch_malloc( sizeof( entry_limbo_t ) );
+
+ el.el_next = NULL;
+ **elpp = el;
+
+ return 0;
+}
+
+int
+monitor_back_register_entry(
+ Entry *e,
+ monitor_callback_t *cb,
+ monitor_subsys_t *mss,
+ unsigned long flags )
+{
+ monitor_info_t *mi;
+ int rc = 0;
+
+ if ( be_monitor == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry(\"%s\"): "
+ "monitor database not configured.\n",
+ e->e_name.bv_val );
+ return -1;
+ }
+
+ mi = ( monitor_info_t * )be_monitor->be_private;
+
+ assert( mi != NULL );
+ assert( e != NULL );
+ assert( e->e_private == NULL );
+
+ if ( monitor_subsys_is_opened() ) {
+ Entry *e_parent = NULL,
+ *e_new = NULL,
+ **ep = NULL;
+ struct berval pdn = BER_BVNULL;
+ monitor_entry_t *mp = NULL,
+ *mp_parent = NULL;
+
+ if ( monitor_cache_get( mi, &e->e_nname, &e_parent ) == 0 ) {
+ /* entry exists */
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry(\"%s\"): "
+ "entry exists\n",
+ e->e_name.bv_val );
+ monitor_cache_release( mi, e_parent );
+ return -1;
+ }
+
+ dnParent( &e->e_nname, &pdn );
+ if ( monitor_cache_get( mi, &pdn, &e_parent ) != 0 ) {
+ /* parent does not exist */
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry(\"%s\"): "
+ "parent \"%s\" not found\n",
+ e->e_name.bv_val, pdn.bv_val );
+ return -1;
+ }
+
+ assert( e_parent->e_private != NULL );
+ mp_parent = ( monitor_entry_t * )e_parent->e_private;
+
+ if ( mp_parent->mp_flags & MONITOR_F_VOLATILE ) {
+ /* entry is volatile; cannot append children */
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry(\"%s\"): "
+ "parent \"%s\" is volatile\n",
+ e->e_name.bv_val, e_parent->e_name.bv_val );
+ rc = -1;
+ goto done;
+ }
+
+ mp = monitor_entrypriv_create();
+ if ( mp == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry(\"%s\"): "
+ "monitor_entrypriv_create() failed\n",
+ e->e_name.bv_val );
+ rc = -1;
+ goto done;
+ }
+
+ e_new = entry_dup( e );
+ if ( e_new == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry(\"%s\"): "
+ "entry_dup() failed\n",
+ e->e_name.bv_val );
+ rc = -1;
+ goto done;
+ }
+
+ e_new->e_private = ( void * )mp;
+ if ( mss != NULL ) {
+ mp->mp_info = mss;
+ mp->mp_flags = flags;
+
+ } else {
+ mp->mp_info = mp_parent->mp_info;
+ mp->mp_flags = mp_parent->mp_flags | MONITOR_F_SUB;
+ }
+ mp->mp_cb = cb;
+
+ ep = &mp_parent->mp_children;
+ for ( ; *ep; ) {
+ mp_parent = ( monitor_entry_t * )(*ep)->e_private;
+ ep = &mp_parent->mp_next;
+ }
+ *ep = e_new;
+
+ if ( monitor_cache_add( mi, e_new ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry(\"%s\"): "
+ "unable to add entry\n",
+ e->e_name.bv_val );
+ rc = -1;
+ goto done;
+ }
+
+done:;
+ if ( rc ) {
+ if ( mp ) {
+ ch_free( mp );
+ }
+ if ( e_new ) {
+ e_new->e_private = NULL;
+ entry_free( e_new );
+ }
+ }
+
+ if ( e_parent ) {
+ monitor_cache_release( mi, e_parent );
+ }
+
+ } else {
+ entry_limbo_t **elpp, el = { 0 };
+
+ el.el_type = LIMBO_ENTRY;
+
+ el.el_e = entry_dup( e );
+ if ( el.el_e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry(\"%s\"): "
+ "entry_dup() failed\n",
+ e->e_name.bv_val );
+ return -1;
+ }
+
+ el.el_cb = cb;
+ el.el_mss = mss;
+ el.el_flags = flags;
+
+ for ( elpp = &mi->mi_entry_limbo;
+ *elpp;
+ elpp = &(*elpp)->el_next )
+ /* go to last */;
+
+ *elpp = (entry_limbo_t *)ch_malloc( sizeof( entry_limbo_t ) );
+ if ( *elpp == NULL ) {
+ el.el_e->e_private = NULL;
+ entry_free( el.el_e );
+ return -1;
+ }
+
+ el.el_next = NULL;
+ **elpp = el;
+ }
+
+ return rc;
+}
+
+int
+monitor_back_register_entry_parent(
+ Entry *e,
+ monitor_callback_t *cb,
+ monitor_subsys_t *mss,
+ unsigned long flags,
+ struct berval *nbase,
+ int scope,
+ struct berval *filter )
+{
+ monitor_info_t *mi;
+ struct berval ndn = BER_BVNULL;
+
+ if ( be_monitor == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry_parent(base=\"%s\" scope=%s filter=\"%s\"): "
+ "monitor database not configured.\n",
+ BER_BVISNULL( nbase ) ? "" : nbase->bv_val,
+ ldap_pvt_scope2str( scope ),
+ BER_BVISNULL( filter ) ? "" : filter->bv_val );
+ return -1;
+ }
+
+ mi = ( monitor_info_t * )be_monitor->be_private;
+
+ assert( mi != NULL );
+ assert( e != NULL );
+ assert( e->e_private == NULL );
+
+ if ( BER_BVISNULL( filter ) ) {
+ /* need a filter */
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry_parent(\"\"): "
+ "need a valid filter\n" );
+ return -1;
+ }
+
+ if ( monitor_subsys_is_opened() ) {
+ Entry *e_parent = NULL,
+ *e_new = NULL,
+ **ep = NULL;
+ struct berval e_name = BER_BVNULL,
+ e_nname = BER_BVNULL;
+ monitor_entry_t *mp = NULL,
+ *mp_parent = NULL;
+ int rc = 0;
+
+ if ( monitor_search2ndn( nbase, scope, filter, &ndn ) ) {
+ /* entry does not exist */
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry_parent(\"\"): "
+ "base=\"%s\" scope=%s filter=\"%s\": "
+ "unable to find entry\n",
+ nbase->bv_val ? nbase->bv_val : "\"\"",
+ ldap_pvt_scope2str( scope ),
+ filter->bv_val );
+ return -1;
+ }
+
+ if ( monitor_cache_get( mi, &ndn, &e_parent ) != 0 ) {
+ /* entry does not exist */
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry_parent(\"%s\"): "
+ "parent entry does not exist\n",
+ ndn.bv_val );
+ rc = -1;
+ goto done;
+ }
+
+ assert( e_parent->e_private != NULL );
+ mp_parent = ( monitor_entry_t * )e_parent->e_private;
+
+ if ( mp_parent->mp_flags & MONITOR_F_VOLATILE ) {
+ /* entry is volatile; cannot append callback */
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry_parent(\"%s\"): "
+ "entry is volatile\n",
+ e_parent->e_name.bv_val );
+ rc = -1;
+ goto done;
+ }
+
+ build_new_dn( &e_name, &e_parent->e_name, &e->e_name, NULL );
+ build_new_dn( &e_nname, &e_parent->e_nname, &e->e_nname, NULL );
+
+ if ( monitor_cache_get( mi, &e_nname, &e_new ) == 0 ) {
+ /* entry already exists */
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry_parent(\"%s\"): "
+ "entry already exists\n",
+ e_name.bv_val );
+ monitor_cache_release( mi, e_new );
+ e_new = NULL;
+ rc = -1;
+ goto done;
+ }
+
+ mp = monitor_entrypriv_create();
+ if ( mp == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry_parent(\"%s\"): "
+ "monitor_entrypriv_create() failed\n",
+ e->e_name.bv_val );
+ rc = -1;
+ goto done;
+ }
+
+ e_new = entry_dup( e );
+ if ( e_new == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry(\"%s\"): "
+ "entry_dup() failed\n",
+ e->e_name.bv_val );
+ rc = -1;
+ goto done;
+ }
+ ch_free( e_new->e_name.bv_val );
+ ch_free( e_new->e_nname.bv_val );
+ e_new->e_name = e_name;
+ e_new->e_nname = e_nname;
+
+ e_new->e_private = ( void * )mp;
+ if ( mss != NULL ) {
+ mp->mp_info = mss;
+ mp->mp_flags = flags;
+
+ } else {
+ mp->mp_info = mp_parent->mp_info;
+ mp->mp_flags = mp_parent->mp_flags | MONITOR_F_SUB;
+ }
+ mp->mp_cb = cb;
+
+ ep = &mp_parent->mp_children;
+ for ( ; *ep; ) {
+ mp_parent = ( monitor_entry_t * )(*ep)->e_private;
+ ep = &mp_parent->mp_next;
+ }
+ *ep = e_new;
+
+ if ( monitor_cache_add( mi, e_new ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry(\"%s\"): "
+ "unable to add entry\n",
+ e->e_name.bv_val );
+ rc = -1;
+ goto done;
+ }
+
+done:;
+ if ( !BER_BVISNULL( &ndn ) ) {
+ ch_free( ndn.bv_val );
+ }
+
+ if ( rc ) {
+ if ( mp ) {
+ ch_free( mp );
+ }
+ if ( e_new ) {
+ e_new->e_private = NULL;
+ entry_free( e_new );
+ }
+ }
+
+ if ( e_parent ) {
+ monitor_cache_release( mi, e_parent );
+ }
+
+ } else {
+ entry_limbo_t **elpp = NULL, el = { 0 };
+
+ el.el_type = LIMBO_ENTRY_PARENT;
+
+ el.el_e = entry_dup( e );
+ if ( el.el_e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry(\"%s\"): "
+ "entry_dup() failed\n",
+ e->e_name.bv_val );
+ goto done_limbo;
+ }
+
+ if ( !BER_BVISNULL( nbase ) ) {
+ ber_dupbv( &el.el_nbase, nbase );
+ }
+
+ el.el_scope = scope;
+ if ( !BER_BVISNULL( filter ) ) {
+ ber_dupbv( &el.el_filter, filter );
+ }
+
+ el.el_cb = cb;
+ el.el_mss = mss;
+ el.el_flags = flags;
+
+ for ( elpp = &mi->mi_entry_limbo;
+ *elpp;
+ elpp = &(*elpp)->el_next )
+ /* go to last */;
+
+ *elpp = (entry_limbo_t *)ch_malloc( sizeof( entry_limbo_t ) );
+ if ( *elpp == NULL ) {
+ goto done_limbo;
+ }
+
+done_limbo:;
+ if ( *elpp != NULL ) {
+ el.el_next = NULL;
+ **elpp = el;
+
+ } else {
+ if ( !BER_BVISNULL( &el.el_filter ) ) {
+ ch_free( el.el_filter.bv_val );
+ }
+ if ( !BER_BVISNULL( &el.el_nbase ) ) {
+ ch_free( el.el_nbase.bv_val );
+ }
+ entry_free( el.el_e );
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+monitor_search2ndn_cb( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_SEARCH ) {
+ struct berval *ndn = op->o_callback->sc_private;
+
+ if ( !BER_BVISNULL( ndn ) ) {
+ rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
+ ch_free( ndn->bv_val );
+ BER_BVZERO( ndn );
+ return rs->sr_err;
+ }
+
+ ber_dupbv( ndn, &rs->sr_entry->e_nname );
+ }
+
+ return 0;
+}
+
+int
+monitor_search2ndn(
+ struct berval *nbase,
+ int scope,
+ struct berval *filter,
+ struct berval *ndn )
+{
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+ Operation *op;
+ void *thrctx;
+ SlapReply rs = { REP_RESULT };
+ slap_callback cb = { NULL, monitor_search2ndn_cb, NULL, NULL };
+ int rc;
+
+ BER_BVZERO( ndn );
+
+ if ( be_monitor == NULL ) {
+ return -1;
+ }
+
+ thrctx = ldap_pvt_thread_pool_context();
+ connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+ op = &opbuf.ob_op;
+
+ op->o_tag = LDAP_REQ_SEARCH;
+
+ /* use global malloc for now */
+ if ( op->o_tmpmemctx ) {
+ op->o_tmpmemctx = NULL;
+ }
+ op->o_tmpmfuncs = &ch_mfuncs;
+
+ op->o_bd = be_monitor;
+ if ( nbase == NULL || BER_BVISNULL( nbase ) ) {
+ ber_dupbv_x( &op->o_req_dn, &op->o_bd->be_suffix[ 0 ],
+ op->o_tmpmemctx );
+ ber_dupbv_x( &op->o_req_ndn, &op->o_bd->be_nsuffix[ 0 ],
+ op->o_tmpmemctx );
+
+ } else {
+ if ( dnPrettyNormal( NULL, nbase, &op->o_req_dn, &op->o_req_ndn,
+ op->o_tmpmemctx ) ) {
+ return -1;
+ }
+ }
+
+ op->o_callback = &cb;
+ cb.sc_private = (void *)ndn;
+
+ op->ors_scope = scope;
+ op->ors_filter = str2filter_x( op, filter->bv_val );
+ if ( op->ors_filter == NULL ) {
+ rc = LDAP_OTHER;
+ goto cleanup;
+ }
+ ber_dupbv_x( &op->ors_filterstr, filter, op->o_tmpmemctx );
+ op->ors_attrs = slap_anlist_no_attrs;
+ op->ors_attrsonly = 0;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_slimit = 1;
+ op->ors_limit = NULL;
+ op->ors_deref = LDAP_DEREF_NEVER;
+
+ op->o_nocaching = 1;
+ op->o_managedsait = SLAP_CONTROL_NONCRITICAL;
+
+ op->o_dn = be_monitor->be_rootdn;
+ op->o_ndn = be_monitor->be_rootndn;
+
+ rc = op->o_bd->be_search( op, &rs );
+
+cleanup:;
+ if ( op->ors_filter != NULL ) {
+ filter_free_x( op, op->ors_filter, 1 );
+ }
+ if ( !BER_BVISNULL( &op->ors_filterstr ) ) {
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ }
+ if ( !BER_BVISNULL( &op->o_req_dn ) ) {
+ op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx );
+ }
+ if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
+ op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( rc != 0 ) {
+ return rc;
+ }
+
+ switch ( rs.sr_err ) {
+ case LDAP_SUCCESS:
+ if ( BER_BVISNULL( ndn ) ) {
+ rc = -1;
+ }
+ break;
+
+ case LDAP_SIZELIMIT_EXCEEDED:
+ default:
+ if ( !BER_BVISNULL( ndn ) ) {
+ ber_memfree( ndn->bv_val );
+ BER_BVZERO( ndn );
+ }
+ rc = -1;
+ break;
+ }
+
+ return rc;
+}
+
+int
+monitor_back_register_entry_attrs(
+ struct berval *ndn_in,
+ Attribute *a,
+ monitor_callback_t *cb,
+ struct berval *nbase,
+ int scope,
+ struct berval *filter )
+{
+ monitor_info_t *mi;
+ struct berval ndn = BER_BVNULL;
+ char *fname = ( a == NULL ? "callback" : "attrs" );
+ struct berval empty_bv = BER_BVC("");
+
+ if ( nbase == NULL ) nbase = &empty_bv;
+ if ( filter == NULL ) filter = &empty_bv;
+
+ if ( be_monitor == NULL ) {
+ Debug(LDAP_DEBUG_ANY,
+ "monitor_back_register_entry_%s(base=\"%s\" scope=%s filter=\"%s\"): " "monitor database not configured.\n\n",
+ fname, BER_BVISNULL(nbase) ? "" : nbase->bv_val,
+ ldap_pvt_scope2str(scope),
+ BER_BVISNULL(filter) ? "" : filter->bv_val );
+
+ return -1;
+ }
+
+ mi = ( monitor_info_t * )be_monitor->be_private;
+
+ assert( mi != NULL );
+
+ if ( ndn_in != NULL ) {
+ ndn = *ndn_in;
+ }
+
+ if ( a == NULL && cb == NULL ) {
+ /* nothing to do */
+ return -1;
+ }
+
+ if ( ( ndn_in == NULL || BER_BVISNULL( &ndn ) )
+ && BER_BVISNULL( filter ) )
+ {
+ /* need a filter */
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry_%s(\"\"): "
+ "need a valid filter\n",
+ fname );
+ return -1;
+ }
+
+ if ( monitor_subsys_is_opened() ) {
+ Entry *e = NULL;
+ Attribute **atp = NULL;
+ monitor_entry_t *mp = NULL;
+ monitor_callback_t **mcp = NULL;
+ int rc = 0;
+ int freeit = 0;
+
+ if ( BER_BVISNULL( &ndn ) ) {
+ if ( monitor_search2ndn( nbase, scope, filter, &ndn ) ) {
+ Debug(LDAP_DEBUG_ANY,
+ "monitor_back_register_entry_%s(\"\"): " "base=\"%s\" scope=%s filter=\"%s\": " "unable to find entry\n\n",
+ fname,
+ nbase->bv_val ? nbase->bv_val : "\"\"",
+ ldap_pvt_scope2str(scope),
+ filter->bv_val );
+ return -1;
+ }
+
+ freeit = 1;
+ }
+
+ if ( monitor_cache_get( mi, &ndn, &e ) != 0 ) {
+ /* entry does not exist */
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry_%s(\"%s\"): "
+ "entry does not exist\n",
+ fname, ndn.bv_val );
+ rc = -1;
+ goto done;
+ }
+
+ assert( e->e_private != NULL );
+ mp = ( monitor_entry_t * )e->e_private;
+
+ if ( mp->mp_flags & MONITOR_F_VOLATILE ) {
+ /* entry is volatile; cannot append callback */
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry_%s(\"%s\"): "
+ "entry is volatile\n",
+ fname, e->e_name.bv_val );
+ rc = -1;
+ goto done;
+ }
+
+ if ( a ) {
+ for ( atp = &e->e_attrs; *atp; atp = &(*atp)->a_next )
+ /* just get to last */ ;
+
+ for ( ; a != NULL; a = a->a_next ) {
+ assert( a->a_desc != NULL );
+ assert( a->a_vals != NULL );
+
+ if ( attr_find( e->e_attrs, a->a_desc ) ) {
+ attr_merge( e, a->a_desc, a->a_vals,
+ a->a_nvals == a->a_vals ? NULL : a->a_nvals );
+
+ } else {
+ *atp = attr_dup( a );
+ if ( *atp == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_register_entry_%s(\"%s\"): "
+ "attr_dup() failed\n",
+ fname, e->e_name.bv_val );
+ rc = -1;
+ goto done;
+ }
+ atp = &(*atp)->a_next;
+ }
+ }
+ }
+
+ if ( cb ) {
+ for ( mcp = &mp->mp_cb; *mcp; mcp = &(*mcp)->mc_next )
+ /* go to tail */ ;
+
+ /* NOTE: we do not clear cb->mc_next, so this function
+ * can be used to append a list of callbacks */
+ (*mcp) = cb;
+ }
+
+done:;
+ if ( rc ) {
+ if ( atp && *atp ) {
+ attrs_free( *atp );
+ *atp = NULL;
+ }
+ }
+
+ if ( freeit ) {
+ ber_memfree( ndn.bv_val );
+ }
+
+ if ( e ) {
+ monitor_cache_release( mi, e );
+ }
+
+ } else {
+ entry_limbo_t **elpp, el = { 0 };
+
+ el.el_type = LIMBO_ATTRS;
+ el.el_ndn = ndn_in;
+ if ( !BER_BVISNULL( nbase ) ) {
+ ber_dupbv( &el.el_nbase, nbase);
+ }
+ el.el_scope = scope;
+ if ( !BER_BVISNULL( filter ) ) {
+ ber_dupbv( &el.el_filter, filter );
+ }
+
+ el.el_a = attrs_dup( a );
+ el.el_cb = cb;
+
+ for ( elpp = &mi->mi_entry_limbo;
+ *elpp;
+ elpp = &(*elpp)->el_next )
+ /* go to last */;
+
+ *elpp = (entry_limbo_t *)ch_malloc( sizeof( entry_limbo_t ) );
+ if ( *elpp == NULL ) {
+ if ( !BER_BVISNULL( &el.el_filter ) ) {
+ ch_free( el.el_filter.bv_val );
+ }
+ if ( el.el_a != NULL ) {
+ attrs_free( el.el_a );
+ }
+ if ( !BER_BVISNULL( &el.el_nbase ) ) {
+ ch_free( &el.el_nbase.bv_val );
+ }
+ return -1;
+ }
+
+ el.el_next = NULL;
+ **elpp = el;
+ }
+
+ return 0;
+}
+
+int
+monitor_back_register_entry_callback(
+ struct berval *ndn,
+ monitor_callback_t *cb,
+ struct berval *nbase,
+ int scope,
+ struct berval *filter )
+{
+ return monitor_back_register_entry_attrs( ndn, NULL, cb,
+ nbase, scope, filter );
+}
+
+/*
+ * TODO: add corresponding calls to remove installed callbacks, entries
+ * and so, in case the entity that installed them is removed (e.g. a
+ * database, via back-config)
+ */
+int
+monitor_back_unregister_entry(
+ struct berval *ndn )
+{
+ monitor_info_t *mi;
+
+ if ( be_monitor == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_unregister_entry(\"%s\"): "
+ "monitor database not configured.\n",
+ ndn->bv_val );
+
+ return -1;
+ }
+
+ /* entry will be regularly freed, and resources released
+ * according to callbacks */
+ if ( slapd_shutdown ) {
+ return 0;
+ }
+
+ mi = ( monitor_info_t * )be_monitor->be_private;
+
+ assert( mi != NULL );
+
+ if ( monitor_subsys_is_opened() ) {
+ Entry *e = NULL;
+ monitor_entry_t *mp = NULL;
+ monitor_callback_t *cb = NULL;
+
+ if ( monitor_cache_remove( mi, ndn, &e ) != 0 ) {
+ /* entry does not exist */
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_unregister_entry(\"%s\"): "
+ "entry removal failed.\n",
+ ndn->bv_val );
+ return -1;
+ }
+
+ mp = (monitor_entry_t *)e->e_private;
+ assert( mp != NULL );
+
+ for ( cb = mp->mp_cb; cb != NULL; ) {
+ monitor_callback_t *next = cb->mc_next;
+
+ if ( cb->mc_free ) {
+ (void)cb->mc_free( e, &cb->mc_private );
+ }
+ ch_free( cb );
+
+ cb = next;
+ }
+
+ ch_free( mp );
+ e->e_private = NULL;
+ entry_free( e );
+
+ } else {
+ entry_limbo_t **elpp;
+
+ for ( elpp = &mi->mi_entry_limbo;
+ *elpp;
+ elpp = &(*elpp)->el_next )
+ {
+ entry_limbo_t *elp = *elpp;
+
+ if ( elp->el_type == LIMBO_ENTRY
+ && dn_match( ndn, &elp->el_e->e_nname ) )
+ {
+ monitor_callback_t *cb, *next;
+
+ for ( cb = elp->el_cb; cb; cb = next ) {
+ /* FIXME: call callbacks? */
+ next = cb->mc_next;
+ if ( cb->mc_dispose ) {
+ cb->mc_dispose( &cb->mc_private );
+ }
+ ch_free( cb );
+ }
+ assert( elp->el_e != NULL );
+ elp->el_e->e_private = NULL;
+ entry_free( elp->el_e );
+ *elpp = elp->el_next;
+ ch_free( elp );
+ elpp = NULL;
+ break;
+ }
+ }
+
+ if ( elpp != NULL ) {
+ /* not found! where did it go? */
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int
+monitor_back_unregister_entry_parent(
+ struct berval *nrdn,
+ monitor_callback_t *target_cb,
+ struct berval *nbase,
+ int scope,
+ struct berval *filter )
+{
+ monitor_info_t *mi;
+ struct berval ndn = BER_BVNULL;
+
+ if ( be_monitor == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_unregister_entry_parent(base=\"%s\" scope=%s filter=\"%s\"): "
+ "monitor database not configured.\n",
+ BER_BVISNULL( nbase ) ? "" : nbase->bv_val,
+ ldap_pvt_scope2str( scope ),
+ BER_BVISNULL( filter ) ? "" : filter->bv_val );
+
+ return -1;
+ }
+
+ /* entry will be regularly freed, and resources released
+ * according to callbacks */
+ if ( slapd_shutdown ) {
+ return 0;
+ }
+
+ mi = ( monitor_info_t * )be_monitor->be_private;
+
+ assert( mi != NULL );
+
+ if ( ( nrdn == NULL || BER_BVISNULL( nrdn ) )
+ && BER_BVISNULL( filter ) )
+ {
+ /* need a filter */
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_unregister_entry_parent(\"\"): "
+ "need a valid filter\n" );
+ return -1;
+ }
+
+ if ( monitor_subsys_is_opened() ) {
+ Entry *e = NULL;
+ monitor_entry_t *mp = NULL;
+
+ if ( monitor_search2ndn( nbase, scope, filter, &ndn ) ) {
+ /* entry does not exist */
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_unregister_entry_parent(\"\"): "
+ "base=\"%s\" scope=%s filter=\"%s\": "
+ "unable to find entry\n",
+ nbase->bv_val ? nbase->bv_val : "\"\"",
+ ldap_pvt_scope2str( scope ),
+ filter->bv_val );
+ return -1;
+ }
+
+ if ( monitor_cache_remove( mi, &ndn, &e ) != 0 ) {
+ /* entry does not exist */
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_unregister_entry(\"%s\"): "
+ "entry removal failed.\n",
+ ndn.bv_val );
+ ber_memfree( ndn.bv_val );
+ return -1;
+ }
+ ber_memfree( ndn.bv_val );
+
+ mp = (monitor_entry_t *)e->e_private;
+ assert( mp != NULL );
+
+ if ( target_cb != NULL ) {
+ monitor_callback_t **cbp;
+
+ for ( cbp = &mp->mp_cb; *cbp != NULL; cbp = &(*cbp)->mc_next ) {
+ if ( *cbp == target_cb ) {
+ if ( (*cbp)->mc_free ) {
+ (void)(*cbp)->mc_free( e, &(*cbp)->mc_private );
+ }
+ *cbp = (*cbp)->mc_next;
+ ch_free( target_cb );
+ break;
+ }
+ }
+ }
+
+
+ ch_free( mp );
+ e->e_private = NULL;
+ entry_free( e );
+
+ } else {
+ entry_limbo_t **elpp;
+
+ for ( elpp = &mi->mi_entry_limbo;
+ *elpp;
+ elpp = &(*elpp)->el_next )
+ {
+ entry_limbo_t *elp = *elpp;
+
+ if ( elp->el_type == LIMBO_ENTRY_PARENT
+ && dn_match( nrdn, &elp->el_e->e_nname )
+ && dn_match( nbase, &elp->el_nbase )
+ && scope == elp->el_scope
+ && bvmatch( filter, &elp->el_filter ) )
+ {
+ monitor_callback_t *cb, *next;
+
+ for ( cb = elp->el_cb; cb; cb = next ) {
+ /* FIXME: call callbacks? */
+ next = cb->mc_next;
+ if ( cb->mc_dispose ) {
+ cb->mc_dispose( &cb->mc_private );
+ }
+ ch_free( cb );
+ }
+ assert( elp->el_e != NULL );
+ elp->el_e->e_private = NULL;
+ entry_free( elp->el_e );
+ if ( !BER_BVISNULL( &elp->el_nbase ) ) {
+ ch_free( elp->el_nbase.bv_val );
+ }
+ if ( !BER_BVISNULL( &elp->el_filter ) ) {
+ ch_free( elp->el_filter.bv_val );
+ }
+ *elpp = elp->el_next;
+ ch_free( elp );
+ elpp = NULL;
+ break;
+ }
+ }
+
+ if ( elpp != NULL ) {
+ /* not found! where did it go? */
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int
+monitor_back_unregister_entry_attrs(
+ struct berval *ndn_in,
+ Attribute *target_a,
+ monitor_callback_t *target_cb,
+ struct berval *nbase,
+ int scope,
+ struct berval *filter )
+{
+ monitor_info_t *mi;
+ struct berval ndn = BER_BVNULL;
+ char *fname = ( target_a == NULL ? "callback" : "attrs" );
+
+ if ( be_monitor == NULL ) {
+ Debug(LDAP_DEBUG_ANY,
+ "monitor_back_unregister_entry_%s(base=\"%s\" scope=%s filter=\"%s\"): " "monitor database not configured.\n\n",
+ fname, BER_BVISNULL(nbase) ? "" : nbase->bv_val,
+ ldap_pvt_scope2str(scope),
+ BER_BVISNULL(filter) ? "" : filter->bv_val );
+
+ return -1;
+ }
+
+ /* entry will be regularly freed, and resources released
+ * according to callbacks */
+ if ( slapd_shutdown ) {
+ return 0;
+ }
+
+ mi = ( monitor_info_t * )be_monitor->be_private;
+
+ assert( mi != NULL );
+
+ if ( ndn_in != NULL ) {
+ ndn = *ndn_in;
+ }
+
+ if ( target_a == NULL && target_cb == NULL ) {
+ /* nothing to do */
+ return -1;
+ }
+
+ if ( ( ndn_in == NULL || BER_BVISNULL( &ndn ) )
+ && BER_BVISNULL( filter ) )
+ {
+ /* need a filter */
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_unregister_entry_%s(\"\"): "
+ "need a valid filter\n",
+ fname );
+ return -1;
+ }
+
+ if ( monitor_subsys_is_opened() ) {
+ Entry *e = NULL;
+ monitor_entry_t *mp = NULL;
+ int freeit = 0;
+
+ if ( BER_BVISNULL( &ndn ) ) {
+ if ( monitor_search2ndn( nbase, scope, filter, &ndn ) ) {
+ Debug(LDAP_DEBUG_ANY,
+ "monitor_back_unregister_entry_%s(\"\"): " "base=\"%s\" scope=%d filter=\"%s\": " "unable to find entry\n\n",
+ fname,
+ nbase->bv_val ? nbase->bv_val : "\"\"",
+ scope, filter->bv_val );
+ return -1;
+ }
+
+ freeit = 1;
+ }
+
+ if ( monitor_cache_get( mi, &ndn, &e ) != 0 ) {
+ /* entry does not exist */
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_unregister_entry(\"%s\"): "
+ "entry removal failed.\n",
+ ndn.bv_val );
+ return -1;
+ }
+
+ mp = (monitor_entry_t *)e->e_private;
+ assert( mp != NULL );
+
+ if ( target_cb != NULL ) {
+ monitor_callback_t **cbp;
+
+ for ( cbp = &mp->mp_cb; *cbp != NULL; cbp = &(*cbp)->mc_next ) {
+ if ( *cbp == target_cb ) {
+ if ( (*cbp)->mc_free ) {
+ (void)(*cbp)->mc_free( e, &(*cbp)->mc_private );
+ }
+ *cbp = (*cbp)->mc_next;
+ ch_free( target_cb );
+ break;
+ }
+ }
+ }
+
+ if ( target_a != NULL ) {
+ Attribute *a;
+
+ for ( a = target_a; a != NULL; a = a->a_next ) {
+ Modification mod = { 0 };
+ const char *text;
+ char textbuf[ SLAP_TEXT_BUFLEN ];
+
+ mod.sm_op = LDAP_MOD_DELETE;
+ mod.sm_desc = a->a_desc;
+ mod.sm_values = a->a_vals;
+ mod.sm_nvalues = a->a_nvals;
+
+ (void)modify_delete_values( e, &mod, 1,
+ &text, textbuf, sizeof( textbuf ) );
+ }
+ }
+
+ if ( freeit ) {
+ ber_memfree( ndn.bv_val );
+ }
+
+ monitor_cache_release( mi, e );
+
+ } else {
+ entry_limbo_t **elpp;
+
+ for ( elpp = &mi->mi_entry_limbo;
+ *elpp;
+ elpp = &(*elpp)->el_next )
+ {
+ entry_limbo_t *elp = *elpp;
+
+ if ( elp->el_type == LIMBO_ATTRS
+ && dn_match( nbase, &elp->el_nbase )
+ && scope == elp->el_scope
+ && bvmatch( filter, &elp->el_filter ) )
+ {
+ monitor_callback_t *cb, *next;
+
+ for ( cb = elp->el_cb; cb; cb = next ) {
+ /* FIXME: call callbacks? */
+ next = cb->mc_next;
+ if ( cb->mc_dispose ) {
+ cb->mc_dispose( &cb->mc_private );
+ }
+ ch_free( cb );
+ }
+ assert( elp->el_e == NULL );
+ if ( elp->el_a != NULL ) {
+ attrs_free( elp->el_a );
+ }
+ if ( !BER_BVISNULL( &elp->el_nbase ) ) {
+ ch_free( elp->el_nbase.bv_val );
+ }
+ if ( !BER_BVISNULL( &elp->el_filter ) ) {
+ ch_free( elp->el_filter.bv_val );
+ }
+ *elpp = elp->el_next;
+ ch_free( elp );
+ elpp = NULL;
+ break;
+ }
+ }
+
+ if ( elpp != NULL ) {
+ /* not found! where did it go? */
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int
+monitor_back_unregister_entry_callback(
+ struct berval *ndn,
+ monitor_callback_t *cb,
+ struct berval *nbase,
+ int scope,
+ struct berval *filter )
+{
+ /* TODO: lookup entry (by ndn, if not NULL, and/or by callback);
+ * unregister the callback; if a is not null, unregister the
+ * given attrs. In any case, call cb->cb_free */
+ return monitor_back_unregister_entry_attrs( ndn,
+ NULL, cb, nbase, scope, filter );
+}
+
+monitor_subsys_t *
+monitor_back_get_subsys( const char *name )
+{
+ if ( monitor_subsys != NULL ) {
+ int i;
+
+ for ( i = 0; monitor_subsys[ i ] != NULL; i++ ) {
+ if ( strcasecmp( monitor_subsys[ i ]->mss_name, name ) == 0 ) {
+ return monitor_subsys[ i ];
+ }
+ }
+ }
+
+ return NULL;
+}
+
+monitor_subsys_t *
+monitor_back_get_subsys_by_dn(
+ struct berval *ndn,
+ int sub )
+{
+ if ( monitor_subsys != NULL ) {
+ int i;
+
+ if ( sub ) {
+ for ( i = 0; monitor_subsys[ i ] != NULL; i++ ) {
+ if ( dnIsSuffix( ndn, &monitor_subsys[ i ]->mss_ndn ) ) {
+ return monitor_subsys[ i ];
+ }
+ }
+
+ } else {
+ for ( i = 0; monitor_subsys[ i ] != NULL; i++ ) {
+ if ( dn_match( ndn, &monitor_subsys[ i ]->mss_ndn ) ) {
+ return monitor_subsys[ i ];
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+int
+monitor_back_initialize(
+ BackendInfo *bi )
+{
+ static char *controls[] = {
+ LDAP_CONTROL_MANAGEDSAIT,
+ NULL
+ };
+
+ static ConfigTable monitorcfg[] = {
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED,
+ NULL, NULL, NULL, NULL }
+ };
+
+ static ConfigOCs monitorocs[] = {
+ { "( OLcfgDbOc:4.1 "
+ "NAME 'olcMonitorConfig' "
+ "DESC 'Monitor backend configuration' "
+ "SUP olcDatabaseConfig "
+ ")",
+ Cft_Database, monitorcfg },
+ { NULL, 0, NULL }
+ };
+
+ struct m_s {
+ char *schema;
+ slap_mask_t flags;
+ int offset;
+ } moc[] = {
+ { "( 1.3.6.1.4.1.4203.666.3.16.1 "
+ "NAME 'monitor' "
+ "DESC 'OpenLDAP system monitoring' "
+ "SUP top STRUCTURAL "
+ "MUST cn "
+ "MAY ( "
+ "description "
+ "$ seeAlso "
+ "$ labeledURI "
+ "$ monitoredInfo "
+ "$ managedInfo "
+ "$ monitorOverlay "
+ ") )", SLAP_OC_OPERATIONAL|SLAP_OC_HIDE,
+ offsetof(monitor_info_t, mi_oc_monitor) },
+ { "( 1.3.6.1.4.1.4203.666.3.16.2 "
+ "NAME 'monitorServer' "
+ "DESC 'Server monitoring root entry' "
+ "SUP monitor STRUCTURAL )", SLAP_OC_OPERATIONAL|SLAP_OC_HIDE,
+ offsetof(monitor_info_t, mi_oc_monitorServer) },
+ { "( 1.3.6.1.4.1.4203.666.3.16.3 "
+ "NAME 'monitorContainer' "
+ "DESC 'monitor container class' "
+ "SUP monitor STRUCTURAL )", SLAP_OC_OPERATIONAL|SLAP_OC_HIDE,
+ offsetof(monitor_info_t, mi_oc_monitorContainer) },
+ { "( 1.3.6.1.4.1.4203.666.3.16.4 "
+ "NAME 'monitorCounterObject' "
+ "DESC 'monitor counter class' "
+ "SUP monitor STRUCTURAL )", SLAP_OC_OPERATIONAL|SLAP_OC_HIDE,
+ offsetof(monitor_info_t, mi_oc_monitorCounterObject) },
+ { "( 1.3.6.1.4.1.4203.666.3.16.5 "
+ "NAME 'monitorOperation' "
+ "DESC 'monitor operation class' "
+ "SUP monitor STRUCTURAL )", SLAP_OC_OPERATIONAL|SLAP_OC_HIDE,
+ offsetof(monitor_info_t, mi_oc_monitorOperation) },
+ { "( 1.3.6.1.4.1.4203.666.3.16.6 "
+ "NAME 'monitorConnection' "
+ "DESC 'monitor connection class' "
+ "SUP monitor STRUCTURAL )", SLAP_OC_OPERATIONAL|SLAP_OC_HIDE,
+ offsetof(monitor_info_t, mi_oc_monitorConnection) },
+ { "( 1.3.6.1.4.1.4203.666.3.16.7 "
+ "NAME 'managedObject' "
+ "DESC 'monitor managed entity class' "
+ "SUP monitor STRUCTURAL )", SLAP_OC_OPERATIONAL|SLAP_OC_HIDE,
+ offsetof(monitor_info_t, mi_oc_managedObject) },
+ { "( 1.3.6.1.4.1.4203.666.3.16.8 "
+ "NAME 'monitoredObject' "
+ "DESC 'monitor monitored entity class' "
+ "SUP monitor STRUCTURAL )", SLAP_OC_OPERATIONAL|SLAP_OC_HIDE,
+ offsetof(monitor_info_t, mi_oc_monitoredObject) },
+ { NULL, 0, -1 }
+ }, mat[] = {
+ { "( 1.3.6.1.4.1.4203.666.1.55.1 "
+ "NAME 'monitoredInfo' "
+ "DESC 'monitored info' "
+ /* "SUP name " */
+ "EQUALITY caseIgnoreMatch "
+ "SUBSTR caseIgnoreSubstringsMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitoredInfo) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.2 "
+ "NAME 'managedInfo' "
+ "DESC 'monitor managed info' "
+ "SUP name )", SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_managedInfo) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.3 "
+ "NAME 'monitorCounter' "
+ "DESC 'monitor counter' "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorCounter) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.4 "
+ "NAME 'monitorOpCompleted' "
+ "DESC 'monitor completed operations' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorOpCompleted) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.5 "
+ "NAME 'monitorOpInitiated' "
+ "DESC 'monitor initiated operations' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorOpInitiated) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.6 "
+ "NAME 'monitorConnectionNumber' "
+ "DESC 'monitor connection number' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorConnectionNumber) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.7 "
+ "NAME 'monitorConnectionAuthzDN' "
+ "DESC 'monitor connection authorization DN' "
+ /* "SUP distinguishedName " */
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorConnectionAuthzDN) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.8 "
+ "NAME 'monitorConnectionLocalAddress' "
+ "DESC 'monitor connection local address' "
+ "SUP monitoredInfo "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorConnectionLocalAddress) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.9 "
+ "NAME 'monitorConnectionPeerAddress' "
+ "DESC 'monitor connection peer address' "
+ "SUP monitoredInfo "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorConnectionPeerAddress) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.10 "
+ "NAME 'monitorTimestamp' "
+ "DESC 'monitor timestamp' "
+ "EQUALITY generalizedTimeMatch "
+ "ORDERING generalizedTimeOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorTimestamp) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.11 "
+ "NAME 'monitorOverlay' "
+ "DESC 'name of overlays defined for a given database' "
+ "SUP monitoredInfo "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorOverlay) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.12 "
+ "NAME 'readOnly' "
+ "DESC 'read/write status of a given database' "
+ "EQUALITY booleanMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
+ "SINGLE-VALUE "
+ "USAGE dSAOperation )", SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_readOnly) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.13 "
+ "NAME 'restrictedOperation' "
+ "DESC 'name of restricted operation for a given database' "
+ "SUP managedInfo )", SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_restrictedOperation ) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.14 "
+ "NAME 'monitorConnectionProtocol' "
+ "DESC 'monitor connection protocol' "
+ "SUP monitoredInfo "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorConnectionProtocol) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.15 "
+ "NAME 'monitorConnectionOpsReceived' "
+ "DESC 'monitor number of operations received by the connection' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorConnectionOpsReceived) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.16 "
+ "NAME 'monitorConnectionOpsExecuting' "
+ "DESC 'monitor number of operations in execution within the connection' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorConnectionOpsExecuting) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.17 "
+ "NAME 'monitorConnectionOpsPending' "
+ "DESC 'monitor number of pending operations within the connection' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorConnectionOpsPending) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.18 "
+ "NAME 'monitorConnectionOpsCompleted' "
+ "DESC 'monitor number of operations completed within the connection' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorConnectionOpsCompleted) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.19 "
+ "NAME 'monitorConnectionGet' "
+ "DESC 'number of times connection_get() was called so far' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorConnectionGet) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.20 "
+ "NAME 'monitorConnectionRead' "
+ "DESC 'number of times connection_read() was called so far' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorConnectionRead) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.21 "
+ "NAME 'monitorConnectionWrite' "
+ "DESC 'number of times connection_write() was called so far' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorConnectionWrite) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.22 "
+ "NAME 'monitorConnectionMask' "
+ "DESC 'monitor connection mask' "
+ "SUP monitoredInfo "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorConnectionMask) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.23 "
+ "NAME 'monitorConnectionListener' "
+ "DESC 'monitor connection listener' "
+ "SUP monitoredInfo "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorConnectionListener) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.24 "
+ "NAME 'monitorConnectionPeerDomain' "
+ "DESC 'monitor connection peer domain' "
+ "SUP monitoredInfo "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorConnectionPeerDomain) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.25 "
+ "NAME 'monitorConnectionStartTime' "
+ "DESC 'monitor connection start time' "
+ "SUP monitorTimestamp "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorConnectionStartTime) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.26 "
+ "NAME 'monitorConnectionActivityTime' "
+ "DESC 'monitor connection activity time' "
+ "SUP monitorTimestamp "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorConnectionActivityTime) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.27 "
+ "NAME 'monitorIsShadow' "
+ "DESC 'TRUE if the database is shadow' "
+ "EQUALITY booleanMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
+ "SINGLE-VALUE "
+ "USAGE dSAOperation )", SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorIsShadow) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.28 "
+ "NAME 'monitorUpdateRef' "
+ "DESC 'update referral for shadow databases' "
+ "SUP monitoredInfo "
+ "SINGLE-VALUE "
+ "USAGE dSAOperation )", SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorUpdateRef) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.29 "
+ "NAME 'monitorRuntimeConfig' "
+ "DESC 'TRUE if component allows runtime configuration' "
+ "EQUALITY booleanMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
+ "SINGLE-VALUE "
+ "USAGE dSAOperation )", SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorRuntimeConfig) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.30 "
+ "NAME 'monitorSuperiorDN' "
+ "DESC 'monitor superior DN' "
+ /* "SUP distinguishedName " */
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorSuperiorDN) },
+ { "( 1.3.6.1.4.1.4203.666.1.55.31 "
+ "NAME 'monitorConnectionOpsAsync' "
+ "DESC 'monitor number of asynchronous operations in execution within the connection' "
+ "SUP monitorCounter "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", SLAP_AT_FINAL|SLAP_AT_HIDE,
+ offsetof(monitor_info_t, mi_ad_monitorConnectionOpsAsync) },
+ { NULL, 0, -1 }
+ };
+
+ static struct {
+ char *name;
+ char *oid;
+ } s_oid[] = {
+ { "olmAttributes", "1.3.6.1.4.1.4203.666.1.55" },
+ { "olmSubSystemAttributes", "olmAttributes:0" },
+ { "olmGenericAttributes", "olmSubSystemAttributes:0" },
+ { "olmDatabaseAttributes", "olmSubSystemAttributes:1" },
+ { "olmOverlayAttributes", "olmSubSystemAttributes:2" },
+ { "olmModuleAttributes", "olmSubSystemAttributes:3" },
+
+ /* for example, back-mdb specific attrs
+ * are in "olmDatabaseAttributes:12"
+ *
+ * NOTE: developers, please record here OID assignments
+ * for other modules */
+
+ { "olmObjectClasses", "1.3.6.1.4.1.4203.666.3.16" },
+ { "olmSubSystemObjectClasses", "olmObjectClasses:0" },
+ { "olmGenericObjectClasses", "olmSubSystemObjectClasses:0" },
+ { "olmDatabaseObjectClasses", "olmSubSystemObjectClasses:1" },
+ { "olmOverlayObjectClasses", "olmSubSystemObjectClasses:2" },
+ { "olmModuleObjectClasses", "olmSubSystemObjectClasses:3" },
+
+ /* for example, back-mdb specific objectClasses
+ * are in "olmDatabaseObjectClasses:12"
+ *
+ * NOTE: developers, please record here OID assignments
+ * for other modules */
+
+ { NULL }
+ };
+
+ int i, rc;
+ monitor_info_t *mi = &monitor_info;
+ ConfigArgs c;
+ char *argv[ 3 ];
+
+ argv[ 0 ] = "monitor";
+ c.argv = argv;
+ c.argc = 3;
+ c.fname = argv[0];
+
+ for ( i = 0; s_oid[ i ].name; i++ ) {
+ argv[ 1 ] = s_oid[ i ].name;
+ argv[ 2 ] = s_oid[ i ].oid;
+
+ if ( parse_oidm( &c, 0, NULL ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_initialize: unable to add "
+ "objectIdentifier \"%s=%s\"\n",
+ s_oid[ i ].name, s_oid[ i ].oid );
+ return 1;
+ }
+ }
+
+ /* schema integration */
+ for ( i = 0; mat[ i ].schema; i++ ) {
+ int code;
+ AttributeDescription **ad =
+ ((AttributeDescription **)&(((char *)mi)[ mat[ i ].offset ]));
+
+ *ad = NULL;
+ code = register_at( mat[ i ].schema, ad, 0 );
+
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_db_init: register_at failed\n" );
+ return -1;
+ }
+ (*ad)->ad_type->sat_flags |= mat[ i ].flags;
+ }
+
+ for ( i = 0; moc[ i ].schema; i++ ) {
+ int code;
+ ObjectClass **Oc =
+ ((ObjectClass **)&(((char *)mi)[ moc[ i ].offset ]));
+
+ code = register_oc( moc[ i ].schema, Oc, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_back_db_init: register_oc failed\n" );
+ return -1;
+ }
+ (*Oc)->soc_flags |= moc[ i ].flags;
+ }
+
+ bi->bi_controls = controls;
+
+ bi->bi_init = 0;
+ bi->bi_open = 0;
+ bi->bi_config = monitor_back_config;
+ bi->bi_close = 0;
+ bi->bi_destroy = 0;
+
+ bi->bi_db_init = monitor_back_db_init;
+#if 0
+ bi->bi_db_config = monitor_back_db_config;
+#endif
+ bi->bi_db_open = monitor_back_db_open;
+ bi->bi_db_close = 0;
+ bi->bi_db_destroy = monitor_back_db_destroy;
+
+ bi->bi_op_bind = monitor_back_bind;
+ bi->bi_op_unbind = 0;
+ bi->bi_op_search = monitor_back_search;
+ bi->bi_op_compare = monitor_back_compare;
+ bi->bi_op_modify = monitor_back_modify;
+ bi->bi_op_modrdn = 0;
+ bi->bi_op_add = 0;
+ bi->bi_op_delete = 0;
+ bi->bi_op_abandon = 0;
+
+ bi->bi_extended = 0;
+
+ bi->bi_entry_release_rw = monitor_back_release;
+ bi->bi_chk_referrals = 0;
+ bi->bi_operational = monitor_back_operational;
+
+ /*
+ * hooks for slap tools
+ */
+ bi->bi_tool_entry_open = 0;
+ bi->bi_tool_entry_close = 0;
+ bi->bi_tool_entry_first = 0;
+ bi->bi_tool_entry_first_x = 0;
+ bi->bi_tool_entry_next = 0;
+ bi->bi_tool_entry_get = 0;
+ bi->bi_tool_entry_put = 0;
+ bi->bi_tool_entry_reindex = 0;
+ bi->bi_tool_sync = 0;
+ bi->bi_tool_dn2id_get = 0;
+ bi->bi_tool_entry_modify = 0;
+
+ bi->bi_connection_init = 0;
+ bi->bi_connection_destroy = 0;
+
+ bi->bi_extra = (void *)&monitor_extra;
+
+ /*
+ * configuration objectClasses (fake)
+ */
+ bi->bi_cf_ocs = monitorocs;
+
+ rc = config_register_schema( monitorcfg, monitorocs );
+ if ( rc ) {
+ return rc;
+ }
+
+ return 0;
+}
+
+int
+monitor_back_db_init(
+ BackendDB *be,
+ ConfigReply *c)
+{
+ int rc;
+ struct berval dn = BER_BVC( SLAPD_MONITOR_DN ),
+ pdn,
+ ndn;
+ BackendDB *be2;
+
+ monitor_subsys_t *ms;
+
+ /*
+ * database monitor can be defined once only
+ */
+ if ( be_monitor != NULL ) {
+ if (c) {
+ snprintf(c->msg, sizeof(c->msg),"only one monitor database allowed");
+ }
+ return( -1 );
+ }
+ be_monitor = be;
+
+ /*
+ * register subsys
+ */
+ for ( ms = known_monitor_subsys; ms->mss_name != NULL; ms++ ) {
+ if ( monitor_back_register_subsys( ms ) ) {
+ return -1;
+ }
+ }
+
+ /* indicate system schema supported */
+ SLAP_BFLAGS(be) |= SLAP_BFLAG_MONITOR;
+
+ rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, NULL );
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "unable to normalize/pretty monitor DN \"%s\" (%d)\n",
+ dn.bv_val, rc );
+ return -1;
+ }
+
+ ber_bvarray_add( &be->be_suffix, &pdn );
+ ber_bvarray_add( &be->be_nsuffix, &ndn );
+
+ /* NOTE: only one monitor database is allowed,
+ * so we use static storage */
+ ldap_pvt_thread_mutex_init( &monitor_info.mi_cache_mutex );
+
+ be->be_private = &monitor_info;
+
+ be2 = select_backend( &ndn, 0 );
+ if ( be2 != be ) {
+ char *type = be2->bd_info->bi_type;
+
+ if ( overlay_is_over( be2 ) ) {
+ slap_overinfo *oi = (slap_overinfo *)be2->bd_info->bi_private;
+ type = oi->oi_orig->bi_type;
+ }
+
+ if (c) {
+ snprintf(c->msg, sizeof(c->msg),
+ "\"monitor\" database serving namingContext \"%s\" "
+ "is hidden by \"%s\" database serving namingContext \"%s\".\n",
+ pdn.bv_val, type, be2->be_nsuffix[ 0 ].bv_val );
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+monitor_back_destroy_limbo_entry(
+ entry_limbo_t *el,
+ int dispose )
+{
+ if ( el->el_e ) {
+ entry_free( el->el_e );
+ }
+ if ( el->el_a ) {
+ attrs_free( el->el_a );
+ }
+ if ( !BER_BVISNULL( &el->el_nbase ) ) {
+ ber_memfree( el->el_nbase.bv_val );
+ }
+ if ( !BER_BVISNULL( &el->el_filter ) ) {
+ ber_memfree( el->el_filter.bv_val );
+ }
+
+ /* NOTE: callbacks are not copied; so only free them
+ * if disposing of */
+ if ( el->el_cb && dispose != 0 ) {
+ monitor_callback_t *next;
+
+ for ( ; el->el_cb; el->el_cb = next ) {
+ next = el->el_cb->mc_next;
+ if ( el->el_cb->mc_dispose ) {
+ el->el_cb->mc_dispose( &el->el_cb->mc_private );
+ }
+ ch_free( el->el_cb );
+ }
+ }
+
+ ch_free( el );
+}
+
+int
+monitor_back_db_open(
+ BackendDB *be,
+ ConfigReply *cr)
+{
+ monitor_info_t *mi = (monitor_info_t *)be->be_private;
+ struct monitor_subsys_t **ms;
+ Entry *e, **ep, *root;
+ monitor_entry_t *mp;
+ int i;
+ struct berval bv, rdn = BER_BVC(SLAPD_MONITOR_DN);
+ struct tm tms;
+ static char tmbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+ struct berval desc[] = {
+ BER_BVC("This subtree contains monitoring/managing objects."),
+ BER_BVC("This object contains information about this server."),
+ BER_BVC("Most of the information is held in operational"
+ " attributes, which must be explicitly requested."),
+ BER_BVNULL };
+
+ int retcode = 0;
+
+ assert( be_monitor != NULL );
+ if ( be != be_monitor ) {
+ be_monitor = be;
+ }
+
+ /*
+ * Start
+ */
+ ldap_pvt_gmtime( &starttime, &tms );
+ lutil_gentime( tmbuf, sizeof(tmbuf), &tms );
+
+ mi->mi_startTime.bv_val = tmbuf;
+ mi->mi_startTime.bv_len = strlen( tmbuf );
+
+ if ( BER_BVISEMPTY( &be->be_rootdn ) ) {
+ BER_BVSTR( &mi->mi_creatorsName, SLAPD_ANONYMOUS );
+ BER_BVSTR( &mi->mi_ncreatorsName, SLAPD_ANONYMOUS );
+ } else {
+ mi->mi_creatorsName = be->be_rootdn;
+ mi->mi_ncreatorsName = be->be_rootndn;
+ }
+
+ /*
+ * creates the "cn=Monitor" entry
+ */
+ e = monitor_entry_stub( NULL, NULL, &rdn, mi->mi_oc_monitorServer,
+ NULL, NULL );
+
+ if ( e == NULL) {
+ Debug( LDAP_DEBUG_ANY,
+ "unable to create \"%s\" entry\n",
+ SLAPD_MONITOR_DN );
+ return( -1 );
+ }
+
+ attr_merge_normalize( e, slap_schema.si_ad_description, desc, NULL );
+
+ bv.bv_val = strchr( (char *) Versionstr, '$' );
+ if ( bv.bv_val != NULL ) {
+ char *end;
+
+ bv.bv_val++;
+ for ( ; bv.bv_val[ 0 ] == ' '; bv.bv_val++ )
+ ;
+
+ end = strchr( bv.bv_val, '$' );
+ if ( end != NULL ) {
+ end--;
+
+ for ( ; end > bv.bv_val && end[ 0 ] == ' '; end-- )
+ ;
+
+ end++;
+
+ bv.bv_len = end - bv.bv_val;
+
+ } else {
+ bv.bv_len = strlen( bv.bv_val );
+ }
+
+ if ( attr_merge_normalize_one( e, mi->mi_ad_monitoredInfo,
+ &bv, NULL ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "unable to add monitoredInfo to \"%s\" entry\n",
+ SLAPD_MONITOR_DN );
+ return( -1 );
+ }
+ }
+
+ mp = monitor_entrypriv_create();
+ if ( mp == NULL ) {
+ return -1;
+ }
+ e->e_private = ( void * )mp;
+ ep = &mp->mp_children;
+
+ if ( monitor_cache_add( mi, e ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "unable to add entry \"%s\" to cache\n",
+ SLAPD_MONITOR_DN );
+ return -1;
+ }
+ root = e;
+
+ /*
+ * Create all the subsystem specific entries
+ */
+ for ( i = 0; monitor_subsys[ i ] != NULL; i++ ) {
+ int len = strlen( monitor_subsys[ i ]->mss_name );
+ struct berval dn;
+ int rc;
+
+ dn.bv_len = len + sizeof( "cn=" ) - 1;
+ dn.bv_val = ch_calloc( sizeof( char ), dn.bv_len + 1 );
+ strcpy( dn.bv_val, "cn=" );
+ strcat( dn.bv_val, monitor_subsys[ i ]->mss_name );
+ rc = dnPretty( NULL, &dn, &monitor_subsys[ i ]->mss_rdn, NULL );
+ free( dn.bv_val );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor RDN \"%s\" is invalid\n",
+ dn.bv_val );
+ return( -1 );
+ }
+
+ e = monitor_entry_stub( &root->e_name, &root->e_nname,
+ &monitor_subsys[ i ]->mss_rdn, mi->mi_oc_monitorContainer,
+ NULL, NULL );
+
+ if ( e == NULL) {
+ Debug( LDAP_DEBUG_ANY,
+ "unable to create \"%s\" entry\n",
+ monitor_subsys[ i ]->mss_dn.bv_val );
+ return( -1 );
+ }
+ monitor_subsys[i]->mss_dn = e->e_name;
+ monitor_subsys[i]->mss_ndn = e->e_nname;
+
+ if ( !BER_BVISNULL( &monitor_subsys[ i ]->mss_desc[ 0 ] ) ) {
+ attr_merge_normalize( e, slap_schema.si_ad_description,
+ monitor_subsys[ i ]->mss_desc, NULL );
+ }
+
+ mp = monitor_entrypriv_create();
+ if ( mp == NULL ) {
+ return -1;
+ }
+ e->e_private = ( void * )mp;
+ mp->mp_info = monitor_subsys[ i ];
+ mp->mp_flags = monitor_subsys[ i ]->mss_flags;
+
+ if ( monitor_cache_add( mi, e ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "unable to add entry \"%s\" to cache\n",
+ monitor_subsys[ i ]->mss_dn.bv_val );
+ return -1;
+ }
+
+ *ep = e;
+ ep = &mp->mp_next;
+ }
+
+ assert( be != NULL );
+
+ be->be_private = mi;
+
+ /*
+ * opens the monitor backend subsystems
+ */
+ for ( ms = monitor_subsys; ms[ 0 ] != NULL; ms++ ) {
+ if ( ms[ 0 ]->mss_open && ms[ 0 ]->mss_open( be, ms[ 0 ] ) ) {
+ return( -1 );
+ }
+ ms[ 0 ]->mss_flags |= MONITOR_F_OPENED;
+ }
+
+ monitor_subsys_opened = 1;
+
+ if ( mi->mi_entry_limbo ) {
+ entry_limbo_t *el = mi->mi_entry_limbo;
+
+ for ( ; el; ) {
+ entry_limbo_t *tmp;
+ int rc;
+
+ switch ( el->el_type ) {
+ case LIMBO_ENTRY:
+ rc = monitor_back_register_entry(
+ el->el_e,
+ el->el_cb,
+ el->el_mss,
+ el->el_flags );
+ break;
+
+ case LIMBO_ENTRY_PARENT:
+ rc = monitor_back_register_entry_parent(
+ el->el_e,
+ el->el_cb,
+ el->el_mss,
+ el->el_flags,
+ &el->el_nbase,
+ el->el_scope,
+ &el->el_filter );
+ break;
+
+
+ case LIMBO_ATTRS:
+ rc = monitor_back_register_entry_attrs(
+ el->el_ndn,
+ el->el_a,
+ el->el_cb,
+ &el->el_nbase,
+ el->el_scope,
+ &el->el_filter );
+ break;
+
+ case LIMBO_CB:
+ rc = monitor_back_register_entry_callback(
+ el->el_ndn,
+ el->el_cb,
+ &el->el_nbase,
+ el->el_scope,
+ &el->el_filter );
+ break;
+
+ case LIMBO_BACKEND:
+ rc = monitor_back_register_backend( el->el_bi );
+ break;
+
+ case LIMBO_DATABASE:
+ rc = monitor_back_register_database( el->el_be, el->el_ndn );
+ break;
+
+ case LIMBO_OVERLAY_INFO:
+ rc = monitor_back_register_overlay_info( el->el_on );
+ break;
+
+ case LIMBO_OVERLAY:
+ rc = monitor_back_register_overlay( el->el_be, el->el_on, el->el_ndn );
+ break;
+
+ case LIMBO_SUBSYS:
+ rc = monitor_back_register_subsys( el->el_mss );
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+ tmp = el;
+ el = el->el_next;
+ monitor_back_destroy_limbo_entry( tmp, rc );
+
+ if ( rc != 0 ) {
+ /* try all, but report error at end */
+ retcode = 1;
+ }
+ }
+
+ mi->mi_entry_limbo = NULL;
+ }
+
+ return retcode;
+}
+
+int
+monitor_back_config(
+ BackendInfo *bi,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv )
+{
+ /*
+ * eventually, will hold backend specific configuration parameters
+ */
+ return SLAP_CONF_UNKNOWN;
+}
+
+#if 0
+int
+monitor_back_db_config(
+ Backend *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv )
+{
+ monitor_info_t *mi = ( monitor_info_t * )be->be_private;
+
+ /*
+ * eventually, will hold database specific configuration parameters
+ */
+ return SLAP_CONF_UNKNOWN;
+}
+#endif
+
+int
+monitor_back_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr)
+{
+ monitor_info_t *mi = ( monitor_info_t * )be->be_private;
+
+ if ( mi == NULL ) {
+ return -1;
+ }
+
+ /*
+ * FIXME: destroys all the data
+ */
+ /* NOTE: mi points to static storage; don't free it */
+
+ (void)monitor_cache_destroy( mi );
+
+ if ( monitor_subsys ) {
+ int i;
+
+ for ( i = 0; monitor_subsys[ i ] != NULL; i++ ) {
+ if ( monitor_subsys[ i ]->mss_destroy ) {
+ monitor_subsys[ i ]->mss_destroy( be, monitor_subsys[ i ] );
+ }
+
+ if ( !BER_BVISNULL( &monitor_subsys[ i ]->mss_rdn ) ) {
+ ch_free( monitor_subsys[ i ]->mss_rdn.bv_val );
+ }
+ }
+
+ ch_free( monitor_subsys );
+ }
+
+ if ( mi->mi_entry_limbo ) {
+ entry_limbo_t *el = mi->mi_entry_limbo;
+
+ for ( ; el; ) {
+ entry_limbo_t *tmp = el;
+ el = el->el_next;
+ monitor_back_destroy_limbo_entry( tmp, 1 );
+ }
+ }
+
+ ldap_pvt_thread_mutex_destroy( &monitor_info.mi_cache_mutex );
+
+ be->be_private = NULL;
+
+ return 0;
+}
diff --git a/servers/slapd/back-monitor/listener.c b/servers/slapd/back-monitor/listener.c
new file mode 100644
index 0000000..3499252
--- /dev/null
+++ b/servers/slapd/back-monitor/listener.c
@@ -0,0 +1,138 @@
+/* listener.c - deals with listener subsystem */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-monitor.h"
+
+int
+monitor_subsys_listener_init(
+ BackendDB *be,
+ monitor_subsys_t *ms
+)
+{
+ monitor_info_t *mi;
+ Entry *e_listener, **ep;
+ int i;
+ monitor_entry_t *mp;
+ Listener **l;
+
+ assert( be != NULL );
+
+ if ( ( l = slapd_get_listeners() ) == NULL ) {
+ if ( slapMode & SLAP_TOOL_MODE ) {
+ return 0;
+ }
+
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_listener_init: "
+ "unable to get listeners\n" );
+ return( -1 );
+ }
+
+ mi = ( monitor_info_t * )be->be_private;
+
+ if ( monitor_cache_get( mi, &ms->mss_ndn, &e_listener ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_listener_init: "
+ "unable to get entry \"%s\"\n",
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ mp = ( monitor_entry_t * )e_listener->e_private;
+ mp->mp_children = NULL;
+ ep = &mp->mp_children;
+
+ for ( i = 0; l[ i ]; i++ ) {
+ char buf[ BACKMONITOR_BUFSIZE ];
+ Entry *e;
+ struct berval bv;
+
+ bv.bv_len = snprintf( buf, sizeof( buf ),
+ "cn=Listener %d", i );
+ bv.bv_val = buf;
+ e = monitor_entry_stub( &ms->mss_dn, &ms->mss_ndn, &bv,
+ mi->mi_oc_monitoredObject, NULL, NULL );
+
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_listener_init: "
+ "unable to create entry \"cn=Listener %d,%s\"\n",
+ i, ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ attr_merge_normalize_one( e, mi->mi_ad_monitorConnectionLocalAddress,
+ &l[ i ]->sl_name, NULL );
+
+ attr_merge_normalize_one( e, slap_schema.si_ad_labeledURI,
+ &l[ i ]->sl_url, NULL );
+
+#ifdef HAVE_TLS
+ if ( l[ i ]->sl_is_tls ) {
+ struct berval bv;
+
+ BER_BVSTR( &bv, "TLS" );
+ attr_merge_normalize_one( e, mi->mi_ad_monitoredInfo,
+ &bv, NULL );
+ }
+#endif /* HAVE_TLS */
+#ifdef LDAP_CONNECTIONLESS
+ if ( l[ i ]->sl_is_udp ) {
+ struct berval bv;
+
+ BER_BVSTR( &bv, "UDP" );
+ attr_merge_normalize_one( e, mi->mi_ad_monitoredInfo,
+ &bv, NULL );
+ }
+#endif /* HAVE_TLS */
+
+ mp = monitor_entrypriv_create();
+ if ( mp == NULL ) {
+ return -1;
+ }
+ e->e_private = ( void * )mp;
+ mp->mp_info = ms;
+ mp->mp_flags = ms->mss_flags
+ | MONITOR_F_SUB;
+
+ if ( monitor_cache_add( mi, e ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_listener_init: "
+ "unable to add entry \"cn=Listener %d,%s\"\n",
+ i, ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ *ep = e;
+ ep = &mp->mp_next;
+ }
+
+ monitor_cache_release( mi, e_listener );
+
+ return( 0 );
+}
+
diff --git a/servers/slapd/back-monitor/log.c b/servers/slapd/back-monitor/log.c
new file mode 100644
index 0000000..32fd057
--- /dev/null
+++ b/servers/slapd/back-monitor/log.c
@@ -0,0 +1,455 @@
+/* log.c - deal with log subsystem */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include <lber_pvt.h>
+#include "lutil.h"
+#include "ldif.h"
+#include "back-monitor.h"
+
+static int
+monitor_subsys_log_open(
+ BackendDB *be,
+ monitor_subsys_t *ms );
+
+static int
+monitor_subsys_log_modify(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e );
+
+/*
+ * log mutex
+ */
+ldap_pvt_thread_mutex_t monitor_log_mutex;
+
+static int add_values( Operation *op, Entry *e, Modification *mod, int *newlevel );
+static int delete_values( Operation *op, Entry *e, Modification *mod, int *newlevel );
+static int replace_values( Operation *op, Entry *e, Modification *mod, int *newlevel );
+
+/*
+ * initializes log subentry
+ */
+int
+monitor_subsys_log_init(
+ BackendDB *be,
+ monitor_subsys_t *ms )
+{
+ ms->mss_open = monitor_subsys_log_open;
+ ms->mss_modify = monitor_subsys_log_modify;
+
+ ldap_pvt_thread_mutex_init( &monitor_log_mutex );
+
+ return( 0 );
+}
+
+/*
+ * opens log subentry
+ */
+int
+monitor_subsys_log_open(
+ BackendDB *be,
+ monitor_subsys_t *ms )
+{
+ BerVarray bva = NULL;
+
+ if ( loglevel2bvarray( ldap_syslog, &bva ) == 0 && bva != NULL ) {
+ monitor_info_t *mi;
+ Entry *e;
+
+ mi = ( monitor_info_t * )be->be_private;
+
+ if ( monitor_cache_get( mi, &ms->mss_ndn, &e ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_log_init: "
+ "unable to get entry \"%s\"\n",
+ ms->mss_ndn.bv_val );
+ ber_bvarray_free( bva );
+ return( -1 );
+ }
+
+ attr_merge_normalize( e, mi->mi_ad_managedInfo, bva, NULL );
+ ber_bvarray_free( bva );
+
+ monitor_cache_release( mi, e );
+ }
+
+ return( 0 );
+}
+
+static int
+monitor_subsys_log_modify(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e )
+{
+ monitor_info_t *mi = ( monitor_info_t * )op->o_bd->be_private;
+ int rc = LDAP_OTHER;
+ int newlevel = ldap_syslog;
+ Attribute *save_attrs;
+ Modifications *modlist = op->orm_modlist;
+ Modifications *ml;
+
+ ldap_pvt_thread_mutex_lock( &monitor_log_mutex );
+
+ save_attrs = e->e_attrs;
+ e->e_attrs = attrs_dup( e->e_attrs );
+
+ for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
+ Modification *mod = &ml->sml_mod;
+
+ /*
+ * accept all operational attributes;
+ * this includes modifiersName and modifyTimestamp
+ * if lastmod is "on"
+ */
+ if ( is_at_operational( mod->sm_desc->ad_type ) ) {
+ ( void ) attr_delete( &e->e_attrs, mod->sm_desc );
+ rc = rs->sr_err = attr_merge( e, mod->sm_desc,
+ mod->sm_values, mod->sm_nvalues );
+ if ( rc != LDAP_SUCCESS ) {
+ break;
+ }
+ continue;
+
+ /*
+ * only the "managedInfo" attribute can be modified
+ */
+ } else if ( mod->sm_desc != mi->mi_ad_managedInfo ) {
+ rc = rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ break;
+ }
+
+ switch ( mod->sm_op ) {
+ case LDAP_MOD_ADD:
+ rc = add_values( op, e, mod, &newlevel );
+ break;
+
+ case LDAP_MOD_DELETE:
+ rc = delete_values( op, e, mod, &newlevel );
+ break;
+
+ case LDAP_MOD_REPLACE:
+ rc = replace_values( op, e, mod, &newlevel );
+ break;
+
+ default:
+ rc = LDAP_OTHER;
+ break;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_err = rc;
+ break;
+ }
+ }
+
+ /* set the new debug level */
+ if ( rc == LDAP_SUCCESS ) {
+ const char *text;
+ static char textbuf[ BACKMONITOR_BUFSIZE ];
+
+ /* check for abandon */
+ if ( op->o_abandon ) {
+ rc = rs->sr_err = SLAPD_ABANDON;
+
+ goto cleanup;
+ }
+
+ /* check that the entry still obeys the schema */
+ rc = entry_schema_check( op, e, save_attrs, 0, 0, NULL,
+ &text, textbuf, sizeof( textbuf ) );
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_err = rc;
+ goto cleanup;
+ }
+
+ /*
+ * Do we need to protect this with a mutex?
+ */
+ ldap_syslog = newlevel;
+
+#if 0 /* debug rather than log */
+ slap_debug = newlevel;
+ lutil_set_debug_level( "slapd", slap_debug );
+ ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &slap_debug);
+ ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &slap_debug);
+ ldif_debug = slap_debug;
+#endif
+ }
+
+cleanup:;
+ if ( rc == LDAP_SUCCESS ) {
+ attrs_free( save_attrs );
+
+ } else {
+ attrs_free( e->e_attrs );
+ e->e_attrs = save_attrs;
+ }
+
+ ldap_pvt_thread_mutex_unlock( &monitor_log_mutex );
+
+ if ( rc == LDAP_SUCCESS ) {
+ rc = SLAP_CB_CONTINUE;
+ }
+
+ return rc;
+}
+
+static int
+check_constraints( Modification *mod, int *newlevel )
+{
+ int i;
+
+ if ( mod->sm_nvalues != NULL ) {
+ ber_bvarray_free( mod->sm_nvalues );
+ mod->sm_nvalues = NULL;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &mod->sm_values[ i ] ); i++ ) {
+ int l;
+ struct berval bv;
+
+ if ( str2loglevel( mod->sm_values[ i ].bv_val, &l ) ) {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ if ( loglevel2bv( l, &bv ) ) {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ assert( bv.bv_len == mod->sm_values[ i ].bv_len );
+
+ AC_MEMCPY( mod->sm_values[ i ].bv_val,
+ bv.bv_val, bv.bv_len );
+
+ *newlevel |= l;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+add_values( Operation *op, Entry *e, Modification *mod, int *newlevel )
+{
+ Attribute *a;
+ int i, rc;
+ MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
+
+ assert( mod->sm_values != NULL );
+
+ rc = check_constraints( mod, newlevel );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ a = attr_find( e->e_attrs, mod->sm_desc );
+
+ if ( a != NULL ) {
+ /* "managedInfo" SHOULD have appropriate rules ... */
+ if ( mr == NULL || !mr->smr_match ) {
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &mod->sm_values[ i ] ); i++ ) {
+ int rc;
+ int j;
+ const char *text = NULL;
+ struct berval asserted;
+
+ rc = asserted_value_validate_normalize(
+ mod->sm_desc, mr, SLAP_MR_EQUALITY,
+ &mod->sm_values[ i ], &asserted, &text,
+ op->o_tmpmemctx );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ for ( j = 0; !BER_BVISNULL( &a->a_vals[ j ] ); j++ ) {
+ int match;
+ int rc = value_match( &match, mod->sm_desc, mr,
+ 0, &a->a_nvals[ j ], &asserted, &text );
+
+ if ( rc == LDAP_SUCCESS && match == 0 ) {
+ free( asserted.bv_val );
+ return LDAP_TYPE_OR_VALUE_EXISTS;
+ }
+ }
+
+ free( asserted.bv_val );
+ }
+ }
+
+ /* no - add them */
+ rc = attr_merge_normalize( e, mod->sm_desc, mod->sm_values,
+ op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+delete_values( Operation *op, Entry *e, Modification *mod, int *newlevel )
+{
+ int i, j, k, found, rc, nl = 0;
+ Attribute *a;
+ MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
+
+ /* delete the entire attribute */
+ if ( mod->sm_values == NULL ) {
+ int rc = attr_delete( &e->e_attrs, mod->sm_desc );
+
+ if ( rc ) {
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+
+ } else {
+ *newlevel = 0;
+ rc = LDAP_SUCCESS;
+ }
+ return rc;
+ }
+
+ rc = check_constraints( mod, &nl );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ *newlevel &= ~nl;
+
+ if ( mr == NULL || !mr->smr_match ) {
+ /* disallow specific attributes from being deleted if
+ * no equality rule */
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+
+ /* delete specific values - find the attribute first */
+ if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
+ return( LDAP_NO_SUCH_ATTRIBUTE );
+ }
+
+ /* find each value to delete */
+ for ( i = 0; !BER_BVISNULL( &mod->sm_values[ i ] ); i++ ) {
+ int rc;
+ const char *text = NULL;
+
+ struct berval asserted;
+
+ rc = asserted_value_validate_normalize(
+ mod->sm_desc, mr, SLAP_MR_EQUALITY,
+ &mod->sm_values[ i ], &asserted, &text,
+ op->o_tmpmemctx );
+
+ if( rc != LDAP_SUCCESS ) return rc;
+
+ found = 0;
+ for ( j = 0; !BER_BVISNULL( &a->a_vals[ j ] ); j++ ) {
+ int match;
+ int rc = value_match( &match, mod->sm_desc, mr,
+ 0, &a->a_nvals[ j ], &asserted, &text );
+
+ if( rc == LDAP_SUCCESS && match != 0 ) {
+ continue;
+ }
+
+ /* found a matching value */
+ found = 1;
+
+ /* delete it */
+ if ( a->a_nvals != a->a_vals ) {
+ free( a->a_nvals[ j ].bv_val );
+ for ( k = j + 1; !BER_BVISNULL( &a->a_nvals[ k ] ); k++ ) {
+ a->a_nvals[ k - 1 ] = a->a_nvals[ k ];
+ }
+ BER_BVZERO( &a->a_nvals[ k - 1 ] );
+ }
+
+ free( a->a_vals[ j ].bv_val );
+ for ( k = j + 1; !BER_BVISNULL( &a->a_vals[ k ] ); k++ ) {
+ a->a_vals[ k - 1 ] = a->a_vals[ k ];
+ }
+ BER_BVZERO( &a->a_vals[ k - 1 ] );
+ a->a_numvals--;
+
+ break;
+ }
+
+ free( asserted.bv_val );
+
+ /* looked through them all w/o finding it */
+ if ( ! found ) {
+ return LDAP_NO_SUCH_ATTRIBUTE;
+ }
+ }
+
+ /* if no values remain, delete the entire attribute */
+ if ( BER_BVISNULL( &a->a_vals[ 0 ] ) ) {
+ assert( a->a_numvals == 0 );
+
+ /* should already be zero */
+ *newlevel = 0;
+
+ if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
+ return LDAP_NO_SUCH_ATTRIBUTE;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+replace_values( Operation *op, Entry *e, Modification *mod, int *newlevel )
+{
+ int rc;
+
+ if ( mod->sm_values != NULL ) {
+ *newlevel = 0;
+ rc = check_constraints( mod, newlevel );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+ }
+
+ rc = attr_delete( &e->e_attrs, mod->sm_desc );
+
+ if ( rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_ATTRIBUTE ) {
+ return rc;
+ }
+
+ if ( mod->sm_values != NULL ) {
+ rc = attr_merge_normalize( e, mod->sm_desc, mod->sm_values,
+ op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
diff --git a/servers/slapd/back-monitor/modify.c b/servers/slapd/back-monitor/modify.c
new file mode 100644
index 0000000..bc6543f
--- /dev/null
+++ b/servers/slapd/back-monitor/modify.c
@@ -0,0 +1,90 @@
+/* modify.c - monitor backend modify routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-monitor.h"
+#include "proto-back-monitor.h"
+
+int
+monitor_back_modify( Operation *op, SlapReply *rs )
+{
+ int rc = 0;
+ monitor_info_t *mi = ( monitor_info_t * )op->o_bd->be_private;
+ Entry *matched;
+ Entry *e;
+
+ Debug(LDAP_DEBUG_ARGS, "monitor_back_modify:\n" );
+
+ /* acquire and lock entry */
+ monitor_cache_dn2entry( op, rs, &op->o_req_ndn, &e, &matched );
+ if ( e == NULL ) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ if ( matched ) {
+ if ( !access_allowed_mask( op, matched,
+ slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL, NULL ) )
+ {
+ /* do nothing */ ;
+ } else {
+ rs->sr_matched = matched->e_dn;
+ }
+ }
+ send_ldap_result( op, rs );
+ if ( matched != NULL ) {
+ rs->sr_matched = NULL;
+ monitor_cache_release( mi, matched );
+ }
+ return rs->sr_err;
+ }
+
+ if ( !acl_check_modlist( op, e, op->orm_modlist )) {
+ rc = LDAP_INSUFFICIENT_ACCESS;
+
+ } else {
+ assert( !SLAP_SHADOW( op->o_bd ) );
+ slap_mods_opattrs( op, &op->orm_modlist, 0 );
+
+ rc = monitor_entry_modify( op, rs, e );
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ if ( !access_allowed_mask( op, e, slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL, NULL ) )
+ {
+ rc = LDAP_NO_SUCH_OBJECT;
+ }
+ }
+
+ rs->sr_err = rc;
+ send_ldap_result( op, rs );
+
+ monitor_cache_release( mi, e );
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/back-monitor/operation.c b/servers/slapd/back-monitor/operation.c
new file mode 100644
index 0000000..875b2c3
--- /dev/null
+++ b/servers/slapd/back-monitor/operation.c
@@ -0,0 +1,244 @@
+/* operation.c - deal with operation subsystem */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-monitor.h"
+#include "lber_pvt.h"
+
+struct monitor_ops_t {
+ struct berval rdn;
+ struct berval nrdn;
+} monitor_op[] = {
+ { BER_BVC( "cn=Bind" ), BER_BVNULL },
+ { BER_BVC( "cn=Unbind" ), BER_BVNULL },
+ { BER_BVC( "cn=Search" ), BER_BVNULL },
+ { BER_BVC( "cn=Compare" ), BER_BVNULL },
+ { BER_BVC( "cn=Modify" ), BER_BVNULL },
+ { BER_BVC( "cn=Modrdn" ), BER_BVNULL },
+ { BER_BVC( "cn=Add" ), BER_BVNULL },
+ { BER_BVC( "cn=Delete" ), BER_BVNULL },
+ { BER_BVC( "cn=Abandon" ), BER_BVNULL },
+ { BER_BVC( "cn=Extended" ), BER_BVNULL },
+ { BER_BVNULL, BER_BVNULL }
+};
+
+static int
+monitor_subsys_ops_destroy(
+ BackendDB *be,
+ monitor_subsys_t *ms );
+
+static int
+monitor_subsys_ops_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e );
+
+int
+monitor_subsys_ops_init(
+ BackendDB *be,
+ monitor_subsys_t *ms )
+{
+ monitor_info_t *mi;
+
+ Entry *e_op, **ep;
+ monitor_entry_t *mp;
+ int i;
+ struct berval bv_zero = BER_BVC( "0" );
+
+ assert( be != NULL );
+
+ ms->mss_destroy = monitor_subsys_ops_destroy;
+ ms->mss_update = monitor_subsys_ops_update;
+
+ mi = ( monitor_info_t * )be->be_private;
+
+ if ( monitor_cache_get( mi,
+ &ms->mss_ndn, &e_op ) )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_ops_init: "
+ "unable to get entry \"%s\"\n",
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ attr_merge_one( e_op, mi->mi_ad_monitorOpInitiated, &bv_zero, NULL );
+ attr_merge_one( e_op, mi->mi_ad_monitorOpCompleted, &bv_zero, NULL );
+
+ mp = ( monitor_entry_t * )e_op->e_private;
+ mp->mp_children = NULL;
+ ep = &mp->mp_children;
+
+ for ( i = 0; i < SLAP_OP_LAST; i++ ) {
+ struct berval rdn;
+ Entry *e;
+ struct berval bv;
+
+ /*
+ * Initiated ops
+ */
+ e = monitor_entry_stub( &ms->mss_dn, &ms->mss_ndn, &monitor_op[i].rdn,
+ mi->mi_oc_monitorOperation, NULL, NULL );
+
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_ops_init: "
+ "unable to create entry \"%s,%s\"\n",
+ monitor_op[ i ].rdn.bv_val,
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ BER_BVSTR( &bv, "0" );
+ attr_merge_one( e, mi->mi_ad_monitorOpInitiated, &bv, NULL );
+ attr_merge_one( e, mi->mi_ad_monitorOpCompleted, &bv, NULL );
+
+ /* steal normalized RDN */
+ dnRdn( &e->e_nname, &rdn );
+ ber_dupbv( &monitor_op[ i ].nrdn, &rdn );
+
+ mp = monitor_entrypriv_create();
+ if ( mp == NULL ) {
+ return -1;
+ }
+ e->e_private = ( void * )mp;
+ mp->mp_info = ms;
+ mp->mp_flags = ms->mss_flags \
+ | MONITOR_F_SUB | MONITOR_F_PERSISTENT;
+
+ if ( monitor_cache_add( mi, e ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_ops_init: "
+ "unable to add entry \"%s,%s\"\n",
+ monitor_op[ i ].rdn.bv_val,
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ *ep = e;
+ ep = &mp->mp_next;
+ }
+
+ monitor_cache_release( mi, e_op );
+
+ return( 0 );
+}
+
+static int
+monitor_subsys_ops_destroy(
+ BackendDB *be,
+ monitor_subsys_t *ms )
+{
+ int i;
+
+ for ( i = 0; i < SLAP_OP_LAST; i++ ) {
+ if ( !BER_BVISNULL( &monitor_op[ i ].nrdn ) ) {
+ ch_free( monitor_op[ i ].nrdn.bv_val );
+ }
+ }
+
+ return 0;
+}
+
+static int
+monitor_subsys_ops_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e )
+{
+ monitor_info_t *mi = ( monitor_info_t * )op->o_bd->be_private;
+
+ ldap_pvt_mp_t nInitiated = LDAP_PVT_MP_INIT,
+ nCompleted = LDAP_PVT_MP_INIT;
+ struct berval rdn;
+ int i;
+ Attribute *a;
+ slap_counters_t *sc;
+ static struct berval bv_ops = BER_BVC( "cn=operations" );
+
+ assert( mi != NULL );
+ assert( e != NULL );
+
+ dnRdn( &e->e_nname, &rdn );
+
+ if ( dn_match( &rdn, &bv_ops ) ) {
+ ldap_pvt_mp_init( nInitiated );
+ ldap_pvt_mp_init( nCompleted );
+
+ ldap_pvt_thread_mutex_lock( &slap_counters.sc_mutex );
+ ldap_pvt_mp_add( nInitiated, slap_counters.sc_ops_initiated );
+ ldap_pvt_mp_add( nCompleted, slap_counters.sc_ops_completed );
+ for ( sc = slap_counters.sc_next; sc; sc = sc->sc_next ) {
+ ldap_pvt_thread_mutex_lock( &sc->sc_mutex );
+ ldap_pvt_mp_add( nInitiated, sc->sc_ops_initiated );
+ ldap_pvt_mp_add( nCompleted, sc->sc_ops_completed );
+ ldap_pvt_thread_mutex_unlock( &sc->sc_mutex );
+ }
+ ldap_pvt_thread_mutex_unlock( &slap_counters.sc_mutex );
+
+ } else {
+ for ( i = 0; i < SLAP_OP_LAST; i++ ) {
+ if ( dn_match( &rdn, &monitor_op[ i ].nrdn ) )
+ {
+ ldap_pvt_thread_mutex_lock( &slap_counters.sc_mutex );
+ ldap_pvt_mp_init_set( nInitiated, slap_counters.sc_ops_initiated_[ i ] );
+ ldap_pvt_mp_init_set( nCompleted, slap_counters.sc_ops_completed_[ i ] );
+ for ( sc = slap_counters.sc_next; sc; sc = sc->sc_next ) {
+ ldap_pvt_thread_mutex_lock( &sc->sc_mutex );
+ ldap_pvt_mp_add( nInitiated, sc->sc_ops_initiated_[ i ] );
+ ldap_pvt_mp_add( nCompleted, sc->sc_ops_completed_[ i ] );
+ ldap_pvt_thread_mutex_unlock( &sc->sc_mutex );
+ }
+ ldap_pvt_thread_mutex_unlock( &slap_counters.sc_mutex );
+ break;
+ }
+ }
+
+ if ( i == SLAP_OP_LAST ) {
+ /* not found ... */
+ return( 0 );
+ }
+ }
+
+ a = attr_find( e->e_attrs, mi->mi_ad_monitorOpInitiated );
+ assert ( a != NULL );
+
+ /* NOTE: no minus sign is allowed in the counters... */
+ UI2BV( &a->a_vals[ 0 ], nInitiated );
+ ldap_pvt_mp_clear( nInitiated );
+
+ a = attr_find( e->e_attrs, mi->mi_ad_monitorOpCompleted );
+ assert ( a != NULL );
+
+ /* NOTE: no minus sign is allowed in the counters... */
+ UI2BV( &a->a_vals[ 0 ], nCompleted );
+ ldap_pvt_mp_clear( nCompleted );
+
+ /* FIXME: touch modifyTimestamp? */
+
+ return SLAP_CB_CONTINUE;
+}
+
diff --git a/servers/slapd/back-monitor/operational.c b/servers/slapd/back-monitor/operational.c
new file mode 100644
index 0000000..994b254
--- /dev/null
+++ b/servers/slapd/back-monitor/operational.c
@@ -0,0 +1,72 @@
+/* operational.c - monitor backend operational attributes function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-monitor.h"
+#include "proto-back-monitor.h"
+
+/*
+ * sets the supported operational attributes (if required)
+ */
+
+int
+monitor_back_operational(
+ Operation *op,
+ SlapReply *rs )
+{
+ Attribute **ap;
+
+ assert( rs->sr_entry != NULL );
+
+ for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next ) {
+ if ( (*ap)->a_desc == slap_schema.si_ad_hasSubordinates ) {
+ break;
+ }
+ }
+
+ if ( *ap == NULL &&
+ attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_hasSubordinates ) == NULL &&
+ ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
+ ad_inlist( slap_schema.si_ad_hasSubordinates, rs->sr_attrs ) ) )
+ {
+ int hs;
+ monitor_entry_t *mp;
+
+ mp = ( monitor_entry_t * )rs->sr_entry->e_private;
+
+ assert( mp != NULL );
+
+ hs = MONITOR_HAS_CHILDREN( mp );
+ *ap = slap_operational_hasSubordinate( hs );
+ assert( *ap != NULL );
+ ap = &(*ap)->a_next;
+ }
+
+ return LDAP_SUCCESS;
+}
+
diff --git a/servers/slapd/back-monitor/overlay.c b/servers/slapd/back-monitor/overlay.c
new file mode 100644
index 0000000..bd6a703
--- /dev/null
+++ b/servers/slapd/back-monitor/overlay.c
@@ -0,0 +1,140 @@
+/* overlay.c - deals with overlay subsystem */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-monitor.h"
+
+/*
+ * initializes overlay subentries
+ */
+int
+monitor_subsys_overlay_init(
+ BackendDB *be,
+ monitor_subsys_t *ms
+)
+{
+ monitor_info_t *mi;
+ Entry *e_overlay, **ep;
+ int i;
+ monitor_entry_t *mp;
+ slap_overinst *on;
+ monitor_subsys_t *ms_database;
+
+ mi = ( monitor_info_t * )be->be_private;
+
+ ms_database = monitor_back_get_subsys( SLAPD_MONITOR_DATABASE_NAME );
+ if ( ms_database == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_backend_init: "
+ "unable to get "
+ "\"" SLAPD_MONITOR_DATABASE_NAME "\" "
+ "subsystem\n" );
+ return -1;
+ }
+
+ if ( monitor_cache_get( mi, &ms->mss_ndn, &e_overlay ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_overlay_init: "
+ "unable to get entry \"%s\"\n",
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ mp = ( monitor_entry_t * )e_overlay->e_private;
+ mp->mp_children = NULL;
+ ep = &mp->mp_children;
+
+ for ( on = overlay_next( NULL ), i = 0; on; on = overlay_next( on ), i++ ) {
+ char buf[ BACKMONITOR_BUFSIZE ];
+ struct berval bv;
+ int j;
+ Entry *e;
+ BackendDB *be;
+
+ bv.bv_len = snprintf( buf, sizeof( buf ), "cn=Overlay %d", i );
+ bv.bv_val = buf;
+ e = monitor_entry_stub( &ms->mss_dn, &ms->mss_ndn, &bv,
+ mi->mi_oc_monitoredObject, NULL, NULL );
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_overlay_init: "
+ "unable to create entry \"cn=Overlay %d,%s\"\n",
+ i, ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+ ber_str2bv( on->on_bi.bi_type, 0, 0, &bv );
+ attr_merge_normalize_one( e, mi->mi_ad_monitoredInfo, &bv, NULL );
+ attr_merge_normalize_one( e, mi->mi_ad_monitorRuntimeConfig,
+ on->on_bi.bi_cf_ocs ? (struct berval *)&slap_true_bv :
+ (struct berval *)&slap_false_bv, NULL );
+
+ attr_merge_normalize_one( e_overlay, mi->mi_ad_monitoredInfo,
+ &bv, NULL );
+
+ j = -1;
+ LDAP_STAILQ_FOREACH( be, &backendDB, be_next ) {
+ char buf[ SLAP_LDAPDN_MAXLEN ];
+ struct berval dn;
+
+ j++;
+ if ( !overlay_is_inst( be, on->on_bi.bi_type ) ) {
+ continue;
+ }
+
+ snprintf( buf, sizeof( buf ), "cn=Database %d,%s",
+ j, ms_database->mss_dn.bv_val );
+
+ ber_str2bv( buf, 0, 0, &dn );
+ attr_merge_normalize_one( e, slap_schema.si_ad_seeAlso,
+ &dn, NULL );
+ }
+
+ mp = monitor_entrypriv_create();
+ if ( mp == NULL ) {
+ return -1;
+ }
+ e->e_private = ( void * )mp;
+ mp->mp_info = ms;
+ mp->mp_flags = ms->mss_flags
+ | MONITOR_F_SUB;
+
+ if ( monitor_cache_add( mi, e ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_overlay_init: "
+ "unable to add entry \"cn=Overlay %d,%s\"\n",
+ i, ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ *ep = e;
+ ep = &mp->mp_next;
+ }
+
+ monitor_cache_release( mi, e_overlay );
+
+ return( 0 );
+}
+
diff --git a/servers/slapd/back-monitor/proto-back-monitor.h b/servers/slapd/back-monitor/proto-back-monitor.h
new file mode 100644
index 0000000..ec609bc
--- /dev/null
+++ b/servers/slapd/back-monitor/proto-back-monitor.h
@@ -0,0 +1,342 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#ifndef _PROTO_BACK_MONITOR
+#define _PROTO_BACK_MONITOR
+
+#include <ldap_cdefs.h>
+
+LDAP_BEGIN_DECL
+
+/*
+ * backends
+ */
+int
+monitor_subsys_backend_init LDAP_P((
+ BackendDB *be,
+ monitor_subsys_t *ms ));
+
+/*
+ * cache
+ */
+extern int
+monitor_cache_cmp LDAP_P((
+ const void *c1,
+ const void *c2 ));
+extern int
+monitor_cache_dup LDAP_P((
+ void *c1,
+ void *c2 ));
+extern int
+monitor_cache_add LDAP_P((
+ monitor_info_t *mi,
+ Entry *e ));
+extern int
+monitor_cache_get LDAP_P((
+ monitor_info_t *mi,
+ struct berval *ndn,
+ Entry **ep ));
+extern int
+monitor_cache_remove LDAP_P((
+ monitor_info_t *mi,
+ struct berval *ndn,
+ Entry **ep ));
+extern int
+monitor_cache_dn2entry LDAP_P((
+ Operation *op,
+ SlapReply *rs,
+ struct berval *ndn,
+ Entry **ep,
+ Entry **matched ));
+extern int
+monitor_cache_lock LDAP_P((
+ Entry *e ));
+extern int
+monitor_cache_release LDAP_P((
+ monitor_info_t *mi,
+ Entry *e ));
+
+extern int
+monitor_cache_destroy LDAP_P((
+ monitor_info_t *mi ));
+
+extern int
+monitor_back_release(
+ Operation *op,
+ Entry *e,
+ int rw );
+
+/*
+ * connections
+ */
+extern int
+monitor_subsys_conn_init LDAP_P((
+ BackendDB *be,
+ monitor_subsys_t *ms ));
+
+/*
+ * databases
+ */
+extern int
+monitor_subsys_database_init LDAP_P((
+ BackendDB *be,
+ monitor_subsys_t *ms ));
+
+/*
+ * entry
+ */
+extern int
+monitor_entry_update LDAP_P((
+ Operation *op,
+ SlapReply *rs,
+ Entry *e ));
+extern int
+monitor_entry_create LDAP_P((
+ Operation *op,
+ SlapReply *rs,
+ struct berval *ndn,
+ Entry *e_parent,
+ Entry **ep ));
+extern int
+monitor_entry_modify LDAP_P((
+ Operation *op,
+ SlapReply *rs,
+ Entry *e ));
+extern int
+monitor_entry_test_flags LDAP_P((
+ monitor_entry_t *mp,
+ int cond ));
+extern monitor_entry_t *
+monitor_back_entrypriv_create LDAP_P((
+ void ));
+extern Entry *
+monitor_back_entry_stub LDAP_P((
+ struct berval *pdn,
+ struct berval *pndn,
+ struct berval *rdn,
+ ObjectClass *oc,
+ struct berval *create,
+ struct berval *modify ));
+extern Entry *
+monitor_back_entry_get_unlocked LDAP_P((
+ struct berval *ndn ));
+
+#define monitor_entrypriv_create monitor_back_entrypriv_create
+#define monitor_entry_stub monitor_back_entry_stub
+#define monitor_entry_get_unlocked monitor_back_entry_get_unlocked
+
+/*
+ * init
+ */
+extern int
+monitor_subsys_is_opened LDAP_P((
+ void ));
+extern int
+monitor_back_register_subsys LDAP_P((
+ monitor_subsys_t *ms ));
+extern int
+monitor_back_register_subsys_late LDAP_P((
+ monitor_subsys_t *ms ));
+extern int
+monitor_back_register_backend LDAP_P((
+ BackendInfo *bi ));
+extern int
+monitor_back_register_database LDAP_P((
+ BackendDB *be,
+ struct berval *ndn ));
+extern int
+monitor_back_register_overlay_info LDAP_P((
+ slap_overinst *on ));
+extern int
+monitor_back_register_overlay LDAP_P((
+ BackendDB *be,
+ struct slap_overinst *on,
+ struct berval *ndn_out ));
+extern int
+monitor_back_register_backend_limbo LDAP_P((
+ BackendInfo *bi ));
+extern int
+monitor_back_register_database_limbo LDAP_P((
+ BackendDB *be,
+ struct berval *ndn_out ));
+extern int
+monitor_back_register_overlay_info_limbo LDAP_P((
+ slap_overinst *on ));
+extern int
+monitor_back_register_overlay_limbo LDAP_P((
+ BackendDB *be,
+ struct slap_overinst *on,
+ struct berval *ndn_out ));
+extern monitor_subsys_t *
+monitor_back_get_subsys LDAP_P((
+ const char *name ));
+extern monitor_subsys_t *
+monitor_back_get_subsys_by_dn LDAP_P((
+ struct berval *ndn,
+ int sub ));
+extern int
+monitor_back_is_configured LDAP_P(( void ));
+extern int
+monitor_back_register_entry LDAP_P((
+ Entry *e,
+ monitor_callback_t *cb,
+ monitor_subsys_t *mss,
+ unsigned long flags ));
+extern int
+monitor_back_register_entry_parent LDAP_P((
+ Entry *e,
+ monitor_callback_t *cb,
+ monitor_subsys_t *mss,
+ unsigned long flags,
+ struct berval *base,
+ int scope,
+ struct berval *filter ));
+extern int
+monitor_search2ndn LDAP_P((
+ struct berval *base,
+ int scope,
+ struct berval *filter,
+ struct berval *ndn ));
+extern int
+monitor_back_register_entry_attrs LDAP_P((
+ struct berval *ndn,
+ Attribute *a,
+ monitor_callback_t *cb,
+ struct berval *base,
+ int scope,
+ struct berval *filter ));
+extern int
+monitor_back_register_entry_callback LDAP_P((
+ struct berval *ndn,
+ monitor_callback_t *cb,
+ struct berval *base,
+ int scope,
+ struct berval *filter ));
+extern int
+monitor_back_unregister_entry LDAP_P((
+ struct berval *ndn ));
+extern int
+monitor_back_unregister_entry_parent LDAP_P((
+ struct berval *nrdn,
+ monitor_callback_t *target_cb,
+ struct berval *base,
+ int scope,
+ struct berval *filter ));
+extern int
+monitor_back_unregister_entry_attrs LDAP_P((
+ struct berval *ndn,
+ Attribute *a,
+ monitor_callback_t *cb,
+ struct berval *base,
+ int scope,
+ struct berval *filter ));
+extern int
+monitor_back_unregister_entry_callback LDAP_P((
+ struct berval *ndn,
+ monitor_callback_t *cb,
+ struct berval *base,
+ int scope,
+ struct berval *filter ));
+
+/*
+ * listener
+ */
+extern int
+monitor_subsys_listener_init LDAP_P((
+ BackendDB *be,
+ monitor_subsys_t *ms ));
+
+/*
+ * log
+ */
+extern int
+monitor_subsys_log_init LDAP_P((
+ BackendDB *be,
+ monitor_subsys_t *ms ));
+
+/*
+ * operations
+ */
+extern int
+monitor_subsys_ops_init LDAP_P((
+ BackendDB *be,
+ monitor_subsys_t *ms ));
+
+/*
+ * overlay
+ */
+extern int
+monitor_subsys_overlay_init LDAP_P((
+ BackendDB *be,
+ monitor_subsys_t *ms ));
+
+/*
+ * sent
+ */
+extern int
+monitor_subsys_sent_init LDAP_P((
+ BackendDB *be,
+ monitor_subsys_t *ms ));
+
+/*
+ * threads
+ */
+extern int
+monitor_subsys_thread_init LDAP_P((
+ BackendDB *be,
+ monitor_subsys_t *ms ));
+
+/*
+ * time
+ */
+extern int monitor_subsys_time_init LDAP_P((
+ BackendDB *be,
+ monitor_subsys_t *ms ));
+
+/*
+ * waiters
+ */
+extern int
+monitor_subsys_rww_init LDAP_P((
+ BackendDB *be,
+ monitor_subsys_t *ms ));
+
+/*
+ * former external.h
+ */
+
+extern BI_init monitor_back_initialize;
+
+extern BI_db_init monitor_back_db_init;
+extern BI_db_open monitor_back_db_open;
+extern BI_config monitor_back_config;
+extern BI_db_destroy monitor_back_db_destroy;
+extern BI_db_config monitor_back_db_config;
+
+extern BI_op_search monitor_back_search;
+extern BI_op_compare monitor_back_compare;
+extern BI_op_modify monitor_back_modify;
+extern BI_op_bind monitor_back_bind;
+extern BI_operational monitor_back_operational;
+
+LDAP_END_DECL
+
+#endif /* _PROTO_BACK_MONITOR */
+
diff --git a/servers/slapd/back-monitor/rww.c b/servers/slapd/back-monitor/rww.c
new file mode 100644
index 0000000..04c2e75
--- /dev/null
+++ b/servers/slapd/back-monitor/rww.c
@@ -0,0 +1,232 @@
+/* readw.c - deal with read waiters subsystem */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "lutil.h"
+#include "back-monitor.h"
+
+static int
+monitor_subsys_rww_destroy(
+ BackendDB *be,
+ monitor_subsys_t *ms );
+
+static int
+monitor_subsys_rww_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e );
+
+enum {
+ MONITOR_RWW_READ = 0,
+ MONITOR_RWW_WRITE,
+
+ MONITOR_RWW_LAST
+};
+
+static struct monitor_rww_t {
+ struct berval rdn;
+ struct berval nrdn;
+} monitor_rww[] = {
+ { BER_BVC("cn=Read"), BER_BVNULL },
+ { BER_BVC("cn=Write"), BER_BVNULL },
+ { BER_BVNULL, BER_BVNULL }
+};
+
+int
+monitor_subsys_rww_init(
+ BackendDB *be,
+ monitor_subsys_t *ms )
+{
+ monitor_info_t *mi;
+
+ Entry **ep, *e_conn;
+ monitor_entry_t *mp;
+ int i;
+
+ assert( be != NULL );
+
+ ms->mss_destroy = monitor_subsys_rww_destroy;
+ ms->mss_update = monitor_subsys_rww_update;
+
+ mi = ( monitor_info_t * )be->be_private;
+
+ if ( monitor_cache_get( mi, &ms->mss_ndn, &e_conn ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_rww_init: "
+ "unable to get entry \"%s\"\n",
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ mp = ( monitor_entry_t * )e_conn->e_private;
+ mp->mp_children = NULL;
+ ep = &mp->mp_children;
+
+ for ( i = 0; i < MONITOR_RWW_LAST; i++ ) {
+ struct berval nrdn, bv;
+ Entry *e;
+
+ e = monitor_entry_stub( &ms->mss_dn, &ms->mss_ndn, &monitor_rww[i].rdn,
+ mi->mi_oc_monitorCounterObject, NULL, NULL );
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_rww_init: "
+ "unable to create entry \"cn=Read,%s\"\n",
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ /* steal normalized RDN */
+ dnRdn( &e->e_nname, &nrdn );
+ ber_dupbv( &monitor_rww[ i ].nrdn, &nrdn );
+
+ BER_BVSTR( &bv, "0" );
+ attr_merge_one( e, mi->mi_ad_monitorCounter, &bv, NULL );
+
+ mp = monitor_entrypriv_create();
+ if ( mp == NULL ) {
+ return -1;
+ }
+ e->e_private = ( void * )mp;
+ mp->mp_info = ms;
+ mp->mp_flags = ms->mss_flags \
+ | MONITOR_F_SUB | MONITOR_F_PERSISTENT;
+
+ if ( monitor_cache_add( mi, e ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_rww_init: "
+ "unable to add entry \"%s,%s\"\n",
+ monitor_rww[ i ].rdn.bv_val,
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ *ep = e;
+ ep = &mp->mp_next;
+ }
+
+ monitor_cache_release( mi, e_conn );
+
+ return( 0 );
+}
+
+static int
+monitor_subsys_rww_destroy(
+ BackendDB *be,
+ monitor_subsys_t *ms )
+{
+ int i;
+
+ for ( i = 0; i < MONITOR_RWW_LAST; i++ ) {
+ ber_memfree_x( monitor_rww[ i ].nrdn.bv_val, NULL );
+ }
+
+ return 0;
+}
+
+static int
+monitor_subsys_rww_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e )
+{
+ monitor_info_t *mi = (monitor_info_t *)op->o_bd->be_private;
+ Connection *c;
+ ber_socket_t connindex;
+ long nconns, nwritewaiters, nreadwaiters;
+
+ int i;
+ struct berval nrdn;
+
+ Attribute *a;
+ char buf[LDAP_PVT_INTTYPE_CHARS(long)];
+ long num = 0;
+ ber_len_t len;
+
+ assert( mi != NULL );
+ assert( e != NULL );
+
+ dnRdn( &e->e_nname, &nrdn );
+
+ for ( i = 0; !BER_BVISNULL( &monitor_rww[ i ].nrdn ); i++ ) {
+ if ( dn_match( &nrdn, &monitor_rww[ i ].nrdn ) ) {
+ break;
+ }
+ }
+
+ if ( i == MONITOR_RWW_LAST ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ nconns = nwritewaiters = nreadwaiters = 0;
+ for ( c = connection_first( &connindex );
+ c != NULL;
+ c = connection_next( c, &connindex ), nconns++ )
+ {
+ if ( c->c_writewaiter ) {
+ nwritewaiters++;
+ }
+
+ /* FIXME: ?!? */
+ if ( c->c_currentber != NULL ) {
+ nreadwaiters++;
+ }
+ }
+ connection_done(c);
+
+ switch ( i ) {
+ case MONITOR_RWW_READ:
+ num = nreadwaiters;
+ break;
+
+ case MONITOR_RWW_WRITE:
+ num = nwritewaiters;
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+ snprintf( buf, sizeof( buf ), "%ld", num );
+
+ a = attr_find( e->e_attrs, mi->mi_ad_monitorCounter );
+ assert( a != NULL );
+ len = strlen( buf );
+ if ( len > a->a_vals[ 0 ].bv_len ) {
+ a->a_vals[ 0 ].bv_val = ber_memrealloc( a->a_vals[ 0 ].bv_val, len + 1 );
+ if ( BER_BVISNULL( &a->a_vals[ 0 ] ) ) {
+ BER_BVZERO( &a->a_vals[ 0 ] );
+ return SLAP_CB_CONTINUE;
+ }
+ }
+ AC_MEMCPY( a->a_vals[ 0 ].bv_val, buf, len + 1 );
+ a->a_vals[ 0 ].bv_len = len;
+
+ /* FIXME: touch modifyTimestamp? */
+
+ return SLAP_CB_CONTINUE;
+}
+
diff --git a/servers/slapd/back-monitor/search.c b/servers/slapd/back-monitor/search.c
new file mode 100644
index 0000000..4c720dd
--- /dev/null
+++ b/servers/slapd/back-monitor/search.c
@@ -0,0 +1,271 @@
+/* search.c - monitor backend search function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-monitor.h"
+#include "proto-back-monitor.h"
+
+static void
+monitor_find_children(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e_parent,
+ Entry **nonv,
+ Entry **vol
+)
+{
+ monitor_entry_t *mp;
+
+ mp = ( monitor_entry_t * )e_parent->e_private;
+ *nonv = mp->mp_children;
+
+ if ( MONITOR_HAS_VOLATILE_CH( mp ) ) {
+ monitor_entry_create( op, rs, NULL, e_parent, vol );
+ }
+}
+
+static int
+monitor_send_children(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e_nonvolatile,
+ Entry *e_ch,
+ int sub )
+{
+ monitor_info_t *mi = ( monitor_info_t * )op->o_bd->be_private;
+ Entry *e,
+ *e_tmp;
+ monitor_entry_t *mp;
+ int rc,
+ nonvolatile = 0;
+
+ e = e_nonvolatile;
+
+ /* no volatile entries? */
+ if ( e_ch == NULL ) {
+ /* no persistent entries? return */
+ if ( e == NULL ) {
+ return LDAP_SUCCESS;
+ }
+
+ /* volatile entries */
+ } else {
+ /* if no persistent, return only volatile */
+ if ( e == NULL ) {
+ e = e_ch;
+
+ /* else append persistent to volatile */
+ } else {
+ e_tmp = e_ch;
+ do {
+ mp = ( monitor_entry_t * )e_tmp->e_private;
+ e_tmp = mp->mp_next;
+
+ if ( e_tmp == NULL ) {
+ mp->mp_next = e;
+ break;
+ }
+ } while ( e_tmp );
+ e = e_ch;
+ }
+ }
+
+ /* return entries */
+ for ( ; e != NULL; e = e_tmp ) {
+ Entry *sub_nv = NULL, *sub_ch = NULL;
+
+ monitor_cache_lock( e );
+ monitor_entry_update( op, rs, e );
+
+ if ( e == e_nonvolatile )
+ nonvolatile = 1;
+
+ mp = ( monitor_entry_t * )e->e_private;
+ e_tmp = mp->mp_next;
+
+ if ( op->o_abandon ) {
+ monitor_cache_release( mi, e );
+ rc = SLAPD_ABANDON;
+ goto freeout;
+ }
+
+ if ( sub )
+ monitor_find_children( op, rs, e, &sub_nv, &sub_ch );
+
+ rc = test_filter( op, e, op->oq_search.rs_filter );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rs->sr_entry = e;
+ rs->sr_flags = REP_ENTRY_MUSTRELEASE;
+ rc = send_search_entry( op, rs );
+ if ( rc ) {
+ for ( e = sub_ch; e != NULL; e = sub_nv ) {
+ mp = ( monitor_entry_t * )e->e_private;
+ sub_nv = mp->mp_next;
+ monitor_cache_lock( e );
+ monitor_cache_release( mi, e );
+ }
+ goto freeout;
+ }
+ } else {
+ monitor_cache_release( mi, e );
+ }
+
+ if ( sub ) {
+ rc = monitor_send_children( op, rs, sub_nv, sub_ch, sub );
+ if ( rc ) {
+freeout:
+ if ( nonvolatile == 0 ) {
+ for ( ; e_tmp != NULL; ) {
+ mp = ( monitor_entry_t * )e_tmp->e_private;
+ e = e_tmp;
+ e_tmp = mp->mp_next;
+ monitor_cache_lock( e );
+ monitor_cache_release( mi, e );
+
+ if ( e_tmp == e_nonvolatile ) {
+ break;
+ }
+ }
+ }
+
+ return( rc );
+ }
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int
+monitor_back_search( Operation *op, SlapReply *rs )
+{
+ monitor_info_t *mi = ( monitor_info_t * )op->o_bd->be_private;
+ int rc = LDAP_SUCCESS;
+ Entry *e = NULL, *matched = NULL;
+ Entry *e_nv = NULL, *e_ch = NULL;
+ slap_mask_t mask;
+
+ Debug( LDAP_DEBUG_TRACE, "=> monitor_back_search\n" );
+
+
+ /* get entry with reader lock */
+ monitor_cache_dn2entry( op, rs, &op->o_req_ndn, &e, &matched );
+ if ( e == NULL ) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ if ( matched ) {
+ if ( !access_allowed_mask( op, matched,
+ slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL, NULL ) )
+ {
+ /* do nothing */ ;
+ } else {
+ rs->sr_matched = matched->e_dn;
+ }
+ }
+
+ send_ldap_result( op, rs );
+ if ( matched ) {
+ monitor_cache_release( mi, matched );
+ rs->sr_matched = NULL;
+ }
+
+ return rs->sr_err;
+ }
+
+ /* NOTE: __NEW__ "search" access is required
+ * on searchBase object */
+ if ( !access_allowed_mask( op, e, slap_schema.si_ad_entry,
+ NULL, ACL_SEARCH, NULL, &mask ) )
+ {
+ monitor_cache_release( mi, e );
+
+ if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ } else {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ }
+
+ send_ldap_result( op, rs );
+
+ return rs->sr_err;
+ }
+
+ rs->sr_attrs = op->oq_search.rs_attrs;
+ switch ( op->oq_search.rs_scope ) {
+ case LDAP_SCOPE_BASE:
+ monitor_entry_update( op, rs, e );
+ rc = test_filter( op, e, op->oq_search.rs_filter );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rs->sr_entry = e;
+ rs->sr_flags = REP_ENTRY_MUSTRELEASE;
+ send_search_entry( op, rs );
+ rs->sr_entry = NULL;
+ } else {
+ monitor_cache_release( mi, e );
+ }
+ rc = LDAP_SUCCESS;
+ break;
+
+ case LDAP_SCOPE_ONELEVEL:
+ case LDAP_SCOPE_SUBORDINATE:
+ monitor_find_children( op, rs, e, &e_nv, &e_ch );
+ monitor_cache_release( mi, e );
+ rc = monitor_send_children( op, rs, e_nv, e_ch,
+ op->oq_search.rs_scope == LDAP_SCOPE_SUBORDINATE );
+ break;
+
+ case LDAP_SCOPE_SUBTREE:
+ monitor_entry_update( op, rs, e );
+ monitor_find_children( op, rs, e, &e_nv, &e_ch );
+ rc = test_filter( op, e, op->oq_search.rs_filter );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rs->sr_entry = e;
+ rs->sr_flags = REP_ENTRY_MUSTRELEASE;
+ send_search_entry( op, rs );
+ rs->sr_entry = NULL;
+ } else {
+ monitor_cache_release( mi, e );
+ }
+
+ rc = monitor_send_children( op, rs, e_nv, e_ch, 1 );
+ break;
+
+ default:
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ monitor_cache_release( mi, e );
+ }
+
+ rs->sr_attrs = NULL;
+ rs->sr_err = rc;
+ if ( rs->sr_err != SLAPD_ABANDON ) {
+ send_ldap_result( op, rs );
+ }
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/back-monitor/sent.c b/servers/slapd/back-monitor/sent.c
new file mode 100644
index 0000000..5db0479
--- /dev/null
+++ b/servers/slapd/back-monitor/sent.c
@@ -0,0 +1,241 @@
+/* sent.c - deal with data sent subsystem */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-monitor.h"
+
+static int
+monitor_subsys_sent_destroy(
+ BackendDB *be,
+ monitor_subsys_t *ms );
+
+static int
+monitor_subsys_sent_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e );
+
+enum {
+ MONITOR_SENT_BYTES = 0,
+ MONITOR_SENT_PDU,
+ MONITOR_SENT_ENTRIES,
+ MONITOR_SENT_REFERRALS,
+
+ MONITOR_SENT_LAST
+};
+
+struct monitor_sent_t {
+ struct berval rdn;
+ struct berval nrdn;
+} monitor_sent[] = {
+ { BER_BVC("cn=Bytes"), BER_BVNULL },
+ { BER_BVC("cn=PDU"), BER_BVNULL },
+ { BER_BVC("cn=Entries"), BER_BVNULL },
+ { BER_BVC("cn=Referrals"), BER_BVNULL },
+ { BER_BVNULL, BER_BVNULL }
+};
+
+int
+monitor_subsys_sent_init(
+ BackendDB *be,
+ monitor_subsys_t *ms )
+{
+ monitor_info_t *mi;
+
+ Entry **ep, *e_sent;
+ monitor_entry_t *mp;
+ int i;
+
+ assert( be != NULL );
+
+ ms->mss_destroy = monitor_subsys_sent_destroy;
+ ms->mss_update = monitor_subsys_sent_update;
+
+ mi = ( monitor_info_t * )be->be_private;
+
+ if ( monitor_cache_get( mi, &ms->mss_ndn, &e_sent ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_sent_init: "
+ "unable to get entry \"%s\"\n",
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ mp = ( monitor_entry_t * )e_sent->e_private;
+ mp->mp_children = NULL;
+ ep = &mp->mp_children;
+
+ for ( i = 0; i < MONITOR_SENT_LAST; i++ ) {
+ struct berval nrdn, bv;
+ Entry *e;
+
+ e = monitor_entry_stub( &ms->mss_dn, &ms->mss_ndn,
+ &monitor_sent[i].rdn, mi->mi_oc_monitorCounterObject,
+ NULL, NULL );
+
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_sent_init: "
+ "unable to create entry \"%s,%s\"\n",
+ monitor_sent[ i ].rdn.bv_val,
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ /* steal normalized RDN */
+ dnRdn( &e->e_nname, &nrdn );
+ ber_dupbv( &monitor_sent[ i ].nrdn, &nrdn );
+
+ BER_BVSTR( &bv, "0" );
+ attr_merge_one( e, mi->mi_ad_monitorCounter, &bv, NULL );
+
+ mp = monitor_entrypriv_create();
+ if ( mp == NULL ) {
+ return -1;
+ }
+ e->e_private = ( void * )mp;
+ mp->mp_info = ms;
+ mp->mp_flags = ms->mss_flags \
+ | MONITOR_F_SUB | MONITOR_F_PERSISTENT;
+
+ if ( monitor_cache_add( mi, e ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_sent_init: "
+ "unable to add entry \"%s,%s\"\n",
+ monitor_sent[ i ].rdn.bv_val,
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ *ep = e;
+ ep = &mp->mp_next;
+ }
+
+ monitor_cache_release( mi, e_sent );
+
+ return( 0 );
+}
+
+static int
+monitor_subsys_sent_destroy(
+ BackendDB *be,
+ monitor_subsys_t *ms )
+{
+ int i;
+
+ for ( i = 0; i < MONITOR_SENT_LAST; i++ ) {
+ if ( !BER_BVISNULL( &monitor_sent[ i ].nrdn ) ) {
+ ch_free( monitor_sent[ i ].nrdn.bv_val );
+ }
+ }
+
+ return 0;
+}
+
+static int
+monitor_subsys_sent_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e )
+{
+ monitor_info_t *mi = ( monitor_info_t *)op->o_bd->be_private;
+
+ struct berval nrdn;
+ ldap_pvt_mp_t n;
+ Attribute *a;
+ slap_counters_t *sc;
+ int i;
+
+ assert( mi != NULL );
+ assert( e != NULL );
+
+ dnRdn( &e->e_nname, &nrdn );
+
+ for ( i = 0; i < MONITOR_SENT_LAST; i++ ) {
+ if ( dn_match( &nrdn, &monitor_sent[ i ].nrdn ) ) {
+ break;
+ }
+ }
+
+ if ( i == MONITOR_SENT_LAST ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ ldap_pvt_thread_mutex_lock(&slap_counters.sc_mutex);
+ switch ( i ) {
+ case MONITOR_SENT_ENTRIES:
+ ldap_pvt_mp_init_set( n, slap_counters.sc_entries );
+ for ( sc = slap_counters.sc_next; sc; sc = sc->sc_next ) {
+ ldap_pvt_thread_mutex_lock( &sc->sc_mutex );
+ ldap_pvt_mp_add( n, sc->sc_entries );
+ ldap_pvt_thread_mutex_unlock( &sc->sc_mutex );
+ }
+ break;
+
+ case MONITOR_SENT_REFERRALS:
+ ldap_pvt_mp_init_set( n, slap_counters.sc_refs );
+ for ( sc = slap_counters.sc_next; sc; sc = sc->sc_next ) {
+ ldap_pvt_thread_mutex_lock( &sc->sc_mutex );
+ ldap_pvt_mp_add( n, sc->sc_refs );
+ ldap_pvt_thread_mutex_unlock( &sc->sc_mutex );
+ }
+ break;
+
+ case MONITOR_SENT_PDU:
+ ldap_pvt_mp_init_set( n, slap_counters.sc_pdu );
+ for ( sc = slap_counters.sc_next; sc; sc = sc->sc_next ) {
+ ldap_pvt_thread_mutex_lock( &sc->sc_mutex );
+ ldap_pvt_mp_add( n, sc->sc_pdu );
+ ldap_pvt_thread_mutex_unlock( &sc->sc_mutex );
+ }
+ break;
+
+ case MONITOR_SENT_BYTES:
+ ldap_pvt_mp_init_set( n, slap_counters.sc_bytes );
+ for ( sc = slap_counters.sc_next; sc; sc = sc->sc_next ) {
+ ldap_pvt_thread_mutex_lock( &sc->sc_mutex );
+ ldap_pvt_mp_add( n, sc->sc_bytes );
+ ldap_pvt_thread_mutex_unlock( &sc->sc_mutex );
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+ ldap_pvt_thread_mutex_unlock(&slap_counters.sc_mutex);
+
+ a = attr_find( e->e_attrs, mi->mi_ad_monitorCounter );
+ assert( a != NULL );
+
+ /* NOTE: no minus sign is allowed in the counters... */
+ UI2BV( &a->a_vals[ 0 ], n );
+ ldap_pvt_mp_clear( n );
+
+ /* FIXME: touch modifyTimestamp? */
+
+ return SLAP_CB_CONTINUE;
+}
+
diff --git a/servers/slapd/back-monitor/thread.c b/servers/slapd/back-monitor/thread.c
new file mode 100644
index 0000000..2fe13d8
--- /dev/null
+++ b/servers/slapd/back-monitor/thread.c
@@ -0,0 +1,351 @@
+/* thread.c - deal with thread subsystem */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-monitor.h"
+
+#include <ldap_rq.h>
+
+typedef enum {
+ MT_UNKNOWN,
+ MT_RUNQUEUE,
+ MT_TASKLIST,
+
+ MT_LAST
+} monitor_thread_t;
+
+static struct {
+ struct berval rdn;
+ struct berval desc;
+ struct berval nrdn;
+ ldap_pvt_thread_pool_param_t param;
+ monitor_thread_t mt;
+} mt[] = {
+ { BER_BVC( "cn=Max" ),
+ BER_BVC("Maximum number of threads as configured"),
+ BER_BVNULL, LDAP_PVT_THREAD_POOL_PARAM_MAX, MT_UNKNOWN },
+ { BER_BVC( "cn=Max Pending" ),
+ BER_BVC("Maximum number of pending threads"),
+ BER_BVNULL, LDAP_PVT_THREAD_POOL_PARAM_MAX_PENDING, MT_UNKNOWN },
+ { BER_BVC( "cn=Open" ),
+ BER_BVC("Number of open threads"),
+ BER_BVNULL, LDAP_PVT_THREAD_POOL_PARAM_OPEN, MT_UNKNOWN },
+ { BER_BVC( "cn=Starting" ),
+ BER_BVC("Number of threads being started"),
+ BER_BVNULL, LDAP_PVT_THREAD_POOL_PARAM_STARTING, MT_UNKNOWN },
+ { BER_BVC( "cn=Active" ),
+ BER_BVC("Number of active threads"),
+ BER_BVNULL, LDAP_PVT_THREAD_POOL_PARAM_ACTIVE, MT_UNKNOWN },
+ { BER_BVC( "cn=Pending" ),
+ BER_BVC("Number of pending threads"),
+ BER_BVNULL, LDAP_PVT_THREAD_POOL_PARAM_PENDING, MT_UNKNOWN },
+ { BER_BVC( "cn=Backload" ),
+ BER_BVC("Number of active plus pending threads"),
+ BER_BVNULL, LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD, MT_UNKNOWN },
+#if 0 /* not meaningful right now */
+ { BER_BVC( "cn=Active Max" ),
+ BER_BVNULL,
+ BER_BVNULL, LDAP_PVT_THREAD_POOL_PARAM_ACTIVE_MAX, MT_UNKNOWN },
+ { BER_BVC( "cn=Pending Max" ),
+ BER_BVNULL,
+ BER_BVNULL, LDAP_PVT_THREAD_POOL_PARAM_PENDING_MAX, MT_UNKNOWN },
+ { BER_BVC( "cn=Backload Max" ),
+ BER_BVNULL,
+ BER_BVNULL, LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD_MAX,MT_UNKNOWN },
+#endif
+ { BER_BVC( "cn=State" ),
+ BER_BVC("Thread pool state"),
+ BER_BVNULL, LDAP_PVT_THREAD_POOL_PARAM_STATE, MT_UNKNOWN },
+
+ { BER_BVC( "cn=Runqueue" ),
+ BER_BVC("Queue of running threads - besides those handling operations"),
+ BER_BVNULL, LDAP_PVT_THREAD_POOL_PARAM_UNKNOWN, MT_RUNQUEUE },
+ { BER_BVC( "cn=Tasklist" ),
+ BER_BVC("List of running plus standby threads - besides those handling operations"),
+ BER_BVNULL, LDAP_PVT_THREAD_POOL_PARAM_UNKNOWN, MT_TASKLIST },
+
+ { BER_BVNULL }
+};
+
+static int
+monitor_subsys_thread_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e );
+
+/*
+ * initializes log subentry
+ */
+int
+monitor_subsys_thread_init(
+ BackendDB *be,
+ monitor_subsys_t *ms )
+{
+ monitor_info_t *mi;
+ monitor_entry_t *mp;
+ Entry *e, **ep, *e_thread;
+ int i;
+
+ ms->mss_update = monitor_subsys_thread_update;
+
+ mi = ( monitor_info_t * )be->be_private;
+
+ if ( monitor_cache_get( mi, &ms->mss_ndn, &e_thread ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_thread_init: unable to get entry \"%s\"\n",
+ ms->mss_dn.bv_val );
+ return( -1 );
+ }
+
+ mp = ( monitor_entry_t * )e_thread->e_private;
+ mp->mp_children = NULL;
+ ep = &mp->mp_children;
+
+ for ( i = 0; !BER_BVISNULL( &mt[ i ].rdn ); i++ ) {
+ static char buf[ BACKMONITOR_BUFSIZE ];
+ int count = -1;
+ char *state = NULL;
+ struct berval bv = BER_BVNULL;
+
+ /*
+ * Max
+ */
+ e = monitor_entry_stub( &ms->mss_dn, &ms->mss_ndn,
+ &mt[ i ].rdn,
+ mi->mi_oc_monitoredObject, NULL, NULL );
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_thread_init: "
+ "unable to create entry \"%s,%s\"\n",
+ mt[ i ].rdn.bv_val,
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ /* NOTE: reference to the normalized DN of the entry,
+ * under the assumption it's not modified */
+ dnRdn( &e->e_nname, &mt[ i ].nrdn );
+
+ switch ( mt[ i ].param ) {
+ case LDAP_PVT_THREAD_POOL_PARAM_UNKNOWN:
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_STATE:
+ if ( ldap_pvt_thread_pool_query( &connection_pool,
+ mt[ i ].param, (void *)&state ) == 0 )
+ {
+ ber_str2bv( state, 0, 0, &bv );
+
+ } else {
+ BER_BVSTR( &bv, "unknown" );
+ }
+ break;
+
+ default:
+ /* NOTE: in case of error, it'll be set to -1 */
+ (void)ldap_pvt_thread_pool_query( &connection_pool,
+ mt[ i ].param, (void *)&count );
+ bv.bv_val = buf;
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%d", count );
+ break;
+ }
+
+ if ( !BER_BVISNULL( &bv ) ) {
+ attr_merge_normalize_one( e, mi->mi_ad_monitoredInfo, &bv, NULL );
+ }
+
+ if ( !BER_BVISNULL( &mt[ i ].desc ) ) {
+ attr_merge_normalize_one( e,
+ slap_schema.si_ad_description,
+ &mt[ i ].desc, NULL );
+ }
+
+ mp = monitor_entrypriv_create();
+ if ( mp == NULL ) {
+ return -1;
+ }
+ e->e_private = ( void * )mp;
+ mp->mp_info = ms;
+ mp->mp_flags = ms->mss_flags \
+ | MONITOR_F_SUB | MONITOR_F_PERSISTENT;
+
+ if ( monitor_cache_add( mi, e ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_thread_init: "
+ "unable to add entry \"%s,%s\"\n",
+ mt[ i ].rdn.bv_val,
+ ms->mss_dn.bv_val );
+ return( -1 );
+ }
+
+ *ep = e;
+ ep = &mp->mp_next;
+ }
+
+ monitor_cache_release( mi, e_thread );
+
+ return( 0 );
+}
+
+static int
+monitor_subsys_thread_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e )
+{
+ monitor_info_t *mi = ( monitor_info_t * )op->o_bd->be_private;
+ Attribute *a;
+ BerVarray vals = NULL;
+ char buf[ BACKMONITOR_BUFSIZE ];
+ struct berval rdn, bv;
+ int which, i;
+ struct re_s *re;
+ int count = -1;
+ char *state = NULL;
+
+ assert( mi != NULL );
+
+ dnRdn( &e->e_nname, &rdn );
+
+ for ( i = 0; !BER_BVISNULL( &mt[ i ].nrdn ); i++ ) {
+ if ( dn_match( &mt[ i ].nrdn, &rdn ) ) {
+ break;
+ }
+ }
+
+ which = i;
+ if ( BER_BVISNULL( &mt[ which ].nrdn ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ a = attr_find( e->e_attrs, mi->mi_ad_monitoredInfo );
+
+ switch ( mt[ which ].param ) {
+ case LDAP_PVT_THREAD_POOL_PARAM_UNKNOWN:
+ switch ( mt[ which ].mt ) {
+ case MT_RUNQUEUE:
+ if ( a != NULL ) {
+ if ( a->a_nvals != a->a_vals ) {
+ ber_bvarray_free( a->a_nvals );
+ }
+ ber_bvarray_free( a->a_vals );
+ a->a_vals = NULL;
+ a->a_nvals = NULL;
+ a->a_numvals = 0;
+ }
+
+ i = 0;
+ bv.bv_val = buf;
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ LDAP_STAILQ_FOREACH( re, &slapd_rq.run_list, rnext ) {
+ bv.bv_len = snprintf( buf, sizeof( buf ), "{%d}%s(%s)",
+ i, re->tname, re->tspec );
+ if ( bv.bv_len < sizeof( buf ) ) {
+ value_add_one( &vals, &bv );
+ }
+ i++;
+ }
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+
+ if ( vals ) {
+ attr_merge_normalize( e, mi->mi_ad_monitoredInfo, vals, NULL );
+ ber_bvarray_free( vals );
+
+ } else {
+ attr_delete( &e->e_attrs, mi->mi_ad_monitoredInfo );
+ }
+ break;
+
+ case MT_TASKLIST:
+ if ( a != NULL ) {
+ if ( a->a_nvals != a->a_vals ) {
+ ber_bvarray_free( a->a_nvals );
+ }
+ ber_bvarray_free( a->a_vals );
+ a->a_vals = NULL;
+ a->a_nvals = NULL;
+ a->a_numvals = 0;
+ }
+
+ i = 0;
+ bv.bv_val = buf;
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ LDAP_STAILQ_FOREACH( re, &slapd_rq.task_list, tnext ) {
+ bv.bv_len = snprintf( buf, sizeof( buf ), "{%d}%s(%s)",
+ i, re->tname, re->tspec );
+ if ( bv.bv_len < sizeof( buf ) ) {
+ value_add_one( &vals, &bv );
+ }
+ i++;
+ }
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+
+ if ( vals ) {
+ attr_merge_normalize( e, mi->mi_ad_monitoredInfo, vals, NULL );
+ ber_bvarray_free( vals );
+
+ } else {
+ attr_delete( &e->e_attrs, mi->mi_ad_monitoredInfo );
+ }
+ break;
+
+ default:
+ assert( 0 );
+ }
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_STATE:
+ if ( a == NULL ) {
+ return rs->sr_err = LDAP_OTHER;
+ }
+ if ( ldap_pvt_thread_pool_query( &connection_pool,
+ mt[ i ].param, (void *)&state ) == 0 )
+ {
+ ber_str2bv( state, 0, 0, &bv );
+ ber_bvreplace( &a->a_vals[ 0 ], &bv );
+ }
+ break;
+
+ default:
+ if ( a == NULL ) {
+ return rs->sr_err = LDAP_OTHER;
+ }
+ if ( ldap_pvt_thread_pool_query( &connection_pool,
+ mt[ i ].param, (void *)&count ) == 0 )
+ {
+ bv.bv_val = buf;
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%d", count );
+ if ( bv.bv_len < sizeof( buf ) ) {
+ ber_bvreplace( &a->a_vals[ 0 ], &bv );
+ }
+ }
+ break;
+ }
+
+ /* FIXME: touch modifyTimestamp? */
+
+ return SLAP_CB_CONTINUE;
+}
diff --git a/servers/slapd/back-monitor/time.c b/servers/slapd/back-monitor/time.c
new file mode 100644
index 0000000..e0ea7c6
--- /dev/null
+++ b/servers/slapd/back-monitor/time.c
@@ -0,0 +1,247 @@
+/* time.c - deal with time subsystem */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2001-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+
+#include "slap.h"
+#include <lutil.h>
+#include "proto-slap.h"
+#include "back-monitor.h"
+
+static int
+monitor_subsys_time_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e );
+
+int
+monitor_subsys_time_init(
+ BackendDB *be,
+ monitor_subsys_t *ms )
+{
+ monitor_info_t *mi;
+
+ Entry *e, **ep, *e_time;
+ monitor_entry_t *mp;
+ struct berval bv, value;
+
+ assert( be != NULL );
+
+ ms->mss_update = monitor_subsys_time_update;
+
+ mi = ( monitor_info_t * )be->be_private;
+
+ if ( monitor_cache_get( mi,
+ &ms->mss_ndn, &e_time ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_time_init: "
+ "unable to get entry \"%s\"\n",
+ ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ mp = ( monitor_entry_t * )e_time->e_private;
+ mp->mp_children = NULL;
+ ep = &mp->mp_children;
+
+ BER_BVSTR( &bv, "cn=Start" );
+ e = monitor_entry_stub( &ms->mss_dn, &ms->mss_ndn, &bv,
+ mi->mi_oc_monitoredObject, NULL, NULL );
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_time_init: "
+ "unable to create entry \"%s,%s\"\n",
+ bv.bv_val, ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+ attr_merge_normalize_one( e, mi->mi_ad_monitorTimestamp,
+ &mi->mi_startTime, NULL );
+
+ mp = monitor_entrypriv_create();
+ if ( mp == NULL ) {
+ return -1;
+ }
+ e->e_private = ( void * )mp;
+ mp->mp_info = ms;
+ mp->mp_flags = ms->mss_flags \
+ | MONITOR_F_SUB | MONITOR_F_PERSISTENT;
+
+ if ( monitor_cache_add( mi, e ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_time_init: "
+ "unable to add entry \"%s,%s\"\n",
+ bv.bv_val, ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ *ep = e;
+ ep = &mp->mp_next;
+
+ /*
+ * Current
+ */
+ BER_BVSTR( &bv, "cn=Current" );
+ e = monitor_entry_stub( &ms->mss_dn, &ms->mss_ndn, &bv,
+ mi->mi_oc_monitoredObject, NULL, NULL );
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_time_init: "
+ "unable to create entry \"%s,%s\"\n",
+ bv.bv_val, ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+ attr_merge_normalize_one( e, mi->mi_ad_monitorTimestamp,
+ &mi->mi_startTime, NULL );
+
+ mp = monitor_entrypriv_create();
+ if ( mp == NULL ) {
+ return -1;
+ }
+ e->e_private = ( void * )mp;
+ mp->mp_info = ms;
+ mp->mp_flags = ms->mss_flags \
+ | MONITOR_F_SUB | MONITOR_F_PERSISTENT;
+
+ if ( monitor_cache_add( mi, e ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_time_init: "
+ "unable to add entry \"%s,%s\"\n",
+ bv.bv_val, ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ *ep = e;
+ ep = &mp->mp_next;
+
+ /*
+ * Uptime
+ */
+ BER_BVSTR( &bv, "cn=Uptime" );
+ e = monitor_entry_stub( &ms->mss_dn, &ms->mss_ndn, &bv,
+ mi->mi_oc_monitoredObject, NULL, NULL );
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_time_init: "
+ "unable to create entry \"%s,%s\"\n",
+ bv.bv_val, ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+ BER_BVSTR( &value, "0" );
+ attr_merge_normalize_one( e, mi->mi_ad_monitoredInfo,
+ &value, NULL );
+
+ mp = monitor_entrypriv_create();
+ if ( mp == NULL ) {
+ return -1;
+ }
+ e->e_private = ( void * )mp;
+ mp->mp_info = ms;
+ mp->mp_flags = ms->mss_flags \
+ | MONITOR_F_SUB | MONITOR_F_PERSISTENT;
+
+ if ( monitor_cache_add( mi, e ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "monitor_subsys_time_init: "
+ "unable to add entry \"%s,%s\"\n",
+ bv.bv_val, ms->mss_ndn.bv_val );
+ return( -1 );
+ }
+
+ *ep = e;
+ ep = &mp->mp_next;
+
+ monitor_cache_release( mi, e_time );
+
+ return( 0 );
+}
+
+static int
+monitor_subsys_time_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e )
+{
+ monitor_info_t *mi = ( monitor_info_t * )op->o_bd->be_private;
+ static struct berval bv_current = BER_BVC( "cn=current" ),
+ bv_uptime = BER_BVC( "cn=uptime" );
+ struct berval rdn;
+
+ assert( mi != NULL );
+ assert( e != NULL );
+
+ dnRdn( &e->e_nname, &rdn );
+
+ if ( dn_match( &rdn, &bv_current ) ) {
+ struct tm tm;
+ char tmbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+ Attribute *a;
+ ber_len_t len;
+ time_t currtime;
+
+ currtime = slap_get_time();
+
+ ldap_pvt_gmtime( &currtime, &tm );
+ lutil_gentime( tmbuf, sizeof( tmbuf ), &tm );
+
+ len = strlen( tmbuf );
+
+ a = attr_find( e->e_attrs, mi->mi_ad_monitorTimestamp );
+ if ( a == NULL ) {
+ return rs->sr_err = LDAP_OTHER;
+ }
+
+ assert( len == a->a_vals[ 0 ].bv_len );
+ AC_MEMCPY( a->a_vals[ 0 ].bv_val, tmbuf, len );
+
+ /* FIXME: touch modifyTimestamp? */
+
+ } else if ( dn_match( &rdn, &bv_uptime ) ) {
+ Attribute *a;
+ double diff;
+ char buf[ BACKMONITOR_BUFSIZE ];
+ struct berval bv;
+
+ a = attr_find( e->e_attrs, mi->mi_ad_monitoredInfo );
+ if ( a == NULL ) {
+ return rs->sr_err = LDAP_OTHER;
+ }
+
+ diff = difftime( slap_get_time(), starttime );
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%lu",
+ (unsigned long) diff );
+ bv.bv_val = buf;
+
+ ber_bvreplace( &a->a_vals[ 0 ], &bv );
+ if ( a->a_nvals != a->a_vals ) {
+ ber_bvreplace( &a->a_nvals[ 0 ], &bv );
+ }
+
+ /* FIXME: touch modifyTimestamp? */
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
diff --git a/servers/slapd/back-ndb/Makefile.in b/servers/slapd/back-ndb/Makefile.in
new file mode 100644
index 0000000..4df6898
--- /dev/null
+++ b/servers/slapd/back-ndb/Makefile.in
@@ -0,0 +1,59 @@
+# Makefile.in for back-ndb
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2008-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This work was initially developed by Howard Chu for inclusion
+## in OpenLDAP Software. This work was sponsored by MySQL.
+
+SRCS = init.cpp tools.cpp config.cpp ndbio.cpp \
+ add.cpp bind.cpp compare.cpp delete.cpp modify.cpp modrdn.cpp search.cpp
+
+OBJS = init.lo tools.lo config.lo ndbio.lo \
+ add.lo bind.lo compare.lo delete.lo modify.lo modrdn.lo search.lo
+
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-ndb"
+BUILD_MOD = @BUILD_NDB@
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = $(@BUILD_NDB@_DEFS)
+MOD_LIBS = $(SLAPD_NDB_LIBS)
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+
+LIBBASE = back_ndb
+
+XINCPATH = -I.. -I$(srcdir)/.. @SLAPD_NDB_INCS@
+XDEFS = $(MODULES_CPPFLAGS)
+
+AC_CXX = g++
+CXX = $(AC_CXX)
+LTCXX_MOD = $(LIBTOOL) $(LTONLY_MOD) --mode=compile \
+ $(CXX) $(LT_CFLAGS) $(LT_CPPFLAGS) $(MOD_DEFS) -c
+
+all-local-lib: ../.backend
+
+.SUFFIXES: .c .o .lo .cpp
+
+.cpp.lo:
+ $(LTCXX_MOD) $<
+
+../.backend: lib$(LIBBASE).a
+ @touch $@
+
diff --git a/servers/slapd/back-ndb/TODO b/servers/slapd/back-ndb/TODO
new file mode 100644
index 0000000..0393954
--- /dev/null
+++ b/servers/slapd/back-ndb/TODO
@@ -0,0 +1,6 @@
+LDAP features not currently supported:
+
+tagged attributes
+aliases
+substring indexing
+subtree rename
diff --git a/servers/slapd/back-ndb/add.cpp b/servers/slapd/back-ndb/add.cpp
new file mode 100644
index 0000000..f89cad4
--- /dev/null
+++ b/servers/slapd/back-ndb/add.cpp
@@ -0,0 +1,347 @@
+/* add.cpp - ldap NDB back-end add routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-ndb.h"
+
+extern "C" int
+ndb_back_add(Operation *op, SlapReply *rs )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ Entry p = {0};
+ Attribute poc;
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof textbuf;
+ AttributeDescription *children = slap_schema.si_ad_children;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+ NdbArgs NA;
+ NdbRdns rdns;
+ struct berval matched;
+ struct berval pdn, pndn;
+
+ int num_retries = 0;
+ int success;
+
+ LDAPControl **postread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+
+ Debug(LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(ndb_back_add) ": %s\n",
+ op->oq_add.rs_e->e_name.bv_val, 0, 0);
+
+ ctrls[num_ctrls] = 0;
+ NA.txn = NULL;
+
+ /* check entry's schema */
+ rs->sr_err = entry_schema_check( op, op->oq_add.rs_e, NULL,
+ get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": entry failed schema check: "
+ "%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
+ goto return_results;
+ }
+
+ /* add opattrs to shadow as well, only missing attrs will actually
+ * be added; helps compatibility with older OL versions */
+ rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": entry failed op attrs add: "
+ "%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
+ goto return_results;
+ }
+
+ /* Get our NDB handle */
+ rs->sr_err = ndb_thread_handle( op, &NA.ndb );
+
+ /*
+ * Get the parent dn and see if the corresponding entry exists.
+ */
+ if ( be_issuffix( op->o_bd, &op->oq_add.rs_e->e_nname ) ) {
+ pdn = slap_empty_bv;
+ pndn = slap_empty_bv;
+ } else {
+ dnParent( &op->ora_e->e_name, &pdn );
+ dnParent( &op->ora_e->e_nname, &pndn );
+ }
+ p.e_name = op->ora_e->e_name;
+ p.e_nname = op->ora_e->e_nname;
+
+ op->ora_e->e_id = NOID;
+ rdns.nr_num = 0;
+ NA.rdns = &rdns;
+
+ if( 0 ) {
+retry: /* transaction retry */
+ NA.txn->close();
+ NA.txn = NULL;
+ if ( op->o_abandon ) {
+ rs->sr_err = SLAPD_ABANDON;
+ goto return_results;
+ }
+ ndb_trans_backoff( ++num_retries );
+ }
+
+ NA.txn = NA.ndb->startTransaction();
+ rs->sr_text = NULL;
+ if( !NA.txn ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": startTransaction failed: %s (%d)\n",
+ NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* get entry or parent */
+ NA.e = &p;
+ NA.ocs = NULL;
+ rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched );
+ switch( rs->sr_err ) {
+ case 0:
+ rs->sr_err = LDAP_ALREADY_EXISTS;
+ goto return_results;
+ case LDAP_NO_SUCH_OBJECT:
+ break;
+#if 0
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto retry;
+#endif
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ if ( NA.ocs ) {
+ int i;
+ for ( i=0; !BER_BVISNULL( &NA.ocs[i] ); i++ );
+ poc.a_numvals = i;
+ poc.a_desc = slap_schema.si_ad_objectClass;
+ poc.a_vals = NA.ocs;
+ poc.a_nvals = poc.a_vals;
+ poc.a_next = NULL;
+ p.e_attrs = &poc;
+ }
+
+ if ( ber_bvstrcasecmp( &pndn, &matched ) ) {
+ rs->sr_matched = matched.bv_val;
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": parent "
+ "does not exist\n", 0, 0, 0 );
+
+ rs->sr_text = "parent does not exist";
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ if ( p.e_attrs && is_entry_referral( &p )) {
+is_ref: p.e_attrs = NULL;
+ ndb_entry_get_data( op, &NA, 0 );
+ rs->sr_ref = get_entry_referrals( op, &p );
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_flags = REP_REF_MUSTBEFREED;
+ attrs_free( p.e_attrs );
+ p.e_attrs = NULL;
+ }
+ goto return_results;
+ }
+
+ p.e_name = pdn;
+ p.e_nname = pndn;
+ rs->sr_err = access_allowed( op, &p,
+ children, NULL, ACL_WADD, NULL );
+
+ if ( ! rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": no write access to parent\n",
+ 0, 0, 0 );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to parent";
+ goto return_results;
+ }
+
+ if ( NA.ocs ) {
+ if ( is_entry_subentry( &p )) {
+ /* parent is a subentry, don't allow add */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": parent is subentry\n",
+ 0, 0, 0 );
+ rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
+ rs->sr_text = "parent is a subentry";
+ goto return_results;
+ }
+
+ if ( is_entry_alias( &p ) ) {
+ /* parent is an alias, don't allow add */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": parent is alias\n",
+ 0, 0, 0 );
+ rs->sr_err = LDAP_ALIAS_PROBLEM;
+ rs->sr_text = "parent is an alias";
+ goto return_results;
+ }
+
+ if ( is_entry_referral( &p ) ) {
+ /* parent is a referral, don't allow add */
+ rs->sr_matched = p.e_name.bv_val;
+ goto is_ref;
+ }
+ }
+
+ rs->sr_err = access_allowed( op, op->ora_e,
+ entry, NULL, ACL_WADD, NULL );
+
+ if ( ! rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": no write access to entry\n",
+ 0, 0, 0 );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to entry";
+ goto return_results;;
+ }
+
+ /*
+ * Check ACL for attribute write access
+ */
+ if (!acl_check_modlist(op, op->ora_e, op->ora_modlist)) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(bdb_add) ": no write access to attribute\n",
+ 0, 0, 0 );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to attribute";
+ goto return_results;;
+ }
+
+
+ /* acquire entry ID */
+ if ( op->ora_e->e_id == NOID ) {
+ rs->sr_err = ndb_next_id( op->o_bd, NA.ndb, &op->ora_e->e_id );
+ if( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": next_id failed (%d)\n",
+ rs->sr_err, 0, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ }
+
+ if ( matched.bv_val )
+ rdns.nr_num++;
+ NA.e = op->ora_e;
+ /* dn2id index */
+ rs->sr_err = ndb_entry_put_info( op->o_bd, &NA, 0 );
+ if ( rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": ndb_entry_put_info failed (%d)\n",
+ rs->sr_err, 0, 0 );
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* id2entry index */
+ rs->sr_err = ndb_entry_put_data( op->o_bd, &NA );
+ if ( rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": ndb_entry_put_data failed (%d) %s(%d)\n",
+ rs->sr_err, NA.txn->getNdbError().message, NA.txn->getNdbError().code );
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* post-read */
+ if( op->o_postread ) {
+ if( postread_ctrl == NULL ) {
+ postread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if ( slap_read_controls( op, rs, op->oq_add.rs_e,
+ &slap_post_read_bv, postread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_add) ": post-read "
+ "failed!\n", 0, 0, 0 );
+ if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ if ( op->o_noop ) {
+ if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback,
+ NdbOperation::AbortOnError, 1 )) != 0 ) {
+ rs->sr_text = "txn (no-op) failed";
+ } else {
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ }
+
+ } else {
+ if(( rs->sr_err=NA.txn->execute( NdbTransaction::Commit,
+ NdbOperation::AbortOnError, 1 )) != 0 ) {
+ rs->sr_text = "txn_commit failed";
+ } else {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+ }
+
+ if ( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": %s : %s (%d)\n",
+ rs->sr_text, NA.txn->getNdbError().message, NA.txn->getNdbError().code );
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+ NA.txn->close();
+ NA.txn = NULL;
+
+ Debug(LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": added%s id=%08lx dn=\"%s\"\n",
+ op->o_noop ? " (no-op)" : "",
+ op->oq_add.rs_e->e_id, op->oq_add.rs_e->e_dn );
+
+ rs->sr_text = NULL;
+ if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+ success = rs->sr_err;
+ send_ldap_result( op, rs );
+ slap_graduate_commit_csn( op );
+
+ if( NA.txn != NULL ) {
+ NA.txn->execute( Rollback );
+ NA.txn->close();
+ }
+
+ if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
+ slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
+ }
+
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-ndb/attrsets.conf b/servers/slapd/back-ndb/attrsets.conf
new file mode 100644
index 0000000..f135e0a
--- /dev/null
+++ b/servers/slapd/back-ndb/attrsets.conf
@@ -0,0 +1,36 @@
+# Definition of useful attribute sets
+# from X.521 section 5
+#
+# TelecommunicationAttributeSet ATTRIBUTE ::= {
+# facsimileTelephoneNumber |
+# internationalISDNNumber |
+# telephoneNumber |
+# teletexTerminalIdentifier |
+# telexNumber |
+# preferredDeliveryMethod |
+# destinationIndicator |
+# registeredAddress |
+# x121Address }
+#
+# PostalAttributeSet ATTRIBUTE ::= {
+# physicalDeliveryOfficeName |
+# postalAddress |
+# postalCode |
+# postOfficeBox |
+# streetAddress }
+#
+# LocaleAttributeSet ATTRIBUTE ::= {
+# localityName |
+# stateOrProvinceName |
+# streetAddress }
+#
+# OrganizationalAttributeSet ATTRIBUTE ::= {
+# description |
+# LocaleAttributeSet |
+# PostalAttributeSet |
+# TelecommunicationAttributeSet |
+# businessCategory |
+# seeAlso |
+# searchGuide |
+# userPassword }
+
diff --git a/servers/slapd/back-ndb/back-ndb.h b/servers/slapd/back-ndb/back-ndb.h
new file mode 100644
index 0000000..c92c3f1
--- /dev/null
+++ b/servers/slapd/back-ndb/back-ndb.h
@@ -0,0 +1,168 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#ifndef SLAPD_NDB_H
+#define SLAPD_NDB_H
+
+#include "slap.h"
+
+#include <mysql.h>
+#include <NdbApi.hpp>
+
+LDAP_BEGIN_DECL
+
+/* The general design is to use one relational table per objectclass. This is
+ * complicated by objectclass inheritance and auxiliary classes though.
+ *
+ * Attributes must only occur in a single table. For objectclasses that inherit
+ * from other classes, attributes defined in the superior class are only stored
+ * in the superior class' table. When multiple unrelated classes define the same
+ * attributes, an attributeSet should be defined instead, containing all of the
+ * common attributes.
+ *
+ * The no_set table lists which other attributeSets apply to the current
+ * objectClass. The no_attrs table lists all of the non-inherited attributes of
+ * the class, including those residing in an attributeSet.
+ *
+ * Usually the table is named identically to the objectClass, but it can also
+ * be explicitly named something else if needed.
+ */
+#define NDB_MAX_OCSETS 8
+
+struct ndb_attrinfo;
+
+typedef struct ndb_ocinfo {
+ struct berval no_name; /* objectclass cname */
+ struct berval no_table;
+ ObjectClass *no_oc;
+ struct ndb_ocinfo *no_sets[NDB_MAX_OCSETS];
+ struct ndb_attrinfo **no_attrs;
+ int no_flag;
+ int no_nsets;
+ int no_nattrs;
+} NdbOcInfo;
+
+#define NDB_INFO_ATLEN 0x01
+#define NDB_INFO_ATSET 0x02
+#define NDB_INFO_INDEX 0x04
+#define NDB_INFO_ATBLOB 0x08
+
+typedef struct ndb_attrinfo {
+ struct berval na_name; /* attribute cname */
+ AttributeDescription *na_desc;
+ AttributeType *na_attr;
+ NdbOcInfo *na_oi;
+ int na_flag;
+ int na_len;
+ int na_column;
+ int na_ixcol;
+} NdbAttrInfo;
+
+typedef struct ListNode {
+ struct ListNode *ln_next;
+ void *ln_data;
+} ListNode;
+
+#define NDB_IS_OPEN(ni) (ni->ni_cluster != NULL)
+
+struct ndb_info {
+ /* NDB connection */
+ char *ni_connectstr;
+ char *ni_dbname;
+ Ndb_cluster_connection **ni_cluster;
+
+ /* MySQL connection parameters */
+ MYSQL ni_sql;
+ char *ni_hostname;
+ char *ni_username;
+ char *ni_password;
+ char *ni_socket;
+ unsigned long ni_clflag;
+ unsigned int ni_port;
+
+ /* Search filter processing */
+ int ni_search_stack_depth;
+ void *ni_search_stack;
+
+#define DEFAULT_SEARCH_STACK_DEPTH 16
+#define MINIMUM_SEARCH_STACK_DEPTH 8
+
+ /* Schema config */
+ NdbOcInfo *ni_opattrs;
+ ListNode *ni_attridxs;
+ ListNode *ni_attrlens;
+ ListNode *ni_attrsets;
+ ListNode *ni_attrblobs;
+ ldap_pvt_thread_rdwr_t ni_ai_rwlock;
+ Avlnode *ni_ai_tree;
+ ldap_pvt_thread_rdwr_t ni_oc_rwlock;
+ Avlnode *ni_oc_tree;
+ int ni_nconns; /* number of connections to open */
+ int ni_nextconn; /* next conn to use */
+ ldap_pvt_thread_mutex_t ni_conn_mutex;
+};
+
+#define NDB_MAX_RDNS 16
+#define NDB_RDN_LEN 128
+#define NDB_MAX_OCS 64
+
+#define DN2ID_TABLE "OL_dn2id"
+#define EID_COLUMN 0U
+#define VID_COLUMN 1U
+#define OCS_COLUMN 1U
+#define RDN_COLUMN 2U
+#define IDX_COLUMN (2U+NDB_MAX_RDNS)
+
+#define NEXTID_TABLE "OL_nextid"
+
+#define NDB_OC_BUFLEN 1026 /* 1024 data plus 2 len bytes */
+
+#define INDEX_NAME "OL_index"
+
+typedef struct NdbRdns {
+ short nr_num;
+ char nr_buf[NDB_MAX_RDNS][NDB_RDN_LEN+1];
+} NdbRdns;
+
+typedef struct NdbOcs {
+ int no_ninfo;
+ int no_ntext;
+ int no_nitext; /* number of implicit classes */
+ NdbOcInfo *no_info[NDB_MAX_OCS];
+ struct berval no_text[NDB_MAX_OCS];
+ struct berval no_itext[NDB_MAX_OCS]; /* implicit classes */
+} NdbOcs;
+
+typedef struct NdbArgs {
+ Ndb *ndb;
+ NdbTransaction *txn;
+ Entry *e;
+ NdbRdns *rdns;
+ struct berval *ocs;
+ int erdns;
+} NdbArgs;
+
+#define NDB_NO_SUCH_OBJECT 626
+#define NDB_ALREADY_EXISTS 630
+
+LDAP_END_DECL
+
+#include "proto-ndb.h"
+
+#endif
diff --git a/servers/slapd/back-ndb/bind.cpp b/servers/slapd/back-ndb/bind.cpp
new file mode 100644
index 0000000..f8d2df7
--- /dev/null
+++ b/servers/slapd/back-ndb/bind.cpp
@@ -0,0 +1,165 @@
+/* bind.cpp - ndb backend bind routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include "back-ndb.h"
+
+extern "C" int
+ndb_back_bind( Operation *op, SlapReply *rs )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ Entry e = {0};
+ Attribute *a;
+
+ AttributeDescription *password = slap_schema.si_ad_userPassword;
+
+ NdbArgs NA;
+
+ Debug( LDAP_DEBUG_ARGS,
+ "==> " LDAP_XSTRING(ndb_back_bind) ": dn: %s\n",
+ op->o_req_dn.bv_val, 0, 0);
+
+ /* allow noauth binds */
+ switch ( be_rootdn_bind( op, NULL ) ) {
+ case LDAP_SUCCESS:
+ /* frontend will send result */
+ return rs->sr_err = LDAP_SUCCESS;
+
+ default:
+ /* give the database a chance */
+ break;
+ }
+
+ /* Get our NDB handle */
+ rs->sr_err = ndb_thread_handle( op, &NA.ndb );
+
+ e.e_name = op->o_req_dn;
+ e.e_nname = op->o_req_ndn;
+ NA.e = &e;
+
+dn2entry_retry:
+ NA.txn = NA.ndb->startTransaction();
+ rs->sr_text = NULL;
+ if( !NA.txn ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_bind) ": startTransaction failed: %s (%d)\n",
+ NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto done;
+ }
+
+ /* get entry */
+ {
+ NdbRdns rdns;
+ rdns.nr_num = 0;
+ NA.rdns = &rdns;
+ NA.ocs = NULL;
+ rs->sr_err = ndb_entry_get_info( op, &NA, 0, NULL );
+ }
+ switch(rs->sr_err) {
+ case 0:
+ break;
+ case LDAP_NO_SUCH_OBJECT:
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap_server_busy";
+ goto done;
+#if 0
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto dn2entry_retry;
+#endif
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto done;
+ }
+
+ rs->sr_err = ndb_entry_get_data( op, &NA, 0 );
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+ ber_dupbv( &op->oq_bind.rb_edn, &e.e_name );
+
+ /* check for deleted */
+ if ( is_entry_subentry( &e ) ) {
+ /* entry is an subentry, don't allow bind */
+ Debug( LDAP_DEBUG_TRACE, "entry is subentry\n", 0,
+ 0, 0 );
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ if ( is_entry_alias( &e ) ) {
+ /* entry is an alias, don't allow bind */
+ Debug( LDAP_DEBUG_TRACE, "entry is alias\n", 0, 0, 0 );
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ if ( is_entry_referral( &e ) ) {
+ Debug( LDAP_DEBUG_TRACE, "entry is referral\n", 0,
+ 0, 0 );
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ switch ( op->oq_bind.rb_method ) {
+ case LDAP_AUTH_SIMPLE:
+ a = attr_find( e.e_attrs, password );
+ if ( a == NULL ) {
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ if ( slap_passwd_check( op, &e, a, &op->oq_bind.rb_cred,
+ &rs->sr_text ) != 0 )
+ {
+ /* failure; stop front end from sending result */
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ rs->sr_err = 0;
+ break;
+
+ default:
+ assert( 0 ); /* should not be reachable */
+ rs->sr_err = LDAP_STRONG_AUTH_NOT_SUPPORTED;
+ rs->sr_text = "authentication method not supported";
+ }
+
+done:
+ NA.txn->close();
+ if ( e.e_attrs ) {
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ }
+ if ( rs->sr_err ) {
+ send_ldap_result( op, rs );
+ }
+ /* front end will send result on success (rs->sr_err==0) */
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-ndb/compare.cpp b/servers/slapd/back-ndb/compare.cpp
new file mode 100644
index 0000000..4fc7401
--- /dev/null
+++ b/servers/slapd/back-ndb/compare.cpp
@@ -0,0 +1,169 @@
+/* compare.cpp - ndb backend compare routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-ndb.h"
+
+int
+ndb_back_compare( Operation *op, SlapReply *rs )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ Entry e = {0};
+ Attribute *a;
+ int manageDSAit = get_manageDSAit( op );
+
+ NdbArgs NA;
+ NdbRdns rdns;
+ struct berval matched;
+
+ /* Get our NDB handle */
+ rs->sr_err = ndb_thread_handle( op, &NA.ndb );
+
+ rdns.nr_num = 0;
+ NA.rdns = &rdns;
+ e.e_name = op->o_req_dn;
+ e.e_nname = op->o_req_ndn;
+ NA.e = &e;
+
+dn2entry_retry:
+ NA.txn = NA.ndb->startTransaction();
+ rs->sr_text = NULL;
+ if( !NA.txn ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_compare) ": startTransaction failed: %s (%d)\n",
+ NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ NA.ocs = NULL;
+ /* get entry */
+ rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched );
+ switch( rs->sr_err ) {
+ case 0:
+ break;
+ case LDAP_NO_SUCH_OBJECT:
+ rs->sr_matched = matched.bv_val;
+ if ( NA.ocs )
+ ndb_check_referral( op, rs, &NA );
+ goto return_results;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+#if 0
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto dn2entry_retry;
+#endif
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ rs->sr_err = ndb_entry_get_data( op, &NA, 0 );
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+ if (!manageDSAit && is_entry_referral( &e ) ) {
+ /* return referral only if "disclose" is granted on the object */
+ if ( !access_allowed( op, &e, slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL ) )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ } else {
+ /* entry is a referral, don't allow compare */
+ rs->sr_ref = get_entry_referrals( op, &e );
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_matched = e.e_name.bv_val;
+ rs->sr_flags |= REP_REF_MUSTBEFREED;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "entry is referral\n", 0, 0, 0 );
+ goto return_results;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, &e, (Filter *)get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ if ( !access_allowed( op, &e, slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL ) )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ } else {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ }
+ goto return_results;
+ }
+
+ if ( !access_allowed( op, &e, op->oq_compare.rs_ava->aa_desc,
+ &op->oq_compare.rs_ava->aa_value, ACL_COMPARE, NULL ) )
+ {
+ /* return error only if "disclose"
+ * is granted on the object */
+ if ( !access_allowed( op, &e, slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL ) )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ } else {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ }
+ goto return_results;
+ }
+
+ rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
+
+ for ( a = attrs_find( e.e_attrs, op->oq_compare.rs_ava->aa_desc );
+ a != NULL;
+ a = attrs_find( a->a_next, op->oq_compare.rs_ava->aa_desc ) )
+ {
+ rs->sr_err = LDAP_COMPARE_FALSE;
+
+ if ( attr_valfind( a,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ &op->oq_compare.rs_ava->aa_value, NULL,
+ op->o_tmpmemctx ) == 0 )
+ {
+ rs->sr_err = LDAP_COMPARE_TRUE;
+ break;
+ }
+ }
+
+return_results:
+ NA.txn->close();
+ if ( e.e_attrs ) {
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ }
+ send_ldap_result( op, rs );
+
+ switch ( rs->sr_err ) {
+ case LDAP_COMPARE_FALSE:
+ case LDAP_COMPARE_TRUE:
+ rs->sr_err = LDAP_SUCCESS;
+ break;
+ }
+
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-ndb/config.cpp b/servers/slapd/back-ndb/config.cpp
new file mode 100644
index 0000000..8b79b8b
--- /dev/null
+++ b/servers/slapd/back-ndb/config.cpp
@@ -0,0 +1,333 @@
+/* config.cpp - ndb backend configuration file routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+#include "lutil.h"
+
+#include "back-ndb.h"
+
+#include "slap-config.h"
+
+extern "C" {
+ static ConfigDriver ndb_cf_gen;
+};
+
+enum {
+ NDB_ATLEN = 1,
+ NDB_ATSET,
+ NDB_INDEX,
+ NDB_ATBLOB
+};
+
+static ConfigTable ndbcfg[] = {
+ { "dbhost", "hostname", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+ (void *)offsetof(struct ndb_info, ni_hostname),
+ "( OLcfgDbAt:6.1 NAME 'olcDbHost' "
+ "DESC 'Hostname of SQL server' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "dbname", "name", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+ (void *)offsetof(struct ndb_info, ni_dbname),
+ "( OLcfgDbAt:6.2 NAME 'olcDbName' "
+ "DESC 'Name of SQL database' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "dbuser", "username", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+ (void *)offsetof(struct ndb_info, ni_username),
+ "( OLcfgDbAt:6.3 NAME 'olcDbUser' "
+ "DESC 'Username for SQL session' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "dbpass", "password", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+ (void *)offsetof(struct ndb_info, ni_password),
+ "( OLcfgDbAt:6.4 NAME 'olcDbPass' "
+ "DESC 'Password for SQL session' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "dbport", "port", 2, 2, 0, ARG_UINT|ARG_OFFSET,
+ (void *)offsetof(struct ndb_info, ni_port),
+ "( OLcfgDbAt:6.5 NAME 'olcDbPort' "
+ "DESC 'Port number of SQL server' "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "dbsocket", "path", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+ (void *)offsetof(struct ndb_info, ni_socket),
+ "( OLcfgDbAt:6.6 NAME 'olcDbSocket' "
+ "DESC 'Local socket path of SQL server' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "dbflag", "flag", 2, 2, 0, ARG_LONG|ARG_OFFSET,
+ (void *)offsetof(struct ndb_info, ni_clflag),
+ "( OLcfgDbAt:6.7 NAME 'olcDbFlag' "
+ "DESC 'Flags for SQL session' "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "dbconnect", "hostname", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+ (void *)offsetof(struct ndb_info, ni_connectstr),
+ "( OLcfgDbAt:6.8 NAME 'olcDbConnect' "
+ "DESC 'Hostname of NDB server' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "dbconnections", "number", 2, 2, 0, ARG_INT|ARG_OFFSET,
+ (void *)offsetof(struct ndb_info, ni_nconns),
+ "( OLcfgDbAt:6.9 NAME 'olcDbConnections' "
+ "DESC 'Number of cluster connections to open' "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "attrlen", "attr> <len", 3, 3, 0, ARG_MAGIC|NDB_ATLEN,
+ (void *)ndb_cf_gen,
+ "( OLcfgDbAt:6.10 NAME 'olcNdbAttrLen' "
+ "DESC 'Column length of a specific attribute' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "attrset", "set> <attrs", 3, 3, 0, ARG_MAGIC|NDB_ATSET,
+ (void *)ndb_cf_gen,
+ "( OLcfgDbAt:6.11 NAME 'olcNdbAttrSet' "
+ "DESC 'Set of common attributes' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "index", "attr", 2, 2, 0, ARG_MAGIC|NDB_INDEX,
+ (void *)ndb_cf_gen, "( OLcfgDbAt:0.2 NAME 'olcDbIndex' "
+ "DESC 'Attribute to index' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "attrblob", "attr", 2, 2, 0, ARG_MAGIC|NDB_ATBLOB,
+ (void *)ndb_cf_gen, "( OLcfgDbAt:6.12 NAME 'olcNdbAttrBlob' "
+ "DESC 'Attribute to treat as a BLOB' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "directory", "dir", 2, 2, 0, ARG_IGNORED,
+ NULL, "( OLcfgDbAt:0.1 NAME 'olcDbDirectory' "
+ "DESC 'Dummy keyword' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED,
+ NULL, NULL, NULL, NULL }
+};
+
+static ConfigOCs ndbocs[] = {
+ {
+ "( OLcfgDbOc:6.2 "
+ "NAME 'olcNdbConfig' "
+ "DESC 'NDB backend configuration' "
+ "SUP olcDatabaseConfig "
+ "MUST ( olcDbHost $ olcDbName $ olcDbConnect ) "
+ "MAY ( olcDbUser $ olcDbPass $ olcDbPort $ olcDbSocket $ "
+ "olcDbFlag $ olcDbConnections $ olcNdbAttrLen $ "
+ "olcDbIndex $ olcNdbAttrSet $ olcNdbAttrBlob ) )",
+ Cft_Database, ndbcfg },
+ { NULL, Cft_Abstract, NULL }
+};
+
+static int
+ndb_cf_gen( ConfigArgs *c )
+{
+ struct ndb_info *ni = (struct ndb_info *)c->be->be_private;
+ int i, rc;
+ NdbAttrInfo *ai;
+ NdbOcInfo *oci;
+ ListNode *ln, **l2;
+ struct berval bv, *bva;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ char buf[BUFSIZ];
+ rc = 0;
+ bv.bv_val = buf;
+ switch( c->type ) {
+ case NDB_ATLEN:
+ if ( ni->ni_attrlens ) {
+ for ( ln = ni->ni_attrlens; ln; ln=ln->ln_next ) {
+ ai = (NdbAttrInfo *)ln->ln_data;
+ bv.bv_len = snprintf( buf, sizeof(buf),
+ "%s %d", ai->na_name.bv_val,
+ ai->na_len );
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ } else {
+ rc = 1;
+ }
+ break;
+
+ case NDB_ATSET:
+ if ( ni->ni_attrsets ) {
+ char *ptr, *end = buf+sizeof(buf);
+ for ( ln = ni->ni_attrsets; ln; ln=ln->ln_next ) {
+ oci = (NdbOcInfo *)ln->ln_data;
+ ptr = lutil_strcopy( buf, oci->no_name.bv_val );
+ *ptr++ = ' ';
+ for ( i=0; i<oci->no_nattrs; i++ ) {
+ if ( end - ptr < oci->no_attrs[i]->na_name.bv_len+1 )
+ break;
+ if ( i )
+ *ptr++ = ',';
+ ptr = lutil_strcopy(ptr,
+ oci->no_attrs[i]->na_name.bv_val );
+ }
+ bv.bv_len = ptr - buf;
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ } else {
+ rc = 1;
+ }
+ break;
+
+ case NDB_INDEX:
+ if ( ni->ni_attridxs ) {
+ for ( ln = ni->ni_attridxs; ln; ln=ln->ln_next ) {
+ ai = (NdbAttrInfo *)ln->ln_data;
+ value_add_one( &c->rvalue_vals, &ai->na_name );
+ }
+ } else {
+ rc = 1;
+ }
+ break;
+
+ case NDB_ATBLOB:
+ if ( ni->ni_attrblobs ) {
+ for ( ln = ni->ni_attrblobs; ln; ln=ln->ln_next ) {
+ ai = (NdbAttrInfo *)ln->ln_data;
+ value_add_one( &c->rvalue_vals, &ai->na_name );
+ }
+ } else {
+ rc = 1;
+ }
+ break;
+
+ }
+ return rc;
+ } else if ( c->op == LDAP_MOD_DELETE ) { /* FIXME */
+ rc = 0;
+ switch( c->type ) {
+ case NDB_INDEX:
+ if ( c->valx == -1 ) {
+
+ /* delete all */
+
+ } else {
+
+ }
+ break;
+ }
+ return rc;
+ }
+
+ switch( c->type ) {
+ case NDB_ATLEN:
+ ber_str2bv( c->argv[1], 0, 0, &bv );
+ ai = ndb_ai_get( ni, &bv );
+ if ( !ai ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: invalid attr %s",
+ c->log, c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+ return -1;
+ }
+ for ( ln = ni->ni_attrlens; ln; ln = ln->ln_next ) {
+ if ( ln->ln_data == (void *)ai ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: attr len already set for %s",
+ c->log, c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+ return -1;
+ }
+ }
+ ai->na_len = atoi( c->argv[2] );
+ ai->na_flag |= NDB_INFO_ATLEN;
+ ln = (ListNode *)ch_malloc( sizeof(ListNode));
+ ln->ln_data = ai;
+ ln->ln_next = NULL;
+ for ( l2 = &ni->ni_attrlens; *l2; l2 = &(*l2)->ln_next );
+ *l2 = ln;
+ break;
+
+ case NDB_INDEX:
+ ber_str2bv( c->argv[1], 0, 0, &bv );
+ ai = ndb_ai_get( ni, &bv );
+ if ( !ai ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: invalid attr %s",
+ c->log, c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+ return -1;
+ }
+ for ( ln = ni->ni_attridxs; ln; ln = ln->ln_next ) {
+ if ( ln->ln_data == (void *)ai ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: attr index already set for %s",
+ c->log, c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+ return -1;
+ }
+ }
+ ai->na_flag |= NDB_INFO_INDEX;
+ ln = (ListNode *)ch_malloc( sizeof(ListNode));
+ ln->ln_data = ai;
+ ln->ln_next = NULL;
+ for ( l2 = &ni->ni_attridxs; *l2; l2 = &(*l2)->ln_next );
+ *l2 = ln;
+ break;
+
+ case NDB_ATSET:
+ ber_str2bv( c->argv[1], 0, 0, &bv );
+ bva = ndb_str2bvarray( c->argv[2], strlen( c->argv[2] ), ',', NULL );
+ rc = ndb_aset_get( ni, &bv, bva, &oci );
+ ber_bvarray_free( bva );
+ if ( rc ) {
+ if ( rc == LDAP_ALREADY_EXISTS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s: attrset %s already defined",
+ c->log, c->argv[1] );
+ } else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s: invalid attrset %s (%d)",
+ c->log, c->argv[1], rc );
+ }
+ Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+ return -1;
+ }
+ ln = (ListNode *)ch_malloc( sizeof(ListNode));
+ ln->ln_data = oci;
+ ln->ln_next = NULL;
+ for ( l2 = &ni->ni_attrsets; *l2; l2 = &(*l2)->ln_next );
+ *l2 = ln;
+ break;
+
+ case NDB_ATBLOB:
+ ber_str2bv( c->argv[1], 0, 0, &bv );
+ ai = ndb_ai_get( ni, &bv );
+ if ( !ai ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: invalid attr %s",
+ c->log, c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+ return -1;
+ }
+ for ( ln = ni->ni_attrblobs; ln; ln = ln->ln_next ) {
+ if ( ln->ln_data == (void *)ai ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: attr blob already set for %s",
+ c->log, c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+ return -1;
+ }
+ }
+ ai->na_flag |= NDB_INFO_ATBLOB;
+ ln = (ListNode *)ch_malloc( sizeof(ListNode));
+ ln->ln_data = ai;
+ ln->ln_next = NULL;
+ for ( l2 = &ni->ni_attrblobs; *l2; l2 = &(*l2)->ln_next );
+ *l2 = ln;
+ break;
+
+ }
+ return 0;
+}
+
+extern "C"
+int ndb_back_init_cf( BackendInfo *bi )
+{
+ bi->bi_cf_ocs = ndbocs;
+
+ return config_register_schema( ndbcfg, ndbocs );
+}
diff --git a/servers/slapd/back-ndb/delete.cpp b/servers/slapd/back-ndb/delete.cpp
new file mode 100644
index 0000000..46b33f6
--- /dev/null
+++ b/servers/slapd/back-ndb/delete.cpp
@@ -0,0 +1,322 @@
+/* delete.cpp - ndb backend delete routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "lutil.h"
+#include "back-ndb.h"
+
+static struct berval glue_bv = BER_BVC("glue");
+
+int
+ndb_back_delete( Operation *op, SlapReply *rs )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ Entry e = {0};
+ Entry p = {0};
+ int manageDSAit = get_manageDSAit( op );
+ AttributeDescription *children = slap_schema.si_ad_children;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+
+ NdbArgs NA;
+ NdbRdns rdns;
+ struct berval matched;
+
+ int num_retries = 0;
+
+ int rc;
+
+ LDAPControl **preread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+
+ Debug( LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(ndb_back_delete) ": %s\n",
+ op->o_req_dn.bv_val, 0, 0 );
+
+ ctrls[num_ctrls] = 0;
+
+ /* allocate CSN */
+ if ( BER_BVISNULL( &op->o_csn ) ) {
+ struct berval csn;
+ char csnbuf[LDAP_PVT_CSNSTR_BUFSIZE];
+
+ csn.bv_val = csnbuf;
+ csn.bv_len = sizeof(csnbuf);
+ slap_get_csn( op, &csn, 1 );
+ }
+
+ if ( !be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
+ dnParent( &op->o_req_dn, &p.e_name );
+ dnParent( &op->o_req_ndn, &p.e_nname );
+ }
+
+ /* Get our NDB handle */
+ rs->sr_err = ndb_thread_handle( op, &NA.ndb );
+ rdns.nr_num = 0;
+ NA.rdns = &rdns;
+ NA.ocs = NULL;
+ NA.e = &e;
+ e.e_name = op->o_req_dn;
+ e.e_nname = op->o_req_ndn;
+
+ if( 0 ) {
+retry: /* transaction retry */
+ NA.txn->close();
+ NA.txn = NULL;
+ Debug( LDAP_DEBUG_TRACE,
+ "==> " LDAP_XSTRING(ndb_back_delete) ": retrying...\n",
+ 0, 0, 0 );
+ if ( op->o_abandon ) {
+ rs->sr_err = SLAPD_ABANDON;
+ goto return_results;
+ }
+ if ( NA.ocs ) {
+ ber_bvarray_free( NA.ocs );
+ NA.ocs = NULL;
+ }
+ ndb_trans_backoff( ++num_retries );
+ }
+
+ /* begin transaction */
+ NA.txn = NA.ndb->startTransaction();
+ rs->sr_text = NULL;
+ if( !NA.txn ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_delete) ": startTransaction failed: %s (%d)\n",
+ NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* get entry */
+ rs->sr_err = ndb_entry_get_info( op, &NA, 1, &matched );
+ switch( rs->sr_err ) {
+ case 0:
+ case LDAP_NO_SUCH_OBJECT:
+ break;
+#if 0
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto retry;
+#endif
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ||
+ ( !manageDSAit && bvmatch( NA.ocs, &glue_bv ))) {
+ Debug( LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(ndb_back_delete) ": no such object %s\n",
+ op->o_req_dn.bv_val, 0, 0);
+
+ if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
+ rs->sr_matched = matched.bv_val;
+ if ( NA.ocs )
+ ndb_check_referral( op, rs, &NA );
+ } else {
+ rs->sr_matched = p.e_name.bv_val;
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ }
+ goto return_results;
+ }
+
+ /* check parent for "children" acl */
+ rs->sr_err = access_allowed( op, &p,
+ children, NULL, ACL_WDEL, NULL );
+
+ if ( !rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_delete) ": no write "
+ "access to parent\n", 0, 0, 0 );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to parent";
+ goto return_results;
+ }
+
+ rs->sr_err = ndb_entry_get_data( op, &NA, 1 );
+
+ rs->sr_err = access_allowed( op, &e,
+ entry, NULL, ACL_WDEL, NULL );
+
+ if ( !rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_delete) ": no write access "
+ "to entry\n", 0, 0, 0 );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to entry";
+ goto return_results;
+ }
+
+ if ( !manageDSAit && is_entry_referral( &e ) ) {
+ /* entry is a referral, don't allow delete */
+ rs->sr_ref = get_entry_referrals( op, &e );
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_delete) ": entry is referral\n",
+ 0, 0, 0 );
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_matched = e.e_name.bv_val;
+ rs->sr_flags = REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, &e, (Filter *)get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+ /* pre-read */
+ if( op->o_preread ) {
+ if( preread_ctrl == NULL ) {
+ preread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if( slap_read_controls( op, rs, &e,
+ &slap_pre_read_bv, preread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_delete) ": pre-read "
+ "failed!\n", 0, 0, 0 );
+ if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ /* Can't do it if we have kids */
+ rs->sr_err = ndb_has_children( &NA, &rc );
+ if ( rs->sr_err ) {
+ Debug(LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(ndb_back_delete)
+ ": has_children failed: %s (%d)\n",
+ NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ Debug(LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(ndb_back_delete)
+ ": non-leaf %s\n",
+ op->o_req_dn.bv_val, 0, 0);
+ rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
+ rs->sr_text = "subordinate objects must be deleted first";
+ goto return_results;
+ }
+
+ /* delete info */
+ rs->sr_err = ndb_entry_del_info( op->o_bd, &NA );
+ if ( rs->sr_err != 0 ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_delete) ": del_info failed: %s (%d)\n",
+ NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
+ rs->sr_text = "DN index delete failed";
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+
+ /* delete data */
+ rs->sr_err = ndb_entry_del_data( op->o_bd, &NA );
+ if ( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_delete) ": del_data failed: %s (%d)\n",
+ NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
+ rs->sr_text = "entry delete failed";
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+
+ if( op->o_noop ) {
+ if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback,
+ NdbOperation::AbortOnError, 1 )) != 0 ) {
+ rs->sr_text = "txn (no-op) failed";
+ } else {
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ }
+ } else {
+ if (( rs->sr_err=NA.txn->execute( NdbTransaction::Commit,
+ NdbOperation::AbortOnError, 1 )) != 0 ) {
+ rs->sr_text = "txn_commit failed";
+ } else {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+ }
+
+ if( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_delete) ": txn_%s failed: %s (%d)\n",
+ op->o_noop ? "abort (no-op)" : "commit",
+ NA.txn->getNdbError().message, NA.txn->getNdbError().code );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "commit failed";
+
+ goto return_results;
+ }
+ NA.txn->close();
+ NA.txn = NULL;
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_delete) ": deleted%s id=%08lx dn=\"%s\"\n",
+ op->o_noop ? " (no-op)" : "",
+ e.e_id, op->o_req_dn.bv_val );
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+ if ( NA.ocs ) {
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+ NA.ocs = NULL;
+ }
+
+ /* free entry */
+ if( e.e_attrs != NULL ) {
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ }
+
+ if( NA.txn != NULL ) {
+ NA.txn->execute( Rollback );
+ NA.txn->close();
+ }
+
+ send_ldap_result( op, rs );
+ slap_graduate_commit_csn( op );
+
+ if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
+ slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
+ }
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-ndb/init.cpp b/servers/slapd/back-ndb/init.cpp
new file mode 100644
index 0000000..d8f6276
--- /dev/null
+++ b/servers/slapd/back-ndb/init.cpp
@@ -0,0 +1,449 @@
+/* init.cpp - initialize ndb backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+#include <ac/stdlib.h>
+#include <ac/errno.h>
+#include <sys/stat.h>
+#include "back-ndb.h"
+#include <lutil.h>
+#include "slap-config.h"
+
+extern "C" {
+ static BI_db_init ndb_db_init;
+ static BI_db_close ndb_db_close;
+ static BI_db_open ndb_db_open;
+ static BI_db_destroy ndb_db_destroy;
+}
+
+static struct berval ndb_optable = BER_BVC("OL_opattrs");
+
+static struct berval ndb_opattrs[] = {
+ BER_BVC("structuralObjectClass"),
+ BER_BVC("entryUUID"),
+ BER_BVC("creatorsName"),
+ BER_BVC("createTimestamp"),
+ BER_BVC("entryCSN"),
+ BER_BVC("modifiersName"),
+ BER_BVC("modifyTimestamp"),
+ BER_BVNULL
+};
+
+static int ndb_oplens[] = {
+ 0, /* structuralOC, default */
+ 36, /* entryUUID */
+ 0, /* creatorsName, default */
+ 26, /* createTimestamp */
+ 40, /* entryCSN */
+ 0, /* modifiersName, default */
+ 26, /* modifyTimestamp */
+ -1
+};
+
+static Uint32 ndb_lastrow[1];
+NdbInterpretedCode *ndb_lastrow_code;
+
+static int
+ndb_db_init( BackendDB *be, ConfigReply *cr )
+{
+ struct ndb_info *ni;
+ int rc = 0;
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_db_init) ": Initializing ndb database\n",
+ 0, 0, 0 );
+
+ /* allocate backend-database-specific stuff */
+ ni = (struct ndb_info *) ch_calloc( 1, sizeof(struct ndb_info) );
+
+ be->be_private = ni;
+ be->be_cf_ocs = be->bd_info->bi_cf_ocs;
+
+ ni->ni_search_stack_depth = DEFAULT_SEARCH_STACK_DEPTH;
+
+ ldap_pvt_thread_rdwr_init( &ni->ni_ai_rwlock );
+ ldap_pvt_thread_rdwr_init( &ni->ni_oc_rwlock );
+ ldap_pvt_thread_mutex_init( &ni->ni_conn_mutex );
+
+#ifdef DO_MONITORING
+ rc = ndb_monitor_db_init( be );
+#endif
+
+ return rc;
+}
+
+static int
+ndb_db_close( BackendDB *be, ConfigReply *cr );
+
+static int
+ndb_db_open( BackendDB *be, ConfigReply *cr )
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ char sqlbuf[BUFSIZ], *ptr;
+ int rc, i;
+
+ if ( be->be_suffix == NULL ) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: need suffix" );
+ Debug( LDAP_DEBUG_ANY, "%s\n",
+ cr->msg, 0, 0 );
+ return -1;
+ }
+
+ Debug( LDAP_DEBUG_ARGS,
+ LDAP_XSTRING(ndb_db_open) ": \"%s\"\n",
+ be->be_suffix[0].bv_val, 0, 0 );
+
+ if ( ni->ni_nconns < 1 )
+ ni->ni_nconns = 1;
+
+ ni->ni_cluster = (Ndb_cluster_connection **)ch_calloc( ni->ni_nconns, sizeof( Ndb_cluster_connection *));
+ for ( i=0; i<ni->ni_nconns; i++ ) {
+ ni->ni_cluster[i] = new Ndb_cluster_connection( ni->ni_connectstr );
+ rc = ni->ni_cluster[i]->connect( 20, 5, 1 );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: ni_cluster[%d]->connect failed (%d)",
+ i, rc );
+ goto fail;
+ }
+ }
+ for ( i=0; i<ni->ni_nconns; i++ ) {
+ rc = ni->ni_cluster[i]->wait_until_ready( 30, 30 );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: ni_cluster[%d]->wait failed (%d)",
+ i, rc );
+ goto fail;
+ }
+ }
+
+ mysql_init( &ni->ni_sql );
+ if ( !mysql_real_connect( &ni->ni_sql, ni->ni_hostname, ni->ni_username, ni->ni_password,
+ "", ni->ni_port, ni->ni_socket, ni->ni_clflag )) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: mysql_real_connect failed, %s (%d)",
+ mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
+ rc = -1;
+ goto fail;
+ }
+
+ sprintf( sqlbuf, "CREATE DATABASE IF NOT EXISTS %s", ni->ni_dbname );
+ rc = mysql_query( &ni->ni_sql, sqlbuf );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: CREATE DATABASE %s failed, %s (%d)",
+ ni->ni_dbname, mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
+ goto fail;
+ }
+
+ sprintf( sqlbuf, "USE %s", ni->ni_dbname );
+ rc = mysql_query( &ni->ni_sql, sqlbuf );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: USE DATABASE %s failed, %s (%d)",
+ ni->ni_dbname, mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
+ goto fail;
+ }
+
+ ptr = sqlbuf;
+ ptr += sprintf( ptr, "CREATE TABLE IF NOT EXISTS " DN2ID_TABLE " ("
+ "eid bigint unsigned NOT NULL, "
+ "object_classes VARCHAR(1024) NOT NULL, "
+ "a0 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a1 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a2 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a3 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a4 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a5 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a6 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a7 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a8 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a9 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a10 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a11 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a12 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a13 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a14 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a15 VARCHAR(128) NOT NULL DEFAULT '', "
+ "PRIMARY KEY (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15), "
+ "UNIQUE KEY eid (eid) USING HASH" );
+ /* Create index columns */
+ if ( ni->ni_attridxs ) {
+ ListNode *ln;
+ int newcol = 0;
+
+ *ptr++ = ',';
+ *ptr++ = ' ';
+ for ( ln = ni->ni_attridxs; ln; ln=ln->ln_next ) {
+ NdbAttrInfo *ai = (NdbAttrInfo *)ln->ln_data;
+ ptr += sprintf( ptr, "`%s` VARCHAR(%d), ",
+ ai->na_name.bv_val, ai->na_len );
+ }
+ ptr = lutil_strcopy(ptr, "KEY " INDEX_NAME " (" );
+
+ for ( ln = ni->ni_attridxs; ln; ln=ln->ln_next ) {
+ NdbAttrInfo *ai = (NdbAttrInfo *)ln->ln_data;
+ if ( newcol ) *ptr++ = ',';
+ *ptr++ = '`';
+ ptr = lutil_strcopy( ptr, ai->na_name.bv_val );
+ *ptr++ = '`';
+ ai->na_ixcol = newcol + 18;
+ newcol++;
+ }
+ *ptr++ = ')';
+ }
+ strcpy( ptr, ") ENGINE=ndb" );
+ rc = mysql_query( &ni->ni_sql, sqlbuf );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: CREATE TABLE " DN2ID_TABLE " failed, %s (%d)",
+ mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
+ goto fail;
+ }
+
+ rc = mysql_query( &ni->ni_sql, "CREATE TABLE IF NOT EXISTS " NEXTID_TABLE " ("
+ "a bigint unsigned AUTO_INCREMENT PRIMARY KEY ) ENGINE=ndb" );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: CREATE TABLE " NEXTID_TABLE " failed, %s (%d)",
+ mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
+ goto fail;
+ }
+
+ {
+ NdbOcInfo *oci;
+
+ rc = ndb_aset_get( ni, &ndb_optable, ndb_opattrs, &oci );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: ndb_aset_get( %s ) failed (%d)",
+ ndb_optable.bv_val, rc );
+ goto fail;
+ }
+ for ( i=0; ndb_oplens[i] >= 0; i++ ) {
+ if ( ndb_oplens[i] )
+ oci->no_attrs[i]->na_len = ndb_oplens[i];
+ }
+ rc = ndb_aset_create( ni, oci );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: ndb_aset_create( %s ) failed (%d)",
+ ndb_optable.bv_val, rc );
+ goto fail;
+ }
+ ni->ni_opattrs = oci;
+ }
+ /* Create attribute sets */
+ {
+ ListNode *ln;
+
+ for ( ln = ni->ni_attrsets; ln; ln=ln->ln_next ) {
+ NdbOcInfo *oci = (NdbOcInfo *)ln->ln_data;
+ rc = ndb_aset_create( ni, oci );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: ndb_aset_create( %s ) failed (%d)",
+ oci->no_name.bv_val, rc );
+ goto fail;
+ }
+ }
+ }
+ /* Initialize any currently used objectClasses */
+ {
+ Ndb *ndb;
+ const NdbDictionary::Dictionary *myDict;
+
+ ndb = new Ndb( ni->ni_cluster[0], ni->ni_dbname );
+ ndb->init(1024);
+
+ myDict = ndb->getDictionary();
+ ndb_oc_read( ni, myDict );
+ delete ndb;
+ }
+
+#ifdef DO_MONITORING
+ /* monitor setup */
+ rc = ndb_monitor_db_open( be );
+ if ( rc != 0 ) {
+ goto fail;
+ }
+#endif
+
+ return 0;
+
+fail:
+ Debug( LDAP_DEBUG_ANY, "%s\n",
+ cr->msg, 0, 0 );
+ ndb_db_close( be, NULL );
+ return rc;
+}
+
+static int
+ndb_db_close( BackendDB *be, ConfigReply *cr )
+{
+ int i;
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+
+ mysql_close( &ni->ni_sql );
+ if ( ni->ni_cluster ) {
+ for ( i=0; i<ni->ni_nconns; i++ ) {
+ if ( ni->ni_cluster[i] ) {
+ delete ni->ni_cluster[i];
+ ni->ni_cluster[i] = NULL;
+ }
+ }
+ ch_free( ni->ni_cluster );
+ ni->ni_cluster = NULL;
+ }
+
+#ifdef DO_MONITORING
+ /* monitor handling */
+ (void)ndb_monitor_db_close( be );
+#endif
+
+ return 0;
+}
+
+static int
+ndb_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+
+#ifdef DO_MONITORING
+ /* monitor handling */
+ (void)ndb_monitor_db_destroy( be );
+#endif
+
+ ldap_pvt_thread_mutex_destroy( &ni->ni_conn_mutex );
+ ldap_pvt_thread_rdwr_destroy( &ni->ni_ai_rwlock );
+ ldap_pvt_thread_rdwr_destroy( &ni->ni_oc_rwlock );
+
+ ch_free( ni );
+ be->be_private = NULL;
+
+ return 0;
+}
+
+extern "C" int
+ndb_back_initialize(
+ BackendInfo *bi )
+{
+ static char *controls[] = {
+ LDAP_CONTROL_ASSERT,
+ LDAP_CONTROL_MANAGEDSAIT,
+ LDAP_CONTROL_NOOP,
+ LDAP_CONTROL_PAGEDRESULTS,
+ LDAP_CONTROL_PRE_READ,
+ LDAP_CONTROL_POST_READ,
+ LDAP_CONTROL_SUBENTRIES,
+ LDAP_CONTROL_X_PERMISSIVE_MODIFY,
+ LDAP_CONTROL_TXN_SPEC,
+ NULL
+ };
+
+ int rc = 0;
+
+ /* initialize the underlying database system */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_initialize) ": initialize ndb backend\n", 0, 0, 0 );
+
+ ndb_init();
+
+ ndb_lastrow_code = new NdbInterpretedCode( NULL, ndb_lastrow, 1 );
+ ndb_lastrow_code->interpret_exit_last_row();
+ ndb_lastrow_code->finalise();
+
+ bi->bi_flags |=
+ SLAP_BFLAG_INCREMENT |
+ SLAP_BFLAG_SUBENTRIES |
+ SLAP_BFLAG_ALIASES |
+ SLAP_BFLAG_REFERRALS;
+
+ bi->bi_controls = controls;
+
+ bi->bi_open = 0;
+ bi->bi_close = 0;
+ bi->bi_config = 0;
+ bi->bi_destroy = 0;
+
+ bi->bi_db_init = ndb_db_init;
+ bi->bi_db_config = config_generic_wrapper;
+ bi->bi_db_open = ndb_db_open;
+ bi->bi_db_close = ndb_db_close;
+ bi->bi_db_destroy = ndb_db_destroy;
+
+ bi->bi_op_add = ndb_back_add;
+ bi->bi_op_bind = ndb_back_bind;
+ bi->bi_op_compare = ndb_back_compare;
+ bi->bi_op_delete = ndb_back_delete;
+ bi->bi_op_modify = ndb_back_modify;
+ bi->bi_op_modrdn = ndb_back_modrdn;
+ bi->bi_op_search = ndb_back_search;
+
+ bi->bi_op_unbind = 0;
+
+#if 0
+ bi->bi_extended = ndb_extended;
+
+ bi->bi_chk_referrals = ndb_referrals;
+#endif
+ bi->bi_operational = ndb_operational;
+ bi->bi_has_subordinates = ndb_has_subordinates;
+ bi->bi_entry_release_rw = 0;
+ bi->bi_entry_get_rw = ndb_entry_get;
+
+ /*
+ * hooks for slap tools
+ */
+ bi->bi_tool_entry_open = ndb_tool_entry_open;
+ bi->bi_tool_entry_close = ndb_tool_entry_close;
+ bi->bi_tool_entry_first = ndb_tool_entry_first;
+ bi->bi_tool_entry_next = ndb_tool_entry_next;
+ bi->bi_tool_entry_get = ndb_tool_entry_get;
+ bi->bi_tool_entry_put = ndb_tool_entry_put;
+#if 0
+ bi->bi_tool_entry_reindex = ndb_tool_entry_reindex;
+ bi->bi_tool_sync = 0;
+ bi->bi_tool_dn2id_get = ndb_tool_dn2id_get;
+ bi->bi_tool_entry_modify = ndb_tool_entry_modify;
+#endif
+
+ bi->bi_connection_init = 0;
+ bi->bi_connection_destroy = 0;
+
+ rc = ndb_back_init_cf( bi );
+
+ return rc;
+}
+
+#if SLAPD_NDB == SLAPD_MOD_DYNAMIC
+
+/* conditionally define the init_module() function */
+extern "C" { int init_module( int argc, char *argv[] ); }
+
+SLAP_BACKEND_INIT_MODULE( ndb )
+
+#endif /* SLAPD_NDB == SLAPD_MOD_DYNAMIC */
+
diff --git a/servers/slapd/back-ndb/modify.cpp b/servers/slapd/back-ndb/modify.cpp
new file mode 100644
index 0000000..243e9d2
--- /dev/null
+++ b/servers/slapd/back-ndb/modify.cpp
@@ -0,0 +1,704 @@
+/* modify.cpp - ndb backend modify routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "back-ndb.h"
+
+/* This is a copy from slapd/mods.c, but with compaction tweaked
+ * to swap values from the tail into deleted slots, to reduce the
+ * overall update traffic.
+ */
+static int
+ndb_modify_delete(
+ Entry *e,
+ Modification *mod,
+ int permissive,
+ const char **text,
+ char *textbuf, size_t textlen,
+ int *idx )
+{
+ Attribute *a;
+ MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
+ struct berval *cvals;
+ int *id2 = NULL;
+ int i, j, rc = 0, num;
+ unsigned flags;
+ char dummy = '\0';
+
+ /* For ordered vals, we have no choice but to preserve order */
+ if ( mod->sm_desc->ad_type->sat_flags & SLAP_AT_ORDERED_VAL )
+ return modify_delete_vindex( e, mod, permissive, text,
+ textbuf, textlen, idx );
+
+ /*
+ * If permissive is set, then the non-existence of an
+ * attribute is not treated as an error.
+ */
+
+ /* delete the entire attribute */
+ if ( mod->sm_values == NULL ) {
+ rc = attr_delete( &e->e_attrs, mod->sm_desc );
+
+ if( permissive ) {
+ rc = LDAP_SUCCESS;
+ } else if( rc != LDAP_SUCCESS ) {
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: no such attribute",
+ mod->sm_desc->ad_cname.bv_val );
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ }
+ return rc;
+ }
+
+ /* FIXME: Catch old code that doesn't set sm_numvals.
+ */
+ if ( !BER_BVISNULL( &mod->sm_values[mod->sm_numvals] )) {
+ for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ );
+ assert( mod->sm_numvals == i );
+ }
+ if ( !idx ) {
+ id2 = (int *)ch_malloc( mod->sm_numvals * sizeof( int ));
+ idx = id2;
+ }
+
+ if( mr == NULL || !mr->smr_match ) {
+ /* disallow specific attributes from being deleted if
+ no equality rule */
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: no equality matching rule",
+ mod->sm_desc->ad_cname.bv_val );
+ rc = LDAP_INAPPROPRIATE_MATCHING;
+ goto return_result;
+ }
+
+ /* delete specific values - find the attribute first */
+ if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
+ if( permissive ) {
+ rc = LDAP_SUCCESS;
+ goto return_result;
+ }
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: no such attribute",
+ mod->sm_desc->ad_cname.bv_val );
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ goto return_result;
+ }
+
+ if ( mod->sm_nvalues ) {
+ flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX
+ | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
+ | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH;
+ cvals = mod->sm_nvalues;
+ } else {
+ flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX;
+ cvals = mod->sm_values;
+ }
+
+ /* Locate values to delete */
+ for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ ) {
+ unsigned sort;
+ rc = attr_valfind( a, flags, &cvals[i], &sort, NULL );
+ if ( rc == LDAP_SUCCESS ) {
+ idx[i] = sort;
+ } else if ( rc == LDAP_NO_SUCH_ATTRIBUTE ) {
+ if ( permissive ) {
+ idx[i] = -1;
+ continue;
+ }
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: no such value",
+ mod->sm_desc->ad_cname.bv_val );
+ goto return_result;
+ } else {
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: matching rule failed",
+ mod->sm_desc->ad_cname.bv_val );
+ goto return_result;
+ }
+ }
+
+ num = a->a_numvals;
+
+ /* Delete the values */
+ for ( i = 0; i < mod->sm_numvals; i++ ) {
+ /* Skip permissive values that weren't found */
+ if ( idx[i] < 0 )
+ continue;
+ /* Skip duplicate delete specs */
+ if ( a->a_vals[idx[i]].bv_val == &dummy )
+ continue;
+ /* delete value and mark it as gone */
+ free( a->a_vals[idx[i]].bv_val );
+ a->a_vals[idx[i]].bv_val = &dummy;
+ if( a->a_nvals != a->a_vals ) {
+ free( a->a_nvals[idx[i]].bv_val );
+ a->a_nvals[idx[i]].bv_val = &dummy;
+ }
+ a->a_numvals--;
+ }
+
+ /* compact array */
+ for ( i=0; i<num; i++ ) {
+ if ( a->a_vals[i].bv_val != &dummy )
+ continue;
+ for ( --num; num > i && a->a_vals[num].bv_val == &dummy; num-- )
+ ;
+ a->a_vals[i] = a->a_vals[num];
+ if ( a->a_nvals != a->a_vals )
+ a->a_nvals[i] = a->a_nvals[num];
+ }
+
+ BER_BVZERO( &a->a_vals[num] );
+ if (a->a_nvals != a->a_vals) {
+ BER_BVZERO( &a->a_nvals[num] );
+ }
+
+ /* if no values remain, delete the entire attribute */
+ if ( !a->a_numvals ) {
+ if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
+ /* Can never happen */
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: no such attribute",
+ mod->sm_desc->ad_cname.bv_val );
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ }
+ }
+return_result:
+ if ( id2 )
+ ch_free( id2 );
+ return rc;
+}
+
+int ndb_modify_internal(
+ Operation *op,
+ NdbArgs *NA,
+ const char **text,
+ char *textbuf,
+ size_t textlen )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ Modification *mod;
+ Modifications *ml;
+ Modifications *modlist = op->orm_modlist;
+ NdbAttrInfo **modai, *atmp;
+ const NdbDictionary::Dictionary *myDict;
+ const NdbDictionary::Table *myTable;
+ int got_oc = 0, nmods = 0, nai = 0, i, j;
+ int rc, indexed = 0;
+ Attribute *old = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "ndb_modify_internal: 0x%08lx: %s\n",
+ NA->e->e_id, NA->e->e_dn, 0);
+
+ if ( !acl_check_modlist( op, NA->e, modlist )) {
+ return LDAP_INSUFFICIENT_ACCESS;
+ }
+
+ old = attrs_dup( NA->e->e_attrs );
+
+ for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
+ mod = &ml->sml_mod;
+ nmods++;
+
+ switch ( mod->sm_op ) {
+ case LDAP_MOD_ADD:
+ Debug(LDAP_DEBUG_ARGS,
+ "ndb_modify_internal: add %s\n",
+ mod->sm_desc->ad_cname.bv_val, 0, 0);
+ rc = modify_add_values( NA->e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+ if( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
+ rc, *text, 0);
+ }
+ break;
+
+ case LDAP_MOD_DELETE:
+ Debug(LDAP_DEBUG_ARGS,
+ "ndb_modify_internal: delete %s\n",
+ mod->sm_desc->ad_cname.bv_val, 0, 0);
+ rc = ndb_modify_delete( NA->e, mod, get_permissiveModify(op),
+ text, textbuf, textlen, NULL );
+ assert( rc != LDAP_TYPE_OR_VALUE_EXISTS );
+ if( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
+ rc, *text, 0);
+ }
+ break;
+
+ case LDAP_MOD_REPLACE:
+ Debug(LDAP_DEBUG_ARGS,
+ "ndb_modify_internal: replace %s\n",
+ mod->sm_desc->ad_cname.bv_val, 0, 0);
+ rc = modify_replace_values( NA->e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+ if( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
+ rc, *text, 0);
+ }
+ break;
+
+ case LDAP_MOD_INCREMENT:
+ Debug(LDAP_DEBUG_ARGS,
+ "ndb_modify_internal: increment %s\n",
+ mod->sm_desc->ad_cname.bv_val, 0, 0);
+ rc = modify_increment_values( NA->e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+ if( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS,
+ "ndb_modify_internal: %d %s\n",
+ rc, *text, 0);
+ }
+ break;
+
+ case SLAP_MOD_SOFTADD:
+ Debug(LDAP_DEBUG_ARGS,
+ "ndb_modify_internal: softadd %s\n",
+ mod->sm_desc->ad_cname.bv_val, 0, 0);
+ mod->sm_op = LDAP_MOD_ADD;
+
+ rc = modify_add_values( NA->e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+
+ mod->sm_op = SLAP_MOD_SOFTADD;
+
+ if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
+ rc = LDAP_SUCCESS;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
+ rc, *text, 0);
+ }
+ break;
+
+ case SLAP_MOD_SOFTDEL:
+ Debug(LDAP_DEBUG_ARGS,
+ "ndb_modify_internal: softdel %s\n",
+ mod->sm_desc->ad_cname.bv_val, 0, 0);
+ mod->sm_op = LDAP_MOD_DELETE;
+
+ rc = modify_delete_values( NA->e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+
+ mod->sm_op = SLAP_MOD_SOFTDEL;
+
+ if ( rc == LDAP_NO_SUCH_ATTRIBUTE) {
+ rc = LDAP_SUCCESS;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
+ rc, *text, 0);
+ }
+ break;
+
+ case SLAP_MOD_ADD_IF_NOT_PRESENT:
+ Debug(LDAP_DEBUG_ARGS,
+ "ndb_modify_internal: add_if_not_present %s\n",
+ mod->sm_desc->ad_cname.bv_val, 0, 0);
+ if ( attr_find( NA->e->e_attrs, mod->sm_desc ) ) {
+ rc = LDAP_SUCCESS;
+ break;
+ }
+
+ mod->sm_op = LDAP_MOD_ADD;
+
+ rc = modify_add_values( NA->e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+
+ mod->sm_op = SLAP_MOD_ADD_IF_NOT_PRESENT;
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
+ rc, *text, 0);
+ }
+ break;
+
+ default:
+ Debug(LDAP_DEBUG_ANY, "ndb_modify_internal: invalid op %d\n",
+ mod->sm_op, 0, 0);
+ *text = "Invalid modify operation";
+ rc = LDAP_OTHER;
+ Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
+ rc, *text, 0);
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ attrs_free( old );
+ return rc;
+ }
+
+ /* If objectClass was modified, reset the flags */
+ if ( mod->sm_desc == slap_schema.si_ad_objectClass ) {
+ NA->e->e_ocflags = 0;
+ got_oc = 1;
+ }
+ }
+
+ /* check that the entry still obeys the schema */
+ rc = entry_schema_check( op, NA->e, NULL, get_relax(op), 0, NULL,
+ text, textbuf, textlen );
+ if ( rc != LDAP_SUCCESS || op->o_noop ) {
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "entry failed schema check: %s\n",
+ *text, 0, 0 );
+ }
+ attrs_free( old );
+ return rc;
+ }
+
+ if ( got_oc ) {
+ rc = ndb_entry_put_info( op->o_bd, NA, 1 );
+ if ( rc ) {
+ attrs_free( old );
+ return rc;
+ }
+ }
+
+ /* apply modifications to DB */
+ modai = (NdbAttrInfo **)op->o_tmpalloc( nmods * sizeof(NdbAttrInfo*), op->o_tmpmemctx );
+
+ /* Get the unique list of modified attributes */
+ ldap_pvt_thread_rdwr_rlock( &ni->ni_ai_rwlock );
+ for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
+ /* Already took care of objectclass */
+ if ( ml->sml_desc == slap_schema.si_ad_objectClass )
+ continue;
+ for ( i=0; i<nai; i++ ) {
+ if ( ml->sml_desc->ad_type == modai[i]->na_attr )
+ break;
+ }
+ /* This attr was already updated */
+ if ( i < nai )
+ continue;
+ modai[nai] = ndb_ai_find( ni, ml->sml_desc->ad_type );
+ if ( modai[nai]->na_flag & NDB_INFO_INDEX )
+ indexed++;
+ nai++;
+ }
+ ldap_pvt_thread_rdwr_runlock( &ni->ni_ai_rwlock );
+
+ /* If got_oc, this was already done above */
+ if ( indexed && !got_oc) {
+ rc = ndb_entry_put_info( op->o_bd, NA, 1 );
+ if ( rc ) {
+ attrs_free( old );
+ return rc;
+ }
+ }
+
+ myDict = NA->ndb->getDictionary();
+
+ /* sort modai so that OcInfo's are contiguous */
+ {
+ int j, k;
+ for ( i=0; i<nai; i++ ) {
+ for ( j=i+1; j<nai; j++ ) {
+ if ( modai[i]->na_oi == modai[j]->na_oi )
+ continue;
+ for ( k=j+1; k<nai; k++ ) {
+ if ( modai[i]->na_oi == modai[k]->na_oi ) {
+ atmp = modai[j];
+ modai[j] = modai[k];
+ modai[k] = atmp;
+ break;
+ }
+ }
+ /* there are no more na_oi's that match modai[i] */
+ if ( k == nai ) {
+ i = j;
+ }
+ }
+ }
+ }
+
+ /* One call per table... */
+ for ( i=0; i<nai; i += j ) {
+ atmp = modai[i];
+ for ( j=i+1; j<nai; j++ )
+ if ( atmp->na_oi != modai[j]->na_oi )
+ break;
+ j -= i;
+ myTable = myDict->getTable( atmp->na_oi->no_table.bv_val );
+ if ( !myTable )
+ continue;
+ rc = ndb_oc_attrs( NA->txn, myTable, NA->e, atmp->na_oi, &modai[i], j, old );
+ if ( rc ) break;
+ }
+ attrs_free( old );
+ return rc;
+}
+
+
+int
+ndb_back_modify( Operation *op, SlapReply *rs )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ Entry e = {0};
+ int manageDSAit = get_manageDSAit( op );
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof textbuf;
+
+ int num_retries = 0;
+
+ NdbArgs NA;
+ NdbRdns rdns;
+ struct berval matched;
+
+ LDAPControl **preread_ctrl = NULL;
+ LDAPControl **postread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+
+ Debug( LDAP_DEBUG_ARGS, LDAP_XSTRING(ndb_back_modify) ": %s\n",
+ op->o_req_dn.bv_val, 0, 0 );
+
+ ctrls[num_ctrls] = NULL;
+
+ slap_mods_opattrs( op, &op->orm_modlist, 1 );
+
+ e.e_name = op->o_req_dn;
+ e.e_nname = op->o_req_ndn;
+
+ /* Get our NDB handle */
+ rs->sr_err = ndb_thread_handle( op, &NA.ndb );
+ rdns.nr_num = 0;
+ NA.rdns = &rdns;
+ NA.e = &e;
+
+ if( 0 ) {
+retry: /* transaction retry */
+ NA.txn->close();
+ NA.txn = NULL;
+ if( e.e_attrs ) {
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ }
+ Debug(LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modify) ": retrying...\n", 0, 0, 0);
+ if ( op->o_abandon ) {
+ rs->sr_err = SLAPD_ABANDON;
+ goto return_results;
+ }
+ if ( NA.ocs ) {
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+ }
+ ndb_trans_backoff( ++num_retries );
+ }
+ NA.ocs = NULL;
+
+ /* begin transaction */
+ NA.txn = NA.ndb->startTransaction();
+ rs->sr_text = NULL;
+ if( !NA.txn ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modify) ": startTransaction failed: %s (%d)\n",
+ NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* get entry or ancestor */
+ rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched );
+ switch( rs->sr_err ) {
+ case 0:
+ break;
+ case LDAP_NO_SUCH_OBJECT:
+ Debug( LDAP_DEBUG_ARGS,
+ "<=- ndb_back_modify: no such object %s\n",
+ op->o_req_dn.bv_val, 0, 0 );
+ rs->sr_matched = matched.bv_val;
+ if (NA.ocs )
+ ndb_check_referral( op, rs, &NA );
+ goto return_results;
+#if 0
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto retry;
+#endif
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* acquire and lock entry */
+ rs->sr_err = ndb_entry_get_data( op, &NA, 1 );
+
+ if ( !manageDSAit && is_entry_referral( &e ) ) {
+ /* entry is a referral, don't allow modify */
+ rs->sr_ref = get_entry_referrals( op, &e );
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modify) ": entry is referral\n",
+ 0, 0, 0 );
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_matched = e.e_name.bv_val;
+ rs->sr_flags = REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, &e, (Filter*)get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+ if( op->o_preread ) {
+ if( preread_ctrl == NULL ) {
+ preread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if ( slap_read_controls( op, rs, &e,
+ &slap_pre_read_bv, preread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_modify) ": pre-read "
+ "failed!\n", 0, 0, 0 );
+ if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ /* Modify the entry */
+ rs->sr_err = ndb_modify_internal( op, &NA, &rs->sr_text, textbuf, textlen );
+
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modify) ": modify failed (%d)\n",
+ rs->sr_err, 0, 0 );
+#if 0
+ switch( rs->sr_err ) {
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto retry;
+ }
+#endif
+ goto return_results;
+ }
+
+ if( op->o_postread ) {
+ if( postread_ctrl == NULL ) {
+ postread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if( slap_read_controls( op, rs, &e,
+ &slap_post_read_bv, postread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_modify)
+ ": post-read failed!\n", 0, 0, 0 );
+ if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ if( op->o_noop ) {
+ if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback,
+ NdbOperation::AbortOnError, 1 )) != 0 ) {
+ rs->sr_text = "txn_abort (no-op) failed";
+ } else {
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ }
+ } else {
+ if (( rs->sr_err=NA.txn->execute( NdbTransaction::Commit,
+ NdbOperation::AbortOnError, 1 )) != 0 ) {
+ rs->sr_text = "txn_commit failed";
+ } else {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+ }
+
+ if( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modify) ": txn_%s failed: %s (%d)\n",
+ op->o_noop ? "abort (no-op)" : "commit",
+ NA.txn->getNdbError().message, NA.txn->getNdbError().code );
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+ NA.txn->close();
+ NA.txn = NULL;
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modify) ": updated%s id=%08lx dn=\"%s\"\n",
+ op->o_noop ? " (no-op)" : "",
+ e.e_id, op->o_req_dn.bv_val );
+
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+ if ( NA.ocs ) {
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+ NA.ocs = NULL;
+ }
+
+ if ( e.e_attrs != NULL ) {
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ }
+
+ if( NA.txn != NULL ) {
+ NA.txn->execute( Rollback );
+ NA.txn->close();
+ }
+
+ send_ldap_result( op, rs );
+ slap_graduate_commit_csn( op );
+
+ if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
+ slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
+ }
+ if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
+ slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
+ }
+
+ rs->sr_text = NULL;
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-ndb/modrdn.cpp b/servers/slapd/back-ndb/modrdn.cpp
new file mode 100644
index 0000000..ecc3c8e
--- /dev/null
+++ b/servers/slapd/back-ndb/modrdn.cpp
@@ -0,0 +1,558 @@
+/* modrdn.cpp - ndb backend modrdn routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-ndb.h"
+
+int
+ndb_back_modrdn( Operation *op, SlapReply *rs )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ AttributeDescription *children = slap_schema.si_ad_children;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+ struct berval new_dn = BER_BVNULL, new_ndn = BER_BVNULL;
+ Entry e = {0};
+ Entry e2 = {0};
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof textbuf;
+
+ struct berval *np_dn = NULL; /* newSuperior dn */
+ struct berval *np_ndn = NULL; /* newSuperior ndn */
+
+ int manageDSAit = get_manageDSAit( op );
+ int num_retries = 0;
+
+ NdbArgs NA, NA2;
+ NdbRdns rdns, rdn2;
+ struct berval matched;
+
+ LDAPControl **preread_ctrl = NULL;
+ LDAPControl **postread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+
+ int rc;
+
+ Debug( LDAP_DEBUG_ARGS, "==>" LDAP_XSTRING(ndb_back_modrdn) "(%s,%s,%s)\n",
+ op->o_req_dn.bv_val,op->oq_modrdn.rs_newrdn.bv_val,
+ op->oq_modrdn.rs_newSup ? op->oq_modrdn.rs_newSup->bv_val : "NULL" );
+
+ ctrls[num_ctrls] = NULL;
+
+ slap_mods_opattrs( op, &op->orr_modlist, 1 );
+
+ e.e_name = op->o_req_dn;
+ e.e_nname = op->o_req_ndn;
+
+ /* Get our NDB handle */
+ rs->sr_err = ndb_thread_handle( op, &NA.ndb );
+ rdns.nr_num = 0;
+ NA.rdns = &rdns;
+ NA.e = &e;
+ NA2.ndb = NA.ndb;
+ NA2.e = &e2;
+ NA2.rdns = &rdn2;
+
+ if( 0 ) {
+retry: /* transaction retry */
+ NA.txn->close();
+ NA.txn = NULL;
+ if ( e.e_attrs ) {
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ }
+ Debug( LDAP_DEBUG_TRACE, "==>" LDAP_XSTRING(ndb_back_modrdn)
+ ": retrying...\n", 0, 0, 0 );
+ if ( op->o_abandon ) {
+ rs->sr_err = SLAPD_ABANDON;
+ goto return_results;
+ }
+ if ( NA2.ocs ) {
+ ber_bvarray_free_x( NA2.ocs, op->o_tmpmemctx );
+ }
+ if ( NA.ocs ) {
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+ }
+ ndb_trans_backoff( ++num_retries );
+ }
+ NA.ocs = NULL;
+ NA2.ocs = NULL;
+
+ /* begin transaction */
+ NA.txn = NA.ndb->startTransaction();
+ rs->sr_text = NULL;
+ if( !NA.txn ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modrdn) ": startTransaction failed: %s (%d)\n",
+ NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ NA2.txn = NA.txn;
+
+ /* get entry */
+ rs->sr_err = ndb_entry_get_info( op, &NA, 1, &matched );
+ switch( rs->sr_err ) {
+ case 0:
+ break;
+ case LDAP_NO_SUCH_OBJECT:
+ Debug( LDAP_DEBUG_ARGS,
+ "<=- ndb_back_modrdn: no such object %s\n",
+ op->o_req_dn.bv_val, 0, 0 );
+ rs->sr_matched = matched.bv_val;
+ if ( NA.ocs )
+ ndb_check_referral( op, rs, &NA );
+ goto return_results;
+#if 0
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto retry;
+#endif
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* acquire and lock entry */
+ rs->sr_err = ndb_entry_get_data( op, &NA, 1 );
+ if ( rs->sr_err )
+ goto return_results;
+
+ if ( !manageDSAit && is_entry_glue( &e )) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto return_results;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, &e, (Filter *)get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+ /* check write on old entry */
+ rs->sr_err = access_allowed( op, &e, entry, NULL, ACL_WRITE, NULL );
+ if ( ! rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE, "no access to entry\n", 0,
+ 0, 0 );
+ rs->sr_text = "no write access to old entry";
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto return_results;
+ }
+
+ /* Can't do it if we have kids */
+ rs->sr_err = ndb_has_children( &NA, &rc );
+ if ( rs->sr_err ) {
+ Debug(LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(ndb_back_modrdn)
+ ": has_children failed: %s (%d)\n",
+ NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ Debug(LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(ndb_back_modrdn)
+ ": non-leaf %s\n",
+ op->o_req_dn.bv_val, 0, 0);
+ rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
+ rs->sr_text = "subtree rename not supported";
+ goto return_results;
+ }
+
+ if (!manageDSAit && is_entry_referral( &e ) ) {
+ /* entry is a referral, don't allow modrdn */
+ rs->sr_ref = get_entry_referrals( op, &e );
+
+ Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(ndb_back_modrdn)
+ ": entry %s is referral\n", e.e_dn, 0, 0 );
+
+ rs->sr_err = LDAP_REFERRAL,
+ rs->sr_matched = op->o_req_dn.bv_val;
+ rs->sr_flags = REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+
+ if ( be_issuffix( op->o_bd, &e.e_nname ) ) {
+ /* There can only be one suffix entry */
+ rs->sr_err = LDAP_NAMING_VIOLATION;
+ rs->sr_text = "cannot rename suffix entry";
+ goto return_results;
+ } else {
+ dnParent( &e.e_nname, &e2.e_nname );
+ dnParent( &e.e_name, &e2.e_name );
+ }
+
+ /* check parent for "children" acl */
+ rs->sr_err = access_allowed( op, &e2,
+ children, NULL,
+ op->oq_modrdn.rs_newSup == NULL ?
+ ACL_WRITE : ACL_WDEL,
+ NULL );
+
+ if ( ! rs->sr_err ) {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
+ 0, 0 );
+ rs->sr_text = "no write access to old parent's children";
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modrdn) ": wr to children "
+ "of entry %s OK\n", e2.e_name.bv_val, 0, 0 );
+
+ if ( op->oq_modrdn.rs_newSup != NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modrdn)
+ ": new parent \"%s\" requested...\n",
+ op->oq_modrdn.rs_newSup->bv_val, 0, 0 );
+
+ /* newSuperior == oldParent? */
+ if( dn_match( &e2.e_nname, op->oq_modrdn.rs_nnewSup ) ) {
+ Debug( LDAP_DEBUG_TRACE, "bdb_back_modrdn: "
+ "new parent \"%s\" same as the old parent \"%s\"\n",
+ op->oq_modrdn.rs_newSup->bv_val, e2.e_name.bv_val, 0 );
+ op->oq_modrdn.rs_newSup = NULL; /* ignore newSuperior */
+ }
+ }
+
+ if ( op->oq_modrdn.rs_newSup != NULL ) {
+ if ( op->oq_modrdn.rs_newSup->bv_len ) {
+ rdn2.nr_num = 0;
+ np_dn = op->oq_modrdn.rs_newSup;
+ np_ndn = op->oq_modrdn.rs_nnewSup;
+
+ /* newSuperior == oldParent? - checked above */
+ /* newSuperior == entry being moved?, if so ==> ERROR */
+ if ( dnIsSuffix( np_ndn, &e.e_nname )) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = "new superior not found";
+ goto return_results;
+ }
+ /* Get Entry with dn=newSuperior. Does newSuperior exist? */
+
+ e2.e_name = *np_dn;
+ e2.e_nname = *np_ndn;
+ rs->sr_err = ndb_entry_get_info( op, &NA2, 1, NULL );
+ switch( rs->sr_err ) {
+ case 0:
+ break;
+ case LDAP_NO_SUCH_OBJECT:
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modrdn)
+ ": newSup(ndn=%s) not here!\n",
+ np_ndn->bv_val, 0, 0);
+ rs->sr_text = "new superior not found";
+ goto return_results;
+#if 0
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto retry;
+#endif
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ if ( NA2.ocs ) {
+ Attribute a;
+ int i;
+
+ for ( i=0; !BER_BVISNULL( &NA2.ocs[i] ); i++);
+ a.a_numvals = i;
+ a.a_desc = slap_schema.si_ad_objectClass;
+ a.a_vals = NA2.ocs;
+ a.a_nvals = NA2.ocs;
+ a.a_next = NULL;
+ e2.e_attrs = &a;
+
+ if ( is_entry_alias( &e2 )) {
+ /* parent is an alias, don't allow move */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modrdn)
+ ": entry is alias\n",
+ 0, 0, 0 );
+ rs->sr_text = "new superior is an alias";
+ rs->sr_err = LDAP_ALIAS_PROBLEM;
+ goto return_results;
+ }
+
+ if ( is_entry_referral( &e2 ) ) {
+ /* parent is a referral, don't allow move */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modrdn)
+ ": entry is referral\n",
+ 0, 0, 0 );
+ rs->sr_text = "new superior is a referral";
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+ }
+ }
+
+ /* check newSuperior for "children" acl */
+ rs->sr_err = access_allowed( op, &e2, children,
+ NULL, ACL_WADD, NULL );
+ if( ! rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modrdn)
+ ": no wr to newSup children\n",
+ 0, 0, 0 );
+ rs->sr_text = "no write access to new superior's children";
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modrdn)
+ ": wr to new parent OK id=%ld\n",
+ (long) e2.e_id, 0, 0 );
+ }
+
+ /* Build target dn and make sure target entry doesn't exist already. */
+ if (!new_dn.bv_val) {
+ build_new_dn( &new_dn, &e2.e_name, &op->oq_modrdn.rs_newrdn, NULL );
+ }
+
+ if (!new_ndn.bv_val) {
+ build_new_dn( &new_ndn, &e2.e_nname, &op->oq_modrdn.rs_nnewrdn, NULL );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(ndb_back_modrdn) ": new ndn=%s\n",
+ new_ndn.bv_val, 0, 0 );
+
+ /* Allow rename to same DN */
+ if ( !bvmatch ( &new_ndn, &e.e_nname )) {
+ rdn2.nr_num = 0;
+ e2.e_name = new_dn;
+ e2.e_nname = new_ndn;
+ NA2.ocs = &matched;
+ rs->sr_err = ndb_entry_get_info( op, &NA2, 1, NULL );
+ NA2.ocs = NULL;
+ switch( rs->sr_err ) {
+#if 0
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto retry;
+#endif
+ case LDAP_NO_SUCH_OBJECT:
+ break;
+ case 0:
+ rs->sr_err = LDAP_ALREADY_EXISTS;
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ }
+
+ assert( op->orr_modlist != NULL );
+
+ if( op->o_preread ) {
+ if( preread_ctrl == NULL ) {
+ preread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if( slap_read_controls( op, rs, &e,
+ &slap_pre_read_bv, preread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_modrdn)
+ ": pre-read failed!\n", 0, 0, 0 );
+ if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ /* delete old DN */
+ rs->sr_err = ndb_entry_del_info( op->o_bd, &NA );
+ if ( rs->sr_err != 0 ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_modrdn)
+ ": dn2id del failed: %s (%d)\n",
+ NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
+#if 0
+ switch( rs->sr_err ) {
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto retry;
+ }
+#endif
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "DN index delete fail";
+ goto return_results;
+ }
+
+ /* copy entry fields */
+ e2.e_attrs = e.e_attrs;
+ e2.e_id = e.e_id;
+
+ /* add new DN */
+ rs->sr_err = ndb_entry_put_info( op->o_bd, &NA2, 0 );
+ if ( rs->sr_err != 0 ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_modrdn)
+ ": dn2id add failed: %s (%d)\n",
+ NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
+#if 0
+ switch( rs->sr_err ) {
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto retry;
+ }
+#endif
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "DN index add failed";
+ goto return_results;
+ }
+
+ /* modify entry */
+ rs->sr_err = ndb_modify_internal( op, &NA2,
+ &rs->sr_text, textbuf, textlen );
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_modrdn)
+ ": modify failed: %s (%d)\n",
+ NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
+#if 0
+ switch( rs->sr_err ) {
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto retry;
+ }
+#endif
+ goto return_results;
+ }
+
+ e.e_attrs = e2.e_attrs;
+
+ if( op->o_postread ) {
+ if( postread_ctrl == NULL ) {
+ postread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if( slap_read_controls( op, rs, &e2,
+ &slap_post_read_bv, postread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_modrdn)
+ ": post-read failed!\n", 0, 0, 0 );
+ if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ if( op->o_noop ) {
+ if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback,
+ NdbOperation::AbortOnError, 1 )) != 0 ) {
+ rs->sr_text = "txn_abort (no-op) failed";
+ } else {
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ }
+ } else {
+ if (( rs->sr_err=NA.txn->execute( NdbTransaction::Commit,
+ NdbOperation::AbortOnError, 1 )) != 0 ) {
+ rs->sr_text = "txn_commit failed";
+ } else {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+ }
+
+ if( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modrdn) ": txn_%s failed: %s (%d)\n",
+ op->o_noop ? "abort (no-op)" : "commit",
+ NA.txn->getNdbError().message, NA.txn->getNdbError().code );
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+ NA.txn->close();
+ NA.txn = NULL;
+
+ Debug(LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modrdn)
+ ": rdn modified%s id=%08lx dn=\"%s\"\n",
+ op->o_noop ? " (no-op)" : "",
+ e.e_id, op->o_req_dn.bv_val );
+
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+ if ( NA2.ocs ) {
+ ber_bvarray_free_x( NA2.ocs, op->o_tmpmemctx );
+ NA2.ocs = NULL;
+ }
+
+ if ( NA.ocs ) {
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+ NA.ocs = NULL;
+ }
+
+ if ( e.e_attrs ) {
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ }
+
+ if( NA.txn != NULL ) {
+ NA.txn->execute( Rollback );
+ NA.txn->close();
+ }
+
+ send_ldap_result( op, rs );
+ slap_graduate_commit_csn( op );
+
+ if( new_dn.bv_val != NULL ) free( new_dn.bv_val );
+ if( new_ndn.bv_val != NULL ) free( new_ndn.bv_val );
+
+ if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
+ slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
+ }
+ if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
+ slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
+ }
+
+ rs->sr_text = NULL;
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-ndb/ndbio.cpp b/servers/slapd/back-ndb/ndbio.cpp
new file mode 100644
index 0000000..53559c5
--- /dev/null
+++ b/servers/slapd/back-ndb/ndbio.cpp
@@ -0,0 +1,1677 @@
+/* ndbio.cpp - get/set/del data for NDB */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/errno.h>
+#include <lutil.h>
+
+#include "back-ndb.h"
+
+/* For reference only */
+typedef struct MedVar {
+ Int16 len; /* length is always little-endian */
+ char buf[1024];
+} MedVar;
+
+extern "C" {
+ static int ndb_name_cmp( const void *v1, const void *v2 );
+ static int ndb_oc_dup_err( void *v1, void *v2 );
+};
+
+static int
+ndb_name_cmp( const void *v1, const void *v2 )
+{
+ NdbOcInfo *oc1 = (NdbOcInfo *)v1, *oc2 = (NdbOcInfo *)v2;
+ return ber_bvstrcasecmp( &oc1->no_name, &oc2->no_name );
+}
+
+static int
+ndb_oc_dup_err( void *v1, void *v2 )
+{
+ NdbOcInfo *oc = (NdbOcInfo *)v2;
+
+ oc->no_oc = (ObjectClass *)v1;
+ return -1;
+}
+
+/* Find an existing NdbAttrInfo */
+extern "C" NdbAttrInfo *
+ndb_ai_find( struct ndb_info *ni, AttributeType *at )
+{
+ NdbAttrInfo atmp;
+ atmp.na_name = at->sat_cname;
+
+ return (NdbAttrInfo *)avl_find( ni->ni_ai_tree, &atmp, ndb_name_cmp );
+}
+
+/* Find or create an NdbAttrInfo */
+extern "C" NdbAttrInfo *
+ndb_ai_get( struct ndb_info *ni, struct berval *aname )
+{
+ NdbAttrInfo atmp, *ai;
+ atmp.na_name = *aname;
+
+ ai = (NdbAttrInfo *)avl_find( ni->ni_ai_tree, &atmp, ndb_name_cmp );
+ if ( !ai ) {
+ const char *text;
+ AttributeDescription *ad = NULL;
+
+ if ( slap_bv2ad( aname, &ad, &text ))
+ return NULL;
+
+ ai = (NdbAttrInfo *)ch_malloc( sizeof( NdbAttrInfo ));
+ ai->na_desc = ad;
+ ai->na_attr = ai->na_desc->ad_type;
+ ai->na_name = ai->na_attr->sat_cname;
+ ai->na_oi = NULL;
+ ai->na_flag = 0;
+ ai->na_ixcol = 0;
+ ai->na_len = ai->na_attr->sat_atype.at_syntax_len;
+ /* Reasonable default */
+ if ( !ai->na_len ) {
+ if ( ai->na_attr->sat_syntax == slap_schema.si_syn_distinguishedName )
+ ai->na_len = 1024;
+ else
+ ai->na_len = 128;
+ }
+ /* Arbitrary limit */
+ if ( ai->na_len > 1024 )
+ ai->na_len = 1024;
+ avl_insert( &ni->ni_ai_tree, ai, ndb_name_cmp, avl_dup_error );
+ }
+ return ai;
+}
+
+static int
+ndb_ai_check( struct ndb_info *ni, NdbOcInfo *oci, AttributeType **attrs, char **ptr, int *col,
+ int create )
+{
+ NdbAttrInfo *ai;
+ int i;
+
+ for ( i=0; attrs[i]; i++ ) {
+ if ( attrs[i] == slap_schema.si_ad_objectClass->ad_type )
+ continue;
+ /* skip attrs that are in a superior */
+ if ( oci->no_oc && oci->no_oc->soc_sups ) {
+ int j, k, found=0;
+ ObjectClass *oc;
+ for ( j=0; oci->no_oc->soc_sups[j]; j++ ) {
+ oc = oci->no_oc->soc_sups[j];
+ if ( oc->soc_kind == LDAP_SCHEMA_ABSTRACT )
+ continue;
+ if ( oc->soc_required ) {
+ for ( k=0; oc->soc_required[k]; k++ ) {
+ if ( attrs[i] == oc->soc_required[k] ) {
+ found = 1;
+ break;
+ }
+ }
+ if ( found ) break;
+ }
+ if ( oc->soc_allowed ) {
+ for ( k=0; oc->soc_allowed[k]; k++ ) {
+ if ( attrs[i] == oc->soc_allowed[k] ) {
+ found = 1;
+ break;
+ }
+ }
+ if ( found ) break;
+ }
+ }
+ if ( found )
+ continue;
+ }
+
+ ai = ndb_ai_get( ni, &attrs[i]->sat_cname );
+ if ( !ai ) {
+ /* can never happen */
+ return LDAP_OTHER;
+ }
+
+ /* An attrset may have already been connected */
+ if (( oci->no_flag & NDB_INFO_ATSET ) && ai->na_oi == oci )
+ continue;
+
+ /* An indexed attr is defined before its OC is */
+ if ( !ai->na_oi ) {
+ ai->na_oi = oci;
+ ai->na_column = (*col)++;
+ }
+
+ oci->no_attrs[oci->no_nattrs++] = ai;
+
+ /* An attrset attr may already be defined */
+ if ( ai->na_oi != oci ) {
+ int j;
+ for ( j=0; j<oci->no_nsets; j++ )
+ if ( oci->no_sets[j] == ai->na_oi ) break;
+ if ( j >= oci->no_nsets ) {
+ /* FIXME: data loss if more sets are in use */
+ if ( oci->no_nsets < NDB_MAX_OCSETS ) {
+ oci->no_sets[oci->no_nsets++] = ai->na_oi;
+ }
+ }
+ continue;
+ }
+
+ if ( create ) {
+ if ( ai->na_flag & NDB_INFO_ATBLOB ) {
+ *ptr += sprintf( *ptr, ", `%s` BLOB", ai->na_attr->sat_cname.bv_val );
+ } else {
+ *ptr += sprintf( *ptr, ", `%s` VARCHAR(%d)", ai->na_attr->sat_cname.bv_val,
+ ai->na_len );
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+ndb_oc_create( struct ndb_info *ni, NdbOcInfo *oci, int create )
+{
+ char buf[4096], *ptr;
+ int i, rc = 0, col;
+
+ if ( create ) {
+ ptr = buf + sprintf( buf,
+ "CREATE TABLE `%s` (eid bigint unsigned NOT NULL, vid int unsigned NOT NULL",
+ oci->no_table.bv_val );
+ }
+
+ col = 0;
+ if ( oci->no_oc->soc_required ) {
+ for ( i=0; oci->no_oc->soc_required[i]; i++ );
+ col += i;
+ }
+ if ( oci->no_oc->soc_allowed ) {
+ for ( i=0; oci->no_oc->soc_allowed[i]; i++ );
+ col += i;
+ }
+ /* assume all are present */
+ oci->no_attrs = (struct ndb_attrinfo **)ch_malloc( col * sizeof(struct ndb_attrinfo *));
+
+ col = 2;
+ ldap_pvt_thread_rdwr_wlock( &ni->ni_ai_rwlock );
+ if ( oci->no_oc->soc_required ) {
+ rc = ndb_ai_check( ni, oci, oci->no_oc->soc_required, &ptr, &col, create );
+ }
+ if ( !rc && oci->no_oc->soc_allowed ) {
+ rc = ndb_ai_check( ni, oci, oci->no_oc->soc_allowed, &ptr, &col, create );
+ }
+ ldap_pvt_thread_rdwr_wunlock( &ni->ni_ai_rwlock );
+
+ /* shrink down to just the needed size */
+ oci->no_attrs = (struct ndb_attrinfo **)ch_realloc( oci->no_attrs,
+ oci->no_nattrs * sizeof(struct ndb_attrinfo *));
+
+ if ( create ) {
+ ptr = lutil_strcopy( ptr, ", PRIMARY KEY(eid, vid) ) ENGINE=ndb PARTITION BY KEY(eid)" );
+ rc = mysql_real_query( &ni->ni_sql, buf, ptr - buf );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ndb_oc_create: CREATE TABLE %s failed, %s (%d)\n",
+ oci->no_table.bv_val, mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
+ }
+ }
+ return rc;
+}
+
+/* Read table definitions from the DB and populate ObjectClassInfo */
+extern "C" int
+ndb_oc_read( struct ndb_info *ni, const NdbDictionary::Dictionary *myDict )
+{
+ const NdbDictionary::Table *myTable;
+ const NdbDictionary::Column *myCol;
+ NdbOcInfo *oci, octmp;
+ NdbAttrInfo *ai;
+ ObjectClass *oc;
+ NdbDictionary::Dictionary::List myList;
+ struct berval bv;
+ int i, j, rc, col;
+
+ rc = myDict->listObjects( myList, NdbDictionary::Object::UserTable );
+ /* Populate our objectClass structures */
+ for ( i=0; i<myList.count; i++ ) {
+ /* Ignore other DBs */
+ if ( strcmp( myList.elements[i].database, ni->ni_dbname ))
+ continue;
+ /* Ignore internal tables */
+ if ( !strncmp( myList.elements[i].name, "OL_", 3 ))
+ continue;
+ ber_str2bv( myList.elements[i].name, 0, 0, &octmp.no_name );
+ oci = (NdbOcInfo *)avl_find( ni->ni_oc_tree, &octmp, ndb_name_cmp );
+ if ( oci )
+ continue;
+
+ oc = oc_bvfind( &octmp.no_name );
+ if ( !oc ) {
+ /* undefined - shouldn't happen */
+ continue;
+ }
+ myTable = myDict->getTable( myList.elements[i].name );
+ oci = (NdbOcInfo *)ch_malloc( sizeof( NdbOcInfo )+oc->soc_cname.bv_len+1 );
+ oci->no_table.bv_val = (char *)(oci+1);
+ strcpy( oci->no_table.bv_val, oc->soc_cname.bv_val );
+ oci->no_table.bv_len = oc->soc_cname.bv_len;
+ oci->no_name = oci->no_table;
+ oci->no_oc = oc;
+ oci->no_flag = 0;
+ oci->no_nsets = 0;
+ oci->no_nattrs = 0;
+ col = 0;
+ /* Make space for all attrs, even tho sups will be dropped */
+ if ( oci->no_oc->soc_required ) {
+ for ( j=0; oci->no_oc->soc_required[j]; j++ );
+ col = j;
+ }
+ if ( oci->no_oc->soc_allowed ) {
+ for ( j=0; oci->no_oc->soc_allowed[j]; j++ );
+ col += j;
+ }
+ oci->no_attrs = (struct ndb_attrinfo **)ch_malloc( col * sizeof(struct ndb_attrinfo *));
+ avl_insert( &ni->ni_oc_tree, oci, ndb_name_cmp, avl_dup_error );
+
+ col = myTable->getNoOfColumns();
+ /* Skip 0 and 1, eid and vid */
+ for ( j = 2; j<col; j++ ) {
+ myCol = myTable->getColumn( j );
+ ber_str2bv( myCol->getName(), 0, 0, &bv );
+ ai = ndb_ai_get( ni, &bv );
+ /* shouldn't happen */
+ if ( !ai )
+ continue;
+ ai->na_oi = oci;
+ ai->na_column = j;
+ ai->na_len = myCol->getLength();
+ if ( myCol->getType() == NdbDictionary::Column::Blob )
+ ai->na_flag |= NDB_INFO_ATBLOB;
+ }
+ }
+ /* Link to any attrsets */
+ for ( i=0; i<myList.count; i++ ) {
+ /* Ignore other DBs */
+ if ( strcmp( myList.elements[i].database, ni->ni_dbname ))
+ continue;
+ /* Ignore internal tables */
+ if ( !strncmp( myList.elements[i].name, "OL_", 3 ))
+ continue;
+ ber_str2bv( myList.elements[i].name, 0, 0, &octmp.no_name );
+ oci = (NdbOcInfo *)avl_find( ni->ni_oc_tree, &octmp, ndb_name_cmp );
+ /* shouldn't happen */
+ if ( !oci )
+ continue;
+ col = 2;
+ if ( oci->no_oc->soc_required ) {
+ rc = ndb_ai_check( ni, oci, oci->no_oc->soc_required, NULL, &col, 0 );
+ }
+ if ( oci->no_oc->soc_allowed ) {
+ rc = ndb_ai_check( ni, oci, oci->no_oc->soc_allowed, NULL, &col, 0 );
+ }
+ /* shrink down to just the needed size */
+ oci->no_attrs = (struct ndb_attrinfo **)ch_realloc( oci->no_attrs,
+ oci->no_nattrs * sizeof(struct ndb_attrinfo *));
+ }
+ return 0;
+}
+
+static int
+ndb_oc_list( struct ndb_info *ni, const NdbDictionary::Dictionary *myDict,
+ struct berval *oname, int implied, NdbOcs *out )
+{
+ const NdbDictionary::Table *myTable;
+ NdbOcInfo *oci, octmp;
+ ObjectClass *oc;
+ int i, rc;
+
+ /* shortcut top */
+ if ( ber_bvstrcasecmp( oname, &slap_schema.si_oc_top->soc_cname )) {
+ octmp.no_name = *oname;
+ oci = (NdbOcInfo *)avl_find( ni->ni_oc_tree, &octmp, ndb_name_cmp );
+ if ( oci ) {
+ oc = oci->no_oc;
+ } else {
+ oc = oc_bvfind( oname );
+ if ( !oc ) {
+ /* undefined - shouldn't happen */
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+ if ( oc->soc_sups ) {
+ int i;
+
+ for ( i=0; oc->soc_sups[i]; i++ ) {
+ rc = ndb_oc_list( ni, myDict, &oc->soc_sups[i]->soc_cname, 1, out );
+ if ( rc ) return rc;
+ }
+ }
+ } else {
+ oc = slap_schema.si_oc_top;
+ }
+ /* Only insert once */
+ for ( i=0; i<out->no_ntext; i++ )
+ if ( out->no_text[i].bv_val == oc->soc_cname.bv_val )
+ break;
+ if ( i == out->no_ntext ) {
+ for ( i=0; i<out->no_nitext; i++ )
+ if ( out->no_itext[i].bv_val == oc->soc_cname.bv_val )
+ break;
+ if ( i == out->no_nitext ) {
+ if ( implied )
+ out->no_itext[out->no_nitext++] = oc->soc_cname;
+ else
+ out->no_text[out->no_ntext++] = oc->soc_cname;
+ }
+ }
+
+ /* ignore top, etc... */
+ if ( oc->soc_kind == LDAP_SCHEMA_ABSTRACT )
+ return 0;
+
+ if ( !oci ) {
+ ldap_pvt_thread_rdwr_runlock( &ni->ni_oc_rwlock );
+ oci = (NdbOcInfo *)ch_malloc( sizeof( NdbOcInfo )+oc->soc_cname.bv_len+1 );
+ oci->no_table.bv_val = (char *)(oci+1);
+ strcpy( oci->no_table.bv_val, oc->soc_cname.bv_val );
+ oci->no_table.bv_len = oc->soc_cname.bv_len;
+ oci->no_name = oci->no_table;
+ oci->no_oc = oc;
+ oci->no_flag = 0;
+ oci->no_nsets = 0;
+ oci->no_nattrs = 0;
+ ldap_pvt_thread_rdwr_wlock( &ni->ni_oc_rwlock );
+ if ( avl_insert( &ni->ni_oc_tree, oci, ndb_name_cmp, ndb_oc_dup_err )) {
+ octmp.no_oc = oci->no_oc;
+ ch_free( oci );
+ oci = (NdbOcInfo *)octmp.no_oc;
+ }
+ /* see if the oc table already exists in the DB */
+ myTable = myDict->getTable( oci->no_table.bv_val );
+ rc = ndb_oc_create( ni, oci, myTable == NULL );
+ ldap_pvt_thread_rdwr_wunlock( &ni->ni_oc_rwlock );
+ ldap_pvt_thread_rdwr_rlock( &ni->ni_oc_rwlock );
+ if ( rc ) return rc;
+ }
+ /* Only insert once */
+ for ( i=0; i<out->no_ninfo; i++ )
+ if ( out->no_info[i] == oci )
+ break;
+ if ( i == out->no_ninfo )
+ out->no_info[out->no_ninfo++] = oci;
+ return 0;
+}
+
+extern "C" int
+ndb_aset_get( struct ndb_info *ni, struct berval *sname, struct berval *attrs, NdbOcInfo **ret )
+{
+ NdbOcInfo *oci, octmp;
+ int i, rc;
+
+ octmp.no_name = *sname;
+ oci = (NdbOcInfo *)avl_find( ni->ni_oc_tree, &octmp, ndb_name_cmp );
+ if ( oci )
+ return LDAP_ALREADY_EXISTS;
+
+ for ( i=0; !BER_BVISNULL( &attrs[i] ); i++ ) {
+ if ( !at_bvfind( &attrs[i] ))
+ return LDAP_NO_SUCH_ATTRIBUTE;
+ }
+ i++;
+
+ oci = (NdbOcInfo *)ch_calloc( 1, sizeof( NdbOcInfo ) + sizeof( ObjectClass ) +
+ i*sizeof(AttributeType *) + sname->bv_len+1 );
+ oci->no_oc = (ObjectClass *)(oci+1);
+ oci->no_oc->soc_required = (AttributeType **)(oci->no_oc+1);
+ oci->no_table.bv_val = (char *)(oci->no_oc->soc_required+i);
+
+ for ( i=0; !BER_BVISNULL( &attrs[i] ); i++ )
+ oci->no_oc->soc_required[i] = at_bvfind( &attrs[i] );
+
+ strcpy( oci->no_table.bv_val, sname->bv_val );
+ oci->no_table.bv_len = sname->bv_len;
+ oci->no_name = oci->no_table;
+ oci->no_oc->soc_cname = oci->no_name;
+ oci->no_flag = NDB_INFO_ATSET;
+
+ if ( !ber_bvcmp( sname, &slap_schema.si_oc_extensibleObject->soc_cname ))
+ oci->no_oc->soc_kind = slap_schema.si_oc_extensibleObject->soc_kind;
+
+ rc = ndb_oc_create( ni, oci, 0 );
+ if ( !rc )
+ rc = avl_insert( &ni->ni_oc_tree, oci, ndb_name_cmp, avl_dup_error );
+ if ( rc ) {
+ ch_free( oci );
+ } else {
+ *ret = oci;
+ }
+ return rc;
+}
+
+extern "C" int
+ndb_aset_create( struct ndb_info *ni, NdbOcInfo *oci )
+{
+ char buf[4096], *ptr;
+ NdbAttrInfo *ai;
+ int i;
+
+ ptr = buf + sprintf( buf,
+ "CREATE TABLE IF NOT EXISTS `%s` (eid bigint unsigned NOT NULL, vid int unsigned NOT NULL",
+ oci->no_table.bv_val );
+
+ for ( i=0; i<oci->no_nattrs; i++ ) {
+ if ( oci->no_attrs[i]->na_oi != oci )
+ continue;
+ ai = oci->no_attrs[i];
+ ptr += sprintf( ptr, ", `%s` VARCHAR(%d)", ai->na_attr->sat_cname.bv_val,
+ ai->na_len );
+ if ( ai->na_flag & NDB_INFO_INDEX ) {
+ ptr += sprintf( ptr, ", INDEX (`%s`)", ai->na_attr->sat_cname.bv_val );
+ }
+ }
+ ptr = lutil_strcopy( ptr, ", PRIMARY KEY(eid, vid) ) ENGINE=ndb PARTITION BY KEY(eid)" );
+ i = mysql_real_query( &ni->ni_sql, buf, ptr - buf );
+ if ( i ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ndb_aset_create: CREATE TABLE %s failed, %s (%d)\n",
+ oci->no_table.bv_val, mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
+ }
+ return i;
+}
+
+static int
+ndb_oc_check( BackendDB *be, Ndb *ndb,
+ struct berval *ocsin, NdbOcs *out )
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ const NdbDictionary::Dictionary *myDict = ndb->getDictionary();
+
+ int i, rc = 0;
+
+ out->no_ninfo = 0;
+ out->no_ntext = 0;
+ out->no_nitext = 0;
+
+ /* Find all objectclasses and their superiors. List
+ * the superiors first.
+ */
+
+ ldap_pvt_thread_rdwr_rlock( &ni->ni_oc_rwlock );
+ for ( i=0; !BER_BVISNULL( &ocsin[i] ); i++ ) {
+ rc = ndb_oc_list( ni, myDict, &ocsin[i], 0, out );
+ if ( rc ) break;
+ }
+ ldap_pvt_thread_rdwr_runlock( &ni->ni_oc_rwlock );
+ return rc;
+}
+
+#define V_INS 1
+#define V_DEL 2
+#define V_REP 3
+
+static int ndb_flush_blobs;
+
+/* set all the unique attrs of this objectclass into the table
+ */
+extern "C" int
+ndb_oc_attrs(
+ NdbTransaction *txn,
+ const NdbDictionary::Table *myTable,
+ Entry *e,
+ NdbOcInfo *no,
+ NdbAttrInfo **attrs,
+ int nattrs,
+ Attribute *old
+)
+{
+ char buf[65538], *ptr;
+ Attribute **an, **ao, *a;
+ NdbOperation *myop;
+ int i, j, max = 0;
+ int changed, rc;
+ Uint64 eid = e->e_id;
+
+ if ( !nattrs )
+ return 0;
+
+ an = (Attribute **)ch_malloc( 2 * nattrs * sizeof(Attribute *));
+ ao = an + nattrs;
+
+ /* Turn lists of attrs into arrays for easier access */
+ for ( i=0; i<nattrs; i++ ) {
+ if ( attrs[i]->na_oi != no ) {
+ an[i] = NULL;
+ ao[i] = NULL;
+ continue;
+ }
+ for ( a=e->e_attrs; a; a=a->a_next ) {
+ if ( a->a_desc == slap_schema.si_ad_objectClass )
+ continue;
+ if ( a->a_desc->ad_type == attrs[i]->na_attr ) {
+ /* Don't process same attr twice */
+ if ( a->a_flags & SLAP_ATTR_IXADD )
+ a = NULL;
+ else
+ a->a_flags |= SLAP_ATTR_IXADD;
+ break;
+ }
+ }
+ an[i] = a;
+ if ( a && a->a_numvals > max )
+ max = a->a_numvals;
+ for ( a=old; a; a=a->a_next ) {
+ if ( a->a_desc == slap_schema.si_ad_objectClass )
+ continue;
+ if ( a->a_desc->ad_type == attrs[i]->na_attr )
+ break;
+ }
+ ao[i] = a;
+ if ( a && a->a_numvals > max )
+ max = a->a_numvals;
+ }
+
+ for ( i=0; i<max; i++ ) {
+ myop = NULL;
+ for ( j=0; j<nattrs; j++ ) {
+ if ( !an[j] && !ao[j] )
+ continue;
+ changed = 0;
+ if ( an[j] && an[j]->a_numvals > i ) {
+ /* both old and new are present, compare for changes */
+ if ( ao[j] && ao[j]->a_numvals > i ) {
+ if ( ber_bvcmp( &ao[j]->a_nvals[i], &an[j]->a_nvals[i] ))
+ changed = V_REP;
+ } else {
+ changed = V_INS;
+ }
+ } else {
+ if ( ao[j] && ao[j]->a_numvals > i )
+ changed = V_DEL;
+ }
+ if ( changed ) {
+ if ( !myop ) {
+ rc = LDAP_OTHER;
+ myop = txn->getNdbOperation( myTable );
+ if ( !myop ) {
+ goto done;
+ }
+ if ( old ) {
+ if ( myop->writeTuple()) {
+ goto done;
+ }
+ } else {
+ if ( myop->insertTuple()) {
+ goto done;
+ }
+ }
+ if ( myop->equal( EID_COLUMN, eid )) {
+ goto done;
+ }
+ if ( myop->equal( VID_COLUMN, i )) {
+ goto done;
+ }
+ }
+ if ( attrs[j]->na_flag & NDB_INFO_ATBLOB ) {
+ NdbBlob *myBlob = myop->getBlobHandle( attrs[j]->na_column );
+ rc = LDAP_OTHER;
+ if ( !myBlob ) {
+ Debug( LDAP_DEBUG_TRACE, "ndb_oc_attrs: getBlobHandle failed %s (%d)\n",
+ myop->getNdbError().message, myop->getNdbError().code, 0 );
+ goto done;
+ }
+ if ( slapMode & SLAP_TOOL_MODE )
+ ndb_flush_blobs = 1;
+ if ( changed & V_INS ) {
+ if ( myBlob->setValue( an[j]->a_vals[i].bv_val, an[j]->a_vals[i].bv_len )) {
+ Debug( LDAP_DEBUG_TRACE, "ndb_oc_attrs: blob->setValue failed %s (%d)\n",
+ myBlob->getNdbError().message, myBlob->getNdbError().code, 0 );
+ goto done;
+ }
+ } else {
+ if ( myBlob->setValue( NULL, 0 )) {
+ Debug( LDAP_DEBUG_TRACE, "ndb_oc_attrs: blob->setValue failed %s (%d)\n",
+ myBlob->getNdbError().message, myBlob->getNdbError().code, 0 );
+ goto done;
+ }
+ }
+ } else {
+ if ( changed & V_INS ) {
+ if ( an[j]->a_vals[i].bv_len > attrs[j]->na_len ) {
+ Debug( LDAP_DEBUG_ANY, "ndb_oc_attrs: attribute %s too long for column\n",
+ attrs[j]->na_name.bv_val, 0, 0 );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+ ptr = buf;
+ *ptr++ = an[j]->a_vals[i].bv_len & 0xff;
+ if ( attrs[j]->na_len > 255 ) {
+ /* MedVar */
+ *ptr++ = an[j]->a_vals[i].bv_len >> 8;
+ }
+ memcpy( ptr, an[j]->a_vals[i].bv_val, an[j]->a_vals[i].bv_len );
+ ptr = buf;
+ } else {
+ ptr = NULL;
+ }
+ if ( myop->setValue( attrs[j]->na_column, ptr )) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ }
+ }
+ }
+ }
+ rc = LDAP_SUCCESS;
+done:
+ ch_free( an );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_TRACE, "ndb_oc_attrs: failed %s (%d)\n",
+ myop->getNdbError().message, myop->getNdbError().code, 0 );
+ }
+ return rc;
+}
+
+static int
+ndb_oc_put(
+ const NdbDictionary::Dictionary *myDict,
+ NdbTransaction *txn, NdbOcInfo *no, Entry *e )
+{
+ const NdbDictionary::Table *myTable;
+ int i, rc;
+
+ for ( i=0; i<no->no_nsets; i++ ) {
+ rc = ndb_oc_put( myDict, txn, no->no_sets[i], e );
+ if ( rc )
+ return rc;
+ }
+
+ myTable = myDict->getTable( no->no_table.bv_val );
+ if ( !myTable )
+ return LDAP_OTHER;
+
+ return ndb_oc_attrs( txn, myTable, e, no, no->no_attrs, no->no_nattrs, NULL );
+}
+
+/* This is now only used for Adds. Modifies call ndb_oc_attrs directly. */
+extern "C" int
+ndb_entry_put_data(
+ BackendDB *be,
+ NdbArgs *NA
+)
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ Attribute *aoc;
+ const NdbDictionary::Dictionary *myDict = NA->ndb->getDictionary();
+ NdbOcs myOcs;
+ int i, rc;
+
+ /* Get the entry's objectClass attribute */
+ aoc = attr_find( NA->e->e_attrs, slap_schema.si_ad_objectClass );
+ if ( !aoc )
+ return LDAP_OTHER;
+
+ ndb_oc_check( be, NA->ndb, aoc->a_nvals, &myOcs );
+ myOcs.no_info[myOcs.no_ninfo++] = ni->ni_opattrs;
+
+ /* Walk thru objectclasses, find all the attributes belonging to a class */
+ for ( i=0; i<myOcs.no_ninfo; i++ ) {
+ rc = ndb_oc_put( myDict, NA->txn, myOcs.no_info[i], NA->e );
+ if ( rc ) return rc;
+ }
+
+ /* slapadd tries to batch multiple entries per txn, but entry data is
+ * transient and blob data is required to remain valid for the whole txn.
+ * So we need to flush blobs before their source data disappears.
+ */
+ if (( slapMode & SLAP_TOOL_MODE ) && ndb_flush_blobs )
+ NA->txn->execute( NdbTransaction::NoCommit );
+
+ return 0;
+}
+
+static void
+ndb_oc_get( Operation *op, NdbOcInfo *no, int *j, int *nocs, NdbOcInfo ***oclist )
+{
+ int i;
+ NdbOcInfo **ol2;
+
+ for ( i=0; i<no->no_nsets; i++ ) {
+ ndb_oc_get( op, no->no_sets[i], j, nocs, oclist );
+ }
+
+ /* Don't insert twice */
+ ol2 = *oclist;
+ for ( i=0; i<*j; i++ )
+ if ( ol2[i] == no )
+ return;
+
+ if ( *j >= *nocs ) {
+ *nocs *= 2;
+ ol2 = (NdbOcInfo **)op->o_tmprealloc( *oclist, *nocs * sizeof(NdbOcInfo *), op->o_tmpmemctx );
+ *oclist = ol2;
+ }
+ ol2 = *oclist;
+ ol2[(*j)++] = no;
+}
+
+/* Retrieve attribute data for given entry. The entry's DN and eid should
+ * already be populated.
+ */
+extern "C" int
+ndb_entry_get_data(
+ Operation *op,
+ NdbArgs *NA,
+ int update
+)
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ const NdbDictionary::Dictionary *myDict = NA->ndb->getDictionary();
+ const NdbDictionary::Table *myTable;
+ NdbIndexScanOperation **myop = NULL;
+ Uint64 eid;
+
+ Attribute *a;
+ NdbOcs myOcs;
+ NdbOcInfo *oci, **oclist = NULL;
+ char abuf[65536], *ptr, **attrs = NULL;
+ struct berval bv[2];
+ int *ocx = NULL;
+
+ /* FIXME: abuf should be dynamically allocated */
+
+ int i, j, k, nocs, nattrs, rc = LDAP_OTHER;
+
+ eid = NA->e->e_id;
+
+ ndb_oc_check( op->o_bd, NA->ndb, NA->ocs, &myOcs );
+ myOcs.no_info[myOcs.no_ninfo++] = ni->ni_opattrs;
+ nocs = myOcs.no_ninfo;
+
+ oclist = (NdbOcInfo **)op->o_tmpcalloc( 1, nocs * sizeof(NdbOcInfo *), op->o_tmpmemctx );
+
+ for ( i=0, j=0; i<myOcs.no_ninfo; i++ ) {
+ ndb_oc_get( op, myOcs.no_info[i], &j, &nocs, &oclist );
+ }
+
+ nocs = j;
+ nattrs = 0;
+ for ( i=0; i<nocs; i++ )
+ nattrs += oclist[i]->no_nattrs;
+
+ ocx = (int *)op->o_tmpalloc( nocs * sizeof(int), op->o_tmpmemctx );
+
+ attrs = (char **)op->o_tmpalloc( nattrs * sizeof(char *), op->o_tmpmemctx );
+
+ myop = (NdbIndexScanOperation **)op->o_tmpalloc( nattrs * sizeof(NdbIndexScanOperation *), op->o_tmpmemctx );
+
+ k = 0;
+ ptr = abuf;
+ for ( i=0; i<nocs; i++ ) {
+ oci = oclist[i];
+
+ myop[i] = NA->txn->getNdbIndexScanOperation( "PRIMARY", oci->no_table.bv_val );
+ if ( !myop[i] )
+ goto leave;
+ if ( myop[i]->readTuples( update ? NdbOperation::LM_Exclusive : NdbOperation::LM_CommittedRead ))
+ goto leave;
+ if ( myop[i]->setBound( 0U, NdbIndexScanOperation::BoundEQ, &eid ))
+ goto leave;
+
+ for ( j=0; j<oci->no_nattrs; j++ ) {
+ if ( oci->no_attrs[j]->na_oi != oci )
+ continue;
+ if ( oci->no_attrs[j]->na_flag & NDB_INFO_ATBLOB ) {
+ NdbBlob *bi = myop[i]->getBlobHandle( oci->no_attrs[j]->na_column );
+ attrs[k++] = (char *)bi;
+ } else {
+ attrs[k] = ptr;
+ *ptr++ = 0;
+ if ( oci->no_attrs[j]->na_len > 255 )
+ *ptr++ = 0;
+ ptr += oci->no_attrs[j]->na_len + 1;
+ myop[i]->getValue( oci->no_attrs[j]->na_column, attrs[k++] );
+ }
+ }
+ ocx[i] = k;
+ }
+ /* Must use IgnoreError, because an entry with multiple objectClasses may not
+ * actually have attributes defined in each class / table.
+ */
+ if ( NA->txn->execute( NdbTransaction::NoCommit, NdbOperation::AO_IgnoreError, 1) < 0 )
+ goto leave;
+
+ /* count results */
+ for ( i=0; i<nocs; i++ ) {
+ if (( j = myop[i]->nextResult(true) )) {
+ if ( j < 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "ndb_entry_get_data: first nextResult(%d) failed: %s (%d)\n",
+ i, myop[i]->getNdbError().message, myop[i]->getNdbError().code );
+ }
+ myop[i] = NULL;
+ }
+ }
+
+ nattrs = 0;
+ k = 0;
+ for ( i=0; i<nocs; i++ ) {
+ oci = oclist[i];
+ for ( j=0; j<oci->no_nattrs; j++ ) {
+ unsigned char *buf;
+ int len;
+ if ( oci->no_attrs[j]->na_oi != oci )
+ continue;
+ if ( !myop[i] ) {
+ attrs[k] = NULL;
+ } else if ( oci->no_attrs[j]->na_flag & NDB_INFO_ATBLOB ) {
+ void *vi = attrs[k];
+ NdbBlob *bi = (NdbBlob *)vi;
+ int isNull;
+ bi->getNull( isNull );
+ if ( !isNull ) {
+ nattrs++;
+ } else {
+ attrs[k] = NULL;
+ }
+ } else {
+ buf = (unsigned char *)attrs[k];
+ len = buf[0];
+ if ( oci->no_attrs[j]->na_len > 255 ) {
+ /* MedVar */
+ len |= (buf[1] << 8);
+ }
+ if ( len ) {
+ nattrs++;
+ } else {
+ attrs[k] = NULL;
+ }
+ }
+ k++;
+ }
+ }
+
+ a = attrs_alloc( nattrs+1 );
+ NA->e->e_attrs = a;
+
+ a->a_desc = slap_schema.si_ad_objectClass;
+ a->a_vals = NULL;
+ ber_bvarray_dup_x( &a->a_vals, NA->ocs, NULL );
+ a->a_nvals = a->a_vals;
+ a->a_numvals = myOcs.no_ntext;
+
+ BER_BVZERO( &bv[1] );
+
+ do {
+ a = NA->e->e_attrs->a_next;
+ k = 0;
+ for ( i=0; i<nocs; k=ocx[i], i++ ) {
+ oci = oclist[i];
+ for ( j=0; j<oci->no_nattrs; j++ ) {
+ unsigned char *buf;
+ struct berval nbv;
+ if ( oci->no_attrs[j]->na_oi != oci )
+ continue;
+ buf = (unsigned char *)attrs[k++];
+ if ( !buf )
+ continue;
+ if ( !myop[i] ) {
+ a=a->a_next;
+ continue;
+ }
+ if ( oci->no_attrs[j]->na_flag & NDB_INFO_ATBLOB ) {
+ void *vi = (void *)buf;
+ NdbBlob *bi = (NdbBlob *)vi;
+ Uint64 len;
+ Uint32 len2;
+ int isNull;
+ bi->getNull( isNull );
+ if ( isNull ) {
+ a = a->a_next;
+ continue;
+ }
+ bi->getLength( len );
+ bv[0].bv_len = len;
+ bv[0].bv_val = (char *)ch_malloc( len+1 );
+ len2 = len;
+ if ( bi->readData( bv[0].bv_val, len2 )) {
+ Debug( LDAP_DEBUG_TRACE,
+ "ndb_entry_get_data: blob readData failed: %s (%d), len %d\n",
+ bi->getNdbError().message, bi->getNdbError().code, len2 );
+ }
+ bv[0].bv_val[len] = '\0';
+ ber_bvarray_add_x( &a->a_vals, bv, NULL );
+ } else {
+ bv[0].bv_len = buf[0];
+ if ( oci->no_attrs[j]->na_len > 255 ) {
+ /* MedVar */
+ bv[0].bv_len |= (buf[1] << 8);
+ bv[0].bv_val = (char *)buf+2;
+ buf[1] = 0;
+ } else {
+ bv[0].bv_val = (char *)buf+1;
+ }
+ buf[0] = 0;
+ if ( bv[0].bv_len == 0 ) {
+ a = a->a_next;
+ continue;
+ }
+ bv[0].bv_val[bv[0].bv_len] = '\0';
+ value_add_one( &a->a_vals, bv );
+ }
+ a->a_desc = oci->no_attrs[j]->na_desc;
+ attr_normalize_one( a->a_desc, bv, &nbv, NULL );
+ a->a_numvals++;
+ if ( !BER_BVISNULL( &nbv )) {
+ ber_bvarray_add_x( &a->a_nvals, &nbv, NULL );
+ } else if ( !a->a_nvals ) {
+ a->a_nvals = a->a_vals;
+ }
+ a = a->a_next;
+ }
+ }
+ k = 0;
+ for ( i=0; i<nocs; i++ ) {
+ if ( !myop[i] )
+ continue;
+ if ((j = myop[i]->nextResult(true))) {
+ if ( j < 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "ndb_entry_get_data: last nextResult(%d) failed: %s (%d)\n",
+ i, myop[i]->getNdbError().message, myop[i]->getNdbError().code );
+ }
+ myop[i] = NULL;
+ } else {
+ k = 1;
+ }
+ }
+ } while ( k );
+
+ rc = 0;
+leave:
+ if ( myop ) {
+ op->o_tmpfree( myop, op->o_tmpmemctx );
+ }
+ if ( attrs ) {
+ op->o_tmpfree( attrs, op->o_tmpmemctx );
+ }
+ if ( ocx ) {
+ op->o_tmpfree( ocx, op->o_tmpmemctx );
+ }
+ if ( oclist ) {
+ op->o_tmpfree( oclist, op->o_tmpmemctx );
+ }
+
+ return rc;
+}
+
+static int
+ndb_oc_del(
+ NdbTransaction *txn, Uint64 eid, NdbOcInfo *no )
+{
+ NdbIndexScanOperation *myop;
+ int i, rc;
+
+ for ( i=0; i<no->no_nsets; i++ ) {
+ rc = ndb_oc_del( txn, eid, no->no_sets[i] );
+ if ( rc ) return rc;
+ }
+
+ myop = txn->getNdbIndexScanOperation( "PRIMARY", no->no_table.bv_val );
+ if ( !myop )
+ return LDAP_OTHER;
+ if ( myop->readTuples( NdbOperation::LM_Exclusive ))
+ return LDAP_OTHER;
+ if ( myop->setBound( 0U, NdbIndexScanOperation::BoundEQ, &eid ))
+ return LDAP_OTHER;
+
+ txn->execute(NoCommit);
+ while ( myop->nextResult(true) == 0) {
+ do {
+ myop->deleteCurrentTuple();
+ } while (myop->nextResult(false) == 0);
+ txn->execute(NoCommit);
+ }
+
+ return 0;
+}
+
+extern "C" int
+ndb_entry_del_data(
+ BackendDB *be,
+ NdbArgs *NA
+)
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ Uint64 eid = NA->e->e_id;
+ int i;
+ NdbOcs myOcs;
+
+ ndb_oc_check( be, NA->ndb, NA->ocs, &myOcs );
+ myOcs.no_info[myOcs.no_ninfo++] = ni->ni_opattrs;
+
+ for ( i=0; i<myOcs.no_ninfo; i++ ) {
+ if ( ndb_oc_del( NA->txn, eid, myOcs.no_info[i] ))
+ return LDAP_OTHER;
+ }
+
+ return 0;
+}
+
+extern "C" int
+ndb_dn2rdns(
+ struct berval *dn,
+ NdbRdns *rdns
+)
+{
+ char *beg, *end;
+ int i, len;
+
+ /* Walk thru RDNs */
+ end = dn->bv_val + dn->bv_len;
+ for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+ for ( beg = end-1; beg > dn->bv_val; beg-- ) {
+ if (*beg == ',') {
+ beg++;
+ break;
+ }
+ }
+ if ( beg >= dn->bv_val ) {
+ len = end - beg;
+ /* RDN is too long */
+ if ( len > NDB_RDN_LEN )
+ return LDAP_CONSTRAINT_VIOLATION;
+ memcpy( rdns->nr_buf[i]+1, beg, len );
+ } else {
+ break;
+ }
+ rdns->nr_buf[i][0] = len;
+ end = beg - 1;
+ }
+ /* Too many RDNs in DN */
+ if ( i == NDB_MAX_RDNS && beg > dn->bv_val ) {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ rdns->nr_num = i;
+ return 0;
+}
+
+static int
+ndb_rdns2keys(
+ NdbOperation *myop,
+ NdbRdns *rdns
+)
+{
+ int i;
+ char dummy[2] = {0,0};
+
+ /* Walk thru RDNs */
+ for ( i=0; i<rdns->nr_num; i++ ) {
+ if ( myop->equal( i+RDN_COLUMN, rdns->nr_buf[i] ))
+ return LDAP_OTHER;
+ }
+ for ( ; i<NDB_MAX_RDNS; i++ ) {
+ if ( myop->equal( i+RDN_COLUMN, dummy ))
+ return LDAP_OTHER;
+ }
+ return 0;
+}
+
+/* Store the DN2ID_TABLE fields */
+extern "C" int
+ndb_entry_put_info(
+ BackendDB *be,
+ NdbArgs *NA,
+ int update
+)
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ const NdbDictionary::Dictionary *myDict = NA->ndb->getDictionary();
+ const NdbDictionary::Table *myTable = myDict->getTable( DN2ID_TABLE );
+ NdbOperation *myop;
+ NdbAttrInfo *ai;
+ Attribute *aoc, *a;
+
+ /* Get the entry's objectClass attribute; it's ok to be
+ * absent on a fresh insert
+ */
+ aoc = attr_find( NA->e->e_attrs, slap_schema.si_ad_objectClass );
+ if ( update && !aoc )
+ return LDAP_OBJECT_CLASS_VIOLATION;
+
+ myop = NA->txn->getNdbOperation( myTable );
+ if ( !myop )
+ return LDAP_OTHER;
+ if ( update ) {
+ if ( myop->updateTuple())
+ return LDAP_OTHER;
+ } else {
+ if ( myop->insertTuple())
+ return LDAP_OTHER;
+ }
+
+ if ( ndb_rdns2keys( myop, NA->rdns ))
+ return LDAP_OTHER;
+
+ /* Set entry ID */
+ {
+ Uint64 eid = NA->e->e_id;
+ if ( myop->setValue( EID_COLUMN, eid ))
+ return LDAP_OTHER;
+ }
+
+ /* Set list of objectClasses */
+ /* List is <sp> <class> <sp> <class> <sp> ... so that
+ * searches for " class " will yield accurate results
+ */
+ if ( aoc ) {
+ char *ptr, buf[sizeof(MedVar)];
+ NdbOcs myOcs;
+ int i;
+
+ ndb_oc_check( be, NA->ndb, aoc->a_nvals, &myOcs );
+ ptr = buf+2;
+ *ptr++ = ' ';
+ for ( i=0; i<myOcs.no_ntext; i++ ) {
+ /* data loss... */
+ if ( ptr + myOcs.no_text[i].bv_len + 1 >= &buf[sizeof(buf)] )
+ break;
+ ptr = lutil_strcopy( ptr, myOcs.no_text[i].bv_val );
+ *ptr++ = ' ';
+ }
+
+ /* implicit classes */
+ if ( myOcs.no_nitext ) {
+ *ptr++ = '@';
+ *ptr++ = ' ';
+ for ( i=0; i<myOcs.no_nitext; i++ ) {
+ /* data loss... */
+ if ( ptr + myOcs.no_itext[i].bv_len + 1 >= &buf[sizeof(buf)] )
+ break;
+ ptr = lutil_strcopy( ptr, myOcs.no_itext[i].bv_val );
+ *ptr++ = ' ';
+ }
+ }
+
+ i = ptr - buf - 2;
+ buf[0] = i & 0xff;
+ buf[1] = i >> 8;
+ if ( myop->setValue( OCS_COLUMN, buf ))
+ return LDAP_OTHER;
+ }
+
+ /* Set any indexed attrs */
+ for ( a = NA->e->e_attrs; a; a=a->a_next ) {
+ ai = ndb_ai_find( ni, a->a_desc->ad_type );
+ if ( ai && ( ai->na_flag & NDB_INFO_INDEX )) {
+ char *ptr, buf[sizeof(MedVar)];
+ int len;
+
+ ptr = buf+1;
+ len = a->a_vals[0].bv_len;
+ /* FIXME: data loss */
+ if ( len > ai->na_len )
+ len = ai->na_len;
+ buf[0] = len & 0xff;
+ if ( ai->na_len > 255 ) {
+ *ptr++ = len >> 8;
+ }
+ memcpy( ptr, a->a_vals[0].bv_val, len );
+ if ( myop->setValue( ai->na_ixcol, buf ))
+ return LDAP_OTHER;
+ }
+ }
+
+ return 0;
+}
+
+extern "C" struct berval *
+ndb_str2bvarray(
+ char *str,
+ int len,
+ char delim,
+ void *ctx
+)
+{
+ struct berval *list, tmp;
+ char *beg;
+ int i, num;
+
+ while ( *str == delim ) {
+ str++;
+ len--;
+ }
+
+ while ( str[len-1] == delim ) {
+ str[--len] = '\0';
+ }
+
+ for ( i = 1, beg = str;; i++ ) {
+ beg = strchr( beg, delim );
+ if ( !beg )
+ break;
+ if ( beg >= str + len )
+ break;
+ beg++;
+ }
+
+ num = i;
+ list = (struct berval *)slap_sl_malloc( (num+1)*sizeof(struct berval), ctx);
+
+ for ( i = 0, beg = str; i<num; i++ ) {
+ tmp.bv_val = beg;
+ beg = strchr( beg, delim );
+ if ( beg >= str + len )
+ beg = NULL;
+ if ( beg ) {
+ tmp.bv_len = beg - tmp.bv_val;
+ } else {
+ tmp.bv_len = len - (tmp.bv_val - str);
+ }
+ ber_dupbv_x( &list[i], &tmp, ctx );
+ beg++;
+ }
+
+ BER_BVZERO( &list[i] );
+ return list;
+}
+
+extern "C" struct berval *
+ndb_ref2oclist(
+ const char *ref,
+ void *ctx
+)
+{
+ char *implied;
+
+ /* MedVar */
+ int len = ref[0] | (ref[1] << 8);
+
+ /* don't return the implied classes */
+ implied = (char *)memchr( ref+2, '@', len );
+ if ( implied ) {
+ len = implied - ref - 2;
+ *implied = '\0';
+ }
+
+ return ndb_str2bvarray( (char *)ref+2, len, ' ', ctx );
+}
+
+/* Retrieve the DN2ID_TABLE fields. Can call with NULL ocs if just verifying
+ * the existence of a DN.
+ */
+extern "C" int
+ndb_entry_get_info(
+ Operation *op,
+ NdbArgs *NA,
+ int update,
+ struct berval *matched
+)
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ const NdbDictionary::Dictionary *myDict = NA->ndb->getDictionary();
+ const NdbDictionary::Table *myTable = myDict->getTable( DN2ID_TABLE );
+ NdbOperation *myop[NDB_MAX_RDNS];
+ NdbRecAttr *eid[NDB_MAX_RDNS], *oc[NDB_MAX_RDNS];
+ char idbuf[NDB_MAX_RDNS][2*sizeof(ID)];
+ char ocbuf[NDB_MAX_RDNS][NDB_OC_BUFLEN];
+
+ if ( matched ) {
+ BER_BVZERO( matched );
+ }
+ if ( !myTable ) {
+ return LDAP_OTHER;
+ }
+
+ myop[0] = NA->txn->getNdbOperation( myTable );
+ if ( !myop[0] ) {
+ return LDAP_OTHER;
+ }
+
+ if ( myop[0]->readTuple( update ? NdbOperation::LM_Exclusive : NdbOperation::LM_CommittedRead )) {
+ return LDAP_OTHER;
+ }
+
+ if ( !NA->rdns->nr_num && ndb_dn2rdns( &NA->e->e_name, NA->rdns )) {
+ return LDAP_NO_SUCH_OBJECT;
+ }
+
+ if ( ndb_rdns2keys( myop[0], NA->rdns )) {
+ return LDAP_OTHER;
+ }
+
+ eid[0] = myop[0]->getValue( EID_COLUMN, idbuf[0] );
+ if ( !eid[0] ) {
+ return LDAP_OTHER;
+ }
+
+ ocbuf[0][0] = 0;
+ ocbuf[0][1] = 0;
+ if ( !NA->ocs ) {
+ oc[0] = myop[0]->getValue( OCS_COLUMN, ocbuf[0] );
+ if ( !oc[0] ) {
+ return LDAP_OTHER;
+ }
+ }
+
+ if ( NA->txn->execute(NdbTransaction::NoCommit, NdbOperation::AO_IgnoreError, 1) < 0 ) {
+ return LDAP_OTHER;
+ }
+
+ switch( myop[0]->getNdbError().code ) {
+ case 0:
+ if ( !eid[0]->isNULL() && ( NA->e->e_id = eid[0]->u_64_value() )) {
+ /* If we didn't care about OCs, or we got them */
+ if ( NA->ocs || ocbuf[0][0] || ocbuf[0][1] ) {
+ /* If wanted, return them */
+ if ( !NA->ocs )
+ NA->ocs = ndb_ref2oclist( ocbuf[0], op->o_tmpmemctx );
+ break;
+ }
+ }
+ /* FALLTHRU */
+ case NDB_NO_SUCH_OBJECT: /* no such tuple: look for closest parent */
+ if ( matched ) {
+ int i, j, k;
+ char dummy[2] = {0,0};
+
+ /* get to last RDN, then back up 1 */
+ k = NA->rdns->nr_num - 1;
+
+ for ( i=0; i<k; i++ ) {
+ myop[i] = NA->txn->getNdbOperation( myTable );
+ if ( !myop[i] )
+ return LDAP_OTHER;
+ if ( myop[i]->readTuple( NdbOperation::LM_CommittedRead ))
+ return LDAP_OTHER;
+ for ( j=0; j<=i; j++ ) {
+ if ( myop[i]->equal( j+RDN_COLUMN, NA->rdns->nr_buf[j] ))
+ return LDAP_OTHER;
+ }
+ for ( ;j<NDB_MAX_RDNS; j++ ) {
+ if ( myop[i]->equal( j+RDN_COLUMN, dummy ))
+ return LDAP_OTHER;
+ }
+ eid[i] = myop[i]->getValue( EID_COLUMN, idbuf[i] );
+ if ( !eid[i] ) {
+ return LDAP_OTHER;
+ }
+ ocbuf[i][0] = 0;
+ ocbuf[i][1] = 0;
+ if ( !NA->ocs ) {
+ oc[i] = myop[0]->getValue( OCS_COLUMN, ocbuf[i] );
+ if ( !oc[i] ) {
+ return LDAP_OTHER;
+ }
+ }
+ }
+ if ( NA->txn->execute(NdbTransaction::NoCommit, NdbOperation::AO_IgnoreError, 1) < 0 ) {
+ return LDAP_OTHER;
+ }
+ for ( --i; i>=0; i-- ) {
+ if ( myop[i]->getNdbError().code == 0 ) {
+ for ( j=0; j<=i; j++ )
+ matched->bv_len += NA->rdns->nr_buf[j][0];
+ NA->erdns = NA->rdns->nr_num;
+ NA->rdns->nr_num = j;
+ matched->bv_len += i;
+ matched->bv_val = NA->e->e_name.bv_val +
+ NA->e->e_name.bv_len - matched->bv_len;
+ if ( !eid[i]->isNULL() )
+ NA->e->e_id = eid[i]->u_64_value();
+ if ( !NA->ocs )
+ NA->ocs = ndb_ref2oclist( ocbuf[i], op->o_tmpmemctx );
+ break;
+ }
+ }
+ }
+ return LDAP_NO_SUCH_OBJECT;
+ default:
+ return LDAP_OTHER;
+ }
+
+ return 0;
+}
+
+extern "C" int
+ndb_entry_del_info(
+ BackendDB *be,
+ NdbArgs *NA
+)
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ const NdbDictionary::Dictionary *myDict = NA->ndb->getDictionary();
+ const NdbDictionary::Table *myTable = myDict->getTable( DN2ID_TABLE );
+ NdbOperation *myop;
+
+ myop = NA->txn->getNdbOperation( myTable );
+ if ( !myop )
+ return LDAP_OTHER;
+ if ( myop->deleteTuple())
+ return LDAP_OTHER;
+
+ if ( ndb_rdns2keys( myop, NA->rdns ))
+ return LDAP_OTHER;
+
+ return 0;
+}
+
+extern "C" int
+ndb_next_id(
+ BackendDB *be,
+ Ndb *ndb,
+ ID *id
+)
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ const NdbDictionary::Dictionary *myDict = ndb->getDictionary();
+ const NdbDictionary::Table *myTable = myDict->getTable( NEXTID_TABLE );
+ Uint64 nid = 0;
+ int rc;
+
+ if ( !myTable ) {
+ Debug( LDAP_DEBUG_ANY, "ndb_next_id: " NEXTID_TABLE " table is missing\n",
+ 0, 0, 0 );
+ return LDAP_OTHER;
+ }
+
+ rc = ndb->getAutoIncrementValue( myTable, nid, 1000 );
+ if ( !rc )
+ *id = nid;
+ return rc;
+}
+
+extern "C" { static void ndb_thread_hfree( void *key, void *data ); };
+static void
+ndb_thread_hfree( void *key, void *data )
+{
+ Ndb *ndb = (Ndb *)data;
+ delete ndb;
+}
+
+extern "C" int
+ndb_thread_handle(
+ Operation *op,
+ Ndb **ndb )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ void *data;
+
+ if ( ldap_pvt_thread_pool_getkey( op->o_threadctx, ni, &data, NULL )) {
+ Ndb *myNdb;
+ int rc;
+ ldap_pvt_thread_mutex_lock( &ni->ni_conn_mutex );
+ myNdb = new Ndb( ni->ni_cluster[ni->ni_nextconn++], ni->ni_dbname );
+ if ( ni->ni_nextconn >= ni->ni_nconns )
+ ni->ni_nextconn = 0;
+ ldap_pvt_thread_mutex_unlock( &ni->ni_conn_mutex );
+ if ( !myNdb ) {
+ return LDAP_OTHER;
+ }
+ rc = myNdb->init(1024);
+ if ( rc ) {
+ delete myNdb;
+ Debug( LDAP_DEBUG_ANY, "ndb_thread_handle: err %d\n",
+ rc, 0, 0 );
+ return rc;
+ }
+ data = (void *)myNdb;
+ if (( rc = ldap_pvt_thread_pool_setkey( op->o_threadctx, ni,
+ data, ndb_thread_hfree, NULL, NULL ))) {
+ delete myNdb;
+ Debug( LDAP_DEBUG_ANY, "ndb_thread_handle: err %d\n",
+ rc, 0, 0 );
+ return rc;
+ }
+ }
+ *ndb = (Ndb *)data;
+ return 0;
+}
+
+extern "C" int
+ndb_entry_get(
+ Operation *op,
+ struct berval *ndn,
+ ObjectClass *oc,
+ AttributeDescription *ad,
+ int rw,
+ Entry **ent )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ NdbArgs NA;
+ Entry e = {0};
+ int rc;
+
+ /* Get our NDB handle */
+ rc = ndb_thread_handle( op, &NA.ndb );
+
+ NA.txn = NA.ndb->startTransaction();
+ if( !NA.txn ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_entry_get) ": startTransaction failed: %s (%d)\n",
+ NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+ return 1;
+ }
+
+ e.e_name = *ndn;
+ NA.e = &e;
+ /* get entry */
+ {
+ NdbRdns rdns;
+ rdns.nr_num = 0;
+ NA.ocs = NULL;
+ NA.rdns = &rdns;
+ rc = ndb_entry_get_info( op, &NA, rw, NULL );
+ }
+ if ( rc == 0 ) {
+ e.e_name = *ndn;
+ e.e_nname = *ndn;
+ rc = ndb_entry_get_data( op, &NA, 0 );
+ ber_bvarray_free( NA.ocs );
+ if ( rc == 0 ) {
+ if ( oc && !is_entry_objectclass_or_sub( &e, oc )) {
+ attrs_free( e.e_attrs );
+ rc = 1;
+ }
+ }
+ }
+ if ( rc == 0 ) {
+ *ent = entry_alloc();
+ **ent = e;
+ ber_dupbv( &(*ent)->e_name, ndn );
+ ber_dupbv( &(*ent)->e_nname, ndn );
+ } else {
+ rc = 1;
+ }
+ NA.txn->close();
+ return rc;
+}
+
+/* Congestion avoidance code
+ * for Deadlock Rollback
+ */
+
+extern "C" void
+ndb_trans_backoff( int num_retries )
+{
+ int i;
+ int delay = 0;
+ int pow_retries = 1;
+ unsigned long key = 0;
+ unsigned long max_key = -1;
+ struct timeval timeout;
+
+ lutil_entropy( (unsigned char *) &key, sizeof( unsigned long ));
+
+ for ( i = 0; i < num_retries; i++ ) {
+ if ( i >= 5 ) break;
+ pow_retries *= 4;
+ }
+
+ delay = 16384 * (key * (double) pow_retries / (double) max_key);
+ delay = delay ? delay : 1;
+
+ Debug( LDAP_DEBUG_TRACE, "delay = %d, num_retries = %d\n", delay, num_retries, 0 );
+
+ timeout.tv_sec = delay / 1000000;
+ timeout.tv_usec = delay % 1000000;
+ select( 0, NULL, NULL, NULL, &timeout );
+}
+
+extern "C" void
+ndb_check_referral( Operation *op, SlapReply *rs, NdbArgs *NA )
+{
+ struct berval dn, ndn;
+ int i, dif;
+ dif = NA->erdns - NA->rdns->nr_num;
+
+ /* Set full DN of matched into entry */
+ for ( i=0; i<dif; i++ ) {
+ dnParent( &NA->e->e_name, &dn );
+ dnParent( &NA->e->e_nname, &ndn );
+ NA->e->e_name = dn;
+ NA->e->e_nname = ndn;
+ }
+
+ /* return referral only if "disclose" is granted on the object */
+ if ( access_allowed( op, NA->e, slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL )) {
+ Attribute a;
+ for ( i=0; !BER_BVISNULL( &NA->ocs[i] ); i++ );
+ a.a_numvals = i;
+ a.a_desc = slap_schema.si_ad_objectClass;
+ a.a_vals = NA->ocs;
+ a.a_nvals = NA->ocs;
+ a.a_next = NULL;
+ NA->e->e_attrs = &a;
+ if ( is_entry_referral( NA->e )) {
+ NA->e->e_attrs = NULL;
+ ndb_entry_get_data( op, NA, 0 );
+ rs->sr_ref = get_entry_referrals( op, NA->e );
+ if ( rs->sr_ref ) {
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_flags |= REP_REF_MUSTBEFREED;
+ }
+ attrs_free( NA->e->e_attrs );
+ }
+ NA->e->e_attrs = NULL;
+ }
+}
diff --git a/servers/slapd/back-ndb/proto-ndb.h b/servers/slapd/back-ndb/proto-ndb.h
new file mode 100644
index 0000000..6a6e092
--- /dev/null
+++ b/servers/slapd/back-ndb/proto-ndb.h
@@ -0,0 +1,166 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#ifndef _PROTO_NDB_H
+#define _PROTO_NDB_H
+
+LDAP_BEGIN_DECL
+
+extern BI_init ndb_back_initialize;
+
+extern BI_open ndb_back_open;
+extern BI_close ndb_back_close;
+extern BI_destroy ndb_back_destroy;
+
+extern BI_db_init ndb_back_db_init;
+extern BI_db_destroy ndb_back_db_destroy;
+
+extern BI_op_bind ndb_back_bind;
+extern BI_op_unbind ndb_back_unbind;
+extern BI_op_search ndb_back_search;
+extern BI_op_compare ndb_back_compare;
+extern BI_op_modify ndb_back_modify;
+extern BI_op_modrdn ndb_back_modrdn;
+extern BI_op_add ndb_back_add;
+extern BI_op_delete ndb_back_delete;
+
+extern BI_operational ndb_operational;
+extern BI_has_subordinates ndb_has_subordinates;
+extern BI_entry_get_rw ndb_entry_get;
+
+extern BI_tool_entry_open ndb_tool_entry_open;
+extern BI_tool_entry_close ndb_tool_entry_close;
+extern BI_tool_entry_first ndb_tool_entry_first;
+extern BI_tool_entry_next ndb_tool_entry_next;
+extern BI_tool_entry_get ndb_tool_entry_get;
+extern BI_tool_entry_put ndb_tool_entry_put;
+extern BI_tool_dn2id_get ndb_tool_dn2id_get;
+
+extern int ndb_modify_internal(
+ Operation *op,
+ NdbArgs *NA,
+ const char **text,
+ char *textbuf,
+ size_t textlen );
+
+extern int
+ndb_entry_get_data(
+ Operation *op,
+ NdbArgs *args,
+ int update );
+
+extern int
+ndb_entry_put_data(
+ BackendDB *be,
+ NdbArgs *args );
+
+extern int
+ndb_entry_del_data(
+ BackendDB *be,
+ NdbArgs *args );
+
+extern int
+ndb_entry_put_info(
+ BackendDB *be,
+ NdbArgs *args,
+ int update );
+
+extern int
+ndb_entry_get_info(
+ Operation *op,
+ NdbArgs *args,
+ int update,
+ struct berval *matched );
+
+extern "C" int
+ndb_entry_del_info(
+ BackendDB *be,
+ NdbArgs *args );
+
+extern int
+ndb_dn2rdns(
+ struct berval *dn,
+ NdbRdns *buf );
+
+extern NdbAttrInfo *
+ndb_ai_find( struct ndb_info *ni, AttributeType *at );
+
+extern NdbAttrInfo *
+ndb_ai_get( struct ndb_info *ni, struct berval *at );
+
+extern int
+ndb_aset_get( struct ndb_info *ni, struct berval *sname, struct berval *attrs, NdbOcInfo **ret );
+
+extern int
+ndb_aset_create( struct ndb_info *ni, NdbOcInfo *oci );
+
+extern int
+ndb_oc_read( struct ndb_info *ni, const NdbDictionary::Dictionary *dict );
+
+extern int
+ndb_oc_attrs(
+ NdbTransaction *txn,
+ const NdbDictionary::Table *myTable,
+ Entry *e,
+ NdbOcInfo *no,
+ NdbAttrInfo **attrs,
+ int nattrs,
+ Attribute *old );
+
+extern int
+ndb_has_children(
+ NdbArgs *NA,
+ int *hasChildren );
+
+extern struct berval *
+ndb_str2bvarray(
+ char *str,
+ int len,
+ char delim,
+ void *ctx );
+
+extern struct berval *
+ndb_ref2oclist(
+ const char *ref,
+ void *ctx );
+
+extern int
+ndb_next_id(
+ BackendDB *be,
+ Ndb *ndb,
+ ID *id );
+
+extern int
+ndb_thread_handle(
+ Operation *op,
+ Ndb **ndb );
+
+extern int
+ndb_back_init_cf(
+ BackendInfo *bi );
+
+extern "C" void
+ndb_trans_backoff( int num_retries );
+
+extern "C" void
+ndb_check_referral( Operation *op, SlapReply *rs, NdbArgs *NA );
+
+LDAP_END_DECL
+
+#endif /* _PROTO_NDB_H */
diff --git a/servers/slapd/back-ndb/search.cpp b/servers/slapd/back-ndb/search.cpp
new file mode 100644
index 0000000..3d0f4b0
--- /dev/null
+++ b/servers/slapd/back-ndb/search.cpp
@@ -0,0 +1,854 @@
+/* search.cpp - tools for slap tools */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/errno.h>
+
+#include "lutil.h"
+
+#include "back-ndb.h"
+
+static int
+ndb_dn2bound(
+ NdbIndexScanOperation *myop,
+ NdbRdns *rdns
+)
+{
+ unsigned int i;
+
+ /* Walk thru RDNs */
+ for ( i=0; i<rdns->nr_num; i++ ) {
+ /* Note: RDN_COLUMN offset not needed here */
+ if ( myop->setBound( i, NdbIndexScanOperation::BoundEQ, rdns->nr_buf[i] ))
+ return LDAP_OTHER;
+ }
+ return i;
+}
+
+/* Check that all filter terms reside in the same table.
+ *
+ * If any of the filter terms are indexed, then only an IndexScan of the OL_index
+ * will be performed. If none are indexed, but all the terms reside in a single
+ * table, a Scan can be performed with the LDAP filter transformed into a ScanFilter.
+ *
+ * Otherwise, a full scan of the DB must be done with all filtering done by slapd.
+ */
+static int ndb_filter_check( struct ndb_info *ni, Filter *f,
+ NdbOcInfo **oci, int *indexed, int *ocfilter )
+{
+ AttributeDescription *ad = NULL;
+ ber_tag_t choice = f->f_choice;
+ int rc = 0, undef = 0;
+
+ if ( choice & SLAPD_FILTER_UNDEFINED ) {
+ choice &= SLAPD_FILTER_MASK;
+ undef = 1;
+ }
+ switch( choice ) {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ for ( f = f->f_list; f; f=f->f_next ) {
+ rc = ndb_filter_check( ni, f, oci, indexed, ocfilter );
+ if ( rc ) return rc;
+ }
+ break;
+ case LDAP_FILTER_PRESENT:
+ ad = f->f_desc;
+ break;
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_SUBSTRINGS:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ ad = f->f_av_desc;
+ break;
+ default:
+ break;
+ }
+ if ( ad && !undef ) {
+ NdbAttrInfo *ai;
+ /* ObjectClass filtering is in dn2id table */
+ if ( ad == slap_schema.si_ad_objectClass ) {
+ if ( choice == LDAP_FILTER_EQUALITY )
+ (*ocfilter)++;
+ return 0;
+ }
+ ai = ndb_ai_find( ni, ad->ad_type );
+ if ( ai ) {
+ if ( ai->na_flag & NDB_INFO_INDEX )
+ (*indexed)++;
+ if ( *oci ) {
+ if ( ai->na_oi != *oci )
+ rc = -1;
+ } else {
+ *oci = ai->na_oi;
+ }
+ }
+ }
+ return rc;
+}
+
+static int ndb_filter_set( Operation *op, struct ndb_info *ni, Filter *f, int indexed,
+ NdbIndexScanOperation *scan, NdbScanFilter *sf, int *bounds )
+{
+ AttributeDescription *ad = NULL;
+ ber_tag_t choice = f->f_choice;
+ int undef = 0;
+
+ if ( choice & SLAPD_FILTER_UNDEFINED ) {
+ choice &= SLAPD_FILTER_MASK;
+ undef = 1;
+ }
+ switch( choice ) {
+ case LDAP_FILTER_NOT:
+ /* no indexing for these */
+ break;
+ case LDAP_FILTER_OR:
+ /* FIXME: these bounds aren't right. */
+ if ( indexed ) {
+ scan->end_of_bound( (*bounds)++ );
+ }
+ case LDAP_FILTER_AND:
+ if ( sf ) {
+ sf->begin( choice == LDAP_FILTER_OR ? NdbScanFilter::OR : NdbScanFilter::AND );
+ }
+ for ( f = f->f_list; f; f=f->f_next ) {
+ if ( ndb_filter_set( op, ni, f, indexed, scan, sf, bounds ))
+ return -1;
+ }
+ if ( sf ) {
+ sf->end();
+ }
+ break;
+ case LDAP_FILTER_PRESENT:
+ ad = f->f_desc;
+ break;
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_SUBSTRINGS:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ ad = f->f_av_desc;
+ break;
+ default:
+ break;
+ }
+ if ( ad && !undef ) {
+ NdbAttrInfo *ai;
+ /* ObjectClass filtering is in dn2id table */
+ if ( ad == slap_schema.si_ad_objectClass ) {
+ return 0;
+ }
+ ai = ndb_ai_find( ni, ad->ad_type );
+ if ( ai ) {
+ int rc;
+ if ( ai->na_flag & NDB_INFO_INDEX ) {
+ char *buf, *ptr;
+ NdbIndexScanOperation::BoundType bt;
+
+ switch(choice) {
+ case LDAP_FILTER_PRESENT:
+ rc = scan->setBound( ai->na_ixcol - IDX_COLUMN,
+ NdbIndexScanOperation::BoundGT, NULL );
+ break;
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_APPROX:
+ bt = NdbIndexScanOperation::BoundEQ;
+ goto setit;
+ case LDAP_FILTER_GE:
+ bt = NdbIndexScanOperation::BoundGE;
+ goto setit;
+ case LDAP_FILTER_LE:
+ bt = NdbIndexScanOperation::BoundLE;
+ setit:
+ rc = f->f_av_value.bv_len+1;
+ if ( ai->na_len > 255 )
+ rc++;
+ buf = (char *)op->o_tmpalloc( rc, op->o_tmpmemctx );
+ rc = f->f_av_value.bv_len;
+ buf[0] = rc & 0xff;
+ ptr = buf+1;
+ if ( ai->na_len > 255 ) {
+ buf[1] = (rc >> 8);
+ ptr++;
+ }
+ memcpy( ptr, f->f_av_value.bv_val, f->f_av_value.bv_len );
+ rc = scan->setBound( ai->na_ixcol - IDX_COLUMN, bt, buf );
+ op->o_tmpfree( buf, op->o_tmpmemctx );
+ break;
+ default:
+ break;
+ }
+ } else if ( sf ) {
+ char *buf, *ptr;
+ NdbScanFilter::BinaryCondition bc;
+
+ switch(choice) {
+ case LDAP_FILTER_PRESENT:
+ rc = sf->isnotnull( ai->na_column );
+ break;
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_APPROX:
+ bc = NdbScanFilter::COND_EQ;
+ goto setf;
+ case LDAP_FILTER_GE:
+ bc = NdbScanFilter::COND_GE;
+ goto setf;
+ case LDAP_FILTER_LE:
+ bc = NdbScanFilter::COND_LE;
+ setf:
+ rc = sf->cmp( bc, ai->na_column, f->f_av_value.bv_val, f->f_av_value.bv_len );
+ break;
+ case LDAP_FILTER_SUBSTRINGS:
+ rc = 0;
+ if ( f->f_sub_initial.bv_val )
+ rc += f->f_sub_initial.bv_len + 1;
+ if ( f->f_sub_any ) {
+ int i;
+ if ( !rc ) rc++;
+ for (i=0; f->f_sub_any[i].bv_val; i++)
+ rc += f->f_sub_any[i].bv_len + 1;
+ }
+ if ( f->f_sub_final.bv_val ) {
+ if ( !rc ) rc++;
+ rc += f->f_sub_final.bv_len;
+ }
+ buf = (char *)op->o_tmpalloc( rc+1, op->o_tmpmemctx );
+ ptr = buf;
+ if ( f->f_sub_initial.bv_val ) {
+ memcpy( ptr, f->f_sub_initial.bv_val, f->f_sub_initial.bv_len );
+ ptr += f->f_sub_initial.bv_len;
+ *ptr++ = '%';
+ }
+ if ( f->f_sub_any ) {
+ int i;
+ if ( ptr == buf )
+ *ptr++ = '%';
+ for (i=0; f->f_sub_any[i].bv_val; i++) {
+ memcpy( ptr, f->f_sub_any[i].bv_val, f->f_sub_any[i].bv_len );
+ ptr += f->f_sub_any[i].bv_len;
+ *ptr++ = '%';
+ }
+ }
+ if ( f->f_sub_final.bv_val ) {
+ if ( ptr == buf )
+ *ptr++ = '%';
+ memcpy( ptr, f->f_sub_final.bv_val, f->f_sub_final.bv_len );
+ ptr += f->f_sub_final.bv_len;
+ }
+ *ptr = '\0';
+ rc = sf->cmp( NdbScanFilter::COND_LIKE, ai->na_column, buf, ptr - buf );
+ op->o_tmpfree( buf, op->o_tmpmemctx );
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int ndb_oc_search( Operation *op, SlapReply *rs, Ndb *ndb, NdbTransaction *txn,
+ NdbRdns *rbase, NdbOcInfo *oci, int indexed )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ const NdbDictionary::Dictionary *myDict = ndb->getDictionary();
+ const NdbDictionary::Table *myTable;
+ const NdbDictionary::Index *myIndex;
+ NdbIndexScanOperation *scan;
+ NdbIndexOperation *ixop;
+ NdbScanFilter *sf = NULL;
+ struct berval *ocs;
+ NdbRecAttr *scanID, *scanOC, *scanDN[NDB_MAX_RDNS];
+ char dnBuf[2048], *ptr;
+ NdbRdns rdns;
+ NdbArgs NA;
+ char idbuf[2*sizeof(ID)];
+ char ocbuf[NDB_OC_BUFLEN];
+ int i, rc, bounds;
+ Entry e = {0};
+ Uint64 eid;
+ time_t stoptime;
+ int manageDSAit;
+
+ stoptime = op->o_time + op->ors_tlimit;
+ manageDSAit = get_manageDSAit( op );
+
+ myTable = myDict->getTable( oci->no_table.bv_val );
+ if ( indexed ) {
+ scan = txn->getNdbIndexScanOperation( INDEX_NAME, DN2ID_TABLE );
+ if ( !scan )
+ return LDAP_OTHER;
+ scan->readTuples( NdbOperation::LM_CommittedRead );
+ } else {
+ myIndex = myDict->getIndex( "eid$unique", DN2ID_TABLE );
+ if ( !myIndex ) {
+ Debug( LDAP_DEBUG_ANY, DN2ID_TABLE " eid index is missing!\n", 0, 0, 0 );
+ rs->sr_err = LDAP_OTHER;
+ goto leave;
+ }
+ scan = (NdbIndexScanOperation *)txn->getNdbScanOperation( myTable );
+ if ( !scan )
+ return LDAP_OTHER;
+ scan->readTuples( NdbOperation::LM_CommittedRead );
+#if 1
+ sf = new NdbScanFilter(scan);
+ if ( !sf )
+ return LDAP_OTHER;
+ switch ( op->ors_filter->f_choice ) {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ break;
+ default:
+ if ( sf->begin() < 0 ) {
+ rc = LDAP_OTHER;
+ goto leave;
+ }
+ }
+#endif
+ }
+
+ bounds = 0;
+ rc = ndb_filter_set( op, ni, op->ors_filter, indexed, scan, sf, &bounds );
+ if ( rc )
+ goto leave;
+ if ( sf ) sf->end();
+
+ scanID = scan->getValue( EID_COLUMN, idbuf );
+ if ( indexed ) {
+ scanOC = scan->getValue( OCS_COLUMN, ocbuf );
+ for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+ rdns.nr_buf[i][0] = '\0';
+ scanDN[i] = scan->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
+ }
+ }
+
+ if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) {
+ rs->sr_err = LDAP_OTHER;
+ goto leave;
+ }
+
+ e.e_name.bv_val = dnBuf;
+ NA.e = &e;
+ NA.ndb = ndb;
+ while ( scan->nextResult( true, true ) == 0 ) {
+ NdbTransaction *tx2;
+ if ( op->o_abandon ) {
+ rs->sr_err = SLAPD_ABANDON;
+ break;
+ }
+ if ( slapd_shutdown ) {
+ rs->sr_err = LDAP_UNAVAILABLE;
+ break;
+ }
+ if ( op->ors_tlimit != SLAP_NO_LIMIT &&
+ slap_get_time() > stoptime ) {
+ rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
+ break;
+ }
+
+ eid = scanID->u_64_value();
+ e.e_id = eid;
+ if ( !indexed ) {
+ tx2 = ndb->startTransaction( myTable );
+ if ( !tx2 ) {
+ rs->sr_err = LDAP_OTHER;
+ goto leave;
+ }
+
+ ixop = tx2->getNdbIndexOperation( myIndex );
+ if ( !ixop ) {
+ tx2->close();
+ rs->sr_err = LDAP_OTHER;
+ goto leave;
+ }
+ ixop->readTuple( NdbOperation::LM_CommittedRead );
+ ixop->equal( EID_COLUMN, eid );
+
+ scanOC = ixop->getValue( OCS_COLUMN, ocbuf );
+ for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+ rdns.nr_buf[i][0] = '\0';
+ scanDN[i] = ixop->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
+ }
+ rc = tx2->execute( NdbTransaction::Commit, NdbOperation::AbortOnError, 1 );
+ tx2->close();
+ if ( rc ) {
+ rs->sr_err = LDAP_OTHER;
+ goto leave;
+ }
+ }
+
+ ocs = ndb_ref2oclist( ocbuf, op->o_tmpmemctx );
+ for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+ if ( scanDN[i]->isNULL() || !rdns.nr_buf[i][0] )
+ break;
+ }
+ rdns.nr_num = i;
+
+ /* entry must be subordinate to the base */
+ if ( i < rbase->nr_num ) {
+ continue;
+ }
+
+ ptr = dnBuf;
+ for ( --i; i>=0; i-- ) {
+ char *buf;
+ int len;
+ buf = rdns.nr_buf[i];
+ len = *buf++;
+ ptr = lutil_strncopy( ptr, buf, len );
+ if ( i ) *ptr++ = ',';
+ }
+ *ptr = '\0';
+ e.e_name.bv_len = ptr - dnBuf;
+
+ /* More scope checks */
+ /* If indexed, these can be moved into the ScanFilter */
+ switch( op->ors_scope ) {
+ case LDAP_SCOPE_ONELEVEL:
+ if ( rdns.nr_num != rbase->nr_num+1 )
+ continue;
+ case LDAP_SCOPE_SUBORDINATE:
+ if ( rdns.nr_num == rbase->nr_num )
+ continue;
+ case LDAP_SCOPE_SUBTREE:
+ default:
+ if ( e.e_name.bv_len <= op->o_req_dn.bv_len ) {
+ if ( op->ors_scope != LDAP_SCOPE_SUBTREE ||
+ strcasecmp( op->o_req_dn.bv_val, e.e_name.bv_val ))
+ continue;
+ } else if ( strcasecmp( op->o_req_dn.bv_val, e.e_name.bv_val +
+ e.e_name.bv_len - op->o_req_dn.bv_len ))
+ continue;
+ }
+
+ dnNormalize( 0, NULL, NULL, &e.e_name, &e.e_nname, op->o_tmpmemctx );
+ {
+#if 1 /* NDBapi was broken here but seems to work now */
+ Ndb::Key_part_ptr keys[2];
+ char xbuf[512];
+ keys[0].ptr = &eid;
+ keys[0].len = sizeof(eid);
+ keys[1].ptr = NULL;
+ keys[1].len = 0;
+ tx2 = ndb->startTransaction( myTable, keys, xbuf, sizeof(xbuf));
+#else
+ tx2 = ndb->startTransaction( myTable );
+#endif
+ if ( !tx2 ) {
+ rs->sr_err = LDAP_OTHER;
+ goto leave;
+ }
+ NA.txn = tx2;
+ NA.ocs = ocs;
+ rc = ndb_entry_get_data( op, &NA, 0 );
+ tx2->close();
+ }
+ ber_bvarray_free_x( ocs, op->o_tmpmemctx );
+ if ( !manageDSAit && is_entry_referral( &e )) {
+ BerVarray erefs = get_entry_referrals( op, &e );
+ rs->sr_ref = referral_rewrite( erefs, &e.e_name, NULL,
+ op->ors_scope == LDAP_SCOPE_ONELEVEL ?
+ LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
+ rc = send_search_reference( op, rs );
+ ber_bvarray_free( rs->sr_ref );
+ ber_bvarray_free( erefs );
+ rs->sr_ref = NULL;
+ } else if ( manageDSAit || !is_entry_glue( &e )) {
+ rc = test_filter( op, &e, op->ors_filter );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rs->sr_entry = &e;
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_flags = 0;
+ rc = send_search_entry( op, rs );
+ rs->sr_entry = NULL;
+ rs->sr_attrs = NULL;
+ } else {
+ rc = 0;
+ }
+ }
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx );
+ if ( rc ) break;
+ }
+leave:
+ if ( sf ) delete sf;
+ return rc;
+}
+
+extern "C"
+int ndb_back_search( Operation *op, SlapReply *rs )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ NdbTransaction *txn;
+ NdbIndexScanOperation *scan;
+ NdbScanFilter *sf = NULL;
+ Entry e = {0};
+ int rc, i, ocfilter, indexed;
+ struct berval matched;
+ NdbRecAttr *scanID, *scanOC, *scanDN[NDB_MAX_RDNS];
+ char dnBuf[2048], *ptr;
+ char idbuf[2*sizeof(ID)];
+ char ocbuf[NDB_OC_BUFLEN];
+ NdbRdns rdns;
+ NdbOcInfo *oci;
+ NdbArgs NA;
+ slap_mask_t mask;
+ time_t stoptime;
+ int manageDSAit;
+
+ rc = ndb_thread_handle( op, &NA.ndb );
+ rdns.nr_num = 0;
+
+ manageDSAit = get_manageDSAit( op );
+
+ txn = NA.ndb->startTransaction();
+ if ( !txn ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_search) ": startTransaction failed: %s (%d)\n",
+ NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto leave;
+ }
+
+ NA.txn = txn;
+ e.e_name = op->o_req_dn;
+ e.e_nname = op->o_req_ndn;
+ NA.e = &e;
+ NA.rdns = &rdns;
+ NA.ocs = NULL;
+
+ rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched );
+ if ( rs->sr_err ) {
+ if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
+ rs->sr_matched = matched.bv_val;
+ if ( NA.ocs )
+ ndb_check_referral( op, rs, &NA );
+ }
+ goto leave;
+ }
+
+ if ( !access_allowed_mask( op, &e, slap_schema.si_ad_entry,
+ NULL, ACL_SEARCH, NULL, &mask )) {
+ if ( !ACL_GRANT( mask, ACL_DISCLOSE ))
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ else
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+ goto leave;
+ }
+
+ rs->sr_err = ndb_entry_get_data( op, &NA, 0 );
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+ if ( rs->sr_err )
+ goto leave;
+
+ if ( !manageDSAit && is_entry_referral( &e )) {
+ rs->sr_ref = get_entry_referrals( op, &e );
+ rs->sr_err = LDAP_REFERRAL;
+ if ( rs->sr_ref )
+ rs->sr_flags |= REP_REF_MUSTBEFREED;
+ rs->sr_matched = e.e_name.bv_val;
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ goto leave;
+ }
+
+ if ( !manageDSAit && is_entry_glue( &e )) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto leave;
+ }
+
+ if ( get_assert( op ) && test_filter( op, &e, (Filter *)get_assertion( op )) !=
+ LDAP_COMPARE_TRUE ) {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ goto leave;
+ }
+
+ /* admin ignores tlimits */
+ stoptime = op->o_time + op->ors_tlimit;
+
+ if ( op->ors_scope == LDAP_SCOPE_BASE ) {
+ rc = test_filter( op, &e, op->ors_filter );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rs->sr_entry = &e;
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_flags = 0;
+ send_search_entry( op, rs );
+ rs->sr_entry = NULL;
+ }
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ rs->sr_err = LDAP_SUCCESS;
+ goto leave;
+ } else {
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ if ( rdns.nr_num == NDB_MAX_RDNS ) {
+ if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ||
+ op->ors_scope == LDAP_SCOPE_CHILDREN )
+ rs->sr_err = LDAP_SUCCESS;
+ goto leave;
+ }
+ }
+
+ /* See if we can handle the filter. Filtering on objectClass is only done
+ * in the DN2ID table scan. If all other filter terms reside in one table,
+ * then we scan the OC table instead of the DN2ID table.
+ */
+ oci = NULL;
+ indexed = 0;
+ ocfilter = 0;
+ rc = ndb_filter_check( ni, op->ors_filter, &oci, &indexed, &ocfilter );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_TRACE, "ndb_back_search: "
+ "filter attributes from multiple tables, indexing ignored\n",
+ 0, 0, 0 );
+ } else if ( oci ) {
+ rc = ndb_oc_search( op, rs, NA.ndb, txn, &rdns, oci, indexed );
+ goto leave;
+ }
+
+ scan = txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
+ if ( !scan ) {
+ rs->sr_err = LDAP_OTHER;
+ goto leave;
+ }
+ scan->readTuples( NdbOperation::LM_CommittedRead );
+ rc = ndb_dn2bound( scan, &rdns );
+
+ /* TODO: if ( ocfilter ) set up scanfilter for objectclass matches
+ * column COND_LIKE "% <class> %"
+ */
+
+ switch( op->ors_scope ) {
+ case LDAP_SCOPE_ONELEVEL:
+ sf = new NdbScanFilter(scan);
+ if ( sf->begin() < 0 ||
+ sf->cmp(NdbScanFilter::COND_NOT_LIKE, rc+3, "_%",
+ STRLENOF("_%")) < 0 ||
+ sf->end() < 0 ) {
+ rs->sr_err = LDAP_OTHER;
+ goto leave;
+ }
+ /* FALLTHRU */
+ case LDAP_SCOPE_CHILDREN:
+ /* Note: RDN_COLUMN offset not needed here */
+ scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" );
+ /* FALLTHRU */
+ case LDAP_SCOPE_SUBTREE:
+ break;
+ }
+ scanID = scan->getValue( EID_COLUMN, idbuf );
+ scanOC = scan->getValue( OCS_COLUMN, ocbuf );
+ for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+ rdns.nr_buf[i][0] = '\0';
+ scanDN[i] = scan->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
+ }
+ if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) {
+ rs->sr_err = LDAP_OTHER;
+ goto leave;
+ }
+
+ e.e_name.bv_val = dnBuf;
+ while ( scan->nextResult( true, true ) == 0 ) {
+ if ( op->o_abandon ) {
+ rs->sr_err = SLAPD_ABANDON;
+ break;
+ }
+ if ( slapd_shutdown ) {
+ rs->sr_err = LDAP_UNAVAILABLE;
+ break;
+ }
+ if ( op->ors_tlimit != SLAP_NO_LIMIT &&
+ slap_get_time() > stoptime ) {
+ rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
+ break;
+ }
+ e.e_id = scanID->u_64_value();
+ NA.ocs = ndb_ref2oclist( ocbuf, op->o_tmpmemctx );
+ for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+ if ( scanDN[i]->isNULL() || !rdns.nr_buf[i][0] )
+ break;
+ }
+ ptr = dnBuf;
+ rdns.nr_num = i;
+ for ( --i; i>=0; i-- ) {
+ char *buf;
+ int len;
+ buf = rdns.nr_buf[i];
+ len = *buf++;
+ ptr = lutil_strncopy( ptr, buf, len );
+ if ( i ) *ptr++ = ',';
+ }
+ *ptr = '\0';
+ e.e_name.bv_len = ptr - dnBuf;
+ dnNormalize( 0, NULL, NULL, &e.e_name, &e.e_nname, op->o_tmpmemctx );
+ NA.txn = NA.ndb->startTransaction();
+ rc = ndb_entry_get_data( op, &NA, 0 );
+ NA.txn->close();
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+ if ( !manageDSAit && is_entry_referral( &e )) {
+ BerVarray erefs = get_entry_referrals( op, &e );
+ rs->sr_ref = referral_rewrite( erefs, &e.e_name, NULL,
+ op->ors_scope == LDAP_SCOPE_ONELEVEL ?
+ LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
+ rc = send_search_reference( op, rs );
+ ber_bvarray_free( rs->sr_ref );
+ ber_bvarray_free( erefs );
+ rs->sr_ref = NULL;
+ } else if ( manageDSAit || !is_entry_glue( &e )) {
+ rc = test_filter( op, &e, op->ors_filter );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rs->sr_entry = &e;
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_flags = 0;
+ rc = send_search_entry( op, rs );
+ rs->sr_entry = NULL;
+ } else {
+ rc = 0;
+ }
+ }
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx );
+ if ( rc ) break;
+ }
+leave:
+ if ( sf )
+ delete sf;
+ if ( txn )
+ txn->close();
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+}
+
+extern NdbInterpretedCode *ndb_lastrow_code; /* init.cpp */
+
+extern "C" int
+ndb_has_children(
+ NdbArgs *NA,
+ int *hasChildren
+)
+{
+ NdbIndexScanOperation *scan;
+ char idbuf[2*sizeof(ID)];
+ int rc;
+
+ if ( NA->rdns->nr_num >= NDB_MAX_RDNS ) {
+ *hasChildren = LDAP_COMPARE_FALSE;
+ return 0;
+ }
+
+ scan = NA->txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
+ if ( !scan )
+ return LDAP_OTHER;
+ scan->readTuples( NdbOperation::LM_Read, 0U, 0U, 1U );
+ rc = ndb_dn2bound( scan, NA->rdns );
+ if ( rc < NDB_MAX_RDNS ) {
+ scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" );
+ }
+#if 0
+ scan->interpret_exit_last_row();
+#else
+ scan->setInterpretedCode(ndb_lastrow_code);
+#endif
+ scan->getValue( EID_COLUMN, idbuf );
+ if ( NA->txn->execute( NdbTransaction::NoCommit, NdbOperation::AO_IgnoreError, 1 )) {
+ return LDAP_OTHER;
+ }
+ if (rc < NDB_MAX_RDNS && scan->nextResult( true, true ) == 0 )
+ *hasChildren = LDAP_COMPARE_TRUE;
+ else
+ *hasChildren = LDAP_COMPARE_FALSE;
+ scan->close();
+ return 0;
+}
+
+extern "C" int
+ndb_has_subordinates(
+ Operation *op,
+ Entry *e,
+ int *hasSubordinates )
+{
+ NdbArgs NA;
+ NdbRdns rdns;
+ int rc;
+
+ NA.rdns = &rdns;
+ rc = ndb_dn2rdns( &e->e_nname, &rdns );
+
+ if ( rc == 0 ) {
+ rc = ndb_thread_handle( op, &NA.ndb );
+ NA.txn = NA.ndb->startTransaction();
+ if ( NA.txn ) {
+ rc = ndb_has_children( &NA, hasSubordinates );
+ NA.txn->close();
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * sets the supported operational attributes (if required)
+ */
+extern "C" int
+ndb_operational(
+ Operation *op,
+ SlapReply *rs )
+{
+ Attribute **ap;
+
+ assert( rs->sr_entry != NULL );
+
+ for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next ) {
+ if ( (*ap)->a_desc == slap_schema.si_ad_hasSubordinates ) {
+ break;
+ }
+ }
+
+ if ( *ap == NULL &&
+ attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_hasSubordinates ) == NULL &&
+ ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
+ ad_inlist( slap_schema.si_ad_hasSubordinates, rs->sr_attrs ) ) )
+ {
+ int hasSubordinates, rc;
+
+ rc = ndb_has_subordinates( op, rs->sr_entry, &hasSubordinates );
+ if ( rc == LDAP_SUCCESS ) {
+ *ap = slap_operational_hasSubordinate( hasSubordinates == LDAP_COMPARE_TRUE );
+ assert( *ap != NULL );
+
+ ap = &(*ap)->a_next;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
diff --git a/servers/slapd/back-ndb/tools.cpp b/servers/slapd/back-ndb/tools.cpp
new file mode 100644
index 0000000..13ea7c3
--- /dev/null
+++ b/servers/slapd/back-ndb/tools.cpp
@@ -0,0 +1,544 @@
+/* tools.cpp - tools for slap tools */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/errno.h>
+
+#include "lutil.h"
+
+#include "back-ndb.h"
+
+typedef struct dn_id {
+ ID id;
+ struct berval dn;
+} dn_id;
+
+#define HOLE_SIZE 4096
+static dn_id hbuf[HOLE_SIZE], *holes = hbuf;
+static unsigned nhmax = HOLE_SIZE;
+static unsigned nholes;
+static Avlnode *myParents;
+
+static Ndb *myNdb;
+static NdbTransaction *myScanTxn;
+static NdbIndexScanOperation *myScanOp;
+
+static NdbRecAttr *myScanID, *myScanOC;
+static NdbRecAttr *myScanDN[NDB_MAX_RDNS];
+static char myDNbuf[2048];
+static char myIdbuf[2*sizeof(ID)];
+static char myOcbuf[NDB_OC_BUFLEN];
+static NdbRdns myRdns;
+
+static NdbTransaction *myPutTxn;
+static int myPutCnt;
+
+static struct berval *myOcList;
+static struct berval myDn;
+
+extern "C"
+int ndb_tool_entry_open(
+ BackendDB *be, int mode )
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+
+ myNdb = new Ndb( ni->ni_cluster[0], ni->ni_dbname );
+ return myNdb->init(1024);
+}
+
+extern "C"
+int ndb_tool_entry_close(
+ BackendDB *be )
+{
+ if ( myPutTxn ) {
+ int rc = myPutTxn->execute(NdbTransaction::Commit);
+ if( rc != 0 ) {
+ char text[1024];
+ snprintf( text, sizeof(text),
+ "txn_commit failed: %s (%d)",
+ myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
+ text, 0, 0 );
+ }
+ myPutTxn->close();
+ myPutTxn = NULL;
+ }
+ myPutCnt = 0;
+
+ if( nholes ) {
+ unsigned i;
+ fprintf( stderr, "Error, entries missing!\n");
+ for (i=0; i<nholes; i++) {
+ fprintf(stderr, " entry %ld: %s\n",
+ holes[i].id, holes[i].dn.bv_val);
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+extern "C"
+ID ndb_tool_entry_next(
+ BackendDB *be )
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ char *ptr;
+ ID id;
+ int i;
+
+ assert( be != NULL );
+ assert( slapMode & SLAP_TOOL_MODE );
+
+ if ( myScanOp->nextResult() ) {
+ myScanOp->close();
+ myScanOp = NULL;
+ myScanTxn->close();
+ myScanTxn = NULL;
+ return NOID;
+ }
+ id = myScanID->u_64_value();
+
+ if ( myOcList ) {
+ ber_bvarray_free( myOcList );
+ }
+ myOcList = ndb_ref2oclist( myOcbuf, NULL );
+ for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+ if ( myScanDN[i]->isNULL() || !myRdns.nr_buf[i][0] )
+ break;
+ }
+ myRdns.nr_num = i;
+ ptr = myDNbuf;
+ for ( --i; i>=0; i-- ) {
+ char *buf;
+ int len;
+ buf = myRdns.nr_buf[i];
+ len = *buf++;
+ ptr = lutil_strncopy( ptr, buf, len );
+ if ( i )
+ *ptr++ = ',';
+ }
+ *ptr = '\0';
+ myDn.bv_val = myDNbuf;
+ myDn.bv_len = ptr - myDNbuf;
+
+ return id;
+}
+
+extern "C"
+ID ndb_tool_entry_first(
+ BackendDB *be )
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ int i;
+
+ myScanTxn = myNdb->startTransaction();
+ if ( !myScanTxn )
+ return NOID;
+
+ myScanOp = myScanTxn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
+ if ( !myScanOp )
+ return NOID;
+
+ if ( myScanOp->readTuples( NdbOperation::LM_CommittedRead, NdbScanOperation::SF_KeyInfo ))
+ return NOID;
+
+ myScanID = myScanOp->getValue( EID_COLUMN, myIdbuf );
+ myScanOC = myScanOp->getValue( OCS_COLUMN, myOcbuf );
+ for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+ myScanDN[i] = myScanOp->getValue( i+RDN_COLUMN, myRdns.nr_buf[i] );
+ }
+ if ( myScanTxn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 ))
+ return NOID;
+
+ return ndb_tool_entry_next( be );
+}
+
+extern "C"
+ID ndb_tool_dn2id_get(
+ Backend *be,
+ struct berval *dn
+)
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ NdbArgs NA;
+ NdbRdns rdns;
+ Entry e;
+ char text[1024];
+ Operation op = {0};
+ Opheader ohdr = {0};
+ int rc;
+
+ if ( BER_BVISEMPTY(dn) )
+ return 0;
+
+ NA.ndb = myNdb;
+ NA.txn = myNdb->startTransaction();
+ if ( !NA.txn ) {
+ snprintf( text, sizeof(text),
+ "startTransaction failed: %s (%d)",
+ myNdb->getNdbError().message, myNdb->getNdbError().code );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(ndb_tool_dn2id_get) ": %s\n",
+ text, 0, 0 );
+ return NOID;
+ }
+ if ( myOcList ) {
+ ber_bvarray_free( myOcList );
+ myOcList = NULL;
+ }
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ NA.e = &e;
+ e.e_name = *dn;
+ NA.rdns = &rdns;
+ NA.ocs = NULL;
+ rc = ndb_entry_get_info( &op, &NA, 0, NULL );
+ myOcList = NA.ocs;
+ NA.txn->close();
+ if ( rc )
+ return NOID;
+
+ myDn = *dn;
+
+ return e.e_id;
+}
+
+extern "C"
+Entry* ndb_tool_entry_get( BackendDB *be, ID id )
+{
+ NdbArgs NA;
+ int rc;
+ char text[1024];
+ Operation op = {0};
+ Opheader ohdr = {0};
+
+ assert( be != NULL );
+ assert( slapMode & SLAP_TOOL_MODE );
+
+ NA.txn = myNdb->startTransaction();
+ if ( !NA.txn ) {
+ snprintf( text, sizeof(text),
+ "start_transaction failed: %s (%d)",
+ myNdb->getNdbError().message, myNdb->getNdbError().code );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(ndb_tool_entry_get) ": %s\n",
+ text, 0, 0 );
+ return NULL;
+ }
+
+ NA.e = entry_alloc();
+ NA.e->e_id = id;
+ ber_dupbv( &NA.e->e_name, &myDn );
+ dnNormalize( 0, NULL, NULL, &NA.e->e_name, &NA.e->e_nname, NULL );
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ NA.ndb = myNdb;
+ NA.ocs = myOcList;
+ rc = ndb_entry_get_data( &op, &NA, 0 );
+
+ if ( rc ) {
+ entry_free( NA.e );
+ NA.e = NULL;
+ }
+ NA.txn->close();
+
+ return NA.e;
+}
+
+static struct berval glueval[] = {
+ BER_BVC("glue"),
+ BER_BVNULL
+};
+
+static int ndb_dnid_cmp( const void *v1, const void *v2 )
+{
+ struct dn_id *dn1 = (struct dn_id *)v1,
+ *dn2 = (struct dn_id *)v2;
+ return ber_bvcmp( &dn1->dn, &dn2->dn );
+}
+
+static int ndb_tool_next_id(
+ Operation *op,
+ NdbArgs *NA,
+ struct berval *text,
+ int hole )
+{
+ struct berval ndn = NA->e->e_nname;
+ int rc;
+
+ if (ndn.bv_len == 0) {
+ NA->e->e_id = 0;
+ return 0;
+ }
+
+ rc = ndb_entry_get_info( op, NA, 0, NULL );
+ if ( rc ) {
+ Attribute *a, tmp = {0};
+ if ( !be_issuffix( op->o_bd, &ndn ) ) {
+ struct dn_id *dptr;
+ struct berval npdn;
+ dnParent( &ndn, &npdn );
+ NA->e->e_nname = npdn;
+ NA->rdns->nr_num--;
+ rc = ndb_tool_next_id( op, NA, text, 1 );
+ NA->e->e_nname = ndn;
+ NA->rdns->nr_num++;
+ if ( rc ) {
+ return rc;
+ }
+ /* If parent didn't exist, it was created just now
+ * and its ID is now in e->e_id.
+ */
+ dptr = (struct dn_id *)ch_malloc( sizeof( struct dn_id ) + npdn.bv_len + 1);
+ dptr->id = NA->e->e_id;
+ dptr->dn.bv_val = (char *)(dptr+1);
+ strcpy(dptr->dn.bv_val, npdn.bv_val );
+ dptr->dn.bv_len = npdn.bv_len;
+ if ( avl_insert( &myParents, dptr, ndb_dnid_cmp, avl_dup_error )) {
+ ch_free( dptr );
+ }
+ }
+ rc = ndb_next_id( op->o_bd, myNdb, &NA->e->e_id );
+ if ( rc ) {
+ snprintf( text->bv_val, text->bv_len,
+ "next_id failed: %s (%d)",
+ myNdb->getNdbError().message, myNdb->getNdbError().code );
+ Debug( LDAP_DEBUG_ANY,
+ "=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
+ return rc;
+ }
+ if ( hole ) {
+ a = NA->e->e_attrs;
+ NA->e->e_attrs = &tmp;
+ tmp.a_desc = slap_schema.si_ad_objectClass;
+ tmp.a_vals = glueval;
+ tmp.a_nvals = tmp.a_vals;
+ tmp.a_numvals = 1;
+ }
+ rc = ndb_entry_put_info( op->o_bd, NA, 0 );
+ if ( hole ) {
+ NA->e->e_attrs = a;
+ }
+ if ( rc ) {
+ snprintf( text->bv_val, text->bv_len,
+ "ndb_entry_put_info failed: %s (%d)",
+ myNdb->getNdbError().message, myNdb->getNdbError().code );
+ Debug( LDAP_DEBUG_ANY,
+ "=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
+ } else if ( hole ) {
+ if ( nholes == nhmax - 1 ) {
+ if ( holes == hbuf ) {
+ holes = (dn_id *)ch_malloc( nhmax * sizeof(dn_id) * 2 );
+ AC_MEMCPY( holes, hbuf, sizeof(hbuf) );
+ } else {
+ holes = (dn_id *)ch_realloc( holes, nhmax * sizeof(dn_id) * 2 );
+ }
+ nhmax *= 2;
+ }
+ ber_dupbv( &holes[nholes].dn, &ndn );
+ holes[nholes++].id = NA->e->e_id;
+ }
+ } else if ( !hole ) {
+ unsigned i;
+
+ for ( i=0; i<nholes; i++) {
+ if ( holes[i].id == NA->e->e_id ) {
+ int j;
+ free(holes[i].dn.bv_val);
+ for (j=i;j<nholes;j++) holes[j] = holes[j+1];
+ holes[j].id = 0;
+ nholes--;
+ rc = ndb_entry_put_info( op->o_bd, NA, 1 );
+ break;
+ } else if ( holes[i].id > NA->e->e_id ) {
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+extern "C"
+ID ndb_tool_entry_put(
+ BackendDB *be,
+ Entry *e,
+ struct berval *text )
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ struct dn_id dtmp, *dptr;
+ NdbArgs NA;
+ NdbRdns rdns;
+ int rc, slow = 0;
+ Operation op = {0};
+ Opheader ohdr = {0};
+
+ assert( be != NULL );
+ assert( slapMode & SLAP_TOOL_MODE );
+
+ assert( text != NULL );
+ assert( text->bv_val != NULL );
+ assert( text->bv_val[0] == '\0' ); /* overconservative? */
+
+ Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(ndb_tool_entry_put)
+ "( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn, 0 );
+
+ if ( !be_issuffix( be, &e->e_nname )) {
+ dnParent( &e->e_nname, &dtmp.dn );
+ dptr = (struct dn_id *)avl_find( myParents, &dtmp, ndb_dnid_cmp );
+ if ( !dptr )
+ slow = 1;
+ }
+
+ rdns.nr_num = 0;
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ if ( !slow ) {
+ rc = ndb_next_id( be, myNdb, &e->e_id );
+ if ( rc ) {
+ snprintf( text->bv_val, text->bv_len,
+ "next_id failed: %s (%d)",
+ myNdb->getNdbError().message, myNdb->getNdbError().code );
+ Debug( LDAP_DEBUG_ANY,
+ "=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
+ return rc;
+ }
+ }
+
+ if ( !myPutTxn )
+ myPutTxn = myNdb->startTransaction();
+ if ( !myPutTxn ) {
+ snprintf( text->bv_val, text->bv_len,
+ "start_transaction failed: %s (%d)",
+ myNdb->getNdbError().message, myNdb->getNdbError().code );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
+ text->bv_val, 0, 0 );
+ return NOID;
+ }
+
+ /* add dn2id indices */
+ ndb_dn2rdns( &e->e_name, &rdns );
+ NA.rdns = &rdns;
+ NA.e = e;
+ NA.ndb = myNdb;
+ NA.txn = myPutTxn;
+ if ( slow ) {
+ rc = ndb_tool_next_id( &op, &NA, text, 0 );
+ if( rc != 0 ) {
+ goto done;
+ }
+ } else {
+ rc = ndb_entry_put_info( be, &NA, 0 );
+ if ( rc != 0 ) {
+ goto done;
+ }
+ }
+
+ /* id2entry index */
+ rc = ndb_entry_put_data( be, &NA );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "ndb_entry_put_data failed: %s (%d)",
+ myNdb->getNdbError().message, myNdb->getNdbError().code );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
+ text->bv_val, 0, 0 );
+ goto done;
+ }
+
+done:
+ if( rc == 0 ) {
+ myPutCnt++;
+ if ( !( myPutCnt & 0x0f )) {
+ rc = myPutTxn->execute(NdbTransaction::Commit);
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "txn_commit failed: %s (%d)",
+ myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
+ text->bv_val, 0, 0 );
+ e->e_id = NOID;
+ }
+ myPutTxn->close();
+ myPutTxn = NULL;
+ }
+ } else {
+ snprintf( text->bv_val, text->bv_len,
+ "txn_aborted! %s (%d)",
+ myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
+ text->bv_val, 0, 0 );
+ e->e_id = NOID;
+ myPutTxn->close();
+ }
+
+ return e->e_id;
+}
+
+extern "C"
+int ndb_tool_entry_reindex(
+ BackendDB *be,
+ ID id,
+ AttributeDescription **adv )
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+
+ Debug( LDAP_DEBUG_ARGS,
+ "=> " LDAP_XSTRING(ndb_tool_entry_reindex) "( %ld )\n",
+ (long) id, 0, 0 );
+
+ return 0;
+}
+
+extern "C"
+ID ndb_tool_entry_modify(
+ BackendDB *be,
+ Entry *e,
+ struct berval *text )
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ int rc;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> " LDAP_XSTRING(ndb_tool_entry_modify) "( %ld, \"%s\" )\n",
+ (long) e->e_id, e->e_dn, 0 );
+
+done:
+ return e->e_id;
+}
+
diff --git a/servers/slapd/back-null/Makefile.in b/servers/slapd/back-null/Makefile.in
new file mode 100644
index 0000000..707c780
--- /dev/null
+++ b/servers/slapd/back-null/Makefile.in
@@ -0,0 +1,41 @@
+# Makefile.in for back-null
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SRCS = null.c
+OBJS = null.lo
+
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-null"
+BUILD_MOD = @BUILD_NULL@
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = $(@BUILD_NULL@_DEFS)
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+
+LIBBASE = back_null
+
+XINCPATH = -I.. -I$(srcdir)/..
+XDEFS = $(MODULES_CPPFLAGS)
+
+all-local-lib: ../.backend
+
+../.backend: lib$(LIBBASE).a
+ @touch $@
+
diff --git a/servers/slapd/back-null/README b/servers/slapd/back-null/README
new file mode 100644
index 0000000..300afd9
--- /dev/null
+++ b/servers/slapd/back-null/README
@@ -0,0 +1 @@
+The Null Backend is described in the slapd-null(5) manual page.
diff --git a/servers/slapd/back-null/null.c b/servers/slapd/back-null/null.c
new file mode 100644
index 0000000..50b6fbc
--- /dev/null
+++ b/servers/slapd/back-null/null.c
@@ -0,0 +1,472 @@
+/* null.c - the null backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by Hallvard Furuseth for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+
+typedef struct null_info {
+ int ni_bind_allowed;
+ int ni_dosearch;
+ ID ni_nextid;
+ Entry *ni_entry;
+} null_info;
+
+static ConfigTable nullcfg[] = {
+ { "bind", "true|FALSE", 1, 2, 0, ARG_ON_OFF|ARG_OFFSET,
+ (void *)offsetof(null_info, ni_bind_allowed),
+ "( OLcfgDbAt:8.1 NAME 'olcDbBindAllowed' "
+ "DESC 'Allow binds to this database' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "dosearch", "true|FALSE", 1, 2, 0, ARG_ON_OFF|ARG_OFFSET,
+ (void *)offsetof(null_info, ni_dosearch),
+ "( OLcfgDbAt:8.2 NAME 'olcDbDoSearch' "
+ "DESC 'Return an entry on searches' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED,
+ NULL, NULL, NULL, NULL }
+};
+
+static ConfigOCs nullocs[] = {
+ { "( OLcfgDbOc:8.1 "
+ "NAME 'olcNullConfig' "
+ "DESC 'Null backend configuration' "
+ "SUP olcDatabaseConfig "
+ "MAY ( olcDbBindAllowed $ olcDbDoSearch ) )",
+ Cft_Database, nullcfg },
+ { NULL, 0, NULL }
+};
+
+
+static int
+null_back_db_open( BackendDB *be, ConfigReply *cr )
+{
+ struct null_info *ni = (struct null_info *) be->be_private;
+ struct berval bv[2];
+ AttributeDescription *ad = NULL;
+ const char *text;
+ Entry *e;
+
+ if ( ni->ni_dosearch ) {
+ e = entry_alloc();
+ e->e_name = be->be_suffix[0];
+ e->e_nname = be->be_nsuffix[0];
+
+ dnRdn( &e->e_nname, &bv[0] );
+ bv[1].bv_val = strchr(bv[0].bv_val, '=') + 1;
+ bv[1].bv_len = bv[0].bv_len - (bv[1].bv_val -
+ bv[0].bv_val);
+ bv[0].bv_len -= bv[1].bv_len + 1;
+ slap_bv2ad( &bv[0], &ad, &text );
+ attr_merge_one( e, ad, &bv[1], NULL );
+
+ ber_str2bv("extensibleObject", 0, 0, &bv[0]);
+ attr_merge_one( e, slap_schema.si_ad_objectClass, &bv[0], NULL);
+ ni->ni_entry = e;
+ }
+ return 0;
+}
+
+/* LDAP operations */
+
+static int
+null_back_bind( Operation *op, SlapReply *rs )
+{
+ struct null_info *ni = (struct null_info *) op->o_bd->be_private;
+
+ if ( ni->ni_bind_allowed || be_isroot_pw( op ) ) {
+ /* front end will send result on success (0) */
+ return LDAP_SUCCESS;
+ }
+
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ send_ldap_result( op, rs );
+
+ return rs->sr_err;
+}
+
+
+static int
+null_back_respond( Operation *op, SlapReply *rs, int rc )
+{
+ LDAPControl ctrl[SLAP_MAX_RESPONSE_CONTROLS], *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int c = 0;
+
+ BerElementBuffer ps_berbuf;
+ BerElement *ps_ber = NULL;
+ LDAPControl **preread_ctrl = NULL,
+ **postread_ctrl = NULL;
+
+ rs->sr_err = LDAP_OTHER;
+
+ /* this comes first, as in case of assertion failure
+ * any further processing must stop */
+ if ( get_assert( op ) ) {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto respond;
+ }
+
+ if ( op->o_preread ) {
+ Entry e = { 0 };
+
+ switch ( op->o_tag ) {
+ case LDAP_REQ_MODIFY:
+ case LDAP_REQ_RENAME:
+ case LDAP_REQ_DELETE:
+ e.e_name = op->o_req_dn;
+ e.e_nname = op->o_req_ndn;
+
+ preread_ctrl = &ctrls[c];
+ *preread_ctrl = NULL;
+
+ if ( slap_read_controls( op, rs, &e,
+ &slap_pre_read_bv, preread_ctrl ) )
+ {
+ preread_ctrl = NULL;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- null_back_respond: pre-read "
+ "failed!\n" );
+
+ if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto respond;
+ }
+
+ } else {
+ c++;
+ }
+ break;
+ }
+ }
+
+ if ( op->o_postread ) {
+ Entry e = { 0 };
+
+ switch ( op->o_tag ) {
+ case LDAP_REQ_ADD:
+ case LDAP_REQ_MODIFY:
+ case LDAP_REQ_RENAME:
+ if ( op->o_tag == LDAP_REQ_ADD ) {
+ e.e_name = op->ora_e->e_name;
+ e.e_nname = op->ora_e->e_nname;
+
+ } else {
+ e.e_name = op->o_req_dn;
+ e.e_nname = op->o_req_ndn;
+ }
+
+ postread_ctrl = &ctrls[c];
+ *postread_ctrl = NULL;
+
+ if ( slap_read_controls( op, rs, &e,
+ &slap_post_read_bv, postread_ctrl ) )
+ {
+ postread_ctrl = NULL;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- null_back_respond: post-read "
+ "failed!\n" );
+
+ if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto respond;
+ }
+
+ } else {
+ c++;
+ }
+ break;
+ }
+ }
+
+ if ( op->o_noop ) {
+ switch ( op->o_tag ) {
+ case LDAP_REQ_ADD:
+ case LDAP_REQ_MODIFY:
+ case LDAP_REQ_RENAME:
+ case LDAP_REQ_DELETE:
+ case LDAP_REQ_EXTENDED:
+ rc = LDAP_X_NO_OPERATION;
+ break;
+ }
+ }
+
+ if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
+ struct berval cookie = BER_BVC( "" );
+
+ /* should not be here... */
+ assert( op->o_tag == LDAP_REQ_SEARCH );
+
+ ctrl[c].ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
+ ctrl[c].ldctl_iscritical = 0;
+
+ ps_ber = (BerElement *)&ps_berbuf;
+ ber_init2( ps_ber, NULL, LBER_USE_DER );
+
+ /* return size of 0 -- no estimate */
+ ber_printf( ps_ber, "{iO}", 0, &cookie );
+
+ if ( ber_flatten2( ps_ber, &ctrl[c].ldctl_value, 0 ) == -1 ) {
+ goto done;
+ }
+
+ ctrls[c] = &ctrl[c];
+ c++;
+ }
+
+ /* terminate controls array */
+ ctrls[c] = NULL;
+ rs->sr_ctrls = ctrls;
+ rs->sr_err = rc;
+
+respond:;
+ send_ldap_result( op, rs );
+ rs->sr_ctrls = NULL;
+
+done:;
+ if ( ps_ber != NULL ) {
+ (void) ber_free_buf( ps_ber );
+ }
+
+ if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
+ slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
+ }
+
+ if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
+ slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
+ }
+
+ return rs->sr_err;
+}
+
+/* add, delete, modify, modrdn, search */
+static int
+null_back_success( Operation *op, SlapReply *rs )
+{
+ return null_back_respond( op, rs, LDAP_SUCCESS );
+}
+
+/* compare */
+static int
+null_back_false( Operation *op, SlapReply *rs )
+{
+ return null_back_respond( op, rs, LDAP_COMPARE_FALSE );
+}
+
+static int
+null_back_search( Operation *op, SlapReply *rs )
+{
+ struct null_info *ni = (struct null_info *) op->o_bd->be_private;
+
+ if ( ni->ni_entry ) {
+ rs->sr_entry = ni->ni_entry;
+ rs->sr_flags = 0;
+
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_operational_attrs = NULL;
+ send_search_entry( op, rs );
+ }
+ return null_back_respond( op, rs, LDAP_SUCCESS );
+}
+
+/* for overlays */
+static int
+null_back_entry_get(
+ Operation *op,
+ struct berval *ndn,
+ ObjectClass *oc,
+ AttributeDescription *at,
+ int rw,
+ Entry **ent )
+{
+ /* don't admit the object isn't there */
+ return oc || at ? LDAP_NO_SUCH_ATTRIBUTE : LDAP_BUSY;
+}
+
+static int
+null_back_entry_release(
+ Operation *op,
+ Entry *e,
+ int rw )
+{
+ /* we reuse our entry, don't free it */
+ return 0;
+}
+
+/* Slap tools */
+
+static int
+null_tool_entry_open( BackendDB *be, int mode )
+{
+ return 0;
+}
+
+static int
+null_tool_entry_close( BackendDB *be )
+{
+ assert( be != NULL );
+ return 0;
+}
+
+static ID
+null_tool_entry_first_x( BackendDB *be, struct berval *base, int scope, Filter *f )
+{
+ return NOID;
+}
+
+static ID
+null_tool_entry_next( BackendDB *be )
+{
+ return NOID;
+}
+
+static Entry *
+null_tool_entry_get( BackendDB *be, ID id )
+{
+ assert( slapMode & SLAP_TOOL_MODE );
+ return NULL;
+}
+
+static ID
+null_tool_entry_put( BackendDB *be, Entry *e, struct berval *text )
+{
+ assert( slapMode & SLAP_TOOL_MODE );
+ assert( text != NULL );
+ assert( text->bv_val != NULL );
+ assert( text->bv_val[0] == '\0' ); /* overconservative? */
+
+ e->e_id = ((struct null_info *) be->be_private)->ni_nextid++;
+ return e->e_id;
+}
+
+
+/* Setup */
+
+static int
+null_back_db_init( BackendDB *be, ConfigReply *cr )
+{
+ struct null_info *ni = ch_calloc( 1, sizeof(struct null_info) );
+ ni->ni_bind_allowed = 0;
+ ni->ni_nextid = 1;
+ be->be_private = ni;
+ be->be_cf_ocs = be->bd_info->bi_cf_ocs;
+ return 0;
+}
+
+static int
+null_back_db_destroy( Backend *be, ConfigReply *cr )
+{
+ struct null_info *ni = be->be_private;
+
+ if ( ni->ni_entry ) {
+ entry_free( ni->ni_entry );
+ ni->ni_entry = NULL;
+ }
+ free( be->be_private );
+ return 0;
+}
+
+
+int
+null_back_initialize( BackendInfo *bi )
+{
+ static char *controls[] = {
+ LDAP_CONTROL_ASSERT,
+ LDAP_CONTROL_MANAGEDSAIT,
+ LDAP_CONTROL_NOOP,
+ LDAP_CONTROL_PAGEDRESULTS,
+ LDAP_CONTROL_SUBENTRIES,
+ LDAP_CONTROL_PRE_READ,
+ LDAP_CONTROL_POST_READ,
+ LDAP_CONTROL_X_PERMISSIVE_MODIFY,
+ NULL
+ };
+
+ Debug( LDAP_DEBUG_TRACE,
+ "null_back_initialize: initialize null backend\n" );
+
+ bi->bi_flags |=
+ SLAP_BFLAG_INCREMENT |
+ SLAP_BFLAG_SUBENTRIES |
+ SLAP_BFLAG_ALIASES |
+ SLAP_BFLAG_REFERRALS;
+
+ bi->bi_controls = controls;
+
+ bi->bi_open = 0;
+ bi->bi_close = 0;
+ bi->bi_config = 0;
+ bi->bi_destroy = 0;
+
+ bi->bi_db_init = null_back_db_init;
+ bi->bi_db_config = config_generic_wrapper;
+ bi->bi_db_open = null_back_db_open;
+ bi->bi_db_close = 0;
+ bi->bi_db_destroy = null_back_db_destroy;
+
+ bi->bi_op_bind = null_back_bind;
+ bi->bi_op_unbind = 0;
+ bi->bi_op_search = null_back_search;
+ bi->bi_op_compare = null_back_false;
+ bi->bi_op_modify = null_back_success;
+ bi->bi_op_modrdn = null_back_success;
+ bi->bi_op_add = null_back_success;
+ bi->bi_op_delete = null_back_success;
+ bi->bi_op_abandon = 0;
+
+ bi->bi_extended = 0;
+
+ bi->bi_chk_referrals = 0;
+
+ bi->bi_connection_init = 0;
+ bi->bi_connection_destroy = 0;
+
+ bi->bi_entry_get_rw = null_back_entry_get;
+ bi->bi_entry_release_rw = null_back_entry_release;
+
+ bi->bi_tool_entry_open = null_tool_entry_open;
+ bi->bi_tool_entry_close = null_tool_entry_close;
+ bi->bi_tool_entry_first = backend_tool_entry_first;
+ bi->bi_tool_entry_first_x = null_tool_entry_first_x;
+ bi->bi_tool_entry_next = null_tool_entry_next;
+ bi->bi_tool_entry_get = null_tool_entry_get;
+ bi->bi_tool_entry_put = null_tool_entry_put;
+
+ bi->bi_cf_ocs = nullocs;
+ return config_register_schema( nullcfg, nullocs );
+}
+
+#if SLAPD_NULL == SLAPD_MOD_DYNAMIC
+
+/* conditionally define the init_module() function */
+SLAP_BACKEND_INIT_MODULE( null )
+
+#endif /* SLAPD_NULL == SLAPD_MOD_DYNAMIC */
diff --git a/servers/slapd/back-passwd/Makefile.in b/servers/slapd/back-passwd/Makefile.in
new file mode 100644
index 0000000..937cf88
--- /dev/null
+++ b/servers/slapd/back-passwd/Makefile.in
@@ -0,0 +1,41 @@
+# Makefile.in for back-passwd
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SRCS = search.c config.c init.c
+OBJS = search.lo config.lo init.lo
+
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-passwd"
+BUILD_MOD = @BUILD_PASSWD@
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = $(@BUILD_PASSWD@_DEFS)
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+
+LIBBASE = back_passwd
+
+XINCPATH = -I.. -I$(srcdir)/..
+XDEFS = $(MODULES_CPPFLAGS)
+
+all-local-lib: ../.backend
+
+../.backend: lib$(LIBBASE).a
+ @touch $@
+
diff --git a/servers/slapd/back-passwd/back-passwd.h b/servers/slapd/back-passwd/back-passwd.h
new file mode 100644
index 0000000..d1957cc
--- /dev/null
+++ b/servers/slapd/back-passwd/back-passwd.h
@@ -0,0 +1,31 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _BACK_PASSWD_H
+#define _BACK_PASSWD_H
+
+#include "proto-passwd.h"
+
+LDAP_BEGIN_DECL
+
+extern ldap_pvt_thread_mutex_t passwd_mutex;
+
+extern BI_destroy passwd_back_destroy;
+
+extern BI_op_search passwd_back_search;
+
+LDAP_END_DECL
+
+#endif /* _BACK_PASSWD_H */
diff --git a/servers/slapd/back-passwd/config.c b/servers/slapd/back-passwd/config.c
new file mode 100644
index 0000000..9168e4b
--- /dev/null
+++ b/servers/slapd/back-passwd/config.c
@@ -0,0 +1,73 @@
+/* config.c - passwd backend configuration file routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by the University of Michigan
+ * (as part of U-MICH LDAP).
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "slap.h"
+#include "back-passwd.h"
+#include "slap-config.h"
+
+static ConfigTable passwdcfg[] = {
+ { "file", "filename", 2, 2, 0,
+#ifdef HAVE_SETPWFILE
+ ARG_STRING|ARG_OFFSET, NULL,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgDbAt:9.1 NAME 'olcPasswdFile' "
+ "DESC 'File containing passwd records' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED,
+ NULL, NULL, NULL, NULL }
+};
+
+static ConfigOCs passwdocs[] = {
+ { "( OLcfgDbOc:9.1 "
+ "NAME 'olcPasswdConfig' "
+ "DESC 'Passwd backend configuration' "
+ "SUP olcDatabaseConfig "
+ "MAY olcPasswdFile )",
+ Cft_Database, passwdcfg },
+ { NULL, 0, NULL }
+};
+
+int
+passwd_back_init_cf( BackendInfo *bi )
+{
+ bi->bi_cf_ocs = passwdocs;
+ return config_register_schema( passwdcfg, passwdocs );
+}
diff --git a/servers/slapd/back-passwd/init.c b/servers/slapd/back-passwd/init.c
new file mode 100644
index 0000000..b855c15
--- /dev/null
+++ b/servers/slapd/back-passwd/init.c
@@ -0,0 +1,122 @@
+/* init.c - initialize passwd backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-passwd.h"
+
+ldap_pvt_thread_mutex_t passwd_mutex;
+
+AttributeDescription *ad_sn;
+AttributeDescription *ad_desc;
+
+static BI_db_init passwd_back_db_init;
+
+int
+passwd_back_initialize(
+ BackendInfo *bi
+)
+{
+ ldap_pvt_thread_mutex_init( &passwd_mutex );
+
+ bi->bi_open = passwd_back_open;
+ bi->bi_config = 0;
+ bi->bi_close = 0;
+ bi->bi_destroy = passwd_back_destroy;
+
+ bi->bi_db_init = passwd_back_db_init;
+ bi->bi_db_config = 0;
+ bi->bi_db_open = 0;
+ bi->bi_db_close = 0;
+ bi->bi_db_destroy = 0;
+
+ bi->bi_op_bind = 0;
+ bi->bi_op_unbind = 0;
+ bi->bi_op_search = passwd_back_search;
+ bi->bi_op_compare = 0;
+ bi->bi_op_modify = 0;
+ bi->bi_op_modrdn = 0;
+ bi->bi_op_add = 0;
+ bi->bi_op_delete = 0;
+ bi->bi_op_abandon = 0;
+
+ bi->bi_extended = 0;
+
+ bi->bi_chk_referrals = 0;
+
+ bi->bi_connection_init = 0;
+ bi->bi_connection_destroy = 0;
+
+ return passwd_back_init_cf( bi );
+}
+
+int
+passwd_back_open(
+ BackendInfo *bi
+)
+{
+ const char *text;
+ int rc;
+
+ rc = slap_str2ad( "sn", &ad_sn, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "passwd_back_open: "
+ "slap_str2ad(\"%s\") returned %d: %s\n",
+ "sn", rc, text );
+ return -1;
+ }
+ rc = slap_str2ad( "description", &ad_desc, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "passwd_back_open: "
+ "slap_str2ad(\"%s\") returned %d: %s\n",
+ "description", rc, text );
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+passwd_back_destroy(
+ BackendInfo *bi
+)
+{
+ ldap_pvt_thread_mutex_destroy( &passwd_mutex );
+ return 0;
+}
+
+static int
+passwd_back_db_init(
+ Backend *be,
+ struct config_reply_s *cr
+)
+{
+ be->be_cf_ocs = be->bd_info->bi_cf_ocs;
+ return 0;
+}
+
+#if SLAPD_PASSWD == SLAPD_MOD_DYNAMIC
+
+/* conditionally define the init_module() function */
+SLAP_BACKEND_INIT_MODULE( passwd )
+
+#endif /* SLAPD_PASSWD == SLAPD_MOD_DYNAMIC */
+
diff --git a/servers/slapd/back-passwd/proto-passwd.h b/servers/slapd/back-passwd/proto-passwd.h
new file mode 100644
index 0000000..96bc73e
--- /dev/null
+++ b/servers/slapd/back-passwd/proto-passwd.h
@@ -0,0 +1,33 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef PROTO_PASSWD_H
+#define PROTO_PASSWD_H
+
+LDAP_BEGIN_DECL
+
+extern BI_init passwd_back_initialize;
+extern BI_open passwd_back_open;
+extern BI_destroy passwd_back_destroy;
+extern BI_op_search passwd_back_search;
+
+extern int passwd_back_init_cf( BackendInfo *bi );
+
+extern AttributeDescription *ad_sn;
+extern AttributeDescription *ad_desc;
+
+LDAP_END_DECL
+
+#endif /* PROTO_PASSWD_H */
diff --git a/servers/slapd/back-passwd/search.c b/servers/slapd/back-passwd/search.c
new file mode 100644
index 0000000..19d847c
--- /dev/null
+++ b/servers/slapd/back-passwd/search.c
@@ -0,0 +1,381 @@
+/* search.c - /etc/passwd backend search function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by the University of Michigan
+ * (as part of U-MICH LDAP). Additional significant contributors
+ * include:
+ * Hallvard B. Furuseth
+ * Howard Chu
+ * Kurt D. Zeilenga
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include <pwd.h>
+
+#include "slap.h"
+#include "back-passwd.h"
+
+static void pw_start( Backend *be );
+
+static int pw2entry(
+ Backend *be,
+ struct passwd *pw,
+ Entry *ep );
+
+int
+passwd_back_search(
+ Operation *op,
+ SlapReply *rs )
+{
+ struct passwd *pw;
+ time_t stoptime = (time_t)-1;
+
+ LDAPRDN rdn = NULL;
+ struct berval parent = BER_BVNULL;
+
+ AttributeDescription *ad_objectClass = slap_schema.si_ad_objectClass;
+
+ if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
+ stoptime = op->o_time + op->ors_tlimit;
+ }
+
+ /* Handle a query for the base of this backend */
+ if ( be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
+ struct berval val;
+
+ rs->sr_matched = op->o_req_dn.bv_val;
+
+ if( op->ors_scope != LDAP_SCOPE_ONELEVEL ) {
+ AttributeDescription *desc = NULL;
+ char *next;
+ Entry e = { 0 };
+
+ /* Create an entry corresponding to the base DN */
+ e.e_name.bv_val = ch_strdup( op->o_req_dn.bv_val );
+ e.e_name.bv_len = op->o_req_dn.bv_len;
+ e.e_nname.bv_val = ch_strdup( op->o_req_ndn.bv_val );
+ e.e_nname.bv_len = op->o_req_ndn.bv_len;
+
+ /* Use the first attribute of the DN
+ * as an attribute within the entry itself.
+ */
+ if( ldap_bv2rdn( &op->o_req_dn, &rdn, &next,
+ LDAP_DN_FORMAT_LDAP ) )
+ {
+ rs->sr_err = LDAP_INVALID_DN_SYNTAX;
+ goto done;
+ }
+
+ if( slap_bv2ad( &rdn[0]->la_attr, &desc, &rs->sr_text )) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ ldap_rdnfree(rdn);
+ goto done;
+ }
+
+ attr_merge_normalize_one( &e, desc, &rdn[0]->la_value, NULL );
+
+ ldap_rdnfree(rdn);
+ rdn = NULL;
+
+ /* Every entry needs an objectclass. We don't really
+ * know if our hardcoded choice here agrees with the
+ * DN that was configured for this backend, but it's
+ * better than nothing.
+ *
+ * should be a configurable item
+ */
+ BER_BVSTR( &val, "organizationalUnit" );
+ attr_merge_one( &e, ad_objectClass, &val, NULL );
+
+ if ( test_filter( op, &e, op->ors_filter ) == LDAP_COMPARE_TRUE ) {
+ rs->sr_entry = &e;
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_flags = REP_ENTRY_MODIFIABLE;
+ send_search_entry( op, rs );
+ rs->sr_flags = 0;
+ rs->sr_attrs = NULL;
+ }
+
+ entry_clean( &e );
+ }
+
+ if ( op->ors_scope != LDAP_SCOPE_BASE ) {
+ /* check all our "children" */
+
+ ldap_pvt_thread_mutex_lock( &passwd_mutex );
+ pw_start( op->o_bd );
+ for ( pw = getpwent(); pw != NULL; pw = getpwent() ) {
+ Entry e = { 0 };
+
+ /* check for abandon */
+ if ( op->o_abandon ) {
+ endpwent();
+ ldap_pvt_thread_mutex_unlock( &passwd_mutex );
+ return( SLAPD_ABANDON );
+ }
+
+ /* check time limit */
+ if ( op->ors_tlimit != SLAP_NO_LIMIT
+ && slap_get_time() > stoptime )
+ {
+ send_ldap_error( op, rs, LDAP_TIMELIMIT_EXCEEDED, NULL );
+ endpwent();
+ ldap_pvt_thread_mutex_unlock( &passwd_mutex );
+ return( 0 );
+ }
+
+ if ( pw2entry( op->o_bd, pw, &e ) ) {
+ rs->sr_err = LDAP_OTHER;
+ endpwent();
+ ldap_pvt_thread_mutex_unlock( &passwd_mutex );
+ goto done;
+ }
+
+ if ( test_filter( op, &e, op->ors_filter ) == LDAP_COMPARE_TRUE ) {
+ /* check size limit */
+ if ( --op->ors_slimit == -1 ) {
+ send_ldap_error( op, rs, LDAP_SIZELIMIT_EXCEEDED, NULL );
+ endpwent();
+ ldap_pvt_thread_mutex_unlock( &passwd_mutex );
+ return( 0 );
+ }
+
+ rs->sr_entry = &e;
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_flags = REP_ENTRY_MODIFIABLE;
+ send_search_entry( op, rs );
+ rs->sr_flags = 0;
+ rs->sr_entry = NULL;
+ }
+
+ entry_clean( &e );
+ }
+ endpwent();
+ ldap_pvt_thread_mutex_unlock( &passwd_mutex );
+ }
+
+ } else {
+ char *next;
+ Entry e = { 0 };
+ int rc;
+
+ if (! be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
+ dnParent( &op->o_req_ndn, &parent );
+ }
+
+ /* This backend is only one layer deep. Don't answer requests for
+ * anything deeper than that.
+ */
+ if( !be_issuffix( op->o_bd, &parent ) ) {
+ int i;
+ for( i=0; op->o_bd->be_nsuffix[i].bv_val != NULL; i++ ) {
+ if( dnIsSuffix( &op->o_req_ndn, &op->o_bd->be_nsuffix[i] ) ) {
+ rs->sr_matched = op->o_bd->be_suffix[i].bv_val;
+ break;
+ }
+ }
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto done;
+ }
+
+ if( op->ors_scope == LDAP_SCOPE_ONELEVEL ) {
+ goto done;
+ }
+
+ if ( ldap_bv2rdn( &op->o_req_dn, &rdn, &next,
+ LDAP_DN_FORMAT_LDAP ))
+ {
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+
+ ldap_pvt_thread_mutex_lock( &passwd_mutex );
+ pw_start( op->o_bd );
+ pw = getpwnam( rdn[0]->la_value.bv_val );
+ if ( pw == NULL ) {
+ rs->sr_matched = parent.bv_val;
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ ldap_pvt_thread_mutex_unlock( &passwd_mutex );
+ goto done;
+ }
+
+ rc = pw2entry( op->o_bd, pw, &e );
+ ldap_pvt_thread_mutex_unlock( &passwd_mutex );
+ if ( rc ) {
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+
+ if ( test_filter( op, &e, op->ors_filter ) == LDAP_COMPARE_TRUE ) {
+ rs->sr_entry = &e;
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_flags = REP_ENTRY_MODIFIABLE;
+ send_search_entry( op, rs );
+ rs->sr_flags = 0;
+ rs->sr_entry = NULL;
+ rs->sr_attrs = NULL;
+ }
+
+ entry_clean( &e );
+ }
+
+done:
+ if( rs->sr_err != LDAP_NO_SUCH_OBJECT ) rs->sr_matched = NULL;
+ send_ldap_result( op, rs );
+
+ if( rdn != NULL ) ldap_rdnfree( rdn );
+
+ return( 0 );
+}
+
+static void
+pw_start(
+ Backend *be
+)
+{
+ endpwent();
+
+#ifdef HAVE_SETPWFILE
+ if ( be->be_private != NULL ) {
+ (void) setpwfile( (char *) be->be_private );
+ }
+#endif /* HAVE_SETPWFILE */
+}
+
+static int
+pw2entry( Backend *be, struct passwd *pw, Entry *e )
+{
+ size_t pwlen;
+ struct berval val;
+ struct berval bv;
+
+ int rc;
+
+ /*
+ * from pw we get pw_name and make it cn
+ * give it an objectclass of person.
+ */
+
+ pwlen = strlen( pw->pw_name );
+ val.bv_len = STRLENOF("uid=,") + ( pwlen + be->be_suffix[0].bv_len );
+ val.bv_val = ch_malloc( val.bv_len + 1 );
+
+ /* rdn attribute type should be a configurable item */
+ sprintf( val.bv_val, "uid=%s,%s",
+ pw->pw_name, be->be_suffix[0].bv_val );
+
+ rc = dnNormalize( 0, NULL, NULL, &val, &bv, NULL );
+ if( rc != LDAP_SUCCESS ) {
+ free( val.bv_val );
+ return( -1 );
+ }
+
+ e->e_name = val;
+ e->e_nname = bv;
+
+ e->e_attrs = NULL;
+
+ /* objectclasses should be configurable items */
+ BER_BVSTR( &val, "person" );
+ attr_merge_one( e, slap_schema.si_ad_objectClass, &val, NULL );
+
+ BER_BVSTR( &val, "uidObject" );
+ attr_merge_one( e, slap_schema.si_ad_objectClass, &val, NULL );
+
+ val.bv_val = pw->pw_name;
+ val.bv_len = pwlen;
+ attr_merge_normalize_one( e, slap_schema.si_ad_uid, &val, NULL ); /* required by uidObject */
+ attr_merge_normalize_one( e, slap_schema.si_ad_cn, &val, NULL ); /* required by person */
+ attr_merge_normalize_one( e, ad_sn, &val, NULL ); /* required by person */
+
+#ifdef HAVE_STRUCT_PASSWD_PW_GECOS
+ /*
+ * if gecos is present, add user's full name as a cn. first process it
+ * according to standard BSD usage. If the processed cn has
+ * a space, use the tail as the surname.
+ */
+ if (pw->pw_gecos[0]) {
+ char *s;
+ char buf[1024];
+
+ ber_str2bv( pw->pw_gecos, 0, 0, &val );
+ attr_merge_normalize_one( e, ad_desc, &val, NULL );
+
+ s = strchr( val.bv_val, (unsigned char)',' );
+ if ( s ) {
+ *s = '\0';
+ val.bv_len = s - val.bv_val;
+ }
+
+ s = strchr( val.bv_val, (unsigned char)'&' );
+ if ( s ) {
+ unsigned r = sizeof buf;
+ /* if name with expanded `&` fits in buf */
+ if ( val.bv_len + pwlen <= r ) {
+ char * d = buf;
+
+ for (;;) {
+ size_t const i = s - val.bv_val;
+ memcpy( d, val.bv_val, i );
+ d += i;
+ memcpy( d, pw->pw_name, pwlen );
+ *d = TOUPPER((unsigned char)*pw->pw_name);
+ d += pwlen;
+ r -= pwlen + i;
+ val.bv_len -= i + 1;
+ val.bv_val = s + 1;
+
+ s = strchr( val.bv_val, (unsigned char)'&' );
+ if (!(s && ( val.bv_len + pwlen <= r )))
+ break;
+ }
+ strcpy( d, val.bv_val );
+ val.bv_len = d - buf + val.bv_len;
+ val.bv_val = buf;
+ } // 1st fits
+ } // 1st &
+
+ if ( val.bv_len && strcasecmp( val.bv_val, pw->pw_name ) ) {
+ attr_merge_normalize_one( e, slap_schema.si_ad_cn, &val, NULL );
+ }
+
+ if ( ( s = strrchr(val.bv_val, ' ' ) ) ) {
+ ber_str2bv( s + 1, 0, 0, &val );
+ attr_merge_normalize_one( e, ad_sn, &val, NULL );
+ }
+ }
+#endif /* HAVE_STRUCT_PASSWD_PW_GECOS */
+
+ return( 0 );
+}
diff --git a/servers/slapd/back-perl/Makefile.in b/servers/slapd/back-perl/Makefile.in
new file mode 100644
index 0000000..3fed1e3
--- /dev/null
+++ b/servers/slapd/back-perl/Makefile.in
@@ -0,0 +1,46 @@
+# Makefile.in for back-perl
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## Portions Copyright 1999 John C. Quillan.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SRCS = init.c search.c close.c config.c bind.c compare.c \
+ modify.c add.c modrdn.c delete.c
+OBJS = init.lo search.lo close.lo config.lo bind.lo compare.lo \
+ modify.lo add.lo modrdn.lo delete.lo
+
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-perl"
+BUILD_MOD = @BUILD_PERL@
+PERL_CPPFLAGS = @PERL_CPPFLAGS@
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = $(@BUILD_PERL@_DEFS)
+MOD_LIBS = @MOD_PERL_LDFLAGS@
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+
+LIBBASE = back_perl
+
+XINCPATH = -I.. -I$(srcdir)/..
+XDEFS = $(PERL_CPPFLAGS) $(MODULES_CPPFLAGS)
+
+all-local-lib: ../.backend
+
+../.backend: lib$(LIBBASE).a
+ @touch $@
+
diff --git a/servers/slapd/back-perl/README b/servers/slapd/back-perl/README
new file mode 100644
index 0000000..1e14a30
--- /dev/null
+++ b/servers/slapd/back-perl/README
@@ -0,0 +1,24 @@
+Differences from 2.0 Perl API:
+
+- Perl 5.6 is supported
+
+- backend methods return actual LDAP result codes, not
+ true/false; this gives the Perl module finer control
+ of the error returned to the client
+
+- a filterSearchResults configuration file directive was
+ added to tell the backend glue that the results returned
+ from the Perl module are candidates only
+
+- the "init" method is called after the backend has been
+ initialized - this lets you do some initialization after
+ *all* configuration file directives have been read
+
+- the interface for the search method is improved to
+ pass the scope, dereferencing policy, size limit, etc.
+ See SampleLDAP.pm for details.
+
+These changes were sponsored by myinternet Limited.
+
+Luke Howard <lukeh@padl.com>
+
diff --git a/servers/slapd/back-perl/SampleLDAP.pm b/servers/slapd/back-perl/SampleLDAP.pm
new file mode 100644
index 0000000..91e9ae3
--- /dev/null
+++ b/servers/slapd/back-perl/SampleLDAP.pm
@@ -0,0 +1,171 @@
+# This is a sample Perl module for the OpenLDAP server slapd.
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## Portions Copyright 1999 John C. Quillan.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+# Usage: Add something like this to slapd.conf:
+#
+# database perl
+# suffix "o=AnyOrg,c=US"
+# perlModulePath /directory/containing/this/module
+# perlModule SampleLDAP
+#
+# See the slapd-perl(5) manual page for details.
+#
+# This demo module keeps an in-memory hash {"DN" => "LDIF entry", ...}
+# built in sub add{} & co. The data is lost when slapd shuts down.
+
+package SampleLDAP;
+use strict;
+use warnings;
+use POSIX;
+
+$SampleLDAP::VERSION = '1.01';
+
+sub new {
+ my $class = shift;
+
+ my $this = {};
+ bless $this, $class;
+ print {*STDERR} "Here in new\n";
+ print {*STDERR} 'Posix Var ' . BUFSIZ . ' and ' . FILENAME_MAX . "\n";
+ return $this;
+}
+
+sub init {
+ return 0;
+}
+
+sub search {
+ my $this = shift;
+ my ( $base, $scope, $deref, $sizeLim, $timeLim, $filterStr, $attrOnly,
+ @attrs )
+ = @_;
+ print {*STDERR} "====$filterStr====\n";
+ $filterStr =~ s/\(|\)//gm;
+ $filterStr =~ s/=/: /m;
+
+ my @match_dn = ();
+ for my $dn ( keys %{$this} ) {
+ if ( $this->{$dn} =~ /$filterStr/imx ) {
+ push @match_dn, $dn;
+ last if ( scalar @match_dn == $sizeLim );
+
+ }
+ }
+
+ my @match_entries = ();
+
+ for my $dn (@match_dn) {
+ push @match_entries, $this->{$dn};
+ }
+
+ return ( 0, @match_entries );
+
+}
+
+sub compare {
+ my $this = shift;
+ my ( $dn, $avaStr ) = @_;
+ my $rc = 5; # LDAP_COMPARE_FALSE
+
+ $avaStr =~ s/=/: /m;
+
+ if ( $this->{$dn} =~ /$avaStr/im ) {
+ $rc = 6; # LDAP_COMPARE_TRUE
+ }
+
+ return $rc;
+}
+
+sub modify {
+ my $this = shift;
+
+ my ( $dn, @list ) = @_;
+
+ while ( @list > 0 ) {
+ my $action = shift @list;
+ my $key = shift @list;
+ my $value = shift @list;
+
+ if ( $action eq 'ADD' ) {
+ $this->{$dn} .= "$key: $value\n";
+
+ }
+ elsif ( $action eq 'DELETE' ) {
+ $this->{$dn} =~ s/^$key:\s*$value\n//im;
+
+ }
+ elsif ( $action eq 'REPLACE' ) {
+ $this->{$dn} =~ s/$key: .*$/$key: $value/im;
+ }
+ }
+
+ return 0;
+}
+
+sub add {
+ my $this = shift;
+
+ my ($entryStr) = @_;
+
+ my ($dn) = ( $entryStr =~ /dn:\s(.*)$/m );
+
+ #
+ # This needs to be here until a normalized dn is
+ # passed to this routine.
+ #
+ $dn = uc $dn;
+ $dn =~ s/\s*//gm;
+
+ $this->{$dn} = $entryStr;
+
+ return 0;
+}
+
+sub modrdn {
+ my $this = shift;
+
+ my ( $dn, $newdn, $delFlag ) = @_;
+
+ $this->{$newdn} = $this->{$dn};
+
+ if ($delFlag) {
+ delete $this->{$dn};
+ }
+ return 0;
+
+}
+
+sub delete {
+ my $this = shift;
+
+ my ($dn) = @_;
+
+ print {*STDERR} "XXXXXX $dn XXXXXXX\n";
+ delete $this->{$dn};
+ return 0;
+}
+
+sub config {
+ my $this = shift;
+
+ my (@args) = @_;
+ local $, = ' - ';
+ print {*STDERR} @args;
+ print {*STDERR} "\n";
+ return 0;
+}
+
+1;
diff --git a/servers/slapd/back-perl/add.c b/servers/slapd/back-perl/add.c
new file mode 100644
index 0000000..2e6cd5c
--- /dev/null
+++ b/servers/slapd/back-perl/add.c
@@ -0,0 +1,62 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 John C. Quillan.
+ * Portions Copyright 2002 myinternet Limited.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "perl_back.h"
+
+int
+perl_back_add(
+ Operation *op,
+ SlapReply *rs )
+{
+ PerlBackend *perl_back = (PerlBackend *) op->o_bd->be_private;
+ int len;
+ int count;
+
+ PERL_SET_CONTEXT( PERL_INTERPRETER );
+ ldap_pvt_thread_mutex_lock( &perl_interpreter_mutex );
+ ldap_pvt_thread_mutex_lock( &entry2str_mutex );
+
+ {
+ dSP; ENTER; SAVETMPS;
+
+ PUSHMARK(sp);
+ XPUSHs( perl_back->pb_obj_ref );
+ XPUSHs(sv_2mortal(newSVpv( entry2str( op->ora_e, &len ), 0 )));
+
+ PUTBACK;
+
+ count = call_method("add", G_SCALAR);
+
+ SPAGAIN;
+
+ if (count != 1) {
+ croak("Big trouble in back_add\n");
+ }
+
+ rs->sr_err = POPi;
+
+ PUTBACK; FREETMPS; LEAVE;
+ }
+
+ ldap_pvt_thread_mutex_unlock( &entry2str_mutex );
+ ldap_pvt_thread_mutex_unlock( &perl_interpreter_mutex );
+
+ send_ldap_result( op, rs );
+
+ Debug( LDAP_DEBUG_ANY, "Perl ADD\n" );
+ return( 0 );
+}
diff --git a/servers/slapd/back-perl/asperl_undefs.h b/servers/slapd/back-perl/asperl_undefs.h
new file mode 100644
index 0000000..80a9243
--- /dev/null
+++ b/servers/slapd/back-perl/asperl_undefs.h
@@ -0,0 +1,38 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* This file is probably obsolete. If it is not, */
+/* #inclusion of it may have to be moved. See ITS#2513. */
+
+/* This file is necessary because both PERL headers */
+/* and OpenLDAP define a number of macros without */
+/* checking whether they're already defined */
+
+#ifndef ASPERL_UNDEFS_H
+#define ASPERL_UNDEFS_H
+
+/* ActiveState Win32 PERL port support */
+/* set in ldap/include/portable.h */
+# ifdef HAVE_WIN32_ASPERL
+/* The following macros are undefined to prevent */
+/* redefinition in PERL headers*/
+# undef gid_t
+# undef uid_t
+# undef mode_t
+# undef caddr_t
+# undef WIN32_LEAN_AND_MEAN
+# endif
+#endif
+
diff --git a/servers/slapd/back-perl/bind.c b/servers/slapd/back-perl/bind.c
new file mode 100644
index 0000000..915c911
--- /dev/null
+++ b/servers/slapd/back-perl/bind.c
@@ -0,0 +1,80 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 John C. Quillan.
+ * Portions Copyright 2002 myinternet Limited.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "perl_back.h"
+
+
+/**********************************************************
+ *
+ * Bind
+ *
+ **********************************************************/
+int
+perl_back_bind(
+ Operation *op,
+ SlapReply *rs )
+{
+ int count;
+
+ PerlBackend *perl_back = (PerlBackend *) op->o_bd->be_private;
+
+ /* allow rootdn as a means to auth without the need to actually
+ * contact the proxied DSA */
+ switch ( be_rootdn_bind( op, rs ) ) {
+ case SLAP_CB_CONTINUE:
+ break;
+
+ default:
+ return rs->sr_err;
+ }
+
+ PERL_SET_CONTEXT( PERL_INTERPRETER );
+ ldap_pvt_thread_mutex_lock( &perl_interpreter_mutex );
+
+ {
+ dSP; ENTER; SAVETMPS;
+
+ PUSHMARK(SP);
+ XPUSHs( perl_back->pb_obj_ref );
+ XPUSHs(sv_2mortal(newSVpv( op->o_req_dn.bv_val , op->o_req_dn.bv_len)));
+ XPUSHs(sv_2mortal(newSVpv( op->orb_cred.bv_val , op->orb_cred.bv_len)));
+ PUTBACK;
+
+ count = call_method("bind", G_SCALAR);
+
+ SPAGAIN;
+
+ if (count != 1) {
+ croak("Big trouble in back_bind\n");
+ }
+
+ rs->sr_err = POPi;
+
+
+ PUTBACK; FREETMPS; LEAVE;
+ }
+
+ ldap_pvt_thread_mutex_unlock( &perl_interpreter_mutex );
+
+ Debug( LDAP_DEBUG_ANY, "Perl BIND returned 0x%04x\n", rs->sr_err );
+
+ /* frontend will send result on success (0) */
+ if( rs->sr_err != LDAP_SUCCESS )
+ send_ldap_result( op, rs );
+
+ return ( rs->sr_err );
+}
diff --git a/servers/slapd/back-perl/close.c b/servers/slapd/back-perl/close.c
new file mode 100644
index 0000000..88b0a2f
--- /dev/null
+++ b/servers/slapd/back-perl/close.c
@@ -0,0 +1,59 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 John C. Quillan.
+ * Portions Copyright 2002 myinternet Limited.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "perl_back.h"
+#include "../slap-config.h"
+/**********************************************************
+ *
+ * Close
+ *
+ **********************************************************/
+
+int
+perl_back_close(
+ BackendInfo *bd
+)
+{
+ perl_destruct(PERL_INTERPRETER);
+ perl_free(PERL_INTERPRETER);
+ PERL_INTERPRETER = NULL;
+#ifdef PERL_SYS_TERM
+ PERL_SYS_TERM();
+#endif
+
+ ldap_pvt_thread_mutex_destroy( &perl_interpreter_mutex );
+
+ return 0;
+}
+
+int
+perl_back_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ PerlBackend *pb = be->be_private;
+
+ ch_free( pb->pb_module_name );
+ ber_bvarray_free( pb->pb_module_path );
+ ber_bvarray_free( pb->pb_module_config );
+
+ free( be->be_private );
+ be->be_private = NULL;
+
+ return 0;
+}
diff --git a/servers/slapd/back-perl/compare.c b/servers/slapd/back-perl/compare.c
new file mode 100644
index 0000000..08e62c9
--- /dev/null
+++ b/servers/slapd/back-perl/compare.c
@@ -0,0 +1,80 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 John C. Quillan.
+ * Portions Copyright 2002 myinternet Limited.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "perl_back.h"
+#include "lutil.h"
+
+/**********************************************************
+ *
+ * Compare
+ *
+ **********************************************************/
+
+int
+perl_back_compare(
+ Operation *op,
+ SlapReply *rs )
+{
+ int count, avalen;
+ char *avastr;
+
+ PerlBackend *perl_back = (PerlBackend *)op->o_bd->be_private;
+
+ avalen = op->orc_ava->aa_desc->ad_cname.bv_len + 1 +
+ op->orc_ava->aa_value.bv_len;
+ avastr = ch_malloc( avalen + 1 );
+
+ lutil_strcopy( lutil_strcopy( lutil_strcopy( avastr,
+ op->orc_ava->aa_desc->ad_cname.bv_val ), "=" ),
+ op->orc_ava->aa_value.bv_val );
+
+ PERL_SET_CONTEXT( PERL_INTERPRETER );
+ ldap_pvt_thread_mutex_lock( &perl_interpreter_mutex );
+
+ {
+ dSP; ENTER; SAVETMPS;
+
+ PUSHMARK(sp);
+ XPUSHs( perl_back->pb_obj_ref );
+ XPUSHs(sv_2mortal(newSVpv( op->o_req_dn.bv_val , op->o_req_dn.bv_len)));
+ XPUSHs(sv_2mortal(newSVpv( avastr , avalen)));
+ PUTBACK;
+
+ count = call_method("compare", G_SCALAR);
+
+ SPAGAIN;
+
+ if (count != 1) {
+ croak("Big trouble in back_compare\n");
+ }
+
+ rs->sr_err = POPi;
+
+ PUTBACK; FREETMPS; LEAVE;
+ }
+
+ ldap_pvt_thread_mutex_unlock( &perl_interpreter_mutex );
+
+ ch_free( avastr );
+
+ send_ldap_result( op, rs );
+
+ Debug( LDAP_DEBUG_ANY, "Perl COMPARE\n" );
+
+ return (0);
+}
+
diff --git a/servers/slapd/back-perl/config.c b/servers/slapd/back-perl/config.c
new file mode 100644
index 0000000..21f198b
--- /dev/null
+++ b/servers/slapd/back-perl/config.c
@@ -0,0 +1,256 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 John C. Quillan.
+ * Portions Copyright 2002 myinternet Limited.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "perl_back.h"
+#include "../slap-config.h"
+
+static ConfigDriver perl_cf;
+
+enum {
+ PERL_MODULE = 1,
+ PERL_PATH,
+ PERL_CONFIG
+};
+
+static ConfigTable perlcfg[] = {
+ { "perlModule", "module", 2, 2, 0,
+ ARG_STRING|ARG_MAGIC|PERL_MODULE, perl_cf,
+ "( OLcfgDbAt:11.1 NAME 'olcPerlModule' "
+ "DESC 'Perl module name' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "perlModulePath", "path", 2, 2, 0,
+ ARG_MAGIC|PERL_PATH, perl_cf,
+ "( OLcfgDbAt:11.2 NAME 'olcPerlModulePath' "
+ "DESC 'Perl module path' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "filterSearchResults", "on|off", 2, 2, 0, ARG_ON_OFF|ARG_OFFSET,
+ (void *)offsetof(PerlBackend, pb_filter_search_results),
+ "( OLcfgDbAt:11.3 NAME 'olcPerlFilterSearchResults' "
+ "DESC 'Filter search results before returning to client' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "perlModuleConfig", "args", 2, 0, 0,
+ ARG_MAGIC|PERL_CONFIG, perl_cf,
+ "( OLcfgDbAt:11.4 NAME 'olcPerlModuleConfig' "
+ "DESC 'Perl module config directives' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { NULL }
+};
+
+static ConfigOCs perlocs[] = {
+ { "( OLcfgDbOc:11.1 "
+ "NAME 'olcDbPerlConfig' "
+ "DESC 'Perl DB configuration' "
+ "SUP olcDatabaseConfig "
+ "MUST ( olcPerlModulePath $ olcPerlModule ) "
+ "MAY ( olcPerlFilterSearchResults $ olcPerlModuleConfig ) )",
+ Cft_Database, perlcfg, NULL, NULL },
+ { NULL }
+};
+
+static ConfigOCs ovperlocs[] = {
+ { "( OLcfgDbOc:11.2 "
+ "NAME 'olcovPerlConfig' "
+ "DESC 'Perl overlay configuration' "
+ "SUP olcOverlayConfig "
+ "MUST ( olcPerlModulePath $ olcPerlModule ) "
+ "MAY ( olcPerlFilterSearchResults $ olcPerlModuleConfig ) )",
+ Cft_Overlay, perlcfg, NULL, NULL },
+ { NULL }
+};
+
+/**********************************************************
+ *
+ * Config
+ *
+ **********************************************************/
+int
+perl_back_db_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv
+)
+{
+ int rc = config_generic_wrapper( be, fname, lineno, argc, argv );
+ /* backward compatibility: map unknown directives to perlModuleConfig */
+ if ( rc == SLAP_CONF_UNKNOWN ) {
+ char **av = ch_malloc( (argc+2) * sizeof(char *));
+ int i;
+ av[0] = "perlModuleConfig";
+ av++;
+ for ( i=0; i<argc; i++ )
+ av[i] = argv[i];
+ av[i] = NULL;
+ av--;
+ rc = config_generic_wrapper( be, fname, lineno, argc+1, av );
+ ch_free( av );
+ }
+ return rc;
+}
+
+static int
+perl_cf(
+ ConfigArgs *c
+)
+{
+ PerlBackend *pb = (PerlBackend *) c->be->be_private;
+ SV* loc_sv;
+ int count ;
+ int args;
+ int rc = 0;
+ char eval_str[EVAL_BUF_SIZE];
+ struct berval bv;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ switch( c-> type ) {
+ case PERL_MODULE:
+ if ( !pb->pb_module_name )
+ return 1;
+ c->value_string = ch_strdup( pb->pb_module_name );
+ break;
+ case PERL_PATH:
+ if ( !pb->pb_module_path )
+ return 1;
+ ber_bvarray_dup_x( &c->rvalue_vals, pb->pb_module_path, NULL );
+ break;
+ case PERL_CONFIG:
+ if ( !pb->pb_module_config )
+ return 1;
+ ber_bvarray_dup_x( &c->rvalue_vals, pb->pb_module_config, NULL );
+ break;
+ }
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ /* FIXME: none of this affects the state of the perl
+ * interpreter at all. We should probably destroy it
+ * and recreate it...
+ */
+ switch( c-> type ) {
+ case PERL_MODULE:
+ ch_free( pb->pb_module_name );
+ pb->pb_module_name = NULL;
+ break;
+ case PERL_PATH:
+ if ( c->valx < 0 ) {
+ ber_bvarray_free( pb->pb_module_path );
+ pb->pb_module_path = NULL;
+ } else {
+ int i = c->valx;
+ ch_free( pb->pb_module_path[i].bv_val );
+ for (; pb->pb_module_path[i].bv_val; i++ )
+ pb->pb_module_path[i] = pb->pb_module_path[i+1];
+ }
+ break;
+ case PERL_CONFIG:
+ if ( c->valx < 0 ) {
+ ber_bvarray_free( pb->pb_module_config );
+ pb->pb_module_config = NULL;
+ } else {
+ int i = c->valx;
+ ch_free( pb->pb_module_config[i].bv_val );
+ for (; pb->pb_module_config[i].bv_val; i++ )
+ pb->pb_module_config[i] = pb->pb_module_config[i+1];
+ }
+ break;
+ }
+ } else {
+ PERL_SET_CONTEXT( PERL_INTERPRETER );
+ switch( c->type ) {
+ case PERL_MODULE:
+ snprintf( eval_str, EVAL_BUF_SIZE, "use %s;", c->argv[1] );
+ eval_pv( eval_str, 0 );
+
+ if (SvTRUE(ERRSV)) {
+ STRLEN len;
+
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: error %s",
+ c->log, SvPV(ERRSV, len ));
+ Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg );
+ rc = 1;
+ } else {
+ dSP; ENTER; SAVETMPS;
+ PUSHMARK(sp);
+ XPUSHs(sv_2mortal(newSVpv(c->argv[1], 0)));
+ PUTBACK;
+
+ count = call_method("new", G_SCALAR);
+
+ SPAGAIN;
+
+ if (count != 1) {
+ croak("Big trouble in config\n") ;
+ }
+
+ pb->pb_obj_ref = newSVsv(POPs);
+
+ PUTBACK; FREETMPS; LEAVE ;
+ pb->pb_module_name = ch_strdup( c->argv[1] );
+ }
+ break;
+
+ case PERL_PATH:
+ snprintf( eval_str, EVAL_BUF_SIZE, "push @INC, '%s';", c->argv[1] );
+ loc_sv = eval_pv( eval_str, 0 );
+ /* XXX loc_sv return value is ignored. */
+ ber_str2bv( c->argv[1], 0, 0, &bv );
+ value_add_one( &pb->pb_module_path, &bv );
+ break;
+
+ case PERL_CONFIG: {
+ dSP ; ENTER ; SAVETMPS;
+
+ PUSHMARK(sp) ;
+ XPUSHs( pb->pb_obj_ref );
+
+ /* Put all arguments on the perl stack */
+ for( args = 1; args < c->argc; args++ )
+ XPUSHs(sv_2mortal(newSVpv(c->argv[args], 0)));
+
+ ber_str2bv( c->line + STRLENOF("perlModuleConfig "), 0, 0, &bv );
+ value_add_one( &pb->pb_module_config, &bv );
+
+ PUTBACK ;
+
+ count = call_method("config", G_SCALAR);
+
+ SPAGAIN ;
+
+ if (count != 1) {
+ croak("Big trouble in config\n") ;
+ }
+
+ rc = POPi;
+
+ PUTBACK ; FREETMPS ; LEAVE ;
+ }
+ break;
+ }
+ }
+ return rc;
+}
+
+int
+perl_back_init_cf( BackendInfo *bi )
+{
+ bi->bi_cf_ocs = perlocs;
+
+ return config_register_schema( perlcfg, perlocs );
+}
diff --git a/servers/slapd/back-perl/delete.c b/servers/slapd/back-perl/delete.c
new file mode 100644
index 0000000..68c1b3a
--- /dev/null
+++ b/servers/slapd/back-perl/delete.c
@@ -0,0 +1,59 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 John C. Quillan.
+ * Portions Copyright 2002 myinternet Limited.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "perl_back.h"
+
+int
+perl_back_delete(
+ Operation *op,
+ SlapReply *rs )
+{
+ PerlBackend *perl_back = (PerlBackend *) op->o_bd->be_private;
+ int count;
+
+ PERL_SET_CONTEXT( PERL_INTERPRETER );
+ ldap_pvt_thread_mutex_lock( &perl_interpreter_mutex );
+
+ {
+ dSP; ENTER; SAVETMPS;
+
+ PUSHMARK(sp);
+ XPUSHs( perl_back->pb_obj_ref );
+ XPUSHs(sv_2mortal(newSVpv( op->o_req_dn.bv_val , op->o_req_dn.bv_len )));
+
+ PUTBACK;
+
+ count = call_method("delete", G_SCALAR);
+
+ SPAGAIN;
+
+ if (count != 1) {
+ croak("Big trouble in perl-back_delete\n");
+ }
+
+ rs->sr_err = POPi;
+
+ PUTBACK; FREETMPS; LEAVE;
+ }
+
+ ldap_pvt_thread_mutex_unlock( &perl_interpreter_mutex );
+
+ send_ldap_result( op, rs );
+
+ Debug( LDAP_DEBUG_ANY, "Perl DELETE\n" );
+ return( 0 );
+}
diff --git a/servers/slapd/back-perl/init.c b/servers/slapd/back-perl/init.c
new file mode 100644
index 0000000..644c855
--- /dev/null
+++ b/servers/slapd/back-perl/init.c
@@ -0,0 +1,176 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 John C. Quillan.
+ * Portions Copyright 2002 myinternet Limited.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "perl_back.h"
+#include "../slap-config.h"
+
+#ifdef PERL_SYS_INIT3
+#include <ac/unistd.h> /* maybe get environ */
+extern char **environ;
+#endif
+
+static void perl_back_xs_init LDAP_P((PERL_BACK_XS_INIT_PARAMS));
+EXT void boot_DynaLoader LDAP_P((PERL_BACK_BOOT_DYNALOADER_PARAMS));
+
+PerlInterpreter *PERL_INTERPRETER = NULL;
+ldap_pvt_thread_mutex_t perl_interpreter_mutex;
+
+
+/**********************************************************
+ *
+ * Init
+ *
+ **********************************************************/
+
+int
+perl_back_initialize(
+ BackendInfo *bi
+)
+{
+ char *embedding[] = { "", "-e", "0", NULL }, **argv = embedding;
+ int argc = 3;
+#ifdef PERL_SYS_INIT3
+ char **env = environ;
+#else
+ char **env = NULL;
+#endif
+
+ bi->bi_open = NULL;
+ bi->bi_config = 0;
+ bi->bi_close = perl_back_close;
+ bi->bi_destroy = 0;
+
+ bi->bi_db_init = perl_back_db_init;
+ bi->bi_db_config = perl_back_db_config;
+ bi->bi_db_open = perl_back_db_open;
+ bi->bi_db_close = 0;
+ bi->bi_db_destroy = perl_back_db_destroy;
+
+ bi->bi_op_bind = perl_back_bind;
+ bi->bi_op_unbind = 0;
+ bi->bi_op_search = perl_back_search;
+ bi->bi_op_compare = perl_back_compare;
+ bi->bi_op_modify = perl_back_modify;
+ bi->bi_op_modrdn = perl_back_modrdn;
+ bi->bi_op_add = perl_back_add;
+ bi->bi_op_delete = perl_back_delete;
+ bi->bi_op_abandon = 0;
+
+ bi->bi_extended = 0;
+
+ bi->bi_chk_referrals = 0;
+
+ bi->bi_connection_init = 0;
+ bi->bi_connection_destroy = 0;
+
+ /* injecting code from perl_back_open, because using function reference (bi->bi_open) is not functional */
+ Debug( LDAP_DEBUG_TRACE, "perl backend open\n" );
+
+ if( PERL_INTERPRETER != NULL ) {
+ Debug( LDAP_DEBUG_ANY, "perl backend open: already opened\n" );
+ return 1;
+ }
+
+ ldap_pvt_thread_mutex_init( &perl_interpreter_mutex );
+
+#ifdef PERL_SYS_INIT3
+ PERL_SYS_INIT3(&argc, &argv, &env);
+#endif
+ PERL_INTERPRETER = perl_alloc();
+ perl_construct(PERL_INTERPRETER);
+#ifdef PERL_EXIT_DESTRUCT_END
+ PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
+#endif
+ perl_parse(PERL_INTERPRETER, perl_back_xs_init, argc, argv, env);
+ perl_run(PERL_INTERPRETER);
+ return perl_back_init_cf( bi );
+}
+
+int
+perl_back_db_init(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ be->be_private = (PerlBackend *) ch_malloc( sizeof(PerlBackend) );
+ memset( be->be_private, '\0', sizeof(PerlBackend));
+
+ ((PerlBackend *)be->be_private)->pb_filter_search_results = 0;
+
+ Debug( LDAP_DEBUG_TRACE, "perl backend db init\n" );
+
+ be->be_cf_ocs = be->bd_info->bi_cf_ocs;
+
+ return 0;
+}
+
+int
+perl_back_db_open(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ int count;
+ int return_code;
+
+ PerlBackend *perl_back = (PerlBackend *) be->be_private;
+
+ ldap_pvt_thread_mutex_lock( &perl_interpreter_mutex );
+
+ {
+ dSP; ENTER; SAVETMPS;
+
+ PUSHMARK(sp);
+ XPUSHs( perl_back->pb_obj_ref );
+
+ PUTBACK;
+
+ count = call_method("init", G_SCALAR);
+
+ SPAGAIN;
+
+ if (count != 1) {
+ croak("Big trouble in perl_back_db_open\n");
+ }
+
+ return_code = POPi;
+
+ PUTBACK; FREETMPS; LEAVE;
+ }
+
+ ldap_pvt_thread_mutex_unlock( &perl_interpreter_mutex );
+
+ return return_code;
+}
+
+
+static void
+perl_back_xs_init(PERL_BACK_XS_INIT_PARAMS)
+{
+ char *file = __FILE__;
+ dXSUB_SYS;
+ newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
+}
+
+#if SLAPD_PERL == SLAPD_MOD_DYNAMIC
+
+/* conditionally define the init_module() function */
+SLAP_BACKEND_INIT_MODULE( perl )
+
+#endif /* SLAPD_PERL == SLAPD_MOD_DYNAMIC */
+
+
diff --git a/servers/slapd/back-perl/modify.c b/servers/slapd/back-perl/modify.c
new file mode 100644
index 0000000..94fed62
--- /dev/null
+++ b/servers/slapd/back-perl/modify.c
@@ -0,0 +1,97 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 John C. Quillan.
+ * Portions Copyright 2002 myinternet Limited.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "perl_back.h"
+#include <ac/string.h>
+
+int
+perl_back_modify(
+ Operation *op,
+ SlapReply *rs )
+{
+ PerlBackend *perl_back = (PerlBackend *)op->o_bd->be_private;
+ Modifications *modlist = op->orm_modlist;
+ int count;
+ int i;
+
+ PERL_SET_CONTEXT( PERL_INTERPRETER );
+ ldap_pvt_thread_mutex_lock( &perl_interpreter_mutex );
+
+ {
+ dSP; ENTER; SAVETMPS;
+
+ PUSHMARK(sp);
+ XPUSHs( perl_back->pb_obj_ref );
+ XPUSHs(sv_2mortal(newSVpv( op->o_req_dn.bv_val , 0)));
+
+ for (; modlist != NULL; modlist = modlist->sml_next ) {
+ Modification *mods = &modlist->sml_mod;
+
+ switch ( mods->sm_op & ~LDAP_MOD_BVALUES ) {
+ case LDAP_MOD_ADD:
+ XPUSHs(sv_2mortal(newSVpv("ADD", STRLENOF("ADD") )));
+ break;
+
+ case LDAP_MOD_DELETE:
+ XPUSHs(sv_2mortal(newSVpv("DELETE", STRLENOF("DELETE") )));
+ break;
+
+ case LDAP_MOD_REPLACE:
+ XPUSHs(sv_2mortal(newSVpv("REPLACE", STRLENOF("REPLACE") )));
+ break;
+ }
+
+
+ XPUSHs(sv_2mortal(newSVpv( mods->sm_desc->ad_cname.bv_val,
+ mods->sm_desc->ad_cname.bv_len )));
+
+ for ( i = 0;
+ mods->sm_values != NULL && mods->sm_values[i].bv_val != NULL;
+ i++ )
+ {
+ XPUSHs(sv_2mortal(newSVpv( mods->sm_values[i].bv_val, mods->sm_values[i].bv_len )));
+ }
+
+ /* Fix delete attrib without value. */
+ if ( i == 0) {
+ XPUSHs(sv_newmortal());
+ }
+ }
+
+ PUTBACK;
+
+ count = call_method("modify", G_SCALAR);
+
+ SPAGAIN;
+
+ if (count != 1) {
+ croak("Big trouble in back_modify\n");
+ }
+
+ rs->sr_err = POPi;
+
+ PUTBACK; FREETMPS; LEAVE;
+ }
+
+ ldap_pvt_thread_mutex_unlock( &perl_interpreter_mutex );
+
+ send_ldap_result( op, rs );
+
+ Debug( LDAP_DEBUG_ANY, "Perl MODIFY\n" );
+ return( 0 );
+}
+
diff --git a/servers/slapd/back-perl/modrdn.c b/servers/slapd/back-perl/modrdn.c
new file mode 100644
index 0000000..4f2dc81
--- /dev/null
+++ b/servers/slapd/back-perl/modrdn.c
@@ -0,0 +1,63 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 John C. Quillan.
+ * Portions Copyright 2002 myinternet Limited.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "perl_back.h"
+
+int
+perl_back_modrdn(
+ Operation *op,
+ SlapReply *rs )
+{
+ PerlBackend *perl_back = (PerlBackend *) op->o_bd->be_private;
+ int count;
+
+ PERL_SET_CONTEXT( PERL_INTERPRETER );
+ ldap_pvt_thread_mutex_lock( &perl_interpreter_mutex );
+
+ {
+ dSP; ENTER; SAVETMPS;
+
+ PUSHMARK(sp) ;
+ XPUSHs( perl_back->pb_obj_ref );
+ XPUSHs(sv_2mortal(newSVpv( op->o_req_dn.bv_val , op->o_req_dn.bv_len )));
+ XPUSHs(sv_2mortal(newSVpv( op->orr_newrdn.bv_val , op->orr_newrdn.bv_len )));
+ XPUSHs(sv_2mortal(newSViv( op->orr_deleteoldrdn )));
+ if ( op->orr_newSup != NULL ) {
+ XPUSHs(sv_2mortal(newSVpv( op->orr_newSup->bv_val , op->orr_newSup->bv_len )));
+ }
+ PUTBACK ;
+
+ count = call_method("modrdn", G_SCALAR);
+
+ SPAGAIN ;
+
+ if (count != 1) {
+ croak("Big trouble in back_modrdn\n") ;
+ }
+
+ rs->sr_err = POPi;
+
+ PUTBACK; FREETMPS; LEAVE ;
+ }
+
+ ldap_pvt_thread_mutex_unlock( &perl_interpreter_mutex );
+
+ send_ldap_result( op, rs );
+
+ Debug( LDAP_DEBUG_ANY, "Perl MODRDN\n" );
+ return( 0 );
+}
diff --git a/servers/slapd/back-perl/perl_back.h b/servers/slapd/back-perl/perl_back.h
new file mode 100644
index 0000000..9446c2d
--- /dev/null
+++ b/servers/slapd/back-perl/perl_back.h
@@ -0,0 +1,82 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 John C. Quillan.
+ * Portions Copyright 2002 myinternet Limited.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef PERL_BACK_H
+#define PERL_BACK_H 1
+
+#include <EXTERN.h>
+#include <perl.h>
+#undef _ /* #defined by both Perl and ac/localize.h */
+#include "asperl_undefs.h"
+
+#include "portable.h"
+
+#include "slap.h"
+
+LDAP_BEGIN_DECL
+
+/*
+ * From Apache mod_perl: test for Perl version.
+ */
+
+#if defined(pTHX_) || (PERL_REVISION > 5 || (PERL_REVISION == 5 && PERL_VERSION >= 6))
+#define PERL_IS_5_6
+#endif
+
+#define EVAL_BUF_SIZE 500
+
+extern ldap_pvt_thread_mutex_t perl_interpreter_mutex;
+
+#ifdef PERL_IS_5_6
+/* We should be using the PL_errgv, I think */
+/* All the old style variables are prefixed with PL_ now */
+# define errgv PL_errgv
+# define na PL_na
+#else
+# define call_method(m, f) perl_call_method(m, f)
+# define eval_pv(m, f) perl_eval_pv(m, f)
+# define ERRSV GvSV(errgv)
+#endif
+
+#if defined( HAVE_WIN32_ASPERL ) || defined( USE_ITHREADS )
+/* pTHX is needed often now */
+# define PERL_INTERPRETER my_perl
+# define PERL_BACK_XS_INIT_PARAMS pTHX
+# define PERL_BACK_BOOT_DYNALOADER_PARAMS pTHX, CV *cv
+#else
+# define PERL_INTERPRETER perl_interpreter
+# define PERL_BACK_XS_INIT_PARAMS void
+# define PERL_BACK_BOOT_DYNALOADER_PARAMS CV *cv
+# define PERL_SET_CONTEXT(i)
+#endif
+
+extern PerlInterpreter *PERL_INTERPRETER;
+
+
+typedef struct perl_backend_instance {
+ char *pb_module_name;
+ BerVarray pb_module_path;
+ BerVarray pb_module_config;
+ SV *pb_obj_ref;
+ int pb_filter_search_results;
+} PerlBackend;
+
+LDAP_END_DECL
+
+#include "proto-perl.h"
+
+#endif /* PERL_BACK_H */
diff --git a/servers/slapd/back-perl/proto-perl.h b/servers/slapd/back-perl/proto-perl.h
new file mode 100644
index 0000000..b6cb387
--- /dev/null
+++ b/servers/slapd/back-perl/proto-perl.h
@@ -0,0 +1,43 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 John C. Quillan.
+ * Portions Copyright 2002 myinternet Limited.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef PROTO_PERL_H
+#define PROTO_PERL_H
+
+LDAP_BEGIN_DECL
+
+extern BI_init perl_back_initialize;
+
+extern BI_close perl_back_close;
+
+extern BI_db_init perl_back_db_init;
+extern BI_db_open perl_back_db_open;
+extern BI_db_destroy perl_back_db_destroy;
+extern BI_db_config perl_back_db_config;
+
+extern BI_op_bind perl_back_bind;
+extern BI_op_search perl_back_search;
+extern BI_op_compare perl_back_compare;
+extern BI_op_modify perl_back_modify;
+extern BI_op_modrdn perl_back_modrdn;
+extern BI_op_add perl_back_add;
+extern BI_op_delete perl_back_delete;
+
+extern int perl_back_init_cf( BackendInfo *bi );
+LDAP_END_DECL
+
+#endif /* PROTO_PERL_H */
diff --git a/servers/slapd/back-perl/search.c b/servers/slapd/back-perl/search.c
new file mode 100644
index 0000000..7566b06
--- /dev/null
+++ b/servers/slapd/back-perl/search.c
@@ -0,0 +1,122 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 John C. Quillan.
+ * Portions Copyright 2002 myinternet Limited.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "perl_back.h"
+
+/**********************************************************
+ *
+ * Search
+ *
+ **********************************************************/
+int
+perl_back_search(
+ Operation *op,
+ SlapReply *rs )
+{
+ PerlBackend *perl_back = (PerlBackend *)op->o_bd->be_private;
+ int count ;
+ AttributeName *an;
+ Entry *e;
+ char *buf;
+ int i;
+
+ PERL_SET_CONTEXT( PERL_INTERPRETER );
+ ldap_pvt_thread_mutex_lock( &perl_interpreter_mutex );
+
+ {
+ dSP; ENTER; SAVETMPS;
+
+ PUSHMARK(sp) ;
+ XPUSHs( perl_back->pb_obj_ref );
+ XPUSHs(sv_2mortal(newSVpv( op->o_req_ndn.bv_val , op->o_req_ndn.bv_len)));
+ XPUSHs(sv_2mortal(newSViv( op->ors_scope )));
+ XPUSHs(sv_2mortal(newSViv( op->ors_deref )));
+ XPUSHs(sv_2mortal(newSViv( op->ors_slimit )));
+ XPUSHs(sv_2mortal(newSViv( op->ors_tlimit )));
+ XPUSHs(sv_2mortal(newSVpv( op->ors_filterstr.bv_val , op->ors_filterstr.bv_len)));
+ XPUSHs(sv_2mortal(newSViv( op->ors_attrsonly )));
+
+ for ( an = op->ors_attrs; an && an->an_name.bv_val; an++ ) {
+ XPUSHs(sv_2mortal(newSVpv( an->an_name.bv_val , an->an_name.bv_len)));
+ }
+ PUTBACK;
+
+ count = call_method("search", G_ARRAY );
+
+ SPAGAIN;
+
+ if (count < 1) {
+ croak("Big trouble in back_search\n") ;
+ }
+
+ if ( count > 1 ) {
+
+ for ( i = 1; i < count; i++ ) {
+
+ buf = POPp;
+
+ if ( (e = str2entry( buf )) == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "str2entry(%s) failed\n", buf );
+
+ } else {
+ int send_entry;
+
+ if (perl_back->pb_filter_search_results)
+ send_entry = (test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE);
+ else
+ send_entry = 1;
+
+ if (send_entry) {
+ rs->sr_entry = e;
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_flags = REP_ENTRY_MODIFIABLE;
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_err = send_search_entry( op, rs );
+ rs->sr_flags = 0;
+ rs->sr_attrs = NULL;
+ rs->sr_entry = NULL;
+ if ( rs->sr_err == LDAP_SIZELIMIT_EXCEEDED || rs->sr_err == LDAP_BUSY ) {
+ goto done;
+ }
+ }
+
+ entry_free( e );
+ }
+ }
+ }
+
+ /*
+ * We grab the return code last because the stack comes
+ * from perl in reverse order.
+ *
+ * ex perl: return ( 0, $res_1, $res_2 );
+ *
+ * ex stack: <$res_2> <$res_1> <0>
+ */
+
+ rs->sr_err = POPi;
+
+done:;
+ PUTBACK; FREETMPS; LEAVE;
+ }
+
+ ldap_pvt_thread_mutex_unlock( &perl_interpreter_mutex );
+
+ send_ldap_result( op, rs );
+
+ return 0;
+}
diff --git a/servers/slapd/back-relay/Makefile.in b/servers/slapd/back-relay/Makefile.in
new file mode 100644
index 0000000..5f111bc
--- /dev/null
+++ b/servers/slapd/back-relay/Makefile.in
@@ -0,0 +1,41 @@
+# Makefile.in for back-relay
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SRCS = init.c op.c
+OBJS = init.lo op.lo
+
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-relay"
+BUILD_MOD = @BUILD_RELAY@
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = $(@BUILD_RELAY@_DEFS)
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS) $(REWRITE)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS) $(REWRITE)
+
+LIBBASE = back_relay
+
+XINCPATH = -I.. -I$(srcdir)/..
+XDEFS = $(MODULES_CPPFLAGS)
+
+all-local-lib: ../.backend
+
+../.backend: lib$(LIBBASE).a
+ @touch $@
+
diff --git a/servers/slapd/back-relay/README b/servers/slapd/back-relay/README
new file mode 100644
index 0000000..81f152c
--- /dev/null
+++ b/servers/slapd/back-relay/README
@@ -0,0 +1,83 @@
+Relay backend sets up a relay virtual database that allows
+to access other databases in the same instance of slapd
+through different naming contexts and remapping attribute
+values.
+
+The DN rewrite, filter rewrite and attributeType/objectClass
+mapping is done by means of the rewrite-remap overlay.
+
+The database containing the real naming context can be
+explicitly selected by means of the "relay" directive,
+which must contain the naming context of the target
+database. This also causes the rewrite-remap overlay
+to be automatically instantiated. If the optional keyword
+"massage" is present, the rewrite-remap overlay is
+automatically configured to map the virtual to the real
+naming context and vice-versa.
+
+Otherwise, the rewrite-remap overlay must be explicitly
+instantiated, by using the "overlay" directive, as
+illustrated below. This allows much more freedom in target
+database selection and DN rewriting.
+
+If the "relay" directive is not present, the backend is
+not bound to a single target database; on the contrary,
+the target database is selected on a per-operation basis.
+
+This allows, for instance, to relay one database for
+authentication and anotheir for search/modify, or allows
+to use one target for persons and another for groups
+and so on.
+
+To summarize: the "relay" directive:
+- explicitly bounds the database to a single database
+ holding the real naming context;
+- automatically instantiates the rewrite-remap overlay;
+- automatically configures the naming context massaging
+ if the optional "massage" keyword is added
+
+If the "relay" directive is not used, the rewrite-remap
+overlay must be explicitly instantiated and the massaging
+must be configured, either by using the "suffixmassage"
+directive, or by issuing more sophisticate rewrite
+instructions.
+
+AttributeType/objectClass mapping must be explicitly
+required.
+
+Note that the rewrite-remap overlay is not complete nor
+production- ready yet.
+Examples are given of all the suggested usages.
+
+# automatically massage from virtual to real naming context
+database relay
+suffix "dc=virtual,dc=naming,dc=context"
+relay "dc=real,dc=naming,dc=context" massage
+
+# explicitly massage (same as above)
+database relay
+suffix "dc=virtual,dc=naming,dc=context"
+relay "dc=real,dc=naming,dc=context"
+suffixmassage "dc=virtual,dc=naming,dc=context" \
+ "dc=real,dc=naming,dc=context"
+
+# explicitly massage (same as above, but dynamic backend resolution)
+database relay
+suffix "dc=virtual,dc=naming,dc=context"
+overlay rewrite-remap
+suffixmassage "dc=virtual,dc=naming,dc=context" \
+ "dc=real,dc=naming,dc=context"
+
+# old fashioned suffixalias, applied also to DN-valued attributes
+# from virtual to real naming context, but not the reverse...
+database relay
+suffix "dc=virtual,dc=naming,dc=context"
+relay "dc=real,dc=naming,dc=context"
+rewriteContext default
+rewriteRule "(.*)dc=virtual,dc=naming,dc=context$" \
+ "$1dc=real,dc=naming,dc=context"
+rewriteContext searchFilter
+rewriteContext searchResult
+rewriteContext searchResultAttrDN
+rewriteContext matchedDN
+
diff --git a/servers/slapd/back-relay/back-relay.h b/servers/slapd/back-relay/back-relay.h
new file mode 100644
index 0000000..e5c0e17
--- /dev/null
+++ b/servers/slapd/back-relay/back-relay.h
@@ -0,0 +1,49 @@
+/* back-relay.h - relay backend header file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2004 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#ifndef SLAPD_RELAY_H
+#define SLAPD_RELAY_H
+
+#include "proto-back-relay.h"
+
+LDAP_BEGIN_DECL
+
+typedef enum relay_operation_e {
+ relay_op_entry_get = op_last,
+ relay_op_entry_release,
+ relay_op_has_subordinates,
+ relay_op_last
+} relay_operation_t;
+
+typedef struct relay_back_info {
+ BackendDB *ri_bd;
+ struct berval ri_realsuffix;
+ int ri_massage;
+} relay_back_info;
+
+/* Pad relay_back_info if needed to create valid OpExtra key addresses */
+#define RELAY_INFO_SIZE \
+ (sizeof(relay_back_info) > (size_t) relay_op_last ? \
+ sizeof(relay_back_info) : (size_t) relay_op_last )
+
+LDAP_END_DECL
+
+#endif /* SLAPD_RELAY_H */
diff --git a/servers/slapd/back-relay/init.c b/servers/slapd/back-relay/init.c
new file mode 100644
index 0000000..efad958
--- /dev/null
+++ b/servers/slapd/back-relay/init.c
@@ -0,0 +1,255 @@
+/* init.c - initialize relay backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2004 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "back-relay.h"
+
+static ConfigDriver relay_back_cf;
+
+static ConfigTable relaycfg[] = {
+ { "relay", "relay", 2, 2, 0,
+ ARG_MAGIC|ARG_DN|ARG_QUOTE,
+ relay_back_cf, "( OLcfgDbAt:5.1 "
+ "NAME 'olcRelay' "
+ "DESC 'Relay DN' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+ { NULL }
+};
+
+static ConfigOCs relayocs[] = {
+ { "( OLcfgDbOc:5.1 "
+ "NAME 'olcRelayConfig' "
+ "DESC 'Relay backend configuration' "
+ "SUP olcDatabaseConfig "
+ "MAY ( olcRelay "
+ ") )",
+ Cft_Database, relaycfg},
+ { NULL, 0, NULL }
+};
+
+static int
+relay_back_cf( ConfigArgs *c )
+{
+ relay_back_info *ri = ( relay_back_info * )c->be->be_private;
+ int rc = 0;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ if ( ri != NULL && !BER_BVISNULL( &ri->ri_realsuffix ) ) {
+ value_add_one( &c->rvalue_vals, &ri->ri_realsuffix );
+ return 0;
+ }
+ return 1;
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ if ( !BER_BVISNULL( &ri->ri_realsuffix ) ) {
+ ch_free( ri->ri_realsuffix.bv_val );
+ BER_BVZERO( &ri->ri_realsuffix );
+ ri->ri_bd = NULL;
+ return 0;
+ }
+ return 1;
+
+ } else {
+ BackendDB *bd;
+
+ assert( ri != NULL );
+ assert( BER_BVISNULL( &ri->ri_realsuffix ) );
+
+ if ( c->be->be_nsuffix == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "\"relay\" directive "
+ "must appear after \"suffix\"" );
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "%s: %s.\n", c->log, c->cr_msg );
+ rc = 1;
+ goto relay_done;
+ }
+
+ if ( !BER_BVISNULL( &c->be->be_nsuffix[ 1 ] ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "relaying of multiple suffix "
+ "database not supported" );
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "%s: %s.\n", c->log, c->cr_msg );
+ rc = 1;
+ goto relay_done;
+ }
+
+ bd = select_backend( &c->value_ndn, 1 );
+ if ( bd == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "cannot find database "
+ "of relay dn \"%s\" "
+ "in \"olcRelay <dn>\"\n",
+ c->value_dn.bv_val );
+ Log( LDAP_DEBUG_CONFIG, LDAP_LEVEL_ERR,
+ "%s: %s.\n", c->log, c->cr_msg );
+
+ } else if ( bd->be_private == c->be->be_private ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "relay dn \"%s\" would call self "
+ "in \"relay <dn>\" line\n",
+ c->value_dn.bv_val );
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "%s: %s.\n", c->log, c->cr_msg );
+ rc = 1;
+ goto relay_done;
+ }
+
+ ri->ri_realsuffix = c->value_ndn;
+ BER_BVZERO( &c->value_ndn );
+
+relay_done:;
+ ch_free( c->value_dn.bv_val );
+ ch_free( c->value_ndn.bv_val );
+ }
+
+ return rc;
+}
+
+int
+relay_back_initialize( BackendInfo *bi )
+{
+ bi->bi_init = 0;
+ bi->bi_open = 0;
+ bi->bi_config = 0;
+ bi->bi_close = 0;
+ bi->bi_destroy = 0;
+
+ bi->bi_db_init = relay_back_db_init;
+ bi->bi_db_config = config_generic_wrapper;
+ bi->bi_db_open = relay_back_db_open;
+#if 0
+ bi->bi_db_close = relay_back_db_close;
+#endif
+ bi->bi_db_destroy = relay_back_db_destroy;
+
+ bi->bi_op_bind = relay_back_op_bind;
+ bi->bi_op_search = relay_back_op_search;
+ bi->bi_op_compare = relay_back_op_compare;
+ bi->bi_op_modify = relay_back_op_modify;
+ bi->bi_op_modrdn = relay_back_op_modrdn;
+ bi->bi_op_add = relay_back_op_add;
+ bi->bi_op_delete = relay_back_op_delete;
+ bi->bi_extended = relay_back_op_extended;
+ bi->bi_entry_release_rw = relay_back_entry_release_rw;
+ bi->bi_entry_get_rw = relay_back_entry_get_rw;
+ bi->bi_operational = relay_back_operational;
+ bi->bi_has_subordinates = relay_back_has_subordinates;
+
+ bi->bi_cf_ocs = relayocs;
+
+ return config_register_schema( relaycfg, relayocs );
+}
+
+int
+relay_back_db_init( Backend *be, ConfigReply *cr)
+{
+ relay_back_info *ri;
+
+ be->be_private = NULL;
+
+ ri = (relay_back_info *) ch_calloc( 1, RELAY_INFO_SIZE );
+ if ( ri == NULL ) {
+ return -1;
+ }
+
+ ri->ri_bd = NULL;
+ BER_BVZERO( &ri->ri_realsuffix );
+ ri->ri_massage = 0;
+
+ be->be_cf_ocs = be->bd_info->bi_cf_ocs;
+
+ be->be_private = (void *)ri;
+
+ return 0;
+}
+
+int
+relay_back_db_open( Backend *be, ConfigReply *cr )
+{
+ relay_back_info *ri = (relay_back_info *)be->be_private;
+
+ assert( ri != NULL );
+
+ if ( !BER_BVISNULL( &ri->ri_realsuffix ) ) {
+ ri->ri_bd = select_backend( &ri->ri_realsuffix, 1 );
+
+ /* must be there: it was during config! */
+ if ( ri->ri_bd == NULL ) {
+ snprintf( cr->msg, sizeof( cr->msg),
+ "cannot find database "
+ "of relay dn \"%s\" "
+ "in \"olcRelay <dn>\"\n",
+ ri->ri_realsuffix.bv_val );
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "relay_back_db_open: %s.\n", cr->msg );
+
+ return 1;
+ }
+
+ /* inherit controls */
+ AC_MEMCPY( be->bd_self->be_ctrls, ri->ri_bd->be_ctrls, sizeof( be->be_ctrls ) );
+
+ } else {
+ /* inherit all? */
+ AC_MEMCPY( be->bd_self->be_ctrls, frontendDB->be_ctrls, sizeof( be->be_ctrls ) );
+ }
+
+ return 0;
+}
+
+int
+relay_back_db_close( Backend *be, ConfigReply *cr )
+{
+ return 0;
+}
+
+int
+relay_back_db_destroy( Backend *be, ConfigReply *cr)
+{
+ relay_back_info *ri = (relay_back_info *)be->be_private;
+
+ if ( ri ) {
+ if ( !BER_BVISNULL( &ri->ri_realsuffix ) ) {
+ ch_free( ri->ri_realsuffix.bv_val );
+ }
+ ch_free( ri );
+ }
+
+ return 0;
+}
+
+#if SLAPD_RELAY == SLAPD_MOD_DYNAMIC
+
+/* conditionally define the init_module() function */
+SLAP_BACKEND_INIT_MODULE( relay )
+
+#endif /* SLAPD_RELAY == SLAPD_MOD_DYNAMIC */
diff --git a/servers/slapd/back-relay/op.c b/servers/slapd/back-relay/op.c
new file mode 100644
index 0000000..40e9e88
--- /dev/null
+++ b/servers/slapd/back-relay/op.c
@@ -0,0 +1,333 @@
+/* op.c - relay backend operations */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2004 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "slap.h"
+#include "back-relay.h"
+
+/* Results when no real database (.rf_bd) or operation handler (.rf_op) */
+static const struct relay_fail_modes_s {
+ slap_mask_t rf_bd, rf_op;
+#define RB_ERR_MASK 0x0000FFFFU /* bitmask for default return value */
+#define RB_BDERR 0x80000000U /* use .rf_bd's default return value */
+#define RB_OPERR 0x40000000U /* set rs->sr_err = .rf_op return value */
+#define RB_REF 0x20000000U /* use default_referral if available */
+#define RB_SEND 0x10000000U /* send result; RB_??ERR is also set */
+#define RB_SENDREF 0/*unused*/ /* like RB_SEND when referral found */
+#define RB_NO_BIND (RB_OPERR | LDAP_INVALID_CREDENTIALS)
+#define RB_NOT_SUPP (RB_OPERR | LDAP_UNWILLING_TO_PERFORM)
+#define RB_NO_OBJ (RB_REF | LDAP_NO_SUCH_OBJECT)
+#define RB_CHK_REF (RB_REF | RB_SENDREF | LDAP_SUCCESS)
+} relay_fail_modes[relay_op_last] = {
+ /* .rf_bd is unused when zero, otherwise both fields have RB_BDERR */
+# define RB_OP(b, o) { (b) | RB_BD2ERR(b), (o) | RB_BD2ERR(b) }
+# define RB_BD2ERR(b) ((b) ? RB_BDERR : 0)
+ /* indexed by slap_operation_t: */
+ RB_OP(RB_NO_BIND|RB_SEND, RB_NO_BIND |RB_SEND), /* Bind */
+ RB_OP(0, LDAP_SUCCESS), /* Unbind: unused */
+ RB_OP(RB_NO_OBJ |RB_SEND, RB_NOT_SUPP |RB_SEND), /* Search */
+ RB_OP(RB_NO_OBJ |RB_SEND, SLAP_CB_CONTINUE), /* Compare */
+ RB_OP(RB_NO_OBJ |RB_SEND, RB_NOT_SUPP |RB_SEND), /* Modify */
+ RB_OP(RB_NO_OBJ |RB_SEND, RB_NOT_SUPP |RB_SEND), /* Modrdn */
+ RB_OP(RB_NO_OBJ |RB_SEND, RB_NOT_SUPP |RB_SEND), /* Add */
+ RB_OP(RB_NO_OBJ |RB_SEND, RB_NOT_SUPP |RB_SEND), /* Delete */
+ RB_OP(0, LDAP_SUCCESS), /* Abandon:unused */
+ RB_OP(RB_NO_OBJ, RB_NOT_SUPP), /* Extended */
+ RB_OP(0, SLAP_CB_CONTINUE), /* Cancel: unused */
+ RB_OP(0, LDAP_SUCCESS), /* operational */
+ RB_OP(RB_CHK_REF, LDAP_SUCCESS), /* chk_referrals:unused*/
+ RB_OP(0, SLAP_CB_CONTINUE),/* chk_controls:unused */
+ /* additional relay_operation_t indexes from back-relay.h: */
+ RB_OP(0, 0/*unused*/), /* entry_get = op_last */
+ RB_OP(0, 0/*unused*/), /* entry_release */
+ RB_OP(0, 0/*unused*/), /* has_subordinates */
+};
+
+/*
+ * Callbacks: Caller changed op->o_bd from Relay to underlying
+ * BackendDB. sc_response sets it to Relay BackendDB, sc_cleanup puts
+ * back underlying BackendDB. Caller will restore Relay BackendDB.
+ */
+
+typedef struct relay_callback {
+ slap_callback rcb_sc;
+ BackendDB *rcb_bd;
+} relay_callback;
+
+static int
+relay_back_cleanup_cb( Operation *op, SlapReply *rs )
+{
+ op->o_bd = ((relay_callback *) op->o_callback)->rcb_bd;
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+relay_back_response_cb( Operation *op, SlapReply *rs )
+{
+ relay_callback *rcb = (relay_callback *) op->o_callback;
+
+ rcb->rcb_sc.sc_cleanup = relay_back_cleanup_cb;
+ rcb->rcb_bd = op->o_bd;
+ op->o_bd = op->o_callback->sc_private;
+ return SLAP_CB_CONTINUE;
+}
+
+#define relay_back_add_cb( rcb, op ) { \
+ (rcb)->rcb_sc.sc_next = (op)->o_callback; \
+ (rcb)->rcb_sc.sc_response = relay_back_response_cb; \
+ (rcb)->rcb_sc.sc_cleanup = 0; \
+ (rcb)->rcb_sc.sc_writewait = 0; \
+ (rcb)->rcb_sc.sc_private = (op)->o_bd; \
+ (op)->o_callback = (slap_callback *) (rcb); \
+}
+
+#define relay_back_remove_cb( rcb, op ) { \
+ slap_callback **sc = &(op)->o_callback; \
+ for ( ;; sc = &(*sc)->sc_next ) \
+ if ( *sc == (slap_callback *) (rcb) ) { \
+ *sc = (*sc)->sc_next; break; \
+ } else if ( *sc == NULL ) break; \
+}
+
+/*
+ * Select the backend database with the operation's DN. On failure,
+ * set/send results depending on operation type <which>'s fail_modes.
+ */
+static BackendDB *
+relay_back_select_backend( Operation *op, SlapReply *rs, int which )
+{
+ OpExtra *oex;
+ char *key = (char *) op->o_bd->be_private;
+ BackendDB *bd = ((relay_back_info *) key)->ri_bd;
+ slap_mask_t fail_mode = relay_fail_modes[which].rf_bd;
+ int useDN = 0, rc = ( fail_mode & RB_ERR_MASK );
+
+ if ( bd == NULL && !BER_BVISNULL( &op->o_req_ndn ) ) {
+ useDN = 1;
+ bd = select_backend( &op->o_req_ndn, 1 );
+ }
+
+ if ( bd != NULL ) {
+ key += which; /* <relay, op type> key from RELAY_WRAP_OP() */
+ LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
+ if ( oex->oe_key == key )
+ break;
+ }
+ if ( oex == NULL ) {
+ return bd;
+ }
+
+ Debug( LDAP_DEBUG_ANY,
+ "%s: back-relay for DN=\"%s\" would call self.\n",
+ op->o_log_prefix, op->o_req_dn.bv_val );
+
+ } else if ( useDN && ( fail_mode & RB_REF ) && default_referral ) {
+ rc = LDAP_REFERRAL;
+
+ /* if we set sr_err to LDAP_REFERRAL, we must provide one */
+ rs->sr_ref = referral_rewrite(
+ default_referral, NULL, &op->o_req_dn,
+ op->o_tag == LDAP_REQ_SEARCH ?
+ op->ors_scope : LDAP_SCOPE_DEFAULT );
+ if ( rs->sr_ref != NULL ) {
+ rs->sr_flags |= REP_REF_MUSTBEFREED;
+ } else {
+ rs->sr_ref = default_referral;
+ }
+
+ if ( fail_mode & RB_SENDREF )
+ fail_mode = (RB_BDERR | RB_SEND);
+ }
+
+ if ( fail_mode & RB_BDERR ) {
+ rs->sr_err = rc;
+ if ( fail_mode & RB_SEND ) {
+ send_ldap_result( op, rs );
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Forward <act> on <op> to database <bd>, with <relay, op type>-specific
+ * key in op->o_extra so relay_back_select_backend() can catch recursion.
+ */
+#define RELAY_WRAP_OP( op, bd, which, act ) { \
+ OpExtraDB wrap_oex; \
+ BackendDB *const wrap_bd = (op)->o_bd; \
+ wrap_oex.oe_db = wrap_bd; \
+ wrap_oex.oe.oe_key = (char *) wrap_bd->be_private + (which); \
+ LDAP_SLIST_INSERT_HEAD( &(op)->o_extra, &wrap_oex.oe, oe_next ); \
+ (op)->o_bd = (bd); \
+ act; \
+ (op)->o_bd = wrap_bd; \
+ LDAP_SLIST_REMOVE( &(op)->o_extra, &wrap_oex.oe, OpExtra, oe_next ); \
+}
+
+/*
+ * Forward backend function #<which> on <op> to operation DN's database
+ * like RELAY_WRAP_OP, after setting up callbacks. If no database or no
+ * backend function, set/send results depending on <which>'s fail_modes.
+ */
+static int
+relay_back_op( Operation *op, SlapReply *rs, int which )
+{
+ BackendDB *bd;
+ BackendInfo *bi;
+ slap_mask_t fail_mode = relay_fail_modes[which].rf_op;
+ int rc = ( fail_mode & RB_ERR_MASK );
+
+ bd = relay_back_select_backend( op, rs, which );
+ if ( bd == NULL ) {
+ if ( fail_mode & RB_BDERR )
+ return rs->sr_err; /* sr_err was set above */
+
+ } else if ( (&( bi = bd->bd_info )->bi_op_bind)[which] ) {
+ relay_callback rcb;
+
+ relay_back_add_cb( &rcb, op );
+ RELAY_WRAP_OP( op, bd, which, {
+ rc = (&bi->bi_op_bind)[which]( op, rs );
+ });
+ relay_back_remove_cb( &rcb, op );
+ if ( which == op_bind && rc == LDAP_SUCCESS )
+ op->o_bd = bd;
+
+ } else if ( fail_mode & RB_OPERR ) {
+ rs->sr_err = rc;
+ if ( rc == LDAP_UNWILLING_TO_PERFORM ) {
+ rs->sr_text = "operation not supported within naming context";
+ }
+
+ if ( fail_mode & RB_SEND ) {
+ send_ldap_result( op, rs );
+ }
+ }
+
+ return rc;
+}
+
+
+int
+relay_back_op_bind( Operation *op, SlapReply *rs )
+{
+ /* allow rootdn as a means to auth without the need to actually
+ * contact the proxied DSA */
+ switch ( be_rootdn_bind( op, rs ) ) {
+ case SLAP_CB_CONTINUE:
+ break;
+
+ default:
+ return rs->sr_err;
+ }
+
+ return relay_back_op( op, rs, op_bind );
+}
+
+#define RELAY_DEFOP(func, which) \
+ int func( Operation *op, SlapReply *rs ) \
+ { return relay_back_op( op, rs, which ); }
+
+RELAY_DEFOP( relay_back_op_search, op_search )
+RELAY_DEFOP( relay_back_op_compare, op_compare )
+RELAY_DEFOP( relay_back_op_modify, op_modify )
+RELAY_DEFOP( relay_back_op_modrdn, op_modrdn )
+RELAY_DEFOP( relay_back_op_add, op_add )
+RELAY_DEFOP( relay_back_op_delete, op_delete )
+RELAY_DEFOP( relay_back_op_extended, op_extended )
+RELAY_DEFOP( relay_back_operational, op_aux_operational )
+
+/* Abandon, Cancel, Unbind and some DN-less calls like be_connection_init
+ * need no extra handling: slapd already calls them for all databases.
+ */
+
+
+int
+relay_back_entry_release_rw( Operation *op, Entry *e, int rw )
+{
+ BackendDB *bd;
+ int rc = LDAP_UNWILLING_TO_PERFORM;
+
+ bd = relay_back_select_backend( op, NULL, relay_op_entry_release );
+ if ( bd && bd->be_release ) {
+ RELAY_WRAP_OP( op, bd, relay_op_entry_release, {
+ rc = bd->be_release( op, e, rw );
+ });
+ } else if ( e->e_private == NULL ) {
+ entry_free( e );
+ rc = LDAP_SUCCESS;
+ }
+
+ return rc;
+}
+
+int
+relay_back_entry_get_rw( Operation *op, struct berval *ndn,
+ ObjectClass *oc, AttributeDescription *at, int rw, Entry **e )
+{
+ BackendDB *bd;
+ int rc = LDAP_NO_SUCH_OBJECT;
+
+ bd = relay_back_select_backend( op, NULL, relay_op_entry_get );
+ if ( bd && bd->be_fetch ) {
+ RELAY_WRAP_OP( op, bd, relay_op_entry_get, {
+ rc = bd->be_fetch( op, ndn, oc, at, rw, e );
+ });
+ }
+
+ return rc;
+}
+
+#if 0 /* Give the RB_SENDREF flag a nonzero value if implementing this */
+/*
+ * NOTE: even the existence of this function is questionable: we cannot
+ * pass the bi_chk_referrals() call thru the rwm overlay because there
+ * is no way to rewrite the req_dn back; but then relay_back_chk_referrals()
+ * is passing the target database a DN that likely does not belong to its
+ * naming context... mmmh.
+ */
+RELAY_DEFOP( relay_back_chk_referrals, op_aux_chk_referrals )
+#endif /*0*/
+
+int
+relay_back_has_subordinates( Operation *op, Entry *e, int *hasSubs )
+{
+ BackendDB *bd;
+ int rc = LDAP_OTHER;
+
+ bd = relay_back_select_backend( op, NULL, relay_op_has_subordinates );
+ if ( bd && bd->be_has_subordinates ) {
+ RELAY_WRAP_OP( op, bd, relay_op_has_subordinates, {
+ rc = bd->be_has_subordinates( op, e, hasSubs );
+ });
+ }
+
+ return rc;
+}
+
+
+/*
+ * FIXME: must implement tools as well
+ */
diff --git a/servers/slapd/back-relay/proto-back-relay.h b/servers/slapd/back-relay/proto-back-relay.h
new file mode 100644
index 0000000..854be4e
--- /dev/null
+++ b/servers/slapd/back-relay/proto-back-relay.h
@@ -0,0 +1,52 @@
+/* proto-back-relay.h - relay backend header file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2004 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#ifndef PROTO_BACK_RELAY
+#define PROTO_BACK_RELAY
+
+#include <ldap_cdefs.h>
+
+LDAP_BEGIN_DECL
+
+extern BI_init relay_back_initialize;
+
+extern BI_db_init relay_back_db_init;
+extern BI_db_open relay_back_db_open;
+extern BI_db_close relay_back_db_close;
+extern BI_db_destroy relay_back_db_destroy;
+
+extern BI_op_bind relay_back_op_bind;
+extern BI_op_search relay_back_op_search;
+extern BI_op_compare relay_back_op_compare;
+extern BI_op_modify relay_back_op_modify;
+extern BI_op_modrdn relay_back_op_modrdn;
+extern BI_op_add relay_back_op_add;
+extern BI_op_delete relay_back_op_delete;
+extern BI_op_extended relay_back_op_extended;
+extern BI_entry_release_rw relay_back_entry_release_rw;
+extern BI_entry_get_rw relay_back_entry_get_rw;
+extern BI_operational relay_back_operational;
+extern BI_has_subordinates relay_back_has_subordinates;
+
+LDAP_END_DECL
+
+#endif /* PROTO_BACK_RELAY */
+
diff --git a/servers/slapd/back-sock/Makefile.in b/servers/slapd/back-sock/Makefile.in
new file mode 100644
index 0000000..1db07bf
--- /dev/null
+++ b/servers/slapd/back-sock/Makefile.in
@@ -0,0 +1,47 @@
+# Makefile.in for back-sock
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2007-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This work was initially developed by Brian Candler for inclusion
+## in OpenLDAP Software.
+
+SRCS = init.c config.c opensock.c search.c bind.c unbind.c add.c \
+ delete.c modify.c modrdn.c compare.c result.c extended.c
+OBJS = init.lo config.lo opensock.lo search.lo bind.lo unbind.lo add.lo \
+ delete.lo modify.lo modrdn.lo compare.lo result.lo extended.lo
+
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-sock"
+BUILD_MOD = @BUILD_SOCK@
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = $(@BUILD_SOCK@_DEFS)
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+
+LIBBASE = back_sock
+
+XINCPATH = -I.. -I$(srcdir)/..
+XDEFS = $(MODULES_CPPFLAGS)
+
+all-local-lib: ../.backend
+
+../.backend: lib$(LIBBASE).a
+ @touch $@
+
diff --git a/servers/slapd/back-sock/add.c b/servers/slapd/back-sock/add.c
new file mode 100644
index 0000000..0e96d95
--- /dev/null
+++ b/servers/slapd/back-sock/add.c
@@ -0,0 +1,69 @@
+/* add.c - sock backend add function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Brian Candler for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-sock.h"
+
+int
+sock_back_add(
+ Operation *op,
+ SlapReply *rs )
+{
+ struct sockinfo *si = (struct sockinfo *) op->o_bd->be_private;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+ FILE *fp;
+ int len;
+
+ if ( ! access_allowed( op, op->oq_add.rs_e,
+ entry, NULL, ACL_WADD, NULL ) )
+ {
+ send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS, NULL );
+ return -1;
+ }
+
+ if ( (fp = opensock( si->si_sockpath )) == NULL ) {
+ send_ldap_error( op, rs, LDAP_OTHER,
+ "could not open socket" );
+ return( -1 );
+ }
+
+ /* write out the request to the add process */
+ fprintf( fp, "ADD\n" );
+ fprintf( fp, "msgid: %ld\n", (long) op->o_msgid );
+ sock_print_conn( fp, op->o_conn, si );
+ sock_print_suffixes( fp, op->o_bd );
+ ldap_pvt_thread_mutex_lock( &entry2str_mutex );
+ fprintf( fp, "%s", entry2str( op->oq_add.rs_e, &len ) );
+ ldap_pvt_thread_mutex_unlock( &entry2str_mutex );
+ fprintf (fp, "\n" );
+
+ /* read in the result and send it along */
+ sock_read_and_send_results( op, rs, fp );
+
+ fclose( fp );
+ return( 0 );
+}
diff --git a/servers/slapd/back-sock/back-sock.h b/servers/slapd/back-sock/back-sock.h
new file mode 100644
index 0000000..55dbe75
--- /dev/null
+++ b/servers/slapd/back-sock/back-sock.h
@@ -0,0 +1,61 @@
+/* sock.h - socket backend header file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Brian Candler for inclusion
+ * in OpenLDAP Software.
+ */
+
+#ifndef SLAPD_SOCK_H
+#define SLAPD_SOCK_H
+
+#include "proto-sock.h"
+
+LDAP_BEGIN_DECL
+
+struct sockinfo {
+ const char *si_sockpath;
+ slap_mask_t si_extensions;
+ slap_mask_t si_ops; /* overlay: operations to act on */
+ slap_mask_t si_resps; /* overlay: responses to forward */
+ regex_t si_dnpat; /* overlay: DN pattern to match */
+ struct berval si_dnpatstr;
+};
+
+#define SOCK_EXT_BINDDN 1
+#define SOCK_EXT_PEERNAME 2
+#define SOCK_EXT_SSF 4
+#define SOCK_EXT_CONNID 8
+
+extern FILE *opensock LDAP_P((
+ const char *sockpath));
+
+extern void sock_print_suffixes LDAP_P((
+ FILE *fp,
+ BackendDB *bd));
+
+extern void sock_print_conn LDAP_P((
+ FILE *fp,
+ Connection *conn,
+ struct sockinfo *si));
+
+extern int sock_read_and_send_results LDAP_P((
+ Operation *op,
+ SlapReply *rs,
+ FILE *fp));
+
+LDAP_END_DECL
+
+#endif
diff --git a/servers/slapd/back-sock/bind.c b/servers/slapd/back-sock/bind.c
new file mode 100644
index 0000000..8e8ec97
--- /dev/null
+++ b/servers/slapd/back-sock/bind.c
@@ -0,0 +1,80 @@
+/* bind.c - sock backend bind function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Brian Candler for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-sock.h"
+
+int
+sock_back_bind(
+ Operation *op,
+ SlapReply *rs )
+{
+ struct sockinfo *si = (struct sockinfo *) op->o_bd->be_private;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+ Entry e;
+ FILE *fp;
+ int rc;
+
+ e.e_id = NOID;
+ e.e_name = op->o_req_dn;
+ e.e_nname = op->o_req_ndn;
+ e.e_attrs = NULL;
+ e.e_ocflags = 0;
+ e.e_bv.bv_len = 0;
+ e.e_bv.bv_val = NULL;
+ e.e_private = NULL;
+
+ if ( ! access_allowed( op, &e,
+ entry, NULL, ACL_AUTH, NULL ) )
+ {
+ send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS, NULL );
+ return -1;
+ }
+
+ if ( (fp = opensock( si->si_sockpath )) == NULL ) {
+ send_ldap_error( op, rs, LDAP_OTHER,
+ "could not open socket" );
+ return( -1 );
+ }
+
+ /* write out the request to the bind process */
+ fprintf( fp, "BIND\n" );
+ fprintf( fp, "msgid: %ld\n", (long) op->o_msgid );
+ sock_print_conn( fp, op->o_conn, si );
+ sock_print_suffixes( fp, op->o_bd );
+ fprintf( fp, "dn: %s\n", op->o_req_dn.bv_val );
+ fprintf( fp, "method: %d\n", op->oq_bind.rb_method );
+ fprintf( fp, "credlen: %lu\n", op->oq_bind.rb_cred.bv_len );
+ fprintf( fp, "cred: %s\n", op->oq_bind.rb_cred.bv_val ); /* XXX */
+ fprintf( fp, "\n" );
+
+ /* read in the results and send them along */
+ rc = sock_read_and_send_results( op, rs, fp );
+ fclose( fp );
+
+ return( rc );
+}
diff --git a/servers/slapd/back-sock/compare.c b/servers/slapd/back-sock/compare.c
new file mode 100644
index 0000000..735e688
--- /dev/null
+++ b/servers/slapd/back-sock/compare.c
@@ -0,0 +1,88 @@
+/* compare.c - sock backend compare function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Brian Candler for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-sock.h"
+#include "ldif.h"
+
+int
+sock_back_compare(
+ Operation *op,
+ SlapReply *rs )
+{
+ struct sockinfo *si = (struct sockinfo *) op->o_bd->be_private;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+ Entry e;
+ FILE *fp;
+ char *text;
+
+ e.e_id = NOID;
+ e.e_name = op->o_req_dn;
+ e.e_nname = op->o_req_ndn;
+ e.e_attrs = NULL;
+ e.e_ocflags = 0;
+ e.e_bv.bv_len = 0;
+ e.e_bv.bv_val = NULL;
+ e.e_private = NULL;
+
+ if ( ! access_allowed( op, &e,
+ entry, NULL, ACL_COMPARE, NULL ) )
+ {
+ send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS, NULL );
+ return -1;
+ }
+
+ if ( (fp = opensock( si->si_sockpath )) == NULL ) {
+ send_ldap_error( op, rs, LDAP_OTHER,
+ "could not open socket" );
+ return( -1 );
+ }
+
+ /* write out the request to the compare process */
+ fprintf( fp, "COMPARE\n" );
+ fprintf( fp, "msgid: %ld\n", (long) op->o_msgid );
+ sock_print_conn( fp, op->o_conn, si );
+ sock_print_suffixes( fp, op->o_bd );
+ fprintf( fp, "dn: %s\n", op->o_req_dn.bv_val );
+ /* could be binary */
+ text = ldif_put_wrap( LDIF_PUT_VALUE,
+ op->orc_ava->aa_desc->ad_cname.bv_val,
+ op->orc_ava->aa_value.bv_val,
+ op->orc_ava->aa_value.bv_len, LDIF_LINE_WIDTH_MAX );
+ if ( text ) {
+ fprintf( fp, "%s\n", text );
+ ber_memfree( text );
+ } else {
+ fprintf( fp, "\n\n" );
+ }
+
+ /* read in the result and send it along */
+ sock_read_and_send_results( op, rs, fp );
+
+ fclose( fp );
+ return( 0 );
+}
diff --git a/servers/slapd/back-sock/config.c b/servers/slapd/back-sock/config.c
new file mode 100644
index 0000000..7cd473b
--- /dev/null
+++ b/servers/slapd/back-sock/config.c
@@ -0,0 +1,420 @@
+/* config.c - sock backend configuration file routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Brian Candler for inclusion
+ * in OpenLDAP Software. Dynamic config support by Howard Chu.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "back-sock.h"
+
+static ConfigDriver bs_cf_gen;
+static int sock_over_setup();
+static slap_response sock_over_response;
+
+enum {
+ BS_EXT = 1,
+ BS_OPS,
+ BS_RESP,
+ BS_DNPAT
+};
+
+/* The number of overlay-only config attrs */
+#define NUM_OV_ATTRS 3
+
+static ConfigTable bscfg[] = {
+ { "sockops", "ops", 2, 0, 0, ARG_MAGIC|BS_OPS,
+ bs_cf_gen, "( OLcfgDbAt:7.3 NAME 'olcOvSocketOps' "
+ "DESC 'Operation types to forward' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "sockresps", "resps", 2, 0, 0, ARG_MAGIC|BS_RESP,
+ bs_cf_gen, "( OLcfgDbAt:7.4 NAME 'olcOvSocketResps' "
+ "DESC 'Response types to forward' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "sockdnpat", "regexp", 2, 2, 0, ARG_MAGIC|BS_DNPAT,
+ bs_cf_gen, "( OLcfgDbAt:7.5 NAME 'olcOvSocketDNpat' "
+ "DESC 'DN pattern to match' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+
+ { "socketpath", "pathname", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+ (void *)offsetof(struct sockinfo, si_sockpath),
+ "( OLcfgDbAt:7.1 NAME 'olcDbSocketPath' "
+ "DESC 'Pathname for Unix domain socket' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "extensions", "ext", 2, 0, 0, ARG_MAGIC|BS_EXT,
+ bs_cf_gen, "( OLcfgDbAt:7.2 NAME 'olcDbSocketExtensions' "
+ "DESC 'binddn, peername, or ssf' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { NULL, NULL }
+};
+
+static ConfigOCs bsocs[] = {
+ { "( OLcfgDbOc:7.1 "
+ "NAME 'olcDbSocketConfig' "
+ "DESC 'Socket backend configuration' "
+ "SUP olcDatabaseConfig "
+ "MUST olcDbSocketPath "
+ "MAY olcDbSocketExtensions )",
+ Cft_Database, bscfg+NUM_OV_ATTRS },
+ { NULL, 0, NULL }
+};
+
+static ConfigOCs osocs[] = {
+ { "( OLcfgDbOc:7.2 "
+ "NAME 'olcOvSocketConfig' "
+ "DESC 'Socket overlay configuration' "
+ "SUP olcOverlayConfig "
+ "MUST olcDbSocketPath "
+ "MAY ( olcDbSocketExtensions $ "
+ " olcOvSocketOps $ olcOvSocketResps $ "
+ " olcOvSocketDNpat ) )",
+ Cft_Overlay, bscfg },
+ { NULL, 0, NULL }
+};
+
+#define SOCK_OP_BIND 0x001
+#define SOCK_OP_UNBIND 0x002
+#define SOCK_OP_SEARCH 0x004
+#define SOCK_OP_COMPARE 0x008
+#define SOCK_OP_MODIFY 0x010
+#define SOCK_OP_MODRDN 0x020
+#define SOCK_OP_ADD 0x040
+#define SOCK_OP_DELETE 0x080
+#define SOCK_OP_EXTENDED 0x100
+
+#define SOCK_REP_RESULT 0x001
+#define SOCK_REP_SEARCH 0x002
+
+static slap_verbmasks bs_exts[] = {
+ { BER_BVC("binddn"), SOCK_EXT_BINDDN },
+ { BER_BVC("peername"), SOCK_EXT_PEERNAME },
+ { BER_BVC("ssf"), SOCK_EXT_SSF },
+ { BER_BVC("connid"), SOCK_EXT_CONNID },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks ov_ops[] = {
+ { BER_BVC("bind"), SOCK_OP_BIND },
+ { BER_BVC("unbind"), SOCK_OP_UNBIND },
+ { BER_BVC("search"), SOCK_OP_SEARCH },
+ { BER_BVC("compare"), SOCK_OP_COMPARE },
+ { BER_BVC("modify"), SOCK_OP_MODIFY },
+ { BER_BVC("modrdn"), SOCK_OP_MODRDN },
+ { BER_BVC("add"), SOCK_OP_ADD },
+ { BER_BVC("delete"), SOCK_OP_DELETE },
+ { BER_BVC("extended"), SOCK_OP_EXTENDED },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks ov_resps[] = {
+ { BER_BVC("result"), SOCK_REP_RESULT },
+ { BER_BVC("search"), SOCK_REP_SEARCH },
+ { BER_BVNULL, 0 }
+};
+
+static int
+bs_cf_gen( ConfigArgs *c )
+{
+ struct sockinfo *si;
+ int rc;
+
+ if ( c->be && c->table == Cft_Database )
+ si = c->be->be_private;
+ else if ( c->bi )
+ si = c->bi->bi_private;
+ else
+ return ARG_BAD_CONF;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ switch( c->type ) {
+ case BS_EXT:
+ return mask_to_verbs( bs_exts, si->si_extensions, &c->rvalue_vals );
+ case BS_OPS:
+ return mask_to_verbs( ov_ops, si->si_ops, &c->rvalue_vals );
+ case BS_RESP:
+ return mask_to_verbs( ov_resps, si->si_resps, &c->rvalue_vals );
+ case BS_DNPAT:
+ value_add_one( &c->rvalue_vals, &si->si_dnpatstr );
+ return 0;
+ }
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ switch( c->type ) {
+ case BS_EXT:
+ if ( c->valx < 0 ) {
+ si->si_extensions = 0;
+ rc = 0;
+ } else {
+ slap_mask_t dels = 0;
+ rc = verbs_to_mask( c->argc, c->argv, bs_exts, &dels );
+ if ( rc == 0 )
+ si->si_extensions ^= dels;
+ }
+ return rc;
+ case BS_OPS:
+ if ( c->valx < 0 ) {
+ si->si_ops = 0;
+ rc = 0;
+ } else {
+ slap_mask_t dels = 0;
+ rc = verbs_to_mask( c->argc, c->argv, ov_ops, &dels );
+ if ( rc == 0 )
+ si->si_ops ^= dels;
+ }
+ return rc;
+ case BS_RESP:
+ if ( c->valx < 0 ) {
+ si->si_resps = 0;
+ rc = 0;
+ } else {
+ slap_mask_t dels = 0;
+ rc = verbs_to_mask( c->argc, c->argv, ov_resps, &dels );
+ if ( rc == 0 )
+ si->si_resps ^= dels;
+ }
+ return rc;
+ case BS_DNPAT:
+ regfree( &si->si_dnpat );
+ ch_free( si->si_dnpatstr.bv_val );
+ BER_BVZERO( &si->si_dnpatstr );
+ return 0;
+ }
+
+ } else {
+ switch( c->type ) {
+ case BS_EXT:
+ return verbs_to_mask( c->argc, c->argv, bs_exts, &si->si_extensions );
+ case BS_OPS:
+ return verbs_to_mask( c->argc, c->argv, ov_ops, &si->si_ops );
+ case BS_RESP:
+ return verbs_to_mask( c->argc, c->argv, ov_resps, &si->si_resps );
+ case BS_DNPAT:
+ if ( !regcomp( &si->si_dnpat, c->argv[1], REG_EXTENDED|REG_ICASE|REG_NOSUB )) {
+ ber_str2bv( c->argv[1], 0, 1, &si->si_dnpatstr );
+ return 0;
+ } else {
+ return 1;
+ }
+ }
+ }
+ return 1;
+}
+
+int
+sock_back_init_cf( BackendInfo *bi )
+{
+ int rc;
+ bi->bi_cf_ocs = bsocs;
+
+ rc = config_register_schema( bscfg, bsocs );
+ if ( !rc )
+ rc = sock_over_setup();
+ return rc;
+}
+
+/* sock overlay wrapper */
+static slap_overinst sockover;
+
+static int sock_over_db_init( Backend *be, struct config_reply_s *cr );
+static int sock_over_db_destroy( Backend *be, struct config_reply_s *cr );
+
+static BI_op_bind *sockfuncs[] = {
+ sock_back_bind,
+ sock_back_unbind,
+ sock_back_search,
+ sock_back_compare,
+ sock_back_modify,
+ sock_back_modrdn,
+ sock_back_add,
+ sock_back_delete,
+ 0, /* abandon not supported */
+ sock_back_extended
+};
+
+static const int sockopflags[] = {
+ SOCK_OP_BIND,
+ SOCK_OP_UNBIND,
+ SOCK_OP_SEARCH,
+ SOCK_OP_COMPARE,
+ SOCK_OP_MODIFY,
+ SOCK_OP_MODRDN,
+ SOCK_OP_ADD,
+ SOCK_OP_DELETE,
+ 0, /* abandon not supported */
+ SOCK_OP_EXTENDED
+};
+
+static int sock_over_op(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ void *private = op->o_bd->be_private;
+ slap_callback *sc;
+ struct sockinfo *si;
+ slap_operation_t which;
+
+ switch (op->o_tag) {
+ case LDAP_REQ_BIND: which = op_bind; break;
+ case LDAP_REQ_UNBIND: which = op_unbind; break;
+ case LDAP_REQ_SEARCH: which = op_search; break;
+ case LDAP_REQ_COMPARE: which = op_compare; break;
+ case LDAP_REQ_MODIFY: which = op_modify; break;
+ case LDAP_REQ_MODRDN: which = op_modrdn; break;
+ case LDAP_REQ_ADD: which = op_add; break;
+ case LDAP_REQ_DELETE: which = op_delete; break;
+ case LDAP_REQ_EXTENDED: which = op_extended; break;
+ default:
+ return SLAP_CB_CONTINUE;
+ }
+ si = on->on_bi.bi_private;
+ if ( !(si->si_ops & sockopflags[which]))
+ return SLAP_CB_CONTINUE;
+
+ if ( !BER_BVISEMPTY( &si->si_dnpatstr ) &&
+ regexec( &si->si_dnpat, op->o_req_ndn.bv_val, 0, NULL, 0 ))
+ return SLAP_CB_CONTINUE;
+
+ op->o_bd->be_private = si;
+ sc = op->o_callback;
+ op->o_callback = NULL;
+ sockfuncs[which]( op, rs );
+ op->o_bd->be_private = private;
+ op->o_callback = sc;
+ return rs->sr_err;
+}
+
+static int
+sock_over_response( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ struct sockinfo *si = (struct sockinfo *)on->on_bi.bi_private;
+ FILE *fp;
+
+ if ( rs->sr_type == REP_RESULT ) {
+ if ( !( si->si_resps & SOCK_REP_RESULT ))
+ return SLAP_CB_CONTINUE;
+ } else if ( rs->sr_type == REP_SEARCH ) {
+ if ( !( si->si_resps & SOCK_REP_SEARCH ))
+ return SLAP_CB_CONTINUE;
+ } else
+ return SLAP_CB_CONTINUE;
+
+ if (( fp = opensock( si->si_sockpath )) == NULL )
+ return SLAP_CB_CONTINUE;
+
+ if ( rs->sr_type == REP_RESULT ) {
+ /* write out the result */
+ fprintf( fp, "RESULT\n" );
+ fprintf( fp, "msgid: %ld\n", (long) op->o_msgid );
+ sock_print_conn( fp, op->o_conn, si );
+ fprintf( fp, "code: %d\n", rs->sr_err );
+ if ( rs->sr_matched )
+ fprintf( fp, "matched: %s\n", rs->sr_matched );
+ if (rs->sr_text )
+ fprintf( fp, "info: %s\n", rs->sr_text );
+ } else {
+ /* write out the search entry */
+ int len;
+ fprintf( fp, "ENTRY\n" );
+ fprintf( fp, "msgid: %ld\n", (long) op->o_msgid );
+ sock_print_conn( fp, op->o_conn, si );
+ ldap_pvt_thread_mutex_lock( &entry2str_mutex );
+ fprintf( fp, "%s", entry2str( rs->sr_entry, &len ) );
+ ldap_pvt_thread_mutex_unlock( &entry2str_mutex );
+ }
+ fprintf( fp, "\n" );
+ fclose( fp );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+sock_over_setup()
+{
+ int rc;
+
+ sockover.on_bi.bi_type = "sock";
+ sockover.on_bi.bi_db_init = sock_over_db_init;
+ sockover.on_bi.bi_db_destroy = sock_over_db_destroy;
+
+ sockover.on_bi.bi_op_bind = sock_over_op;
+ sockover.on_bi.bi_op_unbind = sock_over_op;
+ sockover.on_bi.bi_op_search = sock_over_op;
+ sockover.on_bi.bi_op_compare = sock_over_op;
+ sockover.on_bi.bi_op_modify = sock_over_op;
+ sockover.on_bi.bi_op_modrdn = sock_over_op;
+ sockover.on_bi.bi_op_add = sock_over_op;
+ sockover.on_bi.bi_op_delete = sock_over_op;
+ sockover.on_bi.bi_extended = sock_over_op;
+ sockover.on_response = sock_over_response;
+
+ sockover.on_bi.bi_cf_ocs = osocs;
+
+ rc = config_register_schema( bscfg, osocs );
+ if ( rc ) return rc;
+
+ return overlay_register( &sockover );
+}
+
+static int
+sock_over_db_init(
+ Backend *be,
+ struct config_reply_s *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ void *private = be->be_private;
+ void *cf_ocs = be->be_cf_ocs;
+ int rc;
+
+ be->be_private = NULL;
+ rc = sock_back_db_init( be, cr );
+ on->on_bi.bi_private = be->be_private;
+ be->be_private = private;
+ be->be_cf_ocs = cf_ocs;
+ return rc;
+}
+
+static int
+sock_over_db_destroy(
+ Backend *be,
+ struct config_reply_s *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ void *private = be->be_private;
+ int rc;
+
+ be->be_private = on->on_bi.bi_private;
+ rc = sock_back_db_destroy( be, cr );
+ on->on_bi.bi_private = be->be_private;
+ be->be_private = private;
+ return rc;
+}
diff --git a/servers/slapd/back-sock/delete.c b/servers/slapd/back-sock/delete.c
new file mode 100644
index 0000000..89a268f
--- /dev/null
+++ b/servers/slapd/back-sock/delete.c
@@ -0,0 +1,75 @@
+/* delete.c - sock backend delete function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Brian Candler for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-sock.h"
+
+int
+sock_back_delete(
+ Operation *op,
+ SlapReply *rs )
+{
+ struct sockinfo *si = (struct sockinfo *) op->o_bd->be_private;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+ Entry e;
+ FILE *fp;
+
+ e.e_id = NOID;
+ e.e_name = op->o_req_dn;
+ e.e_nname = op->o_req_ndn;
+ e.e_attrs = NULL;
+ e.e_ocflags = 0;
+ e.e_bv.bv_len = 0;
+ e.e_bv.bv_val = NULL;
+ e.e_private = NULL;
+
+ if ( ! access_allowed( op, &e,
+ entry, NULL, ACL_WDEL, NULL ) )
+ {
+ send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS, NULL );
+ return -1;
+ }
+
+ if ( (fp = opensock( si->si_sockpath )) == NULL ) {
+ send_ldap_error( op, rs, LDAP_OTHER,
+ "could not open socket" );
+ return( -1 );
+ }
+
+ /* write out the request to the delete process */
+ fprintf( fp, "DELETE\n" );
+ fprintf( fp, "msgid: %ld\n", (long) op->o_msgid );
+ sock_print_conn( fp, op->o_conn, si );
+ sock_print_suffixes( fp, op->o_bd );
+ fprintf( fp, "dn: %s\n", op->o_req_dn.bv_val );
+ fprintf( fp, "\n" );
+
+ /* read in the results and send them along */
+ sock_read_and_send_results( op, rs, fp );
+ fclose( fp );
+ return( 0 );
+}
diff --git a/servers/slapd/back-sock/extended.c b/servers/slapd/back-sock/extended.c
new file mode 100644
index 0000000..e065761
--- /dev/null
+++ b/servers/slapd/back-sock/extended.c
@@ -0,0 +1,76 @@
+/* extended.c - sock backend extended routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-sock.h"
+
+#include "lutil.h"
+
+int
+sock_back_extended( Operation *op, SlapReply *rs )
+{
+ int rc;
+ struct sockinfo *si = (struct sockinfo *) op->o_bd->be_private;
+ FILE *fp;
+ struct berval b64;
+
+ Debug( LDAP_DEBUG_ARGS, "==> sock_back_extended(%s, %s)\n",
+ op->ore_reqoid.bv_val, op->o_req_dn.bv_val );
+
+ if ( (fp = opensock( si->si_sockpath )) == NULL ) {
+ send_ldap_error( op, rs, LDAP_OTHER,
+ "could not open socket" );
+ return( -1 );
+ }
+
+ /* write out the request to the extended process */
+ fprintf( fp, "EXTENDED\n" );
+ fprintf( fp, "msgid: %ld\n", (long) op->o_msgid );
+ sock_print_conn( fp, op->o_conn, si );
+ sock_print_suffixes( fp, op->o_bd );
+ fprintf( fp, "oid: %s\n", op->ore_reqoid.bv_val );
+
+ if (op->ore_reqdata) {
+
+ b64.bv_len = LUTIL_BASE64_ENCODE_LEN( op->ore_reqdata->bv_len ) + 1;
+ b64.bv_val = op->o_tmpalloc( b64.bv_len + 1, op->o_tmpmemctx );
+
+ rc = lutil_b64_ntop(
+ (unsigned char *) op->ore_reqdata->bv_val, op->ore_reqdata->bv_len,
+ b64.bv_val, b64.bv_len );
+
+ b64.bv_len = rc;
+ assert( strlen(b64.bv_val) == b64.bv_len );
+
+ fprintf( fp, "value: %s\n", b64.bv_val );
+
+ op->o_tmpfree( b64.bv_val, op->o_tmpmemctx );
+
+ }
+
+ fprintf( fp, "\n" );
+
+ /* read in the results and send them along */
+ rc = sock_read_and_send_results( op, rs, fp );
+ fclose( fp );
+
+ return( rc );
+}
diff --git a/servers/slapd/back-sock/init.c b/servers/slapd/back-sock/init.c
new file mode 100644
index 0000000..02b7bc8
--- /dev/null
+++ b/servers/slapd/back-sock/init.c
@@ -0,0 +1,97 @@
+/* init.c - initialize sock backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Brian Candler for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-sock.h"
+
+int
+sock_back_initialize(
+ BackendInfo *bi
+)
+{
+ bi->bi_open = 0;
+ bi->bi_config = 0;
+ bi->bi_close = 0;
+ bi->bi_destroy = 0;
+
+ bi->bi_db_init = sock_back_db_init;
+ bi->bi_db_config = 0;
+ bi->bi_db_open = 0;
+ bi->bi_db_close = 0;
+ bi->bi_db_destroy = sock_back_db_destroy;
+
+ bi->bi_op_bind = sock_back_bind;
+ bi->bi_op_unbind = sock_back_unbind;
+ bi->bi_op_search = sock_back_search;
+ bi->bi_op_compare = sock_back_compare;
+ bi->bi_op_modify = sock_back_modify;
+ bi->bi_op_modrdn = sock_back_modrdn;
+ bi->bi_op_add = sock_back_add;
+ bi->bi_op_delete = sock_back_delete;
+ bi->bi_op_abandon = 0;
+
+ bi->bi_extended = sock_back_extended;
+
+ bi->bi_chk_referrals = 0;
+
+ bi->bi_connection_init = 0;
+ bi->bi_connection_destroy = 0;
+
+ return sock_back_init_cf( bi );
+}
+
+int
+sock_back_db_init(
+ Backend *be,
+ struct config_reply_s *cr
+)
+{
+ struct sockinfo *si;
+
+ si = (struct sockinfo *) ch_calloc( 1, sizeof(struct sockinfo) );
+
+ be->be_private = si;
+ be->be_cf_ocs = be->bd_info->bi_cf_ocs;
+
+ return si == NULL;
+}
+
+int
+sock_back_db_destroy(
+ Backend *be,
+ struct config_reply_s *cr
+)
+{
+ free( be->be_private );
+ return 0;
+}
+
+#if SLAPD_SOCK == SLAPD_MOD_DYNAMIC
+
+/* conditionally define the init_module() function */
+SLAP_BACKEND_INIT_MODULE( sock )
+
+#endif /* SLAPD_SOCK == SLAPD_MOD_DYNAMIC */
diff --git a/servers/slapd/back-sock/modify.c b/servers/slapd/back-sock/modify.c
new file mode 100644
index 0000000..a5ec012
--- /dev/null
+++ b/servers/slapd/back-sock/modify.c
@@ -0,0 +1,117 @@
+/* modify.c - sock backend modify function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Brian Candler for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "back-sock.h"
+#include "ldif.h"
+
+int
+sock_back_modify(
+ Operation *op,
+ SlapReply *rs )
+{
+ Modification *mod;
+ struct sockinfo *si = (struct sockinfo *) op->o_bd->be_private;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+ Modifications *ml = op->orm_modlist;
+ Entry e;
+ FILE *fp;
+ int i;
+
+ e.e_id = NOID;
+ e.e_name = op->o_req_dn;
+ e.e_nname = op->o_req_ndn;
+ e.e_attrs = NULL;
+ e.e_ocflags = 0;
+ e.e_bv.bv_len = 0;
+ e.e_bv.bv_val = NULL;
+ e.e_private = NULL;
+
+ if ( ! access_allowed( op, &e,
+ entry, NULL, ACL_WRITE, NULL ) )
+ {
+ send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS, NULL );
+ return -1;
+ }
+
+ if ( (fp = opensock( si->si_sockpath )) == NULL ) {
+ send_ldap_error( op, rs, LDAP_OTHER,
+ "could not open socket" );
+ return( -1 );
+ }
+
+ /* write out the request to the modify process */
+ fprintf( fp, "MODIFY\n" );
+ fprintf( fp, "msgid: %ld\n", (long) op->o_msgid );
+ sock_print_conn( fp, op->o_conn, si );
+ sock_print_suffixes( fp, op->o_bd );
+ fprintf( fp, "dn: %s\n", op->o_req_dn.bv_val );
+ for ( ; ml != NULL; ml = ml->sml_next ) {
+ mod = &ml->sml_mod;
+
+ switch ( mod->sm_op ) {
+ case LDAP_MOD_ADD:
+ fprintf( fp, "add: %s\n", mod->sm_desc->ad_cname.bv_val );
+ break;
+
+ case LDAP_MOD_DELETE:
+ fprintf( fp, "delete: %s\n", mod->sm_desc->ad_cname.bv_val );
+ break;
+
+ case LDAP_MOD_REPLACE:
+ fprintf( fp, "replace: %s\n", mod->sm_desc->ad_cname.bv_val );
+ break;
+
+ case LDAP_MOD_INCREMENT:
+ fprintf( fp, "increment: %s\n", mod->sm_desc->ad_cname.bv_val );
+ break;
+ }
+
+ if( mod->sm_values != NULL ) {
+ for ( i = 0; mod->sm_values[i].bv_val != NULL; i++ ) {
+ char *text = ldif_put_wrap( LDIF_PUT_VALUE,
+ mod->sm_desc->ad_cname.bv_val,
+ mod->sm_values[i].bv_val,
+ mod->sm_values[i].bv_len, LDIF_LINE_WIDTH_MAX );
+ if ( text ) {
+ fprintf( fp, "%s", text );
+ ber_memfree( text );
+ } else {
+ break;
+ }
+ }
+ }
+
+ fprintf( fp, "-\n" );
+ }
+ fprintf( fp, "\n" );
+
+ /* read in the results and send them along */
+ sock_read_and_send_results( op, rs, fp );
+ fclose( fp );
+ return( 0 );
+}
diff --git a/servers/slapd/back-sock/modrdn.c b/servers/slapd/back-sock/modrdn.c
new file mode 100644
index 0000000..c5a9195
--- /dev/null
+++ b/servers/slapd/back-sock/modrdn.c
@@ -0,0 +1,81 @@
+/* modrdn.c - sock backend modrdn function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Brian Candler for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-sock.h"
+
+int
+sock_back_modrdn(
+ Operation *op,
+ SlapReply *rs )
+{
+ struct sockinfo *si = (struct sockinfo *) op->o_bd->be_private;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+ Entry e;
+ FILE *fp;
+
+ e.e_id = NOID;
+ e.e_name = op->o_req_dn;
+ e.e_nname = op->o_req_ndn;
+ e.e_attrs = NULL;
+ e.e_ocflags = 0;
+ e.e_bv.bv_len = 0;
+ e.e_bv.bv_val = NULL;
+ e.e_private = NULL;
+
+ if ( ! access_allowed( op, &e, entry, NULL,
+ op->oq_modrdn.rs_newSup ? ACL_WDEL : ACL_WRITE,
+ NULL ) )
+ {
+ send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS, NULL );
+ return -1;
+ }
+
+ if ( (fp = opensock( si->si_sockpath )) == NULL ) {
+ send_ldap_error( op, rs, LDAP_OTHER,
+ "could not open socket" );
+ return( -1 );
+ }
+
+ /* write out the request to the modrdn process */
+ fprintf( fp, "MODRDN\n" );
+ fprintf( fp, "msgid: %ld\n", (long) op->o_msgid );
+ sock_print_conn( fp, op->o_conn, si );
+ sock_print_suffixes( fp, op->o_bd );
+ fprintf( fp, "dn: %s\n", op->o_req_dn.bv_val );
+ fprintf( fp, "newrdn: %s\n", op->oq_modrdn.rs_newrdn.bv_val );
+ fprintf( fp, "deleteoldrdn: %d\n", op->oq_modrdn.rs_deleteoldrdn ? 1 : 0 );
+ if ( op->oq_modrdn.rs_newSup != NULL ) {
+ fprintf( fp, "newSuperior: %s\n", op->oq_modrdn.rs_newSup->bv_val );
+ }
+ fprintf( fp, "\n" );
+
+ /* read in the results and send them along */
+ sock_read_and_send_results( op, rs, fp );
+ fclose( fp );
+ return( 0 );
+}
diff --git a/servers/slapd/back-sock/opensock.c b/servers/slapd/back-sock/opensock.c
new file mode 100644
index 0000000..9b4826d
--- /dev/null
+++ b/servers/slapd/back-sock/opensock.c
@@ -0,0 +1,71 @@
+/* opensock.c - open a unix domain socket */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Brian Candler for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+
+#include "slap.h"
+#include "back-sock.h"
+
+/*
+ * FIXME: count the number of concurrent open sockets (since each thread
+ * may open one). Perhaps block here if a soft limit is reached, and fail
+ * if a hard limit reached
+ */
+
+FILE *
+opensock(
+ const char *sockpath
+)
+{
+ int fd;
+ FILE *fp;
+ struct sockaddr_un sockun;
+
+ fd = socket(PF_UNIX, SOCK_STREAM, 0);
+ if ( fd < 0 ) {
+ Debug( LDAP_DEBUG_ANY, "socket create failed\n" );
+ return( NULL );
+ }
+
+ sockun.sun_family = AF_UNIX;
+ sprintf(sockun.sun_path, "%.*s", (int)(sizeof(sockun.sun_path)-1),
+ sockpath);
+ if ( connect( fd, (struct sockaddr *)&sockun, sizeof(sockun) ) < 0 ) {
+ Debug( LDAP_DEBUG_ANY, "socket connect(%s) failed\n",
+ sockpath ? sockpath : "<null>" );
+ close( fd );
+ return( NULL );
+ }
+
+ if ( ( fp = fdopen( fd, "r+" ) ) == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "fdopen failed\n" );
+ close( fd );
+ return( NULL );
+ }
+
+ return( fp );
+}
diff --git a/servers/slapd/back-sock/proto-sock.h b/servers/slapd/back-sock/proto-sock.h
new file mode 100644
index 0000000..0808329
--- /dev/null
+++ b/servers/slapd/back-sock/proto-sock.h
@@ -0,0 +1,49 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Brian Candler for inclusion
+ * in OpenLDAP Software.
+ */
+
+#ifndef _PROTO_SOCK_H
+#define _PROTO_SOCK_H
+
+LDAP_BEGIN_DECL
+
+extern BI_init sock_back_initialize;
+
+extern BI_open sock_back_open;
+extern BI_close sock_back_close;
+extern BI_destroy sock_back_destroy;
+
+extern BI_db_init sock_back_db_init;
+extern BI_db_destroy sock_back_db_destroy;
+
+extern BI_op_bind sock_back_bind;
+extern BI_op_unbind sock_back_unbind;
+extern BI_op_search sock_back_search;
+extern BI_op_compare sock_back_compare;
+extern BI_op_modify sock_back_modify;
+extern BI_op_modrdn sock_back_modrdn;
+extern BI_op_add sock_back_add;
+extern BI_op_delete sock_back_delete;
+
+extern BI_op_extended sock_back_extended;
+
+extern int sock_back_init_cf( BackendInfo *bi );
+
+LDAP_END_DECL
+
+#endif /* _PROTO_SOCK_H */
diff --git a/servers/slapd/back-sock/result.c b/servers/slapd/back-sock/result.c
new file mode 100644
index 0000000..55a4060
--- /dev/null
+++ b/servers/slapd/back-sock/result.c
@@ -0,0 +1,168 @@
+/* result.c - sock backend result reading function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Brian Candler for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+
+#include "slap.h"
+#include "back-sock.h"
+
+/*
+ * FIXME: make a RESULT section compulsory from the socket response.
+ * Otherwise, a partial/aborted response is treated as 'success'.
+ * This is a divergence from the back-shell protocol, but makes things
+ * more robust.
+ */
+
+int
+sock_read_and_send_results(
+ Operation *op,
+ SlapReply *rs,
+ FILE *fp )
+{
+ int bsize, len;
+ char *buf, *bp;
+ char line[BUFSIZ];
+ char ebuf[128];
+
+ (void) fflush(fp);
+ /* read in the result and send it along */
+ buf = (char *) ch_malloc( BUFSIZ );
+ buf[0] = '\0';
+ bsize = BUFSIZ;
+ bp = buf;
+ while ( !feof(fp) ) {
+ errno = 0;
+ if ( fgets( line, sizeof(line), fp ) == NULL ) {
+ int saved_errno = errno;
+ if ( errno == EINTR ) continue;
+
+ Debug( LDAP_DEBUG_ANY, "sock: fgets failed: %s (%d)\n",
+ AC_STRERROR_R(saved_errno, ebuf, sizeof ebuf), saved_errno );
+ break;
+ }
+
+ Debug( LDAP_DEBUG_SHELL, "sock search reading line (%s)\n",
+ line );
+
+ /* ignore lines beginning with # (LDIFv1 comments) */
+ if ( *line == '#' ) {
+ continue;
+ }
+
+ /* ignore lines beginning with DEBUG: */
+ if ( strncasecmp( line, "DEBUG:", 6 ) == 0 ) {
+ continue;
+ }
+
+ if ( strncasecmp( line, "CONTINUE", 8 ) == 0 ) {
+ struct sockinfo *si = (struct sockinfo *) op->o_bd->be_private;
+ /* Only valid when operating as an overlay! */
+ assert( si->si_ops != 0 );
+ rs->sr_err = SLAP_CB_CONTINUE;
+ goto skip;
+ }
+
+ len = strlen( line );
+ while ( bp + len + 1 - buf > bsize ) {
+ size_t offset = bp - buf;
+ bsize += BUFSIZ;
+ buf = (char *) ch_realloc( buf, bsize );
+ bp = &buf[offset];
+ }
+ strcpy( bp, line );
+ bp += len;
+
+ /* line marked the end of an entry or result */
+ if ( *line == '\n' ) {
+ if ( strncasecmp( buf, "RESULT", 6 ) == 0 ) {
+ break;
+ }
+
+ if ( (rs->sr_entry = str2entry( buf )) == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "str2entry(%s) failed\n",
+ buf );
+ } else {
+ rs->sr_attrs = op->oq_search.rs_attrs;
+ rs->sr_flags = REP_ENTRY_MODIFIABLE;
+ send_search_entry( op, rs );
+ entry_free( rs->sr_entry );
+ rs->sr_attrs = NULL;
+ }
+
+ bp = buf;
+ }
+ }
+ (void) str2result( buf, &rs->sr_err, (char **)&rs->sr_matched, (char **)&rs->sr_text );
+
+ /* otherwise, front end will send this result */
+ if ( rs->sr_err != 0 || op->o_tag != LDAP_REQ_BIND ) {
+ send_ldap_result( op, rs );
+ }
+
+skip:
+ ch_free( buf );
+
+ return( rs->sr_err );
+}
+
+void
+sock_print_suffixes(
+ FILE *fp,
+ Backend *be
+)
+{
+ int i;
+
+ for ( i = 0; be->be_suffix[i].bv_val != NULL; i++ ) {
+ fprintf( fp, "suffix: %s\n", be->be_suffix[i].bv_val );
+ }
+}
+
+void
+sock_print_conn(
+ FILE *fp,
+ Connection *conn,
+ struct sockinfo *si
+)
+{
+ if ( conn == NULL ) return;
+
+ if( si->si_extensions & SOCK_EXT_BINDDN ) {
+ fprintf( fp, "binddn: %s\n",
+ conn->c_dn.bv_len ? conn->c_dn.bv_val : "" );
+ }
+ if( si->si_extensions & SOCK_EXT_PEERNAME ) {
+ fprintf( fp, "peername: %s\n",
+ conn->c_peer_name.bv_len ? conn->c_peer_name.bv_val : "" );
+ }
+ if( si->si_extensions & SOCK_EXT_SSF ) {
+ fprintf( fp, "ssf: %d\n", conn->c_ssf );
+ }
+ if( si->si_extensions & SOCK_EXT_CONNID ) {
+ fprintf( fp, "connid: %lu\n", conn->c_connid );
+ }
+}
diff --git a/servers/slapd/back-sock/search.c b/servers/slapd/back-sock/search.c
new file mode 100644
index 0000000..9812e99
--- /dev/null
+++ b/servers/slapd/back-sock/search.c
@@ -0,0 +1,74 @@
+/* search.c - sock backend search function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Brian Candler for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-sock.h"
+
+/*
+ * FIXME: add a filterSearchResults option like back-perl has
+ */
+
+int
+sock_back_search(
+ Operation *op,
+ SlapReply *rs )
+{
+ struct sockinfo *si = (struct sockinfo *) op->o_bd->be_private;
+ FILE *fp;
+ AttributeName *an;
+
+ if ( (fp = opensock( si->si_sockpath )) == NULL ) {
+ send_ldap_error( op, rs, LDAP_OTHER,
+ "could not open socket" );
+ return( -1 );
+ }
+
+ /* write out the request to the search process */
+ fprintf( fp, "SEARCH\n" );
+ fprintf( fp, "msgid: %ld\n", (long) op->o_msgid );
+ sock_print_conn( fp, op->o_conn, si );
+ sock_print_suffixes( fp, op->o_bd );
+ fprintf( fp, "base: %s\n", op->o_req_dn.bv_val );
+ fprintf( fp, "scope: %d\n", op->oq_search.rs_scope );
+ fprintf( fp, "deref: %d\n", op->oq_search.rs_deref );
+ fprintf( fp, "sizelimit: %d\n", op->oq_search.rs_slimit );
+ fprintf( fp, "timelimit: %d\n", op->oq_search.rs_tlimit );
+ fprintf( fp, "filter: %s\n", op->oq_search.rs_filterstr.bv_val );
+ fprintf( fp, "attrsonly: %d\n", op->oq_search.rs_attrsonly ? 1 : 0 );
+ fprintf( fp, "attrs:%s", op->oq_search.rs_attrs == NULL ? " all" : "" );
+ for ( an = op->oq_search.rs_attrs; an && an->an_name.bv_val; an++ ) {
+ fprintf( fp, " %s", an->an_name.bv_val );
+ }
+ fprintf( fp, "\n\n" ); /* end of attr line plus blank line */
+
+ /* read in the results and send them along */
+ rs->sr_attrs = op->oq_search.rs_attrs;
+ sock_read_and_send_results( op, rs, fp );
+
+ fclose( fp );
+ return( 0 );
+}
diff --git a/servers/slapd/back-sock/searchexample.conf b/servers/slapd/back-sock/searchexample.conf
new file mode 100644
index 0000000..842d6aa
--- /dev/null
+++ b/servers/slapd/back-sock/searchexample.conf
@@ -0,0 +1,23 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2007-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This work was initially developed by Brian Candler for inclusion
+## in OpenLDAP Software.
+
+include /usr/local/etc/openldap/schema/core.schema
+
+database sock
+suffix "dc=example,dc=com"
+socketpath /tmp/example.sock
diff --git a/servers/slapd/back-sock/searchexample.pl b/servers/slapd/back-sock/searchexample.pl
new file mode 100644
index 0000000..f867005
--- /dev/null
+++ b/servers/slapd/back-sock/searchexample.pl
@@ -0,0 +1,90 @@
+#!/usr/bin/perl -w -T
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2007-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This work was initially developed by Brian Candler for inclusion
+## in OpenLDAP Software.
+
+# See: http://search.cpan.org/dist/Net-Server/
+
+package ExampleDB;
+
+use strict;
+use vars qw(@ISA);
+use Net::Server::PreFork; # any personality will do
+
+@ISA = qw(Net::Server::PreFork);
+
+ExampleDB->run(
+ port=>"/tmp/example.sock|unix"
+ #conf_file=>"/etc/example.conf"
+);
+exit;
+
+### over-ridden subs below
+# The protocol is the same as back-shell
+
+sub process_request {
+ my $self = shift;
+
+ eval {
+
+ local $SIG{ALRM} = sub { die "Timed Out!\n" };
+ my $timeout = 30; # give the user 30 seconds to type a line
+ alarm($timeout);
+
+ my $request = <STDIN>;
+
+ if ($request eq "SEARCH\n") {
+ my %req = ();
+ while (my $line = <STDIN>) {
+ chomp($line);
+ last if $line eq "";
+ if ($line =~ /^([^:]+):\s*(.*)$/) { # FIXME: handle base64 encoded
+ $req{$1} = $2;
+ }
+ }
+ #sleep(2); # to test concurrency
+ print "dn: cn=test, dc=example, dc=com\n";
+ print "cn: test\n";
+ print "objectclass: cnobject\n";
+ print "\n";
+ print "RESULT\n";
+ print "code: 0\n";
+ print "info: answered by process $$\n";
+ }
+ else {
+ print "RESULT\n";
+ print "code: 53\n"; # unwillingToPerform
+ print "info: I don't implement $request";
+ }
+
+ };
+
+ return unless $@;
+ if( $@=~/timed out/i ){
+ print "RESULT\n";
+ print "code: 3\n"; # timeLimitExceeded
+ print "info: Timed out\n";
+ }
+ else {
+ print "RESULT\n";
+ print "code: 1\n"; # operationsError
+ print "info: $@\n"; # FIXME: remove CR/LF
+ }
+
+}
+
+1;
diff --git a/servers/slapd/back-sock/unbind.c b/servers/slapd/back-sock/unbind.c
new file mode 100644
index 0000000..0b349e1
--- /dev/null
+++ b/servers/slapd/back-sock/unbind.c
@@ -0,0 +1,57 @@
+/* unbind.c - sock backend unbind function */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2007-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Brian Candler for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "back-sock.h"
+
+int
+sock_back_unbind(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ struct sockinfo *si = (struct sockinfo *) op->o_bd->be_private;
+ FILE *fp;
+
+ if ( (fp = opensock( si->si_sockpath )) == NULL ) {
+ send_ldap_error( op, rs, LDAP_OTHER,
+ "could not open socket" );
+ return( -1 );
+ }
+
+ /* write out the request to the unbind process */
+ fprintf( fp, "UNBIND\n" );
+ fprintf( fp, "msgid: %ld\n", (long) op->o_msgid );
+ sock_print_conn( fp, op->o_conn, si );
+ sock_print_suffixes( fp, op->o_bd );
+ fprintf( fp, "\n" );
+
+ /* no response to unbind */
+ fclose( fp );
+
+ return 0;
+}
diff --git a/servers/slapd/back-sql/Makefile.in b/servers/slapd/back-sql/Makefile.in
new file mode 100644
index 0000000..b90bb31
--- /dev/null
+++ b/servers/slapd/back-sql/Makefile.in
@@ -0,0 +1,45 @@
+# Makefile.in for back-sql
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SRCS = init.c config.c search.c bind.c compare.c operational.c \
+ entry-id.c schema-map.c sql-wrap.c modify.c util.c \
+ add.c delete.c modrdn.c api.c
+OBJS = init.lo config.lo search.lo bind.lo compare.lo operational.lo \
+ entry-id.lo schema-map.lo sql-wrap.lo modify.lo util.lo \
+ add.lo delete.lo modrdn.lo api.lo
+
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-sql"
+BUILD_MOD = @BUILD_SQL@
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = $(@BUILD_SQL@_DEFS)
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS) $(SLAPD_SQL_LIBS)
+
+LIBBASE = back_sql
+
+XINCPATH = -I.. -I$(srcdir)/.. $(SLAPD_SQL_INCLUDES)
+XDEFS = $(MODULES_CPPFLAGS)
+
+all-local-lib: ../.backend
+
+../.backend: lib$(LIBBASE).a
+ @touch $@
+
diff --git a/servers/slapd/back-sql/add.c b/servers/slapd/back-sql/add.c
new file mode 100644
index 0000000..3098521
--- /dev/null
+++ b/servers/slapd/back-sql/add.c
@@ -0,0 +1,1560 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 Dmitry Kovalev.
+ * Portions Copyright 2002 Pierangelo Masarati.
+ * Portions Copyright 2004 Mark Adamson.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Dmitry Kovalev for inclusion
+ * by OpenLDAP Software. Additional significant contributors include
+ * Pierangelo Masarati and Mark Adamson.
+
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "ac/string.h"
+
+#include "slap.h"
+#include "proto-sql.h"
+
+#ifdef BACKSQL_SYNCPROV
+#include <lutil.h>
+#endif /* BACKSQL_SYNCPROV */
+
+const char * processable_op_attrs[] = {
+ "pwdAccountLockedTime",
+ "pwdChangedTime",
+ "pwdFailureTime",
+ "pwdGraceUseTime",
+ "pwdHistory",
+ "pwdPolicySubentry",
+ "pwdReset",
+ "entryUUID"
+};
+
+#define processable_op_attrs_length (sizeof (processable_op_attrs) / sizeof (const char *))
+
+static int indexOf(const char *array[], int array_size, const char * value) {
+ for (int i = 0; i < array_size; ++i) {
+ if(strcmp(array[i], value) == 0) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static int is_processable_opattr(const char * attr) {
+ return indexOf(processable_op_attrs, processable_op_attrs_length, attr) >= 0;
+}
+
+#define backsql_opattr_skip(ad) \
+ (is_at_operational( (ad)->ad_type ) && (ad) != slap_schema.si_ad_ref )
+
+/*
+ * Skip:
+ * - null values (e.g. delete modification)
+ * - single occurrence of objectClass, because it is already used
+ * to determine how to build the SQL entry
+ * - operational attributes (except those in processable_op_attrs)
+ * - empty attributes
+ */
+#define backsql_attr_skip(ad, vals) \
+ ( \
+ ( ( (ad) == slap_schema.si_ad_objectClass \
+ && (vals) && BER_BVISNULL( &((vals)[ 1 ]) ) ) \
+ || backsql_opattr_skip( (ad) ) \
+ || ( (vals) && BER_BVISNULL( &((vals)[ 0 ]) ) ) \
+ ) && !is_processable_opattr( ad->ad_cname.bv_val ) )
+
+int
+backsql_modify_delete_all_values(
+ Operation *op,
+ SlapReply *rs,
+ SQLHDBC dbh,
+ backsql_entryID *e_id,
+ backsql_at_map_rec *at )
+{
+ backsql_info *bi = (backsql_info *)op->o_bd->be_private;
+ RETCODE rc;
+ SQLHSTMT asth = SQL_NULL_HSTMT;
+ BACKSQL_ROW_NTS row;
+
+ assert( at != NULL );
+ if ( at->bam_delete_proc == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_delete_all_values(): "
+ "missing attribute value delete procedure "
+ "for attr \"%s\"\n",
+ at->bam_ad->ad_cname.bv_val );
+ if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+ rs->sr_text = "SQL-backend error";
+ return rs->sr_err = LDAP_OTHER;
+ }
+
+ return LDAP_SUCCESS;
+ }
+
+ rc = backsql_Prepare( dbh, &asth, at->bam_query, 0 );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_delete_all_values(): "
+ "error preparing attribute value select query "
+ "\"%s\"\n",
+ at->bam_query );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ asth, rc );
+
+ rs->sr_text = "SQL-backend error";
+ return rs->sr_err = LDAP_OTHER;
+ }
+
+ rc = backsql_BindParamID( asth, 1, SQL_PARAM_INPUT, &e_id->eid_keyval );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_delete_all_values(): "
+ "error binding key value parameter "
+ "to attribute value select query\n" );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ asth, rc );
+ SQLFreeStmt( asth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ return rs->sr_err = LDAP_OTHER;
+ }
+
+ rc = SQLExecute( asth );
+ if ( !BACKSQL_SUCCESS( rc ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_delete_all_values(): "
+ "error executing attribute value select query\n" );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ asth, rc );
+ SQLFreeStmt( asth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ return rs->sr_err = LDAP_OTHER;
+ }
+
+ backsql_BindRowAsStrings_x( asth, &row, op->o_tmpmemctx );
+ for ( rc = SQLFetch( asth );
+ BACKSQL_SUCCESS( rc );
+ rc = SQLFetch( asth ) )
+ {
+ int i;
+ /* first parameter no, parameter order */
+ SQLUSMALLINT pno = 0,
+ po = 0;
+ /* procedure return code */
+ int prc = LDAP_SUCCESS;
+
+ for ( i = 0; i < row.ncols; i++ ) {
+ SQLHSTMT sth = SQL_NULL_HSTMT;
+ ber_len_t col_len;
+
+ rc = backsql_Prepare( dbh, &sth, at->bam_delete_proc, 0 );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_delete_all_values(): "
+ "error preparing attribute value "
+ "delete procedure "
+ "\"%s\"\n",
+ at->bam_delete_proc );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+
+ if ( BACKSQL_IS_DEL( at->bam_expect_return ) ) {
+ pno = 1;
+ rc = backsql_BindParamInt( sth, 1,
+ SQL_PARAM_OUTPUT, &prc );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_delete_all_values(): "
+ "error binding output parameter for %s[%d]\n",
+ at->bam_ad->ad_cname.bv_val, i );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+ }
+ po = ( BACKSQL_IS_DEL( at->bam_param_order ) ) > 0;
+ rc = backsql_BindParamID( sth, pno + 1 + po,
+ SQL_PARAM_INPUT, &e_id->eid_keyval );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_delete_all_values(): "
+ "error binding keyval parameter for %s[%d]\n",
+ at->bam_ad->ad_cname.bv_val, i );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_delete_all_values() "
+ "arg(%d)=" BACKSQL_IDFMT "\n",
+ pno + 1 + po,
+ BACKSQL_IDARG(e_id->eid_keyval) );
+
+ /*
+ * check for syntax needed here
+ * maybe need binary bind?
+ */
+ col_len = strlen( row.cols[ i ] );
+ rc = backsql_BindParamStr( sth, pno + 2 - po,
+ SQL_PARAM_INPUT, row.cols[ i ], col_len );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_delete_all_values(): "
+ "error binding value parameter for %s[%d]\n",
+ at->bam_ad->ad_cname.bv_val, i );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_delete_all_values(): "
+ "arg(%d)=%s; executing \"%s\"\n",
+ pno + 2 - po, row.cols[ i ],
+ at->bam_delete_proc );
+ rc = SQLExecute( sth );
+ if ( rc == SQL_SUCCESS && prc == LDAP_SUCCESS ) {
+ rs->sr_err = LDAP_SUCCESS;
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_delete_all_values(): "
+ "delete_proc "
+ "execution failed (rc=%d, prc=%d)\n",
+ rc, prc );
+ if ( prc != LDAP_SUCCESS ) {
+ /* SQL procedure executed fine
+ * but returned an error */
+ rs->sr_err = BACKSQL_SANITIZE_ERROR( prc );
+
+ } else {
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ rs->sr_err = LDAP_OTHER;
+ }
+ rs->sr_text = op->o_req_dn.bv_val;
+ SQLFreeStmt( sth, SQL_DROP );
+ goto done;
+ }
+ SQLFreeStmt( sth, SQL_DROP );
+ }
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+
+done:;
+ backsql_FreeRow_x( &row, op->o_tmpmemctx );
+ SQLFreeStmt( asth, SQL_DROP );
+
+ return rs->sr_err;
+}
+
+int
+backsql_modify_internal(
+ Operation *op,
+ SlapReply *rs,
+ SQLHDBC dbh,
+ backsql_oc_map_rec *oc,
+ backsql_entryID *e_id,
+ Modifications *modlist )
+{
+ backsql_info *bi = (backsql_info *)op->o_bd->be_private;
+ RETCODE rc;
+ Modifications *ml;
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_modify_internal(): "
+ "traversing modifications list\n" );
+
+ for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
+ AttributeDescription *ad;
+ int sm_op;
+ static char *sm_ops[] = { "add", "delete", "replace", "increment", NULL };
+
+ BerVarray sm_values;
+#if 0
+ /* NOTE: some day we'll have to pass
+ * the normalized values as well */
+ BerVarray sm_nvalues;
+#endif
+ backsql_at_map_rec *at = NULL;
+ struct berval *at_val;
+ int i;
+
+ ad = ml->sml_mod.sm_desc;
+ sm_op = ( ml->sml_mod.sm_op & LDAP_MOD_OP );
+ sm_values = ml->sml_mod.sm_values;
+#if 0
+ sm_nvalues = ml->sml_mod.sm_nvalues;
+#endif
+
+ Debug( LDAP_DEBUG_TRACE, " backsql_modify_internal(): "
+ "modifying attribute \"%s\" (%s) according to "
+ "mappings for objectClass \"%s\"\n",
+ ad->ad_cname.bv_val, sm_ops[ sm_op ], BACKSQL_OC_NAME( oc ) );
+
+ if ( backsql_attr_skip( ad, sm_values ) ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_modify_internal(): "
+ "skipping attribute \"%s\"\n",
+ ad->ad_cname.bv_val, 0, 0 );
+
+ continue;
+ }
+
+ at = backsql_ad2at( oc, ad );
+ if ( at == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_modify_internal(): "
+ "attribute \"%s\" is not registered "
+ "in objectClass \"%s\"\n",
+ ad->ad_cname.bv_val, BACKSQL_OC_NAME( oc ) );
+
+ if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "operation not permitted "
+ "within namingContext";
+ goto done;
+ }
+
+ continue;
+ }
+
+ switch ( sm_op ) {
+ case LDAP_MOD_REPLACE: {
+ Debug( LDAP_DEBUG_TRACE, " backsql_modify_internal(): "
+ "replacing values for attribute \"%s\"\n",
+ at->bam_ad->ad_cname.bv_val );
+
+ if ( at->bam_add_proc == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_internal(): "
+ "add procedure is not defined "
+ "for attribute \"%s\" "
+ "- unable to perform replacements\n",
+ at->bam_ad->ad_cname.bv_val );
+
+ if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "operation not permitted "
+ "within namingContext";
+ goto done;
+ }
+
+ break;
+ }
+
+ if ( at->bam_delete_proc == NULL ) {
+ if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_internal(): "
+ "delete procedure is not defined "
+ "for attribute \"%s\"\n",
+ at->bam_ad->ad_cname.bv_val );
+
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "operation not permitted "
+ "within namingContext";
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_internal(): "
+ "delete procedure is not defined "
+ "for attribute \"%s\" "
+ "- adding only\n",
+ at->bam_ad->ad_cname.bv_val );
+
+ goto add_only;
+ }
+
+del_all:
+ rs->sr_err = backsql_modify_delete_all_values( op, rs, dbh, e_id, at );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ /* LDAP_MOD_DELETE gets here if all values must be deleted */
+ if ( sm_op == LDAP_MOD_DELETE ) {
+ break;
+ }
+ }
+
+ /*
+ * PASSTHROUGH - to add new attributes -- do NOT add break
+ */
+ case LDAP_MOD_ADD:
+ /* case SLAP_MOD_SOFTADD: */
+ /* case SLAP_MOD_ADD_IF_NOT_PRESENT: */
+add_only:;
+ if ( at->bam_add_proc == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_internal(): "
+ "add procedure is not defined "
+ "for attribute \"%s\"\n",
+ at->bam_ad->ad_cname.bv_val );
+
+ if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "operation not permitted "
+ "within namingContext";
+ goto done;
+ }
+
+ break;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, " backsql_modify_internal(): "
+ "adding new values for attribute \"%s\"\n",
+ at->bam_ad->ad_cname.bv_val );
+
+ /* can't add a NULL val array */
+ assert( sm_values != NULL );
+
+ for ( i = 0, at_val = sm_values;
+ !BER_BVISNULL( at_val );
+ i++, at_val++ )
+ {
+ SQLHSTMT sth = SQL_NULL_HSTMT;
+ /* first parameter position, parameter order */
+ SQLUSMALLINT pno = 0,
+ po;
+ /* procedure return code */
+ int prc = LDAP_SUCCESS;
+
+ rc = backsql_Prepare( dbh, &sth, at->bam_add_proc, 0 );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_internal(): "
+ "error preparing add query\n" );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
+
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "SQL-backend error";
+ goto done;
+ }
+
+ if ( BACKSQL_IS_ADD( at->bam_expect_return ) ) {
+ pno = 1;
+ rc = backsql_BindParamInt( sth, 1,
+ SQL_PARAM_OUTPUT, &prc );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_internal(): "
+ "error binding output parameter for %s[%d]\n",
+ at->bam_ad->ad_cname.bv_val, i );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+ }
+ po = ( BACKSQL_IS_ADD( at->bam_param_order ) ) > 0;
+ rc = backsql_BindParamID( sth, pno + 1 + po,
+ SQL_PARAM_INPUT, &e_id->eid_keyval );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_internal(): "
+ "error binding keyval parameter for %s[%d]\n",
+ at->bam_ad->ad_cname.bv_val, i );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_internal(): "
+ "arg(%d)=" BACKSQL_IDFMT "\n",
+ pno + 1 + po,
+ BACKSQL_IDARG(e_id->eid_keyval) );
+
+ /*
+ * check for syntax needed here
+ * maybe need binary bind?
+ */
+ rc = backsql_BindParamBerVal( sth, pno + 2 - po,
+ SQL_PARAM_INPUT, at_val );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_internal(): "
+ "error binding value parameter for %s[%d]\n",
+ at->bam_ad->ad_cname.bv_val, i );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_internal(): "
+ "arg(%d)=\"%s\"; executing \"%s\"\n",
+ pno + 2 - po, at_val->bv_val,
+ at->bam_add_proc );
+
+ rc = SQLExecute( sth );
+ if ( rc == SQL_SUCCESS && prc == LDAP_SUCCESS ) {
+ rs->sr_err = LDAP_SUCCESS;
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_internal(): "
+ "add_proc execution failed "
+ "(rc=%d, prc=%d)\n",
+ rc, prc );
+ if ( prc != LDAP_SUCCESS ) {
+ /* SQL procedure executed fine
+ * but returned an error */
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_err = BACKSQL_SANITIZE_ERROR( prc );
+ rs->sr_text = at->bam_ad->ad_cname.bv_val;
+ return rs->sr_err;
+
+ } else {
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) )
+ {
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "SQL-backend error";
+ goto done;
+ }
+ }
+ }
+ SQLFreeStmt( sth, SQL_DROP );
+ }
+ break;
+
+ case LDAP_MOD_DELETE:
+ /* case SLAP_MOD_SOFTDEL: */
+ if ( at->bam_delete_proc == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_internal(): "
+ "delete procedure is not defined "
+ "for attribute \"%s\"\n",
+ at->bam_ad->ad_cname.bv_val );
+
+ if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "operation not permitted "
+ "within namingContext";
+ goto done;
+ }
+
+ break;
+ }
+
+ if ( sm_values == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_internal(): "
+ "no values given to delete "
+ "for attribute \"%s\" "
+ "-- deleting all values\n",
+ at->bam_ad->ad_cname.bv_val );
+ goto del_all;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, " backsql_modify_internal(): "
+ "deleting values for attribute \"%s\"\n",
+ at->bam_ad->ad_cname.bv_val );
+
+ for ( i = 0, at_val = sm_values;
+ !BER_BVISNULL( at_val );
+ i++, at_val++ )
+ {
+ SQLHSTMT sth = SQL_NULL_HSTMT;
+ /* first parameter position, parameter order */
+ SQLUSMALLINT pno = 0,
+ po;
+ /* procedure return code */
+ int prc = LDAP_SUCCESS;
+
+ rc = backsql_Prepare( dbh, &sth, at->bam_delete_proc, 0 );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_internal(): "
+ "error preparing delete query\n" );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
+
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "SQL-backend error";
+ goto done;
+ }
+
+ if ( BACKSQL_IS_DEL( at->bam_expect_return ) ) {
+ pno = 1;
+ rc = backsql_BindParamInt( sth, 1,
+ SQL_PARAM_OUTPUT, &prc );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_internal(): "
+ "error binding output parameter for %s[%d]\n",
+ at->bam_ad->ad_cname.bv_val, i );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+ }
+ po = ( BACKSQL_IS_DEL( at->bam_param_order ) ) > 0;
+ rc = backsql_BindParamID( sth, pno + 1 + po,
+ SQL_PARAM_INPUT, &e_id->eid_keyval );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_internal(): "
+ "error binding keyval parameter for %s[%d]\n",
+ at->bam_ad->ad_cname.bv_val, i );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_internal(): "
+ "arg(%d)=" BACKSQL_IDFMT "\n",
+ pno + 1 + po,
+ BACKSQL_IDARG(e_id->eid_keyval) );
+
+ /*
+ * check for syntax needed here
+ * maybe need binary bind?
+ */
+ rc = backsql_BindParamBerVal( sth, pno + 2 - po,
+ SQL_PARAM_INPUT, at_val );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_internal(): "
+ "error binding value parameter for %s[%d]\n",
+ at->bam_ad->ad_cname.bv_val, i );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_internal(): "
+ "executing \"%s\"\n",
+ at->bam_delete_proc );
+ rc = SQLExecute( sth );
+ if ( rc == SQL_SUCCESS && prc == LDAP_SUCCESS )
+ {
+ rs->sr_err = LDAP_SUCCESS;
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modify_internal(): "
+ "delete_proc execution "
+ "failed (rc=%d, prc=%d)\n",
+ rc, prc );
+
+ if ( prc != LDAP_SUCCESS ) {
+ /* SQL procedure executed fine
+ * but returned an error */
+ rs->sr_err = BACKSQL_SANITIZE_ERROR( prc );
+ rs->sr_text = at->bam_ad->ad_cname.bv_val;
+ goto done;
+
+ } else {
+ backsql_PrintErrors( bi->sql_db_env,
+ dbh, sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = at->bam_ad->ad_cname.bv_val;
+ goto done;
+ }
+ }
+ SQLFreeStmt( sth, SQL_DROP );
+ }
+ break;
+
+ case LDAP_MOD_INCREMENT:
+ Debug( LDAP_DEBUG_TRACE, " backsql_modify_internal(): "
+ "increment not supported yet\n" );
+ if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "SQL-backend error";
+ goto done;
+ }
+ break;
+ }
+ }
+
+done:;
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_modify_internal(): %d%s%s\n",
+ rs->sr_err,
+ rs->sr_text ? ": " : "",
+ rs->sr_text ? rs->sr_text : "" );
+
+ /*
+ * FIXME: should fail in case one change fails?
+ */
+ return rs->sr_err;
+}
+
+static int
+backsql_add_attr(
+ Operation *op,
+ SlapReply *rs,
+ SQLHDBC dbh,
+ backsql_oc_map_rec *oc,
+ Attribute *at,
+ backsql_key_t new_keyval )
+{
+ backsql_info *bi = (backsql_info*)op->o_bd->be_private;
+ backsql_at_map_rec *at_rec = NULL;
+ struct berval *at_val;
+ unsigned long i;
+ RETCODE rc;
+ SQLUSMALLINT currpos;
+ SQLHSTMT sth = SQL_NULL_HSTMT;
+
+ at_rec = backsql_ad2at( oc, at->a_desc );
+
+ if ( at_rec == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add_attr(\"%s\"): "
+ "attribute \"%s\" is not registered "
+ "in objectclass \"%s\"\n",
+ op->ora_e->e_name.bv_val,
+ at->a_desc->ad_cname.bv_val,
+ BACKSQL_OC_NAME( oc ) );
+
+ if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+ rs->sr_text = "operation not permitted "
+ "within namingContext";
+ return rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ return LDAP_SUCCESS;
+ }
+
+ if ( at_rec->bam_add_proc == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add_attr(\"%s\"): "
+ "add procedure is not defined "
+ "for attribute \"%s\" "
+ "of structuralObjectClass \"%s\"\n",
+ op->ora_e->e_name.bv_val,
+ at->a_desc->ad_cname.bv_val,
+ BACKSQL_OC_NAME( oc ) );
+
+ if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
+ rs->sr_text = "operation not permitted "
+ "within namingContext";
+ return rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ return LDAP_SUCCESS;
+ }
+
+ for ( i = 0, at_val = &at->a_vals[ i ];
+ !BER_BVISNULL( at_val );
+ i++, at_val = &at->a_vals[ i ] )
+ {
+ /* procedure return code */
+ int prc = LDAP_SUCCESS;
+ /* first parameter #, parameter order */
+ SQLUSMALLINT pno, po;
+
+ /*
+ * Do not deal with the objectClass that is used
+ * to build the entry
+ */
+ if ( at->a_desc == slap_schema.si_ad_objectClass ) {
+ if ( dn_match( at_val, &oc->bom_oc->soc_cname ) )
+ {
+ continue;
+ }
+ }
+
+ rc = backsql_Prepare( dbh, &sth, at_rec->bam_add_proc, 0 );
+ if ( rc != SQL_SUCCESS ) {
+ rs->sr_text = "SQL-backend error";
+ return rs->sr_err = LDAP_OTHER;
+ }
+
+ if ( BACKSQL_IS_ADD( at_rec->bam_expect_return ) ) {
+ pno = 1;
+ rc = backsql_BindParamInt( sth, 1, SQL_PARAM_OUTPUT, &prc );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_add_attr(): "
+ "error binding output parameter for %s[%lu]\n",
+ at_rec->bam_ad->ad_cname.bv_val, i );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ return rs->sr_err = LDAP_OTHER;
+ }
+
+ } else {
+ pno = 0;
+ }
+
+ po = ( BACKSQL_IS_ADD( at_rec->bam_param_order ) ) > 0;
+ currpos = pno + 1 + po;
+ rc = backsql_BindParamNumID( sth, currpos,
+ SQL_PARAM_INPUT, &new_keyval );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_add_attr(): "
+ "error binding keyval parameter for %s[%lu]\n",
+ at_rec->bam_ad->ad_cname.bv_val, i );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ return rs->sr_err = LDAP_OTHER;
+ }
+
+ currpos = pno + 2 - po;
+
+ /*
+ * check for syntax needed here
+ * maybe need binary bind?
+ */
+
+ rc = backsql_BindParamBerVal( sth, currpos, SQL_PARAM_INPUT, at_val );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_add_attr(): "
+ "error binding value parameter for %s[%lu]\n",
+ at_rec->bam_ad->ad_cname.bv_val, i );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ return rs->sr_err = LDAP_OTHER;
+ }
+
+#ifdef LDAP_DEBUG
+ Debug(LDAP_DEBUG_TRACE,
+ " backsql_add_attr(\"%s\"): " "executing \"%s\" val[%lu], id=" BACKSQL_IDNUMFMT "\n",
+ op->ora_e->e_name.bv_val, at_rec->bam_add_proc,
+ i, new_keyval );
+#endif
+ rc = SQLExecute( sth );
+ if ( rc == SQL_SUCCESS && prc == LDAP_SUCCESS ) {
+ rs->sr_err = LDAP_SUCCESS;
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_add_attr(\"%s\"): "
+ "add_proc execution failed (rc=%d, prc=%d)\n",
+ op->ora_e->e_name.bv_val, rc, prc );
+ if ( prc != LDAP_SUCCESS ) {
+ /* SQL procedure executed fine
+ * but returned an error */
+ rs->sr_err = BACKSQL_SANITIZE_ERROR( prc );
+ rs->sr_text = op->ora_e->e_name.bv_val;
+ SQLFreeStmt( sth, SQL_DROP );
+ return rs->sr_err;
+
+ } else {
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = op->ora_e->e_name.bv_val;
+ SQLFreeStmt( sth, SQL_DROP );
+ return rs->sr_err;
+ }
+ }
+ SQLFreeStmt( sth, SQL_DROP );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int
+backsql_add( Operation *op, SlapReply *rs )
+{
+ backsql_info *bi = (backsql_info*)op->o_bd->be_private;
+ SQLHDBC dbh = SQL_NULL_HDBC;
+ SQLHSTMT sth = SQL_NULL_HSTMT;
+ backsql_key_t new_keyval = 0;
+ RETCODE rc;
+ backsql_oc_map_rec *oc = NULL;
+ backsql_srch_info bsi = { 0 };
+ Entry p = { 0 }, *e = NULL;
+ Attribute *at,
+ *at_objectClass = NULL;
+ ObjectClass *soc = NULL;
+ struct berval scname = BER_BVNULL;
+ struct berval pdn;
+ struct berval realdn = BER_BVNULL;
+ int colnum;
+ slap_mask_t mask;
+
+ char textbuf[ SLAP_TEXT_BUFLEN ];
+ size_t textlen = sizeof( textbuf );
+
+#ifdef BACKSQL_SYNCPROV
+ /*
+ * NOTE: fake successful result to force contextCSN to be bumped up
+ */
+ if ( op->o_sync ) {
+ char buf[ LDAP_PVT_CSNSTR_BUFSIZE ];
+ struct berval csn;
+
+ csn.bv_val = buf;
+ csn.bv_len = sizeof( buf );
+ slap_get_csn( op, &csn, 1 );
+
+ rs->sr_err = LDAP_SUCCESS;
+ send_ldap_result( op, rs );
+
+ slap_graduate_commit_csn( op );
+
+ return 0;
+ }
+#endif /* BACKSQL_SYNCPROV */
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_add(\"%s\")\n",
+ op->ora_e->e_name.bv_val );
+
+ /* check schema */
+ if ( BACKSQL_CHECK_SCHEMA( bi ) ) {
+ char textbuf[ SLAP_TEXT_BUFLEN ] = { '\0' };
+
+ rs->sr_err = entry_schema_check( op, op->ora_e, NULL, 0, 1, NULL,
+ &rs->sr_text, textbuf, sizeof( textbuf ) );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): "
+ "entry failed schema check -- aborting\n",
+ op->ora_e->e_name.bv_val );
+ e = NULL;
+ goto done;
+ }
+ }
+
+ slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, op->ora_e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): "
+ "assertion control failed -- aborting\n",
+ op->ora_e->e_name.bv_val );
+ e = NULL;
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto done;
+ }
+
+ /* search structuralObjectClass */
+ for ( at = op->ora_e->e_attrs; at != NULL; at = at->a_next ) {
+ if ( at->a_desc == slap_schema.si_ad_structuralObjectClass ) {
+ break;
+ }
+ }
+
+ /* there must exist */
+ if ( at == NULL ) {
+ char buf[ SLAP_TEXT_BUFLEN ];
+ const char *text;
+
+ /* search structuralObjectClass */
+ for ( at = op->ora_e->e_attrs; at != NULL; at = at->a_next ) {
+ if ( at->a_desc == slap_schema.si_ad_objectClass ) {
+ break;
+ }
+ }
+
+ if ( at == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): "
+ "no objectClass\n",
+ op->ora_e->e_name.bv_val );
+ rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
+ e = NULL;
+ goto done;
+ }
+
+ rs->sr_err = structural_class( at->a_vals, &soc, NULL,
+ &text, buf, sizeof( buf ), op->o_tmpmemctx );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): "
+ "%s (%d)\n",
+ op->ora_e->e_name.bv_val, text, rs->sr_err );
+ e = NULL;
+ goto done;
+ }
+ scname = soc->soc_cname;
+
+ } else {
+ scname = at->a_vals[0];
+ }
+
+ /* I guess we should play with sub/supertypes to find a suitable oc */
+ oc = backsql_name2oc( bi, &scname );
+
+ if ( oc == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): "
+ "cannot map structuralObjectClass \"%s\" -- aborting\n",
+ op->ora_e->e_name.bv_val,
+ scname.bv_val );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "operation not permitted within namingContext";
+ e = NULL;
+ goto done;
+ }
+
+ if ( oc->bom_create_proc == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): "
+ "create procedure is not defined "
+ "for structuralObjectClass \"%s\" - aborting\n",
+ op->ora_e->e_name.bv_val,
+ scname.bv_val );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "operation not permitted within namingContext";
+ e = NULL;
+ goto done;
+
+ } else if ( BACKSQL_CREATE_NEEDS_SELECT( bi )
+ && oc->bom_create_keyval == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): "
+ "create procedure needs select procedure, "
+ "but none is defined for structuralObjectClass \"%s\" "
+ "- aborting\n",
+ op->ora_e->e_name.bv_val,
+ scname.bv_val );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "operation not permitted within namingContext";
+ e = NULL;
+ goto done;
+ }
+
+ /* check write access */
+ if ( !access_allowed_mask( op, op->ora_e,
+ slap_schema.si_ad_entry,
+ NULL, ACL_WADD, NULL, &mask ) )
+ {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ e = op->ora_e;
+ goto done;
+ }
+
+ rs->sr_err = backsql_get_db_conn( op, &dbh );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): "
+ "could not get connection handle - exiting\n",
+ op->ora_e->e_name.bv_val );
+ rs->sr_text = ( rs->sr_err == LDAP_OTHER )
+ ? "SQL-backend error" : NULL;
+ e = NULL;
+ goto done;
+ }
+
+ /*
+ * Check if entry exists
+ *
+ * NOTE: backsql_api_dn2odbc() is called explicitly because
+ * we need the mucked DN to pass it to the create procedure.
+ */
+ realdn = op->ora_e->e_name;
+ if ( backsql_api_dn2odbc( op, rs, &realdn ) ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): "
+ "backsql_api_dn2odbc(\"%s\") failed\n",
+ op->ora_e->e_name.bv_val, realdn.bv_val );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "SQL-backend error";
+ e = NULL;
+ goto done;
+ }
+
+ rs->sr_err = backsql_dn2id( op, rs, dbh, &realdn, NULL, 0, 0 );
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): "
+ "entry exists\n",
+ op->ora_e->e_name.bv_val );
+ rs->sr_err = LDAP_ALREADY_EXISTS;
+ e = op->ora_e;
+ goto done;
+ }
+
+ /*
+ * Get the parent dn and see if the corresponding entry exists.
+ */
+ if ( be_issuffix( op->o_bd, &op->ora_e->e_nname ) ) {
+ pdn = slap_empty_bv;
+
+ } else {
+ dnParent( &op->ora_e->e_nname, &pdn );
+
+ /*
+ * Get the parent
+ */
+ bsi.bsi_e = &p;
+ rs->sr_err = backsql_init_search( &bsi, &pdn,
+ LDAP_SCOPE_BASE,
+ (time_t)(-1), NULL, dbh, op, rs, slap_anlist_no_attrs,
+ ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
+ "could not retrieve addDN parent "
+ "\"%s\" ID - %s matched=\"%s\"\n",
+ pdn.bv_val,
+ rs->sr_err == LDAP_REFERRAL ? "referral" : "no such entry",
+ rs->sr_matched ? rs->sr_matched : "(null)" );
+ e = &p;
+ goto done;
+ }
+
+ /* check "children" pseudo-attribute access to parent */
+ if ( !access_allowed( op, &p, slap_schema.si_ad_children,
+ NULL, ACL_WADD, NULL ) )
+ {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ e = &p;
+ goto done;
+ }
+ }
+
+ /*
+ * create_proc is executed; if expect_return is set, then
+ * an output parameter is bound, which should contain
+ * the id of the added row; otherwise the procedure
+ * is expected to return the id as the first column of a select
+ */
+ rc = backsql_Prepare( dbh, &sth, oc->bom_create_proc, 0 );
+ if ( rc != SQL_SUCCESS ) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "SQL-backend error";
+ e = NULL;
+ goto done;
+ }
+
+ colnum = 1;
+ if ( BACKSQL_IS_ADD( oc->bom_expect_return ) ) {
+ rc = backsql_BindParamNumID( sth, 1, SQL_PARAM_OUTPUT, &new_keyval );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): "
+ "error binding keyval parameter "
+ "for objectClass %s\n",
+ op->ora_e->e_name.bv_val,
+ oc->bom_oc->soc_cname.bv_val );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ e = NULL;
+ goto done;
+ }
+ colnum++;
+ }
+
+ if ( oc->bom_create_hint ) {
+ at = attr_find( op->ora_e->e_attrs, oc->bom_create_hint );
+ if ( at && at->a_vals ) {
+ backsql_BindParamStr( sth, colnum, SQL_PARAM_INPUT,
+ at->a_vals[0].bv_val,
+ at->a_vals[0].bv_len );
+ Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
+ "create_proc hint: param = '%s'\n",
+ at->a_vals[0].bv_val );
+
+ } else {
+ backsql_BindParamStr( sth, colnum, SQL_PARAM_INPUT,
+ "", 0 );
+ Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
+ "create_proc hint (%s) not available\n",
+ oc->bom_create_hint->ad_cname.bv_val );
+ }
+ colnum++;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): executing \"%s\"\n",
+ op->ora_e->e_name.bv_val, oc->bom_create_proc );
+ rc = SQLExecute( sth );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): "
+ "create_proc execution failed\n",
+ op->ora_e->e_name.bv_val );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc);
+ SQLFreeStmt( sth, SQL_DROP );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "SQL-backend error";
+ e = NULL;
+ goto done;
+ }
+
+ /* FIXME: after SQLExecute(), the row is already inserted
+ * (at least with PostgreSQL and unixODBC); needs investigation */
+
+ if ( !BACKSQL_IS_ADD( oc->bom_expect_return ) ) {
+ SWORD ncols;
+ SQLLEN value_len;
+
+ if ( BACKSQL_CREATE_NEEDS_SELECT( bi ) ) {
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rc = backsql_Prepare( dbh, &sth, oc->bom_create_keyval, 0 );
+ if ( rc != SQL_SUCCESS ) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "SQL-backend error";
+ e = NULL;
+ goto done;
+ }
+
+ rc = SQLExecute( sth );
+ if ( rc != SQL_SUCCESS ) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "SQL-backend error";
+ e = NULL;
+ goto done;
+ }
+ }
+
+ /*
+ * the query to know the id of the inserted entry
+ * must be embedded in the create procedure
+ */
+ rc = SQLNumResultCols( sth, &ncols );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): "
+ "create_proc result evaluation failed\n",
+ op->ora_e->e_name.bv_val );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc);
+ SQLFreeStmt( sth, SQL_DROP );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "SQL-backend error";
+ e = NULL;
+ goto done;
+
+ } else if ( ncols != 1 ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): "
+ "create_proc result is bogus (ncols=%d)\n",
+ op->ora_e->e_name.bv_val, ncols );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc);
+ SQLFreeStmt( sth, SQL_DROP );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "SQL-backend error";
+ e = NULL;
+ goto done;
+ }
+
+#if 0
+ {
+ SQLCHAR colname[ 64 ];
+ SQLSMALLINT name_len, col_type, col_scale, col_null;
+ UDWORD col_prec;
+
+ /*
+ * FIXME: check whether col_type is compatible,
+ * if it can be null and so on ...
+ */
+ rc = SQLDescribeCol( sth, (SQLUSMALLINT)1,
+ &colname[ 0 ],
+ (SQLUINTEGER)( sizeof( colname ) - 1 ),
+ &name_len, &col_type,
+ &col_prec, &col_scale, &col_null );
+ }
+#endif
+
+ rc = SQLBindCol( sth, (SQLUSMALLINT)1, SQL_C_ULONG,
+ (SQLPOINTER)&new_keyval,
+ (SQLINTEGER)sizeof( new_keyval ),
+ &value_len );
+
+ rc = SQLFetch( sth );
+
+ if ( value_len <= 0 ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): "
+ "create_proc result is empty?\n",
+ op->ora_e->e_name.bv_val );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc);
+ SQLFreeStmt( sth, SQL_DROP );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "SQL-backend error";
+ e = NULL;
+ goto done;
+ }
+ }
+
+ SQLFreeStmt( sth, SQL_DROP );
+
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): "
+ "create_proc returned keyval=" BACKSQL_IDNUMFMT "\n",
+ op->ora_e->e_name.bv_val, new_keyval );
+
+ rc = backsql_Prepare( dbh, &sth, bi->sql_insentry_stmt, 0 );
+ if ( rc != SQL_SUCCESS ) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "SQL-backend error";
+ e = NULL;
+ goto done;
+ }
+
+ rc = backsql_BindParamBerVal( sth, 1, SQL_PARAM_INPUT, &realdn );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): "
+ "error binding DN parameter for objectClass %s\n",
+ op->ora_e->e_name.bv_val,
+ oc->bom_oc->soc_cname.bv_val );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ e = NULL;
+ goto done;
+ }
+
+ rc = backsql_BindParamNumID( sth, 2, SQL_PARAM_INPUT, &oc->bom_id );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): "
+ "error binding objectClass ID parameter "
+ "for objectClass %s\n",
+ op->ora_e->e_name.bv_val,
+ oc->bom_oc->soc_cname.bv_val );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ e = NULL;
+ goto done;
+ }
+
+ rc = backsql_BindParamID( sth, 3, SQL_PARAM_INPUT, &bsi.bsi_base_id.eid_id );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): "
+ "error binding parent ID parameter "
+ "for objectClass %s\n",
+ op->ora_e->e_name.bv_val,
+ oc->bom_oc->soc_cname.bv_val );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ e = NULL;
+ goto done;
+ }
+
+ rc = backsql_BindParamNumID( sth, 4, SQL_PARAM_INPUT, &new_keyval );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): "
+ "error binding entry ID parameter "
+ "for objectClass %s\n",
+ op->ora_e->e_name.bv_val,
+ oc->bom_oc->soc_cname.bv_val );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ e = NULL;
+ goto done;
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ " backsql_add(): executing \"%s\" for dn=\"%s\" oc_map_id=" BACKSQL_IDNUMFMT " p_id=" BACKSQL_IDFMT " keyval=" BACKSQL_IDNUMFMT "\n",
+ bi->sql_insentry_stmt, op->ora_e->e_name.bv_val,
+ oc->bom_id, BACKSQL_IDARG(bsi.bsi_base_id.eid_id),
+ new_keyval );
+
+ rc = SQLExecute( sth );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(\"%s\"): "
+ "could not insert ldap_entries record\n",
+ op->ora_e->e_name.bv_val );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
+
+ /*
+ * execute delete_proc to delete data added !!!
+ */
+ SQLFreeStmt( sth, SQL_DROP );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "SQL-backend error";
+ e = NULL;
+ goto done;
+ }
+
+ SQLFreeStmt( sth, SQL_DROP );
+
+ for ( at = op->ora_e->e_attrs; at != NULL; at = at->a_next ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_add(): "
+ "adding attribute \"%s\"\n",
+ at->a_desc->ad_cname.bv_val );
+
+ /*
+ * Skip:
+ * - the first occurrence of objectClass, which is used
+ * to determine how to build the SQL entry (FIXME ?!?)
+ * - operational attributes
+ * - empty attributes (FIXME ?!?)
+ */
+ if ( backsql_attr_skip( at->a_desc, at->a_vals ) ) {
+ continue;
+ }
+
+ if ( at->a_desc == slap_schema.si_ad_objectClass ) {
+ at_objectClass = at;
+ continue;
+ }
+
+ rs->sr_err = backsql_add_attr( op, rs, dbh, oc, at, new_keyval );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ e = op->ora_e;
+ goto done;
+ }
+ }
+
+ if ( at_objectClass ) {
+ rs->sr_err = backsql_add_attr( op, rs, dbh, oc,
+ at_objectClass, new_keyval );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ e = op->ora_e;
+ goto done;
+ }
+ }
+
+done:;
+ /*
+ * Commit only if all operations succeed
+ */
+ if ( sth != SQL_NULL_HSTMT ) {
+ SQLUSMALLINT CompletionType = SQL_ROLLBACK;
+
+ if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop ) {
+ assert( e == NULL );
+ CompletionType = SQL_COMMIT;
+ }
+
+ SQLTransact( SQL_NULL_HENV, dbh, CompletionType );
+ }
+
+ /*
+ * FIXME: NOOP does not work for add -- it works for all
+ * the other operations, and I don't get the reason :(
+ *
+ * hint: there might be some autocommit in Postgres
+ * so that when the unique id of the key table is
+ * automatically increased, there's no rollback.
+ * We might implement a "rollback" procedure consisting
+ * in deleting that row.
+ */
+
+ if ( e != NULL ) {
+ int disclose = 1;
+
+ if ( e == op->ora_e && !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
+ /* mask already collected */
+ disclose = 0;
+
+ } else if ( e == &p && !access_allowed( op, &p,
+ slap_schema.si_ad_entry, NULL,
+ ACL_DISCLOSE, NULL ) )
+ {
+ disclose = 0;
+ }
+
+ if ( disclose == 0 ) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = NULL;
+ rs->sr_matched = NULL;
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+ }
+ }
+
+ if ( op->o_noop && rs->sr_err == LDAP_SUCCESS ) {
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ }
+
+ send_ldap_result( op, rs );
+ slap_graduate_commit_csn( op );
+
+ if ( !BER_BVISNULL( &realdn )
+ && realdn.bv_val != op->ora_e->e_name.bv_val )
+ {
+ ch_free( realdn.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
+ (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
+ }
+
+ if ( !BER_BVISNULL( &p.e_nname ) ) {
+ backsql_entry_clean( op, &p );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_add(\"%s\"): %d \"%s\"\n",
+ op->ora_e->e_name.bv_val,
+ rs->sr_err,
+ rs->sr_text ? rs->sr_text : "" );
+
+ rs->sr_text = NULL;
+ rs->sr_matched = NULL;
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/back-sql/api.c b/servers/slapd/back-sql/api.c
new file mode 100644
index 0000000..7ef1d9f
--- /dev/null
+++ b/servers/slapd/back-sql/api.c
@@ -0,0 +1,211 @@
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 Dmitry Kovalev.
+ * Portions Copyright 2004 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Dmitry Kovalev for inclusion
+ * by OpenLDAP Software. Additional significant contributors include
+ * Pierangelo Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "ac/string.h"
+
+#include "slap.h"
+#include "proto-sql.h"
+
+static backsql_api *backsqlapi;
+
+int
+backsql_api_config( backsql_info *bi, const char *name, int argc, char *argv[] )
+{
+ backsql_api *ba;
+
+ assert( bi != NULL );
+ assert( name != NULL );
+
+ for ( ba = backsqlapi; ba; ba = ba->ba_next ) {
+ if ( strcasecmp( name, ba->ba_name ) == 0 ) {
+ backsql_api *ba2;
+
+ ba2 = ch_malloc( sizeof( backsql_api ) );
+ *ba2 = *ba;
+
+ if ( ba2->ba_config ) {
+ if ( ( *ba2->ba_config )( ba2, argc, argv ) ) {
+ ch_free( ba2 );
+ return 1;
+ }
+ ba2->ba_argc = argc;
+ if ( argc ) {
+ int i;
+ ba2->ba_argv = ch_malloc( argc * sizeof(char *));
+ for ( i=0; i<argc; i++ )
+ ba2->ba_argv[i] = ch_strdup( argv[i] );
+ }
+ }
+
+ ba2->ba_next = bi->sql_api;
+ bi->sql_api = ba2;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int
+backsql_api_destroy( backsql_info *bi )
+{
+ backsql_api *ba;
+
+ assert( bi != NULL );
+
+ ba = bi->sql_api;
+
+ if ( ba == NULL ) {
+ return 0;
+ }
+
+ for ( ; ba; ba = ba->ba_next ) {
+ if ( ba->ba_destroy ) {
+ (void)( *ba->ba_destroy )( ba );
+ }
+ }
+
+ return 0;
+}
+
+int
+backsql_api_register( backsql_api *ba )
+{
+ backsql_api *ba2;
+
+ assert( ba != NULL );
+ assert( ba->ba_private == NULL );
+
+ if ( ba->ba_name == NULL ) {
+ fprintf( stderr, "API module has no name\n" );
+ exit(EXIT_FAILURE);
+ }
+
+ for ( ba2 = backsqlapi; ba2; ba2 = ba2->ba_next ) {
+ if ( strcasecmp( ba->ba_name, ba2->ba_name ) == 0 ) {
+ fprintf( stderr, "API module \"%s\" already defined\n", ba->ba_name );
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ ba->ba_next = backsqlapi;
+ backsqlapi = ba;
+
+ return 0;
+}
+
+int
+backsql_api_dn2odbc( Operation *op, SlapReply *rs, struct berval *dn )
+{
+ backsql_info *bi = (backsql_info *)op->o_bd->be_private;
+ backsql_api *ba;
+ int rc;
+ struct berval bv;
+
+ ba = bi->sql_api;
+
+ if ( ba == NULL ) {
+ return 0;
+ }
+
+ ber_dupbv( &bv, dn );
+
+ for ( ; ba; ba = ba->ba_next ) {
+ if ( ba->ba_dn2odbc ) {
+ /*
+ * The dn2odbc() helper is supposed to rewrite
+ * the contents of bv, freeing the original value
+ * with ch_free() if required and replacing it
+ * with a newly allocated one using ch_malloc()
+ * or companion functions.
+ *
+ * NOTE: it is supposed to __always__ free
+ * the value of bv in case of error, and reset
+ * it with BER_BVZERO() .
+ */
+ rc = ( *ba->ba_dn2odbc )( op, rs, &bv );
+
+ if ( rc ) {
+ /* in case of error, dn2odbc() must cleanup */
+ assert( BER_BVISNULL( &bv ) );
+
+ return rc;
+ }
+ }
+ }
+
+ assert( !BER_BVISNULL( &bv ) );
+
+ *dn = bv;
+
+ return 0;
+}
+
+int
+backsql_api_odbc2dn( Operation *op, SlapReply *rs, struct berval *dn )
+{
+ backsql_info *bi = (backsql_info *)op->o_bd->be_private;
+ backsql_api *ba;
+ int rc;
+ struct berval bv;
+
+ ba = bi->sql_api;
+
+ if ( ba == NULL ) {
+ return 0;
+ }
+
+ ber_dupbv( &bv, dn );
+
+ for ( ; ba; ba = ba->ba_next ) {
+ if ( ba->ba_dn2odbc ) {
+ rc = ( *ba->ba_odbc2dn )( op, rs, &bv );
+ /*
+ * The odbc2dn() helper is supposed to rewrite
+ * the contents of bv, freeing the original value
+ * with ch_free() if required and replacing it
+ * with a newly allocated one using ch_malloc()
+ * or companion functions.
+ *
+ * NOTE: it is supposed to __always__ free
+ * the value of bv in case of error, and reset
+ * it with BER_BVZERO() .
+ */
+ if ( rc ) {
+ /* in case of error, odbc2dn() must cleanup */
+ assert( BER_BVISNULL( &bv ) );
+
+ return rc;
+ }
+ }
+ }
+
+ assert( !BER_BVISNULL( &bv ) );
+
+ *dn = bv;
+
+ return 0;
+}
+
diff --git a/servers/slapd/back-sql/back-sql.h b/servers/slapd/back-sql/back-sql.h
new file mode 100644
index 0000000..556ea6f
--- /dev/null
+++ b/servers/slapd/back-sql/back-sql.h
@@ -0,0 +1,631 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 Dmitry Kovalev.
+ * Portions Copyright 2002 Pierangelo Mararati.
+ * Portions Copyright 2004 Mark Adamson.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Dmitry Kovalev for inclusion
+ * by OpenLDAP Software. Additional significant contributors include
+ * Pierangelo Masarati and Mark Adamson.
+ */
+/*
+ * The following changes have been addressed:
+ *
+ * Enhancements:
+ * - re-styled code for better readability
+ * - upgraded backend API to reflect recent changes
+ * - LDAP schema is checked when loading SQL/LDAP mapping
+ * - AttributeDescription/ObjectClass pointers used for more efficient
+ * mapping lookup
+ * - bervals used where string length is required often
+ * - atomized write operations by committing at the end of each operation
+ * and defaulting connection closure to rollback
+ * - added LDAP access control to write operations
+ * - fully implemented modrdn (with rdn attrs change, deleteoldrdn,
+ * access check, parent/children check and more)
+ * - added parent access control, children control to delete operation
+ * - added structuralObjectClass operational attribute check and
+ * value return on search
+ * - added hasSubordinate operational attribute on demand
+ * - search limits are appropriately enforced
+ * - function backsql_strcat() has been made more efficient
+ * - concat function has been made configurable by means of a pattern
+ * - added config switches:
+ * - fail_if_no_mapping write operations fail if there is no mapping
+ * - has_ldapinfo_dn_ru overrides autodetect
+ * - concat_pattern a string containing two '?' is used
+ * (note that "?||?" should be more portable
+ * than builtin function "CONCAT(?,?)")
+ * - strcast_func cast of string constants in "SELECT DISTINCT
+ * statements (needed by PostgreSQL)
+ * - upper_needs_cast cast the argument of upper when required
+ * (basically when building dn substring queries)
+ * - added noop control
+ * - added values return filter control
+ * - hasSubordinate can be used in search filters (with limitations)
+ * - eliminated oc->name; use oc->oc->soc_cname instead
+ *
+ * Todo:
+ * - add security checks for SQL statements that can be injected (?)
+ * - re-test with previously supported RDBMs
+ * - replace dn_ru and so with normalized dn (no need for upper() and so
+ * in dn match)
+ * - implement a backsql_normalize() function to replace the upper()
+ * conversion routines
+ * - note that subtree deletion, subtree renaming and so could be easily
+ * implemented (rollback and consistency checks are available :)
+ * - implement "lastmod" and other operational stuff (ldap_entries table ?)
+ * - check how to allow multiple operations with one statement, to remove
+ * BACKSQL_REALLOC_STMT from modify.c (a more recent unixODBC lib?)
+ */
+/*
+ * Improvements submitted by (ITS#3432)
+ *
+ * 1. id_query.patch applied (with changes)
+ * 2. shortcut.patch applied (reworked)
+ * 3. create_hint.patch applied
+ * 4. count_query.patch applied (reworked)
+ * 5. returncodes.patch applied (with sanity checks)
+ * 6. connpool.patch under evaluation
+ * 7. modoc.patch under evaluation (requires
+ * manageDSAit and "manage"
+ * access privileges)
+ * 8. miscfixes.patch applied (reworked; other
+ * operations need to load the
+ * entire entry for ACL purposes;
+ * see ITS#3480, now fixed)
+ *
+ * original description:
+
+ Changes that were made to the SQL backend.
+
+The patches were made against 2.2.18 and can be applied individually,
+but would best be applied in the numerical order of the file names.
+A synopsis of each patch is given here:
+
+
+1. Added an option to set SQL query for the "id_query" operation.
+
+2. Added an option to the SQL backend called "use_subtree_shortcut".
+When a search is performed, the SQL query includes a WHERE clause
+which says the DN must be "LIKE %<searchbase>". The LIKE operation
+can be slow in an RDBM. This shortcut option says that if the
+searchbase of the LDAP search is the root DN of the SQL backend,
+and thus all objects will match the LIKE operator, do not include
+the "LIKE %<searchbase>" clause in the SQL query (it is replaced
+instead by the always true "1=1" clause to keep the "AND"'s
+working correctly). This option is off by default, and should be
+turned on only if all objects to be found in the RDBM are under the
+same root DN. Multiple backends working within the same RDBM table
+space would encounter problems. LDAP searches whose searchbase are
+not at the root DN will bypass this shortcut and employ the LIKE
+clause.
+
+3. Added a "create_hint" column to ldap_oc_mappings table. Allows
+taking the value of an attr named in "create_hint" and passing it to
+the create_proc procedure. This is necessary for when an objectClass's
+table is partition indexed by some indexing column and thus the value
+in that indexing column cannot change after the row is created. The
+value for the indexed column is passed into the create_proc, which
+uses it to fill in the indexed column as the new row is created.
+
+4. When loading the values of an attribute, the count(*) of the number
+of values is fetched first and memory is allocated for the array of
+values and normalized values. The old system of loading the values one
+by one and running realloc() on the array of values and normalized
+values each time was badly fragmenting memory. The array of values and
+normalized values would be side by side in memory, and realloc()'ing
+them over and over would force them to leapfrog each other through all
+of available memory. Attrs with a large number of values could not be
+loaded without crashing the slapd daemon.
+
+5. Added code to interpret the value returned by stored procedures
+which have expect_return set. Returned value is interpreted as an LDAP
+return code. This allows the distinction between the SQL failing to
+execute and the SQL running to completion and returning an error code
+which can indicate a policy violation.
+
+6. Added RDBM connection pooling. Once an operation is finished the
+connection to the RDBM is returned to a pool rather than closing.
+Allows the next operation to skip the initialization and authentication
+phases of contacting the RDBM. Also, if licensing with ODBC places
+a limit on the number of connections, an LDAP thread can block waiting
+for another thread to finish, so that no LDAP errors are returned
+for having more LDAP connections than allowed RDBM connections. An
+RDBM connection which receives an SQL error is marked as "tainted"
+so that it will be closed rather than returned to the pool.
+ Also, RDBM connections must be bound to a given LDAP connection AND
+operation number, and NOT just the connection number. Asynchronous
+LDAP clients can have multiple simultaneous LDAP operations which
+should not share the same RDBM connection. A given LDAP operation can
+even make multiple SQL operations (e.g. a BIND operation which
+requires SASL to perform an LDAP search to convert the SASL ID to an
+LDAP DN), so each RDBM connection now has a refcount that must reach
+zero before the connection is returned to the free pool.
+
+7. Added ability to change the objectClass of an object. Required
+considerable work to copy all attributes out of old object and into
+new object. Does a schema check before proceeding. Creates a new
+object, fills it in, deletes the old object, then changes the
+oc_map_id and keyval of the entry in the "ldap_entries" table.
+
+8. Generic fixes. Includes initializing pointers before they
+get used in error branch cases, pointer checks before dereferencing,
+resetting a return code to success after a COMPARE op, sealing
+memory leaks, and in search.c, changing some of the "1=1" tests to
+"2=2", "3=3", etc so that when reading slapd trace output, the
+location in the source code where the x=x test was added to the SQL
+can be easily distinguished.
+ */
+
+#ifndef __BACKSQL_H__
+#define __BACKSQL_H__
+
+/* former sql-types.h */
+#include <sql.h>
+#include <sqlext.h>
+
+typedef struct {
+ SWORD ncols;
+ BerVarray col_names;
+ UDWORD *col_prec;
+ SQLSMALLINT *col_type;
+ char **cols;
+ SQLLEN *value_len;
+} BACKSQL_ROW_NTS;
+
+/*
+ * Better use the standard length of 8192 (as of slap.h)?
+ *
+ * NOTE: must be consistent with definition in ldap_entries table
+ */
+/* #define BACKSQL_MAX_DN_LEN SLAP_LDAPDN_MAXLEN */
+#define BACKSQL_MAX_DN_LEN 255
+
+/*
+ * define to enable very extensive trace logging (debug only)
+ */
+#undef BACKSQL_TRACE
+
+/*
+ * define if using MS SQL and workaround needed (see sql-wrap.c)
+ */
+#undef BACKSQL_MSSQL_WORKAROUND
+
+/*
+ * define to enable values counting for attributes
+ */
+#define BACKSQL_COUNTQUERY
+
+/*
+ * define to enable prettification/validation of values
+ */
+#define BACKSQL_PRETTY_VALIDATE
+
+/*
+ * define to enable varchars as unique keys in user tables
+ *
+ * by default integers are used (and recommended)
+ * for performances. Integers are used anyway in back-sql
+ * related tables.
+ */
+#undef BACKSQL_ARBITRARY_KEY
+
+/*
+ * type used for keys
+ */
+#if defined(HAVE_LONG_LONG) && defined(SQL_C_UBIGINT) && \
+ ( defined(HAVE_STRTOULL) || defined(HAVE_STRTOUQ) )
+typedef unsigned long long backsql_key_t;
+#define BACKSQL_C_NUMID SQL_C_UBIGINT
+#define BACKSQL_IDNUMFMT "%llu"
+#define BACKSQL_STR2ID lutil_atoullx
+#else /* ! HAVE_LONG_LONG || ! SQL_C_UBIGINT */
+typedef unsigned long backsql_key_t;
+#define BACKSQL_C_NUMID SQL_C_ULONG
+#define BACKSQL_IDNUMFMT "%lu"
+#define BACKSQL_STR2ID lutil_atoulx
+#endif /* ! HAVE_LONG_LONG */
+
+/*
+ * define to enable support for syncprov overlay
+ */
+#define BACKSQL_SYNCPROV
+
+/*
+ * define to the appropriate aliasing string
+ *
+ * some RDBMSes tolerate (or require) that " AS " is not used
+ * when aliasing tables/columns
+ */
+#define BACKSQL_ALIASING "AS "
+/* #define BACKSQL_ALIASING "" */
+
+/*
+ * define to the appropriate quoting char
+ *
+ * some RDBMSes tolerate/require that the aliases be enclosed
+ * in quotes. This is especially true for those that do not
+ * allow keywords used as aliases.
+ */
+#define BACKSQL_ALIASING_QUOTE ""
+/* #define BACKSQL_ALIASING_QUOTE "\"" */
+/* #define BACKSQL_ALIASING_QUOTE "'" */
+
+/*
+ * API
+ *
+ * a simple mechanism to allow DN mucking between the LDAP
+ * and the stored string representation.
+ */
+typedef struct backsql_api {
+ char *ba_name;
+ int (*ba_config)( struct backsql_api *self, int argc, char *argv[] );
+ int (*ba_destroy)( struct backsql_api *self );
+
+ int (*ba_dn2odbc)( Operation *op, SlapReply *rs, struct berval *dn );
+ int (*ba_odbc2dn)( Operation *op, SlapReply *rs, struct berval *dn );
+
+ void *ba_private;
+ struct backsql_api *ba_next;
+ char **ba_argv;
+ int ba_argc;
+} backsql_api;
+
+/*
+ * "structural" objectClass mapping structure
+ */
+typedef struct backsql_oc_map_rec {
+ /*
+ * Structure of corresponding LDAP objectClass definition
+ */
+ ObjectClass *bom_oc;
+#define BACKSQL_OC_NAME(ocmap) ((ocmap)->bom_oc->soc_cname.bv_val)
+
+ struct berval bom_keytbl;
+ struct berval bom_keycol;
+ /* expected to return keyval of newly created entry */
+ char *bom_create_proc;
+ /* in case create_proc does not return the keyval of the newly
+ * created row */
+ char *bom_create_keyval;
+ /* supposed to expect keyval as parameter and delete
+ * all the attributes as well */
+ char *bom_delete_proc;
+ /* flags whether delete_proc is a function (whether back-sql
+ * should bind first parameter as output for return code) */
+ int bom_expect_return;
+ backsql_key_t bom_id;
+ Avlnode *bom_attrs;
+ AttributeDescription *bom_create_hint;
+} backsql_oc_map_rec;
+
+/*
+ * attributeType mapping structure
+ */
+typedef struct backsql_at_map_rec {
+ /* Description of corresponding LDAP attribute type */
+ AttributeDescription *bam_ad;
+ AttributeDescription *bam_true_ad;
+ /* ObjectClass if bam_ad is objectClass */
+ ObjectClass *bam_oc;
+
+ struct berval bam_from_tbls;
+ struct berval bam_join_where;
+ struct berval bam_sel_expr;
+
+ /* TimesTen, or, if a uppercase function is defined,
+ * an uppercased version of bam_sel_expr */
+ struct berval bam_sel_expr_u;
+
+ /* supposed to expect 2 binded values: entry keyval
+ * and attr. value to add, like "add_name(?,?,?)" */
+ char *bam_add_proc;
+ /* supposed to expect 2 binded values: entry keyval
+ * and attr. value to delete */
+ char *bam_delete_proc;
+ /* for optimization purposes attribute load query
+ * is preconstructed from parts on schemamap load time */
+ char *bam_query;
+#ifdef BACKSQL_COUNTQUERY
+ char *bam_countquery;
+#endif /* BACKSQL_COUNTQUERY */
+ /* following flags are bitmasks (first bit used for add_proc,
+ * second - for delete_proc) */
+ /* order of parameters for procedures above;
+ * 1 means "data then keyval", 0 means "keyval then data" */
+ int bam_param_order;
+ /* flags whether one or more of procedures is a function
+ * (whether back-sql should bind first parameter as output
+ * for return code) */
+ int bam_expect_return;
+
+ /* next mapping for attribute */
+ struct backsql_at_map_rec *bam_next;
+} backsql_at_map_rec;
+
+#define BACKSQL_AT_MAP_REC_INIT { NULL, NULL, BER_BVC(""), BER_BVC(""), BER_BVNULL, BER_BVNULL, NULL, NULL, NULL, 0, 0, NULL }
+
+/* define to uppercase filters only if the matching rule requires it
+ * (currently broken) */
+/* #define BACKSQL_UPPERCASE_FILTER */
+
+#define BACKSQL_AT_CANUPPERCASE(at) ( !BER_BVISNULL( &(at)->bam_sel_expr_u ) )
+
+/* defines to support bitmasks above */
+#define BACKSQL_ADD 0x1
+#define BACKSQL_DEL 0x2
+
+#define BACKSQL_IS_ADD(x) ( ( BACKSQL_ADD & (x) ) == BACKSQL_ADD )
+#define BACKSQL_IS_DEL(x) ( ( BACKSQL_DEL & (x) ) == BACKSQL_DEL )
+
+#define BACKSQL_NCMP(v1,v2) ber_bvcmp((v1),(v2))
+
+#define BACKSQL_CONCAT
+/*
+ * berbuf structure: a berval with a buffer size associated
+ */
+typedef struct berbuf {
+ struct berval bb_val;
+ ber_len_t bb_len;
+} BerBuffer;
+
+#define BB_NULL { BER_BVNULL, 0 }
+
+/*
+ * Entry ID structure
+ */
+typedef struct backsql_entryID {
+ /* #define BACKSQL_ARBITRARY_KEY to allow a non-numeric key.
+ * It is required by some special applications that use
+ * strings as keys for the main table.
+ * In this case, #define BACKSQL_MAX_KEY_LEN consistently
+ * with the key size definition */
+#ifdef BACKSQL_ARBITRARY_KEY
+ struct berval eid_id;
+ struct berval eid_keyval;
+#define BACKSQL_MAX_KEY_LEN 64
+#else /* ! BACKSQL_ARBITRARY_KEY */
+ /* The original numeric key is maintained as default. */
+ backsql_key_t eid_id;
+ backsql_key_t eid_keyval;
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+
+ backsql_key_t eid_oc_id;
+ backsql_oc_map_rec *eid_oc;
+ struct berval eid_dn;
+ struct berval eid_ndn;
+ struct backsql_entryID *eid_next;
+} backsql_entryID;
+
+#ifdef BACKSQL_ARBITRARY_KEY
+#define BACKSQL_ENTRYID_INIT { BER_BVNULL, BER_BVNULL, 0, NULL, BER_BVNULL, BER_BVNULL, NULL }
+#else /* ! BACKSQL_ARBITRARY_KEY */
+#define BACKSQL_ENTRYID_INIT { 0, 0, 0, NULL, BER_BVNULL, BER_BVNULL, NULL }
+#endif /* BACKSQL_ARBITRARY_KEY */
+
+/* the function must collect the entry associated to nbase */
+#define BACKSQL_ISF_GET_ID 0x1U
+#define BACKSQL_ISF_GET_ENTRY ( 0x2U | BACKSQL_ISF_GET_ID )
+#define BACKSQL_ISF_GET_OC ( 0x4U | BACKSQL_ISF_GET_ID )
+#define BACKSQL_ISF_MATCHED 0x8U
+#define BACKSQL_IS_GET_ID(f) \
+ ( ( (f) & BACKSQL_ISF_GET_ID ) == BACKSQL_ISF_GET_ID )
+#define BACKSQL_IS_GET_ENTRY(f) \
+ ( ( (f) & BACKSQL_ISF_GET_ENTRY ) == BACKSQL_ISF_GET_ENTRY )
+#define BACKSQL_IS_GET_OC(f) \
+ ( ( (f) & BACKSQL_ISF_GET_OC ) == BACKSQL_ISF_GET_OC )
+#define BACKSQL_IS_MATCHED(f) \
+ ( ( (f) & BACKSQL_ISF_MATCHED ) == BACKSQL_ISF_MATCHED )
+typedef struct backsql_srch_info {
+ Operation *bsi_op;
+ SlapReply *bsi_rs;
+
+ unsigned bsi_flags;
+#define BSQL_SF_NONE 0x0000U
+#define BSQL_SF_ALL_USER 0x0001U
+#define BSQL_SF_ALL_OPER 0x0002U
+#define BSQL_SF_ALL_ATTRS (BSQL_SF_ALL_USER|BSQL_SF_ALL_OPER)
+#define BSQL_SF_FILTER_HASSUBORDINATE 0x0010U
+#define BSQL_SF_FILTER_ENTRYUUID 0x0020U
+#define BSQL_SF_FILTER_ENTRYCSN 0x0040U
+#define BSQL_SF_RETURN_ENTRYUUID (BSQL_SF_FILTER_ENTRYUUID << 8)
+#define BSQL_ISF(bsi, f) ( ( (bsi)->bsi_flags & f ) == f )
+#define BSQL_ISF_ALL_USER(bsi) BSQL_ISF(bsi, BSQL_SF_ALL_USER)
+#define BSQL_ISF_ALL_OPER(bsi) BSQL_ISF(bsi, BSQL_SF_ALL_OPER)
+#define BSQL_ISF_ALL_ATTRS(bsi) BSQL_ISF(bsi, BSQL_SF_ALL_ATTRS)
+
+ struct berval *bsi_base_ndn;
+ int bsi_use_subtree_shortcut;
+ backsql_entryID bsi_base_id;
+ int bsi_scope;
+/* BACKSQL_SCOPE_BASE_LIKE can be set by API in ors_scope
+ * whenever the search base DN contains chars that cannot
+ * be mapped into the charset used in the RDBMS; so they're
+ * turned into '%' and an approximate ('LIKE') condition
+ * is used */
+#define BACKSQL_SCOPE_BASE_LIKE ( LDAP_SCOPE_BASE | 0x1000 )
+ Filter *bsi_filter;
+ time_t bsi_stoptime;
+
+ backsql_entryID *bsi_id_list,
+ **bsi_id_listtail,
+ *bsi_c_eid;
+ int bsi_n_candidates;
+ int bsi_status;
+
+ backsql_oc_map_rec *bsi_oc;
+ struct berbuf bsi_sel,
+ bsi_from,
+ bsi_join_where,
+ bsi_flt_where;
+ ObjectClass *bsi_filter_oc;
+ SQLHDBC bsi_dbh;
+ AttributeName *bsi_attrs;
+
+ Entry *bsi_e;
+} backsql_srch_info;
+
+/*
+ * Backend private data structure
+ */
+typedef struct backsql_info {
+ char *sql_dbhost;
+ int sql_dbport;
+ char *sql_dbuser;
+ char *sql_dbpasswd;
+ char *sql_dbname;
+
+ /*
+ * SQL condition for subtree searches differs in syntax:
+ * "LIKE CONCAT('%',?)" or "LIKE '%'+?" or "LIKE '%'||?"
+ * or smtg else
+ */
+ struct berval sql_subtree_cond;
+ struct berval sql_children_cond;
+ struct berval sql_dn_match_cond;
+ char *sql_oc_query;
+ char *sql_at_query;
+ char *sql_insentry_stmt;
+ char *sql_delentry_stmt;
+ char *sql_renentry_stmt;
+ char *sql_delobjclasses_stmt;
+ char *sql_id_query;
+ char *sql_has_children_query;
+ char *sql_list_children_query;
+
+ MatchingRule *sql_caseIgnoreMatch;
+ MatchingRule *sql_telephoneNumberMatch;
+
+ struct berval sql_upper_func;
+ struct berval sql_upper_func_open;
+ struct berval sql_upper_func_close;
+ struct berval sql_strcast_func;
+ BerVarray sql_concat_func;
+ char *sql_concat_patt;
+
+ struct berval sql_aliasing;
+ struct berval sql_aliasing_quote;
+ struct berval sql_dn_oc_aliasing;
+
+ AttributeName *sql_anlist;
+
+ unsigned int sql_flags;
+#define BSQLF_SCHEMA_LOADED 0x0001
+#define BSQLF_UPPER_NEEDS_CAST 0x0002
+#define BSQLF_CREATE_NEEDS_SELECT 0x0004
+#define BSQLF_FAIL_IF_NO_MAPPING 0x0008
+#define BSQLF_HAS_LDAPINFO_DN_RU 0x0010
+#define BSQLF_DONTCHECK_LDAPINFO_DN_RU 0x0020
+#define BSQLF_USE_REVERSE_DN 0x0040
+#define BSQLF_ALLOW_ORPHANS 0x0080
+#define BSQLF_USE_SUBTREE_SHORTCUT 0x0100
+#define BSQLF_FETCH_ALL_USERATTRS 0x0200
+#define BSQLF_FETCH_ALL_OPATTRS 0x0400
+#define BSQLF_FETCH_ALL_ATTRS (BSQLF_FETCH_ALL_USERATTRS|BSQLF_FETCH_ALL_OPATTRS)
+#define BSQLF_CHECK_SCHEMA 0x0800
+#define BSQLF_AUTOCOMMIT_ON 0x1000
+
+#define BACKSQL_ISF(si, f) \
+ (((si)->sql_flags & f) == f)
+
+#define BACKSQL_SCHEMA_LOADED(si) \
+ BACKSQL_ISF(si, BSQLF_SCHEMA_LOADED)
+#define BACKSQL_UPPER_NEEDS_CAST(si) \
+ BACKSQL_ISF(si, BSQLF_UPPER_NEEDS_CAST)
+#define BACKSQL_CREATE_NEEDS_SELECT(si) \
+ BACKSQL_ISF(si, BSQLF_CREATE_NEEDS_SELECT)
+#define BACKSQL_FAIL_IF_NO_MAPPING(si) \
+ BACKSQL_ISF(si, BSQLF_FAIL_IF_NO_MAPPING)
+#define BACKSQL_HAS_LDAPINFO_DN_RU(si) \
+ BACKSQL_ISF(si, BSQLF_HAS_LDAPINFO_DN_RU)
+#define BACKSQL_DONTCHECK_LDAPINFO_DN_RU(si) \
+ BACKSQL_ISF(si, BSQLF_DONTCHECK_LDAPINFO_DN_RU)
+#define BACKSQL_USE_REVERSE_DN(si) \
+ BACKSQL_ISF(si, BSQLF_USE_REVERSE_DN)
+#define BACKSQL_CANUPPERCASE(si) \
+ (!BER_BVISNULL( &(si)->sql_upper_func ))
+#define BACKSQL_ALLOW_ORPHANS(si) \
+ BACKSQL_ISF(si, BSQLF_ALLOW_ORPHANS)
+#define BACKSQL_USE_SUBTREE_SHORTCUT(si) \
+ BACKSQL_ISF(si, BSQLF_USE_SUBTREE_SHORTCUT)
+#define BACKSQL_FETCH_ALL_USERATTRS(si) \
+ BACKSQL_ISF(si, BSQLF_FETCH_ALL_USERATTRS)
+#define BACKSQL_FETCH_ALL_OPATTRS(si) \
+ BACKSQL_ISF(si, BSQLF_FETCH_ALL_OPATTRS)
+#define BACKSQL_FETCH_ALL_ATTRS(si) \
+ BACKSQL_ISF(si, BSQLF_FETCH_ALL_ATTRS)
+#define BACKSQL_CHECK_SCHEMA(si) \
+ BACKSQL_ISF(si, BSQLF_CHECK_SCHEMA)
+#define BACKSQL_AUTOCOMMIT_ON(si) \
+ BACKSQL_ISF(si, BSQLF_AUTOCOMMIT_ON)
+
+ Entry *sql_baseObject;
+ char *sql_base_ob_file;
+#ifdef BACKSQL_ARBITRARY_KEY
+#define BACKSQL_BASEOBJECT_IDSTR "baseObject"
+#define BACKSQL_BASEOBJECT_KEYVAL BACKSQL_BASEOBJECT_IDSTR
+#define BACKSQL_IS_BASEOBJECT_ID(id) (bvmatch((id), &backsql_baseObject_bv))
+#else /* ! BACKSQL_ARBITRARY_KEY */
+#define BACKSQL_BASEOBJECT_ID 0
+#define BACKSQL_BASEOBJECT_IDSTR LDAP_XSTRING(BACKSQL_BASEOBJECT_ID)
+#define BACKSQL_BASEOBJECT_KEYVAL 0
+#define BACKSQL_IS_BASEOBJECT_ID(id) (*(id) == BACKSQL_BASEOBJECT_ID)
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+#define BACKSQL_BASEOBJECT_OC 0
+
+ Avlnode *sql_db_conns;
+ SQLHDBC sql_dbh;
+ ldap_pvt_thread_mutex_t sql_dbconn_mutex;
+ Avlnode *sql_oc_by_oc;
+ Avlnode *sql_oc_by_id;
+ ldap_pvt_thread_mutex_t sql_schema_mutex;
+ SQLHENV sql_db_env;
+
+ backsql_api *sql_api;
+} backsql_info;
+
+#define BACKSQL_SUCCESS( rc ) \
+ ( (rc) == SQL_SUCCESS || (rc) == SQL_SUCCESS_WITH_INFO )
+
+#define BACKSQL_AVL_STOP 0
+#define BACKSQL_AVL_CONTINUE 1
+
+/* see ldap.h for the meaning of the macros and of the values */
+#define BACKSQL_LEGAL_ERROR( rc ) \
+ ( LDAP_RANGE( (rc), 0x00, 0x0e ) \
+ || LDAP_ATTR_ERROR( (rc) ) \
+ || LDAP_NAME_ERROR( (rc) ) \
+ || LDAP_SECURITY_ERROR( (rc) ) \
+ || LDAP_SERVICE_ERROR( (rc) ) \
+ || LDAP_UPDATE_ERROR( (rc) ) )
+#define BACKSQL_SANITIZE_ERROR( rc ) \
+ ( BACKSQL_LEGAL_ERROR( (rc) ) ? (rc) : LDAP_OTHER )
+
+#define BACKSQL_IS_BINARY(ct) \
+ ( (ct) == SQL_BINARY \
+ || (ct) == SQL_VARBINARY \
+ || (ct) == SQL_LONGVARBINARY)
+
+#ifdef BACKSQL_ARBITRARY_KEY
+#define BACKSQL_IDFMT "%s"
+#define BACKSQL_IDARG(arg) ((arg).bv_val)
+#else /* ! BACKSQL_ARBITRARY_KEY */
+#define BACKSQL_IDFMT BACKSQL_IDNUMFMT
+#define BACKSQL_IDARG(arg) (arg)
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+
+#endif /* __BACKSQL_H__ */
+
diff --git a/servers/slapd/back-sql/bind.c b/servers/slapd/back-sql/bind.c
new file mode 100644
index 0000000..ac78fc8
--- /dev/null
+++ b/servers/slapd/back-sql/bind.c
@@ -0,0 +1,115 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 Dmitry Kovalev.
+ * Portions Copyright 2002 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Dmitry Kovalev for inclusion
+ * by OpenLDAP Software. Additional significant contributors include
+ * Pierangelo Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "slap.h"
+#include "proto-sql.h"
+
+int
+backsql_bind( Operation *op, SlapReply *rs )
+{
+ SQLHDBC dbh = SQL_NULL_HDBC;
+ Entry e = { 0 };
+ Attribute *a;
+ backsql_srch_info bsi = { 0 };
+ AttributeName anlist[2];
+ int rc;
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_bind()\n" );
+
+ switch ( be_rootdn_bind( op, rs ) ) {
+ case SLAP_CB_CONTINUE:
+ break;
+
+ default:
+ /* in case of success, front end will send result;
+ * otherwise, be_rootdn_bind() did */
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_bind(%d)\n",
+ rs->sr_err );
+ return rs->sr_err;
+ }
+
+ rs->sr_err = backsql_get_db_conn( op, &dbh );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_bind(): "
+ "could not get connection handle - exiting\n" );
+
+ rs->sr_text = ( rs->sr_err == LDAP_OTHER )
+ ? "SQL-backend error" : NULL;
+ goto error_return;
+ }
+
+ anlist[0].an_name = slap_schema.si_ad_userPassword->ad_cname;
+ anlist[0].an_desc = slap_schema.si_ad_userPassword;
+ anlist[1].an_name.bv_val = NULL;
+
+ bsi.bsi_e = &e;
+ rc = backsql_init_search( &bsi, &op->o_req_ndn, LDAP_SCOPE_BASE,
+ (time_t)(-1), NULL, dbh, op, rs, anlist,
+ BACKSQL_ISF_GET_ENTRY );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_bind(): "
+ "could not retrieve bindDN ID - no such entry\n" );
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto error_return;
+ }
+
+ a = attr_find( e.e_attrs, slap_schema.si_ad_userPassword );
+ if ( a == NULL ) {
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto error_return;
+ }
+
+ if ( slap_passwd_check( op, &e, a, &op->oq_bind.rb_cred,
+ &rs->sr_text ) != 0 )
+ {
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto error_return;
+ }
+
+error_return:;
+ if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
+ (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
+ }
+
+ if ( !BER_BVISNULL( &e.e_nname ) ) {
+ backsql_entry_clean( op, &e );
+ }
+
+ if ( bsi.bsi_attrs != NULL ) {
+ op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
+ }
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ }
+
+ SQLTransact( SQL_NULL_HENV, dbh, SQL_ROLLBACK );
+ Debug( LDAP_DEBUG_TRACE,"<==backsql_bind()\n" );
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/back-sql/compare.c b/servers/slapd/back-sql/compare.c
new file mode 100644
index 0000000..d457085
--- /dev/null
+++ b/servers/slapd/back-sql/compare.c
@@ -0,0 +1,194 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 Dmitry Kovalev.
+ * Portions Copyright 2002 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Dmitry Kovalev for inclusion
+ * by OpenLDAP Software. Additional significant contributors include
+ * Pierangelo Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "slap.h"
+#include "proto-sql.h"
+
+int
+backsql_compare( Operation *op, SlapReply *rs )
+{
+ SQLHDBC dbh = SQL_NULL_HDBC;
+ Entry e = { 0 };
+ Attribute *a = NULL;
+ backsql_srch_info bsi = { 0 };
+ int rc;
+ int manageDSAit = get_manageDSAit( op );
+ AttributeName anlist[2];
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_compare()\n" );
+
+ rs->sr_err = backsql_get_db_conn( op, &dbh );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_compare(): "
+ "could not get connection handle - exiting\n" );
+
+ rs->sr_text = ( rs->sr_err == LDAP_OTHER )
+ ? "SQL-backend error" : NULL;
+ goto return_results;
+ }
+
+ anlist[ 0 ].an_name = op->oq_compare.rs_ava->aa_desc->ad_cname;
+ anlist[ 0 ].an_desc = op->oq_compare.rs_ava->aa_desc;
+ BER_BVZERO( &anlist[ 1 ].an_name );
+
+ /*
+ * Get the entry
+ */
+ bsi.bsi_e = &e;
+ rc = backsql_init_search( &bsi, &op->o_req_ndn, LDAP_SCOPE_BASE,
+ (time_t)(-1), NULL, dbh, op, rs, anlist,
+ ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) );
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ break;
+
+ case LDAP_REFERRAL:
+ if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
+ dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) )
+ {
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ rs->sr_matched = NULL;
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+ break;
+ }
+ /* fallthru */
+
+ default:
+ Debug( LDAP_DEBUG_TRACE, "backsql_compare(): "
+ "could not retrieve compareDN ID - no such entry\n" );
+ goto return_results;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, &e, get_assertion( op ) )
+ != LDAP_COMPARE_TRUE ) )
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+ if ( is_at_operational( op->oq_compare.rs_ava->aa_desc->ad_type ) ) {
+ SlapReply nrs = { REP_SEARCH };
+ Attribute **ap;
+
+ for ( ap = &e.e_attrs; *ap; ap = &(*ap)->a_next )
+ ;
+
+ nrs.sr_attrs = anlist;
+ nrs.sr_entry = &e;
+ nrs.sr_attr_flags = SLAP_OPATTRS_NO;
+ nrs.sr_operational_attrs = NULL;
+
+ rs->sr_err = backsql_operational( op, &nrs );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto return_results;
+ }
+
+ *ap = nrs.sr_operational_attrs;
+ }
+
+ if ( ! access_allowed( op, &e, op->oq_compare.rs_ava->aa_desc,
+ &op->oq_compare.rs_ava->aa_value,
+ ACL_COMPARE, NULL ) )
+ {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto return_results;
+ }
+
+ rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
+ for ( a = attrs_find( e.e_attrs, op->oq_compare.rs_ava->aa_desc );
+ a != NULL;
+ a = attrs_find( a->a_next, op->oq_compare.rs_ava->aa_desc ) )
+ {
+ rs->sr_err = LDAP_COMPARE_FALSE;
+ if ( attr_valfind( a,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ &op->oq_compare.rs_ava->aa_value, NULL,
+ op->o_tmpmemctx ) == 0 )
+ {
+ rs->sr_err = LDAP_COMPARE_TRUE;
+ break;
+ }
+ }
+
+return_results:;
+ switch ( rs->sr_err ) {
+ case LDAP_COMPARE_TRUE:
+ case LDAP_COMPARE_FALSE:
+ break;
+
+ default:
+ if ( !BER_BVISNULL( &e.e_nname ) &&
+ ! access_allowed( op, &e,
+ slap_schema.si_ad_entry, NULL,
+ ACL_DISCLOSE, NULL ) )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = NULL;
+ }
+ break;
+ }
+
+ send_ldap_result( op, rs );
+
+ if ( rs->sr_matched ) {
+ rs->sr_matched = NULL;
+ }
+
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+
+ if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
+ (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
+ }
+
+ if ( !BER_BVISNULL( &e.e_nname ) ) {
+ backsql_entry_clean( op, &e );
+ }
+
+ if ( bsi.bsi_attrs != NULL ) {
+ op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
+ }
+
+ Debug(LDAP_DEBUG_TRACE,"<==backsql_compare()\n" );
+ switch ( rs->sr_err ) {
+ case LDAP_COMPARE_TRUE:
+ case LDAP_COMPARE_FALSE:
+ return LDAP_SUCCESS;
+
+ default:
+ return rs->sr_err;
+ }
+}
+
diff --git a/servers/slapd/back-sql/config.c b/servers/slapd/back-sql/config.c
new file mode 100644
index 0000000..51a6be3
--- /dev/null
+++ b/servers/slapd/back-sql/config.c
@@ -0,0 +1,778 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 Dmitry Kovalev.
+ * Portions Copyright 2002 Pierangelo Masarati.
+ * Portions Copyright 2004 Mark Adamson.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Dmitry Kovalev for inclusion
+ * by OpenLDAP Software. Additional significant contributors include
+ * Pierangelo Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include "ac/string.h"
+#include <sys/types.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "ldif.h"
+#include "lutil.h"
+#include "proto-sql.h"
+
+static int
+create_baseObject(
+ BackendDB *be,
+ const char *fname,
+ int lineno );
+
+static int
+read_baseObject(
+ BackendDB *be,
+ const char *fname );
+
+static ConfigDriver sql_cf_gen;
+
+enum {
+ BSQL_CONCAT_PATT = 1,
+ BSQL_CREATE_NEEDS_SEL,
+ BSQL_UPPER_NEEDS_CAST,
+ BSQL_HAS_LDAPINFO_DN_RU,
+ BSQL_FAIL_IF_NO_MAPPING,
+ BSQL_ALLOW_ORPHANS,
+ BSQL_BASE_OBJECT,
+ BSQL_LAYER,
+ BSQL_SUBTREE_SHORTCUT,
+ BSQL_FETCH_ALL_ATTRS,
+ BSQL_FETCH_ATTRS,
+ BSQL_CHECK_SCHEMA,
+ BSQL_ALIASING_KEYWORD,
+ BSQL_AUTOCOMMIT
+};
+
+static ConfigTable sqlcfg[] = {
+ { "dbhost", "hostname", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+ (void *)offsetof(struct backsql_info, sql_dbhost),
+ "( OLcfgDbAt:6.1 NAME 'olcDbHost' "
+ "DESC 'Hostname of SQL server' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "dbname", "name", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+ (void *)offsetof(struct backsql_info, sql_dbname),
+ "( OLcfgDbAt:6.2 NAME 'olcDbName' "
+ "DESC 'Name of SQL database' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "dbuser", "username", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+ (void *)offsetof(struct backsql_info, sql_dbuser),
+ "( OLcfgDbAt:6.3 NAME 'olcDbUser' "
+ "DESC 'Username for SQL session' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "dbpasswd", "password", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+ (void *)offsetof(struct backsql_info, sql_dbpasswd),
+ "( OLcfgDbAt:6.4 NAME 'olcDbPass' "
+ "DESC 'Password for SQL session' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "concat_pattern", "pattern", 2, 2, 0,
+ ARG_STRING|ARG_MAGIC|BSQL_CONCAT_PATT, (void *)sql_cf_gen,
+ "( OLcfgDbAt:6.20 NAME 'olcSqlConcatPattern' "
+ "DESC 'Pattern used to concatenate strings' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "subtree_cond", "SQL expression", 2, 0, 0, ARG_BERVAL|ARG_QUOTE|ARG_OFFSET,
+ (void *)offsetof(struct backsql_info, sql_subtree_cond),
+ "( OLcfgDbAt:6.21 NAME 'olcSqlSubtreeCond' "
+ "DESC 'Where-clause template for a subtree search condition' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "children_cond", "SQL expression", 2, 0, 0, ARG_BERVAL|ARG_QUOTE|ARG_OFFSET,
+ (void *)offsetof(struct backsql_info, sql_children_cond),
+ "( OLcfgDbAt:6.22 NAME 'olcSqlChildrenCond' "
+ "DESC 'Where-clause template for a children search condition' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "dn_match_cond", "SQL expression", 2, 0, 0, ARG_BERVAL|ARG_QUOTE|ARG_OFFSET,
+ (void *)offsetof(struct backsql_info, sql_dn_match_cond),
+ "( OLcfgDbAt:6.23 NAME 'olcSqlDnMatchCond' "
+ "DESC 'Where-clause template for a DN match search condition' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "oc_query", "SQL expression", 2, 0, 0, ARG_STRING|ARG_QUOTE|ARG_OFFSET,
+ (void *)offsetof(struct backsql_info, sql_oc_query),
+ "( OLcfgDbAt:6.24 NAME 'olcSqlOcQuery' "
+ "DESC 'Query used to collect objectClass mapping data' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "at_query", "SQL expression", 2, 0, 0, ARG_STRING|ARG_QUOTE|ARG_OFFSET,
+ (void *)offsetof(struct backsql_info, sql_at_query),
+ "( OLcfgDbAt:6.25 NAME 'olcSqlAtQuery' "
+ "DESC 'Query used to collect attributeType mapping data' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "insentry_stmt", "SQL expression", 2, 0, 0, ARG_STRING|ARG_QUOTE|ARG_OFFSET,
+ (void *)offsetof(struct backsql_info, sql_insentry_stmt),
+ "( OLcfgDbAt:6.26 NAME 'olcSqlInsEntryStmt' "
+ "DESC 'Statement used to insert a new entry' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "create_needs_select", "yes|no", 2, 2, 0,
+ ARG_ON_OFF|ARG_MAGIC|BSQL_CREATE_NEEDS_SEL, (void *)sql_cf_gen,
+ "( OLcfgDbAt:6.27 NAME 'olcSqlCreateNeedsSelect' "
+ "DESC 'Whether entry creation needs a subsequent select' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "upper_func", "SQL function name", 2, 2, 0, ARG_BERVAL|ARG_OFFSET,
+ (void *)offsetof(struct backsql_info, sql_upper_func),
+ "( OLcfgDbAt:6.28 NAME 'olcSqlUpperFunc' "
+ "DESC 'Function that converts a value to uppercase' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "upper_needs_cast", "yes|no", 2, 2, 0,
+ ARG_ON_OFF|ARG_MAGIC|BSQL_UPPER_NEEDS_CAST, (void *)sql_cf_gen,
+ "( OLcfgDbAt:6.29 NAME 'olcSqlUpperNeedsCast' "
+ "DESC 'Whether olcSqlUpperFunc needs an explicit cast' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "strcast_func", "SQL function name", 2, 2, 0, ARG_BERVAL|ARG_OFFSET,
+ (void *)offsetof(struct backsql_info, sql_strcast_func),
+ "( OLcfgDbAt:6.30 NAME 'olcSqlStrcastFunc' "
+ "DESC 'Function that converts a value to a string' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "delentry_stmt", "SQL expression", 2, 0, 0, ARG_STRING|ARG_QUOTE|ARG_OFFSET,
+ (void *)offsetof(struct backsql_info, sql_delentry_stmt),
+ "( OLcfgDbAt:6.31 NAME 'olcSqlDelEntryStmt' "
+ "DESC 'Statement used to delete an existing entry' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "renentry_stmt", "SQL expression", 2, 0, 0, ARG_STRING|ARG_QUOTE|ARG_OFFSET,
+ (void *)offsetof(struct backsql_info, sql_renentry_stmt),
+ "( OLcfgDbAt:6.32 NAME 'olcSqlRenEntryStmt' "
+ "DESC 'Statement used to rename an entry' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "delobjclasses_stmt", "SQL expression", 2, 0, 0, ARG_STRING|ARG_QUOTE|ARG_OFFSET,
+ (void *)offsetof(struct backsql_info, sql_delobjclasses_stmt),
+ "( OLcfgDbAt:6.33 NAME 'olcSqlDelObjclassesStmt' "
+ "DESC 'Statement used to delete the ID of an entry' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "has_ldapinfo_dn_ru", "yes|no", 2, 2, 0,
+ ARG_ON_OFF|ARG_MAGIC|BSQL_HAS_LDAPINFO_DN_RU, (void *)sql_cf_gen,
+ "( OLcfgDbAt:6.34 NAME 'olcSqlHasLDAPinfoDnRu' "
+ "DESC 'Whether the dn_ru column is present' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "fail_if_no_mapping", "yes|no", 2, 2, 0,
+ ARG_ON_OFF|ARG_MAGIC|BSQL_FAIL_IF_NO_MAPPING, (void *)sql_cf_gen,
+ "( OLcfgDbAt:6.35 NAME 'olcSqlFailIfNoMapping' "
+ "DESC 'Whether to fail on unknown attribute mappings' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "allow_orphans", "yes|no", 2, 2, 0,
+ ARG_ON_OFF|ARG_MAGIC|BSQL_ALLOW_ORPHANS, (void *)sql_cf_gen,
+ "( OLcfgDbAt:6.36 NAME 'olcSqlAllowOrphans' "
+ "DESC 'Whether to allow adding entries with no parent' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "baseobject", "[file]", 1, 2, 0,
+ ARG_STRING|ARG_MAGIC|BSQL_BASE_OBJECT, (void *)sql_cf_gen,
+ "( OLcfgDbAt:6.37 NAME 'olcSqlBaseObject' "
+ "DESC 'Manage an in-memory baseObject entry' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "sqllayer", "name", 2, 0, 0,
+ ARG_MAGIC|BSQL_LAYER, (void *)sql_cf_gen,
+ "( OLcfgDbAt:6.38 NAME 'olcSqlLayer' "
+ "DESC 'Helper used to map DNs between LDAP and SQL' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "use_subtree_shortcut", "yes|no", 2, 2, 0,
+ ARG_ON_OFF|ARG_MAGIC|BSQL_SUBTREE_SHORTCUT, (void *)sql_cf_gen,
+ "( OLcfgDbAt:6.39 NAME 'olcSqlUseSubtreeShortcut' "
+ "DESC 'Collect all entries when searchBase is DB suffix' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "fetch_all_attrs", "yes|no", 2, 2, 0,
+ ARG_ON_OFF|ARG_MAGIC|BSQL_FETCH_ALL_ATTRS, (void *)sql_cf_gen,
+ "( OLcfgDbAt:6.40 NAME 'olcSqlFetchAllAttrs' "
+ "DESC 'Require all attributes to always be loaded' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "fetch_attrs", "attrlist", 2, 0, 0,
+ ARG_MAGIC|BSQL_FETCH_ATTRS, (void *)sql_cf_gen,
+ "( OLcfgDbAt:6.41 NAME 'olcSqlFetchAttrs' "
+ "DESC 'Set of attributes to always fetch' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "check_schema", "yes|no", 2, 2, 0,
+ ARG_ON_OFF|ARG_MAGIC|BSQL_CHECK_SCHEMA, (void *)sql_cf_gen,
+ "( OLcfgDbAt:6.42 NAME 'olcSqlCheckSchema' "
+ "DESC 'Check schema after modifications' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "aliasing_keyword", "string", 2, 2, 0,
+ ARG_STRING|ARG_MAGIC|BSQL_ALIASING_KEYWORD, (void *)sql_cf_gen,
+ "( OLcfgDbAt:6.43 NAME 'olcSqlAliasingKeyword' "
+ "DESC 'The aliasing keyword' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "aliasing_quote", "string", 2, 2, 0, ARG_BERVAL|ARG_OFFSET,
+ (void *)offsetof(struct backsql_info, sql_aliasing_quote),
+ "( OLcfgDbAt:6.44 NAME 'olcSqlAliasingQuote' "
+ "DESC 'Quoting char of the aliasing keyword' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "autocommit", "yes|no", 2, 2, 0,
+ ARG_ON_OFF|ARG_MAGIC|BSQL_AUTOCOMMIT, (void *)sql_cf_gen,
+ "( OLcfgDbAt:6.45 NAME 'olcSqlAutocommit' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "id_query", "SQL expression", 2, 0, 0, ARG_STRING|ARG_QUOTE|ARG_OFFSET,
+ (void *)offsetof(struct backsql_info, sql_id_query),
+ "( OLcfgDbAt:6.46 NAME 'olcSqlIdQuery' "
+ "DESC 'Query used to collect entryID mapping data' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED,
+ NULL, NULL, NULL, NULL }
+};
+
+static ConfigOCs sqlocs[] = {
+ {
+ "( OLcfgDbOc:6.1 "
+ "NAME 'olcSqlConfig' "
+ "DESC 'SQL backend configuration' "
+ "SUP olcDatabaseConfig "
+ "MUST olcDbName "
+ "MAY ( olcDbHost $ olcDbUser $ olcDbPass $ olcSqlConcatPattern $ "
+ "olcSqlSubtreeCond $ olcsqlChildrenCond $ olcSqlDnMatchCond $ "
+ "olcSqlOcQuery $ olcSqlAtQuery $ olcSqlInsEntryStmt $ "
+ "olcSqlCreateNeedsSelect $ olcSqlUpperFunc $ olcSqlUpperNeedsCast $ "
+ "olcSqlStrCastFunc $ olcSqlDelEntryStmt $ olcSqlRenEntryStmt $ "
+ "olcSqlDelObjClassesStmt $ olcSqlHasLDAPInfoDnRu $ "
+ "olcSqlFailIfNoMapping $ olcSqlAllowOrphans $ olcSqlBaseObject $ "
+ "olcSqlLayer $ olcSqlUseSubtreeShortcut $ olcSqlFetchAllAttrs $ "
+ "olcSqlFetchAttrs $ olcSqlCheckSchema $ olcSqlAliasingKeyword $ "
+ "olcSqlAliasingQuote $ olcSqlAutocommit $ olcSqlIdQuery ) )",
+ Cft_Database, sqlcfg },
+ { NULL, Cft_Abstract, NULL }
+};
+
+static int
+sql_cf_gen( ConfigArgs *c )
+{
+ backsql_info *bi = (backsql_info *)c->be->be_private;
+ int rc = 0;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ switch( c->type ) {
+ case BSQL_CONCAT_PATT:
+ if ( bi->sql_concat_patt ) {
+ c->value_string = ch_strdup( bi->sql_concat_patt );
+ } else {
+ rc = 1;
+ }
+ break;
+ case BSQL_CREATE_NEEDS_SEL:
+ if ( bi->sql_flags & BSQLF_CREATE_NEEDS_SELECT )
+ c->value_int = 1;
+ break;
+ case BSQL_UPPER_NEEDS_CAST:
+ if ( bi->sql_flags & BSQLF_UPPER_NEEDS_CAST )
+ c->value_int = 1;
+ break;
+ case BSQL_HAS_LDAPINFO_DN_RU:
+ if ( !(bi->sql_flags & BSQLF_DONTCHECK_LDAPINFO_DN_RU) )
+ return 1;
+ if ( bi->sql_flags & BSQLF_HAS_LDAPINFO_DN_RU )
+ c->value_int = 1;
+ break;
+ case BSQL_FAIL_IF_NO_MAPPING:
+ if ( bi->sql_flags & BSQLF_FAIL_IF_NO_MAPPING )
+ c->value_int = 1;
+ break;
+ case BSQL_ALLOW_ORPHANS:
+ if ( bi->sql_flags & BSQLF_ALLOW_ORPHANS )
+ c->value_int = 1;
+ break;
+ case BSQL_SUBTREE_SHORTCUT:
+ if ( bi->sql_flags & BSQLF_USE_SUBTREE_SHORTCUT )
+ c->value_int = 1;
+ break;
+ case BSQL_FETCH_ALL_ATTRS:
+ if ( bi->sql_flags & BSQLF_FETCH_ALL_ATTRS )
+ c->value_int = 1;
+ break;
+ case BSQL_CHECK_SCHEMA:
+ if ( bi->sql_flags & BSQLF_CHECK_SCHEMA )
+ c->value_int = 1;
+ break;
+ case BSQL_AUTOCOMMIT:
+ if ( bi->sql_flags & BSQLF_AUTOCOMMIT_ON )
+ c->value_int = 1;
+ break;
+ case BSQL_BASE_OBJECT:
+ if ( bi->sql_base_ob_file ) {
+ c->value_string = ch_strdup( bi->sql_base_ob_file );
+ } else if ( bi->sql_baseObject ) {
+ c->value_string = ch_strdup( "TRUE" );
+ } else {
+ rc = 1;
+ }
+ break;
+ case BSQL_LAYER:
+ if ( bi->sql_api ) {
+ backsql_api *ba;
+ struct berval bv;
+ char *ptr;
+ int i;
+ for ( ba = bi->sql_api; ba; ba = ba->ba_next ) {
+ bv.bv_len = strlen( ba->ba_name );
+ if ( ba->ba_argc ) {
+ for ( i = 0; i<ba->ba_argc; i++ )
+ bv.bv_len += strlen( ba->ba_argv[i] ) + 3;
+ }
+ bv.bv_val = ch_malloc( bv.bv_len + 1 );
+ ptr = lutil_strcopy( bv.bv_val, ba->ba_name );
+ if ( ba->ba_argc ) {
+ for ( i = 0; i<ba->ba_argc; i++ ) {
+ *ptr++ = ' ';
+ *ptr++ = '"';
+ ptr = lutil_strcopy( ptr, ba->ba_argv[i] );
+ *ptr++ = '"';
+ }
+ }
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ }
+ } else {
+ rc = 1;
+ }
+ break;
+ case BSQL_ALIASING_KEYWORD:
+ if ( !BER_BVISNULL( &bi->sql_aliasing )) {
+ struct berval bv;
+ bv = bi->sql_aliasing;
+ bv.bv_len--;
+ value_add_one( &c->rvalue_vals, &bv );
+ } else {
+ rc = 1;
+ }
+ break;
+ case BSQL_FETCH_ATTRS:
+ if ( bi->sql_anlist ||
+ ( bi->sql_flags & (BSQLF_FETCH_ALL_USERATTRS|
+ BSQLF_FETCH_ALL_OPATTRS)))
+ {
+ char buf[BUFSIZ*2], *ptr;
+ struct berval bv;
+# define WHATSLEFT ((ber_len_t) (&buf[sizeof( buf )] - ptr))
+ ptr = buf;
+ if ( bi->sql_anlist ) {
+ ptr = anlist_unparse( bi->sql_anlist, ptr, WHATSLEFT );
+ if ( ptr == NULL )
+ return 1;
+ }
+ if ( bi->sql_flags & BSQLF_FETCH_ALL_USERATTRS ) {
+ if ( WHATSLEFT <= STRLENOF( ",*" )) return 1;
+ if ( ptr != buf ) *ptr++ = ',';
+ *ptr++ = '*';
+ }
+ if ( bi->sql_flags & BSQLF_FETCH_ALL_OPATTRS ) {
+ if ( WHATSLEFT <= STRLENOF( ",+" )) return 1;
+ if ( ptr != buf ) *ptr++ = ',';
+ *ptr++ = '+';
+ }
+ bv.bv_val = buf;
+ bv.bv_len = ptr - buf;
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
+ }
+ return rc;
+ } else if ( c->op == LDAP_MOD_DELETE ) { /* FIXME */
+ return -1;
+ }
+
+ switch( c->type ) {
+ case BSQL_CONCAT_PATT:
+ if ( backsql_split_pattern( c->argv[ 1 ], &bi->sql_concat_func, 2 ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s: unable to parse pattern \"%s\"",
+ c->log, c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg );
+ return -1;
+ }
+ bi->sql_concat_patt = c->value_string;
+ break;
+ case BSQL_CREATE_NEEDS_SEL:
+ if ( c->value_int )
+ bi->sql_flags |= BSQLF_CREATE_NEEDS_SELECT;
+ else
+ bi->sql_flags &= ~BSQLF_CREATE_NEEDS_SELECT;
+ break;
+ case BSQL_UPPER_NEEDS_CAST:
+ if ( c->value_int )
+ bi->sql_flags |= BSQLF_UPPER_NEEDS_CAST;
+ else
+ bi->sql_flags &= ~BSQLF_UPPER_NEEDS_CAST;
+ break;
+ case BSQL_HAS_LDAPINFO_DN_RU:
+ bi->sql_flags |= BSQLF_DONTCHECK_LDAPINFO_DN_RU;
+ if ( c->value_int )
+ bi->sql_flags |= BSQLF_HAS_LDAPINFO_DN_RU;
+ else
+ bi->sql_flags &= ~BSQLF_HAS_LDAPINFO_DN_RU;
+ break;
+ case BSQL_FAIL_IF_NO_MAPPING:
+ if ( c->value_int )
+ bi->sql_flags |= BSQLF_FAIL_IF_NO_MAPPING;
+ else
+ bi->sql_flags &= ~BSQLF_FAIL_IF_NO_MAPPING;
+ break;
+ case BSQL_ALLOW_ORPHANS:
+ if ( c->value_int )
+ bi->sql_flags |= BSQLF_ALLOW_ORPHANS;
+ else
+ bi->sql_flags &= ~BSQLF_ALLOW_ORPHANS;
+ break;
+ case BSQL_SUBTREE_SHORTCUT:
+ if ( c->value_int )
+ bi->sql_flags |= BSQLF_USE_SUBTREE_SHORTCUT;
+ else
+ bi->sql_flags &= ~BSQLF_USE_SUBTREE_SHORTCUT;
+ break;
+ case BSQL_FETCH_ALL_ATTRS:
+ if ( c->value_int )
+ bi->sql_flags |= BSQLF_FETCH_ALL_ATTRS;
+ else
+ bi->sql_flags &= ~BSQLF_FETCH_ALL_ATTRS;
+ break;
+ case BSQL_CHECK_SCHEMA:
+ if ( c->value_int )
+ bi->sql_flags |= BSQLF_CHECK_SCHEMA;
+ else
+ bi->sql_flags &= ~BSQLF_CHECK_SCHEMA;
+ break;
+ case BSQL_AUTOCOMMIT:
+ if ( c->value_int )
+ bi->sql_flags |= BSQLF_AUTOCOMMIT_ON;
+ else
+ bi->sql_flags &= ~BSQLF_AUTOCOMMIT_ON;
+ break;
+ case BSQL_BASE_OBJECT:
+ if ( c->be->be_nsuffix == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s: suffix must be set", c->log );
+ Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg );
+ rc = ARG_BAD_CONF;
+ break;
+ }
+ if ( bi->sql_baseObject ) {
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: "
+ "\"baseObject\" already provided (will be overwritten)\n",
+ c->log );
+ entry_free( bi->sql_baseObject );
+ }
+ if ( c->argc == 2 && !strcmp( c->argv[1], "TRUE" ))
+ c->argc = 1;
+ switch( c->argc ) {
+ case 1:
+ return create_baseObject( c->be, c->fname, c->lineno );
+
+ case 2:
+ rc = read_baseObject( c->be, c->argv[ 1 ] );
+ if ( rc == 0 ) {
+ ch_free( bi->sql_base_ob_file );
+ bi->sql_base_ob_file = c->value_string;
+ }
+ return rc;
+
+ default:
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s: trailing values in directive", c->log );
+ Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg );
+ return 1;
+ }
+ break;
+ case BSQL_LAYER:
+ if ( backsql_api_config( bi, c->argv[ 1 ], c->argc - 2, &c->argv[ 2 ] ) )
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s: unable to load sql layer", c->log );
+ Debug( LDAP_DEBUG_ANY, "%s \"%s\"\n",
+ c->cr_msg, c->argv[1] );
+ return 1;
+ }
+ break;
+ case BSQL_ALIASING_KEYWORD:
+ if ( ! BER_BVISNULL( &bi->sql_aliasing ) ) {
+ ch_free( bi->sql_aliasing.bv_val );
+ }
+
+ ber_str2bv( c->argv[ 1 ], strlen( c->argv[ 1 ] ) + 1, 1,
+ &bi->sql_aliasing );
+ /* add a trailing space... */
+ bi->sql_aliasing.bv_val[ bi->sql_aliasing.bv_len - 1] = ' ';
+ break;
+ case BSQL_FETCH_ATTRS: {
+ char *str, *s, *next;
+ const char *delimstr = ",";
+
+ str = ch_strdup( c->argv[ 1 ] );
+ for ( s = ldap_pvt_strtok( str, delimstr, &next );
+ s != NULL;
+ s = ldap_pvt_strtok( NULL, delimstr, &next ) )
+ {
+ if ( strlen( s ) == 1 ) {
+ if ( *s == '*' ) {
+ bi->sql_flags |= BSQLF_FETCH_ALL_USERATTRS;
+ c->argv[ 1 ][ s - str ] = ',';
+
+ } else if ( *s == '+' ) {
+ bi->sql_flags |= BSQLF_FETCH_ALL_OPATTRS;
+ c->argv[ 1 ][ s - str ] = ',';
+ }
+ }
+ }
+ ch_free( str );
+ bi->sql_anlist = str2anlist( bi->sql_anlist, c->argv[ 1 ], delimstr );
+ if ( bi->sql_anlist == NULL ) {
+ return -1;
+ }
+ }
+ break;
+ }
+ return rc;
+}
+
+/*
+ * Read the entries specified in fname and merge the attributes
+ * to the user defined baseObject entry. Note that if we find any errors
+ * what so ever, we will discard the entire entries, print an
+ * error message and return.
+ */
+static int
+read_baseObject(
+ BackendDB *be,
+ const char *fname )
+{
+ backsql_info *bi = (backsql_info *)be->be_private;
+ LDIFFP *fp;
+ int rc = 0, lmax = 0, ldifrc;
+ unsigned long lineno = 0;
+ char *buf = NULL;
+
+ assert( fname != NULL );
+
+ fp = ldif_open( fname, "r" );
+ if ( fp == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "could not open back-sql baseObject "
+ "attr file \"%s\" - absolute path?\n",
+ fname );
+ perror( fname );
+ return LDAP_OTHER;
+ }
+
+ bi->sql_baseObject = entry_alloc();
+ if ( bi->sql_baseObject == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "read_baseObject_file: entry_alloc failed" );
+ ldif_close( fp );
+ return LDAP_NO_MEMORY;
+ }
+ bi->sql_baseObject->e_name = be->be_suffix[0];
+ bi->sql_baseObject->e_nname = be->be_nsuffix[0];
+ bi->sql_baseObject->e_attrs = NULL;
+
+ while (( ldifrc = ldif_read_record( fp, &lineno, &buf, &lmax )) > 0 ) {
+ Entry *e = str2entry( buf );
+ Attribute *a;
+
+ if( e == NULL ) {
+ fprintf( stderr, "back-sql baseObject: "
+ "could not parse entry (line=%lu)\n",
+ lineno );
+ rc = LDAP_OTHER;
+ break;
+ }
+
+ /* make sure the DN is the database's suffix */
+ if ( !be_issuffix( be, &e->e_nname ) ) {
+ fprintf( stderr,
+ "back-sql: invalid baseObject - "
+ "dn=\"%s\" (line=%lu)\n",
+ e->e_name.bv_val, lineno );
+ entry_free( e );
+ rc = LDAP_OTHER;
+ break;
+ }
+
+ /*
+ * we found a valid entry, so walk thru all the attributes in the
+ * entry, and add each attribute type and description to baseObject
+ */
+ for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
+ if ( attr_merge( bi->sql_baseObject, a->a_desc,
+ a->a_vals,
+ ( a->a_nvals == a->a_vals ) ?
+ NULL : a->a_nvals ) )
+ {
+ rc = LDAP_OTHER;
+ break;
+ }
+ }
+
+ entry_free( e );
+ if ( rc ) {
+ break;
+ }
+ }
+
+ if ( ldifrc < 0 )
+ rc = LDAP_OTHER;
+
+ if ( rc ) {
+ entry_free( bi->sql_baseObject );
+ bi->sql_baseObject = NULL;
+ }
+
+ ch_free( buf );
+
+ ldif_close( fp );
+
+ Debug( LDAP_DEBUG_CONFIG, "back-sql baseObject file \"%s\" read.\n",
+ fname );
+
+ return rc;
+}
+
+static int
+create_baseObject(
+ BackendDB *be,
+ const char *fname,
+ int lineno )
+{
+ backsql_info *bi = (backsql_info *)be->be_private;
+ LDAPRDN rdn;
+ char *p;
+ int rc, iAVA;
+ char buf[1024];
+
+ snprintf( buf, sizeof(buf),
+ "dn: %s\n"
+ "objectClass: extensibleObject\n"
+ "description: builtin baseObject for back-sql\n"
+ "description: all entries mapped "
+ "in table \"ldap_entries\" "
+ "must have "
+ "\"" BACKSQL_BASEOBJECT_IDSTR "\" "
+ "in the \"parent\" column",
+ be->be_suffix[0].bv_val );
+
+ bi->sql_baseObject = str2entry( buf );
+ if ( bi->sql_baseObject == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<==backsql_db_config (%s line %d): "
+ "unable to parse baseObject entry\n",
+ fname, lineno );
+ return 1;
+ }
+
+ if ( BER_BVISEMPTY( &be->be_suffix[ 0 ] ) ) {
+ return 0;
+ }
+
+ rc = ldap_bv2rdn( &be->be_suffix[ 0 ], &rdn, (char **)&p,
+ LDAP_DN_FORMAT_LDAP );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<==backsql_db_config (%s line %d): unable to extract RDN " "from baseObject DN \"%s\" (%d: %s)\n",
+ fname, lineno, be->be_suffix[0].bv_val, rc,
+ ldap_err2string(rc) );
+ return 1;
+ }
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ iAVA ];
+ AttributeDescription *ad = NULL;
+ slap_syntax_transform_func *transf = NULL;
+ struct berval bv = BER_BVNULL;
+ const char *text = NULL;
+
+ assert( ava != NULL );
+
+ rc = slap_bv2ad( &ava->la_attr, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<==backsql_db_config (%s line %d): AttributeDescription of naming " "attribute #%d from baseObject " "DN \"%s\": %d: %s\n",
+ fname, lineno, iAVA, be->be_suffix[0].bv_val,
+ rc, ldap_err2string(rc) );
+ return 1;
+ }
+
+ transf = ad->ad_type->sat_syntax->ssyn_pretty;
+ if ( transf ) {
+ /*
+ * transform value by pretty function
+ * if value is empty, use empty_bv
+ */
+ rc = ( *transf )( ad->ad_type->sat_syntax,
+ ava->la_value.bv_len
+ ? &ava->la_value
+ : (struct berval *) &slap_empty_bv,
+ &bv, NULL );
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<==backsql_db_config (%s line %d): " "prettying of attribute #%d " "from baseObject " "DN \"%s\" failed: %d: %s\n",
+ fname, lineno, iAVA,
+ be->be_suffix[0].bv_val, rc,
+ ldap_err2string(rc) );
+ return 1;
+ }
+ }
+
+ if ( !BER_BVISNULL( &bv ) ) {
+ if ( ava->la_flags & LDAP_AVA_FREE_VALUE ) {
+ ber_memfree( ava->la_value.bv_val );
+ }
+ ava->la_value = bv;
+ ava->la_flags |= LDAP_AVA_FREE_VALUE;
+ }
+
+ attr_merge_normalize_one( bi->sql_baseObject,
+ ad, &ava->la_value, NULL );
+ }
+
+ ldap_rdnfree( rdn );
+
+ return 0;
+}
+
+int backsql_init_cf( BackendInfo *bi )
+{
+ int rc;
+
+ bi->bi_cf_ocs = sqlocs;
+ rc = config_register_schema( sqlcfg, sqlocs );
+ if ( rc ) return rc;
+ return 0;
+}
diff --git a/servers/slapd/back-sql/delete.c b/servers/slapd/back-sql/delete.c
new file mode 100644
index 0000000..489d4d3
--- /dev/null
+++ b/servers/slapd/back-sql/delete.c
@@ -0,0 +1,627 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 Dmitry Kovalev.
+ * Portions Copyright 2002 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Dmitry Kovalev for inclusion
+ * by OpenLDAP Software. Additional significant contributors include
+ * Pierangelo Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "ac/string.h"
+
+#include "slap.h"
+#include "proto-sql.h"
+
+typedef struct backsql_delete_attr_t {
+ Operation *op;
+ SlapReply *rs;
+ SQLHDBC dbh;
+ backsql_entryID *e_id;
+} backsql_delete_attr_t;
+
+static int
+backsql_delete_attr_f( void *v_at, void *v_bda )
+{
+ backsql_at_map_rec *at = (backsql_at_map_rec *)v_at;
+ backsql_delete_attr_t *bda = (backsql_delete_attr_t *)v_bda;
+ int rc;
+
+ rc = backsql_modify_delete_all_values( bda->op,
+ bda->rs, bda->dbh, bda->e_id, at );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return BACKSQL_AVL_STOP;
+ }
+
+ return BACKSQL_AVL_CONTINUE;
+}
+
+static int
+backsql_delete_all_attrs(
+ Operation *op,
+ SlapReply *rs,
+ SQLHDBC dbh,
+ backsql_entryID *eid )
+{
+ backsql_delete_attr_t bda;
+ int rc;
+
+ bda.op = op;
+ bda.rs = rs;
+ bda.dbh = dbh;
+ bda.e_id = eid;
+
+ rc = ldap_avl_apply( eid->eid_oc->bom_attrs, backsql_delete_attr_f, &bda,
+ BACKSQL_AVL_STOP, AVL_INORDER );
+ if ( rc == BACKSQL_AVL_STOP ) {
+ return rs->sr_err;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+backsql_delete_int(
+ Operation *op,
+ SlapReply *rs,
+ SQLHDBC dbh,
+ SQLHSTMT *sthp,
+ backsql_entryID *eid,
+ Entry **ep )
+{
+ backsql_info *bi = (backsql_info*)op->o_bd->be_private;
+ SQLHSTMT sth = SQL_NULL_HSTMT;
+ RETCODE rc;
+ int prc = LDAP_SUCCESS;
+ /* first parameter no */
+ SQLUSMALLINT pno = 0;
+
+ sth = *sthp;
+
+ /* ldap_avl_apply ... */
+ rs->sr_err = backsql_delete_all_attrs( op, rs, dbh, eid );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ rc = backsql_Prepare( dbh, &sth, eid->eid_oc->bom_delete_proc, 0 );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_delete(): "
+ "error preparing delete query\n" );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
+
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "SQL-backend error";
+ *ep = NULL;
+ goto done;
+ }
+
+ if ( BACKSQL_IS_DEL( eid->eid_oc->bom_expect_return ) ) {
+ pno = 1;
+ rc = backsql_BindParamInt( sth, 1, SQL_PARAM_OUTPUT, &prc );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_delete(): "
+ "error binding output parameter for objectClass %s\n",
+ eid->eid_oc->bom_oc->soc_cname.bv_val );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ *ep = NULL;
+ goto done;
+ }
+ }
+
+ rc = backsql_BindParamID( sth, pno + 1, SQL_PARAM_INPUT, &eid->eid_keyval );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_delete(): "
+ "error binding keyval parameter for objectClass %s\n",
+ eid->eid_oc->bom_oc->soc_cname.bv_val );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ *ep = NULL;
+ goto done;
+ }
+
+ rc = SQLExecute( sth );
+ if ( rc == SQL_SUCCESS && prc == LDAP_SUCCESS ) {
+ rs->sr_err = LDAP_SUCCESS;
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE, " backsql_delete(): "
+ "delete_proc execution failed (rc=%d, prc=%d)\n",
+ rc, prc );
+
+
+ if ( prc != LDAP_SUCCESS ) {
+ /* SQL procedure executed fine
+ * but returned an error */
+ rs->sr_err = BACKSQL_SANITIZE_ERROR( prc );
+
+ } else {
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ rs->sr_err = LDAP_OTHER;
+ }
+ SQLFreeStmt( sth, SQL_DROP );
+ goto done;
+ }
+ SQLFreeStmt( sth, SQL_DROP );
+
+ /* delete "auxiliary" objectClasses, if any... */
+ rc = backsql_Prepare( dbh, &sth, bi->sql_delobjclasses_stmt, 0 );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_delete(): "
+ "error preparing ldap_entry_objclasses delete query\n" );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
+
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "SQL-backend error";
+ *ep = NULL;
+ goto done;
+ }
+
+ rc = backsql_BindParamID( sth, 1, SQL_PARAM_INPUT, &eid->eid_id );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_delete(): "
+ "error binding auxiliary objectClasses "
+ "entry ID parameter for objectClass %s\n",
+ eid->eid_oc->bom_oc->soc_cname.bv_val );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ *ep = NULL;
+ goto done;
+ }
+
+ rc = SQLExecute( sth );
+ switch ( rc ) {
+ case SQL_NO_DATA:
+ /* apparently there were no "auxiliary" objectClasses
+ * for this entry... */
+ case SQL_SUCCESS:
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_TRACE, " backsql_delete(): "
+ "failed to delete record from ldap_entry_objclasses\n" );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "SQL-backend error";
+ *ep = NULL;
+ goto done;
+ }
+ SQLFreeStmt( sth, SQL_DROP );
+
+ /* delete entry... */
+ rc = backsql_Prepare( dbh, &sth, bi->sql_delentry_stmt, 0 );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_delete(): "
+ "error preparing ldap_entries delete query\n" );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
+
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "SQL-backend error";
+ *ep = NULL;
+ goto done;
+ }
+
+ rc = backsql_BindParamID( sth, 1, SQL_PARAM_INPUT, &eid->eid_id );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_delete(): "
+ "error binding entry ID parameter "
+ "for objectClass %s\n",
+ eid->eid_oc->bom_oc->soc_cname.bv_val );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ *ep = NULL;
+ goto done;
+ }
+
+ rc = SQLExecute( sth );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_delete(): "
+ "failed to delete record from ldap_entries\n" );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "SQL-backend error";
+ *ep = NULL;
+ goto done;
+ }
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_err = LDAP_SUCCESS;
+ *ep = NULL;
+
+done:;
+ *sthp = sth;
+
+ return rs->sr_err;
+}
+
+typedef struct backsql_tree_delete_t {
+ Operation *btd_op;
+ int btd_rc;
+ backsql_entryID *btd_eid;
+} backsql_tree_delete_t;
+
+static int
+backsql_tree_delete_search_cb( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_SEARCH ) {
+ backsql_tree_delete_t *btd;
+ backsql_entryID *eid;
+
+ btd = (backsql_tree_delete_t *)op->o_callback->sc_private;
+
+ if ( !access_allowed( btd->btd_op, rs->sr_entry,
+ slap_schema.si_ad_entry, NULL, ACL_WDEL, NULL )
+ || !access_allowed( btd->btd_op, rs->sr_entry,
+ slap_schema.si_ad_children, NULL, ACL_WDEL, NULL ) )
+ {
+ btd->btd_rc = LDAP_INSUFFICIENT_ACCESS;
+ return rs->sr_err = LDAP_UNAVAILABLE;
+ }
+
+ assert( rs->sr_entry != NULL );
+ assert( rs->sr_entry->e_private != NULL );
+
+ eid = (backsql_entryID *)rs->sr_entry->e_private;
+ assert( eid->eid_oc != NULL );
+ if ( eid->eid_oc == NULL || eid->eid_oc->bom_delete_proc == NULL ) {
+ btd->btd_rc = LDAP_UNWILLING_TO_PERFORM;
+ return rs->sr_err = LDAP_UNAVAILABLE;
+ }
+
+ eid = backsql_entryID_dup( eid, op->o_tmpmemctx );
+ eid->eid_next = btd->btd_eid;
+ btd->btd_eid = eid;
+ }
+
+ return 0;
+}
+
+static int
+backsql_tree_delete(
+ Operation *op,
+ SlapReply *rs,
+ SQLHDBC dbh,
+ SQLHSTMT *sthp )
+{
+ Operation op2 = *op;
+ slap_callback sc = { 0 };
+ SlapReply rs2 = { REP_RESULT };
+ backsql_tree_delete_t btd = { 0 };
+
+ int rc;
+
+ /*
+ * - perform an internal subtree search as the rootdn
+ * - for each entry
+ * - check access
+ * - check objectClass and delete method(s)
+ * - for each entry
+ * - delete
+ * - if successful, commit
+ */
+
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2.o_protocol = LDAP_VERSION3;
+
+ btd.btd_op = op;
+ sc.sc_private = &btd;
+ sc.sc_response = backsql_tree_delete_search_cb;
+ op2.o_callback = &sc;
+
+ op2.o_dn = op->o_bd->be_rootdn;
+ op2.o_ndn = op->o_bd->be_rootndn;
+
+ op2.o_managedsait = SLAP_CONTROL_CRITICAL;
+
+ op2.ors_scope = LDAP_SCOPE_SUBTREE;
+ op2.ors_deref = LDAP_DEREF_NEVER;
+ op2.ors_slimit = SLAP_NO_LIMIT;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+ op2.ors_filter = (Filter *)slap_filter_objectClass_pres;
+ op2.ors_filterstr = *slap_filterstr_objectClass_pres;
+ op2.ors_attrs = slap_anlist_all_attributes;
+ op2.ors_attrsonly = 0;
+
+ rc = op->o_bd->be_search( &op2, &rs2 );
+ if ( rc != LDAP_SUCCESS ) {
+ rc = rs->sr_err = btd.btd_rc;
+ rs->sr_text = "subtree delete not possible";
+ send_ldap_result( op, rs );
+ goto clean;
+ }
+
+ for ( ; btd.btd_eid != NULL;
+ btd.btd_eid = backsql_free_entryID( btd.btd_eid,
+ 1, op->o_tmpmemctx ) )
+ {
+ Entry *e = (void *)0xbad;
+ rc = backsql_delete_int( op, rs, dbh, sthp, btd.btd_eid, &e );
+ if ( rc != LDAP_SUCCESS ) {
+ break;
+ }
+ }
+
+clean:;
+ for ( ; btd.btd_eid != NULL;
+ btd.btd_eid = backsql_free_entryID( btd.btd_eid,
+ 1, op->o_tmpmemctx ) )
+ ;
+
+ return rc;
+}
+
+int
+backsql_delete( Operation *op, SlapReply *rs )
+{
+ SQLHDBC dbh = SQL_NULL_HDBC;
+ SQLHSTMT sth = SQL_NULL_HSTMT;
+ backsql_oc_map_rec *oc = NULL;
+ backsql_srch_info bsi = { 0 };
+ backsql_entryID e_id = { 0 };
+ Entry d = { 0 }, p = { 0 }, *e = NULL;
+ struct berval pdn = BER_BVNULL;
+ int manageDSAit = get_manageDSAit( op );
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_delete(): deleting entry \"%s\"\n",
+ op->o_req_ndn.bv_val );
+
+ rs->sr_err = backsql_get_db_conn( op, &dbh );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_delete(): "
+ "could not get connection handle - exiting\n" );
+ rs->sr_text = ( rs->sr_err == LDAP_OTHER )
+ ? "SQL-backend error" : NULL;
+ e = NULL;
+ goto done;
+ }
+
+ /*
+ * Get the entry
+ */
+ bsi.bsi_e = &d;
+ rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn,
+ LDAP_SCOPE_BASE,
+ (time_t)(-1), NULL, dbh, op, rs, slap_anlist_no_attrs,
+ ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY | BACKSQL_ISF_GET_OC ) );
+ switch ( rs->sr_err ) {
+ case LDAP_SUCCESS:
+ break;
+
+ case LDAP_REFERRAL:
+ if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
+ dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) )
+ {
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ rs->sr_matched = NULL;
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+ break;
+ }
+ e = &d;
+ /* fallthru */
+
+ default:
+ Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
+ "could not retrieve deleteDN ID - no such entry\n" );
+ if ( !BER_BVISNULL( &d.e_nname ) ) {
+ /* FIXME: should always be true! */
+ e = &d;
+
+ } else {
+ e = NULL;
+ }
+ goto done;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, &d, get_assertion( op ) )
+ != LDAP_COMPARE_TRUE ) )
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ e = &d;
+ goto done;
+ }
+
+ if ( !access_allowed( op, &d, slap_schema.si_ad_entry,
+ NULL, ACL_WDEL, NULL ) )
+ {
+ Debug( LDAP_DEBUG_TRACE, " backsql_delete(): "
+ "no write access to entry\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ e = &d;
+ goto done;
+ }
+
+ rs->sr_err = backsql_has_children( op, dbh, &op->o_req_ndn );
+ switch ( rs->sr_err ) {
+ case LDAP_COMPARE_FALSE:
+ rs->sr_err = LDAP_SUCCESS;
+ break;
+
+ case LDAP_COMPARE_TRUE:
+#ifdef SLAP_CONTROL_X_TREE_DELETE
+ if ( get_treeDelete( op ) ) {
+ rs->sr_err = LDAP_SUCCESS;
+ break;
+ }
+#endif /* SLAP_CONTROL_X_TREE_DELETE */
+
+ Debug( LDAP_DEBUG_TRACE, " backsql_delete(): "
+ "entry \"%s\" has children\n",
+ op->o_req_dn.bv_val );
+ rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
+ rs->sr_text = "subordinate objects must be deleted first";
+ /* fallthru */
+
+ default:
+ e = &d;
+ goto done;
+ }
+
+ assert( bsi.bsi_base_id.eid_oc != NULL );
+ oc = bsi.bsi_base_id.eid_oc;
+ if ( oc->bom_delete_proc == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_delete(): "
+ "delete procedure is not defined "
+ "for this objectclass - aborting\n" );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "operation not permitted within namingContext";
+ e = NULL;
+ goto done;
+ }
+
+ /*
+ * Get the parent
+ */
+ e_id = bsi.bsi_base_id;
+ memset( &bsi.bsi_base_id, 0, sizeof( bsi.bsi_base_id ) );
+ if ( !be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
+ dnParent( &op->o_req_ndn, &pdn );
+ bsi.bsi_e = &p;
+ rs->sr_err = backsql_init_search( &bsi, &pdn,
+ LDAP_SCOPE_BASE,
+ (time_t)(-1), NULL, dbh, op, rs,
+ slap_anlist_no_attrs,
+ BACKSQL_ISF_GET_ENTRY );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
+ "could not retrieve deleteDN ID "
+ "- no such entry\n" );
+ e = &p;
+ goto done;
+ }
+
+ (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
+
+ /* check parent for "children" acl */
+ if ( !access_allowed( op, &p, slap_schema.si_ad_children,
+ NULL, ACL_WDEL, NULL ) )
+ {
+ Debug( LDAP_DEBUG_TRACE, " backsql_delete(): "
+ "no write access to parent\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ e = &p;
+ goto done;
+
+ }
+ }
+
+ e = &d;
+#ifdef SLAP_CONTROL_X_TREE_DELETE
+ if ( get_treeDelete( op ) ) {
+ backsql_tree_delete( op, rs, dbh, &sth );
+ if ( rs->sr_err == LDAP_OTHER || rs->sr_err == LDAP_SUCCESS )
+ {
+ e = NULL;
+ }
+
+ } else
+#endif /* SLAP_CONTROL_X_TREE_DELETE */
+ {
+ backsql_delete_int( op, rs, dbh, &sth, &e_id, &e );
+ }
+
+ /*
+ * Commit only if all operations succeed
+ */
+ if ( sth != SQL_NULL_HSTMT ) {
+ SQLUSMALLINT CompletionType = SQL_ROLLBACK;
+
+ if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop ) {
+ assert( e == NULL );
+ CompletionType = SQL_COMMIT;
+ }
+
+ SQLTransact( SQL_NULL_HENV, dbh, CompletionType );
+ }
+
+done:;
+ if ( e != NULL ) {
+ if ( !access_allowed( op, e, slap_schema.si_ad_entry, NULL,
+ ACL_DISCLOSE, NULL ) )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = NULL;
+ rs->sr_matched = NULL;
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+ }
+ }
+
+ if ( op->o_noop && rs->sr_err == LDAP_SUCCESS ) {
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ }
+
+ send_ldap_result( op, rs );
+
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_delete()\n" );
+
+ if ( !BER_BVISNULL( &e_id.eid_ndn ) ) {
+ (void)backsql_free_entryID( &e_id, 0, op->o_tmpmemctx );
+ }
+
+ if ( !BER_BVISNULL( &d.e_nname ) ) {
+ backsql_entry_clean( op, &d );
+ }
+
+ if ( !BER_BVISNULL( &p.e_nname ) ) {
+ backsql_entry_clean( op, &p );
+ }
+
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/back-sql/docs/bugs b/servers/slapd/back-sql/docs/bugs
new file mode 100644
index 0000000..4b2f3c7
--- /dev/null
+++ b/servers/slapd/back-sql/docs/bugs
@@ -0,0 +1,16 @@
+1) driver name comparison for MS SQL Server workaround is really kinda dirty
+ hack, but for now i don't know how to code it more carefully
+2) another dirty hack: length of LONGVARCHAR and LONGVARBINARY fields is
+ currently set to MAX_ATTR_LEN. Maybe such fields must be handled with
+ SQLGetData() instead of SQLBindCol(), but it is said in documentation,
+ that it is guaranteed to work only when such column goes after last bound
+ column. Or should we get ALL columns with SQLGetData (then something like
+ _SQLFetchAsStrings() wrapper would do SQLGetData() for all columns)...
+3) in some cases (particularly, when using OpenLink Generic ODBC driver with
+ MS SQL Server), it returns "Function sequence error" after all records are
+ fetched. I really don't know what it means, and after all
+ - it works with any other driver I tried
+4) ldapsearch sometimes refuses to show some attributes ("NOT PRINTABLE" diags)
+ on Win32 (on linux everything's fine)
+5) back-sql crashes on invalid filters (to be fixed ASAP)
+ \ No newline at end of file
diff --git a/servers/slapd/back-sql/docs/concept b/servers/slapd/back-sql/docs/concept
new file mode 100644
index 0000000..ed29047
--- /dev/null
+++ b/servers/slapd/back-sql/docs/concept
@@ -0,0 +1 @@
+The SQL backend is described in the slapd-sql(5) manual page.
diff --git a/servers/slapd/back-sql/docs/install b/servers/slapd/back-sql/docs/install
new file mode 100644
index 0000000..230bf0a
--- /dev/null
+++ b/servers/slapd/back-sql/docs/install
@@ -0,0 +1,86 @@
+PLEASE READ THIS WHOLE FILE AND CONCEPT, BECAUSE THEY COVER SEVERAL STICKY
+ISSUES THAT YOU WILL PROBABLY STUMBLE ACROSS ANYWAY
+
+1. Build
+To build slapd with back-sql under Unix you need to build and install
+iODBC 2.50.3 (later versions should probably work, but not earlier),
+or unixODBC (you will have to change -liodbc to -lodbc then).
+Then, at top of OpenLDAP source tree, run
+"configure <other options you need> --enable-sql", then "make" -
+this should build back-sql-enabled slapd, provided that you have iODBC/unixODBC
+libraries and include files in include/library paths, "make install"...
+In other words, follow installation procedure described in OpenLDAP
+Administrators Guide, adding --enable-sql option to configure, and
+having iODBC/unixODBC libraries installed an accessible by compiler.
+
+Under Win32/MSVC++, I modified the workspace so that back-sql is built into
+slapd automatically, since MS ODBC manager, odbc32.dll, is included in
+standard library pack, and it does no bad even if you don't plan to use it.
+I also could provide precompiled executables for those who don't have MSVC.
+Note that Win32 port of OpenLDAP itself is experimental, and thus doesn't
+provide very convenient build environment (yet).
+
+2. Tune datasources and slapd.conf
+Next, you need to define ODBC datasource with data you want to publish
+with help of back-sql. Assuming that you have your data in some SQL-compliant
+RDBMS, and have installed proper ODBC driver for this RDBMS, this is as simple
+as adding a record into odbc.ini (for iODBC/unixODBC), or using ODBC wizard in
+Control Panel (for odbc32).
+Next, you need to add appropriate "database" record to your slapd.conf.
+See samples provided in "back-sql/RDBMS_DEPENDENT/" subdirectory.
+
+Several things worth noting about ODBC:
+- "dbname" directive stands for ODBC datasource name (DSN),
+ not the name of your database in RDBMS context
+- ODBC under Unix is not so common as under Windows, so you could have
+ problems with Unix drivers for your RDBMS. Visit http://www.openlinksw.com,
+ they provide a multitier solution which allows connecting to DBMSes on
+ different platforms, proxying and other connectivity and integration issues.
+ They also support iODBC, and have good free customer service through
+ newsserver (at news.openlinksw.com).
+ Also worth noting are: ODBC-ODBC bridge by EasySoft (which was claimed
+ by several people to be far more effective and stable than OpenLink),
+ OpenRDA package etc.
+- be careful defining RDBMS connection parameters, you'll probably need only
+ "dbname" directive - all the rest can be defined in datasource. Every other
+ directive is used to override value stored in datasource definition.
+ Maybe you will want to use dbuser/dbpasswd to override credentials defined in datasource
+- full list of configuration directives supported is available in file "guide",
+ you may also analyze output of 'slapd -d 5' to find out some useful
+ directives for redefining default queries
+
+3. Creating and using back-sql metatables
+Read the file "concept" to understand, what metainformation you need to add,
+and what for... ;)
+See SQL scripts and slapd.conf files in samples directory.
+Find subdirectory in "rdbms_depend/" corresponding to your RDBMS (Oracle,
+MS SQL Server and mySQL are listed there currently), or copy and edit
+any of these to conform to SQL dialect of your RDBMS (please be sure to send
+me scripts and notes for new RDBMSes ;).
+
+Execute "backsql_create.sql" from that subdirectory (or edited one),
+so that the tables it creates appear in the same
+context with the data you want to export through LDAP (under same DB/user,
+or whatever is needed in RDBMS you use). You can use something like
+"mysql < xxx.sql" for mySQL, Query Analyzer+Open query file for MS SQL,
+sqlplus and "@xxx.sql" for Oracle.
+
+You may well want to try it with test data first, and see how metatables
+are used. Create test data and metadata by running testdb_create.sql,
+testdb_data.sql, and testdb_metadata.sql scripts (again, adopted for your
+RDBMS, and in the same context as metatables you created before), and
+tune slapd.conf to use your test DB.
+
+4. Testing
+To diagnose back-sql, run slapd with debug level TRACE ("slapd -d 5" will go).
+Then, use some LDAP client to query corresponding subtree (for test database,
+you could for instance search one level from "o=sql,c=RU"). I personally used
+saucer, which is included in OpenLDAP package (it builds automatically under
+Unix/GNU configure and for MSVC I added appropriate project to workspace).
+And also Java LDAP browser-editor (see link somewhere on OpenLDAP site) to
+test ADD/DELETE/MODIFY operations on Oracle and MS SQL.
+
+See file "platforms" if you encounter connection problems - you may find
+a hint for your RDBMS or OS there. If you are stuck - please contact me at
+mit@openldap.org, or (better) post an issue through OpenLDAP's Issue Tracking
+System (see http:/www.openldap.org/its).
diff --git a/servers/slapd/back-sql/docs/platforms b/servers/slapd/back-sql/docs/platforms
new file mode 100644
index 0000000..65e326a
--- /dev/null
+++ b/servers/slapd/back-sql/docs/platforms
@@ -0,0 +1,8 @@
+Platforms and configurations it has been tested on:
+
+General:
+ - ODBC managers: iODBC,unixODBC under unixes, odbc32.dll under Win32 family
+ - OSes: Linux/glibc, FreeBSD, OpenBSD, Solaris 2.6, Win98, WinNT, Win2000 server
+ - RDBMSes: Oracle 7/8/8i, MS SQL Server 6.5/7, mySQL
+ - access suites: OpenLink DAS, EasySoft OOB, various win32 drivers
+
diff --git a/servers/slapd/back-sql/docs/todo b/servers/slapd/back-sql/docs/todo
new file mode 100644
index 0000000..9d8736f
--- /dev/null
+++ b/servers/slapd/back-sql/docs/todo
@@ -0,0 +1,12 @@
+1) must add alias handling
+2) [sizelimit moved to frontend]
+ must set time limit when preparing all queries, and check size limit
+3) there was expressed a need to have access to IP in while constructing
+ queries, to have response alter in accordance to client IP. Will add
+ preprocessor for values in metatables, which would substitute things
+ like "$IP$".
+4) must handle NOT filters (see ITS#2652)
+5) must map attribute types and syntaxes between LDAP and SQL types (e.g.
+ use BLOBs for octet streams)
+6) must define another mech to add auxiliary objectClass to all entries
+ according to ldap_at_mappings (ldap_entry_objclasses has limitations)
diff --git a/servers/slapd/back-sql/entry-id.c b/servers/slapd/back-sql/entry-id.c
new file mode 100644
index 0000000..c2d78a7
--- /dev/null
+++ b/servers/slapd/back-sql/entry-id.c
@@ -0,0 +1,1092 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 Dmitry Kovalev.
+ * Portions Copyright 2002 Pierangelo Masarati.
+ * Portions Copyright 2004 Mark Adamson.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Dmitry Kovalev for inclusion
+ * by OpenLDAP Software. Additional significant contributors include
+ * Pierangelo Masarati and Mark Adamson.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "ac/string.h"
+
+#include "lutil.h"
+#include "slap.h"
+#include "proto-sql.h"
+
+#ifdef BACKSQL_ARBITRARY_KEY
+struct berval backsql_baseObject_bv = BER_BVC( BACKSQL_BASEOBJECT_IDSTR );
+#endif /* BACKSQL_ARBITRARY_KEY */
+
+backsql_entryID *
+backsql_entryID_dup( backsql_entryID *src, void *ctx )
+{
+ backsql_entryID *dst;
+
+ if ( src == NULL ) return NULL;
+
+ dst = slap_sl_calloc( 1, sizeof( backsql_entryID ), ctx );
+ ber_dupbv_x( &dst->eid_ndn, &src->eid_ndn, ctx );
+ if ( src->eid_dn.bv_val == src->eid_ndn.bv_val ) {
+ dst->eid_dn = dst->eid_ndn;
+ } else {
+ ber_dupbv_x( &dst->eid_dn, &src->eid_dn, ctx );
+ }
+
+#ifdef BACKSQL_ARBITRARY_KEY
+ ber_dupbv_x( &dst->eid_id, &src->eid_id, ctx );
+ ber_dupbv_x( &dst->eid_keyval, &src->eid_keyval, ctx );
+#else /* ! BACKSQL_ARBITRARY_KEY */
+ dst->eid_id = src->eid_id;
+ dst->eid_keyval = src->eid_keyval;
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+
+ dst->eid_oc = src->eid_oc;
+ dst->eid_oc_id = src->eid_oc_id;
+
+ return dst;
+}
+
+backsql_entryID *
+backsql_free_entryID( backsql_entryID *id, int freeit, void *ctx )
+{
+ backsql_entryID *next;
+
+ assert( id != NULL );
+
+ next = id->eid_next;
+
+ if ( !BER_BVISNULL( &id->eid_ndn ) ) {
+ if ( !BER_BVISNULL( &id->eid_dn )
+ && id->eid_dn.bv_val != id->eid_ndn.bv_val )
+ {
+ slap_sl_free( id->eid_dn.bv_val, ctx );
+ BER_BVZERO( &id->eid_dn );
+ }
+
+ slap_sl_free( id->eid_ndn.bv_val, ctx );
+ BER_BVZERO( &id->eid_ndn );
+ }
+
+#ifdef BACKSQL_ARBITRARY_KEY
+ if ( !BER_BVISNULL( &id->eid_id ) ) {
+ slap_sl_free( id->eid_id.bv_val, ctx );
+ BER_BVZERO( &id->eid_id );
+ }
+
+ if ( !BER_BVISNULL( &id->eid_keyval ) ) {
+ slap_sl_free( id->eid_keyval.bv_val, ctx );
+ BER_BVZERO( &id->eid_keyval );
+ }
+#endif /* BACKSQL_ARBITRARY_KEY */
+
+ if ( freeit ) {
+ slap_sl_free( id, ctx );
+ }
+
+ return next;
+}
+
+/*
+ * NOTE: the dn must be normalized
+ */
+int
+backsql_dn2id(
+ Operation *op,
+ SlapReply *rs,
+ SQLHDBC dbh,
+ struct berval *ndn,
+ backsql_entryID *id,
+ int matched,
+ int muck )
+{
+ backsql_info *bi = op->o_bd->be_private;
+ SQLHSTMT sth = SQL_NULL_HSTMT;
+ BACKSQL_ROW_NTS row = { 0 };
+ RETCODE rc;
+ int res;
+ struct berval realndn = BER_BVNULL;
+
+ /* TimesTen */
+ char upperdn[ BACKSQL_MAX_DN_LEN + 1 ];
+ struct berval tbbDN;
+ int i, j;
+
+ /*
+ * NOTE: id can be NULL; in this case, the function
+ * simply checks whether the DN can be successfully
+ * turned into an ID, returning LDAP_SUCCESS for
+ * positive cases, or the most appropriate error
+ */
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_dn2id(\"%s\")%s%s\n",
+ ndn->bv_val, id == NULL ? " (no ID expected)" : "",
+ matched ? " matched expected" : "" );
+
+ if ( id ) {
+ /* NOTE: trap inconsistencies */
+ assert( BER_BVISNULL( &id->eid_ndn ) );
+ }
+
+ if ( ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_dn2id(\"%s\"): DN length=%ld "
+ "exceeds max DN length %d:\n",
+ ndn->bv_val, ndn->bv_len, BACKSQL_MAX_DN_LEN );
+ return LDAP_OTHER;
+ }
+
+ /* return baseObject if available and matches */
+ /* FIXME: if ndn is already mucked, we cannot check this */
+ if ( bi->sql_baseObject != NULL &&
+ dn_match( ndn, &bi->sql_baseObject->e_nname ) )
+ {
+ if ( id != NULL ) {
+#ifdef BACKSQL_ARBITRARY_KEY
+ ber_dupbv_x( &id->eid_id, &backsql_baseObject_bv,
+ op->o_tmpmemctx );
+ ber_dupbv_x( &id->eid_keyval, &backsql_baseObject_bv,
+ op->o_tmpmemctx );
+#else /* ! BACKSQL_ARBITRARY_KEY */
+ id->eid_id = BACKSQL_BASEOBJECT_ID;
+ id->eid_keyval = BACKSQL_BASEOBJECT_KEYVAL;
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+ id->eid_oc_id = BACKSQL_BASEOBJECT_OC;
+
+ ber_dupbv_x( &id->eid_ndn, &bi->sql_baseObject->e_nname,
+ op->o_tmpmemctx );
+ ber_dupbv_x( &id->eid_dn, &bi->sql_baseObject->e_name,
+ op->o_tmpmemctx );
+
+ id->eid_next = NULL;
+ }
+
+ return LDAP_SUCCESS;
+ }
+
+ /* begin TimesTen */
+ assert( bi->sql_id_query != NULL );
+ Debug( LDAP_DEBUG_TRACE, " backsql_dn2id(\"%s\"): id_query \"%s\"\n",
+ ndn->bv_val, bi->sql_id_query );
+ rc = backsql_Prepare( dbh, &sth, bi->sql_id_query, 0 );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_dn2id(\"%s\"): "
+ "error preparing SQL:\n %s",
+ ndn->bv_val, bi->sql_id_query );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
+ res = LDAP_OTHER;
+ goto done;
+ }
+
+ realndn = *ndn;
+ if ( muck ) {
+ if ( backsql_api_dn2odbc( op, rs, &realndn ) ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_dn2id(\"%s\"): "
+ "backsql_api_dn2odbc(\"%s\") failed\n",
+ ndn->bv_val, realndn.bv_val );
+ res = LDAP_OTHER;
+ goto done;
+ }
+ }
+
+ if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) {
+ /*
+ * Prepare an upper cased, byte reversed version
+ * that can be searched using indexes
+ */
+
+ for ( i = 0, j = realndn.bv_len - 1; realndn.bv_val[ i ]; i++, j--)
+ {
+ upperdn[ i ] = realndn.bv_val[ j ];
+ }
+ upperdn[ i ] = '\0';
+ ldap_pvt_str2upper( upperdn );
+
+ Debug( LDAP_DEBUG_TRACE, " backsql_dn2id(\"%s\"): "
+ "upperdn=\"%s\"\n",
+ ndn->bv_val, upperdn );
+ ber_str2bv( upperdn, 0, 0, &tbbDN );
+
+ } else {
+ if ( BACKSQL_USE_REVERSE_DN( bi ) ) {
+ AC_MEMCPY( upperdn, realndn.bv_val, realndn.bv_len + 1 );
+ ldap_pvt_str2upper( upperdn );
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_dn2id(\"%s\"): "
+ "upperdn=\"%s\"\n",
+ ndn->bv_val, upperdn );
+ ber_str2bv( upperdn, 0, 0, &tbbDN );
+
+ } else {
+ tbbDN = realndn;
+ }
+ }
+
+ rc = backsql_BindParamBerVal( sth, 1, SQL_PARAM_INPUT, &tbbDN );
+ if ( rc != SQL_SUCCESS) {
+ /* end TimesTen */
+ Debug( LDAP_DEBUG_TRACE, " backsql_dn2id(\"%s\"): "
+ "error binding dn=\"%s\" parameter:\n",
+ ndn->bv_val, tbbDN.bv_val );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
+ res = LDAP_OTHER;
+ goto done;
+ }
+
+ rc = SQLExecute( sth );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_dn2id(\"%s\"): "
+ "error executing query (\"%s\", \"%s\"):\n",
+ ndn->bv_val, bi->sql_id_query, tbbDN.bv_val );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
+ res = LDAP_OTHER;
+ goto done;
+ }
+
+ backsql_BindRowAsStrings_x( sth, &row, op->o_tmpmemctx );
+ rc = SQLFetch( sth );
+ if ( BACKSQL_SUCCESS( rc ) ) {
+#ifdef LDAP_DEBUG
+ Debug(LDAP_DEBUG_TRACE,
+ " backsql_dn2id(\"%s\"): id=%s keyval=%s oc_id=%s dn=%s\n",
+ ndn->bv_val, row.cols[0], row.cols[1], row.cols[2],
+ row.cols[3] );
+#endif /* LDAP_DEBUG */
+
+ res = LDAP_SUCCESS;
+ if ( id != NULL ) {
+ struct berval dn;
+
+ id->eid_next = NULL;
+
+#ifdef BACKSQL_ARBITRARY_KEY
+ ber_str2bv_x( row.cols[ 0 ], 0, 1, &id->eid_id,
+ op->o_tmpmemctx );
+ ber_str2bv_x( row.cols[ 1 ], 0, 1, &id->eid_keyval,
+ op->o_tmpmemctx );
+#else /* ! BACKSQL_ARBITRARY_KEY */
+ if ( BACKSQL_STR2ID( &id->eid_id, row.cols[ 0 ], 0 ) != 0 ) {
+ res = LDAP_OTHER;
+ goto done;
+ }
+ if ( BACKSQL_STR2ID( &id->eid_keyval, row.cols[ 1 ], 0 ) != 0 ) {
+ res = LDAP_OTHER;
+ goto done;
+ }
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+ if ( BACKSQL_STR2ID( &id->eid_oc_id, row.cols[ 2 ], 0 ) != 0 ) {
+ res = LDAP_OTHER;
+ goto done;
+ }
+
+ ber_str2bv( row.cols[ 3 ], 0, 0, &dn );
+
+ if ( backsql_api_odbc2dn( op, rs, &dn ) ) {
+ res = LDAP_OTHER;
+ goto done;
+ }
+
+ res = dnPrettyNormal( NULL, &dn,
+ &id->eid_dn, &id->eid_ndn,
+ op->o_tmpmemctx );
+ if ( res != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_dn2id(\"%s\"): "
+ "dnPrettyNormal failed (%d: %s)\n",
+ realndn.bv_val, res,
+ ldap_err2string( res ) );
+
+ /* cleanup... */
+ (void)backsql_free_entryID( id, 0, op->o_tmpmemctx );
+ }
+
+ if ( dn.bv_val != row.cols[ 3 ] ) {
+ free( dn.bv_val );
+ }
+ }
+
+ } else {
+ res = LDAP_NO_SUCH_OBJECT;
+ if ( matched ) {
+ struct berval pdn = *ndn;
+
+ /*
+ * Look for matched
+ */
+ rs->sr_matched = NULL;
+ while ( !be_issuffix( op->o_bd, &pdn ) ) {
+ char *matchedDN = NULL;
+
+ dnParent( &pdn, &pdn );
+
+ /*
+ * Empty DN ("") defaults to LDAP_SUCCESS
+ */
+ rs->sr_err = backsql_dn2id( op, rs, dbh, &pdn, id, 0, 1 );
+ switch ( rs->sr_err ) {
+ case LDAP_NO_SUCH_OBJECT:
+ /* try another one */
+ break;
+
+ case LDAP_SUCCESS:
+ matchedDN = pdn.bv_val;
+ /* fail over to next case */
+
+ default:
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_matched = matchedDN;
+ goto done;
+ }
+ }
+ }
+ }
+
+done:;
+ backsql_FreeRow_x( &row, op->o_tmpmemctx );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<==backsql_dn2id(\"%s\"): err=%d\n",
+ ndn->bv_val, res );
+ if ( sth != SQL_NULL_HSTMT ) {
+ SQLFreeStmt( sth, SQL_DROP );
+ }
+
+ if ( !BER_BVISNULL( &realndn ) && realndn.bv_val != ndn->bv_val ) {
+ ch_free( realndn.bv_val );
+ }
+
+ return res;
+}
+
+int
+backsql_count_children(
+ Operation *op,
+ SQLHDBC dbh,
+ struct berval *dn,
+ unsigned long *nchildren )
+{
+ backsql_info *bi = (backsql_info *)op->o_bd->be_private;
+ SQLHSTMT sth = SQL_NULL_HSTMT;
+ BACKSQL_ROW_NTS row;
+ RETCODE rc;
+ int res = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_count_children(): dn=\"%s\"\n",
+ dn->bv_val );
+
+ if ( dn->bv_len > BACKSQL_MAX_DN_LEN ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "backsql_count_children(): DN \"%s\" (%ld bytes) "
+ "exceeds max DN length (%d):\n",
+ dn->bv_val, dn->bv_len, BACKSQL_MAX_DN_LEN );
+ return LDAP_OTHER;
+ }
+
+ /* begin TimesTen */
+ assert( bi->sql_has_children_query != NULL );
+ Debug(LDAP_DEBUG_TRACE, "children id query \"%s\"\n",
+ bi->sql_has_children_query );
+ rc = backsql_Prepare( dbh, &sth, bi->sql_has_children_query, 0 );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "backsql_count_children(): error preparing SQL:\n%s",
+ bi->sql_has_children_query );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+ return LDAP_OTHER;
+ }
+
+ rc = backsql_BindParamBerVal( sth, 1, SQL_PARAM_INPUT, dn );
+ if ( rc != SQL_SUCCESS) {
+ /* end TimesTen */
+ Debug( LDAP_DEBUG_TRACE, "backsql_count_children(): "
+ "error binding dn=\"%s\" parameter:\n",
+ dn->bv_val );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+ return LDAP_OTHER;
+ }
+
+ rc = SQLExecute( sth );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_count_children(): "
+ "error executing query (\"%s\", \"%s\"):\n",
+ bi->sql_has_children_query, dn->bv_val );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+ return LDAP_OTHER;
+ }
+
+ backsql_BindRowAsStrings_x( sth, &row, op->o_tmpmemctx );
+
+ rc = SQLFetch( sth );
+ if ( BACKSQL_SUCCESS( rc ) ) {
+ char *end;
+
+ *nchildren = strtol( row.cols[ 0 ], &end, 0 );
+ if ( end == row.cols[ 0 ] ) {
+ res = LDAP_OTHER;
+
+ } else {
+ switch ( end[ 0 ] ) {
+ case '\0':
+ break;
+
+ case '.': {
+ unsigned long ul;
+
+ /* FIXME: braindead RDBMSes return
+ * a fractional number from COUNT!
+ */
+ if ( lutil_atoul( &ul, end + 1 ) != 0 || ul != 0 ) {
+ res = LDAP_OTHER;
+ }
+ } break;
+
+ default:
+ res = LDAP_OTHER;
+ }
+ }
+
+ } else {
+ res = LDAP_OTHER;
+ }
+ backsql_FreeRow_x( &row, op->o_tmpmemctx );
+
+ SQLFreeStmt( sth, SQL_DROP );
+
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_count_children(): %lu\n",
+ *nchildren );
+
+ return res;
+}
+
+int
+backsql_has_children(
+ Operation *op,
+ SQLHDBC dbh,
+ struct berval *dn )
+{
+ unsigned long nchildren;
+ int rc;
+
+ rc = backsql_count_children( op, dbh, dn, &nchildren );
+
+ if ( rc == LDAP_SUCCESS ) {
+ return nchildren > 0 ? LDAP_COMPARE_TRUE : LDAP_COMPARE_FALSE;
+ }
+
+ return rc;
+}
+
+static int
+backsql_get_attr_vals( void *v_at, void *v_bsi )
+{
+ backsql_at_map_rec *at = v_at;
+ backsql_srch_info *bsi = v_bsi;
+ backsql_info *bi;
+ RETCODE rc;
+ SQLHSTMT sth = SQL_NULL_HSTMT;
+ BACKSQL_ROW_NTS row;
+ unsigned long i,
+ k = 0,
+ oldcount = 0,
+ res = 0;
+#ifdef BACKSQL_COUNTQUERY
+ unsigned count,
+ j,
+ append = 0;
+ SQLLEN countsize = sizeof( count );
+ Attribute *attr = NULL;
+
+ slap_mr_normalize_func *normfunc = NULL;
+#endif /* BACKSQL_COUNTQUERY */
+#ifdef BACKSQL_PRETTY_VALIDATE
+ slap_syntax_validate_func *validate = NULL;
+ slap_syntax_transform_func *pretty = NULL;
+#endif /* BACKSQL_PRETTY_VALIDATE */
+
+ assert( at != NULL );
+ assert( bsi != NULL );
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_get_attr_vals(): "
+ "oc=\"%s\" attr=\"%s\" keyval=" BACKSQL_IDFMT "\n",
+ BACKSQL_OC_NAME( bsi->bsi_oc ), at->bam_ad->ad_cname.bv_val,
+ BACKSQL_IDARG(bsi->bsi_c_eid->eid_keyval) );
+
+ bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
+
+#ifdef BACKSQL_PRETTY_VALIDATE
+ validate = at->bam_true_ad->ad_type->sat_syntax->ssyn_validate;
+ pretty = at->bam_true_ad->ad_type->sat_syntax->ssyn_pretty;
+
+ if ( validate == NULL && pretty == NULL ) {
+ return 1;
+ }
+#endif /* BACKSQL_PRETTY_VALIDATE */
+
+#ifdef BACKSQL_COUNTQUERY
+ if ( at->bam_true_ad->ad_type->sat_equality ) {
+ normfunc = at->bam_true_ad->ad_type->sat_equality->smr_normalize;
+ }
+
+ /* Count how many rows will be returned. This avoids memory
+ * fragmentation that can result from loading the values in
+ * one by one and using realloc()
+ */
+ rc = backsql_Prepare( bsi->bsi_dbh, &sth, at->bam_countquery, 0 );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_get_attr_vals(): "
+ "error preparing count query: %s\n",
+ at->bam_countquery );
+ backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
+ return 1;
+ }
+
+ rc = backsql_BindParamID( sth, 1, SQL_PARAM_INPUT,
+ &bsi->bsi_c_eid->eid_keyval );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_get_attr_vals(): "
+ "error binding key value parameter\n" );
+ SQLFreeStmt( sth, SQL_DROP );
+ return 1;
+ }
+
+ rc = SQLExecute( sth );
+ if ( ! BACKSQL_SUCCESS( rc ) ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_get_attr_vals(): "
+ "error executing attribute count query '%s'\n",
+ at->bam_countquery );
+ backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+ return 1;
+ }
+
+ SQLBindCol( sth, (SQLUSMALLINT)1, SQL_C_LONG,
+ (SQLPOINTER)&count,
+ (SQLINTEGER)sizeof( count ),
+ &countsize );
+
+ rc = SQLFetch( sth );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_get_attr_vals(): "
+ "error fetch results of count query: %s\n",
+ at->bam_countquery );
+ backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+ return 1;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "backsql_get_attr_vals(): "
+ "number of values in query: %u\n", count );
+ SQLFreeStmt( sth, SQL_DROP );
+ if ( count == 0 ) {
+ return 1;
+ }
+
+ attr = attr_find( bsi->bsi_e->e_attrs, at->bam_true_ad );
+ if ( attr != NULL ) {
+ BerVarray tmp;
+
+ if ( attr->a_vals != NULL ) {
+ oldcount = attr->a_numvals;
+ }
+
+ tmp = ch_realloc( attr->a_vals, ( oldcount + count + 1 ) * sizeof( struct berval ) );
+ if ( tmp == NULL ) {
+ return 1;
+ }
+ attr->a_vals = tmp;
+ memset( &attr->a_vals[ oldcount ], 0, ( count + 1 ) * sizeof( struct berval ) );
+
+ if ( normfunc ) {
+ tmp = ch_realloc( attr->a_nvals, ( oldcount + count + 1 ) * sizeof( struct berval ) );
+ if ( tmp == NULL ) {
+ return 1;
+ }
+ attr->a_nvals = tmp;
+ memset( &attr->a_nvals[ oldcount ], 0, ( count + 1 ) * sizeof( struct berval ) );
+
+ } else {
+ attr->a_nvals = attr->a_vals;
+ }
+ attr->a_numvals += count;
+
+ } else {
+ append = 1;
+
+ /* Make space for the array of values */
+ attr = attr_alloc( at->bam_true_ad );
+ attr->a_numvals = count;
+ attr->a_vals = ch_calloc( count + 1, sizeof( struct berval ) );
+ if ( attr->a_vals == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "Out of memory!\n" );
+ ch_free( attr );
+ return 1;
+ }
+ if ( normfunc ) {
+ attr->a_nvals = ch_calloc( count + 1, sizeof( struct berval ) );
+ if ( attr->a_nvals == NULL ) {
+ ch_free( attr->a_vals );
+ ch_free( attr );
+ return 1;
+
+ }
+
+ } else {
+ attr->a_nvals = attr->a_vals;
+ }
+ }
+#endif /* BACKSQL_COUNTQUERY */
+
+ rc = backsql_Prepare( bsi->bsi_dbh, &sth, at->bam_query, 0 );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_get_attr_vals(): "
+ "error preparing query: %s\n", at->bam_query );
+ backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
+#ifdef BACKSQL_COUNTQUERY
+ if ( append ) {
+ attr_free( attr );
+ }
+#endif /* BACKSQL_COUNTQUERY */
+ return 1;
+ }
+
+ rc = backsql_BindParamID( sth, 1, SQL_PARAM_INPUT,
+ &bsi->bsi_c_eid->eid_keyval );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_get_attr_vals(): "
+ "error binding key value parameter\n" );
+#ifdef BACKSQL_COUNTQUERY
+ if ( append ) {
+ attr_free( attr );
+ }
+#endif /* BACKSQL_COUNTQUERY */
+ return 1;
+ }
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "backsql_get_attr_vals(): "
+ "query=\"%s\" keyval=" BACKSQL_IDFMT "\n", at->bam_query,
+ BACKSQL_IDARG(bsi->bsi_c_eid->eid_keyval) );
+#endif /* BACKSQL_TRACE */
+
+ rc = SQLExecute( sth );
+ if ( ! BACKSQL_SUCCESS( rc ) ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_get_attr_vals(): "
+ "error executing attribute query \"%s\"\n",
+ at->bam_query );
+ backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+#ifdef BACKSQL_COUNTQUERY
+ if ( append ) {
+ attr_free( attr );
+ }
+#endif /* BACKSQL_COUNTQUERY */
+ return 1;
+ }
+
+ backsql_BindRowAsStrings_x( sth, &row, bsi->bsi_op->o_tmpmemctx );
+#ifdef BACKSQL_COUNTQUERY
+ j = oldcount;
+#endif /* BACKSQL_COUNTQUERY */
+ for ( rc = SQLFetch( sth ), k = 0;
+ BACKSQL_SUCCESS( rc );
+ rc = SQLFetch( sth ), k++ )
+ {
+ for ( i = 0; i < (unsigned long)row.ncols; i++ ) {
+
+ if ( row.value_len[ i ] > 0 ) {
+ struct berval bv;
+ int retval;
+#ifdef BACKSQL_TRACE
+ AttributeDescription *ad = NULL;
+ const char *text;
+
+ retval = slap_bv2ad( &row.col_names[ i ], &ad, &text );
+ if ( retval != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "==>backsql_get_attr_vals(\"%s\"): "
+ "unable to find AttributeDescription %s "
+ "in schema (%d)\n",
+ bsi->bsi_e->e_name.bv_val,
+ row.col_names[ i ].bv_val, retval );
+ res = 1;
+ goto done;
+ }
+
+ if ( ad != at->bam_ad ) {
+ Debug( LDAP_DEBUG_ANY,
+ "==>backsql_get_attr_vals(\"%s\"): "
+ "column name %s differs from "
+ "AttributeDescription %s\n",
+ bsi->bsi_e->e_name.bv_val,
+ ad->ad_cname.bv_val,
+ at->bam_ad->ad_cname.bv_val );
+ res = 1;
+ goto done;
+ }
+#endif /* BACKSQL_TRACE */
+
+ /* ITS#3386, ITS#3113 - 20070308
+ * If a binary is fetched?
+ * must use the actual size read
+ * from the database.
+ */
+ if ( BACKSQL_IS_BINARY( row.col_type[ i ] ) ) {
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_ANY,
+ "==>backsql_get_attr_vals(\"%s\"): "
+ "column name %s: data is binary; "
+ "using database size %ld\n",
+ bsi->bsi_e->e_name.bv_val,
+ ad->ad_cname.bv_val,
+ row.value_len[ i ] );
+#endif /* BACKSQL_TRACE */
+ bv.bv_val = row.cols[ i ];
+ bv.bv_len = row.value_len[ i ];
+
+ } else {
+ ber_str2bv( row.cols[ i ], 0, 0, &bv );
+ }
+
+#ifdef BACKSQL_PRETTY_VALIDATE
+ if ( pretty ) {
+ struct berval pbv;
+
+ retval = pretty( at->bam_true_ad->ad_type->sat_syntax,
+ &bv, &pbv, bsi->bsi_op->o_tmpmemctx );
+ bv = pbv;
+
+ } else {
+ retval = validate( at->bam_true_ad->ad_type->sat_syntax,
+ &bv );
+ }
+
+ if ( retval != LDAP_SUCCESS ) {
+ /* FIXME: we're ignoring invalid values,
+ * but we're accepting the attributes;
+ * should we fail at all? */
+ Debug(LDAP_DEBUG_TRACE,
+ "==>backsql_get_attr_vals(\"%s\"): " "unable to %s value #%lu " "of AttributeDescription %s (%d)\n",
+ bsi->bsi_e->e_name.bv_val,
+ pretty ? "prettify" : "validate",
+ k - oldcount,
+ at->bam_ad->ad_cname.bv_val,
+ retval );
+ continue;
+ }
+#endif /* BACKSQL_PRETTY_VALIDATE */
+
+#ifndef BACKSQL_COUNTQUERY
+ (void)backsql_entry_addattr( bsi->bsi_e,
+ at->bam_true_ad, &bv,
+ bsi->bsi_op->o_tmpmemctx );
+
+#else /* BACKSQL_COUNTQUERY */
+ if ( normfunc ) {
+ struct berval nbv;
+
+ retval = (*normfunc)( SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ at->bam_true_ad->ad_type->sat_syntax,
+ at->bam_true_ad->ad_type->sat_equality,
+ &bv, &nbv,
+ bsi->bsi_op->o_tmpmemctx );
+
+ if ( retval != LDAP_SUCCESS ) {
+ /* FIXME: we're ignoring invalid values,
+ * but we're accepting the attributes;
+ * should we fail at all? */
+ Debug(LDAP_DEBUG_TRACE,
+ "==>backsql_get_attr_vals(\"%s\"): " "unable to normalize value #%lu " "of AttributeDescription %s (%d)\n",
+ bsi->bsi_e->e_name.bv_val,
+ k - oldcount,
+ at->bam_ad->ad_cname.bv_val,
+ retval );
+
+#ifdef BACKSQL_PRETTY_VALIDATE
+ if ( pretty ) {
+ bsi->bsi_op->o_tmpfree( bv.bv_val,
+ bsi->bsi_op->o_tmpmemctx );
+ }
+#endif /* BACKSQL_PRETTY_VALIDATE */
+
+ continue;
+ }
+ ber_dupbv( &attr->a_nvals[ j ], &nbv );
+ bsi->bsi_op->o_tmpfree( nbv.bv_val,
+ bsi->bsi_op->o_tmpmemctx );
+ }
+
+ ber_dupbv( &attr->a_vals[ j ], &bv );
+
+ assert( j < oldcount + count );
+ j++;
+#endif /* BACKSQL_COUNTQUERY */
+
+#ifdef BACKSQL_PRETTY_VALIDATE
+ if ( pretty ) {
+ bsi->bsi_op->o_tmpfree( bv.bv_val,
+ bsi->bsi_op->o_tmpmemctx );
+ }
+#endif /* BACKSQL_PRETTY_VALIDATE */
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "prec=%d\n",
+ (int)row.col_prec[ i ] );
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "NULL value "
+ "in this row for attribute \"%s\"\n",
+ row.col_names[ i ].bv_val );
+#endif /* BACKSQL_TRACE */
+ }
+ }
+ }
+
+#ifdef BACKSQL_COUNTQUERY
+ if ( BER_BVISNULL( &attr->a_vals[ 0 ] ) ) {
+ /* don't leave around attributes with no values */
+ attr_free( attr );
+
+ } else if ( append ) {
+ Attribute **ap;
+
+ for ( ap = &bsi->bsi_e->e_attrs; (*ap) != NULL; ap = &(*ap)->a_next )
+ /* goto last */ ;
+ *ap = attr;
+ }
+#endif /* BACKSQL_COUNTQUERY */
+
+ SQLFreeStmt( sth, SQL_DROP );
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_get_attr_vals()\n" );
+
+ if ( at->bam_next ) {
+ res = backsql_get_attr_vals( at->bam_next, v_bsi );
+ } else {
+ res = 1;
+ }
+
+#ifdef BACKSQL_TRACE
+done:;
+#endif /* BACKSQL_TRACE */
+ backsql_FreeRow_x( &row, bsi->bsi_op->o_tmpmemctx );
+
+ return res;
+}
+
+int
+backsql_id2entry( backsql_srch_info *bsi, backsql_entryID *eid )
+{
+ Operation *op = bsi->bsi_op;
+ backsql_info *bi = (backsql_info *)op->o_bd->be_private;
+ int i;
+ int rc;
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_id2entry()\n" );
+
+ assert( bsi->bsi_e != NULL );
+
+ memset( bsi->bsi_e, 0, sizeof( Entry ) );
+
+ if ( bi->sql_baseObject && BACKSQL_IS_BASEOBJECT_ID( &eid->eid_id ) ) {
+ (void)entry_dup2( bsi->bsi_e, bi->sql_baseObject );
+ goto done;
+ }
+
+ bsi->bsi_e->e_attrs = NULL;
+ bsi->bsi_e->e_private = NULL;
+
+ if ( eid->eid_oc == NULL ) {
+ eid->eid_oc = backsql_id2oc( bsi->bsi_op->o_bd->be_private,
+ eid->eid_oc_id );
+ if ( eid->eid_oc == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "backsql_id2entry(): unable to fetch objectClass with id=" BACKSQL_IDNUMFMT " for entry id=" BACKSQL_IDFMT " dn=\"%s\"\n",
+ eid->eid_oc_id, BACKSQL_IDARG(eid->eid_id),
+ eid->eid_dn.bv_val );
+ return LDAP_OTHER;
+ }
+ }
+ bsi->bsi_oc = eid->eid_oc;
+ bsi->bsi_c_eid = eid;
+
+ ber_dupbv_x( &bsi->bsi_e->e_name, &eid->eid_dn, op->o_tmpmemctx );
+ ber_dupbv_x( &bsi->bsi_e->e_nname, &eid->eid_ndn, op->o_tmpmemctx );
+
+#ifndef BACKSQL_ARBITRARY_KEY
+ /* FIXME: unused */
+ bsi->bsi_e->e_id = eid->eid_id;
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+
+ rc = attr_merge_normalize_one( bsi->bsi_e,
+ slap_schema.si_ad_objectClass,
+ &bsi->bsi_oc->bom_oc->soc_cname,
+ bsi->bsi_op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ backsql_entry_clean( op, bsi->bsi_e );
+ return rc;
+ }
+
+ if ( bsi->bsi_attrs == NULL || ( bsi->bsi_flags & BSQL_SF_ALL_USER ) )
+ {
+ Debug( LDAP_DEBUG_TRACE, "backsql_id2entry(): "
+ "retrieving all attributes\n" );
+ ldap_avl_apply( bsi->bsi_oc->bom_attrs, backsql_get_attr_vals,
+ bsi, 0, AVL_INORDER );
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "backsql_id2entry(): "
+ "custom attribute list\n" );
+ for ( i = 0; !BER_BVISNULL( &bsi->bsi_attrs[ i ].an_name ); i++ ) {
+ backsql_at_map_rec **vat;
+ AttributeName *an = &bsi->bsi_attrs[ i ];
+ int j;
+
+ /* if one of the attributes listed here is
+ * a subtype of another, it must be ignored,
+ * because subtypes are already dealt with
+ * by backsql_supad2at()
+ */
+ for ( j = 0; !BER_BVISNULL( &bsi->bsi_attrs[ j ].an_name ); j++ ) {
+ /* skip self */
+ if ( j == i ) {
+ continue;
+ }
+
+ /* skip subtypes */
+ if ( is_at_subtype( an->an_desc->ad_type,
+ bsi->bsi_attrs[ j ].an_desc->ad_type ) )
+ {
+ goto next;
+ }
+ }
+
+ rc = backsql_supad2at( bsi->bsi_oc, an->an_desc, &vat );
+ if ( rc != 0 || vat == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_id2entry(): "
+ "attribute \"%s\" is not defined "
+ "for objectclass \"%s\"\n",
+ an->an_name.bv_val,
+ BACKSQL_OC_NAME( bsi->bsi_oc ) );
+ continue;
+ }
+
+ for ( j = 0; vat[j]; j++ ) {
+ backsql_get_attr_vals( vat[j], bsi );
+ }
+
+ ch_free( vat );
+
+next:;
+ }
+ }
+
+ if ( bsi->bsi_flags & BSQL_SF_RETURN_ENTRYUUID ) {
+ Attribute *a_entryUUID,
+ **ap;
+
+ a_entryUUID = backsql_operational_entryUUID( bi, eid );
+ if ( a_entryUUID != NULL ) {
+ for ( ap = &bsi->bsi_e->e_attrs;
+ *ap;
+ ap = &(*ap)->a_next );
+
+ *ap = a_entryUUID;
+ }
+ }
+
+ if ( ( bsi->bsi_flags & BSQL_SF_ALL_OPER )
+ || an_find( bsi->bsi_attrs, slap_bv_all_operational_attrs )
+ || an_find( bsi->bsi_attrs, &slap_schema.si_ad_structuralObjectClass->ad_cname ) )
+ {
+ ObjectClass *soc = NULL;
+
+ if ( BACKSQL_CHECK_SCHEMA( bi ) ) {
+ Attribute *a;
+ const char *text = NULL;
+ char textbuf[ 1024 ];
+ size_t textlen = sizeof( textbuf );
+ struct berval bv[ 2 ],
+ *nvals;
+ int rc = LDAP_SUCCESS;
+
+ a = attr_find( bsi->bsi_e->e_attrs,
+ slap_schema.si_ad_objectClass );
+ if ( a != NULL ) {
+ nvals = a->a_nvals;
+
+ } else {
+ bv[ 0 ] = bsi->bsi_oc->bom_oc->soc_cname;
+ BER_BVZERO( &bv[ 1 ] );
+ nvals = bv;
+ }
+
+ rc = structural_class( nvals, &soc, NULL,
+ &text, textbuf, textlen, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_id2entry(%s): "
+ "structural_class() failed %d (%s)\n",
+ bsi->bsi_e->e_name.bv_val,
+ rc, text ? text : "" );
+ backsql_entry_clean( op, bsi->bsi_e );
+ return rc;
+ }
+
+ if ( !bvmatch( &soc->soc_cname, &bsi->bsi_oc->bom_oc->soc_cname ) ) {
+ if ( !is_object_subclass( bsi->bsi_oc->bom_oc, soc ) ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_id2entry(%s): "
+ "computed structuralObjectClass %s "
+ "does not match objectClass %s associated "
+ "to entry\n",
+ bsi->bsi_e->e_name.bv_val, soc->soc_cname.bv_val,
+ bsi->bsi_oc->bom_oc->soc_cname.bv_val );
+ backsql_entry_clean( op, bsi->bsi_e );
+ return rc;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "backsql_id2entry(%s): "
+ "computed structuralObjectClass %s "
+ "is subclass of objectClass %s associated "
+ "to entry\n",
+ bsi->bsi_e->e_name.bv_val, soc->soc_cname.bv_val,
+ bsi->bsi_oc->bom_oc->soc_cname.bv_val );
+ }
+
+ } else {
+ soc = bsi->bsi_oc->bom_oc;
+ }
+
+ rc = attr_merge_normalize_one( bsi->bsi_e,
+ slap_schema.si_ad_structuralObjectClass,
+ &soc->soc_cname,
+ bsi->bsi_op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ backsql_entry_clean( op, bsi->bsi_e );
+ return rc;
+ }
+ }
+
+done:;
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_id2entry()\n" );
+
+ return LDAP_SUCCESS;
+}
+
diff --git a/servers/slapd/back-sql/init.c b/servers/slapd/back-sql/init.c
new file mode 100644
index 0000000..1b45f8f
--- /dev/null
+++ b/servers/slapd/back-sql/init.c
@@ -0,0 +1,661 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 Dmitry Kovalev.
+ * Portions Copyright 2002 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Dmitry Kovalev for inclusion
+ * by OpenLDAP Software. Additional significant contributors include
+ * Pierangelo Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "ac/string.h"
+
+#include "slap.h"
+#include "slap-config.h"
+#include "proto-sql.h"
+
+int
+sql_back_initialize(
+ BackendInfo *bi )
+{
+ static char *controls[] = {
+ LDAP_CONTROL_ASSERT,
+ LDAP_CONTROL_MANAGEDSAIT,
+ LDAP_CONTROL_NOOP,
+#ifdef SLAP_CONTROL_X_TREE_DELETE
+ SLAP_CONTROL_X_TREE_DELETE,
+#endif /* SLAP_CONTROL_X_TREE_DELETE */
+#ifndef BACKSQL_ARBITRARY_KEY
+ LDAP_CONTROL_PAGEDRESULTS,
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+ NULL
+ };
+ int rc;
+
+ bi->bi_controls = controls;
+
+ bi->bi_flags |=
+#if 0
+ SLAP_BFLAG_INCREMENT |
+#endif
+ SLAP_BFLAG_REFERRALS;
+
+ Debug( LDAP_DEBUG_TRACE,"==>sql_back_initialize()\n" );
+
+ bi->bi_db_init = backsql_db_init;
+ bi->bi_db_config = config_generic_wrapper;
+ bi->bi_db_open = backsql_db_open;
+ bi->bi_db_close = backsql_db_close;
+ bi->bi_db_destroy = backsql_db_destroy;
+
+ bi->bi_op_abandon = 0;
+ bi->bi_op_compare = backsql_compare;
+ bi->bi_op_bind = backsql_bind;
+ bi->bi_op_unbind = 0;
+ bi->bi_op_search = backsql_search;
+ bi->bi_op_modify = backsql_modify;
+ bi->bi_op_modrdn = backsql_modrdn;
+ bi->bi_op_add = backsql_add;
+ bi->bi_op_delete = backsql_delete;
+
+ bi->bi_chk_referrals = 0;
+ bi->bi_operational = backsql_operational;
+ bi->bi_entry_get_rw = backsql_entry_get;
+ bi->bi_entry_release_rw = backsql_entry_release;
+
+ bi->bi_connection_init = 0;
+
+ rc = backsql_init_cf( bi );
+ Debug( LDAP_DEBUG_TRACE,"<==sql_back_initialize()\n" );
+ return rc;
+}
+
+int
+backsql_destroy(
+ BackendInfo *bi )
+{
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_destroy()\n" );
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_destroy()\n" );
+ return 0;
+}
+
+int
+backsql_db_init(
+ BackendDB *bd,
+ ConfigReply *cr )
+{
+ backsql_info *bi;
+ int rc = 0;
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_db_init()\n" );
+
+ bi = (backsql_info *)ch_calloc( 1, sizeof( backsql_info ) );
+ ldap_pvt_thread_mutex_init( &bi->sql_dbconn_mutex );
+ ldap_pvt_thread_mutex_init( &bi->sql_schema_mutex );
+
+ if ( backsql_init_db_env( bi ) != SQL_SUCCESS ) {
+ rc = -1;
+ }
+
+ bd->be_private = bi;
+ bd->be_cf_ocs = bd->bd_info->bi_cf_ocs;
+
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_db_init()\n" );
+
+ return rc;
+}
+
+int
+backsql_db_destroy(
+ BackendDB *bd,
+ ConfigReply *cr )
+{
+ backsql_info *bi = (backsql_info*)bd->be_private;
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_db_destroy()\n" );
+
+ backsql_free_db_env( bi );
+ ldap_pvt_thread_mutex_destroy( &bi->sql_dbconn_mutex );
+ backsql_destroy_schema_map( bi );
+ ldap_pvt_thread_mutex_destroy( &bi->sql_schema_mutex );
+
+ if ( bi->sql_dbname ) {
+ ch_free( bi->sql_dbname );
+ }
+ if ( bi->sql_dbuser ) {
+ ch_free( bi->sql_dbuser );
+ }
+ if ( bi->sql_dbpasswd ) {
+ ch_free( bi->sql_dbpasswd );
+ }
+ if ( bi->sql_dbhost ) {
+ ch_free( bi->sql_dbhost );
+ }
+ if ( bi->sql_upper_func.bv_val ) {
+ ch_free( bi->sql_upper_func.bv_val );
+ ch_free( bi->sql_upper_func_open.bv_val );
+ ch_free( bi->sql_upper_func_close.bv_val );
+ }
+ if ( bi->sql_concat_func ) {
+ ber_bvarray_free( bi->sql_concat_func );
+ }
+ if ( !BER_BVISNULL( &bi->sql_strcast_func ) ) {
+ ch_free( bi->sql_strcast_func.bv_val );
+ }
+ if ( !BER_BVISNULL( &bi->sql_children_cond ) ) {
+ ch_free( bi->sql_children_cond.bv_val );
+ }
+ if ( !BER_BVISNULL( &bi->sql_dn_match_cond ) ) {
+ ch_free( bi->sql_dn_match_cond.bv_val );
+ }
+ if ( !BER_BVISNULL( &bi->sql_subtree_cond ) ) {
+ ch_free( bi->sql_subtree_cond.bv_val );
+ }
+ if ( !BER_BVISNULL( &bi->sql_dn_oc_aliasing ) ) {
+ ch_free( bi->sql_dn_oc_aliasing.bv_val );
+ }
+ if ( bi->sql_oc_query ) {
+ ch_free( bi->sql_oc_query );
+ }
+ if ( bi->sql_at_query ) {
+ ch_free( bi->sql_at_query );
+ }
+ if ( bi->sql_id_query ) {
+ ch_free( bi->sql_id_query );
+ }
+ if ( bi->sql_has_children_query ) {
+ ch_free( bi->sql_has_children_query );
+ }
+ if ( bi->sql_insentry_stmt ) {
+ ch_free( bi->sql_insentry_stmt );
+ }
+ if ( bi->sql_delentry_stmt ) {
+ ch_free( bi->sql_delentry_stmt );
+ }
+ if ( bi->sql_renentry_stmt ) {
+ ch_free( bi->sql_renentry_stmt );
+ }
+ if ( bi->sql_delobjclasses_stmt ) {
+ ch_free( bi->sql_delobjclasses_stmt );
+ }
+ if ( !BER_BVISNULL( &bi->sql_aliasing ) ) {
+ ch_free( bi->sql_aliasing.bv_val );
+ }
+ if ( !BER_BVISNULL( &bi->sql_aliasing_quote ) ) {
+ ch_free( bi->sql_aliasing_quote.bv_val );
+ }
+
+ if ( bi->sql_anlist ) {
+ int i;
+
+ for ( i = 0; !BER_BVISNULL( &bi->sql_anlist[ i ].an_name ); i++ )
+ {
+ ch_free( bi->sql_anlist[ i ].an_name.bv_val );
+ }
+ ch_free( bi->sql_anlist );
+ }
+
+ if ( bi->sql_baseObject ) {
+ entry_free( bi->sql_baseObject );
+ }
+
+ ch_free( bi );
+
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_db_destroy()\n" );
+ return 0;
+}
+
+int
+backsql_db_open(
+ BackendDB *bd,
+ ConfigReply *cr )
+{
+ backsql_info *bi = (backsql_info*)bd->be_private;
+ struct berbuf bb = BB_NULL;
+
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+ Operation* op;
+ SQLHDBC dbh = SQL_NULL_HDBC;
+ void *thrctx = ldap_pvt_thread_pool_context();
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_db_open(): "
+ "testing RDBMS connection\n" );
+ if ( bi->sql_dbname == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "datasource name not specified "
+ "(use \"dbname\" directive in slapd.conf)\n" );
+ return 1;
+ }
+
+ if ( bi->sql_concat_func == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "concat func not specified (use \"concat_pattern\" "
+ "directive in slapd.conf)\n" );
+
+ if ( backsql_split_pattern( backsql_def_concat_func,
+ &bi->sql_concat_func, 2 ) ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "unable to parse pattern \"%s\"",
+ backsql_def_concat_func );
+ return 1;
+ }
+ }
+
+ /*
+ * see back-sql.h for default values
+ */
+ if ( BER_BVISNULL( &bi->sql_aliasing ) ) {
+ ber_str2bv( BACKSQL_ALIASING,
+ STRLENOF( BACKSQL_ALIASING ),
+ 1, &bi->sql_aliasing );
+ }
+
+ if ( BER_BVISNULL( &bi->sql_aliasing_quote ) ) {
+ ber_str2bv( BACKSQL_ALIASING_QUOTE,
+ STRLENOF( BACKSQL_ALIASING_QUOTE ),
+ 1, &bi->sql_aliasing_quote );
+ }
+
+ /*
+ * Prepare cast string as required
+ */
+ if ( bi->sql_upper_func.bv_val ) {
+ char buf[1024];
+
+ if ( BACKSQL_UPPER_NEEDS_CAST( bi ) ) {
+ snprintf( buf, sizeof( buf ),
+ "%s(cast (" /* ? as varchar(%d))) */ ,
+ bi->sql_upper_func.bv_val );
+ ber_str2bv( buf, 0, 1, &bi->sql_upper_func_open );
+
+ snprintf( buf, sizeof( buf ),
+ /* (cast(? */ " as varchar(%d)))",
+ BACKSQL_MAX_DN_LEN );
+ ber_str2bv( buf, 0, 1, &bi->sql_upper_func_close );
+
+ } else {
+ snprintf( buf, sizeof( buf ), "%s(" /* ?) */ ,
+ bi->sql_upper_func.bv_val );
+ ber_str2bv( buf, 0, 1, &bi->sql_upper_func_open );
+
+ ber_str2bv( /* (? */ ")", 0, 1, &bi->sql_upper_func_close );
+ }
+ }
+
+ /* normalize filter values only if necessary */
+ bi->sql_caseIgnoreMatch = mr_find( "caseIgnoreMatch" );
+ assert( bi->sql_caseIgnoreMatch != NULL );
+
+ bi->sql_telephoneNumberMatch = mr_find( "telephoneNumberMatch" );
+ assert( bi->sql_telephoneNumberMatch != NULL );
+
+ if ( bi->sql_dbuser == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "user name not specified "
+ "(use \"dbuser\" directive in slapd.conf)\n" );
+ return 1;
+ }
+
+ if ( BER_BVISNULL( &bi->sql_subtree_cond ) ) {
+ /*
+ * Prepare concat function for subtree search condition
+ */
+ struct berval concat;
+ struct berval values[] = {
+ BER_BVC( "'%'" ),
+ BER_BVC( "?" ),
+ BER_BVNULL
+ };
+ struct berbuf bb = BB_NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "subtree search SQL condition not specified "
+ "(use \"subtree_cond\" directive in slapd.conf); "
+ "preparing default\n" );
+
+ if ( backsql_prepare_pattern( bi->sql_concat_func, values,
+ &concat ) ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "unable to prepare CONCAT pattern for subtree search" );
+ return 1;
+ }
+
+ if ( bi->sql_upper_func.bv_val ) {
+
+ /*
+ * UPPER(ldap_entries.dn) LIKE UPPER(CONCAT('%',?))
+ */
+
+ backsql_strfcat_x( &bb, NULL, "blbbb",
+ &bi->sql_upper_func,
+ (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE " ),
+ "(ldap_entries.dn) LIKE ",
+ &bi->sql_upper_func_open,
+ &concat,
+ &bi->sql_upper_func_close );
+
+ } else {
+
+ /*
+ * ldap_entries.dn LIKE CONCAT('%',?)
+ */
+
+ backsql_strfcat_x( &bb, NULL, "lb",
+ (ber_len_t)STRLENOF( "ldap_entries.dn LIKE " ),
+ "ldap_entries.dn LIKE ",
+ &concat );
+ }
+
+ ch_free( concat.bv_val );
+
+ bi->sql_subtree_cond = bb.bb_val;
+
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "setting \"%s\" as default \"subtree_cond\"\n",
+ bi->sql_subtree_cond.bv_val );
+ }
+
+ if ( bi->sql_children_cond.bv_val == NULL ) {
+ /*
+ * Prepare concat function for children search condition
+ */
+ struct berval concat;
+ struct berval values[] = {
+ BER_BVC( "'%,'" ),
+ BER_BVC( "?" ),
+ BER_BVNULL
+ };
+ struct berbuf bb = BB_NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "children search SQL condition not specified "
+ "(use \"children_cond\" directive in slapd.conf); "
+ "preparing default\n" );
+
+ if ( backsql_prepare_pattern( bi->sql_concat_func, values,
+ &concat ) ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "unable to prepare CONCAT pattern for children search" );
+ return 1;
+ }
+
+ if ( bi->sql_upper_func.bv_val ) {
+
+ /*
+ * UPPER(ldap_entries.dn) LIKE UPPER(CONCAT('%,',?))
+ */
+
+ backsql_strfcat_x( &bb, NULL, "blbbb",
+ &bi->sql_upper_func,
+ (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE " ),
+ "(ldap_entries.dn) LIKE ",
+ &bi->sql_upper_func_open,
+ &concat,
+ &bi->sql_upper_func_close );
+
+ } else {
+
+ /*
+ * ldap_entries.dn LIKE CONCAT('%,',?)
+ */
+
+ backsql_strfcat_x( &bb, NULL, "lb",
+ (ber_len_t)STRLENOF( "ldap_entries.dn LIKE " ),
+ "ldap_entries.dn LIKE ",
+ &concat );
+ }
+
+ ch_free( concat.bv_val );
+
+ bi->sql_children_cond = bb.bb_val;
+
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "setting \"%s\" as default \"children_cond\"\n",
+ bi->sql_children_cond.bv_val );
+ }
+
+ if ( bi->sql_dn_match_cond.bv_val == NULL ) {
+ /*
+ * Prepare concat function for dn match search condition
+ */
+ struct berbuf bb = BB_NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "DN match search SQL condition not specified "
+ "(use \"dn_match_cond\" directive in slapd.conf); "
+ "preparing default\n" );
+
+ if ( bi->sql_upper_func.bv_val ) {
+
+ /*
+ * UPPER(ldap_entries.dn)=?
+ */
+
+ backsql_strfcat_x( &bb, NULL, "blbcb",
+ &bi->sql_upper_func,
+ (ber_len_t)STRLENOF( "(ldap_entries.dn)=" ),
+ "(ldap_entries.dn)=",
+ &bi->sql_upper_func_open,
+ '?',
+ &bi->sql_upper_func_close );
+
+ } else {
+
+ /*
+ * ldap_entries.dn=?
+ */
+
+ backsql_strfcat_x( &bb, NULL, "l",
+ (ber_len_t)STRLENOF( "ldap_entries.dn=?" ),
+ "ldap_entries.dn=?" );
+ }
+
+ bi->sql_dn_match_cond = bb.bb_val;
+
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "setting \"%s\" as default \"dn_match_cond\"\n",
+ bi->sql_dn_match_cond.bv_val );
+ }
+
+ if ( bi->sql_oc_query == NULL ) {
+ if ( BACKSQL_CREATE_NEEDS_SELECT( bi ) ) {
+ bi->sql_oc_query =
+ ch_strdup( backsql_def_needs_select_oc_query );
+
+ } else {
+ bi->sql_oc_query = ch_strdup( backsql_def_oc_query );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "objectclass mapping SQL statement not specified "
+ "(use \"oc_query\" directive in slapd.conf)\n" );
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "setting \"%s\" by default\n", bi->sql_oc_query );
+ }
+
+ if ( bi->sql_at_query == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "attribute mapping SQL statement not specified "
+ "(use \"at_query\" directive in slapd.conf)\n" );
+ Debug(LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "setting \"%s\" by default\n",
+ backsql_def_at_query );
+ bi->sql_at_query = ch_strdup( backsql_def_at_query );
+ }
+
+ if ( bi->sql_insentry_stmt == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "entry insertion SQL statement not specified "
+ "(use \"insentry_stmt\" directive in slapd.conf)\n" );
+ Debug(LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "setting \"%s\" by default\n",
+ backsql_def_insentry_stmt );
+ bi->sql_insentry_stmt = ch_strdup( backsql_def_insentry_stmt );
+ }
+
+ if ( bi->sql_delentry_stmt == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "entry deletion SQL statement not specified "
+ "(use \"delentry_stmt\" directive in slapd.conf)\n" );
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "setting \"%s\" by default\n",
+ backsql_def_delentry_stmt );
+ bi->sql_delentry_stmt = ch_strdup( backsql_def_delentry_stmt );
+ }
+
+ if ( bi->sql_renentry_stmt == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "entry deletion SQL statement not specified "
+ "(use \"renentry_stmt\" directive in slapd.conf)\n" );
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "setting \"%s\" by default\n",
+ backsql_def_renentry_stmt );
+ bi->sql_renentry_stmt = ch_strdup( backsql_def_renentry_stmt );
+ }
+
+ if ( bi->sql_delobjclasses_stmt == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "objclasses deletion SQL statement not specified "
+ "(use \"delobjclasses_stmt\" directive in slapd.conf)\n" );
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "setting \"%s\" by default\n",
+ backsql_def_delobjclasses_stmt );
+ bi->sql_delobjclasses_stmt = ch_strdup( backsql_def_delobjclasses_stmt );
+ }
+
+ /* This should just be to force schema loading */
+ connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+ op = &opbuf.ob_op;
+ op->o_bd = bd;
+ if ( backsql_get_db_conn( op, &dbh ) != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "connection failed, exiting\n" );
+ return 1;
+ }
+ if ( backsql_load_schema_map( bi, dbh ) != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "schema mapping failed, exiting\n" );
+ return 1;
+ }
+ if ( backsql_free_db_conn( op, dbh ) != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "connection free failed\n" );
+ }
+ if ( !BACKSQL_SCHEMA_LOADED( bi ) ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_db_open(): "
+ "test failed, schema map not loaded - exiting\n" );
+ return 1;
+ }
+
+ /*
+ * Prepare ID selection query
+ */
+ if ( bi->sql_id_query == NULL ) {
+ /* no custom id_query provided */
+ if ( bi->sql_upper_func.bv_val == NULL ) {
+ backsql_strcat_x( &bb, NULL, backsql_id_query, "dn=?", NULL );
+
+ } else {
+ if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) {
+ backsql_strcat_x( &bb, NULL, backsql_id_query,
+ "dn_ru=?", NULL );
+ } else {
+ if ( BACKSQL_USE_REVERSE_DN( bi ) ) {
+ backsql_strfcat_x( &bb, NULL, "sbl",
+ backsql_id_query,
+ &bi->sql_upper_func,
+ (ber_len_t)STRLENOF( "(dn)=?" ), "(dn)=?" );
+ } else {
+ backsql_strfcat_x( &bb, NULL, "sblbcb",
+ backsql_id_query,
+ &bi->sql_upper_func,
+ (ber_len_t)STRLENOF( "(dn)=" ), "(dn)=",
+ &bi->sql_upper_func_open,
+ '?',
+ &bi->sql_upper_func_close );
+ }
+ }
+ }
+ bi->sql_id_query = bb.bb_val.bv_val;
+ }
+
+ /*
+ * Prepare children count query
+ */
+ BER_BVZERO( &bb.bb_val );
+ bb.bb_len = 0;
+ backsql_strfcat_x( &bb, NULL, "sbsb",
+ "SELECT COUNT(distinct subordinates.id) "
+ "FROM ldap_entries,ldap_entries ",
+ &bi->sql_aliasing, "subordinates "
+ "WHERE subordinates.parent=ldap_entries.id AND ",
+ &bi->sql_dn_match_cond );
+ bi->sql_has_children_query = bb.bb_val.bv_val;
+
+ /*
+ * Prepare DN and objectClass aliasing bit of query
+ */
+ BER_BVZERO( &bb.bb_val );
+ bb.bb_len = 0;
+ backsql_strfcat_x( &bb, NULL, "sbbsbsbbsb",
+ " ", &bi->sql_aliasing, &bi->sql_aliasing_quote,
+ "objectClass", &bi->sql_aliasing_quote,
+ ",ldap_entries.dn ", &bi->sql_aliasing,
+ &bi->sql_aliasing_quote, "dn", &bi->sql_aliasing_quote );
+ bi->sql_dn_oc_aliasing = bb.bb_val;
+
+ /* should never happen! */
+ assert( bd->be_nsuffix != NULL );
+
+ if ( BER_BVISNULL( &bd->be_nsuffix[ 1 ] ) ) {
+ /* enable if only one suffix is defined */
+ bi->sql_flags |= BSQLF_USE_SUBTREE_SHORTCUT;
+ }
+
+ bi->sql_flags |= BSQLF_CHECK_SCHEMA;
+
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_db_open(): "
+ "test succeeded, schema map loaded\n" );
+ return 0;
+}
+
+int
+backsql_db_close(
+ BackendDB *bd,
+ ConfigReply *cr )
+{
+ backsql_info *bi = (backsql_info*)bd->be_private;
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_db_close()\n" );
+
+ backsql_conn_destroy( bi );
+
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_db_close()\n" );
+
+ return 0;
+}
+
+#if SLAPD_SQL == SLAPD_MOD_DYNAMIC
+
+/* conditionally define the init_module() function */
+SLAP_BACKEND_INIT_MODULE( sql )
+
+#endif /* SLAPD_SQL == SLAPD_MOD_DYNAMIC */
+
diff --git a/servers/slapd/back-sql/modify.c b/servers/slapd/back-sql/modify.c
new file mode 100644
index 0000000..83afec3
--- /dev/null
+++ b/servers/slapd/back-sql/modify.c
@@ -0,0 +1,212 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 Dmitry Kovalev.
+ * Portions Copyright 2002 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Dmitry Kovalev for inclusion
+ * by OpenLDAP Software. Additional significant contributors include
+ * Pierangelo Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "ac/string.h"
+
+#include "slap.h"
+#include "proto-sql.h"
+
+int
+backsql_modify( Operation *op, SlapReply *rs )
+{
+ backsql_info *bi = (backsql_info*)op->o_bd->be_private;
+ SQLHDBC dbh = SQL_NULL_HDBC;
+ backsql_oc_map_rec *oc = NULL;
+ backsql_srch_info bsi = { 0 };
+ Entry m = { 0 }, *e = NULL;
+ int manageDSAit = get_manageDSAit( op );
+ SQLUSMALLINT CompletionType = SQL_ROLLBACK;
+
+ /*
+ * FIXME: in case part of the operation cannot be performed
+ * (missing mapping, SQL write fails or so) the entire operation
+ * should be rolled-back
+ */
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_modify(): modifying entry \"%s\"\n",
+ op->o_req_ndn.bv_val );
+
+ rs->sr_err = backsql_get_db_conn( op, &dbh );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_modify(): "
+ "could not get connection handle - exiting\n" );
+ /*
+ * FIXME: we don't want to send back
+ * excessively detailed messages
+ */
+ rs->sr_text = ( rs->sr_err == LDAP_OTHER )
+ ? "SQL-backend error" : NULL;
+ goto done;
+ }
+
+ bsi.bsi_e = &m;
+ rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn,
+ LDAP_SCOPE_BASE,
+ (time_t)(-1), NULL, dbh, op, rs,
+ slap_anlist_all_attributes,
+ ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY | BACKSQL_ISF_GET_OC ) );
+ switch ( rs->sr_err ) {
+ case LDAP_SUCCESS:
+ break;
+
+ case LDAP_REFERRAL:
+ if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
+ dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) )
+ {
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ rs->sr_matched = NULL;
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+ break;
+ }
+ e = &m;
+ /* fallthru */
+
+ default:
+ Debug( LDAP_DEBUG_TRACE, "backsql_modify(): "
+ "could not retrieve modifyDN ID - no such entry\n" );
+ if ( !BER_BVISNULL( &m.e_nname ) ) {
+ /* FIXME: should always be true! */
+ e = &m;
+
+ } else {
+ e = NULL;
+ }
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, " backsql_modify(): "
+ "modifying entry \"%s\" (id=" BACKSQL_IDFMT ")\n",
+ bsi.bsi_base_id.eid_dn.bv_val,
+ BACKSQL_IDARG(bsi.bsi_base_id.eid_id) );
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, &m, get_assertion( op ) )
+ != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ e = &m;
+ goto done;
+ }
+
+ slap_mods_opattrs( op, &op->orm_modlist, 1 );
+
+ assert( bsi.bsi_base_id.eid_oc != NULL );
+ oc = bsi.bsi_base_id.eid_oc;
+
+ if ( !acl_check_modlist( op, &m, op->orm_modlist ) ) {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ e = &m;
+ goto done;
+ }
+
+ rs->sr_err = backsql_modify_internal( op, rs, dbh, oc,
+ &bsi.bsi_base_id, op->orm_modlist );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ e = &m;
+ goto do_transact;
+ }
+
+ if ( BACKSQL_CHECK_SCHEMA( bi ) ) {
+ char textbuf[ SLAP_TEXT_BUFLEN ] = { '\0' };
+
+ backsql_entry_clean( op, &m );
+
+ bsi.bsi_e = &m;
+ rs->sr_err = backsql_id2entry( &bsi, &bsi.bsi_base_id );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ e = &m;
+ goto do_transact;
+ }
+
+ rs->sr_err = entry_schema_check( op, &m, NULL, 0, 0, NULL,
+ &rs->sr_text, textbuf, sizeof( textbuf ) );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_modify(\"%s\"): "
+ "entry failed schema check -- aborting\n",
+ m.e_name.bv_val );
+ e = NULL;
+ goto do_transact;
+ }
+ }
+
+do_transact:;
+ /*
+ * Commit only if all operations succeed
+ */
+ if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop ) {
+ assert( e == NULL );
+ CompletionType = SQL_COMMIT;
+ }
+
+ SQLTransact( SQL_NULL_HENV, dbh, CompletionType );
+
+done:;
+ if ( e != NULL ) {
+ if ( !access_allowed( op, e, slap_schema.si_ad_entry, NULL,
+ ACL_DISCLOSE, NULL ) )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = NULL;
+ rs->sr_matched = NULL;
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+ }
+ }
+
+ if ( op->o_noop && rs->sr_err == LDAP_SUCCESS ) {
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ }
+
+ send_ldap_result( op, rs );
+ slap_graduate_commit_csn( op );
+
+ if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
+ (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
+ }
+
+ if ( !BER_BVISNULL( &m.e_nname ) ) {
+ backsql_entry_clean( op, &m );
+ }
+
+ if ( bsi.bsi_attrs != NULL ) {
+ op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
+ }
+
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_modify()\n" );
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/back-sql/modrdn.c b/servers/slapd/back-sql/modrdn.c
new file mode 100644
index 0000000..1299923
--- /dev/null
+++ b/servers/slapd/back-sql/modrdn.c
@@ -0,0 +1,524 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 Dmitry Kovalev.
+ * Portions Copyright 2002 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Dmitry Kovalev for inclusion
+ * by OpenLDAP Software. Additional significant contributors include
+ * Pierangelo Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "ac/string.h"
+
+#include "slap.h"
+#include "proto-sql.h"
+
+int
+backsql_modrdn( Operation *op, SlapReply *rs )
+{
+ backsql_info *bi = (backsql_info*)op->o_bd->be_private;
+ SQLHDBC dbh = SQL_NULL_HDBC;
+ SQLHSTMT sth = SQL_NULL_HSTMT;
+ RETCODE rc;
+ backsql_entryID e_id = BACKSQL_ENTRYID_INIT,
+ n_id = BACKSQL_ENTRYID_INIT;
+ backsql_srch_info bsi = { 0 };
+ backsql_oc_map_rec *oc = NULL;
+ struct berval pdn = BER_BVNULL, pndn = BER_BVNULL,
+ *new_pdn = NULL, *new_npdn = NULL,
+ new_dn = BER_BVNULL, new_ndn = BER_BVNULL,
+ realnew_dn = BER_BVNULL;
+ Entry r = { 0 },
+ p = { 0 },
+ n = { 0 },
+ *e = NULL;
+ int manageDSAit = get_manageDSAit( op );
+ struct berval *newSuperior = op->oq_modrdn.rs_newSup;
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_modrdn() renaming entry \"%s\", "
+ "newrdn=\"%s\", newSuperior=\"%s\"\n",
+ op->o_req_dn.bv_val, op->oq_modrdn.rs_newrdn.bv_val,
+ newSuperior ? newSuperior->bv_val : "(NULL)" );
+
+ rs->sr_err = backsql_get_db_conn( op, &dbh );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(): "
+ "could not get connection handle - exiting\n" );
+ rs->sr_text = ( rs->sr_err == LDAP_OTHER )
+ ? "SQL-backend error" : NULL;
+ e = NULL;
+ goto done;
+ }
+
+ bsi.bsi_e = &r;
+ rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn,
+ LDAP_SCOPE_BASE,
+ (time_t)(-1), NULL, dbh, op, rs,
+ slap_anlist_all_attributes,
+ ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY | BACKSQL_ISF_GET_OC ) );
+ switch ( rs->sr_err ) {
+ case LDAP_SUCCESS:
+ break;
+
+ case LDAP_REFERRAL:
+ if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
+ dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) )
+ {
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ rs->sr_matched = NULL;
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+ break;
+ }
+ e = &r;
+ /* fallthru */
+
+ default:
+ Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
+ "could not retrieve modrdnDN ID - no such entry\n" );
+ if ( !BER_BVISNULL( &r.e_nname ) ) {
+ /* FIXME: should always be true! */
+ e = &r;
+
+ } else {
+ e = NULL;
+ }
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modrdn(): entry id=" BACKSQL_IDFMT "\n",
+ BACKSQL_IDARG(e_id.eid_id) );
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, &r, get_assertion( op ) )
+ != LDAP_COMPARE_TRUE ) )
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ e = &r;
+ goto done;
+ }
+
+ if ( backsql_has_children( op, dbh, &op->o_req_ndn ) == LDAP_COMPARE_TRUE ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(): "
+ "entry \"%s\" has children\n",
+ op->o_req_dn.bv_val );
+ rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
+ rs->sr_text = "subtree rename not supported";
+ e = &r;
+ goto done;
+ }
+
+ /*
+ * Check for entry access to target
+ */
+ if ( !access_allowed( op, &r, slap_schema.si_ad_entry,
+ NULL, ACL_WRITE, NULL ) ) {
+ Debug( LDAP_DEBUG_TRACE, " no access to entry\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto done;
+ }
+
+ dnParent( &op->o_req_dn, &pdn );
+ dnParent( &op->o_req_ndn, &pndn );
+
+ /*
+ * namingContext "" is not supported
+ */
+ if ( BER_BVISEMPTY( &pdn ) ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(): "
+ "parent is \"\" - aborting\n" );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "not allowed within namingContext";
+ e = NULL;
+ goto done;
+ }
+
+ /*
+ * Check for children access to parent
+ */
+ bsi.bsi_e = &p;
+ e_id = bsi.bsi_base_id;
+ memset( &bsi.bsi_base_id, 0, sizeof( bsi.bsi_base_id ) );
+ rs->sr_err = backsql_init_search( &bsi, &pndn,
+ LDAP_SCOPE_BASE,
+ (time_t)(-1), NULL, dbh, op, rs,
+ slap_anlist_all_attributes,
+ BACKSQL_ISF_GET_ENTRY );
+
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modrdn(): old parent entry id is " BACKSQL_IDFMT "\n",
+ BACKSQL_IDARG(bsi.bsi_base_id.eid_id) );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
+ "could not retrieve renameDN ID - no such entry\n" );
+ e = &p;
+ goto done;
+ }
+
+ if ( !access_allowed( op, &p, slap_schema.si_ad_children, NULL,
+ newSuperior ? ACL_WDEL : ACL_WRITE, NULL ) )
+ {
+ Debug( LDAP_DEBUG_TRACE, " no access to parent\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto done;
+ }
+
+ if ( newSuperior ) {
+ (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
+
+ /*
+ * namingContext "" is not supported
+ */
+ if ( BER_BVISEMPTY( newSuperior ) ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(): "
+ "newSuperior is \"\" - aborting\n" );
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "not allowed within namingContext";
+ e = NULL;
+ goto done;
+ }
+
+ new_pdn = newSuperior;
+ new_npdn = op->oq_modrdn.rs_nnewSup;
+
+ /*
+ * Check for children access to new parent
+ */
+ bsi.bsi_e = &n;
+ rs->sr_err = backsql_init_search( &bsi, new_npdn,
+ LDAP_SCOPE_BASE,
+ (time_t)(-1), NULL, dbh, op, rs,
+ slap_anlist_all_attributes,
+ ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
+ "could not retrieve renameDN ID - no such entry\n" );
+ e = &n;
+ goto done;
+ }
+
+ n_id = bsi.bsi_base_id;
+
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modrdn(): new parent entry id=" BACKSQL_IDFMT "\n",
+ BACKSQL_IDARG(n_id.eid_id) );
+
+ if ( !access_allowed( op, &n, slap_schema.si_ad_children,
+ NULL, ACL_WADD, NULL ) ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(): "
+ "no access to new parent \"%s\"\n",
+ new_pdn->bv_val );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ e = &n;
+ goto done;
+ }
+
+ } else {
+ n_id = bsi.bsi_base_id;
+ new_pdn = &pdn;
+ new_npdn = &pndn;
+ }
+
+ memset( &bsi.bsi_base_id, 0, sizeof( bsi.bsi_base_id ) );
+
+ if ( newSuperior && dn_match( &pndn, new_npdn ) ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(): "
+ "newSuperior is equal to old parent - ignored\n" );
+ newSuperior = NULL;
+ }
+
+ if ( newSuperior && dn_match( &op->o_req_ndn, new_npdn ) ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(): "
+ "newSuperior is equal to entry being moved "
+ "- aborting\n" );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "newSuperior is equal to old DN";
+ e = &r;
+ goto done;
+ }
+
+ build_new_dn( &new_dn, new_pdn, &op->oq_modrdn.rs_newrdn,
+ op->o_tmpmemctx );
+ build_new_dn( &new_ndn, new_npdn, &op->oq_modrdn.rs_nnewrdn,
+ op->o_tmpmemctx );
+
+ Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(): new entry dn is \"%s\"\n",
+ new_dn.bv_val );
+
+ realnew_dn = new_dn;
+ if ( backsql_api_dn2odbc( op, rs, &realnew_dn ) ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(\"%s\"): "
+ "backsql_api_dn2odbc(\"%s\") failed\n",
+ op->o_req_dn.bv_val, realnew_dn.bv_val );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ e = NULL;
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(): "
+ "executing renentry_stmt\n" );
+
+ rc = backsql_Prepare( dbh, &sth, bi->sql_renentry_stmt, 0 );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modrdn(): "
+ "error preparing renentry_stmt\n" );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ e = NULL;
+ goto done;
+ }
+
+ rc = backsql_BindParamBerVal( sth, 1, SQL_PARAM_INPUT, &realnew_dn );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modrdn(): "
+ "error binding DN parameter for objectClass %s\n",
+ oc->bom_oc->soc_cname.bv_val );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ e = NULL;
+ goto done;
+ }
+
+ rc = backsql_BindParamID( sth, 2, SQL_PARAM_INPUT, &n_id.eid_id );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modrdn(): "
+ "error binding parent ID parameter for objectClass %s\n",
+ oc->bom_oc->soc_cname.bv_val );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ e = NULL;
+ goto done;
+ }
+
+ rc = backsql_BindParamID( sth, 3, SQL_PARAM_INPUT, &e_id.eid_keyval );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modrdn(): "
+ "error binding entry ID parameter for objectClass %s\n",
+ oc->bom_oc->soc_cname.bv_val );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ e = NULL;
+ goto done;
+ }
+
+ rc = backsql_BindParamID( sth, 4, SQL_PARAM_INPUT, &e_id.eid_id );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " backsql_modrdn(): "
+ "error binding ID parameter for objectClass %s\n",
+ oc->bom_oc->soc_cname.bv_val );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ rs->sr_text = "SQL-backend error";
+ rs->sr_err = LDAP_OTHER;
+ e = NULL;
+ goto done;
+ }
+
+ rc = SQLExecute( sth );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(): "
+ "could not rename ldap_entries record\n" );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "SQL-backend error";
+ e = NULL;
+ goto done;
+ }
+ SQLFreeStmt( sth, SQL_DROP );
+
+ slap_mods_opattrs( op, &op->orr_modlist, 1 );
+
+ assert( e_id.eid_oc != NULL );
+ oc = e_id.eid_oc;
+
+ if ( op->orr_modlist != NULL ) {
+ rs->sr_err = backsql_modify_internal( op, rs, dbh, oc, &e_id, op->orr_modlist );
+ slap_graduate_commit_csn( op );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ e = &r;
+ goto done;
+ }
+ }
+
+ if ( BACKSQL_CHECK_SCHEMA( bi ) ) {
+ char textbuf[ SLAP_TEXT_BUFLEN ] = { '\0' };
+
+ backsql_entry_clean( op, &r );
+ (void)backsql_free_entryID( &e_id, 0, op->o_tmpmemctx );
+
+ bsi.bsi_e = &r;
+ rs->sr_err = backsql_init_search( &bsi, &new_ndn,
+ LDAP_SCOPE_BASE,
+ (time_t)(-1), NULL, dbh, op, rs,
+ slap_anlist_all_attributes,
+ ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) );
+ switch ( rs->sr_err ) {
+ case LDAP_SUCCESS:
+ break;
+
+ case LDAP_REFERRAL:
+ if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
+ dn_match( &new_ndn, &bsi.bsi_e->e_nname ) )
+ {
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ rs->sr_matched = NULL;
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+ break;
+ }
+ e = &r;
+ /* fallthru */
+
+ default:
+ Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
+ "could not retrieve modrdnDN ID - no such entry\n" );
+ if ( !BER_BVISNULL( &r.e_nname ) ) {
+ /* FIXME: should always be true! */
+ e = &r;
+
+ } else {
+ e = NULL;
+ }
+ goto done;
+ }
+
+ e_id = bsi.bsi_base_id;
+
+ rs->sr_err = entry_schema_check( op, &r, NULL, 0, 0, NULL,
+ &rs->sr_text, textbuf, sizeof( textbuf ) );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, " backsql_modrdn(\"%s\"): "
+ "entry failed schema check -- aborting\n",
+ r.e_name.bv_val );
+ e = NULL;
+ goto done;
+ }
+ }
+
+done:;
+ if ( e != NULL ) {
+ if ( !access_allowed( op, e, slap_schema.si_ad_entry, NULL,
+ ACL_DISCLOSE, NULL ) )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = NULL;
+ rs->sr_matched = NULL;
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+ }
+ }
+
+ /*
+ * Commit only if all operations succeed
+ */
+ if ( sth != SQL_NULL_HSTMT ) {
+ SQLUSMALLINT CompletionType = SQL_ROLLBACK;
+
+ if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop ) {
+ CompletionType = SQL_COMMIT;
+ }
+
+ SQLTransact( SQL_NULL_HENV, dbh, CompletionType );
+ }
+
+ if ( op->o_noop && rs->sr_err == LDAP_SUCCESS ) {
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ }
+
+ send_ldap_result( op, rs );
+ slap_graduate_commit_csn( op );
+
+ if ( !BER_BVISNULL( &realnew_dn ) && realnew_dn.bv_val != new_dn.bv_val ) {
+ ch_free( realnew_dn.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &new_dn ) ) {
+ slap_sl_free( new_dn.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( !BER_BVISNULL( &new_ndn ) ) {
+ slap_sl_free( new_ndn.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( !BER_BVISNULL( &e_id.eid_ndn ) ) {
+ (void)backsql_free_entryID( &e_id, 0, op->o_tmpmemctx );
+ }
+
+ if ( !BER_BVISNULL( &n_id.eid_ndn ) ) {
+ (void)backsql_free_entryID( &n_id, 0, op->o_tmpmemctx );
+ }
+
+ if ( !BER_BVISNULL( &r.e_nname ) ) {
+ backsql_entry_clean( op, &r );
+ }
+
+ if ( !BER_BVISNULL( &p.e_nname ) ) {
+ backsql_entry_clean( op, &p );
+ }
+
+ if ( !BER_BVISNULL( &n.e_nname ) ) {
+ backsql_entry_clean( op, &n );
+ }
+
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_modrdn()\n" );
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/back-sql/operational.c b/servers/slapd/back-sql/operational.c
new file mode 100644
index 0000000..3eb1ec2
--- /dev/null
+++ b/servers/slapd/back-sql/operational.c
@@ -0,0 +1,246 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 Dmitry Kovalev.
+ * Portions Copyright 2002 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Dmitry Kovalev for inclusion
+ * by OpenLDAP Software. Additional significant contributors include
+ * Pierangelo Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "slap.h"
+#include "proto-sql.h"
+#include "lutil.h"
+
+/*
+ * sets the supported operational attributes (if required)
+ */
+
+Attribute *
+backsql_operational_entryUUID( backsql_info *bi, backsql_entryID *id )
+{
+ int rc;
+ struct berval val, nval;
+ AttributeDescription *desc = slap_schema.si_ad_entryUUID;
+ Attribute *a;
+
+ backsql_entryUUID( bi, id, &val, NULL );
+
+ rc = (*desc->ad_type->sat_equality->smr_normalize)(
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ desc->ad_type->sat_syntax,
+ desc->ad_type->sat_equality,
+ &val, &nval, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ ber_memfree( val.bv_val );
+ return NULL;
+ }
+
+ a = attr_alloc( desc );
+
+ a->a_numvals = 1;
+ a->a_vals = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
+ a->a_vals[ 0 ] = val;
+ BER_BVZERO( &a->a_vals[ 1 ] );
+
+ a->a_nvals = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
+ a->a_nvals[ 0 ] = nval;
+ BER_BVZERO( &a->a_nvals[ 1 ] );
+
+ return a;
+}
+
+Attribute *
+backsql_operational_entryCSN( Operation *op )
+{
+ char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE ];
+ struct berval entryCSN;
+ Attribute *a;
+
+ a = attr_alloc( slap_schema.si_ad_entryCSN );
+ a->a_numvals = 1;
+ a->a_vals = ch_malloc( 2 * sizeof( struct berval ) );
+ BER_BVZERO( &a->a_vals[ 1 ] );
+
+#ifdef BACKSQL_SYNCPROV
+ if ( op->o_sync && op->o_tag == LDAP_REQ_SEARCH && op->o_private != NULL ) {
+ assert( op->o_private != NULL );
+
+ entryCSN = *((struct berval *)op->o_private);
+
+ } else
+#endif /* BACKSQL_SYNCPROV */
+ {
+ entryCSN.bv_val = csnbuf;
+ entryCSN.bv_len = sizeof( csnbuf );
+ slap_get_csn( op, &entryCSN, 0 );
+ }
+
+ ber_dupbv( &a->a_vals[ 0 ], &entryCSN );
+
+ a->a_nvals = a->a_vals;
+
+ return a;
+}
+
+int
+backsql_operational(
+ Operation *op,
+ SlapReply *rs )
+{
+
+ backsql_info *bi = (backsql_info*)op->o_bd->be_private;
+ SQLHDBC dbh = SQL_NULL_HDBC;
+ int rc = 0;
+ Attribute **ap;
+ enum {
+ BACKSQL_OP_HASSUBORDINATES = 0,
+ BACKSQL_OP_ENTRYUUID,
+ BACKSQL_OP_ENTRYCSN,
+
+ BACKSQL_OP_LAST
+ };
+ int get_conn = BACKSQL_OP_LAST,
+ got[ BACKSQL_OP_LAST ] = { 0 };
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_operational(): entry \"%s\"\n",
+ rs->sr_entry->e_nname.bv_val );
+
+ for ( ap = &rs->sr_entry->e_attrs; *ap; ap = &(*ap)->a_next ) {
+ if ( (*ap)->a_desc == slap_schema.si_ad_hasSubordinates ) {
+ get_conn--;
+ got[ BACKSQL_OP_HASSUBORDINATES ] = 1;
+
+ } else if ( (*ap)->a_desc == slap_schema.si_ad_entryUUID ) {
+ get_conn--;
+ got[ BACKSQL_OP_ENTRYUUID ] = 1;
+
+ } else if ( (*ap)->a_desc == slap_schema.si_ad_entryCSN ) {
+ get_conn--;
+ got[ BACKSQL_OP_ENTRYCSN ] = 1;
+ }
+ }
+
+ for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next ) {
+ if ( !got[ BACKSQL_OP_HASSUBORDINATES ] &&
+ (*ap)->a_desc == slap_schema.si_ad_hasSubordinates )
+ {
+ get_conn--;
+ got[ BACKSQL_OP_HASSUBORDINATES ] = 1;
+
+ } else if ( !got[ BACKSQL_OP_ENTRYUUID ] &&
+ (*ap)->a_desc == slap_schema.si_ad_entryUUID )
+ {
+ get_conn--;
+ got[ BACKSQL_OP_ENTRYUUID ] = 1;
+
+ } else if ( !got[ BACKSQL_OP_ENTRYCSN ] &&
+ (*ap)->a_desc == slap_schema.si_ad_entryCSN )
+ {
+ get_conn--;
+ got[ BACKSQL_OP_ENTRYCSN ] = 1;
+ }
+ }
+
+ if ( !get_conn ) {
+ return 0;
+ }
+
+ rc = backsql_get_db_conn( op, &dbh );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_operational(): "
+ "could not get connection handle - exiting\n" );
+ return 1;
+ }
+
+ if ( ( SLAP_OPATTRS( rs->sr_attr_flags ) || ad_inlist( slap_schema.si_ad_hasSubordinates, rs->sr_attrs ) )
+ && !got[ BACKSQL_OP_HASSUBORDINATES ]
+ && attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_hasSubordinates ) == NULL )
+ {
+ rc = backsql_has_children( op, dbh, &rs->sr_entry->e_nname );
+
+ switch( rc ) {
+ case LDAP_COMPARE_TRUE:
+ case LDAP_COMPARE_FALSE:
+ *ap = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE );
+ assert( *ap != NULL );
+ ap = &(*ap)->a_next;
+ rc = 0;
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_TRACE, "backsql_operational(): "
+ "has_children failed( %d)\n", rc );
+ return 1;
+ }
+ }
+
+ if ( ( SLAP_OPATTRS( rs->sr_attr_flags ) || ad_inlist( slap_schema.si_ad_entryUUID, rs->sr_attrs ) )
+ && !got[ BACKSQL_OP_ENTRYUUID ]
+ && attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryUUID ) == NULL )
+ {
+ backsql_srch_info bsi = { 0 };
+
+ rc = backsql_init_search( &bsi, &rs->sr_entry->e_nname,
+ LDAP_SCOPE_BASE,
+ (time_t)(-1), NULL, dbh, op, rs, NULL,
+ BACKSQL_ISF_GET_ID );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_operational(): "
+ "could not retrieve entry ID - no such entry\n" );
+ return 1;
+ }
+
+ *ap = backsql_operational_entryUUID( bi, &bsi.bsi_base_id );
+
+ (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
+
+ if ( bsi.bsi_attrs != NULL ) {
+ op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
+ }
+
+ if ( *ap == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_operational(): "
+ "could not retrieve entryUUID\n" );
+ return 1;
+ }
+
+ ap = &(*ap)->a_next;
+ }
+
+ if ( ( SLAP_OPATTRS( rs->sr_attr_flags ) || ad_inlist( slap_schema.si_ad_entryCSN, rs->sr_attrs ) )
+ && !got[ BACKSQL_OP_ENTRYCSN ]
+ && attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryCSN ) == NULL )
+ {
+ *ap = backsql_operational_entryCSN( op );
+ if ( *ap == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_operational(): "
+ "could not retrieve entryCSN\n" );
+ return 1;
+ }
+
+ ap = &(*ap)->a_next;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_operational(%d)\n", rc );
+
+ return rc;
+}
+
diff --git a/servers/slapd/back-sql/proto-sql.h b/servers/slapd/back-sql/proto-sql.h
new file mode 100644
index 0000000..169be76
--- /dev/null
+++ b/servers/slapd/back-sql/proto-sql.h
@@ -0,0 +1,313 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 Dmitry Kovalev.
+ * Portions Copyright 2002 Pierangelo Mararati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Dmitry Kovalev for inclusion
+ * by OpenLDAP Software. Additional significant contributors include
+ * Pierangelo Masarati
+ */
+
+/*
+ * The following changes have been addressed:
+ *
+ * Enhancements:
+ * - re-styled code for better readability
+ * - upgraded backend API to reflect recent changes
+ * - LDAP schema is checked when loading SQL/LDAP mapping
+ * - AttributeDescription/ObjectClass pointers used for more efficient
+ * mapping lookup
+ * - bervals used where string length is required often
+ * - atomized write operations by committing at the end of each operation
+ * and defaulting connection closure to rollback
+ * - added LDAP access control to write operations
+ * - fully implemented modrdn (with rdn attrs change, deleteoldrdn,
+ * access check, parent/children check and more)
+ * - added parent access control, children control to delete operation
+ * - added structuralObjectClass operational attribute check and
+ * value return on search
+ * - added hasSubordinate operational attribute on demand
+ * - search limits are appropriately enforced
+ * - function backsql_strcat() has been made more efficient
+ * - concat function has been made configurable by means of a pattern
+ * - added config switches:
+ * - fail_if_no_mapping write operations fail if there is no mapping
+ * - has_ldapinfo_dn_ru overrides autodetect
+ * - concat_pattern a string containing two '?' is used
+ * (note that "?||?" should be more portable
+ * than builtin function "CONCAT(?,?)")
+ * - strcast_func cast of string constants in "SELECT DISTINCT
+ * statements (needed by PostgreSQL)
+ * - upper_needs_cast cast the argument of upper when required
+ * (basically when building dn substring queries)
+ * - added noop control
+ * - added values return filter control
+ * - hasSubordinate can be used in search filters (with limitations)
+ * - eliminated oc->name; use oc->oc->soc_cname instead
+ *
+ * Todo:
+ * - add security checks for SQL statements that can be injected (?)
+ * - re-test with previously supported RDBMs
+ * - replace dn_ru and so with normalized dn (no need for upper() and so
+ * in dn match)
+ * - implement a backsql_normalize() function to replace the upper()
+ * conversion routines
+ * - note that subtree deletion, subtree renaming and so could be easily
+ * implemented (rollback and consistency checks are available :)
+ * - implement "lastmod" and other operational stuff (ldap_entries table ?)
+ * - check how to allow multiple operations with one statement, to remove
+ * BACKSQL_REALLOC_STMT from modify.c (a more recent unixODBC lib?)
+ */
+
+#ifndef PROTO_SQL_H
+#define PROTO_SQL_H
+
+#include "back-sql.h"
+
+/*
+ * add.c
+ */
+int backsql_modify_delete_all_values(
+ Operation *op,
+ SlapReply *rs,
+ SQLHDBC dbh,
+ backsql_entryID *e_id,
+ backsql_at_map_rec *at );
+
+int backsql_modify_internal(
+ Operation *op,
+ SlapReply *rs,
+ SQLHDBC dbh,
+ backsql_oc_map_rec *oc,
+ backsql_entryID *e_id,
+ Modifications *modlist );
+
+/*
+ * api.c
+ */
+int backsql_api_config( backsql_info *bi, const char *name,
+ int argc, char *argv[] );
+int backsql_api_destroy( backsql_info *bi );
+int backsql_api_register( backsql_api *ba );
+int backsql_api_dn2odbc( Operation *op, SlapReply *rs, struct berval *dn );
+int backsql_api_odbc2dn( Operation *op, SlapReply *rs, struct berval *dn );
+
+/*
+ * entry-id.c
+ */
+#ifdef BACKSQL_ARBITRARY_KEY
+extern struct berval backsql_baseObject_bv;
+#endif /* BACKSQL_ARBITRARY_KEY */
+
+/* stores in *id the ID in table ldap_entries corresponding to DN, if any */
+extern int
+backsql_dn2id( Operation *op, SlapReply *rs, SQLHDBC dbh,
+ struct berval *ndn, backsql_entryID *id,
+ int matched, int muck );
+
+/* stores in *nchildren the count of children for an entry */
+extern int
+backsql_count_children( Operation *op, SQLHDBC dbh,
+ struct berval *dn, unsigned long *nchildren );
+
+/* returns LDAP_COMPARE_TRUE/LDAP_COMPARE_FALSE if the entry corresponding
+ * to DN has/has not children */
+extern int
+backsql_has_children( Operation *op, SQLHDBC dbh, struct berval *dn );
+
+/* free *id and return next in list */
+extern backsql_entryID *
+backsql_free_entryID( backsql_entryID *id, int freeit, void *ctx );
+
+/* turn an ID into an entry */
+extern int
+backsql_id2entry( backsql_srch_info *bsi, backsql_entryID *id );
+
+/* duplicate an entryID */
+extern backsql_entryID *
+backsql_entryID_dup( backsql_entryID *eid, void *ctx );
+
+/*
+ * operational.c
+ */
+
+Attribute *backsql_operational_entryUUID( backsql_info *bi, backsql_entryID *id );
+
+Attribute *backsql_operational_entryCSN( Operation *op );
+
+/*
+ * schema-map.c
+ */
+
+int backsql_load_schema_map( backsql_info *si, SQLHDBC dbh );
+
+backsql_oc_map_rec *backsql_oc2oc( backsql_info *si, ObjectClass *oc );
+
+backsql_oc_map_rec *backsql_id2oc( backsql_info *si, unsigned long id );
+
+backsql_oc_map_rec * backsql_name2oc( backsql_info *si,
+ struct berval *oc_name );
+
+backsql_at_map_rec *backsql_ad2at( backsql_oc_map_rec *objclass,
+ AttributeDescription *ad );
+
+int backsql_supad2at( backsql_oc_map_rec *objclass,
+ AttributeDescription *supad, backsql_at_map_rec ***pret );
+
+int backsql_destroy_schema_map( backsql_info *si );
+
+/*
+ * search.c
+ */
+
+int backsql_init_search( backsql_srch_info *bsi,
+ struct berval *nbase, int scope,
+ time_t stoptime, Filter *filter, SQLHDBC dbh,
+ Operation *op, SlapReply *rs, AttributeName *attrs,
+ unsigned flags );
+
+void backsql_entry_clean( Operation *op, Entry *e );
+
+/*
+ * sql-wrap.h
+ */
+
+RETCODE backsql_Prepare( SQLHDBC dbh, SQLHSTMT *sth, const char* query, int timeout );
+
+#define backsql_BindParamStr( sth, par_ind, io, str, maxlen ) \
+ SQLBindParameter( (sth), (SQLUSMALLINT)(par_ind), \
+ (io), SQL_C_CHAR, SQL_VARCHAR, \
+ (SQLULEN)(maxlen), 0, (SQLPOINTER)(str), \
+ (SQLLEN)(maxlen), NULL )
+
+#define backsql_BindParamBerVal( sth, par_ind, io, bv ) \
+ SQLBindParameter( (sth), (SQLUSMALLINT)(par_ind), \
+ (io), SQL_C_CHAR, SQL_VARCHAR, \
+ (SQLULEN)(bv)->bv_len, 0, \
+ (SQLPOINTER)(bv)->bv_val, \
+ (SQLLEN)(bv)->bv_len, NULL )
+
+#define backsql_BindParamInt( sth, par_ind, io, val ) \
+ SQLBindParameter( (sth), (SQLUSMALLINT)(par_ind), \
+ (io), SQL_C_ULONG, SQL_INTEGER, \
+ 0, 0, (SQLPOINTER)(val), 0, (SQLLEN*)NULL )
+
+#define backsql_BindParamNumID( sth, par_ind, io, val ) \
+ SQLBindParameter( (sth), (SQLUSMALLINT)(par_ind), \
+ (io), BACKSQL_C_NUMID, SQL_INTEGER, \
+ 0, 0, (SQLPOINTER)(val), 0, (SQLLEN*)NULL )
+
+#ifdef BACKSQL_ARBITRARY_KEY
+#define backsql_BindParamID( sth, par_ind, io, id ) \
+ backsql_BindParamBerVal( (sth), (par_ind), (io), (id) )
+#else /* ! BACKSQL_ARBITRARY_KEY */
+#define backsql_BindParamID( sth, par_ind, io, id ) \
+ backsql_BindParamNumID( (sth), (par_ind), (io), (id) )
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+
+RETCODE backsql_BindRowAsStrings_x( SQLHSTMT sth, BACKSQL_ROW_NTS *row, void *ctx );
+
+RETCODE backsql_BindRowAsStrings( SQLHSTMT sth, BACKSQL_ROW_NTS *row );
+
+RETCODE backsql_FreeRow_x( BACKSQL_ROW_NTS *row, void *ctx );
+
+RETCODE backsql_FreeRow( BACKSQL_ROW_NTS *row );
+
+void backsql_PrintErrors( SQLHENV henv, SQLHDBC hdbc, SQLHSTMT sth, int rc );
+
+int backsql_conn_destroy( backsql_info *bi );
+
+int backsql_init_db_env( backsql_info *si );
+
+int backsql_free_db_env( backsql_info *si );
+
+int backsql_get_db_conn( Operation *op, SQLHDBC *dbh );
+
+int backsql_free_db_conn( Operation *op, SQLHDBC dbh );
+
+/*
+ * util.c
+ */
+
+extern const char
+ backsql_def_oc_query[],
+ backsql_def_needs_select_oc_query[],
+ backsql_def_at_query[],
+ backsql_def_delentry_stmt[],
+ backsql_def_renentry_stmt[],
+ backsql_def_insentry_stmt[],
+ backsql_def_delobjclasses_stmt[],
+ backsql_def_subtree_cond[],
+ backsql_def_upper_subtree_cond[],
+ backsql_id_query[],
+ backsql_def_concat_func[],
+ backsql_check_dn_ru_query[];
+
+struct berbuf * backsql_strcat_x( struct berbuf *dest, void *memctx, ... );
+struct berbuf * backsql_strfcat_x( struct berbuf *dest, void *memctx, const char *fmt, ... );
+
+int backsql_entry_addattr( Entry *e, AttributeDescription *ad,
+ struct berval *at_val, void *memctx );
+
+int backsql_merge_from_clause( backsql_info *bi, struct berbuf *dest_from,
+ struct berval *src_from );
+
+int backsql_split_pattern( const char *pattern, BerVarray *split_pattern,
+ int expected );
+
+int backsql_prepare_pattern( BerVarray split_pattern, BerVarray values,
+ struct berval *res );
+
+int backsql_entryUUID( backsql_info *bi, backsql_entryID *id,
+ struct berval *entryUUID, void *memctx );
+int backsql_entryUUID_decode( struct berval *entryUUID, unsigned long *oc_id,
+#ifdef BACKSQL_ARBITRARY_KEY
+ struct berval *keyval
+#else /* ! BACKSQL_ARBITRARY_KEY */
+ unsigned long *keyval
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+ );
+
+/*
+ * former external.h
+ */
+
+extern BI_init sql_back_initialize;
+
+extern BI_destroy backsql_destroy;
+
+extern BI_db_init backsql_db_init;
+extern BI_db_open backsql_db_open;
+extern BI_db_close backsql_db_close;
+extern BI_db_destroy backsql_db_destroy;
+extern BI_db_config backsql_db_config;
+
+extern BI_op_bind backsql_bind;
+extern BI_op_search backsql_search;
+extern BI_op_compare backsql_compare;
+extern BI_op_modify backsql_modify;
+extern BI_op_modrdn backsql_modrdn;
+extern BI_op_add backsql_add;
+extern BI_op_delete backsql_delete;
+
+extern BI_operational backsql_operational;
+extern BI_entry_get_rw backsql_entry_get;
+extern BI_entry_release_rw backsql_entry_release;
+
+extern BI_connection_destroy backsql_connection_destroy;
+
+int backsql_init_cf( BackendInfo * bi );
+
+#endif /* PROTO_SQL_H */
diff --git a/servers/slapd/back-sql/rdbms_depend/README b/servers/slapd/back-sql/rdbms_depend/README
new file mode 100644
index 0000000..2b281f6
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/README
@@ -0,0 +1,189 @@
+Author: Pierangelo Masarati <ando@OpenLDAP.org>
+
+Back-sql can be tested with sql-test000-read; it requires a bit of work
+to get everything up and running appropriately.
+
+This document briefly describes the steps that are required to prepare
+a quick'n'dirty installation of back-sql and of the related RDBMS
+and ODBC; Examples are provided, but by no means they pretent
+to represent an exhaustive source of info about how to setup the ODBC;
+refer to the docs for any problem or detail.
+
+Currently, the system has been tested with IBM db2, PostgreSQL and MySQL;
+basic support and test data for other RDBMSes is in place, but as of
+today (November 2004) it's totally untested. If you succeed in running
+any of the other RDBMSes, please provide feedback about any required
+change either in the code or in the test scripts by means of OpenLDAP's
+Issue Tracking System (http://www.openldap.org/its/).
+
+1) slapd must be compiled with back-sql support, i.e. configure
+with --enable-sql switch. This requires an implementation of the ODBC
+to be installed.
+
+2) The ODBC must be set up appropriately, by editing the odbc.ini file
+in /etc/ (or wherever your installation puts it) and, if appropriate,
+the odbcinst.ini file. Note: you can also use custom odbc.ini and
+odbcinst.ini files, provided you export in ODBCINI the full path to the
+odbc.ini file, and in ODBCSYSINI the directory where the odbcinst.ini
+file resides.
+Relevant info for our test setup is highlighted with '<===' on the right.
+
+2.1) PostgreSQL
+
+2.1.1) Add to the odbc.ini file a block of the form
+
+[example] <===
+Description = Example for OpenLDAP's back-sql
+Driver = PostgreSQL
+Trace = No
+Database = example <===
+Servername = localhost
+UserName = manager <===
+Password = secret <===
+Port = 5432
+;Protocol = 6.4
+ReadOnly = No
+RowVersioning = No
+ShowSystemTables = No
+ShowOidColumn = No
+FakeOidIndex = No
+ConnSettings =
+
+2.1.2) Add to the odbcinst.ini file a block of the form
+
+[PostgreSQL]
+Description = ODBC for PostgreSQL
+Driver = /usr/lib/libodbcpsql.so
+Setup = /usr/lib/libodbcpsqlS.so
+FileUsage = 1
+
+2.2) MySQL
+
+2.2.1) Add to the odbc.ini file a block of the form
+
+[example] <===
+Description = Example for OpenLDAP's back-sql
+Driver = MySQL
+Trace = No
+Database = example <===
+Servername = localhost
+UserName = manager <===
+Password = secret <===
+ReadOnly = No
+RowVersioning = No
+ShowSystemTables = No
+ShowOidColumn = No
+FakeOidIndex = No
+ConnSettings =
+SOCKET = /var/lib/mysql/mysql.sock
+
+2.2.2) Add to the odbcinst.ini file a block of the form
+
+[MySQL]
+Description = ODBC for MySQL
+Driver = /usr/lib/libmyodbc.so
+FileUsage = 1
+
+2.3) IBM db2
+[n.a.]
+
+3) The RDBMS must be setup; examples are provided for my installations
+of PostgreSQL and MySQL, but details may change; other RDBMSes should
+be configured in a similar manner, you need to find out the details by
+reading their documentation.
+
+3.1) PostgreSQL
+
+3.1.1) Start the server
+on RedHat:
+[root@localhost]# service postgresql start
+on other systems: read the docs...
+
+3.1.2) Create the database:
+[root@localhost]# su - postgres
+[postgres@localhost]$ createdb example
+
+3.1.3) Create the user:
+[root@localhost]# su - postgres
+[postgres@localhost]$ psql example
+example=> create user manager with password 'secret';
+example=> <control-D>
+
+3.1.4) Populate the database:
+[root@localhost]# cd $SOURCES/servers/slapd/back-sql/rdbms_depend/pgsql/
+[root@localhost]# psql -U manager -W example
+example=> <control-D>
+[root@localhost]# psql -U manager example < backsql_create.sql
+[root@localhost]# psql -U manager example < testdb_create.sql
+[root@localhost]# psql -U manager example < testdb_data.sql
+[root@localhost]# psql -U manager example < testdb_metadata.sql
+
+3.1.5) Run the test:
+[root@localhost]# cd $SOURCES/tests
+[root@localhost]# SLAPD_USE_SQL=pgsql ./run sql-test000
+
+3.2) MySQL
+
+3.2.1) Start the server
+on RedHat:
+[root@localhost]# service mysqld start
+on other systems: read the docs...
+
+3.2.2) Create the database:
+[root@localhost]# mysqladmin -u root -p create example
+(hit <return> for the empty password).
+
+3.2.3) Create the user:
+[root@localhost]# mysql -u root -p example
+(hit <return> for the empty password)
+mysql> grant all privileges on *.* \
+ to 'manager'@'localhost' identified by 'secret' with grant option;
+mysql> exit;
+
+3.2.4) Populate the database:
+[root@localhost]# cd $SOURCES/servers/slapd/back-sql/rdbms_depend/mysql/
+[root@localhost]# mysql -u manager -p example < backsql_create.sql
+[root@localhost]# mysql -u manager -p example < testdb_create.sql
+[root@localhost]# mysql -u manager -p example < testdb_data.sql
+[root@localhost]# mysql -u manager -p example < testdb_metadata.sql
+
+3.2.5) Run the test:
+[root@localhost]# cd $SOURCES/tests
+[root@localhost]# SLAPD_USE_SQL=mysql ./run sql-test000
+
+3.3) IBM db2
+[n.a.]
+
+3.3.1) Start the server:
+
+3.3.2) Create the database:
+
+3.3.3) Create the user:
+
+3.3.4) Populate the database:
+connect to the database as user manager, and execute the test files
+in auto-commit mode (-c)
+[root@localhost]# su - manager
+[manager@localhost]$ db2 "connect to example user manager using secret"
+[manager@localhost]$ db2 -ctvf backsql_create.sql
+[manager@localhost]$ db2 -ctvf testdb_create.sql
+[manager@localhost]$ db2 -ctvf testdb_data.sql
+[manager@localhost]$ db2 -ctvf testdb_metadata.sql
+[manager@localhost]$ db2 "connect reset"
+
+3.3.5) Run the test:
+[root@localhost]# cd $SOURCES/tests
+[root@localhost]# SLAPD_USE_SQL=ibmdb2 ./run sql-test000
+
+4) Cleanup:
+The test is basically readonly; this can be performed by all RDBMSes
+(listed above).
+
+There is another test, sql-test900-write, which is currently enabled
+only for PostgreSQL and IBM db2. Note that after a successful run
+of the write test, the database is no longer in the correct state
+to restart either of the tests, and step 3.X.4 needs to be re-run first.
+
+More tests are to come; PostgreSQL is known to allow a full reload
+of the test database starting from an empty database.
+
diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_create.sql b/servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_create.sql
new file mode 100644
index 0000000..cb2856b
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_create.sql
@@ -0,0 +1,59 @@
+drop table ldap_oc_mappings;
+create table ldap_oc_mappings
+ (
+ id integer not null primary key,
+ name varchar(64) not null,
+ keytbl varchar(64) not null,
+ keycol varchar(64) not null,
+ create_proc varchar(255),
+ create_keyval varchar(255),
+ delete_proc varchar(255),
+ expect_return integer not null
+);
+
+drop table ldap_attr_mappings;
+create table ldap_attr_mappings
+ (
+ id integer not null primary key,
+ oc_map_id integer not null references ldap_oc_mappings(id),
+ name varchar(255) not null,
+ sel_expr varchar(255) not null,
+ sel_expr_u varchar(255),
+ from_tbls varchar(255) not null,
+ join_where varchar(255),
+ add_proc varchar(255),
+ delete_proc varchar(255),
+ param_order integer not null,
+ expect_return integer not null
+);
+
+drop table ldap_entries;
+create table ldap_entries
+ (
+ id integer not null primary key,
+ dn varchar(255) not null,
+ oc_map_id integer not null references ldap_oc_mappings(id),
+ parent int NOT NULL ,
+ keyval int NOT NULL
+);
+
+alter table ldap_entries add
+ constraint unq1_ldap_entries unique
+ (
+ oc_map_id,
+ keyval
+ );
+
+alter table ldap_entries add
+ constraint unq2_ldap_entries unique
+ (
+ dn
+ );
+
+drop table ldap_entry_objclasses;
+create table ldap_entry_objclasses
+ (
+ entry_id integer not null references ldap_entries(id),
+ oc_name varchar(64)
+ );
+
diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_drop.sql b/servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_drop.sql
new file mode 100644
index 0000000..49e7e3a
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/backsql_drop.sql
@@ -0,0 +1,5 @@
+DROP TABLE ldap_referrals;
+DROP TABLE ldap_entry_objclasses;
+DROP TABLE ldap_attr_mappings;
+DROP TABLE ldap_entries;
+DROP TABLE ldap_oc_mappings;
diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/slapd.conf b/servers/slapd/back-sql/rdbms_depend/ibmdb2/slapd.conf
new file mode 100644
index 0000000..f6c1613
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/slapd.conf
@@ -0,0 +1,36 @@
+# $OpenLDAP$
+#
+# See slapd.conf(5) for details on configuration options.
+# This file should NOT be world readable.
+#
+include /usr/local/etc/openldap/schema/core.schema
+include /usr/local/etc/openldap/schema/cosine.schema
+include /usr/local/etc/openldap/schema/inetorgperson.schema
+
+# Define global ACLs to disable default read access.
+
+# Do not enable referrals until AFTER you have a working directory
+# service AND an understanding of referrals.
+#referral ldap://root.openldap.org
+
+pidfile /usr/local/var/slapd.pid
+argsfile /usr/local/var/slapd.args
+
+#######################################################################
+# sql database definitions
+#######################################################################
+
+database sql
+suffix "o=sql,c=RU"
+rootdn "cn=root,o=sql,c=RU"
+rootpw secret
+dbname ldap_db2
+dbuser db2inst1
+dbpasswd ibmdb2
+subtree_cond "upper(ldap_entries.dn) LIKE CONCAT('%',?)"
+insentry_stmt "insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values ((select max(id)+1 from ldap_entries),?,?,?,?)"
+upper_func "upper"
+upper_needs_cast "yes"
+create_needs_select "yes"
+has_ldapinfo_dn_ru "no"
+
diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_create.sql b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_create.sql
new file mode 100644
index 0000000..b6e850c
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_create.sql
@@ -0,0 +1,75 @@
+drop table persons;
+CREATE TABLE persons (
+ id int NOT NULL,
+ name varchar(255) NOT NULL,
+ surname varchar(255) NOT NULL,
+ password varchar(64)
+);
+
+drop table institutes;
+CREATE TABLE institutes (
+ id int NOT NULL,
+ name varchar(255)
+);
+
+drop table documents;
+CREATE TABLE documents (
+ id int NOT NULL,
+ title varchar(255) NOT NULL,
+ abstract varchar(255)
+);
+
+drop table authors_docs;
+CREATE TABLE authors_docs (
+ pers_id int NOT NULL,
+ doc_id int NOT NULL
+);
+
+drop table phones;
+CREATE TABLE phones (
+ id int NOT NULL ,
+ phone varchar(255) NOT NULL ,
+ pers_id int NOT NULL
+);
+
+drop table referrals;
+CREATE TABLE referrals (
+ id int NOT NULL,
+ name varchar(255) NOT NULL,
+ url varchar(255) NOT NULL
+);
+
+
+
+ALTER TABLE authors_docs ADD
+ CONSTRAINT PK_authors_docs PRIMARY KEY
+ (
+ pers_id,
+ doc_id
+ );
+
+ALTER TABLE documents ADD
+ CONSTRAINT PK_documents PRIMARY KEY
+ (
+ id
+ );
+
+ALTER TABLE institutes ADD
+ CONSTRAINT PK_institutes PRIMARY KEY
+ (
+ id
+ );
+
+
+ALTER TABLE persons ADD
+ CONSTRAINT PK_persons PRIMARY KEY
+ (
+ id
+ );
+
+ALTER TABLE phones ADD
+ CONSTRAINT PK_phones PRIMARY KEY
+ (
+ id
+ );
+
diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_data.sql b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_data.sql
new file mode 100644
index 0000000..7bef374
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_data.sql
@@ -0,0 +1,18 @@
+insert into institutes (id,name) values (1,'Example');
+
+insert into persons (id,name,surname,password) values (1,'Mitya','Kovalev','mit');
+insert into persons (id,name,surname) values (2,'Torvlobnor','Puzdoy');
+insert into persons (id,name,surname) values (3,'Akakiy','Zinberstein');
+
+insert into phones (id,phone,pers_id) values (1,'332-2334',1);
+insert into phones (id,phone,pers_id) values (2,'222-3234',1);
+insert into phones (id,phone,pers_id) values (3,'545-4563',2);
+
+insert into documents (id,abstract,title) values (1,'abstract1','book1');
+insert into documents (id,abstract,title) values (2,'abstract2','book2');
+
+insert into authors_docs (pers_id,doc_id) values (1,1);
+insert into authors_docs (pers_id,doc_id) values (1,2);
+insert into authors_docs (pers_id,doc_id) values (2,1);
+
+insert into referrals (id,name,url) values (1,'Referral','ldap://localhost:9012/');
diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_drop.sql b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_drop.sql
new file mode 100644
index 0000000..17b12af
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_drop.sql
@@ -0,0 +1,5 @@
+DROP TABLE persons;
+DROP TABLE institutes;
+DROP TABLE documents;
+DROP TABLE authors_docs;
+DROP TABLE phones;
diff --git a/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_metadata.sql b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_metadata.sql
new file mode 100644
index 0000000..0b0d1c2
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/ibmdb2/testdb_metadata.sql
@@ -0,0 +1,123 @@
+--mappings
+
+-- objectClass mappings: these may be viewed as structuralObjectClass, the ones that are used to decide how to build an entry
+-- id a unique number identifying the objectClass
+-- name the name of the objectClass; it MUST match the name of an objectClass that is loaded in slapd's schema
+-- keytbl the name of the table that is referenced for the primary key of an entry
+-- keycol the name of the column in "keytbl" that contains the primary key of an entry; the pair "keytbl.keycol" uniquely identifies an entry of objectClass "id"
+-- create_proc a procedure to create the entry
+-- create_keyval a query that returns the id of the last inserted entry
+-- delete_proc a procedure to delete the entry; it takes "keytbl.keycol" of the row to be deleted
+-- expect_return a bitmap that marks whether create_proc (1) and delete_proc (2) return a value or not
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,create_keyval,delete_proc,expect_return)
+values (1,'inetOrgPerson','persons','id','INSERT INTO persons (id,name,surname) VALUES ((SELECT max(id)+1 FROM persons),'''','''')',
+ 'SELECT max(id) FROM persons','DELETE FROM persons WHERE id=?',0);
+
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,create_keyval,delete_proc,expect_return)
+values (2,'document','documents','id','INSERT INTO documents (id,title,abstract) VALUES ((SELECT max(id)+1 FROM documents),'''','''')',
+ 'SELECT max(id) FROM documents','DELETE FROM documents WHERE id=?',0);
+
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,create_keyval,delete_proc,expect_return)
+values (3,'organization','institutes','id','INSERT INTO institutes (id,name) VALUES ((SELECT max(id)+1 FROM institutes),'''')',
+ 'SELECT max(id) FROM institutes','DELETE FROM institutes WHERE id=?',0);
+
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,create_keyval,delete_proc,expect_return)
+values (4,'referral','referrals','id','INSERT INTO referrals (id,name,url) VALUES ((SELECT max(id)+1 FROM referrals),'''','''')',
+ 'SELECT max(id) FROM referrals','DELETE FROM referrals WHERE id=?',0);
+
+-- attributeType mappings: describe how an attributeType for a certain objectClass maps to the SQL data.
+-- id a unique number identifying the attribute
+-- oc_map_id the value of "ldap_oc_mappings.id" that identifies the objectClass this attributeType is defined for
+-- name the name of the attributeType; it MUST match the name of an attributeType that is loaded in slapd's schema
+-- sel_expr the expression that is used to select this attribute (the "select <sel_expr> from ..." portion)
+-- from_tbls the expression that defines the table(s) this attribute is taken from (the "select ... from <from_tbls> where ..." portion)
+-- join_where the expression that defines the condition to select this attribute (the "select ... where <join_where> ..." portion)
+-- add_proc a procedure to insert the attribute; it takes the value of the attribute that is added, and the "keytbl.keycol" of the entry it is associated to
+-- delete_proc a procedure to delete the attribute; it takes the value of the attribute that is added, and the "keytbl.keycol" of the entry it is associated to
+-- param_order a mask that marks if the "keytbl.keycol" value comes before or after the value in add_proc (1) and delete_proc (2)
+-- expect_return a mask that marks whether add_proc (1) and delete_proc(2) are expected to return a value or not
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (1,1,'cn','persons.name||'' ''||persons.surname','persons',NULL,NULL,NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (2,1,'telephoneNumber','phones.phone','persons,phones',
+ 'phones.pers_id=persons.id','INSERT INTO phones (id,phone,pers_id) VALUES ((SELECT max(id)+1 FROM phones),?,?)',
+ 'DELETE FROM phones WHERE phone=? AND pers_id=?',3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (4,1,'givenName','persons.name','persons',NULL,'UPDATE persons SET name=? WHERE id=?',NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (3,1,'sn','persons.surname','persons',NULL,'UPDATE persons SET surname=? WHERE id=?',NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (5,1,'userPassword','persons.password','persons','persons.password IS NOT NULL','UPDATE persons SET password=? WHERE id=?',
+ 'UPDATE persons SET password=NULL WHERE password=? AND id=?',3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (6,1,'seeAlso','seeAlso.dn','ldap_entries AS seeAlso,documents,authors_docs,persons',
+ 'seeAlso.keyval=documents.id AND seeAlso.oc_map_id=2 AND authors_docs.doc_id=documents.id AND authors_docs.pers_id=persons.id',
+ NULL,NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (7,2,'description','documents.abstract','documents',NULL,'UPDATE documents SET abstract=? WHERE id=?',NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (8,2,'documentTitle','documents.title','documents',NULL,'UPDATE documents SET title=? WHERE id=?',NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (9,2,'documentAuthor','documentAuthor.dn','ldap_entries AS documentAuthor,documents,authors_docs,persons',
+ 'documentAuthor.keyval=persons.id AND documentAuthor.oc_map_id=1 AND authors_docs.doc_id=documents.id AND authors_docs.pers_id=persons.id',
+ 'INSERT INTO authors_docs (pers_id,doc_id) VALUES ((SELECT keyval FROM ldap_entries WHERE ucase(cast(? AS VARCHAR(255)))=ucase(dn)),?)',
+ 'DELETE FROM authors_docs WHERE pers_id=(SELECT keyval FROM ldap_entries WHERE ucase(cast(? AS VARCHAR(255))=ucase(dn)) AND doc_id=?',3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (10,2,'documentIdentifier','''document ''||rtrim(cast(documents.id AS CHAR(16)))','documents',NULL,NULL,NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (11,3,'o','institutes.name','institutes',NULL,'UPDATE institutes SET name=? WHERE id=?',NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (12,3,'dc','lcase(institutes.name)','institutes,ldap_entries AS dcObject,ldap_entry_objclasses as auxObjectClass',
+ 'institutes.id=dcObject.keyval AND dcObject.oc_map_id=3 AND dcObject.id=auxObjectClass.entry_id AND auxObjectClass.oc_name=''dcObject''',
+ NULL,NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (13,4,'ou','referrals.name','referrals',NULL,'UPDATE referrals SET name=? WHERE id=?',NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (14,4,'ref','referrals.url','referrals',NULL,'UPDATE referrals SET url=? WHERE id=?',NULL,3,0);
+
+-- entries mapping: each entry must appear in this table, with a unique DN rooted at the database naming context
+-- id a unique number > 0 identifying the entry
+-- dn the DN of the entry, in "pretty" form
+-- oc_map_id the "ldap_oc_mappings.id" of the main objectClass of this entry (view it as the structuralObjectClass)
+-- parent the "ldap_entries.id" of the parent of this objectClass; 0 if it is the "suffix" of the database
+-- keyval the value of the "keytbl.keycol" defined for this objectClass
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (1,'dc=example,dc=com',3,0,1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (2,'cn=Mitya Kovalev,dc=example,dc=com',1,1,1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (3,'cn=Torvlobnor Puzdoy,dc=example,dc=com',1,1,2);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (4,'cn=Akakiy Zinberstein,dc=example,dc=com',1,1,3);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (5,'documentTitle=book1,dc=example,dc=com',2,1,1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (6,'documentTitle=book2,dc=example,dc=com',2,1,2);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (7,'ou=Referral,dc=example,dc=com',4,1,1);
+
+-- objectClass mapping: entries that have multiple objectClass instances are listed here with the objectClass name (view them as auxiliary objectClass)
+-- entry_id the "ldap_entries.id" of the entry this objectClass value must be added
+-- oc_name the name of the objectClass; it MUST match the name of an objectClass that is loaded in slapd's schema
+insert into ldap_entry_objclasses (entry_id,oc_name) values (1,'dcObject');
+
+insert into ldap_entry_objclasses (entry_id,oc_name) values (7,'extensibleObject');
diff --git a/servers/slapd/back-sql/rdbms_depend/mssql/backsql_create.sql b/servers/slapd/back-sql/rdbms_depend/mssql/backsql_create.sql
new file mode 100644
index 0000000..1f1f6d2
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/mssql/backsql_create.sql
@@ -0,0 +1,100 @@
+create table ldap_oc_mappings (
+ id int identity (1, 1) not null ,
+ name varchar (64) not null ,
+ keytbl varchar (64) not null ,
+ keycol varchar (64) not null ,
+ create_proc varchar (255) NULL ,
+ delete_proc varchar (255) NULL,
+ expect_return int not null
+)
+GO
+
+alter table ldap_oc_mappings add
+ constraint pk_ldap_oc_mappings primary key
+ (
+ id
+ )
+GO
+
+
+alter table ldap_oc_mappings add
+ constraint unq1_ldap_oc_mappings unique
+ (
+ name
+ )
+GO
+
+
+create table ldap_attr_mappings (
+ id int identity (1, 1) not null ,
+ oc_map_id int not null references ldap_oc_mappings(id),
+ name varchar (255) not null ,
+ sel_expr varchar (255) not null ,
+ sel_expr_u varchar(255),
+ from_tbls varchar (255) not null ,
+ join_where varchar (255) NULL ,
+ add_proc varchar (255) NULL ,
+ delete_proc varchar (255) NULL ,
+ param_order int not null,
+ expect_return int not null
+)
+GO
+
+alter table ldap_attr_mappings add
+ constraint pk_ldap_attr_mappings primary key
+ (
+ id
+ )
+GO
+
+
+create table ldap_entries (
+ id int identity (1, 1) not null ,
+ dn varchar (255) not null ,
+ oc_map_id int not null references ldap_oc_mappings(id),
+ parent int not null ,
+ keyval int not null
+)
+GO
+
+
+alter table ldap_entries add
+ constraint pk_ldap_entries primary key
+ (
+ id
+ )
+GO
+
+alter table ldap_entries add
+ constraint unq1_ldap_entries unique
+ (
+ oc_map_id,
+ keyval
+ )
+GO
+
+alter table ldap_entries add
+ constraint unq2_ldap_entries unique
+ (
+ dn
+ )
+GO
+
+
+create table ldap_referrals
+ (
+ entry_id int not null references ldap_entries(id),
+ url text not null
+)
+GO
+
+create index entry_idx on ldap_referrals(entry_id);
+
+create table ldap_entry_objclasses
+ (
+ entry_id int not null references ldap_entries(id),
+ oc_name varchar(64)
+ )
+GO
+
+create index entry_idx on ldap_entry_objclasses(entry_id);
diff --git a/servers/slapd/back-sql/rdbms_depend/mssql/backsql_drop.sql b/servers/slapd/back-sql/rdbms_depend/mssql/backsql_drop.sql
new file mode 100644
index 0000000..0e888b3
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/mssql/backsql_drop.sql
@@ -0,0 +1,14 @@
+drop table ldap_attr_mappings
+GO
+
+drop table ldap_referrals
+GO
+
+drop table ldap_entry_objclasses
+GO
+
+drop table ldap_entries
+GO
+
+drop table ldap_oc_mappings
+GO
diff --git a/servers/slapd/back-sql/rdbms_depend/mssql/slapd.conf b/servers/slapd/back-sql/rdbms_depend/mssql/slapd.conf
new file mode 100644
index 0000000..c3032f2
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/mssql/slapd.conf
@@ -0,0 +1,30 @@
+# $OpenLDAP$
+#
+# See slapd.conf(5) for details on configuration options.
+# This file should NOT be world readable.
+#
+include ./schema/core.schema
+include ./schema/cosine.schema
+include ./schema/inetorgperson.schema
+
+# Define global ACLs to disable default read access.
+
+# Do not enable referrals until AFTER you have a working directory
+# service AND an understanding of referrals.
+#referral ldap://root.openldap.org
+
+pidfile ./slapd.pid
+argsfile ./slapd.args
+
+#######################################################################
+# sql database definitions
+#######################################################################
+
+database sql
+suffix "o=sql,c=RU"
+rootdn "cn=root,o=sql,c=RU"
+rootpw secret
+dbname ldap_mssql
+dbuser ldap
+dbpasswd ldap
+subtree_cond "ldap_entries.dn LIKE '%'+?"
diff --git a/servers/slapd/back-sql/rdbms_depend/mssql/testdb_create.sql b/servers/slapd/back-sql/rdbms_depend/mssql/testdb_create.sql
new file mode 100644
index 0000000..2034afd
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/mssql/testdb_create.sql
@@ -0,0 +1,74 @@
+
+CREATE TABLE authors_docs (
+ pers_id int NOT NULL ,
+ doc_id int NOT NULL
+)
+GO
+
+CREATE TABLE documents (
+ id int IDENTITY (1, 1) NOT NULL ,
+ abstract varchar (255) NULL ,
+ title varchar (255) NULL ,
+ body binary (255) NULL
+)
+GO
+
+CREATE TABLE institutes (
+ id int IDENTITY (1, 1) NOT NULL ,
+ name varchar (255) NOT NULL
+)
+GO
+
+
+CREATE TABLE persons (
+ id int IDENTITY (1, 1) NOT NULL ,
+ name varchar (255) NULL ,
+ surname varchar (255) NULL ,
+ password varchar (64) NULL
+)
+GO
+
+CREATE TABLE phones (
+ id int IDENTITY (1, 1) NOT NULL ,
+ phone varchar (255) NOT NULL ,
+ pers_id int NOT NULL
+)
+GO
+
+ALTER TABLE authors_docs WITH NOCHECK ADD
+ CONSTRAINT PK_authors_docs PRIMARY KEY
+ (
+ pers_id,
+ doc_id
+ )
+GO
+
+ALTER TABLE documents WITH NOCHECK ADD
+ CONSTRAINT PK_documents PRIMARY KEY
+ (
+ id
+ )
+GO
+
+ALTER TABLE institutes WITH NOCHECK ADD
+ CONSTRAINT PK_institutes PRIMARY KEY
+ (
+ id
+ )
+GO
+
+
+ALTER TABLE persons WITH NOCHECK ADD
+ CONSTRAINT PK_persons PRIMARY KEY
+ (
+ id
+ )
+GO
+
+ALTER TABLE phones WITH NOCHECK ADD
+ CONSTRAINT PK_phones PRIMARY KEY
+ (
+ id
+ )
+GO
+
diff --git a/servers/slapd/back-sql/rdbms_depend/mssql/testdb_data.sql b/servers/slapd/back-sql/rdbms_depend/mssql/testdb_data.sql
new file mode 100644
index 0000000..21a51ef
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/mssql/testdb_data.sql
@@ -0,0 +1,24 @@
+set IDENTITY_INSERT institutes ON
+insert into institutes (id,name) values (1,'Example')
+set IDENTITY_INSERT institutes OFF
+
+set IDENTITY_INSERT persons ON
+insert into persons (id,name,surname,password) values (1,'Mitya','Kovalev','mit')
+insert into persons (id,name,surname) values (2,'Torvlobnor','Puzdoy')
+insert into persons (id,name,surname) values (3,'Akakiy','Zinberstein')
+set IDENTITY_INSERT persons OFF
+
+set IDENTITY_INSERT phones ON
+insert into phones (id,phone,pers_id) values (1,'332-2334',1)
+insert into phones (id,phone,pers_id) values (2,'222-3234',1)
+insert into phones (id,phone,pers_id) values (3,'545-4563',2)
+set IDENTITY_INSERT phones OFF
+
+set IDENTITY_INSERT documents ON
+insert into documents (id,abstract,title) values (1,'abstract1','book1')
+insert into documents (id,abstract,title) values (2,'abstract2','book2')
+set IDENTITY_INSERT documents OFF
+
+insert into authors_docs (pers_id,doc_id) values (1,1)
+insert into authors_docs (pers_id,doc_id) values (1,2)
+insert into authors_docs (pers_id,doc_id) values (2,1)
diff --git a/servers/slapd/back-sql/rdbms_depend/mssql/testdb_drop.sql b/servers/slapd/back-sql/rdbms_depend/mssql/testdb_drop.sql
new file mode 100644
index 0000000..4842ed8
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/mssql/testdb_drop.sql
@@ -0,0 +1,39 @@
+drop procedure create_person
+drop procedure set_person_name
+drop procedure delete_phone
+drop procedure add_phone
+drop procedure make_doc_link
+drop procedure del_doc_link
+drop procedure delete_person
+
+drop procedure create_org
+drop procedure set_org_name
+drop procedure delete_org
+
+drop procedure create_document
+drop procedure set_doc_title
+drop procedure set_doc_abstract
+drop procedure make_author_link
+drop procedure del_author_link
+drop procedure delete_document
+
+if exists (select * from sysobjects where id = object_id(N'authors_docs') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
+drop table authors_docs
+GO
+
+if exists (select * from sysobjects where id = object_id(N'documents') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
+drop table documents
+GO
+
+if exists (select * from sysobjects where id = object_id(N'institutes') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
+drop table institutes
+GO
+
+if exists (select * from sysobjects where id = object_id(N'persons') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
+drop table persons
+GO
+
+if exists (select * from sysobjects where id = object_id(N'phones') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
+drop table phones
+GO
+
diff --git a/servers/slapd/back-sql/rdbms_depend/mssql/testdb_metadata.sql b/servers/slapd/back-sql/rdbms_depend/mssql/testdb_metadata.sql
new file mode 100644
index 0000000..e087523
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/mssql/testdb_metadata.sql
@@ -0,0 +1,198 @@
+-- mappings
+
+
+SET IDENTITY_INSERT ldap_oc_mappings ON
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,delete_proc,expect_return)
+values (1,'inetOrgPerson','persons','id','{call create_person(?)}','{call delete_person(?)}',0)
+
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,delete_proc,expect_return)
+values (2,'document','documents','id','{call create_document(?)}','{call delete_document(?)}',0)
+
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,delete_proc,expect_return)
+values (3,'organization','institutes','id','{call create_org(?)}','{call delete_org(?)}',0)
+SET IDENTITY_INSERT ldap_oc_mappings OFF
+
+
+SET IDENTITY_INSERT ldap_attr_mappings ON
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (1,1,'cn','persons.name+'' ''+persons.surname','persons',NULL,
+ NULL,NULL,0,0)
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (2,1,'telephoneNumber','phones.phone','persons,phones',
+ 'phones.pers_id=persons.id','{call add_phone(?,?)}',
+ '{call delete_phone(?,?)}',0,0)
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (3,1,'givenName','persons.name','persons',NULL,
+ '{call set_person_name(?,?)}',NULL,0,0)
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (4,1,'sn','persons.surname','persons',NULL,
+ '{call set_person_surname(?,?)}',NULL,0,0)
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (5,1,'userPassword','persons.password','persons','persons.password IS NOT NULL',
+ '{call set_person_password(?,?)}','call del_person_password(?,?)',0,0)
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (6,1,'seeAlso','seeAlso.dn','ldap_entries AS seeAlso,documents,authors_docs,persons',
+ 'seeAlso.keyval=documents.id AND seeAlso.oc_map_id=2 AND authors_docs.doc_id=documents.id AND authors_docs.pers_id=persons.id',
+ NULL,NULL,0,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (7,2,'description','documents.abstract','documents',NULL,'{call set_doc_abstract(?,?)}',
+ NULL,0,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (8,2,'documentTitle','documents.title','documents',NULL, '{call set_doc_title(?,?)}',
+ NULL,0,0)
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (9,2,'documentAuthor','documentAuthor.dn','ldap_entries AS documentAuthor,documents,authors_docs,persons',
+ 'documentAuthor.keyval=persons.id AND documentAuthor.oc_map_id=1 AND authors_docs.doc_id=documents.id AND authors_docs.pers_id=persons.id',
+ 'INSERT INTO authors_docs (pers_id,doc_id) VALUES ((SELECT ldap_entries.keyval FROM ldap_entries WHERE upper(?)=upper(ldap_entries.dn)),?)',
+ 'DELETE FROM authors_docs WHERE authors_docs.pers_id=(SELECT ldap_entries.keyval FROM ldap_entries WHERE upper(?)=upper(ldap_entries.dn)) AND authors_docs.doc_id=?',3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (10,2,'documentIdentifier','''document ''+text(documents.id)','documents',
+ NULL,NULL,NULL,0,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (11,3,'o','institutes.name','institutes',NULL,NULL,NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (12,3,'dc','lower(institutes.name)','institutes,ldap_entries AS dcObject,ldap_entry_objclasses AS auxObjectClass',
+ 'institutes.id=dcObject.keyval AND dcObject.oc_map_id=3 AND dcObject.id=auxObjectClass.entry_id AND auxObjectClass.oc_name=''dcObject''',
+ '{call set_org_name(?,?)}',NULL,3,0);
+
+SET IDENTITY_INSERT ldap_attr_mappings OFF
+
+-- entries
+
+SET IDENTITY_INSERT ldap_entries ON
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (1,'dc=example,dc=com',3,0,1)
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (2,'cn=Mitya Kovalev,dc=example,dc=com',1,1,1)
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (3,'cn=Torvlobnor Puzdoy,dc=example,dc=com',1,1,2)
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (4,'cn=Akakiy Zinberstein,dc=example,dc=com',1,1,3)
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (5,'documentTitle=book1,dc=example,dc=com',2,1,1)
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (6,'documentTitle=book2,dc=example,dc=com',2,1,2)
+
+SET IDENTITY_INSERT ldap_entries OFF
+
+-- referrals
+insert into ldap_entry_objclasses (entry_id,oc_name)
+values (1,'dcObject');
+
+insert into ldap_entry_objclasses (entry_id,oc_name)
+values (4,'referral');
+
+insert into ldap_referrals (entry_id,url)
+values (4,'ldap://localhost:9012/');
+
+-- support procedures
+
+SET QUOTED_IDENTIFIER OFF SET ANSI_NULLS ON
+GO
+
+
+CREATE PROCEDURE create_person @@keyval int OUTPUT AS
+INSERT INTO example.persons (name) VALUES ('');
+set @@keyval=(SELECT MAX(id) FROM example.persons)
+GO
+
+CREATE PROCEDURE delete_person @keyval int AS
+DELETE FROM example.phones WHERE pers_id=@keyval;
+DELETE FROM example.authors_docs WHERE pers_id=@keyval;
+DELETE FROM example.persons WHERE id=@keyval;
+GO
+
+CREATE PROCEDURE create_org @@keyval int OUTPUT AS
+INSERT INTO example.institutes (name) VALUES ('');
+set @@keyval=(SELECT MAX(id) FROM example.institutes)
+GO
+
+CREATE PROCEDURE delete_org @keyval int AS
+DELETE FROM example.institutes WHERE id=@keyval;
+GO
+
+CREATE PROCEDURE create_document @@keyval int OUTPUT AS
+INSERT INTO example.documents (title) VALUES ('');
+set @@keyval=(SELECT MAX(id) FROM example.documents)
+GO
+
+CREATE PROCEDURE delete_document @keyval int AS
+DELETE FROM example.authors_docs WHERE doc_id=@keyval;
+DELETE FROM example.documents WHERE id=@keyval;
+GO
+
+CREATE PROCEDURE add_phone @pers_id int, @phone varchar(255) AS
+INSERT INTO example.phones (pers_id,phone) VALUES (@pers_id,@phone)
+GO
+
+CREATE PROCEDURE delete_phone @keyval int,@phone varchar(64) AS
+DELETE FROM example.phones WHERE pers_id=@keyval AND phone=@phone;
+GO
+
+CREATE PROCEDURE set_person_name @keyval int, @new_name varchar(255) AS
+UPDATE example.persons SET name=@new_name WHERE id=@keyval;
+GO
+
+CREATE PROCEDURE set_person_surname @keyval int, @new_surname varchar(255) AS
+UPDATE example.persons SET surname=@new_surname WHERE id=@keyval;
+GO
+
+CREATE PROCEDURE set_org_name @keyval int, @new_name varchar(255) AS
+UPDATE example.institutes SET name=@new_name WHERE id=@keyval;
+GO
+
+CREATE PROCEDURE set_doc_title @keyval int, @new_title varchar(255) AS
+UPDATE example.documents SET title=@new_title WHERE id=@keyval;
+GO
+
+CREATE PROCEDURE set_doc_abstract @keyval int, @new_abstract varchar(255) AS
+UPDATE example.documents SET abstract=@new_abstract WHERE id=@keyval;
+GO
+
+CREATE PROCEDURE make_author_link @keyval int, @author_dn varchar(255) AS
+DECLARE @per_id int;
+SET @per_id=(SELECT keyval FROM example.ldap_entries
+ WHERE oc_map_id=1 AND dn=@author_dn);
+IF NOT (@per_id IS NULL)
+ INSERT INTO example.authors_docs (doc_id,pers_id) VALUES (@keyval,@per_id);
+GO
+
+CREATE PROCEDURE make_doc_link @keyval int, @doc_dn varchar(255) AS
+DECLARE @doc_id int;
+SET @doc_id=(SELECT keyval FROM example.ldap_entries
+ WHERE oc_map_id=2 AND dn=@doc_dn);
+IF NOT (@doc_id IS NULL)
+ INSERT INTO example.authors_docs (pers_id,doc_id) VALUES (@keyval,@doc_id);
+GO
+
+CREATE PROCEDURE del_doc_link @keyval int, @doc_dn varchar(255) AS
+DECLARE @doc_id int;
+SET @doc_id=(SELECT keyval FROM example.ldap_entries
+ WHERE oc_map_id=2 AND dn=@doc_dn);
+IF NOT (@doc_id IS NULL)
+DELETE FROM example.authors_docs WHERE pers_id=@keyval AND doc_id=@doc_id;
+GO
+
+CREATE PROCEDURE del_author_link @keyval int, @author_dn varchar(255) AS
+DECLARE @per_id int;
+SET @per_id=(SELECT keyval FROM example.ldap_entries
+ WHERE oc_map_id=1 AND dn=@author_dn);
+IF NOT (@per_id IS NULL)
+ DELETE FROM example.authors_docs WHERE doc_id=@keyval AND pers_id=@per_id;
+GO
diff --git a/servers/slapd/back-sql/rdbms_depend/mysql/backsql_create.sql b/servers/slapd/back-sql/rdbms_depend/mysql/backsql_create.sql
new file mode 100644
index 0000000..b2029c4
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/mysql/backsql_create.sql
@@ -0,0 +1,58 @@
+drop table if exists ldap_oc_mappings;
+create table ldap_oc_mappings
+ (
+ id integer unsigned not null primary key auto_increment,
+ name varchar(64) not null,
+ keytbl varchar(64) not null,
+ keycol varchar(64) not null,
+ create_proc varchar(255),
+ delete_proc varchar(255),
+ expect_return tinyint not null
+);
+
+drop table if exists ldap_attr_mappings;
+create table ldap_attr_mappings
+ (
+ id integer unsigned not null primary key auto_increment,
+ oc_map_id integer unsigned not null references ldap_oc_mappings(id),
+ name varchar(255) not null,
+ sel_expr varchar(255) not null,
+ sel_expr_u varchar(255),
+ from_tbls varchar(255) not null,
+ join_where varchar(255),
+ add_proc varchar(255),
+ delete_proc varchar(255),
+ param_order tinyint not null,
+ expect_return tinyint not null
+);
+
+drop table if exists ldap_entries;
+create table ldap_entries
+ (
+ id integer unsigned not null primary key auto_increment,
+ dn varchar(255) not null,
+ oc_map_id integer unsigned not null references ldap_oc_mappings(id),
+ parent int NOT NULL ,
+ keyval int NOT NULL
+);
+
+alter table ldap_entries add
+ constraint unq1_ldap_entries unique
+ (
+ oc_map_id,
+ keyval
+ );
+
+alter table ldap_entries add
+ constraint unq2_ldap_entries unique
+ (
+ dn
+ );
+
+drop table if exists ldap_entry_objclasses;
+create table ldap_entry_objclasses
+ (
+ entry_id integer unsigned not null references ldap_entries(id),
+ oc_name varchar(64)
+ );
+
diff --git a/servers/slapd/back-sql/rdbms_depend/mysql/backsql_drop.sql b/servers/slapd/back-sql/rdbms_depend/mysql/backsql_drop.sql
new file mode 100644
index 0000000..a81fa8b
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/mysql/backsql_drop.sql
@@ -0,0 +1,7 @@
+DROP TABLE IF EXISTS ldap_entry_objclasses;
+
+DROP TABLE IF EXISTS ldap_attr_mappings;
+
+DROP TABLE IF EXISTS ldap_entries;
+
+DROP TABLE IF EXISTS ldap_oc_mappings;
diff --git a/servers/slapd/back-sql/rdbms_depend/mysql/slapd.conf b/servers/slapd/back-sql/rdbms_depend/mysql/slapd.conf
new file mode 100644
index 0000000..8f6e4e1
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/mysql/slapd.conf
@@ -0,0 +1,32 @@
+# $OpenLDAP$
+#
+# See slapd.conf(5) for details on configuration options.
+# This file should NOT be world readable.
+#
+include /usr/local/etc/openldap/schema/core.schema
+include /usr/local/etc/openldap/schema/cosine.schema
+include /usr/local/etc/openldap/schema/inetorgperson.schema
+
+# Define global ACLs to disable default read access.
+
+# Do not enable referrals until AFTER you have a working directory
+# service AND an understanding of referrals.
+#referral ldap://root.openldap.org
+
+pidfile /usr/local/var/slapd.pid
+argsfile /usr/local/var/slapd.args
+
+#######################################################################
+# sql database definitions
+#######################################################################
+
+database sql
+suffix "o=sql,c=RU"
+rootdn "cn=root,o=sql,c=RU"
+rootpw secret
+dbname ldap_mysql
+dbuser root
+dbpasswd
+subtree_cond "ldap_entries.dn LIKE CONCAT('%',?)"
+insentry_stmt "INSERT INTO ldap_entries (dn,oc_map_id,parent,keyval) VALUES (?,?,?,?)"
+has_ldapinfo_dn_ru no
diff --git a/servers/slapd/back-sql/rdbms_depend/mysql/testdb_create.sql b/servers/slapd/back-sql/rdbms_depend/mysql/testdb_create.sql
new file mode 100644
index 0000000..b35261b
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/mysql/testdb_create.sql
@@ -0,0 +1,86 @@
+drop table if exists persons;
+CREATE TABLE persons (
+ id int NOT NULL,
+ name varchar(255) NOT NULL,
+ surname varchar(255) NOT NULL,
+ password varchar(64)
+);
+
+drop table if exists institutes;
+CREATE TABLE institutes (
+ id int NOT NULL,
+ name varchar(255)
+);
+
+drop table if exists documents;
+CREATE TABLE documents (
+ id int NOT NULL,
+ title varchar(255) NOT NULL,
+ abstract varchar(255)
+);
+
+drop table if exists authors_docs;
+CREATE TABLE authors_docs (
+ pers_id int NOT NULL,
+ doc_id int NOT NULL
+);
+
+drop table if exists phones;
+CREATE TABLE phones (
+ id int NOT NULL ,
+ phone varchar(255) NOT NULL ,
+ pers_id int NOT NULL
+);
+
+drop table if exists certs;
+CREATE TABLE certs (
+ id int NOT NULL ,
+ cert LONGBLOB NOT NULL,
+ pers_id int NOT NULL
+);
+
+ALTER TABLE authors_docs ADD
+ CONSTRAINT PK_authors_docs PRIMARY KEY
+ (
+ pers_id,
+ doc_id
+ );
+
+ALTER TABLE documents ADD
+ CONSTRAINT PK_documents PRIMARY KEY
+ (
+ id
+ );
+
+ALTER TABLE institutes ADD
+ CONSTRAINT PK_institutes PRIMARY KEY
+ (
+ id
+ );
+
+
+ALTER TABLE persons ADD
+ CONSTRAINT PK_persons PRIMARY KEY
+ (
+ id
+ );
+
+ALTER TABLE phones ADD
+ CONSTRAINT PK_phones PRIMARY KEY
+ (
+ id
+ );
+
+ALTER TABLE certs ADD
+ CONSTRAINT PK_certs PRIMARY KEY
+ (
+ id
+ );
+
+drop table if exists referrals;
+CREATE TABLE referrals (
+ id int NOT NULL,
+ name varchar(255) NOT NULL,
+ url varchar(255) NOT NULL
+);
+
diff --git a/servers/slapd/back-sql/rdbms_depend/mysql/testdb_data.sql b/servers/slapd/back-sql/rdbms_depend/mysql/testdb_data.sql
new file mode 100644
index 0000000..0ccbfb7
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/mysql/testdb_data.sql
@@ -0,0 +1,21 @@
+insert into institutes (id,name) values (1,'Example');
+
+insert into persons (id,name,surname,password) values (1,'Mitya','Kovalev','mit');
+insert into persons (id,name,surname) values (2,'Torvlobnor','Puzdoy');
+insert into persons (id,name,surname) values (3,'Akakiy','Zinberstein');
+
+insert into phones (id,phone,pers_id) values (1,'332-2334',1);
+insert into phones (id,phone,pers_id) values (2,'222-3234',1);
+insert into phones (id,phone,pers_id) values (3,'545-4563',2);
+
+insert into documents (id,abstract,title) values (1,'abstract1','book1');
+insert into documents (id,abstract,title) values (2,'abstract2','book2');
+
+insert into authors_docs (pers_id,doc_id) values (1,1);
+insert into authors_docs (pers_id,doc_id) values (1,2);
+insert into authors_docs (pers_id,doc_id) values (2,1);
+
+insert into referrals (id,name,url) values (1,'Referral','ldap://localhost:9012/');
+
+insert into certs (id,cert,pers_id) values (1,UNHEX('3082036b308202d4a003020102020102300d06092a864886f70d01010405003077310b3009060355040613025553311330110603550408130a43616c69666f726e6961311f301d060355040a13164f70656e4c444150204578616d706c652c204c74642e311330110603550403130a4578616d706c65204341311d301b06092a864886f70d010901160e6361406578616d706c652e636f6d301e170d3033313031373136333331395a170d3034313031363136333331395a307e310b3009060355040613025553311330110603550408130a43616c69666f726e6961311f301d060355040a13164f70656e4c444150204578616d706c652c204c74642e311830160603550403130f557273756c612048616d7073746572311f301d06092a864886f70d01090116107568616d406578616d706c652e636f6d30819f300d06092a864886f70d010101050003818d0030818902818100eec60a7910b57d2e687158ca55eea738d36f10413dfecf31435e1aeeb9713b8e2da7dd2dde6bc6cec03b4987eaa7b037b9eb50e11c71e58088cc282883122cd8329c6f24f6045e6be9d21b9190c8292998267a5f7905292de936262747ab4b76a88a63872c41629a69d32e894d44c896a8d06fab0a1bc7de343c6c1458478f290203010001a381ff3081fc30090603551d1304023000302c06096086480186f842010d041f161d4f70656e53534c2047656e657261746564204365727469666963617465301d0603551d0e04160414a323de136c19ae0c479450e882dfb10ad147f45e3081a10603551d2304819930819680144b6f211a3624d290f943b053472d7de1c0e69823a17ba4793077310b3009060355040613025553311330110603550408130a43616c69666f726e6961311f301d060355040a13164f70656e4c444150204578616d706c652c204c74642e311330110603550403130a4578616d706c65204341311d301b06092a864886f70d010901160e6361406578616d706c652e636f6d820100300d06092a864886f70d010104050003818100881470045bdce95660d6e6af59e6a844aec4b9f5eaea88d4eb7a5a47080afa64750f81a3e47d00fd39c69a17a1c66d29d36f06edc537107f8c592239c2d4da55fb3f1d488e7b2387ad2a551cbd1ceb070ae9e020a9467275cb28798abb4cbfff98ddb3f1e7689b067072392511bb08125b5bec2bc207b7b6b275c47248f29acd'),3);
+
diff --git a/servers/slapd/back-sql/rdbms_depend/mysql/testdb_drop.sql b/servers/slapd/back-sql/rdbms_depend/mysql/testdb_drop.sql
new file mode 100644
index 0000000..7c5e9e7
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/mysql/testdb_drop.sql
@@ -0,0 +1,5 @@
+DROP TABLE IF EXISTS persons;
+DROP TABLE IF EXISTS institutes;
+DROP TABLE IF EXISTS documents;
+DROP TABLE IF EXISTS authors_docs;
+DROP TABLE IF EXISTS phones;
diff --git a/servers/slapd/back-sql/rdbms_depend/mysql/testdb_metadata.sql b/servers/slapd/back-sql/rdbms_depend/mysql/testdb_metadata.sql
new file mode 100644
index 0000000..d7e88e4
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/mysql/testdb_metadata.sql
@@ -0,0 +1,125 @@
+-- mappings
+
+-- objectClass mappings: these may be viewed as structuralObjectClass, the ones that are used to decide how to build an entry
+-- id a unique number identifying the objectClass
+-- name the name of the objectClass; it MUST match the name of an objectClass that is loaded in slapd's schema
+-- keytbl the name of the table that is referenced for the primary key of an entry
+-- keycol the name of the column in "keytbl" that contains the primary key of an entry; the pair "keytbl.keycol" uniquely identifies an entry of objectClass "id"
+-- create_proc a procedure to create the entry
+-- delete_proc a procedure to delete the entry; it takes "keytbl.keycol" of the row to be deleted
+-- expect_return a bitmap that marks whether create_proc (1) and delete_proc (2) return a value or not
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,delete_proc,expect_return)
+values (1,'inetOrgPerson','persons','id',NULL,NULL,0);
+
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,delete_proc,expect_return)
+values (2,'document','documents','id',NULL,NULL,0);
+
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,delete_proc,expect_return)
+values (3,'organization','institutes','id',NULL,NULL,0);
+
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,delete_proc,expect_return)
+values (4,'referral','referrals','id',NULL,NULL,0);
+
+-- attributeType mappings: describe how an attributeType for a certain objectClass maps to the SQL data.
+-- id a unique number identifying the attribute
+-- oc_map_id the value of "ldap_oc_mappings.id" that identifies the objectClass this attributeType is defined for
+-- name the name of the attributeType; it MUST match the name of an attributeType that is loaded in slapd's schema
+-- sel_expr the expression that is used to select this attribute (the "select <sel_expr> from ..." portion)
+-- from_tbls the expression that defines the table(s) this attribute is taken from (the "select ... from <from_tbls> where ..." portion)
+-- join_where the expression that defines the condition to select this attribute (the "select ... where <join_where> ..." portion)
+-- add_proc a procedure to insert the attribute; it takes the value of the attribute that is added, and the "keytbl.keycol" of the entry it is associated to
+-- delete_proc a procedure to delete the attribute; it takes the value of the attribute that is added, and the "keytbl.keycol" of the entry it is associated to
+-- param_order a mask that marks if the "keytbl.keycol" value comes before or after the value in add_proc (1) and delete_proc (2)
+-- expect_return a mask that marks whether add_proc (1) and delete_proc(2) are expected to return a value or not
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (1,1,'cn',"concat(persons.name,' ',persons.surname)",'persons',NULL,NULL,NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (2,1,'telephoneNumber','phones.phone','persons,phones',
+ 'phones.pers_id=persons.id',NULL,NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (3,1,'givenName','persons.name','persons',NULL,NULL,NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (4,1,'sn','persons.surname','persons',NULL,NULL,NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (5,1,'userPassword','persons.password','persons','persons.password IS NOT NULL',NULL,NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (6,1,'seeAlso','seeAlso.dn','ldap_entries AS seeAlso,documents,authors_docs,persons',
+ 'seeAlso.keyval=documents.id AND seeAlso.oc_map_id=2 AND authors_docs.doc_id=documents.id AND authors_docs.pers_id=persons.id',
+ NULL,NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (7,2,'description','documents.abstract','documents',NULL,NULL,NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (8,2,'documentTitle','documents.title','documents',NULL,NULL,NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (9,2,'documentAuthor','documentAuthor.dn','ldap_entries AS documentAuthor,documents,authors_docs,persons',
+ 'documentAuthor.keyval=persons.id AND documentAuthor.oc_map_id=1 AND authors_docs.doc_id=documents.id AND authors_docs.pers_id=persons.id',
+ NULL,NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (10,2,'documentIdentifier','concat(''document '',documents.id)','documents',NULL,NULL,NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (11,3,'o','institutes.name','institutes',NULL,NULL,NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (12,3,'dc','lower(institutes.name)','institutes,ldap_entries AS dcObject,ldap_entry_objclasses as auxObjectClass',
+ 'institutes.id=dcObject.keyval AND dcObject.oc_map_id=3 AND dcObject.id=auxObjectClass.entry_id AND auxObjectClass.oc_name=''dcObject''',
+ NULL,NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (13,4,'ou','referrals.name','referrals',NULL,NULL,NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (14,4,'ref','referrals.url','referrals',NULL,NULL,NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (15,1,'userCertificate','certs.cert','persons,certs',
+ 'certs.pers_id=persons.id',NULL,NULL,3,0);
+
+-- entries mapping: each entry must appear in this table, with a unique DN rooted at the database naming context
+-- id a unique number > 0 identifying the entry
+-- dn the DN of the entry, in "pretty" form
+-- oc_map_id the "ldap_oc_mappings.id" of the main objectClass of this entry (view it as the structuralObjectClass)
+-- parent the "ldap_entries.id" of the parent of this objectClass; 0 if it is the "suffix" of the database
+-- keyval the value of the "keytbl.keycol" defined for this objectClass
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (1,'dc=example,dc=com',3,0,1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (2,'cn=Mitya Kovalev,dc=example,dc=com',1,1,1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (3,'cn=Torvlobnor Puzdoy,dc=example,dc=com',1,1,2);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (4,'cn=Akakiy Zinberstein,dc=example,dc=com',1,1,3);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (5,'documentTitle=book1,dc=example,dc=com',2,1,1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (6,'documentTitle=book2,dc=example,dc=com',2,1,2);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (7,'ou=Referral,dc=example,dc=com',4,1,1);
+
+-- objectClass mapping: entries that have multiple objectClass instances are listed here with the objectClass name (view them as auxiliary objectClass)
+-- entry_id the "ldap_entries.id" of the entry this objectClass value must be added
+-- oc_name the name of the objectClass; it MUST match the name of an objectClass that is loaded in slapd's schema
+insert into ldap_entry_objclasses (entry_id,oc_name)
+values (1,'dcObject');
+
+insert into ldap_entry_objclasses (entry_id,oc_name)
+values (4,'pkiUser');
+
+insert into ldap_entry_objclasses (entry_id,oc_name)
+values (7,'extensibleObject');
+
diff --git a/servers/slapd/back-sql/rdbms_depend/oracle/backsql_create.sql b/servers/slapd/back-sql/rdbms_depend/oracle/backsql_create.sql
new file mode 100644
index 0000000..2e4e6ec
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/oracle/backsql_create.sql
@@ -0,0 +1,90 @@
+create table ldap_oc_mappings (
+ id number not null ,
+ name varchar2(64) not null ,
+ keytbl varchar2(64) not null ,
+ keycol varchar2(64) not null ,
+ create_proc varchar2(255),
+ delete_proc varchar2(255),
+ expect_return number not null
+);
+
+alter table ldap_oc_mappings add
+ constraint PK_ldap_oc_mappings primary key
+ (
+ id
+ );
+
+alter table ldap_oc_mappings add
+ constraint unq_ldap_oc_mappings unique
+ (
+ name
+ );
+
+create table ldap_attr_mappings (
+ id number not null,
+ oc_map_id number not null references ldap_oc_mappings(id),
+ name varchar2(255) not null,
+ sel_expr varchar2(255) not null,
+ sel_expr_u varchar2(255),
+ from_tbls varchar2(255) not null,
+ join_where varchar2(255),
+ add_proc varchar2(255),
+ delete_proc varchar2(255),
+ param_order number not null,
+ expect_return number not null
+);
+
+alter table ldap_attr_mappings add
+ constraint pk_ldap_attr_mappings primary key
+ (
+ id
+ );
+
+
+create table ldap_entries (
+ id number not null ,
+ dn varchar2(255) not null ,
+ dn_ru varchar2(255),
+ oc_map_id number not null references ldap_oc_mappings(id),
+ parent number not null ,
+ keyval number not null
+);
+
+alter table ldap_entries add
+ constraint PK_ldap_entries primary key
+ (
+ id
+ );
+
+alter table ldap_entries add
+ constraint unq1_ldap_entries unique
+ (
+ oc_map_id,
+ keyval
+ );
+
+alter table ldap_entries add
+ constraint unq2_ldap_entries unique
+ (
+ dn
+ );
+
+create sequence ldap_objclass_ids start with 1 increment by 1;
+
+create sequence ldap_attr_ids start with 1 increment by 1;
+
+create sequence ldap_entry_ids start with 1 increment by 1;
+
+create table ldap_referrals
+ (
+ entry_id number not null references ldap_entries(id),
+ url varchar(1023) not null
+);
+
+create table ldap_entry_objclasses
+ (
+ entry_id number not null references ldap_entries(id),
+ oc_name varchar(64)
+ );
+
+quit
diff --git a/servers/slapd/back-sql/rdbms_depend/oracle/backsql_drop.sql b/servers/slapd/back-sql/rdbms_depend/oracle/backsql_drop.sql
new file mode 100644
index 0000000..19bb8b6
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/oracle/backsql_drop.sql
@@ -0,0 +1,8 @@
+drop table ldap_attr_mappings;
+drop table ldap_entry_objclasses;
+drop table ldap_referrals;
+drop sequence ldap_entry_ids;
+drop sequence ldap_attr_ids;
+drop sequence ldap_objclass_ids;
+drop table ldap_entries;
+drop table ldap_oc_mappings;
diff --git a/servers/slapd/back-sql/rdbms_depend/oracle/slapd.conf b/servers/slapd/back-sql/rdbms_depend/oracle/slapd.conf
new file mode 100644
index 0000000..cc195d9
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/oracle/slapd.conf
@@ -0,0 +1,32 @@
+# $OpenLDAP$
+#
+# See slapd.conf(5) for details on configuration options.
+# This file should NOT be world readable.
+#
+include /usr/local/etc/openldap/schema/core.schema
+include /usr/local/etc/openldap/schema/cosine.schema
+include /usr/local/etc/openldap/schema/inetorgperson.schema
+
+# Define global ACLs to disable default read access.
+
+# Do not enable referrals until AFTER you have a working directory
+# service AND an understanding of referrals.
+#referral ldap://root.openldap.org
+
+pidfile /usr/local/var/slapd.pid
+argsfile /usr/local/var/slapd.args
+
+#######################################################################
+# sql database definitions
+#######################################################################
+
+database sql
+suffix "o=sql,c=RU"
+rootdn "cn=root,o=sql,c=RU"
+rootpw secret
+dbname ldap_ora8
+dbuser ldap
+dbpasswd ldap
+subtree_cond "UPPER(ldap_entries.dn) LIKE CONCAT('%',UPPER(?))"
+insentry_stmt "INSERT INTO ldap_entries (id,dn,oc_map_id,parent,keyval) VALUES (ldap_entry_ids.nextval,?,?,?,?)"
+upper_func UPPER
diff --git a/servers/slapd/back-sql/rdbms_depend/oracle/testdb_create.sql b/servers/slapd/back-sql/rdbms_depend/oracle/testdb_create.sql
new file mode 100644
index 0000000..710a5fa
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/oracle/testdb_create.sql
@@ -0,0 +1,68 @@
+CREATE TABLE persons (
+ id NUMBER NOT NULL,
+ name varchar2(255) NOT NULL,
+ surname varchar2(255) NOT NULL,
+ password varchar2(64) NOT NULL
+);
+
+CREATE TABLE institutes (
+ id NUMBER NOT NULL,
+ name varchar2(255)
+);
+
+CREATE TABLE documents (
+ id NUMBER NOT NULL,
+ title varchar2(255) NOT NULL,
+ abstract varchar2(255)
+);
+
+CREATE TABLE authors_docs (
+ pers_id NUMBER NOT NULL,
+ doc_id NUMBER NOT NULL
+);
+
+CREATE TABLE phones (
+ id NUMBER NOT NULL ,
+ phone varchar2(255) NOT NULL ,
+ pers_id NUMBER NOT NULL
+);
+
+
+ALTER TABLE authors_docs ADD
+ CONSTRAINT PK_authors_docs PRIMARY KEY
+ (
+ pers_id,
+ doc_id
+ );
+
+ALTER TABLE documents ADD
+ CONSTRAINT PK_documents PRIMARY KEY
+ (
+ id
+ );
+
+ALTER TABLE institutes ADD
+ CONSTRAINT PK_institutes PRIMARY KEY
+ (
+ id
+ );
+
+ALTER TABLE persons ADD
+ CONSTRAINT PK_persons PRIMARY KEY
+ (
+ id
+ );
+
+ALTER TABLE phones ADD
+ CONSTRAINT PK_phones PRIMARY KEY
+ (
+ id
+ );
+
+CREATE SEQUENCE person_ids START WITH 1 INCREMENT BY 1;
+
+CREATE SEQUENCE document_ids START WITH 1 INCREMENT BY 1;
+
+CREATE SEQUENCE institute_ids START WITH 1 INCREMENT BY 1;
+
+CREATE SEQUENCE phone_ids START WITH 1 INCREMENT BY 1;
diff --git a/servers/slapd/back-sql/rdbms_depend/oracle/testdb_data.sql b/servers/slapd/back-sql/rdbms_depend/oracle/testdb_data.sql
new file mode 100644
index 0000000..4fc1977
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/oracle/testdb_data.sql
@@ -0,0 +1,27 @@
+insert into institutes (id,name) values (institute_ids.nextval,'example');
+
+insert into persons (id,name,surname,password) values (person_ids.nextval,'Mitya','Kovalev','mit');
+
+insert into persons (id,name,surname) values (person_ids.nextval,'Torvlobnor','Puzdoy');
+
+insert into persons (id,name,surname) values (person_ids.nextval,'Akakiy','Zinberstein');
+
+
+insert into phones (id,phone,pers_id) values (phone_ids.nextval,'332-2334',1);
+
+insert into phones (id,phone,pers_id) values (phone_ids.nextval,'222-3234',1);
+
+insert into phones (id,phone,pers_id) values (phone_ids.nextval,'545-4563',2);
+
+
+insert into documents (id,abstract,title) values (document_ids.nextval,'abstract1','book1');
+
+insert into documents (id,abstract,title) values (document_ids.nextval,'abstract2','book2');
+
+
+insert into authors_docs (pers_id,doc_id) values (1,1);
+
+insert into authors_docs (pers_id,doc_id) values (1,2);
+
+insert into authors_docs (pers_id,doc_id) values (2,1);
+
diff --git a/servers/slapd/back-sql/rdbms_depend/oracle/testdb_drop.sql b/servers/slapd/back-sql/rdbms_depend/oracle/testdb_drop.sql
new file mode 100644
index 0000000..0cf4463
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/oracle/testdb_drop.sql
@@ -0,0 +1,25 @@
+DROP TABLE persons;
+DROP TABLE institutes;
+DROP TABLE documents;
+DROP TABLE authors_docs;
+DROP TABLE phones;
+DROP SEQUENCE person_ids;
+DROP SEQUENCE institute_ids;
+DROP SEQUENCE document_ids;
+DROP SEQUENCE phone_ids;
+DROP PROCEDURE create_person;
+DROP PROCEDURE delete_person;
+DROP PROCEDURE add_phone;
+DROP PROCEDURE delete_phone;
+DROP PROCEDURE set_person_name;
+DROP PROCEDURE set_org_name;
+DROP PROCEDURE set_doc_title;
+DROP PROCEDURE set_doc_abstract;
+DROP PROCEDURE create_document;
+DROP PROCEDURE create_org;
+DROP PROCEDURE delete_document;
+DROP PROCEDURE delete_org;
+DROP PROCEDURE make_doc_link;
+DROP PROCEDURE del_doc_link;
+DROP PROCEDURE make_author_link;
+DROP PROCEDURE del_author_link;
diff --git a/servers/slapd/back-sql/rdbms_depend/oracle/testdb_metadata.sql b/servers/slapd/back-sql/rdbms_depend/oracle/testdb_metadata.sql
new file mode 100644
index 0000000..354d7bd
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/oracle/testdb_metadata.sql
@@ -0,0 +1,252 @@
+-- mappings
+
+-- objectClass mappings: these may be viewed as structuralObjectClass, the ones that are used to decide how to build an entry
+-- id a unique number identifying the objectClass
+-- name the name of the objectClass; it MUST match the name of an objectClass that is loaded in slapd's schema
+-- keytbl the name of the table that is referenced for the primary key of an entry
+-- keycol the name of the column in "keytbl" that contains the primary key of an entry; the pair "keytbl.keycol" uniquely identifies an entry of objectClass "id"
+-- create_proc a procedure to create the entry
+-- delete_proc a procedure to delete the entry; it takes "keytbl.keycol" of the row to be deleted
+-- expect_return a bitmap that marks whether create_proc (1) and delete_proc (2) return a value or not
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,delete_proc,expect_return)
+values (1,'inetOrgPerson','persons','id','call create_person(?)','call delete_person(?)',0);
+
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,delete_proc,expect_return)
+values (2,'document','documents','id','call create_document(?)','call delete_document(?)',0);
+
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,delete_proc,expect_return)
+values (3,'organization','institutes','id','call create_org(?)','call delete_org(?)',0);
+
+-- attributeType mappings: describe how an attributeType for a certain objectClass maps to the SQL data.
+-- id a unique number identifying the attribute
+-- oc_map_id the value of "ldap_oc_mappings.id" that identifies the objectClass this attributeType is defined for
+-- name the name of the attributeType; it MUST match the name of an attributeType that is loaded in slapd's schema
+-- sel_expr the expression that is used to select this attribute (the "select <sel_expr> from ..." portion)
+-- from_tbls the expression that defines the table(s) this attribute is taken from (the "select ... from <from_tbls> where ..." portion)
+-- join_where the expression that defines the condition to select this attribute (the "select ... where <join_where> ..." portion)
+-- add_proc a procedure to insert the attribute; it takes the value of the attribute that is added, and the "keytbl.keycol" of the entry it is associated to
+-- delete_proc a procedure to delete the attribute; it takes the value of the attribute that is added, and the "keytbl.keycol" of the entry it is associated to
+-- param_order a mask that marks if the "keytbl.keycol" value comes before or after the value in add_proc (1) and delete_proc (2)
+-- expect_return a mask that marks whether add_proc (1) and delete_proc(2) are expected to return a value or not
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (1,1,'cn','persons.name||'' ''||persons.surname','persons',NULL,
+ NULL,NULL,0,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (2,1,'telephoneNumber','phones.phone','persons,phones',
+ 'phones.pers_id=persons.id','call add_phone(?,?)',
+ 'call delete_phone(?,?)',0,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (3,1,'givenName','persons.name','persons',NULL,'call set_person_name(?,?)',
+ NULL,0,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (4,1,'sn','persons.surname','persons',NULL,'call set_person_surname(?,?)',
+ NULL,0,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (5,1,'userPassword','persons.password','persons',
+ 'persons.password IS NOT NULL','call set_person_password(?,?)',
+ NULL,0,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (6,1,'seeAlso','seeAlso.dn','ldap_entries seeAlso,documents,authors_docs,persons',
+ 'seeAlso.keyval=documents.id AND seeAlso.oc_map_id=2 AND authors_docs.doc_id=documents.id AND authors_docs.pers_id=persons.id',
+ NULL,NULL,0,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (7,2,'description','documents.abstract','documents',NULL,'call set_doc_abstract(?,?)',
+ NULL,0,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (8,2,'documentTitle','documents.title','documents',NULL,'call set_doc_title(?,?)',NULL,0,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (9,2,'documentAuthor','documentAuthor.dn','ldap_entries documentAuthor,documents,authors_docs,persons',
+ 'documentAuthor.keyval=persons.id AND documentAuthor.oc_map_id=1 AND authors_docs.doc_id=documents.id AND authors_docs.pers_id=persons.id',
+ '?=call make_author_link(?,?)','?=call del_author_link(?,?)',0,3);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (10,2,'documentIdentifier','''document ''||text(documents.id)','documents',NULL,NULL,NULL,0,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (11,3,'o','institutes.name','institutes',NULL,'call set_org_name(?,?)',NULL,0,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (12,3,'dc','lower(institutes.name)','institutes,ldap_entries dcObject,ldap_entry_objclasses auxObjectClass',
+ 'institutes.id=dcObject.keyval AND dcObject.oc_map_id=3 AND dcObject.id=auxObjectClass.entry_id AND auxObjectClass.oc_name=''dcObject''',
+ NULL,NULL,0,0);
+
+-- entries mapping: each entry must appear in this table, with a unique DN rooted at the database naming context
+-- id a unique number > 0 identifying the entry
+-- dn the DN of the entry, in "pretty" form
+-- oc_map_id the "ldap_oc_mappings.id" of the main objectClass of this entry (view it as the structuralObjectClass)
+-- parent the "ldap_entries.id" of the parent of this objectClass; 0 if it is the "suffix" of the database
+-- keyval the value of the "keytbl.keycol" defined for this objectClass
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (ldap_entry_ids.nextval,'dc=example,dc=com',3,0,1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (ldap_entry_ids.nextval,'cn=Mitya Kovalev,dc=example,dc=com',1,1,1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (ldap_entry_ids.nextval,'cn=Torvlobnor Puzdoy,dc=example,dc=com',1,1,2);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (ldap_entry_ids.nextval,'cn=Akakiy Zinberstein,dc=example,dc=com',1,1,3);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (ldap_entry_ids.nextval,'documentTitle=book1,dc=example,dc=com',2,1,1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (ldap_entry_ids.nextval,'documentTitle=book2,dc=example,dc=com',2,1,2);
+
+-- objectClass mapping: entries that have multiple objectClass instances are listed here with the objectClass name (view them as auxiliary objectClass)
+-- entry_id the "ldap_entries.id" of the entry this objectClass value must be added
+-- oc_name the name of the objectClass; it MUST match the name of an objectClass that is loaded in slapd's schema
+insert into ldap_entry_objclasses (entry_id,oc_name)
+values (1,'dcObject');
+
+insert into ldap_entry_objclasses (entry_id,oc_name)
+values (4,'referral');
+
+-- referrals mapping: entries that should be treated as referrals are stored here
+-- entry_id the "ldap_entries.id" of the entry that should be treated as a referral
+-- url the URI of the referral
+insert into ldap_referrals (entry_id,url)
+values (4,'ldap://localhost:9012/');
+
+
+-- procedures
+-- these procedures are specific for this RDBMS and are used in mapping objectClass and attributeType creation/modify/deletion
+CREATE OR REPLACE PROCEDURE create_person(keyval OUT NUMBER) AS
+BEGIN
+INSERT INTO persons (id,name) VALUES (person_ids.nextval,' ');
+SELECT person_ids.currval INTO keyval FROM DUAL;
+END;
+/
+
+CREATE OR REPLACE PROCEDURE delete_person(keyval IN NUMBER) AS
+BEGIN
+DELETE FROM phones WHERE pers_id=keyval;
+DELETE FROM authors_docs WHERE pers_id=keyval;
+DELETE FROM persons WHERE id=keyval;
+END;
+/
+
+CREATE OR REPLACE PROCEDURE create_org(keyval OUT NUMBER) AS
+BEGIN
+INSERT INTO institutes (id,name) VALUES (institute_ids.nextval,' ');
+SELECT institute_ids.currval INTO keyval FROM DUAL;
+END;
+/
+
+CREATE OR REPLACE PROCEDURE delete_org(keyval IN NUMBER) AS
+BEGIN
+DELETE FROM institutes WHERE id=keyval;
+END;
+/
+
+CREATE OR REPLACE PROCEDURE create_document(keyval OUT NUMBER) AS
+BEGIN
+INSERT INTO documents (id,title) VALUES (document_ids.nextval,' ');
+SELECT document_ids.currval INTO keyval FROM DUAL;
+END;
+/
+
+CREATE OR REPLACE PROCEDURE delete_document (keyval IN NUMBER) AS
+BEGIN
+DELETE FROM authors_docs WHERE doc_id=keyval;
+DELETE FROM documents WHERE id=keyval;
+END;
+/
+
+CREATE OR REPLACE PROCEDURE add_phone(pers_id IN NUMBER, phone IN varchar2) AS
+BEGIN
+INSERT INTO phones (id,pers_id,phone) VALUES (phone_ids.nextval,pers_id,phone);
+END;
+/
+
+CREATE OR REPLACE PROCEDURE delete_phone(keyval IN NUMBER, phone IN varchar2) AS
+BEGIN
+DELETE FROM phones WHERE pers_id=keyval AND phone=phone;
+END;
+/
+
+CREATE OR REPLACE PROCEDURE set_person_name(keyval IN NUMBER, new_name IN varchar2) AS
+BEGIN
+UPDATE persons SET name=new_name WHERE id=keyval;
+END;
+/
+
+CREATE OR REPLACE PROCEDURE set_org_name(keyval IN NUMBER, new_name IN varchar2) AS
+BEGIN
+UPDATE institutes SET name=new_name WHERE id=keyval;
+END;
+/
+
+CREATE OR REPLACE PROCEDURE set_doc_title (keyval IN NUMBER, new_title IN varchar2) AS
+BEGIN
+UPDATE documents SET title=new_title WHERE id=keyval;
+END;
+/
+
+CREATE OR REPLACE PROCEDURE set_doc_abstract (keyval IN NUMBER, new_abstract IN varchar2) AS
+BEGIN
+UPDATE documents SET abstract=new_abstract WHERE id=keyval;
+END;
+/
+
+CREATE OR REPLACE FUNCTION make_author_link (keyval IN NUMBER, author_dn IN varchar2) RETURN NUMBER AS
+per_id NUMBER;
+BEGIN
+SELECT keyval INTO per_id FROM ldap_entries
+ WHERE oc_map_id=1 AND dn=author_dn;
+IF NOT (per_id IS NULL) THEN
+ INSERT INTO authors_docs (doc_id,pers_id) VALUES (keyval,per_id);
+ RETURN 1;
+END IF;
+RETURN 0;
+END;
+/
+
+CREATE OR REPLACE FUNCTION make_doc_link (keyval IN NUMBER, doc_dn IN varchar2) RETURN NUMBER AS
+docid NUMBER;
+BEGIN
+SELECT keyval INTO docid FROM ldap_entries
+ WHERE oc_map_id=2 AND dn=doc_dn;
+IF NOT (docid IS NULL) THEN
+ INSERT INTO authors_docs (pers_id,doc_id) VALUES (keyval,docid);
+ RETURN 1;
+END IF;
+RETURN 0;
+END;
+/
+
+CREATE OR REPLACE FUNCTION del_doc_link (keyval IN NUMBER, doc_dn IN varchar2) RETURN NUMBER AS
+docid NUMBER;
+BEGIN
+SELECT keyval INTO docid FROM ldap_entries
+ WHERE oc_map_id=2 AND dn=doc_dn;
+IF NOT (docid IS NULL) THEN
+ DELETE FROM authors_docs WHERE pers_id=keyval AND doc_id=docid;
+ RETURN 1;
+END IF;
+RETURN 0;
+END;
+/
+
+CREATE OR REPLACE FUNCTION del_author_link (keyval IN NUMBER, author_dn IN varchar2) RETURN NUMBER AS
+per_id NUMBER;
+BEGIN
+SELECT keyval INTO per_id FROM ldap_entries
+ WHERE oc_map_id=1 AND dn=author_dn;
+
+IF NOT (per_id IS NULL) THEN
+ DELETE FROM authors_docs WHERE doc_id=keyval AND pers_id=per_id;
+ RETURN 1;
+END IF;
+ RETURN 0;
+END;
+/
diff --git a/servers/slapd/back-sql/rdbms_depend/pgsql/backsql_create.sql b/servers/slapd/back-sql/rdbms_depend/pgsql/backsql_create.sql
new file mode 100644
index 0000000..a4baa70
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/pgsql/backsql_create.sql
@@ -0,0 +1,50 @@
+drop table ldap_oc_mappings;
+drop sequence ldap_oc_mappings_id_seq;
+create table ldap_oc_mappings
+ (
+ id serial not null primary key,
+ name varchar(64) not null,
+ keytbl varchar(64) not null,
+ keycol varchar(64) not null,
+ create_proc varchar(255),
+ delete_proc varchar(255),
+ expect_return int not null
+);
+
+drop table ldap_attr_mappings;
+drop sequence ldap_attr_mappings_id_seq;
+create table ldap_attr_mappings
+ (
+ id serial not null primary key,
+ oc_map_id integer not null references ldap_oc_mappings(id),
+ name varchar(255) not null,
+ sel_expr varchar(255) not null,
+ sel_expr_u varchar(255),
+ from_tbls varchar(255) not null,
+ join_where varchar(255),
+ add_proc varchar(255),
+ delete_proc varchar(255),
+ param_order int not null,
+ expect_return int not null
+);
+
+drop table ldap_entries;
+drop sequence ldap_entries_id_seq;
+create table ldap_entries
+ (
+ id serial not null primary key,
+ dn varchar(255) not null,
+ oc_map_id integer not null references ldap_oc_mappings(id),
+ parent int NOT NULL,
+ keyval int NOT NULL,
+ UNIQUE ( oc_map_id, keyval ),
+ UNIQUE ( dn )
+);
+
+drop table ldap_entry_objclasses;
+create table ldap_entry_objclasses
+ (
+ entry_id integer not null references ldap_entries(id),
+ oc_name varchar(64)
+ );
+
diff --git a/servers/slapd/back-sql/rdbms_depend/pgsql/backsql_drop.sql b/servers/slapd/back-sql/rdbms_depend/pgsql/backsql_drop.sql
new file mode 100644
index 0000000..eff0a9e
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/pgsql/backsql_drop.sql
@@ -0,0 +1,4 @@
+DROP TABLE ldap_entry_objclasses;
+DROP TABLE ldap_attr_mappings;
+DROP TABLE ldap_entries;
+DROP TABLE ldap_oc_mappings;
diff --git a/servers/slapd/back-sql/rdbms_depend/pgsql/slapd.conf b/servers/slapd/back-sql/rdbms_depend/pgsql/slapd.conf
new file mode 100644
index 0000000..70a8dee
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/pgsql/slapd.conf
@@ -0,0 +1,35 @@
+# $OpenLDAP$
+#
+# See slapd.conf(5) for details on configuration options.
+# This file should NOT be world readable.
+#
+include /usr/local/etc/openldap/schema/core.schema
+include /usr/local/etc/openldap/schema/cosine.schema
+include /usr/local/etc/openldap/schema/inetorgperson.schema
+
+# Define global ACLs to disable default read access.
+
+# Do not enable referrals until AFTER you have a working directory
+# service AND an understanding of referrals.
+#referral ldap://root.openldap.org
+
+pidfile /usr/local/var/slapd.pid
+argsfile /usr/local/var/slapd.args
+
+#######################################################################
+# sql database definitions
+#######################################################################
+
+database sql
+suffix "o=sql,c=RU"
+rootdn "cn=root,o=sql,c=RU"
+rootpw secret
+dbname PostgreSQL
+dbuser postgres
+dbpasswd postgres
+insentry_stmt "insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values ((select max(id)+1 from ldap_entries),?,?,?,?)"
+upper_func "upper"
+strcast_func "text"
+concat_pattern "?||?"
+has_ldapinfo_dn_ru no
+
diff --git a/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_create.sql b/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_create.sql
new file mode 100644
index 0000000..e1c57e7
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_create.sql
@@ -0,0 +1,55 @@
+drop table persons;
+drop sequence persons_id_seq;
+create table persons (
+ id serial not null primary key,
+ name varchar(255) not null,
+ surname varchar(255) not null,
+ password varchar(64)
+);
+
+drop table institutes;
+drop sequence institutes_id_seq;
+create table institutes (
+ id serial not null primary key,
+ name varchar(255)
+);
+
+drop table documents;
+drop sequence documents_id_seq;
+create table documents (
+ id serial not null primary key,
+ title varchar(255) not null,
+ abstract varchar(255)
+);
+
+drop table authors_docs;
+create table authors_docs (
+ pers_id int not null,
+ doc_id int not null,
+ primary key ( pers_id, doc_id )
+);
+
+drop table phones;
+drop sequence phones_id_seq;
+create table phones (
+ id serial not null primary key,
+ phone varchar(255) not null ,
+ pers_id int not null
+);
+
+drop table certs;
+drop sequence certs_id_seq;
+CREATE TABLE certs (
+ id int not null primary key,
+ cert bytea not null,
+ pers_id int not null
+);
+
+drop table referrals;
+drop sequence referrals_id_seq;
+create table referrals (
+ id serial not null primary key,
+ name varchar(255) not null,
+ url varchar(255) not null
+);
+
diff --git a/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_data.sql b/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_data.sql
new file mode 100644
index 0000000..0e661d4
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_data.sql
@@ -0,0 +1,21 @@
+insert into institutes (id,name) values (1,'Example');
+
+insert into persons (id,name,surname,password) values (1,'Mitya','Kovalev','mit');
+insert into persons (id,name,surname) values (2,'Torvlobnor','Puzdoy');
+insert into persons (id,name,surname) values (3,'Akakiy','Zinberstein');
+
+insert into phones (id,phone,pers_id) values (1,'332-2334',1);
+insert into phones (id,phone,pers_id) values (2,'222-3234',1);
+insert into phones (id,phone,pers_id) values (3,'545-4563',2);
+
+insert into documents (id,abstract,title) values (1,'abstract1','book1');
+insert into documents (id,abstract,title) values (2,'abstract2','book2');
+
+insert into authors_docs (pers_id,doc_id) values (1,1);
+insert into authors_docs (pers_id,doc_id) values (1,2);
+insert into authors_docs (pers_id,doc_id) values (2,1);
+
+insert into referrals (id,name,url) values (1,'Referral','ldap://localhost:9012/');
+
+insert into certs (id,cert,pers_id) values (1,decode('MIIDazCCAtSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhcNMDMxMDE3MTYzMzE5WhcNMDQxMDE2MTYzMzE5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjEYMBYGA1UEAxMPVXJzdWxhIEhhbXBzdGVyMR8wHQYJKoZIhvcNAQkBFhB1aGFtQGV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDuxgp5ELV9LmhxWMpV7qc4028QQT3+zzFDXhruuXE7ji2n3S3ea8bOwDtJh+qnsDe561DhHHHlgIjMKCiDEizYMpxvJPYEXmvp0huRkMgpKZgmel95BSkt6TYmJ0erS3aoimOHLEFimmnTLolNRMiWqNBvqwobx940PGwUWEePKQIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBSjI94TbBmuDEeUUOiC37EK0Uf0XjCBoQYDVR0jBIGZMIGWgBRLbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAMA0GCSqGSIb3DQEBBAUAA4GBAIgUcARb3OlWYNbmr1nmqESuxLn16uqI1Ot6WkcICvpkdQ+Bo+R9AP05xpoXocZtKdNvBu3FNxB/jFkiOcLU2lX7Px1Ijnsjh60qVRy9HOsHCungIKlGcnXLKHmKu0y//5jds/HnaJsGcHI5JRG7CBJbW+wrwge3trJ1xHJI8prN','base64'),3);
+
diff --git a/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_drop.sql b/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_drop.sql
new file mode 100644
index 0000000..c061ff8
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_drop.sql
@@ -0,0 +1,13 @@
+DROP TABLE persons;
+DROP TABLE institutes;
+DROP TABLE documents;
+DROP TABLE authors_docs;
+DROP TABLE phones;
+DROP TABLE referrals;
+DROP FUNCTION create_person ();
+DROP FUNCTION update_person_cn (varchar, int);
+DROP FUNCTION add_phone (varchar, int);
+DROP FUNCTION create_doc ();
+DROP FUNCTION create_o ();
+DROP FUNCTION create_referral ();
+
diff --git a/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_metadata.sql b/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_metadata.sql
new file mode 100644
index 0000000..d645cf2
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/pgsql/testdb_metadata.sql
@@ -0,0 +1,146 @@
+-- mappings
+
+-- objectClass mappings: these may be viewed as structuralObjectClass, the ones that are used to decide how to build an entry
+-- id a unique number identifying the objectClass
+-- name the name of the objectClass; it MUST match the name of an objectClass that is loaded in slapd's schema
+-- keytbl the name of the table that is referenced for the primary key of an entry
+-- keycol the name of the column in "keytbl" that contains the primary key of an entry; the pair "keytbl.keycol" uniquely identifies an entry of objectClass "id"
+-- create_proc a procedure to create the entry
+-- delete_proc a procedure to delete the entry; it takes "keytbl.keycol" of the row to be deleted
+-- expect_return a bitmap that marks whether create_proc (1) and delete_proc (2) return a value or not
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,delete_proc,expect_return) values (1,'inetOrgPerson','persons','id','SELECT create_person()','DELETE FROM persons WHERE id=?',0);
+
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,delete_proc,expect_return) values (2,'document','documents','id','SELECT create_doc()','DELETE FROM documents WHERE id=?',0);
+
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,delete_proc,expect_return) values (3,'organization','institutes','id','SELECT create_o()','DELETE FROM institutes WHERE id=?',0);
+
+insert into ldap_oc_mappings (id,name,keytbl,keycol,create_proc,delete_proc,expect_return) values (4,'referral','referrals','id','SELECT create_referral()','DELETE FROM referrals WHERE id=?',0);
+
+-- attributeType mappings: describe how an attributeType for a certain objectClass maps to the SQL data.
+-- id a unique number identifying the attribute
+-- oc_map_id the value of "ldap_oc_mappings.id" that identifies the objectClass this attributeType is defined for
+-- name the name of the attributeType; it MUST match the name of an attributeType that is loaded in slapd's schema
+-- sel_expr the expression that is used to select this attribute (the "select <sel_expr> from ..." portion)
+-- from_tbls the expression that defines the table(s) this attribute is taken from (the "select ... from <from_tbls> where ..." portion)
+-- join_where the expression that defines the condition to select this attribute (the "select ... where <join_where> ..." portion)
+-- add_proc a procedure to insert the attribute; it takes the value of the attribute that is added, and the "keytbl.keycol" of the entry it is associated to
+-- delete_proc a procedure to delete the attribute; it takes the value of the attribute that is added, and the "keytbl.keycol" of the entry it is associated to
+-- param_order a mask that marks if the "keytbl.keycol" value comes before or after the value in add_proc (1) and delete_proc (2)
+-- expect_return a mask that marks whether add_proc (1) and delete_proc(2) are expected to return a value or not
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return) values (1,1,'cn','text(persons.name||'' ''||persons.surname)','persons',NULL,'SELECT update_person_cn(?,?)','SELECT 1 FROM persons WHERE persons.name=? AND persons.id=? AND 1=0',3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return) values (2,1,'telephoneNumber','phones.phone','persons,phones','phones.pers_id=persons.id','SELECT add_phone(?,?)','DELETE FROM phones WHERE phone=? AND pers_id=?',3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return) values (3,1,'givenName','persons.name','persons',NULL,'UPDATE persons SET name=? WHERE id=?','UPDATE persons SET name='''' WHERE (name=? OR name='''') AND id=?',3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return) values (4,1,'sn','persons.surname','persons',NULL,'UPDATE persons SET surname=? WHERE id=?','UPDATE persons SET surname='''' WHERE (surname=? OR surname='''') AND id=?',3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return) values (5,1,'userPassword','persons.password','persons','persons.password IS NOT NULL','UPDATE persons SET password=? WHERE id=?','UPDATE persons SET password=NULL WHERE password=? AND id=?',3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return) values (6,1,'seeAlso','seeAlso.dn','ldap_entries AS seeAlso,documents,authors_docs,persons','seeAlso.keyval=documents.id AND seeAlso.oc_map_id=2 AND authors_docs.doc_id=documents.id AND authors_docs.pers_id=persons.id',NULL,'DELETE from authors_docs WHERE authors_docs.doc_id=(SELECT documents.id FROM documents,ldap_entries AS seeAlso WHERE seeAlso.keyval=documents.id AND seeAlso.oc_map_id=2 AND seeAlso.dn=?) AND authors_docs.pers_id=?',3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return) values (7,2,'description','documents.abstract','documents',NULL,'UPDATE documents SET abstract=? WHERE id=?','UPDATE documents SET abstract='''' WHERE abstract=? AND id=?',3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return) values (8,2,'documentTitle','documents.title','documents',NULL,'UPDATE documents SET title=? WHERE id=?','UPDATE documents SET title='''' WHERE title=? AND id=?',3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return) values (9,2,'documentAuthor','documentAuthor.dn','ldap_entries AS documentAuthor,documents,authors_docs,persons','documentAuthor.keyval=persons.id AND documentAuthor.oc_map_id=1 AND authors_docs.doc_id=documents.id AND authors_docs.pers_id=persons.id','INSERT INTO authors_docs (pers_id,doc_id) VALUES ((SELECT ldap_entries.keyval FROM ldap_entries WHERE upper(?)=upper(ldap_entries.dn)),?)','DELETE FROM authors_docs WHERE authors_docs.pers_id=(SELECT ldap_entries.keyval FROM ldap_entries WHERE upper(?)=upper(ldap_entries.dn)) AND authors_docs.doc_id=?',3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return) values (10,2,'documentIdentifier','''document ''||text(documents.id)','documents',NULL,NULL,'SELECT 1 FROM documents WHERE title=? AND id=? AND 1=0',3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return) values (11,3,'o','institutes.name','institutes',NULL,'UPDATE institutes SET name=? WHERE id=?','UPDATE institutes SET name='''' WHERE name=? AND id=?',3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return) values (12,3,'dc','lower(institutes.name)','institutes,ldap_entries AS dcObject,ldap_entry_objclasses AS auxObjectClass','institutes.id=dcObject.keyval AND dcObject.oc_map_id=3 AND dcObject.id=auxObjectClass.entry_id AND auxObjectClass.oc_name=''dcObject''',NULL,'SELECT 1 FROM institutes WHERE lower(name)=? AND id=? and 1=0',3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return) values (13,4,'ou','referrals.name','referrals',NULL,'UPDATE referrals SET name=? WHERE id=?','SELECT 1 FROM referrals WHERE name=? AND id=? and 1=0',3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return) values (14,4,'ref','referrals.url','referrals',NULL,'UPDATE referrals SET url=? WHERE id=?','SELECT 1 FROM referrals WHERE url=? and id=? and 1=0',3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return) values (15,1,'userCertificate','certs.cert','persons,certs','certs.pers_id=persons.id',NULL,NULL,3,0);
+
+-- entries mapping: each entry must appear in this table, with a unique DN rooted at the database naming context
+-- id a unique number > 0 identifying the entry
+-- dn the DN of the entry, in "pretty" form
+-- oc_map_id the "ldap_oc_mappings.id" of the main objectClass of this entry (view it as the structuralObjectClass)
+-- parent the "ldap_entries.id" of the parent of this objectClass; 0 if it is the "suffix" of the database
+-- keyval the value of the "keytbl.keycol" defined for this objectClass
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values (1,'dc=example,dc=com',3,0,1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values (2,'cn=Mitya Kovalev,dc=example,dc=com',1,1,1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values (3,'cn=Torvlobnor Puzdoy,dc=example,dc=com',1,1,2);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values (4,'cn=Akakiy Zinberstein,dc=example,dc=com',1,1,3);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values (5,'documentTitle=book1,dc=example,dc=com',2,1,1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values (6,'documentTitle=book2,dc=example,dc=com',2,1,2);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values (7,'ou=Referral,dc=example,dc=com',4,1,1);
+
+-- objectClass mapping: entries that have multiple objectClass instances are listed here with the objectClass name (view them as auxiliary objectClass)
+-- entry_id the "ldap_entries.id" of the entry this objectClass value must be added
+-- oc_name the name of the objectClass; it MUST match the name of an objectClass that is loaded in slapd's schema
+insert into ldap_entry_objclasses (entry_id,oc_name) values (1,'dcObject');
+
+insert into ldap_entry_objclasses (entry_id,oc_name) values (4,'pkiUser');
+
+insert into ldap_entry_objclasses (entry_id,oc_name) values (7,'extensibleObject');
+
+-- procedures
+-- these procedures are specific for this RDBMS and are used in mapping objectClass and attributeType creation/modify/deletion
+create function create_person () returns int
+as '
+ select setval (''persons_id_seq'', (select case when max(id) is null then 1 else max(id) end from persons));
+ insert into persons (id,name,surname)
+ values ((select case when max(id) is null then 1 else nextval(''persons_id_seq'') end from persons),'''','''');
+ select max(id) from persons
+' language 'sql';
+
+create function update_person_cn (varchar, int) returns int
+as '
+ update persons set name = (
+ select case
+ when position('' '' in $1) = 0 then $1
+ else substr($1, 1, position('' '' in $1) - 1)
+ end
+ ),surname = (
+ select case
+ when position('' '' in $1) = 0 then ''''
+ else substr($1, position('' '' in $1) + 1)
+ end
+ ) where id = $2;
+ select $2 as return
+' language 'sql';
+
+create function add_phone (varchar, int) returns int
+as '
+ select setval (''phones_id_seq'', (select case when max(id) is null then 1 else max(id) end from phones));
+ insert into phones (id,phone,pers_id)
+ values (nextval(''phones_id_seq''),$1,$2);
+ select max(id) from phones
+' language 'sql';
+
+create function create_doc () returns int
+as '
+ select setval (''documents_id_seq'', (select case when max(id) is null then 1 else max(id) end from documents));
+ insert into documents (id,title,abstract)
+ values ((select case when max(id) is null then 1 else nextval(''documents_id_seq'') end from documents),'''','''');
+ select max(id) from documents
+' language 'sql';
+
+create function create_o () returns int
+as '
+ select setval (''institutes_id_seq'', (select case when max(id) is null then 1 else max(id) end from institutes));
+ insert into institutes (id,name)
+ values ((select case when max(id) is null then 1 else nextval(''institutes_id_seq'') end from institutes),'''');
+ select max(id) from institutes
+' language 'sql';
+
+create function create_referral () returns int
+as '
+ select setval (''referrals_id_seq'', (select case when max(id) is null then 1 else max(id) end from referrals));
+ insert into referrals (id,name,url)
+ values ((select case when max(id) is null then 1 else nextval(''referrals_id_seq'') end from referrals),'''','''');
+ select max(id) from referrals
+' language 'sql';
+
diff --git a/servers/slapd/back-sql/rdbms_depend/timesten/backsql_create.sql b/servers/slapd/back-sql/rdbms_depend/timesten/backsql_create.sql
new file mode 100644
index 0000000..055e9df
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/timesten/backsql_create.sql
@@ -0,0 +1,66 @@
+
+create table ldap_oc_mappings
+ (
+ id integer not null primary key,
+ name varchar(64) not null,
+ keytbl varchar(64) not null,
+ keycol varchar(64) not null,
+ create_proc varchar(255),
+ delete_proc varchar(255),
+ expect_return tinyint not null
+);
+
+create table ldap_attr_mappings
+ (
+ id integer not null primary key,
+ oc_map_id integer not null,
+ name varchar(255) not null,
+ sel_expr varchar(255) not null,
+ sel_expr_u varchar(255),
+ from_tbls varchar(255) not null,
+ join_where varchar(255),
+ add_proc varchar(255),
+ delete_proc varchar(255),
+ param_order tinyint not null,
+ expect_return tinyint not null,
+ foreign key (oc_map_id) references ldap_oc_mappings(id)
+);
+
+create table ldap_entries
+ (
+ id integer not null primary key,
+ dn varchar(255) not null,
+ dn_ru varchar(255),
+ oc_map_id integer not null,
+ parent int NOT NULL ,
+ keyval int NOT NULL,
+ foreign key (oc_map_id) references ldap_oc_mappings(id)
+);
+
+create index ldap_entriesx1 on ldap_entries(dn_ru);
+
+create unique index unq1_ldap_entries on ldap_entries
+ (
+ oc_map_id,
+ keyval
+ );
+
+create unique index unq2_ldap_entries on ldap_entries
+ (
+ dn
+ );
+
+create table ldap_referrals
+ (
+ entry_id integer not null,
+ url varchar(4096) not null,
+ foreign key (entry_id) references ldap_entries(id)
+);
+
+create table ldap_entry_objclasses
+ (
+ entry_id integer not null,
+ oc_name varchar(64),
+ foreign key (entry_id) references ldap_entries(id)
+ );
+
diff --git a/servers/slapd/back-sql/rdbms_depend/timesten/backsql_drop.sql b/servers/slapd/back-sql/rdbms_depend/timesten/backsql_drop.sql
new file mode 100644
index 0000000..7aa0b83
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/timesten/backsql_drop.sql
@@ -0,0 +1,9 @@
+DROP TABLE ldap_referrals;
+
+DROP TABLE ldap_entry_objclasses;
+
+DROP TABLE ldap_attr_mappings;
+
+DROP TABLE ldap_entries;
+
+DROP TABLE ldap_oc_mappings;
diff --git a/servers/slapd/back-sql/rdbms_depend/timesten/create_schema.sh b/servers/slapd/back-sql/rdbms_depend/timesten/create_schema.sh
new file mode 100755
index 0000000..947db21
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/timesten/create_schema.sh
@@ -0,0 +1,4 @@
+ttIsql -connStr "DSN=ldap_tt;Overwrite=1" -f backsql_create.sql
+ttIsql -connStr "DSN=ldap_tt" -f testdb_create.sql
+ttIsql -connStr "DSN=ldap_tt" -f testdb_data.sql
+ttIsql -connStr "DSN=ldap_tt" -f testdb_metadata.sql
diff --git a/servers/slapd/back-sql/rdbms_depend/timesten/dnreverse/Makefile b/servers/slapd/back-sql/rdbms_depend/timesten/dnreverse/Makefile
new file mode 100644
index 0000000..1b0b1ee
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/timesten/dnreverse/Makefile
@@ -0,0 +1,48 @@
+## Copyright 1997-2022 The OpenLDAP Foundation, All Rights Reserved.
+## COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+
+#
+# Build TimesTen ODBC Sample Programs for Solaris 2.5.1.
+# (c) Copyright 1996-1998, TimesTen Performance Software.
+# All rights reserved.
+## Note: This file was contributed by Sam Drake of TimesTen Performance
+## Software for use and redistribution as an integral part of
+## OpenLDAP Software. -Kdz
+
+CPLUSPLUS = CC
+TTCLASSES = ../../../../../../../../../cs/classes
+ODBC = /opt/TimesTen4.1/32
+CFLAGS = -g -I$(ODBC)/include -I. -I$(TTCLASSES) -DUNIX
+LDFLAGS = -g
+DIRLIBS = $(TTCLASSES)/ttclasses.a -L $(ODBC)/lib -R $(ODBC)/lib -ltten -lpthread -lm -lrt
+XLALIB = -L $(ODBC)/lib -lxla
+
+DIRPROGS= dnreverse
+
+DNREVERSE= dnreverse.o
+
+#
+# Top-level targets
+#
+
+all: $(DIRPROGS)
+
+direct: $(DIRPROGS)
+
+clean:
+ rm -rf $(DIRPROGS) *.o
+
+
+#
+# Direct-linked programs
+#
+
+dnreverse: $(DNREVERSE)
+ $(CPLUSPLUS) -o dnreverse $(LDFLAGS) $(DNREVERSE) $(DIRLIBS) $(XLALIB)
+
+#
+# .o files
+#
+
+dnreverse.o: dnreverse.cpp
+ $(CPLUSPLUS) $(CFLAGS) -c dnreverse.cpp
diff --git a/servers/slapd/back-sql/rdbms_depend/timesten/dnreverse/dnreverse.cpp b/servers/slapd/back-sql/rdbms_depend/timesten/dnreverse/dnreverse.cpp
new file mode 100644
index 0000000..7407b4e
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/timesten/dnreverse/dnreverse.cpp
@@ -0,0 +1,387 @@
+// Copyright 1997-2022 The OpenLDAP Foundation, All Rights Reserved.
+// COPYING RESTRICTIONS APPLY, see COPYRIGHT file
+
+// (c) Copyright 1999-2001 TimesTen Performance Software. All rights reserved.
+
+//// Note: This file was contributed by Sam Drake of TimesTen Performance
+//// Software for use and redistribution as an integral part of
+//// OpenLDAP Software. -Kdz
+
+#include <stdlib.h>
+
+#include <TTConnectionPool.h>
+#include <TTConnection.h>
+#include <TTCmd.h>
+#include <TTXla.h>
+
+#include <signal.h>
+
+TTConnectionPool pool;
+TTXlaConnection conn;
+TTConnection conn2;
+TTCmd assignDn_ru;
+TTCmd getNullDNs;
+
+//----------------------------------------------------------------------
+// This class contains all the logic to be implemented whenever
+// the SCOTT.MYDATA table is changed. This is the table that is
+// created by "sample.cpp", one of the other TTClasses demos.
+// That application should be executed before this one in order to
+// create and populate the table.
+//----------------------------------------------------------------------
+
+class LDAPEntriesHandler: public TTXlaTableHandler {
+private:
+ // Definition of the columns in the table
+ int Id;
+ int Dn;
+ int Oc_map_id;
+ int Parent;
+ int Keyval;
+ int Dn_ru;
+
+protected:
+
+public:
+ LDAPEntriesHandler(TTXlaConnection& conn, const char* ownerP, const char* nameP);
+ ~LDAPEntriesHandler();
+
+ virtual void HandleDelete(ttXlaUpdateDesc_t*);
+ virtual void HandleInsert(ttXlaUpdateDesc_t*);
+ virtual void HandleUpdate(ttXlaUpdateDesc_t*);
+
+ static void ReverseAndUpper(char* dnP, int id, bool commit=true);
+
+};
+
+LDAPEntriesHandler::LDAPEntriesHandler(TTXlaConnection& conn,
+ const char* ownerP, const char* nameP) :
+ TTXlaTableHandler(conn, ownerP, nameP)
+{
+ Id = Dn = Oc_map_id = Parent = Keyval = Dn_ru = -1;
+
+ // We are looking for several particular named columns. We need to get
+ // the ordinal position of the columns by name for later use.
+
+ Id = tbl.getColNumber("ID");
+ if (Id < 0) {
+ cerr << "target table has no 'ID' column" << endl;
+ exit(1);
+ }
+ Dn = tbl.getColNumber("DN");
+ if (Dn < 0) {
+ cerr << "target table has no 'DN' column" << endl;
+ exit(1);
+ }
+ Oc_map_id = tbl.getColNumber("OC_MAP_ID");
+ if (Oc_map_id < 0) {
+ cerr << "target table has no 'OC_MAP_ID' column" << endl;
+ exit(1);
+ }
+ Parent = tbl.getColNumber("PARENT");
+ if (Parent < 0) {
+ cerr << "target table has no 'PARENT' column" << endl;
+ exit(1);
+ }
+ Keyval = tbl.getColNumber("KEYVAL");
+ if (Keyval < 0) {
+ cerr << "target table has no 'KEYVAL' column" << endl;
+ exit(1);
+ }
+ Dn_ru = tbl.getColNumber("DN_RU");
+ if (Dn_ru < 0) {
+ cerr << "target table has no 'DN_RU' column" << endl;
+ exit(1);
+ }
+
+}
+
+LDAPEntriesHandler::~LDAPEntriesHandler()
+{
+
+}
+
+void LDAPEntriesHandler::ReverseAndUpper(char* dnP, int id, bool commit)
+{
+ TTStatus stat;
+ char dn_rn[512];
+ int i;
+ int j;
+
+ // Reverse and upper case the given DN
+
+ for ((j=0, i = strlen(dnP)-1); i > -1; (j++, i--)) {
+ dn_rn[j] = toupper(*(dnP+i));
+ }
+ dn_rn[j] = '\0';
+
+
+ // Update the database
+
+ try {
+ assignDn_ru.setParam(1, (char*) &dn_rn[0]);
+ assignDn_ru.setParam(2, id);
+ assignDn_ru.Execute(stat);
+ }
+ catch (TTStatus stat) {
+ cerr << "Error updating id " << id << " ('" << dnP << "' to '"
+ << dn_rn << "'): " << stat;
+ exit(1);
+ }
+
+ // Commit the transaction
+
+ if (commit) {
+ try {
+ conn2.Commit(stat);
+ }
+ catch (TTStatus stat) {
+ cerr << "Error committing update: " << stat;
+ exit(1);
+ }
+ }
+
+}
+
+
+
+void LDAPEntriesHandler::HandleInsert(ttXlaUpdateDesc_t* p)
+{
+ char* dnP;
+ int id;
+
+ row.Get(Dn, &dnP);
+ cerr << "DN '" << dnP << "': Inserted ";
+ row.Get(Id, &id);
+
+ ReverseAndUpper(dnP, id);
+
+}
+
+void LDAPEntriesHandler::HandleUpdate(ttXlaUpdateDesc_t* p)
+{
+ char* newDnP;
+ char* oldDnP;
+ char oDn[512];
+ int id;
+
+ // row is 'old'; row2 is 'new'
+ row.Get(Dn, &oldDnP);
+ strcpy(oDn, oldDnP);
+ row.Get(Id, &id);
+ row2.Get(Dn, &newDnP);
+
+ cerr << "old DN '" << oDn << "' / new DN '" << newDnP << "' : Updated ";
+
+ if (strcmp(oDn, newDnP) != 0) {
+ // The DN field changed, update it
+ cerr << "(new DN: '" << newDnP << "')";
+ ReverseAndUpper(newDnP, id);
+ }
+ else {
+ // The DN field did NOT change, leave it alone
+ }
+
+ cerr << endl;
+
+}
+
+void LDAPEntriesHandler::HandleDelete(ttXlaUpdateDesc_t* p)
+{
+ char* dnP;
+
+ row.Get(Dn, &dnP);
+ cerr << "DN '" << dnP << "': Deleted ";
+}
+
+
+
+
+//----------------------------------------------------------------------
+
+int pleaseStop = 0;
+
+extern "C" {
+ void
+ onintr(int sig)
+ {
+ pleaseStop = 1;
+ cerr << "Stopping...\n";
+ }
+};
+
+//----------------------------------------------------------------------
+
+int
+main(int argc, char* argv[])
+{
+
+ char* ownerP;
+
+ TTXlaTableList list(&conn); // List of tables to monitor
+
+ // Handlers, one for each table we want to monitor
+
+ LDAPEntriesHandler* sampP = NULL;
+
+ // Misc stuff
+
+ TTStatus stat;
+
+ ttXlaUpdateDesc_t ** arry;
+
+ int records;
+
+ SQLUBIGINT oldsize;
+ int j;
+
+ if (argc < 2) {
+ cerr << "syntax: " << argv[0] << " <username>" << endl;
+ exit(3);
+ }
+
+ ownerP = argv[1];
+
+ signal(SIGINT, onintr); /* signal for CTRL-C */
+#ifdef _WIN32
+ signal(SIGBREAK, onintr); /* signal for CTRL-BREAK */
+#endif
+
+ // Before we do anything related to XLA, first we connect
+ // to the database. This is the connection we will use
+ // to perform non-XLA operations on the tables.
+
+ try {
+ cerr << "Connecting..." << endl;
+
+ conn2.Connect("DSN=ldap_tt", stat);
+ }
+ catch (TTStatus stat) {
+ cerr << "Error connecting to TimesTen: " << stat;
+ exit(1);
+ }
+
+ try {
+ assignDn_ru.Prepare(&conn2,
+ "update ldap_entries set dn_ru=? where id=?",
+ "", stat);
+ getNullDNs.Prepare(&conn2,
+ "select dn, id from ldap_entries "
+ "where dn_ru is null "
+ "for update",
+ "", stat);
+ conn2.Commit(stat);
+ }
+ catch (TTStatus stat) {
+ cerr << "Error preparing update: " << stat;
+ exit(1);
+ }
+
+ // If there are any entries with a NULL reversed/upper cased DN,
+ // fix them now.
+
+ try {
+ cerr << "Fixing NULL reversed DNs" << endl;
+ getNullDNs.Execute(stat);
+ for (int k = 0;; k++) {
+ getNullDNs.FetchNext(stat);
+ if (stat.rc == SQL_NO_DATA_FOUND) break;
+ char* dnP;
+ int id;
+ getNullDNs.getColumn(1, &dnP);
+ getNullDNs.getColumn(2, &id);
+ // cerr << "Id " << id << ", Dn '" << dnP << "'" << endl;
+ LDAPEntriesHandler::ReverseAndUpper(dnP, id, false);
+ if (k % 1000 == 0)
+ cerr << ".";
+ }
+ getNullDNs.Close(stat);
+ conn2.Commit(stat);
+ }
+ catch (TTStatus stat) {
+ cerr << "Error updating NULL rows: " << stat;
+ exit(1);
+ }
+
+
+ // Go ahead and start up the change monitoring application
+
+ cerr << "Starting change monitoring..." << endl;
+ try {
+ conn.Connect("DSN=ldap_tt", stat);
+ }
+ catch (TTStatus stat) {
+ cerr << "Error connecting to TimesTen: " << stat;
+ exit(1);
+ }
+
+ /* set and configure size of buffer */
+ conn.setXlaBufferSize((SQLUBIGINT) 1000000, &oldsize, stat);
+ if (stat.rc) {
+ cerr << "Error setting buffer size " << stat << endl;
+ exit(1);
+ }
+
+ // Make a handler to process changes to the MYDATA table and
+ // add the handler to the list of all handlers
+
+ sampP = new LDAPEntriesHandler(conn, ownerP, "ldap_entries");
+ if (!sampP) {
+ cerr << "Could not create LDAPEntriesHandler" << endl;
+ exit(3);
+ }
+ list.add(sampP);
+
+ // Enable transaction logging for the table we're interested in
+
+ sampP->EnableTracking(stat);
+
+ // Get updates. Dispatch them to the appropriate handler.
+ // This loop will handle updates to all the tables.
+
+ while (pleaseStop == 0) {
+ conn.fetchUpdates(&arry, 1000, &records, stat);
+ if (stat.rc) {
+ cerr << "Error fetching updates" << stat << endl;
+ exit(1);
+ }
+
+ // Interpret the updates
+
+ for(j=0;j < records;j++){
+ ttXlaUpdateDesc_t *p;
+
+ p = arry[j];
+
+ list.HandleChange(p, stat);
+
+ } // end for each record fetched
+
+ if (records) {
+ cerr << "Processed " << records << " records\n";
+ }
+
+ if (records == 0) {
+#ifdef _WIN32
+ Sleep(250);
+#else
+ struct timeval t;
+ t.tv_sec = 0;
+ t.tv_usec = 250000; // .25 seconds
+ select(0, NULL, NULL, NULL, &t);
+#endif
+ }
+ } // end while pleasestop == 0
+
+
+ // When we get to here, the program is exiting.
+
+ list.del(sampP); // Take the table out of the list
+ delete sampP;
+
+ conn.setXlaBufferSize(oldsize, NULL, stat);
+
+ return 0;
+
+}
+
diff --git a/servers/slapd/back-sql/rdbms_depend/timesten/slapd.conf b/servers/slapd/back-sql/rdbms_depend/timesten/slapd.conf
new file mode 100644
index 0000000..f93de8b
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/timesten/slapd.conf
@@ -0,0 +1,31 @@
+# $OpenLDAP$
+#
+# See slapd.conf(5) for details on configuration options.
+# This file should NOT be world readable.
+#
+include /usr/local/etc/openldap/schema/core.schema
+include /usr/local/etc/openldap/schema/cosine.schema
+include /usr/local/etc/openldap/schema/inetorgperson.schema
+
+# Define global ACLs to disable default read access.
+
+# Do not enable referrals until AFTER you have a working directory
+# service AND an understanding of referrals.
+#referral ldap://root.openldap.org
+
+pidfile /usr/local/var/slapd.pid
+argsfile /usr/local/var/slapd.args
+
+#######################################################################
+# sql database definitions
+#######################################################################
+
+database sql
+suffix "o=sql,c=RU"
+rootdn "cn=root,o=sql,c=RU"
+rootpw secret
+dbname ldap_tt
+dbuser root
+dbpasswd
+subtree_cond "ldap_entries.dn LIKE ?"
+insentry_stmt "INSERT INTO ldap_entries (dn,oc_map_id,parent,keyval) VALUES (?,?,?,?)"
diff --git a/servers/slapd/back-sql/rdbms_depend/timesten/testdb_create.sql b/servers/slapd/back-sql/rdbms_depend/timesten/testdb_create.sql
new file mode 100644
index 0000000..768aec8
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/timesten/testdb_create.sql
@@ -0,0 +1,36 @@
+CREATE TABLE persons (
+ id int NOT NULL primary key,
+ name varchar(255) NOT NULL
+)
+unique hash on (id) pages=100;
+
+CREATE TABLE institutes (
+ id int NOT NULL primary key,
+ name varchar(255)
+)
+unique hash on (id) pages=100;
+
+CREATE TABLE documents (
+ id int NOT NULL primary key,
+ title varchar(255) NOT NULL,
+ abstract varchar(255)
+)
+unique hash on (id) pages=100;
+
+CREATE TABLE authors_docs (
+ pers_id int NOT NULL,
+ doc_id int NOT NULL,
+ PRIMARY KEY
+ (
+ pers_id,
+ doc_id
+ )
+) unique hash on (pers_id, doc_id) pages=100;
+
+CREATE TABLE phones (
+ id int NOT NULL primary key,
+ phone varchar(255) NOT NULL ,
+ pers_id int NOT NULL
+)
+unique hash on (id) pages=100;
+
diff --git a/servers/slapd/back-sql/rdbms_depend/timesten/testdb_data.sql b/servers/slapd/back-sql/rdbms_depend/timesten/testdb_data.sql
new file mode 100644
index 0000000..f141f41
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/timesten/testdb_data.sql
@@ -0,0 +1,16 @@
+insert into institutes (id,name) values (1,'sql');
+
+insert into persons (id,name) values (1,'Mitya Kovalev');
+insert into persons (id,name) values (2,'Torvlobnor Puzdoy');
+insert into persons (id,name) values (3,'Akakiy Zinberstein');
+
+insert into phones (id,phone,pers_id) values (1,'332-2334',1);
+insert into phones (id,phone,pers_id) values (2,'222-3234',1);
+insert into phones (id,phone,pers_id) values (3,'545-4563',2);
+
+insert into documents (id,abstract,title) values (1,'abstract1','book1');
+insert into documents (id,abstract,title) values (2,'abstract2','book2');
+
+insert into authors_docs (pers_id,doc_id) values (1,1);
+insert into authors_docs (pers_id,doc_id) values (1,2);
+insert into authors_docs (pers_id,doc_id) values (2,1);
diff --git a/servers/slapd/back-sql/rdbms_depend/timesten/testdb_drop.sql b/servers/slapd/back-sql/rdbms_depend/timesten/testdb_drop.sql
new file mode 100644
index 0000000..17b12af
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/timesten/testdb_drop.sql
@@ -0,0 +1,5 @@
+DROP TABLE persons;
+DROP TABLE institutes;
+DROP TABLE documents;
+DROP TABLE authors_docs;
+DROP TABLE phones;
diff --git a/servers/slapd/back-sql/rdbms_depend/timesten/testdb_metadata.sql b/servers/slapd/back-sql/rdbms_depend/timesten/testdb_metadata.sql
new file mode 100644
index 0000000..f9e3419
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/timesten/testdb_metadata.sql
@@ -0,0 +1,108 @@
+
+insert into ldap_oc_mappings
+(id,name, keytbl, keycol, create_proc,
+delete_proc,expect_return)
+values
+(1,'inetOrgPerson','persons','id', 'insert into persons (name) values ('');\n select last_insert_id();',
+NULL,0);
+
+insert into ldap_oc_mappings
+(id, name, keytbl, keycol,create_proc,delete_proc,expect_return)
+values
+(2, 'document','documents','id', NULL, NULL, 0);
+
+insert into ldap_oc_mappings
+(id,name, keytbl, keycol,create_proc,delete_proc,expect_return)
+values
+(3,'organization','institutes','id', NULL, NULL, 0);
+
+
+insert into ldap_attr_mappings
+(id, oc_map_id, name, sel_expr, from_tbls,join_where,add_proc,
+delete_proc,param_order,expect_return)
+values
+(1, 1, 'cn', 'persons.name', 'persons',NULL, NULL,
+NULL, 3, 0);
+
+insert into ldap_attr_mappings
+(id, oc_map_id,name, sel_expr, from_tbls,
+join_where, add_proc,delete_proc,param_order,expect_return)
+values
+(2, 1, 'telephoneNumber','phones.phone','persons,phones',
+'phones.pers_id=persons.id', NULL, NULL, 3, 0);
+
+insert into ldap_attr_mappings
+(id,oc_map_id, name, sel_expr, from_tbls, join_where,add_proc,
+delete_proc,param_order,expect_return)
+values
+(3, 1, 'sn', 'persons.name','persons', NULL, NULL,
+NULL, 3, 0);
+
+insert into ldap_attr_mappings
+(id, oc_map_id, name, sel_expr, from_tbls, join_where,
+add_proc,delete_proc,param_order,expect_return)
+values
+(4, 2, 'description', 'documents.abstract','documents', NULL,
+NULL, NULL, 3, 0);
+
+insert into ldap_attr_mappings
+(id, oc_map_id, name, sel_expr, from_tbls, join_where,
+add_proc,delete_proc,param_order,expect_return)
+values
+(5, 2, 'documentTitle','documents.title','documents',NULL,
+NULL, NULL, 3, 0);
+
+-- insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+-- values (6,2,'documentAuthor','persons.name','persons,documents,authors_docs',
+-- 'persons.id=authors_docs.pers_id AND documents.id=authors_docs.doc_id',
+-- NULL,NULL,3,0);
+
+insert into ldap_attr_mappings
+(id, oc_map_id, name, sel_expr, from_tbls, join_where,add_proc,
+delete_proc,param_order,expect_return)
+values
+(7, 3, 'o', 'institutes.name', 'institutes', NULL, NULL,
+NULL, 3, 0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (8,1,'documentDN','ldap_entries.dn','ldap_entries,documents,authors_docs,persons',
+ 'ldap_entries.keyval=documents.id AND ldap_entries.oc_map_id=2 AND authors_docs.doc_id=documents.id AND authors_docs.pers_id=persons.id',
+ NULL,NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (9,2,'documentAuthor','ldap_entries.dn','ldap_entries,documents,authors_docs,persons',
+ 'ldap_entries.keyval=persons.id AND ldap_entries.oc_map_id=1 AND authors_docs.doc_id=documents.id AND authors_docs.pers_id=persons.id',
+ NULL,NULL,3,0);
+
+-- entries
+
+insert into ldap_entries
+(id, dn, oc_map_id, parent, keyval)
+values
+(1, 'o=sql,c=RU', 3, 0, 1);
+
+insert into ldap_entries
+(id, dn, oc_map_id, parent, keyval)
+values
+(2, 'cn=Mitya Kovalev,o=sql,c=RU', 1, 1, 1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (3,'cn=Torvlobnor Puzdoy,o=sql,c=RU',1,1,2);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (4,'cn=Akakiy Zinberstein,o=sql,c=RU',1,1,3);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (5,'documentTitle=book1,o=sql,c=RU',2,1,1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (6,'documentTitle=book2,o=sql,c=RU',2,1,2);
+
+
+-- referrals
+
+insert into ldap_entry_objclasses (entry_id,oc_name)
+values (4,'referral');
+
+insert into ldap_referrals (entry_id,url)
+values (4,'ldap://localhost:9012');
diff --git a/servers/slapd/back-sql/rdbms_depend/timesten/ttcreate_schema.sh b/servers/slapd/back-sql/rdbms_depend/timesten/ttcreate_schema.sh
new file mode 100755
index 0000000..c4c5df2
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/timesten/ttcreate_schema.sh
@@ -0,0 +1,4 @@
+ttIsql -connStr "DSN=ldap_tt;Overwrite=1" -f backsql_create.sql
+ttIsql -connStr "DSN=ldap_tt" -f tttestdb_create.sql
+ttIsql -connStr "DSN=ldap_tt" -f tttestdb_data.sql
+ttIsql -connStr "DSN=ldap_tt" -f tttestdb_metadata.sql
diff --git a/servers/slapd/back-sql/rdbms_depend/timesten/tttestdb_create.sql b/servers/slapd/back-sql/rdbms_depend/timesten/tttestdb_create.sql
new file mode 100644
index 0000000..f5955d2
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/timesten/tttestdb_create.sql
@@ -0,0 +1,42 @@
+CREATE TABLE persons (
+ id int NOT NULL primary key,
+ name varchar(255) NOT NULL,
+ name_u varchar(255),
+ title varchar(255),
+ title_U varchar(255),
+ organization varchar(255)
+)
+unique hash on (id) pages=100;
+create index personsx1 on persons(title_U);
+create index personsx2 on persons(name_u);
+
+CREATE TABLE institutes (
+ id int NOT NULL primary key,
+ name varchar(255)
+)
+unique hash on (id) pages=100;
+
+CREATE TABLE documents (
+ id int NOT NULL primary key,
+ title varchar(255) NOT NULL,
+ abstract varchar(255)
+)
+unique hash on (id) pages=100;
+
+CREATE TABLE authors_docs (
+ pers_id int NOT NULL,
+ doc_id int NOT NULL,
+ PRIMARY KEY
+ (
+ pers_id,
+ doc_id
+ )
+) unique hash on (pers_id, doc_id) pages=100;
+
+CREATE TABLE phones (
+ id int NOT NULL primary key,
+ phone varchar(255) NOT NULL ,
+ pers_id int NOT NULL
+)
+unique hash on (id) pages=100;
+
diff --git a/servers/slapd/back-sql/rdbms_depend/timesten/tttestdb_data.sql b/servers/slapd/back-sql/rdbms_depend/timesten/tttestdb_data.sql
new file mode 100644
index 0000000..ca75339
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/timesten/tttestdb_data.sql
@@ -0,0 +1,20 @@
+insert into institutes (id,name) values (1,'sql');
+
+insert into persons (id,name, title, title_U, organization) values
+(1,'Mitya Kovalev', 'Engineer', 'ENGINEER', 'Development');
+insert into persons (id,name, title, title_U, organization) values
+(2,'Torvlobnor Puzdoy', 'Engineer', 'ENGINEER', 'Sales');
+insert into persons (id,name, title, title_U, organization) values
+(3,'Akakiy Zinberstein', 'Engineer', 'ENGINEER', 'Marketing');
+update persons set name_u = upper(name);
+
+insert into phones (id,phone,pers_id) values (1,'332-2334',1);
+insert into phones (id,phone,pers_id) values (2,'222-3234',1);
+insert into phones (id,phone,pers_id) values (3,'545-4563',2);
+
+insert into documents (id,abstract,title) values (1,'abstract1','book1');
+insert into documents (id,abstract,title) values (2,'abstract2','book2');
+
+insert into authors_docs (pers_id,doc_id) values (1,1);
+insert into authors_docs (pers_id,doc_id) values (1,2);
+insert into authors_docs (pers_id,doc_id) values (2,1);
diff --git a/servers/slapd/back-sql/rdbms_depend/timesten/tttestdb_drop.sql b/servers/slapd/back-sql/rdbms_depend/timesten/tttestdb_drop.sql
new file mode 100644
index 0000000..17b12af
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/timesten/tttestdb_drop.sql
@@ -0,0 +1,5 @@
+DROP TABLE persons;
+DROP TABLE institutes;
+DROP TABLE documents;
+DROP TABLE authors_docs;
+DROP TABLE phones;
diff --git a/servers/slapd/back-sql/rdbms_depend/timesten/tttestdb_metadata.sql b/servers/slapd/back-sql/rdbms_depend/timesten/tttestdb_metadata.sql
new file mode 100644
index 0000000..69bda8a
--- /dev/null
+++ b/servers/slapd/back-sql/rdbms_depend/timesten/tttestdb_metadata.sql
@@ -0,0 +1,122 @@
+
+insert into ldap_oc_mappings
+(id,name, keytbl, keycol, create_proc,
+delete_proc,expect_return)
+values
+(1,'inetOrgPerson','persons','id', 'insert into persons (name) values ('');\n select last_insert_id();',
+NULL,0);
+
+insert into ldap_oc_mappings
+(id, name, keytbl, keycol,create_proc,delete_proc,expect_return)
+values
+(2, 'document','documents','id', NULL, NULL, 0);
+
+insert into ldap_oc_mappings
+(id,name, keytbl, keycol,create_proc,delete_proc,expect_return)
+values
+(3,'organization','institutes','id', NULL, NULL, 0);
+
+
+insert into ldap_attr_mappings
+(id, oc_map_id, name, sel_expr, sel_expr_u, from_tbls,
+join_where,add_proc, delete_proc,param_order,expect_return)
+values
+(1, 1, 'cn', 'persons.name', 'persons.name_u','persons',
+NULL, NULL, NULL, 3, 0);
+
+insert into ldap_attr_mappings
+(id, oc_map_id, name, sel_expr, sel_expr_u, from_tbls,join_where,
+add_proc, delete_proc,param_order,expect_return)
+values
+(10, 1, 'title', 'persons.title', 'persons.title_u', 'persons',NULL, NULL,
+NULL, 3, 0);
+
+insert into ldap_attr_mappings
+(id, oc_map_id,name, sel_expr, from_tbls,
+join_where, add_proc,delete_proc,param_order,expect_return)
+values
+(2, 1, 'telephoneNumber','phones.phone','persons,phones',
+'phones.pers_id=persons.id', NULL, NULL, 3, 0);
+
+insert into ldap_attr_mappings
+(id,oc_map_id, name, sel_expr, from_tbls, join_where,add_proc,
+delete_proc,param_order,expect_return)
+values
+(3, 1, 'sn', 'persons.name','persons', NULL, NULL,
+NULL, 3, 0);
+
+insert into ldap_attr_mappings
+(id, oc_map_id, name, sel_expr, from_tbls, join_where,add_proc,
+delete_proc,param_order,expect_return)
+values
+(30, 1, 'ou', 'persons.organization','persons', NULL, NULL,
+NULL, 3, 0);
+
+insert into ldap_attr_mappings
+(id, oc_map_id, name, sel_expr, from_tbls, join_where,
+add_proc,delete_proc,param_order,expect_return)
+values
+(4, 2, 'description', 'documents.abstract','documents', NULL,
+NULL, NULL, 3, 0);
+
+insert into ldap_attr_mappings
+(id, oc_map_id, name, sel_expr, from_tbls, join_where,
+add_proc,delete_proc,param_order,expect_return)
+values
+(5, 2, 'documentTitle','documents.title','documents',NULL,
+NULL, NULL, 3, 0);
+
+-- insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+-- values (6,2,'documentAuthor','persons.name','persons,documents,authors_docs',
+-- 'persons.id=authors_docs.pers_id AND documents.id=authors_docs.doc_id',
+-- NULL,NULL,3,0);
+
+insert into ldap_attr_mappings
+(id, oc_map_id, name, sel_expr, from_tbls, join_where,add_proc,
+delete_proc,param_order,expect_return)
+values
+(7, 3, 'o', 'institutes.name', 'institutes', NULL, NULL,
+NULL, 3, 0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (8,1,'documentDN','ldap_entries.dn','ldap_entries,documents,authors_docs,persons',
+ 'ldap_entries.keyval=documents.id AND ldap_entries.oc_map_id=2 AND authors_docs.doc_id=documents.id AND authors_docs.pers_id=persons.id',
+ NULL,NULL,3,0);
+
+insert into ldap_attr_mappings (id,oc_map_id,name,sel_expr,from_tbls,join_where,add_proc,delete_proc,param_order,expect_return)
+values (9,2,'documentAuthor','ldap_entries.dn','ldap_entries,documents,authors_docs,persons',
+ 'ldap_entries.keyval=persons.id AND ldap_entries.oc_map_id=1 AND authors_docs.doc_id=documents.id AND authors_docs.pers_id=persons.id',
+ NULL,NULL,3,0);
+
+-- entries
+
+insert into ldap_entries
+(id, dn, oc_map_id, parent, keyval)
+values
+(1, 'o=sql,c=RU', 3, 0, 1);
+
+insert into ldap_entries
+(id, dn, oc_map_id, parent, keyval)
+values
+(2, 'cn=Mitya Kovalev,o=sql,c=RU', 1, 1, 1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (3,'cn=Torvlobnor Puzdoy,o=sql,c=RU',1,1,2);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (4,'cn=Akakiy Zinberstein,o=sql,c=RU',1,1,3);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (5,'documentTitle=book1,o=sql,c=RU',2,1,1);
+
+insert into ldap_entries (id,dn,oc_map_id,parent,keyval)
+values (6,'documentTitle=book2,o=sql,c=RU',2,1,2);
+
+
+-- referrals
+
+insert into ldap_entry_objclasses (entry_id,oc_name)
+values (4,'referral');
+
+insert into ldap_referrals (entry_id,url)
+values (4,'http://localhost');
diff --git a/servers/slapd/back-sql/schema-map.c b/servers/slapd/back-sql/schema-map.c
new file mode 100644
index 0000000..f6294bb
--- /dev/null
+++ b/servers/slapd/back-sql/schema-map.c
@@ -0,0 +1,1012 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 Dmitry Kovalev.
+ * Portions Copyright 2002 Pierangelo Masarati.
+ * Portions Copyright 2004 Mark Adamson.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Dmitry Kovalev for inclusion
+ * by OpenLDAP Software. Additional significant contributors include
+ * Pierangelo Masarati and Mark Adamson.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "ac/string.h"
+
+#include "lutil.h"
+#include "slap.h"
+#include "proto-sql.h"
+
+#define BACKSQL_DUPLICATE (-1)
+
+/* NOTE: by default, cannot just compare pointers because
+ * objectClass/attributeType order would be machine-dependent
+ * (and tests would fail!); however, if you don't want to run
+ * tests, or see attributeTypes written in the same order
+ * they are defined, define */
+/* #undef BACKSQL_USE_PTR_CMP */
+
+/*
+ * Uses the pointer to the ObjectClass structure
+ */
+static int
+backsql_cmp_oc( const void *v_m1, const void *v_m2 )
+{
+ const backsql_oc_map_rec *m1 = v_m1,
+ *m2 = v_m2;
+
+#ifdef BACKSQL_USE_PTR_CMP
+ return SLAP_PTRCMP( m1->bom_oc, m2->bom_oc );
+#else /* ! BACKSQL_USE_PTR_CMP */
+ return ber_bvcmp( &m1->bom_oc->soc_cname, &m2->bom_oc->soc_cname );
+#endif /* ! BACKSQL_USE_PTR_CMP */
+}
+
+static int
+backsql_cmp_oc_id( const void *v_m1, const void *v_m2 )
+{
+ const backsql_oc_map_rec *m1 = v_m1,
+ *m2 = v_m2;
+
+ return ( m1->bom_id < m2->bom_id ? -1 : ( m1->bom_id > m2->bom_id ? 1 : 0 ) );
+}
+
+/*
+ * Uses the pointer to the AttributeDescription structure
+ */
+static int
+backsql_cmp_attr( const void *v_m1, const void *v_m2 )
+{
+ const backsql_at_map_rec *m1 = v_m1,
+ *m2 = v_m2;
+
+ if ( slap_ad_is_binary( m1->bam_ad ) || slap_ad_is_binary( m2->bam_ad ) ) {
+#ifdef BACKSQL_USE_PTR_CMP
+ return SLAP_PTRCMP( m1->bam_ad->ad_type, m2->bam_ad->ad_type );
+#else /* ! BACKSQL_USE_PTR_CMP */
+ return ber_bvcmp( &m1->bam_ad->ad_type->sat_cname, &m2->bam_ad->ad_type->sat_cname );
+#endif /* ! BACKSQL_USE_PTR_CMP */
+ }
+
+#ifdef BACKSQL_USE_PTR_CMP
+ return SLAP_PTRCMP( m1->bam_ad, m2->bam_ad );
+#else /* ! BACKSQL_USE_PTR_CMP */
+ return ber_bvcmp( &m1->bam_ad->ad_cname, &m2->bam_ad->ad_cname );
+#endif /* ! BACKSQL_USE_PTR_CMP */
+}
+
+int
+backsql_dup_attr( void *v_m1, void *v_m2 )
+{
+ backsql_at_map_rec *m1 = v_m1,
+ *m2 = v_m2;
+
+ if ( slap_ad_is_binary( m1->bam_ad ) || slap_ad_is_binary( m2->bam_ad ) ) {
+#ifdef BACKSQL_USE_PTR_CMP
+ assert( m1->bam_ad->ad_type == m2->bam_ad->ad_type );
+#else /* ! BACKSQL_USE_PTR_CMP */
+ assert( ber_bvcmp( &m1->bam_ad->ad_type->sat_cname, &m2->bam_ad->ad_type->sat_cname ) == 0 );
+#endif /* ! BACKSQL_USE_PTR_CMP */
+
+ } else {
+#ifdef BACKSQL_USE_PTR_CMP
+ assert( m1->bam_ad == m2->bam_ad );
+#else /* ! BACKSQL_USE_PTR_CMP */
+ assert( ber_bvcmp( &m1->bam_ad->ad_cname, &m2->bam_ad->ad_cname ) == 0 );
+#endif /* ! BACKSQL_USE_PTR_CMP */
+ }
+
+ /* duplicate definitions of attributeTypes are appended;
+ * this allows to define multiple rules for the same
+ * attributeType. Use with care! */
+ for ( ; m1->bam_next ; m1 = m1->bam_next );
+
+ m1->bam_next = m2;
+ m2->bam_next = NULL;
+
+ return BACKSQL_DUPLICATE;
+}
+
+static int
+backsql_make_attr_query(
+ backsql_info *bi,
+ backsql_oc_map_rec *oc_map,
+ backsql_at_map_rec *at_map )
+{
+ struct berbuf bb = BB_NULL;
+
+ backsql_strfcat_x( &bb, NULL, "lblbbbblblbcbl",
+ (ber_len_t)STRLENOF( "SELECT " ), "SELECT ",
+ &at_map->bam_sel_expr,
+ (ber_len_t)STRLENOF( " " ), " ",
+ &bi->sql_aliasing,
+ &bi->sql_aliasing_quote,
+ &at_map->bam_ad->ad_cname,
+ &bi->sql_aliasing_quote,
+ (ber_len_t)STRLENOF( " FROM " ), " FROM ",
+ &at_map->bam_from_tbls,
+ (ber_len_t)STRLENOF( " WHERE " ), " WHERE ",
+ &oc_map->bom_keytbl,
+ '.',
+ &oc_map->bom_keycol,
+ (ber_len_t)STRLENOF( "=?" ), "=?" );
+
+ if ( !BER_BVISNULL( &at_map->bam_join_where ) ) {
+ backsql_strfcat_x( &bb, NULL, "lb",
+ (ber_len_t)STRLENOF( " AND " ), " AND ",
+ &at_map->bam_join_where );
+ }
+
+ backsql_strfcat_x( &bb, NULL, "lbbb",
+ (ber_len_t)STRLENOF( " ORDER BY " ), " ORDER BY ",
+ &bi->sql_aliasing_quote,
+ &at_map->bam_ad->ad_cname,
+ &bi->sql_aliasing_quote );
+
+ at_map->bam_query = bb.bb_val.bv_val;
+
+#ifdef BACKSQL_COUNTQUERY
+ /* Query to count how many rows will be returned.
+
+ SELECT COUNT(*) FROM <from_tbls> WHERE <keytbl>.<keycol>=?
+ [ AND <join_where> ]
+
+ */
+ BER_BVZERO( &bb.bb_val );
+ bb.bb_len = 0;
+ backsql_strfcat_x( &bb, NULL, "lblbcbl",
+ (ber_len_t)STRLENOF( "SELECT COUNT(*) FROM " ),
+ "SELECT COUNT(*) FROM ",
+ &at_map->bam_from_tbls,
+ (ber_len_t)STRLENOF( " WHERE " ), " WHERE ",
+ &oc_map->bom_keytbl,
+ '.',
+ &oc_map->bom_keycol,
+ (ber_len_t)STRLENOF( "=?" ), "=?" );
+
+ if ( !BER_BVISNULL( &at_map->bam_join_where ) ) {
+ backsql_strfcat_x( &bb, NULL, "lb",
+ (ber_len_t)STRLENOF( " AND " ), " AND ",
+ &at_map->bam_join_where );
+ }
+
+ at_map->bam_countquery = bb.bb_val.bv_val;
+#endif /* BACKSQL_COUNTQUERY */
+
+ return 0;
+}
+
+static int
+backsql_add_sysmaps( backsql_info *bi, backsql_oc_map_rec *oc_map )
+{
+ backsql_at_map_rec *at_map;
+ char s[LDAP_PVT_INTTYPE_CHARS(long)];
+ struct berval sbv;
+ struct berbuf bb;
+
+ sbv.bv_val = s;
+ sbv.bv_len = snprintf( s, sizeof( s ), BACKSQL_IDNUMFMT, oc_map->bom_id );
+
+ /* extra objectClasses */
+ at_map = (backsql_at_map_rec *)ch_calloc(1,
+ sizeof( backsql_at_map_rec ) );
+ at_map->bam_ad = slap_schema.si_ad_objectClass;
+ at_map->bam_true_ad = slap_schema.si_ad_objectClass;
+ ber_str2bv( "ldap_entry_objclasses.oc_name", 0, 1,
+ &at_map->bam_sel_expr );
+ ber_str2bv( "ldap_entry_objclasses,ldap_entries", 0, 1,
+ &at_map->bam_from_tbls );
+
+ bb.bb_len = at_map->bam_from_tbls.bv_len + 1;
+ bb.bb_val = at_map->bam_from_tbls;
+ backsql_merge_from_clause( bi, &bb, &oc_map->bom_keytbl );
+ at_map->bam_from_tbls = bb.bb_val;
+
+ BER_BVZERO( &bb.bb_val );
+ bb.bb_len = 0;
+ backsql_strfcat_x( &bb, NULL, "lbcblb",
+ (ber_len_t)STRLENOF( "ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entries.keyval=" ),
+ "ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entries.keyval=",
+ &oc_map->bom_keytbl,
+ '.',
+ &oc_map->bom_keycol,
+ (ber_len_t)STRLENOF( " and ldap_entries.oc_map_id=" ),
+ " and ldap_entries.oc_map_id=",
+ &sbv );
+ at_map->bam_join_where = bb.bb_val;
+
+ at_map->bam_oc = oc_map->bom_oc;
+
+ at_map->bam_add_proc = NULL;
+ {
+ char tmp[STRLENOF("INSERT INTO ldap_entry_objclasses "
+ "(entry_id,oc_name) VALUES "
+ "((SELECT id FROM ldap_entries "
+ "WHERE oc_map_id= "
+ "AND keyval=?),?)") + LDAP_PVT_INTTYPE_CHARS(unsigned long)];
+ snprintf( tmp, sizeof(tmp),
+ "INSERT INTO ldap_entry_objclasses "
+ "(entry_id,oc_name) VALUES "
+ "((SELECT id FROM ldap_entries "
+ "WHERE oc_map_id=" BACKSQL_IDNUMFMT " "
+ "AND keyval=?),?)", oc_map->bom_id );
+ at_map->bam_add_proc = ch_strdup( tmp );
+ }
+
+ at_map->bam_delete_proc = NULL;
+ {
+ char tmp[STRLENOF("DELETE FROM ldap_entry_objclasses "
+ "WHERE entry_id=(SELECT id FROM ldap_entries "
+ "WHERE oc_map_id= "
+ "AND keyval=?) AND oc_name=?") + LDAP_PVT_INTTYPE_CHARS(unsigned long)];
+ snprintf( tmp, sizeof(tmp),
+ "DELETE FROM ldap_entry_objclasses "
+ "WHERE entry_id=(SELECT id FROM ldap_entries "
+ "WHERE oc_map_id=" BACKSQL_IDNUMFMT " "
+ "AND keyval=?) AND oc_name=?",
+ oc_map->bom_id );
+ at_map->bam_delete_proc = ch_strdup( tmp );
+ }
+
+ at_map->bam_param_order = 0;
+ at_map->bam_expect_return = 0;
+ at_map->bam_next = NULL;
+
+ backsql_make_attr_query( bi, oc_map, at_map );
+ if ( ldap_avl_insert( &oc_map->bom_attrs, at_map, backsql_cmp_attr, backsql_dup_attr ) == BACKSQL_DUPLICATE ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_add_sysmaps(): "
+ "duplicate attribute \"%s\" in objectClass \"%s\" map\n",
+ at_map->bam_ad->ad_cname.bv_val,
+ oc_map->bom_oc->soc_cname.bv_val );
+ }
+
+ /* FIXME: we need to correct the objectClass join_where
+ * after the attribute query is built */
+ ch_free( at_map->bam_join_where.bv_val );
+ BER_BVZERO( &bb.bb_val );
+ bb.bb_len = 0;
+ backsql_strfcat_x( &bb, NULL, "lbcblb",
+ (ber_len_t)STRLENOF( /* "ldap_entries.id=ldap_entry_objclasses.entry_id AND " */ "ldap_entries.keyval=" ),
+ /* "ldap_entries.id=ldap_entry_objclasses.entry_id AND " */ "ldap_entries.keyval=",
+ &oc_map->bom_keytbl,
+ '.',
+ &oc_map->bom_keycol,
+ (ber_len_t)STRLENOF( " AND ldap_entries.oc_map_id=" ),
+ " AND ldap_entries.oc_map_id=",
+ &sbv );
+ at_map->bam_join_where = bb.bb_val;
+
+ return 1;
+}
+
+struct backsql_attr_schema_info {
+ backsql_info *bas_bi;
+ SQLHDBC bas_dbh;
+ SQLHSTMT bas_sth;
+ backsql_key_t *bas_oc_id;
+ int bas_rc;
+};
+
+static int
+backsql_oc_get_attr_mapping( void *v_oc, void *v_bas )
+{
+ RETCODE rc;
+ BACKSQL_ROW_NTS at_row;
+ backsql_oc_map_rec *oc_map = (backsql_oc_map_rec *)v_oc;
+ backsql_at_map_rec *at_map;
+ struct backsql_attr_schema_info *bas = (struct backsql_attr_schema_info *)v_bas;
+
+ /* bas->bas_oc_id has been bound to bas->bas_sth */
+ *bas->bas_oc_id = oc_map->bom_id;
+
+ Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_attr_mapping(): "
+ "executing at_query\n"
+ " \"%s\"\n"
+ " for objectClass \"%s\"\n"
+ " with param oc_id=" BACKSQL_IDNUMFMT "\n",
+ bas->bas_bi->sql_at_query,
+ BACKSQL_OC_NAME( oc_map ),
+ *bas->bas_oc_id );
+
+ rc = SQLExecute( bas->bas_sth );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_attr_mapping(): "
+ "error executing at_query\n"
+ " \"%s\"\n"
+ " for objectClass \"%s\"\n"
+ " with param oc_id=" BACKSQL_IDNUMFMT "\n",
+ bas->bas_bi->sql_at_query,
+ BACKSQL_OC_NAME( oc_map ),
+ *bas->bas_oc_id );
+ backsql_PrintErrors( bas->bas_bi->sql_db_env,
+ bas->bas_dbh, bas->bas_sth, rc );
+ bas->bas_rc = LDAP_OTHER;
+ return BACKSQL_AVL_STOP;
+ }
+
+ backsql_BindRowAsStrings( bas->bas_sth, &at_row );
+ for ( ; rc = SQLFetch( bas->bas_sth ), BACKSQL_SUCCESS( rc ); ) {
+ const char *text = NULL;
+ struct berval bv;
+ struct berbuf bb = BB_NULL;
+ AttributeDescription *ad = NULL;
+
+ {
+ struct {
+ int idx;
+ char *name;
+ } required[] = {
+ { 0, "name" },
+ { 1, "sel_expr" },
+ { 2, "from" },
+ { -1, NULL },
+ };
+ int i;
+
+ for ( i = 0; required[ i ].name != NULL; i++ ) {
+ if ( at_row.value_len[ i ] <= 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "backsql_oc_get_attr_mapping(): "
+ "required column #%d \"%s\" is empty\n",
+ required[ i ].idx, required[ i ].name );
+ bas->bas_rc = LDAP_OTHER;
+ return BACKSQL_AVL_STOP;
+ }
+ }
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ "attributeType: " "name=\"%s\" " "sel_expr=\"%s\" " "from=\"%s\" " "join_where=\"%s\" " "add_proc=\"%s\" " "delete_proc=\"%s\" " "sel_expr_u=\"%s\"\n",
+ at_row.cols[0], at_row.cols[1], at_row.cols[2],
+ at_row.cols[3] ? at_row.cols[3] : "",
+ at_row.cols[4] ? at_row.cols[4] : "",
+ at_row.cols[5] ? at_row.cols[5] : "",
+ at_row.cols[8] ? at_row.cols[8] : "" );
+
+ rc = slap_str2ad( at_row.cols[ 0 ], &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_attr_mapping(): "
+ "attribute \"%s\" for objectClass \"%s\" "
+ "is not defined in schema: %s\n",
+ at_row.cols[ 0 ],
+ BACKSQL_OC_NAME( oc_map ), text );
+ bas->bas_rc = LDAP_CONSTRAINT_VIOLATION;
+ return BACKSQL_AVL_STOP;
+ }
+ at_map = (backsql_at_map_rec *)ch_calloc( 1,
+ sizeof( backsql_at_map_rec ) );
+ at_map->bam_ad = ad;
+ at_map->bam_true_ad = ad;
+ if ( slap_syntax_is_binary( ad->ad_type->sat_syntax )
+ && !slap_ad_is_binary( ad ) )
+ {
+ char buf[ SLAP_TEXT_BUFLEN ];
+ struct berval bv;
+ const char *text = NULL;
+
+ bv.bv_val = buf;
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%s;binary",
+ ad->ad_cname.bv_val );
+ at_map->bam_true_ad = NULL;
+ bas->bas_rc = slap_bv2ad( &bv, &at_map->bam_true_ad, &text );
+ if ( bas->bas_rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_attr_mapping(): "
+ "unable to fetch attribute \"%s\": %s (%d)\n",
+ buf, text, rc );
+ ch_free( at_map );
+ return BACKSQL_AVL_STOP;
+ }
+ }
+
+ ber_str2bv( at_row.cols[ 1 ], 0, 1, &at_map->bam_sel_expr );
+ if ( at_row.value_len[ 8 ] <= 0 ) {
+ BER_BVZERO( &at_map->bam_sel_expr_u );
+
+ } else {
+ ber_str2bv( at_row.cols[ 8 ], 0, 1,
+ &at_map->bam_sel_expr_u );
+ }
+
+ ber_str2bv( at_row.cols[ 2 ], 0, 0, &bv );
+ backsql_merge_from_clause( bas->bas_bi, &bb, &bv );
+ at_map->bam_from_tbls = bb.bb_val;
+ if ( at_row.value_len[ 3 ] <= 0 ) {
+ BER_BVZERO( &at_map->bam_join_where );
+
+ } else {
+ ber_str2bv( at_row.cols[ 3 ], 0, 1,
+ &at_map->bam_join_where );
+ }
+ at_map->bam_add_proc = NULL;
+ if ( at_row.value_len[ 4 ] > 0 ) {
+ at_map->bam_add_proc = ch_strdup( at_row.cols[ 4 ] );
+ }
+ at_map->bam_delete_proc = NULL;
+ if ( at_row.value_len[ 5 ] > 0 ) {
+ at_map->bam_delete_proc = ch_strdup( at_row.cols[ 5 ] );
+ }
+ if ( lutil_atoix( &at_map->bam_param_order, at_row.cols[ 6 ], 0 ) != 0 ) {
+ /* error */
+ }
+ if ( lutil_atoix( &at_map->bam_expect_return, at_row.cols[ 7 ], 0 ) != 0 ) {
+ /* error */
+ }
+ backsql_make_attr_query( bas->bas_bi, oc_map, at_map );
+ Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_attr_mapping(): "
+ "preconstructed query \"%s\"\n",
+ at_map->bam_query );
+ at_map->bam_next = NULL;
+ if ( ldap_avl_insert( &oc_map->bom_attrs, at_map, backsql_cmp_attr, backsql_dup_attr ) == BACKSQL_DUPLICATE ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_attr_mapping(): "
+ "duplicate attribute \"%s\" "
+ "in objectClass \"%s\" map\n",
+ at_map->bam_ad->ad_cname.bv_val,
+ oc_map->bom_oc->soc_cname.bv_val );
+ ch_free( at_map );
+ }
+
+ if ( !BER_BVISNULL( &bas->bas_bi->sql_upper_func ) &&
+ BER_BVISNULL( &at_map->bam_sel_expr_u ) )
+ {
+ struct berbuf bb = BB_NULL;
+
+ backsql_strfcat_x( &bb, NULL, "bcbc",
+ &bas->bas_bi->sql_upper_func,
+ '(' /* ) */ ,
+ &at_map->bam_sel_expr,
+ /* ( */ ')' );
+ at_map->bam_sel_expr_u = bb.bb_val;
+ }
+ }
+ backsql_FreeRow( &at_row );
+ SQLFreeStmt( bas->bas_sth, SQL_CLOSE );
+
+ Debug( LDAP_DEBUG_TRACE, "backsql_load_schema_map(\"%s\"): "
+ "autoadding 'objectClass' and 'ref' mappings\n",
+ BACKSQL_OC_NAME( oc_map ) );
+
+ (void)backsql_add_sysmaps( bas->bas_bi, oc_map );
+
+ return BACKSQL_AVL_CONTINUE;
+}
+
+
+int
+backsql_load_schema_map( backsql_info *bi, SQLHDBC dbh )
+{
+ SQLHSTMT sth = SQL_NULL_HSTMT;
+ RETCODE rc;
+ BACKSQL_ROW_NTS oc_row;
+ backsql_key_t oc_id;
+ backsql_oc_map_rec *oc_map;
+ struct backsql_attr_schema_info bas;
+
+ int delete_proc_idx = 5;
+ int create_hint_idx = delete_proc_idx + 2;
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_load_schema_map()\n" );
+
+ /*
+ * TimesTen : See if the ldap_entries.dn_ru field exists in the schema
+ */
+ if ( !BACKSQL_DONTCHECK_LDAPINFO_DN_RU( bi ) ) {
+ rc = backsql_Prepare( dbh, &sth,
+ backsql_check_dn_ru_query, 0 );
+ if ( rc == SQL_SUCCESS ) {
+ /* Yes, the field exists */
+ bi->sql_flags |= BSQLF_HAS_LDAPINFO_DN_RU;
+ Debug( LDAP_DEBUG_TRACE, "ldapinfo.dn_ru field exists "
+ "in the schema\n" );
+ } else {
+ /* No such field exists */
+ bi->sql_flags &= ~BSQLF_HAS_LDAPINFO_DN_RU;
+ }
+
+ SQLFreeStmt( sth, SQL_DROP );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "backsql_load_schema_map(): oc_query \"%s\"\n",
+ bi->sql_oc_query );
+
+ rc = backsql_Prepare( dbh, &sth, bi->sql_oc_query, 0 );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_load_schema_map(): "
+ "error preparing oc_query: \"%s\"\n",
+ bi->sql_oc_query );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
+ return LDAP_OTHER;
+ }
+
+ rc = SQLExecute( sth );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_load_schema_map(): "
+ "error executing oc_query: \n" );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
+ return LDAP_OTHER;
+ }
+
+ backsql_BindRowAsStrings( sth, &oc_row );
+ rc = SQLFetch( sth );
+
+ if ( BACKSQL_CREATE_NEEDS_SELECT( bi ) ) {
+ delete_proc_idx++;
+ create_hint_idx++;
+ }
+
+ for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
+ {
+ struct {
+ int idx;
+ char *name;
+ } required[] = {
+ { 0, "id" },
+ { 1, "name" },
+ { 2, "keytbl" },
+ { 3, "keycol" },
+ { -1, "expect_return" },
+ { -1, NULL },
+ };
+ int i;
+
+ required[4].idx = delete_proc_idx + 1;
+
+ for ( i = 0; required[ i ].name != NULL; i++ ) {
+ if ( oc_row.value_len[ required[ i ].idx ] <= 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "backsql_load_schema_map(): "
+ "required column #%d \"%s\" is empty\n",
+ required[ i ].idx, required[ i ].name );
+ return LDAP_OTHER;
+ }
+ }
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ "objectClass: " "id=\"%s\" " "name=\"%s\" " "keytbl=\"%s\" " "keycol=\"%s\" " "create_proc=\"%s\" " "create_keyval=\"%s\" " "delete_proc=\"%s\" " "expect_return=\"%s\"" "create_hint=\"%s\" \n",
+ oc_row.cols[0], oc_row.cols[1], oc_row.cols[2],
+ oc_row.cols[3],
+ oc_row.cols[4] ? oc_row.cols[4] : "",
+ (BACKSQL_CREATE_NEEDS_SELECT(bi) && oc_row.cols[5]) ? oc_row.cols[5] : "",
+ oc_row.cols[delete_proc_idx] ? oc_row.cols[delete_proc_idx] : "",
+ oc_row.cols[delete_proc_idx + 1],
+ ((oc_row.ncols > create_hint_idx) && oc_row.cols[create_hint_idx]) ? oc_row.cols[create_hint_idx] : "" );
+
+ oc_map = (backsql_oc_map_rec *)ch_calloc( 1,
+ sizeof( backsql_oc_map_rec ) );
+
+ if ( BACKSQL_STR2ID( &oc_map->bom_id, oc_row.cols[ 0 ], 0 ) != 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_load_schema_map(): "
+ "unable to parse id=\"%s\"\n",
+ oc_row.cols[ 0 ] );
+ ch_free( oc_map );
+ return LDAP_OTHER;
+ }
+
+ oc_map->bom_oc = oc_find( oc_row.cols[ 1 ] );
+ if ( oc_map->bom_oc == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_load_schema_map(): "
+ "objectClass \"%s\" is not defined in schema\n",
+ oc_row.cols[ 1 ] );
+ ch_free( oc_map );
+ return LDAP_OTHER; /* undefined objectClass ? */
+ }
+
+ ber_str2bv( oc_row.cols[ 2 ], 0, 1, &oc_map->bom_keytbl );
+ ber_str2bv( oc_row.cols[ 3 ], 0, 1, &oc_map->bom_keycol );
+ oc_map->bom_create_proc = ( oc_row.value_len[ 4 ] <= 0 ) ? NULL
+ : ch_strdup( oc_row.cols[ 4 ] );
+
+ if ( BACKSQL_CREATE_NEEDS_SELECT( bi ) ) {
+ oc_map->bom_create_keyval = ( oc_row.value_len[ 5 ] <= 0 )
+ ? NULL : ch_strdup( oc_row.cols[ 5 ] );
+ }
+ oc_map->bom_delete_proc = ( oc_row.value_len[ delete_proc_idx ] <= 0 ) ? NULL
+ : ch_strdup( oc_row.cols[ delete_proc_idx ] );
+ if ( lutil_atoix( &oc_map->bom_expect_return, oc_row.cols[ delete_proc_idx + 1 ], 0 ) != 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_load_schema_map(): "
+ "unable to parse expect_return=\"%s\" for objectClass \"%s\"\n",
+ oc_row.cols[ delete_proc_idx + 1 ], oc_row.cols[ 1 ] );
+ ch_free( oc_map );
+ return LDAP_OTHER;
+ }
+
+ if ( ( oc_row.ncols > create_hint_idx ) &&
+ ( oc_row.value_len[ create_hint_idx ] > 0 ) )
+ {
+ const char *text;
+
+ oc_map->bom_create_hint = NULL;
+ rc = slap_str2ad( oc_row.cols[ create_hint_idx ],
+ &oc_map->bom_create_hint, &text );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "load_schema_map(): "
+ "error matching "
+ "AttributeDescription %s "
+ "in create_hint: %s (%d)\n",
+ oc_row.cols[ create_hint_idx ],
+ text, rc );
+ backsql_PrintErrors( bi->sql_db_env, dbh,
+ sth, rc );
+ ch_free( oc_map );
+ return LDAP_OTHER;
+ }
+ }
+
+ /*
+ * FIXME: first attempt to check for offending
+ * instructions in {create|delete}_proc
+ */
+
+ oc_map->bom_attrs = NULL;
+ if ( ldap_avl_insert( &bi->sql_oc_by_oc, oc_map, backsql_cmp_oc, ldap_avl_dup_error ) == -1 ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_load_schema_map(): "
+ "duplicate objectClass \"%s\" in objectClass map\n",
+ oc_map->bom_oc->soc_cname.bv_val );
+ ch_free( oc_map );
+ return LDAP_OTHER;
+ }
+ if ( ldap_avl_insert( &bi->sql_oc_by_id, oc_map, backsql_cmp_oc_id, ldap_avl_dup_error ) == -1 ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_load_schema_map(): "
+ "duplicate objectClass \"%s\" in objectClass by ID map\n",
+ oc_map->bom_oc->soc_cname.bv_val );
+ return LDAP_OTHER;
+ }
+ oc_id = oc_map->bom_id;
+ Debug( LDAP_DEBUG_TRACE, "backsql_load_schema_map(): "
+ "objectClass \"%s\":\n keytbl=\"%s\" keycol=\"%s\"\n",
+ BACKSQL_OC_NAME( oc_map ),
+ oc_map->bom_keytbl.bv_val, oc_map->bom_keycol.bv_val );
+ if ( oc_map->bom_create_proc ) {
+ Debug( LDAP_DEBUG_TRACE, " create_proc=\"%s\"\n",
+ oc_map->bom_create_proc );
+ }
+ if ( oc_map->bom_create_keyval ) {
+ Debug( LDAP_DEBUG_TRACE, " create_keyval=\"%s\"\n",
+ oc_map->bom_create_keyval );
+ }
+ if ( oc_map->bom_create_hint ) {
+ Debug( LDAP_DEBUG_TRACE, " create_hint=\"%s\"\n",
+ oc_map->bom_create_hint->ad_cname.bv_val );
+ }
+ if ( oc_map->bom_delete_proc ) {
+ Debug( LDAP_DEBUG_TRACE, " delete_proc=\"%s\"\n",
+ oc_map->bom_delete_proc );
+ }
+ Debug( LDAP_DEBUG_TRACE, " expect_return: "
+ "add=%d, del=%d; attributes:\n",
+ BACKSQL_IS_ADD( oc_map->bom_expect_return ),
+ BACKSQL_IS_DEL( oc_map->bom_expect_return ) );
+ }
+
+ backsql_FreeRow( &oc_row );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ /* prepare for attribute fetching */
+ Debug( LDAP_DEBUG_TRACE, "backsql_load_schema_map(): at_query \"%s\"\n",
+ bi->sql_at_query );
+
+ rc = backsql_Prepare( dbh, &sth, bi->sql_at_query, 0 );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_load_schema_map(): "
+ "error preparing at_query: \"%s\"\n",
+ bi->sql_at_query );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
+ return LDAP_OTHER;
+ }
+
+ rc = backsql_BindParamNumID( sth, 1, SQL_PARAM_INPUT, &oc_id );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_load_schema_map(): "
+ "error binding param \"oc_id\" for at_query\n" );
+ backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+ return LDAP_OTHER;
+ }
+
+ bas.bas_bi = bi;
+ bas.bas_dbh = dbh;
+ bas.bas_sth = sth;
+ bas.bas_oc_id = &oc_id;
+ bas.bas_rc = LDAP_SUCCESS;
+
+ (void)ldap_avl_apply( bi->sql_oc_by_oc, backsql_oc_get_attr_mapping,
+ &bas, BACKSQL_AVL_STOP, AVL_INORDER );
+
+ SQLFreeStmt( sth, SQL_DROP );
+
+ bi->sql_flags |= BSQLF_SCHEMA_LOADED;
+
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_load_schema_map()\n" );
+
+ return bas.bas_rc;
+}
+
+backsql_oc_map_rec *
+backsql_oc2oc( backsql_info *bi, ObjectClass *oc )
+{
+ backsql_oc_map_rec tmp, *res;
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_oc2oc(): "
+ "searching for objectclass with name=\"%s\"\n",
+ oc->soc_cname.bv_val );
+#endif /* BACKSQL_TRACE */
+
+ tmp.bom_oc = oc;
+ res = (backsql_oc_map_rec *)ldap_avl_find( bi->sql_oc_by_oc, &tmp, backsql_cmp_oc );
+#ifdef BACKSQL_TRACE
+ if ( res != NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_oc2oc(): "
+ "found name=\"%s\", id=%d\n",
+ BACKSQL_OC_NAME( res ), res->bom_id );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_oc2oc(): "
+ "not found\n" );
+ }
+#endif /* BACKSQL_TRACE */
+
+ return res;
+}
+
+backsql_oc_map_rec *
+backsql_name2oc( backsql_info *bi, struct berval *oc_name )
+{
+ backsql_oc_map_rec tmp, *res;
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "==>oc_with_name(): "
+ "searching for objectclass with name=\"%s\"\n",
+ oc_name->bv_val );
+#endif /* BACKSQL_TRACE */
+
+ tmp.bom_oc = oc_bvfind( oc_name );
+ if ( tmp.bom_oc == NULL ) {
+ return NULL;
+ }
+
+ res = (backsql_oc_map_rec *)ldap_avl_find( bi->sql_oc_by_oc, &tmp, backsql_cmp_oc );
+#ifdef BACKSQL_TRACE
+ if ( res != NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "<==oc_with_name(): "
+ "found name=\"%s\", id=%d\n",
+ BACKSQL_OC_NAME( res ), res->bom_id );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "<==oc_with_name(): "
+ "not found\n" );
+ }
+#endif /* BACKSQL_TRACE */
+
+ return res;
+}
+
+backsql_oc_map_rec *
+backsql_id2oc( backsql_info *bi, unsigned long id )
+{
+ backsql_oc_map_rec tmp, *res;
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "==>oc_with_id(): "
+ "searching for objectclass with id=%lu\n", id );
+#endif /* BACKSQL_TRACE */
+
+ tmp.bom_id = id;
+ res = (backsql_oc_map_rec *)ldap_avl_find( bi->sql_oc_by_id, &tmp,
+ backsql_cmp_oc_id );
+
+#ifdef BACKSQL_TRACE
+ if ( res != NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "<==oc_with_id(): "
+ "found name=\"%s\", id=%lu\n",
+ BACKSQL_OC_NAME( res ), res->bom_id );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "<==oc_with_id(): "
+ "id=%lu not found\n", res->bom_id );
+ }
+#endif /* BACKSQL_TRACE */
+
+ return res;
+}
+
+backsql_at_map_rec *
+backsql_ad2at( backsql_oc_map_rec* objclass, AttributeDescription *ad )
+{
+ backsql_at_map_rec tmp = { 0 }, *res;
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_ad2at(): "
+ "searching for attribute \"%s\" for objectclass \"%s\"\n",
+ ad->ad_cname.bv_val, BACKSQL_OC_NAME( objclass ) );
+#endif /* BACKSQL_TRACE */
+
+ tmp.bam_ad = ad;
+ res = (backsql_at_map_rec *)ldap_avl_find( objclass->bom_attrs, &tmp,
+ backsql_cmp_attr );
+
+#ifdef BACKSQL_TRACE
+ if ( res != NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_ad2at(): "
+ "found name=\"%s\", sel_expr=\"%s\"\n",
+ res->bam_ad->ad_cname.bv_val,
+ res->bam_sel_expr.bv_val );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_ad2at(): "
+ "not found\n" );
+ }
+#endif /* BACKSQL_TRACE */
+
+ return res;
+}
+
+/* attributeType inheritance */
+struct supad2at_t {
+ backsql_at_map_rec **ret;
+ AttributeDescription *ad;
+ unsigned n;
+};
+
+#define SUPAD2AT_STOP (-1)
+
+static int
+supad2at_f( void *v_at, void *v_arg )
+{
+ backsql_at_map_rec *at = (backsql_at_map_rec *)v_at;
+ struct supad2at_t *va = (struct supad2at_t *)v_arg;
+
+ if ( is_at_subtype( at->bam_ad->ad_type, va->ad->ad_type ) ) {
+ backsql_at_map_rec **ret = NULL;
+ unsigned i;
+
+ /* if already listed, holler! (should never happen) */
+ if ( va->ret ) {
+ for ( i = 0; i < va->n; i++ ) {
+ if ( va->ret[ i ]->bam_ad == at->bam_ad ) {
+ break;
+ }
+ }
+
+ if ( i < va->n ) {
+ return 0;
+ }
+ }
+
+ ret = ch_realloc( va->ret,
+ sizeof( backsql_at_map_rec * ) * ( va->n + 2 ) );
+ if ( ret == NULL ) {
+ ch_free( va->ret );
+ va->ret = NULL;
+ va->n = 0;
+ return SUPAD2AT_STOP;
+ }
+
+ ret[ va->n ] = at;
+ va->n++;
+ ret[ va->n ] = NULL;
+ va->ret = ret;
+ }
+
+ return 0;
+}
+
+/*
+ * stores in *pret a NULL terminated array of pointers
+ * to backsql_at_map_rec whose attributeType is supad->ad_type
+ * or derived from it
+ */
+int
+backsql_supad2at( backsql_oc_map_rec *objclass, AttributeDescription *supad,
+ backsql_at_map_rec ***pret )
+{
+ struct supad2at_t va = { 0 };
+ int rc;
+
+ assert( objclass != NULL );
+ assert( supad != NULL );
+ assert( pret != NULL );
+
+ *pret = NULL;
+
+ va.ad = supad;
+
+ rc = ldap_avl_apply( objclass->bom_attrs, supad2at_f, &va,
+ SUPAD2AT_STOP, AVL_INORDER );
+ if ( rc == SUPAD2AT_STOP ) {
+ return -1;
+ }
+
+ *pret = va.ret;
+
+ return 0;
+}
+
+static void
+backsql_free_attr( void *v_at )
+{
+ backsql_at_map_rec *at = v_at;
+
+ Debug( LDAP_DEBUG_TRACE, "==>free_attr(): \"%s\"\n",
+ at->bam_ad->ad_cname.bv_val );
+ ch_free( at->bam_sel_expr.bv_val );
+ if ( !BER_BVISNULL( &at->bam_from_tbls ) ) {
+ ch_free( at->bam_from_tbls.bv_val );
+ }
+ if ( !BER_BVISNULL( &at->bam_join_where ) ) {
+ ch_free( at->bam_join_where.bv_val );
+ }
+ if ( at->bam_add_proc != NULL ) {
+ ch_free( at->bam_add_proc );
+ }
+ if ( at->bam_delete_proc != NULL ) {
+ ch_free( at->bam_delete_proc );
+ }
+ if ( at->bam_query != NULL ) {
+ ch_free( at->bam_query );
+ }
+
+#ifdef BACKSQL_COUNTQUERY
+ if ( at->bam_countquery != NULL ) {
+ ch_free( at->bam_countquery );
+ }
+#endif /* BACKSQL_COUNTQUERY */
+
+ /* TimesTen */
+ if ( !BER_BVISNULL( &at->bam_sel_expr_u ) ) {
+ ch_free( at->bam_sel_expr_u.bv_val );
+ }
+
+ if ( at->bam_next ) {
+ backsql_free_attr( at->bam_next );
+ }
+
+ ch_free( at );
+
+ Debug( LDAP_DEBUG_TRACE, "<==free_attr()\n" );
+}
+
+static void
+backsql_free_oc( void *v_oc )
+{
+ backsql_oc_map_rec *oc = v_oc;
+
+ Debug( LDAP_DEBUG_TRACE, "==>free_oc(): \"%s\"\n",
+ BACKSQL_OC_NAME( oc ) );
+ ldap_avl_free( oc->bom_attrs, backsql_free_attr );
+ ch_free( oc->bom_keytbl.bv_val );
+ ch_free( oc->bom_keycol.bv_val );
+ if ( oc->bom_create_proc != NULL ) {
+ ch_free( oc->bom_create_proc );
+ }
+ if ( oc->bom_create_keyval != NULL ) {
+ ch_free( oc->bom_create_keyval );
+ }
+ if ( oc->bom_delete_proc != NULL ) {
+ ch_free( oc->bom_delete_proc );
+ }
+ ch_free( oc );
+
+ Debug( LDAP_DEBUG_TRACE, "<==free_oc()\n" );
+}
+
+int
+backsql_destroy_schema_map( backsql_info *bi )
+{
+ Debug( LDAP_DEBUG_TRACE, "==>destroy_schema_map()\n" );
+ ldap_avl_free( bi->sql_oc_by_oc, 0 );
+ ldap_avl_free( bi->sql_oc_by_id, backsql_free_oc );
+ Debug( LDAP_DEBUG_TRACE, "<==destroy_schema_map()\n" );
+ return 0;
+}
+
diff --git a/servers/slapd/back-sql/search.c b/servers/slapd/back-sql/search.c
new file mode 100644
index 0000000..d4177f6
--- /dev/null
+++ b/servers/slapd/back-sql/search.c
@@ -0,0 +1,2874 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 Dmitry Kovalev.
+ * Portions Copyright 2002 Pierangelo Masarati.
+ * Portions Copyright 2004 Mark Adamson.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Dmitry Kovalev for inclusion
+ * by OpenLDAP Software. Additional significant contributors include
+ * Pierangelo Masarati and Mark Adamson.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "ac/string.h"
+#include "ac/ctype.h"
+
+#include "lutil.h"
+#include "slap.h"
+#include "proto-sql.h"
+
+static int backsql_process_filter( backsql_srch_info *bsi, Filter *f );
+static int backsql_process_filter_eq( backsql_srch_info *bsi,
+ backsql_at_map_rec *at,
+ int casefold, struct berval *filter_value );
+static int backsql_process_filter_like( backsql_srch_info *bsi,
+ backsql_at_map_rec *at,
+ int casefold, struct berval *filter_value );
+static int backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f,
+ backsql_at_map_rec *at );
+
+/* For LDAP_CONTROL_PAGEDRESULTS, a 32 bit cookie is available to keep track of
+ the state of paged results. The ldap_entries.id and oc_map_id values of the
+ last entry returned are used as the cookie, so 6 bits are used for the OC id
+ and the other 26 for ldap_entries ID number. If your max(oc_map_id) is more
+ than 63, you will need to steal more bits from ldap_entries ID number and
+ put them into the OC ID part of the cookie. */
+
+/* NOTE: not supported when BACKSQL_ARBITRARY_KEY is defined */
+#ifndef BACKSQL_ARBITRARY_KEY
+#define SQL_TO_PAGECOOKIE(id, oc) (((id) << 6 ) | ((oc) & 0x3F))
+#define PAGECOOKIE_TO_SQL_ID(pc) ((pc) >> 6)
+#define PAGECOOKIE_TO_SQL_OC(pc) ((pc) & 0x3F)
+
+static int parse_paged_cookie( Operation *op, SlapReply *rs );
+
+static void send_paged_response(
+ Operation *op,
+ SlapReply *rs,
+ ID *lastid );
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+
+/* Look for chars that need to be escaped, return count of them.
+ * If out is non-NULL, copy escape'd val to it.
+ */
+static int
+backsql_val_escape( Operation *op, struct berval *in, struct berval *out )
+{
+ char *ptr, *end;
+ int q = 0;
+
+ ptr = in->bv_val;
+ end = ptr + in->bv_len;
+ while (ptr < end) {
+ if ( *ptr == '\'' )
+ q++;
+ ptr++;
+ }
+ if ( q && out ) {
+ char *dst;
+ out->bv_len = in->bv_len + q;
+ out->bv_val = op->o_tmpalloc( out->bv_len + 1, op->o_tmpmemctx );
+ ptr = in->bv_val;
+ dst = out->bv_val;
+ while (ptr < end ) {
+ if ( *ptr == '\'' )
+ *dst++ = '\'';
+ *dst++ = *ptr++;
+ }
+ *dst = '\0';
+ }
+ return q;
+}
+
+static int
+backsql_attrlist_add( backsql_srch_info *bsi, AttributeDescription *ad )
+{
+ int n_attrs = 0;
+ AttributeName *an = NULL;
+
+ if ( bsi->bsi_attrs == NULL ) {
+ return 1;
+ }
+
+ /*
+ * clear the list (retrieve all attrs)
+ */
+ if ( ad == NULL ) {
+ bsi->bsi_op->o_tmpfree( bsi->bsi_attrs, bsi->bsi_op->o_tmpmemctx );
+ bsi->bsi_attrs = NULL;
+ bsi->bsi_flags |= BSQL_SF_ALL_ATTRS;
+ return 1;
+ }
+
+ /* strip ';binary' */
+ if ( slap_ad_is_binary( ad ) ) {
+ ad = ad->ad_type->sat_ad;
+ }
+
+ for ( ; !BER_BVISNULL( &bsi->bsi_attrs[ n_attrs ].an_name ); n_attrs++ ) {
+ an = &bsi->bsi_attrs[ n_attrs ];
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
+ "attribute \"%s\" is in list\n",
+ an->an_name.bv_val );
+ /*
+ * We can live with strcmp because the attribute
+ * list has been normalized before calling be_search
+ */
+ if ( !BACKSQL_NCMP( &an->an_name, &ad->ad_cname ) ) {
+ return 1;
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_attrlist_add(): "
+ "adding \"%s\" to list\n", ad->ad_cname.bv_val );
+
+ an = (AttributeName *)bsi->bsi_op->o_tmprealloc( bsi->bsi_attrs,
+ sizeof( AttributeName ) * ( n_attrs + 2 ),
+ bsi->bsi_op->o_tmpmemctx );
+ if ( an == NULL ) {
+ return -1;
+ }
+
+ an[ n_attrs ].an_name = ad->ad_cname;
+ an[ n_attrs ].an_desc = ad;
+ BER_BVZERO( &an[ n_attrs + 1 ].an_name );
+
+ bsi->bsi_attrs = an;
+
+ return 1;
+}
+
+/*
+ * Initializes the search structure.
+ *
+ * If get_base_id != 0, the field bsi_base_id is filled
+ * with the entryID of bsi_base_ndn; it must be freed
+ * by backsql_free_entryID() when no longer required.
+ *
+ * NOTE: base must be normalized
+ */
+int
+backsql_init_search(
+ backsql_srch_info *bsi,
+ struct berval *nbase,
+ int scope,
+ time_t stoptime,
+ Filter *filter,
+ SQLHDBC dbh,
+ Operation *op,
+ SlapReply *rs,
+ AttributeName *attrs,
+ unsigned flags )
+{
+ backsql_info *bi = (backsql_info *)op->o_bd->be_private;
+ int rc = LDAP_SUCCESS;
+
+ bsi->bsi_base_ndn = nbase;
+ bsi->bsi_use_subtree_shortcut = 0;
+ BER_BVZERO( &bsi->bsi_base_id.eid_dn );
+ BER_BVZERO( &bsi->bsi_base_id.eid_ndn );
+ bsi->bsi_scope = scope;
+ bsi->bsi_filter = filter;
+ bsi->bsi_dbh = dbh;
+ bsi->bsi_op = op;
+ bsi->bsi_rs = rs;
+ bsi->bsi_flags = BSQL_SF_NONE;
+
+ bsi->bsi_attrs = NULL;
+
+ if ( BACKSQL_FETCH_ALL_ATTRS( bi ) ) {
+ /*
+ * if requested, simply try to fetch all attributes
+ */
+ bsi->bsi_flags |= BSQL_SF_ALL_ATTRS;
+
+ } else {
+ if ( BACKSQL_FETCH_ALL_USERATTRS( bi ) ) {
+ bsi->bsi_flags |= BSQL_SF_ALL_USER;
+
+ } else if ( BACKSQL_FETCH_ALL_OPATTRS( bi ) ) {
+ bsi->bsi_flags |= BSQL_SF_ALL_OPER;
+ }
+
+ if ( attrs == NULL ) {
+ /* NULL means all user attributes */
+ bsi->bsi_flags |= BSQL_SF_ALL_USER;
+
+ } else {
+ AttributeName *p;
+ int got_oc = 0;
+
+ bsi->bsi_attrs = (AttributeName *)bsi->bsi_op->o_tmpalloc(
+ sizeof( AttributeName ),
+ bsi->bsi_op->o_tmpmemctx );
+ BER_BVZERO( &bsi->bsi_attrs[ 0 ].an_name );
+
+ for ( p = attrs; !BER_BVISNULL( &p->an_name ); p++ ) {
+ if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_user_attrs ) == 0 ) {
+ /* handle "*" */
+ bsi->bsi_flags |= BSQL_SF_ALL_USER;
+
+ /* if all attrs are requested, there's
+ * no need to continue */
+ if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
+ bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
+ bsi->bsi_op->o_tmpmemctx );
+ bsi->bsi_attrs = NULL;
+ break;
+ }
+ continue;
+
+ } else if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_operational_attrs ) == 0 ) {
+ /* handle "+" */
+ bsi->bsi_flags |= BSQL_SF_ALL_OPER;
+
+ /* if all attrs are requested, there's
+ * no need to continue */
+ if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
+ bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
+ bsi->bsi_op->o_tmpmemctx );
+ bsi->bsi_attrs = NULL;
+ break;
+ }
+ continue;
+
+ } else if ( BACKSQL_NCMP( &p->an_name, slap_bv_no_attrs ) == 0 ) {
+ /* ignore "1.1" */
+ continue;
+
+ } else if ( p->an_desc == slap_schema.si_ad_objectClass ) {
+ got_oc = 1;
+ }
+
+ backsql_attrlist_add( bsi, p->an_desc );
+ }
+
+ if ( got_oc == 0 && !( bsi->bsi_flags & BSQL_SF_ALL_USER ) ) {
+ /* add objectClass if not present,
+ * because it is required to understand
+ * if an entry is a referral, an alias
+ * or so... */
+ backsql_attrlist_add( bsi, slap_schema.si_ad_objectClass );
+ }
+ }
+
+ if ( !BSQL_ISF_ALL_ATTRS( bsi ) && bi->sql_anlist ) {
+ AttributeName *p;
+
+ /* use hints if available */
+ for ( p = bi->sql_anlist; !BER_BVISNULL( &p->an_name ); p++ ) {
+ if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_user_attrs ) == 0 ) {
+ /* handle "*" */
+ bsi->bsi_flags |= BSQL_SF_ALL_USER;
+
+ /* if all attrs are requested, there's
+ * no need to continue */
+ if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
+ bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
+ bsi->bsi_op->o_tmpmemctx );
+ bsi->bsi_attrs = NULL;
+ break;
+ }
+ continue;
+
+ } else if ( BACKSQL_NCMP( &p->an_name, slap_bv_all_operational_attrs ) == 0 ) {
+ /* handle "+" */
+ bsi->bsi_flags |= BSQL_SF_ALL_OPER;
+
+ /* if all attrs are requested, there's
+ * no need to continue */
+ if ( BSQL_ISF_ALL_ATTRS( bsi ) ) {
+ bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
+ bsi->bsi_op->o_tmpmemctx );
+ bsi->bsi_attrs = NULL;
+ break;
+ }
+ continue;
+ }
+
+ backsql_attrlist_add( bsi, p->an_desc );
+ }
+
+ }
+ }
+
+ bsi->bsi_id_list = NULL;
+ bsi->bsi_id_listtail = &bsi->bsi_id_list;
+ bsi->bsi_n_candidates = 0;
+ bsi->bsi_stoptime = stoptime;
+ BER_BVZERO( &bsi->bsi_sel.bb_val );
+ bsi->bsi_sel.bb_len = 0;
+ BER_BVZERO( &bsi->bsi_from.bb_val );
+ bsi->bsi_from.bb_len = 0;
+ BER_BVZERO( &bsi->bsi_join_where.bb_val );
+ bsi->bsi_join_where.bb_len = 0;
+ BER_BVZERO( &bsi->bsi_flt_where.bb_val );
+ bsi->bsi_flt_where.bb_len = 0;
+ bsi->bsi_filter_oc = NULL;
+
+ if ( BACKSQL_IS_GET_ID( flags ) ) {
+ int matched = BACKSQL_IS_MATCHED( flags );
+ int getentry = BACKSQL_IS_GET_ENTRY( flags );
+ int gotit = 0;
+
+ assert( op->o_bd->be_private != NULL );
+
+ rc = backsql_dn2id( op, rs, dbh, nbase, &bsi->bsi_base_id,
+ matched, 1 );
+
+ /* the entry is collected either if requested for by getentry
+ * or if get noSuchObject and requested to climb the tree,
+ * so that a matchedDN or a referral can be returned */
+ if ( ( rc == LDAP_NO_SUCH_OBJECT && matched ) || getentry ) {
+ if ( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) ) {
+ assert( bsi->bsi_e != NULL );
+
+ if ( dn_match( nbase, &bsi->bsi_base_id.eid_ndn ) )
+ {
+ gotit = 1;
+ }
+
+ /*
+ * let's see if it is a referral and, in case, get it
+ */
+ backsql_attrlist_add( bsi, slap_schema.si_ad_ref );
+ rc = backsql_id2entry( bsi, &bsi->bsi_base_id );
+ if ( rc == LDAP_SUCCESS ) {
+ if ( is_entry_referral( bsi->bsi_e ) )
+ {
+ BerVarray erefs = get_entry_referrals( op, bsi->bsi_e );
+ if ( erefs ) {
+ rc = rs->sr_err = LDAP_REFERRAL;
+ rs->sr_ref = referral_rewrite( erefs,
+ &bsi->bsi_e->e_nname,
+ &op->o_req_dn,
+ scope );
+ ber_bvarray_free( erefs );
+
+ } else {
+ rc = rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "bad referral object";
+ }
+
+ } else if ( !gotit ) {
+ rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ }
+ }
+
+ } else {
+ rs->sr_err = rc;
+ }
+ }
+
+ if ( gotit && BACKSQL_IS_GET_OC( flags ) ) {
+ bsi->bsi_base_id.eid_oc = backsql_id2oc( bi,
+ bsi->bsi_base_id.eid_oc_id );
+ if ( bsi->bsi_base_id.eid_oc == NULL ) {
+ /* error? */
+ backsql_free_entryID( &bsi->bsi_base_id, 1,
+ op->o_tmpmemctx );
+ rc = rs->sr_err = LDAP_OTHER;
+ }
+ }
+ }
+
+ bsi->bsi_status = rc;
+
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ case LDAP_REFERRAL:
+ break;
+
+ default:
+ bsi->bsi_op->o_tmpfree( bsi->bsi_attrs,
+ bsi->bsi_op->o_tmpmemctx );
+ break;
+ }
+
+ return rc;
+}
+
+static int
+backsql_process_filter_list( backsql_srch_info *bsi, Filter *f, int op )
+{
+ int res;
+
+ if ( !f ) {
+ return 0;
+ }
+
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */ );
+
+ while ( 1 ) {
+ res = backsql_process_filter( bsi, f );
+ if ( res < 0 ) {
+ /*
+ * TimesTen : If the query has no answers,
+ * don't bother to run the query.
+ */
+ return -1;
+ }
+
+ f = f->f_next;
+ if ( f == NULL ) {
+ break;
+ }
+
+ switch ( op ) {
+ case LDAP_FILTER_AND:
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx, "l",
+ (ber_len_t)STRLENOF( " AND " ),
+ " AND " );
+ break;
+
+ case LDAP_FILTER_OR:
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx, "l",
+ (ber_len_t)STRLENOF( " OR " ),
+ " OR " );
+ break;
+ }
+ }
+
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx, "c", /* ( */ ')' );
+
+ return 1;
+}
+
+static int
+backsql_process_sub_filter( backsql_srch_info *bsi, Filter *f,
+ backsql_at_map_rec *at )
+{
+ backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
+ int i;
+ int casefold = 0;
+ int escaped = 0;
+ struct berval escval, *fvalue;
+
+ if ( !f ) {
+ return 0;
+ }
+
+ /* always uppercase strings by now */
+#ifdef BACKSQL_UPPERCASE_FILTER
+ if ( f->f_sub_desc->ad_type->sat_substr &&
+ SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
+ bi->sql_caseIgnoreMatch ) )
+#endif /* BACKSQL_UPPERCASE_FILTER */
+ {
+ casefold = 1;
+ }
+
+ if ( f->f_sub_desc->ad_type->sat_substr &&
+ SLAP_MR_ASSOCIATED( f->f_sub_desc->ad_type->sat_substr,
+ bi->sql_telephoneNumberMatch ) )
+ {
+
+ struct berval bv;
+ ber_len_t i, s, a;
+
+ /*
+ * to check for matching telephone numbers
+ * with intermixed chars, e.g. val='1234'
+ * use
+ *
+ * val LIKE '%1%2%3%4%'
+ */
+
+ BER_BVZERO( &bv );
+ if ( f->f_sub_initial.bv_val ) {
+ bv.bv_len += f->f_sub_initial.bv_len + backsql_val_escape( NULL, &f->f_sub_initial, NULL );
+ }
+ if ( f->f_sub_any != NULL ) {
+ for ( a = 0; f->f_sub_any[ a ].bv_val != NULL; a++ ) {
+ bv.bv_len += f->f_sub_any[ a ].bv_len + backsql_val_escape( NULL, &f->f_sub_any[ a ], NULL );
+ }
+ }
+ if ( f->f_sub_final.bv_val ) {
+ bv.bv_len += f->f_sub_final.bv_len + backsql_val_escape( NULL, &f->f_sub_final, NULL );
+ }
+ bv.bv_len = 2 * bv.bv_len - 1;
+ bv.bv_val = ch_malloc( bv.bv_len + 1 );
+
+ s = 0;
+ if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
+ fvalue = &f->f_sub_initial;
+ escaped = backsql_val_escape( bsi->bsi_op, fvalue, &escval );
+ if ( escaped )
+ fvalue = &escval;
+ bv.bv_val[ s ] = fvalue->bv_val[ 0 ];
+ for ( i = 1; i < fvalue->bv_len; i++ ) {
+ bv.bv_val[ s + 2 * i - 1 ] = '%';
+ bv.bv_val[ s + 2 * i ] = fvalue->bv_val[ i ];
+ }
+ bv.bv_val[ s + 2 * i - 1 ] = '%';
+ s += 2 * i;
+ if ( escaped )
+ bsi->bsi_op->o_tmpfree( escval.bv_val, bsi->bsi_op->o_tmpmemctx );
+ }
+
+ if ( f->f_sub_any != NULL ) {
+ for ( a = 0; !BER_BVISNULL( &f->f_sub_any[ a ] ); a++ ) {
+ fvalue = &f->f_sub_any[ a ];
+ escaped = backsql_val_escape( bsi->bsi_op, fvalue, &escval );
+ if ( escaped )
+ fvalue = &escval;
+ bv.bv_val[ s ] = fvalue->bv_val[ 0 ];
+ for ( i = 1; i < fvalue->bv_len; i++ ) {
+ bv.bv_val[ s + 2 * i - 1 ] = '%';
+ bv.bv_val[ s + 2 * i ] = fvalue->bv_val[ i ];
+ }
+ bv.bv_val[ s + 2 * i - 1 ] = '%';
+ s += 2 * i;
+ if ( escaped )
+ bsi->bsi_op->o_tmpfree( escval.bv_val, bsi->bsi_op->o_tmpmemctx );
+ }
+ }
+
+ if ( !BER_BVISNULL( &f->f_sub_final ) ) {
+ fvalue = &f->f_sub_final;
+ escaped = backsql_val_escape( bsi->bsi_op, fvalue, &escval );
+ if ( escaped )
+ fvalue = &escval;
+ bv.bv_val[ s ] = fvalue->bv_val[ 0 ];
+ for ( i = 1; i < fvalue->bv_len; i++ ) {
+ bv.bv_val[ s + 2 * i - 1 ] = '%';
+ bv.bv_val[ s + 2 * i ] = fvalue->bv_val[ i ];
+ }
+ bv.bv_val[ s + 2 * i - 1 ] = '%';
+ s += 2 * i;
+ if ( escaped )
+ bsi->bsi_op->o_tmpfree( escval.bv_val, bsi->bsi_op->o_tmpmemctx );
+ }
+
+ bv.bv_val[ s - 1 ] = '\0';
+
+ (void)backsql_process_filter_like( bsi, at, casefold, &bv );
+ ch_free( bv.bv_val );
+
+ return 1;
+ }
+
+ /*
+ * When dealing with case-sensitive strings
+ * we may omit normalization; however, normalized
+ * SQL filters are more liberal.
+ */
+
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx, "c", '(' /* ) */ );
+
+ /* TimesTen */
+ Debug( LDAP_DEBUG_TRACE, "backsql_process_sub_filter(%s):\n",
+ at->bam_ad->ad_cname.bv_val );
+ Debug(LDAP_DEBUG_TRACE, " expr: '%s%s%s'\n", at->bam_sel_expr.bv_val,
+ at->bam_sel_expr_u.bv_val ? "' '" : "",
+ at->bam_sel_expr_u.bv_val ? at->bam_sel_expr_u.bv_val : "" );
+ if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
+ /*
+ * If a pre-upper-cased version of the column
+ * or a precompiled upper function exists, use it
+ */
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "bl",
+ &at->bam_sel_expr_u,
+ (ber_len_t)STRLENOF( " LIKE '" ),
+ " LIKE '" );
+
+ } else {
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "bl",
+ &at->bam_sel_expr,
+ (ber_len_t)STRLENOF( " LIKE '" ), " LIKE '" );
+ }
+
+ if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
+ ber_len_t start;
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE,
+ "==>backsql_process_sub_filter(%s): "
+ "sub_initial=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
+ f->f_sub_initial.bv_val );
+#endif /* BACKSQL_TRACE */
+
+ fvalue = &f->f_sub_initial;
+ escaped = backsql_val_escape( bsi->bsi_op, fvalue, &escval );
+ if ( escaped )
+ fvalue = &escval;
+ start = bsi->bsi_flt_where.bb_val.bv_len;
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "b",
+ fvalue );
+ if ( escaped )
+ bsi->bsi_op->o_tmpfree( escval.bv_val, bsi->bsi_op->o_tmpmemctx );
+ if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
+ ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
+ }
+ }
+
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "c", '%' );
+
+ if ( f->f_sub_any != NULL ) {
+ for ( i = 0; !BER_BVISNULL( &f->f_sub_any[ i ] ); i++ ) {
+ ber_len_t start;
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE,
+ "==>backsql_process_sub_filter(%s): "
+ "sub_any[%d]=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
+ i, f->f_sub_any[ i ].bv_val );
+#endif /* BACKSQL_TRACE */
+
+ fvalue = &f->f_sub_any[ i ];
+ escaped = backsql_val_escape( bsi->bsi_op, fvalue, &escval );
+ if ( escaped )
+ fvalue = &escval;
+ start = bsi->bsi_flt_where.bb_val.bv_len;
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "bc",
+ fvalue,
+ '%' );
+ if ( escaped )
+ bsi->bsi_op->o_tmpfree( escval.bv_val, bsi->bsi_op->o_tmpmemctx );
+ if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
+ /*
+ * Note: toupper('%') = '%'
+ */
+ ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
+ }
+ }
+ }
+
+ if ( !BER_BVISNULL( &f->f_sub_final ) ) {
+ ber_len_t start;
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE,
+ "==>backsql_process_sub_filter(%s): "
+ "sub_final=\"%s\"\n", at->bam_ad->ad_cname.bv_val,
+ f->f_sub_final.bv_val );
+#endif /* BACKSQL_TRACE */
+
+ fvalue = &f->f_sub_final;
+ escaped = backsql_val_escape( bsi->bsi_op, fvalue, &escval );
+ if ( escaped )
+ fvalue = &escval;
+ start = bsi->bsi_flt_where.bb_val.bv_len;
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "b",
+ fvalue );
+ if ( escaped )
+ bsi->bsi_op->o_tmpfree( escval.bv_val, bsi->bsi_op->o_tmpmemctx );
+ if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
+ ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
+ }
+ }
+
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ (ber_len_t)STRLENOF( /* (' */ "')" ), /* (' */ "')" );
+
+ return 1;
+}
+
+static int
+backsql_merge_from_tbls( backsql_srch_info *bsi, struct berval *from_tbls )
+{
+ if ( BER_BVISNULL( from_tbls ) ) {
+ return LDAP_SUCCESS;
+ }
+
+ if ( !BER_BVISNULL( &bsi->bsi_from.bb_val ) ) {
+ char *start, *end;
+ struct berval tmp;
+
+ ber_dupbv_x( &tmp, from_tbls, bsi->bsi_op->o_tmpmemctx );
+
+ for ( start = tmp.bv_val, end = strchr( start, ',' ); start; ) {
+ if ( end ) {
+ end[0] = '\0';
+ }
+
+ if ( strstr( bsi->bsi_from.bb_val.bv_val, start) == NULL )
+ {
+ backsql_strfcat_x( &bsi->bsi_from,
+ bsi->bsi_op->o_tmpmemctx,
+ "cs", ',', start );
+ }
+
+ if ( end ) {
+ /* in case there are spaces after the comma... */
+ for ( start = &end[1]; isspace( start[0] ); start++ );
+ if ( start[0] ) {
+ end = strchr( start, ',' );
+ } else {
+ start = NULL;
+ }
+ } else {
+ start = NULL;
+ }
+ }
+
+ bsi->bsi_op->o_tmpfree( tmp.bv_val, bsi->bsi_op->o_tmpmemctx );
+
+ } else {
+ backsql_strfcat_x( &bsi->bsi_from,
+ bsi->bsi_op->o_tmpmemctx,
+ "b", from_tbls );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+backsql_process_filter( backsql_srch_info *bsi, Filter *f )
+{
+ backsql_at_map_rec **vat = NULL;
+ AttributeDescription *ad = NULL;
+ unsigned i;
+ int done = 0;
+ int rc = 0;
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter()\n" );
+ if ( f->f_choice == SLAPD_FILTER_COMPUTED ) {
+ struct berval flt;
+ char *msg = NULL;
+
+ switch ( f->f_result ) {
+ case LDAP_COMPARE_TRUE:
+ BER_BVSTR( &flt, "10=10" );
+ msg = "TRUE";
+ break;
+
+ case LDAP_COMPARE_FALSE:
+ BER_BVSTR( &flt, "11=0" );
+ msg = "FALSE";
+ break;
+
+ case SLAPD_COMPARE_UNDEFINED:
+ BER_BVSTR( &flt, "12=0" );
+ msg = "UNDEFINED";
+ break;
+
+ default:
+ rc = -1;
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "backsql_process_filter(): "
+ "filter computed (%s)\n", msg );
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx, "b", &flt );
+ rc = 1;
+ goto done;
+ }
+
+ if ( f->f_choice & SLAPD_FILTER_UNDEFINED ) {
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ (ber_len_t)STRLENOF( "1=0" ), "1=0" );
+ done = 1;
+ rc = 1;
+ goto done;
+ }
+
+ switch( f->f_choice ) {
+ case LDAP_FILTER_OR:
+ rc = backsql_process_filter_list( bsi, f->f_or,
+ LDAP_FILTER_OR );
+ done = 1;
+ break;
+
+ case LDAP_FILTER_AND:
+ rc = backsql_process_filter_list( bsi, f->f_and,
+ LDAP_FILTER_AND );
+ done = 1;
+ break;
+
+ case LDAP_FILTER_NOT:
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ (ber_len_t)STRLENOF( "NOT (" /* ) */ ),
+ "NOT (" /* ) */ );
+ rc = backsql_process_filter( bsi, f->f_not );
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "c", /* ( */ ')' );
+ done = 1;
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ ad = f->f_desc;
+ break;
+
+ case LDAP_FILTER_EXT:
+ ad = f->f_mra->ma_desc;
+ if ( f->f_mr_dnattrs ) {
+ /*
+ * if dn attrs filtering is requested, better return
+ * success and let test_filter() deal with candidate
+ * selection; otherwise we'd need to set conditions
+ * on the contents of the DN, e.g. "SELECT ... FROM
+ * ldap_entries AS attributeName WHERE attributeName.dn
+ * like '%attributeName=value%'"
+ */
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ (ber_len_t)STRLENOF( "1=1" ), "1=1" );
+ bsi->bsi_status = LDAP_SUCCESS;
+ rc = 1;
+ goto done;
+ }
+ break;
+
+ default:
+ ad = f->f_av_desc;
+ break;
+ }
+
+ if ( rc == -1 ) {
+ goto done;
+ }
+
+ if ( done ) {
+ rc = 1;
+ goto done;
+ }
+
+ /*
+ * Turn structuralObjectClass into objectClass
+ */
+ if ( ad == slap_schema.si_ad_objectClass
+ || ad == slap_schema.si_ad_structuralObjectClass )
+ {
+ /*
+ * If the filter is LDAP_FILTER_PRESENT, then it's done;
+ * otherwise, let's see if we are lucky: filtering
+ * for "structural" objectclass or ancestor...
+ */
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_EQUALITY:
+ {
+ ObjectClass *oc = oc_bvfind( &f->f_av_value );
+
+ if ( oc == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "backsql_process_filter(): "
+ "unknown objectClass \"%s\" "
+ "in filter\n",
+ f->f_av_value.bv_val );
+ bsi->bsi_status = LDAP_OTHER;
+ rc = -1;
+ goto done;
+ }
+
+ /*
+ * "structural" objectClass inheritance:
+ * - a search for "person" will also return
+ * "inetOrgPerson"
+ * - a search for "top" will return everything
+ */
+ if ( is_object_subclass( oc, bsi->bsi_oc->bom_oc ) ) {
+ static struct berval ldap_entry_objclasses = BER_BVC( "ldap_entry_objclasses" );
+
+ backsql_merge_from_tbls( bsi, &ldap_entry_objclasses );
+
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "lbl",
+ (ber_len_t)STRLENOF( "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */ ),
+ "(2=2 OR (ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ')) */,
+ &bsi->bsi_oc->bom_oc->soc_cname,
+ (ber_len_t)STRLENOF( /* ((' */ "'))" ),
+ /* ((' */ "'))" );
+ bsi->bsi_status = LDAP_SUCCESS;
+ rc = 1;
+ goto done;
+ }
+
+ break;
+ }
+
+ case LDAP_FILTER_PRESENT:
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ (ber_len_t)STRLENOF( "3=3" ), "3=3" );
+ bsi->bsi_status = LDAP_SUCCESS;
+ rc = 1;
+ goto done;
+
+ /* FIXME: LDAP_FILTER_EXT? */
+
+ default:
+ Debug( LDAP_DEBUG_TRACE,
+ "backsql_process_filter(): "
+ "illegal/unhandled filter "
+ "on objectClass attribute" );
+ bsi->bsi_status = LDAP_OTHER;
+ rc = -1;
+ goto done;
+ }
+
+ } else if ( ad == slap_schema.si_ad_entryUUID ) {
+ unsigned long oc_id;
+#ifdef BACKSQL_ARBITRARY_KEY
+ struct berval keyval;
+#else /* ! BACKSQL_ARBITRARY_KEY */
+ unsigned long keyval;
+ char keyvalbuf[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_EQUALITY:
+ backsql_entryUUID_decode( &f->f_av_value, &oc_id, &keyval );
+
+ if ( oc_id != bsi->bsi_oc->bom_id ) {
+ bsi->bsi_status = LDAP_SUCCESS;
+ rc = -1;
+ goto done;
+ }
+
+#ifdef BACKSQL_ARBITRARY_KEY
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "bcblbc",
+ &bsi->bsi_oc->bom_keytbl, '.',
+ &bsi->bsi_oc->bom_keycol,
+ STRLENOF( " LIKE '" ), " LIKE '",
+ &keyval, '\'' );
+#else /* ! BACKSQL_ARBITRARY_KEY */
+ snprintf( keyvalbuf, sizeof( keyvalbuf ), "%lu", keyval );
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "bcbcs",
+ &bsi->bsi_oc->bom_keytbl, '.',
+ &bsi->bsi_oc->bom_keycol, '=', keyvalbuf );
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ (ber_len_t)STRLENOF( "4=4" ), "4=4" );
+ break;
+
+ default:
+ rc = -1;
+ goto done;
+ }
+
+ bsi->bsi_flags |= BSQL_SF_FILTER_ENTRYUUID;
+ rc = 1;
+ goto done;
+
+#ifdef BACKSQL_SYNCPROV
+ } else if ( ad == slap_schema.si_ad_entryCSN ) {
+ /*
+ * support for syncrepl as provider...
+ */
+#if 0
+ if ( !bsi->bsi_op->o_sync ) {
+ /* unsupported at present... */
+ bsi->bsi_status = LDAP_OTHER;
+ rc = -1;
+ goto done;
+ }
+#endif
+
+ bsi->bsi_flags |= ( BSQL_SF_FILTER_ENTRYCSN | BSQL_SF_RETURN_ENTRYUUID);
+
+ /* if doing a syncrepl, try to return as much as possible,
+ * and always match the filter */
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ (ber_len_t)STRLENOF( "5=5" ), "5=5" );
+
+ /* save for later use in operational attributes */
+ /* FIXME: saves only the first occurrence, because
+ * the filter during updates is written as
+ * "(&(entryCSN<={contextCSN})(entryCSN>={oldContextCSN})({filter}))"
+ * so we want our fake entryCSN to match the greatest
+ * value
+ */
+ if ( bsi->bsi_op->o_private == NULL ) {
+ bsi->bsi_op->o_private = &f->f_av_value;
+ }
+ bsi->bsi_status = LDAP_SUCCESS;
+
+ rc = 1;
+ goto done;
+#endif /* BACKSQL_SYNCPROV */
+
+ } else if ( ad == slap_schema.si_ad_hasSubordinates || ad == NULL ) {
+ /*
+ * FIXME: this is not robust; e.g. a filter
+ * '(!(hasSubordinates=TRUE))' fails because
+ * in SQL it would read 'NOT (1=1)' instead
+ * of no condition.
+ * Note however that hasSubordinates is boolean,
+ * so a more appropriate filter would be
+ * '(hasSubordinates=FALSE)'
+ *
+ * A more robust search for hasSubordinates
+ * would * require joining the ldap_entries table
+ * selecting if there are descendants of the
+ * candidate.
+ */
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ (ber_len_t)STRLENOF( "6=6" ), "6=6" );
+ if ( ad == slap_schema.si_ad_hasSubordinates ) {
+ /*
+ * instruct candidate selection algorithm
+ * and attribute list to try to detect
+ * if an entry has subordinates
+ */
+ bsi->bsi_flags |= BSQL_SF_FILTER_HASSUBORDINATE;
+
+ } else {
+ /*
+ * clear attributes to fetch, to require ALL
+ * and try extended match on all attributes
+ */
+ backsql_attrlist_add( bsi, NULL );
+ }
+ rc = 1;
+ goto done;
+ }
+
+ /*
+ * attribute inheritance:
+ */
+ if ( backsql_supad2at( bsi->bsi_oc, ad, &vat ) ) {
+ bsi->bsi_status = LDAP_OTHER;
+ rc = -1;
+ goto done;
+ }
+
+ if ( vat == NULL ) {
+ /* search anyway; other parts of the filter
+ * may succeed */
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ (ber_len_t)STRLENOF( "7=7" ), "7=7" );
+ bsi->bsi_status = LDAP_SUCCESS;
+ rc = 1;
+ goto done;
+ }
+
+ /* if required, open extra level of parens */
+ done = 0;
+ if ( vat[0]->bam_next || vat[1] ) {
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "c", '(' );
+ done = 1;
+ }
+
+ i = 0;
+next:;
+ /* apply attr */
+ if ( backsql_process_filter_attr( bsi, f, vat[i] ) == -1 ) {
+ return -1;
+ }
+
+ /* if more definitions of the same attr, apply */
+ if ( vat[i]->bam_next ) {
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ STRLENOF( " OR " ), " OR " );
+ vat[i] = vat[i]->bam_next;
+ goto next;
+ }
+
+ /* if more descendants of the same attr, apply */
+ i++;
+ if ( vat[i] ) {
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ STRLENOF( " OR " ), " OR " );
+ goto next;
+ }
+
+ /* if needed, close extra level of parens */
+ if ( done ) {
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "c", ')' );
+ }
+
+ rc = 1;
+
+done:;
+ if ( vat ) {
+ ch_free( vat );
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<==backsql_process_filter() %s\n",
+ rc == 1 ? "succeeded" : "failed" );
+
+ return rc;
+}
+
+static int
+backsql_process_filter_eq( backsql_srch_info *bsi, backsql_at_map_rec *at,
+ int casefold, struct berval *filter_value )
+{
+ /*
+ * maybe we should check type of at->sel_expr here somehow,
+ * to know whether upper_func is applicable, but for now
+ * upper_func stuff is made for Oracle, where UPPER is
+ * safely applicable to NUMBER etc.
+ */
+ if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
+ ber_len_t start;
+
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "cbl",
+ '(', /* ) */
+ &at->bam_sel_expr_u,
+ (ber_len_t)STRLENOF( "='" ),
+ "='" );
+
+ start = bsi->bsi_flt_where.bb_val.bv_len;
+
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "bl",
+ filter_value,
+ (ber_len_t)STRLENOF( /* (' */ "')" ),
+ /* (' */ "')" );
+
+ ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
+
+ } else {
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "cblbl",
+ '(', /* ) */
+ &at->bam_sel_expr,
+ (ber_len_t)STRLENOF( "='" ), "='",
+ filter_value,
+ (ber_len_t)STRLENOF( /* (' */ "')" ),
+ /* (' */ "')" );
+ }
+
+ return 1;
+}
+
+static int
+backsql_process_filter_like( backsql_srch_info *bsi, backsql_at_map_rec *at,
+ int casefold, struct berval *filter_value )
+{
+ /*
+ * maybe we should check type of at->sel_expr here somehow,
+ * to know whether upper_func is applicable, but for now
+ * upper_func stuff is made for Oracle, where UPPER is
+ * safely applicable to NUMBER etc.
+ */
+ if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
+ ber_len_t start;
+
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "cbl",
+ '(', /* ) */
+ &at->bam_sel_expr_u,
+ (ber_len_t)STRLENOF( " LIKE '%" ),
+ " LIKE '%" );
+
+ start = bsi->bsi_flt_where.bb_val.bv_len;
+
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "bl",
+ filter_value,
+ (ber_len_t)STRLENOF( /* (' */ "%')" ),
+ /* (' */ "%')" );
+
+ ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
+
+ } else {
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "cblbl",
+ '(', /* ) */
+ &at->bam_sel_expr,
+ (ber_len_t)STRLENOF( " LIKE '%" ),
+ " LIKE '%",
+ filter_value,
+ (ber_len_t)STRLENOF( /* (' */ "%')" ),
+ /* (' */ "%')" );
+ }
+
+ return 1;
+}
+
+static int
+backsql_process_filter_attr( backsql_srch_info *bsi, Filter *f, backsql_at_map_rec *at )
+{
+ backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
+ int casefold = 0;
+ struct berval *filter_value = NULL;
+ MatchingRule *matching_rule = NULL;
+ struct berval ordering = BER_BVC("<=");
+ struct berval escval;
+ int escaped = 0;
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_process_filter_attr(%s)\n",
+ at->bam_ad->ad_cname.bv_val );
+
+ /*
+ * need to add this attribute to list of attrs to load,
+ * so that we can do test_filter() later
+ */
+ backsql_attrlist_add( bsi, at->bam_ad );
+
+ backsql_merge_from_tbls( bsi, &at->bam_from_tbls );
+
+ if ( !BER_BVISNULL( &at->bam_join_where )
+ && strstr( bsi->bsi_join_where.bb_val.bv_val,
+ at->bam_join_where.bv_val ) == NULL )
+ {
+ backsql_strfcat_x( &bsi->bsi_join_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "lb",
+ (ber_len_t)STRLENOF( " AND " ), " AND ",
+ &at->bam_join_where );
+ }
+
+ if ( f->f_choice & SLAPD_FILTER_UNDEFINED ) {
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ (ber_len_t)STRLENOF( "1=0" ), "1=0" );
+ return 1;
+ }
+
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_EQUALITY:
+ filter_value = &f->f_av_value;
+ matching_rule = at->bam_ad->ad_type->sat_equality;
+
+ goto equality_match;
+
+ /* fail over into next case */
+
+ case LDAP_FILTER_EXT:
+ filter_value = &f->f_mra->ma_value;
+ matching_rule = f->f_mr_rule;
+
+equality_match:;
+ /* always uppercase strings by now */
+#ifdef BACKSQL_UPPERCASE_FILTER
+ if ( SLAP_MR_ASSOCIATED( matching_rule,
+ bi->sql_caseIgnoreMatch ) )
+#endif /* BACKSQL_UPPERCASE_FILTER */
+ {
+ casefold = 1;
+ }
+
+ escaped = backsql_val_escape( bsi->bsi_op, filter_value, &escval );
+ if ( escaped )
+ filter_value = &escval;
+
+ /* FIXME: directoryString filtering should use a similar
+ * approach to deal with non-prettified values like
+ * " A non prettified value ", by using a LIKE
+ * filter with all whitespaces collapsed to a single '%' */
+ if ( SLAP_MR_ASSOCIATED( matching_rule,
+ bi->sql_telephoneNumberMatch ) )
+ {
+ struct berval bv;
+ ber_len_t i;
+
+ /*
+ * to check for matching telephone numbers
+ * with intermized chars, e.g. val='1234'
+ * use
+ *
+ * val LIKE '%1%2%3%4%'
+ */
+
+ bv.bv_len = 2 * filter_value->bv_len - 1;
+ bv.bv_val = ch_malloc( bv.bv_len + 1 );
+
+ bv.bv_val[ 0 ] = filter_value->bv_val[ 0 ];
+ for ( i = 1; i < filter_value->bv_len; i++ ) {
+ bv.bv_val[ 2 * i - 1 ] = '%';
+ bv.bv_val[ 2 * i ] = filter_value->bv_val[ i ];
+ }
+ bv.bv_val[ 2 * i - 1 ] = '\0';
+
+ (void)backsql_process_filter_like( bsi, at, casefold, &bv );
+ ch_free( bv.bv_val );
+
+ break;
+ }
+
+ /* NOTE: this is required by objectClass inheritance
+ * and auxiliary objectClass use in filters for slightly
+ * more efficient candidate selection. */
+ /* FIXME: a bit too many specializations to deal with
+ * very specific cases... */
+ if ( at->bam_ad == slap_schema.si_ad_objectClass
+ || at->bam_ad == slap_schema.si_ad_structuralObjectClass )
+ {
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "lbl",
+ (ber_len_t)STRLENOF( "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */ ),
+ "(ldap_entries.id=ldap_entry_objclasses.entry_id AND ldap_entry_objclasses.oc_name='" /* ') */,
+ filter_value,
+ (ber_len_t)STRLENOF( /* (' */ "')" ),
+ /* (' */ "')" );
+ break;
+ }
+
+ /*
+ * maybe we should check type of at->sel_expr here somehow,
+ * to know whether upper_func is applicable, but for now
+ * upper_func stuff is made for Oracle, where UPPER is
+ * safely applicable to NUMBER etc.
+ */
+ (void)backsql_process_filter_eq( bsi, at, casefold, filter_value );
+ break;
+
+ case LDAP_FILTER_GE:
+ ordering.bv_val = ">=";
+
+ /* fall thru to next case */
+
+ case LDAP_FILTER_LE:
+ filter_value = &f->f_av_value;
+
+ /* always uppercase strings by now */
+#ifdef BACKSQL_UPPERCASE_FILTER
+ if ( at->bam_ad->ad_type->sat_ordering &&
+ SLAP_MR_ASSOCIATED( at->bam_ad->ad_type->sat_ordering,
+ bi->sql_caseIgnoreMatch ) )
+#endif /* BACKSQL_UPPERCASE_FILTER */
+ {
+ casefold = 1;
+ }
+
+ escaped = backsql_val_escape( bsi->bsi_op, filter_value, &escval );
+ if ( escaped )
+ filter_value = &escval;
+
+ /*
+ * FIXME: should we uppercase the operands?
+ */
+ if ( casefold && BACKSQL_AT_CANUPPERCASE( at ) ) {
+ ber_len_t start;
+
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "cbbc",
+ '(', /* ) */
+ &at->bam_sel_expr_u,
+ &ordering,
+ '\'' );
+
+ start = bsi->bsi_flt_where.bb_val.bv_len;
+
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "bl",
+ filter_value,
+ (ber_len_t)STRLENOF( /* (' */ "')" ),
+ /* (' */ "')" );
+
+ ldap_pvt_str2upper( &bsi->bsi_flt_where.bb_val.bv_val[ start ] );
+
+ } else {
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "cbbcbl",
+ '(' /* ) */ ,
+ &at->bam_sel_expr,
+ &ordering,
+ '\'',
+ filter_value,
+ (ber_len_t)STRLENOF( /* (' */ "')" ),
+ /* ( */ "')" );
+ }
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "lbl",
+ (ber_len_t)STRLENOF( "NOT (" /* ) */),
+ "NOT (", /* ) */
+ &at->bam_sel_expr,
+ (ber_len_t)STRLENOF( /* ( */ " IS NULL)" ),
+ /* ( */ " IS NULL)" );
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ backsql_process_sub_filter( bsi, f, at );
+ break;
+
+ case LDAP_FILTER_APPROX:
+ /* we do our best */
+
+ filter_value = &f->f_av_value;
+ escaped = backsql_val_escape( bsi->bsi_op, filter_value, &escval );
+ if ( escaped )
+ filter_value = &escval;
+ /*
+ * maybe we should check type of at->sel_expr here somehow,
+ * to know whether upper_func is applicable, but for now
+ * upper_func stuff is made for Oracle, where UPPER is
+ * safely applicable to NUMBER etc.
+ */
+ (void)backsql_process_filter_like( bsi, at, 1, filter_value );
+ break;
+
+ default:
+ /* unhandled filter type; should not happen */
+ assert( 0 );
+ backsql_strfcat_x( &bsi->bsi_flt_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ (ber_len_t)STRLENOF( "8=8" ), "8=8" );
+ break;
+
+ }
+
+ if ( escaped )
+ bsi->bsi_op->o_tmpfree( escval.bv_val, bsi->bsi_op->o_tmpmemctx );
+
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_process_filter_attr(%s)\n",
+ at->bam_ad->ad_cname.bv_val );
+
+ return 1;
+}
+
+static int
+backsql_srch_query( backsql_srch_info *bsi, struct berval *query )
+{
+ backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
+ int rc;
+
+ assert( query != NULL );
+ BER_BVZERO( query );
+
+ bsi->bsi_use_subtree_shortcut = 0;
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_srch_query()\n" );
+ BER_BVZERO( &bsi->bsi_sel.bb_val );
+ BER_BVZERO( &bsi->bsi_sel.bb_val );
+ bsi->bsi_sel.bb_len = 0;
+ BER_BVZERO( &bsi->bsi_from.bb_val );
+ bsi->bsi_from.bb_len = 0;
+ BER_BVZERO( &bsi->bsi_join_where.bb_val );
+ bsi->bsi_join_where.bb_len = 0;
+ BER_BVZERO( &bsi->bsi_flt_where.bb_val );
+ bsi->bsi_flt_where.bb_len = 0;
+
+ backsql_strfcat_x( &bsi->bsi_sel,
+ bsi->bsi_op->o_tmpmemctx,
+ "lbcbc",
+ (ber_len_t)STRLENOF( "SELECT DISTINCT ldap_entries.id," ),
+ "SELECT DISTINCT ldap_entries.id,",
+ &bsi->bsi_oc->bom_keytbl,
+ '.',
+ &bsi->bsi_oc->bom_keycol,
+ ',' );
+
+ if ( !BER_BVISNULL( &bi->sql_strcast_func ) ) {
+ backsql_strfcat_x( &bsi->bsi_sel,
+ bsi->bsi_op->o_tmpmemctx,
+ "blbl",
+ &bi->sql_strcast_func,
+ (ber_len_t)STRLENOF( "('" /* ') */ ),
+ "('" /* ') */ ,
+ &bsi->bsi_oc->bom_oc->soc_cname,
+ (ber_len_t)STRLENOF( /* (' */ "')" ),
+ /* (' */ "')" );
+ } else {
+ backsql_strfcat_x( &bsi->bsi_sel,
+ bsi->bsi_op->o_tmpmemctx,
+ "cbc",
+ '\'',
+ &bsi->bsi_oc->bom_oc->soc_cname,
+ '\'' );
+ }
+
+ backsql_strfcat_x( &bsi->bsi_sel,
+ bsi->bsi_op->o_tmpmemctx,
+ "b",
+ &bi->sql_dn_oc_aliasing );
+ backsql_strfcat_x( &bsi->bsi_from,
+ bsi->bsi_op->o_tmpmemctx,
+ "lb",
+ (ber_len_t)STRLENOF( " FROM ldap_entries," ),
+ " FROM ldap_entries,",
+ &bsi->bsi_oc->bom_keytbl );
+
+ backsql_strfcat_x( &bsi->bsi_join_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "lbcbl",
+ (ber_len_t)STRLENOF( " WHERE " ), " WHERE ",
+ &bsi->bsi_oc->bom_keytbl,
+ '.',
+ &bsi->bsi_oc->bom_keycol,
+ (ber_len_t)STRLENOF( "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " ),
+ "=ldap_entries.keyval AND ldap_entries.oc_map_id=? AND " );
+
+ switch ( bsi->bsi_scope ) {
+ case LDAP_SCOPE_BASE:
+ if ( BACKSQL_CANUPPERCASE( bi ) ) {
+ backsql_strfcat_x( &bsi->bsi_join_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "bl",
+ &bi->sql_upper_func,
+ (ber_len_t)STRLENOF( "(ldap_entries.dn)=?" ),
+ "(ldap_entries.dn)=?" );
+ } else {
+ backsql_strfcat_x( &bsi->bsi_join_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ (ber_len_t)STRLENOF( "ldap_entries.dn=?" ),
+ "ldap_entries.dn=?" );
+ }
+ break;
+
+ case BACKSQL_SCOPE_BASE_LIKE:
+ if ( BACKSQL_CANUPPERCASE( bi ) ) {
+ backsql_strfcat_x( &bsi->bsi_join_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "bl",
+ &bi->sql_upper_func,
+ (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
+ "(ldap_entries.dn) LIKE ?" );
+ } else {
+ backsql_strfcat_x( &bsi->bsi_join_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
+ "ldap_entries.dn LIKE ?" );
+ }
+ break;
+
+ case LDAP_SCOPE_ONELEVEL:
+ backsql_strfcat_x( &bsi->bsi_join_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ (ber_len_t)STRLENOF( "ldap_entries.parent=?" ),
+ "ldap_entries.parent=?" );
+ break;
+
+ case LDAP_SCOPE_SUBORDINATE:
+ case LDAP_SCOPE_SUBTREE:
+ if ( BACKSQL_USE_SUBTREE_SHORTCUT( bi ) ) {
+ int i;
+ BackendDB *bd = bsi->bsi_op->o_bd;
+
+ assert( bd->be_nsuffix != NULL );
+
+ for ( i = 0; !BER_BVISNULL( &bd->be_nsuffix[ i ] ); i++ )
+ {
+ if ( dn_match( &bd->be_nsuffix[ i ],
+ bsi->bsi_base_ndn ) )
+ {
+ /* pass this to the candidate selection
+ * routine so that the DN is not bound
+ * to the select statement */
+ bsi->bsi_use_subtree_shortcut = 1;
+ break;
+ }
+ }
+ }
+
+ if ( bsi->bsi_use_subtree_shortcut ) {
+ /* Skip the base DN filter, as every entry will match it */
+ backsql_strfcat_x( &bsi->bsi_join_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ (ber_len_t)STRLENOF( "9=9"), "9=9");
+
+ } else if ( !BER_BVISNULL( &bi->sql_subtree_cond ) ) {
+ /* This should always be true... */
+ backsql_strfcat_x( &bsi->bsi_join_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "b",
+ &bi->sql_subtree_cond );
+
+ } else if ( BACKSQL_CANUPPERCASE( bi ) ) {
+ backsql_strfcat_x( &bsi->bsi_join_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "bl",
+ &bi->sql_upper_func,
+ (ber_len_t)STRLENOF( "(ldap_entries.dn) LIKE ?" ),
+ "(ldap_entries.dn) LIKE ?" );
+
+ } else {
+ backsql_strfcat_x( &bsi->bsi_join_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ (ber_len_t)STRLENOF( "ldap_entries.dn LIKE ?" ),
+ "ldap_entries.dn LIKE ?" );
+ }
+
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+#ifndef BACKSQL_ARBITRARY_KEY
+ /* If paged results are in effect, ignore low ldap_entries.id numbers */
+ if ( get_pagedresults(bsi->bsi_op) > SLAP_CONTROL_IGNORED ) {
+ unsigned long lowid = 0;
+
+ /* Pick up the previous ldap_entries.id if the previous page ended in this objectClass */
+ if ( bsi->bsi_oc->bom_id == PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie ) )
+ {
+ lowid = PAGECOOKIE_TO_SQL_ID( ((PagedResultsState *)bsi->bsi_op->o_pagedresults_state)->ps_cookie );
+ }
+
+ if ( lowid ) {
+ char lowidstring[48];
+ int lowidlen;
+
+ lowidlen = snprintf( lowidstring, sizeof( lowidstring ),
+ " AND ldap_entries.id>%lu", lowid );
+ backsql_strfcat_x( &bsi->bsi_join_where,
+ bsi->bsi_op->o_tmpmemctx,
+ "l",
+ (ber_len_t)lowidlen,
+ lowidstring );
+ }
+ }
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+
+ rc = backsql_process_filter( bsi, bsi->bsi_filter );
+ if ( rc > 0 ) {
+ struct berbuf bb = BB_NULL;
+
+ backsql_strfcat_x( &bb,
+ bsi->bsi_op->o_tmpmemctx,
+ "bbblb",
+ &bsi->bsi_sel.bb_val,
+ &bsi->bsi_from.bb_val,
+ &bsi->bsi_join_where.bb_val,
+ (ber_len_t)STRLENOF( " AND " ), " AND ",
+ &bsi->bsi_flt_where.bb_val );
+
+ *query = bb.bb_val;
+
+ } else if ( rc < 0 ) {
+ /*
+ * Indicates that there's no possible way the filter matches
+ * anything. No need to issue the query
+ */
+ free( query->bv_val );
+ BER_BVZERO( query );
+ }
+
+ bsi->bsi_op->o_tmpfree( bsi->bsi_sel.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
+ BER_BVZERO( &bsi->bsi_sel.bb_val );
+ bsi->bsi_sel.bb_len = 0;
+ bsi->bsi_op->o_tmpfree( bsi->bsi_from.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
+ BER_BVZERO( &bsi->bsi_from.bb_val );
+ bsi->bsi_from.bb_len = 0;
+ bsi->bsi_op->o_tmpfree( bsi->bsi_join_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
+ BER_BVZERO( &bsi->bsi_join_where.bb_val );
+ bsi->bsi_join_where.bb_len = 0;
+ bsi->bsi_op->o_tmpfree( bsi->bsi_flt_where.bb_val.bv_val, bsi->bsi_op->o_tmpmemctx );
+ BER_BVZERO( &bsi->bsi_flt_where.bb_val );
+ bsi->bsi_flt_where.bb_len = 0;
+
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_srch_query() returns %s\n",
+ query->bv_val ? query->bv_val : "NULL" );
+
+ return ( rc <= 0 ? 1 : 0 );
+}
+
+static int
+backsql_oc_get_candidates( void *v_oc, void *v_bsi )
+{
+ backsql_oc_map_rec *oc = v_oc;
+ backsql_srch_info *bsi = v_bsi;
+ Operation *op = bsi->bsi_op;
+ backsql_info *bi = (backsql_info *)bsi->bsi_op->o_bd->be_private;
+ struct berval query;
+ SQLHSTMT sth = SQL_NULL_HSTMT;
+ RETCODE rc;
+ int res;
+ BACKSQL_ROW_NTS row;
+ int i;
+ int j;
+ int n_candidates = bsi->bsi_n_candidates;
+
+ /*
+ * + 1 because we need room for '%';
+ * + 1 because we need room for ',' for LDAP_SCOPE_SUBORDINATE;
+ * this makes a subtree
+ * search for a DN BACKSQL_MAX_DN_LEN long legal
+ * if it returns that DN only
+ */
+ char tmp_base_ndn[ BACKSQL_MAX_DN_LEN + 1 + 1 ];
+
+ bsi->bsi_status = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_oc_get_candidates(): oc=\"%s\"\n",
+ BACKSQL_OC_NAME( oc ) );
+
+ /* check for abandon */
+ if ( op->o_abandon ) {
+ bsi->bsi_status = SLAPD_ABANDON;
+ return BACKSQL_AVL_STOP;
+ }
+
+#ifndef BACKSQL_ARBITRARY_KEY
+ /* If paged results have already completed this objectClass, skip it */
+ if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
+ if ( oc->bom_id < PAGECOOKIE_TO_SQL_OC( ((PagedResultsState *)op->o_pagedresults_state)->ps_cookie ) )
+ {
+ return BACKSQL_AVL_CONTINUE;
+ }
+ }
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+
+ if ( bsi->bsi_n_candidates == -1 ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
+ "unchecked limit has been overcome\n" );
+ /* should never get here */
+ assert( 0 );
+ bsi->bsi_status = LDAP_ADMINLIMIT_EXCEEDED;
+ return BACKSQL_AVL_STOP;
+ }
+
+ bsi->bsi_oc = oc;
+ res = backsql_srch_query( bsi, &query );
+ if ( res ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
+ "error while constructing query for objectclass \"%s\"\n",
+ oc->bom_oc->soc_cname.bv_val );
+ /*
+ * FIXME: need to separate errors from legally
+ * impossible filters
+ */
+ switch ( bsi->bsi_status ) {
+ case LDAP_SUCCESS:
+ case LDAP_UNDEFINED_TYPE:
+ case LDAP_NO_SUCH_OBJECT:
+ /* we are conservative... */
+ default:
+ bsi->bsi_status = LDAP_SUCCESS;
+ /* try next */
+ return BACKSQL_AVL_CONTINUE;
+
+ case LDAP_ADMINLIMIT_EXCEEDED:
+ case LDAP_OTHER:
+ /* don't try any more */
+ return BACKSQL_AVL_STOP;
+ }
+ }
+
+ if ( BER_BVISNULL( &query ) ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
+ "could not construct query for objectclass \"%s\"\n",
+ oc->bom_oc->soc_cname.bv_val );
+ bsi->bsi_status = LDAP_SUCCESS;
+ return BACKSQL_AVL_CONTINUE;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "Constructed query: %s\n",
+ query.bv_val );
+
+ rc = backsql_Prepare( bsi->bsi_dbh, &sth, query.bv_val, 0 );
+ bsi->bsi_op->o_tmpfree( query.bv_val, bsi->bsi_op->o_tmpmemctx );
+ BER_BVZERO( &query );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
+ "error preparing query\n" );
+ backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
+ bsi->bsi_status = LDAP_OTHER;
+ return BACKSQL_AVL_CONTINUE;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "id: '" BACKSQL_IDNUMFMT "'\n",
+ bsi->bsi_oc->bom_id );
+
+ rc = backsql_BindParamNumID( sth, 1, SQL_PARAM_INPUT,
+ &bsi->bsi_oc->bom_id );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
+ "error binding objectclass id parameter\n" );
+ bsi->bsi_status = LDAP_OTHER;
+ return BACKSQL_AVL_CONTINUE;
+ }
+
+ switch ( bsi->bsi_scope ) {
+ case LDAP_SCOPE_BASE:
+ case BACKSQL_SCOPE_BASE_LIKE:
+ /*
+ * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
+ * however this should be handled earlier
+ */
+ if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
+ bsi->bsi_status = LDAP_OTHER;
+ return BACKSQL_AVL_CONTINUE;
+ }
+
+ AC_MEMCPY( tmp_base_ndn, bsi->bsi_base_ndn->bv_val,
+ bsi->bsi_base_ndn->bv_len + 1 );
+
+ /* uppercase DN only if the stored DN can be uppercased
+ * for comparison */
+ if ( BACKSQL_CANUPPERCASE( bi ) ) {
+ ldap_pvt_str2upper( tmp_base_ndn );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "(base)dn: \"%s\"\n",
+ tmp_base_ndn );
+
+ rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
+ tmp_base_ndn, BACKSQL_MAX_DN_LEN );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
+ "error binding base_ndn parameter\n" );
+ backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh,
+ sth, rc );
+ bsi->bsi_status = LDAP_OTHER;
+ return BACKSQL_AVL_CONTINUE;
+ }
+ break;
+
+ case LDAP_SCOPE_SUBORDINATE:
+ case LDAP_SCOPE_SUBTREE:
+ {
+ /* if short-cutting the search base,
+ * don't bind any parameter */
+ if ( bsi->bsi_use_subtree_shortcut ) {
+ break;
+ }
+
+ /*
+ * We do not accept DNs longer than BACKSQL_MAX_DN_LEN;
+ * however this should be handled earlier
+ */
+ if ( bsi->bsi_base_ndn->bv_len > BACKSQL_MAX_DN_LEN ) {
+ bsi->bsi_status = LDAP_OTHER;
+ return BACKSQL_AVL_CONTINUE;
+ }
+
+ /*
+ * Sets the parameters for the SQL built earlier
+ * NOTE that all the databases could actually use
+ * the TimesTen version, which would be cleaner
+ * and would also eliminate the need for the
+ * subtree_cond line in the configuration file.
+ * For now, I'm leaving it the way it is,
+ * so non-TimesTen databases use the original code.
+ * But at some point this should get cleaned up.
+ *
+ * If "dn" is being used, do a suffix search.
+ * If "dn_ru" is being used, do a prefix search.
+ */
+ if ( BACKSQL_HAS_LDAPINFO_DN_RU( bi ) ) {
+ tmp_base_ndn[ 0 ] = '\0';
+
+ for ( i = 0, j = bsi->bsi_base_ndn->bv_len - 1;
+ j >= 0; i++, j--) {
+ tmp_base_ndn[ i ] = bsi->bsi_base_ndn->bv_val[ j ];
+ }
+
+ if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
+ tmp_base_ndn[ i++ ] = ',';
+ }
+
+ tmp_base_ndn[ i ] = '%';
+ tmp_base_ndn[ i + 1 ] = '\0';
+
+ } else {
+ i = 0;
+
+ tmp_base_ndn[ i++ ] = '%';
+
+ if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
+ tmp_base_ndn[ i++ ] = ',';
+ }
+
+ AC_MEMCPY( &tmp_base_ndn[ i ], bsi->bsi_base_ndn->bv_val,
+ bsi->bsi_base_ndn->bv_len + 1 );
+ }
+
+ /* uppercase DN only if the stored DN can be uppercased
+ * for comparison */
+ if ( BACKSQL_CANUPPERCASE( bi ) ) {
+ ldap_pvt_str2upper( tmp_base_ndn );
+ }
+
+ if ( bsi->bsi_scope == LDAP_SCOPE_SUBORDINATE ) {
+ Debug( LDAP_DEBUG_TRACE, "(children)dn: \"%s\"\n",
+ tmp_base_ndn );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "(sub)dn: \"%s\"\n",
+ tmp_base_ndn );
+ }
+
+ rc = backsql_BindParamStr( sth, 2, SQL_PARAM_INPUT,
+ tmp_base_ndn, BACKSQL_MAX_DN_LEN );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
+ "error binding base_ndn parameter (2)\n" );
+ backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh,
+ sth, rc );
+ bsi->bsi_status = LDAP_OTHER;
+ return BACKSQL_AVL_CONTINUE;
+ }
+ break;
+ }
+
+ case LDAP_SCOPE_ONELEVEL:
+ assert( !BER_BVISNULL( &bsi->bsi_base_id.eid_ndn ) );
+
+ Debug( LDAP_DEBUG_TRACE, "(one)id=" BACKSQL_IDFMT "\n",
+ BACKSQL_IDARG(bsi->bsi_base_id.eid_id) );
+ rc = backsql_BindParamID( sth, 2, SQL_PARAM_INPUT,
+ &bsi->bsi_base_id.eid_id );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
+ "error binding base id parameter\n" );
+ bsi->bsi_status = LDAP_OTHER;
+ return BACKSQL_AVL_CONTINUE;
+ }
+ break;
+ }
+
+ rc = SQLExecute( sth );
+ if ( !BACKSQL_SUCCESS( rc ) ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
+ "error executing query\n" );
+ backsql_PrintErrors( bi->sql_db_env, bsi->bsi_dbh, sth, rc );
+ SQLFreeStmt( sth, SQL_DROP );
+ bsi->bsi_status = LDAP_OTHER;
+ return BACKSQL_AVL_CONTINUE;
+ }
+
+ backsql_BindRowAsStrings_x( sth, &row, bsi->bsi_op->o_tmpmemctx );
+ rc = SQLFetch( sth );
+ for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( sth ) ) {
+ struct berval dn, pdn, ndn;
+ backsql_entryID *c_id = NULL;
+ int ret;
+
+ ber_str2bv( row.cols[ 3 ], 0, 0, &dn );
+
+ if ( backsql_api_odbc2dn( bsi->bsi_op, bsi->bsi_rs, &dn ) ) {
+ continue;
+ }
+
+ ret = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
+ if ( dn.bv_val != row.cols[ 3 ] ) {
+ free( dn.bv_val );
+ }
+
+ if ( ret != LDAP_SUCCESS ) {
+ continue;
+ }
+
+ if ( bi->sql_baseObject && dn_match( &ndn, &bi->sql_baseObject->e_nname ) ) {
+ goto cleanup;
+ }
+
+ c_id = (backsql_entryID *)op->o_tmpcalloc( 1,
+ sizeof( backsql_entryID ), op->o_tmpmemctx );
+#ifdef BACKSQL_ARBITRARY_KEY
+ ber_str2bv_x( row.cols[ 0 ], 0, 1, &c_id->eid_id,
+ op->o_tmpmemctx );
+ ber_str2bv_x( row.cols[ 1 ], 0, 1, &c_id->eid_keyval,
+ op->o_tmpmemctx );
+#else /* ! BACKSQL_ARBITRARY_KEY */
+ if ( BACKSQL_STR2ID( &c_id->eid_id, row.cols[ 0 ], 0 ) != 0 ) {
+ goto cleanup;
+ }
+ if ( BACKSQL_STR2ID( &c_id->eid_keyval, row.cols[ 1 ], 0 ) != 0 ) {
+ goto cleanup;
+ }
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+ c_id->eid_oc = bsi->bsi_oc;
+ c_id->eid_oc_id = bsi->bsi_oc->bom_id;
+
+ c_id->eid_dn = pdn;
+ c_id->eid_ndn = ndn;
+
+ /* append at end of list ... */
+ c_id->eid_next = NULL;
+ *bsi->bsi_id_listtail = c_id;
+ bsi->bsi_id_listtail = &c_id->eid_next;
+
+ Debug( LDAP_DEBUG_TRACE, "backsql_oc_get_candidates(): "
+ "added entry id=" BACKSQL_IDFMT " keyval=" BACKSQL_IDFMT " dn=\"%s\"\n",
+ BACKSQL_IDARG(c_id->eid_id),
+ BACKSQL_IDARG(c_id->eid_keyval),
+ row.cols[ 3 ] );
+
+ /* count candidates, for unchecked limit */
+ bsi->bsi_n_candidates--;
+ if ( bsi->bsi_n_candidates == -1 ) {
+ break;
+ }
+ continue;
+
+cleanup:;
+ if ( !BER_BVISNULL( &pdn ) ) {
+ op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
+ }
+ if ( !BER_BVISNULL( &ndn ) ) {
+ op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
+ }
+ if ( c_id != NULL ) {
+ ch_free( c_id );
+ }
+ }
+ backsql_FreeRow_x( &row, bsi->bsi_op->o_tmpmemctx );
+ SQLFreeStmt( sth, SQL_DROP );
+
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_oc_get_candidates(): %d\n",
+ n_candidates - bsi->bsi_n_candidates );
+
+ return ( bsi->bsi_n_candidates == -1 ? BACKSQL_AVL_STOP : BACKSQL_AVL_CONTINUE );
+}
+
+int
+backsql_search( Operation *op, SlapReply *rs )
+{
+ backsql_info *bi = (backsql_info *)op->o_bd->be_private;
+ SQLHDBC dbh = SQL_NULL_HDBC;
+ int sres;
+ Entry user_entry = { 0 },
+ base_entry = { 0 };
+ int manageDSAit = get_manageDSAit( op );
+ time_t stoptime = 0;
+ backsql_srch_info bsi = { 0 };
+ backsql_entryID *eid = NULL;
+ struct berval nbase = BER_BVNULL;
+#ifndef BACKSQL_ARBITRARY_KEY
+ ID lastid = 0;
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_search(): "
+ "base=\"%s\", filter=\"%s\", scope=%d,",
+ op->o_req_ndn.bv_val,
+ op->ors_filterstr.bv_val,
+ op->ors_scope );
+ Debug( LDAP_DEBUG_TRACE, " deref=%d, attrsonly=%d, "
+ "attributes to load: %s\n",
+ op->ors_deref,
+ op->ors_attrsonly,
+ op->ors_attrs == NULL ? "all" : "custom list" );
+
+ if ( op->o_req_ndn.bv_len > BACKSQL_MAX_DN_LEN ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
+ "search base length (%ld) exceeds max length (%d)\n",
+ op->o_req_ndn.bv_len, BACKSQL_MAX_DN_LEN );
+ /*
+ * FIXME: a LDAP_NO_SUCH_OBJECT could be appropriate
+ * since it is impossible that such a long DN exists
+ * in the backend
+ */
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ send_ldap_result( op, rs );
+ return 1;
+ }
+
+ sres = backsql_get_db_conn( op, &dbh );
+ if ( sres != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
+ "could not get connection handle - exiting\n" );
+ rs->sr_err = sres;
+ rs->sr_text = sres == LDAP_OTHER ? "SQL-backend error" : NULL;
+ send_ldap_result( op, rs );
+ return 1;
+ }
+
+ /* compute it anyway; root does not use it */
+ stoptime = op->o_time + op->ors_tlimit;
+
+ /* init search */
+ bsi.bsi_e = &base_entry;
+ rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn,
+ op->ors_scope,
+ stoptime, op->ors_filter,
+ dbh, op, rs, op->ors_attrs,
+ ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) );
+ switch ( rs->sr_err ) {
+ case LDAP_SUCCESS:
+ break;
+
+ case LDAP_REFERRAL:
+ if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
+ dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) )
+ {
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ rs->sr_matched = NULL;
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+ break;
+ }
+
+ /* an entry was created; free it */
+ entry_clean( bsi.bsi_e );
+
+ /* fall thru */
+
+ default:
+ if ( !BER_BVISNULL( &base_entry.e_nname )
+ && !access_allowed( op, &base_entry,
+ slap_schema.si_ad_entry, NULL,
+ ACL_DISCLOSE, NULL ) )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+ rs->sr_matched = NULL;
+ rs->sr_text = NULL;
+ }
+
+ send_ldap_result( op, rs );
+
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+
+ if ( !BER_BVISNULL( &base_entry.e_nname ) ) {
+ entry_clean( &base_entry );
+ }
+
+ goto done;
+ }
+ /* NOTE: __NEW__ "search" access is required
+ * on searchBase object */
+ {
+ slap_mask_t mask;
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, &base_entry, get_assertion( op ) )
+ != LDAP_COMPARE_TRUE ) )
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+
+ }
+ if ( ! access_allowed_mask( op, &base_entry,
+ slap_schema.si_ad_entry,
+ NULL, ACL_SEARCH, NULL, &mask ) )
+ {
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ }
+ }
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = NULL;
+ }
+ send_ldap_result( op, rs );
+ goto done;
+ }
+ }
+
+ bsi.bsi_e = NULL;
+
+ bsi.bsi_n_candidates =
+ ( op->ors_limit == NULL /* isroot == TRUE */ ? -2 :
+ ( op->ors_limit->lms_s_unchecked == -1 ? -2 :
+ ( op->ors_limit->lms_s_unchecked ) ) );
+
+#ifndef BACKSQL_ARBITRARY_KEY
+ /* If paged results are in effect, check the paging cookie */
+ if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
+ rs->sr_err = parse_paged_cookie( op, rs );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto done;
+ }
+ }
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+
+ switch ( bsi.bsi_scope ) {
+ case LDAP_SCOPE_BASE:
+ case BACKSQL_SCOPE_BASE_LIKE:
+ /*
+ * probably already found...
+ */
+ bsi.bsi_id_list = &bsi.bsi_base_id;
+ bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
+ break;
+
+ case LDAP_SCOPE_SUBTREE:
+ /*
+ * if baseObject is defined, and if it is the root
+ * of the search, add it to the candidate list
+ */
+ if ( bi->sql_baseObject && BACKSQL_IS_BASEOBJECT_ID( &bsi.bsi_base_id.eid_id ) )
+ {
+ bsi.bsi_id_list = &bsi.bsi_base_id;
+ bsi.bsi_id_listtail = &bsi.bsi_base_id.eid_next;
+ }
+
+ /* FALLTHRU */
+ default:
+
+ /*
+ * for each objectclass we try to construct query which gets IDs
+ * of entries matching LDAP query filter and scope (or at least
+ * candidates), and get the IDs. Do this in ID order for paging.
+ */
+ ldap_avl_apply( bi->sql_oc_by_id, backsql_oc_get_candidates,
+ &bsi, BACKSQL_AVL_STOP, AVL_INORDER );
+
+ /* check for abandon */
+ if ( op->o_abandon ) {
+ eid = bsi.bsi_id_list;
+ rs->sr_err = SLAPD_ABANDON;
+ goto send_results;
+ }
+ }
+
+ if ( op->ors_limit != NULL /* isroot == FALSE */
+ && op->ors_limit->lms_s_unchecked != -1
+ && bsi.bsi_n_candidates == -1 )
+ {
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ /*
+ * now we load candidate entries (only those attributes
+ * mentioned in attrs and filter), test it against full filter
+ * and then send to client; don't free entry_id if baseObject...
+ */
+ for ( eid = bsi.bsi_id_list;
+ eid != NULL;
+ eid = backsql_free_entryID(
+ eid, eid == &bsi.bsi_base_id ? 0 : 1, op->o_tmpmemctx ) )
+ {
+ int rc;
+ Attribute *a_hasSubordinate = NULL,
+ *a_entryUUID = NULL,
+ *a_entryCSN = NULL,
+ **ap = NULL;
+ Entry *e = NULL;
+
+ /* check for abandon */
+ if ( op->o_abandon ) {
+ rs->sr_err = SLAPD_ABANDON;
+ goto send_results;
+ }
+
+ /* check time limit */
+ if ( op->ors_tlimit != SLAP_NO_LIMIT
+ && slap_get_time() > stoptime )
+ {
+ rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
+ rs->sr_ctrls = NULL;
+ rs->sr_ref = rs->sr_v2ref;
+ goto send_results;
+ }
+
+ Debug(LDAP_DEBUG_TRACE, "backsql_search(): loading data "
+ "for entry id=" BACKSQL_IDFMT " oc_id=" BACKSQL_IDNUMFMT ", keyval=" BACKSQL_IDFMT "\n",
+ BACKSQL_IDARG(eid->eid_id),
+ eid->eid_oc_id,
+ BACKSQL_IDARG(eid->eid_keyval) );
+
+ /* check scope */
+ switch ( op->ors_scope ) {
+ case LDAP_SCOPE_BASE:
+ case BACKSQL_SCOPE_BASE_LIKE:
+ if ( !dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
+ goto next_entry2;
+ }
+ break;
+
+ case LDAP_SCOPE_ONE:
+ {
+ struct berval rdn = eid->eid_ndn;
+
+ rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
+ if ( !dnIsOneLevelRDN( &rdn ) ) {
+ goto next_entry2;
+ }
+ /* fall thru */
+ }
+
+ case LDAP_SCOPE_SUBORDINATE:
+ /* discard the baseObject entry */
+ if ( dn_match( &eid->eid_ndn, &op->o_req_ndn ) ) {
+ goto next_entry2;
+ }
+ /* FALLTHRU */
+ case LDAP_SCOPE_SUBTREE:
+ /* FIXME: this should never fail... */
+ if ( !dnIsSuffix( &eid->eid_ndn, &op->o_req_ndn ) ) {
+ goto next_entry2;
+ }
+ break;
+ }
+
+ if ( BACKSQL_IS_BASEOBJECT_ID( &eid->eid_id ) ) {
+ /* don't recollect baseObject... */
+ e = bi->sql_baseObject;
+
+ } else if ( eid == &bsi.bsi_base_id ) {
+ /* don't recollect searchBase object... */
+ e = &base_entry;
+
+ } else {
+ bsi.bsi_e = &user_entry;
+ rc = backsql_id2entry( &bsi, eid );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
+ "error %d in backsql_id2entry() "
+ "- skipping\n", rc );
+ continue;
+ }
+ e = &user_entry;
+ }
+
+ if ( !manageDSAit &&
+ op->ors_scope != LDAP_SCOPE_BASE &&
+ op->ors_scope != BACKSQL_SCOPE_BASE_LIKE &&
+ is_entry_referral( e ) )
+ {
+ BerVarray refs;
+
+ refs = get_entry_referrals( op, e );
+ if ( !refs ) {
+ backsql_srch_info bsi2 = { 0 };
+ Entry user_entry2 = { 0 };
+
+ /* retry with the full entry... */
+ bsi2.bsi_e = &user_entry2;
+ rc = backsql_init_search( &bsi2,
+ &e->e_nname,
+ LDAP_SCOPE_BASE,
+ (time_t)(-1), NULL,
+ dbh, op, rs, NULL,
+ BACKSQL_ISF_GET_ENTRY );
+ if ( rc == LDAP_SUCCESS ) {
+ if ( is_entry_referral( &user_entry2 ) )
+ {
+ refs = get_entry_referrals( op,
+ &user_entry2 );
+ } else {
+ rs->sr_err = LDAP_OTHER;
+ }
+ backsql_entry_clean( op, &user_entry2 );
+ }
+ if ( bsi2.bsi_attrs != NULL ) {
+ op->o_tmpfree( bsi2.bsi_attrs,
+ op->o_tmpmemctx );
+ }
+ }
+
+ if ( refs ) {
+ rs->sr_ref = referral_rewrite( refs,
+ &e->e_name,
+ &op->o_req_dn,
+ op->ors_scope );
+ ber_bvarray_free( refs );
+ }
+
+ if ( rs->sr_ref ) {
+ rs->sr_err = LDAP_REFERRAL;
+
+ } else {
+ rs->sr_text = "bad referral object";
+ }
+
+ rs->sr_entry = e;
+ rs->sr_matched = user_entry.e_name.bv_val;
+ send_search_reference( op, rs );
+
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ rs->sr_matched = NULL;
+ rs->sr_entry = NULL;
+ if ( rs->sr_err == LDAP_REFERRAL ) {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+
+ goto next_entry;
+ }
+
+ /*
+ * We use this flag since we need to parse the filter
+ * anyway; we should have used the frontend API function
+ * filter_has_subordinates()
+ */
+ if ( bsi.bsi_flags & BSQL_SF_FILTER_HASSUBORDINATE ) {
+ rc = backsql_has_children( op, dbh, &e->e_nname );
+
+ switch ( rc ) {
+ case LDAP_COMPARE_TRUE:
+ case LDAP_COMPARE_FALSE:
+ a_hasSubordinate = slap_operational_hasSubordinate( rc == LDAP_COMPARE_TRUE );
+ if ( a_hasSubordinate != NULL ) {
+ for ( ap = &user_entry.e_attrs;
+ *ap;
+ ap = &(*ap)->a_next );
+
+ *ap = a_hasSubordinate;
+ }
+ rc = 0;
+ break;
+
+ default:
+ Debug(LDAP_DEBUG_TRACE,
+ "backsql_search(): "
+ "has_children failed( %d)\n",
+ rc );
+ rc = 1;
+ goto next_entry;
+ }
+ }
+
+ if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYUUID ) {
+ a_entryUUID = backsql_operational_entryUUID( bi, eid );
+ if ( a_entryUUID != NULL ) {
+ if ( ap == NULL ) {
+ ap = &user_entry.e_attrs;
+ }
+
+ for ( ; *ap; ap = &(*ap)->a_next );
+
+ *ap = a_entryUUID;
+ }
+ }
+
+#ifdef BACKSQL_SYNCPROV
+ if ( bsi.bsi_flags & BSQL_SF_FILTER_ENTRYCSN ) {
+ a_entryCSN = backsql_operational_entryCSN( op );
+ if ( a_entryCSN != NULL ) {
+ if ( ap == NULL ) {
+ ap = &user_entry.e_attrs;
+ }
+
+ for ( ; *ap; ap = &(*ap)->a_next );
+
+ *ap = a_entryCSN;
+ }
+ }
+#endif /* BACKSQL_SYNCPROV */
+
+ if ( test_filter( op, e, op->ors_filter ) == LDAP_COMPARE_TRUE )
+ {
+#ifndef BACKSQL_ARBITRARY_KEY
+ /* If paged results are in effect, see if the page limit was exceeded */
+ if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
+ if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size )
+ {
+ e = NULL;
+ send_paged_response( op, rs, &lastid );
+ goto done;
+ }
+ lastid = SQL_TO_PAGECOOKIE( eid->eid_id, eid->eid_oc_id );
+ }
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_operational_attrs = NULL;
+ rs->sr_entry = e;
+ e->e_private = (void *)eid;
+ rs->sr_flags = ( e == &user_entry ) ? REP_ENTRY_MODIFIABLE : 0;
+ /* FIXME: need the whole entry (ITS#3480) */
+ rs->sr_err = send_search_entry( op, rs );
+ e->e_private = NULL;
+ rs->sr_entry = NULL;
+ rs->sr_attrs = NULL;
+ rs->sr_operational_attrs = NULL;
+
+ switch ( rs->sr_err ) {
+ case LDAP_UNAVAILABLE:
+ /*
+ * FIXME: send_search_entry failed;
+ * better stop
+ */
+ Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
+ "connection lost\n" );
+ goto end_of_search;
+
+ case LDAP_SIZELIMIT_EXCEEDED:
+ case LDAP_BUSY:
+ goto send_results;
+ }
+ }
+
+next_entry:;
+ if ( e == &user_entry ) {
+ backsql_entry_clean( op, &user_entry );
+ }
+
+next_entry2:;
+ }
+
+end_of_search:;
+ if ( rs->sr_nentries > 0 ) {
+ rs->sr_ref = rs->sr_v2ref;
+ rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS
+ : LDAP_REFERRAL;
+
+ } else {
+ rs->sr_err = bsi.bsi_status;
+ }
+
+send_results:;
+ if ( rs->sr_err != SLAPD_ABANDON ) {
+#ifndef BACKSQL_ARBITRARY_KEY
+ if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
+ send_paged_response( op, rs, NULL );
+ } else
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+ {
+ send_ldap_result( op, rs );
+ }
+ }
+
+ /* cleanup in case of abandon */
+ for ( ; eid != NULL;
+ eid = backsql_free_entryID(
+ eid, eid == &bsi.bsi_base_id ? 0 : 1, op->o_tmpmemctx ) )
+ ;
+
+ backsql_entry_clean( op, &base_entry );
+
+ /* in case we got here accidentally */
+ backsql_entry_clean( op, &user_entry );
+
+ if ( rs->sr_v2ref ) {
+ ber_bvarray_free( rs->sr_v2ref );
+ rs->sr_v2ref = NULL;
+ }
+
+#ifdef BACKSQL_SYNCPROV
+ if ( op->o_sync ) {
+ Operation op2 = *op;
+ SlapReply rs2 = { REP_RESULT };
+ Entry *e = entry_alloc();
+ slap_callback cb = { 0 };
+
+ op2.o_tag = LDAP_REQ_ADD;
+ op2.o_bd = select_backend( &op->o_bd->be_nsuffix[0], 0 );
+ op2.ora_e = e;
+ op2.o_callback = &cb;
+
+ ber_dupbv( &e->e_name, op->o_bd->be_suffix );
+ ber_dupbv( &e->e_nname, op->o_bd->be_nsuffix );
+
+ cb.sc_response = slap_null_cb;
+
+ op2.o_bd->be_add( &op2, &rs2 );
+
+ if ( op2.ora_e == e )
+ entry_free( e );
+ }
+#endif /* BACKSQL_SYNCPROV */
+
+done:;
+ (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
+
+ if ( bsi.bsi_attrs != NULL ) {
+ op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
+ }
+
+ if ( !BER_BVISNULL( &nbase )
+ && nbase.bv_val != op->o_req_ndn.bv_val )
+ {
+ ch_free( nbase.bv_val );
+ }
+
+ /* restore scope ... FIXME: this should be done before ANY
+ * frontend call that uses op */
+ if ( op->ors_scope == BACKSQL_SCOPE_BASE_LIKE ) {
+ op->ors_scope = LDAP_SCOPE_BASE;
+ }
+
+ SQLTransact( SQL_NULL_HENV, dbh, SQL_ROLLBACK );
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_search()\n" );
+
+ return rs->sr_err;
+}
+
+/* return LDAP_SUCCESS IFF we can retrieve the specified entry.
+ */
+int
+backsql_entry_get(
+ Operation *op,
+ struct berval *ndn,
+ ObjectClass *oc,
+ AttributeDescription *at,
+ int rw,
+ Entry **ent )
+{
+ backsql_srch_info bsi = { 0 };
+ SQLHDBC dbh = SQL_NULL_HDBC;
+ int rc;
+ SlapReply rs = { 0 };
+ AttributeName anlist[ 2 ];
+
+ *ent = NULL;
+
+ rc = backsql_get_db_conn( op, &dbh );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( at ) {
+ anlist[ 0 ].an_name = at->ad_cname;
+ anlist[ 0 ].an_desc = at;
+ BER_BVZERO( &anlist[ 1 ].an_name );
+ }
+
+ bsi.bsi_e = entry_alloc();
+ rc = backsql_init_search( &bsi,
+ ndn,
+ LDAP_SCOPE_BASE,
+ (time_t)(-1), NULL,
+ dbh, op, &rs, at ? anlist : NULL,
+ BACKSQL_ISF_GET_ENTRY );
+
+ if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
+ (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+
+#if 0 /* not supported at present */
+ /* find attribute values */
+ if ( is_entry_alias( bsi.bsi_e ) ) {
+ Debug( LDAP_DEBUG_ACL,
+ "<= backsql_entry_get: entry is an alias\n" );
+ rc = LDAP_ALIAS_PROBLEM;
+ goto return_results;
+ }
+#endif
+
+ if ( is_entry_referral( bsi.bsi_e ) ) {
+ Debug( LDAP_DEBUG_ACL,
+ "<= backsql_entry_get: entry is a referral\n" );
+ rc = LDAP_REFERRAL;
+ goto return_results;
+ }
+
+ if ( oc && !is_entry_objectclass( bsi.bsi_e, oc, 0 ) ) {
+ Debug( LDAP_DEBUG_ACL,
+ "<= backsql_entry_get: "
+ "failed to find objectClass\n" );
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ goto return_results;
+ }
+
+ *ent = bsi.bsi_e;
+ }
+
+return_results:;
+ if ( bsi.bsi_attrs != NULL ) {
+ op->o_tmpfree( bsi.bsi_attrs, op->o_tmpmemctx );
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ if ( bsi.bsi_e ) {
+ entry_free( bsi.bsi_e );
+ }
+ }
+
+ return rc;
+}
+
+void
+backsql_entry_clean(
+ Operation *op,
+ Entry *e )
+{
+ void *ctx;
+
+ ctx = ldap_pvt_thread_pool_context();
+
+ if ( ctx == NULL || ctx != op->o_tmpmemctx ) {
+ if ( !BER_BVISNULL( &e->e_name ) ) {
+ op->o_tmpfree( e->e_name.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &e->e_name );
+ }
+
+ if ( !BER_BVISNULL( &e->e_nname ) ) {
+ op->o_tmpfree( e->e_nname.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &e->e_nname );
+ }
+ }
+
+ entry_clean( e );
+}
+
+int
+backsql_entry_release(
+ Operation *op,
+ Entry *e,
+ int rw )
+{
+ backsql_entry_clean( op, e );
+
+ entry_free( e );
+
+ return 0;
+}
+
+#ifndef BACKSQL_ARBITRARY_KEY
+/* This function is copied verbatim from back-bdb/search.c */
+static int
+parse_paged_cookie( Operation *op, SlapReply *rs )
+{
+ int rc = LDAP_SUCCESS;
+ PagedResultsState *ps = op->o_pagedresults_state;
+
+ /* this function must be invoked only if the pagedResults
+ * control has been detected, parsed and partially checked
+ * by the frontend */
+ assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
+
+ /* cookie decoding/checks deferred to backend... */
+ if ( ps->ps_cookieval.bv_len ) {
+ PagedResultsCookie reqcookie;
+ if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) {
+ /* bad cookie */
+ rs->sr_text = "paged results cookie is invalid";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ AC_MEMCPY( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie ));
+
+ if ( reqcookie > ps->ps_cookie ) {
+ /* bad cookie */
+ rs->sr_text = "paged results cookie is invalid";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+
+ } else if ( reqcookie < ps->ps_cookie ) {
+ rs->sr_text = "paged results cookie is invalid or old";
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ } else {
+ /* Initial request. Initialize state. */
+ ps->ps_cookie = 0;
+ ps->ps_count = 0;
+ }
+
+done:;
+
+ return rc;
+}
+
+/* This function is copied nearly verbatim from back-bdb/search.c */
+static void
+send_paged_response(
+ Operation *op,
+ SlapReply *rs,
+ ID *lastid )
+{
+ LDAPControl ctrl, *ctrls[2];
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ PagedResultsCookie respcookie;
+ struct berval cookie;
+
+ Debug(LDAP_DEBUG_ARGS,
+ "send_paged_response: lastid=0x%08lx nentries=%d\n",
+ lastid ? *lastid : 0, rs->sr_nentries );
+
+ BER_BVZERO( &ctrl.ldctl_value );
+ ctrls[0] = &ctrl;
+ ctrls[1] = NULL;
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+
+ if ( lastid ) {
+ respcookie = ( PagedResultsCookie )(*lastid);
+ cookie.bv_len = sizeof( respcookie );
+ cookie.bv_val = (char *)&respcookie;
+
+ } else {
+ respcookie = ( PagedResultsCookie )0;
+ BER_BVSTR( &cookie, "" );
+ }
+
+ op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
+ op->o_conn->c_pagedresults_state.ps_count =
+ ((PagedResultsState *)op->o_pagedresults_state)->ps_count +
+ rs->sr_nentries;
+
+ /* return size of 0 -- no estimate */
+ ber_printf( ber, "{iO}", 0, &cookie );
+
+ if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
+ goto done;
+ }
+
+ ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
+ ctrls[0]->ldctl_iscritical = 0;
+
+ rs->sr_ctrls = ctrls;
+ rs->sr_err = LDAP_SUCCESS;
+ send_ldap_result( op, rs );
+ rs->sr_ctrls = NULL;
+
+done:
+ (void) ber_free_buf( ber );
+}
+#endif /* ! BACKSQL_ARBITRARY_KEY */
diff --git a/servers/slapd/back-sql/sql-wrap.c b/servers/slapd/back-sql/sql-wrap.c
new file mode 100644
index 0000000..289b590
--- /dev/null
+++ b/servers/slapd/back-sql/sql-wrap.c
@@ -0,0 +1,529 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 Dmitry Kovalev.
+ * Portions Copyright 2002 Pierangelo Masarati.
+ * Portions Copyright 2004 Mark Adamson.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Dmitry Kovalev for inclusion
+ * by OpenLDAP Software. Additional significant contributors include
+ * Pierangelo Masarati and Mark Adamson.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include "ac/string.h"
+#include <sys/types.h>
+
+#include "slap.h"
+#include "proto-sql.h"
+
+#define MAX_ATTR_LEN 16384
+
+void
+backsql_PrintErrors( SQLHENV henv, SQLHDBC hdbc, SQLHSTMT sth, int rc )
+{
+ SQLCHAR msg[SQL_MAX_MESSAGE_LENGTH]; /* msg. buffer */
+ SQLCHAR state[SQL_SQLSTATE_SIZE]; /* statement buf. */
+ SDWORD iSqlCode; /* return code */
+ SWORD len = SQL_MAX_MESSAGE_LENGTH - 1; /* return length */
+
+ Debug( LDAP_DEBUG_TRACE, "Return code: %d\n", rc );
+
+ for ( ; rc = SQLError( henv, hdbc, sth, state, &iSqlCode, msg,
+ SQL_MAX_MESSAGE_LENGTH - 1, &len ), BACKSQL_SUCCESS( rc ); )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ " nativeErrCode=%d SQLengineState=%s msg=\"%s\"\n",
+ (int)iSqlCode, state, msg );
+ }
+}
+
+RETCODE
+backsql_Prepare( SQLHDBC dbh, SQLHSTMT *sth, const char *query, int timeout )
+{
+ RETCODE rc;
+
+ rc = SQLAllocStmt( dbh, sth );
+ if ( rc != SQL_SUCCESS ) {
+ return rc;
+ }
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_Prepare()\n" );
+#endif /* BACKSQL_TRACE */
+
+#ifdef BACKSQL_MSSQL_WORKAROUND
+ {
+ char drv_name[ 30 ];
+ SWORD len;
+
+ SQLGetInfo( dbh, SQL_DRIVER_NAME, drv_name, sizeof( drv_name ), &len );
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "backsql_Prepare(): driver name=\"%s\"\n",
+ drv_name );
+#endif /* BACKSQL_TRACE */
+
+ ldap_pvt_str2upper( drv_name );
+ if ( !strncmp( drv_name, "SQLSRV32.DLL", STRLENOF( "SQLSRV32.DLL" ) ) ) {
+ /*
+ * stupid default result set in MS SQL Server
+ * does not support multiple active statements
+ * on the same connection -- so we are trying
+ * to make it not to use default result set...
+ */
+ Debug( LDAP_DEBUG_TRACE, "_SQLprepare(): "
+ "enabling MS SQL Server default result "
+ "set workaround\n" );
+ rc = SQLSetStmtOption( *sth, SQL_CONCURRENCY,
+ SQL_CONCUR_ROWVER );
+ if ( rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_Prepare(): "
+ "SQLSetStmtOption(SQL_CONCURRENCY,"
+ "SQL_CONCUR_ROWVER) failed:\n" );
+ backsql_PrintErrors( SQL_NULL_HENV, dbh, *sth, rc );
+ SQLFreeStmt( *sth, SQL_DROP );
+ return rc;
+ }
+ }
+ }
+#endif /* BACKSQL_MSSQL_WORKAROUND */
+
+ if ( timeout > 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "_SQLprepare(): "
+ "setting query timeout to %d sec.\n",
+ timeout );
+ rc = SQLSetStmtOption( *sth, SQL_QUERY_TIMEOUT, timeout );
+ if ( rc != SQL_SUCCESS ) {
+ backsql_PrintErrors( SQL_NULL_HENV, dbh, *sth, rc );
+ SQLFreeStmt( *sth, SQL_DROP );
+ return rc;
+ }
+ }
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_Prepare() calling SQLPrepare()\n" );
+#endif /* BACKSQL_TRACE */
+
+ return SQLPrepare( *sth, (SQLCHAR *)query, SQL_NTS );
+}
+
+RETCODE
+backsql_BindRowAsStrings_x( SQLHSTMT sth, BACKSQL_ROW_NTS *row, void *ctx )
+{
+ RETCODE rc;
+
+ if ( row == NULL ) {
+ return SQL_ERROR;
+ }
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "==> backsql_BindRowAsStrings()\n" );
+#endif /* BACKSQL_TRACE */
+
+ rc = SQLNumResultCols( sth, &row->ncols );
+ if ( rc != SQL_SUCCESS ) {
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings(): "
+ "SQLNumResultCols() failed:\n" );
+#endif /* BACKSQL_TRACE */
+
+ backsql_PrintErrors( SQL_NULL_HENV, SQL_NULL_HDBC, sth, rc );
+
+ } else {
+ SQLCHAR colname[ 64 ];
+ SQLSMALLINT name_len, col_type, col_scale, col_null;
+ SQLLEN col_prec;
+ int i;
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings: "
+ "ncols=%d\n", (int)row->ncols );
+#endif /* BACKSQL_TRACE */
+
+ row->col_names = (BerVarray)ber_memcalloc_x( row->ncols + 1,
+ sizeof( struct berval ), ctx );
+ if ( row->col_names == NULL ) {
+ goto nomem;
+ }
+
+ row->col_prec = (UDWORD *)ber_memcalloc_x( row->ncols,
+ sizeof( UDWORD ), ctx );
+ if ( row->col_prec == NULL ) {
+ goto nomem;
+ }
+
+ row->col_type = (SQLSMALLINT *)ber_memcalloc_x( row->ncols,
+ sizeof( SQLSMALLINT ), ctx );
+ if ( row->col_type == NULL ) {
+ goto nomem;
+ }
+
+ row->cols = (char **)ber_memcalloc_x( row->ncols + 1,
+ sizeof( char * ), ctx );
+ if ( row->cols == NULL ) {
+ goto nomem;
+ }
+
+ row->value_len = (SQLLEN *)ber_memcalloc_x( row->ncols,
+ sizeof( SQLLEN ), ctx );
+ if ( row->value_len == NULL ) {
+ goto nomem;
+ }
+
+ if ( 0 ) {
+nomem:
+ ber_memfree_x( row->col_names, ctx );
+ row->col_names = NULL;
+ ber_memfree_x( row->col_prec, ctx );
+ row->col_prec = NULL;
+ ber_memfree_x( row->col_type, ctx );
+ row->col_type = NULL;
+ ber_memfree_x( row->cols, ctx );
+ row->cols = NULL;
+ ber_memfree_x( row->value_len, ctx );
+ row->value_len = NULL;
+
+ Debug( LDAP_DEBUG_ANY, "backsql_BindRowAsStrings: "
+ "out of memory\n" );
+
+ return LDAP_NO_MEMORY;
+ }
+
+ for ( i = 0; i < row->ncols; i++ ) {
+ SQLSMALLINT TargetType;
+
+ rc = SQLDescribeCol( sth, (SQLSMALLINT)(i + 1), &colname[ 0 ],
+ (SQLUINTEGER)( sizeof( colname ) - 1 ),
+ &name_len, &col_type,
+ &col_prec, &col_scale, &col_null );
+ /* FIXME: test rc? */
+
+ ber_str2bv_x( (char *)colname, 0, 1,
+ &row->col_names[ i ], ctx );
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings: "
+ "col_name=%s, col_prec[%d]=%d\n",
+ colname, (int)(i + 1), (int)col_prec );
+#endif /* BACKSQL_TRACE */
+ if ( col_type != SQL_CHAR && col_type != SQL_VARCHAR )
+ {
+ col_prec = MAX_ATTR_LEN;
+ }
+
+ row->cols[ i ] = (char *)ber_memcalloc_x( col_prec + 1,
+ sizeof( char ), ctx );
+ row->col_prec[ i ] = col_prec;
+ row->col_type[ i ] = col_type;
+
+ /*
+ * ITS#3386, ITS#3113 - 20070308
+ * Note: there are many differences between various DPMS and ODBC
+ * Systems; some support SQL_C_BLOB, SQL_C_BLOB_LOCATOR. YMMV:
+ * This has only been tested on Linux/MySQL/UnixODBC
+ * For BINARY-type Fields (BLOB, etc), read the data as BINARY
+ */
+ if ( BACKSQL_IS_BINARY( col_type ) ) {
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings: "
+ "col_name=%s, col_type[%d]=%d: reading binary data\n",
+ colname, (int)(i + 1), (int)col_type);
+#endif /* BACKSQL_TRACE */
+ TargetType = SQL_C_BINARY;
+
+ } else {
+ /* Otherwise read it as Character data */
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "backsql_BindRowAsStrings: "
+ "col_name=%s, col_type[%d]=%d: reading character data\n",
+ colname, (int)(i + 1), (int)col_type);
+#endif /* BACKSQL_TRACE */
+ TargetType = SQL_C_CHAR;
+ }
+
+ rc = SQLBindCol( sth, (SQLUSMALLINT)(i + 1),
+ TargetType,
+ (SQLPOINTER)row->cols[ i ],
+ col_prec + 1,
+ &row->value_len[ i ] );
+
+ /* FIXME: test rc? */
+ }
+
+ BER_BVZERO( &row->col_names[ i ] );
+ row->cols[ i ] = NULL;
+ }
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "<== backsql_BindRowAsStrings()\n" );
+#endif /* BACKSQL_TRACE */
+
+ return rc;
+}
+
+RETCODE
+backsql_BindRowAsStrings( SQLHSTMT sth, BACKSQL_ROW_NTS *row )
+{
+ return backsql_BindRowAsStrings_x( sth, row, NULL );
+}
+
+RETCODE
+backsql_FreeRow_x( BACKSQL_ROW_NTS *row, void *ctx )
+{
+ if ( row->cols == NULL ) {
+ return SQL_ERROR;
+ }
+
+ ber_bvarray_free_x( row->col_names, ctx );
+ ber_memfree_x( row->col_prec, ctx );
+ ber_memfree_x( row->col_type, ctx );
+ ber_memvfree_x( (void **)row->cols, ctx );
+ ber_memfree_x( row->value_len, ctx );
+
+ return SQL_SUCCESS;
+}
+
+
+RETCODE
+backsql_FreeRow( BACKSQL_ROW_NTS *row )
+{
+ return backsql_FreeRow_x( row, NULL );
+}
+
+static void
+backsql_close_db_handle( SQLHDBC dbh )
+{
+ if ( dbh == SQL_NULL_HDBC ) {
+ return;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_close_db_handle(%p)\n",
+ (void *)dbh );
+
+ /*
+ * Default transact is SQL_ROLLBACK; commit is required only
+ * by write operations, and it is explicitly performed after
+ * each atomic operation succeeds.
+ */
+
+ /* TimesTen */
+ SQLTransact( SQL_NULL_HENV, dbh, SQL_ROLLBACK );
+ SQLDisconnect( dbh );
+ SQLFreeConnect( dbh );
+
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_close_db_handle(%p)\n",
+ (void *)dbh );
+}
+
+int
+backsql_conn_destroy(
+ backsql_info *bi )
+{
+ return 0;
+}
+
+int
+backsql_init_db_env( backsql_info *bi )
+{
+ RETCODE rc;
+ int ret = SQL_SUCCESS;
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_init_db_env()\n" );
+
+ rc = SQLAllocEnv( &bi->sql_db_env );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "init_db_env: SQLAllocEnv failed:\n" );
+ backsql_PrintErrors( SQL_NULL_HENV, SQL_NULL_HDBC,
+ SQL_NULL_HENV, rc );
+ ret = SQL_ERROR;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_init_db_env()=%d\n", ret );
+
+ return ret;
+}
+
+int
+backsql_free_db_env( backsql_info *bi )
+{
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_free_db_env()\n" );
+
+ (void)SQLFreeEnv( bi->sql_db_env );
+ bi->sql_db_env = SQL_NULL_HENV;
+
+ /*
+ * stop, if frontend waits for all threads to shutdown
+ * before calling this -- then what are we going to delete??
+ * everything is already deleted...
+ */
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_free_db_env()\n" );
+
+ return SQL_SUCCESS;
+}
+
+static int
+backsql_open_db_handle(
+ backsql_info *bi,
+ SQLHDBC *dbhp )
+{
+ /* TimesTen */
+ char DBMSName[ 32 ];
+ int rc;
+
+ assert( dbhp != NULL );
+ *dbhp = SQL_NULL_HDBC;
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_open_db_handle()\n" );
+
+ rc = SQLAllocConnect( bi->sql_db_env, dbhp );
+ if ( !BACKSQL_SUCCESS( rc ) ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_open_db_handle(): "
+ "SQLAllocConnect() failed:\n" );
+ backsql_PrintErrors( bi->sql_db_env, SQL_NULL_HDBC,
+ SQL_NULL_HENV, rc );
+ return LDAP_UNAVAILABLE;
+ }
+
+ rc = SQLConnect( *dbhp,
+ (SQLCHAR*)bi->sql_dbname, SQL_NTS,
+ (SQLCHAR*)bi->sql_dbuser, SQL_NTS,
+ (SQLCHAR*)bi->sql_dbpasswd, SQL_NTS );
+ if ( rc != SQL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_open_db_handle(): "
+ "SQLConnect() to database \"%s\" %s.\n",
+ bi->sql_dbname,
+ rc == SQL_SUCCESS_WITH_INFO ?
+ "succeeded with info" : "failed" );
+ backsql_PrintErrors( bi->sql_db_env, *dbhp, SQL_NULL_HENV, rc );
+ if ( rc != SQL_SUCCESS_WITH_INFO ) {
+ SQLFreeConnect( *dbhp );
+ return LDAP_UNAVAILABLE;
+ }
+ }
+
+ /*
+ * TimesTen : Turn off autocommit. We must explicitly
+ * commit any transactions.
+ */
+ SQLSetConnectOption( *dbhp, SQL_AUTOCOMMIT,
+ BACKSQL_AUTOCOMMIT_ON( bi ) ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF );
+
+ /*
+ * See if this connection is to TimesTen. If it is,
+ * remember that fact for later use.
+ */
+ /* Assume until proven otherwise */
+ bi->sql_flags &= ~BSQLF_USE_REVERSE_DN;
+ DBMSName[ 0 ] = '\0';
+ rc = SQLGetInfo( *dbhp, SQL_DBMS_NAME, (PTR)&DBMSName,
+ sizeof( DBMSName ), NULL );
+ if ( rc == SQL_SUCCESS ) {
+ if ( strcmp( DBMSName, "TimesTen" ) == 0 ||
+ strcmp( DBMSName, "Front-Tier" ) == 0 )
+ {
+ Debug( LDAP_DEBUG_TRACE, "backsql_open_db_handle(): "
+ "TimesTen database!\n" );
+ bi->sql_flags |= BSQLF_USE_REVERSE_DN;
+ }
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "backsql_open_db_handle(): "
+ "SQLGetInfo() failed.\n" );
+ backsql_PrintErrors( bi->sql_db_env, *dbhp, SQL_NULL_HENV, rc );
+ SQLDisconnect( *dbhp );
+ SQLFreeConnect( *dbhp );
+ return LDAP_UNAVAILABLE;
+ }
+ /* end TimesTen */
+
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_open_db_handle()\n" );
+
+ return LDAP_SUCCESS;
+}
+
+static void *backsql_db_conn_dummy;
+
+static void
+backsql_db_conn_keyfree(
+ void *key,
+ void *data )
+{
+ (void)backsql_close_db_handle( (SQLHDBC)data );
+}
+
+int
+backsql_free_db_conn( Operation *op, SQLHDBC dbh )
+{
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_free_db_conn()\n" );
+
+ (void)backsql_close_db_handle( dbh );
+ ldap_pvt_thread_pool_setkey( op->o_threadctx,
+ &backsql_db_conn_dummy, (void *)SQL_NULL_HDBC,
+ backsql_db_conn_keyfree, NULL, NULL );
+
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_free_db_conn()\n" );
+
+ return LDAP_SUCCESS;
+}
+
+int
+backsql_get_db_conn( Operation *op, SQLHDBC *dbhp )
+{
+ backsql_info *bi = (backsql_info *)op->o_bd->be_private;
+ int rc = LDAP_SUCCESS;
+ SQLHDBC dbh = SQL_NULL_HDBC;
+
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_get_db_conn()\n" );
+
+ assert( dbhp != NULL );
+ *dbhp = SQL_NULL_HDBC;
+
+ if ( op->o_threadctx ) {
+ void *data = NULL;
+
+ ldap_pvt_thread_pool_getkey( op->o_threadctx,
+ &backsql_db_conn_dummy, &data, NULL );
+ dbh = (SQLHDBC)data;
+
+ } else {
+ dbh = bi->sql_dbh;
+ }
+
+ if ( dbh == SQL_NULL_HDBC ) {
+ rc = backsql_open_db_handle( bi, &dbh );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( op->o_threadctx ) {
+ void *data = (void *)dbh;
+
+ ldap_pvt_thread_pool_setkey( op->o_threadctx,
+ &backsql_db_conn_dummy, data,
+ backsql_db_conn_keyfree, NULL, NULL );
+
+ } else {
+ bi->sql_dbh = dbh;
+ }
+ }
+
+ *dbhp = dbh;
+
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_get_db_conn()\n" );
+
+ return LDAP_SUCCESS;
+}
+
diff --git a/servers/slapd/back-sql/util.c b/servers/slapd/back-sql/util.c
new file mode 100644
index 0000000..94a00ca
--- /dev/null
+++ b/servers/slapd/back-sql/util.c
@@ -0,0 +1,572 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 Dmitry Kovalev.
+ * Portions Copyright 2002 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Dmitry Kovalev for inclusion
+ * by OpenLDAP Software. Additional significant contributors include
+ * Pierangelo Masarati.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "ac/string.h"
+#include "ac/ctype.h"
+#include "ac/stdarg.h"
+
+#include "slap.h"
+#include "proto-sql.h"
+#include "lutil.h"
+
+#define BACKSQL_MAX(a,b) ((a)>(b)?(a):(b))
+#define BACKSQL_MIN(a,b) ((a)<(b)?(a):(b))
+
+#define BACKSQL_STR_GROW 256
+
+const char backsql_def_oc_query[] =
+ "SELECT id,name,keytbl,keycol,create_proc,delete_proc,expect_return "
+ "FROM ldap_oc_mappings";
+const char backsql_def_needs_select_oc_query[] =
+ "SELECT id,name,keytbl,keycol,create_proc,create_keyval,delete_proc,"
+ "expect_return FROM ldap_oc_mappings";
+const char backsql_def_at_query[] =
+ "SELECT name,sel_expr,from_tbls,join_where,add_proc,delete_proc,"
+ "param_order,expect_return,sel_expr_u FROM ldap_attr_mappings "
+ "WHERE oc_map_id=?";
+const char backsql_def_delentry_stmt[] = "DELETE FROM ldap_entries WHERE id=?";
+const char backsql_def_renentry_stmt[] =
+ "UPDATE ldap_entries SET dn=?,parent=?,keyval=? WHERE id=?";
+const char backsql_def_insentry_stmt[] =
+ "INSERT INTO ldap_entries (dn,oc_map_id,parent,keyval) "
+ "VALUES (?,?,?,?)";
+const char backsql_def_delobjclasses_stmt[] = "DELETE FROM ldap_entry_objclasses "
+ "WHERE entry_id=?";
+const char backsql_def_subtree_cond[] = "ldap_entries.dn LIKE CONCAT('%',?)";
+const char backsql_def_upper_subtree_cond[] = "(ldap_entries.dn) LIKE CONCAT('%',?)";
+const char backsql_id_query[] = "SELECT id,keyval,oc_map_id,dn FROM ldap_entries WHERE ";
+/* better ?||? or cast(?||? as varchar) */
+const char backsql_def_concat_func[] = "CONCAT(?,?)";
+
+/* TimesTen */
+const char backsql_check_dn_ru_query[] = "SELECT dn_ru FROM ldap_entries";
+
+struct berbuf *
+backsql_strcat_x( struct berbuf *dest, void *memctx, ... )
+{
+ va_list strs;
+ ber_len_t cdlen, cslen, grow;
+ char *cstr;
+
+ assert( dest != NULL );
+ assert( dest->bb_val.bv_val == NULL
+ || dest->bb_val.bv_len == strlen( dest->bb_val.bv_val ) );
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_strcat()\n" );
+#endif /* BACKSQL_TRACE */
+
+ va_start( strs, memctx );
+ if ( dest->bb_val.bv_val == NULL || dest->bb_len == 0 ) {
+ dest->bb_val.bv_val = (char *)ber_memalloc_x( BACKSQL_STR_GROW * sizeof( char ), memctx );
+ dest->bb_val.bv_len = 0;
+ dest->bb_len = BACKSQL_STR_GROW;
+ }
+ cdlen = dest->bb_val.bv_len;
+ while ( ( cstr = va_arg( strs, char * ) ) != NULL ) {
+ cslen = strlen( cstr );
+ grow = BACKSQL_MAX( BACKSQL_STR_GROW, cslen );
+ if ( dest->bb_len - cdlen <= cslen ) {
+ char *tmp_dest;
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "backsql_strcat(): "
+ "buflen=%d, cdlen=%d, cslen=%d "
+ "-- reallocating dest\n",
+ dest->bb_len, cdlen + 1, cslen );
+#endif /* BACKSQL_TRACE */
+
+ tmp_dest = (char *)ber_memrealloc_x( dest->bb_val.bv_val,
+ dest->bb_len + grow * sizeof( char ), memctx );
+ if ( tmp_dest == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "backsql_strcat(): "
+ "could not reallocate string buffer.\n" );
+ va_end( strs );
+ return NULL;
+ }
+ dest->bb_val.bv_val = tmp_dest;
+ dest->bb_len += grow;
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "backsql_strcat(): "
+ "new buflen=%d, dest=%p\n",
+ dest->bb_len, dest );
+#endif /* BACKSQL_TRACE */
+ }
+ AC_MEMCPY( dest->bb_val.bv_val + cdlen, cstr, cslen + 1 );
+ cdlen += cslen;
+ }
+ va_end( strs );
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_strcat() (dest=\"%s\")\n",
+ dest->bb_val.bv_val );
+#endif /* BACKSQL_TRACE */
+
+ dest->bb_val.bv_len = cdlen;
+
+ return dest;
+}
+
+struct berbuf *
+backsql_strfcat_x( struct berbuf *dest, void *memctx, const char *fmt, ... )
+{
+ va_list strs;
+ ber_len_t cdlen;
+
+ assert( dest != NULL );
+ assert( fmt != NULL );
+ assert( dest->bb_len == 0 || dest->bb_len > dest->bb_val.bv_len );
+ assert( dest->bb_val.bv_val == NULL
+ || dest->bb_val.bv_len == strlen( dest->bb_val.bv_val ) );
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_strfcat()\n" );
+#endif /* BACKSQL_TRACE */
+
+ va_start( strs, fmt );
+ if ( dest->bb_val.bv_val == NULL || dest->bb_len == 0 ) {
+ dest->bb_val.bv_val = (char *)ber_memalloc_x( BACKSQL_STR_GROW * sizeof( char ), memctx );
+ dest->bb_val.bv_len = 0;
+ dest->bb_len = BACKSQL_STR_GROW;
+ }
+
+ cdlen = dest->bb_val.bv_len;
+ for ( ; fmt[0]; fmt++ ) {
+ ber_len_t cslen, grow;
+ char *cstr, cc[ 2 ] = { '\0', '\0' };
+ struct berval *cbv;
+
+ switch ( fmt[ 0 ] ) {
+
+ /* berval */
+ case 'b':
+ cbv = va_arg( strs, struct berval * );
+ cstr = cbv->bv_val;
+ cslen = cbv->bv_len;
+ break;
+
+ /* length + string */
+ case 'l':
+ cslen = va_arg( strs, ber_len_t );
+ cstr = va_arg( strs, char * );
+ break;
+
+ /* string */
+ case 's':
+ cstr = va_arg( strs, char * );
+ cslen = strlen( cstr );
+ break;
+
+ /* char */
+ case 'c':
+ /*
+ * `char' is promoted to `int' when passed through `...'
+ */
+ cc[0] = va_arg( strs, int );
+ cstr = cc;
+ cslen = 1;
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+ grow = BACKSQL_MAX( BACKSQL_STR_GROW, cslen );
+ if ( dest->bb_len - cdlen <= cslen ) {
+ char *tmp_dest;
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "backsql_strfcat(): "
+ "buflen=%d, cdlen=%d, cslen=%d "
+ "-- reallocating dest\n",
+ dest->bb_len, cdlen + 1, cslen );
+#endif /* BACKSQL_TRACE */
+
+ tmp_dest = (char *)ber_memrealloc_x( dest->bb_val.bv_val,
+ ( dest->bb_len ) + grow * sizeof( char ), memctx );
+ if ( tmp_dest == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "backsql_strfcat(): "
+ "could not reallocate string buffer.\n" );
+ va_end( strs );
+ return NULL;
+ }
+ dest->bb_val.bv_val = tmp_dest;
+ dest->bb_len += grow * sizeof( char );
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "backsql_strfcat(): "
+ "new buflen=%d, dest=%p\n", dest->bb_len, dest );
+#endif /* BACKSQL_TRACE */
+ }
+
+ assert( cstr != NULL );
+
+ AC_MEMCPY( dest->bb_val.bv_val + cdlen, cstr, cslen + 1 );
+ cdlen += cslen;
+ }
+
+ va_end( strs );
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_strfcat() (dest=\"%s\")\n",
+ dest->bb_val.bv_val );
+#endif /* BACKSQL_TRACE */
+
+ dest->bb_val.bv_len = cdlen;
+
+ return dest;
+}
+
+int
+backsql_entry_addattr(
+ Entry *e,
+ AttributeDescription *ad,
+ struct berval *val,
+ void *memctx )
+{
+ int rc;
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "backsql_entry_addattr(\"%s\"): %s=%s\n",
+ e->e_name.bv_val, ad->ad_cname.bv_val, val->bv_val );
+#endif /* BACKSQL_TRACE */
+
+ rc = attr_merge_normalize_one( e, ad, val, memctx );
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "backsql_entry_addattr(\"%s\"): "
+ "failed to merge value \"%s\" for attribute \"%s\"\n",
+ e->e_name.bv_val, val->bv_val, ad->ad_cname.bv_val );
+ return rc;
+ }
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_entry_addattr(\"%s\")\n",
+ e->e_name.bv_val );
+#endif /* BACKSQL_TRACE */
+
+ return LDAP_SUCCESS;
+}
+
+static char *
+backsql_get_table_spec( backsql_info *bi, char **p )
+{
+ char *s, *q;
+ struct berbuf res = BB_NULL;
+
+ assert( p != NULL );
+ assert( *p != NULL );
+
+ s = *p;
+ while ( **p && **p != ',' ) {
+ (*p)++;
+ }
+
+ if ( **p ) {
+ *(*p)++ = '\0';
+ }
+
+#define BACKSQL_NEXT_WORD { \
+ while ( *s && isspace( (unsigned char)*s ) ) s++; \
+ if ( !*s ) return res.bb_val.bv_val; \
+ q = s; \
+ while ( *q && !isspace( (unsigned char)*q ) ) q++; \
+ if ( *q ) *q++='\0'; \
+ }
+
+ BACKSQL_NEXT_WORD;
+ /* table name */
+ backsql_strcat_x( &res, NULL, s, NULL );
+ s = q;
+
+ BACKSQL_NEXT_WORD;
+ if ( strcasecmp( s, "AS" ) == 0 ) {
+ s = q;
+ BACKSQL_NEXT_WORD;
+ }
+
+ /* oracle doesn't understand "AS" :( and other RDBMSes don't need it */
+ backsql_strfcat_x( &res, NULL, "lbbsb",
+ STRLENOF( " " ), " ",
+ &bi->sql_aliasing,
+ &bi->sql_aliasing_quote,
+ s,
+ &bi->sql_aliasing_quote );
+
+ return res.bb_val.bv_val;
+}
+
+int
+backsql_merge_from_clause(
+ backsql_info *bi,
+ struct berbuf *dest_from,
+ struct berval *src_from )
+{
+ char *s, *p, *srcc, *pos, e;
+ struct berbuf res = BB_NULL;
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "==>backsql_merge_from_clause(): "
+ "dest_from=\"%s\",src_from=\"%s\"\n",
+ dest_from ? dest_from->bb_val.bv_val : "<NULL>",
+ src_from->bv_val );
+#endif /* BACKSQL_TRACE */
+
+ srcc = ch_strdup( src_from->bv_val );
+ p = srcc;
+
+ if ( dest_from != NULL ) {
+ res = *dest_from;
+ }
+
+ while ( *p ) {
+ s = backsql_get_table_spec( bi, &p );
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "backsql_merge_from_clause(): "
+ "p=\"%s\" s=\"%s\"\n", p, s );
+#endif /* BACKSQL_TRACE */
+
+ if ( BER_BVISNULL( &res.bb_val ) ) {
+ backsql_strcat_x( &res, NULL, s, NULL );
+
+ } else {
+ pos = strstr( res.bb_val.bv_val, s );
+ if ( pos == NULL || ( ( e = pos[ strlen( s ) ] ) != '\0' && e != ',' ) ) {
+ backsql_strfcat_x( &res, NULL, "cs", ',', s );
+ }
+ }
+
+ if ( s ) {
+ ch_free( s );
+ }
+ }
+
+#ifdef BACKSQL_TRACE
+ Debug( LDAP_DEBUG_TRACE, "<==backsql_merge_from_clause()\n" );
+#endif /* BACKSQL_TRACE */
+
+ free( srcc );
+ *dest_from = res;
+
+ return 1;
+}
+
+/*
+ * splits a pattern in components separated by '?'
+ * (double ?? are turned into single ? and left in the string)
+ * expected contains the number of expected occurrences of '?'
+ * (a negative value means parse as many as possible)
+ */
+
+int
+backsql_split_pattern(
+ const char *_pattern,
+ BerVarray *split_pattern,
+ int expected )
+{
+ char *pattern, *start, *end;
+ struct berval bv;
+ int rc = 0;
+
+#define SPLIT_CHAR '?'
+
+ assert( _pattern != NULL );
+ assert( split_pattern != NULL );
+
+ pattern = ch_strdup( _pattern );
+
+ start = pattern;
+ end = strchr( start, SPLIT_CHAR );
+ for ( ; start; expected-- ) {
+ char *real_end = end;
+ ber_len_t real_len;
+
+ if ( real_end == NULL ) {
+ real_end = start + strlen( start );
+
+ } else if ( real_end[ 1 ] == SPLIT_CHAR ) {
+ expected++;
+ AC_MEMCPY( real_end, real_end + 1, strlen( real_end ) );
+ end = strchr( real_end + 1, SPLIT_CHAR );
+ continue;
+ }
+
+ real_len = real_end - start;
+ if ( real_len == 0 ) {
+ ber_str2bv( "", 0, 1, &bv );
+ } else {
+ ber_str2bv( start, real_len, 1, &bv );
+ }
+
+ ber_bvarray_add( split_pattern, &bv );
+
+ if ( expected == 0 ) {
+ if ( end != NULL ) {
+ rc = -1;
+ goto done;
+ }
+ break;
+ }
+
+ if ( end != NULL ) {
+ start = end + 1;
+ end = strchr( start, SPLIT_CHAR );
+ }
+ }
+
+done:;
+
+ ch_free( pattern );
+
+ return rc;
+}
+
+int
+backsql_prepare_pattern(
+ BerVarray split_pattern,
+ BerVarray values,
+ struct berval *res )
+{
+ int i;
+ struct berbuf bb = BB_NULL;
+
+ assert( res != NULL );
+
+ for ( i = 0; values[i].bv_val; i++ ) {
+ if ( split_pattern[i].bv_val == NULL ) {
+ ch_free( bb.bb_val.bv_val );
+ return -1;
+ }
+ backsql_strfcat_x( &bb, NULL, "b", &split_pattern[ i ] );
+ backsql_strfcat_x( &bb, NULL, "b", &values[ i ] );
+ }
+
+ if ( split_pattern[ i ].bv_val == NULL ) {
+ ch_free( bb.bb_val.bv_val );
+ return -1;
+ }
+
+ backsql_strfcat_x( &bb, NULL, "b", &split_pattern[ i ] );
+
+ *res = bb.bb_val;
+
+ return 0;
+}
+
+int
+backsql_entryUUID(
+ backsql_info *bi,
+ backsql_entryID *id,
+ struct berval *entryUUID,
+ void *memctx )
+{
+ char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
+ struct berval uuid;
+#ifdef BACKSQL_ARBITRARY_KEY
+ int i;
+ ber_len_t l, lmax;
+#endif /* BACKSQL_ARBITRARY_KEY */
+
+ /* entryUUID is generated as "%08x-%04x-%04x-0000-eaddrXXX"
+ * with eid_oc_id as %08x and hi and lo eid_id as %04x-%04x */
+ assert( bi != NULL );
+ assert( id != NULL );
+ assert( entryUUID != NULL );
+
+#ifdef BACKSQL_ARBITRARY_KEY
+ snprintf( uuidbuf, sizeof( uuidbuf ),
+ "%08x-0000-0000-0000-000000000000",
+ ( id->eid_oc_id & 0xFFFFFFFF ) );
+ lmax = id->eid_keyval.bv_len < 12 ? id->eid_keyval.bv_len : 12;
+ for ( l = 0, i = 9; l < lmax; l++, i += 2 ) {
+ switch ( i ) {
+ case STRLENOF( "00000000-0000" ):
+ case STRLENOF( "00000000-0000-0000" ):
+ case STRLENOF( "00000000-0000-0000-0000" ):
+ uuidbuf[ i++ ] = '-';
+ /* FALLTHRU */
+
+ default:
+ snprintf( &uuidbuf[ i ], 3, "%2x", id->eid_keyval.bv_val[ l ] );
+ break;
+ }
+ }
+#else /* ! BACKSQL_ARBITRARY_KEY */
+ /* note: works only with 32 bit architectures... */
+ snprintf( uuidbuf, sizeof( uuidbuf ),
+ "%08x-%04x-%04x-0000-000000000000",
+ ( (unsigned)id->eid_oc_id & 0xFFFFFFFF ),
+ ( ( (unsigned)id->eid_keyval & 0xFFFF0000 ) >> 020 /* 16 */ ),
+ ( (unsigned)id->eid_keyval & 0xFFFF ) );
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+
+ uuid.bv_val = uuidbuf;
+ uuid.bv_len = strlen( uuidbuf );
+
+ ber_dupbv_x( entryUUID, &uuid, memctx );
+
+ return 0;
+}
+
+int
+backsql_entryUUID_decode(
+ struct berval *entryUUID,
+ unsigned long *oc_id,
+#ifdef BACKSQL_ARBITRARY_KEY
+ struct berval *keyval
+#else /* ! BACKSQL_ARBITRARY_KEY */
+ unsigned long *keyval
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+ )
+{
+#if 0
+ fprintf( stderr, "==> backsql_entryUUID_decode()\n" );
+#endif
+
+ *oc_id = ( entryUUID->bv_val[0] << 030 /* 24 */ )
+ + ( entryUUID->bv_val[1] << 020 /* 16 */ )
+ + ( entryUUID->bv_val[2] << 010 /* 8 */ )
+ + entryUUID->bv_val[3];
+
+#ifdef BACKSQL_ARBITRARY_KEY
+ /* FIXME */
+#else /* ! BACKSQL_ARBITRARY_KEY */
+ *keyval = ( entryUUID->bv_val[4] << 030 /* 24 */ )
+ + ( entryUUID->bv_val[5] << 020 /* 16 */ )
+ + ( entryUUID->bv_val[6] << 010 /* 8 */ )
+ + entryUUID->bv_val[7];
+#endif /* ! BACKSQL_ARBITRARY_KEY */
+
+#if 0
+ fprintf( stderr, "<== backsql_entryUUID_decode(): oc=%lu id=%lu\n",
+ *oc_id, *keyval );
+#endif
+
+ return LDAP_SUCCESS;
+}
+
diff --git a/servers/slapd/back-wt/Makefile.in b/servers/slapd/back-wt/Makefile.in
new file mode 100644
index 0000000..054025e
--- /dev/null
+++ b/servers/slapd/back-wt/Makefile.in
@@ -0,0 +1,54 @@
+# Makefile.in for back-wt
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SRCS = init.c tools.c config.c \
+ add.c bind.c compare.c delete.c modify.c modrdn.c search.c \
+ extended.c operational.c \
+ attr.c index.c key.c filterindex.c \
+ dn2entry.c dn2id.c id2entry.c idl.c \
+ nextid.c ctx.c cache.c
+
+OBJS = init.lo tools.lo config.lo \
+ add.lo bind.lo compare.lo delete.lo modify.lo modrdn.lo search.lo \
+ extended.lo operational.lo \
+ attr.lo index.lo key.lo filterindex.lo \
+ dn2entry.lo dn2id.lo id2entry.lo idl.lo \
+ nextid.lo ctx.lo cache.lo
+
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-wt"
+BUILD_MOD = @BUILD_WT@
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = @WT_CFLAGS@
+MOD_LIBS = @WT_LIBS@
+
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+
+LIBBASE = back_wt
+
+XINCPATH = -I.. -I$(srcdir)/..
+XDEFS = $(MODULES_CPPFLAGS)
+
+all-local-lib: ../.backend
+
+../.backend: lib$(LIBBASE).a
+ @touch $@
+
diff --git a/servers/slapd/back-wt/add.c b/servers/slapd/back-wt/add.c
new file mode 100644
index 0000000..04c08a1
--- /dev/null
+++ b/servers/slapd/back-wt/add.c
@@ -0,0 +1,373 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include "back-wt.h"
+#include "slap-config.h"
+
+int
+wt_add( Operation *op, SlapReply *rs )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ struct berval pdn;
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof textbuf;
+ AttributeDescription *children = slap_schema.si_ad_children;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+ ID eid = NOID;
+ LDAPControl **postread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+ wt_ctx *wc;
+ Entry *e = NULL;
+ Entry *p = NULL;
+ ID pid = NOID;
+ int rc;
+
+ Debug( LDAP_DEBUG_ARGS, "==> wt_add: %s\n", op->ora_e->e_name.bv_val );
+
+ ctrls[num_ctrls] = 0;
+
+ /* check entry's schema */
+ rs->sr_err = entry_schema_check(
+ op, op->ora_e, NULL,
+ get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_add: entry failed schema check: %s (%d)\n",
+ rs->sr_text, rs->sr_err );
+ goto return_results;
+ }
+
+ /* add opattrs to shadow as well, only missing attrs will actually
+ * be added; helps compatibility with older OL versions */
+ rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_add: entry failed op attrs add: %s (%d)\n",
+ rs->sr_text, rs->sr_err );
+ goto return_results;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, op->ora_e, get_assertion( op ))
+ != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+ /* Not used
+ * subentry = is_entry_subentry( op->ora_e );
+ */
+
+ /*
+ * Get the parent dn and see if the corresponding entry exists.
+ */
+ if ( be_issuffix( op->o_bd, &op->ora_e->e_nname ) ) {
+ pdn = slap_empty_bv;
+ } else {
+ dnParent( &op->ora_e->e_nname, &pdn );
+ }
+
+ wc = wt_ctx_get(op, wi);
+ if( !wc ){
+ Debug( LDAP_DEBUG_ANY, "wt_add: wt_ctx_get failed\n" );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+
+ rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ rs->sr_err = LDAP_ALREADY_EXISTS;
+ goto return_results;
+ break;
+ case WT_NOTFOUND:
+ break;
+ default:
+ /* TODO: retry handling */
+ Debug( LDAP_DEBUG_ANY,
+ "wt_add: error at wt_dn2entry() rc=%d\n", rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* get parent entry */
+ rc = wt_dn2pentry(op->o_bd, wc, &op->o_req_ndn, &p);
+ switch( rc ){
+ case 0:
+ case WT_NOTFOUND:
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_add: error at wt_dn2pentry() rc=%d\n", rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ if ( !p )
+ p = (Entry *)&slap_entry_root;
+
+ if ( !bvmatch( &pdn, &p->e_nname ) ) {
+ rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
+ op->o_tmpmemctx );
+ if ( p != (Entry *)&slap_entry_root ) {
+ rs->sr_ref = is_entry_referral( p )
+ ? get_entry_referrals( op, p )
+ : NULL;
+ wt_entry_return( p );
+ } else {
+ rs->sr_ref = NULL;
+ }
+ p = NULL;
+ Debug( LDAP_DEBUG_TRACE, "wt_add: parent does not exist\n" );
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+
+ rs->sr_err = access_allowed( op, p,
+ children, NULL, ACL_WADD, NULL );
+ if ( ! rs->sr_err ) {
+ /*
+ if ( p != (Entry *)&slap_entry_root )
+ wt_entry_return( op, p );
+ */
+ p = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "wt_add: no write access to parent\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to parent";
+ goto return_results;;
+ }
+
+ if ( p != (Entry *)&slap_entry_root ) {
+ if ( is_entry_subentry( p ) ) {
+ wt_entry_return( p );
+ p = NULL;
+ /* parent is a subentry, don't allow add */
+ Debug( LDAP_DEBUG_TRACE, "wt_add: parent is subentry\n" );
+ rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
+ rs->sr_text = "parent is a subentry";
+ goto return_results;;
+ }
+
+ if ( is_entry_alias( p ) ) {
+ wt_entry_return( p );
+ p = NULL;
+ /* parent is an alias, don't allow add */
+ Debug( LDAP_DEBUG_TRACE, "wt_add: parent is alias\n" );
+ rs->sr_err = LDAP_ALIAS_PROBLEM;
+ rs->sr_text = "parent is an alias";
+ goto return_results;;
+ }
+
+ if ( is_entry_referral( p ) ) {
+ BerVarray ref = get_entry_referrals( op, p );
+ /* parent is a referral, don't allow add */
+ rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
+ op->o_tmpmemctx );
+ rs->sr_ref = referral_rewrite( ref, &p->e_name,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ wt_entry_return( p );
+ p = NULL;
+ Debug( LDAP_DEBUG_TRACE, "wt_add: parent is referral\n" );
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+ }
+
+#if 0
+ if ( subentry ) {
+ /* FIXME: */
+ /* parent must be an administrative point of the required kind */
+ }
+#endif
+
+ /* free parent */
+ if ( p != (Entry *)&slap_entry_root ) {
+ pid = p->e_id;
+ if ( p->e_nname.bv_len ) {
+ struct berval ppdn;
+
+ /* ITS#5326: use parent's DN if differs from provided one */
+ dnParent( &op->ora_e->e_name, &ppdn );
+ if ( !dn_match( &p->e_name, &ppdn ) ) {
+ struct berval rdn;
+ struct berval newdn;
+
+ dnRdn( &op->ora_e->e_name, &rdn );
+
+ build_new_dn( &newdn, &p->e_name, &rdn, NULL );
+ if ( op->ora_e->e_name.bv_val != op->o_req_dn.bv_val )
+ ber_memfree( op->ora_e->e_name.bv_val );
+ op->ora_e->e_name = newdn;
+
+ /* FIXME: should check whether
+ * dnNormalize(newdn) == e->e_nname ... */
+ }
+ }
+
+ wt_entry_return( p );
+ }
+ p = NULL;
+
+ rs->sr_err = access_allowed( op, op->ora_e,
+ entry, NULL, ACL_WADD, NULL );
+
+ if ( ! rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE, "wt_add: no write access to entry\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to entry";
+ goto return_results;
+ }
+
+ /*
+ * Check ACL for attribute write access
+ */
+ if (!acl_check_modlist(op, op->ora_e, op->ora_modlist)) {
+ Debug( LDAP_DEBUG_TRACE, "wt_add: no write access to attribute\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to attribute";
+ goto return_results;
+ }
+
+ rc = wc->session->begin_transaction(wc->session, "isolation=read-uncommitted");
+ if( rc ) {
+ Debug( LDAP_DEBUG_TRACE, "wt_add: begin_transaction failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "begin_transaction failed";
+ goto return_results;
+ }
+ Debug( LDAP_DEBUG_TRACE, "wt_add: session id: %p\n", wc->session );
+
+ wt_next_id( op->o_bd, &eid );
+ op->ora_e->e_id = eid;
+
+ rc = wt_dn2id_add( op, wc, pid, op->ora_e );
+ if( rc ){
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_add: dn2id_add failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ switch( rc ) {
+ case WT_DUPLICATE_KEY:
+ rs->sr_err = LDAP_ALREADY_EXISTS;
+ break;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ }
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto return_results;
+ }
+
+ rc = wt_id2entry_add( op, wc, op->ora_e );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_add: id2entry_add failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ if ( rc == LDAP_ADMINLIMIT_EXCEEDED ) {
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ rs->sr_text = "entry is too big";
+ } else {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "entry store failed";
+ }
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto return_results;
+ }
+
+ /* add indices */
+ rc = wt_index_entry_add( op, wc, op->ora_e );
+ if ( rc ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<== wt_add: index add failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "index add failed";
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto return_results;
+ }
+
+ rc = wc->session->commit_transaction(wc->session, NULL);
+ if( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_add: commit_transaction failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "commit_transaction failed";
+ goto return_results;
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+
+ /* post-read */
+ if( op->o_postread ) {
+ if( postread_ctrl == NULL ) {
+ postread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if ( slap_read_controls( op, rs, op->ora_e,
+ &slap_post_read_bv, postread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE, "<=- wt_add: post-read failed!\n" );
+ if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ "wt_add: added%s id=%08lx dn=\"%s\"\n",
+ op->o_noop ? " (no-op)" : "",
+ op->ora_e->e_id, op->ora_e->e_dn );
+
+return_results:
+ send_ldap_result( op, rs );
+
+ slap_graduate_commit_csn( op );
+
+ if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
+ slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
+ }
+ return rs->sr_err;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/attr.c b/servers/slapd/back-wt/attr.c
new file mode 100644
index 0000000..70f278c
--- /dev/null
+++ b/servers/slapd/back-wt/attr.c
@@ -0,0 +1,425 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "back-wt.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+/* Find the ad, return -1 if not found,
+ * set point for insertion if ins is non-NULL
+ */
+int
+wt_attr_slot( struct wt_info *wi, AttributeDescription *ad, int *ins )
+{
+ unsigned base = 0, cursor = 0;
+ unsigned n = wi->wi_nattrs;
+ int val = 0;
+
+ while ( 0 < n ) {
+ unsigned pivot = n >> 1;
+ cursor = base + pivot;
+
+ val = SLAP_PTRCMP( ad, wi->wi_attrs[cursor]->ai_desc );
+ if ( val < 0 ) {
+ n = pivot;
+ } else if ( val > 0 ) {
+ base = cursor + 1;
+ n -= pivot + 1;
+ } else {
+ return cursor;
+ }
+ }
+ if ( ins ) {
+ if ( val > 0 )
+ ++cursor;
+ *ins = cursor;
+ }
+ return -1;
+}
+
+static int
+ainfo_insert( struct wt_info *wi, AttrInfo *a )
+{
+ int x = INT_MAX;
+ int i = wt_attr_slot( wi, a->ai_desc, &x );
+
+ /* Is it a dup? */
+ if ( i >= 0 )
+ return -1;
+
+ wi->wi_attrs = ch_realloc( wi->wi_attrs, ( wi->wi_nattrs+1 ) *
+ sizeof( AttrInfo * ));
+ if ( x < wi->wi_nattrs )
+ AC_MEMCPY( &wi->wi_attrs[x+1], &wi->wi_attrs[x],
+ ( wi->wi_nattrs - x ) * sizeof( AttrInfo *));
+ wi->wi_attrs[x] = a;
+ wi->wi_nattrs++;
+ return 0;
+}
+
+AttrInfo *
+wt_attr_mask(
+ struct wt_info *wi,
+ AttributeDescription *desc )
+{
+ int i = wt_attr_slot( wi, desc, NULL );
+ return i < 0 ? NULL : wi->wi_attrs[i];
+}
+
+int
+wt_attr_index_config(
+ struct wt_info *wi,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv,
+ struct config_reply_s *c_reply)
+{
+ int rc = 0;
+ int i;
+ slap_mask_t mask;
+ char **attrs;
+ char **indexes = NULL;
+
+ attrs = ldap_str2charray( argv[0], "," );
+
+ if( attrs == NULL ) {
+ fprintf( stderr, "%s: line %d: "
+ "no attributes specified: %s\n",
+ fname, lineno, argv[0] );
+ return LDAP_PARAM_ERROR;
+ }
+
+ if ( argc > 1 ) {
+ indexes = ldap_str2charray( argv[1], "," );
+
+ if( indexes == NULL ) {
+ fprintf( stderr, "%s: line %d: "
+ "no indexes specified: %s\n",
+ fname, lineno, argv[1] );
+ rc = LDAP_PARAM_ERROR;
+ goto done;
+ }
+ }
+
+ if( indexes == NULL ) {
+ mask = wi->wi_defaultmask;
+
+ } else {
+ mask = 0;
+
+ for ( i = 0; indexes[i] != NULL; i++ ) {
+ slap_mask_t index;
+
+ rc = slap_str2index( indexes[i], &index );
+
+ if( rc != LDAP_SUCCESS ) {
+ if ( c_reply )
+ {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "index type \"%s\" undefined", indexes[i] );
+
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ rc = LDAP_PARAM_ERROR;
+ goto done;
+ }
+
+ mask |= index;
+ }
+ }
+
+ if( !mask ) {
+ if ( c_reply )
+ {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "no indexes selected" );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ rc = LDAP_PARAM_ERROR;
+ goto done;
+ }
+
+ for ( i = 0; attrs[i] != NULL; i++ ) {
+ AttrInfo *a;
+ AttributeDescription *ad;
+ const char *text;
+#ifdef LDAP_COMP_MATCH
+ ComponentReference* cr = NULL;
+ AttrInfo *a_cr = NULL;
+#endif
+
+ if( strcasecmp( attrs[i], "default" ) == 0 ) {
+ wi->wi_defaultmask |= mask;
+ continue;
+ }
+
+#ifdef LDAP_COMP_MATCH
+ if ( is_component_reference( attrs[i] ) ) {
+ rc = extract_component_reference( attrs[i], &cr );
+ if ( rc != LDAP_SUCCESS ) {
+ if ( c_reply )
+ {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "index component reference\"%s\" undefined",
+ attrs[i] );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ goto done;
+ }
+ cr->cr_indexmask = mask;
+ /*
+ * After extracting a component reference
+ * only the name of a attribute will be remaining
+ */
+ } else {
+ cr = NULL;
+ }
+#endif
+ ad = NULL;
+ rc = slap_str2ad( attrs[i], &ad, &text );
+
+ if( rc != LDAP_SUCCESS ) {
+ if ( c_reply )
+ {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "index attribute \"%s\" undefined",
+ attrs[i] );
+
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+fail:
+#ifdef LDAP_COMP_MATCH
+ ch_free( cr );
+#endif
+ goto done;
+ }
+
+ if( ad == slap_schema.si_ad_entryDN || slap_ad_is_binary( ad ) ) {
+ if (c_reply) {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "index of attribute \"%s\" disallowed", attrs[i] );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto fail;
+ }
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) && !(
+ ad->ad_type->sat_approx
+ && ad->ad_type->sat_approx->smr_indexer
+ && ad->ad_type->sat_approx->smr_filter ) )
+ {
+ if (c_reply) {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "approx index of attribute \"%s\" disallowed", attrs[i] );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ rc = LDAP_INAPPROPRIATE_MATCHING;
+ goto fail;
+ }
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) && !(
+ ad->ad_type->sat_equality
+ && ad->ad_type->sat_equality->smr_indexer
+ && ad->ad_type->sat_equality->smr_filter ) )
+ {
+ if (c_reply) {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "equality index of attribute \"%s\" disallowed", attrs[i] );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ rc = LDAP_INAPPROPRIATE_MATCHING;
+ goto fail;
+ }
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) && !(
+ ad->ad_type->sat_substr
+ && ad->ad_type->sat_substr->smr_indexer
+ && ad->ad_type->sat_substr->smr_filter ) )
+ {
+ if (c_reply) {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "substr index of attribute \"%s\" disallowed", attrs[i] );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+ rc = LDAP_INAPPROPRIATE_MATCHING;
+ goto fail;
+ }
+
+ Debug( LDAP_DEBUG_CONFIG, "index %s 0x%04lx\n",
+ ad->ad_cname.bv_val, mask );
+
+ a = (AttrInfo *) ch_malloc( sizeof(AttrInfo) );
+
+#ifdef LDAP_COMP_MATCH
+ a->ai_cr = NULL;
+#endif
+ a->ai_desc = ad;
+
+ if ( wi->wi_flags & WT_IS_OPEN ) {
+ a->ai_indexmask = 0;
+ a->ai_newmask = mask;
+ } else {
+ a->ai_indexmask = mask;
+ a->ai_newmask = 0;
+ }
+
+#ifdef LDAP_COMP_MATCH
+ if ( cr ) {
+ a_cr = wt_attr_mask( wi, ad );
+ if ( a_cr ) {
+ /*
+ * AttrInfo is already in AVL
+ * just add the extracted component reference
+ * in the AttrInfo
+ */
+ ch_free( a );
+ rc = insert_component_reference( cr, &a_cr->ai_cr );
+ if ( rc != LDAP_SUCCESS) {
+ fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
+ rc = LDAP_PARAM_ERROR;
+ goto fail;
+ }
+ continue;
+ } else {
+ rc = insert_component_reference( cr, &a->ai_cr );
+ if ( rc != LDAP_SUCCESS) {
+ fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
+ rc = LDAP_PARAM_ERROR;
+ ch_free( a );
+ goto fail;
+ }
+ }
+ }
+#endif
+ rc = ainfo_insert( wi, a );
+ if( rc ) {
+ if ( wi->wi_flags & WT_IS_OPEN ) {
+ AttrInfo *b = wt_attr_mask( wi, ad );
+ /* If there is already an index defined for this attribute
+ * it must be replaced. Otherwise we end up with multiple
+ * olcIndex values for the same attribute */
+ if ( b->ai_indexmask & WT_INDEX_DELETING ) {
+ /* If we were editing this attr, reset it */
+ b->ai_indexmask &= ~WT_INDEX_DELETING;
+ /* If this is leftover from a previous add, commit it */
+ if ( b->ai_newmask )
+ b->ai_indexmask = b->ai_newmask;
+ b->ai_newmask = a->ai_newmask;
+ ch_free( a );
+ rc = 0;
+ continue;
+ }
+ }
+ if (c_reply) {
+ snprintf(c_reply->msg, sizeof(c_reply->msg),
+ "duplicate index definition for attr \"%s\"",
+ attrs[i] );
+ fprintf( stderr, "%s: line %d: %s\n",
+ fname, lineno, c_reply->msg );
+ }
+
+ rc = LDAP_PARAM_ERROR;
+ goto done;
+ }
+ }
+
+done:
+ ldap_charray_free( attrs );
+ if ( indexes != NULL ) ldap_charray_free( indexes );
+
+ return rc;
+}
+
+static int
+wt_attr_index_unparser( void *v1, void *v2 )
+{
+ AttrInfo *ai = v1;
+ BerVarray *bva = v2;
+ struct berval bv;
+ char *ptr;
+
+ slap_index2bvlen( ai->ai_indexmask, &bv );
+ if ( bv.bv_len ) {
+ bv.bv_len += ai->ai_desc->ad_cname.bv_len + 1;
+ ptr = ch_malloc( bv.bv_len+1 );
+ bv.bv_val = lutil_strcopy(ptr,
+ (const char*)ai->ai_desc->ad_cname.bv_val );
+ *bv.bv_val++ = ' ';
+ slap_index2bv( ai->ai_indexmask, &bv );
+ bv.bv_val = ptr;
+ ber_bvarray_add( bva, &bv );
+ }
+ return 0;
+}
+
+static AttributeDescription addef = { NULL, NULL, BER_BVC("default") };
+static AttrInfo aidef = { &addef };
+
+void
+wt_attr_index_unparse( struct wt_info *wi, BerVarray *bva )
+{
+ int i;
+
+ if ( wi->wi_defaultmask ) {
+ aidef.ai_indexmask = wi->wi_defaultmask;
+ wt_attr_index_unparser( &aidef, bva );
+ }
+ for ( i=0; i<wi->wi_nattrs; i++ )
+ wt_attr_index_unparser( wi->wi_attrs[i], bva );
+}
+
+void
+wt_attr_info_free( AttrInfo *ai )
+{
+#ifdef LDAP_COMP_MATCH
+ free( ai->ai_cr );
+#endif
+ free( ai );
+}
+
+void
+wt_attr_index_destroy( struct wt_info *wi )
+{
+ int i;
+
+ for ( i=0; i<wi->wi_nattrs; i++ )
+ wt_attr_info_free( wi->wi_attrs[i] );
+
+ free( wi->wi_attrs );
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/back-wt.h b/servers/slapd/back-wt/back-wt.h
new file mode 100644
index 0000000..386dd85
--- /dev/null
+++ b/servers/slapd/back-wt/back-wt.h
@@ -0,0 +1,120 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#ifndef _BACK_WT_H_
+#define _BACK_WT_H_
+
+#include <portable.h>
+
+#include <ac/errno.h>
+#include <sys/stat.h>
+
+#include "slap.h"
+#include "wiredtiger.h"
+
+/* The default search IDL stack cache depth */
+#define DEFAULT_SEARCH_STACK_DEPTH 16
+
+#define WT_CONFIG_MAX 2048
+
+struct wt_info {
+ WT_CONNECTION *wi_conn;
+ WT_CONNECTION *wi_cache;
+ char *wi_home;
+ char *wi_config;
+ ID wi_lastid;
+
+ slap_mask_t wi_defaultmask;
+ int wi_nattrs;
+ struct wt_attrinfo **wi_attrs;
+ void *wi_search_stack;
+ int wi_search_stack_depth;
+
+ struct re_s *wi_index_task;
+
+ int wi_flags;
+#define WT_IS_OPEN 0x01
+#define WT_OPEN_INDEX 0x02
+#define WT_DEL_INDEX 0x08
+#define WT_RE_OPEN 0x10
+#define WT_NEED_UPGRADE 0x20
+#define WT_USE_IDLCACHE 0x40
+};
+
+#define WT_TABLE_ID2ENTRY "table:id2entry"
+#define WT_TABLE_DN2ID "table:dn2id"
+
+#define WT_INDEX_DN "index:id2entry:dn"
+#define WT_INDEX_NDN "index:dn2id:ndn"
+#define WT_INDEX_PID "index:dn2id:pid"
+/* Currently, revdn is primary key, the revdn index is obsolete. */
+#define WT_INDEX_REVDN "index:dn2id:revdn"
+
+/* table for cache */
+#define WT_TABLE_IDLCACHE "table:idlcache"
+
+#define ITEMzero(item) (memset((item), 0, sizeof(WT_ITEM)))
+#define ITEM2bv(item,bv) ((bv)->bv_val = (item)->data, \
+ (bv)->bv_len = (item)->size)
+#define bv2ITEM(bv,item) ((item)->data = (bv)->bv_val, \
+ (item)->size = (bv)->bv_len )
+
+#define WT_INDEX_CACHE_SIZE 1024
+
+typedef struct {
+ WT_SESSION *session;
+ int is_begin_transaction;
+ WT_CURSOR *dn2id;
+ WT_CURSOR *dn2id_w;
+ WT_CURSOR *dn2id_ndn;
+ WT_CURSOR *dn2entry;
+ WT_CURSOR *id2entry;
+ WT_CURSOR *id2entry_add;
+ WT_CURSOR *id2entry_update;
+ WT_SESSION *idlcache_session;
+ WT_CURSOR *index_pid;
+} wt_ctx;
+
+/* for the cache of attribute information (which are indexed, etc.) */
+typedef struct wt_attrinfo {
+ AttributeDescription *ai_desc; /* attribute description cn;lang-en */
+ slap_mask_t ai_indexmask; /* how the attr is indexed */
+ slap_mask_t ai_newmask; /* new settings to replace old mask */
+ #ifdef LDAP_COMP_MATCH
+ ComponentReference* ai_cr; /*component indexing*/
+ #endif
+} AttrInfo;
+
+/* These flags must not clash with SLAP_INDEX flags or ops in slap.h! */
+#define WT_INDEX_DELETING 0x8000U /* index is being modified */
+#define WT_INDEX_UPDATE_OP 0x03 /* performing an index update */
+
+#include "proto-wt.h"
+
+#endif /* _BACK_WT_H_ */
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/bind.c b/servers/slapd/back-wt/bind.c
new file mode 100644
index 0000000..43abe87
--- /dev/null
+++ b/servers/slapd/back-wt/bind.c
@@ -0,0 +1,150 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include "back-wt.h"
+#include "slap-config.h"
+
+int
+wt_bind( Operation *op, SlapReply *rs )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ wt_ctx *wc;
+ int rc;
+ Entry *e = NULL;
+ Attribute *a;
+ AttributeDescription *password = slap_schema.si_ad_userPassword;
+
+ Debug( LDAP_DEBUG_ARGS, "==> wt_bind: dn: %s\n",
+ op->o_req_dn.bv_val );
+
+ /* allow noauth binds */
+ switch ( be_rootdn_bind( op, NULL ) ) {
+ case LDAP_SUCCESS:
+ /* frontend will send result */
+ return rs->sr_err = LDAP_SUCCESS;
+
+ default:
+ /* give the database a chance */
+ /* NOTE: this behavior departs from that of other backends,
+ * since the others, in case of password checking failure
+ * do not give the database a chance. If an entry with
+ * rootdn's name does not exist in the database the result
+ * will be the same. See ITS#4962 for discussion. */
+ break;
+ }
+
+ wc = wt_ctx_get(op, wi);
+ if( !wc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_bind: wt_ctx_get failed\n" );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+
+ /* get entry */
+ rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+
+ ber_dupbv( &op->oq_bind.rb_edn, &e->e_name );
+
+ /* check for deleted */
+ if ( is_entry_subentry( e ) ) {
+ /* entry is an subentry, don't allow bind */
+ Debug( LDAP_DEBUG_TRACE, "entry is subentry\n" );
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ if ( is_entry_alias( e ) ) {
+ /* entry is an alias, don't allow bind */
+ Debug( LDAP_DEBUG_TRACE, "entry is alias\n" );
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ if ( is_entry_referral( e ) ) {
+ Debug( LDAP_DEBUG_TRACE, "entry is referral\n" );
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ switch ( op->oq_bind.rb_method ) {
+ case LDAP_AUTH_SIMPLE:
+ a = attr_find( e->e_attrs, password );
+ if ( a == NULL ) {
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ if ( slap_passwd_check( op, e, a, &op->oq_bind.rb_cred,
+ &rs->sr_text ) != 0 )
+ {
+ /* failure; stop front end from sending result */
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+ rs->sr_err = 0;
+ break;
+
+ default:
+ rs->sr_err = LDAP_STRONG_AUTH_NOT_SUPPORTED;
+ rs->sr_text = "authentication method not supported";
+ }
+
+done:
+ /* free entry */
+ if (e) {
+ wt_entry_return(e);
+ }
+ if (rs->sr_err) {
+ send_ldap_result( op, rs );
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+ }
+ return rs->sr_err;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/cache.c b/servers/slapd/back-wt/cache.c
new file mode 100644
index 0000000..ee393a0
--- /dev/null
+++ b/servers/slapd/back-wt/cache.c
@@ -0,0 +1,231 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "slap-config.h"
+#include "idl.h"
+
+int wt_idlcache_get(wt_ctx *wc, struct berval *ndn, int scope, ID *ids)
+{
+ int rc = 0;
+ WT_ITEM item;
+ WT_SESSION *session = wc->idlcache_session;
+ WT_CURSOR *cursor = NULL;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> wt_idlcache_get(\"%s\", %d)\n",
+ ndn->bv_val, scope );
+
+ rc = session->open_cursor(session, WT_TABLE_IDLCACHE, NULL,
+ NULL, &cursor);
+ if(rc){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_idlcache_get: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return rc;
+ }
+ cursor->set_key(cursor, ndn->bv_val, (int8_t)scope);
+ rc = cursor->search(cursor);
+ switch( rc ){
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ Debug(LDAP_DEBUG_TRACE, "<= wt_idlcache_get: miss\n" );
+ goto done;
+ default:
+ Debug( LDAP_DEBUG_ANY, "<= wt_idlcache_get: search failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rc = 0;
+ goto done;
+ }
+ rc = cursor->get_value(cursor, &item);
+ if (rc) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_idlcache_get: get_value failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ if (item.size == 0) {
+ Debug(LDAP_DEBUG_TRACE, "<= wt_idlcache_get: updating\n");
+ rc = WT_NOTFOUND;
+ goto done;
+ }
+ memcpy(ids, item.data, item.size);
+
+ Debug(LDAP_DEBUG_TRACE,
+ "<= wt_idlcache_get: hit id=%ld first=%ld last=%ld\n",
+ (long)ids[0],
+ (long)WT_IDL_FIRST(ids),
+ (long)WT_IDL_LAST(ids));
+done:
+ if(cursor) {
+ cursor->close(cursor);
+ }
+ return rc;
+}
+
+int wt_idlcache_set(wt_ctx *wc, struct berval *ndn, int scope, ID *ids)
+{
+ int rc = 0;
+ WT_ITEM item;
+ WT_SESSION *session = wc->idlcache_session;
+ WT_CURSOR *cursor = NULL;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> wt_idlcache_set(\"%s\", %d)\n",
+ ndn->bv_val, scope );
+
+ item.size = WT_IDL_SIZEOF(ids);
+ item.data = ids;
+
+ rc = session->open_cursor(session, WT_TABLE_IDLCACHE, NULL,
+ "overwrite=false", &cursor);
+ if(rc){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_idlcache_set: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return rc;
+ }
+ cursor->set_key(cursor, ndn->bv_val, (int8_t)scope);
+ cursor->set_value(cursor, &item);
+ rc = cursor->update(cursor);
+ switch( rc ){
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ // updating cache by another thread
+ goto done;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_idlcache_set: update failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ "<= wt_idlcache_set: set idl size=%ld\n",
+ (long)ids[0]);
+done:
+ if(cursor) {
+ cursor->close(cursor);
+ }
+ return rc;
+}
+
+int wt_idlcache_begin(wt_ctx *wc, struct berval *ndn, int scope)
+{
+ int rc = 0;
+ WT_ITEM item;
+ WT_SESSION *session = wc->idlcache_session;
+ WT_CURSOR *cursor = NULL;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> wt_idlcache_begin(\"%s\", %d)\n",
+ ndn->bv_val, scope );
+
+ item.size = 0;
+ item.data = "";
+
+ rc = session->open_cursor(session, WT_TABLE_IDLCACHE, NULL,
+ "overwrite=true", &cursor);
+ if(rc){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_idlcache_begin: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return rc;
+ }
+ cursor->set_key(cursor, ndn->bv_val, (int8_t)scope);
+ cursor->set_value(cursor, &item);
+ rc = cursor->update(cursor);
+ if(rc){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_idlcache_begin: update failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ "<= wt_idlcache_begin: set updating\n" );
+
+done:
+ if(cursor) {
+ cursor->close(cursor);
+ }
+ return rc;
+}
+
+int wt_idlcache_clear(Operation *op, wt_ctx *wc, struct berval *ndn)
+{
+ BackendDB *be = op->o_bd;
+ int rc = 0;
+ struct berval pdn = *ndn;
+ WT_SESSION *session = wc->idlcache_session;
+ WT_CURSOR *cursor = NULL;
+ int level = 0;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> wt_idlcache_clear(\"%s\")\n",
+ ndn->bv_val );
+
+ if (be_issuffix( be, ndn )) {
+ return 0;
+ }
+
+ rc = session->open_cursor(session, WT_TABLE_IDLCACHE, NULL,
+ NULL, &cursor);
+ if(rc){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_idlcache_clear: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return rc;
+ }
+
+ do {
+ dnParent( &pdn, &pdn );
+ if (level == 0) {
+ /* clear only parent level cache */
+ cursor->set_key(cursor, pdn.bv_val, (int8_t)LDAP_SCOPE_ONE);
+ cursor->remove(cursor);
+ }
+ cursor->set_key(cursor, pdn.bv_val, (int8_t)LDAP_SCOPE_SUB);
+ cursor->remove(cursor);
+ cursor->set_key(cursor, pdn.bv_val, (int8_t)LDAP_SCOPE_CHILDREN);
+ cursor->remove(cursor);
+ level++;
+ }while(!be_issuffix( be, &pdn ));
+
+ if(cursor) {
+ cursor->close(cursor);
+ }
+ return 0;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/compare.c b/servers/slapd/back-wt/compare.c
new file mode 100644
index 0000000..038b273
--- /dev/null
+++ b/servers/slapd/back-wt/compare.c
@@ -0,0 +1,154 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-wt.h"
+#include "slap-config.h"
+
+int
+wt_compare( Operation *op, SlapReply *rs )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ Entry *e = NULL;
+ int manageDSAit = get_manageDSAit( op );
+ int rc;
+ wt_ctx *wc = NULL;
+
+ Debug( LDAP_DEBUG_ARGS, "==> wt_compare: %s\n",
+ op->o_req_dn.bv_val );
+
+ wc = wt_ctx_get(op, wi);
+ if( !wc ){
+ Debug( LDAP_DEBUG_ANY, "wt_compare: wt_ctx_get failed\n" );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+
+ rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ case WT_NOTFOUND:
+ break;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ if ( rc == WT_NOTFOUND ||
+ (!manageDSAit && e && is_entry_glue( e ) )) {
+
+ if ( !e ) {
+ rc = wt_dn2aentry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto return_results;
+ default:
+ Debug( LDAP_DEBUG_ANY, "wt_compare: wt_dn2aentry failed (%d)\n",
+ rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ }
+
+ /* return referral only if "disclose" is granted on the object */
+ if ( ! access_allowed( op, e, slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL ) )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ } else {
+ rs->sr_matched = ch_strdup( e->e_dn );
+ if ( is_entry_referral( e )) {
+ BerVarray ref = get_entry_referrals( op, e );
+ rs->sr_ref = referral_rewrite( ref,
+ &e->e_name,
+ &op->o_req_dn,
+ LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ } else {
+ rs->sr_ref = NULL;
+ }
+ rs->sr_err = LDAP_REFERRAL;
+ }
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ if (!manageDSAit && is_entry_referral( e ) ) {
+ /* return referral only if "disclose" is granted on the object */
+ if ( !access_allowed( op, e, slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL ) )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ } else {
+ /* entry is a referral, don't allow compare */
+ rs->sr_ref = get_entry_referrals( op, e );
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_matched = e->e_name.bv_val;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "entry is referral\n" );
+
+ send_ldap_result( op, rs );
+
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ rs->sr_matched = NULL;
+ goto done;
+ }
+
+ rs->sr_err = slap_compare_entry( op, e, op->orc_ava );
+
+return_results:
+ send_ldap_result( op, rs );
+
+ switch ( rs->sr_err ) {
+ case LDAP_COMPARE_FALSE:
+ case LDAP_COMPARE_TRUE:
+ rs->sr_err = LDAP_SUCCESS;
+ break;
+ }
+
+done:
+ if ( e != NULL ) {
+ wt_entry_return( e );
+ }
+ return rs->sr_err;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/config.c b/servers/slapd/back-wt/config.c
new file mode 100644
index 0000000..804e25b
--- /dev/null
+++ b/servers/slapd/back-wt/config.c
@@ -0,0 +1,209 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "slap-config.h"
+
+#include "lutil.h"
+#include "ldap_rq.h"
+
+static ConfigDriver wt_cf_gen;
+
+enum {
+ WT_DIRECTORY = 1,
+ WT_CONFIG,
+ WT_INDEX,
+ WT_MODE,
+ WT_IDLCACHE,
+};
+
+static ConfigTable wtcfg[] = {
+ { "directory", "dir", 2, 2, 0, ARG_STRING|ARG_MAGIC|WT_DIRECTORY,
+ wt_cf_gen, "( OLcfgDbAt:0.1 NAME 'olcDbDirectory' "
+ "DESC 'Directory for database content' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "index", "attr> <[pres,eq,approx,sub]", 2, 3, 0, ARG_MAGIC|WT_INDEX,
+ wt_cf_gen, "( OLcfgDbAt:0.2 NAME 'olcDbIndex' "
+ "DESC 'Attribute index parameters' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "mode", "mode", 2, 2, 0, ARG_MAGIC|WT_MODE,
+ wt_cf_gen, "( OLcfgDbAt:0.3 NAME 'olcDbMode' "
+ "DESC 'Unix permissions of database files' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "wtconfig", "config", 2, 2, 0, ARG_STRING|ARG_MAGIC|WT_CONFIG,
+ wt_cf_gen, "( OLcfgDbAt:13.1 NAME 'olcWtConfig' "
+ "DESC 'Configuration for WiredTiger' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "idlcache", NULL, 1, 2, 0, ARG_ON_OFF|ARG_MAGIC|WT_IDLCACHE,
+ wt_cf_gen, "( OLcfgDbAt:13.2 NAME 'olcIDLcache' "
+ "DESC 'enable IDL cache' "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED,
+ NULL, NULL, NULL, NULL }
+};
+
+static ConfigOCs wtocs[] = {
+ { "( OLcfgDbOc:13.1 "
+ "NAME 'olcWtConfig' "
+ "DESC 'Wt backend configuration' "
+ "SUP olcDatabaseConfig "
+ "MUST olcDbDirectory "
+ "MAY ( olcWtConfig $ olcDbIndex $ olcDbMode $ olcIDLcache) )",
+ Cft_Database, wtcfg },
+ { NULL, 0, NULL }
+};
+
+/* reindex entries on the fly */
+static void *
+wt_online_index( void *ctx, void *arg )
+{
+ // Not implement yet
+ return NULL;
+}
+
+/* Cleanup loose ends after Modify completes */
+static int
+wt_cf_cleanup( ConfigArgs *c )
+{
+ // Not implement yet
+ return 0;
+}
+
+static int
+wt_cf_gen( ConfigArgs *c )
+{
+ struct wt_info *wi = (struct wt_info *) c->be->be_private;
+ int rc;
+
+ if( c->op == SLAP_CONFIG_EMIT ) {
+ rc = 0;
+ switch( c->type ) {
+ case WT_DIRECTORY:
+ if ( wi->wi_home ) {
+ c->value_string = ch_strdup( wi->wi_home );
+ } else {
+ rc = 1;
+ }
+ break;
+ case WT_INDEX:
+ wt_attr_index_unparse( wi, &c->rvalue_vals );
+ if ( !c->rvalue_vals ) rc = 1;
+ break;
+ case WT_IDLCACHE:
+ if ( wi->wi_flags & WT_USE_IDLCACHE) {
+ c->value_int = 1;
+ }
+ break;
+ }
+ return rc;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ rc = 0;
+ return rc;
+ }
+
+ switch( c->type ) {
+ case WT_DIRECTORY:
+ ch_free( wi->wi_home );
+ wi->wi_home = c->value_string;
+ break;
+ case WT_CONFIG:
+ if(strlen(wi->wi_config) + 1 + strlen(c->value_string) > WT_CONFIG_MAX){
+ fprintf( stderr, "%s: "
+ "\"wtconfig\" are too long. Increase WT_CONFIG_MAX or you may realloc the buffer.\n",
+ c->log );
+ return 1;
+ }
+ /* size of wi->wi_config is WT_CONFIG_MAX + 1, it's guaranteed with NUL-terminate. */
+ strcat(wi->wi_config, ",");
+ strcat(wi->wi_config, c->value_string);
+ break;
+
+ case WT_INDEX:
+ rc = wt_attr_index_config( wi, c->fname, c->lineno,
+ c->argc - 1, &c->argv[1], &c->reply);
+
+ if( rc != LDAP_SUCCESS ) return 1;
+ wi->wi_flags |= WT_OPEN_INDEX;
+
+ if ( wi->wi_flags & WT_IS_OPEN ) {
+ config_push_cleanup( c, wt_cf_cleanup );
+
+ if ( !wi->wi_index_task ) {
+ /* Start the task as soon as we finish here. Set a long
+ * interval (10 hours) so that it only gets scheduled once.
+ */
+ if ( c->be->be_suffix == NULL || BER_BVISNULL( &c->be->be_suffix[0] ) ) {
+ fprintf( stderr, "%s: "
+ "\"index\" must occur after \"suffix\".\n",
+ c->log );
+ return 1;
+ }
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ wi->wi_index_task = ldap_pvt_runqueue_insert(&slapd_rq, 36000,
+ wt_online_index, c->be,
+ LDAP_XSTRING(wt_online_index),
+ c->be->be_suffix[0].bv_val );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+ }
+ break;
+
+ case WT_MODE:
+ fprintf( stderr, "%s: "
+ "back-wt does not support \"mode\" option. use umask instead.\n",
+ c->log );
+ return 1;
+
+ case WT_IDLCACHE:
+ if ( c->value_int ) {
+ wi->wi_flags |= WT_USE_IDLCACHE;
+ } else {
+ wi->wi_flags &= ~WT_USE_IDLCACHE;
+ }
+ break;
+ }
+ return LDAP_SUCCESS;
+}
+
+int wt_back_init_cf( BackendInfo *bi )
+{
+ int rc;
+ bi->bi_cf_ocs = wtocs;
+
+ rc = config_register_schema( wtcfg, wtocs );
+ if ( rc ) return rc;
+ return 0;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/ctx.c b/servers/slapd/back-wt/ctx.c
new file mode 100644
index 0000000..de6578e
--- /dev/null
+++ b/servers/slapd/back-wt/ctx.c
@@ -0,0 +1,117 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "back-wt.h"
+#include "slap-config.h"
+
+wt_ctx *
+wt_ctx_init(struct wt_info *wi)
+{
+ int rc;
+ wt_ctx *wc;
+
+ wc = ch_malloc( sizeof( wt_ctx ) );
+ if( !wc ) {
+ Debug( LDAP_DEBUG_ANY, "wt_ctx_init: cannot allocate memory\n" );
+ return NULL;
+ }
+
+ memset(wc, 0, sizeof(wt_ctx));
+
+ rc = wi->wi_conn->open_session(wi->wi_conn, NULL, NULL, &wc->session);
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY, "wt_ctx_init: open_session error %s(%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return NULL;
+ }
+
+ /* readonly mode */
+ if (!wi->wi_cache) {
+ return wc;
+ }
+
+ rc = wi->wi_cache->open_session(wi->wi_cache, NULL, NULL, &wc->idlcache_session);
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_ctx_init: cannot open idlcache session %s(%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return NULL;
+ }
+
+ return wc;
+}
+
+void
+wt_ctx_free( void *key, void *data )
+{
+ wt_ctx *wc = data;
+
+ if(wc->session){
+ /*
+ * The session will close automatically when db closing.
+ * We can close session here, but it's require to check db
+ * status, otherwise it will cause SEGV.
+ */
+ /*
+ if(IS_DB_OPEN) {
+ wc->session->close(wc->session, NULL);
+ }
+ */
+ wc->session = NULL;
+ }
+
+ ch_free(wc);
+}
+
+wt_ctx *
+wt_ctx_get(Operation *op, struct wt_info *wi){
+ int rc;
+ void *data;
+ wt_ctx *wc = NULL;
+
+ rc = ldap_pvt_thread_pool_getkey(op->o_threadctx,
+ wi, &data, NULL );
+ if( rc ){
+ wc = wt_ctx_init(wi);
+ if( !wc ) {
+ Debug( LDAP_DEBUG_ANY, "wt_ctx: wt_ctx_init failed\n" );
+ return NULL;
+ }
+ rc = ldap_pvt_thread_pool_setkey( op->o_threadctx,
+ wi, wc, wt_ctx_free,
+ NULL, NULL );
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY, "wt_ctx: setkey error(%d)\n",
+ rc );
+ return NULL;
+ }
+ return wc;
+ }
+ return (wt_ctx *)data;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/delete.c b/servers/slapd/back-wt/delete.c
new file mode 100644
index 0000000..9673662
--- /dev/null
+++ b/servers/slapd/back-wt/delete.c
@@ -0,0 +1,419 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-wt.h"
+#include "slap-config.h"
+
+int
+wt_delete( Operation *op, SlapReply *rs )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ struct berval pdn = {0, NULL};
+ Entry *e = NULL;
+ Entry *p = NULL;
+ int manageDSAit = get_manageDSAit( op );
+ AttributeDescription *children = slap_schema.si_ad_children;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+
+ LDAPControl **preread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+
+ wt_ctx *wc;
+ int rc;
+
+ int parent_is_glue = 0;
+ int parent_is_leaf = 0;
+
+ Debug( LDAP_DEBUG_ARGS, "==> wt_delete: %s\n",
+ op->o_req_dn.bv_val );
+
+ if( op->o_txnSpec && txn_preop( op, rs ))
+ return rs->sr_err;
+
+ ctrls[num_ctrls] = 0;
+ rs->sr_text = NULL;
+
+ wc = wt_ctx_get(op, wi);
+ if( !wc ){
+ Debug( LDAP_DEBUG_TRACE, "wt_delete: wt_ctx_get failed\n" );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+/* allocate CSN */
+ if ( BER_BVISNULL( &op->o_csn ) ) {
+ struct berval csn;
+ char csnbuf[LDAP_PVT_CSNSTR_BUFSIZE];
+
+ csn.bv_val = csnbuf;
+ csn.bv_len = sizeof(csnbuf);
+ slap_get_csn( op, &csn, 1 );
+ }
+
+ if ( !be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
+ dnParent( &op->o_req_ndn, &pdn );
+ }
+
+ /* get parent */
+ rc = wt_dn2entry(op->o_bd, wc, &pdn, &p);
+ switch( rc ) {
+ case 0:
+ case WT_NOTFOUND:
+ break;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ Debug( LDAP_DEBUG_ANY,
+ "wt_delete: error at wt_dn2entry() rc=%d\n", rc );
+ goto return_results;
+ }
+
+ if ( rc == WT_NOTFOUND && pdn.bv_len != 0 ) {
+ Debug( LDAP_DEBUG_ARGS,
+ "<== wt_delete: parent not found %s\n", op->o_req_dn.bv_val );
+ rc = wt_dn2aentry(op->o_bd, wc, &op->o_req_ndn, &e);
+ Debug( LDAP_DEBUG_ARGS, "<== wt_delete: rc=%d\n", rc );
+
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto return_results;
+ default:
+ Debug( LDAP_DEBUG_ANY, "wt_delete: wt_dn2aentry failed (%d)\n", rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ if ( e && !BER_BVISEMPTY( &e->e_name )) {
+ rs->sr_matched = ch_strdup( e->e_name.bv_val );
+ if ( is_entry_referral( e )) {
+ BerVarray ref = get_entry_referrals( op, e );
+ rs->sr_ref = referral_rewrite( ref, &e->e_name,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ } else {
+ rs->sr_ref = NULL;
+ }
+ } else {
+ rs->sr_ref = referral_rewrite( default_referral, NULL,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ }
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+
+ /* get entry */
+ rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ break;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ Debug( LDAP_DEBUG_ANY,
+ "wt_delete: error at wt_dn2entry() rc=%d\n", rc );
+ goto return_results;
+ }
+
+ /* FIXME : dn2entry() should return non-glue entry */
+ if (rc == WT_NOTFOUND ||
+ ( !manageDSAit && e && is_entry_glue( e ) )) {
+ if ( !e ) {
+ Debug( LDAP_DEBUG_ARGS,
+ "<== wt_delete: no such object %s\n",
+ op->o_req_dn.bv_val);
+ rc = wt_dn2aentry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto return_results;
+ default:
+ Debug( LDAP_DEBUG_ANY, "wt_delete: wt_dn2aentry failed (%d)\n", rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ }
+
+ rs->sr_matched = ch_strdup( e->e_dn );
+ if ( is_entry_referral( e )) {
+ BerVarray ref = get_entry_referrals( op, e );
+ rs->sr_ref = referral_rewrite( ref, &e->e_name,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ } else {
+ rs->sr_ref = NULL;
+ }
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+
+ if ( pdn.bv_len != 0 ) {
+ /* check parent for "children" acl */
+ rs->sr_err = access_allowed( op, p,
+ children, NULL, ACL_WDEL, NULL );
+
+ if ( !rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_delete: no write access to parent\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to parent";
+ goto return_results;
+ }
+
+ } else {
+ /* no parent, must be root to delete */
+ if( ! be_isroot( op ) ) {
+ if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv )
+ || be_shadow_update( op ) ) {
+ p = (Entry *)&slap_entry_root;
+
+ /* check parent for "children" acl */
+ rs->sr_err = access_allowed( op, p,
+ children, NULL, ACL_WDEL, NULL );
+
+ p = NULL;
+
+ if ( !rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_delete: no access to parent\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to parent";
+ goto return_results;
+ }
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_delete: no parent and not root\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto return_results;
+ }
+ }
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+ rs->sr_err = access_allowed( op, e,
+ entry, NULL, ACL_WDEL, NULL );
+ if ( !rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_delete: no write access to entry\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to entry";
+ goto return_results;
+ }
+
+ if ( !manageDSAit && is_entry_referral( e ) ) {
+ /* entry is a referral, don't allow delete */
+ rs->sr_ref = get_entry_referrals( op, e );
+
+ Debug( LDAP_DEBUG_TRACE, "wt_delete: entry is referral\n" );
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_matched = ch_strdup( e->e_name.bv_val );
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+
+ /* pre-read */
+ if( op->o_preread ) {
+ if( preread_ctrl == NULL ) {
+ preread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if( slap_read_controls( op, rs, e,
+ &slap_pre_read_bv, preread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_delete: pre-read failed!\n" );
+ if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ /* Can't do it if we have kids */
+ rc = wt_dn2id_has_children( op, wc, e->e_id );
+ if( rc != WT_NOTFOUND ) {
+ switch( rc ) {
+ case 0:
+ Debug(LDAP_DEBUG_ARGS,
+ "<== wt_delete: non-leaf %s\n", op->o_req_dn.bv_val );
+ rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
+ rs->sr_text = "subordinate objects must be deleted first";
+ break;
+ default:
+ Debug(LDAP_DEBUG_ARGS,
+ "<== wt_delete: has_children failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ }
+ goto return_results;
+ }
+
+ /* begin transaction */
+ rc = wc->session->begin_transaction(wc->session, NULL);
+ if( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_delete: begin_transaction failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "begin_transaction failed";
+ goto return_results;
+ }
+
+ /* delete from dn2id */
+ rc = wt_dn2id_delete( op, wc, &op->o_req_ndn);
+ if ( rc ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<== wt_delete: dn2id failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "dn2id delete failed";
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto return_results;
+ }
+
+ /* delete indices for old attributes */
+ rc = wt_index_entry_del( op, wc, e );
+ if ( rc ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<== wt_delete: index delete failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "index delete failed";
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto return_results;
+ }
+
+ /* fixup delete CSN */
+ if ( !SLAP_SHADOW( op->o_bd )) {
+ struct berval vals[2];
+
+ assert( !BER_BVISNULL( &op->o_csn ) );
+ vals[0] = op->o_csn;
+ BER_BVZERO( &vals[1] );
+ rs->sr_err = wt_index_values( op, wc, slap_schema.si_ad_entryCSN,
+ vals, 0, SLAP_INDEX_ADD_OP );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ rs->sr_text = "entryCSN index update failed";
+ rs->sr_err = LDAP_OTHER;
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto return_results;
+ }
+ }
+
+ /* delete from id2entry */
+ rc = wt_id2entry_delete( op, wc, e );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_delete: id2entry failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "entry delete failed";
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto return_results;
+ }
+
+ if ( pdn.bv_len != 0 ) {
+ // TODO: glue entry
+ }
+
+ rc = wc->session->commit_transaction(wc->session, NULL);
+ if( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_delete: commit_transaction failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "commit_transaction failed";
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_delete: deleted%s id=%08lx dn=\"%s\"\n",
+ op->o_noop ? " (no-op)" : "", e->e_id, op->o_req_dn.bv_val );
+
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ if( num_ctrls ) {
+ rs->sr_ctrls = ctrls;
+ }
+
+return_results:
+ if ( rs->sr_err == LDAP_SUCCESS && parent_is_glue && parent_is_leaf ) {
+ op->o_delete_glue_parent = 1;
+ }
+
+ if ( p != NULL ) {
+ wt_entry_return( p );
+ }
+
+ /* free entry */
+ if( e != NULL ) {
+ wt_entry_return( e );
+ }
+
+ send_ldap_result( op, rs );
+ slap_graduate_commit_csn( op );
+
+ if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
+ slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
+ }
+
+ /* TODO: checkpoint */
+
+ return rs->sr_err;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/dn2entry.c b/servers/slapd/back-wt/dn2entry.c
new file mode 100644
index 0000000..84cb13d
--- /dev/null
+++ b/servers/slapd/back-wt/dn2entry.c
@@ -0,0 +1,176 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "slap-config.h"
+
+/*
+ * dn2entry - look up dn in the db and return the corresponding entry.
+ * No longer return closest ancestor, see wt_dn2pentry().
+ */
+int wt_dn2entry( BackendDB *be,
+ wt_ctx *wc,
+ struct berval *ndn,
+ Entry **ep ){
+ uint64_t id;
+ WT_ITEM item;
+ EntryHeader eh;
+ int rc;
+ int eoff;
+ Entry *e = NULL;
+ WT_SESSION *session = wc->session;
+ WT_CURSOR *cursor = wc->dn2entry;
+
+ if( ndn->bv_len == 0 ){
+ /* empty dn */
+ e = entry_alloc();
+ ber_dupbv(&e->e_nname, ndn);
+ *ep = e;
+ return LDAP_SUCCESS;
+ }
+
+ if(!cursor){
+ rc = session->open_cursor(session,
+ WT_INDEX_DN"(id, entry)",
+ NULL, NULL, &cursor);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2entry: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ wc->dn2entry = cursor;
+ }
+
+ cursor->set_key(cursor, ndn->bv_val);
+ rc = cursor->search(cursor);
+ switch( rc ){
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ goto done;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2entry: search failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ cursor->get_value(cursor, &id, &item);
+ rc = wt_entry_header( &item, &eh );
+
+ eoff = eh.data - (char *)item.data;
+ eh.bv.bv_len = eh.nvals * sizeof( struct berval ) + item.size;
+ eh.bv.bv_val = ch_malloc( eh.bv.bv_len );
+ memset(eh.bv.bv_val, 0xff, eh.bv.bv_len);
+ eh.data = eh.bv.bv_val + eh.nvals * sizeof( struct berval );
+ memcpy(eh.data, item.data, item.size);
+ eh.data += eoff;
+ rc = entry_decode( &eh, &e );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2entry: entry decode error: %d\n", rc );
+ goto done;
+ }
+
+ e->e_id = id;
+ *ep = e;
+
+done:
+
+#ifdef WT_CURSOR_CACHE
+ if(cursor){
+ cursor->reset(cursor);
+ }
+#else
+ if(cursor){
+ cursor->close(cursor);
+ wc->dn2entry = NULL;
+ }
+#endif
+ return rc;
+}
+
+/* dn2pentry - return parent entry */
+int wt_dn2pentry( BackendDB *be,
+ wt_ctx *wc,
+ struct berval *ndn,
+ Entry **ep ){
+ Entry *e = NULL;
+ struct berval pdn;
+ int rc;
+
+ if (be_issuffix( be, ndn )) {
+ *ep = NULL;
+ return WT_NOTFOUND;
+ }
+
+ dnParent( ndn, &pdn );
+ rc = wt_dn2entry(be, wc, &pdn, &e);
+ *ep = e;
+ return rc;
+}
+
+/* dn2aentry - return ancestor entry */
+int wt_dn2aentry( BackendDB *be,
+ wt_ctx *wc,
+ struct berval *ndn,
+ Entry **ep ) {
+ Entry *e = NULL;
+ struct berval pdn;
+ int rc;
+
+ if (be_issuffix( be, ndn )) {
+ *ep = NULL;
+ return 0;
+ }
+
+ dnParent( ndn, &pdn );
+ rc = wt_dn2entry(be, wc, &pdn, &e);
+ switch( rc ) {
+ case 0:
+ *ep = e;
+ break;
+ case WT_NOTFOUND:
+ rc = wt_dn2aentry(be, wc, &pdn, &e);
+ if (rc != 0 && rc != WT_NOTFOUND) {
+ return rc;
+ }
+ *ep = e;
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2aentry: failed %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ }
+ return rc;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/dn2id.c b/servers/slapd/back-wt/dn2id.c
new file mode 100644
index 0000000..d8765ce
--- /dev/null
+++ b/servers/slapd/back-wt/dn2id.c
@@ -0,0 +1,453 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include "back-wt.h"
+#include "slap-config.h"
+#include "idl.h"
+
+static char *
+mkrevdn(struct berval src){
+ char *dst, *p;
+ char *rdn;
+ size_t rdn_len;
+
+ p = dst = ch_malloc(src.bv_len + 2);
+ while(src.bv_len){
+ rdn = ber_bvrchr( &src, ',' );
+ if (rdn) {
+ rdn_len = src.bv_len;
+ src.bv_len = rdn - src.bv_val;
+ rdn_len -= src.bv_len + 1;
+ rdn++;
+ }else{
+ /* first rdn */
+ rdn_len = src.bv_len;
+ rdn = src.bv_val;
+ src.bv_len = 0;
+ }
+ memcpy( p, rdn, rdn_len );
+ p += rdn_len;
+ *p++ = ',';
+ }
+ *p = '\0';
+ return dst;
+}
+
+int
+wt_dn2id_add(
+ Operation *op,
+ wt_ctx *wc,
+ ID pid,
+ Entry *e)
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ int rc;
+ WT_SESSION *session = wc->session;
+ WT_CURSOR *cursor = wc->dn2id_w;
+ char *revdn = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "=> wt_dn2id_add 0x%lx: \"%s\"\n",
+ e->e_id, e->e_ndn );
+ assert( e->e_id != NOID );
+
+ /* make reverse dn */
+ revdn = mkrevdn(e->e_nname);
+
+ if(!cursor){
+ rc = session->open_cursor(session, WT_TABLE_DN2ID, NULL,
+ "overwrite=false", &cursor);
+ if(rc){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2id_add: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ wc->dn2id_w = cursor;
+ }
+ cursor->set_key(cursor, revdn);
+ cursor->set_value(cursor, e->e_ndn, e->e_id, pid);
+ rc = cursor->insert(cursor);
+ if(rc){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2id_add: insert failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+ if (wi->wi_flags & WT_USE_IDLCACHE) {
+ wt_idlcache_clear(op, wc, &e->e_nname);
+ }
+
+done:
+ if(revdn){
+ ch_free(revdn);
+ }
+
+#ifdef WT_CURSOR_CACHE
+ if(cursor){
+ cursor->reset(cursor);
+ }
+#else
+ if(cursor){
+ cursor->close(cursor);
+ wc->dn2id_w = NULL;
+ }
+#endif
+
+ Debug( LDAP_DEBUG_TRACE, "<= wt_dn2id_add 0x%lx: %d\n", e->e_id, rc );
+ return rc;
+}
+
+int
+wt_dn2id_delete(
+ Operation *op,
+ wt_ctx *wc,
+ struct berval *ndn)
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ int rc = 0;
+ WT_SESSION *session = wc->session;
+ WT_CURSOR *cursor = wc->dn2id_w;
+ char *revdn = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "=> wt_dn2id_delete %s\n", ndn->bv_val );
+
+ /* make reverse dn */
+ revdn = mkrevdn(*ndn);
+
+ if(!cursor){
+ rc = session->open_cursor(session, WT_TABLE_DN2ID, NULL,
+ "overwrite=false", &cursor);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2id_delete: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ wc->dn2id_w = cursor;
+ }
+
+ cursor->set_key(cursor, revdn);
+ rc = cursor->remove(cursor);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2id_delete: remove failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+ if (wi->wi_flags & WT_USE_IDLCACHE) {
+ wt_idlcache_clear(op, wc, ndn);
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_dn2id_delete %s: %d\n", ndn->bv_val, rc );
+done:
+ if(revdn){
+ ch_free(revdn);
+ }
+
+#ifdef WT_CURSOR_CACHE
+ if(cursor){
+ cursor->reset(cursor);
+ }
+#else
+ if(cursor){
+ cursor->close(cursor);
+ wc->dn2id_w = NULL;
+ }
+#endif
+ return rc;
+}
+
+int
+wt_dn2id(
+ Operation *op,
+ wt_ctx *wc,
+ struct berval *ndn,
+ ID *id)
+{
+ WT_SESSION *session = wc->session;
+ WT_CURSOR *cursor = wc->dn2id_ndn;
+ int rc = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_TRACE, "=> wt_dn2id(\"%s\")\n", ndn->bv_val );
+
+ if ( ndn->bv_len == 0 ) {
+ *id = 0;
+ goto done;
+ }
+
+ if(!cursor){
+ rc = session->open_cursor(session, WT_INDEX_NDN
+ "(id)",
+ NULL, NULL, &cursor);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2id: cursor open failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ wc->dn2id_ndn = cursor;
+ }
+
+ cursor->set_key(cursor, ndn->bv_val);
+ rc = cursor->search(cursor);
+ switch( rc ){
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ goto done;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2id: search failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ rc = cursor->get_value(cursor, id);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2id: get_value failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+done:
+
+#ifdef WT_CURSOR_CACHE
+ if(cursor){
+ cursor->reset(cursor);
+ }
+#else
+ if(cursor){
+ cursor->close(cursor);
+ wc->dn2id_ndn = NULL;
+ }
+#endif
+
+ if( rc ) {
+ Debug( LDAP_DEBUG_TRACE, "<= wt_dn2id: get failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "<= wt_dn2id: got id=0x%lx\n",
+ *id );
+ }
+
+ return rc;
+}
+
+int
+wt_dn2id_has_children(
+ Operation *op,
+ wt_ctx *wc,
+ ID id )
+{
+ WT_SESSION *session = wc->session;
+ WT_CURSOR *cursor = wc->index_pid;
+ int rc;
+ uint64_t key = id;
+
+ if(!cursor){
+ rc = session->open_cursor(session, WT_INDEX_PID,
+ NULL, NULL, &cursor);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2id_has_children: cursor open failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ wc->index_pid = cursor;
+ }
+
+ cursor->set_key(cursor, key);
+ rc = cursor->search(cursor);
+
+done:
+
+#ifdef WT_CURSOR_CACHE
+ if(cursor){
+ cursor->reset(cursor);
+ }
+#else
+ if(cursor){
+ cursor->close(cursor);
+ wc->index_pid = NULL;
+ }
+#endif
+
+ return rc;
+}
+
+int
+wt_dn2idl_db(
+ Operation *op,
+ wt_ctx *wc,
+ struct berval *ndn,
+ Entry *e,
+ ID *ids,
+ ID *stack)
+{
+ WT_SESSION *session = wc->session;
+ WT_CURSOR *cursor = wc->dn2id;
+ int rc;
+ char *revdn = NULL;
+ size_t revdn_len;
+ char *key;
+ ID id, pid;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> wt_dn2idl(\"%s\")\n",
+ ndn->bv_val );
+
+ revdn = mkrevdn(*ndn);
+ revdn_len = strlen(revdn);
+
+ if ( !cursor ) {
+ rc = session->open_cursor(session, WT_TABLE_DN2ID"(id, pid)",
+ NULL, NULL, &cursor);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2idl: cursor open failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ wc->dn2id = cursor;
+ }
+ cursor->set_key(cursor, revdn);
+ rc = cursor->search(cursor);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2idl: search failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+ if( op->ors_scope == LDAP_SCOPE_CHILDREN ) {
+ cursor->next(cursor);
+ }
+
+ do {
+ rc = cursor->get_key(cursor, &key);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2idl: get_key failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ rc = cursor->get_value(cursor, &id, &pid);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2id: get_value failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+ if( strncmp(revdn, key, revdn_len) ){
+ break;
+ }
+
+ if( op->ors_scope == LDAP_SCOPE_ONELEVEL && e->e_id != pid ){
+ goto next;
+ }
+ wt_idl_append_one(ids, id);
+ next:
+ rc = cursor->next(cursor);
+ }while(rc == 0);
+
+ if (rc == WT_NOTFOUND ) {
+ rc = LDAP_SUCCESS;
+ }
+
+ wt_idl_sort(ids, stack);
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_dn2idl_db: size=%ld first=%ld last=%ld\n",
+ (long) ids[0],
+ (long) WT_IDL_FIRST(ids),
+ (long) WT_IDL_LAST(ids) );
+
+done:
+ if(revdn){
+ ch_free(revdn);
+ }
+#ifdef WT_CURSOR_CACHE
+ if(cursor){
+ cursor->reset(cursor);
+ }
+#else
+ if(cursor){
+ cursor->close(cursor);
+ wc->dn2id = NULL;
+ }
+#endif
+ return rc;
+}
+
+int
+wt_dn2idl(
+ Operation *op,
+ wt_ctx *wc,
+ struct berval *ndn,
+ Entry *e,
+ ID *ids,
+ ID *stack)
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ int rc;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> wt_dn2idl(\"%s\")\n", ndn->bv_val );
+
+ if(op->ors_scope != LDAP_SCOPE_ONELEVEL &&
+ be_issuffix( op->o_bd, &e->e_nname )){
+ WT_IDL_ALL(wi, ids);
+ return 0;
+ }
+
+ if (wi->wi_flags & WT_USE_IDLCACHE) {
+ rc = wt_idlcache_get(wc, ndn, op->ors_scope, ids);
+ if (rc == 0) {
+ /* cache hit */
+ return rc;
+ }
+ /* cache miss */
+ }
+
+ if ( wi->wi_flags & WT_USE_IDLCACHE ) {
+ wt_idlcache_begin(wc, ndn, op->ors_scope);
+ }
+ rc = wt_dn2idl_db(op, wc, ndn, e, ids, stack);
+ if ( rc == 0 && wi->wi_flags & WT_USE_IDLCACHE ) {
+ wt_idlcache_set(wc, ndn, op->ors_scope, ids);
+ }
+
+ return rc;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/extended.c b/servers/slapd/back-wt/extended.c
new file mode 100644
index 0000000..595672f
--- /dev/null
+++ b/servers/slapd/back-wt/extended.c
@@ -0,0 +1,58 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-wt.h"
+#include "lber_pvt.h"
+
+static struct exop {
+ struct berval *oid;
+ BI_op_extended *extended;
+} exop_table[] = {
+ { NULL, NULL }
+};
+
+int
+wt_extended( Operation *op, SlapReply *rs )
+{
+ int i;
+
+ for( i=0; exop_table[i].extended != NULL; i++ ) {
+ if( ber_bvcmp( exop_table[i].oid, &op->oq_extended.rs_reqoid ) == 0 ) {
+ return (exop_table[i].extended)( op, rs );
+ }
+ }
+
+ rs->sr_text = "not supported within naming context";
+ return rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/filterindex.c b/servers/slapd/back-wt/filterindex.c
new file mode 100644
index 0000000..f321128
--- /dev/null
+++ b/servers/slapd/back-wt/filterindex.c
@@ -0,0 +1,718 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "idl.h"
+
+static int
+presence_candidates(
+ Operation *op,
+ wt_ctx *wc,
+ AttributeDescription *desc,
+ ID *ids )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ slap_mask_t mask;
+ struct berval prefix = {0, NULL};
+ int rc;
+ WT_CURSOR *cursor = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "=> wt_presence_candidates (%s)\n",
+ desc->ad_cname.bv_val );
+
+ WT_IDL_ALL( wi, ids );
+
+ if( desc == slap_schema.si_ad_objectClass ) {
+ return 0;
+ }
+
+ rc = wt_index_param( op->o_bd, desc, LDAP_FILTER_PRESENT,
+ &mask, &prefix );
+
+ if( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+ /* not indexed */
+ Debug( LDAP_DEBUG_FILTER,
+ "<= wt_presence_candidates: (%s) not indexed\n",
+ desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_presence_candidates: (%s) index_param "
+ "returned=%d\n",
+ desc->ad_cname.bv_val, rc );
+ return 0;
+ }
+
+ if( prefix.bv_val == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_presence_candidates: (%s) no prefix\n",
+ desc->ad_cname.bv_val );
+ return -1;
+ }
+
+ /* open index cursor */
+ cursor = wt_index_open(wc, &desc->ad_type->sat_cname, 0);
+ if( !cursor ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= wt_presence_candidates: open index cursor failed: %s\n",
+ desc->ad_type->sat_cname.bv_val );
+ return 0;
+ }
+
+ rc = wt_key_read( op->o_bd, cursor, &prefix, ids, NULL, 0 );
+
+ cursor->close(cursor);
+ Debug(LDAP_DEBUG_TRACE,
+ "<= wt_presence_candidates: id=%ld first=%ld last=%ld\n",
+ (long) ids[0],
+ (long) WT_IDL_FIRST(ids),
+ (long) WT_IDL_LAST(ids) );
+
+ return 0;
+}
+
+static int
+equality_candidates(
+ Operation *op,
+ wt_ctx *wc,
+ AttributeAssertion *ava,
+ ID *ids,
+ ID *tmp)
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ slap_mask_t mask;
+ struct berval prefix = {0, NULL};
+ struct berval *keys = NULL;
+ int i;
+ int rc;
+ MatchingRule *mr;
+ WT_CURSOR *cursor = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "=> wt_equality_candidates (%s=%s)\n",
+ ava->aa_desc->ad_cname.bv_val, ava->aa_value.bv_val );
+
+ if ( ava->aa_desc == slap_schema.si_ad_entryDN ) {
+ ID id = NOID;
+ rc = wt_dn2id(op, wc, &ava->aa_value, &id);
+ if( rc == 0 ){
+ wt_idl_append_one(ids, id);
+ }else if ( rc == WT_NOTFOUND ) {
+ WT_IDL_ZERO( ids );
+ rc = 0;
+ }
+ return rc;
+ }
+
+ WT_IDL_ALL( wi, ids );
+
+ rc = wt_index_param( op->o_bd, ava->aa_desc, LDAP_FILTER_EQUALITY,
+ &mask, &prefix );
+
+ if ( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= wt_equality_candidates: (%s) not indexed\n",
+ ava->aa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= wt_equality_candidates: (%s) index_param failed (%d)\n",
+ ava->aa_desc->ad_cname.bv_val, rc );
+ return 0;
+ }
+
+ mr = ava->aa_desc->ad_type->sat_equality;
+ if( !mr ) {
+ return 0;
+ }
+
+ if( !mr->smr_filter ) {
+ return 0;
+ }
+
+ rc = (mr->smr_filter)(
+ LDAP_FILTER_EQUALITY,
+ mask,
+ ava->aa_desc->ad_type->sat_syntax,
+ mr,
+ &prefix,
+ &ava->aa_value,
+ &keys, op->o_tmpmemctx );
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_equality_candidates: (%s, %s) "
+ "MR filter failed (%d)\n",
+ prefix.bv_val, ava->aa_desc->ad_cname.bv_val, rc );
+ return 0;
+ }
+
+ if( keys == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_equality_candidates: (%s) no keys\n",
+ ava->aa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ /* open index cursor */
+ cursor = wt_index_open(wc, &ava->aa_desc->ad_type->sat_cname, 0);
+ if( !cursor ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= wt_equality_candidates: open index cursor failed: %s\n",
+ ava->aa_desc->ad_type->sat_cname.bv_val );
+ return 0;
+ }
+
+ for ( i= 0; keys[i].bv_val != NULL; i++ ) {
+ rc = wt_key_read( op->o_bd, cursor, &keys[i], tmp, NULL, 0 );
+ if( rc == WT_NOTFOUND ) {
+ WT_IDL_ZERO( ids );
+ rc = 0;
+ break;
+ } else if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_equality_candidates: (%s) key read failed (%d)\n",
+ ava->aa_desc->ad_cname.bv_val, rc );
+ break;
+ }
+ if ( i == 0 ) {
+ WT_IDL_CPY( ids, tmp );
+ } else {
+ wt_idl_intersection( ids, tmp );
+ }
+
+ if( WT_IDL_IS_ZERO( ids ) )
+ break;
+ }
+
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+
+ cursor->close(cursor);
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_equality_candidates: id=%ld, first=%ld, last=%ld\n",
+ (long) ids[0],
+ (long) WT_IDL_FIRST(ids),
+ (long) WT_IDL_LAST(ids) );
+
+ return rc;
+}
+
+static int
+approx_candidates(
+ Operation *op,
+ wt_ctx *wc,
+ AttributeAssertion *ava,
+ ID *ids,
+ ID *tmp )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ int i;
+ int rc;
+ slap_mask_t mask;
+ struct berval prefix = {0, NULL};
+ struct berval *keys = NULL;
+ MatchingRule *mr;
+ WT_CURSOR *cursor = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "=> wt_approx_candidates (%s)\n",
+ ava->aa_desc->ad_cname.bv_val );
+
+ WT_IDL_ALL( wi, ids );
+
+ rc = wt_index_param( op->o_bd, ava->aa_desc, LDAP_FILTER_APPROX,
+ &mask, &prefix );
+
+ if ( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= wt_approx_candidates: (%s) not indexed\n",
+ ava->aa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= wt_approx_candidates: (%s) index_param failed (%d)\n",
+ ava->aa_desc->ad_cname.bv_val, rc );
+ return 0;
+ }
+
+ mr = ava->aa_desc->ad_type->sat_approx;
+ if( !mr ) {
+ /* no approx matching rule, try equality matching rule */
+ mr = ava->aa_desc->ad_type->sat_equality;
+ }
+
+ if( !mr ) {
+ return 0;
+ }
+
+ if( !mr->smr_filter ) {
+ return 0;
+ }
+
+ rc = (mr->smr_filter)(
+ LDAP_FILTER_APPROX,
+ mask,
+ ava->aa_desc->ad_type->sat_syntax,
+ mr,
+ &prefix,
+ &ava->aa_value,
+ &keys, op->o_tmpmemctx );
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_approx_candidates: (%s, %s) MR filter failed (%d)\n",
+ prefix.bv_val, ava->aa_desc->ad_cname.bv_val, rc );
+ return 0;
+ }
+
+ if( keys == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_approx_candidates: (%s) no keys (%s)\n",
+ prefix.bv_val, ava->aa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ /* open index cursor */
+ cursor = wt_index_open(wc, &ava->aa_desc->ad_type->sat_cname, 0);
+ if( !cursor ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= wt_approx_candidates: open index cursor failed: %s\n",
+ ava->aa_desc->ad_type->sat_cname.bv_val );
+ return 0;
+ }
+
+ for ( i= 0; keys[i].bv_val != NULL; i++ ) {
+ rc = wt_key_read( op->o_bd, cursor, &keys[i], tmp, NULL, 0 );
+ if( rc == WT_NOTFOUND ) {
+ WT_IDL_ZERO( ids );
+ rc = 0;
+ break;
+ } else if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_approx_candidates: (%s) key read failed (%d)\n",
+ ava->aa_desc->ad_cname.bv_val, rc );
+ break;
+ }
+
+ if( WT_IDL_IS_ZERO( tmp ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_approx_candidates: (%s) NULL\n",
+ ava->aa_desc->ad_cname.bv_val );
+ WT_IDL_ZERO( ids );
+ break;
+ }
+
+ if ( i == 0 ) {
+ WT_IDL_CPY( ids, tmp );
+ } else {
+ wt_idl_intersection( ids, tmp );
+ }
+
+ if( WT_IDL_IS_ZERO( ids ) )
+ break;
+ }
+
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+
+ cursor->close(cursor);
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_approx_candidates %ld, first=%ld, last=%ld\n",
+ (long) ids[0],
+ (long) WT_IDL_FIRST(ids),
+ (long) WT_IDL_LAST(ids) );
+
+ return rc;
+}
+
+static int
+substring_candidates(
+ Operation *op,
+ wt_ctx *wc,
+ SubstringsAssertion *sub,
+ ID *ids,
+ ID *tmp )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ int i;
+ int rc;
+ slap_mask_t mask;
+ struct berval prefix = {0, NULL};
+ struct berval *keys = NULL;
+ MatchingRule *mr;
+ WT_CURSOR *cursor = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "=> wt_substring_candidates (%s)\n",
+ sub->sa_desc->ad_cname.bv_val );
+
+ WT_IDL_ALL( wi, ids );
+
+ rc = wt_index_param( op->o_bd, sub->sa_desc, LDAP_FILTER_SUBSTRINGS,
+ &mask, &prefix );
+
+ if ( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= wt_substring_candidates: (%s) not indexed\n",
+ sub->sa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= wt_substring_candidates: (%s) index_param failed (%d)\n",
+ sub->sa_desc->ad_cname.bv_val, rc );
+ return 0;
+ }
+
+ mr = sub->sa_desc->ad_type->sat_substr;
+
+ if( !mr ) {
+ return 0;
+ }
+
+ if( !mr->smr_filter ) {
+ return 0;
+ }
+
+ rc = (mr->smr_filter)(
+ LDAP_FILTER_SUBSTRINGS,
+ mask,
+ sub->sa_desc->ad_type->sat_syntax,
+ mr,
+ &prefix,
+ sub,
+ &keys, op->o_tmpmemctx );
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_substring_candidates: (%s) MR filter failed (%d)\n",
+ sub->sa_desc->ad_cname.bv_val, rc );
+ return 0;
+ }
+
+ if( keys == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_substring_candidates: (0x%04lx) no keys (%s)\n",
+ mask, sub->sa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ /* open index cursor */
+ cursor = wt_index_open(wc, &sub->sa_desc->ad_cname, 0);
+ if( !cursor ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= wt_substring_candidates: open index cursor failed: %s\n",
+ sub->sa_desc->ad_cname.bv_val );
+ return 0;
+ }
+
+ for ( i= 0; keys[i].bv_val != NULL; i++ ) {
+ rc = wt_key_read( op->o_bd, cursor, &keys[i], tmp, NULL, 0 );
+
+ if( rc == WT_NOTFOUND ) {
+ WT_IDL_ZERO( ids );
+ rc = 0;
+ break;
+ } else if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_substring_candidates: (%s) key read failed (%d)\n",
+ sub->sa_desc->ad_cname.bv_val, rc );
+ break;
+ }
+
+ if( WT_IDL_IS_ZERO( tmp ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_substring_candidates: (%s) NULL\n",
+ sub->sa_desc->ad_cname.bv_val );
+ WT_IDL_ZERO( ids );
+ break;
+ }
+
+ if ( i == 0 ) {
+ WT_IDL_CPY( ids, tmp );
+ } else {
+ wt_idl_intersection( ids, tmp );
+ }
+
+ if( WT_IDL_IS_ZERO( ids ) )
+ break;
+ }
+
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+
+ cursor->close(cursor);
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<= wt_substring_candidates: %ld, first=%ld, last=%ld\n",
+ (long) ids[0],
+ (long) WT_IDL_FIRST(ids),
+ (long) WT_IDL_LAST(ids));
+ return rc;
+}
+
+#ifdef LDAP_COMP_MATCH
+static int
+comp_candidates (
+ Operation *op,
+ wt_ctx *wc,
+ MatchingRuleAssertion *mra,
+ ComponentFilter *f,
+ ID *ids,
+ ID *tmp,
+ ID *stack)
+{
+ int rc = 0;
+
+ if ( !f ) return LDAP_PROTOCOL_ERROR;
+
+ Debug( LDAP_DEBUG_FILTER, "comp_candidates\n" );
+ /* TODO: */
+ Debug( LDAP_DEBUG_FILTER, "=> not implement yet\n" );
+ return( rc );
+}
+
+#endif
+
+static int
+ext_candidates(
+ Operation *op,
+ wt_ctx *wc,
+ MatchingRuleAssertion *mra,
+ ID *ids,
+ ID *tmp,
+ ID *stack )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+
+#ifdef LDAP_COMP_MATCH
+ /*
+ * Currently Only Component Indexing for componentFilterMatch is supported
+ * Indexing for an extensible filter is not supported yet
+ */
+ if ( mra->ma_cf ) {
+ return comp_candidates ( op, wc, mra, mra->ma_cf, ids, tmp, stack);
+ }
+#endif
+ if ( mra->ma_desc == slap_schema.si_ad_entryDN ) {
+ /* TODO: */
+ Debug( LDAP_DEBUG_FILTER, "=> not implement yet.\n" );
+ }
+ WT_IDL_ALL( wi, ids );
+ return 0;
+}
+
+static int
+list_candidates(
+ Operation *op,
+ wt_ctx *wc,
+ Filter *flist,
+ int ftype,
+ ID *ids,
+ ID *tmp,
+ ID *save )
+{
+ int rc = 0;
+ Filter *f;
+
+ Debug( LDAP_DEBUG_FILTER, "=> wt_list_candidates 0x%x\n", ftype );
+ for ( f = flist; f != NULL; f = f->f_next ) {
+ /* ignore precomputed scopes */
+ if ( f->f_choice == SLAPD_FILTER_COMPUTED &&
+ f->f_result == LDAP_SUCCESS ) {
+ continue;
+ }
+ WT_IDL_ZERO( save );
+ rc = wt_filter_candidates( op, wc, f, save, tmp,
+ save+WT_IDL_UM_SIZE );
+
+ if ( rc != 0 ) {
+ /* TODO: error handling */
+ /*
+ if ( rc == DB_LOCK_DEADLOCK )
+ return rc;
+ */
+ if ( ftype == LDAP_FILTER_AND ) {
+ rc = 0;
+ continue;
+ }
+ break;
+ }
+
+
+ if ( ftype == LDAP_FILTER_AND ) {
+ if ( f == flist ) {
+ WT_IDL_CPY( ids, save );
+ } else {
+ wt_idl_intersection( ids, save );
+ }
+ if( WT_IDL_IS_ZERO( ids ) )
+ break;
+ } else {
+ if ( f == flist ) {
+ WT_IDL_CPY( ids, save );
+ } else {
+ wt_idl_union( ids, save );
+ }
+ }
+ }
+
+ if( rc == LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= wt_list_candidates: id=%ld first=%ld last=%ld\n",
+ (long) ids[0],
+ (long) WT_IDL_FIRST(ids),
+ (long) WT_IDL_LAST(ids) );
+
+ } else {
+ Debug( LDAP_DEBUG_FILTER,
+ "<= wt_list_candidates: undefined rc=%d\n",
+ rc );
+ }
+
+ return 0;
+}
+
+int
+wt_filter_candidates(
+ Operation *op,
+ wt_ctx *wc,
+ Filter *f,
+ ID *ids,
+ ID *tmp,
+ ID *stack )
+{
+ struct wt_info *wi = (struct wt_info *)op->o_bd->be_private;
+ int rc = LDAP_SUCCESS;
+ Debug( LDAP_DEBUG_FILTER, "=> wt_filter_candidates\n" );
+
+ if ( f->f_choice & SLAPD_FILTER_UNDEFINED ) {
+ WT_IDL_ZERO( ids );
+ goto done;
+ }
+
+ switch ( f->f_choice ) {
+ case SLAPD_FILTER_COMPUTED:
+ switch( f->f_result ) {
+ case SLAPD_COMPARE_UNDEFINED:
+ /* This technically is not the same as FALSE, but it
+ * certainly will produce no matches.
+ */
+ /* FALL THRU */
+ case LDAP_COMPARE_FALSE:
+ WT_IDL_ZERO( ids );
+ break;
+ case LDAP_COMPARE_TRUE: {
+
+ WT_IDL_ALL( wi, ids );
+ } break;
+ case LDAP_SUCCESS:
+ /* this is a pre-computed scope, leave it alone */
+ break;
+ }
+ break;
+ case LDAP_FILTER_PRESENT:
+ Debug( LDAP_DEBUG_FILTER, "\tPRESENT\n" );
+ rc = presence_candidates( op, wc, f->f_desc, ids );
+ break;
+
+ case LDAP_FILTER_EQUALITY:
+ Debug( LDAP_DEBUG_FILTER, "\tEQUALITY\n" );
+ rc = equality_candidates( op, wc, f->f_ava, ids, tmp );
+ break;
+
+ case LDAP_FILTER_APPROX:
+ Debug( LDAP_DEBUG_FILTER, "\tAPPROX\n" );
+ rc = approx_candidates( op, wc, f->f_ava, ids, tmp );
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ Debug( LDAP_DEBUG_FILTER, "\tSUBSTRINGS\n" );
+ rc = substring_candidates( op, wc, f->f_sub, ids, tmp );
+ break;
+
+ case LDAP_FILTER_GE:
+ /* if no GE index, use pres */
+ /* TODO: not implement yet */
+ rc = presence_candidates( op, wc, f->f_ava->aa_desc, ids );
+ break;
+
+ case LDAP_FILTER_LE:
+ /* if no LE index, use pres */
+ /* TODO: not implement yet */
+ Debug( LDAP_DEBUG_FILTER, "\tLE\n" );
+ rc = presence_candidates( op, wc, f->f_ava->aa_desc, ids );
+ break;
+
+ case LDAP_FILTER_NOT:
+ /* no indexing to support NOT filters */
+ Debug( LDAP_DEBUG_FILTER, "\tNOT\n" );
+ WT_IDL_ALL( wi, ids );
+ break;
+
+ case LDAP_FILTER_AND:
+ Debug( LDAP_DEBUG_FILTER, "\tAND\n" );
+ rc = list_candidates( op, wc,
+ f->f_and, LDAP_FILTER_AND, ids, tmp, stack );
+ break;
+
+ case LDAP_FILTER_OR:
+ Debug( LDAP_DEBUG_FILTER, "\tOR\n" );
+ rc = list_candidates( op, wc,
+ f->f_or, LDAP_FILTER_OR, ids, tmp, stack );
+ break;
+
+ case LDAP_FILTER_EXT:
+ Debug( LDAP_DEBUG_FILTER, "\tEXT\n" );
+ rc = ext_candidates( op, wc, f->f_mra, ids, tmp, stack);
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_FILTER, "\tUNKNOWN %lu\n",
+ (unsigned long) f->f_choice );
+ /* Must not return NULL, otherwise extended filters break */
+ WT_IDL_ALL( wi, ids );
+ }
+
+done:
+ Debug( LDAP_DEBUG_FILTER,
+ "<= wt_filter_candidates: id=%ld first=%ld last=%ld\n",
+ (long) ids[0],
+ (long) WT_IDL_FIRST( ids ),
+ (long) WT_IDL_LAST( ids ) );
+ return rc;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/id2entry.c b/servers/slapd/back-wt/id2entry.c
new file mode 100644
index 0000000..95b197c
--- /dev/null
+++ b/servers/slapd/back-wt/id2entry.c
@@ -0,0 +1,352 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "back-wt.h"
+#include "slap-config.h"
+
+static int wt_id2entry_put(
+ Operation *op,
+ wt_ctx *wc,
+ Entry *e,
+ WT_CURSOR *cursor)
+{
+ struct berval bv;
+ WT_ITEM item;
+ int rc;
+
+ rc = entry_encode( e, &bv );
+ if(rc != LDAP_SUCCESS){
+ return -1;
+ }
+ item.size = bv.bv_len;
+ item.data = bv.bv_val;
+
+ cursor->set_key(cursor, e->e_id);
+ cursor->set_value(cursor, e->e_ndn, &item);
+ rc = cursor->insert(cursor);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_id2entry_put: insert failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+done:
+ ch_free( bv.bv_val );
+
+ return rc;
+}
+
+int wt_id2entry_add(
+ Operation *op,
+ wt_ctx *wc,
+ Entry *e )
+{
+ WT_SESSION *session = wc->session;
+ WT_CURSOR *cursor = wc->id2entry_add;
+ int rc;
+
+ if(!cursor){
+ rc = session->open_cursor(session, WT_TABLE_ID2ENTRY, NULL,
+ "overwrite=false", &cursor);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_id2entry_put: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return rc;
+ }
+ wc->id2entry_add = cursor;
+ }
+
+ rc = wt_id2entry_put(op, wc, e, cursor);
+
+#ifdef WT_CURSOR_CACHE
+ if(cursor){
+ cursor->reset(cursor);
+ }
+#else
+ if(cursor){
+ cursor->close(cursor);
+ wc->id2entry_add = NULL;
+ }
+#endif
+
+ return rc;
+}
+
+int wt_id2entry_update(
+ Operation *op,
+ wt_ctx *wc,
+ Entry *e )
+{
+ WT_SESSION *session = wc->session;
+ WT_CURSOR *cursor = wc->id2entry_update;
+ int rc;
+
+ if(!cursor){
+ rc = session->open_cursor(session, WT_TABLE_ID2ENTRY, NULL,
+ "overwrite=true", &cursor);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_id2entry_put: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return rc;
+ }
+ wc->id2entry_update = cursor;
+ }
+ rc = wt_id2entry_put(op, wc, e, cursor);
+
+#ifdef WT_CURSOR_CACHE
+ if(cursor){
+ cursor->reset(cursor);
+ }
+#else
+ if(cursor){
+ cursor->close(cursor);
+ wc->id2entry_update = NULL;
+ }
+#endif
+ return rc;
+}
+
+int wt_id2entry_delete(
+ Operation *op,
+ wt_ctx *wc,
+ Entry *e )
+{
+ int rc;
+ WT_SESSION *session = wc->session;
+ WT_CURSOR *cursor = NULL;
+
+ rc = session->open_cursor(session, WT_TABLE_ID2ENTRY, NULL,
+ NULL, &cursor);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_id2entry_delete: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ cursor->set_key(cursor, e->e_id);
+ rc = cursor->remove(cursor);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_id2entry_delete: remove failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+done:
+ if(cursor){
+ cursor->close(cursor);
+ }
+ return rc;
+}
+
+int wt_id2entry( BackendDB *be,
+ wt_ctx *wc,
+ ID id,
+ Entry **ep ){
+ int rc;
+ WT_SESSION *session = wc->session;
+ WT_CURSOR *cursor = wc->id2entry;
+ WT_ITEM item;
+ EntryHeader eh;
+ int eoff;
+ Entry *e = NULL;
+
+ if(!cursor){
+ rc = session->open_cursor(session, WT_TABLE_ID2ENTRY"(entry)", NULL,
+ NULL, &cursor);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_id2entry: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ wc->id2entry = cursor;
+ }
+
+ cursor->set_key(cursor, id);
+ rc = cursor->search(cursor);
+ if ( rc ) {
+ goto done;
+ }
+
+ cursor->get_value(cursor, &item);
+ rc = wt_entry_header( &item, &eh );
+ eoff = eh.data - (char *)item.data;
+ eh.bv.bv_len = eh.nvals * sizeof( struct berval ) + item.size;
+ eh.bv.bv_val = ch_malloc( eh.bv.bv_len );
+ memset(eh.bv.bv_val, 0xff, eh.bv.bv_len);
+ eh.data = eh.bv.bv_val + eh.nvals * sizeof( struct berval );
+ memcpy(eh.data, item.data, item.size);
+ eh.data += eoff;
+ rc = entry_decode( &eh, &e );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_id2entry: entry decode error: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ e->e_id = id;
+ *ep = e;
+
+done:
+
+#ifdef WT_CURSOR_CACHE
+ if(cursor){
+ cursor->reset(cursor);
+ }
+#else
+ if(cursor){
+ cursor->close(cursor);
+ wc->id2entry = NULL;
+ }
+#endif
+ return rc;
+}
+
+int wt_entry_return(
+ Entry *e
+ )
+{
+ if ( !e ) {
+ return 0;
+ }
+
+ /* Our entries are allocated in two blocks; the data comes from
+ * the db itself and the Entry structure and associated pointers
+ * are allocated in entry_decode. The db data pointer is saved
+ * in e_bv.
+ */
+ if ( e->e_bv.bv_val ) {
+#if 0
+ /* See if the DNs were changed by modrdn */
+ if( e->e_nname.bv_val < e->e_bv.bv_val || e->e_nname.bv_val >
+ e->e_bv.bv_val + e->e_bv.bv_len ) {
+ ch_free(e->e_name.bv_val);
+ ch_free(e->e_nname.bv_val);
+ }
+#endif
+ e->e_name.bv_val = NULL;
+ e->e_nname.bv_val = NULL;
+ /* In tool mode the e_bv buffer is realloc'd, leave it alone */
+ if( !(slapMode & SLAP_TOOL_MODE) ) {
+ free( e->e_bv.bv_val );
+ }
+ BER_BVZERO( &e->e_bv );
+ }
+
+ entry_free( e );
+}
+
+int wt_entry_release(
+ Operation *op,
+ Entry *e,
+ int rw )
+{
+ return wt_entry_return( e );
+}
+
+/*
+ * return LDAP_SUCCESS IFF we can retrieve the specified entry.
+ */
+int wt_entry_get(
+ Operation *op,
+ struct berval *ndn,
+ ObjectClass *oc,
+ AttributeDescription *at,
+ int rw,
+ Entry **ent )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ wt_ctx *wc;
+ Entry *e = NULL;
+ int rc;
+ const char *at_name = at ? at->ad_cname.bv_val : "(null)";
+
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_entry_get: ndn: \"%s\"\n", ndn->bv_val );
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_entry_get: oc: \"%s\", at: \"%s\"\n",
+ oc ? oc->soc_cname.bv_val : "(null)", at_name );
+
+ wc = wt_ctx_get(op, wi);
+ if( !wc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_entry_get: wt_ctx_get failed\n" );
+ return LDAP_OTHER;
+ }
+ rc = wt_dn2entry(op->o_bd, wc, ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ Debug( LDAP_DEBUG_ACL,
+ "wt_entry_get: cannot find entry: \"%s\"\n",
+ ndn->bv_val );
+ return LDAP_NO_SUCH_OBJECT;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_entry_get: wt_dn2entry failed %s rc=%d\n",
+ wiredtiger_strerror(rc), rc );
+ rc = LDAP_OTHER;
+ }
+
+ Debug( LDAP_DEBUG_ACL,
+ "wt_entry_get: found entry: \"%s\"\n", ndn->bv_val );
+
+ if ( oc && !is_entry_objectclass( e, oc, 0 )) {
+ Debug( LDAP_DEBUG_ACL,
+ "wt_entry_get: failed to find objectClass %s\n",
+ oc->soc_cname.bv_val );
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ goto return_results;
+ }
+
+ /* NOTE: attr_find() or attrs_find()? */
+ if ( at && attr_find( e->e_attrs, at ) == NULL ) {
+ Debug( LDAP_DEBUG_ACL,
+ "wt_entry_get: failed to find attribute %s\n",
+ at->ad_cname.bv_val );
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ goto return_results;
+ }
+
+return_results:
+ if( rc != LDAP_SUCCESS ) {
+ wt_entry_return( e );
+ }else{
+ *ent = e;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "wt_entry_get: rc=%d\n", rc );
+
+ return rc;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/idl.c b/servers/slapd/back-wt/idl.c
new file mode 100644
index 0000000..0f3167d
--- /dev/null
+++ b/servers/slapd/back-wt/idl.c
@@ -0,0 +1,789 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-wt.h"
+#include "idl.h"
+
+#define IDL_MAX(x,y) ( (x) > (y) ? (x) : (y) )
+#define IDL_MIN(x,y) ( (x) < (y) ? (x) : (y) )
+#define IDL_CMP(x,y) ( (x) < (y) ? -1 : (x) > (y) )
+
+void wt_idl_check( ID *ids )
+{
+ if( WT_IDL_IS_RANGE( ids ) ) {
+ assert( WT_IDL_RANGE_FIRST(ids) <= WT_IDL_RANGE_LAST(ids) );
+ } else {
+ ID i;
+ for( i=1; i < ids[0]; i++ ) {
+ assert( ids[i+1] > ids[i] );
+ }
+ }
+}
+
+void wt_idl_dump( ID *ids )
+{
+ if( WT_IDL_IS_RANGE( ids ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "IDL: range ( %ld - %ld )\n",
+ (long) WT_IDL_RANGE_FIRST( ids ),
+ (long) WT_IDL_RANGE_LAST( ids ) );
+
+ } else {
+ ID i;
+ Debug( LDAP_DEBUG_ANY, "IDL: size %ld", (long) ids[0] );
+
+ for( i=1; i<=ids[0]; i++ ) {
+ if( i % 16 == 1 ) {
+ Debug( LDAP_DEBUG_ANY, "\n" );
+ }
+ Debug( LDAP_DEBUG_ANY, " %02lx", (long) ids[i] );
+ }
+
+ Debug( LDAP_DEBUG_ANY, "\n" );
+ }
+
+ wt_idl_check( ids );
+}
+
+unsigned wt_idl_search( ID *ids, ID id )
+{
+#define IDL_BINARY_SEARCH 1
+#ifdef IDL_BINARY_SEARCH
+ /*
+ * binary search of id in ids
+ * if found, returns position of id
+ * if not found, returns first position greater than id
+ */
+ unsigned base = 0;
+ unsigned cursor = 1;
+ int val = 0;
+ unsigned n = ids[0];
+
+#if IDL_DEBUG > 0
+ idl_check( ids );
+#endif
+
+ while( 0 < n ) {
+ unsigned pivot = n >> 1;
+ cursor = base + pivot + 1;
+ val = IDL_CMP( id, ids[cursor] );
+
+ if( val < 0 ) {
+ n = pivot;
+
+ } else if ( val > 0 ) {
+ base = cursor;
+ n -= pivot + 1;
+
+ } else {
+ return cursor;
+ }
+ }
+
+ if( val > 0 ) {
+ ++cursor;
+ }
+ return cursor;
+
+#else
+ /* (reverse) linear search */
+ int i;
+
+#if IDL_DEBUG > 0
+ idl_check( ids );
+#endif
+
+ for( i=ids[0]; i; i-- ) {
+ if( id > ids[i] ) {
+ break;
+ }
+ }
+
+ return i+1;
+#endif
+}
+
+int wt_idl_insert( ID *ids, ID id )
+{
+ unsigned x;
+
+#if IDL_DEBUG > 1
+ Debug( LDAP_DEBUG_ANY, "insert: %04lx at %d\n", (long) id, x );
+ idl_dump( ids );
+#elif IDL_DEBUG > 0
+ wt_idl_check( ids );
+#endif
+
+ if (WT_IDL_IS_RANGE( ids )) {
+ /* if already in range, treat as a dup */
+ if (id >= WT_IDL_RANGE_FIRST(ids) && id <= WT_IDL_RANGE_LAST(ids))
+ return -1;
+ if (id < WT_IDL_RANGE_FIRST(ids))
+ ids[1] = id;
+ else if (id > WT_IDL_RANGE_LAST(ids))
+ ids[2] = id;
+ return 0;
+ }
+
+ x = wt_idl_search( ids, id );
+ assert( x > 0 );
+
+ if( x < 1 ) {
+ /* internal error */
+ return -2;
+ }
+
+ if ( x <= ids[0] && ids[x] == id ) {
+ /* duplicate */
+ return -1;
+ }
+
+ if ( ++ids[0] >= WT_IDL_DB_MAX ) {
+ if( id < ids[1] ) {
+ ids[1] = id;
+ ids[2] = ids[ids[0]-1];
+ } else if ( ids[ids[0]-1] < id ) {
+ ids[2] = id;
+ } else {
+ ids[2] = ids[ids[0]-1];
+ }
+ ids[0] = NOID;
+
+ } else {
+ /* insert id */
+ AC_MEMCPY( &ids[x+1], &ids[x], (ids[0]-x) * sizeof(ID) );
+ ids[x] = id;
+ }
+
+#if IDL_DEBUG > 1
+ wt_idl_dump( ids );
+#elif IDL_DEBUG > 0
+ wt_idl_check( ids );
+#endif
+
+ return 0;
+}
+
+static int wt_idl_delete( ID *ids, ID id )
+{
+ unsigned x;
+
+#if IDL_DEBUG > 1
+ Debug( LDAP_DEBUG_ANY, "delete: %04lx at %d\n", (long) id, x );
+ idl_dump( ids );
+#elif IDL_DEBUG > 0
+ wt_idl_check( ids );
+#endif
+
+ if (WT_IDL_IS_RANGE( ids )) {
+ /* If deleting a range boundary, adjust */
+ if ( ids[1] == id )
+ ids[1]++;
+ else if ( ids[2] == id )
+ ids[2]--;
+ /* deleting from inside a range is a no-op */
+
+ /* If the range has collapsed, re-adjust */
+ if ( ids[1] > ids[2] )
+ ids[0] = 0;
+ else if ( ids[1] == ids[2] )
+ ids[1] = 1;
+ return 0;
+ }
+
+ x = wt_idl_search( ids, id );
+ assert( x > 0 );
+
+ if( x <= 0 ) {
+ /* internal error */
+ return -2;
+ }
+
+ if( x > ids[0] || ids[x] != id ) {
+ /* not found */
+ return -1;
+
+ } else if ( --ids[0] == 0 ) {
+ if( x != 1 ) {
+ return -3;
+ }
+
+ } else {
+ AC_MEMCPY( &ids[x], &ids[x+1], (1+ids[0]-x) * sizeof(ID) );
+ }
+
+#if IDL_DEBUG > 1
+ wt_idl_dump( ids );
+#elif IDL_DEBUG > 0
+ wt_idl_check( ids );
+#endif
+
+ return 0;
+}
+
+static char *
+wt_show_key(
+ char *buf,
+ void *val,
+ size_t len )
+{
+ if ( len == 4 /* LUTIL_HASH_BYTES */ ) {
+ unsigned char *c = val;
+ sprintf( buf, "[%02x%02x%02x%02x]", c[0], c[1], c[2], c[3] );
+ return buf;
+ } else {
+ return val;
+ }
+}
+
+/*
+ * idl_intersection - return a = a intersection b
+ */
+int
+wt_idl_intersection(
+ ID *a,
+ ID *b )
+{
+ ID ida, idb;
+ ID idmax, idmin;
+ ID cursora = 0, cursorb = 0, cursorc;
+ int swap = 0;
+
+ if ( WT_IDL_IS_ZERO( a ) || WT_IDL_IS_ZERO( b ) ) {
+ a[0] = 0;
+ return 0;
+ }
+
+ idmin = IDL_MAX( WT_IDL_FIRST(a), WT_IDL_FIRST(b) );
+ idmax = IDL_MIN( WT_IDL_LAST(a), WT_IDL_LAST(b) );
+ if ( idmin > idmax ) {
+ a[0] = 0;
+ return 0;
+ } else if ( idmin == idmax ) {
+ a[0] = 1;
+ a[1] = idmin;
+ return 0;
+ }
+
+ if ( WT_IDL_IS_RANGE( a ) ) {
+ if ( WT_IDL_IS_RANGE(b) ) {
+ /* If both are ranges, just shrink the boundaries */
+ a[1] = idmin;
+ a[2] = idmax;
+ return 0;
+ } else {
+ /* Else swap so that b is the range, a is a list */
+ ID *tmp = a;
+ a = b;
+ b = tmp;
+ swap = 1;
+ }
+ }
+
+ /* If a range completely covers the list, the result is
+ * just the list. If idmin to idmax is contiguous, just
+ * turn it into a range.
+ */
+ if ( WT_IDL_IS_RANGE( b )
+ && WT_IDL_RANGE_FIRST( b ) <= WT_IDL_FIRST( a )
+ && WT_IDL_RANGE_LAST( b ) >= WT_IDL_LLAST( a ) ) {
+ if (idmax - idmin + 1 == a[0])
+ {
+ a[0] = NOID;
+ a[1] = idmin;
+ a[2] = idmax;
+ }
+ goto done;
+ }
+
+ /* Fine, do the intersection one element at a time.
+ * First advance to idmin in both IDLs.
+ */
+ cursora = cursorb = idmin;
+ ida = wt_idl_first( a, &cursora );
+ idb = wt_idl_first( b, &cursorb );
+ cursorc = 0;
+
+ while( ida <= idmax || idb <= idmax ) {
+ if( ida == idb ) {
+ a[++cursorc] = ida;
+ ida = wt_idl_next( a, &cursora );
+ idb = wt_idl_next( b, &cursorb );
+ } else if ( ida < idb ) {
+ ida = wt_idl_next( a, &cursora );
+ } else {
+ idb = wt_idl_next( b, &cursorb );
+ }
+ }
+ a[0] = cursorc;
+done:
+ if (swap)
+ WT_IDL_CPY( b, a );
+
+ return 0;
+}
+
+
+/*
+ * idl_union - return a = a union b
+ */
+int
+wt_idl_union(
+ ID *a,
+ ID *b )
+{
+ ID ida, idb;
+ ID cursora = 0, cursorb = 0, cursorc;
+
+ if ( WT_IDL_IS_ZERO( b ) ) {
+ return 0;
+ }
+
+ if ( WT_IDL_IS_ZERO( a ) ) {
+ WT_IDL_CPY( a, b );
+ return 0;
+ }
+
+ if ( WT_IDL_IS_RANGE( a ) || WT_IDL_IS_RANGE(b) ) {
+over: ida = IDL_MIN( WT_IDL_FIRST(a), WT_IDL_FIRST(b) );
+ idb = IDL_MAX( WT_IDL_LAST(a), WT_IDL_LAST(b) );
+ a[0] = NOID;
+ a[1] = ida;
+ a[2] = idb;
+ return 0;
+ }
+
+ ida = wt_idl_first( a, &cursora );
+ idb = wt_idl_first( b, &cursorb );
+
+ cursorc = b[0];
+
+ /* The distinct elements of a are cat'd to b */
+ while( ida != NOID || idb != NOID ) {
+ if ( ida < idb ) {
+ if( ++cursorc > WT_IDL_UM_MAX ) {
+ goto over;
+ }
+ b[cursorc] = ida;
+ ida = wt_idl_next( a, &cursora );
+
+ } else {
+ if ( ida == idb )
+ ida = wt_idl_next( a, &cursora );
+ idb = wt_idl_next( b, &cursorb );
+ }
+ }
+
+ /* b is copied back to a in sorted order */
+ a[0] = cursorc;
+ cursora = 1;
+ cursorb = 1;
+ cursorc = b[0]+1;
+ while (cursorb <= b[0] || cursorc <= a[0]) {
+ if (cursorc > a[0])
+ idb = NOID;
+ else
+ idb = b[cursorc];
+ if (cursorb <= b[0] && b[cursorb] < idb)
+ a[cursora++] = b[cursorb++];
+ else {
+ a[cursora++] = idb;
+ cursorc++;
+ }
+ }
+
+ return 0;
+}
+
+
+#if 0
+/*
+ * wt_idl_notin - return a intersection ~b (or a minus b)
+ */
+int
+wt_idl_notin(
+ ID *a,
+ ID *b,
+ ID *ids )
+{
+ ID ida, idb;
+ ID cursora = 0, cursorb = 0;
+
+ if( WT_IDL_IS_ZERO( a ) ||
+ WT_IDL_IS_ZERO( b ) ||
+ WT_IDL_IS_RANGE( b ) )
+ {
+ WT_IDL_CPY( ids, a );
+ return 0;
+ }
+
+ if( WT_IDL_IS_RANGE( a ) ) {
+ WT_IDL_CPY( ids, a );
+ return 0;
+ }
+
+ ida = wt_idl_first( a, &cursora ),
+ idb = wt_idl_first( b, &cursorb );
+
+ ids[0] = 0;
+
+ while( ida != NOID ) {
+ if ( idb == NOID ) {
+ /* we could shortcut this */
+ ids[++ids[0]] = ida;
+ ida = wt_idl_next( a, &cursora );
+
+ } else if ( ida < idb ) {
+ ids[++ids[0]] = ida;
+ ida = wt_idl_next( a, &cursora );
+
+ } else if ( ida > idb ) {
+ idb = wt_idl_next( b, &cursorb );
+
+ } else {
+ ida = wt_idl_next( a, &cursora );
+ idb = wt_idl_next( b, &cursorb );
+ }
+ }
+
+ return 0;
+}
+#endif
+
+ID wt_idl_first( ID *ids, ID *cursor )
+{
+ ID pos;
+
+ if ( ids[0] == 0 ) {
+ *cursor = NOID;
+ return NOID;
+ }
+
+ if ( WT_IDL_IS_RANGE( ids ) ) {
+ if( *cursor < ids[1] ) {
+ *cursor = ids[1];
+ }
+ return *cursor;
+ }
+
+ if ( *cursor == 0 )
+ pos = 1;
+ else
+ pos = wt_idl_search( ids, *cursor );
+
+ if( pos > ids[0] ) {
+ return NOID;
+ }
+
+ *cursor = pos;
+ return ids[pos];
+}
+
+ID wt_idl_next( ID *ids, ID *cursor )
+{
+ if ( WT_IDL_IS_RANGE( ids ) ) {
+ if( ids[2] < ++(*cursor) ) {
+ return NOID;
+ }
+ return *cursor;
+ }
+
+ if ( ++(*cursor) <= ids[0] ) {
+ return ids[*cursor];
+ }
+
+ return NOID;
+}
+
+/* Add one ID to an unsorted list. We ensure that the first element is the
+ * minimum and the last element is the maximum, for fast range compaction.
+ * this means IDLs up to length 3 are always sorted...
+ */
+int wt_idl_append_one( ID *ids, ID id )
+{
+ if (WT_IDL_IS_RANGE( ids )) {
+ /* if already in range, treat as a dup */
+ if (id >= WT_IDL_RANGE_FIRST(ids) && id <= WT_IDL_RANGE_LAST(ids))
+ return -1;
+ if (id < WT_IDL_RANGE_FIRST(ids))
+ ids[1] = id;
+ else if (id > WT_IDL_RANGE_LAST(ids))
+ ids[2] = id;
+ return 0;
+ }
+ if ( ids[0] ) {
+ ID tmp;
+
+ if (id < ids[1]) {
+ tmp = ids[1];
+ ids[1] = id;
+ id = tmp;
+ }
+ if ( ids[0] > 1 && id < ids[ids[0]] ) {
+ tmp = ids[ids[0]];
+ ids[ids[0]] = id;
+ id = tmp;
+ }
+ }
+ ids[0]++;
+ if ( ids[0] >= WT_IDL_UM_MAX ) {
+ ids[0] = NOID;
+ ids[2] = id;
+ } else {
+ ids[ids[0]] = id;
+ }
+ return 0;
+}
+
+/* Append sorted list b to sorted list a. The result is unsorted but
+ * a[1] is the min of the result and a[a[0]] is the max.
+ */
+int wt_idl_append( ID *a, ID *b )
+{
+ ID ida, idb, tmp, swap = 0;
+
+ if ( WT_IDL_IS_ZERO( b ) ) {
+ return 0;
+ }
+
+ if ( WT_IDL_IS_ZERO( a ) ) {
+ WT_IDL_CPY( a, b );
+ return 0;
+ }
+
+ ida = WT_IDL_LAST( a );
+ idb = WT_IDL_LAST( b );
+ if ( WT_IDL_IS_RANGE( a ) || WT_IDL_IS_RANGE(b) ||
+ a[0] + b[0] >= WT_IDL_UM_MAX ) {
+ a[2] = IDL_MAX( ida, idb );
+ a[1] = IDL_MIN( a[1], b[1] );
+ a[0] = NOID;
+ return 0;
+ }
+
+ if ( b[0] > 1 && ida > idb ) {
+ swap = idb;
+ a[a[0]] = idb;
+ b[b[0]] = ida;
+ }
+
+ if ( b[1] < a[1] ) {
+ tmp = a[1];
+ a[1] = b[1];
+ } else {
+ tmp = b[1];
+ }
+ a[0]++;
+ a[a[0]] = tmp;
+
+ if ( b[0] > 1 ) {
+ int i = b[0] - 1;
+ AC_MEMCPY(a+a[0]+1, b+2, i * sizeof(ID));
+ a[0] += i;
+ }
+ if ( swap ) {
+ b[b[0]] = swap;
+ }
+ return 0;
+}
+
+#if 1
+
+/* Quicksort + Insertion sort for small arrays */
+
+#define SMALL 8
+#define SWAP(a,b) itmp=(a);(a)=(b);(b)=itmp
+
+void
+wt_idl_sort( ID *ids, ID *tmp )
+{
+ int *istack = (int *)tmp; /* Private stack, not used by caller */
+ int i,j,k,l,ir,jstack;
+ ID a, itmp;
+
+ if ( WT_IDL_IS_RANGE( ids ))
+ return;
+
+ ir = ids[0];
+ l = 1;
+ jstack = 0;
+ for(;;) {
+ if (ir - l < SMALL) { /* Insertion sort */
+ for (j=l+1;j<=ir;j++) {
+ a = ids[j];
+ for (i=j-1;i>=1;i--) {
+ if (ids[i] <= a) break;
+ ids[i+1] = ids[i];
+ }
+ ids[i+1] = a;
+ }
+ if (jstack == 0) break;
+ ir = istack[jstack--];
+ l = istack[jstack--];
+ } else {
+ k = (l + ir) >> 1; /* Choose median of left, center, right */
+ SWAP(ids[k], ids[l+1]);
+ if (ids[l] > ids[ir]) {
+ SWAP(ids[l], ids[ir]);
+ }
+ if (ids[l+1] > ids[ir]) {
+ SWAP(ids[l+1], ids[ir]);
+ }
+ if (ids[l] > ids[l+1]) {
+ SWAP(ids[l], ids[l+1]);
+ }
+ i = l+1;
+ j = ir;
+ a = ids[l+1];
+ for(;;) {
+ do i++; while(ids[i] < a);
+ do j--; while(ids[j] > a);
+ if (j < i) break;
+ SWAP(ids[i],ids[j]);
+ }
+ ids[l+1] = ids[j];
+ ids[j] = a;
+ jstack += 2;
+ if (ir-i+1 >= j-l) {
+ istack[jstack] = ir;
+ istack[jstack-1] = i;
+ ir = j-1;
+ } else {
+ istack[jstack] = j-1;
+ istack[jstack-1] = l;
+ l = i;
+ }
+ }
+ }
+}
+
+#else
+
+/* 8 bit Radix sort + insertion sort
+ *
+ * based on code from http://www.cubic.org/docs/radix.htm
+ * with improvements by ebackes@symas.com and hyc@symas.com
+ *
+ * This code is O(n) but has a relatively high constant factor. For lists
+ * up to ~50 Quicksort is slightly faster; up to ~100 they are even.
+ * Much faster than quicksort for lists longer than ~100. Insertion
+ * sort is actually superior for lists <50.
+ */
+
+#define BUCKETS (1<<8)
+#define SMALL 50
+
+void
+wt_idl_sort( ID *ids, ID *tmp )
+{
+ int count, soft_limit, phase = 0, size = ids[0];
+ ID *idls[2];
+ unsigned char *maxv = (unsigned char *)&ids[size];
+
+ if ( WT_IDL_IS_RANGE( ids ))
+ return;
+
+ /* Use insertion sort for small lists */
+ if ( size <= SMALL ) {
+ int i,j;
+ ID a;
+
+ for (j=1;j<=size;j++) {
+ a = ids[j];
+ for (i=j-1;i>=1;i--) {
+ if (ids[i] <= a) break;
+ ids[i+1] = ids[i];
+ }
+ ids[i+1] = a;
+ }
+ return;
+ }
+
+ tmp[0] = size;
+ idls[0] = ids;
+ idls[1] = tmp;
+
+#if BYTE_ORDER == BIG_ENDIAN
+ for (soft_limit = 0; !maxv[soft_limit]; soft_limit++);
+#else
+ for (soft_limit = sizeof(ID)-1; !maxv[soft_limit]; soft_limit--);
+#endif
+
+ for (
+#if BYTE_ORDER == BIG_ENDIAN
+ count = sizeof(ID)-1; count >= soft_limit; --count
+#else
+ count = 0; count <= soft_limit; ++count
+#endif
+ ) {
+ unsigned int num[BUCKETS], * np, n, sum;
+ int i;
+ ID *sp, *source, *dest;
+ unsigned char *bp, *source_start;
+
+ source = idls[phase]+1;
+ dest = idls[phase^1]+1;
+ source_start = ((unsigned char *) source) + count;
+
+ np = num;
+ for ( i = BUCKETS; i > 0; --i ) *np++ = 0;
+
+ /* count occurrences of every byte value */
+ bp = source_start;
+ for ( i = size; i > 0; --i, bp += sizeof(ID) )
+ num[*bp]++;
+
+ /* transform count into index by summing elements and storing
+ * into same array
+ */
+ sum = 0;
+ np = num;
+ for ( i = BUCKETS; i > 0; --i ) {
+ n = *np;
+ *np++ = sum;
+ sum += n;
+ }
+
+ /* fill dest with the right values in the right place */
+ bp = source_start;
+ sp = source;
+ for ( i = size; i > 0; --i, bp += sizeof(ID) ) {
+ np = num + *bp;
+ dest[*np] = *sp++;
+ ++(*np);
+ }
+ phase ^= 1;
+ }
+
+ /* copy back from temp if needed */
+ if ( phase ) {
+ ids++; tmp++;
+ for ( count = 0; count < size; ++count )
+ *ids++ = *tmp++;
+ }
+}
+#endif /* Quick vs Radix */
+
diff --git a/servers/slapd/back-wt/idl.h b/servers/slapd/back-wt/idl.h
new file mode 100644
index 0000000..d323e54
--- /dev/null
+++ b/servers/slapd/back-wt/idl.h
@@ -0,0 +1,80 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#ifndef _WT_IDL_H_
+#define _WT_IDL_H_
+
+/* IDL sizes - likely should be even bigger
+ * limiting factors: sizeof(ID), thread stack size
+ */
+#define WT_IDL_LOGN 16 /* DB_SIZE is 2^16, UM_SIZE is 2^17 */
+#define WT_IDL_DB_SIZE (1<<WT_IDL_LOGN)
+#define WT_IDL_UM_SIZE (1<<(WT_IDL_LOGN+1))
+#define WT_IDL_UM_SIZEOF (WT_IDL_UM_SIZE * sizeof(ID))
+
+#define WT_IDL_DB_MAX (WT_IDL_DB_SIZE-1)
+
+#define WT_IDL_UM_MAX (WT_IDL_UM_SIZE-1)
+
+#define WT_IDL_IS_RANGE(ids) ((ids)[0] == NOID)
+#define WT_IDL_RANGE_SIZE (3)
+#define WT_IDL_RANGE_SIZEOF (WT_IDL_RANGE_SIZE * sizeof(ID))
+#define WT_IDL_SIZEOF(ids) ((WT_IDL_IS_RANGE(ids) \
+ ? WT_IDL_RANGE_SIZE : ((ids)[0]+1)) * sizeof(ID))
+
+#define WT_IDL_RANGE_FIRST(ids) ((ids)[1])
+#define WT_IDL_RANGE_LAST(ids) ((ids)[2])
+
+#define WT_IDL_RANGE( ids, f, l ) \
+ do { \
+ (ids)[0] = NOID; \
+ (ids)[1] = (f); \
+ (ids)[2] = (l); \
+ } while(0)
+
+#define WT_IDL_ZERO(ids) \
+ do { \
+ (ids)[0] = 0; \
+ (ids)[1] = 0; \
+ (ids)[2] = 0; \
+ } while(0)
+
+#define WT_IDL_IS_ZERO(ids) ( (ids)[0] == 0 )
+#define WT_IDL_IS_ALL( range, ids ) ( (ids)[0] == NOID \
+ && (ids)[1] <= (range)[1] && (range)[2] <= (ids)[2] )
+
+#define WT_IDL_CPY( dst, src ) (AC_MEMCPY( dst, src, WT_IDL_SIZEOF( src ) ))
+
+#define WT_IDL_ID( wi, ids, id ) WT_IDL_RANGE( ids, id, ((wi)->wi_lastid) )
+#define WT_IDL_ALL( wi, ids ) WT_IDL_RANGE( ids, 1, ((wi)->wi_lastid) )
+
+#define WT_IDL_FIRST( ids ) ( (ids)[1] )
+#define WT_IDL_LLAST( ids ) ( (ids)[(ids)[0]] )
+#define WT_IDL_LAST( ids ) ( WT_IDL_IS_RANGE(ids) \
+ ? (ids)[2] : (ids)[(ids)[0]] )
+
+#define WT_IDL_N( ids ) ( WT_IDL_IS_RANGE(ids) \
+ ? ((ids)[2]-(ids)[1])+1 : (ids)[0] )
+
+LDAP_BEGIN_DECL
+LDAP_END_DECL
+
+#endif
diff --git a/servers/slapd/back-wt/index.c b/servers/slapd/back-wt/index.c
new file mode 100644
index 0000000..3301977
--- /dev/null
+++ b/servers/slapd/back-wt/index.c
@@ -0,0 +1,423 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "slap-config.h"
+
+static char presence_keyval[] = {0,0};
+static struct berval presence_key = BER_BVC(presence_keyval);
+
+AttrInfo *wt_index_mask(
+ Backend *be,
+ AttributeDescription *desc,
+ struct berval *atname )
+{
+ AttributeType *at;
+ AttrInfo *ai = wt_attr_mask( be->be_private, desc );
+
+ if( ai ) {
+ *atname = desc->ad_cname;
+ return ai;
+ }
+
+ /* If there is a tagging option, did we ever index the base
+ * type? If so, check for mask, otherwise it's not there.
+ */
+ if( slap_ad_is_tagged( desc ) && desc != desc->ad_type->sat_ad ) {
+ /* has tagging option */
+ ai = wt_attr_mask( be->be_private, desc->ad_type->sat_ad );
+
+ if ( ai && !( ai->ai_indexmask & SLAP_INDEX_NOTAGS ) ) {
+ *atname = desc->ad_type->sat_cname;
+ return ai;
+ }
+ }
+
+ /* see if supertype defined mask for its subtypes */
+ for( at = desc->ad_type; at != NULL ; at = at->sat_sup ) {
+ /* If no AD, we've never indexed this type */
+ if ( !at->sat_ad ) continue;
+
+ ai = wt_attr_mask( be->be_private, at->sat_ad );
+
+ if ( ai && !( ai->ai_indexmask & SLAP_INDEX_NOSUBTYPES ) ) {
+ *atname = at->sat_cname;
+ return ai;
+ }
+ }
+
+ return 0;
+}
+
+/* This function is only called when evaluating search filters.
+ */
+int wt_index_param(
+ Backend *be,
+ AttributeDescription *desc,
+ int ftype,
+ slap_mask_t *maskp,
+ struct berval *prefixp )
+{
+ AttrInfo *ai;
+ slap_mask_t mask, type = 0;
+
+ ai = wt_index_mask( be, desc, prefixp );
+
+ if ( !ai ) {
+ /* TODO: add monitor */
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+ mask = ai->ai_indexmask;
+
+ switch( ftype ) {
+ case LDAP_FILTER_PRESENT:
+ type = SLAP_INDEX_PRESENT;
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_PRESENT ) ) {
+ *prefixp = presence_key;
+ *maskp = mask;
+ return LDAP_SUCCESS;
+ }
+ break;
+
+ case LDAP_FILTER_APPROX:
+ type = SLAP_INDEX_APPROX;
+ if ( desc->ad_type->sat_approx ) {
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) ) {
+ *maskp = mask;
+ return LDAP_SUCCESS;
+ }
+ break;
+ }
+
+ /* Use EQUALITY rule and index for approximate match */
+ /* fall thru */
+
+ case LDAP_FILTER_EQUALITY:
+ type = SLAP_INDEX_EQUALITY;
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) ) {
+ *maskp = mask;
+ return LDAP_SUCCESS;
+ }
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ type = SLAP_INDEX_SUBSTR;
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) ) {
+ *maskp = mask;
+ return LDAP_SUCCESS;
+ }
+ break;
+
+ default:
+ return LDAP_OTHER;
+ }
+
+ /* TODO: add monitor index */
+ return LDAP_INAPPROPRIATE_MATCHING;
+}
+
+static int indexer(
+ Operation *op,
+ wt_ctx *wc,
+ AttributeDescription *ad,
+ struct berval *atname,
+ BerVarray vals,
+ ID id,
+ int opid,
+ slap_mask_t mask )
+{
+ int rc = LDAP_SUCCESS, i;
+ struct berval *keys;
+ WT_CURSOR *cursor = NULL;
+ assert( mask != 0 );
+
+ cursor = wt_index_open(wc, atname, 1);
+ if( !cursor ) {
+ Debug( LDAP_DEBUG_ANY,
+ "indexer: open index cursor failed: %s\n",
+ atname->bv_val );
+ goto done;
+ }
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_PRESENT ) ) {
+ rc = wt_key_change( op->o_bd, cursor, &presence_key, id, opid );
+ if( rc ) {
+ goto done;
+ }
+ }
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) ) {
+ rc = ad->ad_type->sat_equality->smr_indexer(
+ LDAP_FILTER_EQUALITY,
+ mask,
+ ad->ad_type->sat_syntax,
+ ad->ad_type->sat_equality,
+ atname, vals, &keys, op->o_tmpmemctx );
+
+ if( rc == LDAP_SUCCESS && keys != NULL ) {
+ for( i=0; keys[i].bv_val != NULL; i++ ) {
+ rc = wt_key_change( op->o_bd, cursor, &keys[i], id, opid );
+ if( rc ) {
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+ goto done;
+ }
+ }
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+ }
+ rc = LDAP_SUCCESS;
+ }
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) ) {
+ rc = ad->ad_type->sat_approx->smr_indexer(
+ LDAP_FILTER_APPROX,
+ mask,
+ ad->ad_type->sat_syntax,
+ ad->ad_type->sat_approx,
+ atname, vals, &keys, op->o_tmpmemctx );
+
+ if( rc == LDAP_SUCCESS && keys != NULL ) {
+ for( i=0; keys[i].bv_val != NULL; i++ ) {
+ rc = wt_key_change( op->o_bd, cursor, &keys[i], id, opid );
+ if( rc ) {
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+ goto done;
+ }
+ }
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+ }
+
+ rc = LDAP_SUCCESS;
+ }
+
+ if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) ) {
+ rc = ad->ad_type->sat_substr->smr_indexer(
+ LDAP_FILTER_SUBSTRINGS,
+ mask,
+ ad->ad_type->sat_syntax,
+ ad->ad_type->sat_substr,
+ atname, vals, &keys, op->o_tmpmemctx );
+
+ if( rc == LDAP_SUCCESS && keys != NULL ) {
+ for( i=0; keys[i].bv_val != NULL; i++ ) {
+ rc = wt_key_change( op->o_bd, cursor, &keys[i], id, opid );
+ if( rc ) {
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+ goto done;
+ }
+ }
+ ber_bvarray_free_x( keys, op->o_tmpmemctx );
+ }
+
+ rc = LDAP_SUCCESS;
+ }
+
+done:
+ cursor->close(cursor);
+ return rc;
+}
+
+static int index_at_values(
+ Operation *op,
+ wt_ctx *wc,
+ AttributeDescription *ad,
+ AttributeType *type,
+ struct berval *tags,
+ BerVarray vals,
+ ID id,
+ int opid )
+{
+ int rc = LDAP_SUCCESS;
+ slap_mask_t mask = 0;
+ int ixop = opid;
+ AttrInfo *ai = NULL;
+
+ if ( opid == WT_INDEX_UPDATE_OP )
+ ixop = SLAP_INDEX_ADD_OP;
+
+ if( type->sat_sup ) {
+ /* recurse */
+ rc = index_at_values( op, wc, NULL,
+ type->sat_sup, tags,
+ vals, id, opid );
+
+ if( rc ) return rc;
+ }
+
+ /* If this type has no AD, we've never used it before */
+ if( type->sat_ad ) {
+ ai = wt_attr_mask( op->o_bd->be_private, type->sat_ad );
+ if ( ai ) {
+ #ifdef LDAP_COMP_MATCH
+ /* component indexing */
+ if ( ai->ai_cr ) {
+ ComponentReference *cr;
+ for( cr = ai->ai_cr ; cr ; cr = cr->cr_next ) {
+ rc = indexer( op, wc, cr->cr_ad, &type->sat_cname,
+ cr->cr_nvals, id, ixop,
+ cr->cr_indexmask );
+ }
+ }
+ #endif
+ ad = type->sat_ad;
+ /* If we're updating the index, just set the new bits that aren't
+ * already in the old mask.
+ */
+ if ( opid == WT_INDEX_UPDATE_OP )
+ mask = ai->ai_newmask & ~ai->ai_indexmask;
+ else
+ /* For regular updates, if there is a newmask use it. Otherwise
+ * just use the old mask.
+ */
+ mask = ai->ai_newmask ? ai->ai_newmask : ai->ai_indexmask;
+ if( mask ) {
+ rc = indexer( op, wc, ad, &type->sat_cname,
+ vals, id, ixop, mask );
+ if( rc ) return rc;
+ }
+ }
+ }
+
+ if( tags->bv_len ) {
+ AttributeDescription *desc;
+
+ desc = ad_find_tags( type, tags );
+ if( desc ) {
+ ai = wt_attr_mask( op->o_bd->be_private, desc );
+
+ if( ai ) {
+ if ( opid == WT_INDEX_UPDATE_OP )
+ mask = ai->ai_newmask & ~ai->ai_indexmask;
+ else
+ mask = ai->ai_newmask ? ai->ai_newmask : ai->ai_indexmask;
+ if ( mask ) {
+ rc = indexer( op, wc, desc, &desc->ad_cname,
+ vals, id, ixop, mask );
+
+ if( rc ) {
+ return rc;
+ }
+ }
+ }
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int wt_index_values(
+ Operation *op,
+ wt_ctx *wc,
+ AttributeDescription *desc,
+ BerVarray vals,
+ ID id,
+ int opid )
+{
+ int rc;
+
+ /* Never index ID 0 */
+ if ( id == 0 )
+ return 0;
+
+ rc = index_at_values( op, wc, desc,
+ desc->ad_type, &desc->ad_tags,
+ vals, id, opid );
+
+ return rc;
+}
+
+int
+wt_index_entry( Operation *op, wt_ctx *wc, int opid, Entry *e )
+{
+ int rc;
+ Attribute *ap = e->e_attrs;
+
+ if ( e->e_id == 0 )
+ return 0;
+
+ Debug( LDAP_DEBUG_TRACE, "=> index_entry_%s( %ld, \"%s\" )\n",
+ opid == SLAP_INDEX_DELETE_OP ? "del" : "add",
+ (long) e->e_id, e->e_dn ? e->e_dn : "" );
+
+ for ( ; ap != NULL; ap = ap->a_next ) {
+ rc = wt_index_values( op, wc, ap->a_desc,
+ ap->a_nvals, e->e_id, opid );
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= index_entry_%s( %ld, \"%s\" ) failure\n",
+ opid == SLAP_INDEX_ADD_OP ? "add" : "del",
+ (long) e->e_id, e->e_dn );
+ return rc;
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<= index_entry_%s( %ld, \"%s\" ) success\n",
+ opid == SLAP_INDEX_DELETE_OP ? "del" : "add",
+ (long) e->e_id, e->e_dn ? e->e_dn : "" );
+ return 0;
+}
+
+WT_CURSOR *
+wt_index_open(wt_ctx *wc, struct berval *name, int create)
+{
+ WT_CURSOR *cursor = NULL;
+ WT_SESSION *session = wc->session;
+ char uri[1024];
+ int rc;
+
+ snprintf(uri, sizeof(uri), "table:%s", name->bv_val);
+
+ rc = session->open_cursor(session, uri, NULL, "overwrite=false", &cursor);
+ if (rc == ENOENT && create) {
+ rc = session->create(session, uri,
+ "key_format=uQ,"
+ "value_format=x,"
+ "columns=(key, id, none)");
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_index_open: table \"%s\": "
+ "cannot create index table: %s (%d)\n",
+ uri, wiredtiger_strerror(rc), rc);
+ return NULL;
+ }
+ rc = session->open_cursor(session, uri, NULL,
+ "overwrite=false", &cursor);
+ }
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_index_open: table \"%s\": "
+ ": open cursor failed: %s (%d)\n",
+ uri, wiredtiger_strerror(rc), rc);
+ return NULL;
+ }
+ return cursor;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/init.c b/servers/slapd/back-wt/init.c
new file mode 100644
index 0000000..cc0c53a
--- /dev/null
+++ b/servers/slapd/back-wt/init.c
@@ -0,0 +1,385 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "slap-config.h"
+
+static int
+wt_db_init( BackendDB *be, ConfigReply *cr )
+{
+ struct wt_info *wi;
+
+ Debug( LDAP_DEBUG_TRACE, "wt_db_init: Initializing wt backend\n" );
+
+ /* allocate backend-database-specific stuff */
+ wi = ch_calloc( 1, sizeof(struct wt_info) );
+ wi->wi_home = ch_strdup( SLAPD_DEFAULT_DB_DIR );
+ wi->wi_config = ch_calloc( 1, WT_CONFIG_MAX + 1);
+ if ( slapMode & SLAP_TOOL_READONLY ) {
+ strcpy(wi->wi_config, "readonly");
+ } else {
+ strcpy(wi->wi_config, "create");
+ }
+ wi->wi_lastid = 0;
+ wi->wi_search_stack_depth = DEFAULT_SEARCH_STACK_DEPTH;
+ wi->wi_search_stack = NULL;
+ wi->wi_flags = WT_USE_IDLCACHE;
+
+ be->be_private = wi;
+ be->be_cf_ocs = be->bd_info->bi_cf_ocs;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+wt_db_open( BackendDB *be, ConfigReply *cr )
+{
+ struct wt_info *wi = (struct wt_info *) be->be_private;
+ int rc;
+ struct stat st;
+ WT_SESSION *session = NULL;
+ WT_SESSION *cache_session = NULL;
+
+ if ( be->be_suffix == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "wt_db_open: need suffix.\n" );
+ return -1;
+ }
+
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_db_open: \"%s\", home=%s, config=%s\n",
+ be->be_suffix[0].bv_val, wi->wi_home, wi->wi_config );
+
+ /* Check existence of home. Any error means trouble */
+ rc = stat( wi->wi_home, &st );
+ if( rc ) {
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot access database directory \"%s\" (%d).\n",
+ be->be_suffix[0].bv_val, wi->wi_home, saved_errno );
+ return -1;
+ }
+
+ /* back-wt is always clean */
+ be->be_flags |= SLAP_DBFLAG_CLEAN;
+
+ /* Open and create database */
+ rc = wiredtiger_open(wi->wi_home, NULL,
+ wi->wi_config, &wi->wi_conn);
+ if( rc ) {
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot open database \"%s\" (%d).\n",
+ be->be_suffix[0].bv_val, wi->wi_home, saved_errno );
+ return -1;
+ }
+
+ rc = wi->wi_conn->open_session(wi->wi_conn, NULL, NULL, &session);
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot open session: \"%s\"\n",
+ be->be_suffix[0].bv_val, wiredtiger_strerror(rc) );
+ return -1;
+ }
+
+ if ( slapMode & SLAP_TOOL_READONLY ) {
+ goto readonly;
+ }
+
+ /* checking for obsolete table */
+ rc = session->verify(session, WT_INDEX_REVDN, NULL);
+ if ( !rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "incompatible wiredtiger table, please restore from LDIF.\n",
+ be->be_suffix[0].bv_val );
+ return -1;
+ }
+
+ /* create tables and indexes */
+ rc = session->create(session,
+ WT_TABLE_ID2ENTRY,
+ "key_format=Q,"
+ "value_format=Su,"
+ "columns=(id,dn,entry)");
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot create entry table: \"%s\"\n",
+ be->be_suffix[0].bv_val, wiredtiger_strerror(rc) );
+ return -1;
+ }
+
+ rc = session->create(session,
+ WT_TABLE_DN2ID,
+ "key_format=S,"
+ "value_format=SQQ,"
+ "columns=(revdn,ndn,id,pid)");
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot create entry table: \"%s\"\n",
+ be->be_suffix[0].bv_val, wiredtiger_strerror(rc) );
+ return -1;
+ }
+
+ /* not using dn2id index for id2entry table */
+ rc = session->create(session, WT_INDEX_DN, "columns=(dn)");
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot create dn index: \"%s\"\n",
+ be->be_suffix[0].bv_val, wiredtiger_strerror(rc) );
+ return -1;
+ }
+
+ rc = session->create(session, WT_INDEX_PID, "columns=(pid)");
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot create pid index: \"%s\"\n",
+ be->be_suffix[0].bv_val, wiredtiger_strerror(rc) );
+ return -1;
+ }
+
+ rc = session->create(session, WT_INDEX_NDN, "columns=(ndn)");
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot create ndn index: \"%s\"\n",
+ be->be_suffix[0].bv_val, wiredtiger_strerror(rc) );
+ return -1;
+ }
+
+ /* open in-memory database for idlcache */
+ rc = wiredtiger_open(be->be_suffix[0].bv_val, NULL,
+ "in_memory=true", &wi->wi_cache);
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot open database for cache (%s).\n",
+ be->be_suffix[0].bv_val, wiredtiger_strerror(rc) );
+ return -1;
+ }
+
+ rc = wi->wi_cache->open_session(wi->wi_cache, NULL, NULL, &cache_session);
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot open session for cache: \"%s\"\n",
+ be->be_suffix[0].bv_val, wiredtiger_strerror(rc) );
+ return -1;
+ }
+
+ rc = cache_session->create(cache_session,
+ WT_TABLE_IDLCACHE,
+ "key_format=Sb,"
+ "value_format=u,"
+ "columns=(ndn,scope,idl)");
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_open: database \"%s\": "
+ "cannot create idlcache table: \"%s\"\n",
+ be->be_suffix[0].bv_val, wiredtiger_strerror(rc) );
+ return -1;
+ }
+
+readonly:
+ rc = wt_last_id( be, session, &wi->wi_lastid);
+ if (rc) {
+ snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
+ "last_id() failed: %s(%d).",
+ be->be_suffix[0].bv_val, wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY, "wt_db_open: %s\n", cr->msg );
+ return rc;
+ }
+
+ if (session) {
+ session->close(session, NULL);
+ }
+ if (cache_session) {
+ cache_session->close(cache_session, NULL);
+ }
+
+ wi->wi_flags |= WT_IS_OPEN;
+ return LDAP_SUCCESS;
+}
+
+static int
+wt_db_close( BackendDB *be, ConfigReply *cr )
+{
+ struct wt_info *wi = (struct wt_info *) be->be_private;
+ int rc;
+
+ if ( wi->wi_cache ) {
+ rc = wi->wi_cache->close(wi->wi_cache, NULL);
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_close: cannot close cache database (%d).\n", rc );
+ return -1;
+ }
+ }
+
+ if ( wi->wi_conn ) {
+ rc = wi->wi_conn->close(wi->wi_conn, NULL);
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_db_close: cannot close database (%d).\n", rc );
+ return -1;
+ }
+ wi->wi_flags &= ~WT_IS_OPEN;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+wt_db_destroy( Backend *be, ConfigReply *cr )
+{
+ struct wt_info *wi = (struct wt_info *) be->be_private;
+
+ if( wi->wi_home ) {
+ ch_free( wi->wi_home );
+ wi->wi_home = NULL;
+ }
+
+ if( wi->wi_config ) {
+ ch_free( wi->wi_config );
+ wi->wi_config = NULL;
+ }
+
+ wt_attr_index_destroy( wi );
+ ch_free( wi );
+ be->be_private = NULL;
+
+ return LDAP_SUCCESS;
+}
+
+int
+wt_back_initialize( BackendInfo *bi )
+{
+ static const char *controls[] = {
+ LDAP_CONTROL_ASSERT,
+ LDAP_CONTROL_MANAGEDSAIT,
+ LDAP_CONTROL_NOOP,
+ LDAP_CONTROL_PAGEDRESULTS,
+ LDAP_CONTROL_PRE_READ,
+ LDAP_CONTROL_POST_READ,
+ LDAP_CONTROL_SUBENTRIES,
+ LDAP_CONTROL_X_PERMISSIVE_MODIFY,
+#ifdef LDAP_X_TXN
+ LDAP_CONTROL_X_TXN_SPEC,
+#endif
+ NULL
+ };
+
+ /* initialize the database system */
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_back_initialize: initialize WiredTiger backend\n" );
+
+ bi->bi_flags |=
+ SLAP_BFLAG_INCREMENT |
+ SLAP_BFLAG_SUBENTRIES |
+ SLAP_BFLAG_ALIASES |
+ SLAP_BFLAG_REFERRALS;
+
+ bi->bi_controls = (char **)controls;
+
+ /* version check */
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_back_initialize: %s\n",
+ wiredtiger_version(NULL, NULL, NULL) );
+
+ bi->bi_open = 0;
+ bi->bi_close = 0;
+ bi->bi_config = 0;
+ bi->bi_destroy = 0;
+
+ bi->bi_db_init = wt_db_init;
+ bi->bi_db_config = config_generic_wrapper;
+ bi->bi_db_open = wt_db_open;
+ bi->bi_db_close = wt_db_close;
+ bi->bi_db_destroy = wt_db_destroy;
+
+ bi->bi_op_add = wt_add;
+ bi->bi_op_bind = wt_bind;
+ bi->bi_op_unbind = 0;
+ bi->bi_op_search = wt_search;
+ bi->bi_op_compare = wt_compare;
+ bi->bi_op_modify = wt_modify;
+ bi->bi_op_modrdn = wt_modrdn;
+ bi->bi_op_delete = wt_delete;
+ bi->bi_op_abandon = 0;
+
+ bi->bi_extended = wt_extended;
+#ifdef LDAP_X_TXN
+ bi->bi_op_txn = 0;
+#endif
+
+ bi->bi_chk_referrals = 0;
+ bi->bi_operational = wt_operational;
+
+ bi->bi_has_subordinates = wt_hasSubordinates;
+ bi->bi_entry_release_rw = wt_entry_release;
+ bi->bi_entry_get_rw = wt_entry_get;
+
+ bi->bi_tool_entry_open = wt_tool_entry_open;
+ bi->bi_tool_entry_close = wt_tool_entry_close;
+ bi->bi_tool_entry_first = backend_tool_entry_first;
+ bi->bi_tool_entry_first_x = wt_tool_entry_first_x;
+ bi->bi_tool_entry_next = wt_tool_entry_next;
+ bi->bi_tool_entry_get = wt_tool_entry_get;
+ bi->bi_tool_entry_put = wt_tool_entry_put;
+ bi->bi_tool_entry_reindex = wt_tool_entry_reindex;
+ bi->bi_tool_sync = 0;
+ bi->bi_tool_dn2id_get = wt_tool_dn2id_get;
+ bi->bi_tool_entry_modify = wt_tool_entry_modify;
+
+#if LDAP_VENDOR_VERSION_MINOR == X || LDAP_VENDOR_VERSION_MINOR >= 5
+ bi->bi_tool_entry_delete = wt_tool_entry_delete;
+#endif
+
+ bi->bi_connection_init = 0;
+ bi->bi_connection_destroy = 0;
+
+ return wt_back_init_cf( bi );
+}
+
+#if SLAPD_WT == SLAPD_MOD_DYNAMIC
+
+/* conditionally define the init_module() function */
+SLAP_BACKEND_INIT_MODULE( wt )
+
+#endif /* SLAPD_WT == SLAPD_MOD_DYNAMIC */
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/key.c b/servers/slapd/back-wt/key.c
new file mode 100644
index 0000000..ea86829
--- /dev/null
+++ b/servers/slapd/back-wt/key.c
@@ -0,0 +1,162 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "slap-config.h"
+#include "idl.h"
+
+/* read a key */
+int
+wt_key_read(
+ Backend *be,
+ WT_CURSOR *cursor,
+ struct berval *bkey,
+ ID *ids,
+ WT_CURSOR **saved_cursor,
+ int get_flag
+ )
+{
+ int rc;
+ WT_ITEM key;
+ int exact;
+ WT_ITEM key2;
+ ID id;
+ int comp;
+ long scanned = 0;
+
+ Debug( LDAP_DEBUG_TRACE, "=> key_read\n" );
+
+ WT_IDL_ZERO(ids);
+ bv2ITEM(bkey, &key);
+ cursor->set_key(cursor, &key, 0);
+ rc = cursor->search_near(cursor, &exact);
+ switch( rc ){
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ rc = LDAP_SUCCESS;
+ goto done;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_key_read: search_near failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc);
+ goto done;
+ }
+ do {
+ scanned++;
+ rc = cursor->get_key(cursor, &key2, &id);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_key_read: get_key failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ break;
+ }
+ comp = 0;
+ if (key.size != key2.size ||
+ (comp = memcmp(key2.data, key.data, key.size))) {
+ if(comp > 0){
+ break;
+ }
+ if(exact < 0){
+ rc = cursor->next(cursor);
+ if (rc) {
+ break;
+ }else{
+ continue;
+ }
+ }
+ break;
+ }
+ exact = 0;
+ wt_idl_append_one(ids, id);
+ rc = cursor->next(cursor);
+ } while(rc == 0);
+
+ if ( rc == WT_NOTFOUND && exact == 0 ) {
+ rc = LDAP_SUCCESS;
+ }
+
+done:
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "<= wt_key_read: failed (%d) %ld scanned\n",
+ rc, scanned );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "<= wt_key_read %ld candidates %ld scanned\n",
+ (long) WT_IDL_N(ids), scanned );
+ }
+
+ return rc;
+}
+
+/* Add or remove stuff from index files */
+int
+wt_key_change(
+ Backend *be,
+ WT_CURSOR *cursor,
+ struct berval *k,
+ ID id,
+ int op
+)
+{
+ int rc;
+ WT_ITEM item;
+
+ Debug( LDAP_DEBUG_TRACE, "=> key_change(%s,%lx)\n",
+ op == SLAP_INDEX_ADD_OP ? "ADD":"DELETE", (long) id );
+
+ bv2ITEM(k, &item);
+ cursor->set_key(cursor, &item, id);
+ cursor->set_value(cursor, NULL);
+
+ if (op == SLAP_INDEX_ADD_OP) {
+ /* Add values */
+ rc = cursor->insert(cursor);
+ if ( rc == WT_DUPLICATE_KEY ) rc = 0;
+ } else {
+ /* Delete values */
+ rc = cursor->remove(cursor);
+ if ( rc == WT_NOTFOUND ) rc = 0;
+ }
+ if( rc ) {
+ if ( rc != WT_ROLLBACK ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_key_change: error: %s (%d)\n",
+ wiredtiger_strerror(rc), rc);
+ }
+ return rc;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<= key_change %d\n", rc );
+
+ return rc;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/modify.c b/servers/slapd/back-wt/modify.c
new file mode 100644
index 0000000..d78c438
--- /dev/null
+++ b/servers/slapd/back-wt/modify.c
@@ -0,0 +1,714 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+B * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "slap-config.h"
+
+static struct berval scbva[] = {
+ BER_BVC("glue"),
+ BER_BVNULL
+};
+
+static void
+wt_modify_idxflags(
+ Operation *op,
+ AttributeDescription *desc,
+ int got_delete,
+ Attribute *newattrs,
+ Attribute *oldattrs )
+{
+ struct berval ix_at;
+ AttrInfo *ai;
+
+ /* check if modified attribute was indexed
+ * but not in case of NOOP... */
+ ai = wt_index_mask( op->o_bd, desc, &ix_at );
+ if ( ai ) {
+ if ( got_delete ) {
+ Attribute *ap;
+ struct berval ix2;
+
+ ap = attr_find( oldattrs, desc );
+ if ( ap ) ap->a_flags |= SLAP_ATTR_IXDEL;
+
+ /* Find all other attrs that index to same slot */
+ for ( ap = newattrs; ap; ap = ap->a_next ) {
+ ai = wt_index_mask( op->o_bd, ap->a_desc, &ix2 );
+ if ( ai && ix2.bv_val == ix_at.bv_val )
+ ap->a_flags |= SLAP_ATTR_IXADD;
+ }
+
+ } else {
+ Attribute *ap;
+
+ ap = attr_find( newattrs, desc );
+ if ( ap ) ap->a_flags |= SLAP_ATTR_IXADD;
+ }
+ }
+}
+
+int wt_modify_internal(
+ Operation *op,
+ wt_ctx *wc,
+ Modifications *modlist,
+ Entry *e,
+ const char **text,
+ char *textbuf,
+ size_t textlen )
+{
+ int rc, err;
+ Modification *mod;
+ Modifications *ml;
+ Attribute *save_attrs;
+ Attribute *ap;
+ int glue_attr_delete = 0;
+ int got_delete;
+
+ Debug( LDAP_DEBUG_TRACE, "wt_modify_internal: 0x%08lx: %s\n",
+ e->e_id, e->e_dn );
+
+ if ( !acl_check_modlist( op, e, modlist )) {
+ return LDAP_INSUFFICIENT_ACCESS;
+ }
+
+ /* save_attrs will be disposed of by caller */
+ save_attrs = e->e_attrs;
+ e->e_attrs = attrs_dup( e->e_attrs );
+
+ for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
+ int match;
+ mod = &ml->sml_mod;
+ switch( mod->sm_op ) {
+ case LDAP_MOD_ADD:
+ case LDAP_MOD_REPLACE:
+ if ( mod->sm_desc == slap_schema.si_ad_structuralObjectClass ) {
+ value_match( &match, slap_schema.si_ad_structuralObjectClass,
+ slap_schema.si_ad_structuralObjectClass->
+ ad_type->sat_equality,
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ &mod->sm_values[0], &scbva[0], text );
+ if ( !match ) glue_attr_delete = 1;
+ }
+ }
+ if ( glue_attr_delete )
+ break;
+ }
+
+ if ( glue_attr_delete ) {
+ Attribute **app = &e->e_attrs;
+ while ( *app != NULL ) {
+ if ( !is_at_operational( (*app)->a_desc->ad_type )) {
+ Attribute *save = *app;
+ *app = (*app)->a_next;
+ attr_free( save );
+ continue;
+ }
+ app = &(*app)->a_next;
+ }
+ }
+
+ for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
+ mod = &ml->sml_mod;
+ got_delete = 0;
+
+ switch ( mod->sm_op ) {
+ case LDAP_MOD_ADD:
+ Debug(LDAP_DEBUG_ARGS,
+ "wt_modify_internal: add %s\n",
+ mod->sm_desc->ad_cname.bv_val );
+ err = modify_add_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+ if( err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "wt_modify_internal: %d %s\n",
+ err, *text );
+ }
+ break;
+
+ case LDAP_MOD_DELETE:
+ if ( glue_attr_delete ) {
+ err = LDAP_SUCCESS;
+ break;
+ }
+
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_modify_internal: delete %s\n",
+ mod->sm_desc->ad_cname.bv_val );
+ err = modify_delete_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+ if( err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS,
+ "wt_modify_internal: %d %s\n", err, *text );
+ } else {
+ got_delete = 1;
+ }
+ break;
+
+ case LDAP_MOD_REPLACE:
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_modify_internal: replace %s\n",
+ mod->sm_desc->ad_cname.bv_val );
+ err = modify_replace_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+ if( err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS,
+ "wt_modify_internal: %d %s\n", err, *text );
+ } else {
+ got_delete = 1;
+ }
+ break;
+
+ case LDAP_MOD_INCREMENT:
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_modify_internal: increment %s\n",
+ mod->sm_desc->ad_cname.bv_val );
+ err = modify_increment_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+ if( err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_modify_internal: %d %s\n", err, *text );
+ } else {
+ got_delete = 1;
+ }
+ break;
+
+ case SLAP_MOD_SOFTADD:
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_modify_internal: softadd %s\n",
+ mod->sm_desc->ad_cname.bv_val );
+ /* Avoid problems in index_add_mods()
+ * We need to add index if necessary.
+ */
+ mod->sm_op = LDAP_MOD_ADD;
+
+ err = modify_add_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+
+ mod->sm_op = SLAP_MOD_SOFTADD;
+
+ if ( err == LDAP_TYPE_OR_VALUE_EXISTS ) {
+ err = LDAP_SUCCESS;
+ }
+
+ if( err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "wt_modify_internal: %d %s\n",
+ err, *text );
+ }
+ break;
+
+ case SLAP_MOD_SOFTDEL:
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_modify_internal: softdel %s\n",
+ mod->sm_desc->ad_cname.bv_val );
+ /* Avoid problems in index_delete_mods()
+ * We need to add index if necessary.
+ */
+ mod->sm_op = LDAP_MOD_DELETE;
+
+ err = modify_delete_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+
+ mod->sm_op = SLAP_MOD_SOFTDEL;
+
+ if ( err == LDAP_SUCCESS ) {
+ got_delete = 1;
+ } else if ( err == LDAP_NO_SUCH_ATTRIBUTE ) {
+ err = LDAP_SUCCESS;
+ }
+
+ if( err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "wt_modify_internal: %d %s\n",
+ err, *text );
+ }
+ break;
+
+ case SLAP_MOD_ADD_IF_NOT_PRESENT:
+ if ( attr_find( e->e_attrs, mod->sm_desc ) != NULL ) {
+ /* skip */
+ err = LDAP_SUCCESS;
+ break;
+ }
+
+ Debug(LDAP_DEBUG_ARGS,
+ "wt_modify_internal: add_if_not_present %s\n",
+ mod->sm_desc->ad_cname.bv_val );
+ /* Avoid problems in index_add_mods()
+ * We need to add index if necessary.
+ */
+ mod->sm_op = LDAP_MOD_ADD;
+
+ err = modify_add_values( e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+
+ mod->sm_op = SLAP_MOD_ADD_IF_NOT_PRESENT;
+
+ if( err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "wt_modify_internal: %d %s\n",
+ err, *text );
+ }
+ break;
+
+ default:
+ Debug(LDAP_DEBUG_ANY, "wt_modify_internal: invalid op %d\n",
+ mod->sm_op );
+ *text = "Invalid modify operation";
+ err = LDAP_OTHER;
+ Debug(LDAP_DEBUG_ARGS, "wt_modify_internal: %d %s\n",
+ err, *text );
+ }
+
+ if ( err != LDAP_SUCCESS ) {
+ attrs_free( e->e_attrs );
+ e->e_attrs = save_attrs;
+ /* unlock entry, delete from cache */
+ return err;
+ }
+
+ /* If objectClass was modified, reset the flags */
+ if ( mod->sm_desc == slap_schema.si_ad_objectClass ) {
+ e->e_ocflags = 0;
+ }
+
+ if ( glue_attr_delete ) e->e_ocflags = 0;
+
+
+ /* check if modified attribute was indexed
+ * but not in case of NOOP... */
+ if ( !op->o_noop ) {
+ wt_modify_idxflags( op, mod->sm_desc, got_delete, e->e_attrs, save_attrs );
+ }
+
+ }
+
+ /* check that the entry still obeys the schema */
+ ap = NULL;
+ rc = entry_schema_check( op, e, save_attrs, get_relax(op), 0, &ap,
+ text, textbuf, textlen );
+ if ( rc != LDAP_SUCCESS || op->o_noop ) {
+ attrs_free( e->e_attrs );
+ /* clear the indexing flags */
+ for ( ap = save_attrs; ap != NULL; ap = ap->a_next ) {
+ ap->a_flags &= ~(SLAP_ATTR_IXADD|SLAP_ATTR_IXDEL);
+ }
+ e->e_attrs = save_attrs;
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "entry failed schema check: %s\n", *text );
+ }
+
+ /* if NOOP then silently revert to saved attrs */
+ return rc;
+ }
+
+ /* structuralObjectClass modified! */
+ if ( ap ) {
+ assert( ap->a_desc == slap_schema.si_ad_structuralObjectClass );
+ if ( !op->o_noop ) {
+ wt_modify_idxflags( op, slap_schema.si_ad_structuralObjectClass,
+ 1, e->e_attrs, save_attrs );
+ }
+ }
+
+ /* update the indices of the modified attributes */
+
+ /* start with deleting the old index entries */
+ for ( ap = save_attrs; ap != NULL; ap = ap->a_next ) {
+ if ( ap->a_flags & SLAP_ATTR_IXDEL ) {
+ struct berval *vals;
+ Attribute *a2;
+ ap->a_flags &= ~SLAP_ATTR_IXDEL;
+ a2 = attr_find( e->e_attrs, ap->a_desc );
+ if ( a2 ) {
+ /* need to detect which values were deleted */
+ int i, j;
+ /* let add know there were deletes */
+ if ( a2->a_flags & SLAP_ATTR_IXADD )
+ a2->a_flags |= SLAP_ATTR_IXDEL;
+ vals = op->o_tmpalloc( (ap->a_numvals + 1) *
+ sizeof(struct berval), op->o_tmpmemctx );
+ j = 0;
+ for ( i=0; i < ap->a_numvals; i++ ) {
+ rc = attr_valfind( a2, SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ &ap->a_nvals[i], NULL, op->o_tmpmemctx );
+ /* Save deleted values */
+ if ( rc == LDAP_NO_SUCH_ATTRIBUTE )
+ vals[j++] = ap->a_nvals[i];
+ }
+ BER_BVZERO(vals+j);
+ } else {
+ /* attribute was completely deleted */
+ vals = ap->a_nvals;
+ }
+ rc = 0;
+ if ( !BER_BVISNULL( vals )) {
+ rc = wt_index_values( op, wc, ap->a_desc,
+ vals, e->e_id, SLAP_INDEX_DELETE_OP );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: attribute \"%s\" index delete failure\n",
+ op->o_log_prefix, ap->a_desc->ad_cname.bv_val );
+ attrs_free( e->e_attrs );
+ e->e_attrs = save_attrs;
+ }
+ }
+ if ( vals != ap->a_nvals )
+ op->o_tmpfree( vals, op->o_tmpmemctx );
+ if ( rc ) return rc;
+ }
+ }
+
+ /* add the new index entries */
+ for ( ap = e->e_attrs; ap != NULL; ap = ap->a_next ) {
+ if (ap->a_flags & SLAP_ATTR_IXADD) {
+ ap->a_flags &= ~SLAP_ATTR_IXADD;
+ if ( ap->a_flags & SLAP_ATTR_IXDEL ) {
+ /* if any values were deleted, we must readd index
+ * for all remaining values.
+ */
+ ap->a_flags &= ~SLAP_ATTR_IXDEL;
+ rc = wt_index_values( op, wc, ap->a_desc, ap->a_nvals,
+ e->e_id, SLAP_INDEX_ADD_OP );
+ } else {
+ int found = 0;
+ /* if this was only an add, we only need to index
+ * the added values.
+ */
+ for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
+ struct berval *vals;
+ if ( ml->sml_desc != ap->a_desc || !ml->sml_numvals )
+ continue;
+ found = 1;
+ switch( ml->sml_op ) {
+ case LDAP_MOD_ADD:
+ case LDAP_MOD_REPLACE:
+ case LDAP_MOD_INCREMENT:
+ case SLAP_MOD_SOFTADD:
+ case SLAP_MOD_ADD_IF_NOT_PRESENT:
+ if ( ml->sml_op == LDAP_MOD_INCREMENT )
+ vals = ap->a_nvals;
+ else if ( ml->sml_nvalues )
+ vals = ml->sml_nvalues;
+ else
+ vals = ml->sml_values;
+ rc = wt_index_values( op, wc, ap->a_desc,
+ vals, e->e_id, SLAP_INDEX_ADD_OP );
+ break;
+ }
+ if ( rc )
+ break;
+ }
+ /* This attr was affected by a modify of a subtype, so
+ * there was no direct match in the modlist. Just readd
+ * all of its values.
+ */
+ if ( !found ) {
+ rc = wt_index_values( op, wc, ap->a_desc, ap->a_nvals,
+ e->e_id, SLAP_INDEX_ADD_OP );
+ }
+ }
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: attribute \"%s\" index add failure\n",
+ op->o_log_prefix, ap->a_desc->ad_cname.bv_val );
+ attrs_free( e->e_attrs );
+ e->e_attrs = save_attrs;
+ return rc;
+ }
+ }
+ }
+
+ return rc;
+}
+
+int
+wt_modify( Operation *op, SlapReply *rs )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ wt_ctx *wc = NULL;
+ Entry *e = NULL;
+ int manageDSAit = get_manageDSAit( op );
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof textbuf;
+ Entry dummy = {0};
+
+ LDAPControl **preread_ctrl = NULL;
+ LDAPControl **postread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+
+ int rc;
+
+ Debug( LDAP_DEBUG_ARGS, "wt_modify: %s\n", op->o_req_dn.bv_val );
+
+#ifdef LDAP_X_TXN
+ if( op->o_txnSpec && txn_preop( op, rs ))
+ return rs->sr_err;
+#endif
+
+ ctrls[num_ctrls] = NULL;
+
+ wc = wt_ctx_get(op, wi);
+ if( !wc ){
+ Debug( LDAP_DEBUG_ANY, "wt_modify: wt_ctx_get failed\n" );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+
+ /* Don't touch the opattrs, if this is a contextCSN update
+ * initiated from updatedn */
+ if ( !be_isupdate(op) || !op->orm_modlist || op->orm_modlist->sml_next ||
+ op->orm_modlist->sml_desc != slap_schema.si_ad_contextCSN ) {
+
+ slap_mods_opattrs( op, &op->orm_modlist, 1 );
+ }
+
+retry:
+ /* begin transaction */
+ wc->is_begin_transaction = 0;
+ rc = wc->session->begin_transaction(wc->session, "isolation=snapshot");
+ if( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modify: begin_transaction failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "begin_transaction failed";
+ goto return_results;
+ }
+ wc->is_begin_transaction = 1;
+ Debug( LDAP_DEBUG_TRACE, "wt_modify: session id: %p\n", wc->session );
+
+ /* get entry */
+ rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "<== wt_modify: wt_dn2entry failed (%d)\n", rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ if ( rc == WT_NOTFOUND ||
+ ( !manageDSAit && e && is_entry_glue( e ))) {
+ if ( !e ) {
+ rc = wt_dn2aentry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto return_results;
+ default:
+ Debug( LDAP_DEBUG_ANY, "wt_modify: wt_dna2entry failed (%d)\n", rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ }
+
+ rs->sr_matched = ch_strdup( e->e_dn );
+
+ if ( is_entry_referral( e ) ) {
+ BerVarray ref = get_entry_referrals( op, e );
+ rs->sr_ref = referral_rewrite( ref, &e->e_name,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ } else {
+ rs->sr_ref = NULL;
+ }
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ rs->sr_err = LDAP_REFERRAL;
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ if ( !manageDSAit && is_entry_referral( e ) ) {
+ /* entry is a referral, don't allow modify */
+ rs->sr_ref = get_entry_referrals( op, e );
+
+ Debug( LDAP_DEBUG_TRACE, "wt_modify: entry is referral\n" );
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_matched = e->e_name.bv_val;
+ rs->sr_flags = REP_REF_MUSTBEFREED;
+ send_ldap_result( op, rs );
+ rs->sr_matched = NULL;
+ goto done;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+ if( op->o_preread ) {
+ if( preread_ctrl == NULL ) {
+ preread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if ( slap_read_controls( op, rs, e,
+ &slap_pre_read_bv, preread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- wt_modify: pre-read failed!\n" );
+ if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ /* Modify the entry */
+ dummy = *e;
+ rs->sr_err = wt_modify_internal( op, wc, op->orm_modlist,
+ &dummy, &rs->sr_text, textbuf, textlen );
+ switch ( rs->sr_err ) {
+ case LDAP_SUCCESS:
+ break;
+ case WT_ROLLBACK:
+ Debug (LDAP_DEBUG_TRACE, "wt_modify: rollback wt_modify_internal failed.\n" );
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto retry;
+ default:
+ Debug( LDAP_DEBUG_ANY, "wt_modify: modify failed (%d)\n", rs->sr_err );
+ /* Only free attrs if they were dup'd. */
+ if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
+ goto return_results;
+ }
+
+ /* change the entry itself */
+ rs->sr_err = wt_id2entry_update( op, wc, &dummy );
+ switch ( rs->sr_err ) {
+ case 0:
+ break;
+ case WT_ROLLBACK:
+ Debug (LDAP_DEBUG_TRACE, "wt_modify: rollback wt_id2entry_update failed.\n");
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto retry;
+ case LDAP_ADMINLIMIT_EXCEEDED:
+ Debug( LDAP_DEBUG_ANY, "wt_modify: id2entry update failed (%d)\n",
+ rs->sr_err);
+ rs->sr_text = "entry too big";
+ goto return_results;
+ default:
+ Debug( LDAP_DEBUG_ANY, "wt_modify: id2entry update failed (%d)\n",
+ rs->sr_err);
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "entry update failed";
+ goto return_results;
+ }
+
+ if( op->o_postread ) {
+ if( postread_ctrl == NULL ) {
+ postread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if( slap_read_controls( op, rs, &dummy,
+ &slap_post_read_bv, postread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_modify: post-read failed!\n");
+ if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ if( op->o_noop ) {
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ goto return_results;
+ }
+
+ /* Only free attrs if they were dup'd. */
+ if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
+
+ rc = wc->session->commit_transaction(wc->session, NULL);
+ wc->is_begin_transaction = 0;
+ if( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_modify: commit failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "commit failed";
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modify: updated%s id=%08lx dn=\"%s\"\n",
+ op->o_noop ? " (no-op)" : "",
+ dummy.e_id, op->o_req_dn.bv_val );
+
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+ if( dummy.e_attrs ) {
+ attrs_free( dummy.e_attrs );
+ }
+ send_ldap_result( op, rs );
+
+done:
+ slap_graduate_commit_csn( op );
+
+ if( wc && wc->is_begin_transaction ){
+ Debug( LDAP_DEBUG_TRACE, "wt_modify: rollback transaction\n" );
+ wc->session->rollback_transaction(wc->session, NULL);
+ wc->is_begin_transaction = 0;
+ }
+
+ if( e != NULL ) {
+ wt_entry_return( e );
+ }
+
+ if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
+ slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
+ }
+ if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
+ slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
+ }
+
+ return rs->sr_err;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/modrdn.c b/servers/slapd/back-wt/modrdn.c
new file mode 100644
index 0000000..faef948
--- /dev/null
+++ b/servers/slapd/back-wt/modrdn.c
@@ -0,0 +1,552 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include "back-wt.h"
+#include "slap-config.h"
+
+int
+wt_modrdn( Operation *op, SlapReply *rs )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ AttributeDescription *children = slap_schema.si_ad_children;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+ wt_ctx *wc = NULL;
+ Entry *e = NULL;
+ Entry *p = NULL;
+ Entry *ne = NULL;
+ Entry dummy = {0};
+
+ struct berval p_dn, p_ndn;
+ struct berval new_dn = {0, NULL}, new_ndn = {0, NULL};
+
+ Entry *np = NULL; /* newSuperior Entry */
+ struct berval *np_dn = NULL; /* newSuperior dn */
+ struct berval *np_ndn = NULL; /* newSuperior ndn */
+ struct berval *new_parent_dn = NULL; /* np_dn, p_dn, or NULL */
+
+ int manageDSAit = get_manageDSAit( op );
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof textbuf;
+ LDAPControl **preread_ctrl = NULL;
+ LDAPControl **postread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+
+ int rc;
+
+ int parent_is_glue = 0;
+ int parent_is_leaf = 0;
+
+ Debug( LDAP_DEBUG_TRACE, "==> wt_modrdn(%s -> newrdn=%s - newsup=%s)\n",
+ op->o_req_dn.bv_val,
+ op->oq_modrdn.rs_newrdn.bv_val,
+ op->oq_modrdn.rs_newSup?op->oq_modrdn.rs_newSup->bv_val:"NULL" );
+
+ ctrls[num_ctrls] = NULL;
+
+ slap_mods_opattrs( op, &op->orr_modlist, 1 );
+
+ wc = wt_ctx_get(op, wi);
+ if( !wc ){
+ Debug( LDAP_DEBUG_ANY, "wt_modrdn: wt_ctx_get failed\n");
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+
+ /* get parent entry */
+ if ( be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
+ rs->sr_err = LDAP_NAMING_VIOLATION;
+ rs->sr_text = "cannot rename suffix entry";
+ goto return_results;
+ } else {
+ dnParent( &op->o_req_ndn, &p_ndn );
+ }
+
+ rc = wt_dn2entry(op->o_bd, wc, &p_ndn, &p);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ Debug( LDAP_DEBUG_ARGS,
+ "<== wt_modrdn: parent does not exist %s\n", p_ndn.bv_val);
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto return_results;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "<== wt_modrdn: wt_dn2entry failed (%d)\n", rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* check parent for "children" acl */
+ rc = access_allowed( op, p, children, NULL,
+ op->oq_modrdn.rs_newSup == NULL ?
+ ACL_WRITE : ACL_WDEL, NULL );
+
+ if ( !rc ) {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: no access to parent\n");
+ rs->sr_text = "no write access to old parent's children";
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: wr to children of entry %s OK\n", p_ndn.bv_val );
+
+ if ( p_ndn.bv_val == slap_empty_bv.bv_val ) {
+ p_dn = slap_empty_bv;
+ } else {
+ dnParent( &op->o_req_dn, &p_dn );
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: parent dn=%s\n", p_dn.bv_val );
+
+ /* get entry */
+ rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "<== wt_modrdn: wt_dn2entry failed (%d)\n", rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ if ( rc == WT_NOTFOUND ||
+ ( !manageDSAit && e && is_entry_glue( e ) )) {
+
+ if ( !e ) {
+ Debug( LDAP_DEBUG_ARGS,
+ "<== wt_modrdn: no such object %s\n", op->o_req_dn.bv_val);
+ rc = wt_dn2aentry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto return_results;
+ default:
+ Debug( LDAP_DEBUG_ANY, "wt_modrdn: wt_dn2aentry failed (%d)\n", rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ }
+
+ rs->sr_matched = ch_strdup( e->e_dn );
+
+ if ( is_entry_referral( e ) ) {
+ BerVarray ref = get_entry_referrals( op, e );
+ rs->sr_ref = referral_rewrite( ref, &e->e_name,
+ &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ ber_bvarray_free( ref );
+ } else {
+ rs->sr_ref = NULL;
+ }
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ rs->sr_err = LDAP_REFERRAL;
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+ /* check write on old entry */
+ rc = access_allowed( op, e, entry, NULL, ACL_WRITE, NULL );
+ if ( !rc ) {
+ Debug( LDAP_DEBUG_TRACE, "wt_modrdn: no access to entry\n");
+ rs->sr_text = "no write access to old entry";
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto return_results;
+ }
+
+ /* Can't do it if we have kids */
+ rc = wt_dn2id_has_children( op, wc, e->e_id );
+ if( rc != WT_NOTFOUND ) {
+ switch( rc ) {
+ case 0:
+ Debug(LDAP_DEBUG_ARGS, "<== wt_modrdn: non-leaf %s\n", op->o_req_dn.bv_val);
+ rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
+ rs->sr_text = "subtree rename not supported";
+ break;
+ default:
+ Debug(LDAP_DEBUG_ARGS,
+ "<== wt_modrdn: has_children failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ }
+ goto return_results;
+ }
+
+ if (!manageDSAit && is_entry_referral( e ) ) {
+ /* parent is a referral, don't allow add */
+ rs->sr_ref = get_entry_referrals( op, e );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: entry %s is referral\n", e->e_dn );
+
+ rs->sr_err = LDAP_REFERRAL,
+ rs->sr_matched = e->e_name.bv_val;
+ send_ldap_result( op, rs );
+
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ rs->sr_matched = NULL;
+ goto done;
+ }
+
+ new_parent_dn = &p_dn; /* New Parent unless newSuperior given */
+ if ( op->oq_modrdn.rs_newSup != NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: new parent \"%s\" requested...\n",
+ op->oq_modrdn.rs_newSup->bv_val );
+
+ /* newSuperior == oldParent? */
+ if( dn_match( &p_ndn, op->oq_modrdn.rs_nnewSup ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: new parent \"%s\" same as the old parent \"%s\"\n",
+ op->oq_modrdn.rs_newSup->bv_val, p_dn.bv_val );
+ op->oq_modrdn.rs_newSup = NULL; /* ignore newSuperior */
+ }
+ }
+
+ if ( op->oq_modrdn.rs_newSup != NULL ) {
+ if ( op->oq_modrdn.rs_newSup->bv_len ) {
+ np_dn = op->oq_modrdn.rs_newSup;
+ np_ndn = op->oq_modrdn.rs_nnewSup;
+
+ /* newSuperior == oldParent? - checked above */
+ /* newSuperior == entry being moved?, if so ==> ERROR */
+ if ( dnIsSuffix( np_ndn, &e->e_nname )) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = "new superior not found";
+ goto return_results;
+ }
+ /* Get Entry with dn=newSuperior. Does newSuperior exist? */
+ rc = wt_dn2entry(op->o_bd, wc, np_ndn, &np);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ Debug( LDAP_DEBUG_ANY,
+ "<== wt_modrdn: new superior not found: %s\n",
+ np_ndn->bv_val );
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = "new superior not found";
+ goto return_results;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "<== wt_modrdn: wt_dn2entry failed %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: wr to new parent OK np=%p, id=%ld\n",
+ (void *) np, (long) np->e_id );
+ rs->sr_err = access_allowed( op, np, children,
+ NULL, ACL_WADD, NULL );
+ if( ! rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: no wr to newSup children\n" );
+ rs->sr_text = "no write access to new superior's children";
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto return_results;
+ }
+ if ( is_entry_alias( np ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: entry is alias\n" );
+ rs->sr_text = "new superior is an alias";
+ rs->sr_err = LDAP_ALIAS_PROBLEM;
+ goto return_results;
+ }
+ if ( is_entry_referral( np ) ) {
+ /* parent is a referral, don't allow add */
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: entry is referral\n" );
+ rs->sr_text = "new superior is a referral";
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+ } else {
+ /* no parent, modrdn entry directly under root */
+ /* TODO: */
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: no parent, not implement yet\n" );
+ rs->sr_text = "not implement yet";
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: wr to new parent's children OK\n" );
+ new_parent_dn = np_dn;
+ }
+
+ /* Build target dn and make sure target entry doesn't exist already. */
+ if (!new_dn.bv_val) {
+ build_new_dn( &new_dn, new_parent_dn, &op->oq_modrdn.rs_newrdn, NULL );
+ }
+
+ if (!new_ndn.bv_val) {
+ struct berval bv = {0, NULL};
+ dnNormalize( 0, NULL, NULL, &new_dn, &bv, op->o_tmpmemctx );
+ ber_dupbv( &new_ndn, &bv );
+ /* FIXME: why not call dnNormalize() w/o ctx? */
+ op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: new ndn=%s\n", new_ndn.bv_val );
+
+ /* check new entry */
+ rc = wt_dn2entry(op->o_bd, wc, &new_ndn, &ne);
+ switch( rc ) {
+ case 0:
+ /* Allow rename to same DN */
+ if(e->e_id == ne->e_id){
+ break;
+ }
+ rs->sr_err = LDAP_ALREADY_EXISTS;
+ goto return_results;
+ break;
+ case WT_NOTFOUND:
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "<== wt_modrdn: wt_dn2entry failed %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ assert( op->orr_modlist != NULL );
+
+ if( op->o_preread ) {
+ if( preread_ctrl == NULL ) {
+ preread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if( slap_read_controls( op, rs, e,
+ &slap_pre_read_bv, preread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_modrdn: pre-read failed!\n" );
+ if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ /* begin transaction */
+ rc = wc->session->begin_transaction(wc->session, NULL);
+ if( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: begin_transaction failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "begin_transaction failed";
+ goto return_results;
+ }
+ wc->is_begin_transaction = 1;
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: session id: %p\n", wc->session );
+
+ /* delete old DN */
+ rc = wt_dn2id_delete( op, wc, &e->e_nname);
+ if ( rc ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<== wt_modrdn: delete failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "dn2id delete failed";
+ goto return_results;
+ }
+
+ /* copy the entry, then override some fields */
+ dummy = *e;
+ dummy.e_name = new_dn;
+ dummy.e_nname = new_ndn;
+ dummy.e_attrs = NULL;
+
+ /* add new DN */
+ rc = wt_dn2id_add( op, wc, np?np->e_id:p->e_id, &dummy );
+ if ( rc ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<== wt_modrdn: add failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "DN add failed";
+ goto return_results;
+ }
+ dummy.e_attrs = e->e_attrs;
+
+ rc = wt_modify_internal( op, wc, op->orm_modlist,
+ &dummy, &rs->sr_text, textbuf, textlen );
+ if( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<== wt_modrdn: modify failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ if ( dummy.e_attrs == e->e_attrs ) dummy.e_attrs = NULL;
+ goto return_results;
+ }
+
+ /* update entry */
+ rc = wt_id2entry_update( op, wc, &dummy );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_modrdn: id2entry update failed(%d)\n", rc );
+ if ( rc == LDAP_ADMINLIMIT_EXCEEDED ) {
+ rs->sr_text = "entry too big";
+ } else {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "entry update failed";
+ }
+ goto return_results;
+ }
+
+ if ( p_ndn.bv_len != 0 ) {
+ parent_is_glue = is_entry_glue(p);
+ /* TODO: glue entry handling */
+ }
+
+ if( op->o_postread ) {
+ if( postread_ctrl == NULL ) {
+ postread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if( slap_read_controls( op, rs, &dummy,
+ &slap_post_read_bv, postread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_modrdn: post-read failed!\n" );
+ if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ if( op->o_noop ) {
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ goto return_results;
+ }
+
+ rc = wc->session->commit_transaction(wc->session, NULL);
+ wc->is_begin_transaction = 0;
+ if( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_modrdn: commit failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "commit failed";
+ goto return_results;
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ "wt_modrdn: rdn modified%s id=%08lx dn=\"%s\"\n",
+ op->o_noop ? " (no-op)" : "",
+ dummy.e_id, op->o_req_dn.bv_val );
+
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+ if ( dummy.e_attrs ) {
+ attrs_free( dummy.e_attrs );
+ }
+ send_ldap_result( op, rs );
+
+ if ( rs->sr_err == LDAP_SUCCESS && parent_is_glue && parent_is_leaf ) {
+ op->o_delete_glue_parent = 1;
+ }
+
+done:
+ if( wc && wc->is_begin_transaction ){
+ Debug( LDAP_DEBUG_TRACE, "wt_modrdn: rollback transaction\n" );
+ wc->session->rollback_transaction(wc->session, NULL);
+ wc->is_begin_transaction = 0;
+ }
+
+ slap_graduate_commit_csn( op );
+
+ if( new_dn.bv_val != NULL ) free( new_dn.bv_val );
+ if( new_ndn.bv_val != NULL ) free( new_ndn.bv_val );
+
+ /* free entry */
+ if( e != NULL ) {
+ wt_entry_return( e );
+ }
+ /* free parent entry */
+ if( p != NULL ) {
+ wt_entry_return( p );
+ }
+ /* free new entry */
+ if( ne != NULL ) {
+ wt_entry_return( ne );
+ }
+ /* free new parent entry */
+ if( np != NULL ) {
+ wt_entry_return( np );
+ }
+
+ if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
+ slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
+ }
+ if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
+ slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
+ }
+ return rs->sr_err;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/nextid.c b/servers/slapd/back-wt/nextid.c
new file mode 100644
index 0000000..99620e9
--- /dev/null
+++ b/servers/slapd/back-wt/nextid.c
@@ -0,0 +1,88 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "slap-config.h"
+
+int wt_next_id(BackendDB *be, ID *out){
+ struct wt_info *wi = (struct wt_info *) be->be_private;
+ *out = __sync_add_and_fetch(&wi->wi_lastid, 1);
+ return 0;
+}
+
+int wt_last_id( BackendDB *be, WT_SESSION *session, ID *out )
+{
+ WT_CURSOR *cursor;
+ int rc;
+ uint64_t id;
+
+ rc = session->open_cursor(session, WT_TABLE_ID2ENTRY, NULL, NULL, &cursor);
+ if(rc){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_last_id: open_cursor failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return rc;
+ }
+
+ rc = cursor->prev(cursor);
+ switch(rc) {
+ case 0:
+ rc = cursor->get_key(cursor, &id);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_last_id: get_key failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return rc;
+ }
+ *out = id;
+ break;
+ case WT_NOTFOUND:
+ /* no entry */
+ *out = 0;
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_last_id: prev failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ }
+
+ rc = cursor->close(cursor);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_last_id: close failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return rc;
+ }
+
+ return 0;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/operational.c b/servers/slapd/back-wt/operational.c
new file mode 100644
index 0000000..df9d0c6
--- /dev/null
+++ b/servers/slapd/back-wt/operational.c
@@ -0,0 +1,110 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-wt.h"
+#include "slap-config.h"
+
+int
+wt_hasSubordinates(
+ Operation *op,
+ Entry *e,
+ int *hasSubordinates )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ wt_ctx *wc = NULL;
+ int rc;
+
+ assert( e != NULL );
+
+ wc = wt_ctx_get(op, wi);
+ if( !wc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_hasSubordinates: wt_ctx_get failed\n" );
+ return LDAP_OTHER;
+ }
+
+ rc = wt_dn2id_has_children(op, wc, e->e_id);
+ switch(rc){
+ case 0:
+ *hasSubordinates = LDAP_COMPARE_TRUE;
+ break;
+ case WT_NOTFOUND:
+ *hasSubordinates = LDAP_COMPARE_FALSE;
+ rc = LDAP_SUCCESS;
+ break;
+ default:
+ Debug(LDAP_DEBUG_ANY,
+ "<=- wt_hasSubordinates: has_children failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ rc = LDAP_OTHER;
+ }
+ return rc;
+}
+
+/*
+ * sets the supported operational attributes (if required)
+ */
+int
+wt_operational(
+ Operation *op,
+ SlapReply *rs )
+{
+ Attribute **ap;
+
+ assert( rs->sr_entry != NULL );
+
+ for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next ) {
+ if ( (*ap)->a_desc == slap_schema.si_ad_hasSubordinates ) {
+ break;
+ }
+ }
+
+ if ( *ap == NULL &&
+ attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_hasSubordinates ) == NULL &&
+ ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
+ ad_inlist( slap_schema.si_ad_hasSubordinates, rs->sr_attrs ) ) )
+ {
+ int hasSubordinates, rc;
+
+ rc = wt_hasSubordinates( op, rs->sr_entry, &hasSubordinates );
+ if ( rc == LDAP_SUCCESS ) {
+ *ap = slap_operational_hasSubordinate( hasSubordinates == LDAP_COMPARE_TRUE );
+ assert( *ap != NULL );
+
+ ap = &(*ap)->a_next;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/proto-wt.h b/servers/slapd/back-wt/proto-wt.h
new file mode 100644
index 0000000..7fa2666
--- /dev/null
+++ b/servers/slapd/back-wt/proto-wt.h
@@ -0,0 +1,268 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#ifndef _PROTO_WT_H_
+#define _PROTO_WT_H_
+
+LDAP_BEGIN_DECL
+
+#define WT_UCTYPE "WT"
+
+/*
+ * attr.c
+ */
+
+AttrInfo *wt_attr_mask( struct wt_info *wi, AttributeDescription *desc );
+void wt_attr_flush( struct wt_info *wi );
+void wt_attr_index_unparse( struct wt_info *wi, BerVarray *bva );
+int wt_attr_index_config(
+ struct wt_info *wi,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv,
+ struct config_reply_s *c_reply);
+void wt_attr_index_destroy( struct wt_info *wi );
+
+/*
+ * id2entry.c
+ */
+int wt_id2entry(BackendDB *be, wt_ctx *wc, ID id, Entry **ep );
+int wt_id2entry_add(Operation *op, wt_ctx *wc, Entry *e );
+int wt_id2entry_update(Operation *op, wt_ctx *wc, Entry *e );
+int wt_id2entry_delete(Operation *op, wt_ctx *wc, Entry *e );
+
+BI_entry_release_rw wt_entry_release;
+BI_entry_get_rw wt_entry_get;
+
+int wt_entry_return(Entry *e);
+int wt_entry_release(Operation *op, Entry *e, int rw);
+
+/*
+ * idl.c
+ */
+
+unsigned wt_idl_search( ID *ids, ID id );
+
+ID wt_idl_first( ID *ids, ID *cursor );
+ID wt_idl_next( ID *ids, ID *cursor );
+int wt_idl_append_one( ID *ids, ID id );
+void wt_idl_sort( ID *ids, ID *tmp );
+int wt_idl_intersection( ID *a, ID *b );
+int wt_filter_candidates(
+ Operation *op,
+ wt_ctx *wc,
+ Filter *f,
+ ID *ids,
+ ID *tmp,
+ ID *stack );
+int
+wt_idl_union(
+ ID *a,
+ ID *b );
+
+/*
+ * index.c
+ */
+
+extern AttrInfo *
+wt_index_mask LDAP_P((
+ Backend *be,
+ AttributeDescription *desc,
+ struct berval *atname ));
+
+int wt_index_entry LDAP_P(( Operation *op, wt_ctx *wc, int r, Entry *e ));
+int wt_index_values(
+ Operation *op,
+ wt_ctx *wc,
+ AttributeDescription *desc,
+ BerVarray vals,
+ ID id,
+ int opid );
+int wt_index_param(
+ Backend *be,
+ AttributeDescription *desc,
+ int ftype,
+ slap_mask_t *maskp,
+ struct berval *prefixp );
+
+WT_CURSOR *wt_index_open(wt_ctx *wc, struct berval *name, int create);
+
+#define wt_index_entry_add(op,t,e) \
+ wt_index_entry((op),(t),SLAP_INDEX_ADD_OP,(e))
+#define wt_index_entry_del(op,t,e) \
+ wt_index_entry((op),(t),SLAP_INDEX_DELETE_OP,(e))
+
+/*
+ * key.c
+ */
+int
+wt_key_read( Backend *be,
+ WT_CURSOR *cursor,
+ struct berval *k,
+ ID *ids,
+ WT_CURSOR **saved_cursor,
+ int get_flag);
+
+int
+wt_key_change( Backend *be,
+ WT_CURSOR *cursor,
+ struct berval *k,
+ ID id,
+ int op);
+
+/*
+ * nextid.c
+ */
+int wt_next_id(BackendDB *be, ID *out);
+int wt_last_id( BackendDB *be, WT_SESSION *session, ID *out );
+
+/*
+ * modify.c
+ */
+int wt_modify_internal(
+ Operation *op,
+ wt_ctx *wc,
+ Modifications *modlist,
+ Entry *e,
+ const char **text,
+ char *textbuf,
+ size_t textlen );
+
+/*
+ * config.c
+ */
+int wt_back_init_cf( BackendInfo *bi );
+
+/*
+ * dn2id.c
+ */
+
+int
+wt_dn2id(
+ Operation *op,
+ wt_ctx *wc,
+ struct berval *ndn,
+ ID *id);
+
+int
+wt_dn2id_add(
+ Operation *op,
+ wt_ctx *wc,
+ ID pid,
+ Entry *e);
+
+int
+wt_dn2idl(
+ Operation *op,
+ wt_ctx *wc,
+ struct berval *ndn,
+ Entry *e,
+ ID *ids,
+ ID *stack);
+
+int
+wt_dn2id_delete(
+ Operation *op,
+ wt_ctx *wc,
+ struct berval *ndn);
+
+int
+wt_dn2id_has_children(
+ Operation *op,
+ wt_ctx *wc,
+ ID id );
+
+/*
+ * dn2entry.c
+ */
+int wt_dn2entry( BackendDB *be,
+ wt_ctx *wc,
+ struct berval *ndn,
+ Entry **ep );
+
+int wt_dn2pentry( BackendDB *be,
+ wt_ctx *wc,
+ struct berval *ndn,
+ Entry **ep );
+int wt_dn2aentry( BackendDB *be,
+ wt_ctx *wc,
+ struct berval *ndn,
+ Entry **ep );
+
+/*
+ * former ctx.c
+ */
+wt_ctx *wt_ctx_init(struct wt_info *wi);
+void wt_ctx_free(void *key, void *data);
+wt_ctx *wt_ctx_get(Operation *op, struct wt_info *wi);
+
+/*
+ * former cache.c
+ */
+int wt_idlcache_get(wt_ctx *wc, struct berval *ndn, int scope, ID *ids);
+int wt_idlcache_set(wt_ctx *wc, struct berval *ndn, int scope, ID *ids);
+int wt_idlcache_begin(wt_ctx *wc, struct berval *ndn, int scope);
+int wt_idlcache_clear(Operation *op, wt_ctx *wc, struct berval *ndn);
+
+/*
+ * former external.h
+ */
+
+extern BI_init wt_back_initialize;
+extern BI_db_config wt_db_config;
+extern BI_op_add wt_add;
+extern BI_op_bind wt_bind;
+extern BI_op_compare wt_compare;
+extern BI_op_delete wt_delete;
+extern BI_op_modify wt_modify;
+extern BI_op_modrdn wt_modrdn;
+extern BI_op_search wt_search;
+extern BI_op_extended wt_extended;
+
+extern BI_operational wt_operational;
+extern BI_has_subordinates wt_hasSubordinates;
+
+/* tools.c */
+int wt_entry_header(WT_ITEM *item, EntryHeader *eh);
+extern BI_tool_entry_open wt_tool_entry_open;
+extern BI_tool_entry_close wt_tool_entry_close;
+extern BI_tool_entry_first_x wt_tool_entry_first_x;
+extern BI_tool_entry_next wt_tool_entry_next;
+extern BI_tool_entry_get wt_tool_entry_get;
+extern BI_tool_entry_put wt_tool_entry_put;
+extern BI_tool_entry_reindex wt_tool_entry_reindex;
+extern BI_tool_dn2id_get wt_tool_dn2id_get;
+extern BI_tool_entry_modify wt_tool_entry_modify;
+extern BI_tool_entry_delete wt_tool_entry_delete;
+
+LDAP_END_DECL
+
+#endif /* _PROTO_WT_H */
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
diff --git a/servers/slapd/back-wt/search.c b/servers/slapd/back-wt/search.c
new file mode 100644
index 0000000..8a313b3
--- /dev/null
+++ b/servers/slapd/back-wt/search.c
@@ -0,0 +1,759 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-wt.h"
+#include "idl.h"
+
+static int search_aliases(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ WT_SESSION *session,
+ ID *ids,
+ ID *scopes,
+ ID *stack )
+{
+ /* TODO: search_aliases does not implement yet. */
+ WT_IDL_ZERO( ids );
+ return 0;
+}
+
+static int base_candidate(
+ BackendDB *be,
+ Entry *e,
+ ID *ids )
+{
+ Debug(LDAP_DEBUG_ARGS,
+ "base_candidate: base: \"%s\" (0x%08lx)\n",
+ e->e_nname.bv_val, (long) e->e_id );
+
+ ids[0] = 1;
+ ids[1] = e->e_id;
+ return 0;
+}
+
+/* Look for "objectClass Present" in this filter.
+ * Also count depth of filter tree while we're at it.
+ */
+static int oc_filter(
+ Filter *f,
+ int cur,
+ int *max )
+{
+ int rc = 0;
+
+ assert( f != NULL );
+
+ if( cur > *max ) *max = cur;
+
+ switch( f->f_choice ) {
+ case LDAP_FILTER_PRESENT:
+ if (f->f_desc == slap_schema.si_ad_objectClass) {
+ rc = 1;
+ }
+ break;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ cur++;
+ for ( f=f->f_and; f; f=f->f_next ) {
+ (void) oc_filter(f, cur, max);
+ }
+ break;
+
+ default:
+ break;
+ }
+ return rc;
+}
+
+static void search_stack_free( void *key, void *data )
+{
+ ber_memfree_x(data, NULL);
+}
+
+static void *search_stack( Operation *op )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ void *ret = NULL;
+
+ if ( op->o_threadctx ) {
+ ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)search_stack,
+ &ret, NULL );
+ } else {
+ ret = wi->wi_search_stack;
+ }
+
+ if ( !ret ) {
+ ret = ch_malloc( wi->wi_search_stack_depth * WT_IDL_UM_SIZE
+ * sizeof( ID ) );
+ if ( op->o_threadctx ) {
+ ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)search_stack,
+ ret, search_stack_free, NULL, NULL );
+ } else {
+ wi->wi_search_stack = ret;
+ }
+ }
+ return ret;
+}
+
+static int search_candidates(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ wt_ctx *wc,
+ ID *ids,
+ ID *scopes )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ int rc, depth = 1;
+ Filter f, rf, xf, nf;
+ ID *stack;
+ AttributeAssertion aa_ref = ATTRIBUTEASSERTION_INIT;
+ Filter sf;
+ AttributeAssertion aa_subentry = ATTRIBUTEASSERTION_INIT;
+
+ Debug(LDAP_DEBUG_TRACE,
+ "wt_search_candidates: base=\"%s\" (0x%08lx) scope=%d\n",
+ e->e_nname.bv_val, (long) e->e_id, op->oq_search.rs_scope );
+
+ xf.f_or = op->oq_search.rs_filter;
+ xf.f_choice = LDAP_FILTER_OR;
+ xf.f_next = NULL;
+
+ /* If the user's filter uses objectClass=*,
+ * these clauses are redundant.
+ */
+ if (!oc_filter(op->oq_search.rs_filter, 1, &depth)
+ && !get_subentries_visibility(op)) {
+ if( !get_manageDSAit(op) && !get_domainScope(op) ) {
+ /* match referral objects */
+ struct berval bv_ref = BER_BVC( "referral" );
+ rf.f_choice = LDAP_FILTER_EQUALITY;
+ rf.f_ava = &aa_ref;
+ rf.f_av_desc = slap_schema.si_ad_objectClass;
+ rf.f_av_value = bv_ref;
+ rf.f_next = xf.f_or;
+ xf.f_or = &rf;
+ depth++;
+ }
+ }
+
+ f.f_next = NULL;
+ f.f_choice = LDAP_FILTER_AND;
+ f.f_and = &nf;
+ /* Dummy; we compute scope separately now */
+ nf.f_choice = SLAPD_FILTER_COMPUTED;
+ nf.f_result = LDAP_SUCCESS;
+ nf.f_next = ( xf.f_or == op->oq_search.rs_filter )
+ ? op->oq_search.rs_filter : &xf ;
+ /* Filter depth increased again, adding dummy clause */
+ depth++;
+
+ if( get_subentries_visibility( op ) ) {
+ struct berval bv_subentry = BER_BVC( "subentry" );
+ sf.f_choice = LDAP_FILTER_EQUALITY;
+ sf.f_ava = &aa_subentry;
+ sf.f_av_desc = slap_schema.si_ad_objectClass;
+ sf.f_av_value = bv_subentry;
+ sf.f_next = nf.f_next;
+ nf.f_next = &sf;
+ }
+
+ /* Allocate IDL stack, plus 1 more for former tmp */
+ if ( depth+1 > wi->wi_search_stack_depth ) {
+ stack = ch_malloc( (depth + 1) * WT_IDL_UM_SIZE * sizeof( ID ) );
+ } else {
+ stack = search_stack( op );
+ }
+
+ if( op->ors_deref & LDAP_DEREF_SEARCHING ) {
+ rc = search_aliases( op, rs, e, wc->session, ids, scopes, stack );
+ if ( WT_IDL_IS_ZERO( ids ) && rc == LDAP_SUCCESS )
+ rc = wt_dn2idl( op, wc, &e->e_nname, e, ids, stack );
+ } else {
+ rc = wt_dn2idl(op, wc, &e->e_nname, e, ids, stack );
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ rc = wt_filter_candidates( op, wc, &f, ids,
+ stack, stack+WT_IDL_UM_SIZE );
+ }
+
+ if ( depth+1 > wi->wi_search_stack_depth ) {
+ ch_free( stack );
+ }
+
+ if( rc ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "wt_search_candidates: failed (rc=%d)\n", rc );
+
+ } else {
+ Debug(LDAP_DEBUG_TRACE,
+ "wt_search_candidates: id=%ld first=%ld last=%ld\n",
+ (long) ids[0],
+ (long) WT_IDL_FIRST(ids),
+ (long) WT_IDL_LAST(ids));
+ }
+ return 0;
+}
+
+static int
+parse_paged_cookie( Operation *op, SlapReply *rs )
+{
+ int rc = LDAP_SUCCESS;
+ PagedResultsState *ps = op->o_pagedresults_state;
+
+ /* this function must be invoked only if the pagedResults
+ * control has been detected, parsed and partially checked
+ * by the frontend */
+ assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
+
+ /* cookie decoding/checks deferred to backend... */
+ if ( ps->ps_cookieval.bv_len ) {
+ PagedResultsCookie reqcookie;
+ if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) {
+ /* bad cookie */
+ rs->sr_text = "paged results cookie is invalid";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ memcpy( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie ));
+
+ if ( reqcookie > ps->ps_cookie ) {
+ /* bad cookie */
+ rs->sr_text = "paged results cookie is invalid";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+
+ } else if ( reqcookie < ps->ps_cookie ) {
+ rs->sr_text = "paged results cookie is invalid or old";
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ } else {
+ /* we're going to use ps_cookie */
+ op->o_conn->c_pagedresults_state.ps_cookie = 0;
+ }
+
+done:;
+
+ return rc;
+}
+
+static void
+send_paged_response(
+ Operation *op,
+ SlapReply *rs,
+ ID *lastid,
+ int tentries )
+{
+ LDAPControl *ctrls[2];
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ PagedResultsCookie respcookie;
+ struct berval cookie;
+
+ Debug(LDAP_DEBUG_ARGS,
+ "send_paged_response: lastid=0x%08lx nentries=%d\n",
+ lastid ? *lastid : 0, rs->sr_nentries );
+
+ ctrls[1] = NULL;
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+
+ if ( lastid ) {
+ respcookie = ( PagedResultsCookie )(*lastid);
+ cookie.bv_len = sizeof( respcookie );
+ cookie.bv_val = (char *)&respcookie;
+
+ } else {
+ respcookie = ( PagedResultsCookie )0;
+ BER_BVSTR( &cookie, "" );
+ }
+
+ op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
+ op->o_conn->c_pagedresults_state.ps_count =
+ ((PagedResultsState *)op->o_pagedresults_state)->ps_count +
+ rs->sr_nentries;
+
+ /* return size of 0 -- no estimate */
+ ber_printf( ber, "{iO}", 0, &cookie );
+
+ ctrls[0] = op->o_tmpalloc( sizeof(LDAPControl), op->o_tmpmemctx );
+ if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
+ goto done;
+ }
+
+ ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
+ ctrls[0]->ldctl_iscritical = 0;
+
+ slap_add_ctrls( op, rs, ctrls );
+ rs->sr_err = LDAP_SUCCESS;
+ send_ldap_result( op, rs );
+
+done:
+ (void) ber_free_buf( ber );
+}
+
+int
+wt_search( Operation *op, SlapReply *rs )
+{
+ struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+ ID id, cursor;
+ ID lastid = NOID;
+ int manageDSAit;
+ wt_ctx *wc;
+ int rc = LDAP_OTHER;
+ Entry *e = NULL;
+ Entry *ae = NULL;
+ Entry *base = NULL;
+ slap_mask_t mask;
+ time_t stoptime;
+
+ ID candidates[WT_IDL_UM_SIZE];
+ ID scopes[WT_IDL_DB_SIZE];
+ int tentries = 0;
+ unsigned nentries = 0;
+
+ Debug( LDAP_DEBUG_ARGS, "==> wt_search: %s\n", op->o_req_dn.bv_val );
+
+ manageDSAit = get_manageDSAit( op );
+
+ wc = wt_ctx_get(op, wi);
+ if( !wc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_search: wt_ctx_get failed: %d\n", rc );
+ send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
+ return rc;
+ }
+
+ /* get entry */
+ rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ rc = wt_dn2aentry(op->o_bd, wc, &op->o_req_ndn, &ae);
+ break;
+ default:
+ /* TODO: error handling */
+ Debug( LDAP_DEBUG_ANY,
+ "<== wt_search: error at wt_dn2entry() rc=%d\n", rc );
+ send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
+ goto done;
+ }
+
+ if ( op->ors_deref & LDAP_DEREF_FINDING ) {
+ /* not implement yet */
+ }
+
+ if ( e == NULL ) {
+ if ( ae ) {
+ struct berval matched_dn = BER_BVNULL;
+ /* found ancestor entry */
+ if ( access_allowed( op, ae,
+ slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL ) ) {
+ BerVarray erefs = NULL;
+ ber_dupbv( &matched_dn, &ae->e_name );
+ erefs = is_entry_referral( ae )
+ ? get_entry_referrals( op, ae )
+ : NULL;
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_matched = matched_dn.bv_val;
+ if ( erefs ) {
+ rs->sr_ref = referral_rewrite( erefs, &matched_dn,
+ &op->o_req_dn, op->oq_search.rs_scope );
+ ber_bvarray_free( erefs );
+ }
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_search: ancestor is referral\n");
+ rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+ send_ldap_result( op, rs );
+ goto done;
+ }
+ }
+ Debug( LDAP_DEBUG_ARGS,
+ "wt_search: no such object %s\n",
+ op->o_req_dn.bv_val);
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ /* NOTE: __NEW__ "search" access is required
+ * on searchBase object */
+ if ( ! access_allowed_mask( op, e, slap_schema.si_ad_entry,
+ NULL, ACL_SEARCH, NULL, &mask ) )
+ {
+ if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ } else {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ }
+
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ if ( !manageDSAit && is_entry_referral( e ) ) {
+ struct berval matched_dn = BER_BVNULL;
+ BerVarray erefs = NULL;
+ ber_dupbv( &matched_dn, &e->e_name );
+ erefs = get_entry_referrals( op, e );
+ rs->sr_err = LDAP_REFERRAL;
+ if ( erefs ) {
+ rs->sr_ref = referral_rewrite( erefs, &matched_dn,
+ &op->o_req_dn, op->oq_search.rs_scope );
+ ber_bvarray_free( erefs );
+ if ( !rs->sr_ref ) {
+ rs->sr_text = "bad_referral object";
+ }
+ }
+ Debug( LDAP_DEBUG_ARGS, "wt_search: entry is referral\n");
+ rs->sr_matched = matched_dn.bv_val;
+ send_ldap_result( op, rs );
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ ber_memfree( matched_dn.bv_val );
+ rs->sr_matched = NULL;
+ goto done;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ /* compute it anyway; root does not use it */
+ stoptime = op->o_time + op->ors_tlimit;
+
+ base = e;
+
+ e = NULL;
+
+ /* select candidates */
+ if ( op->oq_search.rs_scope == LDAP_SCOPE_BASE ) {
+ rs->sr_err = base_candidate( op->o_bd, base, candidates );
+ }else{
+ WT_IDL_ZERO( candidates );
+ WT_IDL_ZERO( scopes );
+ rc = search_candidates( op, rs, base,
+ wc, candidates, scopes );
+ switch(rc){
+ case 0:
+ case WT_NOTFOUND:
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY, "wt_search: error search_candidates\n" );
+ send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
+ goto done;
+ }
+ }
+
+ /* start cursor at beginning of candidates.
+ */
+ cursor = 0;
+
+ if ( candidates[0] == 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "wt_search: no candidates\n" );
+ goto nochange;
+ }
+
+ if ( op->ors_limit &&
+ op->ors_limit->lms_s_unchecked != -1 &&
+ WT_IDL_N(candidates) > (unsigned) op->ors_limit->lms_s_unchecked )
+ {
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ goto done;
+ }
+
+ if ( op->ors_limit == NULL /* isroot == TRUE */ ||
+ !op->ors_limit->lms_s_pr_hide )
+ {
+ tentries = WT_IDL_N(candidates);
+ }
+
+ if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
+ /* TODO: pageresult */
+ PagedResultsState *ps = op->o_pagedresults_state;
+ /* deferred cookie parsing */
+ rs->sr_err = parse_paged_cookie( op, rs );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ cursor = (ID) ps->ps_cookie;
+ if ( cursor && ps->ps_size == 0 ) {
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = "search abandoned by pagedResult size=0";
+ send_ldap_result( op, rs );
+ goto done;
+ }
+ id = wt_idl_first( candidates, &cursor );
+ if ( id == NOID ) {
+ Debug( LDAP_DEBUG_TRACE, "wt_search: no paged results candidates\n" );
+ send_paged_response( op, rs, &lastid, 0 );
+
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+ nentries = ps->ps_count;
+ if ( id == (ID)ps->ps_cookie )
+ id = wt_idl_next( candidates, &cursor );
+ goto loop_begin;
+ }
+
+ for ( id = wt_idl_first( candidates, &cursor );
+ id != NOID ; id = wt_idl_next( candidates, &cursor ) )
+ {
+ int scopeok;
+
+loop_begin:
+
+ /* check for abandon */
+ if ( op->o_abandon ) {
+ rs->sr_err = SLAPD_ABANDON;
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ /* mostly needed by internal searches,
+ * e.g. related to syncrepl, for whom
+ * abandon does not get set... */
+ if ( slapd_shutdown ) {
+ rs->sr_err = LDAP_UNAVAILABLE;
+ send_ldap_disconnect( op, rs );
+ goto done;
+ }
+
+ /* check time limit */
+ if ( op->ors_tlimit != SLAP_NO_LIMIT
+ && slap_get_time() > stoptime )
+ {
+ rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
+ rs->sr_ref = rs->sr_v2ref;
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ goto done;
+ }
+
+ nentries++;
+
+ fetch_entry_retry:
+
+ rc = wt_id2entry(op->o_bd, wc, id, &e);
+ /* TODO: error handling */
+ if ( e == NULL ) {
+ /* TODO: */
+ goto loop_continue;
+ }
+ if ( is_entry_subentry( e ) ) {
+ if( op->oq_search.rs_scope != LDAP_SCOPE_BASE ) {
+ if(!get_subentries_visibility( op )) {
+ /* only subentries are visible */
+ goto loop_continue;
+ }
+
+ } else if ( get_subentries( op ) &&
+ !get_subentries_visibility( op ))
+ {
+ /* only subentries are visible */
+ goto loop_continue;
+ }
+
+ } else if ( get_subentries_visibility( op )) {
+ /* only subentries are visible */
+ goto loop_continue;
+ }
+
+ scopeok = 0;
+ switch( op->ors_scope ) {
+ case LDAP_SCOPE_BASE:
+ /* This is always true, yes? */
+ if ( id == base->e_id ) scopeok = 1;
+ break;
+ case LDAP_SCOPE_ONELEVEL:
+ scopeok = 1;
+ break;
+ case LDAP_SCOPE_CHILDREN:
+ if ( id == base->e_id ) break;
+ /* Fall-thru */
+ case LDAP_SCOPE_SUBTREE:
+ scopeok = dnIsSuffix(&e->e_nname, &base->e_nname);
+ break;
+ }
+
+ /* aliases were already dereferenced in candidate list */
+ if ( op->ors_deref & LDAP_DEREF_SEARCHING ) {
+ /* but if the search base is an alias, and we didn't
+ * deref it when finding, return it.
+ */
+ if ( is_entry_alias(e) &&
+ ((op->ors_deref & LDAP_DEREF_FINDING) ||
+ !bvmatch(&e->e_nname, &op->o_req_ndn)))
+ {
+ goto loop_continue;
+ }
+ /* TODO: alias handling */
+ }
+
+ /* Not in scope, ignore it */
+ if ( !scopeok )
+ {
+ Debug( LDAP_DEBUG_TRACE, "wt_search: %ld scope not okay\n",
+ (long) id );
+ goto loop_continue;
+ }
+
+ /*
+ * if it's a referral, add it to the list of referrals. only do
+ * this for non-base searches, and don't check the filter
+ * explicitly here since it's only a candidate anyway.
+ */
+ if ( !manageDSAit && op->oq_search.rs_scope != LDAP_SCOPE_BASE
+ && is_entry_referral( e ) )
+ {
+ BerVarray erefs = get_entry_referrals( op, e );
+ rs->sr_ref = referral_rewrite( erefs, &e->e_name, NULL,
+ op->oq_search.rs_scope == LDAP_SCOPE_ONELEVEL
+ ? LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
+ rs->sr_entry = e;
+ send_search_reference( op, rs );
+ rs->sr_entry = NULL;
+ ber_bvarray_free( rs->sr_ref );
+ ber_bvarray_free( erefs );
+ goto loop_continue;
+ }
+
+ if ( !manageDSAit && is_entry_glue( e )) {
+ goto loop_continue;
+ }
+
+ /* if it matches the filter and scope, send it */
+ rs->sr_err = test_filter( op, e, op->oq_search.rs_filter );
+ if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
+ /* check size limit */
+ if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
+ if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size ) {
+ wt_entry_return( e );
+ e = NULL;
+ send_paged_response( op, rs, &lastid, tentries );
+ goto done;
+ }
+ lastid = id;
+ }
+
+ if (e) {
+ /* safe default */
+ rs->sr_attrs = op->oq_search.rs_attrs;
+ rs->sr_operational_attrs = NULL;
+ rs->sr_ctrls = NULL;
+ rs->sr_entry = e;
+ RS_ASSERT( e->e_private != NULL );
+ rs->sr_flags = REP_ENTRY_MUSTRELEASE;
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_err = send_search_entry( op, rs );
+ rs->sr_attrs = NULL;
+ rs->sr_entry = NULL;
+ e = NULL;
+
+ switch ( rs->sr_err ) {
+ case LDAP_SUCCESS: /* entry sent ok */
+ break;
+ default: /* entry not sent */
+ break;
+ case LDAP_BUSY:
+ send_ldap_result( op, rs );
+ goto done;
+ case LDAP_UNAVAILABLE:
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ case LDAP_SIZELIMIT_EXCEEDED:
+ rs->sr_ref = rs->sr_v2ref;
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ goto done;
+ }
+ }
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ "wt_search: %ld does not match filter\n", (long) id );
+ }
+
+ loop_continue:
+ if( e ) {
+ wt_entry_return( e );
+ e = NULL;
+ }
+ }
+
+nochange:
+ rs->sr_ctrls = NULL;
+ rs->sr_ref = rs->sr_v2ref;
+ rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS : LDAP_REFERRAL;
+ rs->sr_rspoid = NULL;
+ if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
+ send_paged_response( op, rs, NULL, 0 );
+ } else {
+ send_ldap_result( op, rs );
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+
+done:
+
+ if( base ) {
+ wt_entry_return( base );
+ }
+
+ if( e ) {
+ wt_entry_return( e );
+ }
+
+ if( ae ) {
+ wt_entry_return( ae );
+ }
+
+ return rs->sr_err;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/tools.c b/servers/slapd/back-wt/tools.c
new file mode 100644
index 0000000..658837f
--- /dev/null
+++ b/servers/slapd/back-wt/tools.c
@@ -0,0 +1,712 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "slap-config.h"
+
+typedef struct dn_id {
+ ID id;
+ struct berval dn;
+} dn_id;
+
+#define HOLE_SIZE 4096
+static dn_id hbuf[HOLE_SIZE], *holes = hbuf;
+static unsigned nholes;
+
+static struct berval *tool_base;
+static int tool_scope;
+static Filter *tool_filter;
+
+static wt_ctx *wc;
+static WT_CURSOR *reader;
+static WT_ITEM item;
+
+int
+wt_tool_entry_open( BackendDB *be, int mode )
+{
+ struct wt_info *wi = (struct wt_info *) be->be_private;
+ int rc;
+
+ wc = wt_ctx_init(wi);
+ if( !wc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_open: wt_ctx_get failed\n" );
+ return -1;
+ }
+
+ rc = wc->session->open_cursor(wc->session, WT_TABLE_ID2ENTRY"(entry)"
+ ,NULL, NULL, &reader);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_open: cursor open failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+wt_tool_entry_close( BackendDB *be )
+{
+ if( reader ) {
+ reader->close(reader);
+ reader = NULL;
+ }
+
+ wt_ctx_free(NULL, wc);
+
+ if( nholes ) {
+ unsigned i;
+ fprintf( stderr, "Error, entries missing!\n");
+ for (i=0; i<nholes; i++) {
+ fprintf(stderr, " entry %ld: %s\n",
+ holes[i].id, holes[i].dn.bv_val);
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+ID
+wt_tool_entry_first_x( BackendDB *be,
+ struct berval *base,
+ int scope,
+ Filter *f )
+{
+ tool_base = base;
+ tool_scope = scope;
+ tool_filter = f;
+
+ return wt_tool_entry_next( be );
+}
+
+ID
+wt_tool_entry_next( BackendDB *be )
+{
+ int rc;
+ ID id;
+
+ rc = reader->next(reader);
+ switch( rc ){
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ return NOID;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_next: next failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return NOID;
+ }
+
+ rc = reader->get_key(reader, &id);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_next: get_key failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ }
+ return id;
+}
+
+static ber_len_t
+entry_getlen(unsigned char **buf)
+{
+ ber_len_t len;
+ int i;
+
+ len = *(*buf)++;
+ if (len <= 0x7f)
+ return len;
+ i = len & 0x7f;
+ len = 0;
+ for (;i > 0; i--) {
+ len <<= 8;
+ len |= *(*buf)++;
+ }
+ return len;
+}
+
+int wt_entry_header(WT_ITEM *item, EntryHeader *eh)
+{
+ unsigned char *ptr = (unsigned char *)item->data;
+
+ /* Some overlays can create empty entries
+ * so don't check for zeros here.
+ */
+ eh->nattrs = entry_getlen(&ptr);
+ eh->nvals = entry_getlen(&ptr);
+ eh->data = (char *)ptr;
+ return LDAP_SUCCESS;
+}
+
+Entry *
+wt_tool_entry_get( BackendDB *be, ID id )
+{
+ Entry *e = NULL;
+ static EntryHeader eh;
+ int rc, eoff;
+
+ assert( be != NULL );
+ assert( slapMode & SLAP_TOOL_MODE );
+
+ reader->set_key(reader, id);
+ rc = reader->search(reader);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_get: search failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ rc = reader->get_value(reader, &item);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_get: get_value failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+ rc = wt_entry_header( &item, &eh );
+ assert( rc == 0 );
+ eoff = eh.data - (char *)item.data;
+
+ eh.bv.bv_len = eh.nvals * sizeof( struct berval ) + item.size;
+ eh.bv.bv_val = ch_realloc( eh.bv.bv_val, eh.bv.bv_len );
+ memset(eh.bv.bv_val, 0xff, eh.bv.bv_len);
+ eh.data = eh.bv.bv_val + eh.nvals * sizeof( struct berval );
+ memcpy(eh.data, item.data, item.size);
+ eh.data += eoff;
+
+ rc = entry_decode( &eh, &e );
+ assert( rc == 0 );
+
+ if( rc == LDAP_SUCCESS ) {
+ e->e_id = id;
+ }
+
+done:
+ return e;
+}
+
+static int wt_tool_next_id(
+ Operation *op,
+ Entry *e,
+ struct berval *text,
+ int hole )
+{
+ struct berval dn = e->e_name;
+ struct berval ndn = e->e_nname;
+ struct berval pdn, npdn;
+ int rc;
+ ID id = 0;
+ ID pid = 0;
+
+ if(ndn.bv_len == 0){
+ e->e_id = 0;
+ return 0;
+ }
+
+ rc = wt_dn2id(op, wc, &ndn, &id);
+ if(rc == 0){
+ e->e_id = id;
+ }else if( rc == WT_NOTFOUND ){
+ if ( !be_issuffix( op->o_bd, &ndn ) ) {
+ ID eid = e->e_id;
+ dnParent( &dn, &pdn );
+ dnParent( &ndn, &npdn );
+ e->e_name = pdn;
+ e->e_nname = npdn;
+ rc = wt_tool_next_id( op, e, text, 1 );
+ e->e_name = dn;
+ e->e_nname = ndn;
+ if ( rc ) {
+ return rc;
+ }
+ /* If parent didn't exist, it was created just now
+ * and its ID is now in e->e_id. Make sure the current
+ * entry gets added under the new parent ID.
+ */
+ if ( eid != e->e_id ) {
+ pid = e->e_id;
+ }
+ }else{
+ pid = id;
+ }
+ wt_next_id( op->o_bd, &e->e_id );
+ rc = wt_dn2id_add(op, wc, pid, e);
+ if( rc ){
+ snprintf( text->bv_val, text->bv_len,
+ "wt_dn2id_add failed: %s (%d)",
+ wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> wt_tool_next_id: %s\n", text->bv_val );
+ }
+
+ }else if ( !hole ) {
+ unsigned i, j;
+ e->e_id = id;
+
+ for ( i=0; i<nholes; i++) {
+ if ( holes[i].id == e->e_id ) {
+ free(holes[i].dn.bv_val);
+ for (j=i;j<nholes;j++) holes[j] = holes[j+1];
+ holes[j].id = 0;
+ nholes--;
+ break;
+ } else if ( holes[i].id > e->e_id ) {
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+static int
+wt_tool_index_add(
+ Operation *op,
+ wt_ctx *wc,
+ Entry *e )
+{
+ return wt_index_entry_add( op, wc, e );
+}
+
+ID
+wt_tool_entry_put( BackendDB *be, Entry *e, struct berval *text )
+{
+ int rc;
+ Operation op = {0};
+ Opheader ohdr = {0};
+
+ assert( slapMode & SLAP_TOOL_MODE );
+ assert( text != NULL );
+ assert( text->bv_val != NULL );
+ assert( text->bv_val[0] == '\0' ); /* overconservative? */
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> wt_tool_entry_put: ( \"%s\" )\n", e->e_dn );
+
+ rc = wc->session->begin_transaction(wc->session, NULL);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_dn2id_add: begin_transaction failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return NOID;
+ }
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ rc = wt_tool_next_id( &op, e, text, 0 );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "wt_tool_next_id failed: %s (%d)",
+ wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> wt_tool_entry_put: %s\n", text->bv_val );
+ goto done;
+ }
+
+ rc = wt_id2entry_add( &op, wc, e );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "id2entry_add failed: %s (%d)",
+ wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> wt_tool_entry_put: %s\n",
+ text->bv_val );
+ goto done;
+ }
+
+ rc = wt_tool_index_add( &op, wc, e );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "index_entry_add failed: %s (%d)",
+ rc == LDAP_OTHER ? "Internal error" :
+ wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> wt_tool_entry_put: %s\n", text->bv_val );
+ goto done;
+ }
+
+done:
+ if ( rc == 0 ){
+ rc = wc->session->commit_transaction(wc->session, NULL);
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "txn_commit failed: %s (%d)",
+ wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> wt_tool_entry_put: %s\n", text->bv_val );
+ e->e_id = NOID;
+ }
+ }else{
+ rc = wc->session->rollback_transaction(wc->session, NULL);
+ snprintf( text->bv_val, text->bv_len,
+ "txn_aborted! %s (%d)",
+ rc == LDAP_OTHER ? "Internal error" :
+ wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> wt_tool_entry_put: %s\n", text->bv_val );
+ e->e_id = NOID;
+ }
+
+ return e->e_id;
+}
+
+int wt_tool_entry_reindex(
+ BackendDB *be,
+ ID id,
+ AttributeDescription **adv )
+{
+ struct wt_info *wi = (struct wt_info *) be->be_private;
+ int rc;
+ Entry *e;
+ Operation op = {0};
+ Opheader ohdr = {0};
+
+ Debug( LDAP_DEBUG_ARGS,
+ "=> wt_tool_entry_reindex( %ld )\n", (long) id );
+ assert( tool_base == NULL );
+ assert( tool_filter == NULL );
+
+ /* No indexes configured, nothing to do. Could return an
+ * error here to shortcut things.
+ */
+ if (!wi->wi_attrs) {
+ return 0;
+ }
+
+ /* Check for explicit list of attrs to index */
+ if ( adv ) {
+ int i, j, n;
+
+ if ( wi->wi_attrs[0]->ai_desc != adv[0] ) {
+ /* count */
+ for ( n = 0; adv[n]; n++ ) ;
+
+ /* insertion sort */
+ for ( i = 0; i < n; i++ ) {
+ AttributeDescription *ad = adv[i];
+ for ( j = i-1; j>=0; j--) {
+ if ( SLAP_PTRCMP( adv[j], ad ) <= 0 ) break;
+ adv[j+1] = adv[j];
+ }
+ adv[j+1] = ad;
+ }
+ }
+
+ for ( i = 0; adv[i]; i++ ) {
+ if ( wi->wi_attrs[i]->ai_desc != adv[i] ) {
+ for ( j = i+1; j < wi->wi_nattrs; j++ ) {
+ if ( wi->wi_attrs[j]->ai_desc == adv[i] ) {
+ AttrInfo *ai = wi->wi_attrs[i];
+ wi->wi_attrs[i] = wi->wi_attrs[j];
+ wi->wi_attrs[j] = ai;
+ break;
+ }
+ }
+ if ( j == wi->wi_nattrs ) {
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_reindex: no index configured for %s\n",
+ adv[i]->ad_cname.bv_val );
+ return -1;
+ }
+ }
+ }
+ wi->wi_nattrs = i;
+ }
+
+ e = wt_tool_entry_get( be, id );
+
+ if( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_reindex"
+ ": could not locate id=%ld\n",
+ (long) id );
+ return -1;
+ }
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ rc = wc->session->begin_transaction(wc->session, NULL);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_reindex: begin_transaction failed %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+ Debug( LDAP_DEBUG_TRACE,
+ "=> wt_tool_entry_reindex( %ld, \"%s\" )\n",
+ (long) id, e->e_dn );
+
+ rc = wt_tool_index_add( &op, wc, e );
+
+done:
+ if ( rc == 0 ){
+ rc = wc->session->commit_transaction(wc->session, NULL);
+ if( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "=> wt_tool_entry_reindex: commit_transaction failed %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ }
+ }else{
+ rc = wc->session->rollback_transaction(wc->session, NULL);
+ Debug( LDAP_DEBUG_ANY,
+ "=> wt_tool_entry_reindex: rollback transaction %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ }
+
+ wt_entry_release( &op, e, 0 );
+
+ return rc;
+}
+
+ID wt_tool_dn2id_get(
+ Backend *be,
+ struct berval *dn
+)
+{
+ Operation op = {0};
+ Opheader ohdr = {0};
+ ID id;
+ int rc;
+
+ if ( BER_BVISEMPTY(dn) )
+ return 0;
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ rc = wt_dn2id(&op, wc, dn, &id);
+ switch( rc ){
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ return NOID;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_get: entry get failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return NOID;
+ }
+ return id;
+}
+
+ID wt_tool_entry_modify(
+ BackendDB *be,
+ Entry *e,
+ struct berval *text )
+{
+ int rc;
+ Operation op = {0};
+ Opheader ohdr = {0};
+
+ assert( be != NULL );
+ assert( slapMode & SLAP_TOOL_MODE );
+
+ assert( text != NULL );
+ assert( text->bv_val != NULL );
+ assert( text->bv_val[0] == '\0' ); /* overconservative? */
+
+ assert ( e->e_id != NOID );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> wt_tool_entry_modify( %ld, \"%s\" )\n",
+ (long) e->e_id, e->e_dn );
+
+ rc = wc->session->begin_transaction(wc->session, NULL);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_modify"
+ ": begin_transaction failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ return NOID;
+ }
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ rc = wt_id2entry_update( &op, wc, e );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "id2entry_update failed: %s (%d)",
+ wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_modify: %s\n",
+ text->bv_val );
+ goto done;
+ }
+
+done:
+ if ( rc == 0 ){
+ rc = wc->session->commit_transaction(wc->session, NULL);
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "txn_commit failed: %s (%d)",
+ wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_modify: %s\n",
+ text->bv_val );
+ e->e_id = NOID;
+ }
+ }else{
+ rc = wc->session->rollback_transaction(wc->session, NULL);
+ snprintf( text->bv_val, text->bv_len,
+ "txn_aborted! %s (%d)",
+ rc == LDAP_OTHER ? "Internal error" :
+ wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY, "=> wt_tool_entry_modify: %s\n",
+ text->bv_val );
+ e->e_id = NOID;
+ }
+
+ return e->e_id;
+}
+
+int wt_tool_entry_delete(
+ BackendDB *be,
+ struct berval *ndn,
+ struct berval *text )
+{
+ struct wt_info *wi = (struct wt_info *) be->be_private;
+ int rc;
+ Operation op = {0};
+ Opheader ohdr = {0};
+ Entry *e = NULL;
+
+ assert( be != NULL );
+ assert( slapMode & SLAP_TOOL_MODE );
+
+ assert( text != NULL );
+ assert( text->bv_val != NULL );
+ assert( text->bv_val[0] == '\0' ); /* overconservative? */
+
+ assert ( ndn != NULL );
+ assert ( ndn->bv_val != NULL );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> wt_tool_entry_delete( %s )\n",
+ ndn->bv_val );
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ /* get entry */
+ rc = wt_dn2entry(op.o_bd, wc, ndn, &e);
+ switch( rc ) {
+ case 0:
+ break;
+ case WT_NOTFOUND:
+ Debug( LDAP_DEBUG_ARGS,
+ "<== wt_tool_entry_delete: no such object %s\n",
+ ndn->bv_val);
+ goto done;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_delete: error at wt_dn2entry() rc=%d\n",
+ rc );
+ goto done;
+ }
+
+ rc = wt_dn2id_has_children( &op, wc, e->e_id );
+ if( rc != WT_NOTFOUND ) {
+ /* subordinate objects must be deleted first */
+ rc = -1;
+ goto done;
+ }
+
+ rc = wc->session->begin_transaction(wc->session, NULL);
+ if( rc ){
+ Debug( LDAP_DEBUG_ANY,
+ "wt_tool_entry_delete: begin_transaction failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ goto done;
+ }
+
+ /* delete from dn2id */
+ rc = wt_dn2id_delete( &op, wc, &e->e_nname);
+ if ( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_tool_entry_delete: dn2id failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto done;
+ }
+
+ /* delete indices for old attributes */
+ rc = wt_index_entry_del( &op, wc, e );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_tool_entry_delete: index delete failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto done;
+ }
+
+ /* delete from id2entry */
+ rc = wt_id2entry_delete( &op, wc, e );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<== wt_tool_entry_delete: id2entry failed: %s (%d)\n",
+ wiredtiger_strerror(rc), rc );
+ wc->session->rollback_transaction(wc->session, NULL);
+ goto done;
+ }
+
+ rc = wc->session->commit_transaction(wc->session, NULL);
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "txn_commit failed: %s (%d)",
+ wiredtiger_strerror(rc), rc );
+ Debug( LDAP_DEBUG_ANY,
+ "=> wt_tool_entry_delete: %s\n",
+ text->bv_val );
+ goto done;
+ }
+
+done:
+ /* free entry */
+ if( e != NULL ) {
+ wt_entry_return( e );
+ }
+ return rc;
+}
+
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/backend.c b/servers/slapd/backend.c
new file mode 100644
index 0000000..cfe35aa
--- /dev/null
+++ b/servers/slapd/backend.c
@@ -0,0 +1,2055 @@
+/* backend.c - routines for dealing with back-end databases */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <sys/stat.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+#include "lber_pvt.h"
+
+/*
+ * If a module is configured as dynamic, its header should not
+ * get included into slapd. While this is a general rule and does
+ * not have much of an effect in UNIX, this rule should be adhered
+ * to for Windows, where dynamic object code should not be implicitly
+ * imported into slapd without appropriate __declspec(dllimport) directives.
+ */
+
+int nBackendInfo = 0;
+slap_bi_head backendInfo = LDAP_STAILQ_HEAD_INITIALIZER(backendInfo);
+
+int nBackendDB = 0;
+slap_be_head backendDB = LDAP_STAILQ_HEAD_INITIALIZER(backendDB);
+
+static int
+backend_init_controls( BackendInfo *bi )
+{
+ if ( bi->bi_controls ) {
+ int i;
+
+ for ( i = 0; bi->bi_controls[ i ]; i++ ) {
+ int cid;
+
+ if ( slap_find_control_id( bi->bi_controls[ i ], &cid )
+ == LDAP_CONTROL_NOT_FOUND )
+ {
+ if ( !( slapMode & SLAP_TOOL_MODE ) ) {
+ assert( 0 );
+ }
+
+ return -1;
+ }
+
+ bi->bi_ctrls[ cid ] = 1;
+ }
+ }
+
+ return 0;
+}
+
+extern int syncrepl_monitor_init(void);
+
+int backend_init(void)
+{
+ int rc = -1;
+ BackendInfo *bi;
+
+ if((nBackendInfo != 0) || !LDAP_STAILQ_EMPTY(&backendInfo)) {
+ /* already initialized */
+ Debug( LDAP_DEBUG_ANY,
+ "backend_init: already initialized\n" );
+ return -1;
+ }
+
+ for( bi=slap_binfo; bi->bi_type != NULL; bi++,nBackendInfo++ ) {
+ assert( bi->bi_init != 0 );
+
+ rc = bi->bi_init( bi );
+
+ if(rc != 0) {
+ Debug( LDAP_DEBUG_ANY,
+ "backend_init: initialized for type \"%s\"\n",
+ bi->bi_type );
+ /* destroy those we've already inited */
+ for( nBackendInfo--;
+ nBackendInfo >= 0 ;
+ nBackendInfo-- )
+ {
+ if ( slap_binfo[nBackendInfo].bi_destroy ) {
+ slap_binfo[nBackendInfo].bi_destroy(
+ &slap_binfo[nBackendInfo] );
+ }
+ }
+ return rc;
+ }
+
+ LDAP_STAILQ_INSERT_TAIL(&backendInfo, bi, bi_next);
+ }
+ /* HACK: need schema defined in deterministic order */
+ syncrepl_monitor_init();
+
+ if ( nBackendInfo > 0) {
+ return 0;
+ }
+
+#ifdef SLAPD_MODULES
+ return 0;
+#else
+
+ Debug( LDAP_DEBUG_ANY,
+ "backend_init: failed\n" );
+
+ return rc;
+#endif /* SLAPD_MODULES */
+}
+
+int backend_add(BackendInfo *aBackendInfo)
+{
+ int rc = 0;
+
+ if ( aBackendInfo->bi_init == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "backend_add: "
+ "backend type \"%s\" does not have the (mandatory)init function\n",
+ aBackendInfo->bi_type );
+ return -1;
+ }
+
+ rc = aBackendInfo->bi_init(aBackendInfo);
+ if ( rc != 0) {
+ Debug( LDAP_DEBUG_ANY,
+ "backend_add: initialization for type \"%s\" failed\n",
+ aBackendInfo->bi_type );
+ return rc;
+ }
+
+ (void)backend_init_controls( aBackendInfo );
+
+ /* now add the backend type to the Backend Info List */
+ LDAP_STAILQ_INSERT_TAIL( &backendInfo, aBackendInfo, bi_next );
+ nBackendInfo++;
+ return 0;
+}
+
+static int
+backend_set_controls( BackendDB *be )
+{
+ BackendInfo *bi = be->bd_info;
+
+ /* back-relay takes care of itself; so may do other */
+ if ( overlay_is_over( be ) ) {
+ bi = ((slap_overinfo *)be->bd_info->bi_private)->oi_orig;
+ }
+
+ if ( bi->bi_controls ) {
+ if ( be->be_ctrls[ SLAP_MAX_CIDS ] == 0 ) {
+ AC_MEMCPY( be->be_ctrls, bi->bi_ctrls,
+ sizeof( be->be_ctrls ) );
+ be->be_ctrls[ SLAP_MAX_CIDS ] = 1;
+
+ } else {
+ int i;
+
+ for ( i = 0; i < SLAP_MAX_CIDS; i++ ) {
+ if ( bi->bi_ctrls[ i ] ) {
+ be->be_ctrls[ i ] = bi->bi_ctrls[ i ];
+ }
+ }
+ }
+
+ }
+
+ return 0;
+}
+
+/* startup a specific backend database */
+int backend_startup_one(Backend *be, ConfigReply *cr)
+{
+ int rc = 0;
+
+ assert( be != NULL );
+
+ LDAP_TAILQ_INIT( &be->be_pcsn_st.be_pcsn_list );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "backend_startup_one: starting \"%s\"\n",
+ be->be_suffix ? be->be_suffix[0].bv_val : "(unknown)" );
+
+ /* set database controls */
+ (void)backend_set_controls( be );
+
+#if 0
+ if ( !BER_BVISEMPTY( &be->be_rootndn )
+ && select_backend( &be->be_rootndn, 0 ) == be
+ && BER_BVISNULL( &be->be_rootpw ) )
+ {
+ /* warning: if rootdn entry is created,
+ * it can take rootdn privileges;
+ * set empty rootpw to prevent */
+ }
+#endif
+
+ if ( be->bd_info->bi_db_open ) {
+ rc = be->bd_info->bi_db_open( be, cr );
+ if ( rc == 0 ) {
+ (void)backend_set_controls( be );
+ be->be_flags |= SLAP_DBFLAG_OPEN;
+
+ } else {
+ char *type = be->bd_info->bi_type;
+ char *suffix = "(null)";
+
+ if ( overlay_is_over( be ) ) {
+ slap_overinfo *oi = (slap_overinfo *)be->bd_info->bi_private;
+ type = oi->oi_orig->bi_type;
+ }
+
+ if ( be->be_suffix != NULL && !BER_BVISNULL( &be->be_suffix[0] ) ) {
+ suffix = be->be_suffix[0].bv_val;
+ }
+
+ Debug( LDAP_DEBUG_ANY,
+ "backend_startup_one (type=%s, suffix=\"%s\"): "
+ "bi_db_open failed! (%d)\n",
+ type, suffix, rc );
+ }
+ }
+
+ return rc;
+}
+
+int backend_startup(Backend *be)
+{
+ int i;
+ int rc = 0;
+ BackendInfo *bi;
+ ConfigReply cr={0, ""};
+
+ if( ! ( nBackendDB > 0 ) ) {
+ /* no databases */
+ Debug( LDAP_DEBUG_ANY,
+ "backend_startup: %d databases to startup.\n",
+ nBackendDB );
+ return 1;
+ }
+
+ if(be != NULL) {
+ /* silent noop if disabled */
+ if ( SLAP_DBDISABLED( be ))
+ return 0;
+ if ( be->bd_info->bi_open ) {
+ rc = be->bd_info->bi_open( be->bd_info );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "backend_startup: bi_open failed!\n" );
+
+ return rc;
+ }
+ }
+
+ return backend_startup_one( be, &cr );
+ }
+
+ /* open frontend, if required */
+ if ( frontendDB->bd_info->bi_db_open ) {
+ rc = frontendDB->bd_info->bi_db_open( frontendDB, &cr );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "backend_startup: bi_db_open(frontend) failed! (%d)\n",
+ rc );
+ return rc;
+ }
+ frontendDB->be_flags |= SLAP_DBFLAG_OPEN;
+ }
+
+ /* open each backend type */
+ i = -1;
+ LDAP_STAILQ_FOREACH(bi, &backendInfo, bi_next) {
+ i++;
+ if( bi->bi_nDB == 0) {
+ /* no database of this type, don't open */
+ continue;
+ }
+
+ if( bi->bi_open ) {
+ rc = bi->bi_open( bi );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "backend_startup: bi_open %d (%s) failed!\n",
+ i, bi->bi_type );
+ return rc;
+ }
+ }
+
+ (void)backend_init_controls( bi );
+ }
+
+ /* open each backend database */
+ i = -1;
+ LDAP_STAILQ_FOREACH(be, &backendDB, be_next) {
+ i++;
+ if ( SLAP_DBDISABLED( be ))
+ continue;
+ if ( be->be_suffix == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "backend_startup: warning, database %d (%s) "
+ "has no suffix\n",
+ i, be->bd_info->bi_type );
+ }
+
+ rc = backend_startup_one( be, &cr );
+
+ if ( rc ) return rc;
+ }
+
+ return rc;
+}
+
+int backend_num( Backend *be )
+{
+ int i = 0;
+ BackendDB *b2;
+
+ if( be == NULL ) return -1;
+
+ LDAP_STAILQ_FOREACH( b2, &backendDB, be_next ) {
+ if( be == b2 ) return i;
+ i++;
+ }
+ return -1;
+}
+
+int backend_shutdown( Backend *be )
+{
+ int rc = 0;
+ BackendInfo *bi;
+
+ if( be != NULL ) {
+ /* shutdown a specific backend database */
+
+ if ( be->bd_info->bi_nDB == 0 ) {
+ /* no database of this type, we never opened it */
+ return 0;
+ }
+
+ if ( be->bd_info->bi_db_close ) {
+ rc = be->bd_info->bi_db_close( be, NULL );
+ be->be_flags &= ~SLAP_DBFLAG_OPEN;
+ if ( rc ) return rc;
+ }
+
+ if( be->bd_info->bi_close ) {
+ rc = be->bd_info->bi_close( be->bd_info );
+ if ( rc ) return rc;
+ }
+
+ return 0;
+ }
+
+ /* close each backend database */
+ LDAP_STAILQ_FOREACH( be, &backendDB, be_next ) {
+ if ( SLAP_DBDISABLED( be ))
+ continue;
+ if ( be->bd_info->bi_db_close ) {
+ be->bd_info->bi_db_close( be, NULL );
+ be->be_flags &= ~SLAP_DBFLAG_OPEN;
+ }
+
+ if(rc != 0) {
+ Debug( LDAP_DEBUG_ANY,
+ "backend_close: bi_db_close %s failed!\n",
+ be->be_type );
+ }
+ }
+
+ /* close each backend type */
+ LDAP_STAILQ_FOREACH( bi, &backendInfo, bi_next ) {
+ if( bi->bi_nDB == 0 ) {
+ /* no database of this type */
+ continue;
+ }
+
+ if( bi->bi_close ) {
+ bi->bi_close( bi );
+ }
+ }
+
+ /* close frontend, if required */
+ if ( frontendDB->bd_info->bi_db_close ) {
+ rc = frontendDB->bd_info->bi_db_close ( frontendDB, NULL );
+ frontendDB->be_flags &= ~SLAP_DBFLAG_OPEN;
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "backend_startup: bi_db_close(frontend) failed! (%d)\n",
+ rc );
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * This function is supposed to be the exact counterpart
+ * of backend_startup_one(), although this one calls bi_db_destroy()
+ * while backend_startup_one() calls bi_db_open().
+ *
+ * Make sure backend_stopdown_one() destroys resources allocated
+ * by backend_startup_one(); only call backend_destroy_one() when
+ * all stuff in a BackendDB needs to be destroyed
+ */
+void
+backend_stopdown_one( BackendDB *bd )
+{
+ struct slap_csn_entry *csne;
+ csne = LDAP_TAILQ_FIRST( &bd->be_pcsn_st.be_pcsn_list );
+ while ( csne ) {
+ struct slap_csn_entry *tmp_csne = csne;
+
+ LDAP_TAILQ_REMOVE( &bd->be_pcsn_st.be_pcsn_list, csne, ce_csn_link );
+ ch_free( csne->ce_csn.bv_val );
+ csne = LDAP_TAILQ_NEXT( csne, ce_csn_link );
+ ch_free( tmp_csne );
+ }
+
+ if ( bd->bd_info->bi_db_destroy ) {
+ bd->bd_info->bi_db_destroy( bd, NULL );
+ }
+}
+
+void backend_destroy_one( BackendDB *bd, int dynamic )
+{
+ if ( dynamic ) {
+ LDAP_STAILQ_REMOVE(&backendDB, bd, BackendDB, be_next );
+ }
+
+ if ( bd->be_syncinfo ) {
+ syncinfo_free( bd->be_syncinfo, 1 );
+ }
+
+ backend_stopdown_one( bd );
+
+ ber_bvarray_free( bd->be_suffix );
+ ber_bvarray_free( bd->be_nsuffix );
+ if ( !BER_BVISNULL( &bd->be_rootdn ) ) {
+ free( bd->be_rootdn.bv_val );
+ }
+ if ( !BER_BVISNULL( &bd->be_rootndn ) ) {
+ free( bd->be_rootndn.bv_val );
+ }
+ if ( !BER_BVISNULL( &bd->be_rootpw ) ) {
+ free( bd->be_rootpw.bv_val );
+ }
+ acl_destroy( bd->be_acl );
+ limits_destroy( bd->be_limits );
+ if ( bd->be_extra_anlist ) {
+ anlist_free( bd->be_extra_anlist, 1, NULL );
+ }
+ if ( !BER_BVISNULL( &bd->be_update_ndn ) ) {
+ ch_free( bd->be_update_ndn.bv_val );
+ }
+ if ( bd->be_update_refs ) {
+ ber_bvarray_free( bd->be_update_refs );
+ }
+
+ ldap_pvt_thread_mutex_destroy( &bd->be_pcsn_st.be_pcsn_mutex );
+
+ if ( dynamic ) {
+ free( bd );
+ }
+}
+
+int backend_destroy(void)
+{
+ BackendDB *bd;
+ BackendInfo *bi;
+
+ /* destroy each backend database */
+ while (( bd = LDAP_STAILQ_FIRST(&backendDB))) {
+ backend_destroy_one( bd, 1 );
+ }
+
+ /* destroy each backend type */
+ LDAP_STAILQ_FOREACH( bi, &backendInfo, bi_next ) {
+ if( bi->bi_destroy ) {
+ bi->bi_destroy( bi );
+ }
+ }
+
+ nBackendInfo = 0;
+ LDAP_STAILQ_INIT(&backendInfo);
+
+ /* destroy frontend database */
+ bd = frontendDB;
+ if ( bd ) {
+ if ( bd->bd_info->bi_db_destroy ) {
+ bd->bd_info->bi_db_destroy( bd, NULL );
+ }
+ ber_bvarray_free( bd->be_suffix );
+ ber_bvarray_free( bd->be_nsuffix );
+ if ( !BER_BVISNULL( &bd->be_rootdn ) ) {
+ free( bd->be_rootdn.bv_val );
+ }
+ if ( !BER_BVISNULL( &bd->be_rootndn ) ) {
+ free( bd->be_rootndn.bv_val );
+ }
+ if ( !BER_BVISNULL( &bd->be_rootpw ) ) {
+ free( bd->be_rootpw.bv_val );
+ }
+ acl_destroy( bd->be_acl );
+ frontendDB = NULL;
+ }
+
+ return 0;
+}
+
+BackendInfo* backend_info(const char *type)
+{
+ BackendInfo *bi;
+
+ /* search for the backend type */
+ LDAP_STAILQ_FOREACH(bi,&backendInfo,bi_next) {
+ if( strcasecmp(bi->bi_type, type) == 0 ) {
+ return bi;
+ }
+ }
+
+ return NULL;
+}
+
+void
+backend_db_insert(
+ BackendDB *be,
+ int idx
+)
+{
+ /* If idx < 0, just add to end of list */
+ if ( idx < 0 ) {
+ LDAP_STAILQ_INSERT_TAIL(&backendDB, be, be_next);
+ } else if ( idx == 0 ) {
+ LDAP_STAILQ_INSERT_HEAD(&backendDB, be, be_next);
+ } else {
+ int i;
+ BackendDB *b2;
+
+ b2 = LDAP_STAILQ_FIRST(&backendDB);
+ idx--;
+ for (i=0; i<idx; i++) {
+ b2 = LDAP_STAILQ_NEXT(b2, be_next);
+ }
+ LDAP_STAILQ_INSERT_AFTER(&backendDB, b2, be, be_next);
+ }
+}
+
+void
+backend_db_move(
+ BackendDB *be,
+ int idx
+)
+{
+ LDAP_STAILQ_REMOVE(&backendDB, be, BackendDB, be_next);
+ backend_db_insert(be, idx);
+}
+
+BackendDB *
+backend_db_init(
+ const char *type,
+ BackendDB *b0,
+ int idx,
+ ConfigReply *cr)
+{
+ BackendInfo *bi = backend_info(type);
+ BackendDB *be = b0;
+ int rc = 0;
+
+ if( bi == NULL ) {
+ fprintf( stderr, "Unrecognized database type (%s)\n", type );
+ return NULL;
+ }
+
+ /* If be is provided, treat it as private. Otherwise allocate
+ * one and add it to the global list.
+ */
+ if ( !be ) {
+ be = ch_calloc( 1, sizeof(Backend) );
+ /* Just append */
+ if ( idx >= nbackends )
+ idx = -1;
+ nbackends++;
+ backend_db_insert( be, idx );
+ }
+
+ be->bd_info = bi;
+ be->bd_self = be;
+
+ be->be_def_limit = frontendDB->be_def_limit;
+ be->be_dfltaccess = frontendDB->be_dfltaccess;
+
+ be->be_restrictops = frontendDB->be_restrictops;
+ be->be_requires = frontendDB->be_requires;
+ be->be_ssf_set = frontendDB->be_ssf_set;
+
+ ldap_pvt_thread_mutex_init( &be->be_pcsn_st.be_pcsn_mutex );
+ be->be_pcsn_p = &be->be_pcsn_st;
+
+ /* assign a default depth limit for alias deref */
+ be->be_max_deref_depth = SLAPD_DEFAULT_MAXDEREFDEPTH;
+
+ if ( bi->bi_db_init ) {
+ rc = bi->bi_db_init( be, cr );
+ }
+
+ if ( rc != 0 ) {
+ fprintf( stderr, "database init failed (%s)\n", type );
+ /* If we created and linked this be, remove it and free it */
+ if ( !b0 ) {
+ LDAP_STAILQ_REMOVE(&backendDB, be, BackendDB, be_next);
+ ldap_pvt_thread_mutex_destroy( &be->be_pcsn_st.be_pcsn_mutex );
+ ch_free( be );
+ be = NULL;
+ nbackends--;
+ }
+ } else {
+ if ( !bi->bi_nDB ) {
+ backend_init_controls( bi );
+ }
+ bi->bi_nDB++;
+ }
+ return( be );
+}
+
+void
+be_db_close( void )
+{
+ BackendDB *be;
+
+ LDAP_STAILQ_FOREACH( be, &backendDB, be_next ) {
+ if ( be->bd_info->bi_db_close ) {
+ be->bd_info->bi_db_close( be, NULL );
+ be->be_flags &= ~SLAP_DBFLAG_OPEN;
+ }
+ }
+
+ if ( frontendDB->bd_info->bi_db_close ) {
+ frontendDB->bd_info->bi_db_close( frontendDB, NULL );
+ }
+
+}
+
+Backend *
+select_backend(
+ struct berval * dn,
+ int noSubs )
+{
+ int j;
+ ber_len_t len, dnlen = dn->bv_len;
+ Backend *be;
+
+ LDAP_STAILQ_FOREACH( be, &backendDB, be_next ) {
+ if ( be->be_nsuffix == NULL || SLAP_DBHIDDEN( be ) || SLAP_DBDISABLED( be )) {
+ continue;
+ }
+
+ for ( j = 0; !BER_BVISNULL( &be->be_nsuffix[j] ); j++ )
+ {
+ if ( ( SLAP_GLUE_SUBORDINATE( be ) ) && noSubs )
+ {
+ continue;
+ }
+
+ len = be->be_nsuffix[j].bv_len;
+
+ if ( len > dnlen ) {
+ /* suffix is longer than DN */
+ continue;
+ }
+
+ /*
+ * input DN is normalized, so the separator check
+ * need not look at escaping
+ */
+ if ( len && len < dnlen &&
+ !DN_SEPARATOR( dn->bv_val[(dnlen-len)-1] ))
+ {
+ continue;
+ }
+
+ if ( strcmp( be->be_nsuffix[j].bv_val,
+ &dn->bv_val[dnlen-len] ) == 0 )
+ {
+ return be;
+ }
+ }
+ }
+
+ return be;
+}
+
+int
+be_issuffix(
+ Backend *be,
+ struct berval *bvsuffix )
+{
+ int i;
+
+ if ( be->be_nsuffix == NULL ) {
+ return 0;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &be->be_nsuffix[i] ); i++ ) {
+ if ( bvmatch( &be->be_nsuffix[i], bvsuffix ) ) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int
+be_issubordinate(
+ Backend *be,
+ struct berval *bvsubordinate )
+{
+ int i;
+
+ if ( be->be_nsuffix == NULL ) {
+ return 0;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &be->be_nsuffix[i] ); i++ ) {
+ if ( dnIsSuffix( bvsubordinate, &be->be_nsuffix[i] ) ) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int
+be_isroot_dn( Backend *be, struct berval *ndn )
+{
+ if ( BER_BVISEMPTY( ndn ) || BER_BVISEMPTY( &be->be_rootndn ) ) {
+ return 0;
+ }
+
+ return dn_match( &be->be_rootndn, ndn );
+}
+
+int
+be_slurp_update( Operation *op )
+{
+ return ( SLAP_SLURP_SHADOW( op->o_bd ) &&
+ be_isupdate_dn( op->o_bd, &op->o_ndn ) );
+}
+
+int
+be_shadow_update( Operation *op )
+{
+ /* This assumes that all internal ops (connid <= -1000) on a syncrepl
+ * database are syncrepl operations.
+ */
+ return ( ( SLAP_SYNC_SHADOW( op->o_bd ) && SLAPD_SYNC_IS_SYNCCONN( op->o_connid ) ) ||
+ ( SLAP_SHADOW( op->o_bd ) && be_isupdate_dn( op->o_bd, &op->o_ndn ) ) );
+}
+
+int
+be_isupdate_dn( Backend *be, struct berval *ndn )
+{
+ if ( BER_BVISEMPTY( ndn ) || BER_BVISEMPTY( &be->be_update_ndn ) ) {
+ return 0;
+ }
+
+ return dn_match( &be->be_update_ndn, ndn );
+}
+
+struct berval *
+be_root_dn( Backend *be )
+{
+ return &be->be_rootdn;
+}
+
+int
+be_isroot( Operation *op )
+{
+ return be_isroot_dn( op->o_bd, &op->o_ndn );
+}
+
+int
+be_isroot_pw( Operation *op )
+{
+ return be_rootdn_bind( op, NULL ) == LDAP_SUCCESS;
+}
+
+/*
+ * checks if binding as rootdn
+ *
+ * return value:
+ * SLAP_CB_CONTINUE if not the rootdn, or if rootpw is null
+ * LDAP_SUCCESS if rootdn & rootpw
+ * LDAP_INVALID_CREDENTIALS if rootdn & !rootpw
+ *
+ * if rs != NULL
+ * if LDAP_SUCCESS, op->orb_edn is set
+ * if LDAP_INVALID_CREDENTIALS, response is sent to client
+ */
+int
+be_rootdn_bind( Operation *op, SlapReply *rs )
+{
+ int rc;
+#ifdef SLAPD_SPASSWD
+ void *old_authctx = NULL;
+#endif
+
+ assert( op->o_tag == LDAP_REQ_BIND );
+ assert( op->orb_method == LDAP_AUTH_SIMPLE );
+
+ if ( !be_isroot_dn( op->o_bd, &op->o_req_ndn ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( BER_BVISNULL( &op->o_bd->be_rootpw ) ) {
+ /* give the database a chance */
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( BER_BVISEMPTY( &op->o_bd->be_rootpw ) ) {
+ /* rootdn bind explicitly disallowed */
+ rc = LDAP_INVALID_CREDENTIALS;
+ if ( rs ) {
+ goto send_result;
+ }
+
+ return rc;
+ }
+
+#ifdef SLAPD_SPASSWD
+ ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)slap_sasl_bind,
+ op->o_conn->c_sasl_authctx, 0, &old_authctx, NULL );
+#endif
+
+ rc = lutil_passwd( &op->o_bd->be_rootpw, &op->orb_cred, NULL, NULL );
+
+#ifdef SLAPD_SPASSWD
+ ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)slap_sasl_bind,
+ old_authctx, 0, NULL, NULL );
+#endif
+
+ rc = ( rc == 0 ? LDAP_SUCCESS : LDAP_INVALID_CREDENTIALS );
+ if ( rs ) {
+send_result:;
+ rs->sr_err = rc;
+
+ Debug( LDAP_DEBUG_TRACE, "%s: rootdn=\"%s\" bind%s\n",
+ op->o_log_prefix, op->o_bd->be_rootdn.bv_val,
+ rc == LDAP_SUCCESS ? " succeeded" : " failed" );
+
+ if ( rc == LDAP_SUCCESS ) {
+ /* Set to the pretty rootdn */
+ ber_dupbv( &op->orb_edn, &op->o_bd->be_rootdn );
+
+ } else {
+ send_ldap_result( op, rs );
+ }
+ }
+
+ return rc;
+}
+
+/* Inlined in proto-slap.h, sans assertions, when !(USE_RS_ASSERT) */
+int
+(slap_bi_op)(
+ BackendInfo *bi,
+ slap_operation_t which,
+ Operation *op,
+ SlapReply *rs )
+{
+ int rc;
+#ifndef slap_bi_op
+ void (*rsCheck)( const SlapReply *rs ) =
+ which < op_aux_operational ? rs_assert_ready : rs_assert_ok;
+#else
+# define rsCheck(rs) ((void) 0)
+#endif
+ BI_op_func *fn;
+
+ assert( bi != NULL );
+ assert( (unsigned) which < (unsigned) op_last );
+
+ fn = (&bi->bi_op_bind)[ which ];
+
+ assert( op != NULL );
+ assert( rs != NULL );
+ assert( fn != 0 );
+ rsCheck( rs );
+
+ rc = fn( op, rs );
+
+#ifndef slap_bi_op
+ if ( rc != SLAP_CB_CONTINUE && rc != SLAP_CB_BYPASS ) {
+ int err = rs->sr_err;
+
+ if ( 0 ) /* TODO */
+ if ( err == LDAP_COMPARE_TRUE || err == LDAP_COMPARE_FALSE ) {
+ assert( which == op_compare );
+ assert( rc == LDAP_SUCCESS );
+ }
+
+ rsCheck = which < op_extended ? rs_assert_done : rs_assert_ok;
+ if ( which == op_aux_chk_referrals ) {
+ if ( rc == LDAP_SUCCESS ) rsCheck = rs_assert_ready;
+ else if ( rc == LDAP_REFERRAL ) rsCheck = rs_assert_done;
+ } else if ( which == op_bind ) {
+ if ( rc == LDAP_SUCCESS ) rsCheck = rs_assert_ok;
+ }
+
+ /* TODO: Just what is the relation between rc and rs->sr_err? */
+ if ( rc != err &&
+ (rc != LDAP_SUCCESS ||
+ (err != LDAP_COMPARE_TRUE && err != LDAP_COMPARE_FALSE)) )
+ {
+ rs->sr_err = rc;
+ rsCheck( rs );
+ rs->sr_err = err;
+ }
+ }
+ rsCheck( rs );
+#endif
+
+ return rc;
+}
+
+int
+be_entry_release_rw(
+ Operation *op,
+ Entry *e,
+ int rw )
+{
+ if ( op->o_bd->be_release ) {
+ /* free and release entry from backend */
+ return op->o_bd->be_release( op, e, rw );
+ } else {
+ /* free entry */
+ entry_free( e );
+ return 0;
+ }
+}
+
+int
+backend_unbind( Operation *op, SlapReply *rs )
+{
+ BackendDB *be;
+
+ LDAP_STAILQ_FOREACH( be, &backendDB, be_next ) {
+ if ( be->be_unbind ) {
+ op->o_bd = be;
+ be->be_unbind( op, rs );
+ }
+ }
+
+ return 0;
+}
+
+int
+backend_connection_init(
+ Connection *conn )
+{
+ BackendDB *be;
+
+ LDAP_STAILQ_FOREACH( be, &backendDB, be_next ) {
+ if ( be->be_connection_init ) {
+ be->be_connection_init( be, conn );
+ }
+ }
+
+ return 0;
+}
+
+int
+backend_connection_destroy(
+ Connection *conn )
+{
+ BackendDB *be;
+
+ LDAP_STAILQ_FOREACH( be, &backendDB, be_next ) {
+ if ( be->be_connection_destroy ) {
+ be->be_connection_destroy( be, conn);
+ }
+ }
+
+ return 0;
+}
+
+int
+backend_check_controls(
+ Operation *op,
+ SlapReply *rs )
+{
+ LDAPControl **ctrls = op->o_ctrls;
+ rs->sr_err = LDAP_SUCCESS;
+
+ if( ctrls ) {
+ for( ; *ctrls != NULL ; ctrls++ ) {
+ int cid;
+
+ switch ( slap_global_control( op, (*ctrls)->ldctl_oid, &cid ) ) {
+ case LDAP_CONTROL_NOT_FOUND:
+ /* unrecognized control */
+ if ( (*ctrls)->ldctl_iscritical ) {
+ /* should not be reachable */
+ Debug( LDAP_DEBUG_ANY, "backend_check_controls: "
+ "unrecognized critical control: %s\n",
+ (*ctrls)->ldctl_oid );
+ assert( 0 );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "backend_check_controls: "
+ "unrecognized non-critical control: %s\n",
+ (*ctrls)->ldctl_oid );
+ }
+ break;
+
+ case LDAP_COMPARE_FALSE:
+ if ( !op->o_bd->be_ctrls[cid] && (*ctrls)->ldctl_iscritical ) {
+#ifdef SLAP_CONTROL_X_WHATFAILED
+ if ( get_whatFailed( op ) ) {
+ char *oids[ 2 ];
+ oids[ 0 ] = (*ctrls)->ldctl_oid;
+ oids[ 1 ] = NULL;
+ slap_ctrl_whatFailed_add( op, rs, oids );
+ }
+#endif
+ /* RFC 4511 allows unavailableCriticalExtension to be
+ * returned when the server is unwilling to perform
+ * an operation extended by a recognized critical
+ * control.
+ */
+ rs->sr_text = "critical control unavailable in context";
+ rs->sr_err = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
+ goto done;
+ }
+ break;
+
+ case LDAP_COMPARE_TRUE:
+ break;
+
+ default:
+ /* unreachable */
+ Debug( LDAP_DEBUG_ANY,
+ "backend_check_controls: unable to check control: %s\n",
+ (*ctrls)->ldctl_oid );
+ assert( 0 );
+
+ rs->sr_text = "unable to check control";
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+ }
+ }
+
+#if 0 /* temporarily removed */
+ /* check should be generalized */
+ if( get_relax(op) && !be_isroot(op)) {
+ rs->sr_text = "requires manager authorization";
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ }
+#endif
+
+done:;
+ return rs->sr_err;
+}
+
+int
+backend_check_restrictions(
+ Operation *op,
+ SlapReply *rs,
+ struct berval *opdata )
+{
+ slap_mask_t restrictops;
+ slap_mask_t requires;
+ slap_mask_t opflag;
+ slap_mask_t exopflag = 0;
+ slap_ssf_set_t ssfs, *ssf;
+ int updateop = 0;
+ int starttls = 0;
+ int session = 0;
+
+ restrictops = frontendDB->be_restrictops;
+ requires = frontendDB->be_requires;
+ ssfs = frontendDB->be_ssf_set;
+ ssf = &ssfs;
+
+ if ( op->o_bd ) {
+ slap_ssf_t *fssf, *bssf;
+ int rc = SLAP_CB_CONTINUE, i;
+
+ if ( op->o_bd->be_chk_controls ) {
+ rc = ( *op->o_bd->be_chk_controls )( op, rs );
+ }
+
+ if ( rc == SLAP_CB_CONTINUE ) {
+ rc = backend_check_controls( op, rs );
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ return rs->sr_err;
+ }
+
+ restrictops |= op->o_bd->be_restrictops;
+ requires |= op->o_bd->be_requires;
+ bssf = &op->o_bd->be_ssf_set.sss_ssf;
+ fssf = &ssfs.sss_ssf;
+ for ( i=0; i < (int)(sizeof(ssfs)/sizeof(slap_ssf_t)); i++ ) {
+ if ( bssf[i] ) fssf[i] = bssf[i];
+ }
+ }
+
+ switch( op->o_tag ) {
+ case LDAP_REQ_ADD:
+ opflag = SLAP_RESTRICT_OP_ADD;
+ updateop++;
+ break;
+ case LDAP_REQ_BIND:
+ opflag = SLAP_RESTRICT_OP_BIND;
+ session++;
+ break;
+ case LDAP_REQ_COMPARE:
+ opflag = SLAP_RESTRICT_OP_COMPARE;
+ break;
+ case LDAP_REQ_DELETE:
+ updateop++;
+ opflag = SLAP_RESTRICT_OP_DELETE;
+ break;
+ case LDAP_REQ_EXTENDED:
+ opflag = SLAP_RESTRICT_OP_EXTENDED;
+
+ if( !opdata ) {
+ /* treat unspecified as a modify */
+ opflag = SLAP_RESTRICT_OP_MODIFY;
+ updateop++;
+ break;
+ }
+
+ if( bvmatch( opdata, &slap_EXOP_START_TLS ) ) {
+ session++;
+ starttls++;
+ exopflag = SLAP_RESTRICT_EXOP_START_TLS;
+ break;
+ }
+
+ if( bvmatch( opdata, &slap_EXOP_WHOAMI ) ) {
+ exopflag = SLAP_RESTRICT_EXOP_WHOAMI;
+ break;
+ }
+
+ if ( bvmatch( opdata, &slap_EXOP_CANCEL ) ) {
+ exopflag = SLAP_RESTRICT_EXOP_CANCEL;
+ break;
+ }
+
+ if ( bvmatch( opdata, &slap_EXOP_MODIFY_PASSWD ) ) {
+ exopflag = SLAP_RESTRICT_EXOP_MODIFY_PASSWD;
+ updateop++;
+ break;
+ }
+
+ /* treat everything else as a modify */
+ opflag = SLAP_RESTRICT_OP_MODIFY;
+ updateop++;
+ break;
+
+ case LDAP_REQ_MODIFY:
+ updateop++;
+ opflag = SLAP_RESTRICT_OP_MODIFY;
+ break;
+ case LDAP_REQ_RENAME:
+ updateop++;
+ opflag = SLAP_RESTRICT_OP_RENAME;
+ break;
+ case LDAP_REQ_SEARCH:
+ opflag = SLAP_RESTRICT_OP_SEARCH;
+ break;
+ case LDAP_REQ_UNBIND:
+ session++;
+ opflag = 0;
+ break;
+ default:
+ rs->sr_text = "restrict operations internal error";
+ rs->sr_err = LDAP_OTHER;
+ return rs->sr_err;
+ }
+
+ if ( !starttls ) {
+ /* these checks don't apply to StartTLS */
+
+ rs->sr_err = LDAP_CONFIDENTIALITY_REQUIRED;
+ if( op->o_transport_ssf < ssf->sss_transport ) {
+ rs->sr_text = op->o_transport_ssf
+ ? "stronger transport confidentiality required"
+ : "transport confidentiality required";
+ return rs->sr_err;
+ }
+
+ if( op->o_tls_ssf < ssf->sss_tls ) {
+ rs->sr_text = op->o_tls_ssf
+ ? "stronger TLS confidentiality required"
+ : "TLS confidentiality required";
+ return rs->sr_err;
+ }
+
+
+ if( op->o_tag == LDAP_REQ_BIND && opdata == NULL ) {
+ /* simple bind specific check */
+ if( op->o_ssf < ssf->sss_simple_bind ) {
+ rs->sr_text = op->o_ssf
+ ? "stronger confidentiality required"
+ : "confidentiality required";
+ return rs->sr_err;
+ }
+ }
+
+ if( op->o_tag != LDAP_REQ_BIND || opdata == NULL ) {
+ /* these checks don't apply to SASL bind */
+
+ if( op->o_sasl_ssf < ssf->sss_sasl ) {
+ rs->sr_text = op->o_sasl_ssf
+ ? "stronger SASL confidentiality required"
+ : "SASL confidentiality required";
+ return rs->sr_err;
+ }
+
+ if( op->o_ssf < ssf->sss_ssf ) {
+ rs->sr_text = op->o_ssf
+ ? "stronger confidentiality required"
+ : "confidentiality required";
+ return rs->sr_err;
+ }
+ }
+
+ if( updateop ) {
+ if( op->o_transport_ssf < ssf->sss_update_transport ) {
+ rs->sr_text = op->o_transport_ssf
+ ? "stronger transport confidentiality required for update"
+ : "transport confidentiality required for update";
+ return rs->sr_err;
+ }
+
+ if( op->o_tls_ssf < ssf->sss_update_tls ) {
+ rs->sr_text = op->o_tls_ssf
+ ? "stronger TLS confidentiality required for update"
+ : "TLS confidentiality required for update";
+ return rs->sr_err;
+ }
+
+ if( op->o_sasl_ssf < ssf->sss_update_sasl ) {
+ rs->sr_text = op->o_sasl_ssf
+ ? "stronger SASL confidentiality required for update"
+ : "SASL confidentiality required for update";
+ return rs->sr_err;
+ }
+
+ if( op->o_ssf < ssf->sss_update_ssf ) {
+ rs->sr_text = op->o_ssf
+ ? "stronger confidentiality required for update"
+ : "confidentiality required for update";
+ return rs->sr_err;
+ }
+
+ if( !( global_allows & SLAP_ALLOW_UPDATE_ANON ) &&
+ BER_BVISEMPTY( &op->o_ndn ) )
+ {
+ rs->sr_text = "modifications require authentication";
+ rs->sr_err = LDAP_STRONG_AUTH_REQUIRED;
+ return rs->sr_err;
+ }
+
+#ifdef SLAP_X_LISTENER_MOD
+ if ( op->o_conn->c_listener &&
+ ! ( op->o_conn->c_listener->sl_perms & ( !BER_BVISEMPTY( &op->o_ndn )
+ ? (S_IWUSR|S_IWOTH) : S_IWOTH ) ) )
+ {
+ /* no "w" mode means readonly */
+ rs->sr_text = "modifications not allowed on this listener";
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ return rs->sr_err;
+ }
+#endif /* SLAP_X_LISTENER_MOD */
+ }
+ }
+
+ if ( !session ) {
+ /* these checks don't apply to Bind, StartTLS, or Unbind */
+
+ if( requires & SLAP_REQUIRE_STRONG ) {
+ /* should check mechanism */
+ if( ( op->o_transport_ssf < ssf->sss_transport
+ && op->o_authtype == LDAP_AUTH_SIMPLE )
+ || BER_BVISEMPTY( &op->o_dn ) )
+ {
+ rs->sr_text = "strong(er) authentication required";
+ rs->sr_err = LDAP_STRONG_AUTH_REQUIRED;
+ return rs->sr_err;
+ }
+ }
+
+ if( requires & SLAP_REQUIRE_SASL ) {
+ if( op->o_authtype != LDAP_AUTH_SASL || BER_BVISEMPTY( &op->o_dn ) ) {
+ rs->sr_text = "SASL authentication required";
+ rs->sr_err = LDAP_STRONG_AUTH_REQUIRED;
+ return rs->sr_err;
+ }
+ }
+
+ if( requires & SLAP_REQUIRE_AUTHC ) {
+ if( BER_BVISEMPTY( &op->o_dn ) ) {
+ rs->sr_text = "authentication required";
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ return rs->sr_err;
+ }
+ }
+
+ if( requires & SLAP_REQUIRE_BIND ) {
+ int version;
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+ version = op->o_conn->c_protocol;
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+
+ if( !version ) {
+ /* no bind has occurred */
+ rs->sr_text = "BIND required";
+ rs->sr_err = LDAP_OPERATIONS_ERROR;
+ return rs->sr_err;
+ }
+ }
+
+ if( requires & SLAP_REQUIRE_LDAP_V3 ) {
+ if( op->o_protocol < LDAP_VERSION3 ) {
+ /* no bind has occurred */
+ rs->sr_text = "operation restricted to LDAPv3 clients";
+ rs->sr_err = LDAP_OPERATIONS_ERROR;
+ return rs->sr_err;
+ }
+ }
+
+#ifdef SLAP_X_LISTENER_MOD
+ if ( !starttls && BER_BVISEMPTY( &op->o_dn ) ) {
+ if ( op->o_conn->c_listener &&
+ !( op->o_conn->c_listener->sl_perms & S_IXOTH ))
+ {
+ /* no "x" mode means bind required */
+ rs->sr_text = "bind required on this listener";
+ rs->sr_err = LDAP_STRONG_AUTH_REQUIRED;
+ return rs->sr_err;
+ }
+ }
+
+ if ( !starttls && !updateop ) {
+ if ( op->o_conn->c_listener &&
+ !( op->o_conn->c_listener->sl_perms &
+ ( !BER_BVISEMPTY( &op->o_dn )
+ ? (S_IRUSR|S_IROTH) : S_IROTH )))
+ {
+ /* no "r" mode means no read */
+ rs->sr_text = "read not allowed on this listener";
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ return rs->sr_err;
+ }
+ }
+#endif /* SLAP_X_LISTENER_MOD */
+
+ }
+
+ if( ( restrictops & opflag )
+ || ( exopflag && ( restrictops & exopflag ) )
+ || (( restrictops & SLAP_RESTRICT_READONLY ) && updateop )) {
+ if( ( restrictops & SLAP_RESTRICT_OP_MASK) == SLAP_RESTRICT_OP_READS ) {
+ rs->sr_text = "read operations restricted";
+ } else if ( restrictops & exopflag ) {
+ rs->sr_text = "extended operation restricted";
+ } else {
+ rs->sr_text = "operation restricted";
+ }
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ return rs->sr_err;
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+ return rs->sr_err;
+}
+
+int backend_check_referrals( Operation *op, SlapReply *rs )
+{
+ rs->sr_err = LDAP_SUCCESS;
+
+ if( op->o_bd->be_chk_referrals ) {
+ rs->sr_err = op->o_bd->be_chk_referrals( op, rs );
+
+ if( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_REFERRAL ) {
+ send_ldap_result( op, rs );
+ }
+ }
+
+ return rs->sr_err;
+}
+
+int
+be_entry_get_rw(
+ Operation *op,
+ struct berval *ndn,
+ ObjectClass *oc,
+ AttributeDescription *at,
+ int rw,
+ Entry **e )
+{
+ *e = NULL;
+
+ if ( op->o_bd == NULL ) {
+ return LDAP_NO_SUCH_OBJECT;
+ }
+
+ if ( op->o_bd->be_fetch ) {
+ return op->o_bd->be_fetch( op, ndn, oc, at, rw, e );
+ }
+
+ return LDAP_UNWILLING_TO_PERFORM;
+}
+
+int
+fe_acl_group(
+ Operation *op,
+ Entry *target,
+ struct berval *gr_ndn,
+ struct berval *op_ndn,
+ ObjectClass *group_oc,
+ AttributeDescription *group_at )
+{
+ Entry *e;
+ void *o_priv = op->o_private, *e_priv = NULL;
+ Attribute *a;
+ int rc;
+ GroupAssertion *g;
+ Backend *be = op->o_bd;
+ OpExtra *oex;
+
+ LDAP_SLIST_FOREACH(oex, &op->o_extra, oe_next) {
+ if ( oex->oe_key == (void *)backend_group )
+ break;
+ }
+
+ if ( oex && ((OpExtraDB *)oex)->oe_db )
+ op->o_bd = ((OpExtraDB *)oex)->oe_db;
+
+ if ( !op->o_bd || !SLAP_DBHIDDEN( op->o_bd ))
+ op->o_bd = select_backend( gr_ndn, 0 );
+
+ for ( g = op->o_groups; g; g = g->ga_next ) {
+ if ( g->ga_be != op->o_bd || g->ga_oc != group_oc ||
+ g->ga_at != group_at || g->ga_len != gr_ndn->bv_len )
+ {
+ continue;
+ }
+ if ( strcmp( g->ga_ndn, gr_ndn->bv_val ) == 0 ) {
+ break;
+ }
+ }
+
+ if ( g ) {
+ rc = g->ga_res;
+ goto done;
+ }
+
+ if ( target && dn_match( &target->e_nname, gr_ndn ) ) {
+ e = target;
+ rc = 0;
+
+ } else {
+ op->o_private = NULL;
+ rc = be_entry_get_rw( op, gr_ndn, group_oc, group_at, 0, &e );
+ e_priv = op->o_private;
+ op->o_private = o_priv;
+ }
+
+ if ( e ) {
+ a = attr_find( e->e_attrs, group_at );
+ if ( a ) {
+ /* If the attribute is a subtype of labeledURI,
+ * treat this as a dynamic group ala groupOfURLs
+ */
+ if ( is_at_subtype( group_at->ad_type,
+ slap_schema.si_ad_labeledURI->ad_type ) )
+ {
+ int i;
+ LDAPURLDesc *ludp;
+ struct berval bv, nbase;
+ Filter *filter;
+ Entry *user = NULL;
+ void *user_priv = NULL;
+ Backend *b2 = op->o_bd;
+
+ if ( target && dn_match( &target->e_nname, op_ndn ) ) {
+ user = target;
+ }
+
+ rc = LDAP_COMPARE_FALSE;
+ for ( i = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ ) {
+ if ( ldap_url_parse( a->a_vals[i].bv_val, &ludp ) !=
+ LDAP_URL_SUCCESS )
+ {
+ continue;
+ }
+
+ BER_BVZERO( &nbase );
+
+ /* host, attrs and extensions parts must be empty */
+ if ( ( ludp->lud_host && *ludp->lud_host )
+ || ludp->lud_attrs
+ || ludp->lud_exts )
+ {
+ goto loopit;
+ }
+
+ ber_str2bv( ludp->lud_dn, 0, 0, &bv );
+ if ( dnNormalize( 0, NULL, NULL, &bv, &nbase,
+ op->o_tmpmemctx ) != LDAP_SUCCESS )
+ {
+ goto loopit;
+ }
+
+ switch ( ludp->lud_scope ) {
+ case LDAP_SCOPE_BASE:
+ if ( !dn_match( &nbase, op_ndn ) ) {
+ goto loopit;
+ }
+ break;
+ case LDAP_SCOPE_ONELEVEL:
+ dnParent( op_ndn, &bv );
+ if ( !dn_match( &nbase, &bv ) ) {
+ goto loopit;
+ }
+ break;
+ case LDAP_SCOPE_SUBTREE:
+ if ( !dnIsSuffix( op_ndn, &nbase ) ) {
+ goto loopit;
+ }
+ break;
+ case LDAP_SCOPE_SUBORDINATE:
+ if ( dn_match( &nbase, op_ndn ) ||
+ !dnIsSuffix( op_ndn, &nbase ) )
+ {
+ goto loopit;
+ }
+ }
+
+ /* NOTE: this could be NULL
+ * if no filter is provided,
+ * or if filter parsing fails.
+ * In the latter case,
+ * we should give up. */
+ if ( ludp->lud_filter != NULL && *ludp->lud_filter != '\0') {
+ filter = str2filter_x( op, ludp->lud_filter );
+ if ( filter == NULL ) {
+ /* give up... */
+ rc = LDAP_OTHER;
+ goto loopit;
+ }
+
+ /* only get user if required
+ * and not available yet */
+ if ( user == NULL ) {
+ int rc2;
+
+ op->o_bd = select_backend( op_ndn, 0 );
+ op->o_private = NULL;
+ rc2 = be_entry_get_rw( op, op_ndn, NULL, NULL, 0, &user );
+ user_priv = op->o_private;
+ op->o_private = o_priv;
+ if ( rc2 != 0 ) {
+ /* give up... */
+ rc = (rc2 == LDAP_NO_SUCH_OBJECT) ? rc2 : LDAP_OTHER;
+ goto loopit;
+ }
+ }
+
+ if ( test_filter( NULL, user, filter ) ==
+ LDAP_COMPARE_TRUE )
+ {
+ rc = 0;
+ }
+ filter_free_x( op, filter, 1 );
+ }
+loopit:
+ ldap_free_urldesc( ludp );
+ if ( !BER_BVISNULL( &nbase ) ) {
+ op->o_tmpfree( nbase.bv_val, op->o_tmpmemctx );
+ }
+ if ( rc != LDAP_COMPARE_FALSE ) {
+ break;
+ }
+ }
+
+ if ( user != NULL && user != target ) {
+ op->o_private = user_priv;
+ be_entry_release_r( op, user );
+ op->o_private = o_priv;
+ }
+ op->o_bd = b2;
+
+ } else {
+ rc = attr_valfind( a,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ op_ndn, NULL, op->o_tmpmemctx );
+ if ( rc == LDAP_NO_SUCH_ATTRIBUTE ) {
+ rc = LDAP_COMPARE_FALSE;
+ }
+ }
+
+ } else {
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ }
+
+ if ( e != target ) {
+ op->o_private = e_priv;
+ be_entry_release_r( op, e );
+ op->o_private = o_priv;
+ }
+
+ } else {
+ rc = LDAP_NO_SUCH_OBJECT;
+ }
+
+ if ( op->o_tag != LDAP_REQ_BIND && !op->o_do_not_cache ) {
+ g = op->o_tmpalloc( sizeof( GroupAssertion ) + gr_ndn->bv_len,
+ op->o_tmpmemctx );
+ g->ga_be = op->o_bd;
+ g->ga_oc = group_oc;
+ g->ga_at = group_at;
+ g->ga_res = rc;
+ g->ga_len = gr_ndn->bv_len;
+ strcpy( g->ga_ndn, gr_ndn->bv_val );
+ g->ga_next = op->o_groups;
+ op->o_groups = g;
+ }
+
+done:
+ op->o_bd = be;
+ return rc;
+}
+
+int
+backend_group(
+ Operation *op,
+ Entry *target,
+ struct berval *gr_ndn,
+ struct berval *op_ndn,
+ ObjectClass *group_oc,
+ AttributeDescription *group_at )
+{
+ int rc;
+ BackendDB *be_orig;
+ OpExtraDB oex;
+
+ if ( op->o_abandon ) {
+ return SLAPD_ABANDON;
+ }
+
+ oex.oe_db = op->o_bd;
+ oex.oe.oe_key = (void *)backend_group;
+ LDAP_SLIST_INSERT_HEAD(&op->o_extra, &oex.oe, oe_next);
+
+ be_orig = op->o_bd;
+ op->o_bd = frontendDB;
+ rc = frontendDB->be_group( op, target, gr_ndn,
+ op_ndn, group_oc, group_at );
+ op->o_bd = be_orig;
+ LDAP_SLIST_REMOVE(&op->o_extra, &oex.oe, OpExtra, oe_next);
+
+ return rc;
+}
+
+int
+fe_acl_attribute(
+ Operation *op,
+ Entry *target,
+ struct berval *edn,
+ AttributeDescription *entry_at,
+ BerVarray *vals,
+ slap_access_t access )
+{
+ Entry *e = NULL;
+ void *o_priv = op->o_private, *e_priv = NULL;
+ Attribute *a = NULL;
+ int freeattr = 0, i, j, rc = LDAP_SUCCESS;
+ AccessControlState acl_state = ACL_STATE_INIT;
+ Backend *be = op->o_bd;
+ OpExtra *oex;
+
+ LDAP_SLIST_FOREACH(oex, &op->o_extra, oe_next) {
+ if ( oex->oe_key == (void *)backend_attribute )
+ break;
+ }
+
+ if ( oex && ((OpExtraDB *)oex)->oe_db )
+ op->o_bd = ((OpExtraDB *)oex)->oe_db;
+
+ if ( !op->o_bd || !SLAP_DBHIDDEN( op->o_bd ))
+ op->o_bd = select_backend( edn, 0 );
+
+ if ( target && dn_match( &target->e_nname, edn ) ) {
+ e = target;
+
+ } else {
+ op->o_private = NULL;
+ rc = be_entry_get_rw( op, edn, NULL, entry_at, 0, &e );
+ e_priv = op->o_private;
+ op->o_private = o_priv;
+ }
+
+ if ( e ) {
+ if ( entry_at == slap_schema.si_ad_entry || entry_at == slap_schema.si_ad_children ) {
+ assert( vals == NULL );
+
+ rc = LDAP_SUCCESS;
+ if ( op->o_conn && access > ACL_NONE &&
+ access_allowed( op, e, entry_at, NULL,
+ access, &acl_state ) == 0 )
+ {
+ rc = LDAP_INSUFFICIENT_ACCESS;
+ }
+ goto freeit;
+ }
+
+ a = attr_find( e->e_attrs, entry_at );
+ if ( a == NULL ) {
+ SlapReply rs = { REP_SEARCH };
+ AttributeName anlist[ 2 ];
+
+ anlist[ 0 ].an_name = entry_at->ad_cname;
+ anlist[ 0 ].an_desc = entry_at;
+ BER_BVZERO( &anlist[ 1 ].an_name );
+ rs.sr_attrs = anlist;
+
+ /* NOTE: backend_operational() is also called
+ * when returning results, so it's supposed
+ * to do no harm to entries */
+ rs.sr_entry = e;
+ rc = backend_operational( op, &rs );
+
+ if ( rc == LDAP_SUCCESS ) {
+ if ( rs.sr_operational_attrs ) {
+ freeattr = 1;
+ a = rs.sr_operational_attrs;
+
+ } else {
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ }
+ }
+ }
+
+ if ( a ) {
+ BerVarray v;
+
+ if ( op->o_conn && access > ACL_NONE &&
+ access_allowed( op, e, entry_at, NULL,
+ access, &acl_state ) == 0 )
+ {
+ rc = LDAP_INSUFFICIENT_ACCESS;
+ goto freeit;
+ }
+
+ i = a->a_numvals;
+ v = op->o_tmpalloc( sizeof(struct berval) * ( i + 1 ),
+ op->o_tmpmemctx );
+ for ( i = 0, j = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ )
+ {
+ if ( op->o_conn && access > ACL_NONE &&
+ access_allowed( op, e, entry_at,
+ &a->a_nvals[i],
+ access,
+ &acl_state ) == 0 )
+ {
+ continue;
+ }
+ ber_dupbv_x( &v[j], &a->a_nvals[i],
+ op->o_tmpmemctx );
+ if ( !BER_BVISNULL( &v[j] ) ) {
+ j++;
+ }
+ }
+ if ( j == 0 ) {
+ op->o_tmpfree( v, op->o_tmpmemctx );
+ *vals = NULL;
+ rc = LDAP_INSUFFICIENT_ACCESS;
+
+ } else {
+ BER_BVZERO( &v[j] );
+ *vals = v;
+ rc = LDAP_SUCCESS;
+ }
+ }
+freeit: if ( e != target ) {
+ op->o_private = e_priv;
+ be_entry_release_r( op, e );
+ op->o_private = o_priv;
+ }
+ if ( freeattr ) {
+ attr_free( a );
+ }
+ }
+
+ op->o_bd = be;
+ return rc;
+}
+
+int
+backend_attribute(
+ Operation *op,
+ Entry *target,
+ struct berval *edn,
+ AttributeDescription *entry_at,
+ BerVarray *vals,
+ slap_access_t access )
+{
+ int rc;
+ BackendDB *be_orig;
+ OpExtraDB oex;
+
+ oex.oe_db = op->o_bd;
+ oex.oe.oe_key = (void *)backend_attribute;
+ LDAP_SLIST_INSERT_HEAD(&op->o_extra, &oex.oe, oe_next);
+
+ be_orig = op->o_bd;
+ op->o_bd = frontendDB;
+ rc = frontendDB->be_attribute( op, target, edn,
+ entry_at, vals, access );
+ op->o_bd = be_orig;
+ LDAP_SLIST_REMOVE(&op->o_extra, &oex.oe, OpExtra, oe_next);
+
+ return rc;
+}
+
+int
+backend_access(
+ Operation *op,
+ Entry *target,
+ struct berval *edn,
+ AttributeDescription *entry_at,
+ struct berval *nval,
+ slap_access_t access,
+ slap_mask_t *mask )
+{
+ Entry *e = NULL;
+ void *o_priv, *e_priv = NULL;
+ int rc = LDAP_INSUFFICIENT_ACCESS;
+ Backend *be;
+
+ /* pedantic */
+ assert( op != NULL );
+ assert( op->o_conn != NULL );
+ assert( edn != NULL );
+ assert( access > ACL_NONE );
+
+ be = op->o_bd;
+ o_priv = op->o_private;
+
+ if ( !op->o_bd ) {
+ op->o_bd = select_backend( edn, 0 );
+ }
+
+ if ( target && dn_match( &target->e_nname, edn ) ) {
+ e = target;
+
+ } else {
+ op->o_private = NULL;
+ rc = be_entry_get_rw( op, edn, NULL, entry_at, 0, &e );
+ e_priv = op->o_private;
+ op->o_private = o_priv;
+ }
+
+ if ( e ) {
+ Attribute *a = NULL;
+ int freeattr = 0;
+
+ if ( entry_at == NULL ) {
+ entry_at = slap_schema.si_ad_entry;
+ }
+
+ if ( entry_at == slap_schema.si_ad_entry || entry_at == slap_schema.si_ad_children )
+ {
+ if ( access_allowed_mask( op, e, entry_at,
+ NULL, access, NULL, mask ) == 0 )
+ {
+ rc = LDAP_INSUFFICIENT_ACCESS;
+
+ } else {
+ rc = LDAP_SUCCESS;
+ }
+
+ } else {
+ a = attr_find( e->e_attrs, entry_at );
+ if ( a == NULL ) {
+ SlapReply rs = { REP_SEARCH };
+ AttributeName anlist[ 2 ];
+
+ anlist[ 0 ].an_name = entry_at->ad_cname;
+ anlist[ 0 ].an_desc = entry_at;
+ BER_BVZERO( &anlist[ 1 ].an_name );
+ rs.sr_attrs = anlist;
+
+ rs.sr_attr_flags = slap_attr_flags( rs.sr_attrs );
+
+ /* NOTE: backend_operational() is also called
+ * when returning results, so it's supposed
+ * to do no harm to entries */
+ rs.sr_entry = e;
+ rc = backend_operational( op, &rs );
+
+ if ( rc == LDAP_SUCCESS ) {
+ if ( rs.sr_operational_attrs ) {
+ freeattr = 1;
+ a = rs.sr_operational_attrs;
+
+ } else {
+ rc = LDAP_NO_SUCH_OBJECT;
+ }
+ }
+ }
+
+ if ( a ) {
+ if ( access_allowed_mask( op, e, entry_at,
+ nval, access, NULL, mask ) == 0 )
+ {
+ rc = LDAP_INSUFFICIENT_ACCESS;
+ goto freeit;
+ }
+ rc = LDAP_SUCCESS;
+ }
+ }
+freeit: if ( e != target ) {
+ op->o_private = e_priv;
+ be_entry_release_r( op, e );
+ op->o_private = o_priv;
+ }
+ if ( freeattr ) {
+ attr_free( a );
+ }
+ }
+
+ op->o_bd = be;
+ return rc;
+}
+
+int
+fe_aux_operational(
+ Operation *op,
+ SlapReply *rs )
+{
+ Attribute **ap;
+ int rc = LDAP_SUCCESS;
+ BackendDB *be_orig = op->o_bd;
+ OpExtra *oex;
+
+ LDAP_SLIST_FOREACH(oex, &op->o_extra, oe_next) {
+ if ( oex->oe_key == (void *)backend_operational )
+ break;
+ }
+
+ for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next )
+ /* just count them */ ;
+
+ /*
+ * If operational attributes (allegedly) are required,
+ * and the backend supports specific operational attributes,
+ * add them to the attribute list
+ */
+ if ( !( rs->sr_flags & REP_NO_ENTRYDN )
+ && ( SLAP_OPATTRS( rs->sr_attr_flags ) || ( rs->sr_attrs &&
+ ad_inlist( slap_schema.si_ad_entryDN, rs->sr_attrs ) ) ) )
+ {
+ *ap = slap_operational_entryDN( rs->sr_entry );
+ ap = &(*ap)->a_next;
+ }
+
+ if ( !( rs->sr_flags & REP_NO_SUBSCHEMA)
+ && ( SLAP_OPATTRS( rs->sr_attr_flags ) || ( rs->sr_attrs &&
+ ad_inlist( slap_schema.si_ad_subschemaSubentry, rs->sr_attrs ) ) ) )
+ {
+ *ap = slap_operational_subschemaSubentry( op->o_bd );
+ ap = &(*ap)->a_next;
+ }
+
+ /* Let the overlays have a chance at this */
+ if ( oex && ((OpExtraDB *)oex)->oe_db )
+ op->o_bd = ((OpExtraDB *)oex)->oe_db;
+
+ if ( !op->o_bd || !SLAP_DBHIDDEN( op->o_bd ))
+ op->o_bd = select_backend( &op->o_req_ndn, 0 );
+
+ if ( op->o_bd != NULL && !be_match( op->o_bd, frontendDB ) &&
+ ( SLAP_OPATTRS( rs->sr_attr_flags ) || rs->sr_attrs ) &&
+ op->o_bd->be_operational != NULL )
+ {
+ rc = op->o_bd->be_operational( op, rs );
+ }
+ op->o_bd = be_orig;
+
+ return rc;
+}
+
+int backend_operational( Operation *op, SlapReply *rs )
+{
+ int rc;
+ BackendDB *be_orig;
+ OpExtraDB oex;
+
+ oex.oe_db = op->o_bd;
+ oex.oe.oe_key = (void *)backend_operational;
+ LDAP_SLIST_INSERT_HEAD(&op->o_extra, &oex.oe, oe_next);
+
+ /* Moved this into the frontend so global overlays are called */
+
+ be_orig = op->o_bd;
+ op->o_bd = frontendDB;
+ rc = frontendDB->be_operational( op, rs );
+ op->o_bd = be_orig;
+ LDAP_SLIST_REMOVE(&op->o_extra, &oex.oe, OpExtra, oe_next);
+
+ return rc;
+}
+
+/* helper that calls the bi_tool_entry_first_x() variant with default args;
+ * use to initialize a backend's bi_tool_entry_first() when appropriate
+ */
+ID
+backend_tool_entry_first( BackendDB *be )
+{
+ return be->bd_info->bi_tool_entry_first_x( be,
+ NULL, LDAP_SCOPE_DEFAULT, NULL );
+}
diff --git a/servers/slapd/backglue.c b/servers/slapd/backglue.c
new file mode 100644
index 0000000..3183f2f
--- /dev/null
+++ b/servers/slapd/backglue.c
@@ -0,0 +1,1552 @@
+/* backglue.c - backend glue */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * Functions to glue a bunch of other backends into a single tree.
+ * All of the glued backends must share a common suffix. E.g., you
+ * can glue o=foo and ou=bar,o=foo but you can't glue o=foo and o=bar.
+ *
+ * The purpose of these functions is to allow you to split a single database
+ * into pieces (for load balancing purposes, whatever) but still be able
+ * to treat it as a single database after it's been split. As such, each
+ * of the glued backends should have identical rootdn.
+ * -- Howard Chu
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#define SLAPD_TOOLS
+#include "slap.h"
+#include "lutil.h"
+#include "slap-config.h"
+
+typedef struct gluenode {
+ BackendDB *gn_be;
+ struct berval gn_pdn;
+} gluenode;
+
+typedef struct glueinfo {
+ int gi_nodes;
+ struct berval gi_pdn;
+ gluenode gi_n[1];
+} glueinfo;
+
+static slap_overinst glue;
+
+static int glueMode;
+static BackendDB *glueBack;
+static BackendDB glueBackDone;
+#define GLUEBACK_DONE (&glueBackDone)
+
+static slap_overinst * glue_tool_inst( BackendInfo *bi);
+
+static slap_response glue_op_response;
+
+/* Just like select_backend, but only for our backends */
+static BackendDB *
+glue_back_select (
+ BackendDB *be,
+ struct berval *dn
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ glueinfo *gi = (glueinfo *)on->on_bi.bi_private;
+ int i;
+
+ for (i = gi->gi_nodes-1; i >= 0; i--) {
+ assert( gi->gi_n[i].gn_be->be_nsuffix != NULL );
+
+ if (dnIsSuffix(dn, &gi->gi_n[i].gn_be->be_nsuffix[0])) {
+ return gi->gi_n[i].gn_be;
+ }
+ }
+ be->bd_info = on->on_info->oi_orig;
+ return be;
+}
+
+
+typedef struct glue_state {
+ char *matched;
+ BerVarray refs;
+ LDAPControl **ctrls;
+ int err;
+ int matchlen;
+ int nrefs;
+ int nctrls;
+} glue_state;
+
+static int
+glue_op_cleanup( Operation *op, SlapReply *rs )
+{
+ /* This is not a final result */
+ if (rs->sr_type == REP_RESULT )
+ rs->sr_type = REP_GLUE_RESULT;
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+glue_op_response ( Operation *op, SlapReply *rs )
+{
+ glue_state *gs = op->o_callback->sc_private;
+
+ switch(rs->sr_type) {
+ case REP_SEARCH:
+ case REP_SEARCHREF:
+ case REP_INTERMEDIATE:
+ return SLAP_CB_CONTINUE;
+
+ default:
+ if (rs->sr_err == LDAP_SUCCESS ||
+ rs->sr_err == LDAP_SIZELIMIT_EXCEEDED ||
+ rs->sr_err == LDAP_TIMELIMIT_EXCEEDED ||
+ rs->sr_err == LDAP_ADMINLIMIT_EXCEEDED ||
+ rs->sr_err == LDAP_NO_SUCH_OBJECT ||
+ gs->err != LDAP_SUCCESS)
+ gs->err = rs->sr_err;
+ if (gs->err == LDAP_SUCCESS && gs->matched) {
+ ch_free (gs->matched);
+ gs->matched = NULL;
+ gs->matchlen = 0;
+ }
+ if (gs->err != LDAP_SUCCESS && rs->sr_matched) {
+ int len;
+ len = strlen (rs->sr_matched);
+ if (len > gs->matchlen) {
+ if (gs->matched)
+ ch_free (gs->matched);
+ gs->matched = ch_strdup (rs->sr_matched);
+ gs->matchlen = len;
+ }
+ }
+ if (rs->sr_ref) {
+ int i, j, k;
+ BerVarray new;
+
+ for (i=0; rs->sr_ref[i].bv_val; i++);
+
+ j = gs->nrefs;
+ if (!j) {
+ new = ch_malloc ((i+1)*sizeof(struct berval));
+ } else {
+ new = ch_realloc(gs->refs,
+ (j+i+1)*sizeof(struct berval));
+ }
+ for (k=0; k<i; j++,k++) {
+ ber_dupbv( &new[j], &rs->sr_ref[k] );
+ }
+ new[j].bv_val = NULL;
+ gs->nrefs = j;
+ gs->refs = new;
+ }
+ if (rs->sr_ctrls) {
+ int i, j, k;
+ LDAPControl **newctrls;
+
+ for (i=0; rs->sr_ctrls[i]; i++);
+
+ j = gs->nctrls;
+ if (!j) {
+ newctrls = op->o_tmpalloc((i+1)*sizeof(LDAPControl *),
+ op->o_tmpmemctx);
+ } else {
+ /* Forget old pagedResults response if we're sending
+ * a new one now
+ */
+ if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
+ int newpage = 0;
+ for ( k=0; k<i; k++ ) {
+ if ( !strcmp(rs->sr_ctrls[k]->ldctl_oid,
+ LDAP_CONTROL_PAGEDRESULTS )) {
+ newpage = 1;
+ break;
+ }
+ }
+ if ( newpage ) {
+ for ( k=0; k<j; k++ ) {
+ if ( !strcmp(gs->ctrls[k]->ldctl_oid,
+ LDAP_CONTROL_PAGEDRESULTS ))
+ {
+ op->o_tmpfree(gs->ctrls[k], op->o_tmpmemctx);
+ gs->ctrls[k] = gs->ctrls[--j];
+ gs->ctrls[j] = NULL;
+ break;
+ }
+ }
+ }
+ }
+ newctrls = op->o_tmprealloc(gs->ctrls,
+ (j+i+1)*sizeof(LDAPControl *), op->o_tmpmemctx);
+ }
+ for (k=0; k<i; j++,k++) {
+ ber_len_t oidlen = strlen( rs->sr_ctrls[k]->ldctl_oid );
+ newctrls[j] = op->o_tmpalloc(sizeof(LDAPControl) + oidlen + 1 + rs->sr_ctrls[k]->ldctl_value.bv_len + 1,
+ op->o_tmpmemctx);
+ newctrls[j]->ldctl_iscritical = rs->sr_ctrls[k]->ldctl_iscritical;
+ newctrls[j]->ldctl_oid = (char *)&newctrls[j][1];
+ lutil_strcopy( newctrls[j]->ldctl_oid, rs->sr_ctrls[k]->ldctl_oid );
+ if ( !BER_BVISNULL( &rs->sr_ctrls[k]->ldctl_value ) ) {
+ newctrls[j]->ldctl_value.bv_val = &newctrls[j]->ldctl_oid[oidlen + 1];
+ newctrls[j]->ldctl_value.bv_len = rs->sr_ctrls[k]->ldctl_value.bv_len;
+ lutil_memcopy( newctrls[j]->ldctl_value.bv_val,
+ rs->sr_ctrls[k]->ldctl_value.bv_val,
+ rs->sr_ctrls[k]->ldctl_value.bv_len + 1 );
+ } else {
+ BER_BVZERO( &newctrls[j]->ldctl_value );
+ }
+ }
+ newctrls[j] = NULL;
+ gs->nctrls = j;
+ gs->ctrls = newctrls;
+ }
+ }
+ return 0;
+}
+
+static int
+glue_op_func ( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ BackendDB *b0 = op->o_bd;
+ BackendInfo *bi0 = op->o_bd->bd_info, *bi1;
+ slap_operation_t which = op_bind;
+ int rc;
+
+ op->o_bd = glue_back_select (b0, &op->o_req_ndn);
+
+ /* If we're on the primary backend, let overlay framework handle it */
+ if ( op->o_bd == b0 )
+ return SLAP_CB_CONTINUE;
+
+ b0->bd_info = on->on_info->oi_orig;
+
+ switch(op->o_tag) {
+ case LDAP_REQ_ADD: which = op_add; break;
+ case LDAP_REQ_DELETE: which = op_delete; break;
+ case LDAP_REQ_MODIFY: which = op_modify; break;
+ case LDAP_REQ_MODRDN: which = op_modrdn; break;
+ case LDAP_REQ_EXTENDED: which = op_extended; break;
+ default: assert( 0 ); break;
+ }
+
+ bi1 = op->o_bd->bd_info;
+ rc = (&bi1->bi_op_bind)[ which ] ?
+ (&bi1->bi_op_bind)[ which ]( op, rs ) : SLAP_CB_BYPASS;
+
+ op->o_bd = b0;
+ op->o_bd->bd_info = bi0;
+ return rc;
+}
+
+static int
+glue_op_abandon( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ glueinfo *gi = (glueinfo *)on->on_bi.bi_private;
+ BackendDB *b0 = op->o_bd;
+ BackendInfo *bi0 = op->o_bd->bd_info;
+ int i;
+
+ b0->bd_info = on->on_info->oi_orig;
+
+ for (i = gi->gi_nodes-1; i >= 0; i--) {
+ assert( gi->gi_n[i].gn_be->be_nsuffix != NULL );
+ op->o_bd = gi->gi_n[i].gn_be;
+ if ( op->o_bd == b0 )
+ continue;
+ if ( op->o_bd->bd_info->bi_op_abandon )
+ op->o_bd->bd_info->bi_op_abandon( op, rs );
+ }
+ op->o_bd = b0;
+ op->o_bd->bd_info = bi0;
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+glue_response ( Operation *op, SlapReply *rs )
+{
+ BackendDB *be = op->o_bd;
+ be = glue_back_select (op->o_bd, &op->o_req_ndn);
+
+ /* If we're on the primary backend, let overlay framework handle it.
+ * Otherwise, bail out.
+ */
+ return ( op->o_bd == be ) ? SLAP_CB_CONTINUE : SLAP_CB_BYPASS;
+}
+
+static int
+glue_chk_referrals ( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ BackendDB *b0 = op->o_bd;
+ BackendInfo *bi0 = op->o_bd->bd_info;
+ int rc;
+
+ op->o_bd = glue_back_select (b0, &op->o_req_ndn);
+ if ( op->o_bd == b0 )
+ return SLAP_CB_CONTINUE;
+
+ b0->bd_info = on->on_info->oi_orig;
+
+ if ( op->o_bd->bd_info->bi_chk_referrals )
+ rc = ( *op->o_bd->bd_info->bi_chk_referrals )( op, rs );
+ else
+ rc = SLAP_CB_CONTINUE;
+
+ op->o_bd = b0;
+ op->o_bd->bd_info = bi0;
+ return rc;
+}
+
+static int
+glue_chk_controls ( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ BackendDB *b0 = op->o_bd;
+ BackendInfo *bi0 = op->o_bd->bd_info;
+ int rc = SLAP_CB_CONTINUE;
+
+ op->o_bd = glue_back_select (b0, &op->o_req_ndn);
+ if ( op->o_bd == b0 )
+ return SLAP_CB_CONTINUE;
+
+ b0->bd_info = on->on_info->oi_orig;
+
+ /* if the subordinate database has overlays, the bi_chk_controls()
+ * hook is actually over_aux_chk_controls(); in case it actually
+ * wraps a missing hok, we need to mimic the behavior
+ * of the frontend applied to that database */
+ if ( op->o_bd->bd_info->bi_chk_controls ) {
+ rc = ( *op->o_bd->bd_info->bi_chk_controls )( op, rs );
+ }
+
+
+ if ( rc == SLAP_CB_CONTINUE ) {
+ rc = backend_check_controls( op, rs );
+ }
+
+ op->o_bd = b0;
+ op->o_bd->bd_info = bi0;
+ return rc;
+}
+
+/* ITS#4615 - overlays configured above the glue overlay should be
+ * invoked for the entire glued tree. Overlays configured below the
+ * glue overlay should only be invoked on the primary backend.
+ * So, if we're searching on any subordinates, we need to force the
+ * current overlay chain to stop processing, without stopping the
+ * overall callback flow.
+ */
+static int
+glue_sub_search( Operation *op, SlapReply *rs, BackendDB *b0,
+ slap_overinst *on )
+{
+ /* Process any overlays on the primary backend */
+ if ( op->o_bd == b0 && on->on_next ) {
+ BackendInfo *bi = op->o_bd->bd_info;
+ int rc = SLAP_CB_CONTINUE;
+ for ( on=on->on_next; on; on=on->on_next ) {
+ op->o_bd->bd_info = (BackendInfo *)on;
+ if ( on->on_bi.bi_op_search ) {
+ rc = on->on_bi.bi_op_search( op, rs );
+ if ( rc != SLAP_CB_CONTINUE )
+ break;
+ }
+ }
+ op->o_bd->bd_info = bi;
+ if ( rc != SLAP_CB_CONTINUE )
+ return rc;
+ }
+ return op->o_bd->be_search( op, rs );
+}
+
+static const ID glueID = NOID;
+static const struct berval gluecookie = { sizeof( glueID ), (char *)&glueID };
+
+static int
+glue_op_search ( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ glueinfo *gi = (glueinfo *)on->on_bi.bi_private;
+ BackendDB *b0 = op->o_bd;
+ BackendDB *b1 = NULL, *btmp;
+ BackendInfo *bi0 = op->o_bd->bd_info;
+ int i;
+ long stoptime = 0, starttime;
+ glue_state gs = {NULL, NULL, NULL, 0, 0, 0, 0};
+ slap_callback cb = { NULL, glue_op_response, glue_op_cleanup, NULL };
+ int scope0, tlimit0;
+ struct berval dn, ndn, *pdn;
+
+ cb.sc_private = &gs;
+
+ cb.sc_next = op->o_callback;
+
+ starttime = op->o_time;
+ stoptime = slap_get_time () + op->ors_tlimit;
+
+ /* reset dummy cookie used to keep paged results going across databases */
+ if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED
+ && bvmatch( &((PagedResultsState *)op->o_pagedresults_state)->ps_cookieval, &gluecookie ) )
+ {
+ PagedResultsState *ps = op->o_pagedresults_state;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ struct berval cookie = BER_BVC(""), value;
+ int c;
+
+ for (c = 0; op->o_ctrls[c] != NULL; c++) {
+ if (strcmp(op->o_ctrls[c]->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS) == 0)
+ break;
+ }
+
+ assert( op->o_ctrls[c] != NULL );
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+ ber_printf( ber, "{iO}", ps->ps_size, &cookie );
+ ber_flatten2( ber, &value, 0 );
+ assert( op->o_ctrls[c]->ldctl_value.bv_len >= value.bv_len );
+ op->o_ctrls[c]->ldctl_value.bv_len = value.bv_len;
+ lutil_memcopy( op->o_ctrls[c]->ldctl_value.bv_val,
+ value.bv_val, value.bv_len );
+ ber_free_buf( ber );
+
+ ps->ps_cookie = (PagedResultsCookie)0;
+ BER_BVZERO( &ps->ps_cookieval );
+ }
+
+ op->o_bd = glue_back_select (b0, &op->o_req_ndn);
+ b0->bd_info = on->on_info->oi_orig;
+
+ switch (op->ors_scope) {
+ case LDAP_SCOPE_BASE:
+ if ( op->o_bd == b0 )
+ return SLAP_CB_CONTINUE;
+
+ if (op->o_bd && op->o_bd->be_search) {
+ rs->sr_err = op->o_bd->be_search( op, rs );
+ } else {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ }
+ return rs->sr_err;
+
+ case LDAP_SCOPE_ONELEVEL:
+ case LDAP_SCOPE_SUBTREE:
+ case LDAP_SCOPE_SUBORDINATE: /* FIXME */
+ op->o_callback = &cb;
+ rs->sr_err = gs.err = LDAP_UNWILLING_TO_PERFORM;
+ scope0 = op->ors_scope;
+ tlimit0 = op->ors_tlimit;
+ dn = op->o_req_dn;
+ ndn = op->o_req_ndn;
+ b1 = op->o_bd;
+
+ /*
+ * Execute in reverse order, most specific first
+ */
+ for (i = gi->gi_nodes; i >= 0; i--) {
+ if ( i == gi->gi_nodes ) {
+ btmp = b0;
+ pdn = &gi->gi_pdn;
+ } else {
+ btmp = gi->gi_n[i].gn_be;
+ pdn = &gi->gi_n[i].gn_pdn;
+ }
+ if (!btmp || !btmp->be_search)
+ continue;
+ if (!dnIsSuffix(&btmp->be_nsuffix[0], &b1->be_nsuffix[0]))
+ continue;
+ if (get_no_subordinate_glue(op) && btmp != b1)
+ continue;
+ /* If we remembered which backend we were on before,
+ * skip down to it now
+ */
+ if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED &&
+ op->o_conn->c_pagedresults_state.ps_be &&
+ op->o_conn->c_pagedresults_state.ps_be != btmp )
+ continue;
+
+ if (tlimit0 != SLAP_NO_LIMIT) {
+ op->o_time = slap_get_time();
+ op->ors_tlimit = stoptime - op->o_time;
+ if (op->ors_tlimit <= 0) {
+ rs->sr_err = gs.err = LDAP_TIMELIMIT_EXCEEDED;
+ break;
+ }
+ }
+ rs->sr_err = 0;
+ /*
+ * check for abandon
+ */
+ if (op->o_abandon) {
+ goto end_of_loop;
+ }
+ op->o_bd = btmp;
+
+ assert( op->o_bd->be_suffix != NULL );
+ assert( op->o_bd->be_nsuffix != NULL );
+
+ if (scope0 == LDAP_SCOPE_ONELEVEL &&
+ dn_match(pdn, &ndn))
+ {
+ struct berval mdn, mndn;
+ op->ors_scope = LDAP_SCOPE_BASE;
+ mdn = op->o_req_dn = op->o_bd->be_suffix[0];
+ mndn = op->o_req_ndn = op->o_bd->be_nsuffix[0];
+ rs->sr_err = op->o_bd->be_search(op, rs);
+ if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
+ gs.err = LDAP_SUCCESS;
+ }
+ op->ors_scope = LDAP_SCOPE_ONELEVEL;
+ if ( op->o_req_dn.bv_val == mdn.bv_val )
+ op->o_req_dn = dn;
+ if ( op->o_req_ndn.bv_val == mndn.bv_val )
+ op->o_req_ndn = ndn;
+
+ } else if (scope0 == LDAP_SCOPE_SUBTREE &&
+ dn_match(&op->o_bd->be_nsuffix[0], &ndn))
+ {
+ rs->sr_err = glue_sub_search( op, rs, b0, on );
+
+ } else if (scope0 == LDAP_SCOPE_SUBTREE &&
+ dnIsSuffix(&op->o_bd->be_nsuffix[0], &ndn))
+ {
+ struct berval mdn, mndn;
+ mdn = op->o_req_dn = op->o_bd->be_suffix[0];
+ mndn = op->o_req_ndn = op->o_bd->be_nsuffix[0];
+ rs->sr_err = glue_sub_search( op, rs, b0, on );
+ if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
+ gs.err = LDAP_SUCCESS;
+ }
+ if ( op->o_req_dn.bv_val == mdn.bv_val )
+ op->o_req_dn = dn;
+ if ( op->o_req_ndn.bv_val == mndn.bv_val )
+ op->o_req_ndn = ndn;
+
+ } else if (dnIsSuffix(&ndn, &op->o_bd->be_nsuffix[0])) {
+ rs->sr_err = glue_sub_search( op, rs, b0, on );
+ }
+
+ switch ( gs.err ) {
+
+ /*
+ * Add errors that should result in dropping
+ * the search
+ */
+ case LDAP_SIZELIMIT_EXCEEDED:
+ case LDAP_TIMELIMIT_EXCEEDED:
+ case LDAP_ADMINLIMIT_EXCEEDED:
+ case LDAP_NO_SUCH_OBJECT:
+#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ case LDAP_X_CANNOT_CHAIN:
+#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
+ goto end_of_loop;
+
+ case LDAP_SUCCESS:
+ if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
+ PagedResultsState *ps = op->o_pagedresults_state;
+
+ /* Assume this backend can be forgotten now */
+ op->o_conn->c_pagedresults_state.ps_be = NULL;
+
+ /* If we have a full page, exit the loop. We may
+ * need to remember this backend so we can continue
+ * from here on a subsequent request.
+ */
+ if ( rs->sr_nentries >= ps->ps_size ) {
+ PagedResultsState *cps = &op->o_conn->c_pagedresults_state;
+
+ /* Don't bother to remember the first backend.
+ * Only remember the last one if there's more state left.
+ */
+ if ( op->o_bd != b0 &&
+ ( cps->ps_cookie != NOID
+ || !BER_BVISNULL( &cps->ps_cookieval )
+ || op->o_bd != gi->gi_n[0].gn_be ) )
+ {
+ op->o_conn->c_pagedresults_state.ps_be = op->o_bd;
+ }
+
+ /* Check whether the cookie is empty,
+ * and give remaining databases a chance
+ */
+ if ( op->o_bd != gi->gi_n[0].gn_be || cps->ps_cookie == NOID ) {
+ int c;
+
+ for ( c = 0; gs.ctrls[c] != NULL; c++ ) {
+ if ( strcmp( gs.ctrls[c]->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS ) == 0 ) {
+ break;
+ }
+ }
+
+ if ( gs.ctrls[c] != NULL ) {
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_int_t size;
+ struct berval cookie, value;
+
+ ber_init2( ber, &gs.ctrls[c]->ldctl_value, LBER_USE_DER );
+
+ tag = ber_scanf( ber, "{im}", &size, &cookie );
+ assert( tag != LBER_ERROR );
+
+ if ( BER_BVISEMPTY( &cookie ) && op->o_bd != gi->gi_n[0].gn_be ) {
+ /* delete old, create new cookie with NOID */
+ PagedResultsCookie respcookie = (PagedResultsCookie)NOID;
+ ber_len_t oidlen = strlen( gs.ctrls[c]->ldctl_oid );
+ LDAPControl *newctrl;
+
+ /* it's next database's turn */
+ if ( btmp == b0 ) {
+ op->o_conn->c_pagedresults_state.ps_be = gi->gi_n[gi->gi_nodes - 1].gn_be;
+
+ } else {
+ op->o_conn->c_pagedresults_state.ps_be = gi->gi_n[(i > 0 ? i - 1: 0)].gn_be;
+ }
+
+ cookie.bv_val = (char *)&respcookie;
+ cookie.bv_len = sizeof( PagedResultsCookie );
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+ ber_printf( ber, "{iO}", 0, &cookie );
+ ber_flatten2( ber, &value, 0 );
+
+ newctrl = op->o_tmprealloc( gs.ctrls[c],
+ sizeof(LDAPControl) + oidlen + 1 + value.bv_len + 1,
+ op->o_tmpmemctx);
+ newctrl->ldctl_iscritical = gs.ctrls[c]->ldctl_iscritical;
+ newctrl->ldctl_oid = (char *)&newctrl[1];
+ lutil_strcopy( newctrl->ldctl_oid, gs.ctrls[c]->ldctl_oid );
+ newctrl->ldctl_value.bv_len = value.bv_len;
+ lutil_memcopy( newctrl->ldctl_value.bv_val,
+ value.bv_val, value.bv_len );
+
+ gs.ctrls[c] = newctrl;
+
+ ber_free_buf( ber );
+
+ } else if ( !BER_BVISEMPTY( &cookie ) && op->o_bd != b0 ) {
+ /* if cookie not empty, it's again this database's turn */
+ op->o_conn->c_pagedresults_state.ps_be = op->o_bd;
+ }
+ }
+ }
+
+ goto end_of_loop;
+ }
+
+ /* This backend has run out of entries, but more responses
+ * can fit in the page. Fake a reset of the state so the
+ * next backend will start up properly. Only back-[bh]db
+ * and back-sql look at this state info.
+ */
+ ps->ps_cookie = (PagedResultsCookie)0;
+ BER_BVZERO( &ps->ps_cookieval );
+
+ {
+ /* change the size of the page in the request
+ * that will be propagated, and reset the cookie */
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ int size = ps->ps_size - rs->sr_nentries;
+ struct berval cookie = BER_BVC(""), value;
+ int c;
+
+ for (c = 0; op->o_ctrls[c] != NULL; c++) {
+ if (strcmp(op->o_ctrls[c]->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS) == 0)
+ break;
+ }
+
+ assert( op->o_ctrls[c] != NULL );
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+ ber_printf( ber, "{iO}", size, &cookie );
+ ber_flatten2( ber, &value, 0 );
+ assert( op->o_ctrls[c]->ldctl_value.bv_len >= value.bv_len );
+ op->o_ctrls[c]->ldctl_value.bv_len = value.bv_len;
+ lutil_memcopy( op->o_ctrls[c]->ldctl_value.bv_val,
+ value.bv_val, value.bv_len );
+ ber_free_buf( ber );
+ }
+ }
+
+ default:
+ break;
+ }
+ }
+end_of_loop:;
+ op->ors_scope = scope0;
+ op->ors_tlimit = tlimit0;
+ op->o_time = starttime;
+
+ break;
+ }
+
+ op->o_callback = cb.sc_next;
+ if ( op->o_abandon ) {
+ rs->sr_err = SLAPD_ABANDON;
+ } else {
+ rs->sr_err = gs.err;
+ rs->sr_matched = gs.matched;
+ rs->sr_ref = gs.refs;
+ }
+ rs->sr_ctrls = gs.ctrls;
+
+ send_ldap_result( op, rs );
+
+ op->o_bd = b0;
+ op->o_bd->bd_info = bi0;
+ if (gs.matched)
+ free (gs.matched);
+ if (gs.refs)
+ ber_bvarray_free(gs.refs);
+ if (gs.ctrls) {
+ for (i = gs.nctrls; --i >= 0; ) {
+ op->o_tmpfree(gs.ctrls[i], op->o_tmpmemctx);
+ }
+ op->o_tmpfree(gs.ctrls, op->o_tmpmemctx);
+ }
+ return rs->sr_err;
+}
+
+static BackendDB toolDB;
+
+static int
+glue_tool_entry_open (
+ BackendDB *b0,
+ int mode
+)
+{
+ slap_overinfo *oi = (slap_overinfo *)b0->bd_info;
+
+ /* We don't know which backend to talk to yet, so just
+ * remember the mode and move on...
+ */
+
+ glueMode = mode;
+ glueBack = NULL;
+ toolDB = *b0;
+ toolDB.bd_info = oi->oi_orig;
+
+ /* Sanity checks */
+ {
+ slap_overinst *on = glue_tool_inst( b0->bd_info );
+ glueinfo *gi = on->on_bi.bi_private;
+
+ int i;
+ for (i = 0; i < gi->gi_nodes; i++) {
+ BackendDB *bd;
+ struct berval pdn;
+
+ dnParent( &gi->gi_n[i].gn_be->be_nsuffix[0], &pdn );
+ bd = select_backend( &pdn, 0 );
+ if ( bd ) {
+ ID id;
+ BackendDB db;
+
+ if ( overlay_is_over( bd ) ) {
+ slap_overinfo *oi = (slap_overinfo *)bd->bd_info;
+ db = *bd;
+ db.bd_info = oi->oi_orig;
+ bd = &db;
+ }
+
+ if ( !bd->bd_info->bi_tool_dn2id_get
+ || !bd->bd_info->bi_tool_entry_open
+ || !bd->bd_info->bi_tool_entry_close )
+ {
+ continue;
+ }
+
+ bd->bd_info->bi_tool_entry_open( bd, 0 );
+ id = bd->bd_info->bi_tool_dn2id_get( bd, &gi->gi_n[i].gn_be->be_nsuffix[0] );
+ bd->bd_info->bi_tool_entry_close( bd );
+ if ( id != NOID ) {
+ Debug( LDAP_DEBUG_ANY,
+ "glue_tool_entry_open: subordinate database suffix entry DN=\"%s\" also present in superior database rooted at DN=\"%s\"\n",
+ gi->gi_n[i].gn_be->be_suffix[0].bv_val, bd->be_suffix[0].bv_val );
+ return LDAP_OTHER;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+glue_tool_entry_close (
+ BackendDB *b0
+)
+{
+ int rc = 0;
+
+ if (glueBack && glueBack != GLUEBACK_DONE) {
+ if (!glueBack->be_entry_close)
+ return 0;
+ rc = glueBack->be_entry_close (glueBack);
+ }
+ return rc;
+}
+
+static slap_overinst *
+glue_tool_inst(
+ BackendInfo *bi
+)
+{
+ slap_overinfo *oi = (slap_overinfo *)bi;
+ slap_overinst *on;
+
+ for ( on = oi->oi_list; on; on=on->on_next ) {
+ if ( !strcmp( on->on_bi.bi_type, glue.on_bi.bi_type ))
+ return on;
+ }
+ return NULL;
+}
+
+/* This function will only be called in tool mode */
+static int
+glue_open (
+ BackendInfo *bi
+)
+{
+ slap_overinst *on = glue_tool_inst( bi );
+ glueinfo *gi = on->on_bi.bi_private;
+ static int glueOpened = 0;
+ int i, j, same, bsame = 0, rc = 0;
+ ConfigReply cr = {0};
+
+ if (glueOpened) return 0;
+
+ glueOpened = 1;
+
+ /* If we were invoked in tool mode, open all the underlying backends */
+ if (slapMode & SLAP_TOOL_MODE) {
+ for (i = 0; i<gi->gi_nodes; i++) {
+ same = 0;
+ /* Same bi_open as our main backend? */
+ if ( gi->gi_n[i].gn_be->bd_info->bi_open ==
+ on->on_info->oi_orig->bi_open )
+ bsame = 1;
+
+ /* Loop thru the bd_info's and make sure we only
+ * invoke their bi_open functions once each.
+ */
+ for ( j = 0; j<i; j++ ) {
+ if ( gi->gi_n[i].gn_be->bd_info->bi_open ==
+ gi->gi_n[j].gn_be->bd_info->bi_open ) {
+ same = 1;
+ break;
+ }
+ }
+ /* OK, it's unique and non-NULL, call it. */
+ if ( !same && gi->gi_n[i].gn_be->bd_info->bi_open )
+ rc = gi->gi_n[i].gn_be->bd_info->bi_open(
+ gi->gi_n[i].gn_be->bd_info );
+ /* Let backend.c take care of the rest of startup */
+ if ( !rc )
+ rc = backend_startup_one( gi->gi_n[i].gn_be, &cr );
+ if ( rc ) break;
+ }
+ if ( !rc && !bsame && on->on_info->oi_orig->bi_open )
+ rc = on->on_info->oi_orig->bi_open( on->on_info->oi_orig );
+
+ } /* other case is impossible */
+ return rc;
+}
+
+/* This function will only be called in tool mode */
+static int
+glue_close (
+ BackendInfo *bi
+)
+{
+ static int glueClosed = 0;
+ int rc = 0;
+
+ if (glueClosed) return 0;
+
+ glueClosed = 1;
+
+ if (slapMode & SLAP_TOOL_MODE) {
+ rc = backend_shutdown( NULL );
+ }
+ return rc;
+}
+
+static int
+glue_entry_get_rw (
+ Operation *op,
+ struct berval *dn,
+ ObjectClass *oc,
+ AttributeDescription *ad,
+ int rw,
+ Entry **e )
+{
+ int rc;
+ BackendDB *b0 = op->o_bd;
+ op->o_bd = glue_back_select( b0, dn );
+
+ if ( op->o_bd->be_fetch ) {
+ rc = op->o_bd->be_fetch( op, dn, oc, ad, rw, e );
+ } else {
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ }
+ op->o_bd =b0;
+ return rc;
+}
+
+static int
+glue_entry_release_rw (
+ Operation *op,
+ Entry *e,
+ int rw
+)
+{
+ BackendDB *b0 = op->o_bd;
+ int rc = -1;
+
+ op->o_bd = glue_back_select (b0, &e->e_nname);
+
+ if ( op->o_bd->be_release ) {
+ rc = op->o_bd->be_release( op, e, rw );
+
+ } else {
+ /* FIXME: mimic be_entry_release_rw
+ * when no be_release() available */
+ /* free entry */
+ entry_free( e );
+ rc = 0;
+ }
+ op->o_bd = b0;
+ return rc;
+}
+
+static struct berval *glue_base;
+static int glue_scope;
+static Filter *glue_filter;
+
+static ID
+glue_tool_entry_first (
+ BackendDB *b0
+)
+{
+ slap_overinst *on = glue_tool_inst( b0->bd_info );
+ glueinfo *gi = on->on_bi.bi_private;
+ int i;
+ ID rc;
+
+ /* If we're starting from scratch, start at the most general */
+ if (!glueBack) {
+ if ( toolDB.be_entry_open && toolDB.be_entry_first ) {
+ glueBack = &toolDB;
+ } else {
+ for (i = gi->gi_nodes-1; i >= 0; i--) {
+ if (gi->gi_n[i].gn_be->be_entry_open &&
+ gi->gi_n[i].gn_be->be_entry_first) {
+ glueBack = gi->gi_n[i].gn_be;
+ break;
+ }
+ }
+ }
+ }
+ if (!glueBack || !glueBack->be_entry_open || !glueBack->be_entry_first ||
+ glueBack->be_entry_open (glueBack, glueMode) != 0)
+ return NOID;
+
+ rc = glueBack->be_entry_first (glueBack);
+ while ( rc == NOID ) {
+ if ( glueBack && glueBack->be_entry_close )
+ glueBack->be_entry_close (glueBack);
+ for (i=0; i<gi->gi_nodes; i++) {
+ if (gi->gi_n[i].gn_be == glueBack)
+ break;
+ }
+ if (i == 0) {
+ glueBack = GLUEBACK_DONE;
+ break;
+ } else {
+ glueBack = gi->gi_n[i-1].gn_be;
+ rc = glue_tool_entry_first (b0);
+ if ( glueBack == GLUEBACK_DONE ) {
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+static ID
+glue_tool_entry_first_x (
+ BackendDB *b0,
+ struct berval *base,
+ int scope,
+ Filter *f
+)
+{
+ slap_overinst *on = glue_tool_inst( b0->bd_info );
+ glueinfo *gi = on->on_bi.bi_private;
+ int i;
+ ID rc;
+
+ glue_base = base;
+ glue_scope = scope;
+ glue_filter = f;
+
+ /* If we're starting from scratch, start at the most general */
+ if (!glueBack) {
+ if ( toolDB.be_entry_open && toolDB.be_entry_first_x ) {
+ glueBack = &toolDB;
+ } else {
+ for (i = gi->gi_nodes-1; i >= 0; i--) {
+ if (gi->gi_n[i].gn_be->be_entry_open &&
+ gi->gi_n[i].gn_be->be_entry_first_x)
+ {
+ glueBack = gi->gi_n[i].gn_be;
+ break;
+ }
+ }
+ }
+ }
+ if (!glueBack || !glueBack->be_entry_open || !glueBack->be_entry_first_x ||
+ glueBack->be_entry_open (glueBack, glueMode) != 0)
+ return NOID;
+
+ rc = glueBack->be_entry_first_x (glueBack,
+ glue_base, glue_scope, glue_filter);
+ while ( rc == NOID ) {
+ if ( glueBack && glueBack->be_entry_close )
+ glueBack->be_entry_close (glueBack);
+ for (i=0; i<gi->gi_nodes; i++) {
+ if (gi->gi_n[i].gn_be == glueBack)
+ break;
+ }
+ if (i == 0) {
+ glueBack = GLUEBACK_DONE;
+ break;
+ } else {
+ glueBack = gi->gi_n[i-1].gn_be;
+ rc = glue_tool_entry_first_x (b0,
+ glue_base, glue_scope, glue_filter);
+ if ( glueBack == GLUEBACK_DONE ) {
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+static ID
+glue_tool_entry_next (
+ BackendDB *b0
+)
+{
+ slap_overinst *on = glue_tool_inst( b0->bd_info );
+ glueinfo *gi = on->on_bi.bi_private;
+ int i;
+ ID rc;
+
+ if (!glueBack || !glueBack->be_entry_next)
+ return NOID;
+
+ rc = glueBack->be_entry_next (glueBack);
+
+ /* If we ran out of entries in one database, move on to the next */
+ while (rc == NOID) {
+ if ( glueBack && glueBack->be_entry_close )
+ glueBack->be_entry_close (glueBack);
+ for (i=0; i<gi->gi_nodes; i++) {
+ if (gi->gi_n[i].gn_be == glueBack)
+ break;
+ }
+ if (i == 0) {
+ glueBack = GLUEBACK_DONE;
+ break;
+ } else {
+ glueBack = gi->gi_n[i-1].gn_be;
+ if ( glue_base || glue_filter ) {
+ /* using entry_first_x() */
+ rc = glue_tool_entry_first_x (b0,
+ glue_base, glue_scope, glue_filter);
+
+ } else {
+ /* using entry_first() */
+ rc = glue_tool_entry_first (b0);
+ }
+ if ( glueBack == GLUEBACK_DONE ) {
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+static ID
+glue_tool_dn2id_get (
+ BackendDB *b0,
+ struct berval *dn
+)
+{
+ BackendDB *be, b2;
+ int rc = -1;
+
+ b2 = *b0;
+ b2.bd_info = (BackendInfo *)glue_tool_inst( b0->bd_info );
+ be = glue_back_select (&b2, dn);
+ if ( be == &b2 ) be = &toolDB;
+
+ if (!be->be_dn2id_get)
+ return NOID;
+
+ if (!glueBack) {
+ if ( be->be_entry_open ) {
+ rc = be->be_entry_open (be, glueMode);
+ }
+ if (rc != 0) {
+ return NOID;
+ }
+ } else if (be != glueBack) {
+ /* If this entry belongs in a different branch than the
+ * previous one, close the current database and open the
+ * new one.
+ */
+ if ( glueBack->be_entry_close ) {
+ glueBack->be_entry_close (glueBack);
+ }
+ if ( be->be_entry_open ) {
+ rc = be->be_entry_open (be, glueMode);
+ }
+ if (rc != 0) {
+ return NOID;
+ }
+ }
+ glueBack = be;
+ return be->be_dn2id_get (be, dn);
+}
+
+static Entry *
+glue_tool_entry_get (
+ BackendDB *b0,
+ ID id
+)
+{
+ if (!glueBack || !glueBack->be_entry_get)
+ return NULL;
+
+ return glueBack->be_entry_get (glueBack, id);
+}
+
+static ID
+glue_tool_entry_put (
+ BackendDB *b0,
+ Entry *e,
+ struct berval *text
+)
+{
+ BackendDB *be, b2;
+ int rc = -1;
+
+ b2 = *b0;
+ b2.bd_info = (BackendInfo *)glue_tool_inst( b0->bd_info );
+ be = glue_back_select (&b2, &e->e_nname);
+ if ( be == &b2 ) be = &toolDB;
+
+ if (!be->be_entry_put)
+ return NOID;
+
+ if (!glueBack) {
+ if ( be->be_entry_open ) {
+ rc = be->be_entry_open (be, glueMode);
+ }
+ if (rc != 0) {
+ return NOID;
+ }
+ } else if (be != glueBack) {
+ /* If this entry belongs in a different branch than the
+ * previous one, close the current database and open the
+ * new one.
+ */
+ if ( glueBack->be_entry_close ) {
+ glueBack->be_entry_close (glueBack);
+ }
+ if ( be->be_entry_open ) {
+ rc = be->be_entry_open (be, glueMode);
+ }
+ if (rc != 0) {
+ return NOID;
+ }
+ }
+ glueBack = be;
+ return be->be_entry_put (be, e, text);
+}
+
+static ID
+glue_tool_entry_modify (
+ BackendDB *b0,
+ Entry *e,
+ struct berval *text
+)
+{
+ if (!glueBack || !glueBack->be_entry_modify)
+ return NOID;
+
+ return glueBack->be_entry_modify (glueBack, e, text);
+}
+
+static int
+glue_tool_entry_reindex (
+ BackendDB *b0,
+ ID id,
+ AttributeDescription **adv
+)
+{
+ if (!glueBack || !glueBack->be_entry_reindex)
+ return -1;
+
+ return glueBack->be_entry_reindex (glueBack, id, adv);
+}
+
+static int
+glue_tool_sync (
+ BackendDB *b0
+)
+{
+ slap_overinst *on = glue_tool_inst( b0->bd_info );
+ glueinfo *gi = on->on_bi.bi_private;
+ BackendInfo *bi = b0->bd_info;
+ int i;
+
+ /* just sync everyone */
+ for (i = 0; i<gi->gi_nodes; i++)
+ if (gi->gi_n[i].gn_be->be_sync)
+ gi->gi_n[i].gn_be->be_sync (gi->gi_n[i].gn_be);
+ b0->bd_info = on->on_info->oi_orig;
+ if ( b0->be_sync )
+ b0->be_sync( b0 );
+ b0->bd_info = bi;
+ return 0;
+}
+
+typedef struct glue_Addrec {
+ struct glue_Addrec *ga_next;
+ BackendDB *ga_be;
+} glue_Addrec;
+
+/* List of added subordinates */
+static glue_Addrec *ga_list;
+static int ga_adding;
+
+static int
+glue_db_init(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ slap_overinfo *oi = on->on_info;
+ BackendInfo *bi = oi->oi_orig;
+ glueinfo *gi;
+
+ if ( SLAP_GLUE_SUBORDINATE( be )) {
+ Debug( LDAP_DEBUG_ANY, "glue: backend %s is already subordinate, "
+ "cannot have glue overlay!\n",
+ be->be_suffix[0].bv_val );
+ return LDAP_OTHER;
+ }
+
+ gi = ch_calloc( 1, sizeof(glueinfo));
+ on->on_bi.bi_private = gi;
+ dnParent( be->be_nsuffix, &gi->gi_pdn );
+
+ /* Currently the overlay framework doesn't handle these entry points
+ * but we need them....
+ */
+ oi->oi_bi.bi_open = glue_open;
+ oi->oi_bi.bi_close = glue_close;
+
+ /* Only advertise these if the root DB supports them */
+ if ( bi->bi_tool_entry_open )
+ oi->oi_bi.bi_tool_entry_open = glue_tool_entry_open;
+ if ( bi->bi_tool_entry_close )
+ oi->oi_bi.bi_tool_entry_close = glue_tool_entry_close;
+ if ( bi->bi_tool_entry_first )
+ oi->oi_bi.bi_tool_entry_first = glue_tool_entry_first;
+ /* FIXME: check whether all support bi_tool_entry_first_x() ? */
+ if ( bi->bi_tool_entry_first_x )
+ oi->oi_bi.bi_tool_entry_first_x = glue_tool_entry_first_x;
+ if ( bi->bi_tool_entry_next )
+ oi->oi_bi.bi_tool_entry_next = glue_tool_entry_next;
+ if ( bi->bi_tool_entry_get )
+ oi->oi_bi.bi_tool_entry_get = glue_tool_entry_get;
+ if ( bi->bi_tool_dn2id_get )
+ oi->oi_bi.bi_tool_dn2id_get = glue_tool_dn2id_get;
+ if ( bi->bi_tool_entry_put )
+ oi->oi_bi.bi_tool_entry_put = glue_tool_entry_put;
+ if ( bi->bi_tool_entry_reindex )
+ oi->oi_bi.bi_tool_entry_reindex = glue_tool_entry_reindex;
+ if ( bi->bi_tool_entry_modify )
+ oi->oi_bi.bi_tool_entry_modify = glue_tool_entry_modify;
+ if ( bi->bi_tool_sync )
+ oi->oi_bi.bi_tool_sync = glue_tool_sync;
+
+ SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_GLUE_INSTANCE;
+
+ if ( ga_list && ( slapMode & SLAP_SERVER_MODE ) ) {
+ be->bd_info = (BackendInfo *)oi;
+ glue_sub_attach( 1 );
+ }
+
+ return 0;
+}
+
+static int
+glue_db_destroy (
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ glueinfo *gi = (glueinfo *)on->on_bi.bi_private;
+
+ free (gi);
+ return 0;
+}
+
+static int
+glue_db_close(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+
+ on->on_info->oi_bi.bi_db_close = 0;
+ return 0;
+}
+
+int
+glue_sub_del( BackendDB *b0 )
+{
+ BackendDB *be;
+ int rc = 0;
+
+ /* Find the top backend for this subordinate */
+ be = b0;
+ while ( (be=LDAP_STAILQ_NEXT( be, be_next )) != NULL ) {
+ slap_overinfo *oi;
+ slap_overinst *on;
+ glueinfo *gi;
+ int i;
+
+ if ( SLAP_GLUE_SUBORDINATE( be ))
+ continue;
+ if ( !SLAP_GLUE_INSTANCE( be ))
+ continue;
+ if ( !dnIsSuffix( &b0->be_nsuffix[0], &be->be_nsuffix[0] ))
+ continue;
+
+ /* OK, got the right backend, find the overlay */
+ oi = (slap_overinfo *)be->bd_info;
+ for ( on=oi->oi_list; on; on=on->on_next ) {
+ if ( on->on_bi.bi_type == glue.on_bi.bi_type )
+ break;
+ }
+ assert( on != NULL );
+ gi = on->on_bi.bi_private;
+ for ( i=0; i < gi->gi_nodes; i++ ) {
+ if ( gi->gi_n[i].gn_be == b0 ) {
+ int j;
+
+ for (j=i+1; j < gi->gi_nodes; j++)
+ gi->gi_n[j-1] = gi->gi_n[j];
+
+ gi->gi_nodes--;
+ }
+ }
+ /* Mark as no longer linked/sub */
+ b0->be_flags &= ~(SLAP_DBFLAG_GLUE_SUBORDINATE|SLAP_DBFLAG_GLUE_LINKED|
+ SLAP_DBFLAG_GLUE_ADVERTISE);
+ b0->be_pcsn_p = &b0->be_pcsn_st;
+ break;
+ }
+ if ( be == NULL )
+ rc = LDAP_NO_SUCH_OBJECT;
+
+ return rc;
+}
+
+
+/* Attach all the subordinate backends to their superior */
+int
+glue_sub_attach( int online )
+{
+ glue_Addrec *ga, *gnext = NULL;
+ int rc = 0;
+
+ if ( ga_adding )
+ return 0;
+
+ ga_adding = 1;
+
+ /* For all the subordinate backends */
+ for ( ga=ga_list; ga != NULL; ga = gnext ) {
+ BackendDB *be;
+
+ gnext = ga->ga_next;
+
+ /* Find the top backend for this subordinate */
+ be = ga->ga_be;
+ while ( (be=LDAP_STAILQ_NEXT( be, be_next )) != NULL ) {
+ slap_overinfo *oi;
+ slap_overinst *on;
+ glueinfo *gi;
+
+ if ( SLAP_GLUE_SUBORDINATE( be ))
+ continue;
+ if ( !dnIsSuffix( &ga->ga_be->be_nsuffix[0], &be->be_nsuffix[0] ))
+ continue;
+
+ /* If it's not already configured, set up the overlay */
+ if ( !SLAP_GLUE_INSTANCE( be )) {
+ rc = overlay_config( be, glue.on_bi.bi_type, -1, NULL, NULL);
+ if ( rc )
+ break;
+ }
+ /* Find the overlay instance */
+ oi = (slap_overinfo *)be->bd_info;
+ for ( on=oi->oi_list; on; on=on->on_next ) {
+ if ( on->on_bi.bi_type == glue.on_bi.bi_type )
+ break;
+ }
+ assert( on != NULL );
+ gi = on->on_bi.bi_private;
+ gi = (glueinfo *)ch_realloc( gi, sizeof(glueinfo) +
+ gi->gi_nodes * sizeof(gluenode));
+ gi->gi_n[gi->gi_nodes].gn_be = ga->ga_be;
+ dnParent( &ga->ga_be->be_nsuffix[0],
+ &gi->gi_n[gi->gi_nodes].gn_pdn );
+ gi->gi_nodes++;
+ on->on_bi.bi_private = gi;
+ ga->ga_be->be_pcsn_p = be->be_pcsn_p;
+ ga->ga_be->be_flags |= SLAP_DBFLAG_GLUE_LINKED;
+ break;
+ }
+ if ( !be ) {
+ Debug( LDAP_DEBUG_ANY, "glue: no superior found for sub %s!\n",
+ ga->ga_be->be_suffix[0].bv_val );
+ /* allow this for now, assume a superior will
+ * be added later
+ */
+ if ( online ) {
+ rc = 0;
+ gnext = ga_list;
+ break;
+ }
+ rc = LDAP_NO_SUCH_OBJECT;
+ }
+ ch_free( ga );
+ if ( rc ) break;
+ }
+
+ ga_list = gnext;
+
+ ga_adding = 0;
+
+ return rc;
+}
+
+int
+glue_sub_add( BackendDB *be, int advert, int online )
+{
+ glue_Addrec *ga;
+ int rc = 0;
+
+ if ( overlay_is_inst( be, "glue" )) {
+ Debug( LDAP_DEBUG_ANY, "glue: backend %s already has glue overlay, "
+ "cannot be a subordinate!\n",
+ be->be_suffix[0].bv_val );
+ return LDAP_OTHER;
+ }
+ SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_GLUE_SUBORDINATE;
+ if ( advert )
+ SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_GLUE_ADVERTISE;
+
+ ga = ch_malloc( sizeof( glue_Addrec ));
+ ga->ga_next = ga_list;
+ ga->ga_be = be;
+ ga_list = ga;
+
+ if ( online )
+ rc = glue_sub_attach( online );
+
+ return rc;
+}
+
+static int
+glue_access_allowed(
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ slap_access_t access,
+ AccessControlState *state,
+ slap_mask_t *maskp )
+{
+ BackendDB *b0, *be = glue_back_select( op->o_bd, &e->e_nname );
+ int rc;
+
+ if ( be == NULL || be == op->o_bd || be->bd_info->bi_access_allowed == NULL )
+ return SLAP_CB_CONTINUE;
+
+ b0 = op->o_bd;
+ op->o_bd = be;
+ rc = be->bd_info->bi_access_allowed ( op, e, desc, val, access, state, maskp );
+ op->o_bd = b0;
+ return rc;
+}
+
+int
+glue_sub_init()
+{
+ glue.on_bi.bi_type = "glue";
+
+ glue.on_bi.bi_db_init = glue_db_init;
+ glue.on_bi.bi_db_close = glue_db_close;
+ glue.on_bi.bi_db_destroy = glue_db_destroy;
+
+ glue.on_bi.bi_op_search = glue_op_search;
+ glue.on_bi.bi_op_modify = glue_op_func;
+ glue.on_bi.bi_op_modrdn = glue_op_func;
+ glue.on_bi.bi_op_add = glue_op_func;
+ glue.on_bi.bi_op_delete = glue_op_func;
+ glue.on_bi.bi_op_abandon = glue_op_abandon;
+ glue.on_bi.bi_extended = glue_op_func;
+
+ glue.on_bi.bi_chk_referrals = glue_chk_referrals;
+ glue.on_bi.bi_chk_controls = glue_chk_controls;
+ glue.on_bi.bi_entry_get_rw = glue_entry_get_rw;
+ glue.on_bi.bi_entry_release_rw = glue_entry_release_rw;
+ glue.on_bi.bi_access_allowed = glue_access_allowed;
+
+ glue.on_response = glue_response;
+
+ return overlay_register( &glue );
+}
diff --git a/servers/slapd/backover.c b/servers/slapd/backover.c
new file mode 100644
index 0000000..dbf67e6
--- /dev/null
+++ b/servers/slapd/backover.c
@@ -0,0 +1,1484 @@
+/* backover.c - backend overlay routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/* Functions to overlay other modules over a backend. */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#define SLAPD_TOOLS
+#include "slap.h"
+#include "slap-config.h"
+
+static slap_overinst *overlays;
+
+static int
+over_db_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv
+)
+{
+ slap_overinfo *oi = be->bd_info->bi_private;
+ slap_overinst *on = oi->oi_list;
+ BackendInfo *bi_orig = be->bd_info;
+ struct ConfigOCs *be_cf_ocs = be->be_cf_ocs;
+ ConfigArgs ca = {0};
+ int rc = 0;
+
+ if ( oi->oi_orig->bi_db_config ) {
+ be->bd_info = oi->oi_orig;
+ be->be_cf_ocs = oi->oi_orig->bi_cf_ocs;
+ rc = oi->oi_orig->bi_db_config( be, fname, lineno,
+ argc, argv );
+
+ if ( be->bd_info != oi->oi_orig ) {
+ slap_overinfo *oi2;
+ slap_overinst *on2, **onp;
+ BackendDB be2 = *be;
+ int i;
+
+ /* a database added an overlay;
+ * work it around... */
+ assert( overlay_is_over( be ) );
+
+ oi2 = ( slap_overinfo * )be->bd_info->bi_private;
+ on2 = oi2->oi_list;
+
+ /* need to put a uniqueness check here as well;
+ * note that in principle there could be more than
+ * one overlay as a result of multiple calls to
+ * overlay_config() */
+ be2.bd_info = (BackendInfo *)oi;
+
+ for ( i = 0, onp = &on2; *onp; i++, onp = &(*onp)->on_next ) {
+ if ( overlay_is_inst( &be2, (*onp)->on_bi.bi_type ) ) {
+ Debug( LDAP_DEBUG_ANY, "over_db_config(): "
+ "warning, freshly added "
+ "overlay #%d \"%s\" is already in list\n",
+ i, (*onp)->on_bi.bi_type );
+
+ /* NOTE: if the overlay already exists,
+ * there is no way to merge the results
+ * of the configuration that may have
+ * occurred during bi_db_config(); we
+ * just issue a warning, and the
+ * administrator should deal with this */
+ }
+ }
+ *onp = oi->oi_list;
+
+ oi->oi_list = on2;
+
+ ch_free( be->bd_info );
+ }
+
+ be->bd_info = (BackendInfo *)oi;
+ if ( rc != SLAP_CONF_UNKNOWN ) return rc;
+ }
+
+ ca.argv = argv;
+ ca.argc = argc;
+ ca.fname = fname;
+ ca.lineno = lineno;
+ ca.be = be;
+ snprintf( ca.log, sizeof( ca.log ), "%s: line %d",
+ ca.fname, ca.lineno );
+ ca.op = SLAP_CONFIG_ADD;
+ ca.valx = -1;
+
+ for (; on; on=on->on_next) {
+ rc = SLAP_CONF_UNKNOWN;
+ if (on->on_bi.bi_cf_ocs) {
+ ConfigTable *ct;
+ ca.bi = &on->on_bi;
+ ct = config_find_keyword( on->on_bi.bi_cf_ocs->co_table, &ca );
+ if ( ct ) {
+ ca.table = on->on_bi.bi_cf_ocs->co_type;
+ rc = config_add_vals( ct, &ca );
+ if ( rc != SLAP_CONF_UNKNOWN )
+ break;
+ }
+ }
+ if (on->on_bi.bi_db_config && rc == SLAP_CONF_UNKNOWN) {
+ be->bd_info = &on->on_bi;
+ rc = on->on_bi.bi_db_config( be, fname, lineno,
+ argc, argv );
+ if ( rc != SLAP_CONF_UNKNOWN ) break;
+ }
+ }
+ be->bd_info = bi_orig;
+ be->be_cf_ocs = be_cf_ocs;
+
+ return rc;
+}
+
+static int
+over_db_open(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinfo *oi = be->bd_info->bi_private;
+ slap_overinst *on = oi->oi_list;
+ BackendDB db = *be;
+ int rc = 0;
+
+ db.be_flags |= SLAP_DBFLAG_OVERLAY;
+ db.bd_info = oi->oi_orig;
+ if ( db.bd_info->bi_db_open ) {
+ rc = db.bd_info->bi_db_open( &db, cr );
+ }
+
+ for (; on && rc == 0; on=on->on_next) {
+ if ( on->on_bi.bi_flags & SLAPO_BFLAG_DISABLED )
+ continue;
+ db.bd_info = &on->on_bi;
+ if ( db.bd_info->bi_db_open ) {
+ rc = db.bd_info->bi_db_open( &db, cr );
+ }
+ }
+
+ return rc;
+}
+
+static int
+over_db_close(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinfo *oi = be->bd_info->bi_private;
+ slap_overinst *on = oi->oi_list;
+ BackendInfo *bi_orig = be->bd_info;
+ int rc = 0;
+
+ for (; on && rc == 0; on=on->on_next) {
+ if ( on->on_bi.bi_flags & SLAPO_BFLAG_DISABLED )
+ continue;
+ be->bd_info = &on->on_bi;
+ if ( be->bd_info->bi_db_close ) {
+ rc = be->bd_info->bi_db_close( be, cr );
+ }
+ }
+
+ if ( oi->oi_orig->bi_db_close ) {
+ be->bd_info = oi->oi_orig;
+ rc = be->bd_info->bi_db_close( be, cr );
+ }
+
+ be->bd_info = bi_orig;
+ return rc;
+}
+
+static int
+over_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinfo *oi = be->bd_info->bi_private;
+ slap_overinst *on = oi->oi_list, *next;
+ BackendInfo *bi_orig = be->bd_info;
+ int rc = 0;
+
+ be->bd_info = oi->oi_orig;
+ if ( be->bd_info->bi_db_destroy ) {
+ rc = be->bd_info->bi_db_destroy( be, cr );
+ }
+
+ for (; on && rc == 0; on=on->on_next) {
+ if ( on->on_bi.bi_flags & SLAPO_BFLAG_DISABLED )
+ continue;
+ be->bd_info = &on->on_bi;
+ if ( be->bd_info->bi_db_destroy ) {
+ rc = be->bd_info->bi_db_destroy( be, cr );
+ }
+ }
+
+ on = oi->oi_list;
+ if ( on ) {
+ for (next = on->on_next; on; on=next) {
+ next = on->on_next;
+ free( on );
+ }
+ }
+ be->bd_info = bi_orig;
+ free( oi );
+ return rc;
+}
+
+static int
+over_back_response ( Operation *op, SlapReply *rs )
+{
+ slap_overinfo *oi = op->o_callback->sc_private;
+ slap_overinst *on = oi->oi_list;
+ int rc = SLAP_CB_CONTINUE;
+ BackendDB *be = op->o_bd, db = *op->o_bd;
+
+ db.be_flags |= SLAP_DBFLAG_OVERLAY;
+ op->o_bd = &db;
+ for (; on; on=on->on_next ) {
+ if ( on->on_bi.bi_flags & SLAPO_BFLAG_DISABLED )
+ continue;
+ if ( on->on_response ) {
+ db.bd_info = (BackendInfo *)on;
+ rc = on->on_response( op, rs );
+ if ( rc != SLAP_CB_CONTINUE ) break;
+ }
+ }
+ /* Bypass the remaining on_response layers, but allow
+ * normal execution to continue.
+ */
+ if ( rc == SLAP_CB_BYPASS )
+ rc = SLAP_CB_CONTINUE;
+ op->o_bd = be;
+ return rc;
+}
+
+static int
+over_access_allowed(
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ slap_access_t access,
+ AccessControlState *state,
+ slap_mask_t *maskp )
+{
+ slap_overinfo *oi;
+ slap_overinst *on;
+ BackendInfo *bi;
+ BackendDB *be = op->o_bd, db;
+ int rc = SLAP_CB_CONTINUE;
+
+ /* FIXME: used to happen for instance during abandon
+ * when global overlays are used... */
+ assert( op->o_bd != NULL );
+
+ bi = op->o_bd->bd_info;
+ /* Were we invoked on the frontend? */
+ if ( !bi->bi_access_allowed ) {
+ oi = frontendDB->bd_info->bi_private;
+ } else {
+ oi = op->o_bd->bd_info->bi_private;
+ }
+ on = oi->oi_list;
+
+ for ( ; on; on = on->on_next ) {
+ if ( on->on_bi.bi_flags & SLAPO_BFLAG_DISABLED )
+ continue;
+ if ( on->on_bi.bi_access_allowed ) {
+ /* NOTE: do not copy the structure until required */
+ if ( !SLAP_ISOVERLAY( op->o_bd ) ) {
+ db = *op->o_bd;
+ db.be_flags |= SLAP_DBFLAG_OVERLAY;
+ op->o_bd = &db;
+ }
+
+ op->o_bd->bd_info = (BackendInfo *)on;
+ rc = on->on_bi.bi_access_allowed( op, e,
+ desc, val, access, state, maskp );
+ if ( rc != SLAP_CB_CONTINUE ) break;
+ }
+ }
+
+ if ( rc == SLAP_CB_CONTINUE ) {
+ BI_access_allowed *bi_access_allowed;
+
+ /* if the database structure was changed, o_bd points to a
+ * copy of the structure; put the original bd_info in place */
+ if ( SLAP_ISOVERLAY( op->o_bd ) ) {
+ op->o_bd->bd_info = oi->oi_orig;
+ }
+
+ if ( oi->oi_orig->bi_access_allowed ) {
+ bi_access_allowed = oi->oi_orig->bi_access_allowed;
+ } else {
+ bi_access_allowed = slap_access_allowed;
+ }
+
+ rc = bi_access_allowed( op, e,
+ desc, val, access, state, maskp );
+ }
+ /* should not fall thru this far without anything happening... */
+ if ( rc == SLAP_CB_CONTINUE ) {
+ /* access not allowed */
+ rc = 0;
+ }
+
+ op->o_bd = be;
+ if ( SLAP_ISOVERLAY( op->o_bd ) ) {
+ op->o_bd->bd_info = bi;
+ }
+
+ return rc;
+}
+
+int
+overlay_entry_get_ov(
+ Operation *op,
+ struct berval *dn,
+ ObjectClass *oc,
+ AttributeDescription *ad,
+ int rw,
+ Entry **e,
+ slap_overinst *on )
+{
+ slap_overinfo *oi = on->on_info;
+ BackendDB *be = op->o_bd, db;
+ BackendInfo *bi = op->o_bd->bd_info;
+ int rc = SLAP_CB_CONTINUE;
+
+ for ( ; on; on = on->on_next ) {
+ if ( on->on_bi.bi_flags & SLAPO_BFLAG_DISABLED )
+ continue;
+ if ( on->on_bi.bi_entry_get_rw ) {
+ /* NOTE: do not copy the structure until required */
+ if ( !SLAP_ISOVERLAY( op->o_bd ) ) {
+ db = *op->o_bd;
+ db.be_flags |= SLAP_DBFLAG_OVERLAY;
+ op->o_bd = &db;
+ }
+
+ op->o_bd->bd_info = (BackendInfo *)on;
+ rc = on->on_bi.bi_entry_get_rw( op, dn,
+ oc, ad, rw, e );
+ if ( rc != SLAP_CB_CONTINUE ) break;
+ }
+ }
+
+ if ( rc == SLAP_CB_CONTINUE ) {
+ /* if the database structure was changed, o_bd points to a
+ * copy of the structure; put the original bd_info in place */
+ if ( SLAP_ISOVERLAY( op->o_bd ) ) {
+ op->o_bd->bd_info = oi->oi_orig;
+ }
+
+ if ( oi->oi_orig->bi_entry_get_rw ) {
+ rc = oi->oi_orig->bi_entry_get_rw( op, dn,
+ oc, ad, rw, e );
+ }
+ }
+ /* should not fall thru this far without anything happening... */
+ if ( rc == SLAP_CB_CONTINUE ) {
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ op->o_bd = be;
+ if ( SLAP_ISOVERLAY( op->o_bd ) ) {
+ op->o_bd->bd_info = bi;
+ }
+
+ return rc;
+}
+
+static int
+over_entry_get_rw(
+ Operation *op,
+ struct berval *dn,
+ ObjectClass *oc,
+ AttributeDescription *ad,
+ int rw,
+ Entry **e )
+{
+ slap_overinfo *oi;
+ slap_overinst *on;
+
+ assert( op->o_bd != NULL );
+
+ oi = op->o_bd->bd_info->bi_private;
+ on = oi->oi_list;
+
+ return overlay_entry_get_ov( op, dn, oc, ad, rw, e, on );
+}
+
+int
+overlay_entry_release_ov(
+ Operation *op,
+ Entry *e,
+ int rw,
+ slap_overinst *on )
+{
+ slap_overinfo *oi = on->on_info;
+ BackendDB *be = op->o_bd, db;
+ BackendInfo *bi = op->o_bd->bd_info;
+ int rc = SLAP_CB_CONTINUE;
+
+ for ( ; on; on = on->on_next ) {
+ if ( on->on_bi.bi_flags & SLAPO_BFLAG_DISABLED )
+ continue;
+ if ( on->on_bi.bi_entry_release_rw ) {
+ /* NOTE: do not copy the structure until required */
+ if ( !SLAP_ISOVERLAY( op->o_bd ) ) {
+ db = *op->o_bd;
+ db.be_flags |= SLAP_DBFLAG_OVERLAY;
+ op->o_bd = &db;
+ }
+
+ op->o_bd->bd_info = (BackendInfo *)on;
+ rc = on->on_bi.bi_entry_release_rw( op, e, rw );
+ if ( rc != SLAP_CB_CONTINUE ) break;
+ }
+ }
+
+ if ( rc == SLAP_CB_CONTINUE ) {
+ /* if the database structure was changed, o_bd points to a
+ * copy of the structure; put the original bd_info in place */
+ if ( SLAP_ISOVERLAY( op->o_bd ) ) {
+ op->o_bd->bd_info = oi->oi_orig;
+ }
+
+ if ( oi->oi_orig->bi_entry_release_rw ) {
+ rc = oi->oi_orig->bi_entry_release_rw( op, e, rw );
+ }
+ }
+ /* should not fall thru this far without anything happening... */
+ if ( rc == SLAP_CB_CONTINUE ) {
+ entry_free( e );
+ rc = 0;
+ }
+
+ op->o_bd = be;
+ if ( SLAP_ISOVERLAY( op->o_bd ) ) {
+ op->o_bd->bd_info = bi;
+ }
+
+ return rc;
+}
+
+static int
+over_entry_release_rw(
+ Operation *op,
+ Entry *e,
+ int rw )
+{
+ slap_overinfo *oi;
+ slap_overinst *on;
+
+ assert( op->o_bd != NULL );
+
+ oi = op->o_bd->bd_info->bi_private;
+ on = oi->oi_list;
+
+ return overlay_entry_release_ov( op, e, rw, on );
+}
+
+static int
+over_acl_group(
+ Operation *op,
+ Entry *e,
+ struct berval *gr_ndn,
+ struct berval *op_ndn,
+ ObjectClass *group_oc,
+ AttributeDescription *group_at )
+{
+ slap_overinfo *oi;
+ slap_overinst *on;
+ BackendInfo *bi;
+ BackendDB *be = op->o_bd, db;
+ int rc = SLAP_CB_CONTINUE;
+
+ /* FIXME: used to happen for instance during abandon
+ * when global overlays are used... */
+ assert( be != NULL );
+
+ bi = be->bd_info;
+ oi = bi->bi_private;
+ on = oi->oi_list;
+
+ for ( ; on; on = on->on_next ) {
+ if ( on->on_bi.bi_flags & SLAPO_BFLAG_DISABLED )
+ continue;
+ if ( on->on_bi.bi_acl_group ) {
+ /* NOTE: do not copy the structure until required */
+ if ( !SLAP_ISOVERLAY( op->o_bd ) ) {
+ db = *op->o_bd;
+ db.be_flags |= SLAP_DBFLAG_OVERLAY;
+ op->o_bd = &db;
+ }
+
+ op->o_bd->bd_info = (BackendInfo *)on;
+ rc = on->on_bi.bi_acl_group( op, e,
+ gr_ndn, op_ndn, group_oc, group_at );
+ if ( rc != SLAP_CB_CONTINUE ) break;
+ }
+ }
+
+ if ( rc == SLAP_CB_CONTINUE ) {
+ BI_acl_group *bi_acl_group;
+
+ /* if the database structure was changed, o_bd points to a
+ * copy of the structure; put the original bd_info in place */
+ if ( SLAP_ISOVERLAY( op->o_bd ) ) {
+ op->o_bd->bd_info = oi->oi_orig;
+ }
+
+ if ( oi->oi_orig->bi_acl_group ) {
+ bi_acl_group = oi->oi_orig->bi_acl_group;
+ } else {
+ bi_acl_group = backend_group;
+ }
+
+ rc = bi_acl_group( op, e,
+ gr_ndn, op_ndn, group_oc, group_at );
+ }
+ /* should not fall thru this far without anything happening... */
+ if ( rc == SLAP_CB_CONTINUE ) {
+ /* access not allowed */
+ rc = 0;
+ }
+
+ op->o_bd = be;
+ if ( SLAP_ISOVERLAY( op->o_bd ) ) {
+ op->o_bd->bd_info = bi;
+ }
+
+ return rc;
+}
+
+static int
+over_acl_attribute(
+ Operation *op,
+ Entry *target,
+ struct berval *entry_ndn,
+ AttributeDescription *entry_at,
+ BerVarray *vals,
+ slap_access_t access )
+{
+ slap_overinfo *oi;
+ slap_overinst *on;
+ BackendInfo *bi;
+ BackendDB *be = op->o_bd, db;
+ int rc = SLAP_CB_CONTINUE;
+
+ /* FIXME: used to happen for instance during abandon
+ * when global overlays are used... */
+ assert( be != NULL );
+
+ bi = be->bd_info;
+ oi = bi->bi_private;
+ on = oi->oi_list;
+
+ for ( ; on; on = on->on_next ) {
+ if ( on->on_bi.bi_flags & SLAPO_BFLAG_DISABLED )
+ continue;
+ if ( on->on_bi.bi_acl_attribute ) {
+ /* NOTE: do not copy the structure until required */
+ if ( !SLAP_ISOVERLAY( op->o_bd ) ) {
+ db = *op->o_bd;
+ db.be_flags |= SLAP_DBFLAG_OVERLAY;
+ op->o_bd = &db;
+ }
+
+ op->o_bd->bd_info = (BackendInfo *)on;
+ rc = on->on_bi.bi_acl_attribute( op, target,
+ entry_ndn, entry_at, vals, access );
+ if ( rc != SLAP_CB_CONTINUE ) break;
+ }
+ }
+
+ if ( rc == SLAP_CB_CONTINUE ) {
+ BI_acl_attribute *bi_acl_attribute;
+
+ /* if the database structure was changed, o_bd points to a
+ * copy of the structure; put the original bd_info in place */
+ if ( SLAP_ISOVERLAY( op->o_bd ) ) {
+ op->o_bd->bd_info = oi->oi_orig;
+ }
+
+ if ( oi->oi_orig->bi_acl_attribute ) {
+ bi_acl_attribute = oi->oi_orig->bi_acl_attribute;
+ } else {
+ bi_acl_attribute = backend_attribute;
+ }
+
+ rc = bi_acl_attribute( op, target,
+ entry_ndn, entry_at, vals, access );
+ }
+ /* should not fall thru this far without anything happening... */
+ if ( rc == SLAP_CB_CONTINUE ) {
+ /* access not allowed */
+ rc = 0;
+ }
+
+ op->o_bd = be;
+ if ( SLAP_ISOVERLAY( op->o_bd ) ) {
+ op->o_bd->bd_info = bi;
+ }
+
+ return rc;
+}
+
+int
+overlay_callback_after_backover( Operation *op, slap_callback *sc, int append )
+{
+ slap_callback **scp;
+
+ for ( scp = &op->o_callback; *scp != NULL; scp = &(*scp)->sc_next ) {
+ if ( (*scp)->sc_response == over_back_response ) {
+ sc->sc_next = (*scp)->sc_next;
+ (*scp)->sc_next = sc;
+ return 0;
+ }
+ }
+
+ if ( append ) {
+ *scp = sc;
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * default return code in case of missing backend function
+ * and overlay stack returning SLAP_CB_CONTINUE
+ */
+static int op_rc[ op_last ] = {
+ LDAP_UNWILLING_TO_PERFORM, /* bind */
+ LDAP_UNWILLING_TO_PERFORM, /* unbind */
+ LDAP_UNWILLING_TO_PERFORM, /* search */
+ SLAP_CB_CONTINUE, /* compare; pass to frontend */
+ LDAP_UNWILLING_TO_PERFORM, /* modify */
+ LDAP_UNWILLING_TO_PERFORM, /* modrdn */
+ LDAP_UNWILLING_TO_PERFORM, /* add */
+ LDAP_UNWILLING_TO_PERFORM, /* delete */
+ LDAP_UNWILLING_TO_PERFORM, /* abandon */
+ LDAP_UNWILLING_TO_PERFORM, /* cancel */
+ LDAP_UNWILLING_TO_PERFORM, /* extended */
+ LDAP_SUCCESS, /* aux_operational */
+ LDAP_SUCCESS, /* aux_chk_referrals */
+ SLAP_CB_CONTINUE /* aux_chk_controls; pass to frontend */
+};
+
+int overlay_op_walk(
+ Operation *op,
+ SlapReply *rs,
+ slap_operation_t which,
+ slap_overinfo *oi,
+ slap_overinst *on
+)
+{
+ BackendInfo *bi;
+ int rc = SLAP_CB_CONTINUE;
+
+ for (; on; on=on->on_next ) {
+ if ( on->on_bi.bi_flags & SLAPO_BFLAG_DISABLED )
+ continue;
+ bi = &on->on_bi;
+ if ( (&bi->bi_op_bind)[ which ] ) {
+ op->o_bd->bd_info = (BackendInfo *)on;
+ rc = (&bi->bi_op_bind)[ which ]( op, rs );
+ if ( rc != SLAP_CB_CONTINUE ) break;
+ }
+ }
+ if ( rc == SLAP_CB_BYPASS )
+ rc = SLAP_CB_CONTINUE;
+ /* if an overlay halted processing, make sure
+ * any previously set cleanup handlers are run
+ */
+ if ( rc != SLAP_CB_CONTINUE )
+ goto cleanup;
+
+ bi = oi->oi_orig;
+ if ( (&bi->bi_op_bind)[ which ] ) {
+ op->o_bd->bd_info = bi;
+ rc = (&bi->bi_op_bind)[ which ]( op, rs );
+ }
+ /* should not fall thru this far without anything happening... */
+ if ( rc == SLAP_CB_CONTINUE ) {
+ rc = op_rc[ which ];
+ }
+
+ /* The underlying backend didn't handle the request, make sure
+ * overlay cleanup is processed.
+ */
+ if ( rc == LDAP_UNWILLING_TO_PERFORM ) {
+ slap_callback *sc_next;
+cleanup:
+ for ( ; op->o_callback && op->o_callback->sc_response !=
+ over_back_response; op->o_callback = sc_next ) {
+ sc_next = op->o_callback->sc_next;
+ if ( op->o_callback->sc_cleanup ) {
+ op->o_callback->sc_cleanup( op, rs );
+ }
+ }
+ }
+ return rc;
+}
+
+static int
+over_op_func(
+ Operation *op,
+ SlapReply *rs,
+ slap_operation_t which
+)
+{
+ slap_overinfo *oi;
+ slap_overinst *on;
+ BackendDB *be = op->o_bd, db;
+ slap_callback **sc;
+ slap_callback *cb;
+ int rc = SLAP_CB_CONTINUE;
+
+ /* FIXME: used to happen for instance during abandon
+ * when global overlays are used... */
+ assert( op->o_bd != NULL );
+
+ oi = op->o_bd->bd_info->bi_private;
+ on = oi->oi_list;
+
+ if ( !SLAP_ISOVERLAY( op->o_bd )) {
+ db = *op->o_bd;
+ db.be_flags |= SLAP_DBFLAG_OVERLAY;
+ op->o_bd = &db;
+ }
+ if ( op->o_tag != LDAP_REQ_ABANDON && op->o_tag != LDAP_REQ_UNBIND ) {
+ cb = (slap_callback *)op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
+ cb->sc_cleanup = NULL;
+ cb->sc_response = over_back_response;
+ cb->sc_writewait = NULL;
+ cb->sc_next = op->o_callback;
+ cb->sc_private = oi;
+ op->o_callback = cb;
+ }
+
+ rc = overlay_op_walk( op, rs, which, oi, on );
+ if ( rc != SLAPD_ASYNCOP && op->o_tag != LDAP_REQ_ABANDON && op->o_tag != LDAP_REQ_UNBIND ) {
+ for ( sc = &op->o_callback; *sc; sc = &(*sc)->sc_next ) {
+ if ( *sc == cb ) {
+ *sc = cb->sc_next;
+ op->o_tmpfree( cb, op->o_tmpmemctx );
+ break;
+ }
+ }
+ }
+
+ op->o_bd = be;
+ return rc;
+}
+
+static int
+over_op_bind( Operation *op, SlapReply *rs )
+{
+ return over_op_func( op, rs, op_bind );
+}
+
+static int
+over_op_unbind( Operation *op, SlapReply *rs )
+{
+ return over_op_func( op, rs, op_unbind );
+}
+
+static int
+over_op_search( Operation *op, SlapReply *rs )
+{
+ return over_op_func( op, rs, op_search );
+}
+
+static int
+over_op_compare( Operation *op, SlapReply *rs )
+{
+ return over_op_func( op, rs, op_compare );
+}
+
+static int
+over_op_modify( Operation *op, SlapReply *rs )
+{
+ return over_op_func( op, rs, op_modify );
+}
+
+static int
+over_op_modrdn( Operation *op, SlapReply *rs )
+{
+ return over_op_func( op, rs, op_modrdn );
+}
+
+static int
+over_op_add( Operation *op, SlapReply *rs )
+{
+ return over_op_func( op, rs, op_add );
+}
+
+static int
+over_op_delete( Operation *op, SlapReply *rs )
+{
+ return over_op_func( op, rs, op_delete );
+}
+
+static int
+over_op_abandon( Operation *op, SlapReply *rs )
+{
+ return over_op_func( op, rs, op_abandon );
+}
+
+static int
+over_op_cancel( Operation *op, SlapReply *rs )
+{
+ return over_op_func( op, rs, op_cancel );
+}
+
+static int
+over_op_extended( Operation *op, SlapReply *rs )
+{
+ return over_op_func( op, rs, op_extended );
+}
+
+static int
+over_aux_operational( Operation *op, SlapReply *rs )
+{
+ return over_op_func( op, rs, op_aux_operational );
+}
+
+static int
+over_aux_chk_referrals( Operation *op, SlapReply *rs )
+{
+ return over_op_func( op, rs, op_aux_chk_referrals );
+}
+
+static int
+over_aux_chk_controls( Operation *op, SlapReply *rs )
+{
+ return over_op_func( op, rs, op_aux_chk_controls );
+}
+
+enum conn_which {
+ conn_init = 0,
+ conn_destroy,
+ conn_last
+};
+
+static int
+over_connection_func(
+ BackendDB *bd,
+ Connection *conn,
+ enum conn_which which
+)
+{
+ slap_overinfo *oi;
+ slap_overinst *on;
+ BackendDB db;
+ int rc = SLAP_CB_CONTINUE;
+ BI_connection_init **func;
+
+ /* FIXME: used to happen for instance during abandon
+ * when global overlays are used... */
+ assert( bd != NULL );
+
+ oi = bd->bd_info->bi_private;
+ on = oi->oi_list;
+
+ if ( !SLAP_ISOVERLAY( bd ) ) {
+ db = *bd;
+ db.be_flags |= SLAP_DBFLAG_OVERLAY;
+ bd = &db;
+ }
+
+ for ( ; on; on = on->on_next ) {
+ if ( on->on_bi.bi_flags & SLAPO_BFLAG_DISABLED )
+ continue;
+ func = &on->on_bi.bi_connection_init;
+ if ( func[ which ] ) {
+ bd->bd_info = (BackendInfo *)on;
+ rc = func[ which ]( bd, conn );
+ if ( rc != SLAP_CB_CONTINUE ) break;
+ }
+ }
+
+ func = &oi->oi_orig->bi_connection_init;
+ if ( func[ which ] && rc == SLAP_CB_CONTINUE ) {
+ bd->bd_info = oi->oi_orig;
+ rc = func[ which ]( bd, conn );
+ }
+ /* should not fall thru this far without anything happening... */
+ if ( rc == SLAP_CB_CONTINUE ) {
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ return rc;
+}
+
+static int
+over_connection_init(
+ BackendDB *bd,
+ Connection *conn
+)
+{
+ return over_connection_func( bd, conn, conn_init );
+}
+
+static int
+over_connection_destroy(
+ BackendDB *bd,
+ Connection *conn
+)
+{
+ return over_connection_func( bd, conn, conn_destroy );
+}
+
+int
+overlay_register(
+ slap_overinst *on
+)
+{
+ slap_overinst *tmp;
+
+ /* FIXME: check for duplicates? */
+ for ( tmp = overlays; tmp != NULL; tmp = tmp->on_next ) {
+ if ( strcmp( on->on_bi.bi_type, tmp->on_bi.bi_type ) == 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "overlay_register(\"%s\"): "
+ "name already in use.\n",
+ on->on_bi.bi_type );
+ return -1;
+ }
+
+ if ( on->on_bi.bi_obsolete_names != NULL ) {
+ int i;
+
+ for ( i = 0; on->on_bi.bi_obsolete_names[ i ] != NULL; i++ ) {
+ if ( strcmp( on->on_bi.bi_obsolete_names[ i ], tmp->on_bi.bi_type ) == 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "overlay_register(\"%s\"): "
+ "obsolete name \"%s\" already in use "
+ "by overlay \"%s\".\n",
+ on->on_bi.bi_type,
+ on->on_bi.bi_obsolete_names[ i ],
+ tmp->on_bi.bi_type );
+ return -1;
+ }
+ }
+ }
+
+ if ( tmp->on_bi.bi_obsolete_names != NULL ) {
+ int i;
+
+ for ( i = 0; tmp->on_bi.bi_obsolete_names[ i ] != NULL; i++ ) {
+ int j;
+
+ if ( strcmp( on->on_bi.bi_type, tmp->on_bi.bi_obsolete_names[ i ] ) == 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "overlay_register(\"%s\"): "
+ "name already in use "
+ "as obsolete by overlay \"%s\".\n",
+ on->on_bi.bi_type,
+ tmp->on_bi.bi_obsolete_names[ i ] );
+ return -1;
+ }
+
+ if ( on->on_bi.bi_obsolete_names != NULL ) {
+ for ( j = 0; on->on_bi.bi_obsolete_names[ j ] != NULL; j++ ) {
+ if ( strcmp( on->on_bi.bi_obsolete_names[ j ], tmp->on_bi.bi_obsolete_names[ i ] ) == 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "overlay_register(\"%s\"): "
+ "obsolete name \"%s\" already in use "
+ "as obsolete by overlay \"%s\".\n",
+ on->on_bi.bi_type,
+ on->on_bi.bi_obsolete_names[ j ],
+ tmp->on_bi.bi_type );
+ return -1;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ on->on_next = overlays;
+ overlays = on;
+ return 0;
+}
+
+/*
+ * iterator on registered overlays; overlay_next( NULL ) returns the first
+ * overlay; subsequent calls with the previously returned value allow to
+ * iterate over the entire list; returns NULL when no more overlays are
+ * registered.
+ */
+
+slap_overinst *
+overlay_next(
+ slap_overinst *on
+)
+{
+ if ( on == NULL ) {
+ return overlays;
+ }
+
+ return on->on_next;
+}
+
+/*
+ * returns a specific registered overlay based on the type; NULL if not
+ * registered.
+ */
+
+slap_overinst *
+overlay_find( const char *over_type )
+{
+ slap_overinst *on = overlays;
+
+ assert( over_type != NULL );
+
+ for ( ; on; on = on->on_next ) {
+ if ( strcmp( on->on_bi.bi_type, over_type ) == 0 ) {
+ goto foundit;
+ }
+
+ if ( on->on_bi.bi_obsolete_names != NULL ) {
+ int i;
+
+ for ( i = 0; on->on_bi.bi_obsolete_names[ i ] != NULL; i++ ) {
+ if ( strcmp( on->on_bi.bi_obsolete_names[ i ], over_type ) == 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "overlay_find(\"%s\"): "
+ "obsolete name for \"%s\".\n",
+ on->on_bi.bi_obsolete_names[ i ],
+ on->on_bi.bi_type );
+ goto foundit;
+ }
+ }
+ }
+ }
+
+foundit:;
+ return on;
+}
+
+static const char overtype[] = "over";
+
+/*
+ * returns TRUE (1) if the database is actually an overlay instance;
+ * FALSE (0) otherwise.
+ */
+
+int
+overlay_is_over( BackendDB *be )
+{
+ return be->bd_info->bi_type == overtype;
+}
+
+/*
+ * returns TRUE (1) if the given database is actually an overlay
+ * instance and, somewhere in the list, contains the requested overlay;
+ * FALSE (0) otherwise.
+ */
+
+int
+overlay_is_inst( BackendDB *be, const char *over_type )
+{
+ slap_overinst *on;
+
+ assert( be != NULL );
+
+ if ( !overlay_is_over( be ) ) {
+ return 0;
+ }
+
+ on = ((slap_overinfo *)be->bd_info->bi_private)->oi_list;
+ for ( ; on; on = on->on_next ) {
+ if ( strcmp( on->on_bi.bi_type, over_type ) == 0 ) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int
+overlay_register_control( BackendDB *be, const char *oid )
+{
+ int gotit = 0;
+ int cid;
+
+ if ( slap_find_control_id( oid, &cid ) == LDAP_CONTROL_NOT_FOUND ) {
+ return -1;
+ }
+
+ if ( SLAP_ISGLOBALOVERLAY( be ) ) {
+ BackendDB *bd;
+
+ /* add to all backends... */
+ LDAP_STAILQ_FOREACH( bd, &backendDB, be_next ) {
+ if ( bd == be->bd_self ) {
+ gotit = 1;
+ }
+
+ /* overlays can be instantiated multiple times, use
+ * be_ctrls[ cid ] as an instance counter, so that the
+ * overlay's controls are only really disabled after the
+ * last instance called overlay_register_control() */
+ bd->be_ctrls[ cid ]++;
+ bd->be_ctrls[ SLAP_MAX_CIDS ] = 1;
+ }
+
+ }
+
+ if ( !gotit ) {
+ /* overlays can be instantiated multiple times, use
+ * be_ctrls[ cid ] as an instance counter, so that the
+ * overlay's controls are only really unregistered after the
+ * last instance called overlay_register_control() */
+ be->bd_self->be_ctrls[ cid ]++;
+ be->bd_self->be_ctrls[ SLAP_MAX_CIDS ] = 1;
+ }
+
+ return 0;
+}
+
+#ifdef SLAP_CONFIG_DELETE
+void
+overlay_unregister_control( BackendDB *be, const char *oid )
+{
+ int gotit = 0;
+ int cid;
+
+ if ( slap_find_control_id( oid, &cid ) == LDAP_CONTROL_NOT_FOUND ) {
+ return;
+ }
+
+ if ( SLAP_ISGLOBALOVERLAY( be ) ) {
+ BackendDB *bd;
+
+ /* remove from all backends... */
+ LDAP_STAILQ_FOREACH( bd, &backendDB, be_next ) {
+ if ( bd == be->bd_self ) {
+ gotit = 1;
+ }
+
+ bd->be_ctrls[ cid ]--;
+ }
+ }
+
+ if ( !gotit ) {
+ be->bd_self->be_ctrls[ cid ]--;
+ }
+}
+#endif /* SLAP_CONFIG_DELETE */
+
+void
+overlay_destroy_one( BackendDB *be, slap_overinst *on )
+{
+ slap_overinfo *oi = on->on_info;
+ slap_overinst **oidx;
+
+ for ( oidx = &oi->oi_list; *oidx; oidx = &(*oidx)->on_next ) {
+ if ( *oidx == on ) {
+ *oidx = on->on_next;
+ if ( on->on_bi.bi_db_destroy ) {
+ BackendInfo *bi_orig = be->bd_info;
+ be->bd_info = (BackendInfo *)on;
+ on->on_bi.bi_db_destroy( be, NULL );
+ be->bd_info = bi_orig;
+ }
+ free( on );
+ break;
+ }
+ }
+}
+
+#ifdef SLAP_CONFIG_DELETE
+typedef struct ov_remove_ctx {
+ BackendDB be;
+ slap_overinst *on;
+} ov_remove_ctx;
+
+int
+overlay_remove_cb( Operation *op, SlapReply *rs )
+{
+ slap_callback *sc = op->o_callback;
+ ov_remove_ctx *rm_ctx = (ov_remove_ctx*) op->o_callback->sc_private;
+
+ op->o_callback = sc->sc_next;
+ rm_ctx->be.bd_info = (BackendInfo*) rm_ctx->on;
+
+ if ( rm_ctx->on->on_bi.bi_db_close ) {
+ rm_ctx->on->on_bi.bi_db_close( &rm_ctx->be, NULL );
+ }
+ if ( rm_ctx->on->on_bi.bi_db_destroy ) {
+ rm_ctx->on->on_bi.bi_db_destroy( &rm_ctx->be, NULL );
+ }
+
+ /* clean up after removing last overlay */
+ if ( ! rm_ctx->on->on_info->oi_list ) {
+ ch_free(rm_ctx->on->on_info);
+ }
+ ch_free( rm_ctx->on );
+ op->o_tmpfree( sc, op->o_tmpmemctx );
+ return SLAP_CB_CONTINUE;
+}
+
+void
+overlay_remove( BackendDB *be, slap_overinst *on, Operation *op )
+{
+ slap_overinfo *oi = on->on_info;
+ slap_overinst **oidx;
+ ov_remove_ctx *rm_ctx;
+ slap_callback *rm_cb, *cb;
+
+ /* remove overlay from oi_list */
+ for ( oidx = &oi->oi_list; *oidx; oidx = &(*oidx)->on_next ) {
+ if ( *oidx == on ) {
+ *oidx = on->on_next;
+ break;
+ }
+ }
+
+ /* The db_close and db_destroy handlers to cleanup a release
+ * the overlay's resources are called from the cleanup callback
+ */
+
+ rm_cb = op->o_tmpalloc( sizeof( slap_callback ) + sizeof( ov_remove_ctx ), op->o_tmpmemctx );
+ rm_cb->sc_next = NULL;
+ rm_cb->sc_cleanup = overlay_remove_cb;
+ rm_cb->sc_response = NULL;
+ rm_cb->sc_private = (void*) ( rm_cb + 1 );
+ rm_cb->sc_writewait = NULL;
+
+ rm_ctx = rm_cb->sc_private;
+ rm_ctx->be = *be;
+ rm_ctx->on = on;
+
+ /* Append callback to the end of the list */
+ if ( !op->o_callback ) {
+ op->o_callback = rm_cb;
+ } else {
+ for ( cb = op->o_callback; cb->sc_next; cb = cb->sc_next );
+ cb->sc_next = rm_cb;
+ }
+
+ /* if this is the last overlay */
+ if ( ! on->on_info->oi_list ) {
+ /* reset db flags and bd_info to orig */
+ SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_GLOBAL_OVERLAY;
+ be->bd_info = on->on_info->oi_orig;
+ }
+}
+#endif /* SLAP_CONFIG_DELETE */
+
+void
+overlay_insert( BackendDB *be, slap_overinst *on2, slap_overinst ***prev,
+ int idx )
+{
+ slap_overinfo *oi = (slap_overinfo *)be->bd_info;
+
+ if ( idx == -1 ) {
+ on2->on_next = oi->oi_list;
+ oi->oi_list = on2;
+ } else {
+ int i, novs;
+ slap_overinst *on, **prev;
+
+ /* Since the list is in reverse order and is singly linked,
+ * we have to count the overlays and then insert backwards.
+ * Adding on overlay at a specific point should be a pretty
+ * infrequent occurrence.
+ */
+ novs = 0;
+ for ( on = oi->oi_list; on; on=on->on_next )
+ novs++;
+
+ if (idx > novs)
+ idx = 0;
+ else
+ idx = novs - idx;
+
+ /* advance to insertion point */
+ prev = &oi->oi_list;
+ for ( i=0; i<idx; i++ ) {
+ on = *prev;
+ prev = &on->on_next;
+ }
+ /* insert */
+ on2->on_next = *prev;
+ *prev = on2;
+ }
+}
+
+void
+overlay_move( BackendDB *be, slap_overinst *on, int idx )
+{
+ slap_overinfo *oi = (slap_overinfo *)be->bd_info;
+ slap_overinst **onp;
+
+ for (onp = &oi->oi_list; *onp; onp= &(*onp)->on_next) {
+ if ( *onp == on ) {
+ *onp = on->on_next;
+ break;
+ }
+ }
+ overlay_insert( be, on, &onp, idx );
+}
+
+/* add an overlay to a particular backend. */
+int
+overlay_config( BackendDB *be, const char *ov, int idx, BackendInfo **res, ConfigReply *cr )
+{
+ slap_overinst *on = NULL, *on2 = NULL, **prev;
+ slap_overinfo *oi = NULL;
+ BackendInfo *bi = NULL;
+
+ if ( res )
+ *res = NULL;
+
+ on = overlay_find( ov );
+ if ( !on ) {
+ Debug( LDAP_DEBUG_ANY, "overlay \"%s\" not found\n", ov );
+ return 1;
+ }
+
+ /* If this is the first overlay on this backend, set up the
+ * overlay info structure
+ */
+ if ( !overlay_is_over( be ) ) {
+ int isglobal = 0;
+
+ /* NOTE: the first time a global overlay is configured,
+ * frontendDB gets this flag; it is used later by overlays
+ * to determine if they're stacked on top of the frontendDB */
+ if ( be->bd_info == frontendDB->bd_info || SLAP_ISGLOBALOVERLAY( be ) ) {
+ isglobal = 1;
+ if ( on->on_bi.bi_flags & SLAPO_BFLAG_DBONLY ) {
+ snprintf( cr->msg, sizeof( cr->msg ), "overlay_config(): "
+ "overlay \"%s\" cannot be global.", ov );
+ Debug( LDAP_DEBUG_ANY, "%s\n", cr->msg );
+ return 1;
+ }
+
+ } else if ( on->on_bi.bi_flags & SLAPO_BFLAG_GLOBONLY ) {
+ snprintf( cr->msg, sizeof( cr->msg ), "overlay_config(): "
+ "overlay \"%s\" can only be global.", ov );
+ Debug( LDAP_DEBUG_ANY, "%s\n", cr->msg );
+ return 1;
+ }
+
+ oi = ch_malloc( sizeof( slap_overinfo ) );
+ oi->oi_orig = be->bd_info;
+ oi->oi_bi = *be->bd_info;
+ oi->oi_origdb = be;
+
+ if ( isglobal ) {
+ SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_GLOBAL_OVERLAY;
+ }
+
+ /* Save a pointer to ourself in bi_private.
+ */
+ oi->oi_bi.bi_private = oi;
+ oi->oi_list = NULL;
+ bi = (BackendInfo *)oi;
+
+ bi->bi_type = (char *)overtype;
+
+ bi->bi_db_config = over_db_config;
+ bi->bi_db_open = over_db_open;
+ bi->bi_db_close = over_db_close;
+ bi->bi_db_destroy = over_db_destroy;
+
+ bi->bi_op_bind = over_op_bind;
+ bi->bi_op_unbind = over_op_unbind;
+ bi->bi_op_search = over_op_search;
+ bi->bi_op_compare = over_op_compare;
+ bi->bi_op_modify = over_op_modify;
+ bi->bi_op_modrdn = over_op_modrdn;
+ bi->bi_op_add = over_op_add;
+ bi->bi_op_delete = over_op_delete;
+ bi->bi_op_abandon = over_op_abandon;
+ bi->bi_op_cancel = over_op_cancel;
+
+ bi->bi_extended = over_op_extended;
+
+ /*
+ * this is fine because it has the same
+ * args of the operations; we need to rework
+ * all the hooks to share the same args
+ * of the operations...
+ */
+ bi->bi_operational = over_aux_operational;
+ bi->bi_chk_referrals = over_aux_chk_referrals;
+ bi->bi_chk_controls = over_aux_chk_controls;
+
+ /* these have specific arglists */
+ bi->bi_entry_get_rw = over_entry_get_rw;
+ bi->bi_entry_release_rw = over_entry_release_rw;
+ bi->bi_access_allowed = over_access_allowed;
+ bi->bi_acl_group = over_acl_group;
+ bi->bi_acl_attribute = over_acl_attribute;
+
+ bi->bi_connection_init = over_connection_init;
+ bi->bi_connection_destroy = over_connection_destroy;
+
+ be->bd_info = bi;
+
+ } else {
+ if ( overlay_is_inst( be, ov ) ) {
+ if ( on->on_bi.bi_flags & SLAPO_BFLAG_SINGLE ) {
+ snprintf( cr->msg, sizeof( cr->msg ), "overlay_config(): "
+ "overlay \"%s\" already in list", ov );
+ Debug( LDAP_DEBUG_ANY, "%s\n", cr->msg );
+ return 1;
+ }
+ }
+
+ oi = be->bd_info->bi_private;
+ }
+
+ /* Insert new overlay into list. By default overlays are
+ * added to head of list and executed in LIFO order.
+ */
+ on2 = ch_calloc( 1, sizeof(slap_overinst) );
+ *on2 = *on;
+ on2->on_info = oi;
+
+ prev = &oi->oi_list;
+ /* Do we need to find the insertion point? */
+ if ( idx >= 0 ) {
+ int i;
+
+ /* count current overlays */
+ for ( i=0, on=oi->oi_list; on; on=on->on_next, i++ );
+
+ /* are we just appending a new one? */
+ if ( idx >= i )
+ idx = -1;
+ }
+ overlay_insert( be, on2, &prev, idx );
+
+ /* Any initialization needed? */
+ if ( on2->on_bi.bi_db_init ) {
+ int rc;
+ be->bd_info = (BackendInfo *)on2;
+ rc = on2->on_bi.bi_db_init( be, cr);
+ be->bd_info = (BackendInfo *)oi;
+ if ( rc ) {
+ *prev = on2->on_next;
+ ch_free( on2 );
+ on2 = NULL;
+ return rc;
+ }
+ }
+
+ if ( res )
+ *res = &on2->on_bi;
+
+ return 0;
+}
diff --git a/servers/slapd/bconfig.c b/servers/slapd/bconfig.c
new file mode 100644
index 0000000..c5c69ba
--- /dev/null
+++ b/servers/slapd/bconfig.c
@@ -0,0 +1,8150 @@
+/* bconfig.c - the config backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was originally developed by Howard Chu for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/dirent.h>
+#include <ac/errno.h>
+#include <sys/stat.h>
+#include <ac/unistd.h>
+
+#include "slap.h"
+
+#ifdef LDAP_SLAPI
+#include "slapi/slapi.h"
+#endif
+
+#include <ldif.h>
+#include <lutil.h>
+
+#include "slap-config.h"
+
+#define CONFIG_RDN "cn=config"
+#define SCHEMA_RDN "cn=schema"
+
+static struct berval config_rdn = BER_BVC(CONFIG_RDN);
+static struct berval schema_rdn = BER_BVC(SCHEMA_RDN);
+
+extern int slap_DN_strict; /* dn.c */
+
+#ifdef SLAPD_MODULES
+typedef struct modpath_s {
+ struct modpath_s *mp_next;
+ struct berval mp_path;
+ BerVarray mp_loads;
+} ModPaths;
+
+static ModPaths modpaths, *modlast = &modpaths, *modcur = &modpaths;
+#endif
+
+typedef struct ConfigFile {
+ struct ConfigFile *c_sibs;
+ struct ConfigFile *c_kids;
+ struct berval c_file;
+ AttributeType *c_at_head, *c_at_tail;
+ ContentRule *c_cr_head, *c_cr_tail;
+ ObjectClass *c_oc_head, *c_oc_tail;
+ OidMacro *c_om_head, *c_om_tail;
+ Syntax *c_syn_head, *c_syn_tail;
+ BerVarray c_dseFiles;
+} ConfigFile;
+
+typedef struct {
+ ConfigFile *cb_config;
+ CfEntryInfo *cb_root;
+ BackendDB cb_db; /* underlying database */
+ int cb_got_ldif;
+ int cb_use_ldif;
+ ldap_pvt_thread_rdwr_t cb_rwlock;
+} CfBackInfo;
+
+static CfBackInfo cfBackInfo;
+
+static char *passwd_salt;
+static FILE *logfile;
+static char *logfileName;
+static AccessControl *defacl_parsed = NULL;
+
+static struct berval cfdir;
+
+/* Private state */
+static AttributeDescription *cfAd_backend, *cfAd_database, *cfAd_overlay,
+ *cfAd_include, *cfAd_attr, *cfAd_oc, *cfAd_om, *cfAd_syntax;
+
+static ConfigFile *cfn;
+
+static Avlnode *CfOcTree;
+
+/* System schema state */
+extern AttributeType *at_sys_tail; /* at.c */
+extern ObjectClass *oc_sys_tail; /* oc.c */
+extern OidMacro *om_sys_tail; /* oidm.c */
+extern Syntax *syn_sys_tail; /* syntax.c */
+static AttributeType *cf_at_tail;
+static ObjectClass *cf_oc_tail;
+static OidMacro *cf_om_tail;
+static Syntax *cf_syn_tail;
+
+static int config_add_internal( CfBackInfo *cfb, Entry *e, ConfigArgs *ca,
+ SlapReply *rs, int *renumber, Operation *op );
+
+static int config_check_schema( Operation *op, CfBackInfo *cfb );
+
+static ConfigDriver config_fname;
+static ConfigDriver config_cfdir;
+static ConfigDriver config_generic;
+static ConfigDriver config_search_base;
+static ConfigDriver config_passwd_hash;
+static ConfigDriver config_schema_dn;
+static ConfigDriver config_sizelimit;
+static ConfigDriver config_timelimit;
+static ConfigDriver config_overlay;
+static ConfigDriver config_subordinate;
+static ConfigDriver config_suffix;
+#ifdef LDAP_TCP_BUFFER
+static ConfigDriver config_tcp_buffer;
+#endif /* LDAP_TCP_BUFFER */
+static ConfigDriver config_rootdn;
+static ConfigDriver config_rootpw;
+static ConfigDriver config_restrict;
+static ConfigDriver config_allows;
+static ConfigDriver config_disallows;
+static ConfigDriver config_requires;
+static ConfigDriver config_security;
+static ConfigDriver config_referral;
+static ConfigDriver config_loglevel;
+static ConfigDriver config_updatedn;
+static ConfigDriver config_updateref;
+static ConfigDriver config_extra_attrs;
+static ConfigDriver config_include;
+static ConfigDriver config_obsolete;
+#ifdef HAVE_TLS
+static ConfigDriver config_tls_option;
+static ConfigDriver config_tls_config;
+#endif
+extern ConfigDriver syncrepl_config;
+
+enum {
+ CFG_ACL = 1,
+ CFG_BACKEND,
+ CFG_DATABASE,
+ CFG_TLS_RAND,
+ CFG_TLS_CIPHER,
+ CFG_TLS_PROTOCOL_MIN,
+ CFG_TLS_CERT_FILE,
+ CFG_TLS_CERT_KEY,
+ CFG_TLS_CA_PATH,
+ CFG_TLS_CA_FILE,
+ CFG_TLS_DH_FILE,
+ CFG_TLS_VERIFY,
+ CFG_TLS_CRLCHECK,
+ CFG_TLS_CRL_FILE,
+ CFG_CONCUR,
+ CFG_THREADS,
+ CFG_SALT,
+ CFG_LIMITS,
+ CFG_RO,
+ CFG_REWRITE,
+ CFG_DEPTH,
+ CFG_OID,
+ CFG_OC,
+ CFG_DIT,
+ CFG_ATTR,
+ CFG_ATOPT,
+ CFG_ROOTDSE,
+ CFG_LOGFILE,
+ CFG_PLUGIN,
+ CFG_MODLOAD,
+ CFG_MODPATH,
+ CFG_LASTMOD,
+ CFG_LASTBIND,
+ CFG_AZPOLICY,
+ CFG_AZREGEXP,
+ CFG_AZDUC,
+ CFG_AZDUC_IGNORE,
+ CFG_SASLSECP,
+ CFG_SSTR_IF_MAX,
+ CFG_SSTR_IF_MIN,
+ CFG_TTHREADS,
+ CFG_MULTIPROVIDER,
+ CFG_HIDDEN,
+ CFG_MONITORING,
+ CFG_SERVERID,
+ CFG_SORTVALS,
+ CFG_IX_INTLEN,
+ CFG_SYNTAX,
+ CFG_ACL_ADD,
+ CFG_SYNC_SUBENTRY,
+ CFG_LTHREADS,
+ CFG_IX_HASH64,
+ CFG_DISABLED,
+ CFG_THREADQS,
+ CFG_TLS_ECNAME,
+ CFG_TLS_CACERT,
+ CFG_TLS_CERT,
+ CFG_TLS_KEY,
+
+ CFG_LAST
+};
+
+typedef struct {
+ char *name, *oid;
+} OidRec;
+
+static OidRec OidMacros[] = {
+ /* OpenLDAProot:1.12.2 */
+ { "OLcfg", "1.3.6.1.4.1.4203.1.12.2" },
+ { "OLcfgAt", "OLcfg:3" },
+ { "OLcfgGlAt", "OLcfgAt:0" },
+ { "OLcfgBkAt", "OLcfgAt:1" },
+ { "OLcfgDbAt", "OLcfgAt:2" },
+ { "OLcfgOvAt", "OLcfgAt:3" },
+ { "OLcfgCtAt", "OLcfgAt:4" }, /* contrib modules */
+ { "OLcfgOc", "OLcfg:4" },
+ { "OLcfgGlOc", "OLcfgOc:0" },
+ { "OLcfgBkOc", "OLcfgOc:1" },
+ { "OLcfgDbOc", "OLcfgOc:2" },
+ { "OLcfgOvOc", "OLcfgOc:3" },
+ { "OLcfgCtOc", "OLcfgOc:4" }, /* contrib modules */
+
+ /* Syntaxes. We should just start using the standard names and
+ * document that they are predefined and available for users
+ * to reference in their own schema. Defining schema without
+ * OID macros is for masochists...
+ */
+ { "OMsyn", "1.3.6.1.4.1.1466.115.121.1" },
+ { "OMsBoolean", "OMsyn:7" },
+ { "OMsDN", "OMsyn:12" },
+ { "OMsDirectoryString", "OMsyn:15" },
+ { "OMsIA5String", "OMsyn:26" },
+ { "OMsInteger", "OMsyn:27" },
+ { "OMsOID", "OMsyn:38" },
+ { "OMsOctetString", "OMsyn:40" },
+ { NULL, NULL }
+};
+
+/*
+ * Backend/Database registry
+ *
+ * OLcfg{Bk|Db}{Oc|At}:0 -> common
+ * OLcfg{Bk|Db}{Oc|At}:1 -> back-bdb(/back-hdb) (removed)
+ * OLcfg{Bk|Db}{Oc|At}:2 -> back-ldif
+ * OLcfg{Bk|Db}{Oc|At}:3 -> back-ldap/meta
+ * OLcfg{Bk|Db}{Oc|At}:4 -> back-monitor
+ * OLcfg{Bk|Db}{Oc|At}:5 -> back-relay
+ * OLcfg{Bk|Db}{Oc|At}:6 -> back-sql(/back-ndb)
+ * OLcfg{Bk|Db}{Oc|At}:7 -> back-sock
+ * OLcfg{Bk|Db}{Oc|At}:8 -> back-null
+ * OLcfg{Bk|Db}{Oc|At}:9 -> back-passwd
+ * OLcfg{Bk|Db}{Oc|At}:10 -> back-shell (removed)
+ * OLcfg{Bk|Db}{Oc|At}:11 -> back-perl
+ * OLcfg{Bk|Db}{Oc|At}:12 -> back-mdb
+ * OLcfg{Bk|Db}{Oc|At}:13 -> lloadd
+ */
+
+/*
+ * Overlay registry
+ *
+ * OLcfgOv{Oc|At}:1 -> syncprov
+ * OLcfgOv{Oc|At}:2 -> pcache
+ * OLcfgOv{Oc|At}:3 -> chain
+ * OLcfgOv{Oc|At}:4 -> accesslog
+ * OLcfgOv{Oc|At}:5 -> valsort
+ * OLcfgOv{Oc|At}:7 -> distproc
+ * OLcfgOv{Oc|At}:8 -> dynlist
+ * OLcfgOv{Oc|At}:9 -> dds
+ * OLcfgOv{Oc|At}:10 -> unique
+ * OLcfgOv{Oc|At}:11 -> refint
+ * OLcfgOv{Oc|At}:12 -> ppolicy
+ * OLcfgOv{Oc|At}:13 -> constraint
+ * OLcfgOv{Oc|At}:14 -> translucent
+ * OLcfgOv{Oc|At}:15 -> auditlog
+ * OLcfgOv{Oc|At}:16 -> rwm
+ * OLcfgOv{Oc|At}:17 -> dyngroup
+ * OLcfgOv{Oc|At}:18 -> memberof
+ * OLcfgOv{Oc|At}:19 -> collect
+ * OLcfgOv{Oc|At}:20 -> retcode
+ * OLcfgOv{Oc|At}:21 -> sssvlv
+ * OLcfgOv{Oc|At}:22 -> autoca
+ * OLcfgOv{Oc|At}:24 -> remoteauth
+ */
+
+/* alphabetical ordering */
+
+static ConfigTable config_back_cf_table[] = {
+ /* This attr is read-only */
+ { "", "", 0, 0, 0, ARG_MAGIC,
+ &config_fname, "( OLcfgGlAt:78 NAME 'olcConfigFile' "
+ "DESC 'File for slapd configuration directives' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "", "", 0, 0, 0, ARG_MAGIC,
+ &config_cfdir, "( OLcfgGlAt:79 NAME 'olcConfigDir' "
+ "DESC 'Directory for slapd configuration backend' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "access", NULL, 0, 0, 0, ARG_MAY_DB|ARG_MAGIC|CFG_ACL,
+ &config_generic, "( OLcfgGlAt:1 NAME 'olcAccess' "
+ "DESC 'Access Control List' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )", NULL, NULL },
+ { "add_content_acl", NULL, 0, 0, 0, ARG_MAY_DB|ARG_ON_OFF|ARG_MAGIC|CFG_ACL_ADD,
+ &config_generic, "( OLcfgGlAt:86 NAME 'olcAddContentAcl' "
+ "DESC 'Check ACLs against content of Add ops' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "allows", "features", 2, 0, 5, ARG_PRE_DB|ARG_MAGIC,
+ &config_allows, "( OLcfgGlAt:2 NAME 'olcAllows' "
+ "DESC 'Allowed set of deprecated features' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "argsfile", "file", 2, 2, 0, ARG_STRING,
+ &slapd_args_file, "( OLcfgGlAt:3 NAME 'olcArgsFile' "
+ "DESC 'File for slapd command line options' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "attributeoptions", NULL, 0, 0, 0, ARG_MAGIC|CFG_ATOPT,
+ &config_generic, "( OLcfgGlAt:5 NAME 'olcAttributeOptions' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "attribute", "attribute", 2, 0, STRLENOF( "attribute" ),
+ ARG_PAREN|ARG_MAGIC|CFG_ATTR,
+ &config_generic, "( OLcfgGlAt:4 NAME 'olcAttributeTypes' "
+ "DESC 'OpenLDAP attributeTypes' "
+ "EQUALITY caseIgnoreMatch "
+ "SUBSTR caseIgnoreSubstringsMatch "
+ "SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )",
+ NULL, NULL },
+ { "authid-rewrite", "rewrite", 2, 0, STRLENOF( "authid-rewrite" ),
+ ARG_MAGIC|CFG_REWRITE, &config_generic,
+ "( OLcfgGlAt:6 NAME 'olcAuthIDRewrite' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )", NULL, NULL },
+ { "authz-policy", "policy", 2, 2, 0, ARG_STRING|ARG_MAGIC|CFG_AZPOLICY,
+ &config_generic, "( OLcfgGlAt:7 NAME 'olcAuthzPolicy' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "authz-regexp", "regexp> <DN", 3, 3, 0, ARG_MAGIC|CFG_AZREGEXP,
+ &config_generic, "( OLcfgGlAt:8 NAME 'olcAuthzRegexp' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )", NULL, NULL },
+ { "backend", "type", 2, 2, 0, ARG_PRE_DB|ARG_MAGIC|CFG_BACKEND,
+ &config_generic, "( OLcfgGlAt:9 NAME 'olcBackend' "
+ "DESC 'A type of backend' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE X-ORDERED 'SIBLINGS' )",
+ NULL, NULL },
+ { "concurrency", "level", 2, 2, 0, ARG_INT|ARG_MAGIC|CFG_CONCUR,
+ &config_generic, "( OLcfgGlAt:10 NAME 'olcConcurrency' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "conn_max_pending", "max", 2, 2, 0, ARG_INT,
+ &slap_conn_max_pending, "( OLcfgGlAt:11 NAME 'olcConnMaxPending' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "conn_max_pending_auth", "max", 2, 2, 0, ARG_INT,
+ &slap_conn_max_pending_auth, "( OLcfgGlAt:12 NAME 'olcConnMaxPendingAuth' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "database", "type", 2, 2, 0, ARG_MAGIC|CFG_DATABASE,
+ &config_generic, "( OLcfgGlAt:13 NAME 'olcDatabase' "
+ "DESC 'The backend type for a database instance' "
+ "SUP olcBackend SINGLE-VALUE X-ORDERED 'SIBLINGS' )", NULL, NULL },
+ { "defaultSearchBase", "dn", 2, 2, 0, ARG_PRE_BI|ARG_PRE_DB|ARG_DN|ARG_QUOTE|ARG_MAGIC,
+ &config_search_base, "( OLcfgGlAt:14 NAME 'olcDefaultSearchBase' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
+ { "disabled", "on|off", 2, 2, 0, ARG_DB|ARG_ON_OFF|ARG_MAGIC|CFG_DISABLED,
+ &config_generic, "( OLcfgDbAt:0.21 NAME 'olcDisabled' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "disallows", "features", 2, 0, 8, ARG_PRE_DB|ARG_MAGIC,
+ &config_disallows, "( OLcfgGlAt:15 NAME 'olcDisallows' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "ditcontentrule", NULL, 0, 0, 0, ARG_MAGIC|CFG_DIT|ARG_NO_DELETE|ARG_NO_INSERT,
+ &config_generic, "( OLcfgGlAt:16 NAME 'olcDitContentRules' "
+ "DESC 'OpenLDAP DIT content rules' "
+ "EQUALITY caseIgnoreMatch "
+ "SUBSTR caseIgnoreSubstringsMatch "
+ "SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )",
+ NULL, NULL },
+ { "extra_attrs", "attrlist", 2, 2, 0, ARG_DB|ARG_MAGIC,
+ &config_extra_attrs, "( OLcfgDbAt:0.20 NAME 'olcExtraAttrs' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "gentlehup", "on|off", 2, 2, 0,
+#ifdef SIGHUP
+ ARG_ON_OFF, &global_gentlehup,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:17 NAME 'olcGentleHUP' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "hidden", "on|off", 2, 2, 0, ARG_DB|ARG_ON_OFF|ARG_MAGIC|CFG_HIDDEN,
+ &config_generic, "( OLcfgDbAt:0.17 NAME 'olcHidden' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "idletimeout", "timeout", 2, 2, 0, ARG_INT,
+ &global_idletimeout, "( OLcfgGlAt:18 NAME 'olcIdleTimeout' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "include", "file", 2, 2, 0, ARG_MAGIC,
+ &config_include, "( OLcfgGlAt:19 NAME 'olcInclude' "
+ "SUP labeledURI )", NULL, NULL },
+ { "index_hash64", "on|off", 2, 2, 0, ARG_ON_OFF|ARG_MAGIC|CFG_IX_HASH64,
+ &config_generic, "( OLcfgGlAt:94 NAME 'olcIndexHash64' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "index_substr_if_minlen", "min", 2, 2, 0, ARG_UINT|ARG_NONZERO|ARG_MAGIC|CFG_SSTR_IF_MIN,
+ &config_generic, "( OLcfgGlAt:20 NAME 'olcIndexSubstrIfMinLen' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL,
+ { .v_uint = SLAP_INDEX_SUBSTR_IF_MINLEN_DEFAULT }
+ },
+ { "index_substr_if_maxlen", "max", 2, 2, 0, ARG_UINT|ARG_NONZERO|ARG_MAGIC|CFG_SSTR_IF_MAX,
+ &config_generic, "( OLcfgGlAt:21 NAME 'olcIndexSubstrIfMaxLen' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL,
+ { .v_uint = SLAP_INDEX_SUBSTR_IF_MAXLEN_DEFAULT }
+ },
+ { "index_substr_any_len", "len", 2, 2, 0, ARG_UINT|ARG_NONZERO,
+ &index_substr_any_len, "( OLcfgGlAt:22 NAME 'olcIndexSubstrAnyLen' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL,
+ { .v_uint = SLAP_INDEX_SUBSTR_ANY_LEN_DEFAULT } },
+ { "index_substr_any_step", "step", 2, 2, 0, ARG_UINT|ARG_NONZERO,
+ &index_substr_any_step, "( OLcfgGlAt:23 NAME 'olcIndexSubstrAnyStep' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL,
+ { .v_uint = SLAP_INDEX_SUBSTR_ANY_STEP_DEFAULT } },
+ { "index_intlen", "len", 2, 2, 0, ARG_UINT|ARG_MAGIC|CFG_IX_INTLEN,
+ &config_generic, "( OLcfgGlAt:84 NAME 'olcIndexIntLen' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "lastmod", "on|off", 2, 2, 0, ARG_DB|ARG_ON_OFF|ARG_MAGIC|CFG_LASTMOD,
+ &config_generic, "( OLcfgDbAt:0.4 NAME 'olcLastMod' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "lastbind", "on|off", 2, 2, 0, ARG_DB|ARG_ON_OFF|ARG_MAGIC|CFG_LASTBIND,
+ &config_generic, "( OLcfgDbAt:0.22 NAME 'olcLastBind' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "ldapsyntax", "syntax", 2, 0, 0,
+ ARG_PAREN|ARG_MAGIC|CFG_SYNTAX,
+ &config_generic, "( OLcfgGlAt:85 NAME 'olcLdapSyntaxes' "
+ "DESC 'OpenLDAP ldapSyntax' "
+ "EQUALITY caseIgnoreMatch "
+ "SUBSTR caseIgnoreSubstringsMatch "
+ "SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )",
+ NULL, NULL },
+ { "limits", "limits", 2, 0, 0, ARG_DB|ARG_MAGIC|CFG_LIMITS,
+ &config_generic, "( OLcfgDbAt:0.5 NAME 'olcLimits' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )", NULL, NULL },
+ { "listener-threads", "count", 2, 0, 0,
+ ARG_UINT|ARG_MAGIC|CFG_LTHREADS, &config_generic,
+ "( OLcfgGlAt:93 NAME 'olcListenerThreads' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL,
+ { .v_uint = 1 }
+ },
+ { "localSSF", "ssf", 2, 2, 0, ARG_INT,
+ &local_ssf, "( OLcfgGlAt:26 NAME 'olcLocalSSF' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL,
+ { .v_int = LDAP_PVT_SASL_LOCAL_SSF } },
+ { "logfile", "file", 2, 2, 0, ARG_STRING|ARG_MAGIC|CFG_LOGFILE,
+ &config_generic, "( OLcfgGlAt:27 NAME 'olcLogFile' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "loglevel", "level", 2, 0, 0, ARG_MAGIC,
+ &config_loglevel, "( OLcfgGlAt:28 NAME 'olcLogLevel' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "maxDerefDepth", "depth", 2, 2, 0, ARG_DB|ARG_INT|ARG_MAGIC|CFG_DEPTH,
+ &config_generic, "( OLcfgDbAt:0.6 NAME 'olcMaxDerefDepth' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL,
+ { .v_int = SLAPD_DEFAULT_MAXDEREFDEPTH }
+ },
+ { "maxFilterDepth", "depth", 2, 2, 0, ARG_INT,
+ &slap_max_filter_depth, "( OLcfgGlAt:101 NAME 'olcMaxFilterDepth' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL,
+ { .v_int = SLAP_MAX_FILTER_DEPTH_DEFAULT }
+ },
+ { "multiprovider", "on|off", 2, 2, 0, ARG_DB|ARG_ON_OFF|ARG_MAGIC|CFG_MULTIPROVIDER,
+ &config_generic, "( OLcfgDbAt:0.16 NAME ( 'olcMultiProvider' 'olcMirrorMode' ) "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "moduleload", "file", 2, 0, 0,
+#ifdef SLAPD_MODULES
+ ARG_MAGIC|CFG_MODLOAD|ARG_NO_DELETE, &config_generic,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:30 NAME 'olcModuleLoad' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )", NULL, NULL },
+ { "modulepath", "path", 2, 2, 0,
+#ifdef SLAPD_MODULES
+ ARG_MAGIC|CFG_MODPATH|ARG_NO_DELETE|ARG_NO_INSERT, &config_generic,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:31 NAME 'olcModulePath' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "monitoring", "TRUE|FALSE", 2, 2, 0,
+ ARG_MAGIC|CFG_MONITORING|ARG_DB|ARG_ON_OFF, &config_generic,
+ "( OLcfgDbAt:0.18 NAME 'olcMonitoring' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "objectclass", "objectclass", 2, 0, 0, ARG_PAREN|ARG_MAGIC|CFG_OC,
+ &config_generic, "( OLcfgGlAt:32 NAME 'olcObjectClasses' "
+ "DESC 'OpenLDAP object classes' "
+ "EQUALITY caseIgnoreMatch "
+ "SUBSTR caseIgnoreSubstringsMatch "
+ "SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )",
+ NULL, NULL },
+ { "objectidentifier", "name> <oid", 3, 3, 0, ARG_MAGIC|CFG_OID,
+ &config_generic, "( OLcfgGlAt:33 NAME 'olcObjectIdentifier' "
+ "EQUALITY caseIgnoreMatch "
+ "SUBSTR caseIgnoreSubstringsMatch "
+ "SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )", NULL, NULL },
+ { "overlay", "overlay", 2, 2, 0, ARG_MAGIC,
+ &config_overlay, "( OLcfgGlAt:34 NAME 'olcOverlay' "
+ "SUP olcDatabase SINGLE-VALUE X-ORDERED 'SIBLINGS' )", NULL, NULL },
+ { "password-crypt-salt-format", "salt", 2, 2, 0, ARG_STRING|ARG_MAGIC|CFG_SALT,
+ &config_generic, "( OLcfgGlAt:35 NAME 'olcPasswordCryptSaltFormat' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "password-hash", "hash", 2, 0, 0, ARG_MAGIC,
+ &config_passwd_hash, "( OLcfgGlAt:36 NAME 'olcPasswordHash' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "pidfile", "file", 2, 2, 0, ARG_STRING,
+ &slapd_pid_file, "( OLcfgGlAt:37 NAME 'olcPidFile' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "plugin", NULL, 0, 0, 0,
+#ifdef LDAP_SLAPI
+ ARG_MAGIC|CFG_PLUGIN, &config_generic,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:38 NAME 'olcPlugin' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )", NULL, NULL },
+ { "pluginlog", "filename", 2, 2, 0,
+#ifdef LDAP_SLAPI
+ ARG_STRING, &slapi_log_file,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:39 NAME 'olcPluginLogFile' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "readonly", "on|off", 2, 2, 0, ARG_MAY_DB|ARG_ON_OFF|ARG_MAGIC|CFG_RO,
+ &config_generic, "( OLcfgGlAt:40 NAME 'olcReadOnly' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "referral", "url", 2, 2, 0, ARG_MAGIC,
+ &config_referral, "( OLcfgGlAt:41 NAME 'olcReferral' "
+ "SUP labeledURI SINGLE-VALUE )", NULL, NULL },
+ { "replica", "host or uri", 2, 0, 0, ARG_DB|ARG_MAGIC,
+ &config_obsolete, "( OLcfgDbAt:0.7 NAME 'olcReplica' "
+ "EQUALITY caseIgnoreMatch "
+ "SUP labeledURI X-ORDERED 'VALUES' )", NULL, NULL },
+ { "replica-argsfile", NULL, 0, 0, 0, ARG_MAY_DB|ARG_MAGIC,
+ &config_obsolete, "( OLcfgGlAt:43 NAME 'olcReplicaArgsFile' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "replica-pidfile", NULL, 0, 0, 0, ARG_MAY_DB|ARG_MAGIC,
+ &config_obsolete, "( OLcfgGlAt:44 NAME 'olcReplicaPidFile' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "replicationInterval", NULL, 0, 0, 0, ARG_MAY_DB|ARG_MAGIC,
+ &config_obsolete, "( OLcfgGlAt:45 NAME 'olcReplicationInterval' "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "replogfile", "filename", 2, 2, 0, ARG_MAY_DB|ARG_MAGIC,
+ &config_obsolete, "( OLcfgGlAt:46 NAME 'olcReplogFile' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "require", "features", 2, 0, 7, ARG_MAY_DB|ARG_MAGIC,
+ &config_requires, "( OLcfgGlAt:47 NAME 'olcRequires' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "restrict", "op_list", 2, 0, 0, ARG_MAY_DB|ARG_MAGIC,
+ &config_restrict, "( OLcfgGlAt:48 NAME 'olcRestrict' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "reverse-lookup", "on|off", 2, 2, 0,
+#ifdef SLAPD_RLOOKUPS
+ ARG_ON_OFF, &use_reverse_lookup,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:49 NAME 'olcReverseLookup' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "rootdn", "dn", 2, 2, 0, ARG_DB|ARG_DN|ARG_QUOTE|ARG_MAGIC,
+ &config_rootdn, "( OLcfgDbAt:0.8 NAME 'olcRootDN' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
+ { "rootDSE", "file", 2, 2, 0, ARG_MAGIC|CFG_ROOTDSE,
+ &config_generic, "( OLcfgGlAt:51 NAME 'olcRootDSE' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "rootpw", "password", 2, 2, 0, ARG_BERVAL|ARG_DB|ARG_MAGIC,
+ &config_rootpw, "( OLcfgDbAt:0.9 NAME 'olcRootPW' "
+ "EQUALITY octetStringMatch "
+ "SYNTAX OMsOctetString SINGLE-VALUE )", NULL, NULL },
+ { "sasl-authz-policy", NULL, 2, 2, 0, ARG_MAGIC|CFG_AZPOLICY,
+ &config_generic, NULL, NULL, NULL },
+ { "sasl-auxprops", NULL, 2, 0, 0,
+#ifdef HAVE_CYRUS_SASL
+ ARG_STRING|ARG_UNIQUE, &slap_sasl_auxprops,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:89 NAME 'olcSaslAuxprops' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "sasl-auxprops-dontusecopy", NULL, 2, 0, 0,
+#if defined(HAVE_CYRUS_SASL) && defined(SLAP_AUXPROP_DONTUSECOPY)
+ ARG_MAGIC|CFG_AZDUC, &config_generic,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:91 NAME 'olcSaslAuxpropsDontUseCopy' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "sasl-auxprops-dontusecopy-ignore", "true|FALSE", 2, 0, 0,
+#if defined(HAVE_CYRUS_SASL) && defined(SLAP_AUXPROP_DONTUSECOPY)
+ ARG_ON_OFF|CFG_AZDUC_IGNORE, &slap_dontUseCopy_ignore,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:92 NAME 'olcSaslAuxpropsDontUseCopyIgnore' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "sasl-cbinding", NULL, 2, 2, 0,
+#ifdef HAVE_CYRUS_SASL
+ ARG_STRING, &sasl_cbinding,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:100 NAME 'olcSaslCBinding' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "sasl-host", "host", 2, 2, 0,
+#ifdef HAVE_CYRUS_SASL
+ ARG_STRING|ARG_UNIQUE, &sasl_host,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:53 NAME 'olcSaslHost' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "sasl-realm", "realm", 2, 2, 0,
+#ifdef HAVE_CYRUS_SASL
+ ARG_STRING|ARG_UNIQUE, &global_realm,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:54 NAME 'olcSaslRealm' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "sasl-regexp", NULL, 3, 3, 0, ARG_MAGIC|CFG_AZREGEXP,
+ &config_generic, NULL, NULL, NULL },
+ { "sasl-secprops", "properties", 2, 2, 0,
+#ifdef HAVE_CYRUS_SASL
+ ARG_MAGIC|CFG_SASLSECP, &config_generic,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:56 NAME 'olcSaslSecProps' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "saslRegexp", NULL, 3, 3, 0, ARG_MAGIC|CFG_AZREGEXP,
+ &config_generic, NULL, NULL, NULL },
+ { "schemadn", "dn", 2, 2, 0, ARG_MAY_DB|ARG_DN|ARG_QUOTE|ARG_MAGIC,
+ &config_schema_dn, "( OLcfgGlAt:58 NAME 'olcSchemaDN' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
+ { "security", "factors", 2, 0, 0, ARG_MAY_DB|ARG_MAGIC,
+ &config_security, "( OLcfgGlAt:59 NAME 'olcSecurity' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "serverID", "number> <[URI]", 2, 3, 0, ARG_MAGIC|CFG_SERVERID,
+ &config_generic, "( OLcfgGlAt:81 NAME 'olcServerID' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "sizelimit", "limit", 2, 0, 0, ARG_MAY_DB|ARG_MAGIC,
+ &config_sizelimit, "( OLcfgGlAt:60 NAME 'olcSizeLimit' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "sockbuf_max_incoming", "max", 2, 2, 0, ARG_BER_LEN_T,
+ &sockbuf_max_incoming, "( OLcfgGlAt:61 NAME 'olcSockbufMaxIncoming' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL,
+ { .v_ber_t = SLAP_SB_MAX_INCOMING_DEFAULT } },
+ { "sockbuf_max_incoming_auth", "max", 2, 2, 0, ARG_BER_LEN_T,
+ &sockbuf_max_incoming_auth, "( OLcfgGlAt:62 NAME 'olcSockbufMaxIncomingAuth' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL,
+ { .v_ber_t = SLAP_SB_MAX_INCOMING_AUTH } },
+ { "sortvals", "attr", 2, 0, 0, ARG_MAGIC|CFG_SORTVALS,
+ &config_generic, "( OLcfgGlAt:83 NAME 'olcSortVals' "
+ "DESC 'Attributes whose values will always be sorted' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "subordinate", "[advertise]", 1, 2, 0, ARG_DB|ARG_MAGIC,
+ &config_subordinate, "( OLcfgDbAt:0.15 NAME 'olcSubordinate' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "suffix", "suffix", 2, 2, 0, ARG_DB|ARG_DN|ARG_QUOTE|ARG_MAGIC,
+ &config_suffix, "( OLcfgDbAt:0.10 NAME 'olcSuffix' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN )", NULL, NULL },
+ { "sync_use_subentry", NULL, 0, 0, 0, ARG_ON_OFF|ARG_DB|ARG_MAGIC|CFG_SYNC_SUBENTRY,
+ &config_generic, "( OLcfgDbAt:0.19 NAME 'olcSyncUseSubentry' "
+ "DESC 'Store sync context in a subentry' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "syncrepl", NULL, 0, 0, 0, ARG_DB|ARG_MAGIC,
+ &syncrepl_config, "( OLcfgDbAt:0.11 NAME 'olcSyncrepl' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString X-ORDERED 'VALUES' )", NULL, NULL },
+ { "tcp-buffer", "[listener=<listener>] [{read|write}=]size", 0, 0, 0,
+#ifndef LDAP_TCP_BUFFER
+ ARG_IGNORED, NULL,
+#else /* LDAP_TCP_BUFFER */
+ ARG_MAGIC, &config_tcp_buffer,
+#endif /* LDAP_TCP_BUFFER */
+ "( OLcfgGlAt:90 NAME 'olcTCPBuffer' "
+ "EQUALITY caseExactMatch "
+ "DESC 'Custom TCP buffer size' "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "threads", "count", 2, 2, 0,
+ ARG_INT|ARG_MAGIC|CFG_THREADS, &config_generic,
+ "( OLcfgGlAt:66 NAME 'olcThreads' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL,
+ { .v_int = SLAP_MAX_WORKER_THREADS }
+ },
+ { "threadqueues", "count", 2, 2, 0,
+ ARG_INT|ARG_MAGIC|CFG_THREADQS, &config_generic,
+ "( OLcfgGlAt:95 NAME 'olcThreadQueues' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL,
+ { .v_int = 1 }
+ },
+ { "timelimit", "limit", 2, 0, 0, ARG_MAY_DB|ARG_MAGIC,
+ &config_timelimit, "( OLcfgGlAt:67 NAME 'olcTimeLimit' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "TLSCACertificate", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_CACERT|ARG_BINARY|ARG_MAGIC, &config_tls_option,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:97 NAME 'olcTLSCACertificate' "
+ "DESC 'X.509 certificate, must use ;binary' "
+ "EQUALITY certificateExactMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.8 SINGLE-VALUE )", NULL, NULL },
+ { "TLSCACertificateFile", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_CA_FILE|ARG_STRING|ARG_MAGIC, &config_tls_option,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:68 NAME 'olcTLSCACertificateFile' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "TLSCACertificatePath", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_CA_PATH|ARG_STRING|ARG_MAGIC, &config_tls_option,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:69 NAME 'olcTLSCACertificatePath' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "TLSCertificate", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_CERT|ARG_BINARY|ARG_MAGIC, &config_tls_option,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:98 NAME 'olcTLSCertificate' "
+ "DESC 'X.509 certificate, must use ;binary' "
+ "EQUALITY certificateExactMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.8 SINGLE-VALUE )", NULL, NULL },
+ { "TLSCertificateFile", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_CERT_FILE|ARG_STRING|ARG_MAGIC, &config_tls_option,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:70 NAME 'olcTLSCertificateFile' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "TLSCertificateKey", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_KEY|ARG_BINARY|ARG_MAGIC, &config_tls_option,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:99 NAME 'olcTLSCertificateKey' "
+ "DESC 'X.509 privateKey, must use ;binary' "
+ "EQUALITY privateKeyMatch "
+ "SYNTAX 1.2.840.113549.1.8.1.1 SINGLE-VALUE )", NULL, NULL },
+ { "TLSCertificateKeyFile", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_CERT_KEY|ARG_STRING|ARG_MAGIC, &config_tls_option,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:71 NAME 'olcTLSCertificateKeyFile' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "TLSCipherSuite", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_CIPHER|ARG_STRING|ARG_MAGIC, &config_tls_option,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:72 NAME 'olcTLSCipherSuite' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "TLSCRLCheck", NULL, 2, 2, 0,
+#if defined(HAVE_TLS) && defined(HAVE_OPENSSL)
+ CFG_TLS_CRLCHECK|ARG_STRING|ARG_MAGIC, &config_tls_config,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:73 NAME 'olcTLSCRLCheck' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "TLSCRLFile", NULL, 2, 2, 0,
+#if defined(HAVE_GNUTLS)
+ CFG_TLS_CRL_FILE|ARG_STRING|ARG_MAGIC, &config_tls_option,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:82 NAME 'olcTLSCRLFile' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "TLSRandFile", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_RAND|ARG_STRING|ARG_MAGIC, &config_tls_option,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:74 NAME 'olcTLSRandFile' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "TLSVerifyClient", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_VERIFY|ARG_STRING|ARG_MAGIC, &config_tls_config,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:75 NAME 'olcTLSVerifyClient' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "TLSDHParamFile", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_DH_FILE|ARG_STRING|ARG_MAGIC, &config_tls_option,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:77 NAME 'olcTLSDHParamFile' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "TLSECName", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_ECNAME|ARG_STRING|ARG_MAGIC, &config_tls_option,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:96 NAME 'olcTLSECName' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "TLSProtocolMin", NULL, 2, 2, 0,
+#ifdef HAVE_TLS
+ CFG_TLS_PROTOCOL_MIN|ARG_STRING|ARG_MAGIC, &config_tls_config,
+#else
+ ARG_IGNORED, NULL,
+#endif
+ "( OLcfgGlAt:87 NAME 'olcTLSProtocolMin' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "tool-threads", "count", 2, 2, 0, ARG_INT|ARG_MAGIC|CFG_TTHREADS,
+ &config_generic, "( OLcfgGlAt:80 NAME 'olcToolThreads' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL,
+ { .v_int = 1 }
+ },
+ { "ucdata-path", "path", 2, 2, 0, ARG_IGNORED,
+ NULL, NULL, NULL, NULL },
+ { "updatedn", "dn", 2, 2, 0, ARG_DB|ARG_DN|ARG_QUOTE|ARG_MAGIC,
+ &config_updatedn, "( OLcfgDbAt:0.12 NAME 'olcUpdateDN' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
+ { "updateref", "url", 2, 2, 0, ARG_DB|ARG_MAGIC,
+ &config_updateref, "( OLcfgDbAt:0.13 NAME 'olcUpdateRef' "
+ "EQUALITY caseIgnoreMatch "
+ "SUP labeledURI )", NULL, NULL },
+ { "writetimeout", "timeout", 2, 2, 0, ARG_INT,
+ &global_writetimeout, "( OLcfgGlAt:88 NAME 'olcWriteTimeout' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ /* Legacy keywords */
+ { "mirrormode", "on|off", 2, 2, 0, ARG_DB|ARG_ON_OFF|ARG_MAGIC|CFG_MULTIPROVIDER,
+ &config_generic, NULL, NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED,
+ NULL, NULL, NULL, NULL }
+};
+
+/* Need to no-op this keyword for dynamic config */
+ConfigTable olcDatabaseDummy[] = {
+ { "", "", 0, 0, 0, ARG_IGNORED,
+ NULL, "( OLcfgGlAt:13 NAME 'olcDatabase' "
+ "DESC 'The backend type for a database instance' "
+ "SUP olcBackend SINGLE-VALUE X-ORDERED 'SIBLINGS' )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+/* Routines to check if a child can be added to this type */
+static ConfigLDAPadd cfAddSchema, cfAddInclude, cfAddDatabase,
+ cfAddBackend, cfAddModule, cfAddOverlay;
+
+/* NOTE: be careful when defining array members
+ * that can be conditionally compiled */
+#define CFOC_GLOBAL cf_ocs[1]
+#define CFOC_SCHEMA cf_ocs[2]
+#define CFOC_BACKEND cf_ocs[3]
+#define CFOC_DATABASE cf_ocs[4]
+#define CFOC_OVERLAY cf_ocs[5]
+#define CFOC_INCLUDE cf_ocs[6]
+#define CFOC_FRONTEND cf_ocs[7]
+#ifdef SLAPD_MODULES
+#define CFOC_MODULE cf_ocs[8]
+#endif /* SLAPD_MODULES */
+
+static ConfigOCs cf_ocs[] = {
+ { "( OLcfgGlOc:0 "
+ "NAME 'olcConfig' "
+ "DESC 'OpenLDAP configuration object' "
+ "ABSTRACT SUP top )", Cft_Abstract, NULL },
+ { "( OLcfgGlOc:1 "
+ "NAME 'olcGlobal' "
+ "DESC 'OpenLDAP Global configuration options' "
+ "SUP olcConfig STRUCTURAL "
+ "MAY ( cn $ olcConfigFile $ olcConfigDir $ olcAllows $ olcArgsFile $ "
+ "olcAttributeOptions $ olcAuthIDRewrite $ "
+ "olcAuthzPolicy $ olcAuthzRegexp $ olcConcurrency $ "
+ "olcConnMaxPending $ olcConnMaxPendingAuth $ "
+ "olcDisallows $ olcGentleHUP $ olcIdleTimeout $ "
+ "olcIndexSubstrIfMaxLen $ olcIndexSubstrIfMinLen $ "
+ "olcIndexSubstrAnyLen $ olcIndexSubstrAnyStep $ olcIndexHash64 $ "
+ "olcIndexIntLen $ "
+ "olcListenerThreads $ olcLocalSSF $ olcLogFile $ olcLogLevel $ "
+ "olcMaxFilterDepth $ "
+ "olcPasswordCryptSaltFormat $ olcPasswordHash $ olcPidFile $ "
+ "olcPluginLogFile $ olcReadOnly $ olcReferral $ "
+ "olcReplogFile $ olcRequires $ olcRestrict $ olcReverseLookup $ "
+ "olcRootDSE $ "
+ "olcSaslAuxprops $ olcSaslAuxpropsDontUseCopy $ olcSaslAuxpropsDontUseCopyIgnore $ "
+ "olcSaslCBinding $ olcSaslHost $ olcSaslRealm $ olcSaslSecProps $ "
+ "olcSecurity $ olcServerID $ olcSizeLimit $ "
+ "olcSockbufMaxIncoming $ olcSockbufMaxIncomingAuth $ "
+ "olcTCPBuffer $ "
+ "olcThreads $ olcThreadQueues $ "
+ "olcTimeLimit $ olcTLSCACertificateFile $ "
+ "olcTLSCACertificatePath $ olcTLSCertificateFile $ "
+ "olcTLSCertificateKeyFile $ olcTLSCipherSuite $ olcTLSCRLCheck $ "
+ "olcTLSCACertificate $ olcTLSCertificate $ olcTLSCertificateKey $ "
+ "olcTLSRandFile $ olcTLSVerifyClient $ olcTLSDHParamFile $ olcTLSECName $ "
+ "olcTLSCRLFile $ olcTLSProtocolMin $ olcToolThreads $ olcWriteTimeout $ "
+ "olcObjectIdentifier $ olcAttributeTypes $ olcObjectClasses $ "
+ "olcDitContentRules $ olcLdapSyntaxes ) )", Cft_Global },
+ { "( OLcfgGlOc:2 "
+ "NAME 'olcSchemaConfig' "
+ "DESC 'OpenLDAP schema object' "
+ "SUP olcConfig STRUCTURAL "
+ "MAY ( cn $ olcObjectIdentifier $ olcLdapSyntaxes $ "
+ "olcAttributeTypes $ olcObjectClasses $ olcDitContentRules ) )",
+ Cft_Schema, NULL, cfAddSchema },
+ { "( OLcfgGlOc:3 "
+ "NAME 'olcBackendConfig' "
+ "DESC 'OpenLDAP Backend-specific options' "
+ "SUP olcConfig STRUCTURAL "
+ "MUST olcBackend )", Cft_Backend, NULL, cfAddBackend },
+ { "( OLcfgGlOc:4 "
+ "NAME 'olcDatabaseConfig' "
+ "DESC 'OpenLDAP Database-specific options' "
+ "SUP olcConfig STRUCTURAL "
+ "MUST olcDatabase "
+ "MAY ( olcDisabled $ olcHidden $ olcSuffix $ olcSubordinate $ olcAccess $ "
+ "olcAddContentAcl $ olcLastMod $ olcLastBind $ olcLimits $ "
+ "olcMaxDerefDepth $ olcPlugin $ olcReadOnly $ olcReplica $ "
+ "olcReplicaArgsFile $ olcReplicaPidFile $ olcReplicationInterval $ "
+ "olcReplogFile $ olcRequires $ olcRestrict $ olcRootDN $ olcRootPW $ "
+ "olcSchemaDN $ olcSecurity $ olcSizeLimit $ olcSyncUseSubentry $ olcSyncrepl $ "
+ "olcTimeLimit $ olcUpdateDN $ olcUpdateRef $ olcMultiProvider $ "
+ "olcMonitoring $ olcExtraAttrs ) )",
+ Cft_Database, NULL, cfAddDatabase },
+ { "( OLcfgGlOc:5 "
+ "NAME 'olcOverlayConfig' "
+ "DESC 'OpenLDAP Overlay-specific options' "
+ "SUP olcConfig STRUCTURAL "
+ "MUST olcOverlay "
+ "MAY olcDisabled )", Cft_Overlay, NULL, cfAddOverlay },
+ { "( OLcfgGlOc:6 "
+ "NAME 'olcIncludeFile' "
+ "DESC 'OpenLDAP configuration include file' "
+ "SUP olcConfig STRUCTURAL "
+ "MUST olcInclude "
+ "MAY ( cn $ olcRootDSE ) )",
+ /* Used to be Cft_Include, that def has been removed */
+ Cft_Abstract, NULL, cfAddInclude },
+ /* This should be STRUCTURAL like all the other database classes, but
+ * that would mean inheriting all of the olcDatabaseConfig attributes,
+ * which causes them to be merged twice in config_build_entry.
+ */
+ { "( OLcfgGlOc:7 "
+ "NAME 'olcFrontendConfig' "
+ "DESC 'OpenLDAP frontend configuration' "
+ "AUXILIARY "
+ "MAY ( olcDefaultSearchBase $ olcPasswordHash $ olcSortVals ) )",
+ Cft_Database, NULL, NULL },
+#ifdef SLAPD_MODULES
+ { "( OLcfgGlOc:8 "
+ "NAME 'olcModuleList' "
+ "DESC 'OpenLDAP dynamic module info' "
+ "SUP olcConfig STRUCTURAL "
+ "MAY ( cn $ olcModulePath $ olcModuleLoad ) )",
+ Cft_Module, NULL, cfAddModule },
+#endif
+ { NULL, 0, NULL }
+};
+
+typedef struct ServerID {
+ struct ServerID *si_next;
+ struct berval si_url;
+ int si_num;
+} ServerID;
+
+static ServerID *sid_list;
+static ServerID *sid_set;
+
+typedef struct voidList {
+ struct voidList *vl_next;
+ void *vl_ptr;
+} voidList;
+
+typedef struct ADlist {
+ struct ADlist *al_next;
+ AttributeDescription *al_desc;
+} ADlist;
+
+static ADlist *sortVals;
+
+static int new_daemon_threads;
+
+static int
+config_resize_lthreads(ConfigArgs *c)
+{
+ return slapd_daemon_resize( new_daemon_threads );
+}
+
+#define GOT_CONFIG 1
+#define GOT_FRONTEND 2
+static int
+config_unique_db;
+
+static int
+config_generic(ConfigArgs *c) {
+ int i;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ int rc = 0;
+ switch(c->type) {
+ case CFG_CONCUR:
+ c->value_int = ldap_pvt_thread_get_concurrency();
+ break;
+ case CFG_THREADS:
+ c->value_int = connection_pool_max;
+ break;
+ case CFG_THREADQS:
+ c->value_int = connection_pool_queues;
+ break;
+ case CFG_TTHREADS:
+ c->value_int = slap_tool_thread_max;
+ break;
+ case CFG_LTHREADS:
+ c->value_uint = slapd_daemon_threads;
+ break;
+ case CFG_SALT:
+ if ( passwd_salt )
+ c->value_string = ch_strdup( passwd_salt );
+ else
+ rc = 1;
+ break;
+ case CFG_LIMITS:
+ if ( c->be->be_limits ) {
+ char buf[4096*3];
+ struct berval bv;
+
+ for ( i=0; c->be->be_limits[i]; i++ ) {
+ bv.bv_len = snprintf( buf, sizeof( buf ), SLAP_X_ORDERED_FMT, i );
+ if ( bv.bv_len >= sizeof( buf ) ) {
+ ber_bvarray_free_x( c->rvalue_vals, NULL );
+ c->rvalue_vals = NULL;
+ rc = 1;
+ break;
+ }
+ bv.bv_val = buf + bv.bv_len;
+ limits_unparse( c->be->be_limits[i], &bv,
+ sizeof( buf ) - ( bv.bv_val - buf ) );
+ bv.bv_len += bv.bv_val - buf;
+ bv.bv_val = buf;
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ }
+ if ( !c->rvalue_vals ) rc = 1;
+ break;
+ case CFG_RO:
+ c->value_int = (c->be->be_restrictops & SLAP_RESTRICT_READONLY);
+ break;
+ case CFG_AZPOLICY:
+ c->value_string = ch_strdup( slap_sasl_getpolicy());
+ break;
+ case CFG_AZREGEXP:
+ slap_sasl_regexp_unparse( &c->rvalue_vals );
+ if ( !c->rvalue_vals ) rc = 1;
+ break;
+#ifdef HAVE_CYRUS_SASL
+#ifdef SLAP_AUXPROP_DONTUSECOPY
+ case CFG_AZDUC: {
+ static int duc_done = 0;
+
+ /* take the opportunity to initialize with known values */
+ if ( !duc_done ) {
+ struct berval duc[] = { BER_BVC("cmusaslsecretOTP"), BER_BVNULL };
+ int i;
+
+ for ( i = 0; !BER_BVISNULL( &duc[ i ] ); i++ ) {
+ const char *text = NULL;
+ AttributeDescription *ad = NULL;
+
+ if ( slap_bv2ad( &duc[ i ], &ad, &text ) == LDAP_SUCCESS ) {
+ int gotit = 0;
+ if ( slap_dontUseCopy_propnames ) {
+ int j;
+
+ for ( j = 0; !BER_BVISNULL( &slap_dontUseCopy_propnames[ j ] ); j++ ) {
+ if ( bvmatch( &slap_dontUseCopy_propnames[ j ], &ad->ad_cname ) ) {
+ gotit = 1;
+ }
+ }
+ }
+
+ if ( !gotit ) {
+ value_add_one( &slap_dontUseCopy_propnames, &ad->ad_cname );
+ }
+ }
+ }
+
+ duc_done = 1;
+ }
+
+ if ( slap_dontUseCopy_propnames != NULL ) {
+ ber_bvarray_dup_x( &c->rvalue_vals, slap_dontUseCopy_propnames, NULL );
+ } else {
+ rc = 1;
+ }
+ } break;
+#endif /* SLAP_AUXPROP_DONTUSECOPY */
+ case CFG_SASLSECP: {
+ struct berval bv = BER_BVNULL;
+ slap_sasl_secprops_unparse( &bv );
+ if ( !BER_BVISNULL( &bv )) {
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ } else {
+ rc = 1;
+ }
+ }
+ break;
+#endif
+ case CFG_DEPTH:
+ c->value_int = c->be->be_max_deref_depth;
+ break;
+ case CFG_DISABLED:
+ if ( c->bi ) {
+ /* overlay */
+ if ( c->bi->bi_flags & SLAPO_BFLAG_DISABLED ) {
+ c->value_int = 1;
+ } else {
+ rc = 1;
+ }
+ } else {
+ /* database */
+ if ( SLAP_DBDISABLED( c->be )) {
+ c->value_int = 1;
+ } else {
+ rc = 1;
+ }
+ }
+ break;
+ case CFG_HIDDEN:
+ if ( SLAP_DBHIDDEN( c->be )) {
+ c->value_int = 1;
+ } else {
+ rc = 1;
+ }
+ break;
+ case CFG_OID: {
+ ConfigFile *cf = c->ca_private;
+ if ( !cf )
+ oidm_unparse( &c->rvalue_vals, NULL, NULL, 1 );
+ else if ( cf->c_om_head )
+ oidm_unparse( &c->rvalue_vals, cf->c_om_head,
+ cf->c_om_tail, 0 );
+ if ( !c->rvalue_vals )
+ rc = 1;
+ }
+ break;
+ case CFG_ATOPT:
+ ad_unparse_options( &c->rvalue_vals );
+ break;
+ case CFG_OC: {
+ ConfigFile *cf = c->ca_private;
+ if ( !cf )
+ oc_unparse( &c->rvalue_vals, NULL, NULL, 1 );
+ else if ( cf->c_oc_head )
+ oc_unparse( &c->rvalue_vals, cf->c_oc_head,
+ cf->c_oc_tail, 0 );
+ if ( !c->rvalue_vals )
+ rc = 1;
+ }
+ break;
+ case CFG_ATTR: {
+ ConfigFile *cf = c->ca_private;
+ if ( !cf )
+ at_unparse( &c->rvalue_vals, NULL, NULL, 1 );
+ else if ( cf->c_at_head )
+ at_unparse( &c->rvalue_vals, cf->c_at_head,
+ cf->c_at_tail, 0 );
+ if ( !c->rvalue_vals )
+ rc = 1;
+ }
+ break;
+ case CFG_SYNTAX: {
+ ConfigFile *cf = c->ca_private;
+ if ( !cf )
+ syn_unparse( &c->rvalue_vals, NULL, NULL, 1 );
+ else if ( cf->c_syn_head )
+ syn_unparse( &c->rvalue_vals, cf->c_syn_head,
+ cf->c_syn_tail, 0 );
+ if ( !c->rvalue_vals )
+ rc = 1;
+ }
+ break;
+ case CFG_DIT: {
+ ConfigFile *cf = c->ca_private;
+ if ( !cf )
+ cr_unparse( &c->rvalue_vals, NULL, NULL, 1 );
+ else if ( cf->c_cr_head )
+ cr_unparse( &c->rvalue_vals, cf->c_cr_head,
+ cf->c_cr_tail, 0 );
+ if ( !c->rvalue_vals )
+ rc = 1;
+ }
+ break;
+
+ case CFG_ACL: {
+ AccessControl *a;
+ char *src, *dst, ibuf[11];
+ struct berval bv, abv;
+ for (i=0, a=c->be->be_acl; a; i++,a=a->acl_next) {
+ abv.bv_len = snprintf( ibuf, sizeof( ibuf ), SLAP_X_ORDERED_FMT, i );
+ if ( abv.bv_len >= sizeof( ibuf ) ) {
+ ber_bvarray_free_x( c->rvalue_vals, NULL );
+ c->rvalue_vals = NULL;
+ i = 0;
+ break;
+ }
+ acl_unparse( a, &bv );
+ abv.bv_val = ch_malloc( abv.bv_len + bv.bv_len + 1 );
+ AC_MEMCPY( abv.bv_val, ibuf, abv.bv_len );
+ /* Turn TAB / EOL into plain space */
+ for (src=bv.bv_val,dst=abv.bv_val+abv.bv_len; *src; src++) {
+ if (isspace((unsigned char)*src)) *dst++ = ' ';
+ else *dst++ = *src;
+ }
+ *dst = '\0';
+ if (dst[-1] == ' ') {
+ dst--;
+ *dst = '\0';
+ }
+ abv.bv_len = dst - abv.bv_val;
+ ber_bvarray_add( &c->rvalue_vals, &abv );
+ }
+ rc = (!i);
+ break;
+ }
+ case CFG_ACL_ADD:
+ c->value_int = (SLAP_DBACL_ADD(c->be) != 0);
+ break;
+ case CFG_ROOTDSE: {
+ ConfigFile *cf = c->ca_private;
+ if ( cf->c_dseFiles ) {
+ value_add( &c->rvalue_vals, cf->c_dseFiles );
+ } else {
+ rc = 1;
+ }
+ }
+ break;
+ case CFG_SERVERID:
+ if ( sid_list ) {
+ ServerID *si;
+ struct berval bv;
+
+ for ( si = sid_list; si; si=si->si_next ) {
+ assert( si->si_num >= 0 && si->si_num <= SLAP_SYNC_SID_MAX );
+ if ( !BER_BVISEMPTY( &si->si_url )) {
+ bv.bv_len = si->si_url.bv_len + 6;
+ bv.bv_val = ch_malloc( bv.bv_len );
+ bv.bv_len = sprintf( bv.bv_val, "%d %s", si->si_num,
+ si->si_url.bv_val );
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ } else {
+ char buf[5];
+ bv.bv_val = buf;
+ bv.bv_len = sprintf( buf, "%d", si->si_num );
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ }
+ } else {
+ rc = 1;
+ }
+ break;
+ case CFG_LOGFILE:
+ if ( logfileName )
+ c->value_string = ch_strdup( logfileName );
+ else
+ rc = 1;
+ break;
+ case CFG_LASTMOD:
+ c->value_int = (SLAP_NOLASTMOD(c->be) == 0);
+ break;
+ case CFG_LASTBIND:
+ c->value_int = (SLAP_LASTBIND(c->be) != 0);
+ break;
+ case CFG_SYNC_SUBENTRY:
+ c->value_int = (SLAP_SYNC_SUBENTRY(c->be) != 0);
+ break;
+ case CFG_MULTIPROVIDER:
+ if ( SLAP_SHADOW(c->be))
+ c->value_int = (SLAP_MULTIPROVIDER(c->be) != 0);
+ else
+ rc = 1;
+ break;
+ case CFG_MONITORING:
+ c->value_int = (SLAP_DBMONITORING(c->be) != 0);
+ break;
+ case CFG_SSTR_IF_MAX:
+ c->value_uint = index_substr_if_maxlen;
+ break;
+ case CFG_SSTR_IF_MIN:
+ c->value_uint = index_substr_if_minlen;
+ break;
+ case CFG_IX_HASH64:
+ c->value_int = slap_hash64( -1 );
+ break;
+ case CFG_IX_INTLEN:
+ c->value_int = index_intlen;
+ break;
+ case CFG_SORTVALS: {
+ ADlist *sv;
+ rc = 1;
+ for ( sv = sortVals; sv; sv = sv->al_next ) {
+ value_add_one( &c->rvalue_vals, &sv->al_desc->ad_cname );
+ rc = 0;
+ }
+ } break;
+#ifdef SLAPD_MODULES
+ case CFG_MODLOAD: {
+ ModPaths *mp = c->ca_private;
+ if (mp->mp_loads) {
+ int i;
+ for (i=0; !BER_BVISNULL(&mp->mp_loads[i]); i++) {
+ struct berval bv;
+ bv.bv_val = c->log;
+ bv.bv_len = snprintf( bv.bv_val, sizeof( c->log ),
+ SLAP_X_ORDERED_FMT "%s", i,
+ mp->mp_loads[i].bv_val );
+ if ( bv.bv_len >= sizeof( c->log ) ) {
+ ber_bvarray_free_x( c->rvalue_vals, NULL );
+ c->rvalue_vals = NULL;
+ break;
+ }
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ }
+
+ rc = c->rvalue_vals ? 0 : 1;
+ }
+ break;
+ case CFG_MODPATH: {
+ ModPaths *mp = c->ca_private;
+ if ( !BER_BVISNULL( &mp->mp_path ))
+ value_add_one( &c->rvalue_vals, &mp->mp_path );
+
+ rc = c->rvalue_vals ? 0 : 1;
+ }
+ break;
+#endif
+#ifdef LDAP_SLAPI
+ case CFG_PLUGIN:
+ slapi_int_plugin_unparse( c->be, &c->rvalue_vals );
+ if ( !c->rvalue_vals ) rc = 1;
+ break;
+#endif
+ case CFG_REWRITE:
+ rc = slap_sasl_rewrite_unparse( &c->rvalue_vals );
+ break;
+ default:
+ rc = 1;
+ }
+ return rc;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ int rc = 0;
+ switch(c->type) {
+ /* single-valued attrs */
+ case CFG_CONCUR:
+ /* FIXME: There is currently no way to retrieve the default? */
+ break;
+
+ case CFG_THREADS:
+ if ( slapMode & SLAP_SERVER_MODE )
+ ldap_pvt_thread_pool_maxthreads(&connection_pool,
+ SLAP_MAX_WORKER_THREADS);
+ connection_pool_max = SLAP_MAX_WORKER_THREADS; /* save for reference */
+ break;
+
+ case CFG_THREADQS:
+ if ( slapMode & SLAP_SERVER_MODE )
+ ldap_pvt_thread_pool_queues(&connection_pool, 1);
+ connection_pool_queues = 1; /* save for reference */
+ break;
+
+ case CFG_TTHREADS:
+ slap_tool_thread_max = 1;
+ break;
+
+ case CFG_LTHREADS:
+ new_daemon_threads = 1;
+ config_push_cleanup( c, config_resize_lthreads );
+ break;
+
+ case CFG_AZPOLICY:
+ slap_sasl_setpolicy( "none" );
+ break;
+
+ case CFG_DEPTH:
+ c->be->be_max_deref_depth = SLAPD_DEFAULT_MAXDEREFDEPTH;
+ break;
+
+ case CFG_LASTMOD:
+ SLAP_DBFLAGS(c->be) &= ~SLAP_DBFLAG_NOLASTMOD;
+ break;
+
+ case CFG_LASTBIND:
+ SLAP_DBFLAGS(c->be) &= ~SLAP_DBFLAG_LASTBIND;
+ break;
+
+ case CFG_MONITORING:
+ SLAP_DBFLAGS(c->be) &= ~SLAP_DBFLAG_MONITORING;
+ break;
+
+ case CFG_SASLSECP:
+#ifdef HAVE_CYRUS_SASL
+ slap_sasl_secprops( "" );
+#endif
+ break;
+
+ case CFG_SSTR_IF_MAX:
+ index_substr_if_maxlen = SLAP_INDEX_SUBSTR_IF_MAXLEN_DEFAULT;
+ break;
+
+ case CFG_SSTR_IF_MIN:
+ index_substr_if_minlen = SLAP_INDEX_SUBSTR_IF_MINLEN_DEFAULT;
+ break;
+
+ case CFG_ACL_ADD:
+ SLAP_DBFLAGS(c->be) &= ~SLAP_DBFLAG_ACL_ADD;
+ break;
+
+ case CFG_SYNC_SUBENTRY:
+ SLAP_DBFLAGS(c->be) &= ~SLAP_DBFLAG_SYNC_SUBENTRY;
+ break;
+
+ case CFG_RO:
+ c->be->be_restrictops &= ~SLAP_RESTRICT_READONLY;
+ break;
+
+#ifdef LDAP_SLAPI
+ case CFG_PLUGIN:
+ slapi_int_unregister_plugins(c->be, c->valx);
+ break;
+#endif
+
+ /* no-op, requires slapd restart */
+ case CFG_MODLOAD:
+ snprintf(c->log, sizeof( c->log ), "change requires slapd restart");
+ break;
+
+ case CFG_MULTIPROVIDER:
+ SLAP_DBFLAGS(c->be) &= ~SLAP_DBFLAG_MULTI_SHADOW;
+ if(SLAP_SHADOW(c->be))
+ SLAP_DBFLAGS(c->be) |= SLAP_DBFLAG_SINGLE_SHADOW;
+ break;
+
+#if defined(HAVE_CYRUS_SASL) && defined(SLAP_AUXPROP_DONTUSECOPY)
+ case CFG_AZDUC:
+ if ( c->valx < 0 ) {
+ if ( slap_dontUseCopy_propnames != NULL ) {
+ ber_bvarray_free( slap_dontUseCopy_propnames );
+ slap_dontUseCopy_propnames = NULL;
+ }
+
+ } else {
+ int i;
+
+ if ( slap_dontUseCopy_propnames == NULL ) {
+ rc = 1;
+ break;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &slap_dontUseCopy_propnames[ i ] ) && i < c->valx; i++ );
+ if ( i < c->valx ) {
+ rc = 1;
+ break;
+ }
+ ber_memfree( slap_dontUseCopy_propnames[ i ].bv_val );
+ for ( ; !BER_BVISNULL( &slap_dontUseCopy_propnames[ i + 1 ] ); i++ ) {
+ slap_dontUseCopy_propnames[ i ] = slap_dontUseCopy_propnames[ i + 1 ];
+ }
+ BER_BVZERO( &slap_dontUseCopy_propnames[ i ] );
+ }
+ break;
+#endif /* SLAP_AUXPROP_DONTUSECOPY */
+
+ case CFG_AZREGEXP:
+ rc = slap_sasl_regexp_delete( c->valx );
+ break;
+
+ case CFG_REWRITE:
+ rc = slap_sasl_rewrite_delete( c->valx );
+ break;
+
+ case CFG_SALT:
+ ch_free( passwd_salt );
+ passwd_salt = NULL;
+ break;
+
+ case CFG_LOGFILE:
+ ch_free( logfileName );
+ logfileName = NULL;
+ if ( logfile ) {
+ fclose( logfile );
+ logfile = NULL;
+ }
+ break;
+
+ case CFG_SERVERID: {
+ ServerID *si, **sip;
+
+ for ( i=0, si = sid_list, sip = &sid_list;
+ si; si = *sip, i++ ) {
+ if ( c->valx == -1 || i == c->valx ) {
+ *sip = si->si_next;
+ if ( sid_set == si )
+ sid_set = NULL;
+ ch_free( si );
+ if ( c->valx >= 0 )
+ break;
+ } else {
+ sip = &si->si_next;
+ }
+ }
+ }
+ break;
+ case CFG_HIDDEN:
+ c->be->be_flags &= ~SLAP_DBFLAG_HIDDEN;
+ break;
+
+ case CFG_DISABLED:
+ if ( c->bi ) {
+ c->bi->bi_flags &= ~SLAP_DBFLAG_DISABLED;
+ if ( c->bi->bi_db_open ) {
+ BackendInfo *bi_orig = c->be->bd_info;
+ c->be->bd_info = c->bi;
+ rc = c->bi->bi_db_open( c->be, &c->reply );
+ c->be->bd_info = bi_orig;
+ }
+ } else {
+ c->be->be_flags &= ~SLAP_DBFLAG_DISABLED;
+ rc = backend_startup_one( c->be, &c->reply );
+ }
+ break;
+
+ case CFG_IX_HASH64:
+ slap_hash64( 0 );
+ break;
+
+ case CFG_IX_INTLEN:
+ index_intlen = SLAP_INDEX_INTLEN_DEFAULT;
+ index_intlen_strlen = SLAP_INDEX_INTLEN_STRLEN(
+ SLAP_INDEX_INTLEN_DEFAULT );
+ break;
+
+ case CFG_ACL:
+ if ( c->valx < 0 ) {
+ acl_destroy( c->be->be_acl );
+ c->be->be_acl = NULL;
+
+ } else {
+ AccessControl **prev, *a;
+ int i;
+ for (i=0, prev = &c->be->be_acl; i < c->valx;
+ i++ ) {
+ a = *prev;
+ prev = &a->acl_next;
+ }
+ a = *prev;
+ *prev = a->acl_next;
+ acl_free( a );
+ }
+ if ( SLAP_CONFIG( c->be ) && !c->be->be_acl ) {
+ Debug( LDAP_DEBUG_CONFIG, "config_generic (CFG_ACL): "
+ "Last explicit ACL for back-config removed. "
+ "Using hardcoded default\n" );
+ c->be->be_acl = defacl_parsed;
+ }
+ break;
+
+ case CFG_OC: {
+ CfEntryInfo *ce;
+ /* Can be NULL when undoing a failed add */
+ if ( c->ca_entry ) {
+ ce = c->ca_entry->e_private;
+ /* can't modify the hardcoded schema */
+ if ( ce->ce_parent->ce_type == Cft_Global )
+ return 1;
+ }
+ }
+ cfn = c->ca_private;
+ if ( c->valx < 0 ) {
+ ObjectClass *oc;
+
+ for( oc = cfn->c_oc_head; oc; oc_next( &oc )) {
+ oc_delete( oc );
+ if ( oc == cfn->c_oc_tail )
+ break;
+ }
+ cfn->c_oc_head = cfn->c_oc_tail = NULL;
+ } else {
+ ObjectClass *oc, *prev = NULL;
+
+ for ( i=0, oc=cfn->c_oc_head; i<c->valx; i++) {
+ prev = oc;
+ oc_next( &oc );
+ }
+ oc_delete( oc );
+ if ( cfn->c_oc_tail == oc ) {
+ cfn->c_oc_tail = prev;
+ }
+ if ( cfn->c_oc_head == oc ) {
+ oc_next( &oc );
+ cfn->c_oc_head = oc;
+ }
+ }
+ break;
+
+ case CFG_ATTR: {
+ CfEntryInfo *ce;
+ /* Can be NULL when undoing a failed add */
+ if ( c->ca_entry ) {
+ ce = c->ca_entry->e_private;
+ /* can't modify the hardcoded schema */
+ if ( ce->ce_parent->ce_type == Cft_Global )
+ return 1;
+ }
+ }
+ cfn = c->ca_private;
+ if ( c->valx < 0 ) {
+ AttributeType *at;
+
+ for( at = cfn->c_at_head; at; at_next( &at )) {
+ at_delete( at );
+ if ( at == cfn->c_at_tail )
+ break;
+ }
+ cfn->c_at_head = cfn->c_at_tail = NULL;
+ } else {
+ AttributeType *at, *prev = NULL;
+
+ for ( i=0, at=cfn->c_at_head; i<c->valx; i++) {
+ prev = at;
+ at_next( &at );
+ }
+ at_delete( at );
+ if ( cfn->c_at_tail == at ) {
+ cfn->c_at_tail = prev;
+ }
+ if ( cfn->c_at_head == at ) {
+ at_next( &at );
+ cfn->c_at_head = at;
+ }
+ }
+ break;
+
+ case CFG_SYNTAX: {
+ CfEntryInfo *ce;
+ /* Can be NULL when undoing a failed add */
+ if ( c->ca_entry ) {
+ ce = c->ca_entry->e_private;
+ /* can't modify the hardcoded schema */
+ if ( ce->ce_parent->ce_type == Cft_Global )
+ return 1;
+ }
+ }
+ cfn = c->ca_private;
+ if ( c->valx < 0 ) {
+ Syntax *syn;
+
+ for( syn = cfn->c_syn_head; syn; syn_next( &syn )) {
+ syn_delete( syn );
+ if ( syn == cfn->c_syn_tail )
+ break;
+ }
+ cfn->c_syn_head = cfn->c_syn_tail = NULL;
+ } else {
+ Syntax *syn, *prev = NULL;
+
+ for ( i = 0, syn = cfn->c_syn_head; i < c->valx; i++) {
+ prev = syn;
+ syn_next( &syn );
+ }
+ syn_delete( syn );
+ if ( cfn->c_syn_tail == syn ) {
+ cfn->c_syn_tail = prev;
+ }
+ if ( cfn->c_syn_head == syn ) {
+ syn_next( &syn );
+ cfn->c_syn_head = syn;
+ }
+ }
+ break;
+ case CFG_SORTVALS:
+ if ( c->valx < 0 ) {
+ ADlist *sv;
+ for ( sv = sortVals; sv; sv = sortVals ) {
+ sortVals = sv->al_next;
+ sv->al_desc->ad_type->sat_flags &= ~SLAP_AT_SORTED_VAL;
+ ch_free( sv );
+ }
+ } else {
+ ADlist *sv, **prev;
+ int i = 0;
+
+ for ( prev = &sortVals, sv = sortVals; i < c->valx; i++ ) {
+ prev = &sv->al_next;
+ sv = sv->al_next;
+ }
+ sv->al_desc->ad_type->sat_flags &= ~SLAP_AT_SORTED_VAL;
+ *prev = sv->al_next;
+ ch_free( sv );
+ }
+ break;
+
+ case CFG_LIMITS:
+ /* FIXME: there is no limits_free function */
+ if ( c->valx < 0 ) {
+ limits_destroy( c->be->be_limits );
+ c->be->be_limits = NULL;
+
+ } else {
+ int cnt, num = -1;
+
+ if ( c->be->be_limits ) {
+ for ( num = 0; c->be->be_limits[ num ]; num++ )
+ /* just count */ ;
+ }
+
+ if ( c->valx >= num ) {
+ return 1;
+ }
+
+ if ( num == 1 ) {
+ limits_destroy( c->be->be_limits );
+ c->be->be_limits = NULL;
+
+ } else {
+ limits_free_one( c->be->be_limits[ c->valx ] );
+
+ for ( cnt = c->valx; cnt < num; cnt++ ) {
+ c->be->be_limits[ cnt ] = c->be->be_limits[ cnt + 1 ];
+ }
+ }
+ }
+ break;
+
+ case CFG_ATOPT:
+ /* FIXME: there is no ad_option_free function */
+ case CFG_ROOTDSE:
+ /* FIXME: there is no way to remove attributes added by
+ a DSE file */
+ case CFG_OID:
+ case CFG_DIT:
+ case CFG_MODPATH:
+ default:
+ rc = 1;
+ break;
+ }
+ return rc;
+ }
+
+ switch(c->type) {
+ case CFG_BACKEND:
+ if(!(c->bi = backend_info(c->argv[1]))) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> failed init", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s (%s)!\n",
+ c->log, c->cr_msg, c->argv[1] );
+ return(1);
+ }
+ if ( c->bi->bi_flags & SLAP_BFLAG_STANDALONE ) {
+ c->bi->bi_nDB++;
+ nbackends++;
+ }
+ c->be = NULL;
+ break;
+
+ case CFG_DATABASE:
+ c->bi = NULL;
+ /* NOTE: config is always the first backend!
+ */
+ if ( !strcasecmp( c->argv[1], "config" )) {
+ if (config_unique_db & GOT_CONFIG) {
+ sprintf( c->cr_msg, "config DB already defined");
+ return(1);
+ }
+ c->be = LDAP_STAILQ_FIRST(&backendDB);
+ config_unique_db |= GOT_CONFIG;
+ } else if ( !strcasecmp( c->argv[1], "frontend" )) {
+ if (config_unique_db & GOT_FRONTEND) {
+ sprintf( c->cr_msg, "frontend DB already defined");
+ return(1);
+ }
+ c->be = frontendDB;
+ config_unique_db |= GOT_FRONTEND;
+ } else {
+ c->be = backend_db_init(c->argv[1], NULL, c->valx, &c->reply);
+ if ( !c->be ) {
+ if ( c->cr_msg[0] == 0 )
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> failed init", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s (%s)\n", c->log, c->cr_msg, c->argv[1] );
+ return(1);
+ }
+ }
+ break;
+
+ case CFG_CONCUR:
+ ldap_pvt_thread_set_concurrency(c->value_int);
+ break;
+
+ case CFG_THREADS:
+ if ( c->value_int < 2 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "threads=%d smaller than minimum value 2",
+ c->value_int );
+ Debug(LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+
+ } else if ( c->value_int > 2 * SLAP_MAX_WORKER_THREADS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "warning, threads=%d larger than twice the default (2*%d=%d); YMMV",
+ c->value_int, SLAP_MAX_WORKER_THREADS, 2 * SLAP_MAX_WORKER_THREADS );
+ Debug(LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ }
+ if ( slapMode & SLAP_SERVER_MODE )
+ ldap_pvt_thread_pool_maxthreads(&connection_pool, c->value_int);
+ connection_pool_max = c->value_int; /* save for reference */
+ break;
+
+ case CFG_THREADQS:
+ if ( c->value_int < 1 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "threadqueues=%d smaller than minimum value 1",
+ c->value_int );
+ Debug(LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+ if ( slapMode & SLAP_SERVER_MODE )
+ ldap_pvt_thread_pool_queues(&connection_pool, c->value_int);
+ connection_pool_queues = c->value_int; /* save for reference */
+ break;
+
+ case CFG_TTHREADS:
+ if ( slapMode & SLAP_TOOL_MODE )
+ ldap_pvt_thread_pool_maxthreads(&connection_pool, c->value_int);
+ slap_tool_thread_max = c->value_int; /* save for reference */
+ break;
+
+ case CFG_LTHREADS:
+ if ( c->value_uint < 1 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "listenerthreads=%u smaller than minimum value 1",
+ c->value_uint );
+ Debug(LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+ { int mask = 0;
+ /* use a power of two */
+ while (c->value_uint > 1) {
+ c->value_uint >>= 1;
+ mask <<= 1;
+ mask |= 1;
+ }
+ new_daemon_threads = mask+1;
+ config_push_cleanup( c, config_resize_lthreads );
+ }
+ break;
+
+ case CFG_SALT:
+ if ( passwd_salt ) ch_free( passwd_salt );
+ passwd_salt = c->value_string;
+ lutil_salt_format(passwd_salt);
+ break;
+
+ case CFG_LIMITS:
+ if(limits_parse(c->be, c->fname, c->lineno, c->argc, c->argv))
+ return(1);
+ break;
+
+ case CFG_RO:
+ if(c->value_int)
+ c->be->be_restrictops |= SLAP_RESTRICT_READONLY;
+ else
+ c->be->be_restrictops &= ~SLAP_RESTRICT_READONLY;
+ break;
+
+ case CFG_AZPOLICY:
+ ch_free(c->value_string);
+ if (slap_sasl_setpolicy( c->argv[1] )) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unable to parse value", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
+ c->log, c->cr_msg, c->argv[1] );
+ return(1);
+ }
+ break;
+
+ case CFG_AZREGEXP:
+ if (slap_sasl_regexp_config( c->argv[1], c->argv[2], c->valx ))
+ return(1);
+ break;
+
+#ifdef HAVE_CYRUS_SASL
+#ifdef SLAP_AUXPROP_DONTUSECOPY
+ case CFG_AZDUC: {
+ int arg, cnt;
+
+ for ( arg = 1; arg < c->argc; arg++ ) {
+ int duplicate = 0, err;
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+
+ err = slap_str2ad( c->argv[ arg ], &ad, &text );
+ if ( err != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s>: attr #%d (\"%s\") unknown (err=%d \"%s\"; ignored)",
+ c->argv[0], arg, c->argv[ arg ], err, text );
+ Debug(LDAP_DEBUG_ANY, "%s: %s\n",
+ c->log, c->cr_msg );
+
+ } else {
+ if ( slap_dontUseCopy_propnames != NULL ) {
+ for ( cnt = 0; !BER_BVISNULL( &slap_dontUseCopy_propnames[ cnt ] ); cnt++ ) {
+ if ( bvmatch( &slap_dontUseCopy_propnames[ cnt ], &ad->ad_cname ) ) {
+ duplicate = 1;
+ break;
+ }
+ }
+ }
+
+ if ( duplicate ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s>: attr #%d (\"%s\") already defined (ignored)",
+ c->argv[0], arg, ad->ad_cname.bv_val);
+ Debug(LDAP_DEBUG_ANY, "%s: %s\n",
+ c->log, c->cr_msg );
+ continue;
+ }
+
+ value_add_one( &slap_dontUseCopy_propnames, &ad->ad_cname );
+ }
+ }
+
+ } break;
+#endif /* SLAP_AUXPROP_DONTUSECOPY */
+
+ case CFG_SASLSECP:
+ {
+ char *txt = slap_sasl_secprops( c->argv[1] );
+ if ( txt ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "<%s> %s",
+ c->argv[0], txt );
+ Debug(LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
+ return(1);
+ }
+ break;
+ }
+#endif
+
+ case CFG_DEPTH:
+ c->be->be_max_deref_depth = c->value_int;
+ break;
+
+ case CFG_OID: {
+ OidMacro *om;
+
+ if ( c->op == LDAP_MOD_ADD && c->ca_private && cfn != c->ca_private )
+ cfn = c->ca_private;
+ if(parse_oidm(c, 1, &om))
+ return(1);
+ if (!cfn->c_om_head) cfn->c_om_head = om;
+ cfn->c_om_tail = om;
+ }
+ break;
+
+ case CFG_OC: {
+ ObjectClass *oc, *prev;
+
+ if ( c->op == LDAP_MOD_ADD && c->ca_private && cfn != c->ca_private )
+ cfn = c->ca_private;
+ if ( c->valx < 0 ) {
+ prev = cfn->c_oc_tail;
+ } else {
+ prev = NULL;
+ /* If adding anything after the first, prev is easy */
+ if ( c->valx ) {
+ int i;
+ for (i=0, oc = cfn->c_oc_head; i<c->valx; i++) {
+ prev = oc;
+ if ( !oc_next( &oc ))
+ break;
+ }
+ } else
+ /* If adding the first, and head exists, find its prev */
+ if (cfn->c_oc_head) {
+ for ( oc_start( &oc ); oc != cfn->c_oc_head; ) {
+ prev = oc;
+ oc_next( &oc );
+ }
+ }
+ /* else prev is NULL, append to end of global list */
+ }
+ if(parse_oc(c, &oc, prev)) return(1);
+ if (!cfn->c_oc_head || !c->valx) cfn->c_oc_head = oc;
+ if (cfn->c_oc_tail == prev) cfn->c_oc_tail = oc;
+ }
+ break;
+
+ case CFG_ATTR: {
+ AttributeType *at, *prev;
+
+ if ( c->op == LDAP_MOD_ADD && c->ca_private && cfn != c->ca_private )
+ cfn = c->ca_private;
+ if ( c->valx < 0 ) {
+ prev = cfn->c_at_tail;
+ } else {
+ prev = NULL;
+ /* If adding anything after the first, prev is easy */
+ if ( c->valx ) {
+ int i;
+ for (i=0, at = cfn->c_at_head; i<c->valx; i++) {
+ prev = at;
+ if ( !at_next( &at ))
+ break;
+ }
+ } else
+ /* If adding the first, and head exists, find its prev */
+ if (cfn->c_at_head) {
+ for ( at_start( &at ); at != cfn->c_at_head; ) {
+ prev = at;
+ at_next( &at );
+ }
+ }
+ /* else prev is NULL, append to end of global list */
+ }
+ if(parse_at(c, &at, prev)) return(1);
+ if (!cfn->c_at_head || !c->valx) cfn->c_at_head = at;
+ if (cfn->c_at_tail == prev) cfn->c_at_tail = at;
+ }
+ break;
+
+ case CFG_SYNTAX: {
+ Syntax *syn, *prev;
+
+ if ( c->op == LDAP_MOD_ADD && c->ca_private && cfn != c->ca_private )
+ cfn = c->ca_private;
+ if ( c->valx < 0 ) {
+ prev = cfn->c_syn_tail;
+ } else {
+ prev = NULL;
+ /* If adding anything after the first, prev is easy */
+ if ( c->valx ) {
+ int i;
+ for ( i = 0, syn = cfn->c_syn_head; i < c->valx; i++ ) {
+ prev = syn;
+ if ( !syn_next( &syn ))
+ break;
+ }
+ } else
+ /* If adding the first, and head exists, find its prev */
+ if (cfn->c_syn_head) {
+ for ( syn_start( &syn ); syn != cfn->c_syn_head; ) {
+ prev = syn;
+ syn_next( &syn );
+ }
+ }
+ /* else prev is NULL, append to end of global list */
+ }
+ if ( parse_syn( c, &syn, prev ) ) return(1);
+ if ( !cfn->c_syn_head || !c->valx ) cfn->c_syn_head = syn;
+ if ( cfn->c_syn_tail == prev ) cfn->c_syn_tail = syn;
+ }
+ break;
+
+ case CFG_DIT: {
+ ContentRule *cr;
+
+ if ( c->op == LDAP_MOD_ADD && c->ca_private && cfn != c->ca_private )
+ cfn = c->ca_private;
+ if(parse_cr(c, &cr)) return(1);
+ if (!cfn->c_cr_head) cfn->c_cr_head = cr;
+ cfn->c_cr_tail = cr;
+ }
+ break;
+
+ case CFG_ATOPT:
+ ad_define_option(NULL, NULL, 0);
+ for(i = 1; i < c->argc; i++)
+ if(ad_define_option(c->argv[i], c->fname, c->lineno))
+ return(1);
+ break;
+
+ case CFG_IX_HASH64:
+ if ( slap_hash64( c->value_int != 0 ))
+ return 1;
+ break;
+
+ case CFG_IX_INTLEN:
+ if ( c->value_int < SLAP_INDEX_INTLEN_DEFAULT )
+ c->value_int = SLAP_INDEX_INTLEN_DEFAULT;
+ else if ( c->value_int > 255 )
+ c->value_int = 255;
+ index_intlen = c->value_int;
+ index_intlen_strlen = SLAP_INDEX_INTLEN_STRLEN(
+ index_intlen );
+ break;
+
+ case CFG_SORTVALS: {
+ ADlist *svnew = NULL, *svtail, *sv;
+
+ for ( i = 1; i < c->argc; i++ ) {
+ AttributeDescription *ad = NULL;
+ const char *text;
+ int rc;
+
+ rc = slap_str2ad( c->argv[i], &ad, &text );
+ if ( rc ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unknown attribute type #%d",
+ c->argv[0], i );
+sortval_reject:
+ Debug(LDAP_DEBUG_ANY, "%s: %s %s\n",
+ c->log, c->cr_msg, c->argv[i] );
+ for ( sv = svnew; sv; sv = svnew ) {
+ svnew = sv->al_next;
+ ch_free( sv );
+ }
+ return 1;
+ }
+ if (( ad->ad_type->sat_flags & SLAP_AT_ORDERED ) ||
+ ad->ad_type->sat_single_value ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> inappropriate attribute type #%d",
+ c->argv[0], i );
+ goto sortval_reject;
+ }
+ sv = ch_malloc( sizeof( ADlist ));
+ sv->al_desc = ad;
+ if ( !svnew ) {
+ svnew = sv;
+ } else {
+ svtail->al_next = sv;
+ }
+ svtail = sv;
+ }
+ sv->al_next = NULL;
+ for ( sv = svnew; sv; sv = sv->al_next )
+ sv->al_desc->ad_type->sat_flags |= SLAP_AT_SORTED_VAL;
+ for ( sv = sortVals; sv && sv->al_next; sv = sv->al_next );
+ if ( sv )
+ sv->al_next = svnew;
+ else
+ sortVals = svnew;
+ }
+ break;
+
+ case CFG_ACL:
+ if ( SLAP_CONFIG( c->be ) && c->be->be_acl == defacl_parsed) {
+ c->be->be_acl = NULL;
+ }
+ /* Don't append to the global ACL if we're on a specific DB */
+ i = c->valx;
+ if ( c->valx == -1 ) {
+ AccessControl *a;
+ i = 0;
+ for ( a=c->be->be_acl; a; a = a->acl_next )
+ i++;
+ }
+ if ( parse_acl(c->be, c->fname, c->lineno, c->argc, c->argv, i ) ) {
+ if ( SLAP_CONFIG( c->be ) && !c->be->be_acl) {
+ c->be->be_acl = defacl_parsed;
+ }
+ return 1;
+ }
+ break;
+
+ case CFG_ACL_ADD:
+ if(c->value_int)
+ SLAP_DBFLAGS(c->be) |= SLAP_DBFLAG_ACL_ADD;
+ else
+ SLAP_DBFLAGS(c->be) &= ~SLAP_DBFLAG_ACL_ADD;
+ break;
+
+ case CFG_ROOTDSE:
+ if(root_dse_read_file(c->argv[1])) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> could not read file", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s %s\n",
+ c->log, c->cr_msg, c->argv[1] );
+ return(1);
+ }
+ {
+ struct berval bv;
+ ber_str2bv( c->argv[1], 0, 1, &bv );
+ if ( c->op == LDAP_MOD_ADD && c->ca_private && cfn != c->ca_private )
+ cfn = c->ca_private;
+ ber_bvarray_add( &cfn->c_dseFiles, &bv );
+ }
+ break;
+
+ case CFG_SERVERID:
+ {
+ ServerID *si, **sip;
+ LDAPURLDesc *lud;
+ int num;
+ if (( lutil_atoi( &num, c->argv[1] ) &&
+ lutil_atoix( &num, c->argv[1], 16 )) ||
+ num < 0 || num > SLAP_SYNC_SID_MAX )
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "<%s> illegal server ID", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s %s\n",
+ c->log, c->cr_msg, c->argv[1] );
+ return 1;
+ }
+ /* only one value allowed if no URL is given */
+ if ( c->argc > 2 ) {
+ int len;
+
+ if ( sid_list && BER_BVISEMPTY( &sid_list->si_url )) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "<%s> only one server ID allowed now", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s %s\n",
+ c->log, c->cr_msg, c->argv[1] );
+ return 1;
+ }
+
+ if ( ldap_url_parse( c->argv[2], &lud )) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "<%s> invalid URL", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s %s\n",
+ c->log, c->cr_msg, c->argv[2] );
+ return 1;
+ }
+ len = strlen( c->argv[2] );
+ si = ch_malloc( sizeof(ServerID) + len + 1 );
+ si->si_url.bv_val = (char *)(si+1);
+ si->si_url.bv_len = len;
+ strcpy( si->si_url.bv_val, c->argv[2] );
+ } else {
+ if ( sid_list ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "<%s> unqualified server ID not allowed now", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s %s\n",
+ c->log, c->cr_msg, c->argv[1] );
+ return 1;
+ }
+ si = ch_malloc( sizeof(ServerID) );
+ BER_BVZERO( &si->si_url );
+ slap_serverID = num;
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: SID=0x%03x\n",
+ c->log, slap_serverID );
+ sid_set = si;
+ }
+ si->si_next = NULL;
+ si->si_num = num;
+ for ( sip = &sid_list; *sip; sip = &(*sip)->si_next );
+ *sip = si;
+
+ if (( slapMode & SLAP_SERVER_MODE ) && c->argc > 2 ) {
+ Listener *l = config_check_my_url( c->argv[2], lud );
+ if ( l ) {
+ if ( sid_set ) {
+ ldap_free_urldesc( lud );
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "<%s> multiple server ID URLs matched, only one is allowed", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s %s\n",
+ c->log, c->cr_msg, c->argv[1] );
+ return 1;
+ }
+ slap_serverID = si->si_num;
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: SID=0x%03x (listener=%s)\n",
+ c->log, slap_serverID,
+ l->sl_url.bv_val );
+ sid_set = si;
+ }
+ }
+ if ( c->argc > 2 )
+ ldap_free_urldesc( lud );
+ }
+ break;
+ case CFG_LOGFILE: {
+ if ( logfileName ) ch_free( logfileName );
+ logfileName = c->value_string;
+ logfile = fopen(logfileName, "w");
+ if(logfile) lutil_debug_file(logfile);
+ } break;
+
+ case CFG_LASTMOD:
+ if(SLAP_NOLASTMODCMD(c->be)) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> not available for %s database",
+ c->argv[0], c->be->bd_info->bi_type );
+ Debug(LDAP_DEBUG_ANY, "%s: %s\n",
+ c->log, c->cr_msg );
+ return(1);
+ }
+ if(c->value_int)
+ SLAP_DBFLAGS(c->be) &= ~SLAP_DBFLAG_NOLASTMOD;
+ else
+ SLAP_DBFLAGS(c->be) |= SLAP_DBFLAG_NOLASTMOD;
+ break;
+
+ case CFG_LASTBIND:
+ if (c->value_int)
+ SLAP_DBFLAGS(c->be) |= SLAP_DBFLAG_LASTBIND;
+ else
+ SLAP_DBFLAGS(c->be) &= ~SLAP_DBFLAG_LASTBIND;
+ break;
+
+ case CFG_MULTIPROVIDER:
+ if(c->value_int && !SLAP_SHADOW(c->be)) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> database is not a shadow",
+ c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s\n",
+ c->log, c->cr_msg );
+ return(1);
+ }
+ if(c->value_int) {
+ SLAP_DBFLAGS(c->be) &= ~SLAP_DBFLAG_SINGLE_SHADOW;
+ SLAP_DBFLAGS(c->be) |= SLAP_DBFLAG_MULTI_SHADOW;
+ } else {
+ SLAP_DBFLAGS(c->be) |= SLAP_DBFLAG_SINGLE_SHADOW;
+ SLAP_DBFLAGS(c->be) &= ~SLAP_DBFLAG_MULTI_SHADOW;
+ }
+ break;
+
+ case CFG_MONITORING:
+ if(c->value_int)
+ SLAP_DBFLAGS(c->be) |= SLAP_DBFLAG_MONITORING;
+ else
+ SLAP_DBFLAGS(c->be) &= ~SLAP_DBFLAG_MONITORING;
+ break;
+
+ case CFG_DISABLED:
+ if ( c->bi ) {
+ if (c->value_int) {
+ if ( c->bi->bi_db_close ) {
+ BackendInfo *bi_orig = c->be->bd_info;
+ c->be->bd_info = c->bi;
+ c->bi->bi_db_close( c->be, &c->reply );
+ c->be->bd_info = bi_orig;
+ }
+ c->bi->bi_flags |= SLAPO_BFLAG_DISABLED;
+ } else {
+ c->bi->bi_flags &= ~SLAPO_BFLAG_DISABLED;
+ }
+ } else {
+ if (c->value_int) {
+ backend_shutdown( c->be );
+ SLAP_DBFLAGS(c->be) |= SLAP_DBFLAG_DISABLED;
+ } else {
+ SLAP_DBFLAGS(c->be) &= ~SLAP_DBFLAG_DISABLED;
+ }
+ }
+ break;
+
+ case CFG_HIDDEN:
+ if (c->value_int)
+ SLAP_DBFLAGS(c->be) |= SLAP_DBFLAG_HIDDEN;
+ else
+ SLAP_DBFLAGS(c->be) &= ~SLAP_DBFLAG_HIDDEN;
+ break;
+
+ case CFG_SYNC_SUBENTRY:
+ if (c->value_int)
+ SLAP_DBFLAGS(c->be) |= SLAP_DBFLAG_SYNC_SUBENTRY;
+ else
+ SLAP_DBFLAGS(c->be) &= ~SLAP_DBFLAG_SYNC_SUBENTRY;
+ break;
+
+ case CFG_SSTR_IF_MAX:
+ if (c->value_uint < index_substr_if_minlen) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> invalid value", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s (%d)\n",
+ c->log, c->cr_msg, c->value_int );
+ return(1);
+ }
+ index_substr_if_maxlen = c->value_uint;
+ break;
+
+ case CFG_SSTR_IF_MIN:
+ if (c->value_uint > index_substr_if_maxlen) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> invalid value", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s (%d)\n",
+ c->log, c->cr_msg, c->value_int );
+ return(1);
+ }
+ index_substr_if_minlen = c->value_uint;
+ break;
+
+#ifdef SLAPD_MODULES
+ case CFG_MODLOAD:
+ /* If we're just adding a module on an existing modpath,
+ * make sure we've selected the current path.
+ */
+ if ( c->op == LDAP_MOD_ADD && c->ca_private && modcur != c->ca_private ) {
+ modcur = c->ca_private;
+ /* This should never fail */
+ if ( module_path( modcur->mp_path.bv_val )) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> module path no longer valid",
+ c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s (%s)\n",
+ c->log, c->cr_msg, modcur->mp_path.bv_val );
+ return(1);
+ }
+ }
+ if(module_load(c->argv[1], c->argc - 2, (c->argc > 2) ? c->argv + 2 : NULL))
+ return(1);
+ /* Record this load on the current path */
+ {
+ struct berval bv;
+ char *ptr;
+ if ( c->op == SLAP_CONFIG_ADD ) {
+ ptr = c->line + STRLENOF("moduleload");
+ while (!isspace((unsigned char) *ptr)) ptr++;
+ while (isspace((unsigned char) *ptr)) ptr++;
+ } else {
+ ptr = c->line;
+ }
+ ber_str2bv(ptr, 0, 1, &bv);
+ ber_bvarray_add( &modcur->mp_loads, &bv );
+ }
+ /* Check for any new hardcoded schema */
+ if ( c->op == LDAP_MOD_ADD && CONFIG_ONLINE_ADD( c )) {
+ config_check_schema( NULL, &cfBackInfo );
+ }
+ break;
+
+ case CFG_MODPATH:
+ if(module_path(c->argv[1])) return(1);
+ /* Record which path was used with each module */
+ {
+ ModPaths *mp;
+
+ if (!modpaths.mp_loads) {
+ mp = &modpaths;
+ } else {
+ mp = ch_malloc( sizeof( ModPaths ));
+ modlast->mp_next = mp;
+ }
+ ber_str2bv(c->argv[1], 0, 1, &mp->mp_path);
+ mp->mp_next = NULL;
+ mp->mp_loads = NULL;
+ modlast = mp;
+ c->ca_private = mp;
+ modcur = mp;
+ }
+
+ break;
+#endif
+
+#ifdef LDAP_SLAPI
+ case CFG_PLUGIN:
+ if(slapi_int_read_config(c->be, c->fname, c->lineno, c->argc, c->argv, c->valx) != LDAP_SUCCESS)
+ return(1);
+ slapi_plugins_used++;
+ break;
+#endif
+
+ case CFG_REWRITE: {
+ int rc;
+
+ if ( c->op == LDAP_MOD_ADD ) {
+ c->argv++;
+ c->argc--;
+ }
+ rc = slap_sasl_rewrite_config(c->fname, c->lineno, c->argc, c->argv, c->valx);
+ if ( c->op == LDAP_MOD_ADD ) {
+ c->argv--;
+ c->argc++;
+ }
+ return rc;
+ }
+
+
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "%s: unknown CFG_TYPE %d.\n",
+ c->log, c->type );
+ return 1;
+
+ }
+ return(0);
+}
+
+
+static int
+config_fname(ConfigArgs *c) {
+ if(c->op == SLAP_CONFIG_EMIT) {
+ if (c->ca_private) {
+ ConfigFile *cf = c->ca_private;
+ value_add_one( &c->rvalue_vals, &cf->c_file );
+ return 0;
+ }
+ return 1;
+ }
+ return(0);
+}
+
+static int
+config_cfdir(ConfigArgs *c) {
+ if(c->op == SLAP_CONFIG_EMIT) {
+ if ( !BER_BVISEMPTY( &cfdir )) {
+ value_add_one( &c->rvalue_vals, &cfdir );
+ return 0;
+ }
+ return 1;
+ }
+ return(0);
+}
+
+static int
+config_search_base(ConfigArgs *c) {
+ if(c->op == SLAP_CONFIG_EMIT) {
+ int rc = 1;
+ if (!BER_BVISEMPTY(&default_search_base)) {
+ value_add_one(&c->rvalue_vals, &default_search_base);
+ value_add_one(&c->rvalue_nvals, &default_search_nbase);
+ rc = 0;
+ }
+ return rc;
+ } else if( c->op == LDAP_MOD_DELETE ) {
+ ch_free( default_search_base.bv_val );
+ ch_free( default_search_nbase.bv_val );
+ BER_BVZERO( &default_search_base );
+ BER_BVZERO( &default_search_nbase );
+ return 0;
+ }
+
+ if(c->bi || c->be != frontendDB) {
+ Debug(LDAP_DEBUG_ANY, "%s: defaultSearchBase line must appear "
+ "prior to any backend or database definition\n",
+ c->log );
+ return(1);
+ }
+
+ if(default_search_nbase.bv_len) {
+ free(default_search_base.bv_val);
+ free(default_search_nbase.bv_val);
+ }
+
+ default_search_base = c->value_dn;
+ default_search_nbase = c->value_ndn;
+ return(0);
+}
+
+/* For RE23 compatibility we allow this in the global entry
+ * but we now defer it to the frontend entry to allow modules
+ * to load new hash types.
+ */
+static int
+config_passwd_hash(ConfigArgs *c) {
+ int i;
+ if (c->op == SLAP_CONFIG_EMIT) {
+ struct berval bv;
+ /* Don't generate it in the global entry */
+ if ( c->table == Cft_Global )
+ return 1;
+ for (i=0; default_passwd_hash && default_passwd_hash[i]; i++) {
+ ber_str2bv(default_passwd_hash[i], 0, 0, &bv);
+ value_add_one(&c->rvalue_vals, &bv);
+ }
+ return i ? 0 : 1;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ /* Deleting from global is a no-op, only the frontendDB entry matters */
+ if ( c->table == Cft_Global )
+ return 0;
+ if ( c->valx < 0 ) {
+ ldap_charray_free( default_passwd_hash );
+ default_passwd_hash = NULL;
+ } else {
+ i = c->valx;
+ ch_free( default_passwd_hash[i] );
+ for (; default_passwd_hash[i]; i++ )
+ default_passwd_hash[i] = default_passwd_hash[i+1];
+ }
+ return 0;
+ }
+ for(i = 1; i < c->argc; i++) {
+ if(!lutil_passwd_scheme(c->argv[i])) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> scheme not available", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s (%s)\n",
+ c->log, c->cr_msg, c->argv[i]);
+ } else {
+ ldap_charray_add(&default_passwd_hash, c->argv[i]);
+ }
+ }
+ if(!default_passwd_hash) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> no valid hashes found", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s\n",
+ c->log, c->cr_msg );
+ return(1);
+ }
+ return(0);
+}
+
+static int
+config_schema_dn(ConfigArgs *c) {
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ int rc = 1;
+ if ( !BER_BVISEMPTY( &c->be->be_schemadn )) {
+ value_add_one(&c->rvalue_vals, &c->be->be_schemadn);
+ value_add_one(&c->rvalue_nvals, &c->be->be_schemandn);
+ rc = 0;
+ }
+ return rc;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ ch_free( c->be->be_schemadn.bv_val );
+ ch_free( c->be->be_schemandn.bv_val );
+ BER_BVZERO( &c->be->be_schemadn );
+ BER_BVZERO( &c->be->be_schemandn );
+ return 0;
+ }
+ ch_free( c->be->be_schemadn.bv_val );
+ ch_free( c->be->be_schemandn.bv_val );
+ c->be->be_schemadn = c->value_dn;
+ c->be->be_schemandn = c->value_ndn;
+ return(0);
+}
+
+static int
+config_sizelimit(ConfigArgs *c) {
+ int i, rc = 0;
+ struct slap_limits_set *lim = &c->be->be_def_limit;
+ if (c->op == SLAP_CONFIG_EMIT) {
+ char buf[8192];
+ struct berval bv;
+ bv.bv_val = buf;
+ bv.bv_len = 0;
+ limits_unparse_one( lim, SLAP_LIMIT_SIZE, &bv, sizeof( buf ) );
+ if ( !BER_BVISEMPTY( &bv ))
+ value_add_one( &c->rvalue_vals, &bv );
+ else
+ rc = 1;
+ return rc;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ /* Reset to defaults or values from frontend */
+ if ( c->be == frontendDB ) {
+ lim->lms_s_soft = SLAPD_DEFAULT_SIZELIMIT;
+ lim->lms_s_hard = 0;
+ lim->lms_s_unchecked = -1;
+ lim->lms_s_pr = 0;
+ lim->lms_s_pr_hide = 0;
+ lim->lms_s_pr_total = 0;
+ } else {
+ lim->lms_s_soft = frontendDB->be_def_limit.lms_s_soft;
+ lim->lms_s_hard = frontendDB->be_def_limit.lms_s_hard;
+ lim->lms_s_unchecked = frontendDB->be_def_limit.lms_s_unchecked;
+ lim->lms_s_pr = frontendDB->be_def_limit.lms_s_pr;
+ lim->lms_s_pr_hide = frontendDB->be_def_limit.lms_s_pr_hide;
+ lim->lms_s_pr_total = frontendDB->be_def_limit.lms_s_pr_total;
+ }
+ goto ok;
+ }
+ for(i = 1; i < c->argc; i++) {
+ if(!strncasecmp(c->argv[i], "size", 4)) {
+ rc = limits_parse_one(c->argv[i], lim);
+ if ( rc ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unable to parse value", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
+ c->log, c->cr_msg, c->argv[i]);
+ return(1);
+ }
+ } else {
+ if(!strcasecmp(c->argv[i], "unlimited")) {
+ lim->lms_s_soft = -1;
+ } else {
+ if ( lutil_atoix( &lim->lms_s_soft, c->argv[i], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unable to parse limit", c->argv[0]);
+ Debug(LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
+ c->log, c->cr_msg, c->argv[i]);
+ return(1);
+ }
+ }
+ lim->lms_s_hard = 0;
+ }
+ }
+
+ok:
+ if ( ( c->be == frontendDB ) && ( c->ca_entry ) ) {
+ /* This is a modification to the global limits apply it to
+ * the other databases as needed */
+ AttributeDescription *ad=NULL;
+ const char *text = NULL;
+ CfEntryInfo *ce = c->ca_entry->e_private;
+
+ slap_str2ad(c->argv[0], &ad, &text);
+ /* if we got here... */
+ assert( ad != NULL );
+
+ if ( ce->ce_type == Cft_Global ){
+ ce = ce->ce_kids;
+ }
+ for (; ce; ce=ce->ce_sibs) {
+ Entry *dbe = ce->ce_entry;
+ if ( (ce->ce_type == Cft_Database) && (ce->ce_be != frontendDB)
+ && (!attr_find(dbe->e_attrs, ad)) ) {
+ ce->ce_be->be_def_limit.lms_s_soft = lim->lms_s_soft;
+ ce->ce_be->be_def_limit.lms_s_hard = lim->lms_s_hard;
+ ce->ce_be->be_def_limit.lms_s_unchecked =lim->lms_s_unchecked;
+ ce->ce_be->be_def_limit.lms_s_pr =lim->lms_s_pr;
+ ce->ce_be->be_def_limit.lms_s_pr_hide =lim->lms_s_pr_hide;
+ ce->ce_be->be_def_limit.lms_s_pr_total =lim->lms_s_pr_total;
+ }
+ }
+ }
+ return(0);
+}
+
+static int
+config_timelimit(ConfigArgs *c) {
+ int i, rc = 0;
+ struct slap_limits_set *lim = &c->be->be_def_limit;
+ if (c->op == SLAP_CONFIG_EMIT) {
+ char buf[8192];
+ struct berval bv;
+ bv.bv_val = buf;
+ bv.bv_len = 0;
+ limits_unparse_one( lim, SLAP_LIMIT_TIME, &bv, sizeof( buf ) );
+ if ( !BER_BVISEMPTY( &bv ))
+ value_add_one( &c->rvalue_vals, &bv );
+ else
+ rc = 1;
+ return rc;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ /* Reset to defaults or values from frontend */
+ if ( c->be == frontendDB ) {
+ lim->lms_t_soft = SLAPD_DEFAULT_TIMELIMIT;
+ lim->lms_t_hard = 0;
+ } else {
+ lim->lms_t_soft = frontendDB->be_def_limit.lms_t_soft;
+ lim->lms_t_hard = frontendDB->be_def_limit.lms_t_hard;
+ }
+ goto ok;
+ }
+ for(i = 1; i < c->argc; i++) {
+ if(!strncasecmp(c->argv[i], "time", 4)) {
+ rc = limits_parse_one(c->argv[i], lim);
+ if ( rc ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unable to parse value", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
+ c->log, c->cr_msg, c->argv[i]);
+ return(1);
+ }
+ } else {
+ if(!strcasecmp(c->argv[i], "unlimited")) {
+ lim->lms_t_soft = -1;
+ } else {
+ if ( lutil_atoix( &lim->lms_t_soft, c->argv[i], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unable to parse limit", c->argv[0]);
+ Debug(LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
+ c->log, c->cr_msg, c->argv[i]);
+ return(1);
+ }
+ }
+ lim->lms_t_hard = 0;
+ }
+ }
+
+ok:
+ if ( ( c->be == frontendDB ) && ( c->ca_entry ) ) {
+ /* This is a modification to the global limits apply it to
+ * the other databases as needed */
+ AttributeDescription *ad=NULL;
+ const char *text = NULL;
+ CfEntryInfo *ce = c->ca_entry->e_private;
+
+ slap_str2ad(c->argv[0], &ad, &text);
+ /* if we got here... */
+ assert( ad != NULL );
+
+ if ( ce->ce_type == Cft_Global ){
+ ce = ce->ce_kids;
+ }
+ for (; ce; ce=ce->ce_sibs) {
+ Entry *dbe = ce->ce_entry;
+ if ( (ce->ce_type == Cft_Database) && (ce->ce_be != frontendDB)
+ && (!attr_find(dbe->e_attrs, ad)) ) {
+ ce->ce_be->be_def_limit.lms_t_soft = lim->lms_t_soft;
+ ce->ce_be->be_def_limit.lms_t_hard = lim->lms_t_hard;
+ }
+ }
+ }
+ return(0);
+}
+
+static int
+config_overlay(ConfigArgs *c) {
+ if (c->op == SLAP_CONFIG_EMIT) {
+ return 1;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ assert(0);
+ }
+ if(c->argv[1][0] == '-' && overlay_config(c->be, &c->argv[1][1],
+ c->valx, &c->bi, &c->reply)) {
+ /* log error */
+ Debug( LDAP_DEBUG_ANY,
+ "%s: (optional) %s overlay \"%s\" configuration failed.\n",
+ c->log, c->be == frontendDB ? "global " : "", &c->argv[1][1]);
+ return 1;
+ } else if(overlay_config(c->be, c->argv[1], c->valx, &c->bi, &c->reply)) {
+ return(1);
+ }
+ return(0);
+}
+
+static int
+config_subordinate(ConfigArgs *c)
+{
+ int rc = 1;
+ int advertise = 0;
+
+ switch( c->op ) {
+ case SLAP_CONFIG_EMIT:
+ if ( SLAP_GLUE_SUBORDINATE( c->be )) {
+ struct berval bv;
+
+ bv.bv_val = SLAP_GLUE_ADVERTISE( c->be ) ? "advertise" : "TRUE";
+ bv.bv_len = SLAP_GLUE_ADVERTISE( c->be ) ? STRLENOF("advertise") :
+ STRLENOF("TRUE");
+
+ value_add_one( &c->rvalue_vals, &bv );
+ rc = 0;
+ }
+ break;
+ case LDAP_MOD_DELETE:
+ if ( !c->line || strcasecmp( c->line, "advertise" )) {
+ glue_sub_del( c->be );
+ } else {
+ SLAP_DBFLAGS( c->be ) &= ~SLAP_DBFLAG_GLUE_ADVERTISE;
+ }
+ rc = 0;
+ break;
+ case LDAP_MOD_ADD:
+ case SLAP_CONFIG_ADD:
+ if ( c->be->be_nsuffix == NULL ) {
+ /* log error */
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "subordinate configuration needs a suffix" );
+ Debug( LDAP_DEBUG_ANY,
+ "%s: %s.\n",
+ c->log, c->cr_msg );
+ rc = 1;
+ break;
+ }
+
+ if ( c->argc == 2 ) {
+ if ( strcasecmp( c->argv[1], "advertise" ) == 0 ) {
+ advertise = 1;
+
+ } else if ( strcasecmp( c->argv[1], "TRUE" ) != 0 ) {
+ /* log error */
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "subordinate must be \"TRUE\" or \"advertise\"" );
+ Debug( LDAP_DEBUG_ANY,
+ "%s: suffix \"%s\": %s.\n",
+ c->log, c->be->be_suffix[0].bv_val, c->cr_msg );
+ rc = 1;
+ break;
+ }
+ }
+
+ rc = glue_sub_add( c->be, advertise, CONFIG_ONLINE_ADD( c ));
+ break;
+ }
+
+ return rc;
+}
+
+/*
+ * [listener=<listener>] [{read|write}=]<size>
+ */
+
+#ifdef LDAP_TCP_BUFFER
+static BerVarray tcp_buffer;
+int tcp_buffer_num;
+
+#define SLAP_TCP_RMEM (0x1U)
+#define SLAP_TCP_WMEM (0x2U)
+
+static int
+tcp_buffer_parse( struct berval *val, int argc, char **argv,
+ int *size, int *rw, Listener **l )
+{
+ int i, rc = LDAP_SUCCESS;
+ LDAPURLDesc *lud = NULL;
+ char *ptr;
+
+ if ( val != NULL && argv == NULL ) {
+ char *s = val->bv_val;
+
+ argv = ldap_str2charray( s, " \t" );
+ if ( argv == NULL ) {
+ return LDAP_OTHER;
+ }
+ }
+
+ i = 0;
+ if ( strncasecmp( argv[ i ], "listener=", STRLENOF( "listener=" ) )
+ == 0 )
+ {
+ char *url = argv[ i ] + STRLENOF( "listener=" );
+
+ if ( ldap_url_parse( url, &lud ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ *l = config_check_my_url( url, lud );
+ if ( *l == NULL ) {
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ goto done;
+ }
+
+ i++;
+ }
+
+ ptr = argv[ i ];
+ if ( strncasecmp( ptr, "read=", STRLENOF( "read=" ) ) == 0 ) {
+ *rw |= SLAP_TCP_RMEM;
+ ptr += STRLENOF( "read=" );
+
+ } else if ( strncasecmp( ptr, "write=", STRLENOF( "write=" ) ) == 0 ) {
+ *rw |= SLAP_TCP_WMEM;
+ ptr += STRLENOF( "write=" );
+
+ } else {
+ *rw |= ( SLAP_TCP_RMEM | SLAP_TCP_WMEM );
+ }
+
+ /* accept any base */
+ if ( lutil_atoix( size, ptr, 0 ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+done:;
+ if ( val != NULL && argv != NULL ) {
+ ldap_charray_free( argv );
+ }
+
+ if ( lud != NULL ) {
+ ldap_free_urldesc( lud );
+ }
+
+ return rc;
+}
+
+static int
+tcp_buffer_delete_one( struct berval *val )
+{
+ int rc = 0;
+ int size = -1, rw = 0;
+ Listener *l = NULL;
+
+ rc = tcp_buffer_parse( val, 0, NULL, &size, &rw, &l );
+ if ( rc != 0 ) {
+ return rc;
+ }
+
+ if ( l != NULL ) {
+ int i;
+ Listener **ll = slapd_get_listeners();
+
+ for ( i = 0; ll[ i ] != NULL; i++ ) {
+ if ( ll[ i ] == l ) break;
+ }
+
+ if ( ll[ i ] == NULL ) {
+ return LDAP_NO_SUCH_ATTRIBUTE;
+ }
+
+ if ( rw & SLAP_TCP_RMEM ) l->sl_tcp_rmem = -1;
+ if ( rw & SLAP_TCP_WMEM ) l->sl_tcp_wmem = -1;
+
+ for ( i++ ; ll[ i ] != NULL && bvmatch( &l->sl_url, &ll[ i ]->sl_url ); i++ ) {
+ if ( rw & SLAP_TCP_RMEM ) ll[ i ]->sl_tcp_rmem = -1;
+ if ( rw & SLAP_TCP_WMEM ) ll[ i ]->sl_tcp_wmem = -1;
+ }
+
+ } else {
+ /* NOTE: this affects listeners without a specific setting,
+ * does not reset all listeners. If a listener without
+ * specific settings was assigned a buffer because of
+ * a global setting, it will not be reset. In any case,
+ * buffer changes will only take place at restart. */
+ if ( rw & SLAP_TCP_RMEM ) slapd_tcp_rmem = -1;
+ if ( rw & SLAP_TCP_WMEM ) slapd_tcp_wmem = -1;
+ }
+
+ return rc;
+}
+
+static int
+tcp_buffer_delete( BerVarray vals )
+{
+ int i;
+
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
+ tcp_buffer_delete_one( &vals[ i ] );
+ }
+
+ return 0;
+}
+
+static int
+tcp_buffer_unparse( int size, int rw, Listener *l, struct berval *val )
+{
+ char buf[sizeof("2147483648")], *ptr;
+
+ /* unparse for later use */
+ val->bv_len = snprintf( buf, sizeof( buf ), "%d", size );
+ if ( l != NULL ) {
+ val->bv_len += STRLENOF( "listener=" " " ) + l->sl_url.bv_len;
+ }
+
+ if ( rw != ( SLAP_TCP_RMEM | SLAP_TCP_WMEM ) ) {
+ if ( rw & SLAP_TCP_RMEM ) {
+ val->bv_len += STRLENOF( "read=" );
+ } else if ( rw & SLAP_TCP_WMEM ) {
+ val->bv_len += STRLENOF( "write=" );
+ }
+ }
+
+ val->bv_val = ch_malloc( val->bv_len + 1 );
+
+ ptr = val->bv_val;
+
+ if ( l != NULL ) {
+ ptr = lutil_strcopy( ptr, "listener=" );
+ ptr = lutil_strncopy( ptr, l->sl_url.bv_val, l->sl_url.bv_len );
+ *ptr++ = ' ';
+ }
+
+ if ( rw != ( SLAP_TCP_RMEM | SLAP_TCP_WMEM ) ) {
+ if ( rw & SLAP_TCP_RMEM ) {
+ ptr = lutil_strcopy( ptr, "read=" );
+ } else if ( rw & SLAP_TCP_WMEM ) {
+ ptr = lutil_strcopy( ptr, "write=" );
+ }
+ }
+
+ ptr = lutil_strcopy( ptr, buf );
+ *ptr = '\0';
+
+ assert( val->bv_val + val->bv_len == ptr );
+
+ return LDAP_SUCCESS;
+}
+
+static int
+tcp_buffer_add_one( int argc, char **argv )
+{
+ int rc = 0;
+ int size = -1, rw = 0;
+ Listener *l = NULL;
+
+ struct berval val;
+
+ /* parse */
+ rc = tcp_buffer_parse( NULL, argc, argv, &size, &rw, &l );
+ if ( rc != 0 ) {
+ return rc;
+ }
+
+ /* unparse for later use */
+ rc = tcp_buffer_unparse( size, rw, l, &val );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ /* use parsed values */
+ if ( l != NULL ) {
+ int i;
+ Listener **ll = slapd_get_listeners();
+
+ for ( i = 0; ll[ i ] != NULL; i++ ) {
+ if ( ll[ i ] == l ) break;
+ }
+
+ if ( ll[ i ] == NULL ) {
+ return LDAP_NO_SUCH_ATTRIBUTE;
+ }
+
+ /* buffer only applies to TCP listeners;
+ * we do not do any check here, and delegate them
+ * to setsockopt(2) */
+ if ( rw & SLAP_TCP_RMEM ) l->sl_tcp_rmem = size;
+ if ( rw & SLAP_TCP_WMEM ) l->sl_tcp_wmem = size;
+
+ for ( i++ ; ll[ i ] != NULL && bvmatch( &l->sl_url, &ll[ i ]->sl_url ); i++ ) {
+ if ( rw & SLAP_TCP_RMEM ) ll[ i ]->sl_tcp_rmem = size;
+ if ( rw & SLAP_TCP_WMEM ) ll[ i ]->sl_tcp_wmem = size;
+ }
+
+ } else {
+ /* NOTE: this affects listeners without a specific setting,
+ * does not set all listeners */
+ if ( rw & SLAP_TCP_RMEM ) slapd_tcp_rmem = size;
+ if ( rw & SLAP_TCP_WMEM ) slapd_tcp_wmem = size;
+ }
+
+ tcp_buffer = ch_realloc( tcp_buffer, sizeof( struct berval ) * ( tcp_buffer_num + 2 ) );
+ /* append */
+ tcp_buffer[ tcp_buffer_num ] = val;
+
+ tcp_buffer_num++;
+ BER_BVZERO( &tcp_buffer[ tcp_buffer_num ] );
+
+ return rc;
+}
+
+static int
+config_tcp_buffer( ConfigArgs *c )
+{
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ if ( tcp_buffer == NULL || BER_BVISNULL( &tcp_buffer[ 0 ] ) ) {
+ return 1;
+ }
+ value_add( &c->rvalue_vals, tcp_buffer );
+ value_add( &c->rvalue_nvals, tcp_buffer );
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ if ( !c->line ) {
+ tcp_buffer_delete( tcp_buffer );
+ ber_bvarray_free( tcp_buffer );
+ tcp_buffer = NULL;
+ tcp_buffer_num = 0;
+
+ } else {
+ int rc = 0;
+ int size = -1, rw = 0;
+ Listener *l = NULL;
+
+ struct berval val = BER_BVNULL;
+
+ int i;
+
+ if ( tcp_buffer_num == 0 ) {
+ return 1;
+ }
+
+ /* parse */
+ rc = tcp_buffer_parse( NULL, c->argc - 1, &c->argv[ 1 ], &size, &rw, &l );
+ if ( rc != 0 ) {
+ return 1;
+ }
+
+ /* unparse for later use */
+ rc = tcp_buffer_unparse( size, rw, l, &val );
+ if ( rc != LDAP_SUCCESS ) {
+ return 1;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &tcp_buffer[ i ] ); i++ ) {
+ if ( bvmatch( &tcp_buffer[ i ], &val ) ) {
+ break;
+ }
+ }
+
+ if ( BER_BVISNULL( &tcp_buffer[ i ] ) ) {
+ /* not found */
+ rc = 1;
+ goto done;
+ }
+
+ tcp_buffer_delete_one( &tcp_buffer[ i ] );
+ ber_memfree( tcp_buffer[ i ].bv_val );
+ for ( ; i < tcp_buffer_num; i++ ) {
+ tcp_buffer[ i ] = tcp_buffer[ i + 1 ];
+ }
+ tcp_buffer_num--;
+
+done:;
+ if ( !BER_BVISNULL( &val ) ) {
+ SLAP_FREE( val.bv_val );
+ }
+
+ }
+
+ } else {
+ int rc;
+
+ rc = tcp_buffer_add_one( c->argc - 1, &c->argv[ 1 ] );
+ if ( rc ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "<%s> unable to add value #%d",
+ c->argv[0], tcp_buffer_num );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+ }
+
+ return 0;
+}
+#endif /* LDAP_TCP_BUFFER */
+
+static int
+config_suffix(ConfigArgs *c)
+{
+ Backend *tbe;
+ struct berval pdn, ndn;
+ char *notallowed = NULL;
+
+ if ( c->be == frontendDB ) {
+ notallowed = "frontend";
+
+ } else if ( SLAP_MONITOR(c->be) ) {
+ notallowed = "monitor";
+
+ } else if ( SLAP_CONFIG(c->be) ) {
+ notallowed = "config";
+ }
+
+ if ( notallowed != NULL ) {
+ char buf[ SLAP_TEXT_BUFLEN ] = { '\0' };
+
+ switch ( c->op ) {
+ case LDAP_MOD_ADD:
+ case LDAP_MOD_DELETE:
+ case LDAP_MOD_REPLACE:
+ case LDAP_MOD_INCREMENT:
+ case SLAP_CONFIG_ADD:
+ if ( !BER_BVISNULL( &c->value_dn ) ) {
+ snprintf( buf, sizeof( buf ), "<%s> ",
+ c->value_dn.bv_val );
+ }
+
+ Debug(LDAP_DEBUG_ANY,
+ "%s: suffix %snot allowed in %s database.\n",
+ c->log, buf, notallowed );
+ break;
+
+ case SLAP_CONFIG_EMIT:
+ /* don't complain when emitting... */
+ break;
+
+ default:
+ /* FIXME: don't know what values may be valid;
+ * please remove assertion, or add legal values
+ * to either block */
+ assert( 0 );
+ break;
+ }
+
+ return 1;
+ }
+
+ if (c->op == SLAP_CONFIG_EMIT) {
+ if ( c->be->be_suffix == NULL
+ || BER_BVISNULL( &c->be->be_suffix[0] ) )
+ {
+ return 1;
+ } else {
+ value_add( &c->rvalue_vals, c->be->be_suffix );
+ value_add( &c->rvalue_nvals, c->be->be_nsuffix );
+ return 0;
+ }
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ if ( c->valx < 0 ) {
+ ber_bvarray_free( c->be->be_suffix );
+ ber_bvarray_free( c->be->be_nsuffix );
+ c->be->be_suffix = NULL;
+ c->be->be_nsuffix = NULL;
+ } else {
+ int i = c->valx;
+ ch_free( c->be->be_suffix[i].bv_val );
+ ch_free( c->be->be_nsuffix[i].bv_val );
+ do {
+ c->be->be_suffix[i] = c->be->be_suffix[i+1];
+ c->be->be_nsuffix[i] = c->be->be_nsuffix[i+1];
+ i++;
+ } while ( !BER_BVISNULL( &c->be->be_suffix[i] ) );
+ }
+ return 0;
+ }
+
+#ifdef SLAPD_MONITOR_DN
+ if(!strcasecmp(c->argv[1], SLAPD_MONITOR_DN)) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> DN is reserved for monitoring slapd",
+ c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s (%s)\n",
+ c->log, c->cr_msg, SLAPD_MONITOR_DN);
+ return(1);
+ }
+#endif
+
+ if (SLAP_DB_ONE_SUFFIX( c->be ) && c->be->be_suffix &&
+ !BER_BVISNULL( &c->be->be_suffix[0] )) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> Only one suffix is allowed on this %s backend",
+ c->argv[0], c->be->bd_info->bi_type );
+ Debug(LDAP_DEBUG_ANY, "%s: %s\n",
+ c->log, c->cr_msg );
+ return(1);
+ }
+
+ pdn = c->value_dn;
+ ndn = c->value_ndn;
+
+ if (SLAP_DBHIDDEN( c->be ))
+ tbe = NULL;
+ else
+ tbe = select_backend(&ndn, 0);
+ if(tbe == c->be) {
+ Debug( LDAP_DEBUG_ANY, "%s: suffix already served by this backend!.\n",
+ c->log );
+ free(pdn.bv_val);
+ free(ndn.bv_val);
+ return 1;
+ } else if(tbe) {
+ BackendDB *b2 = tbe;
+
+ /* Does tbe precede be? */
+ while (( b2 = LDAP_STAILQ_NEXT(b2, be_next )) && b2 && b2 != c->be );
+
+ if ( b2 ) {
+ char *type = tbe->bd_info->bi_type;
+
+ if ( overlay_is_over( tbe ) ) {
+ slap_overinfo *oi = (slap_overinfo *)tbe->bd_info->bi_private;
+ type = oi->oi_orig->bi_type;
+ }
+
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> namingContext \"%s\" "
+ "already served by a preceding %s database",
+ c->argv[0], pdn.bv_val, type );
+ Debug(LDAP_DEBUG_ANY, "%s: %s serving namingContext \"%s\"\n",
+ c->log, c->cr_msg, tbe->be_suffix[0].bv_val);
+ free(pdn.bv_val);
+ free(ndn.bv_val);
+ return(1);
+ }
+ }
+ if(pdn.bv_len == 0 && default_search_nbase.bv_len) {
+ Debug(LDAP_DEBUG_ANY, "%s: suffix DN empty and default search "
+ "base provided \"%s\" (assuming okay)\n",
+ c->log, default_search_base.bv_val );
+ }
+ ber_bvarray_add(&c->be->be_suffix, &pdn);
+ ber_bvarray_add(&c->be->be_nsuffix, &ndn);
+ return(0);
+}
+
+static int
+config_rootdn(ConfigArgs *c) {
+ if (c->op == SLAP_CONFIG_EMIT) {
+ if ( !BER_BVISNULL( &c->be->be_rootdn )) {
+ value_add_one(&c->rvalue_vals, &c->be->be_rootdn);
+ value_add_one(&c->rvalue_nvals, &c->be->be_rootndn);
+ return 0;
+ } else {
+ return 1;
+ }
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ ch_free( c->be->be_rootdn.bv_val );
+ ch_free( c->be->be_rootndn.bv_val );
+ BER_BVZERO( &c->be->be_rootdn );
+ BER_BVZERO( &c->be->be_rootndn );
+ return 0;
+ }
+ if ( !BER_BVISNULL( &c->be->be_rootdn )) {
+ ch_free( c->be->be_rootdn.bv_val );
+ ch_free( c->be->be_rootndn.bv_val );
+ }
+ c->be->be_rootdn = c->value_dn;
+ c->be->be_rootndn = c->value_ndn;
+ return(0);
+}
+
+static int
+config_rootpw(ConfigArgs *c) {
+ Backend *tbe;
+
+ if (c->op == SLAP_CONFIG_EMIT) {
+ if (!BER_BVISEMPTY(&c->be->be_rootpw)) {
+ /* don't copy, because "rootpw" is marked
+ * as CFG_BERVAL */
+ c->value_bv = c->be->be_rootpw;
+ return 0;
+ }
+ return 1;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ ch_free( c->be->be_rootpw.bv_val );
+ BER_BVZERO( &c->be->be_rootpw );
+ return 0;
+ }
+
+ tbe = select_backend(&c->be->be_rootndn, 0);
+ if(tbe != c->be && !SLAP_DBHIDDEN( c->be )) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> can only be set when rootdn is under suffix",
+ c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s\n",
+ c->log, c->cr_msg );
+ return(1);
+ }
+ if ( !BER_BVISNULL( &c->be->be_rootpw ))
+ ch_free( c->be->be_rootpw.bv_val );
+ c->be->be_rootpw = c->value_bv;
+ return(0);
+}
+
+static int
+config_restrict(ConfigArgs *c) {
+ slap_mask_t restrictops = 0;
+ int i;
+ slap_verbmasks restrictable_ops[] = {
+ { BER_BVC("bind"), SLAP_RESTRICT_OP_BIND },
+ { BER_BVC("add"), SLAP_RESTRICT_OP_ADD },
+ { BER_BVC("modify"), SLAP_RESTRICT_OP_MODIFY },
+ { BER_BVC("rename"), SLAP_RESTRICT_OP_RENAME },
+ { BER_BVC("modrdn"), 0 },
+ { BER_BVC("delete"), SLAP_RESTRICT_OP_DELETE },
+ { BER_BVC("search"), SLAP_RESTRICT_OP_SEARCH },
+ { BER_BVC("compare"), SLAP_RESTRICT_OP_COMPARE },
+ { BER_BVC("read"), SLAP_RESTRICT_OP_READS },
+ { BER_BVC("write"), SLAP_RESTRICT_OP_WRITES },
+ { BER_BVC("extended"), SLAP_RESTRICT_OP_EXTENDED },
+ { BER_BVC("extended=" LDAP_EXOP_START_TLS ), SLAP_RESTRICT_EXOP_START_TLS },
+ { BER_BVC("extended=" LDAP_EXOP_MODIFY_PASSWD ), SLAP_RESTRICT_EXOP_MODIFY_PASSWD },
+ { BER_BVC("extended=" LDAP_EXOP_X_WHO_AM_I ), SLAP_RESTRICT_EXOP_WHOAMI },
+ { BER_BVC("extended=" LDAP_EXOP_X_CANCEL ), SLAP_RESTRICT_EXOP_CANCEL },
+ { BER_BVC("all"), SLAP_RESTRICT_OP_ALL },
+ { BER_BVNULL, 0 }
+ };
+
+ if (c->op == SLAP_CONFIG_EMIT) {
+ return mask_to_verbs( restrictable_ops, c->be->be_restrictops,
+ &c->rvalue_vals );
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ if ( !c->line ) {
+ c->be->be_restrictops = 0;
+ } else {
+ i = verb_to_mask( c->line, restrictable_ops );
+ c->be->be_restrictops &= ~restrictable_ops[i].mask;
+ }
+ return 0;
+ }
+ i = verbs_to_mask( c->argc, c->argv, restrictable_ops, &restrictops );
+ if ( i ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unknown operation", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s %s\n",
+ c->log, c->cr_msg, c->argv[i]);
+ return(1);
+ }
+ if ( restrictops & SLAP_RESTRICT_OP_EXTENDED )
+ restrictops &= ~SLAP_RESTRICT_EXOP_MASK;
+ c->be->be_restrictops |= restrictops;
+ return(0);
+}
+
+static int
+config_allows(ConfigArgs *c) {
+ slap_mask_t allows = 0;
+ int i;
+ slap_verbmasks allowable_ops[] = {
+ { BER_BVC("bind_v2"), SLAP_ALLOW_BIND_V2 },
+ { BER_BVC("bind_anon_cred"), SLAP_ALLOW_BIND_ANON_CRED },
+ { BER_BVC("bind_anon_dn"), SLAP_ALLOW_BIND_ANON_DN },
+ { BER_BVC("update_anon"), SLAP_ALLOW_UPDATE_ANON },
+ { BER_BVC("proxy_authz_anon"), SLAP_ALLOW_PROXY_AUTHZ_ANON },
+ { BER_BVNULL, 0 }
+ };
+ if (c->op == SLAP_CONFIG_EMIT) {
+ return mask_to_verbs( allowable_ops, global_allows, &c->rvalue_vals );
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ if ( !c->line ) {
+ global_allows = 0;
+ } else {
+ i = verb_to_mask( c->line, allowable_ops );
+ global_allows &= ~allowable_ops[i].mask;
+ }
+ return 0;
+ }
+ i = verbs_to_mask(c->argc, c->argv, allowable_ops, &allows);
+ if ( i ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unknown feature", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s %s\n",
+ c->log, c->cr_msg, c->argv[i]);
+ return(1);
+ }
+ global_allows |= allows;
+ return(0);
+}
+
+static int
+config_disallows(ConfigArgs *c) {
+ slap_mask_t disallows = 0;
+ int i;
+ slap_verbmasks disallowable_ops[] = {
+ { BER_BVC("bind_anon"), SLAP_DISALLOW_BIND_ANON },
+ { BER_BVC("bind_simple"), SLAP_DISALLOW_BIND_SIMPLE },
+ { BER_BVC("tls_2_anon"), SLAP_DISALLOW_TLS_2_ANON },
+ { BER_BVC("tls_authc"), SLAP_DISALLOW_TLS_AUTHC },
+ { BER_BVC("proxy_authz_non_critical"), SLAP_DISALLOW_PROXY_AUTHZ_N_CRIT },
+ { BER_BVC("dontusecopy_non_critical"), SLAP_DISALLOW_DONTUSECOPY_N_CRIT },
+ { BER_BVNULL, 0 }
+ };
+ if (c->op == SLAP_CONFIG_EMIT) {
+ return mask_to_verbs( disallowable_ops, global_disallows, &c->rvalue_vals );
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ if ( !c->line ) {
+ global_disallows = 0;
+ } else {
+ i = verb_to_mask( c->line, disallowable_ops );
+ global_disallows &= ~disallowable_ops[i].mask;
+ }
+ return 0;
+ }
+ i = verbs_to_mask(c->argc, c->argv, disallowable_ops, &disallows);
+ if ( i ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unknown feature", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s %s\n",
+ c->log, c->cr_msg, c->argv[i]);
+ return(1);
+ }
+ global_disallows |= disallows;
+ return(0);
+}
+
+static int
+config_requires(ConfigArgs *c) {
+ slap_mask_t requires = frontendDB->be_requires;
+ int i, argc = c->argc;
+ char **argv = c->argv;
+
+ slap_verbmasks requires_ops[] = {
+ { BER_BVC("bind"), SLAP_REQUIRE_BIND },
+ { BER_BVC("LDAPv3"), SLAP_REQUIRE_LDAP_V3 },
+ { BER_BVC("authc"), SLAP_REQUIRE_AUTHC },
+ { BER_BVC("sasl"), SLAP_REQUIRE_SASL },
+ { BER_BVC("strong"), SLAP_REQUIRE_STRONG },
+ { BER_BVNULL, 0 }
+ };
+ if (c->op == SLAP_CONFIG_EMIT) {
+ return mask_to_verbs( requires_ops, c->be->be_requires, &c->rvalue_vals );
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ if ( !c->line ) {
+ c->be->be_requires = 0;
+ } else {
+ i = verb_to_mask( c->line, requires_ops );
+ c->be->be_requires &= ~requires_ops[i].mask;
+ }
+ return 0;
+ }
+ /* "none" can only be first, to wipe out default/global values */
+ if ( strcasecmp( c->argv[ 1 ], "none" ) == 0 ) {
+ argv++;
+ argc--;
+ requires = 0;
+ }
+ i = verbs_to_mask(argc, argv, requires_ops, &requires);
+ if ( i ) {
+ if (strcasecmp( c->argv[ i ], "none" ) == 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> \"none\" (#%d) must be listed first", c->argv[0], i - 1 );
+ Debug(LDAP_DEBUG_ANY, "%s: %s\n",
+ c->log, c->cr_msg );
+ } else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unknown feature #%d", c->argv[0], i - 1 );
+ Debug(LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
+ c->log, c->cr_msg, c->argv[i]);
+ }
+ return(1);
+ }
+ c->be->be_requires = requires;
+ return(0);
+}
+
+static int
+config_extra_attrs(ConfigArgs *c)
+{
+ assert( c->be != NULL );
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ int i;
+
+ if ( c->be->be_extra_anlist == NULL ) {
+ return 1;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &c->be->be_extra_anlist[i].an_name ); i++ ) {
+ value_add_one( &c->rvalue_vals, &c->be->be_extra_anlist[i].an_name );
+ }
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ if ( c->be->be_extra_anlist == NULL ) {
+ return 1;
+ }
+
+ if ( c->valx < 0 ) {
+ anlist_free( c->be->be_extra_anlist, 1, NULL );
+ c->be->be_extra_anlist = NULL;
+
+ } else {
+ int i;
+
+ for ( i = 0; i < c->valx && !BER_BVISNULL( &c->be->be_extra_anlist[i + 1].an_name ); i++ )
+ ;
+
+ if ( BER_BVISNULL( &c->be->be_extra_anlist[i].an_name ) ) {
+ return 1;
+ }
+
+ ch_free( c->be->be_extra_anlist[i].an_name.bv_val );
+
+ for ( ; !BER_BVISNULL( &c->be->be_extra_anlist[i].an_name ); i++ ) {
+ c->be->be_extra_anlist[i] = c->be->be_extra_anlist[i + 1];
+ }
+ }
+
+ } else {
+ c->be->be_extra_anlist = str2anlist( c->be->be_extra_anlist, c->argv[1], " ,\t" );
+ if ( c->be->be_extra_anlist == NULL ) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static slap_verbmasks *loglevel_ops;
+
+static int
+loglevel_init( void )
+{
+ slap_verbmasks lo[] = {
+ { BER_BVC("Any"), (slap_mask_t) LDAP_DEBUG_ANY },
+ { BER_BVC("Trace"), LDAP_DEBUG_TRACE },
+ { BER_BVC("Packets"), LDAP_DEBUG_PACKETS },
+ { BER_BVC("Args"), LDAP_DEBUG_ARGS },
+ { BER_BVC("Conns"), LDAP_DEBUG_CONNS },
+ { BER_BVC("BER"), LDAP_DEBUG_BER },
+ { BER_BVC("Filter"), LDAP_DEBUG_FILTER },
+ { BER_BVC("Config"), LDAP_DEBUG_CONFIG },
+ { BER_BVC("ACL"), LDAP_DEBUG_ACL },
+ { BER_BVC("Stats"), LDAP_DEBUG_STATS },
+ { BER_BVC("Stats2"), LDAP_DEBUG_STATS2 },
+ { BER_BVC("Shell"), LDAP_DEBUG_SHELL },
+ { BER_BVC("Parse"), LDAP_DEBUG_PARSE },
+#if 0 /* no longer used (nor supported) */
+ { BER_BVC("Cache"), LDAP_DEBUG_CACHE },
+ { BER_BVC("Index"), LDAP_DEBUG_INDEX },
+#endif
+ { BER_BVC("Sync"), LDAP_DEBUG_SYNC },
+ { BER_BVC("None"), LDAP_DEBUG_NONE },
+ { BER_BVNULL, 0 }
+ };
+
+ return slap_verbmasks_init( &loglevel_ops, lo );
+}
+
+static void
+loglevel_destroy( void )
+{
+ if ( loglevel_ops ) {
+ (void)slap_verbmasks_destroy( loglevel_ops );
+ }
+ loglevel_ops = NULL;
+}
+
+static slap_mask_t loglevel_ignore[] = { -1, 0 };
+
+int
+slap_loglevel_register( slap_mask_t m, struct berval *s )
+{
+ int rc;
+
+ if ( loglevel_ops == NULL ) {
+ loglevel_init();
+ }
+
+ rc = slap_verbmasks_append( &loglevel_ops, m, s, loglevel_ignore );
+
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "slap_loglevel_register(%lu, \"%s\") failed\n",
+ m, s->bv_val );
+ }
+
+ return rc;
+}
+
+int
+slap_loglevel_get( struct berval *s, int *l )
+{
+ int rc;
+ slap_mask_t m, i;
+
+ if ( loglevel_ops == NULL ) {
+ loglevel_init();
+ }
+
+ for ( m = 0, i = 1; !BER_BVISNULL( &loglevel_ops[ i ].word ); i++ ) {
+ m |= loglevel_ops[ i ].mask;
+ }
+
+ for ( i = 1; m & i; i <<= 1 )
+ ;
+
+ if ( i == 0 ) {
+ return -1;
+ }
+
+ rc = slap_verbmasks_append( &loglevel_ops, i, s, loglevel_ignore );
+
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "slap_loglevel_get(%lu, \"%s\") failed\n",
+ i, s->bv_val );
+
+ } else {
+ *l = i;
+ slap_check_unknown_level( s->bv_val, i );
+ }
+
+ return rc;
+}
+
+int
+str2loglevel( const char *s, int *l )
+{
+ int i;
+
+ if ( loglevel_ops == NULL ) {
+ loglevel_init();
+ }
+
+ i = verb_to_mask( s, loglevel_ops );
+
+ if ( BER_BVISNULL( &loglevel_ops[ i ].word ) ) {
+ return -1;
+ }
+
+ *l = loglevel_ops[ i ].mask;
+
+ return 0;
+}
+
+const char *
+loglevel2str( int l )
+{
+ struct berval bv = BER_BVNULL;
+
+ loglevel2bv( l, &bv );
+
+ return bv.bv_val;
+}
+
+int
+loglevel2bv( int l, struct berval *bv )
+{
+ if ( loglevel_ops == NULL ) {
+ loglevel_init();
+ }
+
+ BER_BVZERO( bv );
+
+ return enum_to_verb( loglevel_ops, l, bv ) == -1;
+}
+
+int
+loglevel2bvarray( int l, BerVarray *bva )
+{
+ if ( loglevel_ops == NULL ) {
+ loglevel_init();
+ }
+
+ if ( l == 0 ) {
+ struct berval bv = BER_BVC("0");
+ return value_add_one( bva, &bv );
+ }
+
+ return mask_to_verbs( loglevel_ops, l, bva );
+}
+
+int
+loglevel_print( FILE *out )
+{
+ int i;
+
+ if ( loglevel_ops == NULL ) {
+ loglevel_init();
+ }
+
+ fprintf( out, "Installed log subsystems:\n\n" );
+ for ( i = 0; !BER_BVISNULL( &loglevel_ops[ i ].word ); i++ ) {
+ unsigned mask = loglevel_ops[ i ].mask & 0xffffffffUL;
+ fprintf( out,
+ (mask == ((slap_mask_t) -1 & 0xffffffffUL)
+ ? "\t%-30s (-1, 0xffffffff)\n" : "\t%-30s (%u, 0x%x)\n"),
+ loglevel_ops[ i ].word.bv_val, mask, mask );
+ }
+
+ fprintf( out, "\nNOTE: custom log subsystems may be later installed "
+ "by specific code\n\n" );
+
+ return 0;
+}
+
+static int config_syslog;
+
+static int
+config_loglevel(ConfigArgs *c) {
+ int i;
+
+ if ( loglevel_ops == NULL ) {
+ loglevel_init();
+ }
+
+ if (c->op == SLAP_CONFIG_EMIT) {
+ /* Get default or commandline slapd setting */
+ if ( ldap_syslog && !config_syslog )
+ config_syslog = ldap_syslog;
+ return loglevel2bvarray( config_syslog, &c->rvalue_vals );
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ if ( !c->line ) {
+ config_syslog = 0;
+ } else {
+ i = verb_to_mask( c->line, loglevel_ops );
+ config_syslog &= ~loglevel_ops[i].mask;
+ }
+ if ( slapMode & SLAP_SERVER_MODE ) {
+ ldap_syslog = config_syslog;
+ }
+ return 0;
+ }
+
+ for( i=1; i < c->argc; i++ ) {
+ int level;
+
+ if ( isdigit((unsigned char)c->argv[i][0]) || c->argv[i][0] == '-' ) {
+ if( lutil_atoix( &level, c->argv[i], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unable to parse level", c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
+ c->log, c->cr_msg, c->argv[i]);
+ return( 1 );
+ }
+ } else {
+ if ( str2loglevel( c->argv[i], &level ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unknown level", c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
+ c->log, c->cr_msg, c->argv[i]);
+ return( 1 );
+ }
+ }
+ /* Explicitly setting a zero clears all the levels */
+ if ( level )
+ config_syslog |= level;
+ else
+ config_syslog = 0;
+ }
+ if ( slapMode & SLAP_SERVER_MODE ) {
+ ldap_syslog = config_syslog;
+ }
+ return(0);
+}
+
+static int
+config_referral(ConfigArgs *c) {
+ struct berval val;
+ if (c->op == SLAP_CONFIG_EMIT) {
+ if ( default_referral ) {
+ value_add( &c->rvalue_vals, default_referral );
+ return 0;
+ } else {
+ return 1;
+ }
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ if ( c->valx < 0 ) {
+ ber_bvarray_free( default_referral );
+ default_referral = NULL;
+ } else {
+ int i = c->valx;
+ ch_free( default_referral[i].bv_val );
+ for (; default_referral[i].bv_val; i++ )
+ default_referral[i] = default_referral[i+1];
+ }
+ return 0;
+ }
+ if(validate_global_referral(c->argv[1])) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> invalid URL", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s (%s)\n",
+ c->log, c->cr_msg, c->argv[1]);
+ return(1);
+ }
+
+ ber_str2bv(c->argv[1], 0, 0, &val);
+ if(value_add_one(&default_referral, &val)) return(LDAP_OTHER);
+ return(0);
+}
+
+static struct {
+ struct berval key;
+ int off;
+} sec_keys[] = {
+ { BER_BVC("ssf="), offsetof(slap_ssf_set_t, sss_ssf) },
+ { BER_BVC("transport="), offsetof(slap_ssf_set_t, sss_transport) },
+ { BER_BVC("tls="), offsetof(slap_ssf_set_t, sss_tls) },
+ { BER_BVC("sasl="), offsetof(slap_ssf_set_t, sss_sasl) },
+ { BER_BVC("update_ssf="), offsetof(slap_ssf_set_t, sss_update_ssf) },
+ { BER_BVC("update_transport="), offsetof(slap_ssf_set_t, sss_update_transport) },
+ { BER_BVC("update_tls="), offsetof(slap_ssf_set_t, sss_update_tls) },
+ { BER_BVC("update_sasl="), offsetof(slap_ssf_set_t, sss_update_sasl) },
+ { BER_BVC("simple_bind="), offsetof(slap_ssf_set_t, sss_simple_bind) },
+ { BER_BVNULL, 0 }
+};
+
+static int
+config_security(ConfigArgs *c) {
+ slap_ssf_set_t *set = &c->be->be_ssf_set;
+ char *next;
+ int i, j;
+ if (c->op == SLAP_CONFIG_EMIT) {
+ char numbuf[32];
+ struct berval bv;
+ slap_ssf_t *tgt;
+ int rc = 1;
+
+ for (i=0; !BER_BVISNULL( &sec_keys[i].key ); i++) {
+ tgt = (slap_ssf_t *)((char *)set + sec_keys[i].off);
+ if ( *tgt ) {
+ rc = 0;
+ bv.bv_len = snprintf( numbuf, sizeof( numbuf ), "%u", *tgt );
+ if ( bv.bv_len >= sizeof( numbuf ) ) {
+ ber_bvarray_free_x( c->rvalue_vals, NULL );
+ c->rvalue_vals = NULL;
+ rc = 1;
+ break;
+ }
+ bv.bv_len += sec_keys[i].key.bv_len;
+ bv.bv_val = ch_malloc( bv.bv_len + 1);
+ next = lutil_strcopy( bv.bv_val, sec_keys[i].key.bv_val );
+ strcpy( next, numbuf );
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ }
+ }
+ return rc;
+ }
+ for(i = 1; i < c->argc; i++) {
+ slap_ssf_t *tgt = NULL;
+ char *src = NULL;
+ for ( j=0; !BER_BVISNULL( &sec_keys[j].key ); j++ ) {
+ if(!strncasecmp(c->argv[i], sec_keys[j].key.bv_val,
+ sec_keys[j].key.bv_len)) {
+ src = c->argv[i] + sec_keys[j].key.bv_len;
+ tgt = (slap_ssf_t *)((char *)set + sec_keys[j].off);
+ break;
+ }
+ }
+ if ( !tgt ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unknown factor", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s %s\n",
+ c->log, c->cr_msg, c->argv[i]);
+ return(1);
+ }
+
+ if ( lutil_atou( tgt, src ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unable to parse factor", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
+ c->log, c->cr_msg, c->argv[i]);
+ return(1);
+ }
+ }
+ return(0);
+}
+
+char *
+anlist_unparse( AttributeName *an, char *ptr, ber_len_t buflen ) {
+ int comma = 0;
+ char *start = ptr;
+
+ for (; !BER_BVISNULL( &an->an_name ); an++) {
+ /* if buflen == 0, assume the buffer size has been
+ * already checked otherwise */
+ if ( buflen > 0 && buflen - ( ptr - start ) < comma + an->an_name.bv_len ) return NULL;
+ if ( comma ) *ptr++ = ',';
+ ptr = lutil_strcopy( ptr, an->an_name.bv_val );
+ comma = 1;
+ }
+ return ptr;
+}
+
+int
+slap_bv_x_ordered_unparse( BerVarray in, BerVarray *out )
+{
+ int i;
+ BerVarray bva = NULL;
+ char ibuf[32], *ptr;
+ struct berval idx;
+
+ assert( in != NULL );
+
+ for ( i = 0; !BER_BVISNULL( &in[i] ); i++ )
+ /* count'em */ ;
+
+ if ( i == 0 ) {
+ return 1;
+ }
+
+ idx.bv_val = ibuf;
+
+ bva = ch_malloc( ( i + 1 ) * sizeof(struct berval) );
+ BER_BVZERO( &bva[ 0 ] );
+
+ for ( i = 0; !BER_BVISNULL( &in[i] ); i++ ) {
+ idx.bv_len = snprintf( idx.bv_val, sizeof( ibuf ), SLAP_X_ORDERED_FMT, i );
+ if ( idx.bv_len >= sizeof( ibuf ) ) {
+ ber_bvarray_free( bva );
+ return 1;
+ }
+
+ bva[i].bv_len = idx.bv_len + in[i].bv_len;
+ bva[i].bv_val = ch_malloc( bva[i].bv_len + 1 );
+ ptr = lutil_strcopy( bva[i].bv_val, ibuf );
+ ptr = lutil_strcopy( ptr, in[i].bv_val );
+ *ptr = '\0';
+ BER_BVZERO( &bva[ i + 1 ] );
+ }
+
+ *out = bva;
+ return 0;
+}
+
+static int
+config_updatedn(ConfigArgs *c) {
+ if (c->op == SLAP_CONFIG_EMIT) {
+ if (!BER_BVISEMPTY(&c->be->be_update_ndn)) {
+ value_add_one(&c->rvalue_vals, &c->be->be_update_ndn);
+ value_add_one(&c->rvalue_nvals, &c->be->be_update_ndn);
+ return 0;
+ }
+ return 1;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ ch_free( c->be->be_update_ndn.bv_val );
+ BER_BVZERO( &c->be->be_update_ndn );
+ SLAP_DBFLAGS(c->be) ^= (SLAP_DBFLAG_SHADOW | SLAP_DBFLAG_SLURP_SHADOW);
+ return 0;
+ }
+ if(SLAP_SHADOW(c->be)) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> database already shadowed", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s\n",
+ c->log, c->cr_msg );
+ return(1);
+ }
+
+ ber_memfree_x( c->value_dn.bv_val, NULL );
+ if ( !BER_BVISNULL( &c->be->be_update_ndn ) ) {
+ ber_memfree_x( c->be->be_update_ndn.bv_val, NULL );
+ }
+ c->be->be_update_ndn = c->value_ndn;
+ BER_BVZERO( &c->value_dn );
+ BER_BVZERO( &c->value_ndn );
+
+ return config_slurp_shadow( c );
+}
+
+int
+config_shadow( ConfigArgs *c, slap_mask_t flag )
+{
+ char *notallowed = NULL;
+
+ if ( c->be == frontendDB ) {
+ notallowed = "frontend";
+
+ } else if ( SLAP_MONITOR(c->be) ) {
+ notallowed = "monitor";
+ }
+
+ if ( notallowed != NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: %s database cannot be shadow.\n", c->log, notallowed );
+ return 1;
+ }
+
+ if ( SLAP_SHADOW(c->be) ) {
+ /* if already shadow, only check consistency */
+ if ( ( SLAP_DBFLAGS(c->be) & flag ) != flag ) {
+ Debug( LDAP_DEBUG_ANY, "%s: inconsistent shadow flag 0x%lx.\n",
+ c->log, flag );
+ return 1;
+ }
+
+ } else {
+ SLAP_DBFLAGS(c->be) |= (SLAP_DBFLAG_SHADOW | flag);
+ if ( !SLAP_MULTIPROVIDER( c->be ))
+ SLAP_DBFLAGS(c->be) |= SLAP_DBFLAG_SINGLE_SHADOW;
+ }
+
+ return 0;
+}
+
+static int
+config_updateref(ConfigArgs *c) {
+ struct berval val;
+ if (c->op == SLAP_CONFIG_EMIT) {
+ if ( c->be->be_update_refs ) {
+ value_add( &c->rvalue_vals, c->be->be_update_refs );
+ return 0;
+ } else {
+ return 1;
+ }
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ if ( c->valx < 0 ) {
+ ber_bvarray_free( c->be->be_update_refs );
+ c->be->be_update_refs = NULL;
+ } else {
+ int i = c->valx;
+ ch_free( c->be->be_update_refs[i].bv_val );
+ for (; c->be->be_update_refs[i].bv_val; i++)
+ c->be->be_update_refs[i] = c->be->be_update_refs[i+1];
+ }
+ return 0;
+ }
+ if(!SLAP_SHADOW(c->be) && !c->be->be_syncinfo) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> must appear after syncrepl or updatedn",
+ c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s\n",
+ c->log, c->cr_msg );
+ return(1);
+ }
+
+ if(validate_global_referral(c->argv[1])) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> invalid URL", c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s (%s)\n",
+ c->log, c->cr_msg, c->argv[1]);
+ return(1);
+ }
+ ber_str2bv(c->argv[1], 0, 0, &val);
+ if(value_add_one(&c->be->be_update_refs, &val)) return(LDAP_OTHER);
+ return(0);
+}
+
+static int
+config_obsolete(ConfigArgs *c) {
+ if (c->op == SLAP_CONFIG_EMIT)
+ return 1;
+
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> keyword is obsolete (ignored)",
+ c->argv[0] );
+ Debug(LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
+ return(0);
+}
+
+static int
+config_include(ConfigArgs *c) {
+ int savelineno = c->lineno;
+ int rc;
+ ConfigFile *cf;
+ ConfigFile *cfsave = cfn;
+ ConfigFile *cf2 = NULL;
+
+ /* Leftover from RE23. No dynamic config for include files */
+ if ( c->op == SLAP_CONFIG_EMIT || c->op == LDAP_MOD_DELETE )
+ return 1;
+
+ cf = ch_calloc( 1, sizeof(ConfigFile));
+ if ( cfn->c_kids ) {
+ for (cf2=cfn->c_kids; cf2 && cf2->c_sibs; cf2=cf2->c_sibs) ;
+ cf2->c_sibs = cf;
+ } else {
+ cfn->c_kids = cf;
+ }
+ cfn = cf;
+ ber_str2bv( c->argv[1], 0, 1, &cf->c_file );
+ rc = read_config_file(c->argv[1], c->depth + 1, c, config_back_cf_table);
+ c->lineno = savelineno - 1;
+ cfn = cfsave;
+ if ( rc ) {
+ if ( cf2 ) cf2->c_sibs = NULL;
+ else cfn->c_kids = NULL;
+ ch_free( cf->c_file.bv_val );
+ ch_free( cf );
+ } else {
+ c->ca_private = cf;
+ }
+ return(rc);
+}
+
+#ifdef HAVE_TLS
+static int
+config_tls_cleanup(ConfigArgs *c) {
+ int rc = 0;
+
+ if ( slap_tls_ld ) {
+ int opt = 1;
+
+ ldap_pvt_tls_ctx_free( slap_tls_ctx );
+ slap_tls_ctx = NULL;
+
+ /* Force new ctx to be created */
+ rc = ldap_pvt_tls_set_option( slap_tls_ld, LDAP_OPT_X_TLS_NEWCTX, &opt );
+ if( rc == 0 ) {
+ /* The ctx's refcount is bumped up here */
+ ldap_pvt_tls_get_option( slap_tls_ld, LDAP_OPT_X_TLS_CTX, &slap_tls_ctx );
+ /* This is a no-op if it's already loaded */
+ load_extop( &slap_EXOP_START_TLS, 0, starttls_extop );
+ } else {
+ if ( rc == LDAP_NOT_SUPPORTED )
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ else
+ rc = LDAP_OTHER;
+ }
+ }
+ return rc;
+}
+
+static int
+config_tls_option(ConfigArgs *c) {
+ int flag, rc;
+ int berval = 0;
+ LDAP *ld = slap_tls_ld;
+ switch(c->type) {
+ case CFG_TLS_RAND: flag = LDAP_OPT_X_TLS_RANDOM_FILE; ld = NULL; break;
+ case CFG_TLS_CIPHER: flag = LDAP_OPT_X_TLS_CIPHER_SUITE; break;
+ case CFG_TLS_CERT_FILE: flag = LDAP_OPT_X_TLS_CERTFILE; break;
+ case CFG_TLS_CERT_KEY: flag = LDAP_OPT_X_TLS_KEYFILE; break;
+ case CFG_TLS_CA_PATH: flag = LDAP_OPT_X_TLS_CACERTDIR; break;
+ case CFG_TLS_CA_FILE: flag = LDAP_OPT_X_TLS_CACERTFILE; break;
+ case CFG_TLS_DH_FILE: flag = LDAP_OPT_X_TLS_DHFILE; break;
+ case CFG_TLS_ECNAME: flag = LDAP_OPT_X_TLS_ECNAME; break;
+#ifdef HAVE_GNUTLS
+ case CFG_TLS_CRL_FILE: flag = LDAP_OPT_X_TLS_CRLFILE; break;
+#endif
+ case CFG_TLS_CACERT: flag = LDAP_OPT_X_TLS_CACERT; berval = 1; break;
+ case CFG_TLS_CERT: flag = LDAP_OPT_X_TLS_CERT; berval = 1; break;
+ case CFG_TLS_KEY: flag = LDAP_OPT_X_TLS_KEY; berval = 1; break;
+ default: Debug(LDAP_DEBUG_ANY, "%s: "
+ "unknown tls_option <0x%x>\n",
+ c->log, c->type );
+ return 1;
+ }
+ if (c->op == SLAP_CONFIG_EMIT) {
+ return ldap_pvt_tls_get_option( ld, flag, berval ? (void *)&c->value_bv : (void *)&c->value_string );
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ config_push_cleanup( c, config_tls_cleanup );
+ return ldap_pvt_tls_set_option( ld, flag, NULL );
+ }
+ if ( !berval ) ch_free(c->value_string);
+ config_push_cleanup( c, config_tls_cleanup );
+ rc = ldap_pvt_tls_set_option(ld, flag, berval ? (void *)&c->value_bv : (void *)c->argv[1]);
+ if ( berval ) ch_free(c->value_bv.bv_val);
+ return rc;
+}
+
+/* FIXME: this ought to be provided by libldap */
+static int
+config_tls_config(ConfigArgs *c) {
+ int i, flag;
+ switch(c->type) {
+ case CFG_TLS_CRLCHECK: flag = LDAP_OPT_X_TLS_CRLCHECK; break;
+ case CFG_TLS_VERIFY: flag = LDAP_OPT_X_TLS_REQUIRE_CERT; break;
+ case CFG_TLS_PROTOCOL_MIN: flag = LDAP_OPT_X_TLS_PROTOCOL_MIN; break;
+ default:
+ Debug(LDAP_DEBUG_ANY, "%s: "
+ "unknown tls_option <0x%x>\n",
+ c->log, c->type );
+ return 1;
+ }
+ if (c->op == SLAP_CONFIG_EMIT) {
+ return slap_tls_get_config( slap_tls_ld, flag, &c->value_string );
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ int i = 0;
+ config_push_cleanup( c, config_tls_cleanup );
+ return ldap_pvt_tls_set_option( slap_tls_ld, flag, &i );
+ }
+ ch_free( c->value_string );
+ config_push_cleanup( c, config_tls_cleanup );
+ if ( isdigit( (unsigned char)c->argv[1][0] ) && c->type != CFG_TLS_PROTOCOL_MIN ) {
+ if ( lutil_atoi( &i, c->argv[1] ) != 0 ) {
+ Debug(LDAP_DEBUG_ANY, "%s: "
+ "unable to parse %s \"%s\"\n",
+ c->log, c->argv[0], c->argv[1] );
+ return 1;
+ }
+ return(ldap_pvt_tls_set_option(slap_tls_ld, flag, &i));
+ } else {
+ return(ldap_pvt_tls_config(slap_tls_ld, flag, c->argv[1]));
+ }
+}
+#endif
+
+static CfEntryInfo *
+config_find_base( CfEntryInfo *root, struct berval *dn, CfEntryInfo **last )
+{
+ struct berval cdn;
+ char *c;
+
+ if ( !root ) {
+ *last = NULL;
+ return NULL;
+ }
+
+ if ( dn_match( &root->ce_entry->e_nname, dn ))
+ return root;
+
+ c = dn->bv_val+dn->bv_len;
+ for (;*c != ',';c--);
+
+ while(root) {
+ *last = root;
+ for (--c;c>dn->bv_val && *c != ',';c--);
+ cdn.bv_val = c;
+ if ( *c == ',' )
+ cdn.bv_val++;
+ cdn.bv_len = dn->bv_len - (cdn.bv_val - dn->bv_val);
+
+ root = root->ce_kids;
+
+ for (;root;root=root->ce_sibs) {
+ if ( dn_match( &root->ce_entry->e_nname, &cdn )) {
+ if ( cdn.bv_val == dn->bv_val ) {
+ return root;
+ }
+ break;
+ }
+ }
+ }
+ return root;
+}
+
+typedef struct setup_cookie {
+ CfBackInfo *cfb;
+ ConfigArgs *ca;
+ Entry *frontend;
+ Entry *config;
+ int got_frontend;
+ int got_config;
+} setup_cookie;
+
+static int
+config_ldif_resp( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_SEARCH ) {
+ setup_cookie *sc = op->o_callback->sc_private;
+ struct berval pdn;
+
+ sc->cfb->cb_got_ldif = 1;
+ /* Does the frontend exist? */
+ if ( !sc->got_frontend ) {
+ if ( !strncmp( rs->sr_entry->e_nname.bv_val,
+ "olcDatabase", STRLENOF( "olcDatabase" )))
+ {
+ if ( strncmp( rs->sr_entry->e_nname.bv_val +
+ STRLENOF( "olcDatabase" ), "={-1}frontend",
+ STRLENOF( "={-1}frontend" )))
+ {
+ struct berval rdn;
+ int i = op->o_noop;
+ sc->ca->be = frontendDB;
+ sc->ca->bi = frontendDB->bd_info;
+ frontendDB->be_cf_ocs = &CFOC_FRONTEND;
+ rdn.bv_val = sc->ca->log;
+ rdn.bv_len = snprintf(rdn.bv_val, sizeof( sc->ca->log ),
+ "%s=" SLAP_X_ORDERED_FMT "%s",
+ cfAd_database->ad_cname.bv_val, -1,
+ sc->ca->bi->bi_type);
+ op->o_noop = 1;
+ sc->frontend = config_build_entry( op, rs,
+ sc->cfb->cb_root, sc->ca, &rdn, &CFOC_DATABASE,
+ sc->ca->be->be_cf_ocs );
+ op->o_noop = i;
+ sc->got_frontend++;
+ } else {
+ sc->got_frontend++;
+ goto ok;
+ }
+ }
+ }
+
+ dnParent( &rs->sr_entry->e_nname, &pdn );
+
+ /* Does the configDB exist? */
+ if ( sc->got_frontend && !sc->got_config &&
+ !strncmp( rs->sr_entry->e_nname.bv_val,
+ "olcDatabase", STRLENOF( "olcDatabase" )) &&
+ dn_match( &config_rdn, &pdn ) )
+ {
+ if ( strncmp( rs->sr_entry->e_nname.bv_val +
+ STRLENOF( "olcDatabase" ), "={0}config",
+ STRLENOF( "={0}config" )))
+ {
+ struct berval rdn;
+ int i = op->o_noop;
+ sc->ca->be = LDAP_STAILQ_FIRST( &backendDB );
+ sc->ca->bi = sc->ca->be->bd_info;
+ rdn.bv_val = sc->ca->log;
+ rdn.bv_len = snprintf(rdn.bv_val, sizeof( sc->ca->log ),
+ "%s=" SLAP_X_ORDERED_FMT "%s",
+ cfAd_database->ad_cname.bv_val, 0,
+ sc->ca->bi->bi_type);
+ op->o_noop = 1;
+ sc->config = config_build_entry( op, rs, sc->cfb->cb_root,
+ sc->ca, &rdn, &CFOC_DATABASE, sc->ca->be->be_cf_ocs );
+ op->o_noop = i;
+ }
+ sc->got_config++;
+ }
+
+ok:
+ rs->sr_err = config_add_internal( sc->cfb, rs->sr_entry, sc->ca, NULL, NULL, NULL );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "config error processing %s: %s\n",
+ rs->sr_entry->e_name.bv_val, sc->ca->cr_msg );
+ }
+ }
+ return rs->sr_err;
+}
+
+/* Configure and read the underlying back-ldif store */
+static int
+config_setup_ldif( BackendDB *be, const char *dir, int readit ) {
+ CfBackInfo *cfb = be->be_private;
+ ConfigArgs c = {0};
+ ConfigTable *ct;
+ char *argv[3];
+ int rc = 0;
+ setup_cookie sc;
+ slap_callback cb = { NULL, config_ldif_resp, NULL, NULL };
+ Connection conn = {0};
+ OperationBuffer opbuf;
+ Operation *op;
+ SlapReply rs = {REP_RESULT};
+ Filter filter = { LDAP_FILTER_PRESENT };
+ struct berval filterstr = BER_BVC("(objectclass=*)");
+ struct stat st;
+
+ /* Is the config directory available? */
+ if ( stat( dir, &st ) < 0 ) {
+ /* No, so don't bother using the backing store.
+ * All changes will be in-memory only.
+ */
+ return 0;
+ }
+
+ cfb->cb_db.bd_info = backend_info( "ldif" );
+ if ( !cfb->cb_db.bd_info )
+ return 0; /* FIXME: eventually this will be a fatal error */
+
+ if ( backend_db_init( "ldif", &cfb->cb_db, -1, NULL ) == NULL )
+ return 1;
+
+ cfb->cb_db.be_suffix = be->be_suffix;
+ cfb->cb_db.be_nsuffix = be->be_nsuffix;
+
+ /* The suffix is always "cn=config". The underlying DB's rootdn
+ * is always the same as the suffix.
+ */
+ cfb->cb_db.be_rootdn = be->be_suffix[0];
+ cfb->cb_db.be_rootndn = be->be_nsuffix[0];
+
+ ber_str2bv( dir, 0, 1, &cfdir );
+
+ c.be = &cfb->cb_db;
+ c.fname = "slapd";
+ c.argc = 2;
+ argv[0] = "directory";
+ argv[1] = (char *)dir;
+ argv[2] = NULL;
+ c.argv = argv;
+ c.reply.err = 0;
+ c.reply.msg[0] = 0;
+ c.table = Cft_Database;
+
+ ct = config_find_keyword( c.be->be_cf_ocs->co_table, &c );
+ if ( !ct )
+ return 1;
+
+ if ( config_add_vals( ct, &c ))
+ return 1;
+
+ if ( backend_startup_one( &cfb->cb_db, &c.reply ))
+ return 1;
+
+ if ( readit ) {
+ void *thrctx = ldap_pvt_thread_pool_context();
+ int prev_DN_strict;
+
+ connection_fake_init( &conn, &opbuf, thrctx );
+ op = &opbuf.ob_op;
+
+ filter.f_desc = slap_schema.si_ad_objectClass;
+
+ op->o_tag = LDAP_REQ_SEARCH;
+
+ op->ors_filter = &filter;
+ op->ors_filterstr = filterstr;
+ op->ors_scope = LDAP_SCOPE_SUBTREE;
+
+ op->o_dn = c.be->be_rootdn;
+ op->o_ndn = c.be->be_rootndn;
+
+ op->o_req_dn = be->be_suffix[0];
+ op->o_req_ndn = be->be_nsuffix[0];
+
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_slimit = SLAP_NO_LIMIT;
+
+ op->ors_attrs = slap_anlist_all_attributes;
+ op->ors_attrsonly = 0;
+
+ op->o_callback = &cb;
+ sc.cfb = cfb;
+ sc.ca = &c;
+ cb.sc_private = &sc;
+ sc.got_frontend = 0;
+ sc.got_config = 0;
+ sc.frontend = NULL;
+ sc.config = NULL;
+
+ op->o_bd = &cfb->cb_db;
+
+ /* Allow unknown attrs in DNs */
+ prev_DN_strict = slap_DN_strict;
+ slap_DN_strict = 0;
+
+ rc = op->o_bd->be_search( op, &rs );
+
+ /* Restore normal DN validation */
+ slap_DN_strict = prev_DN_strict;
+
+ op->o_tag = LDAP_REQ_ADD;
+ if ( rc == LDAP_SUCCESS && sc.frontend ) {
+ rs_reinit( &rs, REP_RESULT );
+ op->ora_e = sc.frontend;
+ rc = op->o_bd->be_add( op, &rs );
+ }
+ if ( rc == LDAP_SUCCESS && sc.config ) {
+ rs_reinit( &rs, REP_RESULT );
+ op->ora_e = sc.config;
+ rc = op->o_bd->be_add( op, &rs );
+ }
+ ldap_pvt_thread_pool_context_reset( thrctx );
+ } else {
+ /* ITS#9016 Check directory is empty (except perhaps hidden files) */
+ DIR *dir_of_path;
+ struct dirent *entry;
+
+ dir_of_path = opendir( dir );
+ while ( (entry = readdir( dir_of_path )) != NULL ) {
+ if ( entry->d_name[0] != '.' ) {
+ Debug( LDAP_DEBUG_ANY, "config_setup_ldif: "
+ "expected directory %s to be empty!\n",
+ dir );
+ rc = LDAP_ALREADY_EXISTS;
+ break;
+ }
+ }
+ closedir( dir_of_path );
+ }
+
+ /* ITS#4194 - only use if it's present, or we're converting. */
+ if ( !readit || rc == LDAP_SUCCESS )
+ cfb->cb_use_ldif = 1;
+
+ return rc;
+}
+
+static int
+CfOc_cmp( const void *c1, const void *c2 ) {
+ const ConfigOCs *co1 = c1;
+ const ConfigOCs *co2 = c2;
+
+ return ber_bvcmp( co1->co_name, co2->co_name );
+}
+
+int
+config_register_schema(ConfigTable *ct, ConfigOCs *ocs) {
+ int i;
+
+ i = init_config_attrs( ct );
+ if ( i ) return i;
+
+ /* set up the objectclasses */
+ i = init_config_ocs( ocs );
+ if ( i ) return i;
+
+ for (i=0; ocs[i].co_def; i++) {
+ if ( ocs[i].co_oc ) {
+ ocs[i].co_name = &ocs[i].co_oc->soc_cname;
+ if ( !ocs[i].co_table )
+ ocs[i].co_table = ct;
+ ldap_avl_insert( &CfOcTree, &ocs[i], CfOc_cmp, ldap_avl_dup_error );
+ }
+ }
+ return 0;
+}
+
+int
+read_config(const char *fname, const char *dir) {
+ BackendDB *be;
+ CfBackInfo *cfb;
+ const char *cfdir, *cfname;
+ int rc;
+
+ /* Setup the config backend */
+ be = backend_db_init( "config", NULL, 0, NULL );
+ if ( !be )
+ return 1;
+
+ cfb = be->be_private;
+ be->be_dfltaccess = ACL_NONE;
+
+ /* If no .conf, or a dir was specified, setup the dir */
+ if ( !fname || dir ) {
+ if ( dir ) {
+ /* If explicitly given, check for existence */
+ struct stat st;
+
+ if ( stat( dir, &st ) < 0 ) {
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_ANY,
+ "invalid config directory %s, error %d\n",
+ dir, saved_errno );
+ return 1;
+ }
+ cfdir = dir;
+ } else {
+ cfdir = SLAPD_DEFAULT_CONFIGDIR;
+ }
+ /* if fname is defaulted, try reading .d */
+ rc = config_setup_ldif( be, cfdir, !fname );
+
+ if ( rc ) {
+ /* It may be OK if the base object doesn't exist yet. */
+ if ( rc != LDAP_NO_SUCH_OBJECT )
+ return 1;
+ /* ITS#4194: But if dir was specified and no fname,
+ * then we were supposed to read the dir. Unless we're
+ * trying to slapadd the dir...
+ */
+ if ( dir && !fname ) {
+ if ( slapMode & (SLAP_SERVER_MODE|SLAP_TOOL_READMAIN|SLAP_TOOL_READONLY))
+ return 1;
+ /* Assume it's slapadd with a config dir, let it continue */
+ rc = 0;
+ cfb->cb_got_ldif = 1;
+ cfb->cb_use_ldif = 1;
+ goto done;
+ }
+ }
+
+ /* If we read the config from back-ldif, nothing to do here */
+ if ( cfb->cb_got_ldif ) {
+ rc = 0;
+ goto done;
+ }
+ }
+
+ if ( fname )
+ cfname = fname;
+ else
+ cfname = SLAPD_DEFAULT_CONFIGFILE;
+
+ rc = read_config_file(cfname, 0, NULL, config_back_cf_table);
+
+ if ( rc == 0 )
+ ber_str2bv( cfname, 0, 1, &cfb->cb_config->c_file );
+
+done:
+ if ( rc == 0 && BER_BVISNULL( &frontendDB->be_schemadn ) ) {
+ ber_str2bv( SLAPD_SCHEMA_DN, STRLENOF( SLAPD_SCHEMA_DN ), 1,
+ &frontendDB->be_schemadn );
+ rc = dnNormalize( 0, NULL, NULL, &frontendDB->be_schemadn, &frontendDB->be_schemandn, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ANY, "read_config: "
+ "unable to normalize default schema DN \"%s\"\n",
+ frontendDB->be_schemadn.bv_val );
+ /* must not happen */
+ assert( 0 );
+ }
+ }
+ if ( rc == 0 && ( slapMode & SLAP_SERVER_MODE ) && sid_list ) {
+ if ( !BER_BVISEMPTY( &sid_list->si_url ) && !sid_set ) {
+ Debug(LDAP_DEBUG_ANY, "read_config: no serverID / URL match found. "
+ "Check slapd -h arguments.\n" );
+ rc = LDAP_OTHER;
+ }
+ }
+ return rc;
+}
+
+static int
+config_back_bind( Operation *op, SlapReply *rs )
+{
+ if ( be_isroot_pw( op ) ) {
+ ber_dupbv( &op->orb_edn, be_root_dn( op->o_bd ));
+ /* frontend sends result */
+ return LDAP_SUCCESS;
+ }
+
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ send_ldap_result( op, rs );
+
+ return rs->sr_err;
+}
+
+static int
+config_send( Operation *op, SlapReply *rs, CfEntryInfo *ce, int depth )
+{
+ int rc = 0;
+
+ if ( test_filter( op, ce->ce_entry, op->ors_filter ) == LDAP_COMPARE_TRUE )
+ {
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_entry = ce->ce_entry;
+ rs->sr_flags = 0;
+ rc = send_search_entry( op, rs );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+ }
+ if ( op->ors_scope == LDAP_SCOPE_SUBTREE ) {
+ if ( ce->ce_kids ) {
+ rc = config_send( op, rs, ce->ce_kids, 1 );
+ if ( rc ) return rc;
+ }
+ if ( depth ) {
+ for (ce=ce->ce_sibs; ce; ce=ce->ce_sibs) {
+ rc = config_send( op, rs, ce, 0 );
+ if ( rc ) break;
+ }
+ }
+ }
+ return rc;
+}
+
+static ConfigTable *
+config_find_table( ConfigOCs **colst, int nocs, AttributeDescription *ad,
+ ConfigArgs *ca )
+{
+ int i, j;
+ if (ad->ad_flags & SLAP_DESC_BINARY)
+ ad = ad->ad_type->sat_ad;
+
+ for (j=0; j<nocs; j++) {
+ for (i=0; colst[j]->co_table[i].name; i++)
+ if ( colst[j]->co_table[i].ad == ad ) {
+ ca->table = colst[j]->co_type;
+ return &colst[j]->co_table[i];
+ }
+ }
+ return NULL;
+}
+
+/* Sort the attributes of the entry according to the order defined
+ * in the objectclass, with required attributes occurring before
+ * allowed attributes. For any attributes with sequencing dependencies
+ * (e.g., rootDN must be defined after suffix) the objectclass must
+ * list the attributes in the desired sequence.
+ */
+static void
+sort_attrs( Entry *e, ConfigOCs **colst, int nocs )
+{
+ Attribute *a, *head = NULL, *tail = NULL, **prev;
+ int i, j;
+
+ for (i=0; i<nocs; i++) {
+ if ( colst[i]->co_oc->soc_required ) {
+ AttributeType **at = colst[i]->co_oc->soc_required;
+ for (j=0; at[j]; j++) {
+ for (a=e->e_attrs, prev=&e->e_attrs; a;
+ prev = &(*prev)->a_next, a=a->a_next) {
+ if ( a->a_desc == at[j]->sat_ad ) {
+ *prev = a->a_next;
+ if (!head) {
+ head = a;
+ tail = a;
+ } else {
+ tail->a_next = a;
+ tail = a;
+ }
+ break;
+ }
+ }
+ }
+ }
+ if ( colst[i]->co_oc->soc_allowed ) {
+ AttributeType **at = colst[i]->co_oc->soc_allowed;
+ for (j=0; at[j]; j++) {
+ for (a=e->e_attrs, prev=&e->e_attrs; a;
+ prev = &(*prev)->a_next, a=a->a_next) {
+ if ( a->a_desc == at[j]->sat_ad ) {
+ *prev = a->a_next;
+ if (!head) {
+ head = a;
+ tail = a;
+ } else {
+ tail->a_next = a;
+ tail = a;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ if ( tail ) {
+ tail->a_next = e->e_attrs;
+ e->e_attrs = head;
+ }
+}
+
+static int
+check_vals( ConfigTable *ct, ConfigArgs *ca, void *ptr, int isAttr )
+{
+ Attribute *a = NULL;
+ AttributeDescription *ad;
+ BerVarray vals;
+
+ int i, rc = 0;
+
+ if ( isAttr ) {
+ a = ptr;
+ ad = a->a_desc;
+ vals = a->a_vals;
+ } else {
+ Modifications *ml = ptr;
+ ad = ml->sml_desc;
+ vals = ml->sml_values;
+ }
+
+ if ( a && ( ad->ad_type->sat_flags & SLAP_AT_ORDERED_VAL )) {
+ rc = ordered_value_sort( a, 1 );
+ if ( rc ) {
+ snprintf(ca->cr_msg, sizeof( ca->cr_msg ), "ordered_value_sort failed on attr %s\n",
+ ad->ad_cname.bv_val );
+ return rc;
+ }
+ }
+ for ( i=0; vals[i].bv_val; i++ ) {
+ ca->line = vals[i].bv_val;
+ ca->linelen = vals[i].bv_len;
+ if (( ad->ad_type->sat_flags & SLAP_AT_ORDERED_VAL ) &&
+ ca->line[0] == '{' ) {
+ char *idx = strchr( ca->line, '}' );
+ if ( idx ) {
+ ca->linelen -= (idx+1) - ca->line;
+ ca->line = idx+1;
+ }
+ }
+ rc = config_parse_vals( ct, ca, i );
+ if ( rc ) {
+ break;
+ }
+ }
+ return rc;
+}
+
+static int
+config_rename_attr( SlapReply *rs, Entry *e, struct berval *rdn,
+ Attribute **at )
+{
+ struct berval rtype, rval;
+ Attribute *a;
+ AttributeDescription *ad = NULL;
+
+ dnRdn( &e->e_name, rdn );
+ rval.bv_val = strchr(rdn->bv_val, '=' ) + 1;
+ rval.bv_len = rdn->bv_len - (rval.bv_val - rdn->bv_val);
+ rtype.bv_val = rdn->bv_val;
+ rtype.bv_len = rval.bv_val - rtype.bv_val - 1;
+
+ /* Find attr */
+ slap_bv2ad( &rtype, &ad, &rs->sr_text );
+ a = attr_find( e->e_attrs, ad );
+ if (!a ) return LDAP_NAMING_VIOLATION;
+ *at = a;
+
+ return 0;
+}
+
+static void
+config_rename_kids( CfEntryInfo *ce )
+{
+ CfEntryInfo *ce2;
+ struct berval rdn, nrdn;
+
+ for (ce2 = ce->ce_kids; ce2; ce2 = ce2->ce_sibs) {
+ struct berval newdn, newndn;
+ dnRdn ( &ce2->ce_entry->e_name, &rdn );
+ dnRdn ( &ce2->ce_entry->e_nname, &nrdn );
+ build_new_dn( &newdn, &ce->ce_entry->e_name, &rdn, NULL );
+ build_new_dn( &newndn, &ce->ce_entry->e_nname, &nrdn, NULL );
+ free( ce2->ce_entry->e_name.bv_val );
+ free( ce2->ce_entry->e_nname.bv_val );
+ ce2->ce_entry->e_name = newdn;
+ ce2->ce_entry->e_nname = newndn;
+ config_rename_kids( ce2 );
+ }
+}
+
+static int
+config_rename_one( Operation *op, SlapReply *rs, Entry *e,
+ CfEntryInfo *parent, Attribute *a, struct berval *newrdn,
+ struct berval *nnewrdn, int use_ldif )
+{
+ int cnt, rc = 0;
+ struct berval odn, ondn;
+ const char *text = "";
+ LDAPRDN rDN;
+
+ odn = e->e_name;
+ ondn = e->e_nname;
+ build_new_dn( &e->e_name, &parent->ce_entry->e_name, newrdn, NULL );
+ build_new_dn( &e->e_nname, &parent->ce_entry->e_nname, nnewrdn, NULL );
+
+ /* Replace attr */
+ rc = ldap_bv2rdn( &e->e_name, &rDN, (char **)&text, LDAP_DN_FORMAT_LDAP );
+ if ( rc ) {
+ return rc;
+ }
+ for ( cnt = 0; rDN[cnt]; cnt++ ) {
+ AttributeDescription *ad = NULL;
+ LDAPAVA *ava = rDN[cnt];
+
+ rc = slap_bv2ad( &ava->la_attr, &ad, &text );
+ if ( rc ) {
+ break;
+ }
+
+ if ( ad != a->a_desc ) continue;
+
+ free( a->a_vals[0].bv_val );
+ ber_dupbv( &a->a_vals[0], &ava->la_value );
+ if ( a->a_nvals != a->a_vals ) {
+ free( a->a_nvals[0].bv_val );
+ rc = attr_normalize_one( ad, &ava->la_value, &a->a_nvals[0], NULL );
+ if ( rc ) {
+ break;
+ }
+ }
+
+ /* attributes with X-ORDERED 'SIBLINGS' are single-valued, we're done */
+ break;
+ }
+ /* the attribute must be present in rDN */
+ assert( rDN[cnt] );
+ ldap_rdnfree( rDN );
+ if ( rc ) {
+ return rc;
+ }
+
+ if ( use_ldif ) {
+ CfBackInfo *cfb = (CfBackInfo *)op->o_bd->be_private;
+ BackendDB *be = op->o_bd;
+ slap_callback sc = { NULL, slap_null_cb, NULL, NULL }, *scp;
+ struct berval dn, ndn, xdn, xndn;
+
+ op->o_bd = &cfb->cb_db;
+
+ /* Save current rootdn; use the underlying DB's rootdn */
+ dn = op->o_dn;
+ ndn = op->o_ndn;
+ xdn = op->o_req_dn;
+ xndn = op->o_req_ndn;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ op->o_req_dn = odn;
+ op->o_req_ndn = ondn;
+
+ scp = op->o_callback;
+ op->o_callback = &sc;
+ op->orr_newrdn = *newrdn;
+ op->orr_nnewrdn = *nnewrdn;
+ op->orr_newSup = NULL;
+ op->orr_nnewSup = NULL;
+ op->orr_deleteoldrdn = 1;
+ op->orr_modlist = NULL;
+ slap_modrdn2mods( op, rs );
+ slap_mods_opattrs( op, &op->orr_modlist, 1 );
+ rc = op->o_bd->be_modrdn( op, rs );
+ slap_mods_free( op->orr_modlist, 1 );
+
+ op->o_bd = be;
+ op->o_callback = scp;
+ op->o_dn = dn;
+ op->o_ndn = ndn;
+ op->o_req_dn = xdn;
+ op->o_req_ndn = xndn;
+ }
+ free( odn.bv_val );
+ free( ondn.bv_val );
+ if ( e->e_private )
+ config_rename_kids( e->e_private );
+ return rc;
+}
+
+static int
+config_renumber_one( Operation *op, SlapReply *rs, CfEntryInfo *parent,
+ Entry *e, int idx, int tailindex, int use_ldif )
+{
+ struct berval ival, newrdn, nnewrdn;
+ struct berval rdn;
+ Attribute *a;
+ char ibuf[32], *ptr1, *ptr2 = NULL;
+ int rc = 0;
+
+ rc = config_rename_attr( rs, e, &rdn, &a );
+ if ( rc ) return rc;
+
+ ival.bv_val = ibuf;
+ ival.bv_len = snprintf( ibuf, sizeof( ibuf ), SLAP_X_ORDERED_FMT, idx );
+ if ( ival.bv_len >= sizeof( ibuf ) ) {
+ return LDAP_NAMING_VIOLATION;
+ }
+
+ newrdn.bv_len = rdn.bv_len + ival.bv_len;
+ newrdn.bv_val = ch_malloc( newrdn.bv_len+1 );
+
+ if ( tailindex ) {
+ ptr1 = lutil_strncopy( newrdn.bv_val, rdn.bv_val, rdn.bv_len );
+ ptr1 = lutil_strcopy( ptr1, ival.bv_val );
+ } else {
+ int xlen;
+ ptr2 = ber_bvchr( &rdn, '}' );
+ if ( ptr2 ) {
+ ptr2++;
+ } else {
+ ptr2 = rdn.bv_val + a->a_desc->ad_cname.bv_len + 1;
+ }
+ xlen = rdn.bv_len - (ptr2 - rdn.bv_val);
+ ptr1 = lutil_strncopy( newrdn.bv_val, a->a_desc->ad_cname.bv_val,
+ a->a_desc->ad_cname.bv_len );
+ *ptr1++ = '=';
+ ptr1 = lutil_strcopy( ptr1, ival.bv_val );
+ ptr1 = lutil_strncopy( ptr1, ptr2, xlen );
+ *ptr1 = '\0';
+ }
+
+ /* Do the equivalent of ModRDN */
+ /* Replace DN / NDN */
+ newrdn.bv_len = ptr1 - newrdn.bv_val;
+ rc = rdnNormalize( 0, NULL, NULL, &newrdn, &nnewrdn, NULL );
+ if ( rc ) {
+ free( newrdn.bv_val );
+ return LDAP_NAMING_VIOLATION;
+ }
+ rc = config_rename_one( op, rs, e, parent, a, &newrdn, &nnewrdn, use_ldif );
+
+ free( nnewrdn.bv_val );
+ free( newrdn.bv_val );
+ return rc;
+}
+
+static int
+check_name_index( CfEntryInfo *parent, ConfigType ce_type, Entry *e,
+ SlapReply *rs, int *renum, int *ibase )
+{
+ CfEntryInfo *ce;
+ int index = -1, gotindex = 0, nsibs, rc = 0;
+ int renumber = 0, tailindex = 0, isfrontend = 0, isconfig = 0;
+ char *ptr1, *ptr2 = NULL;
+ struct berval rdn;
+
+ if ( renum ) *renum = 0;
+
+ /* These entries don't get indexed/renumbered */
+ if ( ce_type == Cft_Global ) return 0;
+ if ( ce_type == Cft_Schema && parent->ce_type == Cft_Global ) return 0;
+
+ if ( ce_type == Cft_Module )
+ tailindex = 1;
+
+ /* See if the rdn has an index already */
+ dnRdn( &e->e_name, &rdn );
+ if ( ce_type == Cft_Database ) {
+ if ( !strncmp( rdn.bv_val + rdn.bv_len - STRLENOF("frontend"),
+ "frontend", STRLENOF("frontend") ))
+ isfrontend = 1;
+ else if ( !strncmp( rdn.bv_val + rdn.bv_len - STRLENOF("config"),
+ "config", STRLENOF("config") ))
+ isconfig = 1;
+ }
+ ptr1 = ber_bvchr( &e->e_name, '{' );
+ if ( ptr1 && ptr1 < &e->e_name.bv_val[ rdn.bv_len ] ) {
+ char *next;
+ ptr2 = strchr( ptr1, '}' );
+ if ( !ptr2 || ptr2 > &e->e_name.bv_val[ rdn.bv_len ] )
+ return LDAP_NAMING_VIOLATION;
+ if ( ptr2-ptr1 == 1)
+ return LDAP_NAMING_VIOLATION;
+ gotindex = 1;
+ index = strtol( ptr1 + 1, &next, 10 );
+ if ( next == ptr1 + 1 || next[ 0 ] != '}' ) {
+ return LDAP_NAMING_VIOLATION;
+ }
+ if ( index < 0 ) {
+ /* Special case, we allow -1 for the frontendDB */
+ if ( index != -1 || !isfrontend )
+ return LDAP_NAMING_VIOLATION;
+ }
+ if ( isconfig && index != 0 ){
+ return LDAP_NAMING_VIOLATION;
+ }
+ }
+
+ /* count related kids.
+ * For entries of type Cft_Misc, only count siblings with same RDN type
+ */
+ if ( ce_type == Cft_Misc ) {
+ rdn.bv_val = e->e_nname.bv_val;
+ ptr1 = strchr( rdn.bv_val, '=' );
+ assert( ptr1 != NULL );
+
+ rdn.bv_len = ptr1 - rdn.bv_val;
+
+ for (nsibs=0, ce=parent->ce_kids; ce; ce=ce->ce_sibs) {
+ struct berval rdn2;
+ if ( ce->ce_type != ce_type )
+ continue;
+
+ dnRdn( &ce->ce_entry->e_nname, &rdn2 );
+
+ ptr1 = strchr( rdn2.bv_val, '=' );
+ assert( ptr1 != NULL );
+
+ rdn2.bv_len = ptr1 - rdn2.bv_val;
+ if ( bvmatch( &rdn, &rdn2 ))
+ nsibs++;
+ }
+ } else {
+ for (nsibs=0, ce=parent->ce_kids; ce; ce=ce->ce_sibs) {
+ if ( ce->ce_type == ce_type ) nsibs++;
+ }
+ }
+
+ /* account for -1 frontend */
+ if ( ce_type == Cft_Database )
+ nsibs--;
+
+ if ( index != nsibs || isfrontend ) {
+ if ( gotindex ) {
+ if ( index < nsibs ) {
+ if ( tailindex ) return LDAP_NAMING_VIOLATION;
+ /* Siblings need to be renumbered */
+ if ( index != -1 || !isfrontend )
+ renumber = 1;
+ }
+ }
+ /* config DB is always "0" */
+ if ( isconfig && index == -1 ) {
+ index = 0;
+ }
+ if (( !isfrontend && index == -1 ) || ( index > nsibs ) ){
+ index = nsibs;
+ }
+
+ /* just make index = nsibs */
+ if ( !renumber ) {
+ rc = config_renumber_one( NULL, rs, parent, e, index, tailindex, 0 );
+ }
+ }
+ if ( ibase ) *ibase = index;
+ if ( renum ) *renum = renumber;
+ return rc;
+}
+
+/* Insert all superior classes of the given class */
+static int
+count_oc( ObjectClass *oc, ConfigOCs ***copp, int *nocs )
+{
+ ConfigOCs co, *cop;
+ ObjectClass **sups;
+
+ for ( sups = oc->soc_sups; sups && *sups; sups++ ) {
+ if ( count_oc( *sups, copp, nocs ) ) {
+ return -1;
+ }
+ }
+
+ co.co_name = &oc->soc_cname;
+ cop = ldap_avl_find( CfOcTree, &co, CfOc_cmp );
+ if ( cop ) {
+ int i;
+
+ /* check for duplicates */
+ for ( i = 0; i < *nocs; i++ ) {
+ if ( *copp && (*copp)[i] == cop ) {
+ break;
+ }
+ }
+
+ if ( i == *nocs ) {
+ ConfigOCs **tmp = ch_realloc( *copp, (*nocs + 1)*sizeof( ConfigOCs * ) );
+ if ( tmp == NULL ) {
+ return -1;
+ }
+ *copp = tmp;
+ (*copp)[*nocs] = cop;
+ (*nocs)++;
+ }
+ }
+
+ return 0;
+}
+
+/* Find all superior classes of the given objectclasses,
+ * return list in order of most-subordinate first.
+ *
+ * Special / auxiliary / Cft_Misc classes always take precedence.
+ */
+static ConfigOCs **
+count_ocs( Attribute *oc_at, int *nocs )
+{
+ int i, j, misc = -1;
+ ConfigOCs **colst = NULL;
+
+ *nocs = 0;
+
+ for ( i = oc_at->a_numvals; i--; ) {
+ ObjectClass *oc = oc_bvfind( &oc_at->a_nvals[i] );
+
+ assert( oc != NULL );
+ if ( count_oc( oc, &colst, nocs ) ) {
+ ch_free( colst );
+ return NULL;
+ }
+ }
+
+ /* invert order */
+ i = 0;
+ j = *nocs - 1;
+ while ( i < j ) {
+ ConfigOCs *tmp = colst[i];
+ colst[i] = colst[j];
+ colst[j] = tmp;
+ if (tmp->co_type == Cft_Misc)
+ misc = j;
+ i++; j--;
+ }
+ /* Move misc class to front of list */
+ if (misc > 0) {
+ ConfigOCs *tmp = colst[misc];
+ for (i=misc; i>0; i--)
+ colst[i] = colst[i-1];
+ colst[0] = tmp;
+ }
+
+ return colst;
+}
+
+static int
+cfAddInclude( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
+{
+ /* Leftover from RE23. Never parse this entry */
+ return LDAP_COMPARE_TRUE;
+}
+
+static int
+cfAddSchema( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
+{
+ ConfigFile *cfo;
+
+ /* This entry is hardcoded, don't re-parse it */
+ if ( p->ce_type == Cft_Global ) {
+ cfn = p->ce_private;
+ ca->ca_private = cfn;
+ return LDAP_COMPARE_TRUE;
+ }
+ if ( p->ce_type != Cft_Schema )
+ return LDAP_CONSTRAINT_VIOLATION;
+
+ cfn = ch_calloc( 1, sizeof(ConfigFile) );
+ ca->ca_private = cfn;
+ cfo = p->ce_private;
+ cfn->c_sibs = cfo->c_kids;
+ cfo->c_kids = cfn;
+ return LDAP_SUCCESS;
+}
+
+static int
+cfAddDatabase( CfEntryInfo *p, Entry *e, struct config_args_s *ca )
+{
+ if ( p->ce_type != Cft_Global ) {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ /* config must be {0}, nothing else allowed */
+ if ( !strncmp( e->e_nname.bv_val, "olcDatabase={0}", STRLENOF("olcDatabase={0}")) &&
+ strncmp( e->e_nname.bv_val + STRLENOF("olcDatabase={0}"), "config,", STRLENOF("config,") )) {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ ca->be = frontendDB; /* just to get past check_vals */
+ return LDAP_SUCCESS;
+}
+
+static int
+cfAddBackend( CfEntryInfo *p, Entry *e, struct config_args_s *ca )
+{
+ if ( p->ce_type != Cft_Global ) {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ return LDAP_SUCCESS;
+}
+
+static int
+cfAddModule( CfEntryInfo *p, Entry *e, struct config_args_s *ca )
+{
+ if ( p->ce_type != Cft_Global ) {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ return LDAP_SUCCESS;
+}
+
+static int
+cfAddOverlay( CfEntryInfo *p, Entry *e, struct config_args_s *ca )
+{
+ if ( p->ce_type != Cft_Database ) {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ ca->be = p->ce_be;
+ return LDAP_SUCCESS;
+}
+
+static void
+schema_destroy_one( ConfigArgs *ca, ConfigOCs **colst, int nocs,
+ CfEntryInfo *p )
+{
+ ConfigTable *ct;
+ ConfigFile *cfo;
+ AttributeDescription *ad;
+ const char *text;
+
+ ca->valx = -1;
+ ca->line = NULL;
+ ca->argc = 1;
+ if ( cfn->c_cr_head ) {
+ struct berval bv = BER_BVC("olcDitContentRules");
+ ad = NULL;
+ slap_bv2ad( &bv, &ad, &text );
+ ct = config_find_table( colst, nocs, ad, ca );
+ config_del_vals( ct, ca );
+ }
+ if ( cfn->c_oc_head ) {
+ struct berval bv = BER_BVC("olcObjectClasses");
+ ad = NULL;
+ slap_bv2ad( &bv, &ad, &text );
+ ct = config_find_table( colst, nocs, ad, ca );
+ config_del_vals( ct, ca );
+ }
+ if ( cfn->c_at_head ) {
+ struct berval bv = BER_BVC("olcAttributeTypes");
+ ad = NULL;
+ slap_bv2ad( &bv, &ad, &text );
+ ct = config_find_table( colst, nocs, ad, ca );
+ config_del_vals( ct, ca );
+ }
+ if ( cfn->c_syn_head ) {
+ struct berval bv = BER_BVC("olcLdapSyntaxes");
+ ad = NULL;
+ slap_bv2ad( &bv, &ad, &text );
+ ct = config_find_table( colst, nocs, ad, ca );
+ config_del_vals( ct, ca );
+ }
+ if ( cfn->c_om_head ) {
+ struct berval bv = BER_BVC("olcObjectIdentifier");
+ ad = NULL;
+ slap_bv2ad( &bv, &ad, &text );
+ ct = config_find_table( colst, nocs, ad, ca );
+ config_del_vals( ct, ca );
+ }
+ cfo = p->ce_private;
+ cfo->c_kids = cfn->c_sibs;
+ ch_free( cfn );
+}
+
+static int
+config_add_oc( ConfigOCs **cop, CfEntryInfo *last, Entry *e, ConfigArgs *ca )
+{
+ int rc = LDAP_CONSTRAINT_VIOLATION;
+ ObjectClass **ocp;
+
+ if ( (*cop)->co_ldadd ) {
+ rc = (*cop)->co_ldadd( last, e, ca );
+ if ( rc != LDAP_CONSTRAINT_VIOLATION ) {
+ return rc;
+ }
+ }
+
+ for ( ocp = (*cop)->co_oc->soc_sups; ocp && *ocp; ocp++ ) {
+ ConfigOCs co = { 0 };
+
+ co.co_name = &(*ocp)->soc_cname;
+ *cop = ldap_avl_find( CfOcTree, &co, CfOc_cmp );
+ if ( *cop == NULL ) {
+ return rc;
+ }
+
+ rc = config_add_oc( cop, last, e, ca );
+ if ( rc != LDAP_CONSTRAINT_VIOLATION ) {
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static BackendDB *configDB; /* only set by slapadd */
+
+/* Parse an LDAP entry into config directives */
+static int
+config_add_internal( CfBackInfo *cfb, Entry *e, ConfigArgs *ca, SlapReply *rs,
+ int *renum, Operation *op )
+{
+ CfEntryInfo *ce, *last = NULL;
+ ConfigOCs co, *coptr, **colst;
+ Attribute *a, *oc_at, *soc_at;
+ int i, ibase = -1, nocs, rc = 0;
+ struct berval pdn;
+ ConfigTable *ct;
+ char *ptr, *log_prefix = op ? op->o_log_prefix : "";
+
+ memset( ca, 0, sizeof(ConfigArgs));
+
+ /* Make sure parent exists and entry does not. But allow
+ * Databases and Overlays to be inserted. Don't do any
+ * auto-renumbering if manageDSAit control is present.
+ */
+ ce = config_find_base( cfb->cb_root, &e->e_nname, &last );
+ if ( ce ) {
+ if ( ( op && op->o_managedsait ) ||
+ ( ce->ce_type != Cft_Database && ce->ce_type != Cft_Overlay &&
+ ce->ce_type != Cft_Module ) )
+ {
+ Debug( LDAP_DEBUG_TRACE, "%s: config_add_internal: "
+ "DN=\"%s\" already exists\n",
+ log_prefix, e->e_name.bv_val );
+ /* global schema ignores all writes */
+ if ( ce->ce_type == Cft_Schema && ce->ce_parent->ce_type == Cft_Global )
+ return LDAP_COMPARE_TRUE;
+ return LDAP_ALREADY_EXISTS;
+ }
+ }
+
+ dnParent( &e->e_nname, &pdn );
+
+ /* If last is NULL, the new entry is the root/suffix entry,
+ * otherwise last should be the parent.
+ */
+ if ( last && !dn_match( &last->ce_entry->e_nname, &pdn ) ) {
+ if ( rs ) {
+ rs->sr_matched = last->ce_entry->e_name.bv_val;
+ }
+ Debug( LDAP_DEBUG_TRACE, "%s: config_add_internal: "
+ "DN=\"%s\" not child of DN=\"%s\"\n",
+ log_prefix, e->e_name.bv_val,
+ last->ce_entry->e_name.bv_val );
+ return LDAP_NO_SUCH_OBJECT;
+ }
+
+ if ( op ) {
+ /* No parent, must be root. This will never happen... */
+ if ( !last && !be_isroot( op ) && !be_shadow_update( op ) ) {
+ return LDAP_NO_SUCH_OBJECT;
+ }
+
+ if ( last && !access_allowed( op, last->ce_entry,
+ slap_schema.si_ad_children, NULL, ACL_WADD, NULL ) )
+ {
+ Debug( LDAP_DEBUG_TRACE, "%s: config_add_internal: "
+ "DN=\"%s\" no write access to \"children\" of parent\n",
+ log_prefix, e->e_name.bv_val );
+ return LDAP_INSUFFICIENT_ACCESS;
+ }
+ }
+
+ oc_at = attr_find( e->e_attrs, slap_schema.si_ad_objectClass );
+ if ( !oc_at ) {
+ Debug( LDAP_DEBUG_TRACE, "%s: config_add_internal: "
+ "DN=\"%s\" no objectClass\n",
+ log_prefix, e->e_name.bv_val );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ soc_at = attr_find( e->e_attrs, slap_schema.si_ad_structuralObjectClass );
+ if ( !soc_at ) {
+ ObjectClass *soc = NULL;
+ char textbuf[ SLAP_TEXT_BUFLEN ];
+ const char *text = textbuf;
+
+ /* FIXME: check result */
+ rc = structural_class( oc_at->a_nvals, &soc, NULL,
+ &text, textbuf, sizeof(textbuf), NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "%s: config_add_internal: "
+ "DN=\"%s\" no structural objectClass (%s)\n",
+ log_prefix, e->e_name.bv_val, text );
+ return rc;
+ }
+ attr_merge_one( e, slap_schema.si_ad_structuralObjectClass, &soc->soc_cname, NULL );
+ soc_at = attr_find( e->e_attrs, slap_schema.si_ad_structuralObjectClass );
+ if ( soc_at == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "%s: config_add_internal: "
+ "DN=\"%s\" no structural objectClass; "
+ "unable to merge computed class %s\n",
+ log_prefix, e->e_name.bv_val,
+ soc->soc_cname.bv_val );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "%s: config_add_internal: "
+ "DN=\"%s\" no structural objectClass; "
+ "computed objectClass %s merged\n",
+ log_prefix, e->e_name.bv_val,
+ soc->soc_cname.bv_val );
+ }
+
+ /* Fake the coordinates based on whether we're part of an
+ * LDAP Add or if reading the config dir
+ */
+ if ( rs ) {
+ ca->fname = "slapd";
+ ca->lineno = 0;
+ } else {
+ ca->fname = cfdir.bv_val;
+ ca->lineno = 1;
+ }
+ ca->ca_op = op;
+
+ {
+ ObjectClass *soc = oc_bvfind( &soc_at->a_nvals[0] );
+ if ( !soc ) {
+ Debug( LDAP_DEBUG_TRACE, "%s: config_add_internal: "
+ "DN=\"%s\" invalid structural objectClass %s\n",
+ log_prefix, e->e_name.bv_val, soc_at->a_vals[0].bv_val );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+ co.co_name = &soc->soc_cname;
+ }
+ coptr = ldap_avl_find( CfOcTree, &co, CfOc_cmp );
+ if ( coptr == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "%s: config_add_internal: "
+ "DN=\"%s\" no structural objectClass in configuration table\n",
+ log_prefix, e->e_name.bv_val );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ /* Only the root can be Cft_Global, everything else must
+ * have a parent. Only limited nesting arrangements are allowed.
+ */
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ if ( coptr->co_type == Cft_Global && !last ) {
+ cfn = cfb->cb_config;
+ ca->ca_private = cfn;
+ ca->be = frontendDB; /* just to get past check_vals */
+ rc = LDAP_SUCCESS;
+ }
+
+ colst = count_ocs( oc_at, &nocs );
+
+ /* Check whether the Add is allowed by its parent, and do
+ * any necessary arg setup
+ */
+ if ( last ) {
+ rc = config_add_oc( &coptr, last, e, ca );
+ if ( rc == LDAP_CONSTRAINT_VIOLATION ) {
+ for ( i = 0; i<nocs; i++ ) {
+ /* Already checked these */
+ if ( colst[i]->co_oc->soc_kind == LDAP_SCHEMA_STRUCTURAL )
+ continue;
+ if ( colst[i]->co_ldadd &&
+ ( rc = colst[i]->co_ldadd( last, e, ca ))
+ != LDAP_CONSTRAINT_VIOLATION ) {
+ coptr = colst[i];
+ break;
+ }
+ }
+ }
+ if ( rc == LDAP_CONSTRAINT_VIOLATION ) {
+ Debug( LDAP_DEBUG_TRACE, "%s: config_add_internal: "
+ "DN=\"%s\" no structural objectClass add function\n",
+ log_prefix, e->e_name.bv_val );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+ }
+
+ /* Add the entry but don't parse it, we already have its contents */
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rc = LDAP_SUCCESS;
+ goto ok;
+ }
+
+ if ( rc != LDAP_SUCCESS )
+ goto done_noop;
+
+ /* Parse all the values and check for simple syntax errors before
+ * performing any set actions.
+ *
+ * If doing an LDAPadd, check for indexed names and any necessary
+ * renaming/renumbering. Entries that don't need indexed names are
+ * ignored. Entries that need an indexed name and arrive without one
+ * are assigned to the end. Entries that arrive with an index may
+ * cause the following entries to be renumbered/bumped down.
+ *
+ * Note that "pseudo-indexed" entries (cn=Include{xx}, cn=Module{xx})
+ * don't allow Adding an entry with an index that's already in use.
+ * This is flagged as an error (LDAP_ALREADY_EXISTS) up above.
+ *
+ * These entries can have auto-assigned indexes (appended to the end)
+ * but only the other types support auto-renumbering of siblings.
+ */
+ {
+ rc = check_name_index( last, coptr->co_type, e, rs, renum,
+ &ibase );
+ if ( rc ) {
+ goto done_noop;
+ }
+ if ( renum && *renum && coptr->co_type != Cft_Database &&
+ coptr->co_type != Cft_Overlay )
+ {
+ snprintf( ca->cr_msg, sizeof( ca->cr_msg ),
+ "operation requires sibling renumbering" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done_noop;
+ }
+ }
+
+ init_config_argv( ca );
+
+ /* Make sure we process attrs in the required order */
+ sort_attrs( e, colst, nocs );
+
+ for ( a = e->e_attrs; a; a = a->a_next ) {
+ if ( a == oc_at ) continue;
+ ct = config_find_table( colst, nocs, a->a_desc, ca );
+ if ( !ct ) continue; /* user data? */
+ rc = check_vals( ct, ca, a, 1 );
+ if ( rc ) goto done_noop;
+ }
+
+ /* Basic syntax checks are OK. Do the actual settings. */
+ for ( a=e->e_attrs; a; a=a->a_next ) {
+ if ( a == oc_at ) continue;
+ ct = config_find_table( colst, nocs, a->a_desc, ca );
+ if ( !ct ) continue; /* user data? */
+ for (i=0; a->a_vals[i].bv_val; i++) {
+ char *iptr = NULL;
+ ca->valx = -1;
+ ca->line = a->a_vals[i].bv_val;
+ ca->linelen = a->a_vals[i].bv_len;
+ if ( a->a_desc->ad_type->sat_flags & SLAP_AT_ORDERED ) {
+ ptr = strchr( ca->line, '}' );
+ if ( ptr ) {
+ iptr = strchr( ca->line, '{' );
+ ca->linelen -= (ptr+1) - ca->line;
+ ca->line = ptr+1;
+ }
+ }
+ if ( a->a_desc->ad_type->sat_flags & SLAP_AT_ORDERED_SIB ) {
+ if ( iptr ) {
+ ca->valx = strtol( iptr+1, NULL, 0 );
+ }
+ } else {
+ ca->valx = i;
+ }
+ rc = config_parse_add( ct, ca, i );
+ if ( rc ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ }
+ }
+ok:
+ /* Newly added databases and overlays need to be started up */
+ if ( CONFIG_ONLINE_ADD( ca )) {
+ if ( coptr->co_type == Cft_Database ) {
+ rc = backend_startup_one( ca->be, &ca->reply );
+
+ } else if ( coptr->co_type == Cft_Backend ) {
+ if ( ca->bi->bi_open ) {
+ rc = ca->bi->bi_open( ca->bi );
+ }
+
+ } else if ( coptr->co_type == Cft_Overlay ) {
+ if ( ca->bi->bi_db_open ) {
+ BackendInfo *bi_orig = ca->be->bd_info;
+ ca->be->bd_info = ca->bi;
+ rc = ca->bi->bi_db_open( ca->be, &ca->reply );
+ ca->be->bd_info = bi_orig;
+ }
+ } else if ( ca->num_cleanups ) {
+ rc = config_run_cleanup( ca );
+ }
+ if ( rc ) {
+ if (ca->cr_msg[0] == '\0')
+ snprintf( ca->cr_msg, sizeof( ca->cr_msg ), "<%s> failed startup", ca->argv[0] );
+
+ Debug(LDAP_DEBUG_ANY, "%s: %s (%s)!\n",
+ ca->log, ca->cr_msg, ca->argv[1] );
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ }
+
+ ca->valx = ibase;
+ ce = ch_calloc( 1, sizeof(CfEntryInfo) );
+ ce->ce_parent = last;
+ ce->ce_entry = entry_dup( e );
+ ce->ce_entry->e_private = ce;
+ ce->ce_type = coptr->co_type;
+ ce->ce_be = ca->be;
+ ce->ce_bi = ca->bi;
+ ce->ce_private = ca->ca_private;
+ ca->ca_entry = ce->ce_entry;
+ if ( !last ) {
+ cfb->cb_root = ce;
+ } else if ( last->ce_kids ) {
+ CfEntryInfo *c2, **cprev;
+
+ /* Advance to first of this type */
+ cprev = &last->ce_kids;
+ for ( c2 = *cprev; c2 && c2->ce_type < ce->ce_type; ) {
+ cprev = &c2->ce_sibs;
+ c2 = c2->ce_sibs;
+ }
+ /* Account for the (-1) frontendDB entry */
+ if ( ce->ce_type == Cft_Database ) {
+ if ( ca->be == frontendDB )
+ ibase = 0;
+ else if ( ibase != -1 )
+ ibase++;
+ }
+ /* Append */
+ if ( ibase < 0 ) {
+ for (c2 = *cprev; c2 && c2->ce_type == ce->ce_type;) {
+ cprev = &c2->ce_sibs;
+ c2 = c2->ce_sibs;
+ }
+ } else {
+ /* Insert */
+ int i;
+ for ( i=0; i<ibase; i++ ) {
+ c2 = *cprev;
+ cprev = &c2->ce_sibs;
+ }
+ }
+ ce->ce_sibs = *cprev;
+ *cprev = ce;
+ } else {
+ last->ce_kids = ce;
+ }
+
+done:
+ if ( rc ) {
+ if ( (coptr->co_type == Cft_Database) && ca->be ) {
+ if ( ca->be != frontendDB && ca->be != configDB )
+ backend_destroy_one( ca->be, 1 );
+ } else if ( (coptr->co_type == Cft_Overlay) && ca->bi ) {
+ overlay_destroy_one( ca->be, (slap_overinst *)ca->bi );
+ } else if ( coptr->co_type == Cft_Schema ) {
+ schema_destroy_one( ca, colst, nocs, last );
+ } else if ( ca->num_cleanups ) {
+ config_run_cleanup( ca );
+ }
+ }
+done_noop:
+
+ ch_free( ca->argv );
+ if ( colst ) ch_free( colst );
+ return rc;
+}
+
+#define BIGTMP 10000
+static int
+config_rename_add( Operation *op, SlapReply *rs, CfEntryInfo *ce,
+ int base, int rebase, int max, int use_ldif )
+{
+ CfEntryInfo *ce2, *ce3, *cetmp = NULL, *cerem = NULL;
+ ConfigType etype = ce->ce_type;
+ int count = 0, rc = 0;
+
+ /* Reverse ce list */
+ for (ce2 = ce->ce_sibs;ce2;ce2 = ce3) {
+ if (ce2->ce_type != etype) {
+ cerem = ce2;
+ break;
+ }
+ ce3 = ce2->ce_sibs;
+ ce2->ce_sibs = cetmp;
+ cetmp = ce2;
+ count++;
+ if ( max && count >= max ) {
+ cerem = ce3;
+ break;
+ }
+ }
+
+ /* Move original to a temp name until increments are done */
+ if ( rebase ) {
+ ce->ce_entry->e_private = NULL;
+ rc = config_renumber_one( op, rs, ce->ce_parent, ce->ce_entry,
+ base+BIGTMP, 0, use_ldif );
+ ce->ce_entry->e_private = ce;
+ }
+ /* start incrementing */
+ for (ce2=cetmp; ce2; ce2=ce3) {
+ ce3 = ce2->ce_sibs;
+ ce2->ce_sibs = cerem;
+ cerem = ce2;
+ if ( rc == 0 )
+ rc = config_renumber_one( op, rs, ce2->ce_parent, ce2->ce_entry,
+ count+base, 0, use_ldif );
+ count--;
+ }
+ if ( rebase )
+ rc = config_renumber_one( op, rs, ce->ce_parent, ce->ce_entry,
+ base, 0, use_ldif );
+ return rc;
+}
+
+static int
+config_rename_del( Operation *op, SlapReply *rs, CfEntryInfo *ce,
+ CfEntryInfo *ce2, int old, int use_ldif )
+{
+ int count = 0;
+
+ /* Renumber original to a temp value */
+ ce->ce_entry->e_private = NULL;
+ config_renumber_one( op, rs, ce->ce_parent, ce->ce_entry,
+ old+BIGTMP, 0, use_ldif );
+ ce->ce_entry->e_private = ce;
+
+ /* start decrementing */
+ for (; ce2 != ce; ce2=ce2->ce_sibs) {
+ config_renumber_one( op, rs, ce2->ce_parent, ce2->ce_entry,
+ count+old, 0, use_ldif );
+ count++;
+ }
+ return config_renumber_one( op, rs, ce->ce_parent, ce->ce_entry,
+ count+old, 0, use_ldif );
+}
+
+/* Parse an LDAP entry into config directives, then store in underlying
+ * database.
+ */
+static int
+config_back_add( Operation *op, SlapReply *rs )
+{
+ CfBackInfo *cfb;
+ int renumber, dopause = 1;
+ ConfigArgs ca;
+
+ if ( !access_allowed( op, op->ora_e, slap_schema.si_ad_entry,
+ NULL, ACL_WADD, NULL )) {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto out;
+ }
+
+ /*
+ * Check for attribute ACL
+ */
+ if ( !acl_check_modlist( op, op->ora_e, op->orm_modlist )) {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to attribute";
+ goto out;
+ }
+
+ cfb = (CfBackInfo *)op->o_bd->be_private;
+
+ /* add opattrs for syncprov */
+ {
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof textbuf;
+ rs->sr_err = entry_schema_check(op, op->ora_e, NULL, 0, 1, NULL,
+ &rs->sr_text, textbuf, sizeof( textbuf ) );
+ if ( rs->sr_err != LDAP_SUCCESS )
+ goto out;
+ rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(config_back_add) ": entry failed op attrs add: "
+ "%s (%d)\n", rs->sr_text, rs->sr_err );
+ goto out;
+ }
+ }
+
+ if ( op->o_abandon ) {
+ rs->sr_err = SLAPD_ABANDON;
+ goto out;
+ }
+ if ( slap_pause_server() < 0 )
+ dopause = 0;
+
+ ldap_pvt_thread_rdwr_wlock( &cfb->cb_rwlock );
+
+ /* Strategy:
+ * 1) check for existence of entry
+ * 2) check for sibling renumbering
+ * 3) perform internal add
+ * 4) perform any necessary renumbering
+ * 5) store entry in underlying database
+ */
+ rs->sr_err = config_add_internal( cfb, op->ora_e, &ca, rs, &renumber, op );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ rs->sr_text = ca.cr_msg;
+ goto out2;
+ }
+
+ if ( renumber ) {
+ CfEntryInfo *ce = ca.ca_entry->e_private;
+ req_add_s addr = op->oq_add;
+ op->o_tag = LDAP_REQ_MODRDN;
+ rs->sr_err = config_rename_add( op, rs, ce, ca.valx, 0, 0, cfb->cb_use_ldif );
+ op->o_tag = LDAP_REQ_ADD;
+ op->oq_add = addr;
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto out2;
+ }
+ }
+
+ if ( cfb->cb_use_ldif ) {
+ BackendDB *be = op->o_bd;
+ slap_callback sc = { NULL, slap_null_cb, NULL, NULL }, *scp;
+ struct berval dn, ndn;
+
+ op->o_bd = &cfb->cb_db;
+
+ /* Save current rootdn; use the underlying DB's rootdn */
+ dn = op->o_dn;
+ ndn = op->o_ndn;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ scp = op->o_callback;
+ op->o_callback = &sc;
+ op->o_bd->be_add( op, rs );
+ op->o_bd = be;
+ op->o_callback = scp;
+ op->o_dn = dn;
+ op->o_ndn = ndn;
+ }
+
+out2:;
+ ldap_pvt_thread_rdwr_wunlock( &cfb->cb_rwlock );
+ if ( dopause )
+ slap_unpause_server();
+
+out:;
+ { int repl = op->o_dont_replicate;
+ if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
+ rs->sr_text = NULL; /* Set after config_add_internal */
+ rs->sr_err = LDAP_SUCCESS;
+ op->o_dont_replicate = 1;
+ }
+ send_ldap_result( op, rs );
+ op->o_dont_replicate = repl;
+ }
+ slap_graduate_commit_csn( op );
+ return rs->sr_err;
+}
+
+typedef struct delrec {
+ struct delrec *next;
+ int nidx;
+ int idx[1];
+} delrec;
+
+static int
+config_modify_add( ConfigTable *ct, ConfigArgs *ca, AttributeDescription *ad,
+ int i )
+{
+ int rc;
+
+ ca->valx = -1;
+ if (ad->ad_type->sat_flags & SLAP_AT_ORDERED &&
+ ca->line[0] == '{' )
+ {
+ char *ptr = strchr( ca->line + 1, '}' );
+ if ( ptr ) {
+ char *next;
+
+ ca->valx = strtol( ca->line + 1, &next, 0 );
+ if ( next == ca->line + 1 || next[ 0 ] != '}' ) {
+ return LDAP_OTHER;
+ }
+ ca->linelen -= (ptr+1) - ca->line;
+ ca->line = ptr+1;
+ }
+ }
+ rc = config_parse_add( ct, ca, i );
+ if ( rc ) {
+ rc = LDAP_OTHER;
+ }
+ return rc;
+}
+
+static int
+config_modify_internal( CfEntryInfo *ce, Operation *op, SlapReply *rs,
+ ConfigArgs *ca )
+{
+ int rc = LDAP_UNWILLING_TO_PERFORM;
+ Modifications *ml;
+ Entry *e = ce->ce_entry;
+ Attribute *save_attrs = e->e_attrs, *oc_at, *s, *a;
+ ConfigTable *ct;
+ ConfigOCs **colst;
+ int i, nocs;
+ char *ptr;
+ delrec *dels = NULL, *deltail = NULL;
+
+ oc_at = attr_find( e->e_attrs, slap_schema.si_ad_objectClass );
+ if ( !oc_at ) return LDAP_OBJECT_CLASS_VIOLATION;
+
+ for (ml = op->orm_modlist; ml; ml=ml->sml_next) {
+ if (ml->sml_desc == slap_schema.si_ad_objectClass) {
+ /* We'd be fine comparing the structural objectclass before and
+ * after, but AUXILIARY ocs exist so we have to check them all */
+ unsigned int i, j;
+
+ if ( ml->sml_numvals != oc_at->a_numvals ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "objectclass modification disallowed" );
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ for ( i = 0; i < oc_at->a_numvals; i++ ) {
+ ObjectClass *new_oc, *old_oc = oc_bvfind( &oc_at->a_vals[i] );
+ int found = 0;
+
+ if ( old_oc == NULL ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "no objectClass named %s",
+ oc_at->a_vals[i].bv_val );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+ for ( j = 0; j < ml->sml_numvals; j++ ) {
+ new_oc = oc_bvfind( &ml->sml_values[j] );
+ if ( new_oc == NULL ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "no objectClass named %s",
+ ml->sml_values[j].bv_val );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ if ( old_oc == new_oc ) {
+ found = 1;
+ break;
+ }
+ }
+
+ if ( !found ) {
+ snprintf( ca->cr_msg, sizeof(ca->cr_msg),
+ "objectclass modification disallowed" );
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+ }
+ }
+ }
+
+ colst = count_ocs( oc_at, &nocs );
+
+ /* make sure add/del flags are clear; should always be true */
+ for ( s = save_attrs; s; s = s->a_next ) {
+ s->a_flags &= ~(SLAP_ATTR_IXADD|SLAP_ATTR_IXDEL);
+ }
+
+ e->e_attrs = attrs_dup( e->e_attrs );
+
+ init_config_argv( ca );
+ ca->be = ce->ce_be;
+ ca->bi = ce->ce_bi;
+ ca->ca_private = ce->ce_private;
+ ca->ca_entry = e;
+ ca->fname = "slapd";
+ ca->ca_op = op;
+ strcpy( ca->log, "back-config" );
+
+ for (ml = op->orm_modlist; ml; ml=ml->sml_next) {
+ ct = config_find_table( colst, nocs, ml->sml_desc, ca );
+ switch (ml->sml_op) {
+ case LDAP_MOD_DELETE:
+ case LDAP_MOD_REPLACE:
+ case SLAP_MOD_SOFTDEL:
+ {
+ BerVarray vals = NULL, nvals = NULL;
+ int *idx = NULL;
+ if ( ct && ( ct->arg_type & ARG_NO_DELETE )) {
+ rc = LDAP_OTHER;
+ snprintf(ca->cr_msg, sizeof(ca->cr_msg), "cannot delete %s",
+ ml->sml_desc->ad_cname.bv_val );
+ goto out_noop;
+ }
+ if ( ml->sml_op == LDAP_MOD_REPLACE ) {
+ vals = ml->sml_values;
+ nvals = ml->sml_nvalues;
+ ml->sml_values = NULL;
+ ml->sml_nvalues = NULL;
+ }
+ /* If we're deleting by values, remember the indexes of the
+ * values we deleted.
+ */
+ if ( ct && ml->sml_values ) {
+ delrec *d;
+ i = ml->sml_numvals;
+ d = ch_malloc( sizeof(delrec) + (i - 1)* sizeof(int));
+ d->nidx = i;
+ d->next = NULL;
+ if ( dels ) {
+ deltail->next = d;
+ } else {
+ dels = d;
+ }
+ deltail = d;
+ idx = d->idx;
+ }
+ rc = modify_delete_vindex(e, &ml->sml_mod,
+ get_permissiveModify(op),
+ &rs->sr_text, ca->cr_msg, sizeof(ca->cr_msg), idx );
+ if ( ml->sml_op == LDAP_MOD_REPLACE ) {
+ ml->sml_values = vals;
+ ml->sml_nvalues = nvals;
+ }
+ if ( rc == LDAP_NO_SUCH_ATTRIBUTE && ml->sml_op == SLAP_MOD_SOFTDEL )
+ {
+ rc = LDAP_SUCCESS;
+ }
+ /* FIXME: check rc before fallthru? */
+ if ( !vals )
+ break;
+ }
+ /* FALLTHRU: LDAP_MOD_REPLACE && vals */
+
+ case SLAP_MOD_ADD_IF_NOT_PRESENT:
+ if ( ml->sml_op == SLAP_MOD_ADD_IF_NOT_PRESENT
+ && attr_find( e->e_attrs, ml->sml_desc ) )
+ {
+ rc = LDAP_SUCCESS;
+ break;
+ }
+
+ case LDAP_MOD_ADD:
+ case SLAP_MOD_SOFTADD: {
+ int mop = ml->sml_op;
+ int navals = -1;
+ ml->sml_op = LDAP_MOD_ADD;
+ if ( ct ) {
+ if ( ct->arg_type & ARG_NO_INSERT ) {
+ Attribute *a = attr_find( e->e_attrs, ml->sml_desc );
+ if ( a ) {
+ navals = a->a_numvals;
+ }
+ }
+ for ( i=0; !BER_BVISNULL( &ml->sml_values[i] ); i++ ) {
+ if ( ml->sml_values[i].bv_val[0] == '{' &&
+ navals >= 0 )
+ {
+ char *next, *val = ml->sml_values[i].bv_val + 1;
+ int j;
+
+ j = strtol( val, &next, 0 );
+ if ( next == val || next[ 0 ] != '}' || j < navals ) {
+ rc = LDAP_OTHER;
+ snprintf(ca->cr_msg, sizeof(ca->cr_msg), "cannot insert %s",
+ ml->sml_desc->ad_cname.bv_val );
+ goto out_noop;
+ }
+ }
+ rc = check_vals( ct, ca, ml, 0 );
+ if ( rc ) goto out_noop;
+ }
+ }
+ rc = modify_add_values(e, &ml->sml_mod,
+ get_permissiveModify(op),
+ &rs->sr_text, ca->cr_msg, sizeof(ca->cr_msg) );
+
+ /* If value already exists, show success here
+ * and ignore this operation down below.
+ */
+ if ( mop == SLAP_MOD_SOFTADD ) {
+ if ( rc == LDAP_TYPE_OR_VALUE_EXISTS )
+ rc = LDAP_SUCCESS;
+ else
+ mop = LDAP_MOD_ADD;
+ }
+ ml->sml_op = mop;
+ break;
+ }
+
+ break;
+ case LDAP_MOD_INCREMENT: /* FIXME */
+ break;
+ default:
+ break;
+ }
+ if(rc != LDAP_SUCCESS) break;
+ }
+
+ if ( rc == LDAP_SUCCESS) {
+ /* check that the entry still obeys the schema */
+ rc = entry_schema_check(op, e, NULL, 0, 0, NULL,
+ &rs->sr_text, ca->cr_msg, sizeof(ca->cr_msg) );
+ }
+ if ( rc ) goto out_noop;
+
+ /* Basic syntax checks are OK. Do the actual settings. */
+ for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
+ ct = config_find_table( colst, nocs, ml->sml_desc, ca );
+ if ( !ct ) continue;
+
+ s = attr_find( save_attrs, ml->sml_desc );
+ a = attr_find( e->e_attrs, ml->sml_desc );
+
+ switch (ml->sml_op) {
+ case LDAP_MOD_DELETE:
+ case LDAP_MOD_REPLACE: {
+ BerVarray vals = NULL, nvals = NULL;
+ delrec *d = NULL;
+
+ if ( ml->sml_op == LDAP_MOD_REPLACE ) {
+ vals = ml->sml_values;
+ nvals = ml->sml_nvalues;
+ ml->sml_values = NULL;
+ ml->sml_nvalues = NULL;
+ }
+
+ if ( ml->sml_values )
+ d = dels;
+
+ /* If we didn't delete the whole attribute */
+ if ( ml->sml_values && a ) {
+ struct berval *mvals;
+ int j;
+
+ if ( ml->sml_nvalues )
+ mvals = ml->sml_nvalues;
+ else
+ mvals = ml->sml_values;
+
+ /* use the indexes we saved up above */
+ for (i=0; i < d->nidx; i++) {
+ struct berval bv = *mvals++;
+ if ( a->a_desc->ad_type->sat_flags & SLAP_AT_ORDERED &&
+ bv.bv_val[0] == '{' ) {
+ ptr = strchr( bv.bv_val, '}' ) + 1;
+ bv.bv_len -= ptr - bv.bv_val;
+ bv.bv_val = ptr;
+ }
+ ca->line = bv.bv_val;
+ ca->linelen = bv.bv_len;
+ ca->valx = d->idx[i];
+ config_parse_vals(ct, ca, d->idx[i] );
+ rc = config_del_vals( ct, ca );
+ if ( rc != LDAP_SUCCESS ) break;
+ if ( s )
+ s->a_flags |= SLAP_ATTR_IXDEL;
+ for (j=i+1; j < d->nidx; j++)
+ if ( d->idx[j] >d->idx[i] )
+ d->idx[j]--;
+ }
+ } else {
+ ca->valx = -1;
+ ca->line = NULL;
+ ca->argc = 1;
+ rc = config_del_vals( ct, ca );
+ if ( rc ) rc = LDAP_OTHER;
+ if ( s )
+ s->a_flags |= SLAP_ATTR_IXDEL;
+ }
+ if ( ml->sml_values ) {
+ d = d->next;
+ ch_free( dels );
+ dels = d;
+ }
+ if ( ml->sml_op == LDAP_MOD_REPLACE ) {
+ ml->sml_values = vals;
+ ml->sml_nvalues = nvals;
+ }
+ if ( !vals || rc != LDAP_SUCCESS )
+ break;
+ }
+ /* FALLTHRU: LDAP_MOD_REPLACE && vals */
+
+ case LDAP_MOD_ADD:
+ if ( !a )
+ break;
+ for (i=0; ml->sml_values[i].bv_val; i++) {
+ ca->line = ml->sml_values[i].bv_val;
+ ca->linelen = ml->sml_values[i].bv_len;
+ ca->valx = -1;
+ rc = config_modify_add( ct, ca, ml->sml_desc, i );
+ if ( rc )
+ goto out;
+ a->a_flags |= SLAP_ATTR_IXADD;
+ }
+ break;
+ }
+ }
+
+out:
+ /* Undo for a failed operation */
+ if ( rc != LDAP_SUCCESS ) {
+ ConfigReply msg = ca->reply;
+ for ( s = save_attrs; s; s = s->a_next ) {
+ if ( s->a_flags & SLAP_ATTR_IXDEL ) {
+ s->a_flags &= ~(SLAP_ATTR_IXDEL|SLAP_ATTR_IXADD);
+ ct = config_find_table( colst, nocs, s->a_desc, ca );
+ a = attr_find( e->e_attrs, s->a_desc );
+ if ( a ) {
+ /* clear the flag so the add check below will skip it */
+ a->a_flags &= ~(SLAP_ATTR_IXDEL|SLAP_ATTR_IXADD);
+ ca->valx = -1;
+ ca->line = NULL;
+ ca->argc = 1;
+ config_del_vals( ct, ca );
+ }
+ for ( i=0; !BER_BVISNULL( &s->a_vals[i] ); i++ ) {
+ ca->line = s->a_vals[i].bv_val;
+ ca->linelen = s->a_vals[i].bv_len;
+ ca->valx = -1;
+ config_modify_add( ct, ca, s->a_desc, i );
+ }
+ }
+ }
+ for ( a = e->e_attrs; a; a = a->a_next ) {
+ if ( a->a_flags & SLAP_ATTR_IXADD ) {
+ ct = config_find_table( colst, nocs, a->a_desc, ca );
+ ca->valx = -1;
+ ca->line = NULL;
+ ca->argc = 1;
+ config_del_vals( ct, ca );
+ s = attr_find( save_attrs, a->a_desc );
+ if ( s ) {
+ s->a_flags &= ~(SLAP_ATTR_IXDEL|SLAP_ATTR_IXADD);
+ for ( i=0; !BER_BVISNULL( &s->a_vals[i] ); i++ ) {
+ ca->line = s->a_vals[i].bv_val;
+ ca->linelen = s->a_vals[i].bv_len;
+ ca->valx = -1;
+ config_modify_add( ct, ca, s->a_desc, i );
+ }
+ }
+ }
+ }
+ ca->reply = msg;
+ }
+
+ if ( ca->num_cleanups ) {
+ i = config_run_cleanup( ca );
+ if (rc == LDAP_SUCCESS)
+ rc = i;
+ }
+out_noop:
+ if ( rc == LDAP_SUCCESS ) {
+ attrs_free( save_attrs );
+ rs->sr_text = NULL;
+ } else {
+ attrs_free( e->e_attrs );
+ e->e_attrs = save_attrs;
+ }
+ ch_free( ca->argv );
+ if ( colst ) ch_free( colst );
+ while( dels ) {
+ deltail = dels->next;
+ ch_free( dels );
+ dels = deltail;
+ }
+
+ return rc;
+}
+
+static int
+config_back_modify( Operation *op, SlapReply *rs )
+{
+ CfBackInfo *cfb;
+ CfEntryInfo *ce, *last;
+ Modifications *ml;
+ ConfigArgs ca = {0};
+ struct berval rdn;
+ char *ptr;
+ AttributeDescription *rad = NULL;
+ int do_pause = 1;
+
+ cfb = (CfBackInfo *)op->o_bd->be_private;
+
+ ce = config_find_base( cfb->cb_root, &op->o_req_ndn, &last );
+ if ( !ce ) {
+ if ( last )
+ rs->sr_matched = last->ce_entry->e_name.bv_val;
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto out;
+ }
+
+ if ( !acl_check_modlist( op, ce->ce_entry, op->orm_modlist )) {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto out;
+ }
+
+ /* Get type of RDN */
+ rdn = ce->ce_entry->e_nname;
+ ptr = strchr( rdn.bv_val, '=' );
+ rdn.bv_len = ptr - rdn.bv_val;
+ rs->sr_err = slap_bv2ad( &rdn, &rad, &rs->sr_text );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto out;
+ }
+
+ /* Some basic validation... */
+ for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
+ /* Don't allow Modify of RDN; must use ModRdn for that. */
+ if ( ml->sml_desc == rad ) {
+ rs->sr_err = LDAP_NOT_ALLOWED_ON_RDN;
+ rs->sr_text = "Use modrdn to change the entry name";
+ goto out;
+ }
+ /* Internal update of contextCSN? */
+ if ( ml->sml_desc == slap_schema.si_ad_contextCSN && op->o_conn->c_conn_idx == -1 ) {
+ do_pause = 0;
+ break;
+ }
+ }
+
+ slap_mods_opattrs( op, &op->orm_modlist, 1 );
+
+ if ( do_pause ) {
+ if ( op->o_abandon ) {
+ rs->sr_err = SLAPD_ABANDON;
+ goto out;
+ }
+ if ( slap_pause_server() < 0 )
+ do_pause = 0;
+ }
+ ldap_pvt_thread_rdwr_wlock( &cfb->cb_rwlock );
+
+ /* Strategy:
+ * 1) perform the Modify on the cached Entry.
+ * 2) verify that the Entry still satisfies the schema.
+ * 3) perform the individual config operations.
+ * 4) store Modified entry in underlying LDIF backend.
+ */
+ rs->sr_err = config_modify_internal( ce, op, rs, &ca );
+ if ( rs->sr_err ) {
+ rs->sr_text = ca.cr_msg;
+ } else if ( cfb->cb_use_ldif ) {
+ BackendDB *be = op->o_bd;
+ slap_callback sc = { NULL, slap_null_cb, NULL, NULL }, *scp;
+ struct berval dn, ndn;
+
+ op->o_bd = &cfb->cb_db;
+
+ dn = op->o_dn;
+ ndn = op->o_ndn;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ scp = op->o_callback;
+ op->o_callback = &sc;
+ op->o_bd->be_modify( op, rs );
+ op->o_bd = be;
+ op->o_callback = scp;
+ op->o_dn = dn;
+ op->o_ndn = ndn;
+ }
+
+ ldap_pvt_thread_rdwr_wunlock( &cfb->cb_rwlock );
+ if ( do_pause )
+ slap_unpause_server();
+out:
+ send_ldap_result( op, rs );
+ slap_graduate_commit_csn( op );
+ return rs->sr_err;
+}
+
+static int
+config_back_modrdn( Operation *op, SlapReply *rs )
+{
+ CfBackInfo *cfb;
+ CfEntryInfo *ce, *last;
+ struct berval rdn;
+ int ixold, ixnew, dopause = 1;
+
+ cfb = (CfBackInfo *)op->o_bd->be_private;
+
+ ce = config_find_base( cfb->cb_root, &op->o_req_ndn, &last );
+ if ( !ce ) {
+ if ( last )
+ rs->sr_matched = last->ce_entry->e_name.bv_val;
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto out;
+ }
+ if ( !access_allowed( op, ce->ce_entry, slap_schema.si_ad_entry,
+ NULL, ACL_WRITE, NULL )) {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto out;
+ }
+ { Entry *parent;
+ if ( ce->ce_parent )
+ parent = ce->ce_parent->ce_entry;
+ else
+ parent = (Entry *)&slap_entry_root;
+ if ( !access_allowed( op, parent, slap_schema.si_ad_children,
+ NULL, ACL_WRITE, NULL )) {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto out;
+ }
+ }
+
+ /* We don't allow moving objects to new parents.
+ * Generally we only allow reordering a set of ordered entries.
+ */
+ if ( op->orr_newSup ) {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ goto out;
+ }
+
+ /* If newRDN == oldRDN, quietly succeed */
+ dnRdn( &op->o_req_ndn, &rdn );
+ if ( dn_match( &rdn, &op->orr_nnewrdn )) {
+ rs->sr_err = LDAP_SUCCESS;
+ goto out;
+ }
+
+ /* Current behavior, subject to change as needed:
+ *
+ * For backends and overlays, we only allow renumbering.
+ * For schema, we allow renaming with the same number.
+ * Otherwise, the op is not allowed.
+ */
+
+ if ( ce->ce_type == Cft_Schema ) {
+ char *ptr1, *ptr2;
+ int len;
+
+ /* Can't alter the main cn=schema entry */
+ if ( ce->ce_parent->ce_type == Cft_Global ) {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "renaming not allowed for this entry";
+ goto out;
+ }
+
+ /* We could support this later if desired */
+ ptr1 = ber_bvchr( &rdn, '}' );
+ ptr2 = ber_bvchr( &op->orr_newrdn, '}' );
+ len = ptr1 - rdn.bv_val;
+ if ( len != ptr2 - op->orr_newrdn.bv_val ||
+ strncmp( rdn.bv_val, op->orr_newrdn.bv_val, len )) {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "schema reordering not supported";
+ goto out;
+ }
+ } else if ( ce->ce_type == Cft_Database ||
+ ce->ce_type == Cft_Overlay ) {
+ char *ptr1, *ptr2, *iptr1, *iptr2;
+ int len1, len2;
+
+ iptr2 = ber_bvchr( &op->orr_newrdn, '=' ) + 1;
+ if ( *iptr2 != '{' ) {
+ rs->sr_err = LDAP_NAMING_VIOLATION;
+ rs->sr_text = "new ordering index is required";
+ goto out;
+ }
+ iptr2++;
+ iptr1 = ber_bvchr( &rdn, '{' ) + 1;
+ ptr1 = ber_bvchr( &rdn, '}' );
+ ptr2 = ber_bvchr( &op->orr_newrdn, '}' );
+ if ( !ptr2 ) {
+ rs->sr_err = LDAP_NAMING_VIOLATION;
+ rs->sr_text = "new ordering index is required";
+ goto out;
+ }
+
+ len1 = ptr1 - rdn.bv_val;
+ len2 = ptr2 - op->orr_newrdn.bv_val;
+
+ if ( rdn.bv_len - len1 != op->orr_newrdn.bv_len - len2 ||
+ strncmp( ptr1, ptr2, rdn.bv_len - len1 )) {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "changing database/overlay type not allowed";
+ goto out;
+ }
+ ixold = strtol( iptr1, NULL, 0 );
+ ixnew = strtol( iptr2, &ptr1, 0 );
+ if ( ptr1 != ptr2 || ixold < 0 || ixnew < 0 ) {
+ rs->sr_err = LDAP_NAMING_VIOLATION;
+ goto out;
+ }
+ /* config DB is always 0, cannot be changed */
+ if ( ce->ce_type == Cft_Database && ( ixold == 0 || ixnew == 0 )) {
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ goto out;
+ }
+ } else {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "renaming not supported for this entry";
+ goto out;
+ }
+
+ if ( op->o_abandon ) {
+ rs->sr_err = SLAPD_ABANDON;
+ goto out;
+ }
+ if ( slap_pause_server() < 0 )
+ dopause = 0;
+
+ ldap_pvt_thread_rdwr_wlock( &cfb->cb_rwlock );
+
+ if ( ce->ce_type == Cft_Schema ) {
+ req_modrdn_s modr = op->oq_modrdn;
+ struct berval rdn;
+ Attribute *a;
+ rs->sr_err = config_rename_attr( rs, ce->ce_entry, &rdn, &a );
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ rs->sr_err = config_rename_one( op, rs, ce->ce_entry,
+ ce->ce_parent, a, &op->orr_newrdn, &op->orr_nnewrdn,
+ cfb->cb_use_ldif );
+ }
+ op->oq_modrdn = modr;
+ } else {
+ CfEntryInfo *ce2, **cprev, **cbprev, *ceold;
+ req_modrdn_s modr = op->oq_modrdn;
+ int i;
+
+ /* Advance to first of this type */
+ cprev = &ce->ce_parent->ce_kids;
+ for ( ce2 = *cprev; ce2 && ce2->ce_type != ce->ce_type; ) {
+ cprev = &ce2->ce_sibs;
+ ce2 = ce2->ce_sibs;
+ }
+ /* Skip the -1 entry */
+ if ( ce->ce_type == Cft_Database ) {
+ cprev = &ce2->ce_sibs;
+ ce2 = ce2->ce_sibs;
+ }
+ cbprev = cprev;
+
+ /* Remove from old slot */
+ for ( ce2 = *cprev; ce2 && ce2 != ce; ce2 = ce2->ce_sibs )
+ cprev = &ce2->ce_sibs;
+ *cprev = ce->ce_sibs;
+ ceold = ce->ce_sibs;
+
+ /* Insert into new slot */
+ cprev = cbprev;
+ for ( i=0; i<ixnew; i++ ) {
+ ce2 = *cprev;
+ if ( !ce2 )
+ break;
+ cprev = &ce2->ce_sibs;
+ }
+ ce->ce_sibs = *cprev;
+ *cprev = ce;
+
+ ixnew = i;
+
+ /* NOTE: These should be encoded in the OC tables, not inline here */
+ if ( ce->ce_type == Cft_Database )
+ backend_db_move( ce->ce_be, ixnew );
+ else if ( ce->ce_type == Cft_Overlay )
+ overlay_move( ce->ce_be, (slap_overinst *)ce->ce_bi, ixnew );
+
+ if ( ixold < ixnew ) {
+ rs->sr_err = config_rename_del( op, rs, ce, ceold, ixold,
+ cfb->cb_use_ldif );
+ } else {
+ rs->sr_err = config_rename_add( op, rs, ce, ixnew, 1,
+ ixold - ixnew, cfb->cb_use_ldif );
+ }
+ op->oq_modrdn = modr;
+ }
+
+ ldap_pvt_thread_rdwr_wunlock( &cfb->cb_rwlock );
+
+ if ( dopause )
+ slap_unpause_server();
+out:
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+}
+
+static int
+config_back_delete( Operation *op, SlapReply *rs )
+{
+#ifdef SLAP_CONFIG_DELETE
+ CfBackInfo *cfb;
+ CfEntryInfo *ce, *last, *ce2;
+ int dopause = 1;
+
+ cfb = (CfBackInfo *)op->o_bd->be_private;
+
+ ce = config_find_base( cfb->cb_root, &op->o_req_ndn, &last );
+ if ( !ce ) {
+ if ( last )
+ rs->sr_matched = last->ce_entry->e_name.bv_val;
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ } else if ( ce->ce_kids ) {
+ rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
+ } else if ( op->o_abandon ) {
+ rs->sr_err = SLAPD_ABANDON;
+ } else if ( ce->ce_type == Cft_Overlay ||
+ ce->ce_type == Cft_Database ||
+ ce->ce_type == Cft_Misc ){
+ char *iptr;
+ int count, ixold;
+
+ if ( slap_pause_server() < 0 )
+ dopause = 0;
+
+ ldap_pvt_thread_rdwr_wlock( &cfb->cb_rwlock );
+
+ if ( ce->ce_type == Cft_Overlay ){
+ overlay_remove( ce->ce_be, (slap_overinst *)ce->ce_bi, op );
+ } else if ( ce->ce_type == Cft_Misc ) {
+ /*
+ * only Cft_Misc objects that have a co_lddel handler set in
+ * the ConfigOCs struct can be deleted. This code also
+ * assumes that the entry can be only have one objectclass
+ * with co_type == Cft_Misc
+ */
+ ConfigOCs co, *coptr;
+ Attribute *oc_at;
+ int i;
+
+ oc_at = attr_find( ce->ce_entry->e_attrs,
+ slap_schema.si_ad_objectClass );
+ if ( !oc_at ) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "objectclass not found";
+ goto out2;
+ }
+ for ( i=0; !BER_BVISNULL(&oc_at->a_nvals[i]); i++ ) {
+ co.co_name = &oc_at->a_nvals[i];
+ coptr = ldap_avl_find( CfOcTree, &co, CfOc_cmp );
+ if ( coptr == NULL || coptr->co_type != Cft_Misc ) {
+ continue;
+ }
+ if ( ! coptr->co_lddel || coptr->co_lddel( ce, op ) ){
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ if ( ! coptr->co_lddel ) {
+ rs->sr_text = "No delete handler found";
+ } else {
+ rs->sr_err = LDAP_OTHER;
+ /* FIXME: We should return a helpful error message
+ * here */
+ }
+ goto out2;
+ }
+ break;
+ }
+ } else if (ce->ce_type == Cft_Database ) {
+ if ( ce->ce_be == frontendDB || ce->ce_be == op->o_bd ){
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "Cannot delete config or frontend database";
+ goto out2;
+ }
+ if ( ce->ce_be->bd_info->bi_db_close ) {
+ ce->ce_be->bd_info->bi_db_close( ce->ce_be, NULL );
+ }
+ backend_destroy_one( ce->ce_be, 1);
+ }
+
+ /* remove CfEntryInfo from the siblings list */
+ if ( ce->ce_parent->ce_kids == ce ) {
+ ce->ce_parent->ce_kids = ce->ce_sibs;
+ } else {
+ for ( ce2 = ce->ce_parent->ce_kids ; ce2; ce2 = ce2->ce_sibs ) {
+ if ( ce2->ce_sibs == ce ) {
+ ce2->ce_sibs = ce->ce_sibs;
+ break;
+ }
+ }
+ }
+
+ /* remove from underlying database */
+ if ( cfb->cb_use_ldif ) {
+ BackendDB *be = op->o_bd;
+ slap_callback sc = { NULL, slap_null_cb, NULL, NULL }, *scp;
+ struct berval dn, ndn, req_dn, req_ndn;
+
+ op->o_bd = &cfb->cb_db;
+
+ dn = op->o_dn;
+ ndn = op->o_ndn;
+ req_dn = op->o_req_dn;
+ req_ndn = op->o_req_ndn;
+
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ op->o_req_dn = ce->ce_entry->e_name;
+ op->o_req_ndn = ce->ce_entry->e_nname;
+
+ scp = op->o_callback;
+ op->o_callback = &sc;
+ op->o_bd->be_delete( op, rs );
+ op->o_bd = be;
+ op->o_callback = scp;
+ op->o_dn = dn;
+ op->o_ndn = ndn;
+ op->o_req_dn = req_dn;
+ op->o_req_ndn = req_ndn;
+ }
+
+ /* renumber siblings */
+ iptr = ber_bvchr( &op->o_req_ndn, '{' ) + 1;
+ ixold = strtol( iptr, NULL, 0 );
+ for (ce2 = ce->ce_sibs, count=0; ce2; ce2=ce2->ce_sibs) {
+ config_renumber_one( op, rs, ce2->ce_parent, ce2->ce_entry,
+ count+ixold, 0, cfb->cb_use_ldif );
+ count++;
+ }
+
+ ce->ce_entry->e_private=NULL;
+ entry_free(ce->ce_entry);
+ ch_free(ce);
+out2:
+ ldap_pvt_thread_rdwr_wunlock( &cfb->cb_rwlock );
+ if ( dopause ) slap_unpause_server();
+ } else {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ }
+out:
+#else
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+#endif /* SLAP_CONFIG_DELETE */
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+}
+
+static int
+config_back_search( Operation *op, SlapReply *rs )
+{
+ CfBackInfo *cfb;
+ CfEntryInfo *ce, *last;
+ slap_mask_t mask;
+
+ cfb = (CfBackInfo *)op->o_bd->be_private;
+
+ ldap_pvt_thread_rdwr_rlock( &cfb->cb_rwlock );
+ ce = config_find_base( cfb->cb_root, &op->o_req_ndn, &last );
+ if ( !ce ) {
+ if ( last )
+ rs->sr_matched = last->ce_entry->e_name.bv_val;
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto out;
+ }
+ if ( !access_allowed_mask( op, ce->ce_entry, slap_schema.si_ad_entry, NULL,
+ ACL_SEARCH, NULL, &mask ))
+ {
+ if ( !ACL_GRANT( mask, ACL_DISCLOSE )) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ } else {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ }
+ goto out;
+ }
+ switch ( op->ors_scope ) {
+ case LDAP_SCOPE_BASE:
+ case LDAP_SCOPE_SUBTREE:
+ rs->sr_err = config_send( op, rs, ce, 0 );
+ break;
+
+ case LDAP_SCOPE_ONELEVEL:
+ for (ce = ce->ce_kids; ce; ce=ce->ce_sibs) {
+ rs->sr_err = config_send( op, rs, ce, 1 );
+ if ( rs->sr_err ) {
+ break;
+ }
+ }
+ break;
+ }
+
+out:
+ ldap_pvt_thread_rdwr_runlock( &cfb->cb_rwlock );
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+}
+
+/* no-op, we never free entries */
+int config_entry_release(
+ Operation *op,
+ Entry *e,
+ int rw )
+{
+ int rc = LDAP_SUCCESS;
+
+ if ( !e->e_private ) {
+ BackendDB *be = op->o_bd;
+ CfBackInfo *cfb = be->be_private;
+ BackendInfo *bi = cfb->cb_db.bd_info;
+
+ if ( bi && bi->bi_entry_release_rw ) {
+ op->o_bd = &cfb->cb_db;
+ rc = bi->bi_entry_release_rw( op, e, rw );
+ op->o_bd = be;
+ } else {
+ entry_free( e );
+ }
+ }
+ return rc;
+}
+
+/* return LDAP_SUCCESS IFF we can retrieve the specified entry.
+ */
+int config_back_entry_get(
+ Operation *op,
+ struct berval *ndn,
+ ObjectClass *oc,
+ AttributeDescription *at,
+ int rw,
+ Entry **ent )
+{
+ CfBackInfo *cfb;
+ CfEntryInfo *ce, *last;
+ int rc = LDAP_NO_SUCH_OBJECT;
+
+ cfb = (CfBackInfo *)op->o_bd->be_private;
+
+ ce = config_find_base( cfb->cb_root, ndn, &last );
+ if ( ce ) {
+ *ent = ce->ce_entry;
+ if ( *ent ) {
+ rc = LDAP_SUCCESS;
+ if ( oc && !is_entry_objectclass_or_sub( *ent, oc ) ) {
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ *ent = NULL;
+ }
+ }
+ }
+
+ return rc;
+}
+
+static int
+config_build_attrs( Entry *e, AttributeType **at, AttributeDescription *ad,
+ ConfigTable *ct, ConfigArgs *c )
+{
+ int i, rc;
+
+ for (; at && *at; at++) {
+ /* Skip the naming attr */
+ if ((*at)->sat_ad == ad || (*at)->sat_ad == slap_schema.si_ad_cn )
+ continue;
+ for (i=0;ct[i].name;i++) {
+ if (ct[i].ad == (*at)->sat_ad) {
+ rc = config_get_vals(&ct[i], c);
+ /* NOTE: tolerate that config_get_vals()
+ * returns success with no values */
+ if (rc == LDAP_SUCCESS && c->rvalue_vals != NULL ) {
+ if ( c->rvalue_nvals )
+ rc = attr_merge(e, ct[i].ad, c->rvalue_vals,
+ c->rvalue_nvals);
+ else {
+ slap_syntax_validate_func *validate =
+ ct[i].ad->ad_type->sat_syntax->ssyn_validate;
+ if ( validate ) {
+ int j;
+ for ( j=0; c->rvalue_vals[j].bv_val; j++ ) {
+ rc = ordered_value_validate( ct[i].ad,
+ &c->rvalue_vals[j], LDAP_MOD_ADD );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "config_build_attrs: error %d on %s value #%d\n",
+ rc, ct[i].ad->ad_cname.bv_val, j );
+ return rc;
+ }
+ }
+ }
+
+ rc = attr_merge_normalize(e, ct[i].ad,
+ c->rvalue_vals, NULL);
+ }
+ ber_bvarray_free( c->rvalue_nvals );
+ ber_bvarray_free( c->rvalue_vals );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "config_build_attrs: error %d on %s\n",
+ rc, ct[i].ad->ad_cname.bv_val );
+ return rc;
+ }
+ }
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+/* currently (2010) does not access rs except possibly writing rs->sr_err */
+
+Entry *
+config_build_entry( Operation *op, SlapReply *rs, CfEntryInfo *parent,
+ ConfigArgs *c, struct berval *rdn, ConfigOCs *main, ConfigOCs *extra )
+{
+ Entry *e = entry_alloc();
+ CfEntryInfo *ce = ch_calloc( 1, sizeof(CfEntryInfo) );
+ AttributeDescription *ad = NULL;
+ int cnt, rc;
+ const char *text = "";
+ Attribute *oc_at;
+ struct berval pdn;
+ ObjectClass *oc;
+ CfEntryInfo *ceprev = NULL;
+ LDAPRDN rDN;
+
+ Debug( LDAP_DEBUG_TRACE, "config_build_entry: \"%s\"\n", rdn->bv_val );
+ e->e_private = ce;
+ ce->ce_entry = e;
+ ce->ce_type = main->co_type;
+ ce->ce_parent = parent;
+ if ( parent ) {
+ pdn = parent->ce_entry->e_nname;
+ if ( parent->ce_kids && parent->ce_kids->ce_type <= ce->ce_type )
+ for ( ceprev = parent->ce_kids; ceprev->ce_sibs &&
+ ceprev->ce_type <= ce->ce_type;
+ ceprev = ceprev->ce_sibs );
+ } else {
+ BER_BVZERO( &pdn );
+ }
+
+ ce->ce_private = c->ca_private;
+ ce->ce_be = c->be;
+ ce->ce_bi = c->bi;
+
+ build_new_dn( &e->e_name, &pdn, rdn, NULL );
+ ber_dupbv( &e->e_nname, &e->e_name );
+
+ attr_merge_normalize_one(e, slap_schema.si_ad_objectClass,
+ main->co_name, NULL );
+ if ( extra )
+ attr_merge_normalize_one(e, slap_schema.si_ad_objectClass,
+ extra->co_name, NULL );
+
+ rc = ldap_bv2rdn( rdn, &rDN, (char **)&text, LDAP_DN_FORMAT_LDAP );
+ if ( rc ) {
+ goto fail;
+ }
+ for ( cnt = 0; rDN[cnt]; cnt++ ) {
+ LDAPAVA *ava = rDN[cnt];
+
+ ad = NULL;
+ rc = slap_bv2ad( &ava->la_attr, &ad, &text );
+ if ( rc ) {
+ break;
+ }
+ if ( !ad->ad_type->sat_equality ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ text = "attribute has no equality matching rule";
+ break;
+ }
+ if ( !ad->ad_type->sat_equality->smr_match ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ text = "attribute has unsupported equality matching rule";
+ break;
+ }
+ attr_merge_normalize_one(e, ad, &ava->la_value, NULL );
+ }
+ ldap_rdnfree( rDN );
+ if ( rc ) {
+ goto fail;
+ }
+
+ oc = main->co_oc;
+ c->table = main->co_type;
+ if ( oc->soc_required ) {
+ rc = config_build_attrs( e, oc->soc_required, ad, main->co_table, c );
+ if ( rc ) goto fail;
+ }
+
+ if ( oc->soc_allowed ) {
+ rc = config_build_attrs( e, oc->soc_allowed, ad, main->co_table, c );
+ if ( rc ) goto fail;
+ }
+
+ if ( extra ) {
+ oc = extra->co_oc;
+ c->table = extra->co_type;
+ if ( oc->soc_required ) {
+ rc = config_build_attrs( e, oc->soc_required, ad, extra->co_table, c );
+ if ( rc ) goto fail;
+ }
+
+ if ( oc->soc_allowed ) {
+ rc = config_build_attrs( e, oc->soc_allowed, ad, extra->co_table, c );
+ if ( rc ) goto fail;
+ }
+ }
+
+ oc_at = attr_find( e->e_attrs, slap_schema.si_ad_objectClass );
+ rc = structural_class(oc_at->a_vals, &oc, NULL, &text, c->cr_msg,
+ sizeof(c->cr_msg), op ? op->o_tmpmemctx : NULL );
+ if ( rc != LDAP_SUCCESS ) {
+fail:
+ Debug( LDAP_DEBUG_ANY,
+ "config_build_entry: build \"%s\" failed: \"%s\"\n",
+ rdn->bv_val, text );
+ return NULL;
+ }
+ attr_merge_normalize_one(e, slap_schema.si_ad_structuralObjectClass, &oc->soc_cname, NULL );
+ if ( op ) {
+ op->ora_e = e;
+ op->ora_modlist = NULL;
+ slap_add_opattrs( op, NULL, NULL, 0, 0 );
+ if ( !op->o_noop ) {
+ SlapReply rs2 = {REP_RESULT};
+ op->o_bd->be_add( op, &rs2 );
+ rs->sr_err = rs2.sr_err;
+ rs_assert_done( &rs2 );
+ if ( ( rs2.sr_err != LDAP_SUCCESS )
+ && (rs2.sr_err != LDAP_ALREADY_EXISTS) ) {
+ goto fail;
+ }
+ }
+ }
+ if ( ceprev ) {
+ ce->ce_sibs = ceprev->ce_sibs;
+ ceprev->ce_sibs = ce;
+ } else if ( parent ) {
+ ce->ce_sibs = parent->ce_kids;
+ parent->ce_kids = ce;
+ }
+
+ return e;
+}
+
+static int
+config_build_schema_inc( ConfigArgs *c, CfEntryInfo *ceparent,
+ Operation *op, SlapReply *rs )
+{
+ Entry *e;
+ ConfigFile *cf = c->ca_private;
+ char *ptr;
+ struct berval bv, rdn;
+
+ for (; cf; cf=cf->c_sibs, c->depth++) {
+ if ( !cf->c_at_head && !cf->c_cr_head && !cf->c_oc_head &&
+ !cf->c_om_head && !cf->c_syn_head && !cf->c_kids ) continue;
+ c->value_dn.bv_val = c->log;
+ LUTIL_SLASHPATH( cf->c_file.bv_val );
+ bv.bv_val = strrchr(cf->c_file.bv_val, LDAP_DIRSEP[0]);
+ if ( !bv.bv_val ) {
+ bv = cf->c_file;
+ } else {
+ bv.bv_val++;
+ bv.bv_len = cf->c_file.bv_len - (bv.bv_val - cf->c_file.bv_val);
+ }
+ ptr = strchr( bv.bv_val, '.' );
+ if ( ptr )
+ bv.bv_len = ptr - bv.bv_val;
+ c->value_dn.bv_len = snprintf(c->value_dn.bv_val, sizeof( c->log ), "cn=" SLAP_X_ORDERED_FMT, c->depth);
+ if ( c->value_dn.bv_len >= sizeof( c->log ) ) {
+ /* FIXME: how can indicate error? */
+ return -1;
+ }
+ strncpy( c->value_dn.bv_val + c->value_dn.bv_len, bv.bv_val,
+ bv.bv_len );
+ c->value_dn.bv_len += bv.bv_len;
+ c->value_dn.bv_val[c->value_dn.bv_len] ='\0';
+ if ( rdnNormalize( 0, NULL, NULL, &c->value_dn, &rdn, NULL )) {
+ Debug( LDAP_DEBUG_ANY,
+ "config_build_schema_inc: invalid schema name \"%s\"\n",
+ bv.bv_val );
+ return -1;
+ }
+
+ c->ca_private = cf;
+ e = config_build_entry( op, rs, ceparent, c, &rdn,
+ &CFOC_SCHEMA, NULL );
+ ch_free( rdn.bv_val );
+ if ( !e ) {
+ return -1;
+ } else if ( e && cf->c_kids ) {
+ c->ca_private = cf->c_kids;
+ config_build_schema_inc( c, e->e_private, op, rs );
+ }
+ }
+ return 0;
+}
+
+#ifdef SLAPD_MODULES
+
+static int
+config_build_modules( ConfigArgs *c, CfEntryInfo *ceparent,
+ Operation *op, SlapReply *rs )
+{
+ int i;
+ ModPaths *mp;
+
+ for (i=0, mp=&modpaths; mp; mp=mp->mp_next, i++) {
+ if ( BER_BVISNULL( &mp->mp_path ) && !mp->mp_loads )
+ continue;
+ c->value_dn.bv_val = c->log;
+ c->value_dn.bv_len = snprintf(c->value_dn.bv_val, sizeof( c->log ), "cn=module" SLAP_X_ORDERED_FMT, i);
+ if ( c->value_dn.bv_len >= sizeof( c->log ) ) {
+ /* FIXME: how can indicate error? */
+ return -1;
+ }
+ c->ca_private = mp;
+ if ( ! config_build_entry( op, rs, ceparent, c, &c->value_dn, &CFOC_MODULE, NULL )) {
+ return -1;
+ }
+ }
+ return 0;
+}
+#endif
+
+static int
+config_check_schema(Operation *op, CfBackInfo *cfb)
+{
+ struct berval schema_dn = BER_BVC(SCHEMA_RDN "," CONFIG_RDN);
+ ConfigArgs c = {0};
+ CfEntryInfo *ce, *last;
+ Entry *e;
+
+ /* If there's no root entry, we must be in the midst of converting */
+ if ( !cfb->cb_root )
+ return 0;
+
+ /* Make sure the main schema entry exists */
+ ce = config_find_base( cfb->cb_root, &schema_dn, &last );
+ if ( ce ) {
+ Attribute *a;
+ struct berval *bv;
+
+ e = ce->ce_entry;
+
+ /* Make sure it's up to date */
+ if ( cf_om_tail != om_sys_tail ) {
+ a = attr_find( e->e_attrs, cfAd_om );
+ if ( a ) {
+ if ( a->a_nvals != a->a_vals )
+ ber_bvarray_free( a->a_nvals );
+ ber_bvarray_free( a->a_vals );
+ a->a_vals = NULL;
+ a->a_nvals = NULL;
+ a->a_numvals = 0;
+ }
+ oidm_unparse( &bv, NULL, NULL, 1 );
+ attr_merge_normalize( e, cfAd_om, bv, NULL );
+ ber_bvarray_free( bv );
+ cf_om_tail = om_sys_tail;
+ }
+ if ( cf_at_tail != at_sys_tail ) {
+ a = attr_find( e->e_attrs, cfAd_attr );
+ if ( a ) {
+ if ( a->a_nvals != a->a_vals )
+ ber_bvarray_free( a->a_nvals );
+ ber_bvarray_free( a->a_vals );
+ a->a_vals = NULL;
+ a->a_nvals = NULL;
+ a->a_numvals = 0;
+ }
+ at_unparse( &bv, NULL, NULL, 1 );
+ attr_merge_normalize( e, cfAd_attr, bv, NULL );
+ ber_bvarray_free( bv );
+ cf_at_tail = at_sys_tail;
+ }
+ if ( cf_oc_tail != oc_sys_tail ) {
+ a = attr_find( e->e_attrs, cfAd_oc );
+ if ( a ) {
+ if ( a->a_nvals != a->a_vals )
+ ber_bvarray_free( a->a_nvals );
+ ber_bvarray_free( a->a_vals );
+ a->a_vals = NULL;
+ a->a_nvals = NULL;
+ a->a_numvals = 0;
+ }
+ oc_unparse( &bv, NULL, NULL, 1 );
+ attr_merge_normalize( e, cfAd_oc, bv, NULL );
+ ber_bvarray_free( bv );
+ cf_oc_tail = oc_sys_tail;
+ }
+ if ( cf_syn_tail != syn_sys_tail ) {
+ a = attr_find( e->e_attrs, cfAd_syntax );
+ if ( a ) {
+ if ( a->a_nvals != a->a_vals )
+ ber_bvarray_free( a->a_nvals );
+ ber_bvarray_free( a->a_vals );
+ a->a_vals = NULL;
+ a->a_nvals = NULL;
+ a->a_numvals = 0;
+ }
+ syn_unparse( &bv, NULL, NULL, 1 );
+ attr_merge_normalize( e, cfAd_syntax, bv, NULL );
+ ber_bvarray_free( bv );
+ cf_syn_tail = syn_sys_tail;
+ }
+ } else {
+ SlapReply rs = {REP_RESULT};
+ c.ca_private = NULL;
+ e = config_build_entry( op, &rs, cfb->cb_root, &c, &schema_rdn,
+ &CFOC_SCHEMA, NULL );
+ if ( !e ) {
+ return -1;
+ }
+ ce = e->e_private;
+ ce->ce_private = cfb->cb_config;
+ cf_at_tail = at_sys_tail;
+ cf_oc_tail = oc_sys_tail;
+ cf_om_tail = om_sys_tail;
+ cf_syn_tail = syn_sys_tail;
+ }
+ return 0;
+}
+
+static const char *defacl[] = {
+ NULL, "to", "*", "by", "*", "none", NULL
+};
+
+static int
+config_back_db_open( BackendDB *be, ConfigReply *cr )
+{
+ CfBackInfo *cfb = be->be_private;
+ struct berval rdn;
+ Entry *e;
+ CfEntryInfo *ce, *ceparent;
+ int i, unsupp = 0;
+ BackendInfo *bi;
+ ConfigArgs c;
+ Connection conn = {0};
+ OperationBuffer opbuf;
+ Operation *op;
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+ SlapReply rs = {REP_RESULT};
+ void *thrctx = NULL;
+ AccessControl *save_access;
+
+ Debug( LDAP_DEBUG_TRACE, "config_back_db_open\n" );
+
+ /* If we have no explicitly configured ACLs, don't just use
+ * the global ACLs. Explicitly deny access to everything.
+ */
+ save_access = be->bd_self->be_acl;
+ be->bd_self->be_acl = NULL;
+ parse_acl(be->bd_self, "config_back_db_open", 0, 6, (char **)defacl, 0 );
+ defacl_parsed = be->bd_self->be_acl;
+ if ( save_access ) {
+ be->bd_self->be_acl = save_access;
+ } else {
+ Debug( LDAP_DEBUG_CONFIG, "config_back_db_open: "
+ "No explicit ACL for back-config configured. "
+ "Using hardcoded default\n" );
+ }
+
+ thrctx = ldap_pvt_thread_pool_context();
+ connection_fake_init( &conn, &opbuf, thrctx );
+ op = &opbuf.ob_op;
+
+ op->o_tag = LDAP_REQ_ADD;
+ op->o_callback = &cb;
+ op->o_bd = &cfb->cb_db;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ if ( !cfb->cb_use_ldif ) {
+ op->o_noop = 1;
+ }
+
+ /* If we read the config from back-ldif, do some quick sanity checks */
+ if ( cfb->cb_got_ldif ) {
+ return config_check_schema( op, cfb );
+ }
+
+ /* create root of tree */
+ rdn = config_rdn;
+ c.ca_private = cfb->cb_config;
+ c.be = frontendDB;
+ e = config_build_entry( op, &rs, NULL, &c, &rdn, &CFOC_GLOBAL, NULL );
+ if ( !e ) {
+ return -1;
+ }
+ ce = e->e_private;
+ cfb->cb_root = ce;
+
+ ceparent = ce;
+
+#ifdef SLAPD_MODULES
+ /* Create Module nodes... */
+ if ( modpaths.mp_loads ) {
+ if ( config_build_modules( &c, ceparent, op, &rs ) ){
+ return -1;
+ }
+ }
+#endif
+
+ /* Create schema nodes... cn=schema will contain the hardcoded core
+ * schema, read-only. Child objects will contain runtime loaded schema
+ * files.
+ */
+ rdn = schema_rdn;
+ c.ca_private = NULL;
+ e = config_build_entry( op, &rs, ceparent, &c, &rdn, &CFOC_SCHEMA, NULL );
+ if ( !e ) {
+ return -1;
+ }
+ ce = e->e_private;
+ ce->ce_private = cfb->cb_config;
+ cf_at_tail = at_sys_tail;
+ cf_oc_tail = oc_sys_tail;
+ cf_om_tail = om_sys_tail;
+ cf_syn_tail = syn_sys_tail;
+
+ /* Create schema nodes for included schema... */
+ if ( cfb->cb_config->c_kids ) {
+ int rc;
+ c.depth = 0;
+ c.ca_private = cfb->cb_config->c_kids;
+ rc = config_build_schema_inc( &c, ce, op, &rs );
+ if ( rc ) {
+ return -1;
+ }
+ }
+
+ /* Create backend nodes. Skip if they don't provide a cf_table.
+ * There usually aren't any of these.
+ */
+
+ c.line = 0;
+ i = 0;
+ LDAP_STAILQ_FOREACH( bi, &backendInfo, bi_next) {
+ if (!bi->bi_cf_ocs) {
+ /* If it only supports the old config mech, complain. */
+ if ( bi->bi_config ) {
+ Debug( LDAP_DEBUG_ANY,
+ "WARNING: No dynamic config support for backend %s.\n",
+ bi->bi_type );
+ unsupp++;
+ }
+ continue;
+ }
+ if ( !bi->bi_private && !(bi->bi_flags & SLAP_BFLAG_STANDALONE) ) continue;
+
+ rdn.bv_val = c.log;
+ rdn.bv_len = snprintf(rdn.bv_val, sizeof( c.log ),
+ "%s=" SLAP_X_ORDERED_FMT "%s", cfAd_backend->ad_cname.bv_val,
+ i, bi->bi_type);
+ if ( rdn.bv_len >= sizeof( c.log ) ) {
+ /* FIXME: holler ... */ ;
+ }
+ c.bi = bi;
+ e = config_build_entry( op, &rs, ceparent, &c, &rdn, &CFOC_BACKEND,
+ bi->bi_cf_ocs );
+ if ( !e ) {
+ return -1;
+ }
+ if ( bi->bi_cf_ocs && bi->bi_cf_ocs->co_cfadd ) {
+ rs_reinit( &rs, REP_RESULT );
+ bi->bi_cf_ocs->co_cfadd( op, &rs, e, &c );
+ }
+ i++;
+ }
+
+ /* Create database nodes... */
+ frontendDB->be_cf_ocs = &CFOC_FRONTEND;
+ LDAP_STAILQ_NEXT(frontendDB, be_next) = LDAP_STAILQ_FIRST(&backendDB);
+ for ( i = -1, be = frontendDB ; be;
+ i++, be = LDAP_STAILQ_NEXT( be, be_next )) {
+ slap_overinfo *oi = NULL;
+
+ if ( overlay_is_over( be )) {
+ oi = be->bd_info->bi_private;
+ bi = oi->oi_orig;
+ } else {
+ bi = be->bd_info;
+ }
+
+ /* If this backend supports the old config mechanism, but not
+ * the new mech, complain.
+ */
+ if ( !be->be_cf_ocs && bi->bi_db_config ) {
+ Debug( LDAP_DEBUG_ANY,
+ "WARNING: No dynamic config support for database %s.\n",
+ bi->bi_type );
+ unsupp++;
+ }
+ rdn.bv_val = c.log;
+ rdn.bv_len = snprintf(rdn.bv_val, sizeof( c.log ),
+ "%s=" SLAP_X_ORDERED_FMT "%s", cfAd_database->ad_cname.bv_val,
+ i, bi->bi_type);
+ if ( rdn.bv_len >= sizeof( c.log ) ) {
+ /* FIXME: holler ... */ ;
+ }
+ c.be = be;
+ c.bi = bi;
+ e = config_build_entry( op, &rs, ceparent, &c, &rdn, &CFOC_DATABASE,
+ be->be_cf_ocs );
+ if ( !e ) {
+ return -1;
+ }
+ ce = e->e_private;
+ if ( be->be_cf_ocs && be->be_cf_ocs->co_cfadd ) {
+ rs_reinit( &rs, REP_RESULT );
+ be->be_cf_ocs->co_cfadd( op, &rs, e, &c );
+ }
+ /* Iterate through overlays */
+ if ( oi ) {
+ slap_overinst *on;
+ Entry *oe;
+ int j;
+ voidList *vl, *v0 = NULL;
+
+ /* overlays are in LIFO order, must reverse stack */
+ for (on=oi->oi_list; on; on=on->on_next) {
+ vl = ch_malloc( sizeof( voidList ));
+ vl->vl_next = v0;
+ v0 = vl;
+ vl->vl_ptr = on;
+ }
+ for (j=0; vl; j++,vl=v0) {
+ on = vl->vl_ptr;
+ v0 = vl->vl_next;
+ ch_free( vl );
+ if ( on->on_bi.bi_db_config && !on->on_bi.bi_cf_ocs ) {
+ Debug( LDAP_DEBUG_ANY,
+ "WARNING: No dynamic config support for overlay %s.\n",
+ on->on_bi.bi_type );
+ unsupp++;
+ }
+ rdn.bv_val = c.log;
+ rdn.bv_len = snprintf(rdn.bv_val, sizeof( c.log ),
+ "%s=" SLAP_X_ORDERED_FMT "%s",
+ cfAd_overlay->ad_cname.bv_val, j, on->on_bi.bi_type );
+ if ( rdn.bv_len >= sizeof( c.log ) ) {
+ /* FIXME: holler ... */ ;
+ }
+ c.be = be;
+ c.bi = &on->on_bi;
+ oe = config_build_entry( op, &rs, ce, &c, &rdn,
+ &CFOC_OVERLAY, c.bi->bi_cf_ocs );
+ if ( !oe ) {
+ return -1;
+ }
+ if ( c.bi->bi_cf_ocs && c.bi->bi_cf_ocs->co_cfadd ) {
+ rs_reinit( &rs, REP_RESULT );
+ c.bi->bi_cf_ocs->co_cfadd( op, &rs, oe, &c );
+ }
+ }
+ }
+ }
+ if ( thrctx )
+ ldap_pvt_thread_pool_context_reset( thrctx );
+
+ if ( unsupp && cfb->cb_use_ldif ) {
+ Debug( LDAP_DEBUG_ANY, "\nWARNING: The converted cn=config "
+ "directory is incomplete and may not work.\n\n" );
+ }
+
+ return 0;
+}
+
+static void
+cfb_free_cffile( ConfigFile *cf )
+{
+ ConfigFile *next;
+
+ for (; cf; cf=next) {
+ next = cf->c_sibs;
+ if ( cf->c_kids )
+ cfb_free_cffile( cf->c_kids );
+ ch_free( cf->c_file.bv_val );
+ ber_bvarray_free( cf->c_dseFiles );
+ ch_free( cf );
+ }
+}
+
+static void
+cfb_free_entries( CfEntryInfo *ce )
+{
+ CfEntryInfo *next;
+
+ for (; ce; ce=next) {
+ next = ce->ce_sibs;
+ if ( ce->ce_kids )
+ cfb_free_entries( ce->ce_kids );
+ ce->ce_entry->e_private = NULL;
+ entry_free( ce->ce_entry );
+ ch_free( ce );
+ }
+}
+
+static int
+config_back_db_close( BackendDB *be, ConfigReply *cr )
+{
+ CfBackInfo *cfb = be->be_private;
+
+ cfb_free_entries( cfb->cb_root );
+ cfb->cb_root = NULL;
+
+ if ( cfb->cb_db.bd_info ) {
+ backend_shutdown( &cfb->cb_db );
+ }
+
+ if ( defacl_parsed && be->be_acl != defacl_parsed ) {
+ acl_free( defacl_parsed );
+ defacl_parsed = NULL;
+ }
+
+ return 0;
+}
+
+static int
+config_back_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ CfBackInfo *cfb = be->be_private;
+
+ cfb_free_cffile( cfb->cb_config );
+
+ ch_free( cfdir.bv_val );
+
+ ldap_pvt_thread_rdwr_destroy( &cfb->cb_rwlock );
+
+ ldap_avl_free( CfOcTree, NULL );
+
+ if ( cfb->cb_db.bd_info ) {
+ cfb->cb_db.be_suffix = NULL;
+ cfb->cb_db.be_nsuffix = NULL;
+ BER_BVZERO( &cfb->cb_db.be_rootdn );
+ BER_BVZERO( &cfb->cb_db.be_rootndn );
+
+ backend_destroy_one( &cfb->cb_db, 0 );
+ }
+
+ loglevel_destroy();
+
+ return 0;
+}
+
+static int
+config_back_db_init( BackendDB *be, ConfigReply* cr )
+{
+ struct berval dn;
+ CfBackInfo *cfb;
+
+ cfb = &cfBackInfo;
+ cfb->cb_config = ch_calloc( 1, sizeof(ConfigFile));
+ cfn = cfb->cb_config;
+ be->be_private = cfb;
+
+ ber_dupbv( &be->be_rootdn, &config_rdn );
+ ber_dupbv( &be->be_rootndn, &be->be_rootdn );
+ ber_dupbv( &dn, &be->be_rootdn );
+ ber_bvarray_add( &be->be_suffix, &dn );
+ ber_dupbv( &dn, &be->be_rootdn );
+ ber_bvarray_add( &be->be_nsuffix, &dn );
+
+ ldap_pvt_thread_rdwr_init( &cfb->cb_rwlock );
+
+ /* Hide from namingContexts */
+ SLAP_BFLAGS(be) |= SLAP_BFLAG_CONFIG;
+
+ /* Check ACLs on content of Adds by default */
+ SLAP_DBFLAGS(be) |= SLAP_DBFLAG_ACL_ADD;
+
+ return 0;
+}
+
+static int
+config_back_destroy( BackendInfo *bi )
+{
+ ldif_must_b64_encode_release();
+ return 0;
+}
+
+static int
+config_tool_entry_open( BackendDB *be, int mode )
+{
+ CfBackInfo *cfb = be->be_private;
+ BackendInfo *bi = cfb->cb_db.bd_info;
+
+ configDB = be;
+ if ( bi && bi->bi_tool_entry_open )
+ return bi->bi_tool_entry_open( &cfb->cb_db, mode );
+ else
+ return -1;
+
+}
+
+static int
+config_tool_entry_close( BackendDB *be )
+{
+ CfBackInfo *cfb = be->be_private;
+ BackendInfo *bi = cfb->cb_db.bd_info;
+
+ if ( bi && bi->bi_tool_entry_close )
+ return bi->bi_tool_entry_close( &cfb->cb_db );
+ else
+ return -1;
+}
+
+static ID
+config_tool_entry_first( BackendDB *be )
+{
+ CfBackInfo *cfb = be->be_private;
+ BackendInfo *bi = cfb->cb_db.bd_info;
+
+ if ( bi && bi->bi_tool_entry_first ) {
+ return bi->bi_tool_entry_first( &cfb->cb_db );
+ }
+ if ( bi && bi->bi_tool_entry_first_x ) {
+ return bi->bi_tool_entry_first_x( &cfb->cb_db,
+ NULL, LDAP_SCOPE_DEFAULT, NULL );
+ }
+ return NOID;
+}
+
+static ID
+config_tool_entry_first_x(
+ BackendDB *be,
+ struct berval *base,
+ int scope,
+ Filter *f )
+{
+ CfBackInfo *cfb = be->be_private;
+ BackendInfo *bi = cfb->cb_db.bd_info;
+
+ if ( bi && bi->bi_tool_entry_first_x ) {
+ return bi->bi_tool_entry_first_x( &cfb->cb_db, base, scope, f );
+ }
+ return NOID;
+}
+
+static ID
+config_tool_entry_next( BackendDB *be )
+{
+ CfBackInfo *cfb = be->be_private;
+ BackendInfo *bi = cfb->cb_db.bd_info;
+
+ if ( bi && bi->bi_tool_entry_next )
+ return bi->bi_tool_entry_next( &cfb->cb_db );
+ else
+ return NOID;
+}
+
+static ID
+config_tool_dn2id_get( Backend *be, struct berval *dn )
+{
+ CfBackInfo *cfb = be->be_private;
+ BackendInfo *bi = cfb->cb_db.bd_info;
+
+ if ( bi && bi->bi_tool_dn2id_get )
+ return bi->bi_tool_dn2id_get( &cfb->cb_db, dn );
+
+ return NOID;
+}
+
+static Entry *
+config_tool_entry_get( BackendDB *be, ID id )
+{
+ CfBackInfo *cfb = be->be_private;
+ BackendInfo *bi = cfb->cb_db.bd_info;
+
+ if ( bi && bi->bi_tool_entry_get )
+ return bi->bi_tool_entry_get( &cfb->cb_db, id );
+ else
+ return NULL;
+}
+
+static int entry_put_got_frontend=0;
+static int entry_put_got_config=0;
+static ID
+config_tool_entry_put( BackendDB *be, Entry *e, struct berval *text )
+{
+ CfBackInfo *cfb = be->be_private;
+ BackendInfo *bi = cfb->cb_db.bd_info;
+ int rc;
+ struct berval rdn;
+ ConfigArgs ca;
+ OperationBuffer opbuf;
+ Entry *ce;
+ Connection conn = {0};
+ Operation *op = NULL;
+ void *thrctx;
+ int isFrontend = 0;
+ int isFrontendChild = 0;
+
+ /* Create entry for frontend database if it does not exist already */
+ if ( !entry_put_got_frontend ) {
+ if ( !strncmp( e->e_nname.bv_val, "olcDatabase",
+ STRLENOF( "olcDatabase" ))) {
+ if ( strncmp( e->e_nname.bv_val +
+ STRLENOF( "olcDatabase" ), "={-1}frontend",
+ STRLENOF( "={-1}frontend" )) &&
+ strncmp( e->e_nname.bv_val +
+ STRLENOF( "olcDatabase" ), "=frontend",
+ STRLENOF( "=frontend" ))) {
+ memset( &ca, 0, sizeof(ConfigArgs));
+ ca.be = frontendDB;
+ ca.bi = frontendDB->bd_info;
+ ca.be->be_cf_ocs = &CFOC_FRONTEND;
+ rdn.bv_val = ca.log;
+ rdn.bv_len = snprintf(rdn.bv_val, sizeof( ca.log ),
+ "%s=" SLAP_X_ORDERED_FMT "%s",
+ cfAd_database->ad_cname.bv_val, -1,
+ ca.bi->bi_type);
+ ce = config_build_entry( NULL, NULL, cfb->cb_root, &ca, &rdn,
+ &CFOC_DATABASE, ca.be->be_cf_ocs );
+ thrctx = ldap_pvt_thread_pool_context();
+ connection_fake_init2( &conn, &opbuf, thrctx,0 );
+ op = &opbuf.ob_op;
+ op->o_bd = &cfb->cb_db;
+ op->o_tag = LDAP_REQ_ADD;
+ op->ora_e = ce;
+ op->o_dn = be->be_rootdn;
+ op->o_ndn = be->be_rootndn;
+ rc = slap_add_opattrs(op, NULL, NULL, 0, 0);
+ if ( rc != LDAP_SUCCESS ) {
+ text->bv_val = "autocreation of \"olcDatabase={-1}frontend\" failed";
+ text->bv_len = STRLENOF("autocreation of \"olcDatabase={-1}frontend\" failed");
+ return NOID;
+ }
+
+ if ( ce && bi && bi->bi_tool_entry_put &&
+ bi->bi_tool_entry_put( &cfb->cb_db, ce, text ) != NOID ) {
+ entry_put_got_frontend++;
+ } else {
+ text->bv_val = "autocreation of \"olcDatabase={-1}frontend\" failed";
+ text->bv_len = STRLENOF("autocreation of \"olcDatabase={-1}frontend\" failed");
+ return NOID;
+ }
+ } else {
+ entry_put_got_frontend++;
+ isFrontend = 1;
+ }
+ }
+ }
+
+ /* Child entries of the frontend database, e.g. slapo-chain's back-ldap
+ * instances, may appear before the config database entry in the ldif, skip
+ * auto-creation of olcDatabase={0}config in such a case */
+ if ( !entry_put_got_config &&
+ !strncmp( e->e_nname.bv_val, "olcDatabase", STRLENOF( "olcDatabase" ))) {
+ struct berval pdn;
+ dnParent( &e->e_nname, &pdn );
+ while ( pdn.bv_len ) {
+ if ( !strncmp( pdn.bv_val, "olcDatabase",
+ STRLENOF( "olcDatabase" ))) {
+ if ( !strncmp( pdn.bv_val +
+ STRLENOF( "olcDatabase" ), "={-1}frontend",
+ STRLENOF( "={-1}frontend" )) ||
+ !strncmp( pdn.bv_val +
+ STRLENOF( "olcDatabase" ), "=frontend",
+ STRLENOF( "=frontend" ))) {
+
+ isFrontendChild = 1;
+ break;
+ }
+ }
+ dnParent( &pdn, &pdn );
+ }
+ }
+
+ /* Create entry for config database if it does not exist already */
+ if ( !entry_put_got_config && !isFrontend && !isFrontendChild ) {
+ if ( !strncmp( e->e_nname.bv_val, "olcDatabase",
+ STRLENOF( "olcDatabase" ))) {
+ if ( strncmp( e->e_nname.bv_val +
+ STRLENOF( "olcDatabase" ), "={0}config",
+ STRLENOF( "={0}config" )) &&
+ strncmp( e->e_nname.bv_val +
+ STRLENOF( "olcDatabase" ), "=config",
+ STRLENOF( "=config" )) ) {
+ memset( &ca, 0, sizeof(ConfigArgs));
+ ca.be = LDAP_STAILQ_FIRST( &backendDB );
+ ca.bi = ca.be->bd_info;
+ rdn.bv_val = ca.log;
+ rdn.bv_len = snprintf(rdn.bv_val, sizeof( ca.log ),
+ "%s=" SLAP_X_ORDERED_FMT "%s",
+ cfAd_database->ad_cname.bv_val, 0,
+ ca.bi->bi_type);
+ ce = config_build_entry( NULL, NULL, cfb->cb_root, &ca, &rdn, &CFOC_DATABASE,
+ ca.be->be_cf_ocs );
+ if ( ! op ) {
+ thrctx = ldap_pvt_thread_pool_context();
+ connection_fake_init2( &conn, &opbuf, thrctx,0 );
+ op = &opbuf.ob_op;
+ op->o_bd = &cfb->cb_db;
+ op->o_tag = LDAP_REQ_ADD;
+ op->o_dn = be->be_rootdn;
+ op->o_ndn = be->be_rootndn;
+ }
+ op->ora_e = ce;
+ rc = slap_add_opattrs(op, NULL, NULL, 0, 0);
+ if ( rc != LDAP_SUCCESS ) {
+ text->bv_val = "autocreation of \"olcDatabase={0}config\" failed";
+ text->bv_len = STRLENOF("autocreation of \"olcDatabase={0}config\" failed");
+ return NOID;
+ }
+ if (ce && bi && bi->bi_tool_entry_put &&
+ bi->bi_tool_entry_put( &cfb->cb_db, ce, text ) != NOID ) {
+ entry_put_got_config++;
+ } else {
+ text->bv_val = "autocreation of \"olcDatabase={0}config\" failed";
+ text->bv_len = STRLENOF("autocreation of \"olcDatabase={0}config\" failed");
+ return NOID;
+ }
+ } else {
+ entry_put_got_config++;
+ }
+ }
+ }
+ if ( bi && bi->bi_tool_entry_put &&
+ config_add_internal( cfb, e, &ca, NULL, NULL, NULL ) == 0 )
+ return bi->bi_tool_entry_put( &cfb->cb_db, e, text );
+ else {
+ ber_str2bv( ca.cr_msg, 0, 0, text );
+ return NOID;
+ }
+}
+
+static ID
+config_tool_entry_modify( BackendDB *be, Entry *e, struct berval *text )
+{
+ CfBackInfo *cfb = be->be_private;
+ BackendInfo *bi = cfb->cb_db.bd_info;
+ CfEntryInfo *ce, *last;
+
+ ce = config_find_base( cfb->cb_root, &e->e_nname, &last );
+
+ if ( ce && bi && bi->bi_tool_entry_modify )
+ return bi->bi_tool_entry_modify( &cfb->cb_db, e, text );
+
+ return NOID;
+}
+
+static int
+config_tool_entry_delete( BackendDB *be, struct berval *ndn, struct berval *text )
+{
+ CfBackInfo *cfb = be->be_private;
+ BackendInfo *bi = cfb->cb_db.bd_info;
+ CfEntryInfo *ce, *last;
+
+ ce = config_find_base( cfb->cb_root, ndn, &last );
+
+ if ( ce && bi && bi->bi_tool_entry_delete )
+ return bi->bi_tool_entry_delete( &cfb->cb_db, ndn, text );
+
+ return LDAP_OTHER;
+}
+
+static struct {
+ char *name;
+ AttributeDescription **desc;
+} ads[] = {
+ { "attribute", &cfAd_attr },
+ { "backend", &cfAd_backend },
+ { "database", &cfAd_database },
+ { "include", &cfAd_include },
+ { "ldapsyntax", &cfAd_syntax },
+ { "objectclass", &cfAd_oc },
+ { "objectidentifier", &cfAd_om },
+ { "overlay", &cfAd_overlay },
+ { NULL, NULL }
+};
+
+/* Notes:
+ * add / delete: all types that may be added or deleted must use an
+ * X-ORDERED attributeType for their RDN. Adding and deleting entries
+ * should automatically renumber the index of any siblings as needed,
+ * so that no gaps in the numbering sequence exist after the add/delete
+ * is completed.
+ * What can be added:
+ * schema objects
+ * backend objects for backend-specific config directives
+ * database objects
+ * overlay objects
+ *
+ * delete: probably no support this time around.
+ *
+ * modrdn: generally not done. Will be invoked automatically by add/
+ * delete to update numbering sequence. Perform as an explicit operation
+ * so that the renumbering effect may be replicated. Subtree rename must
+ * be supported, since renumbering a database will affect all its child
+ * overlays.
+ *
+ * modify: must be fully supported.
+ */
+
+int
+config_back_initialize( BackendInfo *bi )
+{
+ ConfigTable *ct = config_back_cf_table;
+ ConfigArgs ca;
+ char *argv[4];
+ int i;
+ AttributeDescription *ad = NULL;
+ const char *text;
+ static char *controls[] = {
+ LDAP_CONTROL_MANAGEDSAIT,
+ NULL
+ };
+
+ /* Make sure we don't exceed the bits reserved for userland */
+ config_check_userland( CFG_LAST );
+
+ bi->bi_controls = controls;
+
+ bi->bi_open = 0;
+ bi->bi_close = 0;
+ bi->bi_config = 0;
+ bi->bi_destroy = config_back_destroy;
+
+ bi->bi_db_init = config_back_db_init;
+ bi->bi_db_config = 0;
+ bi->bi_db_open = config_back_db_open;
+ bi->bi_db_close = config_back_db_close;
+ bi->bi_db_destroy = config_back_db_destroy;
+
+ bi->bi_op_bind = config_back_bind;
+ bi->bi_op_unbind = 0;
+ bi->bi_op_search = config_back_search;
+ bi->bi_op_compare = 0;
+ bi->bi_op_modify = config_back_modify;
+ bi->bi_op_modrdn = config_back_modrdn;
+ bi->bi_op_add = config_back_add;
+ bi->bi_op_delete = config_back_delete;
+ bi->bi_op_abandon = 0;
+
+ bi->bi_extended = 0;
+
+ bi->bi_chk_referrals = 0;
+
+ bi->bi_access_allowed = slap_access_allowed;
+
+ bi->bi_connection_init = 0;
+ bi->bi_connection_destroy = 0;
+
+ bi->bi_entry_release_rw = config_entry_release;
+ bi->bi_entry_get_rw = config_back_entry_get;
+
+ bi->bi_tool_entry_open = config_tool_entry_open;
+ bi->bi_tool_entry_close = config_tool_entry_close;
+ bi->bi_tool_entry_first = config_tool_entry_first;
+ bi->bi_tool_entry_first_x = config_tool_entry_first_x;
+ bi->bi_tool_entry_next = config_tool_entry_next;
+ bi->bi_tool_dn2id_get = config_tool_dn2id_get;
+ bi->bi_tool_entry_get = config_tool_entry_get;
+ bi->bi_tool_entry_put = config_tool_entry_put;
+ bi->bi_tool_entry_modify = config_tool_entry_modify;
+ bi->bi_tool_entry_delete = config_tool_entry_delete;
+
+ ca.argv = argv;
+ argv[ 0 ] = "slapd";
+ ca.argv = argv;
+ ca.argc = 3;
+ ca.fname = argv[0];
+
+ argv[3] = NULL;
+ for (i=0; OidMacros[i].name; i++ ) {
+ argv[1] = OidMacros[i].name;
+ argv[2] = OidMacros[i].oid;
+ parse_oidm( &ca, 0, NULL );
+ }
+
+ bi->bi_cf_ocs = cf_ocs;
+
+ i = config_register_schema( ct, cf_ocs );
+ if ( i ) return i;
+
+ i = slap_str2ad( "olcDatabase", &olcDatabaseDummy[0].ad, &text );
+ if ( i ) return i;
+
+ /* setup olcRootPW to be base64-encoded when written in LDIF form;
+ * basically, we don't care if it fails */
+ i = slap_str2ad( "olcRootPW", &ad, &text );
+ if ( i ) {
+ Debug( LDAP_DEBUG_ANY, "config_back_initialize: "
+ "warning, unable to get \"olcRootPW\" "
+ "attribute description: %d: %s\n",
+ i, text );
+ } else {
+ (void)ldif_must_b64_encode_register( ad->ad_cname.bv_val,
+ ad->ad_type->sat_oid );
+ }
+
+ /* set up the notable AttributeDescriptions */
+ i = 0;
+ for (;ct->name;ct++) {
+ if (strcmp(ct->name, ads[i].name)) continue;
+ *ads[i].desc = ct->ad;
+ i++;
+ if (!ads[i].name) break;
+ }
+
+ return 0;
+}
diff --git a/servers/slapd/bind.c b/servers/slapd/bind.c
new file mode 100644
index 0000000..de602c9
--- /dev/null
+++ b/servers/slapd/bind.c
@@ -0,0 +1,555 @@
+/* bind.c - decode an ldap bind operation and pass it to a backend db */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "lutil.h"
+#include "slap.h"
+
+int
+do_bind(
+ Operation *op,
+ SlapReply *rs )
+{
+ BerElement *ber = op->o_ber;
+ ber_int_t version;
+ ber_tag_t method;
+ struct berval mech = BER_BVNULL;
+ struct berval dn = BER_BVNULL;
+ ber_tag_t tag;
+ Backend *be = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "%s do_bind\n",
+ op->o_log_prefix );
+
+ /*
+ * Force the connection to "anonymous" until bind succeeds.
+ */
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+ if ( op->o_conn->c_sasl_bind_in_progress ) {
+ be = op->o_conn->c_authz_backend;
+ }
+ if ( !BER_BVISEMPTY( &op->o_conn->c_dn ) ) {
+ /* log authorization identity demotion */
+ Debug( LDAP_DEBUG_STATS,
+ "%s BIND anonymous mech=implicit bind_ssf=0 ssf=%d\n",
+ op->o_log_prefix, op->o_conn->c_ssf );
+ }
+ connection2anonymous( op->o_conn );
+ if ( op->o_conn->c_sasl_bind_in_progress ) {
+ op->o_conn->c_authz_backend = be;
+ }
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+ if ( !BER_BVISNULL( &op->o_dn ) ) {
+ /* NOTE: temporarily wasting few bytes
+ * (until bind is completed), but saving
+ * a couple of ch_free() and ch_strdup("") */
+ op->o_dn.bv_val[0] = '\0';
+ op->o_dn.bv_len = 0;
+ }
+ if ( !BER_BVISNULL( &op->o_ndn ) ) {
+ op->o_ndn.bv_val[0] = '\0';
+ op->o_ndn.bv_len = 0;
+ }
+
+ /*
+ * Parse the bind request. It looks like this:
+ *
+ * BindRequest ::= SEQUENCE {
+ * version INTEGER, -- version
+ * name DistinguishedName, -- dn
+ * authentication CHOICE {
+ * simple [0] OCTET STRING -- passwd
+ * krbv42ldap [1] OCTET STRING -- OBSOLETE
+ * krbv42dsa [2] OCTET STRING -- OBSOLETE
+ * SASL [3] SaslCredentials
+ * }
+ * }
+ *
+ * SaslCredentials ::= SEQUENCE {
+ * mechanism LDAPString,
+ * credentials OCTET STRING OPTIONAL
+ * }
+ */
+
+ tag = ber_scanf( ber, "{imt" /*}*/, &version, &dn, &method );
+
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_bind: ber_scanf failed\n",
+ op->o_log_prefix );
+ send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
+ rs->sr_err = SLAPD_DISCONNECT;
+ goto cleanup;
+ }
+
+ op->o_protocol = version;
+ op->orb_method = method;
+
+ if( op->orb_method != LDAP_AUTH_SASL ) {
+ tag = ber_scanf( ber, /*{*/ "m}", &op->orb_cred );
+
+ } else {
+ tag = ber_scanf( ber, "{m" /*}*/, &mech );
+
+ if ( tag != LBER_ERROR ) {
+ ber_len_t len;
+ tag = ber_peek_tag( ber, &len );
+
+ if ( tag == LDAP_TAG_LDAPCRED ) {
+ tag = ber_scanf( ber, "m", &op->orb_cred );
+ } else {
+ tag = LDAP_TAG_LDAPCRED;
+ BER_BVZERO( &op->orb_cred );
+ }
+
+ if ( tag != LBER_ERROR ) {
+ tag = ber_scanf( ber, /*{{*/ "}}" );
+ }
+ }
+ }
+
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_bind: ber_scanf failed\n",
+ op->o_log_prefix );
+ send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
+ rs->sr_err = SLAPD_DISCONNECT;
+ goto cleanup;
+ }
+
+ if( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_bind: get_ctrls failed\n",
+ op->o_log_prefix );
+ goto cleanup;
+ }
+
+ /* We use the tmpmemctx here because it speeds up normalization.
+ * However, we must dup with regular malloc when storing any
+ * resulting DNs in the op or conn structures.
+ */
+ rs->sr_err = dnPrettyNormal( NULL, &dn, &op->o_req_dn, &op->o_req_ndn,
+ op->o_tmpmemctx );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_bind: invalid dn (%s)\n",
+ op->o_log_prefix, dn.bv_val );
+ send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid DN" );
+ goto cleanup;
+ }
+
+ Debug( LDAP_DEBUG_STATS, "%s BIND dn=\"%s\" method=%ld\n",
+ op->o_log_prefix, op->o_req_dn.bv_val,
+ (unsigned long) op->orb_method );
+
+ if( op->orb_method == LDAP_AUTH_SASL ) {
+ Debug( LDAP_DEBUG_TRACE, "do_bind: dn (%s) SASL mech %s\n",
+ op->o_req_dn.bv_val, mech.bv_val );
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ "do_bind: version=%ld dn=\"%s\" method=%ld\n",
+ (unsigned long) version, op->o_req_dn.bv_val,
+ (unsigned long) op->orb_method );
+ }
+
+ if ( version < LDAP_VERSION_MIN || version > LDAP_VERSION_MAX ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_bind: unknown version=%ld\n",
+ op->o_log_prefix, (unsigned long) version );
+ send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR,
+ "requested protocol version not supported" );
+ goto cleanup;
+
+ } else if (!( global_allows & SLAP_ALLOW_BIND_V2 ) &&
+ version < LDAP_VERSION3 )
+ {
+ send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR,
+ "historical protocol version requested, use LDAPv3 instead" );
+ goto cleanup;
+ }
+
+ /*
+ * we set connection version regardless of whether bind succeeds or not.
+ */
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+ op->o_conn->c_protocol = version;
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+
+ op->orb_mech = mech;
+
+ op->o_bd = frontendDB;
+ rs->sr_err = frontendDB->be_bind( op, rs );
+
+cleanup:
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ if ( op->orb_method != LDAP_AUTH_SASL ) {
+ ber_dupbv( &op->o_conn->c_authmech, &mech );
+ }
+ op->o_conn->c_authtype = op->orb_method;
+ }
+
+ if( !BER_BVISNULL( &op->o_req_dn ) ) {
+ slap_sl_free( op->o_req_dn.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &op->o_req_dn );
+ }
+ if( !BER_BVISNULL( &op->o_req_ndn ) ) {
+ slap_sl_free( op->o_req_ndn.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &op->o_req_ndn );
+ }
+
+ return rs->sr_err;
+}
+
+int
+fe_op_bind( Operation *op, SlapReply *rs )
+{
+ BackendDB *bd = op->o_bd;
+
+ /* check for inappropriate controls */
+ if( get_manageDSAit( op ) == SLAP_CONTROL_CRITICAL ) {
+ send_ldap_error( op, rs,
+ LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
+ "manageDSAit control inappropriate" );
+ goto cleanup;
+ }
+
+ if ( op->orb_method == LDAP_AUTH_SASL ) {
+ if ( op->o_protocol < LDAP_VERSION3 ) {
+ Debug( LDAP_DEBUG_ANY, "do_bind: sasl with LDAPv%ld\n",
+ (unsigned long)op->o_protocol );
+ send_ldap_discon( op, rs,
+ LDAP_PROTOCOL_ERROR, "SASL bind requires LDAPv3" );
+ rs->sr_err = SLAPD_DISCONNECT;
+ goto cleanup;
+ }
+
+ if( BER_BVISNULL( &op->orb_mech ) || BER_BVISEMPTY( &op->orb_mech ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "do_bind: no sasl mechanism provided\n" );
+ send_ldap_error( op, rs, LDAP_AUTH_METHOD_NOT_SUPPORTED,
+ "no SASL mechanism provided" );
+ goto cleanup;
+ }
+
+ /* check restrictions */
+ if( backend_check_restrictions( op, rs, &op->orb_mech ) != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+ if ( op->o_conn->c_sasl_bind_in_progress ) {
+ if( !bvmatch( &op->o_conn->c_sasl_bind_mech, &op->orb_mech ) ) {
+ /* mechanism changed between bind steps */
+ slap_sasl_reset(op->o_conn);
+ }
+ } else {
+ ber_dupbv(&op->o_conn->c_sasl_bind_mech, &op->orb_mech);
+ }
+
+ /* Set the bindop for the benefit of in-directory SASL lookups */
+ op->o_conn->c_sasl_bindop = op;
+
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+
+ rs->sr_err = slap_sasl_bind( op, rs );
+
+ goto cleanup;
+
+ } else {
+ /* Not SASL, cancel any in-progress bind */
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+
+ if ( !BER_BVISNULL( &op->o_conn->c_sasl_bind_mech ) ) {
+ free( op->o_conn->c_sasl_bind_mech.bv_val );
+ BER_BVZERO( &op->o_conn->c_sasl_bind_mech );
+ }
+ op->o_conn->c_sasl_bind_in_progress = 0;
+
+ slap_sasl_reset( op->o_conn );
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+ }
+
+ if ( op->orb_method == LDAP_AUTH_SIMPLE ) {
+ BER_BVSTR( &op->orb_mech, "SIMPLE" );
+ /* accept "anonymous" binds */
+ if ( BER_BVISEMPTY( &op->orb_cred ) || BER_BVISEMPTY( &op->o_req_ndn ) ) {
+ rs->sr_err = LDAP_SUCCESS;
+
+ if( !BER_BVISEMPTY( &op->orb_cred ) &&
+ !( global_allows & SLAP_ALLOW_BIND_ANON_CRED ))
+ {
+ /* cred is not empty, disallow */
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+
+ } else if ( !BER_BVISEMPTY( &op->o_req_ndn ) &&
+ !( global_allows & SLAP_ALLOW_BIND_ANON_DN ))
+ {
+ /* DN is not empty, disallow */
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text =
+ "unauthenticated bind (DN with no password) disallowed";
+
+ } else if ( global_disallows & SLAP_DISALLOW_BIND_ANON ) {
+ /* disallow */
+ rs->sr_err = LDAP_INAPPROPRIATE_AUTH;
+ rs->sr_text = "anonymous bind disallowed";
+
+ } else {
+ backend_check_restrictions( op, rs, &op->orb_mech );
+ }
+
+ /*
+ * we already forced connection to "anonymous",
+ * just need to send success
+ */
+ send_ldap_result( op, rs );
+ Debug( LDAP_DEBUG_TRACE, "do_bind: v%d anonymous bind\n",
+ op->o_protocol );
+ goto cleanup;
+
+ } else if ( global_disallows & SLAP_DISALLOW_BIND_SIMPLE ) {
+ /* disallow simple authentication */
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "unwilling to perform simple authentication";
+
+ send_ldap_result( op, rs );
+ Debug( LDAP_DEBUG_TRACE,
+ "do_bind: v%d simple bind(%s) disallowed\n",
+ op->o_protocol, op->o_req_ndn.bv_val );
+ goto cleanup;
+ }
+
+ } else {
+ rs->sr_err = LDAP_AUTH_METHOD_NOT_SUPPORTED;
+ rs->sr_text = "unknown authentication method";
+
+ send_ldap_result( op, rs );
+ Debug( LDAP_DEBUG_TRACE,
+ "do_bind: v%d unknown authentication method (%d)\n",
+ op->o_protocol, op->orb_method );
+ goto cleanup;
+ }
+
+ /*
+ * We could be serving multiple database backends. Select the
+ * appropriate one, or send a referral to our "referral server"
+ * if we don't hold it.
+ */
+
+ if ( (op->o_bd = select_backend( &op->o_req_ndn, 0 )) == NULL ) {
+ /* don't return referral for bind requests */
+ /* noSuchObject is not allowed to be returned by bind */
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ op->o_bd = bd;
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ /* check restrictions */
+ if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ if( op->o_bd->be_bind ) {
+ op->o_conn->c_authz_cookie = NULL;
+
+ rs->sr_err = (op->o_bd->be_bind)( op, rs );
+
+ if ( rs->sr_err == 0 ) {
+ (void)fe_op_bind_success( op, rs );
+
+ } else if ( !BER_BVISNULL( &op->orb_edn ) ) {
+ free( op->orb_edn.bv_val );
+ BER_BVZERO( &op->orb_edn );
+ }
+
+ } else {
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "operation not supported within naming context" );
+ }
+
+cleanup:;
+ op->o_bd = bd;
+ return rs->sr_err;
+}
+
+int
+fe_op_lastbind( Operation *op )
+{
+ Operation op2 = *op;
+ SlapReply r2 = { REP_RESULT };
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+ LDAPControl c, *ca[2];
+ Modifications *m;
+ Entry *e;
+ Attribute *a;
+ char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+ struct berval timestamp;
+ time_t bindtime = (time_t)-1;
+ int rc;
+
+ rc = be_entry_get_rw( op, &op->o_conn->c_ndn, NULL, NULL, 0, &e );
+ if ( rc != LDAP_SUCCESS ) {
+ return -1;
+ }
+
+ /* get authTimestamp attribute, if it exists */
+ if ( (a = attr_find( e->e_attrs, slap_schema.si_ad_pwdLastSuccess )) != NULL ) {
+ struct lutil_tm tm;
+ struct lutil_timet tt;
+
+ if ( lutil_parsetime( a->a_nvals[0].bv_val, &tm ) == 0 ) {
+ lutil_tm2time( &tm, &tt );
+ bindtime = tt.tt_sec;
+ }
+ Debug( LDAP_DEBUG_TRACE, "fe_op_lastbind: "
+ "old pwdLastSuccess value=%s %lds ago\n",
+ a->a_nvals[0].bv_val, bindtime == (time_t)-1 ? -1 : op->o_time - bindtime );
+
+ /*
+ * TODO: If the recorded bind time is within configurable precision,
+ * it doesn't need to be updated (save a write for nothing)
+ */
+ if ( bindtime != (time_t)-1 && op->o_time <= bindtime ) {
+ be_entry_release_r( op, e );
+ return LDAP_SUCCESS;
+ }
+ }
+
+ /* update the authTimestamp in the user's entry with the current time */
+ timestamp.bv_val = nowstr;
+ timestamp.bv_len = sizeof(nowstr);
+ slap_timestamp( &op->o_time, &timestamp );
+
+ m = ch_calloc( sizeof(Modifications), 1 );
+ m->sml_op = LDAP_MOD_REPLACE;
+ m->sml_flags = 0;
+ m->sml_type = slap_schema.si_ad_pwdLastSuccess->ad_cname;
+ m->sml_desc = slap_schema.si_ad_pwdLastSuccess;
+ m->sml_numvals = 1;
+ m->sml_values = ch_calloc( sizeof(struct berval), 2 );
+ m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
+
+ ber_dupbv( &m->sml_values[0], &timestamp );
+ ber_dupbv( &m->sml_nvalues[0], &timestamp );
+
+ be_entry_release_r( op, e );
+
+ op2.o_tag = LDAP_REQ_MODIFY;
+ op2.o_req_dn = op->o_conn->c_dn;
+ op2.o_req_ndn = op->o_conn->c_ndn;
+ op2.o_callback = &cb;
+ op2.orm_modlist = m;
+ op2.orm_no_opattrs = 0;
+ op2.o_dn = op->o_bd->be_rootdn;
+ op2.o_ndn = op->o_bd->be_rootndn;
+
+ /*
+ * TODO: this is core+frontend, not everything works the same way?
+ */
+ /*
+ * Code for forwarding of updates adapted from ppolicy.c of slapo-ppolicy
+ *
+ * If this server is a shadow and forward_updates is true,
+ * use the frontend to perform this modify. That will trigger
+ * the update referral, which can then be forwarded by the
+ * chain overlay. Obviously the updateref and chain overlay
+ * must be configured appropriately for this to be useful.
+ */
+ if ( SLAP_SHADOW( op->o_bd ) ) {
+ /* Must use Relax control since these are no-user-mod */
+ op2.o_relax = SLAP_CONTROL_CRITICAL;
+ op2.o_ctrls = ca;
+ ca[0] = &c;
+ ca[1] = NULL;
+ BER_BVZERO( &c.ldctl_value );
+ c.ldctl_iscritical = 1;
+ c.ldctl_oid = LDAP_CONTROL_RELAX;
+ } else {
+ /* If not forwarding, don't update opattrs and don't replicate */
+ if ( SLAP_SINGLE_SHADOW( op->o_bd )) {
+ op2.orm_no_opattrs = 1;
+ op2.o_dont_replicate = 1;
+ }
+ }
+
+ rc = op2.o_bd->be_modify( &op2, &r2 );
+ slap_mods_free( m, 1 );
+
+done:
+ return rc;
+}
+
+int
+fe_op_bind_success( Operation *op, SlapReply *rs )
+{
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+
+ if( op->o_conn->c_authz_backend == NULL ) {
+ op->o_conn->c_authz_backend = op->o_bd;
+ }
+
+ /* be_bind returns regular/global edn */
+ if( !BER_BVISEMPTY( &op->orb_edn ) ) {
+ op->o_conn->c_dn = op->orb_edn;
+ } else {
+ ber_dupbv(&op->o_conn->c_dn, &op->o_req_dn);
+ }
+
+ ber_dupbv( &op->o_conn->c_ndn, &op->o_req_ndn );
+
+ /* op->o_conn->c_sb may be 0 for internal operations */
+ if( !BER_BVISEMPTY( &op->o_conn->c_dn ) && op->o_conn->c_sb != 0 ) {
+ ber_len_t max = sockbuf_max_incoming_auth;
+ ber_sockbuf_ctrl( op->o_conn->c_sb,
+ LBER_SB_OPT_SET_MAX_INCOMING, &max );
+ }
+
+ /* log authorization identity */
+ Debug( LDAP_DEBUG_STATS,
+ "%s BIND dn=\"%s\" mech=%s bind_ssf=0 ssf=%d\n",
+ op->o_log_prefix,
+ op->o_conn->c_dn.bv_val, op->orb_mech.bv_val, op->o_conn->c_ssf );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "do_bind: v%d bind: \"%s\" to \"%s\"\n",
+ op->o_protocol, op->o_req_dn.bv_val, op->o_conn->c_dn.bv_val );
+
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+
+ if ( SLAP_LASTBIND( op->o_bd ) ) {
+ fe_op_lastbind( op );
+ }
+
+ /* send this here to avoid a race condition */
+ send_ldap_result( op, rs );
+
+ return LDAP_SUCCESS;
+}
diff --git a/servers/slapd/cancel.c b/servers/slapd/cancel.c
new file mode 100644
index 0000000..8def763
--- /dev/null
+++ b/servers/slapd/cancel.c
@@ -0,0 +1,162 @@
+/* cancel.c - LDAP cancel extended operation */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include "slap.h"
+
+#include <lber_pvt.h>
+#include <lutil.h>
+
+const struct berval slap_EXOP_CANCEL = BER_BVC(LDAP_EXOP_CANCEL);
+
+int cancel_extop( Operation *op, SlapReply *rs )
+{
+ Operation *o;
+ int rc;
+ int opid;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+
+ assert( ber_bvcmp( &slap_EXOP_CANCEL, &op->ore_reqoid ) == 0 );
+
+ if ( op->ore_reqdata == NULL ) {
+ rs->sr_text = "no message ID supplied";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( op->ore_reqdata->bv_len == 0 ) {
+ rs->sr_text = "empty request data field";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+ ber_init2( ber, op->ore_reqdata, 0 );
+
+ if ( ber_scanf( ber, "{i}", &opid ) == LBER_ERROR ) {
+ rs->sr_text = "message ID parse failed";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ Debug( LDAP_DEBUG_STATS, "%s CANCEL msg=%d\n",
+ op->o_log_prefix, opid );
+
+ if ( opid < 0 ) {
+ rs->sr_text = "message ID invalid";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( opid == op->o_msgid ) {
+ op->o_cancel = SLAP_CANCEL_DONE;
+ return LDAP_SUCCESS;
+ }
+
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+
+ if ( op->o_abandon ) {
+ /* FIXME: Should instead reject the cancel/abandon of this op, but
+ * it seems unsafe to reset op->o_abandon once it is set. ITS#6138.
+ */
+ rc = LDAP_OPERATIONS_ERROR;
+ rs->sr_text = "tried to abandon or cancel this operation";
+ goto out;
+ }
+
+ LDAP_STAILQ_FOREACH( o, &op->o_conn->c_pending_ops, o_next ) {
+ if ( o->o_msgid == opid ) {
+ /* TODO: We could instead remove the cancelled operation
+ * from c_pending_ops like Abandon does, and send its
+ * response here. Not if it is pending because of a
+ * congested connection though.
+ */
+ rc = LDAP_CANNOT_CANCEL;
+ rs->sr_text = "too busy for Cancel, try Abandon instead";
+ goto out;
+ }
+ }
+
+ LDAP_STAILQ_FOREACH( o, &op->o_conn->c_ops, o_next ) {
+ if ( o->o_msgid == opid ) {
+ break;
+ }
+ }
+
+ if ( o == NULL ) {
+ rc = LDAP_NO_SUCH_OPERATION;
+ rs->sr_text = "message ID not found";
+
+ } else if ( o->o_tag == LDAP_REQ_BIND
+ || o->o_tag == LDAP_REQ_UNBIND
+ || o->o_tag == LDAP_REQ_ABANDON ) {
+ rc = LDAP_CANNOT_CANCEL;
+
+ } else if ( o->o_cancel != SLAP_CANCEL_NONE ) {
+ rc = LDAP_OPERATIONS_ERROR;
+ rs->sr_text = "message ID already being cancelled";
+
+#if 0
+ } else if ( o->o_abandon ) {
+ /* TODO: Would this break something when
+ * o_abandon="suppress response"? (ITS#6138)
+ */
+ rc = LDAP_TOO_LATE;
+#endif
+
+ } else {
+ rc = LDAP_SUCCESS;
+ o->o_cancel = SLAP_CANCEL_REQ;
+ o->o_abandon = 1;
+ }
+
+ out:
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+
+ if ( rc == LDAP_SUCCESS ) {
+ LDAP_STAILQ_FOREACH( op->o_bd, &backendDB, be_next ) {
+ if( !op->o_bd->be_cancel ) continue;
+
+ op->oq_cancel.rs_msgid = opid;
+ if ( op->o_bd->be_cancel( op, rs ) == LDAP_SUCCESS ) {
+ return LDAP_SUCCESS;
+ }
+ }
+
+ do {
+ /* Fake a cond_wait with thread_yield, then
+ * verify the result properly mutex-protected.
+ */
+ while ( o->o_cancel == SLAP_CANCEL_REQ )
+ ldap_pvt_thread_yield();
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+ rc = o->o_cancel;
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+ } while ( rc == SLAP_CANCEL_REQ );
+
+ if ( rc == SLAP_CANCEL_ACK ) {
+ rc = LDAP_SUCCESS;
+ }
+
+ o->o_cancel = SLAP_CANCEL_DONE;
+ }
+
+ return rc;
+}
diff --git a/servers/slapd/ch_malloc.c b/servers/slapd/ch_malloc.c
new file mode 100644
index 0000000..6acf831
--- /dev/null
+++ b/servers/slapd/ch_malloc.c
@@ -0,0 +1,142 @@
+/* ch_malloc.c - malloc routines that test returns from malloc and friends */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#define CH_FREE 1
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+BerMemoryFunctions ch_mfuncs = {
+ (BER_MEMALLOC_FN *)ch_malloc,
+ (BER_MEMCALLOC_FN *)ch_calloc,
+ (BER_MEMREALLOC_FN *)ch_realloc,
+ (BER_MEMFREE_FN *)ch_free
+};
+
+void *
+ch_malloc(
+ ber_len_t size
+)
+{
+ void *new;
+
+ if ( (new = (void *) ber_memalloc_x( size, NULL )) == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "ch_malloc of %lu bytes failed\n",
+ (long) size );
+ assert( 0 );
+ exit( EXIT_FAILURE );
+ }
+
+ return( new );
+}
+
+void *
+ch_realloc(
+ void *block,
+ ber_len_t size
+)
+{
+ void *new, *ctx;
+
+ if ( block == NULL ) {
+ return( ch_malloc( size ) );
+ }
+
+ if( size == 0 ) {
+ ch_free( block );
+ return NULL;
+ }
+
+ ctx = slap_sl_context( block );
+ if ( ctx ) {
+ return slap_sl_realloc( block, size, ctx );
+ }
+
+ if ( (new = (void *) ber_memrealloc_x( block, size, NULL )) == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "ch_realloc of %lu bytes failed\n",
+ (long) size );
+ assert( 0 );
+ exit( EXIT_FAILURE );
+ }
+
+ return( new );
+}
+
+void *
+ch_calloc(
+ ber_len_t nelem,
+ ber_len_t size
+)
+{
+ void *new;
+
+ if ( (new = (void *) ber_memcalloc_x( nelem, size, NULL )) == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "ch_calloc of %lu elems of %lu bytes failed\n",
+ (long) nelem, (long) size );
+ assert( 0 );
+ exit( EXIT_FAILURE );
+ }
+
+ return( new );
+}
+
+char *
+ch_strdup(
+ const char *string
+)
+{
+ char *new;
+
+ if ( (new = ber_strdup_x( string, NULL )) == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "ch_strdup(%s) failed\n", string );
+ assert( 0 );
+ exit( EXIT_FAILURE );
+ }
+
+ return( new );
+}
+
+void
+ch_free( void *ptr )
+{
+ void *ctx;
+
+ ctx = slap_sl_context( ptr );
+ if (ctx) {
+ slap_sl_free( ptr, ctx );
+ } else {
+ ber_memfree_x( ptr, NULL );
+ }
+}
+
diff --git a/servers/slapd/compare.c b/servers/slapd/compare.c
new file mode 100644
index 0000000..e7a4f88
--- /dev/null
+++ b/servers/slapd/compare.c
@@ -0,0 +1,413 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+
+int
+do_compare(
+ Operation *op,
+ SlapReply *rs )
+{
+ struct berval dn = BER_BVNULL;
+ struct berval desc = BER_BVNULL;
+ struct berval value = BER_BVNULL;
+ AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
+
+ Debug( LDAP_DEBUG_TRACE, "%s do_compare\n",
+ op->o_log_prefix );
+ /*
+ * Parse the compare request. It looks like this:
+ *
+ * CompareRequest := [APPLICATION 14] SEQUENCE {
+ * entry DistinguishedName,
+ * ava SEQUENCE {
+ * type AttributeType,
+ * value AttributeValue
+ * }
+ * }
+ */
+
+ if ( ber_scanf( op->o_ber, "{m" /*}*/, &dn ) == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_compare: ber_scanf failed\n",
+ op->o_log_prefix );
+ send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
+ return SLAPD_DISCONNECT;
+ }
+
+ if ( ber_scanf( op->o_ber, "{mm}", &desc, &value ) == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_compare: get ava failed\n",
+ op->o_log_prefix );
+ send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
+ return SLAPD_DISCONNECT;
+ }
+
+ if ( ber_scanf( op->o_ber, /*{*/ "}" ) == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_compare: ber_scanf failed\n",
+ op->o_log_prefix );
+ send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
+ return SLAPD_DISCONNECT;
+ }
+
+ if( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_compare: get_ctrls failed\n",
+ op->o_log_prefix );
+ goto cleanup;
+ }
+
+ rs->sr_err = dnPrettyNormal( NULL, &dn, &op->o_req_dn, &op->o_req_ndn,
+ op->o_tmpmemctx );
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_compare: invalid dn (%s)\n",
+ op->o_log_prefix, dn.bv_val );
+ send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid DN" );
+ goto cleanup;
+ }
+
+ Debug( LDAP_DEBUG_STATS,
+ "%s CMP dn=\"%s\" attr=\"%s\"\n",
+ op->o_log_prefix, op->o_req_dn.bv_val,
+ desc.bv_val );
+
+ rs->sr_err = slap_bv2ad( &desc, &ava.aa_desc, &rs->sr_text );
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ rs->sr_err = slap_bv2undef_ad( &desc, &ava.aa_desc,
+ &rs->sr_text,
+ SLAP_AD_PROXIED|SLAP_AD_NOINSERT );
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+ }
+
+ rs->sr_err = asserted_value_validate_normalize( ava.aa_desc,
+ ava.aa_desc->ad_type->sat_equality,
+ SLAP_MR_EQUALITY|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
+ &value, &ava.aa_value, &rs->sr_text, op->o_tmpmemctx );
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ op->orc_ava = &ava;
+
+ Debug( LDAP_DEBUG_ARGS,
+ "do_compare: dn (%s) attr (%s) value (%s)\n",
+ op->o_req_dn.bv_val,
+ ava.aa_desc->ad_cname.bv_val, ava.aa_value.bv_val );
+
+ op->o_bd = frontendDB;
+ rs->sr_err = frontendDB->be_compare( op, rs );
+ if ( rs->sr_err == SLAPD_ASYNCOP ) {
+ /* skip cleanup */
+ return rs->sr_err;
+ }
+
+cleanup:;
+ op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
+ if ( !BER_BVISNULL( &ava.aa_value ) ) {
+ op->o_tmpfree( ava.aa_value.bv_val, op->o_tmpmemctx );
+ }
+
+ return rs->sr_err;
+}
+
+int
+fe_op_compare( Operation *op, SlapReply *rs )
+{
+ Entry *entry = NULL;
+ AttributeAssertion *ava = op->orc_ava;
+ BackendDB *bd = op->o_bd;
+
+ if( strcasecmp( op->o_req_ndn.bv_val, LDAP_ROOT_DSE ) == 0 ) {
+ if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ rs->sr_err = root_dse_info( op->o_conn, &entry, &rs->sr_text );
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ } else if ( bvmatch( &op->o_req_ndn, &frontendDB->be_schemandn ) ) {
+ if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ rs->sr_err = 0;
+ goto cleanup;
+ }
+
+ rs->sr_err = schema_info( &entry, &rs->sr_text );
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ rs->sr_err = 0;
+ goto cleanup;
+ }
+ }
+
+ if( entry ) {
+ rs->sr_err = slap_compare_entry( op, entry, ava );
+ entry_free( entry );
+
+ send_ldap_result( op, rs );
+
+ if( rs->sr_err == LDAP_COMPARE_TRUE ||
+ rs->sr_err == LDAP_COMPARE_FALSE )
+ {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+
+ goto cleanup;
+ }
+
+ /*
+ * We could be serving multiple database backends. Select the
+ * appropriate one, or send a referral to our "referral server"
+ * if we don't hold it.
+ */
+ op->o_bd = select_backend( &op->o_req_ndn, 0 );
+ if ( op->o_bd == NULL ) {
+ rs->sr_ref = referral_rewrite( default_referral,
+ NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+
+ rs->sr_err = LDAP_REFERRAL;
+ if (!rs->sr_ref) rs->sr_ref = default_referral;
+ op->o_bd = bd;
+ send_ldap_result( op, rs );
+
+ if (rs->sr_ref != default_referral) ber_bvarray_free( rs->sr_ref );
+ rs->sr_err = 0;
+ goto cleanup;
+ }
+
+ /* check restrictions */
+ if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ /* check for referrals */
+ if( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) {
+ goto cleanup;
+ }
+
+ if ( SLAP_SHADOW(op->o_bd) && get_dontUseCopy(op) ) {
+ /* don't use shadow copy */
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "copy not used" );
+
+ } else if ( ava->aa_desc == slap_schema.si_ad_entryDN ) {
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "entryDN compare not supported" );
+
+ } else if ( ava->aa_desc == slap_schema.si_ad_subschemaSubentry ) {
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "subschemaSubentry compare not supported" );
+
+#ifndef SLAP_COMPARE_IN_FRONTEND
+ } else if ( ava->aa_desc == slap_schema.si_ad_hasSubordinates
+ && op->o_bd->be_has_subordinates )
+ {
+ int rc, hasSubordinates = LDAP_SUCCESS;
+
+ rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &entry );
+ if ( rc == 0 && entry ) {
+ if ( ! access_allowed( op, entry,
+ ava->aa_desc, &ava->aa_value, ACL_COMPARE, NULL ) )
+ {
+ rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+
+ } else {
+ rc = rs->sr_err = op->o_bd->be_has_subordinates( op,
+ entry, &hasSubordinates );
+ be_entry_release_r( op, entry );
+ }
+ }
+
+ if ( rc == 0 ) {
+ int asserted;
+
+ asserted = bvmatch( &ava->aa_value, &slap_true_bv )
+ ? LDAP_COMPARE_TRUE : LDAP_COMPARE_FALSE;
+ if ( hasSubordinates == asserted ) {
+ rs->sr_err = LDAP_COMPARE_TRUE;
+
+ } else {
+ rs->sr_err = LDAP_COMPARE_FALSE;
+ }
+
+ } else {
+ /* return error only if "disclose"
+ * is granted on the object */
+ if ( backend_access( op, NULL, &op->o_req_ndn,
+ slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL ) == LDAP_INSUFFICIENT_ACCESS )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ }
+ }
+
+ send_ldap_result( op, rs );
+
+ if ( rc == 0 ) {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+
+ } else if ( op->o_bd->be_compare ) {
+ rs->sr_err = op->o_bd->be_compare( op, rs );
+
+#endif /* ! SLAP_COMPARE_IN_FRONTEND */
+ } else {
+ rs->sr_err = SLAP_CB_CONTINUE;
+ }
+
+ if ( rs->sr_err == SLAP_CB_CONTINUE ) {
+ /* do our best to compare that AVA
+ *
+ * NOTE: this code is used only
+ * if SLAP_COMPARE_IN_FRONTEND
+ * is #define'd (it's not by default)
+ * or if op->o_bd->be_compare is NULL.
+ *
+ * FIXME: one potential issue is that
+ * if SLAP_COMPARE_IN_FRONTEND overlays
+ * are not executed for compare. */
+ BerVarray vals = NULL;
+ int rc = LDAP_OTHER;
+
+ rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
+ ava->aa_desc, &vals, ACL_COMPARE );
+ switch ( rs->sr_err ) {
+ default:
+ /* return error only if "disclose"
+ * is granted on the object */
+ if ( backend_access( op, NULL, &op->o_req_ndn,
+ slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL )
+ == LDAP_INSUFFICIENT_ACCESS )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ }
+ break;
+
+ case LDAP_SUCCESS:
+ if ( value_find_ex( op->oq_compare.rs_ava->aa_desc,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ vals, &ava->aa_value, op->o_tmpmemctx ) == 0 )
+ {
+ rs->sr_err = LDAP_COMPARE_TRUE;
+ break;
+
+ } else {
+ rs->sr_err = LDAP_COMPARE_FALSE;
+ }
+ rc = LDAP_SUCCESS;
+ break;
+ }
+
+ send_ldap_result( op, rs );
+
+ if ( rc == 0 ) {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+
+ if ( vals ) {
+ ber_bvarray_free_x( vals, op->o_tmpmemctx );
+ }
+ }
+
+cleanup:;
+ op->o_bd = bd;
+ return rs->sr_err;
+}
+
+int slap_compare_entry(
+ Operation *op,
+ Entry *e,
+ AttributeAssertion *ava )
+{
+ int rc = LDAP_COMPARE_FALSE;
+ Attribute *a;
+
+ if ( ! access_allowed( op, e,
+ ava->aa_desc, &ava->aa_value, ACL_COMPARE, NULL ) )
+ {
+ rc = LDAP_INSUFFICIENT_ACCESS;
+ goto done;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ rc = LDAP_ASSERTION_FAILED;
+ goto done;
+ }
+
+ a = attrs_find( e->e_attrs, ava->aa_desc );
+ if( a == NULL ) {
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ goto done;
+ }
+
+ for(;
+ a != NULL;
+ a = attrs_find( a->a_next, ava->aa_desc ))
+ {
+ if (( ava->aa_desc != a->a_desc ) && ! access_allowed( op,
+ e, a->a_desc, &ava->aa_value, ACL_COMPARE, NULL ) )
+ {
+ rc = LDAP_INSUFFICIENT_ACCESS;
+ break;
+ }
+
+ if ( attr_valfind( a,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ &ava->aa_value, NULL, op->o_tmpmemctx ) == 0 )
+ {
+ rc = LDAP_COMPARE_TRUE;
+ break;
+ }
+ }
+
+done:
+ if( rc != LDAP_COMPARE_TRUE && rc != LDAP_COMPARE_FALSE ) {
+ if ( ! access_allowed( op, e,
+ slap_schema.si_ad_entry, NULL, ACL_DISCLOSE, NULL ) )
+ {
+ rc = LDAP_NO_SUCH_OBJECT;
+ }
+ }
+
+ return rc;
+}
diff --git a/servers/slapd/component.c b/servers/slapd/component.c
new file mode 100644
index 0000000..37ce97d
--- /dev/null
+++ b/servers/slapd/component.c
@@ -0,0 +1,1393 @@
+/* component.c -- Component Filter Match Routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2004 by IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "lutil.h"
+#include <ldap.h>
+#include "slap.h"
+
+#ifdef LDAP_COMP_MATCH
+
+#include "component.h"
+
+/*
+ * Following function pointers are initialized
+ * when a component module is loaded
+ */
+alloc_nibble_func* nibble_mem_allocator = NULL;
+free_nibble_func* nibble_mem_free = NULL;
+convert_attr_to_comp_func* attr_converter = NULL;
+convert_assert_to_comp_func* assert_converter = NULL ;
+free_component_func* component_destructor = NULL ;
+test_component_func* test_components = NULL;
+test_membership_func* is_aliased_attribute = NULL;
+component_encoder_func* component_encoder = NULL;
+get_component_info_func* get_component_description = NULL;
+#define OID_ALL_COMP_MATCH "1.2.36.79672281.1.13.6"
+#define OID_COMP_FILTER_MATCH "1.2.36.79672281.1.13.2"
+#define MAX_LDAP_STR_LEN 128
+
+static int
+peek_componentId_type( ComponentAssertionValue* cav );
+
+static int
+strip_cav_str( ComponentAssertionValue* cav, char* str);
+
+static int
+peek_cav_str( ComponentAssertionValue* cav, char* str );
+
+static int
+parse_comp_filter( Operation* op, ComponentAssertionValue* cav,
+ ComponentFilter** filt, const char** text );
+
+static void
+free_comp_filter( ComponentFilter* f );
+
+static int
+test_comp_filter( Syntax *syn, ComponentSyntaxInfo *a, ComponentFilter *f );
+
+int
+componentCertificateValidate(
+ Syntax *syntax,
+ struct berval *val )
+{
+ return LDAP_SUCCESS;
+}
+
+int
+componentFilterValidate(
+ Syntax *syntax,
+ struct berval *val )
+{
+ return LDAP_SUCCESS;
+}
+
+int
+allComponentsValidate(
+ Syntax *syntax,
+ struct berval *val )
+{
+ return LDAP_SUCCESS;
+}
+
+int
+componentFilterMatch (
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ ComponentSyntaxInfo *csi_attr = (ComponentSyntaxInfo*)value;
+ MatchingRuleAssertion * ma = (MatchingRuleAssertion*)assertedValue;
+ int rc;
+
+ if ( !mr || !ma->ma_cf ) return LDAP_INAPPROPRIATE_MATCHING;
+
+ /* Check if the component module is loaded */
+ if ( !attr_converter || !nibble_mem_allocator ) {
+ return LDAP_OTHER;
+ }
+
+ rc = test_comp_filter( syntax, csi_attr, ma->ma_cf );
+
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ *matchp = 0;
+ return LDAP_SUCCESS;
+ }
+ else if ( rc == LDAP_COMPARE_FALSE ) {
+ *matchp = 1;
+ return LDAP_SUCCESS;
+ }
+ else {
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+}
+
+int
+directoryComponentsMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ /* Only for registration */
+ *matchp = 0;
+ return LDAP_SUCCESS;
+}
+
+int
+allComponentsMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ /* Only for registration */
+ *matchp = 0;
+ return LDAP_SUCCESS;
+}
+
+static int
+slapd_ber2cav( struct berval* bv, ComponentAssertionValue* cav )
+{
+ cav->cav_ptr = cav->cav_buf = bv->bv_val;
+ cav->cav_end = bv->bv_val + bv->bv_len;
+
+ return LDAP_SUCCESS;
+}
+
+ComponentReference*
+dup_comp_ref ( Operation* op, ComponentReference* cr )
+{
+ ComponentReference* dup_cr;
+ ComponentId* ci_curr;
+ ComponentId** ci_temp;
+
+ dup_cr = op->o_tmpalloc( sizeof( ComponentReference ), op->o_tmpmemctx );
+
+ dup_cr->cr_len = cr->cr_len;
+ dup_cr->cr_string = cr->cr_string;
+
+ ci_temp = &dup_cr->cr_list;
+ ci_curr = cr->cr_list;
+
+ for ( ; ci_curr != NULL ;
+ ci_curr = ci_curr->ci_next, ci_temp = &(*ci_temp)->ci_next )
+ {
+ *ci_temp = op->o_tmpalloc( sizeof( ComponentId ), op->o_tmpmemctx );
+ if ( !*ci_temp ) return NULL;
+ **ci_temp = *ci_curr;
+ }
+
+ dup_cr->cr_curr = dup_cr->cr_list;
+
+ return dup_cr;
+}
+
+static int
+dup_comp_filter_list (
+ Operation *op,
+ struct berval *bv,
+ ComponentFilter* in_f,
+ ComponentFilter** out_f )
+{
+ ComponentFilter **new, *f;
+ int rc;
+
+ new = out_f;
+ for ( f = in_f; f != NULL; f = f->cf_next ) {
+ rc = dup_comp_filter( op, bv, f, new );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+ new = &(*new)->cf_next;
+ }
+ return LDAP_SUCCESS;
+}
+
+int
+get_len_of_next_assert_value ( struct berval* bv, char separator )
+{
+ ber_len_t i = 0;
+ while (1) {
+ if ( (bv->bv_val[ i ] == separator) || ( i >= bv->bv_len) )
+ break;
+ i++;
+ }
+ bv->bv_val += (i + 1);
+ bv->bv_len -= (i + 1);
+ return i;
+}
+
+int
+dup_comp_filter_item (
+ Operation *op,
+ struct berval* assert_bv,
+ ComponentAssertion* in_ca,
+ ComponentAssertion** out_ca )
+{
+ int len;
+
+ if ( !in_ca->ca_comp_ref ) return SLAPD_DISCONNECT;
+
+ *out_ca = op->o_tmpalloc( sizeof( ComponentAssertion ), op->o_tmpmemctx );
+ if ( !(*out_ca) ) return LDAP_NO_MEMORY;
+
+ (*out_ca)->ca_comp_data.cd_tree = NULL;
+ (*out_ca)->ca_comp_data.cd_mem_op = NULL;
+
+ (*out_ca)->ca_comp_ref = dup_comp_ref ( op, in_ca->ca_comp_ref );
+ (*out_ca)->ca_use_def = 0;
+ (*out_ca)->ca_ma_rule = in_ca->ca_ma_rule;
+
+ (*out_ca)->ca_ma_value.bv_val = assert_bv->bv_val;
+ len = get_len_of_next_assert_value ( assert_bv, '$' );
+ if ( len <= 0 ) return SLAPD_DISCONNECT;
+ (*out_ca)->ca_ma_value.bv_len = len;
+
+ return LDAP_SUCCESS;
+}
+
+int
+dup_comp_filter (
+ Operation* op,
+ struct berval *bv,
+ ComponentFilter *in_f,
+ ComponentFilter **out_f )
+{
+ int rc;
+ ComponentFilter dup_f = {0};
+
+ if ( !in_f ) return LDAP_PROTOCOL_ERROR;
+
+ switch ( in_f->cf_choice ) {
+ case LDAP_COMP_FILTER_AND:
+ rc = dup_comp_filter_list( op, bv, in_f->cf_and, &dup_f.cf_and);
+ dup_f.cf_choice = LDAP_COMP_FILTER_AND;
+ break;
+ case LDAP_COMP_FILTER_OR:
+ rc = dup_comp_filter_list( op, bv, in_f->cf_or, &dup_f.cf_or);
+ dup_f.cf_choice = LDAP_COMP_FILTER_OR;
+ break;
+ case LDAP_COMP_FILTER_NOT:
+ rc = dup_comp_filter( op, bv, in_f->cf_not, &dup_f.cf_not);
+ dup_f.cf_choice = LDAP_COMP_FILTER_NOT;
+ break;
+ case LDAP_COMP_FILTER_ITEM:
+ rc = dup_comp_filter_item( op, bv, in_f->cf_ca ,&dup_f.cf_ca );
+ dup_f.cf_choice = LDAP_COMP_FILTER_ITEM;
+ break;
+ default:
+ rc = LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ *out_f = op->o_tmpalloc( sizeof(dup_f), op->o_tmpmemctx );
+ **out_f = dup_f;
+ }
+
+ return( rc );
+}
+
+int
+get_aliased_filter_aa ( Operation* op, AttributeAssertion* a_assert, AttributeAliasing* aa, const char** text )
+{
+ struct berval assert_bv;
+
+ Debug( LDAP_DEBUG_FILTER, "get_aliased_filter\n" );
+
+ if ( !aa->aa_cf )
+ return LDAP_PROTOCOL_ERROR;
+
+ assert_bv = a_assert->aa_value;
+ /*
+ * Duplicate aa->aa_cf to ma->ma_cf by replacing the
+ * the component assertion value in assert_bv
+ * Multiple values may be separated with '$'
+ */
+ return dup_comp_filter ( op, &assert_bv, aa->aa_cf, &a_assert->aa_cf );
+}
+
+int
+get_aliased_filter( Operation* op,
+ MatchingRuleAssertion* ma, AttributeAliasing* aa,
+ const char** text )
+{
+ struct berval assert_bv;
+
+ Debug( LDAP_DEBUG_FILTER, "get_aliased_filter\n" );
+
+ if ( !aa->aa_cf ) return LDAP_PROTOCOL_ERROR;
+
+ assert_bv = ma->ma_value;
+ /* Attribute Description is replaced with aliased one */
+ ma->ma_desc = aa->aa_aliased_ad;
+ ma->ma_rule = aa->aa_mr;
+ /*
+ * Duplicate aa->aa_cf to ma->ma_cf by replacing the
+ * the component assertion value in assert_bv
+ * Multiple values may be separated with '$'
+ */
+ return dup_comp_filter ( op, &assert_bv, aa->aa_cf, &ma->ma_cf );
+}
+
+int
+get_comp_filter( Operation* op, struct berval* bv,
+ ComponentFilter** filt, const char **text )
+{
+ ComponentAssertionValue cav;
+ int rc;
+
+ Debug( LDAP_DEBUG_FILTER, "get_comp_filter\n" );
+ if ( (rc = slapd_ber2cav(bv, &cav) ) != LDAP_SUCCESS ) {
+ return rc;
+ }
+ rc = parse_comp_filter( op, &cav, filt, text );
+ /* bv->bv_val = cav.cav_ptr; */
+
+ return rc;
+}
+
+static void
+eat_whsp( ComponentAssertionValue* cav )
+{
+ for ( ; ( *cav->cav_ptr == ' ' ) && ( cav->cav_ptr < cav->cav_end ) ; ) {
+ cav->cav_ptr++;
+ }
+}
+
+static int
+cav_cur_len( ComponentAssertionValue* cav )
+{
+ return cav->cav_end - cav->cav_ptr;
+}
+
+static ber_tag_t
+comp_first_element( ComponentAssertionValue* cav )
+{
+ eat_whsp( cav );
+ if ( cav_cur_len( cav ) >= 8 && strncmp( cav->cav_ptr, "item", 4 ) == 0 ) {
+ return LDAP_COMP_FILTER_ITEM;
+
+ } else if ( cav_cur_len( cav ) >= 7 &&
+ strncmp( cav->cav_ptr, "and", 3 ) == 0 )
+ {
+ return LDAP_COMP_FILTER_AND;
+
+ } else if ( cav_cur_len( cav ) >= 6 &&
+ strncmp( cav->cav_ptr, "or" , 2 ) == 0 )
+ {
+ return LDAP_COMP_FILTER_OR;
+
+ } else if ( cav_cur_len( cav ) >= 7 &&
+ strncmp( cav->cav_ptr, "not", 3 ) == 0 )
+ {
+ return LDAP_COMP_FILTER_NOT;
+
+ } else {
+ return LDAP_COMP_FILTER_UNDEFINED;
+ }
+}
+
+static ber_tag_t
+comp_next_element( ComponentAssertionValue* cav )
+{
+ eat_whsp( cav );
+ if ( *(cav->cav_ptr) == ',' ) {
+ /* move pointer to the next CA */
+ cav->cav_ptr++;
+ return comp_first_element( cav );
+ }
+ else return LDAP_COMP_FILTER_UNDEFINED;
+}
+
+static int
+get_comp_filter_list( Operation *op, ComponentAssertionValue *cav,
+ ComponentFilter** f, const char** text )
+{
+ ComponentFilter **new;
+ int err;
+ ber_tag_t tag;
+
+ Debug( LDAP_DEBUG_FILTER, "get_comp_filter_list\n" );
+ new = f;
+ for ( tag = comp_first_element( cav );
+ tag != LDAP_COMP_FILTER_UNDEFINED;
+ tag = comp_next_element( cav ) )
+ {
+ err = parse_comp_filter( op, cav, new, text );
+ if ( err != LDAP_SUCCESS ) return ( err );
+ new = &(*new)->cf_next;
+ }
+ *new = NULL;
+
+ return( LDAP_SUCCESS );
+}
+
+static int
+get_componentId( Operation *op, ComponentAssertionValue* cav,
+ ComponentId ** cid, const char** text )
+{
+ ber_tag_t type;
+ ComponentId _cid;
+ int len;
+
+ type = peek_componentId_type( cav );
+
+ Debug( LDAP_DEBUG_FILTER, "get_compId [%lu]\n",
+ (unsigned long) type );
+ len = 0;
+ _cid.ci_type = type;
+ _cid.ci_next = NULL;
+ switch ( type ) {
+ case LDAP_COMPREF_IDENTIFIER :
+ _cid.ci_val.ci_identifier.bv_val = cav->cav_ptr;
+ for( ;cav->cav_ptr[len] != ' ' && cav->cav_ptr[len] != '\0' &&
+ cav->cav_ptr[len] != '.' && cav->cav_ptr[len] != '\"' ; len++ );
+ _cid.ci_val.ci_identifier.bv_len = len;
+ cav->cav_ptr += len;
+ break;
+ case LDAP_COMPREF_FROM_BEGINNING :
+ for( ;cav->cav_ptr[len] != ' ' && cav->cav_ptr[len] != '\0' &&
+ cav->cav_ptr[len] != '.' && cav->cav_ptr[len] != '\"' ; len++ );
+ _cid.ci_val.ci_from_beginning = strtol( cav->cav_ptr, NULL, 0 );
+ cav->cav_ptr += len;
+ break;
+ case LDAP_COMPREF_FROM_END :
+ for( ;cav->cav_ptr[len] != ' ' && cav->cav_ptr[len] != '\0' &&
+ cav->cav_ptr[len] != '.' && cav->cav_ptr[len] != '\"' ; len++ );
+ _cid.ci_val.ci_from_end = strtol( cav->cav_ptr, NULL, 0 );
+ cav->cav_ptr += len;
+ break;
+ case LDAP_COMPREF_COUNT :
+ _cid.ci_val.ci_count = 0;
+ cav->cav_ptr++;
+ break;
+ case LDAP_COMPREF_CONTENT :
+ _cid.ci_val.ci_content = 1;
+ cav->cav_ptr += strlen("content");
+ break;
+ case LDAP_COMPREF_SELECT :
+ if ( cav->cav_ptr[len] != '(' ) return LDAP_COMPREF_UNDEFINED;
+ for( ;cav->cav_ptr[len] != ' ' && cav->cav_ptr[len] != '\0' &&
+ cav->cav_ptr[len] != '\"' && cav->cav_ptr[len] != ')'
+ ; len++ );
+ _cid.ci_val.ci_select_value.bv_val = cav->cav_ptr + 1;
+ _cid.ci_val.ci_select_value.bv_len = len - 1 ;
+ cav->cav_ptr += len + 1;
+ break;
+ case LDAP_COMPREF_ALL :
+ _cid.ci_val.ci_all = '*';
+ cav->cav_ptr++;
+ break;
+ default :
+ return LDAP_COMPREF_UNDEFINED;
+ }
+
+ if ( op ) {
+ *cid = op->o_tmpalloc( sizeof( ComponentId ), op->o_tmpmemctx );
+ } else {
+ *cid = SLAP_MALLOC( sizeof( ComponentId ) );
+ }
+ if (*cid == NULL) {
+ return LDAP_NO_MEMORY;
+ }
+ **cid = _cid;
+ return LDAP_SUCCESS;
+}
+
+static int
+peek_componentId_type( ComponentAssertionValue* cav )
+{
+ eat_whsp( cav );
+
+ if ( cav->cav_ptr[0] == '-' ) {
+ return LDAP_COMPREF_FROM_END;
+
+ } else if ( cav->cav_ptr[0] == '(' ) {
+ return LDAP_COMPREF_SELECT;
+
+ } else if ( cav->cav_ptr[0] == '*' ) {
+ return LDAP_COMPREF_ALL;
+
+ } else if ( cav->cav_ptr[0] == '0' ) {
+ return LDAP_COMPREF_COUNT;
+
+ } else if ( cav->cav_ptr[0] > '0' && cav->cav_ptr[0] <= '9' ) {
+ return LDAP_COMPREF_FROM_BEGINNING;
+
+ } else if ( (cav->cav_end - cav->cav_ptr) >= 7 &&
+ strncmp(cav->cav_ptr,"content",7) == 0 )
+ {
+ return LDAP_COMPREF_CONTENT;
+ } else if ( (cav->cav_ptr[0] >= 'a' && cav->cav_ptr[0] <= 'z') ||
+ (cav->cav_ptr[0] >= 'A' && cav->cav_ptr[0] <= 'Z') )
+ {
+ return LDAP_COMPREF_IDENTIFIER;
+ }
+
+ return LDAP_COMPREF_UNDEFINED;
+}
+
+static ber_tag_t
+comp_next_id( ComponentAssertionValue* cav )
+{
+ if ( *(cav->cav_ptr) == '.' ) {
+ cav->cav_ptr++;
+ return LDAP_COMPREF_DEFINED;
+ }
+
+ return LDAP_COMPREF_UNDEFINED;
+}
+
+
+
+static int
+get_component_reference(
+ Operation *op,
+ ComponentAssertionValue* cav,
+ ComponentReference** cr,
+ const char** text )
+{
+ int rc, count = 0;
+ ber_int_t type;
+ ComponentReference* ca_comp_ref;
+ ComponentId** cr_list;
+ char* start, *end;
+
+ eat_whsp( cav );
+
+ start = cav->cav_ptr;
+ if ( ( rc = strip_cav_str( cav,"\"") ) != LDAP_SUCCESS ) return rc;
+ if ( op ) {
+ ca_comp_ref = op->o_tmpalloc( sizeof( ComponentReference ),
+ op->o_tmpmemctx );
+ } else {
+ ca_comp_ref = SLAP_MALLOC( sizeof( ComponentReference ) );
+ }
+
+ if ( !ca_comp_ref ) return LDAP_NO_MEMORY;
+
+ cr_list = &ca_comp_ref->cr_list;
+
+ for ( type = peek_componentId_type( cav ) ; type != LDAP_COMPREF_UNDEFINED
+ ; type = comp_next_id( cav ), count++ )
+ {
+ rc = get_componentId( op, cav, cr_list, text );
+ if ( rc == LDAP_SUCCESS ) {
+ if ( count == 0 ) ca_comp_ref->cr_curr = ca_comp_ref->cr_list;
+ cr_list = &(*cr_list)->ci_next;
+
+ } else if ( rc == LDAP_COMPREF_UNDEFINED ) {
+ if ( op ) {
+ op->o_tmpfree( ca_comp_ref , op->o_tmpmemctx );
+ } else {
+ free( ca_comp_ref );
+ }
+ return rc;
+ }
+ }
+ ca_comp_ref->cr_len = count;
+ end = cav->cav_ptr;
+ if ( ( rc = strip_cav_str( cav,"\"") ) != LDAP_SUCCESS ) {
+ if ( op ) {
+ op->o_tmpfree( ca_comp_ref , op->o_tmpmemctx );
+ } else {
+ free( ca_comp_ref );
+ }
+ return rc;
+ }
+
+ *cr = ca_comp_ref;
+ **cr = *ca_comp_ref;
+
+ (*cr)->cr_string.bv_val = start;
+ (*cr)->cr_string.bv_len = end - start + 1;
+
+ return rc;
+}
+
+int
+insert_component_reference(
+ ComponentReference *cr,
+ ComponentReference** cr_list)
+{
+ if ( !cr ) return LDAP_PARAM_ERROR;
+
+ if ( !(*cr_list) ) {
+ *cr_list = cr;
+ cr->cr_next = NULL;
+ } else {
+ cr->cr_next = *cr_list;
+ *cr_list = cr;
+ }
+ return LDAP_SUCCESS;
+}
+
+/*
+ * If there is '.' in the name of a given attribute
+ * the first '.'- following characters are considered
+ * as a component reference of the attribute
+ * EX) userCertificate.toBeSigned.serialNumber
+ * attribute : userCertificate
+ * component reference : toBeSigned.serialNumber
+ */
+int
+is_component_reference( char* attr ) {
+ int i;
+ for ( i=0; attr[i] != '\0' ; i++ ) {
+ if ( attr[i] == '.' ) return (1);
+ }
+ return (0);
+}
+
+int
+extract_component_reference(
+ char* attr,
+ ComponentReference** cr )
+{
+ int i, rc;
+ char* cr_ptr;
+ int cr_len;
+ ComponentAssertionValue cav;
+ char text[1][128];
+
+ for ( i=0; attr[i] != '\0' ; i++ ) {
+ if ( attr[i] == '.' ) break;
+ }
+
+ if (attr[i] != '.' ) return LDAP_PARAM_ERROR;
+ attr[i] = '\0';
+
+ cr_ptr = attr + i + 1 ;
+ cr_len = strlen ( cr_ptr );
+ if ( cr_len <= 0 ) return LDAP_PARAM_ERROR;
+
+ /* enclosed between double quotes*/
+ cav.cav_ptr = cav.cav_buf = ch_malloc (cr_len+2);
+ memcpy( cav.cav_buf+1, cr_ptr, cr_len );
+ cav.cav_buf[0] = '"';
+ cav.cav_buf[cr_len+1] = '"';
+ cav.cav_end = cr_ptr + cr_len + 2;
+
+ rc = get_component_reference ( NULL, &cav, cr, (const char**)text );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ (*cr)->cr_string.bv_val = cav.cav_buf;
+ (*cr)->cr_string.bv_len = cr_len + 2;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+get_ca_use_default( Operation *op,
+ ComponentAssertionValue* cav,
+ int* ca_use_def, const char** text )
+{
+ strip_cav_str( cav, "useDefaultValues" );
+
+ if ( peek_cav_str( cav, "TRUE" ) == LDAP_SUCCESS ) {
+ strip_cav_str( cav, "TRUE" );
+ *ca_use_def = 1;
+
+ } else if ( peek_cav_str( cav, "FALSE" ) == LDAP_SUCCESS ) {
+ strip_cav_str( cav, "FALSE" );
+ *ca_use_def = 0;
+
+ } else {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+get_matching_rule( Operation *op, ComponentAssertionValue* cav,
+ MatchingRule** mr, const char** text )
+{
+ int count = 0;
+ struct berval rule_text = { 0L, NULL };
+
+ eat_whsp( cav );
+
+ for ( ; ; count++ ) {
+ if ( cav->cav_ptr[count] == ' ' || cav->cav_ptr[count] == ',' ||
+ cav->cav_ptr[count] == '\0' || cav->cav_ptr[count] == '{' ||
+ cav->cav_ptr[count] == '}' || cav->cav_ptr[count] == '\n' )
+ {
+ break;
+ }
+ }
+
+ if ( count == 0 ) {
+ *text = "component matching rule not recognized";
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+
+ rule_text.bv_len = count;
+ rule_text.bv_val = cav->cav_ptr;
+ *mr = mr_bvfind( &rule_text );
+ cav->cav_ptr += count;
+ Debug( LDAP_DEBUG_FILTER, "get_matching_rule: %s\n",
+ (*mr)->smr_mrule.mr_oid );
+ if ( *mr == NULL ) {
+ *text = "component matching rule not recognized";
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+ return LDAP_SUCCESS;
+}
+
+static int
+get_GSER_value( ComponentAssertionValue* cav, struct berval* bv )
+{
+ int count, sequent_dquote, unclosed_brace, succeed;
+
+ eat_whsp( cav );
+ /*
+ * Four cases of GSER <Values>
+ * 1) "..." :
+ * StringVal, GeneralizedTimeVal, UTCTimeVal, ObjectDescriptorVal
+ * 2) '...'B or '...'H :
+ * BitStringVal, OctetStringVal
+ * 3) {...} :
+ * SEQUENCE, SEQUENCEOF, SETOF, SET, CHOICE
+ * 4) Between two white spaces
+ * INTEGER, BOOLEAN, NULL,ENUMERATE, etc
+ */
+
+ succeed = 0;
+ if ( cav->cav_ptr[0] == '"' ) {
+ for( count = 1, sequent_dquote = 0 ; ; count++ ) {
+ /* In order to find escaped double quote */
+ if ( cav->cav_ptr[count] == '"' ) sequent_dquote++;
+ else sequent_dquote = 0;
+
+ if ( cav->cav_ptr[count] == '\0' ||
+ (cav->cav_ptr+count) > cav->cav_end )
+ {
+ break;
+ }
+
+ if ( ( cav->cav_ptr[count] == '"' &&
+ cav->cav_ptr[count-1] != '"') ||
+ ( sequent_dquote > 2 && (sequent_dquote%2) == 1 ) )
+ {
+ succeed = 1;
+ break;
+ }
+ }
+
+ if ( !succeed || cav->cav_ptr[count] != '"' ) {
+ return LDAP_FILTER_ERROR;
+ }
+
+ bv->bv_val = cav->cav_ptr + 1;
+ bv->bv_len = count - 1; /* exclude '"' */
+
+ } else if ( cav->cav_ptr[0] == '\'' ) {
+ for( count = 1 ; ; count++ ) {
+ if ( cav->cav_ptr[count] == '\0' ||
+ (cav->cav_ptr+count) > cav->cav_end )
+ {
+ break;
+ }
+ if ((cav->cav_ptr[count-1] == '\'' && cav->cav_ptr[count] == 'B') ||
+ (cav->cav_ptr[count-1] == '\'' && cav->cav_ptr[count] == 'H') )
+ {
+ succeed = 1;
+ break;
+ }
+ }
+
+ if ( !succeed ||
+ !(cav->cav_ptr[count] == 'H' || cav->cav_ptr[count] == 'B') )
+ {
+ return LDAP_FILTER_ERROR;
+ }
+
+ bv->bv_val = cav->cav_ptr + 1;/*the next to '"' */
+ bv->bv_len = count - 2;/* exclude "'H" or "'B" */
+
+ } else if ( cav->cav_ptr[0] == '{' ) {
+ for( count = 1, unclosed_brace = 1 ; ; count++ ) {
+ if ( cav->cav_ptr[count] == '{' ) unclosed_brace++;
+ if ( cav->cav_ptr[count] == '}' ) unclosed_brace--;
+
+ if ( cav->cav_ptr[count] == '\0' ||
+ (cav->cav_ptr+count) > cav->cav_end )
+ {
+ break;
+ }
+ if ( unclosed_brace == 0 ) {
+ succeed = 1;
+ break;
+ }
+ }
+
+ if ( !succeed || cav->cav_ptr[count] != '}' ) return LDAP_FILTER_ERROR;
+
+ bv->bv_val = cav->cav_ptr + 1;/*the next to '"' */
+ bv->bv_len = count - 1;/* exclude "'B" */
+
+ } else {
+ succeed = 1;
+ /*Find following white space where the value is ended*/
+ for( count = 1 ; ; count++ ) {
+ if ( cav->cav_ptr[count] == '\0' ||
+ cav->cav_ptr[count] == ' ' || cav->cav_ptr[count] == '}' ||
+ cav->cav_ptr[count] == '{' ||
+ (cav->cav_ptr+count) > cav->cav_end )
+ {
+ break;
+ }
+ }
+ bv->bv_val = cav->cav_ptr;
+ bv->bv_len = count;
+ }
+
+ cav->cav_ptr += bv->bv_len;
+ return LDAP_SUCCESS;
+}
+
+static int
+get_matching_value( Operation *op, ComponentAssertion* ca,
+ ComponentAssertionValue* cav, struct berval* bv,
+ const char** text )
+{
+ if ( !(ca->ca_ma_rule->smr_usage & (SLAP_MR_COMPONENT)) ) {
+ if ( get_GSER_value( cav, bv ) != LDAP_SUCCESS ) {
+ return LDAP_FILTER_ERROR;
+ }
+
+ } else {
+ /* embedded componentFilterMatch Description */
+ bv->bv_val = cav->cav_ptr;
+ bv->bv_len = cav_cur_len( cav );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/* Don't move the position pointer, just peek given string */
+static int
+peek_cav_str( ComponentAssertionValue* cav, char* str )
+{
+ eat_whsp( cav );
+ if ( cav_cur_len( cav ) >= strlen( str ) &&
+ strncmp( cav->cav_ptr, str, strlen( str ) ) == 0 )
+ {
+ return LDAP_SUCCESS;
+ }
+
+ return LDAP_INVALID_SYNTAX;
+}
+
+static int
+strip_cav_str( ComponentAssertionValue* cav, char* str)
+{
+ eat_whsp( cav );
+ if ( cav_cur_len( cav ) >= strlen( str ) &&
+ strncmp( cav->cav_ptr, str, strlen( str ) ) == 0 )
+ {
+ cav->cav_ptr += strlen( str );
+ return LDAP_SUCCESS;
+ }
+
+ return LDAP_INVALID_SYNTAX;
+}
+
+/*
+ * TAG : "item", "and", "or", "not"
+ */
+static ber_tag_t
+strip_cav_tag( ComponentAssertionValue* cav )
+{
+ int rc;
+
+ eat_whsp( cav );
+ if ( cav_cur_len( cav ) >= 8 && strncmp( cav->cav_ptr, "item", 4 ) == 0 ) {
+ if ( strip_cav_str( cav , "item:" ))
+ goto fail;
+ return LDAP_COMP_FILTER_ITEM;
+
+ } else if ( cav_cur_len( cav ) >= 7 &&
+ strncmp( cav->cav_ptr, "and", 3 ) == 0 )
+ {
+ if ( strip_cav_str( cav , "and:" ))
+ goto fail;
+ return LDAP_COMP_FILTER_AND;
+
+ } else if ( cav_cur_len( cav ) >= 6 &&
+ strncmp( cav->cav_ptr, "or" , 2 ) == 0 )
+ {
+ if ( strip_cav_str( cav , "or:" ))
+ goto fail;
+ return LDAP_COMP_FILTER_OR;
+
+ } else if ( cav_cur_len( cav ) >= 7 &&
+ strncmp( cav->cav_ptr, "not", 3 ) == 0 )
+ {
+ if ( strip_cav_str( cav , "not:" ))
+ goto fail;
+ return LDAP_COMP_FILTER_NOT;
+ }
+
+fail:
+ return LBER_ERROR;
+}
+
+/*
+ * when encoding, "item" is denotation of ComponentAssertion
+ * ComponentAssertion :: SEQUENCE {
+ * component ComponentReference (SIZE(1..MAX)) OPTIONAL,
+ * useDefaultValues BOOLEAN DEFAULT TRUE,
+ * rule MATCHING-RULE.&id,
+ * value MATCHING-RULE.&AssertionType }
+ */
+static int
+get_item( Operation *op, ComponentAssertionValue* cav, ComponentAssertion** ca,
+ const char** text )
+{
+ int rc, freeval = 0;
+ ComponentAssertion* _ca;
+ struct berval value;
+ MatchingRule* mr;
+
+ Debug( LDAP_DEBUG_FILTER, "get_item \n" );
+ if ( op )
+ _ca = op->o_tmpalloc( sizeof( ComponentAssertion ), op->o_tmpmemctx );
+ else
+ _ca = SLAP_MALLOC( sizeof( ComponentAssertion ) );
+
+ if ( !_ca ) return LDAP_NO_MEMORY;
+
+ _ca->ca_comp_data.cd_tree = NULL;
+ _ca->ca_comp_data.cd_mem_op = NULL;
+ BER_BVZERO( &_ca->ca_ma_value );
+
+ rc = peek_cav_str( cav, "component" );
+ if ( rc == LDAP_SUCCESS ) {
+ strip_cav_str( cav, "component" );
+ rc = get_component_reference( op, cav, &_ca->ca_comp_ref, text );
+ if ( rc != LDAP_SUCCESS ) {
+ rc = LDAP_INVALID_SYNTAX;
+fail:
+ if ( freeval )
+ op->o_tmpfree( _ca->ca_ma_value.bv_val, op->o_tmpmemctx );
+ if ( op )
+ op->o_tmpfree( _ca, op->o_tmpmemctx );
+ else
+ free( _ca );
+ return rc;
+ }
+ if ( ( rc = strip_cav_str( cav,",") ) != LDAP_SUCCESS )
+ goto fail;
+ } else {
+ _ca->ca_comp_ref = NULL;
+ }
+
+ rc = peek_cav_str( cav, "useDefaultValues");
+ if ( rc == LDAP_SUCCESS ) {
+ rc = get_ca_use_default( op, cav, &_ca->ca_use_def, text );
+ if ( rc != LDAP_SUCCESS ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto fail;
+ }
+ if ( ( rc = strip_cav_str( cav,",") ) != LDAP_SUCCESS )
+ goto fail;
+ }
+ else _ca->ca_use_def = 1;
+
+ if ( !( strip_cav_str( cav, "rule" ) == LDAP_SUCCESS &&
+ get_matching_rule( op, cav , &_ca->ca_ma_rule, text ) == LDAP_SUCCESS )) {
+ rc = LDAP_INAPPROPRIATE_MATCHING;
+ goto fail;
+ }
+
+ if ( ( rc = strip_cav_str( cav,",") ) != LDAP_SUCCESS )
+ goto fail;
+ if ( !(strip_cav_str( cav, "value" ) == LDAP_SUCCESS &&
+ get_matching_value( op, _ca, cav,&value ,text ) == LDAP_SUCCESS )) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto fail;
+ }
+
+ /*
+ * Normalize the value of this component assertion when the matching
+ * rule is one of existing matching rules
+ */
+ mr = _ca->ca_ma_rule;
+ if ( op && !(mr->smr_usage & (SLAP_MR_COMPONENT)) && mr->smr_normalize ) {
+
+ value.bv_val[value.bv_len] = '\0';
+ rc = mr->smr_normalize (
+ SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
+ NULL, mr,
+ &value, &_ca->ca_ma_value, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS )
+ goto fail;
+ freeval = 1;
+ }
+ else
+ _ca->ca_ma_value = value;
+ /*
+ * Validate the value of this component assertion
+ */
+ if ( op && mr->smr_syntax->ssyn_validate( mr->smr_syntax, &_ca->ca_ma_value) != LDAP_SUCCESS ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto fail;
+ }
+
+
+ /* componentFilterMatch contains componentFilterMatch in it */
+ if ( strcmp(_ca->ca_ma_rule->smr_mrule.mr_oid, OID_COMP_FILTER_MATCH ) == 0) {
+ struct berval bv;
+ bv.bv_val = cav->cav_ptr;
+ bv.bv_len = cav_cur_len( cav );
+ rc = get_comp_filter( op, &bv,(ComponentFilter**)&_ca->ca_cf, text );
+ if ( rc != LDAP_SUCCESS )
+ goto fail;
+ cav->cav_ptr = bv.bv_val;
+ assert( cav->cav_end >= bv.bv_val );
+ }
+
+ *ca = _ca;
+ return LDAP_SUCCESS;
+}
+
+static int
+parse_comp_filter( Operation* op, ComponentAssertionValue* cav,
+ ComponentFilter** filt, const char** text )
+{
+ /*
+ * A component filter looks like this coming in:
+ * Filter ::= CHOICE {
+ * item [0] ComponentAssertion,
+ * and [1] SEQUENCE OF ComponentFilter,
+ * or [2] SEQUENCE OF ComponentFilter,
+ * not [3] ComponentFilter,
+ * }
+ */
+
+ ber_tag_t tag;
+ int err = LDAP_SUCCESS;
+ ComponentFilter f;
+ /* TAG : item, and, or, not in RFC 4515 */
+ tag = strip_cav_tag( cav );
+
+ if ( tag == LBER_ERROR ) {
+ *text = "error decoding comp filter";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( tag != LDAP_COMP_FILTER_NOT ) {
+ err = strip_cav_str( cav, "{");
+ if ( err )
+ goto invalid;
+ }
+
+ f.cf_next = NULL;
+ f.cf_choice = tag;
+
+ switch ( f.cf_choice ) {
+ case LDAP_COMP_FILTER_AND:
+ Debug( LDAP_DEBUG_FILTER, "LDAP_COMP_FILTER_AND\n" );
+ err = get_comp_filter_list( op, cav, &f.cf_and, text );
+ if ( err != LDAP_SUCCESS ) {
+ break;
+ }
+ if ( f.cf_and == NULL ) {
+ f.cf_choice = SLAPD_FILTER_COMPUTED;
+ f.cf_result = LDAP_COMPARE_TRUE;
+ }
+ break;
+
+ case LDAP_COMP_FILTER_OR:
+ Debug( LDAP_DEBUG_FILTER, "LDAP_COMP_FILTER_OR\n" );
+ err = get_comp_filter_list( op, cav, &f.cf_or, text );
+ if ( err != LDAP_SUCCESS ) {
+ break;
+ }
+ if ( f.cf_or == NULL ) {
+ f.cf_choice = SLAPD_FILTER_COMPUTED;
+ f.cf_result = LDAP_COMPARE_FALSE;
+ }
+ /* no assert - list could be empty */
+ break;
+
+ case LDAP_COMP_FILTER_NOT:
+ Debug( LDAP_DEBUG_FILTER, "LDAP_COMP_FILTER_NOT\n" );
+ err = parse_comp_filter( op, cav, &f.cf_not, text );
+ if ( err != LDAP_SUCCESS ) {
+ break;
+ }
+
+ assert( f.cf_not != NULL );
+ if ( f.cf_not->cf_choice == SLAPD_FILTER_COMPUTED ) {
+ int fresult = f.cf_not->cf_result;
+ f.cf_choice = SLAPD_FILTER_COMPUTED;
+ op->o_tmpfree( f.cf_not, op->o_tmpmemctx );
+ f.cf_not = NULL;
+
+ switch ( fresult ) {
+ case LDAP_COMPARE_TRUE:
+ f.cf_result = LDAP_COMPARE_FALSE;
+ break;
+ case LDAP_COMPARE_FALSE:
+ f.cf_result = LDAP_COMPARE_TRUE;
+ break;
+ default: ;
+ /* (!Undefined) is Undefined */
+ }
+ }
+ break;
+
+ case LDAP_COMP_FILTER_ITEM:
+ Debug( LDAP_DEBUG_FILTER, "LDAP_COMP_FILTER_ITEM\n" );
+ err = get_item( op, cav, &f.cf_ca, text );
+ if ( err != LDAP_SUCCESS ) {
+ break;
+ }
+
+ assert( f.cf_ca != NULL );
+ break;
+
+ default:
+ f.cf_choice = SLAPD_FILTER_COMPUTED;
+ f.cf_result = SLAPD_COMPARE_UNDEFINED;
+ break;
+ }
+
+invalid:
+ if ( err != LDAP_SUCCESS && err != SLAPD_DISCONNECT ) {
+ *text = "Component Filter Syntax Error";
+ return err;
+ }
+
+ if ( tag != LDAP_COMP_FILTER_NOT )
+ err = strip_cav_str( cav, "}");
+
+ if ( err == LDAP_SUCCESS ) {
+ if ( op ) {
+ *filt = op->o_tmpalloc( sizeof(f), op->o_tmpmemctx );
+ } else {
+ *filt = SLAP_MALLOC( sizeof(f) );
+ }
+ if ( *filt == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+ **filt = f;
+ }
+
+ return( err );
+}
+
+static int
+test_comp_filter_and(
+ Syntax *syn,
+ ComponentSyntaxInfo *a,
+ ComponentFilter *flist )
+{
+ ComponentFilter *f;
+ int rtn = LDAP_COMPARE_TRUE;
+
+ for ( f = flist ; f != NULL; f = f->cf_next ) {
+ int rc = test_comp_filter( syn, a, f );
+ if ( rc == LDAP_COMPARE_FALSE ) {
+ rtn = rc;
+ break;
+ }
+
+ if ( rc != LDAP_COMPARE_TRUE ) {
+ rtn = rc;
+ }
+ }
+
+ return rtn;
+}
+
+static int
+test_comp_filter_or(
+ Syntax *syn,
+ ComponentSyntaxInfo *a,
+ ComponentFilter *flist )
+{
+ ComponentFilter *f;
+ int rtn = LDAP_COMPARE_TRUE;
+
+ for ( f = flist ; f != NULL; f = f->cf_next ) {
+ int rc = test_comp_filter( syn, a, f );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rtn = rc;
+ break;
+ }
+
+ if ( rc != LDAP_COMPARE_FALSE ) {
+ rtn = rc;
+ }
+ }
+
+ return rtn;
+}
+
+int
+csi_value_match( MatchingRule *mr, struct berval* bv_attr,
+ struct berval* bv_assert )
+{
+ int rc;
+ int match;
+
+ assert( mr != NULL );
+ assert( !(mr->smr_usage & SLAP_MR_COMPONENT) );
+
+ if( !mr->smr_match ) return LDAP_INAPPROPRIATE_MATCHING;
+
+ rc = (mr->smr_match)( &match, 0, NULL /*ad->ad_type->sat_syntax*/,
+ mr, bv_attr, bv_assert );
+
+ if ( rc != LDAP_SUCCESS ) return rc;
+
+ return match ? LDAP_COMPARE_FALSE : LDAP_COMPARE_TRUE;
+}
+
+/*
+ * return codes : LDAP_COMPARE_TRUE, LDAP_COMPARE_FALSE
+ */
+static int
+test_comp_filter_item(
+ Syntax *syn,
+ ComponentSyntaxInfo *csi_attr,
+ ComponentAssertion *ca )
+{
+ int rc;
+ void *attr_nm, *assert_nm;
+
+ if ( strcmp(ca->ca_ma_rule->smr_mrule.mr_oid,
+ OID_COMP_FILTER_MATCH ) == 0 && ca->ca_cf ) {
+ /* componentFilterMatch inside of componentFilterMatch */
+ rc = test_comp_filter( syn, csi_attr, ca->ca_cf );
+ return rc;
+ }
+
+ /* Memory for storing will-be-extracted attribute values */
+ attr_nm = nibble_mem_allocator ( 1024*4 , 1024 );
+ if ( !attr_nm ) return LDAP_PROTOCOL_ERROR;
+
+ /* Memory for storing component assertion values */
+ if( !ca->ca_comp_data.cd_mem_op ) {
+ assert_nm = nibble_mem_allocator ( 256, 64 );
+ if ( !assert_nm ) {
+ nibble_mem_free ( attr_nm );
+ return LDAP_PROTOCOL_ERROR;
+ }
+ ca->ca_comp_data.cd_mem_op = assert_nm;
+
+ } else {
+ assert_nm = ca->ca_comp_data.cd_mem_op;
+ }
+
+ /* component reference initialization */
+ if ( ca->ca_comp_ref ) {
+ ca->ca_comp_ref->cr_curr = ca->ca_comp_ref->cr_list;
+ }
+ rc = test_components( attr_nm, assert_nm, csi_attr, ca );
+
+ /* free memory used for storing extracted attribute value */
+ nibble_mem_free ( attr_nm );
+ return rc;
+}
+
+static int
+test_comp_filter(
+ Syntax *syn,
+ ComponentSyntaxInfo *a,
+ ComponentFilter *f )
+{
+ int rc;
+
+ if ( !f ) return LDAP_PROTOCOL_ERROR;
+
+ Debug( LDAP_DEBUG_FILTER, "test_comp_filter\n" );
+ switch ( f->cf_choice ) {
+ case SLAPD_FILTER_COMPUTED:
+ rc = f->cf_result;
+ break;
+ case LDAP_COMP_FILTER_AND:
+ rc = test_comp_filter_and( syn, a, f->cf_and );
+ break;
+ case LDAP_COMP_FILTER_OR:
+ rc = test_comp_filter_or( syn, a, f->cf_or );
+ break;
+ case LDAP_COMP_FILTER_NOT:
+ rc = test_comp_filter( syn, a, f->cf_not );
+
+ switch ( rc ) {
+ case LDAP_COMPARE_TRUE:
+ rc = LDAP_COMPARE_FALSE;
+ break;
+ case LDAP_COMPARE_FALSE:
+ rc = LDAP_COMPARE_TRUE;
+ break;
+ }
+ break;
+ case LDAP_COMP_FILTER_ITEM:
+ rc = test_comp_filter_item( syn, a, f->cf_ca );
+ break;
+ default:
+ rc = LDAP_PROTOCOL_ERROR;
+ }
+
+ return( rc );
+}
+
+static void
+free_comp_filter_list( ComponentFilter* f )
+{
+ ComponentFilter* tmp;
+ for ( tmp = f; tmp; tmp = tmp->cf_next ) {
+ free_comp_filter( tmp );
+ }
+}
+
+static void
+free_comp_filter( ComponentFilter* f )
+{
+ if ( !f ) {
+ Debug( LDAP_DEBUG_FILTER,
+ "free_comp_filter: Invalid filter so failed to release memory\n" );
+ return;
+ }
+ switch ( f->cf_choice ) {
+ case LDAP_COMP_FILTER_AND:
+ case LDAP_COMP_FILTER_OR:
+ free_comp_filter_list( f->cf_any );
+ break;
+ case LDAP_COMP_FILTER_NOT:
+ free_comp_filter( f->cf_any );
+ break;
+ case LDAP_COMP_FILTER_ITEM:
+ if ( nibble_mem_free && f->cf_ca->ca_comp_data.cd_mem_op ) {
+ nibble_mem_free( f->cf_ca->ca_comp_data.cd_mem_op );
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void
+component_free( ComponentFilter *f ) {
+ free_comp_filter( f );
+}
+
+void
+free_ComponentData( Attribute *a ) {
+ if ( a->a_comp_data->cd_mem_op )
+ component_destructor( a->a_comp_data->cd_mem_op );
+ free ( a->a_comp_data );
+ a->a_comp_data = NULL;
+}
+#endif
diff --git a/servers/slapd/component.h b/servers/slapd/component.h
new file mode 100644
index 0000000..202773e
--- /dev/null
+++ b/servers/slapd/component.h
@@ -0,0 +1,76 @@
+/* component.h */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2004 by IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _H_SLAPD_COMPONENT
+#define _H_SLAPD_COMPONENT
+
+#include "portable.h"
+
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ldap_pvt.h>
+#include "lutil.h"
+#include <ldap.h>
+#include "slap.h"
+
+typedef enum { ASN_BASIC, ASN_COMPOSITE } AsnType;
+/*
+ * Decoder Modes
+ * Different operation is required to handle Decoding(2), Extracted Component
+ * decoding(0), ANY DEFINED TYPe(2)
+ * b0 : Component Alloc(yes)
+ * Constructed type : Component Alloc (Yes)
+ * Primitive type : Component Alloc (Yes)
+ * set to mode 2 in inner decoders
+ * b1 : Component Alloc (No)
+ * Constructed type : Component Alloc (No)
+ * Primitive type : Component Alloc (No)
+ * set to mode 2 in inner decoders
+ * b2 : Default Mode
+ * Constructed type : Component Alloc (Yes)
+ * Primitive type : Component Alloc (No)
+ * in addition to above modes, the 4th bit has special meaning,
+ * b4 : if the 4th bit is clear, DecxxxContent is called
+ * b4 : if the 4th bit is set, Decxxx is called, then it is cleared.
+ */
+#define DEC_ALLOC_MODE_0 0x01
+#define DEC_ALLOC_MODE_1 0x02
+#define DEC_ALLOC_MODE_2 0x04
+#define CALL_TAG_DECODER 0x08
+#define CALL_CONTENT_DECODER ~0x08
+/*
+ * For Attribute Aliasing
+ */
+#define MAX_ALIASING_ENTRY 128
+typedef struct comp_attribute_aliasing {
+ AttributeDescription* aa_aliasing_ad;
+ AttributeDescription* aa_aliased_ad;
+ ComponentFilter* aa_cf;
+ MatchingRule* aa_mr;
+ char* aa_cf_str;
+} AttributeAliasing;
+
+typedef struct comp_matchingrule_aliasing {
+ MatchingRule* mra_aliasing_attr;
+ MatchingRule* mra_aliased_attr;
+ AttributeDescription* mra_attr;
+ ComponentFilter* mra_cf;
+ MatchingRule* mra_mr;
+ char* mra_cf_str;
+} MatchingRuleAliasing;
+
+#endif
diff --git a/servers/slapd/config.c b/servers/slapd/config.c
new file mode 100644
index 0000000..8823c74
--- /dev/null
+++ b/servers/slapd/config.c
@@ -0,0 +1,2616 @@
+/* config.c - configuration file handling routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/signal.h>
+#include <ac/socket.h>
+#include <ac/errno.h>
+#include <ac/unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef S_ISREG
+#define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
+#endif
+
+#include "slap.h"
+#ifdef LDAP_SLAPI
+#include "slapi/slapi.h"
+#endif
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "ldif.h"
+#include "slap-config.h"
+
+#ifdef _WIN32
+#define LUTIL_ATOULX lutil_atoullx
+#define Z "I"
+#else
+#define LUTIL_ATOULX lutil_atoulx
+#define Z "z"
+#endif
+
+#define ARGS_STEP 512
+
+/*
+ * defaults for various global variables
+ */
+slap_mask_t global_allows = 0;
+slap_mask_t global_disallows = 0;
+int global_gentlehup = 0;
+int global_idletimeout = 0;
+int global_writetimeout = 0;
+char *global_host = NULL;
+struct berval global_host_bv = BER_BVNULL;
+char *global_realm = NULL;
+char *sasl_host = NULL;
+char *sasl_cbinding = NULL;
+char **default_passwd_hash = NULL;
+struct berval default_search_base = BER_BVNULL;
+struct berval default_search_nbase = BER_BVNULL;
+
+ber_len_t sockbuf_max_incoming = SLAP_SB_MAX_INCOMING_DEFAULT;
+ber_len_t sockbuf_max_incoming_auth= SLAP_SB_MAX_INCOMING_AUTH;
+
+int slap_conn_max_pending = SLAP_CONN_MAX_PENDING_DEFAULT;
+int slap_conn_max_pending_auth = SLAP_CONN_MAX_PENDING_AUTH;
+
+int slap_max_filter_depth = SLAP_MAX_FILTER_DEPTH_DEFAULT;
+
+char *slapd_pid_file = NULL;
+char *slapd_args_file = NULL;
+
+int use_reverse_lookup = 0;
+
+#ifdef LDAP_SLAPI
+int slapi_plugins_used = 0;
+#endif
+
+static int fp_getline(FILE *fp, ConfigArgs *c);
+static void fp_getline_init(ConfigArgs *c);
+
+static char *strtok_quote(char *line, char *sep, char **quote_ptr, int *inquote);
+static char *strtok_quote_ldif(char **line);
+
+ConfigArgs *
+new_config_args( BackendDB *be, const char *fname, int lineno, int argc, char **argv )
+{
+ ConfigArgs *c;
+ c = ch_calloc( 1, sizeof( ConfigArgs ) );
+ if ( c == NULL ) return(NULL);
+ c->be = be;
+ c->fname = fname;
+ c->argc = argc;
+ c->argv = argv;
+ c->lineno = lineno;
+ snprintf( c->log, sizeof( c->log ), "%s: line %d", fname, lineno );
+ return(c);
+}
+
+void
+init_config_argv( ConfigArgs *c )
+{
+ c->argv = ch_calloc( ARGS_STEP + 1, sizeof( *c->argv ) );
+ c->argv_size = ARGS_STEP + 1;
+}
+
+ConfigTable *config_find_keyword(ConfigTable *Conf, ConfigArgs *c) {
+ int i;
+
+ for(i = 0; Conf[i].name; i++)
+ if( (Conf[i].length && (!strncasecmp(c->argv[0], Conf[i].name, Conf[i].length))) ||
+ (!strcasecmp(c->argv[0], Conf[i].name)) ) break;
+ if ( !Conf[i].name ) return NULL;
+ if (( Conf[i].arg_type & ARGS_TYPES ) == ARG_BINARY ) {
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(c->linelen);
+ ch_free( c->tline );
+ c->tline = ch_malloc( decode_len+1 );
+ c->linelen = lutil_b64_pton( c->line, c->tline, decode_len );
+ if ( c->linelen < 0 )
+ {
+ ch_free( c->tline );
+ c->tline = NULL;
+ return NULL;
+ }
+ c->line = c->tline;
+ }
+ return Conf+i;
+}
+
+int config_check_vals(ConfigTable *Conf, ConfigArgs *c, int check_only ) {
+ int rc, arg_user, arg_type, arg_syn, iarg;
+ unsigned uiarg;
+ long larg;
+ size_t ularg;
+ ber_len_t barg;
+
+ if(Conf->arg_type == ARG_IGNORED) {
+ Debug(LDAP_DEBUG_CONFIG, "%s: keyword <%s> ignored\n",
+ c->log, Conf->name );
+ return(0);
+ }
+ arg_type = Conf->arg_type & ARGS_TYPES;
+ arg_user = Conf->arg_type & ARGS_USERLAND;
+ arg_syn = Conf->arg_type & ARGS_SYNTAX;
+
+ if((arg_type == ARG_DN) && c->argc == 1) {
+ c->argc = 2;
+ c->argv[1] = "";
+ }
+ if(Conf->min_args && (c->argc < Conf->min_args)) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> missing <%s> argument",
+ c->argv[0], Conf->what ? Conf->what : "" );
+ Debug(LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: keyword %s\n", c->log, c->cr_msg );
+ return(ARG_BAD_CONF);
+ }
+ if(Conf->max_args && (c->argc > Conf->max_args)) {
+ char *ignored = " ignored";
+
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> extra cruft after <%s>",
+ c->argv[0], Conf->what );
+
+ ignored = "";
+ Debug(LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s%s.\n",
+ c->log, c->cr_msg, ignored );
+ return(ARG_BAD_CONF);
+ }
+ if((arg_syn & ARG_DB) && !c->be) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> only allowed within database declaration",
+ c->argv[0] );
+ Debug(LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: keyword %s\n",
+ c->log, c->cr_msg );
+ return(ARG_BAD_CONF);
+ }
+ if((arg_syn & ARG_PRE_BI) && c->bi) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> must occur before any backend %sdeclaration",
+ c->argv[0], (arg_syn & ARG_PRE_DB) ? "or database " : "" );
+ Debug(LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: keyword %s\n",
+ c->log, c->cr_msg );
+ return(ARG_BAD_CONF);
+ }
+ if((arg_syn & ARG_PRE_DB) && c->be && c->be != frontendDB) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> must occur before any database declaration",
+ c->argv[0] );
+ Debug(LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: keyword %s\n",
+ c->log, c->cr_msg );
+ return(ARG_BAD_CONF);
+ }
+ if((arg_syn & ARG_PAREN) && *c->argv[1] != '(' /*')'*/) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> old format not supported", c->argv[0] );
+ Debug(LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return(ARG_BAD_CONF);
+ }
+ if(arg_type && !Conf->arg_item && !(arg_syn & ARG_OFFSET)) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> invalid config_table, arg_item is NULL",
+ c->argv[0] );
+ Debug(LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return(ARG_BAD_CONF);
+ }
+ c->type = arg_user;
+ memset(&c->values, 0, sizeof(c->values));
+ if(arg_type == ARG_STRING) {
+ assert( c->argc == 2 );
+ if ( !check_only )
+ c->value_string = ch_strdup(c->argv[1]);
+ } else if(arg_type == ARG_BERVAL) {
+ assert( c->argc == 2 );
+ if ( !check_only )
+ ber_str2bv( c->argv[1], 0, 1, &c->value_bv );
+ } else if(arg_type == ARG_BINARY) {
+ assert( c->argc == 2 );
+ if ( !check_only ) {
+ c->value_bv.bv_len = c->linelen;
+ c->value_bv.bv_val = ch_malloc( c->linelen );
+ AC_MEMCPY( c->value_bv.bv_val, c->line, c->linelen );
+ }
+ } else if(arg_type == ARG_DN) {
+ struct berval bv;
+ assert( c->argc == 2 );
+ ber_str2bv( c->argv[1], 0, 0, &bv );
+ rc = dnPrettyNormal( NULL, &bv, &c->value_dn, &c->value_ndn, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> invalid DN %d (%s)",
+ c->argv[0], rc, ldap_err2string( rc ));
+ Debug(LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n" , c->log, c->cr_msg );
+ return(ARG_BAD_CONF);
+ }
+ if ( check_only ) {
+ ch_free( c->value_ndn.bv_val );
+ ch_free( c->value_dn.bv_val );
+ }
+ } else if(arg_type == ARG_ATDESC) {
+ const char *text = NULL;
+ assert( c->argc == 2 );
+ c->value_ad = NULL;
+ rc = slap_str2ad( c->argv[1], &c->value_ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> invalid AttributeDescription %d (%s)",
+ c->argv[0], rc, text );
+ Debug(LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n" , c->log, c->cr_msg );
+ return(ARG_BAD_CONF);
+ }
+ } else { /* all numeric */
+ int j;
+ iarg = 0; larg = 0; barg = 0;
+ switch(arg_type) {
+ case ARG_INT:
+ assert( c->argc == 2 );
+ if ( lutil_atoix( &iarg, c->argv[1], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "<%s> unable to parse \"%s\" as int",
+ c->argv[0], c->argv[1] );
+ Debug(LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return(ARG_BAD_CONF);
+ }
+ break;
+ case ARG_UINT:
+ assert( c->argc == 2 );
+ if ( lutil_atoux( &uiarg, c->argv[1], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "<%s> unable to parse \"%s\" as unsigned int",
+ c->argv[0], c->argv[1] );
+ Debug(LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return(ARG_BAD_CONF);
+ }
+ break;
+ case ARG_LONG:
+ assert( c->argc == 2 );
+ if ( lutil_atolx( &larg, c->argv[1], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "<%s> unable to parse \"%s\" as long",
+ c->argv[0], c->argv[1] );
+ Debug(LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return(ARG_BAD_CONF);
+ }
+ break;
+ case ARG_ULONG:
+ assert( c->argc == 2 );
+ if ( LUTIL_ATOULX( &ularg, c->argv[1], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "<%s> unable to parse \"%s\" as unsigned long",
+ c->argv[0], c->argv[1] );
+ Debug(LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return(ARG_BAD_CONF);
+ }
+ break;
+ case ARG_BER_LEN_T: {
+ unsigned long l;
+ assert( c->argc == 2 );
+ if ( lutil_atoulx( &l, c->argv[1], 0 ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "<%s> unable to parse \"%s\" as ber_len_t",
+ c->argv[0], c->argv[1] );
+ Debug(LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return(ARG_BAD_CONF);
+ }
+ barg = (ber_len_t)l;
+ } break;
+ case ARG_ON_OFF:
+ /* note: this is an explicit exception
+ * to the "need exactly 2 args" rule */
+ if (c->argc == 1) {
+ iarg = 1;
+ } else if ( !strcasecmp(c->argv[1], "on") ||
+ !strcasecmp(c->argv[1], "true") ||
+ !strcasecmp(c->argv[1], "yes") )
+ {
+ iarg = 1;
+ } else if ( !strcasecmp(c->argv[1], "off") ||
+ !strcasecmp(c->argv[1], "false") ||
+ !strcasecmp(c->argv[1], "no") )
+ {
+ iarg = 0;
+ } else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> invalid value",
+ c->argv[0] );
+ Debug(LDAP_DEBUG_ANY|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return(ARG_BAD_CONF);
+ }
+ break;
+ }
+ j = (arg_type & ARG_NONZERO) ? 1 : 0;
+ if(iarg < j && larg < j && barg < (unsigned)j ) {
+ larg = larg ? larg : (barg ? (long)barg : iarg);
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> invalid value",
+ c->argv[0] );
+ Debug(LDAP_DEBUG_ANY|LDAP_DEBUG_NONE, "%s: %s\n",
+ c->log, c->cr_msg );
+ return(ARG_BAD_CONF);
+ }
+ switch(arg_type) {
+ case ARG_ON_OFF:
+ case ARG_INT: c->value_int = iarg; break;
+ case ARG_UINT: c->value_uint = uiarg; break;
+ case ARG_LONG: c->value_long = larg; break;
+ case ARG_ULONG: c->value_ulong = ularg; break;
+ case ARG_BER_LEN_T: c->value_ber_t = barg; break;
+ }
+ }
+ return 0;
+}
+
+int config_set_vals(ConfigTable *Conf, ConfigArgs *c) {
+ int rc, arg_type;
+ void *ptr = NULL;
+
+ arg_type = Conf->arg_type;
+ if(arg_type & ARG_MAGIC) {
+ if(!c->be) c->be = frontendDB;
+ c->cr_msg[0] = '\0';
+ rc = (*((ConfigDriver*)Conf->arg_item))(c);
+#if 0
+ if(c->be == frontendDB) c->be = NULL;
+#endif
+ if(rc) {
+ if ( !c->cr_msg[0] ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> handler exited with %d",
+ c->argv[0], rc );
+ Debug(LDAP_DEBUG_CONFIG, "%s: %s!\n",
+ c->log, c->cr_msg );
+ }
+ return(ARG_BAD_CONF);
+ }
+ return(0);
+ }
+ if(arg_type & ARG_OFFSET) {
+ if (c->be && c->table == Cft_Database)
+ ptr = c->be->be_private;
+ else if (c->bi)
+ ptr = c->bi->bi_private;
+ else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> offset is missing base pointer",
+ c->argv[0] );
+ Debug(LDAP_DEBUG_CONFIG, "%s: %s!\n",
+ c->log, c->cr_msg );
+ return(ARG_BAD_CONF);
+ }
+ ptr = (void *)((char *)ptr + (long)Conf->arg_item);
+ } else if (arg_type & ARGS_TYPES) {
+ ptr = Conf->arg_item;
+ }
+ if(arg_type & ARGS_TYPES)
+ switch(arg_type & ARGS_TYPES) {
+ case ARG_ON_OFF:
+ case ARG_INT: *(int*)ptr = c->value_int; break;
+ case ARG_UINT: *(unsigned*)ptr = c->value_uint; break;
+ case ARG_LONG: *(long*)ptr = c->value_long; break;
+ case ARG_ULONG: *(size_t*)ptr = c->value_ulong; break;
+ case ARG_BER_LEN_T: *(ber_len_t*)ptr = c->value_ber_t; break;
+ case ARG_STRING: {
+ char *cc = *(char**)ptr;
+ if(cc) {
+ if ((arg_type & ARG_UNIQUE) && c->op == SLAP_CONFIG_ADD ) {
+ Debug(LDAP_DEBUG_CONFIG, "%s: already set %s!\n",
+ c->log, Conf->name );
+ return(ARG_BAD_CONF);
+ }
+ ch_free(cc);
+ }
+ *(char **)ptr = c->value_string;
+ break;
+ }
+ case ARG_BERVAL:
+ case ARG_BINARY:
+ *(struct berval *)ptr = c->value_bv;
+ break;
+ case ARG_ATDESC:
+ *(AttributeDescription **)ptr = c->value_ad;
+ break;
+ }
+ return(0);
+}
+
+int config_add_vals(ConfigTable *Conf, ConfigArgs *c) {
+ int rc, arg_type;
+
+ arg_type = Conf->arg_type;
+ if(arg_type == ARG_IGNORED) {
+ Debug(LDAP_DEBUG_CONFIG, "%s: keyword <%s> ignored\n",
+ c->log, Conf->name );
+ return(0);
+ }
+ rc = config_check_vals( Conf, c, 0 );
+ if ( rc ) return rc;
+ return config_set_vals( Conf, c );
+}
+
+int
+config_del_vals(ConfigTable *cf, ConfigArgs *c)
+{
+ int rc = 0;
+ void *ptr;
+
+ if ( cf->arg_type & ARG_MAGIC ) {
+ c->argv[0] = cf->ad->ad_cname.bv_val;
+ c->op = LDAP_MOD_DELETE;
+ c->type = cf->arg_type & ARGS_USERLAND;
+ rc = (*((ConfigDriver*)cf->arg_item))(c);
+ return rc;
+ }
+ /* If there is no handler, just zero it */
+ if ( cf->arg_type & ARG_OFFSET ) {
+ if ( c->be && c->table == Cft_Database )
+ ptr = c->be->be_private;
+ else if ( c->bi )
+ ptr = c->bi->bi_private;
+ else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> offset is missing base pointer",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s!\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ ptr = (void *)((char *)ptr + (long)cf->arg_item);
+ } else if ( cf->arg_type & ARGS_TYPES ) {
+ ptr = cf->arg_item;
+ }
+ if ( cf->arg_type & ARGS_TYPES )
+ switch ( cf->arg_type & ARGS_TYPES ) {
+ case ARG_ON_OFF:
+ case ARG_INT: *(int *)ptr = cf->arg_default.v_int; break;
+ case ARG_UINT: *(unsigned *)ptr = cf->arg_default.v_uint; break;
+ case ARG_LONG: *(long *)ptr = cf->arg_default.v_long; break;
+ case ARG_ULONG: *(size_t *)ptr = cf->arg_default.v_ulong; break;
+ case ARG_BER_LEN_T: *(ber_len_t *)ptr = cf->arg_default.v_ber_t; break;
+ case ARG_STRING:
+ ch_free( *(char**)ptr );
+ if ( cf->arg_default.v_string ) {
+ *(char **)ptr = ch_strdup( cf->arg_default.v_string );
+ } else {
+ *(char **)ptr = NULL;
+ }
+ break;
+ case ARG_BERVAL:
+ case ARG_BINARY:
+ ch_free( ((struct berval *)ptr)->bv_val );
+ if ( !BER_BVISNULL( &cf->arg_default.v_bv ) ) {
+ ber_dupbv( (struct berval *)ptr, &cf->arg_default.v_bv );
+ } else {
+ BER_BVZERO( (struct berval *)ptr );
+ }
+ break;
+ case ARG_ATDESC:
+ *(AttributeDescription **)ptr = cf->arg_default.v_ad;
+ break;
+ }
+ return rc;
+}
+
+int
+config_get_vals(ConfigTable *cf, ConfigArgs *c)
+{
+ int rc = 0;
+ struct berval bv;
+ void *ptr;
+
+ if ( cf->arg_type & ARG_IGNORED ) {
+ return 1;
+ }
+
+ memset(&c->values, 0, sizeof(c->values));
+ c->rvalue_vals = NULL;
+ c->rvalue_nvals = NULL;
+ c->op = SLAP_CONFIG_EMIT;
+ c->type = cf->arg_type & ARGS_USERLAND;
+
+ if ( cf->arg_type & ARG_MAGIC ) {
+ rc = (*((ConfigDriver*)cf->arg_item))(c);
+ if ( rc ) return rc;
+ } else {
+ if ( cf->arg_type & ARG_OFFSET ) {
+ if (c->be && c->table == Cft_Database)
+ ptr = c->be->be_private;
+ else if ( c->bi )
+ ptr = c->bi->bi_private;
+ else
+ return 1;
+ ptr = (void *)((char *)ptr + (long)cf->arg_item);
+ } else {
+ ptr = cf->arg_item;
+ }
+
+ switch(cf->arg_type & ARGS_TYPES) {
+ case ARG_ON_OFF:
+ case ARG_INT: c->value_int = *(int *)ptr; break;
+ case ARG_UINT: c->value_uint = *(unsigned *)ptr; break;
+ case ARG_LONG: c->value_long = *(long *)ptr; break;
+ case ARG_ULONG: c->value_ulong = *(size_t *)ptr; break;
+ case ARG_BER_LEN_T: c->value_ber_t = *(ber_len_t *)ptr; break;
+ case ARG_STRING:
+ if ( *(char **)ptr )
+ c->value_string = ch_strdup(*(char **)ptr);
+ break;
+ case ARG_BERVAL:
+ c->value_bv = *((struct berval *)ptr); break;
+ case ARG_ATDESC:
+ c->value_ad = *(AttributeDescription **)ptr; break;
+ }
+ }
+ if ( cf->arg_type & ARGS_TYPES) {
+ bv.bv_len = 0;
+ bv.bv_val = c->log;
+ switch(cf->arg_type & ARGS_TYPES) {
+ case ARG_INT: bv.bv_len = snprintf(bv.bv_val, sizeof( c->log ), "%d", c->value_int); break;
+ case ARG_UINT: bv.bv_len = snprintf(bv.bv_val, sizeof( c->log ), "%u", c->value_uint); break;
+ case ARG_LONG: bv.bv_len = snprintf(bv.bv_val, sizeof( c->log ), "%ld", c->value_long); break;
+ case ARG_ULONG: bv.bv_len = snprintf(bv.bv_val, sizeof( c->log ), "%" Z "u", c->value_ulong); break;
+ case ARG_BER_LEN_T: bv.bv_len = snprintf(bv.bv_val, sizeof( c->log ), "%ld", c->value_ber_t); break;
+ case ARG_ON_OFF: bv.bv_len = snprintf(bv.bv_val, sizeof( c->log ), "%s",
+ c->value_int ? "TRUE" : "FALSE"); break;
+ case ARG_STRING:
+ if ( c->value_string && c->value_string[0]) {
+ ber_str2bv( c->value_string, 0, 0, &bv);
+ } else {
+ return 1;
+ }
+ break;
+ case ARG_BERVAL:
+ if ( !BER_BVISEMPTY( &c->value_bv )) {
+ bv = c->value_bv;
+ } else {
+ return 1;
+ }
+ break;
+ case ARG_ATDESC:
+ if ( c->value_ad ) {
+ bv = c->value_ad->ad_cname;
+ } else {
+ return 1;
+ }
+ break;
+ default:
+ bv.bv_val = NULL;
+ break;
+ }
+ if (bv.bv_val == c->log && bv.bv_len >= sizeof( c->log ) ) {
+ return 1;
+ }
+ if (( cf->arg_type & ARGS_TYPES ) == ARG_STRING ) {
+ ber_bvarray_add(&c->rvalue_vals, &bv);
+ } else if ( !BER_BVISNULL( &bv ) ) {
+ value_add_one(&c->rvalue_vals, &bv);
+ }
+ /* else: maybe c->rvalue_vals already set? */
+ }
+ return rc;
+}
+
+int
+config_push_cleanup(ConfigArgs *ca, ConfigDriver *cleanup)
+{
+ int i;
+ /* silently ignore redundant push */
+ for (i=0; i < ca->num_cleanups; i++) {
+ if ( ca->cleanups[i] == cleanup )
+ return 0;
+ }
+
+ if (ca->num_cleanups >= SLAP_CONFIG_CLEANUP_MAX)
+ return -1;
+ ca->cleanups[ca->num_cleanups++] = cleanup;
+ return 0;
+}
+
+int
+config_run_cleanup(ConfigArgs *ca)
+{
+ int i, rc = 0;
+
+ for (i=0; i < ca->num_cleanups; i++) {
+ rc = ca->cleanups[i](ca);
+ if (rc)
+ break;
+ }
+ return rc;
+}
+
+int
+init_config_attrs(ConfigTable *ct) {
+ int i, code;
+
+ for (i=0; ct[i].name; i++ ) {
+ if ( !ct[i].attribute ) continue;
+ code = register_at( ct[i].attribute, &ct[i].ad, 1 );
+ if ( code ) {
+ fprintf( stderr, "init_config_attrs: register_at failed\n" );
+ return code;
+ }
+ if (( ct[i].arg_type & ARGS_TYPES ) == ARG_BINARY ) {
+ ldif_must_b64_encode_register( ct[i].ad->ad_cname.bv_val,
+ ct[i].ad->ad_type->sat_oid );
+ }
+ }
+
+ return 0;
+}
+
+int
+init_config_ocs( ConfigOCs *ocs ) {
+ int i, code;
+
+ for (i=0;ocs[i].co_def;i++) {
+ code = register_oc( ocs[i].co_def, &ocs[i].co_oc, 1 );
+ if ( code ) {
+ fprintf( stderr, "init_config_ocs: register_oc failed\n" );
+ return code;
+ }
+ }
+ return 0;
+}
+
+/* Split an LDIF line into space-separated tokens. Words may be grouped
+ * by quotes. A quoted string may begin in the middle of a word, but must
+ * end at the end of the word (be followed by whitespace or EOS). Any other
+ * quotes are passed through unchanged. All other characters are passed
+ * through unchanged.
+ */
+static char *
+strtok_quote_ldif( char **line )
+{
+ char *beg, *ptr, *quote=NULL;
+ int inquote=0;
+
+ ptr = *line;
+
+ if ( !ptr || !*ptr )
+ return NULL;
+
+ while( isspace( (unsigned char) *ptr )) ptr++;
+
+ if ( *ptr == '"' ) {
+ inquote = 1;
+ ptr++;
+ }
+
+ beg = ptr;
+
+ for (;*ptr;ptr++) {
+ if ( *ptr == '"' ) {
+ if ( inquote && ( !ptr[1] || isspace((unsigned char) ptr[1]))) {
+ *ptr++ = '\0';
+ break;
+ }
+ inquote = 1;
+ quote = ptr;
+ continue;
+ }
+ if ( inquote )
+ continue;
+ if ( isspace( (unsigned char) *ptr )) {
+ *ptr++ = '\0';
+ break;
+ }
+ }
+ if ( quote ) {
+ while ( quote < ptr ) {
+ *quote = quote[1];
+ quote++;
+ }
+ }
+ if ( !*ptr ) {
+ *line = NULL;
+ } else {
+ while ( isspace( (unsigned char) *ptr )) ptr++;
+ *line = ptr;
+ }
+ return beg;
+}
+
+void
+config_parse_ldif( ConfigArgs *c )
+{
+ char *next;
+ c->tline = ch_strdup(c->line);
+ next = c->tline;
+
+ while ((c->argv[c->argc] = strtok_quote_ldif( &next )) != NULL) {
+ c->argc++;
+ if ( c->argc >= c->argv_size ) {
+ char **tmp = ch_realloc( c->argv, (c->argv_size + ARGS_STEP) *
+ sizeof( *c->argv ));
+ c->argv = tmp;
+ c->argv_size += ARGS_STEP;
+ }
+ }
+ c->argv[c->argc] = NULL;
+}
+
+int
+config_parse_vals(ConfigTable *ct, ConfigArgs *c, int valx)
+{
+ int rc = 0;
+ int arg_type = ct->arg_type & ARGS_TYPES;
+
+ snprintf( c->log, sizeof( c->log ), "%s: value #%d",
+ ct->ad->ad_cname.bv_val, valx );
+ c->argc = 1;
+ c->argv[0] = ct->ad->ad_cname.bv_val;
+
+ if ( (( ct->arg_type & ARG_QUOTE ) && c->line[ 0 ] != '"' ) ||
+ (arg_type == ARG_BERVAL || arg_type == ARG_BINARY)) {
+ c->argv[c->argc] = c->line;
+ c->argc++;
+ c->argv[c->argc] = NULL;
+ c->tline = NULL;
+ } else {
+ config_parse_ldif( c );
+ }
+ rc = config_check_vals( ct, c, 1 );
+ ch_free( c->tline );
+ c->tline = NULL;
+
+ if ( rc )
+ rc = LDAP_CONSTRAINT_VIOLATION;
+
+ return rc;
+}
+
+int
+config_parse_add(ConfigTable *ct, ConfigArgs *c, int valx)
+{
+ int rc = 0;
+ int arg_type = ct->arg_type & ARGS_TYPES;
+
+ snprintf( c->log, sizeof( c->log ), "%s: value #%d",
+ ct->ad->ad_cname.bv_val, valx );
+ c->argc = 1;
+ c->argv[0] = ct->ad->ad_cname.bv_val;
+
+ if ( (( ct->arg_type & ARG_QUOTE ) && c->line[ 0 ] != '"' ) ||
+ (arg_type == ARG_BERVAL || arg_type == ARG_BINARY)) {
+ c->argv[c->argc] = c->line;
+ c->argc++;
+ c->argv[c->argc] = NULL;
+ c->tline = NULL;
+ } else {
+ config_parse_ldif( c );
+ }
+ c->op = LDAP_MOD_ADD;
+ rc = config_add_vals( ct, c );
+ ch_free( c->tline );
+
+ return rc;
+}
+
+int
+read_config_file(const char *fname, int depth, ConfigArgs *cf, ConfigTable *cft)
+{
+ FILE *fp;
+ ConfigTable *ct;
+ ConfigArgs *c;
+ int rc;
+ struct stat s;
+
+ c = ch_calloc( 1, sizeof( ConfigArgs ) );
+ if ( c == NULL ) {
+ return 1;
+ }
+
+ if ( depth ) {
+ memcpy( c, cf, sizeof( ConfigArgs ) );
+ } else {
+ c->depth = depth; /* XXX */
+ c->bi = NULL;
+ c->be = NULL;
+ }
+
+ c->valx = -1;
+ c->fname = fname;
+ init_config_argv( c );
+
+ if ( stat( fname, &s ) != 0 ) {
+ char ebuf[128];
+ int saved_errno = errno;
+ ldap_syslog = 1;
+ Debug(LDAP_DEBUG_ANY,
+ "could not stat config file \"%s\": %s (%d)\n",
+ fname, AC_STRERROR_R( saved_errno, ebuf, sizeof(ebuf) ), saved_errno);
+ ch_free( c->argv );
+ ch_free( c );
+ return(1);
+ }
+
+ if ( !S_ISREG( s.st_mode ) ) {
+ ldap_syslog = 1;
+ Debug(LDAP_DEBUG_ANY,
+ "regular file expected, got \"%s\"\n",
+ fname );
+ ch_free( c->argv );
+ ch_free( c );
+ return(1);
+ }
+
+ fp = fopen( fname, "r" );
+ if ( fp == NULL ) {
+ char ebuf[128];
+ int saved_errno = errno;
+ ldap_syslog = 1;
+ Debug(LDAP_DEBUG_ANY,
+ "could not open config file \"%s\": %s (%d)\n",
+ fname, AC_STRERROR_R( saved_errno, ebuf, sizeof(ebuf) ), saved_errno);
+ ch_free( c->argv );
+ ch_free( c );
+ return(1);
+ }
+
+ Debug(LDAP_DEBUG_CONFIG, "reading config file %s\n", fname );
+
+ fp_getline_init(c);
+
+ c->tline = NULL;
+
+ while ( fp_getline( fp, c ) ) {
+ /* skip comments and blank lines */
+ if ( c->line[0] == '#' || c->line[0] == '\0' ) {
+ continue;
+ }
+
+ snprintf( c->log, sizeof( c->log ), "%s: line %d",
+ c->fname, c->lineno );
+
+ c->argc = 0;
+ ch_free( c->tline );
+ if ( config_fp_parse_line( c ) ) {
+ rc = 1;
+ goto done;
+ }
+
+ if ( c->argc < 1 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: bad config line.\n",
+ c->log );
+ rc = 1;
+ goto done;
+ }
+
+ c->op = SLAP_CONFIG_ADD;
+
+ ct = config_find_keyword( cft, c );
+ if ( ct ) {
+ c->table = Cft_Global;
+ rc = config_add_vals( ct, c );
+ if ( !rc ) continue;
+
+ if ( rc & ARGS_USERLAND ) {
+ /* XXX a usertype would be opaque here */
+ Debug(LDAP_DEBUG_CONFIG, "%s: unknown user type <%s>\n",
+ c->log, c->argv[0] );
+ rc = 1;
+ goto done;
+
+ } else if ( rc == ARG_BAD_CONF ) {
+ rc = 1;
+ goto done;
+ }
+
+ } else if ( ( c->bi && !c->be ) || ( c->bi && c->bi->bi_flags & SLAP_BFLAG_STANDALONE ) ) {
+ rc = SLAP_CONF_UNKNOWN;
+ if ( c->bi->bi_cf_ocs ) {
+ ct = config_find_keyword( c->bi->bi_cf_ocs->co_table, c );
+ if ( ct ) {
+ c->table = c->bi->bi_cf_ocs->co_type;
+ rc = config_add_vals( ct, c );
+ }
+ }
+ if ( c->bi->bi_config && rc == SLAP_CONF_UNKNOWN ) {
+ rc = (*c->bi->bi_config)(c->bi, c->fname, c->lineno,
+ c->argc, c->argv);
+ }
+ if ( rc ) {
+ switch(rc) {
+ case SLAP_CONF_UNKNOWN:
+ Debug( LDAP_DEBUG_ANY, "%s: unknown directive "
+ "<%s> inside backend info definition.\n",
+ c->log, *c->argv );
+ default:
+ rc = 1;
+ goto done;
+ }
+ }
+
+ } else if ( c->be && c->be != frontendDB ) {
+ rc = SLAP_CONF_UNKNOWN;
+ if ( c->be->be_cf_ocs ) {
+ ct = config_find_keyword( c->be->be_cf_ocs->co_table, c );
+ if ( ct ) {
+ c->table = c->be->be_cf_ocs->co_type;
+ rc = config_add_vals( ct, c );
+ }
+ }
+ if ( c->be->be_config && rc == SLAP_CONF_UNKNOWN ) {
+ rc = (*c->be->be_config)(c->be, c->fname, c->lineno,
+ c->argc, c->argv);
+ }
+ if ( rc == SLAP_CONF_UNKNOWN && SLAP_ISGLOBALOVERLAY( frontendDB ) )
+ {
+ /* global overlays may need
+ * definitions inside other databases...
+ */
+ rc = (*frontendDB->be_config)( frontendDB,
+ c->fname, (int)c->lineno, c->argc, c->argv );
+ }
+
+ switch ( rc ) {
+ case 0:
+ break;
+
+ case SLAP_CONF_UNKNOWN:
+ Debug( LDAP_DEBUG_ANY, "%s: unknown directive "
+ "<%s> inside backend database definition.\n",
+ c->log, *c->argv );
+
+ default:
+ rc = 1;
+ goto done;
+ }
+
+ } else if ( frontendDB->be_config ) {
+ rc = (*frontendDB->be_config)( frontendDB,
+ c->fname, (int)c->lineno, c->argc, c->argv);
+ if ( rc ) {
+ switch(rc) {
+ case SLAP_CONF_UNKNOWN:
+ Debug( LDAP_DEBUG_ANY, "%s: unknown directive "
+ "<%s> inside global database definition.\n",
+ c->log, *c->argv );
+
+ default:
+ rc = 1;
+ goto done;
+ }
+ }
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s: unknown directive "
+ "<%s> outside backend info and database definitions.\n",
+ c->log, *c->argv );
+ rc = 1;
+ goto done;
+ }
+ }
+
+ rc = 0;
+
+done:
+ if ( cf ) {
+ cf->be = c->be;
+ cf->bi = c->bi;
+ }
+ ch_free(c->tline);
+ fclose(fp);
+ ch_free(c->argv);
+ ch_free(c);
+ return(rc);
+}
+
+/* restrictops, allows, disallows, requires, loglevel */
+
+int
+bverb_to_mask(struct berval *bword, slap_verbmasks *v) {
+ int i;
+ for(i = 0; !BER_BVISNULL(&v[i].word); i++) {
+ if(!ber_bvstrcasecmp(bword, &v[i].word)) break;
+ }
+ return(i);
+}
+
+int
+verb_to_mask(const char *word, slap_verbmasks *v) {
+ struct berval bword;
+ ber_str2bv( word, 0, 0, &bword );
+ return bverb_to_mask( &bword, v );
+}
+
+int
+verbs_to_mask(int argc, char *argv[], slap_verbmasks *v, slap_mask_t *m) {
+ int i, j;
+ for(i = 1; i < argc; i++) {
+ j = verb_to_mask(argv[i], v);
+ if(BER_BVISNULL(&v[j].word)) return i;
+ while (!v[j].mask) j--;
+ *m |= v[j].mask;
+ }
+ return(0);
+}
+
+/* Mask keywords that represent multiple bits should occur before single
+ * bit keywords in the verbmasks array.
+ */
+int
+mask_to_verbs(slap_verbmasks *v, slap_mask_t m, BerVarray *bva) {
+ int i, rc = 1;
+
+ if (m) {
+ for (i=0; !BER_BVISNULL(&v[i].word); i++) {
+ if (!v[i].mask) continue;
+ if (( m & v[i].mask ) == v[i].mask ) {
+ value_add_one( bva, &v[i].word );
+ rc = 0;
+ m ^= v[i].mask;
+ if ( !m ) break;
+ }
+ }
+ }
+ return rc;
+}
+
+/* Return the verbs as a single string, separated by delim */
+int
+mask_to_verbstring(slap_verbmasks *v, slap_mask_t m0, char delim, struct berval *bv)
+{
+ int i, rc = 1;
+
+ BER_BVZERO( bv );
+ if (m0) {
+ slap_mask_t m = m0;
+ char *ptr;
+ for (i=0; !BER_BVISNULL(&v[i].word); i++) {
+ if (!v[i].mask) continue;
+ if (( m & v[i].mask ) == v[i].mask ) {
+ bv->bv_len += v[i].word.bv_len + 1;
+ rc = 0;
+ m ^= v[i].mask;
+ if ( !m ) break;
+ }
+ }
+ bv->bv_val = ch_malloc(bv->bv_len);
+ bv->bv_len--;
+ ptr = bv->bv_val;
+ m = m0;
+ for (i=0; !BER_BVISNULL(&v[i].word); i++) {
+ if (!v[i].mask) continue;
+ if (( m & v[i].mask ) == v[i].mask ) {
+ ptr = lutil_strcopy(ptr, v[i].word.bv_val);
+ *ptr++ = delim;
+ m ^= v[i].mask;
+ if ( !m ) break;
+ }
+ }
+ ptr[-1] = '\0';
+ }
+ return rc;
+}
+
+/* Parse a verbstring */
+int
+verbstring_to_mask(slap_verbmasks *v, char *str, char delim, slap_mask_t *m) {
+ int j;
+ char *d;
+ struct berval bv;
+
+ do {
+ bv.bv_val = str;
+ d = strchr( str, delim );
+ if ( d )
+ bv.bv_len = d - str;
+ else
+ bv.bv_len = strlen( str );
+ j = bverb_to_mask( &bv, v );
+ if(BER_BVISNULL(&v[j].word)) return 1;
+ while (!v[j].mask) j--;
+ *m |= v[j].mask;
+ str += bv.bv_len + 1;
+ } while ( d );
+ return(0);
+}
+
+int
+slap_verbmasks_init( slap_verbmasks **vp, slap_verbmasks *v )
+{
+ int i;
+
+ assert( *vp == NULL );
+
+ for ( i = 0; !BER_BVISNULL( &v[ i ].word ); i++ ) /* EMPTY */;
+
+ *vp = ch_calloc( i + 1, sizeof( slap_verbmasks ) );
+
+ for ( i = 0; !BER_BVISNULL( &v[ i ].word ); i++ ) {
+ ber_dupbv( &(*vp)[ i ].word, &v[ i ].word );
+ *((slap_mask_t *)&(*vp)[ i ].mask) = v[ i ].mask;
+ }
+
+ BER_BVZERO( &(*vp)[ i ].word );
+
+ return 0;
+}
+
+int
+slap_verbmasks_destroy( slap_verbmasks *v )
+{
+ int i;
+
+ assert( v != NULL );
+
+ for ( i = 0; !BER_BVISNULL( &v[ i ].word ); i++ ) {
+ ch_free( v[ i ].word.bv_val );
+ }
+
+ ch_free( v );
+
+ return 0;
+}
+
+int
+slap_verbmasks_append(
+ slap_verbmasks **vp,
+ slap_mask_t m,
+ struct berval *v,
+ slap_mask_t *ignore )
+{
+ int i;
+
+ if ( !m ) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &(*vp)[ i ].word ); i++ ) {
+ if ( !(*vp)[ i ].mask ) continue;
+
+ if ( ignore != NULL ) {
+ int j;
+
+ for ( j = 0; ignore[ j ] != 0; j++ ) {
+ if ( (*vp)[ i ].mask == ignore[ j ] ) {
+ goto check_next;
+ }
+ }
+ }
+
+ if ( ( m & (*vp)[ i ].mask ) == (*vp)[ i ].mask ) {
+ if ( ber_bvstrcasecmp( v, &(*vp)[ i ].word ) == 0 ) {
+ /* already set; ignore */
+ return LDAP_SUCCESS;
+ }
+ /* conflicts */
+ return LDAP_TYPE_OR_VALUE_EXISTS;
+ }
+
+ if ( m & (*vp)[ i ].mask ) {
+ /* conflicts */
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+check_next:;
+ }
+
+ *vp = ch_realloc( *vp, sizeof( slap_verbmasks ) * ( i + 2 ) );
+ ber_dupbv( &(*vp)[ i ].word, v );
+ *((slap_mask_t *)&(*vp)[ i ].mask) = m;
+ BER_BVZERO( &(*vp)[ i + 1 ].word );
+
+ return LDAP_SUCCESS;
+}
+
+int
+enum_to_verb(slap_verbmasks *v, slap_mask_t m, struct berval *bv) {
+ int i;
+
+ for (i=0; !BER_BVISNULL(&v[i].word); i++) {
+ if ( m == v[i].mask ) {
+ if ( bv != NULL ) {
+ *bv = v[i].word;
+ }
+ return i;
+ }
+ }
+ return -1;
+}
+
+/* register a new verbmask */
+static int
+slap_verbmask_register( slap_verbmasks *vm_, slap_verbmasks **vmp, struct berval *bv, int mask )
+{
+ slap_verbmasks *vm = *vmp;
+ int i;
+
+ /* check for duplicate word */
+ /* NOTE: we accept duplicate codes; the first occurrence will be used
+ * when mapping from mask to verb */
+ i = verb_to_mask( bv->bv_val, vm );
+ if ( !BER_BVISNULL( &vm[ i ].word ) ) {
+ return -1;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &vm[ i ].word ); i++ )
+ ;
+
+ if ( vm == vm_ ) {
+ /* first time: duplicate array */
+ vm = ch_calloc( i + 2, sizeof( slap_verbmasks ) );
+ for ( i = 0; !BER_BVISNULL( &vm_[ i ].word ); i++ )
+ {
+ ber_dupbv( &vm[ i ].word, &vm_[ i ].word );
+ *((slap_mask_t*)&vm[ i ].mask) = vm_[ i ].mask;
+ }
+
+ } else {
+ vm = ch_realloc( vm, (i + 2) * sizeof( slap_verbmasks ) );
+ }
+
+ ber_dupbv( &vm[ i ].word, bv );
+ *((slap_mask_t*)&vm[ i ].mask) = mask;
+
+ BER_BVZERO( &vm[ i+1 ].word );
+
+ *vmp = vm;
+
+ return i;
+}
+
+static slap_verbmasks slap_ldap_response_code_[] = {
+ { BER_BVC("success"), LDAP_SUCCESS },
+
+ { BER_BVC("operationsError"), LDAP_OPERATIONS_ERROR },
+ { BER_BVC("protocolError"), LDAP_PROTOCOL_ERROR },
+ { BER_BVC("timelimitExceeded"), LDAP_TIMELIMIT_EXCEEDED },
+ { BER_BVC("sizelimitExceeded"), LDAP_SIZELIMIT_EXCEEDED },
+ { BER_BVC("compareFalse"), LDAP_COMPARE_FALSE },
+ { BER_BVC("compareTrue"), LDAP_COMPARE_TRUE },
+
+ { BER_BVC("authMethodNotSupported"), LDAP_AUTH_METHOD_NOT_SUPPORTED },
+ { BER_BVC("strongAuthNotSupported"), LDAP_STRONG_AUTH_NOT_SUPPORTED },
+ { BER_BVC("strongAuthRequired"), LDAP_STRONG_AUTH_REQUIRED },
+ { BER_BVC("strongerAuthRequired"), LDAP_STRONGER_AUTH_REQUIRED },
+#if 0 /* not LDAPv3 */
+ { BER_BVC("partialResults"), LDAP_PARTIAL_RESULTS },
+#endif
+
+ { BER_BVC("referral"), LDAP_REFERRAL },
+ { BER_BVC("adminlimitExceeded"), LDAP_ADMINLIMIT_EXCEEDED },
+ { BER_BVC("unavailableCriticalExtension"), LDAP_UNAVAILABLE_CRITICAL_EXTENSION },
+ { BER_BVC("confidentialityRequired"), LDAP_CONFIDENTIALITY_REQUIRED },
+ { BER_BVC("saslBindInProgress"), LDAP_SASL_BIND_IN_PROGRESS },
+
+ { BER_BVC("noSuchAttribute"), LDAP_NO_SUCH_ATTRIBUTE },
+ { BER_BVC("undefinedType"), LDAP_UNDEFINED_TYPE },
+ { BER_BVC("inappropriateMatching"), LDAP_INAPPROPRIATE_MATCHING },
+ { BER_BVC("constraintViolation"), LDAP_CONSTRAINT_VIOLATION },
+ { BER_BVC("typeOrValueExists"), LDAP_TYPE_OR_VALUE_EXISTS },
+ { BER_BVC("invalidSyntax"), LDAP_INVALID_SYNTAX },
+
+ { BER_BVC("noSuchObject"), LDAP_NO_SUCH_OBJECT },
+ { BER_BVC("aliasProblem"), LDAP_ALIAS_PROBLEM },
+ { BER_BVC("invalidDnSyntax"), LDAP_INVALID_DN_SYNTAX },
+#if 0 /* not LDAPv3 */
+ { BER_BVC("isLeaf"), LDAP_IS_LEAF },
+#endif
+ { BER_BVC("aliasDerefProblem"), LDAP_ALIAS_DEREF_PROBLEM },
+
+ { BER_BVC("proxyAuthzFailure"), LDAP_X_PROXY_AUTHZ_FAILURE },
+ { BER_BVC("inappropriateAuth"), LDAP_INAPPROPRIATE_AUTH },
+ { BER_BVC("invalidCredentials"), LDAP_INVALID_CREDENTIALS },
+ { BER_BVC("insufficientAccess"), LDAP_INSUFFICIENT_ACCESS },
+
+ { BER_BVC("busy"), LDAP_BUSY },
+ { BER_BVC("unavailable"), LDAP_UNAVAILABLE },
+ { BER_BVC("unwillingToPerform"), LDAP_UNWILLING_TO_PERFORM },
+ { BER_BVC("loopDetect"), LDAP_LOOP_DETECT },
+
+ { BER_BVC("namingViolation"), LDAP_NAMING_VIOLATION },
+ { BER_BVC("objectClassViolation"), LDAP_OBJECT_CLASS_VIOLATION },
+ { BER_BVC("notAllowedOnNonleaf"), LDAP_NOT_ALLOWED_ON_NONLEAF },
+ { BER_BVC("notAllowedOnRdn"), LDAP_NOT_ALLOWED_ON_RDN },
+ { BER_BVC("alreadyExists"), LDAP_ALREADY_EXISTS },
+ { BER_BVC("noObjectClassMods"), LDAP_NO_OBJECT_CLASS_MODS },
+ { BER_BVC("resultsTooLarge"), LDAP_RESULTS_TOO_LARGE },
+ { BER_BVC("affectsMultipleDsas"), LDAP_AFFECTS_MULTIPLE_DSAS },
+
+ { BER_BVC("other"), LDAP_OTHER },
+
+ /* extension-specific */
+
+ { BER_BVC("cupResourcesExhausted"), LDAP_CUP_RESOURCES_EXHAUSTED },
+ { BER_BVC("cupSecurityViolation"), LDAP_CUP_SECURITY_VIOLATION },
+ { BER_BVC("cupInvalidData"), LDAP_CUP_INVALID_DATA },
+ { BER_BVC("cupUnsupportedScheme"), LDAP_CUP_UNSUPPORTED_SCHEME },
+ { BER_BVC("cupReloadRequired"), LDAP_CUP_RELOAD_REQUIRED },
+
+ { BER_BVC("cancelled"), LDAP_CANCELLED },
+ { BER_BVC("noSuchOperation"), LDAP_NO_SUCH_OPERATION },
+ { BER_BVC("tooLate"), LDAP_TOO_LATE },
+ { BER_BVC("cannotCancel"), LDAP_CANNOT_CANCEL },
+
+ { BER_BVC("assertionFailed"), LDAP_ASSERTION_FAILED },
+
+ { BER_BVC("proxiedAuthorizationDenied"), LDAP_PROXIED_AUTHORIZATION_DENIED },
+
+ { BER_BVC("syncRefreshRequired"), LDAP_SYNC_REFRESH_REQUIRED },
+
+ { BER_BVC("noOperation"), LDAP_X_NO_OPERATION },
+
+ { BER_BVNULL, 0 }
+};
+
+slap_verbmasks *slap_ldap_response_code = slap_ldap_response_code_;
+
+int
+slap_ldap_response_code_register( struct berval *bv, int err )
+{
+ return slap_verbmask_register( slap_ldap_response_code_,
+ &slap_ldap_response_code, bv, err );
+}
+
+#ifdef HAVE_TLS
+static slap_verbmasks tlskey[] = {
+ { BER_BVC("no"), SB_TLS_OFF },
+ { BER_BVC("yes"), SB_TLS_ON },
+ { BER_BVC("critical"), SB_TLS_CRITICAL },
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks crlkeys[] = {
+ { BER_BVC("none"), LDAP_OPT_X_TLS_CRL_NONE },
+ { BER_BVC("peer"), LDAP_OPT_X_TLS_CRL_PEER },
+ { BER_BVC("all"), LDAP_OPT_X_TLS_CRL_ALL },
+ { BER_BVNULL, 0 }
+ };
+
+static slap_verbmasks vfykeys[] = {
+ { BER_BVC("never"), LDAP_OPT_X_TLS_NEVER },
+ { BER_BVC("allow"), LDAP_OPT_X_TLS_ALLOW },
+ { BER_BVC("try"), LDAP_OPT_X_TLS_TRY },
+ { BER_BVC("demand"), LDAP_OPT_X_TLS_DEMAND },
+ { BER_BVC("hard"), LDAP_OPT_X_TLS_HARD },
+ { BER_BVC("true"), LDAP_OPT_X_TLS_HARD },
+ { BER_BVNULL, 0 }
+ };
+#endif
+
+static slap_verbmasks methkey[] = {
+ { BER_BVC("none"), LDAP_AUTH_NONE },
+ { BER_BVC("simple"), LDAP_AUTH_SIMPLE },
+#ifdef HAVE_CYRUS_SASL
+ { BER_BVC("sasl"), LDAP_AUTH_SASL },
+#endif
+ { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks versionkey[] = {
+ { BER_BVC("2"), LDAP_VERSION2 },
+ { BER_BVC("3"), LDAP_VERSION3 },
+ { BER_BVNULL, 0 }
+};
+
+int
+slap_keepalive_parse(
+ struct berval *val,
+ void *bc,
+ slap_cf_aux_table *tab0,
+ const char *tabmsg,
+ int unparse )
+{
+ if ( unparse ) {
+ slap_keepalive *sk = (slap_keepalive *)bc;
+ int rc = snprintf( val->bv_val, val->bv_len, "%d:%d:%d",
+ sk->sk_idle, sk->sk_probes, sk->sk_interval );
+ if ( rc < 0 ) {
+ return -1;
+ }
+
+ if ( (unsigned)rc >= val->bv_len ) {
+ return -1;
+ }
+
+ val->bv_len = rc;
+
+ } else {
+ char *s = val->bv_val;
+ char *next;
+ slap_keepalive *sk = (slap_keepalive *)bc;
+ slap_keepalive sk2;
+
+ if ( s[0] == ':' ) {
+ sk2.sk_idle = 0;
+ s++;
+
+ } else {
+ sk2.sk_idle = strtol( s, &next, 10 );
+ if ( next == s || next[0] != ':' ) {
+ return -1;
+ }
+
+ if ( sk2.sk_idle < 0 ) {
+ return -1;
+ }
+
+ s = ++next;
+ }
+
+ if ( s[0] == ':' ) {
+ sk2.sk_probes = 0;
+ s++;
+
+ } else {
+ sk2.sk_probes = strtol( s, &next, 10 );
+ if ( next == s || next[0] != ':' ) {
+ return -1;
+ }
+
+ if ( sk2.sk_probes < 0 ) {
+ return -1;
+ }
+
+ s = ++next;
+ }
+
+ if ( *s == '\0' ) {
+ sk2.sk_interval = 0;
+
+ } else {
+ sk2.sk_interval = strtol( s, &next, 10 );
+ if ( next == s || next[0] != '\0' ) {
+ return -1;
+ }
+
+ if ( sk2.sk_interval < 0 ) {
+ return -1;
+ }
+ }
+
+ *sk = sk2;
+
+ ber_memfree( val->bv_val );
+ BER_BVZERO( val );
+ }
+
+ return 0;
+}
+
+static int
+slap_sb_uri(
+ struct berval *val,
+ void *bcp,
+ slap_cf_aux_table *tab0,
+ const char *tabmsg,
+ int unparse )
+{
+ slap_bindconf *bc = bcp;
+ if ( unparse ) {
+ if ( bc->sb_uri.bv_len >= val->bv_len )
+ return -1;
+ val->bv_len = bc->sb_uri.bv_len;
+ AC_MEMCPY( val->bv_val, bc->sb_uri.bv_val, val->bv_len );
+ } else {
+ bc->sb_uri = *val;
+#ifdef HAVE_TLS
+ if ( ldap_is_ldaps_url( val->bv_val ))
+ bc->sb_tls_do_init = 1;
+#endif
+ }
+ return 0;
+}
+
+static slap_cf_aux_table bindkey[] = {
+ { BER_BVC("uri="), 0, 'x', 1, slap_sb_uri },
+ { BER_BVC("version="), offsetof(slap_bindconf, sb_version), 'i', 0, versionkey },
+ { BER_BVC("bindmethod="), offsetof(slap_bindconf, sb_method), 'i', 0, methkey },
+ { BER_BVC("timeout="), offsetof(slap_bindconf, sb_timeout_api), 'i', 0, NULL },
+ { BER_BVC("network-timeout="), offsetof(slap_bindconf, sb_timeout_net), 'i', 0, NULL },
+ { BER_BVC("binddn="), offsetof(slap_bindconf, sb_binddn), 'b', 1, (slap_verbmasks *)dnNormalize },
+ { BER_BVC("credentials="), offsetof(slap_bindconf, sb_cred), 'b', 1, NULL },
+ { BER_BVC("saslmech="), offsetof(slap_bindconf, sb_saslmech), 'b', 0, NULL },
+ { BER_BVC("secprops="), offsetof(slap_bindconf, sb_secprops), 's', 0, NULL },
+ { BER_BVC("realm="), offsetof(slap_bindconf, sb_realm), 'b', 0, NULL },
+ { BER_BVC("authcID="), offsetof(slap_bindconf, sb_authcId), 'b', 1, NULL },
+ { BER_BVC("authzID="), offsetof(slap_bindconf, sb_authzId), 'b', 1, (slap_verbmasks *)authzNormalize },
+ { BER_BVC("keepalive="), offsetof(slap_bindconf, sb_keepalive), 'x', 0, (slap_verbmasks *)slap_keepalive_parse },
+ { BER_BVC("tcp-user-timeout="), offsetof(slap_bindconf, sb_tcp_user_timeout), 'u', 0, NULL },
+#ifdef HAVE_TLS
+ /* NOTE: replace "14" with the actual index
+ * of the first TLS-related line */
+#define aux_TLS (bindkey+14) /* beginning of TLS keywords */
+
+ { BER_BVC("starttls="), offsetof(slap_bindconf, sb_tls), 'i', 0, tlskey },
+ { BER_BVC("tls_cert="), offsetof(slap_bindconf, sb_tls_cert), 's', 1, NULL },
+ { BER_BVC("tls_key="), offsetof(slap_bindconf, sb_tls_key), 's', 1, NULL },
+ { BER_BVC("tls_cacert="), offsetof(slap_bindconf, sb_tls_cacert), 's', 1, NULL },
+ { BER_BVC("tls_cacertdir="), offsetof(slap_bindconf, sb_tls_cacertdir), 's', 1, NULL },
+ { BER_BVC("tls_reqcert="), offsetof(slap_bindconf, sb_tls_reqcert), 's', 0, NULL },
+ { BER_BVC("tls_reqsan="), offsetof(slap_bindconf, sb_tls_reqsan), 's', 0, NULL },
+ { BER_BVC("tls_cipher_suite="), offsetof(slap_bindconf, sb_tls_cipher_suite), 's', 0, NULL },
+ { BER_BVC("tls_protocol_min="), offsetof(slap_bindconf, sb_tls_protocol_min), 's', 0, NULL },
+ { BER_BVC("tls_ecname="), offsetof(slap_bindconf, sb_tls_ecname), 's', 0, NULL },
+#ifdef HAVE_OPENSSL
+ { BER_BVC("tls_crlcheck="), offsetof(slap_bindconf, sb_tls_crlcheck), 's', 0, NULL },
+#endif
+#endif
+ { BER_BVNULL, 0, 0, 0, NULL }
+};
+
+/*
+ * 's': char *
+ * 'b': struct berval; if !NULL, normalize using ((slap_mr_normalize_func *)aux)
+ * 'i': int; if !NULL, compute using ((slap_verbmasks *)aux)
+ * 'u': unsigned
+ * 'I': long
+ * 'U': unsigned long
+ */
+
+int
+slap_cf_aux_table_parse( const char *word, void *dst, slap_cf_aux_table *tab0, LDAP_CONST char *tabmsg )
+{
+ int rc = SLAP_CONF_UNKNOWN;
+ slap_cf_aux_table *tab;
+
+ for ( tab = tab0; !BER_BVISNULL( &tab->key ); tab++ ) {
+ if ( !strncasecmp( word, tab->key.bv_val, tab->key.bv_len ) ) {
+ char **cptr;
+ int *iptr, j;
+ unsigned *uptr;
+ long *lptr;
+ unsigned long *ulptr;
+ struct berval *bptr;
+ const char *val = word + tab->key.bv_len;
+
+ switch ( tab->type ) {
+ case 's':
+ cptr = (char **)((char *)dst + tab->off);
+ *cptr = ch_strdup( val );
+ rc = 0;
+ break;
+
+ case 'b':
+ bptr = (struct berval *)((char *)dst + tab->off);
+ if ( tab->aux != NULL ) {
+ struct berval dn;
+ slap_mr_normalize_func *normalize = (slap_mr_normalize_func *)tab->aux;
+
+ ber_str2bv( val, 0, 0, &dn );
+ rc = normalize( 0, NULL, NULL, &dn, bptr, NULL );
+
+ } else {
+ ber_str2bv( val, 0, 1, bptr );
+ rc = 0;
+ }
+ break;
+
+ case 'i':
+ iptr = (int *)((char *)dst + tab->off);
+
+ if ( tab->aux != NULL ) {
+ slap_verbmasks *aux = (slap_verbmasks *)tab->aux;
+
+ assert( aux != NULL );
+
+ rc = 1;
+ for ( j = 0; !BER_BVISNULL( &aux[j].word ); j++ ) {
+ if ( !strcasecmp( val, aux[j].word.bv_val ) ) {
+ *iptr = aux[j].mask;
+ rc = 0;
+ break;
+ }
+ }
+
+ } else {
+ rc = lutil_atoix( iptr, val, 0 );
+ }
+ break;
+
+ case 'u':
+ uptr = (unsigned *)((char *)dst + tab->off);
+
+ rc = lutil_atoux( uptr, val, 0 );
+ break;
+
+ case 'I':
+ lptr = (long *)((char *)dst + tab->off);
+
+ rc = lutil_atolx( lptr, val, 0 );
+ break;
+
+ case 'U':
+ ulptr = (unsigned long *)((char *)dst + tab->off);
+
+ rc = lutil_atoulx( ulptr, val, 0 );
+ break;
+
+ case 'x':
+ if ( tab->aux != NULL ) {
+ struct berval value;
+ slap_cf_aux_table_parse_x *func = (slap_cf_aux_table_parse_x *)tab->aux;
+
+ ber_str2bv( val, 0, 1, &value );
+
+ rc = func( &value, (void *)((char *)dst + tab->off), tab, tabmsg, 0 );
+
+ } else {
+ rc = 1;
+ }
+ break;
+ }
+
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY, "invalid %s value %s\n",
+ tabmsg, word );
+ }
+
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+int
+slap_cf_aux_table_unparse( void *src, struct berval *bv, slap_cf_aux_table *tab0 )
+{
+ char buf[AC_LINE_MAX], *ptr;
+ slap_cf_aux_table *tab;
+ struct berval tmp;
+
+ ptr = buf;
+ for (tab = tab0; !BER_BVISNULL(&tab->key); tab++ ) {
+ char **cptr;
+ int *iptr, i;
+ unsigned *uptr;
+ long *lptr;
+ unsigned long *ulptr;
+ struct berval *bptr;
+
+ cptr = (char **)((char *)src + tab->off);
+
+ switch ( tab->type ) {
+ case 'b':
+ bptr = (struct berval *)((char *)src + tab->off);
+ cptr = &bptr->bv_val;
+
+ case 's':
+ if ( *cptr ) {
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, tab->key.bv_val );
+ if ( tab->quote ) *ptr++ = '"';
+ ptr = lutil_strcopy( ptr, *cptr );
+ if ( tab->quote ) *ptr++ = '"';
+ }
+ break;
+
+ case 'i':
+ iptr = (int *)((char *)src + tab->off);
+
+ if ( tab->aux != NULL ) {
+ slap_verbmasks *aux = (slap_verbmasks *)tab->aux;
+
+ for ( i = 0; !BER_BVISNULL( &aux[i].word ); i++ ) {
+ if ( *iptr == aux[i].mask ) {
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, tab->key.bv_val );
+ ptr = lutil_strcopy( ptr, aux[i].word.bv_val );
+ break;
+ }
+ }
+
+ } else {
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, tab->key.bv_val );
+ ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ), "%d", *iptr );
+ }
+ break;
+
+ case 'u':
+ uptr = (unsigned *)((char *)src + tab->off);
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, tab->key.bv_val );
+ ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ), "%u", *uptr );
+ break;
+
+ case 'I':
+ lptr = (long *)((char *)src + tab->off);
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, tab->key.bv_val );
+ ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ), "%ld", *lptr );
+ break;
+
+ case 'U':
+ ulptr = (unsigned long *)((char *)src + tab->off);
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, tab->key.bv_val );
+ ptr += snprintf( ptr, sizeof( buf ) - ( ptr - buf ), "%lu", *ulptr );
+ break;
+
+ case 'x':
+ {
+ char *saveptr=ptr;
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, tab->key.bv_val );
+ if ( tab->quote ) *ptr++ = '"';
+ if ( tab->aux != NULL ) {
+ struct berval value;
+ slap_cf_aux_table_parse_x *func = (slap_cf_aux_table_parse_x *)tab->aux;
+ int rc;
+
+ value.bv_val = ptr;
+ value.bv_len = buf + sizeof( buf ) - ptr;
+
+ rc = func( &value, (void *)((char *)src + tab->off), tab, "(unparse)", 1 );
+ if ( rc == 0 ) {
+ if (value.bv_len) {
+ ptr += value.bv_len;
+ } else {
+ ptr = saveptr;
+ break;
+ }
+ }
+ }
+ if ( tab->quote ) *ptr++ = '"';
+ }
+ break;
+
+ default:
+ assert( 0 );
+ }
+ }
+ tmp.bv_val = buf;
+ tmp.bv_len = ptr - buf;
+ ber_dupbv( bv, &tmp );
+ return 0;
+}
+
+int
+slap_tls_get_config( LDAP *ld, int opt, char **val )
+{
+#ifdef HAVE_TLS
+ slap_verbmasks *keys;
+ int i, ival;
+
+ *val = NULL;
+ switch( opt ) {
+ case LDAP_OPT_X_TLS_CRLCHECK:
+ keys = crlkeys;
+ break;
+ case LDAP_OPT_X_TLS_REQUIRE_CERT:
+ keys = vfykeys;
+ break;
+ case LDAP_OPT_X_TLS_PROTOCOL_MIN: {
+ char buf[8];
+ ldap_pvt_tls_get_option( ld, opt, &ival );
+ snprintf( buf, sizeof( buf ), "%d.%d",
+ ( ival >> 8 ) & 0xff, ival & 0xff );
+ *val = ch_strdup( buf );
+ return 0;
+ }
+ default:
+ return -1;
+ }
+ ldap_pvt_tls_get_option( ld, opt, &ival );
+ for (i=0; !BER_BVISNULL(&keys[i].word); i++) {
+ if (keys[i].mask == ival) {
+ *val = ch_strdup( keys[i].word.bv_val );
+ return 0;
+ }
+ }
+#endif
+ return -1;
+}
+
+int
+bindconf_tls_parse( const char *word, slap_bindconf *bc )
+{
+#ifdef HAVE_TLS
+ if ( slap_cf_aux_table_parse( word, bc, aux_TLS, "tls config" ) == 0 ) {
+ bc->sb_tls_do_init = 1;
+ return 0;
+ }
+#endif
+ return -1;
+}
+
+int
+bindconf_tls_unparse( slap_bindconf *bc, struct berval *bv )
+{
+#ifdef HAVE_TLS
+ return slap_cf_aux_table_unparse( bc, bv, aux_TLS );
+#endif
+ return -1;
+}
+
+int
+bindconf_parse( const char *word, slap_bindconf *bc )
+{
+#ifdef HAVE_TLS
+ /* Detect TLS config changes explicitly */
+ if ( bindconf_tls_parse( word, bc ) == 0 ) {
+ return 0;
+ }
+#endif
+ return slap_cf_aux_table_parse( word, bc, bindkey, "bind config" );
+}
+
+int
+bindconf_unparse( slap_bindconf *bc, struct berval *bv )
+{
+ return slap_cf_aux_table_unparse( bc, bv, bindkey );
+}
+
+void bindconf_free( slap_bindconf *bc ) {
+ if ( !BER_BVISNULL( &bc->sb_uri ) ) {
+ ch_free( bc->sb_uri.bv_val );
+ BER_BVZERO( &bc->sb_uri );
+ }
+ if ( !BER_BVISNULL( &bc->sb_binddn ) ) {
+ ch_free( bc->sb_binddn.bv_val );
+ BER_BVZERO( &bc->sb_binddn );
+ }
+ if ( !BER_BVISNULL( &bc->sb_cred ) ) {
+ ch_free( bc->sb_cred.bv_val );
+ BER_BVZERO( &bc->sb_cred );
+ }
+ if ( !BER_BVISNULL( &bc->sb_saslmech ) ) {
+ ch_free( bc->sb_saslmech.bv_val );
+ BER_BVZERO( &bc->sb_saslmech );
+ }
+ if ( bc->sb_secprops ) {
+ ch_free( bc->sb_secprops );
+ bc->sb_secprops = NULL;
+ }
+ if ( !BER_BVISNULL( &bc->sb_realm ) ) {
+ ch_free( bc->sb_realm.bv_val );
+ BER_BVZERO( &bc->sb_realm );
+ }
+ if ( !BER_BVISNULL( &bc->sb_authcId ) ) {
+ ch_free( bc->sb_authcId.bv_val );
+ BER_BVZERO( &bc->sb_authcId );
+ }
+ if ( !BER_BVISNULL( &bc->sb_authzId ) ) {
+ ch_free( bc->sb_authzId.bv_val );
+ BER_BVZERO( &bc->sb_authzId );
+ }
+#ifdef HAVE_TLS
+ if ( bc->sb_tls_cert ) {
+ ch_free( bc->sb_tls_cert );
+ bc->sb_tls_cert = NULL;
+ }
+ if ( bc->sb_tls_key ) {
+ ch_free( bc->sb_tls_key );
+ bc->sb_tls_key = NULL;
+ }
+ if ( bc->sb_tls_cacert ) {
+ ch_free( bc->sb_tls_cacert );
+ bc->sb_tls_cacert = NULL;
+ }
+ if ( bc->sb_tls_cacertdir ) {
+ ch_free( bc->sb_tls_cacertdir );
+ bc->sb_tls_cacertdir = NULL;
+ }
+ if ( bc->sb_tls_reqcert ) {
+ ch_free( bc->sb_tls_reqcert );
+ bc->sb_tls_reqcert = NULL;
+ }
+ if ( bc->sb_tls_reqsan ) {
+ ch_free( bc->sb_tls_reqsan );
+ bc->sb_tls_reqsan = NULL;
+ }
+ if ( bc->sb_tls_cipher_suite ) {
+ ch_free( bc->sb_tls_cipher_suite );
+ bc->sb_tls_cipher_suite = NULL;
+ }
+ if ( bc->sb_tls_protocol_min ) {
+ ch_free( bc->sb_tls_protocol_min );
+ bc->sb_tls_protocol_min = NULL;
+ }
+ if ( bc->sb_tls_ecname ) {
+ ch_free( bc->sb_tls_ecname );
+ bc->sb_tls_ecname = NULL;
+ }
+#ifdef HAVE_OPENSSL
+ if ( bc->sb_tls_crlcheck ) {
+ ch_free( bc->sb_tls_crlcheck );
+ bc->sb_tls_crlcheck = NULL;
+ }
+#endif
+ if ( bc->sb_tls_ctx ) {
+ ldap_pvt_tls_ctx_free( bc->sb_tls_ctx );
+ bc->sb_tls_ctx = NULL;
+ }
+#endif
+}
+
+void
+bindconf_tls_defaults( slap_bindconf *bc )
+{
+#ifdef HAVE_TLS
+ if ( bc->sb_tls_do_init ) {
+ if ( !bc->sb_tls_cacert )
+ ldap_pvt_tls_get_option( slap_tls_ld, LDAP_OPT_X_TLS_CACERTFILE,
+ &bc->sb_tls_cacert );
+ if ( !bc->sb_tls_cacertdir )
+ ldap_pvt_tls_get_option( slap_tls_ld, LDAP_OPT_X_TLS_CACERTDIR,
+ &bc->sb_tls_cacertdir );
+ if ( !bc->sb_tls_cert )
+ ldap_pvt_tls_get_option( slap_tls_ld, LDAP_OPT_X_TLS_CERTFILE,
+ &bc->sb_tls_cert );
+ if ( !bc->sb_tls_key )
+ ldap_pvt_tls_get_option( slap_tls_ld, LDAP_OPT_X_TLS_KEYFILE,
+ &bc->sb_tls_key );
+ if ( !bc->sb_tls_cipher_suite )
+ ldap_pvt_tls_get_option( slap_tls_ld, LDAP_OPT_X_TLS_CIPHER_SUITE,
+ &bc->sb_tls_cipher_suite );
+ if ( !bc->sb_tls_reqcert )
+ bc->sb_tls_reqcert = ch_strdup("demand");
+ if ( !bc->sb_tls_reqsan )
+ bc->sb_tls_reqsan = ch_strdup("allow");
+ if ( !bc->sb_tls_ecname )
+ slap_tls_get_config( slap_tls_ld, LDAP_OPT_X_TLS_ECNAME,
+ &bc->sb_tls_ecname );
+#ifdef HAVE_OPENSSL
+ if ( !bc->sb_tls_crlcheck )
+ slap_tls_get_config( slap_tls_ld, LDAP_OPT_X_TLS_CRLCHECK,
+ &bc->sb_tls_crlcheck );
+#endif
+ }
+#endif
+}
+
+#ifdef HAVE_TLS
+static struct {
+ const char *key;
+ size_t offset;
+ int opt;
+} bindtlsopts[] = {
+ { "tls_cert", offsetof(slap_bindconf, sb_tls_cert), LDAP_OPT_X_TLS_CERTFILE },
+ { "tls_key", offsetof(slap_bindconf, sb_tls_key), LDAP_OPT_X_TLS_KEYFILE },
+ { "tls_cacert", offsetof(slap_bindconf, sb_tls_cacert), LDAP_OPT_X_TLS_CACERTFILE },
+ { "tls_cacertdir", offsetof(slap_bindconf, sb_tls_cacertdir), LDAP_OPT_X_TLS_CACERTDIR },
+ { "tls_cipher_suite", offsetof(slap_bindconf, sb_tls_cipher_suite), LDAP_OPT_X_TLS_CIPHER_SUITE },
+ { "tls_ecname", offsetof(slap_bindconf, sb_tls_ecname), LDAP_OPT_X_TLS_ECNAME },
+ {0, 0}
+};
+
+int bindconf_tls_set( slap_bindconf *bc, LDAP *ld )
+{
+ int i, rc, newctx = 0, res = 0;
+ char *ptr = (char *)bc, **word;
+
+ if ( bc->sb_tls_do_init ) {
+ for (i=0; bindtlsopts[i].opt; i++) {
+ word = (char **)(ptr + bindtlsopts[i].offset);
+ if ( *word ) {
+ rc = ldap_set_option( ld, bindtlsopts[i].opt, *word );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "bindconf_tls_set: failed to set %s to %s\n",
+ bindtlsopts[i].key, *word );
+ res = -1;
+ } else
+ newctx = 1;
+ }
+ }
+ if ( bc->sb_tls_reqcert ) {
+ rc = ldap_pvt_tls_config( ld, LDAP_OPT_X_TLS_REQUIRE_CERT,
+ bc->sb_tls_reqcert );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "bindconf_tls_set: failed to set tls_reqcert to %s\n",
+ bc->sb_tls_reqcert );
+ res = -1;
+ } else {
+ newctx = 1;
+ /* retrieve the parsed setting for later use */
+ ldap_get_option( ld, LDAP_OPT_X_TLS_REQUIRE_CERT, &bc->sb_tls_int_reqcert );
+ }
+ }
+ if ( bc->sb_tls_reqsan ) {
+ rc = ldap_pvt_tls_config( ld, LDAP_OPT_X_TLS_REQUIRE_SAN,
+ bc->sb_tls_reqsan );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "bindconf_tls_set: failed to set tls_reqsan to %s\n",
+ bc->sb_tls_reqsan );
+ res = -1;
+ } else {
+ newctx = 1;
+ /* retrieve the parsed setting for later use */
+ ldap_get_option( ld, LDAP_OPT_X_TLS_REQUIRE_SAN, &bc->sb_tls_int_reqsan );
+ }
+ }
+ if ( bc->sb_tls_protocol_min ) {
+ rc = ldap_pvt_tls_config( ld, LDAP_OPT_X_TLS_PROTOCOL_MIN,
+ bc->sb_tls_protocol_min );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "bindconf_tls_set: failed to set tls_protocol_min to %s\n",
+ bc->sb_tls_protocol_min );
+ res = -1;
+ } else
+ newctx = 1;
+ }
+#ifdef HAVE_OPENSSL
+ if ( bc->sb_tls_crlcheck ) {
+ rc = ldap_pvt_tls_config( ld, LDAP_OPT_X_TLS_CRLCHECK,
+ bc->sb_tls_crlcheck );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "bindconf_tls_set: failed to set tls_crlcheck to %s\n",
+ bc->sb_tls_crlcheck );
+ res = -1;
+ } else
+ newctx = 1;
+ }
+#endif
+ if ( !res )
+ bc->sb_tls_do_init = 0;
+ }
+
+ if ( newctx ) {
+ int opt = 0;
+
+ if ( bc->sb_tls_ctx ) {
+ ldap_pvt_tls_ctx_free( bc->sb_tls_ctx );
+ bc->sb_tls_ctx = NULL;
+ }
+ rc = ldap_set_option( ld, LDAP_OPT_X_TLS_NEWCTX, &opt );
+ if ( rc )
+ res = rc;
+ else
+ ldap_get_option( ld, LDAP_OPT_X_TLS_CTX, &bc->sb_tls_ctx );
+ } else if ( bc->sb_tls_ctx ) {
+ rc = ldap_set_option( ld, LDAP_OPT_X_TLS_CTX, bc->sb_tls_ctx );
+ if ( rc == LDAP_SUCCESS ) {
+ /* these options aren't actually inside the ctx, so have to be set again */
+ ldap_set_option( ld, LDAP_OPT_X_TLS_REQUIRE_CERT, &bc->sb_tls_int_reqcert );
+ ldap_set_option( ld, LDAP_OPT_X_TLS_REQUIRE_SAN, &bc->sb_tls_int_reqsan );
+ } else
+ res = rc;
+ }
+
+ return res;
+}
+#endif
+
+/*
+ * set connection keepalive options
+ */
+void
+slap_client_keepalive(LDAP *ld, slap_keepalive *sk)
+{
+ if (!sk) return;
+
+ if ( sk->sk_idle ) {
+ ldap_set_option( ld, LDAP_OPT_X_KEEPALIVE_IDLE, &sk->sk_idle );
+ }
+
+ if ( sk->sk_probes ) {
+ ldap_set_option( ld, LDAP_OPT_X_KEEPALIVE_PROBES, &sk->sk_probes );
+ }
+
+ if ( sk->sk_interval ) {
+ ldap_set_option( ld, LDAP_OPT_X_KEEPALIVE_INTERVAL, &sk->sk_interval );
+ }
+
+ return;
+}
+
+/*
+ * connect to a client using the bindconf data
+ * note: should move "version" into bindconf...
+ */
+int
+slap_client_connect( LDAP **ldp, slap_bindconf *sb )
+{
+ LDAP *ld = NULL;
+ int rc;
+ struct timeval tv;
+
+ /* Init connection to provider */
+ rc = ldap_initialize( &ld, sb->sb_uri.bv_val );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "slap_client_connect: "
+ "ldap_initialize(%s) failed (%d)\n",
+ sb->sb_uri.bv_val, rc );
+ return rc;
+ }
+
+ if ( sb->sb_version != 0 ) {
+ ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION,
+ (const void *)&sb->sb_version );
+ }
+
+ if ( sb->sb_timeout_api ) {
+ tv.tv_sec = sb->sb_timeout_api;
+ tv.tv_usec = 0;
+ ldap_set_option( ld, LDAP_OPT_TIMEOUT, &tv );
+ }
+
+ if ( sb->sb_timeout_net ) {
+ tv.tv_sec = sb->sb_timeout_net;
+ tv.tv_usec = 0;
+ ldap_set_option( ld, LDAP_OPT_NETWORK_TIMEOUT, &tv );
+ }
+
+ /* setting network keepalive options */
+ slap_client_keepalive(ld, &sb->sb_keepalive);
+
+#ifdef HAVE_TLS
+ rc = bindconf_tls_set( sb, ld );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "slap_client_connect: "
+ "URI=%s TLS context initialization failed (%d)\n",
+ sb->sb_uri.bv_val, rc );
+ goto done;
+ }
+#endif
+
+ /* Bind */
+ if ( sb->sb_tls ) {
+ rc = ldap_start_tls_s( ld, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "slap_client_connect: URI=%s "
+ "%s, ldap_start_tls failed (%d)\n",
+ sb->sb_uri.bv_val,
+ sb->sb_tls == SB_TLS_CRITICAL ?
+ "Error" : "Warning",
+ rc );
+ if ( sb->sb_tls == SB_TLS_CRITICAL ) {
+ goto done;
+ }
+ }
+ }
+
+ if ( sb->sb_method == LDAP_AUTH_SASL ) {
+#ifdef HAVE_CYRUS_SASL
+ void *defaults;
+
+ if ( sb->sb_secprops != NULL ) {
+ rc = ldap_set_option( ld,
+ LDAP_OPT_X_SASL_SECPROPS, sb->sb_secprops);
+
+ if( rc != LDAP_OPT_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "slap_client_connect: "
+ "error, ldap_set_option "
+ "(%s,SECPROPS,\"%s\") failed!\n",
+ sb->sb_uri.bv_val, sb->sb_secprops );
+ goto done;
+ }
+ }
+
+ defaults = lutil_sasl_defaults( ld,
+ sb->sb_saslmech.bv_val,
+ sb->sb_realm.bv_val,
+ sb->sb_authcId.bv_val,
+ sb->sb_cred.bv_val,
+ sb->sb_authzId.bv_val );
+ if ( defaults == NULL ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ rc = ldap_sasl_interactive_bind_s( ld,
+ sb->sb_binddn.bv_val,
+ sb->sb_saslmech.bv_val,
+ NULL, NULL,
+ LDAP_SASL_QUIET,
+ lutil_sasl_interact,
+ defaults );
+
+ lutil_sasl_freedefs( defaults );
+
+ /* FIXME: different error behaviors according to
+ * 1) return code
+ * 2) on err policy : exit, retry, backoff ...
+ */
+ if ( rc != LDAP_SUCCESS ) {
+ static struct berval bv_GSSAPI = BER_BVC( "GSSAPI" );
+
+ Debug( LDAP_DEBUG_ANY, "slap_client_connect: URI=%s "
+ "ldap_sasl_interactive_bind_s failed (%d)\n",
+ sb->sb_uri.bv_val, rc );
+
+ /* FIXME (see above comment) */
+ /* if Kerberos credentials cache is not active, retry */
+ if ( ber_bvcmp( &sb->sb_saslmech, &bv_GSSAPI ) == 0 &&
+ rc == LDAP_LOCAL_ERROR )
+ {
+ rc = LDAP_SERVER_DOWN;
+ }
+
+ goto done;
+ }
+#else /* HAVE_CYRUS_SASL */
+ /* Should never get here, we trapped this at config time */
+ assert(0);
+ Debug( LDAP_DEBUG_SYNC, "not compiled with SASL support\n" );
+ rc = LDAP_OTHER;
+ goto done;
+#endif
+
+ } else if ( sb->sb_method == LDAP_AUTH_SIMPLE ) {
+ rc = ldap_sasl_bind_s( ld,
+ sb->sb_binddn.bv_val, LDAP_SASL_SIMPLE,
+ &sb->sb_cred, NULL, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "slap_client_connect: "
+ "URI=%s DN=\"%s\" "
+ "ldap_sasl_bind_s failed (%d)\n",
+ sb->sb_uri.bv_val, sb->sb_binddn.bv_val, rc );
+ goto done;
+ }
+ }
+
+done:;
+ if ( rc ) {
+ if ( ld ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ *ldp = NULL;
+ }
+
+ } else {
+ *ldp = ld;
+ }
+
+ return rc;
+}
+
+/* -------------------------------------- */
+
+
+static char *
+strtok_quote( char *line, char *sep, char **quote_ptr, int *iqp )
+{
+ int inquote;
+ char *tmp;
+ static char *next;
+
+ *quote_ptr = NULL;
+ if ( line != NULL ) {
+ next = line;
+ }
+ while ( *next && strchr( sep, *next ) ) {
+ next++;
+ }
+
+ if ( *next == '\0' ) {
+ next = NULL;
+ return( NULL );
+ }
+ tmp = next;
+
+ for ( inquote = 0; *next; ) {
+ switch ( *next ) {
+ case '"':
+ if ( inquote ) {
+ inquote = 0;
+ } else {
+ inquote = 1;
+ }
+ AC_MEMCPY( next, next + 1, strlen( next + 1 ) + 1 );
+ break;
+
+ case '\\':
+ if ( next[1] )
+ AC_MEMCPY( next,
+ next + 1, strlen( next + 1 ) + 1 );
+ next++; /* dont parse the escaped character */
+ break;
+
+ default:
+ if ( ! inquote ) {
+ if ( strchr( sep, *next ) != NULL ) {
+ *quote_ptr = next;
+ *next++ = '\0';
+ return( tmp );
+ }
+ }
+ next++;
+ break;
+ }
+ }
+ *iqp = inquote;
+
+ return( tmp );
+}
+
+static char buf[AC_LINE_MAX];
+static char *line;
+static size_t lmax, lcur;
+
+#define CATLINE( buf ) \
+ do { \
+ size_t len = strlen( buf ); \
+ while ( lcur + len + 1 > lmax ) { \
+ lmax += AC_LINE_MAX; \
+ line = (char *) ch_realloc( line, lmax ); \
+ } \
+ strcpy( line + lcur, buf ); \
+ lcur += len; \
+ } while( 0 )
+
+static void
+fp_getline_init(ConfigArgs *c) {
+ c->lineno = -1;
+ buf[0] = '\0';
+}
+
+static int
+fp_getline( FILE *fp, ConfigArgs *c )
+{
+ char *p;
+
+ lcur = 0;
+ CATLINE(buf);
+ c->lineno++;
+
+ /* avoid stack of bufs */
+ if ( strncasecmp( line, "include", STRLENOF( "include" ) ) == 0 ) {
+ buf[0] = '\0';
+ c->line = line;
+ return(1);
+ }
+
+ while ( fgets( buf, sizeof( buf ), fp ) ) {
+ p = strchr( buf, '\n' );
+ if ( p ) {
+ if ( p > buf && p[-1] == '\r' ) {
+ --p;
+ }
+ *p = '\0';
+ }
+ /* XXX ugly */
+ c->line = line;
+ if ( line[0]
+ && ( p = line + strlen( line ) - 1 )[0] == '\\'
+ && p[-1] != '\\' )
+ {
+ p[0] = '\0';
+ lcur--;
+
+ } else {
+ if ( !isspace( (unsigned char)buf[0] ) ) {
+ return(1);
+ }
+ buf[0] = ' ';
+ }
+ CATLINE(buf);
+ c->lineno++;
+ }
+
+ buf[0] = '\0';
+ c->line = line;
+ return(line[0] ? 1 : 0);
+}
+
+int
+config_fp_parse_line(ConfigArgs *c)
+{
+ char *token;
+ static char *const hide[] = {
+ "rootpw", "replica", "syncrepl", /* in slapd */
+ "acl-bind", "acl-method", "idassert-bind", /* in back-ldap */
+ "acl-passwd", "bindpw", /* in back-<ldap/meta> */
+ "pseudorootpw", /* in back-meta */
+ "dbpasswd", /* in back-sql */
+ NULL
+ };
+ static char *const raw[] = {
+ "attributetype", "objectclass", "ditcontentrule", "ldapsyntax", NULL };
+ char *quote_ptr;
+ int i = (int)(sizeof(hide)/sizeof(hide[0])) - 1;
+ int inquote = 0;
+
+ c->tline = ch_strdup(c->line);
+ c->linelen = strlen(c->line);
+ token = strtok_quote(c->tline, " \t", &quote_ptr, &inquote);
+
+ if(token) for(i = 0; hide[i]; i++) if(!strcasecmp(token, hide[i])) break;
+ if(quote_ptr) *quote_ptr = ' ';
+ Debug(LDAP_DEBUG_CONFIG, "%s (%s%s)\n", c->log,
+ hide[i] ? hide[i] : c->line, hide[i] ? " ***" : "");
+ if(quote_ptr) *quote_ptr = '\0';
+
+ for(;; token = strtok_quote(NULL, " \t", &quote_ptr, &inquote)) {
+ if(c->argc >= c->argv_size) {
+ char **tmp;
+ tmp = ch_realloc(c->argv, (c->argv_size + ARGS_STEP) * sizeof(*c->argv));
+ if(!tmp) {
+ Debug(LDAP_DEBUG_ANY, "%s: out of memory\n", c->log );
+ return -1;
+ }
+ c->argv = tmp;
+ c->argv_size += ARGS_STEP;
+ }
+ if(token == NULL)
+ break;
+ c->argv[c->argc++] = token;
+ }
+ c->argv[c->argc] = NULL;
+ if (inquote) {
+ /* these directives parse c->line independently of argv tokenizing */
+ for(i = 0; raw[i]; i++) if (!strcasecmp(c->argv[0], raw[i])) return 0;
+
+ Debug(LDAP_DEBUG_ANY, "%s: unterminated quoted string \"%s\"\n", c->log, c->argv[c->argc-1] );
+ return -1;
+ }
+ return(0);
+}
+
+void
+config_destroy( )
+{
+ ucdata_unload( UCDATA_ALL );
+ if ( frontendDB ) {
+ /* NOTE: in case of early exit, frontendDB can be NULL */
+ if ( frontendDB->be_schemandn.bv_val )
+ free( frontendDB->be_schemandn.bv_val );
+ if ( frontendDB->be_schemadn.bv_val )
+ free( frontendDB->be_schemadn.bv_val );
+ if ( frontendDB->be_acl )
+ acl_destroy( frontendDB->be_acl );
+ }
+ free( line );
+ if ( slapd_args_file )
+ free ( slapd_args_file );
+ if ( slapd_pid_file )
+ free ( slapd_pid_file );
+ if ( default_passwd_hash )
+ ldap_charray_free( default_passwd_hash );
+}
+
+char **
+slap_str2clist( char ***out, char *in, const char *brkstr )
+{
+ char *str;
+ char *s;
+ char *lasts;
+ int i, j;
+ char **new;
+
+ /* find last element in list */
+ for (i = 0; *out && (*out)[i]; i++);
+
+ /* protect the input string from strtok */
+ str = ch_strdup( in );
+
+ if ( *str == '\0' ) {
+ free( str );
+ return( *out );
+ }
+
+ /* Count words in string */
+ j=1;
+ for ( s = str; *s; s++ ) {
+ if ( strchr( brkstr, *s ) != NULL ) {
+ j++;
+ }
+ }
+
+ *out = ch_realloc( *out, ( i + j + 1 ) * sizeof( char * ) );
+ new = *out + i;
+ for ( s = ldap_pvt_strtok( str, brkstr, &lasts );
+ s != NULL;
+ s = ldap_pvt_strtok( NULL, brkstr, &lasts ) )
+ {
+ *new = ch_strdup( s );
+ new++;
+ }
+
+ *new = NULL;
+ free( str );
+ return( *out );
+}
+
+int config_generic_wrapper( Backend *be, const char *fname, int lineno,
+ int argc, char **argv )
+{
+ ConfigArgs c = { 0 };
+ ConfigTable *ct;
+ int rc;
+
+ c.be = be;
+ c.fname = fname;
+ c.lineno = lineno;
+ c.argc = argc;
+ c.argv = argv;
+ c.valx = -1;
+ c.line = line;
+ c.op = SLAP_CONFIG_ADD;
+ snprintf( c.log, sizeof( c.log ), "%s: line %d", fname, lineno );
+
+ rc = SLAP_CONF_UNKNOWN;
+ ct = config_find_keyword( be->be_cf_ocs->co_table, &c );
+ if ( ct ) {
+ c.table = be->be_cf_ocs->co_type;
+ rc = config_add_vals( ct, &c );
+ }
+ return rc;
+}
+
+/* See if the given URL (in plain and parsed form) matches
+ * any of the server's listener addresses. Return matching
+ * Listener or NULL for no match.
+ */
+Listener *config_check_my_url( const char *url, LDAPURLDesc *lud )
+{
+ Listener **l = slapd_get_listeners();
+ int i, isMe;
+
+ /* Try a straight compare with Listener strings */
+ for ( i=0; l && l[i]; i++ ) {
+ if ( !strcasecmp( url, l[i]->sl_url.bv_val )) {
+ return l[i];
+ }
+ }
+
+ isMe = 0;
+ /* If hostname is empty, or is localhost, or matches
+ * our hostname, this url refers to this host.
+ * Compare it against listeners and ports.
+ */
+ if ( !lud->lud_host || !lud->lud_host[0] ||
+ !strncasecmp("localhost", lud->lud_host,
+ STRLENOF("localhost")) ||
+ !strcasecmp( global_host, lud->lud_host )) {
+
+ for ( i=0; l && l[i]; i++ ) {
+ LDAPURLDesc *lu2;
+ ldap_url_parse( l[i]->sl_url.bv_val, &lu2 );
+ do {
+ if ( strcasecmp( lud->lud_scheme,
+ lu2->lud_scheme ))
+ break;
+ if ( lud->lud_port != lu2->lud_port )
+ break;
+ /* Listener on ANY address */
+ if ( !lu2->lud_host || !lu2->lud_host[0] ) {
+ isMe = 1;
+ break;
+ }
+ /* URL on ANY address */
+ if ( !lud->lud_host || !lud->lud_host[0] ) {
+ isMe = 1;
+ break;
+ }
+ /* Listener has specific host, must
+ * match it
+ */
+ if ( !strcasecmp( lud->lud_host,
+ lu2->lud_host )) {
+ isMe = 1;
+ break;
+ }
+ } while(0);
+ ldap_free_urldesc( lu2 );
+ if ( isMe ) {
+ return l[i];
+ }
+ }
+ }
+ return NULL;
+}
diff --git a/servers/slapd/connection.c b/servers/slapd/connection.c
new file mode 100644
index 0000000..b8ea92a
--- /dev/null
+++ b/servers/slapd/connection.c
@@ -0,0 +1,2117 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include <ac/socket.h>
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include "lutil.h"
+#include "slap.h"
+
+#ifdef LDAP_CONNECTIONLESS
+#include "../../libraries/liblber/lber-int.h" /* ber_int_sb_read() */
+#endif
+
+#ifdef LDAP_SLAPI
+#include "slapi/slapi.h"
+#endif
+
+static Connection *connections = NULL;
+
+static ldap_pvt_thread_mutex_t conn_nextid_mutex;
+static unsigned long conn_nextid = SLAPD_SYNC_SYNCCONN_OFFSET;
+
+static const char conn_lost_str[] = "connection lost";
+
+const char *
+connection_state2str( int state )
+{
+ switch( state ) {
+ case SLAP_C_INVALID: return "!";
+ case SLAP_C_INACTIVE: return "|";
+ case SLAP_C_CLOSING: return "C";
+ case SLAP_C_ACTIVE: return "";
+ case SLAP_C_BINDING: return "B";
+ case SLAP_C_CLIENT: return "L";
+ }
+
+ return "?";
+}
+
+static Connection* connection_get( ber_socket_t s );
+
+typedef struct conn_readinfo {
+ Operation *op;
+ ldap_pvt_thread_start_t *func;
+ void *arg;
+ void *ctx;
+ int nullop;
+} conn_readinfo;
+
+static int connection_input( Connection *c, conn_readinfo *cri );
+static void connection_close( Connection *c );
+
+static int connection_op_activate( Operation *op );
+static void connection_op_queue( Operation *op );
+static int connection_resched( Connection *conn );
+static void connection_abandon( Connection *conn );
+static void connection_destroy( Connection *c );
+
+static ldap_pvt_thread_start_t connection_operation;
+
+/*
+ * Initialize connection management infrastructure.
+ */
+int connections_init(void)
+{
+ int i;
+
+ assert( connections == NULL );
+
+ if( connections != NULL) {
+ Debug( LDAP_DEBUG_ANY, "connections_init: already initialized.\n" );
+ return -1;
+ }
+
+ /* should check return of every call */
+ ldap_pvt_thread_mutex_init( &conn_nextid_mutex );
+
+ connections = (Connection *) ch_calloc( dtblsize, sizeof(Connection) );
+
+ if( connections == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "connections_init: "
+ "allocation (%d*%ld) of connection array failed\n",
+ dtblsize, (long) sizeof(Connection) );
+ return -1;
+ }
+
+ for (i=0; i<dtblsize; i++) {
+ connections[i].c_conn_idx = i;
+ ldap_pvt_thread_mutex_init( &connections[i].c_mutex );
+ ldap_pvt_thread_mutex_init( &connections[i].c_write1_mutex );
+ ldap_pvt_thread_cond_init( &connections[i].c_write1_cv );
+ }
+
+
+ /*
+ * per entry initialization of the Connection array initialization
+ * will be done by connection_init()
+ */
+
+ return 0;
+}
+
+/*
+ * Destroy connection management infrastructure.
+ */
+
+int connections_destroy(void)
+{
+ ber_socket_t i;
+
+ /* should check return of every call */
+
+ if( connections == NULL) {
+ Debug( LDAP_DEBUG_ANY, "connections_destroy: nothing to destroy.\n" );
+ return -1;
+ }
+
+ for ( i = 0; i < dtblsize; i++ ) {
+ ldap_pvt_thread_mutex_destroy( &connections[i].c_mutex );
+ ldap_pvt_thread_mutex_destroy( &connections[i].c_write1_mutex );
+ ldap_pvt_thread_cond_destroy( &connections[i].c_write1_cv );
+ if( connections[i].c_sb ) {
+ ber_sockbuf_free( connections[i].c_sb );
+#ifdef LDAP_SLAPI
+ if ( slapi_plugins_used ) {
+ slapi_int_free_object_extensions( SLAPI_X_EXT_CONNECTION,
+ &connections[i] );
+ }
+#endif
+ }
+ }
+
+ free( connections );
+ connections = NULL;
+
+ ldap_pvt_thread_mutex_destroy( &conn_nextid_mutex );
+ return 0;
+}
+
+/*
+ * shutdown all connections
+ */
+int connections_shutdown(void)
+{
+ ber_socket_t i;
+
+ for ( i = 0; i < dtblsize; i++ ) {
+ ldap_pvt_thread_mutex_lock( &connections[i].c_mutex );
+ if( connections[i].c_conn_state > SLAP_C_INVALID ) {
+
+ /* give persistent clients a chance to cleanup */
+ if( connections[i].c_conn_state == SLAP_C_CLIENT ) {
+ ldap_pvt_thread_pool_submit( &connection_pool,
+ connections[i].c_clientfunc, connections[i].c_clientarg );
+ } else {
+ /* c_mutex is locked */
+ connection_closing( &connections[i], "slapd shutdown" );
+ connection_close( &connections[i] );
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &connections[i].c_mutex );
+ }
+
+ return 0;
+}
+
+/*
+ * Timeout idle connections.
+ */
+int connections_timeout_idle(time_t now)
+{
+ int i = 0;
+ ber_socket_t connindex;
+ Connection* c;
+
+ for( c = connection_first( &connindex );
+ c != NULL;
+ c = connection_next( c, &connindex ) )
+ {
+ /* Don't timeout a slow-running request or a persistent
+ * outbound connection.
+ */
+ if( c->c_n_ops_executing || c->c_n_ops_async
+ || c->c_conn_state == SLAP_C_CLIENT ) {
+ continue;
+ }
+
+ if( global_idletimeout &&
+ difftime( c->c_activitytime+global_idletimeout, now) < 0 ) {
+ /* close it */
+ connection_closing( c, "idletimeout" );
+ connection_close( c );
+ i++;
+ continue;
+ }
+ }
+ connection_done( c );
+
+ return i;
+}
+
+/* Drop all client connections */
+void connections_drop()
+{
+ Connection* c;
+ ber_socket_t connindex;
+
+ for( c = connection_first( &connindex );
+ c != NULL;
+ c = connection_next( c, &connindex ) )
+ {
+ /* Don't close a slow-running request or a persistent
+ * outbound connection.
+ */
+ if( c->c_n_ops_executing || c->c_n_ops_async
+ || c->c_conn_state == SLAP_C_CLIENT ) {
+ continue;
+ }
+ connection_closing( c, "dropping" );
+ connection_close( c );
+ }
+ connection_done( c );
+}
+
+static Connection* connection_get( ber_socket_t s )
+{
+ Connection *c;
+
+ Debug( LDAP_DEBUG_ARGS,
+ "connection_get(%ld)\n",
+ (long) s );
+
+ assert( connections != NULL );
+
+ if(s == AC_SOCKET_INVALID) return NULL;
+
+ assert( s < dtblsize );
+ c = &connections[s];
+
+ if( c != NULL ) {
+ ldap_pvt_thread_mutex_lock( &c->c_mutex );
+
+ if( c->c_conn_state == SLAP_C_INVALID ) {
+ /* connection must have been closed due to resched */
+
+ Debug( LDAP_DEBUG_CONNS,
+ "connection_get(%d): connection not used\n",
+ s );
+ assert( c->c_sd == AC_SOCKET_INVALID );
+
+ ldap_pvt_thread_mutex_unlock( &c->c_mutex );
+ return NULL;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "connection_get(%d): got connid=%lu\n",
+ s, c->c_connid );
+
+ c->c_n_get++;
+
+ assert( c->c_conn_state != SLAP_C_INVALID );
+ assert( c->c_sd != AC_SOCKET_INVALID );
+
+ c->c_activitytime = slap_get_time();
+ }
+
+ return c;
+}
+
+static void connection_return( Connection *c )
+{
+ ldap_pvt_thread_mutex_unlock( &c->c_mutex );
+}
+
+Connection * connection_init(
+ ber_socket_t s,
+ Listener *listener,
+ const char* dnsname,
+ const char* peername,
+ int flags,
+ slap_ssf_t ssf,
+ struct berval *authid
+ LDAP_PF_LOCAL_SENDMSG_ARG(struct berval *peerbv))
+{
+ unsigned long id;
+ Connection *c;
+ ber_socket_t sfd = SLAP_FD2SOCK(s);
+
+ assert( connections != NULL );
+
+ assert( listener != NULL );
+ assert( dnsname != NULL );
+ assert( peername != NULL );
+
+#ifndef HAVE_TLS
+ assert( !( flags & CONN_IS_TLS ));
+#endif
+
+ if( s == AC_SOCKET_INVALID ) {
+ Debug( LDAP_DEBUG_ANY,
+ "connection_init: init of socket %ld invalid.\n", (long)s );
+ return NULL;
+ }
+
+ assert( s >= 0 );
+ assert( s < dtblsize );
+ c = &connections[s];
+
+ ldap_pvt_thread_mutex_lock( &c->c_mutex );
+
+ if( !c->c_sb ) {
+ c->c_send_ldap_result = slap_send_ldap_result;
+ c->c_send_search_entry = slap_send_search_entry;
+ c->c_send_search_reference = slap_send_search_reference;
+ c->c_send_ldap_extended = slap_send_ldap_extended;
+ c->c_send_ldap_intermediate = slap_send_ldap_intermediate;
+
+ BER_BVZERO( &c->c_authmech );
+ BER_BVZERO( &c->c_dn );
+ BER_BVZERO( &c->c_ndn );
+
+ c->c_listener = NULL;
+ BER_BVZERO( &c->c_peer_domain );
+ BER_BVZERO( &c->c_peer_name );
+
+ LDAP_STAILQ_INIT(&c->c_ops);
+ LDAP_STAILQ_INIT(&c->c_pending_ops);
+
+ c->c_txn = CONN_TXN_INACTIVE;
+ c->c_txn_backend = NULL;
+ LDAP_STAILQ_INIT(&c->c_txn_ops);
+
+ BER_BVZERO( &c->c_sasl_bind_mech );
+ c->c_sasl_done = 0;
+ c->c_sasl_authctx = NULL;
+ c->c_sasl_sockctx = NULL;
+ c->c_sasl_extra = NULL;
+ c->c_sasl_bindop = NULL;
+ c->c_sasl_cbind = NULL;
+
+ c->c_sb = ber_sockbuf_alloc( );
+
+ {
+ ber_len_t max = sockbuf_max_incoming;
+ ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_MAX_INCOMING, &max );
+ }
+
+ c->c_currentber = NULL;
+
+#ifdef LDAP_SLAPI
+ if ( slapi_plugins_used ) {
+ slapi_int_create_object_extensions( SLAPI_X_EXT_CONNECTION, c );
+ }
+#endif
+ }
+
+ assert( BER_BVISNULL( &c->c_authmech ) );
+ assert( BER_BVISNULL( &c->c_dn ) );
+ assert( BER_BVISNULL( &c->c_ndn ) );
+ assert( c->c_listener == NULL );
+ assert( BER_BVISNULL( &c->c_peer_domain ) );
+ assert( BER_BVISNULL( &c->c_peer_name ) );
+ assert( LDAP_STAILQ_EMPTY(&c->c_ops) );
+ assert( LDAP_STAILQ_EMPTY(&c->c_pending_ops) );
+ assert( c->c_txn == CONN_TXN_INACTIVE );
+ assert( c->c_txn_backend == NULL );
+ assert( LDAP_STAILQ_EMPTY(&c->c_txn_ops) );
+ assert( BER_BVISNULL( &c->c_sasl_bind_mech ) );
+ assert( c->c_sasl_done == 0 );
+ assert( c->c_sasl_authctx == NULL );
+ assert( c->c_sasl_sockctx == NULL );
+ assert( c->c_sasl_extra == NULL );
+ assert( c->c_sasl_bindop == NULL );
+ assert( c->c_sasl_cbind == NULL );
+ assert( c->c_currentber == NULL );
+ assert( c->c_writewaiter == 0);
+ assert( c->c_writers == 0);
+
+ c->c_listener = listener;
+ c->c_sd = s;
+
+ if ( flags & CONN_IS_CLIENT ) {
+ c->c_connid = 0;
+ c->c_conn_state = SLAP_C_CLIENT;
+ c->c_close_reason = "?"; /* should never be needed */
+ ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_FD, &sfd );
+ ldap_pvt_thread_mutex_unlock( &c->c_mutex );
+
+ return c;
+ }
+
+ ber_str2bv( dnsname, 0, 1, &c->c_peer_domain );
+ ber_str2bv( peername, 0, 1, &c->c_peer_name );
+
+ c->c_n_ops_received = 0;
+ c->c_n_ops_executing = 0;
+ c->c_n_ops_pending = 0;
+ c->c_n_ops_completed = 0;
+ c->c_n_ops_async = 0;
+
+ c->c_n_get = 0;
+ c->c_n_read = 0;
+ c->c_n_write = 0;
+
+ /* set to zero until bind, implies LDAP_VERSION3 */
+ c->c_protocol = 0;
+
+ c->c_activitytime = c->c_starttime = slap_get_time();
+
+#ifdef LDAP_CONNECTIONLESS
+ c->c_is_udp = 0;
+ if( flags & CONN_IS_UDP ) {
+ c->c_is_udp = 1;
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_PROVIDER, (void*)"udp_" );
+#endif
+ ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_udp,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)&sfd );
+ ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_readahead,
+ LBER_SBIOD_LEVEL_PROVIDER, NULL );
+ } else
+#endif /* LDAP_CONNECTIONLESS */
+#ifdef LDAP_PF_LOCAL
+ if ( flags & CONN_IS_IPC ) {
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_PROVIDER, (void*)"ipc_" );
+#endif
+ ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_fd,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)&sfd );
+#ifdef LDAP_PF_LOCAL_SENDMSG
+ if ( !BER_BVISEMPTY( peerbv ))
+ ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_UNGET_BUF, peerbv );
+#endif
+ } else
+#endif /* LDAP_PF_LOCAL */
+ {
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_PROVIDER, (void*)"tcp_" );
+#endif
+ ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_tcp,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)&sfd );
+ }
+
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( c->c_sb, &ber_sockbuf_io_debug,
+ INT_MAX, (void*)"ldap_" );
+#endif
+
+ if( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_NONBLOCK,
+ c /* non-NULL */ ) < 0 )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "connection_init(%d, %s): set nonblocking failed\n",
+ s, c->c_peer_name.bv_val );
+
+ c->c_listener = NULL;
+ if(c->c_peer_domain.bv_val != NULL) {
+ free(c->c_peer_domain.bv_val);
+ }
+ BER_BVZERO( &c->c_peer_domain );
+ if(c->c_peer_name.bv_val != NULL) {
+ free(c->c_peer_name.bv_val);
+ }
+ BER_BVZERO( &c->c_peer_name );
+
+ ber_sockbuf_free( c->c_sb );
+ c->c_sb = ber_sockbuf_alloc( );
+ {
+ ber_len_t max = sockbuf_max_incoming;
+ ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_MAX_INCOMING, &max );
+ }
+
+ c->c_sd = AC_SOCKET_INVALID;
+ ldap_pvt_thread_mutex_unlock( &c->c_mutex );
+
+ return NULL;
+ }
+
+ ldap_pvt_thread_mutex_lock( &conn_nextid_mutex );
+ id = c->c_connid = conn_nextid++;
+ ldap_pvt_thread_mutex_unlock( &conn_nextid_mutex );
+
+ c->c_conn_state = SLAP_C_INACTIVE;
+ c->c_close_reason = "?"; /* should never be needed */
+
+ c->c_ssf = c->c_transport_ssf = ssf;
+ c->c_tls_ssf = c->c_sasl_ssf = 0;
+
+#ifdef HAVE_TLS
+ if ( flags & CONN_IS_TLS ) {
+ c->c_is_tls = 1;
+ c->c_needs_tls_accept = 1;
+ } else {
+ c->c_is_tls = 0;
+ c->c_needs_tls_accept = 0;
+ }
+#endif
+
+ slap_sasl_open( c, 0 );
+ slap_sasl_external( c, ssf, authid );
+
+ slapd_add_internal( s, 1 );
+
+ backend_connection_init(c);
+ ldap_pvt_thread_mutex_unlock( &c->c_mutex );
+
+ if ( !(flags & CONN_IS_UDP ))
+ Debug( LDAP_DEBUG_STATS,
+ "conn=%ld fd=%ld ACCEPT from %s (%s)\n",
+ id, (long) s, peername, listener->sl_name.bv_val );
+
+ return c;
+}
+
+void connection2anonymous( Connection *c )
+{
+ assert( connections != NULL );
+ assert( c != NULL );
+
+ {
+ ber_len_t max = sockbuf_max_incoming;
+ ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_MAX_INCOMING, &max );
+ }
+
+ if ( !BER_BVISNULL( &c->c_authmech ) ) {
+ ch_free(c->c_authmech.bv_val);
+ }
+ BER_BVZERO( &c->c_authmech );
+
+ if ( !BER_BVISNULL( &c->c_dn ) ) {
+ ch_free(c->c_dn.bv_val);
+ }
+ BER_BVZERO( &c->c_dn );
+
+ if ( !BER_BVISNULL( &c->c_ndn ) ) {
+ ch_free(c->c_ndn.bv_val);
+ }
+ BER_BVZERO( &c->c_ndn );
+
+ if ( !BER_BVISNULL( &c->c_sasl_authz_dn ) ) {
+ ber_memfree_x( c->c_sasl_authz_dn.bv_val, NULL );
+ }
+ BER_BVZERO( &c->c_sasl_authz_dn );
+
+ c->c_authz_backend = NULL;
+}
+
+static void
+connection_destroy( Connection *c )
+{
+ unsigned long connid;
+ const char *close_reason;
+ Sockbuf *sb;
+ ber_socket_t sd;
+
+ assert( connections != NULL );
+ assert( c != NULL );
+ assert( c->c_conn_state != SLAP_C_INVALID );
+ assert( LDAP_STAILQ_EMPTY(&c->c_ops) );
+ assert( LDAP_STAILQ_EMPTY(&c->c_pending_ops) );
+ assert( c->c_txn == CONN_TXN_INACTIVE );
+ assert( c->c_txn_backend == NULL );
+ assert( LDAP_STAILQ_EMPTY(&c->c_txn_ops) );
+ assert( c->c_writewaiter == 0);
+ assert( c->c_writers == 0);
+
+ /* only for stats (print -1 as "%lu" may give unexpected results ;) */
+ connid = c->c_connid;
+ close_reason = c->c_close_reason;
+
+ backend_connection_destroy(c);
+
+ c->c_protocol = 0;
+ c->c_connid = -1;
+
+ c->c_activitytime = c->c_starttime = 0;
+
+ connection2anonymous( c );
+ c->c_listener = NULL;
+
+ if(c->c_peer_domain.bv_val != NULL) {
+ free(c->c_peer_domain.bv_val);
+ }
+ BER_BVZERO( &c->c_peer_domain );
+ if(c->c_peer_name.bv_val != NULL) {
+ free(c->c_peer_name.bv_val);
+ }
+ BER_BVZERO( &c->c_peer_name );
+
+ c->c_sasl_bind_in_progress = 0;
+ if(c->c_sasl_bind_mech.bv_val != NULL) {
+ free(c->c_sasl_bind_mech.bv_val);
+ }
+ BER_BVZERO( &c->c_sasl_bind_mech );
+
+ slap_sasl_close( c );
+
+ if ( c->c_currentber != NULL ) {
+ ber_free( c->c_currentber, 1 );
+ c->c_currentber = NULL;
+ }
+
+
+#ifdef LDAP_SLAPI
+ /* call destructors, then constructors; avoids unnecessary allocation */
+ if ( slapi_plugins_used ) {
+ slapi_int_clear_object_extensions( SLAPI_X_EXT_CONNECTION, c );
+ }
+#endif
+
+ sd = c->c_sd;
+ c->c_sd = AC_SOCKET_INVALID;
+ c->c_close_reason = "?"; /* should never be needed */
+
+ sb = c->c_sb;
+ c->c_sb = ber_sockbuf_alloc( );
+ {
+ ber_len_t max = sockbuf_max_incoming;
+ ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_MAX_INCOMING, &max );
+ }
+ c->c_conn_state = SLAP_C_INVALID;
+
+ /* c must be fully reset by this point; when we call slapd_remove
+ * it may get immediately reused by a new connection.
+ */
+ if ( sd != AC_SOCKET_INVALID ) {
+ slapd_remove( sd, sb, 1, 0, 0 );
+
+ if ( close_reason == NULL ) {
+ Debug( LDAP_DEBUG_STATS, "conn=%lu fd=%ld closed\n",
+ connid, (long) sd );
+ } else {
+ Debug( LDAP_DEBUG_STATS, "conn=%lu fd=%ld closed (%s)\n",
+ connid, (long) sd, close_reason );
+ }
+ }
+}
+
+int connection_is_active( ber_socket_t s )
+{
+ Connection *c;
+
+ assert( s < dtblsize );
+ c = &connections[s];
+ return c->c_conn_state == SLAP_C_CLOSING ||
+ c->c_conn_state == SLAP_C_BINDING ||
+ c->c_conn_state == SLAP_C_ACTIVE ;
+}
+
+int connection_valid( Connection *c )
+{
+ /* c_mutex must be locked by caller */
+
+ assert( c != NULL );
+
+ return c->c_conn_state >= SLAP_C_ACTIVE &&
+ c->c_conn_state <= SLAP_C_CLIENT;
+}
+
+static void connection_abandon( Connection *c )
+{
+ /* c_mutex must be locked by caller */
+
+ Operation *o, *next, op = {0};
+ Opheader ohdr = {0};
+
+ op.o_hdr = &ohdr;
+ op.o_conn = c;
+ op.o_connid = c->c_connid;
+ op.o_tag = LDAP_REQ_ABANDON;
+
+ for ( o = LDAP_STAILQ_FIRST( &c->c_ops ); o; o=next ) {
+ SlapReply rs = {REP_RESULT};
+
+ next = LDAP_STAILQ_NEXT( o, o_next );
+ /* don't abandon an op twice */
+ if ( o->o_abandon )
+ continue;
+ op.orn_msgid = o->o_msgid;
+ o->o_abandon = 1;
+ op.o_bd = frontendDB;
+ frontendDB->be_abandon( &op, &rs );
+ }
+
+ /* remove operations in pending transaction */
+ while ( (o = LDAP_STAILQ_FIRST( &c->c_txn_ops )) != NULL) {
+ LDAP_STAILQ_REMOVE_HEAD( &c->c_txn_ops, o_next );
+ LDAP_STAILQ_NEXT(o, o_next) = NULL;
+ slap_op_free( o, NULL );
+ }
+
+ /* clear transaction */
+ c->c_txn_backend = NULL;
+ c->c_txn = CONN_TXN_INACTIVE;
+
+ /* remove pending operations */
+ while ( (o = LDAP_STAILQ_FIRST( &c->c_pending_ops )) != NULL) {
+ LDAP_STAILQ_REMOVE_HEAD( &c->c_pending_ops, o_next );
+ LDAP_STAILQ_NEXT(o, o_next) = NULL;
+ slap_op_free( o, NULL );
+ }
+ c->c_n_ops_pending = 0;
+}
+
+static void
+connection_wake_writers( Connection *c )
+{
+ /* wake write blocked operations */
+ ldap_pvt_thread_mutex_lock( &c->c_write1_mutex );
+ if ( c->c_writers > 0 ) {
+ c->c_writers = -c->c_writers;
+ ldap_pvt_thread_cond_broadcast( &c->c_write1_cv );
+ ldap_pvt_thread_mutex_unlock( &c->c_write1_mutex );
+ if ( c->c_writewaiter ) {
+ slapd_shutsock( c->c_sd );
+ }
+ ldap_pvt_thread_mutex_lock( &c->c_write1_mutex );
+ while ( c->c_writers ) {
+ ldap_pvt_thread_cond_wait( &c->c_write1_cv, &c->c_write1_mutex );
+ }
+ ldap_pvt_thread_mutex_unlock( &c->c_write1_mutex );
+ } else {
+ ldap_pvt_thread_mutex_unlock( &c->c_write1_mutex );
+ slapd_clr_write( c->c_sd, 1 );
+ }
+}
+
+void connection_closing( Connection *c, const char *why )
+{
+ assert( connections != NULL );
+ assert( c != NULL );
+
+ if( c->c_conn_state == SLAP_C_INVALID )
+ return;
+
+ /* c_mutex must be locked by caller */
+
+ if( c->c_conn_state != SLAP_C_CLOSING ) {
+ Debug( LDAP_DEBUG_CONNS,
+ "connection_closing: readying conn=%lu sd=%d for close\n",
+ c->c_connid, c->c_sd );
+ /* update state to closing */
+ c->c_conn_state = SLAP_C_CLOSING;
+ c->c_close_reason = why;
+
+ /* don't listen on this port anymore */
+ slapd_clr_read( c->c_sd, 0 );
+
+ /* abandon active operations */
+ connection_abandon( c );
+
+ /* wake write blocked operations */
+ connection_wake_writers( c );
+
+ } else if( why == NULL && c->c_close_reason == conn_lost_str ) {
+ /* Client closed connection after doing Unbind. */
+ c->c_close_reason = NULL;
+ }
+}
+
+static void
+connection_close( Connection *c )
+{
+ assert( connections != NULL );
+ assert( c != NULL );
+
+ if ( c->c_conn_state != SLAP_C_CLOSING )
+ return;
+
+ /* NOTE: c_mutex should be locked by caller */
+
+ if ( !LDAP_STAILQ_EMPTY(&c->c_ops) ||
+ !LDAP_STAILQ_EMPTY(&c->c_pending_ops) )
+ {
+ Debug( LDAP_DEBUG_CONNS,
+ "connection_close: deferring conn=%lu sd=%d\n",
+ c->c_connid, c->c_sd );
+ return;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "connection_close: conn=%lu sd=%d\n",
+ c->c_connid, c->c_sd );
+
+ connection_destroy( c );
+}
+
+unsigned long connections_nextid(void)
+{
+ unsigned long id;
+ assert( connections != NULL );
+
+ ldap_pvt_thread_mutex_lock( &conn_nextid_mutex );
+
+ id = conn_nextid;
+
+ ldap_pvt_thread_mutex_unlock( &conn_nextid_mutex );
+
+ return id;
+}
+
+/*
+ * Loop through the connections:
+ *
+ * for (c = connection_first(&i); c; c = connection_next(c, &i)) ...;
+ * connection_done(c);
+ *
+ * 'i' is the cursor, initialized by connection_first().
+ * 'c_mutex' is locked in the returned connection. The functions must
+ * be passed the previous return value so they can unlock it again.
+ */
+
+Connection* connection_first( ber_socket_t *index )
+{
+ assert( connections != NULL );
+ assert( index != NULL );
+
+ for( *index = 0; *index < dtblsize; (*index)++) {
+ if( connections[*index].c_sb ) {
+ break;
+ }
+ }
+
+ return connection_next(NULL, index);
+}
+
+/* Next connection in loop, see connection_first() */
+Connection* connection_next( Connection *c, ber_socket_t *index )
+{
+ assert( connections != NULL );
+ assert( index != NULL );
+ assert( *index <= dtblsize );
+
+ if( c != NULL ) ldap_pvt_thread_mutex_unlock( &c->c_mutex );
+
+ c = NULL;
+
+ for(; *index < dtblsize; (*index)++) {
+ if( connections[*index].c_sb ) {
+ c = &connections[*index];
+ ldap_pvt_thread_mutex_lock( &c->c_mutex );
+ if ( c->c_conn_state == SLAP_C_INVALID ) {
+ ldap_pvt_thread_mutex_unlock( &c->c_mutex );
+ c = NULL;
+ continue;
+ }
+ (*index)++;
+ break;
+ }
+ }
+
+ return c;
+}
+
+/* End connection loop, see connection_first() */
+void connection_done( Connection *c )
+{
+ assert( connections != NULL );
+
+ if( c != NULL ) ldap_pvt_thread_mutex_unlock( &c->c_mutex );
+}
+
+/*
+ * connection_activity - handle the request operation op on connection
+ * conn. This routine figures out what kind of operation it is and
+ * calls the appropriate stub to handle it.
+ */
+
+/* FIXME: returns 0 in case of failure */
+#define INCR_OP_INITIATED(index) \
+ do { \
+ ldap_pvt_thread_mutex_lock( &op->o_counters->sc_mutex ); \
+ ldap_pvt_mp_add_ulong(op->o_counters->sc_ops_initiated_[(index)], 1); \
+ ldap_pvt_thread_mutex_unlock( &op->o_counters->sc_mutex ); \
+ } while (0)
+#define INCR_OP_COMPLETED(index) \
+ do { \
+ ldap_pvt_thread_mutex_lock( &op->o_counters->sc_mutex ); \
+ ldap_pvt_mp_add_ulong(op->o_counters->sc_ops_completed, 1); \
+ ldap_pvt_mp_add_ulong(op->o_counters->sc_ops_completed_[(index)], 1); \
+ ldap_pvt_thread_mutex_unlock( &op->o_counters->sc_mutex ); \
+ } while (0)
+
+/*
+ * NOTE: keep in sync with enum in slapd.h
+ */
+static BI_op_func *opfun[] = {
+ do_bind,
+ do_unbind,
+ do_search,
+ do_compare,
+ do_modify,
+ do_modrdn,
+ do_add,
+ do_delete,
+ do_abandon,
+ do_extended,
+ NULL
+};
+
+/* Counters are per-thread, not per-connection.
+ */
+static void
+conn_counter_destroy( void *key, void *data )
+{
+ slap_counters_t **prev, *sc;
+
+ ldap_pvt_thread_mutex_lock( &slap_counters.sc_mutex );
+ for ( prev = &slap_counters.sc_next, sc = slap_counters.sc_next; sc;
+ prev = &sc->sc_next, sc = sc->sc_next ) {
+ if ( sc == data ) {
+ int i;
+
+ *prev = sc->sc_next;
+ /* Copy data to main counter */
+ ldap_pvt_mp_add( slap_counters.sc_bytes, sc->sc_bytes );
+ ldap_pvt_mp_add( slap_counters.sc_pdu, sc->sc_pdu );
+ ldap_pvt_mp_add( slap_counters.sc_entries, sc->sc_entries );
+ ldap_pvt_mp_add( slap_counters.sc_refs, sc->sc_refs );
+ ldap_pvt_mp_add( slap_counters.sc_ops_initiated, sc->sc_ops_initiated );
+ ldap_pvt_mp_add( slap_counters.sc_ops_completed, sc->sc_ops_completed );
+ for ( i = 0; i < SLAP_OP_LAST; i++ ) {
+ ldap_pvt_mp_add( slap_counters.sc_ops_initiated_[ i ], sc->sc_ops_initiated_[ i ] );
+ ldap_pvt_mp_add( slap_counters.sc_ops_initiated_[ i ], sc->sc_ops_completed_[ i ] );
+ }
+ slap_counters_destroy( sc );
+ ber_memfree_x( data, NULL );
+ break;
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &slap_counters.sc_mutex );
+}
+
+void
+operation_counter_init( Operation *op, void *ctx )
+{
+ slap_counters_t *sc;
+ void *vsc = NULL;
+
+ if ( ldap_pvt_thread_pool_getkey(
+ ctx, (void *)operation_counter_init, &vsc, NULL ) || !vsc ) {
+ vsc = ch_malloc( sizeof( slap_counters_t ));
+ sc = vsc;
+ slap_counters_init( sc );
+ ldap_pvt_thread_pool_setkey( ctx, (void*)operation_counter_init, vsc,
+ conn_counter_destroy, NULL, NULL );
+
+ ldap_pvt_thread_mutex_lock( &slap_counters.sc_mutex );
+ sc->sc_next = slap_counters.sc_next;
+ slap_counters.sc_next = sc;
+ ldap_pvt_thread_mutex_unlock( &slap_counters.sc_mutex );
+ }
+ op->o_counters = vsc;
+}
+
+void
+connection_op_finish( Operation *op )
+{
+ Connection *conn = op->o_conn;
+ void *memctx_null = NULL;
+ slap_op_t opidx = slap_req2op( op->o_tag );
+ assert( opidx != SLAP_OP_LAST );
+
+ INCR_OP_COMPLETED( opidx );
+
+ ldap_pvt_thread_mutex_lock( &conn->c_mutex );
+
+ if ( op->o_tag == LDAP_REQ_BIND && conn->c_conn_state == SLAP_C_BINDING )
+ conn->c_conn_state = SLAP_C_ACTIVE;
+
+ ber_set_option( op->o_ber, LBER_OPT_BER_MEMCTX, &memctx_null );
+
+ LDAP_STAILQ_REMOVE( &conn->c_ops, op, Operation, o_next);
+ LDAP_STAILQ_NEXT(op, o_next) = NULL;
+ conn->c_n_ops_async--;
+ conn->c_n_ops_completed++;
+ connection_resched( conn );
+ ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
+}
+
+static void *
+connection_operation( void *ctx, void *arg_v )
+{
+ int rc = LDAP_OTHER, cancel;
+ Operation *op = arg_v;
+ SlapReply rs = {REP_RESULT};
+ ber_tag_t tag = op->o_tag;
+ slap_op_t opidx = SLAP_OP_LAST;
+ Connection *conn = op->o_conn;
+ void *memctx = NULL;
+ void *memctx_null = NULL;
+ ber_len_t memsiz;
+
+ gettimeofday( &op->o_qtime, NULL );
+ op->o_qtime.tv_usec -= op->o_tusec;
+ if ( op->o_qtime.tv_usec < 0 ) {
+ op->o_qtime.tv_usec += 1000000;
+ op->o_qtime.tv_sec--;
+ }
+ op->o_qtime.tv_sec -= op->o_time;
+ operation_counter_init( op, ctx );
+ ldap_pvt_thread_mutex_lock( &op->o_counters->sc_mutex );
+ /* FIXME: returns 0 in case of failure */
+ ldap_pvt_mp_add_ulong(op->o_counters->sc_ops_initiated, 1);
+ ldap_pvt_thread_mutex_unlock( &op->o_counters->sc_mutex );
+
+ op->o_threadctx = ctx;
+ op->o_tid = ldap_pvt_thread_pool_tid( ctx );
+
+ switch ( tag ) {
+ case LDAP_REQ_BIND:
+ case LDAP_REQ_UNBIND:
+ case LDAP_REQ_ADD:
+ case LDAP_REQ_DELETE:
+ case LDAP_REQ_MODDN:
+ case LDAP_REQ_MODIFY:
+ case LDAP_REQ_COMPARE:
+ case LDAP_REQ_SEARCH:
+ case LDAP_REQ_ABANDON:
+ case LDAP_REQ_EXTENDED:
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY, "connection_operation: "
+ "conn %lu unknown LDAP request 0x%lx\n",
+ conn->c_connid, tag );
+ op->o_tag = LBER_ERROR;
+ rs.sr_err = LDAP_PROTOCOL_ERROR;
+ rs.sr_text = "unknown LDAP request";
+ send_ldap_disconnect( op, &rs );
+ rc = SLAPD_DISCONNECT;
+ goto operations_error;
+ }
+
+ if( conn->c_sasl_bind_in_progress && tag != LDAP_REQ_BIND ) {
+ Debug( LDAP_DEBUG_ANY, "connection_operation: "
+ "error: SASL bind in progress (tag=%ld).\n",
+ (long) tag );
+ send_ldap_error( op, &rs, LDAP_OPERATIONS_ERROR,
+ "SASL bind in progress" );
+ rc = LDAP_OPERATIONS_ERROR;
+ goto operations_error;
+ }
+
+ if (( conn->c_txn == CONN_TXN_SPECIFY ) && (
+ ( tag == LDAP_REQ_ADD ) ||
+ ( tag == LDAP_REQ_DELETE ) ||
+ ( tag == LDAP_REQ_MODIFY ) ||
+ ( tag == LDAP_REQ_MODRDN )))
+ {
+ /* Disable SLAB allocator for all update operations
+ issued inside of a transaction */
+ op->o_tmpmemctx = NULL;
+ op->o_tmpmfuncs = &ch_mfuncs;
+ } else {
+ /* We can use Thread-Local storage for most mallocs. We can
+ * also use TL for ber parsing, but not on Add or Modify.
+ */
+#if 0
+ memsiz = ber_len( op->o_ber ) * 64;
+ if ( SLAP_SLAB_SIZE > memsiz ) memsiz = SLAP_SLAB_SIZE;
+#endif
+ memsiz = SLAP_SLAB_SIZE;
+
+ memctx = slap_sl_mem_create( memsiz, SLAP_SLAB_STACK, ctx, 1 );
+ op->o_tmpmemctx = memctx;
+ op->o_tmpmfuncs = &slap_sl_mfuncs;
+ if ( tag != LDAP_REQ_ADD && tag != LDAP_REQ_MODIFY ) {
+ /* Note - the ber and its buffer are already allocated from
+ * regular memory; this only affects subsequent mallocs that
+ * ber_scanf may invoke.
+ */
+ ber_set_option( op->o_ber, LBER_OPT_BER_MEMCTX, &memctx );
+ }
+ }
+
+ opidx = slap_req2op( tag );
+ assert( opidx != SLAP_OP_LAST );
+ INCR_OP_INITIATED( opidx );
+ rc = (*(opfun[opidx]))( op, &rs );
+
+operations_error:
+ if ( rc == SLAPD_DISCONNECT ) {
+ tag = LBER_ERROR;
+
+ } else if ( rc == SLAPD_ASYNCOP ) {
+ /* someone has claimed ownership of the op
+ * to complete it later. Don't do anything
+ * else with it now. Detach memctx too.
+ */
+ slap_sl_mem_setctx( ctx, NULL );
+ ldap_pvt_thread_mutex_lock( &conn->c_mutex );
+ conn->c_n_ops_executing--;
+ conn->c_n_ops_async++;
+ connection_resched( conn );
+ ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
+ return NULL;
+
+ } else if ( opidx != SLAP_OP_LAST ) {
+ /* increment completed operations count
+ * only if operation was initiated
+ * and rc != SLAPD_DISCONNECT */
+ INCR_OP_COMPLETED( opidx );
+ }
+
+ ldap_pvt_thread_mutex_lock( &conn->c_mutex );
+
+ if ( opidx == SLAP_OP_BIND && conn->c_conn_state == SLAP_C_BINDING )
+ conn->c_conn_state = SLAP_C_ACTIVE;
+
+ cancel = op->o_cancel;
+ if ( cancel != SLAP_CANCEL_NONE && cancel != SLAP_CANCEL_DONE ) {
+ if ( cancel == SLAP_CANCEL_REQ ) {
+ op->o_cancel = rc == SLAPD_ABANDON
+ ? SLAP_CANCEL_ACK : LDAP_TOO_LATE;
+ }
+
+ do {
+ /* Fake a cond_wait with thread_yield, then
+ * verify the result properly mutex-protected.
+ */
+ ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
+ do {
+ ldap_pvt_thread_yield();
+ } while ( (cancel = op->o_cancel) != SLAP_CANCEL_NONE
+ && cancel != SLAP_CANCEL_DONE );
+ ldap_pvt_thread_mutex_lock( &conn->c_mutex );
+ } while ( (cancel = op->o_cancel) != SLAP_CANCEL_NONE
+ && cancel != SLAP_CANCEL_DONE );
+ }
+
+ ber_set_option( op->o_ber, LBER_OPT_BER_MEMCTX, &memctx_null );
+
+ if ( rc != LDAP_TXN_SPECIFY_OKAY ) {
+ LDAP_STAILQ_REMOVE( &conn->c_ops, op, Operation, o_next);
+ LDAP_STAILQ_NEXT(op, o_next) = NULL;
+ }
+ conn->c_n_ops_executing--;
+ conn->c_n_ops_completed++;
+
+ switch( tag ) {
+ case LBER_ERROR:
+ case LDAP_REQ_UNBIND:
+ /* c_mutex is locked */
+ connection_closing( conn,
+ tag == LDAP_REQ_UNBIND ? NULL : "operations error" );
+ break;
+ }
+
+ connection_resched( conn );
+ ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
+ if ( rc != LDAP_TXN_SPECIFY_OKAY ) {
+ slap_op_free( op, ctx );
+ }
+ return NULL;
+}
+
+static const Listener dummy_list = { BER_BVC(""), BER_BVC("") };
+
+Connection *connection_client_setup(
+ ber_socket_t s,
+ ldap_pvt_thread_start_t *func,
+ void *arg )
+{
+ Connection *c;
+ ber_socket_t sfd = SLAP_SOCKNEW( s );
+
+ c = connection_init( sfd, (Listener *)&dummy_list, "", "",
+ CONN_IS_CLIENT, 0, NULL
+ LDAP_PF_LOCAL_SENDMSG_ARG(NULL));
+ if ( c ) {
+ c->c_clientfunc = func;
+ c->c_clientarg = arg;
+
+ slapd_add_internal( sfd, 0 );
+ }
+ return c;
+}
+
+void connection_client_enable(
+ Connection *c )
+{
+ slapd_set_read( c->c_sd, 1 );
+}
+
+void connection_client_stop(
+ Connection *c )
+{
+ Sockbuf *sb;
+ ber_socket_t s = c->c_sd;
+
+ /* get (locked) connection */
+ c = connection_get( s );
+
+ assert( c->c_conn_state == SLAP_C_CLIENT );
+
+ c->c_listener = NULL;
+ c->c_sd = AC_SOCKET_INVALID;
+ c->c_close_reason = "?"; /* should never be needed */
+ sb = c->c_sb;
+ c->c_sb = ber_sockbuf_alloc( );
+ {
+ ber_len_t max = sockbuf_max_incoming;
+ ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_SET_MAX_INCOMING, &max );
+ }
+ c->c_conn_state = SLAP_C_INVALID;
+ slapd_remove( s, sb, 0, 1, 0 );
+
+ connection_return( c );
+}
+
+static int connection_read( ber_socket_t s, conn_readinfo *cri );
+
+static void* connection_read_thread( void* ctx, void* argv )
+{
+ int rc ;
+ conn_readinfo cri = { NULL, NULL, NULL, NULL, 0 };
+ ber_socket_t s = (long)argv;
+
+ /*
+ * read incoming LDAP requests. If there is more than one,
+ * the first one is returned with new_op
+ */
+ cri.ctx = ctx;
+ if( ( rc = connection_read( s, &cri ) ) < 0 ) {
+ Debug( LDAP_DEBUG_CONNS, "connection_read(%d) error\n", s );
+ return (void*)(long)rc;
+ }
+
+ /* execute a single queued request in the same thread */
+ if( cri.op && !cri.nullop ) {
+ rc = (long)connection_operation( ctx, cri.op );
+ } else if ( cri.func ) {
+ rc = (long)cri.func( ctx, cri.arg );
+ }
+
+ return (void*)(long)rc;
+}
+
+int connection_read_activate( ber_socket_t s )
+{
+ int rc;
+
+ /*
+ * suspend reading on this file descriptor until a connection processing
+ * thread reads data on it. Otherwise the listener thread will repeatedly
+ * submit the same event on it to the pool.
+ */
+ rc = slapd_clr_read( s, 0 );
+ if ( rc )
+ return rc;
+
+ rc = ldap_pvt_thread_pool_submit( &connection_pool,
+ connection_read_thread, (void *)(long)s );
+
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "connection_read_activate(%d): submit failed (%d)\n",
+ s, rc );
+ }
+
+ return rc;
+}
+
+static int
+connection_read( ber_socket_t s, conn_readinfo *cri )
+{
+ int rc = 0;
+ Connection *c;
+
+ assert( connections != NULL );
+
+ /* get (locked) connection */
+ c = connection_get( s );
+
+ if( c == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "connection_read(%ld): no connection!\n",
+ (long) s );
+
+ return -1;
+ }
+
+ c->c_n_read++;
+
+ if( c->c_conn_state == SLAP_C_CLOSING ) {
+ Debug( LDAP_DEBUG_CONNS,
+ "connection_read(%d): closing, ignoring input for id=%lu\n",
+ s, c->c_connid );
+ connection_return( c );
+ return 0;
+ }
+
+ if ( c->c_conn_state == SLAP_C_CLIENT ) {
+ cri->func = c->c_clientfunc;
+ cri->arg = c->c_clientarg;
+ /* read should already be cleared */
+ connection_return( c );
+ return 0;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "connection_read(%d): checking for input on id=%lu\n",
+ s, c->c_connid );
+
+#ifdef HAVE_TLS
+ if ( c->c_is_tls && c->c_needs_tls_accept ) {
+ rc = ldap_pvt_tls_accept( c->c_sb, slap_tls_ctx );
+ if ( rc < 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "connection_read(%d): TLS accept failure "
+ "error=%d id=%lu, closing\n",
+ s, rc, c->c_connid );
+
+ c->c_needs_tls_accept = 0;
+ /* c_mutex is locked */
+ connection_closing( c, "TLS negotiation failure" );
+ connection_close( c );
+ connection_return( c );
+ return 0;
+
+ } else if ( rc == 0 ) {
+ void *ssl;
+ struct berval authid = BER_BVNULL;
+ char msgbuf[32];
+
+ c->c_needs_tls_accept = 0;
+
+ /* we need to let SASL know */
+ ssl = ldap_pvt_tls_sb_ctx( c->c_sb );
+
+ c->c_tls_ssf = (slap_ssf_t) ldap_pvt_tls_get_strength( ssl );
+ if( c->c_tls_ssf > c->c_ssf ) {
+ c->c_ssf = c->c_tls_ssf;
+ }
+
+ rc = dnX509peerNormalize( ssl, &authid );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "connection_read(%d): "
+ "unable to get TLS client DN, error=%d id=%lu\n",
+ s, rc, c->c_connid );
+ }
+ sprintf(msgbuf, "tls_ssf=%u ssf=%u", c->c_tls_ssf, c->c_ssf);
+ Debug( LDAP_DEBUG_STATS,
+ "conn=%lu fd=%d TLS established %s tls_proto=%s tls_cipher=%s\n",
+ c->c_connid, (int) s,
+ msgbuf, ldap_pvt_tls_get_version( ssl ), ldap_pvt_tls_get_cipher( ssl ));
+ slap_sasl_external( c, c->c_tls_ssf, &authid );
+ if ( authid.bv_val ) free( authid.bv_val );
+
+ slap_sasl_cbinding( c, ssl );
+
+ } else if ( rc == 1 && ber_sockbuf_ctrl( c->c_sb,
+ LBER_SB_OPT_NEEDS_WRITE, NULL )) { /* need to retry */
+ slapd_set_write( s, 1 );
+ connection_return( c );
+ return 0;
+ }
+
+ /* if success and data is ready, fall thru to data input loop */
+ if( !ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_DATA_READY, NULL ) )
+ {
+ slapd_set_read( s, 1 );
+ connection_return( c );
+ return 0;
+ }
+ }
+#endif
+
+#ifdef HAVE_CYRUS_SASL
+ if ( c->c_sasl_layers ) {
+ /* If previous layer is not removed yet, give up for now */
+ if ( !c->c_sasl_sockctx ) {
+ slapd_set_read( s, 1 );
+ connection_return( c );
+ return 0;
+ }
+
+ c->c_sasl_layers = 0;
+
+ rc = ldap_pvt_sasl_install( c->c_sb, c->c_sasl_sockctx );
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "connection_read(%d): SASL install error "
+ "error=%d id=%lu, closing\n",
+ s, rc, c->c_connid );
+
+ /* c_mutex is locked */
+ connection_closing( c, "SASL layer install failure" );
+ connection_close( c );
+ connection_return( c );
+ return 0;
+ }
+ }
+#endif
+
+#define CONNECTION_INPUT_LOOP 1
+/* #define DATA_READY_LOOP 1 */
+
+ do {
+ /* How do we do this without getting into a busy loop ? */
+ rc = connection_input( c, cri );
+ }
+#ifdef DATA_READY_LOOP
+ while( !rc && ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_DATA_READY, NULL ));
+#elif defined CONNECTION_INPUT_LOOP
+ while(!rc);
+#else
+ while(0);
+#endif
+
+ if( rc < 0 ) {
+ Debug( LDAP_DEBUG_CONNS,
+ "connection_read(%d): input error=%d id=%lu, closing.\n",
+ s, rc, c->c_connid );
+
+ /* c_mutex is locked */
+ connection_closing( c, conn_lost_str );
+ connection_close( c );
+ connection_return( c );
+ return 0;
+ }
+
+ if ( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_WRITE, NULL ) ) {
+ slapd_set_write( s, 0 );
+ }
+
+ slapd_set_read( s, 1 );
+ connection_return( c );
+
+ return 0;
+}
+
+static int
+connection_input( Connection *conn , conn_readinfo *cri )
+{
+ Operation *op;
+ ber_tag_t tag;
+ ber_len_t len;
+ ber_int_t msgid;
+ BerElement *ber;
+ int rc;
+#ifdef LDAP_CONNECTIONLESS
+ Sockaddr peeraddr;
+ char *cdn = NULL;
+#endif
+ char *defer = NULL;
+ void *ctx;
+
+ if ( conn->c_currentber == NULL &&
+ ( conn->c_currentber = ber_alloc()) == NULL )
+ {
+ Debug( LDAP_DEBUG_ANY, "ber_alloc failed\n" );
+ return -1;
+ }
+
+ sock_errset(0);
+
+#ifdef LDAP_CONNECTIONLESS
+ if ( conn->c_is_udp ) {
+#if defined(LDAP_PF_INET6)
+ char peername[sizeof("IP=[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:65535")];
+ char addr[INET6_ADDRSTRLEN];
+#else
+ char peername[sizeof("IP=255.255.255.255:65336")];
+ char addr[INET_ADDRSTRLEN];
+#endif
+ const char *peeraddr_string = NULL;
+
+ len = ber_int_sb_read(conn->c_sb, &peeraddr, sizeof(Sockaddr));
+ if (len != sizeof(Sockaddr)) return 1;
+
+#if defined(LDAP_PF_INET6)
+ if (peeraddr.sa_addr.sa_family == AF_INET6) {
+ if ( IN6_IS_ADDR_V4MAPPED(&peeraddr.sa_in6_addr.sin6_addr) ) {
+#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
+ peeraddr_string = inet_ntop( AF_INET,
+ ((struct in_addr *)&peeraddr.sa_in6_addr.sin6_addr.s6_addr[12]),
+ addr, sizeof(addr) );
+#else /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ peeraddr_string = inet_ntoa( *((struct in_addr *)
+ &peeraddr.sa_in6_addr.sin6_addr.s6_addr[12]) );
+#endif /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ if ( !peeraddr_string ) peeraddr_string = SLAP_STRING_UNKNOWN;
+ sprintf( peername, "IP=%s:%d", peeraddr_string,
+ (unsigned) ntohs( peeraddr.sa_in6_addr.sin6_port ) );
+ } else {
+ peeraddr_string = inet_ntop( AF_INET6,
+ &peeraddr.sa_in6_addr.sin6_addr,
+ addr, sizeof addr );
+ if ( !peeraddr_string ) peeraddr_string = SLAP_STRING_UNKNOWN;
+ sprintf( peername, "IP=[%s]:%d", peeraddr_string,
+ (unsigned) ntohs( peeraddr.sa_in6_addr.sin6_port ) );
+ }
+ } else
+#endif
+#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
+ {
+ peeraddr_string = inet_ntop( AF_INET, &peeraddr.sa_in_addr.sin_addr,
+ addr, sizeof(addr) );
+#else /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ peeraddr_string = inet_ntoa( peeraddr.sa_in_addr.sin_addr );
+#endif /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ sprintf( peername, "IP=%s:%d",
+ peeraddr_string,
+ (unsigned) ntohs( peeraddr.sa_in_addr.sin_port ) );
+ }
+ Debug( LDAP_DEBUG_STATS,
+ "conn=%lu UDP request from %s (%s) accepted.\n",
+ conn->c_connid, peername, conn->c_sock_name.bv_val );
+ }
+#endif
+
+ tag = ber_get_next( conn->c_sb, &len, conn->c_currentber );
+ if ( tag != LDAP_TAG_MESSAGE ) {
+ int err = sock_errno();
+
+ if ( err != EWOULDBLOCK && err != EAGAIN ) {
+ char ebuf[128];
+ /* log, close and send error */
+ Debug( LDAP_DEBUG_TRACE,
+ "ber_get_next on fd %d failed errno=%d (%s)\n",
+ conn->c_sd, err, sock_errstr(err, ebuf, sizeof(ebuf)) );
+ ber_free( conn->c_currentber, 1 );
+ conn->c_currentber = NULL;
+
+ return -2;
+ }
+ return 1;
+ }
+
+ ber = conn->c_currentber;
+ conn->c_currentber = NULL;
+
+ if ( (tag = ber_get_int( ber, &msgid )) != LDAP_TAG_MSGID ) {
+ /* log, close and send error */
+ Debug( LDAP_DEBUG_ANY, "ber_get_int returns 0x%lx\n", tag );
+ ber_free( ber, 1 );
+ return -1;
+ }
+
+ if ( (tag = ber_peek_tag( ber, &len )) == LBER_ERROR ) {
+ /* log, close and send error */
+ Debug( LDAP_DEBUG_ANY, "ber_peek_tag returns 0x%lx\n", tag );
+ ber_free( ber, 1 );
+
+ return -1;
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ if( conn->c_is_udp ) {
+ if( tag == LBER_OCTETSTRING ) {
+ if ( (tag = ber_get_stringa( ber, &cdn )) != LBER_ERROR )
+ tag = ber_peek_tag( ber, &len );
+ }
+ if( tag != LDAP_REQ_ABANDON && tag != LDAP_REQ_SEARCH ) {
+ Debug( LDAP_DEBUG_ANY, "invalid req for UDP 0x%lx\n", tag );
+ ber_free( ber, 1 );
+ return 0;
+ }
+ }
+#endif
+
+ if(tag == LDAP_REQ_BIND) {
+ /* immediately abandon all existing operations upon BIND */
+ connection_abandon( conn );
+ }
+
+ ctx = cri->ctx;
+ op = slap_op_alloc( ber, msgid, tag, conn->c_n_ops_received++, ctx );
+
+ Debug( LDAP_DEBUG_TRACE, "op tag 0x%lx, time %ld\n", tag,
+ (long) op->o_time );
+
+ op->o_conn = conn;
+ /* clear state if the connection is being reused from inactive */
+ if ( conn->c_conn_state == SLAP_C_INACTIVE ) {
+ memset( &conn->c_pagedresults_state, 0,
+ sizeof( conn->c_pagedresults_state ) );
+ }
+
+ op->o_res_ber = NULL;
+
+#ifdef LDAP_CONNECTIONLESS
+ if (conn->c_is_udp) {
+ if ( cdn ) {
+ ber_str2bv( cdn, 0, 1, &op->o_dn );
+ op->o_protocol = LDAP_VERSION2;
+ }
+ op->o_res_ber = ber_alloc_t( LBER_USE_DER );
+ if (op->o_res_ber == NULL) return 1;
+
+ rc = ber_write( op->o_res_ber, (char *)&peeraddr,
+ sizeof(struct sockaddr), 0 );
+
+ if (rc != sizeof(struct sockaddr)) {
+ Debug( LDAP_DEBUG_ANY, "ber_write failed\n" );
+ return 1;
+ }
+
+ if (op->o_protocol == LDAP_VERSION2) {
+ rc = ber_printf(op->o_res_ber, "{is{" /*}}*/, op->o_msgid, "");
+ if (rc == -1) {
+ Debug( LDAP_DEBUG_ANY, "ber_write failed\n" );
+ return rc;
+ }
+ }
+ }
+#endif /* LDAP_CONNECTIONLESS */
+
+ rc = 0;
+
+ /* Don't process requests when the conn is in the middle of a
+ * Bind, or if it's closing. Also, don't let any single conn
+ * use up all the available threads, and don't execute if we're
+ * currently blocked on output. And don't execute if there are
+ * already pending ops, let them go first. Abandon operations
+ * get exceptions to some, but not all, cases.
+ */
+ switch( tag ){
+ default:
+ /* Abandon and Unbind are exempt from these checks */
+ if (conn->c_conn_state == SLAP_C_CLOSING) {
+ defer = "closing";
+ break;
+ } else if (conn->c_writewaiter) {
+ defer = "awaiting write";
+ break;
+ } else if (conn->c_n_ops_pending) {
+ defer = "pending operations";
+ break;
+ }
+ /* FALLTHRU */
+ case LDAP_REQ_ABANDON:
+ /* Unbind is exempt from these checks */
+ if (conn->c_n_ops_executing >= connection_pool_max/2) {
+ defer = "too many executing";
+ break;
+ } else if (conn->c_conn_state == SLAP_C_BINDING) {
+ defer = "binding";
+ break;
+ }
+ /* FALLTHRU */
+ case LDAP_REQ_UNBIND:
+ break;
+ }
+
+ if( defer ) {
+ int max = conn->c_dn.bv_len
+ ? slap_conn_max_pending_auth
+ : slap_conn_max_pending;
+
+ Debug( LDAP_DEBUG_ANY,
+ "connection_input: conn=%lu deferring operation: %s\n",
+ conn->c_connid, defer );
+ conn->c_n_ops_pending++;
+ LDAP_STAILQ_INSERT_TAIL( &conn->c_pending_ops, op, o_next );
+ rc = ( conn->c_n_ops_pending > max ) ? -1 : 0;
+
+ } else {
+ conn->c_n_ops_executing++;
+
+ /*
+ * The first op will be processed in the same thread context,
+ * as long as there is only one op total.
+ * Subsequent ops will be submitted to the pool by
+ * calling connection_op_activate()
+ */
+ if ( cri->op == NULL ) {
+ /* the first incoming request */
+ connection_op_queue( op );
+ cri->op = op;
+ } else {
+ if ( !cri->nullop ) {
+ cri->nullop = 1;
+ rc = ldap_pvt_thread_pool_submit( &connection_pool,
+ connection_operation, (void *) cri->op );
+ }
+ connection_op_activate( op );
+ }
+ }
+
+ return rc;
+}
+
+static int
+connection_resched( Connection *conn )
+{
+ Operation *op;
+
+ if( conn->c_writewaiter )
+ return 0;
+
+ if( conn->c_conn_state == SLAP_C_CLOSING ) {
+ Debug( LDAP_DEBUG_CONNS, "connection_resched: "
+ "attempting closing conn=%lu sd=%d\n",
+ conn->c_connid, conn->c_sd );
+ connection_close( conn );
+ return 0;
+ }
+
+ if( conn->c_conn_state != SLAP_C_ACTIVE ) {
+ /* other states need different handling */
+ return 0;
+ }
+
+ while ((op = LDAP_STAILQ_FIRST( &conn->c_pending_ops )) != NULL) {
+ if ( conn->c_n_ops_executing > connection_pool_max/2 ) break;
+
+ LDAP_STAILQ_REMOVE_HEAD( &conn->c_pending_ops, o_next );
+ LDAP_STAILQ_NEXT(op, o_next) = NULL;
+
+ /* pending operations should not be marked for abandonment */
+ assert(!op->o_abandon);
+
+ conn->c_n_ops_pending--;
+ conn->c_n_ops_executing++;
+
+ connection_op_activate( op );
+
+ if ( conn->c_conn_state == SLAP_C_BINDING ) break;
+ }
+ return 0;
+}
+
+static void
+connection_init_log_prefix( Operation *op )
+{
+ if ( op->o_connid == (unsigned long)(-1) ) {
+ snprintf( op->o_log_prefix, sizeof( op->o_log_prefix ),
+ "conn=-1 op=%lu", op->o_opid );
+
+ } else {
+ snprintf( op->o_log_prefix, sizeof( op->o_log_prefix ),
+ "conn=%lu op=%lu", op->o_connid, op->o_opid );
+ }
+}
+
+static int connection_bind_cleanup_cb( Operation *op, SlapReply *rs )
+{
+ op->o_conn->c_sasl_bindop = NULL;
+
+ ch_free( op->o_callback );
+ op->o_callback = NULL;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int connection_bind_cb( Operation *op, SlapReply *rs )
+{
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+ op->o_conn->c_sasl_bind_in_progress =
+ ( rs->sr_err == LDAP_SASL_BIND_IN_PROGRESS );
+
+ /* Moved here from bind.c due to ITS#4158 */
+ op->o_conn->c_sasl_bindop = NULL;
+ if ( op->orb_method == LDAP_AUTH_SASL ) {
+ if( rs->sr_err == LDAP_SUCCESS ) {
+ ber_dupbv(&op->o_conn->c_dn, &op->orb_edn);
+ if( !BER_BVISEMPTY( &op->orb_edn ) ) {
+ /* edn is always normalized already */
+ ber_dupbv( &op->o_conn->c_ndn, &op->o_conn->c_dn );
+ }
+ op->o_tmpfree( op->orb_edn.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &op->orb_edn );
+ op->o_conn->c_authmech = op->o_conn->c_sasl_bind_mech;
+ BER_BVZERO( &op->o_conn->c_sasl_bind_mech );
+
+ op->o_conn->c_sasl_ssf = op->orb_ssf;
+ if( op->orb_ssf > op->o_conn->c_ssf ) {
+ op->o_conn->c_ssf = op->orb_ssf;
+ }
+
+ if( !BER_BVISEMPTY( &op->o_conn->c_dn ) ) {
+ ber_len_t max = sockbuf_max_incoming_auth;
+ ber_sockbuf_ctrl( op->o_conn->c_sb,
+ LBER_SB_OPT_SET_MAX_INCOMING, &max );
+ }
+
+ /* log authorization identity */
+ Debug( LDAP_DEBUG_STATS,
+ "%s BIND dn=\"%s\" mech=%s bind_ssf=%d ssf=%d\n",
+ op->o_log_prefix,
+ BER_BVISNULL( &op->o_conn->c_dn ) ? "<empty>" : op->o_conn->c_dn.bv_val,
+ op->o_conn->c_authmech.bv_val,
+ op->orb_ssf, op->o_conn->c_ssf );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "do_bind: SASL/%s bind: dn=\"%s\" bind_ssf=%d\n",
+ op->o_conn->c_authmech.bv_val,
+ BER_BVISNULL( &op->o_conn->c_dn ) ? "<empty>" : op->o_conn->c_dn.bv_val,
+ op->orb_ssf );
+
+ } else if ( rs->sr_err != LDAP_SASL_BIND_IN_PROGRESS ) {
+ if ( !BER_BVISNULL( &op->o_conn->c_sasl_bind_mech ) ) {
+ free( op->o_conn->c_sasl_bind_mech.bv_val );
+ BER_BVZERO( &op->o_conn->c_sasl_bind_mech );
+ }
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+
+ ch_free( op->o_callback );
+ op->o_callback = NULL;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static void connection_op_queue( Operation *op )
+{
+ ber_tag_t tag = op->o_tag;
+
+ if (tag == LDAP_REQ_BIND) {
+ slap_callback *sc = ch_calloc( 1, sizeof( slap_callback ));
+ sc->sc_response = connection_bind_cb;
+ sc->sc_cleanup = connection_bind_cleanup_cb;
+ sc->sc_next = op->o_callback;
+ op->o_callback = sc;
+ op->o_conn->c_conn_state = SLAP_C_BINDING;
+ }
+
+ if (!op->o_dn.bv_len) {
+ op->o_authz = op->o_conn->c_authz;
+ if ( BER_BVISNULL( &op->o_conn->c_sasl_authz_dn )) {
+ ber_dupbv( &op->o_dn, &op->o_conn->c_dn );
+ ber_dupbv( &op->o_ndn, &op->o_conn->c_ndn );
+ } else {
+ ber_dupbv( &op->o_dn, &op->o_conn->c_sasl_authz_dn );
+ ber_dupbv( &op->o_ndn, &op->o_conn->c_sasl_authz_dn );
+ }
+ }
+
+ op->o_authtype = op->o_conn->c_authtype;
+ ber_dupbv( &op->o_authmech, &op->o_conn->c_authmech );
+
+ if (!op->o_protocol) {
+ op->o_protocol = op->o_conn->c_protocol
+ ? op->o_conn->c_protocol : LDAP_VERSION3;
+ }
+
+ if (op->o_conn->c_conn_state == SLAP_C_INACTIVE &&
+ op->o_protocol > LDAP_VERSION2)
+ {
+ op->o_conn->c_conn_state = SLAP_C_ACTIVE;
+ }
+
+ op->o_connid = op->o_conn->c_connid;
+ connection_init_log_prefix( op );
+
+ LDAP_STAILQ_INSERT_TAIL( &op->o_conn->c_ops, op, o_next );
+}
+
+static int connection_op_activate( Operation *op )
+{
+ int rc;
+
+ connection_op_queue( op );
+
+ rc = ldap_pvt_thread_pool_submit( &connection_pool,
+ connection_operation, (void *) op );
+
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "connection_op_activate: submit failed (%d) for conn=%lu\n",
+ rc, op->o_connid );
+ /* should move op to pending list */
+ }
+
+ return rc;
+}
+
+int connection_write(ber_socket_t s)
+{
+ Connection *c;
+ Operation *op;
+ int wantwrite;
+
+ assert( connections != NULL );
+
+ c = connection_get( s );
+ if( c == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "connection_write(%ld): no connection!\n",
+ (long)s );
+ return -1;
+ }
+
+ slapd_clr_write( s, 0 );
+
+#ifdef HAVE_TLS
+ if ( c->c_is_tls && c->c_needs_tls_accept ) {
+ connection_return( c );
+ connection_read_activate( s );
+ return 0;
+ }
+#endif
+
+ c->c_n_write++;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "connection_write(%d): waking output for id=%lu\n",
+ s, c->c_connid );
+
+ wantwrite = ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_WRITE, NULL );
+ if ( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_READ, NULL )) {
+ /* don't wakeup twice */
+ slapd_set_read( s, !wantwrite );
+ }
+ if ( wantwrite ) {
+ slapd_set_write( s, 1 );
+ }
+
+ /* If there are ops pending because of a writewaiter,
+ * start one up.
+ */
+ while ((op = LDAP_STAILQ_FIRST( &c->c_pending_ops )) != NULL) {
+ if ( !c->c_writewaiter ) break;
+ if ( c->c_n_ops_executing > connection_pool_max/2 ) break;
+
+ LDAP_STAILQ_REMOVE_HEAD( &c->c_pending_ops, o_next );
+ LDAP_STAILQ_NEXT(op, o_next) = NULL;
+
+ /* pending operations should not be marked for abandonment */
+ assert(!op->o_abandon);
+
+ c->c_n_ops_pending--;
+ c->c_n_ops_executing++;
+
+ connection_op_activate( op );
+
+ break;
+ }
+
+ connection_return( c );
+ return 0;
+}
+
+void connection_write_resume( Connection *c )
+{
+ Operation *op;
+
+ ldap_pvt_thread_mutex_lock( &c->c_mutex );
+ /* If there are ops pending because of a writewaiter,
+ * start one up.
+ */
+ while ((op = LDAP_STAILQ_FIRST( &c->c_pending_ops )) != NULL) {
+ if ( c->c_n_ops_executing > connection_pool_max/2 ) break;
+
+ LDAP_STAILQ_REMOVE_HEAD( &c->c_pending_ops, o_next );
+ LDAP_STAILQ_NEXT(op, o_next) = NULL;
+
+ /* pending operations should not be marked for abandonment */
+ assert(!op->o_abandon);
+
+ c->c_n_ops_pending--;
+ c->c_n_ops_executing++;
+
+ connection_op_activate( op );
+
+ break;
+ }
+
+ connection_return( c );
+}
+
+#ifdef LDAP_SLAPI
+typedef struct conn_fake_extblock {
+ void *eb_conn;
+ void *eb_op;
+} conn_fake_extblock;
+
+static void
+connection_fake_destroy(
+ void *key,
+ void *data )
+{
+ Connection conn = {0};
+ Operation op = {0};
+ Opheader ohdr = {0};
+
+ conn_fake_extblock *eb = data;
+
+ op.o_hdr = &ohdr;
+ op.o_hdr->oh_extensions = eb->eb_op;
+ conn.c_extensions = eb->eb_conn;
+ op.o_conn = &conn;
+ conn.c_connid = -1;
+ op.o_connid = -1;
+
+ ber_memfree_x( eb, NULL );
+ slapi_int_free_object_extensions( SLAPI_X_EXT_OPERATION, &op );
+ slapi_int_free_object_extensions( SLAPI_X_EXT_CONNECTION, &conn );
+}
+#endif
+
+void
+connection_fake_init(
+ Connection *conn,
+ OperationBuffer *opbuf,
+ void *ctx )
+{
+ connection_fake_init2( conn, opbuf, ctx, 1 );
+}
+
+void
+operation_fake_init(
+ Connection *conn,
+ Operation *op,
+ void *ctx,
+ int newmem )
+{
+ /* set memory context */
+ op->o_tmpmemctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx,
+ newmem );
+ op->o_tmpmfuncs = &slap_sl_mfuncs;
+ op->o_threadctx = ctx;
+ op->o_tid = ldap_pvt_thread_pool_tid( ctx );
+
+ op->o_counters = &slap_counters;
+ op->o_conn = conn;
+ op->o_connid = op->o_conn->c_connid;
+ connection_init_log_prefix( op );
+}
+
+
+void
+connection_fake_init2(
+ Connection *conn,
+ OperationBuffer *opbuf,
+ void *ctx,
+ int newmem )
+{
+ Operation *op = (Operation *) opbuf;
+
+ conn->c_connid = -1;
+ conn->c_conn_idx = -1;
+ conn->c_send_ldap_result = slap_send_ldap_result;
+ conn->c_send_search_entry = slap_send_search_entry;
+ conn->c_send_search_reference = slap_send_search_reference;
+ conn->c_send_ldap_extended = slap_send_ldap_extended;
+ conn->c_send_ldap_intermediate = slap_send_ldap_intermediate;
+ conn->c_listener = (Listener *)&dummy_list;
+ conn->c_peer_domain = slap_empty_bv;
+ conn->c_peer_name = slap_empty_bv;
+
+ memset( opbuf, 0, sizeof( *opbuf ));
+ op->o_hdr = &opbuf->ob_hdr;
+ op->o_controls = opbuf->ob_controls;
+
+ operation_fake_init( conn, op, ctx, newmem );
+
+#ifdef LDAP_SLAPI
+ if ( slapi_plugins_used ) {
+ conn_fake_extblock *eb;
+ void *ebx = NULL;
+
+ /* Use thread keys to make sure these eventually get cleaned up */
+ if ( ldap_pvt_thread_pool_getkey( ctx, (void *)connection_fake_init,
+ &ebx, NULL )) {
+ eb = ch_malloc( sizeof( *eb ));
+ slapi_int_create_object_extensions( SLAPI_X_EXT_CONNECTION, conn );
+ slapi_int_create_object_extensions( SLAPI_X_EXT_OPERATION, op );
+ eb->eb_conn = conn->c_extensions;
+ eb->eb_op = op->o_hdr->oh_extensions;
+ ldap_pvt_thread_pool_setkey( ctx, (void *)connection_fake_init,
+ eb, connection_fake_destroy, NULL, NULL );
+ } else {
+ eb = ebx;
+ conn->c_extensions = eb->eb_conn;
+ op->o_hdr->oh_extensions = eb->eb_op;
+ }
+ }
+#endif /* LDAP_SLAPI */
+
+ slap_op_time( &op->o_time, &op->o_tincr );
+}
+
+void
+connection_assign_nextid( Connection *conn )
+{
+ ldap_pvt_thread_mutex_lock( &conn_nextid_mutex );
+ conn->c_connid = conn_nextid++;
+ ldap_pvt_thread_mutex_unlock( &conn_nextid_mutex );
+}
diff --git a/servers/slapd/controls.c b/servers/slapd/controls.c
new file mode 100644
index 0000000..940ca3d
--- /dev/null
+++ b/servers/slapd/controls.c
@@ -0,0 +1,2228 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "ldif.h"
+#include "lutil.h"
+
+#include "../../libraries/liblber/lber-int.h"
+
+static SLAP_CTRL_PARSE_FN parseAssert;
+static SLAP_CTRL_PARSE_FN parseDomainScope;
+static SLAP_CTRL_PARSE_FN parseDontUseCopy;
+static SLAP_CTRL_PARSE_FN parseManageDSAit;
+static SLAP_CTRL_PARSE_FN parseNoOp;
+static SLAP_CTRL_PARSE_FN parsePagedResults;
+static SLAP_CTRL_PARSE_FN parsePermissiveModify;
+static SLAP_CTRL_PARSE_FN parsePreRead, parsePostRead;
+static SLAP_CTRL_PARSE_FN parseProxyAuthz;
+static SLAP_CTRL_PARSE_FN parseRelax;
+static SLAP_CTRL_PARSE_FN parseSearchOptions;
+#ifdef SLAP_CONTROL_X_SORTEDRESULTS
+static SLAP_CTRL_PARSE_FN parseSortedResults;
+#endif
+static SLAP_CTRL_PARSE_FN parseSubentries;
+#ifdef SLAP_CONTROL_X_TREE_DELETE
+static SLAP_CTRL_PARSE_FN parseTreeDelete;
+#endif
+static SLAP_CTRL_PARSE_FN parseValuesReturnFilter;
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+static SLAP_CTRL_PARSE_FN parseSessionTracking;
+#endif
+#ifdef SLAP_CONTROL_X_WHATFAILED
+static SLAP_CTRL_PARSE_FN parseWhatFailed;
+#endif
+#ifdef SLAP_CONTROL_X_LAZY_COMMIT
+static SLAP_CTRL_PARSE_FN parseLazyCommit;
+#endif
+
+#undef sc_mask /* avoid conflict with Irix 6.5 <sys/signal.h> */
+
+const struct berval slap_pre_read_bv = BER_BVC(LDAP_CONTROL_PRE_READ);
+const struct berval slap_post_read_bv = BER_BVC(LDAP_CONTROL_POST_READ);
+
+struct slap_control_ids slap_cids;
+
+struct slap_control {
+ /* Control OID */
+ char *sc_oid;
+
+ /* The controlID for this control */
+ int sc_cid;
+
+ /* Operations supported by control */
+ slap_mask_t sc_mask;
+
+ /* Extended operations supported by control */
+ char **sc_extendedops; /* input */
+ BerVarray sc_extendedopsbv; /* run-time use */
+
+ /* Control parsing callback */
+ SLAP_CTRL_PARSE_FN *sc_parse;
+
+ LDAP_SLIST_ENTRY(slap_control) sc_next;
+};
+
+static LDAP_SLIST_HEAD(ControlsList, slap_control) controls_list
+ = LDAP_SLIST_HEAD_INITIALIZER(&controls_list);
+
+/*
+ * all known request control OIDs should be added to this list
+ */
+/*
+ * NOTE: initialize num_known_controls to 1 so that cid = 0 always
+ * addresses an undefined control; this allows to safely test for
+ * well known controls even if they are not registered, e.g. if
+ * they get moved to modules. An example is sc_LDAPsync, which
+ * is implemented in the syncprov overlay and thus, if configured
+ * as dynamic module, may not be registered. One side effect is that
+ * slap_known_controls[0] == NULL, so it should always be used
+ * starting from 1.
+ * FIXME: should we define the "undefined control" oid?
+ */
+char *slap_known_controls[SLAP_MAX_CIDS+1];
+static int num_known_controls = 1;
+
+static char *proxy_authz_extops[] = {
+ LDAP_EXOP_MODIFY_PASSWD,
+ LDAP_EXOP_WHO_AM_I,
+ LDAP_EXOP_REFRESH,
+ NULL
+};
+
+static char *manageDSAit_extops[] = {
+ LDAP_EXOP_REFRESH,
+ NULL
+};
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+static char *session_tracking_extops[] = {
+ LDAP_EXOP_MODIFY_PASSWD,
+ LDAP_EXOP_WHO_AM_I,
+ LDAP_EXOP_REFRESH,
+ NULL
+};
+#endif
+
+static struct slap_control control_defs[] = {
+ { LDAP_CONTROL_ASSERT,
+ (int)offsetof(struct slap_control_ids, sc_assert),
+ SLAP_CTRL_UPDATE|SLAP_CTRL_COMPARE|SLAP_CTRL_SEARCH,
+ NULL, NULL,
+ parseAssert, LDAP_SLIST_ENTRY_INITIALIZER(next) },
+ { LDAP_CONTROL_PRE_READ,
+ (int)offsetof(struct slap_control_ids, sc_preRead),
+ SLAP_CTRL_DELETE|SLAP_CTRL_MODIFY|SLAP_CTRL_RENAME,
+ NULL, NULL,
+ parsePreRead, LDAP_SLIST_ENTRY_INITIALIZER(next) },
+ { LDAP_CONTROL_POST_READ,
+ (int)offsetof(struct slap_control_ids, sc_postRead),
+ SLAP_CTRL_ADD|SLAP_CTRL_MODIFY|SLAP_CTRL_RENAME,
+ NULL, NULL,
+ parsePostRead, LDAP_SLIST_ENTRY_INITIALIZER(next) },
+ { LDAP_CONTROL_VALUESRETURNFILTER,
+ (int)offsetof(struct slap_control_ids, sc_valuesReturnFilter),
+ SLAP_CTRL_GLOBAL|SLAP_CTRL_SEARCH,
+ NULL, NULL,
+ parseValuesReturnFilter, LDAP_SLIST_ENTRY_INITIALIZER(next) },
+ { LDAP_CONTROL_PAGEDRESULTS,
+ (int)offsetof(struct slap_control_ids, sc_pagedResults),
+ SLAP_CTRL_SEARCH,
+ NULL, NULL,
+ parsePagedResults, LDAP_SLIST_ENTRY_INITIALIZER(next) },
+#ifdef SLAP_CONTROL_X_SORTEDRESULTS
+ { LDAP_CONTROL_SORTREQUEST,
+ (int)offsetof(struct slap_control_ids, sc_sortedResults),
+ SLAP_CTRL_GLOBAL|SLAP_CTRL_SEARCH|SLAP_CTRL_HIDE,
+ NULL, NULL,
+ parseSortedResults, LDAP_SLIST_ENTRY_INITIALIZER(next) },
+#endif
+ { LDAP_CONTROL_X_DOMAIN_SCOPE,
+ (int)offsetof(struct slap_control_ids, sc_domainScope),
+ SLAP_CTRL_GLOBAL|SLAP_CTRL_SEARCH|SLAP_CTRL_HIDE,
+ NULL, NULL,
+ parseDomainScope, LDAP_SLIST_ENTRY_INITIALIZER(next) },
+ { LDAP_CONTROL_DONTUSECOPY,
+ (int)offsetof(struct slap_control_ids, sc_dontUseCopy),
+ SLAP_CTRL_GLOBAL|SLAP_CTRL_INTROGATE,
+ NULL, NULL,
+ parseDontUseCopy, LDAP_SLIST_ENTRY_INITIALIZER(next) },
+ { LDAP_CONTROL_X_PERMISSIVE_MODIFY,
+ (int)offsetof(struct slap_control_ids, sc_permissiveModify),
+ SLAP_CTRL_MODIFY|SLAP_CTRL_HIDE,
+ NULL, NULL,
+ parsePermissiveModify, LDAP_SLIST_ENTRY_INITIALIZER(next) },
+#ifdef SLAP_CONTROL_X_TREE_DELETE
+ { LDAP_CONTROL_X_TREE_DELETE,
+ (int)offsetof(struct slap_control_ids, sc_treeDelete),
+ SLAP_CTRL_DELETE|SLAP_CTRL_HIDE,
+ NULL, NULL,
+ parseTreeDelete, LDAP_SLIST_ENTRY_INITIALIZER(next) },
+#endif
+ { LDAP_CONTROL_X_SEARCH_OPTIONS,
+ (int)offsetof(struct slap_control_ids, sc_searchOptions),
+ SLAP_CTRL_GLOBAL|SLAP_CTRL_SEARCH|SLAP_CTRL_HIDE,
+ NULL, NULL,
+ parseSearchOptions, LDAP_SLIST_ENTRY_INITIALIZER(next) },
+ { LDAP_CONTROL_SUBENTRIES,
+ (int)offsetof(struct slap_control_ids, sc_subentries),
+ SLAP_CTRL_SEARCH,
+ NULL, NULL,
+ parseSubentries, LDAP_SLIST_ENTRY_INITIALIZER(next) },
+ { LDAP_CONTROL_NOOP,
+ (int)offsetof(struct slap_control_ids, sc_noOp),
+ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE,
+ NULL, NULL,
+ parseNoOp, LDAP_SLIST_ENTRY_INITIALIZER(next) },
+ { LDAP_CONTROL_RELAX,
+ (int)offsetof(struct slap_control_ids, sc_relax),
+ SLAP_CTRL_GLOBAL|SLAP_CTRL_UPDATE|SLAP_CTRL_HIDE,
+ NULL, NULL,
+ parseRelax, LDAP_SLIST_ENTRY_INITIALIZER(next) },
+ { LDAP_CONTROL_TXN_SPEC,
+ (int)offsetof(struct slap_control_ids, sc_txnSpec),
+ SLAP_CTRL_UPDATE|SLAP_CTRL_HIDE,
+ NULL, NULL,
+ txn_spec_ctrl, LDAP_SLIST_ENTRY_INITIALIZER(next) },
+ { LDAP_CONTROL_MANAGEDSAIT,
+ (int)offsetof(struct slap_control_ids, sc_manageDSAit),
+ SLAP_CTRL_ACCESS,
+ manageDSAit_extops, NULL,
+ parseManageDSAit, LDAP_SLIST_ENTRY_INITIALIZER(next) },
+ { LDAP_CONTROL_PROXY_AUTHZ,
+ (int)offsetof(struct slap_control_ids, sc_proxyAuthz),
+ SLAP_CTRL_GLOBAL|SLAP_CTRL_ACCESS,
+ proxy_authz_extops, NULL,
+ parseProxyAuthz, LDAP_SLIST_ENTRY_INITIALIZER(next) },
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ { LDAP_CONTROL_X_SESSION_TRACKING,
+ (int)offsetof(struct slap_control_ids, sc_sessionTracking),
+ SLAP_CTRL_GLOBAL|SLAP_CTRL_ACCESS|SLAP_CTRL_BIND|SLAP_CTRL_HIDE,
+ session_tracking_extops, NULL,
+ parseSessionTracking, LDAP_SLIST_ENTRY_INITIALIZER(next) },
+#endif
+#ifdef SLAP_CONTROL_X_WHATFAILED
+ { LDAP_CONTROL_X_WHATFAILED,
+ (int)offsetof(struct slap_control_ids, sc_whatFailed),
+ SLAP_CTRL_GLOBAL|SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE,
+ NULL, NULL,
+ parseWhatFailed, LDAP_SLIST_ENTRY_INITIALIZER(next) },
+#endif
+#ifdef SLAP_CONTROL_X_LAZY_COMMIT
+ { LDAP_CONTROL_X_LAZY_COMMIT,
+ (int)offsetof(struct slap_control_ids, sc_lazyCommit),
+ SLAP_CTRL_GLOBAL|SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE,
+ NULL, NULL,
+ parseLazyCommit, LDAP_SLIST_ENTRY_INITIALIZER(next) },
+#endif
+
+ { NULL, 0, 0, NULL, 0, NULL, LDAP_SLIST_ENTRY_INITIALIZER(next) }
+};
+
+static struct slap_control *
+find_ctrl( const char *oid );
+
+/*
+ * Register a supported control.
+ *
+ * This can be called by an OpenLDAP plugin or, indirectly, by a
+ * SLAPI plugin calling slapi_register_supported_control().
+ *
+ * NOTE: if flags == 1 the control is replaced if already registered;
+ * otherwise registering an already registered control is not allowed.
+ */
+int
+register_supported_control2(const char *controloid,
+ slap_mask_t controlmask,
+ char **controlexops,
+ SLAP_CTRL_PARSE_FN *controlparsefn,
+ unsigned flags,
+ int *controlcid)
+{
+ struct slap_control *sc = NULL;
+ int i;
+ BerVarray extendedopsbv = NULL;
+
+ if ( num_known_controls >= SLAP_MAX_CIDS ) {
+ Debug( LDAP_DEBUG_ANY, "Too many controls registered."
+ " Recompile slapd with SLAP_MAX_CIDS defined > %d\n",
+ num_known_controls );
+ return LDAP_OTHER;
+ }
+
+ if ( controloid == NULL ) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ /* check if already registered */
+ for ( i = 0; slap_known_controls[ i ]; i++ ) {
+ if ( strcmp( controloid, slap_known_controls[ i ] ) == 0 ) {
+ if ( flags == 1 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "Control %s already registered; replacing.\n",
+ controloid );
+ /* (find and) replace existing handler */
+ sc = find_ctrl( controloid );
+ assert( sc != NULL );
+ break;
+ }
+
+ Debug( LDAP_DEBUG_ANY,
+ "Control %s already registered.\n",
+ controloid );
+ return LDAP_PARAM_ERROR;
+ }
+ }
+
+ /* turn compatible extended operations into bervals */
+ if ( controlexops != NULL ) {
+ int i;
+
+ for ( i = 0; controlexops[ i ]; i++ );
+
+ extendedopsbv = ber_memcalloc( i + 1, sizeof( struct berval ) );
+ if ( extendedopsbv == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+
+ for ( i = 0; controlexops[ i ]; i++ ) {
+ ber_str2bv( controlexops[ i ], 0, 1, &extendedopsbv[ i ] );
+ }
+ }
+
+ if ( sc == NULL ) {
+ sc = (struct slap_control *)SLAP_MALLOC( sizeof( *sc ) );
+ if ( sc == NULL ) {
+ ber_bvarray_free( extendedopsbv );
+ return LDAP_NO_MEMORY;
+ }
+
+ sc->sc_oid = ch_strdup( controloid );
+ sc->sc_cid = num_known_controls;
+
+ /* Update slap_known_controls, too. */
+ slap_known_controls[num_known_controls - 1] = sc->sc_oid;
+ slap_known_controls[num_known_controls++] = NULL;
+
+ LDAP_SLIST_NEXT( sc, sc_next ) = NULL;
+ LDAP_SLIST_INSERT_HEAD( &controls_list, sc, sc_next );
+
+ } else {
+ if ( sc->sc_extendedopsbv ) {
+ ber_bvarray_free( sc->sc_extendedopsbv );
+ sc->sc_extendedopsbv = NULL;
+ sc->sc_extendedops = NULL;
+ }
+ }
+
+ sc->sc_extendedopsbv = extendedopsbv;
+ sc->sc_mask = controlmask;
+ sc->sc_parse = controlparsefn;
+ if ( controlcid ) {
+ *controlcid = sc->sc_cid;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+#ifdef SLAP_CONFIG_DELETE
+int
+unregister_supported_control( const char *controloid )
+{
+ struct slap_control *sc;
+ int i;
+
+ if ( controloid == NULL || (sc = find_ctrl( controloid )) == NULL ){
+ return -1;
+ }
+
+ for ( i = 0; slap_known_controls[ i ]; i++ ) {
+ if ( strcmp( controloid, slap_known_controls[ i ] ) == 0 ) {
+ do {
+ slap_known_controls[ i ] = slap_known_controls[ i+1 ];
+ } while ( slap_known_controls[ i++ ] );
+ num_known_controls--;
+ break;
+ }
+ }
+
+ LDAP_SLIST_REMOVE(&controls_list, sc, slap_control, sc_next);
+ ch_free( sc->sc_oid );
+ if ( sc->sc_extendedopsbv != NULL ) {
+ ber_bvarray_free( sc->sc_extendedopsbv );
+ }
+ ch_free( sc );
+
+ return 0;
+}
+#endif /* SLAP_CONFIG_DELETE */
+
+int
+register_control_exop( const char *controloid, char *exopoid )
+{
+ struct slap_control *sc = NULL;
+ BerVarray extendedopsbv;
+ char **extendedops;
+ int i;
+
+ if ( controloid == NULL || exopoid == NULL ) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ for ( i = 0; slap_known_controls[ i ]; i++ ) {
+ if ( strcmp( controloid, slap_known_controls[ i ] ) == 0 ) {
+ sc = find_ctrl( controloid );
+ assert( sc != NULL );
+ break;
+ }
+ }
+
+ if ( !sc ) {
+ Debug( LDAP_DEBUG_ANY, "register_control_exop: "
+ "Control %s not registered.\n",
+ controloid );
+ return LDAP_PARAM_ERROR;
+ }
+
+ for ( i = 0; sc->sc_extendedopsbv && !BER_BVISNULL( &sc->sc_extendedopsbv[ i ] ); i++ ) {
+ if ( strcmp( exopoid, sc->sc_extendedopsbv[ i ].bv_val ) == 0 ) {
+ return LDAP_SUCCESS;
+ }
+ }
+
+ extendedopsbv = ber_memrealloc( sc->sc_extendedopsbv, (i + 2) * sizeof( struct berval ) );
+ if ( extendedopsbv == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+ sc->sc_extendedopsbv = extendedopsbv;
+
+ ber_str2bv( exopoid, 0, 1, &extendedopsbv[ i ] );
+ BER_BVZERO( &extendedopsbv[ i+1 ] );
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * One-time initialization of internal controls.
+ */
+int
+slap_controls_init( void )
+{
+ int i, rc;
+
+ rc = LDAP_SUCCESS;
+
+ for ( i = 0; control_defs[i].sc_oid != NULL; i++ ) {
+ int *cid = (int *)(((char *)&slap_cids) + control_defs[i].sc_cid );
+ rc = register_supported_control( control_defs[i].sc_oid,
+ control_defs[i].sc_mask, control_defs[i].sc_extendedops,
+ control_defs[i].sc_parse, cid );
+ if ( rc != LDAP_SUCCESS ) break;
+ }
+
+ return rc;
+}
+
+/*
+ * Free memory associated with list of supported controls.
+ */
+void
+controls_destroy( void )
+{
+ struct slap_control *sc;
+
+ while ( !LDAP_SLIST_EMPTY(&controls_list) ) {
+ sc = LDAP_SLIST_FIRST(&controls_list);
+ LDAP_SLIST_REMOVE_HEAD(&controls_list, sc_next);
+
+ ch_free( sc->sc_oid );
+ if ( sc->sc_extendedopsbv != NULL ) {
+ ber_bvarray_free( sc->sc_extendedopsbv );
+ }
+ ch_free( sc );
+ }
+}
+
+/*
+ * Format the supportedControl attribute of the root DSE,
+ * detailing which controls are supported by the directory
+ * server.
+ */
+int
+controls_root_dse_info( Entry *e )
+{
+ AttributeDescription *ad_supportedControl
+ = slap_schema.si_ad_supportedControl;
+ struct berval vals[2];
+ struct slap_control *sc;
+
+ vals[1].bv_val = NULL;
+ vals[1].bv_len = 0;
+
+ LDAP_SLIST_FOREACH( sc, &controls_list, sc_next ) {
+ if( sc->sc_mask & SLAP_CTRL_HIDE ) continue;
+
+ vals[0].bv_val = sc->sc_oid;
+ vals[0].bv_len = strlen( sc->sc_oid );
+
+ if ( attr_merge( e, ad_supportedControl, vals, NULL ) ) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Return a list of OIDs and operation masks for supported
+ * controls. Used by SLAPI.
+ */
+int
+get_supported_controls(char ***ctrloidsp,
+ slap_mask_t **ctrlmasks)
+{
+ int n;
+ char **oids;
+ slap_mask_t *masks;
+ struct slap_control *sc;
+
+ n = 0;
+
+ LDAP_SLIST_FOREACH( sc, &controls_list, sc_next ) {
+ n++;
+ }
+
+ if ( n == 0 ) {
+ *ctrloidsp = NULL;
+ *ctrlmasks = NULL;
+ return LDAP_SUCCESS;
+ }
+
+ oids = (char **)SLAP_MALLOC( (n + 1) * sizeof(char *) );
+ if ( oids == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+ masks = (slap_mask_t *)SLAP_MALLOC( (n + 1) * sizeof(slap_mask_t) );
+ if ( masks == NULL ) {
+ SLAP_FREE( oids );
+ return LDAP_NO_MEMORY;
+ }
+
+ n = 0;
+
+ LDAP_SLIST_FOREACH( sc, &controls_list, sc_next ) {
+ oids[n] = ch_strdup( sc->sc_oid );
+ masks[n] = sc->sc_mask;
+ n++;
+ }
+ oids[n] = NULL;
+ masks[n] = 0;
+
+ *ctrloidsp = oids;
+ *ctrlmasks = masks;
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Find a control given its OID.
+ */
+static struct slap_control *
+find_ctrl( const char *oid )
+{
+ struct slap_control *sc;
+
+ LDAP_SLIST_FOREACH( sc, &controls_list, sc_next ) {
+ if ( strcmp( oid, sc->sc_oid ) == 0 ) {
+ return sc;
+ }
+ }
+
+ return NULL;
+}
+
+int
+slap_find_control_id(
+ const char *oid,
+ int *cid )
+{
+ struct slap_control *ctrl = find_ctrl( oid );
+ if ( ctrl ) {
+ if ( cid ) *cid = ctrl->sc_cid;
+ return LDAP_SUCCESS;
+ }
+ return LDAP_CONTROL_NOT_FOUND;
+}
+
+int
+slap_global_control( Operation *op, const char *oid, int *cid )
+{
+ struct slap_control *ctrl = find_ctrl( oid );
+
+ if ( ctrl == NULL ) {
+ /* should not be reachable */
+ Debug( LDAP_DEBUG_ANY,
+ "slap_global_control: unrecognized control: %s\n",
+ oid );
+ return LDAP_CONTROL_NOT_FOUND;
+ }
+
+ if ( cid ) *cid = ctrl->sc_cid;
+
+ if ( ( ctrl->sc_mask & SLAP_CTRL_GLOBAL ) ||
+ ( ( op->o_tag & LDAP_REQ_SEARCH ) &&
+ ( ctrl->sc_mask & SLAP_CTRL_GLOBAL_SEARCH ) ) )
+ {
+ return LDAP_COMPARE_TRUE;
+ }
+
+#if 0
+ Debug( LDAP_DEBUG_TRACE,
+ "slap_global_control: unavailable control: %s\n",
+ oid );
+#endif
+
+ return LDAP_COMPARE_FALSE;
+}
+
+void slap_free_ctrls(
+ Operation *op,
+ LDAPControl **ctrls )
+{
+ int i;
+
+ if( ctrls == op->o_ctrls ) {
+ if( op->o_assertion != NULL ) {
+ filter_free_x( op, op->o_assertion, 1 );
+ op->o_assertion = NULL;
+ }
+ if( op->o_vrFilter != NULL) {
+ vrFilter_free( op, op->o_vrFilter );
+ op->o_vrFilter = NULL;
+ }
+ if( op->o_preread_attrs != NULL ) {
+ op->o_tmpfree( op->o_preread_attrs, op->o_tmpmemctx );
+ op->o_preread_attrs = NULL;
+ }
+ if( op->o_postread_attrs != NULL ) {
+ op->o_tmpfree( op->o_postread_attrs, op->o_tmpmemctx );
+ op->o_postread_attrs = NULL;
+ }
+ if( op->o_pagedresults_state != NULL ) {
+ op->o_tmpfree( op->o_pagedresults_state, op->o_tmpmemctx );
+ op->o_pagedresults_state = NULL;
+ }
+ }
+
+ for (i=0; ctrls[i]; i++) {
+ op->o_tmpfree(ctrls[i], op->o_tmpmemctx );
+ }
+ op->o_tmpfree( ctrls, op->o_tmpmemctx );
+}
+
+int slap_add_ctrls(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl **ctrls )
+{
+ int i = 0, j;
+ LDAPControl **ctrlsp;
+
+ if ( rs->sr_ctrls ) {
+ for ( ; rs->sr_ctrls[ i ]; i++ ) ;
+ }
+
+ for ( j=0; ctrls[j]; j++ ) ;
+
+ ctrlsp = op->o_tmpalloc(( i+j+1 )*sizeof(LDAPControl *), op->o_tmpmemctx );
+ i = 0;
+ if ( rs->sr_ctrls ) {
+ for ( ; rs->sr_ctrls[i]; i++ )
+ ctrlsp[i] = rs->sr_ctrls[i];
+ }
+ for ( j=0; ctrls[j]; j++)
+ ctrlsp[i++] = ctrls[j];
+ ctrlsp[i] = NULL;
+
+ if ( rs->sr_flags & REP_CTRLS_MUSTBEFREED )
+ op->o_tmpfree( rs->sr_ctrls, op->o_tmpmemctx );
+ rs->sr_ctrls = ctrlsp;
+ rs->sr_flags |= REP_CTRLS_MUSTBEFREED;
+ return i;
+}
+
+int slap_parse_ctrl(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *control,
+ const char **text )
+{
+ struct slap_control *sc;
+ int rc = LDAP_SUCCESS;
+
+ sc = find_ctrl( control->ldctl_oid );
+ if( sc != NULL ) {
+ /* recognized control */
+ slap_mask_t tagmask;
+ switch( op->o_tag ) {
+ case LDAP_REQ_ADD:
+ tagmask = SLAP_CTRL_ADD;
+ break;
+ case LDAP_REQ_BIND:
+ tagmask = SLAP_CTRL_BIND;
+ break;
+ case LDAP_REQ_COMPARE:
+ tagmask = SLAP_CTRL_COMPARE;
+ break;
+ case LDAP_REQ_DELETE:
+ tagmask = SLAP_CTRL_DELETE;
+ break;
+ case LDAP_REQ_MODIFY:
+ tagmask = SLAP_CTRL_MODIFY;
+ break;
+ case LDAP_REQ_RENAME:
+ tagmask = SLAP_CTRL_RENAME;
+ break;
+ case LDAP_REQ_SEARCH:
+ tagmask = SLAP_CTRL_SEARCH;
+ break;
+ case LDAP_REQ_UNBIND:
+ tagmask = SLAP_CTRL_UNBIND;
+ break;
+ case LDAP_REQ_ABANDON:
+ tagmask = SLAP_CTRL_ABANDON;
+ break;
+ case LDAP_REQ_EXTENDED:
+ tagmask=~0L;
+ assert( op->ore_reqoid.bv_val != NULL );
+ if( sc->sc_extendedopsbv != NULL ) {
+ int i;
+ for( i=0; !BER_BVISNULL( &sc->sc_extendedopsbv[i] ); i++ ) {
+ if( bvmatch( &op->ore_reqoid,
+ &sc->sc_extendedopsbv[i] ) )
+ {
+ tagmask=0L;
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ *text = "controls internal error";
+ return LDAP_OTHER;
+ }
+
+ if (( sc->sc_mask & tagmask ) == tagmask ) {
+ /* available extension */
+ if ( sc->sc_parse ) {
+ rc = sc->sc_parse( op, rs, control );
+ assert( rc != LDAP_UNAVAILABLE_CRITICAL_EXTENSION );
+
+ } else if ( control->ldctl_iscritical ) {
+ *text = "not yet implemented";
+ rc = LDAP_OTHER;
+ }
+
+
+ } else if ( control->ldctl_iscritical ) {
+ /* unavailable CRITICAL control */
+ *text = "critical extension is unavailable";
+ rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
+ }
+
+ } else if ( control->ldctl_iscritical ) {
+ /* unrecognized CRITICAL control */
+ *text = "critical extension is not recognized";
+ rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
+ }
+
+ return rc;
+}
+
+int
+get_ctrls(
+ Operation *op,
+ SlapReply *rs,
+ int sendres )
+{
+ return get_ctrls2( op, rs, sendres, LDAP_TAG_CONTROLS );
+}
+
+int
+get_ctrls2(
+ Operation *op,
+ SlapReply *rs,
+ int sendres,
+ ber_tag_t ctag )
+{
+ int nctrls = 0;
+ ber_tag_t tag;
+ ber_len_t len;
+ char *opaque;
+ BerElement *ber = op->o_ber;
+ struct berval bv;
+#ifdef SLAP_CONTROL_X_WHATFAILED
+ /* NOTE: right now, slapd checks the validity of each control
+ * while parsing. As a consequence, it can only detect one
+ * cause of failure at a time. This results in returning
+ * exactly one OID with the whatFailed control, or no control
+ * at all.
+ */
+ char *failed_oid = NULL;
+#endif
+
+ len = ber_pvt_ber_remaining(ber);
+
+ if( len == 0) {
+ /* no controls */
+ rs->sr_err = LDAP_SUCCESS;
+ return rs->sr_err;
+ }
+
+ if(( tag = ber_peek_tag( ber, &len )) != ctag ) {
+ if( tag == LBER_ERROR ) {
+ rs->sr_err = SLAPD_DISCONNECT;
+ rs->sr_text = "unexpected data in PDU";
+ }
+
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> get_ctrls\n" );
+
+ if( op->o_protocol < LDAP_VERSION3 ) {
+ rs->sr_err = SLAPD_DISCONNECT;
+ rs->sr_text = "controls require LDAPv3";
+ goto return_results;
+ }
+
+ /* one for first control, one for termination */
+ op->o_ctrls = op->o_tmpalloc( 2 * sizeof(LDAPControl *), op->o_tmpmemctx );
+
+#if 0
+ if( op->ctrls == NULL ) {
+ rs->sr_err = LDAP_NO_MEMORY;
+ rs->sr_text = "no memory";
+ goto return_results;
+ }
+#endif
+
+ op->o_ctrls[nctrls] = NULL;
+
+ /* step through each element */
+ for( tag = ber_first_element( ber, &len, &opaque );
+ tag != LBER_ERROR;
+ tag = ber_next_element( ber, &len, opaque ) )
+ {
+ LDAPControl *c;
+ LDAPControl **tctrls;
+
+ c = op->o_tmpalloc( sizeof(LDAPControl), op->o_tmpmemctx );
+ memset(c, 0, sizeof(LDAPControl));
+
+ /* allocate pointer space for current controls (nctrls)
+ * + this control + extra NULL
+ */
+ tctrls = op->o_tmprealloc( op->o_ctrls,
+ (nctrls+2) * sizeof(LDAPControl *), op->o_tmpmemctx );
+
+#if 0
+ if( tctrls == NULL ) {
+ ch_free( c );
+ ldap_controls_free(op->o_ctrls);
+ op->o_ctrls = NULL;
+
+ rs->sr_err = LDAP_NO_MEMORY;
+ rs->sr_text = "no memory";
+ goto return_results;
+ }
+#endif
+ op->o_ctrls = tctrls;
+
+ op->o_ctrls[nctrls++] = c;
+ op->o_ctrls[nctrls] = NULL;
+
+ tag = ber_scanf( ber, "{m" /*}*/, &bv );
+ c->ldctl_oid = bv.bv_val;
+
+ if( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: get oid failed.\n" );
+
+ slap_free_ctrls( op, op->o_ctrls );
+ op->o_ctrls = NULL;
+ rs->sr_err = SLAPD_DISCONNECT;
+ rs->sr_text = "decoding controls error";
+ goto return_results;
+
+ } else if( c->ldctl_oid == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "get_ctrls: conn %lu got empty OID.\n",
+ op->o_connid );
+
+ slap_free_ctrls( op, op->o_ctrls );
+ op->o_ctrls = NULL;
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ rs->sr_text = "OID field is empty";
+ goto return_results;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+
+ if( tag == LBER_BOOLEAN ) {
+ ber_int_t crit;
+ tag = ber_scanf( ber, "b", &crit );
+
+ if( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: get crit failed.\n" );
+ slap_free_ctrls( op, op->o_ctrls );
+ op->o_ctrls = NULL;
+ rs->sr_err = SLAPD_DISCONNECT;
+ rs->sr_text = "decoding controls error";
+ goto return_results;
+ }
+
+ c->ldctl_iscritical = (crit != 0);
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if( tag == LBER_OCTETSTRING ) {
+ tag = ber_scanf( ber, "m", &c->ldctl_value );
+
+ if( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "=> get_ctrls: conn %lu: "
+ "%s (%scritical): get value failed.\n",
+ op->o_connid, c->ldctl_oid,
+ c->ldctl_iscritical ? "" : "non" );
+ slap_free_ctrls( op, op->o_ctrls );
+ op->o_ctrls = NULL;
+ rs->sr_err = SLAPD_DISCONNECT;
+ rs->sr_text = "decoding controls error";
+ goto return_results;
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> get_ctrls: oid=\"%s\" (%scritical)\n",
+ c->ldctl_oid, c->ldctl_iscritical ? "" : "non" );
+
+ rs->sr_err = slap_parse_ctrl( op, rs, c, &rs->sr_text );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+#ifdef SLAP_CONTROL_X_WHATFAILED
+ failed_oid = c->ldctl_oid;
+#endif
+ goto return_results;
+ }
+ }
+
+return_results:
+ Debug( LDAP_DEBUG_TRACE,
+ "<= get_ctrls: n=%d rc=%d err=\"%s\"\n",
+ nctrls, rs->sr_err, rs->sr_text ? rs->sr_text : "");
+
+ if( sendres && rs->sr_err != LDAP_SUCCESS ) {
+ if( rs->sr_err == SLAPD_DISCONNECT ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ send_ldap_disconnect( op, rs );
+ rs->sr_err = SLAPD_DISCONNECT;
+ } else {
+#ifdef SLAP_CONTROL_X_WHATFAILED
+ /* might have not been parsed yet? */
+ if ( failed_oid != NULL ) {
+ if ( !get_whatFailed( op ) ) {
+ /* look it up */
+
+ /* step through each remaining element */
+ for ( ; tag != LBER_ERROR; tag = ber_next_element( ber, &len, opaque ) )
+ {
+ LDAPControl c = { 0 };
+
+ tag = ber_scanf( ber, "{m" /*}*/, &bv );
+ c.ldctl_oid = bv.bv_val;
+
+ if ( tag == LBER_ERROR ) {
+ slap_free_ctrls( op, op->o_ctrls );
+ op->o_ctrls = NULL;
+ break;
+
+ } else if ( c.ldctl_oid == NULL ) {
+ slap_free_ctrls( op, op->o_ctrls );
+ op->o_ctrls = NULL;
+ break;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LBER_BOOLEAN ) {
+ ber_int_t crit;
+ tag = ber_scanf( ber, "b", &crit );
+ if( tag == LBER_ERROR ) {
+ slap_free_ctrls( op, op->o_ctrls );
+ op->o_ctrls = NULL;
+ break;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag == LBER_OCTETSTRING ) {
+ tag = ber_scanf( ber, "m", &c.ldctl_value );
+
+ if( tag == LBER_ERROR ) {
+ slap_free_ctrls( op, op->o_ctrls );
+ op->o_ctrls = NULL;
+ break;
+ }
+ }
+
+ if ( strcmp( c.ldctl_oid, LDAP_CONTROL_X_WHATFAILED ) == 0 ) {
+ const char *text;
+ slap_parse_ctrl( op, rs, &c, &text );
+ break;
+ }
+ }
+ }
+
+ if ( get_whatFailed( op ) ) {
+ char *oids[ 2 ];
+ oids[ 0 ] = failed_oid;
+ oids[ 1 ] = NULL;
+ slap_ctrl_whatFailed_add( op, rs, oids );
+ }
+ }
+#endif
+
+ send_ldap_result( op, rs );
+ }
+ }
+
+ return rs->sr_err;
+}
+
+int
+slap_remove_control(
+ Operation *op,
+ SlapReply *rs,
+ int ctrl,
+ BI_chk_controls fnc )
+{
+ int i, j;
+
+ switch ( op->o_ctrlflag[ ctrl ] ) {
+ case SLAP_CONTROL_NONCRITICAL:
+ for ( i = 0, j = -1; op->o_ctrls[ i ] != NULL; i++ ) {
+ if ( strcmp( op->o_ctrls[ i ]->ldctl_oid,
+ slap_known_controls[ ctrl - 1 ] ) == 0 )
+ {
+ j = i;
+ }
+ }
+
+ if ( j == -1 ) {
+ rs->sr_err = LDAP_OTHER;
+ break;
+ }
+
+ if ( fnc ) {
+ (void)fnc( op, rs );
+ }
+
+ op->o_tmpfree( op->o_ctrls[ j ], op->o_tmpmemctx );
+
+ if ( i > 1 ) {
+ AC_MEMCPY( &op->o_ctrls[ j ], &op->o_ctrls[ j + 1 ],
+ ( i - j ) * sizeof( LDAPControl * ) );
+
+ } else {
+ op->o_tmpfree( op->o_ctrls, op->o_tmpmemctx );
+ op->o_ctrls = NULL;
+ }
+
+ op->o_ctrlflag[ ctrl ] = SLAP_CONTROL_IGNORED;
+
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "non-critical control \"%s\" not supported; stripped.\n",
+ op->o_log_prefix, slap_known_controls[ ctrl ] );
+ /* fall thru */
+
+ case SLAP_CONTROL_IGNORED:
+ case SLAP_CONTROL_NONE:
+ rs->sr_err = SLAP_CB_CONTINUE;
+ break;
+
+ case SLAP_CONTROL_CRITICAL:
+ rs->sr_err = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
+ if ( fnc ) {
+ (void)fnc( op, rs );
+ }
+ Debug( LDAP_DEBUG_ANY, "%s: "
+ "critical control \"%s\" not supported.\n",
+ op->o_log_prefix, slap_known_controls[ ctrl ] );
+ break;
+
+ default:
+ /* handle all cases! */
+ assert( 0 );
+ }
+
+ return rs->sr_err;
+}
+
+static int parseDontUseCopy (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ if ( op->o_dontUseCopy != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "dontUseCopy control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( !BER_BVISNULL( &ctrl->ldctl_value )) {
+ rs->sr_text = "dontUseCopy control value not absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( ( global_disallows & SLAP_DISALLOW_DONTUSECOPY_N_CRIT )
+ && !ctrl->ldctl_iscritical )
+ {
+ rs->sr_text = "dontUseCopy criticality of FALSE not allowed";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ op->o_dontUseCopy = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ return LDAP_SUCCESS;
+}
+
+static int parseRelax (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ if ( op->o_relax != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "relax control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( !BER_BVISNULL( &ctrl->ldctl_value )) {
+ rs->sr_text = "relax control value not absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ op->o_relax = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ return LDAP_SUCCESS;
+}
+
+static int parseManageDSAit (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ if ( op->o_managedsait != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "manageDSAit control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( !BER_BVISNULL( &ctrl->ldctl_value )) {
+ rs->sr_text = "manageDSAit control value not absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ op->o_managedsait = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ return LDAP_SUCCESS;
+}
+
+static int parseProxyAuthz (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ int rc;
+ struct berval dn = BER_BVNULL;
+
+ if ( op->o_proxy_authz != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "proxy authorization control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISNULL( &ctrl->ldctl_value )) {
+ rs->sr_text = "proxy authorization control value absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( ( global_disallows & SLAP_DISALLOW_PROXY_AUTHZ_N_CRIT )
+ && !ctrl->ldctl_iscritical )
+ {
+ rs->sr_text = "proxied authorization criticality of FALSE not allowed";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( !( global_allows & SLAP_ALLOW_PROXY_AUTHZ_ANON )
+ && BER_BVISEMPTY( &op->o_ndn ) )
+ {
+ rs->sr_text = "anonymous proxied authorization not allowed";
+ return LDAP_PROXIED_AUTHORIZATION_DENIED;
+ }
+
+ op->o_proxy_authz = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ Debug( LDAP_DEBUG_ARGS,
+ "parseProxyAuthz: conn %lu authzid=\"%s\"\n",
+ op->o_connid,
+ ctrl->ldctl_value.bv_len ? ctrl->ldctl_value.bv_val : "anonymous" );
+
+ if ( BER_BVISEMPTY( &ctrl->ldctl_value )) {
+ Debug( LDAP_DEBUG_TRACE,
+ "parseProxyAuthz: conn=%lu anonymous\n",
+ op->o_connid );
+
+ /* anonymous */
+ if ( !BER_BVISNULL( &op->o_ndn ) ) {
+ op->o_ndn.bv_val[ 0 ] = '\0';
+ }
+ op->o_ndn.bv_len = 0;
+
+ if ( !BER_BVISNULL( &op->o_dn ) ) {
+ op->o_dn.bv_val[ 0 ] = '\0';
+ }
+ op->o_dn.bv_len = 0;
+
+ return LDAP_SUCCESS;
+ }
+
+ rc = slap_sasl_getdn( op->o_conn, op, &ctrl->ldctl_value,
+ NULL, &dn, SLAP_GETDN_AUTHZID );
+
+ /* FIXME: empty DN in proxyAuthz control should be legal... */
+ if( rc != LDAP_SUCCESS /* || !dn.bv_len */ ) {
+ if ( dn.bv_val ) {
+ ch_free( dn.bv_val );
+ }
+ rs->sr_text = "authzId mapping failed";
+ return LDAP_PROXIED_AUTHORIZATION_DENIED;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "parseProxyAuthz: conn=%lu \"%s\"\n",
+ op->o_connid,
+ dn.bv_len ? dn.bv_val : "(NULL)" );
+
+ rc = slap_sasl_authorized( op, &op->o_ndn, &dn );
+
+ if ( rc ) {
+ ch_free( dn.bv_val );
+ rs->sr_text = "not authorized to assume identity";
+ return LDAP_PROXIED_AUTHORIZATION_DENIED;
+ }
+
+ ch_free( op->o_ndn.bv_val );
+
+ /*
+ * NOTE: since slap_sasl_getdn() returns a normalized dn,
+ * from now on op->o_dn is normalized
+ */
+ op->o_ndn = dn;
+ ber_bvreplace( &op->o_dn, &dn );
+
+ Debug( LDAP_DEBUG_STATS, "%s PROXYAUTHZ dn=\"%s\"\n",
+ op->o_log_prefix, dn.bv_val );
+
+ return LDAP_SUCCESS;
+}
+
+static int parseNoOp (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ if ( op->o_noop != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "noop control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "noop control value not empty";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ op->o_noop = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ return LDAP_SUCCESS;
+}
+
+static int parsePagedResults (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ struct berval cookie;
+ PagedResultsState *ps;
+ int rc = LDAP_SUCCESS;
+ ber_tag_t tag;
+ ber_int_t size;
+
+ if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "paged results control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "paged results control value is absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "paged results control value is empty";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ /* Parse the control value
+ * realSearchControlValue ::= SEQUENCE {
+ * size INTEGER (0..maxInt),
+ * -- requested page size from client
+ * -- result set size estimate from server
+ * cookie OCTET STRING
+ * }
+ */
+ ber_init2( ber, &ctrl->ldctl_value, LBER_USE_DER );
+
+ tag = ber_scanf( ber, "{im}", &size, &cookie );
+
+ if ( tag == LBER_ERROR ) {
+ rs->sr_text = "paged results control could not be decoded";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ if ( size < 0 ) {
+ rs->sr_text = "paged results control size invalid";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ ps = op->o_tmpalloc( sizeof(PagedResultsState), op->o_tmpmemctx );
+ *ps = op->o_conn->c_pagedresults_state;
+ ps->ps_size = size;
+ ps->ps_cookieval = cookie;
+ op->o_pagedresults_state = ps;
+ if ( !cookie.bv_len ) {
+ ps->ps_count = 0;
+ ps->ps_cookie = 0;
+ /* taint ps_cookie, to detect whether it's set */
+ op->o_conn->c_pagedresults_state.ps_cookie = NOID;
+ }
+
+ /* NOTE: according to RFC 2696 3.:
+
+ If the page size is greater than or equal to the sizeLimit value, the
+ server should ignore the control as the request can be satisfied in a
+ single page.
+
+ * NOTE: this assumes that the op->ors_slimit be set
+ * before the controls are parsed.
+ */
+
+ if ( op->ors_slimit > 0 && size >= op->ors_slimit ) {
+ op->o_pagedresults = SLAP_CONTROL_IGNORED;
+
+ } else if ( ctrl->ldctl_iscritical ) {
+ op->o_pagedresults = SLAP_CONTROL_CRITICAL;
+
+ } else {
+ op->o_pagedresults = SLAP_CONTROL_NONCRITICAL;
+ }
+
+done:;
+ return rc;
+}
+
+#ifdef SLAP_CONTROL_X_SORTEDRESULTS
+static int parseSortedResults (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ int rc = LDAP_SUCCESS;
+
+ if ( op->o_sortedresults != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "sorted results control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "sorted results control value is absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "sorted results control value is empty";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ /* blow off parsing the value */
+
+ op->o_sortedresults = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ return rc;
+}
+#endif
+
+static int parseAssert (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ BerElement *ber;
+ struct berval fstr = BER_BVNULL;
+
+ if ( op->o_assert != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "assert control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISNULL( &ctrl->ldctl_value )) {
+ rs->sr_text = "assert control value is absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISEMPTY( &ctrl->ldctl_value )) {
+ rs->sr_text = "assert control value is empty";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ ber = ber_init( &(ctrl->ldctl_value) );
+ if (ber == NULL) {
+ rs->sr_text = "assert control: internal error";
+ return LDAP_OTHER;
+ }
+
+ rs->sr_err = get_filter( op, ber, (Filter **)&(op->o_assertion),
+ &rs->sr_text);
+ (void) ber_free( ber, 1 );
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ if( rs->sr_err == SLAPD_DISCONNECT ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ send_ldap_disconnect( op, rs );
+ rs->sr_err = SLAPD_DISCONNECT;
+ } else {
+ send_ldap_result( op, rs );
+ }
+ if( op->o_assertion != NULL ) {
+ filter_free_x( op, op->o_assertion, 1 );
+ op->o_assertion = NULL;
+ }
+ return rs->sr_err;
+ }
+
+#ifdef LDAP_DEBUG
+ filter2bv_x( op, op->o_assertion, &fstr );
+
+ Debug( LDAP_DEBUG_ARGS, "parseAssert: conn %ld assert: %s\n",
+ op->o_connid, fstr.bv_len ? fstr.bv_val : "empty" );
+ op->o_tmpfree( fstr.bv_val, op->o_tmpmemctx );
+#endif
+
+ op->o_assert = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ rs->sr_err = LDAP_SUCCESS;
+ return LDAP_SUCCESS;
+}
+
+#define READMSG(post, msg) \
+ ( post ? "postread control: " msg : "preread control: " msg )
+
+static int
+parseReadAttrs(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl,
+ int post )
+{
+ ber_len_t siz, off, i;
+ BerElement *ber;
+ AttributeName *an = NULL;
+
+ if ( ( post && op->o_postread != SLAP_CONTROL_NONE ) ||
+ ( !post && op->o_preread != SLAP_CONTROL_NONE ) )
+ {
+ rs->sr_text = READMSG( post, "specified multiple times" );
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
+ rs->sr_text = READMSG( post, "value is absent" );
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
+ rs->sr_text = READMSG( post, "value is empty" );
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( op->o_txnSpec ) { /* temporary limitation */
+ rs->sr_text = READMSG( post, "cannot perform in transaction" );
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ ber = ber_init( &ctrl->ldctl_value );
+ if ( ber == NULL ) {
+ rs->sr_text = READMSG( post, "internal error" );
+ return LDAP_OTHER;
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+ siz = sizeof( AttributeName );
+ off = offsetof( AttributeName, an_name );
+ if ( ber_scanf( ber, "{M}", &an, &siz, off ) == LBER_ERROR ) {
+ rs->sr_text = READMSG( post, "decoding error" );
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ for ( i = 0; i < siz; i++ ) {
+ const char *dummy = NULL;
+ int rc;
+
+ an[i].an_desc = NULL;
+ an[i].an_oc = NULL;
+ an[i].an_flags = 0;
+ rc = slap_bv2ad( &an[i].an_name, &an[i].an_desc, &dummy );
+ if ( rc == LDAP_SUCCESS ) {
+ an[i].an_name = an[i].an_desc->ad_cname;
+
+ } else {
+ int j;
+ static struct berval special_attrs[] = {
+ BER_BVC( LDAP_NO_ATTRS ),
+ BER_BVC( LDAP_ALL_USER_ATTRIBUTES ),
+ BER_BVC( LDAP_ALL_OPERATIONAL_ATTRIBUTES ),
+ BER_BVNULL
+ };
+
+ /* deal with special attribute types */
+ for ( j = 0; !BER_BVISNULL( &special_attrs[ j ] ); j++ ) {
+ if ( bvmatch( &an[i].an_name, &special_attrs[ j ] ) ) {
+ an[i].an_name = special_attrs[ j ];
+ break;
+ }
+ }
+
+ if ( BER_BVISNULL( &special_attrs[ j ] ) && ctrl->ldctl_iscritical ) {
+ rs->sr_err = rc;
+ rs->sr_text = dummy ? dummy
+ : READMSG( post, "unknown attributeType" );
+ goto done;
+ }
+ }
+ }
+
+ if ( post ) {
+ op->o_postread_attrs = an;
+ op->o_postread = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+ } else {
+ op->o_preread_attrs = an;
+ op->o_preread = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+ }
+
+done:
+ (void) ber_free( ber, 1 );
+ return rs->sr_err;
+}
+
+static int parsePreRead (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ return parseReadAttrs( op, rs, ctrl, 0 );
+}
+
+static int parsePostRead (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ return parseReadAttrs( op, rs, ctrl, 1 );
+}
+
+static int parseValuesReturnFilter (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ BerElement *ber;
+ struct berval fstr = BER_BVNULL;
+
+ if ( op->o_valuesreturnfilter != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "valuesReturnFilter control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISNULL( &ctrl->ldctl_value )) {
+ rs->sr_text = "valuesReturnFilter control value is absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISEMPTY( &ctrl->ldctl_value )) {
+ rs->sr_text = "valuesReturnFilter control value is empty";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ ber = ber_init( &(ctrl->ldctl_value) );
+ if (ber == NULL) {
+ rs->sr_text = "internal error";
+ return LDAP_OTHER;
+ }
+
+ rs->sr_err = get_vrFilter( op, ber,
+ (ValuesReturnFilter **)&(op->o_vrFilter), &rs->sr_text);
+
+ (void) ber_free( ber, 1 );
+
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ if( rs->sr_err == SLAPD_DISCONNECT ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ send_ldap_disconnect( op, rs );
+ rs->sr_err = SLAPD_DISCONNECT;
+ } else {
+ send_ldap_result( op, rs );
+ }
+ if( op->o_vrFilter != NULL) {
+ vrFilter_free( op, op->o_vrFilter );
+ op->o_vrFilter = NULL;
+ }
+ }
+#ifdef LDAP_DEBUG
+ else {
+ vrFilter2bv( op, op->o_vrFilter, &fstr );
+ }
+
+ Debug( LDAP_DEBUG_ARGS, " vrFilter: %s\n",
+ fstr.bv_len ? fstr.bv_val : "empty" );
+ op->o_tmpfree( fstr.bv_val, op->o_tmpmemctx );
+#endif
+
+ op->o_valuesreturnfilter = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ rs->sr_err = LDAP_SUCCESS;
+ return LDAP_SUCCESS;
+}
+
+static int parseSubentries (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ if ( op->o_subentries != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "subentries control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ /* FIXME: should use BER library */
+ if( ( ctrl->ldctl_value.bv_len != 3 )
+ || ( ctrl->ldctl_value.bv_val[0] != 0x01 )
+ || ( ctrl->ldctl_value.bv_val[1] != 0x01 ))
+ {
+ rs->sr_text = "subentries control value encoding is bogus";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ op->o_subentries = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ if (ctrl->ldctl_value.bv_val[2]) {
+ set_subentries_visibility( op );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int parsePermissiveModify (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ if ( op->o_permissive_modify != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "permissiveModify control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( !BER_BVISNULL( &ctrl->ldctl_value )) {
+ rs->sr_text = "permissiveModify control value not absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ op->o_permissive_modify = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ return LDAP_SUCCESS;
+}
+
+static int parseDomainScope (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ if ( op->o_domain_scope != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "domainScope control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ /* this should be checking BVISNULL, but M$ clients are broken
+ * and include the value even though the M$ spec says it must be
+ * omitted. ITS#9100.
+ */
+ if ( !BER_BVISEMPTY( &ctrl->ldctl_value )) {
+ rs->sr_text = "domainScope control value not absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ op->o_domain_scope = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ return LDAP_SUCCESS;
+}
+
+#ifdef SLAP_CONTROL_X_TREE_DELETE
+static int parseTreeDelete (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ if ( op->o_tree_delete != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "treeDelete control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( !BER_BVISNULL( &ctrl->ldctl_value )) {
+ rs->sr_text = "treeDelete control value not absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ op->o_tree_delete = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ return LDAP_SUCCESS;
+}
+#endif
+
+static int parseSearchOptions (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ BerElement *ber;
+ ber_int_t search_flags;
+ ber_tag_t tag;
+
+ if ( BER_BVISNULL( &ctrl->ldctl_value )) {
+ rs->sr_text = "searchOptions control value is absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISEMPTY( &ctrl->ldctl_value )) {
+ rs->sr_text = "searchOptions control value is empty";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ ber = ber_init( &ctrl->ldctl_value );
+ if( ber == NULL ) {
+ rs->sr_text = "internal error";
+ return LDAP_OTHER;
+ }
+
+ tag = ber_scanf( ber, "{i}", &search_flags );
+ (void) ber_free( ber, 1 );
+
+ if ( tag == LBER_ERROR ) {
+ rs->sr_text = "searchOptions control decoding error";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( search_flags & ~(LDAP_SEARCH_FLAG_DOMAIN_SCOPE) ) {
+ /* Search flags not recognised so far,
+ * including:
+ * LDAP_SEARCH_FLAG_PHANTOM_ROOT
+ */
+ if ( ctrl->ldctl_iscritical ) {
+ rs->sr_text = "searchOptions contained unrecognized flag";
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ /* Ignore */
+ Debug( LDAP_DEBUG_TRACE,
+ "searchOptions: conn=%lu unrecognized flag(s) 0x%x (non-critical)\n",
+ op->o_connid, (unsigned)search_flags );
+
+ return LDAP_SUCCESS;
+ }
+
+ if ( search_flags & LDAP_SEARCH_FLAG_DOMAIN_SCOPE ) {
+ if ( op->o_domain_scope != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "searchOptions control specified multiple times "
+ "or with domainScope control";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ op->o_domain_scope = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+struct berval session_tracking_formats[] = {
+ BER_BVC( LDAP_CONTROL_X_SESSION_TRACKING_RADIUS_ACCT_SESSION_ID ),
+ BER_BVC( "RADIUS-Acct-Session-Id" ),
+ BER_BVC( LDAP_CONTROL_X_SESSION_TRACKING_RADIUS_ACCT_MULTI_SESSION_ID ),
+ BER_BVC( "RADIUS-Acct-Multi-Session-Id" ),
+ BER_BVC( LDAP_CONTROL_X_SESSION_TRACKING_USERNAME ),
+ BER_BVC( "USERNAME" ),
+
+ BER_BVNULL
+};
+
+static int parseSessionTracking(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ BerElement *ber;
+ ber_tag_t tag;
+ ber_len_t len;
+ int i, rc;
+
+ struct berval sessionSourceIp = BER_BVNULL,
+ sessionSourceName = BER_BVNULL,
+ formatOID = BER_BVNULL,
+ sessionTrackingIdentifier = BER_BVNULL;
+
+ size_t st_len, st_pos;
+
+ if ( ctrl->ldctl_iscritical ) {
+ rs->sr_text = "sessionTracking criticality is TRUE";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "sessionTracking control value is absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "sessionTracking control value is empty";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ /* TODO: add the capability to determine if a client is allowed
+ * to use this control, based on identity, ip and so */
+
+ ber = ber_init( &ctrl->ldctl_value );
+ if ( ber == NULL ) {
+ rs->sr_text = "internal error";
+ return LDAP_OTHER;
+ }
+
+ tag = ber_skip_tag( ber, &len );
+ if ( tag != LBER_SEQUENCE ) {
+ tag = LBER_ERROR;
+ goto error;
+ }
+
+ /* sessionSourceIp */
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LBER_DEFAULT ) {
+ tag = LBER_ERROR;
+ goto error;
+ }
+
+ if ( len == 0 ) {
+ tag = ber_skip_tag( ber, &len );
+
+ } else if ( len > 128 ) {
+ rs->sr_text = "sessionTracking.sessionSourceIp too long";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto error;
+
+ } else {
+ tag = ber_scanf( ber, "m", &sessionSourceIp );
+ }
+
+ if ( ldif_is_not_printable( sessionSourceIp.bv_val, sessionSourceIp.bv_len ) ) {
+ BER_BVZERO( &sessionSourceIp );
+ }
+
+ /* sessionSourceName */
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LBER_DEFAULT ) {
+ tag = LBER_ERROR;
+ goto error;
+ }
+
+ if ( len == 0 ) {
+ tag = ber_skip_tag( ber, &len );
+
+ } else if ( len > 65536 ) {
+ rs->sr_text = "sessionTracking.sessionSourceName too long";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto error;
+
+ } else {
+ tag = ber_scanf( ber, "m", &sessionSourceName );
+ }
+
+ if ( ldif_is_not_printable( sessionSourceName.bv_val, sessionSourceName.bv_len ) ) {
+ BER_BVZERO( &sessionSourceName );
+ }
+
+ /* formatOID */
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LBER_DEFAULT ) {
+ tag = LBER_ERROR;
+ goto error;
+ }
+
+ if ( len == 0 ) {
+ rs->sr_text = "sessionTracking.formatOID empty";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto error;
+
+ } else if ( len > 1024 ) {
+ rs->sr_text = "sessionTracking.formatOID too long";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto error;
+
+ } else {
+ tag = ber_scanf( ber, "m", &formatOID );
+ }
+
+ rc = numericoidValidate( NULL, &formatOID );
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_text = "sessionTracking.formatOID invalid";
+ goto error;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &session_tracking_formats[ i ] ); i += 2 )
+ {
+ if ( bvmatch( &formatOID, &session_tracking_formats[ i ] ) ) {
+ formatOID = session_tracking_formats[ i + 1 ];
+ break;
+ }
+ }
+
+ /* sessionTrackingIdentifier */
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LBER_DEFAULT ) {
+ tag = LBER_ERROR;
+ goto error;
+ }
+
+ if ( len == 0 ) {
+ tag = ber_skip_tag( ber, &len );
+
+ } else {
+ /* note: should not be more than 65536... */
+ tag = ber_scanf( ber, "m", &sessionTrackingIdentifier );
+ if ( ldif_is_not_printable( sessionTrackingIdentifier.bv_val, sessionTrackingIdentifier.bv_len ) ) {
+ /* we want the OID printed, at least */
+ BER_BVSTR( &sessionTrackingIdentifier, "" );
+ }
+ }
+
+ /* closure */
+ tag = ber_skip_tag( ber, &len );
+ if ( tag != LBER_DEFAULT || len != 0 ) {
+ tag = LBER_ERROR;
+ goto error;
+ }
+ tag = 0;
+
+ st_len = 0;
+ if ( !BER_BVISNULL( &sessionSourceIp ) ) {
+ st_len += STRLENOF( "IP=" ) + sessionSourceIp.bv_len;
+ }
+ if ( !BER_BVISNULL( &sessionSourceName ) ) {
+ if ( st_len ) st_len++;
+ st_len += STRLENOF( "NAME=" ) + sessionSourceName.bv_len;
+ }
+ if ( !BER_BVISNULL( &sessionTrackingIdentifier ) ) {
+ if ( st_len ) st_len++;
+ st_len += formatOID.bv_len + STRLENOF( "=" )
+ + sessionTrackingIdentifier.bv_len;
+ }
+
+ if ( st_len == 0 ) {
+ goto error;
+ }
+
+ st_len += STRLENOF( " []" );
+ st_pos = strlen( op->o_log_prefix );
+
+ if ( sizeof( op->o_log_prefix ) - st_pos > st_len ) {
+ char *ptr = &op->o_log_prefix[ st_pos ];
+
+ ptr = lutil_strcopy( ptr, " [" /*]*/ );
+
+ st_len = 0;
+ if ( !BER_BVISNULL( &sessionSourceIp ) ) {
+ ptr = lutil_strcopy( ptr, "IP=" );
+ ptr = lutil_strcopy( ptr, sessionSourceIp.bv_val );
+ st_len++;
+ }
+
+ if ( !BER_BVISNULL( &sessionSourceName ) ) {
+ if ( st_len ) *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, "NAME=" );
+ ptr = lutil_strcopy( ptr, sessionSourceName.bv_val );
+ st_len++;
+ }
+
+ if ( !BER_BVISNULL( &sessionTrackingIdentifier ) ) {
+ if ( st_len ) *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, formatOID.bv_val );
+ *ptr++ = '=';
+ ptr = lutil_strcopy( ptr, sessionTrackingIdentifier.bv_val );
+ }
+
+ *ptr++ = /*[*/ ']';
+ *ptr = '\0';
+ }
+
+error:;
+ (void)ber_free( ber, 1 );
+
+ if ( tag == LBER_ERROR ) {
+ rs->sr_text = "sessionTracking control decoding error";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+
+ return rs->sr_err;
+}
+
+int
+slap_ctrl_session_tracking_add(
+ Operation *op,
+ SlapReply *rs,
+ struct berval *ip,
+ struct berval *name,
+ struct berval *id,
+ LDAPControl *ctrl )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+
+ static struct berval oid = BER_BVC( LDAP_CONTROL_X_SESSION_TRACKING_USERNAME );
+
+ assert( ctrl != NULL );
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+
+ ber_printf( ber, "{OOOO}", ip, name, &oid, id );
+
+ if ( ber_flatten2( ber, &ctrl->ldctl_value, 0 ) == -1 ) {
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+
+ ctrl->ldctl_oid = LDAP_CONTROL_X_SESSION_TRACKING;
+ ctrl->ldctl_iscritical = 0;
+
+ rs->sr_err = LDAP_SUCCESS;
+
+done:;
+ return rs->sr_err;
+}
+
+int
+slap_ctrl_session_tracking_request_add( Operation *op, SlapReply *rs, LDAPControl *ctrl )
+{
+ static struct berval bv_unknown = BER_BVC( SLAP_STRING_UNKNOWN );
+ struct berval ip = BER_BVNULL,
+ name = BER_BVNULL,
+ id = BER_BVNULL;
+
+ if ( !BER_BVISNULL( &op->o_conn->c_peer_name ) &&
+ memcmp( op->o_conn->c_peer_name.bv_val, "IP=", STRLENOF( "IP=" ) ) == 0 )
+ {
+ char *ptr;
+
+ ip.bv_val = op->o_conn->c_peer_name.bv_val + STRLENOF( "IP=" );
+ ip.bv_len = op->o_conn->c_peer_name.bv_len - STRLENOF( "IP=" );
+
+ ptr = ber_bvchr( &ip, ':' );
+ if ( ptr ) {
+ ip.bv_len = ptr - ip.bv_val;
+ }
+ }
+
+ if ( !BER_BVISNULL( &op->o_conn->c_peer_domain ) &&
+ !bvmatch( &op->o_conn->c_peer_domain, &bv_unknown ) )
+ {
+ name = op->o_conn->c_peer_domain;
+ }
+
+ if ( !BER_BVISNULL( &op->o_dn ) && !BER_BVISEMPTY( &op->o_dn ) ) {
+ id = op->o_dn;
+ }
+
+ return slap_ctrl_session_tracking_add( op, rs, &ip, &name, &id, ctrl );
+}
+#endif
+
+#ifdef SLAP_CONTROL_X_WHATFAILED
+static int parseWhatFailed(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ if ( op->o_whatFailed != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "\"WHat Failed?\" control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( !BER_BVISNULL( &ctrl->ldctl_value )) {
+ rs->sr_text = "\"What Failed?\" control value not absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ op->o_whatFailed = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ return LDAP_SUCCESS;
+}
+
+int
+slap_ctrl_whatFailed_add(
+ Operation *op,
+ SlapReply *rs,
+ char **oids )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *) &berbuf;
+ LDAPControl **ctrls = NULL;
+ struct berval ctrlval;
+ int i, rc = LDAP_SUCCESS;
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+ ber_printf( ber, "[" /*]*/ );
+ for ( i = 0; oids[ i ] != NULL; i++ ) {
+ ber_printf( ber, "s", oids[ i ] );
+ }
+ ber_printf( ber, /*[*/ "]" );
+
+ if ( ber_flatten2( ber, &ctrlval, 0 ) == -1 ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ i = 0;
+ if ( rs->sr_ctrls != NULL ) {
+ for ( ; rs->sr_ctrls[ i ] != NULL; i++ ) {
+ if ( strcmp( rs->sr_ctrls[ i ]->ldctl_oid, LDAP_CONTROL_X_WHATFAILED ) != 0 ) {
+ /* TODO: add */
+ assert( 0 );
+ }
+ }
+ }
+
+ ctrls = op->o_tmprealloc( rs->sr_ctrls,
+ sizeof(LDAPControl *)*( i + 2 )
+ + sizeof(LDAPControl)
+ + ctrlval.bv_len + 1,
+ op->o_tmpmemctx );
+ if ( ctrls == NULL ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ ctrls[ i + 1 ] = NULL;
+ ctrls[ i ] = (LDAPControl *)&ctrls[ i + 2 ];
+ ctrls[ i ]->ldctl_oid = LDAP_CONTROL_X_WHATFAILED;
+ ctrls[ i ]->ldctl_iscritical = 0;
+ ctrls[ i ]->ldctl_value.bv_val = (char *)&ctrls[ i ][ 1 ];
+ AC_MEMCPY( ctrls[ i ]->ldctl_value.bv_val, ctrlval.bv_val, ctrlval.bv_len + 1 );
+ ctrls[ i ]->ldctl_value.bv_len = ctrlval.bv_len;
+
+ ber_free_buf( ber );
+
+ rs->sr_ctrls = ctrls;
+
+done:;
+ return rc;
+}
+#endif
+
+#ifdef SLAP_CONTROL_X_LAZY_COMMIT
+static int parseLazyCommit(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ if ( op->o_lazyCommit != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "\"Lazy Commit?\" control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( !BER_BVISNULL( &ctrl->ldctl_value )) {
+ rs->sr_text = "\"Lazy Commit?\" control value not absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ op->o_lazyCommit = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ return LDAP_SUCCESS;
+}
+#endif
diff --git a/servers/slapd/cr.c b/servers/slapd/cr.c
new file mode 100644
index 0000000..a828aa8
--- /dev/null
+++ b/servers/slapd/cr.c
@@ -0,0 +1,501 @@
+/* cr.c - content rule routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+struct cindexrec {
+ struct berval cir_name;
+ ContentRule *cir_cr;
+};
+
+static Avlnode *cr_index = NULL;
+static LDAP_STAILQ_HEAD(CRList, ContentRule) cr_list
+ = LDAP_STAILQ_HEAD_INITIALIZER(cr_list);
+
+static int
+cr_index_cmp(
+ const void *v_cir1,
+ const void *v_cir2 )
+{
+ const struct cindexrec *cir1 = v_cir1;
+ const struct cindexrec *cir2 = v_cir2;
+ int i = cir1->cir_name.bv_len - cir2->cir_name.bv_len;
+ if (i) return i;
+ return strcasecmp( cir1->cir_name.bv_val, cir2->cir_name.bv_val );
+}
+
+static int
+cr_index_name_cmp(
+ const void *v_name,
+ const void *v_cir )
+{
+ const struct berval *name = v_name;
+ const struct cindexrec *cir = v_cir;
+ int i = name->bv_len - cir->cir_name.bv_len;
+ if (i) return i;
+ return strncasecmp( name->bv_val, cir->cir_name.bv_val, name->bv_len );
+}
+
+ContentRule *
+cr_find( const char *crname )
+{
+ struct berval bv;
+
+ bv.bv_val = (char *)crname;
+ bv.bv_len = strlen( crname );
+
+ return( cr_bvfind( &bv ) );
+}
+
+ContentRule *
+cr_bvfind( struct berval *crname )
+{
+ struct cindexrec *cir;
+
+ cir = ldap_avl_find( cr_index, crname, cr_index_name_cmp );
+
+ if ( cir != NULL ) {
+ return( cir->cir_cr );
+ }
+
+ return( NULL );
+}
+
+static int
+cr_destroy_one( ContentRule *c )
+{
+ assert( c != NULL );
+
+ if (c->scr_auxiliaries) ldap_memfree(c->scr_auxiliaries);
+ if (c->scr_required) ldap_memfree(c->scr_required);
+ if (c->scr_allowed) ldap_memfree(c->scr_allowed);
+ if (c->scr_precluded) ldap_memfree(c->scr_precluded);
+ ldap_contentrule_free((LDAPContentRule *)c);
+
+ return 0;
+}
+
+void
+cr_destroy( void )
+{
+ ContentRule *c;
+
+ ldap_avl_free(cr_index, ldap_memfree);
+
+ while( !LDAP_STAILQ_EMPTY(&cr_list) ) {
+ c = LDAP_STAILQ_FIRST(&cr_list);
+ LDAP_STAILQ_REMOVE_HEAD(&cr_list, scr_next);
+
+ cr_destroy_one( c );
+ }
+}
+
+static int
+cr_insert(
+ ContentRule *scr,
+ const char **err
+)
+{
+ struct cindexrec *cir;
+ char **names;
+
+ assert( scr != NULL );
+
+ if ( scr->scr_oid ) {
+ cir = (struct cindexrec *)
+ ch_calloc( 1, sizeof(struct cindexrec) );
+ cir->cir_name.bv_val = scr->scr_oid;
+ cir->cir_name.bv_len = strlen( scr->scr_oid );
+ cir->cir_cr = scr;
+
+ if ( ldap_avl_insert( &cr_index, (caddr_t) cir,
+ cr_index_cmp, ldap_avl_dup_error ) )
+ {
+ *err = scr->scr_oid;
+ ldap_memfree(cir);
+ return SLAP_SCHERR_CR_DUP;
+ }
+
+ /* FIX: temporal consistency check */
+ assert( cr_bvfind(&cir->cir_name) != NULL );
+ }
+
+ if ( (names = scr->scr_names) ) {
+ while ( *names ) {
+ cir = (struct cindexrec *)
+ ch_calloc( 1, sizeof(struct cindexrec) );
+ cir->cir_name.bv_val = *names;
+ cir->cir_name.bv_len = strlen( *names );
+ cir->cir_cr = scr;
+
+ if ( ldap_avl_insert( &cr_index, (caddr_t) cir,
+ cr_index_cmp, ldap_avl_dup_error ) )
+ {
+ *err = *names;
+ ldap_memfree(cir);
+ return SLAP_SCHERR_CR_DUP;
+ }
+
+ /* FIX: temporal consistency check */
+ assert( cr_bvfind(&cir->cir_name) != NULL );
+
+ names++;
+ }
+ }
+
+ LDAP_STAILQ_INSERT_TAIL(&cr_list, scr, scr_next);
+
+ return 0;
+}
+
+static int
+cr_add_auxiliaries(
+ ContentRule *scr,
+ int *op,
+ const char **err )
+{
+ int naux;
+
+ if( scr->scr_oc_oids_aux == NULL ) return 0;
+
+ for( naux=0; scr->scr_oc_oids_aux[naux]; naux++ ) {
+ /* count them */ ;
+ }
+
+ scr->scr_auxiliaries = ch_calloc( naux+1, sizeof(ObjectClass *));
+
+ for( naux=0; scr->scr_oc_oids_aux[naux]; naux++ ) {
+ ObjectClass *soc = scr->scr_auxiliaries[naux]
+ = oc_find(scr->scr_oc_oids_aux[naux]);
+ if ( !soc ) {
+ *err = scr->scr_oc_oids_aux[naux];
+ return SLAP_SCHERR_CLASS_NOT_FOUND;
+ }
+
+ if( soc->soc_flags & SLAP_OC_OPERATIONAL &&
+ soc != slap_schema.si_oc_extensibleObject )
+ {
+ (*op)++;
+ }
+
+ if( soc->soc_kind != LDAP_SCHEMA_AUXILIARY ) {
+ *err = scr->scr_oc_oids_aux[naux];
+ return SLAP_SCHERR_CR_BAD_AUX;
+ }
+ }
+
+ scr->scr_auxiliaries[naux] = NULL;
+ return 0;
+}
+
+static int
+cr_create_required(
+ ContentRule *scr,
+ int *op,
+ const char **err )
+{
+ char **attrs = scr->scr_at_oids_must;
+ char **attrs1;
+ AttributeType *sat;
+
+ if ( attrs ) {
+ attrs1 = attrs;
+ while ( *attrs1 ) {
+ sat = at_find(*attrs1);
+ if ( !sat ) {
+ *err = *attrs1;
+ return SLAP_SCHERR_ATTR_NOT_FOUND;
+ }
+
+ if( is_at_operational( sat )) (*op)++;
+
+ if ( at_find_in_list(sat, scr->scr_required) < 0) {
+ if ( at_append_to_list(sat, &scr->scr_required) ) {
+ *err = *attrs1;
+ return SLAP_SCHERR_OUTOFMEM;
+ }
+ } else {
+ *err = *attrs1;
+ return SLAP_SCHERR_CR_BAD_AT;
+ }
+ attrs1++;
+ }
+ }
+ return 0;
+}
+
+static int
+cr_create_allowed(
+ ContentRule *scr,
+ int *op,
+ const char **err )
+{
+ char **attrs = scr->scr_at_oids_may;
+ char **attrs1;
+ AttributeType *sat;
+
+ if ( attrs ) {
+ attrs1 = attrs;
+ while ( *attrs1 ) {
+ sat = at_find(*attrs1);
+ if ( !sat ) {
+ *err = *attrs1;
+ return SLAP_SCHERR_ATTR_NOT_FOUND;
+ }
+
+ if( is_at_operational( sat )) (*op)++;
+
+ if ( at_find_in_list(sat, scr->scr_required) < 0 &&
+ at_find_in_list(sat, scr->scr_allowed) < 0 )
+ {
+ if ( at_append_to_list(sat, &scr->scr_allowed) ) {
+ *err = *attrs1;
+ return SLAP_SCHERR_OUTOFMEM;
+ }
+ } else {
+ *err = *attrs1;
+ return SLAP_SCHERR_CR_BAD_AT;
+ }
+ attrs1++;
+ }
+ }
+ return 0;
+}
+
+static int
+cr_create_precluded(
+ ContentRule *scr,
+ int *op,
+ const char **err )
+{
+ char **attrs = scr->scr_at_oids_not;
+ char **attrs1;
+ AttributeType *sat;
+
+ if ( attrs ) {
+ attrs1 = attrs;
+ while ( *attrs1 ) {
+ sat = at_find(*attrs1);
+ if ( !sat ) {
+ *err = *attrs1;
+ return SLAP_SCHERR_ATTR_NOT_FOUND;
+ }
+
+ if( is_at_operational( sat )) (*op)++;
+
+ /* FIXME: should also make sure attribute type is not
+ a required attribute of the structural class or
+ any auxiliary class */
+ if ( at_find_in_list(sat, scr->scr_required) < 0 &&
+ at_find_in_list(sat, scr->scr_allowed) < 0 &&
+ at_find_in_list(sat, scr->scr_precluded) < 0 )
+ {
+ if ( at_append_to_list(sat, &scr->scr_precluded) ) {
+ *err = *attrs1;
+ return SLAP_SCHERR_OUTOFMEM;
+ }
+ } else {
+ *err = *attrs1;
+ return SLAP_SCHERR_CR_BAD_AT;
+ }
+ attrs1++;
+ }
+ }
+ return 0;
+}
+
+int
+cr_add(
+ LDAPContentRule *cr,
+ int user,
+ ContentRule **rscr,
+ const char **err
+)
+{
+ ContentRule *scr;
+ int code;
+ int op = 0;
+ char *oidm = NULL;
+
+ if ( cr->cr_names != NULL ) {
+ int i;
+
+ for( i=0; cr->cr_names[i]; i++ ) {
+ if( !slap_valid_descr( cr->cr_names[i] ) ) {
+ return SLAP_SCHERR_BAD_DESCR;
+ }
+ }
+ }
+
+ if ( !OID_LEADCHAR( cr->cr_oid[0] )) {
+ /* Expand OID macros */
+ char *oid = oidm_find( cr->cr_oid );
+ if ( !oid ) {
+ *err = cr->cr_oid;
+ return SLAP_SCHERR_OIDM;
+ }
+ if ( oid != cr->cr_oid ) {
+ oidm = cr->cr_oid;
+ cr->cr_oid = oid;
+ }
+ }
+
+ scr = (ContentRule *) ch_calloc( 1, sizeof(ContentRule) );
+ AC_MEMCPY( &scr->scr_crule, cr, sizeof(LDAPContentRule) );
+
+ scr->scr_oidmacro = oidm;
+ scr->scr_sclass = oc_find(cr->cr_oid);
+ if ( !scr->scr_sclass ) {
+ *err = cr->cr_oid;
+ code = SLAP_SCHERR_CLASS_NOT_FOUND;
+ goto fail;
+ }
+
+ /* check object class usage */
+ if( scr->scr_sclass->soc_kind != LDAP_SCHEMA_STRUCTURAL )
+ {
+ *err = cr->cr_oid;
+ code = SLAP_SCHERR_CR_BAD_STRUCT;
+ goto fail;
+ }
+
+ if( scr->scr_sclass->soc_flags & SLAP_OC_OPERATIONAL ) op++;
+
+ code = cr_add_auxiliaries( scr, &op, err );
+ if ( code != 0 ) goto fail;
+
+ code = cr_create_required( scr, &op, err );
+ if ( code != 0 ) goto fail;
+
+ code = cr_create_allowed( scr, &op, err );
+ if ( code != 0 ) goto fail;
+
+ code = cr_create_precluded( scr, &op, err );
+ if ( code != 0 ) goto fail;
+
+ if( user && op ) {
+ code = SLAP_SCHERR_CR_BAD_AUX;
+ goto fail;
+ }
+
+ code = cr_insert(scr,err);
+ if ( code == 0 && rscr )
+ *rscr = scr;
+ return code;
+fail:
+ ch_free( scr );
+ return code;
+}
+
+void
+cr_unparse( BerVarray *res, ContentRule *start, ContentRule *end, int sys )
+{
+ ContentRule *cr;
+ int i, num;
+ struct berval bv, *bva = NULL, idx;
+ char ibuf[32];
+
+ if ( !start )
+ start = LDAP_STAILQ_FIRST( &cr_list );
+
+ /* count the result size */
+ i = 0;
+ for ( cr=start; cr; cr=LDAP_STAILQ_NEXT(cr, scr_next)) {
+ if ( sys && !(cr->scr_flags & SLAP_CR_HARDCODE)) continue;
+ i++;
+ if ( cr == end ) break;
+ }
+ if (!i) return;
+
+ num = i;
+ bva = ch_malloc( (num+1) * sizeof(struct berval) );
+ BER_BVZERO( bva );
+ idx.bv_val = ibuf;
+ if ( sys ) {
+ idx.bv_len = 0;
+ ibuf[0] = '\0';
+ }
+ i = 0;
+ for ( cr=start; cr; cr=LDAP_STAILQ_NEXT(cr, scr_next)) {
+ LDAPContentRule lcr, *lcrp;
+ if ( sys && !(cr->scr_flags & SLAP_CR_HARDCODE)) continue;
+ if ( cr->scr_oidmacro ) {
+ lcr = cr->scr_crule;
+ lcr.cr_oid = cr->scr_oidmacro;
+ lcrp = &lcr;
+ } else {
+ lcrp = &cr->scr_crule;
+ }
+ if ( ldap_contentrule2bv( lcrp, &bv ) == NULL ) {
+ ber_bvarray_free( bva );
+ }
+ if ( !sys ) {
+ idx.bv_len = sprintf(idx.bv_val, "{%d}", i);
+ }
+ bva[i].bv_len = idx.bv_len + bv.bv_len;
+ bva[i].bv_val = ch_malloc( bva[i].bv_len + 1 );
+ strcpy( bva[i].bv_val, ibuf );
+ strcpy( bva[i].bv_val + idx.bv_len, bv.bv_val );
+ i++;
+ bva[i].bv_val = NULL;
+ ldap_memfree( bv.bv_val );
+ if ( cr == end ) break;
+ }
+ *res = bva;
+}
+
+int
+cr_schema_info( Entry *e )
+{
+ AttributeDescription *ad_ditContentRules
+ = slap_schema.si_ad_ditContentRules;
+ ContentRule *cr;
+
+ struct berval val;
+ struct berval nval;
+
+ LDAP_STAILQ_FOREACH(cr, &cr_list, scr_next) {
+ if ( ldap_contentrule2bv( &cr->scr_crule, &val ) == NULL ) {
+ return -1;
+ }
+
+#if 0
+ if( cr->scr_flags & SLAP_CR_HIDE ) continue;
+#endif
+#if 0
+ Debug( LDAP_DEBUG_TRACE, "Merging cr [%ld] %s\n",
+ (long) val.bv_len, val.bv_val );
+#endif
+
+ nval.bv_val = cr->scr_oid;
+ nval.bv_len = strlen(cr->scr_oid);
+
+ if( attr_merge_one( e, ad_ditContentRules, &val, &nval ) )
+ {
+ return -1;
+ }
+ ldap_memfree( val.bv_val );
+ }
+ return 0;
+}
diff --git a/servers/slapd/ctxcsn.c b/servers/slapd/ctxcsn.c
new file mode 100644
index 0000000..a8f73c3
--- /dev/null
+++ b/servers/slapd/ctxcsn.c
@@ -0,0 +1,218 @@
+/* ctxcsn.c -- Context CSN Management Routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "lutil.h"
+#include "slap.h"
+#include "lutil_ldap.h"
+
+const struct berval slap_ldapsync_bv = BER_BVC("ldapsync");
+const struct berval slap_ldapsync_cn_bv = BER_BVC("cn=ldapsync");
+int slap_serverID;
+
+/* maxcsn->bv_val must point to a char buf[LDAP_PVT_CSNSTR_BUFSIZE] */
+void
+slap_get_commit_csn(
+ Operation *op,
+ struct berval *maxcsn,
+ int *foundit
+)
+{
+ struct slap_csn_entry *csne, *committed_csne = NULL;
+ BackendDB *be = op->o_bd->bd_self;
+ int sid = -1;
+
+ if ( maxcsn ) {
+ assert( maxcsn->bv_val != NULL );
+ assert( maxcsn->bv_len >= LDAP_PVT_CSNSTR_BUFSIZE );
+ }
+ if ( foundit ) {
+ *foundit = 0;
+ }
+
+ if ( !BER_BVISEMPTY( &op->o_csn )) {
+ sid = slap_parse_csn_sid( &op->o_csn );
+ }
+
+ ldap_pvt_thread_mutex_lock( &be->be_pcsn_p->be_pcsn_mutex );
+
+ LDAP_TAILQ_FOREACH( csne, &be->be_pcsn_p->be_pcsn_list, ce_csn_link ) {
+ if ( csne->ce_op == op ) {
+ csne->ce_state = SLAP_CSN_COMMIT;
+ if ( foundit ) *foundit = 1;
+ break;
+ }
+ }
+
+ LDAP_TAILQ_FOREACH( csne, &be->be_pcsn_p->be_pcsn_list, ce_csn_link ) {
+ if ( sid != -1 && sid == csne->ce_sid ) {
+ if ( csne->ce_state == SLAP_CSN_COMMIT ) committed_csne = csne;
+ if ( csne->ce_state == SLAP_CSN_PENDING ) break;
+ }
+ }
+
+ if ( maxcsn ) {
+ if ( committed_csne ) {
+ if ( committed_csne->ce_csn.bv_len < maxcsn->bv_len )
+ maxcsn->bv_len = committed_csne->ce_csn.bv_len;
+ AC_MEMCPY( maxcsn->bv_val, committed_csne->ce_csn.bv_val,
+ maxcsn->bv_len+1 );
+ } else {
+ maxcsn->bv_len = 0;
+ maxcsn->bv_val[0] = 0;
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &be->be_pcsn_p->be_pcsn_mutex );
+}
+
+void
+slap_rewind_commit_csn( Operation *op )
+{
+ struct slap_csn_entry *csne;
+ BackendDB *be = op->o_bd->bd_self;
+
+ ldap_pvt_thread_mutex_lock( &be->be_pcsn_p->be_pcsn_mutex );
+
+ LDAP_TAILQ_FOREACH( csne, &be->be_pcsn_p->be_pcsn_list, ce_csn_link ) {
+ if ( csne->ce_op == op ) {
+ csne->ce_state = SLAP_CSN_PENDING;
+ break;
+ }
+ }
+
+ ldap_pvt_thread_mutex_unlock( &be->be_pcsn_p->be_pcsn_mutex );
+}
+
+void
+slap_graduate_commit_csn( Operation *op )
+{
+ struct slap_csn_entry *csne;
+ BackendDB *be;
+
+ if ( op == NULL ) return;
+ if ( op->o_bd == NULL ) return;
+ be = op->o_bd->bd_self;
+
+ ldap_pvt_thread_mutex_lock( &be->be_pcsn_p->be_pcsn_mutex );
+
+ LDAP_TAILQ_FOREACH( csne, &be->be_pcsn_p->be_pcsn_list, ce_csn_link ) {
+ if ( csne->ce_op == op ) {
+ LDAP_TAILQ_REMOVE( &be->be_pcsn_p->be_pcsn_list,
+ csne, ce_csn_link );
+ Debug( LDAP_DEBUG_SYNC, "slap_graduate_commit_csn: removing %p %s\n",
+ csne, csne->ce_csn.bv_val );
+ if ( op->o_csn.bv_val == csne->ce_csn.bv_val ) {
+ BER_BVZERO( &op->o_csn );
+ }
+ ch_free( csne->ce_csn.bv_val );
+ ch_free( csne );
+ break;
+ }
+ }
+
+ ldap_pvt_thread_mutex_unlock( &be->be_pcsn_p->be_pcsn_mutex );
+
+ return;
+}
+
+static struct berval ocbva[] = {
+ BER_BVC("top"),
+ BER_BVC("subentry"),
+ BER_BVC("syncProviderSubentry"),
+ BER_BVNULL
+};
+
+Entry *
+slap_create_context_csn_entry(
+ Backend *be,
+ struct berval *context_csn )
+{
+ Entry* e;
+
+ struct berval bv;
+
+ e = entry_alloc();
+
+ attr_merge( e, slap_schema.si_ad_objectClass,
+ ocbva, NULL );
+ attr_merge_one( e, slap_schema.si_ad_structuralObjectClass,
+ &ocbva[1], NULL );
+ attr_merge_one( e, slap_schema.si_ad_cn,
+ (struct berval *)&slap_ldapsync_bv, NULL );
+
+ if ( context_csn ) {
+ attr_merge_one( e, slap_schema.si_ad_contextCSN,
+ context_csn, NULL );
+ }
+
+ BER_BVSTR( &bv, "{}" );
+ attr_merge_one( e, slap_schema.si_ad_subtreeSpecification, &bv, NULL );
+
+ build_new_dn( &e->e_name, &be->be_nsuffix[0],
+ (struct berval *)&slap_ldapsync_cn_bv, NULL );
+ ber_dupbv( &e->e_nname, &e->e_name );
+
+ return e;
+}
+
+void
+slap_queue_csn(
+ Operation *op,
+ struct berval *csn )
+{
+ struct slap_csn_entry *pending;
+ BackendDB *be = op->o_bd->bd_self;
+
+ pending = (struct slap_csn_entry *) ch_calloc( 1,
+ sizeof( struct slap_csn_entry ));
+
+ Debug( LDAP_DEBUG_SYNC, "slap_queue_csn: queueing %p %s\n", pending, csn->bv_val );
+
+ ber_dupbv( &pending->ce_csn, csn );
+ ber_bvreplace_x( &op->o_csn, &pending->ce_csn, op->o_tmpmemctx );
+ pending->ce_sid = slap_parse_csn_sid( csn );
+ pending->ce_op = op;
+ pending->ce_state = SLAP_CSN_PENDING;
+
+ ldap_pvt_thread_mutex_lock( &be->be_pcsn_p->be_pcsn_mutex );
+ LDAP_TAILQ_INSERT_TAIL( &be->be_pcsn_p->be_pcsn_list,
+ pending, ce_csn_link );
+ ldap_pvt_thread_mutex_unlock( &be->be_pcsn_p->be_pcsn_mutex );
+}
+
+int
+slap_get_csn(
+ Operation *op,
+ struct berval *csn,
+ int manage_ctxcsn )
+{
+ if ( csn == NULL ) return LDAP_OTHER;
+
+ csn->bv_len = ldap_pvt_csnstr( csn->bv_val, csn->bv_len, slap_serverID, 0 );
+ Debug( LDAP_DEBUG_SYNC, "slap_get_csn: %s generated new csn=%s manage=%d\n",
+ op->o_log_prefix, csn->bv_val, manage_ctxcsn );
+ if ( manage_ctxcsn )
+ slap_queue_csn( op, csn );
+
+ return LDAP_SUCCESS;
+}
diff --git a/servers/slapd/daemon.c b/servers/slapd/daemon.c
new file mode 100644
index 0000000..18db97a
--- /dev/null
+++ b/servers/slapd/daemon.c
@@ -0,0 +1,3610 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2007 by Howard Chu, Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include "slap.h"
+#include "ldap_pvt_thread.h"
+#include "lutil.h"
+
+#include "ldap_rq.h"
+
+#ifdef HAVE_SYSTEMD_SD_DAEMON_H
+#include <systemd/sd-daemon.h>
+#endif
+
+#ifdef HAVE_POLL
+#include <poll.h>
+#endif
+
+#ifdef HAVE_KQUEUE
+# include <sys/types.h>
+# include <sys/event.h>
+# include <sys/time.h>
+#elif defined(HAVE_SYS_EPOLL_H) && defined(HAVE_EPOLL)
+# include <sys/epoll.h>
+#elif defined(SLAP_X_DEVPOLL) && defined(HAVE_SYS_DEVPOLL_H) && defined(HAVE_DEVPOLL)
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <fcntl.h>
+# include <sys/devpoll.h>
+#endif /* ! kqueue && ! epoll && ! /dev/poll */
+
+#ifdef HAVE_TCPD
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_NOTICE;
+#endif /* TCP Wrappers */
+
+#ifdef LDAP_PF_LOCAL
+# include <sys/stat.h>
+/* this should go in <ldap.h> as soon as it is accepted */
+# define LDAPI_MOD_URLEXT "x-mod"
+#endif /* LDAP_PF_LOCAL */
+
+#ifdef LDAP_PF_INET6
+int slap_inet4or6 = AF_UNSPEC;
+#else /* ! INETv6 */
+int slap_inet4or6 = AF_INET;
+#endif /* ! INETv6 */
+
+/* globals */
+time_t starttime;
+ber_socket_t dtblsize;
+slap_ssf_t local_ssf = LDAP_PVT_SASL_LOCAL_SSF;
+struct runqueue_s slapd_rq;
+
+int slapd_daemon_threads = 1;
+int slapd_daemon_mask;
+
+#ifdef LDAP_TCP_BUFFER
+int slapd_tcp_rmem;
+int slapd_tcp_wmem;
+#endif /* LDAP_TCP_BUFFER */
+
+Listener **slap_listeners = NULL;
+static volatile sig_atomic_t listening = 1; /* 0 when slap_listeners closed */
+
+#ifndef SLAPD_LISTEN_BACKLOG
+#define SLAPD_LISTEN_BACKLOG 2048
+#endif /* ! SLAPD_LISTEN_BACKLOG */
+
+#define DAEMON_ID(fd) (fd & slapd_daemon_mask)
+
+typedef ber_socket_t sdpair[2];
+
+static sdpair *wake_sds;
+static ldap_pvt_thread_mutex_t emfile_mutex;
+static int emfile;
+
+static volatile int waking;
+#define WAKE_LISTENER(l,w) do { \
+ if (w) { \
+ (void)!tcp_write( SLAP_FD2SOCK(wake_sds[l][1]), "0", 1 ); \
+ } \
+} while (0)
+
+ldap_pvt_thread_mutex_t slapd_init_mutex;
+ldap_pvt_thread_cond_t slapd_init_cond;
+int slapd_ready = 0;
+
+volatile sig_atomic_t slapd_shutdown = 0;
+volatile sig_atomic_t slapd_gentle_shutdown = 0;
+volatile sig_atomic_t slapd_abrupt_shutdown = 0;
+
+#ifdef HAVE_WINSOCK
+ldap_pvt_thread_mutex_t slapd_ws_mutex;
+SOCKET *slapd_ws_sockets;
+#define SD_READ 1
+#define SD_WRITE 2
+#define SD_ACTIVE 4
+#define SD_LISTENER 8
+#endif
+
+#ifdef HAVE_TCPD
+static ldap_pvt_thread_mutex_t sd_tcpd_mutex;
+#endif /* TCP Wrappers */
+
+typedef struct slap_daemon_st {
+ ldap_pvt_thread_mutex_t sd_mutex;
+
+ ber_socket_t sd_nactives;
+ int sd_nwriters;
+ int sd_nfds;
+ ldap_pvt_thread_t sd_tid;
+
+#if defined(HAVE_KQUEUE)
+ uint8_t* sd_fdmodes; /* indexed by fd */
+ Listener** sd_l; /* indexed by fd */
+ /* Double buffer the kqueue changes to avoid holding the sd_mutex \
+ * during a kevent() call. \
+ */
+ struct kq_change {
+ struct kevent* sd_changes;
+ int sd_nchanges;
+ int sd_maxchanges;
+ } sd_kqc[2];
+ int sd_changeidx; /* index to current change buffer */
+ int sd_kq;
+#elif defined(HAVE_EPOLL)
+
+ struct epoll_event *sd_epolls;
+ int *sd_index;
+ int sd_epfd;
+#elif defined(SLAP_X_DEVPOLL) && defined(HAVE_DEVPOLL)
+ /* eXperimental */
+ struct pollfd *sd_pollfd;
+ int *sd_index;
+ Listener **sd_l;
+ int sd_dpfd;
+#else /* ! kqueue && ! epoll && ! /dev/poll */
+#ifdef HAVE_WINSOCK
+ char *sd_flags;
+ char *sd_rflags;
+#else /* ! HAVE_WINSOCK */
+ fd_set sd_actives;
+ fd_set sd_readers;
+ fd_set sd_writers;
+#endif /* ! HAVE_WINSOCK */
+#endif /* ! kqueue && ! epoll && ! /dev/poll */
+} slap_daemon_st;
+
+static slap_daemon_st *slap_daemon;
+
+/*
+ * NOTE: naming convention for macros:
+ *
+ * - SLAP_SOCK_* and SLAP_EVENT_* for public interface that deals
+ * with file descriptors and events respectively
+ *
+ * - SLAP_<type>_* for private interface; type by now is one of
+ * EPOLL, DEVPOLL, SELECT, KQUEUE
+ *
+ * private interface should not be used in the code.
+ */
+#ifdef HAVE_KQUEUE
+# define SLAP_EVENT_FNAME "kqueue"
+# define SLAP_EVENTS_ARE_INDEXED 0
+# define SLAP_EVENT_MAX(t) (2 * dtblsize) /* each fd can have a read & a write event */
+
+# define SLAP_EVENT_DECL \
+ static struct kevent* events = NULL
+
+# define SLAP_EVENT_INIT(t) do {\
+ if (!events) { \
+ events = ch_malloc(sizeof(*events) * SLAP_EVENT_MAX(t)); \
+ } \
+} while (0)
+
+# define SLAP_SOCK_INIT(t) do { \
+ int kq_i; \
+ size_t kq_nbytes; \
+ Debug(LDAP_DEBUG_ANY, "daemon: SLAP_SOCK_INIT: dtblsize=%d\n", dtblsize); \
+ slap_daemon[t].sd_nfds = 0; \
+ slap_daemon[t].sd_changeidx = 0; \
+ for (kq_i = 0; kq_i < 2; kq_i++) { \
+ struct kq_change* kqc = &slap_daemon[t].sd_kqc[kq_i]; \
+ kqc->sd_nchanges = 0; \
+ kqc->sd_maxchanges = 256; /* will grow as needed */ \
+ kq_nbytes = sizeof(*kqc->sd_changes) * kqc->sd_maxchanges; \
+ kqc->sd_changes = ch_calloc(1, kq_nbytes); \
+ } \
+ kq_nbytes = sizeof(*slap_daemon[t].sd_fdmodes) * dtblsize; \
+ slap_daemon[t].sd_fdmodes = ch_calloc(1, kq_nbytes); \
+ kq_nbytes = sizeof(*slap_daemon[t].sd_l) * dtblsize; \
+ slap_daemon[t].sd_l = ch_calloc(1, kq_nbytes); \
+ slap_daemon[t].sd_kq = kqueue(); \
+} while (0)
+
+/* a kqueue fd obtained before a fork isn't inherited by child process.
+ * reacquire it.
+ */
+# define SLAP_SOCK_INIT2() do { \
+ slap_daemon[0].sd_kq = kqueue(); \
+} while (0)
+
+# define SLAP_SOCK_DESTROY(t) do { \
+ int kq_i; \
+ if (slap_daemon[t].sd_kq > 0) { \
+ close(slap_daemon[t].sd_kq); \
+ slap_daemon[t].sd_kq = -1; \
+ } \
+ for (kq_i = 0; kq_i < 2; kq_i++) { \
+ if (slap_daemon[t].sd_kqc[kq_i].sd_changes != NULL) { \
+ ch_free(slap_daemon[t].sd_kqc[kq_i].sd_changes); \
+ slap_daemon[t].sd_kqc[kq_i].sd_changes = NULL; \
+ } \
+ slap_daemon[t].sd_kqc[kq_i].sd_nchanges = 0; \
+ slap_daemon[t].sd_kqc[kq_i].sd_maxchanges = 0; \
+ } \
+ if (slap_daemon[t].sd_l != NULL) { \
+ ch_free(slap_daemon[t].sd_l); \
+ slap_daemon[t].sd_l = NULL; \
+ } \
+ if (slap_daemon[t].sd_fdmodes != NULL) { \
+ ch_free(slap_daemon[t].sd_fdmodes); \
+ slap_daemon[t].sd_fdmodes = NULL; \
+ } \
+ slap_daemon[t].sd_nfds = 0; \
+} while (0)
+
+# define SLAP_KQUEUE_SOCK_ACTIVE 0x01
+# define SLAP_KQUEUE_SOCK_READ_ENABLED 0x02
+# define SLAP_KQUEUE_SOCK_WRITE_ENABLED 0x04
+
+# define SLAP_SOCK_IS_ACTIVE(t,s) (slap_daemon[t].sd_fdmodes[(s)] != 0)
+# define SLAP_SOCK_NOT_ACTIVE(t,s) (slap_daemon[t].sd_fdmodes[(s)] == 0)
+# define SLAP_SOCK_IS_READ(t,s) (slap_daemon[t].sd_fdmodes[(s)] & SLAP_KQUEUE_SOCK_READ_ENABLED)
+# define SLAP_SOCK_IS_WRITE(t,s) (slap_daemon[t].sd_fdmodes[(s)] & SLAP_KQUEUE_SOCK_WRITE_ENABLED)
+
+/*
+ * SLAP_SOCK_SET_* & SLAP_SOCK_CLR_* get called a _lot_. Since kevent()
+ * processes changes before it looks for events, batch up the changes which
+ * will get submitted the next time kevent() is called for events.
+ */
+
+# define SLAP_KQUEUE_CHANGE(t, s, filter, flag) do { \
+ /* If maxchanges is reached, have to realloc to make room for more. \
+ * Ideally we'd call kevent(), but the daemon thread could be sitting \
+ * in kevent() waiting for events. \
+ */ \
+ struct kq_change* kqc = &slap_daemon[t].sd_kqc[slap_daemon[t].sd_changeidx]; \
+ if (kqc->sd_nchanges == kqc->sd_maxchanges) { \
+ /* Don't want to do this very often. Double the size. */ \
+ size_t kq_nbytes; \
+ Debug(LDAP_DEBUG_CONNS, \
+ "daemon: SLAP_KQUEUE_CHANGE: increasing slap_daemon.sd_kqc[%d].maxchanges from %d to %d\n", \
+ slap_daemon[t].sd_changeidx, kqc->sd_maxchanges, 2*kqc->sd_maxchanges); \
+ kqc->sd_maxchanges += kqc->sd_maxchanges; \
+ kq_nbytes = sizeof(*kqc->sd_changes) * kqc->sd_maxchanges; \
+ kqc->sd_changes = ch_realloc(kqc->sd_changes, kq_nbytes); \
+ } \
+ EV_SET(&kqc->sd_changes[kqc->sd_nchanges++], \
+ (s), (filter), (flag), 0, 0, slap_daemon[t].sd_l[(s)]); \
+} while (0)
+
+# define SLAP_KQUEUE_SOCK_SET(t, s, filter, mode) do { \
+ if ((slap_daemon[t].sd_fdmodes[(s)] & (mode)) != (mode)) { \
+ slap_daemon[t].sd_fdmodes[(s)] |= (mode); \
+ SLAP_KQUEUE_CHANGE(t, (s), (filter), EV_ENABLE); \
+ } \
+} while (0)
+
+# define SLAP_KQUEUE_SOCK_CLR(t, s, filter, mode) do { \
+ if (slap_daemon[t].sd_fdmodes[(s)] & (mode)) { \
+ slap_daemon[t].sd_fdmodes[(s)] &= ~(mode); \
+ SLAP_KQUEUE_CHANGE(t, (s), (filter), EV_DISABLE); \
+ } \
+} while (0)
+
+# define SLAP_SOCK_SET_READ(t, s) SLAP_KQUEUE_SOCK_SET(t, (s), EVFILT_READ, SLAP_KQUEUE_SOCK_READ_ENABLED)
+# define SLAP_SOCK_SET_WRITE(t, s) SLAP_KQUEUE_SOCK_SET(t, (s), EVFILT_WRITE, SLAP_KQUEUE_SOCK_WRITE_ENABLED)
+# define SLAP_SOCK_CLR_READ(t, s) SLAP_KQUEUE_SOCK_CLR(t, (s), EVFILT_READ, SLAP_KQUEUE_SOCK_READ_ENABLED)
+# define SLAP_SOCK_CLR_WRITE(t, s) SLAP_KQUEUE_SOCK_CLR(t, (s), EVFILT_WRITE, SLAP_KQUEUE_SOCK_WRITE_ENABLED)
+
+/* kqueue doesn't need to do anything to clear the event. */
+# define SLAP_EVENT_CLR_READ(i) do {} while (0)
+# define SLAP_EVENT_CLR_WRITE(i) do {} while (0)
+
+# define SLAP_SOCK_ADD(t, s, l) do { \
+ assert( s < dtblsize ); \
+ slap_daemon[t].sd_l[(s)] = (l); \
+ slap_daemon[t].sd_fdmodes[(s)] = SLAP_KQUEUE_SOCK_ACTIVE | SLAP_KQUEUE_SOCK_READ_ENABLED; \
+ ++slap_daemon[t].sd_nfds; \
+ SLAP_KQUEUE_CHANGE(t, (s), EVFILT_READ, EV_ADD); \
+ SLAP_KQUEUE_CHANGE(t, (s), EVFILT_WRITE, EV_ADD | EV_DISABLE); \
+} while (0)
+
+# define SLAP_SOCK_DEL(t, s) do { \
+ SLAP_KQUEUE_CHANGE(t, (s), EVFILT_READ, EV_DELETE); \
+ SLAP_KQUEUE_CHANGE(t, (s), EVFILT_WRITE, EV_DELETE); \
+ slap_daemon[t].sd_l[(s)] = NULL; \
+ slap_daemon[t].sd_fdmodes[(s)] = 0; \
+ --slap_daemon[t].sd_nfds; \
+} while (0)
+
+# define SLAP_EVENT_FD(t, i) (events[(i)].ident)
+
+# define SLAP_EVENT_IS_READ(t, i) \
+ (events[(i)].filter == EVFILT_READ && SLAP_SOCK_IS_READ(t, SLAP_EVENT_FD(0, i)))
+
+# define SLAP_EVENT_IS_WRITE(t, i) \
+ (events[(i)].filter == EVFILT_WRITE && SLAP_SOCK_IS_WRITE(t, SLAP_EVENT_FD(0, i)))
+
+# define SLAP_EVENT_IS_LISTENER(t, i) \
+ (events[(i)].udata && SLAP_SOCK_IS_READ(t, SLAP_EVENT_FD(t, i)))
+
+# define SLAP_EVENT_LISTENER(t, i) ((Listener*)(events[(i)].udata))
+
+# define SLAP_EVENT_WAIT(t, tvp, nsp) do { \
+ struct timespec kq_ts; \
+ struct timespec* kq_tsp; \
+ int kq_idx; \
+ if (tvp) { \
+ TIMEVAL_TO_TIMESPEC((tvp), &kq_ts); \
+ kq_tsp = &kq_ts; \
+ } else { \
+ kq_tsp = NULL; \
+ } \
+ /* Save the change buffer index for use when the mutex is unlocked, \
+ * then switch the index so new changes go to the other buffer. \
+ */ \
+ ldap_pvt_thread_mutex_lock( &slap_daemon[t].sd_mutex ); \
+ kq_idx = slap_daemon[t].sd_changeidx; \
+ slap_daemon[t].sd_changeidx ^= 1; \
+ ldap_pvt_thread_mutex_unlock( &slap_daemon[t].sd_mutex ); \
+ *(nsp) = kevent(slap_daemon[t].sd_kq, \
+ slap_daemon[t].sd_kqc[kq_idx].sd_nchanges \
+ ? slap_daemon[t].sd_kqc[kq_idx].sd_changes : NULL, \
+ slap_daemon[t].sd_kqc[kq_idx].sd_nchanges, \
+ events, SLAP_EVENT_MAX(t), kq_tsp); \
+ slap_daemon[t].sd_kqc[kq_idx].sd_nchanges = 0; \
+} while(0)
+
+/*-------------------------------------------------------------------------------*/
+
+#elif defined(HAVE_EPOLL)
+/***************************************
+ * Use epoll infrastructure - epoll(4) *
+ ***************************************/
+# define SLAP_EVENT_FNAME "epoll"
+# define SLAP_EVENTS_ARE_INDEXED 0
+# define SLAP_EPOLL_SOCK_IX(t,s) (slap_daemon[t].sd_index[(s)])
+# define SLAP_EPOLL_SOCK_EP(t,s) (slap_daemon[t].sd_epolls[SLAP_EPOLL_SOCK_IX(t,s)])
+# define SLAP_EPOLL_SOCK_EV(t,s) (SLAP_EPOLL_SOCK_EP(t,s).events)
+# define SLAP_SOCK_IS_ACTIVE(t,s) (SLAP_EPOLL_SOCK_IX(t,s) != -1)
+# define SLAP_SOCK_NOT_ACTIVE(t,s) (SLAP_EPOLL_SOCK_IX(t,s) == -1)
+# define SLAP_EPOLL_SOCK_IS_SET(t,s, mode) (SLAP_EPOLL_SOCK_EV(t,s) & (mode))
+
+# define SLAP_SOCK_IS_READ(t,s) SLAP_EPOLL_SOCK_IS_SET(t,(s), EPOLLIN)
+# define SLAP_SOCK_IS_WRITE(t,s) SLAP_EPOLL_SOCK_IS_SET(t,(s), EPOLLOUT)
+
+# define SLAP_EPOLL_SOCK_SET(t,s, mode) do { \
+ if ( (SLAP_EPOLL_SOCK_EV(t,s) & (mode)) != (mode) ) { \
+ SLAP_EPOLL_SOCK_EV(t,s) |= (mode); \
+ epoll_ctl( slap_daemon[t].sd_epfd, EPOLL_CTL_MOD, (s), \
+ &SLAP_EPOLL_SOCK_EP(t,s) ); \
+ } \
+} while (0)
+
+# define SLAP_EPOLL_SOCK_CLR(t,s, mode) do { \
+ if ( (SLAP_EPOLL_SOCK_EV(t,s) & (mode)) ) { \
+ SLAP_EPOLL_SOCK_EV(t,s) &= ~(mode); \
+ epoll_ctl( slap_daemon[t].sd_epfd, EPOLL_CTL_MOD, s, \
+ &SLAP_EPOLL_SOCK_EP(t,s) ); \
+ } \
+} while (0)
+
+# define SLAP_SOCK_SET_READ(t,s) SLAP_EPOLL_SOCK_SET(t,s, EPOLLIN)
+# define SLAP_SOCK_SET_WRITE(t,s) SLAP_EPOLL_SOCK_SET(t,s, EPOLLOUT)
+
+# define SLAP_SOCK_CLR_READ(t,s) SLAP_EPOLL_SOCK_CLR(t,(s), EPOLLIN)
+# define SLAP_SOCK_CLR_WRITE(t,s) SLAP_EPOLL_SOCK_CLR(t,(s), EPOLLOUT)
+
+# define SLAP_SOCK_SET_SUSPEND(t,s) \
+ ( slap_daemon[t].sd_suspend[SLAP_EPOLL_SOCK_IX(t,s)] = 1 )
+# define SLAP_SOCK_CLR_SUSPEND(t,s) \
+ ( slap_daemon[t].sd_suspend[SLAP_EPOLL_SOCK_IX(t,s)] = 0 )
+# define SLAP_SOCK_IS_SUSPEND(t,s) \
+ ( slap_daemon[t].sd_suspend[SLAP_EPOLL_SOCK_IX(t,s)] == 1 )
+
+# define SLAP_EPOLL_EVENT_CLR(i, mode) (revents[(i)].events &= ~(mode))
+
+# define SLAP_EVENT_MAX(t) slap_daemon[t].sd_nfds
+
+/* If a Listener address is provided, store that as the epoll data.
+ * Otherwise, store the address of this socket's slot in the
+ * index array. If we can't do this add, the system is out of
+ * resources and we need to shutdown.
+ */
+# define SLAP_SOCK_ADD(t, s, l) do { \
+ int rc; \
+ SLAP_EPOLL_SOCK_IX(t,(s)) = slap_daemon[t].sd_nfds; \
+ SLAP_EPOLL_SOCK_EP(t,(s)).data.ptr = (l) ? (l) : (void *)(&SLAP_EPOLL_SOCK_IX(t,s)); \
+ SLAP_EPOLL_SOCK_EV(t,(s)) = EPOLLIN; \
+ rc = epoll_ctl(slap_daemon[t].sd_epfd, EPOLL_CTL_ADD, \
+ (s), &SLAP_EPOLL_SOCK_EP(t,(s))); \
+ if ( rc == 0 ) { \
+ slap_daemon[t].sd_nfds++; \
+ } else { \
+ int saved_errno = errno; \
+ Debug( LDAP_DEBUG_ANY, \
+ "daemon: epoll_ctl(ADD,fd=%d) failed, errno=%d, shutting down\n", \
+ s, saved_errno ); \
+ slapd_shutdown = 2; \
+ } \
+} while (0)
+
+# define SLAP_EPOLL_EV_LISTENER(t,ptr) \
+ (((int *)(ptr) >= slap_daemon[t].sd_index && \
+ (int *)(ptr) <= &slap_daemon[t].sd_index[dtblsize]) ? 0 : 1 )
+
+# define SLAP_EPOLL_EV_PTRFD(t,ptr) (SLAP_EPOLL_EV_LISTENER(t,ptr) ? \
+ ((Listener *)ptr)->sl_sd : \
+ (ber_socket_t) ((int *)(ptr) - slap_daemon[t].sd_index))
+
+# define SLAP_SOCK_DEL(t,s) do { \
+ int fd, rc, index = SLAP_EPOLL_SOCK_IX(t,(s)); \
+ if ( index < 0 ) break; \
+ rc = epoll_ctl(slap_daemon[t].sd_epfd, EPOLL_CTL_DEL, \
+ (s), &SLAP_EPOLL_SOCK_EP(t,(s))); \
+ slap_daemon[t].sd_epolls[index] = \
+ slap_daemon[t].sd_epolls[slap_daemon[t].sd_nfds-1]; \
+ fd = SLAP_EPOLL_EV_PTRFD(t,slap_daemon[t].sd_epolls[index].data.ptr); \
+ slap_daemon[t].sd_index[fd] = index; \
+ slap_daemon[t].sd_index[(s)] = -1; \
+ slap_daemon[t].sd_nfds--; \
+} while (0)
+
+# define SLAP_EVENT_CLR_READ(i) SLAP_EPOLL_EVENT_CLR((i), EPOLLIN)
+# define SLAP_EVENT_CLR_WRITE(i) SLAP_EPOLL_EVENT_CLR((i), EPOLLOUT)
+
+# define SLAP_EPOLL_EVENT_CHK(i, mode) (revents[(i)].events & mode)
+
+# define SLAP_EVENT_IS_READ(i) SLAP_EPOLL_EVENT_CHK((i), EPOLLIN)
+# define SLAP_EVENT_IS_WRITE(i) SLAP_EPOLL_EVENT_CHK((i), EPOLLOUT)
+# define SLAP_EVENT_IS_LISTENER(t,i) SLAP_EPOLL_EV_LISTENER(t,revents[(i)].data.ptr)
+# define SLAP_EVENT_LISTENER(t,i) ((Listener *)(revents[(i)].data.ptr))
+
+# define SLAP_EVENT_FD(t,i) SLAP_EPOLL_EV_PTRFD(t,revents[(i)].data.ptr)
+
+# define SLAP_SOCK_INIT(t) do { \
+ int j; \
+ slap_daemon[t].sd_epolls = ch_calloc(1, \
+ ( sizeof(struct epoll_event) * 2 \
+ + sizeof(int) ) * dtblsize * 2); \
+ slap_daemon[t].sd_index = (int *)&slap_daemon[t].sd_epolls[ 2 * dtblsize ]; \
+ slap_daemon[t].sd_epfd = epoll_create( dtblsize / slapd_daemon_threads ); \
+ for ( j = 0; j < dtblsize; j++ ) slap_daemon[t].sd_index[j] = -1; \
+} while (0)
+
+# define SLAP_SOCK_INIT2()
+
+# define SLAP_SOCK_DESTROY(t) do { \
+ if ( slap_daemon[t].sd_epolls != NULL ) { \
+ ch_free( slap_daemon[t].sd_epolls ); \
+ slap_daemon[t].sd_epolls = NULL; \
+ slap_daemon[t].sd_index = NULL; \
+ close( slap_daemon[t].sd_epfd ); \
+ } \
+} while ( 0 )
+
+# define SLAP_EVENT_DECL struct epoll_event *revents
+
+# define SLAP_EVENT_INIT(t) do { \
+ revents = slap_daemon[t].sd_epolls + dtblsize; \
+} while (0)
+
+# define SLAP_EVENT_WAIT(t, tvp, nsp) do { \
+ *(nsp) = epoll_wait( slap_daemon[t].sd_epfd, revents, \
+ dtblsize, (tvp) ? ((tvp)->tv_sec * 1000 + (tvp)->tv_usec / 1000) : -1 ); \
+} while (0)
+
+#elif defined(SLAP_X_DEVPOLL) && defined(HAVE_DEVPOLL)
+
+/*************************************************************
+ * Use Solaris' (>= 2.7) /dev/poll infrastructure - poll(7d) *
+ *************************************************************/
+# define SLAP_EVENT_FNAME "/dev/poll"
+# define SLAP_EVENTS_ARE_INDEXED 0
+/*
+ * - sd_index is used much like with epoll()
+ * - sd_l is maintained as an array containing the address
+ * of the listener; the index is the fd itself
+ * - sd_pollfd is used to keep track of what data has been
+ * registered in /dev/poll
+ */
+# define SLAP_DEVPOLL_SOCK_IX(t,s) (slap_daemon[t].sd_index[(s)])
+# define SLAP_DEVPOLL_SOCK_LX(t,s) (slap_daemon[t].sd_l[(s)])
+# define SLAP_DEVPOLL_SOCK_EP(t,s) (slap_daemon[t].sd_pollfd[SLAP_DEVPOLL_SOCK_IX(t,(s))])
+# define SLAP_DEVPOLL_SOCK_FD(t,s) (SLAP_DEVPOLL_SOCK_EP(t,(s)).fd)
+# define SLAP_DEVPOLL_SOCK_EV(t,s) (SLAP_DEVPOLL_SOCK_EP(t,(s)).events)
+# define SLAP_SOCK_IS_ACTIVE(t,s) (SLAP_DEVPOLL_SOCK_IX(t,(s)) != -1)
+# define SLAP_SOCK_NOT_ACTIVE(t,s) (SLAP_DEVPOLL_SOCK_IX(t,(s)) == -1)
+# define SLAP_SOCK_IS_SET(t,s, mode) (SLAP_DEVPOLL_SOCK_EV(t,(s)) & (mode))
+
+# define SLAP_SOCK_IS_READ(t,s) SLAP_SOCK_IS_SET(t,(s), POLLIN)
+# define SLAP_SOCK_IS_WRITE(t,s) SLAP_SOCK_IS_SET(t,(s), POLLOUT)
+
+/* as far as I understand, any time we need to communicate with the kernel
+ * about the number and/or properties of a file descriptor we need it to
+ * wait for, we have to rewrite the whole set */
+# define SLAP_DEVPOLL_WRITE_POLLFD(t,s, pfd, n, what, shdn) do { \
+ int rc; \
+ size_t size = (n) * sizeof( struct pollfd ); \
+ /* FIXME: use pwrite? */ \
+ rc = write( slap_daemon[t].sd_dpfd, (pfd), size ); \
+ if ( rc != size ) { \
+ int saved_errno = errno; \
+ Debug( LDAP_DEBUG_ANY, "daemon: " SLAP_EVENT_FNAME ": " \
+ "%s fd=%d failed errno=%d\n", \
+ (what), (s), saved_errno ); \
+ if ( (shdn) ) { \
+ slapd_shutdown = 2; \
+ } \
+ } \
+} while (0)
+
+# define SLAP_DEVPOLL_SOCK_SET(t,s, mode) do { \
+ Debug( LDAP_DEBUG_CONNS, "SLAP_SOCK_SET_%s(%d)=%d\n", \
+ (mode) == POLLIN ? "READ" : "WRITE", (s), \
+ ( (SLAP_DEVPOLL_SOCK_EV(t,(s)) & (mode)) != (mode) ) ); \
+ if ( (SLAP_DEVPOLL_SOCK_EV(t,(s)) & (mode)) != (mode) ) { \
+ struct pollfd pfd; \
+ SLAP_DEVPOLL_SOCK_EV(t,(s)) |= (mode); \
+ pfd.fd = SLAP_DEVPOLL_SOCK_FD(t,(s)); \
+ pfd.events = /* (mode) */ SLAP_DEVPOLL_SOCK_EV(t,(s)); \
+ SLAP_DEVPOLL_WRITE_POLLFD(t,(s), &pfd, 1, "SET", 0); \
+ } \
+} while (0)
+
+# define SLAP_DEVPOLL_SOCK_CLR(t,s, mode) do { \
+ Debug( LDAP_DEBUG_CONNS, "SLAP_SOCK_CLR_%s(%d)=%d\n", \
+ (mode) == POLLIN ? "READ" : "WRITE", (s), \
+ ( (SLAP_DEVPOLL_SOCK_EV(t,(s)) & (mode)) == (mode) ) ); \
+ if ((SLAP_DEVPOLL_SOCK_EV(t,(s)) & (mode)) == (mode) ) { \
+ struct pollfd pfd[2]; \
+ SLAP_DEVPOLL_SOCK_EV(t,(s)) &= ~(mode); \
+ pfd[0].fd = SLAP_DEVPOLL_SOCK_FD(t,(s)); \
+ pfd[0].events = POLLREMOVE; \
+ pfd[1] = SLAP_DEVPOLL_SOCK_EP(t,(s)); \
+ SLAP_DEVPOLL_WRITE_POLLFD(t,(s), &pfd[0], 2, "CLR", 0); \
+ } \
+} while (0)
+
+# define SLAP_SOCK_SET_READ(t,s) SLAP_DEVPOLL_SOCK_SET(t,s, POLLIN)
+# define SLAP_SOCK_SET_WRITE(t,s) SLAP_DEVPOLL_SOCK_SET(t,s, POLLOUT)
+
+# define SLAP_SOCK_CLR_READ(t,s) SLAP_DEVPOLL_SOCK_CLR(t,(s), POLLIN)
+# define SLAP_SOCK_CLR_WRITE(t,s) SLAP_DEVPOLL_SOCK_CLR(t,(s), POLLOUT)
+
+# define SLAP_SOCK_SET_SUSPEND(t,s) \
+ ( slap_daemon[t].sd_suspend[SLAP_DEVPOLL_SOCK_IX(t,(s))] = 1 )
+# define SLAP_SOCK_CLR_SUSPEND(t,s) \
+ ( slap_daemon[t].sd_suspend[SLAP_DEVPOLL_SOCK_IX(t,(s))] = 0 )
+# define SLAP_SOCK_IS_SUSPEND(t,s) \
+ ( slap_daemon[t].sd_suspend[SLAP_DEVPOLL_SOCK_IX(t,(s))] == 1 )
+
+# define SLAP_DEVPOLL_EVENT_CLR(i, mode) (revents[(i)].events &= ~(mode))
+
+# define SLAP_EVENT_MAX(t) slap_daemon[t].sd_nfds
+
+/* If a Listener address is provided, store that in the sd_l array.
+ * If we can't do this add, the system is out of resources and we
+ * need to shutdown.
+ */
+# define SLAP_SOCK_ADD(t, s, l) do { \
+ Debug( LDAP_DEBUG_CONNS, "SLAP_SOCK_ADD(%d, %p)\n", (s), (l) ); \
+ SLAP_DEVPOLL_SOCK_IX(t,(s)) = slap_daemon[t].sd_nfds; \
+ SLAP_DEVPOLL_SOCK_LX(t,(s)) = (l); \
+ SLAP_DEVPOLL_SOCK_FD(t,(s)) = (s); \
+ SLAP_DEVPOLL_SOCK_EV(t,(s)) = POLLIN; \
+ SLAP_DEVPOLL_WRITE_POLLFD(t,(s), &SLAP_DEVPOLL_SOCK_EP(t, (s)), 1, "ADD", 1); \
+ slap_daemon[t].sd_nfds++; \
+} while (0)
+
+# define SLAP_DEVPOLL_EV_LISTENER(ptr) ((ptr) != NULL)
+
+# define SLAP_SOCK_DEL(t,s) do { \
+ int fd, index = SLAP_DEVPOLL_SOCK_IX(t,(s)); \
+ Debug( LDAP_DEBUG_CONNS, "SLAP_SOCK_DEL(%d)\n", (s) ); \
+ if ( index < 0 ) break; \
+ if ( index < slap_daemon[t].sd_nfds - 1 ) { \
+ struct pollfd pfd = slap_daemon[t].sd_pollfd[index]; \
+ fd = slap_daemon[t].sd_pollfd[slap_daemon[t].sd_nfds - 1].fd; \
+ slap_daemon[t].sd_pollfd[index] = slap_daemon[t].sd_pollfd[slap_daemon[t].sd_nfds - 1]; \
+ slap_daemon[t].sd_pollfd[slap_daemon[t].sd_nfds - 1] = pfd; \
+ slap_daemon[t].sd_index[fd] = index; \
+ } \
+ slap_daemon[t].sd_index[(s)] = -1; \
+ slap_daemon[t].sd_pollfd[slap_daemon[t].sd_nfds - 1].events = POLLREMOVE; \
+ SLAP_DEVPOLL_WRITE_POLLFD(t,(s), &slap_daemon[t].sd_pollfd[slap_daemon[t].sd_nfds - 1], 1, "DEL", 0); \
+ slap_daemon[t].sd_pollfd[slap_daemon[t].sd_nfds - 1].events = 0; \
+ slap_daemon[t].sd_nfds--; \
+} while (0)
+
+# define SLAP_EVENT_CLR_READ(i) SLAP_DEVPOLL_EVENT_CLR((i), POLLIN)
+# define SLAP_EVENT_CLR_WRITE(i) SLAP_DEVPOLL_EVENT_CLR((i), POLLOUT)
+
+# define SLAP_DEVPOLL_EVENT_CHK(i, mode) (revents[(i)].events & (mode))
+
+# define SLAP_EVENT_FD(t,i) (revents[(i)].fd)
+
+# define SLAP_EVENT_IS_READ(i) SLAP_DEVPOLL_EVENT_CHK((i), POLLIN)
+# define SLAP_EVENT_IS_WRITE(i) SLAP_DEVPOLL_EVENT_CHK((i), POLLOUT)
+# define SLAP_EVENT_IS_LISTENER(t,i) SLAP_DEVPOLL_EV_LISTENER(SLAP_DEVPOLL_SOCK_LX(t, SLAP_EVENT_FD(t,(i))))
+# define SLAP_EVENT_LISTENER(t,i) SLAP_DEVPOLL_SOCK_LX(t, SLAP_EVENT_FD(t,(i)))
+
+# define SLAP_SOCK_DESTROY(t) do { \
+ if ( slap_daemon[t].sd_pollfd != NULL ) { \
+ ch_free( slap_daemon[t].sd_pollfd ); \
+ slap_daemon[t].sd_pollfd = NULL; \
+ slap_daemon[t].sd_index = NULL; \
+ slap_daemon[t].sd_l = NULL; \
+ close( slap_daemon[t].sd_dpfd ); \
+ } \
+} while ( 0 )
+
+# define SLAP_SOCK_INIT(t) do { \
+ slap_daemon[t].sd_pollfd = ch_calloc( 1, \
+ ( sizeof(struct pollfd) * 2 \
+ + sizeof( int ) \
+ + sizeof( Listener * ) ) * dtblsize ); \
+ slap_daemon[t].sd_index = (int *)&slap_daemon[t].sd_pollfd[ 2 * dtblsize ]; \
+ slap_daemon[t].sd_l = (Listener **)&slap_daemon[t].sd_index[ dtblsize ]; \
+ slap_daemon[t].sd_dpfd = open( SLAP_EVENT_FNAME, O_RDWR ); \
+ if ( slap_daemon[t].sd_dpfd == -1 ) { \
+ int saved_errno = errno; \
+ Debug( LDAP_DEBUG_ANY, "daemon: " SLAP_EVENT_FNAME ": " \
+ "open(\"" SLAP_EVENT_FNAME "\") failed errno=%d\n", \
+ saved_errno ); \
+ SLAP_SOCK_DESTROY(t); \
+ return -1; \
+ } \
+ for ( i = 0; i < dtblsize; i++ ) { \
+ slap_daemon[t].sd_pollfd[i].fd = -1; \
+ slap_daemon[t].sd_index[i] = -1; \
+ } \
+} while (0)
+
+# define SLAP_SOCK_INIT2()
+
+# define SLAP_EVENT_DECL struct pollfd *revents
+
+# define SLAP_EVENT_INIT(t) do { \
+ revents = &slap_daemon[t].sd_pollfd[ dtblsize ]; \
+} while (0)
+
+# define SLAP_EVENT_WAIT(t, tvp, nsp) do { \
+ struct dvpoll sd_dvpoll; \
+ sd_dvpoll.dp_timeout = (tvp) ? ((tvp)->tv_sec * 1000 + (tvp)->tv_usec / 1000) : -1; \
+ sd_dvpoll.dp_nfds = dtblsize; \
+ sd_dvpoll.dp_fds = revents; \
+ *(nsp) = ioctl( slap_daemon[t].sd_dpfd, DP_POLL, &sd_dvpoll ); \
+} while (0)
+
+#else /* ! kqueue && ! epoll && ! /dev/poll */
+# ifdef HAVE_WINSOCK
+# define SLAP_EVENT_FNAME "WSselect"
+/* Winsock provides a "select" function but its fd_sets are
+ * actually arrays of sockets. Since these sockets are handles
+ * and not a contiguous range of small integers, we manage our
+ * own "fd" table of socket handles and use their indices as
+ * descriptors.
+ *
+ * All of our listener/connection structures use fds; the actual
+ * I/O functions use sockets. The SLAP_FD2SOCK macro in proto-slap.h
+ * handles the mapping.
+ *
+ * Despite the mapping overhead, this is about 45% more efficient
+ * than just using Winsock's select and FD_ISSET directly.
+ *
+ * Unfortunately Winsock's select implementation doesn't scale well
+ * as the number of connections increases. This probably needs to be
+ * rewritten to use the Winsock overlapped/asynchronous I/O functions.
+ */
+# define SLAP_EVENTS_ARE_INDEXED 1
+# define SLAP_EVENT_DECL fd_set readfds, writefds; char *rflags
+# define SLAP_EVENT_INIT(t) do { \
+ int i; \
+ FD_ZERO( &readfds ); \
+ FD_ZERO( &writefds ); \
+ rflags = slap_daemon[t].sd_rflags; \
+ memset( rflags, 0, slap_daemon[t].sd_nfds ); \
+ for ( i=0; i<slap_daemon[t].sd_nfds; i++ ) { \
+ if ( slap_daemon[t].sd_flags[i] & SD_READ ) \
+ FD_SET( slapd_ws_sockets[i], &readfds );\
+ if ( slap_daemon[t].sd_flags[i] & SD_WRITE ) \
+ FD_SET( slapd_ws_sockets[i], &writefds ); \
+ } } while ( 0 )
+
+# define SLAP_EVENT_MAX(t) slap_daemon[t].sd_nfds
+
+# define SLAP_EVENT_WAIT(t, tvp, nsp) do { \
+ int i; \
+ *(nsp) = select( SLAP_EVENT_MAX(t), &readfds, \
+ nwriters > 0 ? &writefds : NULL, NULL, (tvp) ); \
+ for ( i=0; i<readfds.fd_count; i++) { \
+ int fd = slapd_sock2fd(readfds.fd_array[i]); \
+ if ( fd >= 0 ) { \
+ slap_daemon[t].sd_rflags[fd] = SD_READ; \
+ if ( fd >= *(nsp)) *(nsp) = fd+1; \
+ } \
+ } \
+ for ( i=0; i<writefds.fd_count; i++) { \
+ int fd = slapd_sock2fd(writefds.fd_array[i]); \
+ if ( fd >= 0 ) { \
+ slap_daemon[t].sd_rflags[fd] = SD_WRITE; \
+ if ( fd >= *(nsp)) *(nsp) = fd+1; \
+ } \
+ } \
+} while (0)
+
+# define SLAP_EVENT_IS_READ(fd) (rflags[fd] & SD_READ)
+# define SLAP_EVENT_IS_WRITE(fd) (rflags[fd] & SD_WRITE)
+
+# define SLAP_EVENT_CLR_READ(fd) rflags[fd] &= ~SD_READ
+# define SLAP_EVENT_CLR_WRITE(fd) rflags[fd] &= ~SD_WRITE
+
+# define SLAP_SOCK_INIT(t) do { \
+ if (!t) { \
+ ldap_pvt_thread_mutex_init( &slapd_ws_mutex ); \
+ slapd_ws_sockets = ch_malloc( dtblsize * ( sizeof(SOCKET) + 2)); \
+ memset( slapd_ws_sockets, -1, dtblsize * sizeof(SOCKET) ); \
+ } \
+ slap_daemon[t].sd_flags = (char *)(slapd_ws_sockets + dtblsize); \
+ slap_daemon[t].sd_rflags = slap_daemon[t].sd_flags + dtblsize; \
+ memset( slap_daemon[t].sd_flags, 0, dtblsize ); \
+ slapd_ws_sockets[t*2] = wake_sds[t][0]; \
+ slapd_ws_sockets[t*2+1] = wake_sds[t][1]; \
+ wake_sds[t][0] = t*2; \
+ wake_sds[t][1] = t*2+1; \
+ slap_daemon[t].sd_nfds = t*2 + 2; \
+ } while ( 0 )
+
+# define SLAP_SOCK_INIT2()
+
+# define SLAP_SOCK_DESTROY(t) do { \
+ ch_free( slapd_ws_sockets ); slapd_ws_sockets = NULL; \
+ slap_daemon[t].sd_flags = NULL; \
+ slap_daemon[t].sd_rflags = NULL; \
+ ldap_pvt_thread_mutex_destroy( &slapd_ws_mutex ); \
+ } while ( 0 )
+
+# define SLAP_SOCK_IS_ACTIVE(t,fd) ( slap_daemon[t].sd_flags[fd] & SD_ACTIVE )
+# define SLAP_SOCK_IS_READ(t,fd) ( slap_daemon[t].sd_flags[fd] & SD_READ )
+# define SLAP_SOCK_IS_WRITE(t,fd) ( slap_daemon[t].sd_flags[fd] & SD_WRITE )
+# define SLAP_SOCK_NOT_ACTIVE(t,fd) (!slap_daemon[t].sd_flags[fd])
+
+# define SLAP_SOCK_SET_READ(t,fd) ( slap_daemon[t].sd_flags[fd] |= SD_READ )
+# define SLAP_SOCK_SET_WRITE(t,fd) ( slap_daemon[t].sd_flags[fd] |= SD_WRITE )
+
+# define SLAP_SELECT_ADDTEST(t,s) do { \
+ if ((s) >= slap_daemon[t].sd_nfds) slap_daemon[t].sd_nfds = (s)+1; \
+} while (0)
+
+# define SLAP_SOCK_CLR_READ(t,fd) ( slap_daemon[t].sd_flags[fd] &= ~SD_READ )
+# define SLAP_SOCK_CLR_WRITE(t,fd) ( slap_daemon[t].sd_flags[fd] &= ~SD_WRITE )
+
+# define SLAP_SOCK_ADD(t,s, l) do { \
+ SLAP_SELECT_ADDTEST(t,(s)); \
+ slap_daemon[t].sd_flags[s] = SD_ACTIVE|SD_READ; \
+} while ( 0 )
+
+# define SLAP_SOCK_DEL(t,s) do { \
+ slap_daemon[t].sd_flags[s] = 0; \
+ slapd_sockdel( s ); \
+} while ( 0 )
+
+# else /* !HAVE_WINSOCK */
+
+/**************************************
+ * Use select system call - select(2) *
+ **************************************/
+# define SLAP_EVENT_FNAME "select"
+/* select */
+# define SLAP_EVENTS_ARE_INDEXED 1
+# define SLAP_EVENT_DECL fd_set readfds, writefds
+
+# define SLAP_EVENT_INIT(t) do { \
+ AC_MEMCPY( &readfds, &slap_daemon[t].sd_readers, sizeof(fd_set) ); \
+ if ( nwriters ) { \
+ AC_MEMCPY( &writefds, &slap_daemon[t].sd_writers, sizeof(fd_set) ); \
+ } else { \
+ FD_ZERO( &writefds ); \
+ } \
+} while (0)
+
+# ifdef FD_SETSIZE
+# define SLAP_SELECT_CHK_SETSIZE do { \
+ if (dtblsize > FD_SETSIZE) dtblsize = FD_SETSIZE; \
+} while (0)
+# else /* ! FD_SETSIZE */
+# define SLAP_SELECT_CHK_SETSIZE do { ; } while (0)
+# endif /* ! FD_SETSIZE */
+
+# define SLAP_SOCK_INIT(t) do { \
+ SLAP_SELECT_CHK_SETSIZE; \
+ FD_ZERO(&slap_daemon[t].sd_actives); \
+ FD_ZERO(&slap_daemon[t].sd_readers); \
+ FD_ZERO(&slap_daemon[t].sd_writers); \
+} while (0)
+
+# define SLAP_SOCK_INIT2()
+
+# define SLAP_SOCK_DESTROY(t)
+
+# define SLAP_SOCK_IS_ACTIVE(t,fd) FD_ISSET((fd), &slap_daemon[t].sd_actives)
+# define SLAP_SOCK_IS_READ(t,fd) FD_ISSET((fd), &slap_daemon[t].sd_readers)
+# define SLAP_SOCK_IS_WRITE(t,fd) FD_ISSET((fd), &slap_daemon[t].sd_writers)
+
+# define SLAP_SOCK_NOT_ACTIVE(t,fd) (!SLAP_SOCK_IS_ACTIVE(t,fd) && \
+ !SLAP_SOCK_IS_READ(t,fd) && !SLAP_SOCK_IS_WRITE(t,fd))
+
+# define SLAP_SOCK_SET_READ(t,fd) FD_SET((fd), &slap_daemon[t].sd_readers)
+# define SLAP_SOCK_SET_WRITE(t,fd) FD_SET((fd), &slap_daemon[t].sd_writers)
+
+# define SLAP_EVENT_MAX(t) slap_daemon[t].sd_nfds
+# define SLAP_SELECT_ADDTEST(t,s) do { \
+ if ((s) >= slap_daemon[t].sd_nfds) slap_daemon[t].sd_nfds = (s)+1; \
+} while (0)
+
+# define SLAP_SOCK_CLR_READ(t,fd) FD_CLR((fd), &slap_daemon[t].sd_readers)
+# define SLAP_SOCK_CLR_WRITE(t,fd) FD_CLR((fd), &slap_daemon[t].sd_writers)
+
+# define SLAP_SOCK_ADD(t,s, l) do { \
+ SLAP_SELECT_ADDTEST(t,(s)); \
+ FD_SET((s), &slap_daemon[t].sd_actives); \
+ FD_SET((s), &slap_daemon[t].sd_readers); \
+} while (0)
+
+# define SLAP_SOCK_DEL(t,s) do { \
+ FD_CLR((s), &slap_daemon[t].sd_actives); \
+ FD_CLR((s), &slap_daemon[t].sd_readers); \
+ FD_CLR((s), &slap_daemon[t].sd_writers); \
+} while (0)
+
+# define SLAP_EVENT_IS_READ(fd) FD_ISSET((fd), &readfds)
+# define SLAP_EVENT_IS_WRITE(fd) FD_ISSET((fd), &writefds)
+
+# define SLAP_EVENT_CLR_READ(fd) FD_CLR((fd), &readfds)
+# define SLAP_EVENT_CLR_WRITE(fd) FD_CLR((fd), &writefds)
+
+# define SLAP_EVENT_WAIT(t, tvp, nsp) do { \
+ *(nsp) = select( SLAP_EVENT_MAX(t), &readfds, \
+ nwriters > 0 ? &writefds : NULL, NULL, (tvp) ); \
+} while (0)
+# endif /* !HAVE_WINSOCK */
+#endif /* ! kqueue && ! epoll && ! /dev/poll */
+
+#ifdef HAVE_SLP
+/*
+ * SLP related functions
+ */
+#include <slp.h>
+
+#define LDAP_SRVTYPE_PREFIX "service:ldap://"
+#define LDAPS_SRVTYPE_PREFIX "service:ldaps://"
+static char** slapd_srvurls = NULL;
+static SLPHandle slapd_hslp = 0;
+int slapd_register_slp = 0;
+const char *slapd_slp_attrs = NULL;
+
+static SLPError slapd_slp_cookie;
+
+static void
+slapd_slp_init( const char* urls )
+{
+ int i;
+ SLPError err;
+
+ slapd_srvurls = ldap_str2charray( urls, " " );
+
+ if ( slapd_srvurls == NULL ) return;
+
+ /* find and expand INADDR_ANY URLs */
+ for ( i = 0; slapd_srvurls[i] != NULL; i++ ) {
+ if ( strcmp( slapd_srvurls[i], "ldap:///" ) == 0 ) {
+ slapd_srvurls[i] = (char *) ch_realloc( slapd_srvurls[i],
+ global_host_bv.bv_len +
+ sizeof( LDAP_SRVTYPE_PREFIX ) );
+ strcpy( lutil_strcopy(slapd_srvurls[i],
+ LDAP_SRVTYPE_PREFIX ), global_host_bv.bv_val );
+ } else if ( strcmp( slapd_srvurls[i], "ldaps:///" ) == 0 ) {
+ slapd_srvurls[i] = (char *) ch_realloc( slapd_srvurls[i],
+ global_host_bv.bv_len +
+ sizeof( LDAPS_SRVTYPE_PREFIX ) );
+ strcpy( lutil_strcopy(slapd_srvurls[i],
+ LDAPS_SRVTYPE_PREFIX ), global_host_bv.bv_val );
+ }
+ }
+
+ /* open the SLP handle */
+ err = SLPOpen( "en", 0, &slapd_hslp );
+
+ if ( err != SLP_OK ) {
+ Debug( LDAP_DEBUG_CONNS, "daemon: SLPOpen() failed with %ld\n",
+ (long)err );
+ }
+}
+
+static void
+slapd_slp_deinit( void )
+{
+ if ( slapd_srvurls == NULL ) return;
+
+ ldap_charray_free( slapd_srvurls );
+ slapd_srvurls = NULL;
+
+ /* close the SLP handle */
+ SLPClose( slapd_hslp );
+}
+
+static void
+slapd_slp_regreport(
+ SLPHandle hslp,
+ SLPError errcode,
+ void *cookie )
+{
+ /* return the error code in the cookie */
+ *(SLPError*)cookie = errcode;
+}
+
+static void
+slapd_slp_reg()
+{
+ int i;
+ SLPError err;
+
+ if ( slapd_srvurls == NULL ) return;
+
+ for ( i = 0; slapd_srvurls[i] != NULL; i++ ) {
+ if ( strncmp( slapd_srvurls[i], LDAP_SRVTYPE_PREFIX,
+ sizeof( LDAP_SRVTYPE_PREFIX ) - 1 ) == 0 ||
+ strncmp( slapd_srvurls[i], LDAPS_SRVTYPE_PREFIX,
+ sizeof( LDAPS_SRVTYPE_PREFIX ) - 1 ) == 0 )
+ {
+ err = SLPReg( slapd_hslp,
+ slapd_srvurls[i],
+ SLP_LIFETIME_MAXIMUM,
+ "ldap",
+ (slapd_slp_attrs) ? slapd_slp_attrs : "",
+ SLP_TRUE,
+ slapd_slp_regreport,
+ &slapd_slp_cookie );
+
+ if ( err != SLP_OK || slapd_slp_cookie != SLP_OK ) {
+ Debug( LDAP_DEBUG_CONNS,
+ "daemon: SLPReg(%s) failed with %ld, cookie = %ld\n",
+ slapd_srvurls[i], (long)err, (long)slapd_slp_cookie );
+ }
+ }
+ }
+}
+
+static void
+slapd_slp_dereg( void )
+{
+ int i;
+ SLPError err;
+
+ if ( slapd_srvurls == NULL ) return;
+
+ for ( i = 0; slapd_srvurls[i] != NULL; i++ ) {
+ err = SLPDereg( slapd_hslp,
+ slapd_srvurls[i],
+ slapd_slp_regreport,
+ &slapd_slp_cookie );
+
+ if ( err != SLP_OK || slapd_slp_cookie != SLP_OK ) {
+ Debug( LDAP_DEBUG_CONNS,
+ "daemon: SLPDereg(%s) failed with %ld, cookie = %ld\n",
+ slapd_srvurls[i], (long)err, (long)slapd_slp_cookie );
+ }
+ }
+}
+#endif /* HAVE_SLP */
+
+#ifdef HAVE_WINSOCK
+/* Manage the descriptor to socket table */
+ber_socket_t
+slapd_socknew( ber_socket_t s )
+{
+ ber_socket_t i;
+ ldap_pvt_thread_mutex_lock( &slapd_ws_mutex );
+ for ( i = 0; i < dtblsize && slapd_ws_sockets[i] != INVALID_SOCKET; i++ );
+ if ( i == dtblsize ) {
+ WSASetLastError( WSAEMFILE );
+ } else {
+ slapd_ws_sockets[i] = s;
+ }
+ ldap_pvt_thread_mutex_unlock( &slapd_ws_mutex );
+ return i;
+}
+
+void
+slapd_sockdel( ber_socket_t s )
+{
+ ldap_pvt_thread_mutex_lock( &slapd_ws_mutex );
+ slapd_ws_sockets[s] = INVALID_SOCKET;
+ ldap_pvt_thread_mutex_unlock( &slapd_ws_mutex );
+}
+
+ber_socket_t
+slapd_sock2fd( ber_socket_t s )
+{
+ ber_socket_t i;
+ for ( i=0; i<dtblsize && slapd_ws_sockets[i] != s; i++);
+ if ( i == dtblsize )
+ i = -1;
+ return i;
+}
+#endif
+
+#ifdef DEBUG_CLOSE
+/* Was used to find a bug causing slapd's descriptors to be closed
+ * out from under it. Tracked it down to a long-standing (from 2009)
+ * bug in Heimdal https://github.com/heimdal/heimdal/issues/431 .
+ * Leaving this here for future use, if necessary.
+ */
+#include <dlfcn.h>
+#ifndef RTLD_NEXT
+#define RTLD_NEXT (void *)-1L
+#endif
+static char *newconns;
+typedef int (closefunc)(int fd);
+static closefunc *close_ptr;
+int close( int s )
+{
+ if (newconns) {
+ Debug( LDAP_DEBUG_CONNS,
+ "daemon: close(%d)\n", s );
+ if (s >= 0 && s < dtblsize && newconns[s])
+ assert(newconns[s] == 2);
+ }
+ return close_ptr ? close_ptr(s) : -1;
+}
+
+void slapd_debug_close()
+{
+ if (dtblsize)
+ newconns = ch_calloc(1, dtblsize);
+ close_ptr = dlsym(RTLD_NEXT, "close");
+}
+
+void slapd_set_close(int fd)
+{
+ newconns[fd] = 3;
+}
+#define SETUP_CLOSE() slapd_debug_close()
+#define SET_CLOSE(fd) slapd_set_close(fd)
+#define CLR_CLOSE(fd) if (newconns[fd]) newconns[fd]--
+#else
+#define SETUP_CLOSE(fd)
+#define SET_CLOSE(fd)
+#define CLR_CLOSE(fd)
+#endif
+
+/*
+ * Add a descriptor to daemon control
+ *
+ * If isactive, the descriptor is a live server session and is subject
+ * to idletimeout control. Otherwise, the descriptor is a passive
+ * listener or an outbound client session, and not subject to
+ * idletimeout. The underlying event handler may record the Listener
+ * argument to differentiate Listener's from real sessions.
+ */
+static void
+slapd_add( ber_socket_t s, int isactive, Listener *sl, int id )
+{
+ if (id < 0)
+ id = DAEMON_ID(s);
+ ldap_pvt_thread_mutex_lock( &slap_daemon[id].sd_mutex );
+
+ assert( SLAP_SOCK_NOT_ACTIVE(id, s) );
+
+ if ( isactive ) slap_daemon[id].sd_nactives++;
+
+ SLAP_SOCK_ADD(id, s, sl);
+
+ Debug( LDAP_DEBUG_CONNS, "daemon: added %ldr%s listener=%p\n",
+ (long) s, isactive ? " (active)" : "", (void *)sl );
+
+ ldap_pvt_thread_mutex_unlock( &slap_daemon[id].sd_mutex );
+
+ WAKE_LISTENER(id,1);
+}
+
+/*
+ * Remove the descriptor from daemon control
+ */
+void
+slapd_remove(
+ ber_socket_t s,
+ Sockbuf *sb,
+ int wasactive,
+ int wake,
+ int locked )
+{
+ int waswriter;
+ int wasreader;
+ int id = DAEMON_ID(s);
+
+ if ( !locked )
+ ldap_pvt_thread_mutex_lock( &slap_daemon[id].sd_mutex );
+
+ assert( SLAP_SOCK_IS_ACTIVE( id, s ));
+
+ if ( wasactive ) slap_daemon[id].sd_nactives--;
+
+ waswriter = SLAP_SOCK_IS_WRITE(id, s);
+ wasreader = SLAP_SOCK_IS_READ(id, s);
+
+ Debug( LDAP_DEBUG_CONNS, "daemon: removing %ld%s%s\n",
+ (long) s,
+ wasreader ? "r" : "",
+ waswriter ? "w" : "" );
+
+ if ( waswriter ) slap_daemon[id].sd_nwriters--;
+
+ SLAP_SOCK_DEL(id, s);
+ CLR_CLOSE(s);
+
+ if ( sb )
+ ber_sockbuf_free(sb);
+
+ /* If we ran out of file descriptors, we dropped a listener from
+ * the select() loop. Now that we're removing a session from our
+ * control, we can try to resume a dropped listener to use.
+ */
+ ldap_pvt_thread_mutex_lock( &emfile_mutex );
+ if ( emfile && listening ) {
+ int i;
+ for ( i = 0; slap_listeners[i] != NULL; i++ ) {
+ Listener *lr = slap_listeners[i];
+
+ if ( lr->sl_sd == AC_SOCKET_INVALID ) continue;
+ if ( lr->sl_sd == s ) continue;
+ if ( lr->sl_mute ) {
+ lr->sl_mute = 0;
+ emfile--;
+ if ( DAEMON_ID(lr->sl_sd) != id )
+ WAKE_LISTENER(DAEMON_ID(lr->sl_sd), wake);
+ break;
+ }
+ }
+ /* Walked the entire list without enabling anything; emfile
+ * counter is stale. Reset it.
+ */
+ if ( slap_listeners[i] == NULL ) emfile = 0;
+ }
+ ldap_pvt_thread_mutex_unlock( &emfile_mutex );
+ ldap_pvt_thread_mutex_unlock( &slap_daemon[id].sd_mutex );
+ WAKE_LISTENER(id, wake || slapd_gentle_shutdown == 2);
+}
+
+void
+slapd_clr_write( ber_socket_t s, int wake )
+{
+ int id = DAEMON_ID(s);
+ ldap_pvt_thread_mutex_lock( &slap_daemon[id].sd_mutex );
+
+ if ( SLAP_SOCK_IS_WRITE( id, s )) {
+ assert( SLAP_SOCK_IS_ACTIVE( id, s ));
+
+ SLAP_SOCK_CLR_WRITE( id, s );
+ slap_daemon[id].sd_nwriters--;
+ }
+
+ ldap_pvt_thread_mutex_unlock( &slap_daemon[id].sd_mutex );
+ WAKE_LISTENER(id,wake);
+}
+
+void
+slapd_set_write( ber_socket_t s, int wake )
+{
+ int id = DAEMON_ID(s);
+ ldap_pvt_thread_mutex_lock( &slap_daemon[id].sd_mutex );
+
+ assert( SLAP_SOCK_IS_ACTIVE( id, s ));
+
+ if ( !SLAP_SOCK_IS_WRITE( id, s )) {
+ SLAP_SOCK_SET_WRITE( id, s );
+ slap_daemon[id].sd_nwriters++;
+ }
+
+ ldap_pvt_thread_mutex_unlock( &slap_daemon[id].sd_mutex );
+ WAKE_LISTENER(id,wake);
+}
+
+int
+slapd_clr_read( ber_socket_t s, int wake )
+{
+ int rc = 1;
+ int id = DAEMON_ID(s);
+ ldap_pvt_thread_mutex_lock( &slap_daemon[id].sd_mutex );
+
+ if ( SLAP_SOCK_IS_ACTIVE( id, s )) {
+ SLAP_SOCK_CLR_READ( id, s );
+ rc = 0;
+ }
+ ldap_pvt_thread_mutex_unlock( &slap_daemon[id].sd_mutex );
+ if ( !rc )
+ WAKE_LISTENER(id,wake);
+ return rc;
+}
+
+void
+slapd_set_read( ber_socket_t s, int wake )
+{
+ int do_wake = 1;
+ int id = DAEMON_ID(s);
+ ldap_pvt_thread_mutex_lock( &slap_daemon[id].sd_mutex );
+
+ if( SLAP_SOCK_IS_ACTIVE( id, s ) && !SLAP_SOCK_IS_READ( id, s )) {
+ SLAP_SOCK_SET_READ( id, s );
+ } else {
+ do_wake = 0;
+ }
+ ldap_pvt_thread_mutex_unlock( &slap_daemon[id].sd_mutex );
+ if ( do_wake )
+ WAKE_LISTENER(id,wake);
+}
+
+static void
+slapd_close( ber_socket_t s )
+{
+ Debug( LDAP_DEBUG_CONNS, "daemon: closing %ld\n",
+ (long) s );
+ CLR_CLOSE( SLAP_FD2SOCK(s) );
+ tcp_close( SLAP_FD2SOCK(s) );
+#ifdef HAVE_WINSOCK
+ slapd_sockdel( s );
+#endif
+}
+
+void
+slapd_shutsock( ber_socket_t s )
+{
+ Debug( LDAP_DEBUG_CONNS, "daemon: shutdown socket %ld\n",
+ (long) s );
+ shutdown( SLAP_FD2SOCK(s), 2 );
+}
+
+static void
+slap_free_listener_addresses( struct sockaddr **sal )
+{
+ struct sockaddr **sap;
+ if (sal == NULL) return;
+ for (sap = sal; *sap != NULL; sap++) ch_free(*sap);
+ ch_free(sal);
+}
+
+#if defined(LDAP_PF_LOCAL) || defined(SLAP_X_LISTENER_MOD)
+static int
+get_url_perms(
+ char **exts,
+ mode_t *perms,
+ int *crit )
+{
+ int i;
+
+ assert( exts != NULL );
+ assert( perms != NULL );
+ assert( crit != NULL );
+
+ *crit = 0;
+ for ( i = 0; exts[ i ]; i++ ) {
+ char *type = exts[ i ];
+ int c = 0;
+
+ if ( type[ 0 ] == '!' ) {
+ c = 1;
+ type++;
+ }
+
+ if ( strncasecmp( type, LDAPI_MOD_URLEXT "=",
+ sizeof(LDAPI_MOD_URLEXT "=") - 1 ) == 0 )
+ {
+ char *value = type + ( sizeof(LDAPI_MOD_URLEXT "=") - 1 );
+ mode_t p = 0;
+ int j;
+
+ switch (strlen(value)) {
+ case 4:
+ /* skip leading '0' */
+ if ( value[ 0 ] != '0' ) return LDAP_OTHER;
+ value++;
+
+ case 3:
+ for ( j = 0; j < 3; j++) {
+ int v;
+
+ v = value[ j ] - '0';
+
+ if ( v < 0 || v > 7 ) return LDAP_OTHER;
+
+ p |= v << 3*(2-j);
+ }
+ break;
+
+ case 10:
+ for ( j = 1; j < 10; j++ ) {
+ static mode_t m[] = { 0,
+ S_IRUSR, S_IWUSR, S_IXUSR,
+ S_IRGRP, S_IWGRP, S_IXGRP,
+ S_IROTH, S_IWOTH, S_IXOTH
+ };
+ static const char c[] = "-rwxrwxrwx";
+
+ if ( value[ j ] == c[ j ] ) {
+ p |= m[ j ];
+
+ } else if ( value[ j ] != '-' ) {
+ return LDAP_OTHER;
+ }
+ }
+ break;
+
+ default:
+ return LDAP_OTHER;
+ }
+
+ *crit = c;
+ *perms = p;
+
+ return LDAP_SUCCESS;
+ }
+ }
+
+ return LDAP_OTHER;
+}
+#endif /* LDAP_PF_LOCAL || SLAP_X_LISTENER_MOD */
+
+/* port = 0 indicates AF_LOCAL */
+static int
+slap_get_listener_addresses(
+ const char *host,
+ unsigned short port,
+ struct sockaddr ***sal )
+{
+ struct sockaddr **sap;
+
+#ifdef LDAP_PF_LOCAL
+ if ( port == 0 ) {
+ sap = *sal = ch_malloc(2 * sizeof(void *));
+
+ *sap = ch_malloc(sizeof(struct sockaddr_un));
+ sap[1] = NULL;
+
+ if ( strlen(host) >
+ (sizeof(((struct sockaddr_un *)*sap)->sun_path) - 1) )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "daemon: domain socket path (%s) too long in URL",
+ host );
+ goto errexit;
+ }
+
+ (void)memset( (void *)*sap, '\0', sizeof(struct sockaddr_un) );
+ (*sap)->sa_family = AF_LOCAL;
+ strcpy( ((struct sockaddr_un *)*sap)->sun_path, host );
+ } else
+#endif /* LDAP_PF_LOCAL */
+ {
+#ifdef HAVE_GETADDRINFO
+ struct addrinfo hints, *res, *sai;
+ int n, err;
+ char serv[7];
+
+ memset( &hints, '\0', sizeof(hints) );
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = slap_inet4or6;
+ snprintf(serv, sizeof serv, "%d", port);
+
+ if ( (err = getaddrinfo(host, serv, &hints, &res)) ) {
+ Debug( LDAP_DEBUG_ANY, "daemon: getaddrinfo() failed: %s\n",
+ AC_GAI_STRERROR(err) );
+ return -1;
+ }
+
+ sai = res;
+ for (n=2; (sai = sai->ai_next) != NULL; n++) {
+ /* EMPTY */ ;
+ }
+ sap = *sal = ch_calloc(n, sizeof(void *));
+ *sap = NULL;
+
+ for ( sai=res; sai; sai=sai->ai_next ) {
+ if( sai->ai_addr == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "slap_get_listener_addresses: "
+ "getaddrinfo ai_addr is NULL?\n" );
+ freeaddrinfo(res);
+ goto errexit;
+ }
+
+ switch (sai->ai_family) {
+# ifdef LDAP_PF_INET6
+ case AF_INET6:
+ *sap = ch_malloc(sizeof(struct sockaddr_in6));
+ *(struct sockaddr_in6 *)*sap =
+ *((struct sockaddr_in6 *)sai->ai_addr);
+ break;
+# endif /* LDAP_PF_INET6 */
+ case AF_INET:
+ *sap = ch_malloc(sizeof(struct sockaddr_in));
+ *(struct sockaddr_in *)*sap =
+ *((struct sockaddr_in *)sai->ai_addr);
+ break;
+ default:
+ *sap = NULL;
+ break;
+ }
+
+ if (*sap != NULL) {
+ (*sap)->sa_family = sai->ai_family;
+ sap++;
+ *sap = NULL;
+ }
+ }
+
+ freeaddrinfo(res);
+
+#else /* ! HAVE_GETADDRINFO */
+ int i, n = 1;
+ struct in_addr in;
+ struct hostent *he = NULL;
+
+ if ( host == NULL ) {
+ in.s_addr = htonl(INADDR_ANY);
+
+ } else if ( !inet_aton( host, &in ) ) {
+ he = gethostbyname( host );
+ if( he == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "daemon: invalid host %s", host );
+ return -1;
+ }
+ for (n = 0; he->h_addr_list[n]; n++) /* empty */;
+ }
+
+ sap = *sal = ch_malloc((n+1) * sizeof(void *));
+
+ for ( i = 0; i<n; i++ ) {
+ sap[i] = ch_calloc(1, sizeof(struct sockaddr_in));
+ sap[i]->sa_family = AF_INET;
+ ((struct sockaddr_in *)sap[i])->sin_port = htons(port);
+ AC_MEMCPY( &((struct sockaddr_in *)sap[i])->sin_addr,
+ he ? (struct in_addr *)he->h_addr_list[i] : &in,
+ sizeof(struct in_addr) );
+ }
+ sap[i] = NULL;
+#endif /* ! HAVE_GETADDRINFO */
+ }
+
+ return 0;
+
+errexit:
+ slap_free_listener_addresses(*sal);
+ return -1;
+}
+
+static int
+slap_open_listener(
+ const char* url,
+ int *listeners,
+ int *cur )
+{
+ int num, tmp, rc;
+ Listener l;
+ Listener *li;
+ LDAPURLDesc *lud;
+ unsigned short port;
+ int err, addrlen = 0;
+ struct sockaddr **sal = NULL, **psal;
+ int socktype = SOCK_STREAM; /* default to COTS */
+ ber_socket_t s;
+ char ebuf[128];
+
+#if defined(LDAP_PF_LOCAL) || defined(SLAP_X_LISTENER_MOD)
+ /*
+ * use safe defaults
+ */
+ int crit = 1;
+#endif /* LDAP_PF_LOCAL || SLAP_X_LISTENER_MOD */
+
+ rc = ldap_url_parse( url, &lud );
+
+ if( rc != LDAP_URL_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "daemon: listen URL \"%s\" parse error=%d\n",
+ url, rc );
+ return rc;
+ }
+
+ l.sl_url.bv_val = NULL;
+ l.sl_mute = 0;
+ l.sl_busy = 0;
+
+#ifndef HAVE_TLS
+ if( ldap_pvt_url_scheme2tls( lud->lud_scheme ) ) {
+ Debug( LDAP_DEBUG_ANY, "daemon: TLS not supported (%s)\n",
+ url );
+ ldap_free_urldesc( lud );
+ return -1;
+ }
+
+ if(! lud->lud_port ) lud->lud_port = LDAP_PORT;
+
+#else /* HAVE_TLS */
+ l.sl_is_tls = ldap_pvt_url_scheme2tls( lud->lud_scheme );
+
+ if(! lud->lud_port ) {
+ lud->lud_port = l.sl_is_tls ? LDAPS_PORT : LDAP_PORT;
+ }
+#endif /* HAVE_TLS */
+
+ l.sl_is_proxied = ldap_pvt_url_scheme2proxied( lud->lud_scheme );
+
+#ifdef LDAP_TCP_BUFFER
+ l.sl_tcp_rmem = 0;
+ l.sl_tcp_wmem = 0;
+#endif /* LDAP_TCP_BUFFER */
+
+ port = (unsigned short) lud->lud_port;
+
+ tmp = ldap_pvt_url_scheme2proto(lud->lud_scheme);
+ if ( tmp == LDAP_PROTO_IPC ) {
+#ifdef LDAP_PF_LOCAL
+ if ( lud->lud_host == NULL || lud->lud_host[0] == '\0' ) {
+ err = slap_get_listener_addresses(LDAPI_SOCK, 0, &sal);
+ } else {
+ err = slap_get_listener_addresses(lud->lud_host, 0, &sal);
+ }
+#else /* ! LDAP_PF_LOCAL */
+
+ Debug( LDAP_DEBUG_ANY, "daemon: URL scheme not supported: %s",
+ url );
+ ldap_free_urldesc( lud );
+ return -1;
+#endif /* ! LDAP_PF_LOCAL */
+ } else {
+ if( lud->lud_host == NULL || lud->lud_host[0] == '\0'
+ || strcmp(lud->lud_host, "*") == 0 )
+ {
+ err = slap_get_listener_addresses(NULL, port, &sal);
+ } else {
+ err = slap_get_listener_addresses(lud->lud_host, port, &sal);
+ }
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ l.sl_is_udp = ( tmp == LDAP_PROTO_UDP );
+#endif /* LDAP_CONNECTIONLESS */
+
+#if defined(LDAP_PF_LOCAL) || defined(SLAP_X_LISTENER_MOD)
+ if ( lud->lud_exts ) {
+ err = get_url_perms( lud->lud_exts, &l.sl_perms, &crit );
+ } else {
+ l.sl_perms = S_IRWXU | S_IRWXO;
+ }
+#endif /* LDAP_PF_LOCAL || SLAP_X_LISTENER_MOD */
+
+ if ( lud->lud_dn && lud->lud_dn[0] ) {
+ sprintf( (char *)url, "%s://%s/", lud->lud_scheme, lud->lud_host );
+ Debug( LDAP_DEBUG_ANY, "daemon: listener URL %s<junk> DN must be absent (%s)\n",
+ url, lud->lud_dn );
+ ldap_free_urldesc( lud );
+ return -1;
+ }
+
+ ldap_free_urldesc( lud );
+ if ( err ) {
+ slap_free_listener_addresses(sal);
+ return -1;
+ }
+
+ /* If we got more than one address returned, we need to make space
+ * for it in the slap_listeners array.
+ */
+ for ( num=0; sal[num]; num++ ) /* empty */;
+ if ( num > 1 ) {
+ *listeners += num-1;
+ slap_listeners = ch_realloc( slap_listeners,
+ (*listeners + 1) * sizeof(Listener *) );
+ }
+
+ psal = sal;
+ while ( *sal != NULL ) {
+ char *af;
+ switch( (*sal)->sa_family ) {
+ case AF_INET:
+ af = "IPv4";
+ break;
+#ifdef LDAP_PF_INET6
+ case AF_INET6:
+ af = "IPv6";
+ break;
+#endif /* LDAP_PF_INET6 */
+#ifdef LDAP_PF_LOCAL
+ case AF_LOCAL:
+ af = "Local";
+ break;
+#endif /* LDAP_PF_LOCAL */
+ default:
+ sal++;
+ continue;
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ if( l.sl_is_udp ) socktype = SOCK_DGRAM;
+#endif /* LDAP_CONNECTIONLESS */
+
+ s = socket( (*sal)->sa_family, socktype, 0);
+ if ( s == AC_SOCKET_INVALID ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY,
+ "daemon: %s socket() failed errno=%d (%s)\n",
+ af, err, sock_errstr(err, ebuf, sizeof(ebuf)) );
+ sal++;
+ continue;
+ }
+ l.sl_sd = SLAP_SOCKNEW( s );
+
+ if ( l.sl_sd >= dtblsize ) {
+ Debug( LDAP_DEBUG_ANY,
+ "daemon: listener descriptor %ld is too great %ld\n",
+ (long) l.sl_sd, (long) dtblsize );
+ tcp_close( s );
+ sal++;
+ continue;
+ }
+
+#ifdef LDAP_PF_LOCAL
+ if ( (*sal)->sa_family == AF_LOCAL ) {
+ unlink( ((struct sockaddr_un *)*sal)->sun_path );
+ } else
+#endif /* LDAP_PF_LOCAL */
+ {
+#ifdef SO_REUSEADDR
+ /* enable address reuse */
+ tmp = 1;
+ rc = setsockopt( s, SOL_SOCKET, SO_REUSEADDR,
+ (char *) &tmp, sizeof(tmp) );
+ if ( rc == AC_SOCKET_ERROR ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "slapd(%ld): "
+ "setsockopt(SO_REUSEADDR) failed errno=%d (%s)\n",
+ (long) l.sl_sd, err, sock_errstr(err, ebuf, sizeof(ebuf)) );
+ }
+#endif /* SO_REUSEADDR */
+ }
+
+ switch( (*sal)->sa_family ) {
+ case AF_INET:
+ addrlen = sizeof(struct sockaddr_in);
+ break;
+#ifdef LDAP_PF_INET6
+ case AF_INET6:
+#ifdef IPV6_V6ONLY
+ /* Try to use IPv6 sockets for IPv6 only */
+ tmp = 1;
+ rc = setsockopt( s , IPPROTO_IPV6, IPV6_V6ONLY,
+ (char *) &tmp, sizeof(tmp) );
+ if ( rc == AC_SOCKET_ERROR ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY, "slapd(%ld): "
+ "setsockopt(IPV6_V6ONLY) failed errno=%d (%s)\n",
+ (long) l.sl_sd, err, sock_errstr(err, ebuf, sizeof(ebuf)) );
+ }
+#endif /* IPV6_V6ONLY */
+ addrlen = sizeof(struct sockaddr_in6);
+ break;
+#endif /* LDAP_PF_INET6 */
+
+#ifdef LDAP_PF_LOCAL
+ case AF_LOCAL:
+#ifdef LOCAL_CREDS
+ {
+ int one = 1;
+ setsockopt( s, 0, LOCAL_CREDS, &one, sizeof( one ) );
+ }
+#endif /* LOCAL_CREDS */
+
+ addrlen = sizeof( struct sockaddr_un );
+ break;
+#endif /* LDAP_PF_LOCAL */
+ }
+
+#ifdef LDAP_PF_LOCAL
+ /* create socket with all permissions set for those systems
+ * that honor permissions on sockets (e.g. Linux); typically,
+ * only write is required. To exploit filesystem permissions,
+ * place the socket in a directory and use directory's
+ * permissions. Need write perms to the directory to
+ * create/unlink the socket; likely need exec perms to access
+ * the socket (ITS#4709) */
+ {
+ mode_t old_umask = 0;
+
+ if ( (*sal)->sa_family == AF_LOCAL ) {
+ old_umask = umask( 0 );
+ }
+#endif /* LDAP_PF_LOCAL */
+ rc = bind( s, *sal, addrlen );
+#ifdef LDAP_PF_LOCAL
+ if ( old_umask != 0 ) {
+ umask( old_umask );
+ }
+ }
+#endif /* LDAP_PF_LOCAL */
+ if ( rc ) {
+ err = sock_errno();
+ Debug( LDAP_DEBUG_ANY,
+ "daemon: bind(%ld) failed errno=%d (%s)\n",
+ (long)l.sl_sd, err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ tcp_close( s );
+ sal++;
+ continue;
+ }
+
+ switch ( (*sal)->sa_family ) {
+#ifdef LDAP_PF_LOCAL
+ case AF_LOCAL: {
+ char *path = ((struct sockaddr_un *)*sal)->sun_path;
+ l.sl_name.bv_len = strlen(path) + STRLENOF("PATH=");
+ l.sl_name.bv_val = ch_malloc( l.sl_name.bv_len + 1 );
+ snprintf( l.sl_name.bv_val, l.sl_name.bv_len + 1,
+ "PATH=%s", path );
+ } break;
+#endif /* LDAP_PF_LOCAL */
+
+ case AF_INET: {
+ char addr[INET_ADDRSTRLEN];
+ const char *s;
+#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
+ s = inet_ntop( AF_INET, &((struct sockaddr_in *)*sal)->sin_addr,
+ addr, sizeof(addr) );
+#else /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ s = inet_ntoa( ((struct sockaddr_in *) *sal)->sin_addr );
+#endif /* ! HAVE_GETADDRINFO || ! HAVE_INET_NTOP */
+ if (!s) s = SLAP_STRING_UNKNOWN;
+ port = ntohs( ((struct sockaddr_in *)*sal) ->sin_port );
+ l.sl_name.bv_val =
+ ch_malloc( sizeof("IP=255.255.255.255:65535") );
+ snprintf( l.sl_name.bv_val, sizeof("IP=255.255.255.255:65535"),
+ "IP=%s:%d", s, port );
+ l.sl_name.bv_len = strlen( l.sl_name.bv_val );
+ } break;
+
+#ifdef LDAP_PF_INET6
+ case AF_INET6: {
+ char addr[INET6_ADDRSTRLEN];
+ const char *s;
+ s = inet_ntop( AF_INET6, &((struct sockaddr_in6 *)*sal)->sin6_addr,
+ addr, sizeof addr);
+ if (!s) s = SLAP_STRING_UNKNOWN;
+ port = ntohs( ((struct sockaddr_in6 *)*sal)->sin6_port );
+ l.sl_name.bv_len = strlen(s) + sizeof("IP=[]:65535");
+ l.sl_name.bv_val = ch_malloc( l.sl_name.bv_len );
+ snprintf( l.sl_name.bv_val, l.sl_name.bv_len, "IP=[%s]:%d",
+ s, port );
+ l.sl_name.bv_len = strlen( l.sl_name.bv_val );
+ } break;
+#endif /* LDAP_PF_INET6 */
+
+ default:
+ Debug( LDAP_DEBUG_ANY, "daemon: unsupported address family (%d)\n",
+ (int) (*sal)->sa_family );
+ break;
+ }
+
+ AC_MEMCPY(&l.sl_sa, *sal, addrlen);
+ ber_str2bv( url, 0, 1, &l.sl_url);
+ li = ch_malloc( sizeof( Listener ) );
+ *li = l;
+ slap_listeners[*cur] = li;
+ (*cur)++;
+ sal++;
+ }
+
+ slap_free_listener_addresses(psal);
+
+ if ( l.sl_url.bv_val == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "slap_open_listener: failed on %s\n", url );
+ return -1;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "daemon: listener initialized %s\n",
+ l.sl_url.bv_val );
+ return 0;
+}
+
+static int sockinit(void);
+static int sockdestroy(void);
+
+static int daemon_inited = 0;
+
+int
+slapd_daemon_init( const char *urls )
+{
+ int i, j, n, rc;
+ char **u;
+
+ Debug( LDAP_DEBUG_ARGS, "daemon_init: %s\n",
+ urls ? urls : "<null>" );
+
+ wake_sds = ch_malloc( slapd_daemon_threads * sizeof( sdpair ));
+ for ( i=0; i<slapd_daemon_threads; i++ ) {
+ wake_sds[i][0] = AC_SOCKET_INVALID;
+ wake_sds[i][1] = AC_SOCKET_INVALID;
+ }
+
+ slap_daemon = ch_calloc( slapd_daemon_threads, sizeof( slap_daemon_st ));
+ ldap_pvt_thread_mutex_init( &slap_daemon[0].sd_mutex );
+#ifdef HAVE_TCPD
+ ldap_pvt_thread_mutex_init( &sd_tcpd_mutex );
+#endif /* TCP Wrappers */
+ ldap_pvt_thread_mutex_init( &emfile_mutex );
+
+ daemon_inited = 1;
+
+ if( (rc = sockinit()) != 0 ) return rc;
+
+#ifdef HAVE_SYSCONF
+ dtblsize = sysconf( _SC_OPEN_MAX );
+#elif defined(HAVE_GETDTABLESIZE)
+ dtblsize = getdtablesize();
+#else /* ! HAVE_SYSCONF && ! HAVE_GETDTABLESIZE */
+ dtblsize = FD_SETSIZE;
+#endif /* ! HAVE_SYSCONF && ! HAVE_GETDTABLESIZE */
+
+ SETUP_CLOSE();
+
+ /* open a pipe (or something equivalent connected to itself).
+ * we write a byte on this fd whenever we catch a signal. The main
+ * loop will be select'ing on this socket, and will wake up when
+ * this byte arrives.
+ */
+ if( (rc = lutil_pair( wake_sds[0] )) < 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "daemon: lutil_pair() failed rc=%d\n", rc );
+ return rc;
+ }
+ ber_pvt_socket_set_nonblock( wake_sds[0][1], 1 );
+
+ SLAP_SOCK_INIT(0);
+
+ if( urls == NULL ) urls = "ldap:///";
+
+ u = ldap_str2charray( urls, " " );
+
+ if( u == NULL || u[0] == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "daemon_init: no urls (%s) provided.\n",
+ urls );
+ if ( u )
+ ldap_charray_free( u );
+ return -1;
+ }
+
+ for( i=0; u[i] != NULL; i++ ) {
+ Debug( LDAP_DEBUG_TRACE, "daemon_init: listen on %s\n",
+ u[i] );
+ }
+
+ if( i == 0 ) {
+ Debug( LDAP_DEBUG_ANY, "daemon_init: no listeners to open (%s)\n",
+ urls );
+ ldap_charray_free( u );
+ return -1;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "daemon_init: %d listeners to open...\n",
+ i );
+ slap_listeners = ch_malloc( (i+1)*sizeof(Listener *) );
+
+ for(n = 0, j = 0; u[n]; n++ ) {
+ if ( slap_open_listener( u[n], &i, &j ) ) {
+ ldap_charray_free( u );
+ return -1;
+ }
+ }
+ slap_listeners[j] = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "daemon_init: %d listeners opened\n",
+ i );
+
+
+#ifdef HAVE_SLP
+ if( slapd_register_slp ) {
+ slapd_slp_init( urls );
+ slapd_slp_reg();
+ }
+#endif /* HAVE_SLP */
+
+ ldap_charray_free( u );
+
+ return !i;
+}
+
+/* transfer control of active sockets from old to new listener threads */
+static void
+slapd_socket_realloc( int newnum )
+{
+ int i, j, oldid, newid;
+ int newmask = newnum - 1;
+ Listener *sl;
+ int num_listeners;
+
+ for ( i=0; slap_listeners[i] != NULL; i++ ) ;
+ num_listeners = i;
+
+ for ( i=0; i<dtblsize; i++ ) {
+ int skip = 0;
+
+ /* don't bother with wake_sds, they're assigned independent of mask */
+ for (j=0; j<slapd_daemon_threads; j++) {
+ if ( i == wake_sds[j][0] || i == wake_sds[j][1] ) {
+ skip = 1;
+ break;
+ }
+ }
+ if ( skip ) continue;
+
+ oldid = DAEMON_ID(i);
+ newid = i & newmask;
+ if ( oldid == newid ) continue;
+ if ( !SLAP_SOCK_IS_ACTIVE( oldid, i )) continue;
+ sl = NULL;
+ if ( num_listeners ) {
+ for ( j=0; slap_listeners[j] != NULL; j++ ) {
+ if ( slap_listeners[j]->sl_sd == i ) {
+ sl = slap_listeners[j];
+ num_listeners--;
+ break;
+ }
+ }
+ }
+ SLAP_SOCK_ADD( newid, i, sl );
+ if ( SLAP_SOCK_IS_READ( oldid, i )) {
+ SLAP_SOCK_SET_READ( newid, i );
+ }
+ if ( SLAP_SOCK_IS_WRITE( oldid, i )) {
+ SLAP_SOCK_SET_WRITE( newid, i );
+ slap_daemon[oldid].sd_nwriters--;
+ slap_daemon[newid].sd_nwriters++;
+ }
+ if ( connection_is_active( i )) {
+ slap_daemon[oldid].sd_nactives--;
+ slap_daemon[newid].sd_nactives++;
+ }
+ SLAP_SOCK_DEL( oldid, i );
+ }
+}
+
+
+int
+slapd_daemon_destroy( void )
+{
+ connections_destroy();
+ if ( daemon_inited ) {
+ int i;
+
+ for ( i=0; i<slapd_daemon_threads; i++ ) {
+#ifdef HAVE_WINSOCK
+ if ( wake_sds[i][1] != INVALID_SOCKET &&
+ SLAP_FD2SOCK( wake_sds[i][1] ) != SLAP_FD2SOCK( wake_sds[i][0] ))
+#endif /* HAVE_WINSOCK */
+ tcp_close( SLAP_FD2SOCK(wake_sds[i][1]) );
+#ifdef HAVE_WINSOCK
+ if ( wake_sds[i][0] != INVALID_SOCKET )
+#endif /* HAVE_WINSOCK */
+ tcp_close( SLAP_FD2SOCK(wake_sds[i][0]) );
+ ldap_pvt_thread_mutex_destroy( &slap_daemon[i].sd_mutex );
+ SLAP_SOCK_DESTROY(i);
+ }
+ daemon_inited = 0;
+ ldap_pvt_thread_mutex_destroy( &emfile_mutex );
+#ifdef HAVE_TCPD
+ ldap_pvt_thread_mutex_destroy( &sd_tcpd_mutex );
+#endif /* TCP Wrappers */
+ }
+ sockdestroy();
+
+#ifdef HAVE_SLP
+ if( slapd_register_slp ) {
+ slapd_slp_dereg();
+ slapd_slp_deinit();
+ }
+#endif /* HAVE_SLP */
+
+ return 0;
+}
+
+
+static void
+close_listeners(
+ int remove )
+{
+ int l;
+
+ if ( !listening )
+ return;
+ listening = 0;
+
+ for ( l = 0; slap_listeners[l] != NULL; l++ ) {
+ Listener *lr = slap_listeners[l];
+
+ if ( lr->sl_sd != AC_SOCKET_INVALID ) {
+ int s = lr->sl_sd;
+ lr->sl_sd = AC_SOCKET_INVALID;
+ if ( remove ) slapd_remove( s, NULL, 0, 0, 0 );
+
+#ifdef LDAP_PF_LOCAL
+ if ( lr->sl_sa.sa_addr.sa_family == AF_LOCAL ) {
+ unlink( lr->sl_sa.sa_un_addr.sun_path );
+ }
+#endif /* LDAP_PF_LOCAL */
+
+ slapd_close( s );
+ }
+ }
+}
+
+static void
+destroy_listeners( void )
+{
+ Listener *lr, **ll = slap_listeners;
+
+ if ( ll == NULL )
+ return;
+
+ while ( (lr = *ll++) != NULL ) {
+ if ( lr->sl_url.bv_val ) {
+ ber_memfree( lr->sl_url.bv_val );
+ }
+
+ if ( lr->sl_name.bv_val ) {
+ ber_memfree( lr->sl_name.bv_val );
+ }
+
+ free( lr );
+ }
+
+ free( slap_listeners );
+ slap_listeners = NULL;
+}
+
+static int
+slap_listener(
+ Listener *sl )
+{
+ Sockaddr from;
+
+ ber_socket_t s, sfd;
+ ber_socklen_t len = sizeof(from);
+ Connection *c;
+ slap_ssf_t ssf = 0;
+ struct berval authid = BER_BVNULL;
+#ifdef SLAPD_RLOOKUPS
+ char hbuf[NI_MAXHOST];
+#endif /* SLAPD_RLOOKUPS */
+
+ char *dnsname = NULL;
+ /* we assume INET6_ADDRSTRLEN > INET_ADDRSTRLEN */
+ char peername[LDAP_IPADDRLEN];
+ struct berval peerbv = BER_BVC(peername);
+#ifdef LDAP_PF_LOCAL_SENDMSG
+ char peerbuf[8];
+#endif
+ int cflag;
+ int tid;
+ char ebuf[128];
+
+ Debug( LDAP_DEBUG_TRACE,
+ ">>> slap_listener(%s)\n",
+ sl->sl_url.bv_val );
+
+ peername[0] = '\0';
+
+#ifdef LDAP_CONNECTIONLESS
+ if ( sl->sl_is_udp ) return 1;
+#endif /* LDAP_CONNECTIONLESS */
+
+# ifdef LDAP_PF_LOCAL
+ /* FIXME: apparently accept doesn't fill
+ * the sun_path sun_path member */
+ from.sa_un_addr.sun_path[0] = '\0';
+# endif /* LDAP_PF_LOCAL */
+
+ s = accept( SLAP_FD2SOCK( sl->sl_sd ), (struct sockaddr *) &from, &len );
+ if ( s != AC_SOCKET_INVALID ) {
+ SET_CLOSE(s);
+ }
+ Debug( LDAP_DEBUG_CONNS,
+ "daemon: accept() = %d\n", s );
+
+ /* Resume the listener FD to allow concurrent-processing of
+ * additional incoming connections.
+ */
+ sl->sl_busy = 0;
+ WAKE_LISTENER(DAEMON_ID(sl->sl_sd),1);
+
+ if ( s == AC_SOCKET_INVALID ) {
+ int err = sock_errno();
+
+ if(
+#ifdef EMFILE
+ err == EMFILE ||
+#endif /* EMFILE */
+#ifdef ENFILE
+ err == ENFILE ||
+#endif /* ENFILE */
+ 0 )
+ {
+ ldap_pvt_thread_mutex_lock( &emfile_mutex );
+ emfile++;
+ /* Stop listening until an existing session closes */
+ sl->sl_mute = 1;
+ ldap_pvt_thread_mutex_unlock( &emfile_mutex );
+ }
+
+ Debug( LDAP_DEBUG_ANY,
+ "daemon: accept(%ld) failed errno=%d (%s)\n",
+ (long) sl->sl_sd, err, sock_errstr(err, ebuf, sizeof(ebuf)) );
+ ldap_pvt_thread_yield();
+ return 0;
+ }
+ sfd = SLAP_SOCKNEW( s );
+
+ /* make sure descriptor number isn't too great */
+ if ( sfd >= dtblsize ) {
+ Debug( LDAP_DEBUG_ANY,
+ "daemon: %ld beyond descriptor table size %ld\n",
+ (long) sfd, (long) dtblsize );
+
+ tcp_close(s);
+ ldap_pvt_thread_yield();
+ return 0;
+ }
+ tid = DAEMON_ID(sfd);
+
+#ifdef LDAP_DEBUG
+ ldap_pvt_thread_mutex_lock( &slap_daemon[tid].sd_mutex );
+ /* newly accepted stream should not be in any of the FD SETS */
+ assert( SLAP_SOCK_NOT_ACTIVE( tid, sfd ));
+ ldap_pvt_thread_mutex_unlock( &slap_daemon[tid].sd_mutex );
+#endif /* LDAP_DEBUG */
+
+#if defined( SO_KEEPALIVE ) || defined( TCP_NODELAY )
+#ifdef LDAP_PF_LOCAL
+ /* for IPv4 and IPv6 sockets only */
+ if ( from.sa_addr.sa_family != AF_LOCAL )
+#endif /* LDAP_PF_LOCAL */
+ {
+ int rc;
+ int tmp;
+#ifdef SO_KEEPALIVE
+ /* enable keep alives */
+ tmp = 1;
+ rc = setsockopt( s, SOL_SOCKET, SO_KEEPALIVE,
+ (char *) &tmp, sizeof(tmp) );
+ if ( rc == AC_SOCKET_ERROR ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY,
+ "slapd(%ld): setsockopt(SO_KEEPALIVE) failed "
+ "errno=%d (%s)\n", (long) sfd, err, sock_errstr(err, ebuf, sizeof(ebuf)) );
+ slapd_close(sfd);
+ return 0;
+ }
+#endif /* SO_KEEPALIVE */
+#ifdef TCP_NODELAY
+ /* enable no delay */
+ tmp = 1;
+ rc = setsockopt( s, IPPROTO_TCP, TCP_NODELAY,
+ (char *)&tmp, sizeof(tmp) );
+ if ( rc == AC_SOCKET_ERROR ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY,
+ "slapd(%ld): setsockopt(TCP_NODELAY) failed "
+ "errno=%d (%s)\n", (long) sfd, err, sock_errstr(err, ebuf, sizeof(ebuf)) );
+ slapd_close(sfd);
+ return 0;
+ }
+#endif /* TCP_NODELAY */
+ }
+#endif /* SO_KEEPALIVE || TCP_NODELAY */
+
+ Debug( LDAP_DEBUG_CONNS,
+ "daemon: listen=%ld, new connection on %ld\n",
+ (long) sl->sl_sd, (long) sfd );
+
+ cflag = 0;
+ switch ( from.sa_addr.sa_family ) {
+# ifdef LDAP_PF_LOCAL
+ case AF_LOCAL:
+ cflag |= CONN_IS_IPC;
+
+ /* FIXME: apparently accept doesn't fill
+ * the sun_path sun_path member */
+ if ( from.sa_un_addr.sun_path[0] == '\0' ) {
+ AC_MEMCPY( from.sa_un_addr.sun_path,
+ sl->sl_sa.sa_un_addr.sun_path,
+ sizeof( from.sa_un_addr.sun_path ) );
+ }
+
+ sprintf( peername, "PATH=%s", from.sa_un_addr.sun_path );
+ ssf = local_ssf;
+ {
+ uid_t uid;
+ gid_t gid;
+
+#ifdef LDAP_PF_LOCAL_SENDMSG
+ peerbv.bv_val = peerbuf;
+ peerbv.bv_len = sizeof( peerbuf );
+#endif
+ if( LUTIL_GETPEEREID( s, &uid, &gid, &peerbv ) == 0 ) {
+ authid.bv_val = ch_malloc(
+ STRLENOF( "gidNumber=4294967295+uidNumber=4294967295,"
+ "cn=peercred,cn=external,cn=auth" ) + 1 );
+ authid.bv_len = sprintf( authid.bv_val,
+ "gidNumber=%d+uidNumber=%d,"
+ "cn=peercred,cn=external,cn=auth",
+ (int) gid, (int) uid );
+ assert( authid.bv_len <=
+ STRLENOF( "gidNumber=4294967295+uidNumber=4294967295,"
+ "cn=peercred,cn=external,cn=auth" ) );
+ }
+ }
+ dnsname = "local";
+ break;
+#endif /* LDAP_PF_LOCAL */
+
+# ifdef LDAP_PF_INET6
+ case AF_INET6:
+# endif /* LDAP_PF_INET6 */
+ case AF_INET:
+ if ( sl->sl_is_proxied ) {
+ if ( !proxyp( sfd, &from ) ) {
+ Debug( LDAP_DEBUG_ANY, "slapd(%ld): proxyp failed\n", (long)sfd );
+ slapd_close( sfd );
+ return 0;
+ }
+ }
+ ldap_pvt_sockaddrstr( &from, &peerbv );
+ break;
+
+ default:
+ slapd_close(sfd);
+ return 0;
+ }
+
+ if ( ( from.sa_addr.sa_family == AF_INET )
+#ifdef LDAP_PF_INET6
+ || ( from.sa_addr.sa_family == AF_INET6 )
+#endif /* LDAP_PF_INET6 */
+ )
+ {
+ dnsname = NULL;
+#ifdef SLAPD_RLOOKUPS
+ if ( use_reverse_lookup ) {
+ char *herr;
+ if (ldap_pvt_get_hname( (const struct sockaddr *)&from, len, hbuf,
+ sizeof(hbuf), &herr ) == 0) {
+ ldap_pvt_str2lower( hbuf );
+ dnsname = hbuf;
+ }
+ }
+#endif /* SLAPD_RLOOKUPS */
+
+#ifdef HAVE_TCPD
+ {
+ int rc;
+ char *peeraddr, *paend;
+ peeraddr = peerbv.bv_val + 3;
+ if ( *peeraddr == '[' ) {
+ peeraddr++;
+ paend = strrchr( peeraddr, ']' );
+ } else {
+ paend = strrchr( peeraddr, ':' );
+ }
+ if ( paend )
+ *paend = '\0';
+ ldap_pvt_thread_mutex_lock( &sd_tcpd_mutex );
+ rc = hosts_ctl("slapd",
+ dnsname != NULL ? dnsname : SLAP_STRING_UNKNOWN,
+ peeraddr,
+ SLAP_STRING_UNKNOWN );
+ ldap_pvt_thread_mutex_unlock( &sd_tcpd_mutex );
+ if ( !rc ) {
+ /* DENY ACCESS */
+ Debug( LDAP_DEBUG_STATS,
+ "fd=%ld DENIED from %s (%s)\n",
+ (long) sfd,
+ dnsname != NULL ? dnsname : SLAP_STRING_UNKNOWN,
+ peeraddr );
+ slapd_close(sfd);
+ return 0;
+ }
+ if ( paend ) {
+ if ( peeraddr[-1] == '[' )
+ *paend = ']';
+ else
+ *paend = ':';
+ }
+ }
+#endif /* HAVE_TCPD */
+ }
+
+#ifdef HAVE_TLS
+ if ( sl->sl_is_tls ) cflag |= CONN_IS_TLS;
+#endif
+ c = connection_init(sfd, sl,
+ dnsname != NULL ? dnsname : SLAP_STRING_UNKNOWN,
+ peername, cflag, ssf,
+ authid.bv_val ? &authid : NULL
+ LDAP_PF_LOCAL_SENDMSG_ARG(&peerbv));
+
+ if( authid.bv_val ) ch_free(authid.bv_val);
+
+ if( !c ) {
+ Debug( LDAP_DEBUG_ANY,
+ "daemon: connection_init(%ld, %s, %s) failed.\n",
+ (long) sfd, peername, sl->sl_name.bv_val );
+ slapd_close(sfd);
+ }
+
+ return 0;
+}
+
+static void*
+slap_listener_thread(
+ void* ctx,
+ void* ptr )
+{
+ int rc;
+ Listener *sl = (Listener *)ptr;
+
+ rc = slap_listener( sl );
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "slap_listener_thread(%s): failed err=%d",
+ sl->sl_url.bv_val, rc );
+ }
+
+ return (void*)NULL;
+}
+
+static int
+slap_listener_activate(
+ Listener* sl )
+{
+ int rc;
+
+ Debug( LDAP_DEBUG_TRACE, "slap_listener_activate(%d): %s\n",
+ sl->sl_sd, sl->sl_busy ? "busy" : "" );
+
+ sl->sl_busy = 1;
+
+ rc = ldap_pvt_thread_pool_submit( &connection_pool,
+ slap_listener_thread, (void *) sl );
+
+ if( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "slap_listener_activate(%d): submit failed (%d)\n",
+ sl->sl_sd, rc );
+ }
+ return rc;
+}
+
+static void *
+slapd_rtask_trampoline(
+ void *ctx,
+ void *arg )
+{
+ struct re_s *rtask = arg;
+
+ /* invalidate pool_cookie */
+ rtask->pool_cookie = NULL;
+ return rtask->routine( ctx, arg );
+}
+
+static void *
+slapd_daemon_task(
+ void *ptr )
+{
+ int l;
+ time_t last_idle_check = 0;
+ int ebadf = 0;
+ int tid = (slap_daemon_st *) ptr - slap_daemon;
+ char ebuf[128];
+
+#define SLAPD_IDLE_CHECK_LIMIT 4
+
+ slapd_add( wake_sds[tid][0], 0, NULL, tid );
+ if ( tid )
+ goto loop;
+
+ /* Init stuff done only by thread 0 */
+
+ last_idle_check = slap_get_time();
+
+ for ( l = 0; slap_listeners[l] != NULL; l++ ) {
+ if ( slap_listeners[l]->sl_sd == AC_SOCKET_INVALID ) continue;
+
+#ifdef LDAP_CONNECTIONLESS
+ /* Since this is connectionless, the data port is the
+ * listening port. The listen() and accept() calls
+ * are unnecessary.
+ */
+ if ( slap_listeners[l]->sl_is_udp )
+ continue;
+#endif /* LDAP_CONNECTIONLESS */
+
+ /* FIXME: TCP-only! */
+#ifdef LDAP_TCP_BUFFER
+ if ( 1 ) {
+ int origsize, size, realsize, rc;
+ socklen_t optlen;
+
+ size = 0;
+ if ( slap_listeners[l]->sl_tcp_rmem > 0 ) {
+ size = slap_listeners[l]->sl_tcp_rmem;
+ } else if ( slapd_tcp_rmem > 0 ) {
+ size = slapd_tcp_rmem;
+ }
+
+ if ( size > 0 ) {
+ optlen = sizeof( origsize );
+ rc = getsockopt( SLAP_FD2SOCK( slap_listeners[l]->sl_sd ),
+ SOL_SOCKET,
+ SO_RCVBUF,
+ (void *)&origsize,
+ &optlen );
+
+ if ( rc ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY,
+ "slapd_daemon_task: getsockopt(SO_RCVBUF) failed errno=%d (%s)\n",
+ err, sock_errstr(err, ebuf, sizeof(ebuf)) );
+ }
+
+ optlen = sizeof( size );
+ rc = setsockopt( SLAP_FD2SOCK( slap_listeners[l]->sl_sd ),
+ SOL_SOCKET,
+ SO_RCVBUF,
+ (const void *)&size,
+ optlen );
+
+ if ( rc ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY,
+ "slapd_daemon_task: setsockopt(SO_RCVBUF) failed errno=%d (%s)\n",
+ err, sock_errstr(err, ebuf, sizeof(ebuf)) );
+ }
+
+ optlen = sizeof( realsize );
+ rc = getsockopt( SLAP_FD2SOCK( slap_listeners[l]->sl_sd ),
+ SOL_SOCKET,
+ SO_RCVBUF,
+ (void *)&realsize,
+ &optlen );
+
+ if ( rc ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY,
+ "slapd_daemon_task: getsockopt(SO_RCVBUF) failed errno=%d (%s)\n",
+ err, sock_errstr(err, ebuf, sizeof(ebuf)) );
+ }
+
+ Debug(LDAP_DEBUG_ANY,
+ "slapd_daemon_task: url=%s (#%d) RCVBUF original size=%d requested size=%d real size=%d\n",
+ slap_listeners[l]->sl_url.bv_val, l,
+ origsize, size, realsize );
+ }
+
+ size = 0;
+ if ( slap_listeners[l]->sl_tcp_wmem > 0 ) {
+ size = slap_listeners[l]->sl_tcp_wmem;
+ } else if ( slapd_tcp_wmem > 0 ) {
+ size = slapd_tcp_wmem;
+ }
+
+ if ( size > 0 ) {
+ optlen = sizeof( origsize );
+ rc = getsockopt( SLAP_FD2SOCK( slap_listeners[l]->sl_sd ),
+ SOL_SOCKET,
+ SO_SNDBUF,
+ (void *)&origsize,
+ &optlen );
+
+ if ( rc ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY,
+ "slapd_daemon_task: getsockopt(SO_SNDBUF) failed errno=%d (%s)\n",
+ err, sock_errstr(err, ebuf, sizeof(ebuf)) );
+ }
+
+ optlen = sizeof( size );
+ rc = setsockopt( SLAP_FD2SOCK( slap_listeners[l]->sl_sd ),
+ SOL_SOCKET,
+ SO_SNDBUF,
+ (const void *)&size,
+ optlen );
+
+ if ( rc ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY,
+ "slapd_daemon_task: setsockopt(SO_SNDBUF) failed errno=%d (%s)",
+ err, sock_errstr(err, ebuf, sizeof(ebuf)) );
+ }
+
+ optlen = sizeof( realsize );
+ rc = getsockopt( SLAP_FD2SOCK( slap_listeners[l]->sl_sd ),
+ SOL_SOCKET,
+ SO_SNDBUF,
+ (void *)&realsize,
+ &optlen );
+
+ if ( rc ) {
+ int err = sock_errno();
+ Debug( LDAP_DEBUG_ANY,
+ "slapd_daemon_task: getsockopt(SO_SNDBUF) failed errno=%d (%s)\n",
+ err, sock_errstr(err, ebuf, sizeof(ebuf)) );
+ }
+
+ Debug(LDAP_DEBUG_ANY,
+ "slapd_daemon_task: url=%s (#%d) SNDBUF original size=%d requested size=%d real size=%d\n",
+ slap_listeners[l]->sl_url.bv_val, l,
+ origsize, size, realsize );
+ }
+ }
+#endif /* LDAP_TCP_BUFFER */
+
+ if ( listen( SLAP_FD2SOCK( slap_listeners[l]->sl_sd ), SLAPD_LISTEN_BACKLOG ) == -1 ) {
+ int err = sock_errno();
+
+#ifdef LDAP_PF_INET6
+ /* If error is EADDRINUSE, we are trying to listen to INADDR_ANY and
+ * we are already listening to in6addr_any, then we want to ignore
+ * this and continue.
+ */
+ if ( err == EADDRINUSE ) {
+ int i;
+ struct sockaddr_in sa = slap_listeners[l]->sl_sa.sa_in_addr;
+ struct sockaddr_in6 sa6;
+
+ if ( sa.sin_family == AF_INET &&
+ sa.sin_addr.s_addr == htonl(INADDR_ANY) ) {
+ for ( i = 0 ; i < l; i++ ) {
+ sa6 = slap_listeners[i]->sl_sa.sa_in6_addr;
+ if ( sa6.sin6_family == AF_INET6 &&
+ !memcmp( &sa6.sin6_addr, &in6addr_any,
+ sizeof(struct in6_addr) ) )
+ {
+ break;
+ }
+ }
+
+ if ( i < l ) {
+ /* We are already listening to in6addr_any */
+ Debug( LDAP_DEBUG_CONNS,
+ "daemon: Attempt to listen to 0.0.0.0 failed, "
+ "already listening on ::, assuming IPv4 included\n" );
+ slapd_close( slap_listeners[l]->sl_sd );
+ slap_listeners[l]->sl_sd = AC_SOCKET_INVALID;
+ continue;
+ }
+ }
+ }
+#endif /* LDAP_PF_INET6 */
+ Debug( LDAP_DEBUG_ANY,
+ "daemon: listen(%s, 5) failed errno=%d (%s)\n",
+ slap_listeners[l]->sl_url.bv_val, err,
+ sock_errstr(err, ebuf, sizeof(ebuf)) );
+ ldap_pvt_thread_mutex_lock( &slapd_init_mutex );
+ slapd_shutdown = 2;
+ ldap_pvt_thread_cond_signal( &slapd_init_cond );
+ ldap_pvt_thread_mutex_unlock( &slapd_init_mutex );
+ return (void*)-1;
+ }
+
+ /* make the listening socket non-blocking */
+ if ( ber_pvt_socket_set_nonblock( SLAP_FD2SOCK( slap_listeners[l]->sl_sd ), 1 ) < 0 ) {
+ Debug( LDAP_DEBUG_ANY, "slapd_daemon_task: "
+ "set nonblocking on a listening socket failed\n" );
+ ldap_pvt_thread_mutex_lock( &slapd_init_mutex );
+ slapd_shutdown = 2;
+ ldap_pvt_thread_cond_signal( &slapd_init_cond );
+ ldap_pvt_thread_mutex_unlock( &slapd_init_mutex );
+ return (void*)-1;
+ }
+
+ slapd_add( slap_listeners[l]->sl_sd, 0, slap_listeners[l], -1 );
+ }
+
+ ldap_pvt_thread_mutex_lock( &slapd_init_mutex );
+ slapd_ready = 1;
+ ldap_pvt_thread_cond_signal( &slapd_init_cond );
+ ldap_pvt_thread_mutex_unlock( &slapd_init_mutex );
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+ if ( started_event != NULL ) {
+ ldap_pvt_thread_cond_signal( &started_event );
+ }
+#endif /* HAVE_NT_SERVICE_MANAGER */
+
+loop:
+
+ /* initialization complete. Here comes the loop. */
+
+ while ( !slapd_shutdown ) {
+ ber_socket_t i;
+ int ns, nwriters;
+ int at;
+ ber_socket_t nfds;
+#if SLAP_EVENTS_ARE_INDEXED
+ ber_socket_t nrfds, nwfds;
+#endif /* SLAP_EVENTS_ARE_INDEXED */
+#define SLAPD_EBADF_LIMIT 16
+
+ time_t now;
+
+ SLAP_EVENT_DECL;
+
+ struct timeval tv;
+ struct timeval *tvp;
+
+ struct timeval cat;
+ time_t tdelta = 1;
+ struct re_s* rtask;
+
+ now = slap_get_time();
+
+ if ( !tid && ( global_idletimeout > 0 )) {
+ int check = 0;
+ /* Set the select timeout.
+ * Don't just truncate, preserve the fractions of
+ * seconds to prevent sleeping for zero time.
+ */
+ {
+ tv.tv_sec = global_idletimeout / SLAPD_IDLE_CHECK_LIMIT;
+ tv.tv_usec = global_idletimeout - \
+ ( tv.tv_sec * SLAPD_IDLE_CHECK_LIMIT );
+ tv.tv_usec *= 1000000 / SLAPD_IDLE_CHECK_LIMIT;
+ if ( difftime( last_idle_check +
+ global_idletimeout/SLAPD_IDLE_CHECK_LIMIT, now ) < 0 )
+ check = 1;
+ }
+ if ( check ) {
+ connections_timeout_idle( now );
+ last_idle_check = now;
+ }
+ } else {
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ }
+
+#ifdef SIGHUP
+ if ( slapd_gentle_shutdown ) {
+ ber_socket_t active;
+
+ if ( !tid && slapd_gentle_shutdown == 1 ) {
+ BackendDB *be;
+ Debug( LDAP_DEBUG_ANY, "slapd gentle shutdown\n" );
+ close_listeners( 1 );
+ frontendDB->be_restrictops |= SLAP_RESTRICT_OP_WRITES;
+ LDAP_STAILQ_FOREACH(be, &backendDB, be_next) {
+ be->be_restrictops |= SLAP_RESTRICT_OP_WRITES;
+ }
+ slapd_gentle_shutdown = 2;
+ }
+
+ ldap_pvt_thread_mutex_lock( &slap_daemon[tid].sd_mutex );
+ active = slap_daemon[tid].sd_nactives;
+ ldap_pvt_thread_mutex_unlock( &slap_daemon[tid].sd_mutex );
+
+ if ( active == 0 ) {
+ if ( !tid ) {
+ for ( l=1; l<slapd_daemon_threads; l++ ) {
+ ldap_pvt_thread_mutex_lock( &slap_daemon[l].sd_mutex );
+ active += slap_daemon[l].sd_nactives;
+ ldap_pvt_thread_mutex_unlock( &slap_daemon[l].sd_mutex );
+ }
+ if ( !active )
+ slapd_shutdown = 1;
+ }
+ if ( !active )
+ break;
+ }
+ }
+#endif /* SIGHUP */
+ at = 0;
+
+ ldap_pvt_thread_mutex_lock( &slap_daemon[tid].sd_mutex );
+
+ nwriters = slap_daemon[tid].sd_nwriters;
+
+ if ( listening )
+ for ( l = 0; slap_listeners[l] != NULL; l++ ) {
+ Listener *lr = slap_listeners[l];
+
+ if ( lr->sl_sd == AC_SOCKET_INVALID ) continue;
+ if ( DAEMON_ID( lr->sl_sd ) != tid ) continue;
+ if ( !SLAP_SOCK_IS_ACTIVE( tid, lr->sl_sd )) continue;
+
+ if ( lr->sl_mute || lr->sl_busy )
+ {
+ SLAP_SOCK_CLR_READ( tid, lr->sl_sd );
+ } else {
+ SLAP_SOCK_SET_READ( tid, lr->sl_sd );
+ }
+ }
+
+ SLAP_EVENT_INIT(tid);
+
+ nfds = SLAP_EVENT_MAX(tid);
+
+ if (( global_idletimeout ) && slap_daemon[tid].sd_nactives ) at = 1;
+
+ ldap_pvt_thread_mutex_unlock( &slap_daemon[tid].sd_mutex );
+
+ if ( at
+#if defined(HAVE_YIELDING_SELECT)
+ && ( tv.tv_sec || tv.tv_usec )
+#endif /* HAVE_YIELDING_SELECT */
+ )
+ {
+ tvp = &tv;
+ } else {
+ tvp = NULL;
+ }
+
+ /* Only thread 0 handles runqueue */
+ if ( !tid ) {
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ rtask = ldap_pvt_runqueue_next_sched( &slapd_rq, &cat );
+ while ( rtask && cat.tv_sec && cat.tv_sec <= now ) {
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
+ ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 );
+ } else {
+ ldap_pvt_runqueue_runtask( &slapd_rq, rtask );
+ ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ ldap_pvt_thread_pool_submit2( &connection_pool,
+ slapd_rtask_trampoline, (void *) rtask, &rtask->pool_cookie );
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ }
+ rtask = ldap_pvt_runqueue_next_sched( &slapd_rq, &cat );
+ }
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+
+ if ( rtask && cat.tv_sec ) {
+ /* NOTE: diff __should__ always be >= 0,
+ * AFAI understand; however (ITS#4872),
+ * time_t might be unsigned in some systems,
+ * while difftime() returns a double */
+ double diff = difftime( cat.tv_sec, now );
+ if ( diff <= 0 ) {
+ diff = tdelta;
+ }
+ if ( tvp == NULL || diff < tv.tv_sec ) {
+ tv.tv_sec = diff;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ }
+ }
+ }
+
+ for ( l = 0; slap_listeners[l] != NULL; l++ ) {
+ Listener *lr = slap_listeners[l];
+
+ if ( lr->sl_sd == AC_SOCKET_INVALID ) {
+ continue;
+ }
+
+ if ( DAEMON_ID( lr->sl_sd ) != tid ) continue;
+
+ if ( lr->sl_mute ) {
+ Debug( LDAP_DEBUG_CONNS,
+ "daemon: " SLAP_EVENT_FNAME ": "
+ "listen=%d muted\n",
+ lr->sl_sd );
+ continue;
+ }
+
+ if ( lr->sl_busy ) {
+ Debug( LDAP_DEBUG_CONNS,
+ "daemon: " SLAP_EVENT_FNAME ": "
+ "listen=%d busy\n",
+ lr->sl_sd );
+ continue;
+ }
+
+ Debug( LDAP_DEBUG_CONNS,
+ "daemon: " SLAP_EVENT_FNAME ": "
+ "listen=%d active_threads=%d tvp=%s\n",
+ lr->sl_sd, at, tvp == NULL ? "NULL" : "zero" );
+ }
+
+ SLAP_EVENT_WAIT( tid, tvp, &ns );
+ switch ( ns ) {
+ case -1: { /* failure - try again */
+ int err = sock_errno();
+
+ if ( err != EINTR ) {
+ ebadf++;
+
+ /* Don't log unless we got it twice in a row */
+ if ( !( ebadf & 1 ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "daemon: "
+ SLAP_EVENT_FNAME
+ " failed count %d "
+ "err (%d): %s\n",
+ ebadf, err,
+ sock_errstr( err, ebuf, sizeof(ebuf) ) );
+ }
+ if ( ebadf >= SLAPD_EBADF_LIMIT ) {
+ slapd_shutdown = 2;
+ }
+ }
+ }
+ continue;
+
+ case 0: /* timeout - let threads run */
+ ebadf = 0;
+#ifndef HAVE_YIELDING_SELECT
+ Debug( LDAP_DEBUG_CONNS, "daemon: " SLAP_EVENT_FNAME
+ "timeout - yielding\n" );
+
+ ldap_pvt_thread_yield();
+#endif /* ! HAVE_YIELDING_SELECT */
+ continue;
+
+ default: /* something happened - deal with it */
+ if ( slapd_shutdown ) continue;
+
+ ebadf = 0;
+ Debug( LDAP_DEBUG_CONNS,
+ "daemon: activity on %d descriptor%s\n",
+ ns, ns != 1 ? "s" : "" );
+ /* FALL THRU */
+ }
+
+#if SLAP_EVENTS_ARE_INDEXED
+ if ( SLAP_EVENT_IS_READ( wake_sds[tid][0] ) ) {
+ char c[BUFSIZ];
+ SLAP_EVENT_CLR_READ( wake_sds[tid][0] );
+ waking = 0;
+ tcp_read( SLAP_FD2SOCK(wake_sds[tid][0]), c, sizeof(c) );
+ Debug( LDAP_DEBUG_CONNS, "daemon: waked\n" );
+ continue;
+ }
+
+ /* The event slot equals the descriptor number - this is
+ * true for Unix select and poll. We treat Windows select
+ * like this too, even though it's a kludge.
+ */
+ if ( listening )
+ for ( l = 0; slap_listeners[l] != NULL; l++ ) {
+ int rc;
+
+ if ( ns <= 0 ) break;
+ if ( slap_listeners[l]->sl_sd == AC_SOCKET_INVALID ) continue;
+ if ( DAEMON_ID( slap_listeners[l]->sl_sd ) != tid ) continue;
+#ifdef LDAP_CONNECTIONLESS
+ if ( slap_listeners[l]->sl_is_udp ) continue;
+#endif /* LDAP_CONNECTIONLESS */
+ if ( !SLAP_EVENT_IS_READ( slap_listeners[l]->sl_sd ) ) continue;
+
+ /* clear events */
+ SLAP_EVENT_CLR_READ( slap_listeners[l]->sl_sd );
+ SLAP_EVENT_CLR_WRITE( slap_listeners[l]->sl_sd );
+ ns--;
+
+ rc = slap_listener_activate( slap_listeners[l] );
+ }
+
+ /* bypass the following tests if no descriptors left */
+ if ( ns <= 0 ) {
+#ifndef HAVE_YIELDING_SELECT
+ ldap_pvt_thread_yield();
+#endif /* HAVE_YIELDING_SELECT */
+ continue;
+ }
+
+ Debug( LDAP_DEBUG_CONNS, "daemon: activity on:" );
+ nrfds = 0;
+ nwfds = 0;
+ for ( i = 0; i < nfds; i++ ) {
+ int r, w;
+
+ r = SLAP_EVENT_IS_READ( i );
+ /* writefds was not initialized if nwriters was zero */
+ w = nwriters ? SLAP_EVENT_IS_WRITE( i ) : 0;
+ if ( r || w ) {
+ Debug( LDAP_DEBUG_CONNS, " %d%s%s", i,
+ r ? "r" : "", w ? "w" : "" );
+ if ( r ) {
+ nrfds++;
+ ns--;
+ }
+ if ( w ) {
+ nwfds++;
+ ns--;
+ }
+ }
+ if ( ns <= 0 ) break;
+ }
+ Debug( LDAP_DEBUG_CONNS, "\n" );
+
+ /* loop through the writers */
+ for ( i = 0; nwfds > 0; i++ ) {
+ ber_socket_t wd;
+ if ( ! SLAP_EVENT_IS_WRITE( i ) ) continue;
+ wd = i;
+
+ SLAP_EVENT_CLR_WRITE( wd );
+ nwfds--;
+
+ Debug( LDAP_DEBUG_CONNS,
+ "daemon: write active on %d\n",
+ wd );
+
+ /*
+ * NOTE: it is possible that the connection was closed
+ * and that the stream is now inactive.
+ * connection_write() must validate the stream is still
+ * active.
+ *
+ * ITS#4338: if the stream is invalid, there is no need to
+ * close it here. It has already been closed in connection.c.
+ */
+ if ( connection_write( wd ) < 0 ) {
+ if ( SLAP_EVENT_IS_READ( wd ) ) {
+ SLAP_EVENT_CLR_READ( (unsigned) wd );
+ nrfds--;
+ }
+ }
+ }
+
+ for ( i = 0; nrfds > 0; i++ ) {
+ ber_socket_t rd;
+ if ( ! SLAP_EVENT_IS_READ( i ) ) continue;
+ rd = i;
+ SLAP_EVENT_CLR_READ( rd );
+ nrfds--;
+
+ Debug ( LDAP_DEBUG_CONNS,
+ "daemon: read activity on %d\n", rd );
+ /*
+ * NOTE: it is possible that the connection was closed
+ * and that the stream is now inactive.
+ * connection_read() must valid the stream is still
+ * active.
+ */
+
+ connection_read_activate( rd );
+ }
+#else /* !SLAP_EVENTS_ARE_INDEXED */
+ /* FIXME */
+ /* The events are returned in an arbitrary list. This is true
+ * for /dev/poll, epoll and kqueue. In order to prioritize things
+ * so that we can handle wake_sds first, listeners second, and then
+ * all other connections last (as we do for select), we would need
+ * to use multiple event handles and cascade them.
+ *
+ * That seems like a bit of hassle. So the wake_sds check has been
+ * skipped. For epoll and kqueue we can associate arbitrary data with
+ * an event, so we could use pointers to the listener structure
+ * instead of just the file descriptor. For /dev/poll we have to
+ * search the listeners array for a matching descriptor.
+ *
+ * We now handle wake events when we see them; they are not given
+ * higher priority.
+ */
+#ifdef LDAP_DEBUG
+ Debug( LDAP_DEBUG_CONNS, "daemon: activity on:" );
+
+ for ( i = 0; i < ns; i++ ) {
+ int r, w, fd;
+
+ /* Don't log listener events */
+ if ( SLAP_EVENT_IS_LISTENER( tid, i )
+#ifdef LDAP_CONNECTIONLESS
+ && !( (SLAP_EVENT_LISTENER( tid, i ))->sl_is_udp )
+#endif /* LDAP_CONNECTIONLESS */
+ )
+ {
+ continue;
+ }
+
+ fd = SLAP_EVENT_FD( tid, i );
+ /* Don't log internal wake events */
+ if ( fd == wake_sds[tid][0] ) continue;
+
+#ifdef HAVE_KQUEUE
+ r = SLAP_EVENT_IS_READ( tid, i );
+ w = SLAP_EVENT_IS_WRITE( tid, i );
+#else
+ r = SLAP_EVENT_IS_READ( i );
+ w = SLAP_EVENT_IS_WRITE( i );
+#endif /* HAVE_KQUEUE */
+ if ( r || w ) {
+ Debug( LDAP_DEBUG_CONNS, " %d%s%s", fd,
+ r ? "r" : "", w ? "w" : "" );
+ }
+ }
+ Debug( LDAP_DEBUG_CONNS, "\n" );
+#endif /* LDAP_DEBUG */
+
+ for ( i = 0; i < ns; i++ ) {
+ int rc = 1, fd, w = 0, r = 0;
+
+ if ( SLAP_EVENT_IS_LISTENER( tid, i ) ) {
+ rc = slap_listener_activate( SLAP_EVENT_LISTENER( tid, i ) );
+ }
+
+ /* If we found a regular listener, rc is now zero, and we
+ * can skip the data portion. But if it was a UDP listener
+ * then rc is still 1, and we want to handle the data.
+ */
+ if ( rc ) {
+ fd = SLAP_EVENT_FD( tid, i );
+
+ /* Handle wake events */
+ if ( fd == wake_sds[tid][0] ) {
+ char c[BUFSIZ];
+ waking = 0;
+ (void)!tcp_read( SLAP_FD2SOCK(wake_sds[tid][0]), c, sizeof(c) );
+ continue;
+ }
+
+#ifdef HAVE_KQUEUE
+ if ( SLAP_EVENT_IS_WRITE( tid, i ) )
+#else
+ if ( SLAP_EVENT_IS_WRITE( i ) )
+#endif /* HAVE_KQUEUE */
+ {
+ Debug( LDAP_DEBUG_CONNS,
+ "daemon: write active on %d\n",
+ fd );
+
+ SLAP_EVENT_CLR_WRITE( i );
+ w = 1;
+
+ /*
+ * NOTE: it is possible that the connection was closed
+ * and that the stream is now inactive.
+ * connection_write() must valid the stream is still
+ * active.
+ */
+ if ( connection_write( fd ) < 0 ) {
+ continue;
+ }
+ }
+ /* If event is a read */
+#ifdef HAVE_KQUEUE
+ if ( SLAP_EVENT_IS_READ( tid, i ))
+#else
+ if ( SLAP_EVENT_IS_READ( i ))
+#endif /* HAVE_KQUEUE */
+ {
+ r = 1;
+ Debug( LDAP_DEBUG_CONNS,
+ "daemon: read active on %d\n",
+ fd );
+
+ SLAP_EVENT_CLR_READ( i );
+ connection_read_activate( fd );
+ } else if ( !w ) {
+#ifdef HAVE_EPOLL
+ /* Don't keep reporting the hangup
+ */
+ if ( SLAP_SOCK_IS_ACTIVE( tid, fd )) {
+ SLAP_EPOLL_SOCK_SET( tid, fd, EPOLLET );
+ }
+#endif
+ }
+ }
+ }
+#endif /* SLAP_EVENTS_ARE_INDEXED */
+
+ /* Was number of listener threads decreased? */
+ if ( ldap_pvt_thread_pool_pausecheck_native( &connection_pool )) {
+ /* decreased, let this thread finish */
+ if ( tid >= slapd_daemon_threads )
+ break;
+ }
+
+#ifndef HAVE_YIELDING_SELECT
+ ldap_pvt_thread_yield();
+#endif /* ! HAVE_YIELDING_SELECT */
+ }
+
+ /* Only thread 0 handles shutdown */
+ if ( tid )
+ return NULL;
+
+ if ( slapd_shutdown == 1 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "daemon: shutdown requested and initiated.\n" );
+
+ } else if ( slapd_shutdown == 2 ) {
+#ifdef HAVE_NT_SERVICE_MANAGER
+ Debug( LDAP_DEBUG_ANY,
+ "daemon: shutdown initiated by Service Manager.\n" );
+#else /* !HAVE_NT_SERVICE_MANAGER */
+ Debug( LDAP_DEBUG_ANY,
+ "daemon: abnormal condition, shutdown initiated.\n" );
+#endif /* !HAVE_NT_SERVICE_MANAGER */
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "daemon: no active streams, shutdown initiated.\n" );
+ }
+
+ close_listeners( 1 );
+
+ if ( !slapd_gentle_shutdown ) {
+ slapd_abrupt_shutdown = 1;
+ connections_shutdown();
+ }
+
+#ifdef HAVE_KQUEUE
+ close( slap_daemon[tid].sd_kq );
+#endif
+
+ if ( LogTest( LDAP_DEBUG_ANY )) {
+ int t = ldap_pvt_thread_pool_backload( &connection_pool );
+ Debug( LDAP_DEBUG_ANY,
+ "slapd shutdown: waiting for %d operations/tasks to finish\n",
+ t );
+ }
+ ldap_pvt_thread_pool_close( &connection_pool, 1 );
+
+ return NULL;
+}
+
+typedef struct slap_tid_waiter {
+ int num_tids;
+ ldap_pvt_thread_t tids[0];
+} slap_tid_waiter;
+
+static void *
+slapd_daemon_tid_cleanup(
+ void *ctx,
+ void *ptr )
+{
+ slap_tid_waiter *tids = ptr;
+ int i;
+
+ for ( i=0; i<tids->num_tids; i++ )
+ ldap_pvt_thread_join( tids->tids[i], (void *)NULL );
+ ch_free( ptr );
+ return NULL;
+}
+
+int
+slapd_daemon_resize( int newnum )
+{
+ int i, rc;
+
+ if ( newnum == slapd_daemon_threads )
+ return 0;
+
+ /* wake up all current listener threads */
+ for ( i=0; i<slapd_daemon_threads; i++ )
+ WAKE_LISTENER(i,1);
+
+ /* mutexes may not survive realloc, so destroy & recreate later */
+ for ( i=0; i<slapd_daemon_threads; i++ )
+ ldap_pvt_thread_mutex_destroy( &slap_daemon[i].sd_mutex );
+
+ if ( newnum > slapd_daemon_threads ) {
+ wake_sds = ch_realloc( wake_sds, newnum * sizeof( sdpair ));
+ slap_daemon = ch_realloc( slap_daemon, newnum * sizeof( slap_daemon_st ));
+
+ for ( i=slapd_daemon_threads; i<newnum; i++ )
+ {
+ memset( &slap_daemon[i], 0, sizeof( slap_daemon_st ));
+ if( (rc = lutil_pair( wake_sds[i] )) < 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "daemon: lutil_pair() failed rc=%d\n", rc );
+ return rc;
+ }
+ ber_pvt_socket_set_nonblock( wake_sds[i][1], 1 );
+
+ SLAP_SOCK_INIT(i);
+ }
+
+ for ( i=0; i<newnum; i++ )
+ ldap_pvt_thread_mutex_init( &slap_daemon[i].sd_mutex );
+
+ slapd_socket_realloc( newnum );
+
+ for ( i=slapd_daemon_threads; i<newnum; i++ )
+ {
+ /* listener as a separate THREAD */
+ rc = ldap_pvt_thread_create( &slap_daemon[i].sd_tid,
+ 0, slapd_daemon_task, &slap_daemon[i] );
+
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "listener ldap_pvt_thread_create failed (%d)\n", rc );
+ return rc;
+ }
+ }
+ } else {
+ int j;
+ slap_tid_waiter *tids = ch_malloc( sizeof(slap_tid_waiter) +
+ ((slapd_daemon_threads - newnum) * sizeof(ldap_pvt_thread_t )));
+ slapd_socket_realloc( newnum );
+ tids->num_tids = slapd_daemon_threads - newnum;
+ for ( i=newnum, j=0; i<slapd_daemon_threads; i++, j++ ) {
+ tids->tids[j] = slap_daemon[i].sd_tid;
+#ifdef HAVE_WINSOCK
+ if ( wake_sds[i][1] != INVALID_SOCKET &&
+ SLAP_FD2SOCK( wake_sds[i][1] ) != SLAP_FD2SOCK( wake_sds[i][0] ))
+#endif /* HAVE_WINSOCK */
+ tcp_close( SLAP_FD2SOCK(wake_sds[i][1]) );
+#ifdef HAVE_WINSOCK
+ if ( wake_sds[i][0] != INVALID_SOCKET )
+#endif /* HAVE_WINSOCK */
+ tcp_close( SLAP_FD2SOCK(wake_sds[i][0]) );
+
+ SLAP_SOCK_DESTROY( i );
+ }
+
+ wake_sds = ch_realloc( wake_sds, newnum * sizeof( sdpair ));
+ slap_daemon = ch_realloc( slap_daemon, newnum * sizeof( slap_daemon_st ));
+ for ( i=0; i<newnum; i++ )
+ ldap_pvt_thread_mutex_init( &slap_daemon[i].sd_mutex );
+ ldap_pvt_thread_pool_submit( &connection_pool,
+ slapd_daemon_tid_cleanup, (void *) tids );
+ }
+ slapd_daemon_threads = newnum;
+ slapd_daemon_mask = newnum - 1;
+ return 0;
+}
+
+#ifdef LDAP_CONNECTIONLESS
+static int
+connectionless_init( void )
+{
+ int l;
+
+ for ( l = 0; slap_listeners[l] != NULL; l++ ) {
+ Listener *lr = slap_listeners[l];
+ Connection *c;
+
+ if ( !lr->sl_is_udp ) {
+ continue;
+ }
+
+ c = connection_init( lr->sl_sd, lr, "", "",
+ CONN_IS_UDP, (slap_ssf_t) 0, NULL
+ LDAP_PF_LOCAL_SENDMSG_ARG(NULL));
+
+ if ( !c ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "connectionless_init: failed on %s (%d)\n",
+ lr->sl_url.bv_val, lr->sl_sd );
+ return -1;
+ }
+ lr->sl_is_udp++;
+ }
+
+ return 0;
+}
+#endif /* LDAP_CONNECTIONLESS */
+
+int
+slapd_daemon( void )
+{
+ int i, rc;
+
+#ifdef LDAP_CONNECTIONLESS
+ connectionless_init();
+#endif /* LDAP_CONNECTIONLESS */
+
+ SLAP_SOCK_INIT2();
+
+ /* daemon_init only inits element 0 */
+ for ( i=1; i<slapd_daemon_threads; i++ )
+ {
+ ldap_pvt_thread_mutex_init( &slap_daemon[i].sd_mutex );
+
+ if( (rc = lutil_pair( wake_sds[i] )) < 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "daemon: lutil_pair() failed rc=%d\n", rc );
+ return rc;
+ }
+ ber_pvt_socket_set_nonblock( wake_sds[i][1], 1 );
+
+ SLAP_SOCK_INIT(i);
+ }
+
+ for ( i=0; i<slapd_daemon_threads; i++ )
+ {
+ /* listener as a separate THREAD */
+ rc = ldap_pvt_thread_create( &slap_daemon[i].sd_tid,
+ 0, slapd_daemon_task, &slap_daemon[i] );
+
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "listener ldap_pvt_thread_create failed (%d)\n", rc );
+ return rc;
+ }
+ }
+
+ ldap_pvt_thread_mutex_lock( &slapd_init_mutex );
+ while ( !slapd_ready && !slapd_shutdown ) {
+ ldap_pvt_thread_cond_wait( &slapd_init_cond, &slapd_init_mutex );
+ }
+ ldap_pvt_thread_mutex_unlock( &slapd_init_mutex );
+
+ if ( slapd_shutdown ) {
+ Debug( LDAP_DEBUG_ANY,
+ "listener initialization failed\n" );
+ return 1;
+ }
+
+#ifdef HAVE_SYSTEMD
+ rc = sd_notify( 1, "READY=1" );
+ if ( rc < 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "systemd sd_notify failed (%d)\n", rc );
+ }
+#endif /* HAVE_SYSTEMD */
+
+ /* wait for the listener threads to complete */
+ for ( i=0; i<slapd_daemon_threads; i++ )
+ ldap_pvt_thread_join( slap_daemon[i].sd_tid, (void *)NULL );
+
+ destroy_listeners();
+
+ return 0;
+}
+
+static int
+sockinit( void )
+{
+#if defined( HAVE_WINSOCK2 )
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ int err;
+
+ wVersionRequested = MAKEWORD( 2, 0 );
+
+ err = WSAStartup( wVersionRequested, &wsaData );
+ if ( err != 0 ) {
+ /* Tell the user that we couldn't find a usable */
+ /* WinSock DLL. */
+ return -1;
+ }
+
+ /* Confirm that the WinSock DLL supports 2.0.*/
+ /* Note that if the DLL supports versions greater */
+ /* than 2.0 in addition to 2.0, it will still return */
+ /* 2.0 in wVersion since that is the version we */
+ /* requested. */
+
+ if ( LOBYTE( wsaData.wVersion ) != 2 ||
+ HIBYTE( wsaData.wVersion ) != 0 )
+ {
+ /* Tell the user that we couldn't find a usable */
+ /* WinSock DLL. */
+ WSACleanup();
+ return -1;
+ }
+
+ /* The WinSock DLL is acceptable. Proceed. */
+#elif defined( HAVE_WINSOCK )
+ WSADATA wsaData;
+ if ( WSAStartup( 0x0101, &wsaData ) != 0 ) return -1;
+#endif /* ! HAVE_WINSOCK2 && ! HAVE_WINSOCK */
+
+ return 0;
+}
+
+static int
+sockdestroy( void )
+{
+#if defined( HAVE_WINSOCK2 ) || defined( HAVE_WINSOCK )
+ WSACleanup();
+#endif /* HAVE_WINSOCK2 || HAVE_WINSOCK */
+
+ return 0;
+}
+
+RETSIGTYPE
+slap_sig_shutdown( int sig )
+{
+ int save_errno = errno;
+ int i;
+
+#if 0
+ Debug(LDAP_DEBUG_TRACE, "slap_sig_shutdown: signal %d\n", sig);
+#endif
+
+ /*
+ * If the NT Service Manager is controlling the server, we don't
+ * want SIGBREAK to kill the server. For some strange reason,
+ * SIGBREAK is generated when a user logs out.
+ */
+
+#if defined(HAVE_NT_SERVICE_MANAGER) && defined(SIGBREAK)
+ if (is_NT_Service && sig == SIGBREAK) {
+ /* empty */;
+ } else
+#endif /* HAVE_NT_SERVICE_MANAGER && SIGBREAK */
+#ifdef SIGHUP
+ if (sig == SIGHUP && global_gentlehup && slapd_gentle_shutdown == 0) {
+ slapd_gentle_shutdown = 1;
+ } else
+#endif /* SIGHUP */
+ {
+ slapd_shutdown = 1;
+ }
+
+ for (i=0; i<slapd_daemon_threads; i++) {
+ WAKE_LISTENER(i,1);
+ }
+
+ /* reinstall self */
+ (void) SIGNAL_REINSTALL( sig, slap_sig_shutdown );
+
+ errno = save_errno;
+}
+
+RETSIGTYPE
+slap_sig_wake( int sig )
+{
+ int save_errno = errno;
+
+ WAKE_LISTENER(0,1);
+
+ /* reinstall self */
+ (void) SIGNAL_REINSTALL( sig, slap_sig_wake );
+
+ errno = save_errno;
+}
+
+int
+slap_pause_server( void )
+{
+ BackendInfo *bi;
+ int rc = LDAP_SUCCESS;
+
+ rc = ldap_pvt_thread_pool_pause( &connection_pool );
+
+ LDAP_STAILQ_FOREACH(bi, &backendInfo, bi_next) {
+ if ( bi->bi_pause ) {
+ rc = bi->bi_pause( bi );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "slap_pause_server: "
+ "bi_pause failed for backend %s\n",
+ bi->bi_type );
+ return rc;
+ }
+ }
+ }
+
+ return rc;
+}
+
+int
+slap_unpause_server( void )
+{
+ BackendInfo *bi;
+ int rc = LDAP_SUCCESS;
+
+ LDAP_STAILQ_FOREACH(bi, &backendInfo, bi_next) {
+ if ( bi->bi_unpause ) {
+ rc = bi->bi_unpause( bi );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "slap_unpause_server: "
+ "bi_unpause failed for backend %s\n",
+ bi->bi_type );
+ return rc;
+ }
+ }
+ }
+
+ rc = ldap_pvt_thread_pool_resume( &connection_pool );
+ return rc;
+}
+
+
+void
+slapd_add_internal( ber_socket_t s, int isactive )
+{
+ if (!isactive) {
+ SET_CLOSE(s);
+ }
+ slapd_add( s, isactive, NULL, -1 );
+}
+
+Listener **
+slapd_get_listeners( void )
+{
+ /* Could return array with no listeners if !listening, but current
+ * callers mostly look at the URLs. E.g. syncrepl uses this to
+ * identify the server, which means it wants the startup arguments.
+ */
+ return slap_listeners;
+}
+
+/* Reject all incoming requests */
+void
+slap_suspend_listeners( void )
+{
+ int i;
+ for (i=0; slap_listeners[i]; i++) {
+ slap_listeners[i]->sl_mute = 1;
+ listen( slap_listeners[i]->sl_sd, 0 );
+ }
+}
+
+/* Resume after a suspend */
+void
+slap_resume_listeners( void )
+{
+ int i;
+ for (i=0; slap_listeners[i]; i++) {
+ slap_listeners[i]->sl_mute = 0;
+ listen( slap_listeners[i]->sl_sd, SLAPD_LISTEN_BACKLOG );
+ }
+}
+
+void
+slap_wake_listener()
+{
+ WAKE_LISTENER(0,1);
+}
+
+/* return 0 on timeout, 1 on writer ready
+ * -1 on general error
+ */
+int
+slapd_wait_writer( ber_socket_t sd )
+{
+#ifdef HAVE_WINSOCK
+ fd_set writefds;
+ struct timeval tv, *tvp;
+
+ FD_ZERO( &writefds );
+ FD_SET( slapd_ws_sockets[sd], &writefds );
+ if ( global_writetimeout ) {
+ tv.tv_sec = global_writetimeout;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ } else {
+ tvp = NULL;
+ }
+ return select( 0, NULL, &writefds, NULL, tvp );
+#else
+ struct pollfd fds;
+ int timeout = global_writetimeout ? global_writetimeout * 1000 : -1;
+
+ fds.fd = sd;
+ fds.events = POLLOUT;
+
+ return poll( &fds, 1, timeout );
+#endif
+}
diff --git a/servers/slapd/delete.c b/servers/slapd/delete.c
new file mode 100644
index 0000000..0329154
--- /dev/null
+++ b/servers/slapd/delete.c
@@ -0,0 +1,244 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+#include "lutil.h"
+
+int
+do_delete(
+ Operation *op,
+ SlapReply *rs )
+{
+ struct berval dn = BER_BVNULL;
+
+ Debug( LDAP_DEBUG_TRACE, "%s do_delete\n",
+ op->o_log_prefix );
+ /*
+ * Parse the delete request. It looks like this:
+ *
+ * DelRequest := DistinguishedName
+ */
+
+ if ( ber_scanf( op->o_ber, "m", &dn ) == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_delete: ber_scanf failed\n",
+ op->o_log_prefix );
+ send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
+ return SLAPD_DISCONNECT;
+ }
+
+ if( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_delete: get_ctrls failed\n",
+ op->o_log_prefix );
+ goto cleanup;
+ }
+
+ rs->sr_err = dnPrettyNormal( NULL, &dn, &op->o_req_dn, &op->o_req_ndn,
+ op->o_tmpmemctx );
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_delete: invalid dn (%s)\n",
+ op->o_log_prefix, dn.bv_val );
+ send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid DN" );
+ goto cleanup;
+ }
+
+ Debug( LDAP_DEBUG_STATS, "%s DEL dn=\"%s\"\n",
+ op->o_log_prefix, op->o_req_dn.bv_val );
+
+ if( op->o_req_ndn.bv_len == 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_delete: root dse!\n",
+ op->o_log_prefix );
+ /* protocolError would likely be a more appropriate error */
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "cannot delete the root DSE" );
+ goto cleanup;
+
+ } else if ( bvmatch( &op->o_req_ndn, &frontendDB->be_schemandn ) ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_delete: subschema subentry!\n",
+ op->o_log_prefix );
+ /* protocolError would likely be a more appropriate error */
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "cannot delete the root DSE" );
+ goto cleanup;
+ }
+
+ op->o_bd = frontendDB;
+ rs->sr_err = frontendDB->be_delete( op, rs );
+ if ( rs->sr_err == SLAPD_ASYNCOP ) {
+ /* skip cleanup */
+ return rs->sr_err;
+ }
+
+ if( rs->sr_err == LDAP_TXN_SPECIFY_OKAY ) {
+ /* skip cleanup */
+ return rs->sr_err;
+ }
+
+cleanup:;
+ op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
+ return rs->sr_err;
+}
+
+int
+fe_op_delete( Operation *op, SlapReply *rs )
+{
+ struct berval pdn = BER_BVNULL;
+ BackendDB *op_be, *bd = op->o_bd;
+
+ /*
+ * We could be serving multiple database backends. Select the
+ * appropriate one, or send a referral to our "referral server"
+ * if we don't hold it.
+ */
+ op->o_bd = select_backend( &op->o_req_ndn, 1 );
+ if ( op->o_bd == NULL ) {
+ op->o_bd = bd;
+ rs->sr_ref = referral_rewrite( default_referral,
+ NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+
+ if (!rs->sr_ref) rs->sr_ref = default_referral;
+ if ( rs->sr_ref != NULL ) {
+ rs->sr_err = LDAP_REFERRAL;
+ send_ldap_result( op, rs );
+
+ if (rs->sr_ref != default_referral) ber_bvarray_free( rs->sr_ref );
+ } else {
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "no global superior knowledge" );
+ }
+ goto cleanup;
+ }
+
+ /* If we've got a glued backend, check the real backend */
+ op_be = op->o_bd;
+ if ( SLAP_GLUE_INSTANCE( op->o_bd )) {
+ op->o_bd = select_backend( &op->o_req_ndn, 0 );
+ }
+
+ /* check restrictions */
+ if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ /* check for referrals */
+ if( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) {
+ goto cleanup;
+ }
+
+ /*
+ * do the delete if 1 && (2 || 3)
+ * 1) there is a delete function implemented in this backend;
+ * 2) this backend is the provider for what it holds;
+ * 3) it's a replica and the dn supplied is the update_ndn.
+ */
+ if ( op->o_bd->be_delete ) {
+ /* do the update here */
+ int repl_user = be_isupdate( op );
+ if ( !SLAP_SINGLE_SHADOW(op->o_bd) || repl_user ) {
+ struct berval org_req_dn = BER_BVNULL;
+ struct berval org_req_ndn = BER_BVNULL;
+ struct berval org_dn = BER_BVNULL;
+ struct berval org_ndn = BER_BVNULL;
+ int org_managedsait;
+
+ if ( op->o_txnSpec ) {
+ txn_preop( op, rs );
+ goto cleanup;
+ }
+
+ op->o_bd = op_be;
+ op->o_bd->be_delete( op, rs );
+
+ org_req_dn = op->o_req_dn;
+ org_req_ndn = op->o_req_ndn;
+ org_dn = op->o_dn;
+ org_ndn = op->o_ndn;
+ org_managedsait = get_manageDSAit( op );
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ op->o_managedsait = SLAP_CONTROL_NONCRITICAL;
+
+ while ( rs->sr_err == LDAP_SUCCESS &&
+ op->o_delete_glue_parent )
+ {
+ op->o_delete_glue_parent = 0;
+ if ( !be_issuffix( op->o_bd, &op->o_req_ndn )) {
+ slap_callback cb = { NULL, NULL, NULL, NULL };
+ cb.sc_response = slap_null_cb;
+ dnParent( &op->o_req_ndn, &pdn );
+ op->o_req_dn = pdn;
+ op->o_req_ndn = pdn;
+ op->o_callback = &cb;
+ op->o_bd->be_delete( op, rs );
+ } else {
+ break;
+ }
+ }
+
+ op->o_managedsait = org_managedsait;
+ op->o_dn = org_dn;
+ op->o_ndn = org_ndn;
+ op->o_req_dn = org_req_dn;
+ op->o_req_ndn = org_req_ndn;
+ op->o_delete_glue_parent = 0;
+
+ } else {
+ BerVarray defref = op->o_bd->be_update_refs
+ ? op->o_bd->be_update_refs : default_referral;
+
+ if ( defref != NULL ) {
+ rs->sr_ref = referral_rewrite( defref,
+ NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ if (!rs->sr_ref) rs->sr_ref = defref;
+ rs->sr_err = LDAP_REFERRAL;
+ send_ldap_result( op, rs );
+
+ if (rs->sr_ref != defref) ber_bvarray_free( rs->sr_ref );
+
+ } else {
+ send_ldap_error( op, rs,
+ LDAP_UNWILLING_TO_PERFORM,
+ "shadow context; no update referral" );
+ }
+ }
+
+ } else {
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "operation not supported within namingContext" );
+ }
+
+cleanup:;
+ op->o_bd = bd;
+ return rs->sr_err;
+}
diff --git a/servers/slapd/dn.c b/servers/slapd/dn.c
new file mode 100644
index 0000000..9b82f55
--- /dev/null
+++ b/servers/slapd/dn.c
@@ -0,0 +1,1333 @@
+/* dn.c - routines for dealing with distinguished names */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "slap.h"
+#include "lutil.h"
+
+/*
+ * The DN syntax-related functions take advantage of the dn representation
+ * handling functions ldap_str2dn/ldap_dn2str. The latter are not schema-
+ * aware, so the attributes and their values need be validated (and possibly
+ * normalized). In the current implementation the required validation/nor-
+ * malization/"pretty"ing are done on newly created DN structural represen-
+ * tations; however the idea is to move towards DN handling in structural
+ * representation instead of the current string representation. To this
+ * purpose, we need to do only the required operations and keep track of
+ * what has been done to minimize their impact on performances.
+ *
+ * Developers are strongly encouraged to use this feature, to speed-up
+ * its stabilization.
+ */
+
+#define AVA_PRIVATE( ava ) ( ( AttributeDescription * )(ava)->la_private )
+
+int slap_DN_strict = SLAP_AD_NOINSERT;
+
+static int
+LDAPRDN_validate( LDAPRDN rdn )
+{
+ int iAVA;
+ int rc;
+
+ assert( rdn != NULL );
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ iAVA ];
+ AttributeDescription *ad;
+ slap_syntax_validate_func *validate = NULL;
+
+ assert( ava != NULL );
+
+ if ( ( ad = AVA_PRIVATE( ava ) ) == NULL ) {
+ const char *text = NULL;
+
+ rc = slap_bv2ad( &ava->la_attr, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ rc = slap_bv2undef_ad( &ava->la_attr,
+ &ad, &text,
+ SLAP_AD_PROXIED|slap_DN_strict );
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ ava->la_private = ( void * )ad;
+ }
+
+ /*
+ * Do not allow X-ORDERED 'VALUES' naming attributes
+ */
+ if ( ad->ad_type->sat_flags & SLAP_AT_ORDERED_VAL ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /*
+ * Replace attr oid/name with the canonical name
+ */
+ ava->la_attr = ad->ad_cname;
+
+ validate = ad->ad_type->sat_syntax->ssyn_validate;
+
+ if ( validate ) {
+ /*
+ * validate value by validate function
+ */
+ rc = ( *validate )( ad->ad_type->sat_syntax,
+ &ava->la_value );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * In-place, schema-aware validation of the
+ * structural representation of a distinguished name.
+ */
+static int
+LDAPDN_validate( LDAPDN dn )
+{
+ int iRDN;
+ int rc;
+
+ assert( dn != NULL );
+
+ for ( iRDN = 0; dn[ iRDN ]; iRDN++ ) {
+ rc = LDAPRDN_validate( dn[ iRDN ] );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * dn validate routine
+ */
+int
+dnValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ int rc;
+ LDAPDN dn = NULL;
+
+ assert( in != NULL );
+
+ if ( in->bv_len == 0 ) {
+ return LDAP_SUCCESS;
+
+ } else if ( in->bv_len > SLAP_LDAPDN_MAXLEN ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ rc = ldap_bv2dn( in, &dn, LDAP_DN_FORMAT_LDAP );
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ assert( strlen( in->bv_val ) == in->bv_len );
+
+ /*
+ * Schema-aware validate
+ */
+ rc = LDAPDN_validate( dn );
+ ldap_dnfree( dn );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int
+rdnValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ int rc;
+ LDAPRDN rdn;
+ char* p;
+
+ assert( in != NULL );
+ if ( in->bv_len == 0 ) {
+ return LDAP_SUCCESS;
+
+ } else if ( in->bv_len > SLAP_LDAPDN_MAXLEN ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ rc = ldap_bv2rdn_x( in , &rdn, (char **) &p,
+ LDAP_DN_FORMAT_LDAP, NULL);
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ assert( strlen( in->bv_val ) == in->bv_len );
+
+ /*
+ * Schema-aware validate
+ */
+ rc = LDAPRDN_validate( rdn );
+ ldap_rdnfree( rdn );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+
+/*
+ * AVA sorting inside a RDN
+ *
+ * Rule: sort attributeTypes in alphabetical order.
+ *
+ * Note: the sorting can be slightly improved by sorting first
+ * by attribute type length, then by alphabetical order.
+ *
+ * uses an insertion sort; should be fine since the number of AVAs in
+ * a RDN should be limited.
+ */
+static int
+AVA_Sort( LDAPRDN rdn, int nAVAs )
+{
+ LDAPAVA *ava_i;
+ int i;
+ int rc = LDAP_SUCCESS;
+
+ assert( rdn != NULL );
+
+ for ( i = 1; i < nAVAs; i++ ) {
+ LDAPAVA *ava_j;
+ int j;
+
+ ava_i = rdn[ i ];
+ for ( j = i-1; j >=0; j-- ) {
+ int a;
+
+ ava_j = rdn[ j ];
+ a = strcmp( ava_i->la_attr.bv_val, ava_j->la_attr.bv_val );
+
+ /* RFC4512 does not allow multiple AVAs
+ * with the same attribute type in RDN (ITS#5968) */
+ if ( a == 0 )
+ rc = LDAP_INVALID_DN_SYNTAX;
+
+ if ( a > 0 )
+ break;
+
+ rdn[ j+1 ] = rdn[ j ];
+ }
+ rdn[ j+1 ] = ava_i;
+ }
+ return rc;
+}
+
+static int
+LDAPRDN_rewrite( LDAPRDN rdn, unsigned flags, void *ctx )
+{
+
+ int rc, iAVA, do_sort = 0;
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ iAVA ];
+ AttributeDescription *ad;
+ slap_syntax_validate_func *validf = NULL;
+ slap_mr_normalize_func *normf = NULL;
+ slap_syntax_transform_func *transf = NULL;
+ MatchingRule *mr = NULL;
+ struct berval bv = BER_BVNULL;
+
+ assert( ava != NULL );
+
+ if ( ( ad = AVA_PRIVATE( ava ) ) == NULL ) {
+ const char *text = NULL;
+
+ rc = slap_bv2ad( &ava->la_attr, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ rc = slap_bv2undef_ad( &ava->la_attr,
+ &ad, &text,
+ SLAP_AD_PROXIED|slap_DN_strict );
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ ava->la_private = ( void * )ad;
+ do_sort = 1;
+ }
+
+ /*
+ * Replace attr oid/name with the canonical name
+ */
+ ava->la_attr = ad->ad_cname;
+
+ if( ava->la_flags & LDAP_AVA_BINARY ) {
+ /* AVA is binary encoded, not supported */
+ return LDAP_INVALID_SYNTAX;
+
+ /* Do not allow X-ORDERED 'VALUES' naming attributes */
+ } else if( ad->ad_type->sat_flags & SLAP_AT_ORDERED_VAL ) {
+ return LDAP_INVALID_SYNTAX;
+
+ } else if( flags & SLAP_LDAPDN_PRETTY ) {
+ transf = ad->ad_type->sat_syntax->ssyn_pretty;
+ if( !transf ) {
+ validf = ad->ad_type->sat_syntax->ssyn_validate;
+ }
+ } else { /* normalization */
+ validf = ad->ad_type->sat_syntax->ssyn_validate;
+ mr = ad->ad_type->sat_equality;
+ if( mr && (!( mr->smr_usage & SLAP_MR_MUTATION_NORMALIZER ))) {
+ normf = mr->smr_normalize;
+ }
+ }
+
+ if ( validf ) {
+ /* validate value before normalization */
+ rc = ( *validf )( ad->ad_type->sat_syntax,
+ ava->la_value.bv_len
+ ? &ava->la_value
+ : (struct berval *) &slap_empty_bv );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ if ( transf ) {
+ /*
+ * transform value by pretty function
+ * if value is empty, use empty_bv
+ */
+ rc = ( *transf )( ad->ad_type->sat_syntax,
+ ava->la_value.bv_len
+ ? &ava->la_value
+ : (struct berval *) &slap_empty_bv,
+ &bv, ctx );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ if ( normf ) {
+ /*
+ * normalize value
+ * if value is empty, use empty_bv
+ */
+ rc = ( *normf )(
+ SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
+ ad->ad_type->sat_syntax,
+ mr,
+ ava->la_value.bv_len
+ ? &ava->la_value
+ : (struct berval *) &slap_empty_bv,
+ &bv, ctx );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+
+ if( bv.bv_val ) {
+ if ( ava->la_flags & LDAP_AVA_FREE_VALUE )
+ ber_memfree_x( ava->la_value.bv_val, ctx );
+ ava->la_value = bv;
+ ava->la_flags |= LDAP_AVA_FREE_VALUE;
+ }
+ /* reject empty values */
+ if (!ava->la_value.bv_len) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+ rc = LDAP_SUCCESS;
+
+ if ( do_sort ) {
+ rc = AVA_Sort( rdn, iAVA );
+ }
+
+ return rc;
+}
+
+/*
+ * In-place, schema-aware normalization / "pretty"ing of the
+ * structural representation of a distinguished name.
+ */
+static int
+LDAPDN_rewrite( LDAPDN dn, unsigned flags, void *ctx )
+{
+ int iRDN;
+ int rc;
+
+ assert( dn != NULL );
+
+ for ( iRDN = 0; dn[ iRDN ]; iRDN++ ) {
+ rc = LDAPRDN_rewrite( dn[ iRDN ], flags, ctx );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int
+dnNormalize(
+ slap_mask_t use,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *out,
+ void *ctx)
+{
+ assert( val != NULL );
+ assert( out != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, ">>> dnNormalize: <%s>\n", val->bv_val ? val->bv_val : "" );
+
+ if ( val->bv_len != 0 ) {
+ LDAPDN dn = NULL;
+ int rc;
+
+ /*
+ * Go to structural representation
+ */
+ rc = ldap_bv2dn_x( val, &dn, LDAP_DN_FORMAT_LDAP, ctx );
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ assert( strlen( val->bv_val ) == val->bv_len );
+
+ /*
+ * Schema-aware rewrite
+ */
+ if ( LDAPDN_rewrite( dn, 0, ctx ) != LDAP_SUCCESS ) {
+ ldap_dnfree_x( dn, ctx );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /*
+ * Back to string representation
+ */
+ rc = ldap_dn2bv_x( dn, out,
+ LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PRETTY, ctx );
+
+ ldap_dnfree_x( dn, ctx );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ } else {
+ ber_dupbv_x( out, val, ctx );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<<< dnNormalize: <%s>\n", out->bv_val ? out->bv_val : "" );
+
+ return LDAP_SUCCESS;
+}
+
+int
+rdnNormalize(
+ slap_mask_t use,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *out,
+ void *ctx)
+{
+ assert( val != NULL );
+ assert( out != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, ">>> dnNormalize: <%s>\n", val->bv_val ? val->bv_val : "" );
+ if ( val->bv_len != 0 ) {
+ LDAPRDN rdn = NULL;
+ int rc;
+ char* p;
+
+ /*
+ * Go to structural representation
+ */
+ rc = ldap_bv2rdn_x( val , &rdn, (char **) &p,
+ LDAP_DN_FORMAT_LDAP, ctx);
+
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ assert( strlen( val->bv_val ) == val->bv_len );
+
+ /*
+ * Schema-aware rewrite
+ */
+ if ( LDAPRDN_rewrite( rdn, 0, ctx ) != LDAP_SUCCESS ) {
+ ldap_rdnfree_x( rdn, ctx );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /*
+ * Back to string representation
+ */
+ rc = ldap_rdn2bv_x( rdn, out,
+ LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PRETTY, ctx );
+
+ ldap_rdnfree_x( rdn, ctx );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ } else {
+ ber_dupbv_x( out, val, ctx );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<<< dnNormalize: <%s>\n", out->bv_val ? out->bv_val : "" );
+
+ return LDAP_SUCCESS;
+}
+
+int
+dnPretty(
+ Syntax *syntax,
+ struct berval *val,
+ struct berval *out,
+ void *ctx)
+{
+ assert( val != NULL );
+ assert( out != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, ">>> dnPretty: <%s>\n", val->bv_val ? val->bv_val : "" );
+
+ if ( val->bv_len == 0 ) {
+ ber_dupbv_x( out, val, ctx );
+
+ } else if ( val->bv_len > SLAP_LDAPDN_MAXLEN ) {
+ return LDAP_INVALID_SYNTAX;
+
+ } else {
+ LDAPDN dn = NULL;
+ int rc;
+
+ /* FIXME: should be liberal in what we accept */
+ rc = ldap_bv2dn_x( val, &dn, LDAP_DN_FORMAT_LDAP, ctx );
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ assert( strlen( val->bv_val ) == val->bv_len );
+
+ /*
+ * Schema-aware rewrite
+ */
+ if ( LDAPDN_rewrite( dn, SLAP_LDAPDN_PRETTY, ctx ) != LDAP_SUCCESS ) {
+ ldap_dnfree_x( dn, ctx );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* FIXME: not sure why the default isn't pretty */
+ /* RE: the default is the form that is used as
+ * an internal representation; the pretty form
+ * is a variant */
+ rc = ldap_dn2bv_x( dn, out,
+ LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PRETTY, ctx );
+
+ ldap_dnfree_x( dn, ctx );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<<< dnPretty: <%s>\n", out->bv_val ? out->bv_val : "" );
+
+ return LDAP_SUCCESS;
+}
+
+int
+rdnPretty(
+ Syntax *syntax,
+ struct berval *val,
+ struct berval *out,
+ void *ctx)
+{
+ assert( val != NULL );
+ assert( out != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, ">>> rdnPretty: <%s>\n", val->bv_val ? val->bv_val : "" );
+
+ if ( val->bv_len == 0 ) {
+ ber_dupbv_x( out, val, ctx );
+
+ } else if ( val->bv_len > SLAP_LDAPDN_MAXLEN ) {
+ return LDAP_INVALID_SYNTAX;
+
+ } else {
+ LDAPRDN rdn = NULL;
+ int rc;
+ char* p;
+
+ /* FIXME: should be liberal in what we accept */
+ rc = ldap_bv2rdn_x( val , &rdn, (char **) &p,
+ LDAP_DN_FORMAT_LDAP, ctx);
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ assert( strlen( val->bv_val ) == val->bv_len );
+
+ /*
+ * Schema-aware rewrite
+ */
+ if ( LDAPRDN_rewrite( rdn, SLAP_LDAPDN_PRETTY, ctx ) != LDAP_SUCCESS ) {
+ ldap_rdnfree_x( rdn, ctx );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* FIXME: not sure why the default isn't pretty */
+ /* RE: the default is the form that is used as
+ * an internal representation; the pretty form
+ * is a variant */
+ rc = ldap_rdn2bv_x( rdn, out,
+ LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PRETTY, ctx );
+
+ ldap_rdnfree_x( rdn, ctx );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<<< dnPretty: <%s>\n", out->bv_val ? out->bv_val : "" );
+
+ return LDAP_SUCCESS;
+}
+
+
+int
+dnPrettyNormalDN(
+ Syntax *syntax,
+ struct berval *val,
+ LDAPDN *dn,
+ int flags,
+ void *ctx )
+{
+ assert( val != NULL );
+ assert( dn != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, ">>> dn%sDN: <%s>\n",
+ flags == SLAP_LDAPDN_PRETTY ? "Pretty" : "Normal",
+ val->bv_val ? val->bv_val : "" );
+
+ if ( val->bv_len == 0 ) {
+ return LDAP_SUCCESS;
+
+ } else if ( val->bv_len > SLAP_LDAPDN_MAXLEN ) {
+ return LDAP_INVALID_SYNTAX;
+
+ } else {
+ int rc;
+
+ /* FIXME: should be liberal in what we accept */
+ rc = ldap_bv2dn_x( val, dn, LDAP_DN_FORMAT_LDAP, ctx );
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ assert( strlen( val->bv_val ) == val->bv_len );
+
+ /*
+ * Schema-aware rewrite
+ */
+ if ( LDAPDN_rewrite( *dn, flags, ctx ) != LDAP_SUCCESS ) {
+ ldap_dnfree_x( *dn, ctx );
+ *dn = NULL;
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<<< dn%sDN\n",
+ flags == SLAP_LDAPDN_PRETTY ? "Pretty" : "Normal" );
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Combination of both dnPretty and dnNormalize
+ */
+int
+dnPrettyNormal(
+ Syntax *syntax,
+ struct berval *val,
+ struct berval *pretty,
+ struct berval *normal,
+ void *ctx)
+{
+ assert( val != NULL );
+ assert( pretty != NULL );
+ assert( normal != NULL );
+ Debug( LDAP_DEBUG_TRACE, ">>> dnPrettyNormal: <%s>\n", val->bv_val ? val->bv_val : "" );
+
+ if ( val->bv_len == 0 ) {
+ ber_dupbv_x( pretty, val, ctx );
+ ber_dupbv_x( normal, val, ctx );
+
+ } else if ( val->bv_len > SLAP_LDAPDN_MAXLEN ) {
+ /* too big */
+ return LDAP_INVALID_SYNTAX;
+
+ } else {
+ LDAPDN dn = NULL;
+ int rc;
+
+ pretty->bv_val = NULL;
+ normal->bv_val = NULL;
+ pretty->bv_len = 0;
+ normal->bv_len = 0;
+
+ /* FIXME: should be liberal in what we accept */
+ rc = ldap_bv2dn_x( val, &dn, LDAP_DN_FORMAT_LDAP, ctx );
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ assert( strlen( val->bv_val ) == val->bv_len );
+
+ /*
+ * Schema-aware rewrite
+ */
+ if ( LDAPDN_rewrite( dn, SLAP_LDAPDN_PRETTY, ctx ) != LDAP_SUCCESS ) {
+ ldap_dnfree_x( dn, ctx );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ rc = ldap_dn2bv_x( dn, pretty,
+ LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PRETTY, ctx );
+
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_dnfree_x( dn, ctx );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( LDAPDN_rewrite( dn, 0, ctx ) != LDAP_SUCCESS ) {
+ ldap_dnfree_x( dn, ctx );
+ ber_memfree_x( pretty->bv_val, ctx );
+ pretty->bv_val = NULL;
+ pretty->bv_len = 0;
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ rc = ldap_dn2bv_x( dn, normal,
+ LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PRETTY, ctx );
+
+ ldap_dnfree_x( dn, ctx );
+ if ( rc != LDAP_SUCCESS ) {
+ ber_memfree_x( pretty->bv_val, ctx );
+ pretty->bv_val = NULL;
+ pretty->bv_len = 0;
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<<< dnPrettyNormal: <%s>, <%s>\n",
+ pretty->bv_val ? pretty->bv_val : "",
+ normal->bv_val ? normal->bv_val : "" );
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * dnMatch routine
+ */
+int
+dnMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ int match;
+ struct berval *asserted = (struct berval *) assertedValue;
+
+ assert( matchp != NULL );
+ assert( value != NULL );
+ assert( assertedValue != NULL );
+ assert( !BER_BVISNULL( value ) );
+ assert( !BER_BVISNULL( asserted ) );
+
+ match = value->bv_len - asserted->bv_len;
+
+ if ( match == 0 ) {
+ match = memcmp( value->bv_val, asserted->bv_val,
+ value->bv_len );
+ }
+
+ Debug( LDAP_DEBUG_ARGS, "dnMatch %d\n\t\"%s\"\n\t\"%s\"\n",
+ match, value->bv_val, asserted->bv_val );
+
+ *matchp = match;
+ return LDAP_SUCCESS;
+}
+
+/*
+ * dnRelativeMatch routine
+ */
+int
+dnRelativeMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ int match;
+ struct berval *asserted = (struct berval *) assertedValue;
+
+ assert( matchp != NULL );
+ assert( value != NULL );
+ assert( assertedValue != NULL );
+ assert( !BER_BVISNULL( value ) );
+ assert( !BER_BVISNULL( asserted ) );
+
+ if( mr == slap_schema.si_mr_dnSubtreeMatch ) {
+ if( asserted->bv_len > value->bv_len ) {
+ match = -1;
+ } else if ( asserted->bv_len == value->bv_len ) {
+ match = memcmp( value->bv_val, asserted->bv_val,
+ value->bv_len );
+ } else {
+ if ( BER_BVISEMPTY( asserted ) ) {
+ match = 0;
+ } else if ( DN_SEPARATOR(
+ value->bv_val[value->bv_len - asserted->bv_len - 1] ))
+ {
+ match = memcmp(
+ &value->bv_val[value->bv_len - asserted->bv_len],
+ asserted->bv_val,
+ asserted->bv_len );
+ } else {
+ match = 1;
+ }
+ }
+
+ *matchp = match;
+ return LDAP_SUCCESS;
+ }
+
+ if( mr == slap_schema.si_mr_dnSuperiorMatch ) {
+ asserted = value;
+ value = (struct berval *) assertedValue;
+ mr = slap_schema.si_mr_dnSubordinateMatch;
+ }
+
+ if( mr == slap_schema.si_mr_dnSubordinateMatch ) {
+ if( asserted->bv_len >= value->bv_len ) {
+ match = -1;
+ } else {
+ if ( BER_BVISEMPTY( asserted ) ) {
+ match = 0;
+ } else if ( DN_SEPARATOR(
+ value->bv_val[value->bv_len - asserted->bv_len - 1] ))
+ {
+ match = memcmp(
+ &value->bv_val[value->bv_len - asserted->bv_len],
+ asserted->bv_val,
+ asserted->bv_len );
+ } else {
+ match = 1;
+ }
+ }
+
+ *matchp = match;
+ return LDAP_SUCCESS;
+ }
+
+ if( mr == slap_schema.si_mr_dnOneLevelMatch ) {
+ if( asserted->bv_len >= value->bv_len ) {
+ match = -1;
+ } else {
+ if( DN_SEPARATOR(
+ value->bv_val[value->bv_len - asserted->bv_len - 1] ))
+ {
+ match = memcmp(
+ &value->bv_val[value->bv_len - asserted->bv_len],
+ asserted->bv_val,
+ asserted->bv_len );
+
+ if( !match ) {
+ struct berval rdn;
+ rdn.bv_val = value->bv_val;
+ rdn.bv_len = value->bv_len - asserted->bv_len - 1;
+ match = dnIsOneLevelRDN( &rdn ) ? 0 : 1;
+ }
+ } else {
+ match = 1;
+ }
+ }
+
+ *matchp = match;
+ return LDAP_SUCCESS;
+ }
+
+ /* should not be reachable */
+ assert( 0 );
+ return LDAP_OTHER;
+}
+
+int
+rdnMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ int match;
+ struct berval *asserted = (struct berval *) assertedValue;
+
+ assert( matchp != NULL );
+ assert( value != NULL );
+ assert( assertedValue != NULL );
+
+ match = value->bv_len - asserted->bv_len;
+
+ if ( match == 0 ) {
+ match = memcmp( value->bv_val, asserted->bv_val,
+ value->bv_len );
+ }
+
+ Debug( LDAP_DEBUG_ARGS, "rdnMatch %d\n\t\"%s\"\n\t\"%s\"\n",
+ match, value->bv_val, asserted->bv_val );
+
+ *matchp = match;
+ return LDAP_SUCCESS;
+}
+
+
+/*
+ * dnParent - dn's parent, in-place
+ * note: the incoming dn is assumed to be normalized/prettyfied,
+ * so that escaped rdn/ava separators are in '\'+hexpair form
+ *
+ * note: "dn" and "pdn" can point to the same berval;
+ * beware that, in this case, the pointer to the original buffer
+ * will get lost.
+ */
+void
+dnParent(
+ struct berval *dn,
+ struct berval *pdn )
+{
+ char *p;
+
+ p = ber_bvchr( dn, ',' );
+
+ /* one-level dn */
+ if ( p == NULL ) {
+ pdn->bv_val = dn->bv_val + dn->bv_len;
+ pdn->bv_len = 0;
+ return;
+ }
+
+ assert( DN_SEPARATOR( p[ 0 ] ) );
+ p++;
+
+ assert( ATTR_LEADCHAR( p[ 0 ] ) );
+ pdn->bv_len = dn->bv_len - (p - dn->bv_val);
+ pdn->bv_val = p;
+
+ return;
+}
+
+/*
+ * dnRdn - dn's rdn, in-place
+ * note: the incoming dn is assumed to be normalized/prettyfied,
+ * so that escaped rdn/ava separators are in '\'+hexpair form
+ */
+void
+dnRdn(
+ struct berval *dn,
+ struct berval *rdn )
+{
+ char *p;
+
+ *rdn = *dn;
+ p = ber_bvchr( dn, ',' );
+
+ /* one-level dn */
+ if ( p == NULL ) {
+ return;
+ }
+
+ assert( DN_SEPARATOR( p[ 0 ] ) );
+ assert( ATTR_LEADCHAR( p[ 1 ] ) );
+ rdn->bv_len = p - dn->bv_val;
+
+ return;
+}
+
+int
+dnExtractRdn(
+ struct berval *dn,
+ struct berval *rdn,
+ void *ctx )
+{
+ LDAPRDN tmpRDN;
+ const char *p;
+ int rc;
+
+ assert( dn != NULL );
+ assert( rdn != NULL );
+
+ if( dn->bv_len == 0 ) {
+ return LDAP_OTHER;
+ }
+
+ rc = ldap_bv2rdn_x( dn, &tmpRDN, (char **)&p, LDAP_DN_FORMAT_LDAP, ctx );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ rc = ldap_rdn2bv_x( tmpRDN, rdn, LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PRETTY,
+ ctx );
+
+ ldap_rdnfree_x( tmpRDN, ctx );
+ return rc;
+}
+
+/*
+ * We can assume the input is a prettied or normalized DN
+ */
+ber_len_t
+dn_rdnlen(
+ Backend *be,
+ struct berval *dn_in )
+{
+ const char *p;
+
+ assert( dn_in != NULL );
+
+ if ( dn_in == NULL ) {
+ return 0;
+ }
+
+ if ( !dn_in->bv_len ) {
+ return 0;
+ }
+
+ if ( be != NULL && be_issuffix( be, dn_in ) ) {
+ return 0;
+ }
+
+ p = ber_bvchr( dn_in, ',' );
+
+ return p ? (ber_len_t) (p - dn_in->bv_val) : dn_in->bv_len;
+}
+
+
+/* rdnValidate:
+ *
+ * LDAP_SUCCESS if rdn is a legal rdn;
+ * LDAP_INVALID_SYNTAX otherwise (including a sequence of rdns)
+ */
+int
+rdn_validate( struct berval *rdn )
+{
+#if 1
+ /* Major cheat!
+ * input is a pretty or normalized DN
+ * hence, we can just search for ','
+ */
+ if( rdn == NULL || rdn->bv_len == 0 ||
+ rdn->bv_len > SLAP_LDAPDN_MAXLEN )
+ {
+ return LDAP_INVALID_SYNTAX;
+ }
+ return ber_bvchr( rdn, ',' ) == NULL
+ ? LDAP_SUCCESS : LDAP_INVALID_SYNTAX;
+
+#else
+ LDAPRDN *RDN, **DN[ 2 ] = { &RDN, NULL };
+ const char *p;
+ int rc;
+
+ /*
+ * must be non-empty
+ */
+ if ( rdn == NULL || rdn == '\0' ) {
+ return 0;
+ }
+
+ /*
+ * must be parsable
+ */
+ rc = ldap_bv2rdn( rdn, &RDN, (char **)&p, LDAP_DN_FORMAT_LDAP );
+ if ( rc != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ /*
+ * Must be one-level
+ */
+ if ( p[ 0 ] != '\0' ) {
+ return 0;
+ }
+
+ /*
+ * Schema-aware validate
+ */
+ if ( rc == LDAP_SUCCESS ) {
+ rc = LDAPDN_validate( DN );
+ }
+ ldap_rdnfree( RDN );
+
+ /*
+ * Must validate (there's a repeated parsing ...)
+ */
+ return ( rc == LDAP_SUCCESS );
+#endif
+}
+
+
+/* build_new_dn:
+ *
+ * Used to create the new dn of entries being renamed.
+ *
+ * new_dn = parent (p_dn) + separator + rdn (newrdn) + null.
+ */
+
+void
+build_new_dn( struct berval * new_dn,
+ struct berval * parent_dn,
+ struct berval * newrdn,
+ void *memctx )
+{
+ char *ptr;
+
+ if ( parent_dn == NULL || parent_dn->bv_len == 0 ) {
+ ber_dupbv_x( new_dn, newrdn, memctx );
+ return;
+ }
+
+ new_dn->bv_len = parent_dn->bv_len + newrdn->bv_len + 1;
+ new_dn->bv_val = (char *) slap_sl_malloc( new_dn->bv_len + 1, memctx );
+
+ ptr = lutil_strncopy( new_dn->bv_val, newrdn->bv_val, newrdn->bv_len );
+ *ptr++ = ',';
+ strcpy( ptr, parent_dn->bv_val );
+}
+
+
+/*
+ * dnIsSuffix - tells whether suffix is a suffix of dn.
+ * Both dn and suffix must be normalized.
+ */
+int
+dnIsSuffix(
+ const struct berval *dn,
+ const struct berval *suffix )
+{
+ int d;
+
+ assert( dn != NULL );
+ assert( suffix != NULL );
+
+ d = dn->bv_len - suffix->bv_len;
+
+ /* empty suffix matches any dn */
+ if ( suffix->bv_len == 0 ) {
+ return 1;
+ }
+
+ /* suffix longer than dn */
+ if ( d < 0 ) {
+ return 0;
+ }
+
+ /* no rdn separator or escaped rdn separator */
+ if ( d > 1 && !DN_SEPARATOR( dn->bv_val[ d - 1 ] ) ) {
+ return 0;
+ }
+
+ /* no possible match or malformed dn */
+ if ( d == 1 ) {
+ return 0;
+ }
+
+ /* compare */
+ return( strncmp( dn->bv_val + d, suffix->bv_val, suffix->bv_len ) == 0 );
+}
+
+/*
+ * In place; assumes:
+ * - ndn is normalized
+ * - nbase is normalized
+ * - dnIsSuffix( ndn, nbase ) == TRUE
+ * - LDAP_SCOPE_DEFAULT == LDAP_SCOPE_SUBTREE
+ */
+int
+dnIsWithinScope( struct berval *ndn, struct berval *nbase, int scope )
+{
+ assert( ndn != NULL );
+ assert( nbase != NULL );
+ assert( !BER_BVISNULL( ndn ) );
+ assert( !BER_BVISNULL( nbase ) );
+
+ switch ( scope ) {
+ case LDAP_SCOPE_DEFAULT:
+ case LDAP_SCOPE_SUBTREE:
+ break;
+
+ case LDAP_SCOPE_BASE:
+ if ( ndn->bv_len != nbase->bv_len ) {
+ return 0;
+ }
+ break;
+
+ case LDAP_SCOPE_ONELEVEL: {
+ struct berval pndn;
+ dnParent( ndn, &pndn );
+ if ( pndn.bv_len != nbase->bv_len ) {
+ return 0;
+ }
+ } break;
+
+ case LDAP_SCOPE_SUBORDINATE:
+ if ( ndn->bv_len == nbase->bv_len ) {
+ return 0;
+ }
+ break;
+
+ /* unknown scope */
+ default:
+ return -1;
+ }
+
+ return 1;
+}
+
+/*
+ * In place; assumes:
+ * - ndn is normalized
+ * - nbase is normalized
+ * - LDAP_SCOPE_DEFAULT == LDAP_SCOPE_SUBTREE
+ */
+int
+dnIsSuffixScope( struct berval *ndn, struct berval *nbase, int scope )
+{
+ if ( !dnIsSuffix( ndn, nbase ) ) {
+ return 0;
+ }
+
+ return dnIsWithinScope( ndn, nbase, scope );
+}
+
+int
+dnIsOneLevelRDN( struct berval *rdn )
+{
+ ber_len_t len = rdn->bv_len;
+ for ( ; len--; ) {
+ if ( DN_SEPARATOR( rdn->bv_val[ len ] ) ) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+#ifdef HAVE_TLS
+static SLAP_CERT_MAP_FN *DNX509PeerNormalizeCertMap = NULL;
+#endif
+
+int register_certificate_map_function(SLAP_CERT_MAP_FN *fn)
+{
+#ifdef HAVE_TLS
+ if ( DNX509PeerNormalizeCertMap == NULL ) {
+ DNX509PeerNormalizeCertMap = fn;
+ return 0;
+ }
+#endif
+
+ return -1;
+}
+
+/*
+ * Convert an X.509 DN into a normalized LDAP DN
+ */
+int
+dnX509normalize( void *x509_name, struct berval *out )
+{
+ /* Invoke the LDAP library's converter with our schema-rewriter */
+ int rc = ldap_X509dn2bv( x509_name, out, LDAPDN_rewrite, 0 );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "dnX509Normalize: <%s> (%d)\n",
+ BER_BVISNULL( out ) ? "(null)" : out->bv_val, rc );
+
+ return rc;
+}
+
+#ifdef HAVE_TLS
+/*
+ * Get the TLS session's peer's DN into a normalized LDAP DN
+ */
+int
+dnX509peerNormalize( void *ssl, struct berval *dn )
+{
+ int rc = LDAP_INVALID_CREDENTIALS;
+
+ if ( DNX509PeerNormalizeCertMap != NULL )
+ rc = (*DNX509PeerNormalizeCertMap)( ssl, dn );
+
+ if ( rc != LDAP_SUCCESS ) {
+ rc = ldap_pvt_tls_get_peer_dn( ssl, dn,
+ (LDAPDN_rewrite_dummy *)LDAPDN_rewrite, 0 );
+ }
+
+ return rc;
+}
+#endif
diff --git a/servers/slapd/entry.c b/servers/slapd/entry.c
new file mode 100644
index 0000000..4d9ab28
--- /dev/null
+++ b/servers/slapd/entry.c
@@ -0,0 +1,1024 @@
+/* entry.c - routines for dealing with entries */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "ldif.h"
+
+static char *ebuf; /* buf returned by entry2str */
+static char *ecur; /* pointer to end of currently used ebuf */
+static int emaxsize;/* max size of ebuf */
+
+/*
+ * Empty root entry
+ */
+const Entry slap_entry_root = {
+ NOID, { 0, "" }, { 0, "" }, NULL, 0, { 0, "" }, NULL
+};
+
+/*
+ * these mutexes must be used when calling the entry2str()
+ * routine since it returns a pointer to static data.
+ */
+ldap_pvt_thread_mutex_t entry2str_mutex;
+
+static const struct berval dn_bv = BER_BVC("dn");
+
+/*
+ * Entry free list
+ *
+ * Allocate in chunks, minimum of 1000 at a time.
+ */
+#define CHUNK_SIZE 1000
+typedef struct slap_list {
+ struct slap_list *next;
+} slap_list;
+static slap_list *entry_chunks;
+static Entry *entry_list;
+static ldap_pvt_thread_mutex_t entry_mutex;
+
+int entry_destroy(void)
+{
+ slap_list *e;
+ if ( ebuf ) free( ebuf );
+ ebuf = NULL;
+ ecur = NULL;
+ emaxsize = 0;
+
+ for ( e=entry_chunks; e; e=entry_chunks ) {
+ entry_chunks = e->next;
+ free( e );
+ }
+
+ ldap_pvt_thread_mutex_destroy( &entry_mutex );
+ ldap_pvt_thread_mutex_destroy( &entry2str_mutex );
+ return attr_destroy();
+}
+
+int
+entry_init(void)
+{
+ ldap_pvt_thread_mutex_init( &entry2str_mutex );
+ ldap_pvt_thread_mutex_init( &entry_mutex );
+ return attr_init();
+}
+
+Entry *
+str2entry( char *s )
+{
+ return str2entry2( s, 1 );
+}
+
+#define bvcasematch(bv1, bv2) (ber_bvstrcasecmp(bv1, bv2) == 0)
+
+Entry *
+str2entry2( char *s, int checkvals )
+{
+ int rc;
+ Entry *e;
+ struct berval *type, *vals, *nvals;
+ char *freeval;
+ AttributeDescription *ad, *ad_prev;
+ const char *text;
+ char *next;
+ int attr_cnt;
+ int i, lines;
+ Attribute ahead, *atail;
+
+ /*
+ * LDIF is used as the string format.
+ * An entry looks like this:
+ *
+ * dn: <dn>\n
+ * [<attr>:[:] <value>\n]
+ * [<tab><continuedvalue>\n]*
+ * ...
+ *
+ * If a double colon is used after a type, it means the
+ * following value is encoded as a base 64 string. This
+ * happens if the value contains a non-printing character
+ * or newline.
+ */
+
+ Debug( LDAP_DEBUG_TRACE, "=> str2entry: \"%s\"\n",
+ s ? s : "NULL" );
+
+ e = entry_alloc();
+
+ if( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= str2entry NULL (entry allocation failed)\n" );
+ return( NULL );
+ }
+
+ /* initialize entry */
+ e->e_id = NOID;
+
+ /* dn + attributes */
+ atail = &ahead;
+ ahead.a_next = NULL;
+ ad = NULL;
+ ad_prev = NULL;
+ attr_cnt = 0;
+ next = s;
+
+ lines = ldif_countlines( s );
+ type = ch_calloc( 1, (lines+1)*3*sizeof(struct berval)+lines );
+ vals = type+lines+1;
+ nvals = vals+lines+1;
+ freeval = (char *)(nvals+lines+1);
+ i = -1;
+
+ /* parse into individual values, record DN */
+ while ( (s = ldif_getline( &next )) != NULL ) {
+ int freev;
+ if ( *s == '\n' || *s == '\0' ) {
+ break;
+ }
+ i++;
+ if (i >= lines) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= str2entry ran past end of entry\n" );
+ goto fail;
+ }
+
+ rc = ldif_parse_line2( s, type+i, vals+i, &freev );
+ freeval[i] = freev;
+ if ( rc ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= str2entry NULL (parse_line)\n" );
+ continue;
+ }
+
+ if ( bvcasematch( &type[i], &dn_bv ) ) {
+ if ( e->e_dn != NULL ) {
+ Debug( LDAP_DEBUG_ANY, "str2entry: "
+ "entry %ld has multiple DNs \"%s\" and \"%s\"\n",
+ (long) e->e_id, e->e_dn, vals[i].bv_val );
+ goto fail;
+ }
+
+ rc = dnPrettyNormal( NULL, &vals[i], &e->e_name, &e->e_nname, NULL );
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "str2entry: "
+ "entry %ld has invalid DN \"%s\"\n",
+ (long) e->e_id, vals[i].bv_val );
+ goto fail;
+ }
+ if ( freeval[i] ) free( vals[i].bv_val );
+ vals[i].bv_val = NULL;
+ i--;
+ continue;
+ }
+ }
+ lines = i+1;
+
+ /* check to make sure there was a dn: line */
+ if ( BER_BVISNULL( &e->e_name )) {
+ Debug( LDAP_DEBUG_ANY, "str2entry: entry %ld has no dn\n",
+ (long) e->e_id );
+ goto fail;
+ }
+
+ /* Make sure all attributes with multiple values are contiguous */
+ if ( checkvals ) {
+ int j, k;
+ struct berval bv;
+ int fv;
+
+ for (i=0; i<lines; i++) {
+ for ( j=i+1; j<lines; j++ ) {
+ if ( bvcasematch( type+i, type+j )) {
+ /* out of order, move intervening attributes down */
+ if ( j != i+1 ) {
+ bv = vals[j];
+ fv = freeval[j];
+ for ( k=j; k>i; k-- ) {
+ type[k] = type[k-1];
+ vals[k] = vals[k-1];
+ freeval[k] = freeval[k-1];
+ }
+ k++;
+ type[k] = type[i];
+ vals[k] = bv;
+ freeval[k] = fv;
+ }
+ i++;
+ }
+ }
+ }
+ }
+
+ if ( lines > 0 ) {
+ for ( i=0; i<=lines; i++ ) {
+ ad_prev = ad;
+ if ( !ad || ( i<lines && !bvcasematch( type+i, &ad->ad_cname ))) {
+ ad = NULL;
+ rc = slap_bv2ad( type+i, &ad, &text );
+
+ if( rc != LDAP_SUCCESS ) {
+ int wtool = ( slapMode & (SLAP_TOOL_MODE|SLAP_TOOL_READONLY|SLAP_TOOL_NO_SCHEMA_CHECK) ) == SLAP_TOOL_MODE;
+ Debug( wtool ? LDAP_DEBUG_ANY : LDAP_DEBUG_TRACE,
+ "<= str2entry: str2ad(%s): %s\n", type[i].bv_val, text );
+ if( wtool ) {
+ goto fail;
+ }
+
+ rc = slap_bv2undef_ad( type+i, &ad, &text, 0 );
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= str2entry: slap_str2undef_ad(%s): %s\n",
+ type[i].bv_val, text );
+ goto fail;
+ }
+ }
+
+ /* require ';binary' when appropriate (ITS#5071) */
+ if ( slap_syntax_is_binary( ad->ad_type->sat_syntax ) && !slap_ad_is_binary( ad ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "str2entry: attributeType %s #%d: "
+ "needs ';binary' transfer as per syntax %s\n",
+ ad->ad_cname.bv_val, 0,
+ ad->ad_type->sat_syntax->ssyn_oid );
+ goto fail;
+ }
+ }
+
+ if (( ad_prev && ad != ad_prev ) || ( i == lines )) {
+ int j, k;
+ atail->a_next = attr_alloc( NULL );
+ atail = atail->a_next;
+ atail->a_flags = 0;
+ atail->a_numvals = attr_cnt;
+ atail->a_desc = ad_prev;
+ atail->a_vals = ch_malloc( (attr_cnt + 1) * sizeof(struct berval));
+ if( ad_prev->ad_type->sat_equality &&
+ ad_prev->ad_type->sat_equality->smr_normalize )
+ atail->a_nvals = ch_malloc( (attr_cnt + 1) * sizeof(struct berval));
+ else
+ atail->a_nvals = NULL;
+ k = i - attr_cnt;
+ for ( j=0; j<attr_cnt; j++ ) {
+ if ( freeval[k] )
+ atail->a_vals[j] = vals[k];
+ else
+ ber_dupbv( atail->a_vals+j, &vals[k] );
+ vals[k].bv_val = NULL;
+ if ( atail->a_nvals ) {
+ atail->a_nvals[j] = nvals[k];
+ nvals[k].bv_val = NULL;
+ }
+ k++;
+ }
+ BER_BVZERO( &atail->a_vals[j] );
+ if ( atail->a_nvals ) {
+ BER_BVZERO( &atail->a_nvals[j] );
+ } else {
+ atail->a_nvals = atail->a_vals;
+ }
+ attr_cnt = 0;
+ /* FIXME: we only need this when migrating from an unsorted DB */
+ if ( atail->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL ) {
+ rc = slap_sort_vals( (Modifications *)atail, &text, &j, NULL );
+ if ( rc == LDAP_SUCCESS ) {
+ atail->a_flags |= SLAP_ATTR_SORTED_VALS;
+ } else if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "str2entry: attributeType %s value #%d provided more than once\n",
+ atail->a_desc->ad_cname.bv_val, j );
+ goto fail;
+ }
+ }
+ if ( i == lines ) break;
+ }
+
+ if ( BER_BVISNULL( &vals[i] ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "str2entry: attributeType %s #%d: "
+ "no value\n",
+ ad->ad_cname.bv_val, attr_cnt );
+ goto fail;
+ }
+
+ if ( ad->ad_type->sat_equality &&
+ ad->ad_type->sat_equality->smr_normalize )
+ {
+ rc = ordered_value_normalize(
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ ad,
+ ad->ad_type->sat_equality,
+ &vals[i], &nvals[i], NULL );
+
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= str2entry NULL (smr_normalize %s %d)\n", ad->ad_cname.bv_val, rc );
+ goto fail;
+ }
+ }
+
+ attr_cnt++;
+ }
+ }
+
+ free( type );
+ atail->a_next = NULL;
+ e->e_attrs = ahead.a_next;
+
+ Debug(LDAP_DEBUG_TRACE, "<= str2entry(%s) -> 0x%lx\n",
+ e->e_dn, (unsigned long) e );
+ return( e );
+
+fail:
+ for ( i=0; i<lines; i++ ) {
+ if ( freeval[i] ) free( vals[i].bv_val );
+ free( nvals[i].bv_val );
+ }
+ free( type );
+ entry_free( e );
+ return NULL;
+}
+
+
+#define GRABSIZE BUFSIZ
+
+#define MAKE_SPACE( n ) { \
+ while ( ecur + (n) > ebuf + emaxsize ) { \
+ ptrdiff_t offset; \
+ offset = (int) (ecur - ebuf); \
+ ebuf = ch_realloc( ebuf, \
+ emaxsize + GRABSIZE ); \
+ emaxsize += GRABSIZE; \
+ ecur = ebuf + offset; \
+ } \
+ }
+
+/* NOTE: only preserved for binary compatibility */
+char *
+entry2str(
+ Entry *e,
+ int *len )
+{
+ return entry2str_wrap( e, len, LDIF_LINE_WIDTH );
+}
+
+char *
+entry2str_wrap(
+ Entry *e,
+ int *len,
+ ber_len_t wrap )
+{
+ Attribute *a;
+ struct berval *bv;
+ int i;
+ ber_len_t tmplen;
+
+ assert( e != NULL );
+
+ /*
+ * In string format, an entry looks like this:
+ * dn: <dn>\n
+ * [<attr>: <value>\n]*
+ */
+
+ ecur = ebuf;
+
+ /* put the dn */
+ if ( e->e_dn != NULL ) {
+ /* put "dn: <dn>" */
+ tmplen = e->e_name.bv_len;
+ MAKE_SPACE( LDIF_SIZE_NEEDED( 2, tmplen ));
+ ldif_sput_wrap( &ecur, LDIF_PUT_VALUE, "dn", e->e_dn, tmplen, wrap );
+ }
+
+ /* put the attributes */
+ for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
+ /* put "<type>:[:] <value>" line for each value */
+ for ( i = 0; a->a_vals[i].bv_val != NULL; i++ ) {
+ bv = &a->a_vals[i];
+ tmplen = a->a_desc->ad_cname.bv_len;
+ MAKE_SPACE( LDIF_SIZE_NEEDED( tmplen, bv->bv_len ));
+ ldif_sput_wrap( &ecur, LDIF_PUT_VALUE,
+ a->a_desc->ad_cname.bv_val,
+ bv->bv_val, bv->bv_len, wrap );
+ }
+ }
+ MAKE_SPACE( 1 );
+ *ecur = '\0';
+ *len = ecur - ebuf;
+
+ return( ebuf );
+}
+
+void
+entry_clean( Entry *e )
+{
+ /* free an entry structure */
+ assert( e != NULL );
+
+ /* e_private must be freed by the caller */
+ assert( e->e_private == NULL );
+
+ e->e_id = 0;
+
+ /* free DNs */
+ if ( !BER_BVISNULL( &e->e_name ) ) {
+ free( e->e_name.bv_val );
+ BER_BVZERO( &e->e_name );
+ }
+ if ( !BER_BVISNULL( &e->e_nname ) ) {
+ free( e->e_nname.bv_val );
+ BER_BVZERO( &e->e_nname );
+ }
+
+ if ( !BER_BVISNULL( &e->e_bv ) ) {
+ free( e->e_bv.bv_val );
+ BER_BVZERO( &e->e_bv );
+ }
+
+ /* free attributes */
+ if ( e->e_attrs ) {
+ attrs_free( e->e_attrs );
+ e->e_attrs = NULL;
+ }
+
+ e->e_ocflags = 0;
+}
+
+void
+entry_free( Entry *e )
+{
+ entry_clean( e );
+
+ ldap_pvt_thread_mutex_lock( &entry_mutex );
+ e->e_private = entry_list;
+ entry_list = e;
+ ldap_pvt_thread_mutex_unlock( &entry_mutex );
+}
+
+/* These parameters work well on AMD64 */
+#if 0
+#define STRIDE 8
+#define STRIPE 5
+#else
+#define STRIDE 1
+#define STRIPE 1
+#endif
+#define STRIDE_FACTOR (STRIDE*STRIPE)
+
+int
+entry_prealloc( int num )
+{
+ Entry *e, **prev, *tmp;
+ slap_list *s;
+ int i, j;
+
+ if (!num) return 0;
+
+#if STRIDE_FACTOR > 1
+ /* Round up to our stride factor */
+ num += STRIDE_FACTOR-1;
+ num /= STRIDE_FACTOR;
+ num *= STRIDE_FACTOR;
+#endif
+
+ s = ch_calloc( 1, sizeof(slap_list) + num * sizeof(Entry));
+ s->next = entry_chunks;
+ entry_chunks = s;
+
+ prev = &tmp;
+ for (i=0; i<STRIPE; i++) {
+ e = (Entry *)(s+1);
+ e += i;
+ for (j=i; j<num; j+= STRIDE) {
+ *prev = e;
+ prev = (Entry **)&e->e_private;
+ e += STRIDE;
+ }
+ }
+ *prev = entry_list;
+ entry_list = (Entry *)(s+1);
+
+ return 0;
+}
+
+Entry *
+entry_alloc( void )
+{
+ Entry *e;
+
+ ldap_pvt_thread_mutex_lock( &entry_mutex );
+ if ( !entry_list )
+ entry_prealloc( CHUNK_SIZE );
+ e = entry_list;
+ entry_list = e->e_private;
+ e->e_private = NULL;
+ ldap_pvt_thread_mutex_unlock( &entry_mutex );
+
+ return e;
+}
+
+
+/*
+ * These routines are used only by Backend.
+ *
+ * the Entry has three entry points (ways to find things):
+ *
+ * by entry e.g., if you already have an entry from the cache
+ * and want to delete it. (really by entry ptr)
+ * by dn e.g., when looking for the base object of a search
+ * by id e.g., for search candidates
+ *
+ * these correspond to three different avl trees that are maintained.
+ */
+
+int
+entry_cmp( Entry *e1, Entry *e2 )
+{
+ return SLAP_PTRCMP( e1, e2 );
+}
+
+int
+entry_dn_cmp( const void *v_e1, const void *v_e2 )
+{
+ /* compare their normalized UPPERCASED dn's */
+ const Entry *e1 = v_e1, *e2 = v_e2;
+
+ return ber_bvcmp( &e1->e_nname, &e2->e_nname );
+}
+
+int
+entry_id_cmp( const void *v_e1, const void *v_e2 )
+{
+ const Entry *e1 = v_e1, *e2 = v_e2;
+ return( e1->e_id < e2->e_id ? -1 : (e1->e_id > e2->e_id ? 1 : 0) );
+}
+
+/* This is like a ber_len */
+#define entry_lenlen(l) (((l) < 0x80) ? 1 : ((l) < 0x100) ? 2 : \
+ ((l) < 0x10000) ? 3 : ((l) < 0x1000000) ? 4 : 5)
+
+static void
+entry_putlen(unsigned char **buf, ber_len_t len)
+{
+ ber_len_t lenlen = entry_lenlen(len);
+
+ if (lenlen == 1) {
+ **buf = (unsigned char) len;
+ } else {
+ int i;
+ **buf = 0x80 | ((unsigned char) lenlen - 1);
+ for (i=lenlen-1; i>0; i--) {
+ (*buf)[i] = (unsigned char) len;
+ len >>= 8;
+ }
+ }
+ *buf += lenlen;
+}
+
+static ber_len_t
+entry_getlen(unsigned char **buf)
+{
+ ber_len_t len;
+ int i;
+
+ len = *(*buf)++;
+ if (len <= 0x7f)
+ return len;
+ i = len & 0x7f;
+ len = 0;
+ for (;i > 0; i--) {
+ len <<= 8;
+ len |= *(*buf)++;
+ }
+ return len;
+}
+
+/* Count up the sizes of the components of an entry */
+void entry_partsize(Entry *e, ber_len_t *plen,
+ int *pnattrs, int *pnvals, int norm)
+{
+ ber_len_t len, dnlen, ndnlen;
+ int i, nat = 0, nval = 0;
+ Attribute *a;
+
+ dnlen = e->e_name.bv_len;
+ len = dnlen + 1; /* trailing NUL byte */
+ len += entry_lenlen(dnlen);
+ if (norm) {
+ ndnlen = e->e_nname.bv_len;
+ len += ndnlen + 1;
+ len += entry_lenlen(ndnlen);
+ }
+ for (a=e->e_attrs; a; a=a->a_next) {
+ /* For AttributeDesc, we only store the attr name */
+ nat++;
+ len += a->a_desc->ad_cname.bv_len+1;
+ len += entry_lenlen(a->a_desc->ad_cname.bv_len);
+ for (i=0; a->a_vals[i].bv_val; i++) {
+ nval++;
+ len += a->a_vals[i].bv_len + 1;
+ len += entry_lenlen(a->a_vals[i].bv_len);
+ }
+ len += entry_lenlen(i);
+ nval++; /* empty berval at end */
+ if (norm && a->a_nvals != a->a_vals) {
+ for (i=0; a->a_nvals[i].bv_val; i++) {
+ nval++;
+ len += a->a_nvals[i].bv_len + 1;
+ len += entry_lenlen(a->a_nvals[i].bv_len);
+ }
+ len += entry_lenlen(i); /* i nvals */
+ nval++;
+ } else {
+ len += entry_lenlen(0); /* 0 nvals */
+ }
+ }
+ len += entry_lenlen(nat);
+ len += entry_lenlen(nval);
+ *plen = len;
+ *pnattrs = nat;
+ *pnvals = nval;
+}
+
+/* Add up the size of the entry for a flattened buffer */
+ber_len_t entry_flatsize(Entry *e, int norm)
+{
+ ber_len_t len;
+ int nattrs, nvals;
+
+ entry_partsize(e, &len, &nattrs, &nvals, norm);
+ len += sizeof(Entry) + (nattrs * sizeof(Attribute)) +
+ (nvals * sizeof(struct berval));
+ return len;
+}
+
+/* Flatten an Entry into a buffer. The buffer is filled with just the
+ * strings/bervals of all the entry components. Each field is preceded
+ * by its length, encoded the way ber_put_len works. Every field is NUL
+ * terminated. The entire buffer size is precomputed so that a single
+ * malloc can be performed. The entry size is also recorded,
+ * to aid in entry_decode.
+ */
+int entry_encode(Entry *e, struct berval *bv)
+{
+ ber_len_t len, dnlen, ndnlen, i;
+ int nattrs, nvals;
+ Attribute *a;
+ unsigned char *ptr;
+
+ Debug( LDAP_DEBUG_TRACE, "=> entry_encode(0x%08lx): %s\n",
+ (long) e->e_id, e->e_dn );
+
+ dnlen = e->e_name.bv_len;
+ ndnlen = e->e_nname.bv_len;
+
+ entry_partsize( e, &len, &nattrs, &nvals, 1 );
+
+ bv->bv_len = len;
+ bv->bv_val = ch_malloc(len);
+ ptr = (unsigned char *)bv->bv_val;
+ entry_putlen(&ptr, nattrs);
+ entry_putlen(&ptr, nvals);
+ entry_putlen(&ptr, dnlen);
+ AC_MEMCPY(ptr, e->e_dn, dnlen);
+ ptr += dnlen;
+ *ptr++ = '\0';
+ entry_putlen(&ptr, ndnlen);
+ AC_MEMCPY(ptr, e->e_ndn, ndnlen);
+ ptr += ndnlen;
+ *ptr++ = '\0';
+
+ for (a=e->e_attrs; a; a=a->a_next) {
+ entry_putlen(&ptr, a->a_desc->ad_cname.bv_len);
+ AC_MEMCPY(ptr, a->a_desc->ad_cname.bv_val,
+ a->a_desc->ad_cname.bv_len);
+ ptr += a->a_desc->ad_cname.bv_len;
+ *ptr++ = '\0';
+ if (a->a_vals) {
+ for (i=0; a->a_vals[i].bv_val; i++);
+ assert( i == a->a_numvals );
+ entry_putlen(&ptr, i);
+ for (i=0; a->a_vals[i].bv_val; i++) {
+ entry_putlen(&ptr, a->a_vals[i].bv_len);
+ AC_MEMCPY(ptr, a->a_vals[i].bv_val,
+ a->a_vals[i].bv_len);
+ ptr += a->a_vals[i].bv_len;
+ *ptr++ = '\0';
+ }
+ if (a->a_nvals != a->a_vals) {
+ entry_putlen(&ptr, i);
+ for (i=0; a->a_nvals[i].bv_val; i++) {
+ entry_putlen(&ptr, a->a_nvals[i].bv_len);
+ AC_MEMCPY(ptr, a->a_nvals[i].bv_val,
+ a->a_nvals[i].bv_len);
+ ptr += a->a_nvals[i].bv_len;
+ *ptr++ = '\0';
+ }
+ } else {
+ entry_putlen(&ptr, 0);
+ }
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<= entry_encode(0x%08lx): %s\n",
+ (long) e->e_id, e->e_dn );
+
+ return 0;
+}
+
+/* Retrieve an Entry that was stored using entry_encode above.
+ * First entry_header must be called to decode the size of the entry.
+ * Then a single block of memory must be malloc'd to accommodate the
+ * bervals and the bulk data. Next the bulk data is retrieved from
+ * the DB and parsed by entry_decode.
+ *
+ * Note: everything is stored in a single contiguous block, so
+ * you can not free individual attributes or names from this
+ * structure. Attempting to do so will likely corrupt memory.
+ */
+int entry_header(EntryHeader *eh)
+{
+ unsigned char *ptr = (unsigned char *)eh->bv.bv_val;
+
+ /* Some overlays can create empty entries
+ * so don't check for zeros here.
+ */
+ eh->nattrs = entry_getlen(&ptr);
+ eh->nvals = entry_getlen(&ptr);
+ eh->data = (char *)ptr;
+ return LDAP_SUCCESS;
+}
+
+int
+entry_decode_dn( EntryHeader *eh, struct berval *dn, struct berval *ndn )
+{
+ int i;
+ unsigned char *ptr = (unsigned char *)eh->bv.bv_val;
+
+ assert( dn != NULL || ndn != NULL );
+
+ ptr = (unsigned char *)eh->data;
+ i = entry_getlen(&ptr);
+ if ( dn != NULL ) {
+ dn->bv_val = (char *) ptr;
+ dn->bv_len = i;
+ }
+
+ if ( ndn != NULL ) {
+ ptr += i + 1;
+ i = entry_getlen(&ptr);
+ ndn->bv_val = (char *) ptr;
+ ndn->bv_len = i;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "entry_decode_dn: \"%s\"\n",
+ dn ? dn->bv_val : ndn->bv_val );
+
+ return 0;
+}
+
+#ifdef SLAP_ZONE_ALLOC
+int entry_decode(EntryHeader *eh, Entry **e, void *ctx)
+#else
+int entry_decode(EntryHeader *eh, Entry **e)
+#endif
+{
+ int i, j, nattrs, nvals;
+ int rc;
+ Attribute *a;
+ Entry *x;
+ const char *text;
+ AttributeDescription *ad;
+ unsigned char *ptr = (unsigned char *)eh->bv.bv_val;
+ BerVarray bptr;
+
+ nattrs = eh->nattrs;
+ nvals = eh->nvals;
+ x = entry_alloc();
+ x->e_attrs = attrs_alloc( nattrs );
+ ptr = (unsigned char *)eh->data;
+ i = entry_getlen(&ptr);
+ x->e_name.bv_val = (char *) ptr;
+ x->e_name.bv_len = i;
+ ptr += i+1;
+ i = entry_getlen(&ptr);
+ x->e_nname.bv_val = (char *) ptr;
+ x->e_nname.bv_len = i;
+ ptr += i+1;
+ Debug( LDAP_DEBUG_TRACE,
+ "entry_decode: \"%s\"\n",
+ x->e_dn );
+ x->e_bv = eh->bv;
+
+ a = x->e_attrs;
+ bptr = (BerVarray)eh->bv.bv_val;
+
+ while ((i = entry_getlen(&ptr))) {
+ struct berval bv;
+ bv.bv_len = i;
+ bv.bv_val = (char *) ptr;
+ ad = NULL;
+ rc = slap_bv2ad( &bv, &ad, &text );
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<= entry_decode: str2ad(%s): %s\n", ptr, text );
+ rc = slap_bv2undef_ad( &bv, &ad, &text, 0 );
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= entry_decode: slap_str2undef_ad(%s): %s\n",
+ ptr, text );
+ return rc;
+ }
+ }
+ ptr += i + 1;
+ a->a_desc = ad;
+ a->a_flags = SLAP_ATTR_DONT_FREE_DATA | SLAP_ATTR_DONT_FREE_VALS;
+ j = entry_getlen(&ptr);
+ a->a_numvals = j;
+ a->a_vals = bptr;
+
+ while (j) {
+ i = entry_getlen(&ptr);
+ bptr->bv_len = i;
+ bptr->bv_val = (char *)ptr;
+ ptr += i+1;
+ bptr++;
+ j--;
+ }
+ bptr->bv_val = NULL;
+ bptr->bv_len = 0;
+ bptr++;
+
+ j = entry_getlen(&ptr);
+ if (j) {
+ a->a_nvals = bptr;
+ while (j) {
+ i = entry_getlen(&ptr);
+ bptr->bv_len = i;
+ bptr->bv_val = (char *)ptr;
+ ptr += i+1;
+ bptr++;
+ j--;
+ }
+ bptr->bv_val = NULL;
+ bptr->bv_len = 0;
+ bptr++;
+ } else {
+ a->a_nvals = a->a_vals;
+ }
+ /* FIXME: This is redundant once a sorted entry is saved into the DB */
+ if ( a->a_desc->ad_type->sat_flags & SLAP_AT_SORTED_VAL ) {
+ rc = slap_sort_vals( (Modifications *)a, &text, &j, NULL );
+ if ( rc == LDAP_SUCCESS ) {
+ a->a_flags |= SLAP_ATTR_SORTED_VALS;
+ } else if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
+ /* should never happen */
+ Debug( LDAP_DEBUG_ANY,
+ "entry_decode: attributeType %s value #%d provided more than once\n",
+ a->a_desc->ad_cname.bv_val, j );
+ return rc;
+ }
+ }
+ a = a->a_next;
+ nattrs--;
+ if ( !nattrs )
+ break;
+ }
+
+ Debug(LDAP_DEBUG_TRACE, "<= entry_decode(%s)\n",
+ x->e_dn );
+ *e = x;
+ return 0;
+}
+
+Entry *
+entry_dup2( Entry *dest, Entry *source )
+{
+ assert( dest != NULL );
+ assert( source != NULL );
+
+ assert( dest->e_private == NULL );
+
+ dest->e_id = source->e_id;
+ ber_dupbv( &dest->e_name, &source->e_name );
+ ber_dupbv( &dest->e_nname, &source->e_nname );
+ dest->e_attrs = attrs_dup( source->e_attrs );
+ dest->e_ocflags = source->e_ocflags;
+
+ return dest;
+}
+
+Entry *
+entry_dup( Entry *e )
+{
+ return entry_dup2( entry_alloc(), e );
+}
+
+#if 1
+/* Duplicates an entry using a single malloc. Saves CPU time, increases
+ * heap usage because a single large malloc is harder to satisfy than
+ * lots of small ones, and the freed space isn't as easily reusable.
+ *
+ * Probably not worth using this function.
+ */
+Entry *entry_dup_bv( Entry *e )
+{
+ ber_len_t len;
+ int nattrs, nvals;
+ Entry *ret;
+ struct berval *bvl;
+ char *ptr;
+ Attribute *src, *dst;
+
+ ret = entry_alloc();
+
+ entry_partsize(e, &len, &nattrs, &nvals, 1);
+ ret->e_id = e->e_id;
+ ret->e_attrs = attrs_alloc( nattrs );
+ ret->e_ocflags = e->e_ocflags;
+ ret->e_bv.bv_len = len + nvals * sizeof(struct berval);
+ ret->e_bv.bv_val = ch_malloc( ret->e_bv.bv_len );
+
+ bvl = (struct berval *)ret->e_bv.bv_val;
+ ptr = (char *)(bvl + nvals);
+
+ ret->e_name.bv_len = e->e_name.bv_len;
+ ret->e_name.bv_val = ptr;
+ AC_MEMCPY( ptr, e->e_name.bv_val, e->e_name.bv_len );
+ ptr += e->e_name.bv_len;
+ *ptr++ = '\0';
+
+ ret->e_nname.bv_len = e->e_nname.bv_len;
+ ret->e_nname.bv_val = ptr;
+ AC_MEMCPY( ptr, e->e_nname.bv_val, e->e_nname.bv_len );
+ ptr += e->e_name.bv_len;
+ *ptr++ = '\0';
+
+ dst = ret->e_attrs;
+ for (src = e->e_attrs; src; src=src->a_next,dst=dst->a_next ) {
+ int i;
+ dst->a_desc = src->a_desc;
+ dst->a_flags = SLAP_ATTR_DONT_FREE_DATA | SLAP_ATTR_DONT_FREE_VALS;
+ dst->a_vals = bvl;
+ dst->a_numvals = src->a_numvals;
+ for ( i=0; src->a_vals[i].bv_val; i++ ) {
+ bvl->bv_len = src->a_vals[i].bv_len;
+ bvl->bv_val = ptr;
+ AC_MEMCPY( ptr, src->a_vals[i].bv_val, bvl->bv_len );
+ ptr += bvl->bv_len;
+ *ptr++ = '\0';
+ bvl++;
+ }
+ BER_BVZERO(bvl);
+ bvl++;
+ if ( src->a_vals != src->a_nvals ) {
+ dst->a_nvals = bvl;
+ for ( i=0; src->a_nvals[i].bv_val; i++ ) {
+ bvl->bv_len = src->a_nvals[i].bv_len;
+ bvl->bv_val = ptr;
+ AC_MEMCPY( ptr, src->a_nvals[i].bv_val, bvl->bv_len );
+ ptr += bvl->bv_len;
+ *ptr++ = '\0';
+ bvl++;
+ }
+ BER_BVZERO(bvl);
+ bvl++;
+ }
+ }
+ return ret;
+}
+#endif
diff --git a/servers/slapd/extended.c b/servers/slapd/extended.c
new file mode 100644
index 0000000..9d0067f
--- /dev/null
+++ b/servers/slapd/extended.c
@@ -0,0 +1,462 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * LDAPv3 Extended Operation Request
+ * ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
+ * requestName [0] LDAPOID,
+ * requestValue [1] OCTET STRING OPTIONAL
+ * }
+ *
+ * LDAPv3 Extended Operation Response
+ * ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
+ * COMPONENTS OF LDAPResult,
+ * responseName [10] LDAPOID OPTIONAL,
+ * response [11] OCTET STRING OPTIONAL
+ * }
+ *
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "lber_pvt.h"
+
+static struct extop_list {
+ struct extop_list *next;
+ struct berval oid;
+ slap_mask_t flags;
+ SLAP_EXTOP_MAIN_FN *ext_main;
+} *supp_ext_list = NULL;
+
+static SLAP_EXTOP_MAIN_FN whoami_extop;
+
+/* This list of built-in extops is for extops that are not part
+ * of backends or in external modules. Essentially, this is
+ * just a way to get built-in extops onto the extop list without
+ * having a separate init routine for each built-in extop.
+ */
+static struct {
+ const struct berval *oid;
+ slap_mask_t flags;
+ SLAP_EXTOP_MAIN_FN *ext_main;
+} builtin_extops[] = {
+ { &slap_EXOP_TXN_START, 0, txn_start_extop },
+ { &slap_EXOP_TXN_END, 0, txn_end_extop },
+ { &slap_EXOP_CANCEL, 0, cancel_extop },
+ { &slap_EXOP_WHOAMI, 0, whoami_extop },
+ { &slap_EXOP_MODIFY_PASSWD, SLAP_EXOP_WRITES, passwd_extop },
+ { NULL, 0, NULL }
+};
+
+
+static struct extop_list *find_extop(
+ struct extop_list *list, struct berval *oid );
+
+struct berval *
+get_supported_extop (int index)
+{
+ struct extop_list *ext;
+
+ /* linear scan is slow, but this way doesn't force a
+ * big change on root_dse.c, where this routine is used.
+ */
+ for (ext = supp_ext_list; ext != NULL && --index >= 0; ext = ext->next) {
+ ; /* empty */
+ }
+
+ if (ext == NULL) return NULL;
+
+ return &ext->oid;
+}
+
+
+int exop_root_dse_info( Entry *e )
+{
+ AttributeDescription *ad_supportedExtension
+ = slap_schema.si_ad_supportedExtension;
+ struct berval vals[2];
+ struct extop_list *ext;
+
+ vals[1].bv_val = NULL;
+ vals[1].bv_len = 0;
+
+ for (ext = supp_ext_list; ext != NULL; ext = ext->next) {
+ if( ext->flags & SLAP_EXOP_HIDE ) continue;
+
+ vals[0] = ext->oid;
+
+ if( attr_merge( e, ad_supportedExtension, vals, NULL ) ) {
+ return LDAP_OTHER;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int
+do_extended(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ struct berval reqdata = {0, NULL};
+ ber_len_t len;
+
+ Debug( LDAP_DEBUG_TRACE, "%s do_extended\n",
+ op->o_log_prefix );
+
+ if( op->o_protocol < LDAP_VERSION3 ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_extended: protocol version (%d) too low\n",
+ op->o_log_prefix, op->o_protocol );
+ send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "requires LDAPv3" );
+ rs->sr_err = SLAPD_DISCONNECT;
+ goto done;
+ }
+
+ if ( ber_scanf( op->o_ber, "{m" /*}*/, &op->ore_reqoid ) == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_extended: ber_scanf failed\n",
+ op->o_log_prefix );
+ send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
+ rs->sr_err = SLAPD_DISCONNECT;
+ goto done;
+ }
+
+ if( ber_peek_tag( op->o_ber, &len ) == LDAP_TAG_EXOP_REQ_VALUE ) {
+ if( ber_scanf( op->o_ber, "m", &reqdata ) == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_extended: ber_scanf failed\n",
+ op->o_log_prefix );
+ send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
+ rs->sr_err = SLAPD_DISCONNECT;
+ goto done;
+ }
+ }
+
+ if( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_extended: get_ctrls failed\n",
+ op->o_log_prefix );
+ return rs->sr_err;
+ }
+
+ Debug( LDAP_DEBUG_STATS, "%s EXT oid=%s\n",
+ op->o_log_prefix, op->ore_reqoid.bv_val );
+
+ /* check for controls inappropriate for all extended operations */
+ if( get_manageDSAit( op ) == SLAP_CONTROL_CRITICAL ) {
+ send_ldap_error( op, rs,
+ LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
+ "manageDSAit control inappropriate" );
+ goto done;
+ }
+
+ /* FIXME: temporary? */
+ if ( reqdata.bv_val ) {
+ op->ore_reqdata = &reqdata;
+ }
+
+ op->o_bd = frontendDB;
+ rs->sr_err = frontendDB->be_extended( op, rs );
+
+ if ( rs->sr_err == SLAPD_ASYNCOP ||
+ rs->sr_err == LDAP_TXN_SPECIFY_OKAY ) {
+ /* skip cleanup */
+ return rs->sr_err;
+ }
+
+ /* clean up in case some overlay set them? */
+ if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
+ if ( !BER_BVISNULL( &op->o_req_dn )
+ && op->o_req_ndn.bv_val != op->o_req_dn.bv_val )
+ {
+ op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx );
+ }
+ op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &op->o_req_dn );
+ BER_BVZERO( &op->o_req_ndn );
+ }
+
+done:
+ return rs->sr_err;
+}
+
+int
+fe_extended( Operation *op, SlapReply *rs )
+{
+ struct extop_list *ext = NULL;
+ struct berval reqdata = BER_BVNULL;
+
+ if (op->ore_reqdata) {
+ reqdata = *op->ore_reqdata;
+ }
+
+ ext = find_extop(supp_ext_list, &op->ore_reqoid );
+ if ( ext == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_extended: unsupported operation \"%s\"\n",
+ op->o_log_prefix, op->ore_reqoid.bv_val );
+ send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR,
+ "unsupported extended operation" );
+ goto done;
+ }
+
+ op->ore_flags = ext->flags;
+
+ Debug( LDAP_DEBUG_ARGS, "do_extended: oid=%s\n",
+ op->ore_reqoid.bv_val );
+
+ { /* start of OpenLDAP extended operation */
+ BackendDB *bd = op->o_bd;
+
+ rs->sr_err = (ext->ext_main)( op, rs );
+
+ if( rs->sr_err != SLAPD_ABANDON ) {
+ if ( rs->sr_err == LDAP_REFERRAL && rs->sr_ref == NULL ) {
+ rs->sr_ref = referral_rewrite( default_referral,
+ NULL, NULL, LDAP_SCOPE_DEFAULT );
+ if ( !rs->sr_ref ) rs->sr_ref = default_referral;
+ if ( !rs->sr_ref ) {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "referral missing";
+ }
+ }
+
+ if ( op->o_bd == NULL )
+ op->o_bd = bd;
+ send_ldap_extended( op, rs );
+
+ if ( rs->sr_ref != default_referral ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+ }
+
+ if ( rs->sr_rspoid != NULL ) {
+ free( (char *)rs->sr_rspoid );
+ rs->sr_rspoid = NULL;
+ }
+
+ if ( rs->sr_rspdata != NULL ) {
+ ber_bvfree( rs->sr_rspdata );
+ rs->sr_rspdata = NULL;
+ }
+ } /* end of OpenLDAP extended operation */
+
+done:;
+ return rs->sr_err;
+}
+
+int
+load_extop2(
+ const struct berval *ext_oid,
+ slap_mask_t ext_flags,
+ SLAP_EXTOP_MAIN_FN *ext_main,
+ unsigned flags )
+{
+ struct berval oidm = BER_BVNULL;
+ struct extop_list *ext;
+ int insertme = 0;
+
+ if ( !ext_main ) {
+ return -1;
+ }
+
+ if ( ext_oid == NULL || BER_BVISNULL( ext_oid ) ||
+ BER_BVISEMPTY( ext_oid ) )
+ {
+ return -1;
+ }
+
+ if ( numericoidValidate( NULL, (struct berval *)ext_oid ) !=
+ LDAP_SUCCESS )
+ {
+ oidm.bv_val = oidm_find( ext_oid->bv_val );
+ if ( oidm.bv_val == NULL ) {
+ return -1;
+ }
+ oidm.bv_len = strlen( oidm.bv_val );
+ ext_oid = &oidm;
+ }
+
+ for ( ext = supp_ext_list; ext; ext = ext->next ) {
+ if ( bvmatch( ext_oid, &ext->oid ) ) {
+ if ( flags == 1 ) {
+ break;
+ }
+ return -1;
+ }
+ }
+
+ if ( flags == 0 || ext == NULL ) {
+ ext = ch_calloc( 1, sizeof(struct extop_list) + ext_oid->bv_len + 1 );
+ if ( ext == NULL ) {
+ return(-1);
+ }
+
+ ext->oid.bv_val = (char *)(ext + 1);
+ AC_MEMCPY( ext->oid.bv_val, ext_oid->bv_val, ext_oid->bv_len );
+ ext->oid.bv_len = ext_oid->bv_len;
+ ext->oid.bv_val[ext->oid.bv_len] = '\0';
+
+ insertme = 1;
+ }
+
+ ext->flags = ext_flags;
+ ext->ext_main = ext_main;
+
+ if ( insertme ) {
+ ext->next = supp_ext_list;
+ supp_ext_list = ext;
+ }
+
+ return(0);
+}
+
+int
+unload_extop(
+ const struct berval *ext_oid,
+ SLAP_EXTOP_MAIN_FN *ext_main,
+ unsigned flags )
+{
+ struct berval oidm = BER_BVNULL;
+ struct extop_list *ext, **extp;
+
+ /* oid must be given */
+ if ( ext_oid == NULL || BER_BVISNULL( ext_oid ) ||
+ BER_BVISEMPTY( ext_oid ) )
+ {
+ return -1;
+ }
+
+ /* if it's not an oid, check if it's a macto */
+ if ( numericoidValidate( NULL, (struct berval *)ext_oid ) !=
+ LDAP_SUCCESS )
+ {
+ oidm.bv_val = oidm_find( ext_oid->bv_val );
+ if ( oidm.bv_val == NULL ) {
+ return -1;
+ }
+ oidm.bv_len = strlen( oidm.bv_val );
+ ext_oid = &oidm;
+ }
+
+ /* lookup the oid */
+ for ( extp = &supp_ext_list; *extp; extp = &(*extp)->next ) {
+ if ( bvmatch( ext_oid, &(*extp)->oid ) ) {
+ /* if ext_main is given, only remove if it matches */
+ if ( ext_main != NULL && (*extp)->ext_main != ext_main ) {
+ return -1;
+ }
+ break;
+ }
+ }
+
+ if ( *extp == NULL ) {
+ return -1;
+ }
+
+ ext = *extp;
+ *extp = (*extp)->next;
+
+ ch_free( ext );
+
+ return 0;
+}
+
+int
+extops_init (void)
+{
+ int i;
+
+ for ( i = 0; builtin_extops[i].oid != NULL; i++ ) {
+ load_extop( (struct berval *)builtin_extops[i].oid,
+ builtin_extops[i].flags,
+ builtin_extops[i].ext_main );
+ }
+
+ return(0);
+}
+
+int
+extops_kill (void)
+{
+ struct extop_list *ext;
+
+ /* we allocated the memory, so we have to free it, too. */
+ while ((ext = supp_ext_list) != NULL) {
+ supp_ext_list = ext->next;
+ ch_free(ext);
+ }
+ return(0);
+}
+
+static struct extop_list *
+find_extop( struct extop_list *list, struct berval *oid )
+{
+ struct extop_list *ext;
+
+ for (ext = list; ext; ext = ext->next) {
+ if (bvmatch(&ext->oid, oid))
+ return(ext);
+ }
+ return(NULL);
+}
+
+
+const struct berval slap_EXOP_WHOAMI = BER_BVC(LDAP_EXOP_WHO_AM_I);
+
+static int
+whoami_extop (
+ Operation *op,
+ SlapReply *rs )
+{
+ struct berval *bv;
+
+ if ( op->ore_reqdata != NULL ) {
+ /* no request data should be provided */
+ rs->sr_text = "no request data expected";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ Debug( LDAP_DEBUG_STATS, "%s WHOAMI\n",
+ op->o_log_prefix );
+
+ op->o_bd = op->o_conn->c_authz_backend;
+ if( backend_check_restrictions( op, rs,
+ (struct berval *)&slap_EXOP_WHOAMI ) != LDAP_SUCCESS )
+ {
+ return rs->sr_err;
+ }
+
+ bv = (struct berval *) ch_malloc( sizeof(struct berval) );
+ if( op->o_dn.bv_len ) {
+ bv->bv_len = op->o_dn.bv_len + STRLENOF( "dn:" );
+ bv->bv_val = ch_malloc( bv->bv_len + 1 );
+ AC_MEMCPY( bv->bv_val, "dn:", STRLENOF( "dn:" ) );
+ AC_MEMCPY( &bv->bv_val[STRLENOF( "dn:" )], op->o_dn.bv_val,
+ op->o_dn.bv_len );
+ bv->bv_val[bv->bv_len] = '\0';
+
+ } else {
+ bv->bv_len = 0;
+ bv->bv_val = NULL;
+ }
+
+ rs->sr_rspdata = bv;
+ return LDAP_SUCCESS;
+}
diff --git a/servers/slapd/filter.c b/servers/slapd/filter.c
new file mode 100644
index 0000000..1d7ee21
--- /dev/null
+++ b/servers/slapd/filter.c
@@ -0,0 +1,1450 @@
+/* filter.c - routines for parsing and dealing with filters */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "lutil.h"
+
+const Filter *slap_filter_objectClass_pres;
+const struct berval *slap_filterstr_objectClass_pres;
+
+static int get_filter_list(
+ Operation *op,
+ BerElement *ber,
+ Filter **f,
+ const char **text,
+ int depth );
+
+static int get_ssa(
+ Operation *op,
+ BerElement *ber,
+ Filter *f,
+ const char **text );
+
+static void simple_vrFilter2bv(
+ Operation *op,
+ ValuesReturnFilter *f,
+ struct berval *fstr );
+
+static int get_simple_vrFilter(
+ Operation *op,
+ BerElement *ber,
+ ValuesReturnFilter **f,
+ const char **text );
+
+int
+filter_init( void )
+{
+ static Filter filter_objectClass_pres = { LDAP_FILTER_PRESENT };
+ static struct berval filterstr_objectClass_pres = BER_BVC("(objectClass=*)");
+
+ filter_objectClass_pres.f_desc = slap_schema.si_ad_objectClass;
+
+ slap_filter_objectClass_pres = &filter_objectClass_pres;
+ slap_filterstr_objectClass_pres = &filterstr_objectClass_pres;
+
+ return 0;
+}
+
+void
+filter_destroy( void )
+{
+ return;
+}
+
+static int
+get_filter0(
+ Operation *op,
+ BerElement *ber,
+ Filter **filt,
+ const char **text,
+ int depth )
+{
+ ber_tag_t tag;
+ ber_len_t len;
+ int err;
+ Filter f;
+
+ Debug( LDAP_DEBUG_FILTER, "begin get_filter\n" );
+ /*
+ * A filter looks like this coming in:
+ * Filter ::= CHOICE {
+ * and [0] SET OF Filter,
+ * or [1] SET OF Filter,
+ * not [2] Filter,
+ * equalityMatch [3] AttributeValueAssertion,
+ * substrings [4] SubstringFilter,
+ * greaterOrEqual [5] AttributeValueAssertion,
+ * lessOrEqual [6] AttributeValueAssertion,
+ * present [7] AttributeType,
+ * approxMatch [8] AttributeValueAssertion,
+ * extensibleMatch [9] MatchingRuleAssertion
+ * }
+ *
+ * SubstringFilter ::= SEQUENCE {
+ * type AttributeType,
+ * SEQUENCE OF CHOICE {
+ * initial [0] IA5String,
+ * any [1] IA5String,
+ * final [2] IA5String
+ * }
+ * }
+ *
+ * MatchingRuleAssertion ::= SEQUENCE {
+ * matchingRule [1] MatchingRuleId OPTIONAL,
+ * type [2] AttributeDescription OPTIONAL,
+ * matchValue [3] AssertionValue,
+ * dnAttributes [4] BOOLEAN DEFAULT FALSE
+ * }
+ *
+ */
+
+ if( depth > slap_max_filter_depth ) {
+ *text = "filter nested too deeply";
+ return SLAPD_DISCONNECT;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+
+ if( tag == LBER_ERROR ) {
+ *text = "error decoding filter";
+ return SLAPD_DISCONNECT;
+ }
+
+ err = LDAP_SUCCESS;
+
+ f.f_next = NULL;
+ f.f_choice = tag;
+
+ switch ( f.f_choice ) {
+ case LDAP_FILTER_EQUALITY:
+ Debug( LDAP_DEBUG_FILTER, "EQUALITY\n" );
+ err = get_ava( op, ber, &f, SLAP_MR_EQUALITY, text );
+ if ( err != LDAP_SUCCESS ) {
+ break;
+ }
+
+ assert( f.f_ava != NULL );
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ Debug( LDAP_DEBUG_FILTER, "SUBSTRINGS\n" );
+ err = get_ssa( op, ber, &f, text );
+ if( err != LDAP_SUCCESS ) {
+ break;
+ }
+ assert( f.f_sub != NULL );
+ break;
+
+ case LDAP_FILTER_GE:
+ Debug( LDAP_DEBUG_FILTER, "GE\n" );
+ err = get_ava( op, ber, &f, SLAP_MR_ORDERING, text );
+ if ( err != LDAP_SUCCESS ) {
+ break;
+ }
+ assert( f.f_ava != NULL );
+ break;
+
+ case LDAP_FILTER_LE:
+ Debug( LDAP_DEBUG_FILTER, "LE\n" );
+ err = get_ava( op, ber, &f, SLAP_MR_ORDERING, text );
+ if ( err != LDAP_SUCCESS ) {
+ break;
+ }
+ assert( f.f_ava != NULL );
+ break;
+
+ case LDAP_FILTER_PRESENT: {
+ struct berval type;
+
+ Debug( LDAP_DEBUG_FILTER, "PRESENT\n" );
+ if ( ber_scanf( ber, "m", &type ) == LBER_ERROR ) {
+ err = SLAPD_DISCONNECT;
+ *text = "error decoding filter";
+ break;
+ }
+
+ f.f_desc = NULL;
+ err = slap_bv2ad( &type, &f.f_desc, text );
+
+ if( err != LDAP_SUCCESS ) {
+ f.f_choice |= SLAPD_FILTER_UNDEFINED;
+ err = slap_bv2undef_ad( &type, &f.f_desc, text,
+ SLAP_AD_PROXIED|SLAP_AD_NOINSERT );
+
+ if ( err != LDAP_SUCCESS ) {
+ /* unrecognized attribute description or other error */
+ Debug( LDAP_DEBUG_ANY,
+ "get_filter: conn %lu unknown attribute "
+ "type=%s (%d)\n",
+ op->o_connid, type.bv_val, err );
+
+ err = LDAP_SUCCESS;
+ f.f_desc = slap_bv2tmp_ad( &type, op->o_tmpmemctx );
+ }
+ *text = NULL;
+ }
+
+ assert( f.f_desc != NULL );
+ } break;
+
+ case LDAP_FILTER_APPROX:
+ Debug( LDAP_DEBUG_FILTER, "APPROX\n" );
+ err = get_ava( op, ber, &f, SLAP_MR_EQUALITY_APPROX, text );
+ if ( err != LDAP_SUCCESS ) {
+ break;
+ }
+ assert( f.f_ava != NULL );
+ break;
+
+ case LDAP_FILTER_AND:
+ Debug( LDAP_DEBUG_FILTER, "AND\n" );
+ err = get_filter_list( op, ber, &f.f_and, text, depth+1 );
+ if ( err != LDAP_SUCCESS ) {
+ break;
+ }
+ if ( f.f_and == NULL ) {
+ f.f_choice = SLAPD_FILTER_COMPUTED;
+ f.f_result = LDAP_COMPARE_TRUE;
+ }
+ /* no assert - list could be empty */
+ break;
+
+ case LDAP_FILTER_OR:
+ Debug( LDAP_DEBUG_FILTER, "OR\n" );
+ err = get_filter_list( op, ber, &f.f_or, text, depth+1 );
+ if ( err != LDAP_SUCCESS ) {
+ break;
+ }
+ if ( f.f_or == NULL ) {
+ f.f_choice = SLAPD_FILTER_COMPUTED;
+ f.f_result = LDAP_COMPARE_FALSE;
+ }
+ /* no assert - list could be empty */
+ break;
+
+ case LDAP_FILTER_NOT:
+ Debug( LDAP_DEBUG_FILTER, "NOT\n" );
+ (void) ber_skip_tag( ber, &len );
+ err = get_filter0( op, ber, &f.f_not, text, depth+1 );
+ if ( err != LDAP_SUCCESS ) {
+ break;
+ }
+
+ assert( f.f_not != NULL );
+ if ( f.f_not->f_choice == SLAPD_FILTER_COMPUTED ) {
+ int fresult = f.f_not->f_result;
+ f.f_choice = SLAPD_FILTER_COMPUTED;
+ op->o_tmpfree( f.f_not, op->o_tmpmemctx );
+ f.f_not = NULL;
+
+ switch( fresult ) {
+ case LDAP_COMPARE_TRUE:
+ f.f_result = LDAP_COMPARE_FALSE;
+ break;
+ case LDAP_COMPARE_FALSE:
+ f.f_result = LDAP_COMPARE_TRUE;
+ break;
+ default: ;
+ /* (!Undefined) is Undefined */
+ }
+ }
+ break;
+
+ case LDAP_FILTER_EXT:
+ Debug( LDAP_DEBUG_FILTER, "EXTENSIBLE\n" );
+
+ err = get_mra( op, ber, &f, text );
+ if ( err != LDAP_SUCCESS ) {
+ break;
+ }
+
+ assert( f.f_mra != NULL );
+ break;
+
+ default:
+ (void) ber_scanf( ber, "x" ); /* skip the element */
+ Debug( LDAP_DEBUG_ANY, "get_filter: unknown filter type=%lu\n",
+ f.f_choice );
+ f.f_choice = SLAPD_FILTER_COMPUTED;
+ f.f_result = SLAPD_COMPARE_UNDEFINED;
+ break;
+ }
+
+ if( err != LDAP_SUCCESS && err != SLAPD_DISCONNECT ) {
+ /* ignore error */
+ *text = NULL;
+ f.f_choice = SLAPD_FILTER_COMPUTED;
+ f.f_result = SLAPD_COMPARE_UNDEFINED;
+ err = LDAP_SUCCESS;
+ }
+
+ if ( err == LDAP_SUCCESS ) {
+ *filt = op->o_tmpalloc( sizeof(f), op->o_tmpmemctx );
+ **filt = f;
+ }
+
+ Debug( LDAP_DEBUG_FILTER, "end get_filter %d\n", err );
+
+ return( err );
+}
+
+int
+get_filter(
+ Operation *op,
+ BerElement *ber,
+ Filter **filt,
+ const char **text )
+{
+ return get_filter0( op, ber, filt, text, 0 );
+}
+
+
+static int
+get_filter_list( Operation *op, BerElement *ber,
+ Filter **f,
+ const char **text,
+ int depth )
+{
+ Filter **new;
+ int err;
+ ber_tag_t tag;
+ ber_len_t len;
+ char *last;
+
+ Debug( LDAP_DEBUG_FILTER, "begin get_filter_list\n" );
+ new = f;
+ for ( tag = ber_first_element( ber, &len, &last );
+ tag != LBER_DEFAULT;
+ tag = ber_next_element( ber, &len, last ) )
+ {
+ err = get_filter0( op, ber, new, text, depth );
+ if ( err != LDAP_SUCCESS )
+ return( err );
+ new = &(*new)->f_next;
+ }
+ *new = NULL;
+
+ Debug( LDAP_DEBUG_FILTER, "end get_filter_list\n" );
+ return( LDAP_SUCCESS );
+}
+
+static int
+get_ssa(
+ Operation *op,
+ BerElement *ber,
+ Filter *f,
+ const char **text )
+{
+ ber_tag_t tag;
+ ber_len_t len;
+ int rc;
+ struct berval desc, value, nvalue;
+ char *last;
+ SubstringsAssertion ssa;
+
+ *text = "error decoding filter";
+
+ Debug( LDAP_DEBUG_FILTER, "begin get_ssa\n" );
+ if ( ber_scanf( ber, "{m" /*}*/, &desc ) == LBER_ERROR ) {
+ return SLAPD_DISCONNECT;
+ }
+
+ *text = NULL;
+
+ ssa.sa_desc = NULL;
+ ssa.sa_initial.bv_val = NULL;
+ ssa.sa_any = NULL;
+ ssa.sa_final.bv_val = NULL;
+
+ rc = slap_bv2ad( &desc, &ssa.sa_desc, text );
+
+ if( rc != LDAP_SUCCESS ) {
+ f->f_choice |= SLAPD_FILTER_UNDEFINED;
+ rc = slap_bv2undef_ad( &desc, &ssa.sa_desc, text,
+ SLAP_AD_PROXIED|SLAP_AD_NOINSERT );
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "get_ssa: conn %lu unknown attribute type=%s (%ld)\n",
+ op->o_connid, desc.bv_val, (long) rc );
+
+ ssa.sa_desc = slap_bv2tmp_ad( &desc, op->o_tmpmemctx );
+ }
+ }
+
+ rc = LDAP_PROTOCOL_ERROR;
+
+ /* If there is no substring matching rule, there's nothing
+ * we can do with this filter. But we continue to parse it
+ * for logging purposes.
+ */
+ if ( ssa.sa_desc->ad_type->sat_substr == NULL ) {
+ f->f_choice |= SLAPD_FILTER_UNDEFINED;
+ Debug( LDAP_DEBUG_FILTER,
+ "get_ssa: no substring matching rule for attributeType %s\n",
+ desc.bv_val );
+ }
+
+ for ( tag = ber_first_element( ber, &len, &last );
+ tag != LBER_DEFAULT;
+ tag = ber_next_element( ber, &len, last ) )
+ {
+ unsigned usage;
+
+ if ( ber_scanf( ber, "m", &value ) == LBER_ERROR ) {
+ rc = SLAPD_DISCONNECT;
+ goto return_error;
+ }
+
+ if ( value.bv_val == NULL || value.bv_len == 0 ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto return_error;
+ }
+
+ switch ( tag ) {
+ case LDAP_SUBSTRING_INITIAL:
+ if ( ssa.sa_initial.bv_val != NULL
+ || ssa.sa_any != NULL
+ || ssa.sa_final.bv_val != NULL )
+ {
+ rc = LDAP_PROTOCOL_ERROR;
+ goto return_error;
+ }
+ usage = SLAP_MR_SUBSTR_INITIAL;
+ break;
+
+ case LDAP_SUBSTRING_ANY:
+ if ( ssa.sa_final.bv_val != NULL ) {
+ rc = LDAP_PROTOCOL_ERROR;
+ goto return_error;
+ }
+ usage = SLAP_MR_SUBSTR_ANY;
+ break;
+
+ case LDAP_SUBSTRING_FINAL:
+ if ( ssa.sa_final.bv_val != NULL ) {
+ rc = LDAP_PROTOCOL_ERROR;
+ goto return_error;
+ }
+
+ usage = SLAP_MR_SUBSTR_FINAL;
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_FILTER,
+ " unknown substring choice=%ld\n",
+ (long) tag );
+
+ rc = LDAP_PROTOCOL_ERROR;
+ goto return_error;
+ }
+
+ /* validate/normalize using equality matching rule validator! */
+ rc = asserted_value_validate_normalize(
+ ssa.sa_desc, ssa.sa_desc->ad_type->sat_equality,
+ usage, &value, &nvalue, text, op->o_tmpmemctx );
+ if( rc != LDAP_SUCCESS ) {
+ f->f_choice |= SLAPD_FILTER_UNDEFINED;
+ Debug( LDAP_DEBUG_FILTER,
+ "get_ssa: illegal value for attributeType %s (%d) %s\n",
+ desc.bv_val, rc, *text );
+ ber_dupbv_x( &nvalue, &value, op->o_tmpmemctx );
+ }
+
+ switch ( tag ) {
+ case LDAP_SUBSTRING_INITIAL:
+ Debug( LDAP_DEBUG_FILTER, " INITIAL\n" );
+ ssa.sa_initial = nvalue;
+ break;
+
+ case LDAP_SUBSTRING_ANY:
+ Debug( LDAP_DEBUG_FILTER, " ANY\n" );
+ ber_bvarray_add_x( &ssa.sa_any, &nvalue, op->o_tmpmemctx );
+ break;
+
+ case LDAP_SUBSTRING_FINAL:
+ Debug( LDAP_DEBUG_FILTER, " FINAL\n" );
+ ssa.sa_final = nvalue;
+ break;
+
+ default:
+ assert( 0 );
+ slap_sl_free( nvalue.bv_val, op->o_tmpmemctx );
+ rc = LDAP_PROTOCOL_ERROR;
+
+return_error:
+ Debug( LDAP_DEBUG_FILTER, " error=%ld\n",
+ (long) rc );
+ slap_sl_free( ssa.sa_initial.bv_val, op->o_tmpmemctx );
+ ber_bvarray_free_x( ssa.sa_any, op->o_tmpmemctx );
+ if ( ssa.sa_desc->ad_flags & SLAP_DESC_TEMPORARY )
+ op->o_tmpfree( ssa.sa_desc, op->o_tmpmemctx );
+ slap_sl_free( ssa.sa_final.bv_val, op->o_tmpmemctx );
+ return rc;
+ }
+
+ *text = NULL;
+ rc = LDAP_SUCCESS;
+ }
+
+ if( rc == LDAP_SUCCESS ) {
+ f->f_sub = op->o_tmpalloc( sizeof( ssa ), op->o_tmpmemctx );
+ *f->f_sub = ssa;
+ }
+
+ Debug( LDAP_DEBUG_FILTER, "end get_ssa\n" );
+ return rc /* LDAP_SUCCESS */ ;
+}
+
+void
+filter_free_x( Operation *op, Filter *f, int freeme )
+{
+ Filter *p, *next;
+
+ if ( f == NULL ) {
+ return;
+ }
+
+ f->f_choice &= SLAPD_FILTER_MASK;
+
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_PRESENT:
+ if ( f->f_desc->ad_flags & SLAP_DESC_TEMPORARY )
+ op->o_tmpfree( f->f_desc, op->o_tmpmemctx );
+ break;
+
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ ava_free( op, f->f_ava, 1 );
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ if ( f->f_sub_initial.bv_val != NULL ) {
+ op->o_tmpfree( f->f_sub_initial.bv_val, op->o_tmpmemctx );
+ }
+ ber_bvarray_free_x( f->f_sub_any, op->o_tmpmemctx );
+ if ( f->f_sub_final.bv_val != NULL ) {
+ op->o_tmpfree( f->f_sub_final.bv_val, op->o_tmpmemctx );
+ }
+ if ( f->f_sub->sa_desc->ad_flags & SLAP_DESC_TEMPORARY )
+ op->o_tmpfree( f->f_sub->sa_desc, op->o_tmpmemctx );
+ op->o_tmpfree( f->f_sub, op->o_tmpmemctx );
+ break;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ for ( p = f->f_list; p != NULL; p = next ) {
+ next = p->f_next;
+ filter_free_x( op, p, 1 );
+ }
+ break;
+
+ case LDAP_FILTER_EXT:
+ mra_free( op, f->f_mra, 1 );
+ break;
+
+ case SLAPD_FILTER_COMPUTED:
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY, "filter_free: unknown filter type=%lu\n",
+ f->f_choice );
+ break;
+ }
+
+ if ( freeme ) {
+ op->o_tmpfree( f, op->o_tmpmemctx );
+ }
+}
+
+void
+filter_free( Filter *f )
+{
+ Operation op;
+ Opheader ohdr;
+
+ op.o_hdr = &ohdr;
+ op.o_tmpmemctx = slap_sl_context( f );
+ op.o_tmpmfuncs = &slap_sl_mfuncs;
+ filter_free_x( &op, f, 1 );
+}
+
+void
+filter2bv_x( Operation *op, Filter *f, struct berval *fstr )
+{
+ filter2bv_undef_x( op, f, 0, fstr );
+}
+
+void
+filter2bv_undef_x( Operation *op, Filter *f, int noundef, struct berval *fstr )
+{
+ int i;
+ Filter *p;
+ struct berval tmp, value;
+ static struct berval
+ ber_bvfalse = BER_BVC( "(?=false)" ),
+ ber_bvtrue = BER_BVC( "(?=true)" ),
+ ber_bvundefined = BER_BVC( "(?=undefined)" ),
+ ber_bverror = BER_BVC( "(?=error)" ),
+ ber_bvunknown = BER_BVC( "(?=unknown)" ),
+ ber_bvnone = BER_BVC( "(?=none)" ),
+ ber_bvF = BER_BVC( "(|)" ),
+ ber_bvT = BER_BVC( "(&)" );
+ ber_len_t len;
+ ber_tag_t choice;
+ int undef, undef2;
+ char *sign;
+
+ if ( f == NULL ) {
+ ber_dupbv_x( fstr, &ber_bvnone, op->o_tmpmemctx );
+ return;
+ }
+
+ undef = f->f_choice & SLAPD_FILTER_UNDEFINED;
+ undef2 = (undef && !noundef);
+ choice = f->f_choice & SLAPD_FILTER_MASK;
+
+ switch ( choice ) {
+ case LDAP_FILTER_EQUALITY:
+ fstr->bv_len = STRLENOF("(=)");
+ sign = "=";
+ goto simple;
+ case LDAP_FILTER_GE:
+ fstr->bv_len = STRLENOF("(>=)");
+ sign = ">=";
+ goto simple;
+ case LDAP_FILTER_LE:
+ fstr->bv_len = STRLENOF("(<=)");
+ sign = "<=";
+ goto simple;
+ case LDAP_FILTER_APPROX:
+ fstr->bv_len = STRLENOF("(~=)");
+ sign = "~=";
+
+simple:
+ value = f->f_av_value;
+ if ( f->f_av_desc->ad_type->sat_equality &&
+ !undef &&
+ ( f->f_av_desc->ad_type->sat_equality->smr_usage & SLAP_MR_MUTATION_NORMALIZER ))
+ {
+ f->f_av_desc->ad_type->sat_equality->smr_normalize(
+ (SLAP_MR_DENORMALIZE|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX),
+ NULL, NULL, &f->f_av_value, &value, op->o_tmpmemctx );
+ }
+
+ filter_escape_value_x( &value, &tmp, op->o_tmpmemctx );
+ /* NOTE: tmp can legitimately be NULL (meaning empty)
+ * since in a Filter values in AVAs are supposed
+ * to have been normalized, meaning that an empty value
+ * is legal for that attribute's syntax */
+
+ fstr->bv_len += f->f_av_desc->ad_cname.bv_len + tmp.bv_len;
+ if ( undef2 )
+ fstr->bv_len++;
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s%s%s)",
+ undef2 ? "?" : "",
+ f->f_av_desc->ad_cname.bv_val, sign,
+ tmp.bv_len ? tmp.bv_val : "" );
+
+ if ( value.bv_val != f->f_av_value.bv_val ) {
+ ber_memfree_x( value.bv_val, op->o_tmpmemctx );
+ }
+
+ ber_memfree_x( tmp.bv_val, op->o_tmpmemctx );
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ fstr->bv_len = f->f_sub_desc->ad_cname.bv_len +
+ STRLENOF("(=*)");
+ if ( undef2 )
+ fstr->bv_len++;
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 128, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s=*)",
+ undef2 ? "?" : "",
+ f->f_sub_desc->ad_cname.bv_val );
+
+ if ( f->f_sub_initial.bv_val != NULL ) {
+ ber_len_t tmplen;
+
+ len = fstr->bv_len;
+
+ filter_escape_value_x( &f->f_sub_initial, &tmp, op->o_tmpmemctx );
+ tmplen = tmp.bv_len;
+
+ fstr->bv_len += tmplen;
+ fstr->bv_val = op->o_tmprealloc( fstr->bv_val,
+ fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( &fstr->bv_val[len - 2],
+ tmplen + STRLENOF( /*(*/ "*)" ) + 1,
+ /* "(attr=" */ "%s*)",
+ tmp.bv_len ? tmp.bv_val : "");
+
+ ber_memfree_x( tmp.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( f->f_sub_any != NULL ) {
+ for ( i = 0; f->f_sub_any[i].bv_val != NULL; i++ ) {
+ ber_len_t tmplen;
+
+ len = fstr->bv_len;
+ filter_escape_value_x( &f->f_sub_any[i],
+ &tmp, op->o_tmpmemctx );
+ tmplen = tmp.bv_len;
+
+ fstr->bv_len += tmplen + STRLENOF( /*(*/ ")" );
+ fstr->bv_val = op->o_tmprealloc( fstr->bv_val,
+ fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( &fstr->bv_val[len - 1],
+ tmplen + STRLENOF( /*(*/ "*)" ) + 1,
+ /* "(attr=[init]*[any*]" */ "%s*)",
+ tmp.bv_len ? tmp.bv_val : "");
+ ber_memfree_x( tmp.bv_val, op->o_tmpmemctx );
+ }
+ }
+
+ if ( f->f_sub_final.bv_val != NULL ) {
+ ber_len_t tmplen;
+
+ len = fstr->bv_len;
+
+ filter_escape_value_x( &f->f_sub_final, &tmp, op->o_tmpmemctx );
+ tmplen = tmp.bv_len;
+
+ fstr->bv_len += tmplen;
+ fstr->bv_val = op->o_tmprealloc( fstr->bv_val,
+ fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( &fstr->bv_val[len - 1],
+ tmplen + STRLENOF( /*(*/ ")" ) + 1,
+ /* "(attr=[init*][any*]" */ "%s)",
+ tmp.bv_len ? tmp.bv_val : "");
+
+ ber_memfree_x( tmp.bv_val, op->o_tmpmemctx );
+ }
+
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ fstr->bv_len = f->f_desc->ad_cname.bv_len +
+ STRLENOF("(=*)");
+ if ( undef2 )
+ fstr->bv_len++;
+
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s=*)",
+ undef2 ? "?" : "",
+ f->f_desc->ad_cname.bv_val );
+ break;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ fstr->bv_len = STRLENOF("(%)");
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 128, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%c)",
+ f->f_choice == LDAP_FILTER_AND ? '&' :
+ f->f_choice == LDAP_FILTER_OR ? '|' : '!' );
+
+ for ( p = f->f_list; p != NULL; p = p->f_next ) {
+ len = fstr->bv_len;
+
+ filter2bv_undef_x( op, p, noundef, &tmp );
+
+ fstr->bv_len += tmp.bv_len;
+ fstr->bv_val = op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1,
+ op->o_tmpmemctx );
+
+ snprintf( &fstr->bv_val[len-1],
+ tmp.bv_len + STRLENOF( /*(*/ ")" ) + 1,
+ /*"("*/ "%s)", tmp.bv_val );
+
+ op->o_tmpfree( tmp.bv_val, op->o_tmpmemctx );
+ }
+
+ break;
+
+ case LDAP_FILTER_EXT: {
+ struct berval ad;
+
+ filter_escape_value_x( &f->f_mr_value, &tmp, op->o_tmpmemctx );
+ /* NOTE: tmp can legitimately be NULL (meaning empty)
+ * since in a Filter values in MRAs are supposed
+ * to have been normalized, meaning that an empty value
+ * is legal for that attribute's syntax */
+
+ if ( f->f_mr_desc ) {
+ ad = f->f_mr_desc->ad_cname;
+ } else {
+ ad.bv_len = 0;
+ ad.bv_val = "";
+ }
+
+ fstr->bv_len = ad.bv_len +
+ ( undef2 ? 1 : 0 ) +
+ ( f->f_mr_dnattrs ? STRLENOF(":dn") : 0 ) +
+ ( f->f_mr_rule_text.bv_len ? f->f_mr_rule_text.bv_len + STRLENOF(":") : 0 ) +
+ tmp.bv_len + STRLENOF("(:=)");
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s%s%s%s:=%s)",
+ undef2 ? "?" : "",
+ ad.bv_val,
+ f->f_mr_dnattrs ? ":dn" : "",
+ f->f_mr_rule_text.bv_len ? ":" : "",
+ f->f_mr_rule_text.bv_len ? f->f_mr_rule_text.bv_val : "",
+ tmp.bv_len ? tmp.bv_val : "" );
+ ber_memfree_x( tmp.bv_val, op->o_tmpmemctx );
+ } break;
+
+ case SLAPD_FILTER_COMPUTED:
+ switch ( f->f_result ) {
+ case LDAP_COMPARE_FALSE:
+ tmp = ( noundef ? ber_bvF : ber_bvfalse );
+ break;
+
+ case LDAP_COMPARE_TRUE:
+ tmp = ( noundef ? ber_bvT : ber_bvtrue );
+ break;
+
+ case SLAPD_COMPARE_UNDEFINED:
+ tmp = ber_bvundefined;
+ break;
+
+ default:
+ tmp = ber_bverror;
+ break;
+ }
+
+ ber_dupbv_x( fstr, &tmp, op->o_tmpmemctx );
+ break;
+
+ default:
+ ber_dupbv_x( fstr, &ber_bvunknown, op->o_tmpmemctx );
+ break;
+ }
+}
+
+void
+filter2bv( Filter *f, struct berval *fstr )
+{
+ filter2bv_undef( f, 0, fstr );
+}
+
+void
+filter2bv_undef( Filter *f, int noundef, struct berval *fstr )
+{
+ Operation op;
+ Opheader ohdr;
+
+ op.o_hdr = &ohdr;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ filter2bv_undef_x( &op, f, noundef, fstr );
+}
+
+Filter *
+filter_dup( Filter *f, void *memctx )
+{
+ BerMemoryFunctions *mf = &slap_sl_mfuncs;
+ Filter *n;
+
+ if ( !f )
+ return NULL;
+
+ n = mf->bmf_malloc( sizeof(Filter), memctx );
+ n->f_choice = f->f_choice;
+ n->f_next = NULL;
+
+ switch( f->f_choice & SLAPD_FILTER_MASK ) {
+ case SLAPD_FILTER_COMPUTED:
+ n->f_result = f->f_result;
+ break;
+ case LDAP_FILTER_PRESENT:
+ if ( f->f_desc->ad_flags & SLAP_DESC_TEMPORARY )
+ n->f_desc = slap_bv2tmp_ad( &f->f_desc->ad_cname, memctx );
+ else
+ n->f_desc = f->f_desc;
+ break;
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ /* Should this be ava_dup() ? */
+ n->f_ava = mf->bmf_calloc( 1, sizeof(AttributeAssertion), memctx );
+ *n->f_ava = *f->f_ava;
+ if ( f->f_av_desc->ad_flags & SLAP_DESC_TEMPORARY )
+ n->f_av_desc = slap_bv2tmp_ad( &f->f_av_desc->ad_cname, memctx );
+ ber_dupbv_x( &n->f_av_value, &f->f_av_value, memctx );
+ break;
+ case LDAP_FILTER_SUBSTRINGS:
+ n->f_sub = mf->bmf_calloc( 1, sizeof(SubstringsAssertion), memctx );
+ if ( f->f_sub_desc->ad_flags & SLAP_DESC_TEMPORARY )
+ n->f_sub_desc = slap_bv2tmp_ad( &f->f_sub_desc->ad_cname, memctx );
+ else
+ n->f_sub_desc = f->f_sub_desc;
+ if ( !BER_BVISNULL( &f->f_sub_initial ))
+ ber_dupbv_x( &n->f_sub_initial, &f->f_sub_initial, memctx );
+ if ( f->f_sub_any ) {
+ int i;
+ for ( i = 0; !BER_BVISNULL( &f->f_sub_any[i] ); i++ );
+ n->f_sub_any = mf->bmf_malloc(( i+1 )*sizeof( struct berval ),
+ memctx );
+ for ( i = 0; !BER_BVISNULL( &f->f_sub_any[i] ); i++ ) {
+ ber_dupbv_x( &n->f_sub_any[i], &f->f_sub_any[i], memctx );
+ }
+ BER_BVZERO( &n->f_sub_any[i] );
+ }
+ if ( !BER_BVISNULL( &f->f_sub_final ))
+ ber_dupbv_x( &n->f_sub_final, &f->f_sub_final, memctx );
+ break;
+ case LDAP_FILTER_EXT: {
+ /* Should this be mra_dup() ? */
+ ber_len_t length;
+ length = sizeof(MatchingRuleAssertion);
+ if ( !BER_BVISNULL( &f->f_mr_rule_text ))
+ length += f->f_mr_rule_text.bv_len + 1;
+ n->f_mra = mf->bmf_calloc( 1, length, memctx );
+ *n->f_mra = *f->f_mra;
+ if ( f->f_mr_desc && ( f->f_sub_desc->ad_flags & SLAP_DESC_TEMPORARY ))
+ n->f_mr_desc = slap_bv2tmp_ad( &f->f_mr_desc->ad_cname, memctx );
+ ber_dupbv_x( &n->f_mr_value, &f->f_mr_value, memctx );
+ if ( !BER_BVISNULL( &f->f_mr_rule_text )) {
+ n->f_mr_rule_text.bv_val = (char *)(n->f_mra+1);
+ AC_MEMCPY(n->f_mr_rule_text.bv_val,
+ f->f_mr_rule_text.bv_val, f->f_mr_rule_text.bv_len );
+ }
+ } break;
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT: {
+ Filter **p;
+ for ( p = &n->f_list, f = f->f_list; f; f = f->f_next ) {
+ *p = filter_dup( f, memctx );
+ p = &(*p)->f_next;
+ }
+ } break;
+ }
+ return n;
+}
+
+static int
+get_simple_vrFilter(
+ Operation *op,
+ BerElement *ber,
+ ValuesReturnFilter **filt,
+ const char **text )
+{
+ ber_tag_t tag;
+ ber_len_t len;
+ int err;
+ ValuesReturnFilter vrf;
+
+ Debug( LDAP_DEBUG_FILTER, "begin get_simple_vrFilter\n" );
+
+ tag = ber_peek_tag( ber, &len );
+
+ if( tag == LBER_ERROR ) {
+ *text = "error decoding filter";
+ return SLAPD_DISCONNECT;
+ }
+
+ vrf.vrf_next = NULL;
+
+ err = LDAP_SUCCESS;
+ vrf.vrf_choice = tag;
+
+ switch ( vrf.vrf_choice ) {
+ case LDAP_FILTER_EQUALITY:
+ Debug( LDAP_DEBUG_FILTER, "EQUALITY\n" );
+ err = get_ava( op, ber, (Filter *)&vrf, SLAP_MR_EQUALITY, text );
+ if ( err != LDAP_SUCCESS ) {
+ break;
+ }
+
+ assert( vrf.vrf_ava != NULL );
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ Debug( LDAP_DEBUG_FILTER, "SUBSTRINGS\n" );
+ err = get_ssa( op, ber, (Filter *)&vrf, text );
+ break;
+
+ case LDAP_FILTER_GE:
+ Debug( LDAP_DEBUG_FILTER, "GE\n" );
+ err = get_ava( op, ber, (Filter *)&vrf, SLAP_MR_ORDERING, text );
+ if ( err != LDAP_SUCCESS ) {
+ break;
+ }
+ break;
+
+ case LDAP_FILTER_LE:
+ Debug( LDAP_DEBUG_FILTER, "LE\n" );
+ err = get_ava( op, ber, (Filter *)&vrf, SLAP_MR_ORDERING, text );
+ if ( err != LDAP_SUCCESS ) {
+ break;
+ }
+ break;
+
+ case LDAP_FILTER_PRESENT: {
+ struct berval type;
+
+ Debug( LDAP_DEBUG_FILTER, "PRESENT\n" );
+ if ( ber_scanf( ber, "m", &type ) == LBER_ERROR ) {
+ err = SLAPD_DISCONNECT;
+ *text = "error decoding filter";
+ break;
+ }
+
+ vrf.vrf_desc = NULL;
+ err = slap_bv2ad( &type, &vrf.vrf_desc, text );
+
+ if( err != LDAP_SUCCESS ) {
+ vrf.vrf_choice |= SLAPD_FILTER_UNDEFINED;
+ err = slap_bv2undef_ad( &type, &vrf.vrf_desc, text,
+ SLAP_AD_PROXIED);
+
+ if( err != LDAP_SUCCESS ) {
+ /* unrecognized attribute description or other error */
+ Debug( LDAP_DEBUG_ANY,
+ "get_simple_vrFilter: conn %lu unknown "
+ "attribute type=%s (%d)\n",
+ op->o_connid, type.bv_val, err );
+
+ vrf.vrf_choice = SLAPD_FILTER_COMPUTED;
+ vrf.vrf_result = LDAP_COMPARE_FALSE;
+ err = LDAP_SUCCESS;
+ break;
+ }
+ }
+ } break;
+
+ case LDAP_FILTER_APPROX:
+ Debug( LDAP_DEBUG_FILTER, "APPROX\n" );
+ err = get_ava( op, ber, (Filter *)&vrf, SLAP_MR_EQUALITY_APPROX, text );
+ if ( err != LDAP_SUCCESS ) {
+ break;
+ }
+ break;
+
+ case LDAP_FILTER_EXT:
+ Debug( LDAP_DEBUG_FILTER, "EXTENSIBLE\n" );
+
+ err = get_mra( op, ber, (Filter *)&vrf, text );
+ if ( err != LDAP_SUCCESS ) {
+ break;
+ }
+
+ assert( vrf.vrf_mra != NULL );
+ break;
+
+ default:
+ (void) ber_scanf( ber, "x" ); /* skip the element */
+ Debug( LDAP_DEBUG_ANY, "get_simple_vrFilter: unknown filter type=%lu\n",
+ vrf.vrf_choice );
+ vrf.vrf_choice = SLAPD_FILTER_COMPUTED;
+ vrf.vrf_result = SLAPD_COMPARE_UNDEFINED;
+ break;
+ }
+
+ if ( err != LDAP_SUCCESS && err != SLAPD_DISCONNECT ) {
+ /* ignore error */
+ vrf.vrf_choice = SLAPD_FILTER_COMPUTED;
+ vrf.vrf_result = SLAPD_COMPARE_UNDEFINED;
+ err = LDAP_SUCCESS;
+ }
+
+ if ( err == LDAP_SUCCESS ) {
+ *filt = op->o_tmpalloc( sizeof vrf, op->o_tmpmemctx );
+ **filt = vrf;
+ }
+
+ Debug( LDAP_DEBUG_FILTER, "end get_simple_vrFilter %d\n", err );
+
+ return err;
+}
+
+int
+get_vrFilter( Operation *op, BerElement *ber,
+ ValuesReturnFilter **vrf,
+ const char **text )
+{
+ /*
+ * A ValuesReturnFilter looks like this:
+ *
+ * ValuesReturnFilter ::= SEQUENCE OF SimpleFilterItem
+ * SimpleFilterItem ::= CHOICE {
+ * equalityMatch [3] AttributeValueAssertion,
+ * substrings [4] SubstringFilter,
+ * greaterOrEqual [5] AttributeValueAssertion,
+ * lessOrEqual [6] AttributeValueAssertion,
+ * present [7] AttributeType,
+ * approxMatch [8] AttributeValueAssertion,
+ * extensibleMatch [9] SimpleMatchingAssertion -- LDAPv3
+ * }
+ *
+ * SubstringFilter ::= SEQUENCE {
+ * type AttributeType,
+ * SEQUENCE OF CHOICE {
+ * initial [0] IA5String,
+ * any [1] IA5String,
+ * final [2] IA5String
+ * }
+ * }
+ *
+ * SimpleMatchingAssertion ::= SEQUENCE { -- LDAPv3
+ * matchingRule [1] MatchingRuleId OPTIONAL,
+ * type [2] AttributeDescription OPTIONAL,
+ * matchValue [3] AssertionValue }
+ */
+
+ ValuesReturnFilter **n;
+ ber_tag_t tag;
+ ber_len_t len;
+ char *last;
+
+ Debug( LDAP_DEBUG_FILTER, "begin get_vrFilter\n" );
+
+ tag = ber_peek_tag( ber, &len );
+
+ if( tag == LBER_ERROR ) {
+ *text = "error decoding vrFilter";
+ return SLAPD_DISCONNECT;
+ }
+
+ if( tag != LBER_SEQUENCE ) {
+ *text = "error decoding vrFilter, expect SEQUENCE tag";
+ return SLAPD_DISCONNECT;
+ }
+
+ n = vrf;
+ for ( tag = ber_first_element( ber, &len, &last );
+ tag != LBER_DEFAULT;
+ tag = ber_next_element( ber, &len, last ) )
+ {
+ int err = get_simple_vrFilter( op, ber, n, text );
+
+ if ( err != LDAP_SUCCESS ) return( err );
+
+ n = &(*n)->vrf_next;
+ }
+ *n = NULL;
+
+ Debug( LDAP_DEBUG_FILTER, "end get_vrFilter\n" );
+ return( LDAP_SUCCESS );
+}
+
+void
+vrFilter_free( Operation *op, ValuesReturnFilter *vrf )
+{
+ ValuesReturnFilter *next;
+
+ for ( ; vrf != NULL; vrf = next ) {
+ next = vrf->vrf_next;
+
+ switch ( vrf->vrf_choice & SLAPD_FILTER_MASK ) {
+ case LDAP_FILTER_PRESENT:
+ break;
+
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ ava_free( op, vrf->vrf_ava, 1 );
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ if ( vrf->vrf_sub_initial.bv_val != NULL ) {
+ op->o_tmpfree( vrf->vrf_sub_initial.bv_val, op->o_tmpmemctx );
+ }
+ ber_bvarray_free_x( vrf->vrf_sub_any, op->o_tmpmemctx );
+ if ( vrf->vrf_sub_final.bv_val != NULL ) {
+ op->o_tmpfree( vrf->vrf_sub_final.bv_val, op->o_tmpmemctx );
+ }
+ op->o_tmpfree( vrf->vrf_sub, op->o_tmpmemctx );
+ break;
+
+ case LDAP_FILTER_EXT:
+ mra_free( op, vrf->vrf_mra, 1 );
+ break;
+
+ case SLAPD_FILTER_COMPUTED:
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY, "filter_free: unknown filter type=%lu\n",
+ vrf->vrf_choice );
+ break;
+ }
+
+ op->o_tmpfree( vrf, op->o_tmpmemctx );
+ }
+}
+
+void
+vrFilter2bv( Operation *op, ValuesReturnFilter *vrf, struct berval *fstr )
+{
+ ValuesReturnFilter *p;
+ struct berval tmp;
+ ber_len_t len;
+
+ if ( vrf == NULL ) {
+ ber_str2bv_x( "No filter!", STRLENOF("No filter!"),
+ 1, fstr, op->o_tmpmemctx );
+ return;
+ }
+
+ fstr->bv_len = STRLENOF("()");
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 128, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "()");
+
+ for ( p = vrf; p != NULL; p = p->vrf_next ) {
+ len = fstr->bv_len;
+
+ simple_vrFilter2bv( op, p, &tmp );
+
+ fstr->bv_len += tmp.bv_len;
+ fstr->bv_val = op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1,
+ op->o_tmpmemctx );
+
+ snprintf( &fstr->bv_val[len-1], tmp.bv_len + 2,
+ /*"("*/ "%s)", tmp.bv_val );
+
+ op->o_tmpfree( tmp.bv_val, op->o_tmpmemctx );
+ }
+}
+
+static void
+simple_vrFilter2bv( Operation *op, ValuesReturnFilter *vrf, struct berval *fstr )
+{
+ struct berval tmp;
+ ber_len_t len;
+ int undef;
+
+ if ( vrf == NULL ) {
+ ber_str2bv_x( "No filter!", STRLENOF("No filter!"), 1, fstr,
+ op->o_tmpmemctx );
+ return;
+ }
+ undef = vrf->vrf_choice & SLAPD_FILTER_UNDEFINED;
+
+ switch ( vrf->vrf_choice & SLAPD_FILTER_MASK ) {
+ case LDAP_FILTER_EQUALITY:
+ filter_escape_value_x( &vrf->vrf_av_value, &tmp, op->o_tmpmemctx );
+
+ fstr->bv_len = vrf->vrf_av_desc->ad_cname.bv_len +
+ tmp.bv_len + STRLENOF("(=)");
+ if ( undef ) fstr->bv_len++;
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=%s)",
+ vrf->vrf_av_desc->ad_cname.bv_val,
+ tmp.bv_val );
+
+ ber_memfree_x( tmp.bv_val, op->o_tmpmemctx );
+ break;
+
+ case LDAP_FILTER_GE:
+ filter_escape_value_x( &vrf->vrf_av_value, &tmp, op->o_tmpmemctx );
+
+ fstr->bv_len = vrf->vrf_av_desc->ad_cname.bv_len +
+ tmp.bv_len + STRLENOF("(>=)");
+ if ( undef ) fstr->bv_len++;
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s>=%s)",
+ vrf->vrf_av_desc->ad_cname.bv_val,
+ tmp.bv_val );
+
+ ber_memfree_x( tmp.bv_val, op->o_tmpmemctx );
+ break;
+
+ case LDAP_FILTER_LE:
+ filter_escape_value_x( &vrf->vrf_av_value, &tmp, op->o_tmpmemctx );
+
+ fstr->bv_len = vrf->vrf_av_desc->ad_cname.bv_len +
+ tmp.bv_len + STRLENOF("(<=)");
+ if ( undef ) fstr->bv_len++;
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s<=%s)",
+ vrf->vrf_av_desc->ad_cname.bv_val,
+ tmp.bv_val );
+
+ ber_memfree_x( tmp.bv_val, op->o_tmpmemctx );
+ break;
+
+ case LDAP_FILTER_APPROX:
+ filter_escape_value_x( &vrf->vrf_av_value, &tmp, op->o_tmpmemctx );
+
+ fstr->bv_len = vrf->vrf_av_desc->ad_cname.bv_len +
+ tmp.bv_len + STRLENOF("(~=)");
+ if ( undef ) fstr->bv_len++;
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s~=%s)",
+ vrf->vrf_av_desc->ad_cname.bv_val,
+ tmp.bv_val );
+ ber_memfree_x( tmp.bv_val, op->o_tmpmemctx );
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ fstr->bv_len = vrf->vrf_sub_desc->ad_cname.bv_len +
+ STRLENOF("(=*)");
+ if ( undef ) fstr->bv_len++;
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 128, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
+ vrf->vrf_sub_desc->ad_cname.bv_val );
+
+ if ( vrf->vrf_sub_initial.bv_val != NULL ) {
+ len = fstr->bv_len;
+
+ filter_escape_value_x( &vrf->vrf_sub_initial, &tmp, op->o_tmpmemctx );
+
+ fstr->bv_len += tmp.bv_len;
+ fstr->bv_val = op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1,
+ op->o_tmpmemctx );
+
+ snprintf( &fstr->bv_val[len-2], tmp.bv_len+3,
+ /* "(attr=" */ "%s*)",
+ tmp.bv_val );
+
+ ber_memfree_x( tmp.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( vrf->vrf_sub_any != NULL ) {
+ int i;
+ for ( i = 0; vrf->vrf_sub_any[i].bv_val != NULL; i++ ) {
+ len = fstr->bv_len;
+ filter_escape_value_x( &vrf->vrf_sub_any[i], &tmp,
+ op->o_tmpmemctx );
+
+ fstr->bv_len += tmp.bv_len + 1;
+ fstr->bv_val = op->o_tmprealloc( fstr->bv_val,
+ fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( &fstr->bv_val[len-1], tmp.bv_len+3,
+ /* "(attr=[init]*[any*]" */ "%s*)",
+ tmp.bv_val );
+ ber_memfree_x( tmp.bv_val, op->o_tmpmemctx );
+ }
+ }
+
+ if ( vrf->vrf_sub_final.bv_val != NULL ) {
+ len = fstr->bv_len;
+
+ filter_escape_value_x( &vrf->vrf_sub_final, &tmp, op->o_tmpmemctx );
+
+ fstr->bv_len += tmp.bv_len;
+ fstr->bv_val = op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1,
+ op->o_tmpmemctx );
+
+ snprintf( &fstr->bv_val[len-1], tmp.bv_len+3,
+ /* "(attr=[init*][any*]" */ "%s)",
+ tmp.bv_val );
+
+ ber_memfree_x( tmp.bv_val, op->o_tmpmemctx );
+ }
+
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ fstr->bv_len = vrf->vrf_desc->ad_cname.bv_len +
+ STRLENOF("(=*)");
+ if ( undef ) fstr->bv_len++;
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
+ vrf->vrf_desc->ad_cname.bv_val );
+ break;
+
+ case LDAP_FILTER_EXT: {
+ struct berval ad;
+ filter_escape_value_x( &vrf->vrf_mr_value, &tmp, op->o_tmpmemctx );
+
+ if ( vrf->vrf_mr_desc ) {
+ ad = vrf->vrf_mr_desc->ad_cname;
+ } else {
+ ad.bv_len = 0;
+ ad.bv_val = "";
+ }
+
+ fstr->bv_len = ad.bv_len +
+ ( vrf->vrf_mr_dnattrs ? STRLENOF(":dn") : 0 ) +
+ ( vrf->vrf_mr_rule_text.bv_len
+ ? vrf->vrf_mr_rule_text.bv_len+1 : 0 ) +
+ tmp.bv_len + STRLENOF("(:=)");
+ if ( undef ) fstr->bv_len++;
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s%s%s:=%s)",
+ ad.bv_val,
+ vrf->vrf_mr_dnattrs ? ":dn" : "",
+ vrf->vrf_mr_rule_text.bv_len ? ":" : "",
+ vrf->vrf_mr_rule_text.bv_len ? vrf->vrf_mr_rule_text.bv_val : "",
+ tmp.bv_val );
+
+ ber_memfree_x( tmp.bv_val, op->o_tmpmemctx );
+ } break;
+
+ case SLAPD_FILTER_COMPUTED:
+ ber_str2bv_x(
+ vrf->vrf_result == LDAP_COMPARE_FALSE ? "(?=false)" :
+ vrf->vrf_result == LDAP_COMPARE_TRUE ? "(?=true)" :
+ vrf->vrf_result == SLAPD_COMPARE_UNDEFINED
+ ? "(?=undefined)" : "(?=error)",
+ vrf->vrf_result == LDAP_COMPARE_FALSE ? STRLENOF("(?=false)") :
+ vrf->vrf_result == LDAP_COMPARE_TRUE ? STRLENOF("(?=true)") :
+ vrf->vrf_result == SLAPD_COMPARE_UNDEFINED
+ ? STRLENOF("(?=undefined)") : STRLENOF("(?=error)"),
+ 1, fstr, op->o_tmpmemctx );
+ break;
+
+ default:
+ ber_str2bv_x( "(?=unknown)", STRLENOF("(?=unknown)"),
+ 1, fstr, op->o_tmpmemctx );
+ break;
+ }
+}
diff --git a/servers/slapd/filterentry.c b/servers/slapd/filterentry.c
new file mode 100644
index 0000000..61b64ff
--- /dev/null
+++ b/servers/slapd/filterentry.c
@@ -0,0 +1,986 @@
+/* filterentry.c - apply a filter to an entry */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+
+#ifdef LDAP_COMP_MATCH
+#include "component.h"
+#endif
+
+static int test_filter_and( Operation *op, Entry *e, Filter *flist );
+static int test_filter_or( Operation *op, Entry *e, Filter *flist );
+static int test_substrings_filter( Operation *op, Entry *e, Filter *f);
+static int test_ava_filter( Operation *op,
+ Entry *e, AttributeAssertion *ava, int type );
+static int test_mra_filter( Operation *op,
+ Entry *e, MatchingRuleAssertion *mra );
+static int test_presence_filter( Operation *op,
+ Entry *e, AttributeDescription *desc );
+
+
+/*
+ * test_filter - test a filter against a single entry.
+ * returns:
+ * LDAP_COMPARE_TRUE filter matched
+ * LDAP_COMPARE_FALSE filter did not match
+ * SLAPD_COMPARE_UNDEFINED filter is undefined
+ * or an ldap result code indicating error
+ */
+
+int
+test_filter(
+ Operation *op,
+ Entry *e,
+ Filter *f )
+{
+ int rc;
+ Debug( LDAP_DEBUG_FILTER, "=> test_filter\n" );
+
+ if ( f->f_choice & SLAPD_FILTER_UNDEFINED ) {
+ Debug( LDAP_DEBUG_FILTER, " UNDEFINED\n" );
+ rc = SLAPD_COMPARE_UNDEFINED;
+ goto out;
+ }
+
+ switch ( f->f_choice ) {
+ case SLAPD_FILTER_COMPUTED:
+ Debug( LDAP_DEBUG_FILTER, " COMPUTED %s (%d)\n",
+ f->f_result == LDAP_COMPARE_FALSE ? "false" :
+ f->f_result == LDAP_COMPARE_TRUE ? "true" :
+ f->f_result == SLAPD_COMPARE_UNDEFINED ? "undefined" : "error",
+ f->f_result );
+
+ rc = f->f_result;
+ break;
+
+ case LDAP_FILTER_EQUALITY:
+ Debug( LDAP_DEBUG_FILTER, " EQUALITY\n" );
+ rc = test_ava_filter( op, e, f->f_ava, LDAP_FILTER_EQUALITY );
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ Debug( LDAP_DEBUG_FILTER, " SUBSTRINGS\n" );
+ rc = test_substrings_filter( op, e, f );
+ break;
+
+ case LDAP_FILTER_GE:
+ Debug( LDAP_DEBUG_FILTER, " GE\n" );
+ rc = test_ava_filter( op, e, f->f_ava, LDAP_FILTER_GE );
+ break;
+
+ case LDAP_FILTER_LE:
+ Debug( LDAP_DEBUG_FILTER, " LE\n" );
+ rc = test_ava_filter( op, e, f->f_ava, LDAP_FILTER_LE );
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ Debug( LDAP_DEBUG_FILTER, " PRESENT\n" );
+ rc = test_presence_filter( op, e, f->f_desc );
+ break;
+
+ case LDAP_FILTER_APPROX:
+ Debug( LDAP_DEBUG_FILTER, " APPROX\n" );
+ rc = test_ava_filter( op, e, f->f_ava, LDAP_FILTER_APPROX );
+ break;
+
+ case LDAP_FILTER_AND:
+ Debug( LDAP_DEBUG_FILTER, " AND\n" );
+ rc = test_filter_and( op, e, f->f_and );
+ break;
+
+ case LDAP_FILTER_OR:
+ Debug( LDAP_DEBUG_FILTER, " OR\n" );
+ rc = test_filter_or( op, e, f->f_or );
+ break;
+
+ case LDAP_FILTER_NOT:
+ Debug( LDAP_DEBUG_FILTER, " NOT\n" );
+ rc = test_filter( op, e, f->f_not );
+
+ /* Flip true to false and false to true
+ * but leave Undefined alone.
+ */
+ switch( rc ) {
+ case LDAP_COMPARE_TRUE:
+ rc = LDAP_COMPARE_FALSE;
+ break;
+ case LDAP_COMPARE_FALSE:
+ rc = LDAP_COMPARE_TRUE;
+ break;
+ }
+ break;
+
+ case LDAP_FILTER_EXT:
+ Debug( LDAP_DEBUG_FILTER, " EXT\n" );
+ rc = test_mra_filter( op, e, f->f_mra );
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY, " unknown filter type %lu\n",
+ f->f_choice );
+ rc = LDAP_PROTOCOL_ERROR;
+ }
+out:
+ Debug( LDAP_DEBUG_FILTER, "<= test_filter %d\n", rc );
+ return( rc );
+}
+
+static int test_mra_filter(
+ Operation *op,
+ Entry *e,
+ MatchingRuleAssertion *mra )
+{
+ Attribute *a;
+ void *memctx;
+ BER_MEMFREE_FN *memfree;
+#ifdef LDAP_COMP_MATCH
+ int i, num_attr_vals = 0;
+#endif
+
+ if ( op == NULL ) {
+ memctx = NULL;
+ memfree = slap_sl_mfuncs.bmf_free;
+ } else {
+ memctx = op->o_tmpmemctx;
+ memfree = op->o_tmpfree;
+ }
+
+ if ( mra->ma_desc ) {
+ /*
+ * if ma_desc is available, then we're filtering for
+ * one attribute, and SEARCH permissions can be checked
+ * directly.
+ */
+ if ( !access_allowed( op, e,
+ mra->ma_desc, &mra->ma_value, ACL_SEARCH, NULL ) )
+ {
+ return LDAP_INSUFFICIENT_ACCESS;
+ }
+
+ if ( mra->ma_desc == slap_schema.si_ad_entryDN ) {
+ int ret, rc;
+ const char *text;
+
+ rc = value_match( &ret, slap_schema.si_ad_entryDN, mra->ma_rule,
+ SLAP_MR_EXT, &e->e_nname, &mra->ma_value, &text );
+
+
+ if( rc != LDAP_SUCCESS ) return rc;
+ if ( ret == 0 ) return LDAP_COMPARE_TRUE;
+ return LDAP_COMPARE_FALSE;
+ }
+
+ for ( a = attrs_find( e->e_attrs, mra->ma_desc );
+ a != NULL;
+ a = attrs_find( a->a_next, mra->ma_desc ) )
+ {
+ struct berval *bv;
+ int normalize_attribute = 0;
+
+#ifdef LDAP_COMP_MATCH
+ /* Component Matching */
+ if ( mra->ma_cf && mra->ma_rule->smr_usage & SLAP_MR_COMPONENT ) {
+ num_attr_vals = 0;
+ if ( !a->a_comp_data ) {
+ num_attr_vals = a->a_numvals;
+ if ( num_attr_vals <= 0 ) {
+ /* no attribute value */
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+ num_attr_vals++;
+
+ /* following malloced will be freed by comp_tree_free () */
+ a->a_comp_data = SLAP_MALLOC( sizeof( ComponentData ) +
+ sizeof( ComponentSyntaxInfo* )*num_attr_vals );
+
+ if ( !a->a_comp_data ) return LDAP_NO_MEMORY;
+ a->a_comp_data->cd_tree = (ComponentSyntaxInfo**)
+ ((char*)a->a_comp_data + sizeof(ComponentData));
+ a->a_comp_data->cd_tree[num_attr_vals - 1] =
+ (ComponentSyntaxInfo*) NULL;
+ a->a_comp_data->cd_mem_op =
+ nibble_mem_allocator( 1024*16, 1024 );
+ }
+ }
+#endif
+
+ /* If ma_rule is not the same as the attribute's
+ * normal rule, then we can't use the a_nvals.
+ */
+ if ( mra->ma_rule == a->a_desc->ad_type->sat_equality ) {
+ bv = a->a_nvals;
+
+ } else {
+ bv = a->a_vals;
+ normalize_attribute = 1;
+ }
+#ifdef LDAP_COMP_MATCH
+ i = 0;
+#endif
+ for ( ; !BER_BVISNULL( bv ); bv++ ) {
+ int ret;
+ int rc;
+ const char *text;
+
+#ifdef LDAP_COMP_MATCH
+ if ( mra->ma_cf &&
+ mra->ma_rule->smr_usage & SLAP_MR_COMPONENT )
+ {
+ /* Check if decoded component trees are already linked */
+ if ( num_attr_vals ) {
+ a->a_comp_data->cd_tree[i] = attr_converter(
+ a, a->a_desc->ad_type->sat_syntax, bv );
+ }
+ /* decoding error */
+ if ( !a->a_comp_data->cd_tree[i] ) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+ rc = value_match( &ret, a->a_desc, mra->ma_rule,
+ SLAP_MR_COMPONENT,
+ (struct berval*)a->a_comp_data->cd_tree[i++],
+ (void*)mra, &text );
+ } else
+#endif
+ {
+ struct berval nbv = BER_BVNULL;
+
+ if ( normalize_attribute && mra->ma_rule->smr_normalize ) {
+ /*
+
+ Document: RFC 4511
+
+ 4.5.1. Search Request
+ ...
+ If the type field is present and the matchingRule is present,
+ the matchValue is compared against entry attributes of the
+ specified type. In this case, the matchingRule MUST be one
+ suitable for use with the specified type (see [RFC4517]),
+ otherwise the filter item is Undefined.
+
+
+ In this case, since the matchingRule requires the assertion
+ value to be normalized, we normalize the attribute value
+ according to the syntax of the matchingRule.
+
+ This should likely be done inside value_match(), by passing
+ the appropriate flags, but this is not done at present.
+ See ITS#3406.
+ */
+ if ( mra->ma_rule->smr_normalize(
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ mra->ma_rule->smr_syntax,
+ mra->ma_rule,
+ bv, &nbv, memctx ) != LDAP_SUCCESS )
+ {
+ /* FIXME: stop processing? */
+ continue;
+ }
+
+ } else {
+ nbv = *bv;
+ }
+
+ rc = value_match( &ret, a->a_desc, mra->ma_rule,
+ SLAP_MR_EXT, &nbv, &mra->ma_value, &text );
+
+ if ( nbv.bv_val != bv->bv_val ) {
+ memfree( nbv.bv_val, memctx );
+ }
+ }
+
+ if ( rc != LDAP_SUCCESS ) return rc;
+ if ( ret == 0 ) return LDAP_COMPARE_TRUE;
+ }
+ }
+
+ } else {
+ /*
+ * No attribute description: test all
+ */
+ for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
+ struct berval *bv, value;
+ const char *text = NULL;
+ int rc;
+ int normalize_attribute = 0;
+
+ /* check if matching is appropriate */
+ if ( !mr_usable_with_at( mra->ma_rule, a->a_desc->ad_type ) ) {
+ continue;
+ }
+
+ /* normalize for equality */
+ rc = asserted_value_validate_normalize( a->a_desc, mra->ma_rule,
+ SLAP_MR_EXT|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
+ &mra->ma_value, &value, &text, memctx );
+ if ( rc != LDAP_SUCCESS ) continue;
+
+ /* check search access */
+ if ( !access_allowed( op, e,
+ a->a_desc, &value, ACL_SEARCH, NULL ) )
+ {
+ memfree( value.bv_val, memctx );
+ continue;
+ }
+#ifdef LDAP_COMP_MATCH
+ /* Component Matching */
+ if ( mra->ma_cf &&
+ mra->ma_rule->smr_usage & SLAP_MR_COMPONENT )
+ {
+ int ret;
+
+ rc = value_match( &ret, a->a_desc, mra->ma_rule,
+ SLAP_MR_COMPONENT,
+ (struct berval*)a, (void*)mra, &text );
+ if ( rc != LDAP_SUCCESS ) break;
+
+ if ( ret == 0 ) {
+ rc = LDAP_COMPARE_TRUE;
+ break;
+ }
+
+ }
+#endif
+
+ /* check match */
+ if ( mra->ma_rule == a->a_desc->ad_type->sat_equality ) {
+ bv = a->a_nvals;
+
+ } else {
+ bv = a->a_vals;
+ normalize_attribute = 1;
+ }
+
+ for ( ; !BER_BVISNULL( bv ); bv++ ) {
+ int ret;
+ struct berval nbv = BER_BVNULL;
+
+ if ( normalize_attribute && mra->ma_rule->smr_normalize ) {
+ /* see comment above */
+ if ( mra->ma_rule->smr_normalize(
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ mra->ma_rule->smr_syntax,
+ mra->ma_rule,
+ bv, &nbv, memctx ) != LDAP_SUCCESS )
+ {
+ /* FIXME: stop processing? */
+ continue;
+ }
+
+ } else {
+ nbv = *bv;
+ }
+
+ rc = value_match( &ret, a->a_desc, mra->ma_rule,
+ SLAP_MR_EXT, &nbv, &value, &text );
+
+ if ( nbv.bv_val != bv->bv_val ) {
+ memfree( nbv.bv_val, memctx );
+ }
+
+ if ( rc != LDAP_SUCCESS ) break;
+
+ if ( ret == 0 ) {
+ rc = LDAP_COMPARE_TRUE;
+ break;
+ }
+ }
+ memfree( value.bv_val, memctx );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ }
+ }
+
+ /* check attrs in DN AVAs if required */
+ if ( mra->ma_dnattrs && !BER_BVISEMPTY( &e->e_nname ) ) {
+ LDAPDN dn = NULL;
+ int iRDN, iAVA;
+ int rc;
+
+ /* parse and pretty the dn */
+ rc = dnPrettyDN( NULL, &e->e_name, &dn, memctx );
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* for each AVA of each RDN ... */
+ for ( iRDN = 0; dn[ iRDN ]; iRDN++ ) {
+ LDAPRDN rdn = dn[ iRDN ];
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ iAVA ];
+ struct berval *bv = &ava->la_value,
+ value = BER_BVNULL,
+ nbv = BER_BVNULL;
+ AttributeDescription *ad =
+ (AttributeDescription *)ava->la_private;
+ int ret;
+ const char *text;
+
+ assert( ad != NULL );
+
+ if ( mra->ma_desc ) {
+ /* have a mra type? check for subtype */
+ if ( !is_ad_subtype( ad, mra->ma_desc ) ) {
+ continue;
+ }
+ value = mra->ma_value;
+
+ } else {
+ const char *text = NULL;
+
+ /* check if matching is appropriate */
+ if ( !mr_usable_with_at( mra->ma_rule, ad->ad_type ) ) {
+ continue;
+ }
+
+ /* normalize for equality */
+ rc = asserted_value_validate_normalize( ad,
+ mra->ma_rule,
+ SLAP_MR_EXT|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
+ &mra->ma_value, &value, &text, memctx );
+ if ( rc != LDAP_SUCCESS ) continue;
+
+ /* check search access */
+ if ( !access_allowed( op, e,
+ ad, &value, ACL_SEARCH, NULL ) )
+ {
+ memfree( value.bv_val, memctx );
+ continue;
+ }
+ }
+
+ if ( mra->ma_rule->smr_normalize ) {
+ /* see comment above */
+ if ( mra->ma_rule->smr_normalize(
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ mra->ma_rule->smr_syntax,
+ mra->ma_rule,
+ bv, &nbv, memctx ) != LDAP_SUCCESS )
+ {
+ /* FIXME: stop processing? */
+ rc = LDAP_SUCCESS;
+ ret = -1;
+ goto cleanup;
+ }
+
+ } else {
+ nbv = *bv;
+ }
+
+ /* check match */
+ rc = value_match( &ret, ad, mra->ma_rule, SLAP_MR_EXT,
+ &nbv, &value, &text );
+
+cleanup:;
+ if ( !BER_BVISNULL( &value ) && value.bv_val != mra->ma_value.bv_val ) {
+ memfree( value.bv_val, memctx );
+ }
+
+ if ( !BER_BVISNULL( &nbv ) && nbv.bv_val != bv->bv_val ) {
+ memfree( nbv.bv_val, memctx );
+ }
+
+ if ( rc == LDAP_SUCCESS && ret == 0 ) rc = LDAP_COMPARE_TRUE;
+
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_dnfree_x( dn, memctx );
+ return rc;
+ }
+ }
+ }
+ ldap_dnfree_x( dn, memctx );
+ }
+
+ return LDAP_COMPARE_FALSE;
+}
+
+static int
+test_ava_filter(
+ Operation *op,
+ Entry *e,
+ AttributeAssertion *ava,
+ int type )
+{
+ int rc;
+ Attribute *a;
+#ifdef LDAP_COMP_MATCH
+ int i, num_attr_vals = 0;
+ AttributeAliasing *a_alias = NULL;
+#endif
+
+ if ( !access_allowed( op, e,
+ ava->aa_desc, &ava->aa_value, ACL_SEARCH, NULL ) )
+ {
+ return LDAP_INSUFFICIENT_ACCESS;
+ }
+
+ if ( ava->aa_desc == slap_schema.si_ad_hasSubordinates
+ && op && op->o_bd && op->o_bd->be_has_subordinates )
+ {
+ int hasSubordinates = 0;
+ struct berval hs;
+
+ if( type != LDAP_FILTER_EQUALITY &&
+ type != LDAP_FILTER_APPROX )
+ {
+ /* No other match is allowed */
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+
+ if ( op->o_bd->be_has_subordinates( op, e, &hasSubordinates ) !=
+ LDAP_SUCCESS )
+ {
+ return LDAP_OTHER;
+ }
+
+ if ( hasSubordinates == LDAP_COMPARE_TRUE ) {
+ hs = slap_true_bv;
+
+ } else if ( hasSubordinates == LDAP_COMPARE_FALSE ) {
+ hs = slap_false_bv;
+
+ } else {
+ return LDAP_OTHER;
+ }
+
+ if ( bvmatch( &ava->aa_value, &hs ) ) return LDAP_COMPARE_TRUE;
+ return LDAP_COMPARE_FALSE;
+ }
+
+ if ( ava->aa_desc == slap_schema.si_ad_entryDN ) {
+ MatchingRule *mr;
+ int match;
+ const char *text;
+
+ if( type != LDAP_FILTER_EQUALITY &&
+ type != LDAP_FILTER_APPROX )
+ {
+ /* No other match is allowed */
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+
+ mr = slap_schema.si_ad_entryDN->ad_type->sat_equality;
+ assert( mr != NULL );
+
+ rc = value_match( &match, slap_schema.si_ad_entryDN, mr,
+ SLAP_MR_EXT, &e->e_nname, &ava->aa_value, &text );
+
+ if( rc != LDAP_SUCCESS ) return rc;
+ if( match == 0 ) return LDAP_COMPARE_TRUE;
+ return LDAP_COMPARE_FALSE;
+ }
+
+ rc = LDAP_COMPARE_FALSE;
+
+#ifdef LDAP_COMP_MATCH
+ if ( is_aliased_attribute && ava->aa_cf )
+ {
+ a_alias = is_aliased_attribute ( ava->aa_desc );
+ if ( a_alias )
+ ava->aa_desc = a_alias->aa_aliased_ad;
+ else
+ ava->aa_cf = NULL;
+ }
+#endif
+
+ for(a = attrs_find( e->e_attrs, ava->aa_desc );
+ a != NULL;
+ a = attrs_find( a->a_next, ava->aa_desc ) )
+ {
+ int use;
+ MatchingRule *mr;
+ struct berval *bv;
+
+ if (( ava->aa_desc != a->a_desc ) && !access_allowed( op,
+ e, a->a_desc, &ava->aa_value, ACL_SEARCH, NULL ))
+ {
+ rc = LDAP_INSUFFICIENT_ACCESS;
+ continue;
+ }
+
+ use = SLAP_MR_EQUALITY;
+
+ switch ( type ) {
+ case LDAP_FILTER_APPROX:
+ use = SLAP_MR_EQUALITY_APPROX;
+ mr = a->a_desc->ad_type->sat_approx;
+ if( mr != NULL ) break;
+
+ /* fallthru: use EQUALITY matching rule if no APPROX rule */
+
+ case LDAP_FILTER_EQUALITY:
+ /* use variable set above so fall thru use is not clobbered */
+ mr = a->a_desc->ad_type->sat_equality;
+ break;
+
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ use = SLAP_MR_ORDERING;
+ mr = a->a_desc->ad_type->sat_ordering;
+ break;
+
+ default:
+ mr = NULL;
+ }
+
+ if( mr == NULL ) {
+ rc = LDAP_INAPPROPRIATE_MATCHING;
+ continue;
+ }
+
+ /* We have no Sort optimization for Approx matches */
+ if (( a->a_flags & SLAP_ATTR_SORTED_VALS ) && type != LDAP_FILTER_APPROX ) {
+ unsigned slot;
+ int ret;
+
+ /* For Ordering matches, we just need to do one comparison with
+ * either the first (least) or last (greatest) value.
+ */
+ if ( use == SLAP_MR_ORDERING ) {
+ const char *text;
+ int match, which;
+ which = (type == LDAP_FILTER_LE) ? 0 : a->a_numvals-1;
+ ret = value_match( &match, a->a_desc, mr, use,
+ &a->a_nvals[which], &ava->aa_value, &text );
+ if ( ret != LDAP_SUCCESS ) return ret;
+ if (( type == LDAP_FILTER_LE && match <= 0 ) ||
+ ( type == LDAP_FILTER_GE && match >= 0 ))
+ return LDAP_COMPARE_TRUE;
+ continue;
+ }
+ /* Only Equality will get here */
+ ret = attr_valfind( a, use | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
+ &ava->aa_value, &slot, NULL );
+ if ( ret == LDAP_SUCCESS )
+ return LDAP_COMPARE_TRUE;
+ else if ( ret != LDAP_NO_SUCH_ATTRIBUTE )
+ return ret;
+#if 0
+ /* The following is useful if we want to know which values
+ * matched an ordering test. But here we don't care, we just
+ * want to know if any value did, and that is checked above.
+ */
+ if ( ret == LDAP_NO_SUCH_ATTRIBUTE ) {
+ /* If insertion point is not the end of the list, there was
+ * at least one value greater than the assertion.
+ */
+ if ( type == LDAP_FILTER_GE && slot < a->a_numvals )
+ return LDAP_COMPARE_TRUE;
+ /* Likewise, if insertion point is not the head of the list,
+ * there was at least one value less than the assertion.
+ */
+ if ( type == LDAP_FILTER_LE && slot > 0 )
+ return LDAP_COMPARE_TRUE;
+ return LDAP_COMPARE_FALSE;
+ }
+#endif
+ continue;
+ }
+
+#ifdef LDAP_COMP_MATCH
+ if ( nibble_mem_allocator && ava->aa_cf && !a->a_comp_data ) {
+ /* Component Matching */
+ for ( num_attr_vals = 0; a->a_vals[num_attr_vals].bv_val != NULL; num_attr_vals++ );
+ if ( num_attr_vals <= 0 )/* no attribute value */
+ return LDAP_INAPPROPRIATE_MATCHING;
+ num_attr_vals++;/* for NULL termination */
+
+ /* following malloced will be freed by comp_tree_free () */
+ a->a_comp_data = SLAP_MALLOC( sizeof( ComponentData ) + sizeof( ComponentSyntaxInfo* )*num_attr_vals );
+
+ if ( !a->a_comp_data ) {
+ return LDAP_NO_MEMORY;
+ }
+
+ a->a_comp_data->cd_tree = (ComponentSyntaxInfo**)((char*)a->a_comp_data + sizeof(ComponentData));
+ i = num_attr_vals;
+ for ( ; i ; i-- ) {
+ a->a_comp_data->cd_tree[ i-1 ] = (ComponentSyntaxInfo*)NULL;
+ }
+
+ a->a_comp_data->cd_mem_op = nibble_mem_allocator ( 1024*10*(num_attr_vals-1), 1024 );
+ if ( a->a_comp_data->cd_mem_op == NULL ) {
+ free ( a->a_comp_data );
+ a->a_comp_data = NULL;
+ return LDAP_OPERATIONS_ERROR;
+ }
+ }
+
+ i = 0;
+#endif
+
+ for ( bv = a->a_nvals; !BER_BVISNULL( bv ); bv++ ) {
+ int ret, match;
+ const char *text;
+
+#ifdef LDAP_COMP_MATCH
+ if( attr_converter && ava->aa_cf && a->a_comp_data ) {
+ /* Check if decoded component trees are already linked */
+ struct berval cf_bv = { 20, "componentFilterMatch" };
+ MatchingRule* cf_mr = mr_bvfind( &cf_bv );
+ MatchingRuleAssertion mra;
+ mra.ma_cf = ava->aa_cf;
+
+ if ( a->a_comp_data->cd_tree[i] == NULL )
+ a->a_comp_data->cd_tree[i] = attr_converter (a, a->a_desc->ad_type->sat_syntax, (a->a_vals + i));
+ /* decoding error */
+ if ( !a->a_comp_data->cd_tree[i] ) {
+ free_ComponentData ( a );
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ ret = value_match( &match, a->a_desc, cf_mr,
+ SLAP_MR_COMPONENT,
+ (struct berval*)a->a_comp_data->cd_tree[i++],
+ (void*)&mra, &text );
+ if ( ret == LDAP_INAPPROPRIATE_MATCHING ) {
+ /* cached component tree is broken, just remove it */
+ free_ComponentData ( a );
+ return ret;
+ }
+ if ( a_alias )
+ ava->aa_desc = a_alias->aa_aliasing_ad;
+
+ } else
+#endif
+ {
+ ret = ordered_value_match( &match, a->a_desc, mr, use,
+ bv, &ava->aa_value, &text );
+ }
+
+ if( ret != LDAP_SUCCESS ) {
+ rc = ret;
+ break;
+ }
+
+ switch ( type ) {
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_APPROX:
+ if ( match == 0 ) return LDAP_COMPARE_TRUE;
+ break;
+
+ case LDAP_FILTER_GE:
+ if ( match >= 0 ) return LDAP_COMPARE_TRUE;
+ break;
+
+ case LDAP_FILTER_LE:
+ if ( match <= 0 ) return LDAP_COMPARE_TRUE;
+ break;
+ }
+ }
+ }
+
+#ifdef LDAP_COMP_MATCH
+ if ( a_alias )
+ ava->aa_desc = a_alias->aa_aliasing_ad;
+#endif
+
+ return rc;
+}
+
+
+static int
+test_presence_filter(
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc )
+{
+ Attribute *a;
+ int rc;
+
+ if ( !access_allowed( op, e, desc, NULL, ACL_SEARCH, NULL ) ) {
+ return LDAP_INSUFFICIENT_ACCESS;
+ }
+
+ if ( desc == slap_schema.si_ad_hasSubordinates ) {
+ /*
+ * XXX: fairly optimistic: if the function is defined,
+ * then PRESENCE must succeed, because hasSubordinate
+ * is boolean-valued; I think we may live with this
+ * simplification by now.
+ */
+ if ( op && op->o_bd && op->o_bd->be_has_subordinates ) {
+ return LDAP_COMPARE_TRUE;
+ }
+
+ return LDAP_COMPARE_FALSE;
+ }
+
+ if ( desc == slap_schema.si_ad_entryDN ||
+ desc == slap_schema.si_ad_subschemaSubentry )
+ {
+ /* entryDN and subschemaSubentry are always present */
+ return LDAP_COMPARE_TRUE;
+ }
+
+ rc = LDAP_COMPARE_FALSE;
+
+ for(a = attrs_find( e->e_attrs, desc );
+ a != NULL;
+ a = attrs_find( a->a_next, desc ) )
+ {
+ if (( desc != a->a_desc ) && !access_allowed( op,
+ e, a->a_desc, NULL, ACL_SEARCH, NULL ))
+ {
+ rc = LDAP_INSUFFICIENT_ACCESS;
+ continue;
+ }
+
+ rc = LDAP_COMPARE_TRUE;
+ break;
+ }
+
+ return rc;
+}
+
+
+static int
+test_filter_and(
+ Operation *op,
+ Entry *e,
+ Filter *flist )
+{
+ Filter *f;
+ int rtn = LDAP_COMPARE_TRUE; /* True if empty */
+
+ Debug( LDAP_DEBUG_FILTER, "=> test_filter_and\n" );
+
+ for ( f = flist; f != NULL; f = f->f_next ) {
+ int rc = test_filter( op, e, f );
+
+ if ( rc == LDAP_COMPARE_FALSE ) {
+ /* filter is False */
+ rtn = rc;
+ break;
+ }
+
+ if ( rc != LDAP_COMPARE_TRUE ) {
+ /* filter is Undefined unless later elements are False */
+ rtn = rc;
+ }
+ }
+
+ Debug( LDAP_DEBUG_FILTER, "<= test_filter_and %d\n", rtn );
+
+ return rtn;
+}
+
+static int
+test_filter_or(
+ Operation *op,
+ Entry *e,
+ Filter *flist )
+{
+ Filter *f;
+ int rtn = LDAP_COMPARE_FALSE; /* False if empty */
+
+ Debug( LDAP_DEBUG_FILTER, "=> test_filter_or\n" );
+
+ for ( f = flist; f != NULL; f = f->f_next ) {
+ int rc = test_filter( op, e, f );
+
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ /* filter is True */
+ rtn = rc;
+ break;
+ }
+
+ if ( rc != LDAP_COMPARE_FALSE ) {
+ /* filter is Undefined unless later elements are True */
+ rtn = rc;
+ }
+ }
+
+ Debug( LDAP_DEBUG_FILTER, "<= test_filter_or %d\n", rtn );
+ return rtn;
+}
+
+
+static int
+test_substrings_filter(
+ Operation *op,
+ Entry *e,
+ Filter *f )
+{
+ Attribute *a;
+ int rc;
+
+ Debug( LDAP_DEBUG_FILTER, "begin test_substrings_filter\n" );
+
+ if ( !access_allowed( op, e,
+ f->f_sub_desc, NULL, ACL_SEARCH, NULL ) )
+ {
+ return LDAP_INSUFFICIENT_ACCESS;
+ }
+
+ rc = LDAP_COMPARE_FALSE;
+
+ for(a = attrs_find( e->e_attrs, f->f_sub_desc );
+ a != NULL;
+ a = attrs_find( a->a_next, f->f_sub_desc ) )
+ {
+ MatchingRule *mr;
+ struct berval *bv;
+
+ if (( f->f_sub_desc != a->a_desc ) && !access_allowed( op,
+ e, a->a_desc, NULL, ACL_SEARCH, NULL ))
+ {
+ rc = LDAP_INSUFFICIENT_ACCESS;
+ continue;
+ }
+
+ mr = a->a_desc->ad_type->sat_substr;
+ if( mr == NULL ) {
+ rc = LDAP_INAPPROPRIATE_MATCHING;
+ continue;
+ }
+
+ for ( bv = a->a_nvals; !BER_BVISNULL( bv ); bv++ ) {
+ int ret, match;
+ const char *text;
+
+ ret = value_match( &match, a->a_desc, mr, SLAP_MR_SUBSTR,
+ bv, f->f_sub, &text );
+
+ if( ret != LDAP_SUCCESS ) {
+ rc = ret;
+ break;
+ }
+ if ( match == 0 ) return LDAP_COMPARE_TRUE;
+ }
+ }
+
+ Debug( LDAP_DEBUG_FILTER, "end test_substrings_filter %d\n",
+ rc );
+ return rc;
+}
diff --git a/servers/slapd/frontend.c b/servers/slapd/frontend.c
new file mode 100644
index 0000000..d0ca419
--- /dev/null
+++ b/servers/slapd/frontend.c
@@ -0,0 +1,174 @@
+/* frontend.c - routines for dealing with frontend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <sys/stat.h>
+
+#include "slap.h"
+#include "lutil.h"
+#include "lber_pvt.h"
+
+#include "ldap_rq.h"
+
+static BackendInfo slap_frontendInfo;
+static BackendDB slap_frontendDB;
+BackendDB *frontendDB;
+
+static int
+fe_entry_get_rw(
+ Operation *op,
+ struct berval *ndn,
+ ObjectClass *oc,
+ AttributeDescription *at,
+ int rw,
+ Entry **e )
+{
+ BackendDB *bd;
+ int rc = LDAP_NO_SUCH_OBJECT;
+
+ bd = op->o_bd;
+ op->o_bd = select_backend( ndn, 0 );
+ if ( op->o_bd != NULL ) {
+ if ( op->o_bd->be_fetch ) {
+ rc = op->o_bd->be_fetch( op, ndn, oc, at, rw, e );
+ }
+ }
+ op->o_bd = bd;
+
+ return rc;
+}
+
+static int
+fe_entry_release_rw(
+ Operation *op,
+ Entry *e,
+ int rw )
+{
+ BackendDB *bd;
+ int rc = LDAP_NO_SUCH_OBJECT;
+
+ bd = op->o_bd;
+ op->o_bd = select_backend( &e->e_nname, 0 );
+ if ( op->o_bd != NULL ) {
+ if ( op->o_bd->be_release ) {
+ rc = op->o_bd->be_release( op, e, rw );
+ }
+ }
+ op->o_bd = bd;
+
+ return rc;
+}
+
+int
+frontend_init( void )
+{
+ /* data */
+ frontendDB = &slap_frontendDB;
+ frontendDB->bd_self = frontendDB;
+
+ /* ACLs */
+ frontendDB->be_dfltaccess = ACL_READ;
+
+ /* limits */
+ frontendDB->be_def_limit.lms_t_soft = SLAPD_DEFAULT_TIMELIMIT; /* backward compatible limits */
+ frontendDB->be_def_limit.lms_t_hard = 0;
+ frontendDB->be_def_limit.lms_s_soft = SLAPD_DEFAULT_SIZELIMIT; /* backward compatible limits */
+ frontendDB->be_def_limit.lms_s_hard = 0;
+ frontendDB->be_def_limit.lms_s_unchecked = -1; /* no limit on unchecked size */
+ frontendDB->be_def_limit.lms_s_pr = 0; /* page limit */
+ frontendDB->be_def_limit.lms_s_pr_hide = 0; /* don't hide number of entries left */
+ frontendDB->be_def_limit.lms_s_pr_total = 0; /* number of total entries returned by pagedResults equal to hard limit */
+
+ ldap_pvt_thread_mutex_init( &frontendDB->be_pcsn_st.be_pcsn_mutex );
+
+ /* suffix */
+ frontendDB->be_suffix = ch_calloc( 2, sizeof( struct berval ) );
+ ber_str2bv( "", 0, 1, &frontendDB->be_suffix[0] );
+ BER_BVZERO( &frontendDB->be_suffix[1] );
+ frontendDB->be_nsuffix = ch_calloc( 2, sizeof( struct berval ) );
+ ber_str2bv( "", 0, 1, &frontendDB->be_nsuffix[0] );
+ BER_BVZERO( &frontendDB->be_nsuffix[1] );
+
+ /* info */
+ frontendDB->bd_info = &slap_frontendInfo;
+
+ SLAP_BFLAGS(frontendDB) |= SLAP_BFLAG_FRONTEND;
+
+ /* name */
+ frontendDB->bd_info->bi_type = "frontend";
+
+ /* known controls */
+ {
+ int i;
+
+ frontendDB->bd_info->bi_controls = slap_known_controls;
+
+ for ( i = 0; slap_known_controls[ i ]; i++ ) {
+ int cid;
+
+ if ( slap_find_control_id( slap_known_controls[ i ], &cid )
+ == LDAP_CONTROL_NOT_FOUND )
+ {
+ assert( 0 );
+ return -1;
+ }
+
+ frontendDB->bd_info->bi_ctrls[ cid ] = 1;
+ frontendDB->be_ctrls[ cid ] = 1;
+ }
+ }
+
+ /* calls */
+ frontendDB->bd_info->bi_op_abandon = fe_op_abandon;
+ frontendDB->bd_info->bi_op_add = fe_op_add;
+ frontendDB->bd_info->bi_op_bind = fe_op_bind;
+ frontendDB->bd_info->bi_op_compare = fe_op_compare;
+ frontendDB->bd_info->bi_op_delete = fe_op_delete;
+ frontendDB->bd_info->bi_op_modify = fe_op_modify;
+ frontendDB->bd_info->bi_op_modrdn = fe_op_modrdn;
+ frontendDB->bd_info->bi_op_search = fe_op_search;
+ frontendDB->bd_info->bi_extended = fe_extended;
+ frontendDB->bd_info->bi_operational = fe_aux_operational;
+ frontendDB->bd_info->bi_entry_get_rw = fe_entry_get_rw;
+ frontendDB->bd_info->bi_entry_release_rw = fe_entry_release_rw;
+ frontendDB->bd_info->bi_access_allowed = fe_access_allowed;
+ frontendDB->bd_info->bi_acl_group = fe_acl_group;
+ frontendDB->bd_info->bi_acl_attribute = fe_acl_attribute;
+
+#if 0
+ /* FIXME: is this too early? */
+ return backend_startup_one( frontendDB );
+#endif
+
+ return 0;
+}
+
diff --git a/servers/slapd/globals.c b/servers/slapd/globals.c
new file mode 100644
index 0000000..c785165
--- /dev/null
+++ b/servers/slapd/globals.c
@@ -0,0 +1,38 @@
+/* globals.c - various global variables */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <ac/string.h>
+#include "lber_pvt.h"
+
+#include "slap.h"
+
+
+/*
+ * Global variables, in general, should be declared in the file
+ * primarily responsible for its management. Configurable globals
+ * belong in config.c. Variables declared here have no other
+ * sensible home.
+ */
+
+const struct berval slap_empty_bv = BER_BVC("");
+const struct berval slap_unknown_bv = BER_BVC("unknown");
+
+/* normalized boolean values */
+const struct berval slap_true_bv = BER_BVC("TRUE");
+const struct berval slap_false_bv = BER_BVC("FALSE");
+
diff --git a/servers/slapd/index.c b/servers/slapd/index.c
new file mode 100644
index 0000000..303cb9a
--- /dev/null
+++ b/servers/slapd/index.c
@@ -0,0 +1,91 @@
+/* index.c - index utilities */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <lutil.h>
+
+#include "slap.h"
+
+static slap_verbmasks idxstr[] = {
+ { BER_BVC("pres"), SLAP_INDEX_PRESENT },
+ { BER_BVC("eq"), SLAP_INDEX_EQUALITY },
+ { BER_BVC("approx"), SLAP_INDEX_APPROX },
+ { BER_BVC("subinitial"), SLAP_INDEX_SUBSTR_INITIAL },
+ { BER_BVC("subany"), SLAP_INDEX_SUBSTR_ANY },
+ { BER_BVC("subfinal"), SLAP_INDEX_SUBSTR_FINAL },
+ { BER_BVC("sub"), SLAP_INDEX_SUBSTR_DEFAULT },
+ { BER_BVC("substr"), 0 },
+ { BER_BVC("notags"), SLAP_INDEX_NOTAGS },
+ { BER_BVC("nolang"), 0 }, /* backwards compat */
+ { BER_BVC("nosubtypes"), SLAP_INDEX_NOSUBTYPES },
+ { BER_BVNULL, 0 }
+};
+
+
+int slap_str2index( const char *str, slap_mask_t *idx )
+{
+ int i;
+
+ i = verb_to_mask( str, idxstr );
+ if ( BER_BVISNULL(&idxstr[i].word) ) return LDAP_OTHER;
+ while ( !idxstr[i].mask ) i--;
+ *idx = idxstr[i].mask;
+
+
+ return LDAP_SUCCESS;
+}
+
+void slap_index2bvlen( slap_mask_t idx, struct berval *bv )
+{
+ int i;
+
+ bv->bv_len = 0;
+
+ for ( i=0; !BER_BVISNULL( &idxstr[i].word ); i++ ) {
+ if ( !idxstr[i].mask ) continue;
+ if ( IS_SLAP_INDEX( idx, idxstr[i].mask )) {
+ if ( (idxstr[i].mask & SLAP_INDEX_SUBSTR) &&
+ ((idx & SLAP_INDEX_SUBSTR_DEFAULT) != idxstr[i].mask))
+ continue;
+ if ( bv->bv_len ) bv->bv_len++;
+ bv->bv_len += idxstr[i].word.bv_len;
+ }
+ }
+}
+
+/* caller must provide buffer space, after calling index2bvlen */
+void slap_index2bv( slap_mask_t idx, struct berval *bv )
+{
+ int i;
+ char *ptr;
+
+ if ( !bv->bv_len ) return;
+
+ ptr = bv->bv_val;
+ for ( i=0; !BER_BVISNULL( &idxstr[i].word ); i++ ) {
+ if ( !idxstr[i].mask ) continue;
+ if ( IS_SLAP_INDEX( idx, idxstr[i].mask )) {
+ if ( (idxstr[i].mask & SLAP_INDEX_SUBSTR) &&
+ ((idx & SLAP_INDEX_SUBSTR_DEFAULT) != idxstr[i].mask))
+ continue;
+ if ( ptr != bv->bv_val ) *ptr++ = ',';
+ ptr = lutil_strcopy( ptr, idxstr[i].word.bv_val );
+ }
+ }
+}
diff --git a/servers/slapd/init.c b/servers/slapd/init.c
new file mode 100644
index 0000000..591276b
--- /dev/null
+++ b/servers/slapd/init.c
@@ -0,0 +1,330 @@
+/* init.c - initialize various things */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "slap.h"
+#include "lber_pvt.h"
+
+#include "ldap_rq.h"
+
+/*
+ * read-only global variables or variables only written by the listener
+ * thread (after they are initialized) - no need to protect them with a mutex.
+ */
+int slap_debug = 0;
+
+#ifdef LDAP_DEBUG
+int ldap_syslog = LDAP_DEBUG_STATS;
+#else
+int ldap_syslog;
+#endif
+
+#ifdef LOG_DEBUG
+int ldap_syslog_level = LOG_DEBUG;
+#endif
+
+BerVarray default_referral = NULL;
+
+/*
+ * global variables that need mutex protection
+ */
+ldap_pvt_thread_pool_t connection_pool;
+int connection_pool_max = SLAP_MAX_WORKER_THREADS;
+int connection_pool_queues = 1;
+int slap_tool_thread_max = 1;
+
+slap_counters_t slap_counters, *slap_counters_list;
+
+static const char* slap_name = NULL;
+int slapMode = SLAP_UNDEFINED_MODE;
+
+int
+slap_init( int mode, const char *name )
+{
+ int rc;
+
+ assert( mode );
+
+ if ( slapMode != SLAP_UNDEFINED_MODE ) {
+ /* Make sure we write something to stderr */
+ slap_debug |= LDAP_DEBUG_NONE;
+ Debug( LDAP_DEBUG_ANY,
+ "%s init: init called twice (old=%d, new=%d)\n",
+ name, slapMode, mode );
+
+ return 1;
+ }
+
+ slapMode = mode;
+
+ slap_op_init();
+
+ ldap_pvt_thread_mutex_init( &slapd_init_mutex );
+ ldap_pvt_thread_cond_init( &slapd_init_cond );
+
+#ifdef SLAPD_MODULES
+ if ( module_init() != 0 ) {
+ slap_debug |= LDAP_DEBUG_NONE;
+ Debug( LDAP_DEBUG_ANY,
+ "%s: module_init failed\n",
+ name );
+ return 1;
+ }
+#endif
+
+ if ( slap_schema_init( ) != 0 ) {
+ slap_debug |= LDAP_DEBUG_NONE;
+ Debug( LDAP_DEBUG_ANY,
+ "%s: slap_schema_init failed\n",
+ name );
+ return 1;
+ }
+
+ if ( filter_init() != 0 ) {
+ slap_debug |= LDAP_DEBUG_NONE;
+ Debug( LDAP_DEBUG_ANY,
+ "%s: filter_init failed\n",
+ name );
+ return 1;
+ }
+
+ if ( entry_init() != 0 ) {
+ slap_debug |= LDAP_DEBUG_NONE;
+ Debug( LDAP_DEBUG_ANY,
+ "%s: entry_init failed\n",
+ name );
+ return 1;
+ }
+
+ switch ( slapMode & SLAP_MODE ) {
+ case SLAP_SERVER_MODE:
+ root_dse_init();
+
+ /* FALLTHRU */
+ case SLAP_TOOL_MODE:
+ Debug( LDAP_DEBUG_TRACE,
+ "%s init: initiated %s.\n", name,
+ (mode & SLAP_MODE) == SLAP_TOOL_MODE ? "tool" : "server" );
+
+ slap_name = name;
+
+ ldap_pvt_thread_pool_init_q( &connection_pool,
+ connection_pool_max, 0, connection_pool_queues);
+
+ slap_counters_init( &slap_counters );
+
+ ldap_pvt_thread_mutex_init( &slapd_rq.rq_mutex );
+ LDAP_STAILQ_INIT( &slapd_rq.task_list );
+ LDAP_STAILQ_INIT( &slapd_rq.run_list );
+
+ slap_passwd_init();
+
+ rc = slap_sasl_init();
+
+ if( rc == 0 ) {
+ rc = backend_init( );
+ }
+ if ( rc )
+ return rc;
+
+ break;
+
+ default:
+ slap_debug |= LDAP_DEBUG_NONE;
+ Debug( LDAP_DEBUG_ANY,
+ "%s init: undefined mode (%d).\n", name, mode );
+
+ rc = 1;
+ break;
+ }
+
+ if ( slap_controls_init( ) != 0 ) {
+ slap_debug |= LDAP_DEBUG_NONE;
+ Debug( LDAP_DEBUG_ANY,
+ "%s: slap_controls_init failed\n",
+ name );
+ return 1;
+ }
+
+ if ( frontend_init() ) {
+ slap_debug |= LDAP_DEBUG_NONE;
+ Debug( LDAP_DEBUG_ANY,
+ "%s: frontend_init failed\n",
+ name );
+ return 1;
+ }
+
+ if ( overlay_init() ) {
+ slap_debug |= LDAP_DEBUG_NONE;
+ Debug( LDAP_DEBUG_ANY,
+ "%s: overlay_init failed\n",
+ name );
+ return 1;
+ }
+
+ if ( glue_sub_init() ) {
+ slap_debug |= LDAP_DEBUG_NONE;
+ Debug( LDAP_DEBUG_ANY,
+ "%s: glue/subordinate init failed\n",
+ name );
+
+ return 1;
+ }
+
+ if ( acl_init() ) {
+ slap_debug |= LDAP_DEBUG_NONE;
+ Debug( LDAP_DEBUG_ANY,
+ "%s: acl_init failed\n",
+ name );
+ return 1;
+ }
+
+ return rc;
+}
+
+int slap_startup( Backend *be )
+{
+ int rc;
+ Debug( LDAP_DEBUG_TRACE,
+ "%s startup: initiated.\n",
+ slap_name );
+
+ rc = backend_startup( be );
+ if ( !rc && ( slapMode & SLAP_SERVER_MODE ))
+ slapMode |= SLAP_SERVER_RUNNING;
+ return rc;
+}
+
+int slap_shutdown( Backend *be )
+{
+ Debug( LDAP_DEBUG_TRACE,
+ "%s shutdown: initiated\n",
+ slap_name );
+
+ /* Make sure the pool stops now even if we did not start up fully */
+ ldap_pvt_thread_pool_close( &connection_pool, 1 );
+
+ /* let backends do whatever cleanup they need to do */
+ return backend_shutdown( be );
+}
+
+int slap_destroy(void)
+{
+ int rc;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "%s destroy: freeing system resources.\n",
+ slap_name );
+
+ if ( default_referral ) {
+ ber_bvarray_free( default_referral );
+ }
+
+ ldap_pvt_thread_pool_free( &connection_pool );
+
+ /* clear out any thread-keys for the main thread */
+ ldap_pvt_thread_pool_context_reset( ldap_pvt_thread_pool_context());
+
+ rc = backend_destroy();
+
+ slap_sasl_destroy();
+
+ /* rootdse destroy goes before entry_destroy()
+ * because it may use entry_free() */
+ root_dse_destroy();
+ entry_destroy();
+
+ switch ( slapMode & SLAP_MODE ) {
+ case SLAP_SERVER_MODE:
+ case SLAP_TOOL_MODE:
+ slap_counters_destroy( &slap_counters );
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "slap_destroy(): undefined mode (%d).\n", slapMode );
+
+ rc = 1;
+ break;
+
+ }
+
+ ldap_pvt_thread_mutex_destroy( &slapd_init_mutex );
+ ldap_pvt_thread_cond_destroy( &slapd_init_cond );
+
+ slap_op_destroy();
+
+ ldap_pvt_thread_destroy();
+
+ /* should destroy the above mutex */
+ return rc;
+}
+
+void slap_counters_init( slap_counters_t *sc )
+{
+ int i;
+
+ ldap_pvt_thread_mutex_init( &sc->sc_mutex );
+ ldap_pvt_mp_init( sc->sc_bytes );
+ ldap_pvt_mp_init( sc->sc_pdu );
+ ldap_pvt_mp_init( sc->sc_entries );
+ ldap_pvt_mp_init( sc->sc_refs );
+
+ ldap_pvt_mp_init( sc->sc_ops_initiated );
+ ldap_pvt_mp_init( sc->sc_ops_completed );
+
+ for ( i = 0; i < SLAP_OP_LAST; i++ ) {
+ ldap_pvt_mp_init( sc->sc_ops_initiated_[ i ] );
+ ldap_pvt_mp_init( sc->sc_ops_completed_[ i ] );
+ }
+}
+
+void slap_counters_destroy( slap_counters_t *sc )
+{
+ int i;
+
+ ldap_pvt_thread_mutex_destroy( &sc->sc_mutex );
+ ldap_pvt_mp_clear( sc->sc_bytes );
+ ldap_pvt_mp_clear( sc->sc_pdu );
+ ldap_pvt_mp_clear( sc->sc_entries );
+ ldap_pvt_mp_clear( sc->sc_refs );
+
+ ldap_pvt_mp_clear( sc->sc_ops_initiated );
+ ldap_pvt_mp_clear( sc->sc_ops_completed );
+
+ for ( i = 0; i < SLAP_OP_LAST; i++ ) {
+ ldap_pvt_mp_clear( sc->sc_ops_initiated_[ i ] );
+ ldap_pvt_mp_clear( sc->sc_ops_completed_[ i ] );
+ }
+}
+
diff --git a/servers/slapd/ldapsync.c b/servers/slapd/ldapsync.c
new file mode 100644
index 0000000..f922cd5
--- /dev/null
+++ b/servers/slapd/ldapsync.c
@@ -0,0 +1,525 @@
+/* ldapsync.c -- LDAP Content Sync Routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "lutil.h"
+#include "slap.h"
+#include "../../libraries/liblber/lber-int.h" /* get ber_strndup() */
+#include "lutil_ldap.h"
+
+struct slap_sync_cookie_s slap_sync_cookie =
+ LDAP_STAILQ_HEAD_INITIALIZER( slap_sync_cookie );
+
+void
+slap_compose_sync_cookie(
+ Operation *op,
+ struct berval *cookie,
+ BerVarray csn,
+ int rid,
+ int sid,
+ struct berval *delcsn )
+{
+ int len, numcsn = 0;
+
+ if ( csn ) {
+ for (; !BER_BVISNULL( &csn[numcsn] ); numcsn++);
+ }
+
+ if ( numcsn == 0 || rid == -1 ) {
+ char cookiestr[ LDAP_PVT_CSNSTR_BUFSIZE + 20 ];
+ if ( rid == -1 ) {
+ cookiestr[0] = '\0';
+ len = 0;
+ } else {
+ len = snprintf( cookiestr, sizeof( cookiestr ),
+ "rid=%03d", rid );
+ if ( sid >= 0 ) {
+ len += sprintf( cookiestr+len, ",sid=%03x", sid );
+ }
+ }
+ ber_str2bv_x( cookiestr, len, 1, cookie,
+ op ? op->o_tmpmemctx : NULL );
+ } else {
+ char *ptr;
+ int i;
+
+ len = 0;
+ for ( i=0; i<numcsn; i++)
+ len += csn[i].bv_len + 1;
+ if ( delcsn && !BER_BVISEMPTY(delcsn) )
+ len += STRLENOF(",delcsn=") + delcsn->bv_len;
+
+ len += STRLENOF("rid=123,csn=");
+ if ( sid >= 0 )
+ len += STRLENOF("sid=xxx,");
+
+ cookie->bv_val = slap_sl_malloc( len, op ? op->o_tmpmemctx : NULL );
+
+ len = sprintf( cookie->bv_val, "rid=%03d,", rid );
+ ptr = cookie->bv_val + len;
+ if ( sid >= 0 ) {
+ ptr += sprintf( ptr, "sid=%03x,", sid );
+ }
+ ptr = lutil_strcopy( ptr, "csn=" );
+ for ( i=0; i<numcsn; i++) {
+ ptr = lutil_strncopy( ptr, csn[i].bv_val, csn[i].bv_len );
+ *ptr++ = ';';
+ }
+ ptr--;
+ if ( delcsn && !BER_BVISEMPTY(delcsn) ) {
+ ptr = lutil_strcopy( ptr, ",delcsn=" );
+ ptr = lutil_strncopy( ptr, delcsn->bv_val, delcsn->bv_len );
+ }
+ *ptr = '\0';
+ cookie->bv_len = ptr - cookie->bv_val;
+ }
+}
+
+void
+slap_sync_cookie_free(
+ struct sync_cookie *cookie,
+ int free_cookie
+)
+{
+ if ( cookie == NULL )
+ return;
+
+ if ( cookie->sids ) {
+ ch_free( cookie->sids );
+ cookie->sids = NULL;
+ }
+
+ if ( cookie->ctxcsn ) {
+ ber_bvarray_free( cookie->ctxcsn );
+ cookie->ctxcsn = NULL;
+ }
+ cookie->numcsns = 0;
+ if ( !BER_BVISNULL( &cookie->octet_str )) {
+ ch_free( cookie->octet_str.bv_val );
+ BER_BVZERO( &cookie->octet_str );
+ }
+
+ if ( !BER_BVISNULL( &cookie->delcsn )) {
+ ch_free( cookie->delcsn.bv_val );
+ BER_BVZERO( &cookie->delcsn );
+ }
+
+ if ( free_cookie ) {
+ ch_free( cookie );
+ }
+
+ return;
+}
+
+int
+slap_parse_csn_sid( struct berval *csnp )
+{
+ char *p, *q;
+ struct berval csn = *csnp;
+ int i;
+
+ p = ber_bvchr( &csn, '#' );
+ if ( !p )
+ return -1;
+ p++;
+ csn.bv_len -= p - csn.bv_val;
+ csn.bv_val = p;
+
+ p = ber_bvchr( &csn, '#' );
+ if ( !p )
+ return -1;
+ p++;
+ csn.bv_len -= p - csn.bv_val;
+ csn.bv_val = p;
+
+ q = ber_bvchr( &csn, '#' );
+ if ( !q )
+ return -1;
+
+ csn.bv_len = q - p;
+
+ i = strtol( p, &q, 16 );
+ if ( p == q || q != p + csn.bv_len || i < 0 || i > SLAP_SYNC_SID_MAX ) {
+ i = -1;
+ }
+
+ return i;
+}
+
+int *
+slap_parse_csn_sids( BerVarray csns, int numcsns, void *memctx )
+{
+ int i, *ret;
+
+ ret = slap_sl_malloc( numcsns * sizeof(int), memctx );
+ for ( i=0; i<numcsns; i++ ) {
+ ret[i] = slap_parse_csn_sid( &csns[i] );
+ }
+ return ret;
+}
+
+static slap_mr_match_func sidsort_cmp;
+
+static const MatchingRule sidsort_mr = {
+ { 0 },
+ NULL,
+ { 0 },
+ { 0 },
+ 0,
+ NULL, NULL, NULL, sidsort_cmp
+};
+static const AttributeType sidsort_at = {
+ { 0 },
+ { 0 },
+ NULL, NULL, (MatchingRule *)&sidsort_mr,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, SLAP_AT_SORTED_VAL
+};
+static const AttributeDescription sidsort_ad = {
+ NULL,
+ (AttributeType *)&sidsort_at
+};
+
+static int
+sidsort_cmp(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *b1,
+ void *v2 )
+{
+ struct berval *b2 = v2;
+ *matchp = b1->bv_len - b2->bv_len;
+ return LDAP_SUCCESS;
+}
+
+/* sort CSNs by SID. Use a fake Attribute with our own
+ * syntax and matching rule, which sorts the nvals by
+ * bv_len order. Stuff our sids into the bv_len.
+ */
+int
+slap_sort_csn_sids( BerVarray csns, int *sids, int numcsns, void *memctx )
+{
+ Attribute a;
+ const char *text;
+ int i, rc;
+
+ a.a_desc = (AttributeDescription *)&sidsort_ad;
+ a.a_nvals = slap_sl_malloc( numcsns * sizeof(struct berval), memctx );
+ for ( i=0; i<numcsns; i++ ) {
+ a.a_nvals[i].bv_len = sids[i];
+ a.a_nvals[i].bv_val = NULL;
+ }
+ a.a_vals = csns;
+ a.a_numvals = numcsns;
+ a.a_flags = 0;
+ rc = slap_sort_vals( (Modifications *)&a, &text, &i, memctx );
+ for ( i=0; i<numcsns; i++ )
+ sids[i] = a.a_nvals[i].bv_len;
+ slap_sl_free( a.a_nvals, memctx );
+ return rc;
+}
+
+void
+slap_insert_csn_sids(
+ struct sync_cookie *ck,
+ int pos,
+ int sid,
+ struct berval *csn
+)
+{
+ int i;
+ ck->numcsns++;
+ ck->ctxcsn = ch_realloc( ck->ctxcsn,
+ (ck->numcsns+1) * sizeof(struct berval));
+ BER_BVZERO( &ck->ctxcsn[ck->numcsns] );
+ ck->sids = ch_realloc( ck->sids, ck->numcsns * sizeof(int));
+ for ( i = ck->numcsns-1; i > pos; i-- ) {
+ ck->ctxcsn[i] = ck->ctxcsn[i-1];
+ ck->sids[i] = ck->sids[i-1];
+ }
+ ck->sids[i] = sid;
+ ber_dupbv( &ck->ctxcsn[i], csn );
+}
+
+int
+slap_parse_sync_cookie(
+ struct sync_cookie *cookie,
+ void *memctx
+)
+{
+ char *csn_ptr;
+ char *csn_str;
+ char *cval;
+ char *next, *end;
+ AttributeDescription *ad = slap_schema.si_ad_entryCSN;
+
+ if ( cookie == NULL )
+ return -1;
+
+ if ( cookie->octet_str.bv_len <= STRLENOF( "rid=" ) )
+ return -1;
+
+ cookie->rid = -1;
+ cookie->sid = -1;
+ cookie->ctxcsn = NULL;
+ cookie->sids = NULL;
+ cookie->numcsns = 0;
+ BER_BVZERO( &cookie->delcsn );
+
+ end = cookie->octet_str.bv_val + cookie->octet_str.bv_len;
+
+ for ( next=cookie->octet_str.bv_val; next < end; ) {
+ if ( !strncmp( next, "rid=", STRLENOF("rid=") )) {
+ char *rid_ptr = next;
+ cookie->rid = strtol( &rid_ptr[ STRLENOF( "rid=" ) ], &next, 10 );
+ if ( next == rid_ptr ||
+ next > end ||
+ ( *next && *next != ',' ) ||
+ cookie->rid < 0 ||
+ cookie->rid > SLAP_SYNC_RID_MAX )
+ {
+ return -1;
+ }
+ if ( *next == ',' ) {
+ next++;
+ }
+ if ( !ad ) {
+ break;
+ }
+ continue;
+ }
+ if ( !strncmp( next, "sid=", STRLENOF("sid=") )) {
+ char *sid_ptr = next;
+ sid_ptr = next;
+ cookie->sid = strtol( &sid_ptr[ STRLENOF( "sid=" ) ], &next, 16 );
+ if ( next == sid_ptr ||
+ next > end ||
+ ( *next && *next != ',' ) ||
+ cookie->sid < 0 ||
+ cookie->sid > SLAP_SYNC_SID_MAX )
+ {
+ return -1;
+ }
+ if ( *next == ',' ) {
+ next++;
+ }
+ continue;
+ }
+ if ( !strncmp( next, "csn=", STRLENOF("csn=") )) {
+ struct berval stamp;
+
+ next += STRLENOF("csn=");
+ while ( next < end ) {
+ csn_str = next;
+ csn_ptr = strchr( csn_str, '#' );
+ if ( !csn_ptr || csn_ptr > end )
+ break;
+ /* ad will be NULL when called from main. we just
+ * want to parse the rid then. But we still iterate
+ * through the string to find the end.
+ */
+ cval = strchr( csn_ptr, ';' );
+ if ( !cval )
+ cval = strchr(csn_ptr, ',' );
+ if ( cval )
+ stamp.bv_len = cval - csn_str;
+ else
+ stamp.bv_len = end - csn_str;
+ if ( ad ) {
+ struct berval bv;
+ stamp.bv_val = csn_str;
+ if ( ad->ad_type->sat_syntax->ssyn_validate(
+ ad->ad_type->sat_syntax, &stamp ) != LDAP_SUCCESS )
+ break;
+ if ( ad->ad_type->sat_equality->smr_normalize(
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ ad->ad_type->sat_syntax,
+ ad->ad_type->sat_equality,
+ &stamp, &bv, memctx ) != LDAP_SUCCESS )
+ break;
+ ber_bvarray_add_x( &cookie->ctxcsn, &bv, memctx );
+ cookie->numcsns++;
+ }
+ if ( cval ) {
+ next = cval + 1;
+ if ( *cval != ';' )
+ break;
+ } else {
+ next = end;
+ break;
+ }
+ }
+ continue;
+ }
+ if ( !strncmp( next, "delcsn=", STRLENOF("delcsn=") )) {
+ struct berval stamp;
+
+ next += STRLENOF("delcsn=");
+ while ( next < end ) {
+ csn_str = next;
+ csn_ptr = strchr( csn_str, '#' );
+ if ( !csn_ptr || csn_ptr > end )
+ break;
+ /* ad will be NULL when called from main. we just
+ * want to parse the rid then. But we still iterate
+ * through the string to find the end.
+ */
+ cval = strchr( csn_ptr, ';' );
+ if ( !cval )
+ cval = strchr(csn_ptr, ',' );
+ if ( cval )
+ stamp.bv_len = cval - csn_str;
+ else
+ stamp.bv_len = end - csn_str;
+ if ( ad ) {
+ struct berval bv;
+ stamp.bv_val = csn_str;
+ if ( ad->ad_type->sat_syntax->ssyn_validate(
+ ad->ad_type->sat_syntax, &stamp ) != LDAP_SUCCESS )
+ break;
+ if ( ad->ad_type->sat_equality->smr_normalize(
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ ad->ad_type->sat_syntax,
+ ad->ad_type->sat_equality,
+ &stamp, &bv, memctx ) != LDAP_SUCCESS )
+ break;
+ cookie->delcsn = bv;
+ }
+ if ( cval ) {
+ next = cval + 1;
+ } else {
+ next = end;
+ }
+ break;
+ }
+ continue;
+ }
+ next++;
+ }
+ if ( cookie->numcsns ) {
+ cookie->sids = slap_parse_csn_sids( cookie->ctxcsn, cookie->numcsns,
+ memctx );
+ if ( cookie->numcsns > 1 )
+ slap_sort_csn_sids( cookie->ctxcsn, cookie->sids, cookie->numcsns, memctx );
+ }
+ return 0;
+}
+
+/* count the numcsns and regenerate the list of SIDs in a recomposed cookie */
+void
+slap_reparse_sync_cookie(
+ struct sync_cookie *cookie,
+ void *memctx )
+{
+ if ( cookie->ctxcsn ) {
+ for (; !BER_BVISNULL( &cookie->ctxcsn[cookie->numcsns] ); cookie->numcsns++);
+ }
+ if ( cookie->numcsns ) {
+ cookie->sids = slap_parse_csn_sids( cookie->ctxcsn, cookie->numcsns, NULL );
+ if ( cookie->numcsns > 1 )
+ slap_sort_csn_sids( cookie->ctxcsn, cookie->sids, cookie->numcsns, memctx );
+ }
+}
+
+int
+slap_init_sync_cookie_ctxcsn(
+ struct sync_cookie *cookie
+)
+{
+ char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE + 4 ];
+ struct berval octet_str = BER_BVNULL;
+ struct berval ctxcsn = BER_BVNULL;
+
+ if ( cookie == NULL )
+ return -1;
+
+ octet_str.bv_len = snprintf( csnbuf, LDAP_PVT_CSNSTR_BUFSIZE + 4,
+ "csn=%4d%02d%02d%02d%02d%02dZ#%06x#%02x#%06x",
+ 1900, 1, 1, 0, 0, 0, 0, 0, 0 );
+ octet_str.bv_val = csnbuf;
+ ch_free( cookie->octet_str.bv_val );
+ ber_dupbv( &cookie->octet_str, &octet_str );
+
+ ctxcsn.bv_val = octet_str.bv_val + 4;
+ ctxcsn.bv_len = octet_str.bv_len - 4;
+ cookie->ctxcsn = NULL;
+ value_add_one( &cookie->ctxcsn, &ctxcsn );
+ cookie->numcsns = 1;
+ cookie->sid = -1;
+ BER_BVZERO( &cookie->delcsn );
+
+ return 0;
+}
+
+struct sync_cookie *
+slap_dup_sync_cookie(
+ struct sync_cookie *dst,
+ struct sync_cookie *src
+)
+{
+ struct sync_cookie *new;
+ int i;
+
+ if ( src == NULL )
+ return NULL;
+
+ if ( dst ) {
+ ber_bvarray_free( dst->ctxcsn );
+ dst->ctxcsn = NULL;
+ dst->sids = NULL;
+ ch_free( dst->octet_str.bv_val );
+ BER_BVZERO( &dst->octet_str );
+ new = dst;
+ } else {
+ new = ( struct sync_cookie * )
+ ch_calloc( 1, sizeof( struct sync_cookie ));
+ }
+
+ new->rid = src->rid;
+ new->sid = src->sid;
+ new->numcsns = src->numcsns;
+
+ if ( src->numcsns ) {
+ if ( ber_bvarray_dup_x( &new->ctxcsn, src->ctxcsn, NULL )) {
+ if ( !dst ) {
+ ch_free( new );
+ }
+ return NULL;
+ }
+ new->sids = ch_malloc( src->numcsns * sizeof(int) );
+ for (i=0; i<src->numcsns; i++)
+ new->sids[i] = src->sids[i];
+ }
+
+ if ( !BER_BVISNULL( &src->delcsn )) {
+ ber_dupbv( &new->delcsn, &src->delcsn );
+ }
+
+ if ( !BER_BVISNULL( &src->octet_str )) {
+ ber_dupbv( &new->octet_str, &src->octet_str );
+ }
+
+ return new;
+}
+
diff --git a/servers/slapd/limits.c b/servers/slapd/limits.c
new file mode 100644
index 0000000..2e4d051
--- /dev/null
+++ b/servers/slapd/limits.c
@@ -0,0 +1,1355 @@
+/* limits.c - routines to handle regex-based size and time limits */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/regex.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "lutil.h"
+
+/* define to get an error if requesting limit higher than hard */
+#undef ABOVE_HARD_LIMIT_IS_ERROR
+
+static const struct berval lmpats[] = {
+ BER_BVC( "base" ),
+ BER_BVC( "base" ),
+ BER_BVC( "onelevel" ),
+ BER_BVC( "subtree" ),
+ BER_BVC( "children" ),
+ BER_BVC( "regex" ),
+ BER_BVC( "anonymous" ),
+ BER_BVC( "users" ),
+ BER_BVC( "*" )
+};
+
+#ifdef LDAP_DEBUG
+static const char *const dn_source[2] = { "DN", "DN.THIS" };
+static const char *const lmpats_out[] = {
+ "UNDEFINED",
+ "EXACT",
+ "ONELEVEL",
+ "SUBTREE",
+ "CHILDREN",
+ "REGEX",
+ "ANONYMOUS",
+ "USERS",
+ "ANY"
+};
+
+static const char *
+limits2str( unsigned i )
+{
+ return i < (sizeof( lmpats_out ) / sizeof( lmpats_out[0] ))
+ ? lmpats_out[i] : "UNKNOWN";
+}
+#endif /* LDAP_DEBUG */
+
+static int
+limits_get(
+ Operation *op,
+ struct slap_limits_set **limit
+)
+{
+ static struct berval empty_dn = BER_BVC( "" );
+ struct slap_limits **lm;
+ struct berval *ndns[2];
+
+ assert( op != NULL );
+ assert( limit != NULL );
+
+ ndns[0] = &op->o_ndn;
+ ndns[1] = &op->o_req_ndn;
+
+ Debug( LDAP_DEBUG_TRACE, "==> limits_get: %s self=\"%s\" this=\"%s\"\n",
+ op->o_log_prefix,
+ BER_BVISNULL( ndns[0] ) ? "[anonymous]" : ndns[0]->bv_val,
+ BER_BVISNULL( ndns[1] ) ? "" : ndns[1]->bv_val );
+ /*
+ * default values
+ */
+ *limit = &op->o_bd->be_def_limit;
+
+ if ( op->o_bd->be_limits == NULL ) {
+ return( 0 );
+ }
+
+ for ( lm = op->o_bd->be_limits; lm[0] != NULL; lm++ ) {
+ unsigned style = lm[0]->lm_flags & SLAP_LIMITS_MASK;
+ unsigned type = lm[0]->lm_flags & SLAP_LIMITS_TYPE_MASK;
+ unsigned isthis = type == SLAP_LIMITS_TYPE_THIS;
+ struct berval *ndn = ndns[isthis];
+
+ if ( style == SLAP_LIMITS_ANY )
+ goto found_any;
+
+ if ( BER_BVISEMPTY( ndn ) ) {
+ if ( style == SLAP_LIMITS_ANONYMOUS )
+ goto found_nodn;
+ if ( !isthis )
+ continue;
+ ndn = &empty_dn;
+ }
+
+ switch ( style ) {
+ case SLAP_LIMITS_EXACT:
+ if ( type == SLAP_LIMITS_TYPE_GROUP ) {
+ int rc = backend_group( op, NULL,
+ &lm[0]->lm_pat, ndn,
+ lm[0]->lm_group_oc,
+ lm[0]->lm_group_ad );
+ if ( rc == 0 ) {
+ goto found_group;
+ }
+ } else {
+ if ( dn_match( &lm[0]->lm_pat, ndn ) ) {
+ goto found_dn;
+ }
+ }
+ break;
+
+ case SLAP_LIMITS_ONE:
+ case SLAP_LIMITS_SUBTREE:
+ case SLAP_LIMITS_CHILDREN: {
+ ber_len_t d;
+
+ /* ndn shorter than lm_pat */
+ if ( ndn->bv_len < lm[0]->lm_pat.bv_len ) {
+ break;
+ }
+ d = ndn->bv_len - lm[0]->lm_pat.bv_len;
+
+ if ( d == 0 ) {
+ /* allow exact match for SUBTREE only */
+ if ( style != SLAP_LIMITS_SUBTREE ) {
+ break;
+ }
+ } else {
+ /* check for unescaped rdn separator */
+ if ( !DN_SEPARATOR( ndn->bv_val[d - 1] ) ) {
+ break;
+ }
+ }
+
+ /* check that ndn ends with lm_pat */
+ if ( strcmp( lm[0]->lm_pat.bv_val, &ndn->bv_val[d] ) != 0 ) {
+ break;
+ }
+
+ /* in case of ONE, require exactly one rdn below lm_pat */
+ if ( style == SLAP_LIMITS_ONE ) {
+ if ( dn_rdnlen( NULL, ndn ) != d - 1 ) {
+ break;
+ }
+ }
+
+ goto found_dn;
+ }
+
+ case SLAP_LIMITS_REGEX:
+ if ( regexec( &lm[0]->lm_regex, ndn->bv_val, 0, NULL, 0 ) == 0 ) {
+ goto found_dn;
+ }
+ break;
+
+ case SLAP_LIMITS_ANONYMOUS:
+ break;
+
+ case SLAP_LIMITS_USERS:
+ found_nodn:
+ Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=%s match=%s\n",
+ dn_source[isthis], limits2str( style ) );
+ found_any:
+ *limit = &lm[0]->lm_limits;
+ return( 0 );
+
+ found_dn:
+ Debug( LDAP_DEBUG_TRACE,
+ "<== limits_get: type=%s match=%s dn=\"%s\"\n",
+ dn_source[isthis], limits2str( style ), lm[0]->lm_pat.bv_val );
+ *limit = &lm[0]->lm_limits;
+ return( 0 );
+
+ found_group:
+ Debug( LDAP_DEBUG_TRACE, "<== limits_get: type=GROUP match=EXACT "
+ "dn=\"%s\" oc=\"%s\" ad=\"%s\"\n",
+ lm[0]->lm_pat.bv_val,
+ lm[0]->lm_group_oc->soc_cname.bv_val,
+ lm[0]->lm_group_ad->ad_cname.bv_val );
+ *limit = &lm[0]->lm_limits;
+ return( 0 );
+
+ default:
+ assert( 0 ); /* unreachable */
+ return( -1 );
+ }
+ }
+
+ return( 0 );
+}
+
+static int
+limits_add(
+ Backend *be,
+ unsigned flags,
+ const char *pattern,
+ ObjectClass *group_oc,
+ AttributeDescription *group_ad,
+ struct slap_limits_set *limit
+)
+{
+ int i;
+ struct slap_limits *lm;
+ unsigned type, style;
+
+ assert( be != NULL );
+ assert( limit != NULL );
+
+ type = flags & SLAP_LIMITS_TYPE_MASK;
+ style = flags & SLAP_LIMITS_MASK;
+
+ switch ( style ) {
+ case SLAP_LIMITS_ANONYMOUS:
+ case SLAP_LIMITS_USERS:
+ case SLAP_LIMITS_ANY:
+ /* For these styles, type == 0 (SLAP_LIMITS_TYPE_SELF). */
+ for ( i = 0; be->be_limits && be->be_limits[ i ]; i++ ) {
+ if ( be->be_limits[ i ]->lm_flags == style ) {
+ return( -1 );
+ }
+ }
+ break;
+ }
+
+
+ lm = ( struct slap_limits * )ch_calloc( sizeof( struct slap_limits ), 1 );
+
+ switch ( style ) {
+ case SLAP_LIMITS_UNDEFINED:
+ style = SLAP_LIMITS_EXACT;
+ /* continue to next cases */
+ case SLAP_LIMITS_EXACT:
+ case SLAP_LIMITS_ONE:
+ case SLAP_LIMITS_SUBTREE:
+ case SLAP_LIMITS_CHILDREN:
+ {
+ int rc;
+ struct berval bv;
+
+ ber_str2bv( pattern, 0, 0, &bv );
+
+ rc = dnNormalize( 0, NULL, NULL, &bv, &lm->lm_pat, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ ch_free( lm );
+ return( -1 );
+ }
+ }
+ break;
+
+ case SLAP_LIMITS_REGEX:
+ ber_str2bv( pattern, 0, 1, &lm->lm_pat );
+ if ( regcomp( &lm->lm_regex, lm->lm_pat.bv_val,
+ REG_EXTENDED | REG_ICASE ) ) {
+ free( lm->lm_pat.bv_val );
+ ch_free( lm );
+ return( -1 );
+ }
+ break;
+
+ case SLAP_LIMITS_ANONYMOUS:
+ case SLAP_LIMITS_USERS:
+ case SLAP_LIMITS_ANY:
+ BER_BVZERO( &lm->lm_pat );
+ break;
+ }
+
+ switch ( type ) {
+ case SLAP_LIMITS_TYPE_GROUP:
+ assert( group_oc != NULL );
+ assert( group_ad != NULL );
+ lm->lm_group_oc = group_oc;
+ lm->lm_group_ad = group_ad;
+ break;
+ }
+
+ lm->lm_flags = style | type;
+ lm->lm_limits = *limit;
+
+ i = 0;
+ if ( be->be_limits != NULL ) {
+ for ( ; be->be_limits[i]; i++ );
+ }
+
+ be->be_limits = ( struct slap_limits ** )ch_realloc( be->be_limits,
+ sizeof( struct slap_limits * ) * ( i + 2 ) );
+ be->be_limits[i] = lm;
+ be->be_limits[i+1] = NULL;
+
+ return( 0 );
+}
+
+#define STRSTART( s, m ) (strncasecmp( s, m, STRLENOF( "" m "" )) == 0)
+
+int
+limits_parse(
+ Backend *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv
+)
+{
+ int flags = SLAP_LIMITS_UNDEFINED;
+ char *pattern;
+ struct slap_limits_set limit;
+ int i, rc = 0;
+ ObjectClass *group_oc = NULL;
+ AttributeDescription *group_ad = NULL;
+
+ assert( be != NULL );
+
+ if ( argc < 3 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s : line %d: missing arg(s) in "
+ "\"limits <pattern> <limits>\" line.\n",
+ fname, lineno );
+ return( -1 );
+ }
+
+ limit = be->be_def_limit;
+
+ /*
+ * syntax:
+ *
+ * "limits" <pattern> <limit> [ ... ]
+ *
+ *
+ * <pattern>:
+ *
+ * "anonymous"
+ * "users"
+ * [ "dn" [ "." { "this" | "self" } ] [ "." { "exact" | "base" |
+ * "onelevel" | "subtree" | "children" | "regex" | "anonymous" } ]
+ * "=" ] <dn pattern>
+ *
+ * Note:
+ * "this" is the baseobject, "self" (the default) is the bound DN
+ * "exact" and "base" are the same (exact match);
+ * "onelevel" means exactly one rdn below, NOT including pattern
+ * "subtree" means any rdn below, including pattern
+ * "children" means any rdn below, NOT including pattern
+ *
+ * "anonymous" may be deprecated in favour
+ * of the pattern = "anonymous" form
+ *
+ * "group[/objectClass[/attributeType]]" "=" "<dn pattern>"
+ *
+ * <limit>:
+ *
+ * "time" [ "." { "soft" | "hard" } ] "=" <integer>
+ *
+ * "size" [ "." { "soft" | "hard" | "unchecked" } ] "=" <integer>
+ */
+
+ pattern = argv[1];
+ if ( strcmp( pattern, "*" ) == 0) {
+ flags = SLAP_LIMITS_ANY;
+
+ } else if ( strcasecmp( pattern, "anonymous" ) == 0 ) {
+ flags = SLAP_LIMITS_ANONYMOUS;
+
+ } else if ( strcasecmp( pattern, "users" ) == 0 ) {
+ flags = SLAP_LIMITS_USERS;
+
+ } else if ( STRSTART( pattern, "dn" ) ) {
+ pattern += STRLENOF( "dn" );
+ flags = SLAP_LIMITS_TYPE_SELF;
+ if ( pattern[0] == '.' ) {
+ pattern++;
+ if ( STRSTART( pattern, "this" ) ) {
+ flags = SLAP_LIMITS_TYPE_THIS;
+ pattern += STRLENOF( "this" );
+ } else if ( STRSTART( pattern, "self" ) ) {
+ pattern += STRLENOF( "self" );
+ } else {
+ goto got_dn_dot;
+ }
+ }
+ if ( pattern[0] == '.' ) {
+ pattern++;
+ got_dn_dot:
+ if ( STRSTART( pattern, "exact" ) ) {
+ flags |= SLAP_LIMITS_EXACT;
+ pattern += STRLENOF( "exact" );
+
+ } else if ( STRSTART( pattern, "base" ) ) {
+ flags |= SLAP_LIMITS_BASE;
+ pattern += STRLENOF( "base" );
+
+ } else if ( STRSTART( pattern, "one" ) ) {
+ flags |= SLAP_LIMITS_ONE;
+ pattern += STRLENOF( "one" );
+ if ( STRSTART( pattern, "level" ) ) {
+ pattern += STRLENOF( "level" );
+
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "%s : line %d: deprecated \"one\" style "
+ "\"limits <pattern> <limits>\" line; "
+ "use \"onelevel\" instead.\n", fname, lineno );
+ }
+
+ } else if ( STRSTART( pattern, "sub" ) ) {
+ flags |= SLAP_LIMITS_SUBTREE;
+ pattern += STRLENOF( "sub" );
+ if ( STRSTART( pattern, "tree" ) ) {
+ pattern += STRLENOF( "tree" );
+
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "%s : line %d: deprecated \"sub\" style "
+ "\"limits <pattern> <limits>\" line; "
+ "use \"subtree\" instead.\n", fname, lineno );
+ }
+
+ } else if ( STRSTART( pattern, "children" ) ) {
+ flags |= SLAP_LIMITS_CHILDREN;
+ pattern += STRLENOF( "children" );
+
+ } else if ( STRSTART( pattern, "regex" ) ) {
+ flags |= SLAP_LIMITS_REGEX;
+ pattern += STRLENOF( "regex" );
+
+ /*
+ * this could be deprecated in favour
+ * of the pattern = "anonymous" form
+ */
+ } else if ( STRSTART( pattern, "anonymous" )
+ && flags == SLAP_LIMITS_TYPE_SELF )
+ {
+ flags = SLAP_LIMITS_ANONYMOUS;
+ pattern = NULL;
+
+ } else {
+ /* force error below */
+ if ( *pattern == '=' )
+ --pattern;
+ }
+ }
+
+ /* pre-check the data */
+ if ( pattern != NULL ) {
+ if ( pattern[0] != '=' ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s : line %d: %s in "
+ "\"dn[.{this|self}][.{exact|base"
+ "|onelevel|subtree|children|regex"
+ "|anonymous}]=<pattern>\" in "
+ "\"limits <pattern> <limits>\" line.\n",
+ fname, lineno,
+ isalnum( (unsigned char)pattern[0] )
+ ? "unknown DN modifier" : "missing '='" );
+ return( -1 );
+ }
+
+ /* skip '=' (required) */
+ pattern++;
+
+ /* trim obvious cases */
+ if ( strcmp( pattern, "*" ) == 0 ) {
+ flags = SLAP_LIMITS_ANY;
+ pattern = NULL;
+
+ } else if ( (flags & SLAP_LIMITS_MASK) == SLAP_LIMITS_REGEX
+ && strcmp( pattern, ".*" ) == 0 ) {
+ flags = SLAP_LIMITS_ANY;
+ pattern = NULL;
+ }
+ }
+
+ } else if (STRSTART( pattern, "group" ) ) {
+ pattern += STRLENOF( "group" );
+
+ if ( pattern[0] == '/' ) {
+ struct berval oc, ad;
+
+ oc.bv_val = pattern + 1;
+ pattern = strchr( pattern, '=' );
+ if ( pattern == NULL ) {
+ return -1;
+ }
+
+ ad.bv_val = strchr( oc.bv_val, '/' );
+ if ( ad.bv_val != NULL ) {
+ const char *text = NULL;
+
+ oc.bv_len = ad.bv_val - oc.bv_val;
+
+ ad.bv_val++;
+ ad.bv_len = pattern - ad.bv_val;
+ rc = slap_bv2ad( &ad, &group_ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ goto no_ad;
+ }
+
+ } else {
+ oc.bv_len = pattern - oc.bv_val;
+ }
+
+ group_oc = oc_bvfind( &oc );
+ if ( group_oc == NULL ) {
+ goto no_oc;
+ }
+ }
+
+ if ( group_oc == NULL ) {
+ group_oc = oc_find( SLAPD_GROUP_CLASS );
+ if ( group_oc == NULL ) {
+no_oc:;
+ return( -1 );
+ }
+ }
+
+ if ( group_ad == NULL ) {
+ const char *text = NULL;
+
+ rc = slap_str2ad( SLAPD_GROUP_ATTR, &group_ad, &text );
+
+ if ( rc != LDAP_SUCCESS ) {
+no_ad:;
+ return( -1 );
+ }
+ }
+
+ flags = SLAP_LIMITS_TYPE_GROUP | SLAP_LIMITS_EXACT;
+
+ if ( pattern[0] != '=' ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s : line %d: missing '=' in "
+ "\"group[/objectClass[/attributeType]]"
+ "=<pattern>\" in "
+ "\"limits <pattern> <limits>\" line.\n",
+ fname, lineno );
+ return( -1 );
+ }
+
+ /* skip '=' (required) */
+ pattern++;
+ }
+
+ /* get the limits */
+ for ( i = 2; i < argc; i++ ) {
+ if ( limits_parse_one( argv[i], &limit ) ) {
+
+ Debug( LDAP_DEBUG_ANY,
+ "%s : line %d: unknown limit values \"%s\" in "
+ "\"limits <pattern> <limits>\" line.\n",
+ fname, lineno, argv[i] );
+
+ return( 1 );
+ }
+ }
+
+ /*
+ * sanity checks ...
+ *
+ * FIXME: add warnings?
+ */
+ if ( limit.lms_t_hard > 0 &&
+ ( limit.lms_t_hard < limit.lms_t_soft
+ || limit.lms_t_soft == -1 ) ) {
+ limit.lms_t_hard = limit.lms_t_soft;
+ }
+
+ if ( limit.lms_s_hard > 0 &&
+ ( limit.lms_s_hard < limit.lms_s_soft
+ || limit.lms_s_soft == -1 ) ) {
+ limit.lms_s_hard = limit.lms_s_soft;
+ }
+
+ /*
+ * defaults ...
+ *
+ * lms_t_hard:
+ * -1 => no limits
+ * 0 => same as soft
+ * > 0 => limit (in seconds)
+ *
+ * lms_s_hard:
+ * -1 => no limits
+ * 0 0> same as soft
+ * > 0 => limit (in entries)
+ *
+ * lms_s_pr_total:
+ * -2 => disable the control
+ * -1 => no limits
+ * 0 => same as soft
+ * > 0 => limit (in entries)
+ *
+ * lms_s_pr:
+ * -1 => no limits
+ * 0 => no limits?
+ * > 0 => limit size (in entries)
+ */
+ if ( limit.lms_s_pr_total > 0 &&
+ limit.lms_s_pr > limit.lms_s_pr_total ) {
+ limit.lms_s_pr = limit.lms_s_pr_total;
+ }
+
+ rc = limits_add( be, flags, pattern, group_oc, group_ad, &limit );
+ if ( rc ) {
+
+ Debug( LDAP_DEBUG_ANY,
+ "%s : line %d: unable to add limit in "
+ "\"limits <pattern> <limits>\" line.\n",
+ fname, lineno );
+ }
+
+ return( rc );
+}
+
+int
+limits_parse_one(
+ const char *arg,
+ struct slap_limits_set *limit
+)
+{
+ assert( arg != NULL );
+ assert( limit != NULL );
+
+ if ( STRSTART( arg, "time" ) ) {
+ arg += STRLENOF( "time" );
+
+ if ( arg[0] == '.' ) {
+ arg++;
+ if ( STRSTART( arg, "soft=" ) ) {
+ arg += STRLENOF( "soft=" );
+ if ( strcasecmp( arg, "unlimited" ) == 0
+ || strcasecmp( arg, "none" ) == 0 )
+ {
+ limit->lms_t_soft = -1;
+
+ } else {
+ int soft;
+
+ if ( lutil_atoi( &soft, arg ) != 0 || soft < -1 ) {
+ return( 1 );
+ }
+
+ if ( soft == -1 ) {
+ /* FIXME: use "unlimited" instead; issue warning? */
+ }
+
+ limit->lms_t_soft = soft;
+ }
+
+ } else if ( STRSTART( arg, "hard=" ) ) {
+ arg += STRLENOF( "hard=" );
+ if ( strcasecmp( arg, "soft" ) == 0 ) {
+ limit->lms_t_hard = 0;
+
+ } else if ( strcasecmp( arg, "unlimited" ) == 0
+ || strcasecmp( arg, "none" ) == 0 )
+ {
+ limit->lms_t_hard = -1;
+
+ } else {
+ int hard;
+
+ if ( lutil_atoi( &hard, arg ) != 0 || hard < -1 ) {
+ return( 1 );
+ }
+
+ if ( hard == -1 ) {
+ /* FIXME: use "unlimited" instead */
+ }
+
+ if ( hard == 0 ) {
+ /* FIXME: use "soft" instead */
+ }
+
+ limit->lms_t_hard = hard;
+ }
+
+ } else {
+ return( 1 );
+ }
+
+ } else if ( arg[0] == '=' ) {
+ arg++;
+ if ( strcasecmp( arg, "unlimited" ) == 0
+ || strcasecmp( arg, "none" ) == 0 )
+ {
+ limit->lms_t_soft = -1;
+
+ } else {
+ if ( lutil_atoi( &limit->lms_t_soft, arg ) != 0
+ || limit->lms_t_soft < -1 )
+ {
+ return( 1 );
+ }
+ }
+ limit->lms_t_hard = 0;
+
+ } else {
+ return( 1 );
+ }
+
+ } else if ( STRSTART( arg, "size" ) ) {
+ arg += STRLENOF( "size" );
+
+ if ( arg[0] == '.' ) {
+ arg++;
+ if ( STRSTART( arg, "soft=" ) ) {
+ arg += STRLENOF( "soft=" );
+ if ( strcasecmp( arg, "unlimited" ) == 0
+ || strcasecmp( arg, "none" ) == 0 )
+ {
+ limit->lms_s_soft = -1;
+
+ } else {
+ int soft;
+
+ if ( lutil_atoi( &soft, arg ) != 0 || soft < -1 ) {
+ return( 1 );
+ }
+
+ if ( soft == -1 ) {
+ /* FIXME: use "unlimited" instead */
+ }
+
+ limit->lms_s_soft = soft;
+ }
+
+ } else if ( STRSTART( arg, "hard=" ) ) {
+ arg += STRLENOF( "hard=" );
+ if ( strcasecmp( arg, "soft" ) == 0 ) {
+ limit->lms_s_hard = 0;
+
+ } else if ( strcasecmp( arg, "unlimited" ) == 0
+ || strcasecmp( arg, "none" ) == 0 )
+ {
+ limit->lms_s_hard = -1;
+
+ } else {
+ int hard;
+
+ if ( lutil_atoi( &hard, arg ) != 0 || hard < -1 ) {
+ return( 1 );
+ }
+
+ if ( hard == -1 ) {
+ /* FIXME: use "unlimited" instead */
+ }
+
+ if ( hard == 0 ) {
+ /* FIXME: use "soft" instead */
+ }
+
+ limit->lms_s_hard = hard;
+ }
+
+ } else if ( STRSTART( arg, "unchecked=" ) ) {
+ arg += STRLENOF( "unchecked=" );
+ if ( strcasecmp( arg, "unlimited" ) == 0
+ || strcasecmp( arg, "none" ) == 0 )
+ {
+ limit->lms_s_unchecked = -1;
+
+ } else if ( strcasecmp( arg, "disabled" ) == 0 ) {
+ limit->lms_s_unchecked = 0;
+
+ } else {
+ int unchecked;
+
+ if ( lutil_atoi( &unchecked, arg ) != 0 || unchecked < -1 ) {
+ return( 1 );
+ }
+
+ if ( unchecked == -1 ) {
+ /* FIXME: use "unlimited" instead */
+ }
+
+ limit->lms_s_unchecked = unchecked;
+ }
+
+ } else if ( STRSTART( arg, "pr=" ) ) {
+ arg += STRLENOF( "pr=" );
+ if ( strcasecmp( arg, "noEstimate" ) == 0 ) {
+ limit->lms_s_pr_hide = 1;
+
+ } else if ( strcasecmp( arg, "unlimited" ) == 0
+ || strcasecmp( arg, "none" ) == 0 )
+ {
+ limit->lms_s_pr = -1;
+
+ } else {
+ int pr;
+
+ if ( lutil_atoi( &pr, arg ) != 0 || pr < -1 ) {
+ return( 1 );
+ }
+
+ if ( pr == -1 ) {
+ /* FIXME: use "unlimited" instead */
+ }
+
+ limit->lms_s_pr = pr;
+ }
+
+ } else if ( STRSTART( arg, "prtotal=" ) ) {
+ arg += STRLENOF( "prtotal=" );
+
+ if ( strcasecmp( arg, "unlimited" ) == 0
+ || strcasecmp( arg, "none" ) == 0 )
+ {
+ limit->lms_s_pr_total = -1;
+
+ } else if ( strcasecmp( arg, "disabled" ) == 0 ) {
+ limit->lms_s_pr_total = -2;
+
+ } else if ( strcasecmp( arg, "hard" ) == 0 ) {
+ limit->lms_s_pr_total = 0;
+
+ } else {
+ int total;
+
+ if ( lutil_atoi( &total, arg ) != 0 || total < -1 ) {
+ return( 1 );
+ }
+
+ if ( total == -1 ) {
+ /* FIXME: use "unlimited" instead */
+ }
+
+ if ( total == 0 ) {
+ /* FIXME: use "pr=disable" instead */
+ }
+
+ limit->lms_s_pr_total = total;
+ }
+
+ } else {
+ return( 1 );
+ }
+
+ } else if ( arg[0] == '=' ) {
+ arg++;
+ if ( strcasecmp( arg, "unlimited" ) == 0
+ || strcasecmp( arg, "none" ) == 0 )
+ {
+ limit->lms_s_soft = -1;
+
+ } else {
+ if ( lutil_atoi( &limit->lms_s_soft, arg ) != 0
+ || limit->lms_s_soft < -1 )
+ {
+ return( 1 );
+ }
+ }
+ limit->lms_s_hard = 0;
+
+ } else {
+ return( 1 );
+ }
+ }
+
+ return 0;
+}
+
+/* Helper macros for limits_unparse() and limits_unparse_one():
+ * Write to ptr, but not past bufEnd. Move ptr past the new text.
+ * Return (success && enough room ? 0 : -1).
+ */
+#define ptr_APPEND_BV(bv) /* Append a \0-terminated berval */ \
+ (WHATSLEFT <= (bv).bv_len ? -1 : \
+ ((void) (ptr = lutil_strcopy( ptr, (bv).bv_val )), 0))
+#define ptr_APPEND_LIT(str) /* Append a string literal */ \
+ (WHATSLEFT <= STRLENOF( "" str "" ) ? -1 : \
+ ((void) (ptr = lutil_strcopy( ptr, str )), 0))
+#define ptr_APPEND_FMT(args) /* Append formatted text */ \
+ (WHATSLEFT <= (tmpLen = snprintf args) ? -1 : ((void) (ptr += tmpLen), 0))
+#define ptr_APPEND_FMT1(fmt, arg) ptr_APPEND_FMT(( ptr, WHATSLEFT, fmt, arg ))
+#define WHATSLEFT ((ber_len_t) (bufEnd - ptr))
+
+/* Caller must provide an adequately sized buffer in bv */
+int
+limits_unparse( struct slap_limits *lim, struct berval *bv, ber_len_t buflen )
+{
+ struct berval btmp;
+ char *ptr, *bufEnd; /* Updated/used by ptr_APPEND_*()/WHATSLEFT */
+ ber_len_t tmpLen; /* Used by ptr_APPEND_FMT*() */
+ unsigned type, style;
+ int rc = 0;
+
+ if ( !bv || !bv->bv_val ) return -1;
+
+ ptr = bv->bv_val;
+ bufEnd = ptr + buflen;
+ type = lim->lm_flags & SLAP_LIMITS_TYPE_MASK;
+
+ if ( type == SLAP_LIMITS_TYPE_GROUP ) {
+ rc = ptr_APPEND_FMT(( ptr, WHATSLEFT, "group/%s/%s=\"%s\"",
+ lim->lm_group_oc->soc_cname.bv_val,
+ lim->lm_group_ad->ad_cname.bv_val,
+ lim->lm_pat.bv_val ));
+ } else {
+ style = lim->lm_flags & SLAP_LIMITS_MASK;
+ switch( style ) {
+ case SLAP_LIMITS_ANONYMOUS:
+ case SLAP_LIMITS_USERS:
+ case SLAP_LIMITS_ANY:
+ rc = ptr_APPEND_BV( lmpats[style] );
+ break;
+ case SLAP_LIMITS_UNDEFINED:
+ case SLAP_LIMITS_EXACT:
+ case SLAP_LIMITS_ONE:
+ case SLAP_LIMITS_SUBTREE:
+ case SLAP_LIMITS_CHILDREN:
+ case SLAP_LIMITS_REGEX:
+ rc = ptr_APPEND_FMT(( ptr, WHATSLEFT, "dn.%s%s=\"%s\"",
+ type == SLAP_LIMITS_TYPE_SELF ? "" : "this.",
+ lmpats[style].bv_val, lim->lm_pat.bv_val ));
+ break;
+ }
+ }
+ if ( rc == 0 ) {
+ bv->bv_len = ptr - bv->bv_val;
+ btmp.bv_val = ptr;
+ btmp.bv_len = 0;
+ rc = limits_unparse_one( &lim->lm_limits,
+ SLAP_LIMIT_SIZE | SLAP_LIMIT_TIME,
+ &btmp, WHATSLEFT );
+ if ( rc == 0 )
+ bv->bv_len += btmp.bv_len;
+ }
+ return rc;
+}
+
+/* Caller must provide an adequately sized buffer in bv */
+int
+limits_unparse_one(
+ struct slap_limits_set *lim,
+ int which,
+ struct berval *bv,
+ ber_len_t buflen )
+{
+ char *ptr, *bufEnd; /* Updated/used by ptr_APPEND_*()/WHATSLEFT */
+ ber_len_t tmpLen; /* Used by ptr_APPEND_FMT*() */
+
+ if ( !bv || !bv->bv_val ) return -1;
+
+ ptr = bv->bv_val;
+ bufEnd = ptr + buflen;
+
+ if ( which & SLAP_LIMIT_SIZE ) {
+ if ( lim->lms_s_soft != SLAPD_DEFAULT_SIZELIMIT ) {
+
+ /* If same as global limit, drop it */
+ if ( lim != &frontendDB->be_def_limit &&
+ lim->lms_s_soft == frontendDB->be_def_limit.lms_s_soft )
+ {
+ goto s_hard;
+ /* If there's also a hard limit, fully qualify this one */
+ } else if ( lim->lms_s_hard ) {
+ if ( ptr_APPEND_LIT( " size.soft=" ) ) return -1;
+
+ /* If doing both size & time, qualify this */
+ } else if ( which & SLAP_LIMIT_TIME ) {
+ if ( ptr_APPEND_LIT( " size=" ) ) return -1;
+ }
+
+ if ( lim->lms_s_soft == -1
+ ? ptr_APPEND_LIT( "unlimited " )
+ : ptr_APPEND_FMT1( "%d ", lim->lms_s_soft ) )
+ return -1;
+ }
+s_hard:
+ if ( lim->lms_s_hard ) {
+ if ( ptr_APPEND_LIT( " size.hard=" ) ) return -1;
+ if ( lim->lms_s_hard == -1
+ ? ptr_APPEND_LIT( "unlimited " )
+ : ptr_APPEND_FMT1( "%d ", lim->lms_s_hard ) )
+ return -1;
+ }
+ if ( lim->lms_s_unchecked != -1 ) {
+ if ( ptr_APPEND_LIT( " size.unchecked=" ) ) return -1;
+ if ( lim->lms_s_unchecked == 0
+ ? ptr_APPEND_LIT( "disabled " )
+ : ptr_APPEND_FMT1( "%d ", lim->lms_s_unchecked ) )
+ return -1;
+ }
+ if ( lim->lms_s_pr_hide ) {
+ if ( ptr_APPEND_LIT( " size.pr=noEstimate " ) ) return -1;
+ }
+ if ( lim->lms_s_pr ) {
+ if ( ptr_APPEND_LIT( " size.pr=" ) ) return -1;
+ if ( lim->lms_s_pr == -1
+ ? ptr_APPEND_LIT( "unlimited " )
+ : ptr_APPEND_FMT1( "%d ", lim->lms_s_pr ) )
+ return -1;
+ }
+ if ( lim->lms_s_pr_total ) {
+ if ( ptr_APPEND_LIT( " size.prtotal=" ) ) return -1;
+ if ( lim->lms_s_pr_total == -1 ? ptr_APPEND_LIT( "unlimited " )
+ : lim->lms_s_pr_total == -2 ? ptr_APPEND_LIT( "disabled " )
+ : ptr_APPEND_FMT1( "%d ", lim->lms_s_pr_total ) )
+ return -1;
+ }
+ }
+
+ if ( which & SLAP_LIMIT_TIME ) {
+ if ( lim->lms_t_soft != SLAPD_DEFAULT_TIMELIMIT ) {
+
+ /* If same as global limit, drop it */
+ if ( lim != &frontendDB->be_def_limit &&
+ lim->lms_t_soft == frontendDB->be_def_limit.lms_t_soft )
+ {
+ goto t_hard;
+
+ /* If there's also a hard limit, fully qualify this one */
+ } else if ( lim->lms_t_hard ) {
+ if ( ptr_APPEND_LIT( " time.soft=" ) ) return -1;
+
+ /* If doing both size & time, qualify this */
+ } else if ( which & SLAP_LIMIT_SIZE ) {
+ if ( ptr_APPEND_LIT( " time=" ) ) return -1;
+ }
+
+ if ( lim->lms_t_soft == -1
+ ? ptr_APPEND_LIT( "unlimited " )
+ : ptr_APPEND_FMT1( "%d ", lim->lms_t_soft ) )
+ return -1;
+ }
+t_hard:
+ if ( lim->lms_t_hard ) {
+ if ( ptr_APPEND_LIT( " time.hard=" ) ) return -1;
+ if ( lim->lms_t_hard == -1
+ ? ptr_APPEND_LIT( "unlimited " )
+ : ptr_APPEND_FMT1( "%d ", lim->lms_t_hard ) )
+ return -1;
+ }
+ }
+ if ( ptr != bv->bv_val ) {
+ ptr--;
+ *ptr = '\0';
+ bv->bv_len = ptr - bv->bv_val;
+ }
+
+ return 0;
+}
+
+int
+limits_check( Operation *op, SlapReply *rs )
+{
+ assert( op != NULL );
+ assert( rs != NULL );
+ /* FIXME: should this be always true? */
+ assert( op->o_tag == LDAP_REQ_SEARCH);
+
+ /* protocol only allows 0..maxInt;
+ *
+ * internal searches:
+ * - may use SLAP_NO_LIMIT ( = -1 ) to indicate no limits;
+ * - should use slimit = N and tlimit = SLAP_NO_LIMIT to
+ * indicate searches that should return exactly N matches,
+ * and handle errors thru a callback (see for instance
+ * slap_sasl_match() and slap_sasl2dn())
+ */
+ if ( op->ors_tlimit == SLAP_NO_LIMIT && op->ors_slimit == SLAP_NO_LIMIT ) {
+ return 0;
+ }
+
+ /* allow root to set no limit */
+ if ( be_isroot( op ) ) {
+ op->ors_limit = NULL;
+
+ if ( op->ors_tlimit == 0 ) {
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ }
+
+ if ( op->ors_slimit == 0 ) {
+ op->ors_slimit = SLAP_NO_LIMIT;
+ }
+
+ /* if paged results and slimit are requested */
+ if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED &&
+ op->ors_slimit != SLAP_NO_LIMIT ) {
+ PagedResultsState *ps = op->o_pagedresults_state;
+ int total = op->ors_slimit - ps->ps_count;
+ if ( total > 0 ) {
+ op->ors_slimit = total;
+ } else {
+ op->ors_slimit = 0;
+ }
+ }
+
+ /* if not root, get appropriate limits */
+ } else {
+ ( void ) limits_get( op, &op->ors_limit );
+
+ assert( op->ors_limit != NULL );
+
+ /* if no limit is required, use soft limit */
+ if ( op->ors_tlimit == 0 ) {
+ op->ors_tlimit = op->ors_limit->lms_t_soft;
+
+ /* limit required: check if legal */
+ } else {
+ if ( op->ors_limit->lms_t_hard == 0 ) {
+ if ( op->ors_limit->lms_t_soft > 0
+ && ( op->ors_tlimit > op->ors_limit->lms_t_soft ) ) {
+ op->ors_tlimit = op->ors_limit->lms_t_soft;
+ }
+
+ } else if ( op->ors_limit->lms_t_hard > 0 ) {
+#ifdef ABOVE_HARD_LIMIT_IS_ERROR
+ if ( op->ors_tlimit == SLAP_MAX_LIMIT ) {
+ op->ors_tlimit = op->ors_limit->lms_t_hard;
+
+ } else if ( op->ors_tlimit > op->ors_limit->lms_t_hard ) {
+ /* error if exceeding hard limit */
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ return -1;
+ }
+#else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
+ if ( op->ors_tlimit > op->ors_limit->lms_t_hard ) {
+ op->ors_tlimit = op->ors_limit->lms_t_hard;
+ }
+#endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
+ }
+ }
+
+ /* else leave as is */
+
+ /* don't even get to backend if candidate check is disabled */
+ if ( op->ors_limit->lms_s_unchecked == 0 ) {
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ return -1;
+ }
+
+ /* if paged results is requested */
+ if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
+ int slimit = -2;
+ int pr_total;
+ PagedResultsState *ps = op->o_pagedresults_state;
+
+ /* paged results is not allowed */
+ if ( op->ors_limit->lms_s_pr_total == -2 ) {
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ rs->sr_text = "pagedResults control not allowed";
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ return -1;
+ }
+
+ if ( op->ors_limit->lms_s_pr > 0
+ && ps->ps_size > op->ors_limit->lms_s_pr )
+ {
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ rs->sr_text = "illegal pagedResults page size";
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ return -1;
+ }
+
+ if ( op->ors_limit->lms_s_pr_total == 0 ) {
+ if ( op->ors_limit->lms_s_hard == 0 ) {
+ pr_total = op->ors_limit->lms_s_soft;
+ } else {
+ pr_total = op->ors_limit->lms_s_hard;
+ }
+ } else {
+ pr_total = op->ors_limit->lms_s_pr_total;
+ }
+
+ if ( pr_total == -1 ) {
+ if ( op->ors_slimit == 0 || op->ors_slimit == SLAP_MAX_LIMIT ) {
+ slimit = -1;
+
+ } else {
+ slimit = op->ors_slimit - ps->ps_count;
+ }
+
+#ifdef ABOVE_HARD_LIMIT_IS_ERROR
+ } else if ( pr_total > 0 && op->ors_slimit != SLAP_MAX_LIMIT
+ && ( op->ors_slimit == SLAP_NO_LIMIT
+ || op->ors_slimit > pr_total ) )
+ {
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ return -1;
+#endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
+
+ } else {
+ /* if no limit is required, use soft limit */
+ int total;
+ int slimit2;
+
+ /* first round of pagedResults:
+ * set count to any appropriate limit */
+
+ /* if the limit is set, check that it does
+ * not violate any server-side limit */
+#ifdef ABOVE_HARD_LIMIT_IS_ERROR
+ if ( op->ors_slimit == SLAP_MAX_LIMIT )
+#else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
+ if ( op->ors_slimit == SLAP_MAX_LIMIT
+ || op->ors_slimit > pr_total )
+#endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
+ {
+ slimit2 = op->ors_slimit = pr_total;
+
+ } else if ( op->ors_slimit == 0 ) {
+ slimit2 = pr_total;
+
+ } else {
+ slimit2 = op->ors_slimit;
+ }
+
+ total = slimit2 - ps->ps_count;
+
+ if ( total >= 0 ) {
+ if ( op->ors_limit->lms_s_pr > 0 ) {
+ /* use the smallest limit set by total/per page */
+ if ( total < op->ors_limit->lms_s_pr ) {
+ slimit = total;
+
+ } else {
+ /* use the perpage limit if any
+ * NOTE: + 1 because given value must be legal */
+ slimit = op->ors_limit->lms_s_pr + 1;
+ }
+
+ } else {
+ /* use the total limit if any */
+ slimit = total;
+ }
+
+ } else if ( op->ors_limit->lms_s_pr > 0 ) {
+ /* use the perpage limit if any
+ * NOTE: + 1 because the given value must be legal */
+ slimit = op->ors_limit->lms_s_pr + 1;
+
+ } else {
+ /* use the standard hard/soft limit if any */
+ slimit = op->ors_limit->lms_s_hard;
+ }
+ }
+
+ /* if got any limit, use it */
+ if ( slimit != -2 ) {
+ if ( op->ors_slimit == 0 ) {
+ op->ors_slimit = slimit;
+
+ } else if ( slimit > 0 ) {
+ if ( op->ors_slimit - ps->ps_count > slimit ) {
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ return -1;
+ }
+ op->ors_slimit = slimit;
+
+ } else if ( slimit == 0 ) {
+ op->ors_slimit = 0;
+ }
+
+ } else {
+ /* use the standard hard/soft limit if any */
+ op->ors_slimit = pr_total;
+ }
+
+ /* no limit requested: use soft, whatever it is */
+ } else if ( op->ors_slimit == 0 ) {
+ op->ors_slimit = op->ors_limit->lms_s_soft;
+
+ /* limit requested: check if legal */
+ } else {
+ /* hard limit as soft (traditional behavior) */
+ if ( op->ors_limit->lms_s_hard == 0 ) {
+ if ( op->ors_limit->lms_s_soft > 0
+ && op->ors_slimit > op->ors_limit->lms_s_soft ) {
+ op->ors_slimit = op->ors_limit->lms_s_soft;
+ }
+
+ /* explicit hard limit: error if violated */
+ } else if ( op->ors_limit->lms_s_hard > 0 ) {
+#ifdef ABOVE_HARD_LIMIT_IS_ERROR
+ if ( op->ors_slimit == SLAP_MAX_LIMIT ) {
+ op->ors_slimit = op->ors_limit->lms_s_hard;
+
+ } else if ( op->ors_slimit > op->ors_limit->lms_s_hard ) {
+ /* if limit exceeds hard, error */
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ send_ldap_result( op, rs );
+ rs->sr_err = LDAP_SUCCESS;
+ return -1;
+ }
+#else /* ! ABOVE_HARD_LIMIT_IS_ERROR */
+ if ( op->ors_slimit > op->ors_limit->lms_s_hard ) {
+ op->ors_slimit = op->ors_limit->lms_s_hard;
+ }
+#endif /* ! ABOVE_HARD_LIMIT_IS_ERROR */
+ }
+ }
+
+ /* else leave as is */
+ }
+
+ return 0;
+}
+
+void
+limits_free_one(
+ struct slap_limits *lm )
+{
+ if ( ( lm->lm_flags & SLAP_LIMITS_MASK ) == SLAP_LIMITS_REGEX )
+ regfree( &lm->lm_regex );
+
+ if ( !BER_BVISNULL( &lm->lm_pat ) )
+ ch_free( lm->lm_pat.bv_val );
+
+ ch_free( lm );
+}
+
+void
+limits_destroy(
+ struct slap_limits **lm )
+{
+ int i;
+
+ if ( lm == NULL ) {
+ return;
+ }
+
+ for ( i = 0; lm[ i ]; i++ ) {
+ limits_free_one( lm[ i ] );
+ }
+
+ ch_free( lm );
+}
diff --git a/servers/slapd/lock.c b/servers/slapd/lock.c
new file mode 100644
index 0000000..91a4e34
--- /dev/null
+++ b/servers/slapd/lock.c
@@ -0,0 +1,83 @@
+/* lock.c - routines to open and apply an advisory lock to a file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+
+#include "slap.h"
+#include <lutil.h>
+
+FILE *
+lock_fopen( const char *fname, const char *type, FILE **lfp )
+{
+ FILE *fp;
+ char buf[MAXPATHLEN];
+
+ /* open the lock file */
+ snprintf( buf, sizeof buf, "%s.lock", fname );
+
+ if ( (*lfp = fopen( buf, "w" )) == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "could not open \"%s\"\n", buf );
+
+ return( NULL );
+ }
+
+ /* acquire the lock */
+ ldap_lockf( fileno(*lfp) );
+
+ /* open the log file */
+ if ( (fp = fopen( fname, type )) == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "could not open \"%s\"\n", fname );
+
+ ldap_unlockf( fileno(*lfp) );
+ fclose( *lfp );
+ *lfp = NULL;
+ return( NULL );
+ }
+
+ return( fp );
+}
+
+int
+lock_fclose( FILE *fp, FILE *lfp )
+{
+ int rc = fclose( fp );
+ /* unlock */
+ ldap_unlockf( fileno(lfp) );
+ fclose( lfp );
+
+ return( rc );
+}
diff --git a/servers/slapd/main.c b/servers/slapd/main.c
new file mode 100644
index 0000000..11e7a8f
--- /dev/null
+++ b/servers/slapd/main.c
@@ -0,0 +1,1193 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+#include <ac/wait.h>
+#include <ac/errno.h>
+
+#include "slap.h"
+#include "lutil.h"
+#include "ldif.h"
+
+#ifdef LDAP_SLAPI
+#include "slapi/slapi.h"
+#endif
+
+#ifdef LDAP_SIGCHLD
+static RETSIGTYPE wait4child( int sig );
+#endif
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+#define MAIN_RETURN(x) return
+static struct sockaddr_in bind_addr;
+
+#define SERVICE_EXIT( e, n ) do { \
+ if ( is_NT_Service ) { \
+ lutil_ServiceStatus.dwWin32ExitCode = (e); \
+ lutil_ServiceStatus.dwServiceSpecificExitCode = (n); \
+ } \
+} while ( 0 )
+
+#else
+#define SERVICE_EXIT( e, n )
+#define MAIN_RETURN(x) return(x)
+#endif
+
+typedef int (MainFunc) LDAP_P(( int argc, char *argv[] ));
+extern MainFunc slapadd, slapcat, slapdn, slapindex, slappasswd,
+ slaptest, slapauth, slapacl, slapschema, slapmodify;
+
+static struct {
+ char *name;
+ MainFunc *func;
+} tools[] = {
+ {"slapadd", slapadd},
+ {"slapcat", slapcat},
+ {"slapdn", slapdn},
+ {"slapindex", slapindex},
+ {"slapmodify", slapmodify},
+ {"slappasswd", slappasswd},
+ {"slapschema", slapschema},
+ {"slaptest", slaptest},
+ {"slapauth", slapauth},
+ {"slapacl", slapacl},
+ /* NOTE: new tools must be added in chronological order,
+ * not in alphabetical order, because for backwards
+ * compatibility name[4] is used to identify the
+ * tools; so name[4]=='a' must refer to "slapadd" and
+ * not to "slapauth". Alphabetical order can be used
+ * for tools whose name[4] is not used yet */
+ {NULL, NULL}
+};
+
+/*
+ * when more than one slapd is running on one machine, each one might have
+ * it's own LOCAL for syslogging and must have its own pid/args files
+ */
+
+#ifndef HAVE_MKVERSION
+const char Versionstr[] =
+ OPENLDAP_PACKAGE " " OPENLDAP_VERSION " Standalone LDAP Server (slapd)";
+#endif
+
+extern OverlayInit slap_oinfo[];
+extern BackendInfo slap_binfo[];
+
+#define CHECK_NONE 0x00
+#define CHECK_CONFIG 0x01
+#define CHECK_LOGLEVEL 0x02
+static int check = CHECK_NONE;
+static int version = 0;
+
+void *slap_tls_ctx;
+LDAP *slap_tls_ld;
+
+static int
+slapd_opt_slp( const char *val, void *arg )
+{
+#ifdef HAVE_SLP
+ /* NULL is default */
+ if ( val == NULL || *val == '(' || strcasecmp( val, "on" ) == 0 ) {
+ slapd_register_slp = 1;
+ slapd_slp_attrs = (val != NULL && *val == '(') ? val : NULL;
+
+ } else if ( strcasecmp( val, "off" ) == 0 ) {
+ slapd_register_slp = 0;
+
+ /* NOTE: add support for URL specification? */
+
+ } else {
+ fprintf(stderr, "unrecognized value \"%s\" for SLP option\n", val );
+ return -1;
+ }
+
+ return 0;
+
+#else
+ fputs( "slapd: SLP support is not available\n", stderr );
+ return 0;
+#endif
+}
+
+/*
+ * Option helper structure:
+ *
+ * oh_nam is left-hand part of <option>[=<value>]
+ * oh_fnc is handler function
+ * oh_arg is an optional arg to oh_fnc
+ * oh_usage is the one-line usage string related to the option,
+ * which is assumed to start with <option>[=<value>]
+ *
+ * please leave valid options in the structure, and optionally #ifdef
+ * their processing inside the helper, so that reasonable and helpful
+ * error messages can be generated if a disabled option is requested.
+ */
+struct option_helper {
+ struct berval oh_name;
+ int (*oh_fnc)(const char *val, void *arg);
+ void *oh_arg;
+ const char *oh_usage;
+} option_helpers[] = {
+ { BER_BVC("slp"), slapd_opt_slp, NULL, "slp[={on|off|(attrs)}] enable/disable SLP using (attrs)" },
+ { BER_BVNULL, 0, NULL, NULL }
+};
+
+#if defined(LDAP_DEBUG) && defined(LDAP_SYSLOG)
+#ifdef LOG_LOCAL4
+int
+parse_syslog_user( const char *arg, int *syslogUser )
+{
+ static slap_verbmasks syslogUsers[] = {
+ { BER_BVC( "LOCAL0" ), LOG_LOCAL0 },
+ { BER_BVC( "LOCAL1" ), LOG_LOCAL1 },
+ { BER_BVC( "LOCAL2" ), LOG_LOCAL2 },
+ { BER_BVC( "LOCAL3" ), LOG_LOCAL3 },
+ { BER_BVC( "LOCAL4" ), LOG_LOCAL4 },
+ { BER_BVC( "LOCAL5" ), LOG_LOCAL5 },
+ { BER_BVC( "LOCAL6" ), LOG_LOCAL6 },
+ { BER_BVC( "LOCAL7" ), LOG_LOCAL7 },
+#ifdef LOG_USER
+ { BER_BVC( "USER" ), LOG_USER },
+#endif /* LOG_USER */
+#ifdef LOG_DAEMON
+ { BER_BVC( "DAEMON" ), LOG_DAEMON },
+#endif /* LOG_DAEMON */
+ { BER_BVNULL, 0 }
+ };
+ int i = verb_to_mask( arg, syslogUsers );
+
+ if ( BER_BVISNULL( &syslogUsers[ i ].word ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "unrecognized syslog user \"%s\".\n",
+ arg );
+ return 1;
+ }
+
+ *syslogUser = syslogUsers[ i ].mask;
+
+ return 0;
+}
+#endif /* LOG_LOCAL4 */
+
+int
+parse_syslog_level( const char *arg, int *levelp )
+{
+ static slap_verbmasks str2syslog_level[] = {
+ { BER_BVC( "EMERG" ), LOG_EMERG },
+ { BER_BVC( "ALERT" ), LOG_ALERT },
+ { BER_BVC( "CRIT" ), LOG_CRIT },
+ { BER_BVC( "ERR" ), LOG_ERR },
+ { BER_BVC( "WARNING" ), LOG_WARNING },
+ { BER_BVC( "NOTICE" ), LOG_NOTICE },
+ { BER_BVC( "INFO" ), LOG_INFO },
+ { BER_BVC( "DEBUG" ), LOG_DEBUG },
+ { BER_BVNULL, 0 }
+ };
+ int i = verb_to_mask( arg, str2syslog_level );
+ if ( BER_BVISNULL( &str2syslog_level[ i ].word ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "unknown syslog level \"%s\".\n",
+ arg );
+ return 1;
+ }
+
+ *levelp = str2syslog_level[ i ].mask;
+
+ return 0;
+}
+#endif /* LDAP_DEBUG && LDAP_SYSLOG */
+
+static char **debug_unknowns;
+static char **syslog_unknowns;
+
+int
+parse_debug_unknowns( char **unknowns, int *levelp )
+{
+ int i, level, rc = 0;
+
+ for ( i = 0; unknowns[ i ] != NULL; i++ ) {
+ level = 0;
+ if ( str2loglevel( unknowns[ i ], &level )) {
+ fprintf( stderr,
+ "unrecognized log level \"%s\"\n", unknowns[ i ] );
+ rc = 1;
+ } else {
+ *levelp |= level;
+ }
+ }
+ return rc;
+}
+
+int
+parse_debug_level( const char *arg, int *levelp, char ***unknowns )
+{
+ int level;
+
+ if ( arg && arg[ 0 ] != '-' && !isdigit( (unsigned char) arg[ 0 ] ) )
+ {
+ int i;
+ char **levels;
+
+ levels = ldap_str2charray( arg, "," );
+
+ for ( i = 0; levels[ i ] != NULL; i++ ) {
+ level = 0;
+
+ if ( str2loglevel( levels[ i ], &level ) ) {
+ /* remember this for later */
+ ldap_charray_add( unknowns, levels[ i ] );
+ fprintf( stderr,
+ "unrecognized log level \"%s\" (deferred)\n",
+ levels[ i ] );
+ } else {
+ *levelp |= level;
+ }
+ }
+
+ ldap_charray_free( levels );
+
+ } else {
+ int rc;
+
+ if ( arg[0] == '-' ) {
+ rc = lutil_atoix( &level, arg, 0 );
+ } else {
+ unsigned ulevel;
+
+ rc = lutil_atoux( &ulevel, arg, 0 );
+ level = (int)ulevel;
+ }
+
+ if ( rc ) {
+ fprintf( stderr,
+ "unrecognized log level "
+ "\"%s\"\n", arg );
+ return 1;
+ }
+
+ if ( level == 0 ) {
+ *levelp = 0;
+
+ } else {
+ *levelp |= level;
+ }
+ }
+
+ return 0;
+}
+
+void slap_check_unknown_level( char *levelstr, int level )
+{
+ int i;
+
+ if ( debug_unknowns ) {
+ for ( i = 0; debug_unknowns[ i ]; i++ ) {
+ if ( !strcasecmp( debug_unknowns[ i ], levelstr )) {
+ slap_debug |= level;
+ break;
+ }
+ }
+ }
+
+ if ( syslog_unknowns ) {
+ for ( i = 0; syslog_unknowns[ i ]; i++ ) {
+ if ( !strcasecmp( syslog_unknowns[ i ], levelstr )) {
+ ldap_syslog |= level;
+ break;
+ }
+ }
+ }
+}
+
+static void
+usage( char *name )
+{
+ fprintf( stderr,
+ "usage: %s options\n", name );
+ fprintf( stderr,
+ "\t-4\t\tIPv4 only\n"
+#ifdef LDAP_PF_INET6
+ "\t-6\t\tIPv6 only\n"
+#endif
+ "\t-T {acl|add|auth|cat|dn|index|modify|passwd|test}\n"
+ "\t\t\tRun in Tool mode\n"
+ "\t-c cookie\tSync cookie of consumer\n"
+ "\t-d level\tDebug level" "\n"
+ "\t-f filename\tConfiguration file\n"
+ "\t-F dir\tConfiguration directory\n"
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+ "\t-g group\tGroup (id or name) to run as\n"
+#endif
+ "\t-h URLs\t\tList of URLs to serve\n"
+#ifdef SLAP_DEFAULT_SYSLOG_USER
+ "\t-l facility\tSyslog facility (default: LOCAL4)\n"
+#endif
+ "\t-n serverName\tService name\n"
+ "\t-o <opt>[=val] generic means to specify options" );
+ if ( !BER_BVISNULL( &option_helpers[0].oh_name ) ) {
+ int i;
+
+ fprintf( stderr, "; supported options:\n" );
+ for ( i = 0; !BER_BVISNULL( &option_helpers[i].oh_name ); i++) {
+ fprintf( stderr, "\t\t%s\n", option_helpers[i].oh_usage );
+ }
+ } else {
+ fprintf( stderr, "\n" );
+ }
+ fprintf( stderr,
+#ifdef HAVE_CHROOT
+ "\t-r directory\tSandbox directory to chroot to\n"
+#endif
+ "\t-s level\tSyslog level\n"
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+ "\t-u user\t\tUser (id or name) to run as\n"
+#endif
+ "\t-V\t\tprint version info (-VV exit afterwards, -VVV print\n"
+ "\t\t\tinfo about static overlays and backends)\n"
+ );
+}
+
+typedef void (BER_logger)(const char *buf);
+static BER_logger *ber_logger;
+static void debug_print( const char *data )
+{
+ char buf[4136]; /* 4096 + 40 */
+#ifdef HAVE_CLOCK_GETTIME
+ struct timespec tv;
+#define TS "%08x"
+#define Tfrac tv.tv_nsec
+ clock_gettime( CLOCK_REALTIME, &tv );
+#else
+ struct timeval tv;
+#define TS "%05x"
+#define Tfrac tv.tv_usec
+ gettimeofday( &tv, NULL );
+#endif
+
+ buf[sizeof(buf)-1] = '\0';
+ snprintf( buf, sizeof(buf)-1, "%lx." TS " %p %s",
+ (long)tv.tv_sec, Tfrac, (void *)ldap_pvt_thread_self(), data );
+ ber_logger( buf );
+}
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+void WINAPI ServiceMain( DWORD argc, LPTSTR *argv )
+#else
+int main( int argc, char **argv )
+#endif
+{
+ int i, no_detach = 0;
+ int rc = 1;
+ char *urls = NULL;
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+ char *username = NULL;
+ char *groupname = NULL;
+#endif
+#if defined(HAVE_CHROOT)
+ char *sandbox = NULL;
+#endif
+#ifdef SLAP_DEFAULT_SYSLOG_USER
+ int syslogUser = SLAP_DEFAULT_SYSLOG_USER;
+#endif
+
+#ifndef HAVE_WINSOCK
+ int pid, waitfds[2];
+#endif
+ int g_argc = argc;
+ char **g_argv = argv;
+
+ char *configfile = NULL;
+ char *configdir = NULL;
+ char *serverName;
+ int serverMode = SLAP_SERVER_MODE;
+
+ struct sync_cookie *scp = NULL;
+ struct sync_cookie *scp_entry = NULL;
+
+ char *serverNamePrefix = "";
+ size_t l;
+
+ int slapd_pid_file_unlink = 0, slapd_args_file_unlink = 0;
+ int firstopt = 1;
+
+#ifdef CSRIMALLOC
+ FILE *leakfile;
+ if( ( leakfile = fopen( "slapd.leak", "w" )) == NULL ) {
+ leakfile = stderr;
+ }
+#endif
+
+ slap_sl_mem_init();
+
+
+ (void) ldap_pvt_thread_initialize();
+
+#ifdef HAVE_TLS
+ rc = ldap_create( &slap_tls_ld );
+ if ( rc ) {
+ MAIN_RETURN( rc );
+ }
+ /* Library defaults to full certificate checking. This is correct when
+ * a client is verifying a server because all servers should have a
+ * valid cert. But few clients have valid certs, so we want our default
+ * to be no checking. The config file can override this as usual.
+ */
+ rc = LDAP_OPT_X_TLS_NEVER;
+ (void) ldap_pvt_tls_set_option( slap_tls_ld, LDAP_OPT_X_TLS_REQUIRE_CERT, &rc );
+#endif
+
+ serverName = lutil_progname( "slapd", argc, argv );
+
+ if ( strcmp( serverName, "slapd" ) ) {
+#ifdef DEBUG_CLOSE
+ extern void slapd_debug_close();
+ slapd_debug_close();
+#endif
+ for (i=0; tools[i].name; i++) {
+ if ( !strcmp( serverName, tools[i].name ) ) {
+ rc = tools[i].func(argc, argv);
+ MAIN_RETURN(rc);
+ }
+ }
+ }
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+ {
+ int *ip;
+ char *newConfigFile;
+ char *newConfigDir;
+ char *newUrls;
+ char *regService = NULL;
+
+ if ( is_NT_Service ) {
+ lutil_CommenceStartupProcessing( serverName, slap_sig_shutdown );
+ if ( strcmp(serverName, SERVICE_NAME) )
+ regService = serverName;
+ }
+
+ ip = (int*)lutil_getRegParam( regService, "DebugLevel" );
+ if ( ip != NULL ) {
+ slap_debug = *ip;
+ Debug( LDAP_DEBUG_ANY,
+ "new debug level from registry is: %d\n", slap_debug );
+ }
+
+ newUrls = (char *) lutil_getRegParam(regService, "Urls");
+ if (newUrls) {
+ if (urls)
+ ch_free(urls);
+
+ urls = ch_strdup(newUrls);
+ Debug(LDAP_DEBUG_ANY, "new urls from registry: %s\n",
+ urls );
+ }
+
+ newConfigFile = (char*)lutil_getRegParam( regService, "ConfigFile" );
+ if ( newConfigFile != NULL ) {
+ configfile = ch_strdup(newConfigFile);
+ Debug ( LDAP_DEBUG_ANY, "new config file from registry is: %s\n", configfile );
+ }
+
+ newConfigDir = (char*)lutil_getRegParam( regService, "ConfigDir" );
+ if ( newConfigDir != NULL ) {
+ configdir = ch_strdup(newConfigDir);
+ Debug ( LDAP_DEBUG_ANY, "new config dir from registry is: %s\n", configdir );
+ }
+ }
+#endif
+
+ while ( (i = getopt( argc, argv,
+ "c:d:f:F:h:n:o:s:tT:V"
+#ifdef LDAP_PF_INET6
+ "46"
+#endif
+#ifdef HAVE_CHROOT
+ "r:"
+#endif
+#if defined(LDAP_DEBUG) && defined(LDAP_SYSLOG)
+ "S:"
+#ifdef LOG_LOCAL4
+ "l:"
+#endif
+#endif
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+ "u:g:"
+#endif
+ )) != EOF ) {
+ switch ( i ) {
+ case '4':
+ slap_inet4or6 = AF_INET;
+ break;
+#ifdef LDAP_PF_INET6
+ case '6':
+ slap_inet4or6 = AF_INET6;
+ break;
+#endif
+
+ case 'h': /* listen URLs */
+ if ( urls != NULL ) free( urls );
+ urls = optarg;
+ break;
+
+ case 'c': /* provide sync cookie, override if exist in consumer */
+ scp = (struct sync_cookie *) ch_calloc( 1,
+ sizeof( struct sync_cookie ));
+ ber_str2bv( optarg, 0, 1, &scp->octet_str );
+
+ /* This only parses out the rid at this point */
+ slap_parse_sync_cookie( scp, NULL );
+
+ if ( scp->rid == -1 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "main: invalid cookie \"%s\"\n",
+ optarg );
+ slap_sync_cookie_free( scp, 1 );
+ goto destroy;
+ }
+
+ LDAP_STAILQ_FOREACH( scp_entry, &slap_sync_cookie, sc_next ) {
+ if ( scp->rid == scp_entry->rid ) {
+ Debug( LDAP_DEBUG_ANY,
+ "main: duplicated replica id in cookies\n" );
+ slap_sync_cookie_free( scp, 1 );
+ goto destroy;
+ }
+ }
+ LDAP_STAILQ_INSERT_TAIL( &slap_sync_cookie, scp, sc_next );
+ break;
+
+ case 'd': { /* set debug level and 'do not detach' flag */
+ int level = 0;
+
+ if ( strcmp( optarg, "?" ) == 0 ) {
+ check |= CHECK_LOGLEVEL;
+ break;
+ }
+
+ no_detach = 1;
+ if ( parse_debug_level( optarg, &level, &debug_unknowns ) ) {
+ goto destroy;
+ }
+#ifdef LDAP_DEBUG
+ slap_debug |= level;
+#else
+ if ( level != 0 )
+ fputs( "must compile with LDAP_DEBUG for debugging\n",
+ stderr );
+#endif
+ } break;
+
+ case 'f': /* read config file */
+ configfile = optarg;
+ break;
+
+ case 'F': /* use config dir */
+ configdir = optarg;
+ break;
+
+ case 'o': {
+ char *val = strchr( optarg, '=' );
+ struct berval opt;
+
+ opt.bv_val = optarg;
+
+ if ( val ) {
+ opt.bv_len = ( val - optarg );
+ val++;
+
+ } else {
+ opt.bv_len = strlen( optarg );
+ }
+
+ for ( i = 0; !BER_BVISNULL( &option_helpers[i].oh_name ); i++ ) {
+ if ( ber_bvstrcasecmp( &option_helpers[i].oh_name, &opt ) == 0 ) {
+ assert( option_helpers[i].oh_fnc != NULL );
+ if ( (*option_helpers[i].oh_fnc)( val, option_helpers[i].oh_arg ) == -1 ) {
+ /* we assume the option parsing helper
+ * issues appropriate and self-explanatory
+ * error messages... */
+ goto stop;
+ }
+ break;
+ }
+ }
+
+ if ( BER_BVISNULL( &option_helpers[i].oh_name ) ) {
+ goto unhandled_option;
+ }
+ break;
+ }
+
+ case 's': /* set syslog level */
+ if ( strcmp( optarg, "?" ) == 0 ) {
+ check |= CHECK_LOGLEVEL;
+ break;
+ }
+
+ if ( parse_debug_level( optarg, &ldap_syslog, &syslog_unknowns ) ) {
+ goto destroy;
+ }
+ break;
+
+#if defined(LDAP_DEBUG) && defined(LDAP_SYSLOG)
+ case 'S':
+ if ( parse_syslog_level( optarg, &ldap_syslog_level ) ) {
+ goto destroy;
+ }
+ break;
+
+#ifdef LOG_LOCAL4
+ case 'l': /* set syslog local user */
+ if ( parse_syslog_user( optarg, &syslogUser ) ) {
+ goto destroy;
+ }
+ break;
+#endif
+#endif /* LDAP_DEBUG && LDAP_SYSLOG */
+
+#ifdef HAVE_CHROOT
+ case 'r':
+ sandbox = optarg;
+ break;
+#endif
+
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+ case 'u': /* user name */
+ username = optarg;
+ break;
+
+ case 'g': /* group name */
+ groupname = optarg;
+ break;
+#endif /* SETUID && GETUID */
+
+ case 'n': /* NT service name */
+ serverName = optarg;
+ break;
+
+ case 't':
+ /* deprecated; use slaptest instead */
+ fprintf( stderr, "option -t deprecated; "
+ "use slaptest command instead\n" );
+ check |= CHECK_CONFIG;
+ break;
+
+ case 'V':
+ version++;
+ break;
+
+ case 'T':
+ if ( firstopt == 0 ) {
+ fprintf( stderr, "warning: \"-T %s\" "
+ "should be the first option.\n",
+ optarg );
+ }
+
+#ifdef DEBUG_CLOSE
+ extern void slapd_debug_close();
+ slapd_debug_close();
+#endif
+ /* try full option string first */
+ for ( i = 0; tools[i].name; i++ ) {
+ if ( strcmp( optarg, &tools[i].name[4] ) == 0 ) {
+ rc = tools[i].func( argc, argv );
+ MAIN_RETURN( rc );
+ }
+ }
+
+ /* try bits of option string (backward compatibility for single char) */
+ l = strlen( optarg );
+ for ( i = 0; tools[i].name; i++ ) {
+ if ( strncmp( optarg, &tools[i].name[4], l ) == 0 ) {
+ rc = tools[i].func( argc, argv );
+ MAIN_RETURN( rc );
+ }
+ }
+
+ /* issue error */
+ serverName = optarg;
+ serverNamePrefix = "slap";
+ fprintf( stderr, "program name \"%s%s\" unrecognized; "
+ "aborting...\n", serverNamePrefix, serverName );
+ /* FALLTHRU */
+ default:
+unhandled_option:;
+ usage( argv[0] );
+ rc = 1;
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 15 );
+ goto stop;
+ }
+
+ if ( firstopt ) {
+ firstopt = 0;
+ }
+ }
+
+ if ( optind != argc )
+ goto unhandled_option;
+
+ ber_get_option(NULL, LBER_OPT_LOG_PRINT_FN, &ber_logger);
+ ber_set_option(NULL, LBER_OPT_LOG_PRINT_FN, debug_print);
+ ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &slap_debug);
+ ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &slap_debug);
+ ldif_debug = slap_debug;
+
+ if ( version ) {
+ fprintf( stderr, "%s\n", Versionstr );
+ if ( version > 2 ) {
+ if ( slap_oinfo[0].ov_type ) {
+ fprintf( stderr, "Included static overlays:\n");
+ for ( i= 0 ; slap_oinfo[i].ov_type; i++ ) {
+ fprintf( stderr, " %s\n", slap_oinfo[i].ov_type );
+ }
+ }
+ if ( slap_binfo[0].bi_type ) {
+ fprintf( stderr, "Included static backends:\n");
+ for ( i= 0 ; slap_binfo[i].bi_type; i++ ) {
+ fprintf( stderr, " %s\n", slap_binfo[i].bi_type );
+ }
+ }
+ }
+
+ if ( version > 1 ) goto stop;
+ }
+
+#if defined(LDAP_DEBUG) && defined(LDAP_SYSLOG)
+ {
+ char *logName;
+#ifdef HAVE_EBCDIC
+ logName = ch_strdup( serverName );
+ __atoe( logName );
+#else
+ logName = serverName;
+#endif
+
+#ifdef LOG_LOCAL4
+ openlog( logName, OPENLOG_OPTIONS, syslogUser );
+#elif defined LOG_DEBUG
+ openlog( logName, OPENLOG_OPTIONS );
+#endif
+#ifdef HAVE_EBCDIC
+ free( logName );
+#endif
+ }
+#endif /* LDAP_DEBUG && LDAP_SYSLOG */
+
+ Debug( LDAP_DEBUG_ANY, "%s", Versionstr );
+
+ global_host = ldap_pvt_get_fqdn( NULL );
+ ber_str2bv( global_host, 0, 0, &global_host_bv );
+
+ if( check == CHECK_NONE && slapd_daemon_init( urls ) != 0 ) {
+ rc = 1;
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 16 );
+ goto stop;
+ }
+
+#if defined(HAVE_CHROOT)
+ if ( sandbox ) {
+ if ( chdir( sandbox ) ) {
+ perror("chdir");
+ rc = 1;
+ goto stop;
+ }
+ if ( chroot( sandbox ) ) {
+ perror("chroot");
+ rc = 1;
+ goto stop;
+ }
+ if ( chdir( "/" ) ) {
+ perror("chdir");
+ rc = 1;
+ goto stop;
+ }
+ }
+#endif
+
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+ if ( username != NULL || groupname != NULL ) {
+ slap_init_user( username, groupname );
+ }
+#endif
+
+ extops_init();
+ lutil_passwd_init();
+
+ rc = slap_init( serverMode, serverName );
+ if ( rc ) {
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 18 );
+ goto destroy;
+ }
+
+ if ( read_config( configfile, configdir ) != 0 ) {
+ rc = 1;
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 19 );
+
+ if ( check & CHECK_CONFIG ) {
+ fprintf( stderr, "config check failed\n" );
+ }
+
+ goto destroy;
+ }
+
+ if ( debug_unknowns ) {
+ rc = parse_debug_unknowns( debug_unknowns, &slap_debug );
+ ldap_charray_free( debug_unknowns );
+ debug_unknowns = NULL;
+ if ( rc )
+ goto destroy;
+ ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &slap_debug );
+ ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &slap_debug );
+ }
+ if ( syslog_unknowns ) {
+ rc = parse_debug_unknowns( syslog_unknowns, &ldap_syslog );
+ ldap_charray_free( syslog_unknowns );
+ syslog_unknowns = NULL;
+ if ( rc )
+ goto destroy;
+ }
+
+ if ( check & CHECK_LOGLEVEL ) {
+ rc = 0;
+ goto destroy;
+ }
+
+ if ( check & CHECK_CONFIG ) {
+ fprintf( stderr, "config check succeeded\n" );
+
+ check &= ~CHECK_CONFIG;
+ if ( check == CHECK_NONE ) {
+ rc = 0;
+ goto destroy;
+ }
+ }
+
+ if ( glue_sub_attach( 0 ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "subordinate config error\n" );
+
+ goto destroy;
+ }
+
+ if ( slap_schema_check( ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "schema prep error\n" );
+
+ goto destroy;
+ }
+
+#ifdef HAVE_TLS
+ rc = ldap_pvt_tls_init( 1 );
+ if( rc != 0) {
+ Debug( LDAP_DEBUG_ANY,
+ "main: TLS init failed: %d\n",
+ rc );
+ rc = 1;
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 20 );
+ goto destroy;
+ }
+
+ {
+ int opt = 1;
+
+ /* Force new ctx to be created */
+ rc = ldap_pvt_tls_set_option( slap_tls_ld, LDAP_OPT_X_TLS_NEWCTX, &opt );
+ if( rc == 0 ) {
+ /* The ctx's refcount is bumped up here */
+ ldap_pvt_tls_get_option( slap_tls_ld, LDAP_OPT_X_TLS_CTX, &slap_tls_ctx );
+ load_extop( &slap_EXOP_START_TLS, 0, starttls_extop );
+ } else if ( rc != LDAP_NOT_SUPPORTED ) {
+ Debug( LDAP_DEBUG_ANY,
+ "main: TLS init def ctx failed: %d\n",
+ rc );
+ rc = 1;
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 20 );
+ goto destroy;
+ }
+ }
+#endif
+
+#ifdef HAVE_CYRUS_SASL
+ if( sasl_host == NULL ) {
+ sasl_host = ch_strdup( global_host );
+ }
+#endif
+
+ (void) SIGNAL( LDAP_SIGUSR1, slap_sig_wake );
+ (void) SIGNAL( LDAP_SIGUSR2, slap_sig_shutdown );
+
+#ifdef SIGPIPE
+ (void) SIGNAL( SIGPIPE, SIG_IGN );
+#endif
+#ifdef SIGHUP
+ (void) SIGNAL( SIGHUP, slap_sig_shutdown );
+#endif
+ (void) SIGNAL( SIGINT, slap_sig_shutdown );
+ (void) SIGNAL( SIGTERM, slap_sig_shutdown );
+#ifdef SIGTRAP
+ (void) SIGNAL( SIGTRAP, slap_sig_shutdown );
+#endif
+#ifdef LDAP_SIGCHLD
+ (void) SIGNAL( LDAP_SIGCHLD, wait4child );
+#endif
+#ifdef SIGBREAK
+ /* SIGBREAK is generated when Ctrl-Break is pressed. */
+ (void) SIGNAL( SIGBREAK, slap_sig_shutdown );
+#endif
+
+#ifndef HAVE_WINSOCK
+ if ( !no_detach ) {
+ if ( lutil_pair( waitfds ) < 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "main: lutil_pair failed: %d\n",
+ 0 );
+ rc = 1;
+ goto destroy;
+ }
+ pid = lutil_detach( no_detach, 0 );
+ if ( pid ) {
+ char buf[4];
+ rc = EXIT_SUCCESS;
+ close( waitfds[1] );
+ if ( read( waitfds[0], buf, 1 ) != 1 )
+ rc = EXIT_FAILURE;
+ _exit( rc );
+ } else {
+ close( waitfds[0] );
+ }
+ }
+#endif /* HAVE_WINSOCK */
+
+#ifdef CSRIMALLOC
+ mal_leaktrace(1);
+#endif
+
+ if ( slapd_pid_file != NULL ) {
+ FILE *fp = fopen( slapd_pid_file, "w" );
+
+ if ( fp == NULL ) {
+ char ebuf[128];
+ int save_errno = errno;
+
+ Debug( LDAP_DEBUG_ANY, "unable to open pid file "
+ "\"%s\": %d (%s)\n",
+ slapd_pid_file,
+ save_errno, AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+
+ free( slapd_pid_file );
+ slapd_pid_file = NULL;
+
+ rc = 1;
+ goto destroy;
+ }
+ fprintf( fp, "%d\n", (int) getpid() );
+ fclose( fp );
+ slapd_pid_file_unlink = 1;
+ }
+
+ if ( slapd_args_file != NULL ) {
+ FILE *fp = fopen( slapd_args_file, "w" );
+
+ if ( fp == NULL ) {
+ char ebuf[128];
+ int save_errno = errno;
+
+ Debug( LDAP_DEBUG_ANY, "unable to open args file "
+ "\"%s\": %d (%s)\n",
+ slapd_args_file,
+ save_errno, AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+
+ free( slapd_args_file );
+ slapd_args_file = NULL;
+
+ rc = 1;
+ goto destroy;
+ }
+
+ for ( i = 0; i < g_argc; i++ ) {
+ fprintf( fp, "%s ", g_argv[i] );
+ }
+ fprintf( fp, "\n" );
+ fclose( fp );
+ slapd_args_file_unlink = 1;
+ }
+
+ /*
+ * FIXME: moved here from slapd_daemon_task()
+ * because back-monitor db_open() needs it
+ */
+ time( &starttime );
+
+ connections_init();
+
+ if ( slap_startup( NULL ) != 0 ) {
+ rc = 1;
+ SERVICE_EXIT( ERROR_SERVICE_SPECIFIC_ERROR, 21 );
+ goto shutdown;
+ }
+
+ Debug( LDAP_DEBUG_ANY, "slapd starting\n" );
+
+#ifndef HAVE_WINSOCK
+ if ( !no_detach ) {
+ write( waitfds[1], "1", 1 );
+ close( waitfds[1] );
+ }
+#endif
+
+#ifdef HAVE_NT_EVENT_LOG
+ if (is_NT_Service)
+ lutil_LogStartedEvent( serverName, slap_debug, configfile ?
+ configfile : SLAPD_DEFAULT_CONFIGFILE , urls );
+#endif
+
+ rc = slapd_daemon();
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+ /* Throw away the event that we used during the startup process. */
+ if ( is_NT_Service )
+ ldap_pvt_thread_cond_destroy( &started_event );
+#endif
+
+shutdown:
+ /* remember an error during shutdown */
+ rc |= slap_shutdown( NULL );
+
+destroy:
+ if ( check & CHECK_LOGLEVEL ) {
+ (void)loglevel_print( stdout );
+ }
+ /* remember an error during destroy */
+ rc |= slap_destroy();
+
+ while ( !LDAP_STAILQ_EMPTY( &slap_sync_cookie )) {
+ scp = LDAP_STAILQ_FIRST( &slap_sync_cookie );
+ LDAP_STAILQ_REMOVE_HEAD( &slap_sync_cookie, sc_next );
+ ch_free( scp );
+ }
+
+#ifdef SLAPD_MODULES
+ module_kill();
+#endif
+
+ extops_kill();
+
+ supported_feature_destroy();
+ entry_info_destroy();
+
+stop:
+#ifdef HAVE_NT_EVENT_LOG
+ if (is_NT_Service)
+ lutil_LogStoppedEvent( serverName );
+#endif
+
+ Debug( LDAP_DEBUG_ANY, "slapd stopped.\n" );
+
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+ lutil_ReportShutdownComplete();
+#endif
+
+#ifdef LOG_DEBUG
+ closelog();
+#endif
+ slapd_daemon_destroy();
+
+ controls_destroy();
+
+ filter_destroy();
+
+ schema_destroy();
+
+ lutil_passwd_destroy();
+
+#ifdef HAVE_TLS
+ if ( slap_tls_ld ) {
+ ldap_pvt_tls_ctx_free( slap_tls_ctx );
+ ldap_unbind_ext( slap_tls_ld, NULL, NULL );
+ }
+ ldap_pvt_tls_destroy();
+#endif
+
+ slap_sasl_regexp_destroy();
+
+ if ( slapd_pid_file_unlink ) {
+ unlink( slapd_pid_file );
+ }
+ if ( slapd_args_file_unlink ) {
+ unlink( slapd_args_file );
+ }
+
+ config_destroy();
+
+ if ( global_host )
+ ch_free( global_host );
+
+ /* kludge, get symbols referenced */
+ ldap_tavl_free( NULL, NULL );
+
+#ifdef CSRIMALLOC
+ mal_dumpleaktrace( leakfile );
+#endif
+
+ MAIN_RETURN(rc);
+}
+
+
+#ifdef LDAP_SIGCHLD
+
+/*
+ * Catch and discard terminated child processes, to avoid zombies.
+ */
+
+static RETSIGTYPE
+wait4child( int sig )
+{
+ int save_errno = errno;
+
+#ifdef WNOHANG
+ do
+ errno = 0;
+#ifdef HAVE_WAITPID
+ while ( waitpid( (pid_t)-1, NULL, WNOHANG ) > 0 || errno == EINTR );
+#else
+ while ( wait3( NULL, WNOHANG, NULL ) > 0 || errno == EINTR );
+#endif
+#else
+ (void) wait( NULL );
+#endif
+ (void) SIGNAL_REINSTALL( sig, wait4child );
+ errno = save_errno;
+}
+
+#endif /* LDAP_SIGCHLD */
diff --git a/servers/slapd/matchedValues.c b/servers/slapd/matchedValues.c
new file mode 100644
index 0000000..574e085
--- /dev/null
+++ b/servers/slapd/matchedValues.c
@@ -0,0 +1,348 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+static int
+test_mra_vrFilter(
+ Operation *op,
+ Attribute *a,
+ MatchingRuleAssertion *mra,
+ char ***e_flags
+);
+
+static int
+test_substrings_vrFilter(
+ Operation *op,
+ Attribute *a,
+ ValuesReturnFilter *f,
+ char ***e_flags
+);
+
+static int
+test_presence_vrFilter(
+ Operation *op,
+ Attribute *a,
+ AttributeDescription *desc,
+ char ***e_flags
+);
+
+static int
+test_ava_vrFilter(
+ Operation *op,
+ Attribute *a,
+ AttributeAssertion *ava,
+ int type,
+ char ***e_flags
+);
+
+
+int
+filter_matched_values(
+ Operation *op,
+ Attribute *a,
+ char ***e_flags )
+{
+ ValuesReturnFilter *vrf;
+ int rc = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_FILTER, "=> filter_matched_values\n" );
+
+ for ( vrf = op->o_vrFilter; vrf != NULL; vrf = vrf->vrf_next ) {
+ switch ( vrf->vrf_choice ) {
+ case SLAPD_FILTER_COMPUTED:
+ Debug( LDAP_DEBUG_FILTER, " COMPUTED %s (%d)\n",
+ vrf->vrf_result == LDAP_COMPARE_FALSE ? "false"
+ : vrf->vrf_result == LDAP_COMPARE_TRUE ? "true"
+ : vrf->vrf_result == SLAPD_COMPARE_UNDEFINED ? "undefined"
+ : "error",
+ vrf->vrf_result );
+ /*This type of filter does not affect the result */
+ rc = LDAP_SUCCESS;
+ break;
+
+ case LDAP_FILTER_EQUALITY:
+ Debug( LDAP_DEBUG_FILTER, " EQUALITY\n" );
+ rc = test_ava_vrFilter( op, a, vrf->vrf_ava,
+ LDAP_FILTER_EQUALITY, e_flags );
+ if( rc == -1 ) return rc;
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ Debug( LDAP_DEBUG_FILTER, " SUBSTRINGS\n" );
+ rc = test_substrings_vrFilter( op, a,
+ vrf, e_flags );
+ if( rc == -1 ) return rc;
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ Debug( LDAP_DEBUG_FILTER, " PRESENT\n" );
+ rc = test_presence_vrFilter( op, a,
+ vrf->vrf_desc, e_flags );
+ if( rc == -1 ) return rc;
+ break;
+
+ case LDAP_FILTER_GE:
+ rc = test_ava_vrFilter( op, a, vrf->vrf_ava,
+ LDAP_FILTER_GE, e_flags );
+ if( rc == -1 ) return rc;
+ break;
+
+ case LDAP_FILTER_LE:
+ rc = test_ava_vrFilter( op, a, vrf->vrf_ava,
+ LDAP_FILTER_LE, e_flags );
+ if( rc == -1 ) return rc;
+ break;
+
+ case LDAP_FILTER_EXT:
+ Debug( LDAP_DEBUG_FILTER, " EXT\n" );
+ rc = test_mra_vrFilter( op, a,
+ vrf->vrf_mra, e_flags );
+ if( rc == -1 ) return rc;
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY, " unknown filter type %lu\n",
+ vrf->vrf_choice );
+ rc = LDAP_PROTOCOL_ERROR;
+ }
+ }
+
+ Debug( LDAP_DEBUG_FILTER, "<= filter_matched_values %d\n", rc );
+ return( rc );
+}
+
+static int
+test_ava_vrFilter(
+ Operation *op,
+ Attribute *a,
+ AttributeAssertion *ava,
+ int type,
+ char ***e_flags )
+{
+ int i, j;
+
+ for ( i=0; a != NULL; a = a->a_next, i++ ) {
+ MatchingRule *mr;
+ struct berval *bv;
+
+ if ( !is_ad_subtype( a->a_desc, ava->aa_desc ) ) {
+ continue;
+ }
+
+ switch ( type ) {
+ case LDAP_FILTER_APPROX:
+ mr = a->a_desc->ad_type->sat_approx;
+ if( mr != NULL ) break;
+ /* use EQUALITY matching rule if no APPROX rule */
+
+ case LDAP_FILTER_EQUALITY:
+ mr = a->a_desc->ad_type->sat_equality;
+ break;
+
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ mr = a->a_desc->ad_type->sat_ordering;
+ break;
+
+ default:
+ mr = NULL;
+ }
+
+ if( mr == NULL ) continue;
+
+ bv = a->a_nvals;
+ for ( j=0; !BER_BVISNULL( bv ); bv++, j++ ) {
+ int rc, match;
+ const char *text;
+
+ rc = value_match( &match, a->a_desc, mr, 0,
+ bv, &ava->aa_value, &text );
+ if( rc != LDAP_SUCCESS ) return rc;
+
+ switch ( type ) {
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_APPROX:
+ if ( match == 0 ) {
+ (*e_flags)[i][j] = 1;
+ }
+ break;
+
+ case LDAP_FILTER_GE:
+ if ( match >= 0 ) {
+ (*e_flags)[i][j] = 1;
+ }
+ break;
+
+ case LDAP_FILTER_LE:
+ if ( match <= 0 ) {
+ (*e_flags)[i][j] = 1;
+ }
+ break;
+ }
+ }
+ }
+ return LDAP_SUCCESS;
+}
+
+static int
+test_presence_vrFilter(
+ Operation *op,
+ Attribute *a,
+ AttributeDescription *desc,
+ char ***e_flags )
+{
+ int i, j;
+
+ for ( i=0; a != NULL; a = a->a_next, i++ ) {
+ struct berval *bv;
+
+ if ( !is_ad_subtype( a->a_desc, desc ) ) continue;
+
+ for ( bv = a->a_vals, j = 0; !BER_BVISNULL( bv ); bv++, j++ );
+ memset( (*e_flags)[i], 1, j);
+ }
+
+ return( LDAP_SUCCESS );
+}
+
+static int
+test_substrings_vrFilter(
+ Operation *op,
+ Attribute *a,
+ ValuesReturnFilter *vrf,
+ char ***e_flags )
+{
+ int i, j;
+
+ for ( i=0; a != NULL; a = a->a_next, i++ ) {
+ MatchingRule *mr = a->a_desc->ad_type->sat_substr;
+ struct berval *bv;
+
+ if ( !is_ad_subtype( a->a_desc, vrf->vrf_sub_desc ) ) {
+ continue;
+ }
+
+ if( mr == NULL ) continue;
+
+ bv = a->a_nvals;
+ for ( j = 0; !BER_BVISNULL( bv ); bv++, j++ ) {
+ int rc, match;
+ const char *text;
+
+ rc = value_match( &match, a->a_desc, mr, 0,
+ bv, vrf->vrf_sub, &text );
+
+ if( rc != LDAP_SUCCESS ) return rc;
+
+ if ( match == 0 ) {
+ (*e_flags)[i][j] = 1;
+ }
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+test_mra_vrFilter(
+ Operation *op,
+ Attribute *a,
+ MatchingRuleAssertion *mra,
+ char ***e_flags )
+{
+ int i, j;
+
+ for ( i = 0; a != NULL; a = a->a_next, i++ ) {
+ struct berval *bv, assertedValue;
+ int normalize_attribute = 0;
+
+ if ( mra->ma_desc ) {
+ if ( !is_ad_subtype( a->a_desc, mra->ma_desc ) ) {
+ continue;
+ }
+ assertedValue = mra->ma_value;
+
+ } else {
+ int rc;
+ const char *text = NULL;
+
+ /* check if matching is appropriate */
+ if ( !mr_usable_with_at( mra->ma_rule, a->a_desc->ad_type ) ) {
+ continue;
+ }
+
+ rc = asserted_value_validate_normalize( a->a_desc, mra->ma_rule,
+ SLAP_MR_EXT|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
+ &mra->ma_value, &assertedValue, &text, op->o_tmpmemctx );
+
+ if ( rc != LDAP_SUCCESS ) continue;
+ }
+
+ /* check match */
+ if ( mra->ma_rule == a->a_desc->ad_type->sat_equality ) {
+ bv = a->a_nvals;
+
+ } else {
+ bv = a->a_vals;
+ normalize_attribute = 1;
+ }
+
+ for ( j = 0; !BER_BVISNULL( bv ); bv++, j++ ) {
+ int rc, match;
+ const char *text;
+ struct berval nbv = BER_BVNULL;
+
+ if ( normalize_attribute && mra->ma_rule->smr_normalize ) {
+ /* see comment in filterentry.c */
+ if ( mra->ma_rule->smr_normalize(
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ mra->ma_rule->smr_syntax,
+ mra->ma_rule,
+ bv, &nbv, op->o_tmpmemctx ) != LDAP_SUCCESS )
+ {
+ /* FIXME: stop processing? */
+ continue;
+ }
+
+ } else {
+ nbv = *bv;
+ }
+
+ rc = value_match( &match, a->a_desc, mra->ma_rule, 0,
+ &nbv, &assertedValue, &text );
+
+ if ( nbv.bv_val != bv->bv_val ) {
+ op->o_tmpfree( nbv.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( rc != LDAP_SUCCESS ) return rc;
+
+ if ( match == 0 ) {
+ (*e_flags)[i][j] = 1;
+ }
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
diff --git a/servers/slapd/modify.c b/servers/slapd/modify.c
new file mode 100644
index 0000000..0da8953
--- /dev/null
+++ b/servers/slapd/modify.c
@@ -0,0 +1,1099 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "slap.h"
+#include "lutil.h"
+
+
+int
+do_modify(
+ Operation *op,
+ SlapReply *rs )
+{
+ struct berval dn = BER_BVNULL;
+ char textbuf[ SLAP_TEXT_BUFLEN ];
+ size_t textlen = sizeof( textbuf );
+#ifdef LDAP_DEBUG
+ Modifications *tmp;
+#endif
+
+ Debug( LDAP_DEBUG_TRACE, "%s do_modify\n",
+ op->o_log_prefix );
+ /*
+ * Parse the modify request. It looks like this:
+ *
+ * ModifyRequest := [APPLICATION 6] SEQUENCE {
+ * name DistinguishedName,
+ * mods SEQUENCE OF SEQUENCE {
+ * operation ENUMERATED {
+ * add (0),
+ * delete (1),
+ * replace (2)
+ * },
+ * modification SEQUENCE {
+ * type AttributeType,
+ * values SET OF AttributeValue
+ * }
+ * }
+ * }
+ */
+
+ if ( ber_scanf( op->o_ber, "{m" /*}*/, &dn ) == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_modify: ber_scanf failed\n",
+ op->o_log_prefix );
+ send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
+ return SLAPD_DISCONNECT;
+ }
+
+ Debug( LDAP_DEBUG_ARGS, "%s do_modify: dn (%s)\n",
+ op->o_log_prefix, dn.bv_val );
+
+ rs->sr_err = slap_parse_modlist( op, rs, op->o_ber, &op->oq_modify );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_modify: slap_parse_modlist failed err=%d msg=%s\n",
+ op->o_log_prefix, rs->sr_err, rs->sr_text );
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ if( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_modify: get_ctrls failed\n",
+ op->o_log_prefix );
+ /* get_ctrls has sent results. Now clean up. */
+ goto cleanup;
+ }
+
+ rs->sr_err = dnPrettyNormal( NULL, &dn, &op->o_req_dn, &op->o_req_ndn,
+ op->o_tmpmemctx );
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_modify: invalid dn (%s)\n",
+ op->o_log_prefix, dn.bv_val );
+ send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid DN" );
+ goto cleanup;
+ }
+
+ op->orm_no_opattrs = 0;
+
+#ifdef LDAP_DEBUG
+ Debug( LDAP_DEBUG_ARGS, "%s modifications:\n",
+ op->o_log_prefix );
+
+ for ( tmp = op->orm_modlist; tmp != NULL; tmp = tmp->sml_next ) {
+ Debug( LDAP_DEBUG_ARGS, "\t%s: %s\n",
+ tmp->sml_op == LDAP_MOD_ADD ? "add" :
+ (tmp->sml_op == LDAP_MOD_INCREMENT ? "increment" :
+ (tmp->sml_op == LDAP_MOD_DELETE ? "delete" :
+ "replace")), tmp->sml_type.bv_val );
+
+ if ( tmp->sml_values == NULL ) {
+ Debug( LDAP_DEBUG_ARGS, "\t\tno values\n" );
+ } else if ( BER_BVISNULL( &tmp->sml_values[ 0 ] ) ) {
+ Debug( LDAP_DEBUG_ARGS, "\t\tzero values\n" );
+ } else if ( BER_BVISNULL( &tmp->sml_values[ 1 ] ) ) {
+ Debug( LDAP_DEBUG_ARGS, "\t\tone value, length %ld\n",
+ (long) tmp->sml_values[0].bv_len );
+ } else {
+ Debug( LDAP_DEBUG_ARGS, "\t\tmultiple values\n" );
+ }
+ }
+
+ if (LogTest( LDAP_DEBUG_STATS ) ) {
+ char abuf[BUFSIZ/2], *ptr = abuf;
+ int len = 0;
+
+ Debug( LDAP_DEBUG_STATS, "%s MOD dn=\"%s\"\n",
+ op->o_log_prefix, op->o_req_dn.bv_val );
+
+ for ( tmp = op->orm_modlist; tmp != NULL; tmp = tmp->sml_next ) {
+ if (len + 1 + tmp->sml_type.bv_len > sizeof(abuf)) {
+ Debug( LDAP_DEBUG_STATS, "%s MOD attr=%s\n",
+ op->o_log_prefix, abuf );
+
+ len = 0;
+ ptr = abuf;
+
+ if( 1 + tmp->sml_type.bv_len > sizeof(abuf)) {
+ Debug( LDAP_DEBUG_STATS, "%s MOD attr=%s\n",
+ op->o_log_prefix, tmp->sml_type.bv_val );
+ continue;
+ }
+ }
+ if (len) {
+ *ptr++ = ' ';
+ len++;
+ }
+ ptr = lutil_strcopy(ptr, tmp->sml_type.bv_val);
+ len += tmp->sml_type.bv_len;
+ }
+ if (len) {
+ Debug( LDAP_DEBUG_STATS, "%s MOD attr=%s\n",
+ op->o_log_prefix, abuf );
+ }
+ }
+#endif /* LDAP_DEBUG */
+
+ rs->sr_err = slap_mods_check( op, op->orm_modlist,
+ &rs->sr_text, textbuf, textlen, NULL );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ op->o_bd = frontendDB;
+ rs->sr_err = frontendDB->be_modify( op, rs );
+ if ( rs->sr_err == SLAPD_ASYNCOP ) {
+ /* skip cleanup */
+ return rs->sr_err;
+ }
+
+ if( rs->sr_err == LDAP_TXN_SPECIFY_OKAY ) {
+ /* skip cleanup */
+ return rs->sr_err;
+ }
+
+cleanup:
+ op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
+ if ( op->orm_modlist != NULL ) slap_mods_free( op->orm_modlist, 1 );
+
+ return rs->sr_err;
+}
+
+int
+fe_op_modify( Operation *op, SlapReply *rs )
+{
+ BackendDB *op_be, *bd = op->o_bd;
+ char textbuf[ SLAP_TEXT_BUFLEN ];
+ size_t textlen = sizeof( textbuf );
+
+ if ( BER_BVISEMPTY( &op->o_req_ndn ) ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_modify: root dse!\n",
+ op->o_log_prefix );
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "modify upon the root DSE not supported" );
+ goto cleanup;
+
+ } else if ( bvmatch( &op->o_req_ndn, &frontendDB->be_schemandn ) ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_modify: subschema subentry!\n",
+ op->o_log_prefix );
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "modification of subschema subentry not supported" );
+ goto cleanup;
+ }
+
+ /*
+ * We could be serving multiple database backends. Select the
+ * appropriate one, or send a referral to our "referral server"
+ * if we don't hold it.
+ */
+ op->o_bd = select_backend( &op->o_req_ndn, 1 );
+ if ( op->o_bd == NULL ) {
+ op->o_bd = bd;
+ rs->sr_ref = referral_rewrite( default_referral,
+ NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ if ( !rs->sr_ref ) {
+ rs->sr_ref = default_referral;
+ }
+
+ if ( rs->sr_ref != NULL ) {
+ rs->sr_err = LDAP_REFERRAL;
+ send_ldap_result( op, rs );
+
+ if ( rs->sr_ref != default_referral ) {
+ ber_bvarray_free( rs->sr_ref );
+ }
+
+ } else {
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "no global superior knowledge" );
+ }
+ goto cleanup;
+ }
+
+ /* If we've got a glued backend, check the real backend */
+ op_be = op->o_bd;
+ if ( SLAP_GLUE_INSTANCE( op->o_bd )) {
+ op->o_bd = select_backend( &op->o_req_ndn, 0 );
+ }
+
+ /* check restrictions */
+ if ( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ /* check for referrals */
+ if ( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) {
+ goto cleanup;
+ }
+
+ rs->sr_err = slap_mods_obsolete_check( op, op->orm_modlist,
+ &rs->sr_text, textbuf, textlen );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ /* check for modify/increment support */
+ if ( op->orm_increment && !SLAP_INCREMENT( op->o_bd ) ) {
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "modify/increment not supported in context" );
+ goto cleanup;
+ }
+
+ /*
+ * do the modify if 1 && (2 || 3)
+ * 1) there is a modify function implemented in this backend;
+ * 2) this backend is the provider for what it holds;
+ * 3) it's a replica and the dn supplied is the update_ndn.
+ */
+ if ( op->o_bd->be_modify ) {
+ /* do the update here */
+ int repl_user = be_isupdate( op );
+
+ /*
+ * Multimaster slapd does not have to check for replicator dn
+ * because it accepts each modify request
+ */
+ if ( !SLAP_SINGLE_SHADOW(op->o_bd) || repl_user ) {
+ int update = !BER_BVISEMPTY( &op->o_bd->be_update_ndn );
+
+ if ( !update ) {
+ rs->sr_err = slap_mods_no_user_mod_check( op, op->orm_modlist,
+ &rs->sr_text, textbuf, textlen );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+ }
+ if ( op->o_txnSpec ) {
+ txn_preop( op, rs );
+ goto cleanup;
+ }
+ op->o_bd = op_be;
+ op->o_bd->be_modify( op, rs );
+
+ } else { /* send a referral */
+ BerVarray defref = op->o_bd->be_update_refs
+ ? op->o_bd->be_update_refs : default_referral;
+ if ( defref != NULL ) {
+ rs->sr_ref = referral_rewrite( defref,
+ NULL, &op->o_req_dn,
+ LDAP_SCOPE_DEFAULT );
+ if ( rs->sr_ref == NULL ) {
+ /* FIXME: must duplicate, because
+ * overlays may muck with it */
+ rs->sr_ref = defref;
+ }
+ rs->sr_err = LDAP_REFERRAL;
+ send_ldap_result( op, rs );
+ if ( rs->sr_ref != defref ) {
+ ber_bvarray_free( rs->sr_ref );
+ }
+
+ } else {
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "shadow context; no update referral" );
+ }
+ }
+
+ } else {
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "operation not supported within namingContext" );
+ }
+
+cleanup:;
+ op->o_bd = bd;
+ return rs->sr_err;
+}
+
+/*
+ * Obsolete constraint checking.
+ */
+int
+slap_mods_obsolete_check(
+ Operation *op,
+ Modifications *ml,
+ const char **text,
+ char *textbuf,
+ size_t textlen )
+{
+ if( get_relax( op ) ) return LDAP_SUCCESS;
+
+ for ( ; ml != NULL; ml = ml->sml_next ) {
+ if ( is_at_obsolete( ml->sml_desc->ad_type ) &&
+ (( ml->sml_op != LDAP_MOD_REPLACE &&
+ ml->sml_op != LDAP_MOD_DELETE ) ||
+ ml->sml_values != NULL ))
+ {
+ /*
+ * attribute is obsolete,
+ * only allow replace/delete with no values
+ */
+ snprintf( textbuf, textlen,
+ "%s: attribute is obsolete",
+ ml->sml_type.bv_val );
+ *text = textbuf;
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * No-user-modification constraint checking.
+ */
+int
+slap_mods_no_user_mod_check(
+ Operation *op,
+ Modifications *ml,
+ const char **text,
+ char *textbuf,
+ size_t textlen )
+{
+ for ( ; ml != NULL; ml = ml->sml_next ) {
+ if ( !is_at_no_user_mod( ml->sml_desc->ad_type ) ) {
+ continue;
+ }
+
+ if ( ml->sml_flags & SLAP_MOD_INTERNAL ) {
+ continue;
+ }
+
+ if ( get_relax( op ) ) {
+ if ( ml->sml_desc->ad_type->sat_flags & SLAP_AT_MANAGEABLE ) {
+ ml->sml_flags |= SLAP_MOD_MANAGING;
+ continue;
+ }
+
+ /* attribute not manageable */
+ snprintf( textbuf, textlen,
+ "%s: no-user-modification attribute not manageable",
+ ml->sml_type.bv_val );
+
+ } else {
+ /* user modification disallowed */
+ snprintf( textbuf, textlen,
+ "%s: no user modification allowed",
+ ml->sml_type.bv_val );
+ }
+
+ *text = textbuf;
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int
+slap_mods_no_repl_user_mod_check(
+ Operation *op,
+ Modifications *ml,
+ const char **text,
+ char *textbuf,
+ size_t textlen )
+{
+ Modifications *mods;
+ Modifications *modp;
+
+ for ( mods = ml; mods != NULL; mods = mods->sml_next ) {
+ assert( mods->sml_op == LDAP_MOD_ADD );
+
+ /* check doesn't already appear */
+ for ( modp = ml; modp != NULL; modp = modp->sml_next ) {
+ if ( mods->sml_desc == modp->sml_desc && mods != modp ) {
+ snprintf( textbuf, textlen,
+ "attribute '%s' provided more than once",
+ mods->sml_desc->ad_cname.bv_val );
+ *text = textbuf;
+ return LDAP_TYPE_OR_VALUE_EXISTS;
+ }
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Do basic attribute type checking and syntax validation.
+ */
+int slap_mods_check(
+ Operation *op,
+ Modifications *ml,
+ const char **text,
+ char *textbuf,
+ size_t textlen,
+ void *ctx )
+{
+ int rc;
+
+ for( ; ml != NULL; ml = ml->sml_next ) {
+ AttributeDescription *ad = NULL;
+
+ /* convert to attribute description */
+ if ( ml->sml_desc == NULL ) {
+ rc = slap_bv2ad( &ml->sml_type, &ml->sml_desc, text );
+ if( rc != LDAP_SUCCESS ) {
+ if ( get_no_schema_check( op )) {
+ rc = slap_bv2undef_ad( &ml->sml_type, &ml->sml_desc,
+ text, 0 );
+ }
+ }
+ if( rc != LDAP_SUCCESS ) {
+ snprintf( textbuf, textlen, "%s: %s",
+ ml->sml_type.bv_val, *text );
+ *text = textbuf;
+ return rc;
+ }
+ }
+
+ ad = ml->sml_desc;
+
+ if( slap_syntax_is_binary( ad->ad_type->sat_syntax )
+ && !slap_ad_is_binary( ad ))
+ {
+ /* attribute requires binary transfer */
+ snprintf( textbuf, textlen,
+ "%s: requires ;binary transfer",
+ ml->sml_type.bv_val );
+ *text = textbuf;
+ return LDAP_UNDEFINED_TYPE;
+ }
+
+ if( !slap_syntax_is_binary( ad->ad_type->sat_syntax )
+ && slap_ad_is_binary( ad ))
+ {
+ /* attribute does not require binary transfer */
+ snprintf( textbuf, textlen,
+ "%s: disallows ;binary transfer",
+ ml->sml_type.bv_val );
+ *text = textbuf;
+ return LDAP_UNDEFINED_TYPE;
+ }
+
+ if( slap_ad_is_tag_range( ad )) {
+ /* attribute requires binary transfer */
+ snprintf( textbuf, textlen,
+ "%s: inappropriate use of tag range option",
+ ml->sml_type.bv_val );
+ *text = textbuf;
+ return LDAP_UNDEFINED_TYPE;
+ }
+
+#if 0
+ if ( is_at_obsolete( ad->ad_type ) &&
+ (( ml->sml_op != LDAP_MOD_REPLACE &&
+ ml->sml_op != LDAP_MOD_DELETE ) ||
+ ml->sml_values != NULL ))
+ {
+ /*
+ * attribute is obsolete,
+ * only allow replace/delete with no values
+ */
+ snprintf( textbuf, textlen,
+ "%s: attribute is obsolete",
+ ml->sml_type.bv_val );
+ *text = textbuf;
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+#endif
+
+ if ( ml->sml_op == LDAP_MOD_INCREMENT &&
+#ifdef SLAPD_REAL_SYNTAX
+ !is_at_syntax( ad->ad_type, SLAPD_REAL_SYNTAX ) &&
+#endif
+ !is_at_syntax( ad->ad_type, SLAPD_INTEGER_SYNTAX ) )
+ {
+ /*
+ * attribute values must be INTEGER or REAL
+ */
+ snprintf( textbuf, textlen,
+ "%s: attribute syntax inappropriate for increment",
+ ml->sml_type.bv_val );
+ *text = textbuf;
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ /*
+ * check values
+ */
+ if( ml->sml_values != NULL ) {
+ ber_len_t nvals;
+ slap_syntax_validate_func *validate =
+ ad->ad_type->sat_syntax->ssyn_validate;
+ slap_syntax_transform_func *pretty =
+ ad->ad_type->sat_syntax->ssyn_pretty;
+
+ if( !pretty && !validate ) {
+ *text = "no validator for syntax";
+ snprintf( textbuf, textlen,
+ "%s: no validator for syntax %s",
+ ml->sml_type.bv_val,
+ ad->ad_type->sat_syntax->ssyn_oid );
+ *text = textbuf;
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /*
+ * check that each value is valid per syntax
+ * and pretty if appropriate
+ */
+ for ( nvals = 0; !BER_BVISNULL( &ml->sml_values[nvals] ); nvals++ ) {
+ struct berval pval;
+
+ if ( pretty ) {
+ rc = ordered_value_pretty( ad,
+ &ml->sml_values[nvals], &pval, ctx );
+ } else {
+ rc = ordered_value_validate( ad,
+ &ml->sml_values[nvals], ml->sml_op );
+ }
+
+ if( rc != 0 ) {
+ snprintf( textbuf, textlen,
+ "%s: value #%ld invalid per syntax",
+ ml->sml_type.bv_val, (long) nvals );
+ *text = textbuf;
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if( pretty ) {
+ ber_memfree_x( ml->sml_values[nvals].bv_val, ctx );
+ ml->sml_values[nvals] = pval;
+ }
+ }
+ ml->sml_values[nvals].bv_len = 0;
+ ml->sml_numvals = nvals;
+
+ /*
+ * a rough single value check... an additional check is needed
+ * to catch add of single value to existing single valued attribute
+ */
+ if ((ml->sml_op == LDAP_MOD_ADD || ml->sml_op == LDAP_MOD_REPLACE)
+ && nvals > 1 && is_at_single_value( ad->ad_type ))
+ {
+ snprintf( textbuf, textlen,
+ "%s: multiple values provided",
+ ml->sml_type.bv_val );
+ *text = textbuf;
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ /* if the type has a normalizer, generate the
+ * normalized values. otherwise leave them NULL.
+ *
+ * this is different from the rule for attributes
+ * in an entry - in an attribute list, the normalized
+ * value is set equal to the non-normalized value
+ * when there is no normalizer.
+ */
+ if( nvals && ad->ad_type->sat_equality &&
+ ad->ad_type->sat_equality->smr_normalize )
+ {
+ ml->sml_nvalues = slap_sl_malloc(
+ (nvals+1)*sizeof(struct berval), ctx );
+
+ for ( nvals = 0; !BER_BVISNULL( &ml->sml_values[nvals] ); nvals++ ) {
+ rc = ordered_value_normalize(
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ ad,
+ ad->ad_type->sat_equality,
+ &ml->sml_values[nvals], &ml->sml_nvalues[nvals], ctx );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "<= str2entry NULL (ssyn_normalize %d)\n",
+ rc );
+ snprintf( textbuf, textlen,
+ "%s: value #%ld normalization failed",
+ ml->sml_type.bv_val, (long) nvals );
+ *text = textbuf;
+ BER_BVZERO( &ml->sml_nvalues[nvals] );
+ return rc;
+ }
+ }
+
+ BER_BVZERO( &ml->sml_nvalues[nvals] );
+ }
+
+ /* check for duplicates, but ignore Deletes.
+ */
+ if( nvals > 1 && ml->sml_op != LDAP_MOD_DELETE ) {
+ int i;
+ rc = slap_sort_vals( ml, text, &i, ctx );
+ if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
+ /* value exists already */
+ snprintf( textbuf, textlen,
+ "%s: value #%d provided more than once",
+ ml->sml_desc->ad_cname.bv_val, i );
+ *text = textbuf;
+ }
+ if ( rc )
+ return rc;
+ }
+ } else {
+ ml->sml_numvals = 0;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/* Sort a set of values. An (Attribute *) may be used interchangeably here
+ * instead of a (Modifications *) structure.
+ *
+ * Uses Quicksort + Insertion sort for small arrays
+ */
+
+int
+slap_sort_vals(
+ Modifications *ml,
+ const char **text,
+ int *dup,
+ void *ctx )
+{
+ AttributeDescription *ad;
+ MatchingRule *mr;
+ int istack[sizeof(int)*16];
+ int i, j, k, l, ir, jstack, match, *ix, itmp, nvals, rc = LDAP_SUCCESS;
+ int is_norm;
+ struct berval a, *cv;
+
+#define SMALL 8
+#define SWAP(a,b,tmp) tmp=(a);(a)=(b);(b)=tmp
+#define COMP(a,b) match=0; rc = ordered_value_match( &match, \
+ ad, mr, SLAP_MR_EQUALITY \
+ | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX \
+ | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH \
+ | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH, \
+ &(a), &(b), text );
+
+#define IX(x) ix[x]
+#define EXCH(x,y) SWAP(ix[x],ix[y],itmp)
+#define SETA(x) itmp = ix[x]; a = cv[itmp]
+#define GETA(x) ix[x] = itmp;
+#define SET(x,y) ix[x] = ix[y]
+
+ ad = ml->sml_desc;
+ nvals = ml->sml_numvals;
+ if ( nvals <= 1 )
+ goto ret;
+
+ /* For Modifications, sml_nvalues is NULL if normalization wasn't needed.
+ * For Attributes, sml_nvalues == sml_values when normalization isn't needed.
+ */
+ if ( ml->sml_nvalues && ml->sml_nvalues != ml->sml_values ) {
+ cv = ml->sml_nvalues;
+ is_norm = 1;
+ } else {
+ cv = ml->sml_values;
+ is_norm = 0;
+ }
+
+ if ( ad == slap_schema.si_ad_objectClass )
+ mr = NULL; /* shortcut matching */
+ else
+ mr = ad->ad_type->sat_equality;
+
+ /* record indices to preserve input ordering */
+ ix = slap_sl_malloc( nvals * sizeof(int), ctx );
+ for (i=0; i<nvals; i++) ix[i] = i;
+
+ ir = nvals-1;
+ l = 0;
+ jstack = 0;
+
+ for(;;) {
+ if (ir - l < SMALL) { /* Insertion sort */
+ match=1;
+ for (j=l+1;j<=ir;j++) {
+ SETA(j);
+ for (i=j-1;i>=0;i--) {
+ COMP(cv[IX(i)], a);
+ if ( match <= 0 )
+ break;
+ SET(i+1,i);
+ }
+ GETA(i+1);
+ if ( match == 0 ) goto done;
+ }
+ if ( jstack == 0 ) break;
+ ir = istack[jstack--];
+ l = istack[jstack--];
+ } else {
+ k = (l + ir) >> 1; /* Choose median of left, center, right */
+ EXCH(k, l+1);
+ COMP( cv[IX(l)], cv[IX(ir)] );
+ if ( match > 0 ) {
+ EXCH(l, ir);
+ } else if ( match == 0 ) {
+ i = ir;
+ break;
+ }
+ COMP( cv[IX(l+1)], cv[IX(ir)] );
+ if ( match > 0 ) {
+ EXCH(l+1, ir);
+ } else if ( match == 0 ) {
+ i = ir;
+ break;
+ }
+ COMP( cv[IX(l)], cv[IX(l+1)] );
+ if ( match > 0 ) {
+ EXCH(l, l+1);
+ } else if ( match == 0 ) {
+ i = l;
+ break;
+ }
+ i = l+1;
+ j = ir;
+ a = cv[IX(i)];
+ for(;;) {
+ do {
+ i++;
+ COMP( cv[IX(i)], a );
+ } while( match < 0 );
+ while( match > 0 ) {
+ j--;
+ COMP( cv[IX(j)], a );
+ }
+ if (j < i) {
+ match = 1;
+ break;
+ }
+ if ( match == 0 ) {
+ i = l+1;
+ break;
+ }
+ EXCH(i,j);
+ }
+ if ( match == 0 )
+ break;
+ EXCH(l+1,j);
+ jstack += 2;
+ if (ir-i+1 > j-l) {
+ istack[jstack] = ir;
+ istack[jstack-1] = i;
+ ir = j;
+ } else {
+ istack[jstack] = j;
+ istack[jstack-1] = l;
+ l = i;
+ }
+ }
+ }
+ done:
+ if ( match == 0 && i >= 0 )
+ *dup = ix[i];
+
+ /* For sorted attributes, put the values in index order */
+ if ( rc == LDAP_SUCCESS && match &&
+ ( ad->ad_type->sat_flags & SLAP_AT_SORTED_VAL )) {
+ BerVarray tmpv = slap_sl_malloc( sizeof( struct berval ) * nvals, ctx );
+ for ( i = 0; i<nvals; i++ )
+ tmpv[i] = cv[ix[i]];
+ for ( i = 0; i<nvals; i++ )
+ cv[i] = tmpv[i];
+ /* Check if the non-normalized array needs to move too */
+ if ( is_norm ) {
+ cv = ml->sml_values;
+ for ( i = 0; i<nvals; i++ )
+ tmpv[i] = cv[ix[i]];
+ for ( i = 0; i<nvals; i++ )
+ cv[i] = tmpv[i];
+ }
+ slap_sl_free( tmpv, ctx );
+ }
+
+ slap_sl_free( ix, ctx );
+
+ if ( rc == LDAP_SUCCESS && match == 0 ) {
+ /* value exists already */
+ assert( i >= 0 );
+ assert( i < nvals );
+ rc = LDAP_TYPE_OR_VALUE_EXISTS;
+ }
+ ret:
+ return rc;
+}
+
+/* Enter with bv->bv_len = sizeof buffer, returns with
+ * actual length of string
+ */
+void slap_timestamp( time_t *tm, struct berval *bv )
+{
+ struct tm ltm;
+
+ ldap_pvt_gmtime( tm, &ltm );
+
+ bv->bv_len = lutil_gentime( bv->bv_val, bv->bv_len, &ltm );
+}
+
+/* Called for all modify and modrdn ops. If the current op was replicated
+ * from elsewhere, all of the attrs should already be present.
+ */
+void slap_mods_opattrs(
+ Operation *op,
+ Modifications **modsp,
+ int manage_ctxcsn )
+{
+ struct berval name, timestamp, csn = BER_BVNULL;
+ struct berval nname;
+ char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+ char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE ];
+ Modifications *mod, **modtail, *modlast;
+ int gotcsn = 0, gotmname = 0, gotmtime = 0;
+
+ if ( SLAP_LASTMOD( op->o_bd ) && !op->orm_no_opattrs ) {
+ char *ptr;
+ timestamp.bv_val = timebuf;
+ for ( modtail = modsp; *modtail; modtail = &(*modtail)->sml_next ) {
+ if ( (*modtail)->sml_op != LDAP_MOD_ADD &&
+ (*modtail)->sml_op != SLAP_MOD_SOFTADD &&
+ (*modtail)->sml_op != SLAP_MOD_ADD_IF_NOT_PRESENT &&
+ (*modtail)->sml_op != LDAP_MOD_REPLACE )
+ {
+ continue;
+ }
+
+ if ( (*modtail)->sml_desc == slap_schema.si_ad_entryCSN )
+ {
+ csn = (*modtail)->sml_values[0];
+ gotcsn = 1;
+
+ } else if ( (*modtail)->sml_desc == slap_schema.si_ad_modifiersName )
+ {
+ gotmname = 1;
+
+ } else if ( (*modtail)->sml_desc == slap_schema.si_ad_modifyTimestamp )
+ {
+ gotmtime = 1;
+ }
+ }
+
+ if ( BER_BVISEMPTY( &op->o_csn )) {
+ if ( !gotcsn ) {
+ csn.bv_val = csnbuf;
+ csn.bv_len = sizeof( csnbuf );
+ slap_get_csn( op, &csn, manage_ctxcsn );
+
+ } else {
+ if ( manage_ctxcsn ) {
+ slap_queue_csn( op, &csn );
+ }
+ }
+
+ } else {
+ csn = op->o_csn;
+ }
+
+ ptr = ber_bvchr( &csn, '#' );
+ if ( ptr ) {
+ timestamp.bv_len = STRLENOF("YYYYMMDDHHMMSSZ");
+ AC_MEMCPY( timebuf, csn.bv_val, timestamp.bv_len );
+ timebuf[timestamp.bv_len-1] = 'Z';
+ timebuf[timestamp.bv_len] = '\0';
+
+ } else {
+ time_t now = slap_get_time();
+
+ timestamp.bv_len = sizeof(timebuf);
+
+ slap_timestamp( &now, &timestamp );
+ }
+
+ if ( BER_BVISEMPTY( &op->o_dn ) ) {
+ BER_BVSTR( &name, SLAPD_ANONYMOUS );
+ nname = name;
+
+ } else {
+ name = op->o_dn;
+ nname = op->o_ndn;
+ }
+
+ if ( !gotcsn ) {
+ mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+ mod->sml_op = LDAP_MOD_REPLACE;
+ mod->sml_flags = SLAP_MOD_INTERNAL;
+ mod->sml_next = NULL;
+ BER_BVZERO( &mod->sml_type );
+ mod->sml_desc = slap_schema.si_ad_entryCSN;
+ mod->sml_numvals = 1;
+ mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
+ ber_dupbv( &mod->sml_values[0], &csn );
+ BER_BVZERO( &mod->sml_values[1] );
+ assert( !BER_BVISNULL( &mod->sml_values[0] ) );
+ mod->sml_nvalues = NULL;
+ *modtail = mod;
+ modlast = mod;
+ modtail = &mod->sml_next;
+ }
+
+ if ( !gotmname ) {
+ mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+ mod->sml_op = LDAP_MOD_REPLACE;
+ mod->sml_flags = SLAP_MOD_INTERNAL;
+ mod->sml_next = NULL;
+ BER_BVZERO( &mod->sml_type );
+ mod->sml_desc = slap_schema.si_ad_modifiersName;
+ mod->sml_numvals = 1;
+ mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
+ ber_dupbv( &mod->sml_values[0], &name );
+ BER_BVZERO( &mod->sml_values[1] );
+ assert( !BER_BVISNULL( &mod->sml_values[0] ) );
+ mod->sml_nvalues =
+ (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
+ ber_dupbv( &mod->sml_nvalues[0], &nname );
+ BER_BVZERO( &mod->sml_nvalues[1] );
+ assert( !BER_BVISNULL( &mod->sml_nvalues[0] ) );
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ }
+
+ if ( !gotmtime ) {
+ mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+ mod->sml_op = LDAP_MOD_REPLACE;
+ mod->sml_flags = SLAP_MOD_INTERNAL;
+ mod->sml_next = NULL;
+ BER_BVZERO( &mod->sml_type );
+ mod->sml_desc = slap_schema.si_ad_modifyTimestamp;
+ mod->sml_numvals = 1;
+ mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
+ ber_dupbv( &mod->sml_values[0], &timestamp );
+ BER_BVZERO( &mod->sml_values[1] );
+ assert( !BER_BVISNULL( &mod->sml_values[0] ) );
+ mod->sml_nvalues = NULL;
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ }
+ }
+}
+
+int
+slap_parse_modlist(
+ Operation *op,
+ SlapReply *rs,
+ BerElement *ber,
+ req_modify_s *ms )
+{
+ ber_tag_t tag;
+ ber_len_t len;
+ char *last;
+ Modifications **modtail = &ms->rs_mods.rs_modlist;
+
+ ms->rs_mods.rs_modlist = NULL;
+ ms->rs_increment = 0;
+
+ rs->sr_err = LDAP_SUCCESS;
+
+ /* collect modifications & save for later */
+ for ( tag = ber_first_element( ber, &len, &last );
+ tag != LBER_DEFAULT;
+ tag = ber_next_element( ber, &len, last ) )
+ {
+ ber_int_t mop;
+ Modifications tmp, *mod;
+
+ tmp.sml_nvalues = NULL;
+
+ if ( ber_scanf( ber, "{e{m[W]}}", &mop,
+ &tmp.sml_type, &tmp.sml_values ) == LBER_ERROR )
+ {
+ rs->sr_text = "decoding modlist error";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ mod = (Modifications *) ch_malloc( sizeof(Modifications) );
+ mod->sml_op = mop;
+ mod->sml_flags = 0;
+ mod->sml_type = tmp.sml_type;
+ mod->sml_values = tmp.sml_values;
+ mod->sml_nvalues = NULL;
+ mod->sml_desc = NULL;
+ mod->sml_next = NULL;
+ *modtail = mod;
+
+ switch( mop ) {
+ case LDAP_MOD_ADD:
+ if ( mod->sml_values == NULL ) {
+ rs->sr_text = "modify/add operation requires values";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ /* fall through */
+
+ case LDAP_MOD_DELETE:
+ case LDAP_MOD_REPLACE:
+ break;
+
+ case LDAP_MOD_INCREMENT:
+ if( op->o_protocol >= LDAP_VERSION3 ) {
+ ms->rs_increment++;
+ if ( mod->sml_values == NULL ) {
+ rs->sr_text = "modify/increment operation requires value";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ if ( !BER_BVISNULL( &mod->sml_values[ 1 ] ) ) {
+ rs->sr_text = "modify/increment operation requires single value";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ break;
+ }
+ /* fall thru */
+
+ default:
+ rs->sr_text = "unrecognized modify operation";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ modtail = &mod->sml_next;
+ }
+ *modtail = NULL;
+
+done:
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ slap_mods_free( ms->rs_mods.rs_modlist, 1 );
+ ms->rs_mods.rs_modlist = NULL;
+ ms->rs_increment = 0;
+ }
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/modrdn.c b/servers/slapd/modrdn.c
new file mode 100644
index 0000000..4777639
--- /dev/null
+++ b/servers/slapd/modrdn.c
@@ -0,0 +1,547 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright 1999, Juan C. Gomez, All rights reserved.
+ * This software is not subject to any license of Silicon Graphics
+ * Inc. or Purdue University.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * without restriction or fee of any kind as long as this notice
+ * is preserved.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+
+int
+do_modrdn(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ struct berval dn = BER_BVNULL;
+ struct berval newrdn = BER_BVNULL;
+ struct berval newSuperior = BER_BVNULL;
+ ber_int_t deloldrdn;
+
+ struct berval pnewSuperior = BER_BVNULL;
+
+ struct berval nnewSuperior = BER_BVNULL;
+
+ ber_len_t length;
+
+ Debug( LDAP_DEBUG_TRACE, "%s do_modrdn\n",
+ op->o_log_prefix );
+ /*
+ * Parse the modrdn request. It looks like this:
+ *
+ * ModifyRDNRequest := SEQUENCE {
+ * entry DistinguishedName,
+ * newrdn RelativeDistinguishedName
+ * deleteoldrdn BOOLEAN,
+ * newSuperior [0] LDAPDN OPTIONAL (v3 Only!)
+ * }
+ */
+
+ if ( ber_scanf( op->o_ber, "{mmb", &dn, &newrdn, &deloldrdn )
+ == LBER_ERROR )
+ {
+ Debug( LDAP_DEBUG_ANY, "%s do_modrdn: ber_scanf failed\n",
+ op->o_log_prefix );
+ send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
+ return SLAPD_DISCONNECT;
+ }
+
+ /* Check for newSuperior parameter, if present scan it */
+
+ if ( ber_peek_tag( op->o_ber, &length ) == LDAP_TAG_NEWSUPERIOR ) {
+ if ( op->o_protocol < LDAP_VERSION3 ) {
+ /* Connection record indicates v2 but field
+ * newSuperior is present: report error.
+ */
+ Debug( LDAP_DEBUG_ANY,
+ "%s do_modrdn: newSuperior requires LDAPv3\n",
+ op->o_log_prefix );
+
+ send_ldap_discon( op, rs,
+ LDAP_PROTOCOL_ERROR, "newSuperior requires LDAPv3" );
+ rs->sr_err = SLAPD_DISCONNECT;
+ goto cleanup;
+ }
+
+ if ( ber_scanf( op->o_ber, "m", &newSuperior )
+ == LBER_ERROR ) {
+
+ Debug( LDAP_DEBUG_ANY, "%s do_modrdn: ber_scanf(\"m\") failed\n",
+ op->o_log_prefix );
+
+ send_ldap_discon( op, rs,
+ LDAP_PROTOCOL_ERROR, "decoding error" );
+ rs->sr_err = SLAPD_DISCONNECT;
+ goto cleanup;
+ }
+ op->orr_newSup = &pnewSuperior;
+ op->orr_nnewSup = &nnewSuperior;
+ }
+
+ Debug( LDAP_DEBUG_ARGS,
+ "do_modrdn: dn (%s) newrdn (%s) newsuperior (%s)\n",
+ dn.bv_val, newrdn.bv_val,
+ newSuperior.bv_len ? newSuperior.bv_val : "" );
+
+ if ( ber_scanf( op->o_ber, /*{*/ "}") == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_modrdn: ber_scanf failed\n",
+ op->o_log_prefix );
+ send_ldap_discon( op, rs,
+ LDAP_PROTOCOL_ERROR, "decoding error" );
+ rs->sr_err = SLAPD_DISCONNECT;
+ goto cleanup;
+ }
+
+ if( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_modrdn: get_ctrls failed\n",
+ op->o_log_prefix );
+ /* get_ctrls has sent results. Now clean up. */
+ goto cleanup;
+ }
+
+ rs->sr_err = dnPrettyNormal( NULL, &dn, &op->o_req_dn, &op->o_req_ndn, op->o_tmpmemctx );
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_modrdn: invalid dn (%s)\n",
+ op->o_log_prefix, dn.bv_val );
+ send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid DN" );
+ goto cleanup;
+ }
+
+ /* FIXME: should have/use rdnPretty / rdnNormalize routines */
+
+ rs->sr_err = dnPrettyNormal( NULL, &newrdn, &op->orr_newrdn, &op->orr_nnewrdn, op->o_tmpmemctx );
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_modrdn: invalid newrdn (%s)\n",
+ op->o_log_prefix, newrdn.bv_val );
+ send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid new RDN" );
+ goto cleanup;
+ }
+
+ if( rdn_validate( &op->orr_newrdn ) != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_modrdn: invalid rdn (%s)\n",
+ op->o_log_prefix, op->orr_newrdn.bv_val );
+ send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid new RDN" );
+ goto cleanup;
+ }
+
+ if( op->orr_newSup ) {
+ rs->sr_err = dnPrettyNormal( NULL, &newSuperior, &pnewSuperior,
+ &nnewSuperior, op->o_tmpmemctx );
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s do_modrdn: invalid newSuperior (%s)\n",
+ op->o_log_prefix, newSuperior.bv_val );
+ send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid newSuperior" );
+ goto cleanup;
+ }
+ }
+
+ Debug( LDAP_DEBUG_STATS, "%s MODRDN dn=\"%s\"\n",
+ op->o_log_prefix, op->o_req_dn.bv_val );
+
+ op->orr_deleteoldrdn = deloldrdn;
+ op->orr_modlist = NULL;
+
+ /* prepare modlist of modifications from old/new RDN */
+ rs->sr_err = slap_modrdn2mods( op, rs );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ op->o_bd = frontendDB;
+ rs->sr_err = frontendDB->be_modrdn( op, rs );
+
+ if ( rs->sr_err == SLAPD_ASYNCOP ) {
+ /* skip cleanup */
+ return rs->sr_err;
+ }
+ if( rs->sr_err == LDAP_TXN_SPECIFY_OKAY ) {
+ /* skip cleanup */
+ return rs->sr_err;
+ }
+
+cleanup:
+ op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
+
+ op->o_tmpfree( op->orr_newrdn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( op->orr_nnewrdn.bv_val, op->o_tmpmemctx );
+
+ if ( op->orr_modlist != NULL )
+ slap_mods_free( op->orr_modlist, 1 );
+
+ if ( !BER_BVISNULL( &pnewSuperior ) ) {
+ op->o_tmpfree( pnewSuperior.bv_val, op->o_tmpmemctx );
+ }
+ if ( !BER_BVISNULL( &nnewSuperior ) ) {
+ op->o_tmpfree( nnewSuperior.bv_val, op->o_tmpmemctx );
+ }
+
+ return rs->sr_err;
+}
+
+int
+fe_op_modrdn( Operation *op, SlapReply *rs )
+{
+ struct berval dest_ndn = BER_BVNULL, dest_pndn, pdn = BER_BVNULL;
+ BackendDB *op_be, *bd = op->o_bd;
+ ber_slen_t diff;
+
+ if( op->o_req_ndn.bv_len == 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_modrdn: root dse!\n",
+ op->o_log_prefix );
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "cannot rename the root DSE" );
+ goto cleanup;
+
+ } else if ( bvmatch( &op->o_req_ndn, &frontendDB->be_schemandn ) ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_modrdn: subschema subentry: %s (%ld)\n",
+ op->o_log_prefix, frontendDB->be_schemandn.bv_val, (long)frontendDB->be_schemandn.bv_len );
+
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "cannot rename subschema subentry" );
+ goto cleanup;
+ }
+
+ if( op->orr_nnewSup ) {
+ dest_pndn = *op->orr_nnewSup;
+ } else {
+ dnParent( &op->o_req_ndn, &dest_pndn );
+ }
+ build_new_dn( &dest_ndn, &dest_pndn, &op->orr_nnewrdn, op->o_tmpmemctx );
+
+ diff = (ber_slen_t) dest_ndn.bv_len - (ber_slen_t) op->o_req_ndn.bv_len;
+ if ( diff > 0 ? dnIsSuffix( &dest_ndn, &op->o_req_ndn )
+ : diff < 0 && dnIsSuffix( &op->o_req_ndn, &dest_ndn ) )
+ {
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ diff > 0 ? "cannot place an entry below itself"
+ : "cannot place an entry above itself" );
+ goto cleanup;
+ }
+
+ /*
+ * We could be serving multiple database backends. Select the
+ * appropriate one, or send a referral to our "referral server"
+ * if we don't hold it.
+ */
+ op->o_bd = select_backend( &op->o_req_ndn, 1 );
+ if ( op->o_bd == NULL ) {
+ op->o_bd = bd;
+ rs->sr_ref = referral_rewrite( default_referral,
+ NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ if (!rs->sr_ref) rs->sr_ref = default_referral;
+
+ if ( rs->sr_ref != NULL ) {
+ rs->sr_err = LDAP_REFERRAL;
+ send_ldap_result( op, rs );
+
+ if (rs->sr_ref != default_referral) ber_bvarray_free( rs->sr_ref );
+ } else {
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "no global superior knowledge" );
+ }
+ goto cleanup;
+ }
+
+ /* If we've got a glued backend, check the real backend */
+ op_be = op->o_bd;
+ if ( SLAP_GLUE_INSTANCE( op->o_bd )) {
+ op->o_bd = select_backend( &op->o_req_ndn, 0 );
+ }
+
+ /* check restrictions */
+ if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto cleanup;
+ }
+
+ /* check for referrals */
+ if ( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) {
+ goto cleanup;
+ }
+
+ /* check that destination DN is in the same backend as source DN */
+ if ( select_backend( &dest_ndn, 0 ) != op->o_bd ) {
+ send_ldap_error( op, rs, LDAP_AFFECTS_MULTIPLE_DSAS,
+ "cannot rename between DSAs" );
+ goto cleanup;
+ }
+
+ /*
+ * do the modrdn if 1 && (2 || 3)
+ * 1) there is a modrdn function implemented in this backend;
+ * 2) this backend is the provider for what it holds;
+ * 3) it's a replica and the dn supplied is the update_ndn.
+ */
+ if ( op->o_bd->be_modrdn ) {
+ /* do the update here */
+ int repl_user = be_isupdate( op );
+ if ( !SLAP_SINGLE_SHADOW(op->o_bd) || repl_user )
+ {
+ if ( op->o_txnSpec ) {
+ txn_preop( op, rs );
+ goto cleanup;
+ }
+
+ op->o_bd = op_be;
+ op->o_bd->be_modrdn( op, rs );
+
+ if ( op->o_bd->be_delete ) {
+ struct berval org_req_dn = BER_BVNULL;
+ struct berval org_req_ndn = BER_BVNULL;
+ struct berval org_dn = BER_BVNULL;
+ struct berval org_ndn = BER_BVNULL;
+ int org_managedsait;
+
+ org_req_dn = op->o_req_dn;
+ org_req_ndn = op->o_req_ndn;
+ org_dn = op->o_dn;
+ org_ndn = op->o_ndn;
+ org_managedsait = get_manageDSAit( op );
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ op->o_managedsait = SLAP_CONTROL_NONCRITICAL;
+
+ while ( rs->sr_err == LDAP_SUCCESS &&
+ op->o_delete_glue_parent ) {
+ op->o_delete_glue_parent = 0;
+ if ( !be_issuffix( op->o_bd, &op->o_req_ndn )) {
+ slap_callback cb = { NULL };
+ cb.sc_response = slap_null_cb;
+ dnParent( &op->o_req_ndn, &pdn );
+ op->o_req_dn = pdn;
+ op->o_req_ndn = pdn;
+ op->o_callback = &cb;
+ op->o_bd->be_delete( op, rs );
+ } else {
+ break;
+ }
+ }
+ op->o_managedsait = org_managedsait;
+ op->o_dn = org_dn;
+ op->o_ndn = org_ndn;
+ op->o_req_dn = org_req_dn;
+ op->o_req_ndn = org_req_ndn;
+ op->o_delete_glue_parent = 0;
+ }
+
+ } else {
+ BerVarray defref = op->o_bd->be_update_refs
+ ? op->o_bd->be_update_refs : default_referral;
+
+ if ( defref != NULL ) {
+ rs->sr_ref = referral_rewrite( defref,
+ NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+ if (!rs->sr_ref) rs->sr_ref = defref;
+
+ rs->sr_err = LDAP_REFERRAL;
+ send_ldap_result( op, rs );
+
+ if (rs->sr_ref != defref) ber_bvarray_free( rs->sr_ref );
+ } else {
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "shadow context; no update referral" );
+ }
+ }
+ } else {
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "operation not supported within namingContext" );
+ }
+
+cleanup:;
+ if ( dest_ndn.bv_val != NULL )
+ ber_memfree_x( dest_ndn.bv_val, op->o_tmpmemctx );
+ op->o_bd = bd;
+ return rs->sr_err;
+}
+
+/* extracted from slap_modrdn2mods() */
+static int
+mod_op_add_val(
+ Operation *op,
+ AttributeDescription * const desc,
+ struct berval * const val,
+ short const sm_op )
+{
+ int rv = LDAP_SUCCESS;
+ Modifications *mod_tmp;
+ mod_tmp = ( Modifications * )ch_malloc( sizeof( Modifications ) );
+ mod_tmp->sml_desc = desc;
+ BER_BVZERO( &mod_tmp->sml_type );
+ mod_tmp->sml_numvals = 1;
+ mod_tmp->sml_values = ( BerVarray )ch_malloc( 2 * sizeof( struct berval ) );
+ ber_dupbv( &mod_tmp->sml_values[0], val );
+ mod_tmp->sml_values[1].bv_val = NULL;
+ if( desc->ad_type->sat_equality && desc->ad_type->sat_equality->smr_normalize) {
+ mod_tmp->sml_nvalues = ( BerVarray )ch_malloc( 2 * sizeof( struct berval ) );
+ rv = desc->ad_type->sat_equality->smr_normalize(
+ SLAP_MR_EQUALITY|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
+ desc->ad_type->sat_syntax,
+ desc->ad_type->sat_equality,
+ &mod_tmp->sml_values[0],
+ &mod_tmp->sml_nvalues[0], NULL );
+ if (rv != LDAP_SUCCESS) {
+ ch_free(mod_tmp->sml_nvalues);
+ ch_free(mod_tmp->sml_values[0].bv_val);
+ ch_free(mod_tmp->sml_values);
+ ch_free(mod_tmp);
+ goto done;
+ }
+ mod_tmp->sml_nvalues[1].bv_val = NULL;
+ } else {
+ mod_tmp->sml_nvalues = NULL;
+ }
+ mod_tmp->sml_op = sm_op;
+ mod_tmp->sml_flags = 0;
+ mod_tmp->sml_next = op->orr_modlist;
+ op->orr_modlist = mod_tmp;
+done:
+ return rv;
+}
+
+int
+slap_modrdn2mods(
+ Operation *op,
+ SlapReply *rs )
+{
+ int a_cnt, d_cnt;
+ LDAPRDN old_rdn = NULL;
+ LDAPRDN new_rdn = NULL;
+
+ assert( !BER_BVISEMPTY( &op->oq_modrdn.rs_newrdn ) );
+
+ /* if requestDN is empty, silently reset deleteOldRDN */
+ if ( BER_BVISEMPTY( &op->o_req_dn ) ) op->orr_deleteoldrdn = 0;
+
+ if ( ldap_bv2rdn_x( &op->oq_modrdn.rs_newrdn, &new_rdn,
+ (char **)&rs->sr_text, LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "%s slap_modrdn2mods: can't figure out "
+ "type(s)/value(s) of newrdn\n",
+ op->o_log_prefix );
+ rs->sr_err = LDAP_INVALID_DN_SYNTAX;
+ rs->sr_text = "unknown type(s)/value(s) used in RDN";
+ goto done;
+ }
+
+ if ( op->oq_modrdn.rs_deleteoldrdn ) {
+ if ( ldap_bv2rdn_x( &op->o_req_dn, &old_rdn,
+ (char **)&rs->sr_text, LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "%s slap_modrdn2mods: can't figure out "
+ "type(s)/value(s) of oldrdn\n",
+ op->o_log_prefix );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "cannot parse RDN from old DN";
+ goto done;
+ }
+ }
+ rs->sr_text = NULL;
+
+ /* Add new attribute values to the entry */
+ for ( a_cnt = 0; new_rdn[a_cnt]; a_cnt++ ) {
+ AttributeDescription *desc = NULL;
+
+ rs->sr_err = slap_bv2ad( &new_rdn[a_cnt]->la_attr, &desc, &rs->sr_text );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "%s slap_modrdn2mods: %s: %s (new)\n",
+ op->o_log_prefix,
+ rs->sr_text,
+ new_rdn[ a_cnt ]->la_attr.bv_val );
+ goto done;
+ }
+
+ if ( !desc->ad_type->sat_equality ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "%s slap_modrdn2mods: %s: %s (new)\n",
+ op->o_log_prefix,
+ rs->sr_text,
+ new_rdn[ a_cnt ]->la_attr.bv_val );
+ rs->sr_text = "naming attribute has no equality matching rule";
+ rs->sr_err = LDAP_NAMING_VIOLATION;
+ goto done;
+ }
+
+ /* Apply modification */
+ rs->sr_err = mod_op_add_val( op, desc, &new_rdn[a_cnt]->la_value, SLAP_MOD_SOFTADD );
+ if (rs->sr_err != LDAP_SUCCESS)
+ goto done;
+ }
+
+ /* Remove old rdn value if required */
+ if ( op->orr_deleteoldrdn ) {
+ for ( d_cnt = 0; old_rdn[d_cnt]; d_cnt++ ) {
+ AttributeDescription *desc = NULL;
+
+ rs->sr_err = slap_bv2ad( &old_rdn[d_cnt]->la_attr, &desc, &rs->sr_text );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "%s slap_modrdn2mods: %s: %s (old)\n",
+ op->o_log_prefix,
+ rs->sr_text,
+ old_rdn[d_cnt]->la_attr.bv_val );
+ goto done;
+ }
+
+ /* Apply modification */
+ rs->sr_err = mod_op_add_val( op, desc, &old_rdn[d_cnt]->la_value, LDAP_MOD_DELETE );
+ if (rs->sr_err != LDAP_SUCCESS)
+ goto done;
+ }
+ }
+
+done:
+
+ /* LDAP v2 supporting correct attribute handling. */
+ if ( rs->sr_err != LDAP_SUCCESS && op->orr_modlist != NULL ) {
+ slap_mods_free( op->orr_modlist, 1 );
+ op->orr_modlist = NULL;
+ }
+
+ if ( new_rdn != NULL ) {
+ ldap_rdnfree_x( new_rdn, op->o_tmpmemctx );
+ }
+ if ( old_rdn != NULL ) {
+ ldap_rdnfree_x( old_rdn, op->o_tmpmemctx );
+ }
+
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/mods.c b/servers/slapd/mods.c
new file mode 100644
index 0000000..989274f
--- /dev/null
+++ b/servers/slapd/mods.c
@@ -0,0 +1,487 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "lutil.h"
+
+int
+modify_add_values(
+ Entry *e,
+ Modification *mod,
+ int permissive,
+ const char **text,
+ char *textbuf,
+ size_t textlen )
+{
+ int rc;
+ const char *op;
+ Attribute *a;
+ Modification pmod = *mod;
+
+ switch ( mod->sm_op ) {
+ case LDAP_MOD_ADD:
+ op = "add";
+ break;
+ case LDAP_MOD_REPLACE:
+ op = "replace";
+ break;
+ default:
+ op = "?";
+ assert( 0 );
+ }
+
+ /* FIXME: Catch old code that doesn't set sm_numvals.
+ */
+ if ( !BER_BVISNULL( &mod->sm_values[mod->sm_numvals] )) {
+ unsigned i;
+ for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ );
+ assert( mod->sm_numvals == i );
+ }
+
+ /* check if values to add exist in attribute */
+ a = attr_find( e->e_attrs, mod->sm_desc );
+ if ( a != NULL ) {
+ MatchingRule *mr;
+ struct berval *cvals;
+ int rc;
+ unsigned i, p, flags;
+
+ mr = mod->sm_desc->ad_type->sat_equality;
+ if( mr == NULL || !mr->smr_match ) {
+ /* do not allow add of additional attribute
+ if no equality rule exists */
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/%s: %s: no equality matching rule",
+ op, mod->sm_desc->ad_cname.bv_val );
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+
+ if ( permissive ) {
+ i = mod->sm_numvals;
+ pmod.sm_values = (BerVarray)ch_malloc(
+ (i + 1) * sizeof( struct berval ));
+ if ( pmod.sm_nvalues != NULL ) {
+ pmod.sm_nvalues = (BerVarray)ch_malloc(
+ (i + 1) * sizeof( struct berval ));
+ }
+ }
+
+ /* no normalization is done in this routine nor
+ * in the matching routines called by this routine.
+ * values are now normalized once on input to the
+ * server (whether from LDAP or from the underlying
+ * database).
+ */
+ if ( a->a_desc == slap_schema.si_ad_objectClass ) {
+ /* Needed by ITS#5517 */
+ flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX;
+
+ } else {
+ flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX;
+ }
+ if ( mod->sm_nvalues ) {
+ flags |= SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH;
+ cvals = mod->sm_nvalues;
+ } else {
+ cvals = mod->sm_values;
+ }
+ for ( p = i = 0; i < mod->sm_numvals; i++ ) {
+ unsigned slot;
+
+ rc = attr_valfind( a, flags, &cvals[i], &slot, NULL );
+ if ( rc == LDAP_SUCCESS ) {
+ if ( !permissive ) {
+ /* value already exists */
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/%s: %s: value #%u already exists",
+ op, mod->sm_desc->ad_cname.bv_val, i );
+ return LDAP_TYPE_OR_VALUE_EXISTS;
+ }
+ } else if ( rc != LDAP_NO_SUCH_ATTRIBUTE ) {
+ return rc;
+ }
+
+ if ( permissive && rc ) {
+ if ( pmod.sm_nvalues ) {
+ pmod.sm_nvalues[p] = mod->sm_nvalues[i];
+ }
+ pmod.sm_values[p++] = mod->sm_values[i];
+ }
+ }
+
+ if ( permissive ) {
+ if ( p == 0 ) {
+ /* all new values match exist */
+ ch_free( pmod.sm_values );
+ if ( pmod.sm_nvalues ) ch_free( pmod.sm_nvalues );
+ return LDAP_SUCCESS;
+ }
+
+ BER_BVZERO( &pmod.sm_values[p] );
+ if ( pmod.sm_nvalues ) {
+ BER_BVZERO( &pmod.sm_nvalues[p] );
+ }
+ }
+ }
+
+ /* no - add them */
+ if ( mod->sm_desc->ad_type->sat_flags & SLAP_AT_ORDERED_VAL ) {
+ rc = ordered_value_add( e, mod->sm_desc, a,
+ pmod.sm_values, pmod.sm_nvalues );
+ } else {
+ rc = attr_merge( e, mod->sm_desc, pmod.sm_values, pmod.sm_nvalues );
+ }
+
+ if ( a != NULL && permissive ) {
+ ch_free( pmod.sm_values );
+ if ( pmod.sm_nvalues ) ch_free( pmod.sm_nvalues );
+ }
+
+ if ( rc != 0 ) {
+ /* this should return result of attr_merge */
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/%s: %s: merge error (%d)",
+ op, mod->sm_desc->ad_cname.bv_val, rc );
+ return LDAP_OTHER;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int
+modify_delete_values(
+ Entry *e,
+ Modification *m,
+ int perm,
+ const char **text,
+ char *textbuf, size_t textlen )
+{
+ return modify_delete_vindex( e, m, perm, text, textbuf, textlen, NULL );
+}
+
+int
+modify_delete_vindex(
+ Entry *e,
+ Modification *mod,
+ int permissive,
+ const char **text,
+ char *textbuf, size_t textlen,
+ int *idx )
+{
+ Attribute *a;
+ MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
+ struct berval *cvals;
+ int *id2 = NULL;
+ int rc = 0;
+ unsigned i, j, flags;
+ char dummy = '\0';
+
+ /*
+ * If permissive is set, then the non-existence of an
+ * attribute is not treated as an error.
+ */
+
+ /* delete the entire attribute */
+ if ( mod->sm_values == NULL ) {
+ rc = attr_delete( &e->e_attrs, mod->sm_desc );
+
+ if( permissive ) {
+ rc = LDAP_SUCCESS;
+ } else if( rc != LDAP_SUCCESS ) {
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: no such attribute",
+ mod->sm_desc->ad_cname.bv_val );
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ }
+ return rc;
+ }
+
+ /* FIXME: Catch old code that doesn't set sm_numvals.
+ */
+ if ( !BER_BVISNULL( &mod->sm_values[mod->sm_numvals] )) {
+ for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ );
+ assert( mod->sm_numvals == i );
+ }
+ if ( !idx ) {
+ id2 = ch_malloc( mod->sm_numvals * sizeof( int ));
+ idx = id2;
+ }
+
+ if( mr == NULL || !mr->smr_match ) {
+ /* disallow specific attributes from being deleted if
+ no equality rule */
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: no equality matching rule",
+ mod->sm_desc->ad_cname.bv_val );
+ rc = LDAP_INAPPROPRIATE_MATCHING;
+ goto return_result;
+ }
+
+ /* delete specific values - find the attribute first */
+ if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
+ if( permissive ) {
+ rc = LDAP_SUCCESS;
+ goto return_result;
+ }
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: no such attribute",
+ mod->sm_desc->ad_cname.bv_val );
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ goto return_result;
+ }
+
+ if ( a->a_desc == slap_schema.si_ad_objectClass ) {
+ /* Needed by ITS#5517,ITS#5963 */
+ flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX;
+
+ } else {
+ flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX;
+ }
+ if ( mod->sm_nvalues ) {
+ flags |= SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
+ | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH;
+ cvals = mod->sm_nvalues;
+ } else {
+ cvals = mod->sm_values;
+ }
+
+ /* Locate values to delete */
+ for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ ) {
+ unsigned sort;
+ rc = attr_valfind( a, flags, &cvals[i], &sort, NULL );
+ if ( rc == LDAP_SUCCESS ) {
+ idx[i] = sort;
+ } else if ( rc == LDAP_NO_SUCH_ATTRIBUTE ) {
+ if ( permissive ) {
+ idx[i] = -1;
+ continue;
+ }
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: no such value",
+ mod->sm_desc->ad_cname.bv_val );
+ goto return_result;
+ } else {
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: matching rule failed",
+ mod->sm_desc->ad_cname.bv_val );
+ goto return_result;
+ }
+ }
+
+ /* Delete the values */
+ for ( i = 0; i < mod->sm_numvals; i++ ) {
+ /* Skip permissive values that weren't found */
+ if ( idx[i] < 0 )
+ continue;
+ /* Skip duplicate delete specs */
+ if ( a->a_vals[idx[i]].bv_val == &dummy )
+ continue;
+ /* delete value and mark it as gone */
+ free( a->a_vals[idx[i]].bv_val );
+ a->a_vals[idx[i]].bv_val = &dummy;
+ if( a->a_nvals != a->a_vals ) {
+ free( a->a_nvals[idx[i]].bv_val );
+ a->a_nvals[idx[i]].bv_val = &dummy;
+ }
+ a->a_numvals--;
+ }
+
+ /* compact array skipping dummies */
+ for ( i = 0, j = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ ) {
+ /* skip dummies */
+ if( a->a_vals[i].bv_val == &dummy ) {
+ assert( a->a_nvals[i].bv_val == &dummy );
+ continue;
+ }
+ if ( j != i ) {
+ a->a_vals[ j ] = a->a_vals[ i ];
+ if (a->a_nvals != a->a_vals) {
+ a->a_nvals[ j ] = a->a_nvals[ i ];
+ }
+ }
+ j++;
+ }
+
+ BER_BVZERO( &a->a_vals[j] );
+ if (a->a_nvals != a->a_vals) {
+ BER_BVZERO( &a->a_nvals[j] );
+ }
+
+ /* if no values remain, delete the entire attribute */
+ if ( !a->a_numvals ) {
+ if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
+ /* Can never happen */
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: no such attribute",
+ mod->sm_desc->ad_cname.bv_val );
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ }
+ } else if ( a->a_desc->ad_type->sat_flags & SLAP_AT_ORDERED_VAL ) {
+ /* For an ordered attribute, renumber the value indices */
+ ordered_value_sort( a, 1 );
+ }
+return_result:
+ if ( id2 )
+ ch_free( id2 );
+ return rc;
+}
+
+int
+modify_replace_values(
+ Entry *e,
+ Modification *mod,
+ int permissive,
+ const char **text,
+ char *textbuf, size_t textlen )
+{
+ (void) attr_delete( &e->e_attrs, mod->sm_desc );
+
+ if ( mod->sm_values ) {
+ return modify_add_values( e, mod, permissive, text, textbuf, textlen );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int
+modify_increment_values(
+ Entry *e,
+ Modification *mod,
+ int permissive,
+ const char **text,
+ char *textbuf, size_t textlen )
+{
+ Attribute *a;
+ const char *syn_oid;
+
+ a = attr_find( e->e_attrs, mod->sm_desc );
+ if( a == NULL ) {
+ if ( permissive ) {
+ Modification modReplace = *mod;
+
+ modReplace.sm_op = LDAP_MOD_REPLACE;
+
+ return modify_add_values(e, &modReplace, permissive, text, textbuf, textlen);
+ } else {
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/increment: %s: no such attribute",
+ mod->sm_desc->ad_cname.bv_val );
+ return LDAP_NO_SUCH_ATTRIBUTE;
+ }
+ }
+
+ syn_oid = at_syntax( a->a_desc->ad_type );
+ if ( syn_oid && !strcmp( syn_oid, SLAPD_INTEGER_SYNTAX )) {
+ int i;
+ char str[sizeof(long)*3 + 2]; /* overly long */
+ long incr;
+
+ if ( lutil_atol( &incr, mod->sm_values[0].bv_val ) != 0 ) {
+ *text = "modify/increment: invalid syntax of increment";
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* treat zero and errors as a no-op */
+ if( incr == 0 ) {
+ return LDAP_SUCCESS;
+ }
+
+ for( i = 0; !BER_BVISNULL( &a->a_nvals[i] ); i++ ) {
+ char *tmp;
+ long value;
+ size_t strln;
+ if ( lutil_atol( &value, a->a_nvals[i].bv_val ) != 0 ) {
+ *text = "modify/increment: invalid syntax of original value";
+ return LDAP_INVALID_SYNTAX;
+ }
+ strln = snprintf( str, sizeof(str), "%ld", value+incr );
+
+ tmp = SLAP_REALLOC( a->a_nvals[i].bv_val, strln+1 );
+ if( tmp == NULL ) {
+ *text = "modify/increment: reallocation error";
+ return LDAP_OTHER;
+ }
+ a->a_nvals[i].bv_val = tmp;
+ a->a_nvals[i].bv_len = strln;
+
+ AC_MEMCPY( a->a_nvals[i].bv_val, str, strln+1 );
+ }
+
+ } else {
+ snprintf( textbuf, textlen,
+ "modify/increment: %s: increment not supported for value syntax %s",
+ mod->sm_desc->ad_cname.bv_val,
+ syn_oid ? syn_oid : "(NULL)" );
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+void
+slap_mod_free(
+ Modification *mod,
+ int freeit )
+{
+ if ( mod->sm_values != NULL ) ber_bvarray_free( mod->sm_values );
+ mod->sm_values = NULL;
+
+ if ( mod->sm_nvalues != NULL ) ber_bvarray_free( mod->sm_nvalues );
+ mod->sm_nvalues = NULL;
+
+ if( freeit ) free( mod );
+}
+
+void
+slap_mods_free(
+ Modifications *ml,
+ int freevals )
+{
+ Modifications *next;
+
+ for ( ; ml != NULL; ml = next ) {
+ next = ml->sml_next;
+
+ if ( freevals )
+ slap_mod_free( &ml->sml_mod, 0 );
+ free( ml );
+ }
+}
+
diff --git a/servers/slapd/module.c b/servers/slapd/module.c
new file mode 100644
index 0000000..048ce8d
--- /dev/null
+++ b/servers/slapd/module.c
@@ -0,0 +1,367 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+#include <stdio.h>
+#include "slap.h"
+
+#ifdef SLAPD_MODULES
+
+#include <ltdl.h>
+
+typedef int (*MODULE_INIT_FN)(
+ int argc,
+ char *argv[]);
+typedef int (*MODULE_LOAD_FN)(
+ const void *module,
+ const char *filename);
+typedef int (*MODULE_TERM_FN)(void);
+
+
+struct module_regtable_t {
+ char *type;
+ MODULE_LOAD_FN proc;
+} module_regtable[] = {
+ { "null", load_null_module },
+#ifdef SLAPD_EXTERNAL_EXTENSIONS
+ { "extension", load_extop_module },
+#endif
+ { NULL, NULL }
+};
+
+typedef struct module_loaded_t {
+ struct module_loaded_t *next;
+ lt_dlhandle lib;
+ char name[1];
+} module_loaded_t;
+
+module_loaded_t *module_list = NULL;
+
+static int module_int_unload (module_loaded_t *module);
+
+#ifdef HAVE_EBCDIC
+static char ebuf[BUFSIZ];
+#endif
+
+int module_init (void)
+{
+ if (lt_dlinit()) {
+ const char *error = lt_dlerror();
+#ifdef HAVE_EBCDIC
+ strcpy( ebuf, error );
+ __etoa( ebuf );
+ error = ebuf;
+#endif
+ Debug(LDAP_DEBUG_ANY, "lt_dlinit failed: %s\n", error );
+
+ return -1;
+ }
+
+ return module_path( LDAP_MODULEDIR );
+}
+
+int module_kill (void)
+{
+ /* unload all modules before shutdown */
+ while (module_list != NULL) {
+ module_int_unload(module_list);
+ }
+
+ if (lt_dlexit()) {
+ const char *error = lt_dlerror();
+#ifdef HAVE_EBCDIC
+ strcpy( ebuf, error );
+ __etoa( ebuf );
+ error = ebuf;
+#endif
+ Debug(LDAP_DEBUG_ANY, "lt_dlexit failed: %s\n", error );
+
+ return -1;
+ }
+ return 0;
+}
+
+void * module_handle( const char *file_name )
+{
+ module_loaded_t *module;
+
+ for ( module = module_list; module; module= module->next ) {
+ if ( !strcmp( module->name, file_name )) {
+ return module;
+ }
+ }
+ return NULL;
+}
+
+int module_unload( const char *file_name )
+{
+ module_loaded_t *module;
+
+ module = module_handle( file_name );
+ if ( module ) {
+ module_int_unload( module );
+ return 0;
+ }
+ return -1; /* not found */
+}
+
+int module_load(const char* file_name, int argc, char *argv[])
+{
+ module_loaded_t *module;
+ const char *error;
+ int rc;
+ MODULE_INIT_FN initialize;
+#ifdef HAVE_EBCDIC
+#define file ebuf
+#else
+#define file file_name
+#endif
+
+ module = module_handle( file_name );
+ if ( module ) {
+ Debug( LDAP_DEBUG_ANY, "module_load: (%s) already loaded\n",
+ file_name );
+ return -1;
+ }
+
+ /* If loading a backend, see if we already have it */
+ if ( !strncasecmp( file_name, "back_", 5 )) {
+ char *name = (char *)file_name + 5;
+ char *dot = strchr( name, '.');
+ if (dot) *dot = '\0';
+ rc = backend_info( name ) != NULL;
+ if (dot) *dot = '.';
+ if ( rc ) {
+ Debug( LDAP_DEBUG_CONFIG, "module_load: (%s) already present (static)\n",
+ file_name );
+ return 0;
+ }
+ } else {
+ /* check for overlays too */
+ char *dot = strchr( file_name, '.' );
+ if ( dot ) *dot = '\0';
+ rc = overlay_find( file_name ) != NULL;
+ if ( dot ) *dot = '.';
+ if ( rc ) {
+ Debug( LDAP_DEBUG_CONFIG, "module_load: (%s) already present (static)\n",
+ file_name );
+ return 0;
+ }
+ }
+
+ module = (module_loaded_t *)ch_calloc(1, sizeof(module_loaded_t) +
+ strlen(file_name));
+ if (module == NULL) {
+ Debug(LDAP_DEBUG_ANY, "module_load failed: (%s) out of memory\n", file_name );
+
+ return -1;
+ }
+ strcpy( module->name, file_name );
+
+#ifdef HAVE_EBCDIC
+ strcpy( file, file_name );
+ __atoe( file );
+#endif
+ /*
+ * The result of lt_dlerror(), when called, must be cached prior
+ * to calling Debug. This is because Debug is a macro that expands
+ * into multiple function calls.
+ */
+ if ((module->lib = lt_dlopenext(file)) == NULL) {
+ error = lt_dlerror();
+#ifdef HAVE_EBCDIC
+ strcpy( ebuf, error );
+ __etoa( ebuf );
+ error = ebuf;
+#endif
+ Debug(LDAP_DEBUG_ANY, "lt_dlopenext failed: (%s) %s\n", file_name,
+ error );
+
+ ch_free(module);
+ return -1;
+ }
+
+ Debug(LDAP_DEBUG_CONFIG, "loaded module %s\n", file_name );
+
+
+#ifdef HAVE_EBCDIC
+#pragma convlit(suspend)
+#endif
+ if ((initialize = lt_dlsym(module->lib, "init_module")) == NULL) {
+#ifdef HAVE_EBCDIC
+#pragma convlit(resume)
+#endif
+ Debug(LDAP_DEBUG_CONFIG, "module %s: no init_module() function found\n",
+ file_name );
+
+ lt_dlclose(module->lib);
+ ch_free(module);
+ return -1;
+ }
+
+ /* The imported init_module() routine passes back the type of
+ * module (i.e., which part of slapd it should be hooked into)
+ * or -1 for error. If it passes back 0, then you get the
+ * old behavior (i.e., the library is loaded and not hooked
+ * into anything).
+ *
+ * It might be better if the conf file could specify the type
+ * of module. That way, a single module could support multiple
+ * type of hooks. This could be done by using something like:
+ *
+ * moduleload extension /usr/local/openldap/whatever.so
+ *
+ * then we'd search through module_regtable for a matching
+ * module type, and hook in there.
+ */
+ rc = initialize(argc, argv);
+ if (rc == -1) {
+ Debug(LDAP_DEBUG_CONFIG, "module %s: init_module() failed\n",
+ file_name );
+
+ lt_dlclose(module->lib);
+ ch_free(module);
+ return rc;
+ }
+
+ if (rc >= (int)(sizeof(module_regtable) / sizeof(struct module_regtable_t))
+ || module_regtable[rc].proc == NULL)
+ {
+ Debug(LDAP_DEBUG_CONFIG, "module %s: unknown registration type (%d)\n",
+ file_name, rc );
+
+ module_int_unload(module);
+ return -1;
+ }
+
+ rc = (module_regtable[rc].proc)(module, file_name);
+ if (rc != 0) {
+ Debug(LDAP_DEBUG_CONFIG, "module %s: %s module could not be registered\n",
+ file_name, module_regtable[rc].type );
+
+ module_int_unload(module);
+ return rc;
+ }
+
+ module->next = module_list;
+ module_list = module;
+
+ Debug(LDAP_DEBUG_CONFIG, "module %s: %s module registered\n",
+ file_name, module_regtable[rc].type );
+
+ return 0;
+}
+
+int module_path(const char *path)
+{
+#ifdef HAVE_EBCDIC
+ strcpy(ebuf, path);
+ __atoe(ebuf);
+ path = ebuf;
+#endif
+ return lt_dlsetsearchpath( path );
+}
+
+void *module_resolve (const void *module, const char *name)
+{
+#ifdef HAVE_EBCDIC
+ strcpy(ebuf, name);
+ __atoe(ebuf);
+ name = ebuf;
+#endif
+ if (module == NULL || name == NULL)
+ return(NULL);
+ return(lt_dlsym(((module_loaded_t *)module)->lib, name));
+}
+
+static int module_int_unload (module_loaded_t *module)
+{
+ module_loaded_t *mod;
+ MODULE_TERM_FN terminate;
+
+ if (module != NULL) {
+ /* remove module from tracking list */
+ if (module_list == module) {
+ module_list = module->next;
+ } else {
+ for (mod = module_list; mod; mod = mod->next) {
+ if (mod->next == module) {
+ mod->next = module->next;
+ break;
+ }
+ }
+ }
+
+ /* call module's terminate routine, if present */
+#ifdef HAVE_EBCDIC
+#pragma convlit(suspend)
+#endif
+ if ((terminate = lt_dlsym(module->lib, "term_module"))) {
+#ifdef HAVE_EBCDIC
+#pragma convlit(resume)
+#endif
+ terminate();
+ }
+
+ /* close the library and free the memory */
+ lt_dlclose(module->lib);
+ ch_free(module);
+ }
+ return 0;
+}
+
+int load_null_module (const void *module, const char *file_name)
+{
+ return 0;
+}
+
+#ifdef SLAPD_EXTERNAL_EXTENSIONS
+int
+load_extop_module (
+ const void *module,
+ const char *file_name
+)
+{
+ SLAP_EXTOP_MAIN_FN *ext_main;
+ SLAP_EXTOP_GETOID_FN *ext_getoid;
+ struct berval oid;
+ int rc;
+
+ ext_main = (SLAP_EXTOP_MAIN_FN *)module_resolve(module, "ext_main");
+ if (ext_main == NULL) {
+ return(-1);
+ }
+
+ ext_getoid = module_resolve(module, "ext_getoid");
+ if (ext_getoid == NULL) {
+ return(-1);
+ }
+
+ rc = (ext_getoid)(0, &oid, 256);
+ if (rc != 0) {
+ return(rc);
+ }
+ if (oid.bv_val == NULL || oid.bv_len == 0) {
+ return(-1);
+ }
+
+ /* FIXME: this is broken, and no longer needed,
+ * as a module can call load_extop() itself... */
+ rc = load_extop( &oid, ext_main );
+ return rc;
+}
+#endif /* SLAPD_EXTERNAL_EXTENSIONS */
+#endif /* SLAPD_MODULES */
+
diff --git a/servers/slapd/mr.c b/servers/slapd/mr.c
new file mode 100644
index 0000000..8170345
--- /dev/null
+++ b/servers/slapd/mr.c
@@ -0,0 +1,549 @@
+/* mr.c - routines to manage matching rule definitions */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+struct mindexrec {
+ struct berval mir_name;
+ MatchingRule *mir_mr;
+};
+
+static Avlnode *mr_index = NULL;
+static LDAP_SLIST_HEAD(MRList, MatchingRule) mr_list
+ = LDAP_SLIST_HEAD_INITIALIZER(&mr_list);
+static LDAP_SLIST_HEAD(MRUList, MatchingRuleUse) mru_list
+ = LDAP_SLIST_HEAD_INITIALIZER(&mru_list);
+
+static int
+mr_index_cmp(
+ const void *v_mir1,
+ const void *v_mir2
+)
+{
+ const struct mindexrec *mir1 = v_mir1;
+ const struct mindexrec *mir2 = v_mir2;
+ int i = mir1->mir_name.bv_len - mir2->mir_name.bv_len;
+ if (i) return i;
+ return (strcasecmp( mir1->mir_name.bv_val, mir2->mir_name.bv_val ));
+}
+
+static int
+mr_index_name_cmp(
+ const void *v_name,
+ const void *v_mir
+)
+{
+ const struct berval *name = v_name;
+ const struct mindexrec *mir = v_mir;
+ int i = name->bv_len - mir->mir_name.bv_len;
+ if (i) return i;
+ return (strncasecmp( name->bv_val, mir->mir_name.bv_val, name->bv_len ));
+}
+
+MatchingRule *
+mr_find( const char *mrname )
+{
+ struct berval bv;
+
+ bv.bv_val = (char *)mrname;
+ bv.bv_len = strlen( mrname );
+ return mr_bvfind( &bv );
+}
+
+MatchingRule *
+mr_bvfind( struct berval *mrname )
+{
+ struct mindexrec *mir = NULL;
+
+ if ( (mir = ldap_avl_find( mr_index, mrname, mr_index_name_cmp )) != NULL ) {
+ return( mir->mir_mr );
+ }
+ return( NULL );
+}
+
+void
+mr_destroy( void )
+{
+ MatchingRule *m;
+
+ ldap_avl_free(mr_index, ldap_memfree);
+ while( !LDAP_SLIST_EMPTY(&mr_list) ) {
+ m = LDAP_SLIST_FIRST(&mr_list);
+ LDAP_SLIST_REMOVE_HEAD(&mr_list, smr_next);
+ ch_free( m->smr_str.bv_val );
+ ch_free( m->smr_compat_syntaxes );
+ ldap_matchingrule_free((LDAPMatchingRule *)m);
+ }
+}
+
+static int
+mr_insert(
+ MatchingRule *smr,
+ const char **err
+)
+{
+ struct mindexrec *mir;
+ char **names;
+
+ LDAP_SLIST_NEXT( smr, smr_next ) = NULL;
+ LDAP_SLIST_INSERT_HEAD(&mr_list, smr, smr_next);
+
+ if ( smr->smr_oid ) {
+ mir = (struct mindexrec *)
+ ch_calloc( 1, sizeof(struct mindexrec) );
+ mir->mir_name.bv_val = smr->smr_oid;
+ mir->mir_name.bv_len = strlen( smr->smr_oid );
+ mir->mir_mr = smr;
+ if ( ldap_avl_insert( &mr_index, (caddr_t) mir,
+ mr_index_cmp, ldap_avl_dup_error ) ) {
+ *err = smr->smr_oid;
+ ldap_memfree(mir);
+ return SLAP_SCHERR_MR_DUP;
+ }
+ /* FIX: temporal consistency check */
+ mr_bvfind(&mir->mir_name);
+ }
+ if ( (names = smr->smr_names) ) {
+ while ( *names ) {
+ mir = (struct mindexrec *)
+ ch_calloc( 1, sizeof(struct mindexrec) );
+ mir->mir_name.bv_val = *names;
+ mir->mir_name.bv_len = strlen( *names );
+ mir->mir_mr = smr;
+ if ( ldap_avl_insert( &mr_index, (caddr_t) mir,
+ mr_index_cmp, ldap_avl_dup_error ) ) {
+ *err = *names;
+ ldap_memfree(mir);
+ return SLAP_SCHERR_MR_DUP;
+ }
+ /* FIX: temporal consistency check */
+ mr_bvfind(&mir->mir_name);
+ names++;
+ }
+ }
+ return 0;
+}
+
+int
+mr_make_syntax_compat_with_mr(
+ Syntax *syn,
+ MatchingRule *mr )
+{
+ int n = 0;
+
+ assert( syn != NULL );
+ assert( mr != NULL );
+
+ if ( mr->smr_compat_syntaxes ) {
+ /* count existing */
+ for ( n = 0;
+ mr->smr_compat_syntaxes[ n ];
+ n++ )
+ {
+ if ( mr->smr_compat_syntaxes[ n ] == syn ) {
+ /* already compatible; mmmmh... */
+ return 1;
+ }
+ }
+ }
+
+ mr->smr_compat_syntaxes = ch_realloc(
+ mr->smr_compat_syntaxes,
+ sizeof( Syntax * )*(n + 2) );
+ mr->smr_compat_syntaxes[ n ] = syn;
+ mr->smr_compat_syntaxes[ n + 1 ] = NULL;
+
+ return 0;
+}
+
+int
+mr_make_syntax_compat_with_mrs(
+ const char *syntax,
+ char *const *mrs )
+{
+ int r, rc = 0;
+ Syntax *syn;
+
+ assert( syntax != NULL );
+ assert( mrs != NULL );
+
+ syn = syn_find( syntax );
+ if ( syn == NULL ) {
+ return -1;
+ }
+
+ for ( r = 0; mrs[ r ] != NULL; r++ ) {
+ MatchingRule *mr = mr_find( mrs[ r ] );
+ if ( mr == NULL ) {
+ /* matchingRule not found -- ignore by now */
+ continue;
+ }
+
+ rc += mr_make_syntax_compat_with_mr( syn, mr );
+ }
+
+ return rc;
+}
+
+int
+mr_add(
+ LDAPMatchingRule *mr,
+ slap_mrule_defs_rec *def,
+ MatchingRule *amr,
+ const char **err
+)
+{
+ MatchingRule *smr;
+ Syntax *syn;
+ Syntax **compat_syn = NULL;
+ int code;
+
+ if( def->mrd_compat_syntaxes ) {
+ int i;
+ for( i=0; def->mrd_compat_syntaxes[i]; i++ ) {
+ /* just count em */
+ }
+
+ compat_syn = ch_malloc( sizeof(Syntax *) * (i+1) );
+
+ for( i=0; def->mrd_compat_syntaxes[i]; i++ ) {
+ compat_syn[i] = syn_find( def->mrd_compat_syntaxes[i] );
+ if( compat_syn[i] == NULL ) {
+ ch_free( compat_syn );
+ return SLAP_SCHERR_SYN_NOT_FOUND;
+ }
+ }
+
+ compat_syn[i] = NULL;
+ }
+
+ smr = (MatchingRule *) ch_calloc( 1, sizeof(MatchingRule) );
+ AC_MEMCPY( &smr->smr_mrule, mr, sizeof(LDAPMatchingRule));
+
+ /*
+ * note: smr_bvoid uses the same memory of smr_mrule.mr_oid;
+ * smr_oidlen is #defined as smr_bvoid.bv_len
+ */
+ smr->smr_bvoid.bv_val = smr->smr_mrule.mr_oid;
+ smr->smr_oidlen = strlen( mr->mr_oid );
+ smr->smr_usage = def->mrd_usage;
+ smr->smr_compat_syntaxes = compat_syn;
+ smr->smr_normalize = def->mrd_normalize;
+ smr->smr_match = def->mrd_match;
+ smr->smr_indexer = def->mrd_indexer;
+ smr->smr_filter = def->mrd_filter;
+ smr->smr_associated = amr;
+
+ if ( smr->smr_syntax_oid ) {
+ if ( (syn = syn_find(smr->smr_syntax_oid)) ) {
+ smr->smr_syntax = syn;
+ } else {
+ *err = smr->smr_syntax_oid;
+ ch_free( smr );
+ return SLAP_SCHERR_SYN_NOT_FOUND;
+ }
+ } else {
+ *err = "";
+ ch_free( smr );
+ return SLAP_SCHERR_MR_INCOMPLETE;
+ }
+ code = mr_insert(smr,err);
+ return code;
+}
+
+int
+register_matching_rule(
+ slap_mrule_defs_rec *def )
+{
+ LDAPMatchingRule *mr;
+ MatchingRule *amr = NULL;
+ int code;
+ const char *err;
+
+ if( def->mrd_usage == SLAP_MR_NONE && def->mrd_compat_syntaxes == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "register_matching_rule: not usable %s\n",
+ def->mrd_desc );
+
+ return -1;
+ }
+
+ if( def->mrd_associated != NULL ) {
+ amr = mr_find( def->mrd_associated );
+ if( amr == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "register_matching_rule: "
+ "could not locate associated matching rule %s for %s\n",
+ def->mrd_associated, def->mrd_desc );
+
+ return -1;
+ }
+
+ if (( def->mrd_usage & SLAP_MR_EQUALITY ) &&
+ (( def->mrd_usage & SLAP_MR_SUBTYPE_MASK ) == SLAP_MR_NONE ))
+ {
+ if (( def->mrd_usage & SLAP_MR_EQUALITY ) &&
+ (( def->mrd_usage & SLAP_MR_SUBTYPE_MASK ) != SLAP_MR_NONE ))
+ {
+ Debug( LDAP_DEBUG_ANY, "register_matching_rule: "
+ "inappropriate (approx) association %s for %s\n",
+ def->mrd_associated, def->mrd_desc );
+ return -1;
+ }
+
+ } else if (!( amr->smr_usage & SLAP_MR_EQUALITY )) {
+ Debug( LDAP_DEBUG_ANY, "register_matching_rule: "
+ "inappropriate (equalilty) association %s for %s\n",
+ def->mrd_associated, def->mrd_desc );
+ return -1;
+ }
+ }
+
+ mr = ldap_str2matchingrule( def->mrd_desc, &code, &err,
+ LDAP_SCHEMA_ALLOW_ALL );
+ if ( !mr ) {
+ Debug( LDAP_DEBUG_ANY,
+ "Error in register_matching_rule: %s before %s in %s\n",
+ ldap_scherr2str(code), err, def->mrd_desc );
+
+ return -1;
+ }
+
+
+ code = mr_add( mr, def, amr, &err );
+
+ ldap_memfree( mr );
+
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "Error in register_matching_rule: %s for %s in %s\n",
+ scherr2str(code), err, def->mrd_desc );
+
+ return -1;
+ }
+
+ return 0;
+}
+
+void
+mru_destroy( void )
+{
+ MatchingRuleUse *m;
+
+ while( !LDAP_SLIST_EMPTY(&mru_list) ) {
+ m = LDAP_SLIST_FIRST(&mru_list);
+ LDAP_SLIST_REMOVE_HEAD(&mru_list, smru_next);
+
+ if ( m->smru_str.bv_val ) {
+ ch_free( m->smru_str.bv_val );
+ m->smru_str.bv_val = NULL;
+ }
+ /* memory borrowed from m->smru_mr */
+ m->smru_oid = NULL;
+ m->smru_names = NULL;
+ m->smru_desc = NULL;
+
+ /* free what's left (basically smru_mruleuse.mru_applies_oids) */
+ ldap_matchingruleuse_free((LDAPMatchingRuleUse *)m);
+ }
+}
+
+int
+matching_rule_use_init( void )
+{
+ MatchingRule *mr;
+ MatchingRuleUse **mru_ptr = &LDAP_SLIST_FIRST(&mru_list);
+
+ Debug( LDAP_DEBUG_TRACE, "matching_rule_use_init\n" );
+
+ LDAP_SLIST_FOREACH( mr, &mr_list, smr_next ) {
+ AttributeType *at;
+ MatchingRuleUse mru_storage = {{ 0 }},
+ *mru = &mru_storage;
+
+ char **applies_oids = NULL;
+
+ mr->smr_mru = NULL;
+
+ /* hide rules marked as HIDE */
+ if ( mr->smr_usage & SLAP_MR_HIDE ) {
+ continue;
+ }
+
+ /* hide rules not marked as designed for extensibility */
+ /* MR_EXT means can be used any attribute type whose
+ * syntax is same as the assertion syntax.
+ * Another mechanism is needed where rule can be used
+ * with attribute of other syntaxes.
+ * Framework doesn't support this (yet).
+ */
+
+ if (!( ( mr->smr_usage & SLAP_MR_EXT )
+ || mr->smr_compat_syntaxes ) )
+ {
+ continue;
+ }
+
+ /*
+ * Note: we're using the same values of the corresponding
+ * MatchingRule structure; maybe we'd copy them ...
+ */
+ mru->smru_mr = mr;
+ mru->smru_obsolete = mr->smr_obsolete;
+ mru->smru_applies_oids = NULL;
+ LDAP_SLIST_NEXT(mru, smru_next) = NULL;
+ mru->smru_oid = mr->smr_oid;
+ mru->smru_names = mr->smr_names;
+ mru->smru_desc = mr->smr_desc;
+
+ Debug( LDAP_DEBUG_TRACE, " %s (%s):\n",
+ mru->smru_oid,
+ mru->smru_names ? mru->smru_names[ 0 ] : "" );
+
+ at = NULL;
+ for ( at_start( &at ); at; at_next( &at ) ) {
+ if( at->sat_flags & SLAP_AT_HIDE ) continue;
+
+ if( mr_usable_with_at( mr, at )) {
+ ldap_charray_add( &applies_oids, at->sat_cname.bv_val );
+ }
+ }
+
+ /*
+ * Note: the matchingRules that are not used
+ * by any attributeType are not listed as
+ * matchingRuleUse
+ */
+ if ( applies_oids != NULL ) {
+ mru->smru_applies_oids = applies_oids;
+ {
+ char *str = ldap_matchingruleuse2str( &mru->smru_mruleuse );
+ Debug( LDAP_DEBUG_TRACE, " matchingRuleUse: %s\n", str );
+ ldap_memfree( str );
+ }
+
+ mru = (MatchingRuleUse *)ber_memalloc( sizeof( MatchingRuleUse ) );
+ /* call-forward from MatchingRule to MatchingRuleUse */
+ mr->smr_mru = mru;
+ /* copy static data to newly allocated struct */
+ *mru = mru_storage;
+ /* append the struct pointer to the end of the list */
+ *mru_ptr = mru;
+ /* update the list head pointer */
+ mru_ptr = &LDAP_SLIST_NEXT(mru,smru_next);
+ }
+ }
+
+ return( 0 );
+}
+
+int
+mr_usable_with_at(
+ MatchingRule *mr,
+ AttributeType *at )
+{
+ if ( ( mr->smr_usage & SLAP_MR_EXT ) && (
+ mr->smr_syntax == at->sat_syntax ||
+ mr == at->sat_equality ||
+ mr == at->sat_approx ||
+ syn_is_sup( at->sat_syntax, mr->smr_syntax ) ) )
+ {
+ return 1;
+ }
+
+ if ( mr->smr_compat_syntaxes ) {
+ int i;
+ for( i=0; mr->smr_compat_syntaxes[i]; i++ ) {
+ if( at->sat_syntax == mr->smr_compat_syntaxes[i] ) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+int mr_schema_info( Entry *e )
+{
+ AttributeDescription *ad_matchingRules = slap_schema.si_ad_matchingRules;
+ MatchingRule *mr;
+ struct berval nval;
+
+ LDAP_SLIST_FOREACH(mr, &mr_list, smr_next ) {
+ if ( mr->smr_usage & SLAP_MR_HIDE ) {
+ /* skip hidden rules */
+ continue;
+ }
+
+ if ( ! mr->smr_match ) {
+ /* skip rules without matching functions */
+ continue;
+ }
+
+ if ( mr->smr_str.bv_val == NULL ) {
+ if ( ldap_matchingrule2bv( &mr->smr_mrule, &mr->smr_str ) == NULL ) {
+ return -1;
+ }
+ }
+#if 0
+ Debug( LDAP_DEBUG_TRACE, "Merging mr [%lu] %s\n",
+ mr->smr_str.bv_len, mr->smr_str.bv_val );
+#endif
+
+ nval.bv_val = mr->smr_oid;
+ nval.bv_len = strlen(mr->smr_oid);
+ if( attr_merge_one( e, ad_matchingRules, &mr->smr_str, &nval ) ) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int mru_schema_info( Entry *e )
+{
+ AttributeDescription *ad_matchingRuleUse
+ = slap_schema.si_ad_matchingRuleUse;
+ MatchingRuleUse *mru;
+ struct berval nval;
+
+ LDAP_SLIST_FOREACH( mru, &mru_list, smru_next ) {
+ assert( !( mru->smru_usage & SLAP_MR_HIDE ) );
+
+ if ( mru->smru_str.bv_val == NULL ) {
+ if ( ldap_matchingruleuse2bv( &mru->smru_mruleuse, &mru->smru_str )
+ == NULL ) {
+ return -1;
+ }
+ }
+
+#if 0
+ Debug( LDAP_DEBUG_TRACE, "Merging mru [%lu] %s\n",
+ mru->smru_str.bv_len, mru->smru_str.bv_val );
+#endif
+
+ nval.bv_val = mru->smru_oid;
+ nval.bv_len = strlen(mru->smru_oid);
+ if( attr_merge_one( e, ad_matchingRuleUse, &mru->smru_str, &nval ) ) {
+ return -1;
+ }
+ }
+ return 0;
+}
diff --git a/servers/slapd/mra.c b/servers/slapd/mra.c
new file mode 100644
index 0000000..10cf117
--- /dev/null
+++ b/servers/slapd/mra.c
@@ -0,0 +1,231 @@
+/* mra.c - routines for dealing with extensible matching rule assertions */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+#ifdef LDAP_COMP_MATCH
+#include "component.h"
+#endif
+
+void
+mra_free(
+ Operation *op,
+ MatchingRuleAssertion *mra,
+ int freeit )
+{
+#ifdef LDAP_COMP_MATCH
+ /* free component assertion */
+ if ( mra->ma_rule->smr_usage & SLAP_MR_COMPONENT && mra->ma_cf ) {
+ component_free( mra->ma_cf );
+ }
+#endif
+ /* op->o_tmpfree( mra->ma_value.bv_val, op->o_tmpmemctx ); */
+ ch_free( mra->ma_value.bv_val );
+ if ( mra->ma_desc && mra->ma_desc->ad_flags & SLAP_DESC_TEMPORARY )
+ op->o_tmpfree( mra->ma_desc, op->o_tmpmemctx );
+ if ( freeit ) op->o_tmpfree( (char *) mra, op->o_tmpmemctx );
+}
+
+int
+get_mra(
+ Operation *op,
+ BerElement *ber,
+ Filter *f,
+ const char **text )
+{
+ int rc;
+ ber_tag_t tag, rtag;
+ ber_len_t length;
+ struct berval type = BER_BVNULL;
+ struct berval value = BER_BVNULL;
+ struct berval rule_text = BER_BVNULL;
+ MatchingRuleAssertion ma = { 0 };
+#ifdef LDAP_COMP_MATCH
+ AttributeAliasing* aa = NULL;
+#endif
+
+ rtag = ber_scanf( ber, "{t" /*"}"*/, &tag );
+
+ if( rtag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, " get_mra ber_scanf\n" );
+
+ *text = "Error parsing matching rule assertion";
+ return SLAPD_DISCONNECT;
+ }
+
+ if ( tag == LDAP_FILTER_EXT_OID ) {
+ rtag = ber_scanf( ber, "m", &rule_text );
+ if ( rtag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, " get_mra ber_scanf for mr\n" );
+
+ *text = "Error parsing matching rule in matching rule assertion";
+ return SLAPD_DISCONNECT;
+ }
+
+ rtag = ber_scanf( ber, "t", &tag );
+ if( rtag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, " get_mra ber_scanf\n" );
+
+ *text = "Error parsing matching rule assertion";
+ return SLAPD_DISCONNECT;
+ }
+ }
+
+ if ( tag == LDAP_FILTER_EXT_TYPE ) {
+ rtag = ber_scanf( ber, "m", &type );
+ if ( rtag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, " get_mra ber_scanf for ad\n" );
+
+ *text = "Error parsing attribute description in matching rule assertion";
+ return SLAPD_DISCONNECT;
+ }
+
+ rtag = ber_scanf( ber, "t", &tag );
+ if( rtag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, " get_mra ber_scanf\n" );
+
+ *text = "Error parsing matching rule assertion";
+ return SLAPD_DISCONNECT;
+ }
+ }
+
+ if ( tag != LDAP_FILTER_EXT_VALUE ) {
+ Debug( LDAP_DEBUG_ANY, " get_mra ber_scanf missing value\n" );
+
+ *text = "Missing value in matching rule assertion";
+ return SLAPD_DISCONNECT;
+ }
+
+ rtag = ber_scanf( ber, "m", &value );
+
+ if( rtag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, " get_mra ber_scanf\n" );
+
+ *text = "Error decoding value in matching rule assertion";
+ return SLAPD_DISCONNECT;
+ }
+
+ tag = ber_peek_tag( ber, &length );
+
+ if ( tag == LDAP_FILTER_EXT_DNATTRS ) {
+ rtag = ber_scanf( ber, /*"{"*/ "b}", &ma.ma_dnattrs );
+ } else {
+ rtag = ber_scanf( ber, /*"{"*/ "}" );
+ }
+
+ if( rtag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, " get_mra ber_scanf\n" );
+
+ *text = "Error decoding dnattrs matching rule assertion";
+ return SLAPD_DISCONNECT;
+ }
+
+ if( type.bv_val != NULL ) {
+ rc = slap_bv2ad( &type, &ma.ma_desc, text );
+ if( rc != LDAP_SUCCESS ) {
+ f->f_choice |= SLAPD_FILTER_UNDEFINED;
+ rc = slap_bv2undef_ad( &type, &ma.ma_desc, text,
+ SLAP_AD_PROXIED|SLAP_AD_NOINSERT );
+
+ if( rc != LDAP_SUCCESS ) {
+ ma.ma_desc = slap_bv2tmp_ad( &type, op->o_tmpmemctx );
+ rc = LDAP_SUCCESS;
+ }
+ }
+ }
+
+ if( rule_text.bv_val != NULL ) {
+ ma.ma_rule = mr_bvfind( &rule_text );
+ if( ma.ma_rule == NULL ) {
+ *text = "matching rule not recognized";
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+ }
+
+ if ( ma.ma_rule == NULL ) {
+ /*
+ * Need either type or rule ...
+ */
+ if ( ma.ma_desc == NULL ) {
+ *text = "no matching rule or type";
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+
+ if ( ma.ma_desc->ad_type->sat_equality != NULL &&
+ ma.ma_desc->ad_type->sat_equality->smr_usage & SLAP_MR_EXT )
+ {
+ /* no matching rule was provided, use the attribute's
+ equality rule if it supports extensible matching. */
+ ma.ma_rule = ma.ma_desc->ad_type->sat_equality;
+
+ } else {
+ *text = "no appropriate rule to use for type";
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+ }
+
+ if ( ma.ma_desc != NULL ) {
+ if( !mr_usable_with_at( ma.ma_rule, ma.ma_desc->ad_type ) ) {
+ *text = "matching rule use with this attribute not appropriate";
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+
+ }
+
+ /*
+ * Normalize per matching rule
+ */
+ rc = asserted_value_validate_normalize( ma.ma_desc,
+ ma.ma_rule,
+ SLAP_MR_EXT|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
+ &value, &ma.ma_value, text, op->o_tmpmemctx );
+
+ if( rc != LDAP_SUCCESS ) return rc;
+
+#ifdef LDAP_COMP_MATCH
+ /* Check If this attribute is aliased */
+ if ( is_aliased_attribute && ma.ma_desc && ( aa = is_aliased_attribute ( ma.ma_desc ) ) ) {
+ rc = get_aliased_filter ( op, &ma, aa, text );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ }
+ else if ( ma.ma_rule && ma.ma_rule->smr_usage & SLAP_MR_COMPONENT ) {
+ /* Matching Rule for Component Matching */
+ rc = get_comp_filter( op, &ma.ma_value, &ma.ma_cf, text );
+ if ( rc != LDAP_SUCCESS ) return rc;
+ }
+#endif
+
+ length = sizeof(ma);
+ /* Append rule_text to end of struct */
+ if (rule_text.bv_val) length += rule_text.bv_len + 1;
+ f->f_mra = op->o_tmpalloc( length, op->o_tmpmemctx );
+ *f->f_mra = ma;
+ if (rule_text.bv_val) {
+ f->f_mra->ma_rule_text.bv_len = rule_text.bv_len;
+ f->f_mra->ma_rule_text.bv_val = (char *)(f->f_mra+1);
+ AC_MEMCPY(f->f_mra->ma_rule_text.bv_val, rule_text.bv_val,
+ rule_text.bv_len+1);
+ }
+
+ return LDAP_SUCCESS;
+}
diff --git a/servers/slapd/nt_svc.c b/servers/slapd/nt_svc.c
new file mode 100644
index 0000000..1d2c1c3
--- /dev/null
+++ b/servers/slapd/nt_svc.c
@@ -0,0 +1,110 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+#include <stdio.h>
+#include <ac/string.h>
+#include "slap.h"
+#include "lutil.h"
+
+#ifdef HAVE_NT_SERVICE_MANAGER
+
+/* in main.c */
+void WINAPI ServiceMain( DWORD argc, LPTSTR *argv );
+
+/* in ntservice.c */
+int main( int argc, LPTSTR *argv )
+{
+ int length;
+ char filename[MAX_PATH], *fname_start;
+
+ /*
+ * Because the service was registered as SERVICE_WIN32_OWN_PROCESS,
+ * the lpServiceName element of the SERVICE_TABLE_ENTRY will be
+ * ignored.
+ */
+
+ SERVICE_TABLE_ENTRY DispatchTable[] = {
+ { "", (LPSERVICE_MAIN_FUNCTION) ServiceMain },
+ { NULL, NULL }
+ };
+
+ /*
+ * set the service's current directory to the installation directory
+ * for the service. this way we don't have to write absolute paths
+ * in the configuration files
+ */
+ GetModuleFileName( NULL, filename, sizeof( filename ) );
+ fname_start = strrchr( filename, *LDAP_DIRSEP );
+
+ if ( argc > 1 ) {
+ if ( _stricmp( "install", argv[1] ) == 0 )
+ {
+ char *svcName = SERVICE_NAME;
+ char *displayName = "OpenLDAP Directory Service";
+ BOOL auto_start = FALSE;
+
+ if ( (argc > 2) && (argv[2] != NULL) )
+ svcName = argv[2];
+
+ if ( argc > 3 && argv[3])
+ displayName = argv[3];
+
+ if ( argc > 4 && stricmp(argv[4], "auto") == 0)
+ auto_start = TRUE;
+
+ strcat(filename, " service");
+ if ( !lutil_srv_install(svcName, displayName, filename, auto_start) )
+ {
+ fputs( "service failed installation ...\n", stderr );
+ return EXIT_FAILURE;
+ }
+ fputs( "service has been installed ...\n", stderr );
+ return EXIT_SUCCESS;
+ }
+
+ if ( _stricmp( "remove", argv[1] ) == 0 )
+ {
+ char *svcName = SERVICE_NAME;
+ if ( (argc > 2) && (argv[2] != NULL) )
+ svcName = argv[2];
+ if ( !lutil_srv_remove(svcName, filename) )
+ {
+ fputs( "failed to remove the service ...\n", stderr );
+ return EXIT_FAILURE;
+ }
+ fputs( "service has been removed ...\n", stderr );
+ return EXIT_SUCCESS;
+ }
+ if ( _stricmp( "service", argv[1] ) == 0 )
+ {
+ is_NT_Service = 1;
+ *fname_start = '\0';
+ SetCurrentDirectory( filename );
+ }
+ }
+
+ if (is_NT_Service)
+ {
+ StartServiceCtrlDispatcher(DispatchTable);
+ } else
+ {
+ ServiceMain( argc, argv );
+ }
+
+ return EXIT_SUCCESS;
+}
+
+#endif
diff --git a/servers/slapd/oc.c b/servers/slapd/oc.c
new file mode 100644
index 0000000..d88c88c
--- /dev/null
+++ b/servers/slapd/oc.c
@@ -0,0 +1,940 @@
+/* oc.c - object class routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+int is_object_subclass(
+ ObjectClass *sup,
+ ObjectClass *sub )
+{
+ int i;
+
+ if( sub == NULL || sup == NULL ) return 0;
+
+#if 0
+ Debug( LDAP_DEBUG_TRACE, "is_object_subclass(%s,%s) %d\n",
+ sup->soc_oid, sub->soc_oid, sup == sub );
+#endif
+
+ if ( sup == sub ) {
+ return 1;
+ }
+
+ if ( sub->soc_sups == NULL ) {
+ return 0;
+ }
+
+ for ( i = 0; sub->soc_sups[i] != NULL; i++ ) {
+ if ( is_object_subclass( sup, sub->soc_sups[i] ) ) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int is_entry_objectclass(
+ Entry* e,
+ ObjectClass *oc,
+ unsigned flags )
+{
+ /*
+ * set_flags should only be true if oc is one of operational
+ * object classes which we support objectClass flags for
+ * (e.g., referral, alias, ...). See <slap.h>.
+ */
+
+ Attribute *attr;
+ struct berval *bv;
+
+ assert( !( e == NULL || oc == NULL ) );
+ assert( ( flags & SLAP_OCF_MASK ) != SLAP_OCF_MASK );
+
+ if ( e == NULL || oc == NULL ) {
+ return 0;
+ }
+
+ if ( flags == SLAP_OCF_SET_FLAGS && ( e->e_ocflags & SLAP_OC__END ) )
+ {
+ /* flags are set, use them */
+ return (e->e_ocflags & oc->soc_flags & SLAP_OC__MASK) != 0;
+ }
+
+ /*
+ * find objectClass attribute
+ */
+ attr = attr_find( e->e_attrs, slap_schema.si_ad_objectClass );
+ if ( attr == NULL ) {
+ /* no objectClass attribute */
+ Debug( LDAP_DEBUG_ANY, "is_entry_objectclass(\"%s\", \"%s\") "
+ "no objectClass attribute\n",
+ e->e_dn == NULL ? "" : e->e_dn,
+ oc->soc_oclass.oc_oid );
+
+ /* mark flags as set */
+ e->e_ocflags |= SLAP_OC__END;
+
+ return 0;
+ }
+
+ for ( bv = attr->a_vals; bv->bv_val; bv++ ) {
+ ObjectClass *objectClass = oc_bvfind( bv );
+
+ if ( objectClass == NULL ) {
+ /* FIXME: is this acceptable? */
+ continue;
+ }
+
+ if ( !( flags & SLAP_OCF_SET_FLAGS ) ) {
+ if ( objectClass == oc ) {
+ return 1;
+ }
+
+ if ( ( flags & SLAP_OCF_CHECK_SUP )
+ && is_object_subclass( oc, objectClass ) )
+ {
+ return 1;
+ }
+ }
+
+ e->e_ocflags |= objectClass->soc_flags;
+ }
+
+ /* mark flags as set */
+ e->e_ocflags |= SLAP_OC__END;
+
+ return ( e->e_ocflags & oc->soc_flags & SLAP_OC__MASK ) != 0;
+}
+
+
+struct oindexrec {
+ struct berval oir_name;
+ ObjectClass *oir_oc;
+};
+
+static Avlnode *oc_index = NULL;
+static Avlnode *oc_cache = NULL;
+static LDAP_STAILQ_HEAD(OCList, ObjectClass) oc_list
+ = LDAP_STAILQ_HEAD_INITIALIZER(oc_list);
+
+ObjectClass *oc_sys_tail;
+
+static int
+oc_index_cmp(
+ const void *v_oir1,
+ const void *v_oir2 )
+{
+ const struct oindexrec *oir1 = v_oir1, *oir2 = v_oir2;
+ int i = oir1->oir_name.bv_len - oir2->oir_name.bv_len;
+ if (i) return i;
+ return strcasecmp( oir1->oir_name.bv_val, oir2->oir_name.bv_val );
+}
+
+static int
+oc_index_name_cmp(
+ const void *v_name,
+ const void *v_oir )
+{
+ const struct berval *name = v_name;
+ const struct oindexrec *oir = v_oir;
+ int i = name->bv_len - oir->oir_name.bv_len;
+ if (i) return i;
+ return strncasecmp( name->bv_val, oir->oir_name.bv_val, name->bv_len );
+}
+
+ObjectClass *
+oc_find( const char *ocname )
+{
+ struct berval bv;
+
+ bv.bv_val = (char *)ocname;
+ bv.bv_len = strlen( ocname );
+
+ return( oc_bvfind( &bv ) );
+}
+
+ObjectClass *
+oc_bvfind( struct berval *ocname )
+{
+ struct oindexrec *oir;
+
+ if ( oc_cache ) {
+ oir = ldap_avl_find( oc_cache, ocname, oc_index_name_cmp );
+ if ( oir ) return oir->oir_oc;
+ }
+ oir = ldap_avl_find( oc_index, ocname, oc_index_name_cmp );
+
+ if ( oir != NULL ) {
+ if ( at_oc_cache ) {
+ ldap_avl_insert( &oc_cache, (caddr_t) oir,
+ oc_index_cmp, ldap_avl_dup_error );
+ }
+ return( oir->oir_oc );
+ }
+
+ return( NULL );
+}
+
+static LDAP_STAILQ_HEAD(OCUList, ObjectClass) oc_undef_list
+ = LDAP_STAILQ_HEAD_INITIALIZER(oc_undef_list);
+
+ObjectClass *
+oc_bvfind_undef( struct berval *ocname )
+{
+ ObjectClass *oc = oc_bvfind( ocname );
+
+ if ( oc ) {
+ return oc;
+ }
+
+ LDAP_STAILQ_FOREACH( oc, &oc_undef_list, soc_next ) {
+ int d = oc->soc_cname.bv_len - ocname->bv_len;
+
+ if ( d ) {
+ continue;
+ }
+
+ if ( strcasecmp( oc->soc_cname.bv_val, ocname->bv_val ) == 0 ) {
+ break;
+ }
+ }
+
+ if ( oc ) {
+ return oc;
+ }
+
+ oc = ch_malloc( sizeof( ObjectClass ) + ocname->bv_len + 1 );
+ memset( oc, 0, sizeof( ObjectClass ) );
+
+ oc->soc_cname.bv_len = ocname->bv_len;
+ oc->soc_cname.bv_val = (char *)&oc[ 1 ];
+ AC_MEMCPY( oc->soc_cname.bv_val, ocname->bv_val, ocname->bv_len );
+ oc->soc_cname.bv_val[ oc->soc_cname.bv_len ] = '\0';
+
+ /* canonical to upper case */
+ ldap_pvt_str2upper( oc->soc_cname.bv_val );
+
+ LDAP_STAILQ_NEXT( oc, soc_next ) = NULL;
+ ldap_pvt_thread_mutex_lock( &oc_undef_mutex );
+ LDAP_STAILQ_INSERT_HEAD( &oc_undef_list, oc, soc_next );
+ ldap_pvt_thread_mutex_unlock( &oc_undef_mutex );
+
+ return oc;
+}
+
+static int
+oc_create_required(
+ ObjectClass *soc,
+ char **attrs,
+ int *op,
+ const char **err )
+{
+ char **attrs1;
+ AttributeType *sat;
+ AttributeType **satp;
+ int i;
+
+ if ( attrs ) {
+ attrs1 = attrs;
+ while ( *attrs1 ) {
+ sat = at_find(*attrs1);
+ if ( !sat ) {
+ *err = *attrs1;
+ return SLAP_SCHERR_ATTR_NOT_FOUND;
+ }
+
+ if( is_at_operational( sat )) (*op)++;
+
+ if ( at_find_in_list(sat, soc->soc_required) < 0) {
+ if ( at_append_to_list(sat, &soc->soc_required) ) {
+ *err = *attrs1;
+ return SLAP_SCHERR_OUTOFMEM;
+ }
+ }
+ attrs1++;
+ }
+ /* Now delete duplicates from the allowed list */
+ for ( satp = soc->soc_required; *satp; satp++ ) {
+ i = at_find_in_list(*satp, soc->soc_allowed);
+ if ( i >= 0 ) {
+ at_delete_from_list(i, &soc->soc_allowed);
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+oc_create_allowed(
+ ObjectClass *soc,
+ char **attrs,
+ int *op,
+ const char **err )
+{
+ char **attrs1;
+ AttributeType *sat;
+
+ if ( attrs ) {
+ attrs1 = attrs;
+ while ( *attrs1 ) {
+ sat = at_find(*attrs1);
+ if ( !sat ) {
+ *err = *attrs1;
+ return SLAP_SCHERR_ATTR_NOT_FOUND;
+ }
+
+ if( is_at_operational( sat )) (*op)++;
+
+ if ( at_find_in_list(sat, soc->soc_required) < 0 &&
+ at_find_in_list(sat, soc->soc_allowed) < 0 ) {
+ if ( at_append_to_list(sat, &soc->soc_allowed) ) {
+ *err = *attrs1;
+ return SLAP_SCHERR_OUTOFMEM;
+ }
+ }
+ attrs1++;
+ }
+ }
+ return 0;
+}
+
+static int
+oc_add_sups(
+ ObjectClass *soc,
+ char **sups,
+ int *op,
+ const char **err )
+{
+ int code;
+ ObjectClass *soc1;
+ int nsups;
+ char **sups1;
+ int add_sups = 0;
+
+ if ( sups ) {
+ if ( !soc->soc_sups ) {
+ /* We are at the first recursive level */
+ add_sups = 1;
+ nsups = 1;
+ sups1 = sups;
+ while ( *sups1 ) {
+ nsups++;
+ sups1++;
+ }
+ soc->soc_sups = (ObjectClass **)ch_calloc(nsups,
+ sizeof(ObjectClass *));
+ }
+
+ nsups = 0;
+ sups1 = sups;
+ while ( *sups1 ) {
+ soc1 = oc_find(*sups1);
+ if ( !soc1 ) {
+ *err = *sups1;
+ return SLAP_SCHERR_CLASS_NOT_FOUND;
+ }
+
+ /* check object class usage
+ * abstract classes can only sup abstract classes
+ * structural classes can not sup auxiliary classes
+ * auxiliary classes can not sup structural classes
+ */
+ if( soc->soc_kind != soc1->soc_kind
+ && soc1->soc_kind != LDAP_SCHEMA_ABSTRACT )
+ {
+ *err = *sups1;
+ return SLAP_SCHERR_CLASS_BAD_SUP;
+ }
+
+ if( soc1->soc_obsolete && !soc->soc_obsolete ) {
+ *err = *sups1;
+ return SLAP_SCHERR_CLASS_BAD_SUP;
+ }
+
+ if( soc->soc_flags & SLAP_OC_OPERATIONAL ) (*op)++;
+
+ if ( add_sups ) {
+ soc->soc_sups[nsups] = soc1;
+ }
+
+ code = oc_add_sups( soc, soc1->soc_sup_oids, op, err );
+ if ( code ) return code;
+
+ code = oc_create_required( soc, soc1->soc_at_oids_must, op, err );
+ if ( code ) return code;
+
+ code = oc_create_allowed( soc, soc1->soc_at_oids_may, op, err );
+ if ( code ) return code;
+
+ nsups++;
+ sups1++;
+ }
+ }
+
+ return 0;
+}
+
+static void
+oc_delete_names( ObjectClass *oc )
+{
+ char **names = oc->soc_names;
+
+ if (!names) return;
+
+ while (*names) {
+ struct oindexrec tmpoir, *oir;
+
+ ber_str2bv( *names, 0, 0, &tmpoir.oir_name );
+ tmpoir.oir_oc = oc;
+ oir = (struct oindexrec *)ldap_avl_delete( &oc_index,
+ (caddr_t)&tmpoir, oc_index_cmp );
+ assert( oir != NULL );
+ ldap_memfree( oir );
+ names++;
+ }
+}
+
+/* Mark the ObjectClass as deleted, remove from list, and remove all its
+ * names from the AVL tree. Leave the OID in the tree.
+ */
+void
+oc_delete( ObjectClass *oc )
+{
+ oc->soc_flags |= SLAP_OC_DELETED;
+
+ LDAP_STAILQ_REMOVE(&oc_list, oc, ObjectClass, soc_next);
+
+ oc_delete_names( oc );
+}
+
+static void
+oc_clean( ObjectClass *o )
+{
+ if (o->soc_sups) {
+ ldap_memfree(o->soc_sups);
+ o->soc_sups = NULL;
+ }
+ if (o->soc_required) {
+ ldap_memfree(o->soc_required);
+ o->soc_required = NULL;
+ }
+ if (o->soc_allowed) {
+ ldap_memfree(o->soc_allowed);
+ o->soc_allowed = NULL;
+ }
+ if (o->soc_oidmacro) {
+ ldap_memfree(o->soc_oidmacro);
+ o->soc_oidmacro = NULL;
+ }
+}
+
+static void
+oc_destroy_one( void *v )
+{
+ struct oindexrec *oir = v;
+ ObjectClass *o = oir->oir_oc;
+
+ oc_clean( o );
+ ldap_objectclass_free((LDAPObjectClass *)o);
+ ldap_memfree(oir);
+}
+
+void
+oc_destroy( void )
+{
+ ObjectClass *o;
+
+ while( !LDAP_STAILQ_EMPTY(&oc_list) ) {
+ o = LDAP_STAILQ_FIRST(&oc_list);
+ LDAP_STAILQ_REMOVE_HEAD(&oc_list, soc_next);
+
+ oc_delete_names( o );
+ }
+
+ ldap_avl_free( oc_index, oc_destroy_one );
+
+ while( !LDAP_STAILQ_EMPTY(&oc_undef_list) ) {
+ o = LDAP_STAILQ_FIRST(&oc_undef_list);
+ LDAP_STAILQ_REMOVE_HEAD(&oc_undef_list, soc_next);
+
+ ch_free( (ObjectClass *)o );
+ }
+}
+
+int
+oc_start( ObjectClass **oc )
+{
+ assert( oc != NULL );
+
+ *oc = LDAP_STAILQ_FIRST(&oc_list);
+
+ return (*oc != NULL);
+}
+
+int
+oc_next( ObjectClass **oc )
+{
+ assert( oc != NULL );
+
+#if 0 /* pedantic check: breaks when deleting an oc, don't use it. */
+ {
+ ObjectClass *tmp = NULL;
+
+ LDAP_STAILQ_FOREACH(tmp,&oc_list,soc_next) {
+ if ( tmp == *oc ) {
+ break;
+ }
+ }
+
+ assert( tmp != NULL );
+ }
+#endif
+
+ if ( *oc == NULL ) {
+ return 0;
+ }
+
+ *oc = LDAP_STAILQ_NEXT(*oc,soc_next);
+
+ return (*oc != NULL);
+}
+
+/*
+ * check whether the two ObjectClasses actually __are__ identical,
+ * or rather inconsistent
+ */
+static int
+oc_check_dup(
+ ObjectClass *soc,
+ ObjectClass *new_soc )
+{
+ if ( new_soc->soc_oid != NULL ) {
+ if ( soc->soc_oid == NULL ) {
+ return SLAP_SCHERR_CLASS_INCONSISTENT;
+ }
+
+ if ( strcmp( soc->soc_oid, new_soc->soc_oid ) != 0 ) {
+ return SLAP_SCHERR_CLASS_INCONSISTENT;
+ }
+
+ } else {
+ if ( soc->soc_oid != NULL ) {
+ return SLAP_SCHERR_CLASS_INCONSISTENT;
+ }
+ }
+
+ if ( new_soc->soc_names ) {
+ int i;
+
+ if ( soc->soc_names == NULL ) {
+ return SLAP_SCHERR_CLASS_INCONSISTENT;
+ }
+
+ for ( i = 0; new_soc->soc_names[ i ]; i++ ) {
+ if ( soc->soc_names[ i ] == NULL ) {
+ return SLAP_SCHERR_CLASS_INCONSISTENT;
+ }
+
+ if ( strcasecmp( soc->soc_names[ i ],
+ new_soc->soc_names[ i ] ) != 0 )
+ {
+ return SLAP_SCHERR_CLASS_INCONSISTENT;
+ }
+ }
+ } else {
+ if ( soc->soc_names != NULL ) {
+ return SLAP_SCHERR_CLASS_INCONSISTENT;
+ }
+ }
+
+ return SLAP_SCHERR_CLASS_DUP;
+}
+
+static struct oindexrec *oir_old;
+
+static int
+oc_dup_error( void *left, void *right )
+{
+ oir_old = left;
+ return -1;
+}
+
+static int
+oc_insert(
+ ObjectClass **roc,
+ ObjectClass *prev,
+ const char **err )
+{
+ struct oindexrec *oir;
+ char **names;
+ ObjectClass *soc = *roc;
+
+ if ( soc->soc_oid ) {
+ oir = (struct oindexrec *)
+ ch_calloc( 1, sizeof(struct oindexrec) );
+ ber_str2bv( soc->soc_oid, 0, 0, &oir->oir_name );
+ oir->oir_oc = soc;
+ oir_old = NULL;
+
+ if ( ldap_avl_insert( &oc_index, (caddr_t) oir,
+ oc_index_cmp, oc_dup_error ) )
+ {
+ ObjectClass *old_soc;
+ int rc;
+
+ *err = soc->soc_oid;
+
+ assert( oir_old != NULL );
+ old_soc = oir_old->oir_oc;
+
+ /* replacing a deleted definition? */
+ if ( old_soc->soc_flags & SLAP_OC_DELETED ) {
+ ObjectClass tmp;
+
+ /* Keep old oid, free new oid;
+ * Keep new everything else, free old
+ */
+ tmp = *old_soc;
+ *old_soc = *soc;
+ old_soc->soc_oid = tmp.soc_oid;
+ tmp.soc_oid = soc->soc_oid;
+ *soc = tmp;
+
+ oc_clean( soc );
+ oc_destroy_one( oir );
+
+ oir = oir_old;
+ soc = old_soc;
+ *roc = soc;
+ } else {
+ rc = oc_check_dup( old_soc, soc );
+
+ ldap_memfree( oir );
+ return rc;
+ }
+ }
+
+ /* FIX: temporal consistency check */
+ assert( oc_bvfind( &oir->oir_name ) != NULL );
+ }
+
+ assert( soc != NULL );
+
+ if ( (names = soc->soc_names) ) {
+ while ( *names ) {
+ oir = (struct oindexrec *)
+ ch_calloc( 1, sizeof(struct oindexrec) );
+ oir->oir_name.bv_val = *names;
+ oir->oir_name.bv_len = strlen( *names );
+ oir->oir_oc = soc;
+
+ if ( ldap_avl_insert( &oc_index, (caddr_t) oir,
+ oc_index_cmp, ldap_avl_dup_error ) )
+ {
+ ObjectClass *old_soc;
+ int rc;
+
+ *err = *names;
+
+ old_soc = oc_bvfind( &oir->oir_name );
+ assert( old_soc != NULL );
+ rc = oc_check_dup( old_soc, soc );
+
+ ldap_memfree( oir );
+
+ while ( names > soc->soc_names ) {
+ struct oindexrec tmpoir;
+
+ names--;
+ ber_str2bv( *names, 0, 0, &tmpoir.oir_name );
+ tmpoir.oir_oc = soc;
+ oir = (struct oindexrec *)ldap_avl_delete( &oc_index,
+ (caddr_t)&tmpoir, oc_index_cmp );
+ assert( oir != NULL );
+ ldap_memfree( oir );
+ }
+
+ if ( soc->soc_oid ) {
+ struct oindexrec tmpoir;
+
+ ber_str2bv( soc->soc_oid, 0, 0, &tmpoir.oir_name );
+ tmpoir.oir_oc = soc;
+ oir = (struct oindexrec *)ldap_avl_delete( &oc_index,
+ (caddr_t)&tmpoir, oc_index_cmp );
+ assert( oir != NULL );
+ ldap_memfree( oir );
+ }
+
+ return rc;
+ }
+
+ /* FIX: temporal consistency check */
+ assert( oc_bvfind(&oir->oir_name) != NULL );
+
+ names++;
+ }
+ }
+ if ( soc->soc_flags & SLAP_OC_HARDCODE ) {
+ prev = oc_sys_tail;
+ oc_sys_tail = soc;
+ }
+ if ( prev ) {
+ LDAP_STAILQ_INSERT_AFTER( &oc_list, prev, soc, soc_next );
+ } else {
+ LDAP_STAILQ_INSERT_TAIL( &oc_list, soc, soc_next );
+ }
+
+ return 0;
+}
+
+int
+oc_add(
+ LDAPObjectClass *oc,
+ int user,
+ ObjectClass **rsoc,
+ ObjectClass *prev,
+ const char **err )
+{
+ ObjectClass *soc;
+ int code;
+ int op = 0;
+ char *oidm = NULL;
+
+ if ( oc->oc_names != NULL ) {
+ int i;
+
+ for( i=0; oc->oc_names[i]; i++ ) {
+ if( !slap_valid_descr( oc->oc_names[i] ) ) {
+ return SLAP_SCHERR_BAD_DESCR;
+ }
+ }
+ }
+
+ if ( !OID_LEADCHAR( oc->oc_oid[0] )) {
+ /* Expand OID macros */
+ char *oid = oidm_find( oc->oc_oid );
+ if ( !oid ) {
+ *err = oc->oc_oid;
+ return SLAP_SCHERR_OIDM;
+ }
+ if ( oid != oc->oc_oid ) {
+ oidm = oc->oc_oid;
+ oc->oc_oid = oid;
+ }
+ }
+
+ soc = (ObjectClass *) ch_calloc( 1, sizeof(ObjectClass) );
+ AC_MEMCPY( &soc->soc_oclass, oc, sizeof(LDAPObjectClass) );
+
+ soc->soc_oidmacro = oidm;
+ if( oc->oc_names != NULL ) {
+ soc->soc_cname.bv_val = soc->soc_names[0];
+ } else {
+ soc->soc_cname.bv_val = soc->soc_oid;
+ }
+ soc->soc_cname.bv_len = strlen( soc->soc_cname.bv_val );
+
+ if( soc->soc_sup_oids == NULL &&
+ soc->soc_kind == LDAP_SCHEMA_STRUCTURAL )
+ {
+ /* structural object classes implicitly inherit from 'top' */
+ static char *top_oids[] = { SLAPD_TOP_OID, NULL };
+ code = oc_add_sups( soc, top_oids, &op, err );
+ } else {
+ code = oc_add_sups( soc, soc->soc_sup_oids, &op, err );
+ }
+
+ if ( code != 0 ) {
+ goto done;
+ }
+
+ if ( user && op ) {
+ code = SLAP_SCHERR_CLASS_BAD_SUP;
+ goto done;
+ }
+
+ code = oc_create_required( soc, soc->soc_at_oids_must, &op, err );
+ if ( code != 0 ) {
+ goto done;
+ }
+
+ code = oc_create_allowed( soc, soc->soc_at_oids_may, &op, err );
+ if ( code != 0 ) {
+ goto done;
+ }
+
+ if ( user && op ) {
+ code = SLAP_SCHERR_CLASS_BAD_USAGE;
+ goto done;
+ }
+
+ if ( !user ) {
+ soc->soc_flags |= SLAP_OC_HARDCODE;
+ }
+
+ code = oc_insert(&soc,prev,err);
+done:;
+ if ( code != 0 ) {
+ if ( soc->soc_sups ) {
+ ch_free( soc->soc_sups );
+ }
+
+ if ( soc->soc_required ) {
+ ch_free( soc->soc_required );
+ }
+
+ if ( soc->soc_allowed ) {
+ ch_free( soc->soc_allowed );
+ }
+
+ if ( soc->soc_oidmacro ) {
+ ch_free( soc->soc_oidmacro );
+ }
+
+ ch_free( soc );
+
+ } else if ( rsoc ) {
+ *rsoc = soc;
+ }
+ return code;
+}
+
+void
+oc_unparse( BerVarray *res, ObjectClass *start, ObjectClass *end, int sys )
+{
+ ObjectClass *oc;
+ int i, num;
+ struct berval bv, *bva = NULL, idx;
+ char ibuf[32];
+
+ if ( !start )
+ start = LDAP_STAILQ_FIRST( &oc_list );
+
+ /* count the result size */
+ i = 0;
+ for ( oc=start; oc; oc=LDAP_STAILQ_NEXT(oc, soc_next)) {
+ if ( sys && !(oc->soc_flags & SLAP_OC_HARDCODE)) break;
+ i++;
+ if ( oc == end ) break;
+ }
+ if (!i) return;
+
+ num = i;
+ bva = ch_malloc( (num+1) * sizeof(struct berval) );
+ BER_BVZERO( bva );
+ idx.bv_val = ibuf;
+ if ( sys ) {
+ idx.bv_len = 0;
+ ibuf[0] = '\0';
+ }
+ i = 0;
+ for ( oc=start; oc; oc=LDAP_STAILQ_NEXT(oc, soc_next)) {
+ LDAPObjectClass loc, *locp;
+ if ( sys && !(oc->soc_flags & SLAP_OC_HARDCODE)) break;
+ if ( oc->soc_oidmacro ) {
+ loc = oc->soc_oclass;
+ loc.oc_oid = oc->soc_oidmacro;
+ locp = &loc;
+ } else {
+ locp = &oc->soc_oclass;
+ }
+ if ( ldap_objectclass2bv( locp, &bv ) == NULL ) {
+ ber_bvarray_free( bva );
+ }
+ if ( !sys ) {
+ idx.bv_len = sprintf(idx.bv_val, "{%d}", i);
+ }
+ bva[i].bv_len = idx.bv_len + bv.bv_len;
+ bva[i].bv_val = ch_malloc( bva[i].bv_len + 1 );
+ strcpy( bva[i].bv_val, ibuf );
+ strcpy( bva[i].bv_val + idx.bv_len, bv.bv_val );
+ i++;
+ bva[i].bv_val = NULL;
+ ldap_memfree( bv.bv_val );
+ if ( oc == end ) break;
+ }
+ *res = bva;
+}
+
+int
+oc_schema_info( Entry *e )
+{
+ AttributeDescription *ad_objectClasses = slap_schema.si_ad_objectClasses;
+ ObjectClass *oc;
+ struct berval val;
+ struct berval nval;
+
+ LDAP_STAILQ_FOREACH( oc, &oc_list, soc_next ) {
+ if( oc->soc_flags & SLAP_OC_HIDE ) continue;
+
+ if ( ldap_objectclass2bv( &oc->soc_oclass, &val ) == NULL ) {
+ return -1;
+ }
+
+ nval = oc->soc_cname;
+
+#if 0
+ Debug( LDAP_DEBUG_TRACE, "Merging oc [%ld] %s (%s)\n",
+ (long) val.bv_len, val.bv_val, nval.bv_val );
+#endif
+
+ if( attr_merge_one( e, ad_objectClasses, &val, &nval ) ) {
+ return -1;
+ }
+ ldap_memfree( val.bv_val );
+ }
+ return 0;
+}
+
+int
+register_oc( const char *def, ObjectClass **soc, int dupok )
+{
+ LDAPObjectClass *oc;
+ int code;
+ const char *err;
+
+ oc = ldap_str2objectclass( def, &code, &err, LDAP_SCHEMA_ALLOW_ALL );
+ if ( !oc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "register_oc: objectclass \"%s\": %s, %s\n",
+ def, ldap_scherr2str(code), err );
+ return code;
+ }
+ code = oc_add(oc,0,NULL,NULL,&err);
+ if ( code && ( code != SLAP_SCHERR_CLASS_DUP || !dupok )) {
+ Debug( LDAP_DEBUG_ANY,
+ "register_oc: objectclass \"%s\": %s, %s\n",
+ def, scherr2str(code), err );
+ ldap_objectclass_free(oc);
+ return code;
+ }
+ if ( soc )
+ *soc = oc_find(oc->oc_names[0]);
+ if ( code ) {
+ ldap_objectclass_free(oc);
+ } else {
+ ldap_memfree(oc);
+ }
+ return 0;
+}
diff --git a/servers/slapd/oidm.c b/servers/slapd/oidm.c
new file mode 100644
index 0000000..19725ac
--- /dev/null
+++ b/servers/slapd/oidm.c
@@ -0,0 +1,217 @@
+/* oidm.c - object identifier macro routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "lutil.h"
+#include "slap-config.h"
+
+static LDAP_STAILQ_HEAD(OidMacroList, OidMacro) om_list
+ = LDAP_STAILQ_HEAD_INITIALIZER(om_list);
+
+OidMacro *om_sys_tail;
+
+/* Replace an OID Macro invocation with its full numeric OID.
+ * If the macro is used with "macroname:suffix" append ".suffix"
+ * to the expansion.
+ */
+char *
+oidm_find(char *oid)
+{
+ OidMacro *om;
+
+ /* OID macros must start alpha */
+ if ( OID_LEADCHAR( *oid ) ) {
+ return oid;
+ }
+
+ LDAP_STAILQ_FOREACH( om, &om_list, som_next ) {
+ BerVarray names = om->som_names;
+
+ if( names == NULL ) {
+ continue;
+ }
+
+ for( ; !BER_BVISNULL( names ) ; names++ ) {
+ int pos = dscompare(names->bv_val, oid, ':');
+
+ if( pos ) {
+ int suflen = strlen(oid + pos);
+ char *tmp = SLAP_MALLOC( om->som_oid.bv_len
+ + suflen + 1);
+ if( tmp == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "oidm_find: SLAP_MALLOC failed" );
+ return NULL;
+ }
+ strcpy(tmp, om->som_oid.bv_val);
+ if( suflen ) {
+ suflen = om->som_oid.bv_len;
+ tmp[suflen++] = '.';
+ strcpy(tmp+suflen, oid+pos+1);
+ }
+ return tmp;
+ }
+ }
+ }
+ return NULL;
+}
+
+void
+oidm_destroy()
+{
+ OidMacro *om;
+ while( !LDAP_STAILQ_EMPTY( &om_list )) {
+ om = LDAP_STAILQ_FIRST( &om_list );
+ LDAP_STAILQ_REMOVE_HEAD( &om_list, som_next );
+
+ ber_bvarray_free(om->som_names);
+ ber_bvarray_free(om->som_subs);
+ free(om->som_oid.bv_val);
+ free(om);
+
+ }
+}
+
+int
+parse_oidm(
+ struct config_args_s *c,
+ int user,
+ OidMacro **rom)
+{
+ char *oid, *oidv;
+ OidMacro *om = NULL, *prev = NULL;
+ struct berval bv;
+
+ oidv = oidm_find( c->argv[2] );
+ if( !oidv ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s: OID %s not recognized",
+ c->argv[0], c->argv[2] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s %s\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ oid = oidm_find( c->argv[1] );
+ if( oid != NULL ) {
+ int rc;
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s: \"%s\" previously defined \"%s\"",
+ c->argv[0], c->argv[1], oid );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s %s\n", c->log, c->cr_msg );
+ /* Allow duplicate if the definition is identical */
+ rc = strcmp( oid, oidv ) != 0;
+ SLAP_FREE( oid );
+ if ( oidv != c->argv[2] )
+ SLAP_FREE( oidv );
+ return rc;
+ }
+
+ om = (OidMacro *) SLAP_CALLOC( sizeof(OidMacro), 1 );
+ if( om == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s: SLAP_CALLOC failed", c->argv[0] );
+ Debug( LDAP_DEBUG_ANY,
+ "%s %s\n", c->log, c->cr_msg );
+ if ( oidv != c->argv[2] )
+ SLAP_FREE( oidv );
+ return 1;
+ }
+
+ om->som_names = NULL;
+ om->som_subs = NULL;
+ ber_str2bv( c->argv[1], 0, 1, &bv );
+ ber_bvarray_add( &om->som_names, &bv );
+ ber_str2bv( c->argv[2], 0, 1, &bv );
+ ber_bvarray_add( &om->som_subs, &bv );
+ om->som_oid.bv_val = oidv;
+
+ if (om->som_oid.bv_val == c->argv[2]) {
+ om->som_oid.bv_val = ch_strdup( c->argv[2] );
+ }
+
+ om->som_oid.bv_len = strlen( om->som_oid.bv_val );
+ if ( !user ) {
+ om->som_flags |= SLAP_OM_HARDCODE;
+ prev = om_sys_tail;
+ om_sys_tail = om;
+ }
+
+ if ( prev ) {
+ LDAP_STAILQ_INSERT_AFTER( &om_list, prev, om, som_next );
+ } else {
+ LDAP_STAILQ_INSERT_TAIL( &om_list, om, som_next );
+ }
+ if ( rom ) *rom = om;
+ return 0;
+}
+
+void oidm_unparse( BerVarray *res, OidMacro *start, OidMacro *end, int sys )
+{
+ OidMacro *om;
+ int i, j, num;
+ struct berval *bva = NULL, idx;
+ char ibuf[32], *ptr;
+
+ if ( !start )
+ start = LDAP_STAILQ_FIRST( &om_list );
+
+ /* count the result size */
+ i = 0;
+ for ( om=start; om; om=LDAP_STAILQ_NEXT(om, som_next)) {
+ if ( sys && !(om->som_flags & SLAP_OM_HARDCODE)) break;
+ for ( j=0; !BER_BVISNULL(&om->som_names[j]); j++ );
+ i += j;
+ if ( om == end ) break;
+ }
+ num = i;
+ if (!i) return;
+
+ bva = ch_malloc( (num+1) * sizeof(struct berval) );
+ BER_BVZERO( bva+num );
+ idx.bv_val = ibuf;
+ if ( sys ) {
+ idx.bv_len = 0;
+ ibuf[0] = '\0';
+ }
+ for ( i=0,om=start; om; om=LDAP_STAILQ_NEXT(om, som_next)) {
+ if ( sys && !(om->som_flags & SLAP_OM_HARDCODE)) break;
+ for ( j=0; !BER_BVISNULL(&om->som_names[j]); i++,j++ ) {
+ if ( !sys ) {
+ idx.bv_len = sprintf(idx.bv_val, "{%d}", i );
+ }
+ bva[i].bv_len = idx.bv_len + om->som_names[j].bv_len +
+ om->som_subs[j].bv_len + 1;
+ bva[i].bv_val = ch_malloc( bva[i].bv_len + 1 );
+ ptr = lutil_strcopy( bva[i].bv_val, ibuf );
+ ptr = lutil_strcopy( ptr, om->som_names[j].bv_val );
+ *ptr++ = ' ';
+ strcpy( ptr, om->som_subs[j].bv_val );
+ }
+ if ( i>=num ) break;
+ if ( om == end ) break;
+ }
+ *res = bva;
+}
diff --git a/servers/slapd/operation.c b/servers/slapd/operation.c
new file mode 100644
index 0000000..b4b4830
--- /dev/null
+++ b/servers/slapd/operation.c
@@ -0,0 +1,252 @@
+/* operation.c - routines to deal with pending ldap operations */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+#ifdef LDAP_SLAPI
+#include "slapi/slapi.h"
+#endif
+
+static ldap_pvt_thread_mutex_t slap_op_mutex;
+static time_t last_time;
+static int last_incr;
+
+void slap_op_init(void)
+{
+ struct timeval tv;
+ ldap_pvt_thread_mutex_init( &slap_op_mutex );
+ gettimeofday( &tv, NULL );
+ last_time = tv.tv_sec;
+ last_incr = tv.tv_usec;
+}
+
+void slap_op_destroy(void)
+{
+ ldap_pvt_thread_mutex_destroy( &slap_op_mutex );
+}
+
+static void
+slap_op_q_destroy( void *key, void *data )
+{
+ Operation *op, *op2;
+ for ( op = data; op; op = op2 ) {
+ op2 = LDAP_STAILQ_NEXT( op, o_next );
+ ber_memfree_x( op, NULL );
+ }
+}
+
+void
+slap_op_groups_free( Operation *op )
+{
+ GroupAssertion *g, *n;
+ for ( g = op->o_groups; g; g = n ) {
+ n = g->ga_next;
+ slap_sl_free( g, op->o_tmpmemctx );
+ }
+ op->o_groups = NULL;
+}
+
+void
+slap_op_free( Operation *op, void *ctx )
+{
+ OperationBuffer *opbuf;
+
+ assert( LDAP_STAILQ_NEXT(op, o_next) == NULL );
+
+ /* paranoia */
+ op->o_abandon = 1;
+
+ if ( op->o_ber != NULL ) {
+ ber_free( op->o_ber, 1 );
+ }
+ if ( !BER_BVISNULL( &op->o_dn ) ) {
+ ch_free( op->o_dn.bv_val );
+ }
+ if ( !BER_BVISNULL( &op->o_ndn ) ) {
+ ch_free( op->o_ndn.bv_val );
+ }
+ if ( !BER_BVISNULL( &op->o_authmech ) ) {
+ ch_free( op->o_authmech.bv_val );
+ }
+ if ( op->o_ctrls != NULL ) {
+ slap_free_ctrls( op, op->o_ctrls );
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ if ( op->o_res_ber != NULL ) {
+ ber_free( op->o_res_ber, 1 );
+ }
+#endif
+
+ if ( op->o_groups ) {
+ slap_op_groups_free( op );
+ }
+
+#if defined( LDAP_SLAPI )
+ if ( slapi_plugins_used ) {
+ slapi_int_free_object_extensions( SLAPI_X_EXT_OPERATION, op );
+ }
+#endif /* defined( LDAP_SLAPI ) */
+
+ if ( !BER_BVISNULL( &op->o_csn ) ) {
+ op->o_tmpfree( op->o_csn.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( op->o_pagedresults_state != NULL ) {
+ op->o_tmpfree( op->o_pagedresults_state, op->o_tmpmemctx );
+ }
+
+ /* Selectively zero out the struct. Ignore fields that will
+ * get explicitly initialized later anyway. Keep o_abandon intact.
+ */
+ opbuf = (OperationBuffer *) op;
+ op->o_bd = NULL;
+ BER_BVZERO( &op->o_req_dn );
+ BER_BVZERO( &op->o_req_ndn );
+ memset( op->o_hdr, 0, sizeof( *op->o_hdr ));
+ memset( &op->o_request, 0, sizeof( op->o_request ));
+ memset( &op->o_do_not_cache, 0, sizeof( Operation ) - offsetof( Operation, o_do_not_cache ));
+ memset( opbuf->ob_controls, 0, sizeof( opbuf->ob_controls ));
+ op->o_controls = opbuf->ob_controls;
+
+ if ( ctx ) {
+ Operation *op2 = NULL;
+ ldap_pvt_thread_pool_setkey( ctx, (void *)slap_op_free,
+ op, slap_op_q_destroy, (void **)&op2, NULL );
+ LDAP_STAILQ_NEXT( op, o_next ) = op2;
+ if ( op2 ) {
+ op->o_tincr = op2->o_tincr + 1;
+ /* No more than 10 ops on per-thread free list */
+ if ( op->o_tincr > 10 ) {
+ ldap_pvt_thread_pool_setkey( ctx, (void *)slap_op_free,
+ op2, slap_op_q_destroy, NULL, NULL );
+ ber_memfree_x( op, NULL );
+ }
+ } else {
+ op->o_tincr = 1;
+ }
+ } else {
+ ber_memfree_x( op, NULL );
+ }
+}
+
+void
+slap_op_time(time_t *t, int *nop)
+{
+ struct timeval tv;
+ ldap_pvt_thread_mutex_lock( &slap_op_mutex );
+ gettimeofday( &tv, NULL );
+ *t = tv.tv_sec;
+ if ( *t == last_time ) {
+ *nop = ++last_incr;
+ } else {
+ last_time = *t;
+ last_incr = 0;
+ *nop = 0;
+ }
+ ldap_pvt_thread_mutex_unlock( &slap_op_mutex );
+ nop[1] = tv.tv_usec;
+}
+
+Operation *
+slap_op_alloc(
+ BerElement *ber,
+ ber_int_t msgid,
+ ber_tag_t tag,
+ ber_int_t id,
+ void *ctx )
+{
+ Operation *op = NULL;
+
+ if ( ctx ) {
+ void *otmp = NULL;
+ ldap_pvt_thread_pool_getkey( ctx, (void *)slap_op_free, &otmp, NULL );
+ if ( otmp ) {
+ op = otmp;
+ otmp = LDAP_STAILQ_NEXT( op, o_next );
+ ldap_pvt_thread_pool_setkey( ctx, (void *)slap_op_free,
+ otmp, slap_op_q_destroy, NULL, NULL );
+ op->o_abandon = 0;
+ op->o_cancel = 0;
+ }
+ }
+ if (!op) {
+ op = (Operation *) ch_calloc( 1, sizeof(OperationBuffer) );
+ op->o_hdr = &((OperationBuffer *) op)->ob_hdr;
+ op->o_controls = ((OperationBuffer *) op)->ob_controls;
+ }
+
+ op->o_ber = ber;
+ op->o_msgid = msgid;
+ op->o_tag = tag;
+
+ slap_op_time( &op->o_time, &op->o_tincr );
+ op->o_opid = id;
+
+#if defined( LDAP_SLAPI )
+ if ( slapi_plugins_used ) {
+ slapi_int_create_object_extensions( SLAPI_X_EXT_OPERATION, op );
+ }
+#endif /* defined( LDAP_SLAPI ) */
+
+ return( op );
+}
+
+slap_op_t
+slap_req2op( ber_tag_t tag )
+{
+ switch ( tag ) {
+ case LDAP_REQ_BIND:
+ return SLAP_OP_BIND;
+ case LDAP_REQ_UNBIND:
+ return SLAP_OP_UNBIND;
+ case LDAP_REQ_ADD:
+ return SLAP_OP_ADD;
+ case LDAP_REQ_DELETE:
+ return SLAP_OP_DELETE;
+ case LDAP_REQ_MODRDN:
+ return SLAP_OP_MODRDN;
+ case LDAP_REQ_MODIFY:
+ return SLAP_OP_MODIFY;
+ case LDAP_REQ_COMPARE:
+ return SLAP_OP_COMPARE;
+ case LDAP_REQ_SEARCH:
+ return SLAP_OP_SEARCH;
+ case LDAP_REQ_ABANDON:
+ return SLAP_OP_ABANDON;
+ case LDAP_REQ_EXTENDED:
+ return SLAP_OP_EXTENDED;
+ }
+
+ return SLAP_OP_LAST;
+}
diff --git a/servers/slapd/operational.c b/servers/slapd/operational.c
new file mode 100644
index 0000000..77dce46
--- /dev/null
+++ b/servers/slapd/operational.c
@@ -0,0 +1,90 @@
+/* operational.c - routines to deal with on-the-fly operational attrs */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include "slap.h"
+
+/*
+ * helpers for on-the-fly operational attribute generation
+ */
+
+Attribute *
+slap_operational_subschemaSubentry( Backend *be )
+{
+ Attribute *a;
+
+ /* The backend wants to take care of it */
+ if ( be && !SLAP_FRONTEND(be) && be->be_schemadn.bv_val ) return NULL;
+
+ a = attr_alloc( slap_schema.si_ad_subschemaSubentry );
+
+ a->a_numvals = 1;
+ a->a_vals = ch_malloc( 2 * sizeof( struct berval ) );
+ ber_dupbv( a->a_vals, &frontendDB->be_schemadn );
+ a->a_vals[1].bv_len = 0;
+ a->a_vals[1].bv_val = NULL;
+
+ a->a_nvals = ch_malloc( 2 * sizeof( struct berval ) );
+ ber_dupbv( a->a_nvals, &frontendDB->be_schemandn );
+ a->a_nvals[1].bv_len = 0;
+ a->a_nvals[1].bv_val = NULL;
+
+ return a;
+}
+
+Attribute *
+slap_operational_entryDN( Entry *e )
+{
+ Attribute *a;
+
+ assert( e != NULL );
+ assert( !BER_BVISNULL( &e->e_name ) );
+ assert( !BER_BVISNULL( &e->e_nname ) );
+
+ a = attr_alloc( slap_schema.si_ad_entryDN );
+
+ a->a_numvals = 1;
+ a->a_vals = ch_malloc( 2 * sizeof( struct berval ) );
+ ber_dupbv( &a->a_vals[ 0 ], &e->e_name );
+ BER_BVZERO( &a->a_vals[ 1 ] );
+
+ a->a_nvals = ch_malloc( 2 * sizeof( struct berval ) );
+ ber_dupbv( &a->a_nvals[ 0 ], &e->e_nname );
+ BER_BVZERO( &a->a_nvals[ 1 ] );
+
+ return a;
+}
+
+Attribute *
+slap_operational_hasSubordinate( int hs )
+{
+ Attribute *a;
+ struct berval val;
+
+ val = hs ? slap_true_bv : slap_false_bv;
+
+ a = attr_alloc( slap_schema.si_ad_hasSubordinates );
+ a->a_numvals = 1;
+ a->a_vals = ch_malloc( 2 * sizeof( struct berval ) );
+
+ ber_dupbv( &a->a_vals[0], &val );
+ a->a_vals[1].bv_val = NULL;
+
+ a->a_nvals = a->a_vals;
+
+ return a;
+}
+
diff --git a/servers/slapd/overlays/Makefile.in b/servers/slapd/overlays/Makefile.in
new file mode 100644
index 0000000..e6711fe
--- /dev/null
+++ b/servers/slapd/overlays/Makefile.in
@@ -0,0 +1,172 @@
+# Makefile.in for overlays
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2003-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SRCS = overlays.c \
+ accesslog.c \
+ auditlog.c \
+ autoca.c \
+ constraint.c \
+ dds.c \
+ deref.c \
+ dyngroup.c \
+ dynlist.c \
+ homedir.c \
+ memberof.c \
+ otp.c \
+ pcache.c \
+ collect.c \
+ ppolicy.c \
+ refint.c \
+ remoteauth.c \
+ retcode.c \
+ rwm.c rwmconf.c rwmdn.c rwmmap.c \
+ seqmod.c \
+ sssvlv.c \
+ syncprov.c \
+ translucent.c \
+ unique.c \
+ valsort.c
+OBJS = statover.o \
+ @SLAPD_STATIC_OVERLAYS@ \
+ overlays.o
+
+# Add here the objs that are needed by overlays, but do not make it
+# into SLAPD_STATIC_OVERLAYS...
+OBJDEP=rwm.o rwmconf.o rwmdn.o rwmmap.o
+
+LTONLY_MOD = $(LTONLY_mod)
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+MOD_DEFS = -DSLAPD_IMPORT
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+
+LIBRARY = ../liboverlays.a
+PROGRAMS = @SLAPD_DYNAMIC_OVERLAYS@
+
+XINCPATH = -I.. -I$(srcdir)/..
+XDEFS = $(MODULES_CPPFLAGS)
+
+static: $(LIBRARY)
+
+dynamic: $(PROGRAMS)
+
+accesslog.la : accesslog.lo
+ $(LTLINK_MOD) -module -o $@ accesslog.lo version.lo $(LINK_LIBS)
+
+auditlog.la : auditlog.lo
+ $(LTLINK_MOD) -module -o $@ auditlog.lo version.lo $(LINK_LIBS)
+
+autoca.la : autoca.lo
+ $(LTLINK_MOD) -module -o $@ autoca.lo version.lo $(LINK_LIBS)
+
+constraint.la : constraint.lo
+ $(LTLINK_MOD) -module -o $@ constraint.lo version.lo $(LINK_LIBS)
+
+dds.la : dds.lo
+ $(LTLINK_MOD) -module -o $@ dds.lo version.lo $(LINK_LIBS)
+
+deref.la : deref.lo
+ $(LTLINK_MOD) -module -o $@ deref.lo version.lo $(LINK_LIBS)
+
+dyngroup.la : dyngroup.lo
+ $(LTLINK_MOD) -module -o $@ dyngroup.lo version.lo $(LINK_LIBS)
+
+dynlist.la : dynlist.lo
+ $(LTLINK_MOD) -module -o $@ dynlist.lo version.lo $(LINK_LIBS)
+
+homedir.la : homedir.lo
+ $(LTLINK_MOD) -module -o $@ homedir.lo version.lo $(LINK_LIBS)
+
+memberof.la : memberof.lo
+ $(LTLINK_MOD) -module -o $@ memberof.lo version.lo $(LINK_LIBS)
+
+otp.la : otp.lo
+ $(LTLINK_MOD) -module -o $@ otp.lo version.lo $(LINK_LIBS)
+
+pcache.la : pcache.lo
+ $(LTLINK_MOD) -module -o $@ pcache.lo version.lo $(LINK_LIBS)
+
+collect.la : collect.lo
+ $(LTLINK_MOD) -module -o $@ collect.lo version.lo $(LINK_LIBS)
+
+ppolicy.la : ppolicy.lo
+ $(LTLINK_MOD) -module -o $@ ppolicy.lo version.lo $(LINK_LIBS) $(MODULES_LIBS)
+
+refint.la : refint.lo
+ $(LTLINK_MOD) -module -o $@ refint.lo version.lo $(LINK_LIBS)
+
+remoteauth.la : remoteauth.lo
+ $(LTLINK_MOD) -module -o $@ remoteauth.lo version.lo $(LINK_LIBS)
+
+retcode.la : retcode.lo
+ $(LTLINK_MOD) -module -o $@ retcode.lo version.lo $(LINK_LIBS)
+
+rwm_x.o: rwm.o rwmconf.o rwmdn.o rwmmap.o
+ $(LD) -r -o $@ rwm.o rwmconf.o rwmdn.o rwmmap.o
+
+rwm.la : rwm.lo rwmconf.lo rwmdn.lo rwmmap.lo
+ $(LTLINK_MOD) -module -o $@ rwm.lo rwmconf.lo rwmdn.lo rwmmap.lo version.lo $(LINK_LIBS)
+
+seqmod.la : seqmod.lo
+ $(LTLINK_MOD) -module -o $@ seqmod.lo version.lo $(LINK_LIBS)
+
+sssvlv.la : sssvlv.lo
+ $(LTLINK_MOD) -module -o $@ sssvlv.lo version.lo $(LINK_LIBS)
+
+syncprov.la : syncprov.lo
+ $(LTLINK_MOD) -module -o $@ syncprov.lo version.lo $(LINK_LIBS)
+
+translucent.la : translucent.lo
+ $(LTLINK_MOD) -module -o $@ translucent.lo version.lo $(LINK_LIBS)
+
+unique.la : unique.lo
+ $(LTLINK_MOD) -module -o $@ unique.lo version.lo $(LINK_LIBS)
+
+valsort.la : valsort.lo
+ $(LTLINK_MOD) -module -o $@ valsort.lo version.lo $(LINK_LIBS)
+
+install-local: $(PROGRAMS)
+ @if test -n "$?" ; then \
+ $(MKDIR) $(DESTDIR)$(moduledir); \
+ $(LTINSTALL) $(INSTALLFLAGS) -m 755 $? $(DESTDIR)$(moduledir);\
+ fi
+
+MKDEPFLAG = -l
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LTCOMPILE_MOD) $<
+
+statover.o: statover.c $(srcdir)/../slap.h
+
+$(LIBRARY): $(OBJS) version.lo
+ $(AR) rs $@ $(OBJS)
+
+# Must fixup depends for non-libtool objects
+depend-local: depend-common
+ @if test -n "$(OBJS)"; then \
+ OBJ2=`echo $(OBJS) $(OBJDEP) | $(SED) -e 's/\.o//g'`; \
+ SCR=''; for i in $$OBJ2; do SCR="$$SCR -e s/^$$i.lo:/$$i.o:/"; done; \
+ mv Makefile Makefile.bak; $(SED) $$SCR Makefile.bak > Makefile && \
+ $(RM) Makefile.bak; fi
+
+veryclean-local:
+ $(RM) statover.c
+
diff --git a/servers/slapd/overlays/README b/servers/slapd/overlays/README
new file mode 100644
index 0000000..e426e4b
--- /dev/null
+++ b/servers/slapd/overlays/README
@@ -0,0 +1,5 @@
+This directory contains a number of SLAPD overlays, some
+project-maintained, some not. Some are generally usable,
+others are purely experimental. Additional overlays can
+be found in the contrib/slapd-modules directory.
+
diff --git a/servers/slapd/overlays/accesslog.c b/servers/slapd/overlays/accesslog.c
new file mode 100644
index 0000000..cbdaa53
--- /dev/null
+++ b/servers/slapd/overlays/accesslog.c
@@ -0,0 +1,2774 @@
+/* accesslog.c - log operations for audit/history purposes */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2022 The OpenLDAP Foundation.
+ * Portions copyright 2004-2005 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_ACCESSLOG
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/ctype.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+#include "ldap_rq.h"
+
+#define LOG_OP_ADD 0x001
+#define LOG_OP_DELETE 0x002
+#define LOG_OP_MODIFY 0x004
+#define LOG_OP_MODRDN 0x008
+#define LOG_OP_COMPARE 0x010
+#define LOG_OP_SEARCH 0x020
+#define LOG_OP_BIND 0x040
+#define LOG_OP_UNBIND 0x080
+#define LOG_OP_ABANDON 0x100
+#define LOG_OP_EXTENDED 0x200
+#define LOG_OP_UNKNOWN 0x400
+
+#define LOG_OP_WRITES (LOG_OP_ADD|LOG_OP_DELETE|LOG_OP_MODIFY|LOG_OP_MODRDN)
+#define LOG_OP_READS (LOG_OP_COMPARE|LOG_OP_SEARCH)
+#define LOG_OP_SESSION (LOG_OP_BIND|LOG_OP_UNBIND|LOG_OP_ABANDON)
+#define LOG_OP_ALL (LOG_OP_READS|LOG_OP_WRITES|LOG_OP_SESSION| \
+ LOG_OP_EXTENDED|LOG_OP_UNKNOWN)
+
+typedef struct log_attr {
+ struct log_attr *next;
+ AttributeDescription *attr;
+} log_attr;
+
+typedef struct log_base {
+ struct log_base *lb_next;
+ slap_mask_t lb_ops;
+ struct berval lb_base;
+ struct berval lb_line;
+} log_base;
+
+typedef struct log_info {
+ BackendDB *li_db;
+ struct berval li_db_suffix;
+ int li_open;
+
+ slap_mask_t li_ops;
+ int li_age;
+ int li_cycle;
+ struct re_s *li_task;
+ Filter *li_oldf;
+ Entry *li_old;
+ log_attr *li_oldattrs;
+ struct berval li_uuid;
+ int li_success;
+ log_base *li_bases;
+ BerVarray li_mincsn;
+ int *li_sids, li_numcsns;
+ ldap_pvt_thread_mutex_t li_op_rmutex;
+ ldap_pvt_thread_mutex_t li_log_mutex;
+} log_info;
+
+static ConfigDriver log_cf_gen;
+
+enum {
+ LOG_DB = 1,
+ LOG_OPS,
+ LOG_PURGE,
+ LOG_SUCCESS,
+ LOG_OLD,
+ LOG_OLDATTR,
+ LOG_BASE
+};
+
+static ConfigTable log_cfats[] = {
+ { "logdb", "suffix", 2, 2, 0, ARG_DN|ARG_QUOTE|ARG_MAGIC|LOG_DB,
+ log_cf_gen, "( OLcfgOvAt:4.1 NAME 'olcAccessLogDB' "
+ "DESC 'Suffix of database for log content' "
+ "EQUALITY distinguishedNameMatch "
+ "SUP distinguishedName SINGLE-VALUE )", NULL, NULL },
+ { "logops", "op|writes|reads|session|all", 2, 0, 0,
+ ARG_MAGIC|LOG_OPS,
+ log_cf_gen, "( OLcfgOvAt:4.2 NAME 'olcAccessLogOps' "
+ "DESC 'Operation types to log' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "logpurge", "age> <interval", 3, 3, 0, ARG_MAGIC|LOG_PURGE,
+ log_cf_gen, "( OLcfgOvAt:4.3 NAME 'olcAccessLogPurge' "
+ "DESC 'Log cleanup parameters' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "logsuccess", NULL, 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|LOG_SUCCESS,
+ log_cf_gen, "( OLcfgOvAt:4.4 NAME 'olcAccessLogSuccess' "
+ "DESC 'Log successful ops only' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "logold", "filter", 2, 2, 0, ARG_MAGIC|LOG_OLD,
+ log_cf_gen, "( OLcfgOvAt:4.5 NAME 'olcAccessLogOld' "
+ "DESC 'Log old values when modifying entries matching the filter' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "logoldattr", "attrs", 2, 0, 0, ARG_MAGIC|LOG_OLDATTR,
+ log_cf_gen, "( OLcfgOvAt:4.6 NAME 'olcAccessLogOldAttr' "
+ "DESC 'Log old values of these attributes even if unmodified' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "logbase", "op|writes|reads|session|all< <baseDN", 3, 3, 0,
+ ARG_MAGIC|LOG_BASE,
+ log_cf_gen, "( OLcfgOvAt:4.7 NAME 'olcAccessLogBase' "
+ "DESC 'Operation types to log under a specific branch' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { NULL }
+};
+
+static ConfigOCs log_cfocs[] = {
+ { "( OLcfgOvOc:4.1 "
+ "NAME 'olcAccessLogConfig' "
+ "DESC 'Access log configuration' "
+ "SUP olcOverlayConfig "
+ "MUST olcAccessLogDB "
+ "MAY ( olcAccessLogOps $ olcAccessLogPurge $ olcAccessLogSuccess $ "
+ "olcAccessLogOld $ olcAccessLogOldAttr $ olcAccessLogBase ) )",
+ Cft_Overlay, log_cfats },
+ { NULL }
+};
+
+static slap_verbmasks logops[] = {
+ { BER_BVC("all"), LOG_OP_ALL },
+ { BER_BVC("writes"), LOG_OP_WRITES },
+ { BER_BVC("session"), LOG_OP_SESSION },
+ { BER_BVC("reads"), LOG_OP_READS },
+ { BER_BVC("add"), LOG_OP_ADD },
+ { BER_BVC("delete"), LOG_OP_DELETE },
+ { BER_BVC("modify"), LOG_OP_MODIFY },
+ { BER_BVC("modrdn"), LOG_OP_MODRDN },
+ { BER_BVC("compare"), LOG_OP_COMPARE },
+ { BER_BVC("search"), LOG_OP_SEARCH },
+ { BER_BVC("bind"), LOG_OP_BIND },
+ { BER_BVC("unbind"), LOG_OP_UNBIND },
+ { BER_BVC("abandon"), LOG_OP_ABANDON },
+ { BER_BVC("extended"), LOG_OP_EXTENDED },
+ { BER_BVC("unknown"), LOG_OP_UNKNOWN },
+ { BER_BVNULL, 0 }
+};
+
+/* Start with "add" in logops */
+#define EN_OFFSET 4
+
+enum {
+ LOG_EN_ADD = 0,
+ LOG_EN_DELETE,
+ LOG_EN_MODIFY,
+ LOG_EN_MODRDN,
+ LOG_EN_COMPARE,
+ LOG_EN_SEARCH,
+ LOG_EN_BIND,
+ LOG_EN_UNBIND,
+ LOG_EN_ABANDON,
+ LOG_EN_EXTENDED,
+ LOG_EN_UNKNOWN,
+ LOG_EN__COUNT
+};
+
+static ObjectClass *log_ocs[LOG_EN__COUNT], *log_container,
+ *log_oc_read, *log_oc_write;
+
+#define LOG_SCHEMA_ROOT "1.3.6.1.4.1.4203.666.11.5"
+
+#define LOG_SCHEMA_AT LOG_SCHEMA_ROOT ".1"
+#define LOG_SCHEMA_OC LOG_SCHEMA_ROOT ".2"
+#define LOG_SCHEMA_SYN LOG_SCHEMA_ROOT ".3"
+
+static AttributeDescription *ad_reqDN, *ad_reqStart, *ad_reqEnd, *ad_reqType,
+ *ad_reqSession, *ad_reqResult, *ad_reqAuthzID, *ad_reqControls,
+ *ad_reqRespControls, *ad_reqMethod, *ad_reqAssertion, *ad_reqNewRDN,
+ *ad_reqNewSuperior, *ad_reqDeleteOldRDN, *ad_reqMod,
+ *ad_reqScope, *ad_reqFilter, *ad_reqAttr, *ad_reqEntries,
+ *ad_reqSizeLimit, *ad_reqTimeLimit, *ad_reqAttrsOnly, *ad_reqData,
+ *ad_reqId, *ad_reqMessage, *ad_reqVersion, *ad_reqDerefAliases,
+ *ad_reqReferral, *ad_reqOld, *ad_auditContext, *ad_reqEntryUUID,
+ *ad_minCSN, *ad_reqNewDN;
+
+static int
+logSchemaControlValidate(
+ Syntax *syntax,
+ struct berval *val );
+
+char *mrControl[] = {
+ "objectIdentifierFirstComponentMatch",
+ NULL
+};
+
+static struct {
+ char *oid;
+ slap_syntax_defs_rec syn;
+ char **mrs;
+} lsyntaxes[] = {
+ { LOG_SCHEMA_SYN ".1" ,
+ { "( " LOG_SCHEMA_SYN ".1 DESC 'Control' )",
+ SLAP_SYNTAX_HIDE,
+ NULL,
+ logSchemaControlValidate,
+ NULL },
+ mrControl },
+ { NULL }
+};
+
+static struct {
+ char *at;
+ AttributeDescription **ad;
+} lattrs[] = {
+ { "( " LOG_SCHEMA_AT ".1 NAME 'reqDN' "
+ "DESC 'Target DN of request' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN "
+ "SINGLE-VALUE )", &ad_reqDN },
+ { "( " LOG_SCHEMA_AT ".2 NAME 'reqStart' "
+ "DESC 'Start time of request' "
+ "EQUALITY generalizedTimeMatch "
+ "ORDERING generalizedTimeOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+ "SINGLE-VALUE )", &ad_reqStart },
+ { "( " LOG_SCHEMA_AT ".3 NAME 'reqEnd' "
+ "DESC 'End time of request' "
+ "EQUALITY generalizedTimeMatch "
+ "ORDERING generalizedTimeOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+ "SINGLE-VALUE )", &ad_reqEnd },
+ { "( " LOG_SCHEMA_AT ".4 NAME 'reqType' "
+ "DESC 'Type of request' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )", &ad_reqType },
+ { "( " LOG_SCHEMA_AT ".5 NAME 'reqSession' "
+ "DESC 'Session ID of request' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )", &ad_reqSession },
+ { "( " LOG_SCHEMA_AT ".6 NAME 'reqAuthzID' "
+ "DESC 'Authorization ID of requestor' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN "
+ "SINGLE-VALUE )", &ad_reqAuthzID },
+ { "( " LOG_SCHEMA_AT ".7 NAME 'reqResult' "
+ "DESC 'Result code of request' "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )", &ad_reqResult },
+ { "( " LOG_SCHEMA_AT ".8 NAME 'reqMessage' "
+ "DESC 'Error text of request' "
+ "EQUALITY caseIgnoreMatch "
+ "SUBSTR caseIgnoreSubstringsMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )", &ad_reqMessage },
+ { "( " LOG_SCHEMA_AT ".9 NAME 'reqReferral' "
+ "DESC 'Referrals returned for request' "
+ "SUP labeledURI )", &ad_reqReferral },
+ { "( " LOG_SCHEMA_AT ".10 NAME 'reqControls' "
+ "DESC 'Request controls' "
+ "EQUALITY objectIdentifierFirstComponentMatch "
+ "SYNTAX " LOG_SCHEMA_SYN ".1 "
+ "X-ORDERED 'VALUES' )", &ad_reqControls },
+ { "( " LOG_SCHEMA_AT ".11 NAME 'reqRespControls' "
+ "DESC 'Response controls of request' "
+ "EQUALITY objectIdentifierFirstComponentMatch "
+ "SYNTAX " LOG_SCHEMA_SYN ".1 "
+ "X-ORDERED 'VALUES' )", &ad_reqRespControls },
+ { "( " LOG_SCHEMA_AT ".12 NAME 'reqId' "
+ "DESC 'ID of Request to Abandon' "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )", &ad_reqId },
+ { "( " LOG_SCHEMA_AT ".13 NAME 'reqVersion' "
+ "DESC 'Protocol version of Bind request' "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )", &ad_reqVersion },
+ { "( " LOG_SCHEMA_AT ".14 NAME 'reqMethod' "
+ "DESC 'Bind method of request' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )", &ad_reqMethod },
+ { "( " LOG_SCHEMA_AT ".15 NAME 'reqAssertion' "
+ "DESC 'Compare Assertion of request' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )", &ad_reqAssertion },
+ { "( " LOG_SCHEMA_AT ".16 NAME 'reqMod' "
+ "DESC 'Modifications of request' "
+ "EQUALITY octetStringMatch "
+ "SUBSTR octetStringSubstringsMatch "
+ "SYNTAX OMsOctetString )", &ad_reqMod },
+ { "( " LOG_SCHEMA_AT ".17 NAME 'reqOld' "
+ "DESC 'Old values of entry before request completed' "
+ "EQUALITY octetStringMatch "
+ "SUBSTR octetStringSubstringsMatch "
+ "SYNTAX OMsOctetString )", &ad_reqOld },
+ { "( " LOG_SCHEMA_AT ".18 NAME 'reqNewRDN' "
+ "DESC 'New RDN of request' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN "
+ "SINGLE-VALUE )", &ad_reqNewRDN },
+ { "( " LOG_SCHEMA_AT ".19 NAME 'reqDeleteOldRDN' "
+ "DESC 'Delete old RDN' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )", &ad_reqDeleteOldRDN },
+ { "( " LOG_SCHEMA_AT ".20 NAME 'reqNewSuperior' "
+ "DESC 'New superior DN of request' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN "
+ "SINGLE-VALUE )", &ad_reqNewSuperior },
+ { "( " LOG_SCHEMA_AT ".21 NAME 'reqScope' "
+ "DESC 'Scope of request' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )", &ad_reqScope },
+ { "( " LOG_SCHEMA_AT ".22 NAME 'reqDerefAliases' "
+ "DESC 'Disposition of Aliases in request' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )", &ad_reqDerefAliases },
+ { "( " LOG_SCHEMA_AT ".23 NAME 'reqAttrsOnly' "
+ "DESC 'Attributes and values of request' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )", &ad_reqAttrsOnly },
+ { "( " LOG_SCHEMA_AT ".24 NAME 'reqFilter' "
+ "DESC 'Filter of request' "
+ "EQUALITY caseIgnoreMatch "
+ "SUBSTR caseIgnoreSubstringsMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )", &ad_reqFilter },
+ { "( " LOG_SCHEMA_AT ".25 NAME 'reqAttr' "
+ "DESC 'Attributes of request' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", &ad_reqAttr },
+ { "( " LOG_SCHEMA_AT ".26 NAME 'reqSizeLimit' "
+ "DESC 'Size limit of request' "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )", &ad_reqSizeLimit },
+ { "( " LOG_SCHEMA_AT ".27 NAME 'reqTimeLimit' "
+ "DESC 'Time limit of request' "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )", &ad_reqTimeLimit },
+ { "( " LOG_SCHEMA_AT ".28 NAME 'reqEntries' "
+ "DESC 'Number of entries returned' "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )", &ad_reqEntries },
+ { "( " LOG_SCHEMA_AT ".29 NAME 'reqData' "
+ "DESC 'Data of extended request' "
+ "EQUALITY octetStringMatch "
+ "SUBSTR octetStringSubstringsMatch "
+ "SYNTAX OMsOctetString "
+ "SINGLE-VALUE )", &ad_reqData },
+
+ /*
+ * from <draft-chu-ldap-logschema-01.txt>:
+ *
+
+ ( LOG_SCHEMA_AT .30 NAME 'auditContext'
+ DESC 'DN of auditContainer'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )
+
+ * - removed EQUALITY matchingRule
+ * - changed directoryOperation in dSAOperation
+ */
+ { "( " LOG_SCHEMA_AT ".30 NAME 'auditContext' "
+ "DESC 'DN of auditContainer' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", &ad_auditContext },
+
+ /*
+ * ITS#6656
+ */
+ { "( " LOG_SCHEMA_AT ".31 NAME 'reqEntryUUID' "
+ "DESC 'UUID of entry' "
+ "EQUALITY UUIDMatch "
+ "ORDERING UUIDOrderingMatch "
+ "SYNTAX 1.3.6.1.1.16.1 "
+ "SINGLE-VALUE )", &ad_reqEntryUUID },
+
+ /*
+ * ITS#8486
+ */
+ { "( " LOG_SCHEMA_AT ".32 NAME 'minCSN' "
+ "DESC 'CSN set that the logs are recorded from' "
+ "EQUALITY CSNMatch "
+ "ORDERING CSNOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.4203.666.11.2.1{64} "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )", &ad_minCSN },
+
+ /*
+ * ITS#9552
+ */
+ { "( " LOG_SCHEMA_AT ".33 NAME 'reqNewDN' "
+ "DESC 'New DN after rename' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN "
+ "SINGLE-VALUE )", &ad_reqNewDN },
+ { NULL, NULL }
+};
+
+static struct {
+ char *ot;
+ ObjectClass **oc;
+} locs[] = {
+ { "( " LOG_SCHEMA_OC ".0 NAME 'auditContainer' "
+ "DESC 'AuditLog container' "
+ "SUP top STRUCTURAL "
+ "MAY ( cn $ reqStart $ reqEnd ) )", &log_container },
+ { "( " LOG_SCHEMA_OC ".1 NAME 'auditObject' "
+ "DESC 'OpenLDAP request auditing' "
+ "SUP top STRUCTURAL "
+ "MUST ( reqStart $ reqType $ reqSession ) "
+ "MAY ( reqDN $ reqAuthzID $ reqControls $ reqRespControls $ reqEnd $ "
+ "reqResult $ reqMessage $ reqReferral $ reqEntryUUID ) )",
+ &log_ocs[LOG_EN_UNBIND] },
+ { "( " LOG_SCHEMA_OC ".2 NAME 'auditReadObject' "
+ "DESC 'OpenLDAP read request record' "
+ "SUP auditObject STRUCTURAL )", &log_oc_read },
+ { "( " LOG_SCHEMA_OC ".3 NAME 'auditWriteObject' "
+ "DESC 'OpenLDAP write request record' "
+ "SUP auditObject STRUCTURAL )", &log_oc_write },
+ { "( " LOG_SCHEMA_OC ".4 NAME 'auditAbandon' "
+ "DESC 'Abandon operation' "
+ "SUP auditObject STRUCTURAL "
+ "MUST reqId )", &log_ocs[LOG_EN_ABANDON] },
+ { "( " LOG_SCHEMA_OC ".5 NAME 'auditAdd' "
+ "DESC 'Add operation' "
+ "SUP auditWriteObject STRUCTURAL "
+ "MUST reqMod )", &log_ocs[LOG_EN_ADD] },
+ { "( " LOG_SCHEMA_OC ".6 NAME 'auditBind' "
+ "DESC 'Bind operation' "
+ "SUP auditObject STRUCTURAL "
+ "MUST ( reqVersion $ reqMethod ) )", &log_ocs[LOG_EN_BIND] },
+ { "( " LOG_SCHEMA_OC ".7 NAME 'auditCompare' "
+ "DESC 'Compare operation' "
+ "SUP auditReadObject STRUCTURAL "
+ "MUST reqAssertion )", &log_ocs[LOG_EN_COMPARE] },
+ { "( " LOG_SCHEMA_OC ".8 NAME 'auditDelete' "
+ "DESC 'Delete operation' "
+ "SUP auditWriteObject STRUCTURAL "
+ "MAY reqOld )", &log_ocs[LOG_EN_DELETE] },
+ { "( " LOG_SCHEMA_OC ".9 NAME 'auditModify' "
+ "DESC 'Modify operation' "
+ "SUP auditWriteObject STRUCTURAL "
+ "MAY ( reqOld $ reqMod ) )", &log_ocs[LOG_EN_MODIFY] },
+ { "( " LOG_SCHEMA_OC ".10 NAME 'auditModRDN' "
+ "DESC 'ModRDN operation' "
+ "SUP auditWriteObject STRUCTURAL "
+ "MUST ( reqNewRDN $ reqDeleteOldRDN ) "
+ "MAY ( reqNewSuperior $ reqMod $ reqOld $ reqNewDN ) )",
+ &log_ocs[LOG_EN_MODRDN] },
+ { "( " LOG_SCHEMA_OC ".11 NAME 'auditSearch' "
+ "DESC 'Search operation' "
+ "SUP auditReadObject STRUCTURAL "
+ "MUST ( reqScope $ reqDerefAliases $ reqAttrsonly ) "
+ "MAY ( reqFilter $ reqAttr $ reqEntries $ reqSizeLimit $ "
+ "reqTimeLimit ) )", &log_ocs[LOG_EN_SEARCH] },
+ { "( " LOG_SCHEMA_OC ".12 NAME 'auditExtended' "
+ "DESC 'Extended operation' "
+ "SUP auditObject STRUCTURAL "
+ "MAY reqData )", &log_ocs[LOG_EN_EXTENDED] },
+ { NULL, NULL }
+};
+
+#define RDNEQ "reqStart="
+
+/* Our time intervals are of the form [ddd+]hh:mm[:ss]
+ * If a field is present, it must be two digits. (Except for
+ * days, which can be arbitrary width.)
+ */
+static int
+log_age_parse(char *agestr)
+{
+ int t1, t2;
+ int gotdays = 0;
+ char *endptr;
+
+ t1 = strtol( agestr, &endptr, 10 );
+ /* Is there a days delimiter? */
+ if ( *endptr == '+' ) {
+ /* 32 bit time only covers about 68 years */
+ if ( t1 < 0 || t1 > 25000 )
+ return -1;
+ t1 *= 24;
+ gotdays = 1;
+ agestr = endptr + 1;
+ } else {
+ if ( agestr[2] != ':' ) {
+ /* No valid delimiter found, fail */
+ return -1;
+ }
+ t1 *= 60;
+ agestr += 3;
+ }
+
+ t2 = atoi( agestr );
+ t1 += t2;
+
+ if ( agestr[2] ) {
+ /* if there's a delimiter, it can only be a colon */
+ if ( agestr[2] != ':' )
+ return -1;
+ } else {
+ /* If we're at the end of the string, and we started with days,
+ * fail because we expected to find minutes too.
+ */
+ return gotdays ? -1 : t1 * 60;
+ }
+
+ agestr += 3;
+ t2 = atoi( agestr );
+
+ /* last field can only be seconds */
+ if ( agestr[2] && ( agestr[2] != ':' || !gotdays ))
+ return -1;
+ t1 *= 60;
+ t1 += t2;
+
+ if ( agestr[2] ) {
+ agestr += 3;
+ if ( agestr[2] )
+ return -1;
+ t1 *= 60;
+ t1 += atoi( agestr );
+ } else if ( gotdays ) {
+ /* only got days+hh:mm */
+ t1 *= 60;
+ }
+ return t1;
+}
+
+static void
+log_age_unparse( int age, struct berval *agebv, size_t size )
+{
+ int dd, hh, mm, ss, len;
+ char *ptr;
+
+ assert( size > 0 );
+
+ ss = age % 60;
+ age /= 60;
+ mm = age % 60;
+ age /= 60;
+ hh = age % 24;
+ age /= 24;
+ dd = age;
+
+ ptr = agebv->bv_val;
+
+ if ( dd ) {
+ len = snprintf( ptr, size, "%d+", dd );
+ assert( len >= 0 && (unsigned) len < size );
+ size -= len;
+ ptr += len;
+ }
+ len = snprintf( ptr, size, "%02d:%02d", hh, mm );
+ assert( len >= 0 && (unsigned) len < size );
+ size -= len;
+ ptr += len;
+ if ( ss ) {
+ len = snprintf( ptr, size, ":%02d", ss );
+ assert( len >= 0 && (unsigned) len < size );
+ size -= len;
+ ptr += len;
+ }
+
+ agebv->bv_len = ptr - agebv->bv_val;
+}
+
+static slap_callback nullsc;
+
+#define PURGE_INCREMENT 100
+
+typedef struct purge_data {
+ struct log_info *li;
+ int slots;
+ int used;
+ int mincsn_updated;
+ BerVarray dn;
+ BerVarray ndn;
+} purge_data;
+
+static int
+log_old_lookup( Operation *op, SlapReply *rs )
+{
+ purge_data *pd = op->o_callback->sc_private;
+ struct log_info *li = pd->li;
+ Attribute *a;
+
+ if ( rs->sr_type != REP_SEARCH) return 0;
+
+ if ( slapd_shutdown ) return 0;
+
+ /* Update minCSN */
+ a = attr_find( rs->sr_entry->e_attrs,
+ slap_schema.si_ad_entryCSN );
+ if ( a ) {
+ ber_len_t len = a->a_nvals[0].bv_len;
+ int i, sid;
+
+ /* Find the correct sid */
+ sid = slap_parse_csn_sid( &a->a_nvals[0] );
+ ldap_pvt_thread_mutex_lock( &li->li_log_mutex );
+ for ( i=0; i < li->li_numcsns; i++ ) {
+ if ( sid <= li->li_sids[i] ) break;
+ }
+ if ( i >= li->li_numcsns || sid != li->li_sids[i] ) {
+ Debug( LDAP_DEBUG_ANY, "log_old_lookup: "
+ "csn=%s with sid not in minCSN set!\n",
+ a->a_nvals[0].bv_val );
+ slap_insert_csn_sids( (struct sync_cookie *)&li->li_mincsn, i,
+ sid, &a->a_nvals[0] );
+ } else {
+ /* Paranoid len check, normalized CSNs are always the same length */
+ if ( len > li->li_mincsn[i].bv_len )
+ len = li->li_mincsn[i].bv_len;
+ if ( ber_bvcmp( &li->li_mincsn[i], &a->a_nvals[0] ) < 0 ) {
+ pd->mincsn_updated = 1;
+ AC_MEMCPY( li->li_mincsn[i].bv_val, a->a_nvals[0].bv_val, len );
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &li->li_log_mutex );
+ }
+ if ( pd->used >= pd->slots ) {
+ pd->slots += PURGE_INCREMENT;
+ pd->dn = ch_realloc( pd->dn, pd->slots * sizeof( struct berval ));
+ pd->ndn = ch_realloc( pd->ndn, pd->slots * sizeof( struct berval ));
+ }
+ ber_dupbv( &pd->dn[pd->used], &rs->sr_entry->e_name );
+ ber_dupbv( &pd->ndn[pd->used], &rs->sr_entry->e_nname );
+ pd->used++;
+ return 0;
+}
+
+/* Periodically search for old entries in the log database and delete them */
+static void *
+accesslog_purge( void *ctx, void *arg )
+{
+ struct re_s *rtask = arg;
+ struct log_info *li = rtask->arg;
+
+ Connection conn = {0};
+ OperationBuffer opbuf;
+ Operation *op;
+ SlapReply rs = {REP_RESULT};
+ slap_callback cb = { NULL, log_old_lookup, NULL, NULL, NULL };
+ Filter f;
+ AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
+ purge_data pd = { .li = li };
+ char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE];
+ char csnbuf[LDAP_PVT_CSNSTR_BUFSIZE];
+ time_t old = slap_get_time();
+
+ connection_fake_init( &conn, &opbuf, ctx );
+ op = &opbuf.ob_op;
+
+ f.f_choice = LDAP_FILTER_LE;
+ f.f_ava = &ava;
+ f.f_next = NULL;
+
+ ava.aa_desc = ad_reqStart;
+ ava.aa_value.bv_val = timebuf;
+ ava.aa_value.bv_len = sizeof(timebuf);
+
+ old -= li->li_age;
+ slap_timestamp( &old, &ava.aa_value );
+
+ op->o_tag = LDAP_REQ_SEARCH;
+ op->o_bd = li->li_db;
+ op->o_dn = li->li_db->be_rootdn;
+ op->o_ndn = li->li_db->be_rootndn;
+ op->o_req_dn = li->li_db->be_suffix[0];
+ op->o_req_ndn = li->li_db->be_nsuffix[0];
+ op->o_callback = &cb;
+ op->ors_scope = LDAP_SCOPE_ONELEVEL;
+ op->ors_deref = LDAP_DEREF_NEVER;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_slimit = SLAP_NO_LIMIT;
+ op->ors_filter = &f;
+ filter2bv_x( op, &f, &op->ors_filterstr );
+ op->ors_attrs = slap_anlist_no_attrs;
+ op->ors_attrsonly = 1;
+
+ cb.sc_private = &pd;
+
+ op->o_bd->be_search( op, &rs );
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+
+ if ( pd.used ) {
+ int i;
+
+ op->o_callback = &nullsc;
+ op->o_dont_replicate = 1;
+ op->o_csn = slap_empty_bv;
+
+ if ( pd.mincsn_updated ) {
+ Modifications mod;
+ /* update context's minCSN to reflect oldest CSN */
+ ldap_pvt_thread_mutex_lock( &li->li_log_mutex );
+ mod.sml_numvals = li->li_numcsns;
+ mod.sml_values = li->li_mincsn;
+ mod.sml_nvalues = li->li_mincsn;
+ mod.sml_desc = ad_minCSN;
+ mod.sml_op = LDAP_MOD_REPLACE;
+ mod.sml_flags = SLAP_MOD_INTERNAL;
+ mod.sml_next = NULL;
+
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->orm_modlist = &mod;
+ op->orm_no_opattrs = 1;
+ op->o_req_dn = li->li_db->be_suffix[0];
+ op->o_req_ndn = li->li_db->be_nsuffix[0];
+ op->o_no_schema_check = 1;
+ op->o_managedsait = SLAP_CONTROL_NONCRITICAL;
+ if ( !slapd_shutdown ) {
+ Debug( LDAP_DEBUG_SYNC, "accesslog_purge: "
+ "updating minCSN with %d values\n",
+ li->li_numcsns );
+ op->o_bd->be_modify( op, &rs );
+ }
+ ldap_pvt_thread_mutex_unlock( &li->li_log_mutex );
+ }
+
+ /* delete the expired entries */
+ op->o_tag = LDAP_REQ_DELETE;
+ for (i=0; i<pd.used; i++) {
+ op->o_req_dn = pd.dn[i];
+ op->o_req_ndn = pd.ndn[i];
+ if ( !slapd_shutdown ) {
+ rs_reinit( &rs, REP_RESULT );
+ op->o_bd->be_delete( op, &rs );
+ }
+ ch_free( pd.ndn[i].bv_val );
+ ch_free( pd.dn[i].bv_val );
+ ldap_pvt_thread_pool_pausecheck( &connection_pool );
+ }
+ ch_free( pd.ndn );
+ ch_free( pd.dn );
+ }
+
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+
+ return NULL;
+}
+
+static int
+log_cf_gen(ConfigArgs *c)
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ struct log_info *li = on->on_bi.bi_private;
+ int rc = 0;
+ slap_mask_t tmask = 0;
+ char agebuf[2*STRLENOF("ddddd+hh:mm:ss ")];
+ struct berval agebv, cyclebv;
+
+ switch( c->op ) {
+ case SLAP_CONFIG_EMIT:
+ switch( c->type ) {
+ case LOG_DB:
+ if ( !BER_BVISEMPTY( &li->li_db_suffix )) {
+ value_add_one( &c->rvalue_vals, &li->li_db_suffix );
+ value_add_one( &c->rvalue_nvals, &li->li_db_suffix );
+ } else if ( li->li_db ) {
+ value_add_one( &c->rvalue_vals, li->li_db->be_suffix );
+ value_add_one( &c->rvalue_nvals, li->li_db->be_nsuffix );
+ } else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "accesslog: \"logdb <suffix>\" must be specified" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
+ c->log, c->cr_msg, c->value_dn.bv_val );
+ rc = 1;
+ break;
+ }
+ break;
+ case LOG_OPS:
+ rc = mask_to_verbs( logops, li->li_ops, &c->rvalue_vals );
+ break;
+ case LOG_PURGE:
+ if ( !li->li_age ) {
+ rc = 1;
+ break;
+ }
+ agebv.bv_val = agebuf;
+ log_age_unparse( li->li_age, &agebv, sizeof( agebuf ) );
+ agebv.bv_val[agebv.bv_len] = ' ';
+ agebv.bv_len++;
+ cyclebv.bv_val = agebv.bv_val + agebv.bv_len;
+ log_age_unparse( li->li_cycle, &cyclebv, sizeof( agebuf ) - agebv.bv_len );
+ agebv.bv_len += cyclebv.bv_len;
+ value_add_one( &c->rvalue_vals, &agebv );
+ break;
+ case LOG_SUCCESS:
+ if ( li->li_success )
+ c->value_int = li->li_success;
+ else
+ rc = 1;
+ break;
+ case LOG_OLD:
+ if ( li->li_oldf ) {
+ filter2bv( li->li_oldf, &agebv );
+ ber_bvarray_add( &c->rvalue_vals, &agebv );
+ }
+ else
+ rc = 1;
+ break;
+ case LOG_OLDATTR:
+ if ( li->li_oldattrs ) {
+ log_attr *la;
+
+ for ( la = li->li_oldattrs; la; la=la->next )
+ value_add_one( &c->rvalue_vals, &la->attr->ad_cname );
+ }
+ else
+ rc = 1;
+ break;
+ case LOG_BASE:
+ if ( li->li_bases ) {
+ log_base *lb;
+
+ for ( lb = li->li_bases; lb; lb=lb->lb_next )
+ value_add_one( &c->rvalue_vals, &lb->lb_line );
+ }
+ else
+ rc = 1;
+ break;
+ }
+ break;
+ case LDAP_MOD_DELETE:
+ switch( c->type ) {
+ case LOG_DB:
+ /* noop. this should always be a valid backend. */
+ break;
+ case LOG_OPS:
+ if ( c->valx < 0 ) {
+ li->li_ops = 0;
+ } else {
+ rc = verbs_to_mask( 1, &c->line, logops, &tmask );
+ if ( rc == 0 )
+ li->li_ops &= ~tmask;
+ }
+ break;
+ case LOG_PURGE:
+ if ( li->li_task ) {
+ struct re_s *re = li->li_task;
+ li->li_task = NULL;
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, re ))
+ ldap_pvt_runqueue_stoptask( &slapd_rq, re );
+ ldap_pvt_runqueue_remove( &slapd_rq, re );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+ li->li_age = 0;
+ li->li_cycle = 0;
+ break;
+ case LOG_SUCCESS:
+ li->li_success = 0;
+ break;
+ case LOG_OLD:
+ if ( li->li_oldf ) {
+ filter_free( li->li_oldf );
+ li->li_oldf = NULL;
+ }
+ break;
+ case LOG_OLDATTR:
+ if ( c->valx < 0 ) {
+ log_attr *la, *ln;
+
+ for ( la = li->li_oldattrs; la; la = ln ) {
+ ln = la->next;
+ ch_free( la );
+ }
+ } else {
+ log_attr *la = NULL, **lp;
+ int i;
+
+ for ( lp = &li->li_oldattrs, i=0; i < c->valx; i++ ) {
+ la = *lp;
+ lp = &la->next;
+ }
+ *lp = la->next;
+ ch_free( la );
+ }
+ break;
+ case LOG_BASE:
+ if ( c->valx < 0 ) {
+ log_base *lb, *ln;
+
+ for ( lb = li->li_bases; lb; lb = ln ) {
+ ln = lb->lb_next;
+ ch_free( lb );
+ }
+ } else {
+ log_base *lb = NULL, **lp;
+ int i;
+
+ for ( lp = &li->li_bases, i=0; i < c->valx; i++ ) {
+ lb = *lp;
+ lp = &lb->lb_next;
+ }
+ *lp = lb->lb_next;
+ ch_free( lb );
+ }
+ break;
+ }
+ break;
+ default:
+ switch( c->type ) {
+ case LOG_DB:
+ if ( CONFIG_ONLINE_ADD( c )) {
+ li->li_db = select_backend( &c->value_ndn, 0 );
+ if ( !li->li_db ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "<%s> no matching backend found for suffix",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
+ c->log, c->cr_msg, c->value_dn.bv_val );
+ rc = 1;
+ }
+ if ( !rc && ( li->li_db->bd_self == c->be->bd_self )) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "<%s> invalid suffix, points to itself",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
+ c->log, c->cr_msg, c->value_dn.bv_val );
+ rc = 1;
+ }
+ ch_free( c->value_ndn.bv_val );
+ } else {
+ li->li_db_suffix = c->value_ndn;
+ }
+ ch_free( c->value_dn.bv_val );
+ break;
+ case LOG_OPS:
+ rc = verbs_to_mask( c->argc, c->argv, logops, &tmask );
+ if ( rc == 0 )
+ li->li_ops |= tmask;
+ break;
+ case LOG_PURGE:
+ li->li_age = log_age_parse( c->argv[1] );
+ if ( li->li_age < 1 ) {
+ rc = 1;
+ } else {
+ li->li_cycle = log_age_parse( c->argv[2] );
+ if ( li->li_cycle < 1 ) {
+ rc = 1;
+ } else if ( slapMode & SLAP_SERVER_MODE ) {
+ struct re_s *re = li->li_task;
+ if ( re )
+ re->interval.tv_sec = li->li_cycle;
+ else if ( li->li_open ) {
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ li->li_task = ldap_pvt_runqueue_insert( &slapd_rq,
+ li->li_cycle, accesslog_purge, li,
+ "accesslog_purge", li->li_db ?
+ li->li_db->be_suffix[0].bv_val :
+ c->be->be_suffix[0].bv_val );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+ }
+ }
+ break;
+ case LOG_SUCCESS:
+ li->li_success = c->value_int;
+ break;
+ case LOG_OLD:
+ li->li_oldf = str2filter( c->argv[1] );
+ if ( !li->li_oldf ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "bad filter!" );
+ rc = 1;
+ }
+ break;
+ case LOG_OLDATTR: {
+ int i;
+ AttributeDescription *ad;
+ const char *text;
+
+ for ( i=1; i< c->argc; i++ ) {
+ ad = NULL;
+ if ( slap_str2ad( c->argv[i], &ad, &text ) == LDAP_SUCCESS ) {
+ log_attr *la = ch_malloc( sizeof( log_attr ));
+ la->attr = ad;
+ la->next = li->li_oldattrs;
+ li->li_oldattrs = la;
+ } else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s <%s>: %s",
+ c->argv[0], c->argv[i], text );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg );
+ rc = ARG_BAD_CONF;
+ break;
+ }
+ }
+ }
+ break;
+ case LOG_BASE: {
+ slap_mask_t m = 0;
+ rc = verbstring_to_mask( logops, c->argv[1], '|', &m );
+ if ( rc == 0 ) {
+ struct berval dn, ndn;
+ ber_str2bv( c->argv[2], 0, 0, &dn );
+ rc = dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL );
+ if ( rc == 0 ) {
+ log_base *lb;
+ struct berval mbv;
+ char *ptr;
+ mask_to_verbstring( logops, m, '|', &mbv );
+ lb = ch_malloc( sizeof( log_base ) + mbv.bv_len + ndn.bv_len + 3 + 1 );
+ lb->lb_line.bv_val = (char *)(lb + 1);
+ lb->lb_line.bv_len = mbv.bv_len + ndn.bv_len + 3;
+ ptr = lutil_strcopy( lb->lb_line.bv_val, mbv.bv_val );
+ *ptr++ = ' ';
+ *ptr++ = '"';
+ lb->lb_base.bv_val = ptr;
+ lb->lb_base.bv_len = ndn.bv_len;
+ ptr = lutil_strcopy( ptr, ndn.bv_val );
+ *ptr++ = '"';
+ lb->lb_ops = m;
+ lb->lb_next = li->li_bases;
+ li->li_bases = lb;
+ } else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s invalid DN: %s",
+ c->argv[0], c->argv[2] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg );
+ rc = ARG_BAD_CONF;
+ }
+ } else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s invalid ops: %s",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg );
+ rc = ARG_BAD_CONF;
+ }
+ }
+ break;
+ }
+ break;
+ }
+ return rc;
+}
+
+static int
+logSchemaControlValidate(
+ Syntax *syntax,
+ struct berval *valp )
+{
+ struct berval val, bv;
+ ber_len_t i;
+ int rc = LDAP_SUCCESS;
+
+ assert( valp != NULL );
+
+ val = *valp;
+
+ /* check minimal size */
+ if ( val.bv_len < STRLENOF( "{*}" ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ val.bv_len--;
+
+ /* check SEQUENCE boundaries */
+ if ( val.bv_val[ 0 ] != '{' /*}*/ ||
+ val.bv_val[ val.bv_len ] != /*{*/ '}' )
+ {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* extract and check OID */
+ for ( i = 1; i < val.bv_len; i++ ) {
+ if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
+ break;
+ }
+ }
+
+ bv.bv_val = &val.bv_val[ i ];
+
+ for ( i++; i < val.bv_len; i++ ) {
+ if ( ASCII_SPACE( val.bv_val[ i ] ) )
+ {
+ break;
+ }
+ }
+
+ bv.bv_len = &val.bv_val[ i ] - bv.bv_val;
+
+ rc = numericoidValidate( NULL, &bv );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( i == val.bv_len ) {
+ return LDAP_SUCCESS;
+ }
+
+ if ( val.bv_val[ i ] != ' ' ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ for ( i++; i < val.bv_len; i++ ) {
+ if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
+ break;
+ }
+ }
+
+ if ( i == val.bv_len ) {
+ return LDAP_SUCCESS;
+ }
+
+ /* extract and check criticality */
+ if ( strncasecmp( &val.bv_val[ i ], "criticality ", STRLENOF( "criticality " ) ) == 0 )
+ {
+ i += STRLENOF( "criticality " );
+ for ( ; i < val.bv_len; i++ ) {
+ if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
+ break;
+ }
+ }
+
+ if ( i == val.bv_len ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ bv.bv_val = &val.bv_val[ i ];
+
+ for ( ; i < val.bv_len; i++ ) {
+ if ( ASCII_SPACE( val.bv_val[ i ] ) ) {
+ break;
+ }
+ }
+
+ bv.bv_len = &val.bv_val[ i ] - bv.bv_val;
+
+ if ( !bvmatch( &bv, &slap_true_bv ) && !bvmatch( &bv, &slap_false_bv ) )
+ {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( i == val.bv_len ) {
+ return LDAP_SUCCESS;
+ }
+
+ if ( val.bv_val[ i ] != ' ' ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ for ( i++; i < val.bv_len; i++ ) {
+ if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
+ break;
+ }
+ }
+
+ if ( i == val.bv_len ) {
+ return LDAP_SUCCESS;
+ }
+ }
+
+ /* extract and check controlValue */
+ if ( strncasecmp( &val.bv_val[ i ], "controlValue ", STRLENOF( "controlValue " ) ) == 0 )
+ {
+ ber_len_t valueStart, valueLen;
+
+ i += STRLENOF( "controlValue " );
+ for ( ; i < val.bv_len; i++ ) {
+ if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
+ break;
+ }
+ }
+
+ if ( i == val.bv_len ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( val.bv_val[ i ] != '"' ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ i++;
+ valueStart = i;
+
+ for ( ; i < val.bv_len; i++ ) {
+ if ( val.bv_val[ i ] == '"' ) {
+ break;
+ }
+
+ if ( !ASCII_HEX( val.bv_val[ i ] ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ if ( val.bv_val[ i ] != '"' ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ valueLen = i - valueStart;
+ if ( (valueLen/2)*2 != valueLen ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ for ( i++; i < val.bv_len; i++ ) {
+ if ( !ASCII_SPACE( val.bv_val[ i ] ) ) {
+ break;
+ }
+ }
+
+ if ( i == val.bv_len ) {
+ return LDAP_SUCCESS;
+ }
+ }
+
+ return LDAP_INVALID_SYNTAX;
+}
+
+static int
+accesslog_ctrls(
+ LDAPControl **ctrls,
+ BerVarray *valsp,
+ BerVarray *nvalsp,
+ void *memctx )
+{
+ long i, rc = 0;
+
+ assert( valsp != NULL );
+ assert( ctrls != NULL );
+
+ *valsp = NULL;
+ *nvalsp = NULL;
+
+ for ( i = 0; ctrls[ i ] != NULL; i++ ) {
+ struct berval idx,
+ oid,
+ noid,
+ bv;
+ char *ptr,
+ buf[ 32 ];
+
+ if ( ctrls[ i ]->ldctl_oid == NULL ) {
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ idx.bv_len = snprintf( buf, sizeof( buf ), "{%ld}", i );
+ idx.bv_val = buf;
+
+ ber_str2bv( ctrls[ i ]->ldctl_oid, 0, 0, &oid );
+ noid.bv_len = idx.bv_len + oid.bv_len;
+ ptr = noid.bv_val = ber_memalloc_x( noid.bv_len + 1, memctx );
+ ptr = lutil_strcopy( ptr, idx.bv_val );
+ ptr = lutil_strcopy( ptr, oid.bv_val );
+
+ bv.bv_len = idx.bv_len + STRLENOF( "{}" ) + oid.bv_len;
+
+ if ( ctrls[ i ]->ldctl_iscritical ) {
+ bv.bv_len += STRLENOF( " criticality TRUE" );
+ }
+
+ if ( !BER_BVISNULL( &ctrls[ i ]->ldctl_value ) ) {
+ bv.bv_len += STRLENOF( " controlValue \"\"" )
+ + 2 * ctrls[ i ]->ldctl_value.bv_len;
+ }
+
+ ptr = bv.bv_val = ber_memalloc_x( bv.bv_len + 1, memctx );
+ if ( ptr == NULL ) {
+ ber_bvarray_free( *valsp );
+ *valsp = NULL;
+ ber_bvarray_free( *nvalsp );
+ *nvalsp = NULL;
+ return LDAP_OTHER;
+ }
+
+ ptr = lutil_strcopy( ptr, idx.bv_val );
+
+ *ptr++ = '{' /*}*/ ;
+ ptr = lutil_strcopy( ptr, oid.bv_val );
+
+ if ( ctrls[ i ]->ldctl_iscritical ) {
+ ptr = lutil_strcopy( ptr, " criticality TRUE" );
+ }
+
+ if ( !BER_BVISNULL( &ctrls[ i ]->ldctl_value ) ) {
+ ber_len_t j;
+
+ ptr = lutil_strcopy( ptr, " controlValue \"" );
+ for ( j = 0; j < ctrls[ i ]->ldctl_value.bv_len; j++ ) {
+ *ptr++ = SLAP_ESCAPE_HI(ctrls[ i ]->ldctl_value.bv_val[ j ]);
+ *ptr++ = SLAP_ESCAPE_LO(ctrls[ i ]->ldctl_value.bv_val[ j ]);
+ }
+
+ *ptr++ = '"';
+ }
+
+ *ptr++ = '}';
+ *ptr = '\0';
+
+ ber_bvarray_add_x( valsp, &bv, memctx );
+ ber_bvarray_add_x( nvalsp, &noid, memctx );
+ }
+
+ return rc;
+
+}
+
+static Entry *accesslog_entry( Operation *op, SlapReply *rs,
+ log_info *li, int logop, Operation *op2 ) {
+
+ char rdnbuf[STRLENOF(RDNEQ)+LDAP_LUTIL_GENTIME_BUFSIZE+8];
+ char nrdnbuf[STRLENOF(RDNEQ)+LDAP_LUTIL_GENTIME_BUFSIZE+8];
+
+ struct berval rdn, nrdn, timestamp, ntimestamp, bv;
+ slap_verbmasks *lo = logops+logop+EN_OFFSET;
+
+ Entry *e = entry_alloc();
+
+ strcpy( rdnbuf, RDNEQ );
+ rdn.bv_val = rdnbuf;
+ strcpy( nrdnbuf, RDNEQ );
+ nrdn.bv_val = nrdnbuf;
+
+ timestamp.bv_val = rdnbuf+STRLENOF(RDNEQ);
+ timestamp.bv_len = sizeof(rdnbuf) - STRLENOF(RDNEQ);
+ slap_timestamp( &op->o_time, &timestamp );
+ snprintf( timestamp.bv_val + timestamp.bv_len-1, sizeof(".123456Z"), ".%06dZ", op->o_tincr );
+ timestamp.bv_len += STRLENOF(".123456");
+
+ rdn.bv_len = STRLENOF(RDNEQ)+timestamp.bv_len;
+ ad_reqStart->ad_type->sat_equality->smr_normalize(
+ SLAP_MR_VALUE_OF_ASSERTION_SYNTAX, ad_reqStart->ad_type->sat_syntax,
+ ad_reqStart->ad_type->sat_equality, &timestamp, &ntimestamp,
+ op->o_tmpmemctx );
+
+ strcpy( nrdn.bv_val + STRLENOF(RDNEQ), ntimestamp.bv_val );
+ nrdn.bv_len = STRLENOF(RDNEQ)+ntimestamp.bv_len;
+ build_new_dn( &e->e_name, li->li_db->be_suffix, &rdn, NULL );
+ build_new_dn( &e->e_nname, li->li_db->be_nsuffix, &nrdn, NULL );
+
+ attr_merge_one( e, slap_schema.si_ad_objectClass,
+ &log_ocs[logop]->soc_cname, NULL );
+ attr_merge_one( e, slap_schema.si_ad_structuralObjectClass,
+ &log_ocs[logop]->soc_cname, NULL );
+ attr_merge_one( e, ad_reqStart, &timestamp, &ntimestamp );
+ op->o_tmpfree( ntimestamp.bv_val, op->o_tmpmemctx );
+
+ slap_op_time( &op2->o_time, &op2->o_tincr );
+
+ timestamp.bv_len = sizeof(rdnbuf) - STRLENOF(RDNEQ);
+ slap_timestamp( &op2->o_time, &timestamp );
+ snprintf( timestamp.bv_val + timestamp.bv_len-1, sizeof(".123456Z"), ".%06dZ", op2->o_tincr );
+ timestamp.bv_len += STRLENOF(".123456");
+
+ attr_merge_normalize_one( e, ad_reqEnd, &timestamp, op->o_tmpmemctx );
+
+ /* Exops have OID appended */
+ if ( logop == LOG_EN_EXTENDED ) {
+ bv.bv_len = lo->word.bv_len + op->ore_reqoid.bv_len + 2;
+ bv.bv_val = ch_malloc( bv.bv_len + 1 );
+ AC_MEMCPY( bv.bv_val, lo->word.bv_val, lo->word.bv_len );
+ bv.bv_val[lo->word.bv_len] = '{';
+ AC_MEMCPY( bv.bv_val+lo->word.bv_len+1, op->ore_reqoid.bv_val,
+ op->ore_reqoid.bv_len );
+ bv.bv_val[bv.bv_len-1] = '}';
+ bv.bv_val[bv.bv_len] = '\0';
+ attr_merge_one( e, ad_reqType, &bv, NULL );
+ } else {
+ attr_merge_one( e, ad_reqType, &lo->word, NULL );
+ }
+
+ rdn.bv_len = snprintf( rdn.bv_val, sizeof( rdnbuf ), "%lu", op->o_connid );
+ if ( rdn.bv_len < sizeof( rdnbuf ) ) {
+ attr_merge_one( e, ad_reqSession, &rdn, NULL );
+ } /* else? */
+
+ if ( BER_BVISNULL( &op->o_dn ) ) {
+ attr_merge_one( e, ad_reqAuthzID, (struct berval *)&slap_empty_bv,
+ (struct berval *)&slap_empty_bv );
+ } else {
+ attr_merge_one( e, ad_reqAuthzID, &op->o_dn, &op->o_ndn );
+ }
+
+ /* FIXME: need to add reqControls and reqRespControls */
+ if ( op->o_ctrls ) {
+ BerVarray vals = NULL,
+ nvals = NULL;
+
+ if ( accesslog_ctrls( op->o_ctrls, &vals, &nvals,
+ op->o_tmpmemctx ) == LDAP_SUCCESS && vals )
+ {
+ attr_merge( e, ad_reqControls, vals, nvals );
+ ber_bvarray_free_x( vals, op->o_tmpmemctx );
+ ber_bvarray_free_x( nvals, op->o_tmpmemctx );
+ }
+ }
+
+ if ( rs->sr_ctrls ) {
+ BerVarray vals = NULL,
+ nvals = NULL;
+
+ if ( accesslog_ctrls( rs->sr_ctrls, &vals, &nvals,
+ op->o_tmpmemctx ) == LDAP_SUCCESS && vals )
+ {
+ attr_merge( e, ad_reqRespControls, vals, nvals );
+ ber_bvarray_free_x( vals, op->o_tmpmemctx );
+ ber_bvarray_free_x( nvals, op->o_tmpmemctx );
+ }
+
+ }
+
+ return e;
+}
+
+static struct berval scopes[] = {
+ BER_BVC("base"),
+ BER_BVC("one"),
+ BER_BVC("sub"),
+ BER_BVC("subord")
+};
+
+static struct berval derefs[] = {
+ BER_BVC("never"),
+ BER_BVC("searching"),
+ BER_BVC("finding"),
+ BER_BVC("always")
+};
+
+static struct berval simple = BER_BVC("SIMPLE");
+
+static void accesslog_val2val(AttributeDescription *ad, struct berval *val,
+ char c_op, struct berval *dst) {
+ char *ptr;
+
+ dst->bv_len = ad->ad_cname.bv_len + val->bv_len + 2;
+ if ( c_op ) dst->bv_len++;
+
+ dst->bv_val = ch_malloc( dst->bv_len+1 );
+
+ ptr = lutil_strcopy( dst->bv_val, ad->ad_cname.bv_val );
+ *ptr++ = ':';
+ if ( c_op )
+ *ptr++ = c_op;
+ *ptr++ = ' ';
+ AC_MEMCPY( ptr, val->bv_val, val->bv_len );
+ dst->bv_val[dst->bv_len] = '\0';
+}
+
+static int
+accesslog_op2logop( Operation *op )
+{
+ switch ( op->o_tag ) {
+ case LDAP_REQ_ADD: return LOG_EN_ADD;
+ case LDAP_REQ_DELETE: return LOG_EN_DELETE;
+ case LDAP_REQ_MODIFY: return LOG_EN_MODIFY;
+ case LDAP_REQ_MODRDN: return LOG_EN_MODRDN;
+ case LDAP_REQ_COMPARE: return LOG_EN_COMPARE;
+ case LDAP_REQ_SEARCH: return LOG_EN_SEARCH;
+ case LDAP_REQ_BIND: return LOG_EN_BIND;
+ case LDAP_REQ_EXTENDED: return LOG_EN_EXTENDED;
+ default: /* unknown operation type */
+ break;
+ } /* Unbind and Abandon never reach here */
+ return LOG_EN_UNKNOWN;
+}
+
+static int accesslog_response(Operation *op, SlapReply *rs) {
+ slap_overinst *on = (slap_overinst *)op->o_callback->sc_private;
+ log_info *li = on->on_bi.bi_private;
+ Attribute *a, *last_attr;
+ Modifications *m;
+ struct berval *b, uuid = BER_BVNULL;
+ int i;
+ int logop;
+ slap_verbmasks *lo;
+ Entry *e = NULL, *old = NULL, *e_uuid = NULL;
+ char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE+8];
+ struct berval bv, bv2 = BER_BVNULL;
+ char *ptr;
+ BerVarray vals;
+ Operation op2 = {0};
+ SlapReply rs2 = {REP_RESULT};
+
+ /* ITS#9051 Make sure we only remove the callback on a final response */
+ if ( rs->sr_type == REP_RESULT || rs->sr_type == REP_EXTENDED ||
+ rs->sr_type == REP_SASL ) {
+ slap_callback *sc = op->o_callback;
+ op->o_callback = sc->sc_next;
+ op->o_tmpfree(sc, op->o_tmpmemctx );
+ }
+
+ if ( rs->sr_type != REP_RESULT && rs->sr_type != REP_EXTENDED )
+ return SLAP_CB_CONTINUE;
+
+ /* can't do anything if logDB isn't open */
+ if ( !SLAP_DBOPEN( li->li_db ))
+ return SLAP_CB_CONTINUE;
+
+ logop = accesslog_op2logop( op );
+ lo = logops+logop+EN_OFFSET;
+ if ( !( li->li_ops & lo->mask )) {
+ log_base *lb;
+
+ i = 0;
+ for ( lb = li->li_bases; lb; lb=lb->lb_next )
+ if (( lb->lb_ops & lo->mask ) && dnIsSuffix( &op->o_req_ndn, &lb->lb_base )) {
+ i = 1;
+ break;
+ }
+ if ( !i )
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* mutex and so were only set for write operations;
+ * if we got here, the operation must be logged */
+ if ( lo->mask & LOG_OP_WRITES ) {
+ slap_callback *cb;
+
+ /* These internal ops are not logged */
+ if ( op->o_dont_replicate )
+ return SLAP_CB_CONTINUE;
+
+ ldap_pvt_thread_mutex_lock( &li->li_log_mutex );
+ old = li->li_old;
+ uuid = li->li_uuid;
+ li->li_old = NULL;
+ BER_BVZERO( &li->li_uuid );
+#ifdef RMUTEX_DEBUG
+ Debug( LDAP_DEBUG_SYNC,
+ "accesslog_response: unlocking rmutex for tid %x\n",
+ op->o_tid );
+#endif
+ ldap_pvt_thread_mutex_unlock( &li->li_op_rmutex );
+ }
+
+ /* ignore these internal reads */
+ if (( lo->mask & LOG_OP_READS ) && op->o_do_not_cache ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ /*
+ * ITS#9051 Technically LDAP_REFERRAL and LDAP_SASL_BIND_IN_PROGRESS
+ * are not errors, but they aren't really success either
+ */
+ if ( li->li_success && rs->sr_err != LDAP_SUCCESS &&
+ rs->sr_err != LDAP_COMPARE_TRUE &&
+ rs->sr_err != LDAP_COMPARE_FALSE )
+ goto done;
+
+ e = accesslog_entry( op, rs, li, logop, &op2 );
+
+ if ( !BER_BVISNULL( &op->o_req_ndn ))
+ attr_merge_one( e, ad_reqDN, &op->o_req_dn, &op->o_req_ndn );
+
+ if ( rs->sr_text ) {
+ ber_str2bv( rs->sr_text, 0, 0, &bv );
+ attr_merge_normalize_one( e, ad_reqMessage, &bv, op->o_tmpmemctx );
+ }
+ bv.bv_len = snprintf( timebuf, sizeof( timebuf ), "%d", rs->sr_err );
+ if ( bv.bv_len < sizeof( timebuf ) ) {
+ bv.bv_val = timebuf;
+ attr_merge_one( e, ad_reqResult, &bv, NULL );
+ }
+
+ last_attr = attr_find( e->e_attrs, ad_reqResult );
+
+ e_uuid = old;
+ switch( logop ) {
+ case LOG_EN_ADD:
+ case LOG_EN_DELETE: {
+ char c_op;
+ Entry *e2;
+
+ if ( logop == LOG_EN_ADD ) {
+ e2 = op->ora_e;
+ e_uuid = op->ora_e;
+ c_op = '+';
+
+ } else {
+ if ( !old )
+ break;
+ e2 = old;
+ c_op = 0;
+ }
+ /* count all the vals */
+ i = 0;
+ for ( a=e2->e_attrs; a; a=a->a_next ) {
+ i += a->a_numvals;
+ }
+ vals = ch_malloc( (i+1) * sizeof( struct berval ));
+ i = 0;
+ for ( a=e2->e_attrs; a; a=a->a_next ) {
+ if ( a->a_vals ) {
+ for (b=a->a_vals; !BER_BVISNULL( b ); b++,i++) {
+ accesslog_val2val( a->a_desc, b, c_op, &vals[i] );
+ }
+ }
+ }
+ vals[i].bv_val = NULL;
+ vals[i].bv_len = 0;
+ a = attr_alloc( logop == LOG_EN_ADD ? ad_reqMod : ad_reqOld );
+ a->a_numvals = i;
+ a->a_vals = vals;
+ a->a_nvals = vals;
+ last_attr->a_next = a;
+ break;
+ }
+
+ case LOG_EN_MODRDN:
+ case LOG_EN_MODIFY:
+ /* count all the mods + attributes (ITS#6545) */
+ i = 0;
+ for ( m = op->orm_modlist; m; m = m->sml_next ) {
+ if ( m->sml_values ) {
+ i += m->sml_numvals;
+ } else if ( m->sml_op == LDAP_MOD_DELETE ||
+ m->sml_op == SLAP_MOD_SOFTDEL ||
+ m->sml_op == LDAP_MOD_REPLACE )
+ {
+ i++;
+ }
+ if ( m->sml_next && m->sml_desc == m->sml_next->sml_desc ) {
+ i++;
+ }
+ }
+ vals = ch_malloc( (i+1) * sizeof( struct berval ));
+ i = 0;
+
+ /* init flags on old entry */
+ if ( old ) {
+ for ( a = old->e_attrs; a; a = a->a_next ) {
+ log_attr *la;
+ a->a_flags = 0;
+
+ /* look for attrs that are always logged */
+ for ( la = li->li_oldattrs; la; la = la->next ) {
+ if ( a->a_desc == la->attr ) {
+ a->a_flags = 1;
+ }
+ }
+ }
+ }
+
+ for ( m = op->orm_modlist; m; m = m->sml_next ) {
+ /* Mark this attribute as modified */
+ if ( old ) {
+ a = attr_find( old->e_attrs, m->sml_desc );
+ if ( a ) {
+ a->a_flags = 1;
+ }
+ }
+
+ /* don't log the RDN mods; they're explicitly logged later */
+ if ( logop == LOG_EN_MODRDN &&
+ ( m->sml_op == SLAP_MOD_SOFTADD ||
+ m->sml_op == LDAP_MOD_DELETE ) )
+ {
+ continue;
+ }
+
+ if ( m->sml_values ) {
+ for ( b = m->sml_values; !BER_BVISNULL( b ); b++, i++ ) {
+ char c_op;
+
+ switch ( m->sml_op ) {
+ case LDAP_MOD_ADD: /* FALLTHRU */
+ case SLAP_MOD_SOFTADD: c_op = '+'; break;
+ case LDAP_MOD_DELETE: /* FALLTHRU */
+ case SLAP_MOD_SOFTDEL: c_op = '-'; break;
+ case LDAP_MOD_REPLACE: c_op = '='; break;
+ case LDAP_MOD_INCREMENT: c_op = '#'; break;
+
+ /* unknown op. there shouldn't be any of these. we
+ * don't know what to do with it, but we shouldn't just
+ * ignore it.
+ */
+ default: c_op = '?'; break;
+ }
+ accesslog_val2val( m->sml_desc, b, c_op, &vals[i] );
+ }
+ } else if ( m->sml_op == LDAP_MOD_DELETE ||
+ m->sml_op == SLAP_MOD_SOFTDEL ||
+ m->sml_op == LDAP_MOD_REPLACE )
+ {
+ vals[i].bv_len = m->sml_desc->ad_cname.bv_len + 2;
+ vals[i].bv_val = ch_malloc( vals[i].bv_len + 1 );
+ ptr = lutil_strcopy( vals[i].bv_val,
+ m->sml_desc->ad_cname.bv_val );
+ *ptr++ = ':';
+ if ( m->sml_op == LDAP_MOD_DELETE || m->sml_op == SLAP_MOD_SOFTDEL ) {
+ *ptr++ = '-';
+ } else {
+ *ptr++ = '=';
+ }
+ *ptr = '\0';
+ i++;
+ }
+ /* ITS#6545: when the same attribute is edited multiple times,
+ * record the transition */
+ if ( m->sml_next && m->sml_desc == m->sml_next->sml_desc &&
+ m->sml_op == m->sml_next->sml_op ) {
+ ber_str2bv( ":", STRLENOF(":"), 1, &vals[i] );
+ i++;
+ }
+ }
+
+ if ( i > 0 ) {
+ BER_BVZERO( &vals[i] );
+ a = attr_alloc( ad_reqMod );
+ a->a_numvals = i;
+ a->a_vals = vals;
+ a->a_nvals = vals;
+ last_attr->a_next = a;
+ last_attr = a;
+
+ } else {
+ ch_free( vals );
+ }
+
+ if ( old ) {
+ /* count all the vals */
+ i = 0;
+ for ( a = old->e_attrs; a != NULL; a = a->a_next ) {
+ if ( a->a_vals && a->a_flags ) {
+ i += a->a_numvals;
+ }
+ }
+ if ( i ) {
+ vals = ch_malloc( (i + 1) * sizeof( struct berval ) );
+ i = 0;
+ for ( a=old->e_attrs; a; a=a->a_next ) {
+ if ( a->a_vals && a->a_flags ) {
+ for (b=a->a_vals; !BER_BVISNULL( b ); b++,i++) {
+ accesslog_val2val( a->a_desc, b, 0, &vals[i] );
+ }
+ }
+ }
+ vals[i].bv_val = NULL;
+ vals[i].bv_len = 0;
+ a = attr_alloc( ad_reqOld );
+ a->a_numvals = i;
+ a->a_vals = vals;
+ a->a_nvals = vals;
+ last_attr->a_next = a;
+ }
+ }
+ if ( logop == LOG_EN_MODIFY ) {
+ break;
+ }
+
+ /* Now log the actual modRDN info */
+ attr_merge_one( e, ad_reqNewRDN, &op->orr_newrdn, &op->orr_nnewrdn );
+ attr_merge_one( e, ad_reqDeleteOldRDN, op->orr_deleteoldrdn ?
+ (struct berval *)&slap_true_bv : (struct berval *)&slap_false_bv,
+ NULL );
+ if ( op->orr_newSup ) {
+ attr_merge_one( e, ad_reqNewSuperior, op->orr_newSup, op->orr_nnewSup );
+ bv2 = *op->orr_nnewSup;
+ } else {
+ dnParent( &op->o_req_ndn, &bv2 );
+ }
+ build_new_dn( &bv, &bv2, &op->orr_nnewrdn, op->o_tmpmemctx );
+ attr_merge_one( e, ad_reqNewDN, &bv, NULL );
+ op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
+ break;
+
+ case LOG_EN_COMPARE:
+ bv.bv_len = op->orc_ava->aa_desc->ad_cname.bv_len + 1 +
+ op->orc_ava->aa_value.bv_len;
+ bv.bv_val = op->o_tmpalloc( bv.bv_len+1, op->o_tmpmemctx );
+ ptr = lutil_strcopy( bv.bv_val, op->orc_ava->aa_desc->ad_cname.bv_val );
+ *ptr++ = '=';
+ AC_MEMCPY( ptr, op->orc_ava->aa_value.bv_val, op->orc_ava->aa_value.bv_len );
+ bv.bv_val[bv.bv_len] = '\0';
+ attr_merge_one( e, ad_reqAssertion, &bv, NULL );
+ op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
+ break;
+
+ case LOG_EN_SEARCH:
+ attr_merge_one( e, ad_reqScope, &scopes[op->ors_scope], NULL );
+ attr_merge_one( e, ad_reqDerefAliases, &derefs[op->ors_deref], NULL );
+ attr_merge_one( e, ad_reqAttrsOnly, op->ors_attrsonly ?
+ (struct berval *)&slap_true_bv : (struct berval *)&slap_false_bv,
+ NULL );
+ if ( !BER_BVISEMPTY( &op->ors_filterstr ))
+ attr_merge_normalize_one( e, ad_reqFilter, &op->ors_filterstr, op->o_tmpmemctx );
+ if ( op->ors_attrs ) {
+ int j;
+ /* count them */
+ for (i=0; !BER_BVISNULL(&op->ors_attrs[i].an_name );i++)
+ ;
+ vals = op->o_tmpalloc( (i+1) * sizeof(struct berval),
+ op->o_tmpmemctx );
+ for (i=0, j=0; !BER_BVISNULL(&op->ors_attrs[i].an_name );i++) {
+ if (!BER_BVISEMPTY(&op->ors_attrs[i].an_name)) {
+ vals[j] = op->ors_attrs[i].an_name;
+ j++;
+ }
+ }
+ BER_BVZERO(&vals[j]);
+ attr_merge_normalize( e, ad_reqAttr, vals, op->o_tmpmemctx );
+ op->o_tmpfree( vals, op->o_tmpmemctx );
+ }
+ bv.bv_val = timebuf;
+ bv.bv_len = snprintf( bv.bv_val, sizeof( timebuf ), "%d", rs->sr_nentries );
+ if ( bv.bv_len < sizeof( timebuf ) ) {
+ attr_merge_one( e, ad_reqEntries, &bv, NULL );
+ } /* else? */
+
+ bv.bv_len = snprintf( bv.bv_val, sizeof( timebuf ), "%d", op->ors_tlimit );
+ if ( bv.bv_len < sizeof( timebuf ) ) {
+ attr_merge_one( e, ad_reqTimeLimit, &bv, NULL );
+ } /* else? */
+
+ bv.bv_len = snprintf( bv.bv_val, sizeof( timebuf ), "%d", op->ors_slimit );
+ if ( bv.bv_len < sizeof( timebuf ) ) {
+ attr_merge_one( e, ad_reqSizeLimit, &bv, NULL );
+ } /* else? */
+ break;
+
+ case LOG_EN_BIND:
+ bv.bv_val = timebuf;
+ bv.bv_len = snprintf( bv.bv_val, sizeof( timebuf ), "%d", op->o_protocol );
+ if ( bv.bv_len < sizeof( timebuf ) ) {
+ attr_merge_one( e, ad_reqVersion, &bv, NULL );
+ } /* else? */
+ if ( op->orb_method == LDAP_AUTH_SIMPLE ) {
+ attr_merge_normalize_one( e, ad_reqMethod, &simple, op->o_tmpmemctx );
+ } else {
+ bv.bv_len = STRLENOF("SASL()") + op->orb_mech.bv_len;
+ bv.bv_val = op->o_tmpalloc( bv.bv_len + 1, op->o_tmpmemctx );
+ ptr = lutil_strcopy( bv.bv_val, "SASL(" );
+ ptr = lutil_strcopy( ptr, op->orb_mech.bv_val );
+ *ptr++ = ')';
+ *ptr = '\0';
+ attr_merge_normalize_one( e, ad_reqMethod, &bv, op->o_tmpmemctx );
+ op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
+ }
+
+ break;
+
+ case LOG_EN_EXTENDED:
+ if ( op->ore_reqdata ) {
+ attr_merge_one( e, ad_reqData, op->ore_reqdata, NULL );
+ }
+ break;
+
+ case LOG_EN_UNKNOWN:
+ /* we don't know its parameters, don't add any */
+ break;
+ }
+
+ if ( e_uuid || !BER_BVISNULL( &uuid ) ) {
+ struct berval *pbv = NULL;
+
+ if ( !BER_BVISNULL( &uuid ) ) {
+ pbv = &uuid;
+
+ } else {
+ a = attr_find( e_uuid->e_attrs, slap_schema.si_ad_entryUUID );
+ if ( a ) {
+ pbv = &a->a_vals[0];
+ }
+ }
+
+ if ( pbv ) {
+ attr_merge_normalize_one( e, ad_reqEntryUUID, pbv, op->o_tmpmemctx );
+ }
+
+ if ( !BER_BVISNULL( &uuid ) ) {
+ ber_memfree( uuid.bv_val );
+ BER_BVZERO( &uuid );
+ }
+ }
+
+ op2.o_hdr = op->o_hdr;
+ op2.o_tag = LDAP_REQ_ADD;
+ op2.o_bd = li->li_db;
+ op2.o_dn = li->li_db->be_rootdn;
+ op2.o_ndn = li->li_db->be_rootndn;
+ op2.o_req_dn = e->e_name;
+ op2.o_req_ndn = e->e_nname;
+ op2.ora_e = e;
+ op2.o_callback = &nullsc;
+ op2.o_csn = op->o_csn;
+ /* contextCSN updates may still reach here */
+ op2.o_dont_replicate = op->o_dont_replicate;
+
+ if (( lo->mask & LOG_OP_WRITES ) && !BER_BVISEMPTY( &op->o_csn )) {
+ struct berval maxcsn;
+ char cbuf[LDAP_PVT_CSNSTR_BUFSIZE];
+ int foundit;
+ cbuf[0] = '\0';
+ maxcsn.bv_val = cbuf;
+ maxcsn.bv_len = sizeof(cbuf);
+ /* If there was a commit CSN on the main DB,
+ * we must propagate it to the log DB for its
+ * own syncprov. Otherwise, don't generate one.
+ */
+ slap_get_commit_csn( op, &maxcsn, &foundit );
+ if ( !BER_BVISEMPTY( &maxcsn ) ) {
+ slap_queue_csn( &op2, &op->o_csn );
+ } else {
+ attr_merge_normalize_one( e, slap_schema.si_ad_entryCSN,
+ &op->o_csn, op->o_tmpmemctx );
+ }
+ }
+
+ op2.o_bd->be_add( &op2, &rs2 );
+ if ( rs2.sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_SYNC, "%s accesslog_response: "
+ "got result 0x%x adding log entry %s\n",
+ op->o_log_prefix, rs2.sr_err, op2.o_req_dn.bv_val );
+ }
+ if ( e == op2.ora_e ) entry_free( e );
+ e = NULL;
+
+ /* TODO: What to do about minCSN when we have an op without a CSN? */
+ if ( !BER_BVISEMPTY( &op->o_csn ) ) {
+ Modifications mod;
+ int i, sid = slap_parse_csn_sid( &op->o_csn );
+
+ for ( i=0; i < li->li_numcsns; i++ ) {
+ if ( sid <= li->li_sids[i] ) break;
+ }
+ if ( i >= li->li_numcsns || sid != li->li_sids[i] ) {
+ /* SID not in minCSN set, add */
+ struct berval bv[2];
+
+ Debug( LDAP_DEBUG_TRACE, "accesslog_response: "
+ "adding minCSN %s\n",
+ op->o_csn.bv_val );
+ slap_insert_csn_sids( (struct sync_cookie *)&li->li_mincsn, i,
+ sid, &op->o_csn );
+
+ op2.o_tag = LDAP_REQ_MODIFY;
+ op2.o_req_dn = li->li_db->be_suffix[0];
+ op2.o_req_ndn = li->li_db->be_nsuffix[0];
+
+ bv[0] = op->o_csn;
+ BER_BVZERO( &bv[1] );
+
+ mod.sml_numvals = 1;
+ mod.sml_values = bv;
+ mod.sml_nvalues = bv;
+ mod.sml_desc = ad_minCSN;
+ mod.sml_op = LDAP_MOD_ADD;
+ mod.sml_flags = SLAP_MOD_INTERNAL;
+ mod.sml_next = NULL;
+
+ op2.orm_modlist = &mod;
+ op2.orm_no_opattrs = 1;
+
+ Debug( LDAP_DEBUG_SYNC, "accesslog_response: "
+ "adding a new csn=%s into minCSN\n",
+ bv[0].bv_val );
+ rs_reinit( &rs2, REP_RESULT );
+ op2.o_bd->be_modify( &op2, &rs2 );
+ if ( rs2.sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_SYNC, "accesslog_response: "
+ "got result 0x%x adding minCSN %s\n",
+ rs2.sr_err, op->o_csn.bv_val );
+ }
+ } else if ( ber_bvcmp( &op->o_csn, &li->li_mincsn[i] ) < 0 ) {
+ Debug( LDAP_DEBUG_ANY, "accesslog_response: "
+ "csn=%s older than existing minCSN csn=%s for this sid\n",
+ op->o_csn.bv_val, li->li_mincsn[i].bv_val );
+ }
+ }
+
+done:
+ if ( lo->mask & LOG_OP_WRITES )
+ ldap_pvt_thread_mutex_unlock( &li->li_log_mutex );
+ if ( old ) entry_free( old );
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+accesslog_op_misc( Operation *op, SlapReply *rs )
+{
+ slap_callback *sc;
+
+ sc = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
+ sc->sc_response = accesslog_response;
+ sc->sc_private = op->o_bd->bd_info;
+
+ if ( op->o_callback ) {
+ sc->sc_next = op->o_callback->sc_next;
+ op->o_callback->sc_next = sc;
+ } else {
+ op->o_callback = sc;
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+accesslog_op_mod( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ log_info *li = on->on_bi.bi_private;
+ slap_verbmasks *lo;
+ int logop;
+ int doit = 0;
+
+ /* These internal ops are not logged */
+ if ( op->o_dont_replicate )
+ return SLAP_CB_CONTINUE;
+
+ /* can't do anything if logDB isn't open */
+ if ( !SLAP_DBOPEN( li->li_db ))
+ return SLAP_CB_CONTINUE;
+
+ logop = accesslog_op2logop( op );
+ lo = logops+logop+EN_OFFSET;
+
+ if ( li->li_ops & lo->mask ) {
+ doit = 1;
+ } else {
+ log_base *lb;
+ for ( lb = li->li_bases; lb; lb = lb->lb_next )
+ if (( lb->lb_ops & lo->mask ) && dnIsSuffix( &op->o_req_ndn, &lb->lb_base )) {
+ doit = 1;
+ break;
+ }
+ }
+
+ if ( doit ) {
+ slap_callback *cb = op->o_tmpcalloc( 1, sizeof( slap_callback ), op->o_tmpmemctx );
+ cb->sc_cleanup = accesslog_response;
+ cb->sc_response = accesslog_response;
+ cb->sc_private = on;
+ cb->sc_next = op->o_callback;
+ op->o_callback = cb;
+
+#ifdef RMUTEX_DEBUG
+ Debug( LDAP_DEBUG_SYNC,
+ "accesslog_op_mod: locking rmutex for tid %x\n",
+ op->o_tid );
+#endif
+ ldap_pvt_thread_mutex_lock( &li->li_op_rmutex );
+#ifdef RMUTEX_DEBUG
+ Debug( LDAP_DEBUG_STATS,
+ "accesslog_op_mod: locked rmutex for tid %x\n",
+ op->o_tid );
+#endif
+ if ( li->li_oldf && ( op->o_tag == LDAP_REQ_DELETE ||
+ op->o_tag == LDAP_REQ_MODIFY ||
+ ( op->o_tag == LDAP_REQ_MODRDN && li->li_oldattrs )))
+ {
+ int rc;
+ Entry *e;
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
+ if ( e ) {
+ if ( test_filter( op, e, li->li_oldf ) == LDAP_COMPARE_TRUE )
+ li->li_old = entry_dup( e );
+ be_entry_release_rw( op, e, 0 );
+ }
+ op->o_bd->bd_info = (BackendInfo *)on;
+
+ } else {
+ int rc;
+ Entry *e;
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
+ if ( e ) {
+ Attribute *a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
+ if ( a ) {
+ ber_dupbv( &li->li_uuid, &a->a_vals[0] );
+ }
+ be_entry_release_rw( op, e, 0 );
+ }
+ op->o_bd->bd_info = (BackendInfo *)on;
+ }
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+/* unbinds are broadcast to all backends; we only log it if this
+ * backend was used for the original bind.
+ */
+static int
+accesslog_unbind( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ if ( op->o_conn->c_authz_backend == on->on_info->oi_origdb ) {
+ log_info *li = on->on_bi.bi_private;
+ Operation op2 = {0};
+ void *cids[SLAP_MAX_CIDS];
+ SlapReply rs2 = {REP_RESULT};
+ Entry *e;
+
+ if ( !( li->li_ops & LOG_OP_UNBIND )) {
+ log_base *lb;
+ int i = 0;
+
+ for ( lb = li->li_bases; lb; lb=lb->lb_next )
+ if (( lb->lb_ops & LOG_OP_UNBIND ) && dnIsSuffix( &op->o_ndn, &lb->lb_base )) {
+ i = 1;
+ break;
+ }
+ if ( !i )
+ return SLAP_CB_CONTINUE;
+ }
+
+ e = accesslog_entry( op, rs, li, LOG_EN_UNBIND, &op2 );
+ op2.o_hdr = op->o_hdr;
+ op2.o_tag = LDAP_REQ_ADD;
+ op2.o_bd = li->li_db;
+ op2.o_dn = li->li_db->be_rootdn;
+ op2.o_ndn = li->li_db->be_rootndn;
+ op2.o_req_dn = e->e_name;
+ op2.o_req_ndn = e->e_nname;
+ op2.ora_e = e;
+ op2.o_callback = &nullsc;
+ op2.o_controls = cids;
+ memset(cids, 0, sizeof( cids ));
+
+ op2.o_bd->be_add( &op2, &rs2 );
+ if ( e == op2.ora_e )
+ entry_free( e );
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+accesslog_abandon( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ log_info *li = on->on_bi.bi_private;
+ Operation op2 = {0};
+ void *cids[SLAP_MAX_CIDS];
+ SlapReply rs2 = {REP_RESULT};
+ Entry *e;
+ char buf[64];
+ struct berval bv;
+
+ if ( !op->o_time )
+ return SLAP_CB_CONTINUE;
+
+ if ( !( li->li_ops & LOG_OP_ABANDON )) {
+ log_base *lb;
+ int i = 0;
+
+ for ( lb = li->li_bases; lb; lb=lb->lb_next )
+ if (( lb->lb_ops & LOG_OP_ABANDON ) && dnIsSuffix( &op->o_ndn, &lb->lb_base )) {
+ i = 1;
+ break;
+ }
+ if ( !i )
+ return SLAP_CB_CONTINUE;
+ }
+
+ e = accesslog_entry( op, rs, li, LOG_EN_ABANDON, &op2 );
+ bv.bv_val = buf;
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%d", op->orn_msgid );
+ if ( bv.bv_len < sizeof( buf ) ) {
+ attr_merge_one( e, ad_reqId, &bv, NULL );
+ } /* else? */
+
+ op2.o_hdr = op->o_hdr;
+ op2.o_tag = LDAP_REQ_ADD;
+ op2.o_bd = li->li_db;
+ op2.o_dn = li->li_db->be_rootdn;
+ op2.o_ndn = li->li_db->be_rootndn;
+ op2.o_req_dn = e->e_name;
+ op2.o_req_ndn = e->e_nname;
+ op2.ora_e = e;
+ op2.o_callback = &nullsc;
+ op2.o_controls = cids;
+ memset(cids, 0, sizeof( cids ));
+
+ op2.o_bd->be_add( &op2, &rs2 );
+ if ( e == op2.ora_e )
+ entry_free( e );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+accesslog_operational( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ log_info *li = on->on_bi.bi_private;
+
+ if ( op->o_sync != SLAP_CONTROL_NONE )
+ return SLAP_CB_CONTINUE;
+
+ if ( rs->sr_entry != NULL
+ && dn_match( &op->o_bd->be_nsuffix[0], &rs->sr_entry->e_nname ) )
+ {
+ Attribute **ap;
+
+ for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next )
+ /* just count */ ;
+
+ if ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
+ ad_inlist( ad_auditContext, rs->sr_attrs ) )
+ {
+ *ap = attr_alloc( ad_auditContext );
+ attr_valadd( *ap,
+ &li->li_db->be_suffix[0],
+ &li->li_db->be_nsuffix[0], 1 );
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static slap_overinst accesslog;
+
+static int
+accesslog_db_init(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ log_info *li = ch_calloc(1, sizeof(log_info));
+
+ on->on_bi.bi_private = li;
+ ldap_pvt_thread_mutex_recursive_init( &li->li_op_rmutex );
+ ldap_pvt_thread_mutex_init( &li->li_log_mutex );
+ return 0;
+}
+
+static int
+accesslog_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ log_info *li = on->on_bi.bi_private;
+ log_attr *la;
+
+ if ( li->li_oldf )
+ filter_free( li->li_oldf );
+ for ( la=li->li_oldattrs; la; la=li->li_oldattrs ) {
+ li->li_oldattrs = la->next;
+ ch_free( la );
+ }
+ if ( li->li_sids )
+ ch_free( li->li_sids );
+ if ( li->li_mincsn )
+ ber_bvarray_free( li->li_mincsn );
+ if ( li->li_db_suffix.bv_val )
+ ch_free( li->li_db_suffix.bv_val );
+ ldap_pvt_thread_mutex_destroy( &li->li_log_mutex );
+ ldap_pvt_thread_mutex_destroy( &li->li_op_rmutex );
+ free( li );
+ return LDAP_SUCCESS;
+}
+
+/* Create the logdb's root entry if it's missing, load mincsn */
+static void *
+accesslog_db_root(
+ void *ctx,
+ void *arg )
+{
+ struct re_s *rtask = arg;
+ slap_overinst *on = rtask->arg;
+ log_info *li = on->on_bi.bi_private;
+
+ Connection conn = {0};
+ OperationBuffer opbuf;
+ Operation *op;
+
+ Entry *e;
+ int rc;
+
+ ldap_pvt_thread_mutex_lock( &li->li_log_mutex );
+ connection_fake_init( &conn, &opbuf, ctx );
+ op = &opbuf.ob_op;
+ op->o_bd = li->li_db;
+ op->o_dn = li->li_db->be_rootdn;
+ op->o_ndn = li->li_db->be_rootndn;
+ rc = be_entry_get_rw( op, li->li_db->be_nsuffix, NULL, NULL, 0, &e );
+
+ if ( e ) {
+ Attribute *a = attr_find( e->e_attrs, ad_minCSN );
+ if ( !a ) {
+ /* TODO: find the lowest CSN we are safe to put in */
+ a = attr_find( e->e_attrs, slap_schema.si_ad_contextCSN );
+ if ( a ) {
+ SlapReply rs = {REP_RESULT};
+ Modifications mod;
+ BackendDB db = *li->li_db;
+
+ op->o_bd = &db;
+
+ mod.sml_numvals = a->a_numvals;
+ mod.sml_values = a->a_vals;
+ mod.sml_nvalues = a->a_nvals;
+ mod.sml_desc = ad_minCSN;
+ mod.sml_op = LDAP_MOD_REPLACE;
+ mod.sml_flags = SLAP_MOD_INTERNAL;
+ mod.sml_next = NULL;
+
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->o_req_dn = e->e_name;
+ op->o_req_ndn = e->e_nname;
+ op->o_callback = &nullsc;
+ SLAP_DBFLAGS( op->o_bd ) |= SLAP_DBFLAG_NOLASTMOD;
+
+ Debug( LDAP_DEBUG_SYNC, "accesslog_db_root: "
+ "setting up minCSN with %d values\n",
+ a->a_numvals );
+
+ op->orm_modlist = &mod;
+ op->orm_no_opattrs = 1;
+ rc = op->o_bd->be_modify( op, &rs );
+ }
+ }
+ if ( a ) {
+ ber_bvarray_dup_x( &li->li_mincsn, a->a_vals, NULL );
+ li->li_numcsns = a->a_numvals;
+ li->li_sids = slap_parse_csn_sids( li->li_mincsn, li->li_numcsns, NULL );
+ slap_sort_csn_sids( li->li_mincsn, li->li_sids, li->li_numcsns, NULL );
+ }
+ be_entry_release_rw( op, e, 0 );
+ } else {
+ SlapReply rs = {REP_RESULT};
+ struct berval rdn, nrdn, attr;
+ char *ptr;
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+ Entry *e_ctx;
+ BackendDB db;
+
+ e = entry_alloc();
+ ber_dupbv( &e->e_name, li->li_db->be_suffix );
+ ber_dupbv( &e->e_nname, li->li_db->be_nsuffix );
+
+ attr_merge_one( e, slap_schema.si_ad_objectClass,
+ &log_container->soc_cname, NULL );
+
+ dnRdn( &e->e_name, &rdn );
+ dnRdn( &e->e_nname, &nrdn );
+ ptr = ber_bvchr( &rdn, '=' );
+
+ assert( ptr != NULL );
+
+ attr.bv_val = rdn.bv_val;
+ attr.bv_len = ptr - rdn.bv_val;
+
+ slap_bv2ad( &attr, &ad, &text );
+
+ rdn.bv_val = ptr+1;
+ rdn.bv_len -= attr.bv_len + 1;
+ ptr = ber_bvchr( &nrdn, '=' );
+ nrdn.bv_len -= ptr - nrdn.bv_val + 1;
+ nrdn.bv_val = ptr+1;
+ attr_merge_one( e, ad, &rdn, &nrdn );
+
+ /* Get contextCSN from main DB */
+ op->o_bd = on->on_info->oi_origdb;
+ rc = be_entry_get_rw( op, op->o_bd->be_nsuffix, NULL,
+ slap_schema.si_ad_contextCSN, 0, &e_ctx );
+
+ if ( e_ctx ) {
+ Attribute *a;
+
+ a = attr_find( e_ctx->e_attrs, slap_schema.si_ad_contextCSN );
+ if ( a ) {
+ /* FIXME: contextCSN could have multiple values!
+ * should select the one with the server's SID */
+ attr_merge_one( e, slap_schema.si_ad_entryCSN,
+ &a->a_vals[0], &a->a_nvals[0] );
+ attr_merge( e, a->a_desc, a->a_vals, a->a_nvals );
+ attr_merge( e, ad_minCSN, a->a_vals, a->a_nvals );
+ }
+ be_entry_release_rw( op, e_ctx, 0 );
+ }
+ db = *li->li_db;
+ op->o_bd = &db;
+
+ op->o_tag = LDAP_REQ_ADD;
+ op->ora_e = e;
+ op->o_req_dn = e->e_name;
+ op->o_req_ndn = e->e_nname;
+ op->o_callback = &nullsc;
+ SLAP_DBFLAGS( op->o_bd ) |= SLAP_DBFLAG_NOLASTMOD;
+ rc = op->o_bd->be_add( op, &rs );
+ if ( e == op->ora_e )
+ entry_free( e );
+ }
+ li->li_open = 1;
+ ldap_pvt_thread_mutex_unlock( &li->li_log_mutex );
+
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
+ ldap_pvt_runqueue_remove( &slapd_rq, rtask );
+
+ if ( li->li_age && li->li_cycle ) {
+ assert( li->li_task == NULL );
+ li->li_task = ldap_pvt_runqueue_insert( &slapd_rq,
+ li->li_cycle, accesslog_purge, li,
+ "accesslog_purge", li->li_db->be_suffix[0].bv_val );
+ }
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+
+ return NULL;
+}
+
+static int
+accesslog_db_open(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ log_info *li = on->on_bi.bi_private;
+
+
+ if ( !BER_BVISEMPTY( &li->li_db_suffix )) {
+ li->li_db = select_backend( &li->li_db_suffix, 0 );
+ ch_free( li->li_db_suffix.bv_val );
+ BER_BVZERO( &li->li_db_suffix );
+ }
+ if ( li->li_db == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "accesslog: \"logdb <suffix>\" missing or invalid.\n" );
+ return 1;
+ }
+ if ( li->li_db->bd_self == be->bd_self ) {
+ Debug( LDAP_DEBUG_ANY,
+ "accesslog: \"logdb <suffix>\" is this database, cannot log to itself.\n" );
+ return 1;
+ }
+
+ if ( slapMode & SLAP_TOOL_MODE )
+ return 0;
+
+ if ( BER_BVISEMPTY( &li->li_db->be_rootndn )) {
+ ber_dupbv( &li->li_db->be_rootdn, li->li_db->be_suffix );
+ ber_dupbv( &li->li_db->be_rootndn, li->li_db->be_nsuffix );
+ }
+
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ ldap_pvt_runqueue_insert( &slapd_rq, 3600, accesslog_db_root, on,
+ "accesslog_db_root", li->li_db->be_suffix[0].bv_val );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+
+ return 0;
+}
+
+static int
+accesslog_db_close(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ log_info *li = on->on_bi.bi_private;
+ struct re_s *re = li->li_task;
+
+ li->li_open = 0;
+
+ if ( re ) {
+ li->li_task = NULL;
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, re ) )
+ ldap_pvt_runqueue_stoptask( &slapd_rq, re );
+ ldap_pvt_runqueue_remove( &slapd_rq, re );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+
+ return 0;
+}
+
+enum { start = 0 };
+
+static int
+check_rdntime_syntax (struct berval *val,
+ int *parts,
+ struct berval *fraction)
+{
+ /*
+ * GeneralizedTime YYYYmmddHH[MM[SS]][(./,)d...](Z|(+/-)HH[MM])
+ * GeneralizedTime supports leap seconds, UTCTime does not.
+ */
+ static const int ceiling[9] = { 100, 100, 12, 31, 24, 60, 60, 24, 60 };
+ static const int mdays[2][12] = {
+ /* non-leap years */
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ /* leap years */
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+ };
+ char *p, *e;
+ int part, c, c1, c2, tzoffset, leapyear = 0;
+
+ p = val->bv_val;
+ e = p + val->bv_len;
+
+ for (part = start; part < 7 && p < e; part++) {
+ c1 = *p;
+ if (!ASCII_DIGIT(c1)) {
+ break;
+ }
+ p++;
+ if (p == e) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ c = *p++;
+ if (!ASCII_DIGIT(c)) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ c += c1 * 10 - '0' * 11;
+ if ((part | 1) == 3) {
+ --c;
+ if (c < 0) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+ if (c >= ceiling[part]) {
+ if (! (c == 60 && part == 6 && start == 0))
+ return LDAP_INVALID_SYNTAX;
+ }
+ parts[part] = c;
+ }
+ if (part < 5 + start) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ for (; part < 9; part++) {
+ parts[part] = 0;
+ }
+
+ /* leapyear check for the Gregorian calendar (year>1581) */
+ if (parts[parts[1] == 0 ? 0 : 1] % 4 == 0) {
+ leapyear = 1;
+ }
+
+ if (parts[3] >= mdays[leapyear][parts[2]]) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if (start == 0) {
+ fraction->bv_val = p;
+ fraction->bv_len = 0;
+ if (p < e && (*p == '.' || *p == ',')) {
+ char *end_num;
+ while (++p < e && ASCII_DIGIT(*p)) {
+ /* EMPTY */;
+ }
+ if (p - fraction->bv_val == 1) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+#if 0 /* don't truncate trailing zeros */
+ for (end_num = p; end_num[-1] == '0'; --end_num) {
+ /* EMPTY */;
+ }
+ c = end_num - fraction->bv_val;
+#else
+ c = p - fraction->bv_val;
+#endif
+ if (c != 1) fraction->bv_len = c;
+ }
+ }
+
+ if (p == e) {
+ /* no time zone */
+ return start == 0 ? LDAP_INVALID_SYNTAX : LDAP_SUCCESS;
+ }
+
+ tzoffset = *p++;
+ switch (tzoffset) {
+ case 'Z':
+ /* UTC */
+ break;
+ default:
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ return p != e ? LDAP_INVALID_SYNTAX : LDAP_SUCCESS;
+}
+
+static int
+rdnTimestampValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ int parts[9];
+ struct berval fraction;
+ return check_rdntime_syntax(in, parts, &fraction);
+}
+
+static int
+rdnTimestampNormalize(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ int parts[9], rc;
+ unsigned int len;
+ struct berval fraction;
+
+ rc = check_rdntime_syntax(val, parts, &fraction);
+ if (rc != LDAP_SUCCESS) {
+ return rc;
+ }
+
+ len = STRLENOF("YYYYmmddHHMMSSZ") + fraction.bv_len;
+ normalized->bv_val = slap_sl_malloc( len + 1, ctx );
+ if ( BER_BVISNULL( normalized ) ) {
+ return LBER_ERROR_MEMORY;
+ }
+
+ sprintf( normalized->bv_val, "%02d%02d%02d%02d%02d%02d%02d",
+ parts[0], parts[1], parts[2] + 1, parts[3] + 1,
+ parts[4], parts[5], parts[6] );
+ if ( !BER_BVISEMPTY( &fraction ) ) {
+ memcpy( normalized->bv_val + STRLENOF("YYYYmmddHHMMSSZ")-1,
+ fraction.bv_val, fraction.bv_len );
+ normalized->bv_val[STRLENOF("YYYYmmddHHMMSSZ")-1] = '.';
+ }
+ strcpy( normalized->bv_val + len-1, "Z" );
+ normalized->bv_len = len;
+
+ return LDAP_SUCCESS;
+}
+
+
+int accesslog_initialize()
+{
+ int i, rc;
+ Syntax *rdnTimestampSyntax;
+ MatchingRule *rdnTimestampMatch;
+
+ accesslog.on_bi.bi_type = "accesslog";
+ accesslog.on_bi.bi_db_init = accesslog_db_init;
+ accesslog.on_bi.bi_db_destroy = accesslog_db_destroy;
+ accesslog.on_bi.bi_db_open = accesslog_db_open;
+ accesslog.on_bi.bi_db_close = accesslog_db_close;
+
+ accesslog.on_bi.bi_op_add = accesslog_op_mod;
+ accesslog.on_bi.bi_op_bind = accesslog_op_misc;
+ accesslog.on_bi.bi_op_compare = accesslog_op_misc;
+ accesslog.on_bi.bi_op_delete = accesslog_op_mod;
+ accesslog.on_bi.bi_op_modify = accesslog_op_mod;
+ accesslog.on_bi.bi_op_modrdn = accesslog_op_mod;
+ accesslog.on_bi.bi_op_search = accesslog_op_misc;
+ accesslog.on_bi.bi_extended = accesslog_op_misc;
+ accesslog.on_bi.bi_op_unbind = accesslog_unbind;
+ accesslog.on_bi.bi_op_abandon = accesslog_abandon;
+ accesslog.on_bi.bi_operational = accesslog_operational;
+
+ accesslog.on_bi.bi_cf_ocs = log_cfocs;
+
+ nullsc.sc_response = slap_null_cb;
+
+ rc = config_register_schema( log_cfats, log_cfocs );
+ if ( rc ) return rc;
+
+ /* log schema integration */
+ for ( i=0; lsyntaxes[i].oid; i++ ) {
+ int code;
+
+ code = register_syntax( &lsyntaxes[ i ].syn );
+ if ( code != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "accesslog_init: register_syntax failed\n" );
+ return code;
+ }
+
+ if ( lsyntaxes[i].mrs != NULL ) {
+ code = mr_make_syntax_compat_with_mrs(
+ lsyntaxes[i].oid, lsyntaxes[i].mrs );
+ if ( code < 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "accesslog_init: "
+ "mr_make_syntax_compat_with_mrs "
+ "failed\n" );
+ return code;
+ }
+ }
+ }
+
+ for ( i=0; lattrs[i].at; i++ ) {
+ int code;
+
+ code = register_at( lattrs[i].at, lattrs[i].ad, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "accesslog_init: register_at failed\n" );
+ return -1;
+ }
+ }
+
+ /* Inject custom normalizer for reqStart/reqEnd */
+ rdnTimestampMatch = ch_malloc( sizeof( MatchingRule ));
+ rdnTimestampSyntax = ch_malloc( sizeof( Syntax ));
+ *rdnTimestampMatch = *ad_reqStart->ad_type->sat_equality;
+ rdnTimestampMatch->smr_normalize = rdnTimestampNormalize;
+ *rdnTimestampSyntax = *ad_reqStart->ad_type->sat_syntax;
+ rdnTimestampSyntax->ssyn_validate = rdnTimestampValidate;
+ ad_reqStart->ad_type->sat_equality = rdnTimestampMatch;
+ ad_reqStart->ad_type->sat_syntax = rdnTimestampSyntax;
+
+ rdnTimestampMatch = ch_malloc( sizeof( MatchingRule ));
+ rdnTimestampSyntax = ch_malloc( sizeof( Syntax ));
+ *rdnTimestampMatch = *ad_reqStart->ad_type->sat_equality;
+ rdnTimestampMatch->smr_normalize = rdnTimestampNormalize;
+ *rdnTimestampSyntax = *ad_reqStart->ad_type->sat_syntax;
+ rdnTimestampSyntax->ssyn_validate = rdnTimestampValidate;
+ ad_reqEnd->ad_type->sat_equality = rdnTimestampMatch;
+ ad_reqEnd->ad_type->sat_syntax = rdnTimestampSyntax;
+
+ for ( i=0; locs[i].ot; i++ ) {
+ int code;
+
+ code = register_oc( locs[i].ot, locs[i].oc, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "accesslog_init: register_oc failed\n" );
+ return -1;
+ }
+ }
+
+ return overlay_register(&accesslog);
+}
+
+#if SLAPD_OVER_ACCESSLOG == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return accesslog_initialize();
+}
+#endif
+
+#endif /* SLAPD_OVER_ACCESSLOG */
diff --git a/servers/slapd/overlays/auditlog.c b/servers/slapd/overlays/auditlog.c
new file mode 100644
index 0000000..9292d4a
--- /dev/null
+++ b/servers/slapd/overlays/auditlog.c
@@ -0,0 +1,242 @@
+/* auditlog.c - log modifications for audit/history purposes */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2022 The OpenLDAP Foundation.
+ * Portions copyright 2004-2005 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Symas Corp. for inclusion in
+ * OpenLDAP Software. This work was sponsored by Hewlett-Packard.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_AUDITLOG
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/ctype.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "ldif.h"
+
+typedef struct auditlog_data {
+ ldap_pvt_thread_mutex_t ad_mutex;
+ char *ad_logfile;
+} auditlog_data;
+
+static ConfigTable auditlogcfg[] = {
+ { "auditlog", "filename", 2, 2, 0,
+ ARG_STRING|ARG_OFFSET,
+ (void *)offsetof(auditlog_data, ad_logfile),
+ "( OLcfgOvAt:15.1 NAME 'olcAuditlogFile' "
+ "DESC 'Filename for auditlogging' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs auditlogocs[] = {
+ { "( OLcfgOvOc:15.1 "
+ "NAME 'olcAuditlogConfig' "
+ "DESC 'Auditlog configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcAuditlogFile ) )",
+ Cft_Overlay, auditlogcfg },
+ { NULL, 0, NULL }
+};
+
+static int fprint_ldif(FILE *f, char *name, char *val, ber_len_t len) {
+ char *s;
+ if((s = ldif_put(LDIF_PUT_VALUE, name, val, len)) == NULL)
+ return(-1);
+ fputs(s, f);
+ ber_memfree(s);
+ return(0);
+}
+
+static int auditlog_response(Operation *op, SlapReply *rs) {
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ auditlog_data *ad = on->on_bi.bi_private;
+ FILE *f;
+ Attribute *a;
+ Modifications *m;
+ struct berval *b, *who = NULL, peername;
+ char *what, *whatm, *suffix;
+ time_t stamp;
+ int i;
+
+ if ( rs->sr_err != LDAP_SUCCESS ) return SLAP_CB_CONTINUE;
+
+ if ( !ad->ad_logfile ) return SLAP_CB_CONTINUE;
+
+/*
+** add or modify: use modifiersName if present
+**
+*/
+ switch(op->o_tag) {
+ case LDAP_REQ_MODRDN: what = "modrdn"; break;
+ case LDAP_REQ_DELETE: what = "delete"; break;
+ case LDAP_REQ_ADD:
+ what = "add";
+ for(a = op->ora_e->e_attrs; a; a = a->a_next)
+ if( a->a_desc == slap_schema.si_ad_modifiersName ) {
+ who = &a->a_vals[0];
+ break;
+ }
+ break;
+ case LDAP_REQ_MODIFY:
+ what = "modify";
+ for(m = op->orm_modlist; m; m = m->sml_next)
+ if( m->sml_desc == slap_schema.si_ad_modifiersName &&
+ ( m->sml_op == LDAP_MOD_ADD ||
+ m->sml_op == LDAP_MOD_REPLACE )) {
+ who = &m->sml_values[0];
+ break;
+ }
+ break;
+ default:
+ return SLAP_CB_CONTINUE;
+ }
+
+ suffix = op->o_bd->be_suffix[0].bv_len ? op->o_bd->be_suffix[0].bv_val :
+ "global";
+
+/*
+** note: this means requestor's dn when modifiersName is null
+*/
+ if ( !who )
+ who = &op->o_dn;
+
+ peername = op->o_conn->c_peer_name;
+ ldap_pvt_thread_mutex_lock(&ad->ad_mutex);
+ if((f = fopen(ad->ad_logfile, "a")) == NULL) {
+ ldap_pvt_thread_mutex_unlock(&ad->ad_mutex);
+ return SLAP_CB_CONTINUE;
+ }
+
+ stamp = slap_get_time();
+ fprintf(f, "# %s %ld %s%s%s %s conn=%ld\n",
+ what, (long)stamp, suffix, who ? " " : "", who ? who->bv_val : "",
+ peername.bv_val ? peername.bv_val: "", op->o_conn->c_connid);
+
+ if ( !BER_BVISEMPTY( &op->o_conn->c_dn ) &&
+ (!who || !dn_match( who, &op->o_conn->c_dn )))
+ fprintf(f, "# realdn: %s\n", op->o_conn->c_dn.bv_val );
+
+ fprintf(f, "dn: %s\nchangetype: %s\n",
+ op->o_req_dn.bv_val, what);
+
+ switch(op->o_tag) {
+ case LDAP_REQ_ADD:
+ for(a = op->ora_e->e_attrs; a; a = a->a_next)
+ if((b = a->a_vals) != NULL)
+ for(i = 0; b[i].bv_val; i++)
+ fprint_ldif(f, a->a_desc->ad_cname.bv_val, b[i].bv_val, b[i].bv_len);
+ break;
+
+ case LDAP_REQ_MODIFY:
+ for(m = op->orm_modlist; m; m = m->sml_next) {
+ switch(m->sml_op & LDAP_MOD_OP) {
+ case LDAP_MOD_ADD: whatm = "add"; break;
+ case LDAP_MOD_REPLACE: whatm = "replace"; break;
+ case LDAP_MOD_DELETE: whatm = "delete"; break;
+ case LDAP_MOD_INCREMENT: whatm = "increment"; break;
+ default:
+ fprintf(f, "# MOD_TYPE_UNKNOWN:%02x\n", m->sml_op & LDAP_MOD_OP);
+ continue;
+ }
+ fprintf(f, "%s: %s\n", whatm, m->sml_desc->ad_cname.bv_val);
+ if((b = m->sml_values) != NULL)
+ for(i = 0; b[i].bv_val; i++)
+ fprint_ldif(f, m->sml_desc->ad_cname.bv_val, b[i].bv_val, b[i].bv_len);
+ fprintf(f, "-\n");
+ }
+ break;
+
+ case LDAP_REQ_MODRDN:
+ fprintf(f, "newrdn: %s\ndeleteoldrdn: %s\n",
+ op->orr_newrdn.bv_val, op->orr_deleteoldrdn ? "1" : "0");
+ if(op->orr_newSup) fprintf(f, "newsuperior: %s\n", op->orr_newSup->bv_val);
+ break;
+
+ case LDAP_REQ_DELETE:
+ /* nothing else needed */
+ break;
+ }
+
+ fprintf(f, "# end %s %ld\n\n", what, (long)stamp);
+
+ fclose(f);
+ ldap_pvt_thread_mutex_unlock(&ad->ad_mutex);
+ return SLAP_CB_CONTINUE;
+}
+
+static slap_overinst auditlog;
+
+static int
+auditlog_db_init(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ auditlog_data *ad = ch_calloc(1, sizeof(auditlog_data));
+
+ on->on_bi.bi_private = ad;
+ ldap_pvt_thread_mutex_init( &ad->ad_mutex );
+ return 0;
+}
+
+static int
+auditlog_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ auditlog_data *ad = on->on_bi.bi_private;
+
+ ldap_pvt_thread_mutex_destroy( &ad->ad_mutex );
+ free( ad->ad_logfile );
+ free( ad );
+ return 0;
+}
+
+int auditlog_initialize() {
+ int rc;
+
+ auditlog.on_bi.bi_type = "auditlog";
+ auditlog.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ auditlog.on_bi.bi_db_init = auditlog_db_init;
+ auditlog.on_bi.bi_db_destroy = auditlog_db_destroy;
+ auditlog.on_response = auditlog_response;
+
+ auditlog.on_bi.bi_cf_ocs = auditlogocs;
+ rc = config_register_schema( auditlogcfg, auditlogocs );
+ if ( rc ) return rc;
+
+ return overlay_register(&auditlog);
+}
+
+#if SLAPD_OVER_AUDITLOG == SLAPD_MOD_DYNAMIC && defined(PIC)
+int
+init_module( int argc, char *argv[] )
+{
+ return auditlog_initialize();
+}
+#endif
+
+#endif /* SLAPD_OVER_AUDITLOG */
diff --git a/servers/slapd/overlays/autoca.c b/servers/slapd/overlays/autoca.c
new file mode 100644
index 0000000..50d3ca4
--- /dev/null
+++ b/servers/slapd/overlays/autoca.c
@@ -0,0 +1,1117 @@
+/* autoca.c - Automatic Certificate Authority */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2009-2022 The OpenLDAP Foundation.
+ * Copyright 2009-2018 by Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_AUTOCA
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "lutil.h"
+#include "slap.h"
+#include "slap-config.h"
+
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/evp.h>
+#include <openssl/bn.h>
+
+/* Starting with OpenSSL 1.1.0, rsa.h is no longer included in
+ * x509.h, so we need to explicitly include it for the
+ * call to EVP_PKEY_CTX_set_rsa_keygen_bits
+ */
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+#include <openssl/rsa.h>
+#define X509_get_notBefore(x) X509_getm_notBefore(x)
+#define X509_get_notAfter(x) X509_getm_notAfter(x)
+#endif
+
+/* This overlay implements a certificate authority that can generate
+ * certificates automatically for any entry in the directory.
+ * On startup it generates a self-signed CA cert for the directory's
+ * suffix entry and uses this to sign all other certs that it generates.
+ * User and server certs are generated on demand, using a Search request.
+ */
+
+#define LBER_TAG_OID ((ber_tag_t) 0x06UL)
+#define LBER_TAG_UTF8 ((ber_tag_t) 0x0cUL)
+
+#define KEYBITS 2048
+#define MIN_KEYBITS 512
+
+#define ACA_SCHEMA_ROOT "1.3.6.1.4.1.4203.666.11.11"
+
+#define ACA_SCHEMA_AT ACA_SCHEMA_ROOT ".1"
+#define ACA_SCHEMA_OC ACA_SCHEMA_ROOT ".2"
+
+static AttributeDescription *ad_caCert, *ad_caPkey, *ad_usrCert, *ad_usrPkey;
+static AttributeDescription *ad_mail, *ad_ipaddr;
+static ObjectClass *oc_caObj, *oc_usrObj;
+
+static char *aca_attrs[] = {
+ "( " ACA_SCHEMA_AT ".1 NAME 'cAPrivateKey' "
+ "DESC 'X.509 CA private key, use ;binary' "
+ "SUP pKCS8PrivateKey )",
+ "( " ACA_SCHEMA_AT ".2 NAME 'userPrivateKey' "
+ "DESC 'X.509 user private key, use ;binary' "
+ "SUP pKCS8PrivateKey )",
+ NULL
+};
+
+static struct {
+ char *at;
+ AttributeDescription **ad;
+} aca_attr2[] = {
+ { "cACertificate;binary", &ad_caCert },
+ { "cAPrivateKey;binary", &ad_caPkey },
+ { "userCertificate;binary", &ad_usrCert },
+ { "userPrivateKey;binary", &ad_usrPkey },
+ { "mail", &ad_mail },
+ { NULL }
+};
+
+static struct {
+ char *ot;
+ ObjectClass **oc;
+} aca_ocs[] = {
+ { "( " ACA_SCHEMA_OC ".1 NAME 'autoCA' "
+ "DESC 'Automated PKI certificate authority' "
+ "SUP pkiCA AUXILIARY "
+ "MAY cAPrivateKey )", &oc_caObj },
+ { "( " ACA_SCHEMA_OC ".2 NAME 'autoCAuser' "
+ "DESC 'Automated PKI CA user' "
+ "SUP pkiUser AUXILIARY "
+ "MAY userPrivateKey )", &oc_usrObj },
+ { NULL }
+};
+
+typedef struct autoca_info {
+ X509 *ai_cert;
+ EVP_PKEY *ai_pkey;
+ ObjectClass *ai_usrclass;
+ ObjectClass *ai_srvclass;
+ struct berval ai_localdn;
+ struct berval ai_localndn;
+ int ai_usrkeybits;
+ int ai_srvkeybits;
+ int ai_cakeybits;
+ int ai_usrdays;
+ int ai_srvdays;
+ int ai_cadays;
+} autoca_info;
+
+/* Rewrite an LDAP DN in DER form
+ * Input must be valid DN, therefore no error checking is done here.
+ */
+static int autoca_dnbv2der( Operation *op, struct berval *bv, struct berval *der )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ LDAPDN dn;
+ LDAPRDN rdn;
+ LDAPAVA *ava;
+ AttributeDescription *ad;
+ int irdn, iava;
+
+ ldap_bv2dn_x( bv, &dn, LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx );
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+
+ /* count RDNs, we need them in reverse order */
+ for (irdn = 0; dn[irdn]; irdn++);
+ irdn--;
+
+ /* DN is a SEQuence of RDNs */
+ ber_start_seq( ber, LBER_SEQUENCE );
+ for (; irdn >=0; irdn--)
+ {
+ /* RDN is a SET of AVAs */
+ ber_start_set( ber, LBER_SET );
+ rdn = dn[irdn];
+ for (iava = 0; rdn[iava]; iava++)
+ {
+ const char *text;
+ char oid[1024];
+ struct berval bvo = { sizeof(oid), oid };
+ struct berval bva;
+
+ /* AVA is a SEQuence of attr and value */
+ ber_start_seq( ber, LBER_SEQUENCE );
+ ava = rdn[iava];
+ ad = NULL;
+ slap_bv2ad( &ava->la_attr, &ad, &text );
+ ber_str2bv( ad->ad_type->sat_oid, 0, 0, &bva );
+ ber_encode_oid( &bva, &bvo );
+ ber_put_berval( ber, &bvo, LBER_TAG_OID );
+ ber_put_berval( ber, &ava->la_value, LBER_TAG_UTF8 );
+ ber_put_seq( ber );
+ }
+ ber_put_set( ber );
+ }
+ ber_put_seq( ber );
+ ber_flatten2( ber, der, 0 );
+ ldap_dnfree_x( dn, op->o_tmpmemctx );
+ return 0;
+}
+
+static int autoca_genpkey(int bits, EVP_PKEY **pkey)
+{
+ EVP_PKEY_CTX *kctx;
+ int rc;
+
+ kctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
+ if (kctx == NULL)
+ return -1;
+ if (EVP_PKEY_keygen_init(kctx) <= 0)
+ {
+ EVP_PKEY_CTX_free(kctx);
+ return -1;
+ }
+ if (EVP_PKEY_CTX_set_rsa_keygen_bits(kctx, bits) <= 0)
+ {
+ EVP_PKEY_CTX_free(kctx);
+ return -1;
+ }
+ rc = EVP_PKEY_keygen(kctx, pkey);
+ EVP_PKEY_CTX_free(kctx);
+ return rc;
+}
+
+static int autoca_signcert(X509 *cert, EVP_PKEY *pkey)
+{
+ EVP_MD_CTX *ctx = EVP_MD_CTX_create();
+ EVP_PKEY_CTX *pkctx = NULL;
+ int rc = -1;
+
+ if ( ctx == NULL )
+ return -1;
+ if (EVP_DigestSignInit(ctx, &pkctx, NULL, NULL, pkey))
+ {
+ rc = X509_sign_ctx(cert, ctx);
+ }
+ EVP_MD_CTX_destroy(ctx);
+ return rc;
+}
+
+#define SERIAL_BITS 64 /* should be less than 160 */
+
+typedef struct myext {
+ char *name;
+ char *value;
+} myext;
+
+static myext CAexts[] = {
+ { "subjectKeyIdentifier", "hash" },
+ { "authorityKeyIdentifier", "keyid:always,issuer" },
+ { "basicConstraints", "critical,CA:true" },
+ { "keyUsage", "digitalSignature,cRLSign,keyCertSign" },
+ { "nsComment", "OpenLDAP automatic certificate" },
+ { NULL }
+};
+
+static myext usrExts[] = {
+ { "subjectKeyIdentifier", "hash" },
+ { "authorityKeyIdentifier", "keyid:always,issuer" },
+ { "basicConstraints", "CA:false" },
+ { "keyUsage", "digitalSignature,nonRepudiation,keyEncipherment" },
+ { "extendedKeyUsage", "clientAuth,emailProtection,codeSigning" },
+ { "nsComment", "OpenLDAP automatic certificate" },
+ { NULL }
+};
+
+static myext srvExts[] = {
+ { "subjectKeyIdentifier", "hash" },
+ { "authorityKeyIdentifier", "keyid:always,issuer" },
+ { "basicConstraints", "CA:false" },
+ { "keyUsage", "digitalSignature,keyEncipherment" },
+ { "extendedKeyUsage", "serverAuth,clientAuth" },
+ { "nsComment", "OpenLDAP automatic certificate" },
+ { NULL }
+};
+
+typedef struct genargs {
+ X509 *issuer_cert;
+ EVP_PKEY *issuer_pkey;
+ struct berval *subjectDN;
+ myext *cert_exts;
+ myext *more_exts;
+ X509 *newcert;
+ EVP_PKEY *newpkey;
+ struct berval dercert;
+ struct berval derpkey;
+ int keybits;
+ int days;
+} genargs;
+
+static int autoca_gencert( Operation *op, genargs *args )
+{
+ X509_NAME *subj_name, *issuer_name;
+ X509 *subj_cert;
+ struct berval derdn;
+ unsigned char *pp;
+ EVP_PKEY *evpk = NULL;
+ int rc;
+
+ if ((subj_cert = X509_new()) == NULL)
+ return -1;
+
+ autoca_dnbv2der( op, args->subjectDN, &derdn );
+ pp = (unsigned char *)derdn.bv_val;
+ subj_name = d2i_X509_NAME( NULL, (const unsigned char **)&pp, derdn.bv_len );
+ op->o_tmpfree( derdn.bv_val, op->o_tmpmemctx );
+ if ( subj_name == NULL )
+ {
+fail1:
+ X509_free( subj_cert );
+ return -1;
+ }
+
+ rc = autoca_genpkey( args->keybits, &evpk );
+ if ( rc <= 0 )
+ {
+fail2:
+ if ( subj_name ) X509_NAME_free( subj_name );
+ goto fail1;
+ }
+ /* encode DER in PKCS#8 */
+ {
+ PKCS8_PRIV_KEY_INFO *p8inf;
+ if (( p8inf = EVP_PKEY2PKCS8( evpk )) == NULL )
+ goto fail2;
+ args->derpkey.bv_len = i2d_PKCS8_PRIV_KEY_INFO( p8inf, NULL );
+ args->derpkey.bv_val = op->o_tmpalloc( args->derpkey.bv_len, op->o_tmpmemctx );
+ pp = (unsigned char *)args->derpkey.bv_val;
+ i2d_PKCS8_PRIV_KEY_INFO( p8inf, &pp );
+ PKCS8_PRIV_KEY_INFO_free( p8inf );
+ }
+ args->newpkey = evpk;
+
+ /* set random serial */
+ {
+ BIGNUM *bn = BN_new();
+ if ( bn == NULL )
+ {
+fail3:
+ EVP_PKEY_free( evpk );
+ goto fail2;
+ }
+ if (!BN_pseudo_rand(bn, SERIAL_BITS, 0, 0))
+ {
+ BN_free( bn );
+ goto fail3;
+ }
+ if (!BN_to_ASN1_INTEGER(bn, X509_get_serialNumber(subj_cert)))
+ {
+ BN_free( bn );
+ goto fail3;
+ }
+ BN_free(bn);
+ }
+ if (args->issuer_cert) {
+ issuer_name = X509_get_subject_name(args->issuer_cert);
+ } else {
+ issuer_name = subj_name;
+ args->issuer_cert = subj_cert;
+ args->issuer_pkey = evpk;
+ }
+ if (!X509_set_version(subj_cert, 2) || /* set version to V3 */
+ !X509_set_issuer_name(subj_cert, issuer_name) ||
+ !X509_set_subject_name(subj_cert, subj_name) ||
+ !X509_gmtime_adj(X509_get_notBefore(subj_cert), 0) ||
+ !X509_time_adj_ex(X509_get_notAfter(subj_cert), args->days, 0, NULL) ||
+ !X509_set_pubkey(subj_cert, evpk))
+ {
+ goto fail3;
+ }
+ X509_NAME_free(subj_name);
+ subj_name = NULL;
+
+ /* set cert extensions */
+ {
+ X509V3_CTX ctx;
+ X509_EXTENSION *ext;
+ int i;
+
+ X509V3_set_ctx(&ctx, args->issuer_cert, subj_cert, NULL, NULL, 0);
+ for (i=0; args->cert_exts[i].name; i++) {
+ ext = X509V3_EXT_nconf(NULL, &ctx, args->cert_exts[i].name, args->cert_exts[i].value);
+ if ( ext == NULL )
+ goto fail3;
+ rc = X509_add_ext(subj_cert, ext, -1);
+ X509_EXTENSION_free(ext);
+ if ( !rc )
+ goto fail3;
+ }
+ if (args->more_exts) {
+ for (i=0; args->more_exts[i].name; i++) {
+ ext = X509V3_EXT_nconf(NULL, &ctx, args->more_exts[i].name, args->more_exts[i].value);
+ if ( ext == NULL )
+ goto fail3;
+ rc = X509_add_ext(subj_cert, ext, -1);
+ X509_EXTENSION_free(ext);
+ if ( !rc )
+ goto fail3;
+ }
+ }
+ }
+ rc = autoca_signcert( subj_cert, args->issuer_pkey );
+ if ( rc < 0 )
+ goto fail3;
+ args->dercert.bv_len = i2d_X509( subj_cert, NULL );
+ args->dercert.bv_val = op->o_tmpalloc( args->dercert.bv_len, op->o_tmpmemctx );
+ pp = (unsigned char *)args->dercert.bv_val;
+ i2d_X509( subj_cert, &pp );
+ args->newcert = subj_cert;
+ return 0;
+}
+
+typedef struct saveargs {
+ ObjectClass *oc;
+ struct berval *dercert;
+ struct berval *derpkey;
+ slap_overinst *on;
+ struct berval *dn;
+ struct berval *ndn;
+ int isca;
+} saveargs;
+
+static int autoca_savecert( Operation *op, saveargs *args )
+{
+ Modifications mod[3], *mp = mod;
+ struct berval bvs[6], *bp = bvs;
+ BackendInfo *bi;
+ slap_callback cb = {0};
+ SlapReply rs = {REP_RESULT};
+
+ if ( args->oc ) {
+ mp->sml_numvals = 1;
+ mp->sml_values = bp;
+ mp->sml_nvalues = NULL;
+ mp->sml_desc = slap_schema.si_ad_objectClass;
+ mp->sml_op = LDAP_MOD_ADD;
+ mp->sml_flags = SLAP_MOD_INTERNAL;
+ *bp++ = args->oc->soc_cname;
+ BER_BVZERO( bp );
+ bp++;
+ mp->sml_next = mp+1;
+ mp++;
+ }
+ mp->sml_numvals = 1;
+ mp->sml_values = bp;
+ mp->sml_nvalues = NULL;
+ mp->sml_desc = args->isca ? ad_caCert : ad_usrCert;
+ mp->sml_op = LDAP_MOD_REPLACE;
+ mp->sml_flags = SLAP_MOD_INTERNAL;
+ *bp++ = *args->dercert;
+ BER_BVZERO( bp );
+ bp++;
+ mp->sml_next = mp+1;
+ mp++;
+
+ mp->sml_numvals = 1;
+ mp->sml_values = bp;
+ mp->sml_nvalues = NULL;
+ mp->sml_desc = args->isca ? ad_caPkey : ad_usrPkey;
+ mp->sml_op = LDAP_MOD_ADD;
+ mp->sml_flags = SLAP_MOD_INTERNAL;
+ *bp++ = *args->derpkey;
+ BER_BVZERO( bp );
+ mp->sml_next = NULL;
+
+ cb.sc_response = slap_null_cb;
+ bi = op->o_bd->bd_info;
+ op->o_bd->bd_info = args->on->on_info->oi_orig;
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->o_callback = &cb;
+ op->orm_modlist = mod;
+ op->orm_no_opattrs = 1;
+ op->o_req_dn = *args->dn;
+ op->o_req_ndn = *args->ndn;
+ op->o_bd->be_modify( op, &rs );
+ op->o_bd->bd_info = bi;
+ return rs.sr_err;
+}
+
+static const struct berval configDN = BER_BVC("cn=config");
+
+/* must run as a pool thread to avoid cn=config deadlock */
+static void *
+autoca_setca_task( void *ctx, void *arg )
+{
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+ Operation *op;
+ struct berval *cacert = arg;
+ Modifications mod;
+ struct berval bvs[2];
+ slap_callback cb = {0};
+ SlapReply rs = {REP_RESULT};
+ const char *text;
+
+ connection_fake_init( &conn, &opbuf, ctx );
+ op = &opbuf.ob_op;
+
+ mod.sml_numvals = 1;
+ mod.sml_values = bvs;
+ mod.sml_nvalues = NULL;
+ mod.sml_desc = NULL;
+ if ( slap_str2ad( "olcTLSCACertificate;binary", &mod.sml_desc, &text ))
+ goto leave;
+ mod.sml_op = LDAP_MOD_REPLACE;
+ mod.sml_flags = SLAP_MOD_INTERNAL;
+ bvs[0] = *cacert;
+ BER_BVZERO( &bvs[1] );
+ mod.sml_next = NULL;
+
+ cb.sc_response = slap_null_cb;
+ op->o_bd = select_backend( (struct berval *)&configDN, 0 );
+ if ( !op->o_bd )
+ goto leave;
+
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->o_callback = &cb;
+ op->orm_modlist = &mod;
+ op->orm_no_opattrs = 1;
+ op->o_req_dn = configDN;
+ op->o_req_ndn = configDN;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ op->o_bd->be_modify( op, &rs );
+leave:
+ ch_free( arg );
+ return NULL;
+}
+
+static int
+autoca_setca( struct berval *cacert )
+{
+ struct berval *bv = ch_malloc( sizeof(struct berval) + cacert->bv_len );
+ bv->bv_len = cacert->bv_len;
+ bv->bv_val = (char *)(bv+1);
+ AC_MEMCPY( bv->bv_val, cacert->bv_val, bv->bv_len );
+ return ldap_pvt_thread_pool_submit( &connection_pool, autoca_setca_task, bv );
+}
+
+static int
+autoca_setlocal( Operation *op, struct berval *cert, struct berval *pkey )
+{
+ Modifications mod[2];
+ struct berval bvs[4];
+ slap_callback cb = {0};
+ SlapReply rs = {REP_RESULT};
+ const char *text;
+
+ mod[0].sml_numvals = 1;
+ mod[0].sml_values = bvs;
+ mod[0].sml_nvalues = NULL;
+ mod[0].sml_desc = NULL;
+ if ( slap_str2ad( "olcTLSCertificate;binary", &mod[0].sml_desc, &text ))
+ return -1;
+ mod[0].sml_op = LDAP_MOD_REPLACE;
+ mod[0].sml_flags = SLAP_MOD_INTERNAL;
+ bvs[0] = *cert;
+ BER_BVZERO( &bvs[1] );
+ mod[0].sml_next = &mod[1];
+
+ mod[1].sml_numvals = 1;
+ mod[1].sml_values = &bvs[2];
+ mod[1].sml_nvalues = NULL;
+ mod[1].sml_desc = NULL;
+ if ( slap_str2ad( "olcTLSCertificateKey;binary", &mod[1].sml_desc, &text ))
+ return -1;
+ mod[1].sml_op = LDAP_MOD_REPLACE;
+ mod[1].sml_flags = SLAP_MOD_INTERNAL;
+ bvs[2] = *pkey;
+ BER_BVZERO( &bvs[3] );
+ mod[1].sml_next = NULL;
+
+ cb.sc_response = slap_null_cb;
+ op->o_bd = select_backend( (struct berval *)&configDN, 0 );
+ if ( !op->o_bd )
+ return -1;
+
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->o_callback = &cb;
+ op->orm_modlist = mod;
+ op->orm_no_opattrs = 1;
+ op->o_req_dn = configDN;
+ op->o_req_ndn = configDN;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ op->o_bd->be_modify( op, &rs );
+ return rs.sr_err;
+}
+
+enum {
+ ACA_USRCLASS = 1,
+ ACA_SRVCLASS,
+ ACA_USRKEYBITS,
+ ACA_SRVKEYBITS,
+ ACA_CAKEYBITS,
+ ACA_USRDAYS,
+ ACA_SRVDAYS,
+ ACA_CADAYS,
+ ACA_LOCALDN
+};
+
+static int autoca_cf( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ autoca_info *ai = on->on_bi.bi_private;
+ int rc = 0;
+
+ switch( c->op ) {
+ case SLAP_CONFIG_EMIT:
+ switch( c->type ) {
+ case ACA_USRCLASS:
+ if ( ai->ai_usrclass ) {
+ c->value_string = ch_strdup( ai->ai_usrclass->soc_cname.bv_val );
+ } else {
+ rc = 1;
+ }
+ break;
+ case ACA_SRVCLASS:
+ if ( ai->ai_srvclass ) {
+ c->value_string = ch_strdup( ai->ai_srvclass->soc_cname.bv_val );
+ } else {
+ rc = 1;
+ }
+ break;
+ case ACA_USRKEYBITS:
+ c->value_int = ai->ai_usrkeybits;
+ break;
+ case ACA_SRVKEYBITS:
+ c->value_int = ai->ai_srvkeybits;
+ break;
+ case ACA_CAKEYBITS:
+ c->value_int = ai->ai_cakeybits;
+ break;
+ case ACA_USRDAYS:
+ c->value_int = ai->ai_usrdays;
+ break;
+ case ACA_SRVDAYS:
+ c->value_int = ai->ai_srvdays;
+ break;
+ case ACA_CADAYS:
+ c->value_int = ai->ai_cadays;
+ break;
+ case ACA_LOCALDN:
+ if ( !BER_BVISNULL( &ai->ai_localdn )) {
+ rc = value_add_one( &c->rvalue_vals, &ai->ai_localdn );
+ } else {
+ rc = 1;
+ }
+ break;
+ }
+ break;
+ case LDAP_MOD_DELETE:
+ switch( c->type ) {
+ case ACA_USRCLASS:
+ ai->ai_usrclass = NULL;
+ break;
+ case ACA_SRVCLASS:
+ ai->ai_srvclass = NULL;
+ break;
+ case ACA_LOCALDN:
+ if ( ai->ai_localdn.bv_val ) {
+ ch_free( ai->ai_localdn.bv_val );
+ ch_free( ai->ai_localndn.bv_val );
+ BER_BVZERO( &ai->ai_localdn );
+ BER_BVZERO( &ai->ai_localndn );
+ }
+ break;
+ /* single-valued attrs, all no-ops */
+ }
+ break;
+ case SLAP_CONFIG_ADD:
+ case LDAP_MOD_ADD:
+ switch( c->type ) {
+ case ACA_USRCLASS:
+ {
+ ObjectClass *oc = oc_find( c->value_string );
+ if ( oc )
+ ai->ai_usrclass = oc;
+ else
+ rc = 1;
+ }
+ break;
+ case ACA_SRVCLASS:
+ {
+ ObjectClass *oc = oc_find( c->value_string );
+ if ( oc )
+ ai->ai_srvclass = oc;
+ else
+ rc = 1;
+ }
+ case ACA_USRKEYBITS:
+ if ( c->value_int < MIN_KEYBITS )
+ rc = 1;
+ else
+ ai->ai_usrkeybits = c->value_int;
+ break;
+ case ACA_SRVKEYBITS:
+ if ( c->value_int < MIN_KEYBITS )
+ rc = 1;
+ else
+ ai->ai_srvkeybits = c->value_int;
+ break;
+ case ACA_CAKEYBITS:
+ if ( c->value_int < MIN_KEYBITS )
+ rc = 1;
+ else
+ ai->ai_cakeybits = c->value_int;
+ break;
+ case ACA_USRDAYS:
+ ai->ai_usrdays = c->value_int;
+ break;
+ case ACA_SRVDAYS:
+ ai->ai_srvdays = c->value_int;
+ break;
+ case ACA_CADAYS:
+ ai->ai_cadays = c->value_int;
+ break;
+ case ACA_LOCALDN:
+ if ( c->be->be_nsuffix == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "suffix must be set" );
+ Debug( LDAP_DEBUG_CONFIG, "autoca_config: %s\n",
+ c->cr_msg );
+ rc = ARG_BAD_CONF;
+ break;
+ }
+ if ( !dnIsSuffix( &c->value_ndn, c->be->be_nsuffix )) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "DN is not a subordinate of backend" );
+ Debug( LDAP_DEBUG_CONFIG, "autoca_config: %s\n",
+ c->cr_msg );
+ rc = ARG_BAD_CONF;
+ break;
+ }
+ if ( ai->ai_localdn.bv_val ) {
+ ch_free( ai->ai_localdn.bv_val );
+ ch_free( ai->ai_localndn.bv_val );
+ }
+ ai->ai_localdn = c->value_dn;
+ ai->ai_localndn = c->value_ndn;
+ }
+ }
+ return rc;
+}
+
+static ConfigTable autoca_cfg[] = {
+ { "userClass", "objectclass", 2, 2, 0,
+ ARG_STRING|ARG_MAGIC|ACA_USRCLASS, autoca_cf,
+ "( OLcfgOvAt:22.1 NAME 'olcAutoCAuserClass' "
+ "DESC 'ObjectClass of user entries' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "serverClass", "objectclass", 2, 2, 0,
+ ARG_STRING|ARG_MAGIC|ACA_SRVCLASS, autoca_cf,
+ "( OLcfgOvAt:22.2 NAME 'olcAutoCAserverClass' "
+ "DESC 'ObjectClass of server entries' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "userKeybits", "integer", 2, 2, 0,
+ ARG_INT|ARG_MAGIC|ACA_USRKEYBITS, autoca_cf,
+ "( OLcfgOvAt:22.3 NAME 'olcAutoCAuserKeybits' "
+ "DESC 'Size of PrivateKey for user entries' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "serverKeybits", "integer", 2, 2, 0,
+ ARG_INT|ARG_MAGIC|ACA_SRVKEYBITS, autoca_cf,
+ "( OLcfgOvAt:22.4 NAME 'olcAutoCAserverKeybits' "
+ "DESC 'Size of PrivateKey for server entries' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "caKeybits", "integer", 2, 2, 0,
+ ARG_INT|ARG_MAGIC|ACA_CAKEYBITS, autoca_cf,
+ "( OLcfgOvAt:22.5 NAME 'olcAutoCAKeybits' "
+ "DESC 'Size of PrivateKey for CA certificate' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "userDays", "integer", 2, 2, 0,
+ ARG_INT|ARG_MAGIC|ACA_USRDAYS, autoca_cf,
+ "( OLcfgOvAt:22.6 NAME 'olcAutoCAuserDays' "
+ "DESC 'Lifetime of user certificates in days' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "serverDays", "integer", 2, 2, 0,
+ ARG_INT|ARG_MAGIC|ACA_SRVDAYS, autoca_cf,
+ "( OLcfgOvAt:22.7 NAME 'olcAutoCAserverDays' "
+ "DESC 'Lifetime of server certificates in days' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "caDays", "integer", 2, 2, 0,
+ ARG_INT|ARG_MAGIC|ACA_CADAYS, autoca_cf,
+ "( OLcfgOvAt:22.8 NAME 'olcAutoCADays' "
+ "DESC 'Lifetime of CA certificate in days' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "localdn", "dn", 2, 2, 0,
+ ARG_DN|ARG_QUOTE|ARG_MAGIC|ACA_LOCALDN, autoca_cf,
+ "( OLcfgOvAt:22.9 NAME 'olcAutoCAlocalDN' "
+ "DESC 'DN of local server cert' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs autoca_ocs[] = {
+ { "( OLcfgOvOc:22.1 "
+ "NAME 'olcAutoCAConfig' "
+ "DESC 'AutoCA configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcAutoCAuserClass $ olcAutoCAserverClass $ "
+ "olcAutoCAuserKeybits $ olcAutoCAserverKeybits $ olcAutoCAKeyBits $ "
+ "olcAutoCAuserDays $ olcAutoCAserverDays $ olcAutoCADays $ "
+ "olcAutoCAlocalDN ) )",
+ Cft_Overlay, autoca_cfg },
+ { NULL, 0, NULL }
+};
+
+static int
+autoca_op_response(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ slap_overinst *on = op->o_callback->sc_private;
+ autoca_info *ai = on->on_bi.bi_private;
+ Attribute *a;
+ int isusr = 0;
+
+ if (rs->sr_type != REP_SEARCH)
+ return SLAP_CB_CONTINUE;
+
+ /* If root or self */
+ if ( !be_isroot( op ) &&
+ !dn_match( &rs->sr_entry->e_nname, &op->o_ndn ))
+ return SLAP_CB_CONTINUE;
+
+ isusr = is_entry_objectclass( rs->sr_entry, ai->ai_usrclass, SLAP_OCF_CHECK_SUP );
+ if ( !isusr )
+ {
+ if (!is_entry_objectclass( rs->sr_entry, ai->ai_srvclass, SLAP_OCF_CHECK_SUP ))
+ return SLAP_CB_CONTINUE;
+ }
+ a = attr_find( rs->sr_entry->e_attrs, ad_usrPkey );
+ if ( !a )
+ {
+ Operation op2;
+ genargs args;
+ saveargs arg2;
+ myext extras[2];
+ int rc;
+
+ args.issuer_cert = ai->ai_cert;
+ args.issuer_pkey = ai->ai_pkey;
+ args.subjectDN = &rs->sr_entry->e_name;
+ args.more_exts = NULL;
+ if ( isusr )
+ {
+ args.cert_exts = usrExts;
+ args.keybits = ai->ai_usrkeybits;
+ args.days = ai->ai_usrdays;
+ a = attr_find( rs->sr_entry->e_attrs, ad_mail );
+ if ( a )
+ {
+ extras[0].name = "subjectAltName";
+ extras[1].name = NULL;
+ extras[0].value = op->o_tmpalloc( sizeof("email:") + a->a_vals[0].bv_len, op->o_tmpmemctx );
+ sprintf(extras[0].value, "email:%s", a->a_vals[0].bv_val);
+ args.more_exts = extras;
+ }
+ } else
+ {
+ args.cert_exts = srvExts;
+ args.keybits = ai->ai_srvkeybits;
+ args.days = ai->ai_srvdays;
+ if ( ad_ipaddr && (a = attr_find( rs->sr_entry->e_attrs, ad_ipaddr )))
+ {
+ extras[0].name = "subjectAltName";
+ extras[1].name = NULL;
+ extras[0].value = op->o_tmpalloc( sizeof("IP:") + a->a_vals[0].bv_len, op->o_tmpmemctx );
+ sprintf(extras[0].value, "IP:%s", a->a_vals[0].bv_val);
+ args.more_exts = extras;
+ }
+ }
+ rc = autoca_gencert( op, &args );
+ if ( rc )
+ return SLAP_CB_CONTINUE;
+ X509_free( args.newcert );
+ EVP_PKEY_free( args.newpkey );
+
+ if ( is_entry_objectclass( rs->sr_entry, oc_usrObj, 0 ))
+ arg2.oc = NULL;
+ else
+ arg2.oc = oc_usrObj;
+ if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE ))
+ {
+ Entry *e = entry_dup( rs->sr_entry );
+ rs_replace_entry( op, rs, on, e );
+ rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED;
+ }
+ arg2.dercert = &args.dercert;
+ arg2.derpkey = &args.derpkey;
+ arg2.on = on;
+ arg2.dn = &rs->sr_entry->e_name;
+ arg2.ndn = &rs->sr_entry->e_nname;
+ arg2.isca = 0;
+ op2 = *op;
+ rc = autoca_savecert( &op2, &arg2 );
+ if ( !rc )
+ {
+ /* If this is our cert DN, configure it */
+ if ( dn_match( &rs->sr_entry->e_nname, &ai->ai_localndn ))
+ autoca_setlocal( &op2, &args.dercert, &args.derpkey );
+ attr_merge_one( rs->sr_entry, ad_usrCert, &args.dercert, NULL );
+ attr_merge_one( rs->sr_entry, ad_usrPkey, &args.derpkey, NULL );
+ }
+ op->o_tmpfree( args.dercert.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( args.derpkey.bv_val, op->o_tmpmemctx );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+autoca_op_search(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ /* we only act on a search that returns just our cert/key attrs */
+ if ( op->ors_attrs && op->ors_attrs[0].an_desc == ad_usrCert &&
+ op->ors_attrs[1].an_desc == ad_usrPkey &&
+ op->ors_attrs[2].an_name.bv_val == NULL )
+ {
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ slap_callback *sc = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
+ sc->sc_response = autoca_op_response;
+ sc->sc_private = on;
+ sc->sc_next = op->o_callback;
+ op->o_callback = sc;
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+autoca_db_init(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ autoca_info *ai;
+
+ ai = ch_calloc(1, sizeof(autoca_info));
+ on->on_bi.bi_private = ai;
+
+ /* set defaults */
+ ai->ai_usrclass = oc_find( "person" );
+ ai->ai_srvclass = oc_find( "ipHost" );
+ ai->ai_usrkeybits = KEYBITS;
+ ai->ai_srvkeybits = KEYBITS;
+ ai->ai_cakeybits = KEYBITS;
+ ai->ai_usrdays = 365; /* 1 year */
+ ai->ai_srvdays = 1826; /* 5 years */
+ ai->ai_cadays = 3652; /* 10 years */
+ return 0;
+}
+
+static int
+autoca_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ autoca_info *ai = on->on_bi.bi_private;
+
+ if ( ai->ai_cert )
+ X509_free( ai->ai_cert );
+ if ( ai->ai_pkey )
+ EVP_PKEY_free( ai->ai_pkey );
+ ch_free( ai );
+
+ return 0;
+}
+
+static int
+autoca_db_open(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ autoca_info *ai = on->on_bi.bi_private;
+
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+ Operation *op;
+ void *thrctx;
+ Entry *e = NULL;
+ Attribute *a;
+ int rc;
+
+ if (slapMode & SLAP_TOOL_MODE)
+ return 0;
+
+ if ( ! *aca_attr2[0].ad ) {
+ int i, code;
+ const char *text;
+
+ for ( i=0; aca_attr2[i].at; i++ ) {
+ code = slap_str2ad( aca_attr2[i].at, aca_attr2[i].ad, &text );
+ if ( code ) return code;
+ }
+
+ /* Schema may not be loaded, ignore if missing */
+ slap_str2ad( "ipHostNumber", &ad_ipaddr, &text );
+
+ for ( i=0; aca_ocs[i].ot; i++ ) {
+ code = register_oc( aca_ocs[i].ot, aca_ocs[i].oc, 0 );
+ if ( code ) return code;
+ }
+ }
+
+ thrctx = ldap_pvt_thread_pool_context();
+ connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+ op = &opbuf.ob_op;
+ op->o_bd = be;
+ op->o_dn = be->be_rootdn;
+ op->o_ndn = be->be_rootndn;
+ rc = overlay_entry_get_ov( op, be->be_nsuffix, NULL,
+ NULL, 0, &e, on );
+
+ if ( e ) {
+ int gotoc = 0, gotat = 0;
+ if ( is_entry_objectclass( e, oc_caObj, 0 )) {
+ gotoc = 1;
+ a = attr_find( e->e_attrs, ad_caPkey );
+ if ( a ) {
+ const unsigned char *pp;
+ pp = (unsigned char *)a->a_vals[0].bv_val;
+ ai->ai_pkey = d2i_AutoPrivateKey( NULL, &pp, a->a_vals[0].bv_len );
+ if ( ai->ai_pkey )
+ {
+ a = attr_find( e->e_attrs, ad_caCert );
+ if ( a )
+ {
+ pp = (unsigned char *)a->a_vals[0].bv_val;
+ ai->ai_cert = d2i_X509( NULL, &pp, a->a_vals[0].bv_len );
+ /* If TLS wasn't configured yet, set this as our CA */
+ if ( !slap_tls_ctx )
+ autoca_setca( a->a_vals );
+ }
+ }
+ gotat = 1;
+ }
+ }
+ overlay_entry_release_ov( op, e, 0, on );
+ /* generate attrs, store... */
+ if ( !gotat ) {
+ genargs args;
+ saveargs arg2;
+
+ args.issuer_cert = NULL;
+ args.issuer_pkey = NULL;
+ args.subjectDN = &be->be_suffix[0];
+ args.cert_exts = CAexts;
+ args.more_exts = NULL;
+ args.keybits = ai->ai_cakeybits;
+ args.days = ai->ai_cadays;
+
+ rc = autoca_gencert( op, &args );
+ if ( rc )
+ return -1;
+
+ ai->ai_cert = args.newcert;
+ ai->ai_pkey = args.newpkey;
+
+ arg2.dn = be->be_suffix;
+ arg2.ndn = be->be_nsuffix;
+ arg2.isca = 1;
+ if ( !gotoc )
+ arg2.oc = oc_caObj;
+ else
+ arg2.oc = NULL;
+ arg2.on = on;
+ arg2.dercert = &args.dercert;
+ arg2.derpkey = &args.derpkey;
+
+ autoca_savecert( op, &arg2 );
+
+ /* If TLS wasn't configured yet, set this as our CA */
+ if ( !slap_tls_ctx )
+ autoca_setca( &args.dercert );
+
+ op->o_tmpfree( args.dercert.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( args.derpkey.bv_val, op->o_tmpmemctx );
+ }
+ }
+
+ return 0;
+}
+
+static slap_overinst autoca;
+
+/* This overlay is set up for dynamic loading via moduleload. For static
+ * configuration, you'll need to arrange for the slap_overinst to be
+ * initialized and registered by some other function inside slapd.
+ */
+
+int autoca_initialize() {
+ int i, code;
+
+ autoca.on_bi.bi_type = "autoca";
+ autoca.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ autoca.on_bi.bi_db_init = autoca_db_init;
+ autoca.on_bi.bi_db_destroy = autoca_db_destroy;
+ autoca.on_bi.bi_db_open = autoca_db_open;
+ autoca.on_bi.bi_op_search = autoca_op_search;
+
+ autoca.on_bi.bi_cf_ocs = autoca_ocs;
+ code = config_register_schema( autoca_cfg, autoca_ocs );
+ if ( code ) return code;
+
+ for ( i=0; aca_attrs[i]; i++ ) {
+ code = register_at( aca_attrs[i], NULL, 0 );
+ if ( code ) return code;
+ }
+
+ return overlay_register( &autoca );
+}
+
+#if SLAPD_OVER_AUTOCA == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return autoca_initialize();
+}
+#endif
+
+#endif /* defined(SLAPD_OVER_AUTOCA) */
diff --git a/servers/slapd/overlays/collect.c b/servers/slapd/overlays/collect.c
new file mode 100644
index 0000000..bbc6219
--- /dev/null
+++ b/servers/slapd/overlays/collect.c
@@ -0,0 +1,440 @@
+/* collect.c - Demonstration of overlay code */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2003 Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_COLLECT
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "slap-config.h"
+
+#include "lutil.h"
+
+/* This is a cheap hack to implement a collective attribute.
+ *
+ * This demonstration overlay looks for a specified attribute in an
+ * ancestor of a given entry and adds that attribute to the given
+ * entry when it is returned in a search response. It takes no effect
+ * for any other operations. If the ancestor does not exist, there
+ * is no effect. If no attribute was configured, there is no effect.
+ */
+
+typedef struct collect_info {
+ struct collect_info *ci_next;
+ struct berval ci_dn;
+ int ci_ad_num;
+ AttributeDescription *ci_ad[1];
+} collect_info;
+
+static int collect_cf( ConfigArgs *c );
+
+static ConfigTable collectcfg[] = {
+ { "collectinfo", "dn> <attribute", 3, 3, 0,
+ ARG_MAGIC, collect_cf,
+ "( OLcfgOvAt:19.1 NAME 'olcCollectInfo' "
+ "DESC 'DN of entry and attribute to distribute' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs collectocs[] = {
+ { "( OLcfgOvOc:19.1 "
+ "NAME 'olcCollectConfig' "
+ "DESC 'Collective Attribute configuration' "
+ "SUP olcOverlayConfig "
+ "MAY olcCollectInfo )",
+ Cft_Overlay, collectcfg },
+ { NULL, 0, NULL }
+};
+
+/*
+ * inserts a collect_info into on->on_bi.bi_private taking into account
+ * order. this means longer dn's (i.e. more specific dn's) will be found
+ * first when searching, allowing some limited overlap of dn's
+ */
+static void
+insert_ordered( slap_overinst *on, collect_info *ci ) {
+ collect_info *find = on->on_bi.bi_private;
+ collect_info *prev = NULL;
+ int found = 0;
+
+ while (!found) {
+ if (find == NULL) {
+ if (prev == NULL) {
+ /* base case - empty list */
+ on->on_bi.bi_private = ci;
+ ci->ci_next = NULL;
+ } else {
+ /* final case - end of list */
+ prev->ci_next = ci;
+ ci->ci_next = NULL;
+ }
+ found = 1;
+ } else if (find->ci_dn.bv_len < ci->ci_dn.bv_len) {
+ /* insert into list here */
+ if (prev == NULL) {
+ /* entry is head of list */
+ ci->ci_next = on->on_bi.bi_private;
+ on->on_bi.bi_private = ci;
+ } else {
+ /* entry is not head of list */
+ prev->ci_next = ci;
+ ci->ci_next = find;
+ }
+ found = 1;
+ } else {
+ /* keep looking */
+ prev = find;
+ find = find->ci_next;
+ }
+ }
+}
+
+static int
+collect_cf( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ int rc = 1, idx;
+
+ switch( c->op ) {
+ case SLAP_CONFIG_EMIT:
+ {
+ collect_info *ci;
+ for ( ci = on->on_bi.bi_private; ci; ci = ci->ci_next ) {
+ struct berval bv;
+ char *ptr;
+ int len;
+
+ /* calculate the length & malloc memory */
+ bv.bv_len = ci->ci_dn.bv_len + STRLENOF("\"\" ");
+ for (idx=0; idx<ci->ci_ad_num; idx++) {
+ bv.bv_len += ci->ci_ad[idx]->ad_cname.bv_len;
+ if (idx<(ci->ci_ad_num-1)) {
+ bv.bv_len++;
+ }
+ }
+ bv.bv_val = ch_malloc( bv.bv_len + 1 );
+
+ /* copy the value and update len */
+ len = snprintf( bv.bv_val, bv.bv_len + 1, "\"%s\" ",
+ ci->ci_dn.bv_val);
+ ptr = bv.bv_val + len;
+ for (idx=0; idx<ci->ci_ad_num; idx++) {
+ ptr = lutil_strncopy( ptr,
+ ci->ci_ad[idx]->ad_cname.bv_val,
+ ci->ci_ad[idx]->ad_cname.bv_len);
+ if (idx<(ci->ci_ad_num-1)) {
+ *ptr++ = ',';
+ }
+ }
+ *ptr = '\0';
+ bv.bv_len = ptr - bv.bv_val;
+
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ rc = 0;
+ }
+ }
+ break;
+ case LDAP_MOD_DELETE:
+ if ( c->valx == -1 ) {
+ /* Delete entire attribute */
+ collect_info *ci;
+ while (( ci = on->on_bi.bi_private )) {
+ on->on_bi.bi_private = ci->ci_next;
+ ch_free( ci->ci_dn.bv_val );
+ ch_free( ci );
+ }
+ } else {
+ /* Delete just one value */
+ collect_info **cip, *ci;
+ int i;
+ cip = (collect_info **)&on->on_bi.bi_private;
+ ci = *cip;
+ for ( i=0; i < c->valx; i++ ) {
+ cip = &ci->ci_next;
+ ci = *cip;
+ }
+ *cip = ci->ci_next;
+ ch_free( ci->ci_dn.bv_val );
+ ch_free( ci );
+ }
+ rc = 0;
+ break;
+ case SLAP_CONFIG_ADD:
+ case LDAP_MOD_ADD:
+ {
+ collect_info *ci;
+ struct berval bv, dn;
+ const char *text;
+ int idx, count=0;
+ char *arg;
+
+ /* count delimiters in attribute argument */
+ arg = strtok(c->argv[2], ",");
+ while (arg!=NULL) {
+ count++;
+ arg = strtok(NULL, ",");
+ }
+
+ /* validate and normalize dn */
+ ber_str2bv( c->argv[1], 0, 0, &bv );
+ if ( dnNormalize( 0, NULL, NULL, &bv, &dn, NULL ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s invalid DN: \"%s\"",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+
+ /* check for duplicate DNs */
+ for ( ci = (collect_info *)on->on_bi.bi_private; ci;
+ ci = ci->ci_next ) {
+ /* If new DN is longest, there are no possible matches */
+ if ( dn.bv_len > ci->ci_dn.bv_len ) {
+ ci = NULL;
+ break;
+ }
+ if ( bvmatch( &dn, &ci->ci_dn )) {
+ break;
+ }
+ }
+ if ( ci ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s DN already configured: \"%s\"",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+
+ /* allocate config info with room for attribute array */
+ ci = ch_malloc( sizeof( collect_info ) +
+ sizeof( AttributeDescription * ) * count );
+
+ /* load attribute description for attribute list */
+ arg = c->argv[2];
+ for( idx=0; idx<count; idx++) {
+ ci->ci_ad[idx] = NULL;
+
+ if ( slap_str2ad( arg, &ci->ci_ad[idx], &text ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s attribute description unknown: \"%s\"",
+ c->argv[0], arg);
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg );
+ ch_free( ci );
+ return ARG_BAD_CONF;
+ }
+ while(*arg!='\0') {
+ arg++; /* skip to end of argument */
+ }
+ if (idx<count-1) {
+ arg++; /* skip inner delimiters */
+ }
+ }
+
+ /* The on->on_bi.bi_private pointer can be used for
+ * anything this instance of the overlay needs.
+ */
+ ci->ci_ad[count] = NULL;
+ ci->ci_ad_num = count;
+ ci->ci_dn = dn;
+
+ /* creates list of ci's ordered by dn length */
+ insert_ordered ( on, ci );
+
+ /* New ci wasn't simply appended to end, adjust its
+ * position in the config entry's a_vals
+ */
+ if ( c->ca_entry && ci->ci_next ) {
+ Attribute *a = attr_find( c->ca_entry->e_attrs,
+ collectcfg[0].ad );
+ if ( a ) {
+ struct berval bv, nbv;
+ collect_info *c2 = (collect_info *)on->on_bi.bi_private;
+ int i, j;
+ for ( i=0; c2 != ci; i++, c2 = c2->ci_next );
+ bv = a->a_vals[a->a_numvals-1];
+ nbv = a->a_nvals[a->a_numvals-1];
+ for ( j=a->a_numvals-1; j>i; j-- ) {
+ a->a_vals[j] = a->a_vals[j-1];
+ a->a_nvals[j] = a->a_nvals[j-1];
+ }
+ a->a_vals[j] = bv;
+ a->a_nvals[j] = nbv;
+ }
+ }
+
+ rc = 0;
+ }
+ }
+ return rc;
+}
+
+static int
+collect_destroy(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ collect_info *ci;
+
+ while (( ci = on->on_bi.bi_private )) {
+ on->on_bi.bi_private = ci->ci_next;
+ ch_free( ci->ci_dn.bv_val );
+ ch_free( ci );
+ }
+ return 0;
+}
+
+static int
+collect_modify( Operation *op, SlapReply *rs)
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ collect_info *ci = on->on_bi.bi_private;
+ Modifications *ml;
+ char errMsg[100];
+ int idx;
+
+ for ( ml = op->orm_modlist; ml != NULL; ml = ml->sml_next) {
+ for (; ci; ci=ci->ci_next ) {
+ /* Is this entry an ancestor of this collectinfo ? */
+ if (!dnIsSuffix(&op->o_req_ndn, &ci->ci_dn)) {
+ /* this collectinfo does not match */
+ continue;
+ }
+
+ /* Is this entry the same as the template DN ? */
+ if ( dn_match(&op->o_req_ndn, &ci->ci_dn)) {
+ /* all changes in this ci are allowed */
+ continue;
+ }
+
+ /* check for collect attributes - disallow modify if present */
+ for(idx=0; idx<ci->ci_ad_num; idx++) {
+ if (ml->sml_desc == ci->ci_ad[idx]) {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ snprintf( errMsg, sizeof( errMsg ),
+ "cannot change virtual attribute '%s'",
+ ci->ci_ad[idx]->ad_cname.bv_val);
+ rs->sr_text = errMsg;
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+ }
+ }
+
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+collect_response( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ collect_info *ci = on->on_bi.bi_private;
+
+ /* If we've been configured and the current response is
+ * a search entry
+ */
+ if ( ci && rs->sr_type == REP_SEARCH ) {
+ int rc;
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+
+ for (; ci; ci=ci->ci_next ) {
+ int idx=0;
+
+ /* Is this entry an ancestor of this collectinfo ? */
+ if (!dnIsSuffix(&rs->sr_entry->e_nname, &ci->ci_dn)) {
+ /* collectinfo does not match */
+ continue;
+ }
+
+ /* Is this entry the same as the template DN ? */
+ if ( dn_match(&rs->sr_entry->e_nname, &ci->ci_dn)) {
+ /* dont apply change to parent */
+ continue;
+ }
+
+ /* The current entry may live in a cache, so
+ * don't modify it directly. Make a copy and
+ * work with that instead.
+ */
+ rs_entry2modifiable( op, rs, on );
+
+ /* Loop for each attribute in this collectinfo */
+ for(idx=0; idx<ci->ci_ad_num; idx++) {
+ BerVarray vals = NULL;
+
+ /* Extract the values of the desired attribute from
+ * the ancestor entry */
+ rc = backend_attribute( op, NULL, &ci->ci_dn,
+ ci->ci_ad[idx], &vals, ACL_READ );
+
+ /* If there are any values, merge them into the
+ * current search result
+ */
+ if ( vals ) {
+ attr_merge_normalize( rs->sr_entry, ci->ci_ad[idx],
+ vals, op->o_tmpmemctx );
+ ber_bvarray_free_x( vals, op->o_tmpmemctx );
+ }
+ }
+ }
+ }
+
+ /* Default is to just fall through to the normal processing */
+ return SLAP_CB_CONTINUE;
+}
+
+static slap_overinst collect;
+
+int collect_initialize() {
+ int code;
+
+ collect.on_bi.bi_type = "collect";
+ collect.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ collect.on_bi.bi_db_destroy = collect_destroy;
+ collect.on_bi.bi_op_modify = collect_modify;
+ collect.on_response = collect_response;
+
+ collect.on_bi.bi_cf_ocs = collectocs;
+ code = config_register_schema( collectcfg, collectocs );
+ if ( code ) return code;
+
+ return overlay_register( &collect );
+}
+
+#if SLAPD_OVER_COLLECT == SLAPD_MOD_DYNAMIC
+int init_module(int argc, char *argv[]) {
+ return collect_initialize();
+}
+#endif
+
+#endif /* SLAPD_OVER_COLLECT */
diff --git a/servers/slapd/overlays/constraint.c b/servers/slapd/overlays/constraint.c
new file mode 100644
index 0000000..1feff7a
--- /dev/null
+++ b/servers/slapd/overlays/constraint.c
@@ -0,0 +1,1249 @@
+/* $OpenLDAP$ */
+/* constraint.c - Overlay to constrain attributes to certain values */
+/*
+ * Copyright 2003-2004 Hewlett-Packard Company
+ * Copyright 2007 Emmanuel Dreyfus
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/*
+ * Authors: Neil Dunbar <neil.dunbar@hp.com>
+ * Emmanuel Dreyfus <manu@netbsd.org>
+ */
+#include "portable.h"
+
+#ifdef SLAPD_OVER_CONSTRAINT
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/regex.h>
+
+#include "lutil.h"
+#include "slap.h"
+#include "slap-config.h"
+
+/*
+ * This overlay limits the values which can be placed into an
+ * attribute, over and above the limits placed by the schema.
+ *
+ * It traps only LDAP adds and modify commands (and only seeks to
+ * control the add and modify value mods of a modify)
+ */
+
+#define REGEX_STR "regex"
+#define NEG_REGEX_STR "negregex"
+#define URI_STR "uri"
+#define SET_STR "set"
+#define SIZE_STR "size"
+#define COUNT_STR "count"
+
+/*
+ * Linked list of attribute constraints which we should enforce.
+ * This is probably a sub optimal structure - some form of sorted
+ * array would be better if the number of attributes constrained is
+ * likely to be much bigger than 4 or 5. We stick with a list for
+ * the moment.
+ */
+
+typedef struct constraint {
+ struct constraint *ap_next;
+ AttributeDescription **ap;
+
+ LDAPURLDesc *restrict_lud;
+ struct berval restrict_ndn;
+ Filter *restrict_filter;
+ struct berval restrict_val;
+
+ int type;
+ regex_t *re;
+ LDAPURLDesc *lud;
+ int set;
+ size_t size;
+ size_t count;
+ AttributeDescription **attrs;
+ struct berval val; /* constraint value */
+ struct berval dn;
+ struct berval filter;
+} constraint;
+
+enum {
+ CONSTRAINT_ATTRIBUTE = 1,
+ CONSTRAINT_COUNT,
+ CONSTRAINT_SIZE,
+ CONSTRAINT_REGEX,
+ CONSTRAINT_NEG_REGEX,
+ CONSTRAINT_SET,
+ CONSTRAINT_URI,
+};
+
+static ConfigDriver constraint_cf_gen;
+
+static ConfigTable constraintcfg[] = {
+ { "constraint_attribute", "attribute[list]> (regex|negregex|uri|set|size|count) <value> [<restrict URI>]",
+ 4, 0, 0, ARG_MAGIC | CONSTRAINT_ATTRIBUTE, constraint_cf_gen,
+ "( OLcfgOvAt:13.1 NAME 'olcConstraintAttribute' "
+ "DESC 'constraint for list of attributes' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs constraintocs[] = {
+ { "( OLcfgOvOc:13.1 "
+ "NAME 'olcConstraintConfig' "
+ "DESC 'Constraint overlay configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcConstraintAttribute ) )",
+ Cft_Overlay, constraintcfg },
+ { NULL, 0, NULL }
+};
+
+static void
+constraint_free( constraint *cp, int freeme )
+{
+ if (cp->restrict_lud)
+ ldap_free_urldesc(cp->restrict_lud);
+ if (!BER_BVISNULL(&cp->restrict_ndn))
+ ch_free(cp->restrict_ndn.bv_val);
+ if (cp->restrict_filter != NULL && cp->restrict_filter != slap_filter_objectClass_pres)
+ filter_free(cp->restrict_filter);
+ if (!BER_BVISNULL(&cp->restrict_val))
+ ch_free(cp->restrict_val.bv_val);
+ if (cp->re) {
+ regfree(cp->re);
+ ch_free(cp->re);
+ }
+ if (!BER_BVISNULL(&cp->val))
+ ch_free(cp->val.bv_val);
+ if (cp->lud)
+ ldap_free_urldesc(cp->lud);
+ if (cp->attrs)
+ ch_free(cp->attrs);
+ if (cp->ap)
+ ch_free(cp->ap);
+ if (freeme)
+ ch_free(cp);
+}
+
+static int
+constraint_cf_gen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)(c->bi);
+ constraint *cn = on->on_bi.bi_private, *cp;
+ struct berval bv;
+ int i, rc = 0;
+ constraint ap = { NULL };
+ const char *text = NULL;
+
+ switch ( c->op ) {
+ case SLAP_CONFIG_EMIT:
+ switch (c->type) {
+ case CONSTRAINT_ATTRIBUTE:
+ for (cp=cn; cp; cp=cp->ap_next) {
+ char *s;
+ char *tstr = NULL;
+ int quotes = 0, numeric = 0;
+ int j;
+ size_t val;
+ char val_buf[SLAP_TEXT_BUFLEN] = { '\0' };
+
+ bv.bv_len = STRLENOF(" ");
+ for (j = 0; cp->ap[j]; j++) {
+ bv.bv_len += cp->ap[j]->ad_cname.bv_len;
+ }
+
+ /* room for commas */
+ bv.bv_len += j - 1;
+
+ switch (cp->type) {
+ case CONSTRAINT_COUNT:
+ tstr = COUNT_STR;
+ val = cp->count;
+ numeric = 1;
+ break;
+ case CONSTRAINT_SIZE:
+ tstr = SIZE_STR;
+ val = cp->size;
+ numeric = 1;
+ break;
+ case CONSTRAINT_REGEX:
+ tstr = REGEX_STR;
+ quotes = 1;
+ break;
+ case CONSTRAINT_NEG_REGEX:
+ tstr = NEG_REGEX_STR;
+ quotes = 1;
+ break;
+ case CONSTRAINT_SET:
+ tstr = SET_STR;
+ quotes = 1;
+ break;
+ case CONSTRAINT_URI:
+ tstr = URI_STR;
+ quotes = 1;
+ break;
+ default:
+ abort();
+ }
+
+ bv.bv_len += strlen(tstr);
+ bv.bv_len += cp->val.bv_len + 2*quotes;
+
+ if (cp->restrict_lud != NULL) {
+ bv.bv_len += cp->restrict_val.bv_len + STRLENOF(" restrict=\"\"");
+ }
+
+ if (numeric) {
+ int len = snprintf(val_buf, sizeof(val_buf), "%zu", val);
+ if (len <= 0) {
+ /* error */
+ return -1;
+ }
+ bv.bv_len += len;
+ }
+
+ s = bv.bv_val = ch_malloc(bv.bv_len + 1);
+
+ s = lutil_strncopy( s, cp->ap[0]->ad_cname.bv_val, cp->ap[0]->ad_cname.bv_len );
+ for (j = 1; cp->ap[j]; j++) {
+ *s++ = ',';
+ s = lutil_strncopy( s, cp->ap[j]->ad_cname.bv_val, cp->ap[j]->ad_cname.bv_len );
+ }
+ *s++ = ' ';
+ s = lutil_strcopy( s, tstr );
+ *s++ = ' ';
+ if (numeric) {
+ s = lutil_strcopy( s, val_buf );
+ } else {
+ if ( quotes ) *s++ = '"';
+ s = lutil_strncopy( s, cp->val.bv_val, cp->val.bv_len );
+ if ( quotes ) *s++ = '"';
+ }
+ if (cp->restrict_lud != NULL) {
+ s = lutil_strcopy( s, " restrict=\"" );
+ s = lutil_strncopy( s, cp->restrict_val.bv_val, cp->restrict_val.bv_len );
+ *s++ = '"';
+ }
+ *s = '\0';
+
+ rc = value_add_one( &c->rvalue_vals, &bv );
+ if (rc == LDAP_SUCCESS)
+ rc = value_add_one( &c->rvalue_nvals, &bv );
+ ch_free(bv.bv_val);
+ if (rc) return rc;
+ }
+ break;
+ default:
+ abort();
+ break;
+ }
+ break;
+ case LDAP_MOD_DELETE:
+ switch (c->type) {
+ case CONSTRAINT_ATTRIBUTE:
+ if (!cn) break; /* nothing to do */
+
+ if (c->valx < 0) {
+ /* zap all constraints */
+ while (cn) {
+ cp = cn->ap_next;
+ constraint_free( cn, 1 );
+ cn = cp;
+ }
+
+ on->on_bi.bi_private = NULL;
+ } else {
+ constraint **cpp;
+
+ /* zap constraint numbered 'valx' */
+ for(i=0, cp = cn, cpp = &cn;
+ (cp) && (i<c->valx);
+ i++, cpp = &cp->ap_next, cp = *cpp);
+
+ if (cp) {
+ /* zap cp, and join cpp to cp->ap_next */
+ *cpp = cp->ap_next;
+ constraint_free( cp, 1 );
+ }
+ on->on_bi.bi_private = cn;
+ }
+ break;
+
+ default:
+ abort();
+ break;
+ }
+ break;
+ case SLAP_CONFIG_ADD:
+ case LDAP_MOD_ADD:
+ switch (c->type) {
+ case CONSTRAINT_ATTRIBUTE: {
+ int j;
+ char **attrs = ldap_str2charray( c->argv[1], "," );
+
+ for ( j = 0; attrs[j]; j++)
+ /* just count */ ;
+ ap.ap = ch_calloc( sizeof(AttributeDescription*), j + 1 );
+ for ( j = 0; attrs[j]; j++) {
+ if ( slap_str2ad( attrs[j], &ap.ap[j], &text ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s <%s>: %s\n", c->argv[0], attrs[j], text );
+ rc = ARG_BAD_CONF;
+ goto done;
+ }
+ }
+
+ int is_regex = strcasecmp( c->argv[2], REGEX_STR ) == 0;
+ int is_neg_regex = strcasecmp( c->argv[2], NEG_REGEX_STR ) == 0;
+ if ( is_regex || is_neg_regex ) {
+ int err;
+
+ ap.type = is_regex ? CONSTRAINT_REGEX : CONSTRAINT_NEG_REGEX;
+ ap.re = ch_malloc( sizeof(regex_t) );
+ if ((err = regcomp( ap.re,
+ c->argv[3], REG_EXTENDED )) != 0) {
+ char errmsg[1024];
+
+ regerror( err, ap.re, errmsg, sizeof(errmsg) );
+ ch_free(ap.re);
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s %s: Illegal regular expression \"%s\": Error %s",
+ c->argv[0], c->argv[1], c->argv[3], errmsg);
+ ap.re = NULL;
+ rc = ARG_BAD_CONF;
+ goto done;
+ }
+ ber_str2bv( c->argv[3], 0, 1, &ap.val );
+ } else if ( strcasecmp( c->argv[2], SIZE_STR ) == 0 ) {
+ size_t size;
+ char *endptr;
+
+ ap.type = CONSTRAINT_SIZE;
+ ap.size = strtoull(c->argv[3], &endptr, 10);
+ if ( *endptr )
+ rc = ARG_BAD_CONF;
+ } else if ( strcasecmp( c->argv[2], COUNT_STR ) == 0 ) {
+ size_t count;
+ char *endptr;
+
+ ap.type = CONSTRAINT_COUNT;
+ ap.count = strtoull(c->argv[3], &endptr, 10);
+ if ( *endptr )
+ rc = ARG_BAD_CONF;
+ } else if ( strcasecmp( c->argv[2], URI_STR ) == 0 ) {
+ int err;
+
+ ap.type = CONSTRAINT_URI;
+ err = ldap_url_parse(c->argv[3], &ap.lud);
+ if ( err != LDAP_URL_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s %s: Invalid URI \"%s\"",
+ c->argv[0], c->argv[1], c->argv[3]);
+ rc = ARG_BAD_CONF;
+ goto done;
+ }
+
+ if (ap.lud->lud_host != NULL) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s %s: unsupported hostname in URI \"%s\"",
+ c->argv[0], c->argv[1], c->argv[3]);
+ ldap_free_urldesc(ap.lud);
+ rc = ARG_BAD_CONF;
+ goto done;
+ }
+
+ for ( i=0; ap.lud->lud_attrs[i]; i++);
+ /* FIXME: This is worthless without at least one attr */
+ if ( i ) {
+ ap.attrs = ch_malloc( (i+1)*sizeof(AttributeDescription *));
+ for ( i=0; ap.lud->lud_attrs[i]; i++) {
+ ap.attrs[i] = NULL;
+ if ( slap_str2ad( ap.lud->lud_attrs[i], &ap.attrs[i], &text ) ) {
+ ch_free( ap.attrs );
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s <%s>: %s\n", c->argv[0], ap.lud->lud_attrs[i], text );
+ rc = ARG_BAD_CONF;
+ goto done;
+ }
+ }
+ ap.attrs[i] = NULL;
+ }
+
+ if (ap.lud->lud_dn == NULL) {
+ ap.lud->lud_dn = ch_strdup("");
+ } else {
+ struct berval dn, ndn;
+
+ ber_str2bv( ap.lud->lud_dn, 0, 0, &dn );
+ if (dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL ) ) {
+ /* cleanup */
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s %s: URI %s DN normalization failed",
+ c->argv[0], c->argv[1], c->argv[3] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg );
+ rc = ARG_BAD_CONF;
+ goto done;
+ }
+ ldap_memfree( ap.lud->lud_dn );
+ ap.lud->lud_dn = ndn.bv_val;
+ }
+
+ if (ap.lud->lud_filter == NULL) {
+ ap.lud->lud_filter = ch_strdup("objectClass=*");
+ } else if ( ap.lud->lud_filter[0] == '(' ) {
+ ber_len_t len = strlen( ap.lud->lud_filter );
+ if ( ap.lud->lud_filter[len - 1] != ')' ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s %s: invalid URI filter: %s",
+ c->argv[0], c->argv[1], ap.lud->lud_filter );
+ rc = ARG_BAD_CONF;
+ goto done;
+ }
+ AC_MEMCPY( &ap.lud->lud_filter[0], &ap.lud->lud_filter[1], len - 2 );
+ ap.lud->lud_filter[len - 2] = '\0';
+ }
+
+ ber_str2bv( c->argv[3], 0, 1, &ap.val );
+
+ } else if ( strcasecmp( c->argv[2], SET_STR ) == 0 ) {
+ ap.set = 1;
+ ber_str2bv( c->argv[3], 0, 1, &ap.val );
+ ap.type = CONSTRAINT_SET;
+
+ } else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s %s: Unknown constraint type: %s",
+ c->argv[0], c->argv[1], c->argv[2] );
+ rc = ARG_BAD_CONF;
+ goto done;
+ }
+
+ if ( c->argc > 4 ) {
+ int argidx;
+
+ for ( argidx = 4; argidx < c->argc; argidx++ ) {
+ if ( strncasecmp( c->argv[argidx], "restrict=", STRLENOF("restrict=") ) == 0 ) {
+ int err;
+ char *arg = c->argv[argidx] + STRLENOF("restrict=");
+
+ err = ldap_url_parse(arg, &ap.restrict_lud);
+ if ( err != LDAP_URL_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s %s: Invalid restrict URI \"%s\"",
+ c->argv[0], c->argv[1], arg);
+ rc = ARG_BAD_CONF;
+ goto done;
+ }
+
+ if (ap.restrict_lud->lud_host != NULL) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s %s: unsupported hostname in restrict URI \"%s\"",
+ c->argv[0], c->argv[1], arg);
+ rc = ARG_BAD_CONF;
+ goto done;
+ }
+
+ if ( ap.restrict_lud->lud_attrs != NULL ) {
+ if ( ap.restrict_lud->lud_attrs[0] != NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s %s: attrs not allowed in restrict URI %s\n",
+ c->argv[0], c->argv[1], arg);
+ rc = ARG_BAD_CONF;
+ goto done;
+ }
+ ldap_memvfree((void *)ap.restrict_lud->lud_attrs);
+ ap.restrict_lud->lud_attrs = NULL;
+ }
+
+ if (ap.restrict_lud->lud_dn != NULL) {
+ if (ap.restrict_lud->lud_dn[0] == '\0') {
+ ldap_memfree(ap.restrict_lud->lud_dn);
+ ap.restrict_lud->lud_dn = NULL;
+
+ } else {
+ struct berval dn, ndn;
+ int j;
+
+ ber_str2bv(ap.restrict_lud->lud_dn, 0, 0, &dn);
+ if (dnNormalize(0, NULL, NULL, &dn, &ndn, NULL)) {
+ /* cleanup */
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s %s: restrict URI %s DN normalization failed",
+ c->argv[0], c->argv[1], arg );
+ rc = ARG_BAD_CONF;
+ goto done;
+ }
+
+ assert(c->be != NULL);
+ if (c->be->be_nsuffix == NULL) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s %s: restrict URI requires suffix",
+ c->argv[0], c->argv[1] );
+ rc = ARG_BAD_CONF;
+ goto done;
+ }
+
+ for ( j = 0; !BER_BVISNULL(&c->be->be_nsuffix[j]); j++) {
+ if (dnIsSuffix(&ndn, &c->be->be_nsuffix[j])) break;
+ }
+
+ if (BER_BVISNULL(&c->be->be_nsuffix[j])) {
+ /* error */
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s %s: restrict URI DN %s not within database naming context(s)",
+ c->argv[0], c->argv[1], dn.bv_val );
+ rc = ARG_BAD_CONF;
+ goto done;
+ }
+
+ ap.restrict_ndn = ndn;
+ }
+ }
+
+ if (ap.restrict_lud->lud_filter != NULL) {
+ ap.restrict_filter = str2filter(ap.restrict_lud->lud_filter);
+ if (ap.restrict_filter == NULL) {
+ /* error */
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s %s: restrict URI filter %s invalid",
+ c->argv[0], c->argv[1], ap.restrict_lud->lud_filter );
+ rc = ARG_BAD_CONF;
+ goto done;
+ }
+ }
+
+ ber_str2bv(c->argv[argidx] + STRLENOF("restrict="), 0, 1, &ap.restrict_val);
+
+ } else {
+ /* cleanup */
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s %s: unrecognized arg #%d (%s)",
+ c->argv[0], c->argv[1], argidx, c->argv[argidx] );
+ rc = ARG_BAD_CONF;
+ goto done;
+ }
+ }
+ }
+
+done:;
+ if ( rc == LDAP_SUCCESS ) {
+ constraint **app, *a2 = ch_calloc( sizeof(constraint), 1 );
+
+ a2->ap = ap.ap;
+ a2->type = ap.type;
+ a2->re = ap.re;
+ a2->val = ap.val;
+ a2->lud = ap.lud;
+ a2->set = ap.set;
+ a2->size = ap.size;
+ a2->count = ap.count;
+ if ( a2->lud ) {
+ ber_str2bv(a2->lud->lud_dn, 0, 0, &a2->dn);
+ ber_str2bv(a2->lud->lud_filter, 0, 0, &a2->filter);
+ }
+ a2->attrs = ap.attrs;
+ a2->restrict_lud = ap.restrict_lud;
+ a2->restrict_ndn = ap.restrict_ndn;
+ a2->restrict_filter = ap.restrict_filter;
+ a2->restrict_val = ap.restrict_val;
+
+ for ( app = &on->on_bi.bi_private; *app; app = &(*app)->ap_next )
+ /* Get to the end */ ;
+
+ a2->ap_next = *app;
+ *app = a2;
+
+ } else {
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg );
+ constraint_free( &ap, 0 );
+ }
+
+ ldap_memvfree((void**)attrs);
+ } break;
+ default:
+ abort();
+ break;
+ }
+ break;
+ default:
+ abort();
+ }
+
+ return rc;
+}
+
+static int
+constraint_uri_cb( Operation *op, SlapReply *rs )
+{
+ if(rs->sr_type == REP_SEARCH) {
+ int *foundp = op->o_callback->sc_private;
+
+ *foundp = 1;
+
+ Debug(LDAP_DEBUG_TRACE, "==> constraint_uri_cb <%s>\n",
+ rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN" );
+ }
+ return 0;
+}
+
+static int
+constraint_violation( constraint *c, struct berval *bv, Operation *op )
+{
+ if ((!c) || (!bv)) return LDAP_SUCCESS;
+
+ switch (c->type) {
+ case CONSTRAINT_SIZE:
+ if (bv->bv_len > c->size)
+ return LDAP_CONSTRAINT_VIOLATION; /* size violation */
+ break;
+ case CONSTRAINT_REGEX:
+ if (regexec(c->re, bv->bv_val, 0, NULL, 0) == REG_NOMATCH)
+ return LDAP_CONSTRAINT_VIOLATION; /* regular expression violation */
+ break;
+ case CONSTRAINT_NEG_REGEX:
+ if (regexec(c->re, bv->bv_val, 0, NULL, 0) != REG_NOMATCH)
+ return LDAP_CONSTRAINT_VIOLATION; /* regular expression violation */
+ break;
+ case CONSTRAINT_URI: {
+ Operation nop = *op;
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ slap_callback cb = { 0 };
+ int i;
+ int found = 0;
+ int rc;
+ size_t len;
+ struct berval filterstr;
+ char *ptr;
+
+ cb.sc_response = constraint_uri_cb;
+ cb.sc_private = &found;
+
+ nop.o_protocol = LDAP_VERSION3;
+ nop.o_tag = LDAP_REQ_SEARCH;
+ nop.o_time = slap_get_time();
+ if (c->lud->lud_dn) {
+ struct berval dn;
+
+ ber_str2bv(c->lud->lud_dn, 0, 0, &dn);
+ nop.o_req_dn = dn;
+ nop.o_req_ndn = dn;
+ nop.o_bd = select_backend(&nop.o_req_ndn, 1 );
+ if (!nop.o_bd) {
+ return LDAP_NO_SUCH_OBJECT; /* unexpected error */
+ }
+ if (!nop.o_bd->be_search) {
+ return LDAP_OTHER; /* unexpected error */
+ }
+ } else {
+ nop.o_req_dn = nop.o_bd->be_nsuffix[0];
+ nop.o_req_ndn = nop.o_bd->be_nsuffix[0];
+ nop.o_bd = on->on_info->oi_origdb;
+ }
+ nop.o_do_not_cache = 1;
+ nop.o_callback = &cb;
+
+ nop.ors_scope = c->lud->lud_scope;
+ nop.ors_deref = LDAP_DEREF_NEVER;
+ nop.ors_slimit = SLAP_NO_LIMIT;
+ nop.ors_tlimit = SLAP_NO_LIMIT;
+ nop.ors_limit = NULL;
+
+ nop.ors_attrsonly = 0;
+ nop.ors_attrs = slap_anlist_no_attrs;
+
+ len = STRLENOF("(&(") +
+ c->filter.bv_len +
+ STRLENOF(")(|");
+
+ for (i = 0; c->attrs[i]; i++) {
+ len += STRLENOF("(") +
+ c->attrs[i]->ad_cname.bv_len +
+ STRLENOF("=") +
+ bv->bv_len +
+ STRLENOF(")");
+ }
+
+ len += STRLENOF("))");
+ filterstr.bv_len = len;
+ filterstr.bv_val = op->o_tmpalloc(len + 1, op->o_tmpmemctx);
+
+ ptr = filterstr.bv_val +
+ snprintf(filterstr.bv_val, len, "(&(%s)(|", c->lud->lud_filter);
+ for (i = 0; c->attrs[i]; i++) {
+ *ptr++ = '(';
+ ptr = lutil_strcopy( ptr, c->attrs[i]->ad_cname.bv_val );
+ *ptr++ = '=';
+ ptr = lutil_strcopy( ptr, bv->bv_val );
+ *ptr++ = ')';
+ }
+ *ptr++ = ')';
+ *ptr++ = ')';
+ *ptr++ = '\0';
+
+ nop.ors_filterstr = filterstr;
+ nop.ors_filter = str2filter_x(&nop, filterstr.bv_val);
+ if ( nop.ors_filter == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s constraint_violation uri filter=\"%s\" invalid\n",
+ op->o_log_prefix, filterstr.bv_val );
+ rc = LDAP_OTHER;
+
+ } else {
+ SlapReply nrs = { REP_RESULT };
+
+ Debug(LDAP_DEBUG_TRACE,
+ "==> constraint_violation uri filter = %s\n",
+ filterstr.bv_val );
+
+ rc = nop.o_bd->be_search( &nop, &nrs );
+
+ Debug(LDAP_DEBUG_TRACE,
+ "==> constraint_violation uri rc = %d, found = %d\n",
+ rc, found );
+ }
+ op->o_tmpfree(filterstr.bv_val, op->o_tmpmemctx);
+
+ if ((rc != LDAP_SUCCESS) && (rc != LDAP_NO_SUCH_OBJECT)) {
+ return rc; /* unexpected error */
+ }
+
+ if (!found)
+ return LDAP_CONSTRAINT_VIOLATION; /* constraint violation */
+ break;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static char *
+print_message( struct berval *errtext, AttributeDescription *a )
+{
+ char *ret;
+ int sz;
+
+ sz = errtext->bv_len + sizeof(" on ") + a->ad_cname.bv_len;
+ ret = ch_malloc(sz);
+ snprintf( ret, sz, "%s on %s", errtext->bv_val, a->ad_cname.bv_val );
+ return ret;
+}
+
+static unsigned
+constraint_count_attr(Entry *e, AttributeDescription *ad)
+{
+ struct Attribute *a;
+
+ if ((a = attr_find(e->e_attrs, ad)) != NULL)
+ return a->a_numvals;
+ return 0;
+}
+
+static int
+constraint_check_restrict( Operation *op, constraint *c, Entry *e )
+{
+ assert( c->restrict_lud != NULL );
+
+ if ( c->restrict_lud->lud_dn != NULL ) {
+ int diff = e->e_nname.bv_len - c->restrict_ndn.bv_len;
+
+ if ( diff < 0 ) {
+ return 0;
+ }
+
+ if ( c->restrict_lud->lud_scope == LDAP_SCOPE_BASE ) {
+ return bvmatch( &e->e_nname, &c->restrict_ndn );
+ }
+
+ if ( !dnIsSuffix( &e->e_nname, &c->restrict_ndn ) ) {
+ return 0;
+ }
+
+ if ( c->restrict_lud->lud_scope != LDAP_SCOPE_SUBTREE ) {
+ struct berval pdn;
+
+ if ( diff == 0 ) {
+ return 0;
+ }
+
+ dnParent( &e->e_nname, &pdn );
+
+ if ( c->restrict_lud->lud_scope == LDAP_SCOPE_ONELEVEL
+ && pdn.bv_len != c->restrict_ndn.bv_len )
+ {
+ return 0;
+ }
+ }
+ }
+
+ if ( c->restrict_filter != NULL ) {
+ int rc;
+ struct berval save_dn = op->o_dn, save_ndn = op->o_ndn;
+
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ rc = test_filter( op, e, c->restrict_filter );
+ op->o_dn = save_dn;
+ op->o_ndn = save_ndn;
+
+ if ( rc != LDAP_COMPARE_TRUE ) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int
+constraint_add( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ Attribute *a;
+ constraint *c = on->on_bi.bi_private, *cp;
+ BerVarray b = NULL;
+ int i;
+ struct berval rsv = BER_BVC("add breaks constraint");
+ int rc = 0;
+ char *msg = NULL;
+
+ if (get_relax(op) || SLAPD_SYNC_IS_SYNCCONN( op->o_connid )) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ((a = op->ora_e->e_attrs) == NULL) {
+ op->o_bd->bd_info = (BackendInfo *)(on->on_info);
+ send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
+ "constraint_add: no attrs");
+ return(rs->sr_err);
+ }
+
+ for(; a; a = a->a_next ) {
+ /* we don't constrain operational attributes */
+ if (is_at_operational(a->a_desc->ad_type)) continue;
+
+ for(cp = c; cp; cp = cp->ap_next) {
+ int j;
+ for (j = 0; cp->ap[j]; j++) {
+ if (cp->ap[j] == a->a_desc) break;
+ }
+ if (cp->ap[j] == NULL) continue;
+ if ((b = a->a_vals) == NULL) continue;
+
+ if (cp->restrict_lud != NULL && constraint_check_restrict(op, cp, op->ora_e) == 0) {
+ continue;
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ "==> constraint_add, "
+ "a->a_numvals = %u, cp->count = %lu\n",
+ a->a_numvals, (unsigned long) cp->count );
+
+ switch (cp->type) {
+ case CONSTRAINT_COUNT:
+ if (a->a_numvals > cp->count)
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ break;
+ case CONSTRAINT_SET:
+ if (acl_match_set(&cp->val, op, op->ora_e, NULL) == 0)
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ break;
+ default:
+ for ( i = 0; b[i].bv_val; i++ ) {
+ rc = constraint_violation( cp, &b[i], op );
+ if ( rc ) {
+ goto add_violation;
+ }
+ }
+ }
+ if ( rc )
+ goto add_violation;
+
+ }
+ }
+
+ /* Default is to just fall through to the normal processing */
+ return SLAP_CB_CONTINUE;
+
+add_violation:
+ op->o_bd->bd_info = (BackendInfo *)(on->on_info);
+ if (rc == LDAP_CONSTRAINT_VIOLATION ) {
+ msg = print_message( &rsv, a->a_desc );
+ }
+ send_ldap_error(op, rs, rc, msg );
+ ch_free(msg);
+ return (rs->sr_err);
+}
+
+
+static int
+constraint_check_count_violation( Modifications *m, Entry *target_entry, constraint *cp )
+{
+ BerVarray b = NULL;
+ unsigned ce = 0;
+ unsigned ca;
+ int j;
+
+ for ( j = 0; cp->ap[j]; j++ ) {
+ /* Get this attribute count */
+ if ( target_entry )
+ ce = constraint_count_attr( target_entry, cp->ap[j] );
+
+ for( ; m; m = m->sml_next ) {
+ if ( cp->ap[j] == m->sml_desc ) {
+ ca = m->sml_numvals;
+ switch ( m->sml_op ) {
+ case LDAP_MOD_DELETE:
+ case SLAP_MOD_SOFTDEL:
+ if ( !ca || ca > ce ) {
+ ce = 0;
+ } else {
+ /* No need to check for values' validity. Invalid values
+ * cause the whole transaction to die anyway. */
+ ce -= ca;
+ }
+ break;
+
+ case LDAP_MOD_ADD:
+ case SLAP_MOD_SOFTADD:
+ ce += ca;
+ break;
+
+ case LDAP_MOD_REPLACE:
+ ce = ca;
+ break;
+
+#if 0
+ /* TODO */
+ case handle SLAP_MOD_ADD_IF_NOT_PRESENT:
+#endif
+
+ default:
+ /* impossible! assert? */
+ return 1;
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ "==> constraint_check_count_violation ce = %u, "
+ "ca = %u, cp->count = %lu\n",
+ ce, ca, (unsigned long) cp->count);
+ }
+ }
+ }
+
+ return ( ce > cp->count );
+}
+
+static int
+constraint_update( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ Backend *be = op->o_bd;
+ constraint *c = on->on_bi.bi_private, *cp;
+ Entry *target_entry = NULL, *target_entry_copy = NULL;
+ Modifications *modlist, *m;
+ BerVarray b = NULL;
+ int i;
+ struct berval rsv = BER_BVC("modify breaks constraint");
+ int rc;
+ char *msg = NULL;
+ int is_v;
+
+ if (get_relax(op) || SLAPD_SYNC_IS_SYNCCONN( op->o_connid )) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ switch ( op->o_tag ) {
+ case LDAP_REQ_MODIFY:
+ modlist = op->orm_modlist;
+ break;
+
+ case LDAP_REQ_MODRDN:
+ modlist = op->orr_modlist;
+ break;
+
+ default:
+ /* impossible! assert? */
+ return LDAP_OTHER;
+ }
+
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "constraint_update()\n" );
+ if ((m = modlist) == NULL) {
+ op->o_bd->bd_info = (BackendInfo *)(on->on_info);
+ send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
+ "constraint_update() got null modlist");
+ return(rs->sr_err);
+ }
+
+ op->o_bd = on->on_info->oi_origdb;
+ rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &target_entry );
+ op->o_bd = be;
+
+ /* let the backend send the error */
+ if ( target_entry == NULL )
+ return SLAP_CB_CONTINUE;
+
+ /* Do we need to count attributes? */
+ for(cp = c; cp; cp = cp->ap_next) {
+ if (cp->type == CONSTRAINT_COUNT) {
+ if (cp->restrict_lud && constraint_check_restrict(op, cp, target_entry) == 0) {
+ continue;
+ }
+
+ is_v = constraint_check_count_violation(m, target_entry, cp);
+
+ Debug(LDAP_DEBUG_TRACE,
+ "==> constraint_update is_v: %d\n", is_v );
+
+ if (is_v) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto mod_violation;
+ }
+ }
+ }
+
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ for(;m; m = m->sml_next) {
+ unsigned ce = 0;
+
+ if (is_at_operational( m->sml_desc->ad_type )) continue;
+
+ if ((( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_ADD) &&
+ (( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_REPLACE) &&
+ (( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_DELETE))
+ continue;
+ /* we only care about ADD and REPLACE modifications */
+ /* and DELETE are used to track attribute count */
+ if ((( b = m->sml_values ) == NULL ) || (b[0].bv_val == NULL))
+ continue;
+
+ for(cp = c; cp; cp = cp->ap_next) {
+ int j;
+ for (j = 0; cp->ap[j]; j++) {
+ if (cp->ap[j] == m->sml_desc) {
+ break;
+ }
+ }
+ if (cp->ap[j] == NULL) continue;
+
+ if (cp->restrict_lud != NULL && constraint_check_restrict(op, cp, target_entry) == 0) {
+ continue;
+ }
+
+ /* DELETE are to be ignored beyond this point */
+ if (( m->sml_op & LDAP_MOD_OP ) == LDAP_MOD_DELETE)
+ continue;
+
+ for ( i = 0; b[i].bv_val; i++ ) {
+ rc = constraint_violation( cp, &b[i], op );
+ if ( rc ) {
+ goto mod_violation;
+ }
+ }
+
+ if (cp->type == CONSTRAINT_SET && target_entry) {
+ if (target_entry_copy == NULL) {
+ Modifications *ml;
+
+ target_entry_copy = entry_dup(target_entry);
+
+ /* if rename, set the new entry's name
+ * (in normalized form only) */
+ if ( op->o_tag == LDAP_REQ_MODRDN ) {
+ struct berval pdn, ndn = BER_BVNULL;
+
+ if ( op->orr_nnewSup ) {
+ pdn = *op->orr_nnewSup;
+
+ } else {
+ dnParent( &target_entry_copy->e_nname, &pdn );
+ }
+
+ build_new_dn( &ndn, &pdn, &op->orr_nnewrdn, NULL );
+
+ ber_memfree( target_entry_copy->e_nname.bv_val );
+ target_entry_copy->e_nname = ndn;
+ ber_bvreplace( &target_entry_copy->e_name, &ndn );
+ }
+
+ /* apply modifications, in an attempt
+ * to estimate what the entry would
+ * look like in case all modifications
+ * pass */
+ for ( ml = modlist; ml; ml = ml->sml_next ) {
+ Modification *mod = &ml->sml_mod;
+ const char *text;
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof(textbuf);
+ int err;
+
+ switch ( mod->sm_op ) {
+ case LDAP_MOD_ADD:
+ err = modify_add_values( target_entry_copy,
+ mod, get_permissiveModify(op),
+ &text, textbuf, textlen );
+ break;
+
+ case LDAP_MOD_DELETE:
+ err = modify_delete_values( target_entry_copy,
+ mod, get_permissiveModify(op),
+ &text, textbuf, textlen );
+ break;
+
+ case LDAP_MOD_REPLACE:
+ err = modify_replace_values( target_entry_copy,
+ mod, get_permissiveModify(op),
+ &text, textbuf, textlen );
+ break;
+
+ case LDAP_MOD_INCREMENT:
+ err = modify_increment_values( target_entry_copy,
+ mod, get_permissiveModify(op),
+ &text, textbuf, textlen );
+ break;
+
+ case SLAP_MOD_SOFTADD:
+ mod->sm_op = LDAP_MOD_ADD;
+ err = modify_add_values( target_entry_copy,
+ mod, get_permissiveModify(op),
+ &text, textbuf, textlen );
+ mod->sm_op = SLAP_MOD_SOFTADD;
+ if ( err == LDAP_TYPE_OR_VALUE_EXISTS ) {
+ err = LDAP_SUCCESS;
+ }
+ break;
+
+ case SLAP_MOD_SOFTDEL:
+ mod->sm_op = LDAP_MOD_ADD;
+ err = modify_delete_values( target_entry_copy,
+ mod, get_permissiveModify(op),
+ &text, textbuf, textlen );
+ mod->sm_op = SLAP_MOD_SOFTDEL;
+ if ( err == LDAP_NO_SUCH_ATTRIBUTE ) {
+ err = LDAP_SUCCESS;
+ }
+ break;
+
+ case SLAP_MOD_ADD_IF_NOT_PRESENT:
+ if ( attr_find( target_entry_copy->e_attrs, mod->sm_desc ) ) {
+ err = LDAP_SUCCESS;
+ break;
+ }
+ mod->sm_op = LDAP_MOD_ADD;
+ err = modify_add_values( target_entry_copy,
+ mod, get_permissiveModify(op),
+ &text, textbuf, textlen );
+ mod->sm_op = SLAP_MOD_ADD_IF_NOT_PRESENT;
+ break;
+
+ default:
+ err = LDAP_OTHER;
+ break;
+ }
+
+ if ( err != LDAP_SUCCESS ) {
+ rc = err;
+ goto mod_violation;
+ }
+ }
+ }
+
+ if ( acl_match_set(&cp->val, op, target_entry_copy, NULL) == 0) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto mod_violation;
+ }
+ }
+ }
+ }
+
+ if (target_entry) {
+ op->o_bd = on->on_info->oi_origdb;
+ be_entry_release_r(op, target_entry);
+ op->o_bd = be;
+ }
+
+ if (target_entry_copy) {
+ entry_free(target_entry_copy);
+ }
+
+ return SLAP_CB_CONTINUE;
+
+mod_violation:
+ /* violation */
+ if (target_entry) {
+ op->o_bd = on->on_info->oi_origdb;
+ be_entry_release_r(op, target_entry);
+ op->o_bd = be;
+ }
+
+ if (target_entry_copy) {
+ entry_free(target_entry_copy);
+ }
+
+ op->o_bd->bd_info = (BackendInfo *)(on->on_info);
+ if ( rc == LDAP_CONSTRAINT_VIOLATION ) {
+ msg = print_message( &rsv, m->sml_desc );
+ }
+ send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION, msg );
+ ch_free(msg);
+ return (rs->sr_err);
+}
+
+static int
+constraint_destroy(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ constraint *ap, *a2;
+
+ for ( ap = on->on_bi.bi_private; ap; ap = a2 ) {
+ a2 = ap->ap_next;
+ constraint_free( ap, 1 );
+ }
+
+ return 0;
+}
+
+static slap_overinst constraint_ovl;
+
+#if SLAPD_OVER_CONSTRAINT == SLAPD_MOD_DYNAMIC
+static
+#endif
+int
+constraint_initialize( void ) {
+ int rc;
+
+ constraint_ovl.on_bi.bi_type = "constraint";
+ constraint_ovl.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ constraint_ovl.on_bi.bi_db_destroy = constraint_destroy;
+ constraint_ovl.on_bi.bi_op_add = constraint_add;
+ constraint_ovl.on_bi.bi_op_modify = constraint_update;
+ constraint_ovl.on_bi.bi_op_modrdn = constraint_update;
+
+ constraint_ovl.on_bi.bi_private = NULL;
+
+ constraint_ovl.on_bi.bi_cf_ocs = constraintocs;
+ rc = config_register_schema( constraintcfg, constraintocs );
+ if (rc) return rc;
+
+ return overlay_register( &constraint_ovl );
+}
+
+#if SLAPD_OVER_CONSTRAINT == SLAPD_MOD_DYNAMIC
+int init_module(int argc, char *argv[]) {
+ return constraint_initialize();
+}
+#endif
+
+#endif /* defined(SLAPD_OVER_CONSTRAINT) */
+
diff --git a/servers/slapd/overlays/dds.c b/servers/slapd/overlays/dds.c
new file mode 100644
index 0000000..c19f042
--- /dev/null
+++ b/servers/slapd/overlays/dds.c
@@ -0,0 +1,2056 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2005-2006 SysNet s.n.c.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software, sponsored by SysNet s.n.c.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_DDS
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "slap.h"
+#include "lutil.h"
+#include "ldap_rq.h"
+
+#include "slap-config.h"
+
+#define DDS_RF2589_MAX_TTL (31557600) /* 1 year + 6 hours */
+#define DDS_RF2589_DEFAULT_TTL (86400) /* 1 day */
+#define DDS_DEFAULT_INTERVAL (3600) /* 1 hour */
+
+typedef struct dds_info_t {
+ unsigned di_flags;
+#define DDS_FOFF (0x1U) /* is this really needed? */
+#define DDS_SET(di, f) ( (di)->di_flags & (f) )
+
+#define DDS_OFF(di) DDS_SET( (di), DDS_FOFF )
+
+ time_t di_max_ttl;
+ time_t di_min_ttl;
+ time_t di_default_ttl;
+#define DDS_DEFAULT_TTL(di) \
+ ( (di)->di_default_ttl ? (di)->di_default_ttl : (di)->di_max_ttl )
+
+ time_t di_tolerance;
+
+ /* expire check interval and task */
+ time_t di_interval;
+#define DDS_INTERVAL(di) \
+ ( (di)->di_interval ? (di)->di_interval : DDS_DEFAULT_INTERVAL )
+ struct re_s *di_expire_task;
+
+ /* allows to limit the maximum number of dynamic objects */
+ ldap_pvt_thread_mutex_t di_mutex;
+ int di_num_dynamicObjects;
+ int di_max_dynamicObjects;
+
+ /* used to advertise the dynamicSubtrees in the root DSE,
+ * and to select the database in the expiration task */
+ BerVarray di_suffix;
+ BerVarray di_nsuffix;
+} dds_info_t;
+
+static struct berval slap_EXOP_REFRESH = BER_BVC( LDAP_EXOP_REFRESH );
+static AttributeDescription *ad_entryExpireTimestamp;
+
+/* list of expired DNs */
+typedef struct dds_expire_t {
+ struct berval de_ndn;
+ struct dds_expire_t *de_next;
+} dds_expire_t;
+
+typedef struct dds_cb_t {
+ dds_expire_t *dc_ndnlist;
+} dds_cb_t;
+
+static int
+dds_expire_cb( Operation *op, SlapReply *rs )
+{
+ dds_cb_t *dc = (dds_cb_t *)op->o_callback->sc_private;
+ dds_expire_t *de;
+ int rc;
+
+ switch ( rs->sr_type ) {
+ case REP_SEARCH:
+ /* alloc list and buffer for berval all in one */
+ de = op->o_tmpalloc( sizeof( dds_expire_t ) + rs->sr_entry->e_nname.bv_len + 1,
+ op->o_tmpmemctx );
+
+ de->de_next = dc->dc_ndnlist;
+ dc->dc_ndnlist = de;
+
+ de->de_ndn.bv_len = rs->sr_entry->e_nname.bv_len;
+ de->de_ndn.bv_val = (char *)&de[ 1 ];
+ AC_MEMCPY( de->de_ndn.bv_val, rs->sr_entry->e_nname.bv_val,
+ rs->sr_entry->e_nname.bv_len + 1 );
+ rc = 0;
+ break;
+
+ case REP_SEARCHREF:
+ case REP_RESULT:
+ rc = rs->sr_err;
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+ return rc;
+}
+
+static int
+dds_expire( void *ctx, dds_info_t *di )
+{
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+ Operation *op;
+ slap_callback sc = { 0 };
+ dds_cb_t dc = { 0 };
+ dds_expire_t *de = NULL, **dep;
+ SlapReply rs = { REP_RESULT };
+
+ time_t expire;
+ char tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+ struct berval ts;
+
+ int ndeletes, ntotdeletes;
+
+ int rc;
+ char *extra = "";
+
+ connection_fake_init2( &conn, &opbuf, ctx, 0 );
+ op = &opbuf.ob_op;
+
+ op->o_tag = LDAP_REQ_SEARCH;
+ memset( &op->oq_search, 0, sizeof( op->oq_search ) );
+
+ op->o_bd = select_backend( &di->di_nsuffix[ 0 ], 0 );
+
+ op->o_req_dn = op->o_bd->be_suffix[ 0 ];
+ op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
+
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ op->ors_scope = LDAP_SCOPE_SUBTREE;
+ op->ors_tlimit = DDS_INTERVAL( di )/2 + 1;
+ op->ors_slimit = SLAP_NO_LIMIT;
+ op->ors_attrs = slap_anlist_no_attrs;
+ op->o_do_not_cache = 1;
+
+ expire = slap_get_time() - di->di_tolerance;
+ ts.bv_val = tsbuf;
+ ts.bv_len = sizeof( tsbuf );
+ slap_timestamp( &expire, &ts );
+
+ op->ors_filterstr.bv_len = STRLENOF( "(&(objectClass=" ")(" "<=" "))" )
+ + slap_schema.si_oc_dynamicObject->soc_cname.bv_len
+ + ad_entryExpireTimestamp->ad_cname.bv_len
+ + ts.bv_len;
+ op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
+ snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
+ "(&(objectClass=%s)(%s<=%s))",
+ slap_schema.si_oc_dynamicObject->soc_cname.bv_val,
+ ad_entryExpireTimestamp->ad_cname.bv_val, ts.bv_val );
+
+ op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
+ if ( op->ors_filter == NULL ) {
+ rs.sr_err = LDAP_OTHER;
+ goto done_search;
+ }
+
+ op->o_callback = &sc;
+ sc.sc_response = dds_expire_cb;
+ sc.sc_private = &dc;
+
+ (void)op->o_bd->bd_info->bi_op_search( op, &rs );
+
+done_search:;
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ filter_free_x( op, op->ors_filter, 1 );
+
+ rc = rs.sr_err;
+ switch ( rs.sr_err ) {
+ case LDAP_SUCCESS:
+ break;
+
+ case LDAP_NO_SUCH_OBJECT:
+ /* (ITS#5267) database not created yet? */
+ rs.sr_err = LDAP_SUCCESS;
+ extra = " (ignored)";
+ /* fallthru */
+
+ default:
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "DDS expired objects lookup failed err=%d%s\n",
+ rc, extra );
+ goto done;
+ }
+
+ op->o_tag = LDAP_REQ_DELETE;
+ op->o_callback = &sc;
+ sc.sc_response = slap_null_cb;
+ sc.sc_private = NULL;
+
+ for ( ntotdeletes = 0, ndeletes = 1; dc.dc_ndnlist != NULL && ndeletes > 0; ) {
+ ndeletes = 0;
+
+ for ( dep = &dc.dc_ndnlist; *dep != NULL; ) {
+ de = *dep;
+
+ op->o_req_dn = de->de_ndn;
+ op->o_req_ndn = de->de_ndn;
+ (void)op->o_bd->bd_info->bi_op_delete( op, &rs );
+ switch ( rs.sr_err ) {
+ case LDAP_SUCCESS:
+ Log( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
+ "DDS dn=\"%s\" expired.\n",
+ de->de_ndn.bv_val );
+ ndeletes++;
+ break;
+
+ case LDAP_NOT_ALLOWED_ON_NONLEAF:
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
+ "DDS dn=\"%s\" is non-leaf; "
+ "deferring.\n",
+ de->de_ndn.bv_val );
+ dep = &de->de_next;
+ de = NULL;
+ break;
+
+ default:
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
+ "DDS dn=\"%s\" err=%d; "
+ "deferring.\n",
+ de->de_ndn.bv_val, rs.sr_err );
+ break;
+ }
+
+ if ( de != NULL ) {
+ *dep = de->de_next;
+ op->o_tmpfree( de, op->o_tmpmemctx );
+ }
+ }
+
+ ntotdeletes += ndeletes;
+ }
+
+ rs.sr_err = LDAP_SUCCESS;
+
+ Log( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
+ "DDS expired=%d\n", ntotdeletes );
+
+done:;
+ return rs.sr_err;
+}
+
+static void *
+dds_expire_fn( void *ctx, void *arg )
+{
+ struct re_s *rtask = arg;
+ dds_info_t *di = rtask->arg;
+
+ assert( di->di_expire_task == rtask );
+
+ (void)dds_expire( ctx, di );
+
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
+ ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
+ }
+ ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+
+ return NULL;
+}
+
+/* frees the callback */
+static int
+dds_freeit_cb( Operation *op, SlapReply *rs )
+{
+ op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
+ op->o_callback = NULL;
+
+ return SLAP_CB_CONTINUE;
+}
+
+/* updates counter - installed on add/delete only if required */
+static int
+dds_counter_cb( Operation *op, SlapReply *rs )
+{
+ assert( rs->sr_type == REP_RESULT );
+
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ dds_info_t *di = op->o_callback->sc_private;
+
+ ldap_pvt_thread_mutex_lock( &di->di_mutex );
+ switch ( op->o_tag ) {
+ case LDAP_REQ_DELETE:
+ assert( di->di_num_dynamicObjects > 0 );
+ di->di_num_dynamicObjects--;
+ break;
+
+ case LDAP_REQ_ADD:
+ assert( di->di_num_dynamicObjects < di->di_max_dynamicObjects );
+ di->di_num_dynamicObjects++;
+ break;
+
+ default:
+ assert( 0 );
+ }
+ ldap_pvt_thread_mutex_unlock( &di->di_mutex );
+ }
+
+ return dds_freeit_cb( op, rs );
+}
+
+static int
+dds_op_add( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ dds_info_t *di = on->on_bi.bi_private;
+ int is_dynamicObject;
+
+ if ( DDS_OFF( di ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ is_dynamicObject = is_entry_dynamicObject( op->ora_e );
+
+ /* FIXME: do not allow this right now, pending clarification */
+ if ( is_dynamicObject ) {
+ rs->sr_err = LDAP_SUCCESS;
+
+ if ( is_entry_referral( op->ora_e ) ) {
+ rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
+ rs->sr_text = "a referral cannot be a dynamicObject";
+
+ } else if ( is_entry_alias( op->ora_e ) ) {
+ rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
+ rs->sr_text = "an alias cannot be a dynamicObject";
+ }
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+ }
+
+ /* we don't allow dynamicObjects to have static subordinates */
+ if ( !dn_match( &op->o_req_ndn, &op->o_bd->be_nsuffix[ 0 ] ) ) {
+ struct berval p_ndn;
+ Entry *e = NULL;
+ int rc;
+ BackendInfo *bi = op->o_bd->bd_info;
+
+ dnParent( &op->o_req_ndn, &p_ndn );
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rc = be_entry_get_rw( op, &p_ndn,
+ slap_schema.si_oc_dynamicObject, NULL, 0, &e );
+ if ( rc == LDAP_SUCCESS && e != NULL ) {
+ if ( !is_dynamicObject ) {
+ /* return referral only if "disclose"
+ * is granted on the object */
+ if ( ! access_allowed( op, e,
+ slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL ) )
+ {
+ rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ send_ldap_result( op, rs );
+
+ } else {
+ rc = rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ send_ldap_error( op, rs, rc, "no static subordinate entries allowed for dynamicObject" );
+ }
+ }
+
+ be_entry_release_r( op, e );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+ }
+ op->o_bd->bd_info = bi;
+ }
+
+ /* handle dynamic object operational attr(s) */
+ if ( is_dynamicObject ) {
+ time_t ttl, expire;
+ char ttlbuf[STRLENOF("31557600") + 1];
+ char tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+ struct berval bv;
+
+ if ( !be_isroot_dn( op->o_bd, &op->o_req_ndn ) ) {
+ ldap_pvt_thread_mutex_lock( &di->di_mutex );
+ rs->sr_err = ( di->di_max_dynamicObjects &&
+ di->di_num_dynamicObjects >= di->di_max_dynamicObjects );
+ ldap_pvt_thread_mutex_unlock( &di->di_mutex );
+ if ( rs->sr_err ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "too many dynamicObjects in context" );
+ return rs->sr_err;
+ }
+ }
+
+ ttl = DDS_DEFAULT_TTL( di );
+
+ /* assert because should be checked at configure */
+ assert( ttl <= DDS_RF2589_MAX_TTL );
+
+ bv.bv_val = ttlbuf;
+ bv.bv_len = snprintf( ttlbuf, sizeof( ttlbuf ), "%ld", ttl );
+ assert( bv.bv_len < sizeof( ttlbuf ) );
+
+ /* FIXME: apparently, values in op->ora_e are malloc'ed
+ * on the thread's slab; works fine by chance,
+ * only because the attribute doesn't exist yet. */
+ assert( attr_find( op->ora_e->e_attrs, slap_schema.si_ad_entryTtl ) == NULL );
+ attr_merge_one( op->ora_e, slap_schema.si_ad_entryTtl, &bv, &bv );
+
+ expire = slap_get_time() + ttl;
+ bv.bv_val = tsbuf;
+ bv.bv_len = sizeof( tsbuf );
+ slap_timestamp( &expire, &bv );
+ assert( attr_find( op->ora_e->e_attrs, ad_entryExpireTimestamp ) == NULL );
+ attr_merge_one( op->ora_e, ad_entryExpireTimestamp, &bv, &bv );
+
+ /* if required, install counter callback */
+ if ( di->di_max_dynamicObjects > 0) {
+ slap_callback *sc;
+
+ sc = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
+ sc->sc_cleanup = dds_freeit_cb;
+ sc->sc_response = dds_counter_cb;
+ sc->sc_private = di;
+ sc->sc_next = op->o_callback;
+ sc->sc_writewait = 0;
+
+ op->o_callback = sc;
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+dds_op_delete( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ dds_info_t *di = on->on_bi.bi_private;
+
+ /* if required, install counter callback */
+ if ( !DDS_OFF( di ) && di->di_max_dynamicObjects > 0 ) {
+ Entry *e = NULL;
+ BackendInfo *bi = op->o_bd->bd_info;
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
+ slap_schema.si_oc_dynamicObject, NULL, 0, &e );
+
+ /* FIXME: couldn't the entry be added before deletion? */
+ if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
+ slap_callback *sc;
+
+ be_entry_release_r( op, e );
+ e = NULL;
+
+ sc = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
+ sc->sc_cleanup = dds_freeit_cb;
+ sc->sc_response = dds_counter_cb;
+ sc->sc_private = di;
+ sc->sc_writewait = 0;
+ sc->sc_next = op->o_callback;
+
+ op->o_callback = sc;
+ }
+ op->o_bd->bd_info = bi;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+dds_op_modify( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ dds_info_t *di = (dds_info_t *)on->on_bi.bi_private;
+ Modifications *mod;
+ Entry *e = NULL;
+ BackendInfo *bi = op->o_bd->bd_info;
+ int was_dynamicObject = 0,
+ is_dynamicObject = 0;
+ struct berval bv_entryTtl = BER_BVNULL;
+ time_t entryTtl = 0;
+ char textbuf[ SLAP_TEXT_BUFLEN ];
+
+ if ( DDS_OFF( di ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* bv_entryTtl stores the string representation of the entryTtl
+ * across modifies for consistency checks of the final value;
+ * the bv_val points to a static buffer; the bv_len is zero when
+ * the attribute is deleted.
+ * entryTtl stores the integer representation of the entryTtl;
+ * its value is -1 when the attribute is deleted; it is 0 only
+ * if no modifications of the entryTtl occurred, as an entryTtl
+ * of 0 is invalid. */
+ bv_entryTtl.bv_val = textbuf;
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
+ slap_schema.si_oc_dynamicObject, slap_schema.si_ad_entryTtl, 0, &e );
+ if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
+ Attribute *a = attr_find( e->e_attrs, slap_schema.si_ad_entryTtl );
+
+ /* the value of the entryTtl is saved for later checks */
+ if ( a != NULL ) {
+ unsigned long ttl;
+ int rc;
+
+ bv_entryTtl.bv_len = a->a_nvals[ 0 ].bv_len;
+ AC_MEMCPY( bv_entryTtl.bv_val, a->a_nvals[ 0 ].bv_val, bv_entryTtl.bv_len );
+ bv_entryTtl.bv_val[ bv_entryTtl.bv_len ] = '\0';
+ rc = lutil_atoul( &ttl, bv_entryTtl.bv_val );
+ assert( rc == 0 );
+ entryTtl = (time_t)ttl;
+ }
+
+ be_entry_release_r( op, e );
+ e = NULL;
+ was_dynamicObject = is_dynamicObject = 1;
+ }
+ op->o_bd->bd_info = bi;
+
+ rs->sr_err = LDAP_SUCCESS;
+ for ( mod = op->orm_modlist; mod; mod = mod->sml_next ) {
+ if ( mod->sml_desc == slap_schema.si_ad_objectClass ) {
+ int i;
+ ObjectClass *oc;
+
+ switch ( mod->sml_op ) {
+ case LDAP_MOD_DELETE:
+ if ( mod->sml_values == NULL ) {
+ is_dynamicObject = 0;
+ break;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &mod->sml_values[ i ] ); i++ ) {
+ oc = oc_bvfind( &mod->sml_values[ i ] );
+ if ( oc == slap_schema.si_oc_dynamicObject ) {
+ is_dynamicObject = 0;
+ break;
+ }
+ }
+
+ break;
+
+ case LDAP_MOD_REPLACE:
+ if ( mod->sml_values == NULL ) {
+ is_dynamicObject = 0;
+ break;
+ }
+ /* fallthru */
+
+ case LDAP_MOD_ADD:
+ for ( i = 0; !BER_BVISNULL( &mod->sml_values[ i ] ); i++ ) {
+ oc = oc_bvfind( &mod->sml_values[ i ] );
+ if ( oc == slap_schema.si_oc_dynamicObject ) {
+ is_dynamicObject = 1;
+ break;
+ }
+ }
+ break;
+ }
+
+ } else if ( mod->sml_desc == slap_schema.si_ad_entryTtl ) {
+ unsigned long uttl;
+ time_t ttl;
+ int rc;
+
+ switch ( mod->sml_op ) {
+ case LDAP_MOD_DELETE:
+ case SLAP_MOD_SOFTDEL: /* FIXME? */
+ if ( mod->sml_values != NULL ) {
+ if ( BER_BVISEMPTY( &bv_entryTtl )
+ || !bvmatch( &bv_entryTtl, &mod->sml_values[ 0 ] ) )
+ {
+ rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
+ slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
+ if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+
+ } else {
+ rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
+ }
+ goto done;
+ }
+ }
+ bv_entryTtl.bv_len = 0;
+ entryTtl = -1;
+ break;
+
+ case LDAP_MOD_REPLACE:
+ bv_entryTtl.bv_len = 0;
+ entryTtl = -1;
+ /* fallthru */
+
+ case LDAP_MOD_ADD:
+ case SLAP_MOD_SOFTADD: /* FIXME? */
+ case SLAP_MOD_ADD_IF_NOT_PRESENT: /* FIXME? */
+ assert( mod->sml_values != NULL );
+ assert( BER_BVISNULL( &mod->sml_values[ 1 ] ) );
+
+ if ( !BER_BVISEMPTY( &bv_entryTtl ) ) {
+ rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
+ slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
+ if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+
+ } else {
+ rs->sr_text = "attribute 'entryTtl' cannot have multiple values";
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ }
+ goto done;
+ }
+
+ rc = lutil_atoul( &uttl, mod->sml_values[ 0 ].bv_val );
+ ttl = (time_t)uttl;
+ assert( rc == 0 );
+ if ( ttl > DDS_RF2589_MAX_TTL ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ rs->sr_text = "invalid time-to-live for dynamicObject";
+ goto done;
+ }
+
+ if ( ttl <= 0 || ttl > di->di_max_ttl ) {
+ /* FIXME: I don't understand if this has to be an error,
+ * or an indication that the requested Ttl has been
+ * shortened to di->di_max_ttl >= 1 day */
+ rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
+ rs->sr_text = "time-to-live for dynamicObject exceeds administrative limit";
+ goto done;
+ }
+
+ entryTtl = ttl;
+ bv_entryTtl.bv_len = mod->sml_values[ 0 ].bv_len;
+ AC_MEMCPY( bv_entryTtl.bv_val, mod->sml_values[ 0 ].bv_val, bv_entryTtl.bv_len );
+ bv_entryTtl.bv_val[ bv_entryTtl.bv_len ] = '\0';
+ break;
+
+ case LDAP_MOD_INCREMENT:
+ if ( BER_BVISEMPTY( &bv_entryTtl ) ) {
+ rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
+ slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
+ if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+
+ } else {
+ rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
+ rs->sr_text = "modify/increment: entryTtl: no such attribute";
+ }
+ goto done;
+ }
+
+ entryTtl++;
+ if ( entryTtl > DDS_RF2589_MAX_TTL ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ rs->sr_text = "invalid time-to-live for dynamicObject";
+
+ } else if ( entryTtl <= 0 || entryTtl > di->di_max_ttl ) {
+ /* FIXME: I don't understand if this has to be an error,
+ * or an indication that the requested Ttl has been
+ * shortened to di->di_max_ttl >= 1 day */
+ rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
+ rs->sr_text = "time-to-live for dynamicObject exceeds administrative limit";
+ }
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ rc = backend_attribute( op, NULL, &op->o_req_ndn,
+ slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
+ if ( rc == LDAP_INSUFFICIENT_ACCESS ) {
+ rs->sr_text = NULL;
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+
+ }
+ goto done;
+ }
+
+ bv_entryTtl.bv_len = snprintf( textbuf, sizeof( textbuf ), "%ld", entryTtl );
+ break;
+
+ default:
+ assert( 0 );
+ break;
+ }
+
+ } else if ( mod->sml_desc == ad_entryExpireTimestamp ) {
+ /* should have been trapped earlier */
+ assert( mod->sml_flags & SLAP_MOD_INTERNAL );
+ }
+ }
+
+done:;
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ int rc;
+
+ /* FIXME: this could be allowed when the Relax control is used...
+ * in that case:
+ *
+ * TODO
+ *
+ * static => dynamic:
+ * entryTtl must be provided; add
+ * entryExpireTimestamp accordingly
+ *
+ * dynamic => static:
+ * entryTtl must be removed; remove
+ * entryTimestamp accordingly
+ *
+ * ... but we need to make sure that there are no subordinate
+ * issues...
+ */
+ rc = is_dynamicObject - was_dynamicObject;
+ if ( rc ) {
+#if 0 /* fix subordinate issues first */
+ if ( get_relax( op ) ) {
+ switch ( rc ) {
+ case -1:
+ /* need to delete entryTtl to have a consistent entry */
+ if ( entryTtl != -1 ) {
+ rs->sr_text = "objectClass modification from dynamicObject to static entry requires entryTtl deletion";
+ rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
+ }
+ break;
+
+ case 1:
+ /* need to add entryTtl to have a consistent entry */
+ if ( entryTtl <= 0 ) {
+ rs->sr_text = "objectClass modification from static entry to dynamicObject requires entryTtl addition";
+ rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
+ }
+ break;
+ }
+
+ } else
+#endif
+ {
+ switch ( rc ) {
+ case -1:
+ rs->sr_text = "objectClass modification cannot turn dynamicObject into static entry";
+ break;
+
+ case 1:
+ rs->sr_text = "objectClass modification cannot turn static entry into dynamicObject";
+ break;
+ }
+ rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ rc = backend_attribute( op, NULL, &op->o_req_ndn,
+ slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
+ if ( rc == LDAP_INSUFFICIENT_ACCESS ) {
+ rs->sr_text = NULL;
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ }
+ }
+ }
+ }
+
+ if ( rs->sr_err == LDAP_SUCCESS && entryTtl != 0 ) {
+ Modifications *tmpmod = NULL, **modp;
+
+ for ( modp = &op->orm_modlist; *modp; modp = &(*modp)->sml_next )
+ ;
+
+ tmpmod = ch_calloc( 1, sizeof( Modifications ) );
+ tmpmod->sml_flags = SLAP_MOD_INTERNAL;
+ tmpmod->sml_type = ad_entryExpireTimestamp->ad_cname;
+ tmpmod->sml_desc = ad_entryExpireTimestamp;
+
+ *modp = tmpmod;
+
+ if ( entryTtl == -1 ) {
+ /* delete entryExpireTimestamp */
+ tmpmod->sml_op = LDAP_MOD_DELETE;
+
+ } else {
+ time_t expire;
+ char tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+ struct berval bv;
+
+ /* keep entryExpireTimestamp consistent
+ * with entryTtl */
+ expire = slap_get_time() + entryTtl;
+ bv.bv_val = tsbuf;
+ bv.bv_len = sizeof( tsbuf );
+ slap_timestamp( &expire, &bv );
+
+ tmpmod->sml_op = LDAP_MOD_REPLACE;
+ value_add_one( &tmpmod->sml_values, &bv );
+ value_add_one( &tmpmod->sml_nvalues, &bv );
+ tmpmod->sml_numvals = 1;
+ }
+ }
+
+ if ( rs->sr_err ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+dds_op_rename( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ dds_info_t *di = on->on_bi.bi_private;
+
+ if ( DDS_OFF( di ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* we don't allow dynamicObjects to have static subordinates */
+ if ( op->orr_nnewSup != NULL ) {
+ Entry *e = NULL;
+ BackendInfo *bi = op->o_bd->bd_info;
+ int is_dynamicObject = 0,
+ rc;
+
+ rs->sr_err = LDAP_SUCCESS;
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rc = be_entry_get_rw( op, &op->o_req_ndn,
+ slap_schema.si_oc_dynamicObject, NULL, 0, &e );
+ if ( rc == LDAP_SUCCESS && e != NULL ) {
+ be_entry_release_r( op, e );
+ e = NULL;
+ is_dynamicObject = 1;
+ }
+
+ rc = be_entry_get_rw( op, op->orr_nnewSup,
+ slap_schema.si_oc_dynamicObject, NULL, 0, &e );
+ if ( rc == LDAP_SUCCESS && e != NULL ) {
+ if ( !is_dynamicObject ) {
+ /* return referral only if "disclose"
+ * is granted on the object */
+ if ( ! access_allowed( op, e,
+ slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL ) )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ send_ldap_result( op, rs );
+
+ } else {
+ send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
+ "static entry cannot have dynamicObject as newSuperior" );
+ }
+ }
+ be_entry_release_r( op, e );
+ }
+ op->o_bd->bd_info = bi;
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ return rs->sr_err;
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+/* entryTtl update for client */
+static int
+dds_response( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ dds_info_t *di = on->on_bi.bi_private;
+ int rc;
+
+ if ( !DDS_OFF( di )
+ && rs->sr_type == REP_SEARCH
+ && attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryTtl ) )
+ {
+ BerVarray vals = NULL;
+ struct lutil_tm tm;
+ struct lutil_timet tt;
+ char ttlbuf[STRLENOF("31557600") + 1];
+ struct berval ttlvalue;
+ time_t ttl;
+ int len;
+
+ /* User already has access to entryTtl, skip ACL checks on
+ * entryExpireTimestamp */
+ rc = backend_attribute( op, NULL, &rs->sr_entry->e_nname,
+ ad_entryExpireTimestamp, &vals, ACL_NONE );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ assert( vals[0].bv_val[vals[0].bv_len] == '\0' );
+ if ( lutil_parsetime( vals[0].bv_val, &tm ) ) {
+ goto done;
+ }
+
+ lutil_tm2time( &tm, &tt );
+ ttl = tt.tt_sec - op->o_time;
+ ttl = (ttl < 0) ? 0 : ttl;
+ assert( ttl <= DDS_RF2589_MAX_TTL );
+
+ len = snprintf( ttlbuf, sizeof(ttlbuf), "%ld", ttl );
+ if ( len < 0 )
+ {
+ goto done;
+ }
+ ttlvalue.bv_val = ttlbuf;
+ ttlvalue.bv_len = len;
+
+ rs_entry2modifiable( op, rs, on );
+
+ if ( attr_delete( &rs->sr_entry->e_attrs,
+ slap_schema.si_ad_entryTtl ) )
+ {
+ goto done;
+ }
+ if ( attr_merge_normalize_one( rs->sr_entry,
+ slap_schema.si_ad_entryTtl,
+ &ttlvalue, op->o_tmpmemctx ) )
+ {
+ goto done;
+ }
+
+done:;
+ ber_bvarray_free_x( vals, op->o_tmpmemctx );
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+slap_parse_refresh(
+ struct berval *in,
+ struct berval *ndn,
+ time_t *ttl,
+ const char **text,
+ void *ctx )
+{
+ int rc = LDAP_SUCCESS;
+ ber_tag_t tag;
+ ber_len_t len = -1;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ struct berval reqdata = BER_BVNULL;
+ int tmp;
+
+ *text = NULL;
+
+ if ( ndn ) {
+ BER_BVZERO( ndn );
+ }
+
+ if ( in == NULL || in->bv_len == 0 ) {
+ *text = "empty request data field in refresh exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ ber_dupbv_x( &reqdata, in, ctx );
+
+ /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+ ber_init2( ber, &reqdata, 0 );
+
+ tag = ber_scanf( ber, "{" /*}*/ );
+
+ if ( tag == LBER_ERROR ) {
+ Log( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
+ "slap_parse_refresh: decoding error.\n" );
+ goto decoding_error;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if ( tag != LDAP_TAG_EXOP_REFRESH_REQ_DN ) {
+ Log( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
+ "slap_parse_refresh: decoding error.\n" );
+ goto decoding_error;
+ }
+
+ if ( ndn ) {
+ struct berval dn;
+
+ tag = ber_scanf( ber, "m", &dn );
+ if ( tag == LBER_ERROR ) {
+ Log( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
+ "slap_parse_refresh: DN parse failed.\n" );
+ goto decoding_error;
+ }
+
+ rc = dnNormalize( 0, NULL, NULL, &dn, ndn, ctx );
+ if ( rc != LDAP_SUCCESS ) {
+ *text = "invalid DN in refresh exop request data";
+ goto done;
+ }
+
+ } else {
+ tag = ber_scanf( ber, "x" /* "m" */ );
+ if ( tag == LBER_DEFAULT ) {
+ goto decoding_error;
+ }
+ }
+
+ tag = ber_peek_tag( ber, &len );
+
+ if ( tag != LDAP_TAG_EXOP_REFRESH_REQ_TTL ) {
+ Log( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
+ "slap_parse_refresh: decoding error.\n" );
+ goto decoding_error;
+ }
+
+ tag = ber_scanf( ber, "i", &tmp );
+ if ( tag == LBER_ERROR ) {
+ Log( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
+ "slap_parse_refresh: TTL parse failed.\n" );
+ goto decoding_error;
+ }
+
+ if ( ttl ) {
+ *ttl = tmp;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+
+ if ( tag != LBER_DEFAULT || len != 0 ) {
+decoding_error:;
+ Log( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
+ "slap_parse_refresh: decoding error, len=%ld\n",
+ (long)len );
+ rc = LDAP_PROTOCOL_ERROR;
+ *text = "data decoding error";
+
+done:;
+ if ( ndn && !BER_BVISNULL( ndn ) ) {
+ slap_sl_free( ndn->bv_val, ctx );
+ BER_BVZERO( ndn );
+ }
+ }
+
+ if ( !BER_BVISNULL( &reqdata ) ) {
+ ber_memfree_x( reqdata.bv_val, ctx );
+ }
+
+ return rc;
+}
+
+static int
+dds_op_extended( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ dds_info_t *di = on->on_bi.bi_private;
+
+ if ( DDS_OFF( di ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( bvmatch( &op->ore_reqoid, &slap_EXOP_REFRESH ) ) {
+ Entry *e = NULL;
+ time_t ttl;
+ BackendDB db = *op->o_bd;
+ SlapReply rs2 = { REP_RESULT };
+ Operation op2 = *op;
+ slap_callback sc = { 0 };
+ Modifications ttlmod = { { 0 } };
+ struct berval ttlvalues[ 2 ];
+ char ttlbuf[STRLENOF("31557600") + 1];
+
+ rs->sr_err = slap_parse_refresh( op->ore_reqdata, NULL, &ttl,
+ &rs->sr_text, NULL );
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ if ( ttl <= 0 || ttl > DDS_RF2589_MAX_TTL ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ rs->sr_text = "invalid time-to-live for dynamicObject";
+ return rs->sr_err;
+ }
+
+ if ( ttl > di->di_max_ttl ) {
+ /* FIXME: I don't understand if this has to be an error,
+ * or an indication that the requested Ttl has been
+ * shortened to di->di_max_ttl >= 1 day */
+ rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
+ rs->sr_text = "time-to-live for dynamicObject exceeds limit";
+ return rs->sr_err;
+ }
+
+ if ( di->di_min_ttl && ttl < di->di_min_ttl ) {
+ ttl = di->di_min_ttl;
+ }
+
+ /* This does not apply to multi-provider case */
+ if ( !( !SLAP_SINGLE_SHADOW( op->o_bd ) || be_isupdate( op ) ) ) {
+ /* we SHOULD return a referral in this case */
+ BerVarray defref = op->o_bd->be_update_refs
+ ? op->o_bd->be_update_refs : default_referral;
+
+ if ( defref != NULL ) {
+ rs->sr_ref = referral_rewrite( op->o_bd->be_update_refs,
+ NULL, NULL, LDAP_SCOPE_DEFAULT );
+ if ( rs->sr_ref ) {
+ rs->sr_flags |= REP_REF_MUSTBEFREED;
+ } else {
+ rs->sr_ref = defref;
+ }
+ rs->sr_err = LDAP_REFERRAL;
+
+ } else {
+ rs->sr_text = "shadow context; no update referral";
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ return rs->sr_err;
+ }
+
+ assert( !BER_BVISNULL( &op->o_req_ndn ) );
+
+
+
+ /* check if exists but not dynamicObject */
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
+ slap_schema.si_oc_dynamicObject, NULL, 0, &e );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
+ NULL, NULL, 0, &e );
+ if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
+ /* return referral only if "disclose"
+ * is granted on the object */
+ if ( ! access_allowed( op, e,
+ slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL ) )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+
+ } else {
+ rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
+ rs->sr_text = "refresh operation only applies to dynamic objects";
+ }
+ be_entry_release_r( op, e );
+
+ } else {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ }
+ return rs->sr_err;
+
+ } else if ( e != NULL ) {
+ be_entry_release_r( op, e );
+ }
+
+ /* we require manage privileges on the entryTtl,
+ * and fake a Relax control */
+ op2.o_tag = LDAP_REQ_MODIFY;
+ op2.o_bd = &db;
+ db.bd_info = (BackendInfo *)on->on_info;
+ op2.o_callback = &sc;
+ sc.sc_response = slap_null_cb;
+ op2.o_relax = SLAP_CONTROL_CRITICAL;
+ op2.orm_modlist = &ttlmod;
+
+ ttlmod.sml_op = LDAP_MOD_REPLACE;
+ ttlmod.sml_flags = SLAP_MOD_MANAGING;
+ ttlmod.sml_desc = slap_schema.si_ad_entryTtl;
+ ttlmod.sml_values = ttlvalues;
+ ttlmod.sml_numvals = 1;
+ ttlvalues[ 0 ].bv_val = ttlbuf;
+ ttlvalues[ 0 ].bv_len = snprintf( ttlbuf, sizeof( ttlbuf ), "%ld", ttl );
+ BER_BVZERO( &ttlvalues[ 1 ] );
+
+ /* the entryExpireTimestamp is added by modify */
+ rs->sr_err = op2.o_bd->be_modify( &op2, &rs2 );
+
+ if ( ttlmod.sml_next != NULL ) {
+ slap_mods_free( ttlmod.sml_next, 1 );
+ }
+
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ int rc;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+
+ ber_init_w_nullc( ber, LBER_USE_DER );
+
+ rc = ber_printf( ber, "{tiN}", LDAP_TAG_EXOP_REFRESH_RES_TTL, (int)ttl );
+
+ if ( rc < 0 ) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+
+ } else {
+ (void)ber_flatten( ber, &rs->sr_rspdata );
+ rs->sr_rspoid = ch_strdup( slap_EXOP_REFRESH.bv_val );
+
+ Log( LDAP_DEBUG_TRACE, LDAP_LEVEL_INFO,
+ "%s REFRESH dn=\"%s\" TTL=%ld\n",
+ op->o_log_prefix, op->o_req_ndn.bv_val, ttl );
+ }
+
+ ber_free_buf( ber );
+ }
+
+ return rs->sr_err;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+enum {
+ DDS_STATE = 1,
+ DDS_MAXTTL,
+ DDS_MINTTL,
+ DDS_DEFAULTTTL,
+ DDS_INTERVAL,
+ DDS_TOLERANCE,
+ DDS_MAXDYNAMICOBJS,
+
+ DDS_LAST
+};
+
+static ConfigDriver dds_cfgen;
+#if 0
+static ConfigLDAPadd dds_ldadd;
+static ConfigCfAdd dds_cfadd;
+#endif
+
+static ConfigTable dds_cfg[] = {
+ { "dds-state", "on|off",
+ 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|DDS_STATE, dds_cfgen,
+ "( OLcfgOvAt:9.1 NAME 'olcDDSstate' "
+ "DESC 'RFC2589 Dynamic directory services state' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )", NULL, NULL },
+ { "dds-max-ttl", "ttl",
+ 2, 2, 0, ARG_MAGIC|DDS_MAXTTL, dds_cfgen,
+ "( OLcfgOvAt:9.2 NAME 'olcDDSmaxTtl' "
+ "DESC 'RFC2589 Dynamic directory services max TTL' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )", NULL, NULL },
+ { "dds-min-ttl", "ttl",
+ 2, 2, 0, ARG_MAGIC|DDS_MINTTL, dds_cfgen,
+ "( OLcfgOvAt:9.3 NAME 'olcDDSminTtl' "
+ "DESC 'RFC2589 Dynamic directory services min TTL' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )", NULL, NULL },
+ { "dds-default-ttl", "ttl",
+ 2, 2, 0, ARG_MAGIC|DDS_DEFAULTTTL, dds_cfgen,
+ "( OLcfgOvAt:9.4 NAME 'olcDDSdefaultTtl' "
+ "DESC 'RFC2589 Dynamic directory services default TTL' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )", NULL, NULL },
+ { "dds-interval", "interval",
+ 2, 2, 0, ARG_MAGIC|DDS_INTERVAL, dds_cfgen,
+ "( OLcfgOvAt:9.5 NAME 'olcDDSinterval' "
+ "DESC 'RFC2589 Dynamic directory services expiration "
+ "task run interval' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )", NULL, NULL },
+ { "dds-tolerance", "ttl",
+ 2, 2, 0, ARG_MAGIC|DDS_TOLERANCE, dds_cfgen,
+ "( OLcfgOvAt:9.6 NAME 'olcDDStolerance' "
+ "DESC 'RFC2589 Dynamic directory services additional "
+ "TTL in expiration scheduling' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )", NULL, NULL },
+ { "dds-max-dynamicObjects", "num",
+ 2, 2, 0, ARG_MAGIC|ARG_INT|DDS_MAXDYNAMICOBJS, dds_cfgen,
+ "( OLcfgOvAt:9.7 NAME 'olcDDSmaxDynamicObjects' "
+ "DESC 'RFC2589 Dynamic directory services max number of dynamic objects' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs dds_ocs[] = {
+ { "( OLcfgOvOc:9.1 "
+ "NAME 'olcDDSConfig' "
+ "DESC 'RFC2589 Dynamic directory services configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( "
+ "olcDDSstate "
+ "$ olcDDSmaxTtl "
+ "$ olcDDSminTtl "
+ "$ olcDDSdefaultTtl "
+ "$ olcDDSinterval "
+ "$ olcDDStolerance "
+ "$ olcDDSmaxDynamicObjects "
+ " ) "
+ ")", Cft_Overlay, dds_cfg, NULL, NULL /* dds_cfadd */ },
+ { NULL, 0, NULL }
+};
+
+#if 0
+static int
+dds_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
+{
+ return LDAP_SUCCESS;
+}
+
+static int
+dds_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
+{
+ return 0;
+}
+#endif
+
+static int
+dds_cfgen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ dds_info_t *di = on->on_bi.bi_private;
+ int rc = 0;
+ unsigned long t;
+
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ char buf[ SLAP_TEXT_BUFLEN ];
+ struct berval bv;
+
+ switch( c->type ) {
+ case DDS_STATE:
+ c->value_int = !DDS_OFF( di );
+ break;
+
+ case DDS_MAXTTL:
+ lutil_unparse_time( buf, sizeof( buf ), di->di_max_ttl );
+ ber_str2bv( buf, 0, 0, &bv );
+ value_add_one( &c->rvalue_vals, &bv );
+ break;
+
+ case DDS_MINTTL:
+ if ( di->di_min_ttl ) {
+ lutil_unparse_time( buf, sizeof( buf ), di->di_min_ttl );
+ ber_str2bv( buf, 0, 0, &bv );
+ value_add_one( &c->rvalue_vals, &bv );
+
+ } else {
+ rc = 1;
+ }
+ break;
+
+ case DDS_DEFAULTTTL:
+ if ( di->di_default_ttl ) {
+ lutil_unparse_time( buf, sizeof( buf ), di->di_default_ttl );
+ ber_str2bv( buf, 0, 0, &bv );
+ value_add_one( &c->rvalue_vals, &bv );
+
+ } else {
+ rc = 1;
+ }
+ break;
+
+ case DDS_INTERVAL:
+ if ( di->di_interval ) {
+ lutil_unparse_time( buf, sizeof( buf ), di->di_interval );
+ ber_str2bv( buf, 0, 0, &bv );
+ value_add_one( &c->rvalue_vals, &bv );
+
+ } else {
+ rc = 1;
+ }
+ break;
+
+ case DDS_TOLERANCE:
+ if ( di->di_tolerance ) {
+ lutil_unparse_time( buf, sizeof( buf ), di->di_tolerance );
+ ber_str2bv( buf, 0, 0, &bv );
+ value_add_one( &c->rvalue_vals, &bv );
+
+ } else {
+ rc = 1;
+ }
+ break;
+
+ case DDS_MAXDYNAMICOBJS:
+ if ( di->di_max_dynamicObjects > 0 ) {
+ c->value_int = di->di_max_dynamicObjects;
+
+ } else {
+ rc = 1;
+ }
+ break;
+
+ default:
+ rc = 1;
+ break;
+ }
+
+ return rc;
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ switch( c->type ) {
+ case DDS_STATE:
+ di->di_flags &= ~DDS_FOFF;
+ break;
+
+ case DDS_MAXTTL:
+ di->di_min_ttl = DDS_RF2589_DEFAULT_TTL;
+ break;
+
+ case DDS_MINTTL:
+ di->di_min_ttl = 0;
+ break;
+
+ case DDS_DEFAULTTTL:
+ di->di_default_ttl = 0;
+ break;
+
+ case DDS_INTERVAL:
+ di->di_interval = 0;
+ break;
+
+ case DDS_TOLERANCE:
+ di->di_tolerance = 0;
+ break;
+
+ case DDS_MAXDYNAMICOBJS:
+ di->di_max_dynamicObjects = 0;
+ break;
+
+ default:
+ rc = 1;
+ break;
+ }
+
+ return rc;
+ }
+
+ switch ( c->type ) {
+ case DDS_STATE:
+ if ( c->value_int ) {
+ di->di_flags &= ~DDS_FOFF;
+
+ } else {
+ di->di_flags |= DDS_FOFF;
+ }
+ break;
+
+ case DDS_MAXTTL:
+ if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "DDS unable to parse dds-max-ttl \"%s\"",
+ c->argv[ 1 ] );
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( t < DDS_RF2589_DEFAULT_TTL || t > DDS_RF2589_MAX_TTL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "DDS invalid dds-max-ttl=%lu; must be between %d and %d",
+ t, DDS_RF2589_DEFAULT_TTL, DDS_RF2589_MAX_TTL );
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ di->di_max_ttl = (time_t)t;
+ break;
+
+ case DDS_MINTTL:
+ if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "DDS unable to parse dds-min-ttl \"%s\"",
+ c->argv[ 1 ] );
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( t > DDS_RF2589_MAX_TTL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "DDS invalid dds-min-ttl=%lu",
+ t );
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( t == 0 ) {
+ di->di_min_ttl = DDS_RF2589_DEFAULT_TTL;
+
+ } else {
+ di->di_min_ttl = (time_t)t;
+ }
+ break;
+
+ case DDS_DEFAULTTTL:
+ if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "DDS unable to parse dds-default-ttl \"%s\"",
+ c->argv[ 1 ] );
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( t > DDS_RF2589_MAX_TTL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "DDS invalid dds-default-ttl=%lu",
+ t );
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( t == 0 ) {
+ di->di_default_ttl = DDS_RF2589_DEFAULT_TTL;
+
+ } else {
+ di->di_default_ttl = (time_t)t;
+ }
+ break;
+
+ case DDS_INTERVAL:
+ if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "DDS unable to parse dds-interval \"%s\"",
+ c->argv[ 1 ] );
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( t <= 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "DDS invalid dds-interval=%lu",
+ t );
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( t < 60 ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
+ "%s: dds-interval=%lu may be too small.\n",
+ c->log, t );
+ }
+
+ di->di_interval = (time_t)t;
+ if ( di->di_expire_task ) {
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, di->di_expire_task ) ) {
+ ldap_pvt_runqueue_stoptask( &slapd_rq, di->di_expire_task );
+ }
+ di->di_expire_task->interval.tv_sec = DDS_INTERVAL( di );
+ ldap_pvt_runqueue_resched( &slapd_rq, di->di_expire_task, 0 );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+ break;
+
+ case DDS_TOLERANCE:
+ if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg),
+ "DDS unable to parse dds-tolerance \"%s\"",
+ c->argv[ 1 ] );
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( t > DDS_RF2589_MAX_TTL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "DDS invalid dds-tolerance=%lu",
+ t );
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+
+ di->di_tolerance = (time_t)t;
+ break;
+
+ case DDS_MAXDYNAMICOBJS:
+ if ( c->value_int < 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "DDS invalid dds-max-dynamicObjects=%d", c->value_int );
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ di->di_max_dynamicObjects = c->value_int;
+ break;
+
+ default:
+ rc = 1;
+ break;
+ }
+
+ return rc;
+}
+
+static int
+dds_db_init(
+ BackendDB *be,
+ ConfigReply *cr)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ dds_info_t *di;
+ BackendInfo *bi = on->on_info->oi_orig;
+
+ if ( SLAP_ISGLOBALOVERLAY( be ) ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "DDS cannot be used as global overlay.\n" );
+ return 1;
+ }
+
+ /* check support for required functions */
+ /* FIXME: some could be provided by other overlays in between */
+ if ( bi->bi_op_add == NULL /* object creation */
+ || bi->bi_op_delete == NULL /* object deletion */
+ || bi->bi_op_modify == NULL /* object refresh */
+ || bi->bi_op_search == NULL /* object expiration */
+ || bi->bi_entry_get_rw == NULL ) /* object type/existence checking */
+ {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "DDS backend \"%s\" does not provide "
+ "required functionality.\n",
+ bi->bi_type );
+ return 1;
+ }
+
+ di = (dds_info_t *)ch_calloc( 1, sizeof( dds_info_t ) );
+ on->on_bi.bi_private = di;
+
+ di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
+ di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
+
+ ldap_pvt_thread_mutex_init( &di->di_mutex );
+
+ SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_DYNAMIC;
+
+ return 0;
+}
+
+/* adds dynamicSubtrees to root DSE */
+static int
+dds_entry_info( void *arg, Entry *e )
+{
+ dds_info_t *di = (dds_info_t *)arg;
+
+ attr_merge( e, slap_schema.si_ad_dynamicSubtrees,
+ di->di_suffix, di->di_nsuffix );
+
+ return 0;
+}
+
+/* callback that counts the returned entries, since the search
+ * does not get to the point in slap_send_search_entries where
+ * the actual count occurs */
+static int
+dds_count_cb( Operation *op, SlapReply *rs )
+{
+ int *nump = (int *)op->o_callback->sc_private;
+
+ switch ( rs->sr_type ) {
+ case REP_SEARCH:
+ (*nump)++;
+ break;
+
+ case REP_SEARCHREF:
+ case REP_RESULT:
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+ return 0;
+}
+
+/* count dynamic objects existing in the database at startup */
+static int
+dds_count( void *ctx, BackendDB *be )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ dds_info_t *di = (dds_info_t *)on->on_bi.bi_private;
+
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+ Operation *op;
+ slap_callback sc = { 0 };
+ SlapReply rs = { REP_RESULT };
+
+ int rc;
+ char *extra = "";
+
+ connection_fake_init2( &conn, &opbuf, ctx, 0 );
+ op = &opbuf.ob_op;
+
+ op->o_tag = LDAP_REQ_SEARCH;
+ memset( &op->oq_search, 0, sizeof( op->oq_search ) );
+
+ op->o_bd = be;
+
+ op->o_req_dn = op->o_bd->be_suffix[ 0 ];
+ op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
+
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+
+ op->ors_scope = LDAP_SCOPE_SUBTREE;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_slimit = SLAP_NO_LIMIT;
+ op->ors_attrs = slap_anlist_no_attrs;
+ op->o_do_not_cache = 1;
+
+ op->ors_filterstr.bv_len = STRLENOF( "(objectClass=" ")" )
+ + slap_schema.si_oc_dynamicObject->soc_cname.bv_len;
+ op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
+ snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
+ "(objectClass=%s)",
+ slap_schema.si_oc_dynamicObject->soc_cname.bv_val );
+
+ op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
+ if ( op->ors_filter == NULL ) {
+ rs.sr_err = LDAP_OTHER;
+ goto done_search;
+ }
+
+ op->o_callback = &sc;
+ sc.sc_response = dds_count_cb;
+ sc.sc_private = &di->di_num_dynamicObjects;
+ di->di_num_dynamicObjects = 0;
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ (void)op->o_bd->bd_info->bi_op_search( op, &rs );
+ op->o_bd->bd_info = (BackendInfo *)on;
+
+done_search:;
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ filter_free_x( op, op->ors_filter, 1 );
+
+ rc = rs.sr_err;
+ switch ( rs.sr_err ) {
+ case LDAP_SUCCESS:
+ Log( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
+ "DDS non-expired=%d\n",
+ di->di_num_dynamicObjects );
+ break;
+
+ case LDAP_NO_SUCH_OBJECT:
+ /* (ITS#5267) database not created yet? */
+ rs.sr_err = LDAP_SUCCESS;
+ extra = " (ignored)";
+ /* fallthru */
+
+ default:
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "DDS non-expired objects lookup failed err=%d%s\n",
+ rc, extra );
+ break;
+ }
+
+ return rs.sr_err;
+}
+
+static int
+dds_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ dds_info_t *di = on->on_bi.bi_private;
+ int rc = 0;
+ void *thrctx = ldap_pvt_thread_pool_context();
+
+ if ( slapMode & SLAP_TOOL_MODE )
+ return 0;
+
+ if ( DDS_OFF( di ) ) {
+ goto done;
+ }
+
+ if ( SLAP_SINGLE_SHADOW( be ) ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "DDS incompatible with shadow database \"%s\".\n",
+ be->be_suffix[ 0 ].bv_val );
+ return 1;
+ }
+
+ if ( di->di_max_ttl == 0 ) {
+ di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
+ }
+
+ if ( di->di_min_ttl == 0 ) {
+ di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
+ }
+
+ di->di_suffix = be->be_suffix;
+ di->di_nsuffix = be->be_nsuffix;
+
+ /* count the dynamic objects first */
+ rc = dds_count( thrctx, be );
+ if ( rc != LDAP_SUCCESS ) {
+ rc = 1;
+ goto done;
+ }
+
+ /* start expire task */
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ di->di_expire_task = ldap_pvt_runqueue_insert( &slapd_rq,
+ DDS_INTERVAL( di ),
+ dds_expire_fn, di, "dds_expire_fn",
+ be->be_suffix[ 0 ].bv_val );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+
+ /* register dinamicSubtrees root DSE info support */
+ rc = entry_info_register( dds_entry_info, (void *)di );
+
+done:;
+
+ return rc;
+}
+
+static int
+dds_db_close(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ dds_info_t *di = on->on_bi.bi_private;
+
+ /* stop expire task */
+ if ( di && di->di_expire_task ) {
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, di->di_expire_task ) ) {
+ ldap_pvt_runqueue_stoptask( &slapd_rq, di->di_expire_task );
+ }
+ ldap_pvt_runqueue_remove( &slapd_rq, di->di_expire_task );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ di->di_expire_task = NULL;
+ }
+
+ (void)entry_info_unregister( dds_entry_info, (void *)di );
+
+ return 0;
+}
+
+static int
+dds_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ dds_info_t *di = on->on_bi.bi_private;
+
+ if ( di != NULL ) {
+ ldap_pvt_thread_mutex_destroy( &di->di_mutex );
+
+ free( di );
+ }
+
+ return 0;
+}
+
+static int
+slap_exop_refresh(
+ Operation *op,
+ SlapReply *rs )
+{
+ BackendDB *bd = op->o_bd;
+
+ rs->sr_err = slap_parse_refresh( op->ore_reqdata, &op->o_req_ndn, NULL,
+ &rs->sr_text, op->o_tmpmemctx );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ return rs->sr_err;
+ }
+
+ Log( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
+ "%s REFRESH dn=\"%s\"\n",
+ op->o_log_prefix, op->o_req_ndn.bv_val );
+ op->o_req_dn = op->o_req_ndn;
+
+ op->o_bd = select_backend( &op->o_req_ndn, 0 );
+ if ( op->o_bd == NULL ) {
+ send_ldap_error( op, rs, LDAP_NO_SUCH_OBJECT,
+ "no global superior knowledge" );
+ goto done;
+ }
+
+ if ( !SLAP_DYNAMIC( op->o_bd ) ) {
+ send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
+ "backend does not support dynamic directory services" );
+ goto done;
+ }
+
+ rs->sr_err = backend_check_restrictions( op, rs,
+ (struct berval *)&slap_EXOP_REFRESH );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ if ( op->o_bd->be_extended == NULL ) {
+ send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
+ "backend does not support extended operations" );
+ goto done;
+ }
+
+ op->o_bd->be_extended( op, rs );
+
+done:;
+ if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
+ op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &op->o_req_ndn );
+ BER_BVZERO( &op->o_req_dn );
+ }
+ op->o_bd = bd;
+
+ return rs->sr_err;
+}
+
+static slap_overinst dds;
+
+static int do_not_load_exop;
+static int do_not_replace_exop;
+static int do_not_load_schema;
+
+#if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
+static
+#endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
+int
+dds_initialize()
+{
+ int rc = 0;
+ int i, code;
+
+ /* Make sure we don't exceed the bits reserved for userland */
+ config_check_userland( DDS_LAST );
+
+ if ( !do_not_load_schema ) {
+ static struct {
+ char *desc;
+ slap_mask_t flags;
+ AttributeDescription **ad;
+ } s_at[] = {
+ { "( 1.3.6.1.4.1.4203.666.1.57 "
+ "NAME ( 'entryExpireTimestamp' ) "
+ "DESC 'RFC2589 OpenLDAP extension: expire time of a dynamic object, "
+ "computed as now + entryTtl' "
+ "EQUALITY generalizedTimeMatch "
+ "ORDERING generalizedTimeOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ SLAP_AT_HIDE,
+ &ad_entryExpireTimestamp },
+ { NULL }
+ };
+
+ for ( i = 0; s_at[ i ].desc != NULL; i++ ) {
+ code = register_at( s_at[ i ].desc, s_at[ i ].ad, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "dds_initialize: register_at failed\n" );
+ return code;
+ }
+ (*s_at[ i ].ad)->ad_type->sat_flags |= SLAP_AT_HIDE;
+ }
+ }
+
+ if ( !do_not_load_exop ) {
+ rc = load_extop2( (struct berval *)&slap_EXOP_REFRESH,
+ SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, slap_exop_refresh,
+ !do_not_replace_exop );
+ if ( rc != LDAP_SUCCESS ) {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "DDS unable to register refresh exop: %d.\n",
+ rc );
+ return rc;
+ }
+ }
+
+ dds.on_bi.bi_type = "dds";
+
+ dds.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ dds.on_bi.bi_db_init = dds_db_init;
+ dds.on_bi.bi_db_open = dds_db_open;
+ dds.on_bi.bi_db_close = dds_db_close;
+ dds.on_bi.bi_db_destroy = dds_db_destroy;
+
+ dds.on_bi.bi_op_add = dds_op_add;
+ dds.on_bi.bi_op_delete = dds_op_delete;
+ dds.on_bi.bi_op_modify = dds_op_modify;
+ dds.on_bi.bi_op_modrdn = dds_op_rename;
+ dds.on_bi.bi_extended = dds_op_extended;
+ dds.on_response = dds_response;
+
+ dds.on_bi.bi_cf_ocs = dds_ocs;
+
+ rc = config_register_schema( dds_cfg, dds_ocs );
+ if ( rc ) {
+ return rc;
+ }
+
+ return overlay_register( &dds );
+}
+
+#if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ int i;
+
+ for ( i = 0; i < argc; i++ ) {
+ char *arg = argv[ i ];
+ int no = 0;
+
+ if ( strncasecmp( arg, "no-", STRLENOF( "no-" ) ) == 0 ) {
+ arg += STRLENOF( "no-" );
+ no = 1;
+ }
+
+ if ( strcasecmp( arg, "exop" ) == 0 ) {
+ do_not_load_exop = no;
+
+ } else if ( strcasecmp( arg, "replace" ) == 0 ) {
+ do_not_replace_exop = no;
+
+ } else if ( strcasecmp( arg, "schema" ) == 0 ) {
+ do_not_load_schema = no;
+
+ } else {
+ Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+ "DDS unknown module arg[#%d]=\"%s\".\n",
+ i, argv[ i ] );
+ return 1;
+ }
+ }
+
+ return dds_initialize();
+}
+#endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
+
+#endif /* defined(SLAPD_OVER_DDS) */
diff --git a/servers/slapd/overlays/deref.c b/servers/slapd/overlays/deref.c
new file mode 100644
index 0000000..89dc227
--- /dev/null
+++ b/servers/slapd/overlays/deref.c
@@ -0,0 +1,586 @@
+/* deref.c - dereference overlay */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2008 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati
+ * for inclusion in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_DEREF
+
+#include <stdio.h>
+
+#include "ac/string.h"
+#include "ac/socket.h"
+
+#include "slap.h"
+#include "slap-config.h"
+
+#include "lutil.h"
+
+/*
+ * 1. Specification
+ *
+ * 1.1. Request
+ *
+ * controlValue ::= SEQUENCE OF derefSpec DerefSpec
+ *
+ * DerefSpec ::= SEQUENCE {
+ * derefAttr attributeDescription, ; DN-valued
+ * attributes AttributeList }
+ *
+ * AttributeList ::= SEQUENCE OF attr AttributeDescription
+ *
+ * derefAttr MUST be unique within controlValue
+ *
+ *
+ * 1.2. Response
+ *
+ * controlValue ::= SEQUENCE OF DerefRes
+ *
+ * From RFC 4511:
+ * PartialAttribute ::= SEQUENCE {
+ * type AttributeDescription,
+ * vals SET OF value AttributeValue }
+ *
+ * PartialAttributeList ::= SEQUENCE OF
+ * partialAttribute PartialAttribute
+ *
+ * DerefRes ::= SEQUENCE {
+ * derefAttr AttributeDescription,
+ * derefVal LDAPDN,
+ * attrVals [0] PartialAttributeList OPTIONAL }
+ *
+ * If vals is empty, partialAttribute is omitted.
+ * If all vals in attrVals are empty, attrVals is omitted.
+ *
+ * 2. Examples
+ *
+ * 2.1. Example
+ *
+ * 2.1.1. Request
+ *
+ * { { member, { GUID, SID } }, { memberOf, { GUID, SID } } }
+ *
+ * 2.1.2. Response
+ *
+ * { { memberOf, "cn=abartlet,cn=users,dc=abartlet,dc=net",
+ * { { GUID, [ "0bc11d00-e431-40a0-8767-344a320142fa" ] },
+ * { SID, [ "S-1-2-3-2345" ] } } },
+ * { memberOf, "cn=ando,cn=users,dc=sys-net,dc=it",
+ * { { GUID, [ "0bc11d00-e431-40a0-8767-344a320142fb" ] },
+ * { SID, [ "S-1-2-3-2346" ] } } } }
+ *
+ * 2.2. Example
+ *
+ * 2.2.1. Request
+ *
+ * { { member, { cn, uid, drink } } }
+ *
+ * 2.2.2. Response
+ *
+ * { { member, "cn=ando,cn=users,dc=sys-net,dc=it",
+ * { { cn, [ "ando", "Pierangelo Masarati" ] },
+ * { uid, [ "ando" ] } } },
+ * { member, "dc=sys-net,dc=it" } }
+ *
+ *
+ * 3. Security considerations
+ *
+ * The control result must not disclose information the client's
+ * identity could not have accessed directly by performing the related
+ * search operations. The presence of a derefVal in the control
+ * response does not imply neither the existence of nor any access
+ * privilege to the corresponding entry. It is merely a consequence
+ * of the read access the client's identity has on the corresponding
+ * attribute's value.
+ */
+
+#define o_deref o_ctrlflag[deref_cid]
+#define o_ctrlderef o_controls[deref_cid]
+
+typedef struct DerefSpec {
+ AttributeDescription *ds_derefAttr;
+ AttributeDescription **ds_attributes;
+ int ds_nattrs;
+ struct DerefSpec *ds_next;
+} DerefSpec;
+
+typedef struct DerefVal {
+ struct berval dv_derefSpecVal;
+ BerVarray *dv_attrVals;
+} DerefVal;
+
+typedef struct DerefRes {
+ DerefSpec dr_spec;
+ DerefVal *dr_vals;
+ struct DerefRes *dr_next;
+} DerefRes;
+
+typedef struct deref_cb_t {
+ slap_overinst *dc_on;
+ DerefSpec *dc_ds;
+} deref_cb_t;
+
+static int deref_cid;
+static slap_overinst deref;
+static int ov_count;
+
+static int
+deref_parseCtrl (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ ber_tag_t tag;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_len_t len;
+ char *last;
+ DerefSpec *dshead = NULL, **dsp = &dshead;
+ BerVarray attributes = NULL;
+
+ if ( op->o_deref != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "Dereference control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "Dereference control value is absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "Dereference control value is empty";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ ber_init2( ber, &ctrl->ldctl_value, 0 );
+
+ for ( tag = ber_first_element( ber, &len, &last );
+ tag != LBER_DEFAULT;
+ tag = ber_next_element( ber, &len, last ) )
+ {
+ struct berval derefAttr;
+ DerefSpec *ds, *dstmp;
+ const char *text;
+ int rc;
+ ber_len_t cnt = sizeof(struct berval);
+ ber_len_t off = 0;
+
+ if ( ber_scanf( ber, "{m{M}}", &derefAttr, &attributes, &cnt, off ) == LBER_ERROR
+ || !cnt )
+ {
+ rs->sr_text = "Dereference control: derefSpec decoding error";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ ds = (DerefSpec *)op->o_tmpcalloc( 1,
+ sizeof(DerefSpec) + sizeof(AttributeDescription *)*(cnt + 1),
+ op->o_tmpmemctx );
+ ds->ds_attributes = (AttributeDescription **)&ds[ 1 ];
+ ds->ds_nattrs = cnt;
+
+ rc = slap_bv2ad( &derefAttr, &ds->ds_derefAttr, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_text = "Dereference control: derefAttr decoding error";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ for ( dstmp = dshead; dstmp && dstmp != ds; dstmp = dstmp->ds_next ) {
+ if ( dstmp->ds_derefAttr == ds->ds_derefAttr ) {
+ rs->sr_text = "Dereference control: derefAttr must be unique within control";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+ }
+
+ if ( !( ds->ds_derefAttr->ad_type->sat_syntax->ssyn_flags & SLAP_SYNTAX_DN )) {
+ if ( ctrl->ldctl_iscritical ) {
+ rs->sr_text = "Dereference control: derefAttr syntax not distinguishedName";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+ goto justcleanup;
+ }
+
+ for ( cnt = 0; !BER_BVISNULL( &attributes[ cnt ] ); cnt++ ) {
+ rc = slap_bv2ad( &attributes[ cnt ], &ds->ds_attributes[ cnt ], &text );
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_text = "Dereference control: attribute decoding error";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+ }
+
+ ber_memfree_x( attributes, op->o_tmpmemctx );
+ attributes = NULL;
+
+ *dsp = ds;
+ dsp = &ds->ds_next;
+ }
+
+ op->o_ctrlderef = (void *)dshead;
+
+ op->o_deref = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ rs->sr_err = LDAP_SUCCESS;
+
+done:;
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+justcleanup:;
+ for ( ; dshead; ) {
+ DerefSpec *dsnext = dshead->ds_next;
+ op->o_tmpfree( dshead, op->o_tmpmemctx );
+ dshead = dsnext;
+ }
+ }
+
+ if ( attributes != NULL ) {
+ ber_memfree_x( attributes, op->o_tmpmemctx );
+ }
+
+ return rs->sr_err;
+}
+
+static int
+deref_cleanup( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_RESULT || rs->sr_err == SLAPD_ABANDON ) {
+ op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
+ op->o_callback = NULL;
+
+ op->o_tmpfree( op->o_ctrlderef, op->o_tmpmemctx );
+ op->o_ctrlderef = NULL;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+deref_response( Operation *op, SlapReply *rs )
+{
+ int rc = SLAP_CB_CONTINUE;
+
+ if ( rs->sr_type == REP_SEARCH ) {
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *) &berbuf;
+ deref_cb_t *dc = (deref_cb_t *)op->o_callback->sc_private;
+ DerefSpec *ds;
+ DerefRes *dr, *drhead = NULL, **drp = &drhead;
+ struct berval bv = BER_BVNULL;
+ int nDerefRes = 0, nDerefVals = 0, nAttrs = 0, nVals = 0;
+ struct berval ctrlval;
+ LDAPControl *ctrl, *ctrlsp[2];
+ AccessControlState acl_state = ACL_STATE_INIT;
+ static char dummy = '\0';
+ Entry *ebase;
+ int i;
+
+ rc = overlay_entry_get_ov( op, &rs->sr_entry->e_nname, NULL, NULL, 0, &ebase, dc->dc_on );
+ if ( rc != LDAP_SUCCESS || ebase == NULL ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ for ( ds = dc->dc_ds; ds; ds = ds->ds_next ) {
+ Attribute *a = attr_find( ebase->e_attrs, ds->ds_derefAttr );
+
+ if ( a != NULL ) {
+ DerefVal *dv;
+ BerVarray *bva;
+
+ if ( !access_allowed( op, rs->sr_entry, a->a_desc,
+ NULL, ACL_READ, &acl_state ) )
+ {
+ continue;
+ }
+
+ dr = op->o_tmpcalloc( 1,
+ sizeof( DerefRes ) + ( sizeof( DerefVal ) + sizeof( BerVarray * ) * ds->ds_nattrs ) * ( a->a_numvals + 1 ),
+ op->o_tmpmemctx );
+ dr->dr_spec = *ds;
+ dv = dr->dr_vals = (DerefVal *)&dr[ 1 ];
+ bva = (BerVarray *)&dv[ a->a_numvals + 1 ];
+
+ bv.bv_len += ds->ds_derefAttr->ad_cname.bv_len;
+ nAttrs++;
+ nDerefRes++;
+
+ for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
+ Entry *e = NULL;
+
+ dv[ i ].dv_attrVals = bva;
+ bva += ds->ds_nattrs;
+
+
+ if ( !access_allowed( op, rs->sr_entry, a->a_desc,
+ &a->a_nvals[ i ], ACL_READ, &acl_state ) )
+ {
+ dv[ i ].dv_derefSpecVal.bv_val = &dummy;
+ continue;
+ }
+
+ ber_dupbv_x( &dv[ i ].dv_derefSpecVal, &a->a_vals[ i ], op->o_tmpmemctx );
+ bv.bv_len += dv[ i ].dv_derefSpecVal.bv_len;
+ nVals++;
+ nDerefVals++;
+
+ rc = overlay_entry_get_ov( op, &a->a_nvals[ i ], NULL, NULL, 0, &e, dc->dc_on );
+ if ( rc == LDAP_SUCCESS && e != NULL ) {
+ int j;
+
+ if ( access_allowed( op, e, slap_schema.si_ad_entry,
+ NULL, ACL_READ, NULL ) )
+ {
+ for ( j = 0; j < ds->ds_nattrs; j++ ) {
+ Attribute *aa;
+
+ if ( !access_allowed( op, e, ds->ds_attributes[ j ], NULL,
+ ACL_READ, &acl_state ) )
+ {
+ continue;
+ }
+
+ aa = attr_find( e->e_attrs, ds->ds_attributes[ j ] );
+ if ( aa != NULL ) {
+ unsigned k, h, last = aa->a_numvals;
+
+ ber_bvarray_dup_x( &dv[ i ].dv_attrVals[ j ],
+ aa->a_vals, op->o_tmpmemctx );
+
+ bv.bv_len += ds->ds_attributes[ j ]->ad_cname.bv_len;
+
+ for ( k = 0, h = 0; k < aa->a_numvals; k++ ) {
+ if ( !access_allowed( op, e,
+ aa->a_desc,
+ &aa->a_nvals[ k ],
+ ACL_READ, &acl_state ) )
+ {
+ op->o_tmpfree( dv[ i ].dv_attrVals[ j ][ h ].bv_val,
+ op->o_tmpmemctx );
+ dv[ i ].dv_attrVals[ j ][ h ] = dv[ i ].dv_attrVals[ j ][ --last ];
+ BER_BVZERO( &dv[ i ].dv_attrVals[ j ][ last ] );
+ continue;
+ }
+ bv.bv_len += dv[ i ].dv_attrVals[ j ][ h ].bv_len;
+ nVals++;
+ h++;
+ }
+ nAttrs++;
+ }
+ }
+ }
+
+ overlay_entry_release_ov( op, e, 0, dc->dc_on );
+ }
+ }
+
+ *drp = dr;
+ drp = &dr->dr_next;
+ }
+ }
+ overlay_entry_release_ov( op, ebase, 0, dc->dc_on );
+
+ if ( drhead == NULL ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* cook the control value */
+ bv.bv_len += nVals * sizeof(struct berval)
+ + nAttrs * sizeof(struct berval)
+ + nDerefVals * sizeof(DerefVal)
+ + nDerefRes * sizeof(DerefRes);
+ bv.bv_val = op->o_tmpalloc( bv.bv_len, op->o_tmpmemctx );
+
+ ber_init2( ber, &bv, LBER_USE_DER );
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+
+ rc = ber_printf( ber, "{" /*}*/ );
+ for ( dr = drhead; dr != NULL; dr = dr->dr_next ) {
+ for ( i = 0; !BER_BVISNULL( &dr->dr_vals[ i ].dv_derefSpecVal ); i++ ) {
+ int j, first = 1;
+
+ if ( dr->dr_vals[ i ].dv_derefSpecVal.bv_val == &dummy ) {
+ continue;
+ }
+
+ rc = ber_printf( ber, "{OO" /*}*/,
+ &dr->dr_spec.ds_derefAttr->ad_cname,
+ &dr->dr_vals[ i ].dv_derefSpecVal );
+ op->o_tmpfree( dr->dr_vals[ i ].dv_derefSpecVal.bv_val, op->o_tmpmemctx );
+ for ( j = 0; j < dr->dr_spec.ds_nattrs; j++ ) {
+ if ( dr->dr_vals[ i ].dv_attrVals[ j ] != NULL ) {
+ if ( first ) {
+ rc = ber_printf( ber, "t{" /*}*/,
+ (LBER_CONSTRUCTED|LBER_CLASS_CONTEXT) );
+ first = 0;
+ }
+ rc = ber_printf( ber, "{O[W]}",
+ &dr->dr_spec.ds_attributes[ j ]->ad_cname,
+ dr->dr_vals[ i ].dv_attrVals[ j ] );
+ op->o_tmpfree( dr->dr_vals[ i ].dv_attrVals[ j ],
+ op->o_tmpmemctx );
+ }
+ }
+ if ( !first ) {
+ rc = ber_printf( ber, /*{{*/ "}N}" );
+ } else {
+ rc = ber_printf( ber, /*{*/ "}" );
+ }
+ }
+ }
+ rc = ber_printf( ber, /*{*/ "}" );
+ if ( ber_flatten2( ber, &ctrlval, 0 ) == -1 ) {
+ if ( op->o_deref == SLAP_CONTROL_CRITICAL ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+
+ } else {
+ rc = SLAP_CB_CONTINUE;
+ }
+ goto cleanup;
+ }
+
+ ctrl = op->o_tmpcalloc( 1,
+ sizeof( LDAPControl ) + ctrlval.bv_len + 1,
+ op->o_tmpmemctx );
+ ctrl->ldctl_value.bv_val = (char *)&ctrl[ 1 ];
+ ctrl->ldctl_oid = LDAP_CONTROL_X_DEREF;
+ ctrl->ldctl_iscritical = 0;
+ ctrl->ldctl_value.bv_len = ctrlval.bv_len;
+ AC_MEMCPY( ctrl->ldctl_value.bv_val, ctrlval.bv_val, ctrlval.bv_len );
+ ctrl->ldctl_value.bv_val[ ctrl->ldctl_value.bv_len ] = '\0';
+
+ ber_free_buf( ber );
+
+ ctrlsp[0] = ctrl;
+ ctrlsp[1] = NULL;
+ slap_add_ctrls( op, rs, ctrlsp );
+
+ rc = SLAP_CB_CONTINUE;
+
+cleanup:;
+ /* release all */
+ for ( ; drhead != NULL; ) {
+ DerefRes *drnext = drhead->dr_next;
+ op->o_tmpfree( drhead, op->o_tmpmemctx );
+ drhead = drnext;
+ }
+
+ } else if ( rs->sr_type == REP_RESULT ) {
+ rc = deref_cleanup( op, rs );
+ }
+
+ return rc;
+}
+
+static int
+deref_op_search( Operation *op, SlapReply *rs )
+{
+ if ( op->o_deref ) {
+ slap_callback *sc;
+ deref_cb_t *dc;
+
+ sc = op->o_tmpcalloc( 1, sizeof( slap_callback ) + sizeof( deref_cb_t ), op->o_tmpmemctx );
+
+ dc = (deref_cb_t *)&sc[ 1 ];
+ dc->dc_on = (slap_overinst *)op->o_bd->bd_info;
+ dc->dc_ds = (DerefSpec *)op->o_ctrlderef;
+
+ sc->sc_response = deref_response;
+ sc->sc_cleanup = deref_cleanup;
+ sc->sc_private = (void *)dc;
+
+ sc->sc_next = op->o_callback->sc_next;
+ op->o_callback->sc_next = sc;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+deref_db_init( BackendDB *be, ConfigReply *cr)
+{
+ if ( ov_count == 0 ) {
+ int rc;
+
+ rc = register_supported_control2( LDAP_CONTROL_X_DEREF,
+ SLAP_CTRL_SEARCH,
+ NULL,
+ deref_parseCtrl,
+ 1, /* replace */
+ &deref_cid );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "deref_init: Failed to register control (%d)\n",
+ rc );
+ return rc;
+ }
+ }
+ ov_count++;
+ return LDAP_SUCCESS;
+}
+
+static int
+deref_db_open( BackendDB *be, ConfigReply *cr)
+{
+ return overlay_register_control( be, LDAP_CONTROL_X_DEREF );
+}
+
+#ifdef SLAP_CONFIG_DELETE
+static int
+deref_db_destroy( BackendDB *be, ConfigReply *cr)
+{
+ ov_count--;
+ overlay_unregister_control( be, LDAP_CONTROL_X_DEREF );
+ if ( ov_count == 0 ) {
+ unregister_supported_control( LDAP_CONTROL_X_DEREF );
+ }
+ return 0;
+}
+#endif /* SLAP_CONFIG_DELETE */
+
+int
+deref_initialize(void)
+{
+ deref.on_bi.bi_type = "deref";
+ deref.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ deref.on_bi.bi_db_init = deref_db_init;
+ deref.on_bi.bi_db_open = deref_db_open;
+#ifdef SLAP_CONFIG_DELETE
+ deref.on_bi.bi_db_destroy = deref_db_destroy;
+#endif /* SLAP_CONFIG_DELETE */
+ deref.on_bi.bi_op_search = deref_op_search;
+
+ return overlay_register( &deref );
+}
+
+#if SLAPD_OVER_DEREF == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return deref_initialize();
+}
+#endif /* SLAPD_OVER_DEREF == SLAPD_MOD_DYNAMIC */
+
+#endif /* SLAPD_OVER_DEREF */
diff --git a/servers/slapd/overlays/dyngroup.c b/servers/slapd/overlays/dyngroup.c
new file mode 100644
index 0000000..5d890d6
--- /dev/null
+++ b/servers/slapd/overlays/dyngroup.c
@@ -0,0 +1,234 @@
+/* dyngroup.c - Demonstration of overlay code */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Copyright 2003 by Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_DYNGROUP
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "lutil.h"
+#include "slap.h"
+#include "slap-config.h"
+
+/* This overlay extends the Compare operation to detect members of a
+ * dynamic group. It has no effect on any other operations. It must
+ * be configured with a pair of attributes to trigger on, e.g.
+ * attrpair member memberURL
+ * will cause compares on "member" to trigger a compare on "memberURL".
+ */
+
+typedef struct adpair {
+ struct adpair *ap_next;
+ AttributeDescription *ap_mem;
+ AttributeDescription *ap_uri;
+} adpair;
+
+static int dgroup_cf( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ int rc = 1;
+
+ switch( c->op ) {
+ case SLAP_CONFIG_EMIT:
+ {
+ adpair *ap;
+ for ( ap = on->on_bi.bi_private; ap; ap = ap->ap_next ) {
+ struct berval bv;
+ char *ptr;
+ bv.bv_len = ap->ap_mem->ad_cname.bv_len + 1 +
+ ap->ap_uri->ad_cname.bv_len;
+ bv.bv_val = ch_malloc( bv.bv_len + 1 );
+ ptr = lutil_strcopy( bv.bv_val, ap->ap_mem->ad_cname.bv_val );
+ *ptr++ = ' ';
+ strcpy( ptr, ap->ap_uri->ad_cname.bv_val );
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ rc = 0;
+ }
+ }
+ break;
+ case LDAP_MOD_DELETE:
+ if ( c->valx == -1 ) {
+ adpair *ap;
+ while (( ap = on->on_bi.bi_private )) {
+ on->on_bi.bi_private = ap->ap_next;
+ ch_free( ap );
+ }
+ } else {
+ adpair **app, *ap;
+ int i;
+ app = (adpair **)&on->on_bi.bi_private;
+ for (i=0; i<=c->valx; i++, app = &ap->ap_next) {
+ ap = *app;
+ }
+ *app = ap->ap_next;
+ ch_free( ap );
+ }
+ rc = 0;
+ break;
+ case SLAP_CONFIG_ADD:
+ case LDAP_MOD_ADD:
+ {
+ adpair ap = { NULL, NULL, NULL }, **app, *a2;
+ const char *text;
+ if ( slap_str2ad( c->argv[1], &ap.ap_mem, &text ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s attribute description unknown: \"%s\"",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ if ( slap_str2ad( c->argv[2], &ap.ap_uri, &text ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s attribute description unknown: \"%s\"",
+ c->argv[0], c->argv[2] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ /* The on->on_bi.bi_private pointer can be used for
+ * anything this instance of the overlay needs.
+ */
+ a2 = ch_malloc( sizeof(adpair) );
+
+ for ( app = &on->on_bi.bi_private; *app; app = &(*app)->ap_next )
+ /* Get to the end */ ;
+
+ a2->ap_mem = ap.ap_mem;
+ a2->ap_uri = ap.ap_uri;
+ a2->ap_next = *app;
+ *app = a2;
+ rc = 0;
+ }
+ }
+ return rc;
+}
+
+static ConfigTable dgroupcfg[] = {
+ { "attrpair", "member-attribute> <URL-attribute", 3, 3, 0,
+ ARG_MAGIC, dgroup_cf,
+ "( OLcfgOvAt:17.1 NAME ( 'olcDynGroupAttrPair' 'olcDGAttrPair' ) "
+ "EQUALITY caseIgnoreMatch "
+ "DESC 'Member and MemberURL attribute pair' "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs dgroupocs[] = {
+ { "( OLcfgOvOc:17.1 "
+ "NAME ( 'olcDynGroupConfig' 'olcDGConfig' ) "
+ "DESC 'Dynamic Group configuration' "
+ "SUP olcOverlayConfig "
+ "MAY olcDynGroupAttrPair)",
+ Cft_Overlay, dgroupcfg },
+ { NULL, 0, NULL }
+};
+
+static int
+dyngroup_response( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ adpair *ap = on->on_bi.bi_private;
+
+ /* If we've been configured and the current response is
+ * what we're looking for...
+ */
+ if ( ap && op->o_tag == LDAP_REQ_COMPARE &&
+ rs->sr_err == LDAP_NO_SUCH_ATTRIBUTE ) {
+
+ for (;ap;ap=ap->ap_next) {
+ if ( op->oq_compare.rs_ava->aa_desc == ap->ap_mem ) {
+ /* This compare is for one of the attributes we're
+ * interested in. We'll use slapd's existing dyngroup
+ * evaluator to get the answer we want.
+ */
+ int cache = op->o_do_not_cache;
+
+ op->o_do_not_cache = 1;
+ rs->sr_err = backend_group( op, NULL, &op->o_req_ndn,
+ &op->oq_compare.rs_ava->aa_value, NULL, ap->ap_uri );
+ op->o_do_not_cache = cache;
+ switch ( rs->sr_err ) {
+ case LDAP_SUCCESS:
+ rs->sr_err = LDAP_COMPARE_TRUE;
+ break;
+
+ case LDAP_NO_SUCH_OBJECT:
+ rs->sr_err = LDAP_COMPARE_FALSE;
+ break;
+ }
+ break;
+ }
+ }
+ }
+ /* Default is to just fall through to the normal processing */
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+dyngroup_destroy(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ adpair *ap, *a2;
+
+ for ( ap = on->on_bi.bi_private; ap; ap = a2 ) {
+ a2 = ap->ap_next;
+ ch_free( ap );
+ }
+ return 0;
+}
+
+static slap_overinst dyngroup;
+
+/* This overlay is set up for dynamic loading via moduleload. For static
+ * configuration, you'll need to arrange for the slap_overinst to be
+ * initialized and registered by some other function inside slapd.
+ */
+
+int dyngroup_initialize() {
+ int code;
+
+ dyngroup.on_bi.bi_type = "dyngroup";
+ dyngroup.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ dyngroup.on_bi.bi_db_destroy = dyngroup_destroy;
+ dyngroup.on_response = dyngroup_response;
+
+ dyngroup.on_bi.bi_cf_ocs = dgroupocs;
+ code = config_register_schema( dgroupcfg, dgroupocs );
+ if ( code ) return code;
+
+ return overlay_register( &dyngroup );
+}
+
+#if SLAPD_OVER_DYNGROUP == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return dyngroup_initialize();
+}
+#endif
+
+#endif /* defined(SLAPD_OVER_DYNGROUP) */
diff --git a/servers/slapd/overlays/dynlist.c b/servers/slapd/overlays/dynlist.c
new file mode 100644
index 0000000..3490cfb
--- /dev/null
+++ b/servers/slapd/overlays/dynlist.c
@@ -0,0 +1,2706 @@
+/* dynlist.c - dynamic list overlay */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2004-2005 Pierangelo Masarati.
+ * Portions Copyright 2008 Emmanuel Dreyfus.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati
+ * for SysNet s.n.c., for inclusion in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_DYNLIST
+
+#if SLAPD_OVER_DYNGROUP != SLAPD_MOD_STATIC
+#define TAKEOVER_DYNGROUP
+#endif
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+static AttributeDescription *ad_dgIdentity, *ad_dgAuthz;
+static AttributeDescription *ad_memberOf;
+
+typedef struct dynlist_map_t {
+ AttributeDescription *dlm_member_ad;
+ AttributeDescription *dlm_mapped_ad;
+ AttributeDescription *dlm_memberOf_ad;
+ ObjectClass *dlm_static_oc;
+ int dlm_memberOf_nested;
+ int dlm_member_oper;
+ int dlm_memberOf_oper;
+ struct dynlist_map_t *dlm_next;
+} dynlist_map_t;
+
+typedef struct dynlist_info_t {
+ ObjectClass *dli_oc;
+ AttributeDescription *dli_ad;
+ struct dynlist_map_t *dli_dlm;
+ struct berval dli_uri;
+ LDAPURLDesc *dli_lud;
+ struct berval dli_uri_nbase;
+ Filter *dli_uri_filter;
+ struct berval dli_default_filter;
+ struct dynlist_info_t *dli_next;
+} dynlist_info_t;
+
+typedef struct dynlist_gen_t {
+ dynlist_info_t *dlg_dli;
+ int dlg_memberOf;
+} dynlist_gen_t;
+
+#define DYNLIST_USAGE \
+ "\"dynlist-attrset <oc> [uri] <URL-ad> [[<mapped-ad>:]<member-ad>[+<memberOf-ad>[@<static-oc>[*]] ...]\": "
+
+static int
+ad_infilter( AttributeDescription *ad, Filter *f )
+{
+ if ( !f )
+ return 0;
+
+ switch( f->f_choice & SLAPD_FILTER_MASK ) {
+ case SLAPD_FILTER_COMPUTED:
+ return 0;
+ case LDAP_FILTER_PRESENT:
+ return f->f_desc == ad;
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ case LDAP_FILTER_SUBSTRINGS:
+ case LDAP_FILTER_EXT:
+ return f->f_av_desc == ad;
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT: {
+ for ( f = f->f_list; f; f = f->f_next )
+ if ( ad_infilter( ad, f ))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int
+dynlist_make_filter( Operation *op, Entry *e, dynlist_info_t *dli, const char *url, struct berval *oldf, struct berval *newf )
+{
+ char *ptr;
+ int needBrackets = 0;
+
+ assert( oldf != NULL );
+ assert( newf != NULL );
+ assert( !BER_BVISNULL( oldf ) );
+ assert( !BER_BVISEMPTY( oldf ) );
+
+ if ( oldf->bv_val[0] != '(' ) {
+ Debug( LDAP_DEBUG_ANY, "%s: dynlist, DN=\"%s\": missing parentheses in URI=\"%s\" filter\n",
+ op->o_log_prefix, e->e_name.bv_val, url );
+ needBrackets = 2;
+ }
+
+ newf->bv_len = STRLENOF( "(&(!(objectClass=" "))" ")" )
+ + dli->dli_oc->soc_cname.bv_len + oldf->bv_len + needBrackets;
+ newf->bv_val = op->o_tmpalloc( newf->bv_len + 1, op->o_tmpmemctx );
+ if ( newf->bv_val == NULL ) {
+ return -1;
+ }
+ ptr = lutil_strcopy( newf->bv_val, "(&(!(objectClass=" );
+ ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val );
+ ptr = lutil_strcopy( ptr, "))" );
+ if ( needBrackets ) *ptr++ = '(';
+ ptr = lutil_strcopy( ptr, oldf->bv_val );
+ if ( needBrackets ) *ptr++ = ')';
+ ptr = lutil_strcopy( ptr, ")" );
+ newf->bv_len = ptr - newf->bv_val;
+
+ return 0;
+}
+
+/* dynlist_sc_update() callback info set by dynlist_prepare_entry() */
+typedef struct dynlist_sc_t {
+ dynlist_info_t *dlc_dli;
+ Entry *dlc_e;
+ char **dlc_attrs;
+} dynlist_sc_t;
+
+static int
+dynlist_sc_update( Operation *op, SlapReply *rs )
+{
+ Entry *e;
+ Attribute *a;
+ int opattrs,
+ userattrs;
+ AccessControlState acl_state = ACL_STATE_INIT;
+
+ dynlist_sc_t *dlc;
+ dynlist_map_t *dlm;
+
+ if ( rs->sr_type != REP_SEARCH ) {
+ return 0;
+ }
+
+ dlc = (dynlist_sc_t *)op->o_callback->sc_private;
+ e = dlc->dlc_e;
+
+ assert( e != NULL );
+ assert( rs->sr_entry != NULL );
+
+ /* test access to entry */
+ if ( !access_allowed( op, rs->sr_entry, slap_schema.si_ad_entry,
+ NULL, ACL_READ, NULL ) )
+ {
+ goto done;
+ }
+
+ /* if there is only one member_ad, and it's not mapped,
+ * consider it as old-style member listing */
+ dlm = dlc->dlc_dli->dli_dlm;
+ if ( dlm && dlm->dlm_mapped_ad == NULL && dlm->dlm_next == NULL && dlc->dlc_attrs == NULL ) {
+ /* if access allowed, try to add values, emulating permissive
+ * control to silently ignore duplicates */
+ if ( access_allowed( op, rs->sr_entry, slap_schema.si_ad_entry,
+ NULL, ACL_READ, NULL ) )
+ {
+ Modification mod;
+ const char *text = NULL;
+ char textbuf[1024];
+ struct berval vals[ 2 ], nvals[ 2 ];
+
+ vals[ 0 ] = rs->sr_entry->e_name;
+ BER_BVZERO( &vals[ 1 ] );
+ nvals[ 0 ] = rs->sr_entry->e_nname;
+ BER_BVZERO( &nvals[ 1 ] );
+
+ mod.sm_op = LDAP_MOD_ADD;
+ mod.sm_desc = dlm->dlm_member_ad;
+ mod.sm_type = dlm->dlm_member_ad->ad_cname;
+ mod.sm_values = vals;
+ mod.sm_nvalues = nvals;
+ mod.sm_numvals = 1;
+
+ (void)modify_add_values( e, &mod, /* permissive */ 1,
+ &text, textbuf, sizeof( textbuf ) );
+ }
+
+ goto done;
+ }
+
+ opattrs = SLAP_OPATTRS( rs->sr_attr_flags );
+ userattrs = SLAP_USERATTRS( rs->sr_attr_flags );
+
+ for ( a = rs->sr_entry->e_attrs; a != NULL; a = a->a_next ) {
+ BerVarray vals, nvals = NULL;
+ int i, j,
+ is_oc = a->a_desc == slap_schema.si_ad_objectClass;
+
+ /* if attribute is not requested, skip it */
+ if ( rs->sr_attrs == NULL ) {
+ if ( is_at_operational( a->a_desc->ad_type ) ) {
+ continue;
+ }
+
+ } else {
+ if ( is_at_operational( a->a_desc->ad_type ) ) {
+ if ( !opattrs && !ad_inlist( a->a_desc, rs->sr_attrs ) )
+ {
+ continue;
+ }
+
+ } else {
+ if ( !userattrs && !ad_inlist( a->a_desc, rs->sr_attrs ) )
+ {
+ continue;
+ }
+ }
+ }
+
+ /* test access to attribute */
+ if ( op->ors_attrsonly ) {
+ if ( !access_allowed( op, rs->sr_entry, a->a_desc, NULL,
+ ACL_READ, &acl_state ) )
+ {
+ continue;
+ }
+ }
+
+ /* single-value check: keep first only */
+ if ( is_at_single_value( a->a_desc->ad_type ) ) {
+ if ( attr_find( e->e_attrs, a->a_desc ) != NULL ) {
+ continue;
+ }
+ }
+
+ /* test access to attribute */
+ i = a->a_numvals;
+
+ vals = op->o_tmpalloc( ( i + 1 ) * sizeof( struct berval ), op->o_tmpmemctx );
+ if ( a->a_nvals != a->a_vals ) {
+ nvals = op->o_tmpalloc( ( i + 1 ) * sizeof( struct berval ), op->o_tmpmemctx );
+ }
+
+ for ( i = 0, j = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ ) {
+ if ( is_oc ) {
+ ObjectClass *soc = oc_bvfind( &a->a_vals[i] );
+
+ if ( soc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) {
+ continue;
+ }
+ }
+
+ if ( access_allowed( op, rs->sr_entry, a->a_desc,
+ &a->a_nvals[i], ACL_READ, &acl_state ) )
+ {
+ vals[j] = a->a_vals[i];
+ if ( nvals ) {
+ nvals[j] = a->a_nvals[i];
+ }
+ j++;
+ }
+ }
+
+ /* if access allowed, try to add values, emulating permissive
+ * control to silently ignore duplicates */
+ if ( j != 0 ) {
+ Modification mod;
+ const char *text = NULL;
+ char textbuf[1024];
+ dynlist_map_t *dlm;
+ AttributeDescription *ad;
+
+ BER_BVZERO( &vals[j] );
+ if ( nvals ) {
+ BER_BVZERO( &nvals[j] );
+ }
+
+ ad = a->a_desc;
+ for ( dlm = dlc->dlc_dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
+ if ( dlm->dlm_member_ad == a->a_desc ) {
+ if ( dlm->dlm_mapped_ad ) {
+ ad = dlm->dlm_mapped_ad;
+ }
+ break;
+ }
+ }
+
+ mod.sm_op = LDAP_MOD_ADD;
+ mod.sm_desc = ad;
+ mod.sm_type = ad->ad_cname;
+ mod.sm_values = vals;
+ mod.sm_nvalues = nvals;
+ mod.sm_numvals = j;
+
+ (void)modify_add_values( e, &mod, /* permissive */ 1,
+ &text, textbuf, sizeof( textbuf ) );
+ }
+
+ op->o_tmpfree( vals, op->o_tmpmemctx );
+ if ( nvals ) {
+ op->o_tmpfree( nvals, op->o_tmpmemctx );
+ }
+ }
+
+done:;
+ if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
+ entry_free( rs->sr_entry );
+ rs->sr_entry = NULL;
+ rs->sr_flags &= ~REP_ENTRY_MASK;
+ }
+
+ return 0;
+}
+
+typedef struct dynlist_name_t {
+ struct berval dy_name;
+ dynlist_info_t *dy_dli;
+ AttributeDescription *dy_staticmember;
+ int dy_seen;
+ int dy_numuris;
+ TAvlnode *dy_subs;
+ TAvlnode *dy_sups;
+ LDAPURLDesc *dy_uris[];
+} dynlist_name_t;
+
+static void
+dynlist_urlmembers( Operation *op, dynlist_name_t *dyn, slap_callback *sc )
+{
+ Operation o = *op;
+ LDAPURLDesc *ludp;
+ int i;
+
+ o.ors_deref = LDAP_DEREF_NEVER;
+ o.ors_limit = NULL;
+ o.ors_tlimit = SLAP_NO_LIMIT;
+ o.ors_slimit = SLAP_NO_LIMIT;
+ o.ors_attrs = NULL;
+ memset( o.o_ctrlflag, 0, sizeof( o.o_ctrlflag ));
+ o.o_callback = sc;
+
+ for (i=0; i<dyn->dy_numuris; i++) {
+ ludp = dyn->dy_uris[i];
+ if ( ludp->lud_attrs )
+ continue;
+ o.o_req_dn.bv_val = ludp->lud_dn;
+ o.o_req_dn.bv_len = ludp->lud_port;
+ o.o_req_ndn = o.o_req_dn;
+ o.ors_scope = ludp->lud_scope;
+ o.ors_filter = (Filter *)ludp->lud_filter;
+ filter2bv_x( op, o.ors_filter, &o.ors_filterstr );
+ o.o_bd = select_backend( &o.o_req_ndn, 1 );
+ if ( o.o_bd && o.o_bd->be_search ) {
+ SlapReply r = { REP_SEARCH };
+ r.sr_attr_flags = slap_attr_flags( o.ors_attrs );
+ o.o_managedsait = SLAP_CONTROL_CRITICAL;
+ (void)o.o_bd->be_search( &o, &r );
+ }
+ op->o_tmpfree( o.ors_filterstr.bv_val, op->o_tmpmemctx );
+ }
+}
+
+static void
+dynlist_nested_memberOf( Entry *e, AttributeDescription *ad, TAvlnode *sups )
+{
+ TAvlnode *ptr;
+ dynlist_name_t *dyn;
+ Attribute *a;
+
+ a = attr_find( e->e_attrs, ad );
+ for ( ptr = ldap_tavl_end( sups, TAVL_DIR_LEFT ); ptr;
+ ptr = ldap_tavl_next( ptr, TAVL_DIR_RIGHT )) {
+ dyn = ptr->avl_data;
+ if ( a ) {
+ unsigned slot;
+ if ( attr_valfind( a, SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
+ &dyn->dy_name, &slot, NULL ) == LDAP_SUCCESS )
+ continue;
+ }
+ attr_merge_one( e, ad, &dyn->dy_name, &dyn->dy_name );
+ if ( !a )
+ a = attr_find( e->e_attrs, ad );
+ if ( dyn->dy_sups )
+ dynlist_nested_memberOf( e, ad, dyn->dy_sups );
+ }
+}
+
+typedef struct dynlist_member_t {
+ Entry *dm_e;
+ AttributeDescription *dm_ad;
+ Modification dm_mod;
+ TAvlnode *dm_groups;
+ struct berval dm_bv[2];
+ struct berval dm_nbv[2];
+ const char *dm_text;
+ char dm_textbuf[1024];
+} dynlist_member_t;
+
+static int
+dynlist_ptr_cmp( const void *c1, const void *c2 )
+{
+ return ( c1 < c2 ) ? -1 : c1 > c2;
+}
+
+static int
+dynlist_nested_member_dg( Operation *op, SlapReply *rs )
+{
+ dynlist_member_t *dm = op->o_callback->sc_private;
+
+ if ( rs->sr_type != REP_SEARCH )
+ return LDAP_SUCCESS;
+
+ dm->dm_bv[0] = rs->sr_entry->e_name;
+ dm->dm_nbv[0] = rs->sr_entry->e_nname;
+ modify_add_values( dm->dm_e, &dm->dm_mod, /* permissive */ 1,
+ &dm->dm_text, dm->dm_textbuf, sizeof( dm->dm_textbuf ));
+
+ return LDAP_SUCCESS;
+}
+
+static void
+dynlist_nested_member( Operation *op, dynlist_member_t *dm, TAvlnode *subs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ TAvlnode *ptr;
+ dynlist_name_t *dyn;
+ Entry *ne;
+ Attribute *a, *b;
+
+ a = attr_find( dm->dm_e->e_attrs, dm->dm_ad );
+ if ( !a )
+ return;
+
+ for ( ptr = ldap_tavl_end( subs, TAVL_DIR_LEFT ); ptr;
+ ptr = ldap_tavl_next( ptr, TAVL_DIR_RIGHT )) {
+ dyn = ptr->avl_data;
+ if ( ldap_tavl_insert( &dm->dm_groups, dyn, dynlist_ptr_cmp, ldap_avl_dup_error ))
+ continue;
+ if ( overlay_entry_get_ov( op, &dyn->dy_name, NULL, NULL, 0, &ne, on ) != LDAP_SUCCESS || ne == NULL )
+ continue;
+ b = attr_find( ne->e_attrs, dm->dm_ad );
+ if ( b ) {
+ dm->dm_mod.sm_values = b->a_vals;
+ dm->dm_mod.sm_nvalues = b->a_nvals;
+ dm->dm_mod.sm_numvals = b->a_numvals;
+ modify_add_values( dm->dm_e, &dm->dm_mod, /* permissive */ 1,
+ &dm->dm_text, dm->dm_textbuf, sizeof( dm->dm_textbuf ));
+ }
+ overlay_entry_release_ov( op, ne, 0, on );
+ if ( dyn->dy_numuris ) {
+ slap_callback cb = { 0 };
+ cb.sc_private = dm;
+ BER_BVZERO( &dm->dm_bv[1] );
+ BER_BVZERO( &dm->dm_nbv[1] );
+ dm->dm_mod.sm_values = dm->dm_bv;
+ dm->dm_mod.sm_nvalues = dm->dm_nbv;
+ dm->dm_mod.sm_numvals = 1;
+ cb.sc_response = dynlist_nested_member_dg;
+ dynlist_urlmembers( op, dyn, &cb );
+ }
+ if ( dyn->dy_subs )
+ dynlist_nested_member( op, dm, dyn->dy_subs );
+ }
+}
+
+static int
+dynlist_prepare_entry( Operation *op, SlapReply *rs, dynlist_info_t *dli, dynlist_name_t *dyn )
+{
+ Attribute *a, *id = NULL;
+ slap_callback cb = { 0 };
+ Operation o = *op;
+ struct berval *url;
+ Entry *e;
+ int opattrs,
+ userattrs;
+ dynlist_sc_t dlc = { 0 };
+ dynlist_map_t *dlm;
+
+ e = rs->sr_entry;
+ a = attrs_find( rs->sr_entry->e_attrs, dli->dli_ad );
+ if ( a == NULL ) {
+ /* FIXME: error? */
+ goto checkdyn;
+ }
+
+ opattrs = SLAP_OPATTRS( rs->sr_attr_flags );
+ userattrs = SLAP_USERATTRS( rs->sr_attr_flags );
+
+ /* Don't generate member list if it wasn't requested */
+ for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
+ AttributeDescription *ad = dlm->dlm_mapped_ad ? dlm->dlm_mapped_ad : dlm->dlm_member_ad;
+ if ( userattrs || ad_inlist( ad, rs->sr_attrs )
+ || ad_infilter( ad, op->ors_filter ))
+ break;
+ }
+ if ( dli->dli_dlm && !dlm )
+ goto checkdyn;
+
+ if ( ad_dgIdentity && ( id = attrs_find( rs->sr_entry->e_attrs, ad_dgIdentity ))) {
+ Attribute *authz = NULL;
+
+ /* if not rootdn and dgAuthz is present,
+ * check if user can be authorized as dgIdentity */
+ if ( ad_dgAuthz && !BER_BVISEMPTY( &id->a_nvals[0] ) && !be_isroot( op )
+ && ( authz = attrs_find( rs->sr_entry->e_attrs, ad_dgAuthz ) ) )
+ {
+ if ( slap_sasl_matches( op, authz->a_nvals,
+ &o.o_ndn, &o.o_ndn ) != LDAP_SUCCESS )
+ {
+ goto checkdyn;
+ }
+ }
+
+ o.o_dn = id->a_vals[0];
+ o.o_ndn = id->a_nvals[0];
+ o.o_groups = NULL;
+ }
+
+ /* ensure e is modifiable, but do not replace
+ * sr_entry yet since we have pointers into it */
+ if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE ) ) {
+ e = entry_dup( rs->sr_entry );
+ }
+
+ dlc.dlc_e = e;
+ dlc.dlc_dli = dli;
+ cb.sc_private = &dlc;
+ cb.sc_response = dynlist_sc_update;
+
+ o.o_callback = &cb;
+ o.ors_deref = LDAP_DEREF_NEVER;
+ o.ors_limit = NULL;
+ o.ors_tlimit = SLAP_NO_LIMIT;
+ o.ors_slimit = SLAP_NO_LIMIT;
+ memset( o.o_ctrlflag, 0, sizeof( o.o_ctrlflag ));
+
+ for ( url = a->a_nvals; !BER_BVISNULL( url ); url++ ) {
+ LDAPURLDesc *lud = NULL;
+ int i, j;
+ struct berval dn;
+ int rc;
+
+ BER_BVZERO( &o.o_req_dn );
+ BER_BVZERO( &o.o_req_ndn );
+ o.ors_filter = NULL;
+ o.ors_attrs = NULL;
+ BER_BVZERO( &o.ors_filterstr );
+
+ if ( ldap_url_parse( url->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
+ /* FIXME: error? */
+ continue;
+ }
+
+ if ( lud->lud_host != NULL ) {
+ /* FIXME: host not allowed; reject as illegal? */
+ Debug( LDAP_DEBUG_ANY, "dynlist_prepare_entry(\"%s\"): "
+ "illegal URI \"%s\"\n",
+ e->e_name.bv_val, url->bv_val );
+ goto cleanup;
+ }
+
+ if ( lud->lud_dn == NULL ) {
+ /* note that an empty base is not honored in terms
+ * of defaultSearchBase, because select_backend()
+ * is not aware of the defaultSearchBase option;
+ * this can be useful in case of a database serving
+ * the empty suffix */
+ BER_BVSTR( &dn, "" );
+
+ } else {
+ ber_str2bv( lud->lud_dn, 0, 0, &dn );
+ }
+ rc = dnPrettyNormal( NULL, &dn, &o.o_req_dn, &o.o_req_ndn, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ /* FIXME: error? */
+ goto cleanup;
+ }
+ o.ors_scope = lud->lud_scope;
+
+ for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
+ if ( dlm->dlm_mapped_ad != NULL ) {
+ break;
+ }
+ }
+
+ if ( dli->dli_dlm && !dlm ) {
+ /* if ( lud->lud_attrs != NULL ),
+ * the URL should be ignored */
+ o.ors_attrs = slap_anlist_no_attrs;
+
+ } else if ( lud->lud_attrs == NULL ) {
+ o.ors_attrs = rs->sr_attrs;
+
+ } else {
+ for ( i = 0; lud->lud_attrs[i]; i++)
+ /* just count */ ;
+
+ o.ors_attrs = op->o_tmpcalloc( i + 1, sizeof( AttributeName ), op->o_tmpmemctx );
+ for ( i = 0, j = 0; lud->lud_attrs[i]; i++) {
+ const char *text = NULL;
+
+ ber_str2bv( lud->lud_attrs[i], 0, 0, &o.ors_attrs[j].an_name );
+ o.ors_attrs[j].an_desc = NULL;
+ (void)slap_bv2ad( &o.ors_attrs[j].an_name, &o.ors_attrs[j].an_desc, &text );
+ /* FIXME: ignore errors... */
+
+ if ( ad_infilter( o.ors_attrs[j].an_desc, op->ors_filter )) {
+ /* if referenced in filter, must retrieve */
+ } else if ( rs->sr_attrs == NULL ) {
+ if ( o.ors_attrs[j].an_desc != NULL &&
+ is_at_operational( o.ors_attrs[j].an_desc->ad_type ) )
+ {
+ continue;
+ }
+
+ } else {
+ if ( o.ors_attrs[j].an_desc != NULL &&
+ is_at_operational( o.ors_attrs[j].an_desc->ad_type ) )
+ {
+ if ( !opattrs ) {
+ continue;
+ }
+
+ if ( !ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) ) {
+ /* lookup if mapped -- linear search,
+ * not very efficient unless list
+ * is very short */
+ for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
+ if ( dlm->dlm_member_ad == o.ors_attrs[j].an_desc ) {
+ break;
+ }
+ }
+
+ if ( dlm == NULL ) {
+ continue;
+ }
+ }
+
+ } else {
+ if ( !userattrs &&
+ o.ors_attrs[j].an_desc != NULL &&
+ !ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) )
+ {
+ /* lookup if mapped -- linear search,
+ * not very efficient unless list
+ * is very short */
+ for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
+ if ( dlm->dlm_member_ad == o.ors_attrs[j].an_desc ) {
+ break;
+ }
+ }
+
+ if ( dlm == NULL ) {
+ continue;
+ }
+ }
+ }
+ }
+
+ j++;
+ }
+
+ if ( j == 0 ) {
+ goto cleanup;
+ }
+
+ BER_BVZERO( &o.ors_attrs[j].an_name );
+ }
+ dlc.dlc_attrs = lud->lud_attrs;
+
+ if ( lud->lud_filter == NULL ) {
+ ber_dupbv_x( &o.ors_filterstr,
+ &dli->dli_default_filter, op->o_tmpmemctx );
+
+ } else {
+ /* don't allow recursion in lists */
+ if ( lud->lud_attrs ) {
+ struct berval flt;
+ ber_str2bv( lud->lud_filter, 0, 0, &flt );
+ if ( dynlist_make_filter( op, rs->sr_entry, dli, url->bv_val, &flt, &o.ors_filterstr ) ) {
+ /* error */
+ goto cleanup;
+ }
+ } else {
+ ber_str2bv( lud->lud_filter, 0, 0, &o.ors_filterstr );
+ }
+ }
+ o.ors_filter = str2filter_x( op, o.ors_filterstr.bv_val );
+ if ( o.ors_filter == NULL ) {
+ goto cleanup;
+ }
+
+ o.o_bd = select_backend( &o.o_req_ndn, 1 );
+ if ( o.o_bd && o.o_bd->be_search ) {
+ SlapReply r = { REP_SEARCH };
+ r.sr_attr_flags = slap_attr_flags( o.ors_attrs );
+ o.o_managedsait = SLAP_CONTROL_CRITICAL;
+ (void)o.o_bd->be_search( &o, &r );
+ }
+
+cleanup:;
+ if ( id ) {
+ slap_op_groups_free( &o );
+ }
+ if ( o.ors_filter ) {
+ filter_free_x( &o, o.ors_filter, 1 );
+ }
+ if ( o.ors_attrs && o.ors_attrs != rs->sr_attrs
+ && o.ors_attrs != slap_anlist_no_attrs )
+ {
+ op->o_tmpfree( o.ors_attrs, op->o_tmpmemctx );
+ }
+ if ( !BER_BVISNULL( &o.o_req_dn ) ) {
+ op->o_tmpfree( o.o_req_dn.bv_val, op->o_tmpmemctx );
+ }
+ if ( !BER_BVISNULL( &o.o_req_ndn ) ) {
+ op->o_tmpfree( o.o_req_ndn.bv_val, op->o_tmpmemctx );
+ }
+ if ( lud->lud_attrs ) {
+ assert( BER_BVISNULL( &o.ors_filterstr )
+ || o.ors_filterstr.bv_val != lud->lud_filter );
+ op->o_tmpfree( o.ors_filterstr.bv_val, op->o_tmpmemctx );
+ } else {
+ if ( o.ors_filterstr.bv_val != lud->lud_filter )
+ op->o_tmpfree( o.ors_filterstr.bv_val, op->o_tmpmemctx );
+ }
+ ldap_free_urldesc( lud );
+ }
+
+checkdyn:
+ /* handle nested groups */
+ if ( dyn && ( dyn->dy_sups || dyn->dy_subs )) {
+ /* ensure e is modifiable */
+ if ( e == rs->sr_entry && !( rs->sr_flags & REP_ENTRY_MODIFIABLE ) ) {
+ e = entry_dup( rs->sr_entry );
+ rs_replace_entry( op, rs, (slap_overinst *)op->o_bd->bd_info, e );
+ rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED;
+ }
+ if ( dyn->dy_subs ) {
+ for ( dlm = dyn->dy_dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
+ if ( dlm->dlm_member_ad ) {
+ dynlist_member_t dm;
+ dm.dm_groups = NULL;
+ dm.dm_mod.sm_op = LDAP_MOD_ADD;
+ dm.dm_mod.sm_desc = dlm->dlm_member_ad;
+ dm.dm_mod.sm_type = dlm->dlm_member_ad->ad_cname;
+ dm.dm_e = e;
+ dm.dm_ad = dlm->dlm_member_ad;
+ dynlist_nested_member( op, &dm, dyn->dy_subs );
+ if ( dm.dm_groups )
+ ldap_tavl_free( dm.dm_groups, NULL );
+ }
+ }
+ }
+ if ( dyn->dy_sups ) {
+ for ( dlm = dyn->dy_dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
+ if ( dlm->dlm_memberOf_ad ) {
+ dynlist_nested_memberOf( e, dlm->dlm_memberOf_ad, dyn->dy_sups );
+ }
+ }
+ }
+ }
+
+ if ( e != rs->sr_entry ) {
+ rs_replace_entry( op, rs, (slap_overinst *)op->o_bd->bd_info, e );
+ rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+/* dynlist_sc_compare_entry() callback set by dynlist_compare() */
+typedef struct dynlist_cc_t {
+ slap_callback dc_cb;
+# define dc_ava dc_cb.sc_private /* attr:val to compare with */
+ int *dc_res;
+} dynlist_cc_t;
+
+static int
+dynlist_sc_compare_entry( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_SEARCH && rs->sr_entry != NULL ) {
+ dynlist_cc_t *dc = (dynlist_cc_t *)op->o_callback;
+ AttributeAssertion *ava = dc->dc_ava;
+ Attribute *a = attrs_find( rs->sr_entry->e_attrs, ava->aa_desc );
+
+ if ( a != NULL ) {
+ while ( LDAP_SUCCESS != attr_valfind( a,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ &ava->aa_value, NULL, op->o_tmpmemctx )
+ && (a = attrs_find( a->a_next, ava->aa_desc )) != NULL )
+ ;
+ *dc->dc_res = a ? LDAP_COMPARE_TRUE : LDAP_COMPARE_FALSE;
+ }
+ }
+
+ return 0;
+}
+
+static int
+dynlist_compare( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ dynlist_gen_t *dlg = (dynlist_gen_t *)on->on_bi.bi_private;
+ dynlist_info_t *dli = dlg->dlg_dli;
+ Operation o = *op;
+ Entry *e = NULL;
+ dynlist_map_t *dlm;
+ BackendDB *be;
+ int ret = SLAP_CB_CONTINUE;
+
+ if ( get_manageDSAit( op ) )
+ return SLAP_CB_CONTINUE;
+
+ for ( ; dli != NULL; dli = dli->dli_next ) {
+ for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
+ AttributeDescription *ad = dlm->dlm_mapped_ad ? dlm->dlm_mapped_ad : dlm->dlm_member_ad;
+ /* builtin dyngroup evaluator only works for DNs */
+ if ( ad->ad_type->sat_syntax != slap_schema.si_syn_distinguishedName )
+ continue;
+ if ( op->oq_compare.rs_ava->aa_desc == ad )
+ break;
+ }
+
+ if ( dlm ) {
+ /* This compare is for one of the attributes we're
+ * interested in. We'll use slapd's existing dyngroup
+ * evaluator to get the answer we want.
+ */
+ BerVarray id = NULL, authz = NULL;
+
+ o.o_do_not_cache = 1;
+
+ if ( ad_dgIdentity && backend_attribute( &o, NULL, &o.o_req_ndn,
+ ad_dgIdentity, &id, ACL_READ ) == LDAP_SUCCESS )
+ {
+ /* if not rootdn and dgAuthz is present,
+ * check if user can be authorized as dgIdentity */
+ if ( ad_dgAuthz && !BER_BVISEMPTY( id ) && !be_isroot( op )
+ && backend_attribute( &o, NULL, &o.o_req_ndn,
+ ad_dgAuthz, &authz, ACL_READ ) == LDAP_SUCCESS )
+ {
+
+ rs->sr_err = slap_sasl_matches( op, authz,
+ &o.o_ndn, &o.o_ndn );
+ ber_bvarray_free_x( authz, op->o_tmpmemctx );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto done;
+ }
+ }
+
+ o.o_dn = *id;
+ o.o_ndn = *id;
+ o.o_groups = NULL; /* authz changed, invalidate cached groups */
+ }
+
+ rs->sr_err = backend_group( &o, NULL, &o.o_req_ndn,
+ &o.oq_compare.rs_ava->aa_value, dli->dli_oc, dli->dli_ad );
+ switch ( rs->sr_err ) {
+ case LDAP_SUCCESS:
+ rs->sr_err = LDAP_COMPARE_TRUE;
+ break;
+
+ case LDAP_NO_SUCH_OBJECT:
+ /* NOTE: backend_group() returns noSuchObject
+ * if op_ndn does not exist; however, since
+ * dynamic list expansion means that the
+ * member attribute is virtually present, the
+ * non-existence of the asserted value implies
+ * the assertion is FALSE rather than
+ * UNDEFINED */
+ rs->sr_err = LDAP_COMPARE_FALSE;
+
+ /* If also using static groups, fallback to
+ * vanilla compare
+ */
+ if ( dlm->dlm_static_oc )
+ return SLAP_CB_CONTINUE;
+
+ break;
+ }
+
+done:;
+ if ( id ) ber_bvarray_free_x( id, o.o_tmpmemctx );
+
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+ }
+
+ be = select_backend( &o.o_req_ndn, 1 );
+ if ( !be || !be->be_search ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( overlay_entry_get_ov( &o, &o.o_req_ndn, NULL, NULL, 0, &e, on ) !=
+ LDAP_SUCCESS || e == NULL )
+ {
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* check for dynlist objectClass; done if not found */
+ dli = (dynlist_info_t *)dlg->dlg_dli;
+ while ( dli != NULL && !is_entry_objectclass_or_sub( e, dli->dli_oc ) ) {
+ dli = dli->dli_next;
+ }
+ if ( dli == NULL ) {
+ goto release;
+ }
+
+ if ( ad_dgIdentity ) {
+ Attribute *id = attrs_find( e->e_attrs, ad_dgIdentity );
+ if ( id ) {
+ Attribute *authz;
+
+ /* if not rootdn and dgAuthz is present,
+ * check if user can be authorized as dgIdentity */
+ if ( ad_dgAuthz && !BER_BVISEMPTY( &id->a_nvals[0] ) && !be_isroot( op )
+ && ( authz = attrs_find( e->e_attrs, ad_dgAuthz ) ) )
+ {
+ if ( slap_sasl_matches( op, authz->a_nvals,
+ &o.o_ndn, &o.o_ndn ) != LDAP_SUCCESS )
+ {
+ goto release;
+ }
+ }
+
+ o.o_dn = id->a_vals[0];
+ o.o_ndn = id->a_nvals[0];
+ o.o_groups = NULL;
+ }
+ }
+
+ /* generate dynamic list with dynlist_response() and compare */
+ {
+ SlapReply r = { REP_SEARCH };
+ Attribute *a;
+ AttributeName an[2];
+
+ o.o_tag = LDAP_REQ_SEARCH;
+ o.ors_limit = NULL;
+ o.ors_tlimit = SLAP_NO_LIMIT;
+ o.ors_slimit = SLAP_NO_LIMIT;
+
+ o.ors_filterstr = *slap_filterstr_objectClass_pres;
+ o.ors_filter = (Filter *) slap_filter_objectClass_pres;
+
+ o.ors_scope = LDAP_SCOPE_BASE;
+ o.ors_deref = LDAP_DEREF_NEVER;
+ an[0].an_name = op->orc_ava->aa_desc->ad_cname;
+ an[0].an_desc = op->orc_ava->aa_desc;
+ BER_BVZERO( &an[1].an_name );
+ o.ors_attrs = an;
+ o.ors_attrsonly = 0;
+ r.sr_entry = e;
+ r.sr_attrs = an;
+
+ o.o_acl_priv = ACL_COMPARE;
+ dynlist_prepare_entry( &o, &r, dli, NULL );
+ a = attrs_find( r.sr_entry->e_attrs, op->orc_ava->aa_desc );
+
+ ret = LDAP_NO_SUCH_ATTRIBUTE;
+ for ( ; a ; a = attrs_find( a->a_next, op->orc_ava->aa_desc )) {
+ ret = LDAP_COMPARE_FALSE;
+ if ( attr_valfind( a,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ &op->orc_ava->aa_value, NULL, op->o_tmpmemctx ) == LDAP_SUCCESS ) {
+ ret = LDAP_COMPARE_TRUE;
+ break;
+ }
+ }
+ rs->sr_err = ret;
+
+ if ( r.sr_entry != e )
+ entry_free( r.sr_entry );
+ send_ldap_result( op, rs );
+ }
+
+release:;
+ if ( e != NULL ) {
+ overlay_entry_release_ov( &o, e, 0, on );
+ }
+
+ return ret;
+}
+
+#define WANT_MEMBEROF 1
+#define WANT_MEMBER 2
+
+typedef struct dynlist_search_t {
+ TAvlnode *ds_names;
+ TAvlnode *ds_fnodes;
+ dynlist_info_t *ds_dli;
+ dynlist_map_t *ds_dlm;
+ Filter *ds_origfilter;
+ struct berval ds_origfilterbv;
+ int ds_want;
+ int ds_found;
+} dynlist_search_t;
+
+static int
+dynlist_avl_cmp( const void *c1, const void *c2 )
+{
+ const dynlist_name_t *n1, *n2;
+ int rc;
+ n1 = c1; n2 = c2;
+
+ rc = n1->dy_name.bv_len - n2->dy_name.bv_len;
+ if ( rc ) return rc;
+ return ber_bvcmp( &n1->dy_name, &n2->dy_name );
+}
+
+/* build a list of dynamic entries */
+static int
+dynlist_search1resp( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_SEARCH && rs->sr_entry != NULL ) {
+ dynlist_search_t *ds = op->o_callback->sc_private;
+ Attribute *a, *b = NULL;
+
+ if ( ds->ds_dlm && ds->ds_dlm->dlm_static_oc && is_entry_objectclass( rs->sr_entry, ds->ds_dlm->dlm_static_oc, 0 ))
+ b = attr_find( rs->sr_entry->e_attrs, ds->ds_dlm->dlm_member_ad );
+ a = attr_find( rs->sr_entry->e_attrs, ds->ds_dli->dli_ad );
+ if ( a || b ) {
+ unsigned len;
+ dynlist_name_t *dyn;
+ struct berval bv, nbase;
+ LDAPURLDesc *ludp;
+ int i, j = 0;
+
+ if ( a )
+ len = a->a_numvals * sizeof(LDAPURLDesc *);
+ else
+ len = 0;
+
+ dyn = ch_calloc(1, sizeof(dynlist_name_t)+rs->sr_entry->e_nname.bv_len + 1 + len);
+ dyn->dy_name.bv_val = ((char *)(dyn+1)) + len;
+ dyn->dy_dli = ds->ds_dli;
+ dyn->dy_name.bv_len = rs->sr_entry->e_nname.bv_len;
+ if ( a ) {
+ Filter *f;
+ /* parse and validate the URIs */
+ for (i=0; i<a->a_numvals; i++) {
+ if (ldap_url_parse( a->a_vals[i].bv_val, &ludp ) != LDAP_URL_SUCCESS )
+ continue;
+ if (( ludp->lud_host && *ludp->lud_host)
+ || ludp->lud_exts ) {
+ skipit:
+ ldap_free_urldesc( ludp );
+ continue;
+ }
+ ber_str2bv( ludp->lud_dn, 0, 0, &bv );
+ if ( dnNormalize( 0, NULL, NULL, &bv, &nbase, op->o_tmpmemctx ) != LDAP_SUCCESS )
+ goto skipit;
+ ldap_memfree( ludp->lud_dn );
+ ludp->lud_dn = ldap_strdup( nbase.bv_val );
+ op->o_tmpfree( nbase.bv_val, op->o_tmpmemctx );
+ /* cheat here, reuse fields */
+ ludp->lud_port = nbase.bv_len;
+ if ( ludp->lud_filter && *ludp->lud_filter ) {
+ f = str2filter( ludp->lud_filter );
+ if ( f == NULL )
+ goto skipit;
+ ldap_memfree( ludp->lud_filter );
+ } else {
+ f = ch_malloc( sizeof( Filter ));
+ f->f_choice = SLAPD_FILTER_COMPUTED;
+ f->f_result = LDAP_COMPARE_TRUE;
+ f->f_next = NULL;
+ }
+ ludp->lud_filter = (char *)f;
+ dyn->dy_uris[j] = ludp;
+ j++;
+ }
+ }
+ dyn->dy_numuris = j;
+ memcpy(dyn->dy_name.bv_val, rs->sr_entry->e_nname.bv_val, rs->sr_entry->e_nname.bv_len );
+ if ( b )
+ dyn->dy_staticmember = ds->ds_dlm->dlm_member_ad;
+
+ if ( ldap_tavl_insert( &ds->ds_names, dyn, dynlist_avl_cmp, ldap_avl_dup_error )) {
+ for (i=dyn->dy_numuris-1; i>=0; i--) {
+ ludp = dyn->dy_uris[i];
+ if ( ludp->lud_filter ) {
+ filter_free( (Filter *)ludp->lud_filter );
+ ludp->lud_filter = NULL;
+ }
+ ldap_free_urldesc( ludp );
+ }
+ ch_free( dyn );
+ } else {
+ ds->ds_found++;
+ }
+ }
+ }
+ return 0;
+}
+
+/* replace a filter clause (memberOf=<groupDN>) with an expansion
+ * of its dynamic members
+ * using (&(entryDN=<groupURIbase>)<groupURIfilter>)
+ */
+static int
+dynlist_filter_dyngroup( Operation *op, Filter *n, Attribute *a )
+{
+ Filter *andf = NULL, *dnf, *urif, *orf = NULL;
+ LDAPURLDesc *ludp;
+ struct berval bv, nbase;
+ int i;
+
+ for (i=0; i<a->a_numvals; i++) {
+ if ( ldap_url_parse( a->a_vals[i].bv_val, &ludp ) != LDAP_URL_SUCCESS )
+ continue;
+ if (( ludp->lud_host && *ludp->lud_host )
+ || ludp->lud_attrs
+ || ludp->lud_exts ) {
+ skip:
+ ldap_free_urldesc( ludp );
+ continue;
+ }
+ ber_str2bv( ludp->lud_dn, 0, 0, &bv );
+ if ( dnNormalize( 0, NULL, NULL, &bv, &nbase, op->o_tmpmemctx ) != LDAP_SUCCESS )
+ goto skip;
+ if ( ludp->lud_filter && *ludp->lud_filter ) {
+ urif = str2filter_x( op, ludp->lud_filter );
+ if ( urif == NULL ) {
+ op->o_tmpfree( nbase.bv_val, op->o_tmpmemctx );
+ goto skip;
+ }
+ } else {
+ urif = NULL;
+ }
+ if ( !andf && n->f_choice == SLAPD_FILTER_COMPUTED ) {
+ andf = n;
+ andf->f_next = NULL;
+ } else {
+ orf = n;
+ if ( n->f_choice != LDAP_FILTER_OR ) {
+ andf = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ *andf = *n;
+ orf->f_choice = LDAP_FILTER_OR;
+ orf->f_next = NULL;
+ orf->f_list = andf;
+ }
+ andf = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ andf->f_next = orf->f_list;
+ orf->f_list = andf;
+ }
+ dnf = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ andf->f_choice = LDAP_FILTER_AND;
+ andf->f_list = dnf;
+ dnf->f_next = urif;
+ if ( ludp->lud_scope == LDAP_SCOPE_BASE ) {
+ dnf->f_choice = LDAP_FILTER_EQUALITY;
+ dnf->f_ava = op->o_tmpcalloc( 1, sizeof(AttributeAssertion), op->o_tmpmemctx );
+ dnf->f_av_desc = slap_schema.si_ad_entryDN;
+ dnf->f_av_value = nbase;
+ } else {
+ dnf->f_choice = LDAP_FILTER_EXT;
+ dnf->f_mra = op->o_tmpcalloc( 1, sizeof(MatchingRuleAssertion), op->o_tmpmemctx );
+ dnf->f_mr_desc = slap_schema.si_ad_entryDN;
+ dnf->f_mr_value = nbase;
+ switch ( ludp->lud_scope ) {
+ case LDAP_SCOPE_ONELEVEL:
+ dnf->f_mr_rule = slap_schema.si_mr_dnOneLevelMatch;
+ break;
+ case LDAP_SCOPE_SUBTREE:
+ dnf->f_mr_rule = slap_schema.si_mr_dnSubtreeMatch;
+ break;
+ case LDAP_SCOPE_SUBORDINATE:
+ dnf->f_mr_rule = slap_schema.si_mr_dnSubordinateMatch;
+ break;
+ }
+ ber_str2bv( dnf->f_mr_rule->smr_names[0], 0, 0, &dnf->f_mr_rule_text );
+ }
+ ldap_free_urldesc( ludp );
+ }
+ if ( !andf )
+ return -1;
+ return 0;
+}
+
+/* replace a filter clause (memberOf=<groupDN>) with an expansion
+ * of its static members
+ * using (|(entryDN=<memberN>)[...])
+ */
+static int
+dynlist_filter_stgroup( Operation *op, Filter *n, Attribute *a )
+{
+ Filter *dnf, *orf = NULL;
+ int i;
+
+ if ( a->a_numvals == 1 && n->f_choice == SLAPD_FILTER_COMPUTED ) {
+ dnf = n;
+ } else {
+ orf = n;
+ if ( n->f_choice != LDAP_FILTER_OR ) {
+ orf->f_choice = LDAP_FILTER_OR;
+ orf->f_list = NULL;
+ }
+ dnf = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ dnf->f_next = orf->f_list;
+ orf->f_list = dnf;
+ }
+
+ for (i=0; i<a->a_numvals; i++) {
+ if ( i ) {
+ dnf = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ dnf->f_next = orf->f_list;
+ orf->f_list = dnf;
+ }
+ dnf->f_choice = LDAP_FILTER_EQUALITY;
+ dnf->f_ava = op->o_tmpcalloc( 1, sizeof(AttributeAssertion), op->o_tmpmemctx );
+ dnf->f_av_desc = slap_schema.si_ad_entryDN;
+ ber_dupbv_x( &dnf->f_av_value, &a->a_nvals[i], op->o_tmpmemctx );
+ }
+ return 0;
+}
+
+/* replace a filter clause (memberOf=<groupDN>) with an expansion of
+ * its members.
+ */
+static int
+dynlist_filter_group( Operation *op, dynlist_name_t *dyn, Filter *n, dynlist_search_t *ds )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ Entry *e;
+ Attribute *a;
+ int rc = -1;
+
+ if ( ldap_tavl_insert( &ds->ds_fnodes, dyn, dynlist_ptr_cmp, ldap_avl_dup_error ))
+ return 0;
+
+ if ( overlay_entry_get_ov( op, &dyn->dy_name, NULL, NULL, 0, &e, on ) !=
+ LDAP_SUCCESS || e == NULL ) {
+ return -1;
+ }
+ if ( ds->ds_dlm->dlm_static_oc && is_entry_objectclass( e, ds->ds_dlm->dlm_static_oc, 0 )) {
+ a = attr_find( e->e_attrs, ds->ds_dlm->dlm_member_ad );
+ if ( a ) {
+ rc = dynlist_filter_stgroup( op, n, a );
+ }
+ } else {
+ a = attr_find( e->e_attrs, ds->ds_dli->dli_ad );
+ if ( a ) {
+ rc = dynlist_filter_dyngroup( op, n, a );
+ }
+ }
+ overlay_entry_release_ov( op, e, 0, on );
+ if ( dyn->dy_subs && !rc ) {
+ TAvlnode *ptr;
+ for ( ptr = ldap_tavl_end( dyn->dy_subs, TAVL_DIR_LEFT ); ptr;
+ ptr = ldap_tavl_next( ptr, TAVL_DIR_RIGHT )) {
+ dyn = ptr->avl_data;
+ rc = dynlist_filter_group( op, dyn, n, ds );
+ if ( rc )
+ break;
+ }
+ }
+ return rc;
+}
+
+/* Dup the filter, replacing any references to given ad with group evaluation */
+static Filter *
+dynlist_filter_dup( Operation *op, Filter *f, AttributeDescription *ad, dynlist_search_t *ds )
+{
+ Filter *n = NULL;
+
+ if ( !f )
+ return NULL;
+
+ n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ n->f_next = NULL;
+ switch( f->f_choice & SLAPD_FILTER_MASK ) {
+ case SLAPD_FILTER_COMPUTED:
+ n->f_choice = f->f_choice;
+ n->f_result = f->f_result;
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ n->f_choice = f->f_choice;
+ n->f_desc = f->f_desc;
+ break;
+
+ case LDAP_FILTER_EQUALITY:
+ if ( f->f_av_desc == ad ) {
+ dynlist_name_t *dyn = ldap_tavl_find( ds->ds_names, &f->f_av_value, dynlist_avl_cmp );
+ n->f_choice = SLAPD_FILTER_COMPUTED;
+ if ( dyn && !dynlist_filter_group( op, dyn, n, ds ))
+ break;
+ }
+ /* FALLTHRU */
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ n->f_choice = f->f_choice;
+ n->f_ava = f->f_ava;
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ n->f_choice = f->f_choice;
+ n->f_sub = f->f_sub;
+ break;
+
+ case LDAP_FILTER_EXT:
+ n->f_choice = f->f_choice;
+ n->f_mra = f->f_mra;
+ break;
+
+ case LDAP_FILTER_NOT:
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR: {
+ Filter **p;
+
+ n->f_choice = f->f_choice;
+
+ for ( p = &n->f_list, f = f->f_list; f; f = f->f_next ) {
+ *p = dynlist_filter_dup( op, f, ad, ds );
+ if ( !*p )
+ continue;
+ p = &(*p)->f_next;
+ }
+ }
+ break;
+ }
+ return n;
+}
+
+static void
+dynlist_filter_free( Operation *op, Filter *f )
+{
+ Filter *p, *next;
+
+ if ( f == NULL )
+ return;
+
+ f->f_choice &= SLAPD_FILTER_MASK;
+ switch( f->f_choice ) {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ for ( p = f->f_list; p; p = next ) {
+ next = p->f_next;
+ op->o_tmpfree( p, op->o_tmpmemctx );
+ }
+ break;
+ default:
+ op->o_tmpfree( f, op->o_tmpmemctx );
+ }
+}
+
+static void
+dynlist_search_free( void *ptr )
+{
+ dynlist_name_t *dyn = (dynlist_name_t *)ptr;
+ LDAPURLDesc *ludp;
+ int i;
+
+ for (i=dyn->dy_numuris-1; i>=0; i--) {
+ ludp = dyn->dy_uris[i];
+ if ( ludp->lud_filter ) {
+ filter_free( (Filter *)ludp->lud_filter );
+ ludp->lud_filter = NULL;
+ }
+ ldap_free_urldesc( ludp );
+ }
+ if ( dyn->dy_subs )
+ ldap_tavl_free( dyn->dy_subs, NULL );
+ if ( dyn->dy_sups )
+ ldap_tavl_free( dyn->dy_sups, NULL );
+ ch_free( ptr );
+}
+
+static int
+dynlist_search_cleanup( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_RESULT || op->o_abandon ||
+ rs->sr_err == SLAPD_ABANDON ) {
+ slap_callback *sc = op->o_callback;
+ dynlist_search_t *ds = op->o_callback->sc_private;
+ ldap_tavl_free( ds->ds_names, dynlist_search_free );
+ if ( ds->ds_fnodes )
+ ldap_tavl_free( ds->ds_fnodes, NULL );
+ if ( ds->ds_origfilter ) {
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ dynlist_filter_free( op, op->ors_filter );
+ op->ors_filter = ds->ds_origfilter;
+ op->ors_filterstr = ds->ds_origfilterbv;
+ }
+ op->o_callback = sc->sc_next;
+ op->o_tmpfree( sc, op->o_tmpmemctx );
+
+ }
+ return 0;
+}
+
+static int
+dynlist_test_membership(Operation *op, dynlist_name_t *dyn, Entry *e)
+{
+ LDAPURLDesc *ludp;
+ struct berval nbase, bv;
+ int i, rc = LDAP_COMPARE_FALSE;
+ if ( dyn->dy_staticmember ) {
+ Entry *grp;
+ if ( overlay_entry_get_ov( op, &dyn->dy_name, NULL, NULL, 0, &grp, (slap_overinst *)op->o_bd->bd_info ) == LDAP_SUCCESS && grp ) {
+ Attribute *a = attr_find( grp->e_attrs, dyn->dy_staticmember );
+ if ( a ) {
+ i = value_find_ex( dyn->dy_staticmember, SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH, a->a_nvals, &e->e_nname, op->o_tmpmemctx );
+ }
+ overlay_entry_release_ov( op, grp, 0, (slap_overinst *)op->o_bd->bd_info );
+ return i == LDAP_SUCCESS ? LDAP_COMPARE_TRUE : LDAP_COMPARE_FALSE;
+ }
+ }
+ for (i=0; i<dyn->dy_numuris; i++) {
+ ludp = dyn->dy_uris[i];
+ nbase.bv_val = ludp->lud_dn;
+ nbase.bv_len = ludp->lud_port;
+ if ( ludp->lud_attrs )
+ continue;
+ switch( ludp->lud_scope ) {
+ case LDAP_SCOPE_BASE:
+ if ( !dn_match( &nbase, &e->e_nname ))
+ continue;
+ break;
+ case LDAP_SCOPE_ONELEVEL:
+ dnParent( &e->e_nname, &bv );
+ if ( !dn_match( &nbase, &bv ))
+ continue;
+ break;
+ case LDAP_SCOPE_SUBTREE:
+ if ( !dnIsSuffix( &e->e_nname, &nbase ))
+ continue;
+ break;
+ case LDAP_SCOPE_SUBORDINATE:
+ if ( dn_match( &nbase, &e->e_nname ) ||
+ !dnIsSuffix( &e->e_nname, &nbase ))
+ continue;
+ break;
+ }
+ if ( !ludp->lud_filter ) /* there really should always be a filter */
+ rc = LDAP_COMPARE_TRUE;
+ else
+ rc = test_filter( op, e, (Filter *)ludp->lud_filter );
+ if ( rc == LDAP_COMPARE_TRUE )
+ break;
+ }
+ return rc;
+}
+
+static void
+dynlist_add_memberOf(Operation *op, SlapReply *rs, dynlist_search_t *ds)
+{
+ TAvlnode *ptr;
+ Entry *e = rs->sr_entry;
+ dynlist_name_t *dyn;
+ Attribute *a;
+
+ /* See if there are any memberOf values to attach to this entry */
+ for ( ptr = ldap_tavl_end( ds->ds_names, TAVL_DIR_LEFT ); ptr;
+ ptr = ldap_tavl_next( ptr, TAVL_DIR_RIGHT )) {
+ dynlist_map_t *dlm;
+ dyn = ptr->avl_data;
+ for ( dlm = dyn->dy_dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
+ if ( dlm->dlm_memberOf_ad ) {
+ if ( dynlist_test_membership( op, dyn, e ) == LDAP_COMPARE_TRUE ) {
+ /* ensure e is modifiable, but do not replace
+ * sr_entry yet since we have pointers into it */
+ if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE ) && e == rs->sr_entry ) {
+ e = entry_dup( rs->sr_entry );
+ }
+ a = attr_find( e->e_attrs, dlm->dlm_memberOf_ad );
+ if ( a ) {
+ unsigned slot;
+ if ( attr_valfind( a, SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
+ &dyn->dy_name, &slot, NULL ) != LDAP_SUCCESS )
+ a = NULL;
+ }
+ if ( !a )
+ attr_merge_one( e, dlm->dlm_memberOf_ad, &dyn->dy_name, &dyn->dy_name );
+ if ( dyn->dy_sups ) {
+ dynlist_nested_memberOf( e, dlm->dlm_memberOf_ad, dyn->dy_sups );
+ }
+ break;
+ }
+ }
+ }
+ }
+ if ( e != rs->sr_entry ) {
+ rs_replace_entry( op, rs, (slap_overinst *)op->o_bd->bd_info, e );
+ rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED;
+ }
+}
+
+/* process the search responses */
+static int
+dynlist_search2resp( Operation *op, SlapReply *rs )
+{
+ dynlist_search_t *ds = op->o_callback->sc_private;
+ dynlist_name_t *dyn;
+ int rc;
+
+ if ( rs->sr_type == REP_SEARCH && rs->sr_entry != NULL ) {
+ rc = SLAP_CB_CONTINUE;
+ /* See if this is one of our dynamic entries */
+ dyn = ldap_tavl_find( ds->ds_names, &rs->sr_entry->e_nname, dynlist_avl_cmp );
+ if ( dyn ) {
+ dyn->dy_seen = 1;
+ rc = dynlist_prepare_entry( op, rs, dyn->dy_dli, dyn );
+ } else if ( ds->ds_want )
+ dynlist_add_memberOf( op, rs, ds );
+ if ( ds->ds_origfilter && test_filter( op, rs->sr_entry, ds->ds_origfilter ) != LDAP_COMPARE_TRUE ) {
+ rs_flush_entry( op, rs, NULL );
+ return LDAP_SUCCESS;
+ }
+ return rc;
+ } else if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS ) {
+ TAvlnode *ptr;
+ SlapReply r = *rs;
+ Filter *f = ds->ds_origfilter ? ds->ds_origfilter : op->ors_filter;
+
+ if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED )
+ return SLAP_CB_CONTINUE;
+
+ /* Check for any unexpanded dynamic group entries that weren't picked up
+ * by the original search filter.
+ */
+ for ( ptr = ldap_tavl_end( ds->ds_names, TAVL_DIR_LEFT ); ptr;
+ ptr = ldap_tavl_next( ptr, TAVL_DIR_RIGHT )) {
+ dyn = ptr->avl_data;
+ if ( dyn->dy_seen )
+ continue;
+ if ( !dnIsSuffixScope( &dyn->dy_name, &op->o_req_ndn, op->ors_scope ))
+ continue;
+ if ( overlay_entry_get_ov( op, &dyn->dy_name, NULL, NULL, 0, &r.sr_entry, (slap_overinst *)op->o_bd->bd_info ) != LDAP_SUCCESS ||
+ r.sr_entry == NULL )
+ continue;
+ r.sr_flags = REP_ENTRY_MUSTRELEASE;
+ dynlist_prepare_entry( op, &r, dyn->dy_dli, dyn );
+ if ( test_filter( op, r.sr_entry, f ) == LDAP_COMPARE_TRUE ) {
+ r.sr_attrs = op->ors_attrs;
+ rs->sr_err = send_search_entry( op, &r );
+ if ( rs->sr_err != LDAP_SUCCESS )
+ break;
+ } else {
+ rs_flush_entry( op, &r, NULL );
+ }
+ }
+ rs->sr_nentries = r.sr_nentries;
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static void
+dynlist_fix_filter( Operation *op, AttributeDescription *ad, dynlist_search_t *ds )
+{
+ Filter *f;
+ f = dynlist_filter_dup( op, op->ors_filter, ad, ds );
+ if ( ds->ds_origfilter ) {
+ dynlist_filter_free( op, op->ors_filter );
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ } else {
+ ds->ds_origfilter = op->ors_filter;
+ ds->ds_origfilterbv = op->ors_filterstr;
+ }
+ op->ors_filter = f;
+ filter2bv_x( op, f, &op->ors_filterstr );
+}
+
+typedef struct dynlist_link_t {
+ dynlist_search_t *dl_ds;
+ dynlist_name_t *dl_sup;
+} dynlist_link_t;
+
+static int
+dynlist_nestlink_dg( Operation *op, SlapReply *rs )
+{
+ dynlist_link_t *dll = op->o_callback->sc_private;
+ dynlist_search_t *ds = dll->dl_ds;
+ dynlist_name_t *di = dll->dl_sup, *dj;
+
+ if ( rs->sr_type != REP_SEARCH )
+ return LDAP_SUCCESS;
+
+ dj = ldap_tavl_find( dll->dl_ds->ds_names, &rs->sr_entry->e_nname, dynlist_avl_cmp );
+ if ( dj ) {
+ if ( ds->ds_want & WANT_MEMBEROF ) {
+ ldap_tavl_insert( &dj->dy_sups, di, dynlist_ptr_cmp, ldap_avl_dup_error );
+ }
+ if ( ds->ds_want & WANT_MEMBER ) {
+ ldap_tavl_insert( &di->dy_subs, dj, dynlist_ptr_cmp, ldap_avl_dup_error );
+ }
+ }
+ return LDAP_SUCCESS;
+}
+
+/* Connect all nested groups to their parents/children */
+static void
+dynlist_nestlink( Operation *op, dynlist_search_t *ds )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ dynlist_name_t *di, *dj;
+ TAvlnode *ptr;
+ Entry *e;
+ Attribute *a;
+ int i;
+
+ for ( ptr = ldap_tavl_end( ds->ds_names, TAVL_DIR_LEFT ); ptr;
+ ptr = ldap_tavl_next( ptr, TAVL_DIR_RIGHT )) {
+ di = ptr->avl_data;
+ if ( ds->ds_dlm ) {
+ if ( overlay_entry_get_ov( op, &di->dy_name, NULL, NULL, 0, &e, on ) != LDAP_SUCCESS || e == NULL )
+ continue;
+ a = attr_find( e->e_attrs, ds->ds_dlm->dlm_member_ad );
+ if ( a ) {
+ for ( i=0; i < a->a_numvals; i++ ) {
+ dj = ldap_tavl_find( ds->ds_names, &a->a_nvals[i], dynlist_avl_cmp );
+ if ( dj ) {
+ if ( ds->ds_want & WANT_MEMBEROF ) {
+ ldap_tavl_insert( &dj->dy_sups, di, dynlist_ptr_cmp, ldap_avl_dup_error );
+ }
+ if ( ds->ds_want & WANT_MEMBER ) {
+ ldap_tavl_insert( &di->dy_subs, dj, dynlist_ptr_cmp, ldap_avl_dup_error );
+ }
+ }
+ }
+ }
+ overlay_entry_release_ov( op, e, 0, on );
+ }
+
+ if ( di->dy_numuris ) {
+ slap_callback cb = { 0 };
+ dynlist_link_t dll;
+ dll.dl_ds = ds;
+ dll.dl_sup = di;
+ cb.sc_private = &dll;
+ cb.sc_response = dynlist_nestlink_dg;
+ dynlist_urlmembers( op, di, &cb );
+ }
+ }
+}
+
+static int
+dynlist_search( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ dynlist_gen_t *dlg = (dynlist_gen_t *)on->on_bi.bi_private;
+ dynlist_info_t *dli;
+ Operation o = *op;
+ dynlist_map_t *dlm;
+ Filter f[3];
+ AttributeAssertion ava[2];
+ AttributeName an[2] = {0};
+
+ slap_callback *sc;
+ dynlist_search_t *ds;
+ ObjectClass *static_oc;
+ int nested, found, tmpwant;
+ int opattrs, userattrs;
+
+ if ( get_manageDSAit( op ) )
+ return SLAP_CB_CONTINUE;
+
+ sc = op->o_tmpcalloc( 1, sizeof(slap_callback)+sizeof(dynlist_search_t), op->o_tmpmemctx );
+ sc->sc_private = (void *)(sc+1);
+ ds = sc->sc_private;
+
+ memset( o.o_ctrlflag, 0, sizeof( o.o_ctrlflag ));
+ o.o_managedsait = SLAP_CONTROL_CRITICAL;
+
+ /* Are we using memberOf, and does it affect this request? */
+ if ( dlg->dlg_memberOf ) {
+ int attrflags = slap_attr_flags( op->ors_attrs );
+ opattrs = SLAP_OPATTRS( attrflags );
+ userattrs = SLAP_USERATTRS( attrflags );
+ }
+
+ /* Find all groups in scope. For group expansion
+ * we only need the groups within the search scope, but
+ * for memberOf populating, we need all dyngroups.
+ */
+ for ( dli = dlg->dlg_dli; dli; dli = dli->dli_next ) {
+ static_oc = NULL;
+ nested = 0;
+ tmpwant = 0;
+ if ( dlg->dlg_memberOf ) {
+ for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
+ if ( dlm->dlm_memberOf_ad ) {
+ int want = 0;
+
+ /* is attribute in filter? */
+ if ( ad_infilter( dlm->dlm_memberOf_ad, op->ors_filter )) {
+ want |= WANT_MEMBEROF;
+ /* with nesting, filter attributes also require nestlink */
+ if ( dlm->dlm_memberOf_nested ) {
+ /* WANT_ flags have inverted meaning here:
+ * to satisfy (memberOf=) filter, we need to also
+ * find all subordinate groups. No special
+ * treatment is needed for (member=) since we
+ * already search all group entries.
+ */
+ want |= WANT_MEMBER;
+ }
+ }
+
+ /* if attribute is not requested, skip it */
+ if ( op->ors_attrs == NULL ) {
+ if ( !dlm->dlm_memberOf_oper ) {
+ want |= WANT_MEMBEROF;
+ if ( dlm->dlm_memberOf_nested && !dlm->dlm_member_oper )
+ want |= WANT_MEMBER;
+ }
+ } else {
+ if ( ad_inlist( dlm->dlm_memberOf_ad, op->ors_attrs )) {
+ want |= WANT_MEMBEROF;
+ if ( dlm->dlm_memberOf_nested && ad_inlist( dlm->dlm_member_ad, op->ors_attrs ))
+ want |= WANT_MEMBER;
+ } else {
+ if ( opattrs ) {
+ if ( dlm->dlm_memberOf_oper ) {
+ want |= WANT_MEMBEROF;
+ if ( dlm->dlm_memberOf_nested && dlm->dlm_member_oper )
+ want |= WANT_MEMBER;
+ }
+ }
+ if ( userattrs ) {
+ if ( !dlm->dlm_memberOf_oper ) {
+ want |= WANT_MEMBEROF;
+ if ( dlm->dlm_memberOf_nested && !dlm->dlm_member_oper )
+ want |= WANT_MEMBER;
+ }
+ }
+ }
+ }
+ if ( want ) {
+ nested = dlm->dlm_memberOf_nested;
+ ds->ds_want = tmpwant = want;
+ if ( dlm->dlm_static_oc ) {
+ static_oc = dlm->dlm_static_oc;
+ ds->ds_dlm = dlm;
+ }
+ }
+ }
+ }
+ }
+
+ if ( static_oc ) {
+ f[0].f_choice = LDAP_FILTER_OR;
+ f[0].f_list = &f[1];
+ f[0].f_next = NULL;
+ f[1].f_choice = LDAP_FILTER_EQUALITY;
+ f[1].f_next = &f[2];
+ f[1].f_ava = &ava[0];
+ f[1].f_av_desc = slap_schema.si_ad_objectClass;
+ f[1].f_av_value = dli->dli_oc->soc_cname;
+ f[2].f_choice = LDAP_FILTER_EQUALITY;
+ f[2].f_ava = &ava[1];
+ f[2].f_av_desc = slap_schema.si_ad_objectClass;
+ f[2].f_av_value = static_oc->soc_cname;
+ f[2].f_next = NULL;
+ } else {
+ f[0].f_choice = LDAP_FILTER_EQUALITY;
+ f[0].f_ava = ava;
+ f[0].f_av_desc = slap_schema.si_ad_objectClass;
+ f[0].f_av_value = dli->dli_oc->soc_cname;
+ f[0].f_next = NULL;
+ }
+
+ if ( o.o_callback != sc ) {
+ o.o_callback = sc;
+ o.ors_filter = f;
+ if ( tmpwant ) {
+ o.o_req_dn = op->o_bd->be_suffix[0];
+ o.o_req_ndn = op->o_bd->be_nsuffix[0];
+ o.ors_scope = LDAP_SCOPE_SUBTREE;
+ } else {
+ o.o_req_dn = op->o_req_dn;
+ o.o_req_ndn = op->o_req_ndn;
+ o.ors_scope = op->ors_scope;
+ }
+ o.ors_attrsonly = 0;
+ o.ors_attrs = an;
+ o.o_bd = select_backend( op->o_bd->be_nsuffix, 1 );
+ BER_BVZERO( &o.ors_filterstr );
+ sc->sc_response = dynlist_search1resp;
+ }
+
+ ds->ds_dli = dli;
+ if ( o.ors_filterstr.bv_val )
+ o.o_tmpfree( o.ors_filterstr.bv_val, o.o_tmpmemctx );
+ filter2bv_x( &o, f, &o.ors_filterstr );
+ an[0].an_desc = dli->dli_ad;
+ an[0].an_name = dli->dli_ad->ad_cname;
+ found = ds->ds_found;
+ {
+ SlapReply r = { REP_SEARCH };
+ (void)o.o_bd->be_search( &o, &r );
+ }
+ if ( found != ds->ds_found && nested )
+ dynlist_nestlink( op, ds );
+ }
+
+ if ( ds->ds_names != NULL ) {
+ sc->sc_response = dynlist_search2resp;
+ sc->sc_cleanup = dynlist_search_cleanup;
+ sc->sc_next = op->o_callback;
+ op->o_callback = sc;
+
+ /* see if filter needs fixing */
+ if ( dlg->dlg_memberOf ) {
+ for ( dli = dlg->dlg_dli; dli; dli = dli->dli_next ) {
+ for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
+ if ( dlm->dlm_memberOf_ad ) {
+
+ /* if attribute is in filter, fix it */
+ if ( ad_infilter( dlm->dlm_memberOf_ad, op->ors_filter )) {
+ ds->ds_dli = dli;
+ ds->ds_dlm = dlm;
+ dynlist_fix_filter( op, dlm->dlm_memberOf_ad, ds );
+ }
+ }
+ }
+ }
+ }
+
+ } else {
+ op->o_tmpfree( sc, op->o_tmpmemctx );
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+dynlist_build_def_filter( dynlist_info_t *dli )
+{
+ char *ptr;
+
+ dli->dli_default_filter.bv_len = STRLENOF( "(!(objectClass=" "))" )
+ + dli->dli_oc->soc_cname.bv_len;
+ dli->dli_default_filter.bv_val = ch_malloc( dli->dli_default_filter.bv_len + 1 );
+ if ( dli->dli_default_filter.bv_val == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "dynlist_db_open: malloc failed.\n" );
+ return -1;
+ }
+
+ ptr = lutil_strcopy( dli->dli_default_filter.bv_val, "(!(objectClass=" );
+ ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val );
+ ptr = lutil_strcopy( ptr, "))" );
+
+ assert( ptr == &dli->dli_default_filter.bv_val[dli->dli_default_filter.bv_len] );
+
+ return 0;
+}
+
+enum {
+ DL_ATTRSET = 1,
+ DL_ATTRPAIR,
+ DL_ATTRPAIR_COMPAT,
+ DL_LAST
+};
+
+static ConfigDriver dl_cfgen;
+
+/* XXXmanu 255 is the maximum arguments we allow. Can we go beyond? */
+static ConfigTable dlcfg[] = {
+ { "dynlist-attrset", "group-oc> [uri] <URL-ad> <[mapped:]member-ad> [...]",
+ 3, 0, 0, ARG_MAGIC|DL_ATTRSET, dl_cfgen,
+ "( OLcfgOvAt:8.1 NAME ( 'olcDynListAttrSet' 'olcDlAttrSet' ) "
+ "DESC 'Dynamic list: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "X-ORDERED 'VALUES' )",
+ NULL, NULL },
+ { "dynlist-attrpair", "member-ad> <URL-ad",
+ 3, 3, 0, ARG_MAGIC|DL_ATTRPAIR, dl_cfgen,
+ NULL, NULL, NULL },
+#ifdef TAKEOVER_DYNGROUP
+ { "attrpair", "member-ad> <URL-ad",
+ 3, 3, 0, ARG_MAGIC|DL_ATTRPAIR_COMPAT, dl_cfgen,
+ NULL, NULL, NULL },
+#endif
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs dlocs[] = {
+ { "( OLcfgOvOc:8.1 "
+ "NAME ( 'olcDynListConfig' 'olcDynamicList' ) "
+ "DESC 'Dynamic list configuration' "
+ "SUP olcOverlayConfig "
+ "MAY olcDynListAttrSet )",
+ Cft_Overlay, dlcfg, NULL, NULL },
+ { NULL, 0, NULL }
+};
+
+static int
+dl_cfgen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ dynlist_gen_t *dlg = (dynlist_gen_t *)on->on_bi.bi_private;
+ dynlist_info_t *dli = dlg->dlg_dli;
+
+ int rc = 0, i;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ switch( c->type ) {
+ case DL_ATTRSET:
+ for ( i = 0; dli; i++, dli = dli->dli_next ) {
+ struct berval bv;
+ char *ptr = c->cr_msg;
+ dynlist_map_t *dlm;
+
+ assert( dli->dli_oc != NULL );
+ assert( dli->dli_ad != NULL );
+
+ /* FIXME: check buffer overflow! */
+ ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ SLAP_X_ORDERED_FMT "%s", i,
+ dli->dli_oc->soc_cname.bv_val );
+
+ if ( !BER_BVISNULL( &dli->dli_uri ) ) {
+ *ptr++ = ' ';
+ *ptr++ = '"';
+ ptr = lutil_strncopy( ptr, dli->dli_uri.bv_val,
+ dli->dli_uri.bv_len );
+ *ptr++ = '"';
+ }
+
+ *ptr++ = ' ';
+ ptr = lutil_strncopy( ptr, dli->dli_ad->ad_cname.bv_val,
+ dli->dli_ad->ad_cname.bv_len );
+
+ for ( dlm = dli->dli_dlm; dlm; dlm = dlm->dlm_next ) {
+ ptr[ 0 ] = ' ';
+ ptr++;
+ if ( dlm->dlm_mapped_ad ) {
+ ptr = lutil_strcopy( ptr, dlm->dlm_mapped_ad->ad_cname.bv_val );
+ ptr[ 0 ] = ':';
+ ptr++;
+ }
+
+ ptr = lutil_strcopy( ptr, dlm->dlm_member_ad->ad_cname.bv_val );
+
+ if ( dlm->dlm_memberOf_ad ) {
+ *ptr++ = '+';
+ ptr = lutil_strcopy( ptr, dlm->dlm_memberOf_ad->ad_cname.bv_val );
+ if ( dlm->dlm_static_oc ) {
+ *ptr++ = '@';
+ ptr = lutil_strcopy( ptr, dlm->dlm_static_oc->soc_cname.bv_val );
+ }
+ if ( dlm->dlm_memberOf_nested ) {
+ *ptr++ = '*';
+ }
+ }
+ }
+
+ bv.bv_val = c->cr_msg;
+ bv.bv_len = ptr - bv.bv_val;
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
+
+ case DL_ATTRPAIR_COMPAT:
+ case DL_ATTRPAIR:
+ rc = 1;
+ break;
+
+ default:
+ rc = 1;
+ break;
+ }
+
+ return rc;
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ switch( c->type ) {
+ case DL_ATTRSET:
+ if ( c->valx < 0 ) {
+ dynlist_info_t *dli_next;
+
+ for ( dli_next = dli; dli_next; dli = dli_next ) {
+ dynlist_map_t *dlm = dli->dli_dlm;
+ dynlist_map_t *dlm_next;
+
+ dli_next = dli->dli_next;
+
+ if ( !BER_BVISNULL( &dli->dli_uri ) ) {
+ ch_free( dli->dli_uri.bv_val );
+ }
+
+ if ( dli->dli_lud != NULL ) {
+ ldap_free_urldesc( dli->dli_lud );
+ }
+
+ if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) {
+ ber_memfree( dli->dli_uri_nbase.bv_val );
+ }
+
+ if ( dli->dli_uri_filter != NULL ) {
+ filter_free( dli->dli_uri_filter );
+ }
+
+ ch_free( dli->dli_default_filter.bv_val );
+
+ while ( dlm != NULL ) {
+ dlm_next = dlm->dlm_next;
+ ch_free( dlm );
+ dlm = dlm_next;
+ }
+ ch_free( dli );
+ }
+
+ dlg->dlg_dli = NULL;
+ dlg->dlg_memberOf = 0;
+
+ } else {
+ dynlist_info_t **dlip;
+ dynlist_map_t *dlm;
+ dynlist_map_t *dlm_next;
+
+ for ( i = 0, dlip = (dynlist_info_t **)&dlg->dlg_dli;
+ i < c->valx; i++ )
+ {
+ if ( *dlip == NULL ) {
+ return 1;
+ }
+ dlip = &(*dlip)->dli_next;
+ }
+
+ dli = *dlip;
+ *dlip = dli->dli_next;
+
+ if ( !BER_BVISNULL( &dli->dli_uri ) ) {
+ ch_free( dli->dli_uri.bv_val );
+ }
+
+ if ( dli->dli_lud != NULL ) {
+ ldap_free_urldesc( dli->dli_lud );
+ }
+
+ if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) {
+ ber_memfree( dli->dli_uri_nbase.bv_val );
+ }
+
+ if ( dli->dli_uri_filter != NULL ) {
+ filter_free( dli->dli_uri_filter );
+ }
+
+ ch_free( dli->dli_default_filter.bv_val );
+
+ dlm = dli->dli_dlm;
+ while ( dlm != NULL ) {
+ dlm_next = dlm->dlm_next;
+ if ( dlm->dlm_memberOf_ad )
+ dlg->dlg_memberOf--;
+ ch_free( dlm );
+ dlm = dlm_next;
+ }
+ ch_free( dli );
+
+ dli = (dynlist_info_t *)dlg->dlg_dli;
+ }
+ break;
+
+ case DL_ATTRPAIR_COMPAT:
+ case DL_ATTRPAIR:
+ rc = 1;
+ break;
+
+ default:
+ rc = 1;
+ break;
+ }
+
+ return rc;
+ }
+
+ switch( c->type ) {
+ case DL_ATTRSET: {
+ dynlist_info_t **dlip,
+ *dli_next = NULL;
+ ObjectClass *oc = NULL;
+ AttributeDescription *ad = NULL;
+ int attridx = 2;
+ LDAPURLDesc *lud = NULL;
+ struct berval nbase = BER_BVNULL;
+ Filter *filter = NULL;
+ struct berval uri = BER_BVNULL;
+ dynlist_map_t *dlm = NULL, *dlml = NULL;
+ const char *text;
+
+ oc = oc_find( c->argv[ 1 ] );
+ if ( oc == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
+ "unable to find ObjectClass \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( strncasecmp( c->argv[ attridx ], "ldap://", STRLENOF("ldap://") ) == 0 ) {
+ if ( ldap_url_parse( c->argv[ attridx ], &lud ) != LDAP_URL_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
+ "unable to parse URI \"%s\"",
+ c->argv[ attridx ] );
+ rc = 1;
+ goto done_uri;
+ }
+
+ if ( lud->lud_host != NULL ) {
+ if ( lud->lud_host[0] == '\0' ) {
+ ch_free( lud->lud_host );
+ lud->lud_host = NULL;
+
+ } else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
+ "host not allowed in URI \"%s\"",
+ c->argv[ attridx ] );
+ rc = 1;
+ goto done_uri;
+ }
+ }
+
+ if ( lud->lud_attrs != NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
+ "attrs not allowed in URI \"%s\"",
+ c->argv[ attridx ] );
+ rc = 1;
+ goto done_uri;
+ }
+
+ if ( lud->lud_exts != NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
+ "extensions not allowed in URI \"%s\"",
+ c->argv[ attridx ] );
+ rc = 1;
+ goto done_uri;
+ }
+
+ if ( lud->lud_dn != NULL && lud->lud_dn[ 0 ] != '\0' ) {
+ struct berval dn;
+ ber_str2bv( lud->lud_dn, 0, 0, &dn );
+ rc = dnNormalize( 0, NULL, NULL, &dn, &nbase, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
+ "DN normalization failed in URI \"%s\"",
+ c->argv[ attridx ] );
+ goto done_uri;
+ }
+ }
+
+ if ( lud->lud_filter != NULL && lud->lud_filter[ 0 ] != '\0' ) {
+ filter = str2filter( lud->lud_filter );
+ if ( filter == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
+ "filter parsing failed in URI \"%s\"",
+ c->argv[ attridx ] );
+ rc = 1;
+ goto done_uri;
+ }
+ }
+
+ ber_str2bv( c->argv[ attridx ], 0, 1, &uri );
+
+done_uri:;
+ if ( rc ) {
+ if ( lud ) {
+ ldap_free_urldesc( lud );
+ }
+
+ if ( !BER_BVISNULL( &nbase ) ) {
+ ber_memfree( nbase.bv_val );
+ }
+
+ if ( filter != NULL ) {
+ filter_free( filter );
+ }
+
+ while ( dlm != NULL ) {
+ dlml = dlm;
+ dlm = dlm->dlm_next;
+ ch_free( dlml );
+ }
+
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+
+ return rc;
+ }
+
+ attridx++;
+ }
+
+ rc = slap_str2ad( c->argv[ attridx ], &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
+ "unable to find AttributeDescription \"%s\"",
+ c->argv[ attridx ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ rc = 1;
+ goto done_uri;
+ }
+
+ if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), DYNLIST_USAGE
+ "AttributeDescription \"%s\" "
+ "must be a subtype of \"labeledURI\"",
+ c->argv[ attridx ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ rc = 1;
+ goto done_uri;
+ }
+
+ attridx++;
+
+ for ( i = attridx; i < c->argc; i++ ) {
+ char *arg;
+ char *cp;
+ AttributeDescription *member_ad = NULL;
+ AttributeDescription *mapped_ad = NULL;
+ AttributeDescription *memberOf_ad = NULL;
+ ObjectClass *static_oc = NULL;
+ int nested = 0;
+ dynlist_map_t *dlmp;
+
+
+ /*
+ * If no mapped attribute is given, dn is used
+ * for backward compatibility.
+ */
+ arg = c->argv[i];
+ if ( ( cp = strchr( arg, ':' ) ) != NULL ) {
+ struct berval bv;
+ ber_str2bv( arg, cp - arg, 0, &bv );
+ rc = slap_bv2ad( &bv, &mapped_ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ DYNLIST_USAGE
+ "unable to find mapped AttributeDescription #%d \"%s\"\n",
+ i - 3, c->argv[ i ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ rc = 1;
+ goto done_uri;
+ }
+ arg = cp + 1;
+ }
+ if ( ( cp = strchr( arg, '+' ) ) != NULL ) {
+ struct berval bv;
+ char *ocp, *np;
+ np = strrchr( cp+1, '*' );
+ if ( np ) {
+ nested = 1;
+ *np = '\0';
+ }
+ ocp = strchr( cp+1, '@' );
+ if ( ocp ) {
+ static_oc = oc_find( ocp+1 );
+ if ( !static_oc ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ DYNLIST_USAGE
+ "unable to find static-oc ObjectClass #%d \"%s\"\n",
+ i - 3, c->argv[ i ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ rc = 1;
+ goto done_uri;
+ }
+ *ocp = '\0';
+ }
+ ber_str2bv( cp+1, 0, 0, &bv );
+ rc = slap_bv2ad( &bv, &memberOf_ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ DYNLIST_USAGE
+ "unable to find memberOf AttributeDescription #%d \"%s\"\n",
+ i - 3, c->argv[ i ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ rc = 1;
+ goto done_uri;
+ }
+ dlg->dlg_memberOf++;
+ *cp = '\0';
+ }
+
+ rc = slap_str2ad( arg, &member_ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ DYNLIST_USAGE
+ "unable to find AttributeDescription #%d \"%s\"\n",
+ i - 3, c->argv[ i ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ rc = 1;
+ goto done_uri;
+ }
+
+ dlmp = (dynlist_map_t *)ch_calloc( 1, sizeof( dynlist_map_t ) );
+ if ( dlm == NULL ) {
+ dlm = dlmp;
+ }
+ dlmp->dlm_member_ad = member_ad;
+ dlmp->dlm_mapped_ad = mapped_ad;
+ dlmp->dlm_memberOf_ad = memberOf_ad;
+ dlmp->dlm_static_oc = static_oc;
+ dlmp->dlm_memberOf_nested = nested;
+ dlmp->dlm_member_oper = is_at_operational( member_ad->ad_type );
+ if ( memberOf_ad ) {
+ dlmp->dlm_memberOf_oper = is_at_operational( memberOf_ad->ad_type );
+ } else {
+ dlmp->dlm_memberOf_oper = 0;
+ }
+ dlmp->dlm_next = NULL;
+
+ if ( dlml != NULL )
+ dlml->dlm_next = dlmp;
+ dlml = dlmp;
+ }
+
+ if ( c->valx > 0 ) {
+ int i;
+
+ for ( i = 0, dlip = (dynlist_info_t **)&dlg->dlg_dli;
+ i < c->valx; i++ )
+ {
+ if ( *dlip == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ DYNLIST_USAGE
+ "invalid index {%d}\n",
+ c->valx );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ rc = 1;
+ goto done_uri;
+ }
+ dlip = &(*dlip)->dli_next;
+ }
+ dli_next = *dlip;
+
+ } else {
+ for ( dlip = (dynlist_info_t **)&dlg->dlg_dli;
+ *dlip; dlip = &(*dlip)->dli_next )
+ /* goto last */;
+ }
+
+ *dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
+
+ (*dlip)->dli_oc = oc;
+ (*dlip)->dli_ad = ad;
+ (*dlip)->dli_dlm = dlm;
+ (*dlip)->dli_next = dli_next;
+
+ (*dlip)->dli_lud = lud;
+ (*dlip)->dli_uri_nbase = nbase;
+ (*dlip)->dli_uri_filter = filter;
+ (*dlip)->dli_uri = uri;
+
+ rc = dynlist_build_def_filter( *dlip );
+
+ } break;
+
+ case DL_ATTRPAIR_COMPAT:
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "warning: \"attrpair\" only supported for limited "
+ "backward compatibility with overlay \"dyngroup\"" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ /* fallthru */
+
+ case DL_ATTRPAIR: {
+ dynlist_info_t **dlip;
+ ObjectClass *oc = NULL;
+ AttributeDescription *ad = NULL,
+ *member_ad = NULL;
+ const char *text;
+
+ oc = oc_find( "groupOfURLs" );
+ if ( oc == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"dynlist-attrpair <member-ad> <URL-ad>\": "
+ "unable to find default ObjectClass \"groupOfURLs\"" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+ rc = slap_str2ad( c->argv[ 1 ], &member_ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"dynlist-attrpair <member-ad> <URL-ad>\": "
+ "unable to find AttributeDescription \"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+ rc = slap_str2ad( c->argv[ 2 ], &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"dynlist-attrpair <member-ad> <URL-ad>\": "
+ "unable to find AttributeDescription \"%s\"\n",
+ c->argv[ 2 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+ if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ DYNLIST_USAGE
+ "AttributeDescription \"%s\" "
+ "must be a subtype of \"labeledURI\"",
+ c->argv[ 2 ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+ for ( dlip = (dynlist_info_t **)&dlg->dlg_dli;
+ *dlip; dlip = &(*dlip)->dli_next )
+ {
+ /*
+ * The same URL attribute / member attribute pair
+ * cannot be repeated, but we enforce this only
+ * when the member attribute is unique. Performing
+ * the check for multiple values would require
+ * sorting and comparing the lists, which is left
+ * as a future improvement
+ */
+ if ( (*dlip)->dli_ad == ad &&
+ (*dlip)->dli_dlm->dlm_next == NULL &&
+ member_ad == (*dlip)->dli_dlm->dlm_member_ad ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "\"dynlist-attrpair <member-ad> <URL-ad>\": "
+ "URL attributeDescription \"%s\" already mapped.\n",
+ ad->ad_cname.bv_val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
+ c->log, c->cr_msg );
+#if 0
+ /* make it a warning... */
+ return 1;
+#endif
+ }
+ }
+
+ *dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
+
+ (*dlip)->dli_oc = oc;
+ (*dlip)->dli_ad = ad;
+ (*dlip)->dli_dlm = (dynlist_map_t *)ch_calloc( 1, sizeof( dynlist_map_t ) );
+ (*dlip)->dli_dlm->dlm_member_ad = member_ad;
+ (*dlip)->dli_dlm->dlm_mapped_ad = NULL;
+
+ rc = dynlist_build_def_filter( *dlip );
+
+ } break;
+
+ default:
+ rc = 1;
+ break;
+ }
+
+ return rc;
+}
+
+static int
+dynlist_db_init(
+ BackendDB *be,
+ ConfigReply *cr)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ dynlist_gen_t *dlg;
+
+ dlg = (dynlist_gen_t *)ch_malloc( sizeof( *dlg ));
+ on->on_bi.bi_private = dlg;
+ dlg->dlg_dli = NULL;
+ dlg->dlg_memberOf = 0;
+
+ return 0;
+}
+
+static int
+dynlist_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ dynlist_gen_t *dlg = (dynlist_gen_t *)on->on_bi.bi_private;
+ dynlist_info_t *dli = dlg->dlg_dli;
+ ObjectClass *oc = NULL;
+ AttributeDescription *ad = NULL;
+ const char *text;
+ int rc;
+
+ if ( dli == NULL ) {
+ dli = ch_calloc( 1, sizeof( dynlist_info_t ) );
+ dlg->dlg_dli = dli;
+ }
+
+ for ( ; dli; dli = dli->dli_next ) {
+ if ( dli->dli_oc == NULL ) {
+ if ( oc == NULL ) {
+ oc = oc_find( "groupOfURLs" );
+ if ( oc == NULL ) {
+ snprintf( cr->msg, sizeof( cr->msg),
+ "unable to fetch objectClass \"groupOfURLs\"" );
+ Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s.\n", cr->msg );
+ return 1;
+ }
+ }
+
+ dli->dli_oc = oc;
+ }
+
+ if ( dli->dli_ad == NULL ) {
+ if ( ad == NULL ) {
+ rc = slap_str2ad( "memberURL", &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( cr->msg, sizeof( cr->msg),
+ "unable to fetch attributeDescription \"memberURL\": %d (%s)",
+ rc, text );
+ Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s.\n", cr->msg );
+ return 1;
+ }
+ }
+
+ dli->dli_ad = ad;
+ }
+
+ if ( BER_BVISNULL( &dli->dli_default_filter ) ) {
+ rc = dynlist_build_def_filter( dli );
+ if ( rc != 0 ) {
+ return rc;
+ }
+ }
+ }
+
+ if ( ad_dgIdentity == NULL ) {
+ rc = slap_str2ad( "dgIdentity", &ad_dgIdentity, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( cr->msg, sizeof( cr->msg),
+ "unable to fetch attributeDescription \"dgIdentity\": %d (%s)",
+ rc, text );
+ Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s\n", cr->msg );
+ /* Just a warning */
+ }
+ }
+
+ if ( ad_dgAuthz == NULL ) {
+ rc = slap_str2ad( "dgAuthz", &ad_dgAuthz, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( cr->msg, sizeof( cr->msg),
+ "unable to fetch attributeDescription \"dgAuthz\": %d (%s)",
+ rc, text );
+ Debug( LDAP_DEBUG_ANY, "dynlist_db_open: %s\n", cr->msg );
+ /* Just a warning */
+ }
+ }
+
+ return 0;
+}
+
+static int
+dynlist_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+
+ if ( on->on_bi.bi_private ) {
+ dynlist_gen_t *dlg = (dynlist_gen_t *)on->on_bi.bi_private;
+ dynlist_info_t *dli = dlg->dlg_dli,
+ *dli_next;
+
+ for ( dli_next = dli; dli_next; dli = dli_next ) {
+ dynlist_map_t *dlm;
+ dynlist_map_t *dlm_next;
+
+ dli_next = dli->dli_next;
+
+ if ( !BER_BVISNULL( &dli->dli_uri ) ) {
+ ch_free( dli->dli_uri.bv_val );
+ }
+
+ if ( dli->dli_lud != NULL ) {
+ ldap_free_urldesc( dli->dli_lud );
+ }
+
+ if ( !BER_BVISNULL( &dli->dli_uri_nbase ) ) {
+ ber_memfree( dli->dli_uri_nbase.bv_val );
+ }
+
+ if ( dli->dli_uri_filter != NULL ) {
+ filter_free( dli->dli_uri_filter );
+ }
+
+ ch_free( dli->dli_default_filter.bv_val );
+
+ dlm = dli->dli_dlm;
+ while ( dlm != NULL ) {
+ dlm_next = dlm->dlm_next;
+ ch_free( dlm );
+ dlm = dlm_next;
+ }
+ ch_free( dli );
+ }
+ ch_free( dlg );
+ }
+
+ return 0;
+}
+
+static slap_overinst dynlist = { { NULL } };
+#ifdef TAKEOVER_DYNGROUP
+static char *obsolete_names[] = {
+ "dyngroup",
+ NULL
+};
+#endif
+
+#if SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC
+static
+#endif /* SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC */
+int
+dynlist_initialize(void)
+{
+ const char *text;
+ int rc = 0;
+
+ /* See if we need to define memberOf opattr */
+ rc = slap_str2ad( "memberOf", &ad_memberOf, &text );
+ if ( rc ) {
+ rc = register_at(
+ "( 1.2.840.113556.1.2.102 "
+ "NAME 'memberOf' "
+ "DESC 'Group that the entry belongs to' "
+ "SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' "
+ "EQUALITY distinguishedNameMatch " /* added */
+ "USAGE dSAOperation " /* added; questioned */
+ "NO-USER-MODIFICATION " /* added */
+ "X-ORIGIN 'iPlanet Delegated Administrator' )",
+ &ad_memberOf, 0 );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "dynlist_initialize: register_at (memberOf) failed\n" );
+ return rc;
+ }
+ }
+
+ dynlist.on_bi.bi_type = "dynlist";
+
+#ifdef TAKEOVER_DYNGROUP
+ /* makes dynlist incompatible with dyngroup */
+ dynlist.on_bi.bi_obsolete_names = obsolete_names;
+#endif
+
+ dynlist.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ dynlist.on_bi.bi_db_init = dynlist_db_init;
+ dynlist.on_bi.bi_db_config = config_generic_wrapper;
+ dynlist.on_bi.bi_db_open = dynlist_db_open;
+ dynlist.on_bi.bi_db_destroy = dynlist_db_destroy;
+
+ dynlist.on_bi.bi_op_search = dynlist_search;
+ dynlist.on_bi.bi_op_compare = dynlist_compare;
+
+ dynlist.on_bi.bi_cf_ocs = dlocs;
+
+ rc = config_register_schema( dlcfg, dlocs );
+ if ( rc ) {
+ return rc;
+ }
+
+ return overlay_register( &dynlist );
+}
+
+#if SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return dynlist_initialize();
+}
+#endif
+
+#endif /* SLAPD_OVER_DYNLIST */
diff --git a/servers/slapd/overlays/homedir.c b/servers/slapd/overlays/homedir.c
new file mode 100644
index 0000000..159090e
--- /dev/null
+++ b/servers/slapd/overlays/homedir.c
@@ -0,0 +1,2074 @@
+/* homedir.c - create/remove user home directories */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2009-2022 The OpenLDAP Foundation.
+ * Portions copyright 2009-2010 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Emily Backes at Symas
+ * Corp. for inclusion in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_HOMEDIR
+
+#define _FILE_OFFSET_BITS 64
+
+#include <stdio.h>
+#include <fcntl.h>
+
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/errno.h>
+#include <sys/stat.h>
+#include <ac/unistd.h>
+#include <ac/dirent.h>
+#include <ac/time.h>
+
+#include "slap.h"
+#include "slap-config.h"
+
+#define DEFAULT_MIN_UID ( 100 )
+#define DEFAULT_SKEL ( LDAP_DIRSEP "etc" LDAP_DIRSEP "skel" )
+
+typedef struct homedir_regexp {
+ char *match;
+ char *replace;
+ regex_t compiled;
+ struct homedir_regexp *next;
+} homedir_regexp;
+
+typedef enum {
+ DEL_IGNORE,
+ DEL_DELETE,
+ DEL_ARCHIVE
+} delete_style;
+
+typedef struct homedir_data {
+ char *skeleton_path;
+ unsigned min_uid;
+ AttributeDescription *home_ad;
+ AttributeDescription *uidn_ad;
+ AttributeDescription *gidn_ad;
+ homedir_regexp *regexps;
+ delete_style style;
+ char *archive_path;
+} homedir_data;
+
+typedef struct homedir_cb_data {
+ slap_overinst *on;
+ Entry *entry;
+} homedir_cb_data;
+
+typedef struct name_list {
+ char *name;
+ struct stat st;
+ struct name_list *next;
+} name_list;
+
+typedef struct name_list_list {
+ name_list *list;
+ struct name_list_list *next;
+} name_list_list;
+
+typedef enum {
+ TRAVERSE_CB_CONTINUE,
+ TRAVERSE_CB_DONE,
+ TRAVERSE_CB_FAIL
+} traverse_cb_ret;
+
+/* private, file info, context */
+typedef traverse_cb_ret (*traverse_cb_func)(
+ void *,
+ const char *,
+ const struct stat *,
+ void * );
+typedef struct traverse_cb {
+ traverse_cb_func pre_func;
+ traverse_cb_func post_func;
+ void *pre_private;
+ void *post_private;
+} traverse_cb;
+
+typedef struct copy_private {
+ int source_prefix_len;
+ const char *dest_prefix;
+ int dest_prefix_len;
+ uid_t uidn;
+ gid_t gidn;
+} copy_private;
+
+typedef struct chown_private {
+ uid_t old_uidn;
+ uid_t new_uidn;
+ gid_t old_gidn;
+ gid_t new_gidn;
+} chown_private;
+
+typedef struct ustar_header {
+ char name[100];
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char size[12];
+ char mtime[12];
+ char checksum[8];
+ char typeflag[1];
+ char linkname[100];
+ char magic[6];
+ char version[2];
+ char uname[32];
+ char gname[32];
+ char devmajor[8];
+ char devminor[8];
+ char prefix[155];
+ char pad[12];
+} ustar_header;
+
+typedef struct tar_private {
+ FILE *file;
+ const char *name;
+} tar_private;
+
+/* FIXME: This mutex really needs to be executable-global, but this
+ * will have to do for now.
+ */
+static ldap_pvt_thread_mutex_t readdir_mutex;
+static ConfigDriver homedir_regexp_cfg;
+static ConfigDriver homedir_style_cfg;
+static slap_overinst homedir;
+
+static ConfigTable homedircfg[] = {
+ { "homedir-skeleton-path", "pathname", 2, 2, 0,
+ ARG_STRING|ARG_OFFSET,
+ (void *)offsetof(homedir_data, skeleton_path),
+ "( OLcfgCtAt:8.1 "
+ "NAME 'olcSkeletonPath' "
+ "DESC 'Pathname for home directory skeleton template' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, { .v_string = DEFAULT_SKEL }
+ },
+
+ { "homedir-min-uidnumber", "uid number", 2, 2, 0,
+ ARG_UINT|ARG_OFFSET,
+ (void *)offsetof(homedir_data, min_uid),
+ "( OLcfgCtAt:8.2 "
+ "NAME 'olcMinimumUidNumber' "
+ "DESC 'Minimum uidNumber attribute to consider' "
+ "SYNTAX OMsInteger "
+ "SINGLE-VALUE )",
+ NULL, { .v_uint = DEFAULT_MIN_UID }
+ },
+
+ { "homedir-regexp", "regexp> <path", 3, 3, 0,
+ ARG_MAGIC,
+ homedir_regexp_cfg,
+ "( OLcfgCtAt:8.3 "
+ "NAME 'olcHomedirRegexp' "
+ "DESC 'Regular expression for matching and transforming paths' "
+ "SYNTAX OMsDirectoryString "
+ "X-ORDERED 'VALUES' )",
+ NULL, NULL
+ },
+
+ { "homedir-delete-style", "style", 2, 2, 0,
+ ARG_MAGIC,
+ homedir_style_cfg,
+ "( OLcfgCtAt:8.4 "
+ "NAME 'olcHomedirDeleteStyle' "
+ "DESC 'Action to perform when removing a home directory' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+
+ { "homedir-archive-path", "pathname", 2, 2, 0,
+ ARG_STRING|ARG_OFFSET,
+ (void *)offsetof(homedir_data, archive_path),
+ "( OLcfgCtAt:8.5 "
+ "NAME 'olcHomedirArchivePath' "
+ "DESC 'Pathname for home directory archival' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL
+ },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs homedirocs[] = {
+ { "( OLcfgCtOc:8.1 "
+ "NAME 'olcHomedirConfig' "
+ "DESC 'Homedir configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcSkeletonPath $ olcMinimumUidNumber "
+ "$ olcHomedirRegexp $ olcHomedirDeleteStyle "
+ "$ olcHomedirArchivePath ) )",
+ Cft_Overlay, homedircfg },
+
+ { NULL, 0, NULL }
+};
+
+static int
+homedir_regexp_cfg( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ homedir_data *data = (homedir_data *)on->on_bi.bi_private;
+ int rc = ARG_BAD_CONF;
+
+ assert( data != NULL );
+
+ switch ( c->op ) {
+ case SLAP_CONFIG_EMIT: {
+ int i;
+ homedir_regexp *r;
+ struct berval bv;
+ char buf[4096];
+
+ bv.bv_val = buf;
+ for ( i = 0, r = data->regexps; r != NULL; ++i, r = r->next ) {
+ bv.bv_len = snprintf( buf, sizeof(buf), "{%d}%s %s", i,
+ r->match, r->replace );
+ if ( bv.bv_len >= sizeof(buf) ) {
+ Debug( LDAP_DEBUG_ANY, "homedir_regexp_cfg: "
+ "emit serialization failed: size %lu\n",
+ (unsigned long)bv.bv_len );
+ return ARG_BAD_CONF;
+ }
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ rc = 0;
+ } break;
+
+ case LDAP_MOD_DELETE:
+ if ( c->valx < 0 ) { /* delete all values */
+ homedir_regexp *r, *rnext;
+
+ for ( r = data->regexps; r != NULL; r = rnext ) {
+ rnext = r->next;
+ ch_free( r->match );
+ ch_free( r->replace );
+ regfree( &r->compiled );
+ ch_free( r );
+ }
+ data->regexps = NULL;
+ rc = 0;
+
+ } else { /* delete value by index*/
+ homedir_regexp **rp, *r;
+ int i;
+
+ for ( i = 0, rp = &data->regexps; i < c->valx;
+ ++i, rp = &(*rp)->next )
+ ;
+
+ r = *rp;
+ *rp = r->next;
+ ch_free( r->match );
+ ch_free( r->replace );
+ regfree( &r->compiled );
+ ch_free( r );
+
+ rc = 0;
+ }
+ break;
+
+ case LDAP_MOD_ADD: /* fallthrough */
+ case SLAP_CONFIG_ADD: { /* add values */
+ char *match = c->argv[1];
+ char *replace = c->argv[2];
+ regex_t compiled;
+ homedir_regexp **rp, *r;
+
+ memset( &compiled, 0, sizeof(compiled) );
+ rc = regcomp( &compiled, match, REG_EXTENDED );
+ if ( rc ) {
+ regerror( rc, &compiled, c->cr_msg, sizeof(c->cr_msg) );
+ regfree( &compiled );
+ return ARG_BAD_CONF;
+ }
+
+ r = ch_calloc( 1, sizeof(homedir_regexp) );
+ r->match = strdup( match );
+ r->replace = strdup( replace );
+ r->compiled = compiled;
+
+ if ( c->valx == -1 ) { /* append */
+ for ( rp = &data->regexps; ( *rp ) != NULL;
+ rp = &(*rp)->next )
+ ;
+ *rp = r;
+
+ } else { /* insert at valx */
+ int i;
+ for ( i = 0, rp = &data->regexps; i < c->valx;
+ rp = &(*rp)->next, ++i )
+ ;
+ r->next = *rp;
+ *rp = r;
+ }
+ rc = 0;
+ break;
+ }
+ default:
+ abort();
+ }
+
+ return rc;
+}
+
+static int
+homedir_style_cfg( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ homedir_data *data = (homedir_data *)on->on_bi.bi_private;
+ int rc = ARG_BAD_CONF;
+ struct berval bv;
+
+ assert( data != NULL );
+
+ switch ( c->op ) {
+ case SLAP_CONFIG_EMIT:
+ bv.bv_val = data->style == DEL_IGNORE ? "IGNORE" :
+ data->style == DEL_DELETE ? "DELETE" :
+ "ARCHIVE";
+ bv.bv_len = strlen( bv.bv_val );
+ rc = value_add_one( &c->rvalue_vals, &bv );
+ if ( rc != 0 ) return ARG_BAD_CONF;
+ break;
+
+ case LDAP_MOD_DELETE:
+ data->style = DEL_IGNORE;
+ rc = 0;
+ break;
+
+ case LDAP_MOD_ADD: /* fallthrough */
+ case SLAP_CONFIG_ADD: /* add values */
+ if ( strcasecmp( c->argv[1], "IGNORE" ) == 0 )
+ data->style = DEL_IGNORE;
+ else if ( strcasecmp( c->argv[1], "DELETE" ) == 0 )
+ data->style = DEL_DELETE;
+ else if ( strcasecmp( c->argv[1], "ARCHIVE" ) == 0 )
+ data->style = DEL_ARCHIVE;
+ else {
+ Debug( LDAP_DEBUG_ANY, "homedir_style_cfg: "
+ "unrecognized style keyword\n" );
+ return ARG_BAD_CONF;
+ }
+ rc = 0;
+ break;
+
+ default:
+ abort();
+ }
+
+ return rc;
+}
+
+#define HOMEDIR_NULLWRAP(x) ( ( x ) == NULL ? "unknown" : (x) )
+static void
+report_errno( const char *parent_func, const char *func, const char *filename )
+{
+ int save_errno = errno;
+ char ebuf[1024];
+
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "%s: %s: \"%s\": %d (%s)\n",
+ HOMEDIR_NULLWRAP(parent_func), HOMEDIR_NULLWRAP(func),
+ HOMEDIR_NULLWRAP(filename), save_errno,
+ AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+}
+
+static int
+copy_link(
+ const char *dest_file,
+ const char *source_file,
+ const struct stat *st,
+ uid_t uidn,
+ gid_t gidn,
+ void *ctx )
+{
+ char *buf = NULL;
+ int rc;
+
+ assert( dest_file != NULL );
+ assert( source_file != NULL );
+ assert( st != NULL );
+ assert( (st->st_mode & S_IFMT) == S_IFLNK );
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "copy_link: %s to %s\n",
+ source_file, dest_file );
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "copy_link: %s uid %ld gid %ld\n",
+ dest_file, (long)uidn, (long)gidn );
+
+ /* calloc +1 for terminator */
+ buf = ber_memcalloc_x( 1, st->st_size + 1, ctx );
+ if ( buf == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "copy_link: alloc failed\n" );
+ return 1;
+ }
+ rc = readlink( source_file, buf, st->st_size );
+ if ( rc == -1 ) {
+ report_errno( "copy_link", "readlink", source_file );
+ goto fail;
+ }
+ rc = symlink( buf, dest_file );
+ if ( rc ) {
+ report_errno( "copy_link", "symlink", dest_file );
+ goto fail;
+ }
+ rc = lchown( dest_file, uidn, gidn );
+ if ( rc ) {
+ report_errno( "copy_link", "lchown", dest_file );
+ goto fail;
+ }
+ goto out;
+
+fail:
+ rc = 1;
+
+out:
+ if ( buf != NULL ) ber_memfree_x( buf, ctx );
+ return rc;
+}
+
+static int
+copy_blocks(
+ FILE *source,
+ FILE *dest,
+ const char *source_file,
+ const char *dest_file )
+{
+ char buf[4096];
+ size_t nread = 0;
+ int done = 0;
+
+ while ( !done ) {
+ nread = fread( buf, 1, sizeof(buf), source );
+ if ( nread == 0 ) {
+ if ( feof( source ) ) {
+ done = 1;
+ } else if ( ferror( source ) ) {
+ if ( source_file != NULL )
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "read error on %s\n",
+ source_file );
+ goto fail;
+ }
+ } else {
+ size_t nwritten = 0;
+ nwritten = fwrite( buf, 1, nread, dest );
+ if ( nwritten < nread ) {
+ if ( dest_file != NULL )
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "write error on %s\n",
+ dest_file );
+ goto fail;
+ }
+ }
+ }
+ return 0;
+fail:
+ return 1;
+}
+
+static int
+copy_file(
+ const char *dest_file,
+ const char *source_file,
+ uid_t uid,
+ gid_t gid,
+ int mode )
+{
+ FILE *source = NULL;
+ FILE *dest = NULL;
+ int rc;
+
+ assert( dest_file != NULL );
+ assert( source_file != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "copy_file: %s to %s mode 0%o\n",
+ source_file, dest_file, mode );
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "copy_file: %s uid %ld gid %ld\n",
+ dest_file, (long)uid, (long)gid );
+
+ source = fopen( source_file, "rb" );
+ if ( source == NULL ) {
+ report_errno( "copy_file", "fopen", source_file );
+ goto fail;
+ }
+ dest = fopen( dest_file, "wb" );
+ if ( dest == NULL ) {
+ report_errno( "copy_file", "fopen", dest_file );
+ goto fail;
+ }
+
+ rc = copy_blocks( source, dest, source_file, dest_file );
+ if ( rc != 0 ) goto fail;
+
+ fclose( source );
+ source = NULL;
+ rc = fclose( dest );
+ dest = NULL;
+ if ( rc != 0 ) {
+ report_errno( "copy_file", "fclose", dest_file );
+ goto fail;
+ }
+
+ /* set owner/permission */
+ rc = lchown( dest_file, uid, gid );
+ if ( rc != 0 ) {
+ report_errno( "copy_file", "lchown", dest_file );
+ goto fail;
+ }
+ rc = chmod( dest_file, mode );
+ if ( rc != 0 ) {
+ report_errno( "copy_file", "chmod", dest_file );
+ goto fail;
+ }
+
+ rc = 0;
+ goto out;
+fail:
+ rc = 1;
+out:
+ if ( source != NULL ) fclose( source );
+ if ( dest != NULL ) fclose( dest );
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "copy_file: %s to %s exit %d\n",
+ source_file, dest_file, rc );
+ return rc;
+}
+
+static void
+free_name_list( name_list *names, void *ctx )
+{
+ name_list *next;
+
+ while ( names != NULL ) {
+ next = names->next;
+ if ( names->name != NULL ) ber_memfree_x( names->name, ctx );
+ ber_memfree_x( names, ctx );
+ names = next;
+ }
+}
+
+static int
+grab_names( const char *dir_path, name_list **names, void *ctx )
+{
+ int locked = 0;
+ DIR *dir = NULL;
+ struct dirent *entry = NULL;
+ name_list **tail = NULL;
+ int dir_path_len = 0;
+ int rc = 0;
+
+ assert( dir_path != NULL );
+ assert( names != NULL );
+ assert( *names == NULL );
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "grab_names: %s\n", dir_path );
+
+ tail = names;
+ dir_path_len = strlen( dir_path );
+ ldap_pvt_thread_mutex_lock( &readdir_mutex );
+ locked = 1;
+
+ dir = opendir( dir_path );
+ if ( dir == NULL ) {
+ report_errno( "grab_names", "opendir", dir_path );
+ goto fail;
+ }
+
+ while ( ( entry = readdir( dir ) ) != NULL ) {
+ /* no d_namelen in ac/dirent.h */
+ int d_namelen = strlen( entry->d_name );
+ int full_len;
+
+ /* Skip . and .. */
+ if ( ( d_namelen == 1 && entry->d_name[0] == '.' ) ||
+ ( d_namelen == 2 && entry->d_name[0] == '.' &&
+ entry->d_name[1] == '.' ) ) {
+ continue;
+ }
+
+ *tail = ber_memcalloc_x( 1, sizeof(**tail), ctx );
+ if ( *tail == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "grab_names: list alloc failed\n" );
+ goto fail;
+ }
+ (*tail)->next = NULL;
+
+ /* +1 for dirsep, +1 for term */
+ full_len = dir_path_len + 1 + d_namelen + 1;
+ (*tail)->name = ber_memalloc_x( full_len, ctx );
+ if ( (*tail)->name == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "grab_names: name alloc failed\n" );
+ goto fail;
+ }
+ snprintf( (*tail)->name, full_len, "%s" LDAP_DIRSEP "%s",
+ dir_path, entry->d_name );
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "grab_names: found \"%s\"\n",
+ (*tail)->name );
+
+ rc = lstat( (*tail)->name, &(*tail)->st );
+ if ( rc ) {
+ report_errno( "grab_names", "lstat", (*tail)->name );
+ goto fail;
+ }
+
+ tail = &(*tail)->next;
+ }
+ closedir( dir );
+ ldap_pvt_thread_mutex_unlock( &readdir_mutex );
+ locked = 0;
+
+ dir = NULL;
+ goto success;
+
+success:
+ rc = 0;
+ goto out;
+fail:
+ rc = 1;
+ goto out;
+out:
+ if ( dir != NULL ) closedir( dir );
+ if ( locked ) ldap_pvt_thread_mutex_unlock( &readdir_mutex );
+ if ( rc != 0 && *names != NULL ) {
+ free_name_list( *names, ctx );
+ *names = NULL;
+ }
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "grab_names: %s exit %d\n",
+ dir_path, rc );
+ return rc;
+}
+
+static int
+traverse( const char *path, const traverse_cb *cb, void *ctx )
+{
+ name_list *next_name = NULL;
+ name_list_list *dir_stack = NULL;
+ name_list_list *next_dir;
+ int rc = 0;
+
+ assert( path != NULL );
+ assert( cb != NULL );
+ assert( cb->pre_func || cb->post_func );
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "traverse: %s\n", path );
+
+ dir_stack = ber_memcalloc_x( 1, sizeof(*dir_stack), ctx );
+ if ( dir_stack == NULL ) goto alloc_fail;
+ dir_stack->next = NULL;
+ dir_stack->list = ber_memcalloc_x( 1, sizeof(name_list), ctx );
+ if ( dir_stack->list == NULL ) goto alloc_fail;
+ rc = lstat( path, &dir_stack->list->st );
+ if ( rc != 0 ) {
+ report_errno( "traverse", "lstat", path );
+ goto fail;
+ }
+ dir_stack->list->next = NULL;
+ dir_stack->list->name = ber_strdup_x( path, ctx );
+ if ( dir_stack->list->name == NULL ) goto alloc_fail;
+
+ while ( dir_stack != NULL ) {
+ while ( dir_stack->list != NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "traverse: top of loop with \"%s\"\n",
+ dir_stack->list->name );
+
+ if ( cb->pre_func != NULL ) {
+ traverse_cb_ret cb_rc;
+ cb_rc = cb->pre_func( cb->pre_private, dir_stack->list->name,
+ &dir_stack->list->st, ctx );
+
+ if ( cb_rc == TRAVERSE_CB_DONE ) goto cb_done;
+ if ( cb_rc == TRAVERSE_CB_FAIL ) goto cb_fail;
+ }
+ if ( (dir_stack->list->st.st_mode & S_IFMT) == S_IFDIR ) {
+ /* push dir onto stack */
+ next_dir = dir_stack;
+ dir_stack = ber_memalloc_x( sizeof(*dir_stack), ctx );
+ if ( dir_stack == NULL ) {
+ dir_stack = next_dir;
+ goto alloc_fail;
+ }
+ dir_stack->list = NULL;
+ dir_stack->next = next_dir;
+ rc = grab_names(
+ dir_stack->next->list->name, &dir_stack->list, ctx );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "traverse: grab_names %s failed\n",
+ dir_stack->next->list->name );
+ goto fail;
+ }
+ } else {
+ /* just a file */
+ if ( cb->post_func != NULL ) {
+ traverse_cb_ret cb_rc;
+ cb_rc = cb->post_func( cb->post_private,
+ dir_stack->list->name, &dir_stack->list->st, ctx );
+
+ if ( cb_rc == TRAVERSE_CB_DONE ) goto cb_done;
+ if ( cb_rc == TRAVERSE_CB_FAIL ) goto cb_fail;
+ }
+ next_name = dir_stack->list->next;
+ ber_memfree_x( dir_stack->list->name, ctx );
+ ber_memfree_x( dir_stack->list, ctx );
+ dir_stack->list = next_name;
+ }
+ }
+ /* Time to pop a directory off the stack */
+ next_dir = dir_stack->next;
+ ber_memfree_x( dir_stack, ctx );
+ dir_stack = next_dir;
+ if ( dir_stack != NULL ) {
+ if ( cb->post_func != NULL ) {
+ traverse_cb_ret cb_rc;
+ cb_rc = cb->post_func( cb->post_private, dir_stack->list->name,
+ &dir_stack->list->st, ctx );
+
+ if ( cb_rc == TRAVERSE_CB_DONE ) goto cb_done;
+ if ( cb_rc == TRAVERSE_CB_FAIL ) goto cb_fail;
+ }
+ next_name = dir_stack->list->next;
+ ber_memfree_x( dir_stack->list->name, ctx );
+ ber_memfree_x( dir_stack->list, ctx );
+ dir_stack->list = next_name;
+ }
+ }
+
+ goto success;
+
+cb_done:
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "traverse: cb signaled completion\n" );
+success:
+ rc = 0;
+ goto out;
+
+cb_fail:
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "traverse: cb signaled failure\n" );
+ goto fail;
+alloc_fail:
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "traverse: allocation failed\n" );
+fail:
+ rc = 1;
+ goto out;
+
+out:
+ while ( dir_stack != NULL ) {
+ free_name_list( dir_stack->list, ctx );
+ next_dir = dir_stack->next;
+ ber_memfree_x( dir_stack, ctx );
+ dir_stack = next_dir;
+ }
+ return rc;
+}
+
+static traverse_cb_ret
+traverse_copy_pre(
+ void *private,
+ const char *name,
+ const struct stat *st,
+ void *ctx )
+{
+ copy_private *cp = private;
+ char *dest_name = NULL;
+ int source_name_len;
+ int dest_name_len;
+ int rc;
+
+ assert( private != NULL );
+ assert( name != NULL );
+ assert( st != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "traverse_copy_pre: %s entering\n",
+ name );
+
+ assert( cp->source_prefix_len >= 0 );
+ assert( cp->dest_prefix != NULL );
+ assert( cp->dest_prefix_len > 1 );
+
+ source_name_len = strlen( name );
+ assert( source_name_len >= cp->source_prefix_len );
+ /* +1 for terminator */
+ dest_name_len =
+ source_name_len + cp->dest_prefix_len - cp->source_prefix_len + 1;
+ dest_name = ber_memalloc_x( dest_name_len, ctx );
+ if ( dest_name == NULL ) goto alloc_fail;
+
+ snprintf( dest_name, dest_name_len, "%s%s", cp->dest_prefix,
+ name + cp->source_prefix_len );
+
+ switch ( st->st_mode & S_IFMT ) {
+ case S_IFDIR:
+ rc = mkdir( dest_name, st->st_mode & 06775 );
+ if ( rc ) {
+ int save_errno = errno;
+ switch ( save_errno ) {
+ case EEXIST:
+ /* directory already present; nothing to do */
+ goto exists;
+ break;
+ case ENOENT:
+ /* FIXME: should mkdir -p here */
+ /* fallthrough for now */
+ default:
+ report_errno( "traverse_copy_pre", "mkdir", dest_name );
+ goto fail;
+ }
+ }
+ rc = lchown( dest_name, cp->uidn, cp->gidn );
+ if ( rc ) {
+ report_errno( "traverse_copy_pre", "lchown", dest_name );
+ goto fail;
+ }
+ rc = chmod( dest_name, st->st_mode & 07777 );
+ if ( rc ) {
+ report_errno( "traverse_copy_pre", "chmod", dest_name );
+ goto fail;
+ }
+ break;
+ case S_IFREG:
+ rc = copy_file(
+ dest_name, name, cp->uidn, cp->gidn, st->st_mode & 07777 );
+ if ( rc ) goto fail;
+ break;
+ case S_IFIFO:
+ rc = mkfifo( dest_name, 0700 );
+ if ( rc ) {
+ report_errno( "traverse_copy_pre", "mkfifo", dest_name );
+ goto fail;
+ }
+ rc = lchown( dest_name, cp->uidn, cp->gidn );
+ if ( rc ) {
+ report_errno( "traverse_copy_pre", "lchown", dest_name );
+ goto fail;
+ }
+ rc = chmod( dest_name, st->st_mode & 07777 );
+ if ( rc ) {
+ report_errno( "traverse_copy_pre", "chmod", dest_name );
+ goto fail;
+ }
+ break;
+ case S_IFLNK:
+ rc = copy_link( dest_name, name, st, cp->uidn, cp->gidn, ctx );
+ if ( rc ) goto fail;
+ break;
+ default:
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "traverse_copy_pre: skipping special: %s\n",
+ name );
+ }
+
+ goto success;
+
+alloc_fail:
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "traverse_copy_pre: allocation failed\n" );
+fail:
+ rc = TRAVERSE_CB_FAIL;
+ goto out;
+
+exists:
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "traverse_copy_pre: \"%s\" already exists,"
+ " skipping the rest\n",
+ dest_name );
+ rc = TRAVERSE_CB_DONE;
+ goto out;
+
+success:
+ rc = TRAVERSE_CB_CONTINUE;
+out:
+ if ( dest_name != NULL ) ber_memfree_x( dest_name, ctx );
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "traverse_copy_pre: exit %d\n", rc );
+ return rc;
+}
+
+static int
+copy_tree(
+ const char *dest_path,
+ const char *source_path,
+ uid_t uidn,
+ gid_t gidn,
+ void *ctx )
+{
+ traverse_cb cb;
+ copy_private cp;
+ int rc;
+
+ assert( dest_path != NULL );
+ assert( source_path != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "copy_tree: %s to %s entering\n",
+ source_path, dest_path );
+
+ cb.pre_func = traverse_copy_pre;
+ cb.post_func = NULL;
+ cb.pre_private = &cp;
+ cb.post_private = NULL;
+
+ cp.source_prefix_len = strlen( source_path );
+ cp.dest_prefix = dest_path;
+ cp.dest_prefix_len = strlen( dest_path );
+ cp.uidn = uidn;
+ cp.gidn = gidn;
+
+ if ( cp.source_prefix_len <= cp.dest_prefix_len &&
+ strncmp( source_path, dest_path, cp.source_prefix_len ) == 0 &&
+ ( cp.source_prefix_len == cp.dest_prefix_len ||
+ dest_path[cp.source_prefix_len] == LDAP_DIRSEP[0] ) ) {
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "copy_tree: aborting: %s contains %s\n",
+ source_path, dest_path );
+ return 1;
+ }
+
+ rc = traverse( source_path, &cb, ctx );
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "copy_tree: %s exit %d\n", source_path,
+ rc );
+
+ return rc;
+}
+
+static int
+homedir_provision(
+ const char *dest_path,
+ const char *skel_path,
+ uid_t uidn,
+ gid_t gidn,
+ void *ctx )
+{
+ int rc;
+
+ assert( dest_path != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_provision: %s from skeleton %s\n",
+ dest_path, skel_path == NULL ? "(none)" : skel_path );
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_provision: %s uidn %ld gidn %ld\n",
+ dest_path, (long)uidn, (long)gidn );
+
+ if ( skel_path == NULL ) {
+ rc = mkdir( dest_path, 0700 );
+ if ( rc ) {
+ int save_errno = errno;
+ switch ( save_errno ) {
+ case EEXIST:
+ /* directory already present; nothing to do */
+ /* but down chown either */
+ rc = 0;
+ goto out;
+ break;
+ default:
+ report_errno( "provision_homedir", "mkdir", dest_path );
+ goto fail;
+ }
+ }
+ rc = lchown( dest_path, uidn, gidn );
+ if ( rc ) {
+ report_errno( "provision_homedir", "lchown", dest_path );
+ goto fail;
+ }
+
+ } else {
+ rc = copy_tree( dest_path, skel_path, uidn, gidn, ctx );
+ }
+
+ goto out;
+
+fail:
+ rc = 1;
+ goto out;
+out:
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_provision: %s to %s exit %d\n",
+ skel_path, dest_path, rc );
+ return rc;
+}
+
+/* traverse func for rm -rf */
+static traverse_cb_ret
+traverse_remove_post(
+ void *private,
+ const char *name,
+ const struct stat *st,
+ void *ctx )
+{
+ int rc;
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "traverse_remove_post: %s entering\n",
+ name );
+
+ if ( (st->st_mode & S_IFMT) == S_IFDIR ) {
+ rc = rmdir( name );
+ if ( rc != 0 ) {
+ report_errno( "traverse_remove_post", "rmdir", name );
+ goto fail;
+ }
+ } else {
+ rc = unlink( name );
+ if ( rc != 0 ) {
+ report_errno( "traverse_remove_post", "unlink", name );
+ goto fail;
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "traverse_remove_post: %s exit continue\n",
+ name );
+ return TRAVERSE_CB_CONTINUE;
+
+fail:
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "traverse_remove_post: %s exit failure\n",
+ name );
+ return TRAVERSE_CB_FAIL;
+}
+
+static int
+delete_tree( const char *path, void *ctx )
+{
+ const static traverse_cb cb = { NULL, traverse_remove_post, NULL, NULL };
+ int rc;
+
+ assert( path != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "delete_tree: %s entering\n", path );
+
+ rc = traverse( path, &cb, ctx );
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "delete_tree: %s exit %d\n", path, rc );
+
+ return rc;
+}
+
+static int
+get_tar_name(
+ const char *path,
+ const char *tar_path,
+ char *tar_name,
+ int name_size )
+{
+ int rc = 0;
+ const char *ch;
+ int fd = -1;
+ int counter = 0;
+ time_t now;
+
+ assert( path != NULL );
+ assert( tar_path != NULL );
+ assert( tar_name != NULL );
+
+ for ( ch = path + strlen( path );
+ *ch != LDAP_DIRSEP[0] && ch > path;
+ --ch )
+ ;
+ if ( ch <= path || strlen( ch ) < 2 ) {
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "get_tar_name: unable to construct a tar name from input "
+ "path \"%s\"\n",
+ path );
+ goto fail;
+ }
+ ++ch; /* skip past sep */
+ time( &now );
+
+ while ( fd < 0 ) {
+ snprintf( tar_name, name_size, "%s" LDAP_DIRSEP "%s-%ld-%d.tar",
+ tar_path, ch, (long)now, counter );
+ fd = open( tar_name, O_WRONLY|O_CREAT|O_EXCL, 0600 );
+ if ( fd < 0 ) {
+ int save_errno = errno;
+ if ( save_errno != EEXIST ) {
+ report_errno( "get_tar_name", "open", tar_name );
+ goto fail;
+ }
+ ++counter;
+ }
+ }
+
+ rc = 0;
+ goto out;
+
+fail:
+ rc = 1;
+ *tar_name = '\0';
+out:
+ if ( fd >= 0 ) close( fd );
+ return rc;
+}
+
+/* traverse func for rechown */
+static traverse_cb_ret
+traverse_chown_pre(
+ void *private,
+ const char *name,
+ const struct stat *st,
+ void *ctx )
+{
+ int rc;
+ chown_private *cp = private;
+ uid_t set_uidn = -1;
+ gid_t set_gidn = -1;
+
+ assert( private != NULL );
+ assert( name != NULL );
+ assert( st != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "traverse_chown_pre: %s entering\n",
+ name );
+
+ if ( st->st_uid == cp->old_uidn ) set_uidn = cp->new_uidn;
+ if ( st->st_gid == cp->old_gidn ) set_gidn = cp->new_gidn;
+
+ if ( set_uidn != (uid_t)-1 || set_gidn != (gid_t)-1 ) {
+ rc = lchown( name, set_uidn, set_gidn );
+ if ( rc ) {
+ report_errno( "traverse_chown_pre", "lchown", name );
+ goto fail;
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "traverse_chown_pre: %s exit continue\n",
+ name );
+ return TRAVERSE_CB_CONTINUE;
+
+fail:
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "traverse_chown_pre: %s exit failure\n",
+ name );
+ return TRAVERSE_CB_FAIL;
+}
+
+static int
+chown_tree(
+ const char *path,
+ uid_t old_uidn,
+ uid_t new_uidn,
+ gid_t old_gidn,
+ gid_t new_gidn,
+ void *ctx )
+{
+ traverse_cb cb;
+ chown_private cp;
+ int rc;
+
+ assert( path != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "chown_tree: %s entering\n", path );
+
+ cb.pre_func = traverse_chown_pre;
+ cb.post_func = NULL;
+ cb.pre_private = &cp;
+ cb.post_private = NULL;
+
+ cp.old_uidn = old_uidn;
+ cp.new_uidn = new_uidn;
+ cp.old_gidn = old_gidn;
+ cp.new_gidn = new_gidn;
+
+ rc = traverse( path, &cb, ctx );
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "chown_tree: %s exit %d\n", path, rc );
+
+ return rc;
+}
+
+static int
+homedir_rename( const char *source_path, const char *dest_path )
+{
+ int rc = 0;
+
+ assert( source_path != NULL );
+ assert( dest_path != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_rename: %s to %s\n",
+ source_path, dest_path );
+ rc = rename( source_path, dest_path );
+ if ( rc != 0 ) {
+ char ebuf[1024];
+ int save_errno = errno;
+
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "homedir_rename: rename(\"%s\", \"%s\"): (%s)\n",
+ source_path, dest_path,
+ AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_rename: %s to %s exit %d\n",
+ source_path, dest_path, rc );
+ return rc;
+}
+
+/* FIXME: This assumes ASCII; needs fixing for z/OS */
+static int
+tar_set_header( ustar_header *tar, const struct stat *st, const char *name )
+{
+ int name_len;
+ int rc;
+ const char *ch, *end;
+
+ assert( tar != NULL );
+ assert( st != NULL );
+ assert( name != NULL );
+ assert( sizeof(*tar) == 512 );
+ assert( sizeof(tar->name) == 100 );
+ assert( sizeof(tar->prefix) == 155 );
+ assert( sizeof(tar->checksum) == 8 );
+
+ memset( tar, 0, sizeof(*tar) );
+
+ assert( name[0] == LDAP_DIRSEP[0] );
+ name += 1; /* skip leading / */
+
+ name_len = strlen( name );
+
+ /* fits in tar->name? */
+ /* Yes, name and prefix do not need a trailing nul. */
+ if ( name_len <= 100 ) {
+ strncpy( tar->name, name, 100 );
+
+ /* try fit in tar->name + tar->prefix */
+ } else {
+ /* try to find something to stick into tar->name */
+ for ( ch = name + name_len - 100, end = name + name_len;
+ ch < end && *ch != LDAP_DIRSEP[0];
+ ++ch )
+ ;
+ if ( end - ch > 0 ) /* +1 skip past sep */
+ ch++;
+ else {
+ /* reset; name too long for UStar */
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "tar_set_header: name too long: \"%s\"\n",
+ name );
+ ch = name + name_len - 100;
+ }
+ strncpy( tar->name, ch + 1, 100 );
+ {
+ int prefix_len = ( ch - 1 ) - name;
+ if ( prefix_len > 155 ) prefix_len = 155;
+ strncpy( tar->prefix, name, prefix_len );
+ }
+ }
+
+ snprintf( tar->mode, 8, "%06lo ", (long)st->st_mode & 07777 );
+ snprintf( tar->uid, 8, "%06lo ", (long)st->st_uid );
+ snprintf( tar->gid, 8, "%06lo ", (long)st->st_gid );
+ snprintf( tar->mtime, 12, "%010lo ", (long)st->st_mtime );
+ snprintf( tar->size, 12, "%010lo ", (long)0 );
+ switch ( st->st_mode & S_IFMT ) {
+ case S_IFREG:
+ tar->typeflag[0] = '0';
+ snprintf( tar->size, 12, "%010lo ", (long)st->st_size );
+ break;
+ case S_IFLNK:
+ tar->typeflag[0] = '2';
+ rc = readlink( name - 1, tar->linkname, 99 );
+ if ( rc == -1 ) {
+ report_errno( "tar_set_header", "readlink", name );
+ goto fail;
+ }
+ break;
+ case S_IFCHR:
+ tar->typeflag[0] = '3';
+ /* FIXME: this is probably wrong but shouldn't likely be an issue */
+ snprintf( tar->devmajor, 8, "%06lo ", (long)st->st_rdev >> 16 );
+ snprintf( tar->devminor, 8, "%06lo ", (long)st->st_rdev & 0xffff );
+ break;
+ case S_IFBLK:
+ tar->typeflag[0] = '4';
+ /* FIXME: this is probably wrong but shouldn't likely be an issue */
+ snprintf( tar->devmajor, 8, "%06lo ", (long)st->st_rdev >> 16 );
+ snprintf( tar->devminor, 8, "%06lo ", (long)st->st_rdev & 0xffff );
+ break;
+ case S_IFDIR:
+ tar->typeflag[0] = '5';
+ break;
+ case S_IFIFO:
+ tar->typeflag[0] = '6';
+ break;
+ default:
+ goto fail;
+ }
+ snprintf( tar->magic, 6, "ustar" );
+ tar->version[0] = '0';
+ tar->version[1] = '0';
+
+ {
+ unsigned char *uch = (unsigned char *)tar;
+ unsigned char *uend = uch + 512;
+ unsigned long sum = 0;
+
+ memset( &tar->checksum, ' ', sizeof(tar->checksum) );
+
+ for ( ; uch < uend; ++uch )
+ sum += *uch;
+
+ /* zero-padded, six octal digits, followed by NUL then space (!) */
+ /* Yes, that's terminated exactly reverse of the others. */
+ snprintf( tar->checksum, sizeof(tar->checksum) - 1, "%06lo", sum );
+ }
+
+ return 0;
+fail:
+ return 1;
+}
+
+static traverse_cb_ret
+traverse_tar_pre(
+ void *private,
+ const char *name,
+ const struct stat *st,
+ void *ctx )
+{
+ int rc;
+ traverse_cb_ret cbrc;
+ tar_private *tp = private;
+ ustar_header tar;
+ FILE *source = NULL;
+
+ assert( private != NULL );
+ assert( name != NULL );
+ assert( st != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "traverse_tar_pre: %s entering\n", name );
+
+ switch ( st->st_mode & S_IFMT ) {
+ case S_IFREG:
+ if ( sizeof(st->st_size) > 4 && ( st->st_size >> 33 ) >= 1 ) {
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "traverse_tar_pre: %s is larger than 8GiB POSIX UStar "
+ "file size limit\n",
+ name );
+ goto fail;
+ }
+ /* fallthrough */
+ case S_IFDIR:
+ case S_IFLNK:
+ case S_IFIFO:
+ case S_IFCHR:
+ case S_IFBLK:
+ rc = tar_set_header( &tar, st, name );
+ if ( rc ) goto fail;
+ break;
+ default:
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "traverse_tar_pre: skipping \"%s\" mode %o\n",
+ name, st->st_mode );
+ goto done;
+ }
+
+ rc = fwrite( &tar, 1, 512, tp->file );
+ if ( rc != 512 ) {
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "traverse_tar_pre: write error in tar header\n" );
+ goto fail;
+ }
+
+ if ( (st->st_mode & S_IFMT) == S_IFREG ) {
+ source = fopen( name, "rb" );
+ if ( source == NULL ) {
+ report_errno( "traverse_tar_pre", "fopen", name );
+ goto fail;
+ }
+ rc = copy_blocks( source, tp->file, name, tp->name );
+ if ( rc != 0 ) goto fail;
+ fclose( source );
+ source = NULL;
+ }
+
+ { /* advance to end of record */
+ off_t pos = ftello( tp->file );
+ if ( pos == -1 ) {
+ report_errno( "traverse_tar_pre", "ftello", tp->name );
+ goto fail;
+ }
+ pos += ( 512 - ( pos % 512 ) ) % 512;
+ rc = fseeko( tp->file, pos, SEEK_SET );
+ if ( rc != 0 ) {
+ report_errno( "traverse_tar_pre", "fseeko", tp->name );
+ goto fail;
+ }
+ }
+
+done:
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "traverse_tar_pre: %s exit continue\n",
+ name );
+ cbrc = TRAVERSE_CB_CONTINUE;
+ goto out;
+fail:
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "traverse_tar_pre: %s exit failure\n",
+ name );
+ cbrc = TRAVERSE_CB_FAIL;
+
+out:
+ if ( source != NULL ) fclose( source );
+ return cbrc;
+}
+
+static int
+tar_tree( const char *path, const char *tar_name, void *ctx )
+{
+ traverse_cb cb;
+ tar_private tp;
+ int rc;
+
+ assert( path != NULL );
+ assert( tar_name != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "tar_tree: %s into %s entering\n", path,
+ tar_name );
+
+ cb.pre_func = traverse_tar_pre;
+ cb.post_func = NULL;
+ cb.pre_private = &tp;
+ cb.post_private = NULL;
+
+ tp.name = tar_name;
+ tp.file = fopen( tar_name, "wb" );
+ if ( tp.file == NULL ) {
+ report_errno( "tar_tree", "fopen", tar_name );
+ goto fail;
+ }
+
+ rc = traverse( path, &cb, ctx );
+ if ( rc != 0 ) goto fail;
+
+ {
+ off_t pos = ftello( tp.file );
+ if ( pos == -1 ) {
+ report_errno( "tar_tree", "ftello", tp.name );
+ goto fail;
+ }
+ pos += 1024; /* two zero records */
+ pos += ( 10240 - ( pos % 10240 ) ) % 10240;
+ rc = ftruncate( fileno( tp.file ), pos );
+ if ( rc != 0 ) {
+ report_errno( "tar_tree", "ftrunctate", tp.name );
+ goto fail;
+ }
+ }
+
+ rc = fclose( tp.file );
+ tp.file = NULL;
+ if ( rc != 0 ) {
+ report_errno( "tar_tree", "fclose", tp.name );
+ goto fail;
+ }
+ goto out;
+
+fail:
+ rc = 1;
+out:
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "tar_tree: %s exit %d\n", path, rc );
+ if ( tp.file != NULL ) fclose( tp.file );
+ return rc;
+}
+
+static int
+homedir_deprovision( const homedir_data *data, const char *path, void *ctx )
+{
+ int rc = 0;
+ char tar_name[1024];
+
+ assert( data != NULL );
+ assert( path != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_deprovision: %s entering\n",
+ path );
+
+ switch ( data->style ) {
+ case DEL_IGNORE:
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_deprovision: style is ignore\n" );
+ break;
+ case DEL_ARCHIVE:
+ if ( data->archive_path == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "homedir_deprovision: archive path not set\n" );
+ goto fail;
+ }
+ rc = get_tar_name( path, data->archive_path, tar_name, 1024 );
+ if ( rc != 0 ) goto fail;
+ rc = tar_tree( path, tar_name, ctx );
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "homedir_deprovision: archive failed, not deleting\n" );
+ goto fail;
+ }
+ /* fall-through */
+ case DEL_DELETE:
+ rc = delete_tree( path, ctx );
+ break;
+ default:
+ abort();
+ }
+
+ rc = 0;
+ goto out;
+
+fail:
+ rc = 1;
+out:
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_deprovision: %s leaving\n",
+ path );
+
+ return rc;
+}
+
+/* FIXME: This assumes ASCII; needs fixing for z/OS */
+/* FIXME: This should also be in a slapd library function somewhere */
+#define MAX_MATCHES ( 10 )
+static int
+homedir_match(
+ const homedir_regexp *r,
+ const char *homedir,
+ char *result,
+ size_t result_size )
+{
+ int rc;
+ int n;
+ regmatch_t matches[MAX_MATCHES];
+ char *resc, *repc;
+
+ assert( r != NULL );
+ assert( homedir != NULL );
+ assert( result_size > 1 );
+
+ memset( matches, 0, sizeof(matches) );
+ rc = regexec( &r->compiled, homedir, MAX_MATCHES, matches, 0 );
+ if ( rc ) {
+ if ( rc != REG_NOMATCH ) {
+ char msg[256];
+ regerror( rc, &r->compiled, msg, sizeof(msg) );
+ Debug( LDAP_DEBUG_ANY, "homedir_match: "
+ "%s\n", msg );
+ }
+ return rc;
+ }
+
+ for ( resc = result, repc = r->replace;
+ result_size > 1 && *repc != '\0';
+ ++repc, ++resc, --result_size ) {
+ switch ( *repc ) {
+ case '$':
+ ++repc;
+ n = ( *repc ) - '0';
+ if ( n < 0 || n > ( MAX_MATCHES - 1 ) ||
+ matches[n].rm_so < 0 ) {
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "invalid regex term expansion in \"%s\" "
+ "at char %ld, n is %d\n",
+ r->replace, (long)( repc - r->replace ), n );
+ return 1;
+ }
+ {
+ size_t match_len = matches[n].rm_eo - matches[n].rm_so;
+ const char *match_start = homedir + matches[n].rm_so;
+ if ( match_len >= result_size ) goto too_long;
+
+ memcpy( resc, match_start, match_len );
+ result_size -= match_len;
+ resc += match_len - 1;
+ }
+ break;
+
+ case '\\':
+ ++repc;
+ /* fallthrough */
+
+ default:
+ *resc = *repc;
+ }
+ }
+ *resc = '\0';
+ if ( *repc != '\0' ) goto too_long;
+
+ return 0;
+
+too_long:
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "regex expansion of %s too long\n",
+ r->replace );
+ *result = '\0';
+ return 1;
+}
+
+/* Sift through an entry for interesting values
+ * return 0 on success and set vars
+ * return 1 if homedir is not present or not valid
+ * sets presence if any homedir attributes are noticed
+ */
+static int
+harvest_values(
+ const homedir_data *data,
+ const Entry *e,
+ char *home_buf,
+ int home_buf_size,
+ uid_t *uidn,
+ gid_t *gidn,
+ int *presence )
+{
+ Attribute *a;
+ char *homedir = NULL;
+
+ assert( data != NULL );
+ assert( e != NULL );
+ assert( home_buf != NULL );
+ assert( home_buf_size > 1 );
+ assert( uidn != NULL );
+ assert( gidn != NULL );
+ assert( presence != NULL );
+
+ *presence = 0;
+ if ( e == NULL ) return 1;
+ *uidn = 0;
+ *gidn = 0;
+
+ for ( a = e->e_attrs; a->a_next != NULL; a = a->a_next ) {
+ if ( a->a_desc == data->home_ad ) {
+ homedir = a->a_vals[0].bv_val;
+ *presence = 1;
+ } else if ( a->a_desc == data->uidn_ad ) {
+ *uidn = (uid_t)strtol( a->a_vals[0].bv_val, NULL, 10 );
+ *presence = 1;
+ } else if ( a->a_desc == data->gidn_ad ) {
+ *gidn = (gid_t)strtol( a->a_vals[0].bv_val, NULL, 10 );
+ *presence = 1;
+ }
+ }
+ if ( homedir != NULL ) {
+ homedir_regexp *r;
+
+ for ( r = data->regexps; r != NULL; r = r->next ) {
+ int rc = homedir_match( r, homedir, home_buf, home_buf_size );
+ if ( rc == 0 ) return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int
+homedir_mod_cleanup( Operation *op, SlapReply *rs )
+{
+ slap_callback *cb = NULL;
+ slap_callback **cbp = NULL;
+ homedir_cb_data *cb_data = NULL;
+ Entry *e = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_mod_cleanup: entering\n" );
+
+ for ( cbp = &op->o_callback;
+ *cbp != NULL && (*cbp)->sc_cleanup != homedir_mod_cleanup;
+ cbp = &(*cbp)->sc_next )
+ ;
+
+ if ( *cbp == NULL ) goto out;
+ cb = *cbp;
+
+ cb_data = (homedir_cb_data *)cb->sc_private;
+ e = cb_data->entry;
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_mod_cleanup: found <%s>\n",
+ e->e_nname.bv_val );
+ entry_free( e );
+ op->o_tmpfree( cb_data, op->o_tmpmemctx );
+ *cbp = cb->sc_next;
+ op->o_tmpfree( cb, op->o_tmpmemctx );
+
+out:
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_mod_cleanup: leaving\n" );
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+homedir_mod_response( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = NULL;
+ homedir_data *data = NULL;
+ slap_callback *cb = NULL;
+ homedir_cb_data *cb_data = NULL;
+ Entry *e = NULL;
+ int rc = SLAP_CB_CONTINUE;
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_mod_response: entering\n" );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_mod_response: op was not successful\n" );
+ goto out;
+ }
+
+ /* Retrieve stashed entry */
+ for ( cb = op->o_callback;
+ cb != NULL && cb->sc_cleanup != homedir_mod_cleanup;
+ cb = cb->sc_next )
+ ;
+ if ( cb == NULL ) goto out;
+ cb_data = (homedir_cb_data *)cb->sc_private;
+ e = cb_data->entry;
+ on = cb_data->on;
+ data = on->on_bi.bi_private;
+ assert( e != NULL );
+ assert( data != NULL );
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_mod_response: found <%s>\n",
+ e->e_nname.bv_val );
+
+ switch ( op->o_tag ) {
+ case LDAP_REQ_DELETE: {
+ char home_buf[1024];
+ uid_t uidn = 0;
+ gid_t gidn = 0;
+ int presence;
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_mod_response: successful delete found\n" );
+ rc = harvest_values( data, e, home_buf, sizeof(home_buf), &uidn,
+ &gidn, &presence );
+ if ( rc == 0 && uidn >= data->min_uid ) {
+ homedir_deprovision( data, home_buf, op->o_tmpmemctx );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_mod_response: skipping\n" );
+ }
+ rc = SLAP_CB_CONTINUE;
+ break;
+ }
+
+ case LDAP_REQ_MODIFY:
+ case LDAP_REQ_MODRDN: {
+ Operation nop = *op;
+ Entry *old_entry = e;
+ Entry *new_entry = NULL;
+ Entry *etmp;
+ char old_home[1024];
+ char new_home[1024];
+ uid_t old_uidn, new_uidn;
+ uid_t old_gidn, new_gidn;
+ int old_valid = 0;
+ int new_valid = 0;
+ int old_presence, new_presence;
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_mod_response: successful modify/modrdn found\n" );
+
+ /* retrieve the revised entry */
+ nop.o_bd = on->on_info->oi_origdb;
+ rc = overlay_entry_get_ov(
+ &nop, &op->o_req_ndn, NULL, NULL, 0, &etmp, on );
+ if ( etmp != NULL ) {
+ new_entry = entry_dup( etmp );
+ overlay_entry_release_ov( &nop, etmp, 0, on );
+ }
+ if ( rc || new_entry == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "homedir_mod_response: unable to get revised <%s>\n",
+ op->o_req_ndn.bv_val );
+ if ( new_entry != NULL ) {
+ entry_free( new_entry );
+ new_entry = NULL;
+ }
+ }
+
+ /* analyze old and new */
+ rc = harvest_values( data, old_entry, old_home, 1024, &old_uidn,
+ &old_gidn, &old_presence );
+ if ( rc == 0 && old_uidn >= data->min_uid ) old_valid = 1;
+ if ( new_entry != NULL ) {
+ rc = harvest_values( data, new_entry, new_home, 1024, &new_uidn,
+ &new_gidn, &new_presence );
+ if ( rc == 0 && new_uidn >= data->min_uid ) new_valid = 1;
+ entry_free( new_entry );
+ new_entry = NULL;
+ }
+
+ if ( new_valid && !old_valid ) { /* like an add */
+ if ( old_presence )
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_mod_response: old entry is now valid\n" );
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_mod_response: treating like an add\n" );
+ homedir_provision( new_home, data->skeleton_path, new_uidn,
+ new_gidn, op->o_tmpmemctx );
+
+ } else if ( old_valid && !new_valid &&
+ !new_presence ) { /* like a del */
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_mod_response: treating like a del\n" );
+ homedir_deprovision( data, old_home, op->o_tmpmemctx );
+
+ } else if ( new_valid && old_valid ) { /* change */
+ int did_something = 0;
+
+ if ( strcmp( old_home, new_home ) != 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_mod_response: treating like a rename\n" );
+ homedir_rename( old_home, new_home );
+ did_something = 1;
+ }
+ if ( old_uidn != new_uidn || old_gidn != new_gidn ) {
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "homedir_mod_response: rechowning\n" );
+ chown_tree( new_home, old_uidn, new_uidn, old_gidn,
+ new_gidn, op->o_tmpmemctx );
+ did_something = 1;
+ }
+ if ( !did_something ) {
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_mod_response: nothing to do\n" );
+ }
+ } else if ( old_presence || new_presence ) {
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "homedir_mod_response: <%s> values present "
+ "but invalid; ignoring\n",
+ op->o_req_ndn.bv_val );
+ }
+ rc = SLAP_CB_CONTINUE;
+ break;
+ }
+
+ default:
+ rc = SLAP_CB_CONTINUE;
+ }
+
+out:
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_mod_response: leaving\n" );
+ return rc;
+}
+
+static int
+homedir_op_mod( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ slap_callback *cb = NULL;
+ homedir_cb_data *cb_data = NULL;
+ Entry *e = NULL;
+ Entry *se = NULL;
+ Operation nop = *op;
+ int rc;
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_op_mod: entering\n" );
+
+ /* retrieve the entry */
+ nop.o_bd = on->on_info->oi_origdb;
+ rc = overlay_entry_get_ov( &nop, &op->o_req_ndn, NULL, NULL, 0, &e, on );
+ if ( e != NULL ) {
+ se = entry_dup( e );
+ overlay_entry_release_ov( &nop, e, 0, on );
+ e = se;
+ }
+ if ( rc || e == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "homedir_op_mod: unable to get <%s>\n",
+ op->o_req_ndn.bv_val );
+ goto out;
+ }
+
+ /* Allocate the callback to hold the entry */
+ cb = op->o_tmpalloc( sizeof(slap_callback), op->o_tmpmemctx );
+ cb_data = op->o_tmpalloc( sizeof(homedir_cb_data), op->o_tmpmemctx );
+ cb->sc_cleanup = homedir_mod_cleanup;
+ cb->sc_response = homedir_mod_response;
+ cb->sc_private = cb_data;
+ cb_data->entry = e;
+ e = NULL;
+ cb_data->on = on;
+ cb->sc_next = op->o_callback;
+ op->o_callback = cb;
+
+out:
+ if ( e != NULL ) entry_free( e );
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_op_mod: leaving\n" );
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+homedir_response( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ homedir_data *data = on->on_bi.bi_private;
+
+ Debug( LDAP_DEBUG_TRACE, "homedir: "
+ "homedir_response: entering\n" );
+ if ( rs->sr_err != LDAP_SUCCESS || data == NULL ) return SLAP_CB_CONTINUE;
+
+ switch ( op->o_tag ) {
+ case LDAP_REQ_ADD: { /* Check for new homedir */
+ char home_buf[1024];
+ uid_t uidn = 0;
+ gid_t gidn = 0;
+ int rc, presence;
+
+ rc = harvest_values( data, op->ora_e, home_buf, sizeof(home_buf),
+ &uidn, &gidn, &presence );
+ if ( rc == 0 && uidn >= data->min_uid ) {
+ homedir_provision( home_buf, data->skeleton_path, uidn, gidn,
+ op->o_tmpmemctx );
+ }
+ return SLAP_CB_CONTINUE;
+ }
+
+ default:
+ return SLAP_CB_CONTINUE;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+homedir_db_init( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ homedir_data *data = ch_calloc( 1, sizeof(homedir_data) );
+ const char *text;
+
+ if ( slap_str2ad( "homeDirectory", &data->home_ad, &text ) ||
+ slap_str2ad( "uidNumber", &data->uidn_ad, &text ) ||
+ slap_str2ad( "gidNumber", &data->gidn_ad, &text ) ) {
+ Debug( LDAP_DEBUG_ANY, "homedir: "
+ "nis schema not available\n" );
+ return 1;
+ }
+
+ data->skeleton_path = strdup( DEFAULT_SKEL );
+ data->min_uid = DEFAULT_MIN_UID;
+ data->archive_path = NULL;
+
+ on->on_bi.bi_private = data;
+ return 0;
+}
+
+static int
+homedir_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ homedir_data *data = on->on_bi.bi_private;
+ homedir_regexp *r, *rnext;
+
+ if ( data != NULL ) {
+ for ( r = data->regexps; r != NULL; r = rnext ) {
+ rnext = r->next;
+ ch_free( r->match );
+ ch_free( r->replace );
+ regfree( &r->compiled );
+ ch_free( r );
+ }
+ data->regexps = NULL;
+ if ( data->skeleton_path != NULL ) ch_free( data->skeleton_path );
+ if ( data->archive_path != NULL ) ch_free( data->archive_path );
+ ch_free( data );
+ }
+
+ return 0;
+}
+
+int
+homedir_initialize()
+{
+ int rc;
+
+ assert( ' ' == 32 ); /* Lots of ASCII requirements for now */
+
+ memset( &homedir, 0, sizeof(homedir) );
+
+ homedir.on_bi.bi_type = "homedir";
+ homedir.on_bi.bi_db_init = homedir_db_init;
+ homedir.on_bi.bi_db_destroy = homedir_db_destroy;
+ homedir.on_bi.bi_op_delete = homedir_op_mod;
+ homedir.on_bi.bi_op_modify = homedir_op_mod;
+ homedir.on_response = homedir_response;
+
+ homedir.on_bi.bi_cf_ocs = homedirocs;
+ rc = config_register_schema( homedircfg, homedirocs );
+ if ( rc ) return rc;
+
+ ldap_pvt_thread_mutex_init( &readdir_mutex );
+
+ return overlay_register( &homedir );
+}
+
+int
+homedir_terminate()
+{
+ ldap_pvt_thread_mutex_destroy( &readdir_mutex );
+ return 0;
+}
+
+#if SLAPD_OVER_HOMEDIR == SLAPD_MOD_DYNAMIC && defined(PIC)
+int
+init_module( int argc, char *argv[] )
+{
+ return homedir_initialize();
+}
+
+int
+term_module()
+{
+ return homedir_terminate();
+}
+#endif
+
+#endif /* SLAPD_OVER_HOMEDIR */
diff --git a/servers/slapd/overlays/memberof.c b/servers/slapd/overlays/memberof.c
new file mode 100644
index 0000000..d76f8f4
--- /dev/null
+++ b/servers/slapd/overlays/memberof.c
@@ -0,0 +1,2209 @@
+/* memberof.c - back-reference for group membership */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2007 Pierangelo Masarati <ando@sys-net.it>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software, sponsored by SysNet s.r.l.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_MEMBEROF
+
+#include <stdio.h>
+
+#include "ac/string.h"
+#include "ac/socket.h"
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+/*
+ * Glossary:
+ *
+ * GROUP a group object (an entry with GROUP_OC
+ * objectClass)
+ * MEMBER a member object (an entry whose DN is
+ * listed as MEMBER_AT value of a GROUP)
+ * GROUP_OC the objectClass of the group object
+ * (default: groupOfNames)
+ * MEMBER_AT the membership attribute, DN-valued;
+ * note: nameAndOptionalUID is tolerated
+ * as soon as the optionalUID is absent
+ * (default: member)
+ * MEMBER_OF reverse membership attribute
+ * (default: memberOf)
+ *
+ * - add:
+ * - if the entry that is being added is a GROUP,
+ * the MEMBER_AT defined as values of the add operation
+ * get the MEMBER_OF value directly from the request.
+ *
+ * if configured to do so, the MEMBER objects do not exist,
+ * and no relax control is issued, either:
+ * - fail
+ * - drop non-existing members
+ * (by default: don't muck with values)
+ *
+ * - if (configured to do so,) the referenced GROUP exists,
+ * the relax control is set and the user has
+ * "manage" privileges, allow to add MEMBER_OF values to
+ * generic entries.
+ *
+ * - modify:
+ * - if the entry being modified is a GROUP_OC and the
+ * MEMBER_AT attribute is modified, the MEMBER_OF value
+ * of the (existing) MEMBER_AT entries that are affected
+ * is modified according to the request:
+ * - if a MEMBER is removed from the group,
+ * delete the corresponding MEMBER_OF
+ * - if a MEMBER is added to a group,
+ * add the corresponding MEMBER_OF
+ *
+ * We need to determine, from the database, if it is
+ * a GROUP_OC, and we need to check, from the
+ * modification list, if the MEMBER_AT attribute is being
+ * affected, and what MEMBER_AT values are affected.
+ *
+ * if configured to do so, the entries corresponding to
+ * the MEMBER_AT values do not exist, and no relax control
+ * is issued, either:
+ * - fail
+ * - drop non-existing members
+ * (by default: don't muck with values)
+ *
+ * - if configured to do so, the referenced GROUP exists,
+ * (the relax control is set) and the user has
+ * "manage" privileges, allow to add MEMBER_OF values to
+ * generic entries; the change is NOT automatically reflected
+ * in the MEMBER attribute of the GROUP referenced
+ * by the value of MEMBER_OF; a separate modification,
+ * with or without relax control, needs to be performed.
+ *
+ * - modrdn:
+ * - if the entry being renamed is a GROUP, the MEMBER_OF
+ * value of the (existing) MEMBER objects is modified
+ * accordingly based on the newDN of the GROUP.
+ *
+ * We need to determine, from the database, if it is
+ * a GROUP; the list of MEMBER objects is obtained from
+ * the database.
+ *
+ * Non-existing MEMBER objects are ignored, since the
+ * MEMBER_AT is not being addressed by the operation.
+ *
+ * - if the entry being renamed has the MEMBER_OF attribute,
+ * the corresponding MEMBER value must be modified in the
+ * respective group entries.
+ *
+ *
+ * - delete:
+ * - if the entry being deleted is a GROUP, the (existing)
+ * MEMBER objects are modified accordingly; a copy of the
+ * values of the MEMBER_AT is saved and, if the delete
+ * succeeds, the MEMBER_OF value of the (existing) MEMBER
+ * objects is deleted.
+ *
+ * We need to determine, from the database, if it is
+ * a GROUP.
+ *
+ * Non-existing MEMBER objects are ignored, since the entry
+ * is being deleted.
+ *
+ * - if the entry being deleted has the MEMBER_OF attribute,
+ * the corresponding value of the MEMBER_AT must be deleted
+ * from the respective GROUP entries.
+ */
+
+#define SLAPD_MEMBEROF_ATTR "memberOf"
+
+static AttributeDescription *ad_member;
+static AttributeDescription *ad_memberOf;
+
+static ObjectClass *oc_group;
+
+static slap_overinst memberof;
+
+typedef struct memberof_t {
+ struct berval mo_dn;
+ struct berval mo_ndn;
+
+ ObjectClass *mo_oc_group;
+ AttributeDescription *mo_ad_member;
+ AttributeDescription *mo_ad_memberof;
+
+ struct berval mo_groupFilterstr;
+ AttributeAssertion mo_groupAVA;
+ Filter mo_groupFilter;
+
+ struct berval mo_memberFilterstr;
+ Filter mo_memberFilter;
+
+ unsigned mo_flags;
+#define MEMBEROF_NONE 0x00U
+#define MEMBEROF_FDANGLING_DROP 0x01U
+#define MEMBEROF_FDANGLING_ERROR 0x02U
+#define MEMBEROF_FDANGLING_MASK (MEMBEROF_FDANGLING_DROP|MEMBEROF_FDANGLING_ERROR)
+#define MEMBEROF_FREFINT 0x04U
+#define MEMBEROF_FREVERSE 0x08U
+
+ ber_int_t mo_dangling_err;
+
+#define MEMBEROF_CHK(mo,f) \
+ (((mo)->mo_flags & (f)) == (f))
+#define MEMBEROF_DANGLING_CHECK(mo) \
+ ((mo)->mo_flags & MEMBEROF_FDANGLING_MASK)
+#define MEMBEROF_DANGLING_DROP(mo) \
+ MEMBEROF_CHK((mo),MEMBEROF_FDANGLING_DROP)
+#define MEMBEROF_DANGLING_ERROR(mo) \
+ MEMBEROF_CHK((mo),MEMBEROF_FDANGLING_ERROR)
+#define MEMBEROF_REFINT(mo) \
+ MEMBEROF_CHK((mo),MEMBEROF_FREFINT)
+#define MEMBEROF_REVERSE(mo) \
+ MEMBEROF_CHK((mo),MEMBEROF_FREVERSE)
+} memberof_t;
+
+typedef enum memberof_is_t {
+ MEMBEROF_IS_NONE = 0x00,
+ MEMBEROF_IS_GROUP = 0x01,
+ MEMBEROF_IS_MEMBER = 0x02,
+ MEMBEROF_IS_BOTH = (MEMBEROF_IS_GROUP|MEMBEROF_IS_MEMBER)
+} memberof_is_t;
+
+typedef struct memberof_cookie_t {
+ AttributeDescription *ad;
+ BerVarray vals;
+ int foundit;
+} memberof_cookie_t;
+
+typedef struct memberof_cbinfo_t {
+ slap_overinst *on;
+ BerVarray member;
+ BerVarray memberof;
+ memberof_is_t what;
+} memberof_cbinfo_t;
+
+static void
+memberof_set_backend( Operation *op_target, Operation *op, slap_overinst *on )
+{
+ BackendInfo *bi = op->o_bd->bd_info;
+
+ if ( bi->bi_type == memberof.on_bi.bi_type )
+ op_target->o_bd->bd_info = (BackendInfo *)on->on_info;
+}
+
+static int
+memberof_isGroupOrMember_cb( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_SEARCH ) {
+ memberof_cookie_t *mc;
+
+ mc = (memberof_cookie_t *)op->o_callback->sc_private;
+ mc->foundit = 1;
+ }
+
+ return 0;
+}
+
+/*
+ * callback for internal search that saves the member attribute values
+ * of groups being deleted.
+ */
+static int
+memberof_saveMember_cb( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_SEARCH ) {
+ memberof_cookie_t *mc;
+ Attribute *a;
+
+ mc = (memberof_cookie_t *)op->o_callback->sc_private;
+ mc->foundit = 1;
+
+ assert( rs->sr_entry != NULL );
+ assert( rs->sr_entry->e_attrs != NULL );
+
+ a = attr_find( rs->sr_entry->e_attrs, mc->ad );
+ if ( a != NULL ) {
+ ber_bvarray_dup_x( &mc->vals, a->a_nvals, op->o_tmpmemctx );
+
+ assert( attr_find( a->a_next, mc->ad ) == NULL );
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * the delete hook performs an internal search that saves the member
+ * attribute values of groups being deleted.
+ */
+static int
+memberof_isGroupOrMember( Operation *op, memberof_cbinfo_t *mci )
+{
+ slap_overinst *on = mci->on;
+ memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
+
+ Operation op2 = *op;
+ slap_callback cb = { 0 };
+ BackendInfo *bi = op->o_bd->bd_info;
+ AttributeName an[ 2 ];
+
+ memberof_is_t iswhat = MEMBEROF_IS_NONE;
+ memberof_cookie_t mc;
+
+ assert( mci->what != MEMBEROF_IS_NONE );
+
+ cb.sc_private = &mc;
+ if ( op->o_tag == LDAP_REQ_DELETE ) {
+ cb.sc_response = memberof_saveMember_cb;
+
+ } else {
+ cb.sc_response = memberof_isGroupOrMember_cb;
+ }
+
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2.o_callback = &cb;
+ op2.o_dn = op->o_bd->be_rootdn;
+ op2.o_ndn = op->o_bd->be_rootndn;
+
+ op2.ors_scope = LDAP_SCOPE_BASE;
+ op2.ors_deref = LDAP_DEREF_NEVER;
+ BER_BVZERO( &an[ 1 ].an_name );
+ op2.ors_attrs = an;
+ op2.ors_attrsonly = 0;
+ op2.ors_limit = NULL;
+ op2.ors_slimit = 1;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+
+ if ( mci->what & MEMBEROF_IS_GROUP ) {
+ SlapReply rs2 = { REP_RESULT };
+
+ mc.ad = mo->mo_ad_member;
+ mc.foundit = 0;
+ mc.vals = NULL;
+ an[ 0 ].an_desc = mo->mo_ad_member;
+ an[ 0 ].an_name = an[ 0 ].an_desc->ad_cname;
+ op2.ors_filterstr = mo->mo_groupFilterstr;
+ op2.ors_filter = &mo->mo_groupFilter;
+ op2.o_do_not_cache = 1; /* internal search, don't log */
+
+ memberof_set_backend( &op2, op, on );
+ (void)op->o_bd->be_search( &op2, &rs2 );
+ op2.o_bd->bd_info = bi;
+
+ if ( mc.foundit ) {
+ iswhat |= MEMBEROF_IS_GROUP;
+ if ( mc.vals ) mci->member = mc.vals;
+
+ }
+ }
+
+ if ( mci->what & MEMBEROF_IS_MEMBER ) {
+ SlapReply rs2 = { REP_RESULT };
+
+ mc.ad = mo->mo_ad_memberof;
+ mc.foundit = 0;
+ mc.vals = NULL;
+ an[ 0 ].an_desc = mo->mo_ad_memberof;
+ an[ 0 ].an_name = an[ 0 ].an_desc->ad_cname;
+ op2.ors_filterstr = mo->mo_memberFilterstr;
+ op2.ors_filter = &mo->mo_memberFilter;
+ op2.o_do_not_cache = 1; /* internal search, don't log */
+
+ memberof_set_backend( &op2, op, on );
+ (void)op->o_bd->be_search( &op2, &rs2 );
+ op2.o_bd->bd_info = bi;
+
+ if ( mc.foundit ) {
+ iswhat |= MEMBEROF_IS_MEMBER;
+ if ( mc.vals ) mci->memberof = mc.vals;
+
+ }
+ }
+
+ mci->what = iswhat;
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * response callback that adds memberof values when a group is modified.
+ */
+static void
+memberof_value_modify(
+ Operation *op,
+ struct berval *ndn,
+ AttributeDescription *ad,
+ struct berval *old_dn,
+ struct berval *old_ndn,
+ struct berval *new_dn,
+ struct berval *new_ndn )
+{
+ memberof_cbinfo_t *mci = op->o_callback->sc_private;
+ slap_overinst *on = mci->on;
+ memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
+
+ Operation op2 = *op;
+ unsigned long opid = op->o_opid;
+ SlapReply rs2 = { REP_RESULT };
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+ Modifications mod[ 2 ] = { { { 0 } } }, *ml;
+ struct berval values[ 4 ], nvalues[ 4 ];
+ int mcnt = 0;
+
+ if ( old_ndn != NULL && new_ndn != NULL &&
+ ber_bvcmp( old_ndn, new_ndn ) == 0 ) {
+ /* DNs compare equal, it's a noop */
+ return;
+ }
+
+ op2.o_tag = LDAP_REQ_MODIFY;
+
+ op2.o_req_dn = *ndn;
+ op2.o_req_ndn = *ndn;
+
+ op2.o_callback = &cb;
+ op2.o_dn = op->o_bd->be_rootdn;
+ op2.o_ndn = op->o_bd->be_rootndn;
+ op2.orm_modlist = NULL;
+
+ /* Internal ops, never replicate these */
+ op2.o_opid = 0; /* shared with op, saved above */
+ op2.orm_no_opattrs = 1;
+ op2.o_dont_replicate = 1;
+
+ if ( !BER_BVISNULL( &mo->mo_ndn ) ) {
+ ml = &mod[ mcnt ];
+ ml->sml_numvals = 1;
+ ml->sml_values = &values[ 0 ];
+ ml->sml_values[ 0 ] = mo->mo_dn;
+ BER_BVZERO( &ml->sml_values[ 1 ] );
+ ml->sml_nvalues = &nvalues[ 0 ];
+ ml->sml_nvalues[ 0 ] = mo->mo_ndn;
+ BER_BVZERO( &ml->sml_nvalues[ 1 ] );
+ ml->sml_desc = slap_schema.si_ad_modifiersName;
+ ml->sml_type = ml->sml_desc->ad_cname;
+ ml->sml_op = LDAP_MOD_REPLACE;
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+ ml->sml_next = op2.orm_modlist;
+ op2.orm_modlist = ml;
+
+ mcnt++;
+ }
+
+ ml = &mod[ mcnt ];
+ ml->sml_numvals = 1;
+ ml->sml_values = &values[ 2 ];
+ BER_BVZERO( &ml->sml_values[ 1 ] );
+ ml->sml_nvalues = &nvalues[ 2 ];
+ BER_BVZERO( &ml->sml_nvalues[ 1 ] );
+ ml->sml_desc = ad;
+ ml->sml_type = ml->sml_desc->ad_cname;
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+ ml->sml_next = op2.orm_modlist;
+ op2.orm_modlist = ml;
+
+ if ( new_ndn != NULL ) {
+ BackendInfo *bi = op2.o_bd->bd_info;
+ OpExtra oex;
+
+ assert( !BER_BVISNULL( new_dn ) );
+ assert( !BER_BVISNULL( new_ndn ) );
+
+ ml = &mod[ mcnt ];
+ ml->sml_op = LDAP_MOD_ADD;
+
+ ml->sml_values[ 0 ] = *new_dn;
+ ml->sml_nvalues[ 0 ] = *new_ndn;
+
+ oex.oe_key = (void *)&memberof;
+ LDAP_SLIST_INSERT_HEAD(&op2.o_extra, &oex, oe_next);
+ memberof_set_backend( &op2, op, on );
+ (void)op->o_bd->be_modify( &op2, &rs2 );
+ op2.o_bd->bd_info = bi;
+ LDAP_SLIST_REMOVE(&op2.o_extra, &oex, OpExtra, oe_next);
+ if ( rs2.sr_err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ANY,
+ "%s: memberof_value_modify DN=\"%s\" add %s=\"%s\" failed err=%d\n",
+ op->o_log_prefix, op2.o_req_dn.bv_val,
+ ad->ad_cname.bv_val, new_dn->bv_val, rs2.sr_err );
+ }
+
+ assert( op2.orm_modlist == &mod[ mcnt ] );
+ assert( mcnt == 0 || op2.orm_modlist->sml_next == &mod[ 0 ] );
+ ml = op2.orm_modlist->sml_next;
+ if ( mcnt == 1 ) {
+ assert( ml == &mod[ 0 ] );
+ ml = ml->sml_next;
+ }
+ if ( ml != NULL ) {
+ slap_mods_free( ml, 1 );
+ }
+
+ mod[ 0 ].sml_next = NULL;
+ }
+
+ if ( old_ndn != NULL ) {
+ BackendInfo *bi = op2.o_bd->bd_info;
+ OpExtra oex;
+
+ assert( !BER_BVISNULL( old_dn ) );
+ assert( !BER_BVISNULL( old_ndn ) );
+
+ ml = &mod[ mcnt ];
+ ml->sml_op = LDAP_MOD_DELETE;
+
+ ml->sml_values[ 0 ] = *old_dn;
+ ml->sml_nvalues[ 0 ] = *old_ndn;
+
+ oex.oe_key = (void *)&memberof;
+ LDAP_SLIST_INSERT_HEAD(&op2.o_extra, &oex, oe_next);
+ memberof_set_backend( &op2, op, on );
+ (void)op->o_bd->be_modify( &op2, &rs2 );
+ op2.o_bd->bd_info = bi;
+ LDAP_SLIST_REMOVE(&op2.o_extra, &oex, OpExtra, oe_next);
+ if ( rs2.sr_err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ANY,
+ "%s: memberof_value_modify DN=\"%s\" delete %s=\"%s\" failed err=%d\n",
+ op->o_log_prefix, op2.o_req_dn.bv_val,
+ ad->ad_cname.bv_val, old_dn->bv_val, rs2.sr_err );
+ }
+
+ assert( op2.orm_modlist == &mod[ mcnt ] );
+ ml = op2.orm_modlist->sml_next;
+ if ( mcnt == 1 ) {
+ assert( ml == &mod[ 0 ] );
+ ml = ml->sml_next;
+ }
+ if ( ml != NULL ) {
+ slap_mods_free( ml, 1 );
+ }
+ }
+ /* restore original opid */
+ op->o_opid = opid;
+
+ /* FIXME: if old_group_ndn doesn't exist, both delete __and__
+ * add will fail; better split in two operations, although
+ * not optimal in terms of performance. At least it would
+ * move towards self-repairing capabilities. */
+}
+
+static int
+memberof_cleanup( Operation *op, SlapReply *rs )
+{
+ slap_callback *sc = op->o_callback;
+ memberof_cbinfo_t *mci = sc->sc_private;
+
+ op->o_callback = sc->sc_next;
+ if ( mci->memberof )
+ ber_bvarray_free_x( mci->memberof, op->o_tmpmemctx );
+ if ( mci->member )
+ ber_bvarray_free_x( mci->member, op->o_tmpmemctx );
+ op->o_tmpfree( sc, op->o_tmpmemctx );
+ return 0;
+}
+
+static int memberof_res_add( Operation *op, SlapReply *rs );
+static int memberof_res_delete( Operation *op, SlapReply *rs );
+static int memberof_res_modify( Operation *op, SlapReply *rs );
+static int memberof_res_modrdn( Operation *op, SlapReply *rs );
+
+static int
+memberof_op_add( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
+
+ Attribute **ap, **map = NULL;
+ int rc = SLAP_CB_CONTINUE;
+ int i;
+ struct berval save_dn, save_ndn;
+ slap_callback *sc;
+ memberof_cbinfo_t *mci;
+ OpExtra *oex;
+
+ LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
+ if ( oex->oe_key == (void *)&memberof )
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( op->ora_e->e_attrs == NULL ) {
+ /* FIXME: global overlay; need to deal with */
+ Debug( LDAP_DEBUG_ANY, "%s: memberof_op_add(\"%s\"): "
+ "consistency checks not implemented when overlay "
+ "is instantiated as global.\n",
+ op->o_log_prefix, op->o_req_dn.bv_val );
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( MEMBEROF_REVERSE( mo ) ) {
+ for ( ap = &op->ora_e->e_attrs; *ap; ap = &(*ap)->a_next ) {
+ Attribute *a = *ap;
+
+ if ( a->a_desc == mo->mo_ad_memberof ) {
+ map = ap;
+ break;
+ }
+ }
+ }
+
+ save_dn = op->o_dn;
+ save_ndn = op->o_ndn;
+
+ if ( MEMBEROF_DANGLING_CHECK( mo )
+ && !get_relax( op )
+ && is_entry_objectclass_or_sub( op->ora_e, mo->mo_oc_group ) )
+ {
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+
+ for ( ap = &op->ora_e->e_attrs; *ap; ) {
+ Attribute *a = *ap;
+
+ if ( !is_ad_subtype( a->a_desc, mo->mo_ad_member ) ) {
+ ap = &a->a_next;
+ continue;
+ }
+
+ assert( a->a_nvals != NULL );
+
+ for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
+ Entry *e = NULL;
+
+ /* ITS#6670 Ignore member pointing to this entry */
+ if ( dn_match( &a->a_nvals[i], &save_ndn ))
+ continue;
+
+ rc = be_entry_get_rw( op, &a->a_nvals[ i ],
+ NULL, NULL, 0, &e );
+ if ( rc == LDAP_SUCCESS ) {
+ be_entry_release_r( op, e );
+ continue;
+ }
+
+ if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
+ rc = rs->sr_err = mo->mo_dangling_err;
+ rs->sr_text = "adding non-existing object "
+ "as group member";
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ if ( MEMBEROF_DANGLING_DROP( mo ) ) {
+ int j;
+
+ Debug( LDAP_DEBUG_ANY, "%s: memberof_op_add(\"%s\"): "
+ "member=\"%s\" does not exist (stripping...)\n",
+ op->o_log_prefix, op->ora_e->e_name.bv_val,
+ a->a_vals[ i ].bv_val );
+
+ for ( j = i + 1; !BER_BVISNULL( &a->a_nvals[ j ] ); j++ );
+ ber_memfree( a->a_vals[ i ].bv_val );
+ BER_BVZERO( &a->a_vals[ i ] );
+ if ( a->a_nvals != a->a_vals ) {
+ ber_memfree( a->a_nvals[ i ].bv_val );
+ BER_BVZERO( &a->a_nvals[ i ] );
+ }
+ a->a_numvals--;
+ if ( j - i == 1 ) {
+ break;
+ }
+
+ AC_MEMCPY( &a->a_vals[ i ], &a->a_vals[ i + 1 ],
+ sizeof( struct berval ) * ( j - i ) );
+ if ( a->a_nvals != a->a_vals ) {
+ AC_MEMCPY( &a->a_nvals[ i ], &a->a_nvals[ i + 1 ],
+ sizeof( struct berval ) * ( j - i ) );
+ }
+ i--;
+ }
+ }
+
+ /* If all values have been removed,
+ * remove the attribute itself. */
+ if ( BER_BVISNULL( &a->a_nvals[ 0 ] ) ) {
+ *ap = a->a_next;
+ attr_free( a );
+
+ } else {
+ ap = &a->a_next;
+ }
+ }
+ op->o_dn = save_dn;
+ op->o_ndn = save_ndn;
+ op->o_bd->bd_info = (BackendInfo *)on;
+ }
+
+ if ( map != NULL ) {
+ Attribute *a = *map;
+ AccessControlState acl_state = ACL_STATE_INIT;
+
+ for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
+ Entry *e;
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ /* access is checked with the original identity */
+ rc = access_allowed( op, op->ora_e, mo->mo_ad_memberof,
+ &a->a_nvals[ i ], ACL_WADD,
+ &acl_state );
+ if ( rc == 0 ) {
+ rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = NULL;
+ send_ldap_result( op, rs );
+ goto done;
+ }
+ /* ITS#6670 Ignore member pointing to this entry */
+ if ( dn_match( &a->a_nvals[i], &save_ndn ))
+ continue;
+
+ rc = be_entry_get_rw( op, &a->a_nvals[ i ],
+ NULL, NULL, 0, &e );
+ op->o_bd->bd_info = (BackendInfo *)on;
+ if ( rc != LDAP_SUCCESS ) {
+ if ( get_relax( op ) ) {
+ continue;
+ }
+
+ if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
+ rc = rs->sr_err = mo->mo_dangling_err;
+ rs->sr_text = "adding non-existing object "
+ "as memberof";
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ if ( MEMBEROF_DANGLING_DROP( mo ) ) {
+ int j;
+
+ Debug( LDAP_DEBUG_ANY, "%s: memberof_op_add(\"%s\"): "
+ "memberof=\"%s\" does not exist (stripping...)\n",
+ op->o_log_prefix, op->ora_e->e_name.bv_val,
+ a->a_nvals[ i ].bv_val );
+
+ for ( j = i + 1; !BER_BVISNULL( &a->a_nvals[ j ] ); j++ );
+ ber_memfree( a->a_vals[ i ].bv_val );
+ BER_BVZERO( &a->a_vals[ i ] );
+ if ( a->a_nvals != a->a_vals ) {
+ ber_memfree( a->a_nvals[ i ].bv_val );
+ BER_BVZERO( &a->a_nvals[ i ] );
+ }
+ if ( j - i == 1 ) {
+ break;
+ }
+
+ AC_MEMCPY( &a->a_vals[ i ], &a->a_vals[ i + 1 ],
+ sizeof( struct berval ) * ( j - i ) );
+ if ( a->a_nvals != a->a_vals ) {
+ AC_MEMCPY( &a->a_nvals[ i ], &a->a_nvals[ i + 1 ],
+ sizeof( struct berval ) * ( j - i ) );
+ }
+ i--;
+ }
+
+ continue;
+ }
+
+ /* access is checked with the original identity */
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rc = access_allowed( op, e, mo->mo_ad_member,
+ &op->o_req_ndn, ACL_WADD, NULL );
+ be_entry_release_r( op, e );
+ op->o_bd->bd_info = (BackendInfo *)on;
+
+ if ( !rc ) {
+ rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "insufficient access to object referenced by memberof";
+ send_ldap_result( op, rs );
+ goto done;
+ }
+ }
+
+ if ( BER_BVISNULL( &a->a_nvals[ 0 ] ) ) {
+ *map = a->a_next;
+ attr_free( a );
+ }
+ }
+
+ rc = SLAP_CB_CONTINUE;
+
+ sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
+ sc->sc_private = sc+1;
+ sc->sc_response = memberof_res_add;
+ sc->sc_cleanup = memberof_cleanup;
+ sc->sc_writewait = 0;
+ mci = sc->sc_private;
+ mci->on = on;
+ mci->member = NULL;
+ mci->memberof = NULL;
+ sc->sc_next = op->o_callback;
+ op->o_callback = sc;
+
+done:;
+ op->o_dn = save_dn;
+ op->o_ndn = save_ndn;
+ op->o_bd->bd_info = (BackendInfo *)on;
+
+ return rc;
+}
+
+static int
+memberof_op_delete( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
+
+ slap_callback *sc;
+ memberof_cbinfo_t *mci;
+ OpExtra *oex;
+
+ LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
+ if ( oex->oe_key == (void *)&memberof )
+ return SLAP_CB_CONTINUE;
+ }
+
+ sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
+ sc->sc_private = sc+1;
+ sc->sc_response = memberof_res_delete;
+ sc->sc_cleanup = memberof_cleanup;
+ sc->sc_writewait = 0;
+ mci = sc->sc_private;
+ mci->on = on;
+ mci->member = NULL;
+ mci->memberof = NULL;
+ mci->what = MEMBEROF_IS_GROUP;
+ if ( MEMBEROF_REFINT( mo ) ) {
+ mci->what = MEMBEROF_IS_BOTH;
+ }
+
+ memberof_isGroupOrMember( op, mci );
+
+ sc->sc_next = op->o_callback;
+ op->o_callback = sc;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+memberof_op_modify( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
+
+ Modifications **mlp, **mmlp = NULL;
+ int rc = SLAP_CB_CONTINUE, save_member = 0;
+ struct berval save_dn, save_ndn;
+ slap_callback *sc;
+ memberof_cbinfo_t *mci, mcis;
+ OpExtra *oex;
+
+ LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
+ if ( oex->oe_key == (void *)&memberof )
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( MEMBEROF_REVERSE( mo ) ) {
+ for ( mlp = &op->orm_modlist; *mlp; mlp = &(*mlp)->sml_next ) {
+ Modifications *ml = *mlp;
+
+ if ( ml->sml_desc == mo->mo_ad_memberof ) {
+ mmlp = mlp;
+ break;
+ }
+ }
+ }
+
+ save_dn = op->o_dn;
+ save_ndn = op->o_ndn;
+ mcis.on = on;
+ mcis.what = MEMBEROF_IS_GROUP;
+
+ if ( memberof_isGroupOrMember( op, &mcis ) == LDAP_SUCCESS
+ && ( mcis.what & MEMBEROF_IS_GROUP ) )
+ {
+ Modifications *ml;
+
+ for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
+ if ( ml->sml_desc == mo->mo_ad_member ) {
+ switch ( ml->sml_op ) {
+ case LDAP_MOD_DELETE:
+ case LDAP_MOD_REPLACE:
+ case SLAP_MOD_SOFTDEL: /* ITS#7487: can be used by syncrepl (in mirror mode?) */
+ save_member = 1;
+ break;
+ }
+ }
+ }
+
+
+ if ( MEMBEROF_DANGLING_CHECK( mo )
+ && !get_relax( op ) )
+ {
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+
+ assert( op->orm_modlist != NULL );
+
+ for ( mlp = &op->orm_modlist; *mlp; ) {
+ Modifications *ml = *mlp;
+ int i;
+
+ if ( !is_ad_subtype( ml->sml_desc, mo->mo_ad_member ) ) {
+ mlp = &ml->sml_next;
+ continue;
+ }
+
+ switch ( ml->sml_op ) {
+ case LDAP_MOD_DELETE:
+ case SLAP_MOD_SOFTDEL: /* ITS#7487: can be used by syncrepl (in mirror mode?) */
+ /* we don't care about cancellations: if the value
+ * exists, fine; if it doesn't, we let the underlying
+ * database fail as appropriate; */
+ mlp = &ml->sml_next;
+ break;
+
+ case LDAP_MOD_REPLACE:
+ /* Handle this just like a delete (see above) */
+ if ( !ml->sml_values ) {
+ mlp = &ml->sml_next;
+ break;
+ }
+
+ case LDAP_MOD_ADD:
+ case SLAP_MOD_SOFTADD: /* ITS#7487 */
+ case SLAP_MOD_ADD_IF_NOT_PRESENT: /* ITS#7487 */
+ /* NOTE: right now, the attributeType we use
+ * for member must have a normalized value */
+ assert( ml->sml_nvalues != NULL );
+
+ for ( i = 0; !BER_BVISNULL( &ml->sml_nvalues[ i ] ); i++ ) {
+ Entry *e;
+
+ /* ITS#6670 Ignore member pointing to this entry */
+ if ( dn_match( &ml->sml_nvalues[i], &save_ndn ))
+ continue;
+
+ if ( be_entry_get_rw( op, &ml->sml_nvalues[ i ],
+ NULL, NULL, 0, &e ) == LDAP_SUCCESS )
+ {
+ be_entry_release_r( op, e );
+ continue;
+ }
+
+ if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
+ rc = rs->sr_err = mo->mo_dangling_err;
+ rs->sr_text = "adding non-existing object "
+ "as group member";
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ if ( MEMBEROF_DANGLING_DROP( mo ) ) {
+ int j;
+
+ Debug( LDAP_DEBUG_ANY, "%s: memberof_op_modify(\"%s\"): "
+ "member=\"%s\" does not exist (stripping...)\n",
+ op->o_log_prefix, op->o_req_dn.bv_val,
+ ml->sml_nvalues[ i ].bv_val );
+
+ for ( j = i + 1; !BER_BVISNULL( &ml->sml_nvalues[ j ] ); j++ );
+ ber_memfree( ml->sml_values[ i ].bv_val );
+ BER_BVZERO( &ml->sml_values[ i ] );
+ ber_memfree( ml->sml_nvalues[ i ].bv_val );
+ BER_BVZERO( &ml->sml_nvalues[ i ] );
+ ml->sml_numvals--;
+ if ( j - i == 1 ) {
+ break;
+ }
+
+ AC_MEMCPY( &ml->sml_values[ i ], &ml->sml_values[ i + 1 ],
+ sizeof( struct berval ) * ( j - i ) );
+ AC_MEMCPY( &ml->sml_nvalues[ i ], &ml->sml_nvalues[ i + 1 ],
+ sizeof( struct berval ) * ( j - i ) );
+ i--;
+ }
+ }
+
+ if ( BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
+ *mlp = ml->sml_next;
+ slap_mod_free( &ml->sml_mod, 0 );
+ free( ml );
+
+ } else {
+ mlp = &ml->sml_next;
+ }
+
+ break;
+
+ default:
+ assert( 0 );
+ }
+ }
+ }
+ }
+
+ if ( mmlp != NULL ) {
+ Modifications *ml = *mmlp;
+ int i;
+ Entry *target;
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rc = be_entry_get_rw( op, &op->o_req_ndn,
+ NULL, NULL, 0, &target );
+ op->o_bd->bd_info = (BackendInfo *)on;
+ if ( rc != LDAP_SUCCESS ) {
+ rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ send_ldap_result( op, rs );
+ goto done;
+ }
+
+ switch ( ml->sml_op ) {
+ case LDAP_MOD_DELETE:
+ case SLAP_MOD_SOFTDEL: /* ITS#7487: can be used by syncrepl (in mirror mode?) */
+ if ( ml->sml_nvalues != NULL ) {
+ AccessControlState acl_state = ACL_STATE_INIT;
+
+ for ( i = 0; !BER_BVISNULL( &ml->sml_nvalues[ i ] ); i++ ) {
+ Entry *e;
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ /* access is checked with the original identity */
+ rc = access_allowed( op, target,
+ mo->mo_ad_memberof,
+ &ml->sml_nvalues[ i ],
+ ACL_WDEL,
+ &acl_state );
+ if ( rc == 0 ) {
+ rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = NULL;
+ send_ldap_result( op, rs );
+ goto done2;
+ }
+
+ rc = be_entry_get_rw( op, &ml->sml_nvalues[ i ],
+ NULL, NULL, 0, &e );
+ op->o_bd->bd_info = (BackendInfo *)on;
+ if ( rc != LDAP_SUCCESS ) {
+ if ( get_relax( op ) ) {
+ continue;
+ }
+
+ if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
+ rc = rs->sr_err = mo->mo_dangling_err;
+ rs->sr_text = "deleting non-existing object "
+ "as memberof";
+ send_ldap_result( op, rs );
+ goto done2;
+ }
+
+ if ( MEMBEROF_DANGLING_DROP( mo ) ) {
+ int j;
+
+ Debug( LDAP_DEBUG_ANY, "%s: memberof_op_modify(\"%s\"): "
+ "memberof=\"%s\" does not exist (stripping...)\n",
+ op->o_log_prefix, op->o_req_ndn.bv_val,
+ ml->sml_nvalues[ i ].bv_val );
+
+ for ( j = i + 1; !BER_BVISNULL( &ml->sml_nvalues[ j ] ); j++ );
+ ber_memfree( ml->sml_values[ i ].bv_val );
+ BER_BVZERO( &ml->sml_values[ i ] );
+ if ( ml->sml_nvalues != ml->sml_values ) {
+ ber_memfree( ml->sml_nvalues[ i ].bv_val );
+ BER_BVZERO( &ml->sml_nvalues[ i ] );
+ }
+ ml->sml_numvals--;
+ if ( j - i == 1 ) {
+ break;
+ }
+
+ AC_MEMCPY( &ml->sml_values[ i ], &ml->sml_values[ i + 1 ],
+ sizeof( struct berval ) * ( j - i ) );
+ if ( ml->sml_nvalues != ml->sml_values ) {
+ AC_MEMCPY( &ml->sml_nvalues[ i ], &ml->sml_nvalues[ i + 1 ],
+ sizeof( struct berval ) * ( j - i ) );
+ }
+ i--;
+ }
+
+ continue;
+ }
+
+ /* access is checked with the original identity */
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rc = access_allowed( op, e, mo->mo_ad_member,
+ &op->o_req_ndn,
+ ACL_WDEL, NULL );
+ be_entry_release_r( op, e );
+ op->o_bd->bd_info = (BackendInfo *)on;
+
+ if ( !rc ) {
+ rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "insufficient access to object referenced by memberof";
+ send_ldap_result( op, rs );
+ goto done;
+ }
+ }
+
+ if ( BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
+ *mmlp = ml->sml_next;
+ slap_mod_free( &ml->sml_mod, 0 );
+ free( ml );
+ }
+
+ break;
+ }
+ /* fall thru */
+
+ case LDAP_MOD_REPLACE:
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ /* access is checked with the original identity */
+ rc = access_allowed( op, target,
+ mo->mo_ad_memberof,
+ NULL,
+ ACL_WDEL, NULL );
+ op->o_bd->bd_info = (BackendInfo *)on;
+ if ( rc == 0 ) {
+ rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = NULL;
+ send_ldap_result( op, rs );
+ goto done2;
+ }
+
+ if ( ml->sml_op == LDAP_MOD_DELETE || ml->sml_op == SLAP_MOD_SOFTDEL || !ml->sml_values ) {
+ break;
+ }
+ /* fall thru */
+
+ case LDAP_MOD_ADD:
+ case SLAP_MOD_SOFTADD: /* ITS#7487 */
+ case SLAP_MOD_ADD_IF_NOT_PRESENT: /* ITS#7487 */
+ {
+ AccessControlState acl_state = ACL_STATE_INIT;
+
+ for ( i = 0; !BER_BVISNULL( &ml->sml_nvalues[ i ] ); i++ ) {
+ Entry *e;
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ /* access is checked with the original identity */
+ rc = access_allowed( op, target,
+ mo->mo_ad_memberof,
+ &ml->sml_nvalues[ i ],
+ ACL_WADD,
+ &acl_state );
+ if ( rc == 0 ) {
+ rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = NULL;
+ send_ldap_result( op, rs );
+ goto done2;
+ }
+
+ /* ITS#6670 Ignore member pointing to this entry */
+ if ( dn_match( &ml->sml_nvalues[i], &save_ndn ))
+ continue;
+
+ rc = be_entry_get_rw( op, &ml->sml_nvalues[ i ],
+ NULL, NULL, 0, &e );
+ op->o_bd->bd_info = (BackendInfo *)on;
+ if ( rc != LDAP_SUCCESS ) {
+ if ( MEMBEROF_DANGLING_ERROR( mo ) ) {
+ rc = rs->sr_err = mo->mo_dangling_err;
+ rs->sr_text = "adding non-existing object "
+ "as memberof";
+ send_ldap_result( op, rs );
+ goto done2;
+ }
+
+ if ( MEMBEROF_DANGLING_DROP( mo ) ) {
+ int j;
+
+ Debug( LDAP_DEBUG_ANY, "%s: memberof_op_modify(\"%s\"): "
+ "memberof=\"%s\" does not exist (stripping...)\n",
+ op->o_log_prefix, op->o_req_ndn.bv_val,
+ ml->sml_nvalues[ i ].bv_val );
+
+ for ( j = i + 1; !BER_BVISNULL( &ml->sml_nvalues[ j ] ); j++ );
+ ber_memfree( ml->sml_values[ i ].bv_val );
+ BER_BVZERO( &ml->sml_values[ i ] );
+ if ( ml->sml_nvalues != ml->sml_values ) {
+ ber_memfree( ml->sml_nvalues[ i ].bv_val );
+ BER_BVZERO( &ml->sml_nvalues[ i ] );
+ }
+ ml->sml_numvals--;
+ if ( j - i == 1 ) {
+ break;
+ }
+
+ AC_MEMCPY( &ml->sml_values[ i ], &ml->sml_values[ i + 1 ],
+ sizeof( struct berval ) * ( j - i ) );
+ if ( ml->sml_nvalues != ml->sml_values ) {
+ AC_MEMCPY( &ml->sml_nvalues[ i ], &ml->sml_nvalues[ i + 1 ],
+ sizeof( struct berval ) * ( j - i ) );
+ }
+ i--;
+ }
+
+ continue;
+ }
+
+ /* access is checked with the original identity */
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rc = access_allowed( op, e, mo->mo_ad_member,
+ &op->o_req_ndn,
+ ACL_WDEL, NULL );
+ be_entry_release_r( op, e );
+ op->o_bd->bd_info = (BackendInfo *)on;
+
+ if ( !rc ) {
+ rc = rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "insufficient access to object referenced by memberof";
+ send_ldap_result( op, rs );
+ goto done;
+ }
+ }
+
+ if ( BER_BVISNULL( &ml->sml_nvalues[ 0 ] ) ) {
+ *mmlp = ml->sml_next;
+ slap_mod_free( &ml->sml_mod, 0 );
+ free( ml );
+ }
+
+ } break;
+
+ default:
+ assert( 0 );
+ }
+
+done2:;
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ be_entry_release_r( op, target );
+ op->o_bd->bd_info = (BackendInfo *)on;
+ }
+
+ sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
+ sc->sc_private = sc+1;
+ sc->sc_response = memberof_res_modify;
+ sc->sc_cleanup = memberof_cleanup;
+ sc->sc_writewait = 0;
+ mci = sc->sc_private;
+ mci->on = on;
+ mci->member = NULL;
+ mci->memberof = NULL;
+ mci->what = mcis.what;
+
+ if ( save_member ) {
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rc = backend_attribute( op, NULL, &op->o_req_ndn,
+ mo->mo_ad_member, &mci->member, ACL_READ );
+ op->o_bd->bd_info = (BackendInfo *)on;
+ }
+
+ sc->sc_next = op->o_callback;
+ op->o_callback = sc;
+
+ rc = SLAP_CB_CONTINUE;
+
+done:;
+ op->o_dn = save_dn;
+ op->o_ndn = save_ndn;
+ op->o_bd->bd_info = (BackendInfo *)on;
+
+ return rc;
+}
+
+static int
+memberof_op_modrdn( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ slap_callback *sc;
+ memberof_cbinfo_t *mci;
+ OpExtra *oex;
+
+ LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
+ if ( oex->oe_key == (void *)&memberof )
+ return SLAP_CB_CONTINUE;
+ }
+
+ sc = op->o_tmpalloc( sizeof(slap_callback)+sizeof(*mci), op->o_tmpmemctx );
+ sc->sc_private = sc+1;
+ sc->sc_response = memberof_res_modrdn;
+ sc->sc_cleanup = memberof_cleanup;
+ sc->sc_writewait = 0;
+ mci = sc->sc_private;
+ mci->on = on;
+ mci->member = NULL;
+ mci->memberof = NULL;
+
+ sc->sc_next = op->o_callback;
+ op->o_callback = sc;
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+ * response callback that adds memberof values when a group is added.
+ */
+static int
+memberof_res_add( Operation *op, SlapReply *rs )
+{
+ memberof_cbinfo_t *mci = op->o_callback->sc_private;
+ slap_overinst *on = mci->on;
+ memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
+
+ int i;
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( MEMBEROF_REVERSE( mo ) ) {
+ Attribute *ma;
+
+ ma = attr_find( op->ora_e->e_attrs, mo->mo_ad_memberof );
+ if ( ma != NULL ) {
+ /* relax is required to allow to add
+ * a non-existing member */
+ op->o_relax = SLAP_CONTROL_CRITICAL;
+
+ for ( i = 0; !BER_BVISNULL( &ma->a_nvals[ i ] ); i++ ) {
+
+ /* ITS#6670 Ignore member pointing to this entry */
+ if ( dn_match( &ma->a_nvals[i], &op->o_req_ndn ))
+ continue;
+
+ /* the modification is attempted
+ * with the original identity */
+ memberof_value_modify( op,
+ &ma->a_nvals[ i ], mo->mo_ad_member,
+ NULL, NULL, &op->o_req_dn, &op->o_req_ndn );
+ }
+ }
+ }
+
+ if ( is_entry_objectclass_or_sub( op->ora_e, mo->mo_oc_group ) ) {
+ Attribute *a;
+
+ for ( a = attrs_find( op->ora_e->e_attrs, mo->mo_ad_member );
+ a != NULL;
+ a = attrs_find( a->a_next, mo->mo_ad_member ) )
+ {
+ for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
+ /* ITS#6670 Ignore member pointing to this entry */
+ if ( dn_match( &a->a_nvals[i], &op->o_req_ndn ))
+ continue;
+
+ memberof_value_modify( op,
+ &a->a_nvals[ i ],
+ mo->mo_ad_memberof,
+ NULL, NULL,
+ &op->o_req_dn,
+ &op->o_req_ndn );
+ }
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+ * response callback that deletes memberof values when a group is deleted.
+ */
+static int
+memberof_res_delete( Operation *op, SlapReply *rs )
+{
+ memberof_cbinfo_t *mci = op->o_callback->sc_private;
+ slap_overinst *on = mci->on;
+ memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
+
+ BerVarray vals;
+ int i;
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ vals = mci->member;
+ if ( vals != NULL ) {
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
+ memberof_value_modify( op,
+ &vals[ i ], mo->mo_ad_memberof,
+ &op->o_req_dn, &op->o_req_ndn,
+ NULL, NULL );
+ }
+ }
+
+ if ( MEMBEROF_REFINT( mo ) ) {
+ vals = mci->memberof;
+ if ( vals != NULL ) {
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
+ memberof_value_modify( op,
+ &vals[ i ], mo->mo_ad_member,
+ &op->o_req_dn, &op->o_req_ndn,
+ NULL, NULL );
+ }
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+ * response callback that adds/deletes memberof values when a group
+ * is modified.
+ */
+static int
+memberof_res_modify( Operation *op, SlapReply *rs )
+{
+ memberof_cbinfo_t *mci = op->o_callback->sc_private;
+ slap_overinst *on = mci->on;
+ memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
+
+ int i, rc;
+ Modifications *ml, *mml = NULL;
+ BerVarray vals;
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( MEMBEROF_REVERSE( mo ) ) {
+ for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
+ if ( ml->sml_desc == mo->mo_ad_memberof ) {
+ mml = ml;
+ break;
+ }
+ }
+ }
+
+ if ( mml != NULL ) {
+ BerVarray vals = mml->sml_nvalues;
+
+ switch ( mml->sml_op ) {
+ case LDAP_MOD_DELETE:
+ case SLAP_MOD_SOFTDEL: /* ITS#7487: can be used by syncrepl (in mirror mode?) */
+ if ( vals != NULL ) {
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
+ memberof_value_modify( op,
+ &vals[ i ], mo->mo_ad_member,
+ &op->o_req_dn, &op->o_req_ndn,
+ NULL, NULL );
+ }
+ break;
+ }
+ /* fall thru */
+
+ case LDAP_MOD_REPLACE:
+ /* delete all ... */
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rc = backend_attribute( op, NULL, &op->o_req_ndn,
+ mo->mo_ad_memberof, &vals, ACL_READ );
+ op->o_bd->bd_info = (BackendInfo *)on;
+ if ( rc == LDAP_SUCCESS ) {
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
+ memberof_value_modify( op,
+ &vals[ i ], mo->mo_ad_member,
+ &op->o_req_dn, &op->o_req_ndn,
+ NULL, NULL );
+ }
+ ber_bvarray_free_x( vals, op->o_tmpmemctx );
+ }
+
+ if ( ml->sml_op == LDAP_MOD_DELETE || !mml->sml_values ) {
+ break;
+ }
+ /* fall thru */
+
+ case LDAP_MOD_ADD:
+ case SLAP_MOD_SOFTADD: /* ITS#7487 */
+ case SLAP_MOD_ADD_IF_NOT_PRESENT: /* ITS#7487 */
+ assert( vals != NULL );
+
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
+ memberof_value_modify( op,
+ &vals[ i ], mo->mo_ad_member,
+ NULL, NULL,
+ &op->o_req_dn, &op->o_req_ndn );
+ }
+ break;
+
+ default:
+ assert( 0 );
+ }
+ }
+
+ if ( mci->what & MEMBEROF_IS_GROUP )
+ {
+ for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
+ if ( ml->sml_desc != mo->mo_ad_member ) {
+ continue;
+ }
+
+ switch ( ml->sml_op ) {
+ case LDAP_MOD_DELETE:
+ case SLAP_MOD_SOFTDEL: /* ITS#7487: can be used by syncrepl (in mirror mode?) */
+ vals = ml->sml_nvalues;
+ if ( vals != NULL ) {
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
+ memberof_value_modify( op,
+ &vals[ i ], mo->mo_ad_memberof,
+ &op->o_req_dn, &op->o_req_ndn,
+ NULL, NULL );
+ }
+ break;
+ }
+ /* fall thru */
+
+ case LDAP_MOD_REPLACE:
+ vals = mci->member;
+
+ /* delete all ... */
+ if ( vals != NULL ) {
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
+ memberof_value_modify( op,
+ &vals[ i ], mo->mo_ad_memberof,
+ &op->o_req_dn, &op->o_req_ndn,
+ NULL, NULL );
+ }
+ }
+
+ if ( ml->sml_op == LDAP_MOD_DELETE || ml->sml_op == SLAP_MOD_SOFTDEL || !ml->sml_values ) {
+ break;
+ }
+ /* fall thru */
+
+ case LDAP_MOD_ADD:
+ case SLAP_MOD_SOFTADD: /* ITS#7487 */
+ case SLAP_MOD_ADD_IF_NOT_PRESENT : /* ITS#7487 */
+ assert( ml->sml_nvalues != NULL );
+ vals = ml->sml_nvalues;
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
+ memberof_value_modify( op,
+ &vals[ i ], mo->mo_ad_memberof,
+ NULL, NULL,
+ &op->o_req_dn, &op->o_req_ndn );
+ }
+ break;
+
+ default:
+ assert( 0 );
+ }
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+ * response callback that adds/deletes member values when a group member
+ * is renamed.
+ */
+static int
+memberof_res_modrdn( Operation *op, SlapReply *rs )
+{
+ memberof_cbinfo_t *mci = op->o_callback->sc_private;
+ slap_overinst *on = mci->on;
+ memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
+
+ struct berval newPDN, newDN = BER_BVNULL, newPNDN, newNDN;
+ int i, rc;
+ BerVarray vals;
+
+ struct berval save_dn, save_ndn;
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ mci->what = MEMBEROF_IS_GROUP;
+ if ( MEMBEROF_REFINT( mo ) ) {
+ mci->what |= MEMBEROF_IS_MEMBER;
+ }
+
+ if ( op->orr_nnewSup ) {
+ newPNDN = *op->orr_nnewSup;
+
+ } else {
+ dnParent( &op->o_req_ndn, &newPNDN );
+ }
+
+ build_new_dn( &newNDN, &newPNDN, &op->orr_nnewrdn, op->o_tmpmemctx );
+
+ save_dn = op->o_req_dn;
+ save_ndn = op->o_req_ndn;
+
+ op->o_req_dn = newNDN;
+ op->o_req_ndn = newNDN;
+ rc = memberof_isGroupOrMember( op, mci );
+ op->o_req_dn = save_dn;
+ op->o_req_ndn = save_ndn;
+
+ if ( rc != LDAP_SUCCESS || mci->what == MEMBEROF_IS_NONE ) {
+ goto done;
+ }
+
+ if ( op->orr_newSup ) {
+ newPDN = *op->orr_newSup;
+
+ } else {
+ dnParent( &op->o_req_dn, &newPDN );
+ }
+
+ build_new_dn( &newDN, &newPDN, &op->orr_newrdn, op->o_tmpmemctx );
+
+ if ( mci->what & MEMBEROF_IS_GROUP ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rc = backend_attribute( op, NULL, &newNDN,
+ mo->mo_ad_member, &vals, ACL_READ );
+ op->o_bd->bd_info = (BackendInfo *)on;
+
+ if ( rc == LDAP_SUCCESS ) {
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
+ memberof_value_modify( op,
+ &vals[ i ], mo->mo_ad_memberof,
+ &op->o_req_dn, &op->o_req_ndn,
+ &newDN, &newNDN );
+ }
+ ber_bvarray_free_x( vals, op->o_tmpmemctx );
+ }
+ }
+
+ if ( MEMBEROF_REFINT( mo ) && ( mci->what & MEMBEROF_IS_MEMBER ) ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rc = backend_attribute( op, NULL, &newNDN,
+ mo->mo_ad_memberof, &vals, ACL_READ );
+ op->o_bd->bd_info = (BackendInfo *)on;
+
+ if ( rc == LDAP_SUCCESS ) {
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
+ memberof_value_modify( op,
+ &vals[ i ], mo->mo_ad_member,
+ &op->o_req_dn, &op->o_req_ndn,
+ &newDN, &newNDN );
+ }
+ ber_bvarray_free_x( vals, op->o_tmpmemctx );
+ }
+ }
+
+done:;
+ if ( !BER_BVISNULL( &newDN ) ) {
+ op->o_tmpfree( newDN.bv_val, op->o_tmpmemctx );
+ }
+ op->o_tmpfree( newNDN.bv_val, op->o_tmpmemctx );
+
+ return SLAP_CB_CONTINUE;
+}
+
+
+static int
+memberof_db_init(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ memberof_t *mo;
+ const char *text = NULL;
+ int rc;
+
+ mo = (memberof_t *)ch_calloc( 1, sizeof( memberof_t ) );
+
+ /* safe default */
+ mo->mo_dangling_err = LDAP_CONSTRAINT_VIOLATION;
+
+ if ( !ad_memberOf ) {
+ rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &ad_memberOf, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "memberof_db_init: "
+ "unable to find attribute=\"%s\": %s (%d)\n",
+ SLAPD_MEMBEROF_ATTR, text, rc );
+ return rc;
+ }
+ }
+
+ if ( !ad_member ) {
+ rc = slap_str2ad( SLAPD_GROUP_ATTR, &ad_member, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "memberof_db_init: "
+ "unable to find attribute=\"%s\": %s (%d)\n",
+ SLAPD_GROUP_ATTR, text, rc );
+ return rc;
+ }
+ }
+
+ if ( !oc_group ) {
+ oc_group = oc_find( SLAPD_GROUP_CLASS );
+ if ( oc_group == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "memberof_db_init: "
+ "unable to find objectClass=\"%s\"\n",
+ SLAPD_GROUP_CLASS );
+ return 1;
+ }
+ }
+
+ on->on_bi.bi_private = (void *)mo;
+
+ return 0;
+}
+
+enum {
+ MO_DN = 1,
+ MO_DANGLING,
+ MO_REFINT,
+ MO_GROUP_OC,
+ MO_MEMBER_AD,
+ MO_MEMBER_OF_AD,
+#if 0
+ MO_REVERSE,
+#endif
+
+ MO_DANGLING_ERROR,
+
+ MO_LAST
+};
+
+static ConfigDriver mo_cf_gen;
+
+#define OID "1.3.6.1.4.1.7136.2.666.4"
+#define OIDAT OID ".1.1"
+#define OIDCFGAT OID ".1.2"
+#define OIDOC OID ".2.1"
+#define OIDCFGOC OID ".2.2"
+
+
+static ConfigTable mo_cfg[] = {
+ { "memberof-dn", "modifiersName",
+ 2, 2, 0, ARG_MAGIC|ARG_QUOTE|ARG_DN|MO_DN, mo_cf_gen,
+ "( OLcfgOvAt:18.0 NAME 'olcMemberOfDN' "
+ "DESC 'DN to be used as modifiersName' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "memberof-dangling", "ignore|drop|error",
+ 2, 2, 0, ARG_MAGIC|MO_DANGLING, mo_cf_gen,
+ "( OLcfgOvAt:18.1 NAME 'olcMemberOfDangling' "
+ "DESC 'Behavior with respect to dangling members, "
+ "constrained to ignore, drop, error' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "memberof-refint", "true|FALSE",
+ 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|MO_REFINT, mo_cf_gen,
+ "( OLcfgOvAt:18.2 NAME 'olcMemberOfRefInt' "
+ "DESC 'Take care of referential integrity' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "memberof-group-oc", "objectClass",
+ 2, 2, 0, ARG_MAGIC|MO_GROUP_OC, mo_cf_gen,
+ "( OLcfgOvAt:18.3 NAME 'olcMemberOfGroupOC' "
+ "DESC 'Group objectClass' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "memberof-member-ad", "member attribute",
+ 2, 2, 0, ARG_MAGIC|ARG_ATDESC|MO_MEMBER_AD, mo_cf_gen,
+ "( OLcfgOvAt:18.4 NAME 'olcMemberOfMemberAD' "
+ "DESC 'member attribute' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "memberof-memberof-ad", "memberOf attribute",
+ 2, 2, 0, ARG_MAGIC|ARG_ATDESC|MO_MEMBER_OF_AD, mo_cf_gen,
+ "( OLcfgOvAt:18.5 NAME 'olcMemberOfMemberOfAD' "
+ "DESC 'memberOf attribute' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )",
+ NULL, NULL },
+
+#if 0
+ { "memberof-reverse", "true|FALSE",
+ 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|MO_REVERSE, mo_cf_gen,
+ "( OLcfgOvAt:18.6 NAME 'olcMemberOfReverse' "
+ "DESC 'Take care of referential integrity "
+ "also when directly modifying memberOf' "
+ "SYNTAX OMsBoolean SINGLE-VALUE )",
+ NULL, NULL },
+#endif
+
+ { "memberof-dangling-error", "error code",
+ 2, 2, 0, ARG_MAGIC|MO_DANGLING_ERROR, mo_cf_gen,
+ "( OLcfgOvAt:18.7 NAME 'olcMemberOfDanglingError' "
+ "DESC 'Error code returned in case of dangling back reference' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )",
+ NULL, NULL },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs mo_ocs[] = {
+ { "( OLcfgOvOc:18.1 "
+ "NAME ( 'olcMemberOfConfig' 'olcMemberOf' ) "
+ "DESC 'Member-of configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( "
+ "olcMemberOfDN "
+ "$ olcMemberOfDangling "
+ "$ olcMemberOfDanglingError"
+ "$ olcMemberOfRefInt "
+ "$ olcMemberOfGroupOC "
+ "$ olcMemberOfMemberAD "
+ "$ olcMemberOfMemberOfAD "
+#if 0
+ "$ olcMemberOfReverse "
+#endif
+ ") "
+ ")",
+ Cft_Overlay, mo_cfg, NULL, NULL },
+ { NULL, 0, NULL }
+};
+
+static slap_verbmasks dangling_mode[] = {
+ { BER_BVC( "ignore" ), MEMBEROF_NONE },
+ { BER_BVC( "drop" ), MEMBEROF_FDANGLING_DROP },
+ { BER_BVC( "error" ), MEMBEROF_FDANGLING_ERROR },
+ { BER_BVNULL, 0 }
+};
+
+static int
+memberof_make_group_filter( memberof_t *mo )
+{
+ char *ptr;
+
+ if ( !BER_BVISNULL( &mo->mo_groupFilterstr ) ) {
+ ch_free( mo->mo_groupFilterstr.bv_val );
+ }
+
+ mo->mo_groupFilter.f_choice = LDAP_FILTER_EQUALITY;
+ mo->mo_groupFilter.f_ava = &mo->mo_groupAVA;
+
+ mo->mo_groupFilter.f_av_desc = slap_schema.si_ad_objectClass;
+ mo->mo_groupFilter.f_av_value = mo->mo_oc_group->soc_cname;
+
+ mo->mo_groupFilterstr.bv_len = STRLENOF( "(=)" )
+ + slap_schema.si_ad_objectClass->ad_cname.bv_len
+ + mo->mo_oc_group->soc_cname.bv_len;
+ ptr = mo->mo_groupFilterstr.bv_val = ch_malloc( mo->mo_groupFilterstr.bv_len + 1 );
+ *ptr++ = '(';
+ ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
+ *ptr++ = '=';
+ ptr = lutil_strcopy( ptr, mo->mo_oc_group->soc_cname.bv_val );
+ *ptr++ = ')';
+ *ptr = '\0';
+
+ return 0;
+}
+
+static int
+memberof_make_member_filter( memberof_t *mo )
+{
+ char *ptr;
+
+ if ( !BER_BVISNULL( &mo->mo_memberFilterstr ) ) {
+ ch_free( mo->mo_memberFilterstr.bv_val );
+ }
+
+ mo->mo_memberFilter.f_choice = LDAP_FILTER_PRESENT;
+ mo->mo_memberFilter.f_desc = mo->mo_ad_memberof;
+
+ mo->mo_memberFilterstr.bv_len = STRLENOF( "(=*)" )
+ + mo->mo_ad_memberof->ad_cname.bv_len;
+ ptr = mo->mo_memberFilterstr.bv_val = ch_malloc( mo->mo_memberFilterstr.bv_len + 1 );
+ *ptr++ = '(';
+ ptr = lutil_strcopy( ptr, mo->mo_ad_memberof->ad_cname.bv_val );
+ ptr = lutil_strcopy( ptr, "=*)" );
+
+ return 0;
+}
+
+static int
+mo_cf_gen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
+
+ int i, rc = 0;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ struct berval bv = BER_BVNULL;
+
+ switch( c->type ) {
+ case MO_DN:
+ if ( mo->mo_dn.bv_val != NULL) {
+ value_add_one( &c->rvalue_vals, &mo->mo_dn );
+ value_add_one( &c->rvalue_nvals, &mo->mo_ndn );
+ }
+ break;
+
+ case MO_DANGLING:
+ enum_to_verb( dangling_mode, (mo->mo_flags & MEMBEROF_FDANGLING_MASK), &bv );
+ if ( BER_BVISNULL( &bv ) ) {
+ /* there's something wrong... */
+ assert( 0 );
+ rc = 1;
+
+ } else {
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
+
+ case MO_DANGLING_ERROR:
+ if ( mo->mo_flags & MEMBEROF_FDANGLING_ERROR ) {
+ char buf[ SLAP_TEXT_BUFLEN ];
+ enum_to_verb( slap_ldap_response_code, mo->mo_dangling_err, &bv );
+ if ( BER_BVISNULL( &bv ) ) {
+ bv.bv_len = snprintf( buf, sizeof( buf ), "0x%x", mo->mo_dangling_err );
+ if ( bv.bv_len < sizeof( buf ) ) {
+ bv.bv_val = buf;
+ } else {
+ rc = 1;
+ break;
+ }
+ }
+ value_add_one( &c->rvalue_vals, &bv );
+ } else {
+ rc = 1;
+ }
+ break;
+
+ case MO_REFINT:
+ c->value_int = MEMBEROF_REFINT( mo );
+ break;
+
+#if 0
+ case MO_REVERSE:
+ c->value_int = MEMBEROF_REVERSE( mo );
+ break;
+#endif
+
+ case MO_GROUP_OC:
+ if ( mo->mo_oc_group != NULL ){
+ value_add_one( &c->rvalue_vals, &mo->mo_oc_group->soc_cname );
+ }
+ break;
+
+ case MO_MEMBER_AD:
+ c->value_ad = mo->mo_ad_member;
+ break;
+
+ case MO_MEMBER_OF_AD:
+ c->value_ad = mo->mo_ad_memberof;
+ break;
+
+ default:
+ assert( 0 );
+ return 1;
+ }
+
+ return rc;
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ switch( c->type ) {
+ case MO_DN:
+ if ( !BER_BVISNULL( &mo->mo_dn ) ) {
+ ber_memfree( mo->mo_dn.bv_val );
+ ber_memfree( mo->mo_ndn.bv_val );
+ BER_BVZERO( &mo->mo_dn );
+ BER_BVZERO( &mo->mo_ndn );
+ }
+ break;
+
+ case MO_DANGLING:
+ mo->mo_flags &= ~MEMBEROF_FDANGLING_MASK;
+ break;
+
+ case MO_DANGLING_ERROR:
+ mo->mo_dangling_err = LDAP_CONSTRAINT_VIOLATION;
+ break;
+
+ case MO_REFINT:
+ mo->mo_flags &= ~MEMBEROF_FREFINT;
+ break;
+
+#if 0
+ case MO_REVERSE:
+ mo->mo_flags &= ~MEMBEROF_FREVERSE;
+ break;
+#endif
+
+ case MO_GROUP_OC:
+ mo->mo_oc_group = oc_group;
+ memberof_make_group_filter( mo );
+ break;
+
+ case MO_MEMBER_AD:
+ mo->mo_ad_member = ad_member;
+ break;
+
+ case MO_MEMBER_OF_AD:
+ mo->mo_ad_memberof = ad_memberOf;
+ memberof_make_member_filter( mo );
+ break;
+
+ default:
+ assert( 0 );
+ return 1;
+ }
+
+ } else {
+ switch( c->type ) {
+ case MO_DN:
+ if ( !BER_BVISNULL( &mo->mo_dn ) ) {
+ ber_memfree( mo->mo_dn.bv_val );
+ ber_memfree( mo->mo_ndn.bv_val );
+ }
+ mo->mo_dn = c->value_dn;
+ mo->mo_ndn = c->value_ndn;
+ break;
+
+ case MO_DANGLING:
+ i = verb_to_mask( c->argv[ 1 ], dangling_mode );
+ if ( BER_BVISNULL( &dangling_mode[ i ].word ) ) {
+ return 1;
+ }
+
+ mo->mo_flags &= ~MEMBEROF_FDANGLING_MASK;
+ mo->mo_flags |= dangling_mode[ i ].mask;
+ break;
+
+ case MO_DANGLING_ERROR:
+ i = verb_to_mask( c->argv[ 1 ], slap_ldap_response_code );
+ if ( !BER_BVISNULL( &slap_ldap_response_code[ i ].word ) ) {
+ mo->mo_dangling_err = slap_ldap_response_code[ i ].mask;
+ } else if ( lutil_atoix( &mo->mo_dangling_err, c->argv[ 1 ], 0 ) ) {
+ return 1;
+ }
+ break;
+
+ case MO_REFINT:
+ if ( c->value_int ) {
+ mo->mo_flags |= MEMBEROF_FREFINT;
+
+ } else {
+ mo->mo_flags &= ~MEMBEROF_FREFINT;
+ }
+ break;
+
+#if 0
+ case MO_REVERSE:
+ if ( c->value_int ) {
+ mo->mo_flags |= MEMBEROF_FREVERSE;
+
+ } else {
+ mo->mo_flags &= ~MEMBEROF_FREVERSE;
+ }
+ break;
+#endif
+
+ case MO_GROUP_OC: {
+ ObjectClass *oc = NULL;
+
+ oc = oc_find( c->argv[ 1 ] );
+ if ( oc == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to find group objectClass=\"%s\"",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+ mo->mo_oc_group = oc;
+ memberof_make_group_filter( mo );
+ } break;
+
+ case MO_MEMBER_AD: {
+ AttributeDescription *ad = c->value_ad;
+
+ if ( !is_at_syntax( ad->ad_type, SLAPD_DN_SYNTAX ) /* e.g. "member" */
+ && !is_at_syntax( ad->ad_type, SLAPD_NAMEUID_SYNTAX ) ) /* e.g. "uniqueMember" */
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "member attribute=\"%s\" must either "
+ "have DN (%s) or nameUID (%s) syntax",
+ c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+ mo->mo_ad_member = ad;
+ } break;
+
+ case MO_MEMBER_OF_AD: {
+ AttributeDescription *ad = c->value_ad;
+
+ if ( !is_at_syntax( ad->ad_type, SLAPD_DN_SYNTAX ) /* e.g. "member" */
+ && !is_at_syntax( ad->ad_type, SLAPD_NAMEUID_SYNTAX ) ) /* e.g. "uniqueMember" */
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "memberof attribute=\"%s\" must either "
+ "have DN (%s) or nameUID (%s) syntax",
+ c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n",
+ c->log, c->cr_msg );
+ return 1;
+ }
+
+ mo->mo_ad_memberof = ad;
+ memberof_make_member_filter( mo );
+ } break;
+
+ default:
+ assert( 0 );
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+memberof_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
+
+ int rc;
+
+ if ( !mo->mo_ad_memberof ) {
+ mo->mo_ad_memberof = ad_memberOf;
+ }
+
+ if ( ! mo->mo_ad_member ) {
+ mo->mo_ad_member = ad_member;
+ }
+
+ if ( ! mo->mo_oc_group ) {
+ mo->mo_oc_group = oc_group;
+ }
+
+ if ( BER_BVISNULL( &mo->mo_dn ) && !BER_BVISNULL( &be->be_rootdn ) ) {
+ ber_dupbv( &mo->mo_dn, &be->be_rootdn );
+ ber_dupbv( &mo->mo_ndn, &be->be_rootndn );
+ }
+
+ if ( BER_BVISNULL( &mo->mo_groupFilterstr ) ) {
+ memberof_make_group_filter( mo );
+ }
+
+ if ( BER_BVISNULL( &mo->mo_memberFilterstr ) ) {
+ memberof_make_member_filter( mo );
+ }
+
+ return 0;
+}
+
+static int
+memberof_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
+
+ if ( mo ) {
+ if ( !BER_BVISNULL( &mo->mo_dn ) ) {
+ ber_memfree( mo->mo_dn.bv_val );
+ ber_memfree( mo->mo_ndn.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &mo->mo_groupFilterstr ) ) {
+ ber_memfree( mo->mo_groupFilterstr.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &mo->mo_memberFilterstr ) ) {
+ ber_memfree( mo->mo_memberFilterstr.bv_val );
+ }
+
+ ber_memfree( mo );
+ }
+
+ return 0;
+}
+
+static struct {
+ char *desc;
+ AttributeDescription **adp;
+} as[] = {
+ { "( 1.2.840.113556.1.2.102 "
+ "NAME 'memberOf' "
+ "DESC 'Group that the entry belongs to' "
+ "SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' "
+ "EQUALITY distinguishedNameMatch " /* added */
+ "USAGE dSAOperation " /* added; questioned */
+ "NO-USER-MODIFICATION " /* added */
+ "X-ORIGIN 'iPlanet Delegated Administrator' )",
+ &ad_memberOf },
+ { NULL }
+};
+
+#if SLAPD_OVER_MEMBEROF == SLAPD_MOD_DYNAMIC
+static
+#endif /* SLAPD_OVER_MEMBEROF == SLAPD_MOD_DYNAMIC */
+int
+memberof_initialize( void )
+{
+ int code, i;
+
+ for ( i = 0; as[ i ].desc != NULL; i++ ) {
+ code = register_at( as[ i ].desc, as[ i ].adp, 1 );
+ if ( code && code != SLAP_SCHERR_ATTR_DUP ) {
+ Debug( LDAP_DEBUG_ANY,
+ "memberof_initialize: register_at #%d failed\n",
+ i );
+ return code;
+ }
+ }
+
+ memberof.on_bi.bi_type = "memberof";
+
+ memberof.on_bi.bi_db_init = memberof_db_init;
+ memberof.on_bi.bi_db_open = memberof_db_open;
+ memberof.on_bi.bi_db_destroy = memberof_db_destroy;
+
+ memberof.on_bi.bi_op_add = memberof_op_add;
+ memberof.on_bi.bi_op_delete = memberof_op_delete;
+ memberof.on_bi.bi_op_modify = memberof_op_modify;
+ memberof.on_bi.bi_op_modrdn = memberof_op_modrdn;
+
+ memberof.on_bi.bi_cf_ocs = mo_ocs;
+
+ code = config_register_schema( mo_cfg, mo_ocs );
+ if ( code ) return code;
+
+ return overlay_register( &memberof );
+}
+
+#if SLAPD_OVER_MEMBEROF == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return memberof_initialize();
+}
+#endif /* SLAPD_OVER_MEMBEROF == SLAPD_MOD_DYNAMIC */
+
+#endif /* SLAPD_OVER_MEMBEROF */
diff --git a/servers/slapd/overlays/otp.c b/servers/slapd/overlays/otp.c
new file mode 100644
index 0000000..c9c0332
--- /dev/null
+++ b/servers/slapd/overlays/otp.c
@@ -0,0 +1,974 @@
+/* otp.c - OATH 2-factor authentication module */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2015-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2015 by Howard Chu, Symas Corp.
+ * Portions Copyright 2016-2017 by Michael Ströder <michael@stroeder.com>
+ * Portions Copyright 2018 by Ondřej Kuzník, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work includes code from the lastbind overlay.
+ */
+
+#include <portable.h>
+
+#ifdef SLAPD_OVER_OTP
+
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+#include <lber.h>
+#include <lber_pvt.h>
+#include "lutil.h"
+#include <ac/stdlib.h>
+#include <ac/ctype.h>
+#include <ac/string.h>
+/* include socket.h to get sys/types.h and/or winsock2.h */
+#include <ac/socket.h>
+
+#if HAVE_OPENSSL
+#include <openssl/sha.h>
+#include <openssl/hmac.h>
+
+#define TOTP_SHA512_DIGEST_LENGTH SHA512_DIGEST_LENGTH
+#define TOTP_SHA1 EVP_sha1()
+#define TOTP_SHA224 EVP_sha224()
+#define TOTP_SHA256 EVP_sha256()
+#define TOTP_SHA384 EVP_sha384()
+#define TOTP_SHA512 EVP_sha512()
+#define TOTP_HMAC_CTX HMAC_CTX *
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+static HMAC_CTX *
+HMAC_CTX_new( void )
+{
+ HMAC_CTX *ctx = OPENSSL_malloc( sizeof(*ctx) );
+ if ( ctx != NULL ) {
+ HMAC_CTX_init( ctx );
+ }
+ return ctx;
+}
+
+static void
+HMAC_CTX_free( HMAC_CTX *ctx )
+{
+ if ( ctx != NULL ) {
+ HMAC_CTX_cleanup( ctx );
+ OPENSSL_free( ctx );
+ }
+}
+#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
+
+#define HMAC_setup( ctx, key, len, hash ) \
+ ctx = HMAC_CTX_new(); \
+ HMAC_Init_ex( ctx, key, len, hash, 0 )
+#define HMAC_crunch( ctx, buf, len ) HMAC_Update( ctx, buf, len )
+#define HMAC_finish( ctx, dig, dlen ) \
+ HMAC_Final( ctx, dig, &dlen ); \
+ HMAC_CTX_free( ctx )
+
+#elif HAVE_GNUTLS
+#include <nettle/hmac.h>
+
+#define TOTP_SHA512_DIGEST_LENGTH SHA512_DIGEST_SIZE
+#define TOTP_SHA1 &nettle_sha1
+#define TOTP_SHA224 &nettle_sha224
+#define TOTP_SHA256 &nettle_sha256
+#define TOTP_SHA384 &nettle_sha384
+#define TOTP_SHA512 &nettle_sha512
+#define TOTP_HMAC_CTX struct hmac_sha512_ctx
+
+#define HMAC_setup( ctx, key, len, hash ) \
+ const struct nettle_hash *h = hash; \
+ hmac_set_key( &ctx.outer, &ctx.inner, &ctx.state, h, len, key )
+#define HMAC_crunch( ctx, buf, len ) hmac_update( &ctx.state, h, len, buf )
+#define HMAC_finish( ctx, dig, dlen ) \
+ hmac_digest( &ctx.outer, &ctx.inner, &ctx.state, h, h->digest_size, dig ); \
+ dlen = h->digest_size
+
+#else
+#error Unsupported crypto backend.
+#endif
+
+#include "slap.h"
+#include "slap-config.h"
+
+/* Schema from OATH-LDAP project by Michael Ströder */
+
+static struct {
+ char *name, *oid;
+} otp_oid[] = {
+ { "oath-ldap", "1.3.6.1.4.1.5427.1.389.4226" },
+ { "oath-ldap-at", "oath-ldap:4" },
+ { "oath-ldap-oc", "oath-ldap:6" },
+ { NULL }
+};
+
+AttributeDescription *ad_oathOTPToken;
+AttributeDescription *ad_oathSecret;
+AttributeDescription *ad_oathOTPLength;
+AttributeDescription *ad_oathHMACAlgorithm;
+
+AttributeDescription *ad_oathHOTPParams;
+AttributeDescription *ad_oathHOTPToken;
+AttributeDescription *ad_oathHOTPCounter;
+AttributeDescription *ad_oathHOTPLookahead;
+
+AttributeDescription *ad_oathTOTPTimeStepPeriod;
+AttributeDescription *ad_oathTOTPParams;
+AttributeDescription *ad_oathTOTPToken;
+AttributeDescription *ad_oathTOTPLastTimeStep;
+AttributeDescription *ad_oathTOTPTimeStepWindow;
+AttributeDescription *ad_oathTOTPTimeStepDrift;
+
+static struct otp_at {
+ char *schema;
+ AttributeDescription **adp;
+} otp_at[] = {
+ { "( oath-ldap-at:1 "
+ "NAME 'oathSecret' "
+ "DESC 'OATH-LDAP: Shared Secret (possibly encrypted with public key in oathEncKey)' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "EQUALITY octetStringMatch "
+ "SUBSTR octetStringSubstringsMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )",
+ &ad_oathSecret },
+
+ { "( oath-ldap-at:2 "
+ "NAME 'oathTokenSerialNumber' "
+ "DESC 'OATH-LDAP: Proprietary hardware token serial number assigned by vendor' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "EQUALITY caseIgnoreMatch "
+ "SUBSTR caseIgnoreSubstringsMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.44{64})" },
+
+ { "( oath-ldap-at:3 "
+ "NAME 'oathTokenIdentifier' "
+ "DESC 'OATH-LDAP: Globally unique OATH token identifier' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )" },
+
+ { "( oath-ldap-at:4 "
+ "NAME 'oathParamsEntry' "
+ "DESC 'OATH-LDAP: DN pointing to OATH parameter/policy object' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "SUP distinguishedName )" },
+ { "( oath-ldap-at:4.1 "
+ "NAME 'oathTOTPTimeStepPeriod' "
+ "DESC 'OATH-LDAP: Time window for TOTP (seconds)' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
+ &ad_oathTOTPTimeStepPeriod },
+
+ { "( oath-ldap-at:5 "
+ "NAME 'oathOTPLength' "
+ "DESC 'OATH-LDAP: Length of OTP (number of digits)' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
+ &ad_oathOTPLength },
+ { "( oath-ldap-at:5.1 "
+ "NAME 'oathHOTPParams' "
+ "DESC 'OATH-LDAP: DN pointing to HOTP parameter object' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "SUP oathParamsEntry )",
+ &ad_oathHOTPParams },
+ { "( oath-ldap-at:5.2 "
+ "NAME 'oathTOTPParams' "
+ "DESC 'OATH-LDAP: DN pointing to TOTP parameter object' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "SUP oathParamsEntry )",
+ &ad_oathTOTPParams },
+
+ { "( oath-ldap-at:6 "
+ "NAME 'oathHMACAlgorithm' "
+ "DESC 'OATH-LDAP: HMAC algorithm used for generating OTP values' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "EQUALITY objectIdentifierMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )",
+ &ad_oathHMACAlgorithm },
+
+ { "( oath-ldap-at:7 "
+ "NAME 'oathTimestamp' "
+ "DESC 'OATH-LDAP: Timestamp (not directly used).' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "EQUALITY generalizedTimeMatch "
+ "ORDERING generalizedTimeOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )" },
+ { "( oath-ldap-at:7.1 "
+ "NAME 'oathLastFailure' "
+ "DESC 'OATH-LDAP: Timestamp of last failed OATH validation' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "SUP oathTimestamp )" },
+ { "( oath-ldap-at:7.2 "
+ "NAME 'oathLastLogin' "
+ "DESC 'OATH-LDAP: Timestamp of last successful OATH validation' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "SUP oathTimestamp )" },
+ { "( oath-ldap-at:7.3 "
+ "NAME 'oathSecretTime' "
+ "DESC 'OATH-LDAP: Timestamp of generation of oathSecret attribute.' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "SUP oathTimestamp )" },
+
+ { "( oath-ldap-at:8 "
+ "NAME 'oathSecretMaxAge' "
+ "DESC 'OATH-LDAP: Time in seconds for which the shared secret (oathSecret) will be valid from oathSecretTime value.' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )" },
+
+ { "( oath-ldap-at:9 "
+ "NAME 'oathToken' "
+ "DESC 'OATH-LDAP: DN pointing to OATH token object' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "SUP distinguishedName )" },
+ { "( oath-ldap-at:9.1 "
+ "NAME 'oathHOTPToken' "
+ "DESC 'OATH-LDAP: DN pointing to OATH/HOTP token object' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "SUP oathToken )",
+ &ad_oathHOTPToken },
+ { "( oath-ldap-at:9.2 "
+ "NAME 'oathTOTPToken' "
+ "DESC 'OATH-LDAP: DN pointing to OATH/TOTP token object' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "SUP oathToken )",
+ &ad_oathTOTPToken },
+
+ { "( oath-ldap-at:10 "
+ "NAME 'oathCounter' "
+ "DESC 'OATH-LDAP: Counter for OATH data (not directly used)' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )" },
+ { "( oath-ldap-at:10.1 "
+ "NAME 'oathFailureCount' "
+ "DESC 'OATH-LDAP: OATH failure counter' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "SUP oathCounter )" },
+ { "( oath-ldap-at:10.2 "
+ "NAME 'oathHOTPCounter' "
+ "DESC 'OATH-LDAP: Counter for HOTP' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "SUP oathCounter )",
+ &ad_oathHOTPCounter },
+ { "( oath-ldap-at:10.3 "
+ "NAME 'oathHOTPLookAhead' "
+ "DESC 'OATH-LDAP: Look-ahead window for HOTP' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "SUP oathCounter )",
+ &ad_oathHOTPLookahead },
+ { "( oath-ldap-at:10.5 "
+ "NAME 'oathThrottleLimit' "
+ "DESC 'OATH-LDAP: Failure throttle limit' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "SUP oathCounter )" },
+ { "( oath-ldap-at:10.6 "
+ "NAME 'oathTOTPLastTimeStep' "
+ "DESC 'OATH-LDAP: Last time step seen for TOTP (time/period)' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "SUP oathCounter )",
+ &ad_oathTOTPLastTimeStep },
+ { "( oath-ldap-at:10.7 "
+ "NAME 'oathMaxUsageCount' "
+ "DESC 'OATH-LDAP: Maximum number of times a token can be used' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "SUP oathCounter )" },
+ { "( oath-ldap-at:10.8 "
+ "NAME 'oathTOTPTimeStepWindow' "
+ "DESC 'OATH-LDAP: Size of time step +/- tolerance window used for TOTP validation' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "SUP oathCounter )",
+ &ad_oathTOTPTimeStepWindow },
+ { "( oath-ldap-at:10.9 "
+ "NAME 'oathTOTPTimeStepDrift' "
+ "DESC 'OATH-LDAP: Last observed time step shift seen for TOTP' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "SUP oathCounter )",
+ &ad_oathTOTPTimeStepDrift },
+
+ { "( oath-ldap-at:11 "
+ "NAME 'oathSecretLength' "
+ "DESC 'OATH-LDAP: Length of plain-text shared secret (number of bytes)' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )" },
+
+ { "( oath-ldap-at:12 "
+ "NAME 'oathEncKey' "
+ "DESC 'OATH-LDAP: public key to be used for encrypting new shared secrets' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "EQUALITY caseIgnoreMatch "
+ "SUBSTR caseIgnoreSubstringsMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )" },
+
+ { "( oath-ldap-at:13 "
+ "NAME 'oathResultCode' "
+ "DESC 'OATH-LDAP: LDAP resultCode to use in response' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )" },
+ { "( oath-ldap-at:13.1 "
+ "NAME 'oathSuccessResultCode' "
+ "DESC 'OATH-LDAP: success resultCode to use in bind/compare response' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SUP oathResultCode )" },
+ { "( oath-ldap-at:13.2 "
+ "NAME 'oathFailureResultCode' "
+ "DESC 'OATH-LDAP: failure resultCode to use in bind/compare response' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SUP oathResultCode )" },
+
+ { "( oath-ldap-at:14 "
+ "NAME 'oathTokenPIN' "
+ "DESC 'OATH-LDAP: Configuration PIN (possibly encrypted with oathEncKey)' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "EQUALITY caseIgnoreMatch "
+ "SUBSTR caseIgnoreSubstringsMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )" },
+
+ { "( oath-ldap-at:15 "
+ "NAME 'oathMessage' "
+ "DESC 'OATH-LDAP: success diagnosticMessage to use in bind/compare response' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SINGLE-VALUE "
+ "EQUALITY caseIgnoreMatch "
+ "SUBSTR caseIgnoreSubstringsMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )" },
+ { "( oath-ldap-at:15.1 "
+ "NAME 'oathSuccessMessage' "
+ "DESC 'OATH-LDAP: success diagnosticMessage to use in bind/compare response' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SUP oathMessage )" },
+ { "( oath-ldap-at:15.2 "
+ "NAME 'oathFailureMessage' "
+ "DESC 'OATH-LDAP: failure diagnosticMessage to use in bind/compare response' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "SUP oathMessage )" },
+
+ { NULL }
+};
+
+ObjectClass *oc_oathOTPUser;
+ObjectClass *oc_oathHOTPToken;
+ObjectClass *oc_oathTOTPToken;
+ObjectClass *oc_oathHOTPParams;
+ObjectClass *oc_oathTOTPParams;
+
+static struct otp_oc {
+ char *schema;
+ ObjectClass **ocp;
+} otp_oc[] = {
+ { "( oath-ldap-oc:1 "
+ "NAME 'oathUser' "
+ "DESC 'OATH-LDAP: User Object' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "ABSTRACT )",
+ &oc_oathOTPUser },
+ { "( oath-ldap-oc:1.1 "
+ "NAME 'oathHOTPUser' "
+ "DESC 'OATH-LDAP: HOTP user object' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "AUXILIARY "
+ "SUP oathUser "
+ "MAY ( oathHOTPToken ) )" },
+ { "( oath-ldap-oc:1.2 "
+ "NAME 'oathTOTPUser' "
+ "DESC 'OATH-LDAP: TOTP user object' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "AUXILIARY "
+ "SUP oathUser "
+ "MUST ( oathTOTPToken ) )" },
+ { "( oath-ldap-oc:2 "
+ "NAME 'oathParams' "
+ "DESC 'OATH-LDAP: Parameter object' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "ABSTRACT "
+ "MUST ( oathOTPLength $ oathHMACAlgorithm ) "
+ "MAY ( oathSecretMaxAge $ oathSecretLength $ "
+ "oathMaxUsageCount $ oathThrottleLimit $ oathEncKey $ "
+ "oathSuccessResultCode $ oathSuccessMessage $ "
+ "oathFailureResultCode $ oathFailureMessage ) )" },
+ { "( oath-ldap-oc:2.1 "
+ "NAME 'oathHOTPParams' "
+ "DESC 'OATH-LDAP: HOTP parameter object' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "AUXILIARY "
+ "SUP oathParams "
+ "MUST ( oathHOTPLookAhead ) )",
+ &oc_oathHOTPParams },
+ { "( oath-ldap-oc:2.2 "
+ "NAME 'oathTOTPParams' "
+ "DESC 'OATH-LDAP: TOTP parameter object' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "AUXILIARY "
+ "SUP oathParams "
+ "MUST ( oathTOTPTimeStepPeriod ) "
+ "MAY ( oathTOTPTimeStepWindow ) )",
+ &oc_oathTOTPParams },
+ { "( oath-ldap-oc:3 "
+ "NAME 'oathToken' "
+ "DESC 'OATH-LDAP: User Object' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "ABSTRACT "
+ "MAY ( oathSecret $ oathSecretTime $ "
+ "oathLastLogin $ oathFailureCount $ oathLastFailure $ "
+ "oathTokenSerialNumber $ oathTokenIdentifier $ oathTokenPIN ) )" },
+ { "( oath-ldap-oc:3.1 "
+ "NAME 'oathHOTPToken' "
+ "DESC 'OATH-LDAP: HOTP token object' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "AUXILIARY "
+ "SUP oathToken "
+ "MAY ( oathHOTPParams $ oathHOTPCounter ) )",
+ &oc_oathHOTPToken },
+ { "( oath-ldap-oc:3.2 "
+ "NAME 'oathTOTPToken' "
+ "DESC 'OATH-LDAP: TOTP token' "
+ "X-ORIGIN 'OATH-LDAP' "
+ "AUXILIARY "
+ "SUP oathToken "
+ "MAY ( oathTOTPParams $ oathTOTPLastTimeStep $ oathTOTPTimeStepDrift ) )",
+ &oc_oathTOTPToken },
+ { NULL }
+};
+
+typedef struct myval {
+ ber_len_t mv_len;
+ void *mv_val;
+} myval;
+
+static void
+do_hmac( const void *hash, myval *key, myval *data, myval *out )
+{
+ TOTP_HMAC_CTX ctx;
+ unsigned int digestLen;
+
+ HMAC_setup( ctx, key->mv_val, key->mv_len, hash );
+ HMAC_crunch( ctx, data->mv_val, data->mv_len );
+ HMAC_finish( ctx, out->mv_val, digestLen );
+ out->mv_len = digestLen;
+}
+
+#define MAX_DIGITS 8
+static const int DIGITS_POWER[] = {
+ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
+};
+
+static const void *
+otp_choose_mech( struct berval *oid )
+{
+ /* RFC 8018 OIDs */
+ const struct berval oid_hmacwithSHA1 = BER_BVC("1.2.840.113549.2.7");
+ const struct berval oid_hmacwithSHA224 = BER_BVC("1.2.840.113549.2.8");
+ const struct berval oid_hmacwithSHA256 = BER_BVC("1.2.840.113549.2.9");
+ const struct berval oid_hmacwithSHA384 = BER_BVC("1.2.840.113549.2.10");
+ const struct berval oid_hmacwithSHA512 = BER_BVC("1.2.840.113549.2.11");
+
+ if ( !ber_bvcmp( &oid_hmacwithSHA1, oid ) ) {
+ return TOTP_SHA1;
+ } else if ( !ber_bvcmp( &oid_hmacwithSHA224, oid ) ) {
+ return TOTP_SHA224;
+ } else if ( !ber_bvcmp( &oid_hmacwithSHA256, oid ) ) {
+ return TOTP_SHA256;
+ } else if ( !ber_bvcmp( &oid_hmacwithSHA384, oid ) ) {
+ return TOTP_SHA384;
+ } else if ( !ber_bvcmp( &oid_hmacwithSHA512, oid ) ) {
+ return TOTP_SHA512;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "otp_choose_mech: "
+ "hmac OID %s unsupported\n",
+ oid->bv_val );
+ return NULL;
+}
+
+static void
+generate(
+ struct berval *bv,
+ uint64_t tval,
+ int digits,
+ struct berval *out,
+ const void *mech )
+{
+ unsigned char digest[TOTP_SHA512_DIGEST_LENGTH];
+ myval digval;
+ myval key, data;
+ unsigned char msg[8];
+ int i, offset, res, otp;
+
+#if WORDS_BIGENDIAN
+ *(uint64_t *)msg = tval;
+#else
+ for ( i = 7; i >= 0; i-- ) {
+ msg[i] = tval & 0xff;
+ tval >>= 8;
+ }
+#endif
+
+ key.mv_len = bv->bv_len;
+ key.mv_val = bv->bv_val;
+
+ data.mv_val = msg;
+ data.mv_len = sizeof(msg);
+
+ digval.mv_val = digest;
+ digval.mv_len = sizeof(digest);
+ do_hmac( mech, &key, &data, &digval );
+
+ offset = digest[digval.mv_len - 1] & 0xf;
+ res = ( (digest[offset] & 0x7f) << 24 ) |
+ ( ( digest[offset + 1] & 0xff ) << 16 ) |
+ ( ( digest[offset + 2] & 0xff ) << 8 ) |
+ ( digest[offset + 3] & 0xff );
+
+ otp = res % DIGITS_POWER[digits];
+ out->bv_len = snprintf( out->bv_val, out->bv_len, "%0*d", digits, otp );
+}
+
+static int
+otp_bind_response( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ /* If the bind succeeded, return our result */
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static long
+otp_hotp( Operation *op, Entry *token )
+{
+ char outbuf[MAX_DIGITS + 1];
+ Entry *params = NULL;
+ Attribute *a;
+ BerValue *secret, client_otp;
+ const void *mech;
+ long last_step = -1, found = -1;
+ int i, otp_len, window;
+
+ a = attr_find( token->e_attrs, ad_oathSecret );
+ secret = &a->a_vals[0];
+
+ a = attr_find( token->e_attrs, ad_oathHOTPCounter );
+ if ( a && lutil_atol( &last_step, a->a_vals[0].bv_val ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "otp_hotp: "
+ "could not parse oathHOTPCounter value %s\n",
+ a->a_vals[0].bv_val );
+ goto done;
+ }
+
+ a = attr_find( token->e_attrs, ad_oathHOTPParams );
+ if ( !a ||
+ be_entry_get_rw( op, &a->a_nvals[0], oc_oathHOTPParams, NULL, 0,
+ &params ) ) {
+ goto done;
+ }
+
+ a = attr_find( params->e_attrs, ad_oathOTPLength );
+ if ( lutil_atoi( &otp_len, a->a_vals[0].bv_val ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "otp_hotp: "
+ "could not parse oathOTPLength value %s\n",
+ a->a_vals[0].bv_val );
+ goto done;
+ }
+ if ( otp_len > MAX_DIGITS || op->orb_cred.bv_len < otp_len ) {
+ /* Client didn't even send the token, fail immediately */
+ goto done;
+ }
+
+ a = attr_find( params->e_attrs, ad_oathHOTPLookahead );
+ if ( lutil_atoi( &window, a->a_vals[0].bv_val ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "otp_hotp: "
+ "could not parse oathHOTPLookAhead value %s\n",
+ a->a_vals[0].bv_val );
+ goto done;
+ }
+ window++;
+
+ a = attr_find( params->e_attrs, ad_oathHMACAlgorithm );
+ if ( !(mech = otp_choose_mech( &a->a_vals[0] )) ) {
+ goto done;
+ }
+ be_entry_release_r( op, params );
+ params = NULL;
+
+ /* We are provided "password" + "OTP", split accordingly */
+ client_otp.bv_len = otp_len;
+ client_otp.bv_val = op->orb_cred.bv_val + op->orb_cred.bv_len - otp_len;
+
+ /* If check succeeds, advance the step counter accordingly */
+ for ( i = 1; i <= window; i++ ) {
+ BerValue out = { .bv_val = outbuf, .bv_len = sizeof(outbuf) };
+
+ generate( secret, last_step + i, otp_len, &out, mech );
+ if ( !ber_bvcmp( &out, &client_otp ) ) {
+ found = last_step + i;
+ /* Would we leak information if we stopped right now? */
+ }
+ }
+
+ if ( found >= 0 ) {
+ /* OTP check passed, trim the password */
+ op->orb_cred.bv_len -= otp_len;
+ Debug( LDAP_DEBUG_STATS, "%s HOTP token %s no. %ld redeemed\n",
+ op->o_log_prefix, token->e_name.bv_val, found );
+ }
+
+done:
+ memset( outbuf, 0, sizeof(outbuf) );
+ if ( params ) {
+ be_entry_release_r( op, params );
+ }
+ return found;
+}
+
+static long
+otp_totp( Operation *op, Entry *token, long *drift )
+{
+ char outbuf[MAX_DIGITS + 1];
+ Entry *params = NULL;
+ Attribute *a;
+ BerValue *secret, client_otp;
+ const void *mech;
+ long t, last_step = -1, found = -1, window = 0, old_drift;
+ int i, otp_len, time_step;
+
+ a = attr_find( token->e_attrs, ad_oathSecret );
+ secret = &a->a_vals[0];
+
+ a = attr_find( token->e_attrs, ad_oathTOTPLastTimeStep );
+ if ( a && lutil_atol( &last_step, a->a_vals[0].bv_val ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "otp_totp: "
+ "could not parse oathTOTPLastTimeStep value %s\n",
+ a->a_vals[0].bv_val );
+ goto done;
+ }
+
+ a = attr_find( token->e_attrs, ad_oathTOTPParams );
+ if ( !a ||
+ be_entry_get_rw( op, &a->a_nvals[0], oc_oathTOTPParams, NULL, 0,
+ &params ) ) {
+ goto done;
+ }
+
+ a = attr_find( params->e_attrs, ad_oathTOTPTimeStepPeriod );
+ if ( lutil_atoi( &time_step, a->a_vals[0].bv_val ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "otp_totp: "
+ "could not parse oathTOTPTimeStepPeriod value %s\n",
+ a->a_vals[0].bv_val );
+ goto done;
+ }
+
+ a = attr_find( params->e_attrs, ad_oathTOTPTimeStepWindow );
+ if ( a && lutil_atol( &window, a->a_vals[0].bv_val ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "otp_totp: "
+ "could not parse oathTOTPTimeStepWindow value %s\n",
+ a->a_vals[0].bv_val );
+ goto done;
+ }
+
+ a = attr_find( params->e_attrs, ad_oathTOTPTimeStepDrift );
+ if ( a && lutil_atol( drift, a->a_vals[0].bv_val ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "otp_totp: "
+ "could not parse oathTOTPTimeStepDrift value %s\n",
+ a->a_vals[0].bv_val );
+ goto done;
+ }
+ old_drift = *drift;
+ t = op->o_time / time_step + *drift;
+
+ a = attr_find( params->e_attrs, ad_oathOTPLength );
+ if ( lutil_atoi( &otp_len, a->a_vals[0].bv_val ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "otp_totp: "
+ "could not parse oathOTPLength value %s\n",
+ a->a_vals[0].bv_val );
+ goto done;
+ }
+ if ( otp_len > MAX_DIGITS || op->orb_cred.bv_len < otp_len ) {
+ /* Client didn't even send the token, fail immediately */
+ goto done;
+ }
+
+ a = attr_find( params->e_attrs, ad_oathHMACAlgorithm );
+ if ( !(mech = otp_choose_mech( &a->a_vals[0] )) ) {
+ goto done;
+ }
+ be_entry_release_r( op, params );
+ params = NULL;
+
+ /* We are provided "password" + "OTP", split accordingly */
+ client_otp.bv_len = otp_len;
+ client_otp.bv_val = op->orb_cred.bv_val + op->orb_cred.bv_len - otp_len;
+
+ /* If check succeeds, advance the step counter accordingly */
+ /* Negation of A001057 series that enumerates all integers:
+ * (0, -1, 1, -2, 2, ...) */
+ for ( i = 0; i >= -window; i = ( i < 0 ) ? -i : ~i ) {
+ BerValue out = { .bv_val = outbuf, .bv_len = sizeof(outbuf) };
+
+ if ( t + i <= last_step ) continue;
+
+ generate( secret, t + i, otp_len, &out, mech );
+ if ( !ber_bvcmp( &out, &client_otp ) ) {
+ found = t + i;
+ *drift = old_drift + i;
+ /* Would we leak information if we stopped right now? */
+ }
+ }
+
+ /* OTP check passed, trim the password */
+ if ( found >= 0 ) {
+ assert( found > last_step );
+
+ op->orb_cred.bv_len -= otp_len;
+ Debug( LDAP_DEBUG_TRACE, "%s TOTP token %s redeemed with new drift of %ld\n",
+ op->o_log_prefix, token->e_name.bv_val, *drift );
+ }
+
+done:
+ memset( outbuf, 0, sizeof(outbuf) );
+ if ( params ) {
+ be_entry_release_r( op, params );
+ }
+ return found;
+}
+
+static int
+otp_op_bind( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ BerValue totpdn = BER_BVNULL, hotpdn = BER_BVNULL, ndn;
+ Entry *user = NULL, *token = NULL;
+ AttributeDescription *ad = NULL, *drift_ad = NULL;
+ Attribute *a;
+ long t = -1, drift = 0;
+ int rc = SLAP_CB_CONTINUE;
+
+ if ( op->oq_bind.rb_method != LDAP_AUTH_SIMPLE ) {
+ return rc;
+ }
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+
+ if ( be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &user ) ) {
+ goto done;
+ }
+
+ if ( !is_entry_objectclass_or_sub( user, oc_oathOTPUser ) ) {
+ be_entry_release_r( op, user );
+ goto done;
+ }
+
+ if ( (a = attr_find( user->e_attrs, ad_oathTOTPToken )) ) {
+ ber_dupbv_x( &totpdn, &a->a_nvals[0], op->o_tmpmemctx );
+ }
+
+ if ( (a = attr_find( user->e_attrs, ad_oathHOTPToken )) ) {
+ ber_dupbv_x( &hotpdn, &a->a_nvals[0], op->o_tmpmemctx );
+ }
+ be_entry_release_r( op, user );
+
+ if ( !BER_BVISNULL( &totpdn ) &&
+ be_entry_get_rw( op, &totpdn, oc_oathTOTPToken, ad_oathSecret, 0,
+ &token ) == LDAP_SUCCESS ) {
+ ndn = totpdn;
+ ad = ad_oathTOTPLastTimeStep;
+ drift_ad = ad_oathTOTPTimeStepDrift;
+ t = otp_totp( op, token, &drift );
+ be_entry_release_r( op, token );
+ token = NULL;
+ }
+ if ( t < 0 && !BER_BVISNULL( &hotpdn ) &&
+ be_entry_get_rw( op, &hotpdn, oc_oathHOTPToken, ad_oathSecret, 0,
+ &token ) == LDAP_SUCCESS ) {
+ ndn = hotpdn;
+ ad = ad_oathHOTPCounter;
+ t = otp_hotp( op, token );
+ be_entry_release_r( op, token );
+ token = NULL;
+ }
+
+ /* If check succeeds, advance the step counter and drift accordingly */
+ if ( t >= 0 ) {
+ char outbuf[32], drift_buf[32];
+ Operation op2;
+ Opheader oh;
+ Modifications mod[2], *m = mod;
+ SlapReply rs2 = { REP_RESULT };
+ slap_callback cb = { .sc_response = &slap_null_cb };
+ BerValue bv[2], bv_drift[2];
+
+ bv[0].bv_val = outbuf;
+ bv[0].bv_len = snprintf( bv[0].bv_val, sizeof(outbuf), "%ld", t );
+ BER_BVZERO( &bv[1] );
+
+ m->sml_numvals = 1;
+ m->sml_values = bv;
+ m->sml_nvalues = NULL;
+ m->sml_desc = ad;
+ m->sml_op = LDAP_MOD_REPLACE;
+ m->sml_flags = SLAP_MOD_INTERNAL;
+
+ if ( drift_ad ) {
+ m->sml_next = &mod[1];
+
+ bv_drift[0].bv_val = drift_buf;
+ bv_drift[0].bv_len = snprintf(
+ bv_drift[0].bv_val, sizeof(drift_buf), "%ld", drift );
+ BER_BVZERO( &bv_drift[1] );
+
+ m++;
+ m->sml_numvals = 1;
+ m->sml_values = bv_drift;
+ m->sml_nvalues = NULL;
+ m->sml_desc = drift_ad;
+ m->sml_op = LDAP_MOD_REPLACE;
+ m->sml_flags = SLAP_MOD_INTERNAL;
+ }
+ m->sml_next = NULL;
+
+ op2 = *op;
+ oh = *op->o_hdr;
+ op2.o_hdr = &oh;
+
+ op2.o_callback = &cb;
+
+ op2.o_tag = LDAP_REQ_MODIFY;
+ op2.orm_modlist = mod;
+ op2.o_dn = op->o_bd->be_rootdn;
+ op2.o_ndn = op->o_bd->be_rootndn;
+ op2.o_req_dn = ndn;
+ op2.o_req_ndn = ndn;
+ op2.o_opid = -1;
+
+ op2.o_bd->be_modify( &op2, &rs2 );
+ if ( rs2.sr_err != LDAP_SUCCESS ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ } else {
+ /* Client failed the bind, but we still have to pass it over to the
+ * backend and fail the Bind later */
+ slap_callback *cb;
+ cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
+ cb->sc_response = otp_bind_response;
+ cb->sc_next = op->o_callback;
+ op->o_callback = cb;
+ }
+
+done:
+ if ( !BER_BVISNULL( &hotpdn ) ) {
+ ber_memfree_x( hotpdn.bv_val, op->o_tmpmemctx );
+ }
+ if ( !BER_BVISNULL( &totpdn ) ) {
+ ber_memfree_x( totpdn.bv_val, op->o_tmpmemctx );
+ }
+ op->o_bd->bd_info = (BackendInfo *)on;
+ return rc;
+}
+
+static slap_overinst otp;
+
+int
+otp_initialize( void )
+{
+ ConfigArgs ca;
+ char *argv[4];
+ int i;
+
+ otp.on_bi.bi_type = "otp";
+ otp.on_bi.bi_op_bind = otp_op_bind;
+
+ ca.argv = argv;
+ argv[0] = "otp";
+ ca.argv = argv;
+ ca.argc = 3;
+ ca.fname = argv[0];
+
+ argv[3] = NULL;
+ for ( i = 0; otp_oid[i].name; i++ ) {
+ argv[1] = otp_oid[i].name;
+ argv[2] = otp_oid[i].oid;
+ parse_oidm( &ca, 0, NULL );
+ }
+
+ /* schema integration */
+ for ( i = 0; otp_at[i].schema; i++ ) {
+ if ( register_at( otp_at[i].schema, otp_at[i].adp, 0 ) ) {
+ Debug( LDAP_DEBUG_ANY, "otp_initialize: "
+ "register_at failed\n" );
+ return -1;
+ }
+ }
+
+ for ( i = 0; otp_oc[i].schema; i++ ) {
+ if ( register_oc( otp_oc[i].schema, otp_oc[i].ocp, 0 ) ) {
+ Debug( LDAP_DEBUG_ANY, "otp_initialize: "
+ "register_oc failed\n" );
+ return -1;
+ }
+ }
+
+ return overlay_register( &otp );
+}
+
+#if SLAPD_OVER_OTP == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return otp_initialize();
+}
+#endif /* SLAPD_OVER_OTP == SLAPD_MOD_DYNAMIC */
+
+#endif /* defined(SLAPD_OVER_OTP) */
diff --git a/servers/slapd/overlays/overlays.c b/servers/slapd/overlays/overlays.c
new file mode 100644
index 0000000..8290200
--- /dev/null
+++ b/servers/slapd/overlays/overlays.c
@@ -0,0 +1,44 @@
+/* overlays.c - Static overlay framework */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Copyright 2003 by Howard Chu.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include "slap.h"
+
+extern OverlayInit slap_oinfo[];
+
+int
+overlay_init(void)
+{
+ int i, rc = 0;
+
+ for ( i= 0 ; slap_oinfo[i].ov_type; i++ ) {
+ rc = slap_oinfo[i].ov_init();
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s overlay setup failed, err %d\n",
+ slap_oinfo[i].ov_type, rc );
+ break;
+ }
+ }
+
+ return rc;
+}
diff --git a/servers/slapd/overlays/pcache.c b/servers/slapd/overlays/pcache.c
new file mode 100644
index 0000000..423c196
--- /dev/null
+++ b/servers/slapd/overlays/pcache.c
@@ -0,0 +1,5814 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2003 IBM Corporation.
+ * Portions Copyright 2003-2009 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Apurva Kumar for inclusion
+ * in OpenLDAP Software and subsequently rewritten by Howard Chu.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_PROXYCACHE
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "slap.h"
+#include "lutil.h"
+#include "ldap_rq.h"
+#include "ldap_avl.h"
+
+#include "../back-monitor/back-monitor.h"
+
+#include "slap-config.h"
+
+/*
+ * Control that allows to access the private DB
+ * instead of the public one
+ */
+#define PCACHE_CONTROL_PRIVDB "1.3.6.1.4.1.4203.666.11.9.5.1"
+
+/*
+ * Extended Operation that allows to remove a query from the cache
+ */
+#define PCACHE_EXOP_QUERY_DELETE "1.3.6.1.4.1.4203.666.11.9.6.1"
+
+/*
+ * Monitoring
+ */
+#define PCACHE_MONITOR
+
+/* query cache structs */
+/* query */
+
+typedef struct Query_s {
+ Filter* filter; /* Search Filter */
+ struct berval base; /* Search Base */
+ int scope; /* Search scope */
+} Query;
+
+struct query_template_s;
+
+typedef struct Qbase_s {
+ TAvlnode *scopes[4]; /* threaded AVL trees of cached queries */
+ struct berval base;
+ int queries;
+} Qbase;
+
+/* struct representing a cached query */
+typedef struct cached_query_s {
+ Filter *filter;
+ Filter *first;
+ Qbase *qbase;
+ int scope;
+ struct berval q_uuid; /* query identifier */
+ int q_sizelimit;
+ struct query_template_s *qtemp; /* template of the query */
+ time_t expiry_time; /* time till the query is considered invalid */
+ time_t refresh_time; /* time till the query is refreshed */
+ time_t bindref_time; /* time till the bind is refreshed */
+ int bind_refcnt; /* number of bind operation referencing this query */
+ unsigned long answerable_cnt; /* how many times it was answerable */
+ int refcnt; /* references since last refresh */
+ int in_lru; /* query is in LRU list */
+ ldap_pvt_thread_mutex_t answerable_cnt_mutex;
+ struct cached_query_s *next; /* next query in the template */
+ struct cached_query_s *prev; /* previous query in the template */
+ struct cached_query_s *lru_up; /* previous query in the LRU list */
+ struct cached_query_s *lru_down; /* next query in the LRU list */
+ ldap_pvt_thread_rdwr_t rwlock;
+} CachedQuery;
+
+/*
+ * URL representation:
+ *
+ * ldap:///<base>??<scope>?<filter>?x-uuid=<uid>,x-template=<template>,x-attrset=<attrset>,x-expiry=<expiry>,x-refresh=<refresh>
+ *
+ * <base> ::= CachedQuery.qbase->base
+ * <scope> ::= CachedQuery.scope
+ * <filter> ::= filter2bv(CachedQuery.filter)
+ * <uuid> ::= CachedQuery.q_uuid
+ * <attrset> ::= CachedQuery.qtemp->attr_set_index
+ * <expiry> ::= CachedQuery.expiry_time
+ * <refresh> ::= CachedQuery.refresh_time
+ *
+ * quick hack: parse URI, call add_query() and then fix
+ * CachedQuery.expiry_time and CachedQuery.q_uuid
+ *
+ * NOTE: if the <attrset> changes, all stored URLs will be invalidated.
+ */
+
+/*
+ * Represents a set of projected attributes.
+ */
+
+struct attr_set {
+ struct query_template_s *templates;
+ AttributeName* attrs; /* specifies the set */
+ unsigned flags;
+#define PC_CONFIGURED (0x1)
+#define PC_REFERENCED (0x2)
+#define PC_GOT_OC (0x4)
+ int count; /* number of attributes */
+};
+
+/* struct representing a query template
+ * e.g. template string = &(cn=)(mail=)
+ */
+typedef struct query_template_s {
+ struct query_template_s *qtnext;
+ struct query_template_s *qmnext;
+
+ Avlnode* qbase;
+ CachedQuery* query; /* most recent query cached for the template */
+ CachedQuery* query_last; /* oldest query cached for the template */
+ ldap_pvt_thread_rdwr_t t_rwlock; /* Rd/wr lock for accessing queries in the template */
+ struct berval querystr; /* Filter string corresponding to the QT */
+ struct berval bindbase; /* base DN for Bind request */
+ struct berval bindfilterstr; /* Filter string for Bind request */
+ struct berval bindftemp; /* bind filter template */
+ Filter *bindfilter;
+ AttributeDescription **bindfattrs; /* attrs to substitute in ftemp */
+
+ int bindnattrs; /* number of bindfattrs */
+ int bindscope;
+ int attr_set_index; /* determines the projected attributes */
+ int no_of_queries; /* Total number of queries in the template */
+ time_t ttl; /* TTL for the queries of this template */
+ time_t negttl; /* TTL for negative results */
+ time_t limitttl; /* TTL for sizelimit exceeding results */
+ time_t ttr; /* time to refresh */
+ time_t bindttr; /* TTR for cached binds */
+ struct attr_set t_attrs; /* filter attrs + attr_set */
+} QueryTemplate;
+
+typedef enum {
+ PC_IGNORE = 0,
+ PC_POSITIVE,
+ PC_NEGATIVE,
+ PC_SIZELIMIT
+} pc_caching_reason_t;
+
+static const char *pc_caching_reason_str[] = {
+ "IGNORE",
+ "POSITIVE",
+ "NEGATIVE",
+ "SIZELIMIT",
+
+ NULL
+};
+
+struct query_manager_s;
+
+/* prototypes for functions for 1) query containment
+ * 2) query addition, 3) cache replacement
+ */
+typedef CachedQuery *(QCfunc)(Operation *op, struct query_manager_s*,
+ Query*, QueryTemplate*);
+typedef CachedQuery *(AddQueryfunc)(Operation *op, struct query_manager_s*,
+ Query*, QueryTemplate*, pc_caching_reason_t, int wlock);
+typedef void (CRfunc)(struct query_manager_s*, struct berval*);
+
+/* LDAP query cache */
+typedef struct query_manager_s {
+ struct attr_set* attr_sets; /* possible sets of projected attributes */
+ QueryTemplate* templates; /* cacheable templates */
+
+ CachedQuery* lru_top; /* top and bottom of LRU list */
+ CachedQuery* lru_bottom;
+
+ ldap_pvt_thread_mutex_t lru_mutex; /* mutex for accessing LRU list */
+
+ /* Query cache methods */
+ QCfunc *qcfunc; /* Query containment*/
+ CRfunc *crfunc; /* cache replacement */
+ AddQueryfunc *addfunc; /* add query */
+} query_manager;
+
+/* LDAP query cache manager */
+typedef struct cache_manager_s {
+ BackendDB db; /* underlying database */
+ unsigned long num_cached_queries; /* total number of cached queries */
+ unsigned long max_queries; /* upper bound on # of cached queries */
+ int save_queries; /* save cached queries across restarts */
+ int check_cacheability; /* check whether a query is cacheable */
+ int numattrsets; /* number of attribute sets */
+ int cur_entries; /* current number of entries cached */
+ int max_entries; /* max number of entries cached */
+ int num_entries_limit; /* max # of entries in a cacheable query */
+
+ char response_cb; /* install the response callback
+ * at the tail of the callback list */
+#define PCACHE_RESPONSE_CB_HEAD 0
+#define PCACHE_RESPONSE_CB_TAIL 1
+ char defer_db_open; /* defer open for online add */
+ char cache_binds; /* cache binds or just passthru */
+
+ time_t cc_period; /* interval between successive consistency checks (sec) */
+#define PCACHE_CC_PAUSED 1
+#define PCACHE_CC_OFFLINE 2
+ int cc_paused;
+ void *cc_arg;
+
+ ldap_pvt_thread_mutex_t cache_mutex;
+
+ query_manager* qm; /* query cache managed by the cache manager */
+
+#ifdef PCACHE_MONITOR
+ void *monitor_cb;
+ struct berval monitor_ndn;
+#endif /* PCACHE_MONITOR */
+} cache_manager;
+
+#ifdef PCACHE_MONITOR
+static int pcache_monitor_db_init( BackendDB *be );
+static int pcache_monitor_db_open( BackendDB *be );
+static int pcache_monitor_db_close( BackendDB *be );
+static int pcache_monitor_db_destroy( BackendDB *be );
+#endif /* PCACHE_MONITOR */
+
+static int pcache_debug;
+
+#ifdef PCACHE_CONTROL_PRIVDB
+static int privDB_cid;
+#endif /* PCACHE_CONTROL_PRIVDB */
+
+static AttributeDescription *ad_queryId, *ad_cachedQueryURL;
+
+#ifdef PCACHE_MONITOR
+static AttributeDescription *ad_numQueries, *ad_numEntries;
+static ObjectClass *oc_olmPCache;
+#endif /* PCACHE_MONITOR */
+
+static struct {
+ char *name;
+ char *oid;
+} s_oid[] = {
+ { "PCacheOID", "1.3.6.1.4.1.4203.666.11.9.1" },
+ { "PCacheAttributes", "PCacheOID:1" },
+ { "PCacheObjectClasses", "PCacheOID:2" },
+
+ { NULL }
+};
+
+static struct {
+ char *desc;
+ AttributeDescription **adp;
+} s_ad[] = {
+ { "( PCacheAttributes:1 "
+ "NAME 'pcacheQueryID' "
+ "DESC 'ID of query the entry belongs to, formatted as a UUID' "
+ "EQUALITY octetStringMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{64} "
+ "NO-USER-MODIFICATION "
+ "USAGE directoryOperation )",
+ &ad_queryId },
+ { "( PCacheAttributes:2 "
+ "NAME 'pcacheQueryURL' "
+ "DESC 'URI describing a cached query' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
+ "NO-USER-MODIFICATION "
+ "USAGE directoryOperation )",
+ &ad_cachedQueryURL },
+#ifdef PCACHE_MONITOR
+ { "( PCacheAttributes:3 "
+ "NAME 'pcacheNumQueries' "
+ "DESC 'Number of cached queries' "
+ "EQUALITY integerMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "NO-USER-MODIFICATION "
+ "USAGE directoryOperation )",
+ &ad_numQueries },
+ { "( PCacheAttributes:4 "
+ "NAME 'pcacheNumEntries' "
+ "DESC 'Number of cached entries' "
+ "EQUALITY integerMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "NO-USER-MODIFICATION "
+ "USAGE directoryOperation )",
+ &ad_numEntries },
+#endif /* PCACHE_MONITOR */
+
+ { NULL }
+};
+
+static struct {
+ char *desc;
+ ObjectClass **ocp;
+} s_oc[] = {
+#ifdef PCACHE_MONITOR
+ /* augments an existing object, so it must be AUXILIARY */
+ { "( PCacheObjectClasses:1 "
+ "NAME ( 'olmPCache' ) "
+ "SUP top AUXILIARY "
+ "MAY ( "
+ "pcacheQueryURL "
+ "$ pcacheNumQueries "
+ "$ pcacheNumEntries "
+ " ) )",
+ &oc_olmPCache },
+#endif /* PCACHE_MONITOR */
+
+ { NULL }
+};
+
+static int
+filter2template(
+ Operation *op,
+ Filter *f,
+ struct berval *fstr );
+
+static CachedQuery *
+add_query(
+ Operation *op,
+ query_manager* qm,
+ Query* query,
+ QueryTemplate *templ,
+ pc_caching_reason_t why,
+ int wlock);
+
+static int
+remove_query_data(
+ Operation *op,
+ struct berval *query_uuid );
+
+/*
+ * Turn a cached query into its URL representation
+ */
+static int
+query2url( Operation *op, CachedQuery *q, struct berval *urlbv, int dolock )
+{
+ struct berval bv_scope,
+ bv_filter;
+ char attrset_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ],
+ expiry_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ],
+ refresh_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ],
+ answerable_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ],
+ *ptr;
+ ber_len_t attrset_len,
+ expiry_len,
+ refresh_len,
+ answerable_len;
+
+ if ( dolock ) {
+ ldap_pvt_thread_rdwr_rlock( &q->rwlock );
+ }
+
+ ldap_pvt_scope2bv( q->scope, &bv_scope );
+ filter2bv_x( op, q->filter, &bv_filter );
+ attrset_len = sprintf( attrset_buf,
+ "%lu", (unsigned long)q->qtemp->attr_set_index );
+ expiry_len = sprintf( expiry_buf,
+ "%lu", (unsigned long)q->expiry_time );
+ answerable_len = snprintf( answerable_buf, sizeof( answerable_buf ),
+ "%lu", q->answerable_cnt );
+ if ( q->refresh_time )
+ refresh_len = sprintf( refresh_buf,
+ "%lu", (unsigned long)q->refresh_time );
+ else
+ refresh_len = 0;
+
+ urlbv->bv_len = STRLENOF( "ldap:///" )
+ + q->qbase->base.bv_len
+ + STRLENOF( "??" )
+ + bv_scope.bv_len
+ + STRLENOF( "?" )
+ + bv_filter.bv_len
+ + STRLENOF( "?x-uuid=" )
+ + q->q_uuid.bv_len
+ + STRLENOF( ",x-attrset=" )
+ + attrset_len
+ + STRLENOF( ",x-expiry=" )
+ + expiry_len
+ + STRLENOF( ",x-answerable=" )
+ + answerable_len;
+ if ( refresh_len )
+ urlbv->bv_len += STRLENOF( ",x-refresh=" )
+ + refresh_len;
+
+ ptr = urlbv->bv_val = ber_memalloc_x( urlbv->bv_len + 1, op->o_tmpmemctx );
+ ptr = lutil_strcopy( ptr, "ldap:///" );
+ ptr = lutil_strcopy( ptr, q->qbase->base.bv_val );
+ ptr = lutil_strcopy( ptr, "??" );
+ ptr = lutil_strcopy( ptr, bv_scope.bv_val );
+ ptr = lutil_strcopy( ptr, "?" );
+ ptr = lutil_strcopy( ptr, bv_filter.bv_val );
+ ptr = lutil_strcopy( ptr, "?x-uuid=" );
+ ptr = lutil_strcopy( ptr, q->q_uuid.bv_val );
+ ptr = lutil_strcopy( ptr, ",x-attrset=" );
+ ptr = lutil_strcopy( ptr, attrset_buf );
+ ptr = lutil_strcopy( ptr, ",x-expiry=" );
+ ptr = lutil_strcopy( ptr, expiry_buf );
+ ptr = lutil_strcopy( ptr, ",x-answerable=" );
+ ptr = lutil_strcopy( ptr, answerable_buf );
+ if ( refresh_len ) {
+ ptr = lutil_strcopy( ptr, ",x-refresh=" );
+ ptr = lutil_strcopy( ptr, refresh_buf );
+ }
+
+ ber_memfree_x( bv_filter.bv_val, op->o_tmpmemctx );
+
+ if ( dolock ) {
+ ldap_pvt_thread_rdwr_runlock( &q->rwlock );
+ }
+
+ return 0;
+}
+
+/* Find and record the empty filter clauses */
+
+static int
+ftemp_attrs( struct berval *ftemp, struct berval *template,
+ AttributeDescription ***ret, const char **text )
+{
+ int i;
+ int attr_cnt=0;
+ struct berval bv;
+ char *p1, *p2, *t1;
+ AttributeDescription *ad;
+ AttributeDescription **descs = NULL;
+ char *temp2;
+
+ temp2 = ch_malloc( ftemp->bv_len + 1 );
+ p1 = ftemp->bv_val;
+ t1 = temp2;
+
+ *ret = NULL;
+
+ for (;;) {
+ while ( *p1 == '(' || *p1 == '&' || *p1 == '|' || *p1 == ')' )
+ *t1++ = *p1++;
+
+ p2 = strchr( p1, '=' );
+ if ( !p2 ) {
+ if ( !descs ) {
+ ch_free( temp2 );
+ return -1;
+ }
+ break;
+ }
+ i = p2 - p1;
+ AC_MEMCPY( t1, p1, i );
+ t1 += i;
+ *t1++ = '=';
+
+ if ( p2[-1] == '<' || p2[-1] == '>' ) p2--;
+ bv.bv_val = p1;
+ bv.bv_len = p2 - p1;
+ ad = NULL;
+ i = slap_bv2ad( &bv, &ad, text );
+ if ( i ) {
+ ch_free( temp2 );
+ ch_free( descs );
+ return -1;
+ }
+ if ( *p2 == '<' || *p2 == '>' ) p2++;
+ if ( p2[1] != ')' ) {
+ p2++;
+ while ( *p2 != ')' ) p2++;
+ p1 = p2;
+ continue;
+ }
+
+ descs = (AttributeDescription **)ch_realloc(descs,
+ (attr_cnt + 2)*sizeof(AttributeDescription *));
+
+ descs[attr_cnt++] = ad;
+
+ p1 = p2+1;
+ }
+ *t1 = '\0';
+ descs[attr_cnt] = NULL;
+ *ret = descs;
+ template->bv_val = temp2;
+ template->bv_len = t1 - temp2;
+ return attr_cnt;
+}
+
+static int
+template_attrs( char *template, struct attr_set *set, AttributeName **ret,
+ const char **text )
+{
+ int got_oc = 0;
+ int alluser = 0;
+ int allop = 0;
+ int i;
+ int attr_cnt;
+ int t_cnt = 0;
+ struct berval bv;
+ char *p1, *p2;
+ AttributeDescription *ad;
+ AttributeName *attrs;
+
+ p1 = template;
+
+ *ret = NULL;
+
+ attrs = ch_calloc( set->count + 1, sizeof(AttributeName) );
+ for ( i=0; i < set->count; i++ )
+ attrs[i] = set->attrs[i];
+ attr_cnt = i;
+ alluser = an_find( attrs, slap_bv_all_user_attrs );
+ allop = an_find( attrs, slap_bv_all_operational_attrs );
+
+ for (;;) {
+ while ( *p1 == '(' || *p1 == '&' || *p1 == '|' || *p1 == ')' ) p1++;
+ p2 = strchr( p1, '=' );
+ if ( !p2 )
+ break;
+ if ( p2[-1] == '<' || p2[-1] == '>' ) p2--;
+ bv.bv_val = p1;
+ bv.bv_len = p2 - p1;
+ ad = NULL;
+ i = slap_bv2ad( &bv, &ad, text );
+ if ( i ) {
+ ch_free( attrs );
+ return -1;
+ }
+ t_cnt++;
+
+ if ( ad == slap_schema.si_ad_objectClass )
+ got_oc = 1;
+
+ if ( is_at_operational(ad->ad_type)) {
+ if ( allop ) {
+ goto bottom;
+ }
+ } else if ( alluser ) {
+ goto bottom;
+ }
+ if ( !ad_inlist( ad, attrs )) {
+ attrs = (AttributeName *)ch_realloc(attrs,
+ (attr_cnt + 2)*sizeof(AttributeName));
+
+ attrs[attr_cnt].an_desc = ad;
+ attrs[attr_cnt].an_name = ad->ad_cname;
+ attrs[attr_cnt].an_oc = NULL;
+ attrs[attr_cnt].an_flags = 0;
+ BER_BVZERO( &attrs[attr_cnt+1].an_name );
+ attr_cnt++;
+ }
+
+bottom:
+ p1 = p2+2;
+ }
+ if ( !t_cnt ) {
+ *text = "couldn't parse template";
+ ch_free(attrs);
+ return -1;
+ }
+ if ( !got_oc && !( set->flags & PC_GOT_OC )) {
+ attrs = (AttributeName *)ch_realloc(attrs,
+ (attr_cnt + 2)*sizeof(AttributeName));
+
+ ad = slap_schema.si_ad_objectClass;
+ attrs[attr_cnt].an_desc = ad;
+ attrs[attr_cnt].an_name = ad->ad_cname;
+ attrs[attr_cnt].an_oc = NULL;
+ attrs[attr_cnt].an_flags = 0;
+ BER_BVZERO( &attrs[attr_cnt+1].an_name );
+ attr_cnt++;
+ }
+ *ret = attrs;
+ return attr_cnt;
+}
+
+/*
+ * Turn an URL representing a formerly cached query into a cached query,
+ * and try to cache it
+ */
+static int
+url2query(
+ char *url,
+ Operation *op,
+ query_manager *qm )
+{
+ Query query = { 0 };
+ QueryTemplate *qt;
+ CachedQuery *cq;
+ LDAPURLDesc *lud = NULL;
+ struct berval base,
+ tempstr = BER_BVNULL,
+ uuid = BER_BVNULL;
+ int attrset;
+ time_t expiry_time;
+ time_t refresh_time;
+ unsigned long answerable_cnt;
+ int i,
+ got = 0,
+#define GOT_UUID 0x1U
+#define GOT_ATTRSET 0x2U
+#define GOT_EXPIRY 0x4U
+#define GOT_ANSWERABLE 0x8U
+#define GOT_REFRESH 0x10U
+#define GOT_ALL (GOT_UUID|GOT_ATTRSET|GOT_EXPIRY|GOT_ANSWERABLE)
+ rc = 0;
+
+ rc = ldap_url_parse( url, &lud );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ return -1;
+ }
+
+ /* non-allowed fields */
+ if ( lud->lud_host != NULL ) {
+ rc = 1;
+ goto error;
+ }
+
+ if ( lud->lud_attrs != NULL ) {
+ rc = 1;
+ goto error;
+ }
+
+ /* be pedantic */
+ if ( strcmp( lud->lud_scheme, "ldap" ) != 0 ) {
+ rc = 1;
+ goto error;
+ }
+
+ /* required fields */
+ if ( lud->lud_dn == NULL || lud->lud_dn[ 0 ] == '\0' ) {
+ rc = 1;
+ goto error;
+ }
+
+ switch ( lud->lud_scope ) {
+ case LDAP_SCOPE_BASE:
+ case LDAP_SCOPE_ONELEVEL:
+ case LDAP_SCOPE_SUBTREE:
+ case LDAP_SCOPE_SUBORDINATE:
+ break;
+
+ default:
+ rc = 1;
+ goto error;
+ }
+
+ if ( lud->lud_filter == NULL || lud->lud_filter[ 0 ] == '\0' ) {
+ rc = 1;
+ goto error;
+ }
+
+ if ( lud->lud_exts == NULL ) {
+ rc = 1;
+ goto error;
+ }
+
+ for ( i = 0; lud->lud_exts[ i ] != NULL; i++ ) {
+ if ( strncmp( lud->lud_exts[ i ], "x-uuid=", STRLENOF( "x-uuid=" ) ) == 0 ) {
+ struct berval tmpUUID;
+ Syntax *syn_UUID = slap_schema.si_ad_entryUUID->ad_type->sat_syntax;
+
+ if ( got & GOT_UUID ) {
+ rc = 1;
+ goto error;
+ }
+
+ ber_str2bv( &lud->lud_exts[ i ][ STRLENOF( "x-uuid=" ) ], 0, 0, &tmpUUID );
+ if ( !BER_BVISEMPTY( &tmpUUID ) ) {
+ rc = syn_UUID->ssyn_pretty( syn_UUID, &tmpUUID, &uuid, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ goto error;
+ }
+ }
+ got |= GOT_UUID;
+
+ } else if ( strncmp( lud->lud_exts[ i ], "x-attrset=", STRLENOF( "x-attrset=" ) ) == 0 ) {
+ if ( got & GOT_ATTRSET ) {
+ rc = 1;
+ goto error;
+ }
+
+ rc = lutil_atoi( &attrset, &lud->lud_exts[ i ][ STRLENOF( "x-attrset=" ) ] );
+ if ( rc ) {
+ goto error;
+ }
+ got |= GOT_ATTRSET;
+
+ } else if ( strncmp( lud->lud_exts[ i ], "x-expiry=", STRLENOF( "x-expiry=" ) ) == 0 ) {
+ unsigned long l;
+
+ if ( got & GOT_EXPIRY ) {
+ rc = 1;
+ goto error;
+ }
+
+ rc = lutil_atoul( &l, &lud->lud_exts[ i ][ STRLENOF( "x-expiry=" ) ] );
+ if ( rc ) {
+ goto error;
+ }
+ expiry_time = (time_t)l;
+ got |= GOT_EXPIRY;
+
+ } else if ( strncmp( lud->lud_exts[ i ], "x-answerable=", STRLENOF( "x-answerable=" ) ) == 0 ) {
+ if ( got & GOT_ANSWERABLE ) {
+ rc = 1;
+ goto error;
+ }
+
+ rc = lutil_atoul( &answerable_cnt, &lud->lud_exts[ i ][ STRLENOF( "x-answerable=" ) ] );
+ if ( rc ) {
+ goto error;
+ }
+ got |= GOT_ANSWERABLE;
+
+ } else if ( strncmp( lud->lud_exts[ i ], "x-refresh=", STRLENOF( "x-refresh=" ) ) == 0 ) {
+ unsigned long l;
+
+ if ( got & GOT_REFRESH ) {
+ rc = 1;
+ goto error;
+ }
+
+ rc = lutil_atoul( &l, &lud->lud_exts[ i ][ STRLENOF( "x-refresh=" ) ] );
+ if ( rc ) {
+ goto error;
+ }
+ refresh_time = (time_t)l;
+ got |= GOT_REFRESH;
+
+ } else {
+ rc = -1;
+ goto error;
+ }
+ }
+
+ if ( got != GOT_ALL ) {
+ rc = 1;
+ goto error;
+ }
+
+ if ( !(got & GOT_REFRESH ))
+ refresh_time = 0;
+
+ /* ignore expired queries */
+ if ( expiry_time <= slap_get_time()) {
+ Operation op2 = *op;
+
+ memset( &op2.oq_search, 0, sizeof( op2.oq_search ) );
+
+ (void)remove_query_data( &op2, &uuid );
+
+ rc = 0;
+
+ } else {
+ ber_str2bv( lud->lud_dn, 0, 0, &base );
+ rc = dnNormalize( 0, NULL, NULL, &base, &query.base, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ goto error;
+ }
+ query.scope = lud->lud_scope;
+ query.filter = str2filter( lud->lud_filter );
+ if ( query.filter == NULL ) {
+ rc = -1;
+ goto error;
+ }
+
+ tempstr.bv_val = ch_malloc( strlen( lud->lud_filter ) + 1 );
+ tempstr.bv_len = 0;
+ if ( filter2template( op, query.filter, &tempstr ) ) {
+ ch_free( tempstr.bv_val );
+ rc = -1;
+ goto error;
+ }
+
+ /* check for query containment */
+ qt = qm->attr_sets[attrset].templates;
+ for ( ; qt; qt = qt->qtnext ) {
+ /* find if template i can potentially answer tempstr */
+ if ( bvmatch( &qt->querystr, &tempstr ) ) {
+ break;
+ }
+ }
+
+ if ( qt == NULL ) {
+ rc = 1;
+ goto error;
+ }
+
+ cq = add_query( op, qm, &query, qt, PC_POSITIVE, 0 );
+ if ( cq != NULL ) {
+ cq->expiry_time = expiry_time;
+ cq->refresh_time = refresh_time;
+ cq->q_uuid = uuid;
+ cq->answerable_cnt = answerable_cnt;
+ cq->refcnt = 0;
+
+ /* it's now into cq->filter */
+ BER_BVZERO( &uuid );
+ query.filter = NULL;
+
+ } else {
+ rc = 1;
+ }
+ }
+
+error:;
+ if ( query.filter != NULL ) filter_free( query.filter );
+ if ( !BER_BVISNULL( &tempstr ) ) ch_free( tempstr.bv_val );
+ if ( !BER_BVISNULL( &query.base ) ) ch_free( query.base.bv_val );
+ if ( !BER_BVISNULL( &uuid ) ) ch_free( uuid.bv_val );
+ if ( lud != NULL ) ldap_free_urldesc( lud );
+
+ return rc;
+}
+
+/* Return 1 for an added entry, else 0 */
+static int
+merge_entry(
+ Operation *op,
+ Entry *e,
+ int dup,
+ struct berval* query_uuid )
+{
+ int rc;
+ Modifications* modlist = NULL;
+ const char* text = NULL;
+ Attribute *attr;
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof(textbuf);
+
+ SlapReply sreply = {REP_RESULT};
+
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+
+ if ( dup )
+ e = entry_dup( e );
+ attr = e->e_attrs;
+ e->e_attrs = NULL;
+
+ /* add queryId attribute */
+ attr_merge_one( e, ad_queryId, query_uuid, NULL );
+
+ /* append the attribute list from the fetched entry */
+ e->e_attrs->a_next = attr;
+
+ op->o_tag = LDAP_REQ_ADD;
+ op->o_protocol = LDAP_VERSION3;
+ op->o_callback = &cb;
+ op->o_time = slap_get_time();
+ op->o_do_not_cache = 1;
+
+ op->ora_e = e;
+ op->o_req_dn = e->e_name;
+ op->o_req_ndn = e->e_nname;
+ rc = op->o_bd->be_add( op, &sreply );
+
+ if ( rc != LDAP_SUCCESS ) {
+ if ( rc == LDAP_ALREADY_EXISTS ) {
+ rs_reinit( &sreply, REP_RESULT );
+ slap_entry2mods( e, &modlist, &text, textbuf, textlen );
+ modlist->sml_op = LDAP_MOD_ADD;
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->orm_modlist = modlist;
+ op->o_managedsait = SLAP_CONTROL_CRITICAL;
+ op->o_bd->be_modify( op, &sreply );
+ slap_mods_free( modlist, 1 );
+ } else if ( rc == LDAP_REFERRAL ||
+ rc == LDAP_NO_SUCH_OBJECT ) {
+ syncrepl_add_glue( op, e );
+ e = NULL;
+ rc = 1;
+ }
+ if ( e ) {
+ entry_free( e );
+ rc = 0;
+ }
+ } else {
+ if ( op->ora_e == e )
+ entry_free( e );
+ rc = 1;
+ }
+
+ return rc;
+}
+
+/* Length-ordered sort on normalized DNs */
+static int pcache_dn_cmp( const void *v1, const void *v2 )
+{
+ const Qbase *q1 = v1, *q2 = v2;
+
+ int rc = q1->base.bv_len - q2->base.bv_len;
+ if ( rc == 0 )
+ rc = strncmp( q1->base.bv_val, q2->base.bv_val, q1->base.bv_len );
+ return rc;
+}
+
+static int lex_bvcmp( struct berval *bv1, struct berval *bv2 )
+{
+ int len, dif;
+ dif = bv1->bv_len - bv2->bv_len;
+ len = bv1->bv_len;
+ if ( dif > 0 ) len -= dif;
+ len = memcmp( bv1->bv_val, bv2->bv_val, len );
+ if ( !len )
+ len = dif;
+ return len;
+}
+
+/* compare the current value in each filter */
+static int pcache_filter_cmp( Filter *f1, Filter *f2 )
+{
+ int rc, weight1, weight2;
+
+ switch( f1->f_choice ) {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ weight1 = 0;
+ break;
+ case LDAP_FILTER_PRESENT:
+ weight1 = 1;
+ break;
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ weight1 = 2;
+ break;
+ default:
+ weight1 = 3;
+ }
+ switch( f2->f_choice ) {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ weight2 = 0;
+ break;
+ case LDAP_FILTER_PRESENT:
+ weight2 = 1;
+ break;
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ weight2 = 2;
+ break;
+ default:
+ weight2 = 3;
+ }
+ rc = weight1 - weight2;
+ if ( !rc ) {
+ switch( weight1 ) {
+ case 0:
+ rc = pcache_filter_cmp( f1->f_and, f2->f_and );
+ break;
+ case 1:
+ break;
+ case 2:
+ rc = lex_bvcmp( &f1->f_av_value, &f2->f_av_value );
+ break;
+ case 3:
+ if ( f1->f_choice == LDAP_FILTER_SUBSTRINGS ) {
+ rc = 0;
+ if ( !BER_BVISNULL( &f1->f_sub_initial )) {
+ if ( !BER_BVISNULL( &f2->f_sub_initial )) {
+ rc = lex_bvcmp( &f1->f_sub_initial,
+ &f2->f_sub_initial );
+ } else {
+ rc = 1;
+ }
+ } else if ( !BER_BVISNULL( &f2->f_sub_initial )) {
+ rc = -1;
+ }
+ if ( rc ) break;
+ if ( f1->f_sub_any ) {
+ if ( f2->f_sub_any ) {
+ rc = lex_bvcmp( f1->f_sub_any,
+ f2->f_sub_any );
+ } else {
+ rc = 1;
+ }
+ } else if ( f2->f_sub_any ) {
+ rc = -1;
+ }
+ if ( rc ) break;
+ if ( !BER_BVISNULL( &f1->f_sub_final )) {
+ if ( !BER_BVISNULL( &f2->f_sub_final )) {
+ rc = lex_bvcmp( &f1->f_sub_final,
+ &f2->f_sub_final );
+ } else {
+ rc = 1;
+ }
+ } else if ( !BER_BVISNULL( &f2->f_sub_final )) {
+ rc = -1;
+ }
+ } else {
+ rc = lex_bvcmp( &f1->f_mr_value,
+ &f2->f_mr_value );
+ }
+ break;
+ }
+ while ( !rc ) {
+ f1 = f1->f_next;
+ f2 = f2->f_next;
+ if ( f1 || f2 ) {
+ if ( !f1 )
+ rc = -1;
+ else if ( !f2 )
+ rc = 1;
+ else {
+ rc = pcache_filter_cmp( f1, f2 );
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+/* compare filters in each query */
+static int pcache_query_cmp( const void *v1, const void *v2 )
+{
+ const CachedQuery *q1 = v1, *q2 =v2;
+ return pcache_filter_cmp( q1->filter, q2->filter );
+}
+
+/* add query on top of LRU list */
+static void
+add_query_on_top (query_manager* qm, CachedQuery* qc)
+{
+ CachedQuery* top = qm->lru_top;
+
+ qc->in_lru = 1;
+ qm->lru_top = qc;
+
+ if (top)
+ top->lru_up = qc;
+ else
+ qm->lru_bottom = qc;
+
+ qc->lru_down = top;
+ qc->lru_up = NULL;
+ Debug( pcache_debug, "Base of added query = %s\n",
+ qc->qbase->base.bv_val );
+}
+
+/* remove_query from LRU list */
+
+static void
+remove_query (query_manager* qm, CachedQuery* qc)
+{
+ CachedQuery* up;
+ CachedQuery* down;
+
+ if (!qc || !qc->in_lru)
+ return;
+
+ qc->in_lru = 0;
+ up = qc->lru_up;
+ down = qc->lru_down;
+
+ if (!up)
+ qm->lru_top = down;
+
+ if (!down)
+ qm->lru_bottom = up;
+
+ if (down)
+ down->lru_up = up;
+
+ if (up)
+ up->lru_down = down;
+
+ qc->lru_up = qc->lru_down = NULL;
+}
+
+/* find and remove string2 from string1
+ * from start if position = 1,
+ * from end if position = 3,
+ * from anywhere if position = 2
+ * string1 is overwritten if position = 2.
+ */
+
+static int
+find_and_remove(struct berval* ber1, struct berval* ber2, int position)
+{
+ int ret=0;
+
+ if ( !ber2->bv_val )
+ return 1;
+ if ( !ber1->bv_val )
+ return 0;
+
+ switch( position ) {
+ case 1:
+ if ( ber1->bv_len >= ber2->bv_len && !memcmp( ber1->bv_val,
+ ber2->bv_val, ber2->bv_len )) {
+ ret = 1;
+ ber1->bv_val += ber2->bv_len;
+ ber1->bv_len -= ber2->bv_len;
+ }
+ break;
+ case 2: {
+ char *temp;
+ ber1->bv_val[ber1->bv_len] = '\0';
+ temp = strstr( ber1->bv_val, ber2->bv_val );
+ if ( temp ) {
+ strcpy( temp, temp+ber2->bv_len );
+ ber1->bv_len -= ber2->bv_len;
+ ret = 1;
+ }
+ break;
+ }
+ case 3:
+ if ( ber1->bv_len >= ber2->bv_len &&
+ !memcmp( ber1->bv_val+ber1->bv_len-ber2->bv_len, ber2->bv_val,
+ ber2->bv_len )) {
+ ret = 1;
+ ber1->bv_len -= ber2->bv_len;
+ }
+ break;
+ }
+ return ret;
+}
+
+
+static struct berval*
+merge_init_final(Operation *op, struct berval* init, struct berval* any,
+ struct berval* final)
+{
+ struct berval* merged, *temp;
+ int i, any_count, count;
+
+ for (any_count=0; any && any[any_count].bv_val; any_count++)
+ ;
+
+ count = any_count;
+
+ if (init->bv_val)
+ count++;
+ if (final->bv_val)
+ count++;
+
+ merged = (struct berval*)op->o_tmpalloc( (count+1)*sizeof(struct berval),
+ op->o_tmpmemctx );
+ temp = merged;
+
+ if (init->bv_val) {
+ ber_dupbv_x( temp, init, op->o_tmpmemctx );
+ temp++;
+ }
+
+ for (i=0; i<any_count; i++) {
+ ber_dupbv_x( temp, any, op->o_tmpmemctx );
+ temp++; any++;
+ }
+
+ if (final->bv_val){
+ ber_dupbv_x( temp, final, op->o_tmpmemctx );
+ temp++;
+ }
+ BER_BVZERO( temp );
+ return merged;
+}
+
+/* Each element in stored must be found in incoming. Incoming is overwritten.
+ */
+static int
+strings_containment(struct berval* stored, struct berval* incoming)
+{
+ struct berval* element;
+ int k=0;
+ int j, rc = 0;
+
+ for ( element=stored; element->bv_val != NULL; element++ ) {
+ for (j = k; incoming[j].bv_val != NULL; j++) {
+ if (find_and_remove(&(incoming[j]), element, 2)) {
+ k = j;
+ rc = 1;
+ break;
+ }
+ rc = 0;
+ }
+ if ( rc ) {
+ continue;
+ } else {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+substr_containment_substr(Operation *op, Filter* stored, Filter* incoming)
+{
+ int rc = 0;
+
+ struct berval init_incoming;
+ struct berval final_incoming;
+ struct berval *remaining_incoming = NULL;
+
+ if ((!(incoming->f_sub_initial.bv_val) && (stored->f_sub_initial.bv_val))
+ || (!(incoming->f_sub_final.bv_val) && (stored->f_sub_final.bv_val)))
+ return 0;
+
+ init_incoming = incoming->f_sub_initial;
+ final_incoming = incoming->f_sub_final;
+
+ if (find_and_remove(&init_incoming,
+ &(stored->f_sub_initial), 1) && find_and_remove(&final_incoming,
+ &(stored->f_sub_final), 3))
+ {
+ if (stored->f_sub_any == NULL) {
+ rc = 1;
+ goto final;
+ }
+ remaining_incoming = merge_init_final(op, &init_incoming,
+ incoming->f_sub_any, &final_incoming);
+ rc = strings_containment(stored->f_sub_any, remaining_incoming);
+ ber_bvarray_free_x( remaining_incoming, op->o_tmpmemctx );
+ }
+final:
+ return rc;
+}
+
+static int
+substr_containment_equality(Operation *op, Filter* stored, Filter* incoming)
+{
+ struct berval incoming_val[2];
+ int rc = 0;
+
+ incoming_val[1] = incoming->f_av_value;
+
+ if (find_and_remove(incoming_val+1,
+ &(stored->f_sub_initial), 1) && find_and_remove(incoming_val+1,
+ &(stored->f_sub_final), 3)) {
+ if (stored->f_sub_any == NULL){
+ rc = 1;
+ goto final;
+ }
+ ber_dupbv_x( incoming_val, incoming_val+1, op->o_tmpmemctx );
+ BER_BVZERO( incoming_val+1 );
+ rc = strings_containment(stored->f_sub_any, incoming_val);
+ op->o_tmpfree( incoming_val[0].bv_val, op->o_tmpmemctx );
+ }
+final:
+ return rc;
+}
+
+static Filter *
+filter_first( Filter *f )
+{
+ while ( f->f_choice == LDAP_FILTER_OR || f->f_choice == LDAP_FILTER_AND )
+ f = f->f_and;
+ return f;
+}
+
+typedef struct fstack {
+ struct fstack *fs_next;
+ Filter *fs_fs;
+ Filter *fs_fi;
+} fstack;
+
+static CachedQuery *
+find_filter( Operation *op, TAvlnode *root, Filter *inputf, Filter *first )
+{
+ Filter* fs;
+ Filter* fi;
+ MatchingRule* mrule = NULL;
+ int res=0, eqpass= 0;
+ int ret, rc, dir;
+ TAvlnode *ptr;
+ CachedQuery cq, *qc;
+ fstack *stack = NULL, *fsp;
+
+ cq.filter = inputf;
+ cq.first = first;
+
+ /* substring matches sort to the end, and we just have to
+ * walk the entire list.
+ */
+ if ( first->f_choice == LDAP_FILTER_SUBSTRINGS ) {
+ ptr = ldap_tavl_end( root, 1 );
+ dir = TAVL_DIR_LEFT;
+ } else {
+ ptr = ldap_tavl_find3( root, &cq, pcache_query_cmp, &ret );
+ dir = (first->f_choice == LDAP_FILTER_GE) ? TAVL_DIR_LEFT :
+ TAVL_DIR_RIGHT;
+ }
+
+ while (ptr) {
+ qc = ptr->avl_data;
+ fi = inputf;
+ fs = qc->filter;
+
+ /* an incoming substr query can only be satisfied by a cached
+ * substr query.
+ */
+ if ( first->f_choice == LDAP_FILTER_SUBSTRINGS &&
+ qc->first->f_choice != LDAP_FILTER_SUBSTRINGS )
+ break;
+
+ /* an incoming eq query can be satisfied by a cached eq or substr
+ * query
+ */
+ if ( first->f_choice == LDAP_FILTER_EQUALITY ) {
+ if ( eqpass == 0 ) {
+ if ( qc->first->f_choice != LDAP_FILTER_EQUALITY ) {
+nextpass: eqpass = 1;
+ ptr = ldap_tavl_end( root, 1 );
+ dir = TAVL_DIR_LEFT;
+ continue;
+ }
+ } else {
+ if ( qc->first->f_choice != LDAP_FILTER_SUBSTRINGS )
+ break;
+ }
+ }
+ do {
+ res=0;
+ switch (fs->f_choice) {
+ case LDAP_FILTER_EQUALITY:
+ if (fi->f_choice == LDAP_FILTER_EQUALITY)
+ mrule = fs->f_ava->aa_desc->ad_type->sat_equality;
+ else
+ ret = 1;
+ break;
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ mrule = fs->f_ava->aa_desc->ad_type->sat_ordering;
+ break;
+ default:
+ mrule = NULL;
+ }
+ if (mrule) {
+ const char *text;
+ rc = value_match(&ret, fs->f_ava->aa_desc, mrule,
+ SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
+ &(fi->f_ava->aa_value),
+ &(fs->f_ava->aa_value), &text);
+ if (rc != LDAP_SUCCESS) {
+ return NULL;
+ }
+ if ( fi==first && fi->f_choice==LDAP_FILTER_EQUALITY && ret )
+ goto nextpass;
+ }
+ switch (fs->f_choice) {
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_AND:
+ if ( fs->f_next ) {
+ /* save our stack position */
+ fsp = op->o_tmpalloc(sizeof(fstack), op->o_tmpmemctx);
+ fsp->fs_next = stack;
+ fsp->fs_fs = fs->f_next;
+ fsp->fs_fi = fi->f_next;
+ stack = fsp;
+ }
+ fs = fs->f_and;
+ fi = fi->f_and;
+ res=1;
+ break;
+ case LDAP_FILTER_SUBSTRINGS:
+ /* check if the equality query can be
+ * answered with cached substring query */
+ if ((fi->f_choice == LDAP_FILTER_EQUALITY)
+ && substr_containment_equality( op,
+ fs, fi))
+ res=1;
+ /* check if the substring query can be
+ * answered with cached substring query */
+ if ((fi->f_choice ==LDAP_FILTER_SUBSTRINGS
+ ) && substr_containment_substr( op,
+ fs, fi))
+ res= 1;
+ fs=fs->f_next;
+ fi=fi->f_next;
+ break;
+ case LDAP_FILTER_PRESENT:
+ res=1;
+ fs=fs->f_next;
+ fi=fi->f_next;
+ break;
+ case LDAP_FILTER_EQUALITY:
+ if (ret == 0)
+ res = 1;
+ fs=fs->f_next;
+ fi=fi->f_next;
+ break;
+ case LDAP_FILTER_GE:
+ if (mrule && ret >= 0)
+ res = 1;
+ fs=fs->f_next;
+ fi=fi->f_next;
+ break;
+ case LDAP_FILTER_LE:
+ if (mrule && ret <= 0)
+ res = 1;
+ fs=fs->f_next;
+ fi=fi->f_next;
+ break;
+ case LDAP_FILTER_NOT:
+ res=0;
+ break;
+ default:
+ break;
+ }
+ if (!fs && !fi && stack) {
+ /* pop the stack */
+ fsp = stack;
+ stack = fsp->fs_next;
+ fs = fsp->fs_fs;
+ fi = fsp->fs_fi;
+ op->o_tmpfree(fsp, op->o_tmpmemctx);
+ }
+ } while((res) && (fi != NULL) && (fs != NULL));
+
+ if ( res )
+ return qc;
+ ptr = ldap_tavl_next( ptr, dir );
+ }
+ return NULL;
+}
+
+/* check whether query is contained in any of
+ * the cached queries in template
+ */
+static CachedQuery *
+query_containment(Operation *op, query_manager *qm,
+ Query *query,
+ QueryTemplate *templa)
+{
+ CachedQuery* qc;
+ int depth = 0, tscope;
+ Qbase qbase, *qbptr = NULL;
+ struct berval pdn;
+
+ if (query->filter != NULL) {
+ Filter *first;
+
+ Debug( pcache_debug, "Lock QC index = %p\n",
+ (void *) templa );
+ qbase.base = query->base;
+
+ first = filter_first( query->filter );
+
+ ldap_pvt_thread_rdwr_rlock(&templa->t_rwlock);
+ for( ;; ) {
+ /* Find the base */
+ qbptr = ldap_avl_find( templa->qbase, &qbase, pcache_dn_cmp );
+ if ( qbptr ) {
+ tscope = query->scope;
+ /* Find a matching scope:
+ * match at depth 0 OK
+ * scope is BASE,
+ * one at depth 1 OK
+ * subord at depth > 0 OK
+ * subtree at any depth OK
+ * scope is ONE,
+ * subtree or subord at any depth OK
+ * scope is SUBORD,
+ * subtree or subord at any depth OK
+ * scope is SUBTREE,
+ * subord at depth > 0 OK
+ * subtree at any depth OK
+ */
+ for ( tscope = 0 ; tscope <= LDAP_SCOPE_CHILDREN; tscope++ ) {
+ switch ( query->scope ) {
+ case LDAP_SCOPE_BASE:
+ if ( tscope == LDAP_SCOPE_BASE && depth ) continue;
+ if ( tscope == LDAP_SCOPE_ONE && depth != 1) continue;
+ if ( tscope == LDAP_SCOPE_CHILDREN && !depth ) continue;
+ break;
+ case LDAP_SCOPE_ONE:
+ if ( tscope == LDAP_SCOPE_BASE )
+ tscope = LDAP_SCOPE_ONE;
+ if ( tscope == LDAP_SCOPE_ONE && depth ) continue;
+ if ( !depth ) break;
+ if ( tscope < LDAP_SCOPE_SUBTREE )
+ tscope = LDAP_SCOPE_SUBTREE;
+ break;
+ case LDAP_SCOPE_SUBTREE:
+ if ( tscope < LDAP_SCOPE_SUBTREE )
+ tscope = LDAP_SCOPE_SUBTREE;
+ if ( tscope == LDAP_SCOPE_CHILDREN && !depth ) continue;
+ break;
+ case LDAP_SCOPE_CHILDREN:
+ if ( tscope < LDAP_SCOPE_SUBTREE )
+ tscope = LDAP_SCOPE_SUBTREE;
+ break;
+ }
+ if ( !qbptr->scopes[tscope] ) continue;
+
+ /* Find filter */
+ qc = find_filter( op, qbptr->scopes[tscope],
+ query->filter, first );
+ if ( qc ) {
+ if ( qc->q_sizelimit ) {
+ ldap_pvt_thread_rdwr_runlock(&templa->t_rwlock);
+ return NULL;
+ }
+ ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
+ if (qm->lru_top != qc) {
+ remove_query(qm, qc);
+ add_query_on_top(qm, qc);
+ }
+ ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
+ return qc;
+ }
+ }
+ }
+ if ( be_issuffix( op->o_bd, &qbase.base ))
+ break;
+ /* Up a level */
+ dnParent( &qbase.base, &pdn );
+ qbase.base = pdn;
+ depth++;
+ }
+
+ Debug( pcache_debug,
+ "Not answerable: Unlock QC index=%p\n",
+ (void *) templa );
+ ldap_pvt_thread_rdwr_runlock(&templa->t_rwlock);
+ }
+ return NULL;
+}
+
+static void
+free_query (CachedQuery* qc)
+{
+ free(qc->q_uuid.bv_val);
+ filter_free(qc->filter);
+ ldap_pvt_thread_mutex_destroy(&qc->answerable_cnt_mutex);
+ ldap_pvt_thread_rdwr_destroy( &qc->rwlock );
+ memset(qc, 0, sizeof(*qc));
+ free(qc);
+}
+
+
+/* Add query to query cache, the returned Query is locked for writing */
+static CachedQuery *
+add_query(
+ Operation *op,
+ query_manager* qm,
+ Query* query,
+ QueryTemplate *templ,
+ pc_caching_reason_t why,
+ int wlock)
+{
+ CachedQuery* new_cached_query = (CachedQuery*) ch_malloc(sizeof(CachedQuery));
+ Qbase *qbase, qb;
+ Filter *first;
+ int rc;
+ time_t ttl = 0, ttr = 0;
+ time_t now;
+
+ new_cached_query->qtemp = templ;
+ BER_BVZERO( &new_cached_query->q_uuid );
+ new_cached_query->q_sizelimit = 0;
+
+ now = slap_get_time();
+ switch ( why ) {
+ case PC_POSITIVE:
+ ttl = templ->ttl;
+ if ( templ->ttr )
+ ttr = now + templ->ttr;
+ break;
+
+ case PC_NEGATIVE:
+ ttl = templ->negttl;
+ break;
+
+ case PC_SIZELIMIT:
+ ttl = templ->limitttl;
+ break;
+
+ default:
+ assert( 0 );
+ break;
+ }
+ new_cached_query->expiry_time = now + ttl;
+ new_cached_query->refresh_time = ttr;
+ new_cached_query->bindref_time = 0;
+
+ new_cached_query->bind_refcnt = 0;
+ new_cached_query->answerable_cnt = 0;
+ new_cached_query->refcnt = 1;
+ ldap_pvt_thread_mutex_init(&new_cached_query->answerable_cnt_mutex);
+
+ new_cached_query->lru_up = NULL;
+ new_cached_query->lru_down = NULL;
+ Debug( pcache_debug, "Added query expires at %ld (%s)\n",
+ (long) new_cached_query->expiry_time,
+ pc_caching_reason_str[ why ] );
+
+ new_cached_query->scope = query->scope;
+ new_cached_query->filter = query->filter;
+ new_cached_query->first = first = filter_first( query->filter );
+
+ ldap_pvt_thread_rdwr_init(&new_cached_query->rwlock);
+ if (wlock)
+ ldap_pvt_thread_rdwr_wlock(&new_cached_query->rwlock);
+
+ qb.base = query->base;
+
+ /* Adding a query */
+ Debug( pcache_debug, "Lock AQ index = %p\n",
+ (void *) templ );
+ ldap_pvt_thread_rdwr_wlock(&templ->t_rwlock);
+ qbase = ldap_avl_find( templ->qbase, &qb, pcache_dn_cmp );
+ if ( !qbase ) {
+ qbase = ch_calloc( 1, sizeof(Qbase) + qb.base.bv_len + 1 );
+ qbase->base.bv_len = qb.base.bv_len;
+ qbase->base.bv_val = (char *)(qbase+1);
+ memcpy( qbase->base.bv_val, qb.base.bv_val, qb.base.bv_len );
+ qbase->base.bv_val[qbase->base.bv_len] = '\0';
+ ldap_avl_insert( &templ->qbase, qbase, pcache_dn_cmp, ldap_avl_dup_error );
+ }
+ new_cached_query->next = templ->query;
+ new_cached_query->prev = NULL;
+ new_cached_query->qbase = qbase;
+ rc = ldap_tavl_insert( &qbase->scopes[query->scope], new_cached_query,
+ pcache_query_cmp, ldap_avl_dup_error );
+ if ( rc == 0 ) {
+ qbase->queries++;
+ if (templ->query == NULL)
+ templ->query_last = new_cached_query;
+ else
+ templ->query->prev = new_cached_query;
+ templ->query = new_cached_query;
+ templ->no_of_queries++;
+ } else {
+ ldap_pvt_thread_mutex_destroy(&new_cached_query->answerable_cnt_mutex);
+ if (wlock)
+ ldap_pvt_thread_rdwr_wunlock(&new_cached_query->rwlock);
+ ldap_pvt_thread_rdwr_destroy( &new_cached_query->rwlock );
+ ch_free( new_cached_query );
+ new_cached_query = find_filter( op, qbase->scopes[query->scope],
+ query->filter, first );
+ filter_free( query->filter );
+ query->filter = NULL;
+ }
+ Debug( pcache_debug, "TEMPLATE %p QUERIES++ %d\n",
+ (void *) templ, templ->no_of_queries );
+
+ /* Adding on top of LRU list */
+ if ( rc == 0 ) {
+ ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
+ add_query_on_top(qm, new_cached_query);
+ ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
+ }
+ Debug( pcache_debug, "Unlock AQ index = %p \n",
+ (void *) templ );
+ ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock);
+
+ return rc == 0 ? new_cached_query : NULL;
+}
+
+static void
+remove_from_template (CachedQuery* qc, QueryTemplate* template)
+{
+ if (!qc->prev && !qc->next) {
+ template->query_last = template->query = NULL;
+ } else if (qc->prev == NULL) {
+ qc->next->prev = NULL;
+ template->query = qc->next;
+ } else if (qc->next == NULL) {
+ qc->prev->next = NULL;
+ template->query_last = qc->prev;
+ } else {
+ qc->next->prev = qc->prev;
+ qc->prev->next = qc->next;
+ }
+ ldap_tavl_delete( &qc->qbase->scopes[qc->scope], qc, pcache_query_cmp );
+ qc->qbase->queries--;
+ if ( qc->qbase->queries == 0 ) {
+ ldap_avl_delete( &template->qbase, qc->qbase, pcache_dn_cmp );
+ ch_free( qc->qbase );
+ qc->qbase = NULL;
+ }
+
+ template->no_of_queries--;
+}
+
+/* remove bottom query of LRU list from the query cache */
+/*
+ * NOTE: slight change in functionality.
+ *
+ * - if result->bv_val is NULL, the query at the bottom of the LRU
+ * is removed
+ * - otherwise, the query whose UUID is *result is removed
+ * - if not found, result->bv_val is zeroed
+ */
+static void
+cache_replacement(query_manager* qm, struct berval *result)
+{
+ CachedQuery* bottom;
+ QueryTemplate *temp;
+
+ ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
+ if ( BER_BVISNULL( result ) ) {
+ bottom = qm->lru_bottom;
+
+ if (!bottom) {
+ Debug ( pcache_debug,
+ "Cache replacement invoked without "
+ "any query in LRU list\n" );
+ ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
+ return;
+ }
+
+ } else {
+ for ( bottom = qm->lru_bottom;
+ bottom != NULL;
+ bottom = bottom->lru_up )
+ {
+ if ( bvmatch( result, &bottom->q_uuid ) ) {
+ break;
+ }
+ }
+
+ if ( !bottom ) {
+ Debug ( pcache_debug,
+ "Could not find query with uuid=\"%s\""
+ "in LRU list\n", result->bv_val );
+ ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
+ BER_BVZERO( result );
+ return;
+ }
+ }
+
+ temp = bottom->qtemp;
+ remove_query(qm, bottom);
+ ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
+
+ *result = bottom->q_uuid;
+ BER_BVZERO( &bottom->q_uuid );
+
+ Debug( pcache_debug, "Lock CR index = %p\n", (void *) temp );
+ ldap_pvt_thread_rdwr_wlock(&temp->t_rwlock);
+ remove_from_template(bottom, temp);
+ Debug( pcache_debug, "TEMPLATE %p QUERIES-- %d\n",
+ (void *) temp, temp->no_of_queries );
+ Debug( pcache_debug, "Unlock CR index = %p\n", (void *) temp );
+ ldap_pvt_thread_rdwr_wunlock(&temp->t_rwlock);
+ free_query(bottom);
+}
+
+struct query_info {
+ struct query_info *next;
+ struct berval xdn;
+ int del;
+};
+
+static int
+remove_func (
+ Operation *op,
+ SlapReply *rs
+)
+{
+ Attribute *attr;
+ struct query_info *qi;
+ int count = 0;
+
+ if ( rs->sr_type != REP_SEARCH ) return 0;
+
+ attr = attr_find( rs->sr_entry->e_attrs, ad_queryId );
+ if ( attr == NULL ) return 0;
+
+ count = attr->a_numvals;
+ assert( count > 0 );
+ qi = op->o_tmpalloc( sizeof( struct query_info ), op->o_tmpmemctx );
+ qi->next = op->o_callback->sc_private;
+ op->o_callback->sc_private = qi;
+ ber_dupbv_x( &qi->xdn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
+ qi->del = ( count == 1 );
+
+ return 0;
+}
+
+static int
+remove_query_data(
+ Operation *op,
+ struct berval *query_uuid )
+{
+ struct query_info *qi, *qnext;
+ char filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(pcacheQueryID=)" ) ];
+ AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
+ Filter filter = {LDAP_FILTER_EQUALITY};
+ SlapReply sreply = {REP_RESULT};
+ slap_callback cb = { NULL, remove_func, NULL, NULL };
+ int deleted = 0;
+
+ op->ors_filterstr.bv_len = snprintf(filter_str, sizeof(filter_str),
+ "(%s=%s)", ad_queryId->ad_cname.bv_val, query_uuid->bv_val);
+ filter.f_ava = &ava;
+ filter.f_av_desc = ad_queryId;
+ filter.f_av_value = *query_uuid;
+
+ op->o_tag = LDAP_REQ_SEARCH;
+ op->o_protocol = LDAP_VERSION3;
+ op->o_callback = &cb;
+ op->o_time = slap_get_time();
+ op->o_do_not_cache = 1;
+
+ op->o_req_dn = op->o_bd->be_suffix[0];
+ op->o_req_ndn = op->o_bd->be_nsuffix[0];
+ op->ors_scope = LDAP_SCOPE_SUBTREE;
+ op->ors_deref = LDAP_DEREF_NEVER;
+ op->ors_slimit = SLAP_NO_LIMIT;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_limit = NULL;
+ op->ors_filter = &filter;
+ op->ors_filterstr.bv_val = filter_str;
+ op->ors_filterstr.bv_len = strlen(filter_str);
+ op->ors_attrs = NULL;
+ op->ors_attrsonly = 0;
+
+ op->o_bd->be_search( op, &sreply );
+
+ for ( qi=cb.sc_private; qi; qi=qnext ) {
+ qnext = qi->next;
+
+ op->o_req_dn = qi->xdn;
+ op->o_req_ndn = qi->xdn;
+ rs_reinit( &sreply, REP_RESULT );
+
+ if ( qi->del ) {
+ Debug( pcache_debug, "DELETING ENTRY TEMPLATE=%s\n",
+ query_uuid->bv_val );
+
+ op->o_tag = LDAP_REQ_DELETE;
+
+ if (op->o_bd->be_delete(op, &sreply) == LDAP_SUCCESS) {
+ deleted++;
+ }
+
+ } else {
+ Modifications mod;
+ struct berval vals[2];
+
+ vals[0] = *query_uuid;
+ vals[1].bv_val = NULL;
+ vals[1].bv_len = 0;
+ mod.sml_op = LDAP_MOD_DELETE;
+ mod.sml_flags = 0;
+ mod.sml_desc = ad_queryId;
+ mod.sml_type = ad_queryId->ad_cname;
+ mod.sml_values = vals;
+ mod.sml_nvalues = NULL;
+ mod.sml_numvals = 1;
+ mod.sml_next = NULL;
+ Debug( pcache_debug,
+ "REMOVING TEMP ATTR : TEMPLATE=%s\n",
+ query_uuid->bv_val );
+
+ op->orm_modlist = &mod;
+
+ op->o_bd->be_modify( op, &sreply );
+ }
+ op->o_tmpfree( qi->xdn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( qi, op->o_tmpmemctx );
+ }
+ return deleted;
+}
+
+static int
+get_attr_set(
+ AttributeName* attrs,
+ query_manager* qm,
+ int num
+);
+
+static int
+filter2template(
+ Operation *op,
+ Filter *f,
+ struct berval *fstr )
+{
+ AttributeDescription *ad;
+ int len, ret;
+
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_EQUALITY:
+ ad = f->f_av_desc;
+ len = STRLENOF( "(=)" ) + ad->ad_cname.bv_len;
+ ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s=)", ad->ad_cname.bv_val );
+ assert( ret == len );
+ fstr->bv_len += len;
+ break;
+
+ case LDAP_FILTER_GE:
+ ad = f->f_av_desc;
+ len = STRLENOF( "(>=)" ) + ad->ad_cname.bv_len;
+ ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s>=)", ad->ad_cname.bv_val);
+ assert( ret == len );
+ fstr->bv_len += len;
+ break;
+
+ case LDAP_FILTER_LE:
+ ad = f->f_av_desc;
+ len = STRLENOF( "(<=)" ) + ad->ad_cname.bv_len;
+ ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s<=)", ad->ad_cname.bv_val);
+ assert( ret == len );
+ fstr->bv_len += len;
+ break;
+
+ case LDAP_FILTER_APPROX:
+ ad = f->f_av_desc;
+ len = STRLENOF( "(~=)" ) + ad->ad_cname.bv_len;
+ ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s~=)", ad->ad_cname.bv_val);
+ assert( ret == len );
+ fstr->bv_len += len;
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ ad = f->f_sub_desc;
+ len = STRLENOF( "(=)" ) + ad->ad_cname.bv_len;
+ ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s=)", ad->ad_cname.bv_val );
+ assert( ret == len );
+ fstr->bv_len += len;
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ ad = f->f_desc;
+ len = STRLENOF( "(=*)" ) + ad->ad_cname.bv_len;
+ ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s=*)", ad->ad_cname.bv_val );
+ assert( ret == len );
+ fstr->bv_len += len;
+ break;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT: {
+ int rc = 0;
+ fstr->bv_val[fstr->bv_len++] = '(';
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_AND:
+ fstr->bv_val[fstr->bv_len] = '&';
+ break;
+ case LDAP_FILTER_OR:
+ fstr->bv_val[fstr->bv_len] = '|';
+ break;
+ case LDAP_FILTER_NOT:
+ fstr->bv_val[fstr->bv_len] = '!';
+ break;
+ }
+ fstr->bv_len++;
+
+ for ( f = f->f_list; f != NULL; f = f->f_next ) {
+ rc = filter2template( op, f, fstr );
+ if ( rc ) break;
+ }
+ fstr->bv_val[fstr->bv_len++] = ')';
+ fstr->bv_val[fstr->bv_len] = '\0';
+
+ return rc;
+ }
+
+ default:
+ /* a filter should at least have room for "()",
+ * an "=" and for a 1-char attr */
+ strcpy( fstr->bv_val, "(?=)" );
+ fstr->bv_len += STRLENOF("(?=)");
+ return -1;
+ }
+
+ return 0;
+}
+
+#define BI_HASHED 0x01
+#define BI_DIDCB 0x02
+#define BI_LOOKUP 0x04
+
+struct search_info;
+
+typedef struct bindinfo {
+ cache_manager *bi_cm;
+ CachedQuery *bi_cq;
+ QueryTemplate *bi_templ;
+ struct search_info *bi_si;
+ int bi_flags;
+ slap_callback bi_cb;
+} bindinfo;
+
+struct search_info {
+ slap_overinst *on;
+ Query query;
+ QueryTemplate *qtemp;
+ AttributeName* save_attrs; /* original attributes, saved for response */
+ int swap_saved_attrs;
+ int max;
+ int over;
+ int count;
+ int slimit;
+ int slimit_exceeded;
+ pc_caching_reason_t caching_reason;
+ Entry *head, *tail;
+ bindinfo *pbi;
+};
+
+static void
+remove_query_and_data(
+ Operation *op,
+ cache_manager *cm,
+ struct berval *uuid )
+{
+ query_manager* qm = cm->qm;
+
+ qm->crfunc( qm, uuid );
+ if ( !BER_BVISNULL( uuid ) ) {
+ int return_val;
+
+ Debug( pcache_debug,
+ "Removing query UUID %s\n",
+ uuid->bv_val );
+ return_val = remove_query_data( op, uuid );
+ Debug( pcache_debug,
+ "QUERY REMOVED, SIZE=%d\n",
+ return_val );
+ ldap_pvt_thread_mutex_lock( &cm->cache_mutex );
+ cm->cur_entries -= return_val;
+ cm->num_cached_queries--;
+ Debug( pcache_debug,
+ "STORED QUERIES = %lu\n",
+ cm->num_cached_queries );
+ ldap_pvt_thread_mutex_unlock( &cm->cache_mutex );
+ Debug( pcache_debug,
+ "QUERY REMOVED, CACHE ="
+ "%d entries\n",
+ cm->cur_entries );
+ }
+}
+
+/*
+ * Callback used to fetch queryId values based on entryUUID;
+ * used by pcache_remove_entries_from_cache()
+ */
+static int
+fetch_queryId_cb( Operation *op, SlapReply *rs )
+{
+ int rc = 0;
+
+ /* only care about searchEntry responses */
+ if ( rs->sr_type != REP_SEARCH ) {
+ return 0;
+ }
+
+ /* allow only one response per entryUUID */
+ if ( op->o_callback->sc_private != NULL ) {
+ rc = 1;
+
+ } else {
+ Attribute *a;
+
+ /* copy all queryId values into callback's private data */
+ a = attr_find( rs->sr_entry->e_attrs, ad_queryId );
+ if ( a != NULL ) {
+ BerVarray vals = NULL;
+
+ ber_bvarray_dup_x( &vals, a->a_nvals, op->o_tmpmemctx );
+ op->o_callback->sc_private = (void *)vals;
+ }
+ }
+
+ /* clear entry if required */
+ rs_flush_entry( op, rs, (slap_overinst *) op->o_bd->bd_info );
+
+ return rc;
+}
+
+/*
+ * Call that allows to remove a set of entries from the cache,
+ * by forcing the removal of all the related queries.
+ */
+int
+pcache_remove_entries_from_cache(
+ Operation *op,
+ cache_manager *cm,
+ BerVarray entryUUIDs )
+{
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+ Operation op2;
+ slap_callback sc = { 0 };
+ Filter f = { 0 };
+ char filtbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(entryUUID=)" ) ];
+ AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
+ AttributeName attrs[ 2 ] = {{{ 0 }}};
+ int s, rc;
+
+ if ( op == NULL ) {
+ void *thrctx = ldap_pvt_thread_pool_context();
+
+ connection_fake_init( &conn, &opbuf, thrctx );
+ op = &opbuf.ob_op;
+
+ } else {
+ op2 = *op;
+ op = &op2;
+ }
+
+ memset( &op->oq_search, 0, sizeof( op->oq_search ) );
+ op->ors_scope = LDAP_SCOPE_SUBTREE;
+ op->ors_deref = LDAP_DEREF_NEVER;
+ f.f_choice = LDAP_FILTER_EQUALITY;
+ f.f_ava = &ava;
+ ava.aa_desc = slap_schema.si_ad_entryUUID;
+ op->ors_filter = &f;
+ op->ors_slimit = 1;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_limit = NULL;
+ attrs[ 0 ].an_desc = ad_queryId;
+ attrs[ 0 ].an_name = ad_queryId->ad_cname;
+ op->ors_attrs = attrs;
+ op->ors_attrsonly = 0;
+
+ op->o_req_dn = cm->db.be_suffix[ 0 ];
+ op->o_req_ndn = cm->db.be_nsuffix[ 0 ];
+
+ op->o_tag = LDAP_REQ_SEARCH;
+ op->o_protocol = LDAP_VERSION3;
+ op->o_managedsait = SLAP_CONTROL_CRITICAL;
+ op->o_bd = &cm->db;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ sc.sc_response = fetch_queryId_cb;
+ op->o_callback = &sc;
+
+ for ( s = 0; !BER_BVISNULL( &entryUUIDs[ s ] ); s++ ) {
+ BerVarray vals = NULL;
+ SlapReply rs = { REP_RESULT };
+
+ op->ors_filterstr.bv_len = snprintf( filtbuf, sizeof( filtbuf ),
+ "(entryUUID=%s)", entryUUIDs[ s ].bv_val );
+ op->ors_filterstr.bv_val = filtbuf;
+ ava.aa_value = entryUUIDs[ s ];
+
+ rc = op->o_bd->be_search( op, &rs );
+ if ( rc != LDAP_SUCCESS ) {
+ continue;
+ }
+
+ vals = (BerVarray)op->o_callback->sc_private;
+ if ( vals != NULL ) {
+ int i;
+
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
+ struct berval val = vals[ i ];
+
+ remove_query_and_data( op, cm, &val );
+
+ if ( !BER_BVISNULL( &val ) && val.bv_val != vals[ i ].bv_val ) {
+ ch_free( val.bv_val );
+ }
+ }
+
+ ber_bvarray_free_x( vals, op->o_tmpmemctx );
+ op->o_callback->sc_private = NULL;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Call that allows to remove a query from the cache.
+ */
+int
+pcache_remove_query_from_cache(
+ Operation *op,
+ cache_manager *cm,
+ struct berval *queryid )
+{
+ Operation op2 = *op;
+
+ op2.o_bd = &cm->db;
+
+ /* remove the selected query */
+ remove_query_and_data( &op2, cm, queryid );
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Call that allows to remove a set of queries related to an entry
+ * from the cache; if queryid is not null, the entry must belong to
+ * the query indicated by queryid.
+ */
+int
+pcache_remove_entry_queries_from_cache(
+ Operation *op,
+ cache_manager *cm,
+ struct berval *ndn,
+ struct berval *queryid )
+{
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+ Operation op2;
+ slap_callback sc = { 0 };
+ SlapReply rs = { REP_RESULT };
+ Filter f = { 0 };
+ char filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(pcacheQueryID=)" ) ];
+ AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
+ AttributeName attrs[ 2 ] = {{{ 0 }}};
+ int rc;
+
+ BerVarray vals = NULL;
+
+ if ( op == NULL ) {
+ void *thrctx = ldap_pvt_thread_pool_context();
+
+ connection_fake_init( &conn, &opbuf, thrctx );
+ op = &opbuf.ob_op;
+
+ } else {
+ op2 = *op;
+ op = &op2;
+ }
+
+ memset( &op->oq_search, 0, sizeof( op->oq_search ) );
+ op->ors_scope = LDAP_SCOPE_BASE;
+ op->ors_deref = LDAP_DEREF_NEVER;
+ if ( queryid == NULL || BER_BVISNULL( queryid ) ) {
+ BER_BVSTR( &op->ors_filterstr, "(objectClass=*)" );
+ f.f_choice = LDAP_FILTER_PRESENT;
+ f.f_desc = slap_schema.si_ad_objectClass;
+
+ } else {
+ op->ors_filterstr.bv_len = snprintf( filter_str,
+ sizeof( filter_str ), "(%s=%s)",
+ ad_queryId->ad_cname.bv_val, queryid->bv_val );
+ f.f_choice = LDAP_FILTER_EQUALITY;
+ f.f_ava = &ava;
+ f.f_av_desc = ad_queryId;
+ f.f_av_value = *queryid;
+ }
+ op->ors_filter = &f;
+ op->ors_slimit = 1;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_limit = NULL;
+ attrs[ 0 ].an_desc = ad_queryId;
+ attrs[ 0 ].an_name = ad_queryId->ad_cname;
+ op->ors_attrs = attrs;
+ op->ors_attrsonly = 0;
+
+ op->o_req_dn = *ndn;
+ op->o_req_ndn = *ndn;
+
+ op->o_tag = LDAP_REQ_SEARCH;
+ op->o_protocol = LDAP_VERSION3;
+ op->o_managedsait = SLAP_CONTROL_CRITICAL;
+ op->o_bd = &cm->db;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ sc.sc_response = fetch_queryId_cb;
+ op->o_callback = &sc;
+
+ rc = op->o_bd->be_search( op, &rs );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ vals = (BerVarray)op->o_callback->sc_private;
+ if ( vals != NULL ) {
+ int i;
+
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
+ struct berval val = vals[ i ];
+
+ remove_query_and_data( op, cm, &val );
+
+ if ( !BER_BVISNULL( &val ) && val.bv_val != vals[ i ].bv_val ) {
+ ch_free( val.bv_val );
+ }
+ }
+
+ ber_bvarray_free_x( vals, op->o_tmpmemctx );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+cache_entries(
+ Operation *op,
+ struct berval *query_uuid )
+{
+ struct search_info *si = op->o_callback->sc_private;
+ slap_overinst *on = si->on;
+ cache_manager *cm = on->on_bi.bi_private;
+ int return_val = 0;
+ Entry *e;
+ struct berval crp_uuid;
+ char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
+ Operation *op_tmp;
+ Connection conn = {0};
+ OperationBuffer opbuf;
+ void *thrctx = ldap_pvt_thread_pool_context();
+
+ query_uuid->bv_len = lutil_uuidstr(uuidbuf, sizeof(uuidbuf));
+ ber_str2bv(uuidbuf, query_uuid->bv_len, 1, query_uuid);
+
+ connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+ op_tmp = &opbuf.ob_op;
+ op_tmp->o_bd = &cm->db;
+ op_tmp->o_dn = cm->db.be_rootdn;
+ op_tmp->o_ndn = cm->db.be_rootndn;
+
+ Debug( pcache_debug, "UUID for query being added = %s\n",
+ uuidbuf );
+
+ for ( e=si->head; e; e=si->head ) {
+ si->head = e->e_private;
+ e->e_private = NULL;
+ while ( cm->cur_entries > (cm->max_entries) ) {
+ BER_BVZERO( &crp_uuid );
+ remove_query_and_data( op_tmp, cm, &crp_uuid );
+ }
+
+ return_val = merge_entry(op_tmp, e, 0, query_uuid);
+ ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
+ cm->cur_entries += return_val;
+ Debug( pcache_debug,
+ "ENTRY ADDED/MERGED, CACHED ENTRIES=%d\n",
+ cm->cur_entries );
+ return_val = 0;
+ ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
+ }
+
+ return return_val;
+}
+
+static int
+pcache_op_cleanup( Operation *op, SlapReply *rs ) {
+ slap_callback *cb = op->o_callback;
+ struct search_info *si = cb->sc_private;
+ slap_overinst *on = si->on;
+ cache_manager *cm = on->on_bi.bi_private;
+ query_manager* qm = cm->qm;
+
+ if ( rs->sr_type == REP_RESULT ||
+ op->o_abandon || rs->sr_err == SLAPD_ABANDON )
+ {
+ if ( si->swap_saved_attrs ) {
+ rs->sr_attrs = si->save_attrs;
+ op->ors_attrs = si->save_attrs;
+ }
+ if ( (op->o_abandon || rs->sr_err == SLAPD_ABANDON) &&
+ si->caching_reason == PC_IGNORE )
+ {
+ filter_free( si->query.filter );
+ if ( si->count ) {
+ /* duplicate query, free it */
+ Entry *e;
+ for (;si->head; si->head=e) {
+ e = si->head->e_private;
+ si->head->e_private = NULL;
+ entry_free(si->head);
+ }
+ }
+
+ } else if ( si->caching_reason != PC_IGNORE ) {
+ CachedQuery *qc = qm->addfunc(op, qm, &si->query,
+ si->qtemp, si->caching_reason, 1 );
+
+ if ( qc != NULL ) {
+ switch ( si->caching_reason ) {
+ case PC_POSITIVE:
+ cache_entries( op, &qc->q_uuid );
+ if ( si->pbi ) {
+ qc->bind_refcnt++;
+ si->pbi->bi_cq = qc;
+ }
+ break;
+
+ case PC_SIZELIMIT:
+ qc->q_sizelimit = rs->sr_nentries;
+ break;
+
+ case PC_NEGATIVE:
+ break;
+
+ default:
+ assert( 0 );
+ break;
+ }
+ ldap_pvt_thread_rdwr_wunlock(&qc->rwlock);
+ ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
+ cm->num_cached_queries++;
+ Debug( pcache_debug, "STORED QUERIES = %lu\n",
+ cm->num_cached_queries );
+ ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
+
+ /* If the consistency checker suspended itself,
+ * wake it back up
+ */
+ if ( cm->cc_paused == PCACHE_CC_PAUSED ) {
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( cm->cc_paused == PCACHE_CC_PAUSED ) {
+ cm->cc_paused = 0;
+ ldap_pvt_runqueue_resched( &slapd_rq, cm->cc_arg, 0 );
+ }
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+
+ } else if ( si->count ) {
+ /* duplicate query, free it */
+ Entry *e;
+ for (;si->head; si->head=e) {
+ e = si->head->e_private;
+ si->head->e_private = NULL;
+ entry_free(si->head);
+ }
+ }
+
+ } else {
+ filter_free( si->query.filter );
+ }
+
+ op->o_callback = op->o_callback->sc_next;
+ op->o_tmpfree( cb, op->o_tmpmemctx );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+pcache_response(
+ Operation *op,
+ SlapReply *rs )
+{
+ struct search_info *si = op->o_callback->sc_private;
+
+ if ( si->swap_saved_attrs ) {
+ rs->sr_attrs = si->save_attrs;
+ rs->sr_attr_flags = slap_attr_flags( si->save_attrs );
+ op->ors_attrs = si->save_attrs;
+ }
+
+ if ( rs->sr_type == REP_SEARCH ) {
+ Entry *e;
+
+ /* don't return more entries than requested by the client */
+ if ( si->slimit > 0 && rs->sr_nentries >= si->slimit ) {
+ si->slimit_exceeded = 1;
+ }
+
+ /* If we haven't exceeded the limit for this query,
+ * build a chain of answers to store. If we hit the
+ * limit, empty the chain and ignore the rest.
+ */
+ if ( !si->over ) {
+ slap_overinst *on = si->on;
+ cache_manager *cm = on->on_bi.bi_private;
+
+ /* check if the entry contains undefined
+ * attributes/objectClasses (ITS#5680) */
+ if ( cm->check_cacheability && test_filter( op, rs->sr_entry, si->query.filter ) != LDAP_COMPARE_TRUE ) {
+ Debug( pcache_debug, "%s: query not cacheable because of schema issues in DN \"%s\"\n",
+ op->o_log_prefix, rs->sr_entry->e_name.bv_val );
+ goto over;
+ }
+
+ /* check for malformed entries: attrs with no values */
+ {
+ Attribute *a = rs->sr_entry->e_attrs;
+ for (; a; a=a->a_next) {
+ if ( !a->a_numvals ) {
+ Debug( pcache_debug, "%s: query not cacheable because of attrs without values in DN \"%s\" (%s)\n",
+ op->o_log_prefix, rs->sr_entry->e_name.bv_val,
+ a->a_desc->ad_cname.bv_val );
+ goto over;
+ }
+ }
+ }
+
+ if ( si->count < si->max ) {
+ si->count++;
+ e = entry_dup( rs->sr_entry );
+ if ( !si->head ) si->head = e;
+ if ( si->tail ) si->tail->e_private = e;
+ si->tail = e;
+
+ } else {
+over:;
+ si->over = 1;
+ si->count = 0;
+ for (;si->head; si->head=e) {
+ e = si->head->e_private;
+ si->head->e_private = NULL;
+ entry_free(si->head);
+ }
+ si->tail = NULL;
+ }
+ }
+ if ( si->slimit_exceeded ) {
+ return 0;
+ }
+ } else if ( rs->sr_type == REP_RESULT ) {
+
+ if ( si->count ) {
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ si->caching_reason = PC_POSITIVE;
+
+ } else if ( rs->sr_err == LDAP_SIZELIMIT_EXCEEDED
+ && si->qtemp->limitttl )
+ {
+ Entry *e;
+
+ si->caching_reason = PC_SIZELIMIT;
+ for (;si->head; si->head=e) {
+ e = si->head->e_private;
+ si->head->e_private = NULL;
+ entry_free(si->head);
+ }
+ }
+
+ } else if ( si->qtemp->negttl && !si->count && !si->over &&
+ rs->sr_err == LDAP_SUCCESS )
+ {
+ si->caching_reason = PC_NEGATIVE;
+ }
+
+
+ if ( si->slimit_exceeded ) {
+ rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+/* NOTE: this is a quick workaround to let pcache minimally interact
+ * with pagedResults. A more articulated solutions would be to
+ * perform the remote query without control and cache all results,
+ * performing the pagedResults search only within the client
+ * and the proxy. This requires pcache to understand pagedResults. */
+static int
+pcache_chk_controls(
+ Operation *op,
+ SlapReply *rs )
+{
+ const char *non = "";
+ const char *stripped = "";
+
+ switch( op->o_pagedresults ) {
+ case SLAP_CONTROL_NONCRITICAL:
+ non = "non-";
+ stripped = "; stripped";
+ /* fallthru */
+
+ case SLAP_CONTROL_CRITICAL:
+ Debug( pcache_debug, "%s: "
+ "%scritical pagedResults control "
+ "disabled with proxy cache%s.\n",
+ op->o_log_prefix, non, stripped );
+
+ slap_remove_control( op, rs, slap_cids.sc_pagedResults, NULL );
+ break;
+
+ default:
+ rs->sr_err = SLAP_CB_CONTINUE;
+ break;
+ }
+
+ return rs->sr_err;
+}
+
+static int
+pc_setpw( Operation *op, struct berval *pwd, cache_manager *cm )
+{
+ struct berval vals[2];
+
+ {
+ const char *text = NULL;
+ BER_BVZERO( &vals[0] );
+ slap_passwd_hash( pwd, &vals[0], &text );
+ if ( BER_BVISEMPTY( &vals[0] )) {
+ Debug( pcache_debug, "pc_setpw: hash failed %s\n",
+ text );
+ return LDAP_OTHER;
+ }
+ }
+
+ BER_BVZERO( &vals[1] );
+
+ {
+ Modifications mod;
+ SlapReply sr = { REP_RESULT };
+ slap_callback cb = { 0, slap_null_cb, 0, 0 };
+ int rc;
+
+ mod.sml_op = LDAP_MOD_REPLACE;
+ mod.sml_flags = 0;
+ mod.sml_desc = slap_schema.si_ad_userPassword;
+ mod.sml_type = mod.sml_desc->ad_cname;
+ mod.sml_values = vals;
+ mod.sml_nvalues = NULL;
+ mod.sml_numvals = 1;
+ mod.sml_next = NULL;
+
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->orm_modlist = &mod;
+ op->o_bd = &cm->db;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ op->o_callback = &cb;
+ Debug( pcache_debug, "pc_setpw: CACHING BIND for %s\n",
+ op->o_req_dn.bv_val );
+ rc = op->o_bd->be_modify( op, &sr );
+ ch_free( vals[0].bv_val );
+ return rc;
+ }
+}
+
+typedef struct bindcacheinfo {
+ slap_overinst *on;
+ CachedQuery *qc;
+} bindcacheinfo;
+
+static int
+pc_bind_save( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ bindcacheinfo *bci = op->o_callback->sc_private;
+ slap_overinst *on = bci->on;
+ cache_manager *cm = on->on_bi.bi_private;
+ CachedQuery *qc = bci->qc;
+ int delete = 0;
+
+ ldap_pvt_thread_rdwr_wlock( &qc->rwlock );
+ if ( qc->bind_refcnt-- ) {
+ Operation op2 = *op;
+ if ( pc_setpw( &op2, &op->orb_cred, cm ) == LDAP_SUCCESS )
+ bci->qc->bindref_time = op->o_time + bci->qc->qtemp->bindttr;
+ } else {
+ bci->qc = NULL;
+ delete = 1;
+ }
+ ldap_pvt_thread_rdwr_wunlock( &qc->rwlock );
+ if ( delete ) free_query(qc);
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static Filter *
+pc_bind_attrs( Operation *op, Entry *e, QueryTemplate *temp,
+ struct berval *fbv )
+{
+ int i, len = 0;
+ struct berval *vals, pres = BER_BVC("*");
+ char *p1, *p2;
+ Attribute *a;
+
+ vals = op->o_tmpalloc( temp->bindnattrs * sizeof( struct berval ),
+ op->o_tmpmemctx );
+
+ for ( i=0; i<temp->bindnattrs; i++ ) {
+ a = attr_find( e->e_attrs, temp->bindfattrs[i] );
+ if ( a && a->a_vals ) {
+ vals[i] = a->a_vals[0];
+ len += a->a_vals[0].bv_len;
+ } else {
+ vals[i] = pres;
+ }
+ }
+ fbv->bv_len = len + temp->bindftemp.bv_len;
+ fbv->bv_val = op->o_tmpalloc( fbv->bv_len + 1, op->o_tmpmemctx );
+
+ p1 = temp->bindftemp.bv_val;
+ p2 = fbv->bv_val;
+ i = 0;
+ while ( *p1 ) {
+ *p2++ = *p1;
+ if ( p1[0] == '=' && p1[1] == ')' ) {
+ AC_MEMCPY( p2, vals[i].bv_val, vals[i].bv_len );
+ p2 += vals[i].bv_len;
+ i++;
+ }
+ p1++;
+ }
+ *p2 = '\0';
+ op->o_tmpfree( vals, op->o_tmpmemctx );
+
+ /* FIXME: are we sure str2filter_x can't fail?
+ * caller needs to check */
+ {
+ Filter *f = str2filter_x( op, fbv->bv_val );
+ assert( f != NULL );
+ return f;
+ }
+}
+
+/* Check if the requested entry is from the cache and has a valid
+ * ttr and password hash
+ */
+static int
+pc_bind_search( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_SEARCH ) {
+ bindinfo *pbi = op->o_callback->sc_private;
+
+ /* We only care if this is an already cached result and we're
+ * below the refresh time, or we're offline.
+ */
+ if ( pbi->bi_cq ) {
+ if (( pbi->bi_cm->cc_paused & PCACHE_CC_OFFLINE ) ||
+ op->o_time < pbi->bi_cq->bindref_time ) {
+ Attribute *a;
+
+ /* See if a recognized password is hashed here */
+ a = attr_find( rs->sr_entry->e_attrs,
+ slap_schema.si_ad_userPassword );
+ if ( a && a->a_vals[0].bv_val[0] == '{' &&
+ lutil_passwd_scheme( a->a_vals[0].bv_val ))
+ pbi->bi_flags |= BI_HASHED;
+ } else {
+ Debug( pcache_debug, "pc_bind_search: cache is stale, "
+ "reftime: %ld, current time: %ld\n",
+ pbi->bi_cq->bindref_time, op->o_time );
+ }
+ } else if ( pbi->bi_si ) {
+ /* This search result is going into the cache */
+ struct berval fbv;
+ Filter *f;
+
+ filter_free( pbi->bi_si->query.filter );
+ f = pc_bind_attrs( op, rs->sr_entry, pbi->bi_templ, &fbv );
+ op->o_tmpfree( fbv.bv_val, op->o_tmpmemctx );
+ pbi->bi_si->query.filter = filter_dup( f, NULL );
+ filter_free_x( op, f, 1 );
+ }
+ }
+ return 0;
+}
+
+/* We always want pc_bind_search to run after the search handlers */
+static int
+pc_bind_resp( Operation *op, SlapReply *rs )
+{
+ bindinfo *pbi = op->o_callback->sc_private;
+ if ( !( pbi->bi_flags & BI_DIDCB )) {
+ slap_callback *sc = op->o_callback;
+ while ( sc && sc->sc_response != pcache_response )
+ sc = sc->sc_next;
+ if ( !sc )
+ sc = op->o_callback;
+ pbi->bi_cb.sc_next = sc->sc_next;
+ sc->sc_next = &pbi->bi_cb;
+ pbi->bi_flags |= BI_DIDCB;
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+#ifdef PCACHE_CONTROL_PRIVDB
+static int
+pcache_op_privdb(
+ Operation *op,
+ SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ cache_manager *cm = on->on_bi.bi_private;
+ slap_callback *save_cb;
+ slap_op_t type;
+
+ /* skip if control is unset */
+ if ( op->o_ctrlflag[ privDB_cid ] != SLAP_CONTROL_CRITICAL ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* The cache DB isn't open yet */
+ if ( cm->defer_db_open ) {
+ send_ldap_error( op, rs, LDAP_UNAVAILABLE,
+ "pcachePrivDB: cacheDB not available" );
+ return rs->sr_err;
+ }
+
+ /* FIXME: might be a little bit exaggerated... */
+ if ( !be_isroot( op ) ) {
+ save_cb = op->o_callback;
+ op->o_callback = NULL;
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "pcachePrivDB: operation not allowed" );
+ op->o_callback = save_cb;
+
+ return rs->sr_err;
+ }
+
+ /* map tag to operation */
+ type = slap_req2op( op->o_tag );
+ if ( type != SLAP_OP_LAST ) {
+ BackendInfo *bi = cm->db.bd_info;
+ int rc;
+
+ /* execute, if possible */
+ if ( (&bi->bi_op_bind)[ type ] ) {
+ Operation op2 = *op;
+
+ op2.o_bd = &cm->db;
+
+ rc = (&bi->bi_op_bind)[ type ]( &op2, rs );
+ if ( type == SLAP_OP_BIND && rc == LDAP_SUCCESS ) {
+ op->o_conn->c_authz_cookie = cm->db.be_private;
+ }
+
+ return rs->sr_err;
+ }
+ }
+
+ /* otherwise fall back to error */
+ save_cb = op->o_callback;
+ op->o_callback = NULL;
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "operation not supported with pcachePrivDB control" );
+ op->o_callback = save_cb;
+
+ return rs->sr_err;
+}
+#endif /* PCACHE_CONTROL_PRIVDB */
+
+static int
+pcache_op_bind(
+ Operation *op,
+ SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ cache_manager *cm = on->on_bi.bi_private;
+ QueryTemplate *temp;
+ Entry *e;
+ slap_callback cb = { 0 }, *sc;
+ bindinfo bi = { 0 };
+ bindcacheinfo *bci;
+ Operation op2;
+ int rc;
+
+#ifdef PCACHE_CONTROL_PRIVDB
+ if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL )
+ return pcache_op_privdb( op, rs );
+#endif /* PCACHE_CONTROL_PRIVDB */
+
+ /* Skip if we're not configured for Binds, or cache DB isn't open yet */
+ if ( !cm->cache_binds || cm->defer_db_open )
+ return SLAP_CB_CONTINUE;
+
+ /* First find a matching template with Bind info */
+ for ( temp=cm->qm->templates; temp; temp=temp->qmnext ) {
+ if ( temp->bindttr && dnIsSuffix( &op->o_req_ndn, &temp->bindbase ))
+ break;
+ }
+ /* Didn't find a suitable template, just passthru */
+ if ( !temp )
+ return SLAP_CB_CONTINUE;
+
+ /* See if the entry is already locally cached. If so, we can
+ * populate the query filter to retrieve the cached query. We
+ * need to check the bindrefresh time in the query.
+ */
+ op2 = *op;
+ op2.o_dn = op->o_bd->be_rootdn;
+ op2.o_ndn = op->o_bd->be_rootndn;
+
+ op2.o_bd = &cm->db;
+ e = NULL;
+ rc = be_entry_get_rw( &op2, &op->o_req_ndn, NULL, NULL, 0, &e );
+ if ( rc == LDAP_SUCCESS && e ) {
+ bi.bi_flags |= BI_LOOKUP;
+ op2.ors_filter = pc_bind_attrs( op, e, temp, &op2.ors_filterstr );
+ be_entry_release_r( &op2, e );
+ } else {
+ op2.ors_filter = temp->bindfilter;
+ op2.ors_filterstr = temp->bindfilterstr;
+ }
+
+ op2.o_bd = op->o_bd;
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2.ors_scope = LDAP_SCOPE_BASE;
+ op2.ors_deref = LDAP_DEREF_NEVER;
+ op2.ors_slimit = 1;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+ op2.ors_limit = NULL;
+ op2.ors_attrs = cm->qm->attr_sets[temp->attr_set_index].attrs;
+ op2.ors_attrsonly = 0;
+
+ /* We want to invoke search at the same level of the stack
+ * as we're already at...
+ */
+ bi.bi_cm = cm;
+ bi.bi_templ = temp;
+
+ bi.bi_cb.sc_response = pc_bind_search;
+ bi.bi_cb.sc_private = &bi;
+ cb.sc_private = &bi;
+ cb.sc_response = pc_bind_resp;
+ op2.o_callback = &cb;
+ overlay_op_walk( &op2, rs, op_search, on->on_info, on );
+
+ /* OK, just bind locally */
+ if ( bi.bi_flags & BI_HASHED ) {
+ int delete = 0;
+ BackendDB *be = op->o_bd;
+ op->o_bd = &cm->db;
+
+ Debug( pcache_debug, "pcache_op_bind: CACHED BIND for %s\n",
+ op->o_req_dn.bv_val );
+
+ if ( op->o_bd->be_bind( op, rs ) == LDAP_SUCCESS ) {
+ op->o_conn->c_authz_cookie = cm->db.be_private;
+ }
+ op->o_bd = be;
+ ldap_pvt_thread_rdwr_wlock( &bi.bi_cq->rwlock );
+ if ( !bi.bi_cq->bind_refcnt-- ) {
+ delete = 1;
+ }
+ ldap_pvt_thread_rdwr_wunlock( &bi.bi_cq->rwlock );
+ if ( delete ) free_query( bi.bi_cq );
+ return rs->sr_err;
+ }
+
+ /* We have a cached query to work with */
+ if ( bi.bi_cq ) {
+ sc = op->o_tmpalloc( sizeof(slap_callback) + sizeof(bindcacheinfo),
+ op->o_tmpmemctx );
+ sc->sc_response = pc_bind_save;
+ sc->sc_cleanup = NULL;
+ sc->sc_private = sc+1;
+ sc->sc_writewait = NULL;
+ bci = sc->sc_private;
+ sc->sc_next = op->o_callback;
+ op->o_callback = sc;
+ bci->on = on;
+ bci->qc = bi.bi_cq;
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static slap_response refresh_merge;
+
+static int
+pcache_op_search(
+ Operation *op,
+ SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ cache_manager *cm = on->on_bi.bi_private;
+ query_manager* qm = cm->qm;
+
+ int i = -1;
+
+ Query query;
+ QueryTemplate *qtemp = NULL;
+ bindinfo *pbi = NULL;
+
+ int attr_set = -1;
+ CachedQuery *answerable = NULL;
+ int cacheable = 0;
+
+ struct berval tempstr;
+
+#ifdef PCACHE_CONTROL_PRIVDB
+ if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL ) {
+ return pcache_op_privdb( op, rs );
+ }
+#endif /* PCACHE_CONTROL_PRIVDB */
+
+ /* The cache DB isn't open yet */
+ if ( cm->defer_db_open ) {
+ send_ldap_error( op, rs, LDAP_UNAVAILABLE,
+ "pcachePrivDB: cacheDB not available" );
+ return rs->sr_err;
+ }
+
+ /* pickup runtime ACL changes */
+ cm->db.be_acl = op->o_bd->be_acl;
+
+ {
+ /* See if we're processing a Bind request
+ * or a cache refresh */
+ slap_callback *cb = op->o_callback;
+
+ for ( ; cb; cb=cb->sc_next ) {
+ if ( cb->sc_response == pc_bind_resp ) {
+ pbi = cb->sc_private;
+ break;
+ }
+ if ( cb->sc_response == refresh_merge ) {
+ /* This is a refresh, do not search the cache */
+ return SLAP_CB_CONTINUE;
+ }
+ }
+ }
+
+ /* FIXME: cannot cache/answer requests with pagedResults control */
+
+ query.filter = op->ors_filter;
+
+ if ( pbi ) {
+ query.base = pbi->bi_templ->bindbase;
+ query.scope = pbi->bi_templ->bindscope;
+ attr_set = pbi->bi_templ->attr_set_index;
+ cacheable = 1;
+ qtemp = pbi->bi_templ;
+ if ( pbi->bi_flags & BI_LOOKUP )
+ answerable = qm->qcfunc(op, qm, &query, qtemp);
+
+ } else {
+ tempstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len+1,
+ op->o_tmpmemctx );
+ tempstr.bv_len = 0;
+ if ( filter2template( op, op->ors_filter, &tempstr ))
+ {
+ op->o_tmpfree( tempstr.bv_val, op->o_tmpmemctx );
+ return SLAP_CB_CONTINUE;
+ }
+
+ Debug( pcache_debug, "query template of incoming query = %s\n",
+ tempstr.bv_val );
+
+ /* find attr set */
+ attr_set = get_attr_set(op->ors_attrs, qm, cm->numattrsets);
+
+ query.base = op->o_req_ndn;
+ query.scope = op->ors_scope;
+
+ /* check for query containment */
+ if (attr_set > -1) {
+ QueryTemplate *qt = qm->attr_sets[attr_set].templates;
+ for (; qt; qt = qt->qtnext ) {
+ /* find if template i can potentially answer tempstr */
+ if ( ber_bvstrcasecmp( &qt->querystr, &tempstr ) != 0 )
+ continue;
+ cacheable = 1;
+ qtemp = qt;
+ Debug( pcache_debug, "Entering QC, querystr = %s\n",
+ op->ors_filterstr.bv_val );
+ answerable = qm->qcfunc(op, qm, &query, qt);
+
+ /* if != NULL, rlocks qtemp->t_rwlock */
+ if (answerable)
+ break;
+ }
+ }
+ op->o_tmpfree( tempstr.bv_val, op->o_tmpmemctx );
+ }
+
+ if (answerable) {
+ BackendDB *save_bd = op->o_bd;
+
+ ldap_pvt_thread_mutex_lock( &answerable->answerable_cnt_mutex );
+ answerable->answerable_cnt++;
+ /* we only care about refcnts if we're refreshing */
+ if ( answerable->refresh_time )
+ answerable->refcnt++;
+ Debug( pcache_debug, "QUERY ANSWERABLE (answered %lu times)\n",
+ answerable->answerable_cnt );
+ ldap_pvt_thread_mutex_unlock( &answerable->answerable_cnt_mutex );
+
+ ldap_pvt_thread_rdwr_wlock(&answerable->rwlock);
+ if ( BER_BVISNULL( &answerable->q_uuid )) {
+ /* No entries cached, just an empty result set */
+ i = rs->sr_err = 0;
+ send_ldap_result( op, rs );
+ } else {
+ /* Let Bind know we used a cached query */
+ if ( pbi ) {
+ answerable->bind_refcnt++;
+ pbi->bi_cq = answerable;
+ }
+
+ op->o_bd = &cm->db;
+ if ( cm->response_cb == PCACHE_RESPONSE_CB_TAIL ) {
+ slap_callback cb;
+ /* The cached entry was already processed by any
+ * other overlays, so don't let it get processed again.
+ *
+ * This loop removes over_back_response from the stack.
+ */
+ if ( overlay_callback_after_backover( op, &cb, 0) == 0 ) {
+ slap_callback **scp;
+ for ( scp = &op->o_callback; *scp != NULL;
+ scp = &(*scp)->sc_next ) {
+ if ( (*scp)->sc_next == &cb ) {
+ *scp = cb.sc_next;
+ break;
+ }
+ }
+ }
+ }
+ i = cm->db.bd_info->bi_op_search( op, rs );
+ }
+ ldap_pvt_thread_rdwr_wunlock(&answerable->rwlock);
+ /* locked by qtemp->qcfunc (query_containment) */
+ ldap_pvt_thread_rdwr_runlock(&qtemp->t_rwlock);
+ op->o_bd = save_bd;
+ return i;
+ }
+
+ Debug( pcache_debug, "QUERY NOT ANSWERABLE\n" );
+
+ ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
+ if (cm->num_cached_queries >= cm->max_queries) {
+ cacheable = 0;
+ }
+ ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
+
+ if (op->ors_attrsonly)
+ cacheable = 0;
+
+ if (cacheable) {
+ slap_callback *cb;
+ struct search_info *si;
+
+ Debug( pcache_debug, "QUERY CACHEABLE\n" );
+ query.filter = filter_dup(op->ors_filter, NULL);
+
+ cb = op->o_tmpalloc( sizeof(*cb) + sizeof(*si), op->o_tmpmemctx );
+ cb->sc_response = pcache_response;
+ cb->sc_cleanup = pcache_op_cleanup;
+ cb->sc_private = (cb+1);
+ cb->sc_writewait = 0;
+ si = cb->sc_private;
+ si->on = on;
+ si->query = query;
+ si->qtemp = qtemp;
+ si->max = cm->num_entries_limit ;
+ si->over = 0;
+ si->count = 0;
+ si->slimit = 0;
+ si->slimit_exceeded = 0;
+ si->caching_reason = PC_IGNORE;
+ if ( op->ors_slimit > 0 && op->ors_slimit < cm->num_entries_limit ) {
+ si->slimit = op->ors_slimit;
+ op->ors_slimit = cm->num_entries_limit;
+ }
+ si->head = NULL;
+ si->tail = NULL;
+ si->swap_saved_attrs = 1;
+ si->save_attrs = op->ors_attrs;
+ si->pbi = pbi;
+ if ( pbi )
+ pbi->bi_si = si;
+
+ op->ors_attrs = qtemp->t_attrs.attrs;
+
+ if ( cm->response_cb == PCACHE_RESPONSE_CB_HEAD ) {
+ cb->sc_next = op->o_callback;
+ op->o_callback = cb;
+
+ } else {
+ slap_callback **pcb;
+
+ /* need to move the callback at the end, in case other
+ * overlays are present, so that the final entry is
+ * actually cached */
+ cb->sc_next = NULL;
+ for ( pcb = &op->o_callback; *pcb; pcb = &(*pcb)->sc_next );
+ *pcb = cb;
+ }
+
+ } else {
+ Debug( pcache_debug, "QUERY NOT CACHEABLE\n" );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+get_attr_set(
+ AttributeName* attrs,
+ query_manager* qm,
+ int num )
+{
+ int i = 0;
+ int count = 0;
+
+ if ( attrs ) {
+ for ( ; attrs[i].an_name.bv_val; i++ ) {
+ /* only count valid attribute names
+ * (searches ignore others, this overlay does the same) */
+ if ( attrs[i].an_desc ) {
+ count++;
+ }
+ }
+ }
+
+ /* recognize default or explicit single "*" */
+ if ( ! attrs ||
+ ( i == 1 && bvmatch( &attrs[0].an_name, slap_bv_all_user_attrs ) ) )
+ {
+ count = 1;
+ attrs = slap_anlist_all_user_attributes;
+
+ /* recognize implicit (no valid attributes) or explicit single "1.1" */
+ } else if ( count == 0 ||
+ ( i == 1 && bvmatch( &attrs[0].an_name, slap_bv_no_attrs ) ) )
+ {
+ count = 0;
+ attrs = NULL;
+ }
+
+ for ( i = 0; i < num; i++ ) {
+ AttributeName *a2;
+ int found = 1;
+
+ if ( count > qm->attr_sets[i].count ) {
+ if ( qm->attr_sets[i].count &&
+ bvmatch( &qm->attr_sets[i].attrs[0].an_name, slap_bv_all_user_attrs )) {
+ break;
+ }
+ continue;
+ }
+
+ if ( !count ) {
+ if ( !qm->attr_sets[i].count ) {
+ break;
+ }
+ continue;
+ }
+
+ for ( a2 = attrs; a2->an_name.bv_val; a2++ ) {
+ if ( !a2->an_desc && !bvmatch( &a2->an_name, slap_bv_all_user_attrs ) ) continue;
+
+ if ( !an_find( qm->attr_sets[i].attrs, &a2->an_name ) ) {
+ found = 0;
+ break;
+ }
+ }
+
+ if ( found ) {
+ break;
+ }
+ }
+
+ if ( i == num ) {
+ i = -1;
+ }
+
+ return i;
+}
+
+/* Refresh a cached query:
+ * 1: Replay the query on the remote DB and merge each entry into
+ * the local DB. Remember the DNs of each remote entry.
+ * 2: Search the local DB for all entries matching this queryID.
+ * Delete any entry whose DN is not in the list from (1).
+ */
+typedef struct dnlist {
+ struct dnlist *next;
+ struct berval dn;
+ char delete;
+} dnlist;
+
+typedef struct refresh_info {
+ dnlist *ri_dns;
+ dnlist *ri_tail;
+ dnlist *ri_dels;
+ BackendDB *ri_be;
+ CachedQuery *ri_q;
+} refresh_info;
+
+static dnlist *dnl_alloc( Operation *op, struct berval *bvdn )
+{
+ dnlist *dn = op->o_tmpalloc( sizeof(dnlist) + bvdn->bv_len + 1,
+ op->o_tmpmemctx );
+ dn->dn.bv_len = bvdn->bv_len;
+ dn->dn.bv_val = (char *)(dn+1);
+ AC_MEMCPY( dn->dn.bv_val, bvdn->bv_val, dn->dn.bv_len );
+ dn->dn.bv_val[dn->dn.bv_len] = '\0';
+ return dn;
+}
+
+static int
+refresh_merge( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_SEARCH ) {
+ refresh_info *ri = op->o_callback->sc_private;
+ Entry *e;
+ dnlist *dnl;
+ slap_callback *ocb;
+ int rc;
+
+ ocb = op->o_callback;
+ /* Find local entry, merge */
+ op->o_bd = ri->ri_be;
+ rc = be_entry_get_rw( op, &rs->sr_entry->e_nname, NULL, NULL, 0, &e );
+ if ( rc != LDAP_SUCCESS || e == NULL ) {
+ /* No local entry, just add it. FIXME: we are not checking
+ * the cache entry limit here
+ */
+ merge_entry( op, rs->sr_entry, 1, &ri->ri_q->q_uuid );
+ } else {
+ /* Entry exists, update it */
+ Entry ne;
+ Attribute *a, **b;
+ Modifications *modlist, *mods = NULL;
+ const char* text = NULL;
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof(textbuf);
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+
+ ne = *e;
+ b = &ne.e_attrs;
+ /* Get a copy of only the attrs we requested */
+ for ( a=e->e_attrs; a; a=a->a_next ) {
+ if ( ad_inlist( a->a_desc, rs->sr_attrs )) {
+ *b = attr_alloc( a->a_desc );
+ *(*b) = *a;
+ /* The actual values still belong to e */
+ (*b)->a_flags |= SLAP_ATTR_DONT_FREE_VALS |
+ SLAP_ATTR_DONT_FREE_DATA;
+ b = &((*b)->a_next);
+ }
+ }
+ *b = NULL;
+ slap_entry2mods( rs->sr_entry, &modlist, &text, textbuf, textlen );
+ syncrepl_diff_entry( op, ne.e_attrs, rs->sr_entry->e_attrs,
+ &mods, &modlist, 0 );
+ be_entry_release_r( op, e );
+ attrs_free( ne.e_attrs );
+ slap_mods_free( modlist, 1 );
+ /* mods is NULL if there are no changes */
+ if ( mods ) {
+ SlapReply rs2 = { REP_RESULT };
+ struct berval dn = op->o_req_dn;
+ struct berval ndn = op->o_req_ndn;
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->orm_modlist = mods;
+ op->o_req_dn = rs->sr_entry->e_name;
+ op->o_req_ndn = rs->sr_entry->e_nname;
+ op->o_callback = &cb;
+ op->o_bd->be_modify( op, &rs2 );
+ rs->sr_err = rs2.sr_err;
+ rs_assert_done( &rs2 );
+ slap_mods_free( mods, 1 );
+ op->o_req_dn = dn;
+ op->o_req_ndn = ndn;
+ }
+ }
+
+ /* Add DN to list */
+ dnl = dnl_alloc( op, &rs->sr_entry->e_nname );
+ dnl->next = NULL;
+ if ( ri->ri_tail ) {
+ ri->ri_tail->next = dnl;
+ } else {
+ ri->ri_dns = dnl;
+ }
+ ri->ri_tail = dnl;
+ op->o_callback = ocb;
+ }
+ return 0;
+}
+
+static int
+refresh_purge( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_SEARCH ) {
+ refresh_info *ri = op->o_callback->sc_private;
+ dnlist **dn;
+ int del = 1;
+
+ /* Did the entry exist on the remote? */
+ for ( dn=&ri->ri_dns; *dn; dn = &(*dn)->next ) {
+ if ( dn_match( &(*dn)->dn, &rs->sr_entry->e_nname )) {
+ dnlist *dnext = (*dn)->next;
+ op->o_tmpfree( *dn, op->o_tmpmemctx );
+ *dn = dnext;
+ del = 0;
+ break;
+ }
+ }
+ /* No, so put it on the list to delete */
+ if ( del ) {
+ Attribute *a;
+ dnlist *dnl = dnl_alloc( op, &rs->sr_entry->e_nname );
+ dnl->next = ri->ri_dels;
+ ri->ri_dels = dnl;
+ a = attr_find( rs->sr_entry->e_attrs, ad_queryId );
+ /* If ours is the only queryId, delete entry */
+ dnl->delete = ( a->a_numvals == 1 );
+ }
+ }
+ return 0;
+}
+
+static int
+refresh_query( Operation *op, CachedQuery *query, slap_overinst *on )
+{
+ SlapReply rs = {REP_RESULT};
+ slap_callback cb = { 0 };
+ refresh_info ri = { 0 };
+ char filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(pcacheQueryID=)" ) ];
+ AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
+ Filter filter = {LDAP_FILTER_EQUALITY};
+ AttributeName attrs[ 2 ] = {{{ 0 }}};
+ dnlist *dn;
+ int rc;
+
+ ldap_pvt_thread_mutex_lock( &query->answerable_cnt_mutex );
+ query->refcnt = 0;
+ ldap_pvt_thread_mutex_unlock( &query->answerable_cnt_mutex );
+
+ cb.sc_response = refresh_merge;
+ cb.sc_private = &ri;
+
+ /* cache DB */
+ ri.ri_be = op->o_bd;
+ ri.ri_q = query;
+
+ op->o_tag = LDAP_REQ_SEARCH;
+ op->o_protocol = LDAP_VERSION3;
+ op->o_callback = &cb;
+ op->o_do_not_cache = 1;
+
+ op->o_req_dn = query->qbase->base;
+ op->o_req_ndn = query->qbase->base;
+ op->ors_scope = query->scope;
+ op->ors_deref = LDAP_DEREF_NEVER;
+ op->ors_slimit = SLAP_NO_LIMIT;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_limit = NULL;
+ op->ors_filter = query->filter;
+ filter2bv_x( op, query->filter, &op->ors_filterstr );
+ op->ors_attrs = query->qtemp->t_attrs.attrs;
+ op->ors_attrsonly = 0;
+
+ op->o_bd = on->on_info->oi_origdb;
+ rc = op->o_bd->be_search( op, &rs );
+ if ( rc ) {
+ op->o_bd = ri.ri_be;
+ goto leave;
+ }
+
+ /* Get the DNs of all entries matching this query */
+ cb.sc_response = refresh_purge;
+
+ op->o_bd = ri.ri_be;
+ op->o_req_dn = op->o_bd->be_suffix[0];
+ op->o_req_ndn = op->o_bd->be_nsuffix[0];
+ op->ors_scope = LDAP_SCOPE_SUBTREE;
+ op->ors_deref = LDAP_DEREF_NEVER;
+ op->ors_filterstr.bv_len = snprintf(filter_str, sizeof(filter_str),
+ "(%s=%s)", ad_queryId->ad_cname.bv_val, query->q_uuid.bv_val);
+ filter.f_ava = &ava;
+ filter.f_av_desc = ad_queryId;
+ filter.f_av_value = query->q_uuid;
+ attrs[ 0 ].an_desc = ad_queryId;
+ attrs[ 0 ].an_name = ad_queryId->ad_cname;
+ op->ors_attrs = attrs;
+ op->ors_attrsonly = 0;
+ rs_reinit( &rs, REP_RESULT );
+ rc = op->o_bd->be_search( op, &rs );
+ if ( rc ) goto leave;
+
+ while (( dn = ri.ri_dels )) {
+ op->o_req_dn = dn->dn;
+ op->o_req_ndn = dn->dn;
+ rs_reinit( &rs, REP_RESULT );
+ if ( dn->delete ) {
+ op->o_tag = LDAP_REQ_DELETE;
+ op->o_bd->be_delete( op, &rs );
+ } else {
+ Modifications mod;
+ struct berval vals[2];
+
+ vals[0] = query->q_uuid;
+ BER_BVZERO( &vals[1] );
+ mod.sml_op = LDAP_MOD_DELETE;
+ mod.sml_flags = 0;
+ mod.sml_desc = ad_queryId;
+ mod.sml_type = ad_queryId->ad_cname;
+ mod.sml_values = vals;
+ mod.sml_nvalues = NULL;
+ mod.sml_numvals = 1;
+ mod.sml_next = NULL;
+
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->orm_modlist = &mod;
+ op->o_bd->be_modify( op, &rs );
+ }
+ ri.ri_dels = dn->next;
+ op->o_tmpfree( dn, op->o_tmpmemctx );
+ }
+
+leave:
+ /* reset our local heap, we're done with it */
+ slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, op->o_threadctx, 1 );
+ return rc;
+}
+
+static void*
+consistency_check(
+ void *ctx,
+ void *arg )
+{
+ struct re_s *rtask = arg;
+ slap_overinst *on = rtask->arg;
+ cache_manager *cm = on->on_bi.bi_private;
+ query_manager *qm = cm->qm;
+ Connection conn = {0};
+ OperationBuffer opbuf;
+ Operation *op;
+
+ CachedQuery *query, *qprev;
+ CachedQuery *expires = NULL;
+ int return_val, pause = PCACHE_CC_PAUSED;
+ QueryTemplate *templ;
+
+ /* Don't expire anything when we're offline */
+ if ( cm->cc_paused & PCACHE_CC_OFFLINE ) {
+ pause = PCACHE_CC_OFFLINE;
+ goto leave;
+ }
+
+ connection_fake_init( &conn, &opbuf, ctx );
+ op = &opbuf.ob_op;
+
+ op->o_bd = &cm->db;
+ op->o_dn = cm->db.be_rootdn;
+ op->o_ndn = cm->db.be_rootndn;
+
+ cm->cc_arg = arg;
+
+ for (templ = qm->templates; templ; templ=templ->qmnext) {
+ time_t ttl;
+ if ( !templ->query_last ) continue;
+ pause = 0;
+ op->o_time = slap_get_time();
+ if ( !templ->ttr ) {
+ ttl = templ->ttl;
+ if ( templ->negttl && templ->negttl < ttl )
+ ttl = templ->negttl;
+ if ( templ->limitttl && templ->limitttl < ttl )
+ ttl = templ->limitttl;
+ /* The oldest timestamp that needs expiration checking */
+ ttl += op->o_time;
+ }
+
+ Debug( pcache_debug, "Lock CR index = %p\n",
+ (void *) templ );
+ ldap_pvt_thread_rdwr_wlock(&templ->t_rwlock);
+ for ( query=templ->query_last; query; query=qprev ) {
+ qprev = query->prev;
+ if ( query->refresh_time && query->refresh_time < op->o_time ) {
+ /* A refresh will extend the expiry if the query has been
+ * referenced, but not if it's unreferenced. If the
+ * expiration has been hit, then skip the refresh since
+ * we're just going to discard the result anyway.
+ */
+ if ( query->refcnt )
+ query->expiry_time = op->o_time + templ->ttl;
+ if ( query->expiry_time > op->o_time ) {
+ /* perform actual refresh below */
+ continue;
+ }
+ }
+
+ if (query->expiry_time < op->o_time) {
+ int rem = 0;
+ if ( query != templ->query_last )
+ continue;
+ ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
+ if (query->in_lru) {
+ remove_query(qm, query);
+ rem = 1;
+ }
+ ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
+ if (!rem)
+ continue;
+ remove_from_template(query, templ);
+ Debug( pcache_debug, "TEMPLATE %p QUERIES-- %d\n",
+ (void *) templ, templ->no_of_queries );
+ query->prev = expires;
+ expires = query;
+ query->qtemp = NULL;
+ } else if ( !templ->ttr && query->expiry_time > ttl ) {
+ /* We don't need to check for refreshes, and this
+ * query's expiry is too new, and all subsequent queries
+ * will be newer yet. So stop looking.
+ *
+ * If we have refreshes, then we always have to walk the
+ * entire query list.
+ */
+ break;
+ }
+ }
+ Debug( pcache_debug, "Unlock CR index = %p\n",
+ (void *) templ );
+ ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock);
+ for ( query=expires; query; query=qprev ) {
+ int rem;
+ qprev = query->prev;
+ if ( BER_BVISNULL( &query->q_uuid ))
+ return_val = 0;
+ else
+ return_val = remove_query_data(op, &query->q_uuid);
+ Debug( pcache_debug, "STALE QUERY REMOVED, SIZE=%d\n",
+ return_val );
+ ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
+ cm->cur_entries -= return_val;
+ cm->num_cached_queries--;
+ Debug( pcache_debug, "STORED QUERIES = %lu\n",
+ cm->num_cached_queries );
+ ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
+ Debug( pcache_debug,
+ "STALE QUERY REMOVED, CACHE ="
+ "%d entries\n",
+ cm->cur_entries );
+ ldap_pvt_thread_rdwr_wlock( &query->rwlock );
+ if ( query->bind_refcnt-- ) {
+ rem = 0;
+ } else {
+ rem = 1;
+ }
+ ldap_pvt_thread_rdwr_wunlock( &query->rwlock );
+ if ( rem ) free_query(query);
+ }
+
+ /* handle refreshes that we skipped earlier */
+ if ( templ->ttr ) {
+ ldap_pvt_thread_rdwr_rlock(&templ->t_rwlock);
+ for ( query=templ->query_last; query; query=qprev ) {
+ qprev = query->prev;
+ if ( query->refresh_time && query->refresh_time < op->o_time ) {
+ /* A refresh will extend the expiry if the query has been
+ * referenced, but not if it's unreferenced. If the
+ * expiration has been hit, then skip the refresh since
+ * we're just going to discard the result anyway.
+ */
+ if ( query->expiry_time > op->o_time ) {
+ refresh_query( op, query, on );
+ query->refresh_time = op->o_time + templ->ttr;
+ }
+ }
+ }
+ ldap_pvt_thread_rdwr_runlock(&templ->t_rwlock);
+ }
+ }
+
+leave:
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
+ ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
+ }
+ /* If there were no queries, defer processing for a while */
+ if ( cm->cc_paused != pause )
+ cm->cc_paused = pause;
+ ldap_pvt_runqueue_resched( &slapd_rq, rtask, pause );
+
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ return NULL;
+}
+
+
+#define MAX_ATTR_SETS 500
+
+enum {
+ PC_MAIN = 1,
+ PC_ATTR,
+ PC_TEMP,
+ PC_RESP,
+ PC_QUERIES,
+ PC_OFFLINE,
+ PC_BIND,
+ PC_PRIVATE_DB
+};
+
+static ConfigDriver pc_cf_gen;
+static ConfigLDAPadd pc_ldadd;
+static ConfigCfAdd pc_cfadd;
+
+static ConfigTable pccfg[] = {
+ { "pcache", "backend> <max_entries> <numattrsets> <entry limit> "
+ "<cycle_time",
+ 6, 6, 0, ARG_MAGIC|ARG_NO_DELETE|PC_MAIN, pc_cf_gen,
+ "( OLcfgOvAt:2.1 NAME ( 'olcPcache' 'olcProxyCache' ) "
+ "DESC 'Proxy Cache basic parameters' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "pcacheAttrset", "index> <attributes...",
+ 2, 0, 0, ARG_MAGIC|PC_ATTR, pc_cf_gen,
+ "( OLcfgOvAt:2.2 NAME ( 'olcPcacheAttrset' 'olcProxyAttrset' ) "
+ "DESC 'A set of attributes to cache' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "pcacheTemplate", "filter> <attrset-index> <TTL> <negTTL> "
+ "<limitTTL> <TTR",
+ 4, 7, 0, ARG_MAGIC|PC_TEMP, pc_cf_gen,
+ "( OLcfgOvAt:2.3 NAME ( 'olcPcacheTemplate' 'olcProxyCacheTemplate' ) "
+ "DESC 'Filter template, attrset, cache TTL, "
+ "optional negative TTL, optional sizelimit TTL, "
+ "optional TTR' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "pcachePosition", "head|tail(default)",
+ 2, 2, 0, ARG_MAGIC|PC_RESP, pc_cf_gen,
+ "( OLcfgOvAt:2.4 NAME 'olcPcachePosition' "
+ "DESC 'Response callback position in overlay stack' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "pcacheMaxQueries", "queries",
+ 2, 2, 0, ARG_INT|ARG_MAGIC|PC_QUERIES, pc_cf_gen,
+ "( OLcfgOvAt:2.5 NAME ( 'olcPcacheMaxQueries' 'olcProxyCacheQueries' ) "
+ "DESC 'Maximum number of queries to cache' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "pcachePersist", "TRUE|FALSE",
+ 2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, save_queries),
+ "( OLcfgOvAt:2.6 NAME ( 'olcPcachePersist' 'olcProxySaveQueries' ) "
+ "DESC 'Save cached queries for hot restart' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "pcacheValidate", "TRUE|FALSE",
+ 2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, check_cacheability),
+ "( OLcfgOvAt:2.7 NAME ( 'olcPcacheValidate' 'olcProxyCheckCacheability' ) "
+ "DESC 'Check whether the results of a query are cacheable, e.g. for schema issues' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "pcacheOffline", "TRUE|FALSE",
+ 2, 2, 0, ARG_ON_OFF|ARG_MAGIC|PC_OFFLINE, pc_cf_gen,
+ "( OLcfgOvAt:2.8 NAME 'olcPcacheOffline' "
+ "DESC 'Set cache to offline mode and disable expiration' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "pcacheBind", "filter> <attrset-index> <TTR> <scope> <base",
+ 6, 6, 0, ARG_MAGIC|PC_BIND, pc_cf_gen,
+ "( OLcfgOvAt:2.9 NAME 'olcPcacheBind' "
+ "DESC 'Parameters for caching Binds' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "pcache-", "private database args",
+ 1, 0, STRLENOF("pcache-"), ARG_MAGIC|PC_PRIVATE_DB, pc_cf_gen,
+ NULL, NULL, NULL },
+
+ /* Legacy keywords */
+ { "proxycache", "backend> <max_entries> <numattrsets> <entry limit> "
+ "<cycle_time",
+ 6, 6, 0, ARG_MAGIC|ARG_NO_DELETE|PC_MAIN, pc_cf_gen,
+ NULL, NULL, NULL },
+ { "proxyattrset", "index> <attributes...",
+ 2, 0, 0, ARG_MAGIC|PC_ATTR, pc_cf_gen,
+ NULL, NULL, NULL },
+ { "proxytemplate", "filter> <attrset-index> <TTL> <negTTL",
+ 4, 7, 0, ARG_MAGIC|PC_TEMP, pc_cf_gen,
+ NULL, NULL, NULL },
+ { "response-callback", "head|tail(default)",
+ 2, 2, 0, ARG_MAGIC|PC_RESP, pc_cf_gen,
+ NULL, NULL, NULL },
+ { "proxyCacheQueries", "queries",
+ 2, 2, 0, ARG_INT|ARG_MAGIC|PC_QUERIES, pc_cf_gen,
+ NULL, NULL, NULL },
+ { "proxySaveQueries", "TRUE|FALSE",
+ 2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, save_queries),
+ NULL, NULL, NULL },
+ { "proxyCheckCacheability", "TRUE|FALSE",
+ 2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, check_cacheability),
+ NULL, NULL, NULL },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs pcocs[] = {
+ { "( OLcfgOvOc:2.1 "
+ "NAME 'olcPcacheConfig' "
+ "DESC 'ProxyCache configuration' "
+ "SUP olcOverlayConfig "
+ "MUST ( olcPcache $ olcPcacheAttrset $ olcPcacheTemplate ) "
+ "MAY ( olcPcachePosition $ olcPcacheMaxQueries $ olcPcachePersist $ "
+ "olcPcacheValidate $ olcPcacheOffline $ olcPcacheBind ) )",
+ Cft_Overlay, pccfg, NULL, pc_cfadd },
+ { "( OLcfgOvOc:2.2 "
+ "NAME 'olcPcacheDatabase' "
+ "DESC 'Cache database configuration' "
+ /* co_table is initialized in pcache_initialize */
+ "AUXILIARY )", Cft_Misc, NULL, pc_ldadd },
+ { NULL, 0, NULL }
+};
+
+static int pcache_db_open2( slap_overinst *on, ConfigReply *cr );
+
+static int
+pc_ldadd_cleanup( ConfigArgs *c )
+{
+ slap_overinst *on = c->ca_private;
+ return pcache_db_open2( on, &c->reply );
+}
+
+static int
+pc_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
+{
+ slap_overinst *on;
+ cache_manager *cm;
+
+ if ( p->ce_type != Cft_Overlay || !p->ce_bi ||
+ p->ce_bi->bi_cf_ocs != pcocs )
+ return LDAP_CONSTRAINT_VIOLATION;
+
+ on = (slap_overinst *)p->ce_bi;
+ cm = on->on_bi.bi_private;
+ ca->be = &cm->db;
+ /* Defer open if this is an LDAPadd */
+ if ( CONFIG_ONLINE_ADD( ca ))
+ config_push_cleanup( ca, pc_ldadd_cleanup );
+ else
+ cm->defer_db_open = 0;
+ ca->ca_private = on;
+ return LDAP_SUCCESS;
+}
+
+static int
+pc_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
+{
+ CfEntryInfo *pe = p->e_private;
+ slap_overinst *on = (slap_overinst *)pe->ce_bi;
+ cache_manager *cm = on->on_bi.bi_private;
+ struct berval bv;
+
+ /* FIXME: should not hardcode "olcDatabase" here */
+ bv.bv_len = snprintf( ca->cr_msg, sizeof( ca->cr_msg ),
+ "olcDatabase=" SLAP_X_ORDERED_FMT "%s",
+ 0, cm->db.bd_info->bi_type );
+ if ( bv.bv_len >= sizeof( ca->cr_msg ) ) {
+ return -1;
+ }
+ bv.bv_val = ca->cr_msg;
+ ca->be = &cm->db;
+ cm->defer_db_open = 0;
+
+ /* We can only create this entry if the database is table-driven
+ */
+ if ( cm->db.bd_info->bi_cf_ocs )
+ config_build_entry( op, rs, pe, ca, &bv, cm->db.bd_info->bi_cf_ocs,
+ &pcocs[1] );
+
+ return 0;
+}
+
+static int
+pc_cf_gen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ cache_manager* cm = on->on_bi.bi_private;
+ query_manager* qm = cm->qm;
+ QueryTemplate* temp;
+ AttributeName* attr_name;
+ AttributeName* attrarray;
+ const char* text=NULL;
+ int i, num, rc = 0;
+ char *ptr;
+ unsigned long t;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ struct berval bv;
+ switch( c->type ) {
+ case PC_MAIN:
+ bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s %d %d %d %ld",
+ cm->db.bd_info->bi_type, cm->max_entries, cm->numattrsets,
+ cm->num_entries_limit, cm->cc_period );
+ bv.bv_val = c->cr_msg;
+ value_add_one( &c->rvalue_vals, &bv );
+ break;
+ case PC_ATTR:
+ for (i=0; i<cm->numattrsets; i++) {
+ if ( !qm->attr_sets[i].count ) continue;
+
+ bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ), "%d", i );
+
+ /* count the attr length */
+ for ( attr_name = qm->attr_sets[i].attrs;
+ attr_name->an_name.bv_val; attr_name++ )
+ {
+ bv.bv_len += attr_name->an_name.bv_len + 1;
+ if ( attr_name->an_desc &&
+ ( attr_name->an_desc->ad_flags & SLAP_DESC_TEMPORARY ) ) {
+ bv.bv_len += STRLENOF("undef:");
+ }
+ }
+
+ bv.bv_val = ch_malloc( bv.bv_len+1 );
+ ptr = lutil_strcopy( bv.bv_val, c->cr_msg );
+ for ( attr_name = qm->attr_sets[i].attrs;
+ attr_name->an_name.bv_val; attr_name++ ) {
+ *ptr++ = ' ';
+ if ( attr_name->an_desc &&
+ ( attr_name->an_desc->ad_flags & SLAP_DESC_TEMPORARY ) ) {
+ ptr = lutil_strcopy( ptr, "undef:" );
+ }
+ ptr = lutil_strcopy( ptr, attr_name->an_name.bv_val );
+ }
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ }
+ if ( !c->rvalue_vals )
+ rc = 1;
+ break;
+ case PC_TEMP:
+ for (temp=qm->templates; temp; temp=temp->qmnext) {
+ /* HEADS-UP: always print all;
+ * if optional == 0, ignore */
+ bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ " %d %ld %ld %ld %ld",
+ temp->attr_set_index,
+ temp->ttl,
+ temp->negttl,
+ temp->limitttl,
+ temp->ttr );
+ bv.bv_len += temp->querystr.bv_len + 2;
+ bv.bv_val = ch_malloc( bv.bv_len+1 );
+ ptr = bv.bv_val;
+ *ptr++ = '"';
+ ptr = lutil_strcopy( ptr, temp->querystr.bv_val );
+ *ptr++ = '"';
+ strcpy( ptr, c->cr_msg );
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ }
+ if ( !c->rvalue_vals )
+ rc = 1;
+ break;
+ case PC_BIND:
+ for (temp=qm->templates; temp; temp=temp->qmnext) {
+ if ( !temp->bindttr ) continue;
+ bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ " %d %ld %s ",
+ temp->attr_set_index,
+ temp->bindttr,
+ ldap_pvt_scope2str( temp->bindscope ));
+ bv.bv_len += temp->bindbase.bv_len + temp->bindftemp.bv_len + 4;
+ bv.bv_val = ch_malloc( bv.bv_len + 1 );
+ ptr = bv.bv_val;
+ *ptr++ = '"';
+ ptr = lutil_strcopy( ptr, temp->bindftemp.bv_val );
+ *ptr++ = '"';
+ ptr = lutil_strcopy( ptr, c->cr_msg );
+ *ptr++ = '"';
+ ptr = lutil_strcopy( ptr, temp->bindbase.bv_val );
+ *ptr++ = '"';
+ *ptr = '\0';
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ }
+ if ( !c->rvalue_vals )
+ rc = 1;
+ break;
+ case PC_RESP:
+ if ( cm->response_cb == PCACHE_RESPONSE_CB_HEAD ) {
+ BER_BVSTR( &bv, "head" );
+ } else {
+ BER_BVSTR( &bv, "tail" );
+ }
+ value_add_one( &c->rvalue_vals, &bv );
+ break;
+ case PC_QUERIES:
+ c->value_int = cm->max_queries;
+ break;
+ case PC_OFFLINE:
+ c->value_int = (cm->cc_paused & PCACHE_CC_OFFLINE) != 0;
+ break;
+ }
+ return rc;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ rc = 1;
+ switch( c->type ) {
+ case PC_ATTR: /* FIXME */
+ case PC_TEMP:
+ case PC_BIND:
+ break;
+ case PC_OFFLINE:
+ cm->cc_paused &= ~PCACHE_CC_OFFLINE;
+ /* If there were cached queries when we went offline,
+ * restart the checker now.
+ */
+ if ( cm->num_cached_queries ) {
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ cm->cc_paused = 0;
+ ldap_pvt_runqueue_resched( &slapd_rq, cm->cc_arg, 0 );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+ rc = 0;
+ break;
+ }
+ return rc;
+ }
+
+ switch( c->type ) {
+ case PC_MAIN:
+ if ( cm->numattrsets > 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcache\" directive already provided" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+
+ if ( lutil_atoi( &cm->numattrsets, c->argv[3] ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse num attrsets=\"%s\" (arg #3)",
+ c->argv[3] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+ if ( cm->numattrsets <= 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "numattrsets (arg #3) must be positive" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+ if ( cm->numattrsets > MAX_ATTR_SETS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "numattrsets (arg #3) must be <= %d", MAX_ATTR_SETS );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+
+ if ( !backend_db_init( c->argv[1], &cm->db, -1, NULL )) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "unknown backend type (arg #1)" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+
+ if ( lutil_atoi( &cm->max_entries, c->argv[2] ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse max entries=\"%s\" (arg #2)",
+ c->argv[2] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+ if ( cm->max_entries <= 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "max entries (arg #2) must be positive.\n" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+
+ if ( lutil_atoi( &cm->num_entries_limit, c->argv[4] ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse entry limit=\"%s\" (arg #4)",
+ c->argv[4] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+ if ( cm->num_entries_limit <= 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "entry limit (arg #4) must be positive" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+ if ( cm->num_entries_limit > cm->max_entries ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "entry limit (arg #4) must be less than max entries %d (arg #2)", cm->max_entries );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+
+ if ( lutil_parse_time( c->argv[5], &t ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse period=\"%s\" (arg #5)",
+ c->argv[5] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+
+ cm->cc_period = (time_t)t;
+ Debug( pcache_debug,
+ "Total # of attribute sets to be cached = %d.\n",
+ cm->numattrsets );
+ qm->attr_sets = ( struct attr_set * )ch_calloc( cm->numattrsets,
+ sizeof( struct attr_set ) );
+ break;
+ case PC_ATTR:
+ if ( cm->numattrsets == 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcache\" directive not provided yet" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+ if ( lutil_atoi( &num, c->argv[1] ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse attrset #=\"%s\"",
+ c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+
+ if ( num < 0 || num >= cm->numattrsets ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "attrset index %d out of bounds (must be %s%d)",
+ num, cm->numattrsets > 1 ? "0->" : "", cm->numattrsets - 1 );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ qm->attr_sets[num].flags |= PC_CONFIGURED;
+ if ( c->argc == 2 ) {
+ /* assume "1.1" */
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "need an explicit attr in attrlist; use \"*\" to indicate all attrs" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+
+ } else if ( c->argc == 3 ) {
+ if ( strcmp( c->argv[2], LDAP_ALL_USER_ATTRIBUTES ) == 0 ) {
+ qm->attr_sets[num].count = 1;
+ qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( 2,
+ sizeof( AttributeName ) );
+ BER_BVSTR( &qm->attr_sets[num].attrs[0].an_name, LDAP_ALL_USER_ATTRIBUTES );
+ break;
+
+ } else if ( strcmp( c->argv[2], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 ) {
+ qm->attr_sets[num].count = 1;
+ qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( 2,
+ sizeof( AttributeName ) );
+ BER_BVSTR( &qm->attr_sets[num].attrs[0].an_name, LDAP_ALL_OPERATIONAL_ATTRIBUTES );
+ break;
+
+ } else if ( strcmp( c->argv[2], LDAP_NO_ATTRS ) == 0 ) {
+ break;
+ }
+ /* else: fallthru */
+
+ } else if ( c->argc == 4 ) {
+ if ( ( strcmp( c->argv[2], LDAP_ALL_USER_ATTRIBUTES ) == 0 && strcmp( c->argv[3], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 )
+ || ( strcmp( c->argv[2], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 && strcmp( c->argv[3], LDAP_ALL_USER_ATTRIBUTES ) == 0 ) )
+ {
+ qm->attr_sets[num].count = 2;
+ qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( 3,
+ sizeof( AttributeName ) );
+ BER_BVSTR( &qm->attr_sets[num].attrs[0].an_name, LDAP_ALL_USER_ATTRIBUTES );
+ BER_BVSTR( &qm->attr_sets[num].attrs[1].an_name, LDAP_ALL_OPERATIONAL_ATTRIBUTES );
+ break;
+ }
+ /* else: fallthru */
+ }
+
+ if ( c->argc > 2 ) {
+ int all_user = 0, all_op = 0;
+
+ qm->attr_sets[num].count = c->argc - 2;
+ qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( c->argc - 1,
+ sizeof( AttributeName ) );
+ attr_name = qm->attr_sets[num].attrs;
+ for ( i = 2; i < c->argc; i++ ) {
+ attr_name->an_desc = NULL;
+ if ( strcmp( c->argv[i], LDAP_NO_ATTRS ) == 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "invalid attr #%d \"%s\" in attrlist",
+ i - 2, c->argv[i] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ ch_free( qm->attr_sets[num].attrs );
+ qm->attr_sets[num].attrs = NULL;
+ qm->attr_sets[num].count = 0;
+ return 1;
+ }
+ if ( strcmp( c->argv[i], LDAP_ALL_USER_ATTRIBUTES ) == 0 ) {
+ all_user = 1;
+ BER_BVSTR( &attr_name->an_name, LDAP_ALL_USER_ATTRIBUTES );
+ } else if ( strcmp( c->argv[i], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 ) {
+ all_op = 1;
+ BER_BVSTR( &attr_name->an_name, LDAP_ALL_OPERATIONAL_ATTRIBUTES );
+ } else {
+ if ( strncasecmp( c->argv[i], "undef:", STRLENOF("undef:") ) == 0 ) {
+ struct berval bv;
+ ber_str2bv( c->argv[i] + STRLENOF("undef:"), 0, 0, &bv );
+ attr_name->an_desc = slap_bv2tmp_ad( &bv, NULL );
+
+ } else if ( slap_str2ad( c->argv[i], &attr_name->an_desc, &text ) ) {
+ strcpy( c->cr_msg, text );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ ch_free( qm->attr_sets[num].attrs );
+ qm->attr_sets[num].attrs = NULL;
+ qm->attr_sets[num].count = 0;
+ return 1;
+ }
+ attr_name->an_name = attr_name->an_desc->ad_cname;
+ }
+ attr_name->an_oc = NULL;
+ attr_name->an_flags = 0;
+ if ( attr_name->an_desc == slap_schema.si_ad_objectClass )
+ qm->attr_sets[num].flags |= PC_GOT_OC;
+ attr_name++;
+ BER_BVZERO( &attr_name->an_name );
+ }
+
+ /* warn if list contains both "*" and "+" */
+ if ( i > 4 && all_user && all_op ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "warning: attribute list contains \"*\" and \"+\"" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ }
+ }
+ break;
+ case PC_TEMP:
+ if ( cm->numattrsets == 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcache\" directive not provided yet" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+ if ( lutil_atoi( &i, c->argv[2] ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse template #=\"%s\"",
+ c->argv[2] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+
+ if ( i < 0 || i >= cm->numattrsets ||
+ !(qm->attr_sets[i].flags & PC_CONFIGURED )) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "template index %d invalid (%s%d)",
+ i, cm->numattrsets > 1 ? "0->" : "", cm->numattrsets - 1 );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ {
+ AttributeName *attrs;
+ int cnt;
+ cnt = template_attrs( c->argv[1], &qm->attr_sets[i], &attrs, &text );
+ if ( cnt < 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse template: %s",
+ text );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ temp = ch_calloc( 1, sizeof( QueryTemplate ));
+ temp->qmnext = qm->templates;
+ qm->templates = temp;
+ temp->t_attrs.attrs = attrs;
+ temp->t_attrs.count = cnt;
+ }
+ ldap_pvt_thread_rdwr_init( &temp->t_rwlock );
+ temp->query = temp->query_last = NULL;
+ if ( lutil_parse_time( c->argv[3], &t ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse template ttl=\"%s\"",
+ c->argv[3] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+pc_temp_fail:
+ ch_free( temp->t_attrs.attrs );
+ ch_free( temp );
+ return( 1 );
+ }
+ temp->ttl = (time_t)t;
+ temp->negttl = (time_t)0;
+ temp->limitttl = (time_t)0;
+ temp->ttr = (time_t)0;
+ switch ( c->argc ) {
+ case 7:
+ if ( lutil_parse_time( c->argv[6], &t ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse template ttr=\"%s\"",
+ c->argv[6] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ goto pc_temp_fail;
+ }
+ temp->ttr = (time_t)t;
+ /* fallthru */
+
+ case 6:
+ if ( lutil_parse_time( c->argv[5], &t ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse template sizelimit ttl=\"%s\"",
+ c->argv[5] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ goto pc_temp_fail;
+ }
+ temp->limitttl = (time_t)t;
+ /* fallthru */
+
+ case 5:
+ if ( lutil_parse_time( c->argv[4], &t ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse template negative ttl=\"%s\"",
+ c->argv[4] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ goto pc_temp_fail;
+ }
+ temp->negttl = (time_t)t;
+ break;
+ }
+
+ temp->no_of_queries = 0;
+
+ ber_str2bv( c->argv[1], 0, 1, &temp->querystr );
+ Debug( pcache_debug, "Template:\n" );
+ Debug( pcache_debug, " query template: %s\n",
+ temp->querystr.bv_val );
+ temp->attr_set_index = i;
+ qm->attr_sets[i].flags |= PC_REFERENCED;
+ temp->qtnext = qm->attr_sets[i].templates;
+ qm->attr_sets[i].templates = temp;
+ Debug( pcache_debug, " attributes: \n" );
+ if ( ( attrarray = qm->attr_sets[i].attrs ) != NULL ) {
+ for ( i=0; attrarray[i].an_name.bv_val; i++ )
+ Debug( pcache_debug, "\t%s\n",
+ attrarray[i].an_name.bv_val );
+ }
+ break;
+ case PC_BIND:
+ if ( !qm->templates ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcacheTemplate\" directive not provided yet" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+ if ( lutil_atoi( &i, c->argv[2] ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse Bind index #=\"%s\"",
+ c->argv[2] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+
+ if ( i < 0 || i >= cm->numattrsets ||
+ !(qm->attr_sets[i].flags & PC_CONFIGURED )) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "Bind index %d invalid (%s%d)",
+ i, cm->numattrsets > 1 ? "0->" : "", cm->numattrsets - 1 );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ { struct berval bv, tempbv;
+ AttributeDescription **descs;
+ int ndescs;
+ ber_str2bv( c->argv[1], 0, 0, &bv );
+ ndescs = ftemp_attrs( &bv, &tempbv, &descs, &text );
+ if ( ndescs < 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse template: %s",
+ text );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ for ( temp = qm->templates; temp; temp=temp->qmnext ) {
+ if ( temp->attr_set_index == i && bvmatch( &tempbv,
+ &temp->querystr ))
+ break;
+ }
+ ch_free( tempbv.bv_val );
+ if ( !temp ) {
+ ch_free( descs );
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "Bind template %s %d invalid",
+ c->argv[1], i );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ ber_dupbv( &temp->bindftemp, &bv );
+ temp->bindfattrs = descs;
+ temp->bindnattrs = ndescs;
+ }
+ if ( lutil_parse_time( c->argv[3], &t ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse bind ttr=\"%s\"",
+ c->argv[3] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+pc_bind_fail:
+ ch_free( temp->bindfattrs );
+ temp->bindfattrs = NULL;
+ ch_free( temp->bindftemp.bv_val );
+ BER_BVZERO( &temp->bindftemp );
+ return( 1 );
+ }
+ num = ldap_pvt_str2scope( c->argv[4] );
+ if ( num < 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse bind scope=\"%s\"",
+ c->argv[4] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ goto pc_bind_fail;
+ }
+ {
+ struct berval dn, ndn;
+ ber_str2bv( c->argv[5], 0, 0, &dn );
+ rc = dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL );
+ if ( rc ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "invalid bind baseDN=\"%s\"",
+ c->argv[5] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ goto pc_bind_fail;
+ }
+ if ( temp->bindbase.bv_val )
+ ch_free( temp->bindbase.bv_val );
+ temp->bindbase = ndn;
+ }
+ {
+ /* convert the template into dummy filter */
+ struct berval bv;
+ char *eq = temp->bindftemp.bv_val, *e2;
+ Filter *f;
+ i = 0;
+ while ((eq = strchr(eq, '=' ))) {
+ eq++;
+ if ( eq[0] == ')' )
+ i++;
+ }
+ bv.bv_len = temp->bindftemp.bv_len + i;
+ bv.bv_val = ch_malloc( bv.bv_len + 1 );
+ for ( e2 = bv.bv_val, eq = temp->bindftemp.bv_val;
+ *eq; eq++ ) {
+ if ( *eq == '=' ) {
+ *e2++ = '=';
+ if ( eq[1] == ')' )
+ *e2++ = '*';
+ } else {
+ *e2++ = *eq;
+ }
+ }
+ *e2 = '\0';
+ f = str2filter( bv.bv_val );
+ if ( !f ) {
+ ch_free( bv.bv_val );
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unable to parse bindfilter=\"%s\"", bv.bv_val );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ ch_free( temp->bindbase.bv_val );
+ BER_BVZERO( &temp->bindbase );
+ goto pc_bind_fail;
+ }
+ if ( temp->bindfilter )
+ filter_free( temp->bindfilter );
+ if ( temp->bindfilterstr.bv_val )
+ ch_free( temp->bindfilterstr.bv_val );
+ temp->bindfilterstr = bv;
+ temp->bindfilter = f;
+ }
+ temp->bindttr = (time_t)t;
+ temp->bindscope = num;
+ cm->cache_binds = 1;
+ break;
+
+ case PC_RESP:
+ if ( strcasecmp( c->argv[1], "head" ) == 0 ) {
+ cm->response_cb = PCACHE_RESPONSE_CB_HEAD;
+
+ } else if ( strcasecmp( c->argv[1], "tail" ) == 0 ) {
+ cm->response_cb = PCACHE_RESPONSE_CB_TAIL;
+
+ } else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "unknown specifier" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ break;
+ case PC_QUERIES:
+ if ( c->value_int <= 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "max queries must be positive" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+ cm->max_queries = c->value_int;
+ break;
+ case PC_OFFLINE:
+ if ( c->value_int )
+ cm->cc_paused |= PCACHE_CC_OFFLINE;
+ else
+ cm->cc_paused &= ~PCACHE_CC_OFFLINE;
+ break;
+ case PC_PRIVATE_DB:
+ if ( cm->db.be_private == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "private database must be defined before setting database specific options" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return( 1 );
+ }
+
+ if ( cm->db.bd_info->bi_cf_ocs ) {
+ ConfigTable *ct;
+ ConfigArgs c2 = *c;
+ char *argv0 = c->argv[ 0 ];
+
+ c->argv[ 0 ] = &argv0[ STRLENOF( "pcache-" ) ];
+
+ ct = config_find_keyword( cm->db.bd_info->bi_cf_ocs->co_table, c );
+ if ( ct == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "private database does not recognize specific option '%s'",
+ c->argv[ 0 ] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ rc = 1;
+
+ } else {
+ c->table = cm->db.bd_info->bi_cf_ocs->co_type;
+ c->be = &cm->db;
+ c->bi = c->be->bd_info;
+
+ rc = config_add_vals( ct, c );
+
+ c->bi = c2.bi;
+ c->be = c2.be;
+ c->table = c2.table;
+ }
+
+ c->argv[ 0 ] = argv0;
+
+ } else if ( cm->db.be_config != NULL ) {
+ char *argv0 = c->argv[ 0 ];
+
+ c->argv[ 0 ] = &argv0[ STRLENOF( "pcache-" ) ];
+ rc = cm->db.be_config( &cm->db, c->fname, c->lineno, c->argc, c->argv );
+ c->argv[ 0 ] = argv0;
+
+ } else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "no means to set private database specific options" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ break;
+ default:
+ rc = SLAP_CONF_UNKNOWN;
+ break;
+ }
+
+ return rc;
+}
+
+static int
+pcache_db_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ cache_manager* cm = on->on_bi.bi_private;
+
+ /* Something for the cache database? */
+ if ( cm->db.bd_info && cm->db.bd_info->bi_db_config )
+ return cm->db.bd_info->bi_db_config( &cm->db, fname, lineno,
+ argc, argv );
+ return SLAP_CONF_UNKNOWN;
+}
+
+static int
+pcache_db_init(
+ BackendDB *be,
+ ConfigReply *cr)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ cache_manager *cm;
+ query_manager *qm;
+
+ cm = (cache_manager *)ch_malloc(sizeof(cache_manager));
+ on->on_bi.bi_private = cm;
+
+ qm = (query_manager*)ch_malloc(sizeof(query_manager));
+
+ cm->db = *be;
+ cm->db.bd_info = NULL;
+ SLAP_DBFLAGS(&cm->db) |= SLAP_DBFLAG_NO_SCHEMA_CHECK;
+ cm->db.be_private = NULL;
+ cm->db.bd_self = &cm->db;
+ cm->qm = qm;
+ cm->numattrsets = 0;
+ cm->num_entries_limit = 5;
+ cm->num_cached_queries = 0;
+ cm->max_entries = 0;
+ cm->cur_entries = 0;
+ cm->max_queries = 10000;
+ cm->save_queries = 0;
+ cm->check_cacheability = 0;
+ cm->response_cb = PCACHE_RESPONSE_CB_TAIL;
+ cm->defer_db_open = 1;
+ cm->cache_binds = 0;
+ cm->cc_period = 1000;
+ cm->cc_paused = 0;
+ cm->cc_arg = NULL;
+#ifdef PCACHE_MONITOR
+ cm->monitor_cb = NULL;
+#endif /* PCACHE_MONITOR */
+
+ qm->attr_sets = NULL;
+ qm->templates = NULL;
+ qm->lru_top = NULL;
+ qm->lru_bottom = NULL;
+
+ qm->qcfunc = query_containment;
+ qm->crfunc = cache_replacement;
+ qm->addfunc = add_query;
+ ldap_pvt_thread_mutex_init(&qm->lru_mutex);
+
+ ldap_pvt_thread_mutex_init(&cm->cache_mutex);
+
+#ifndef PCACHE_MONITOR
+ return 0;
+#else /* PCACHE_MONITOR */
+ return pcache_monitor_db_init( be );
+#endif /* PCACHE_MONITOR */
+}
+
+static int
+pcache_cachedquery_open_cb( Operation *op, SlapReply *rs )
+{
+ assert( op->o_tag == LDAP_REQ_SEARCH );
+
+ if ( rs->sr_type == REP_SEARCH ) {
+ Attribute *a;
+
+ a = attr_find( rs->sr_entry->e_attrs, ad_cachedQueryURL );
+ if ( a != NULL ) {
+ BerVarray *valsp;
+
+ assert( a->a_nvals != NULL );
+
+ valsp = op->o_callback->sc_private;
+ assert( *valsp == NULL );
+
+ ber_bvarray_dup_x( valsp, a->a_nvals, op->o_tmpmemctx );
+ }
+ }
+
+ return 0;
+}
+
+static int
+pcache_cachedquery_count_cb( Operation *op, SlapReply *rs )
+{
+ assert( op->o_tag == LDAP_REQ_SEARCH );
+
+ if ( rs->sr_type == REP_SEARCH ) {
+ int *countp = (int *)op->o_callback->sc_private;
+
+ (*countp)++;
+ }
+
+ return 0;
+}
+
+static int
+pcache_db_open2(
+ slap_overinst *on,
+ ConfigReply *cr )
+{
+ cache_manager *cm = on->on_bi.bi_private;
+ query_manager* qm = cm->qm;
+ int rc;
+
+ rc = backend_startup_one( &cm->db, cr );
+ if ( rc == 0 ) {
+ cm->defer_db_open = 0;
+ }
+
+ /* There is no runqueue in TOOL mode */
+ if (( slapMode & SLAP_SERVER_MODE ) && rc == 0 ) {
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ ldap_pvt_runqueue_insert( &slapd_rq, cm->cc_period,
+ consistency_check, on,
+ "pcache_consistency", cm->db.be_suffix[0].bv_val );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+
+ /* Cached database must have the rootdn */
+ if ( BER_BVISNULL( &cm->db.be_rootndn )
+ || BER_BVISEMPTY( &cm->db.be_rootndn ) )
+ {
+ Debug( LDAP_DEBUG_ANY, "pcache_db_open(): "
+ "underlying database of type \"%s\"\n"
+ " serving naming context \"%s\"\n"
+ " has no \"rootdn\", required by \"pcache\".\n",
+ on->on_info->oi_orig->bi_type,
+ cm->db.be_suffix[0].bv_val );
+ return 1;
+ }
+
+ if ( cm->save_queries ) {
+ void *thrctx = ldap_pvt_thread_pool_context();
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+ Operation *op;
+ slap_callback cb = { 0 };
+ SlapReply rs = { REP_RESULT };
+ BerVarray vals = NULL;
+ Filter f = { 0 }, f2 = { 0 };
+ AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
+ AttributeName attrs[ 2 ] = {{{ 0 }}};
+
+ connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+ op = &opbuf.ob_op;
+
+ op->o_bd = &cm->db;
+
+ op->o_tag = LDAP_REQ_SEARCH;
+ op->o_protocol = LDAP_VERSION3;
+ cb.sc_response = pcache_cachedquery_open_cb;
+ cb.sc_private = &vals;
+ op->o_callback = &cb;
+ op->o_time = slap_get_time();
+ op->o_do_not_cache = 1;
+ op->o_managedsait = SLAP_CONTROL_CRITICAL;
+
+ op->o_dn = cm->db.be_rootdn;
+ op->o_ndn = cm->db.be_rootndn;
+ op->o_req_dn = cm->db.be_suffix[ 0 ];
+ op->o_req_ndn = cm->db.be_nsuffix[ 0 ];
+
+ op->ors_scope = LDAP_SCOPE_BASE;
+ op->ors_deref = LDAP_DEREF_NEVER;
+ op->ors_slimit = 1;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_limit = NULL;
+ ber_str2bv( "(pcacheQueryURL=*)", 0, 0, &op->ors_filterstr );
+ f.f_choice = LDAP_FILTER_PRESENT;
+ f.f_desc = ad_cachedQueryURL;
+ op->ors_filter = &f;
+ attrs[ 0 ].an_desc = ad_cachedQueryURL;
+ attrs[ 0 ].an_name = ad_cachedQueryURL->ad_cname;
+ op->ors_attrs = attrs;
+ op->ors_attrsonly = 0;
+
+ rc = op->o_bd->be_search( op, &rs );
+ if ( rc == LDAP_SUCCESS && vals != NULL ) {
+ int i;
+
+ for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
+ if ( url2query( vals[ i ].bv_val, op, qm ) == 0 ) {
+ cm->num_cached_queries++;
+ }
+ }
+
+ ber_bvarray_free_x( vals, op->o_tmpmemctx );
+ }
+
+ /* count cached entries */
+ f.f_choice = LDAP_FILTER_NOT;
+ f.f_not = &f2;
+ f2.f_choice = LDAP_FILTER_EQUALITY;
+ f2.f_ava = &ava;
+ f2.f_av_desc = slap_schema.si_ad_objectClass;
+ BER_BVSTR( &f2.f_av_value, "glue" );
+ ber_str2bv( "(!(objectClass=glue))", 0, 0, &op->ors_filterstr );
+
+ op->ors_slimit = SLAP_NO_LIMIT;
+ op->ors_scope = LDAP_SCOPE_SUBTREE;
+ op->ors_attrs = slap_anlist_no_attrs;
+
+ rs_reinit( &rs, REP_RESULT );
+ op->o_callback->sc_response = pcache_cachedquery_count_cb;
+ op->o_callback->sc_private = &rs.sr_nentries;
+
+ rc = op->o_bd->be_search( op, &rs );
+
+ cm->cur_entries = rs.sr_nentries;
+
+ /* ignore errors */
+ rc = 0;
+ }
+ }
+ return rc;
+}
+
+static int
+pcache_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ cache_manager *cm = on->on_bi.bi_private;
+ query_manager* qm = cm->qm;
+ int i, ncf = 0, rf = 0, nrf = 0, rc = 0;
+
+ /* check attr sets */
+ for ( i = 0; i < cm->numattrsets; i++) {
+ if ( !( qm->attr_sets[i].flags & PC_CONFIGURED ) ) {
+ if ( qm->attr_sets[i].flags & PC_REFERENCED ) {
+ Debug( LDAP_DEBUG_CONFIG, "pcache: attr set #%d not configured but referenced.\n", i );
+ rf++;
+
+ } else {
+ Debug( LDAP_DEBUG_CONFIG, "pcache: warning, attr set #%d not configured.\n", i );
+ }
+ ncf++;
+
+ } else if ( !( qm->attr_sets[i].flags & PC_REFERENCED ) ) {
+ Debug( LDAP_DEBUG_CONFIG, "pcache: attr set #%d configured but not referenced.\n", i );
+ nrf++;
+ }
+ }
+
+ if ( ncf || rf || nrf ) {
+ Debug( LDAP_DEBUG_CONFIG, "pcache: warning, %d attr sets configured but not referenced.\n", nrf );
+ Debug( LDAP_DEBUG_CONFIG, "pcache: warning, %d attr sets not configured.\n", ncf );
+ Debug( LDAP_DEBUG_CONFIG, "pcache: %d attr sets not configured but referenced.\n", rf );
+
+ if ( rf > 0 ) {
+ return 1;
+ }
+ }
+
+ /* need to inherit something from the original database... */
+ cm->db.be_def_limit = be->be_def_limit;
+ cm->db.be_limits = be->be_limits;
+ cm->db.be_acl = be->be_acl;
+ cm->db.be_dfltaccess = be->be_dfltaccess;
+
+ if ( SLAP_DBMONITORING( be ) ) {
+ SLAP_DBFLAGS( &cm->db ) |= SLAP_DBFLAG_MONITORING;
+
+ } else {
+ SLAP_DBFLAGS( &cm->db ) &= ~SLAP_DBFLAG_MONITORING;
+ }
+
+ if ( !cm->defer_db_open ) {
+ rc = pcache_db_open2( on, cr );
+ }
+
+#ifdef PCACHE_MONITOR
+ if ( rc == LDAP_SUCCESS ) {
+ rc = pcache_monitor_db_open( be );
+ }
+#endif /* PCACHE_MONITOR */
+
+ return rc;
+}
+
+static void
+pcache_free_qbase( void *v )
+{
+ Qbase *qb = v;
+ int i;
+
+ for (i=0; i<3; i++)
+ ldap_tavl_free( qb->scopes[i], NULL );
+ ch_free( qb );
+}
+
+static int
+pcache_db_close(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ cache_manager *cm = on->on_bi.bi_private;
+ query_manager *qm = cm->qm;
+ QueryTemplate *tm;
+ int rc = 0;
+
+ /* stop the thread ... */
+ if ( cm->cc_arg ) {
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, cm->cc_arg ) ) {
+ ldap_pvt_runqueue_stoptask( &slapd_rq, cm->cc_arg );
+ }
+ ldap_pvt_runqueue_remove( &slapd_rq, cm->cc_arg );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ cm->cc_arg = NULL;
+ }
+
+ if ( cm->save_queries ) {
+ CachedQuery *qc;
+ BerVarray vals = NULL;
+
+ void *thrctx;
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+ Operation *op;
+ slap_callback cb = { 0 };
+
+ SlapReply rs = { REP_RESULT };
+ Modifications mod = {{ 0 }};
+
+ thrctx = ldap_pvt_thread_pool_context();
+
+ connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+ op = &opbuf.ob_op;
+
+ mod.sml_numvals = 0;
+ if ( qm->templates != NULL ) {
+ for ( tm = qm->templates; tm != NULL; tm = tm->qmnext ) {
+ for ( qc = tm->query; qc; qc = qc->next ) {
+ struct berval bv;
+
+ if ( query2url( op, qc, &bv, 0 ) == 0 ) {
+ ber_bvarray_add_x( &vals, &bv, op->o_tmpmemctx );
+ mod.sml_numvals++;
+ }
+ }
+ }
+ }
+
+ op->o_bd = &cm->db;
+ op->o_dn = cm->db.be_rootdn;
+ op->o_ndn = cm->db.be_rootndn;
+
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->o_protocol = LDAP_VERSION3;
+ cb.sc_response = slap_null_cb;
+ op->o_callback = &cb;
+ op->o_time = slap_get_time();
+ op->o_do_not_cache = 1;
+ op->o_managedsait = SLAP_CONTROL_CRITICAL;
+
+ op->o_req_dn = op->o_bd->be_suffix[0];
+ op->o_req_ndn = op->o_bd->be_nsuffix[0];
+
+ mod.sml_op = LDAP_MOD_REPLACE;
+ mod.sml_flags = 0;
+ mod.sml_desc = ad_cachedQueryURL;
+ mod.sml_type = ad_cachedQueryURL->ad_cname;
+ mod.sml_values = vals;
+ mod.sml_nvalues = NULL;
+ mod.sml_next = NULL;
+ Debug( pcache_debug,
+ "%sSETTING CACHED QUERY URLS\n",
+ vals == NULL ? "RE" : "" );
+
+ op->orm_modlist = &mod;
+
+ op->o_bd->be_modify( op, &rs );
+
+ ber_bvarray_free_x( vals, op->o_tmpmemctx );
+ }
+
+ /* cleanup stuff inherited from the original database... */
+ cm->db.be_limits = NULL;
+ cm->db.be_acl = NULL;
+
+ if ( cm->db.bd_info->bi_db_close ) {
+ rc = cm->db.bd_info->bi_db_close( &cm->db, NULL );
+ }
+
+#ifdef PCACHE_MONITOR
+ if ( rc == LDAP_SUCCESS ) {
+ rc = pcache_monitor_db_close( be );
+ }
+#endif /* PCACHE_MONITOR */
+
+ return rc;
+}
+
+static int
+pcache_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ cache_manager *cm = on->on_bi.bi_private;
+ query_manager *qm = cm->qm;
+ QueryTemplate *tm;
+ int i;
+
+ if ( cm->db.be_private != NULL ) {
+ backend_stopdown_one( &cm->db );
+ }
+
+ while ( (tm = qm->templates) != NULL ) {
+ CachedQuery *qc, *qn;
+ qm->templates = tm->qmnext;
+ for ( qc = tm->query; qc; qc = qn ) {
+ qn = qc->next;
+ free_query( qc );
+ }
+ ldap_avl_free( tm->qbase, pcache_free_qbase );
+ free( tm->querystr.bv_val );
+ free( tm->bindfattrs );
+ free( tm->bindftemp.bv_val );
+ free( tm->bindfilterstr.bv_val );
+ free( tm->bindbase.bv_val );
+ filter_free( tm->bindfilter );
+ ldap_pvt_thread_rdwr_destroy( &tm->t_rwlock );
+ free( tm->t_attrs.attrs );
+ free( tm );
+ }
+
+ for ( i = 0; i < cm->numattrsets; i++ ) {
+ int j;
+
+ /* Account of LDAP_NO_ATTRS */
+ if ( !qm->attr_sets[i].count ) continue;
+
+ for ( j = 0; !BER_BVISNULL( &qm->attr_sets[i].attrs[j].an_name ); j++ ) {
+ if ( qm->attr_sets[i].attrs[j].an_desc &&
+ ( qm->attr_sets[i].attrs[j].an_desc->ad_flags &
+ SLAP_DESC_TEMPORARY ) ) {
+ slap_sl_mfuncs.bmf_free( qm->attr_sets[i].attrs[j].an_desc, NULL );
+ }
+ }
+ free( qm->attr_sets[i].attrs );
+ }
+ free( qm->attr_sets );
+ qm->attr_sets = NULL;
+
+ ldap_pvt_thread_mutex_destroy( &qm->lru_mutex );
+ ldap_pvt_thread_mutex_destroy( &cm->cache_mutex );
+ free( qm );
+ free( cm );
+
+#ifdef PCACHE_MONITOR
+ pcache_monitor_db_destroy( be );
+#endif /* PCACHE_MONITOR */
+
+ return 0;
+}
+
+#ifdef PCACHE_CONTROL_PRIVDB
+/*
+ Control ::= SEQUENCE {
+ controlType LDAPOID,
+ criticality BOOLEAN DEFAULT FALSE,
+ controlValue OCTET STRING OPTIONAL }
+
+ controlType ::= 1.3.6.1.4.1.4203.666.11.9.5.1
+
+ * criticality must be TRUE; controlValue must be absent.
+ */
+static int
+parse_privdb_ctrl(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ if ( op->o_ctrlflag[ privDB_cid ] != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "privateDB control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "privateDB control value not absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( !ctrl->ldctl_iscritical ) {
+ rs->sr_text = "privateDB control criticality required";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ op->o_ctrlflag[ privDB_cid ] = SLAP_CONTROL_CRITICAL;
+
+ return LDAP_SUCCESS;
+}
+
+static char *extops[] = {
+ LDAP_EXOP_MODIFY_PASSWD,
+ NULL
+};
+#endif /* PCACHE_CONTROL_PRIVDB */
+
+static struct berval pcache_exop_MODIFY_PASSWD = BER_BVC( LDAP_EXOP_MODIFY_PASSWD );
+#ifdef PCACHE_EXOP_QUERY_DELETE
+static struct berval pcache_exop_QUERY_DELETE = BER_BVC( PCACHE_EXOP_QUERY_DELETE );
+
+#define LDAP_TAG_EXOP_QUERY_DELETE_BASE ((LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + 0)
+#define LDAP_TAG_EXOP_QUERY_DELETE_DN ((LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + 1)
+#define LDAP_TAG_EXOP_QUERY_DELETE_UUID ((LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + 2)
+
+/*
+ ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
+ requestName [0] LDAPOID,
+ requestValue [1] OCTET STRING OPTIONAL }
+
+ requestName ::= 1.3.6.1.4.1.4203.666.11.9.6.1
+
+ requestValue ::= SEQUENCE { CHOICE {
+ baseDN [0] LDAPDN
+ entryDN [1] LDAPDN },
+ queryID [2] OCTET STRING (SIZE(16))
+ -- constrained to UUID }
+
+ * Either baseDN or entryDN must be present, to allow database selection.
+ *
+ * 1. if baseDN and queryID are present, then the query corresponding
+ * to queryID is deleted;
+ * 2. if baseDN is present and queryID is absent, then all queries
+ * are deleted;
+ * 3. if entryDN is present and queryID is absent, then all queries
+ * corresponding to the queryID values present in entryDN are deleted;
+ * 4. if entryDN and queryID are present, then all queries
+ * corresponding to the queryID values present in entryDN are deleted,
+ * but only if the value of queryID is contained in the entry;
+ *
+ * Currently, only 1, 3 and 4 are implemented. 2 can be obtained by either
+ * recursively deleting the database (ldapdelete -r) with PRIVDB control,
+ * or by removing the database files.
+
+ ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
+ COMPONENTS OF LDAPResult,
+ responseName [10] LDAPOID OPTIONAL,
+ responseValue [11] OCTET STRING OPTIONAL }
+
+ * responseName and responseValue must be absent.
+ */
+
+/*
+ * - on success, *tagp is either LDAP_TAG_EXOP_QUERY_DELETE_BASE
+ * or LDAP_TAG_EXOP_QUERY_DELETE_DN.
+ * - if ndn != NULL, it is set to the normalized DN in the request
+ * corresponding to either the baseDN or the entryDN, according
+ * to *tagp; memory is malloc'ed on the Operation's slab, and must
+ * be freed by the caller.
+ * - if uuid != NULL, it is set to point to the normalized UUID;
+ * memory is malloc'ed on the Operation's slab, and must
+ * be freed by the caller.
+ */
+static int
+pcache_parse_query_delete(
+ struct berval *in,
+ ber_tag_t *tagp,
+ struct berval *ndn,
+ struct berval *uuid,
+ const char **text,
+ void *ctx )
+{
+ int rc = LDAP_SUCCESS;
+ ber_tag_t tag;
+ ber_len_t len = -1;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ struct berval reqdata = BER_BVNULL;
+
+ *text = NULL;
+
+ if ( ndn ) {
+ BER_BVZERO( ndn );
+ }
+
+ if ( uuid ) {
+ BER_BVZERO( uuid );
+ }
+
+ if ( in == NULL || in->bv_len == 0 ) {
+ *text = "empty request data field in queryDelete exop";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ ber_dupbv_x( &reqdata, in, ctx );
+
+ /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+ ber_init2( ber, &reqdata, 0 );
+
+ tag = ber_scanf( ber, "{" /*}*/ );
+
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "pcache_parse_query_delete: decoding error.\n" );
+ goto decoding_error;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_BASE
+ || tag == LDAP_TAG_EXOP_QUERY_DELETE_DN )
+ {
+ *tagp = tag;
+
+ if ( ndn != NULL ) {
+ struct berval dn;
+
+ tag = ber_scanf( ber, "m", &dn );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "pcache_parse_query_delete: DN parse failed.\n" );
+ goto decoding_error;
+ }
+
+ rc = dnNormalize( 0, NULL, NULL, &dn, ndn, ctx );
+ if ( rc != LDAP_SUCCESS ) {
+ *text = "invalid DN in queryDelete exop request data";
+ goto done;
+ }
+
+ } else {
+ tag = ber_scanf( ber, "x" /* "m" */ );
+ if ( tag == LBER_DEFAULT ) {
+ goto decoding_error;
+ }
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_UUID ) {
+ if ( uuid != NULL ) {
+ struct berval bv;
+ char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
+
+ tag = ber_scanf( ber, "m", &bv );
+ if ( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "pcache_parse_query_delete: UUID parse failed.\n" );
+ goto decoding_error;
+ }
+
+ if ( bv.bv_len != 16 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "pcache_parse_query_delete: invalid UUID length %lu.\n",
+ (unsigned long)bv.bv_len );
+ goto decoding_error;
+ }
+
+ rc = lutil_uuidstr_from_normalized(
+ bv.bv_val, bv.bv_len,
+ uuidbuf, sizeof( uuidbuf ) );
+ if ( rc == -1 ) {
+ goto decoding_error;
+ }
+ ber_str2bv( uuidbuf, rc, 1, uuid );
+ rc = LDAP_SUCCESS;
+
+ } else {
+ tag = ber_skip_tag( ber, &len );
+ if ( tag == LBER_DEFAULT ) {
+ goto decoding_error;
+ }
+
+ if ( len != 16 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "pcache_parse_query_delete: invalid UUID length %lu.\n",
+ (unsigned long)len );
+ goto decoding_error;
+ }
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag != LBER_DEFAULT || len != 0 ) {
+decoding_error:;
+ Debug( LDAP_DEBUG_TRACE,
+ "pcache_parse_query_delete: decoding error\n" );
+ rc = LDAP_PROTOCOL_ERROR;
+ *text = "queryDelete data decoding error";
+
+done:;
+ if ( ndn && !BER_BVISNULL( ndn ) ) {
+ slap_sl_free( ndn->bv_val, ctx );
+ BER_BVZERO( ndn );
+ }
+
+ if ( uuid && !BER_BVISNULL( uuid ) ) {
+ slap_sl_free( uuid->bv_val, ctx );
+ BER_BVZERO( uuid );
+ }
+ }
+
+ if ( !BER_BVISNULL( &reqdata ) ) {
+ ber_memfree_x( reqdata.bv_val, ctx );
+ }
+
+ return rc;
+}
+
+static int
+pcache_exop_query_delete(
+ Operation *op,
+ SlapReply *rs )
+{
+ BackendDB *bd = op->o_bd;
+
+ struct berval uuid = BER_BVNULL,
+ *uuidp = NULL;
+ char buf[ SLAP_TEXT_BUFLEN ];
+ unsigned len;
+ ber_tag_t tag = LBER_DEFAULT;
+
+ if ( LogTest( LDAP_DEBUG_STATS ) ) {
+ uuidp = &uuid;
+ }
+
+ rs->sr_err = pcache_parse_query_delete( op->ore_reqdata,
+ &tag, &op->o_req_ndn, uuidp,
+ &rs->sr_text, op->o_tmpmemctx );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ return rs->sr_err;
+ }
+
+ if ( LogTest( LDAP_DEBUG_STATS ) ) {
+ assert( !BER_BVISNULL( &op->o_req_ndn ) );
+ len = snprintf( buf, sizeof( buf ), " dn=\"%s\"", op->o_req_ndn.bv_val );
+
+ if ( !BER_BVISNULL( &uuid ) && len < sizeof( buf ) ) {
+ snprintf( &buf[ len ], sizeof( buf ) - len, " pcacheQueryId=\"%s\"", uuid.bv_val );
+ }
+
+ Debug( LDAP_DEBUG_STATS, "%s QUERY DELETE%s\n",
+ op->o_log_prefix, buf );
+ }
+ op->o_req_dn = op->o_req_ndn;
+
+ op->o_bd = select_backend( &op->o_req_ndn, 0 );
+ if ( op->o_bd == NULL ) {
+ send_ldap_error( op, rs, LDAP_NO_SUCH_OBJECT,
+ "no global superior knowledge" );
+ }
+ rs->sr_err = backend_check_restrictions( op, rs,
+ (struct berval *)&pcache_exop_QUERY_DELETE );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ if ( op->o_bd->be_extended == NULL ) {
+ send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
+ "backend does not support extended operations" );
+ goto done;
+ }
+
+ op->o_bd->be_extended( op, rs );
+
+done:;
+ if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
+ op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &op->o_req_ndn );
+ BER_BVZERO( &op->o_req_dn );
+ }
+
+ if ( !BER_BVISNULL( &uuid ) ) {
+ op->o_tmpfree( uuid.bv_val, op->o_tmpmemctx );
+ }
+
+ op->o_bd = bd;
+
+ return rs->sr_err;
+}
+#endif /* PCACHE_EXOP_QUERY_DELETE */
+
+static int
+pcache_op_extended( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ cache_manager *cm = on->on_bi.bi_private;
+
+#ifdef PCACHE_CONTROL_PRIVDB
+ if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL ) {
+ return pcache_op_privdb( op, rs );
+ }
+#endif /* PCACHE_CONTROL_PRIVDB */
+
+#ifdef PCACHE_EXOP_QUERY_DELETE
+ if ( bvmatch( &op->ore_reqoid, &pcache_exop_QUERY_DELETE ) ) {
+ struct berval uuid = BER_BVNULL;
+ ber_tag_t tag = LBER_DEFAULT;
+
+ rs->sr_err = pcache_parse_query_delete( op->ore_reqdata,
+ &tag, NULL, &uuid, &rs->sr_text, op->o_tmpmemctx );
+ assert( rs->sr_err == LDAP_SUCCESS );
+
+ if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_DN ) {
+ /* remove all queries related to the selected entry */
+ rs->sr_err = pcache_remove_entry_queries_from_cache( op,
+ cm, &op->o_req_ndn, &uuid );
+
+ } else if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_BASE ) {
+ if ( !BER_BVISNULL( &uuid ) ) {
+ /* remove the selected query */
+ rs->sr_err = pcache_remove_query_from_cache( op,
+ cm, &uuid );
+
+ } else {
+ /* TODO: remove all queries */
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "deletion of all queries not implemented";
+ }
+ }
+
+ op->o_tmpfree( uuid.bv_val, op->o_tmpmemctx );
+ return rs->sr_err;
+ }
+#endif /* PCACHE_EXOP_QUERY_DELETE */
+
+ /* We only care if we're configured for Bind caching */
+ if ( bvmatch( &op->ore_reqoid, &pcache_exop_MODIFY_PASSWD ) &&
+ cm->cache_binds ) {
+ /* See if the local entry exists and has a password.
+ * It's too much work to find the matching query, so
+ * we just see if there's a hashed password to update.
+ */
+ Operation op2 = *op;
+ Entry *e = NULL;
+ int rc;
+ int doit = 0;
+
+ op2.o_bd = &cm->db;
+ op2.o_dn = op->o_bd->be_rootdn;
+ op2.o_ndn = op->o_bd->be_rootndn;
+ rc = be_entry_get_rw( &op2, &op->o_req_ndn, NULL,
+ slap_schema.si_ad_userPassword, 0, &e );
+ if ( rc == LDAP_SUCCESS && e ) {
+ /* See if a recognized password is hashed here */
+ Attribute *a = attr_find( e->e_attrs,
+ slap_schema.si_ad_userPassword );
+ if ( a && a->a_vals[0].bv_val[0] == '{' &&
+ lutil_passwd_scheme( a->a_vals[0].bv_val )) {
+ doit = 1;
+ }
+ be_entry_release_r( &op2, e );
+ }
+
+ if ( doit ) {
+ rc = overlay_op_walk( op, rs, op_extended, on->on_info,
+ on->on_next );
+ if ( rc == LDAP_SUCCESS ) {
+ req_pwdexop_s *qpw = &op->oq_pwdexop;
+
+ /* We don't care if it succeeds or not */
+ pc_setpw( &op2, &qpw->rs_new, cm );
+ }
+ return rc;
+ }
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+pcache_entry_release( Operation *op, Entry *e, int rw )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ cache_manager *cm = on->on_bi.bi_private;
+ BackendDB *db = op->o_bd;
+ int rc;
+
+ op->o_bd = &cm->db;
+ rc = be_entry_release_rw( op, e, rw );
+ op->o_bd = db;
+ return rc;
+}
+
+#ifdef PCACHE_MONITOR
+
+static int
+pcache_monitor_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ void *priv )
+{
+ cache_manager *cm = (cache_manager *) priv;
+ query_manager *qm = cm->qm;
+
+ CachedQuery *qc;
+ BerVarray vals = NULL;
+
+ attr_delete( &e->e_attrs, ad_cachedQueryURL );
+ if ( ( SLAP_OPATTRS( rs->sr_attr_flags ) || ad_inlist( ad_cachedQueryURL, rs->sr_attrs ) )
+ && qm->templates != NULL )
+ {
+ QueryTemplate *tm;
+
+ for ( tm = qm->templates; tm != NULL; tm = tm->qmnext ) {
+ for ( qc = tm->query; qc; qc = qc->next ) {
+ struct berval bv;
+
+ if ( query2url( op, qc, &bv, 1 ) == 0 ) {
+ ber_bvarray_add_x( &vals, &bv, op->o_tmpmemctx );
+ }
+ }
+ }
+
+
+ if ( vals != NULL ) {
+ attr_merge_normalize( e, ad_cachedQueryURL, vals, NULL );
+ ber_bvarray_free_x( vals, op->o_tmpmemctx );
+ }
+ }
+
+ {
+ Attribute *a;
+ char buf[ SLAP_TEXT_BUFLEN ];
+ struct berval bv;
+
+ /* number of cached queries */
+ a = attr_find( e->e_attrs, ad_numQueries );
+ assert( a != NULL );
+
+ bv.bv_val = buf;
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%lu", cm->num_cached_queries );
+
+ if ( a->a_nvals != a->a_vals ) {
+ ber_bvreplace( &a->a_nvals[ 0 ], &bv );
+ }
+ ber_bvreplace( &a->a_vals[ 0 ], &bv );
+
+ /* number of cached entries */
+ a = attr_find( e->e_attrs, ad_numEntries );
+ assert( a != NULL );
+
+ bv.bv_val = buf;
+ bv.bv_len = snprintf( buf, sizeof( buf ), "%d", cm->cur_entries );
+
+ if ( a->a_nvals != a->a_vals ) {
+ ber_bvreplace( &a->a_nvals[ 0 ], &bv );
+ }
+ ber_bvreplace( &a->a_vals[ 0 ], &bv );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+pcache_monitor_free(
+ Entry *e,
+ void **priv )
+{
+ struct berval values[ 2 ];
+ Modification mod = { 0 };
+
+ const char *text;
+ char textbuf[ SLAP_TEXT_BUFLEN ];
+
+ int rc;
+
+ /* NOTE: if slap_shutdown != 0, priv might have already been freed */
+ *priv = NULL;
+
+ /* Remove objectClass */
+ mod.sm_op = LDAP_MOD_DELETE;
+ mod.sm_desc = slap_schema.si_ad_objectClass;
+ mod.sm_values = values;
+ mod.sm_numvals = 1;
+ values[ 0 ] = oc_olmPCache->soc_cname;
+ BER_BVZERO( &values[ 1 ] );
+
+ rc = modify_delete_values( e, &mod, 1, &text,
+ textbuf, sizeof( textbuf ) );
+ /* don't care too much about return code... */
+
+ /* remove attrs */
+ mod.sm_values = NULL;
+ mod.sm_desc = ad_cachedQueryURL;
+ mod.sm_numvals = 0;
+ rc = modify_delete_values( e, &mod, 1, &text,
+ textbuf, sizeof( textbuf ) );
+ /* don't care too much about return code... */
+
+ /* remove attrs */
+ mod.sm_values = NULL;
+ mod.sm_desc = ad_numQueries;
+ mod.sm_numvals = 0;
+ rc = modify_delete_values( e, &mod, 1, &text,
+ textbuf, sizeof( textbuf ) );
+ /* don't care too much about return code... */
+
+ /* remove attrs */
+ mod.sm_values = NULL;
+ mod.sm_desc = ad_numEntries;
+ mod.sm_numvals = 0;
+ rc = modify_delete_values( e, &mod, 1, &text,
+ textbuf, sizeof( textbuf ) );
+ /* don't care too much about return code... */
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+ * call from within pcache_initialize()
+ */
+static int
+pcache_monitor_initialize( void )
+{
+ static int pcache_monitor_initialized = 0;
+
+ if ( backend_info( "monitor" ) == NULL ) {
+ return -1;
+ }
+
+ if ( pcache_monitor_initialized++ ) {
+ return 0;
+ }
+
+ return 0;
+}
+
+static int
+pcache_monitor_db_init( BackendDB *be )
+{
+ if ( pcache_monitor_initialize() == LDAP_SUCCESS ) {
+ SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_MONITORING;
+ }
+
+ return 0;
+}
+
+static int
+pcache_monitor_db_open( BackendDB *be )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ cache_manager *cm = on->on_bi.bi_private;
+ Attribute *a, *next;
+ monitor_callback_t *cb = NULL;
+ int rc = 0;
+ BackendInfo *mi;
+ monitor_extra_t *mbe;
+
+ if ( !SLAP_DBMONITORING( be ) ) {
+ return 0;
+ }
+
+ mi = backend_info( "monitor" );
+ if ( !mi || !mi->bi_extra ) {
+ SLAP_DBFLAGS( be ) ^= SLAP_DBFLAG_MONITORING;
+ return 0;
+ }
+ mbe = mi->bi_extra;
+
+ /* don't bother if monitor is not configured */
+ if ( !mbe->is_configured() ) {
+ static int warning = 0;
+
+ if ( warning++ == 0 ) {
+ Debug( LDAP_DEBUG_CONFIG, "pcache_monitor_db_open: "
+ "monitoring disabled; "
+ "configure monitor database to enable\n" );
+ }
+
+ return 0;
+ }
+
+ /* alloc as many as required (plus 1 for objectClass) */
+ a = attrs_alloc( 1 + 2 );
+ if ( a == NULL ) {
+ rc = 1;
+ goto cleanup;
+ }
+
+ a->a_desc = slap_schema.si_ad_objectClass;
+ attr_valadd( a, &oc_olmPCache->soc_cname, NULL, 1 );
+ next = a->a_next;
+
+ {
+ struct berval bv = BER_BVC( "0" );
+
+ next->a_desc = ad_numQueries;
+ attr_valadd( next, &bv, NULL, 1 );
+ next = next->a_next;
+
+ next->a_desc = ad_numEntries;
+ attr_valadd( next, &bv, NULL, 1 );
+ next = next->a_next;
+ }
+
+ cb = ch_calloc( sizeof( monitor_callback_t ), 1 );
+ cb->mc_update = pcache_monitor_update;
+ cb->mc_free = pcache_monitor_free;
+ cb->mc_private = (void *)cm;
+
+ /* make sure the database is registered; then add monitor attributes */
+ BER_BVZERO( &cm->monitor_ndn );
+ rc = mbe->register_overlay( be, on, &cm->monitor_ndn );
+ if ( rc == 0 ) {
+ rc = mbe->register_entry_attrs( &cm->monitor_ndn, a, cb,
+ NULL, -1, NULL);
+ }
+
+cleanup:;
+ if ( rc != 0 ) {
+ if ( cb != NULL ) {
+ ch_free( cb );
+ cb = NULL;
+ }
+
+ if ( a != NULL ) {
+ attrs_free( a );
+ a = NULL;
+ }
+ }
+
+ /* store for cleanup */
+ cm->monitor_cb = (void *)cb;
+
+ /* we don't need to keep track of the attributes, because
+ * mdb_monitor_free() takes care of everything */
+ if ( a != NULL ) {
+ attrs_free( a );
+ }
+
+ return rc;
+}
+
+static int
+pcache_monitor_db_close( BackendDB *be )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ cache_manager *cm = on->on_bi.bi_private;
+
+ if ( !BER_BVISNULL( &cm->monitor_ndn )) {
+ BackendInfo *mi = backend_info( "monitor" );
+ monitor_extra_t *mbe;
+
+ if ( mi && mi->bi_extra ) {
+ struct berval dummy = BER_BVNULL;
+ mbe = mi->bi_extra;
+ mbe->unregister_entry_callback( &cm->monitor_ndn,
+ (monitor_callback_t *)cm->monitor_cb,
+ &dummy, 0, &dummy );
+ }
+ }
+
+ return 0;
+}
+
+static int
+pcache_monitor_db_destroy( BackendDB *be )
+{
+ return 0;
+}
+
+#endif /* PCACHE_MONITOR */
+
+static slap_overinst pcache;
+
+static char *obsolete_names[] = {
+ "proxycache",
+ NULL
+};
+
+#if SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC
+static
+#endif /* SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC */
+int
+pcache_initialize()
+{
+ int i, code;
+ struct berval debugbv = BER_BVC("pcache");
+ ConfigArgs c;
+ char *argv[ 4 ];
+
+ /* olcDatabaseDummy is defined in slapd, and Windows
+ will not let us initialize a struct element with a data pointer
+ from another library, so we have to initialize this element
+ "by hand". */
+ pcocs[1].co_table = olcDatabaseDummy;
+
+
+ code = slap_loglevel_get( &debugbv, &pcache_debug );
+ if ( code ) {
+ return code;
+ }
+
+#ifdef PCACHE_CONTROL_PRIVDB
+ code = register_supported_control( PCACHE_CONTROL_PRIVDB,
+ SLAP_CTRL_BIND|SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, extops,
+ parse_privdb_ctrl, &privDB_cid );
+ if ( code != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "pcache_initialize: failed to register control %s (%d)\n",
+ PCACHE_CONTROL_PRIVDB, code );
+ return code;
+ }
+#endif /* PCACHE_CONTROL_PRIVDB */
+
+#ifdef PCACHE_EXOP_QUERY_DELETE
+ code = load_extop2( (struct berval *)&pcache_exop_QUERY_DELETE,
+ SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, pcache_exop_query_delete,
+ 0 );
+ if ( code != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "pcache_initialize: unable to register queryDelete exop: %d.\n",
+ code );
+ return code;
+ }
+#endif /* PCACHE_EXOP_QUERY_DELETE */
+
+ argv[ 0 ] = "back-mdb monitor";
+ c.argv = argv;
+ c.argc = 3;
+ c.fname = argv[0];
+
+ for ( i = 0; s_oid[ i ].name; i++ ) {
+ c.lineno = i;
+ argv[ 1 ] = s_oid[ i ].name;
+ argv[ 2 ] = s_oid[ i ].oid;
+
+ if ( parse_oidm( &c, 0, NULL ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "pcache_initialize: "
+ "unable to add objectIdentifier \"%s=%s\"\n",
+ s_oid[ i ].name, s_oid[ i ].oid );
+ return 1;
+ }
+ }
+
+ for ( i = 0; s_ad[i].desc != NULL; i++ ) {
+ code = register_at( s_ad[i].desc, s_ad[i].adp, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "pcache_initialize: register_at #%d failed\n", i );
+ return code;
+ }
+ (*s_ad[i].adp)->ad_type->sat_flags |= SLAP_AT_HIDE;
+ }
+
+ for ( i = 0; s_oc[i].desc != NULL; i++ ) {
+ code = register_oc( s_oc[i].desc, s_oc[i].ocp, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "pcache_initialize: register_oc #%d failed\n", i );
+ return code;
+ }
+ (*s_oc[i].ocp)->soc_flags |= SLAP_OC_HIDE;
+ }
+
+ pcache.on_bi.bi_type = "pcache";
+ pcache.on_bi.bi_obsolete_names = obsolete_names;
+ pcache.on_bi.bi_db_init = pcache_db_init;
+ pcache.on_bi.bi_db_config = pcache_db_config;
+ pcache.on_bi.bi_db_open = pcache_db_open;
+ pcache.on_bi.bi_db_close = pcache_db_close;
+ pcache.on_bi.bi_db_destroy = pcache_db_destroy;
+
+ pcache.on_bi.bi_op_search = pcache_op_search;
+ pcache.on_bi.bi_op_bind = pcache_op_bind;
+#ifdef PCACHE_CONTROL_PRIVDB
+ pcache.on_bi.bi_op_compare = pcache_op_privdb;
+ pcache.on_bi.bi_op_modrdn = pcache_op_privdb;
+ pcache.on_bi.bi_op_modify = pcache_op_privdb;
+ pcache.on_bi.bi_op_add = pcache_op_privdb;
+ pcache.on_bi.bi_op_delete = pcache_op_privdb;
+#endif /* PCACHE_CONTROL_PRIVDB */
+ pcache.on_bi.bi_extended = pcache_op_extended;
+
+ pcache.on_bi.bi_entry_release_rw = pcache_entry_release;
+ pcache.on_bi.bi_chk_controls = pcache_chk_controls;
+
+ pcache.on_bi.bi_cf_ocs = pcocs;
+
+ code = config_register_schema( pccfg, pcocs );
+ if ( code ) return code;
+
+ return overlay_register( &pcache );
+}
+
+#if SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC
+int init_module(int argc, char *argv[]) {
+ return pcache_initialize();
+}
+#endif
+
+#endif /* defined(SLAPD_OVER_PROXYCACHE) */
diff --git a/servers/slapd/overlays/ppolicy.c b/servers/slapd/overlays/ppolicy.c
new file mode 100644
index 0000000..a3f2e70
--- /dev/null
+++ b/servers/slapd/overlays/ppolicy.c
@@ -0,0 +1,3413 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2004-2005 Howard Chu, Symas Corporation.
+ * Portions Copyright 2004 Hewlett-Packard Company.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Howard Chu for inclusion in
+ * OpenLDAP Software, based on prior work by Neil Dunbar (HP).
+ * This work was sponsored by the Hewlett-Packard Company.
+ */
+
+#include "portable.h"
+
+/* This file implements "Password Policy for LDAP Directories",
+ * based on draft behera-ldap-password-policy-09
+ */
+
+#ifdef SLAPD_OVER_PPOLICY
+
+#include <ldap.h>
+#include "lutil.h"
+#include "slap.h"
+#ifdef SLAPD_MODULES
+#define LIBLTDL_DLL_IMPORT /* Win32: don't re-export libltdl's symbols */
+#include <ltdl.h>
+#endif
+#include <ac/errno.h>
+#include <ac/time.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include "slap-config.h"
+
+#ifndef MODULE_NAME_SZ
+#define MODULE_NAME_SZ 256
+#endif
+
+#ifndef PPOLICY_DEFAULT_MAXRECORDED_FAILURE
+#define PPOLICY_DEFAULT_MAXRECORDED_FAILURE 5
+#endif
+
+/* Per-instance configuration information */
+typedef struct pp_info {
+ struct berval def_policy; /* DN of default policy subentry */
+ int use_lockout; /* send AccountLocked result? */
+ int hash_passwords; /* transparently hash cleartext pwds */
+ int forward_updates; /* use frontend for policy state updates */
+ int disable_write;
+ int send_netscape_controls; /* send netscape password controls */
+ ldap_pvt_thread_mutex_t pwdFailureTime_mutex;
+} pp_info;
+
+/* Our per-connection info - note, it is not per-instance, it is
+ * used by all instances
+ */
+typedef struct pw_conn {
+ struct berval dn; /* DN of restricted user */
+} pw_conn;
+
+static pw_conn *pwcons;
+static int ppolicy_cid;
+static int account_usability_cid;
+static int ov_count;
+
+typedef struct pass_policy {
+ AttributeDescription *ad; /* attribute to which the policy applies */
+ int pwdMinAge; /* minimum time (seconds) until passwd can change */
+ int pwdMaxAge; /* time in seconds until pwd will expire after change */
+ int pwdMaxIdle; /* number of seconds since last successful bind before
+ passwd gets locked out */
+ int pwdInHistory; /* number of previous passwords kept */
+ int pwdCheckQuality; /* 0 = don't check quality, 1 = check if possible,
+ 2 = check mandatory; fail if not possible */
+ int pwdMinLength; /* minimum number of chars in password */
+ int pwdMaxLength; /* maximum number of chars in password */
+ int pwdExpireWarning; /* number of seconds that warning controls are
+ sent before a password expires */
+ int pwdGraceExpiry; /* number of seconds after expiry grace logins are
+ valid */
+ int pwdGraceAuthNLimit; /* number of times you can log in with an
+ expired password */
+ int pwdLockout; /* 0 = do not lockout passwords, 1 = lock them out */
+ int pwdLockoutDuration; /* time in seconds a password is locked out for */
+ int pwdMinDelay; /* base bind delay in seconds on failure */
+ int pwdMaxDelay; /* maximum bind delay in seconds */
+ int pwdMaxFailure; /* number of failed binds allowed before lockout */
+ int pwdMaxRecordedFailure; /* number of failed binds to store */
+ int pwdFailureCountInterval; /* number of seconds before failure
+ counts are zeroed */
+ int pwdMustChange; /* 0 = users can use admin set password
+ 1 = users must change password after admin set */
+ int pwdAllowUserChange; /* 0 = users cannot change their passwords
+ 1 = users can change them */
+ int pwdSafeModify; /* 0 = old password doesn't need to come
+ with password change request
+ 1 = password change must supply existing pwd */
+ char pwdCheckModule[MODULE_NAME_SZ]; /* name of module to dynamically
+ load to check password */
+ struct berval pwdCheckModuleArg; /* Optional argument to the password check
+ module */
+} PassPolicy;
+
+typedef struct pw_hist {
+ time_t t; /* timestamp of history entry */
+ struct berval pw; /* old password hash */
+ struct berval bv; /* text of entire entry */
+ struct pw_hist *next;
+} pw_hist;
+
+/* Operational attributes */
+static AttributeDescription *ad_pwdChangedTime, *ad_pwdAccountLockedTime,
+ *ad_pwdFailureTime, *ad_pwdHistory, *ad_pwdGraceUseTime, *ad_pwdReset,
+ *ad_pwdPolicySubentry, *ad_pwdStartTime, *ad_pwdEndTime,
+ *ad_pwdLastSuccess, *ad_pwdAccountTmpLockoutEnd;
+
+/* Policy attributes */
+static AttributeDescription *ad_pwdMinAge, *ad_pwdMaxAge, *ad_pwdMaxIdle,
+ *ad_pwdInHistory, *ad_pwdCheckQuality, *ad_pwdMinLength, *ad_pwdMaxLength,
+ *ad_pwdMaxFailure, *ad_pwdGraceExpiry, *ad_pwdGraceAuthNLimit,
+ *ad_pwdExpireWarning, *ad_pwdMinDelay, *ad_pwdMaxDelay,
+ *ad_pwdLockoutDuration, *ad_pwdFailureCountInterval,
+ *ad_pwdCheckModule, *ad_pwdCheckModuleArg, *ad_pwdLockout,
+ *ad_pwdMustChange, *ad_pwdAllowUserChange, *ad_pwdSafeModify,
+ *ad_pwdAttribute, *ad_pwdMaxRecordedFailure;
+
+static struct schema_info {
+ char *def;
+ AttributeDescription **ad;
+} pwd_OpSchema[] = {
+ { "( 1.3.6.1.4.1.42.2.27.8.1.16 "
+ "NAME ( 'pwdChangedTime' ) "
+ "DESC 'The time the password was last changed' "
+ "EQUALITY generalizedTimeMatch "
+ "ORDERING generalizedTimeOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION "
+ "USAGE directoryOperation )",
+ &ad_pwdChangedTime },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.17 "
+ "NAME ( 'pwdAccountLockedTime' ) "
+ "DESC 'The time an user account was locked' "
+ "EQUALITY generalizedTimeMatch "
+ "ORDERING generalizedTimeOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+ "SINGLE-VALUE "
+#if 0 /* FIXME: ITS#9671 until we introduce a separate lockout flag? */
+ "NO-USER-MODIFICATION "
+#endif
+ "USAGE directoryOperation )",
+ &ad_pwdAccountLockedTime },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.19 "
+ "NAME ( 'pwdFailureTime' ) "
+ "DESC 'The timestamps of the last consecutive authentication failures' "
+ "EQUALITY generalizedTimeMatch "
+ "ORDERING generalizedTimeOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+ "NO-USER-MODIFICATION "
+ "USAGE directoryOperation )",
+ &ad_pwdFailureTime },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.20 "
+ "NAME ( 'pwdHistory' ) "
+ "DESC 'The history of users passwords' "
+ "EQUALITY octetStringMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 "
+ "NO-USER-MODIFICATION "
+ "USAGE directoryOperation )",
+ &ad_pwdHistory },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.21 "
+ "NAME ( 'pwdGraceUseTime' ) "
+ "DESC 'The timestamps of the grace login once the password has expired' "
+ "EQUALITY generalizedTimeMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+ "NO-USER-MODIFICATION "
+ "USAGE directoryOperation )",
+ &ad_pwdGraceUseTime },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.22 "
+ "NAME ( 'pwdReset' ) "
+ "DESC 'The indication that the password has been reset' "
+ "EQUALITY booleanMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
+ "SINGLE-VALUE "
+ "USAGE directoryOperation )",
+ &ad_pwdReset },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.23 "
+ "NAME ( 'pwdPolicySubentry' ) "
+ "DESC 'The pwdPolicy subentry in effect for this object' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
+ "SINGLE-VALUE "
+#if 0 /* ITS#9671: until we implement ITS#9343 or similar */
+ "NO-USER-MODIFICATION "
+#endif
+ "USAGE directoryOperation )",
+ &ad_pwdPolicySubentry },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.27 "
+ "NAME ( 'pwdStartTime' ) "
+ "DESC 'The time the password becomes enabled' "
+ "EQUALITY generalizedTimeMatch "
+ "ORDERING generalizedTimeOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+ "SINGLE-VALUE "
+ "USAGE directoryOperation )",
+ &ad_pwdStartTime },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.28 "
+ "NAME ( 'pwdEndTime' ) "
+ "DESC 'The time the password becomes disabled' "
+ "EQUALITY generalizedTimeMatch "
+ "ORDERING generalizedTimeOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+ "SINGLE-VALUE "
+ "USAGE directoryOperation )",
+ &ad_pwdEndTime },
+ /* Defined in schema_prep.c now
+ { "( 1.3.6.1.4.1.42.2.27.8.1.29 "
+ "NAME ( 'pwdLastSuccess' ) "
+ "DESC 'The timestamp of the last successful authentication' "
+ "EQUALITY generalizedTimeMatch "
+ "ORDERING generalizedTimeOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION "
+ "USAGE directoryOperation )",
+ &ad_pwdLastSuccess },
+ */
+ { "( 1.3.6.1.4.1.42.2.27.8.1.33 "
+ "NAME ( 'pwdAccountTmpLockoutEnd' ) "
+ "DESC 'Temporary lockout end' "
+ "EQUALITY generalizedTimeMatch "
+ "ORDERING generalizedTimeOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION "
+ "USAGE directoryOperation )",
+ &ad_pwdAccountTmpLockoutEnd },
+
+ { "( 1.3.6.1.4.1.42.2.27.8.1.1 "
+ "NAME ( 'pwdAttribute' ) "
+ "EQUALITY objectIdentifierMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )",
+ &ad_pwdAttribute },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.2 "
+ "NAME ( 'pwdMinAge' ) "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE )",
+ &ad_pwdMinAge },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.3 "
+ "NAME ( 'pwdMaxAge' ) "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE )",
+ &ad_pwdMaxAge },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.4 "
+ "NAME ( 'pwdInHistory' ) "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE )",
+ &ad_pwdInHistory },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.5 "
+ "NAME ( 'pwdCheckQuality' ) "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE )",
+ &ad_pwdCheckQuality },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.6 "
+ "NAME ( 'pwdMinLength' ) "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE )",
+ &ad_pwdMinLength },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.31 "
+ "NAME ( 'pwdMaxLength' ) "
+ "EQUALITY integerMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE )",
+ &ad_pwdMaxLength },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.7 "
+ "NAME ( 'pwdExpireWarning' ) "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE )",
+ &ad_pwdExpireWarning },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.8 "
+ "NAME ( 'pwdGraceAuthNLimit' ) "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE )",
+ &ad_pwdGraceAuthNLimit },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.30 "
+ "NAME ( 'pwdGraceExpiry' ) "
+ "EQUALITY integerMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE )",
+ &ad_pwdGraceExpiry },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.9 "
+ "NAME ( 'pwdLockout' ) "
+ "EQUALITY booleanMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
+ "SINGLE-VALUE )",
+ &ad_pwdLockout },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.10 "
+ "NAME ( 'pwdLockoutDuration' ) "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE )",
+ &ad_pwdLockoutDuration },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.11 "
+ "NAME ( 'pwdMaxFailure' ) "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE )",
+ &ad_pwdMaxFailure },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.12 "
+ "NAME ( 'pwdFailureCountInterval' ) "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE )",
+ &ad_pwdFailureCountInterval },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.13 "
+ "NAME ( 'pwdMustChange' ) "
+ "EQUALITY booleanMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
+ "SINGLE-VALUE )",
+ &ad_pwdMustChange },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.14 "
+ "NAME ( 'pwdAllowUserChange' ) "
+ "EQUALITY booleanMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
+ "SINGLE-VALUE )",
+ &ad_pwdAllowUserChange },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.15 "
+ "NAME ( 'pwdSafeModify' ) "
+ "EQUALITY booleanMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
+ "SINGLE-VALUE )",
+ &ad_pwdSafeModify },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.24 "
+ "NAME ( 'pwdMinDelay' ) "
+ "EQUALITY integerMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE )",
+ &ad_pwdMinDelay },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.25 "
+ "NAME ( 'pwdMaxDelay' ) "
+ "EQUALITY integerMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE )",
+ &ad_pwdMaxDelay },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.26 "
+ "NAME ( 'pwdMaxIdle' ) "
+ "EQUALITY integerMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE )",
+ &ad_pwdMaxIdle },
+ { "( 1.3.6.1.4.1.42.2.27.8.1.32 "
+ "NAME ( 'pwdMaxRecordedFailure' ) "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE )",
+ &ad_pwdMaxRecordedFailure },
+ { "( 1.3.6.1.4.1.4754.1.99.1 "
+ "NAME ( 'pwdCheckModule' ) "
+ "EQUALITY caseExactIA5Match "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 "
+ "DESC 'Loadable module that instantiates check_password() function' "
+ "SINGLE-VALUE )",
+ &ad_pwdCheckModule },
+ { "( 1.3.6.1.4.1.4754.1.99.2 "
+ "NAME ( 'pwdCheckModuleArg' ) "
+ "EQUALITY octetStringMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 "
+ "DESC 'Argument to pass to check_password() function' "
+ "SINGLE-VALUE )",
+ &ad_pwdCheckModuleArg },
+
+ { NULL, NULL }
+};
+
+static char *pwd_ocs[] = {
+ "( 1.3.6.1.4.1.4754.2.99.1 "
+ "NAME 'pwdPolicyChecker' "
+ "SUP top "
+ "AUXILIARY "
+ "MAY ( pwdCheckModule $ pwdCheckModuleArg ) )" ,
+ "( 1.3.6.1.4.1.42.2.27.8.2.1 "
+ "NAME 'pwdPolicy' "
+ "SUP top "
+ "AUXILIARY "
+ "MUST ( pwdAttribute ) "
+ "MAY ( pwdMinAge $ pwdMaxAge $ pwdInHistory $ pwdCheckQuality $ "
+ "pwdMinLength $ pwdMaxLength $ pwdExpireWarning $ "
+ "pwdGraceAuthNLimit $ pwdGraceExpiry $ pwdLockout $ "
+ "pwdLockoutDuration $ pwdMaxFailure $ pwdFailureCountInterval $ "
+ "pwdMustChange $ pwdAllowUserChange $ pwdSafeModify $ "
+ "pwdMinDelay $ pwdMaxDelay $ pwdMaxIdle $ "
+ "pwdMaxRecordedFailure ) )",
+ NULL
+};
+
+static ldap_pvt_thread_mutex_t chk_syntax_mutex;
+
+enum {
+ PPOLICY_DEFAULT = 1,
+ PPOLICY_HASH_CLEARTEXT,
+ PPOLICY_USE_LOCKOUT,
+ PPOLICY_DISABLE_WRITE,
+};
+
+static ConfigDriver ppolicy_cf_default;
+
+static ConfigTable ppolicycfg[] = {
+ { "ppolicy_default", "policyDN", 2, 2, 0,
+ ARG_DN|ARG_QUOTE|ARG_MAGIC|PPOLICY_DEFAULT, ppolicy_cf_default,
+ "( OLcfgOvAt:12.1 NAME 'olcPPolicyDefault' "
+ "DESC 'DN of a pwdPolicy object for uncustomized objects' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
+ { "ppolicy_hash_cleartext", "on|off", 1, 2, 0,
+ ARG_ON_OFF|ARG_OFFSET|PPOLICY_HASH_CLEARTEXT,
+ (void *)offsetof(pp_info,hash_passwords),
+ "( OLcfgOvAt:12.2 NAME 'olcPPolicyHashCleartext' "
+ "DESC 'Hash passwords on add or modify' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "ppolicy_forward_updates", "on|off", 1, 2, 0,
+ ARG_ON_OFF|ARG_OFFSET,
+ (void *)offsetof(pp_info,forward_updates),
+ "( OLcfgOvAt:12.4 NAME 'olcPPolicyForwardUpdates' "
+ "DESC 'Allow policy state updates to be forwarded via updateref' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "ppolicy_use_lockout", "on|off", 1, 2, 0,
+ ARG_ON_OFF|ARG_OFFSET|PPOLICY_USE_LOCKOUT,
+ (void *)offsetof(pp_info,use_lockout),
+ "( OLcfgOvAt:12.3 NAME 'olcPPolicyUseLockout' "
+ "DESC 'Warn clients with AccountLocked' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "ppolicy_disable_write", "on|off", 1, 2, 0,
+ ARG_ON_OFF|ARG_OFFSET|PPOLICY_DISABLE_WRITE,
+ (void *)offsetof(pp_info,disable_write),
+ "( OLcfgOvAt:12.5 NAME 'olcPPolicyDisableWrite' "
+ "DESC 'Prevent all policy overlay writes' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "ppolicy_send_netscape_controls", "on|off", 1, 2, 0,
+ ARG_ON_OFF|ARG_OFFSET,
+ (void *)offsetof(pp_info,send_netscape_controls),
+ "( OLcfgOvAt:12.6 NAME 'olcPPolicySendNetscapeControls' "
+ "DESC 'Send Netscape policy controls' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs ppolicyocs[] = {
+ { "( OLcfgOvOc:12.1 "
+ "NAME 'olcPPolicyConfig' "
+ "DESC 'Password Policy configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcPPolicyDefault $ olcPPolicyHashCleartext $ "
+ "olcPPolicyUseLockout $ olcPPolicyForwardUpdates $ "
+ "olcPPolicyDisableWrite $ olcPPolicySendNetscapeControls ) )",
+ Cft_Overlay, ppolicycfg },
+ { NULL, 0, NULL }
+};
+
+static int
+ppolicy_cf_default( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ pp_info *pi = (pp_info *)on->on_bi.bi_private;
+ int rc = ARG_BAD_CONF;
+
+ assert ( c->type == PPOLICY_DEFAULT );
+ Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default\n" );
+
+ switch ( c->op ) {
+ case SLAP_CONFIG_EMIT:
+ Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default emit\n" );
+ rc = 0;
+ if ( !BER_BVISEMPTY( &pi->def_policy )) {
+ rc = value_add_one( &c->rvalue_vals,
+ &pi->def_policy );
+ if ( rc ) return rc;
+ rc = value_add_one( &c->rvalue_nvals,
+ &pi->def_policy );
+ }
+ break;
+ case LDAP_MOD_DELETE:
+ Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default delete\n" );
+ if ( pi->def_policy.bv_val ) {
+ ber_memfree ( pi->def_policy.bv_val );
+ pi->def_policy.bv_val = NULL;
+ }
+ pi->def_policy.bv_len = 0;
+ rc = 0;
+ break;
+ case SLAP_CONFIG_ADD:
+ /* fallthru to LDAP_MOD_ADD */
+ case LDAP_MOD_ADD:
+ Debug(LDAP_DEBUG_TRACE, "==> ppolicy_cf_default add\n" );
+ if ( pi->def_policy.bv_val ) {
+ ber_memfree ( pi->def_policy.bv_val );
+ }
+ pi->def_policy = c->value_ndn;
+ ber_memfree( c->value_dn.bv_val );
+ BER_BVZERO( &c->value_dn );
+ BER_BVZERO( &c->value_ndn );
+ rc = 0;
+ break;
+ default:
+ abort ();
+ }
+
+ return rc;
+}
+
+static time_t
+parse_time( char *atm )
+{
+ struct lutil_tm tm;
+ struct lutil_timet tt;
+ time_t ret = (time_t)-1;
+
+ if ( lutil_parsetime( atm, &tm ) == 0) {
+ lutil_tm2time( &tm, &tt );
+ ret = tt.tt_sec;
+ }
+ return ret;
+}
+
+static int
+account_locked( Operation *op, Entry *e,
+ PassPolicy *pp, Modifications **mod )
+{
+ Attribute *la;
+
+ if ( (la = attr_find( e->e_attrs, ad_pwdStartTime )) != NULL ) {
+ BerVarray vals = la->a_nvals;
+ time_t then, now = op->o_time;
+
+ /*
+ * Password has a defined start of validity
+ */
+ if ( vals[0].bv_val != NULL ) {
+ if ( (then = parse_time( vals[0].bv_val )) == (time_t)-1 ) {
+ return 1;
+ }
+ if ( now < then ) {
+ return 1;
+ }
+ }
+ }
+
+ if ( (la = attr_find( e->e_attrs, ad_pwdEndTime )) != NULL ) {
+ BerVarray vals = la->a_nvals;
+ time_t then, now = op->o_time;
+
+ /*
+ * Password has a defined end of validity
+ */
+ if ( vals[0].bv_val != NULL ) {
+ if ( (then = parse_time( vals[0].bv_val )) == (time_t)-1 ) {
+ return 1;
+ }
+ if ( then <= now ) {
+ return 1;
+ }
+ }
+ }
+
+ if ( !pp->pwdLockout )
+ return 0;
+
+ if ( (la = attr_find( e->e_attrs, ad_pwdAccountTmpLockoutEnd )) != NULL ) {
+ BerVarray vals = la->a_nvals;
+ time_t then, now = op->o_time;
+
+ /*
+ * We have temporarily locked the account after a failure
+ */
+ if ( vals[0].bv_val != NULL ) {
+ if ( (then = parse_time( vals[0].bv_val )) == (time_t)-1 ) {
+ return 1;
+ }
+ if ( now < then ) {
+ return 1;
+ }
+ }
+ }
+
+ /* Only check if database maintains lastbind */
+ if ( pp->pwdMaxIdle && SLAP_LASTBIND( op->o_bd ) ) {
+ time_t lastbindtime = (time_t)-1;
+
+ la = attr_find( e->e_attrs, ad_pwdLastSuccess );
+ if ( la == NULL ) {
+ la = attr_find( e->e_attrs, ad_pwdChangedTime );
+ }
+ if ( la != NULL ) {
+ lastbindtime = parse_time( la->a_nvals[0].bv_val );
+ }
+
+ if ( lastbindtime != (time_t)-1 &&
+ op->o_time > lastbindtime + pp->pwdMaxIdle ) {
+ return 1;
+ }
+ }
+
+ if ( (la = attr_find( e->e_attrs, ad_pwdAccountLockedTime )) != NULL ) {
+ BerVarray vals = la->a_nvals;
+
+ /*
+ * there is a lockout stamp - we now need to know if it's
+ * a valid one.
+ */
+ if (vals[0].bv_val != NULL) {
+ time_t then, now;
+ Modifications *m;
+
+ if ((then = parse_time( vals[0].bv_val )) == (time_t)0)
+ return 1;
+
+ now = slap_get_time();
+
+ /* Still in the future? not yet in effect */
+ if (now < then)
+ return 0;
+
+ if (!pp->pwdLockoutDuration)
+ return 1;
+
+ if (now < then + pp->pwdLockoutDuration)
+ return 1;
+
+ if ( mod != NULL ) {
+ m = ch_calloc( sizeof(Modifications), 1 );
+ m->sml_op = LDAP_MOD_DELETE;
+ m->sml_flags = 0;
+ m->sml_type = ad_pwdAccountLockedTime->ad_cname;
+ m->sml_desc = ad_pwdAccountLockedTime;
+ m->sml_next = *mod;
+ *mod = m;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* IMPLICIT TAGS, all context-specific */
+#define PPOLICY_WARNING 0xa0L /* constructed + 0 */
+#define PPOLICY_ERROR 0x81L /* primitive + 1 */
+
+#define PPOLICY_EXPIRE 0x80L /* primitive + 0 */
+#define PPOLICY_GRACE 0x81L /* primitive + 1 */
+
+static const char ppolicy_ctrl_oid[] = LDAP_CONTROL_PASSWORDPOLICYRESPONSE;
+static const char ppolicy_account_ctrl_oid[] = LDAP_CONTROL_X_ACCOUNT_USABILITY;
+static const char ppolicy_pwd_expired_oid[] = LDAP_CONTROL_X_PASSWORD_EXPIRED;
+static const char ppolicy_pwd_expiring_oid[] = LDAP_CONTROL_X_PASSWORD_EXPIRING;
+
+static LDAPControl *
+create_passcontrol( Operation *op, int exptime, int grace, LDAPPasswordPolicyError err )
+{
+ BerElementBuffer berbuf, bb2;
+ BerElement *ber = (BerElement *) &berbuf, *b2 = (BerElement *) &bb2;
+ LDAPControl c = { 0 }, *cp;
+ struct berval bv;
+ int rc;
+
+ BER_BVZERO( &c.ldctl_value );
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+ ber_printf( ber, "{" /*}*/ );
+
+ if ( exptime >= 0 ) {
+ ber_init2( b2, NULL, LBER_USE_DER );
+ ber_printf( b2, "ti", PPOLICY_EXPIRE, exptime );
+ rc = ber_flatten2( b2, &bv, 1 );
+ (void)ber_free_buf(b2);
+ if (rc == -1) {
+ cp = NULL;
+ goto fail;
+ }
+ ber_printf( ber, "tO", PPOLICY_WARNING, &bv );
+ ch_free( bv.bv_val );
+ } else if ( grace >= 0 ) {
+ ber_init2( b2, NULL, LBER_USE_DER );
+ ber_printf( b2, "ti", PPOLICY_GRACE, grace );
+ rc = ber_flatten2( b2, &bv, 1 );
+ (void)ber_free_buf(b2);
+ if (rc == -1) {
+ cp = NULL;
+ goto fail;
+ }
+ ber_printf( ber, "tO", PPOLICY_WARNING, &bv );
+ ch_free( bv.bv_val );
+ }
+
+ if (err != PP_noError ) {
+ ber_printf( ber, "te", PPOLICY_ERROR, err );
+ }
+ ber_printf( ber, /*{*/ "N}" );
+
+ if (ber_flatten2( ber, &c.ldctl_value, 0 ) == -1) {
+ return NULL;
+ }
+ cp = op->o_tmpalloc( sizeof( LDAPControl ) + c.ldctl_value.bv_len, op->o_tmpmemctx );
+ cp->ldctl_oid = (char *)ppolicy_ctrl_oid;
+ cp->ldctl_iscritical = 0;
+ cp->ldctl_value.bv_val = (char *)&cp[1];
+ cp->ldctl_value.bv_len = c.ldctl_value.bv_len;
+ AC_MEMCPY( cp->ldctl_value.bv_val, c.ldctl_value.bv_val, c.ldctl_value.bv_len );
+fail:
+ (void)ber_free_buf(ber);
+
+ return cp;
+}
+
+static LDAPControl *
+create_passexpiry( Operation *op, int expired, int warn )
+{
+ LDAPControl *cp;
+ char buf[sizeof("-2147483648")];
+ struct berval bv = { .bv_val = buf, .bv_len = sizeof(buf) };
+
+ bv.bv_len = snprintf( bv.bv_val, bv.bv_len, "%d", warn );
+
+ cp = op->o_tmpalloc( sizeof( LDAPControl ) + bv.bv_len, op->o_tmpmemctx );
+ if ( expired ) {
+ cp->ldctl_oid = (char *)ppolicy_pwd_expired_oid;
+ } else {
+ cp->ldctl_oid = (char *)ppolicy_pwd_expiring_oid;
+ }
+ cp->ldctl_iscritical = 0;
+ cp->ldctl_value.bv_val = (char *)&cp[1];
+ cp->ldctl_value.bv_len = bv.bv_len;
+ AC_MEMCPY( cp->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
+ return cp;
+}
+
+static LDAPControl **
+add_passcontrol( Operation *op, SlapReply *rs, LDAPControl *ctrl )
+{
+ LDAPControl **ctrls, **oldctrls = rs->sr_ctrls;
+ int n;
+
+ n = 0;
+ if ( oldctrls ) {
+ for ( ; oldctrls[n]; n++ )
+ ;
+ }
+ n += 2;
+
+ ctrls = op->o_tmpcalloc( sizeof( LDAPControl * ), n, op->o_tmpmemctx );
+
+ n = 0;
+ if ( oldctrls ) {
+ for ( ; oldctrls[n]; n++ ) {
+ ctrls[n] = oldctrls[n];
+ }
+ }
+ ctrls[n] = ctrl;
+ ctrls[n+1] = NULL;
+
+ rs->sr_ctrls = ctrls;
+
+ return oldctrls;
+}
+
+static void
+add_account_control(
+ Operation *op,
+ SlapReply *rs,
+ int available,
+ int remaining,
+ LDAPAccountUsabilityMoreInfo *more_info )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *) &berbuf;
+ LDAPControl c = { 0 }, *cp = NULL, **ctrls;
+ int i = 0;
+
+ BER_BVZERO( &c.ldctl_value );
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+
+ if ( available ) {
+ ber_put_int( ber, remaining, LDAP_TAG_X_ACCOUNT_USABILITY_AVAILABLE );
+ } else {
+ assert( more_info != NULL );
+
+ ber_start_seq( ber, LDAP_TAG_X_ACCOUNT_USABILITY_NOT_AVAILABLE );
+ ber_put_boolean( ber, more_info->inactive, LDAP_TAG_X_ACCOUNT_USABILITY_INACTIVE );
+ ber_put_boolean( ber, more_info->reset, LDAP_TAG_X_ACCOUNT_USABILITY_RESET );
+ ber_put_boolean( ber, more_info->expired, LDAP_TAG_X_ACCOUNT_USABILITY_EXPIRED );
+ ber_put_int( ber, more_info->remaining_grace, LDAP_TAG_X_ACCOUNT_USABILITY_REMAINING_GRACE );
+ ber_put_int( ber, more_info->seconds_before_unlock, LDAP_TAG_X_ACCOUNT_USABILITY_UNTIL_UNLOCK );
+ ber_put_seq( ber );
+ }
+
+ if (ber_flatten2( ber, &c.ldctl_value, 0 ) == -1) {
+ goto fail;
+ }
+
+ if ( rs->sr_ctrls != NULL ) {
+ for ( ; rs->sr_ctrls[ i ] != NULL; i++ ) /* Count */;
+ }
+
+ ctrls = op->o_tmprealloc( rs->sr_ctrls, sizeof(LDAPControl *)*( i + 2 ), op->o_tmpmemctx );
+ if ( ctrls == NULL ) {
+ goto fail;
+ }
+
+ cp = op->o_tmpalloc( sizeof( LDAPControl ) + c.ldctl_value.bv_len, op->o_tmpmemctx );
+ cp->ldctl_oid = (char *)ppolicy_account_ctrl_oid;
+ cp->ldctl_iscritical = 0;
+ cp->ldctl_value.bv_val = (char *)&cp[1];
+ cp->ldctl_value.bv_len = c.ldctl_value.bv_len;
+ AC_MEMCPY( cp->ldctl_value.bv_val, c.ldctl_value.bv_val, c.ldctl_value.bv_len );
+
+ ctrls[ i ] = cp;
+ ctrls[ i + 1 ] = NULL;
+ rs->sr_ctrls = ctrls;
+
+fail:
+ (void)ber_free_buf(ber);
+}
+
+static void
+ppolicy_get_default( PassPolicy *pp )
+{
+ memset( pp, 0, sizeof(PassPolicy) );
+
+ pp->ad = slap_schema.si_ad_userPassword;
+
+ /* Users can change their own password by default */
+ pp->pwdAllowUserChange = 1;
+}
+
+
+static int
+ppolicy_get( Operation *op, Entry *e, PassPolicy *pp )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ pp_info *pi = on->on_bi.bi_private;
+ BackendDB *bd, *bd_orig = op->o_bd;
+ AttributeDescription *ad = NULL;
+ Attribute *a;
+ BerVarray vals;
+ int rc = LDAP_SUCCESS;
+ Entry *pe = NULL;
+#if 0
+ const char *text;
+#endif
+
+ ppolicy_get_default( pp );
+
+ ad = ad_pwdPolicySubentry;
+ if ( (a = attr_find( e->e_attrs, ad )) == NULL ) {
+ /*
+ * entry has no password policy assigned - use default
+ */
+ vals = &pi->def_policy;
+ if ( !vals->bv_val )
+ goto defaultpol;
+ } else {
+ vals = a->a_nvals;
+ if (vals[0].bv_val == NULL) {
+ Debug( LDAP_DEBUG_ANY,
+ "ppolicy_get: NULL value for policySubEntry\n" );
+ goto defaultpol;
+ }
+ }
+
+ op->o_bd = bd = select_backend( vals, 0 );
+ if ( op->o_bd == NULL ) {
+ op->o_bd = bd_orig;
+ goto defaultpol;
+ }
+
+ rc = be_entry_get_rw( op, vals, NULL, NULL, 0, &pe );
+ op->o_bd = bd_orig;
+
+ if ( rc ) goto defaultpol;
+
+#if 0 /* Only worry about userPassword for now */
+ if ((a = attr_find( pe->e_attrs, ad_pwdAttribute )))
+ slap_bv2ad( &a->a_vals[0], &pp->ad, &text );
+#endif
+
+ ad = ad_pwdMinAge;
+ if ( (a = attr_find( pe->e_attrs, ad ))
+ && lutil_atoi( &pp->pwdMinAge, a->a_vals[0].bv_val ) != 0 ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto defaultpol;
+ }
+
+ ad = ad_pwdMaxAge;
+ if ( (a = attr_find( pe->e_attrs, ad ))
+ && lutil_atoi( &pp->pwdMaxAge, a->a_vals[0].bv_val ) != 0 ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto defaultpol;
+ }
+
+ ad = ad_pwdMaxIdle;
+ if ( (a = attr_find( pe->e_attrs, ad ))
+ && lutil_atoi( &pp->pwdMaxIdle, a->a_vals[0].bv_val ) != 0 ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto defaultpol;
+ }
+
+ ad = ad_pwdInHistory;
+ if ( (a = attr_find( pe->e_attrs, ad ))
+ && lutil_atoi( &pp->pwdInHistory, a->a_vals[0].bv_val ) != 0 ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto defaultpol;
+ }
+
+ ad = ad_pwdCheckQuality;
+ if ( (a = attr_find( pe->e_attrs, ad ))
+ && lutil_atoi( &pp->pwdCheckQuality, a->a_vals[0].bv_val ) != 0 ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto defaultpol;
+ }
+
+ ad = ad_pwdMinLength;
+ if ( (a = attr_find( pe->e_attrs, ad ))
+ && lutil_atoi( &pp->pwdMinLength, a->a_vals[0].bv_val ) != 0 ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto defaultpol;
+ }
+
+ ad = ad_pwdMaxLength;
+ if ( (a = attr_find( pe->e_attrs, ad ))
+ && lutil_atoi( &pp->pwdMaxLength, a->a_vals[0].bv_val ) != 0 ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto defaultpol;
+ }
+
+ ad = ad_pwdMaxFailure;
+ if ( (a = attr_find( pe->e_attrs, ad ))
+ && lutil_atoi( &pp->pwdMaxFailure, a->a_vals[0].bv_val ) != 0 ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto defaultpol;
+ }
+
+ ad = ad_pwdMaxRecordedFailure;
+ if ( (a = attr_find( pe->e_attrs, ad ))
+ && lutil_atoi( &pp->pwdMaxRecordedFailure, a->a_vals[0].bv_val ) != 0 ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto defaultpol;
+ }
+
+ ad = ad_pwdGraceExpiry;
+ if ( (a = attr_find( pe->e_attrs, ad ))
+ && lutil_atoi( &pp->pwdGraceExpiry, a->a_vals[0].bv_val ) != 0 ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto defaultpol;
+ }
+
+ ad = ad_pwdGraceAuthNLimit;
+ if ( (a = attr_find( pe->e_attrs, ad ))
+ && lutil_atoi( &pp->pwdGraceAuthNLimit, a->a_vals[0].bv_val ) != 0 ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto defaultpol;
+ }
+
+ ad = ad_pwdExpireWarning;
+ if ( (a = attr_find( pe->e_attrs, ad ))
+ && lutil_atoi( &pp->pwdExpireWarning, a->a_vals[0].bv_val ) != 0 ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto defaultpol;
+ }
+
+ ad = ad_pwdFailureCountInterval;
+ if ( (a = attr_find( pe->e_attrs, ad ))
+ && lutil_atoi( &pp->pwdFailureCountInterval, a->a_vals[0].bv_val ) != 0 ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto defaultpol;
+ }
+
+ ad = ad_pwdLockoutDuration;
+ if ( (a = attr_find( pe->e_attrs, ad ))
+ && lutil_atoi( &pp->pwdLockoutDuration, a->a_vals[0].bv_val ) != 0 ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto defaultpol;
+ }
+
+ ad = ad_pwdMinDelay;
+ if ( (a = attr_find( pe->e_attrs, ad ))
+ && lutil_atoi( &pp->pwdMinDelay, a->a_vals[0].bv_val ) != 0 ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto defaultpol;
+ }
+
+ ad = ad_pwdMaxDelay;
+ if ( (a = attr_find( pe->e_attrs, ad ))
+ && lutil_atoi( &pp->pwdMaxDelay, a->a_vals[0].bv_val ) != 0 ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto defaultpol;
+ }
+
+ ad = ad_pwdCheckModule;
+ if ( (a = attr_find( pe->e_attrs, ad )) ) {
+ strncpy( pp->pwdCheckModule, a->a_vals[0].bv_val,
+ sizeof(pp->pwdCheckModule) );
+ pp->pwdCheckModule[sizeof(pp->pwdCheckModule)-1] = '\0';
+ }
+
+ ad = ad_pwdCheckModuleArg;
+ if ( (a = attr_find( pe->e_attrs, ad )) ) {
+ ber_dupbv_x( &pp->pwdCheckModuleArg, &a->a_vals[0], op->o_tmpmemctx );
+ }
+
+ ad = ad_pwdLockout;
+ if ( (a = attr_find( pe->e_attrs, ad )) )
+ pp->pwdLockout = bvmatch( &a->a_nvals[0], &slap_true_bv );
+
+ ad = ad_pwdMustChange;
+ if ( (a = attr_find( pe->e_attrs, ad )) )
+ pp->pwdMustChange = bvmatch( &a->a_nvals[0], &slap_true_bv );
+
+ ad = ad_pwdAllowUserChange;
+ if ( (a = attr_find( pe->e_attrs, ad )) )
+ pp->pwdAllowUserChange = bvmatch( &a->a_nvals[0], &slap_true_bv );
+
+ ad = ad_pwdSafeModify;
+ if ( (a = attr_find( pe->e_attrs, ad )) )
+ pp->pwdSafeModify = bvmatch( &a->a_nvals[0], &slap_true_bv );
+
+ if ( pp->pwdMaxRecordedFailure < pp->pwdMaxFailure )
+ pp->pwdMaxRecordedFailure = pp->pwdMaxFailure;
+
+ if ( !pp->pwdMaxRecordedFailure && pp->pwdMinDelay )
+ pp->pwdMaxRecordedFailure = PPOLICY_DEFAULT_MAXRECORDED_FAILURE;
+
+ if ( pp->pwdMinDelay && !pp->pwdMaxDelay ) {
+ Debug( LDAP_DEBUG_ANY, "ppolicy_get: "
+ "pwdMinDelay was set but pwdMaxDelay wasn't, assuming they "
+ "are equal\n" );
+ pp->pwdMaxDelay = pp->pwdMinDelay;
+ }
+
+ op->o_bd = bd;
+ be_entry_release_r( op, pe );
+ op->o_bd = bd_orig;
+
+ return LDAP_SUCCESS;
+
+defaultpol:
+ if ( pe ) {
+ op->o_bd = bd;
+ be_entry_release_r( op, pe );
+ op->o_bd = bd_orig;
+ }
+
+ if ( rc && !BER_BVISNULL( vals ) ) {
+ Debug( LDAP_DEBUG_ANY, "ppolicy_get: "
+ "policy subentry %s missing or invalid at '%s', "
+ "no policy will be applied!\n",
+ vals->bv_val, ad ? ad->ad_cname.bv_val : "" );
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ "ppolicy_get: using default policy\n" );
+ }
+
+ ppolicy_get_default( pp );
+
+ return -1;
+}
+
+static int
+password_scheme( struct berval *cred, struct berval *sch )
+{
+ int e;
+
+ assert( cred != NULL );
+
+ if (sch) {
+ sch->bv_val = NULL;
+ sch->bv_len = 0;
+ }
+
+ if ((cred->bv_len == 0) || (cred->bv_val == NULL) ||
+ (cred->bv_val[0] != '{')) return LDAP_OTHER;
+
+ for(e = 1; cred->bv_val[e] && cred->bv_val[e] != '}'; e++);
+ if (cred->bv_val[e]) {
+ int rc;
+ rc = lutil_passwd_scheme( cred->bv_val );
+ if (rc) {
+ if (sch) {
+ sch->bv_val = cred->bv_val;
+ sch->bv_len = e;
+ }
+ return LDAP_SUCCESS;
+ }
+ }
+ return LDAP_OTHER;
+}
+
+static int
+check_password_quality( struct berval *cred, PassPolicy *pp, LDAPPasswordPolicyError *err, Entry *e, char **txt )
+{
+ int rc = LDAP_SUCCESS, ok = LDAP_SUCCESS;
+ char *ptr;
+ struct berval sch;
+
+ assert( cred != NULL );
+ assert( pp != NULL );
+ assert( txt != NULL );
+
+ ptr = cred->bv_val;
+
+ *txt = NULL;
+
+ if ((cred->bv_len == 0) || (pp->pwdMinLength > cred->bv_len)) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ if ( err ) *err = PP_passwordTooShort;
+ return rc;
+ }
+
+ if ( pp->pwdMaxLength && cred->bv_len > pp->pwdMaxLength ) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ if ( err ) *err = PP_passwordTooLong;
+ return rc;
+ }
+
+ /*
+ * We need to know if the password is already hashed - if so
+ * what scheme is it. The reason being that the "hash" of
+ * {cleartext} still allows us to check the password.
+ */
+ rc = password_scheme( cred, &sch );
+ if (rc == LDAP_SUCCESS) {
+ if ((sch.bv_val) && (strncasecmp( sch.bv_val, "{cleartext}",
+ sch.bv_len ) == 0)) {
+ /*
+ * We can check the cleartext "hash"
+ */
+ ptr = cred->bv_val + sch.bv_len;
+ } else {
+ /* everything else, we can't check */
+ if (pp->pwdCheckQuality == 2) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ if (err) *err = PP_insufficientPasswordQuality;
+ return rc;
+ }
+ /*
+ * We can't check the syntax of the password, but it's not
+ * mandatory (according to the policy), so we return success.
+ */
+
+ return LDAP_SUCCESS;
+ }
+ }
+
+ rc = LDAP_SUCCESS;
+
+ if (pp->pwdCheckModule[0]) {
+#ifdef SLAPD_MODULES
+ lt_dlhandle mod;
+ const char *err;
+
+ if ((mod = lt_dlopen( pp->pwdCheckModule )) == NULL) {
+ err = lt_dlerror();
+
+ Debug(LDAP_DEBUG_ANY,
+ "check_password_quality: lt_dlopen failed: (%s) %s.\n",
+ pp->pwdCheckModule, err );
+ ok = LDAP_OTHER; /* internal error */
+ } else {
+ /* FIXME: the error message ought to be passed thru a
+ * struct berval, with preallocated buffer and size
+ * passed in. Module can still allocate a buffer for
+ * it if the provided one is too small.
+ */
+ int (*prog)( char *passwd, char **text, Entry *ent, struct berval *arg );
+
+ if ((prog = lt_dlsym( mod, "check_password" )) == NULL) {
+ err = lt_dlerror();
+
+ Debug(LDAP_DEBUG_ANY,
+ "check_password_quality: lt_dlsym failed: (%s) %s.\n",
+ pp->pwdCheckModule, err );
+ ok = LDAP_OTHER;
+ } else {
+ struct berval *arg = NULL;
+ if ( !BER_BVISNULL( &pp->pwdCheckModuleArg ) ) {
+ arg = &pp->pwdCheckModuleArg;
+ }
+
+ ldap_pvt_thread_mutex_lock( &chk_syntax_mutex );
+ ok = prog( ptr, txt, e, arg );
+ ldap_pvt_thread_mutex_unlock( &chk_syntax_mutex );
+ if (ok != LDAP_SUCCESS) {
+ Debug(LDAP_DEBUG_ANY,
+ "check_password_quality: module error: (%s) %s.[%d]\n",
+ pp->pwdCheckModule, *txt ? *txt : "", ok );
+ }
+ }
+
+ lt_dlclose( mod );
+ }
+#else
+ Debug(LDAP_DEBUG_ANY, "check_password_quality: external modules not "
+ "supported. pwdCheckModule ignored.\n" );
+#endif /* SLAPD_MODULES */
+ }
+
+
+ if (ok != LDAP_SUCCESS) {
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ if (err) *err = PP_insufficientPasswordQuality;
+ }
+
+ return rc;
+}
+
+static int
+parse_pwdhistory( struct berval *bv, char **oid, time_t *oldtime, struct berval *oldpw )
+{
+ char *ptr;
+ struct berval nv, npw;
+ ber_len_t i, j;
+
+ assert (bv && (bv->bv_len > 0) && (bv->bv_val) && oldtime && oldpw );
+
+ if ( oid ) {
+ *oid = 0;
+ }
+ *oldtime = (time_t)-1;
+ BER_BVZERO( oldpw );
+
+ ber_dupbv( &nv, bv );
+
+ /* first get the time field */
+ for ( i = 0; (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ )
+ ;
+ if ( i == nv.bv_len ) {
+ goto exit_failure; /* couldn't locate the '#' separator */
+ }
+ nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
+ ptr = nv.bv_val;
+ *oldtime = parse_time( ptr );
+ if (*oldtime == (time_t)-1) {
+ goto exit_failure;
+ }
+
+ /* get the OID field */
+ for (ptr = &(nv.bv_val[i]); (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ )
+ ;
+ if ( i == nv.bv_len ) {
+ goto exit_failure; /* couldn't locate the '#' separator */
+ }
+ nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
+ if ( oid ) {
+ *oid = ber_strdup( ptr );
+ }
+
+ /* get the length field */
+ for ( ptr = &(nv.bv_val[i]); (i < nv.bv_len) && (nv.bv_val[i] != '#'); i++ )
+ ;
+ if ( i == nv.bv_len ) {
+ goto exit_failure; /* couldn't locate the '#' separator */
+ }
+ nv.bv_val[i++] = '\0'; /* terminate the string & move to next field */
+ oldpw->bv_len = strtol( ptr, NULL, 10 );
+ if (errno == ERANGE) {
+ goto exit_failure;
+ }
+
+ /* lastly, get the octets of the string */
+ for ( j = i, ptr = &(nv.bv_val[i]); i < nv.bv_len; i++ )
+ ;
+ if ( i - j != oldpw->bv_len) {
+ goto exit_failure; /* length is wrong */
+ }
+
+ npw.bv_val = ptr;
+ npw.bv_len = oldpw->bv_len;
+ ber_dupbv( oldpw, &npw );
+ ber_memfree( nv.bv_val );
+
+ return LDAP_SUCCESS;
+
+exit_failure:;
+ if ( oid && *oid ) {
+ ber_memfree(*oid);
+ *oid = NULL;
+ }
+ if ( oldpw->bv_val ) {
+ ber_memfree( oldpw->bv_val);
+ BER_BVZERO( oldpw );
+ }
+ ber_memfree( nv.bv_val );
+
+ return LDAP_OTHER;
+}
+
+static void
+add_to_pwd_history( pw_hist **l, time_t t,
+ struct berval *oldpw, struct berval *bv )
+{
+ pw_hist *p, *p1, *p2;
+
+ if (!l) return;
+
+ p = ch_malloc( sizeof( pw_hist ));
+ p->pw = *oldpw;
+ ber_dupbv( &p->bv, bv );
+ p->t = t;
+ p->next = NULL;
+
+ if (*l == NULL) {
+ /* degenerate case */
+ *l = p;
+ return;
+ }
+ /*
+ * advance p1 and p2 such that p1 is the node before the
+ * new one, and p2 is the node after it
+ */
+ for (p1 = NULL, p2 = *l; p2 && p2->t <= t; p1 = p2, p2=p2->next );
+ p->next = p2;
+ if (p1 == NULL) { *l = p; return; }
+ p1->next = p;
+}
+
+#ifndef MAX_PWD_HISTORY_SZ
+#define MAX_PWD_HISTORY_SZ 1024
+#endif /* MAX_PWD_HISTORY_SZ */
+
+static void
+make_pwd_history_value( char *timebuf, struct berval *bv, Attribute *pa )
+{
+ char str[ MAX_PWD_HISTORY_SZ ];
+ int nlen;
+
+ snprintf( str, MAX_PWD_HISTORY_SZ,
+ "%s#%s#%lu#", timebuf,
+ pa->a_desc->ad_type->sat_syntax->ssyn_oid,
+ (unsigned long) pa->a_nvals[0].bv_len );
+ str[MAX_PWD_HISTORY_SZ-1] = 0;
+ nlen = strlen(str);
+
+ /*
+ * We have to assume that the string is a string of octets,
+ * not readable characters. In reality, yes, it probably is
+ * a readable (ie, base64) string, but we can't count on that
+ * Hence, while the first 3 fields of the password history
+ * are definitely readable (a timestamp, an OID and an integer
+ * length), the remaining octets of the actual password
+ * are deemed to be binary data.
+ */
+ AC_MEMCPY( str + nlen, pa->a_nvals[0].bv_val, pa->a_nvals[0].bv_len );
+ nlen += pa->a_nvals[0].bv_len;
+ bv->bv_val = ch_malloc( nlen + 1 );
+ AC_MEMCPY( bv->bv_val, str, nlen );
+ bv->bv_val[nlen] = '\0';
+ bv->bv_len = nlen;
+}
+
+static void
+free_pwd_history_list( pw_hist **l )
+{
+ pw_hist *p;
+
+ if (!l) return;
+ p = *l;
+ while (p) {
+ pw_hist *pp = p->next;
+
+ free(p->pw.bv_val);
+ free(p->bv.bv_val);
+ free(p);
+ p = pp;
+ }
+ *l = NULL;
+}
+
+typedef struct ppbind {
+ pp_info *pi;
+ BackendDB *be;
+ int send_ctrl;
+ int set_restrict;
+ LDAPControl **oldctrls;
+ Modifications *mod;
+ LDAPPasswordPolicyError pErr;
+ PassPolicy pp;
+} ppbind;
+
+static void
+ctrls_cleanup( Operation *op, SlapReply *rs, LDAPControl **oldctrls )
+{
+ int n;
+
+ assert( rs->sr_ctrls != NULL );
+ assert( rs->sr_ctrls[0] != NULL );
+
+ for ( n = 0; rs->sr_ctrls[n]; n++ ) {
+ if ( rs->sr_ctrls[n]->ldctl_oid == ppolicy_ctrl_oid ||
+ rs->sr_ctrls[n]->ldctl_oid == ppolicy_pwd_expired_oid ||
+ rs->sr_ctrls[n]->ldctl_oid == ppolicy_pwd_expiring_oid ) {
+ op->o_tmpfree( rs->sr_ctrls[n], op->o_tmpmemctx );
+ rs->sr_ctrls[n] = (LDAPControl *)(-1);
+ break;
+ }
+ }
+
+ if ( rs->sr_ctrls[n] == NULL ) {
+ /* missed? */
+ }
+
+ op->o_tmpfree( rs->sr_ctrls, op->o_tmpmemctx );
+
+ rs->sr_ctrls = oldctrls;
+}
+
+static int
+ppolicy_ctrls_cleanup( Operation *op, SlapReply *rs )
+{
+ ppbind *ppb = op->o_callback->sc_private;
+ if ( ppb->send_ctrl ) {
+ ctrls_cleanup( op, rs, ppb->oldctrls );
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+ppolicy_bind_response( Operation *op, SlapReply *rs )
+{
+ ppbind *ppb = op->o_callback->sc_private;
+ pp_info *pi = ppb->pi;
+ Modifications *mod = ppb->mod, *m;
+ int pwExpired = 0;
+ int ngut = -1, warn = -1, fc = 0, age, rc;
+ Attribute *a;
+ time_t now, pwtime = (time_t)-1;
+ struct lutil_tm now_tm;
+ struct lutil_timet now_usec;
+ char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+ char nowstr_usec[ LDAP_LUTIL_GENTIME_BUFSIZE+8 ];
+ struct berval timestamp, timestamp_usec;
+ BackendDB *be = op->o_bd;
+ LDAPControl *ctrl = NULL;
+ Entry *e;
+
+ ldap_pvt_thread_mutex_lock( &pi->pwdFailureTime_mutex );
+ /* If we already know it's locked, just get on with it */
+ if ( ppb->pErr != PP_noError ) {
+ goto locked;
+ }
+
+ op->o_bd = ppb->be;
+ rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
+ op->o_bd = be;
+
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_pvt_thread_mutex_unlock( &pi->pwdFailureTime_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* ITS#7089 Skip lockout checks/modifications if password attribute missing */
+ if ( attr_find( e->e_attrs, ppb->pp.ad ) == NULL ) {
+ goto done;
+ }
+
+ ldap_pvt_gettime(&now_tm); /* stored for later consideration */
+ lutil_tm2time(&now_tm, &now_usec);
+ now = now_usec.tt_sec;
+ timestamp.bv_val = nowstr;
+ timestamp.bv_len = sizeof(nowstr);
+ slap_timestamp( &now, &timestamp );
+
+ /* Separate timestamp for pwdFailureTime with microsecond granularity */
+ strcpy(nowstr_usec, nowstr);
+ timestamp_usec.bv_val = nowstr_usec;
+ timestamp_usec.bv_len = timestamp.bv_len;
+ snprintf( timestamp_usec.bv_val + timestamp_usec.bv_len-1, sizeof(".123456Z"), ".%06dZ", now_usec.tt_nsec / 1000 );
+ timestamp_usec.bv_len += STRLENOF(".123456");
+
+ if ( rs->sr_err == LDAP_INVALID_CREDENTIALS && ppb->pp.pwdMaxRecordedFailure ) {
+ int i = 0;
+
+ m = ch_calloc( sizeof(Modifications), 1 );
+ m->sml_op = LDAP_MOD_ADD;
+ m->sml_flags = 0;
+ m->sml_type = ad_pwdFailureTime->ad_cname;
+ m->sml_desc = ad_pwdFailureTime;
+ m->sml_numvals = 1;
+ m->sml_values = ch_calloc( sizeof(struct berval), 2 );
+ m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
+
+ ber_dupbv( &m->sml_values[0], &timestamp_usec );
+ ber_dupbv( &m->sml_nvalues[0], &timestamp_usec );
+ m->sml_next = mod;
+ mod = m;
+
+ /*
+ * Count the pwdFailureTimes - if it's
+ * greater than the policy pwdMaxFailure,
+ * then lock the account.
+ */
+ if ((a = attr_find( e->e_attrs, ad_pwdFailureTime )) != NULL) {
+ for(i=0; a->a_nvals[i].bv_val; i++) {
+
+ /*
+ * If the interval is 0, then failures
+ * stay on the record until explicitly
+ * reset by successful authentication.
+ */
+ if (ppb->pp.pwdFailureCountInterval == 0) {
+ fc++;
+ } else if (now <=
+ parse_time(a->a_nvals[i].bv_val) +
+ ppb->pp.pwdFailureCountInterval) {
+
+ fc++;
+ }
+ /*
+ * We only count those failures
+ * which are not due to expire.
+ */
+ }
+ /* Do we have too many timestamps? If so, delete some values.
+ * We don't bother to sort the values here. OpenLDAP keeps the
+ * values in order by default. Fundamentally, relying on the
+ * information here is wrong anyway; monitoring systems should
+ * be tracking Bind failures in syslog, not here.
+ */
+ if (a->a_numvals >= ppb->pp.pwdMaxRecordedFailure) {
+ int j = ppb->pp.pwdMaxRecordedFailure-1;
+ /* If more than 2x, cheaper to perform a Replace */
+ if (a->a_numvals >= 2 * ppb->pp.pwdMaxRecordedFailure) {
+ struct berval v, nv;
+
+ /* Change the mod we constructed above */
+ m->sml_op = LDAP_MOD_REPLACE;
+ m->sml_numvals = ppb->pp.pwdMaxRecordedFailure;
+ v = m->sml_values[0];
+ nv = m->sml_nvalues[0];
+ ch_free(m->sml_values);
+ ch_free(m->sml_nvalues);
+ m->sml_values = ch_calloc( sizeof(struct berval), ppb->pp.pwdMaxRecordedFailure+1 );
+ m->sml_nvalues = ch_calloc( sizeof(struct berval), ppb->pp.pwdMaxRecordedFailure+1 );
+ for (i=0; i<j; i++) {
+ ber_dupbv(&m->sml_values[i], &a->a_vals[a->a_numvals-j+i]);
+ ber_dupbv(&m->sml_nvalues[i], &a->a_nvals[a->a_numvals-j+i]);
+ }
+ m->sml_values[i] = v;
+ m->sml_nvalues[i] = nv;
+ } else {
+ /* else just delete some */
+ m = ch_calloc( sizeof(Modifications), 1 );
+ m->sml_op = LDAP_MOD_DELETE;
+ m->sml_type = ad_pwdFailureTime->ad_cname;
+ m->sml_desc = ad_pwdFailureTime;
+ m->sml_numvals = a->a_numvals - j;
+ m->sml_values = ch_calloc( sizeof(struct berval), m->sml_numvals+1 );
+ m->sml_nvalues = ch_calloc( sizeof(struct berval), m->sml_numvals+1 );
+ for (i=0; i<m->sml_numvals; i++) {
+ ber_dupbv(&m->sml_values[i], &a->a_vals[i]);
+ ber_dupbv(&m->sml_nvalues[i], &a->a_nvals[i]);
+ }
+ m->sml_next = mod;
+ mod = m;
+ }
+ }
+ }
+
+ if ((ppb->pp.pwdMaxFailure > 0) &&
+ (fc >= ppb->pp.pwdMaxFailure - 1)) {
+
+ /*
+ * We subtract 1 from the failure max
+ * because the new failure entry hasn't
+ * made it to the entry yet.
+ */
+ m = ch_calloc( sizeof(Modifications), 1 );
+ m->sml_op = LDAP_MOD_REPLACE;
+ m->sml_flags = 0;
+ m->sml_type = ad_pwdAccountLockedTime->ad_cname;
+ m->sml_desc = ad_pwdAccountLockedTime;
+ m->sml_numvals = 1;
+ m->sml_values = ch_calloc( sizeof(struct berval), 2 );
+ m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
+ ber_dupbv( &m->sml_values[0], &timestamp );
+ ber_dupbv( &m->sml_nvalues[0], &timestamp );
+ m->sml_next = mod;
+ mod = m;
+ } else if ( ppb->pp.pwdMinDelay ) {
+ int waittime = ppb->pp.pwdMinDelay << fc;
+ time_t wait_end;
+ struct berval lockout_stamp;
+
+ if ( waittime > ppb->pp.pwdMaxDelay ) {
+ waittime = ppb->pp.pwdMaxDelay;
+ }
+ wait_end = now + waittime;
+
+ slap_timestamp( &wait_end, &lockout_stamp );
+
+ m = ch_calloc( sizeof(Modifications), 1 );
+ m->sml_op = LDAP_MOD_REPLACE;
+ m->sml_flags = 0;
+ m->sml_type = ad_pwdAccountTmpLockoutEnd->ad_cname;
+ m->sml_desc = ad_pwdAccountTmpLockoutEnd;
+ m->sml_numvals = 1;
+ m->sml_values = ch_calloc( sizeof(struct berval), 2 );
+ m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
+ ber_dupbv( &m->sml_values[0], &lockout_stamp );
+ ber_dupbv( &m->sml_nvalues[0], &lockout_stamp );
+ m->sml_next = mod;
+ mod = m;
+ }
+ } else if ( rs->sr_err == LDAP_SUCCESS ) {
+ if ((a = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
+ pwtime = parse_time( a->a_nvals[0].bv_val );
+
+ /* delete all pwdFailureTimes */
+ if ( attr_find( e->e_attrs, ad_pwdFailureTime )) {
+ m = ch_calloc( sizeof(Modifications), 1 );
+ m->sml_op = LDAP_MOD_DELETE;
+ m->sml_flags = 0;
+ m->sml_type = ad_pwdFailureTime->ad_cname;
+ m->sml_desc = ad_pwdFailureTime;
+ m->sml_next = mod;
+ mod = m;
+ }
+
+ /*
+ * check to see if the password must be changed
+ */
+ if ( ppb->pp.pwdMustChange &&
+ (a = attr_find( e->e_attrs, ad_pwdReset )) &&
+ bvmatch( &a->a_nvals[0], &slap_true_bv ) )
+ {
+ /*
+ * need to inject client controls here to give
+ * more information. For the moment, we ensure
+ * that we are disallowed from doing anything
+ * other than change password.
+ */
+ if ( ppb->set_restrict ) {
+ ber_dupbv( &pwcons[op->o_conn->c_conn_idx].dn,
+ &op->o_conn->c_ndn );
+ }
+
+ ppb->pErr = PP_changeAfterReset;
+
+ } else {
+ /*
+ * the password does not need to be changed, so
+ * we now check whether the password has expired.
+ *
+ * We can skip this bit if passwords don't age in
+ * the policy. Also, if there was no pwdChangedTime
+ * attribute in the entry, the password never expires.
+ */
+ if (ppb->pp.pwdMaxAge == 0) goto grace;
+
+ if (pwtime != (time_t)-1) {
+ /*
+ * Check: was the last change time of
+ * the password older than the maximum age
+ * allowed. (Ignore case 2 from I-D, it's just silly.)
+ */
+ if (now - pwtime > ppb->pp.pwdMaxAge ) pwExpired = 1;
+ }
+ }
+
+grace:
+ if (!pwExpired) goto check_expiring_password;
+
+ if ( ppb->pp.pwdGraceExpiry &&
+ now - pwtime > ppb->pp.pwdMaxAge + ppb->pp.pwdGraceExpiry ) {
+ /* Grace logins have expired now */
+ ngut = 0;
+ } else if ((a = attr_find( e->e_attrs, ad_pwdGraceUseTime )) == NULL) {
+ ngut = ppb->pp.pwdGraceAuthNLimit;
+ } else {
+ for(ngut=0; a->a_nvals[ngut].bv_val; ngut++);
+ ngut = ppb->pp.pwdGraceAuthNLimit - ngut;
+ }
+
+ /*
+ * ngut is the number of remaining grace logins
+ */
+ Debug( LDAP_DEBUG_ANY,
+ "ppolicy_bind: Entry %s has an expired password: %d grace logins\n",
+ e->e_name.bv_val, ngut );
+
+ ngut--;
+
+ if (ngut < 0) {
+ ppb->pErr = PP_passwordExpired;
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ /*
+ * Add a grace user time to the entry
+ */
+ m = ch_calloc( sizeof(Modifications), 1 );
+ m->sml_op = LDAP_MOD_ADD;
+ m->sml_flags = 0;
+ m->sml_type = ad_pwdGraceUseTime->ad_cname;
+ m->sml_desc = ad_pwdGraceUseTime;
+ m->sml_numvals = 1;
+ m->sml_values = ch_calloc( sizeof(struct berval), 2 );
+ m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
+ ber_dupbv( &m->sml_values[0], &timestamp_usec );
+ ber_dupbv( &m->sml_nvalues[0], &timestamp_usec );
+ m->sml_next = mod;
+ mod = m;
+
+check_expiring_password:
+ /*
+ * Now we need to check to see
+ * if it is about to expire, and if so, should the user
+ * be warned about it in the password policy control.
+ *
+ * If the password has expired, and we're in the grace period, then
+ * we don't need to do this bit. Similarly, if we don't have password
+ * aging, then there's no need to do this bit either.
+ *
+ * If pwdtime is -1 there is no password Change Time attribute on the
+ * entry so we skip the expiry check.
+ *
+ */
+ if ((ppb->pp.pwdMaxAge < 1) || (pwExpired) || (ppb->pp.pwdExpireWarning < 1) ||
+ (pwtime == -1))
+ goto done;
+
+ age = (int)(now - pwtime);
+
+ /*
+ * We know that there is a password Change Time attribute - if
+ * there wasn't, then the pwdExpired value would be true, unless
+ * there is no password aging - and if there is no password aging,
+ * then this section isn't called anyway - you can't have an
+ * expiring password if there's no limit to expire.
+ */
+ if (ppb->pp.pwdMaxAge - age < ppb->pp.pwdExpireWarning ) {
+ /*
+ * Set the warning value.
+ */
+ warn = ppb->pp.pwdMaxAge - age; /* seconds left until expiry */
+ if (warn < 0) warn = 0; /* something weird here - why is pwExpired not set? */
+
+ Debug( LDAP_DEBUG_TRACE,
+ "ppolicy_bind: Setting warning for password expiry for %s = %d seconds\n",
+ op->o_req_dn.bv_val, warn );
+ }
+ }
+
+done:
+ op->o_bd = ppb->be;
+ be_entry_release_r( op, e );
+ op->o_bd = be;
+
+locked:
+ if ( mod && !pi->disable_write ) {
+ Operation op2 = *op;
+ SlapReply r2 = { REP_RESULT };
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+ LDAPControl c, *ca[2];
+
+ op2.o_tag = LDAP_REQ_MODIFY;
+ op2.o_callback = &cb;
+ op2.orm_modlist = mod;
+ op2.orm_no_opattrs = 0;
+ op2.o_dn = op->o_bd->be_rootdn;
+ op2.o_ndn = op->o_bd->be_rootndn;
+
+ /* If this server is a shadow and forward_updates is true,
+ * use the frontend to perform this modify. That will trigger
+ * the update referral, which can then be forwarded by the
+ * chain overlay. Obviously the updateref and chain overlay
+ * must be configured appropriately for this to be useful.
+ */
+ if ( SLAP_SHADOW( op->o_bd ) && pi->forward_updates ) {
+ op2.o_bd = frontendDB;
+
+ /* Must use Relax control since these are no-user-mod */
+ op2.o_relax = SLAP_CONTROL_CRITICAL;
+ op2.o_ctrls = ca;
+ ca[0] = &c;
+ ca[1] = NULL;
+ BER_BVZERO( &c.ldctl_value );
+ c.ldctl_iscritical = 1;
+ c.ldctl_oid = LDAP_CONTROL_RELAX;
+ } else {
+ /* If not forwarding, don't update opattrs and don't replicate */
+ if ( SLAP_SINGLE_SHADOW( op->o_bd )) {
+ op2.orm_no_opattrs = 1;
+ op2.o_dont_replicate = 1;
+ }
+ op2.o_bd = ppb->be;
+ }
+ rc = op2.o_bd->be_modify( &op2, &r2 );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s ppolicy_bind_response: "
+ "ppolicy state change failed with rc=%d text=%s\n",
+ op->o_log_prefix, rc, r2.sr_text );
+ }
+ }
+ if ( mod ) {
+ slap_mods_free( mod, 1 );
+ }
+
+ if ( ppb->send_ctrl ) {
+
+ /* Do we really want to tell that the account is locked? */
+ if ( ppb->pErr == PP_accountLocked && !pi->use_lockout ) {
+ ppb->pErr = PP_noError;
+ }
+ ctrl = create_passcontrol( op, warn, ngut, ppb->pErr );
+ } else if ( pi->send_netscape_controls ) {
+ if ( ppb->pErr != PP_noError || pwExpired ) {
+ ctrl = create_passexpiry( op, 1, 0 );
+ } else if ( warn > 0 ) {
+ ctrl = create_passexpiry( op, 0, warn );
+ }
+ }
+ if ( ctrl ) {
+ ppb->oldctrls = add_passcontrol( op, rs, ctrl );
+ op->o_callback->sc_cleanup = ppolicy_ctrls_cleanup;
+ }
+ ldap_pvt_thread_mutex_unlock( &pi->pwdFailureTime_mutex );
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+ppolicy_bind( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+
+ /* Reset lockout status on all Bind requests */
+ if ( !BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) {
+ ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
+ BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
+ }
+
+ /* Root bypasses policy */
+ if ( !be_isroot_dn( op->o_bd, &op->o_req_ndn )) {
+ Entry *e;
+ int rc;
+ ppbind *ppb;
+ slap_callback *cb;
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback),
+ 1, op->o_tmpmemctx );
+ ppb = (ppbind *)(cb+1);
+ ppb->pi = on->on_bi.bi_private;
+ ppb->be = op->o_bd->bd_self;
+ ppb->pErr = PP_noError;
+ ppb->set_restrict = 1;
+
+ /* Setup a callback so we can munge the result */
+
+ cb->sc_response = ppolicy_bind_response;
+ cb->sc_private = ppb;
+ overlay_callback_after_backover( op, cb, 1 );
+
+ /* Did we receive a password policy request control? */
+ if ( op->o_ctrlflag[ppolicy_cid] ) {
+ ppb->send_ctrl = 1;
+ }
+
+ op->o_bd->bd_info = (BackendInfo *)on;
+
+ if ( ppolicy_get( op, e, &ppb->pp ) == LDAP_SUCCESS ) {
+ rc = account_locked( op, e, &ppb->pp, &ppb->mod );
+ }
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ be_entry_release_r( op, e );
+
+ if ( rc ) {
+ ppb->pErr = PP_accountLocked;
+ send_ldap_error( op, rs, LDAP_INVALID_CREDENTIALS, NULL );
+ return rs->sr_err;
+ }
+
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+/* Reset the restricted info for the next session on this connection */
+static int
+ppolicy_connection_destroy( BackendDB *bd, Connection *conn )
+{
+ if ( pwcons && !BER_BVISEMPTY( &pwcons[conn->c_conn_idx].dn )) {
+ ch_free( pwcons[conn->c_conn_idx].dn.bv_val );
+ BER_BVZERO( &pwcons[conn->c_conn_idx].dn );
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+/* Check if this connection is restricted */
+static int
+ppolicy_restrict(
+ Operation *op,
+ SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ int send_ctrl = 0;
+
+ /* Did we receive a password policy request control? */
+ if ( op->o_ctrlflag[ppolicy_cid] ) {
+ send_ctrl = 1;
+ }
+
+ if ( op->o_conn && !BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) {
+ LDAPControl **oldctrls;
+ /* if the current authcDN doesn't match the one we recorded,
+ * then an intervening Bind has succeeded and the restriction
+ * no longer applies. (ITS#4516)
+ */
+ if ( !dn_match( &op->o_conn->c_ndn,
+ &pwcons[op->o_conn->c_conn_idx].dn )) {
+ ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
+ BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
+ return SLAP_CB_CONTINUE;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "connection restricted to password changing only\n" );
+ if ( send_ctrl ) {
+ LDAPControl *ctrl = NULL;
+ ctrl = create_passcontrol( op, -1, -1, PP_changeAfterReset );
+ oldctrls = add_passcontrol( op, rs, ctrl );
+ }
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, LDAP_INSUFFICIENT_ACCESS,
+ "Operations are restricted to bind/unbind/abandon/StartTLS/modify password" );
+ if ( send_ctrl ) {
+ ctrls_cleanup( op, rs, oldctrls );
+ }
+ return rs->sr_err;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+ppolicy_account_usability_entry_cb( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = op->o_callback->sc_private;
+ BackendInfo *bi = op->o_bd->bd_info;
+ LDAPControl *ctrl = NULL;
+ PassPolicy pp;
+ Attribute *a;
+ Entry *e = NULL;
+ time_t pwtime = 0, seconds_until_expiry = -1, now = op->o_time;
+ int isExpired = 0, grace = -1;
+
+ if ( rs->sr_type != REP_SEARCH ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( be_entry_get_rw( op, &rs->sr_entry->e_nname, NULL, NULL, 0, &e ) != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ op->o_bd->bd_info = (BackendInfo *)on;
+
+ if ( ppolicy_get( op, e, &pp ) != LDAP_SUCCESS ) {
+ /* TODO: If there is no policy, should we check if */
+ goto done;
+ }
+
+ if ( !access_allowed( op, e, pp.ad, NULL, ACL_COMPARE, NULL ) ) {
+ goto done;
+ }
+
+ if ( attr_find( e->e_attrs, pp.ad ) == NULL ) {
+ goto done;
+ }
+
+ if ((a = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL) {
+ pwtime = parse_time( a->a_nvals[0].bv_val );
+ }
+
+ if ( pp.pwdMaxAge && pwtime ) {
+ seconds_until_expiry = pwtime + pp.pwdMaxAge - now;
+ if ( seconds_until_expiry <= 0 ) isExpired = 1;
+ if ( pp.pwdGraceAuthNLimit ) {
+ if ( !pp.pwdGraceExpiry || seconds_until_expiry + pp.pwdGraceExpiry > 0 ) {
+ grace = pp.pwdGraceAuthNLimit;
+ if ( attr_find( e->e_attrs, ad_pwdGraceUseTime ) ) {
+ grace -= a->a_numvals;
+ }
+ }
+ }
+ }
+ if ( !isExpired && pp.pwdMaxIdle && (a = attr_find( e->e_attrs, ad_pwdLastSuccess )) ) {
+ time_t lastbindtime = pwtime;
+
+ if ( (a = attr_find( e->e_attrs, ad_pwdLastSuccess )) != NULL ) {
+ lastbindtime = parse_time( a->a_nvals[0].bv_val );
+ }
+
+ if ( lastbindtime ) {
+ int remaining_idle = lastbindtime + pp.pwdMaxIdle - now;
+ if ( remaining_idle <= 0 ) {
+ isExpired = 1;
+ } else if ( seconds_until_expiry == -1 || remaining_idle < seconds_until_expiry ) {
+ seconds_until_expiry = remaining_idle;
+ }
+ }
+ }
+
+ if ( isExpired || account_locked( op, e, &pp, NULL ) ) {
+ LDAPAccountUsabilityMoreInfo more_info = { 0, 0, 0, -1, -1 };
+ time_t then, lockoutEnd = 0;
+
+ if ( isExpired ) more_info.remaining_grace = grace;
+
+ if ( (a = attr_find( e->e_attrs, ad_pwdAccountLockedTime )) != NULL ) {
+ then = parse_time( a->a_vals[0].bv_val );
+ if ( then == 0 )
+ lockoutEnd = -1;
+
+ /* Still in the future? not yet in effect */
+ if ( now < then )
+ then = 0;
+
+ if ( !pp.pwdLockoutDuration )
+ lockoutEnd = -1;
+
+ if ( now < then + pp.pwdLockoutDuration )
+ lockoutEnd = then + pp.pwdLockoutDuration;
+ }
+
+ if ( (a = attr_find( e->e_attrs, ad_pwdAccountTmpLockoutEnd )) != NULL ) {
+ then = parse_time( a->a_vals[0].bv_val );
+ if ( lockoutEnd != -1 && then > lockoutEnd )
+ lockoutEnd = then;
+ }
+
+ if ( lockoutEnd > now ) {
+ more_info.inactive = 1;
+ more_info.seconds_before_unlock = lockoutEnd - now;
+ }
+
+ if ( pp.pwdMustChange &&
+ (a = attr_find( e->e_attrs, ad_pwdReset )) &&
+ bvmatch( &a->a_nvals[0], &slap_true_bv ) )
+ {
+ more_info.reset = 1;
+ }
+
+ add_account_control( op, rs, 0, -1, &more_info );
+ } else {
+ add_account_control( op, rs, 1, seconds_until_expiry, NULL );
+ }
+
+done:
+ op->o_bd->bd_info = bi;
+ if ( e ) {
+ be_entry_release_r( op, e );
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+ppolicy_search(
+ Operation *op,
+ SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ int rc = ppolicy_restrict( op, rs );
+
+ if ( rc != SLAP_CB_CONTINUE ) {
+ return rc;
+ }
+
+ if ( op->o_ctrlflag[account_usability_cid] ) {
+ slap_callback *cb;
+
+ cb = op->o_tmpcalloc( sizeof(slap_callback), 1, op->o_tmpmemctx );
+
+ cb->sc_response = ppolicy_account_usability_entry_cb;
+ cb->sc_private = on;
+ overlay_callback_after_backover( op, cb, 1 );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+ppolicy_compare_response(
+ Operation *op,
+ SlapReply *rs )
+{
+ /* map compare responses to bind responses */
+ if ( rs->sr_err == LDAP_COMPARE_TRUE )
+ rs->sr_err = LDAP_SUCCESS;
+ else if ( rs->sr_err == LDAP_COMPARE_FALSE )
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+
+ ppolicy_bind_response( op, rs );
+
+ /* map back to compare */
+ if ( rs->sr_err == LDAP_SUCCESS )
+ rs->sr_err = LDAP_COMPARE_TRUE;
+ else if ( rs->sr_err == LDAP_INVALID_CREDENTIALS )
+ rs->sr_err = LDAP_COMPARE_FALSE;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+ppolicy_compare(
+ Operation *op,
+ SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+
+ if ( ppolicy_restrict( op, rs ) != SLAP_CB_CONTINUE )
+ return rs->sr_err;
+
+ /* Did we receive a password policy request control?
+ * Are we testing the userPassword?
+ */
+ if ( op->o_ctrlflag[ppolicy_cid] &&
+ op->orc_ava->aa_desc == slap_schema.si_ad_userPassword ) {
+ Entry *e;
+ int rc;
+ ppbind *ppb;
+ slap_callback *cb;
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback),
+ 1, op->o_tmpmemctx );
+ ppb = (ppbind *)(cb+1);
+ ppb->pi = on->on_bi.bi_private;
+ ppb->be = op->o_bd->bd_self;
+ ppb->pErr = PP_noError;
+ ppb->send_ctrl = 1;
+ /* failures here don't lockout the connection */
+ ppb->set_restrict = 0;
+
+ /* Setup a callback so we can munge the result */
+
+ cb->sc_response = ppolicy_compare_response;
+ cb->sc_private = ppb;
+ overlay_callback_after_backover( op, cb, 1 );
+
+ op->o_bd->bd_info = (BackendInfo *)on;
+
+ if ( ppolicy_get( op, e, &ppb->pp ) == LDAP_SUCCESS ) {
+ rc = account_locked( op, e, &ppb->pp, &ppb->mod );
+ }
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ be_entry_release_r( op, e );
+
+ if ( rc ) {
+ ppb->pErr = PP_accountLocked;
+ send_ldap_error( op, rs, LDAP_COMPARE_FALSE, NULL );
+ return rs->sr_err;
+ }
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+ppolicy_add(
+ Operation *op,
+ SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ pp_info *pi = on->on_bi.bi_private;
+ PassPolicy pp;
+ Attribute *pa;
+ const char *txt;
+ int is_pwdadmin = 0;
+
+ if ( ppolicy_restrict( op, rs ) != SLAP_CB_CONTINUE )
+ return rs->sr_err;
+
+ /* If this is a replica, assume the provider checked everything */
+ if ( SLAPD_SYNC_IS_SYNCCONN( op->o_connid ) )
+ return SLAP_CB_CONTINUE;
+
+ ppolicy_get( op, op->ora_e, &pp );
+
+ if ( access_allowed( op, op->ora_e, pp.ad, NULL, ACL_MANAGE, NULL ) ) {
+ is_pwdadmin = 1;
+ }
+
+ /* Check for password in entry */
+ if ( (pa = attr_find( op->oq_add.rs_e->e_attrs, pp.ad )) ) {
+ assert( pa->a_vals != NULL );
+ assert( !BER_BVISNULL( &pa->a_vals[ 0 ] ) );
+
+ if ( !BER_BVISNULL( &pa->a_vals[ 1 ] ) ) {
+ send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION, "Password policy only allows one password value" );
+ return rs->sr_err;
+ }
+
+ /*
+ * new entry contains a password - if we're not the password admin
+ * then we need to check that the password fits in with the
+ * security policy for the new entry.
+ */
+
+ if ( pp.pwdCheckQuality > 0 && !is_pwdadmin ) {
+ struct berval *bv = &(pa->a_vals[0]);
+ int rc, send_ctrl = 0;
+ LDAPPasswordPolicyError pErr = PP_noError;
+ char *txt;
+
+ /* Did we receive a password policy request control? */
+ if ( op->o_ctrlflag[ppolicy_cid] ) {
+ send_ctrl = 1;
+ }
+ rc = check_password_quality( bv, &pp, &pErr, op->ora_e, &txt );
+ if (rc != LDAP_SUCCESS) {
+ LDAPControl **oldctrls = NULL;
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ if ( send_ctrl ) {
+ LDAPControl *ctrl = NULL;
+ ctrl = create_passcontrol( op, -1, -1, pErr );
+ oldctrls = add_passcontrol( op, rs, ctrl );
+ }
+ send_ldap_error( op, rs, rc, txt ? txt : "Password fails quality checking policy" );
+ if ( txt ) {
+ free( txt );
+ }
+ if ( send_ctrl ) {
+ ctrls_cleanup( op, rs, oldctrls );
+ }
+ return rs->sr_err;
+ }
+ }
+ /*
+ * A controversial bit. We hash cleartext
+ * passwords provided via add and modify operations
+ * You're not really supposed to do this, since
+ * the X.500 model says "store attributes" as they
+ * get provided. By default, this is what we do
+ *
+ * But if the hash_passwords flag is set, we hash
+ * any cleartext password attribute values via the
+ * default password hashing scheme.
+ */
+ if ((pi->hash_passwords) &&
+ (password_scheme( &(pa->a_vals[0]), NULL ) != LDAP_SUCCESS)) {
+ struct berval hpw;
+
+ slap_passwd_hash( &(pa->a_vals[0]), &hpw, &txt );
+ if (hpw.bv_val == NULL) {
+ /*
+ * hashing didn't work. Emit an error.
+ */
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = txt;
+ send_ldap_error( op, rs, LDAP_OTHER, "Password hashing failed" );
+ return rs->sr_err;
+ }
+
+ memset( pa->a_vals[0].bv_val, 0, pa->a_vals[0].bv_len);
+ ber_memfree( pa->a_vals[0].bv_val );
+ pa->a_vals[0].bv_val = hpw.bv_val;
+ pa->a_vals[0].bv_len = hpw.bv_len;
+ }
+
+ /* If password aging is in effect, set the pwdChangedTime */
+ if ( ( pp.pwdMaxAge || pp.pwdMinAge ) &&
+ !attr_find( op->ora_e->e_attrs, ad_pwdChangedTime ) ) {
+ struct berval timestamp;
+ char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+ time_t now = slap_get_time();
+
+ timestamp.bv_val = timebuf;
+ timestamp.bv_len = sizeof(timebuf);
+ slap_timestamp( &now, &timestamp );
+
+ attr_merge_one( op->ora_e, ad_pwdChangedTime, &timestamp, &timestamp );
+ }
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+ppolicy_mod_cb( Operation *op, SlapReply *rs )
+{
+ slap_callback *sc = op->o_callback;
+ op->o_callback = sc->sc_next;
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
+ BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
+ }
+ op->o_tmpfree( sc, op->o_tmpmemctx );
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+ppolicy_text_cleanup( Operation *op, SlapReply *rs )
+{
+ slap_callback *sc = op->o_callback;
+
+ if ( rs->sr_text == sc->sc_private ) {
+ rs->sr_text = NULL;
+ }
+ free( sc->sc_private );
+
+ op->o_callback = sc->sc_next;
+ op->o_tmpfree( sc, op->o_tmpmemctx );
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+ppolicy_modify( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ pp_info *pi = on->on_bi.bi_private;
+ int i, rc, mod_pw_only, pwmod = 0, pwmop = -1, deladd,
+ hsize = 0, hskip;
+ PassPolicy pp;
+ Modifications *mods = NULL, *modtail = NULL,
+ *ml, *delmod, *addmod;
+ Attribute *pa, *ha, at;
+ const char *txt;
+ pw_hist *tl = NULL, *p;
+ int zapReset, send_ctrl = 0, free_txt = 0;
+ Entry *e;
+ struct berval newpw = BER_BVNULL, oldpw = BER_BVNULL,
+ *bv, cr[2];
+ LDAPPasswordPolicyError pErr = PP_noError;
+ LDAPControl *ctrl = NULL;
+ LDAPControl **oldctrls = NULL;
+ int is_pwdexop = 0, is_pwdadmin = 0;
+ int got_del_grace = 0, got_del_lock = 0, got_pw = 0, got_del_fail = 0,
+ got_del_success = 0;
+ int got_changed = 0, got_history = 0;
+ int have_policy = 0;
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
+ op->o_bd->bd_info = (BackendInfo *)on;
+
+ if ( rc != LDAP_SUCCESS ) return SLAP_CB_CONTINUE;
+ if ( pi->disable_write ) return SLAP_CB_CONTINUE;
+
+ /* If this is a replica, we may need to tweak some of the
+ * provider's modifications. Otherwise, just pass it through.
+ */
+ if ( SLAPD_SYNC_IS_SYNCCONN( op->o_connid ) ) {
+ Modifications **prev;
+ Attribute *a_grace, *a_lock, *a_fail, *a_success;
+
+ a_grace = attr_find( e->e_attrs, ad_pwdGraceUseTime );
+ a_lock = attr_find( e->e_attrs, ad_pwdAccountLockedTime );
+ a_fail = attr_find( e->e_attrs, ad_pwdFailureTime );
+ a_success = attr_find( e->e_attrs, ad_pwdLastSuccess );
+
+ for( prev = &op->orm_modlist, ml = *prev; ml; ml = *prev ) {
+
+ if ( ml->sml_desc == slap_schema.si_ad_userPassword )
+ got_pw = 1;
+
+ /* If we're deleting an attr that didn't exist,
+ * drop this delete op
+ */
+ if ( ml->sml_op == LDAP_MOD_DELETE ||
+ ml->sml_op == SLAP_MOD_SOFTDEL ) {
+ int drop = 0;
+
+ if ( ml->sml_desc == ad_pwdGraceUseTime ) {
+ if ( !a_grace || got_del_grace ) {
+ drop = ml->sml_op == LDAP_MOD_DELETE;
+ } else {
+ got_del_grace = 1;
+ }
+ } else
+ if ( ml->sml_desc == ad_pwdAccountLockedTime ) {
+ if ( !a_lock || got_del_lock ) {
+ drop = ml->sml_op == LDAP_MOD_DELETE;
+ } else {
+ got_del_lock = 1;
+ }
+ } else
+ if ( ml->sml_desc == ad_pwdFailureTime ) {
+ if ( !a_fail || got_del_fail ) {
+ drop = ml->sml_op == LDAP_MOD_DELETE;
+ } else {
+ got_del_fail = 1;
+ }
+ }
+ if ( ml->sml_desc == ad_pwdLastSuccess ) {
+ if ( !a_success || got_del_success ) {
+ drop = ml->sml_op == LDAP_MOD_DELETE;
+ } else {
+ got_del_success = 1;
+ }
+ }
+ if ( drop ) {
+ *prev = ml->sml_next;
+ ml->sml_next = NULL;
+ slap_mods_free( ml, 1 );
+ continue;
+ }
+ }
+ prev = &ml->sml_next;
+ }
+
+ /* If we're resetting the password, make sure grace, accountlock,
+ * success, and failure also get removed.
+ */
+ if ( got_pw ) {
+ if ( a_grace && !got_del_grace ) {
+ ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
+ ml->sml_op = LDAP_MOD_DELETE;
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+ ml->sml_type.bv_val = NULL;
+ ml->sml_desc = ad_pwdGraceUseTime;
+ ml->sml_numvals = 0;
+ ml->sml_values = NULL;
+ ml->sml_nvalues = NULL;
+ ml->sml_next = NULL;
+ *prev = ml;
+ prev = &ml->sml_next;
+ }
+ if ( a_lock && !got_del_lock ) {
+ ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
+ ml->sml_op = LDAP_MOD_DELETE;
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+ ml->sml_type.bv_val = NULL;
+ ml->sml_desc = ad_pwdAccountLockedTime;
+ ml->sml_numvals = 0;
+ ml->sml_values = NULL;
+ ml->sml_nvalues = NULL;
+ ml->sml_next = NULL;
+ *prev = ml;
+ }
+ if ( a_fail && !got_del_fail ) {
+ ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
+ ml->sml_op = LDAP_MOD_DELETE;
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+ ml->sml_type.bv_val = NULL;
+ ml->sml_desc = ad_pwdFailureTime;
+ ml->sml_numvals = 0;
+ ml->sml_values = NULL;
+ ml->sml_nvalues = NULL;
+ ml->sml_next = NULL;
+ *prev = ml;
+ }
+ if ( a_success && !got_del_success ) {
+ ml = (Modifications *) ch_malloc( sizeof( Modifications ) );
+ ml->sml_op = LDAP_MOD_DELETE;
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+ ml->sml_type.bv_val = NULL;
+ ml->sml_desc = ad_pwdLastSuccess;
+ ml->sml_numvals = 0;
+ ml->sml_values = NULL;
+ ml->sml_nvalues = NULL;
+ ml->sml_next = NULL;
+ *prev = ml;
+ }
+ }
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ be_entry_release_r( op, e );
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* Did we receive a password policy request control? */
+ if ( op->o_ctrlflag[ppolicy_cid] ) {
+ send_ctrl = 1;
+ }
+
+ /* See if this is a pwdModify exop. If so, we can
+ * access the plaintext passwords from that request.
+ */
+ {
+ slap_callback *sc;
+
+ for ( sc = op->o_callback; sc; sc=sc->sc_next ) {
+ if ( sc->sc_response == slap_null_cb &&
+ sc->sc_private ) {
+ req_pwdexop_s *qpw = sc->sc_private;
+ newpw = qpw->rs_new;
+ oldpw = qpw->rs_old;
+ is_pwdexop = 1;
+ break;
+ }
+ }
+ }
+
+ /* ppolicy_hash_cleartext depends on pwmod being determined first */
+ if ( ppolicy_get( op, e, &pp ) == LDAP_SUCCESS ) {
+ have_policy = 1;
+ }
+
+ if ( access_allowed( op, e, pp.ad, NULL, ACL_MANAGE, NULL ) ) {
+ is_pwdadmin = 1;
+ }
+
+ for ( ml = op->orm_modlist,
+ pwmod = 0, mod_pw_only = 1,
+ deladd = 0, delmod = NULL,
+ addmod = NULL,
+ zapReset = 1;
+ ml != NULL; modtail = ml, ml = ml->sml_next )
+ {
+ if ( ml->sml_desc == pp.ad ) {
+ pwmod = 1;
+ pwmop = ml->sml_op;
+ if ((deladd == 0) && (ml->sml_op == LDAP_MOD_DELETE) &&
+ (ml->sml_values) && !BER_BVISNULL( &ml->sml_values[0] ))
+ {
+ deladd = 1;
+ delmod = ml;
+ }
+
+ if ((ml->sml_op == LDAP_MOD_ADD) ||
+ (ml->sml_op == LDAP_MOD_REPLACE))
+ {
+ if ( ml->sml_values && !BER_BVISNULL( &ml->sml_values[0] )) {
+ if ( deladd == 1 )
+ deladd = 2;
+
+ /* FIXME: there's no easy way to ensure
+ * that add does not cause multiple
+ * userPassword values; one way (that
+ * would be consistent with the single
+ * password constraint) would be to turn
+ * add into replace); another would be
+ * to disallow add.
+ *
+ * Let's check at least that a single value
+ * is being added
+ */
+ if ( addmod || !BER_BVISNULL( &ml->sml_values[ 1 ] ) ) {
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ rs->sr_text = "Password policy only allows one password value";
+ goto return_results;
+ }
+
+ addmod = ml;
+ } else {
+ /* replace can have no values, add cannot */
+ assert( ml->sml_op == LDAP_MOD_REPLACE );
+ }
+ }
+
+ } else if ( !(ml->sml_flags & SLAP_MOD_INTERNAL) && !is_at_operational( ml->sml_desc->ad_type ) ) {
+ mod_pw_only = 0;
+ /* modifying something other than password */
+ }
+
+ /*
+ * If there is a request to explicitly add a pwdReset
+ * attribute, then we suppress the normal behaviour on
+ * password change, which is to remove the pwdReset
+ * attribute.
+ *
+ * This enables an administrator to assign a new password
+ * and place a "must reset" flag on the entry, which will
+ * stay until the user explicitly changes his/her password.
+ */
+ if (ml->sml_desc == ad_pwdReset ) {
+ if ((ml->sml_op == LDAP_MOD_ADD) ||
+ (ml->sml_op == LDAP_MOD_REPLACE))
+ zapReset = 0;
+ }
+ if ( ml->sml_op == LDAP_MOD_DELETE ) {
+ if ( ml->sml_desc == ad_pwdGraceUseTime ) {
+ got_del_grace = 1;
+ } else if ( ml->sml_desc == ad_pwdAccountLockedTime ) {
+ got_del_lock = 1;
+ } else if ( ml->sml_desc == ad_pwdFailureTime ) {
+ got_del_fail = 1;
+ } else if ( ml->sml_desc == ad_pwdLastSuccess ) {
+ got_del_success = 1;
+ }
+ }
+ if ( ml->sml_desc == ad_pwdChangedTime ) {
+ got_changed = 1;
+ } else if (ml->sml_desc == ad_pwdHistory ) {
+ got_history = 1;
+ }
+ }
+
+ if (!BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn ) && !mod_pw_only ) {
+ if ( dn_match( &op->o_conn->c_ndn,
+ &pwcons[op->o_conn->c_conn_idx].dn )) {
+ Debug( LDAP_DEBUG_TRACE,
+ "connection restricted to password changing only\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "Operations are restricted to bind/unbind/abandon/StartTLS/modify password";
+ pErr = PP_changeAfterReset;
+ goto return_results;
+ } else {
+ ch_free( pwcons[op->o_conn->c_conn_idx].dn.bv_val );
+ BER_BVZERO( &pwcons[op->o_conn->c_conn_idx].dn );
+ }
+ }
+
+ /*
+ * if we have a "safe password modify policy", then we need to check if we're doing
+ * a delete (with the old password), followed by an add (with the new password).
+ *
+ * If we got just a delete with nothing else, just let it go. We also skip all the checks if
+ * the root user is bound. Root can do anything, including avoid the policies.
+ */
+
+ if (!have_policy || !pwmod) goto do_modify;
+
+ /*
+ * Build the password history list in ascending time order
+ * We need this, even if the user is root, in order to maintain
+ * the pwdHistory operational attributes properly.
+ */
+ if (addmod && pp.pwdInHistory > 0 && (ha = attr_find( e->e_attrs, ad_pwdHistory ))) {
+ struct berval oldpw;
+ time_t oldtime;
+
+ for(i=0; ha->a_nvals[i].bv_val; i++) {
+ rc = parse_pwdhistory( &(ha->a_nvals[i]), NULL,
+ &oldtime, &oldpw );
+
+ if (rc != LDAP_SUCCESS) continue; /* invalid history entry */
+
+ if (oldpw.bv_val) {
+ add_to_pwd_history( &tl, oldtime, &oldpw,
+ &(ha->a_nvals[i]) );
+ oldpw.bv_val = NULL;
+ oldpw.bv_len = 0;
+ }
+ }
+ for(p=tl; p; p=p->next, hsize++); /* count history size */
+ }
+
+ if (is_pwdadmin) goto do_modify;
+
+ /* NOTE: according to draft-behera-ldap-password-policy
+ * pwdAllowUserChange == FALSE must only prevent pwd changes
+ * by the user the pwd belongs to (ITS#7021) */
+ if (!pp.pwdAllowUserChange && dn_match(&op->o_req_ndn, &op->o_ndn)) {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "User alteration of password is not allowed";
+ pErr = PP_passwordModNotAllowed;
+ goto return_results;
+ }
+
+ /* Just deleting? */
+ if (!addmod) {
+ /* skip everything else */
+ pwmod = 0;
+ goto do_modify;
+ }
+
+ /* This is a pwdModify exop that provided the old pw.
+ * We need to create a Delete mod for this old pw and
+ * let the matching value get found later
+ */
+ if (pp.pwdSafeModify && oldpw.bv_val ) {
+ ml = (Modifications *)ch_calloc( sizeof( Modifications ), 1 );
+ ml->sml_op = LDAP_MOD_DELETE;
+ ml->sml_flags = SLAP_MOD_INTERNAL;
+ ml->sml_desc = pp.ad;
+ ml->sml_type = pp.ad->ad_cname;
+ ml->sml_numvals = 1;
+ ml->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
+ ber_dupbv( &ml->sml_values[0], &oldpw );
+ BER_BVZERO( &ml->sml_values[1] );
+ ml->sml_next = op->orm_modlist;
+ op->orm_modlist = ml;
+ delmod = ml;
+ deladd = 2;
+ }
+
+ if (pp.pwdSafeModify && deladd != 2) {
+ Debug( LDAP_DEBUG_TRACE,
+ "change password must use DELETE followed by ADD/REPLACE\n" );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "Must supply old password to be changed as well as new one";
+ pErr = PP_mustSupplyOldPassword;
+ goto return_results;
+ }
+
+ /* Check age, but only if pwdReset is not TRUE */
+ pa = attr_find( e->e_attrs, ad_pwdReset );
+ if ((!pa || !bvmatch( &pa->a_nvals[0], &slap_true_bv )) &&
+ pp.pwdMinAge > 0) {
+ time_t pwtime = (time_t)-1, now;
+ int age;
+
+ if ((pa = attr_find( e->e_attrs, ad_pwdChangedTime )) != NULL)
+ pwtime = parse_time( pa->a_nvals[0].bv_val );
+ now = slap_get_time();
+ age = (int)(now - pwtime);
+ if ((pwtime != (time_t)-1) && (age < pp.pwdMinAge)) {
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ rs->sr_text = "Password is too young to change";
+ pErr = PP_passwordTooYoung;
+ goto return_results;
+ }
+ }
+
+ /* pa is used in password history check below, be sure it's set */
+ if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL && delmod) {
+ /*
+ * we have a password to check
+ */
+ bv = oldpw.bv_val ? &oldpw : delmod->sml_values;
+ /* FIXME: no access checking? */
+ rc = slap_passwd_check( op, NULL, pa, bv, &txt );
+ if (rc != LDAP_SUCCESS) {
+ Debug( LDAP_DEBUG_TRACE,
+ "old password check failed: %s\n", txt );
+
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "Must supply correct old password to change to new one";
+ pErr = PP_mustSupplyOldPassword;
+ goto return_results;
+
+ } else {
+ int i;
+
+ /*
+ * replace the delete value with the (possibly hashed)
+ * value which is currently in the password.
+ */
+ for ( i = 0; !BER_BVISNULL( &delmod->sml_values[i] ); i++ ) {
+ free( delmod->sml_values[i].bv_val );
+ BER_BVZERO( &delmod->sml_values[i] );
+ }
+ free( delmod->sml_values );
+ delmod->sml_values = ch_calloc( sizeof(struct berval), 2 );
+ BER_BVZERO( &delmod->sml_values[1] );
+ ber_dupbv( &(delmod->sml_values[0]), &(pa->a_nvals[0]) );
+ }
+ }
+
+ bv = newpw.bv_val ? &newpw : &addmod->sml_values[0];
+ if (pp.pwdCheckQuality > 0) {
+
+ rc = check_password_quality( bv, &pp, &pErr, e, (char **)&txt );
+ if (rc != LDAP_SUCCESS) {
+ rs->sr_err = rc;
+ if ( txt ) {
+ rs->sr_text = txt;
+ free_txt = 1;
+ } else {
+ rs->sr_text = "Password fails quality checking policy";
+ }
+ goto return_results;
+ }
+ }
+
+ /* If pwdInHistory is zero, passwords may be reused */
+ if (pa && pp.pwdInHistory > 0) {
+ /*
+ * Last check - the password history.
+ */
+ /* FIXME: no access checking? */
+ if (slap_passwd_check( op, NULL, pa, bv, &txt ) == LDAP_SUCCESS) {
+ /*
+ * This is bad - it means that the user is attempting
+ * to set the password to the same as the old one.
+ */
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ rs->sr_text = "Password is not being changed from existing value";
+ pErr = PP_passwordInHistory;
+ goto return_results;
+ }
+
+ /* We need this when reduce pwdInHistory */
+ hskip = hsize - pp.pwdInHistory;
+
+ /*
+ * Iterate through the password history, and fail on any
+ * password matches.
+ */
+ at = *pa;
+ at.a_vals = cr;
+ cr[1].bv_val = NULL;
+ for(p=tl; p; p=p->next) {
+ if(hskip > 0){
+ hskip--;
+ continue;
+ }
+ cr[0] = p->pw;
+ /* FIXME: no access checking? */
+ rc = slap_passwd_check( op, NULL, &at, bv, &txt );
+
+ if (rc != LDAP_SUCCESS) continue;
+
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ rs->sr_text = "Password is in history of old passwords";
+ pErr = PP_passwordInHistory;
+ goto return_results;
+ }
+ }
+
+do_modify:
+ if (pwmod) {
+ struct berval timestamp;
+ char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+ time_t now = slap_get_time();
+
+ /* If the conn is restricted, set a callback to clear it
+ * if the pwmod succeeds
+ */
+ if (!BER_BVISEMPTY( &pwcons[op->o_conn->c_conn_idx].dn )) {
+ slap_callback *sc = op->o_tmpcalloc( 1, sizeof( slap_callback ),
+ op->o_tmpmemctx );
+ sc->sc_next = op->o_callback;
+ /* Must use sc_response to insure we reset on success, before
+ * the client sees the response. Must use sc_cleanup to insure
+ * that it gets cleaned up if sc_response is not called.
+ */
+ sc->sc_response = ppolicy_mod_cb;
+ sc->sc_cleanup = ppolicy_mod_cb;
+ op->o_callback = sc;
+ }
+
+ /*
+ * keep the necessary pwd.. operational attributes
+ * up to date.
+ */
+
+ if (!got_changed) {
+ timestamp.bv_val = timebuf;
+ timestamp.bv_len = sizeof(timebuf);
+ slap_timestamp( &now, &timestamp );
+
+ mods = NULL;
+ if (pwmop != LDAP_MOD_DELETE) {
+ mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
+ mods->sml_op = LDAP_MOD_REPLACE;
+ mods->sml_numvals = 1;
+ mods->sml_values = (BerVarray) ch_calloc( sizeof( struct berval ), 2 );
+ mods->sml_nvalues = (BerVarray) ch_calloc( sizeof( struct berval ), 2 );
+
+ ber_dupbv( &mods->sml_values[0], &timestamp );
+ ber_dupbv( &mods->sml_nvalues[0], &timestamp );
+ } else if (attr_find(e->e_attrs, ad_pwdChangedTime )) {
+ mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
+ mods->sml_op = LDAP_MOD_DELETE;
+ }
+ if (mods) {
+ mods->sml_desc = ad_pwdChangedTime;
+ mods->sml_flags = SLAP_MOD_INTERNAL;
+ mods->sml_next = NULL;
+ modtail->sml_next = mods;
+ modtail = mods;
+ }
+ }
+
+ if (!got_del_grace && attr_find(e->e_attrs, ad_pwdGraceUseTime )) {
+ mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
+ mods->sml_op = LDAP_MOD_DELETE;
+ mods->sml_desc = ad_pwdGraceUseTime;
+ mods->sml_flags = SLAP_MOD_INTERNAL;
+ mods->sml_next = NULL;
+ modtail->sml_next = mods;
+ modtail = mods;
+ }
+
+ if (!got_del_lock && attr_find(e->e_attrs, ad_pwdAccountLockedTime )) {
+ mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
+ mods->sml_op = LDAP_MOD_DELETE;
+ mods->sml_desc = ad_pwdAccountLockedTime;
+ mods->sml_flags = SLAP_MOD_INTERNAL;
+ mods->sml_next = NULL;
+ modtail->sml_next = mods;
+ modtail = mods;
+ }
+
+ if (!got_del_fail && attr_find(e->e_attrs, ad_pwdFailureTime )) {
+ mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
+ mods->sml_op = LDAP_MOD_DELETE;
+ mods->sml_desc = ad_pwdFailureTime;
+ mods->sml_flags = SLAP_MOD_INTERNAL;
+ mods->sml_next = NULL;
+ modtail->sml_next = mods;
+ modtail = mods;
+ }
+
+ if ( zapReset ) {
+ /*
+ * ITS#7084 Is this a modification by the password
+ * administrator? Then force a reset if configured.
+ * Otherwise clear it.
+ */
+ if ( pp.pwdMustChange && is_pwdadmin ) {
+ mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
+ mods->sml_op = LDAP_MOD_REPLACE;
+ mods->sml_desc = ad_pwdReset;
+ mods->sml_flags = SLAP_MOD_INTERNAL;
+ mods->sml_numvals = 1;
+ mods->sml_values = (BerVarray) ch_calloc( sizeof( struct berval ), 2 );
+ mods->sml_nvalues = (BerVarray) ch_calloc( sizeof( struct berval ), 2 );
+
+ ber_dupbv( &mods->sml_values[0], (struct berval *)&slap_true_bv );
+ ber_dupbv( &mods->sml_nvalues[0], (struct berval *)&slap_true_bv );
+
+ mods->sml_next = NULL;
+ modtail->sml_next = mods;
+ modtail = mods;
+ } else if ( attr_find( e->e_attrs, ad_pwdReset ) ) {
+ mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
+ mods->sml_op = LDAP_MOD_DELETE;
+ mods->sml_desc = ad_pwdReset;
+ mods->sml_flags = SLAP_MOD_INTERNAL;
+ mods->sml_next = NULL;
+ modtail->sml_next = mods;
+ modtail = mods;
+ }
+ }
+
+ /* TODO: do we remove pwdLastSuccess or set it to 'now'? */
+ if (!got_del_success && attr_find(e->e_attrs, ad_pwdLastSuccess )){
+ mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
+ mods->sml_op = LDAP_MOD_DELETE;
+ mods->sml_flags = SLAP_MOD_INTERNAL;
+ mods->sml_desc = ad_pwdLastSuccess;
+ mods->sml_next = NULL;
+ modtail->sml_next = mods;
+ modtail = mods;
+ }
+
+ /* Delete all pwdInHistory attribute */
+ if (!got_history && pp.pwdInHistory == 0 &&
+ attr_find(e->e_attrs, ad_pwdHistory )){
+ mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
+ mods->sml_op = LDAP_MOD_DELETE;
+ mods->sml_flags = SLAP_MOD_INTERNAL;
+ mods->sml_desc = ad_pwdHistory;
+ mods->sml_next = NULL;
+ modtail->sml_next = mods;
+ modtail = mods;
+ }
+
+ if (!got_history && pp.pwdInHistory > 0){
+ if (hsize >= pp.pwdInHistory) {
+ /*
+ * We use the >= operator, since we are going to add
+ * the existing password attribute value into the
+ * history - thus the cardinality of history values is
+ * about to rise by one.
+ *
+ * If this would push it over the limit of history
+ * values (remembering - the password policy could have
+ * changed since the password was last altered), we must
+ * delete at least 1 value from the pwdHistory list.
+ *
+ * In fact, we delete '(#pwdHistory attrs - max pwd
+ * history length) + 1' values, starting with the oldest.
+ * This is easily evaluated, since the linked list is
+ * created in ascending time order.
+ */
+ mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
+ mods->sml_op = LDAP_MOD_DELETE;
+ mods->sml_flags = SLAP_MOD_INTERNAL;
+ mods->sml_desc = ad_pwdHistory;
+ mods->sml_numvals = hsize - pp.pwdInHistory + 1;
+ mods->sml_values = ch_calloc( sizeof( struct berval ),
+ hsize - pp.pwdInHistory + 2 );
+ BER_BVZERO( &mods->sml_values[ hsize - pp.pwdInHistory + 1 ] );
+ for(i=0,p=tl; i < (hsize - pp.pwdInHistory + 1); i++, p=p->next) {
+ BER_BVZERO( &mods->sml_values[i] );
+ ber_dupbv( &(mods->sml_values[i]), &p->bv );
+ }
+ mods->sml_next = NULL;
+ modtail->sml_next = mods;
+ modtail = mods;
+ }
+ free_pwd_history_list( &tl );
+
+ /*
+ * Now add the existing password into the history list.
+ * This will be executed even if the operation is to delete
+ * the password entirely.
+ *
+ * This isn't in the spec explicitly, but it seems to make
+ * sense that the password history list is the list of all
+ * previous passwords - even if they were deleted. Thus, if
+ * someone tries to add a historical password at some future
+ * point, it will fail.
+ */
+ if ((pa = attr_find( e->e_attrs, pp.ad )) != NULL) {
+ mods = (Modifications *) ch_malloc( sizeof( Modifications ) );
+ mods->sml_op = LDAP_MOD_ADD;
+ mods->sml_flags = SLAP_MOD_INTERNAL;
+ mods->sml_type.bv_val = NULL;
+ mods->sml_desc = ad_pwdHistory;
+ mods->sml_nvalues = NULL;
+ mods->sml_numvals = 1;
+ mods->sml_values = ch_calloc( sizeof( struct berval ), 2 );
+ mods->sml_values[ 1 ].bv_val = NULL;
+ mods->sml_values[ 1 ].bv_len = 0;
+ make_pwd_history_value( timebuf, &mods->sml_values[0], pa );
+ mods->sml_next = NULL;
+ modtail->sml_next = mods;
+ modtail = mods;
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ "ppolicy_modify: password attr lookup failed\n" );
+ }
+ }
+
+ /*
+ * Controversial bit here. If the new password isn't hashed
+ * (ie, is cleartext), we probably should hash it according
+ * to the default hash. The reason for this is that we want
+ * to use the policy if possible, but if we hash the password
+ * before, then we're going to run into trouble when it
+ * comes time to check the password.
+ *
+ * Now, the right thing to do is to use the extended password
+ * modify operation, but not all software can do this,
+ * therefore it makes sense to hash the new password, now
+ * we know it passes the policy requirements.
+ *
+ * Of course, if the password is already hashed, then we
+ * leave it alone.
+ */
+
+ if ((pi->hash_passwords) && (addmod) && !newpw.bv_val &&
+ (password_scheme( &(addmod->sml_values[0]), NULL ) != LDAP_SUCCESS))
+ {
+ struct berval hpw, bv;
+
+ slap_passwd_hash( &(addmod->sml_values[0]), &hpw, &txt );
+ if (hpw.bv_val == NULL) {
+ /*
+ * hashing didn't work. Emit an error.
+ */
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = txt;
+ goto return_results;
+ }
+ bv = addmod->sml_values[0];
+ /* clear and discard the clear password */
+ memset(bv.bv_val, 0, bv.bv_len);
+ ber_memfree(bv.bv_val);
+ addmod->sml_values[0] = hpw;
+ }
+ } else {
+ /* ITS#8762 Make sure we drop pwdFailureTime if unlocking */
+ if (got_del_lock && !got_del_fail && attr_find(e->e_attrs, ad_pwdFailureTime )) {
+ mods = (Modifications *) ch_calloc( sizeof( Modifications ), 1 );
+ mods->sml_op = LDAP_MOD_DELETE;
+ mods->sml_desc = ad_pwdFailureTime;
+ mods->sml_flags = SLAP_MOD_INTERNAL;
+ mods->sml_next = NULL;
+ modtail->sml_next = mods;
+ modtail = mods;
+ }
+ }
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ be_entry_release_r( op, e );
+ return SLAP_CB_CONTINUE;
+
+return_results:
+ free_pwd_history_list( &tl );
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ be_entry_release_r( op, e );
+ if ( send_ctrl ) {
+ ctrl = create_passcontrol( op, -1, -1, pErr );
+ oldctrls = add_passcontrol( op, rs, ctrl );
+ }
+ send_ldap_result( op, rs );
+ if ( free_txt ) {
+ if ( is_pwdexop ) {
+ slap_callback *cb;
+ cb = op->o_tmpcalloc( sizeof(ppbind)+sizeof(slap_callback),
+ 1, op->o_tmpmemctx );
+
+ /* Setup a callback so we can free the text when sent */
+ cb->sc_cleanup = ppolicy_text_cleanup;
+ cb->sc_private = (void *)txt;
+ overlay_callback_after_backover( op, cb, 1 );
+ } else {
+ if ( rs->sr_text == txt ) {
+ rs->sr_text = NULL;
+ }
+ free( (char *)txt );
+ }
+ }
+ if ( send_ctrl ) {
+ if ( is_pwdexop ) {
+ if ( rs->sr_flags & REP_CTRLS_MUSTBEFREED ) {
+ op->o_tmpfree( oldctrls, op->o_tmpmemctx );
+ }
+ oldctrls = NULL;
+ rs->sr_flags |= REP_CTRLS_MUSTBEFREED;
+
+ } else {
+ ctrls_cleanup( op, rs, oldctrls );
+ }
+ }
+ return rs->sr_err;
+}
+
+static int
+ppolicy_parseCtrl(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "passwordPolicyRequest control value not absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+ op->o_ctrlflag[ppolicy_cid] = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+ppolicy_au_parseCtrl(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "account usability control value not absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+ op->o_ctrlflag[account_usability_cid] = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+attrPretty(
+ Syntax *syntax,
+ struct berval *val,
+ struct berval *out,
+ void *ctx )
+{
+ AttributeDescription *ad = NULL;
+ const char *err;
+ int code;
+
+ code = slap_bv2ad( val, &ad, &err );
+ if ( !code ) {
+ ber_dupbv_x( out, &ad->ad_type->sat_cname, ctx );
+ }
+ return code;
+}
+
+static int
+attrNormalize(
+ slap_mask_t use,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *out,
+ void *ctx )
+{
+ AttributeDescription *ad = NULL;
+ const char *err;
+ int code;
+
+ code = slap_bv2ad( val, &ad, &err );
+ if ( !code ) {
+ ber_str2bv_x( ad->ad_type->sat_oid, 0, 1, out, ctx );
+ }
+ return code;
+}
+
+static int
+ppolicy_db_init(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ pp_info *pi;
+
+ if ( SLAP_ISGLOBALOVERLAY( be ) ) {
+ /* do not allow slapo-ppolicy to be global by now (ITS#5858) */
+ if ( cr ){
+ snprintf( cr->msg, sizeof(cr->msg),
+ "slapo-ppolicy cannot be global" );
+ Debug( LDAP_DEBUG_ANY, "%s\n", cr->msg );
+ }
+ return 1;
+ }
+
+ pi = on->on_bi.bi_private = ch_calloc( sizeof(pp_info), 1 );
+
+ if ( !pwcons ) {
+ /* accommodate for c_conn_idx == -1 */
+ pwcons = ch_calloc( sizeof(pw_conn), dtblsize + 1 );
+ pwcons++;
+ }
+
+ ov_count++;
+
+ ldap_pvt_thread_mutex_init( &pi->pwdFailureTime_mutex );
+
+ return 0;
+}
+
+static int
+ppolicy_db_open(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ int rc;
+
+ if ( (rc = overlay_register_control( be, LDAP_CONTROL_X_ACCOUNT_USABILITY )) != LDAP_SUCCESS ) {
+ return rc;
+ }
+ return overlay_register_control( be, LDAP_CONTROL_PASSWORDPOLICYREQUEST );
+}
+
+static int
+ppolicy_db_close(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+#ifdef SLAP_CONFIG_DELETE
+ overlay_unregister_control( be, LDAP_CONTROL_PASSWORDPOLICYREQUEST );
+ overlay_unregister_control( be, LDAP_CONTROL_X_ACCOUNT_USABILITY );
+#endif /* SLAP_CONFIG_DELETE */
+
+ return 0;
+}
+
+static int
+ppolicy_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ pp_info *pi = on->on_bi.bi_private;
+
+ on->on_bi.bi_private = NULL;
+ ldap_pvt_thread_mutex_destroy( &pi->pwdFailureTime_mutex );
+ free( pi->def_policy.bv_val );
+ free( pi );
+
+ ov_count--;
+ if ( ov_count <=0 && pwcons ) {
+ pw_conn *pwc = pwcons;
+ pwcons = NULL;
+ pwc--;
+ ch_free( pwc );
+ }
+ return 0;
+}
+
+static char *extops[] = {
+ LDAP_EXOP_MODIFY_PASSWD,
+ NULL
+};
+
+static slap_overinst ppolicy;
+
+int ppolicy_initialize()
+{
+ int i, code;
+
+ for (i=0; pwd_OpSchema[i].def; i++) {
+ code = register_at( pwd_OpSchema[i].def, pwd_OpSchema[i].ad, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ppolicy_initialize: register_at failed\n" );
+ return code;
+ }
+ /* Allow Manager to set these as needed */
+ if ( is_at_no_user_mod( (*pwd_OpSchema[i].ad)->ad_type )) {
+ (*pwd_OpSchema[i].ad)->ad_type->sat_flags |=
+ SLAP_AT_MANAGEABLE;
+ }
+ }
+ ad_pwdLastSuccess = slap_schema.si_ad_pwdLastSuccess;
+ {
+ Syntax *syn;
+ MatchingRule *mr;
+
+ syn = ch_malloc( sizeof( Syntax ));
+ *syn = *ad_pwdAttribute->ad_type->sat_syntax;
+ syn->ssyn_pretty = attrPretty;
+ ad_pwdAttribute->ad_type->sat_syntax = syn;
+
+ mr = ch_malloc( sizeof( MatchingRule ));
+ *mr = *ad_pwdAttribute->ad_type->sat_equality;
+ mr->smr_normalize = attrNormalize;
+ ad_pwdAttribute->ad_type->sat_equality = mr;
+ }
+
+ for (i=0; pwd_ocs[i]; i++) {
+ code = register_oc( pwd_ocs[i], NULL, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY, "ppolicy_initialize: "
+ "register_oc failed\n" );
+ return code;
+ }
+ }
+
+ code = register_supported_control( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
+ SLAP_CTRL_ADD|SLAP_CTRL_BIND|SLAP_CTRL_MODIFY, extops,
+ ppolicy_parseCtrl, &ppolicy_cid );
+ if ( code != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "Failed to register control %d\n", code );
+ return code;
+ }
+
+ code = register_supported_control( LDAP_CONTROL_X_ACCOUNT_USABILITY,
+ SLAP_CTRL_SEARCH, NULL,
+ ppolicy_au_parseCtrl, &account_usability_cid );
+ if ( code != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "Failed to register control %d\n", code );
+ return code;
+ }
+
+ /* We don't expect to receive these controls, only send them */
+ code = register_supported_control( LDAP_CONTROL_X_PASSWORD_EXPIRED,
+ 0, NULL, NULL, NULL );
+ if ( code != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "Failed to register control %d\n", code );
+ return code;
+ }
+
+ code = register_supported_control( LDAP_CONTROL_X_PASSWORD_EXPIRING,
+ 0, NULL, NULL, NULL );
+ if ( code != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "Failed to register control %d\n", code );
+ return code;
+ }
+
+ ldap_pvt_thread_mutex_init( &chk_syntax_mutex );
+
+ ppolicy.on_bi.bi_type = "ppolicy";
+ ppolicy.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ ppolicy.on_bi.bi_db_init = ppolicy_db_init;
+ ppolicy.on_bi.bi_db_open = ppolicy_db_open;
+ ppolicy.on_bi.bi_db_close = ppolicy_db_close;
+ ppolicy.on_bi.bi_db_destroy = ppolicy_db_destroy;
+
+ ppolicy.on_bi.bi_op_add = ppolicy_add;
+ ppolicy.on_bi.bi_op_bind = ppolicy_bind;
+ ppolicy.on_bi.bi_op_compare = ppolicy_compare;
+ ppolicy.on_bi.bi_op_delete = ppolicy_restrict;
+ ppolicy.on_bi.bi_op_modify = ppolicy_modify;
+ ppolicy.on_bi.bi_op_search = ppolicy_search;
+ ppolicy.on_bi.bi_connection_destroy = ppolicy_connection_destroy;
+
+ ppolicy.on_bi.bi_cf_ocs = ppolicyocs;
+ code = config_register_schema( ppolicycfg, ppolicyocs );
+ if ( code ) return code;
+
+ return overlay_register( &ppolicy );
+}
+
+#if SLAPD_OVER_PPOLICY == SLAPD_MOD_DYNAMIC
+int init_module(int argc, char *argv[]) {
+ return ppolicy_initialize();
+}
+#endif
+
+#endif /* defined(SLAPD_OVER_PPOLICY) */
diff --git a/servers/slapd/overlays/refint.c b/servers/slapd/overlays/refint.c
new file mode 100644
index 0000000..75d0360
--- /dev/null
+++ b/servers/slapd/overlays/refint.c
@@ -0,0 +1,1097 @@
+/* refint.c - referential integrity module */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2004 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Symas Corp. for inclusion in
+ * OpenLDAP Software. This work was sponsored by Hewlett-Packard.
+ */
+
+#include "portable.h"
+
+/* This module maintains referential integrity for a set of
+ * DN-valued attributes by searching for all references to a given
+ * DN whenever the DN is changed or its entry is deleted, and making
+ * the appropriate update.
+ *
+ * Updates are performed using the database rootdn in a separate task
+ * to allow the original operation to complete immediately.
+ */
+
+#ifdef SLAPD_OVER_REFINT
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "ldap_rq.h"
+
+static slap_overinst refint;
+
+/* The DN to use in the ModifiersName for all refint updates */
+static BerValue refint_dn = BER_BVC("cn=Referential Integrity Overlay");
+static BerValue refint_ndn = BER_BVC("cn=referential integrity overlay");
+
+typedef struct refint_attrs_s {
+ struct refint_attrs_s *next;
+ AttributeDescription *attr;
+ BerVarray old_vals;
+ BerVarray old_nvals;
+ BerVarray new_vals;
+ BerVarray new_nvals;
+ int ra_numvals;
+ int dont_empty;
+} refint_attrs;
+
+typedef struct dependents_s {
+ struct dependents_s *next;
+ BerValue dn; /* target dn */
+ BerValue ndn;
+ refint_attrs *attrs;
+} dependent_data;
+
+typedef struct refint_q {
+ struct refint_q *next;
+ struct refint_data_s *rdata;
+ dependent_data *attrs; /* entries and attrs returned from callback */
+ BackendDB *db;
+ BerValue olddn;
+ BerValue oldndn;
+ BerValue newdn;
+ BerValue newndn;
+ int do_sub;
+} refint_q;
+
+typedef struct refint_data_s {
+ struct refint_attrs_s *attrs; /* list of known attrs */
+ BerValue dn; /* basedn in parent, */
+ BerValue nothing; /* the nothing value, if needed */
+ BerValue nnothing; /* normalized nothingness */
+ BerValue refint_dn; /* modifier's name */
+ BerValue refint_ndn; /* normalized modifier's name */
+ struct re_s *qtask;
+ refint_q *qhead;
+ refint_q *qtail;
+ BackendDB *db;
+ ldap_pvt_thread_mutex_t qmutex;
+} refint_data;
+
+typedef struct refint_pre_s {
+ slap_overinst *on;
+ int do_sub;
+} refint_pre;
+
+#define RUNQ_INTERVAL 36000 /* a long time */
+
+static MatchingRule *mr_dnSubtreeMatch;
+
+enum {
+ REFINT_ATTRS = 1,
+ REFINT_NOTHING,
+ REFINT_MODIFIERSNAME
+};
+
+static ConfigDriver refint_cf_gen;
+
+static ConfigTable refintcfg[] = {
+ { "refint_attributes", "attribute...", 2, 0, 0,
+ ARG_MAGIC|REFINT_ATTRS, refint_cf_gen,
+ "( OLcfgOvAt:11.1 NAME 'olcRefintAttribute' "
+ "DESC 'Attributes for referential integrity' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "refint_nothing", "string", 2, 2, 0,
+ ARG_DN|ARG_QUOTE|ARG_MAGIC|REFINT_NOTHING, refint_cf_gen,
+ "( OLcfgOvAt:11.2 NAME 'olcRefintNothing' "
+ "DESC 'Replacement DN to supply when needed' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
+ { "refint_modifiersName", "DN", 2, 2, 0,
+ ARG_DN|ARG_QUOTE|ARG_MAGIC|REFINT_MODIFIERSNAME, refint_cf_gen,
+ "( OLcfgOvAt:11.3 NAME 'olcRefintModifiersName' "
+ "DESC 'The DN to use as modifiersName' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs refintocs[] = {
+ { "( OLcfgOvOc:11.1 "
+ "NAME 'olcRefintConfig' "
+ "DESC 'Referential integrity configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcRefintAttribute "
+ "$ olcRefintNothing "
+ "$ olcRefintModifiersName "
+ ") )",
+ Cft_Overlay, refintcfg },
+ { NULL, 0, NULL }
+};
+
+static int
+refint_cf_gen(ConfigArgs *c)
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ refint_data *dd = (refint_data *)on->on_bi.bi_private;
+ refint_attrs *ip, *pip, **pipp = NULL;
+ AttributeDescription *ad;
+ const char *text;
+ int rc = ARG_BAD_CONF;
+ int i;
+
+ switch ( c->op ) {
+ case SLAP_CONFIG_EMIT:
+ switch ( c->type ) {
+ case REFINT_ATTRS:
+ ip = dd->attrs;
+ while ( ip ) {
+ value_add_one( &c->rvalue_vals,
+ &ip->attr->ad_cname );
+ ip = ip->next;
+ }
+ rc = 0;
+ break;
+ case REFINT_NOTHING:
+ if ( !BER_BVISEMPTY( &dd->nothing )) {
+ rc = value_add_one( &c->rvalue_vals,
+ &dd->nothing );
+ if ( rc ) return rc;
+ rc = value_add_one( &c->rvalue_nvals,
+ &dd->nnothing );
+ return rc;
+ }
+ rc = 0;
+ break;
+ case REFINT_MODIFIERSNAME:
+ if ( !BER_BVISEMPTY( &dd->refint_dn )) {
+ rc = value_add_one( &c->rvalue_vals,
+ &dd->refint_dn );
+ if ( rc ) return rc;
+ rc = value_add_one( &c->rvalue_nvals,
+ &dd->refint_ndn );
+ return rc;
+ }
+ rc = 0;
+ break;
+ default:
+ abort ();
+ }
+ break;
+ case LDAP_MOD_DELETE:
+ switch ( c->type ) {
+ case REFINT_ATTRS:
+ pipp = &dd->attrs;
+ if ( c->valx < 0 ) {
+ ip = *pipp;
+ *pipp = NULL;
+ while ( ip ) {
+ pip = ip;
+ ip = ip->next;
+ ch_free ( pip );
+ }
+ } else {
+ /* delete from linked list */
+ for ( i=0; i < c->valx; ++i ) {
+ pipp = &(*pipp)->next;
+ }
+ ip = *pipp;
+ *pipp = (*pipp)->next;
+
+ /* AttributeDescriptions are global so
+ * shouldn't be freed here... */
+ ch_free ( ip );
+ }
+ rc = 0;
+ break;
+ case REFINT_NOTHING:
+ ch_free( dd->nothing.bv_val );
+ ch_free( dd->nnothing.bv_val );
+ BER_BVZERO( &dd->nothing );
+ BER_BVZERO( &dd->nnothing );
+ rc = 0;
+ break;
+ case REFINT_MODIFIERSNAME:
+ ch_free( dd->refint_dn.bv_val );
+ ch_free( dd->refint_ndn.bv_val );
+ BER_BVZERO( &dd->refint_dn );
+ BER_BVZERO( &dd->refint_ndn );
+ rc = 0;
+ break;
+ default:
+ abort ();
+ }
+ break;
+ case SLAP_CONFIG_ADD:
+ /* fallthru to LDAP_MOD_ADD */
+ case LDAP_MOD_ADD:
+ switch ( c->type ) {
+ case REFINT_ATTRS:
+ rc = 0;
+ if ( c->op != SLAP_CONFIG_ADD && c->argc > 2 ) {
+ /* We wouldn't know how to delete these values later */
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "Supplying multiple names in a single %s value is "
+ "unsupported and will be disallowed in a future version\n",
+ c->argv[0] );
+ }
+
+ for ( i=1; i < c->argc; ++i ) {
+ ad = NULL;
+ if ( slap_str2ad ( c->argv[i], &ad, &text )
+ == LDAP_SUCCESS) {
+ ip = ch_malloc (
+ sizeof ( refint_attrs ) );
+ ip->attr = ad;
+
+ for ( pipp = &dd->attrs; *pipp; pipp = &(*pipp)->next )
+ /* Get to the end */ ;
+ ip->next = *pipp;
+ *pipp = ip;
+ } else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s <%s>: %s", c->argv[0], c->argv[i], text );
+ Debug ( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg );
+ rc = ARG_BAD_CONF;
+ }
+ }
+ break;
+ case REFINT_NOTHING:
+ if ( !BER_BVISNULL( &c->value_ndn )) {
+ ch_free ( dd->nothing.bv_val );
+ ch_free ( dd->nnothing.bv_val );
+ dd->nothing = c->value_dn;
+ dd->nnothing = c->value_ndn;
+ rc = 0;
+ } else {
+ rc = ARG_BAD_CONF;
+ }
+ break;
+ case REFINT_MODIFIERSNAME:
+ if ( !BER_BVISNULL( &c->value_ndn )) {
+ ch_free( dd->refint_dn.bv_val );
+ ch_free( dd->refint_ndn.bv_val );
+ dd->refint_dn = c->value_dn;
+ dd->refint_ndn = c->value_ndn;
+ rc = 0;
+ } else {
+ rc = ARG_BAD_CONF;
+ }
+ break;
+ default:
+ abort ();
+ }
+ break;
+ default:
+ abort ();
+ }
+
+ return rc;
+}
+
+/*
+** allocate new refint_data;
+** store in on_bi.bi_private;
+**
+*/
+
+static int
+refint_db_init(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ refint_data *id = ch_calloc(1,sizeof(refint_data));
+
+ on->on_bi.bi_private = id;
+ ldap_pvt_thread_mutex_init( &id->qmutex );
+ return(0);
+}
+
+static int
+refint_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+
+ if ( on->on_bi.bi_private ) {
+ refint_data *id = on->on_bi.bi_private;
+ refint_attrs *ii, *ij;
+
+ on->on_bi.bi_private = NULL;
+ ldap_pvt_thread_mutex_destroy( &id->qmutex );
+
+ for(ii = id->attrs; ii; ii = ij) {
+ ij = ii->next;
+ ch_free(ii);
+ }
+
+ ch_free( id->nothing.bv_val );
+ BER_BVZERO( &id->nothing );
+ ch_free( id->nnothing.bv_val );
+ BER_BVZERO( &id->nnothing );
+
+ ch_free( id );
+ }
+ return(0);
+}
+
+/*
+** initialize, copy basedn if not already set
+**
+*/
+
+static int
+refint_open(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ refint_data *id = on->on_bi.bi_private;
+
+ if ( BER_BVISNULL( &id->dn )) {
+ if ( BER_BVISNULL( &be->be_nsuffix[0] ))
+ return -1;
+ ber_dupbv( &id->dn, &be->be_nsuffix[0] );
+ }
+ if ( BER_BVISNULL( &id->refint_dn ) ) {
+ ber_dupbv( &id->refint_dn, &refint_dn );
+ ber_dupbv( &id->refint_ndn, &refint_ndn );
+ }
+
+ /*
+ ** find the backend that matches our configured basedn;
+ ** make sure it exists and has search and modify methods;
+ **
+ */
+
+ if ( on->on_info->oi_origdb != frontendDB ) {
+ BackendDB *db = select_backend(&id->dn, 1);
+
+ if ( db ) {
+ BackendInfo *bi;
+ if ( db == be )
+ bi = on->on_info->oi_orig;
+ else
+ bi = db->bd_info;
+ if ( !bi->bi_op_search || !bi->bi_op_modify ) {
+ Debug( LDAP_DEBUG_CONFIG,
+ "refint_response: backend missing search and/or modify\n" );
+ return -1;
+ }
+ id->db = db;
+ } else {
+ Debug( LDAP_DEBUG_CONFIG,
+ "refint_response: no backend for our baseDN %s??\n",
+ id->dn.bv_val );
+ return -1;
+ }
+ }
+ return(0);
+}
+
+
+/*
+** free our basedn;
+** free our refintdn
+**
+*/
+
+static int
+refint_close(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ refint_data *id = on->on_bi.bi_private;
+
+ ch_free( id->dn.bv_val );
+ BER_BVZERO( &id->dn );
+ ch_free( id->refint_dn.bv_val );
+ BER_BVZERO( &id->refint_dn );
+ ch_free( id->refint_ndn.bv_val );
+ BER_BVZERO( &id->refint_ndn );
+
+ return(0);
+}
+
+/*
+** search callback
+** generates a list of Attributes from search results
+*/
+
+static int
+refint_search_cb(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ Attribute *a;
+ BerVarray b = NULL;
+ refint_q *rq = op->o_callback->sc_private;
+ refint_data *dd = rq->rdata;
+ refint_attrs *ia, *da = dd->attrs, *na;
+ dependent_data *ip;
+ int i;
+
+ Debug(LDAP_DEBUG_TRACE, "refint_search_cb <%s>\n",
+ rs->sr_entry ? rs->sr_entry->e_name.bv_val : "NOTHING" );
+
+ if (rs->sr_type != REP_SEARCH || !rs->sr_entry) return(0);
+
+ /*
+ ** foreach configured attribute type:
+ ** if this attr exists in the search result,
+ ** and it has a value matching the target:
+ ** allocate an attr;
+ ** save/build DNs of any subordinate matches;
+ ** handle special case: found exact + subordinate match;
+ ** handle olcRefintNothing;
+ **
+ */
+
+ ip = op->o_tmpalloc(sizeof(dependent_data), op->o_tmpmemctx );
+ ber_dupbv_x( &ip->dn, &rs->sr_entry->e_name, op->o_tmpmemctx );
+ ber_dupbv_x( &ip->ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
+ ip->next = rq->attrs;
+ rq->attrs = ip;
+ ip->attrs = NULL;
+ for(ia = da; ia; ia = ia->next) {
+ if ( (a = attr_find(rs->sr_entry->e_attrs, ia->attr) ) ) {
+ int exact = -1, is_exact;
+
+ na = NULL;
+
+ /* Are we doing subtree matching or simple equality? */
+ if ( rq->do_sub ) {
+ for(i = 0, b = a->a_nvals; b[i].bv_val; i++) {
+ if(dnIsSuffix(&b[i], &rq->oldndn)) {
+ is_exact = b[i].bv_len == rq->oldndn.bv_len;
+
+ /* Paranoia: skip buggy duplicate exact match,
+ * it would break ra_numvals
+ */
+ if ( is_exact && exact >= 0 )
+ continue;
+
+ /* first match? create structure */
+ if ( na == NULL ) {
+ na = op->o_tmpcalloc( 1,
+ sizeof( refint_attrs ),
+ op->o_tmpmemctx );
+ na->next = ip->attrs;
+ ip->attrs = na;
+ na->attr = ia->attr;
+ }
+
+ na->ra_numvals++;
+
+ if ( is_exact ) {
+ /* Exact match: refint_repair will deduce the DNs */
+ exact = i;
+
+ } else {
+ /* Subordinate match */
+ struct berval newsub, newdn, olddn, oldndn;
+
+ /* Save old DN */
+ ber_dupbv_x( &olddn, &a->a_vals[i], op->o_tmpmemctx );
+ ber_bvarray_add_x( &na->old_vals, &olddn, op->o_tmpmemctx );
+
+ ber_dupbv_x( &oldndn, &a->a_nvals[i], op->o_tmpmemctx );
+ ber_bvarray_add_x( &na->old_nvals, &oldndn, op->o_tmpmemctx );
+
+ if ( BER_BVISEMPTY( &rq->newdn ) )
+ continue;
+
+ /* Rename subordinate match: Build new DN */
+ newsub = a->a_vals[i];
+ newsub.bv_len -= rq->olddn.bv_len + 1;
+ build_new_dn( &newdn, &rq->newdn, &newsub, op->o_tmpmemctx );
+ ber_bvarray_add_x( &na->new_vals, &newdn, op->o_tmpmemctx );
+
+ newsub = a->a_nvals[i];
+ newsub.bv_len -= rq->oldndn.bv_len + 1;
+ build_new_dn( &newdn, &rq->newndn, &newsub, op->o_tmpmemctx );
+ ber_bvarray_add_x( &na->new_nvals, &newdn, op->o_tmpmemctx );
+ }
+ }
+ }
+
+ /* If we got both subordinate and exact match,
+ * refint_repair won't special-case the exact match */
+ if ( exact >= 0 && na->old_vals ) {
+ struct berval dn;
+
+ ber_dupbv_x( &dn, &a->a_vals[exact], op->o_tmpmemctx );
+ ber_bvarray_add_x( &na->old_vals, &dn, op->o_tmpmemctx );
+ ber_dupbv_x( &dn, &a->a_nvals[exact], op->o_tmpmemctx );
+ ber_bvarray_add_x( &na->old_nvals, &dn, op->o_tmpmemctx );
+
+ if ( !BER_BVISEMPTY( &rq->newdn ) ) {
+ ber_dupbv_x( &dn, &rq->newdn, op->o_tmpmemctx );
+ ber_bvarray_add_x( &na->new_vals, &dn, op->o_tmpmemctx );
+ ber_dupbv_x( &dn, &rq->newndn, op->o_tmpmemctx );
+ ber_bvarray_add_x( &na->new_nvals, &dn, op->o_tmpmemctx );
+ }
+ }
+ } else {
+ /* entry has no children, just equality matching */
+ is_exact = attr_valfind( a,
+ SLAP_MR_EQUALITY|SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH|
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH, &rq->oldndn, &i, NULL );
+ if ( is_exact == LDAP_SUCCESS ) {
+ na = op->o_tmpcalloc( 1,
+ sizeof( refint_attrs ),
+ op->o_tmpmemctx );
+ na->next = ip->attrs;
+ ip->attrs = na;
+ na->attr = ia->attr;
+ na->ra_numvals = 1;
+ }
+ }
+
+ /* Deleting/replacing all values and a nothing DN is configured? */
+ if ( na && na->ra_numvals == a->a_numvals && !BER_BVISNULL(&dd->nothing) )
+ na->dont_empty = 1;
+
+ Debug( LDAP_DEBUG_TRACE, "refint_search_cb: %s: %s (#%d)\n",
+ a->a_desc->ad_cname.bv_val, rq->olddn.bv_val, i );
+ }
+ }
+
+ return(0);
+}
+
+static int
+refint_repair(
+ Operation *op,
+ refint_data *id,
+ refint_q *rq )
+{
+ dependent_data *dp;
+ SlapReply rs = {REP_RESULT};
+ Operation op2;
+ unsigned long opid;
+ int rc;
+ int cache;
+
+ op->o_callback->sc_response = refint_search_cb;
+ op->o_req_dn = op->o_bd->be_suffix[ 0 ];
+ op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ cache = op->o_do_not_cache;
+ op->o_do_not_cache = 1;
+
+ /* search */
+ rc = op->o_bd->be_search( op, &rs );
+ op->o_do_not_cache = cache;
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "refint_repair: search failed: %d\n",
+ rc );
+ return rc;
+ }
+
+ /* safety? paranoid just in case */
+ if ( op->o_callback->sc_private == NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "refint_repair: callback wiped out sc_private?!\n" );
+ return 0;
+ }
+
+ /* Set up the Modify requests */
+ op->o_callback->sc_response = &slap_null_cb;
+
+ /*
+ * [our search callback builds a list of attrs]
+ * foreach attr:
+ * make sure its dn has a backend;
+ * build Modification* chain;
+ * call the backend modify function;
+ *
+ */
+
+ opid = op->o_opid;
+ op2 = *op;
+ for ( dp = rq->attrs; dp; dp = dp->next ) {
+ SlapReply rs2 = {REP_RESULT};
+ refint_attrs *ra;
+ Modifications *m;
+
+ if ( dp->attrs == NULL ) continue; /* TODO: Is this needed? */
+
+ op2.o_bd = select_backend( &dp->ndn, 1 );
+ if ( !op2.o_bd ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "refint_repair: no backend for DN %s!\n",
+ dp->dn.bv_val );
+ continue;
+ }
+ op2.o_tag = LDAP_REQ_MODIFY;
+ op2.orm_modlist = NULL;
+ op2.o_req_dn = dp->dn;
+ op2.o_req_ndn = dp->ndn;
+ /* Internal ops, never replicate these */
+ op2.orm_no_opattrs = 1;
+ op2.o_dont_replicate = 1;
+ op2.o_opid = 0;
+
+ /* Set our ModifiersName */
+ if ( SLAP_LASTMOD( op->o_bd ) ) {
+ m = op2.o_tmpalloc( sizeof(Modifications) +
+ 4*sizeof(BerValue), op2.o_tmpmemctx );
+ m->sml_next = op2.orm_modlist;
+ op2.orm_modlist = m;
+ m->sml_op = LDAP_MOD_REPLACE;
+ m->sml_flags = SLAP_MOD_INTERNAL;
+ m->sml_desc = slap_schema.si_ad_modifiersName;
+ m->sml_type = m->sml_desc->ad_cname;
+ m->sml_numvals = 1;
+ m->sml_values = (BerVarray)(m+1);
+ m->sml_nvalues = m->sml_values+2;
+ BER_BVZERO( &m->sml_values[1] );
+ BER_BVZERO( &m->sml_nvalues[1] );
+ m->sml_values[0] = id->refint_dn;
+ m->sml_nvalues[0] = id->refint_ndn;
+ }
+
+ for ( ra = dp->attrs; ra; ra = ra->next ) {
+ size_t len;
+
+ /* Add values */
+ if ( ra->dont_empty || !BER_BVISEMPTY( &rq->newdn ) ) {
+ len = sizeof(Modifications);
+
+ if ( ra->new_vals == NULL ) {
+ len += 4*sizeof(BerValue);
+ }
+
+ m = op2.o_tmpalloc( len, op2.o_tmpmemctx );
+ m->sml_next = op2.orm_modlist;
+ op2.orm_modlist = m;
+ m->sml_op = LDAP_MOD_ADD;
+ m->sml_flags = 0;
+ m->sml_desc = ra->attr;
+ m->sml_type = ra->attr->ad_cname;
+ if ( ra->new_vals == NULL ) {
+ m->sml_values = (BerVarray)(m+1);
+ m->sml_nvalues = m->sml_values+2;
+ BER_BVZERO( &m->sml_values[1] );
+ BER_BVZERO( &m->sml_nvalues[1] );
+ m->sml_numvals = 1;
+ if ( BER_BVISEMPTY( &rq->newdn ) ) {
+ m->sml_values[0] = id->nothing;
+ m->sml_nvalues[0] = id->nnothing;
+ } else {
+ m->sml_values[0] = rq->newdn;
+ m->sml_nvalues[0] = rq->newndn;
+ }
+ } else {
+ m->sml_values = ra->new_vals;
+ m->sml_nvalues = ra->new_nvals;
+ m->sml_numvals = ra->ra_numvals;
+ }
+ }
+
+ /* Delete values */
+ len = sizeof(Modifications);
+ if ( ra->old_vals == NULL ) {
+ len += 4*sizeof(BerValue);
+ }
+ m = op2.o_tmpalloc( len, op2.o_tmpmemctx );
+ m->sml_next = op2.orm_modlist;
+ op2.orm_modlist = m;
+ m->sml_op = LDAP_MOD_DELETE;
+ m->sml_flags = 0;
+ m->sml_desc = ra->attr;
+ m->sml_type = ra->attr->ad_cname;
+ if ( ra->old_vals == NULL ) {
+ m->sml_numvals = 1;
+ m->sml_values = (BerVarray)(m+1);
+ m->sml_nvalues = m->sml_values+2;
+ m->sml_values[0] = rq->olddn;
+ m->sml_nvalues[0] = rq->oldndn;
+ BER_BVZERO( &m->sml_values[1] );
+ BER_BVZERO( &m->sml_nvalues[1] );
+ } else {
+ m->sml_values = ra->old_vals;
+ m->sml_nvalues = ra->old_nvals;
+ m->sml_numvals = ra->ra_numvals;
+ }
+ }
+
+ op2.o_dn = op2.o_bd->be_rootdn;
+ op2.o_ndn = op2.o_bd->be_rootndn;
+ rc = op2.o_bd->be_modify( &op2, &rs2 );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "refint_repair: dependent modify failed: %d\n",
+ rs2.sr_err );
+ }
+
+ while ( ( m = op2.orm_modlist ) ) {
+ op2.orm_modlist = m->sml_next;
+ op2.o_tmpfree( m, op2.o_tmpmemctx );
+ }
+ }
+ op2.o_opid = opid;
+
+ return 0;
+}
+
+static void *
+refint_qtask( void *ctx, void *arg )
+{
+ struct re_s *rtask = arg;
+ refint_data *id = rtask->arg;
+ Connection conn = {0};
+ OperationBuffer opbuf;
+ Operation *op;
+ slap_callback cb = { NULL, NULL, NULL, NULL };
+ Filter ftop, *fptr;
+ refint_q *rq;
+ refint_attrs *ip;
+ int pausing = 0, rc = 0;
+
+ connection_fake_init( &conn, &opbuf, ctx );
+ op = &opbuf.ob_op;
+
+ /*
+ ** build a search filter for all configured attributes;
+ ** populate our Operation;
+ ** pass our data (attr list, dn) to backend via sc_private;
+ ** call the backend search function;
+ ** nb: (|(one=thing)) is valid, but do smart formatting anyway;
+ ** nb: 16 is arbitrarily a dozen or so extra bytes;
+ **
+ */
+
+ ftop.f_choice = LDAP_FILTER_OR;
+ ftop.f_next = NULL;
+ ftop.f_or = NULL;
+ op->ors_filter = &ftop;
+ for(ip = id->attrs; ip; ip = ip->next) {
+ /* this filter can be either EQUALITY or EXT */
+ fptr = op->o_tmpcalloc( sizeof(Filter) + sizeof(MatchingRuleAssertion),
+ 1, op->o_tmpmemctx );
+ fptr->f_mra = (MatchingRuleAssertion *)(fptr+1);
+ fptr->f_mr_rule = mr_dnSubtreeMatch;
+ fptr->f_mr_rule_text = mr_dnSubtreeMatch->smr_bvoid;
+ fptr->f_mr_desc = ip->attr;
+ fptr->f_mr_dnattrs = 0;
+ fptr->f_next = ftop.f_or;
+ ftop.f_or = fptr;
+ }
+
+ for (;;) {
+ dependent_data *dp, *dp_next;
+ refint_attrs *ra, *ra_next;
+
+ if ( ldap_pvt_thread_pool_pausing( &connection_pool ) > 0 ) {
+ pausing = 1;
+ break;
+ }
+
+ /* Dequeue an op */
+ ldap_pvt_thread_mutex_lock( &id->qmutex );
+ rq = id->qhead;
+ if ( rq ) {
+ id->qhead = rq->next;
+ if ( !id->qhead )
+ id->qtail = NULL;
+ }
+ ldap_pvt_thread_mutex_unlock( &id->qmutex );
+ if ( !rq )
+ break;
+
+ for (fptr = ftop.f_or; fptr; fptr = fptr->f_next ) {
+ fptr->f_mr_value = rq->oldndn;
+ /* Use (attr:dnSubtreeMatch:=value) to catch subtree rename
+ * and subtree delete where supported */
+ if (rq->do_sub)
+ fptr->f_choice = LDAP_FILTER_EXT;
+ else
+ fptr->f_choice = LDAP_FILTER_EQUALITY;
+ }
+
+ filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
+
+ /* callback gets the searched dn instead */
+ cb.sc_private = rq;
+ cb.sc_response = refint_search_cb;
+ op->o_callback = &cb;
+ op->o_tag = LDAP_REQ_SEARCH;
+ op->ors_scope = LDAP_SCOPE_SUBTREE;
+ op->ors_deref = LDAP_DEREF_NEVER;
+ op->ors_limit = NULL;
+ op->ors_slimit = SLAP_NO_LIMIT;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+
+ /* no attrs! */
+ op->ors_attrs = slap_anlist_no_attrs;
+
+ slap_op_time( &op->o_time, &op->o_tincr );
+
+ if ( rq->db != NULL ) {
+ op->o_bd = rq->db;
+ rc = refint_repair( op, id, rq );
+
+ } else {
+ BackendDB *be;
+
+ LDAP_STAILQ_FOREACH( be, &backendDB, be_next ) {
+ /* we may want to skip cn=config */
+ if ( be == LDAP_STAILQ_FIRST(&backendDB) ) {
+ continue;
+ }
+
+ if ( be->be_search && be->be_modify ) {
+ op->o_bd = be;
+ rc = refint_repair( op, id, rq );
+ }
+ }
+ }
+
+ for ( dp = rq->attrs; dp; dp = dp_next ) {
+ dp_next = dp->next;
+ for ( ra = dp->attrs; ra; ra = ra_next ) {
+ ra_next = ra->next;
+ ber_bvarray_free_x( ra->new_nvals, op->o_tmpmemctx );
+ ber_bvarray_free_x( ra->new_vals, op->o_tmpmemctx );
+ ber_bvarray_free_x( ra->old_nvals, op->o_tmpmemctx );
+ ber_bvarray_free_x( ra->old_vals, op->o_tmpmemctx );
+ op->o_tmpfree( ra, op->o_tmpmemctx );
+ }
+ op->o_tmpfree( dp->ndn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( dp->dn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( dp, op->o_tmpmemctx );
+ }
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ if ( rc == LDAP_BUSY ) {
+ pausing = 1;
+ /* re-queue this op */
+ ldap_pvt_thread_mutex_lock( &id->qmutex );
+ rq->next = id->qhead;
+ id->qhead = rq;
+ if ( !id->qtail )
+ id->qtail = rq;
+ ldap_pvt_thread_mutex_unlock( &id->qmutex );
+ break;
+ }
+
+ if ( !BER_BVISNULL( &rq->newndn )) {
+ ch_free( rq->newndn.bv_val );
+ ch_free( rq->newdn.bv_val );
+ }
+ ch_free( rq->oldndn.bv_val );
+ ch_free( rq->olddn.bv_val );
+ ch_free( rq );
+ }
+
+ /* free filter */
+ for ( fptr = ftop.f_or; fptr; ) {
+ Filter *f_next = fptr->f_next;
+ op->o_tmpfree( fptr, op->o_tmpmemctx );
+ fptr = f_next;
+ }
+
+ /* wait until we get explicitly scheduled again */
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ ldap_pvt_runqueue_stoptask( &slapd_rq, id->qtask );
+ if ( pausing ) {
+ /* try to run again as soon as the pause is done */
+ id->qtask->interval.tv_sec = 0;
+ ldap_pvt_runqueue_resched( &slapd_rq, id->qtask, 0 );
+ id->qtask->interval.tv_sec = RUNQ_INTERVAL;
+ } else {
+ ldap_pvt_runqueue_resched( &slapd_rq,id->qtask, 1 );
+ }
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+
+ return NULL;
+}
+
+/*
+** refint_response
+** search for matching records and modify them
+*/
+
+static int
+refint_response(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ refint_pre *rp;
+ slap_overinst *on;
+ refint_data *id;
+ BerValue pdn;
+ refint_q *rq;
+ refint_attrs *ip;
+ int ac;
+
+ /* If the main op failed or is not a Delete or ModRdn, ignore it */
+ if (( op->o_tag != LDAP_REQ_DELETE && op->o_tag != LDAP_REQ_MODRDN ) ||
+ rs->sr_err != LDAP_SUCCESS )
+ return SLAP_CB_CONTINUE;
+
+ rp = op->o_callback->sc_private;
+ on = rp->on;
+ id = on->on_bi.bi_private;
+
+ rq = ch_calloc( 1, sizeof( refint_q ));
+ ber_dupbv( &rq->olddn, &op->o_req_dn );
+ ber_dupbv( &rq->oldndn, &op->o_req_ndn );
+ rq->db = id->db;
+ rq->rdata = id;
+ rq->do_sub = rp->do_sub;
+
+ if ( op->o_tag == LDAP_REQ_MODRDN ) {
+ if ( op->oq_modrdn.rs_newSup ) {
+ pdn = *op->oq_modrdn.rs_newSup;
+ } else {
+ dnParent( &op->o_req_dn, &pdn );
+ }
+ build_new_dn( &rq->newdn, &pdn, &op->orr_newrdn, NULL );
+ if ( op->oq_modrdn.rs_nnewSup ) {
+ pdn = *op->oq_modrdn.rs_nnewSup;
+ } else {
+ dnParent( &op->o_req_ndn, &pdn );
+ }
+ build_new_dn( &rq->newndn, &pdn, &op->orr_nnewrdn, NULL );
+ }
+
+ ldap_pvt_thread_mutex_lock( &id->qmutex );
+ if ( id->qtail ) {
+ id->qtail->next = rq;
+ } else {
+ id->qhead = rq;
+ }
+ id->qtail = rq;
+ ldap_pvt_thread_mutex_unlock( &id->qmutex );
+
+ ac = 0;
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( !id->qtask ) {
+ id->qtask = ldap_pvt_runqueue_insert( &slapd_rq, RUNQ_INTERVAL,
+ refint_qtask, id, "refint_qtask",
+ op->o_bd->be_suffix[0].bv_val );
+ ac = 1;
+ } else {
+ if ( !ldap_pvt_runqueue_isrunning( &slapd_rq, id->qtask ) &&
+ !id->qtask->next_sched.tv_sec ) {
+ id->qtask->interval.tv_sec = 0;
+ ldap_pvt_runqueue_resched( &slapd_rq, id->qtask, 0 );
+ id->qtask->interval.tv_sec = RUNQ_INTERVAL;
+ ac = 1;
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ if ( ac )
+ slap_wake_listener();
+
+ return SLAP_CB_CONTINUE;
+}
+
+/* Check if the target entry exists and has children.
+ * Do nothing if target doesn't exist.
+ */
+static int
+refint_preop(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ refint_data *id = on->on_bi.bi_private;
+ Entry *e;
+ int rc;
+
+ /* are any attrs configured? */
+ if ( !id->attrs )
+ return SLAP_CB_CONTINUE;
+
+ rc = overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on );
+ if ( rc == LDAP_SUCCESS ) {
+ slap_callback *sc = op->o_tmpcalloc( 1,
+ sizeof(slap_callback)+sizeof(refint_pre), op->o_tmpmemctx );
+ refint_pre *rp = (refint_pre *)(sc+1);
+ rp->on = on;
+ rp->do_sub = 1; /* assume there are children */
+ if ( op->o_bd->be_has_subordinates ) {
+ int has = 0;
+ rc = op->o_bd->be_has_subordinates( op, e, &has );
+ /* there definitely are not children */
+ if ( rc == LDAP_SUCCESS && has == LDAP_COMPARE_FALSE )
+ rp->do_sub = 0;
+ }
+ overlay_entry_release_ov( op, e, 0, on );
+ sc->sc_response = refint_response;
+ sc->sc_private = rp;
+ sc->sc_next = op->o_callback;
+ op->o_callback = sc;
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+** init_module is last so the symbols resolve "for free" --
+** it expects to be called automagically during dynamic module initialization
+*/
+
+int refint_initialize() {
+ int rc;
+
+ mr_dnSubtreeMatch = mr_find( "dnSubtreeMatch" );
+ if ( mr_dnSubtreeMatch == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "refint_initialize: "
+ "unable to find MatchingRule 'dnSubtreeMatch'.\n" );
+ return 1;
+ }
+
+ /* statically declared just after the #includes at top */
+ refint.on_bi.bi_type = "refint";
+ refint.on_bi.bi_db_init = refint_db_init;
+ refint.on_bi.bi_db_destroy = refint_db_destroy;
+ refint.on_bi.bi_db_open = refint_open;
+ refint.on_bi.bi_db_close = refint_close;
+ refint.on_bi.bi_op_delete = refint_preop;
+ refint.on_bi.bi_op_modrdn = refint_preop;
+
+ refint.on_bi.bi_cf_ocs = refintocs;
+ rc = config_register_schema ( refintcfg, refintocs );
+ if ( rc ) return rc;
+
+ return(overlay_register(&refint));
+}
+
+#if SLAPD_OVER_REFINT == SLAPD_MOD_DYNAMIC && defined(PIC)
+int init_module(int argc, char *argv[]) {
+ return refint_initialize();
+}
+#endif
+
+#endif /* SLAPD_OVER_REFINT */
diff --git a/servers/slapd/overlays/remoteauth.c b/servers/slapd/overlays/remoteauth.c
new file mode 100644
index 0000000..87397a1
--- /dev/null
+++ b/servers/slapd/overlays/remoteauth.c
@@ -0,0 +1,996 @@
+/* $OpenLDAP$ */
+/* remoteauth.c - Overlay to delegate bind processing to a remote server */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2017-2021 Ondřej Kuzník, Symas Corporation.
+ * Portions Copyright 2004-2017 Howard Chu, Symas Corporation.
+ * Portions Copyright 2004 Hewlett-Packard Company.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <ldap.h>
+#if SLAPD_MODULES
+#define LIBLTDL_DLL_IMPORT /* Win32: don't re-export libltdl's symbols */
+#include <ltdl.h>
+#endif
+#include <ac/errno.h>
+#include <ac/time.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include "lutil.h"
+#include "slap.h"
+#include "slap-config.h"
+
+#ifndef UP_STR
+#define UP_STR "userPassword"
+#endif /* UP_STR */
+
+#ifndef LDAP_PREFIX
+#define LDAP_PREFIX "ldap://"
+#endif /* LDAP_PREFIX */
+
+#ifndef FILE_PREFIX
+#define FILE_PREFIX "file://"
+#endif /* LDAP_PREFIX */
+
+typedef struct _ad_info {
+ struct _ad_info *next;
+ char *domain;
+ char *realm;
+} ad_info;
+
+typedef struct _ad_pin {
+ struct _ad_pin *next;
+ char *hostname;
+ char *pin;
+} ad_pin;
+
+typedef struct _ad_private {
+ char *dn;
+ AttributeDescription *dn_ad;
+ char *domain_attr;
+ AttributeDescription *domain_ad;
+
+ AttributeDescription *up_ad;
+ ad_info *mappings;
+
+ char *default_realm;
+ char *default_domain;
+
+ int up_set;
+ int retry_count;
+ int store_on_success;
+
+ ad_pin *pins;
+ slap_bindconf ad_tls;
+} ad_private;
+
+enum {
+ REMOTE_AUTH_MAPPING = 1,
+ REMOTE_AUTH_DN_ATTRIBUTE,
+ REMOTE_AUTH_DOMAIN_ATTRIBUTE,
+ REMOTE_AUTH_DEFAULT_DOMAIN,
+ REMOTE_AUTH_DEFAULT_REALM,
+ REMOTE_AUTH_CACERT_DIR,
+ REMOTE_AUTH_CACERT_FILE,
+ REMOTE_AUTH_VALIDATE_CERTS,
+ REMOTE_AUTH_RETRY_COUNT,
+ REMOTE_AUTH_TLS,
+ REMOTE_AUTH_TLS_PIN,
+ REMOTE_AUTH_STORE_ON_SUCCESS,
+};
+
+static ConfigDriver remoteauth_cf_gen;
+
+static ConfigTable remoteauthcfg[] = {
+ { "remoteauth_mapping", "mapping between domain and realm", 2, 3, 0,
+ ARG_MAGIC|REMOTE_AUTH_MAPPING,
+ remoteauth_cf_gen,
+ "( OLcfgOvAt:24.1 NAME 'olcRemoteAuthMapping' "
+ "DESC 'Mapping from domain name to server' "
+ "SYNTAX OMsDirectoryString )",
+ NULL, NULL
+ },
+ { "remoteauth_dn_attribute", "Attribute to use as AD bind DN", 2, 2, 0,
+ ARG_MAGIC|REMOTE_AUTH_DN_ATTRIBUTE,
+ remoteauth_cf_gen,
+ "( OLcfgOvAt:24.2 NAME 'olcRemoteAuthDNAttribute' "
+ "DESC 'Attribute in entry to use as bind DN for AD' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "remoteauth_domain_attribute", "Attribute to use as domain determinant", 2, 2, 0,
+ ARG_MAGIC|REMOTE_AUTH_DOMAIN_ATTRIBUTE,
+ remoteauth_cf_gen,
+ "( OLcfgOvAt:24.3 NAME 'olcRemoteAuthDomainAttribute' "
+ "DESC 'Attribute in entry to determine windows domain' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "remoteauth_default_domain", "Default Windows domain", 2, 2, 0,
+ ARG_MAGIC|REMOTE_AUTH_DEFAULT_DOMAIN,
+ remoteauth_cf_gen,
+ "( OLcfgOvAt:24.4 NAME 'olcRemoteAuthDefaultDomain' "
+ "DESC 'Default Windows domain to use' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "remoteauth_default_realm", "Default AD realm", 2, 2, 0,
+ ARG_MAGIC|REMOTE_AUTH_DEFAULT_REALM,
+ remoteauth_cf_gen,
+ "( OLcfgOvAt:24.5 NAME 'olcRemoteAuthDefaultRealm' "
+ "DESC 'Default AD realm to use' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "remoteauth_store", "on|off", 1, 2, 0,
+ ARG_OFFSET|ARG_ON_OFF|REMOTE_AUTH_STORE_ON_SUCCESS,
+ (void *)offsetof(ad_private, store_on_success),
+ "( OLcfgOvAt:24.6 NAME 'olcRemoteAuthStore' "
+ "DESC 'Store password locally on success' "
+ "SYNTAX OMsBoolean SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "remoteauth_retry_count", "integer", 2, 2, 0,
+ ARG_OFFSET|ARG_UINT|REMOTE_AUTH_RETRY_COUNT,
+ (void *)offsetof(ad_private, retry_count),
+ "( OLcfgOvAt:24.7 NAME 'olcRemoteAuthRetryCount' "
+ "DESC 'Number of retries attempted' "
+ "SYNTAX OMsInteger SINGLE-VALUE )",
+ NULL, { .v_uint = 3 }
+ },
+ { "remoteauth_tls", "tls settings", 2, 0, 0,
+ ARG_MAGIC|REMOTE_AUTH_TLS,
+ remoteauth_cf_gen,
+ "( OLcfgOvAt:24.8 NAME 'olcRemoteAuthTLS' "
+ "DESC 'StartTLS settings' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )",
+ NULL, NULL
+ },
+ { "remoteauth_tls_peerkey_hash", "mapping between hostnames and their public key hash", 3, 3, 0,
+ ARG_MAGIC|REMOTE_AUTH_TLS_PIN,
+ remoteauth_cf_gen,
+ "( OLcfgOvAt:24.9 NAME 'olcRemoteAuthTLSPeerkeyHash' "
+ "DESC 'StartTLS hostname to public key pin mapping file' "
+ "SYNTAX OMsDirectoryString )",
+ NULL, NULL
+ },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED, NULL }
+};
+
+static ConfigOCs remoteauthocs[] = {
+ { "( OLcfgOvOc:24.1 "
+ "NAME 'olcRemoteAuthCfg' "
+ "DESC 'Remote Directory passthough authentication configuration' "
+ "SUP olcOverlayConfig "
+ "MUST olcRemoteAuthTLS "
+ "MAY ( olcRemoteAuthMapping $ olcRemoteAuthDNAttribute $ "
+ " olcRemoteAuthDomainAttribute $ olcRemoteAuthDefaultDomain $ "
+ " olcRemoteAuthDefaultRealm $ olcRemoteAuthStore $ "
+ " olcRemoteAuthRetryCount $ olcRemoteAuthTLSPeerkeyHash ) )",
+ Cft_Overlay, remoteauthcfg },
+ { NULL, 0, NULL }
+};
+
+static int
+remoteauth_cf_gen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ ad_private *ad = (ad_private *)on->on_bi.bi_private;
+ struct berval bv;
+ int i, rc = 0;
+ ad_info *map;
+ const char *text = NULL;
+
+ switch ( c->op ) {
+ case SLAP_CONFIG_EMIT:
+ switch ( c->type ) {
+ case REMOTE_AUTH_MAPPING:
+ for ( map = ad->mappings; map; map = map->next ) {
+ char *str;
+
+ str = ch_malloc( strlen( map->domain ) +
+ strlen( map->realm ) + 2 );
+ sprintf( str, "%s %s", map->domain, map->realm );
+ ber_str2bv( str, strlen( str ), 1, &bv );
+ ch_free( str );
+ rc = value_add_one( &c->rvalue_vals, &bv );
+ if ( rc ) return rc;
+ rc = value_add_one( &c->rvalue_nvals, &bv );
+ if ( rc ) return rc;
+ }
+ break;
+ case REMOTE_AUTH_DN_ATTRIBUTE:
+ if ( ad->dn )
+ value_add_one( &c->rvalue_vals, &ad->dn_ad->ad_cname );
+ break;
+ case REMOTE_AUTH_DOMAIN_ATTRIBUTE:
+ if ( ad->domain_attr )
+ value_add_one(
+ &c->rvalue_vals, &ad->domain_ad->ad_cname );
+ break;
+ case REMOTE_AUTH_DEFAULT_DOMAIN:
+ if ( ad->default_domain ) {
+ ber_str2bv( ad->default_domain, 0, 1, &bv );
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
+ case REMOTE_AUTH_DEFAULT_REALM:
+ if ( ad->default_realm ) {
+ ber_str2bv( ad->default_realm, 0, 1, &bv );
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
+ case REMOTE_AUTH_TLS:
+ bindconf_tls_unparse( &ad->ad_tls, &bv );
+
+ for ( i = 0; isspace( (unsigned char) bv.bv_val[ i ] ); i++ )
+ /* count spaces */ ;
+
+ if ( i ) {
+ bv.bv_len -= i;
+ AC_MEMCPY( bv.bv_val, &bv.bv_val[ i ],
+ bv.bv_len + 1 );
+ }
+
+ value_add_one( &c->rvalue_vals, &bv );
+ break;
+ case REMOTE_AUTH_TLS_PIN: {
+ ad_pin *pin = ad->pins;
+ for ( pin = ad->pins; pin; pin = pin->next ) {
+ bv.bv_val = ch_malloc( strlen( pin->hostname ) +
+ strlen( pin->pin ) + 2 );
+ bv.bv_len = sprintf(
+ bv.bv_val, "%s %s", pin->hostname, pin->pin );
+ rc = value_add_one( &c->rvalue_vals, &bv );
+ if ( rc ) return rc;
+ rc = value_add_one( &c->rvalue_nvals, &bv );
+ if ( rc ) return rc;
+ }
+ } break;
+
+ default:
+ abort();
+ }
+ break;
+ case LDAP_MOD_DELETE:
+ switch ( c->type ) {
+ case REMOTE_AUTH_MAPPING:
+ if ( c->valx < 0 ) {
+ /* delete all mappings */
+ while ( ad->mappings ) {
+ map = ad->mappings;
+ ad->mappings = ad->mappings->next;
+ ch_free( map->domain );
+ ch_free( map->realm );
+ ch_free( map );
+ }
+ } else {
+ /* delete a specific mapping indicated by 'valx'*/
+ ad_info *pmap = NULL;
+
+ for ( map = ad->mappings, i = 0;
+ ( map ) && ( i < c->valx );
+ pmap = map, map = map->next, i++ )
+ ;
+
+ if ( pmap ) {
+ pmap->next = map->next;
+ map->next = NULL;
+
+ ch_free( map->domain );
+ ch_free( map->realm );
+ ch_free( map );
+ } else if ( ad->mappings ) {
+ /* delete the first item in the list */
+ map = ad->mappings;
+ ad->mappings = map->next;
+ ch_free( map->domain );
+ ch_free( map->realm );
+ ch_free( map );
+ }
+ }
+ break;
+ case REMOTE_AUTH_DN_ATTRIBUTE:
+ if ( ad->dn ) {
+ ch_free( ad->dn );
+ ad->dn = NULL; /* Don't free AttributeDescription */
+ }
+ break;
+ case REMOTE_AUTH_DOMAIN_ATTRIBUTE:
+ if ( ad->domain_attr ) {
+ ch_free( ad->domain_attr );
+ /* Don't free AttributeDescription */
+ ad->domain_attr = NULL;
+ }
+ break;
+ case REMOTE_AUTH_DEFAULT_DOMAIN:
+ if ( ad->default_domain ) {
+ ch_free( ad->default_domain );
+ ad->default_domain = NULL;
+ }
+ break;
+ case REMOTE_AUTH_DEFAULT_REALM:
+ if ( ad->default_realm ) {
+ ch_free( ad->default_realm );
+ ad->default_realm = NULL;
+ }
+ break;
+ case REMOTE_AUTH_TLS:
+ /* MUST + SINGLE-VALUE -> this is a replace */
+ bindconf_free( &ad->ad_tls );
+ break;
+ case REMOTE_AUTH_TLS_PIN:
+ while ( ad->pins ) {
+ ad_pin *pin = ad->pins;
+ ad->pins = ad->pins->next;
+ ch_free( pin->hostname );
+ ch_free( pin->pin );
+ ch_free( pin );
+ }
+ break;
+ /* ARG_OFFSET */
+ case REMOTE_AUTH_STORE_ON_SUCCESS:
+ case REMOTE_AUTH_RETRY_COUNT:
+ abort();
+ break;
+ default:
+ abort();
+ }
+ break;
+ case SLAP_CONFIG_ADD:
+ case LDAP_MOD_ADD:
+ switch ( c->type ) {
+ case REMOTE_AUTH_MAPPING:
+ /* add mapping to head of list */
+ map = ch_malloc( sizeof(ad_info) );
+ map->domain = ber_strdup( c->argv[1] );
+ map->realm = ber_strdup( c->argv[2] );
+ map->next = ad->mappings;
+ ad->mappings = map;
+
+ break;
+ case REMOTE_AUTH_DN_ATTRIBUTE:
+ if ( slap_str2ad( c->argv[1], &ad->dn_ad, &text ) ==
+ LDAP_SUCCESS ) {
+ ad->dn = ber_strdup( ad->dn_ad->ad_cname.bv_val );
+ } else {
+ strncpy( c->cr_msg, text, sizeof(c->cr_msg) );
+ c->cr_msg[sizeof(c->cr_msg) - 1] = '\0';
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ rc = ARG_BAD_CONF;
+ }
+ break;
+ case REMOTE_AUTH_DOMAIN_ATTRIBUTE:
+ if ( slap_str2ad( c->argv[1], &ad->domain_ad, &text ) ==
+ LDAP_SUCCESS ) {
+ ad->domain_attr =
+ ber_strdup( ad->domain_ad->ad_cname.bv_val );
+ } else {
+ strncpy( c->cr_msg, text, sizeof(c->cr_msg) );
+ c->cr_msg[sizeof(c->cr_msg) - 1] = '\0';
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ rc = ARG_BAD_CONF;
+ }
+ break;
+ case REMOTE_AUTH_DEFAULT_DOMAIN:
+ if ( ad->default_domain ) {
+ ch_free( ad->default_domain );
+ ad->default_domain = NULL;
+ }
+ ad->default_domain = ber_strdup( c->argv[1] );
+ break;
+ case REMOTE_AUTH_DEFAULT_REALM:
+ if ( ad->default_realm ) {
+ ch_free( ad->default_realm );
+ ad->default_realm = NULL;
+ }
+ ad->default_realm = ber_strdup( c->argv[1] );
+ break;
+ case REMOTE_AUTH_TLS:
+ for ( i=1; i < c->argc; i++ ) {
+ if ( bindconf_tls_parse( c->argv[i], &ad->ad_tls ) ) {
+ rc = 1;
+ break;
+ }
+ }
+ bindconf_tls_defaults( &ad->ad_tls );
+ break;
+ case REMOTE_AUTH_TLS_PIN: {
+ ad_pin *pin = ch_calloc( 1, sizeof(ad_pin) );
+
+ pin->hostname = ber_strdup( c->argv[1] );
+ pin->pin = ber_strdup( c->argv[2] );
+ pin->next = ad->pins;
+ ad->pins = pin;
+ } break;
+ /* ARG_OFFSET */
+ case REMOTE_AUTH_STORE_ON_SUCCESS:
+ case REMOTE_AUTH_RETRY_COUNT:
+ abort();
+ break;
+ default:
+ abort();
+ }
+ break;
+ default:
+ abort();
+ }
+
+ return rc;
+}
+
+static char *
+get_realm(
+ const char *domain,
+ ad_info *mappings,
+ const char *default_realm,
+ int *isfile )
+{
+ ad_info *ai;
+ char *dom = NULL, *ch, *ret = NULL;
+
+ if ( isfile ) *isfile = 0;
+
+ if ( !domain ) {
+ ret = default_realm ? ch_strdup( default_realm ) : NULL;
+ goto exit;
+ }
+
+ /* munge any DOMAIN\user or DOMAIN:user values into just DOMAIN */
+
+ ch = strchr( domain, '\\' );
+ if ( !ch ) ch = strchr( domain, ':' );
+
+ if ( ch ) {
+ dom = ch_malloc( ch - domain + 1 );
+ strncpy( dom, domain, ch - domain );
+ dom[ch - domain] = '\0';
+ } else {
+ dom = ch_strdup( domain );
+ }
+
+ for ( ai = mappings; ai; ai = ai->next )
+ if ( strcasecmp( ai->domain, dom ) == 0 ) {
+ ret = ch_strdup( ai->realm );
+ break;
+ }
+
+ if ( !ai )
+ ret = default_realm ? ch_strdup( default_realm ) :
+ NULL; /* no mapping found */
+exit:
+ if ( dom ) ch_free( dom );
+ if ( ret &&
+ ( strncasecmp( ret, FILE_PREFIX, strlen( FILE_PREFIX ) ) == 0 ) ) {
+ char *p;
+
+ p = ret;
+ ret = ch_strdup( p + strlen( FILE_PREFIX ) );
+ ch_free( p );
+ if ( isfile ) *isfile = 1;
+ }
+
+ return ret;
+}
+
+static char *
+get_ldap_url( const char *realm, int isfile )
+{
+ char *ldap_url = NULL;
+ FILE *fp;
+
+ if ( !realm ) return NULL;
+
+ if ( !isfile ) {
+ if ( strstr( realm, "://" ) ) {
+ return ch_strdup( realm );
+ }
+
+ ldap_url = ch_malloc( 1 + strlen( LDAP_PREFIX ) + strlen( realm ) );
+ sprintf( ldap_url, "%s%s", LDAP_PREFIX, realm );
+ return ldap_url;
+ }
+
+ fp = fopen( realm, "r" );
+ if ( !fp ) {
+ char ebuf[128];
+ int saved_errno = errno;
+ Debug( LDAP_DEBUG_TRACE, "remoteauth: "
+ "Unable to open realm file (%s)\n",
+ sock_errstr( saved_errno, ebuf, sizeof(ebuf) ) );
+ return NULL;
+ }
+ /*
+ * Read each line in the file and return a URL of the form
+ * "ldap://<line1> ldap://<line2> ... ldap://<lineN>"
+ * which can be passed to ldap_initialize.
+ */
+ while ( !feof( fp ) ) {
+ char line[512], *p;
+
+ p = fgets( line, sizeof(line), fp );
+ if ( !p ) continue;
+
+ /* terminate line at first whitespace */
+ for ( p = line; *p; p++ )
+ if ( isspace( *p ) ) {
+ *p = '\0';
+ break;
+ }
+
+ if ( ldap_url ) {
+ char *nu;
+
+ nu = ch_malloc( strlen( ldap_url ) + 2 + strlen( LDAP_PREFIX ) +
+ strlen( line ) );
+
+ if ( strstr( line, "://" ) ) {
+ sprintf( nu, "%s %s", ldap_url, line );
+ } else {
+ sprintf( nu, "%s %s%s", ldap_url, LDAP_PREFIX, line );
+ }
+ ch_free( ldap_url );
+ ldap_url = nu;
+ } else {
+ ldap_url = ch_malloc( 1 + strlen( line ) + strlen( LDAP_PREFIX ) );
+ if ( strstr( line, "://" ) ) {
+ strcpy( ldap_url, line );
+ } else {
+ sprintf( ldap_url, "%s%s", LDAP_PREFIX, line );
+ }
+ }
+ }
+
+ fclose( fp );
+
+ return ldap_url;
+}
+
+static void
+trace_remoteauth_parameters( ad_private *ap )
+{
+ ad_info *pad_info;
+ struct berval bv;
+
+ if ( !ap ) return;
+
+ Debug( LDAP_DEBUG_TRACE, "remoteauth_dn_attribute: %s\n",
+ ap->dn ? ap->dn : "NULL" );
+
+ Debug( LDAP_DEBUG_TRACE, "remoteauth_domain_attribute: %s\n",
+ ap->domain_attr ? ap->domain_attr : "NULL" );
+
+ Debug( LDAP_DEBUG_TRACE, "remoteauth_default_realm: %s\n",
+ ap->default_realm ? ap->default_realm : "NULL" );
+
+ Debug( LDAP_DEBUG_TRACE, "remoteauth_default_domain: %s\n",
+ ap->default_domain ? ap->default_domain : "NULL" );
+
+ Debug( LDAP_DEBUG_TRACE, "remoteauth_retry_count: %d\n", ap->retry_count );
+
+ bindconf_tls_unparse( &ap->ad_tls, &bv );
+ Debug( LDAP_DEBUG_TRACE, "remoteauth_tls:%s\n", bv.bv_val );
+ ch_free( bv.bv_val );
+
+ pad_info = ap->mappings;
+ while ( pad_info ) {
+ Debug( LDAP_DEBUG_TRACE, "remoteauth_mappings(%s,%s)\n",
+ pad_info->domain ? pad_info->domain : "NULL",
+ pad_info->realm ? pad_info->realm : "NULL" );
+ pad_info = pad_info->next;
+ }
+
+ return;
+}
+
+static int
+remoteauth_conn_cb(
+ LDAP *ld,
+ Sockbuf *sb,
+ LDAPURLDesc *srv,
+ struct sockaddr *addr,
+ struct ldap_conncb *ctx )
+{
+ ad_private *ap = ctx->lc_arg;
+ ad_pin *pin = NULL;
+ char *host;
+
+ host = srv->lud_host;
+ if ( !host || !*host ) {
+ host = "localhost";
+ }
+
+ for ( pin = ap->pins; pin; pin = pin->next ) {
+ if ( !strcasecmp( host, pin->hostname ) ) break;
+ }
+
+ if ( pin ) {
+ int rc = ldap_set_option( ld, LDAP_OPT_X_TLS_PEERKEY_HASH, pin->pin );
+ if ( rc == LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "remoteauth_conn_cb: "
+ "TLS Peerkey hash could not be set to '%s': %d\n",
+ pin->pin, rc );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "remoteauth_conn_cb: "
+ "No TLS Peerkey hash found for host '%s'\n",
+ host );
+ }
+
+ return -1;
+}
+
+static void
+remoteauth_conn_delcb( LDAP *ld, Sockbuf *sb, struct ldap_conncb *ctx )
+{
+ return;
+}
+
+static int
+remoteauth_bind( Operation *op, SlapReply *rs )
+{
+ Entry *e;
+ int rc;
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ ad_private *ap = (ad_private *)on->on_bi.bi_private;
+ Attribute *a_dom, *a_dn;
+ struct ldap_conncb ad_conncb = { .lc_add = remoteauth_conn_cb,
+ .lc_del = remoteauth_conn_delcb,
+ .lc_arg = ap };
+ struct berval dn = { 0 };
+ char *dom_val, *realm = NULL;
+ char *ldap_url = NULL;
+ LDAP *ld = NULL;
+ int protocol = LDAP_VERSION3, isfile = 0;
+ int tries = 0;
+
+ if ( LogTest( LDAP_DEBUG_TRACE ) ) {
+ trace_remoteauth_parameters( ap );
+ }
+
+ if ( op->orb_method != LDAP_AUTH_SIMPLE )
+ return SLAP_CB_CONTINUE; /* only do password auth */
+
+ /* Can't handle root via this mechanism */
+ if ( be_isroot_dn( op->o_bd, &op->o_req_ndn ) ) return SLAP_CB_CONTINUE;
+
+ if ( !ap->up_set ) {
+ const char *txt = NULL;
+
+ if ( slap_str2ad( UP_STR, &ap->up_ad, &txt ) )
+ Debug( LDAP_DEBUG_TRACE, "remoteauth_bind: "
+ "userPassword attr undefined: %s\n",
+ txt ? txt : "" );
+ ap->up_set = 1;
+ }
+
+ if ( !ap->up_ad ) {
+ Debug( LDAP_DEBUG_TRACE, "remoteauth_bind: "
+ "password attribute not configured\n" );
+ return SLAP_CB_CONTINUE; /* userPassword not defined */
+ }
+
+ if ( !ap->dn ) {
+ Debug( LDAP_DEBUG_TRACE, "remoteauth_bind: "
+ "remote DN attribute not configured\n" );
+ return SLAP_CB_CONTINUE; /* no mapped DN attribute */
+ }
+
+ if ( !ap->domain_attr ) {
+ Debug( LDAP_DEBUG_TRACE, "remoteauth_bind: "
+ "domain attribute not configured\n" );
+ return SLAP_CB_CONTINUE; /* no way to know domain */
+ }
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
+ if ( rc != LDAP_SUCCESS ) return SLAP_CB_CONTINUE;
+
+ rc = SLAP_CB_CONTINUE;
+ /* if userPassword is defined in entry, skip to the end */
+ if ( attr_find( e->e_attrs, ap->up_ad ) ) {
+ Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: "
+ "user has a password, skipping\n",
+ op->o_log_prefix );
+ goto exit;
+ }
+
+ a_dom = attr_find( e->e_attrs, ap->domain_ad );
+ if ( !a_dom )
+ dom_val = ap->default_domain;
+ else {
+ dom_val = a_dom->a_vals[0].bv_val;
+ }
+
+ if ( !dom_val ) {
+ Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: "
+ "user has no domain nor do we have a default, skipping\n",
+ op->o_log_prefix );
+ goto exit; /* user has no domain */
+ }
+
+ realm = get_realm( dom_val, ap->mappings, ap->default_realm, &isfile );
+ if ( !realm ) goto exit;
+
+ a_dn = attr_find( e->e_attrs, ap->dn_ad );
+ if ( !a_dn ) {
+ Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: "
+ "no remote DN found on user\n",
+ op->o_log_prefix );
+ goto exit; /* user has no DN for the other directory */
+ }
+
+ ber_dupbv_x( &dn, a_dn->a_vals, op->o_tmpmemctx );
+ be_entry_release_r( op, e );
+ e = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: "
+ "(realm, dn) = (%s, %s)\n",
+ op->o_log_prefix, realm, dn.bv_val );
+
+ ldap_url = get_ldap_url( realm, isfile );
+ if ( !ldap_url ) {
+ Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: "
+ "No LDAP URL obtained\n",
+ op->o_log_prefix );
+ goto exit;
+ }
+
+retry:
+ rc = ldap_initialize( &ld, ldap_url );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: "
+ "Cannot initialize %s: %s\n",
+ op->o_log_prefix, ldap_url, ldap_err2string( rc ) );
+ goto exit; /* user has no DN for the other directory */
+ }
+
+ ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &protocol );
+
+#ifdef HAVE_TLS
+ rc = bindconf_tls_set( &ap->ad_tls, ld );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: "
+ "bindconf_tls_set failed\n",
+ op->o_log_prefix );
+ goto exit;
+ }
+
+ if ( ap->pins ) {
+ if ( (rc = ldap_set_option( ld, LDAP_OPT_CONNECT_CB, &ad_conncb )) !=
+ LDAP_SUCCESS ) {
+ goto exit;
+ }
+ }
+
+ if ( (rc = ldap_connect( ld )) != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: "
+ "Cannot connect to %s: %s\n",
+ op->o_log_prefix, ldap_url, ldap_err2string( rc ) );
+ goto exit;
+ }
+
+ if ( ap->ad_tls.sb_tls && !ldap_tls_inplace( ld ) ) {
+ if ( (rc = ldap_start_tls_s( ld, NULL, NULL )) != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: "
+ "LDAP TLS failed %s: %s\n",
+ op->o_log_prefix, ldap_url, ldap_err2string( rc ) );
+ goto exit;
+ }
+ }
+
+#endif /* HAVE_TLS */
+
+ rc = ldap_sasl_bind_s( ld, dn.bv_val, LDAP_SASL_SIMPLE,
+ &op->oq_bind.rb_cred, NULL, NULL, NULL );
+ if ( rc == LDAP_SUCCESS ) {
+ if ( ap->store_on_success ) {
+ const char *txt;
+
+ Operation op2 = *op;
+ SlapReply r2 = { REP_RESULT };
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+ Modifications m = {};
+
+ op2.o_tag = LDAP_REQ_MODIFY;
+ op2.o_callback = &cb;
+ op2.orm_modlist = &m;
+ op2.orm_no_opattrs = 0;
+ op2.o_dn = op->o_bd->be_rootdn;
+ op2.o_ndn = op->o_bd->be_rootndn;
+
+ m.sml_op = LDAP_MOD_ADD;
+ m.sml_flags = 0;
+ m.sml_next = NULL;
+ m.sml_type = ap->up_ad->ad_cname;
+ m.sml_desc = ap->up_ad;
+ m.sml_numvals = 1;
+ m.sml_values = op->o_tmpcalloc(
+ sizeof(struct berval), 2, op->o_tmpmemctx );
+
+ slap_passwd_hash( &op->oq_bind.rb_cred, &m.sml_values[0], &txt );
+ if ( m.sml_values[0].bv_val == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s remoteauth_bind: "
+ "password hashing for '%s' failed, storing password in "
+ "plain text\n",
+ op->o_log_prefix, op->o_req_dn.bv_val );
+ ber_dupbv( &m.sml_values[0], &op->oq_bind.rb_cred );
+ }
+
+ /*
+ * If this server is a shadow use the frontend to perform this
+ * modify. That will trigger the update referral, which can then be
+ * forwarded by the chain overlay. Obviously the updateref and
+ * chain overlay must be configured appropriately for this to be
+ * useful.
+ */
+ if ( SLAP_SHADOW(op->o_bd) ) {
+ op2.o_bd = frontendDB;
+ } else {
+ op2.o_bd->bd_info = (BackendInfo *)on->on_info;
+ }
+
+ if ( op2.o_bd->be_modify( &op2, &r2 ) != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s remoteauth_bind: "
+ "attempt to store password in entry '%s' failed, "
+ "ignoring\n",
+ op->o_log_prefix, op->o_req_dn.bv_val );
+ }
+ ch_free( m.sml_values[0].bv_val );
+ }
+ goto exit;
+ }
+
+ if ( rc == LDAP_INVALID_CREDENTIALS ) {
+ Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: "
+ "ldap_sasl_bind_s (%s) failed: invalid credentials\n",
+ op->o_log_prefix, ldap_url );
+ goto exit;
+ }
+
+ if ( tries < ap->retry_count ) {
+ Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: "
+ "ldap_sasl_bind_s failed %s: %s (try #%d)\n",
+ op->o_log_prefix, ldap_url, ldap_err2string( rc ), tries );
+ if ( ld ) ldap_unbind_ext_s( ld, NULL, NULL );
+ tries++;
+ goto retry;
+ } else
+ goto exit;
+
+exit:
+ if ( dn.bv_val ) {
+ op->o_tmpfree( dn.bv_val, op->o_tmpmemctx );
+ }
+ if ( e ) {
+ be_entry_release_r( op, e );
+ }
+ if ( ld ) ldap_unbind_ext_s( ld, NULL, NULL );
+ if ( ldap_url ) ch_free( ldap_url );
+ if ( realm ) ch_free( realm );
+ if ( rc == SLAP_CB_CONTINUE ) {
+ Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: "
+ "continue\n", op->o_log_prefix );
+ return rc;
+ } else {
+ /* for rc == 0, frontend sends result */
+ if ( rc ) {
+ if ( rc > 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: "
+ "failed\n", op->o_log_prefix );
+ send_ldap_error( op, rs, rc, "remoteauth_bind failed" );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "%s remoteauth_bind: "
+ "operations error\n", op->o_log_prefix );
+ send_ldap_error( op, rs, LDAP_OPERATIONS_ERROR,
+ "remoteauth_bind operations error" );
+ }
+ }
+
+ return rs->sr_err;
+ }
+}
+
+static int
+remoteauth_db_init( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ ad_private *ap;
+
+ if ( SLAP_ISGLOBALOVERLAY(be) ) {
+ Debug( LDAP_DEBUG_ANY, "remoteauth_db_init: "
+ "remoteauth overlay must be instantiated within a "
+ "database.\n" );
+ return 1;
+ }
+
+ ap = ch_calloc( 1, sizeof(ad_private) );
+
+ ap->dn = NULL;
+ ap->dn_ad = NULL;
+ ap->domain_attr = NULL;
+ ap->domain_ad = NULL;
+
+ ap->up_ad = NULL;
+ ap->mappings = NULL;
+
+ ap->default_realm = NULL;
+ ap->default_domain = NULL;
+
+ ap->pins = NULL;
+
+ ap->up_set = 0;
+ ap->retry_count = 3;
+
+ on->on_bi.bi_private = ap;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+remoteauth_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ ad_private *ap = (ad_private *)on->on_bi.bi_private;
+ ad_info *ai = ap->mappings;
+
+ while ( ai ) {
+ if ( ai->domain ) ch_free( ai->domain );
+ if ( ai->realm ) ch_free( ai->realm );
+ ai = ai->next;
+ }
+
+ if ( ap->dn ) ch_free( ap->dn );
+ if ( ap->default_domain ) ch_free( ap->default_domain );
+ if ( ap->default_realm ) ch_free( ap->default_realm );
+
+ bindconf_free( &ap->ad_tls );
+
+ ch_free( ap );
+
+ return 0;
+}
+
+static slap_overinst remoteauth;
+
+int
+remoteauth_initialize( void )
+{
+ int rc;
+
+ remoteauth.on_bi.bi_type = "remoteauth";
+ remoteauth.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+
+ remoteauth.on_bi.bi_cf_ocs = remoteauthocs;
+ rc = config_register_schema( remoteauthcfg, remoteauthocs );
+ if ( rc ) return rc;
+
+ remoteauth.on_bi.bi_db_init = remoteauth_db_init;
+ remoteauth.on_bi.bi_db_destroy = remoteauth_db_destroy;
+ remoteauth.on_bi.bi_op_bind = remoteauth_bind;
+
+ return overlay_register( &remoteauth );
+}
+
+#if SLAPD_OVER_ACCESSLOG == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return remoteauth_initialize();
+}
+#endif
diff --git a/servers/slapd/overlays/retcode.c b/servers/slapd/overlays/retcode.c
new file mode 100644
index 0000000..ac57146
--- /dev/null
+++ b/servers/slapd/overlays/retcode.c
@@ -0,0 +1,1578 @@
+/* retcode.c - customizable response for client testing purposes */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2005 Pierangelo Masarati <ando@sys-net.it>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_RETCODE
+
+#include <stdio.h>
+
+#include <ac/unistd.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+#include "ldif.h"
+
+static slap_overinst retcode;
+
+static AttributeDescription *ad_errCode;
+static AttributeDescription *ad_errText;
+static AttributeDescription *ad_errOp;
+static AttributeDescription *ad_errSleepTime;
+static AttributeDescription *ad_errMatchedDN;
+static AttributeDescription *ad_errUnsolicitedOID;
+static AttributeDescription *ad_errUnsolicitedData;
+static AttributeDescription *ad_errDisconnect;
+
+static ObjectClass *oc_errAbsObject;
+static ObjectClass *oc_errObject;
+static ObjectClass *oc_errAuxObject;
+
+typedef enum retcode_op_e {
+ SN_DG_OP_NONE = 0x0000,
+ SN_DG_OP_ADD = 0x0001,
+ SN_DG_OP_BIND = 0x0002,
+ SN_DG_OP_COMPARE = 0x0004,
+ SN_DG_OP_DELETE = 0x0008,
+ SN_DG_OP_MODIFY = 0x0010,
+ SN_DG_OP_RENAME = 0x0020,
+ SN_DG_OP_SEARCH = 0x0040,
+ SN_DG_EXTENDED = 0x0080,
+ SN_DG_OP_AUTH = SN_DG_OP_BIND,
+ SN_DG_OP_READ = (SN_DG_OP_COMPARE|SN_DG_OP_SEARCH),
+ SN_DG_OP_WRITE = (SN_DG_OP_ADD|SN_DG_OP_DELETE|SN_DG_OP_MODIFY|SN_DG_OP_RENAME),
+ SN_DG_OP_ALL = (SN_DG_OP_AUTH|SN_DG_OP_READ|SN_DG_OP_WRITE|SN_DG_EXTENDED)
+} retcode_op_e;
+
+typedef struct retcode_item_t {
+ struct berval rdi_line;
+ struct berval rdi_dn;
+ struct berval rdi_ndn;
+ struct berval rdi_text;
+ struct berval rdi_matched;
+ int rdi_err;
+ BerVarray rdi_ref;
+ int rdi_sleeptime;
+ Entry rdi_e;
+ slap_mask_t rdi_mask;
+ struct berval rdi_unsolicited_oid;
+ struct berval rdi_unsolicited_data;
+
+ unsigned rdi_flags;
+#define RDI_PRE_DISCONNECT (0x1U)
+#define RDI_POST_DISCONNECT (0x2U)
+
+ struct retcode_item_t *rdi_next;
+} retcode_item_t;
+
+typedef struct retcode_t {
+ struct berval rd_pdn;
+ struct berval rd_npdn;
+
+ int rd_sleep;
+
+ retcode_item_t *rd_item;
+
+ int rd_indir;
+#define RETCODE_FINDIR 0x01
+#define RETCODE_INDIR( rd ) ( (rd)->rd_indir )
+} retcode_t;
+
+static int
+retcode_entry_response( Operation *op, SlapReply *rs, BackendInfo *bi, Entry *e );
+
+static unsigned int
+retcode_sleep( int s )
+{
+ unsigned int r = 0;
+
+ /* sleep as required */
+ if ( s < 0 ) {
+#if 0 /* use high-order bits for better randomness (Numerical Recipes in "C") */
+ r = rand() % (-s);
+#endif
+ r = ((double)(-s))*rand()/(RAND_MAX + 1.0);
+ } else if ( s > 0 ) {
+ r = (unsigned int)s;
+ }
+ if ( r ) {
+ sleep( r );
+ }
+
+ return r;
+}
+
+static int
+retcode_cleanup_cb( Operation *op, SlapReply *rs )
+{
+ rs->sr_matched = NULL;
+ rs->sr_text = NULL;
+
+ if ( rs->sr_ref != NULL ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+
+ ch_free( op->o_callback );
+ op->o_callback = NULL;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+retcode_send_onelevel( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ retcode_t *rd = (retcode_t *)on->on_bi.bi_private;
+
+ retcode_item_t *rdi;
+
+ for ( rdi = rd->rd_item; rdi != NULL; rdi = rdi->rdi_next ) {
+ if ( op->o_abandon ) {
+ return rs->sr_err = SLAPD_ABANDON;
+ }
+
+ rs->sr_err = test_filter( op, &rdi->rdi_e, op->ors_filter );
+ if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
+ /* safe default */
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_operational_attrs = NULL;
+ rs->sr_ctrls = NULL;
+ rs->sr_flags = 0;
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_entry = &rdi->rdi_e;
+
+ rs->sr_err = send_search_entry( op, rs );
+ rs->sr_flags = 0;
+ rs->sr_entry = NULL;
+ rs->sr_attrs = NULL;
+
+ switch ( rs->sr_err ) {
+ case LDAP_UNAVAILABLE: /* connection closed */
+ rs->sr_err = LDAP_OTHER;
+ /* fallthru */
+ case LDAP_SIZELIMIT_EXCEEDED:
+ goto done;
+ }
+ }
+ rs->sr_err = LDAP_SUCCESS;
+ }
+
+done:;
+
+ send_ldap_result( op, rs );
+
+ return rs->sr_err;
+}
+
+static int
+retcode_op_add( Operation *op, SlapReply *rs )
+{
+ return retcode_entry_response( op, rs, NULL, op->ora_e );
+}
+
+typedef struct retcode_cb_t {
+ BackendInfo *rdc_info;
+ unsigned rdc_flags;
+ ber_tag_t rdc_tag;
+ AttributeName *rdc_attrs;
+} retcode_cb_t;
+
+static int
+retcode_cb_response( Operation *op, SlapReply *rs )
+{
+ retcode_cb_t *rdc = (retcode_cb_t *)op->o_callback->sc_private;
+
+ op->o_tag = rdc->rdc_tag;
+ if ( rs->sr_type == REP_SEARCH ) {
+ ber_tag_t o_tag = op->o_tag;
+ int rc;
+
+ if ( op->o_tag == LDAP_REQ_SEARCH ) {
+ rs->sr_attrs = rdc->rdc_attrs;
+ }
+ rc = retcode_entry_response( op, rs, rdc->rdc_info, rs->sr_entry );
+ op->o_tag = o_tag;
+
+ return rc;
+ }
+
+ switch ( rs->sr_err ) {
+ case LDAP_SUCCESS:
+ case LDAP_NO_SUCH_OBJECT:
+ /* in case of noSuchObject, stop the internal search
+ * for in-directory error stuff */
+ if ( !op->o_abandon ) {
+ rdc->rdc_flags = SLAP_CB_CONTINUE;
+ }
+ return 0;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+retcode_op_internal( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+
+ Operation op2 = *op;
+ BackendDB db = *op->o_bd;
+ slap_callback sc = { 0 };
+ retcode_cb_t rdc;
+
+ int rc;
+
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2.ors_scope = LDAP_SCOPE_BASE;
+ op2.ors_deref = LDAP_DEREF_NEVER;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+ op2.ors_slimit = SLAP_NO_LIMIT;
+ op2.ors_limit = NULL;
+ op2.ors_attrsonly = 0;
+ op2.ors_attrs = slap_anlist_all_attributes;
+
+ ber_str2bv_x( "(objectClass=errAbsObject)",
+ STRLENOF( "(objectClass=errAbsObject)" ),
+ 1, &op2.ors_filterstr, op2.o_tmpmemctx );
+ op2.ors_filter = str2filter_x( &op2, op2.ors_filterstr.bv_val );
+
+ /* errAbsObject is defined by this overlay! */
+ assert( op2.ors_filter != NULL );
+
+ db.bd_info = on->on_info->oi_orig;
+ op2.o_bd = &db;
+
+ rdc.rdc_info = on->on_info->oi_orig;
+ rdc.rdc_flags = RETCODE_FINDIR;
+ if ( op->o_tag == LDAP_REQ_SEARCH ) {
+ rdc.rdc_attrs = op->ors_attrs;
+ }
+ rdc.rdc_tag = op->o_tag;
+ sc.sc_response = retcode_cb_response;
+ sc.sc_private = &rdc;
+ op2.o_callback = &sc;
+
+ rc = op2.o_bd->be_search( &op2, rs );
+ op->o_abandon = op2.o_abandon;
+
+ filter_free_x( &op2, op2.ors_filter, 1 );
+ ber_memfree_x( op2.ors_filterstr.bv_val, op2.o_tmpmemctx );
+
+ if ( rdc.rdc_flags == SLAP_CB_CONTINUE ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ return rc;
+}
+
+static int
+retcode_op_func( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ retcode_t *rd = (retcode_t *)on->on_bi.bi_private;
+
+ retcode_item_t *rdi;
+ struct berval nrdn, npdn;
+
+ slap_callback *cb = NULL;
+
+ /* sleep as required */
+ retcode_sleep( rd->rd_sleep );
+
+ if ( !dnIsSuffix( &op->o_req_ndn, &rd->rd_npdn ) ) {
+ if ( RETCODE_INDIR( rd ) ) {
+ switch ( op->o_tag ) {
+ case LDAP_REQ_ADD:
+ return retcode_op_add( op, rs );
+
+ case LDAP_REQ_BIND:
+ /* skip if rootdn */
+ /* FIXME: better give the db a chance? */
+ if ( be_isroot_pw( op ) ) {
+ return LDAP_SUCCESS;
+ }
+ return retcode_op_internal( op, rs );
+
+ case LDAP_REQ_SEARCH:
+ if ( op->ors_scope == LDAP_SCOPE_BASE ) {
+ rs->sr_err = retcode_op_internal( op, rs );
+ switch ( rs->sr_err ) {
+ case SLAP_CB_CONTINUE:
+ if ( rs->sr_nentries == 0 ) {
+ break;
+ }
+ rs->sr_err = LDAP_SUCCESS;
+ /* fallthru */
+
+ default:
+ send_ldap_result( op, rs );
+ break;
+ }
+ return rs->sr_err;
+ }
+ break;
+
+ case LDAP_REQ_MODIFY:
+ case LDAP_REQ_DELETE:
+ case LDAP_REQ_MODRDN:
+ case LDAP_REQ_COMPARE:
+ return retcode_op_internal( op, rs );
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( op->o_tag == LDAP_REQ_SEARCH
+ && op->ors_scope != LDAP_SCOPE_BASE
+ && op->o_req_ndn.bv_len == rd->rd_npdn.bv_len )
+ {
+ return retcode_send_onelevel( op, rs );
+ }
+
+ dnParent( &op->o_req_ndn, &npdn );
+ if ( npdn.bv_len != rd->rd_npdn.bv_len ) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_matched = rd->rd_pdn.bv_val;
+ send_ldap_result( op, rs );
+ rs->sr_matched = NULL;
+ return rs->sr_err;
+ }
+
+ dnRdn( &op->o_req_ndn, &nrdn );
+
+ for ( rdi = rd->rd_item; rdi != NULL; rdi = rdi->rdi_next ) {
+ struct berval rdi_nrdn;
+
+ dnRdn( &rdi->rdi_ndn, &rdi_nrdn );
+ if ( dn_match( &nrdn, &rdi_nrdn ) ) {
+ break;
+ }
+ }
+
+ if ( rdi != NULL && rdi->rdi_mask != SN_DG_OP_ALL ) {
+ retcode_op_e o_tag = SN_DG_OP_NONE;
+
+ switch ( op->o_tag ) {
+ case LDAP_REQ_ADD:
+ o_tag = SN_DG_OP_ADD;
+ break;
+
+ case LDAP_REQ_BIND:
+ o_tag = SN_DG_OP_BIND;
+ break;
+
+ case LDAP_REQ_COMPARE:
+ o_tag = SN_DG_OP_COMPARE;
+ break;
+
+ case LDAP_REQ_DELETE:
+ o_tag = SN_DG_OP_DELETE;
+ break;
+
+ case LDAP_REQ_MODIFY:
+ o_tag = SN_DG_OP_MODIFY;
+ break;
+
+ case LDAP_REQ_MODRDN:
+ o_tag = SN_DG_OP_RENAME;
+ break;
+
+ case LDAP_REQ_SEARCH:
+ o_tag = SN_DG_OP_SEARCH;
+ break;
+
+ case LDAP_REQ_EXTENDED:
+ o_tag = SN_DG_EXTENDED;
+ break;
+
+ default:
+ /* Should not happen */
+ break;
+ }
+
+ if ( !( o_tag & rdi->rdi_mask ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+ }
+
+ if ( rdi == NULL ) {
+ rs->sr_matched = rd->rd_pdn.bv_val;
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = "retcode not found";
+
+ } else {
+ if ( rdi->rdi_flags & RDI_PRE_DISCONNECT ) {
+ return rs->sr_err = SLAPD_DISCONNECT;
+ }
+
+ rs->sr_err = rdi->rdi_err;
+ rs->sr_text = rdi->rdi_text.bv_val;
+ rs->sr_matched = rdi->rdi_matched.bv_val;
+
+ /* FIXME: we only honor the rdi_ref field in case rdi_err
+ * is LDAP_REFERRAL otherwise send_ldap_result() bails out */
+ if ( rs->sr_err == LDAP_REFERRAL ) {
+ BerVarray ref;
+
+ if ( rdi->rdi_ref != NULL ) {
+ ref = rdi->rdi_ref;
+ } else {
+ ref = default_referral;
+ }
+
+ if ( ref != NULL ) {
+ rs->sr_ref = referral_rewrite( ref,
+ NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+
+ } else {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "bad referral object";
+ }
+ }
+
+ retcode_sleep( rdi->rdi_sleeptime );
+ }
+
+ switch ( op->o_tag ) {
+ case LDAP_REQ_EXTENDED:
+ if ( rdi == NULL ) {
+ break;
+ }
+ cb = ( slap_callback * )ch_malloc( sizeof( slap_callback ) );
+ memset( cb, 0, sizeof( slap_callback ) );
+ cb->sc_cleanup = retcode_cleanup_cb;
+ op->o_callback = cb;
+ break;
+
+ default:
+ if ( rdi && !BER_BVISNULL( &rdi->rdi_unsolicited_oid ) ) {
+ ber_int_t msgid = op->o_msgid;
+
+ /* RFC 4511 unsolicited response */
+
+ op->o_msgid = 0;
+ if ( strcmp( rdi->rdi_unsolicited_oid.bv_val, "0" ) == 0 ) {
+ send_ldap_result( op, rs );
+
+ } else {
+ ber_tag_t tag = op->o_tag;
+
+ op->o_tag = LDAP_REQ_EXTENDED;
+ rs->sr_rspoid = rdi->rdi_unsolicited_oid.bv_val;
+ if ( !BER_BVISNULL( &rdi->rdi_unsolicited_data ) ) {
+ rs->sr_rspdata = &rdi->rdi_unsolicited_data;
+ }
+ send_ldap_extended( op, rs );
+ rs->sr_rspoid = NULL;
+ rs->sr_rspdata = NULL;
+ op->o_tag = tag;
+
+ }
+ op->o_msgid = msgid;
+
+ } else {
+ send_ldap_result( op, rs );
+ }
+
+ if ( rs->sr_ref != NULL ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+ rs->sr_matched = NULL;
+ rs->sr_text = NULL;
+
+ if ( rdi && rdi->rdi_flags & RDI_POST_DISCONNECT ) {
+ return rs->sr_err = SLAPD_DISCONNECT;
+ }
+ break;
+ }
+
+ return rs->sr_err;
+}
+
+static int
+retcode_op2str( ber_tag_t op, struct berval *bv )
+{
+ switch ( op ) {
+ case LDAP_REQ_BIND:
+ BER_BVSTR( bv, "bind" );
+ return 0;
+ case LDAP_REQ_ADD:
+ BER_BVSTR( bv, "add" );
+ return 0;
+ case LDAP_REQ_DELETE:
+ BER_BVSTR( bv, "delete" );
+ return 0;
+ case LDAP_REQ_MODRDN:
+ BER_BVSTR( bv, "modrdn" );
+ return 0;
+ case LDAP_REQ_MODIFY:
+ BER_BVSTR( bv, "modify" );
+ return 0;
+ case LDAP_REQ_COMPARE:
+ BER_BVSTR( bv, "compare" );
+ return 0;
+ case LDAP_REQ_SEARCH:
+ BER_BVSTR( bv, "search" );
+ return 0;
+ case LDAP_REQ_EXTENDED:
+ BER_BVSTR( bv, "extended" );
+ return 0;
+ }
+ return -1;
+}
+
+static int
+retcode_entry_response( Operation *op, SlapReply *rs, BackendInfo *bi, Entry *e )
+{
+ Attribute *a;
+ int err;
+ char *next;
+ int disconnect = 0;
+
+ if ( get_manageDSAit( op ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( !is_entry_objectclass_or_sub( e, oc_errAbsObject ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* operation */
+ a = attr_find( e->e_attrs, ad_errOp );
+ if ( a != NULL ) {
+ int i,
+ gotit = 0;
+ struct berval bv = BER_BVNULL;
+
+ (void)retcode_op2str( op->o_tag, &bv );
+
+ if ( BER_BVISNULL( &bv ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
+ if ( bvmatch( &a->a_nvals[ i ], &bv ) ) {
+ gotit = 1;
+ break;
+ }
+ }
+
+ if ( !gotit ) {
+ return SLAP_CB_CONTINUE;
+ }
+ }
+
+ /* disconnect */
+ a = attr_find( e->e_attrs, ad_errDisconnect );
+ if ( a != NULL ) {
+ if ( bvmatch( &a->a_nvals[ 0 ], &slap_true_bv ) ) {
+ return rs->sr_err = SLAPD_DISCONNECT;
+ }
+ disconnect = 1;
+ }
+
+ /* error code */
+ a = attr_find( e->e_attrs, ad_errCode );
+ if ( a == NULL ) {
+ return SLAP_CB_CONTINUE;
+ }
+ err = strtol( a->a_nvals[ 0 ].bv_val, &next, 0 );
+ if ( next == a->a_nvals[ 0 ].bv_val || next[ 0 ] != '\0' ) {
+ return SLAP_CB_CONTINUE;
+ }
+ rs->sr_err = err;
+
+ /* sleep time */
+ a = attr_find( e->e_attrs, ad_errSleepTime );
+ if ( a != NULL && a->a_nvals[ 0 ].bv_val[ 0 ] != '-' ) {
+ int sleepTime;
+
+ if ( lutil_atoi( &sleepTime, a->a_nvals[ 0 ].bv_val ) == 0 ) {
+ retcode_sleep( sleepTime );
+ }
+ }
+
+ if ( rs->sr_err != LDAP_SUCCESS && !LDAP_API_ERROR( rs->sr_err )) {
+ BackendDB db = *op->o_bd,
+ *o_bd = op->o_bd;
+ void *o_callback = op->o_callback;
+
+ /* message text */
+ a = attr_find( e->e_attrs, ad_errText );
+ if ( a != NULL ) {
+ rs->sr_text = a->a_vals[ 0 ].bv_val;
+ }
+
+ /* matched DN */
+ a = attr_find( e->e_attrs, ad_errMatchedDN );
+ if ( a != NULL ) {
+ rs->sr_matched = a->a_vals[ 0 ].bv_val;
+ }
+
+ if ( bi == NULL ) {
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+
+ bi = on->on_info->oi_orig;
+ }
+
+ db.bd_info = bi;
+ op->o_bd = &db;
+ op->o_callback = NULL;
+
+ /* referral */
+ if ( rs->sr_err == LDAP_REFERRAL ) {
+ BerVarray refs = default_referral;
+
+ a = attr_find( e->e_attrs, slap_schema.si_ad_ref );
+ if ( a != NULL ) {
+ refs = a->a_vals;
+ }
+ rs->sr_ref = referral_rewrite( refs,
+ NULL, &op->o_req_dn, op->oq_search.rs_scope );
+
+ send_search_reference( op, rs );
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+
+ } else {
+ a = attr_find( e->e_attrs, ad_errUnsolicitedOID );
+ if ( a != NULL ) {
+ struct berval oid = BER_BVNULL,
+ data = BER_BVNULL;
+ ber_int_t msgid = op->o_msgid;
+
+ /* RFC 4511 unsolicited response */
+
+ op->o_msgid = 0;
+
+ oid = a->a_nvals[ 0 ];
+
+ a = attr_find( e->e_attrs, ad_errUnsolicitedData );
+ if ( a != NULL ) {
+ data = a->a_nvals[ 0 ];
+ }
+
+ if ( strcmp( oid.bv_val, "0" ) == 0 ) {
+ send_ldap_result( op, rs );
+
+ } else {
+ ber_tag_t tag = op->o_tag;
+
+ op->o_tag = LDAP_REQ_EXTENDED;
+ rs->sr_rspoid = oid.bv_val;
+ if ( !BER_BVISNULL( &data ) ) {
+ rs->sr_rspdata = &data;
+ }
+ send_ldap_extended( op, rs );
+ rs->sr_rspoid = NULL;
+ rs->sr_rspdata = NULL;
+ op->o_tag = tag;
+ }
+ op->o_msgid = msgid;
+
+ } else {
+ send_ldap_result( op, rs );
+ }
+ }
+
+ rs->sr_text = NULL;
+ rs->sr_matched = NULL;
+ op->o_bd = o_bd;
+ op->o_callback = o_callback;
+ }
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ if ( disconnect ) {
+ return rs->sr_err = SLAPD_DISCONNECT;
+ }
+
+ op->o_abandon = 1;
+ return rs->sr_err;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+retcode_response( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ retcode_t *rd = (retcode_t *)on->on_bi.bi_private;
+
+ if ( rs->sr_type != REP_SEARCH || !RETCODE_INDIR( rd ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ return retcode_entry_response( op, rs, NULL, rs->sr_entry );
+}
+
+static int
+retcode_db_init( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ retcode_t *rd;
+
+ srand( getpid() );
+
+ rd = (retcode_t *)ch_malloc( sizeof( retcode_t ) );
+ memset( rd, 0, sizeof( retcode_t ) );
+
+ on->on_bi.bi_private = (void *)rd;
+
+ return 0;
+}
+
+static void
+retcode_item_destroy( retcode_item_t *rdi )
+{
+ ber_memfree( rdi->rdi_line.bv_val );
+
+ ber_memfree( rdi->rdi_dn.bv_val );
+ ber_memfree( rdi->rdi_ndn.bv_val );
+
+ if ( !BER_BVISNULL( &rdi->rdi_text ) ) {
+ ber_memfree( rdi->rdi_text.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &rdi->rdi_matched ) ) {
+ ber_memfree( rdi->rdi_matched.bv_val );
+ }
+
+ if ( rdi->rdi_ref ) {
+ ber_bvarray_free( rdi->rdi_ref );
+ }
+
+ BER_BVZERO( &rdi->rdi_e.e_name );
+ BER_BVZERO( &rdi->rdi_e.e_nname );
+
+ entry_clean( &rdi->rdi_e );
+
+ if ( !BER_BVISNULL( &rdi->rdi_unsolicited_oid ) ) {
+ ber_memfree( rdi->rdi_unsolicited_oid.bv_val );
+ if ( !BER_BVISNULL( &rdi->rdi_unsolicited_data ) )
+ ber_memfree( rdi->rdi_unsolicited_data.bv_val );
+ }
+
+ ch_free( rdi );
+}
+
+enum {
+ RC_PARENT = 1,
+ RC_ITEM
+};
+
+static ConfigDriver rc_cf_gen;
+
+static ConfigTable rccfg[] = {
+ { "retcode-parent", "dn",
+ 2, 2, 0, ARG_MAGIC|ARG_DN|ARG_QUOTE|RC_PARENT, rc_cf_gen,
+ "( OLcfgOvAt:20.1 NAME 'olcRetcodeParent' "
+ "DESC '' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
+ { "retcode-item", "rdn> <retcode> <...",
+ 3, 0, 0, ARG_MAGIC|RC_ITEM, rc_cf_gen,
+ "( OLcfgOvAt:20.2 NAME 'olcRetcodeItem' "
+ "DESC '' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "X-ORDERED 'VALUES' )", NULL, NULL },
+ { "retcode-indir", "on|off",
+ 1, 2, 0, ARG_OFFSET|ARG_ON_OFF,
+ (void *)offsetof(retcode_t, rd_indir),
+ "( OLcfgOvAt:20.3 NAME 'olcRetcodeInDir' "
+ "DESC '' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+
+ { "retcode-sleep", "sleeptime",
+ 2, 2, 0, ARG_OFFSET|ARG_INT,
+ (void *)offsetof(retcode_t, rd_sleep),
+ "( OLcfgOvAt:20.4 NAME 'olcRetcodeSleep' "
+ "DESC '' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs rcocs[] = {
+ { "( OLcfgOvOc:20.1 "
+ "NAME 'olcRetcodeConfig' "
+ "DESC 'Retcode configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcRetcodeParent "
+ "$ olcRetcodeItem "
+ "$ olcRetcodeInDir "
+ "$ olcRetcodeSleep "
+ ") )",
+ Cft_Overlay, rccfg, NULL, NULL },
+ { NULL, 0, NULL }
+};
+
+static int
+rc_cf_gen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ retcode_t *rd = (retcode_t *)on->on_bi.bi_private;
+ int rc = ARG_BAD_CONF;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ switch( c->type ) {
+ case RC_PARENT:
+ if ( !BER_BVISEMPTY( &rd->rd_pdn )) {
+ rc = value_add_one( &c->rvalue_vals,
+ &rd->rd_pdn );
+ if ( rc == 0 ) {
+ rc = value_add_one( &c->rvalue_nvals,
+ &rd->rd_npdn );
+ }
+ return rc;
+ }
+ rc = 0;
+ break;
+
+ case RC_ITEM: {
+ retcode_item_t *rdi;
+ int i;
+
+ for ( rdi = rd->rd_item, i = 0; rdi; rdi = rdi->rdi_next, i++ ) {
+ char buf[4096];
+ struct berval bv;
+ char *ptr;
+
+ bv.bv_len = snprintf( buf, sizeof( buf ), SLAP_X_ORDERED_FMT, i );
+ bv.bv_len += rdi->rdi_line.bv_len;
+ ptr = bv.bv_val = ch_malloc( bv.bv_len + 1 );
+ ptr = lutil_strcopy( ptr, buf );
+ ptr = lutil_strncopy( ptr, rdi->rdi_line.bv_val, rdi->rdi_line.bv_len );
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ }
+ rc = 0;
+ } break;
+
+ default:
+ assert( 0 );
+ break;
+ }
+
+ return rc;
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ switch( c->type ) {
+ case RC_PARENT:
+ if ( rd->rd_pdn.bv_val ) {
+ ber_memfree ( rd->rd_pdn.bv_val );
+ rc = 0;
+ }
+ if ( rd->rd_npdn.bv_val ) {
+ ber_memfree ( rd->rd_npdn.bv_val );
+ }
+ break;
+
+ case RC_ITEM:
+ if ( c->valx == -1 ) {
+ retcode_item_t *rdi, *next;
+
+ for ( rdi = rd->rd_item; rdi != NULL; rdi = next ) {
+ next = rdi->rdi_next;
+ retcode_item_destroy( rdi );
+ }
+
+ } else {
+ retcode_item_t **rdip, *rdi;
+ int i;
+
+ for ( rdip = &rd->rd_item, i = 0; i <= c->valx && *rdip; i++, rdip = &(*rdip)->rdi_next )
+ ;
+ if ( *rdip == NULL ) {
+ return 1;
+ }
+ rdi = *rdip;
+ *rdip = rdi->rdi_next;
+
+ retcode_item_destroy( rdi );
+ }
+ rc = 0;
+ break;
+
+ default:
+ assert( 0 );
+ break;
+ }
+ return rc; /* FIXME */
+ }
+
+ switch( c->type ) {
+ case RC_PARENT:
+ if ( rd->rd_pdn.bv_val ) {
+ ber_memfree ( rd->rd_pdn.bv_val );
+ }
+ if ( rd->rd_npdn.bv_val ) {
+ ber_memfree ( rd->rd_npdn.bv_val );
+ }
+ rd->rd_pdn = c->value_dn;
+ rd->rd_npdn = c->value_ndn;
+ rc = 0;
+ break;
+
+ case RC_ITEM: {
+ retcode_item_t rdi = { BER_BVNULL }, **rdip;
+ struct berval bv, rdn, nrdn;
+ char *next = NULL;
+ int i;
+
+ if ( c->argc < 3 ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "\"retcode-item <RDN> <retcode> [<text>]\": "
+ "missing args" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+
+ ber_str2bv( c->argv[ 1 ], 0, 0, &bv );
+
+ rc = dnPrettyNormal( NULL, &bv, &rdn, &nrdn, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "unable to normalize RDN \"%s\": %d",
+ c->argv[ 1 ], rc );
+ Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+
+ if ( !dnIsOneLevelRDN( &nrdn ) ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "value \"%s\" is not a RDN",
+ c->argv[ 1 ] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+
+ if ( BER_BVISNULL( &rd->rd_npdn ) ) {
+ /* FIXME: we use the database suffix */
+ if ( c->be->be_nsuffix == NULL ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "either \"retcode-parent\" "
+ "or \"suffix\" must be defined" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+
+ ber_dupbv( &rd->rd_pdn, &c->be->be_suffix[ 0 ] );
+ ber_dupbv( &rd->rd_npdn, &c->be->be_nsuffix[ 0 ] );
+ }
+
+ build_new_dn( &rdi.rdi_dn, &rd->rd_pdn, &rdn, NULL );
+ build_new_dn( &rdi.rdi_ndn, &rd->rd_npdn, &nrdn, NULL );
+
+ ch_free( rdn.bv_val );
+ ch_free( nrdn.bv_val );
+
+ rdi.rdi_err = strtol( c->argv[ 2 ], &next, 0 );
+ if ( next == c->argv[ 2 ] || next[ 0 ] != '\0' ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "unable to parse return code \"%s\"",
+ c->argv[ 2 ] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+
+ rdi.rdi_mask = SN_DG_OP_ALL;
+
+ if ( c->argc > 3 ) {
+ for ( i = 3; i < c->argc; i++ ) {
+ if ( strncasecmp( c->argv[ i ], "op=", STRLENOF( "op=" ) ) == 0 )
+ {
+ char **ops;
+ int j;
+
+ ops = ldap_str2charray( &c->argv[ i ][ STRLENOF( "op=" ) ], "," );
+ assert( ops != NULL );
+
+ rdi.rdi_mask = SN_DG_OP_NONE;
+
+ for ( j = 0; ops[ j ] != NULL; j++ ) {
+ if ( strcasecmp( ops[ j ], "add" ) == 0 ) {
+ rdi.rdi_mask |= SN_DG_OP_ADD;
+
+ } else if ( strcasecmp( ops[ j ], "bind" ) == 0 ) {
+ rdi.rdi_mask |= SN_DG_OP_BIND;
+
+ } else if ( strcasecmp( ops[ j ], "compare" ) == 0 ) {
+ rdi.rdi_mask |= SN_DG_OP_COMPARE;
+
+ } else if ( strcasecmp( ops[ j ], "delete" ) == 0 ) {
+ rdi.rdi_mask |= SN_DG_OP_DELETE;
+
+ } else if ( strcasecmp( ops[ j ], "modify" ) == 0 ) {
+ rdi.rdi_mask |= SN_DG_OP_MODIFY;
+
+ } else if ( strcasecmp( ops[ j ], "rename" ) == 0
+ || strcasecmp( ops[ j ], "modrdn" ) == 0 )
+ {
+ rdi.rdi_mask |= SN_DG_OP_RENAME;
+
+ } else if ( strcasecmp( ops[ j ], "search" ) == 0 ) {
+ rdi.rdi_mask |= SN_DG_OP_SEARCH;
+
+ } else if ( strcasecmp( ops[ j ], "extended" ) == 0 ) {
+ rdi.rdi_mask |= SN_DG_EXTENDED;
+
+ } else if ( strcasecmp( ops[ j ], "auth" ) == 0 ) {
+ rdi.rdi_mask |= SN_DG_OP_AUTH;
+
+ } else if ( strcasecmp( ops[ j ], "read" ) == 0 ) {
+ rdi.rdi_mask |= SN_DG_OP_READ;
+
+ } else if ( strcasecmp( ops[ j ], "write" ) == 0 ) {
+ rdi.rdi_mask |= SN_DG_OP_WRITE;
+
+ } else if ( strcasecmp( ops[ j ], "all" ) == 0 ) {
+ rdi.rdi_mask |= SN_DG_OP_ALL;
+
+ } else {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "unknown op \"%s\"",
+ ops[ j ] );
+ ldap_charray_free( ops );
+ Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ }
+
+ ldap_charray_free( ops );
+
+ } else if ( strncasecmp( c->argv[ i ], "text=", STRLENOF( "text=" ) ) == 0 )
+ {
+ if ( !BER_BVISNULL( &rdi.rdi_text ) ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "\"text\" already provided" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ ber_str2bv( &c->argv[ i ][ STRLENOF( "text=" ) ], 0, 1, &rdi.rdi_text );
+
+ } else if ( strncasecmp( c->argv[ i ], "matched=", STRLENOF( "matched=" ) ) == 0 )
+ {
+ struct berval dn;
+
+ if ( !BER_BVISNULL( &rdi.rdi_matched ) ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "\"matched\" already provided" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ ber_str2bv( &c->argv[ i ][ STRLENOF( "matched=" ) ], 0, 0, &dn );
+ if ( dnPretty( NULL, &dn, &rdi.rdi_matched, NULL ) != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "unable to prettify matched DN \"%s\"",
+ &c->argv[ i ][ STRLENOF( "matched=" ) ] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+
+ } else if ( strncasecmp( c->argv[ i ], "ref=", STRLENOF( "ref=" ) ) == 0 )
+ {
+ char **refs;
+ int j;
+
+ if ( rdi.rdi_ref != NULL ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "\"ref\" already provided" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+
+ if ( rdi.rdi_err != LDAP_REFERRAL ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "providing \"ref\" "
+ "along with a non-referral "
+ "resultCode may cause slapd failures "
+ "related to internal checks" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
+ c->log, c->cr_msg );
+ }
+
+ refs = ldap_str2charray( &c->argv[ i ][ STRLENOF( "ref=" ) ], " " );
+ assert( refs != NULL );
+
+ for ( j = 0; refs[ j ] != NULL; j++ ) {
+ struct berval bv;
+
+ ber_str2bv( refs[ j ], 0, 1, &bv );
+ ber_bvarray_add( &rdi.rdi_ref, &bv );
+ }
+
+ ldap_charray_free( refs );
+
+ } else if ( strncasecmp( c->argv[ i ], "sleeptime=", STRLENOF( "sleeptime=" ) ) == 0 )
+ {
+ if ( rdi.rdi_sleeptime != 0 ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "\"sleeptime\" already provided" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+
+ if ( lutil_atoi( &rdi.rdi_sleeptime, &c->argv[ i ][ STRLENOF( "sleeptime=" ) ] ) ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "unable to parse \"sleeptime=%s\"",
+ &c->argv[ i ][ STRLENOF( "sleeptime=" ) ] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+
+ } else if ( strncasecmp( c->argv[ i ], "unsolicited=", STRLENOF( "unsolicited=" ) ) == 0 )
+ {
+ char *data;
+
+ if ( !BER_BVISNULL( &rdi.rdi_unsolicited_oid ) ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "\"unsolicited\" already provided" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+
+ data = strchr( &c->argv[ i ][ STRLENOF( "unsolicited=" ) ], ':' );
+ if ( data != NULL ) {
+ struct berval oid;
+
+ if ( ldif_parse_line2( &c->argv[ i ][ STRLENOF( "unsolicited=" ) ],
+ &oid, &rdi.rdi_unsolicited_data, NULL ) )
+ {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "unable to parse \"unsolicited\"" );
+ Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+
+ ber_dupbv( &rdi.rdi_unsolicited_oid, &oid );
+
+ } else {
+ ber_str2bv( &c->argv[ i ][ STRLENOF( "unsolicited=" ) ], 0, 1,
+ &rdi.rdi_unsolicited_oid );
+ }
+
+ } else if ( strncasecmp( c->argv[ i ], "flags=", STRLENOF( "flags=" ) ) == 0 )
+ {
+ char *arg = &c->argv[ i ][ STRLENOF( "flags=" ) ];
+ if ( strcasecmp( arg, "disconnect" ) == 0 ) {
+ rdi.rdi_flags |= RDI_PRE_DISCONNECT;
+
+ } else if ( strcasecmp( arg, "pre-disconnect" ) == 0 ) {
+ rdi.rdi_flags |= RDI_PRE_DISCONNECT;
+
+ } else if ( strcasecmp( arg, "post-disconnect" ) == 0 ) {
+ rdi.rdi_flags |= RDI_POST_DISCONNECT;
+
+ } else {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "unknown flag \"%s\"", arg );
+ Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+
+ } else {
+ snprintf( c->cr_msg, sizeof(c->cr_msg),
+ "unknown option \"%s\"",
+ c->argv[ i ] );
+ Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
+ c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ }
+ }
+
+ rdi.rdi_line.bv_len = 2*(c->argc - 1) + c->argc - 2;
+ for ( i = 1; i < c->argc; i++ ) {
+ rdi.rdi_line.bv_len += strlen( c->argv[ i ] );
+ }
+ next = rdi.rdi_line.bv_val = ch_malloc( rdi.rdi_line.bv_len + 1 );
+
+ for ( i = 1; i < c->argc; i++ ) {
+ *next++ = '"';
+ next = lutil_strcopy( next, c->argv[ i ] );
+ *next++ = '"';
+ *next++ = ' ';
+ }
+ *--next = '\0';
+
+ /* We're marked X-ORDERED 'VALUES', valx might be valid */
+ for ( i = 0, rdip = &rd->rd_item;
+ *rdip && (c->valx < 0 || i < c->valx);
+ rdip = &(*rdip)->rdi_next, i++ )
+ /* go to position */ ;
+
+
+ rdi.rdi_next = *rdip;
+ *rdip = ( retcode_item_t * )ch_malloc( sizeof( retcode_item_t ) );
+ *(*rdip) = rdi;
+
+ rc = 0;
+ } break;
+
+ default:
+ rc = SLAP_CONF_UNKNOWN;
+ break;
+ }
+
+ return rc;
+}
+
+static int
+retcode_db_open( BackendDB *be, ConfigReply *cr)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ retcode_t *rd = (retcode_t *)on->on_bi.bi_private;
+
+ retcode_item_t *rdi;
+
+ for ( rdi = rd->rd_item; rdi; rdi = rdi->rdi_next ) {
+ LDAPRDN rdn = NULL;
+ int rc, j;
+ char* p;
+ struct berval val[ 3 ];
+ char buf[ SLAP_TEXT_BUFLEN ];
+
+ /* DN */
+ rdi->rdi_e.e_name = rdi->rdi_dn;
+ rdi->rdi_e.e_nname = rdi->rdi_ndn;
+
+ /* objectClass */
+ val[ 0 ] = oc_errObject->soc_cname;
+ val[ 1 ] = slap_schema.si_oc_extensibleObject->soc_cname;
+ BER_BVZERO( &val[ 2 ] );
+
+ attr_merge( &rdi->rdi_e, slap_schema.si_ad_objectClass, val, NULL );
+
+ /* RDN avas */
+ rc = ldap_bv2rdn( &rdi->rdi_dn, &rdn, (char **) &p,
+ LDAP_DN_FORMAT_LDAP );
+
+ assert( rc == LDAP_SUCCESS );
+
+ for ( j = 0; rdn[ j ]; j++ ) {
+ LDAPAVA *ava = rdn[ j ];
+ AttributeDescription *ad = NULL;
+ const char *text;
+
+ rc = slap_bv2ad( &ava->la_attr, &ad, &text );
+ assert( rc == LDAP_SUCCESS );
+
+ attr_merge_normalize_one( &rdi->rdi_e, ad,
+ &ava->la_value, NULL );
+ }
+
+ ldap_rdnfree( rdn );
+
+ /* error code */
+ snprintf( buf, sizeof( buf ), "%d", rdi->rdi_err );
+ ber_str2bv( buf, 0, 0, &val[ 0 ] );
+
+ attr_merge_one( &rdi->rdi_e, ad_errCode, &val[ 0 ], NULL );
+
+ if ( rdi->rdi_ref != NULL ) {
+ attr_merge_normalize( &rdi->rdi_e, slap_schema.si_ad_ref,
+ rdi->rdi_ref, NULL );
+ }
+
+ /* text */
+ if ( !BER_BVISNULL( &rdi->rdi_text ) ) {
+ val[ 0 ] = rdi->rdi_text;
+
+ attr_merge_normalize_one( &rdi->rdi_e, ad_errText, &val[ 0 ], NULL );
+ }
+
+ /* matched */
+ if ( !BER_BVISNULL( &rdi->rdi_matched ) ) {
+ val[ 0 ] = rdi->rdi_matched;
+
+ attr_merge_normalize_one( &rdi->rdi_e, ad_errMatchedDN, &val[ 0 ], NULL );
+ }
+
+ /* sleep time */
+ if ( rdi->rdi_sleeptime ) {
+ snprintf( buf, sizeof( buf ), "%d", rdi->rdi_sleeptime );
+ ber_str2bv( buf, 0, 0, &val[ 0 ] );
+
+ attr_merge_one( &rdi->rdi_e, ad_errSleepTime, &val[ 0 ], NULL );
+ }
+
+ /* operations */
+ if ( rdi->rdi_mask & SN_DG_OP_ADD ) {
+ BER_BVSTR( &val[ 0 ], "add" );
+ attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
+ }
+
+ if ( rdi->rdi_mask & SN_DG_OP_BIND ) {
+ BER_BVSTR( &val[ 0 ], "bind" );
+ attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
+ }
+
+ if ( rdi->rdi_mask & SN_DG_OP_COMPARE ) {
+ BER_BVSTR( &val[ 0 ], "compare" );
+ attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
+ }
+
+ if ( rdi->rdi_mask & SN_DG_OP_DELETE ) {
+ BER_BVSTR( &val[ 0 ], "delete" );
+ attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
+ }
+
+ if ( rdi->rdi_mask & SN_DG_EXTENDED ) {
+ BER_BVSTR( &val[ 0 ], "extended" );
+ attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
+ }
+
+ if ( rdi->rdi_mask & SN_DG_OP_MODIFY ) {
+ BER_BVSTR( &val[ 0 ], "modify" );
+ attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
+ }
+
+ if ( rdi->rdi_mask & SN_DG_OP_RENAME ) {
+ BER_BVSTR( &val[ 0 ], "rename" );
+ attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
+ }
+
+ if ( rdi->rdi_mask & SN_DG_OP_SEARCH ) {
+ BER_BVSTR( &val[ 0 ], "search" );
+ attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
+ }
+ }
+
+ return 0;
+}
+
+static int
+retcode_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ retcode_t *rd = (retcode_t *)on->on_bi.bi_private;
+
+ if ( rd ) {
+ retcode_item_t *rdi, *next;
+
+ for ( rdi = rd->rd_item; rdi != NULL; rdi = next ) {
+ next = rdi->rdi_next;
+ retcode_item_destroy( rdi );
+ }
+
+ if ( !BER_BVISNULL( &rd->rd_pdn ) ) {
+ ber_memfree( rd->rd_pdn.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &rd->rd_npdn ) ) {
+ ber_memfree( rd->rd_npdn.bv_val );
+ }
+
+ ber_memfree( rd );
+ }
+
+ return 0;
+}
+
+#if SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC
+static
+#endif /* SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC */
+int
+retcode_initialize( void )
+{
+ int i, code;
+
+ static struct {
+ char *desc;
+ AttributeDescription **ad;
+ } retcode_at[] = {
+ { "( 1.3.6.1.4.1.4203.666.11.4.1.1 "
+ "NAME ( 'errCode' ) "
+ "DESC 'LDAP error code' "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE )",
+ &ad_errCode },
+ { "( 1.3.6.1.4.1.4203.666.11.4.1.2 "
+ "NAME ( 'errOp' ) "
+ "DESC 'Operations the errObject applies to' "
+ "EQUALITY caseIgnoreMatch "
+ "SUBSTR caseIgnoreSubstringsMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
+ &ad_errOp},
+ { "( 1.3.6.1.4.1.4203.666.11.4.1.3 "
+ "NAME ( 'errText' ) "
+ "DESC 'LDAP error textual description' "
+ "EQUALITY caseIgnoreMatch "
+ "SUBSTR caseIgnoreSubstringsMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
+ "SINGLE-VALUE )",
+ &ad_errText },
+ { "( 1.3.6.1.4.1.4203.666.11.4.1.4 "
+ "NAME ( 'errSleepTime' ) "
+ "DESC 'Time to wait before returning the error' "
+ "EQUALITY integerMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE )",
+ &ad_errSleepTime },
+ { "( 1.3.6.1.4.1.4203.666.11.4.1.5 "
+ "NAME ( 'errMatchedDN' ) "
+ "DESC 'Value to be returned as matched DN' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
+ "SINGLE-VALUE )",
+ &ad_errMatchedDN },
+ { "( 1.3.6.1.4.1.4203.666.11.4.1.6 "
+ "NAME ( 'errUnsolicitedOID' ) "
+ "DESC 'OID to be returned within unsolicited response' "
+ "EQUALITY objectIdentifierMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
+ "SINGLE-VALUE )",
+ &ad_errUnsolicitedOID },
+ { "( 1.3.6.1.4.1.4203.666.11.4.1.7 "
+ "NAME ( 'errUnsolicitedData' ) "
+ "DESC 'Data to be returned within unsolicited response' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 "
+ "SINGLE-VALUE )",
+ &ad_errUnsolicitedData },
+ { "( 1.3.6.1.4.1.4203.666.11.4.1.8 "
+ "NAME ( 'errDisconnect' ) "
+ "DESC 'Disconnect without notice' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
+ "SINGLE-VALUE )",
+ &ad_errDisconnect },
+ { NULL }
+ };
+
+ static struct {
+ char *desc;
+ ObjectClass **oc;
+ } retcode_oc[] = {
+ { "( 1.3.6.1.4.1.4203.666.11.4.3.0 "
+ "NAME ( 'errAbsObject' ) "
+ "SUP top ABSTRACT "
+ "MUST ( errCode ) "
+ "MAY ( "
+ "cn "
+ "$ description "
+ "$ errOp "
+ "$ errText "
+ "$ errSleepTime "
+ "$ errMatchedDN "
+ "$ errUnsolicitedOID "
+ "$ errUnsolicitedData "
+ "$ errDisconnect "
+ ") )",
+ &oc_errAbsObject },
+ { "( 1.3.6.1.4.1.4203.666.11.4.3.1 "
+ "NAME ( 'errObject' ) "
+ "SUP errAbsObject STRUCTURAL "
+ ")",
+ &oc_errObject },
+ { "( 1.3.6.1.4.1.4203.666.11.4.3.2 "
+ "NAME ( 'errAuxObject' ) "
+ "SUP errAbsObject AUXILIARY "
+ ")",
+ &oc_errAuxObject },
+ { NULL }
+ };
+
+
+ for ( i = 0; retcode_at[ i ].desc != NULL; i++ ) {
+ code = register_at( retcode_at[ i ].desc, retcode_at[ i ].ad, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "retcode: register_at failed\n" );
+ return code;
+ }
+
+ (*retcode_at[ i ].ad)->ad_type->sat_flags |= SLAP_AT_HIDE;
+ }
+
+ for ( i = 0; retcode_oc[ i ].desc != NULL; i++ ) {
+ code = register_oc( retcode_oc[ i ].desc, retcode_oc[ i ].oc, 0 );
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY,
+ "retcode: register_oc failed\n" );
+ return code;
+ }
+
+ (*retcode_oc[ i ].oc)->soc_flags |= SLAP_OC_HIDE;
+ }
+
+ retcode.on_bi.bi_type = "retcode";
+
+ retcode.on_bi.bi_db_init = retcode_db_init;
+ retcode.on_bi.bi_db_open = retcode_db_open;
+ retcode.on_bi.bi_db_destroy = retcode_db_destroy;
+
+ retcode.on_bi.bi_op_add = retcode_op_func;
+ retcode.on_bi.bi_op_bind = retcode_op_func;
+ retcode.on_bi.bi_op_compare = retcode_op_func;
+ retcode.on_bi.bi_op_delete = retcode_op_func;
+ retcode.on_bi.bi_op_modify = retcode_op_func;
+ retcode.on_bi.bi_op_modrdn = retcode_op_func;
+ retcode.on_bi.bi_op_search = retcode_op_func;
+
+ retcode.on_bi.bi_extended = retcode_op_func;
+
+ retcode.on_response = retcode_response;
+
+ retcode.on_bi.bi_cf_ocs = rcocs;
+
+ code = config_register_schema( rccfg, rcocs );
+ if ( code ) {
+ return code;
+ }
+
+ return overlay_register( &retcode );
+}
+
+#if SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return retcode_initialize();
+}
+#endif /* SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC */
+
+#endif /* SLAPD_OVER_RETCODE */
diff --git a/servers/slapd/overlays/rwm.c b/servers/slapd/overlays/rwm.c
new file mode 100644
index 0000000..8023ba5
--- /dev/null
+++ b/servers/slapd/overlays/rwm.c
@@ -0,0 +1,2723 @@
+/* rwm.c - rewrite/remap operations */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_RWM
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+#include "rwm.h"
+
+typedef struct rwm_op_state {
+ ber_tag_t r_tag;
+ struct berval ro_dn;
+ struct berval ro_ndn;
+ struct berval r_dn;
+ struct berval r_ndn;
+ struct berval rx_dn;
+ struct berval rx_ndn;
+ AttributeName *mapped_attrs;
+ OpRequest o_request;
+} rwm_op_state;
+
+typedef struct rwm_op_cb {
+ slap_callback cb;
+ rwm_op_state ros;
+} rwm_op_cb;
+
+static int
+rwm_db_destroy( BackendDB *be, ConfigReply *cr );
+
+static int
+rwm_send_entry( Operation *op, SlapReply *rs );
+
+static void
+rwm_op_rollback( Operation *op, SlapReply *rs, rwm_op_state *ros )
+{
+ /* in case of successful extended operation cleanup
+ * gets called *after* (ITS#6632); this hack counts
+ * on others to cleanup our o_req_dn/o_req_ndn,
+ * while we cleanup theirs. */
+ if ( ros->r_tag == LDAP_REQ_EXTENDED && rs->sr_err == LDAP_SUCCESS ) {
+ if ( !BER_BVISNULL( &ros->rx_dn ) ) {
+ ch_free( ros->rx_dn.bv_val );
+ }
+ if ( !BER_BVISNULL( &ros->rx_ndn ) ) {
+ ch_free( ros->rx_ndn.bv_val );
+ }
+
+ } else {
+ if ( !BER_BVISNULL( &ros->ro_dn ) ) {
+ op->o_req_dn = ros->ro_dn;
+ }
+ if ( !BER_BVISNULL( &ros->ro_ndn ) ) {
+ op->o_req_ndn = ros->ro_ndn;
+ }
+
+ if ( !BER_BVISNULL( &ros->r_dn )
+ && ros->r_dn.bv_val != ros->ro_dn.bv_val )
+ {
+ assert( ros->r_dn.bv_val != ros->r_ndn.bv_val );
+ ch_free( ros->r_dn.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &ros->r_ndn )
+ && ros->r_ndn.bv_val != ros->ro_ndn.bv_val )
+ {
+ ch_free( ros->r_ndn.bv_val );
+ }
+ }
+
+ BER_BVZERO( &ros->r_dn );
+ BER_BVZERO( &ros->r_ndn );
+ BER_BVZERO( &ros->ro_dn );
+ BER_BVZERO( &ros->ro_ndn );
+ BER_BVZERO( &ros->rx_dn );
+ BER_BVZERO( &ros->rx_ndn );
+
+ switch( ros->r_tag ) {
+ case LDAP_REQ_COMPARE:
+ if ( op->orc_ava->aa_value.bv_val != ros->orc_ava->aa_value.bv_val )
+ op->o_tmpfree( op->orc_ava->aa_value.bv_val, op->o_tmpmemctx );
+ op->orc_ava = ros->orc_ava;
+ break;
+ case LDAP_REQ_MODIFY:
+ slap_mods_free( op->orm_modlist, 1 );
+ op->orm_modlist = ros->orm_modlist;
+ break;
+ case LDAP_REQ_MODRDN:
+ if ( op->orr_newSup != ros->orr_newSup ) {
+ if ( op->orr_newSup ) {
+ ch_free( op->orr_newSup->bv_val );
+ ch_free( op->orr_nnewSup->bv_val );
+ op->o_tmpfree( op->orr_newSup, op->o_tmpmemctx );
+ op->o_tmpfree( op->orr_nnewSup, op->o_tmpmemctx );
+ }
+ op->orr_newSup = ros->orr_newSup;
+ op->orr_nnewSup = ros->orr_nnewSup;
+ }
+ if ( op->orr_newrdn.bv_val != ros->orr_newrdn.bv_val ) {
+ ch_free( op->orr_newrdn.bv_val );
+ ch_free( op->orr_nnewrdn.bv_val );
+ op->orr_newrdn = ros->orr_newrdn;
+ op->orr_nnewrdn = ros->orr_nnewrdn;
+ }
+ break;
+ case LDAP_REQ_SEARCH:
+ op->o_tmpfree( ros->mapped_attrs, op->o_tmpmemctx );
+ op->ors_attrs = ros->ors_attrs;
+ if ( op->ors_filter != ros->ors_filter ) {
+ filter_free_x( op, op->ors_filter, 1 );
+ op->ors_filter = ros->ors_filter;
+ }
+ if ( op->ors_filterstr.bv_val != ros->ors_filterstr.bv_val ) {
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ op->ors_filterstr = ros->ors_filterstr;
+ }
+ break;
+ case LDAP_REQ_EXTENDED:
+ if ( op->ore_reqdata != ros->ore_reqdata ) {
+ ber_bvfree( op->ore_reqdata );
+ op->ore_reqdata = ros->ore_reqdata;
+ }
+ break;
+ case LDAP_REQ_BIND:
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+#if 0
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+ /* too late, c_mutex released */
+ Debug( LDAP_DEBUG_ANY, "*** DN: \"%s\" => \"%s\"\n",
+ op->o_conn->c_ndn.bv_val,
+ op->o_req_ndn.bv_val );
+ ber_bvreplace( &op->o_conn->c_ndn,
+ &op->o_req_ndn );
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+#endif
+ }
+ break;
+ default: break;
+ }
+}
+
+static int
+rwm_op_cleanup( Operation *op, SlapReply *rs )
+{
+ slap_callback *cb = op->o_callback;
+ rwm_op_state *ros = cb->sc_private;
+
+ if ( rs->sr_type == REP_RESULT || rs->sr_type == REP_EXTENDED ||
+ op->o_abandon || rs->sr_err == SLAPD_ABANDON )
+ {
+ rwm_op_rollback( op, rs, ros );
+
+ op->o_callback = op->o_callback->sc_next;
+ op->o_tmpfree( cb, op->o_tmpmemctx );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static rwm_op_cb *
+rwm_callback_get( Operation *op )
+{
+ rwm_op_cb *roc;
+
+ roc = op->o_tmpcalloc( 1, sizeof( struct rwm_op_cb ), op->o_tmpmemctx );
+ roc->cb.sc_cleanup = rwm_op_cleanup;
+ roc->cb.sc_response = NULL;
+ roc->cb.sc_next = op->o_callback;
+ roc->cb.sc_private = &roc->ros;
+ roc->ros.r_tag = op->o_tag;
+ roc->ros.ro_dn = op->o_req_dn;
+ roc->ros.ro_ndn = op->o_req_ndn;
+ BER_BVZERO( &roc->ros.r_dn );
+ BER_BVZERO( &roc->ros.r_ndn );
+ BER_BVZERO( &roc->ros.rx_dn );
+ BER_BVZERO( &roc->ros.rx_ndn );
+ roc->ros.mapped_attrs = NULL;
+ roc->ros.o_request = op->o_request;
+
+ return roc;
+}
+
+
+static int
+rwm_op_dn_massage( Operation *op, SlapReply *rs, void *cookie,
+ rwm_op_state *ros )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ struct berval dn = BER_BVNULL,
+ ndn = BER_BVNULL;
+ int rc = 0;
+ dncookie dc;
+
+ /*
+ * Rewrite the dn if needed
+ */
+ dc.rwmap = rwmap;
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+ dc.ctx = (char *)cookie;
+
+ /* NOTE: in those cases where only the ndn is available,
+ * and the caller sets op->o_req_dn = op->o_req_ndn,
+ * only rewrite the op->o_req_ndn and use it as
+ * op->o_req_dn as well */
+ ndn = op->o_req_ndn;
+ if ( op->o_req_dn.bv_val != op->o_req_ndn.bv_val ) {
+ dn = op->o_req_dn;
+ rc = rwm_dn_massage_pretty_normalize( &dc, &op->o_req_dn, &dn, &ndn );
+ } else {
+ rc = rwm_dn_massage_normalize( &dc, &op->o_req_ndn, &ndn );
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( ( op->o_req_dn.bv_val != op->o_req_ndn.bv_val && dn.bv_val == op->o_req_dn.bv_val )
+ || ndn.bv_val == op->o_req_ndn.bv_val )
+ {
+ return LDAP_SUCCESS;
+ }
+
+ if ( op->o_req_dn.bv_val != op->o_req_ndn.bv_val ) {
+ op->o_req_dn = dn;
+ assert( BER_BVISNULL( &ros->r_dn ) );
+ ros->r_dn = dn;
+ } else {
+ op->o_req_dn = ndn;
+ }
+ op->o_req_ndn = ndn;
+ assert( BER_BVISNULL( &ros->r_ndn ) );
+ ros->r_ndn = ndn;
+
+ if ( ros->r_tag == LDAP_REQ_EXTENDED ) {
+ ros->rx_dn = ros->r_dn;
+ ros->rx_ndn = ros->r_ndn;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+rwm_op_add( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ int rc,
+ i;
+ Attribute **ap = NULL;
+ char *olddn = op->o_req_dn.bv_val;
+ int isupdate;
+
+ rwm_op_cb *roc = rwm_callback_get( op );
+
+ rc = rwm_op_dn_massage( op, rs, "addDN", &roc->ros );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "addDN massage error" );
+ return -1;
+ }
+
+ if ( olddn != op->o_req_dn.bv_val ) {
+ ber_bvreplace( &op->ora_e->e_name, &op->o_req_dn );
+ ber_bvreplace( &op->ora_e->e_nname, &op->o_req_ndn );
+ }
+
+ /* Count number of attributes in entry */
+ isupdate = be_shadow_update( op );
+ for ( i = 0, ap = &op->oq_add.rs_e->e_attrs; *ap; ) {
+ Attribute *a;
+
+ if ( (*ap)->a_desc == slap_schema.si_ad_objectClass ||
+ (*ap)->a_desc == slap_schema.si_ad_structuralObjectClass )
+ {
+ int j, last;
+
+ last = (*ap)->a_numvals - 1;
+ for ( j = 0; !BER_BVISNULL( &(*ap)->a_vals[ j ] ); j++ ) {
+ struct ldapmapping *mapping = NULL;
+
+ ( void )rwm_mapping( &rwmap->rwm_oc, &(*ap)->a_vals[ j ],
+ &mapping, RWM_MAP );
+ if ( mapping == NULL ) {
+ if ( rwmap->rwm_at.drop_missing ) {
+ /* FIXME: we allow to remove objectClasses as well;
+ * if the resulting entry is inconsistent, that's
+ * the relayed database's business...
+ */
+ ch_free( (*ap)->a_vals[ j ].bv_val );
+ if ( last > j ) {
+ (*ap)->a_vals[ j ] = (*ap)->a_vals[ last ];
+ }
+ BER_BVZERO( &(*ap)->a_vals[ last ] );
+ (*ap)->a_numvals--;
+ last--;
+ j--;
+ }
+
+ } else {
+ ch_free( (*ap)->a_vals[ j ].bv_val );
+ ber_dupbv( &(*ap)->a_vals[ j ], &mapping->m_dst );
+ }
+ }
+
+ } else if ( !isupdate && !get_relax( op ) && (*ap)->a_desc->ad_type->sat_no_user_mod )
+ {
+ goto next_attr;
+
+ } else {
+ struct ldapmapping *mapping = NULL;
+
+ ( void )rwm_mapping( &rwmap->rwm_at, &(*ap)->a_desc->ad_cname,
+ &mapping, RWM_MAP );
+ if ( mapping == NULL ) {
+ if ( rwmap->rwm_at.drop_missing ) {
+ goto cleanup_attr;
+ }
+ }
+
+ if ( (*ap)->a_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName
+ || ( mapping != NULL && mapping->m_dst_ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) )
+ {
+ /*
+ * FIXME: rewrite could fail; in this case
+ * the operation should give up, right?
+ */
+ rc = rwm_dnattr_rewrite( op, rs, "addAttrDN",
+ (*ap)->a_vals,
+ (*ap)->a_nvals ? &(*ap)->a_nvals : NULL );
+ if ( rc ) {
+ goto cleanup_attr;
+ }
+
+ } else if ( (*ap)->a_desc == slap_schema.si_ad_ref ) {
+ rc = rwm_referral_rewrite( op, rs, "referralAttrDN",
+ (*ap)->a_vals,
+ (*ap)->a_nvals ? &(*ap)->a_nvals : NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ goto cleanup_attr;
+ }
+ }
+
+ if ( mapping != NULL ) {
+ assert( mapping->m_dst_ad != NULL );
+ (*ap)->a_desc = mapping->m_dst_ad;
+ }
+ }
+
+next_attr:;
+ ap = &(*ap)->a_next;
+ continue;
+
+cleanup_attr:;
+ /* FIXME: leaking attribute/values? */
+ a = *ap;
+
+ *ap = (*ap)->a_next;
+ attr_free( a );
+ }
+
+ op->o_callback = &roc->cb;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rwm_conn_init( BackendDB *be, Connection *conn )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ ( void )rewrite_session_init( rwmap->rwm_rw, conn );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rwm_conn_destroy( BackendDB *be, Connection *conn )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ ( void )rewrite_session_delete( rwmap->rwm_rw, conn );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rwm_op_bind( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ int rc;
+
+ rwm_op_cb *roc = rwm_callback_get( op );
+
+ rc = rwm_op_dn_massage( op, rs, "bindDN", &roc->ros );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "bindDN massage error" );
+ return -1;
+ }
+
+ overlay_callback_after_backover( op, &roc->cb, 1 );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rwm_op_unbind( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ rewrite_session_delete( rwmap->rwm_rw, op->o_conn );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rwm_op_compare( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ int rc;
+ struct berval mapped_vals[2] = { BER_BVNULL, BER_BVNULL };
+
+ rwm_op_cb *roc = rwm_callback_get( op );
+
+ rc = rwm_op_dn_massage( op, rs, "compareDN", &roc->ros );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "compareDN massage error" );
+ return -1;
+ }
+
+ /* if the attribute is an objectClass, try to remap its value */
+ if ( op->orc_ava->aa_desc == slap_schema.si_ad_objectClass
+ || op->orc_ava->aa_desc == slap_schema.si_ad_structuralObjectClass )
+ {
+ rwm_map( &rwmap->rwm_oc, &op->orc_ava->aa_value,
+ &mapped_vals[0], RWM_MAP );
+ if ( BER_BVISNULL( &mapped_vals[0] ) || BER_BVISEMPTY( &mapped_vals[0] ) )
+ {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, LDAP_OTHER, "compare objectClass map error" );
+ return -1;
+
+ } else if ( mapped_vals[0].bv_val != op->orc_ava->aa_value.bv_val ) {
+ ber_dupbv_x( &op->orc_ava->aa_value, &mapped_vals[0],
+ op->o_tmpmemctx );
+ }
+
+ } else {
+ struct ldapmapping *mapping = NULL;
+ AttributeDescription *ad = op->orc_ava->aa_desc;
+
+ ( void )rwm_mapping( &rwmap->rwm_at, &op->orc_ava->aa_desc->ad_cname,
+ &mapping, RWM_MAP );
+ if ( mapping == NULL ) {
+ if ( rwmap->rwm_at.drop_missing ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, LDAP_OTHER, "compare attributeType map error" );
+ return -1;
+ }
+
+ } else {
+ assert( mapping->m_dst_ad != NULL );
+ ad = mapping->m_dst_ad;
+ }
+
+ if ( op->orc_ava->aa_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName
+ || ( mapping != NULL && mapping->m_dst_ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) )
+ {
+ struct berval *mapped_valsp[2];
+
+ mapped_valsp[0] = &mapped_vals[0];
+ mapped_valsp[1] = &mapped_vals[1];
+
+ mapped_vals[0] = op->orc_ava->aa_value;
+
+ rc = rwm_dnattr_rewrite( op, rs, "compareAttrDN", NULL, mapped_valsp );
+
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "compareAttrDN massage error" );
+ return -1;
+ }
+
+ if ( mapped_vals[ 0 ].bv_val != op->orc_ava->aa_value.bv_val ) {
+ /* NOTE: if we get here, rwm_dnattr_rewrite()
+ * already freed the old value, so now
+ * it's invalid */
+ ber_dupbv_x( &op->orc_ava->aa_value, &mapped_vals[0],
+ op->o_tmpmemctx );
+ ber_memfree_x( mapped_vals[ 0 ].bv_val, NULL );
+ }
+ }
+ op->orc_ava->aa_desc = ad;
+ }
+
+ op->o_callback = &roc->cb;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rwm_op_delete( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ int rc;
+
+ rwm_op_cb *roc = rwm_callback_get( op );
+
+ rc = rwm_op_dn_massage( op, rs, "deleteDN", &roc->ros );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "deleteDN massage error" );
+ return -1;
+ }
+
+ op->o_callback = &roc->cb;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rwm_op_modify( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ int isupdate;
+ Modifications **mlp;
+ int rc;
+
+ rwm_op_cb *roc = rwm_callback_get( op );
+
+ rc = rwm_op_dn_massage( op, rs, "modifyDN", &roc->ros );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "modifyDN massage error" );
+ return -1;
+ }
+
+ isupdate = be_shadow_update( op );
+ for ( mlp = &op->orm_modlist; *mlp; ) {
+ int is_oc = 0;
+ Modifications *ml = *mlp;
+ struct ldapmapping *mapping = NULL;
+
+ /* ml points to a temporary mod until needs duplication */
+ if ( ml->sml_desc == slap_schema.si_ad_objectClass
+ || ml->sml_desc == slap_schema.si_ad_structuralObjectClass )
+ {
+ is_oc = 1;
+
+ } else if ( !isupdate && !get_relax( op ) && ml->sml_desc->ad_type->sat_no_user_mod )
+ {
+ ml = ch_malloc( sizeof( Modifications ) );
+ *ml = **mlp;
+ if ( (*mlp)->sml_values ) {
+ ber_bvarray_dup_x( &ml->sml_values, (*mlp)->sml_values, NULL );
+ if ( (*mlp)->sml_nvalues ) {
+ ber_bvarray_dup_x( &ml->sml_nvalues, (*mlp)->sml_nvalues, NULL );
+ }
+ }
+ *mlp = ml;
+ goto next_mod;
+
+ } else {
+ int drop_missing;
+
+ drop_missing = rwm_mapping( &rwmap->rwm_at,
+ &ml->sml_desc->ad_cname,
+ &mapping, RWM_MAP );
+ if ( drop_missing || ( mapping != NULL && BER_BVISNULL( &mapping->m_dst ) ) )
+ {
+ goto skip_mod;
+ }
+ }
+
+ /* duplicate the modlist */
+ ml = ch_malloc( sizeof( Modifications ));
+ *ml = **mlp;
+ *mlp = ml;
+
+ if ( ml->sml_values != NULL ) {
+ int i, num;
+ struct berval *bva;
+
+ for ( num = 0; !BER_BVISNULL( &ml->sml_values[ num ] ); num++ )
+ /* count values */ ;
+
+ bva = ch_malloc( (num+1) * sizeof( struct berval ));
+ for (i=0; i<num; i++)
+ ber_dupbv( &bva[i], &ml->sml_values[i] );
+ BER_BVZERO( &bva[i] );
+ ml->sml_values = bva;
+
+ if ( ml->sml_nvalues ) {
+ bva = ch_malloc( (num+1) * sizeof( struct berval ));
+ for (i=0; i<num; i++)
+ ber_dupbv( &bva[i], &ml->sml_nvalues[i] );
+ BER_BVZERO( &bva[i] );
+ ml->sml_nvalues = bva;
+ }
+
+ if ( is_oc ) {
+ int last, j;
+
+ last = num-1;
+
+ for ( j = 0; !BER_BVISNULL( &ml->sml_values[ j ] ); j++ ) {
+ struct ldapmapping *oc_mapping = NULL;
+
+ ( void )rwm_mapping( &rwmap->rwm_oc, &ml->sml_values[ j ],
+ &oc_mapping, RWM_MAP );
+ if ( oc_mapping == NULL ) {
+ if ( rwmap->rwm_at.drop_missing ) {
+ /* FIXME: we allow to remove objectClasses as well;
+ * if the resulting entry is inconsistent, that's
+ * the relayed database's business...
+ */
+ if ( last > j ) {
+ ch_free( ml->sml_values[ j ].bv_val );
+ ml->sml_values[ j ] = ml->sml_values[ last ];
+ }
+ BER_BVZERO( &ml->sml_values[ last ] );
+ last--;
+ j--;
+ }
+
+ } else {
+ ch_free( ml->sml_values[ j ].bv_val );
+ ber_dupbv( &ml->sml_values[ j ], &oc_mapping->m_dst );
+ }
+ }
+
+ } else {
+ if ( ml->sml_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName
+ || ( mapping != NULL && mapping->m_dst_ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) )
+ {
+ rc = rwm_dnattr_rewrite( op, rs, "modifyAttrDN",
+ ml->sml_values,
+ ml->sml_nvalues ? &ml->sml_nvalues : NULL );
+
+ } else if ( ml->sml_desc == slap_schema.si_ad_ref ) {
+ rc = rwm_referral_rewrite( op, rs,
+ "referralAttrDN",
+ ml->sml_values,
+ ml->sml_nvalues ? &ml->sml_nvalues : NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ goto cleanup_mod;
+ }
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ goto cleanup_mod;
+ }
+ }
+ }
+
+next_mod:;
+ if ( mapping != NULL ) {
+ /* use new attribute description */
+ assert( mapping->m_dst_ad != NULL );
+ ml->sml_desc = mapping->m_dst_ad;
+ }
+
+ mlp = &ml->sml_next;
+ continue;
+
+skip_mod:;
+ *mlp = (*mlp)->sml_next;
+ continue;
+
+cleanup_mod:;
+ ml = *mlp;
+ *mlp = (*mlp)->sml_next;
+ slap_mod_free( &ml->sml_mod, 0 );
+ free( ml );
+ }
+
+ op->o_callback = &roc->cb;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rwm_op_modrdn( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ int rc;
+ dncookie dc;
+
+ rwm_op_cb *roc = rwm_callback_get( op );
+
+ if ( op->orr_newSup ) {
+ struct berval nnewSup = BER_BVNULL;
+ struct berval newSup = BER_BVNULL;
+
+ /*
+ * Rewrite the new superior, if defined and required
+ */
+ dc.rwmap = rwmap;
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+ dc.ctx = "newSuperiorDN";
+ newSup = *op->orr_newSup;
+ nnewSup = *op->orr_nnewSup;
+ rc = rwm_dn_massage_pretty_normalize( &dc, op->orr_newSup, &newSup, &nnewSup );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "newSuperiorDN massage error" );
+ return -1;
+ }
+
+ if ( op->orr_newSup->bv_val != newSup.bv_val ) {
+ op->orr_newSup = op->o_tmpalloc( sizeof( struct berval ),
+ op->o_tmpmemctx );
+ op->orr_nnewSup = op->o_tmpalloc( sizeof( struct berval ),
+ op->o_tmpmemctx );
+ *op->orr_newSup = newSup;
+ *op->orr_nnewSup = nnewSup;
+ }
+ }
+
+ /*
+ * Rewrite the newRDN, if needed
+ */
+ {
+ struct berval newrdn = BER_BVNULL;
+ struct berval nnewrdn = BER_BVNULL;
+
+ dc.rwmap = rwmap;
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+ dc.ctx = "newRDN";
+ newrdn = op->orr_newrdn;
+ nnewrdn = op->orr_nnewrdn;
+ rc = rwm_dn_massage_pretty_normalize( &dc, &op->orr_newrdn, &newrdn, &nnewrdn );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "newRDN massage error" );
+ goto err;
+ }
+
+ if ( op->orr_newrdn.bv_val != newrdn.bv_val ) {
+ op->orr_newrdn = newrdn;
+ op->orr_nnewrdn = nnewrdn;
+ }
+ }
+
+ /*
+ * Rewrite the dn, if needed
+ */
+ rc = rwm_op_dn_massage( op, rs, "renameDN", &roc->ros );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "renameDN massage error" );
+ goto err;
+ }
+
+ op->o_callback = &roc->cb;
+
+ rc = SLAP_CB_CONTINUE;
+
+ if ( 0 ) {
+err:;
+ if ( op->orr_newSup != roc->ros.orr_newSup ) {
+ ch_free( op->orr_newSup->bv_val );
+ ch_free( op->orr_nnewSup->bv_val );
+ op->o_tmpfree( op->orr_newSup, op->o_tmpmemctx );
+ op->o_tmpfree( op->orr_nnewSup, op->o_tmpmemctx );
+ op->orr_newSup = roc->ros.orr_newSup;
+ op->orr_nnewSup = roc->ros.orr_nnewSup;
+ }
+
+ if ( op->orr_newrdn.bv_val != roc->ros.orr_newrdn.bv_val ) {
+ ch_free( op->orr_newrdn.bv_val );
+ ch_free( op->orr_nnewrdn.bv_val );
+ op->orr_newrdn = roc->ros.orr_newrdn;
+ op->orr_nnewrdn = roc->ros.orr_nnewrdn;
+ }
+ }
+
+ return rc;
+}
+
+
+static int
+rwm_swap_attrs( Operation *op, SlapReply *rs )
+{
+ slap_callback *cb = op->o_callback;
+ rwm_op_state *ros = cb->sc_private;
+
+ rs->sr_attrs = ros->ors_attrs;
+
+ /* other overlays might have touched op->ors_attrs,
+ * so we restore the original version here, otherwise
+ * attribute-mapping might fail */
+ op->ors_attrs = ros->mapped_attrs;
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+ * NOTE: this implementation of get/release entry is probably far from
+ * optimal. The rationale consists in intercepting the request directed
+ * to the underlying database, in order to rewrite/remap the request,
+ * perform it using the modified data, duplicate the resulting entry
+ * and finally free it when release is called.
+ * This implies that subsequent overlays are not called, as the request
+ * is directly shunted to the underlying database.
+ */
+static int
+rwm_entry_release_rw( Operation *op, Entry *e, int rw )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+
+ /* can't be ours */
+ if ( ((BackendInfo *)on->on_info->oi_orig)->bi_entry_get_rw == NULL ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* just free entry if (probably) ours */
+ if ( e->e_private == NULL && BER_BVISNULL( &e->e_bv ) ) {
+ entry_free( e );
+ return LDAP_SUCCESS;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rwm_entry_get_rw( Operation *op, struct berval *ndn,
+ ObjectClass *oc, AttributeDescription *at, int rw, Entry **ep )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ int rc;
+ BackendDB db;
+ Operation op2;
+ SlapReply rs = { REP_SEARCH };
+
+ rwm_op_state ros = { 0 };
+ struct berval mndn = BER_BVNULL;
+
+ if ( ((BackendInfo *)on->on_info->oi_orig)->bi_entry_get_rw == NULL ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* massage DN */
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2 = *op;
+ op2.o_req_dn = *ndn;
+ op2.o_req_ndn = *ndn;
+ rc = rwm_op_dn_massage( &op2, &rs, "searchDN", &ros );
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_OTHER;
+ }
+
+ mndn = BER_BVISNULL( &ros.r_ndn ) ? *ndn : ros.r_ndn;
+
+ /* map attribute & objectClass */
+ if ( at != NULL ) {
+ }
+
+ if ( oc != NULL ) {
+ }
+
+ /* fetch entry */
+ db = *op->o_bd;
+ op2.o_bd = &db;
+ op2.o_bd->bd_info = (BackendInfo *)on->on_info->oi_orig;
+ op2.ors_attrs = slap_anlist_all_attributes;
+ rc = op2.o_bd->bd_info->bi_entry_get_rw( &op2, &mndn, oc, at, rw, ep );
+ if ( rc == LDAP_SUCCESS && *ep != NULL ) {
+ /* we assume be_entry_release() needs to be called */
+ rs.sr_flags = REP_ENTRY_MUSTRELEASE;
+ rs.sr_entry = *ep;
+
+ /* duplicate & release */
+ op2.o_bd->bd_info = (BackendInfo *)on;
+ rc = rwm_send_entry( &op2, &rs );
+ RS_ASSERT( rs.sr_flags & REP_ENTRY_MUSTFLUSH );
+ if ( rc == SLAP_CB_CONTINUE ) {
+ *ep = rs.sr_entry;
+ rc = LDAP_SUCCESS;
+ } else {
+ assert( rc != LDAP_SUCCESS && rs.sr_entry == *ep );
+ *ep = NULL;
+ op2.o_bd->bd_info = (BackendInfo *)on->on_info;
+ be_entry_release_r( &op2, rs.sr_entry );
+ op2.o_bd->bd_info = (BackendInfo *)on;
+ }
+ }
+
+ if ( !BER_BVISNULL( &ros.r_ndn) && ros.r_ndn.bv_val != ndn->bv_val ) {
+ op->o_tmpfree( ros.r_ndn.bv_val, op->o_tmpmemctx );
+ }
+
+ return rc;
+}
+
+static int
+rwm_op_search( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ int rc;
+ dncookie dc;
+
+ struct berval fstr = BER_BVNULL;
+ Filter *f = NULL;
+
+ AttributeName *an = NULL;
+
+ char *text = NULL;
+
+ rwm_op_cb *roc = rwm_callback_get( op );
+
+ rc = rewrite_session_var_set( rwmap->rwm_rw, op->o_conn,
+ "searchFilter", op->ors_filterstr.bv_val );
+ if ( rc == LDAP_SUCCESS )
+ rc = rwm_op_dn_massage( op, rs, "searchDN", &roc->ros );
+ if ( rc != LDAP_SUCCESS ) {
+ text = "searchDN massage error";
+ goto error_return;
+ }
+
+ /*
+ * Rewrite the dn if needed
+ */
+ dc.rwmap = rwmap;
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+ dc.ctx = "searchFilterAttrDN";
+
+ rc = rwm_filter_map_rewrite( op, &dc, op->ors_filter, &fstr );
+ if ( rc != LDAP_SUCCESS ) {
+ text = "searchFilter/searchFilterAttrDN massage error";
+ goto error_return;
+ }
+
+ f = str2filter_x( op, fstr.bv_val );
+
+ if ( f == NULL ) {
+ text = "massaged filter parse error";
+ goto error_return;
+ }
+
+ op->ors_filter = f;
+ op->ors_filterstr = fstr;
+
+ rc = rwm_map_attrnames( op, &rwmap->rwm_at, &rwmap->rwm_oc,
+ op->ors_attrs, &an, RWM_MAP );
+ if ( rc != LDAP_SUCCESS ) {
+ text = "attribute list mapping error";
+ goto error_return;
+ }
+
+ op->ors_attrs = an;
+ /* store the mapped Attributes for later usage, in
+ * the case that other overlays change op->ors_attrs */
+ roc->ros.mapped_attrs = an;
+ roc->cb.sc_response = rwm_swap_attrs;
+
+ op->o_callback = &roc->cb;
+
+ return SLAP_CB_CONTINUE;
+
+error_return:;
+ if ( an != NULL ) {
+ ch_free( an );
+ }
+
+ if ( f != NULL ) {
+ filter_free_x( op, f, 1 );
+ }
+
+ if ( !BER_BVISNULL( &fstr ) ) {
+ op->o_tmpfree( fstr.bv_val, op->o_tmpmemctx );
+ }
+
+ rwm_op_rollback( op, rs, &roc->ros );
+ op->oq_search = roc->ros.oq_search;
+ op->o_tmpfree( roc, op->o_tmpmemctx );
+
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, text );
+
+ return -1;
+
+}
+
+static int
+rwm_exop_passwd( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ int rc;
+ rwm_op_cb *roc;
+
+ struct berval id = BER_BVNULL,
+ pwold = BER_BVNULL,
+ pwnew = BER_BVNULL;
+ BerElement *ber = NULL;
+
+ if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
+ return LDAP_SUCCESS;
+ }
+
+ if ( !SLAP_ISGLOBALOVERLAY( op->o_bd ) ) {
+ rs->sr_err = LDAP_OTHER;
+ return rs->sr_err;
+ }
+
+ rs->sr_err = slap_passwd_parse( op->ore_reqdata, &id,
+ &pwold, &pwnew, &rs->sr_text );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ return rs->sr_err;
+ }
+
+ if ( !BER_BVISNULL( &id ) ) {
+ char idNul = id.bv_val[id.bv_len];
+ id.bv_val[id.bv_len] = '\0';
+ rs->sr_err = dnPrettyNormal( NULL, &id, &op->o_req_dn,
+ &op->o_req_ndn, op->o_tmpmemctx );
+ id.bv_val[id.bv_len] = idNul;
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ rs->sr_text = "Invalid DN";
+ return rs->sr_err;
+ }
+
+ } else {
+ ber_dupbv_x( &op->o_req_dn, &op->o_dn, op->o_tmpmemctx );
+ ber_dupbv_x( &op->o_req_ndn, &op->o_ndn, op->o_tmpmemctx );
+ }
+
+ roc = rwm_callback_get( op );
+
+ rc = rwm_op_dn_massage( op, rs, "extendedDN", &roc->ros );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "extendedDN massage error" );
+ return -1;
+ }
+
+ ber = ber_alloc_t( LBER_USE_DER );
+ if ( !ber ) {
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "No memory";
+ return rs->sr_err;
+ }
+ ber_printf( ber, "{" );
+ if ( !BER_BVISNULL( &id )) {
+ ber_printf( ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_ID,
+ &op->o_req_dn );
+ }
+ if ( !BER_BVISNULL( &pwold )) {
+ ber_printf( ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_OLD, &pwold );
+ }
+ if ( !BER_BVISNULL( &pwnew )) {
+ ber_printf( ber, "tO", LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, &pwnew );
+ }
+ ber_printf( ber, "N}" );
+ ber_flatten( ber, &op->ore_reqdata );
+ ber_free( ber, 1 );
+
+ op->o_callback = &roc->cb;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static struct exop {
+ struct berval oid;
+ BI_op_extended *extended;
+} exop_table[] = {
+ { BER_BVC(LDAP_EXOP_MODIFY_PASSWD), rwm_exop_passwd },
+ { BER_BVNULL, NULL }
+};
+
+static int
+rwm_extended( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ int rc;
+ rwm_op_cb *roc;
+
+ int i;
+
+ for ( i = 0; exop_table[i].extended != NULL; i++ ) {
+ if ( bvmatch( &exop_table[i].oid, &op->oq_extended.rs_reqoid ) )
+ {
+ rc = exop_table[i].extended( op, rs );
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ break;
+
+ case SLAP_CB_CONTINUE:
+ case SLAPD_ABANDON:
+ return rc;
+
+ default:
+ send_ldap_result( op, rs );
+ return rc;
+ }
+ break;
+ }
+ }
+
+ roc = rwm_callback_get( op );
+
+ rc = rwm_op_dn_massage( op, rs, "extendedDN", &roc->ros );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "extendedDN massage error" );
+ return -1;
+ }
+
+ /* TODO: rewrite/map extended data ? ... */
+ op->o_callback = &roc->cb;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static void
+rwm_matched( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ struct berval dn, mdn;
+ dncookie dc;
+ int rc;
+
+ if ( rs->sr_matched == NULL ) {
+ return;
+ }
+
+ dc.rwmap = rwmap;
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+ dc.ctx = "matchedDN";
+ ber_str2bv( rs->sr_matched, 0, 0, &dn );
+ mdn = dn;
+ rc = rwm_dn_massage_pretty( &dc, &dn, &mdn );
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_err = rc;
+ rs->sr_text = "Rewrite error";
+
+ } else if ( mdn.bv_val != dn.bv_val ) {
+ if ( rs->sr_flags & REP_MATCHED_MUSTBEFREED ) {
+ ch_free( (void *)rs->sr_matched );
+
+ } else {
+ rs->sr_flags |= REP_MATCHED_MUSTBEFREED;
+ }
+ rs->sr_matched = mdn.bv_val;
+ }
+}
+
+static int
+rwm_attrs( Operation *op, SlapReply *rs, Attribute** a_first, int stripEntryDN )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ dncookie dc;
+ int rc;
+ Attribute **ap;
+ int isupdate;
+ int check_duplicate_attrs = 0;
+
+ /*
+ * Rewrite the dn attrs, if needed
+ */
+ dc.rwmap = rwmap;
+ dc.conn = op->o_conn;
+ dc.rs = NULL;
+
+ /* FIXME: the entries are in the remote mapping form;
+ * so we need to select those attributes we are willing
+ * to return, and remap them accordingly */
+
+ /* FIXME: in principle, one could map an attribute
+ * on top of another, which already exists.
+ * As such, in the end there might exist more than
+ * one instance of an attribute.
+ * We should at least check if this occurs, and issue
+ * an error (because multiple instances of attrs in
+ * response are not valid), or merge the values (what
+ * about duplicate values?) */
+ isupdate = be_shadow_update( op );
+ for ( ap = a_first; *ap; ) {
+ struct ldapmapping *mapping = NULL;
+ int drop_missing;
+ int last = -1;
+ Attribute *a;
+
+ if ( ( rwmap->rwm_flags & RWM_F_DROP_UNREQUESTED_ATTRS ) &&
+ op->ors_attrs != NULL &&
+ !SLAP_USERATTRS( rs->sr_attr_flags ) &&
+ !ad_inlist( (*ap)->a_desc, op->ors_attrs ) )
+ {
+ goto cleanup_attr;
+ }
+
+ drop_missing = rwm_mapping( &rwmap->rwm_at,
+ &(*ap)->a_desc->ad_cname, &mapping, RWM_REMAP );
+ if ( drop_missing || ( mapping != NULL && BER_BVISEMPTY( &mapping->m_dst ) ) )
+ {
+ goto cleanup_attr;
+ }
+ if ( mapping != NULL ) {
+ assert( mapping->m_dst_ad != NULL );
+
+ /* try to normalize mapped Attributes if the original
+ * AttributeType was not normalized */
+ if ( (!(*ap)->a_desc->ad_type->sat_equality ||
+ !(*ap)->a_desc->ad_type->sat_equality->smr_normalize) &&
+ mapping->m_dst_ad->ad_type->sat_equality &&
+ mapping->m_dst_ad->ad_type->sat_equality->smr_normalize )
+ {
+ if ((rwmap->rwm_flags & RWM_F_NORMALIZE_MAPPED_ATTRS))
+ {
+ int i = 0;
+
+ last = (*ap)->a_numvals;
+ if ( last )
+ {
+ (*ap)->a_nvals = ch_malloc( (last+1) * sizeof(struct berval) );
+
+ for ( i = 0; !BER_BVISNULL( &(*ap)->a_vals[i]); i++ ) {
+ int rc;
+ /*
+ * check that each value is valid per syntax
+ * and pretty if appropriate
+ */
+ rc = mapping->m_dst_ad->ad_type->sat_equality->smr_normalize(
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ mapping->m_dst_ad->ad_type->sat_syntax,
+ mapping->m_dst_ad->ad_type->sat_equality,
+ &(*ap)->a_vals[i], &(*ap)->a_nvals[i],
+ NULL );
+
+ if ( rc != LDAP_SUCCESS ) {
+ /* FIXME: this is wrong, putting a non-normalized value
+ * into nvals. But when a proxy sends us bogus data,
+ * we still need to give it to the client, even if it
+ * violates the syntax. I.e., we don't want to silently
+ * drop things and trigger an apparent data loss.
+ */
+ ber_dupbv( &(*ap)->a_nvals[i], &(*ap)->a_vals[i] );
+ }
+ }
+ BER_BVZERO( &(*ap)->a_nvals[i] );
+ }
+
+ } else {
+ assert( (*ap)->a_nvals == (*ap)->a_vals );
+ (*ap)->a_nvals = NULL;
+ ber_bvarray_dup_x( &(*ap)->a_nvals, (*ap)->a_vals, NULL );
+ }
+ }
+
+ /* rewrite the attribute description */
+ (*ap)->a_desc = mapping->m_dst_ad;
+
+ /* will need to check for duplicate attrs */
+ check_duplicate_attrs++;
+ }
+
+ if ( (*ap)->a_desc == slap_schema.si_ad_entryDN ) {
+ if ( stripEntryDN ) {
+ /* will be generated by frontend */
+ goto cleanup_attr;
+ }
+
+ } else if ( !isupdate
+ && !get_relax( op )
+ && (*ap)->a_desc->ad_type->sat_no_user_mod
+ && (*ap)->a_desc->ad_type != slap_schema.si_at_undefined )
+ {
+ goto next_attr;
+ }
+
+ if ( last == -1 ) { /* not yet counted */
+ last = (*ap)->a_numvals;
+ }
+
+ if ( last == 0 ) {
+ /* empty? leave it in place because of attrsonly and vlv */
+ goto next_attr;
+ }
+ last--;
+
+ if ( (*ap)->a_desc == slap_schema.si_ad_objectClass
+ || (*ap)->a_desc == slap_schema.si_ad_structuralObjectClass )
+ {
+ struct berval *bv;
+
+ for ( bv = (*ap)->a_vals; !BER_BVISNULL( bv ); bv++ ) {
+ struct berval mapped;
+
+ rwm_map( &rwmap->rwm_oc, &bv[0], &mapped, RWM_REMAP );
+ if ( BER_BVISNULL( &mapped ) || BER_BVISEMPTY( &mapped ) ) {
+remove_oc:;
+ ch_free( bv[0].bv_val );
+ BER_BVZERO( &bv[0] );
+ if ( &(*ap)->a_vals[last] > &bv[0] ) {
+ bv[0] = (*ap)->a_vals[last];
+ BER_BVZERO( &(*ap)->a_vals[last] );
+ }
+ last--;
+ bv--;
+
+ } else if ( mapped.bv_val != bv[0].bv_val
+ && ber_bvstrcasecmp( &mapped, &bv[0] ) != 0 )
+ {
+ int i;
+
+ for ( i = 0; !BER_BVISNULL( &(*ap)->a_vals[ i ] ); i++ ) {
+ if ( &(*ap)->a_vals[ i ] == bv ) {
+ continue;
+ }
+
+ if ( ber_bvstrcasecmp( &mapped, &(*ap)->a_vals[ i ] ) == 0 ) {
+ break;
+ }
+ }
+
+ if ( !BER_BVISNULL( &(*ap)->a_vals[ i ] ) ) {
+ goto remove_oc;
+ }
+
+ /*
+ * FIXME: after LBER_FREEing
+ * the value is replaced by
+ * ch_alloc'ed memory
+ */
+ ber_bvreplace( &bv[0], &mapped );
+
+ /* FIXME: will need to check
+ * if the structuralObjectClass
+ * changed */
+ }
+ }
+
+ /*
+ * It is necessary to try to rewrite attributes with
+ * dn syntax because they might be used in ACLs as
+ * members of groups; since ACLs are applied to the
+ * rewritten stuff, no dn-based subject clause could
+ * be used at the ldap backend side (see
+ * http://www.OpenLDAP.org/faq/data/cache/452.html)
+ * The problem can be overcome by moving the dn-based
+ * ACLs to the target directory server, and letting
+ * everything pass thru the ldap backend. */
+ /* FIXME: handle distinguishedName-like syntaxes, like
+ * nameAndOptionalUID */
+ } else if ( (*ap)->a_desc->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName
+ || ( mapping != NULL && mapping->m_src_ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) )
+ {
+ dc.ctx = "searchAttrDN";
+ rc = rwm_dnattr_result_rewrite( &dc, (*ap)->a_vals, (*ap)->a_nvals );
+ if ( rc != LDAP_SUCCESS ) {
+ goto cleanup_attr;
+ }
+
+ } else if ( (*ap)->a_desc == slap_schema.si_ad_ref ) {
+ dc.ctx = "searchAttrDN";
+ rc = rwm_referral_result_rewrite( &dc, (*ap)->a_vals );
+ if ( rc != LDAP_SUCCESS ) {
+ goto cleanup_attr;
+ }
+ }
+
+
+next_attr:;
+ ap = &(*ap)->a_next;
+ continue;
+
+cleanup_attr:;
+ a = *ap;
+ *ap = (*ap)->a_next;
+
+ attr_free( a );
+ }
+
+ /* only check if some mapping occurred */
+ if ( check_duplicate_attrs ) {
+ for ( ap = a_first; *ap != NULL; ap = &(*ap)->a_next ) {
+ Attribute **tap;
+
+ for ( tap = &(*ap)->a_next; *tap != NULL; ) {
+ if ( (*tap)->a_desc == (*ap)->a_desc ) {
+ Entry e = { 0 };
+ Modification mod = { 0 };
+ const char *text = NULL;
+ char textbuf[ SLAP_TEXT_BUFLEN ];
+ Attribute *next = (*tap)->a_next;
+
+ BER_BVSTR( &e.e_name, "" );
+ BER_BVSTR( &e.e_nname, "" );
+ e.e_attrs = *ap;
+ mod.sm_op = LDAP_MOD_ADD;
+ mod.sm_desc = (*ap)->a_desc;
+ mod.sm_type = mod.sm_desc->ad_cname;
+ mod.sm_numvals = (*tap)->a_numvals;
+ mod.sm_values = (*tap)->a_vals;
+ if ( (*tap)->a_nvals != (*tap)->a_vals ) {
+ mod.sm_nvalues = (*tap)->a_nvals;
+ }
+
+ (void)modify_add_values( &e, &mod,
+ /* permissive */ 1,
+ &text, textbuf, sizeof( textbuf ) );
+
+ /* should not insert new attrs! */
+ assert( e.e_attrs == *ap );
+
+ attr_free( *tap );
+ *tap = next;
+
+ } else {
+ tap = &(*tap)->a_next;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Should return SLAP_CB_CONTINUE or failure, never LDAP_SUCCESS. */
+static int
+rwm_send_entry( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ Entry *e = NULL;
+ struct berval dn = BER_BVNULL,
+ ndn = BER_BVNULL;
+ dncookie dc;
+ int rc;
+
+ assert( rs->sr_entry != NULL );
+
+ /*
+ * Rewrite the dn of the result, if needed
+ */
+ dc.rwmap = rwmap;
+ dc.conn = op->o_conn;
+ dc.rs = NULL;
+ dc.ctx = "searchEntryDN";
+
+ e = rs->sr_entry;
+ if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE ) ) {
+ /* FIXME: all we need to duplicate are:
+ * - dn
+ * - ndn
+ * - attributes that are requested
+ * - no values if attrsonly is set
+ */
+ e = entry_dup( e );
+ if ( e == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto fail;
+ }
+ } else if ( rs->sr_flags & REP_ENTRY_MUSTRELEASE ) {
+ /* ITS#6423: REP_ENTRY_MUSTRELEASE incompatible
+ * with REP_ENTRY_MODIFIABLE */
+ RS_ASSERT( 0 );
+ rc = 1;
+ goto fail;
+ }
+
+ /*
+ * Note: this may fail if the target host(s) schema differs
+ * from the one known to the meta, and a DN with unknown
+ * attributes is returned.
+ */
+ dn = e->e_name;
+ ndn = e->e_nname;
+ rc = rwm_dn_massage_pretty_normalize( &dc, &e->e_name, &dn, &ndn );
+ if ( rc != LDAP_SUCCESS ) {
+ rc = 1;
+ goto fail;
+ }
+
+ if ( e->e_name.bv_val != dn.bv_val ) {
+ ch_free( e->e_name.bv_val );
+ ch_free( e->e_nname.bv_val );
+
+ e->e_name = dn;
+ e->e_nname = ndn;
+ }
+
+ /* TODO: map entry attribute types, objectclasses
+ * and dn-valued attribute values */
+
+ /* FIXME: the entries are in the remote mapping form;
+ * so we need to select those attributes we are willing
+ * to return, and remap them accordingly */
+ (void)rwm_attrs( op, rs, &e->e_attrs, 1 );
+
+ if ( e != rs->sr_entry ) {
+ /* Reimplementing rs_replace_entry(), I suppose to
+ * bypass our own dubious rwm_entry_release_rw() */
+ if ( rs->sr_flags & REP_ENTRY_MUSTRELEASE ) {
+ rs->sr_flags ^= REP_ENTRY_MUSTRELEASE;
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ be_entry_release_r( op, rs->sr_entry );
+ op->o_bd->bd_info = (BackendInfo *)on;
+ } else if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
+ entry_free( rs->sr_entry );
+ }
+ rs->sr_entry = e;
+ rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED;
+ }
+
+ return SLAP_CB_CONTINUE;
+
+fail:;
+ if ( e != NULL && e != rs->sr_entry ) {
+ if ( e->e_name.bv_val == dn.bv_val ) {
+ BER_BVZERO( &e->e_name );
+ }
+
+ if ( e->e_nname.bv_val == ndn.bv_val ) {
+ BER_BVZERO( &e->e_nname );
+ }
+
+ entry_free( e );
+ }
+
+ if ( !BER_BVISNULL( &dn ) ) {
+ ch_free( dn.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &ndn ) ) {
+ ch_free( ndn.bv_val );
+ }
+
+ return rc;
+}
+
+static int
+rwm_operational( Operation *op, SlapReply *rs )
+{
+ /* FIXME: the entries are in the remote mapping form;
+ * so we need to select those attributes we are willing
+ * to return, and remap them accordingly */
+ if ( rs->sr_operational_attrs ) {
+ rwm_attrs( op, rs, &rs->sr_operational_attrs, 1 );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+#if 0
+/* don't use this; it cannot be reverted, and leaves op->o_req_dn
+ * rewritten for subsequent operations; fine for plain suffixmassage,
+ * but destroys everything else */
+static int
+rwm_chk_referrals( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ int rc;
+
+ rc = rwm_op_dn_massage( op, rs, "referralCheckDN" );
+ if ( rc != LDAP_SUCCESS ) {
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ send_ldap_error( op, rs, rc, "referralCheckDN massage error" );
+ return -1;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+#endif
+
+static int
+rwm_rw_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ return rewrite_parse( rwmap->rwm_rw,
+ fname, lineno, argc, argv );
+
+ return 0;
+}
+
+static int
+rwm_suffixmassage_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ struct berval bvnc, nvnc, pvnc, brnc, nrnc, prnc;
+ int massaged;
+ int rc;
+
+ /*
+ * syntax:
+ *
+ * suffixmassage [<suffix>] <massaged suffix>
+ *
+ * the [<suffix>] field must be defined as a valid suffix
+ * for the current database;
+ * the <massaged suffix> shouldn't have already been
+ * defined as a valid suffix for the current server
+ */
+ if ( argc == 2 ) {
+ if ( be->be_suffix == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: "
+ " \"suffixMassage [<suffix>]"
+ " <massaged suffix>\" without "
+ "<suffix> part requires database "
+ "suffix be defined first.\n",
+ fname, lineno );
+ return 1;
+ }
+ bvnc = be->be_suffix[ 0 ];
+ massaged = 1;
+
+ } else if ( argc == 3 ) {
+ ber_str2bv( argv[ 1 ], 0, 0, &bvnc );
+ massaged = 2;
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: syntax is"
+ " \"suffixMassage [<suffix>]"
+ " <massaged suffix>\"\n",
+ fname, lineno );
+ return 1;
+ }
+
+ if ( dnPrettyNormal( NULL, &bvnc, &pvnc, &nvnc, NULL ) != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: suffix DN %s is invalid\n",
+ fname, lineno, bvnc.bv_val );
+ return 1;
+ }
+
+ ber_str2bv( argv[ massaged ], 0, 0, &brnc );
+ if ( dnPrettyNormal( NULL, &brnc, &prnc, &nrnc, NULL ) != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: suffix DN %s is invalid\n",
+ fname, lineno, brnc.bv_val );
+ free( nvnc.bv_val );
+ free( pvnc.bv_val );
+ return 1;
+ }
+
+ /*
+ * The suffix massaging is emulated
+ * by means of the rewrite capabilities
+ */
+ rc = rwm_suffix_massage_config( rwmap->rwm_rw,
+ &pvnc, &nvnc, &prnc, &nrnc );
+ free( nvnc.bv_val );
+ free( pvnc.bv_val );
+ free( nrnc.bv_val );
+ free( prnc.bv_val );
+
+ return rc;
+}
+
+static int
+rwm_m_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ /* objectclass/attribute mapping */
+ return rwm_map_config( &rwmap->rwm_oc,
+ &rwmap->rwm_at,
+ fname, lineno, argc, argv );
+}
+
+static int
+rwm_response( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ int rc;
+
+ if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH ) {
+ return rwm_send_entry( op, rs );
+ }
+
+ switch( op->o_tag ) {
+ case LDAP_REQ_SEARCH:
+ case LDAP_REQ_BIND:
+ case LDAP_REQ_ADD:
+ case LDAP_REQ_DELETE:
+ case LDAP_REQ_MODRDN:
+ case LDAP_REQ_MODIFY:
+ case LDAP_REQ_COMPARE:
+ case LDAP_REQ_EXTENDED:
+ if ( rs->sr_ref ) {
+ dncookie dc;
+
+ /*
+ * Rewrite the dn of the referrals, if needed
+ */
+ dc.rwmap = rwmap;
+ dc.conn = op->o_conn;
+ dc.rs = NULL;
+ dc.ctx = "referralDN";
+ rc = rwm_referral_result_rewrite( &dc, rs->sr_ref );
+ /* FIXME: impossible, so far */
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_err = rc;
+ break;
+ }
+ }
+
+ rwm_matched( op, rs );
+ break;
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+rwm_db_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ int rc = 0;
+ char *argv0 = NULL;
+
+ if ( strncasecmp( argv[ 0 ], "rwm-", STRLENOF( "rwm-" ) ) == 0 ) {
+ argv0 = argv[ 0 ];
+ argv[ 0 ] = &argv0[ STRLENOF( "rwm-" ) ];
+ }
+
+ if ( strncasecmp( argv[0], "rewrite", STRLENOF("rewrite") ) == 0 ) {
+ rc = rwm_rw_config( be, fname, lineno, argc, argv );
+
+ } else if ( strcasecmp( argv[0], "map" ) == 0 ) {
+ rc = rwm_m_config( be, fname, lineno, argc, argv );
+
+ } else if ( strcasecmp( argv[0], "suffixmassage" ) == 0 ) {
+ rc = rwm_suffixmassage_config( be, fname, lineno, argc, argv );
+
+ } else if ( strcasecmp( argv[0], "t-f-support" ) == 0 ) {
+ if ( argc != 2 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: \"t-f-support {no|yes|discover}\" needs 1 argument.\n",
+ fname, lineno );
+ return( 1 );
+ }
+
+ if ( strcasecmp( argv[ 1 ], "no" ) == 0 ) {
+ rwmap->rwm_flags &= ~(RWM_F_SUPPORT_T_F_MASK2);
+
+ } else if ( strcasecmp( argv[ 1 ], "yes" ) == 0 ) {
+ rwmap->rwm_flags |= RWM_F_SUPPORT_T_F;
+
+ /* TODO: not implemented yet */
+ } else if ( strcasecmp( argv[ 1 ], "discover" ) == 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: \"discover\" not supported yet "
+ "in \"t-f-support {no|yes|discover}\".\n",
+ fname, lineno );
+ return( 1 );
+#if 0
+ rwmap->rwm_flags |= RWM_F_SUPPORT_T_F_DISCOVER;
+#endif
+
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: unknown value \"%s\" for \"t-f-support {no|yes|discover}\".\n",
+ fname, lineno, argv[ 1 ] );
+ return 1;
+ }
+
+ } else if ( strcasecmp( argv[0], "normalize-mapped-attrs" ) == 0 ) {
+ if ( argc !=2 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: \"normalize-mapped-attrs {no|yes}\" needs 1 argument.\n",
+ fname, lineno );
+ return( 1 );
+ }
+
+ if ( strcasecmp( argv[ 1 ], "no" ) == 0 ) {
+ rwmap->rwm_flags &= ~(RWM_F_NORMALIZE_MAPPED_ATTRS);
+
+ } else if ( strcasecmp( argv[ 1 ], "yes" ) == 0 ) {
+ rwmap->rwm_flags |= RWM_F_NORMALIZE_MAPPED_ATTRS;
+ }
+
+ } else {
+ rc = SLAP_CONF_UNKNOWN;
+ }
+
+ if ( argv0 ) {
+ argv[ 0 ] = argv0;
+ }
+
+ return rc;
+}
+
+/*
+ * dynamic configuration...
+ */
+
+enum {
+ /* rewrite */
+ RWM_CF_REWRITE = 1,
+
+ /* map */
+ RWM_CF_MAP,
+ RWM_CF_T_F_SUPPORT,
+ RWM_CF_NORMALIZE_MAPPED,
+ RWM_CF_DROP_UNREQUESTED,
+
+ RWM_CF_LAST
+};
+
+static slap_verbmasks t_f_mode[] = {
+ { BER_BVC( "true" ), RWM_F_SUPPORT_T_F },
+ { BER_BVC( "yes" ), RWM_F_SUPPORT_T_F },
+ { BER_BVC( "discover" ), RWM_F_SUPPORT_T_F_DISCOVER },
+ { BER_BVC( "false" ), RWM_F_NONE },
+ { BER_BVC( "no" ), RWM_F_NONE },
+ { BER_BVNULL, 0 }
+};
+
+static ConfigDriver rwm_cf_gen;
+
+static ConfigTable rwmcfg[] = {
+ { "rwm-rewrite", "rewrite",
+ 2, 0, STRLENOF("rwm-rewrite"),
+ ARG_MAGIC|RWM_CF_REWRITE, rwm_cf_gen,
+ "( OLcfgOvAt:16.1 NAME 'olcRwmRewrite' "
+ "DESC 'Rewrites strings' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "X-ORDERED 'VALUES' )",
+ NULL, NULL },
+
+ { "rwm-suffixmassage", "[virtual]> <real",
+ 2, 3, 0, ARG_MAGIC|RWM_CF_REWRITE, rwm_cf_gen,
+ NULL, NULL, NULL },
+
+ { "rwm-t-f-support", "true|false|discover",
+ 2, 2, 0, ARG_MAGIC|RWM_CF_T_F_SUPPORT, rwm_cf_gen,
+ "( OLcfgOvAt:16.2 NAME 'olcRwmTFSupport' "
+ "DESC 'Absolute filters support' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "rwm-map", "{objectClass|attribute}",
+ 2, 4, 0, ARG_MAGIC|RWM_CF_MAP, rwm_cf_gen,
+ "( OLcfgOvAt:16.3 NAME 'olcRwmMap' "
+ "DESC 'maps attributes/objectClasses' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString "
+ "X-ORDERED 'VALUES' )",
+ NULL, NULL },
+
+ { "rwm-normalize-mapped-attrs", "true|false",
+ 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|RWM_CF_NORMALIZE_MAPPED, rwm_cf_gen,
+ "( OLcfgOvAt:16.4 NAME 'olcRwmNormalizeMapped' "
+ "DESC 'Normalize mapped attributes/objectClasses' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+
+ { "rwm-drop-unrequested-attrs", "true|false",
+ 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|RWM_CF_DROP_UNREQUESTED, rwm_cf_gen,
+ "( OLcfgOvAt:16.5 NAME 'olcRwmDropUnrequested' "
+ "DESC 'Drop unrequested attributes' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean "
+ "SINGLE-VALUE )",
+ NULL, NULL },
+
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs rwmocs[] = {
+ { "( OLcfgOvOc:16.1 "
+ "NAME 'olcRwmConfig' "
+ "DESC 'Rewrite/remap configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( "
+ "olcRwmRewrite $ "
+ "olcRwmTFSupport $ "
+ "olcRwmMap $ "
+ "olcRwmNormalizeMapped $ "
+ "olcRwmDropUnrequested"
+ ") )",
+ Cft_Overlay, rwmcfg, NULL, NULL },
+ { NULL, 0, NULL }
+};
+
+static int
+rwm_bva_add(
+ BerVarray *bva,
+ int idx,
+ char **argv )
+{
+ char *line;
+ struct berval bv;
+
+ line = ldap_charray2str( argv, "\" \"" );
+ if ( line != NULL ) {
+ int len = strlen( argv[ 0 ] );
+
+ ber_str2bv( line, 0, 0, &bv );
+ AC_MEMCPY( &bv.bv_val[ len ], &bv.bv_val[ len + 1 ],
+ bv.bv_len - ( len + 1 ) );
+ bv.bv_val[ bv.bv_len - 1 ] = '"';
+
+ if ( idx == -1 ) {
+ ber_bvarray_add( bva, &bv );
+
+ } else {
+ (*bva)[ idx ] = bv;
+ }
+
+ return 0;
+ }
+
+ return -1;
+}
+
+static int
+rwm_bva_rewrite_add(
+ struct ldaprwmap *rwmap,
+ int idx,
+ char **argv )
+{
+ return rwm_bva_add( &rwmap->rwm_bva_rewrite, idx, argv );
+}
+
+#ifdef unused
+static int
+rwm_bva_map_add(
+ struct ldaprwmap *rwmap,
+ int idx,
+ char **argv )
+{
+ return rwm_bva_add( &rwmap->rwm_bva_map, idx, argv );
+}
+#endif /* unused */
+
+static int
+rwm_info_init( struct rewrite_info ** rwm_rw )
+{
+ char *rargv[ 3 ];
+
+ *rwm_rw = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
+ if ( *rwm_rw == NULL ) {
+ return -1;
+ }
+
+ /* this rewriteContext by default must be null;
+ * rules can be added if required */
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "searchFilter";
+ rargv[ 2 ] = NULL;
+ rewrite_parse( *rwm_rw, "<suffix massage>", 1, 2, rargv );
+
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "default";
+ rargv[ 2 ] = NULL;
+ rewrite_parse( *rwm_rw, "<suffix massage>", 2, 2, rargv );
+
+ return 0;
+}
+
+static int
+rwm_cf_gen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ BackendDB db;
+ char *argv0;
+ int idx0 = 0;
+ int rc = 0;
+
+ db = *c->be;
+ db.bd_info = c->bi;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ struct berval bv = BER_BVNULL;
+
+ switch ( c->type ) {
+ case RWM_CF_REWRITE:
+ if ( rwmap->rwm_bva_rewrite == NULL ) {
+ rc = 1;
+
+ } else {
+ rc = slap_bv_x_ordered_unparse( rwmap->rwm_bva_rewrite, &c->rvalue_vals );
+ }
+ break;
+
+ case RWM_CF_T_F_SUPPORT:
+ enum_to_verb( t_f_mode, (rwmap->rwm_flags & RWM_F_SUPPORT_T_F_MASK2), &bv );
+ if ( BER_BVISNULL( &bv ) ) {
+ /* there's something wrong... */
+ assert( 0 );
+ rc = 1;
+
+ } else {
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ break;
+
+ case RWM_CF_MAP:
+ if ( rwmap->rwm_bva_map == NULL ) {
+ rc = 1;
+
+ } else {
+ slap_bv_x_ordered_unparse( rwmap->rwm_bva_map, &c->rvalue_vals );
+ if ( !c->rvalue_vals ) {
+ rc = 1;
+ }
+ }
+ break;
+
+ case RWM_CF_NORMALIZE_MAPPED:
+ c->value_int = ( rwmap->rwm_flags & RWM_F_NORMALIZE_MAPPED_ATTRS );
+ break;
+
+ case RWM_CF_DROP_UNREQUESTED:
+ c->value_int = ( rwmap->rwm_flags & RWM_F_DROP_UNREQUESTED_ATTRS );
+ break;
+
+ default:
+ assert( 0 );
+ rc = 1;
+ }
+
+ return rc;
+
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ switch ( c->type ) {
+ case RWM_CF_REWRITE:
+ if ( c->valx >= 0 ) {
+ int i;
+
+ for ( i = 0; !BER_BVISNULL( &rwmap->rwm_bva_rewrite[ i ] ); i++ )
+ /* count'em */ ;
+
+ if ( c->valx >= i ) {
+ rc = 1;
+ break;
+ }
+
+ ber_memfree( rwmap->rwm_bva_rewrite[ c->valx ].bv_val );
+ for ( i = c->valx; !BER_BVISNULL( &rwmap->rwm_bva_rewrite[ i + 1 ] ); i++ )
+ {
+ rwmap->rwm_bva_rewrite[ i ] = rwmap->rwm_bva_rewrite[ i + 1 ];
+ }
+ BER_BVZERO( &rwmap->rwm_bva_rewrite[ i ] );
+
+ rewrite_info_delete( &rwmap->rwm_rw );
+ assert( rwmap->rwm_rw == NULL );
+
+ rc = rwm_info_init( &rwmap->rwm_rw );
+
+ for ( i = 0; !BER_BVISNULL( &rwmap->rwm_bva_rewrite[ i ] ); i++ )
+ {
+ ConfigArgs ca = { 0 };
+
+ ca.line = rwmap->rwm_bva_rewrite[ i ].bv_val;
+ ca.argc = 0;
+ init_config_argv( &ca );
+ config_parse_ldif( &ca );
+
+ argv0 = ca.argv[ 0 ];
+ ca.argv[ 0 ] += STRLENOF( "rwm-" );
+
+ if ( strcasecmp( ca.argv[ 0 ], "suffixmassage" ) == 0 ) {
+ rc = rwm_suffixmassage_config( &db, c->fname, c->lineno,
+ ca.argc, ca.argv );
+
+ } else {
+ rc = rwm_rw_config( &db, c->fname, c->lineno,
+ ca.argc, ca.argv );
+ }
+
+ ca.argv[ 0 ] = argv0;
+
+ ch_free( ca.tline );
+ ch_free( ca.argv );
+
+ assert( rc == 0 );
+ }
+
+ } else if ( rwmap->rwm_rw != NULL ) {
+ rewrite_info_delete( &rwmap->rwm_rw );
+ assert( rwmap->rwm_rw == NULL );
+
+ ber_bvarray_free( rwmap->rwm_bva_rewrite );
+ rwmap->rwm_bva_rewrite = NULL;
+
+ rc = rwm_info_init( &rwmap->rwm_rw );
+ }
+ break;
+
+ case RWM_CF_T_F_SUPPORT:
+ rwmap->rwm_flags &= ~RWM_F_SUPPORT_T_F_MASK2;
+ break;
+
+ case RWM_CF_MAP:
+ if ( c->valx >= 0 ) {
+ struct ldapmap rwm_oc = rwmap->rwm_oc;
+ struct ldapmap rwm_at = rwmap->rwm_at;
+ char *argv[5];
+ int cnt = 0;
+
+ if ( rwmap->rwm_bva_map ) {
+ for ( ; !BER_BVISNULL( &rwmap->rwm_bva_map[ cnt ] ); cnt++ )
+ /* count */ ;
+ }
+
+ if ( c->valx >= cnt ) {
+ rc = 1;
+ break;
+ }
+
+ memset( &rwmap->rwm_oc, 0, sizeof( rwmap->rwm_oc ) );
+ memset( &rwmap->rwm_at, 0, sizeof( rwmap->rwm_at ) );
+
+ /* re-parse all mappings except the one
+ * that needs to be eliminated */
+ argv[0] = "map";
+ for ( cnt = 0; !BER_BVISNULL( &rwmap->rwm_bva_map[ cnt ] ); cnt++ ) {
+ ConfigArgs ca = { 0 };
+
+ if ( cnt == c->valx ) {
+ continue;
+ }
+
+ ca.line = rwmap->rwm_bva_map[ cnt ].bv_val;
+ ca.argc = 0;
+ init_config_argv( &ca );
+ config_parse_ldif( &ca );
+
+ argv[1] = ca.argv[0];
+ argv[2] = ca.argv[1];
+ argv[3] = ca.argv[2];
+ argv[4] = ca.argv[3];
+
+ rc = rwm_m_config( &db, c->fname, c->lineno, ca.argc + 1, argv );
+
+ ch_free( ca.tline );
+ ch_free( ca.argv );
+
+ /* in case of failure, restore
+ * the existing mapping */
+ if ( rc ) {
+ ldap_avl_free( rwmap->rwm_oc.remap, rwm_mapping_dst_free );
+ ldap_avl_free( rwmap->rwm_oc.map, rwm_mapping_free );
+ ldap_avl_free( rwmap->rwm_at.remap, rwm_mapping_dst_free );
+ ldap_avl_free( rwmap->rwm_at.map, rwm_mapping_free );
+ rwmap->rwm_oc = rwm_oc;
+ rwmap->rwm_at = rwm_at;
+ break;
+ }
+ }
+
+ /* in case of success, destroy the old mapping
+ * and eliminate the deleted one */
+ if ( rc == 0 ) {
+ ldap_avl_free( rwm_oc.remap, rwm_mapping_dst_free );
+ ldap_avl_free( rwm_oc.map, rwm_mapping_free );
+ ldap_avl_free( rwm_at.remap, rwm_mapping_dst_free );
+ ldap_avl_free( rwm_at.map, rwm_mapping_free );
+
+ ber_memfree( rwmap->rwm_bva_map[ c->valx ].bv_val );
+ for ( cnt = c->valx; !BER_BVISNULL( &rwmap->rwm_bva_map[ cnt ] ); cnt++ ) {
+ rwmap->rwm_bva_map[ cnt ] = rwmap->rwm_bva_map[ cnt + 1 ];
+ }
+ }
+
+ } else {
+ ldap_avl_free( rwmap->rwm_oc.remap, rwm_mapping_dst_free );
+ ldap_avl_free( rwmap->rwm_oc.map, rwm_mapping_free );
+ ldap_avl_free( rwmap->rwm_at.remap, rwm_mapping_dst_free );
+ ldap_avl_free( rwmap->rwm_at.map, rwm_mapping_free );
+
+ rwmap->rwm_oc.remap = NULL;
+ rwmap->rwm_oc.map = NULL;
+ rwmap->rwm_at.remap = NULL;
+ rwmap->rwm_at.map = NULL;
+
+ ber_bvarray_free( rwmap->rwm_bva_map );
+ rwmap->rwm_bva_map = NULL;
+ }
+ break;
+
+ case RWM_CF_NORMALIZE_MAPPED:
+ rwmap->rwm_flags &= ~RWM_F_NORMALIZE_MAPPED_ATTRS;
+ break;
+
+ case RWM_CF_DROP_UNREQUESTED:
+ rwmap->rwm_flags &= ~RWM_F_DROP_UNREQUESTED_ATTRS;
+ break;
+
+ default:
+ return 1;
+ }
+ return rc;
+ }
+
+ if ( strncasecmp( c->argv[ 0 ], "olcRwm", STRLENOF( "olcRwm" ) ) == 0 ) {
+ idx0 = 1;
+ }
+
+ switch ( c->type ) {
+ case RWM_CF_REWRITE:
+ if ( c->valx >= 0 ) {
+ struct rewrite_info *rwm_rw = rwmap->rwm_rw;
+ int i, last;
+
+ for ( last = 0; rwmap->rwm_bva_rewrite && !BER_BVISNULL( &rwmap->rwm_bva_rewrite[ last ] ); last++ )
+ /* count'em */ ;
+
+ if ( c->valx > last ) {
+ c->valx = last;
+ }
+
+ rwmap->rwm_rw = NULL;
+ rc = rwm_info_init( &rwmap->rwm_rw );
+
+ for ( i = 0; i < c->valx; i++ ) {
+ ConfigArgs ca = { 0 };
+
+ ca.line = rwmap->rwm_bva_rewrite[ i ].bv_val;
+ ca.argc = 0;
+ init_config_argv( &ca );
+ config_parse_ldif( &ca );
+
+ argv0 = ca.argv[ 0 ];
+ ca.argv[ 0 ] += STRLENOF( "rwm-" );
+
+ if ( strcasecmp( ca.argv[ 0 ], "suffixmassage" ) == 0 ) {
+ rc = rwm_suffixmassage_config( &db, c->fname, c->lineno,
+ ca.argc, ca.argv );
+
+ } else {
+ rc = rwm_rw_config( &db, c->fname, c->lineno,
+ ca.argc, ca.argv );
+ }
+
+ ca.argv[ 0 ] = argv0;
+
+ ch_free( ca.tline );
+ ch_free( ca.argv );
+
+ assert( rc == 0 );
+ }
+
+ argv0 = c->argv[ idx0 ];
+ if ( strncasecmp( argv0, "rwm-", STRLENOF( "rwm-" ) ) != 0 ) {
+ return 1;
+ }
+ c->argv[ idx0 ] += STRLENOF( "rwm-" );
+ if ( strcasecmp( c->argv[ idx0 ], "suffixmassage" ) == 0 ) {
+ rc = rwm_suffixmassage_config( &db, c->fname, c->lineno,
+ c->argc - idx0, &c->argv[ idx0 ] );
+
+ } else {
+ rc = rwm_rw_config( &db, c->fname, c->lineno,
+ c->argc - idx0, &c->argv[ idx0 ] );
+ }
+ c->argv[ idx0 ] = argv0;
+ if ( rc != 0 ) {
+ rewrite_info_delete( &rwmap->rwm_rw );
+ assert( rwmap->rwm_rw == NULL );
+
+ rwmap->rwm_rw = rwm_rw;
+ return 1;
+ }
+
+ for ( i = c->valx; rwmap->rwm_bva_rewrite && !BER_BVISNULL( &rwmap->rwm_bva_rewrite[ i ] ); i++ )
+ {
+ ConfigArgs ca = { 0 };
+
+ ca.line = rwmap->rwm_bva_rewrite[ i ].bv_val;
+ ca.argc = 0;
+ init_config_argv( &ca );
+ config_parse_ldif( &ca );
+
+ argv0 = ca.argv[ 0 ];
+ ca.argv[ 0 ] += STRLENOF( "rwm-" );
+
+ if ( strcasecmp( ca.argv[ 0 ], "suffixmassage" ) == 0 ) {
+ rc = rwm_suffixmassage_config( &db, c->fname, c->lineno,
+ ca.argc, ca.argv );
+
+ } else {
+ rc = rwm_rw_config( &db, c->fname, c->lineno,
+ ca.argc, ca.argv );
+ }
+
+ ca.argv[ 0 ] = argv0;
+
+ ch_free( ca.tline );
+ ch_free( ca.argv );
+
+ assert( rc == 0 );
+ }
+
+ rwmap->rwm_bva_rewrite = ch_realloc( rwmap->rwm_bva_rewrite,
+ ( last + 2 )*sizeof( struct berval ) );
+ BER_BVZERO( &rwmap->rwm_bva_rewrite[last+1] );
+
+ for ( i = last - 1; i >= c->valx; i-- )
+ {
+ rwmap->rwm_bva_rewrite[ i + 1 ] = rwmap->rwm_bva_rewrite[ i ];
+ }
+
+ rwm_bva_rewrite_add( rwmap, c->valx, &c->argv[ idx0 ] );
+
+ rewrite_info_delete( &rwm_rw );
+ assert( rwm_rw == NULL );
+
+ break;
+ }
+
+ argv0 = c->argv[ idx0 ];
+ if ( strncasecmp( argv0, "rwm-", STRLENOF( "rwm-" ) ) != 0 ) {
+ return 1;
+ }
+ c->argv[ idx0 ] += STRLENOF( "rwm-" );
+ if ( strcasecmp( c->argv[ idx0 ], "suffixmassage" ) == 0 ) {
+ rc = rwm_suffixmassage_config( &db, c->fname, c->lineno,
+ c->argc - idx0, &c->argv[ idx0 ] );
+
+ } else {
+ rc = rwm_rw_config( &db, c->fname, c->lineno,
+ c->argc - idx0, &c->argv[ idx0 ] );
+ }
+ c->argv[ idx0 ] = argv0;
+ if ( rc ) {
+ return 1;
+
+ } else {
+ rwm_bva_rewrite_add( rwmap, -1, &c->argv[ idx0 ] );
+ }
+ break;
+
+ case RWM_CF_T_F_SUPPORT:
+ rc = verb_to_mask( c->argv[ 1 ], t_f_mode );
+ if ( BER_BVISNULL( &t_f_mode[ rc ].word ) ) {
+ return 1;
+ }
+
+ rwmap->rwm_flags &= ~RWM_F_SUPPORT_T_F_MASK2;
+ rwmap->rwm_flags |= t_f_mode[ rc ].mask;
+ rc = 0;
+ break;
+
+ case RWM_CF_MAP:
+ if ( c->valx >= 0 ) {
+ struct ldapmap rwm_oc = rwmap->rwm_oc;
+ struct ldapmap rwm_at = rwmap->rwm_at;
+ char *argv[5];
+ int cnt = 0;
+
+ if ( rwmap->rwm_bva_map ) {
+ for ( ; !BER_BVISNULL( &rwmap->rwm_bva_map[ cnt ] ); cnt++ )
+ /* count */ ;
+ }
+
+ if ( c->valx >= cnt ) {
+ c->valx = cnt;
+ }
+
+ memset( &rwmap->rwm_oc, 0, sizeof( rwmap->rwm_oc ) );
+ memset( &rwmap->rwm_at, 0, sizeof( rwmap->rwm_at ) );
+
+ /* re-parse all mappings, including the one
+ * that needs to be added */
+ argv[0] = "map";
+ for ( cnt = 0; cnt < c->valx; cnt++ ) {
+ ConfigArgs ca = { 0 };
+
+ ca.line = rwmap->rwm_bva_map[ cnt ].bv_val;
+ ca.argc = 0;
+ init_config_argv( &ca );
+ config_parse_ldif( &ca );
+
+ argv[1] = ca.argv[0];
+ argv[2] = ca.argv[1];
+ argv[3] = ca.argv[2];
+ argv[4] = ca.argv[3];
+
+ rc = rwm_m_config( &db, c->fname, c->lineno, ca.argc + 1, argv );
+
+ ch_free( ca.tline );
+ ch_free( ca.argv );
+
+ /* in case of failure, restore
+ * the existing mapping */
+ if ( rc ) {
+ goto rwmmap_fail;
+ }
+ }
+
+ argv0 = c->argv[0];
+ c->argv[0] = "map";
+ rc = rwm_m_config( &db, c->fname, c->lineno, c->argc, c->argv );
+ c->argv[0] = argv0;
+ if ( rc ) {
+ goto rwmmap_fail;
+ }
+
+ if ( rwmap->rwm_bva_map ) {
+ for ( ; !BER_BVISNULL( &rwmap->rwm_bva_map[ cnt ] ); cnt++ ) {
+ ConfigArgs ca = { 0 };
+
+ ca.line = rwmap->rwm_bva_map[ cnt ].bv_val;
+ ca.argc = 0;
+ init_config_argv( &ca );
+ config_parse_ldif( &ca );
+
+ argv[1] = ca.argv[0];
+ argv[2] = ca.argv[1];
+ argv[3] = ca.argv[2];
+ argv[4] = ca.argv[3];
+
+ rc = rwm_m_config( &db, c->fname, c->lineno, ca.argc + 1, argv );
+
+ ch_free( ca.tline );
+ ch_free( ca.argv );
+
+ /* in case of failure, restore
+ * the existing mapping */
+ if ( rc ) {
+ goto rwmmap_fail;
+ }
+ }
+ }
+
+ /* in case of success, destroy the old mapping
+ * and add the new one */
+ if ( rc == 0 ) {
+ BerVarray tmp;
+ struct berval bv, *bvp = &bv;
+
+ if ( rwm_bva_add( &bvp, 0, &c->argv[ idx0 ] ) ) {
+ rc = 1;
+ goto rwmmap_fail;
+ }
+
+ tmp = ber_memrealloc( rwmap->rwm_bva_map,
+ sizeof( struct berval )*( cnt + 2 ) );
+ if ( tmp == NULL ) {
+ ber_memfree( bv.bv_val );
+ rc = 1;
+ goto rwmmap_fail;
+ }
+ rwmap->rwm_bva_map = tmp;
+ BER_BVZERO( &rwmap->rwm_bva_map[ cnt + 1 ] );
+
+ ldap_avl_free( rwm_oc.remap, rwm_mapping_dst_free );
+ ldap_avl_free( rwm_oc.map, rwm_mapping_free );
+ ldap_avl_free( rwm_at.remap, rwm_mapping_dst_free );
+ ldap_avl_free( rwm_at.map, rwm_mapping_free );
+
+ for ( ; cnt-- > c->valx; ) {
+ rwmap->rwm_bva_map[ cnt + 1 ] = rwmap->rwm_bva_map[ cnt ];
+ }
+ rwmap->rwm_bva_map[ c->valx ] = bv;
+
+ } else {
+rwmmap_fail:;
+ ldap_avl_free( rwmap->rwm_oc.remap, rwm_mapping_dst_free );
+ ldap_avl_free( rwmap->rwm_oc.map, rwm_mapping_free );
+ ldap_avl_free( rwmap->rwm_at.remap, rwm_mapping_dst_free );
+ ldap_avl_free( rwmap->rwm_at.map, rwm_mapping_free );
+ rwmap->rwm_oc = rwm_oc;
+ rwmap->rwm_at = rwm_at;
+ }
+
+ break;
+ }
+
+ argv0 = c->argv[ 0 ];
+ c->argv[ 0 ] += STRLENOF( "rwm-" );
+ rc = rwm_m_config( &db, c->fname, c->lineno, c->argc, c->argv );
+ c->argv[ 0 ] = argv0;
+ if ( rc ) {
+ return 1;
+
+ } else {
+ char *line;
+ struct berval bv;
+
+ line = ldap_charray2str( &c->argv[ 1 ], " " );
+ if ( line != NULL ) {
+ ber_str2bv( line, 0, 0, &bv );
+ ber_bvarray_add( &rwmap->rwm_bva_map, &bv );
+ }
+ }
+ break;
+
+ case RWM_CF_NORMALIZE_MAPPED:
+ if ( c->value_int ) {
+ rwmap->rwm_flags |= RWM_F_NORMALIZE_MAPPED_ATTRS;
+ } else {
+ rwmap->rwm_flags &= ~RWM_F_NORMALIZE_MAPPED_ATTRS;
+ }
+ break;
+
+ case RWM_CF_DROP_UNREQUESTED:
+ if ( c->value_int ) {
+ rwmap->rwm_flags |= RWM_F_DROP_UNREQUESTED_ATTRS;
+ } else {
+ rwmap->rwm_flags &= ~RWM_F_DROP_UNREQUESTED_ATTRS;
+ }
+ break;
+
+ default:
+ assert( 0 );
+ return 1;
+ }
+
+ return rc;
+}
+
+static int
+rwm_db_init(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ struct ldaprwmap *rwmap;
+ int rc = 0;
+
+ rwmap = (struct ldaprwmap *)ch_calloc( 1, sizeof( struct ldaprwmap ) );
+
+ /* default */
+ rwmap->rwm_flags = RWM_F_DROP_UNREQUESTED_ATTRS;
+
+ rc = rwm_info_init( &rwmap->rwm_rw );
+
+ on->on_bi.bi_private = (void *)rwmap;
+
+ if ( rc ) {
+ (void)rwm_db_destroy( be, NULL );
+ }
+
+ return rc;
+}
+
+static int
+rwm_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ int rc = 0;
+
+ if ( on->on_bi.bi_private ) {
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ if ( rwmap->rwm_rw ) {
+ rewrite_info_delete( &rwmap->rwm_rw );
+ if ( rwmap->rwm_bva_rewrite )
+ ber_bvarray_free( rwmap->rwm_bva_rewrite );
+ }
+
+ ldap_avl_free( rwmap->rwm_oc.remap, rwm_mapping_dst_free );
+ ldap_avl_free( rwmap->rwm_oc.map, rwm_mapping_free );
+ ldap_avl_free( rwmap->rwm_at.remap, rwm_mapping_dst_free );
+ ldap_avl_free( rwmap->rwm_at.map, rwm_mapping_free );
+ ber_bvarray_free( rwmap->rwm_bva_map );
+
+ ch_free( rwmap );
+ }
+
+ return rc;
+}
+
+static slap_overinst rwm = { { NULL } };
+
+#if SLAPD_OVER_RWM == SLAPD_MOD_DYNAMIC
+static
+#endif /* SLAPD_OVER_RWM == SLAPD_MOD_DYNAMIC */
+int
+rwm_initialize( void )
+{
+ int rc;
+
+ /* Make sure we don't exceed the bits reserved for userland */
+ config_check_userland( RWM_CF_LAST );
+
+ memset( &rwm, 0, sizeof( slap_overinst ) );
+
+ rwm.on_bi.bi_type = "rwm";
+ rwm.on_bi.bi_flags =
+ SLAPO_BFLAG_SINGLE |
+ 0;
+
+ rwm.on_bi.bi_db_init = rwm_db_init;
+ rwm.on_bi.bi_db_config = rwm_db_config;
+ rwm.on_bi.bi_db_destroy = rwm_db_destroy;
+
+ rwm.on_bi.bi_op_bind = rwm_op_bind;
+ rwm.on_bi.bi_op_search = rwm_op_search;
+ rwm.on_bi.bi_op_compare = rwm_op_compare;
+ rwm.on_bi.bi_op_modify = rwm_op_modify;
+ rwm.on_bi.bi_op_modrdn = rwm_op_modrdn;
+ rwm.on_bi.bi_op_add = rwm_op_add;
+ rwm.on_bi.bi_op_delete = rwm_op_delete;
+ rwm.on_bi.bi_op_unbind = rwm_op_unbind;
+ rwm.on_bi.bi_extended = rwm_extended;
+#if 1 /* TODO */
+ rwm.on_bi.bi_entry_release_rw = rwm_entry_release_rw;
+ rwm.on_bi.bi_entry_get_rw = rwm_entry_get_rw;
+#endif
+
+ rwm.on_bi.bi_operational = rwm_operational;
+ rwm.on_bi.bi_chk_referrals = 0 /* rwm_chk_referrals */ ;
+
+ rwm.on_bi.bi_connection_init = rwm_conn_init;
+ rwm.on_bi.bi_connection_destroy = rwm_conn_destroy;
+
+ rwm.on_response = rwm_response;
+
+ rwm.on_bi.bi_cf_ocs = rwmocs;
+
+ rc = config_register_schema( rwmcfg, rwmocs );
+ if ( rc ) {
+ return rc;
+ }
+
+ return overlay_register( &rwm );
+}
+
+#if SLAPD_OVER_RWM == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return rwm_initialize();
+}
+#endif /* SLAPD_OVER_RWM == SLAPD_MOD_DYNAMIC */
+
+#endif /* SLAPD_OVER_RWM */
diff --git a/servers/slapd/overlays/rwm.h b/servers/slapd/overlays/rwm.h
new file mode 100644
index 0000000..6753737
--- /dev/null
+++ b/servers/slapd/overlays/rwm.h
@@ -0,0 +1,183 @@
+/* rwm.h - dn rewrite/attribute mapping header file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#ifndef RWM_H
+#define RWM_H
+
+/* String rewrite library */
+#include "rewrite.h"
+
+LDAP_BEGIN_DECL
+
+/* define to enable referral DN massage by default */
+#undef RWM_REFERRAL_REWRITE
+
+struct ldapmap {
+ int drop_missing;
+
+ Avlnode *map;
+ Avlnode *remap;
+};
+
+struct ldapmapping {
+ int m_flags;
+#define RWMMAP_F_NONE 0x00
+#define RWMMAP_F_IS_OC 0x01
+#define RWMMAP_F_FREE_SRC 0x10
+#define RWMMAP_F_FREE_DST 0x20
+ struct berval m_src;
+ union {
+ AttributeDescription *m_s_ad;
+ ObjectClass *m_s_oc;
+ } m_src_ref;
+#define m_src_ad m_src_ref.m_s_ad
+#define m_src_oc m_src_ref.m_s_oc
+ struct berval m_dst;
+ union {
+ AttributeDescription *m_d_ad;
+ ObjectClass *m_d_oc;
+ } m_dst_ref;
+#define m_dst_ad m_dst_ref.m_d_ad
+#define m_dst_oc m_dst_ref.m_d_oc
+};
+
+struct ldaprwmap {
+ /*
+ * DN rewriting
+ */
+ struct rewrite_info *rwm_rw;
+ BerVarray rwm_bva_rewrite;
+
+ /*
+ * Attribute/objectClass mapping
+ */
+ struct ldapmap rwm_oc;
+ struct ldapmap rwm_at;
+ BerVarray rwm_bva_map;
+
+#define RWM_F_NONE (0x0000U)
+#define RWM_F_NORMALIZE_MAPPED_ATTRS (0x0001U)
+#define RWM_F_DROP_UNREQUESTED_ATTRS (0x0002U)
+#define RWM_F_SUPPORT_T_F (0x4000U)
+#define RWM_F_SUPPORT_T_F_DISCOVER (0x8000U)
+#define RWM_F_SUPPORT_T_F_MASK (RWM_F_SUPPORT_T_F)
+#define RWM_F_SUPPORT_T_F_MASK2 (RWM_F_SUPPORT_T_F|RWM_F_SUPPORT_T_F_DISCOVER)
+ unsigned rwm_flags;
+};
+
+/* Whatever context ldap_back_dn_massage needs... */
+typedef struct dncookie {
+ struct ldaprwmap *rwmap;
+
+ Connection *conn;
+ char *ctx;
+ SlapReply *rs;
+} dncookie;
+
+int rwm_dn_massage( dncookie *dc, struct berval *in, struct berval *dn );
+int rwm_dn_massage_pretty( dncookie *dc, struct berval *in, struct berval *pdn );
+int rwm_dn_massage_normalize( dncookie *dc, struct berval *in, struct berval *ndn );
+int rwm_dn_massage_pretty_normalize( dncookie *dc, struct berval *in, struct berval *pdn, struct berval *ndn );
+
+/* attributeType/objectClass mapping */
+int rwm_mapping_cmp (const void *, const void *);
+int rwm_mapping_dup (void *, void *);
+
+int rwm_map_init ( struct ldapmap *lm, struct ldapmapping ** );
+void rwm_map ( struct ldapmap *map, struct berval *s, struct berval *m,
+ int remap );
+int rwm_mapping ( struct ldapmap *map, struct berval *s,
+ struct ldapmapping **m, int remap );
+#define RWM_MAP 0
+#define RWM_REMAP 1
+char *
+rwm_map_filter(
+ struct ldapmap *at_map,
+ struct ldapmap *oc_map,
+ struct berval *f );
+
+#if 0 /* unused! */
+int
+rwm_map_attrs(
+ struct ldapmap *at_map,
+ AttributeName *a,
+ int remap,
+ char ***mapped_attrs );
+#endif
+
+int
+rwm_map_attrnames(
+ Operation *op,
+ struct ldapmap *at_map,
+ struct ldapmap *oc_map,
+ AttributeName *an,
+ AttributeName **anp,
+ int remap );
+
+extern void rwm_mapping_dst_free ( void *mapping );
+
+extern void rwm_mapping_free ( void *mapping );
+
+extern int rwm_map_config(
+ struct ldapmap *oc_map,
+ struct ldapmap *at_map,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv );
+
+extern int
+rwm_filter_map_rewrite(
+ Operation *op,
+ dncookie *dc,
+ Filter *f,
+ struct berval *fstr );
+
+/* suffix massaging by means of librewrite */
+extern int
+rwm_suffix_massage_config(
+ struct rewrite_info *info,
+ struct berval *pvnc,
+ struct berval *nvnc,
+ struct berval *prnc,
+ struct berval *nrnc);
+extern int
+rwm_dnattr_rewrite(
+ Operation *op,
+ SlapReply *rs,
+ void *cookie,
+ BerVarray a_vals,
+ BerVarray *pa_nvals );
+extern int
+rwm_referral_rewrite(
+ Operation *op,
+ SlapReply *rs,
+ void *cookie,
+ BerVarray a_vals,
+ BerVarray *pa_nvals );
+extern int rwm_dnattr_result_rewrite( dncookie *dc, BerVarray a_vals, BerVarray a_nvals );
+extern int rwm_referral_result_rewrite( dncookie *dc, BerVarray a_vals );
+
+LDAP_END_DECL
+
+#endif /* RWM_H */
diff --git a/servers/slapd/overlays/rwmconf.c b/servers/slapd/overlays/rwmconf.c
new file mode 100644
index 0000000..a1a9f36
--- /dev/null
+++ b/servers/slapd/overlays/rwmconf.c
@@ -0,0 +1,413 @@
+/* rwmconf.c - rewrite/map configuration file routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_RWM
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "rwm.h"
+#include "lutil.h"
+
+int
+rwm_map_config(
+ struct ldapmap *oc_map,
+ struct ldapmap *at_map,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv )
+{
+ struct ldapmap *map;
+ struct ldapmapping *mapping;
+ char *src, *dst;
+ int is_oc = 0;
+ int rc = 0;
+
+ if ( argc < 3 || argc > 4 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: syntax is \"map {objectclass | attribute} [<local> | *] {<foreign> | *}\"\n",
+ fname, lineno );
+ return 1;
+ }
+
+ if ( strcasecmp( argv[1], "objectclass" ) == 0 ) {
+ map = oc_map;
+ is_oc = 1;
+
+ } else if ( strcasecmp( argv[1], "attribute" ) == 0 ) {
+ map = at_map;
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: syntax is "
+ "\"map {objectclass | attribute} [<local> | *] "
+ "{<foreign> | *}\"\n",
+ fname, lineno );
+ return 1;
+ }
+
+ if ( !is_oc && map->map == NULL ) {
+ /* only init if required */
+ if ( rwm_map_init( map, &mapping ) != LDAP_SUCCESS ) {
+ return 1;
+ }
+ }
+
+ if ( strcmp( argv[2], "*" ) == 0 ) {
+ if ( argc < 4 || strcmp( argv[3], "*" ) == 0 ) {
+ map->drop_missing = ( argc < 4 );
+ goto success_return;
+ }
+ src = dst = argv[3];
+
+ } else if ( argc < 4 ) {
+ src = "";
+ dst = argv[2];
+
+ } else {
+ src = argv[2];
+ dst = ( strcmp( argv[3], "*" ) == 0 ? src : argv[3] );
+ }
+
+ if ( ( map == at_map )
+ && ( strcasecmp( src, "objectclass" ) == 0
+ || strcasecmp( dst, "objectclass" ) == 0 ) )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: objectclass attribute cannot be mapped\n",
+ fname, lineno );
+ return 1;
+ }
+
+ mapping = (struct ldapmapping *)ch_calloc( 2,
+ sizeof(struct ldapmapping) );
+ if ( mapping == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: out of memory\n",
+ fname, lineno );
+ return 1;
+ }
+ ber_str2bv( src, 0, 1, &mapping[0].m_src );
+ ber_str2bv( dst, 0, 1, &mapping[0].m_dst );
+ mapping[1].m_src = mapping[0].m_dst;
+ mapping[1].m_dst = mapping[0].m_src;
+
+ mapping[0].m_flags = RWMMAP_F_NONE;
+ mapping[1].m_flags = RWMMAP_F_NONE;
+
+ /*
+ * schema check
+ */
+ if ( is_oc ) {
+ if ( src[0] != '\0' ) {
+ mapping[0].m_src_oc = oc_bvfind( &mapping[0].m_src );
+ if ( mapping[0].m_src_oc == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: warning, source objectClass '%s' "
+ "should be defined in schema\n",
+ fname, lineno, src );
+
+ /*
+ * FIXME: this should become an err
+ */
+ mapping[0].m_src_oc = ch_malloc( sizeof( ObjectClass ) );
+ memset( mapping[0].m_src_oc, 0, sizeof( ObjectClass ) );
+ mapping[0].m_src_oc->soc_cname = mapping[0].m_src;
+ mapping[0].m_flags |= RWMMAP_F_FREE_SRC;
+ }
+ mapping[1].m_dst_oc = mapping[0].m_src_oc;
+ }
+
+ mapping[0].m_dst_oc = oc_bvfind( &mapping[0].m_dst );
+ if ( mapping[0].m_dst_oc == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: warning, destination objectClass '%s' "
+ "is not defined in schema\n",
+ fname, lineno, dst );
+
+ mapping[0].m_dst_oc = oc_bvfind_undef( &mapping[0].m_dst );
+ if ( mapping[0].m_dst_oc == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "%s: line %d: unable to mimic destination objectClass '%s'\n",
+ fname, lineno, dst );
+ goto error_return;
+ }
+ }
+ mapping[1].m_src_oc = mapping[0].m_dst_oc;
+
+ mapping[0].m_flags |= RWMMAP_F_IS_OC;
+ mapping[1].m_flags |= RWMMAP_F_IS_OC;
+
+ } else {
+ int rc;
+ const char *text = NULL;
+
+ if ( src[0] != '\0' ) {
+ rc = slap_bv2ad( &mapping[0].m_src,
+ &mapping[0].m_src_ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: warning, source attributeType '%s' "
+ "should be defined in schema\n",
+ fname, lineno, src );
+
+ /*
+ * we create a fake "proxied" ad
+ * and add it here.
+ */
+
+ rc = slap_bv2undef_ad( &mapping[0].m_src,
+ &mapping[0].m_src_ad, &text,
+ SLAP_AD_PROXIED );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ANY,
+ "%s: line %d: source attributeType '%s': %d (%s)\n",
+ fname, lineno, src, rc,
+ text ? text : "null" );
+ goto error_return;
+ }
+
+ }
+ mapping[1].m_dst_ad = mapping[0].m_src_ad;
+ }
+
+ rc = slap_bv2ad( &mapping[0].m_dst, &mapping[0].m_dst_ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: warning, destination attributeType '%s' "
+ "is not defined in schema\n",
+ fname, lineno, dst );
+
+ rc = slap_bv2undef_ad( &mapping[0].m_dst,
+ &mapping[0].m_dst_ad, &text,
+ SLAP_AD_PROXIED );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ANY,
+ "%s: line %d: destination attributeType '%s': %d (%s)\n",
+ fname, lineno, dst, rc,
+ text ? text : "null" );
+ goto error_return;
+ }
+ }
+ mapping[1].m_src_ad = mapping[0].m_dst_ad;
+ }
+
+ if ( ( src[0] != '\0' && ldap_avl_find( map->map, (caddr_t)mapping, rwm_mapping_cmp ) != NULL)
+ || ldap_avl_find( map->remap, (caddr_t)&mapping[1], rwm_mapping_cmp ) != NULL)
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: duplicate mapping found.\n",
+ fname, lineno );
+ /* FIXME: free stuff */
+ goto error_return;
+ }
+
+ if ( src[0] != '\0' ) {
+ ldap_avl_insert( &map->map, (caddr_t)&mapping[0],
+ rwm_mapping_cmp, rwm_mapping_dup );
+ }
+ ldap_avl_insert( &map->remap, (caddr_t)&mapping[1],
+ rwm_mapping_cmp, rwm_mapping_dup );
+
+success_return:;
+ return rc;
+
+error_return:;
+ if ( mapping ) {
+ rwm_mapping_free( mapping );
+ }
+
+ return 1;
+}
+
+static char *
+rwm_suffix_massage_regexize( const char *s )
+{
+ char *res, *ptr;
+ const char *p, *r;
+ int i;
+
+ if ( s[0] == '\0' ) {
+ return ch_strdup( "^(.+)$" );
+ }
+
+ for ( i = 0, p = s;
+ ( r = strchr( p, ',' ) ) != NULL;
+ p = r + 1, i++ )
+ ;
+
+ res = ch_calloc( sizeof( char ), strlen( s )
+ + STRLENOF( "((.+),)?" )
+ + STRLENOF( "[ ]?" ) * i
+ + STRLENOF( "$" ) + 1 );
+
+ ptr = lutil_strcopy( res, "((.+),)?" );
+ for ( i = 0, p = s;
+ ( r = strchr( p, ',' ) ) != NULL;
+ p = r + 1 , i++ ) {
+ ptr = lutil_strncopy( ptr, p, r - p + 1 );
+ ptr = lutil_strcopy( ptr, "[ ]?" );
+
+ if ( r[ 1 ] == ' ' ) {
+ r++;
+ }
+ }
+ ptr = lutil_strcopy( ptr, p );
+ ptr[0] = '$';
+ ptr[1] = '\0';
+
+ return res;
+}
+
+static char *
+rwm_suffix_massage_patternize( const char *s, const char *p )
+{
+ ber_len_t len;
+ char *res, *ptr;
+
+ len = strlen( p );
+
+ if ( s[ 0 ] == '\0' ) {
+ len++;
+ }
+
+ res = ch_calloc( sizeof( char ), len + STRLENOF( "%1" ) + 1 );
+ if ( res == NULL ) {
+ return NULL;
+ }
+
+ ptr = lutil_strcopy( res, ( p[0] == '\0' ? "%2" : "%1" ) );
+ if ( s[ 0 ] == '\0' ) {
+ ptr[ 0 ] = ',';
+ ptr++;
+ }
+ lutil_strcopy( ptr, p );
+
+ return res;
+}
+
+int
+rwm_suffix_massage_config(
+ struct rewrite_info *info,
+ struct berval *pvnc,
+ struct berval *nvnc,
+ struct berval *prnc,
+ struct berval *nrnc
+)
+{
+ char *rargv[ 5 ];
+ int line = 0;
+
+ rargv[ 0 ] = "rewriteEngine";
+ rargv[ 1 ] = "on";
+ rargv[ 2 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
+
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "default";
+ rargv[ 2 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
+
+ rargv[ 0 ] = "rewriteRule";
+ rargv[ 1 ] = rwm_suffix_massage_regexize( pvnc->bv_val );
+ rargv[ 2 ] = rwm_suffix_massage_patternize( pvnc->bv_val, prnc->bv_val );
+ rargv[ 3 ] = ":";
+ rargv[ 4 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
+ ch_free( rargv[ 1 ] );
+ ch_free( rargv[ 2 ] );
+
+ if ( BER_BVISEMPTY( pvnc ) ) {
+ rargv[ 0 ] = "rewriteRule";
+ rargv[ 1 ] = "^$";
+ rargv[ 2 ] = prnc->bv_val;
+ rargv[ 3 ] = ":";
+ rargv[ 4 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
+ }
+
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "searchEntryDN";
+ rargv[ 2 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
+
+ rargv[ 0 ] = "rewriteRule";
+ rargv[ 1 ] = rwm_suffix_massage_regexize( prnc->bv_val );
+ rargv[ 2 ] = rwm_suffix_massage_patternize( prnc->bv_val, pvnc->bv_val );
+ rargv[ 3 ] = ":";
+ rargv[ 4 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
+ ch_free( rargv[ 1 ] );
+ ch_free( rargv[ 2 ] );
+
+ if ( BER_BVISEMPTY( prnc ) ) {
+ rargv[ 0 ] = "rewriteRule";
+ rargv[ 1 ] = "^$";
+ rargv[ 2 ] = pvnc->bv_val;
+ rargv[ 3 ] = ":";
+ rargv[ 4 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
+ }
+
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "matchedDN";
+ rargv[ 2 ] = "alias";
+ rargv[ 3 ] = "searchEntryDN";
+ rargv[ 4 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
+
+#ifdef RWM_REFERRAL_REWRITE
+ /* FIXME: we don't want this on by default, do we? */
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "referralDN";
+ rargv[ 2 ] = "alias";
+ rargv[ 3 ] = "searchEntryDN";
+ rargv[ 4 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
+#else /* ! RWM_REFERRAL_REWRITE */
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "referralAttrDN";
+ rargv[ 2 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
+
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "referralDN";
+ rargv[ 2 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 2, rargv );
+#endif /* ! RWM_REFERRAL_REWRITE */
+
+ rargv[ 0 ] = "rewriteContext";
+ rargv[ 1 ] = "searchAttrDN";
+ rargv[ 2 ] = "alias";
+ rargv[ 3 ] = "searchEntryDN";
+ rargv[ 4 ] = NULL;
+ rewrite_parse( info, "<suffix massage>", ++line, 4, rargv );
+
+ return 0;
+}
+
+#endif /* SLAPD_OVER_RWM */
diff --git a/servers/slapd/overlays/rwmdn.c b/servers/slapd/overlays/rwmdn.c
new file mode 100644
index 0000000..c67e3cf
--- /dev/null
+++ b/servers/slapd/overlays/rwmdn.c
@@ -0,0 +1,215 @@
+/* rwmdn.c - massages dns */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_RWM
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "rwm.h"
+
+/* FIXME: after rewriting, we should also remap attributes ... */
+
+/*
+ * massages "in" and normalizes it into "ndn"
+ *
+ * "ndn" may be untouched if no massaging occurred and its value was not null
+ */
+int
+rwm_dn_massage_normalize(
+ dncookie *dc,
+ struct berval *in,
+ struct berval *ndn )
+{
+ int rc;
+ struct berval mdn = BER_BVNULL;
+
+ /* massage and normalize a DN */
+ rc = rwm_dn_massage( dc, in, &mdn );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( mdn.bv_val == in->bv_val && !BER_BVISNULL( ndn ) ) {
+ return rc;
+ }
+
+ rc = dnNormalize( 0, NULL, NULL, &mdn, ndn, NULL );
+
+ if ( mdn.bv_val != in->bv_val ) {
+ ch_free( mdn.bv_val );
+ }
+
+ return rc;
+}
+
+/*
+ * massages "in" and prettifies it into "pdn"
+ *
+ * "pdn" may be untouched if no massaging occurred and its value was not null
+ */
+int
+rwm_dn_massage_pretty(
+ dncookie *dc,
+ struct berval *in,
+ struct berval *pdn )
+{
+ int rc;
+ struct berval mdn = BER_BVNULL;
+
+ /* massage and pretty a DN */
+ rc = rwm_dn_massage( dc, in, &mdn );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( mdn.bv_val == in->bv_val && !BER_BVISNULL( pdn ) ) {
+ return rc;
+ }
+
+ rc = dnPretty( NULL, &mdn, pdn, NULL );
+
+ if ( mdn.bv_val != in->bv_val ) {
+ ch_free( mdn.bv_val );
+ }
+
+ return rc;
+}
+
+/*
+ * massages "in" and prettifies and normalizes it into "pdn" and "ndn"
+ *
+ * "pdn" may be untouched if no massaging occurred and its value was not null;
+ * "ndn" may be untouched if no massaging occurred and its value was not null;
+ * if no massage occurred and "ndn" value was not null, it is filled
+ * with the normalized value of "pdn", much like ndn = dnNormalize( pdn )
+ */
+int
+rwm_dn_massage_pretty_normalize(
+ dncookie *dc,
+ struct berval *in,
+ struct berval *pdn,
+ struct berval *ndn )
+{
+ int rc;
+ struct berval mdn = BER_BVNULL;
+
+ /* massage, pretty and normalize a DN */
+ rc = rwm_dn_massage( dc, in, &mdn );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( mdn.bv_val == in->bv_val && !BER_BVISNULL( pdn ) ) {
+ if ( BER_BVISNULL( ndn ) ) {
+ rc = dnNormalize( 0, NULL, NULL, &mdn, ndn, NULL );
+ }
+ return rc;
+ }
+
+ rc = dnPrettyNormal( NULL, &mdn, pdn, ndn, NULL );
+
+ if ( mdn.bv_val != in->bv_val ) {
+ ch_free( mdn.bv_val );
+ }
+
+ return rc;
+}
+
+/*
+ * massages "in" into "dn"
+ *
+ * "dn" may contain the value of "in" if no massage occurred
+ */
+int
+rwm_dn_massage(
+ dncookie *dc,
+ struct berval *in,
+ struct berval *dn
+)
+{
+ int rc = 0;
+ struct berval mdn;
+ static char *dmy = "";
+ char *in_val;
+
+ assert( dc != NULL );
+ assert( in != NULL );
+ assert( dn != NULL );
+
+ /* protect from NULL berval */
+ in_val = in->bv_val ? in->bv_val : dmy;
+
+ rc = rewrite_session( dc->rwmap->rwm_rw, dc->ctx,
+ in_val, dc->conn, &mdn.bv_val );
+ switch ( rc ) {
+ case REWRITE_REGEXEC_OK:
+ if ( !BER_BVISNULL( &mdn ) && mdn.bv_val != in_val ) {
+ mdn.bv_len = strlen( mdn.bv_val );
+ *dn = mdn;
+ } else {
+ dn->bv_len = in->bv_len;
+ dn->bv_val = in_val;
+ }
+ rc = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_ARGS,
+ "[rw] %s: \"%s\" -> \"%s\"\n",
+ dc->ctx, in_val, dn->bv_val );
+ break;
+
+ case REWRITE_REGEXEC_UNWILLING:
+ if ( dc->rs ) {
+ dc->rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ dc->rs->sr_text = "Operation not allowed";
+ }
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ break;
+
+ case REWRITE_REGEXEC_ERR:
+ if ( dc->rs ) {
+ dc->rs->sr_err = LDAP_OTHER;
+ dc->rs->sr_text = "Rewrite error";
+ }
+ rc = LDAP_OTHER;
+ break;
+ }
+
+ if ( mdn.bv_val == dmy ) {
+ BER_BVZERO( &mdn );
+ }
+
+ if ( dn->bv_val == dmy ) {
+ BER_BVZERO( dn );
+ }
+
+ return rc;
+}
+
+#endif /* SLAPD_OVER_RWM */
diff --git a/servers/slapd/overlays/rwmmap.c b/servers/slapd/overlays/rwmmap.c
new file mode 100644
index 0000000..74ffd05
--- /dev/null
+++ b/servers/slapd/overlays/rwmmap.c
@@ -0,0 +1,1347 @@
+/* rwmmap.c - rewrite/mapping routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Howard Chu.
+ * Portions Copyright 2000-2003 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by the Howard Chu for inclusion
+ * in OpenLDAP Software and subsequently enhanced by Pierangelo
+ * Masarati.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_RWM
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "rwm.h"
+
+#undef ldap_debug /* silence a warning in ldap-int.h */
+#include "../../../libraries/libldap/ldap-int.h"
+
+int
+rwm_mapping_cmp( const void *c1, const void *c2 )
+{
+ struct ldapmapping *map1 = (struct ldapmapping *)c1;
+ struct ldapmapping *map2 = (struct ldapmapping *)c2;
+ int rc = map1->m_src.bv_len - map2->m_src.bv_len;
+
+ if ( rc ) {
+ return rc;
+ }
+
+ return strcasecmp( map1->m_src.bv_val, map2->m_src.bv_val );
+}
+
+int
+rwm_mapping_dup( void *c1, void *c2 )
+{
+ struct ldapmapping *map1 = (struct ldapmapping *)c1;
+ struct ldapmapping *map2 = (struct ldapmapping *)c2;
+ int rc = map1->m_src.bv_len - map2->m_src.bv_len;
+
+ if ( rc ) {
+ return 0;
+ }
+
+ return ( ( strcasecmp( map1->m_src.bv_val, map2->m_src.bv_val ) == 0 ) ? -1 : 0 );
+}
+
+int
+rwm_map_init( struct ldapmap *lm, struct ldapmapping **m )
+{
+ struct ldapmapping *mapping;
+ const char *text;
+ int rc;
+
+ assert( m != NULL );
+
+ *m = NULL;
+
+ mapping = (struct ldapmapping *)ch_calloc( 2,
+ sizeof( struct ldapmapping ) );
+ if ( mapping == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+
+ /* NOTE: this is needed to make sure that
+ * rwm-map attribute *
+ * does not filter out all attributes including objectClass */
+ rc = slap_str2ad( "objectClass", &mapping[0].m_src_ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ ch_free( mapping );
+ return rc;
+ }
+
+ mapping[0].m_dst_ad = mapping[0].m_src_ad;
+ ber_dupbv( &mapping[0].m_src, &mapping[0].m_src_ad->ad_cname );
+ ber_dupbv( &mapping[0].m_dst, &mapping[0].m_src );
+
+ mapping[1].m_src = mapping[0].m_src;
+ mapping[1].m_dst = mapping[0].m_dst;
+ mapping[1].m_src_ad = mapping[0].m_src_ad;
+ mapping[1].m_dst_ad = mapping[1].m_src_ad;
+
+ ldap_avl_insert( &lm->map, (caddr_t)&mapping[0],
+ rwm_mapping_cmp, rwm_mapping_dup );
+ ldap_avl_insert( &lm->remap, (caddr_t)&mapping[1],
+ rwm_mapping_cmp, rwm_mapping_dup );
+
+ *m = mapping;
+
+ return rc;
+}
+
+int
+rwm_mapping( struct ldapmap *map, struct berval *s, struct ldapmapping **m, int remap )
+{
+ Avlnode *tree;
+ struct ldapmapping fmapping;
+
+ if ( map == NULL ) {
+ return 0;
+ }
+
+ assert( m != NULL );
+
+ /* let special attrnames slip through (ITS#5760) */
+ if ( bvmatch( s, slap_bv_no_attrs )
+ || bvmatch( s, slap_bv_all_user_attrs )
+ || bvmatch( s, slap_bv_all_operational_attrs ) )
+ {
+ *m = NULL;
+ return 0;
+ }
+
+ if ( remap == RWM_REMAP ) {
+ tree = map->remap;
+
+ } else {
+ tree = map->map;
+ }
+
+ fmapping.m_src = *s;
+ *m = (struct ldapmapping *)ldap_avl_find( tree, (caddr_t)&fmapping,
+ rwm_mapping_cmp );
+
+ if ( *m == NULL ) {
+ return map->drop_missing;
+ }
+
+ return 0;
+}
+
+void
+rwm_map( struct ldapmap *map, struct berval *s, struct berval *bv, int remap )
+{
+ struct ldapmapping *mapping;
+
+ /* map->map may be NULL when mapping is configured,
+ * but map->remap can't */
+ if ( map->remap == NULL ) {
+ *bv = *s;
+ return;
+ }
+
+ BER_BVZERO( bv );
+ ( void )rwm_mapping( map, s, &mapping, remap );
+ if ( mapping != NULL ) {
+ if ( !BER_BVISNULL( &mapping->m_dst ) ) {
+ *bv = mapping->m_dst;
+ }
+ return;
+ }
+
+ if ( !map->drop_missing ) {
+ *bv = *s;
+ }
+}
+
+/*
+ * Map attribute names in place
+ */
+int
+rwm_map_attrnames(
+ Operation *op,
+ struct ldapmap *at_map,
+ struct ldapmap *oc_map,
+ AttributeName *an,
+ AttributeName **anp,
+ int remap )
+{
+ int i, j, x;
+
+ assert( anp != NULL );
+
+ *anp = NULL;
+
+ if ( an == NULL && op->o_bd->be_extra_anlist == NULL ) {
+ return LDAP_SUCCESS;
+ }
+
+ i = 0;
+ if ( an != NULL ) {
+ for ( i = 0; !BER_BVISNULL( &an[i].an_name ); i++ )
+ /* just count */ ;
+ }
+
+ x = 0;
+ if ( op->o_bd->be_extra_anlist ) {
+ for ( ; !BER_BVISNULL( &op->o_bd->be_extra_anlist[x].an_name ); x++ )
+ /* just count */ ;
+ }
+
+ assert( i > 0 || x > 0 );
+ *anp = op->o_tmpcalloc( ( i + x + 1 ), sizeof( AttributeName ),
+ op->o_tmpmemctx );
+ if ( *anp == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+
+ j = 0;
+ if ( an != NULL ) {
+ for ( i = 0; !BER_BVISNULL( &an[i].an_name ); i++ ) {
+ struct ldapmapping *m;
+ int at_drop_missing = 0,
+ oc_drop_missing = 0;
+
+ if ( an[i].an_desc ) {
+ if ( !at_map ) {
+ /* FIXME: better leave as is? */
+ continue;
+ }
+
+ at_drop_missing = rwm_mapping( at_map, &an[i].an_name, &m, remap );
+ if ( at_drop_missing || ( m && BER_BVISNULL( &m->m_dst ) ) ) {
+ continue;
+ }
+
+ if ( !m ) {
+ (*anp)[j] = an[i];
+ j++;
+ continue;
+ }
+
+ (*anp)[j] = an[i];
+ if ( remap == RWM_MAP ) {
+ (*anp)[j].an_name = m->m_dst;
+ (*anp)[j].an_desc = m->m_dst_ad;
+ } else {
+ (*anp)[j].an_name = m->m_src;
+ (*anp)[j].an_desc = m->m_src_ad;
+
+ }
+
+ j++;
+ continue;
+
+ } else if ( an[i].an_oc ) {
+ if ( !oc_map ) {
+ /* FIXME: better leave as is? */
+ continue;
+ }
+
+ oc_drop_missing = rwm_mapping( oc_map, &an[i].an_name, &m, remap );
+
+ if ( oc_drop_missing || ( m && BER_BVISNULL( &m->m_dst ) ) ) {
+ continue;
+ }
+
+ if ( !m ) {
+ (*anp)[j] = an[i];
+ j++;
+ continue;
+ }
+
+ (*anp)[j] = an[i];
+ if ( remap == RWM_MAP ) {
+ (*anp)[j].an_name = m->m_dst;
+ (*anp)[j].an_oc = m->m_dst_oc;
+ } else {
+ (*anp)[j].an_name = m->m_src;
+ (*anp)[j].an_oc = m->m_src_oc;
+ }
+
+ } else {
+ at_drop_missing = rwm_mapping( at_map, &an[i].an_name, &m, remap );
+
+ if ( at_drop_missing || !m ) {
+ oc_drop_missing = rwm_mapping( oc_map, &an[i].an_name, &m, remap );
+
+ /* if both at_map and oc_map required to drop missing,
+ * then do it */
+ if ( oc_drop_missing && at_drop_missing ) {
+ continue;
+ }
+
+ /* if no oc_map mapping was found and at_map required
+ * to drop missing, then do it; otherwise, at_map wins
+ * and an is considered an attr and is left unchanged */
+ if ( !m ) {
+ if ( at_drop_missing ) {
+ continue;
+ }
+ (*anp)[j] = an[i];
+ j++;
+ continue;
+ }
+
+ if ( BER_BVISNULL( &m->m_dst ) ) {
+ continue;
+ }
+
+ (*anp)[j] = an[i];
+ if ( remap == RWM_MAP ) {
+ (*anp)[j].an_name = m->m_dst;
+ (*anp)[j].an_oc = m->m_dst_oc;
+ } else {
+ (*anp)[j].an_name = m->m_src;
+ (*anp)[j].an_oc = m->m_src_oc;
+ }
+ j++;
+ continue;
+ }
+
+ if ( !BER_BVISNULL( &m->m_dst ) ) {
+ (*anp)[j] = an[i];
+ if ( remap == RWM_MAP ) {
+ (*anp)[j].an_name = m->m_dst;
+ (*anp)[j].an_desc = m->m_dst_ad;
+ } else {
+ (*anp)[j].an_name = m->m_src;
+ (*anp)[j].an_desc = m->m_src_ad;
+ }
+ j++;
+ continue;
+ }
+ }
+ }
+ }
+
+ if ( op->o_bd->be_extra_anlist != NULL ) {
+ /* we assume be_extra_anlist are already mapped */
+ for ( x = 0; !BER_BVISNULL( &op->o_bd->be_extra_anlist[x].an_name ); x++ ) {
+ BER_BVZERO( &(*anp)[j].an_name );
+ if ( op->o_bd->be_extra_anlist[x].an_desc &&
+ ad_inlist( op->o_bd->be_extra_anlist[x].an_desc, *anp ) )
+ {
+ continue;
+ }
+
+ (*anp)[j] = op->o_bd->be_extra_anlist[x];
+ j++;
+ }
+ }
+
+ if ( j == 0 && ( i != 0 || x != 0 ) ) {
+ memset( &(*anp)[0], 0, sizeof( AttributeName ) );
+ (*anp)[0].an_name = *slap_bv_no_attrs;
+ j = 1;
+ }
+ memset( &(*anp)[j], 0, sizeof( AttributeName ) );
+
+ return LDAP_SUCCESS;
+}
+
+#if 0 /* unused! */
+int
+rwm_map_attrs(
+ struct ldapmap *at_map,
+ AttributeName *an,
+ int remap,
+ char ***mapped_attrs )
+{
+ int i, j;
+ char **na;
+
+ if ( an == NULL ) {
+ *mapped_attrs = NULL;
+ return LDAP_SUCCESS;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &an[ i ].an_name ); i++ )
+ /* count'em */ ;
+
+ na = (char **)ch_calloc( i + 1, sizeof( char * ) );
+ if ( na == NULL ) {
+ *mapped_attrs = NULL;
+ return LDAP_NO_MEMORY;
+ }
+
+ for ( i = j = 0; !BER_BVISNULL( &an[i].an_name ); i++ ) {
+ struct ldapmapping *mapping;
+
+ if ( rwm_mapping( at_map, &an[i].an_name, &mapping, remap ) ) {
+ continue;
+ }
+
+ if ( !mapping ) {
+ na[ j++ ] = an[ i ].an_name.bv_val;
+
+ } else if ( !BER_BVISNULL( &mapping->m_dst ) ) {
+ na[ j++ ] = mapping->m_dst.bv_val;
+ }
+ }
+
+ if ( j == 0 && i != 0 ) {
+ na[ j++ ] = LDAP_NO_ATTRS;
+ }
+
+ na[ j ] = NULL;
+
+ *mapped_attrs = na;
+
+ return LDAP_SUCCESS;
+}
+#endif
+
+static int
+map_attr_value(
+ dncookie *dc,
+ AttributeDescription **adp,
+ struct berval *mapped_attr,
+ struct berval *value,
+ struct berval *mapped_value,
+ int remap,
+ void *memctx )
+{
+ struct berval vtmp = BER_BVNULL;
+ int freeval = 0;
+ AttributeDescription *ad = *adp;
+ struct ldapmapping *mapping = NULL;
+
+ rwm_mapping( &dc->rwmap->rwm_at, &ad->ad_cname, &mapping, remap );
+ if ( mapping == NULL ) {
+ if ( dc->rwmap->rwm_at.drop_missing ) {
+ return -1;
+ }
+
+ *mapped_attr = ad->ad_cname;
+
+ } else {
+ *mapped_attr = mapping->m_dst;
+ }
+
+ if ( value != NULL ) {
+ assert( mapped_value != NULL );
+
+ if ( ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName
+ || ( mapping != NULL && mapping->m_dst_ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) )
+ {
+ dncookie fdc = *dc;
+ int rc;
+
+ fdc.ctx = "searchFilterAttrDN";
+
+ vtmp = *value;
+ rc = rwm_dn_massage_normalize( &fdc, value, &vtmp );
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ if ( vtmp.bv_val != value->bv_val ) {
+ freeval = 1;
+ }
+ break;
+
+ case LDAP_UNWILLING_TO_PERFORM:
+ case LDAP_OTHER:
+ default:
+ return -1;
+ }
+
+ } else if ( ad->ad_type->sat_equality &&
+ ( ad->ad_type->sat_equality->smr_usage & SLAP_MR_MUTATION_NORMALIZER ) )
+ {
+ if ( ad->ad_type->sat_equality->smr_normalize(
+ (SLAP_MR_DENORMALIZE|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX),
+ NULL, NULL, value, &vtmp, memctx ) )
+ {
+ return -1;
+ }
+ freeval = 2;
+
+ } else if ( ad == slap_schema.si_ad_objectClass
+ || ad == slap_schema.si_ad_structuralObjectClass )
+ {
+ rwm_map( &dc->rwmap->rwm_oc, value, &vtmp, remap );
+ if ( BER_BVISNULL( &vtmp ) || BER_BVISEMPTY( &vtmp ) ) {
+ vtmp = *value;
+ }
+
+ } else {
+ vtmp = *value;
+ }
+
+ filter_escape_value_x( &vtmp, mapped_value, memctx );
+
+ switch ( freeval ) {
+ case 1:
+ ch_free( vtmp.bv_val );
+ break;
+
+ case 2:
+ ber_memfree_x( vtmp.bv_val, memctx );
+ break;
+ }
+ }
+
+ if ( mapping != NULL ) {
+ assert( mapping->m_dst_ad != NULL );
+ *adp = mapping->m_dst_ad;
+ }
+
+ return 0;
+}
+
+static int
+rwm_int_filter_map_rewrite(
+ Operation *op,
+ dncookie *dc,
+ Filter *f,
+ struct berval *fstr )
+{
+ int i;
+ Filter *p, ftmp;
+ AttributeDescription *ad;
+ struct berval atmp,
+ vtmp,
+ *tmp;
+ static struct berval
+ /* better than nothing... */
+ ber_bvfalse = BER_BVC( "(!(objectClass=*))" ),
+ ber_bvtf_false = BER_BVC( "(|)" ),
+ /* better than nothing... */
+ ber_bvtrue = BER_BVC( "(objectClass=*)" ),
+ ber_bvtf_true = BER_BVC( "(&)" ),
+#if 0
+ /* no longer needed; preserved for completeness */
+ ber_bvundefined = BER_BVC( "(?=undefined)" ),
+#endif
+ ber_bverror = BER_BVC( "(?=error)" ),
+ ber_bvunknown = BER_BVC( "(?=unknown)" ),
+ ber_bvnone = BER_BVC( "(?=none)" );
+ ber_len_t len;
+
+ assert( fstr != NULL );
+ BER_BVZERO( fstr );
+
+ if ( f == NULL ) {
+ ber_dupbv_x( fstr, &ber_bvnone, op->o_tmpmemctx );
+ return LDAP_OTHER;
+ }
+
+#if 0
+ /* ITS#6814: give the caller a chance to use undefined filters */
+ if ( f->f_choice & SLAPD_FILTER_UNDEFINED ) {
+ goto computed;
+ }
+#endif
+
+ switch ( f->f_choice & SLAPD_FILTER_MASK ) {
+ case LDAP_FILTER_EQUALITY:
+ ad = f->f_av_desc;
+ if ( map_attr_value( dc, &ad, &atmp,
+ &f->f_av_value, &vtmp, RWM_MAP, op->o_tmpmemctx ) )
+ {
+ goto computed;
+ }
+
+ fstr->bv_len = atmp.bv_len + vtmp.bv_len + STRLENOF( "(=)" );
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=%s)",
+ atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
+
+ op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
+ break;
+
+ case LDAP_FILTER_GE:
+ ad = f->f_av_desc;
+ if ( map_attr_value( dc, &ad, &atmp,
+ &f->f_av_value, &vtmp, RWM_MAP, op->o_tmpmemctx ) )
+ {
+ goto computed;
+ }
+
+ fstr->bv_len = atmp.bv_len + vtmp.bv_len + STRLENOF( "(>=)" );
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s>=%s)",
+ atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
+
+ op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
+ break;
+
+ case LDAP_FILTER_LE:
+ ad = f->f_av_desc;
+ if ( map_attr_value( dc, &ad, &atmp,
+ &f->f_av_value, &vtmp, RWM_MAP, op->o_tmpmemctx ) )
+ {
+ goto computed;
+ }
+
+ fstr->bv_len = atmp.bv_len + vtmp.bv_len + STRLENOF( "(<=)" );
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s<=%s)",
+ atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
+
+ op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
+ break;
+
+ case LDAP_FILTER_APPROX:
+ ad = f->f_av_desc;
+ if ( map_attr_value( dc, &ad, &atmp,
+ &f->f_av_value, &vtmp, RWM_MAP, op->o_tmpmemctx ) )
+ {
+ goto computed;
+ }
+
+ fstr->bv_len = atmp.bv_len + vtmp.bv_len + STRLENOF( "(~=)" );
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s~=%s)",
+ atmp.bv_val, vtmp.bv_len ? vtmp.bv_val : "" );
+
+ op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ ad = f->f_sub_desc;
+ if ( map_attr_value( dc, &ad, &atmp,
+ NULL, NULL, RWM_MAP, op->o_tmpmemctx ) )
+ {
+ goto computed;
+ }
+
+ /* cannot be a DN ... */
+
+ fstr->bv_len = atmp.bv_len + STRLENOF( "(=*)" );
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 128, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
+ atmp.bv_val );
+
+ if ( !BER_BVISNULL( &f->f_sub_initial ) ) {
+ len = fstr->bv_len;
+
+ filter_escape_value_x( &f->f_sub_initial, &vtmp, op->o_tmpmemctx );
+
+ fstr->bv_len += vtmp.bv_len;
+ fstr->bv_val = op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1,
+ op->o_tmpmemctx );
+
+ snprintf( &fstr->bv_val[len - 2], vtmp.bv_len + 3,
+ /* "(attr=" */ "%s*)",
+ vtmp.bv_len ? vtmp.bv_val : "" );
+
+ op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( f->f_sub_any != NULL ) {
+ for ( i = 0; !BER_BVISNULL( &f->f_sub_any[i] ); i++ ) {
+ len = fstr->bv_len;
+ filter_escape_value_x( &f->f_sub_any[i], &vtmp,
+ op->o_tmpmemctx );
+
+ fstr->bv_len += vtmp.bv_len + 1;
+ fstr->bv_val = op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1,
+ op->o_tmpmemctx );
+
+ snprintf( &fstr->bv_val[len - 1], vtmp.bv_len + 3,
+ /* "(attr=[init]*[any*]" */ "%s*)",
+ vtmp.bv_len ? vtmp.bv_val : "" );
+ op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
+ }
+ }
+
+ if ( !BER_BVISNULL( &f->f_sub_final ) ) {
+ len = fstr->bv_len;
+
+ filter_escape_value_x( &f->f_sub_final, &vtmp, op->o_tmpmemctx );
+
+ fstr->bv_len += vtmp.bv_len;
+ fstr->bv_val = op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1,
+ op->o_tmpmemctx );
+
+ snprintf( &fstr->bv_val[len - 1], vtmp.bv_len + 3,
+ /* "(attr=[init*][any*]" */ "%s)",
+ vtmp.bv_len ? vtmp.bv_val : "" );
+
+ op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
+ }
+
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ ad = f->f_desc;
+ if ( map_attr_value( dc, &ad, &atmp,
+ NULL, NULL, RWM_MAP, op->o_tmpmemctx ) )
+ {
+ goto computed;
+ }
+
+ fstr->bv_len = atmp.bv_len + STRLENOF( "(=*)" );
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s=*)",
+ atmp.bv_val );
+ break;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ fstr->bv_len = STRLENOF( "(%)" );
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 128, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%c)",
+ f->f_choice == LDAP_FILTER_AND ? '&' :
+ f->f_choice == LDAP_FILTER_OR ? '|' : '!' );
+
+ for ( p = f->f_list; p != NULL; p = p->f_next ) {
+ int rc;
+
+ len = fstr->bv_len;
+
+ rc = rwm_int_filter_map_rewrite( op, dc, p, &vtmp );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ fstr->bv_len += vtmp.bv_len;
+ fstr->bv_val = op->o_tmprealloc( fstr->bv_val, fstr->bv_len + 1,
+ op->o_tmpmemctx );
+
+ snprintf( &fstr->bv_val[len-1], vtmp.bv_len + 2,
+ /*"("*/ "%s)", vtmp.bv_len ? vtmp.bv_val : "" );
+
+ op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
+ }
+
+ break;
+
+ case LDAP_FILTER_EXT: {
+ if ( f->f_mr_desc ) {
+ ad = f->f_mr_desc;
+ if ( map_attr_value( dc, &ad, &atmp,
+ &f->f_mr_value, &vtmp, RWM_MAP, op->o_tmpmemctx ) )
+ {
+ goto computed;
+ }
+
+ } else {
+ BER_BVSTR( &atmp, "" );
+ filter_escape_value_x( &f->f_mr_value, &vtmp, op->o_tmpmemctx );
+ }
+
+
+ fstr->bv_len = atmp.bv_len +
+ ( f->f_mr_dnattrs ? STRLENOF( ":dn" ) : 0 ) +
+ ( f->f_mr_rule_text.bv_len ? f->f_mr_rule_text.bv_len + 1 : 0 ) +
+ vtmp.bv_len + STRLENOF( "(:=)" );
+ fstr->bv_val = op->o_tmpalloc( fstr->bv_len + 1, op->o_tmpmemctx );
+
+ snprintf( fstr->bv_val, fstr->bv_len + 1, "(%s%s%s%s:=%s)",
+ atmp.bv_val,
+ f->f_mr_dnattrs ? ":dn" : "",
+ !BER_BVISEMPTY( &f->f_mr_rule_text ) ? ":" : "",
+ !BER_BVISEMPTY( &f->f_mr_rule_text ) ? f->f_mr_rule_text.bv_val : "",
+ vtmp.bv_len ? vtmp.bv_val : "" );
+ op->o_tmpfree( vtmp.bv_val, op->o_tmpmemctx );
+ break;
+ }
+
+ case -1:
+computed:;
+ f = &ftmp;
+ f->f_choice = SLAPD_FILTER_COMPUTED;
+ f->f_result = SLAPD_COMPARE_UNDEFINED;
+ /* fallthru */
+
+ case SLAPD_FILTER_COMPUTED:
+ switch ( f->f_result ) {
+ case LDAP_COMPARE_FALSE:
+ /* FIXME: treat UNDEFINED as FALSE */
+ case SLAPD_COMPARE_UNDEFINED:
+ if ( dc->rwmap->rwm_flags & RWM_F_SUPPORT_T_F ) {
+ tmp = &ber_bvtf_false;
+ break;
+ }
+ tmp = &ber_bvfalse;
+ break;
+
+ case LDAP_COMPARE_TRUE:
+ if ( dc->rwmap->rwm_flags & RWM_F_SUPPORT_T_F ) {
+ tmp = &ber_bvtf_true;
+ break;
+ }
+ tmp = &ber_bvtrue;
+ break;
+
+ default:
+ tmp = &ber_bverror;
+ break;
+ }
+
+ ber_dupbv_x( fstr, tmp, op->o_tmpmemctx );
+ break;
+
+ default:
+ ber_dupbv_x( fstr, &ber_bvunknown, op->o_tmpmemctx );
+ break;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int
+rwm_filter_map_rewrite(
+ Operation *op,
+ dncookie *dc,
+ Filter *f,
+ struct berval *fstr )
+{
+ int rc;
+ dncookie fdc;
+ struct berval ftmp;
+
+ rc = rwm_int_filter_map_rewrite( op, dc, f, fstr );
+
+ if ( rc != 0 ) {
+ return rc;
+ }
+
+ fdc = *dc;
+ ftmp = *fstr;
+
+ fdc.ctx = "searchFilter";
+
+ switch ( rewrite_session( fdc.rwmap->rwm_rw, fdc.ctx,
+ ( !BER_BVISEMPTY( &ftmp ) ? ftmp.bv_val : "" ),
+ fdc.conn, &fstr->bv_val ) )
+ {
+ case REWRITE_REGEXEC_OK:
+ if ( !BER_BVISNULL( fstr ) ) {
+ fstr->bv_len = strlen( fstr->bv_val );
+
+ } else {
+ *fstr = ftmp;
+ }
+
+ Debug( LDAP_DEBUG_ARGS,
+ "[rw] %s: \"%s\" -> \"%s\"\n",
+ fdc.ctx, ftmp.bv_val, fstr->bv_val );
+ if ( fstr->bv_val != ftmp.bv_val ) {
+ ber_bvreplace_x( &ftmp, fstr, op->o_tmpmemctx );
+ ch_free( fstr->bv_val );
+ *fstr = ftmp;
+ }
+ rc = LDAP_SUCCESS;
+ break;
+
+ case REWRITE_REGEXEC_UNWILLING:
+ if ( fdc.rs ) {
+ fdc.rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ fdc.rs->sr_text = "Operation not allowed";
+ }
+ op->o_tmpfree( ftmp.bv_val, op->o_tmpmemctx );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ break;
+
+ case REWRITE_REGEXEC_ERR:
+ if ( fdc.rs ) {
+ fdc.rs->sr_err = LDAP_OTHER;
+ fdc.rs->sr_text = "Rewrite error";
+ }
+ op->o_tmpfree( ftmp.bv_val, op->o_tmpmemctx );
+ rc = LDAP_OTHER;
+ break;
+ }
+
+ return rc;
+}
+
+/*
+ * I don't like this much, but we need two different
+ * functions because different heap managers may be
+ * in use in back-ldap/meta to reduce the amount of
+ * calls to malloc routines, and some of the free()
+ * routines may be macros with args
+ */
+int
+rwm_referral_rewrite(
+ Operation *op,
+ SlapReply *rs,
+ void *cookie,
+ BerVarray a_vals,
+ BerVarray *pa_nvals )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ int i, last;
+
+ dncookie dc;
+ struct berval dn = BER_BVNULL,
+ ndn = BER_BVNULL;
+
+ assert( a_vals != NULL );
+
+ /*
+ * Rewrite the dn if needed
+ */
+ dc.rwmap = rwmap;
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+ dc.ctx = (char *)cookie;
+
+ for ( last = 0; !BER_BVISNULL( &a_vals[last] ); last++ )
+ ;
+ last--;
+
+ if ( pa_nvals != NULL ) {
+ if ( *pa_nvals == NULL ) {
+ *pa_nvals = ch_malloc( ( last + 2 ) * sizeof(struct berval) );
+ memset( *pa_nvals, 0, ( last + 2 ) * sizeof(struct berval) );
+ }
+ }
+
+ for ( i = 0; !BER_BVISNULL( &a_vals[i] ); i++ ) {
+ struct berval olddn = BER_BVNULL,
+ oldval;
+ int rc;
+ LDAPURLDesc *ludp;
+
+ oldval = a_vals[i];
+ rc = ldap_url_parse( oldval.bv_val, &ludp );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ /* leave attr untouched if massage failed */
+ if ( pa_nvals && BER_BVISNULL( &(*pa_nvals)[i] ) ) {
+ ber_dupbv( &(*pa_nvals)[i], &oldval );
+ }
+ continue;
+ }
+
+ /* FIXME: URLs like "ldap:///dc=suffix" if passed
+ * thru ldap_url_parse() and ldap_url_desc2str()
+ * get rewritten as "ldap:///dc=suffix??base";
+ * we don't want this to occur... */
+ if ( ludp->lud_scope == LDAP_SCOPE_BASE ) {
+ ludp->lud_scope = LDAP_SCOPE_DEFAULT;
+ }
+
+ ber_str2bv( ludp->lud_dn, 0, 0, &olddn );
+
+ dn = olddn;
+ if ( pa_nvals ) {
+ ndn = olddn;
+ rc = rwm_dn_massage_pretty_normalize( &dc, &olddn,
+ &dn, &ndn );
+ } else {
+ rc = rwm_dn_massage_pretty( &dc, &olddn, &dn );
+ }
+
+ switch ( rc ) {
+ case LDAP_UNWILLING_TO_PERFORM:
+ /*
+ * FIXME: need to check if it may be considered
+ * legal to trim values when adding/modifying;
+ * it should be when searching (e.g. ACLs).
+ */
+ ch_free( a_vals[i].bv_val );
+ if (last > i ) {
+ a_vals[i] = a_vals[last];
+ if ( pa_nvals ) {
+ (*pa_nvals)[i] = (*pa_nvals)[last];
+ }
+ }
+ BER_BVZERO( &a_vals[last] );
+ if ( pa_nvals ) {
+ BER_BVZERO( &(*pa_nvals)[last] );
+ }
+ last--;
+ break;
+
+ case LDAP_SUCCESS:
+ if ( !BER_BVISNULL( &dn ) && dn.bv_val != olddn.bv_val ) {
+ char *newurl;
+
+ ludp->lud_dn = dn.bv_val;
+ newurl = ldap_url_desc2str( ludp );
+ ludp->lud_dn = olddn.bv_val;
+ ch_free( dn.bv_val );
+ if ( newurl == NULL ) {
+ /* FIXME: leave attr untouched
+ * even if ldap_url_desc2str failed...
+ */
+ break;
+ }
+
+ ber_str2bv( newurl, 0, 1, &a_vals[i] );
+ ber_memfree( newurl );
+
+ if ( pa_nvals ) {
+ ludp->lud_dn = ndn.bv_val;
+ newurl = ldap_url_desc2str( ludp );
+ ludp->lud_dn = olddn.bv_val;
+ ch_free( ndn.bv_val );
+ if ( newurl == NULL ) {
+ /* FIXME: leave attr untouched
+ * even if ldap_url_desc2str failed...
+ */
+ ch_free( a_vals[i].bv_val );
+ a_vals[i] = oldval;
+ break;
+ }
+
+ if ( !BER_BVISNULL( &(*pa_nvals)[i] ) ) {
+ ch_free( (*pa_nvals)[i].bv_val );
+ }
+ ber_str2bv( newurl, 0, 1, &(*pa_nvals)[i] );
+ ber_memfree( newurl );
+ }
+
+ ch_free( oldval.bv_val );
+ ludp->lud_dn = olddn.bv_val;
+ }
+ break;
+
+ default:
+ /* leave attr untouched if massage failed */
+ if ( pa_nvals && BER_BVISNULL( &(*pa_nvals)[i] ) ) {
+ ber_dupbv( &(*pa_nvals)[i], &a_vals[i] );
+ }
+ break;
+ }
+ ldap_free_urldesc( ludp );
+ }
+
+ return 0;
+}
+
+/*
+ * I don't like this much, but we need two different
+ * functions because different heap managers may be
+ * in use in back-ldap/meta to reduce the amount of
+ * calls to malloc routines, and some of the free()
+ * routines may be macros with args
+ */
+int
+rwm_dnattr_rewrite(
+ Operation *op,
+ SlapReply *rs,
+ void *cookie,
+ BerVarray a_vals,
+ BerVarray *pa_nvals )
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct ldaprwmap *rwmap =
+ (struct ldaprwmap *)on->on_bi.bi_private;
+
+ int i, last;
+
+ dncookie dc;
+ struct berval dn = BER_BVNULL,
+ ndn = BER_BVNULL;
+ BerVarray in;
+
+ if ( a_vals ) {
+ in = a_vals;
+
+ } else {
+ if ( pa_nvals == NULL || *pa_nvals == NULL ) {
+ return LDAP_OTHER;
+ }
+ in = *pa_nvals;
+ }
+
+ /*
+ * Rewrite the dn if needed
+ */
+ dc.rwmap = rwmap;
+ dc.conn = op->o_conn;
+ dc.rs = rs;
+ dc.ctx = (char *)cookie;
+
+ for ( last = 0; !BER_BVISNULL( &in[last] ); last++ );
+ last--;
+ if ( pa_nvals != NULL ) {
+ if ( *pa_nvals == NULL ) {
+ *pa_nvals = ch_malloc( ( last + 2 ) * sizeof(struct berval) );
+ memset( *pa_nvals, 0, ( last + 2 ) * sizeof(struct berval) );
+ }
+ }
+
+ for ( i = 0; !BER_BVISNULL( &in[i] ); i++ ) {
+ int rc;
+
+ if ( a_vals ) {
+ dn = in[i];
+ if ( pa_nvals ) {
+ ndn = (*pa_nvals)[i];
+ rc = rwm_dn_massage_pretty_normalize( &dc, &in[i], &dn, &ndn );
+ } else {
+ rc = rwm_dn_massage_pretty( &dc, &in[i], &dn );
+ }
+ } else {
+ ndn = in[i];
+ rc = rwm_dn_massage_normalize( &dc, &in[i], &ndn );
+ }
+
+ switch ( rc ) {
+ case LDAP_UNWILLING_TO_PERFORM:
+ /*
+ * FIXME: need to check if it may be considered
+ * legal to trim values when adding/modifying;
+ * it should be when searching (e.g. ACLs).
+ */
+ ch_free( in[i].bv_val );
+ if (last > i ) {
+ in[i] = in[last];
+ if ( a_vals && pa_nvals ) {
+ (*pa_nvals)[i] = (*pa_nvals)[last];
+ }
+ }
+ BER_BVZERO( &in[last] );
+ if ( a_vals && pa_nvals ) {
+ BER_BVZERO( &(*pa_nvals)[last] );
+ }
+ last--;
+ break;
+
+ case LDAP_SUCCESS:
+ if ( a_vals ) {
+ if ( !BER_BVISNULL( &dn ) && dn.bv_val != a_vals[i].bv_val ) {
+ ch_free( a_vals[i].bv_val );
+ a_vals[i] = dn;
+
+ if ( pa_nvals ) {
+ if ( !BER_BVISNULL( &(*pa_nvals)[i] ) ) {
+ ch_free( (*pa_nvals)[i].bv_val );
+ }
+ (*pa_nvals)[i] = ndn;
+ }
+ }
+
+ } else {
+ if ( !BER_BVISNULL( &ndn ) && ndn.bv_val != (*pa_nvals)[i].bv_val ) {
+ ch_free( (*pa_nvals)[i].bv_val );
+ (*pa_nvals)[i] = ndn;
+ }
+ }
+ break;
+
+ default:
+ /* leave attr untouched if massage failed */
+ if ( a_vals && pa_nvals && BER_BVISNULL( &(*pa_nvals)[i] ) ) {
+ dnNormalize( 0, NULL, NULL, &a_vals[i], &(*pa_nvals)[i], NULL );
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int
+rwm_referral_result_rewrite(
+ dncookie *dc,
+ BerVarray a_vals )
+{
+ int i, last;
+
+ for ( last = 0; !BER_BVISNULL( &a_vals[last] ); last++ );
+ last--;
+
+ for ( i = 0; !BER_BVISNULL( &a_vals[i] ); i++ ) {
+ struct berval dn,
+ olddn = BER_BVNULL;
+ int rc;
+ LDAPURLDesc *ludp;
+
+ rc = ldap_url_parse( a_vals[i].bv_val, &ludp );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ /* leave attr untouched if massage failed */
+ continue;
+ }
+
+ /* FIXME: URLs like "ldap:///dc=suffix" if passed
+ * thru ldap_url_parse() and ldap_url_desc2str()
+ * get rewritten as "ldap:///dc=suffix??base";
+ * we don't want this to occur... */
+ if ( ludp->lud_scope == LDAP_SCOPE_BASE ) {
+ ludp->lud_scope = LDAP_SCOPE_DEFAULT;
+ }
+
+ ber_str2bv( ludp->lud_dn, 0, 0, &olddn );
+
+ dn = olddn;
+ rc = rwm_dn_massage_pretty( dc, &olddn, &dn );
+ switch ( rc ) {
+ case LDAP_UNWILLING_TO_PERFORM:
+ /*
+ * FIXME: need to check if it may be considered
+ * legal to trim values when adding/modifying;
+ * it should be when searching (e.g. ACLs).
+ */
+ ch_free( a_vals[i].bv_val );
+ if ( last > i ) {
+ a_vals[i] = a_vals[last];
+ }
+ BER_BVZERO( &a_vals[last] );
+ last--;
+ i--;
+ break;
+
+ default:
+ /* leave attr untouched if massage failed */
+ if ( !BER_BVISNULL( &dn ) && olddn.bv_val != dn.bv_val ) {
+ char *newurl;
+
+ ludp->lud_dn = dn.bv_val;
+ newurl = ldap_url_desc2str( ludp );
+ if ( newurl == NULL ) {
+ /* FIXME: leave attr untouched
+ * even if ldap_url_desc2str failed...
+ */
+ break;
+ }
+
+ ch_free( a_vals[i].bv_val );
+ ber_str2bv( newurl, 0, 1, &a_vals[i] );
+ ber_memfree( newurl );
+ ludp->lud_dn = olddn.bv_val;
+ }
+ break;
+ }
+
+ ldap_free_urldesc( ludp );
+ }
+
+ return 0;
+}
+
+int
+rwm_dnattr_result_rewrite(
+ dncookie *dc,
+ BerVarray a_vals,
+ BerVarray a_nvals )
+{
+ int i, last;
+
+ for ( last = 0; !BER_BVISNULL( &a_vals[last] ); last++ );
+ last--;
+
+ for ( i = 0; !BER_BVISNULL( &a_vals[i] ); i++ ) {
+ struct berval pdn, ndn = BER_BVNULL;
+ int rc;
+
+ pdn = a_vals[i];
+ rc = rwm_dn_massage_pretty_normalize( dc, &a_vals[i], &pdn, &ndn );
+ switch ( rc ) {
+ case LDAP_UNWILLING_TO_PERFORM:
+ /*
+ * FIXME: need to check if it may be considered
+ * legal to trim values when adding/modifying;
+ * it should be when searching (e.g. ACLs).
+ */
+ assert( a_vals[i].bv_val != a_nvals[i].bv_val );
+ ch_free( a_vals[i].bv_val );
+ ch_free( a_nvals[i].bv_val );
+ if ( last > i ) {
+ a_vals[i] = a_vals[last];
+ a_nvals[i] = a_nvals[last];
+ }
+ BER_BVZERO( &a_vals[last] );
+ BER_BVZERO( &a_nvals[last] );
+ last--;
+ break;
+
+ default:
+ /* leave attr untouched if massage failed */
+ if ( !BER_BVISNULL( &pdn ) && a_vals[i].bv_val != pdn.bv_val ) {
+ ch_free( a_vals[i].bv_val );
+ a_vals[i] = pdn;
+ }
+ if ( !BER_BVISNULL( &ndn ) && a_nvals[i].bv_val != ndn.bv_val ) {
+ ch_free( a_nvals[i].bv_val );
+ a_nvals[i] = ndn;
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void
+rwm_mapping_dst_free( void *v_mapping )
+{
+ struct ldapmapping *mapping = v_mapping;
+
+ if ( BER_BVISEMPTY( &mapping[0].m_dst ) ) {
+ rwm_mapping_free( &mapping[ -1 ] );
+ }
+}
+
+void
+rwm_mapping_free( void *v_mapping )
+{
+ struct ldapmapping *mapping = v_mapping;
+
+ if ( !BER_BVISNULL( &mapping[0].m_src ) ) {
+ ch_free( mapping[0].m_src.bv_val );
+ }
+
+ if ( mapping[0].m_flags & RWMMAP_F_FREE_SRC ) {
+ if ( mapping[0].m_flags & RWMMAP_F_IS_OC ) {
+ if ( mapping[0].m_src_oc ) {
+ ch_free( mapping[0].m_src_oc );
+ }
+
+ } else {
+ if ( mapping[0].m_src_ad ) {
+ ch_free( mapping[0].m_src_ad );
+ }
+ }
+ }
+
+ if ( !BER_BVISNULL( &mapping[0].m_dst ) ) {
+ ch_free( mapping[0].m_dst.bv_val );
+ }
+
+ if ( mapping[0].m_flags & RWMMAP_F_FREE_DST ) {
+ if ( mapping[0].m_flags & RWMMAP_F_IS_OC ) {
+ if ( mapping[0].m_dst_oc ) {
+ ch_free( mapping[0].m_dst_oc );
+ }
+
+ } else {
+ if ( mapping[0].m_dst_ad ) {
+ ch_free( mapping[0].m_dst_ad );
+ }
+ }
+ }
+
+ ch_free( mapping );
+
+}
+
+#endif /* SLAPD_OVER_RWM */
diff --git a/servers/slapd/overlays/seqmod.c b/servers/slapd/overlays/seqmod.c
new file mode 100644
index 0000000..503d6a6
--- /dev/null
+++ b/servers/slapd/overlays/seqmod.c
@@ -0,0 +1,207 @@
+/* seqmod.c - sequenced modifies */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_SEQMOD
+
+#include "slap.h"
+#include "slap-config.h"
+
+/* This overlay serializes concurrent attempts to modify a single entry */
+
+typedef struct modtarget {
+ struct modtarget *mt_next;
+ struct modtarget *mt_tail;
+ Operation *mt_op;
+} modtarget;
+
+typedef struct seqmod_info {
+ Avlnode *sm_mods; /* entries being modified */
+ ldap_pvt_thread_mutex_t sm_mutex;
+} seqmod_info;
+
+static int
+sm_avl_cmp( const void *c1, const void *c2 )
+{
+ const modtarget *m1, *m2;
+ int rc;
+
+ m1 = c1; m2 = c2;
+ rc = m1->mt_op->o_req_ndn.bv_len - m2->mt_op->o_req_ndn.bv_len;
+
+ if ( rc ) return rc;
+ return ber_bvcmp( &m1->mt_op->o_req_ndn, &m2->mt_op->o_req_ndn );
+}
+
+static int
+seqmod_op_cleanup( Operation *op, SlapReply *rs )
+{
+ slap_callback *sc = op->o_callback;
+ seqmod_info *sm = sc->sc_private;
+ modtarget *mt, mtdummy;
+ Avlnode *av;
+
+ mtdummy.mt_op = op;
+ /* This op is done, remove it */
+ ldap_pvt_thread_mutex_lock( &sm->sm_mutex );
+ av = ldap_avl_find2( sm->sm_mods, &mtdummy, sm_avl_cmp );
+ assert(av != NULL);
+
+ mt = av->avl_data;
+
+ /* If there are more, promote the next one */
+ if ( mt->mt_next ) {
+ av->avl_data = mt->mt_next;
+ mt->mt_next->mt_tail = mt->mt_tail;
+ } else {
+ ldap_avl_delete( &sm->sm_mods, mt, sm_avl_cmp );
+ }
+ ldap_pvt_thread_mutex_unlock( &sm->sm_mutex );
+ op->o_callback = sc->sc_next;
+ op->o_tmpfree( sc, op->o_tmpmemctx );
+
+ return 0;
+}
+
+static int
+seqmod_op_mod( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ seqmod_info *sm = on->on_bi.bi_private;
+ modtarget *mt;
+ Avlnode *av;
+ slap_callback *cb;
+
+ cb = op->o_tmpcalloc( 1, sizeof(slap_callback) + sizeof(modtarget),
+ op->o_tmpmemctx );
+ mt = (modtarget *)(cb+1);
+ mt->mt_next = NULL;
+ mt->mt_tail = mt;
+ mt->mt_op = op;
+
+ /* See if we're already modifying this entry - don't allow
+ * near-simultaneous mods of the same entry
+ */
+ ldap_pvt_thread_mutex_lock( &sm->sm_mutex );
+ av = ldap_avl_find2( sm->sm_mods, mt, sm_avl_cmp );
+ if ( av ) {
+ modtarget *mtp = av->avl_data;
+ mtp->mt_tail->mt_next = mt;
+ mtp->mt_tail = mt;
+ /* Wait for this op to get to head of list */
+ while ( mtp != mt ) {
+ ldap_pvt_thread_mutex_unlock( &sm->sm_mutex );
+ ldap_pvt_thread_yield();
+ /* Let it finish - should use a condition
+ * variable here... */
+ ldap_pvt_thread_mutex_lock( &sm->sm_mutex );
+ mtp = av->avl_data;
+ }
+ } else {
+ /* Record that we're modifying this now */
+ ldap_avl_insert( &sm->sm_mods, mt, sm_avl_cmp, ldap_avl_dup_error );
+ }
+ ldap_pvt_thread_mutex_unlock( &sm->sm_mutex );
+
+ cb->sc_cleanup = seqmod_op_cleanup;
+ cb->sc_private = sm;
+ cb->sc_next = op->o_callback;
+ op->o_callback = cb;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+seqmod_op_extended(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ if ( exop_is_write( op )) return seqmod_op_mod( op, rs );
+ else return SLAP_CB_CONTINUE;
+}
+
+static int
+seqmod_db_open(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ seqmod_info *sm;
+
+ sm = ch_calloc(1, sizeof(seqmod_info));
+ on->on_bi.bi_private = sm;
+
+ ldap_pvt_thread_mutex_init( &sm->sm_mutex );
+
+ return 0;
+}
+
+static int
+seqmod_db_close(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ seqmod_info *sm = (seqmod_info *)on->on_bi.bi_private;
+
+ if ( sm ) {
+ ldap_pvt_thread_mutex_destroy( &sm->sm_mutex );
+
+ ch_free( sm );
+ on->on_bi.bi_private = NULL;
+ }
+
+ return 0;
+}
+
+/* This overlay is set up for dynamic loading via moduleload. For static
+ * configuration, you'll need to arrange for the slap_overinst to be
+ * initialized and registered by some other function inside slapd.
+ */
+
+static slap_overinst seqmod;
+
+int
+seqmod_initialize()
+{
+ seqmod.on_bi.bi_type = "seqmod";
+ seqmod.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ seqmod.on_bi.bi_db_open = seqmod_db_open;
+ seqmod.on_bi.bi_db_close = seqmod_db_close;
+
+ seqmod.on_bi.bi_op_modify = seqmod_op_mod;
+ seqmod.on_bi.bi_op_modrdn = seqmod_op_mod;
+ seqmod.on_bi.bi_extended = seqmod_op_extended;
+
+ return overlay_register( &seqmod );
+}
+
+#if SLAPD_OVER_SEQMOD == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return seqmod_initialize();
+}
+#endif /* SLAPD_OVER_SEQMOD == SLAPD_MOD_DYNAMIC */
+
+#endif /* defined(SLAPD_OVER_SEQMOD) */
diff --git a/servers/slapd/overlays/slapover.txt b/servers/slapd/overlays/slapover.txt
new file mode 100644
index 0000000..2015d8d
--- /dev/null
+++ b/servers/slapd/overlays/slapover.txt
@@ -0,0 +1,158 @@
+slapd internal APIs
+
+Introduction
+
+Frontend, backend, database, callback, overlay - what does it all mean?
+
+The "frontend" refers to all the code that deals with the actual interaction
+with an LDAP client. This includes the code to read requests from the network
+and parse them into C data structures, all of the session management, and the
+formatting of responses for transmission onto the network. It also includes the
+access control engine and other features that are generic to LDAP processing,
+features which are not dependent on a particular database implementation.
+Because the frontend serves as the framework that ties everything together,
+it should not change much over time.
+
+The terms "backend" and "database" have historically been used interchangeably
+and/or in combination as if they are the same thing, but the code has a clear
+distinction between the two. A "backend" is a type of module, and a "database"
+is an instance of a backend type. Together they work with the frontend to
+manage the actual data that are operated on by LDAP requests. Originally the
+backend interface was relatively compact, with individual functions
+corresponding to each LDAP operation type, plus functions for init, config, and
+shutdown. The number of entry points has grown to allow greater flexibility,
+but the concept is much the same as before.
+
+The language here can get a bit confusing. A backend in slapd is embodied in a
+BackendInfo data structure, and a database is held in a BackendDB structure.
+Originally it was all just a single Backend data structure, but things have
+grown and the concept was split into these two parts. The idea behind the
+distinct BackendInfo is to allow for any initialization and configuration that
+may be needed by every instance of a type of database, as opposed to items that
+are specific to just one instance. For example, you might have a database
+library that requires an initialization routine to be called exactly once at
+program startup. Then there may be a "open" function that must be called once
+for each database instance. The BackendInfo.bi_open function provides the
+one-time startup, while the BackendInfo.bi_db_open function provides the
+per-database startup. The main feature of the BackendInfo structure is its
+table of entry points for all of the database functions that it implements.
+There's also a bi_private pointer that can be used to carry any configuration
+state needed by the backend. (Note that this is state that applies to the
+backend type, and thus to all database instances of the backend as well.) The
+BackendDB structure carries all of the per-instance state for a backend
+database. This includes the database suffix, ACLs, flags, various DNs, etc. It
+also has a pointer to its BackendInfo, and a be_private pointer for use by the
+particular backend instance. In practice, the per-type features are seldom
+used, and all of the work is done in the per-instance data structures.
+
+Ordinarily an LDAP request is received by the slapd frontend, parsed into a
+request structure, and then passed to the backend for processing. The backend
+may call various utility functions in the frontend to assist in processing, and
+then it eventually calls some send_ldap_result function in the frontend to send
+results back to the client. The processing flow is pretty rigidly defined; even
+though slapd is capable of dynamically loading new code modules, it was
+difficult to add extensions that changed the basic protocol operations. If you
+wanted to extend the server with special behaviors you would need to modify the
+frontend or the backend or both, and generally you would need to write an
+entire new backend to get some set of special features working. With OpenLDAP
+2.1 we added the notion of a callback, which can intercept the results sent
+from a backend before they are sent to a client. Using callbacks makes it
+possible to modify the results if desired, or to simply discard the results
+instead of sending them to any client. This callback feature is used
+extensively in the SASL support to perform internal searches of slapd databases
+when mapping authentication IDs into regular DNs. The callback mechanism is
+also the basis of backglue, which allows separate databases to be searched as
+if they were a single naming context.
+
+Very often, one needs to add just a tiny feature onto an otherwise "normal"
+database. The usual way to achieve this was to use a programmable backend (like
+back-perl) to preprocess various requests and then forward them back into slapd
+to be handled by the real database. While this technique works, it is fairly
+inefficient because it involves many transitions from network to slapd and back
+again. The overlay concept introduced in OpenLDAP 2.2 allows code to be
+inserted between the slapd frontend and any backend, so that incoming requests
+can be intercepted before reaching the backend database. (There is also a SLAPI
+plugin framework in OpenLDAP 2.2; it offers a lot of flexibility as well but is
+not discussed here.) The overlay framework also uses the callback mechanism, so
+outgoing results can also be intercepted by external code. All of this could
+get unwieldy if a lot of overlays were being used, but there was also another
+significant API change in OpenLDAP 2.2 to streamline internal processing. (See
+the document "Refactoring the slapd ABI"...)
+
+OK, enough generalities... You should probably have a copy of slap.h in front
+of you to continue here.
+
+What is an overlay? The structure defining it includes a BackendInfo structure
+plus a few additional fields. It gets inserted into the usual frontend->backend
+call chain by replacing the BackendDB's BackendInfo pointer with its own. The
+framework to accomplish this is in backover.c. For a given backend, the
+BackendInfo will point to a slap_overinfo structure. The slap_overinfo has a
+BackendInfo that points to all of the overlay framework's entry points. It also
+holds a copy of the original BackendInfo pointer, and a linked list of
+slap_overinst structures. There is one slap_overinst per configured overlay,
+and the set of overlays configured on a backend are treated like a stack; i.e.,
+the last one configured is at the top of the stack, and it executes first.
+
+Continuing with the stack notion - a request enters the frontend, is directed
+to a backend by select_backend, and then intercepted by the top of the overlay
+stack. This first overlay may do something with the request, and then return
+SLAP_CB_CONTINUE, which will then cause processing to fall into the next
+overlay, and so on down the stack until finally the request is handed to the
+actual backend database. Likewise, when the database finishes processing and
+sends a result, the overlay callback intercepts this and the topmost overlay
+gets to process the result. If it returns SLAP_CB_CONTINUE then processing will
+continue in the next overlay, and then any other callbacks, then finally the
+result reaches the frontend for sending back to the client. At any step along
+the way, a module may choose to fully process the request or result and not
+allow it to propagate any further down the stack. Whenever a module returns
+anything other than SLAP_CB_CONTINUE the processing stops.
+
+An overlay can call most frontend functions without any special consideration.
+However, if a call is going to result in any backend code being invoked, then
+the backend environment must be correct. During a normal backend invocation,
+op->o_bd points to the BackendDB structure for the backend, and
+op->o_bd->bd_info points to the BackendInfo for the backend. All of the
+information a specific backend instance needs is in op->o_bd->be_private and
+all of its entry points are in the BackendInfo structure. When overlays are in
+use on a backend, op->o_bd->bd_info points to the BackendInfo (actually a
+slap_overinfo) that contains the overlay framework. When a particular overlay
+instance is executing, op->o_bd points to a copy of the original op->o_bd, and
+op->o_bd->bd_info points to a slap_overinst which carries the information about
+the current overlay. The slap_overinst contains an on_private pointer which can
+be used to carry any configuration or state information the overlay needs. The
+normal way to invoke a backend function is through the op->o_bd->bd_info table
+of entry points, but obviously this must be set to the backend's original
+BackendInfo in order to get to the right function.
+
+There are two approaches here. The slap_overinst also contains a on_info field
+that points to the top slap_overinfo that wraps the current backend. The
+simplest thing is for the overlay to set op->o_bd->bd_info to this on_info
+value before invoking a backend function. This will cause processing of that
+particular operation to begin at the top of the overlay stack, so all the other
+overlays on the backend will also get a chance to handle this internal request.
+The other possibility is to invoke the underlying backend directly, bypassing
+the rest of the overlays, by calling through on_info->oi_orig. You should be
+careful in choosing this approach, since it precludes other overlays from doing
+their jobs.
+
+One of the more interesting uses for an overlay is to attach two (or more)
+different database backends into a single execution stack. Assuming that the
+basic frontend-managed information (suffix, rootdn, ACLs, etc.) will be the
+same for all of the backends, the only thing the overlay needs to maintain is a
+be_private and bd_info pointer for the added backends. The chain and proxycache
+overlays are two complementary examples of this usage. The chain overlay
+attaches a back-ldap backend to a local database backend, and allows referrals
+to remote servers generated by the database to be processed by slapd instead of
+being returned to the client. The proxycache overlay attaches a local database
+to a back-ldap (or back-meta) backend and allows search results from remote
+servers to be cached locally. In both cases the overlays must provide a bit of
+glue to swap in the appropriate be_private and bd_info pointers before invoking
+the attached backend, which can then be invoked as usual.
+
+Note on overlay initialization/destruction: you should allocate storage for
+config info in the _db_init handler, and free this storage in the _db_destroy
+handler. You must not free it in the _db_close handler because a module may
+be opened/closed multiple times in a running slapd when using dynamic
+configuration and the config info must remain intact.
+
+---
diff --git a/servers/slapd/overlays/sssvlv.c b/servers/slapd/overlays/sssvlv.c
new file mode 100644
index 0000000..828782a
--- /dev/null
+++ b/servers/slapd/overlays/sssvlv.c
@@ -0,0 +1,1439 @@
+/* sssvlv.c - server side sort / virtual list view */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2009-2022 The OpenLDAP Foundation.
+ * Portions copyright 2009 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion in
+ * OpenLDAP Software. Support for multiple sorts per connection added
+ * by Raphael Ouazana.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_SSSVLV
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/ctype.h>
+
+#include <ldap_avl.h>
+
+#include "slap.h"
+#include "lutil.h"
+#include "slap-config.h"
+
+#include "../../../libraries/liblber/lber-int.h" /* ber_rewind */
+
+/* RFC2891: Server Side Sorting
+ * RFC2696: Paged Results
+ */
+#ifndef LDAP_MATCHRULE_IDENTIFIER
+#define LDAP_MATCHRULE_IDENTIFIER 0x80L
+#define LDAP_REVERSEORDER_IDENTIFIER 0x81L
+#define LDAP_ATTRTYPES_IDENTIFIER 0x80L
+#endif
+
+/* draft-ietf-ldapext-ldapv3-vlv-09.txt: Virtual List Views
+ */
+#ifndef LDAP_VLVBYINDEX_IDENTIFIER
+#define LDAP_VLVBYINDEX_IDENTIFIER 0xa0L
+#define LDAP_VLVBYVALUE_IDENTIFIER 0x81L
+#define LDAP_VLVCONTEXT_IDENTIFIER 0x04L
+
+#define LDAP_VLV_SSS_MISSING 0x4C
+#define LDAP_VLV_RANGE_ERROR 0x4D
+#endif
+
+#define SAFESTR(macro_str, macro_def) ((macro_str) ? (macro_str) : (macro_def))
+
+#define SSSVLV_DEFAULT_MAX_KEYS 5
+#define SSSVLV_DEFAULT_MAX_REQUEST_PER_CONN 5
+
+#define NO_PS_COOKIE (PagedResultsCookie) -1
+#define NO_VC_CONTEXT (unsigned long) -1
+
+typedef struct vlv_ctrl {
+ int vc_before;
+ int vc_after;
+ int vc_offset;
+ int vc_count;
+ struct berval vc_value;
+ unsigned long vc_context;
+} vlv_ctrl;
+
+typedef struct sort_key
+{
+ AttributeDescription *sk_ad;
+ MatchingRule *sk_ordering;
+ int sk_direction; /* 1=normal, -1=reverse */
+} sort_key;
+
+typedef struct sort_ctrl {
+ int sc_nkeys;
+ sort_key sc_keys[1];
+} sort_ctrl;
+
+
+typedef struct sort_node
+{
+ int sn_conn;
+ int sn_session;
+ struct berval sn_dn;
+ struct berval *sn_vals;
+} sort_node;
+
+typedef struct sssvlv_info
+{
+ int svi_max; /* max concurrent sorts */
+ int svi_num; /* current # sorts */
+ int svi_max_keys; /* max sort keys per request */
+ int svi_max_percon; /* max concurrent sorts per con */
+} sssvlv_info;
+
+typedef struct sort_op
+{
+ TAvlnode *so_tree;
+ sort_ctrl *so_ctrl;
+ sssvlv_info *so_info;
+ int so_paged;
+ int so_page_size;
+ int so_nentries;
+ int so_vlv;
+ int so_vlv_rc;
+ int so_vlv_target;
+ int so_session;
+ unsigned long so_vcontext;
+ int so_running;
+} sort_op;
+
+/* There is only one conn table for all overlay instances */
+/* Each conn can handle one session by context */
+static sort_op ***sort_conns;
+static ldap_pvt_thread_mutex_t sort_conns_mutex;
+static int ov_count;
+static const char *debug_header = "sssvlv";
+
+static int sss_cid;
+static int vlv_cid;
+
+/* RFC 2981 Section 2.2
+ * If a sort key is a multi-valued attribute, and an entry happens to
+ * have multiple values for that attribute and no other controls are
+ * present that affect the sorting order, then the server SHOULD use the
+ * least value (according to the ORDERING rule for that attribute).
+ */
+static struct berval* select_value(
+ Attribute *attr,
+ sort_key *key )
+{
+ struct berval* ber1, *ber2;
+ MatchingRule *mr = key->sk_ordering;
+ unsigned i;
+ int cmp;
+
+ ber1 = &(attr->a_nvals[0]);
+ ber2 = ber1+1;
+ for ( i = 1; i < attr->a_numvals; i++,ber2++ ) {
+ mr->smr_match( &cmp, 0, mr->smr_syntax, mr, ber1, ber2 );
+ if ( cmp > 0 ) {
+ ber1 = ber2;
+ }
+ }
+
+ Debug(LDAP_DEBUG_TRACE, "%s: value selected for compare: %s\n",
+ debug_header,
+ SAFESTR(ber1->bv_val, "<Empty>") );
+
+ return ber1;
+}
+
+static int node_cmp( const void* val1, const void* val2 )
+{
+ sort_node *sn1 = (sort_node *)val1;
+ sort_node *sn2 = (sort_node *)val2;
+ sort_ctrl *sc;
+ MatchingRule *mr;
+ int i, cmp = 0;
+ assert( sort_conns[sn1->sn_conn]
+ && sort_conns[sn1->sn_conn][sn1->sn_session]
+ && sort_conns[sn1->sn_conn][sn1->sn_session]->so_ctrl );
+ sc = sort_conns[sn1->sn_conn][sn1->sn_session]->so_ctrl;
+
+ for ( i=0; cmp == 0 && i<sc->sc_nkeys; i++ ) {
+ if ( BER_BVISNULL( &sn1->sn_vals[i] )) {
+ if ( BER_BVISNULL( &sn2->sn_vals[i] ))
+ cmp = 0;
+ else
+ cmp = sc->sc_keys[i].sk_direction;
+ } else if ( BER_BVISNULL( &sn2->sn_vals[i] )) {
+ cmp = sc->sc_keys[i].sk_direction * -1;
+ } else {
+ mr = sc->sc_keys[i].sk_ordering;
+ mr->smr_match( &cmp, 0, mr->smr_syntax, mr,
+ &sn1->sn_vals[i], &sn2->sn_vals[i] );
+ if ( cmp )
+ cmp *= sc->sc_keys[i].sk_direction;
+ }
+ }
+ return cmp;
+}
+
+static int node_insert( const void *val1, const void *val2 )
+{
+ /* Never return equal so that new entries are always inserted */
+ return node_cmp( val1, val2 ) < 0 ? -1 : 1;
+}
+
+static int pack_vlv_response_control(
+ Operation *op,
+ SlapReply *rs,
+ sort_op *so,
+ LDAPControl **ctrlsp )
+{
+ LDAPControl *ctrl;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ struct berval cookie, bv;
+ int rc;
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+
+ rc = ber_printf( ber, "{iie", so->so_vlv_target, so->so_nentries,
+ so->so_vlv_rc );
+
+ if ( rc != -1 && so->so_vcontext ) {
+ cookie.bv_val = (char *)&so->so_vcontext;
+ cookie.bv_len = sizeof(so->so_vcontext);
+ rc = ber_printf( ber, "tO", LDAP_VLVCONTEXT_IDENTIFIER, &cookie );
+ }
+
+ if ( rc != -1 ) {
+ rc = ber_printf( ber, "}" );
+ }
+
+ if ( rc != -1 ) {
+ rc = ber_flatten2( ber, &bv, 0 );
+ }
+
+ if ( rc != -1 ) {
+ ctrl = (LDAPControl *)op->o_tmpalloc( sizeof(LDAPControl)+
+ bv.bv_len, op->o_tmpmemctx );
+ ctrl->ldctl_oid = LDAP_CONTROL_VLVRESPONSE;
+ ctrl->ldctl_iscritical = 0;
+ ctrl->ldctl_value.bv_val = (char *)(ctrl+1);
+ ctrl->ldctl_value.bv_len = bv.bv_len;
+ AC_MEMCPY( ctrl->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
+ ctrlsp[0] = ctrl;
+ } else {
+ ctrlsp[0] = NULL;
+ rs->sr_err = LDAP_OTHER;
+ }
+
+ ber_free_buf( ber );
+
+ return rs->sr_err;
+}
+
+static int pack_pagedresult_response_control(
+ Operation *op,
+ SlapReply *rs,
+ sort_op *so,
+ LDAPControl **ctrlsp )
+{
+ LDAPControl *ctrl;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ PagedResultsCookie resp_cookie;
+ struct berval cookie, bv;
+ int rc;
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+
+ if ( so->so_nentries > 0 ) {
+ resp_cookie = ( PagedResultsCookie )so->so_tree;
+ cookie.bv_len = sizeof( PagedResultsCookie );
+ cookie.bv_val = (char *)&resp_cookie;
+ } else {
+ resp_cookie = ( PagedResultsCookie )0;
+ BER_BVZERO( &cookie );
+ }
+
+ op->o_conn->c_pagedresults_state.ps_cookie = resp_cookie;
+ op->o_conn->c_pagedresults_state.ps_count
+ = ((PagedResultsState *)op->o_pagedresults_state)->ps_count
+ + rs->sr_nentries;
+
+ rc = ber_printf( ber, "{iO}", so->so_nentries, &cookie );
+ if ( rc != -1 ) {
+ rc = ber_flatten2( ber, &bv, 0 );
+ }
+
+ if ( rc != -1 ) {
+ ctrl = (LDAPControl *)op->o_tmpalloc( sizeof(LDAPControl)+
+ bv.bv_len, op->o_tmpmemctx );
+ ctrl->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
+ ctrl->ldctl_iscritical = 0;
+ ctrl->ldctl_value.bv_val = (char *)(ctrl+1);
+ ctrl->ldctl_value.bv_len = bv.bv_len;
+ AC_MEMCPY( ctrl->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
+ ctrlsp[0] = ctrl;
+ } else {
+ ctrlsp[0] = NULL;
+ rs->sr_err = LDAP_OTHER;
+ }
+
+ ber_free_buf( ber );
+
+ return rs->sr_err;
+}
+
+static int pack_sss_response_control(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl **ctrlsp )
+{
+ LDAPControl *ctrl;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ struct berval bv;
+ int rc;
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+
+ /* Pack error code */
+ rc = ber_printf(ber, "{e}", rs->sr_err);
+
+ if ( rc != -1)
+ rc = ber_flatten2( ber, &bv, 0 );
+
+ if ( rc != -1 ) {
+ ctrl = (LDAPControl *)op->o_tmpalloc( sizeof(LDAPControl)+
+ bv.bv_len, op->o_tmpmemctx );
+ ctrl->ldctl_oid = LDAP_CONTROL_SORTRESPONSE;
+ ctrl->ldctl_iscritical = 0;
+ ctrl->ldctl_value.bv_val = (char *)(ctrl+1);
+ ctrl->ldctl_value.bv_len = bv.bv_len;
+ AC_MEMCPY( ctrl->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
+ ctrlsp[0] = ctrl;
+ } else {
+ ctrlsp[0] = NULL;
+ rs->sr_err = LDAP_OTHER;
+ }
+
+ ber_free_buf( ber );
+
+ return rs->sr_err;
+}
+
+/* Return the session id or -1 if unknown */
+static int find_session_by_so(
+ int svi_max_percon,
+ int conn_id,
+ sort_op *so )
+{
+ int sess_id;
+ if (so == NULL) {
+ return -1;
+ }
+ for (sess_id = 0; sess_id < svi_max_percon; sess_id++) {
+ if ( sort_conns[conn_id] && sort_conns[conn_id][sess_id] == so )
+ return sess_id;
+ }
+ return -1;
+}
+
+/* Return the session id or -1 if unknown */
+static int find_session_by_context(
+ int svi_max_percon,
+ int conn_id,
+ unsigned long vc_context,
+ PagedResultsCookie ps_cookie )
+{
+ int sess_id;
+ for(sess_id = 0; sess_id < svi_max_percon; sess_id++) {
+ if( sort_conns[conn_id] && sort_conns[conn_id][sess_id] &&
+ ( sort_conns[conn_id][sess_id]->so_vcontext == vc_context ||
+ (PagedResultsCookie) sort_conns[conn_id][sess_id]->so_tree == ps_cookie ) )
+ return sess_id;
+ }
+ return -1;
+}
+
+static int find_next_session(
+ int svi_max_percon,
+ int conn_id )
+{
+ int sess_id;
+ assert(sort_conns[conn_id] != NULL);
+ for(sess_id = 0; sess_id < svi_max_percon; sess_id++) {
+ if(!sort_conns[conn_id][sess_id]) {
+ return sess_id;
+ }
+ }
+ if (sess_id >= svi_max_percon) {
+ return -1;
+ } else {
+ return sess_id;
+ }
+}
+
+static void free_sort_op( Connection *conn, sort_op *so )
+{
+ int sess_id;
+
+ ldap_pvt_thread_mutex_lock( &sort_conns_mutex );
+ sess_id = find_session_by_so( so->so_info->svi_max_percon, conn->c_conn_idx, so );
+ if ( sess_id > -1 ) {
+ sort_conns[conn->c_conn_idx][sess_id] = NULL;
+ so->so_info->svi_num--;
+ }
+ ldap_pvt_thread_mutex_unlock( &sort_conns_mutex );
+
+ if ( sess_id > -1 ){
+ if ( so->so_tree ) {
+ if ( so->so_paged > SLAP_CONTROL_IGNORED ) {
+ TAvlnode *cur_node, *next_node;
+ cur_node = so->so_tree;
+ while ( cur_node ) {
+ next_node = ldap_tavl_next( cur_node, TAVL_DIR_RIGHT );
+ ch_free( cur_node->avl_data );
+ ber_memfree( cur_node );
+
+ cur_node = next_node;
+ }
+ } else {
+ ldap_tavl_free( so->so_tree, ch_free );
+ }
+ so->so_tree = NULL;
+ }
+
+ ch_free( so );
+ }
+}
+
+static void free_sort_ops( Connection *conn, sort_op **sos, int svi_max_percon )
+{
+ int sess_id;
+ sort_op *so;
+
+ for( sess_id = 0; sess_id < svi_max_percon ; sess_id++ ) {
+ so = sort_conns[conn->c_conn_idx][sess_id];
+ if ( so ) {
+ free_sort_op( conn, so );
+ sort_conns[conn->c_conn_idx][sess_id] = NULL;
+ }
+ }
+}
+
+static void send_list(
+ Operation *op,
+ SlapReply *rs,
+ sort_op *so)
+{
+ TAvlnode *cur_node, *tmp_node;
+ vlv_ctrl *vc = op->o_controls[vlv_cid];
+ int i, j, dir, rc;
+ BackendDB *be;
+ Entry *e;
+ LDAPControl *ctrls[2];
+
+ rs->sr_attrs = op->ors_attrs;
+
+ /* FIXME: it may be better to just flatten the tree into
+ * an array before doing all of this...
+ */
+
+ /* Are we just counting an offset? */
+ if ( BER_BVISNULL( &vc->vc_value )) {
+ if ( vc->vc_offset == vc->vc_count ) {
+ /* wants the last entry in the list */
+ cur_node = ldap_tavl_end(so->so_tree, TAVL_DIR_RIGHT);
+ so->so_vlv_target = so->so_nentries;
+ } else if ( vc->vc_offset == 1 ) {
+ /* wants the first entry in the list */
+ cur_node = ldap_tavl_end(so->so_tree, TAVL_DIR_LEFT);
+ so->so_vlv_target = 1;
+ } else {
+ int target;
+ /* Just iterate to the right spot */
+ if ( vc->vc_count && vc->vc_count != so->so_nentries ) {
+ if ( vc->vc_offset > vc->vc_count )
+ goto range_err;
+ target = so->so_nentries * vc->vc_offset / vc->vc_count;
+ } else {
+ if ( vc->vc_offset > so->so_nentries ) {
+range_err:
+ so->so_vlv_rc = LDAP_VLV_RANGE_ERROR;
+ pack_vlv_response_control( op, rs, so, ctrls );
+ ctrls[1] = NULL;
+ slap_add_ctrls( op, rs, ctrls );
+ rs->sr_err = LDAP_VLV_ERROR;
+ return;
+ }
+ target = vc->vc_offset;
+ }
+ so->so_vlv_target = target;
+ /* Start at left and go right, or start at right and go left? */
+ if ( target < so->so_nentries / 2 ) {
+ cur_node = ldap_tavl_end(so->so_tree, TAVL_DIR_LEFT);
+ dir = TAVL_DIR_RIGHT;
+ } else {
+ cur_node = ldap_tavl_end(so->so_tree, TAVL_DIR_RIGHT);
+ dir = TAVL_DIR_LEFT;
+ target = so->so_nentries - target + 1;
+ }
+ for ( i=1; i<target; i++ )
+ cur_node = ldap_tavl_next( cur_node, dir );
+ }
+ } else {
+ /* we're looking for a specific value */
+ sort_ctrl *sc = so->so_ctrl;
+ MatchingRule *mr = sc->sc_keys[0].sk_ordering;
+ sort_node *sn;
+ struct berval bv;
+
+ if ( mr->smr_normalize ) {
+ rc = mr->smr_normalize( SLAP_MR_VALUE_OF_SYNTAX,
+ mr->smr_syntax, mr, &vc->vc_value, &bv, op->o_tmpmemctx );
+ if ( rc ) {
+ so->so_vlv_rc = LDAP_INAPPROPRIATE_MATCHING;
+ pack_vlv_response_control( op, rs, so, ctrls );
+ ctrls[1] = NULL;
+ slap_add_ctrls( op, rs, ctrls );
+ rs->sr_err = LDAP_VLV_ERROR;
+ return;
+ }
+ } else {
+ bv = vc->vc_value;
+ }
+
+ sn = op->o_tmpalloc( sizeof(sort_node) +
+ sc->sc_nkeys * sizeof(struct berval), op->o_tmpmemctx );
+ sn->sn_vals = (struct berval *)(sn+1);
+ sn->sn_conn = op->o_conn->c_conn_idx;
+ sn->sn_session = find_session_by_so( so->so_info->svi_max_percon, op->o_conn->c_conn_idx, so );
+ sn->sn_vals[0] = bv;
+ for (i=1; i<sc->sc_nkeys; i++) {
+ BER_BVZERO( &sn->sn_vals[i] );
+ }
+ cur_node = ldap_tavl_find3( so->so_tree, sn, node_cmp, &j );
+ /* didn't find >= match */
+ if ( j > 0 ) {
+ if ( cur_node )
+ cur_node = ldap_tavl_next( cur_node, TAVL_DIR_RIGHT );
+ }
+ op->o_tmpfree( sn, op->o_tmpmemctx );
+
+ if ( !cur_node ) {
+ so->so_vlv_target = so->so_nentries + 1;
+ } else {
+ sort_node *sn = so->so_tree->avl_data;
+ /* start from the left or the right side? */
+ mr->smr_match( &i, 0, mr->smr_syntax, mr, &bv, &sn->sn_vals[0] );
+ if ( i > 0 ) {
+ tmp_node = ldap_tavl_end(so->so_tree, TAVL_DIR_RIGHT);
+ dir = TAVL_DIR_LEFT;
+ } else {
+ tmp_node = ldap_tavl_end(so->so_tree, TAVL_DIR_LEFT);
+ dir = TAVL_DIR_RIGHT;
+ }
+ for (i=0; tmp_node != cur_node;
+ tmp_node = ldap_tavl_next( tmp_node, dir ), i++);
+ so->so_vlv_target = (dir == TAVL_DIR_RIGHT) ? i+1 : so->so_nentries - i;
+ }
+ if ( bv.bv_val != vc->vc_value.bv_val )
+ op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
+ }
+ if ( !cur_node ) {
+ i = 1;
+ cur_node = ldap_tavl_end(so->so_tree, TAVL_DIR_RIGHT);
+ } else {
+ i = 0;
+ }
+ for ( ; i<vc->vc_before; i++ ) {
+ tmp_node = ldap_tavl_next( cur_node, TAVL_DIR_LEFT );
+ if ( !tmp_node ) break;
+ cur_node = tmp_node;
+ }
+ j = i + vc->vc_after + 1;
+ be = op->o_bd;
+ for ( i=0; i<j; i++ ) {
+ sort_node *sn = cur_node->avl_data;
+
+ if ( slapd_shutdown ) break;
+
+ op->o_bd = select_backend( &sn->sn_dn, 0 );
+ e = NULL;
+ rc = be_entry_get_rw( op, &sn->sn_dn, NULL, NULL, 0, &e );
+
+ if ( e && rc == LDAP_SUCCESS ) {
+ rs->sr_entry = e;
+ rs->sr_flags = REP_ENTRY_MUSTRELEASE;
+ rs->sr_err = send_search_entry( op, rs );
+ if ( rs->sr_err == LDAP_UNAVAILABLE )
+ break;
+ }
+ cur_node = ldap_tavl_next( cur_node, TAVL_DIR_RIGHT );
+ if ( !cur_node ) break;
+ }
+ so->so_vlv_rc = LDAP_SUCCESS;
+
+ op->o_bd = be;
+}
+
+static void send_page( Operation *op, SlapReply *rs, sort_op *so )
+{
+ TAvlnode *cur_node = so->so_tree;
+ TAvlnode *next_node = NULL;
+ BackendDB *be = op->o_bd;
+ Entry *e;
+ int rc;
+
+ rs->sr_attrs = op->ors_attrs;
+
+ while ( cur_node && rs->sr_nentries < so->so_page_size ) {
+ sort_node *sn = cur_node->avl_data;
+
+ if ( slapd_shutdown ) break;
+
+ next_node = ldap_tavl_next( cur_node, TAVL_DIR_RIGHT );
+
+ op->o_bd = select_backend( &sn->sn_dn, 0 );
+ e = NULL;
+ rc = be_entry_get_rw( op, &sn->sn_dn, NULL, NULL, 0, &e );
+
+ ch_free( cur_node->avl_data );
+ ber_memfree( cur_node );
+
+ cur_node = next_node;
+ so->so_nentries--;
+
+ if ( e && rc == LDAP_SUCCESS ) {
+ rs->sr_entry = e;
+ rs->sr_flags = REP_ENTRY_MUSTRELEASE;
+ rs->sr_err = send_search_entry( op, rs );
+ if ( rs->sr_err == LDAP_UNAVAILABLE )
+ break;
+ }
+ }
+
+ /* Set the first entry to send for the next page */
+ so->so_tree = next_node;
+ if ( next_node )
+ next_node->avl_left = NULL;
+
+ op->o_bd = be;
+}
+
+static void send_entry(
+ Operation *op,
+ SlapReply *rs,
+ sort_op *so)
+{
+ Debug(LDAP_DEBUG_TRACE,
+ "%s: response control: status=%d, text=%s\n",
+ debug_header, rs->sr_err, SAFESTR(rs->sr_text, "<None>"));
+
+ if ( !so->so_tree )
+ return;
+
+ /* RFC 2891: If critical then send the entries iff they were
+ * successfully sorted. If non-critical send all entries
+ * whether they were sorted or not.
+ */
+ if ( (op->o_ctrlflag[sss_cid] != SLAP_CONTROL_CRITICAL) ||
+ (rs->sr_err == LDAP_SUCCESS) )
+ {
+ if ( so->so_vlv > SLAP_CONTROL_IGNORED ) {
+ send_list( op, rs, so );
+ } else {
+ /* Get the first node to send */
+ TAvlnode *start_node = ldap_tavl_end(so->so_tree, TAVL_DIR_LEFT);
+ so->so_tree = start_node;
+
+ if ( so->so_paged <= SLAP_CONTROL_IGNORED ) {
+ /* Not paged result search. Send all entries.
+ * Set the page size to the number of entries
+ * so that send_page() will send all entries.
+ */
+ so->so_page_size = so->so_nentries;
+ }
+
+ send_page( op, rs, so );
+ }
+ }
+}
+
+static void send_result(
+ Operation *op,
+ SlapReply *rs,
+ sort_op *so)
+{
+ LDAPControl *ctrls[3];
+ int rc, i = 0;
+
+ rc = pack_sss_response_control( op, rs, ctrls );
+ if ( rc == LDAP_SUCCESS ) {
+ i++;
+ rc = -1;
+ if ( so->so_paged > SLAP_CONTROL_IGNORED ) {
+ rc = pack_pagedresult_response_control( op, rs, so, ctrls+1 );
+ } else if ( so->so_vlv > SLAP_CONTROL_IGNORED ) {
+ rc = pack_vlv_response_control( op, rs, so, ctrls+1 );
+ }
+ if ( rc == LDAP_SUCCESS )
+ i++;
+ }
+ ctrls[i] = NULL;
+
+ if ( ctrls[0] != NULL )
+ slap_add_ctrls( op, rs, ctrls );
+ send_ldap_result( op, rs );
+
+ if ( so->so_tree == NULL ) {
+ /* Search finished, so clean up */
+ free_sort_op( op->o_conn, so );
+ } else {
+ so->so_running = 0;
+ }
+}
+
+static int sssvlv_op_response(
+ Operation *op,
+ SlapReply *rs )
+{
+ sort_ctrl *sc = op->o_controls[sss_cid];
+ sort_op *so = op->o_callback->sc_private;
+
+ if ( rs->sr_type == REP_SEARCH ) {
+ int i;
+ size_t len;
+ sort_node *sn, *sn2;
+ struct berval *bv;
+ char *ptr;
+
+ len = sizeof(sort_node) + sc->sc_nkeys * sizeof(struct berval) +
+ rs->sr_entry->e_nname.bv_len + 1;
+ sn = op->o_tmpalloc( len, op->o_tmpmemctx );
+ sn->sn_vals = (struct berval *)(sn+1);
+
+ /* Build tmp list of key values */
+ for ( i=0; i<sc->sc_nkeys; i++ ) {
+ Attribute *a = attr_find( rs->sr_entry->e_attrs,
+ sc->sc_keys[i].sk_ad );
+ if ( a ) {
+ if ( a->a_numvals > 1 ) {
+ bv = select_value( a, &sc->sc_keys[i] );
+ } else {
+ bv = a->a_nvals;
+ }
+ sn->sn_vals[i] = *bv;
+ len += bv->bv_len + 1;
+ } else {
+ BER_BVZERO( &sn->sn_vals[i] );
+ }
+ }
+
+ /* Now dup into regular memory */
+ sn2 = ch_malloc( len );
+ sn2->sn_vals = (struct berval *)(sn2+1);
+ AC_MEMCPY( sn2->sn_vals, sn->sn_vals,
+ sc->sc_nkeys * sizeof(struct berval));
+
+ ptr = (char *)(sn2->sn_vals + sc->sc_nkeys);
+ sn2->sn_dn.bv_val = ptr;
+ sn2->sn_dn.bv_len = rs->sr_entry->e_nname.bv_len;
+ AC_MEMCPY( ptr, rs->sr_entry->e_nname.bv_val,
+ rs->sr_entry->e_nname.bv_len );
+ ptr += rs->sr_entry->e_nname.bv_len;
+ *ptr++ = '\0';
+ for ( i=0; i<sc->sc_nkeys; i++ ) {
+ if ( !BER_BVISNULL( &sn2->sn_vals[i] )) {
+ AC_MEMCPY(ptr, sn2->sn_vals[i].bv_val, sn2->sn_vals[i].bv_len);
+ sn2->sn_vals[i].bv_val = ptr;
+ ptr += sn2->sn_vals[i].bv_len;
+ *ptr++ = '\0';
+ }
+ }
+ op->o_tmpfree( sn, op->o_tmpmemctx );
+ sn = sn2;
+ sn->sn_conn = op->o_conn->c_conn_idx;
+ sn->sn_session = find_session_by_so( so->so_info->svi_max_percon, op->o_conn->c_conn_idx, so );
+
+ /* Insert into the AVL tree */
+ ldap_tavl_insert(&(so->so_tree), sn, node_insert, ldap_avl_dup_error);
+
+ so->so_nentries++;
+
+ /* Collected the keys so that they can be sorted. Thus, stop
+ * the entry from propagating.
+ */
+ rs->sr_err = LDAP_SUCCESS;
+ }
+ else if ( rs->sr_type == REP_RESULT ) {
+ /* Remove serversort response callback.
+ * We don't want the entries that we are about to send to be
+ * processed by serversort response again.
+ */
+ if ( op->o_callback->sc_response == sssvlv_op_response ) {
+ op->o_callback = op->o_callback->sc_next;
+ }
+
+ send_entry( op, rs, so );
+ send_result( op, rs, so );
+ }
+
+ return rs->sr_err;
+}
+
+static int sssvlv_op_search(
+ Operation *op,
+ SlapReply *rs)
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ sssvlv_info *si = on->on_bi.bi_private;
+ int rc = SLAP_CB_CONTINUE;
+ int ok;
+ sort_op *so = NULL, so2;
+ sort_ctrl *sc;
+ PagedResultsState *ps;
+ vlv_ctrl *vc;
+ int sess_id;
+
+ if ( op->o_ctrlflag[sss_cid] <= SLAP_CONTROL_IGNORED ) {
+ if ( op->o_ctrlflag[vlv_cid] > SLAP_CONTROL_IGNORED ) {
+ LDAPControl *ctrls[2];
+ so2.so_vcontext = 0;
+ so2.so_vlv_target = 0;
+ so2.so_nentries = 0;
+ so2.so_vlv_rc = LDAP_VLV_SSS_MISSING;
+ so2.so_vlv = op->o_ctrlflag[vlv_cid];
+ rc = pack_vlv_response_control( op, rs, &so2, ctrls );
+ if ( rc == LDAP_SUCCESS ) {
+ ctrls[1] = NULL;
+ slap_add_ctrls( op, rs, ctrls );
+ }
+ rs->sr_err = LDAP_VLV_ERROR;
+ rs->sr_text = "Sort control is required with VLV";
+ goto leave;
+ }
+ /* Not server side sort so just continue */
+ return SLAP_CB_CONTINUE;
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ "==> sssvlv_search: <%s> %s, control flag: %d\n",
+ op->o_req_dn.bv_val, op->ors_filterstr.bv_val,
+ op->o_ctrlflag[sss_cid]);
+
+ sc = op->o_controls[sss_cid];
+ if ( sc->sc_nkeys > si->svi_max_keys ) {
+ rs->sr_text = "Too many sort keys";
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ goto leave;
+ }
+
+ ps = ( op->o_pagedresults > SLAP_CONTROL_IGNORED ) ?
+ (PagedResultsState*)(op->o_pagedresults_state) : NULL;
+ vc = op->o_ctrlflag[vlv_cid] > SLAP_CONTROL_IGNORED ?
+ op->o_controls[vlv_cid] : NULL;
+
+ if ( ps && vc ) {
+ rs->sr_text = "VLV incompatible with PagedResults";
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ goto leave;
+ }
+
+ ok = 1;
+ ldap_pvt_thread_mutex_lock( &sort_conns_mutex );
+ /* Is there already a sort running on this conn? */
+ sess_id = find_session_by_context( si->svi_max_percon, op->o_conn->c_conn_idx, vc ? vc->vc_context : NO_VC_CONTEXT, ps ? ps->ps_cookie : NO_PS_COOKIE );
+ if ( sess_id >= 0 ) {
+ so = sort_conns[op->o_conn->c_conn_idx][sess_id];
+
+ if( so->so_running > 0 ){
+ /* another thread is handling, response busy to client */
+ so = NULL;
+ ok = 0;
+ } else {
+
+ /* Is it a continuation of a VLV search? */
+ if ( !vc || so->so_vlv <= SLAP_CONTROL_IGNORED ||
+ vc->vc_context != so->so_vcontext ) {
+ /* Is it a continuation of a paged search? */
+ if ( !ps || so->so_paged <= SLAP_CONTROL_IGNORED ||
+ op->o_conn->c_pagedresults_state.ps_cookie != ps->ps_cookie ) {
+ ok = 0;
+ } else if ( !ps->ps_size ) {
+ /* Abandoning current request */
+ ok = 0;
+ so->so_nentries = 0;
+ rs->sr_err = LDAP_SUCCESS;
+ }
+ }
+ if (( vc && so->so_paged > SLAP_CONTROL_IGNORED ) ||
+ ( ps && so->so_vlv > SLAP_CONTROL_IGNORED )) {
+ /* changed from paged to vlv or vice versa, abandon */
+ ok = 0;
+ so->so_nentries = 0;
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ if ( ok ) {
+ /* occupy before mutex unlock */
+ so->so_running = 1;
+ }
+
+ }
+ /* Are there too many running overall? */
+ } else if ( si->svi_num >= si->svi_max ) {
+ ok = 0;
+ } else if ( ( sess_id = find_next_session(si->svi_max_percon, op->o_conn->c_conn_idx ) ) < 0 ) {
+ ok = 0;
+ } else {
+ /* OK, this connection now has a sort running */
+ si->svi_num++;
+ sort_conns[op->o_conn->c_conn_idx][sess_id] = &so2;
+ sort_conns[op->o_conn->c_conn_idx][sess_id]->so_session = sess_id;
+ }
+ ldap_pvt_thread_mutex_unlock( &sort_conns_mutex );
+ if ( ok ) {
+ /* If we're a global overlay, this check got bypassed */
+ if ( !op->ors_limit && limits_check( op, rs ))
+ return rs->sr_err;
+ /* are we continuing a VLV search? */
+ if ( so && vc && vc->vc_context ) {
+ so->so_ctrl = sc;
+ send_list( op, rs, so );
+ send_result( op, rs, so );
+ rc = LDAP_SUCCESS;
+ /* are we continuing a paged search? */
+ } else if ( so && ps && ps->ps_cookie ) {
+ so->so_ctrl = sc;
+ send_page( op, rs, so );
+ send_result( op, rs, so );
+ rc = LDAP_SUCCESS;
+ } else {
+ slap_callback *cb = op->o_tmpalloc( sizeof(slap_callback),
+ op->o_tmpmemctx );
+ /* Install serversort response callback to handle a new search */
+ if ( ps || vc ) {
+ so = ch_calloc( 1, sizeof(sort_op));
+ } else {
+ so = op->o_tmpcalloc( 1, sizeof(sort_op), op->o_tmpmemctx );
+ }
+ sort_conns[op->o_conn->c_conn_idx][sess_id] = so;
+
+ cb->sc_cleanup = NULL;
+ cb->sc_response = sssvlv_op_response;
+ cb->sc_next = op->o_callback;
+ cb->sc_private = so;
+ cb->sc_writewait = NULL;
+
+ so->so_tree = NULL;
+ so->so_ctrl = sc;
+ so->so_info = si;
+ if ( ps ) {
+ so->so_paged = op->o_pagedresults;
+ so->so_page_size = ps->ps_size;
+ op->o_pagedresults = SLAP_CONTROL_IGNORED;
+ } else {
+ so->so_paged = 0;
+ so->so_page_size = 0;
+ if ( vc ) {
+ so->so_vlv = op->o_ctrlflag[vlv_cid];
+ so->so_vlv_target = 0;
+ so->so_vlv_rc = 0;
+ } else {
+ so->so_vlv = SLAP_CONTROL_NONE;
+ }
+ }
+ so->so_session = sess_id;
+ so->so_vlv = op->o_ctrlflag[vlv_cid];
+ so->so_vcontext = (unsigned long)so;
+ so->so_nentries = 0;
+ so->so_running = 1;
+
+ op->o_callback = cb;
+ }
+ } else {
+ if ( so && !so->so_nentries ) {
+ free_sort_op( op->o_conn, so );
+ } else {
+ rs->sr_text = "Other sort requests already in progress";
+ rs->sr_err = LDAP_BUSY;
+ }
+leave:
+ rc = rs->sr_err;
+ send_ldap_result( op, rs );
+ }
+
+ return rc;
+}
+
+static int get_ordering_rule(
+ AttributeDescription *ad,
+ struct berval *matchrule,
+ SlapReply *rs,
+ MatchingRule **ordering )
+{
+ MatchingRule* mr;
+
+ if ( matchrule && matchrule->bv_val ) {
+ mr = mr_find( matchrule->bv_val );
+ if ( mr == NULL ) {
+ rs->sr_err = LDAP_INAPPROPRIATE_MATCHING;
+ rs->sr_text = "serverSort control: No ordering rule";
+ Debug(LDAP_DEBUG_TRACE, "%s: no ordering rule function for %s\n",
+ debug_header, matchrule->bv_val );
+ }
+ }
+ else {
+ mr = ad->ad_type->sat_ordering;
+ if ( mr == NULL ) {
+ rs->sr_err = LDAP_INAPPROPRIATE_MATCHING;
+ rs->sr_text = "serverSort control: No ordering rule";
+ Debug(LDAP_DEBUG_TRACE,
+ "%s: no ordering rule specified and no default ordering rule for attribute %s\n",
+ debug_header, ad->ad_cname.bv_val );
+ }
+ }
+
+ *ordering = mr;
+ return rs->sr_err;
+}
+
+static int count_key(BerElement *ber)
+{
+ char *end;
+ ber_len_t len;
+ ber_tag_t tag;
+ int count = 0;
+
+ /* Server Side Sort Control is a SEQUENCE of SEQUENCE */
+ for ( tag = ber_first_element( ber, &len, &end );
+ tag == LBER_SEQUENCE;
+ tag = ber_next_element( ber, &len, end ))
+ {
+ tag = ber_skip_tag( ber, &len );
+ ber_skip_data( ber, len );
+ ++count;
+ }
+ ber_rewind( ber );
+
+ return count;
+}
+
+static int build_key(
+ BerElement *ber,
+ SlapReply *rs,
+ sort_key *key )
+{
+ struct berval attr;
+ struct berval matchrule = BER_BVNULL;
+ ber_int_t reverse = 0;
+ ber_tag_t tag;
+ ber_len_t len;
+ MatchingRule *ordering = NULL;
+ AttributeDescription *ad = NULL;
+ const char *text;
+
+ if (( tag = ber_scanf( ber, "{" )) == LBER_ERROR ) {
+ rs->sr_text = "serverSort control: decoding error";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ return rs->sr_err;
+ }
+
+ if (( tag = ber_scanf( ber, "m", &attr )) == LBER_ERROR ) {
+ rs->sr_text = "serverSort control: attribute decoding error";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ return rs->sr_err;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LDAP_MATCHRULE_IDENTIFIER ) {
+ if (( tag = ber_scanf( ber, "m", &matchrule )) == LBER_ERROR ) {
+ rs->sr_text = "serverSort control: matchrule decoding error";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ return rs->sr_err;
+ }
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag == LDAP_REVERSEORDER_IDENTIFIER ) {
+ if (( tag = ber_scanf( ber, "b", &reverse )) == LBER_ERROR ) {
+ rs->sr_text = "serverSort control: reverse decoding error";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ return rs->sr_err;
+ }
+ }
+
+ if (( tag = ber_scanf( ber, "}" )) == LBER_ERROR ) {
+ rs->sr_text = "serverSort control: decoding error";
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ return rs->sr_err;
+ }
+
+ if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS ) {
+ rs->sr_text =
+ "serverSort control: Unrecognized attribute type in sort key";
+ Debug(LDAP_DEBUG_TRACE,
+ "%s: Unrecognized attribute type in sort key: %s\n",
+ debug_header, SAFESTR(attr.bv_val, "<None>") );
+ rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
+ return rs->sr_err;
+ }
+
+ /* get_ordering_rule will set sr_err and sr_text */
+ get_ordering_rule( ad, &matchrule, rs, &ordering );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ return rs->sr_err;
+ }
+
+ key->sk_ad = ad;
+ key->sk_ordering = ordering;
+ key->sk_direction = reverse ? -1 : 1;
+
+ return rs->sr_err;
+}
+
+/* Conforms to RFC4510 re: Criticality, original RFC2891 spec is broken
+ * Also see ITS#7253 for discussion
+ */
+static int sss_parseCtrl(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber;
+ ber_tag_t tag;
+ ber_len_t len;
+ int i;
+ sort_ctrl *sc;
+
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+
+ if ( op->o_ctrlflag[sss_cid] > SLAP_CONTROL_IGNORED ) {
+ rs->sr_text = "sorted results control specified multiple times";
+ } else if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "sorted results control value is absent";
+ } else if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "sorted results control value is empty";
+ } else {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+ if ( rs->sr_err != LDAP_SUCCESS )
+ return rs->sr_err;
+
+ op->o_ctrlflag[sss_cid] = ctrl->ldctl_iscritical ?
+ SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL;
+
+ ber = (BerElement *)&berbuf;
+ ber_init2( ber, &ctrl->ldctl_value, 0 );
+ i = count_key( ber );
+
+ sc = op->o_tmpalloc( sizeof(sort_ctrl) +
+ (i-1) * sizeof(sort_key), op->o_tmpmemctx );
+ sc->sc_nkeys = i;
+ op->o_controls[sss_cid] = sc;
+
+ /* peel off initial sequence */
+ ber_scanf( ber, "{" );
+
+ i = 0;
+ do {
+ if ( build_key( ber, rs, &sc->sc_keys[i] ) != LDAP_SUCCESS )
+ break;
+ i++;
+ tag = ber_peek_tag( ber, &len );
+ } while ( tag != LBER_DEFAULT );
+
+ return rs->sr_err;
+}
+
+static int vlv_parseCtrl(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber;
+ ber_tag_t tag;
+ ber_len_t len;
+ vlv_ctrl *vc, vc2;
+
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ rs->sr_text = NULL;
+
+ if ( op->o_ctrlflag[vlv_cid] > SLAP_CONTROL_IGNORED ) {
+ rs->sr_text = "vlv control specified multiple times";
+ } else if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "vlv control value is absent";
+ } else if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "vlv control value is empty";
+ }
+ if ( rs->sr_text != NULL )
+ return rs->sr_err;
+
+ op->o_ctrlflag[vlv_cid] = ctrl->ldctl_iscritical ?
+ SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL;
+
+ ber = (BerElement *)&berbuf;
+ ber_init2( ber, &ctrl->ldctl_value, 0 );
+
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+
+ tag = ber_scanf( ber, "{ii", &vc2.vc_before, &vc2.vc_after );
+ if ( tag == LBER_ERROR ) {
+ return rs->sr_err;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LDAP_VLVBYINDEX_IDENTIFIER ) {
+ tag = ber_scanf( ber, "{ii}", &vc2.vc_offset, &vc2.vc_count );
+ if ( tag == LBER_ERROR )
+ return rs->sr_err;
+ BER_BVZERO( &vc2.vc_value );
+ } else if ( tag == LDAP_VLVBYVALUE_IDENTIFIER ) {
+ tag = ber_scanf( ber, "m", &vc2.vc_value );
+ if ( tag == LBER_ERROR || BER_BVISNULL( &vc2.vc_value ))
+ return rs->sr_err;
+ } else {
+ return rs->sr_err;
+ }
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LDAP_VLVCONTEXT_IDENTIFIER ) {
+ struct berval bv;
+ tag = ber_scanf( ber, "m", &bv );
+ if ( tag == LBER_ERROR || bv.bv_len != sizeof(vc2.vc_context))
+ return rs->sr_err;
+ AC_MEMCPY( &vc2.vc_context, bv.bv_val, bv.bv_len );
+ } else {
+ vc2.vc_context = 0;
+ }
+
+ vc = op->o_tmpalloc( sizeof(vlv_ctrl), op->o_tmpmemctx );
+ *vc = vc2;
+ op->o_controls[vlv_cid] = vc;
+ rs->sr_err = LDAP_SUCCESS;
+
+ return rs->sr_err;
+}
+
+static int sssvlv_connection_destroy( BackendDB *be, Connection *conn )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ sssvlv_info *si = on->on_bi.bi_private;
+
+ if ( sort_conns[conn->c_conn_idx] ) {
+ free_sort_ops( conn, sort_conns[conn->c_conn_idx], si->svi_max_percon );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int sssvlv_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ sssvlv_info *si = on->on_bi.bi_private;
+ int rc;
+ int conn_index;
+
+ /* If not set, default to 1/2 of available threads */
+ if ( !si->svi_max )
+ si->svi_max = connection_pool_max / 2;
+
+ if ( dtblsize && !sort_conns ) {
+ ldap_pvt_thread_mutex_init( &sort_conns_mutex );
+ /* accommodate for c_conn_idx == -1 */
+ sort_conns = ch_calloc( dtblsize + 1, sizeof(sort_op **) );
+ for ( conn_index = 0 ; conn_index < dtblsize + 1 ; conn_index++ ) {
+ sort_conns[conn_index] = ch_calloc( si->svi_max_percon, sizeof(sort_op *) );
+ }
+ sort_conns++;
+ }
+
+ rc = overlay_register_control( be, LDAP_CONTROL_SORTREQUEST );
+ if ( rc == LDAP_SUCCESS )
+ rc = overlay_register_control( be, LDAP_CONTROL_VLVREQUEST );
+ return rc;
+}
+
+static ConfigTable sssvlv_cfg[] = {
+ { "sssvlv-max", "num",
+ 2, 2, 0, ARG_INT|ARG_OFFSET,
+ (void *)offsetof(sssvlv_info, svi_max),
+ "( OLcfgOvAt:21.1 NAME 'olcSssVlvMax' "
+ "DESC 'Maximum number of concurrent Sort requests' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "sssvlv-maxkeys", "num",
+ 2, 2, 0, ARG_INT|ARG_OFFSET,
+ (void *)offsetof(sssvlv_info, svi_max_keys),
+ "( OLcfgOvAt:21.2 NAME 'olcSssVlvMaxKeys' "
+ "DESC 'Maximum number of Keys in a Sort request' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL,
+ { .v_int = SSSVLV_DEFAULT_MAX_KEYS } },
+ { "sssvlv-maxperconn", "num",
+ 2, 2, 0, ARG_INT|ARG_OFFSET,
+ (void *)offsetof(sssvlv_info, svi_max_percon),
+ "( OLcfgOvAt:21.3 NAME 'olcSssVlvMaxPerConn' "
+ "DESC 'Maximum number of concurrent paged search requests per connection' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL,
+ { .v_int = SSSVLV_DEFAULT_MAX_REQUEST_PER_CONN } },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs sssvlv_ocs[] = {
+ { "( OLcfgOvOc:21.1 "
+ "NAME 'olcSssVlvConfig' "
+ "DESC 'SSS VLV configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcSssVlvMax $ olcSssVlvMaxKeys $ olcSssVlvMaxPerConn ) )",
+ Cft_Overlay, sssvlv_cfg, NULL, NULL },
+ { NULL, 0, NULL }
+};
+
+static int sssvlv_db_init(
+ BackendDB *be,
+ ConfigReply *cr)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ sssvlv_info *si;
+
+ if ( ov_count == 0 ) {
+ int rc;
+
+ rc = register_supported_control2( LDAP_CONTROL_SORTREQUEST,
+ SLAP_CTRL_SEARCH,
+ NULL,
+ sss_parseCtrl,
+ 1 /* replace */,
+ &sss_cid );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "Failed to register Sort Request control '%s' (%d)\n",
+ LDAP_CONTROL_SORTREQUEST, rc );
+ return rc;
+ }
+
+ rc = register_supported_control2( LDAP_CONTROL_VLVREQUEST,
+ SLAP_CTRL_SEARCH,
+ NULL,
+ vlv_parseCtrl,
+ 1 /* replace */,
+ &vlv_cid );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "Failed to register VLV Request control '%s' (%d)\n",
+ LDAP_CONTROL_VLVREQUEST, rc );
+#ifdef SLAP_CONFIG_DELETE
+ overlay_unregister_control( be, LDAP_CONTROL_SORTREQUEST );
+ unregister_supported_control( LDAP_CONTROL_SORTREQUEST );
+#endif /* SLAP_CONFIG_DELETE */
+ return rc;
+ }
+ }
+
+ si = (sssvlv_info *)ch_malloc(sizeof(sssvlv_info));
+ on->on_bi.bi_private = si;
+
+ si->svi_max = 0;
+ si->svi_num = 0;
+ si->svi_max_keys = SSSVLV_DEFAULT_MAX_KEYS;
+ si->svi_max_percon = SSSVLV_DEFAULT_MAX_REQUEST_PER_CONN;
+
+ ov_count++;
+
+ return LDAP_SUCCESS;
+}
+
+static int sssvlv_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ sssvlv_info *si = (sssvlv_info *)on->on_bi.bi_private;
+ int conn_index;
+
+ ov_count--;
+ if ( !ov_count && sort_conns) {
+ sort_conns--;
+ for ( conn_index = 0 ; conn_index < dtblsize + 1 ; conn_index++ ) {
+ ch_free(sort_conns[conn_index]);
+ }
+ ch_free(sort_conns);
+ ldap_pvt_thread_mutex_destroy( &sort_conns_mutex );
+ }
+
+#ifdef SLAP_CONFIG_DELETE
+ overlay_unregister_control( be, LDAP_CONTROL_SORTREQUEST );
+ overlay_unregister_control( be, LDAP_CONTROL_VLVREQUEST );
+ if ( ov_count == 0 ) {
+ unregister_supported_control( LDAP_CONTROL_SORTREQUEST );
+ unregister_supported_control( LDAP_CONTROL_VLVREQUEST );
+ }
+#endif /* SLAP_CONFIG_DELETE */
+
+ if ( si ) {
+ ch_free( si );
+ on->on_bi.bi_private = NULL;
+ }
+ return LDAP_SUCCESS;
+}
+
+static slap_overinst sssvlv;
+
+int sssvlv_initialize()
+{
+ int rc;
+
+ sssvlv.on_bi.bi_type = "sssvlv";
+ sssvlv.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ sssvlv.on_bi.bi_db_init = sssvlv_db_init;
+ sssvlv.on_bi.bi_db_destroy = sssvlv_db_destroy;
+ sssvlv.on_bi.bi_db_open = sssvlv_db_open;
+ sssvlv.on_bi.bi_connection_destroy = sssvlv_connection_destroy;
+ sssvlv.on_bi.bi_op_search = sssvlv_op_search;
+
+ sssvlv.on_bi.bi_cf_ocs = sssvlv_ocs;
+
+ rc = config_register_schema( sssvlv_cfg, sssvlv_ocs );
+ if ( rc )
+ return rc;
+
+ rc = overlay_register( &sssvlv );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "Failed to register server side sort overlay\n" );
+ }
+
+ return rc;
+}
+
+#if SLAPD_OVER_SSSVLV == SLAPD_MOD_DYNAMIC
+int init_module( int argc, char *argv[])
+{
+ return sssvlv_initialize();
+}
+#endif
+
+#endif /* SLAPD_OVER_SSSVLV */
diff --git a/servers/slapd/overlays/syncprov.c b/servers/slapd/overlays/syncprov.c
new file mode 100644
index 0000000..6d749a5
--- /dev/null
+++ b/servers/slapd/overlays/syncprov.c
@@ -0,0 +1,4368 @@
+/* $OpenLDAP$ */
+/* syncprov.c - syncrepl provider */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_SYNCPROV
+
+#include <ac/string.h>
+#include "lutil.h"
+#include "slap.h"
+#include "slap-config.h"
+#include "ldap_rq.h"
+
+#ifdef LDAP_DEVEL
+#define CHECK_CSN 1
+#endif
+
+/* A modify request on a particular entry */
+typedef struct modinst {
+ struct modinst *mi_next;
+ Operation *mi_op;
+} modinst;
+
+typedef struct modtarget {
+ struct modinst *mt_mods;
+ struct modinst *mt_tail;
+ struct berval mt_dn;
+ ldap_pvt_thread_mutex_t mt_mutex;
+} modtarget;
+
+/* All the info of a psearch result that's shared between
+ * multiple queues
+ */
+typedef struct resinfo {
+ struct syncres *ri_list;
+ Entry *ri_e;
+ struct berval ri_dn;
+ struct berval ri_ndn;
+ struct berval ri_uuid;
+ struct berval ri_csn;
+ struct berval ri_cookie;
+ char ri_isref;
+ ldap_pvt_thread_mutex_t ri_mutex;
+} resinfo;
+
+/* A queued result of a persistent search */
+typedef struct syncres {
+ struct syncres *s_next; /* list of results on this psearch queue */
+ struct syncres *s_rilist; /* list of psearches using this result */
+ resinfo *s_info;
+ char s_mode;
+} syncres;
+
+/* Record of a persistent search */
+typedef struct syncops {
+ struct syncops *s_next;
+ struct syncprov_info_t *s_si;
+ struct berval s_base; /* ndn of search base */
+ ID s_eid; /* entryID of search base */
+ Operation *s_op; /* search op */
+ int s_rid;
+ int s_sid;
+ struct berval s_filterstr;
+ int s_flags; /* search status */
+#define PS_IS_REFRESHING 0x01
+#define PS_IS_DETACHED 0x02
+#define PS_WROTE_BASE 0x04
+#define PS_FIND_BASE 0x08
+#define PS_FIX_FILTER 0x10
+#define PS_TASK_QUEUED 0x20
+
+ int s_inuse; /* reference count */
+ struct syncres *s_res;
+ struct syncres *s_restail;
+ void *s_pool_cookie;
+ ldap_pvt_thread_mutex_t s_mutex;
+} syncops;
+
+/* A received sync control */
+typedef struct sync_control {
+ struct sync_cookie sr_state;
+ int sr_rhint;
+} sync_control;
+
+#if 0 /* moved back to slap.h */
+#define o_sync o_ctrlflag[slap_cids.sc_LDAPsync]
+#endif
+/* o_sync_mode uses data bits of o_sync */
+#define o_sync_mode o_ctrlflag[slap_cids.sc_LDAPsync]
+
+#define SLAP_SYNC_NONE (LDAP_SYNC_NONE<<SLAP_CONTROL_SHIFT)
+#define SLAP_SYNC_REFRESH (LDAP_SYNC_REFRESH_ONLY<<SLAP_CONTROL_SHIFT)
+#define SLAP_SYNC_PERSIST (LDAP_SYNC_RESERVED<<SLAP_CONTROL_SHIFT)
+#define SLAP_SYNC_REFRESH_AND_PERSIST (LDAP_SYNC_REFRESH_AND_PERSIST<<SLAP_CONTROL_SHIFT)
+
+/* Record of which searches matched at premodify step */
+typedef struct syncmatches {
+ struct syncmatches *sm_next;
+ syncops *sm_op;
+} syncmatches;
+
+/* Session log data */
+typedef struct slog_entry {
+ struct berval se_uuid;
+ struct berval se_csn;
+ int se_sid;
+ ber_tag_t se_tag;
+} slog_entry;
+
+typedef struct sessionlog {
+ BerVarray sl_mincsn;
+ int *sl_sids;
+ int sl_numcsns;
+ int sl_num;
+ int sl_size;
+ int sl_playing;
+ TAvlnode *sl_entries;
+ ldap_pvt_thread_rdwr_t sl_mutex;
+} sessionlog;
+
+/* Accesslog callback data */
+typedef struct syncprov_accesslog_deletes {
+ Operation *op;
+ SlapReply *rs;
+ sync_control *srs;
+ BerVarray ctxcsn;
+ int numcsns, *sids;
+ Avlnode *uuids;
+ BerVarray uuid_list;
+ int ndel, list_len;
+ char *uuid_buf;
+} syncprov_accesslog_deletes;
+
+/* The main state for this overlay */
+typedef struct syncprov_info_t {
+ syncops *si_ops;
+ struct berval si_contextdn;
+ struct berval si_logbase;
+ BerVarray si_ctxcsn; /* ldapsync context */
+ int *si_sids;
+ int si_numcsns;
+ int si_chkops; /* checkpointing info */
+ int si_chktime;
+ int si_numops; /* number of ops since last checkpoint */
+ int si_nopres; /* Skip present phase */
+ int si_usehint; /* use reload hint */
+ int si_active; /* True if there are active mods */
+ int si_dirty; /* True if the context is dirty, i.e changes
+ * have been made without updating the csn. */
+ time_t si_chklast; /* time of last checkpoint */
+ Avlnode *si_mods; /* entries being modified */
+ sessionlog *si_logs;
+ ldap_pvt_thread_rdwr_t si_csn_rwlock;
+ ldap_pvt_thread_mutex_t si_ops_mutex;
+ ldap_pvt_thread_mutex_t si_mods_mutex;
+ ldap_pvt_thread_mutex_t si_resp_mutex;
+} syncprov_info_t;
+
+typedef struct opcookie {
+ slap_overinst *son;
+ syncmatches *smatches;
+ modtarget *smt;
+ Entry *se;
+ struct berval sdn; /* DN of entry, for deletes */
+ struct berval sndn;
+ struct berval suuid; /* UUID of entry */
+ struct berval sctxcsn;
+ short osid; /* sid of op csn */
+ short rsid; /* sid of relay */
+ short sreference; /* Is the entry a reference? */
+ syncres ssres;
+} opcookie;
+
+typedef struct fbase_cookie {
+ struct berval *fdn; /* DN of a modified entry, for scope testing */
+ syncops *fss; /* persistent search we're testing against */
+ int fbase; /* if TRUE we found the search base and it's still valid */
+ int fscope; /* if TRUE then fdn is within the psearch scope */
+} fbase_cookie;
+
+static AttributeName csn_anlist[3];
+static AttributeName uuid_anlist[2];
+
+static AttributeDescription *ad_reqType, *ad_reqResult, *ad_reqDN,
+ *ad_reqEntryUUID, *ad_minCSN, *ad_reqNewDN;
+
+/* Build a LDAPsync intermediate state control */
+static int
+syncprov_state_ctrl(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ int entry_sync_state,
+ LDAPControl **ctrls,
+ int num_ctrls,
+ int send_cookie,
+ struct berval *cookie )
+{
+ Attribute* a;
+ int ret;
+
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ LDAPControl *cp;
+ struct berval bv;
+ struct berval entryuuid_bv = BER_BVNULL;
+
+ ber_init2( ber, 0, LBER_USE_DER );
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+
+ for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
+ AttributeDescription *desc = a->a_desc;
+ if ( desc == slap_schema.si_ad_entryUUID ) {
+ entryuuid_bv = a->a_nvals[0];
+ break;
+ }
+ }
+
+ /* FIXME: what if entryuuid is NULL or empty ? */
+
+ if ( send_cookie && cookie ) {
+ ber_printf( ber, "{eOON}",
+ entry_sync_state, &entryuuid_bv, cookie );
+ } else {
+ ber_printf( ber, "{eON}",
+ entry_sync_state, &entryuuid_bv );
+ }
+
+ ret = ber_flatten2( ber, &bv, 0 );
+ if ( ret == 0 ) {
+ cp = op->o_tmpalloc( sizeof( LDAPControl ) + bv.bv_len, op->o_tmpmemctx );
+ cp->ldctl_oid = LDAP_CONTROL_SYNC_STATE;
+ cp->ldctl_iscritical = (op->o_sync == SLAP_CONTROL_CRITICAL);
+ cp->ldctl_value.bv_val = (char *)&cp[1];
+ cp->ldctl_value.bv_len = bv.bv_len;
+ AC_MEMCPY( cp->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
+ ctrls[num_ctrls] = cp;
+ }
+ ber_free_buf( ber );
+
+ if ( ret < 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "slap_build_sync_ctrl: ber_flatten2 failed (%d)\n",
+ ret );
+ send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
+ return LDAP_OTHER;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/* Build a LDAPsync final state control */
+static int
+syncprov_done_ctrl(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl **ctrls,
+ int num_ctrls,
+ int send_cookie,
+ struct berval *cookie,
+ int refreshDeletes )
+{
+ int ret;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ LDAPControl *cp;
+ struct berval bv;
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+
+ ber_printf( ber, "{" );
+ if ( send_cookie && cookie ) {
+ ber_printf( ber, "O", cookie );
+ }
+ if ( refreshDeletes == LDAP_SYNC_REFRESH_DELETES ) {
+ ber_printf( ber, "b", refreshDeletes );
+ }
+ ber_printf( ber, "N}" );
+
+ ret = ber_flatten2( ber, &bv, 0 );
+ if ( ret == 0 ) {
+ cp = op->o_tmpalloc( sizeof( LDAPControl ) + bv.bv_len, op->o_tmpmemctx );
+ cp->ldctl_oid = LDAP_CONTROL_SYNC_DONE;
+ cp->ldctl_iscritical = (op->o_sync == SLAP_CONTROL_CRITICAL);
+ cp->ldctl_value.bv_val = (char *)&cp[1];
+ cp->ldctl_value.bv_len = bv.bv_len;
+ AC_MEMCPY( cp->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
+ ctrls[num_ctrls] = cp;
+ }
+
+ ber_free_buf( ber );
+
+ if ( ret < 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "syncprov_done_ctrl: ber_flatten2 failed (%d)\n",
+ ret );
+ send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
+ return LDAP_OTHER;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+syncprov_sendinfo(
+ Operation *op,
+ SlapReply *rs,
+ int type,
+ struct berval *cookie,
+ int refreshDone,
+ BerVarray syncUUIDs,
+ int refreshDeletes )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ struct berval rspdata;
+
+ int ret;
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+
+ if ( type ) {
+ switch ( type ) {
+ case LDAP_TAG_SYNC_NEW_COOKIE:
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendinfo: "
+ "sending a new cookie=%s\n",
+ op->o_log_prefix, cookie->bv_val );
+ ber_printf( ber, "tO", type, cookie );
+ break;
+ case LDAP_TAG_SYNC_REFRESH_DELETE:
+ case LDAP_TAG_SYNC_REFRESH_PRESENT:
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendinfo: "
+ "%s cookie=%s\n",
+ op->o_log_prefix,
+ type == LDAP_TAG_SYNC_REFRESH_DELETE ? "refreshDelete" : "refreshPresent",
+ cookie ? cookie->bv_val : "" );
+ ber_printf( ber, "t{", type );
+ if ( cookie ) {
+ ber_printf( ber, "O", cookie );
+ }
+ if ( refreshDone == 0 ) {
+ ber_printf( ber, "b", refreshDone );
+ }
+ ber_printf( ber, "N}" );
+ break;
+ case LDAP_TAG_SYNC_ID_SET:
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendinfo: "
+ "%s syncIdSet cookie=%s\n",
+ op->o_log_prefix, refreshDeletes ? "delete" : "present",
+ cookie ? cookie->bv_val : "" );
+ ber_printf( ber, "t{", type );
+ if ( cookie ) {
+ ber_printf( ber, "O", cookie );
+ }
+ if ( refreshDeletes == 1 ) {
+ ber_printf( ber, "b", refreshDeletes );
+ }
+ ber_printf( ber, "[W]", syncUUIDs );
+ ber_printf( ber, "N}" );
+ break;
+ default:
+ Debug( LDAP_DEBUG_TRACE,
+ "%s syncprov_sendinfo: invalid syncinfo type (%d)\n",
+ op->o_log_prefix, type );
+ return LDAP_OTHER;
+ }
+ }
+
+ ret = ber_flatten2( ber, &rspdata, 0 );
+
+ if ( ret < 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "syncprov_sendinfo: ber_flatten2 failed (%d)\n",
+ ret );
+ send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
+ return LDAP_OTHER;
+ }
+
+ rs->sr_rspoid = LDAP_SYNC_INFO;
+ rs->sr_rspdata = &rspdata;
+ send_ldap_intermediate( op, rs );
+ rs->sr_rspdata = NULL;
+ ber_free_buf( ber );
+
+ return LDAP_SUCCESS;
+}
+
+/* Find a modtarget in an AVL tree */
+static int
+sp_avl_cmp( const void *c1, const void *c2 )
+{
+ const modtarget *m1, *m2;
+ int rc;
+
+ m1 = c1; m2 = c2;
+ rc = m1->mt_dn.bv_len - m2->mt_dn.bv_len;
+
+ if ( rc ) return rc;
+ return ber_bvcmp( &m1->mt_dn, &m2->mt_dn );
+}
+
+static int
+sp_uuid_cmp( const void *l, const void *r )
+{
+ const struct berval *left = l, *right = r;
+
+ return ber_bvcmp( left, right );
+}
+
+static int
+syncprov_sessionlog_cmp( const void *l, const void *r )
+{
+ const slog_entry *left = l, *right = r;
+ int ret = ber_bvcmp( &left->se_csn, &right->se_csn );
+ if ( !ret )
+ ret = ber_bvcmp( &left->se_uuid, &right->se_uuid );
+ /* Only time we have two modifications with same CSN is when we detect a
+ * rename during replication.
+ * We invert the test here because LDAP_REQ_MODDN is
+ * numerically greater than LDAP_REQ_MODIFY but we
+ * want it to occur first.
+ */
+ if ( !ret )
+ ret = right->se_tag - left->se_tag;
+
+ return ret;
+}
+
+/* syncprov_findbase:
+ * finds the true DN of the base of a search (with alias dereferencing) and
+ * checks to make sure the base entry doesn't get replaced with a different
+ * entry (e.g., swapping trees via ModDN, or retargeting an alias). If a
+ * change is detected, any persistent search on this base must be terminated /
+ * reloaded.
+ * On the first call, we just save the DN and entryID. On subsequent calls
+ * we compare the DN and entryID with the saved values.
+ */
+static int
+findbase_cb( Operation *op, SlapReply *rs )
+{
+ slap_callback *sc = op->o_callback;
+
+ if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) {
+ fbase_cookie *fc = sc->sc_private;
+
+ /* If no entryID, we're looking for the first time.
+ * Just store whatever we got.
+ */
+ if ( fc->fss->s_eid == NOID ) {
+ fc->fbase = 2;
+ fc->fss->s_eid = rs->sr_entry->e_id;
+ ber_dupbv( &fc->fss->s_base, &rs->sr_entry->e_nname );
+
+ } else if ( rs->sr_entry->e_id == fc->fss->s_eid &&
+ dn_match( &rs->sr_entry->e_nname, &fc->fss->s_base )) {
+
+ /* OK, the DN is the same and the entryID is the same. */
+ fc->fbase = 1;
+ }
+ }
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "findbase failed! %d\n", rs->sr_err );
+ }
+ return LDAP_SUCCESS;
+}
+
+static Filter generic_filter = { LDAP_FILTER_PRESENT, { 0 }, NULL };
+static struct berval generic_filterstr = BER_BVC("(objectclass=*)");
+
+static int
+syncprov_findbase( Operation *op, fbase_cookie *fc )
+{
+ /* Use basic parameters from syncrepl search, but use
+ * current op's threadctx / tmpmemctx
+ */
+ ldap_pvt_thread_mutex_lock( &fc->fss->s_mutex );
+ if ( fc->fss->s_flags & PS_FIND_BASE ) {
+ slap_callback cb = {0};
+ Operation fop;
+ SlapReply frs = { REP_RESULT };
+ int rc;
+
+ fc->fss->s_flags ^= PS_FIND_BASE;
+ ldap_pvt_thread_mutex_unlock( &fc->fss->s_mutex );
+
+ fop = *fc->fss->s_op;
+
+ fop.o_bd = fop.o_bd->bd_self;
+ fop.o_hdr = op->o_hdr;
+ fop.o_time = op->o_time;
+ fop.o_tincr = op->o_tincr;
+ fop.o_extra = op->o_extra;
+
+ cb.sc_response = findbase_cb;
+ cb.sc_private = fc;
+
+ fop.o_sync_mode = 0; /* turn off sync mode */
+ fop.o_managedsait = SLAP_CONTROL_CRITICAL;
+ fop.o_callback = &cb;
+ fop.o_tag = LDAP_REQ_SEARCH;
+ fop.ors_scope = LDAP_SCOPE_BASE;
+ fop.ors_limit = NULL;
+ fop.ors_slimit = 1;
+ fop.ors_tlimit = SLAP_NO_LIMIT;
+ fop.ors_attrs = slap_anlist_no_attrs;
+ fop.ors_attrsonly = 1;
+ fop.ors_filter = &generic_filter;
+ fop.ors_filterstr = generic_filterstr;
+
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_findbase: searching\n", op->o_log_prefix );
+ rc = fop.o_bd->be_search( &fop, &frs );
+ } else {
+ ldap_pvt_thread_mutex_unlock( &fc->fss->s_mutex );
+ fc->fbase = 1;
+ }
+
+ /* After the first call, see if the fdn resides in the scope */
+ if ( fc->fbase == 1 ) {
+ switch ( fc->fss->s_op->ors_scope ) {
+ case LDAP_SCOPE_BASE:
+ fc->fscope = dn_match( fc->fdn, &fc->fss->s_base );
+ break;
+ case LDAP_SCOPE_ONELEVEL: {
+ struct berval pdn;
+ dnParent( fc->fdn, &pdn );
+ fc->fscope = dn_match( &pdn, &fc->fss->s_base );
+ break; }
+ case LDAP_SCOPE_SUBTREE:
+ fc->fscope = dnIsSuffix( fc->fdn, &fc->fss->s_base );
+ break;
+ case LDAP_SCOPE_SUBORDINATE:
+ fc->fscope = dnIsSuffix( fc->fdn, &fc->fss->s_base ) &&
+ !dn_match( fc->fdn, &fc->fss->s_base );
+ break;
+ }
+ }
+
+ if ( fc->fbase )
+ return LDAP_SUCCESS;
+
+ /* If entryID has changed, then the base of this search has
+ * changed. Invalidate the psearch.
+ */
+ return LDAP_NO_SUCH_OBJECT;
+}
+
+/* syncprov_findcsn:
+ * This function has three different purposes, but they all use a search
+ * that filters on entryCSN so they're combined here.
+ * 1: at startup time, after a contextCSN has been read from the database,
+ * we search for all entries with CSN >= contextCSN in case the contextCSN
+ * was not checkpointed at the previous shutdown.
+ *
+ * 2: when the current contextCSN is known and we have a sync cookie, we search
+ * for one entry with CSN = the cookie CSN. If not found, try <= cookie CSN.
+ * If an entry is found, the cookie CSN is valid, otherwise it is stale.
+ *
+ * 3: during a refresh phase, we search for all entries with CSN <= the cookie
+ * CSN, and generate Present records for them. We always collect this result
+ * in SyncID sets, even if there's only one match.
+ */
+typedef enum find_csn_t {
+ FIND_MAXCSN = 1,
+ FIND_CSN = 2,
+ FIND_PRESENT = 3
+} find_csn_t;
+
+static int
+findmax_cb( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) {
+ struct berval *maxcsn = op->o_callback->sc_private;
+ Attribute *a = attr_find( rs->sr_entry->e_attrs,
+ slap_schema.si_ad_entryCSN );
+
+ if ( a && ber_bvcmp( &a->a_vals[0], maxcsn ) > 0 &&
+ slap_parse_csn_sid( &a->a_vals[0] ) == slap_serverID ) {
+ maxcsn->bv_len = a->a_vals[0].bv_len;
+ strcpy( maxcsn->bv_val, a->a_vals[0].bv_val );
+ }
+ }
+ return LDAP_SUCCESS;
+}
+
+static int
+findcsn_cb( Operation *op, SlapReply *rs )
+{
+ slap_callback *sc = op->o_callback;
+
+ /* We just want to know that at least one exists, so it's OK if
+ * we exceed the unchecked limit.
+ */
+ if ( rs->sr_err == LDAP_ADMINLIMIT_EXCEEDED ||
+ (rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS )) {
+ sc->sc_private = (void *)1;
+ }
+ return LDAP_SUCCESS;
+}
+
+/* Build a list of entryUUIDs for sending in a SyncID set */
+
+#define UUID_LEN 16
+
+typedef struct fpres_cookie {
+ int num;
+ BerVarray uuids;
+ char *last;
+} fpres_cookie;
+
+static int
+findpres_cb( Operation *op, SlapReply *rs )
+{
+ slap_callback *sc = op->o_callback;
+ fpres_cookie *pc = sc->sc_private;
+ Attribute *a;
+ int ret = SLAP_CB_CONTINUE;
+
+ switch ( rs->sr_type ) {
+ case REP_SEARCH:
+ a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryUUID );
+ if ( a ) {
+ pc->uuids[pc->num].bv_val = pc->last;
+ AC_MEMCPY( pc->uuids[pc->num].bv_val, a->a_nvals[0].bv_val,
+ pc->uuids[pc->num].bv_len );
+ pc->num++;
+ pc->last = pc->uuids[pc->num].bv_val;
+ pc->uuids[pc->num].bv_val = NULL;
+ }
+ ret = LDAP_SUCCESS;
+ if ( pc->num != SLAP_SYNCUUID_SET_SIZE )
+ break;
+ /* FALLTHRU */
+ case REP_RESULT:
+ ret = rs->sr_err;
+ if ( pc->num ) {
+ ret = syncprov_sendinfo( op, rs, LDAP_TAG_SYNC_ID_SET, NULL,
+ 0, pc->uuids, 0 );
+ pc->uuids[pc->num].bv_val = pc->last;
+ pc->num = 0;
+ pc->last = pc->uuids[0].bv_val;
+ }
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static int
+syncprov_findcsn( Operation *op, find_csn_t mode, struct berval *csn )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ syncprov_info_t *si = on->on_bi.bi_private;
+
+ slap_callback cb = {0};
+ Operation fop;
+ SlapReply frs = { REP_RESULT };
+ char buf[LDAP_PVT_CSNSTR_BUFSIZE + STRLENOF("(entryCSN<=)")];
+ char cbuf[LDAP_PVT_CSNSTR_BUFSIZE];
+ struct berval maxcsn;
+ Filter cf;
+ AttributeAssertion eq = ATTRIBUTEASSERTION_INIT;
+ fpres_cookie pcookie;
+ sync_control *srs = NULL;
+ struct slap_limits_set fc_limits;
+ int i, rc = LDAP_SUCCESS, findcsn_retry = 1;
+ int maxid;
+
+ if ( mode != FIND_MAXCSN ) {
+ srs = op->o_controls[slap_cids.sc_LDAPsync];
+ }
+
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_findcsn: mode=%s csn=%s\n",
+ op->o_log_prefix,
+ mode == FIND_MAXCSN ?
+ "FIND_MAXCSN" :
+ mode == FIND_CSN ?
+ "FIND_CSN" :
+ "FIND_PRESENT",
+ csn ? csn->bv_val : "" );
+
+ fop = *op;
+ fop.o_sync_mode &= SLAP_CONTROL_MASK; /* turn off sync_mode */
+ /* We want pure entries, not referrals */
+ fop.o_managedsait = SLAP_CONTROL_CRITICAL;
+
+ cf.f_ava = &eq;
+ cf.f_av_desc = slap_schema.si_ad_entryCSN;
+ BER_BVZERO( &cf.f_av_value );
+ cf.f_next = NULL;
+
+ fop.o_callback = &cb;
+ fop.ors_limit = NULL;
+ fop.ors_tlimit = SLAP_NO_LIMIT;
+ fop.ors_filter = &cf;
+ fop.ors_filterstr.bv_val = buf;
+
+again:
+ switch( mode ) {
+ case FIND_MAXCSN:
+ cf.f_choice = LDAP_FILTER_GE;
+ /* If there are multiple CSNs, use the one with our serverID */
+ for ( i=0; i<si->si_numcsns; i++) {
+ if ( slap_serverID == si->si_sids[i] ) {
+ maxid = i;
+ break;
+ }
+ }
+ if ( i == si->si_numcsns ) {
+ /* No match: this is multimaster, and none of the content in the DB
+ * originated locally. Treat like no CSN.
+ */
+ return LDAP_NO_SUCH_OBJECT;
+ }
+ cf.f_av_value = si->si_ctxcsn[maxid];
+ fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
+ "(entryCSN>=%s)", cf.f_av_value.bv_val );
+ if ( fop.ors_filterstr.bv_len >= sizeof( buf ) ) {
+ return LDAP_OTHER;
+ }
+ fop.ors_attrsonly = 0;
+ fop.ors_attrs = csn_anlist;
+ fop.ors_slimit = SLAP_NO_LIMIT;
+ cb.sc_private = &maxcsn;
+ cb.sc_response = findmax_cb;
+ strcpy( cbuf, cf.f_av_value.bv_val );
+ maxcsn.bv_val = cbuf;
+ maxcsn.bv_len = cf.f_av_value.bv_len;
+ break;
+ case FIND_CSN:
+ if ( BER_BVISEMPTY( &cf.f_av_value )) {
+ cf.f_av_value = *csn;
+ }
+ fop.o_dn = op->o_bd->be_rootdn;
+ fop.o_ndn = op->o_bd->be_rootndn;
+ fop.o_req_dn = op->o_bd->be_suffix[0];
+ fop.o_req_ndn = op->o_bd->be_nsuffix[0];
+ /* Look for exact match the first time */
+ if ( findcsn_retry ) {
+ cf.f_choice = LDAP_FILTER_EQUALITY;
+ fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
+ "(entryCSN=%s)", cf.f_av_value.bv_val );
+ /* On retry, look for <= */
+ } else {
+ cf.f_choice = LDAP_FILTER_LE;
+ fop.ors_limit = &fc_limits;
+ memset( &fc_limits, 0, sizeof( fc_limits ));
+ fc_limits.lms_s_unchecked = 1;
+ fop.ors_filterstr.bv_len = snprintf( buf, sizeof( buf ),
+ "(entryCSN<=%s)", cf.f_av_value.bv_val );
+ }
+ if ( fop.ors_filterstr.bv_len >= sizeof( buf ) ) {
+ return LDAP_OTHER;
+ }
+ fop.ors_attrsonly = 1;
+ fop.ors_attrs = slap_anlist_no_attrs;
+ fop.ors_slimit = 1;
+ cb.sc_private = NULL;
+ cb.sc_response = findcsn_cb;
+ break;
+ case FIND_PRESENT:
+ fop.ors_filter = op->ors_filter;
+ fop.ors_filterstr = op->ors_filterstr;
+ fop.ors_attrsonly = 0;
+ fop.ors_attrs = uuid_anlist;
+ fop.ors_slimit = SLAP_NO_LIMIT;
+ cb.sc_private = &pcookie;
+ cb.sc_response = findpres_cb;
+ pcookie.num = 0;
+
+ /* preallocate storage for a full set */
+ pcookie.uuids = op->o_tmpalloc( (SLAP_SYNCUUID_SET_SIZE+1) *
+ sizeof(struct berval) + SLAP_SYNCUUID_SET_SIZE * UUID_LEN,
+ op->o_tmpmemctx );
+ pcookie.last = (char *)(pcookie.uuids + SLAP_SYNCUUID_SET_SIZE+1);
+ pcookie.uuids[0].bv_val = pcookie.last;
+ pcookie.uuids[0].bv_len = UUID_LEN;
+ for (i=1; i<SLAP_SYNCUUID_SET_SIZE; i++) {
+ pcookie.uuids[i].bv_val = pcookie.uuids[i-1].bv_val + UUID_LEN;
+ pcookie.uuids[i].bv_len = UUID_LEN;
+ }
+ break;
+ }
+
+ fop.o_bd->bd_info = (BackendInfo *)on->on_info;
+ fop.o_bd->be_search( &fop, &frs );
+ fop.o_bd->bd_info = (BackendInfo *)on;
+
+ switch( mode ) {
+ case FIND_MAXCSN:
+ if ( ber_bvcmp( &si->si_ctxcsn[maxid], &maxcsn )) {
+#ifdef CHECK_CSN
+ Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax;
+ assert( !syn->ssyn_validate( syn, &maxcsn ));
+#endif
+ ber_bvreplace( &si->si_ctxcsn[maxid], &maxcsn );
+ si->si_numops++; /* ensure a checkpoint */
+ }
+ break;
+ case FIND_CSN:
+ /* If matching CSN was not found, invalidate the context. */
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_findcsn: csn%s=%s %sfound\n",
+ op->o_log_prefix,
+ cf.f_choice == LDAP_FILTER_EQUALITY ? "=" : "<",
+ cf.f_av_value.bv_val, cb.sc_private ? "" : "not " );
+ if ( !cb.sc_private ) {
+ /* If we didn't find an exact match, then try for <= */
+ if ( findcsn_retry ) {
+ findcsn_retry = 0;
+ rs_reinit( &frs, REP_RESULT );
+ goto again;
+ }
+ rc = LDAP_NO_SUCH_OBJECT;
+ }
+ break;
+ case FIND_PRESENT:
+ op->o_tmpfree( pcookie.uuids, op->o_tmpmemctx );
+ break;
+ }
+
+ return rc;
+}
+
+static void free_resinfo( syncres *sr )
+{
+ syncres **st;
+ resinfo *ri = sr->s_info;
+ int freeit = 0;
+
+ ldap_pvt_thread_mutex_lock( &ri->ri_mutex );
+ for (st = &sr->s_info->ri_list; *st; st = &(*st)->s_rilist) {
+ if (*st == sr) {
+ *st = sr->s_rilist;
+ if ( !sr->s_info->ri_list )
+ freeit = 1;
+ sr->s_info = NULL;
+ break;
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &ri->ri_mutex );
+ if ( freeit ) {
+ ldap_pvt_thread_mutex_destroy( &ri->ri_mutex );
+ if ( ri->ri_e )
+ entry_free( ri->ri_e );
+ if ( !BER_BVISNULL( &ri->ri_cookie ))
+ ch_free( ri->ri_cookie.bv_val );
+ ch_free( ri );
+ }
+}
+
+#define FS_UNLINK 1
+#define FS_LOCK 2
+
+static int
+syncprov_free_syncop( syncops *so, int flags )
+{
+ syncres *sr, *srnext;
+ GroupAssertion *ga, *gnext;
+
+ if ( flags & FS_LOCK )
+ ldap_pvt_thread_mutex_lock( &so->s_mutex );
+ /* already being freed, or still in use */
+ if ( !so->s_inuse || --so->s_inuse > 0 ) {
+ if ( flags & FS_LOCK )
+ ldap_pvt_thread_mutex_unlock( &so->s_mutex );
+ return 0;
+ }
+ ldap_pvt_thread_mutex_unlock( &so->s_mutex );
+ if (( flags & FS_UNLINK ) && so->s_si ) {
+ syncops **sop;
+ ldap_pvt_thread_mutex_lock( &so->s_si->si_ops_mutex );
+ for ( sop = &so->s_si->si_ops; *sop; sop = &(*sop)->s_next ) {
+ if ( *sop == so ) {
+ *sop = so->s_next;
+ break;
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &so->s_si->si_ops_mutex );
+ }
+ if ( so->s_flags & PS_IS_DETACHED ) {
+ filter_free( so->s_op->ors_filter );
+ for ( ga = so->s_op->o_groups; ga; ga=gnext ) {
+ gnext = ga->ga_next;
+ ch_free( ga );
+ }
+ ch_free( so->s_op );
+ }
+ ch_free( so->s_base.bv_val );
+ for ( sr=so->s_res; sr; sr=srnext ) {
+ srnext = sr->s_next;
+ free_resinfo( sr );
+ ch_free( sr );
+ }
+ ldap_pvt_thread_mutex_destroy( &so->s_mutex );
+ ch_free( so );
+ return 1;
+}
+
+/* Send a persistent search response */
+static int
+syncprov_sendresp( Operation *op, resinfo *ri, syncops *so, int mode )
+{
+ SlapReply rs = { REP_SEARCH };
+ struct berval cookie, csns[2];
+ Entry e_uuid = {0};
+ Attribute a_uuid = {0};
+
+ if ( so->s_op->o_abandon )
+ return SLAPD_ABANDON;
+
+ rs.sr_ctrls = op->o_tmpalloc( sizeof(LDAPControl *)*2, op->o_tmpmemctx );
+ rs.sr_ctrls[1] = NULL;
+ rs.sr_flags = REP_CTRLS_MUSTBEFREED;
+ csns[0] = ri->ri_csn;
+ BER_BVZERO( &csns[1] );
+ slap_compose_sync_cookie( op, &cookie, csns, so->s_rid,
+ slap_serverID ? slap_serverID : -1, NULL );
+
+#ifdef LDAP_DEBUG
+ if ( so->s_sid > 0 ) {
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendresp: to=%03x, cookie=%s\n",
+ op->o_log_prefix, so->s_sid, cookie.bv_val );
+ } else {
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendresp: cookie=%s\n",
+ op->o_log_prefix, cookie.bv_val );
+ }
+#endif
+
+ e_uuid.e_attrs = &a_uuid;
+ a_uuid.a_desc = slap_schema.si_ad_entryUUID;
+ a_uuid.a_nvals = &ri->ri_uuid;
+ rs.sr_err = syncprov_state_ctrl( op, &rs, &e_uuid,
+ mode, rs.sr_ctrls, 0, 1, &cookie );
+ op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
+
+ rs.sr_entry = &e_uuid;
+ if ( mode == LDAP_SYNC_ADD || mode == LDAP_SYNC_MODIFY ) {
+ e_uuid = *ri->ri_e;
+ e_uuid.e_private = NULL;
+ }
+
+ switch( mode ) {
+ case LDAP_SYNC_ADD:
+ if ( ri->ri_isref && so->s_op->o_managedsait <= SLAP_CONTROL_IGNORED ) {
+ rs.sr_ref = get_entry_referrals( op, rs.sr_entry );
+ rs.sr_err = send_search_reference( op, &rs );
+ ber_bvarray_free( rs.sr_ref );
+ break;
+ }
+ /* fallthru */
+ case LDAP_SYNC_MODIFY:
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendresp: sending %s, dn=%s\n",
+ op->o_log_prefix,
+ mode == LDAP_SYNC_ADD ? "LDAP_SYNC_ADD" : "LDAP_SYNC_MODIFY",
+ e_uuid.e_nname.bv_val );
+ rs.sr_attrs = op->ors_attrs;
+ rs.sr_err = send_search_entry( op, &rs );
+ break;
+ case LDAP_SYNC_DELETE:
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_sendresp: "
+ "sending LDAP_SYNC_DELETE, dn=%s\n",
+ op->o_log_prefix, ri->ri_dn.bv_val );
+ e_uuid.e_attrs = NULL;
+ e_uuid.e_name = ri->ri_dn;
+ e_uuid.e_nname = ri->ri_ndn;
+ if ( ri->ri_isref && so->s_op->o_managedsait <= SLAP_CONTROL_IGNORED ) {
+ struct berval bv = BER_BVNULL;
+ rs.sr_ref = &bv;
+ rs.sr_err = send_search_reference( op, &rs );
+ } else {
+ rs.sr_err = send_search_entry( op, &rs );
+ }
+ break;
+ default:
+ assert(0);
+ }
+ return rs.sr_err;
+}
+
+static void
+syncprov_qstart( syncops *so );
+
+/* Play back queued responses */
+static int
+syncprov_qplay( Operation *op, syncops *so )
+{
+ syncres *sr;
+ int rc = 0;
+
+ do {
+ ldap_pvt_thread_mutex_lock( &so->s_mutex );
+ sr = so->s_res;
+ /* Exit loop with mutex held */
+ if ( !sr )
+ break;
+ so->s_res = sr->s_next;
+ if ( !so->s_res )
+ so->s_restail = NULL;
+ ldap_pvt_thread_mutex_unlock( &so->s_mutex );
+
+ if ( !so->s_op->o_abandon ) {
+
+ if ( sr->s_mode == LDAP_SYNC_NEW_COOKIE ) {
+ SlapReply rs = { REP_INTERMEDIATE };
+
+ rc = syncprov_sendinfo( op, &rs, LDAP_TAG_SYNC_NEW_COOKIE,
+ &sr->s_info->ri_cookie, 0, NULL, 0 );
+ } else {
+ rc = syncprov_sendresp( op, sr->s_info, so, sr->s_mode );
+ }
+ }
+
+ free_resinfo( sr );
+ ch_free( sr );
+
+ if ( so->s_op->o_abandon )
+ continue;
+
+ /* Exit loop with mutex held */
+ ldap_pvt_thread_mutex_lock( &so->s_mutex );
+ break;
+
+ } while (1);
+
+ /* We now only send one change at a time, to prevent one
+ * psearch from hogging all the CPU. Resubmit this task if
+ * there are more responses queued and no errors occurred.
+ */
+
+ if ( rc == 0 && so->s_res ) {
+ syncprov_qstart( so );
+ }
+
+ return rc;
+}
+
+/* task for playing back queued responses */
+static void *
+syncprov_qtask( void *ctx, void *arg )
+{
+ syncops *so = arg;
+ OperationBuffer opbuf;
+ Operation *op;
+ BackendDB be;
+ int rc;
+
+ op = &opbuf.ob_op;
+ *op = *so->s_op;
+ op->o_hdr = &opbuf.ob_hdr;
+ op->o_controls = opbuf.ob_controls;
+ memset( op->o_controls, 0, sizeof(opbuf.ob_controls) );
+ op->o_sync = SLAP_CONTROL_IGNORED;
+
+ *op->o_hdr = *so->s_op->o_hdr;
+
+ op->o_tmpmemctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 1);
+ op->o_tmpmfuncs = &slap_sl_mfuncs;
+ op->o_threadctx = ctx;
+ operation_counter_init( op, ctx );
+
+ /* syncprov_qplay expects a fake db */
+ be = *so->s_op->o_bd;
+ be.be_flags |= SLAP_DBFLAG_OVERLAY;
+ op->o_bd = &be;
+ LDAP_SLIST_FIRST(&op->o_extra) = NULL;
+ op->o_callback = NULL;
+
+ rc = syncprov_qplay( op, so );
+
+ /* if an error occurred, or no responses left, task is no longer queued */
+ if ( !rc && !so->s_res )
+ rc = 1;
+
+ /* decrement use count... */
+ if ( !syncprov_free_syncop( so, FS_UNLINK )) {
+ if ( rc )
+ /* if we didn't unlink, and task is no longer queued, clear flag */
+ so->s_flags ^= PS_TASK_QUEUED;
+ ldap_pvt_thread_mutex_unlock( &so->s_mutex );
+ }
+
+ return NULL;
+}
+
+/* Start the task to play back queued psearch responses */
+static void
+syncprov_qstart( syncops *so )
+{
+ so->s_flags |= PS_TASK_QUEUED;
+ so->s_inuse++;
+ ldap_pvt_thread_pool_submit2( &connection_pool,
+ syncprov_qtask, so, &so->s_pool_cookie );
+}
+
+/* Queue a persistent search response */
+static int
+syncprov_qresp( opcookie *opc, syncops *so, int mode )
+{
+ syncres *sr;
+ resinfo *ri;
+ int srsize;
+ struct berval csn = opc->sctxcsn;
+
+ sr = ch_malloc( sizeof( syncres ));
+ sr->s_next = NULL;
+ sr->s_mode = mode;
+ if ( !opc->ssres.s_info ) {
+
+ srsize = sizeof( resinfo );
+ if ( csn.bv_len )
+ srsize += csn.bv_len + 1;
+
+ if ( opc->se ) {
+ Attribute *a;
+ ri = ch_malloc( srsize );
+ ri->ri_dn = opc->se->e_name;
+ ri->ri_ndn = opc->se->e_nname;
+ a = attr_find( opc->se->e_attrs, slap_schema.si_ad_entryUUID );
+ if ( a )
+ ri->ri_uuid = a->a_nvals[0];
+ else
+ ri->ri_uuid.bv_len = 0;
+ if ( csn.bv_len ) {
+ ri->ri_csn.bv_val = (char *)(ri + 1);
+ ri->ri_csn.bv_len = csn.bv_len;
+ memcpy( ri->ri_csn.bv_val, csn.bv_val, csn.bv_len );
+ ri->ri_csn.bv_val[csn.bv_len] = '\0';
+ } else {
+ ri->ri_csn.bv_val = NULL;
+ }
+ } else {
+ srsize += opc->suuid.bv_len +
+ opc->sdn.bv_len + 1 + opc->sndn.bv_len + 1;
+ ri = ch_malloc( srsize );
+ ri->ri_dn.bv_val = (char *)(ri + 1);
+ ri->ri_dn.bv_len = opc->sdn.bv_len;
+ ri->ri_ndn.bv_val = lutil_strcopy( ri->ri_dn.bv_val,
+ opc->sdn.bv_val ) + 1;
+ ri->ri_ndn.bv_len = opc->sndn.bv_len;
+ ri->ri_uuid.bv_val = lutil_strcopy( ri->ri_ndn.bv_val,
+ opc->sndn.bv_val ) + 1;
+ ri->ri_uuid.bv_len = opc->suuid.bv_len;
+ AC_MEMCPY( ri->ri_uuid.bv_val, opc->suuid.bv_val, opc->suuid.bv_len );
+ if ( csn.bv_len ) {
+ ri->ri_csn.bv_val = ri->ri_uuid.bv_val + ri->ri_uuid.bv_len;
+ memcpy( ri->ri_csn.bv_val, csn.bv_val, csn.bv_len );
+ ri->ri_csn.bv_val[csn.bv_len] = '\0';
+ } else {
+ ri->ri_csn.bv_val = NULL;
+ }
+ }
+ ri->ri_list = &opc->ssres;
+ ri->ri_e = opc->se;
+ ri->ri_csn.bv_len = csn.bv_len;
+ ri->ri_isref = opc->sreference;
+ BER_BVZERO( &ri->ri_cookie );
+ ldap_pvt_thread_mutex_init( &ri->ri_mutex );
+ opc->se = NULL;
+ opc->ssres.s_info = ri;
+ }
+ ri = opc->ssres.s_info;
+ sr->s_info = ri;
+ ldap_pvt_thread_mutex_lock( &ri->ri_mutex );
+ sr->s_rilist = ri->ri_list;
+ ri->ri_list = sr;
+ if ( mode == LDAP_SYNC_NEW_COOKIE && BER_BVISNULL( &ri->ri_cookie )) {
+ syncprov_info_t *si = opc->son->on_bi.bi_private;
+
+ slap_compose_sync_cookie( NULL, &ri->ri_cookie, si->si_ctxcsn,
+ so->s_rid, slap_serverID ? slap_serverID : -1, NULL );
+ }
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_qresp: "
+ "set up a new syncres mode=%d csn=%s\n",
+ so->s_op->o_log_prefix, mode, csn.bv_val ? csn.bv_val : "" );
+ ldap_pvt_thread_mutex_unlock( &ri->ri_mutex );
+
+ ldap_pvt_thread_mutex_lock( &so->s_mutex );
+ if ( !so->s_res ) {
+ so->s_res = sr;
+ } else {
+ so->s_restail->s_next = sr;
+ }
+ so->s_restail = sr;
+
+ /* If the base of the psearch was modified, check it next time round */
+ if ( so->s_flags & PS_WROTE_BASE ) {
+ so->s_flags ^= PS_WROTE_BASE;
+ so->s_flags |= PS_FIND_BASE;
+ }
+ if (( so->s_flags & (PS_IS_DETACHED|PS_TASK_QUEUED)) == PS_IS_DETACHED ) {
+ syncprov_qstart( so );
+ }
+ ldap_pvt_thread_mutex_unlock( &so->s_mutex );
+ return LDAP_SUCCESS;
+}
+
+static int
+syncprov_drop_psearch( syncops *so, int lock )
+{
+ if ( so->s_flags & PS_IS_DETACHED ) {
+ if ( lock )
+ ldap_pvt_thread_mutex_lock( &so->s_op->o_conn->c_mutex );
+ so->s_op->o_conn->c_n_ops_executing--;
+ so->s_op->o_conn->c_n_ops_completed++;
+ LDAP_STAILQ_REMOVE( &so->s_op->o_conn->c_ops, so->s_op, Operation,
+ o_next );
+ if ( lock )
+ ldap_pvt_thread_mutex_unlock( &so->s_op->o_conn->c_mutex );
+ }
+ return syncprov_free_syncop( so, FS_LOCK );
+}
+
+static int
+syncprov_ab_cleanup( Operation *op, SlapReply *rs )
+{
+ slap_callback *sc = op->o_callback;
+ op->o_callback = sc->sc_next;
+ syncprov_drop_psearch( sc->sc_private, 0 );
+ op->o_tmpfree( sc, op->o_tmpmemctx );
+ return 0;
+}
+
+static int
+syncprov_op_abandon( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ syncprov_info_t *si = on->on_bi.bi_private;
+ syncops *so, **sop;
+
+ ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
+ for ( sop=&si->si_ops; (so = *sop); sop = &(*sop)->s_next ) {
+ if ( so->s_op->o_connid == op->o_connid &&
+ so->s_op->o_msgid == op->orn_msgid ) {
+ so->s_op->o_abandon = 1;
+ *sop = so->s_next;
+ break;
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
+ if ( so ) {
+ /* Is this really a Cancel exop? */
+ if ( op->o_tag != LDAP_REQ_ABANDON ) {
+ so->s_op->o_cancel = SLAP_CANCEL_ACK;
+ rs->sr_err = LDAP_CANCELLED;
+ send_ldap_result( so->s_op, rs );
+ if ( so->s_flags & PS_IS_DETACHED ) {
+ slap_callback *cb;
+ cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
+ cb->sc_cleanup = syncprov_ab_cleanup;
+ cb->sc_next = op->o_callback;
+ cb->sc_private = so;
+ op->o_callback = cb;
+ return SLAP_CB_CONTINUE;
+ }
+ }
+ syncprov_drop_psearch( so, 0 );
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+/* Find which persistent searches are affected by this operation */
+static void
+syncprov_matchops( Operation *op, opcookie *opc, int saveit )
+{
+ slap_overinst *on = opc->son;
+ syncprov_info_t *si = on->on_bi.bi_private;
+
+ fbase_cookie fc;
+ syncops **pss;
+ Entry *e = NULL;
+ Attribute *a;
+ int rc, gonext;
+ struct berval newdn;
+ int freefdn = 0;
+ BackendDB *b0 = op->o_bd, db;
+
+ fc.fdn = &op->o_req_ndn;
+ /* compute new DN */
+ if ( op->o_tag == LDAP_REQ_MODRDN && !saveit ) {
+ struct berval pdn;
+ if ( op->orr_nnewSup ) pdn = *op->orr_nnewSup;
+ else dnParent( fc.fdn, &pdn );
+ build_new_dn( &newdn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
+ fc.fdn = &newdn;
+ freefdn = 1;
+ }
+ if ( op->o_tag != LDAP_REQ_ADD ) {
+ if ( !SLAP_ISOVERLAY( op->o_bd )) {
+ db = *op->o_bd;
+ op->o_bd = &db;
+ }
+ rc = overlay_entry_get_ov( op, fc.fdn, NULL, NULL, 0, &e, on );
+ /* If we're sending responses now, make a copy and unlock the DB */
+ if ( e && !saveit ) {
+ if ( !opc->se )
+ opc->se = entry_dup( e );
+ overlay_entry_release_ov( op, e, 0, on );
+ e = opc->se;
+ }
+ if ( rc ) {
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_matchops: "
+ "%s check, error finding entry dn=%s in database\n",
+ op->o_log_prefix, saveit ? "initial" : "final", fc.fdn->bv_val );
+ op->o_bd = b0;
+ return;
+ }
+ } else {
+ e = op->ora_e;
+ if ( !saveit ) {
+ if ( !opc->se )
+ opc->se = entry_dup( e );
+ e = opc->se;
+ }
+ }
+
+ if ( saveit || op->o_tag == LDAP_REQ_ADD ) {
+ ber_dupbv_x( &opc->sdn, &e->e_name, op->o_tmpmemctx );
+ ber_dupbv_x( &opc->sndn, &e->e_nname, op->o_tmpmemctx );
+ opc->sreference = is_entry_referral( e );
+ a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
+ if ( a )
+ ber_dupbv_x( &opc->suuid, &a->a_nvals[0], op->o_tmpmemctx );
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_matchops: "
+ "%srecording uuid for dn=%s on opc=%p\n",
+ op->o_log_prefix, a ? "" : "not ", opc->sdn.bv_val, opc );
+ } else if ( op->o_tag == LDAP_REQ_MODRDN && !saveit ) {
+ op->o_tmpfree( opc->sndn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( opc->sdn.bv_val, op->o_tmpmemctx );
+ ber_dupbv_x( &opc->sdn, &e->e_name, op->o_tmpmemctx );
+ ber_dupbv_x( &opc->sndn, &e->e_nname, op->o_tmpmemctx );
+ }
+
+ ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
+ for (pss = &si->si_ops; *pss; pss = gonext ? &(*pss)->s_next : pss)
+ {
+ Operation op2;
+ Opheader oh;
+ syncmatches *sm;
+ int found = 0;
+ syncops *snext, *ss = *pss;
+
+ gonext = 1;
+ if ( ss->s_op->o_abandon )
+ continue;
+
+ /* Don't send ops back to the originator */
+ if ( opc->osid > 0 && opc->osid == ss->s_sid ) {
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_matchops: "
+ "skipping original sid %03x\n",
+ ss->s_op->o_log_prefix, opc->osid );
+ continue;
+ }
+
+ /* Don't send ops back to the messenger */
+ if ( opc->rsid > 0 && opc->rsid == ss->s_sid ) {
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_matchops: "
+ "skipping relayed sid %03x\n",
+ ss->s_op->o_log_prefix, opc->rsid );
+ continue;
+ }
+
+ /* validate base */
+ fc.fss = ss;
+ fc.fbase = 0;
+ fc.fscope = 0;
+
+ /* If the base of the search is missing, signal a refresh */
+ rc = syncprov_findbase( op, &fc );
+ if ( rc != LDAP_SUCCESS ) {
+ SlapReply rs = {REP_RESULT};
+ send_ldap_error( ss->s_op, &rs, LDAP_SYNC_REFRESH_REQUIRED,
+ "search base has changed" );
+ snext = ss->s_next;
+ if ( syncprov_drop_psearch( ss, 1 ) )
+ *pss = snext;
+ gonext = 0;
+ continue;
+ }
+
+ /* If we're sending results now, look for this op in old matches */
+ if ( !saveit ) {
+ syncmatches *old;
+
+ /* Did we modify the search base? */
+ if ( dn_match( &op->o_req_ndn, &ss->s_base )) {
+ ldap_pvt_thread_mutex_lock( &ss->s_mutex );
+ ss->s_flags |= PS_WROTE_BASE;
+ ldap_pvt_thread_mutex_unlock( &ss->s_mutex );
+ }
+
+ for ( sm=opc->smatches, old=(syncmatches *)&opc->smatches; sm;
+ old=sm, sm=sm->sm_next ) {
+ if ( sm->sm_op == ss ) {
+ found = 1;
+ old->sm_next = sm->sm_next;
+ op->o_tmpfree( sm, op->o_tmpmemctx );
+ break;
+ }
+ }
+ }
+
+ if ( fc.fscope ) {
+ ldap_pvt_thread_mutex_lock( &ss->s_mutex );
+ op2 = *ss->s_op;
+ oh = *op->o_hdr;
+ oh.oh_conn = ss->s_op->o_conn;
+ oh.oh_connid = ss->s_op->o_connid;
+ op2.o_bd = op->o_bd->bd_self;
+ op2.o_hdr = &oh;
+ op2.o_extra = op->o_extra;
+ op2.o_callback = NULL;
+ if (ss->s_flags & PS_FIX_FILTER) {
+ /* Skip the AND/GE clause that we stuck on in front. We
+ would lose deletes/mods that happen during the refresh
+ phase otherwise (ITS#6555) */
+ op2.ors_filter = ss->s_op->ors_filter->f_and->f_next;
+ }
+ rc = test_filter( &op2, e, op2.ors_filter );
+ ldap_pvt_thread_mutex_unlock( &ss->s_mutex );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "%s syncprov_matchops: "
+ "sid %03x fscope %d rc %d\n",
+ ss->s_op->o_log_prefix, ss->s_sid, fc.fscope, rc );
+
+ /* check if current o_req_dn is in scope and matches filter */
+ if ( fc.fscope && rc == LDAP_COMPARE_TRUE ) {
+ if ( saveit ) {
+ sm = op->o_tmpalloc( sizeof(syncmatches), op->o_tmpmemctx );
+ sm->sm_next = opc->smatches;
+ sm->sm_op = ss;
+ ldap_pvt_thread_mutex_lock( &ss->s_mutex );
+ ++ss->s_inuse;
+ ldap_pvt_thread_mutex_unlock( &ss->s_mutex );
+ opc->smatches = sm;
+ } else {
+ /* if found send UPDATE else send ADD */
+ syncprov_qresp( opc, ss,
+ found ? LDAP_SYNC_MODIFY : LDAP_SYNC_ADD );
+ }
+ } else if ( !saveit && found ) {
+ /* send DELETE */
+ syncprov_qresp( opc, ss, LDAP_SYNC_DELETE );
+ } else if ( !saveit ) {
+ syncprov_qresp( opc, ss, LDAP_SYNC_NEW_COOKIE );
+ }
+ if ( !saveit && found ) {
+ /* Decrement s_inuse, was incremented when called
+ * with saveit == TRUE
+ */
+ snext = ss->s_next;
+ if ( syncprov_free_syncop( ss, FS_LOCK ) ) {
+ *pss = snext;
+ gonext = 0;
+ }
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
+
+ if ( op->o_tag != LDAP_REQ_ADD && e ) {
+ if ( !SLAP_ISOVERLAY( op->o_bd )) {
+ op->o_bd = &db;
+ }
+ if ( saveit )
+ overlay_entry_release_ov( op, e, 0, on );
+ op->o_bd = b0;
+ }
+ if ( !saveit ) {
+ if ( opc->ssres.s_info )
+ free_resinfo( &opc->ssres );
+ else if ( opc->se )
+ entry_free( opc->se );
+ }
+ if ( freefdn ) {
+ op->o_tmpfree( fc.fdn->bv_val, op->o_tmpmemctx );
+ }
+ op->o_bd = b0;
+}
+
+static int
+syncprov_op_cleanup( Operation *op, SlapReply *rs )
+{
+ slap_callback *cb = op->o_callback;
+ opcookie *opc = cb->sc_private;
+ slap_overinst *on = opc->son;
+ syncprov_info_t *si = on->on_bi.bi_private;
+ syncmatches *sm, *snext;
+ modtarget *mt;
+
+ ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
+ if ( si->si_active )
+ si->si_active--;
+ ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
+
+ for (sm = opc->smatches; sm; sm=snext) {
+ snext = sm->sm_next;
+ syncprov_free_syncop( sm->sm_op, FS_LOCK|FS_UNLINK );
+ op->o_tmpfree( sm, op->o_tmpmemctx );
+ }
+
+ /* Remove op from lock table */
+ mt = opc->smt;
+ if ( mt ) {
+ modinst *mi = (modinst *)(opc+1), **m2;
+ ldap_pvt_thread_mutex_lock( &mt->mt_mutex );
+ for (m2 = &mt->mt_mods; ; m2 = &(*m2)->mi_next) {
+ if ( *m2 == mi ) {
+ *m2 = mi->mi_next;
+ if ( mt->mt_tail == mi )
+ mt->mt_tail = ( m2 == &mt->mt_mods ) ? NULL : (modinst *)m2;
+ break;
+ }
+ }
+ /* If there are more, promote the next one */
+ if ( mt->mt_mods ) {
+ ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
+ } else {
+ ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
+ ldap_pvt_thread_mutex_lock( &si->si_mods_mutex );
+ ldap_avl_delete( &si->si_mods, mt, sp_avl_cmp );
+ ldap_pvt_thread_mutex_unlock( &si->si_mods_mutex );
+ ldap_pvt_thread_mutex_destroy( &mt->mt_mutex );
+ ch_free( mt->mt_dn.bv_val );
+ ch_free( mt );
+ }
+ }
+ if ( !BER_BVISNULL( &opc->suuid ))
+ op->o_tmpfree( opc->suuid.bv_val, op->o_tmpmemctx );
+ if ( !BER_BVISNULL( &opc->sndn ))
+ op->o_tmpfree( opc->sndn.bv_val, op->o_tmpmemctx );
+ if ( !BER_BVISNULL( &opc->sdn ))
+ op->o_tmpfree( opc->sdn.bv_val, op->o_tmpmemctx );
+ op->o_callback = cb->sc_next;
+
+ if ( opc->ssres.s_info ) {
+ free_resinfo( &opc->ssres );
+ }
+ op->o_tmpfree(cb, op->o_tmpmemctx);
+
+ return 0;
+}
+
+static void
+syncprov_checkpoint( Operation *op, slap_overinst *on )
+{
+ syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
+ Modifications mod;
+ Operation opm;
+ SlapReply rsm = {REP_RESULT};
+ slap_callback cb = {0};
+ BackendDB be;
+ BackendInfo *bi;
+
+#ifdef CHECK_CSN
+ Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax;
+
+ int i;
+ for ( i=0; i<si->si_numcsns; i++ ) {
+ assert( !syn->ssyn_validate( syn, si->si_ctxcsn+i ));
+ }
+#endif
+
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_checkpoint: running checkpoint\n",
+ op->o_log_prefix );
+
+ mod.sml_numvals = si->si_numcsns;
+ mod.sml_values = si->si_ctxcsn;
+ mod.sml_nvalues = NULL;
+ mod.sml_desc = slap_schema.si_ad_contextCSN;
+ mod.sml_op = LDAP_MOD_REPLACE;
+ mod.sml_flags = SLAP_MOD_INTERNAL;
+ mod.sml_next = NULL;
+
+ cb.sc_response = slap_null_cb;
+ opm = *op;
+ opm.o_tag = LDAP_REQ_MODIFY;
+ opm.o_callback = &cb;
+ opm.orm_modlist = &mod;
+ opm.orm_no_opattrs = 1;
+ if ( SLAP_GLUE_SUBORDINATE( op->o_bd )) {
+ be = *on->on_info->oi_origdb;
+ opm.o_bd = &be;
+ }
+ opm.o_req_dn = si->si_contextdn;
+ opm.o_req_ndn = si->si_contextdn;
+ bi = opm.o_bd->bd_info;
+ opm.o_bd->bd_info = on->on_info->oi_orig;
+ opm.o_managedsait = SLAP_CONTROL_NONCRITICAL;
+ opm.o_no_schema_check = 1;
+ opm.o_dont_replicate = 1;
+ opm.o_opid = -1;
+ opm.o_bd->be_modify( &opm, &rsm );
+
+ if ( rsm.sr_err == LDAP_NO_SUCH_OBJECT &&
+ SLAP_SYNC_SUBENTRY( opm.o_bd )) {
+ const char *text;
+ char txtbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof txtbuf;
+ Entry *e = slap_create_context_csn_entry( opm.o_bd, NULL );
+ rs_reinit( &rsm, REP_RESULT );
+ slap_mods2entry( &mod, &e, 0, 1, &text, txtbuf, textlen);
+ opm.ora_e = e;
+ opm.o_bd->be_add( &opm, &rsm );
+ if ( e == opm.ora_e )
+ be_entry_release_w( &opm, opm.ora_e );
+ }
+ opm.o_bd->bd_info = bi;
+
+ if ( mod.sml_next != NULL ) {
+ slap_mods_free( mod.sml_next, 1 );
+ }
+#ifdef CHECK_CSN
+ for ( i=0; i<si->si_numcsns; i++ ) {
+ assert( !syn->ssyn_validate( syn, si->si_ctxcsn+i ));
+ }
+#endif
+}
+
+static void
+syncprov_add_slog( Operation *op )
+{
+ opcookie *opc = op->o_callback->sc_private;
+ slap_overinst *on = opc->son;
+ syncprov_info_t *si = on->on_bi.bi_private;
+ sessionlog *sl;
+ slog_entry *se;
+ char uuidstr[40];
+ int rc;
+
+ sl = si->si_logs;
+ {
+ if ( BER_BVISEMPTY( &op->o_csn ) ) {
+ /* During the syncrepl refresh phase we can receive operations
+ * without a csn. We cannot reliably determine the consumers
+ * state with respect to such operations, so we ignore them and
+ * wipe out anything in the log if we see them.
+ */
+ ldap_pvt_thread_rdwr_wlock( &sl->sl_mutex );
+ /* can only do this if no one else is reading the log at the moment */
+ if ( !sl->sl_playing ) {
+ ldap_tavl_free( sl->sl_entries, (AVL_FREE)ch_free );
+ sl->sl_num = 0;
+ sl->sl_entries = NULL;
+ }
+ ldap_pvt_thread_rdwr_wunlock( &sl->sl_mutex );
+ return;
+ }
+
+ /* Allocate a record. UUIDs are not NUL-terminated. */
+ se = ch_malloc( sizeof( slog_entry ) + opc->suuid.bv_len +
+ op->o_csn.bv_len + 1 );
+ se->se_tag = op->o_tag;
+
+ se->se_uuid.bv_val = (char *)(&se[1]);
+ AC_MEMCPY( se->se_uuid.bv_val, opc->suuid.bv_val, opc->suuid.bv_len );
+ se->se_uuid.bv_len = opc->suuid.bv_len;
+
+ se->se_csn.bv_val = se->se_uuid.bv_val + opc->suuid.bv_len;
+ AC_MEMCPY( se->se_csn.bv_val, op->o_csn.bv_val, op->o_csn.bv_len );
+ se->se_csn.bv_val[op->o_csn.bv_len] = '\0';
+ se->se_csn.bv_len = op->o_csn.bv_len;
+ se->se_sid = slap_parse_csn_sid( &se->se_csn );
+
+ ldap_pvt_thread_rdwr_wlock( &sl->sl_mutex );
+ if ( LogTest( LDAP_DEBUG_SYNC ) ) {
+ uuidstr[0] = 0;
+ if ( !BER_BVISEMPTY( &opc->suuid ) ) {
+ lutil_uuidstr_from_normalized( opc->suuid.bv_val, opc->suuid.bv_len,
+ uuidstr, 40 );
+ }
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_add_slog: "
+ "adding csn=%s to sessionlog, uuid=%s\n",
+ op->o_log_prefix, se->se_csn.bv_val, uuidstr );
+ }
+ if ( !sl->sl_entries ) {
+ if ( !sl->sl_mincsn ) {
+ sl->sl_numcsns = 1;
+ sl->sl_mincsn = ch_malloc( 2*sizeof( struct berval ));
+ sl->sl_sids = ch_malloc( sizeof( int ));
+ sl->sl_sids[0] = se->se_sid;
+ ber_dupbv( sl->sl_mincsn, &se->se_csn );
+ BER_BVZERO( &sl->sl_mincsn[1] );
+ }
+ }
+ rc = ldap_tavl_insert( &sl->sl_entries, se, syncprov_sessionlog_cmp, ldap_avl_dup_error );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_add_slog: "
+ "duplicate sessionlog entry ignored: csn=%s, uuid=%s\n",
+ op->o_log_prefix, se->se_csn.bv_val, uuidstr );
+ ch_free( se );
+ goto leave;
+ }
+ sl->sl_num++;
+ if ( !sl->sl_playing && sl->sl_num > sl->sl_size ) {
+ TAvlnode *edge = ldap_tavl_end( sl->sl_entries, TAVL_DIR_LEFT );
+ while ( sl->sl_num > sl->sl_size ) {
+ int i;
+ TAvlnode *next = ldap_tavl_next( edge, TAVL_DIR_RIGHT );
+ se = edge->avl_data;
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_add_slog: "
+ "expiring csn=%s from sessionlog (sessionlog size=%d)\n",
+ op->o_log_prefix, se->se_csn.bv_val, sl->sl_num );
+ for ( i=0; i<sl->sl_numcsns; i++ )
+ if ( sl->sl_sids[i] >= se->se_sid )
+ break;
+ if ( i == sl->sl_numcsns || sl->sl_sids[i] != se->se_sid ) {
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_add_slog: "
+ "adding csn=%s to mincsn\n",
+ op->o_log_prefix, se->se_csn.bv_val );
+ slap_insert_csn_sids( (struct sync_cookie *)sl,
+ i, se->se_sid, &se->se_csn );
+ } else {
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_add_slog: "
+ "updating mincsn for sid=%d csn=%s to %s\n",
+ op->o_log_prefix, se->se_sid, sl->sl_mincsn[i].bv_val, se->se_csn.bv_val );
+ ber_bvreplace( &sl->sl_mincsn[i], &se->se_csn );
+ }
+ ldap_tavl_delete( &sl->sl_entries, se, syncprov_sessionlog_cmp );
+ ch_free( se );
+ edge = next;
+ sl->sl_num--;
+ }
+ }
+leave:
+ ldap_pvt_thread_rdwr_wunlock( &sl->sl_mutex );
+ }
+}
+
+/* Just set a flag if we found the matching entry */
+static int
+playlog_cb( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_SEARCH ) {
+ op->o_callback->sc_private = (void *)1;
+ }
+ return rs->sr_err;
+}
+
+/*
+ * Check whether the last nmods UUIDs in the uuids list exist in the database
+ * and (still) match the op filter, zero out the bv_len of any that still exist
+ * and return the number of UUIDs we have confirmed are gone now.
+ */
+static int
+check_uuidlist_presence(
+ Operation *op,
+ struct berval *uuids,
+ int len,
+ int nmods )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ Operation fop = *op;
+ SlapReply frs = { REP_RESULT };
+ Filter mf, af;
+ AttributeAssertion eq = ATTRIBUTEASSERTION_INIT;
+ slap_callback cb = {0};
+ int i, mods = nmods;
+
+ fop.o_sync_mode = 0;
+ fop.o_callback = &cb;
+ fop.ors_limit = NULL;
+ fop.ors_tlimit = SLAP_NO_LIMIT;
+ fop.ors_attrs = slap_anlist_all_attributes;
+ fop.ors_attrsonly = 0;
+ fop.o_managedsait = SLAP_CONTROL_CRITICAL;
+
+ af.f_choice = LDAP_FILTER_AND;
+ af.f_next = NULL;
+ af.f_and = &mf;
+ mf.f_choice = LDAP_FILTER_EQUALITY;
+ mf.f_ava = &eq;
+ mf.f_av_desc = slap_schema.si_ad_entryUUID;
+ mf.f_next = fop.ors_filter;
+
+ fop.ors_filter = &af;
+
+ cb.sc_response = playlog_cb;
+
+ fop.o_bd->bd_info = (BackendInfo *)on->on_info;
+ for ( i=0; i<nmods; i++ ) {
+ mf.f_av_value = uuids[ len - 1 - i ];
+ cb.sc_private = NULL;
+ fop.ors_slimit = 1;
+
+ if ( BER_BVISEMPTY( &mf.f_av_value ) ) {
+ mods--;
+ continue;
+ }
+
+ rs_reinit( &frs, REP_RESULT );
+ fop.o_bd->be_search( &fop, &frs );
+ if ( cb.sc_private ) {
+ uuids[ len - 1 - i ].bv_len = 0;
+ mods--;
+ }
+ }
+ fop.o_bd->bd_info = (BackendInfo *)on;
+
+ return mods;
+}
+
+/*
+ * On each entry we get from the DB:
+ * - if it's an ADD, skip
+ * - check we've not handled it yet, skip if we have
+ * - check if it's a DELETE or missing from the DB now
+ * - send a new syncinfo entry
+ * - remember we've handled it already
+ *
+ * If we exhaust the list, clear it, forgetting entries we've handled so far.
+ */
+static int
+syncprov_accesslog_uuid_cb( Operation *op, SlapReply *rs )
+{
+ slap_callback *sc = op->o_callback;
+ syncprov_accesslog_deletes *uuid_progress = sc->sc_private;
+ Attribute *a, *attrs;
+ sync_control *srs = uuid_progress->srs;
+ struct berval *bv, csn[2] = {}, uuid[2] = {},
+ add = BER_BVC("add"),
+ delete = BER_BVC("delete"),
+ modrdn = BER_BVC("modrdn");
+ int cmp, sid, i, is_delete = 0, rc;
+
+ if ( rs->sr_type != REP_SEARCH ) {
+ return rs->sr_err;
+ }
+ attrs = rs->sr_entry->e_attrs;
+
+ a = attr_find( attrs, ad_reqType );
+ if ( !a || a->a_numvals == 0 ) {
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ return rs->sr_err;
+ }
+
+ if ( bvmatch( &a->a_nvals[0], &add ) ) {
+ return rs->sr_err;
+ }
+
+ if ( bvmatch( &a->a_nvals[0], &delete ) ) {
+ is_delete = 1;
+ }
+
+ if ( bvmatch( &a->a_nvals[0], &modrdn ) ) {
+ a = attr_find( attrs, ad_reqDN );
+ if ( !a || a->a_numvals == 0 ) {
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ return rs->sr_err;
+ }
+
+ /* Was it present in the first place? If not, skip: */
+ if ( !dnIsSuffix( &a->a_nvals[0], &uuid_progress->op->o_req_ndn ) ) {
+ return rs->sr_err;
+ }
+
+ a = attr_find( attrs, ad_reqNewDN );
+ if ( !a || a->a_numvals == 0 ) {
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ return rs->sr_err;
+ }
+
+ /* Has it gone away? */
+ if ( !dnIsSuffix( &a->a_nvals[0], &uuid_progress->op->o_req_ndn ) ) {
+ is_delete = 1;
+ }
+ }
+
+ /*
+ * Only pick entries that are both:
+ */
+ a = attr_find( attrs, slap_schema.si_ad_entryCSN );
+ if ( !a || a->a_numvals == 0 ) {
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ return rs->sr_err;
+ }
+ csn[0] = a->a_nvals[0];
+
+ sid = slap_parse_csn_sid( &csn[0] );
+
+ /*
+ * newer than cookieCSN (srs->sr_state.ctxcsn)
+ */
+ cmp = 1;
+ for ( i=0; i<srs->sr_state.numcsns; i++ ) {
+ if ( sid == srs->sr_state.sids[i] ) {
+ cmp = ber_bvcmp( &csn[0], &srs->sr_state.ctxcsn[i] );
+ break;
+ }
+ }
+ if ( cmp <= 0 ) {
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_accesslog_uuid_cb: "
+ "cmp %d, csn %s too old\n",
+ op->o_log_prefix, cmp, csn[0].bv_val );
+ return rs->sr_err;
+ }
+
+ /*
+ * not newer than snapshot ctxcsn (uuid_progress->ctxcsn)
+ */
+ cmp = 0;
+ for ( i=0; i<uuid_progress->numcsns; i++ ) {
+ if ( sid == uuid_progress->sids[i] ) {
+ cmp = ber_bvcmp( &csn[0], &uuid_progress->ctxcsn[i] );
+ break;
+ }
+ }
+ if ( cmp > 0 ) {
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_accesslog_uuid_cb: "
+ "cmp %d, csn %s too new\n",
+ op->o_log_prefix, cmp, csn[0].bv_val );
+ return rs->sr_err;
+ }
+
+ a = attr_find( attrs, ad_reqEntryUUID );
+ if ( !a || a->a_numvals == 0 ) {
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+ return rs->sr_err;
+ }
+ uuid[0] = a->a_nvals[0];
+
+ bv = ldap_avl_find( uuid_progress->uuids, uuid, sp_uuid_cmp );
+ if ( bv ) {
+ /* Already checked or sent, no change */
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_accesslog_uuid_cb: "
+ "uuid %s already checked\n",
+ op->o_log_prefix, a->a_vals[0].bv_val );
+ return rs->sr_err;
+ }
+
+ if ( !is_delete ) {
+ is_delete = check_uuidlist_presence( uuid_progress->op, uuid, 1, 1 );
+ }
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_accesslog_uuid_cb: "
+ "uuid %s is %s present\n",
+ op->o_log_prefix, a->a_vals[0].bv_val,
+ is_delete ? "no longer" : "still" );
+
+ i = uuid_progress->ndel++;
+
+ bv = &uuid_progress->uuid_list[i];
+ bv->bv_val = &uuid_progress->uuid_buf[i*UUID_LEN];
+ bv->bv_len = a->a_nvals[0].bv_len;
+ AC_MEMCPY( bv->bv_val, a->a_nvals[0].bv_val, a->a_nvals[0].bv_len );
+
+ rc = ldap_avl_insert( &uuid_progress->uuids, bv, sp_uuid_cmp, ldap_avl_dup_error );
+ assert( rc == LDAP_SUCCESS );
+
+ if ( is_delete ) {
+ struct berval cookie;
+
+ slap_compose_sync_cookie( op, &cookie, srs->sr_state.ctxcsn,
+ srs->sr_state.rid, slap_serverID ? slap_serverID : -1, csn );
+ syncprov_sendinfo( uuid_progress->op, uuid_progress->rs,
+ LDAP_TAG_SYNC_ID_SET, &cookie, 0, uuid, 1 );
+ op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
+ }
+
+ if ( uuid_progress->ndel >= uuid_progress->list_len ) {
+ int ndel;
+
+ assert( uuid_progress->ndel == uuid_progress->list_len );
+ ndel = ldap_avl_free( uuid_progress->uuids, NULL );
+ assert( ndel == uuid_progress->ndel );
+ uuid_progress->uuids = NULL;
+ uuid_progress->ndel = 0;
+ }
+
+ return rs->sr_err;
+}
+
+static int
+syncprov_play_sessionlog( Operation *op, SlapReply *rs, sync_control *srs,
+ BerVarray ctxcsn, int numcsns, int *sids,
+ struct berval *mincsn, int minsid )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
+ sessionlog *sl = si->si_logs;
+ int i, j, ndel, num, nmods, mmods, do_play = 0, rc = -1;
+ BerVarray uuids, csns;
+ struct berval uuid[2] = {}, csn[2] = {};
+ slog_entry *se;
+ TAvlnode *entry;
+ char cbuf[LDAP_PVT_CSNSTR_BUFSIZE];
+ struct berval delcsn[2];
+
+ ldap_pvt_thread_rdwr_wlock( &sl->sl_mutex );
+ /* Are there any log entries, and is the consumer state
+ * present in the session log?
+ */
+ if ( !sl->sl_num ) {
+ ldap_pvt_thread_rdwr_wunlock( &sl->sl_mutex );
+ return rc;
+ }
+ assert( sl->sl_num > 0 );
+
+ for ( i=0; i<sl->sl_numcsns; i++ ) {
+ /* SID not present == new enough */
+ if ( minsid < sl->sl_sids[i] ) {
+ do_play = 1;
+ break;
+ }
+ /* SID present */
+ if ( minsid == sl->sl_sids[i] ) {
+ /* new enough? */
+ if ( ber_bvcmp( mincsn, &sl->sl_mincsn[i] ) >= 0 )
+ do_play = 1;
+ break;
+ }
+ }
+ /* SID not present == new enough */
+ if ( i == sl->sl_numcsns )
+ do_play = 1;
+
+ if ( !do_play ) {
+ ldap_pvt_thread_rdwr_wunlock( &sl->sl_mutex );
+ return rc;
+ }
+
+ num = sl->sl_num;
+ i = 0;
+ nmods = 0;
+ sl->sl_playing++;
+ ldap_pvt_thread_rdwr_wunlock( &sl->sl_mutex );
+
+ uuids = op->o_tmpalloc( (num) * sizeof( struct berval ) +
+ num * UUID_LEN, op->o_tmpmemctx );
+ uuids[0].bv_val = (char *)(uuids + num);
+ csns = op->o_tmpalloc( (num) * sizeof( struct berval ) +
+ num * LDAP_PVT_CSNSTR_BUFSIZE, op->o_tmpmemctx );
+ csns[0].bv_val = (char *)(csns + num);
+
+ ldap_pvt_thread_rdwr_rlock( &sl->sl_mutex );
+ /* Make a copy of the relevant UUIDs. Put the Deletes up front
+ * and everything else at the end. Do this first so we can
+ * let the write side manage the sessionlog again.
+ */
+ assert( sl->sl_entries );
+
+ /* Find first relevant log entry. If greater than mincsn, backtrack one entry */
+ {
+ slog_entry te = {0};
+ te.se_csn = *mincsn;
+ entry = ldap_tavl_find3( sl->sl_entries, &te, syncprov_sessionlog_cmp, &ndel );
+ }
+ if ( ndel > 0 && entry )
+ entry = ldap_tavl_next( entry, TAVL_DIR_LEFT );
+ /* if none, just start at beginning */
+ if ( !entry )
+ entry = ldap_tavl_end( sl->sl_entries, TAVL_DIR_LEFT );
+
+ do {
+ char uuidstr[40] = {};
+ slog_entry *se = entry->avl_data;
+ int k;
+
+ /* Make sure writes can still make progress */
+ ldap_pvt_thread_rdwr_runlock( &sl->sl_mutex );
+ ndel = 1;
+ for ( k=0; k<srs->sr_state.numcsns; k++ ) {
+ if ( se->se_sid == srs->sr_state.sids[k] ) {
+ ndel = ber_bvcmp( &se->se_csn, &srs->sr_state.ctxcsn[k] );
+ break;
+ }
+ }
+ if ( ndel <= 0 ) {
+ ldap_pvt_thread_rdwr_rlock( &sl->sl_mutex );
+ continue;
+ }
+ ndel = 0;
+ for ( k=0; k<numcsns; k++ ) {
+ if ( se->se_sid == sids[k] ) {
+ ndel = ber_bvcmp( &se->se_csn, &ctxcsn[k] );
+ break;
+ }
+ }
+ if ( ndel > 0 ) {
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_play_sessionlog: "
+ "cmp %d, csn %s too new, we're finished\n",
+ op->o_log_prefix, ndel, se->se_csn.bv_val );
+ ldap_pvt_thread_rdwr_rlock( &sl->sl_mutex );
+ break;
+ }
+ if ( se->se_tag == LDAP_REQ_DELETE ) {
+ j = i;
+ i++;
+ } else {
+ if ( se->se_tag == LDAP_REQ_ADD ) {
+ ldap_pvt_thread_rdwr_rlock( &sl->sl_mutex );
+ continue;
+ }
+ nmods++;
+ j = num - nmods;
+ }
+ uuids[j].bv_val = uuids[0].bv_val + (j * UUID_LEN);
+ AC_MEMCPY(uuids[j].bv_val, se->se_uuid.bv_val, UUID_LEN);
+ uuids[j].bv_len = UUID_LEN;
+
+ csns[j].bv_val = csns[0].bv_val + (j * LDAP_PVT_CSNSTR_BUFSIZE);
+ AC_MEMCPY(csns[j].bv_val, se->se_csn.bv_val, se->se_csn.bv_len);
+ csns[j].bv_len = se->se_csn.bv_len;
+ /* We're printing it */
+ csns[j].bv_val[csns[j].bv_len] = '\0';
+
+ if ( LogTest( LDAP_DEBUG_SYNC ) ) {
+ lutil_uuidstr_from_normalized( uuids[j].bv_val, uuids[j].bv_len,
+ uuidstr, 40 );
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_play_sessionlog: "
+ "picking a %s entry uuid=%s cookie=%s\n",
+ op->o_log_prefix, se->se_tag == LDAP_REQ_DELETE ? "deleted" : "modified",
+ uuidstr, csns[j].bv_val );
+ }
+ ldap_pvt_thread_rdwr_rlock( &sl->sl_mutex );
+ } while ( (entry = ldap_tavl_next( entry, TAVL_DIR_RIGHT )) != NULL );
+ ldap_pvt_thread_rdwr_runlock( &sl->sl_mutex );
+ ldap_pvt_thread_rdwr_wlock( &sl->sl_mutex );
+ sl->sl_playing--;
+ ldap_pvt_thread_rdwr_wunlock( &sl->sl_mutex );
+
+ ndel = i;
+
+ /* Zero out unused slots */
+ for ( i=ndel; i < num - nmods; i++ )
+ uuids[i].bv_len = 0;
+
+ /* Mods must be validated to see if they belong in this delete set.
+ */
+
+ mmods = nmods;
+ /* Strip any duplicates */
+ for ( i=0; i<nmods; i++ ) {
+ for ( j=0; j<ndel; j++ ) {
+ if ( bvmatch( &uuids[j], &uuids[num - 1 - i] )) {
+ uuids[num - 1 - i].bv_len = 0;
+ mmods --;
+ break;
+ }
+ }
+ if ( uuids[num - 1 - i].bv_len == 0 ) continue;
+ for ( j=0; j<i; j++ ) {
+ if ( bvmatch( &uuids[num - 1 - j], &uuids[num - 1 - i] )) {
+ uuids[num - 1 - i].bv_len = 0;
+ mmods --;
+ break;
+ }
+ }
+ }
+
+ /* Check mods now */
+ if ( mmods ) {
+ check_uuidlist_presence( op, uuids, num, nmods );
+ }
+
+ /* ITS#8768 Send entries sorted by CSN order */
+ i = j = 0;
+ while ( i < ndel || j < nmods ) {
+ struct berval cookie;
+ int index;
+
+ /* Skip over duplicate mods */
+ if ( j < nmods && BER_BVISEMPTY( &uuids[ num - 1 - j ] ) ) {
+ j++;
+ continue;
+ }
+ index = num - 1 - j;
+
+ if ( i >= ndel ) {
+ j++;
+ } else if ( j >= nmods ) {
+ index = i++;
+ /* Take the oldest by CSN order */
+ } else if ( ber_bvcmp( &csns[index], &csns[i] ) < 0 ) {
+ j++;
+ } else {
+ index = i++;
+ }
+
+ uuid[0] = uuids[index];
+ csn[0] = csns[index];
+
+ slap_compose_sync_cookie( op, &cookie, srs->sr_state.ctxcsn,
+ srs->sr_state.rid, slap_serverID ? slap_serverID : -1, csn );
+ if ( LogTest( LDAP_DEBUG_SYNC ) ) {
+ char uuidstr[40];
+ lutil_uuidstr_from_normalized( uuid[0].bv_val, uuid[0].bv_len,
+ uuidstr, 40 );
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_play_sessionlog: "
+ "sending a new disappearing entry uuid=%s cookie=%s\n",
+ op->o_log_prefix, uuidstr, cookie.bv_val );
+ }
+
+ /* TODO: we might batch those that share the same CSN (think present
+ * phase), but would have to limit how many we send out at once */
+ syncprov_sendinfo( op, rs, LDAP_TAG_SYNC_ID_SET, &cookie, 0, uuid, 1 );
+ }
+ op->o_tmpfree( uuids, op->o_tmpmemctx );
+ op->o_tmpfree( csns, op->o_tmpmemctx );
+
+ return LDAP_SUCCESS;
+}
+
+static int
+syncprov_play_accesslog( Operation *op, SlapReply *rs, sync_control *srs,
+ BerVarray ctxcsn, int numcsns, int *sids,
+ struct berval *mincsn, int minsid )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ syncprov_info_t *si = on->on_bi.bi_private;
+ Operation fop;
+ SlapReply frs = { REP_RESULT };
+ slap_callback cb = {};
+ Filter *f;
+ syncprov_accesslog_deletes uuid_progress = {
+ .op = op,
+ .rs = rs,
+ .srs = srs,
+ .ctxcsn = ctxcsn,
+ .numcsns = numcsns,
+ .sids = sids,
+ };
+ struct berval oldestcsn = BER_BVNULL, newestcsn = BER_BVNULL,
+ basedn, filterpattern = BER_BVC(
+ "(&"
+ "(entryCSN>=%s)"
+ "(entryCSN<=%s)"
+ "(reqResult=0)"
+ "(|"
+ "(reqDN:dnSubtreeMatch:=%s)"
+ "(reqNewDN:dnSubtreeMatch:=%s)"
+ ")"
+ "(|"
+ "(objectclass=auditWriteObject)"
+ "(objectclass=auditExtended)"
+ "))" );
+ BackendDB *db;
+ Entry *e;
+ Attribute *a;
+ int *minsids, i, j = 0, rc = -1;
+
+ assert( !BER_BVISNULL( &si->si_logbase ) );
+
+ db = select_backend( &si->si_logbase, 0 );
+ if ( !db ) {
+ Debug( LDAP_DEBUG_ANY, "%s syncprov_play_accesslog: "
+ "No database configured to hold accesslog dn=%s\n",
+ op->o_log_prefix, si->si_logbase.bv_val );
+ return LDAP_NO_SUCH_OBJECT;
+ }
+
+ fop = *op;
+ fop.o_sync_mode = 0;
+ fop.o_bd = db;
+ rc = be_entry_get_rw( &fop, &si->si_logbase, NULL, ad_minCSN, 0, &e );
+ if ( rc ) {
+ return rc;
+ }
+
+ a = attr_find( e->e_attrs, ad_minCSN );
+ if ( !a ) {
+ be_entry_release_rw( &fop, e, 0 );
+ return LDAP_NO_SUCH_ATTRIBUTE;
+ }
+
+ /*
+ * If we got here:
+ * - the consumer's cookie (srs->sr_state.ctxcsn) has the same sids in the
+ * same order as ctxcsn
+ * - at least one of the cookie's csns is older than its ctxcsn counterpart
+ *
+ * Now prepare the filter, we want it to be the union of all the intervals
+ * between the cookie and our contextCSN for each sid. Right now, we can't
+ * specify them separately, so just pick the boundary CSNs of non-empty
+ * intervals as a conservative overestimate.
+ *
+ * Also check accesslog can actually serve this query based on what's
+ * stored in minCSN.
+ */
+
+ assert( srs->sr_state.numcsns == numcsns );
+
+ minsids = slap_parse_csn_sids( a->a_nvals, a->a_numvals, op->o_tmpmemctx );
+ slap_sort_csn_sids( a->a_nvals, minsids, a->a_numvals, op->o_tmpmemctx );
+ for ( i=0, j=0; i < numcsns; i++ ) {
+ assert( srs->sr_state.sids[i] == sids[i] );
+ if ( ber_bvcmp( &srs->sr_state.ctxcsn[i], &ctxcsn[i] ) >= 0 ) {
+ /* Consumer is up to date for this sid */
+ continue;
+ }
+ for ( ; j < a->a_numvals && minsids[j] < sids[i]; j++ )
+ /* Find the right minCSN, if present */;
+ if ( j == a->a_numvals || minsids[j] != sids[i] ||
+ ber_bvcmp( &srs->sr_state.ctxcsn[i], &a->a_nvals[j] ) < 0 ) {
+ /* Consumer is missing changes for a sid and minCSN indicates we
+ * can't replay all relevant history */
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_play_accesslog: "
+ "accesslog information inadequate for log replay on csn=%s\n",
+ op->o_log_prefix, srs->sr_state.ctxcsn[i].bv_val );
+ slap_sl_free( minsids, op->o_tmpmemctx );
+ be_entry_release_rw( &fop, e, 0 );
+ return 1;
+ }
+ if ( BER_BVISEMPTY( &oldestcsn ) ||
+ ber_bvcmp( &oldestcsn, &srs->sr_state.ctxcsn[i] ) > 0 ) {
+ oldestcsn = srs->sr_state.ctxcsn[i];
+ }
+ if ( BER_BVISEMPTY( &newestcsn ) ||
+ ber_bvcmp( &newestcsn, &ctxcsn[i] ) < 0 ) {
+ newestcsn = ctxcsn[i];
+ }
+ }
+ assert( !BER_BVISEMPTY( &oldestcsn ) && !BER_BVISEMPTY( &newestcsn ) &&
+ ber_bvcmp( &oldestcsn, &newestcsn ) < 0 );
+ slap_sl_free( minsids, op->o_tmpmemctx );
+
+ filter_escape_value_x( &op->o_req_ndn, &basedn, fop.o_tmpmemctx );
+ /* filter_escape_value_x sets output to BVNULL if input value is empty,
+ * supply our own copy */
+ if ( BER_BVISEMPTY( &basedn ) ) {
+ basedn.bv_val = "";
+ }
+ fop.o_req_ndn = fop.o_req_dn = si->si_logbase;
+ fop.ors_filterstr.bv_val = fop.o_tmpalloc(
+ filterpattern.bv_len +
+ oldestcsn.bv_len + newestcsn.bv_len + 2 * basedn.bv_len,
+ fop.o_tmpmemctx );
+ fop.ors_filterstr.bv_len = sprintf( fop.ors_filterstr.bv_val,
+ filterpattern.bv_val,
+ oldestcsn.bv_val, newestcsn.bv_val, basedn.bv_val, basedn.bv_val );
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_play_accesslog: "
+ "prepared filter '%s', base='%s'\n",
+ op->o_log_prefix, fop.ors_filterstr.bv_val, si->si_logbase.bv_val );
+ f = str2filter_x( &fop, fop.ors_filterstr.bv_val );
+ assert( f != NULL );
+ fop.ors_filter = f;
+
+ if ( !BER_BVISEMPTY( &basedn ) ) {
+ fop.o_tmpfree( basedn.bv_val, fop.o_tmpmemctx );
+ }
+ be_entry_release_rw( &fop, e, 0 );
+
+ /*
+ * Allocate memory for list_len uuids for use by the callback, populate
+ * with entries that we have sent or checked still match the filter.
+ * A disappearing entry gets its uuid sent as a delete.
+ *
+ * in the callback, we need:
+ * - original op and rs so we can send the message
+ * - sync_control
+ * - the uuid buffer and list and their length
+ * - number of uuids we already have in the list
+ * - the lookup structure so we don't have to check/send a uuid twice
+ * (AVL?)
+ */
+ uuid_progress.list_len = SLAP_SYNCUUID_SET_SIZE;
+ uuid_progress.uuid_list = fop.o_tmpalloc( (uuid_progress.list_len) * sizeof(struct berval), fop.o_tmpmemctx );
+ uuid_progress.uuid_buf = fop.o_tmpalloc( (uuid_progress.list_len) * UUID_LEN, fop.o_tmpmemctx );
+
+ cb.sc_private = &uuid_progress;
+ cb.sc_response = syncprov_accesslog_uuid_cb;
+
+ fop.o_callback = &cb;
+
+ rc = fop.o_bd->be_search( &fop, &frs );
+
+ ldap_avl_free( uuid_progress.uuids, NULL );
+ fop.o_tmpfree( uuid_progress.uuid_buf, fop.o_tmpmemctx );
+ fop.o_tmpfree( uuid_progress.uuid_list, fop.o_tmpmemctx );
+ fop.o_tmpfree( fop.ors_filterstr.bv_val, fop.o_tmpmemctx );
+ filter_free_x( &fop, f, 1 );
+
+ return rc;
+}
+
+static int
+syncprov_new_ctxcsn( opcookie *opc, syncprov_info_t *si, int csn_changed, int numvals, BerVarray vals )
+{
+ unsigned i;
+ int j, sid;
+
+ for ( i=0; i<numvals; i++ ) {
+ sid = slap_parse_csn_sid( &vals[i] );
+ for ( j=0; j<si->si_numcsns; j++ ) {
+ if ( sid < si->si_sids[j] )
+ break;
+ if ( sid == si->si_sids[j] ) {
+ if ( ber_bvcmp( &vals[i], &si->si_ctxcsn[j] ) > 0 ) {
+ ber_bvreplace( &si->si_ctxcsn[j], &vals[i] );
+ csn_changed = 1;
+ }
+ break;
+ }
+ }
+
+ if ( j == si->si_numcsns || sid != si->si_sids[j] ) {
+ slap_insert_csn_sids( (struct sync_cookie *)&si->si_ctxcsn,
+ j, sid, &vals[i] );
+ csn_changed = 1;
+ }
+ }
+ if ( csn_changed )
+ si->si_dirty = 0;
+ ldap_pvt_thread_rdwr_wunlock( &si->si_csn_rwlock );
+
+ if ( csn_changed ) {
+ syncops *ss;
+ ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
+ for ( ss = si->si_ops; ss; ss = ss->s_next ) {
+ if ( ss->s_op->o_abandon )
+ continue;
+ /* Send the updated csn to all syncrepl consumers,
+ * including the server from which it originated.
+ * The syncrepl consumer and syncprov provider on
+ * the originating server may be configured to store
+ * their csn values in different entries.
+ */
+ syncprov_qresp( opc, ss, LDAP_SYNC_NEW_COOKIE );
+ }
+ ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
+ }
+ return csn_changed;
+}
+
+static int
+syncprov_op_response( Operation *op, SlapReply *rs )
+{
+ opcookie *opc = op->o_callback->sc_private;
+ slap_overinst *on = opc->son;
+ syncprov_info_t *si = on->on_bi.bi_private;
+ syncmatches *sm;
+
+ if ( rs->sr_err == LDAP_SUCCESS )
+ {
+ struct berval maxcsn;
+ char cbuf[LDAP_PVT_CSNSTR_BUFSIZE];
+ int do_check = 0, have_psearches, foundit, csn_changed = 0;
+
+ ldap_pvt_thread_mutex_lock( &si->si_resp_mutex );
+
+ /* Update our context CSN */
+ cbuf[0] = '\0';
+ maxcsn.bv_val = cbuf;
+ maxcsn.bv_len = sizeof(cbuf);
+ ldap_pvt_thread_rdwr_wlock( &si->si_csn_rwlock );
+
+ slap_get_commit_csn( op, &maxcsn, &foundit );
+ if ( BER_BVISEMPTY( &maxcsn ) && SLAP_GLUE_SUBORDINATE( op->o_bd )) {
+ /* syncrepl queues the CSN values in the db where
+ * it is configured , not where the changes are made.
+ * So look for a value in the glue db if we didn't
+ * find any in this db.
+ */
+ BackendDB *be = op->o_bd;
+ op->o_bd = select_backend( &be->be_nsuffix[0], 1);
+ maxcsn.bv_val = cbuf;
+ maxcsn.bv_len = sizeof(cbuf);
+ slap_get_commit_csn( op, &maxcsn, &foundit );
+ op->o_bd = be;
+ }
+ if ( !BER_BVISEMPTY( &maxcsn ) ) {
+ int i, sid;
+#ifdef CHECK_CSN
+ Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax;
+ assert( !syn->ssyn_validate( syn, &maxcsn ));
+#endif
+ sid = slap_parse_csn_sid( &maxcsn );
+ for ( i=0; i<si->si_numcsns; i++ ) {
+ if ( sid < si->si_sids[i] )
+ break;
+ if ( sid == si->si_sids[i] ) {
+ if ( ber_bvcmp( &maxcsn, &si->si_ctxcsn[i] ) > 0 ) {
+ ber_bvreplace( &si->si_ctxcsn[i], &maxcsn );
+ csn_changed = 1;
+ }
+ break;
+ }
+ }
+ /* It's a new SID for us */
+ if ( i == si->si_numcsns || sid != si->si_sids[i] ) {
+ slap_insert_csn_sids((struct sync_cookie *)&(si->si_ctxcsn),
+ i, sid, &maxcsn );
+ csn_changed = 1;
+ }
+ }
+
+ /* Don't do any processing for consumer contextCSN updates */
+ if ( SLAPD_SYNC_IS_SYNCCONN( op->o_connid ) &&
+ op->o_tag == LDAP_REQ_MODIFY &&
+ op->orm_modlist &&
+ op->orm_modlist->sml_op == LDAP_MOD_REPLACE &&
+ op->orm_modlist->sml_desc == slap_schema.si_ad_contextCSN ) {
+ /* Catch contextCSN updates from syncrepl. We have to look at
+ * all the attribute values, as there may be more than one csn
+ * that changed, and only one can be passed in the csn queue.
+ */
+ csn_changed = syncprov_new_ctxcsn( opc, si, csn_changed,
+ op->orm_modlist->sml_numvals, op->orm_modlist->sml_values );
+ if ( csn_changed )
+ si->si_numops++;
+ goto leave;
+ }
+ if ( op->o_dont_replicate ) {
+ if ( csn_changed )
+ si->si_numops++;
+ ldap_pvt_thread_rdwr_wunlock( &si->si_csn_rwlock );
+ goto leave;
+ }
+
+ /* If we're adding the context entry, parse all of its contextCSNs */
+ if ( op->o_tag == LDAP_REQ_ADD &&
+ dn_match( &op->o_req_ndn, &si->si_contextdn )) {
+ Attribute *a = attr_find( op->ora_e->e_attrs, slap_schema.si_ad_contextCSN );
+ if ( a ) {
+ csn_changed = syncprov_new_ctxcsn( opc, si, csn_changed, a->a_numvals, a->a_vals );
+ if ( csn_changed )
+ si->si_numops++;
+ goto added;
+ }
+ }
+
+ if ( csn_changed )
+ si->si_numops++;
+ if ( si->si_chkops || si->si_chktime ) {
+ /* Never checkpoint adding the context entry,
+ * it will deadlock
+ */
+ if ( op->o_tag != LDAP_REQ_ADD ||
+ !dn_match( &op->o_req_ndn, &si->si_contextdn )) {
+ if ( si->si_chkops && si->si_numops >= si->si_chkops ) {
+ do_check = 1;
+ si->si_numops = 0;
+ }
+ if ( si->si_chktime &&
+ (op->o_time - si->si_chklast >= si->si_chktime )) {
+ if ( si->si_chklast ) {
+ do_check = 1;
+ si->si_chklast = op->o_time;
+ } else {
+ si->si_chklast = 1;
+ }
+ }
+ }
+ }
+ si->si_dirty = !csn_changed;
+ ldap_pvt_thread_rdwr_wunlock( &si->si_csn_rwlock );
+
+added:
+ if ( do_check ) {
+ ldap_pvt_thread_rdwr_rlock( &si->si_csn_rwlock );
+ syncprov_checkpoint( op, on );
+ ldap_pvt_thread_rdwr_runlock( &si->si_csn_rwlock );
+ }
+
+ /* only update consumer ctx if this is a newer csn */
+ if ( csn_changed ) {
+ opc->sctxcsn = maxcsn;
+ }
+
+ /* Handle any persistent searches */
+ ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
+ have_psearches = ( si->si_ops != NULL );
+ ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
+ if ( have_psearches ) {
+ switch(op->o_tag) {
+ case LDAP_REQ_ADD:
+ case LDAP_REQ_MODIFY:
+ case LDAP_REQ_MODRDN:
+ case LDAP_REQ_EXTENDED:
+ syncprov_matchops( op, opc, 0 );
+ break;
+ case LDAP_REQ_DELETE:
+ /* for each match in opc->smatches:
+ * send DELETE msg
+ */
+ for ( sm = opc->smatches; sm; sm=sm->sm_next ) {
+ if ( sm->sm_op->s_op->o_abandon )
+ continue;
+ syncprov_qresp( opc, sm->sm_op, LDAP_SYNC_DELETE );
+ }
+ if ( opc->ssres.s_info )
+ free_resinfo( &opc->ssres );
+ break;
+ }
+ }
+
+ /* Add any log records */
+ if ( si->si_logs ) {
+ syncprov_add_slog( op );
+ }
+leave: ldap_pvt_thread_mutex_unlock( &si->si_resp_mutex );
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+/* We don't use a subentry to store the context CSN any more.
+ * We expose the current context CSN as an operational attribute
+ * of the suffix entry.
+ */
+static int
+syncprov_op_compare( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ syncprov_info_t *si = on->on_bi.bi_private;
+ int rc = SLAP_CB_CONTINUE;
+
+ if ( dn_match( &op->o_req_ndn, &si->si_contextdn ) &&
+ op->oq_compare.rs_ava->aa_desc == slap_schema.si_ad_contextCSN )
+ {
+ Entry e = {0};
+ Attribute a = {0};
+
+ e.e_name = si->si_contextdn;
+ e.e_nname = si->si_contextdn;
+ e.e_attrs = &a;
+
+ a.a_desc = slap_schema.si_ad_contextCSN;
+
+ ldap_pvt_thread_rdwr_rlock( &si->si_csn_rwlock );
+
+ a.a_vals = si->si_ctxcsn;
+ a.a_nvals = a.a_vals;
+ a.a_numvals = si->si_numcsns;
+
+ rs->sr_err = access_allowed( op, &e, op->oq_compare.rs_ava->aa_desc,
+ &op->oq_compare.rs_ava->aa_value, ACL_COMPARE, NULL );
+ if ( ! rs->sr_err ) {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto return_results;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, &e, get_assertion( op ) ) != LDAP_COMPARE_TRUE ) )
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+
+ rs->sr_err = LDAP_COMPARE_FALSE;
+
+ if ( attr_valfind( &a,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ &op->oq_compare.rs_ava->aa_value, NULL, op->o_tmpmemctx ) == 0 )
+ {
+ rs->sr_err = LDAP_COMPARE_TRUE;
+ }
+
+return_results:;
+
+ ldap_pvt_thread_rdwr_runlock( &si->si_csn_rwlock );
+
+ send_ldap_result( op, rs );
+
+ if( rs->sr_err == LDAP_COMPARE_FALSE || rs->sr_err == LDAP_COMPARE_TRUE ) {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+ rc = rs->sr_err;
+ }
+
+ return rc;
+}
+
+static int
+syncprov_op_mod( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ syncprov_info_t *si = on->on_bi.bi_private;
+ slap_callback *cb;
+ opcookie *opc;
+ int have_psearches, cbsize;
+
+ ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
+ have_psearches = ( si->si_ops != NULL );
+ si->si_active++;
+ ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
+
+ cbsize = sizeof(slap_callback) + sizeof(opcookie) +
+ (have_psearches ? sizeof(modinst) : 0 );
+
+ cb = op->o_tmpcalloc(1, cbsize, op->o_tmpmemctx);
+ opc = (opcookie *)(cb+1);
+ opc->son = on;
+ cb->sc_response = syncprov_op_response;
+ cb->sc_cleanup = syncprov_op_cleanup;
+ cb->sc_private = opc;
+ cb->sc_next = op->o_callback;
+ op->o_callback = cb;
+
+ opc->osid = -1;
+ opc->rsid = -1;
+ if ( op->o_csn.bv_val ) {
+ opc->osid = slap_parse_csn_sid( &op->o_csn );
+ }
+ if ( op->o_controls ) {
+ struct sync_cookie *scook =
+ op->o_controls[slap_cids.sc_LDAPsync];
+ if ( scook )
+ opc->rsid = scook->sid;
+ }
+
+ if ( op->o_dont_replicate )
+ return SLAP_CB_CONTINUE;
+
+ /* If there are active persistent searches, lock this operation.
+ * See seqmod.c for the locking logic on its own.
+ */
+ if ( have_psearches ) {
+ modtarget *mt, mtdummy;
+ modinst *mi;
+
+ mi = (modinst *)(opc+1);
+ mi->mi_op = op;
+
+ /* See if we're already modifying this entry... */
+ mtdummy.mt_dn = op->o_req_ndn;
+retry:
+ ldap_pvt_thread_mutex_lock( &si->si_mods_mutex );
+ mt = ldap_avl_find( si->si_mods, &mtdummy, sp_avl_cmp );
+ if ( mt ) {
+ ldap_pvt_thread_mutex_lock( &mt->mt_mutex );
+ if ( mt->mt_mods == NULL ) {
+ /* Cannot reuse this mt, as another thread is about
+ * to release it in syncprov_op_cleanup. Wait for them
+ * to finish; our own insert is required to succeed.
+ */
+ ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
+ ldap_pvt_thread_mutex_unlock( &si->si_mods_mutex );
+ ldap_pvt_thread_yield();
+ goto retry;
+ }
+ }
+ if ( mt ) {
+ mt->mt_tail->mi_next = mi;
+ mt->mt_tail = mi;
+ ldap_pvt_thread_mutex_unlock( &si->si_mods_mutex );
+ /* wait for this op to get to head of list */
+ while ( mt->mt_mods != mi ) {
+ modinst *m2;
+ /* don't wait on other mods from the same thread */
+ for ( m2 = mt->mt_mods; m2; m2 = m2->mi_next ) {
+ if ( m2->mi_op->o_threadctx == op->o_threadctx ) {
+ break;
+ }
+ }
+ if ( m2 )
+ break;
+
+ ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
+ /* FIXME: if dynamic config can delete overlays or
+ * databases we'll have to check for cleanup here.
+ * Currently it's not an issue because there are
+ * no dynamic config deletes...
+ */
+ if ( slapd_shutdown )
+ return SLAPD_ABANDON;
+
+ if ( !ldap_pvt_thread_pool_pausecheck( &connection_pool ))
+ ldap_pvt_thread_yield();
+ ldap_pvt_thread_mutex_lock( &mt->mt_mutex );
+
+ /* clean up if the caller is giving up */
+ if ( op->o_abandon ) {
+ modinst **m2;
+ slap_callback **sc;
+ for (m2 = &mt->mt_mods; ; m2 = &(*m2)->mi_next) {
+ if ( *m2 == mi ) {
+ *m2 = mi->mi_next;
+ if ( mt->mt_tail == mi )
+ mt->mt_tail = ( m2 == &mt->mt_mods ) ? NULL : (modinst *)m2;
+ break;
+ }
+ }
+ for (sc = &op->o_callback; ; sc = &(*sc)->sc_next) {
+ if ( *sc == cb ) {
+ *sc = cb->sc_next;
+ break;
+ }
+ }
+ op->o_tmpfree( cb, op->o_tmpmemctx );
+ ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
+ return SLAPD_ABANDON;
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &mt->mt_mutex );
+ } else {
+ /* Record that we're modifying this entry now */
+ mt = ch_malloc( sizeof(modtarget) );
+ mt->mt_mods = mi;
+ mt->mt_tail = mi;
+ ber_dupbv( &mt->mt_dn, &mi->mi_op->o_req_ndn );
+ ldap_pvt_thread_mutex_init( &mt->mt_mutex );
+ ldap_avl_insert( &si->si_mods, mt, sp_avl_cmp, ldap_avl_dup_error );
+ ldap_pvt_thread_mutex_unlock( &si->si_mods_mutex );
+ }
+ opc->smt = mt;
+ }
+
+ if (( have_psearches || si->si_logs ) && op->o_tag != LDAP_REQ_ADD )
+ syncprov_matchops( op, opc, 1 );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+syncprov_op_extended( Operation *op, SlapReply *rs )
+{
+ if ( exop_is_write( op ))
+ return syncprov_op_mod( op, rs );
+
+ return SLAP_CB_CONTINUE;
+}
+
+typedef struct searchstate {
+ slap_overinst *ss_on;
+ syncops *ss_so;
+ BerVarray ss_ctxcsn;
+ int *ss_sids;
+ int ss_numcsns;
+#define SS_PRESENT 0x01
+#define SS_CHANGED 0x02
+ int ss_flags;
+} searchstate;
+
+typedef struct SyncOperationBuffer {
+ Operation sob_op;
+ Opheader sob_hdr;
+ OpExtra sob_oe;
+ AttributeName sob_extra; /* not always present */
+ /* Further data allocated here */
+} SyncOperationBuffer;
+
+static void
+syncprov_detach_op( Operation *op, syncops *so, slap_overinst *on )
+{
+ SyncOperationBuffer *sopbuf2;
+ Operation *op2;
+ int i, alen = 0;
+ size_t size;
+ char *ptr;
+ GroupAssertion *g1, *g2;
+
+ /* count the search attrs */
+ for (i=0; op->ors_attrs && !BER_BVISNULL( &op->ors_attrs[i].an_name ); i++) {
+ alen += op->ors_attrs[i].an_name.bv_len + 1;
+ }
+ /* Make a new copy of the operation */
+ size = offsetof( SyncOperationBuffer, sob_extra ) +
+ (i ? ( (i+1) * sizeof(AttributeName) + alen) : 0) +
+ op->o_req_dn.bv_len + 1 +
+ op->o_req_ndn.bv_len + 1 +
+ op->o_ndn.bv_len + 1 +
+ so->s_filterstr.bv_len + 1;
+ sopbuf2 = ch_calloc( 1, size );
+ op2 = &sopbuf2->sob_op;
+ op2->o_hdr = &sopbuf2->sob_hdr;
+ LDAP_SLIST_FIRST(&op2->o_extra) = &sopbuf2->sob_oe;
+
+ /* Copy the fields we care about explicitly, leave the rest alone */
+ *op2->o_hdr = *op->o_hdr;
+ op2->o_tag = op->o_tag;
+ op2->o_time = op->o_time;
+ op2->o_bd = on->on_info->oi_origdb;
+ op2->o_request = op->o_request;
+ op2->o_managedsait = op->o_managedsait;
+ LDAP_SLIST_FIRST(&op2->o_extra)->oe_key = on;
+ LDAP_SLIST_NEXT(LDAP_SLIST_FIRST(&op2->o_extra), oe_next) = NULL;
+
+ ptr = (char *) sopbuf2 + offsetof( SyncOperationBuffer, sob_extra );
+ if ( i ) {
+ op2->ors_attrs = (AttributeName *) ptr;
+ ptr = (char *) &op2->ors_attrs[i+1];
+ for (i=0; !BER_BVISNULL( &op->ors_attrs[i].an_name ); i++) {
+ op2->ors_attrs[i] = op->ors_attrs[i];
+ op2->ors_attrs[i].an_name.bv_val = ptr;
+ ptr = lutil_strcopy( ptr, op->ors_attrs[i].an_name.bv_val ) + 1;
+ }
+ BER_BVZERO( &op2->ors_attrs[i].an_name );
+ }
+
+ op2->o_authz = op->o_authz;
+ op2->o_ndn.bv_val = ptr;
+ ptr = lutil_strcopy(ptr, op->o_ndn.bv_val) + 1;
+ op2->o_dn = op2->o_ndn;
+ op2->o_req_dn.bv_len = op->o_req_dn.bv_len;
+ op2->o_req_dn.bv_val = ptr;
+ ptr = lutil_strcopy(ptr, op->o_req_dn.bv_val) + 1;
+ op2->o_req_ndn.bv_len = op->o_req_ndn.bv_len;
+ op2->o_req_ndn.bv_val = ptr;
+ ptr = lutil_strcopy(ptr, op->o_req_ndn.bv_val) + 1;
+ op2->ors_filterstr.bv_val = ptr;
+ strcpy( ptr, so->s_filterstr.bv_val );
+ op2->ors_filterstr.bv_len = so->s_filterstr.bv_len;
+
+ /* Skip the AND/GE clause that we stuck on in front */
+ if ( so->s_flags & PS_FIX_FILTER ) {
+ op2->ors_filter = op->ors_filter->f_and->f_next;
+ so->s_flags ^= PS_FIX_FILTER;
+ } else {
+ op2->ors_filter = op->ors_filter;
+ }
+ op2->ors_filter = filter_dup( op2->ors_filter, NULL );
+ so->s_op = op2;
+
+ /* Copy any cached group ACLs individually */
+ op2->o_groups = NULL;
+ for ( g1=op->o_groups; g1; g1=g1->ga_next ) {
+ g2 = ch_malloc( sizeof(GroupAssertion) + g1->ga_len );
+ *g2 = *g1;
+ strcpy( g2->ga_ndn, g1->ga_ndn );
+ g2->ga_next = op2->o_groups;
+ op2->o_groups = g2;
+ }
+ /* Don't allow any further group caching */
+ op2->o_do_not_cache = 1;
+
+ /* Add op2 to conn so abandon will find us */
+ op->o_conn->c_n_ops_executing++;
+ op->o_conn->c_n_ops_completed--;
+ LDAP_STAILQ_INSERT_TAIL( &op->o_conn->c_ops, op2, o_next );
+ so->s_flags |= PS_IS_DETACHED;
+
+ /* Prevent anyone else from trying to send a result for this op */
+ op->o_abandon = 1;
+}
+
+static int
+syncprov_search_response( Operation *op, SlapReply *rs )
+{
+ searchstate *ss = op->o_callback->sc_private;
+ slap_overinst *on = ss->ss_on;
+ syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
+ sync_control *srs = op->o_controls[slap_cids.sc_LDAPsync];
+
+ if ( rs->sr_type == REP_SEARCH || rs->sr_type == REP_SEARCHREF ) {
+ Attribute *a;
+ /* If we got a referral without a referral object, there's
+ * something missing that we cannot replicate. Just ignore it.
+ * The consumer will abort because we didn't send the expected
+ * control.
+ */
+ if ( !rs->sr_entry ) {
+ assert( rs->sr_entry != NULL );
+ Debug( LDAP_DEBUG_ANY, "%s syncprov_search_response: "
+ "bogus referral in context\n", op->o_log_prefix );
+ return SLAP_CB_CONTINUE;
+ }
+ a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryCSN );
+ if ( a == NULL && rs->sr_operational_attrs != NULL ) {
+ a = attr_find( rs->sr_operational_attrs, slap_schema.si_ad_entryCSN );
+ }
+ if ( a ) {
+ int i, sid;
+ sid = slap_parse_csn_sid( &a->a_nvals[0] );
+
+ /* If not a persistent search */
+ if ( !ss->ss_so ) {
+ /* Make sure entry is less than the snapshot'd contextCSN */
+ for ( i=0; i<ss->ss_numcsns; i++ ) {
+ if ( sid == ss->ss_sids[i] && ber_bvcmp( &a->a_nvals[0],
+ &ss->ss_ctxcsn[i] ) > 0 ) {
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_search_response: "
+ "Entry %s CSN %s greater than snapshot %s\n",
+ op->o_log_prefix,
+ rs->sr_entry->e_name.bv_val,
+ a->a_nvals[0].bv_val,
+ ss->ss_ctxcsn[i].bv_val );
+ return LDAP_SUCCESS;
+ }
+ }
+ }
+
+ /* Don't send old entries twice */
+ if ( srs->sr_state.ctxcsn ) {
+ for ( i=0; i<srs->sr_state.numcsns; i++ ) {
+ if ( sid == srs->sr_state.sids[i] &&
+ ber_bvcmp( &a->a_nvals[0],
+ &srs->sr_state.ctxcsn[i] )<= 0 ) {
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_search_response: "
+ "Entry %s CSN %s older or equal to ctx %s\n",
+ op->o_log_prefix,
+ rs->sr_entry->e_name.bv_val,
+ a->a_nvals[0].bv_val,
+ srs->sr_state.ctxcsn[i].bv_val );
+ return LDAP_SUCCESS;
+ }
+ }
+ }
+ }
+ rs->sr_ctrls = op->o_tmpalloc( sizeof(LDAPControl *)*2,
+ op->o_tmpmemctx );
+ rs->sr_ctrls[1] = NULL;
+ rs->sr_flags |= REP_CTRLS_MUSTBEFREED;
+ /* If we're in delta-sync mode, always send a cookie */
+ if ( si->si_nopres && si->si_usehint && a ) {
+ struct berval cookie;
+ slap_compose_sync_cookie( op, &cookie, a->a_nvals, srs->sr_state.rid,
+ slap_serverID ? slap_serverID : -1, NULL );
+ rs->sr_err = syncprov_state_ctrl( op, rs, rs->sr_entry,
+ LDAP_SYNC_ADD, rs->sr_ctrls, 0, 1, &cookie );
+ op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
+ } else {
+ rs->sr_err = syncprov_state_ctrl( op, rs, rs->sr_entry,
+ LDAP_SYNC_ADD, rs->sr_ctrls, 0, 0, NULL );
+ }
+ } else if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS ) {
+ struct berval cookie = BER_BVNULL;
+
+ if ( ( ss->ss_flags & SS_CHANGED ) &&
+ ss->ss_ctxcsn && !BER_BVISNULL( &ss->ss_ctxcsn[0] )) {
+ slap_compose_sync_cookie( op, &cookie, ss->ss_ctxcsn,
+ srs->sr_state.rid,
+ slap_serverID ? slap_serverID : -1, NULL );
+
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_search_response: cookie=%s\n",
+ op->o_log_prefix, cookie.bv_val );
+ }
+
+ /* Is this a regular refresh?
+ * Note: refresh never gets here if there were no changes
+ */
+ if ( !ss->ss_so ) {
+ rs->sr_ctrls = op->o_tmpalloc( sizeof(LDAPControl *)*2,
+ op->o_tmpmemctx );
+ rs->sr_ctrls[1] = NULL;
+ rs->sr_flags |= REP_CTRLS_MUSTBEFREED;
+ rs->sr_err = syncprov_done_ctrl( op, rs, rs->sr_ctrls,
+ 0, 1, &cookie, ( ss->ss_flags & SS_PRESENT ) ? LDAP_SYNC_REFRESH_PRESENTS :
+ LDAP_SYNC_REFRESH_DELETES );
+ op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
+ } else {
+ /* It's RefreshAndPersist, transition to Persist phase */
+ syncprov_sendinfo( op, rs, ( ss->ss_flags & SS_PRESENT ) ?
+ LDAP_TAG_SYNC_REFRESH_PRESENT : LDAP_TAG_SYNC_REFRESH_DELETE,
+ ( ss->ss_flags & SS_CHANGED ) ? &cookie : NULL,
+ 1, NULL, 0 );
+ if ( !BER_BVISNULL( &cookie ))
+ op->o_tmpfree( cookie.bv_val, op->o_tmpmemctx );
+
+ /* Detach this Op from frontend control */
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+
+ /* But not if this connection was closed along the way */
+ if ( op->o_abandon ) {
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+ /* syncprov_ab_cleanup will free this syncop */
+ return SLAPD_ABANDON;
+
+ } else {
+ ldap_pvt_thread_mutex_lock( &ss->ss_so->s_mutex );
+ /* Turn off the refreshing flag */
+ ss->ss_so->s_flags ^= PS_IS_REFRESHING;
+
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_search_response: "
+ "detaching op\n", op->o_log_prefix );
+ syncprov_detach_op( op, ss->ss_so, on );
+
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+
+ /* If there are queued responses, fire them off */
+ if ( ss->ss_so->s_res )
+ syncprov_qstart( ss->ss_so );
+ ldap_pvt_thread_mutex_unlock( &ss->ss_so->s_mutex );
+ }
+
+ return LDAP_SUCCESS;
+ }
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+syncprov_op_search( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
+ slap_callback *cb;
+ int gotstate = 0, changed = 0, do_present = 0;
+ syncops *sop = NULL;
+ searchstate *ss;
+ sync_control *srs;
+ BerVarray ctxcsn;
+ int i, *sids, numcsns;
+ struct berval mincsn, maxcsn;
+ int minsid, maxsid;
+ int dirty = 0;
+
+ if ( !(op->o_sync_mode & SLAP_SYNC_REFRESH) ) return SLAP_CB_CONTINUE;
+
+ if ( op->ors_deref & LDAP_DEREF_SEARCHING ) {
+ send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR, "illegal value for derefAliases" );
+ return rs->sr_err;
+ }
+
+ srs = op->o_controls[slap_cids.sc_LDAPsync];
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_op_search: "
+ "got a %ssearch with a cookie=%s\n",
+ op->o_log_prefix,
+ op->o_sync_mode & SLAP_SYNC_PERSIST ? "persistent ": "",
+ srs->sr_state.octet_str.bv_val );
+
+ /* If this is a persistent search, set it up right away */
+ if ( op->o_sync_mode & SLAP_SYNC_PERSIST ) {
+ syncops so = {0};
+ fbase_cookie fc;
+ opcookie opc;
+ slap_callback sc = {0};
+
+ fc.fss = &so;
+ fc.fbase = 0;
+ so.s_eid = NOID;
+ so.s_op = op;
+ so.s_flags = PS_IS_REFRESHING | PS_FIND_BASE;
+ /* syncprov_findbase expects to be called as a callback... */
+ sc.sc_private = &opc;
+ opc.son = on;
+ ldap_pvt_thread_mutex_init( &so.s_mutex );
+ cb = op->o_callback;
+ op->o_callback = &sc;
+ rs->sr_err = syncprov_findbase( op, &fc );
+ op->o_callback = cb;
+ ldap_pvt_thread_mutex_destroy( &so.s_mutex );
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+ sop = ch_malloc( sizeof( syncops ));
+ *sop = so;
+ sop->s_rid = srs->sr_state.rid;
+ sop->s_sid = srs->sr_state.sid;
+ /* set refcount=2 to prevent being freed out from under us
+ * by abandons that occur while we're running here
+ */
+ sop->s_inuse = 2;
+
+ ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
+ while ( si->si_active ) {
+ /* Wait for active mods to finish before proceeding, as they
+ * may already have inspected the si_ops list looking for
+ * consumers to replicate the change to. Using the log
+ * doesn't help, as we may finish playing it before the
+ * active mods gets added to it.
+ */
+ ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
+ if ( slapd_shutdown ) {
+aband:
+ ch_free( sop->s_base.bv_val );
+ ch_free( sop );
+ return SLAPD_ABANDON;
+ }
+ if ( !ldap_pvt_thread_pool_pausecheck( &connection_pool ))
+ ldap_pvt_thread_yield();
+ ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
+ }
+ if ( op->o_abandon ) {
+ ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
+ goto aband;
+ }
+ ldap_pvt_thread_mutex_init( &sop->s_mutex );
+ sop->s_next = si->si_ops;
+ sop->s_si = si;
+ si->si_ops = sop;
+ ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_op_search: "
+ "registered persistent search\n", op->o_log_prefix );
+ }
+
+ /* snapshot the ctxcsn
+ * Note: this must not be done before the psearch setup. (ITS#8365)
+ */
+ ldap_pvt_thread_rdwr_rlock( &si->si_csn_rwlock );
+ numcsns = si->si_numcsns;
+ if ( numcsns ) {
+ ber_bvarray_dup_x( &ctxcsn, si->si_ctxcsn, op->o_tmpmemctx );
+ sids = op->o_tmpalloc( numcsns * sizeof(int), op->o_tmpmemctx );
+ for ( i=0; i<numcsns; i++ )
+ sids[i] = si->si_sids[i];
+ } else {
+ ctxcsn = NULL;
+ sids = NULL;
+ }
+ dirty = si->si_dirty;
+ ldap_pvt_thread_rdwr_runlock( &si->si_csn_rwlock );
+
+ /* If we have a cookie, handle the PRESENT lookups */
+ if ( srs->sr_state.ctxcsn ) {
+ sessionlog *sl;
+ int i, j;
+
+ /* If we don't have any CSN of our own yet, bail out.
+ */
+ if ( !numcsns ) {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "consumer has state info but provider doesn't!";
+ goto bailout;
+ }
+
+ if ( !si->si_nopres )
+ do_present = SS_PRESENT;
+
+ /* If there are SIDs we don't recognize in the cookie, drop them */
+ for (i=0; i<srs->sr_state.numcsns; ) {
+ for (j=i; j<numcsns; j++) {
+ if ( srs->sr_state.sids[i] <= sids[j] ) {
+ break;
+ }
+ }
+ /* not found */
+ if ( j == numcsns || srs->sr_state.sids[i] != sids[j] ) {
+ char *tmp = srs->sr_state.ctxcsn[i].bv_val;
+ srs->sr_state.numcsns--;
+ for ( j=i; j<srs->sr_state.numcsns; j++ ) {
+ srs->sr_state.ctxcsn[j] = srs->sr_state.ctxcsn[j+1];
+ srs->sr_state.sids[j] = srs->sr_state.sids[j+1];
+ }
+ srs->sr_state.ctxcsn[j].bv_val = tmp;
+ srs->sr_state.ctxcsn[j].bv_len = 0;
+ continue;
+ }
+ i++;
+ }
+
+ if (srs->sr_state.numcsns != numcsns) {
+ /* consumer doesn't have the right number of CSNs */
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_op_search: "
+ "consumer cookie is missing a csn we track\n",
+ op->o_log_prefix );
+
+ changed = SS_CHANGED;
+ if ( srs->sr_state.ctxcsn ) {
+ ber_bvarray_free_x( srs->sr_state.ctxcsn, op->o_tmpmemctx );
+ srs->sr_state.ctxcsn = NULL;
+ }
+ if ( srs->sr_state.sids ) {
+ slap_sl_free( srs->sr_state.sids, op->o_tmpmemctx );
+ srs->sr_state.sids = NULL;
+ }
+ srs->sr_state.numcsns = 0;
+ goto shortcut;
+ }
+
+ /* Find the smallest CSN which differs from contextCSN */
+ mincsn.bv_len = 0;
+ maxcsn.bv_len = 0;
+ for ( i=0,j=0; i<srs->sr_state.numcsns; i++ ) {
+ int newer;
+ while ( srs->sr_state.sids[i] != sids[j] ) j++;
+ if ( BER_BVISEMPTY( &maxcsn ) || ber_bvcmp( &maxcsn,
+ &srs->sr_state.ctxcsn[i] ) < 0 ) {
+ maxcsn = srs->sr_state.ctxcsn[i];
+ maxsid = sids[j];
+ }
+ newer = ber_bvcmp( &srs->sr_state.ctxcsn[i], &ctxcsn[j] );
+ /* If our state is newer, tell consumer about changes */
+ if ( newer < 0) {
+ changed = SS_CHANGED;
+ if ( BER_BVISEMPTY( &mincsn ) || ber_bvcmp( &mincsn,
+ &srs->sr_state.ctxcsn[i] ) > 0 ) {
+ mincsn = srs->sr_state.ctxcsn[i];
+ minsid = sids[j];
+ }
+ } else if ( newer > 0 && sids[j] == slap_serverID ) {
+ /* our state is older, complain to consumer */
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ rs->sr_text = "consumer state is newer than provider!";
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_op_search: "
+ "consumer %d state %s is newer than provider %d state %s\n",
+ op->o_log_prefix, sids[i], srs->sr_state.ctxcsn[i].bv_val,
+ sids[j], /* == slap_serverID */
+ ctxcsn[j].bv_val);
+bailout:
+ if ( sop ) {
+ syncops **sp = &si->si_ops;
+
+ ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
+ while ( *sp != sop )
+ sp = &(*sp)->s_next;
+ *sp = sop->s_next;
+ ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
+ ch_free( sop->s_base.bv_val );
+ ch_free( sop );
+ }
+ rs->sr_ctrls = NULL;
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+ }
+ if ( BER_BVISEMPTY( &mincsn )) {
+ mincsn = maxcsn;
+ minsid = maxsid;
+ }
+
+ /* If nothing has changed, shortcut it */
+ if ( !changed && !dirty ) {
+ do_present = 0;
+no_change: if ( !(op->o_sync_mode & SLAP_SYNC_PERSIST) ) {
+ LDAPControl *ctrls[2];
+
+ ctrls[0] = NULL;
+ ctrls[1] = NULL;
+ syncprov_done_ctrl( op, rs, ctrls, 0, 0,
+ NULL, LDAP_SYNC_REFRESH_DELETES );
+ rs->sr_ctrls = ctrls;
+ rs->sr_err = LDAP_SUCCESS;
+ send_ldap_result( op, rs );
+ rs->sr_ctrls = NULL;
+ return rs->sr_err;
+ }
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_op_search: "
+ "no change, skipping log replay\n",
+ op->o_log_prefix );
+ goto shortcut;
+ }
+
+ if ( !BER_BVISNULL( &si->si_logbase ) ) {
+ do_present = 0;
+ if ( syncprov_play_accesslog( op, rs, srs, ctxcsn,
+ numcsns, sids, &mincsn, minsid ) ) {
+ do_present = SS_PRESENT;
+ }
+ } else if ( si->si_logs ) {
+ do_present = 0;
+ if ( syncprov_play_sessionlog( op, rs, srs, ctxcsn,
+ numcsns, sids, &mincsn, minsid ) ) {
+ do_present = SS_PRESENT;
+ }
+ } else if ( ad_minCSN != NULL && si->si_nopres && si->si_usehint ) {
+ /* We are instructed to trust minCSN if it exists. */
+ Entry *e;
+ Attribute *a = NULL;
+ int rc;
+
+ /*
+ * ITS#9580 FIXME: when we've figured out and split the
+ * sessionlog/deltalog tracking, use the appropriate attribute
+ */
+ rc = overlay_entry_get_ov( op, &op->o_bd->be_nsuffix[0], NULL,
+ ad_minCSN, 0, &e, on );
+ if ( rc == LDAP_SUCCESS && e != NULL ) {
+ a = attr_find( e->e_attrs, ad_minCSN );
+ }
+
+ if ( a != NULL ) {
+ int *minsids;
+
+ minsids = slap_parse_csn_sids( a->a_vals, a->a_numvals, op->o_tmpmemctx );
+ slap_sort_csn_sids( a->a_vals, minsids, a->a_numvals, op->o_tmpmemctx );
+
+ for ( i=0, j=0; i < a->a_numvals; i++ ) {
+ while ( j < numcsns && minsids[i] > sids[j] ) j++;
+ if ( j < numcsns && minsids[i] == sids[j] &&
+ ber_bvcmp( &a->a_vals[i], &srs->sr_state.ctxcsn[j] ) <= 0 ) {
+ /* minCSN for this serverID is contained, keep going */
+ continue;
+ }
+ /*
+ * Log DB's minCSN claims we can only replay from a certain
+ * CSN for this serverID, but consumer's cookie hasn't met that
+ * threshold: they need to refresh
+ */
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_op_search: "
+ "consumer not within recorded mincsn for DB's mincsn=%s\n",
+ op->o_log_prefix, a->a_vals[i].bv_val );
+ rs->sr_err = LDAP_SYNC_REFRESH_REQUIRED;
+ rs->sr_text = "sync cookie is stale";
+ slap_sl_free( minsids, op->o_tmpmemctx );
+ overlay_entry_release_ov( op, e, 0, on );
+ goto bailout;
+ }
+ slap_sl_free( minsids, op->o_tmpmemctx );
+ }
+ if ( e != NULL )
+ overlay_entry_release_ov( op, e, 0, on );
+ }
+
+ /*
+ * If sessionlog wasn't useful, see if we can find at least one entry
+ * that hasn't changed based on the cookie.
+ *
+ * TODO: Using mincsn only (rather than the whole cookie) will
+ * under-approximate the set of entries that haven't changed, but we
+ * can't look up CSNs by serverid with the current indexing support.
+ *
+ * As a result, dormant serverids in the cluster become mincsns and
+ * more likely to make syncprov_findcsn(,FIND_CSN,) fail -> triggering
+ * an expensive refresh...
+ */
+ if ( !do_present ) {
+ gotstate = 1;
+ } else if ( syncprov_findcsn( op, FIND_CSN, &mincsn ) != LDAP_SUCCESS ) {
+ /* No, so a reload is required */
+ /* the 2.2 consumer doesn't send this hint */
+ if ( si->si_usehint && srs->sr_rhint == 0 ) {
+ if ( ctxcsn )
+ ber_bvarray_free_x( ctxcsn, op->o_tmpmemctx );
+ if ( sids )
+ op->o_tmpfree( sids, op->o_tmpmemctx );
+ rs->sr_err = LDAP_SYNC_REFRESH_REQUIRED;
+ rs->sr_text = "sync cookie is stale";
+ goto bailout;
+ }
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_op_search: "
+ "failed to find entry with csn=%s, ignoring cookie\n",
+ op->o_log_prefix, mincsn.bv_val );
+ if ( srs->sr_state.ctxcsn ) {
+ ber_bvarray_free_x( srs->sr_state.ctxcsn, op->o_tmpmemctx );
+ srs->sr_state.ctxcsn = NULL;
+ }
+ if ( srs->sr_state.sids ) {
+ slap_sl_free( srs->sr_state.sids, op->o_tmpmemctx );
+ srs->sr_state.sids = NULL;
+ }
+ srs->sr_state.numcsns = 0;
+ } else {
+ gotstate = 1;
+ /* If changed and doing Present lookup, send Present UUIDs */
+ if ( syncprov_findcsn( op, FIND_PRESENT, 0 ) != LDAP_SUCCESS ) {
+ if ( ctxcsn )
+ ber_bvarray_free_x( ctxcsn, op->o_tmpmemctx );
+ if ( sids )
+ op->o_tmpfree( sids, op->o_tmpmemctx );
+ goto bailout;
+ }
+ }
+ } else {
+ /* The consumer knows nothing, we know nothing. OK. */
+ if (!numcsns)
+ goto no_change;
+ /* No consumer state, assume something has changed */
+ changed = SS_CHANGED;
+ }
+
+shortcut:
+ /* Append CSN range to search filter, save original filter
+ * for persistent search evaluation
+ */
+ if ( sop ) {
+ ldap_pvt_thread_mutex_lock( &sop->s_mutex );
+ sop->s_filterstr = op->ors_filterstr;
+ /* correct the refcount that was set to 2 before */
+ sop->s_inuse--;
+ }
+
+ /* If something changed, find the changes */
+ if ( gotstate && ( changed || dirty ) ) {
+ Filter *fand, *fava;
+
+ fand = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ fand->f_choice = LDAP_FILTER_AND;
+ fand->f_next = NULL;
+ fava = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ fand->f_and = fava;
+ fava->f_choice = LDAP_FILTER_GE;
+ fava->f_ava = op->o_tmpalloc( sizeof(AttributeAssertion), op->o_tmpmemctx );
+ fava->f_ava->aa_desc = slap_schema.si_ad_entryCSN;
+#ifdef LDAP_COMP_MATCH
+ fava->f_ava->aa_cf = NULL;
+#endif
+ ber_dupbv_x( &fava->f_ava->aa_value, &mincsn, op->o_tmpmemctx );
+ fava->f_next = op->ors_filter;
+ op->ors_filter = fand;
+ filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
+ if ( sop ) {
+ sop->s_flags |= PS_FIX_FILTER;
+ }
+ }
+ if ( sop ) {
+ ldap_pvt_thread_mutex_unlock( &sop->s_mutex );
+ }
+
+ /* Let our callback add needed info to returned entries */
+ cb = op->o_tmpcalloc(1, sizeof(slap_callback)+sizeof(searchstate), op->o_tmpmemctx);
+ ss = (searchstate *)(cb+1);
+ ss->ss_on = on;
+ ss->ss_so = sop;
+ ss->ss_flags = do_present | changed;
+ ss->ss_ctxcsn = ctxcsn;
+ ss->ss_numcsns = numcsns;
+ ss->ss_sids = sids;
+ cb->sc_response = syncprov_search_response;
+ cb->sc_private = ss;
+ cb->sc_next = op->o_callback;
+ op->o_callback = cb;
+
+ /* If this is a persistent search and no changes were reported during
+ * the refresh phase, just invoke the response callback to transition
+ * us into persist phase
+ */
+ if ( !changed && !dirty ) {
+ Debug( LDAP_DEBUG_SYNC, "%s syncprov_op_search: "
+ "nothing changed, finishing up initial search early\n",
+ op->o_log_prefix );
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_nentries = 0;
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+syncprov_operational(
+ Operation *op,
+ SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
+
+ /* This prevents generating unnecessarily; frontend will strip
+ * any statically stored copy.
+ */
+ if ( op->o_sync != SLAP_CONTROL_NONE )
+ return SLAP_CB_CONTINUE;
+
+ if ( rs->sr_entry &&
+ dn_match( &rs->sr_entry->e_nname, &si->si_contextdn )) {
+
+ if ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
+ ad_inlist( slap_schema.si_ad_contextCSN, rs->sr_attrs )) {
+ Attribute *a, **ap = NULL;
+
+ for ( a=rs->sr_entry->e_attrs; a; a=a->a_next ) {
+ if ( a->a_desc == slap_schema.si_ad_contextCSN )
+ break;
+ }
+
+ ldap_pvt_thread_rdwr_rlock( &si->si_csn_rwlock );
+ if ( si->si_ctxcsn ) {
+ if ( !a ) {
+ for ( ap = &rs->sr_operational_attrs; *ap;
+ ap=&(*ap)->a_next );
+
+ a = attr_alloc( slap_schema.si_ad_contextCSN );
+ *ap = a;
+ }
+
+ if ( !ap ) {
+ if ( rs_entry2modifiable( op, rs, on )) {
+ a = attr_find( rs->sr_entry->e_attrs,
+ slap_schema.si_ad_contextCSN );
+ }
+ if ( a->a_nvals != a->a_vals ) {
+ ber_bvarray_free( a->a_nvals );
+ }
+ a->a_nvals = NULL;
+ ber_bvarray_free( a->a_vals );
+ a->a_vals = NULL;
+ a->a_numvals = 0;
+ }
+ attr_valadd( a, si->si_ctxcsn, si->si_ctxcsn, si->si_numcsns );
+ }
+ ldap_pvt_thread_rdwr_runlock( &si->si_csn_rwlock );
+ }
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+syncprov_setup_accesslog(void)
+{
+ const char *text;
+ int rc = -1;
+
+ if ( !ad_reqType ) {
+ if ( slap_str2ad( "reqType", &ad_reqType, &text ) ) {
+ Debug( LDAP_DEBUG_ANY, "syncprov_setup_accesslog: "
+ "couldn't get definition for attribute reqType, "
+ "is accessslog configured?\n" );
+ return rc;
+ }
+ }
+
+ if ( !ad_reqResult ) {
+ if ( slap_str2ad( "reqResult", &ad_reqResult, &text ) ) {
+ Debug( LDAP_DEBUG_ANY, "syncprov_setup_accesslog: "
+ "couldn't get definition for attribute reqResult, "
+ "is accessslog configured?\n" );
+ return rc;
+ }
+ }
+
+ if ( !ad_reqDN ) {
+ if ( slap_str2ad( "reqDN", &ad_reqDN, &text ) ) {
+ Debug( LDAP_DEBUG_ANY, "syncprov_setup_accesslog: "
+ "couldn't get definition for attribute reqDN, "
+ "is accessslog configured?\n" );
+ return rc;
+ }
+ }
+
+ if ( !ad_reqEntryUUID ) {
+ if ( slap_str2ad( "reqEntryUUID", &ad_reqEntryUUID, &text ) ) {
+ Debug( LDAP_DEBUG_ANY, "syncprov_setup_accesslog: "
+ "couldn't get definition for attribute reqEntryUUID, "
+ "is accessslog configured?\n" );
+ return rc;
+ }
+ }
+
+ if ( !ad_reqNewDN ) {
+ if ( slap_str2ad( "reqNewDN", &ad_reqNewDN, &text ) ) {
+ Debug( LDAP_DEBUG_ANY, "syncprov_setup_accesslog: "
+ "couldn't get definition for attribute reqNewDN, "
+ "is accessslog configured?\n" );
+ return rc;
+ }
+ }
+
+ if ( !ad_minCSN ) {
+ if ( slap_str2ad( "minCSN", &ad_minCSN, &text ) ) {
+ Debug( LDAP_DEBUG_ANY, "syncprov_setup_accesslog: "
+ "couldn't get definition for attribute minCSN, "
+ "is accessslog configured?\n" );
+ return rc;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+enum {
+ SP_CHKPT = 1,
+ SP_SESSL,
+ SP_NOPRES,
+ SP_USEHINT,
+ SP_LOGDB
+};
+
+static ConfigDriver sp_cf_gen;
+
+static ConfigTable spcfg[] = {
+ { "syncprov-checkpoint", "ops> <minutes", 3, 3, 0, ARG_MAGIC|SP_CHKPT,
+ sp_cf_gen, "( OLcfgOvAt:1.1 NAME 'olcSpCheckpoint' "
+ "DESC 'ContextCSN checkpoint interval in ops and minutes' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "syncprov-sessionlog", "ops", 2, 2, 0, ARG_INT|ARG_MAGIC|SP_SESSL,
+ sp_cf_gen, "( OLcfgOvAt:1.2 NAME 'olcSpSessionlog' "
+ "DESC 'Session log size in ops' "
+ "EQUALITY integerMatch "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "syncprov-nopresent", NULL, 2, 2, 0, ARG_ON_OFF|ARG_MAGIC|SP_NOPRES,
+ sp_cf_gen, "( OLcfgOvAt:1.3 NAME 'olcSpNoPresent' "
+ "DESC 'Omit Present phase processing' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "syncprov-reloadhint", NULL, 2, 2, 0, ARG_ON_OFF|ARG_MAGIC|SP_USEHINT,
+ sp_cf_gen, "( OLcfgOvAt:1.4 NAME 'olcSpReloadHint' "
+ "DESC 'Observe Reload Hint in Request control' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "syncprov-sessionlog-source", NULL, 2, 2, 0, ARG_DN|ARG_QUOTE|ARG_MAGIC|SP_LOGDB,
+ sp_cf_gen, "( OLcfgOvAt:1.5 NAME 'olcSpSessionlogSource' "
+ "DESC 'On startup, try loading sessionlog from this subtree' "
+ "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs spocs[] = {
+ { "( OLcfgOvOc:1.1 "
+ "NAME 'olcSyncProvConfig' "
+ "DESC 'SyncRepl Provider configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcSpCheckpoint "
+ "$ olcSpSessionlog "
+ "$ olcSpNoPresent "
+ "$ olcSpReloadHint "
+ "$ olcSpSessionlogSource "
+ ") )",
+ Cft_Overlay, spcfg },
+ { NULL, 0, NULL }
+};
+
+static int
+sp_cf_gen(ConfigArgs *c)
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
+ int rc = 0;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ switch ( c->type ) {
+ case SP_CHKPT:
+ if ( si->si_chkops || si->si_chktime ) {
+ struct berval bv;
+ /* we assume si_chktime is a multiple of 60
+ * because the parsed value was originally
+ * multiplied by 60 */
+ bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%d %d", si->si_chkops, si->si_chktime/60 );
+ if ( bv.bv_len >= sizeof( c->cr_msg ) ) {
+ rc = 1;
+ } else {
+ bv.bv_val = c->cr_msg;
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ } else {
+ rc = 1;
+ }
+ break;
+ case SP_SESSL:
+ if ( si->si_logs ) {
+ c->value_int = si->si_logs->sl_size;
+ } else {
+ rc = 1;
+ }
+ break;
+ case SP_NOPRES:
+ if ( si->si_nopres ) {
+ c->value_int = 1;
+ } else {
+ rc = 1;
+ }
+ break;
+ case SP_USEHINT:
+ if ( si->si_usehint ) {
+ c->value_int = 1;
+ } else {
+ rc = 1;
+ }
+ break;
+ case SP_LOGDB:
+ if ( BER_BVISEMPTY( &si->si_logbase ) ) {
+ rc = 1;
+ } else {
+ value_add_one( &c->rvalue_vals, &si->si_logbase );
+ value_add_one( &c->rvalue_nvals, &si->si_logbase );
+ }
+ break;
+ }
+ return rc;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ switch ( c->type ) {
+ case SP_CHKPT:
+ si->si_chkops = 0;
+ si->si_chktime = 0;
+ break;
+ case SP_SESSL:
+ if ( si->si_logs )
+ si->si_logs->sl_size = 0;
+ break;
+ case SP_NOPRES:
+ si->si_nopres = 0;
+ break;
+ case SP_USEHINT:
+ si->si_usehint = 0;
+ break;
+ case SP_LOGDB:
+ if ( !BER_BVISNULL( &si->si_logbase ) ) {
+ ch_free( si->si_logbase.bv_val );
+ BER_BVZERO( &si->si_logbase );
+ }
+ break;
+ }
+ return rc;
+ }
+ switch ( c->type ) {
+ case SP_CHKPT:
+ if ( lutil_atoi( &si->si_chkops, c->argv[1] ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s unable to parse checkpoint ops # \"%s\"",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ if ( si->si_chkops <= 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s invalid checkpoint ops # \"%d\"",
+ c->argv[0], si->si_chkops );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ if ( lutil_atoi( &si->si_chktime, c->argv[2] ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s unable to parse checkpoint time \"%s\"",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ if ( si->si_chktime <= 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s invalid checkpoint time \"%d\"",
+ c->argv[0], si->si_chkops );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ si->si_chktime *= 60;
+ break;
+ case SP_SESSL: {
+ sessionlog *sl;
+ int size = c->value_int;
+
+ if ( size < 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s size %d is negative",
+ c->argv[0], size );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ if ( size && !BER_BVISNULL( &si->si_logbase ) ) {
+ Debug( LDAP_DEBUG_ANY, "syncprov_config: while configuring "
+ "internal sessionlog, accesslog source has already been "
+ "configured, this results in wasteful operation\n" );
+ }
+ sl = si->si_logs;
+ if ( !sl ) {
+ if ( !size ) break;
+ sl = ch_calloc( 1, sizeof( sessionlog ));
+ ldap_pvt_thread_rdwr_init( &sl->sl_mutex );
+ si->si_logs = sl;
+ }
+ sl->sl_size = size;
+ }
+ break;
+ case SP_NOPRES:
+ si->si_nopres = c->value_int;
+ break;
+ case SP_USEHINT:
+ si->si_usehint = c->value_int;
+ if ( si->si_usehint ) {
+ /* Consider we might be a delta provider, but it's ok if not */
+ (void)syncprov_setup_accesslog();
+ }
+ break;
+ case SP_LOGDB:
+ if ( si->si_logs ) {
+ Debug( LDAP_DEBUG_ANY, "syncprov_config: while configuring "
+ "accesslog source, internal sessionlog has already been "
+ "configured, this results in wasteful operation\n" );
+ }
+ if ( CONFIG_ONLINE_ADD( c ) ) {
+ if ( !select_backend( &c->value_ndn, 0 ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "<%s> no matching backend found for suffix",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s \"%s\"\n",
+ c->log, c->cr_msg, c->value_dn.bv_val );
+ rc = 1;
+ break;
+ }
+ ch_free( c->value_ndn.bv_val );
+ }
+ si->si_logbase = c->value_ndn;
+ rc = syncprov_setup_accesslog();
+ ch_free( c->value_dn.bv_val );
+ break;
+ }
+ return rc;
+}
+
+/* ITS#3456 we cannot run this search on the main thread, must use a
+ * child thread in order to insure we have a big enough stack.
+ */
+static void *
+syncprov_db_otask(
+ void *ptr
+)
+{
+ syncprov_findcsn( ptr, FIND_MAXCSN, 0 );
+ return NULL;
+}
+
+static int
+syncprov_db_ocallback(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ if ( rs->sr_type == REP_SEARCH && rs->sr_err == LDAP_SUCCESS ) {
+ if ( rs->sr_entry->e_name.bv_len )
+ op->o_callback->sc_private = (void *)1;
+ }
+ return LDAP_SUCCESS;
+}
+
+/* ITS#9015 see if the DB is really empty */
+static void *
+syncprov_db_otask2(
+ void *ptr
+)
+{
+ Operation *op = ptr;
+ SlapReply rs = {REP_RESULT};
+ slap_callback cb = {0};
+ int rc;
+
+ cb.sc_response = syncprov_db_ocallback;
+
+ op->o_managedsait = SLAP_CONTROL_CRITICAL;
+ op->o_callback = &cb;
+ op->o_tag = LDAP_REQ_SEARCH;
+ op->ors_scope = LDAP_SCOPE_SUBTREE;
+ op->ors_limit = NULL;
+ op->ors_slimit = 1;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_attrs = slap_anlist_no_attrs;
+ op->ors_attrsonly = 1;
+ op->ors_deref = LDAP_DEREF_NEVER;
+ op->ors_filter = &generic_filter;
+ op->ors_filterstr = generic_filterstr;
+ rc = op->o_bd->be_search( op, &rs );
+ if ( rc == LDAP_SIZELIMIT_EXCEEDED || cb.sc_private )
+ op->ors_slimit = 2;
+ return NULL;
+}
+
+/* Read any existing contextCSN from the underlying db.
+ * Then search for any entries newer than that. If no value exists,
+ * just generate it. Cache whatever result.
+ */
+static int
+syncprov_db_open(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
+
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+ Operation *op;
+ Entry *e = NULL;
+ Attribute *a;
+ int rc;
+ void *thrctx = NULL;
+
+ if ( !SLAP_LASTMOD( be )) {
+ Debug( LDAP_DEBUG_ANY,
+ "syncprov_db_open: invalid config, lastmod must be enabled\n" );
+ return -1;
+ }
+
+ if ( slapMode & SLAP_TOOL_MODE ) {
+ return 0;
+ }
+
+ rc = overlay_register_control( be, LDAP_CONTROL_SYNC );
+ if ( rc ) {
+ return rc;
+ }
+
+ Debug( LDAP_DEBUG_SYNC, "syncprov_db_open: "
+ "starting syncprov for suffix %s\n",
+ be->be_suffix[0].bv_val );
+
+ thrctx = ldap_pvt_thread_pool_context();
+ connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+ op = &opbuf.ob_op;
+ op->o_bd = be;
+ op->o_dn = be->be_rootdn;
+ op->o_ndn = be->be_rootndn;
+
+ if ( SLAP_SYNC_SUBENTRY( be )) {
+ build_new_dn( &si->si_contextdn, be->be_nsuffix,
+ (struct berval *)&slap_ldapsync_cn_bv, NULL );
+ } else {
+ si->si_contextdn = be->be_nsuffix[0];
+ }
+ rc = overlay_entry_get_ov( op, &si->si_contextdn, NULL,
+ slap_schema.si_ad_contextCSN, 0, &e, on );
+
+ if ( e ) {
+ ldap_pvt_thread_t tid;
+
+ a = attr_find( e->e_attrs, slap_schema.si_ad_contextCSN );
+ if ( a ) {
+ ber_bvarray_dup_x( &si->si_ctxcsn, a->a_vals, NULL );
+ si->si_numcsns = a->a_numvals;
+ si->si_sids = slap_parse_csn_sids( si->si_ctxcsn, a->a_numvals, NULL );
+ slap_sort_csn_sids( si->si_ctxcsn, si->si_sids, si->si_numcsns, NULL );
+ }
+ overlay_entry_release_ov( op, e, 0, on );
+ if ( si->si_ctxcsn && !SLAP_DBCLEAN( be )) {
+ op->o_tag = LDAP_REQ_SEARCH;
+ op->o_req_dn = be->be_suffix[0];
+ op->o_req_ndn = be->be_nsuffix[0];
+ op->ors_scope = LDAP_SCOPE_SUBTREE;
+ ldap_pvt_thread_create( &tid, 0, syncprov_db_otask, op );
+ ldap_pvt_thread_join( tid, NULL );
+ }
+ }
+
+ /* Didn't find a contextCSN, should we generate one? */
+ if ( !si->si_ctxcsn ) {
+ char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE ];
+ struct berval csn;
+
+ if ( SLAP_SINGLE_SHADOW( op->o_bd ) ) {
+ /* Not in charge of this serverID, don't generate anything. */
+ goto out;
+ }
+ if ( !SLAP_SYNC_SUBENTRY( be ) && rc != LDAP_SUCCESS
+ && rc != LDAP_NO_SUCH_ATTRIBUTE ) {
+ /* If the DB is genuinely empty, don't generate one either. */
+ goto out;
+ }
+ if ( !si->si_contextdn.bv_len ) {
+ ldap_pvt_thread_t tid;
+ /* a glue entry here with no contextCSN might mean an empty DB.
+ * we need to search for children, to be sure.
+ */
+ op->o_req_dn = be->be_suffix[0];
+ op->o_req_ndn = be->be_nsuffix[0];
+ op->o_bd->bd_info = (BackendInfo *)on->on_info;
+ ldap_pvt_thread_create( &tid, 0, syncprov_db_otask2, op );
+ ldap_pvt_thread_join( tid, NULL );
+ if ( op->ors_slimit == 1 )
+ goto out;
+ }
+
+ csn.bv_val = csnbuf;
+ csn.bv_len = sizeof( csnbuf );
+ slap_get_csn( op, &csn, 0 );
+ value_add_one( &si->si_ctxcsn, &csn );
+ si->si_numcsns = 1;
+ si->si_sids = ch_malloc( sizeof(int) );
+ si->si_sids[0] = slap_serverID;
+ Debug( LDAP_DEBUG_SYNC, "syncprov_db_open: "
+ "generated a new ctxcsn=%s for suffix %s\n",
+ csn.bv_val, be->be_suffix[0].bv_val );
+
+ /* make sure we do a checkpoint on close */
+ si->si_numops++;
+ }
+
+ /* Initialize the sessionlog mincsn */
+ if ( si->si_logs && si->si_numcsns ) {
+ sessionlog *sl = si->si_logs;
+ int i;
+ ber_bvarray_dup_x( &sl->sl_mincsn, si->si_ctxcsn, NULL );
+ sl->sl_numcsns = si->si_numcsns;
+ sl->sl_sids = ch_malloc( si->si_numcsns * sizeof(int) );
+ for ( i=0; i < si->si_numcsns; i++ )
+ sl->sl_sids[i] = si->si_sids[i];
+ }
+
+ if ( !BER_BVISNULL( &si->si_logbase ) ) {
+ BackendDB *db = select_backend( &si->si_logbase, 0 );
+ if ( !db ) {
+ Debug( LDAP_DEBUG_ANY, "syncprov_db_open: "
+ "configured accesslog database dn='%s' not present\n",
+ si->si_logbase.bv_val );
+ return -1;
+ }
+ }
+
+out:
+ op->o_bd->bd_info = (BackendInfo *)on;
+ return 0;
+}
+
+/* Write the current contextCSN into the underlying db.
+ */
+static int
+syncprov_db_close(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
+#ifdef SLAP_CONFIG_DELETE
+ syncops *so, *sonext;
+#endif /* SLAP_CONFIG_DELETE */
+
+ if ( slapMode & SLAP_TOOL_MODE ) {
+ return 0;
+ }
+ if ( si->si_numops ) {
+ Connection conn = {0};
+ OperationBuffer opbuf;
+ Operation *op;
+ void *thrctx;
+
+ thrctx = ldap_pvt_thread_pool_context();
+ connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+ op = &opbuf.ob_op;
+ op->o_bd = be;
+ op->o_dn = be->be_rootdn;
+ op->o_ndn = be->be_rootndn;
+ syncprov_checkpoint( op, on );
+ }
+
+#ifdef SLAP_CONFIG_DELETE
+ if ( !slapd_shutdown ) {
+ ldap_pvt_thread_mutex_lock( &si->si_ops_mutex );
+ for ( so=si->si_ops, sonext=so; so; so=sonext ) {
+ SlapReply rs = {REP_RESULT};
+ rs.sr_err = LDAP_UNAVAILABLE;
+ ldap_pvt_thread_mutex_lock( &so->s_mutex );
+ send_ldap_result( so->s_op, &rs );
+ sonext=so->s_next;
+ if ( so->s_flags & PS_TASK_QUEUED )
+ ldap_pvt_thread_pool_retract( so->s_pool_cookie );
+ ldap_pvt_thread_mutex_unlock( &so->s_mutex );
+ if ( !syncprov_drop_psearch( so, 0 ))
+ so->s_si = NULL;
+ }
+ si->si_ops=NULL;
+ ldap_pvt_thread_mutex_unlock( &si->si_ops_mutex );
+ }
+ overlay_unregister_control( be, LDAP_CONTROL_SYNC );
+#endif /* SLAP_CONFIG_DELETE */
+
+ return 0;
+}
+
+static int
+syncprov_db_init(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ syncprov_info_t *si;
+
+ if ( SLAP_ISGLOBALOVERLAY( be ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "syncprov must be instantiated within a database.\n" );
+ return 1;
+ }
+
+ si = ch_calloc(1, sizeof(syncprov_info_t));
+ on->on_bi.bi_private = si;
+ ldap_pvt_thread_rdwr_init( &si->si_csn_rwlock );
+ ldap_pvt_thread_mutex_init( &si->si_ops_mutex );
+ ldap_pvt_thread_mutex_init( &si->si_mods_mutex );
+ ldap_pvt_thread_mutex_init( &si->si_resp_mutex );
+
+ csn_anlist[0].an_desc = slap_schema.si_ad_entryCSN;
+ csn_anlist[0].an_name = slap_schema.si_ad_entryCSN->ad_cname;
+ csn_anlist[1].an_desc = slap_schema.si_ad_entryUUID;
+ csn_anlist[1].an_name = slap_schema.si_ad_entryUUID->ad_cname;
+
+ uuid_anlist[0].an_desc = slap_schema.si_ad_entryUUID;
+ uuid_anlist[0].an_name = slap_schema.si_ad_entryUUID->ad_cname;
+
+ return 0;
+}
+
+static int
+syncprov_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ syncprov_info_t *si = (syncprov_info_t *)on->on_bi.bi_private;
+
+ if ( si ) {
+ if ( si->si_logs ) {
+ sessionlog *sl = si->si_logs;
+
+ ldap_tavl_free( sl->sl_entries, (AVL_FREE)ch_free );
+ if ( sl->sl_mincsn )
+ ber_bvarray_free( sl->sl_mincsn );
+ if ( sl->sl_sids )
+ ch_free( sl->sl_sids );
+
+ ldap_pvt_thread_rdwr_destroy(&si->si_logs->sl_mutex);
+ ch_free( si->si_logs );
+ }
+ if ( si->si_ctxcsn )
+ ber_bvarray_free( si->si_ctxcsn );
+ if ( si->si_sids )
+ ch_free( si->si_sids );
+ if ( si->si_logbase.bv_val )
+ ch_free( si->si_logbase.bv_val );
+ ldap_pvt_thread_mutex_destroy( &si->si_resp_mutex );
+ ldap_pvt_thread_mutex_destroy( &si->si_mods_mutex );
+ ldap_pvt_thread_mutex_destroy( &si->si_ops_mutex );
+ ldap_pvt_thread_rdwr_destroy( &si->si_csn_rwlock );
+ ch_free( si );
+ }
+
+ return 0;
+}
+
+static int syncprov_parseCtrl (
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ ber_tag_t tag;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_int_t mode;
+ ber_len_t len;
+ struct berval cookie = BER_BVNULL;
+ sync_control *sr;
+ int rhint = 0;
+
+ if ( op->o_sync != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "Sync control specified multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
+ rs->sr_text = "Sync control specified with pagedResults control";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "Sync control value is absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
+ rs->sr_text = "Sync control value is empty";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ /* Parse the control value
+ * syncRequestValue ::= SEQUENCE {
+ * mode ENUMERATED {
+ * -- 0 unused
+ * refreshOnly (1),
+ * -- 2 reserved
+ * refreshAndPersist (3)
+ * },
+ * cookie syncCookie OPTIONAL
+ * }
+ */
+
+ ber_init2( ber, &ctrl->ldctl_value, 0 );
+
+ if ( (tag = ber_scanf( ber, "{i" /*}*/, &mode )) == LBER_ERROR ) {
+ rs->sr_text = "Sync control : mode decoding error";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ switch( mode ) {
+ case LDAP_SYNC_REFRESH_ONLY:
+ mode = SLAP_SYNC_REFRESH;
+ break;
+ case LDAP_SYNC_REFRESH_AND_PERSIST:
+ mode = SLAP_SYNC_REFRESH_AND_PERSIST;
+ break;
+ default:
+ rs->sr_text = "Sync control : unknown update mode";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+
+ if ( tag == LDAP_TAG_SYNC_COOKIE ) {
+ if (( ber_scanf( ber, /*{*/ "m", &cookie )) == LBER_ERROR ) {
+ rs->sr_text = "Sync control : cookie decoding error";
+ return LDAP_PROTOCOL_ERROR;
+ }
+ tag = ber_peek_tag( ber, &len );
+ }
+ if ( tag == LDAP_TAG_RELOAD_HINT ) {
+ if (( ber_scanf( ber, /*{*/ "b", &rhint )) == LBER_ERROR ) {
+ rs->sr_text = "Sync control : rhint decoding error";
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+ if (( ber_scanf( ber, /*{*/ "}")) == LBER_ERROR ) {
+ rs->sr_text = "Sync control : decoding error";
+ return LDAP_PROTOCOL_ERROR;
+ }
+ sr = op->o_tmpcalloc( 1, sizeof(struct sync_control), op->o_tmpmemctx );
+ sr->sr_rhint = rhint;
+ if (!BER_BVISNULL(&cookie)) {
+ ber_dupbv_x( &sr->sr_state.octet_str, &cookie, op->o_tmpmemctx );
+ /* If parse fails, pretend no cookie was sent */
+ if ( slap_parse_sync_cookie( &sr->sr_state, op->o_tmpmemctx ) ||
+ sr->sr_state.rid == -1 ) {
+ if ( sr->sr_state.ctxcsn ) {
+ ber_bvarray_free_x( sr->sr_state.ctxcsn, op->o_tmpmemctx );
+ sr->sr_state.ctxcsn = NULL;
+ }
+ sr->sr_state.numcsns = 0;
+ }
+ }
+
+ op->o_controls[slap_cids.sc_LDAPsync] = sr;
+
+ op->o_sync = ctrl->ldctl_iscritical
+ ? SLAP_CONTROL_CRITICAL
+ : SLAP_CONTROL_NONCRITICAL;
+
+ op->o_sync_mode |= mode; /* o_sync_mode shares o_sync */
+
+ return LDAP_SUCCESS;
+}
+
+/* This overlay is set up for dynamic loading via moduleload. For static
+ * configuration, you'll need to arrange for the slap_overinst to be
+ * initialized and registered by some other function inside slapd.
+ */
+
+static slap_overinst syncprov;
+
+int
+syncprov_initialize()
+{
+ int rc;
+
+ rc = register_supported_control( LDAP_CONTROL_SYNC,
+ SLAP_CTRL_SEARCH, NULL,
+ syncprov_parseCtrl, &slap_cids.sc_LDAPsync );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "syncprov_init: Failed to register control %d\n", rc );
+ return rc;
+ }
+
+ syncprov.on_bi.bi_type = "syncprov";
+ syncprov.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ syncprov.on_bi.bi_db_init = syncprov_db_init;
+ syncprov.on_bi.bi_db_destroy = syncprov_db_destroy;
+ syncprov.on_bi.bi_db_open = syncprov_db_open;
+ syncprov.on_bi.bi_db_close = syncprov_db_close;
+
+ syncprov.on_bi.bi_op_abandon = syncprov_op_abandon;
+ syncprov.on_bi.bi_op_cancel = syncprov_op_abandon;
+
+ syncprov.on_bi.bi_op_add = syncprov_op_mod;
+ syncprov.on_bi.bi_op_compare = syncprov_op_compare;
+ syncprov.on_bi.bi_op_delete = syncprov_op_mod;
+ syncprov.on_bi.bi_op_modify = syncprov_op_mod;
+ syncprov.on_bi.bi_op_modrdn = syncprov_op_mod;
+ syncprov.on_bi.bi_op_search = syncprov_op_search;
+ syncprov.on_bi.bi_extended = syncprov_op_extended;
+ syncprov.on_bi.bi_operational = syncprov_operational;
+
+ syncprov.on_bi.bi_cf_ocs = spocs;
+
+ generic_filter.f_desc = slap_schema.si_ad_objectClass;
+
+ rc = config_register_schema( spcfg, spocs );
+ if ( rc ) return rc;
+
+ return overlay_register( &syncprov );
+}
+
+#if SLAPD_OVER_SYNCPROV == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+ return syncprov_initialize();
+}
+#endif /* SLAPD_OVER_SYNCPROV == SLAPD_MOD_DYNAMIC */
+
+#endif /* defined(SLAPD_OVER_SYNCPROV) */
diff --git a/servers/slapd/overlays/translucent.c b/servers/slapd/overlays/translucent.c
new file mode 100644
index 0000000..2d31bb0
--- /dev/null
+++ b/servers/slapd/overlays/translucent.c
@@ -0,0 +1,1497 @@
+/* translucent.c - translucent proxy module */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2005 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Symas Corp. for inclusion in
+ * OpenLDAP Software. This work was sponsored by Hewlett-Packard.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_TRANSLUCENT
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "lutil.h"
+
+#include "slap-config.h"
+
+/* config block */
+typedef struct translucent_info {
+ BackendDB db; /* captive backend */
+ AttributeName *local; /* valid attrs for local filters */
+ AttributeName *remote; /* valid attrs for remote filters */
+ int strict;
+ int no_glue;
+ int defer_db_open;
+ int bind_local;
+ int pwmod_local;
+} translucent_info;
+
+static ConfigLDAPadd translucent_ldadd;
+static ConfigCfAdd translucent_cfadd;
+
+static ConfigDriver translucent_cf_gen;
+
+enum {
+ TRANS_LOCAL = 1,
+ TRANS_REMOTE
+};
+
+static ConfigTable translucentcfg[] = {
+ { "translucent_strict", "on|off", 1, 2, 0,
+ ARG_ON_OFF|ARG_OFFSET,
+ (void *)offsetof(translucent_info, strict),
+ "( OLcfgOvAt:14.1 NAME 'olcTranslucentStrict' "
+ "DESC 'Reveal attribute deletion constraint violations' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "translucent_no_glue", "on|off", 1, 2, 0,
+ ARG_ON_OFF|ARG_OFFSET,
+ (void *)offsetof(translucent_info, no_glue),
+ "( OLcfgOvAt:14.2 NAME 'olcTranslucentNoGlue' "
+ "DESC 'Disable automatic glue records for ADD and MODRDN' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "translucent_local", "attr[,attr...]", 1, 2, 0,
+ ARG_MAGIC|TRANS_LOCAL,
+ translucent_cf_gen,
+ "( OLcfgOvAt:14.3 NAME 'olcTranslucentLocal' "
+ "DESC 'Attributes to use in local search filter' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "translucent_remote", "attr[,attr...]", 1, 2, 0,
+ ARG_MAGIC|TRANS_REMOTE,
+ translucent_cf_gen,
+ "( OLcfgOvAt:14.4 NAME 'olcTranslucentRemote' "
+ "DESC 'Attributes to use in remote search filter' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "translucent_bind_local", "on|off", 1, 2, 0,
+ ARG_ON_OFF|ARG_OFFSET,
+ (void *)offsetof(translucent_info, bind_local),
+ "( OLcfgOvAt:14.5 NAME 'olcTranslucentBindLocal' "
+ "DESC 'Enable local bind' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE)", NULL, NULL },
+ { "translucent_pwmod_local", "on|off", 1, 2, 0,
+ ARG_ON_OFF|ARG_OFFSET,
+ (void *)offsetof(translucent_info, pwmod_local),
+ "( OLcfgOvAt:14.6 NAME 'olcTranslucentPwModLocal' "
+ "DESC 'Enable local RFC 3062 Password Modify extended operation' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE)", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs translucentocs[] = {
+ { "( OLcfgOvOc:14.1 "
+ "NAME 'olcTranslucentConfig' "
+ "DESC 'Translucent configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcTranslucentStrict $ olcTranslucentNoGlue $"
+ " olcTranslucentLocal $ olcTranslucentRemote $"
+ " olcTranslucentBindLocal $ olcTranslucentPwModLocal ) )",
+ Cft_Overlay, translucentcfg, NULL, translucent_cfadd },
+ { "( OLcfgOvOc:14.2 "
+ "NAME 'olcTranslucentDatabase' "
+ "DESC 'Translucent target database configuration' "
+ /* co_table is initialized in translucent_initialize() */
+ "AUXILIARY )", Cft_Misc, NULL, translucent_ldadd },
+ { NULL, 0, NULL }
+};
+/* for translucent_init() */
+
+static int
+translucent_ldadd_cleanup( ConfigArgs *ca )
+{
+ slap_overinst *on = ca->ca_private;
+ translucent_info *ov = on->on_bi.bi_private;
+
+ ov->defer_db_open = 0;
+ return backend_startup_one( ca->be, &ca->reply );
+}
+
+static int
+translucent_ldadd( CfEntryInfo *cei, Entry *e, ConfigArgs *ca )
+{
+ slap_overinst *on;
+ translucent_info *ov;
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_ldadd\n" );
+
+ if ( cei->ce_type != Cft_Overlay || !cei->ce_bi ||
+ cei->ce_bi->bi_cf_ocs != translucentocs )
+ return LDAP_CONSTRAINT_VIOLATION;
+
+ on = (slap_overinst *)cei->ce_bi;
+ ov = on->on_bi.bi_private;
+ ca->be = &ov->db;
+ ca->ca_private = on;
+ if ( CONFIG_ONLINE_ADD( ca ))
+ config_push_cleanup( ca, translucent_ldadd_cleanup );
+ else
+ ov->defer_db_open = 0;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+translucent_cfadd( Operation *op, SlapReply *rs, Entry *e, ConfigArgs *ca )
+{
+ CfEntryInfo *cei = e->e_private;
+ slap_overinst *on = (slap_overinst *)cei->ce_bi;
+ translucent_info *ov = on->on_bi.bi_private;
+ struct berval bv;
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_cfadd\n" );
+
+ /* FIXME: should not hardcode "olcDatabase" here */
+ bv.bv_len = snprintf( ca->cr_msg, sizeof( ca->cr_msg ),
+ "olcDatabase=" SLAP_X_ORDERED_FMT "%s",
+ 0, ov->db.bd_info->bi_type );
+ if ( bv.bv_len >= sizeof( ca->cr_msg ) ) {
+ return -1;
+ }
+ bv.bv_val = ca->cr_msg;
+ ca->be = &ov->db;
+ ov->defer_db_open = 0;
+
+ /* We can only create this entry if the database is table-driven
+ */
+ if ( ov->db.bd_info->bi_cf_ocs )
+ config_build_entry( op, rs, cei, ca, &bv,
+ ov->db.bd_info->bi_cf_ocs,
+ &translucentocs[1] );
+
+ return 0;
+}
+
+static int
+translucent_cf_gen( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ translucent_info *ov = on->on_bi.bi_private;
+ AttributeName **an, *a2;
+ int i;
+
+ if ( c->type == TRANS_LOCAL )
+ an = &ov->local;
+ else
+ an = &ov->remote;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ if ( !*an )
+ return 1;
+ for ( i = 0; !BER_BVISNULL(&(*an)[i].an_name); i++ ) {
+ value_add_one( &c->rvalue_vals, &(*an)[i].an_name );
+ }
+ return ( i < 1 );
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ if ( c->valx < 0 ) {
+ anlist_free( *an, 1, NULL );
+ *an = NULL;
+ } else {
+ i = c->valx;
+ ch_free( (*an)[i].an_name.bv_val );
+ do {
+ (*an)[i] = (*an)[i+1];
+ i++;
+ } while ( !BER_BVISNULL( &(*an)[i].an_name ));
+ }
+ return 0;
+ }
+
+ /* cn=config values could be deleted later, we only want one name
+ * per value for valx to match. */
+ if ( c->op != SLAP_CONFIG_ADD && strchr( c->argv[1], ',' ) ) {
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "%s: %s: "
+ "Supplying multiple attribute names in a single value is "
+ "unsupported and will be disallowed in a future version\n",
+ c->log, c->argv[0] );
+ }
+
+ a2 = str2anlist( *an, c->argv[1], "," );
+ if ( !a2 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s unable to parse attribute %s",
+ c->argv[0], c->argv[1] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg );
+ return ARG_BAD_CONF;
+ }
+ *an = a2;
+ return 0;
+}
+
+static slap_overinst translucent;
+
+/*
+** glue_parent()
+** call syncrepl_add_glue() with the parent suffix;
+**
+*/
+
+static struct berval glue[] = { BER_BVC("top"), BER_BVC("glue"), BER_BVNULL };
+
+void glue_parent(Operation *op) {
+ Operation nop = *op;
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ struct berval ndn = BER_BVNULL;
+ Attribute *a;
+ Entry *e;
+ struct berval pdn;
+
+ dnParent( &op->o_req_ndn, &pdn );
+ ber_dupbv_x( &ndn, &pdn, op->o_tmpmemctx );
+
+ Debug(LDAP_DEBUG_TRACE, "=> glue_parent: fabricating glue for <%s>\n", ndn.bv_val );
+
+ e = entry_alloc();
+ e->e_id = NOID;
+ ber_dupbv(&e->e_name, &ndn);
+ ber_dupbv(&e->e_nname, &ndn);
+
+ a = attr_alloc( slap_schema.si_ad_objectClass );
+ a->a_numvals = 2;
+ a->a_vals = ch_malloc(sizeof(struct berval) * 3);
+ ber_dupbv(&a->a_vals[0], &glue[0]);
+ ber_dupbv(&a->a_vals[1], &glue[1]);
+ ber_dupbv(&a->a_vals[2], &glue[2]);
+ a->a_nvals = a->a_vals;
+ a->a_next = e->e_attrs;
+ e->e_attrs = a;
+
+ a = attr_alloc( slap_schema.si_ad_structuralObjectClass );
+ a->a_numvals = 1;
+ a->a_vals = ch_malloc(sizeof(struct berval) * 2);
+ ber_dupbv(&a->a_vals[0], &glue[1]);
+ ber_dupbv(&a->a_vals[1], &glue[2]);
+ a->a_nvals = a->a_vals;
+ a->a_next = e->e_attrs;
+ e->e_attrs = a;
+
+ nop.o_req_dn = ndn;
+ nop.o_req_ndn = ndn;
+ nop.ora_e = e;
+
+ nop.o_bd->bd_info = (BackendInfo *) on->on_info->oi_orig;
+ syncrepl_add_glue(&nop, e);
+ nop.o_bd->bd_info = (BackendInfo *) on;
+
+ op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
+
+ return;
+}
+
+/*
+** free_attr_chain()
+** free only the Attribute*, not the contents;
+**
+*/
+void free_attr_chain(Attribute *b) {
+ Attribute *a;
+ for(a=b; a; a=a->a_next) {
+ a->a_vals = NULL;
+ a->a_nvals = NULL;
+ }
+ attrs_free( b );
+ return;
+}
+
+/*
+** translucent_add()
+** if not bound as root, send ACCESS error;
+** if glue, glue_parent();
+** return CONTINUE;
+**
+*/
+
+static int translucent_add(Operation *op, SlapReply *rs) {
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_add: %s\n",
+ op->o_req_dn.bv_val );
+ if(!be_isroot(op)) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_INSUFFICIENT_ACCESS,
+ "user modification of overlay database not permitted");
+ op->o_bd->bd_info = (BackendInfo *) on;
+ return(rs->sr_err);
+ }
+ if(!ov->no_glue) glue_parent(op);
+ return(SLAP_CB_CONTINUE);
+}
+
+/*
+** translucent_modrdn()
+** if not bound as root, send ACCESS error;
+** if !glue, glue_parent();
+** else return CONTINUE;
+**
+*/
+
+static int translucent_modrdn(Operation *op, SlapReply *rs) {
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_modrdn: %s -> %s\n",
+ op->o_req_dn.bv_val, op->orr_newrdn.bv_val );
+ if(!be_isroot(op)) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_INSUFFICIENT_ACCESS,
+ "user modification of overlay database not permitted");
+ op->o_bd->bd_info = (BackendInfo *) on;
+ return(rs->sr_err);
+ }
+ if(!ov->no_glue) {
+ op->o_tag = LDAP_REQ_ADD;
+ glue_parent(op);
+ op->o_tag = LDAP_REQ_MODRDN;
+ }
+ return(SLAP_CB_CONTINUE);
+}
+
+/*
+** translucent_delete()
+** if not bound as root, send ACCESS error;
+** else return CONTINUE;
+**
+*/
+
+static int translucent_delete(Operation *op, SlapReply *rs) {
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_delete: %s\n",
+ op->o_req_dn.bv_val );
+ if(!be_isroot(op)) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_INSUFFICIENT_ACCESS,
+ "user modification of overlay database not permitted");
+ op->o_bd->bd_info = (BackendInfo *) on;
+ return(rs->sr_err);
+ }
+ return(SLAP_CB_CONTINUE);
+}
+
+static int
+translucent_tag_cb( Operation *op, SlapReply *rs )
+{
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->orm_modlist = op->o_callback->sc_private;
+ rs->sr_tag = slap_req2res( op->o_tag );
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+** translucent_modify()
+** modify in local backend if exists in both;
+** otherwise, add to local backend;
+** fail if not defined in captive backend;
+**
+*/
+
+static int translucent_modify(Operation *op, SlapReply *rs) {
+ SlapReply nrs = { REP_RESULT };
+
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ Entry *e = NULL, *re = NULL;
+ Attribute *a, *ax;
+ Modifications *m, **mm;
+ BackendDB *db;
+ int del, rc, erc = 0;
+ slap_callback cb = { 0 };
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_modify: %s\n",
+ op->o_req_dn.bv_val );
+
+ if(ov->defer_db_open) {
+ send_ldap_error(op, rs, LDAP_UNAVAILABLE,
+ "remote DB not available");
+ return(rs->sr_err);
+ }
+/*
+** fetch entry from the captive backend;
+** if it did not exist, fail;
+** release it, if captive backend supports this;
+**
+*/
+
+ db = op->o_bd;
+ op->o_bd = &ov->db;
+ ov->db.be_acl = op->o_bd->be_acl;
+ rc = ov->db.bd_info->bi_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &re);
+ op->o_bd = db;
+ if(rc != LDAP_SUCCESS || re == NULL ) {
+ send_ldap_error((op), rs, LDAP_NO_SUCH_OBJECT,
+ "attempt to modify nonexistent local record");
+ return(rs->sr_err);
+ }
+/*
+** fetch entry from local backend;
+** if it exists:
+** foreach Modification:
+** if attr not present in local:
+** if Mod == LDAP_MOD_DELETE:
+** if remote attr not present, return NO_SUCH;
+** if remote attr present, drop this Mod;
+** else force this Mod to LDAP_MOD_ADD;
+** return CONTINUE;
+**
+*/
+
+ op->o_bd->bd_info = (BackendInfo *) on->on_info->oi_orig;
+ rc = be_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &e);
+ op->o_bd->bd_info = (BackendInfo *) on;
+
+ if(e && rc == LDAP_SUCCESS) {
+ Debug(LDAP_DEBUG_TRACE, "=> translucent_modify: found local entry\n" );
+ for(mm = &op->orm_modlist; *mm; ) {
+ m = *mm;
+ for(a = e->e_attrs; a; a = a->a_next)
+ if(a->a_desc == m->sml_desc) break;
+ if(a) {
+ mm = &m->sml_next;
+ continue; /* found local attr */
+ }
+ if(m->sml_op == LDAP_MOD_DELETE) {
+ for(a = re->e_attrs; a; a = a->a_next)
+ if(a->a_desc == m->sml_desc) break;
+ /* not found remote attr */
+ if(!a) {
+ erc = LDAP_NO_SUCH_ATTRIBUTE;
+ goto release;
+ }
+ if(ov->strict) {
+ erc = LDAP_CONSTRAINT_VIOLATION;
+ goto release;
+ }
+ Debug(LDAP_DEBUG_TRACE,
+ "=> translucent_modify: silently dropping delete: %s\n",
+ m->sml_desc->ad_cname.bv_val );
+ *mm = m->sml_next;
+ m->sml_next = NULL;
+ slap_mods_free(m, 1);
+ continue;
+ }
+ m->sml_op = LDAP_MOD_ADD;
+ mm = &m->sml_next;
+ }
+ erc = SLAP_CB_CONTINUE;
+release:
+ if(re) {
+ if(ov->db.bd_info->bi_entry_release_rw) {
+ op->o_bd = &ov->db;
+ ov->db.bd_info->bi_entry_release_rw(op, re, 0);
+ op->o_bd = db;
+ } else
+ entry_free(re);
+ }
+ op->o_bd->bd_info = (BackendInfo *) on->on_info->oi_orig;
+ be_entry_release_r(op, e);
+ op->o_bd->bd_info = (BackendInfo *) on;
+ if(erc == SLAP_CB_CONTINUE) {
+ return(erc);
+ } else if(erc) {
+ send_ldap_error(op, rs, erc,
+ "attempt to delete nonexistent attribute");
+ return(erc);
+ }
+ }
+
+ /* don't leak remote entry copy */
+ if(re) {
+ if(ov->db.bd_info->bi_entry_release_rw) {
+ op->o_bd = &ov->db;
+ ov->db.bd_info->bi_entry_release_rw(op, re, 0);
+ op->o_bd = db;
+ } else
+ entry_free(re);
+ }
+/*
+** foreach Modification:
+** if MOD_ADD or MOD_REPLACE, add Attribute;
+** if no Modifications were suitable:
+** if strict, throw CONSTRAINT_VIOLATION;
+** else, return early SUCCESS;
+** fabricate Entry with new Attribute chain;
+** glue_parent() for this Entry;
+** call bi_op_add() in local backend;
+**
+*/
+
+ Debug(LDAP_DEBUG_TRACE, "=> translucent_modify: fabricating local add\n" );
+ a = NULL;
+ for(del = 0, ax = NULL, m = op->orm_modlist; m; m = m->sml_next) {
+ Attribute atmp;
+ if(((m->sml_op & LDAP_MOD_OP) != LDAP_MOD_ADD) &&
+ ((m->sml_op & LDAP_MOD_OP) != LDAP_MOD_REPLACE)) {
+ Debug(LDAP_DEBUG_ANY,
+ "=> translucent_modify: silently dropped modification(%d): %s\n",
+ m->sml_op, m->sml_desc->ad_cname.bv_val );
+ if((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE) del++;
+ continue;
+ }
+ atmp.a_desc = m->sml_desc;
+ atmp.a_vals = m->sml_values;
+ atmp.a_nvals = m->sml_nvalues ? m->sml_nvalues : atmp.a_vals;
+ atmp.a_numvals = m->sml_numvals;
+ atmp.a_flags = 0;
+ a = attr_dup( &atmp );
+ a->a_next = ax;
+ ax = a;
+ }
+
+ if(del && ov->strict) {
+ attrs_free( a );
+ send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
+ "attempt to delete attributes from local database");
+ return(rs->sr_err);
+ }
+
+ if(!ax) {
+ if(ov->strict) {
+ send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
+ "modification contained other than ADD or REPLACE");
+ return(rs->sr_err);
+ }
+ /* rs->sr_text = "no valid modification found"; */
+ rs->sr_err = LDAP_SUCCESS;
+ send_ldap_result(op, rs);
+ return(rs->sr_err);
+ }
+
+ e = entry_alloc();
+ ber_dupbv( &e->e_name, &op->o_req_dn );
+ ber_dupbv( &e->e_nname, &op->o_req_ndn );
+ e->e_attrs = a;
+
+ op->o_tag = LDAP_REQ_ADD;
+ cb.sc_response = translucent_tag_cb;
+ cb.sc_private = op->orm_modlist;
+ op->oq_add.rs_e = e;
+
+ glue_parent(op);
+
+ cb.sc_next = op->o_callback;
+ op->o_callback = &cb;
+ rc = on->on_info->oi_orig->bi_op_add(op, &nrs);
+ if ( op->ora_e == e )
+ entry_free( e );
+ op->o_callback = cb.sc_next;
+
+ return(rc);
+}
+
+static int translucent_compare(Operation *op, SlapReply *rs) {
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ AttributeAssertion *ava = op->orc_ava;
+ Entry *e = NULL;
+ BackendDB *db;
+ int rc;
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_compare: <%s> %s:%s\n",
+ op->o_req_dn.bv_val, ava->aa_desc->ad_cname.bv_val, ava->aa_value.bv_val);
+
+/*
+** if the local backend has an entry for this attribute:
+** CONTINUE and let it do the compare;
+**
+*/
+ rc = overlay_entry_get_ov(op, &op->o_req_ndn, NULL, ava->aa_desc, 0, &e, on);
+ if(rc == LDAP_SUCCESS && e) {
+ overlay_entry_release_ov(op, e, 0, on);
+ return(SLAP_CB_CONTINUE);
+ }
+
+ if(ov->defer_db_open) {
+ send_ldap_error(op, rs, LDAP_UNAVAILABLE,
+ "remote DB not available");
+ return(rs->sr_err);
+ }
+/*
+** call compare() in the captive backend;
+** return the result;
+**
+*/
+ db = op->o_bd;
+ op->o_bd = &ov->db;
+ ov->db.be_acl = op->o_bd->be_acl;
+ rc = ov->db.bd_info->bi_op_compare(op, rs);
+ op->o_bd = db;
+
+ return(rc);
+}
+
+static int translucent_pwmod(Operation *op, SlapReply *rs) {
+ SlapReply nrs = { REP_RESULT };
+ Operation nop;
+
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ Entry *e = NULL, *re = NULL;
+ BackendDB *db;
+ int rc = 0;
+ slap_callback cb = { 0 };
+
+ if (!ov->pwmod_local) {
+ rs->sr_err = LDAP_CONSTRAINT_VIOLATION,
+ rs->sr_text = "attempt to modify password in local database";
+ return rs->sr_err;
+ }
+
+/*
+** fetch entry from the captive backend;
+** if it did not exist, fail;
+** release it, if captive backend supports this;
+**
+*/
+ db = op->o_bd;
+ op->o_bd = &ov->db;
+ ov->db.be_acl = op->o_bd->be_acl;
+ rc = ov->db.bd_info->bi_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &re);
+ if(rc != LDAP_SUCCESS || re == NULL ) {
+ send_ldap_error((op), rs, LDAP_NO_SUCH_OBJECT,
+ "attempt to modify nonexistent local record");
+ return(rs->sr_err);
+ }
+ op->o_bd = db;
+/*
+** fetch entry from local backend;
+** if it exists:
+** return CONTINUE;
+*/
+
+ op->o_bd->bd_info = (BackendInfo *) on->on_info->oi_orig;
+ rc = be_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &e);
+ op->o_bd->bd_info = (BackendInfo *) on;
+
+ if(e && rc == LDAP_SUCCESS) {
+ if(re) {
+ if(ov->db.bd_info->bi_entry_release_rw) {
+ op->o_bd = &ov->db;
+ ov->db.bd_info->bi_entry_release_rw(op, re, 0);
+ op->o_bd = db;
+ } else {
+ entry_free(re);
+ }
+ }
+ op->o_bd->bd_info = (BackendInfo *) on->on_info->oi_orig;
+ be_entry_release_r(op, e);
+ op->o_bd->bd_info = (BackendInfo *) on;
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* don't leak remote entry copy */
+ if(re) {
+ if(ov->db.bd_info->bi_entry_release_rw) {
+ op->o_bd = &ov->db;
+ ov->db.bd_info->bi_entry_release_rw(op, re, 0);
+ op->o_bd = db;
+ } else {
+ entry_free(re);
+ }
+ }
+/*
+** glue_parent() for this Entry;
+** call bi_op_add() in local backend;
+**
+*/
+ e = entry_alloc();
+ ber_dupbv( &e->e_name, &op->o_req_dn );
+ ber_dupbv( &e->e_nname, &op->o_req_ndn );
+ e->e_attrs = NULL;
+
+ nop = *op;
+ nop.o_tag = LDAP_REQ_ADD;
+ cb.sc_response = slap_null_cb;
+ nop.oq_add.rs_e = e;
+
+ glue_parent(&nop);
+
+ nop.o_callback = &cb;
+ rc = on->on_info->oi_orig->bi_op_add(&nop, &nrs);
+ if ( nop.ora_e == e ) {
+ entry_free( e );
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ return rc;
+}
+
+static int translucent_exop(Operation *op, SlapReply *rs) {
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ const struct berval bv_exop_pwmod = BER_BVC(LDAP_EXOP_MODIFY_PASSWD);
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_exop: %s\n",
+ op->o_req_dn.bv_val );
+
+ if(ov->defer_db_open) {
+ send_ldap_error(op, rs, LDAP_UNAVAILABLE,
+ "remote DB not available");
+ return(rs->sr_err);
+ }
+
+ if ( bvmatch( &bv_exop_pwmod, &op->ore_reqoid ) ) {
+ return translucent_pwmod( op, rs );
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+** translucent_search_cb()
+** merge local data with remote data
+**
+** Four cases:
+** 1: remote search, no local filter
+** merge data and send immediately
+** 2: remote search, with local filter
+** merge data and save
+** 3: local search, no remote filter
+** merge data and send immediately
+** 4: local search, with remote filter
+** check list, merge, send, delete
+*/
+
+#define RMT_SIDE 0
+#define LCL_SIDE 1
+#define USE_LIST 2
+
+typedef struct trans_ctx {
+ BackendDB *db;
+ slap_overinst *on;
+ Filter *orig;
+ TAvlnode *list;
+ int step;
+ int slimit;
+ AttributeName *attrs;
+} trans_ctx;
+
+static int translucent_search_cb(Operation *op, SlapReply *rs) {
+ trans_ctx *tc;
+ BackendDB *db;
+ slap_overinst *on;
+ translucent_info *ov;
+ Entry *le, *re;
+ Attribute *a, *ax, *an, *as = NULL;
+ int rc;
+ int test_f = 0;
+
+ tc = op->o_callback->sc_private;
+
+ /* Don't let the op complete while we're gathering data */
+ if ( rs->sr_type == REP_RESULT && ( tc->step & USE_LIST ))
+ return 0;
+
+ if(rs->sr_type != REP_SEARCH || !rs->sr_entry)
+ return(SLAP_CB_CONTINUE);
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_search_cb: %s\n",
+ rs->sr_entry->e_name.bv_val );
+
+ op->ors_slimit = tc->slimit + ( tc->slimit > 0 ? 1 : 0 );
+ if ( op->ors_attrs == slap_anlist_all_attributes ) {
+ op->ors_attrs = tc->attrs;
+ rs->sr_attrs = tc->attrs;
+ rs->sr_attr_flags = slap_attr_flags( rs->sr_attrs );
+ }
+
+ on = tc->on;
+ ov = on->on_bi.bi_private;
+
+ db = op->o_bd;
+ re = NULL;
+
+ /* If we have local, get remote */
+ if ( tc->step & LCL_SIDE ) {
+ le = rs->sr_entry;
+ /* If entry is already on list, use it */
+ if ( tc->step & USE_LIST ) {
+ re = ldap_tavl_delete( &tc->list, le, entry_dn_cmp );
+ if ( re ) {
+ rs_flush_entry( op, rs, on );
+ rc = test_filter( op, re, tc->orig );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rs->sr_flags |= REP_ENTRY_MUSTBEFREED;
+ rs->sr_entry = re;
+
+ if ( tc->slimit >= 0 && rs->sr_nentries >= tc->slimit ) {
+ return LDAP_SIZELIMIT_EXCEEDED;
+ }
+
+ return SLAP_CB_CONTINUE;
+ } else {
+ entry_free( re );
+ rs->sr_entry = NULL;
+ return 0;
+ }
+ }
+ }
+ op->o_bd = &ov->db;
+ rc = be_entry_get_rw( op, &rs->sr_entry->e_nname, NULL, NULL, 0, &re );
+ if ( rc == LDAP_SUCCESS && re ) {
+ Entry *tmp = entry_dup( re );
+ be_entry_release_r( op, re );
+ re = tmp;
+ test_f = 1;
+ }
+ } else {
+ /* Else we have remote, get local */
+ op->o_bd = tc->db;
+ le = NULL;
+ rc = overlay_entry_get_ov(op, &rs->sr_entry->e_nname, NULL, NULL, 0, &le, on);
+ if ( rc == LDAP_SUCCESS && le ) {
+ re = entry_dup( rs->sr_entry );
+ rs_flush_entry( op, rs, on );
+ } else {
+ le = NULL;
+ }
+ }
+
+/*
+** if we got remote and local entry:
+** foreach local attr:
+** foreach remote attr:
+** if match, remote attr with local attr;
+** if new local, add to list;
+** append new local attrs to remote;
+**
+*/
+
+ if ( re && le ) {
+ for(ax = le->e_attrs; ax; ax = ax->a_next) {
+ for(a = re->e_attrs; a; a = a->a_next) {
+ if(a->a_desc == ax->a_desc) {
+ test_f = 1;
+ if(a->a_vals != a->a_nvals)
+ ber_bvarray_free(a->a_nvals);
+ ber_bvarray_free(a->a_vals);
+ ber_bvarray_dup_x( &a->a_vals, ax->a_vals, NULL );
+ if ( ax->a_vals == ax->a_nvals ) {
+ a->a_nvals = a->a_vals;
+ } else {
+ ber_bvarray_dup_x( &a->a_nvals, ax->a_nvals, NULL );
+ }
+ break;
+ }
+ }
+ if(a) continue;
+ an = attr_dup(ax);
+ an->a_next = as;
+ as = an;
+ }
+ /* Dispose of local entry */
+ if ( tc->step & LCL_SIDE ) {
+ rs_flush_entry(op, rs, on);
+ } else {
+ overlay_entry_release_ov(op, le, 0, on);
+ }
+
+ /* literally append, so locals are always last */
+ if(as) {
+ if(re->e_attrs) {
+ for(ax = re->e_attrs; ax->a_next; ax = ax->a_next);
+ ax->a_next = as;
+ } else {
+ re->e_attrs = as;
+ }
+ }
+ /* If both filters, save entry for later */
+ if ( tc->step == (USE_LIST|RMT_SIDE) ) {
+ ldap_tavl_insert( &tc->list, re, entry_dn_cmp, ldap_avl_dup_error );
+ rs->sr_entry = NULL;
+ rc = 0;
+ } else {
+ /* send it now */
+ rs->sr_entry = re;
+ rs->sr_flags |= REP_ENTRY_MUSTBEFREED;
+ if ( test_f ) {
+ rc = test_filter( op, rs->sr_entry, tc->orig );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rc = SLAP_CB_CONTINUE;
+ } else {
+ rc = 0;
+ }
+ } else {
+ rc = SLAP_CB_CONTINUE;
+ }
+ }
+ } else if ( le ) {
+ /* Only a local entry: remote was deleted
+ * Ought to delete the local too...
+ */
+ rc = 0;
+ } else if ( tc->step & USE_LIST ) {
+ /* Only a remote entry, but both filters:
+ * Test the complete filter
+ */
+ rc = test_filter( op, rs->sr_entry, tc->orig );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rc = SLAP_CB_CONTINUE;
+ } else {
+ rc = 0;
+ }
+ } else {
+ /* Only a remote entry, only remote filter:
+ * just pass thru
+ */
+ rc = SLAP_CB_CONTINUE;
+ }
+
+ op->o_bd = db;
+
+ if ( rc == SLAP_CB_CONTINUE && tc->slimit >= 0 && rs->sr_nentries >= tc->slimit ) {
+ return LDAP_SIZELIMIT_EXCEEDED;
+ }
+
+ return rc;
+}
+
+/* Dup the filter, excluding invalid elements */
+static Filter *
+trans_filter_dup(Operation *op, Filter *f, AttributeName *an)
+{
+ Filter *n = NULL;
+
+ if ( !f )
+ return NULL;
+
+ switch( f->f_choice & SLAPD_FILTER_MASK ) {
+ case SLAPD_FILTER_COMPUTED:
+ n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ n->f_choice = f->f_choice;
+ n->f_result = f->f_result;
+ n->f_next = NULL;
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ if ( ad_inlist( f->f_desc, an )) {
+ n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ n->f_choice = f->f_choice;
+ n->f_desc = f->f_desc;
+ n->f_next = NULL;
+ }
+ break;
+
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ case LDAP_FILTER_EXT:
+ if ( !f->f_av_desc || ad_inlist( f->f_av_desc, an )) {
+ AttributeAssertion *nava;
+
+ n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ n->f_choice = f->f_choice;
+
+ nava = op->o_tmpalloc( sizeof(AttributeAssertion), op->o_tmpmemctx );
+ *nava = *f->f_ava;
+ n->f_ava = nava;
+
+ ber_dupbv_x( &n->f_av_value, &f->f_av_value, op->o_tmpmemctx );
+ n->f_next = NULL;
+ }
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ if ( !f->f_av_desc || ad_inlist( f->f_av_desc, an )) {
+ SubstringsAssertion *nsub;
+
+ n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ n->f_choice = f->f_choice;
+
+ nsub = op->o_tmpalloc( sizeof(SubstringsAssertion), op->o_tmpmemctx );
+ *nsub = *f->f_sub;
+ n->f_sub = nsub;
+
+ if ( !BER_BVISNULL( &f->f_sub_initial ))
+ ber_dupbv_x( &n->f_sub_initial, &f->f_sub_initial, op->o_tmpmemctx );
+
+ ber_bvarray_dup_x( &n->f_sub_any, f->f_sub_any, op->o_tmpmemctx );
+
+ if ( !BER_BVISNULL( &f->f_sub_final ))
+ ber_dupbv_x( &n->f_sub_final, &f->f_sub_final, op->o_tmpmemctx );
+
+ n->f_next = NULL;
+ }
+ break;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT: {
+ Filter **p;
+
+ n = op->o_tmpalloc( sizeof(Filter), op->o_tmpmemctx );
+ n->f_choice = f->f_choice;
+ n->f_next = NULL;
+
+ for ( p = &n->f_list, f = f->f_list; f; f = f->f_next ) {
+ *p = trans_filter_dup( op, f, an );
+ if ( !*p )
+ continue;
+ p = &(*p)->f_next;
+ }
+ /* nothing valid in this list */
+ if ( !n->f_list ) {
+ op->o_tmpfree( n, op->o_tmpmemctx );
+ return NULL;
+ }
+ /* Only 1 element in this list */
+ if ((n->f_choice & SLAPD_FILTER_MASK) != LDAP_FILTER_NOT &&
+ !n->f_list->f_next ) {
+ f = n->f_list;
+ *n = *f;
+ op->o_tmpfree( f, op->o_tmpmemctx );
+ }
+ break;
+ }
+ }
+ return n;
+}
+
+static void
+trans_filter_free( Operation *op, Filter *f )
+{
+ Filter *n, *p, *next;
+
+ f->f_choice &= SLAPD_FILTER_MASK;
+
+ switch( f->f_choice ) {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ /* Free in reverse order */
+ n = NULL;
+ for ( p = f->f_list; p; p = next ) {
+ next = p->f_next;
+ p->f_next = n;
+ n = p;
+ }
+ for ( p = n; p; p = next ) {
+ next = p->f_next;
+ trans_filter_free( op, p );
+ }
+ break;
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ case LDAP_FILTER_SUBSTRINGS:
+ case LDAP_FILTER_EXT:
+ op->o_tmpfree( f->f_av_value.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( f->f_ava, op->o_tmpmemctx );
+ break;
+ default:
+ break;
+ }
+ op->o_tmpfree( f, op->o_tmpmemctx );
+}
+
+static int
+translucent_search_cleanup( Operation *op, SlapReply *rs )
+{
+ trans_ctx *tc = op->o_callback->sc_private;
+
+ op->ors_filter = tc->orig;
+ return LDAP_SUCCESS;
+}
+
+/*
+** translucent_search()
+** search via captive backend;
+** override results with any local data;
+**
+*/
+
+static int translucent_search(Operation *op, SlapReply *rs) {
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ slap_callback cb = { NULL, NULL, NULL, NULL };
+ trans_ctx tc;
+ Filter *fl, *fr;
+ struct berval fbv;
+ int rc = 0;
+
+ if ( op->o_managedsait > SLAP_CONTROL_IGNORED )
+ return SLAP_CB_CONTINUE;
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_search: <%s> %s\n",
+ op->o_req_dn.bv_val, op->ors_filterstr.bv_val );
+
+ if(ov->defer_db_open) {
+ send_ldap_error(op, rs, LDAP_UNAVAILABLE,
+ "remote DB not available");
+ return(rs->sr_err);
+ }
+
+ fr = ov->remote ? trans_filter_dup( op, op->ors_filter, ov->remote ) : NULL;
+ fl = ov->local ? trans_filter_dup( op, op->ors_filter, ov->local ) : NULL;
+ cb.sc_response = (slap_response *) translucent_search_cb;
+ cb.sc_cleanup = (slap_response *) translucent_search_cleanup;
+ cb.sc_private = &tc;
+
+ ov->db.be_acl = op->o_bd->be_acl;
+ tc.db = op->o_bd;
+ tc.on = on;
+ tc.orig = op->ors_filter;
+ tc.list = NULL;
+ tc.step = 0;
+ tc.slimit = op->ors_slimit;
+ tc.attrs = NULL;
+ fbv = op->ors_filterstr;
+
+ if ( fr || !fl ) {
+ Operation op2;
+ Opheader oh;
+
+ op2 = *op;
+ oh = *op->o_hdr;
+ oh.oh_conn = op->o_conn;
+ oh.oh_connid = op->o_connid;
+ op2.o_bd = &ov->db;
+ op2.o_hdr = &oh;
+ op2.o_extra = op->o_extra;
+ op2.o_callback = &cb;
+
+ tc.attrs = op->ors_attrs;
+ op->ors_slimit = SLAP_NO_LIMIT;
+ op->ors_attrs = slap_anlist_all_attributes;
+ tc.step |= RMT_SIDE;
+ if ( fl ) {
+ tc.step |= USE_LIST;
+ op->ors_filter = fr;
+ filter2bv_x( op, fr, &op2.ors_filterstr );
+ }
+ rc = ov->db.bd_info->bi_op_search( &op2, rs );
+ if ( op->ors_attrs == slap_anlist_all_attributes )
+ op->ors_attrs = tc.attrs;
+ if ( fl ) {
+ op->o_tmpfree( op2.ors_filterstr.bv_val, op2.o_tmpmemctx );
+ }
+ }
+
+ cb.sc_next = op->o_callback;
+ op->o_callback = &cb;
+
+ if ( fl && !rc ) {
+ tc.step |= LCL_SIDE;
+ op->ors_filter = fl;
+ filter2bv_x( op, fl, &op->ors_filterstr );
+ rc = overlay_op_walk( op, rs, op_search, on->on_info, on->on_next );
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ }
+ op->ors_filterstr = fbv;
+ op->o_callback = cb.sc_next;
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_attr_flags = slap_attr_flags( rs->sr_attrs );
+
+ /* Send out anything remaining on the list and finish */
+ if ( tc.step & USE_LIST ) {
+ if ( tc.list ) {
+ TAvlnode *av;
+
+ av = ldap_tavl_end( tc.list, TAVL_DIR_LEFT );
+ while ( av ) {
+ rs->sr_entry = av->avl_data;
+ if ( rc == LDAP_SUCCESS && LDAP_COMPARE_TRUE ==
+ test_filter( op, rs->sr_entry, op->ors_filter ))
+ {
+ rs->sr_flags = REP_ENTRY_MUSTBEFREED;
+ rc = send_search_entry( op, rs );
+ } else {
+ entry_free( rs->sr_entry );
+ }
+ av = ldap_tavl_next( av, TAVL_DIR_RIGHT );
+ }
+ ldap_tavl_free( tc.list, NULL );
+ rs->sr_flags = 0;
+ rs->sr_entry = NULL;
+ }
+ send_ldap_result( op, rs );
+ }
+
+ op->ors_slimit = tc.slimit;
+
+ /* Free in reverse order */
+ if ( fl )
+ trans_filter_free( op, fl );
+ if ( fr )
+ trans_filter_free( op, fr );
+
+ return rc;
+}
+
+
+/*
+** translucent_bind()
+** pass bind request to captive backend;
+**
+*/
+
+static int translucent_bind(Operation *op, SlapReply *rs) {
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ BackendDB *db;
+ slap_callback sc = { 0 }, *save_cb;
+ int rc;
+
+ Debug(LDAP_DEBUG_TRACE, "translucent_bind: <%s> method %d\n",
+ op->o_req_dn.bv_val, op->orb_method );
+
+ if(ov->defer_db_open) {
+ send_ldap_error(op, rs, LDAP_UNAVAILABLE,
+ "remote DB not available");
+ return(rs->sr_err);
+ }
+
+ if (ov->bind_local) {
+ sc.sc_response = slap_null_cb;
+ save_cb = op->o_callback;
+ op->o_callback = &sc;
+ }
+
+ db = op->o_bd;
+ op->o_bd = &ov->db;
+ ov->db.be_acl = op->o_bd->be_acl;
+ rc = ov->db.bd_info->bi_op_bind(op, rs);
+ op->o_bd = db;
+
+ if (ov->bind_local) {
+ op->o_callback = save_cb;
+ if (rc != LDAP_SUCCESS) {
+ rc = SLAP_CB_CONTINUE;
+ }
+ }
+
+ return rc;
+}
+
+/*
+** translucent_connection_destroy()
+** pass disconnect notification to captive backend;
+**
+*/
+
+static int translucent_connection_destroy(BackendDB *be, Connection *conn) {
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ int rc = 0;
+
+ Debug(LDAP_DEBUG_TRACE, "translucent_connection_destroy\n" );
+
+ rc = ov->db.bd_info->bi_connection_destroy(&ov->db, conn);
+
+ return(rc);
+}
+
+/*
+** translucent_db_config()
+** pass config directives to captive backend;
+** parse unrecognized directives ourselves;
+**
+*/
+
+static int translucent_db_config(
+ BackendDB *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv
+)
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_db_config: %s\n",
+ argc ? argv[0] : "" );
+
+ /* Something for the captive database? */
+ if ( ov->db.bd_info && ov->db.bd_info->bi_db_config )
+ return ov->db.bd_info->bi_db_config( &ov->db, fname, lineno,
+ argc, argv );
+ return SLAP_CONF_UNKNOWN;
+}
+
+/*
+** translucent_db_init()
+** initialize the captive backend;
+**
+*/
+
+static int translucent_db_init(BackendDB *be, ConfigReply *cr) {
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ translucent_info *ov;
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_db_init\n" );
+
+ ov = ch_calloc(1, sizeof(translucent_info));
+ on->on_bi.bi_private = ov;
+ ov->db = *be;
+ ov->db.be_private = NULL;
+ ov->defer_db_open = 1;
+
+ if ( !backend_db_init( "ldap", &ov->db, -1, NULL )) {
+ Debug( LDAP_DEBUG_CONFIG, "translucent: unable to open captive back-ldap\n" );
+ return 1;
+ }
+ SLAP_DBFLAGS(be) |= SLAP_DBFLAG_NO_SCHEMA_CHECK;
+ SLAP_DBFLAGS(be) |= SLAP_DBFLAG_NOLASTMOD;
+
+ return 0;
+}
+
+/*
+** translucent_db_open()
+** if the captive backend has an open() method, call it;
+**
+*/
+
+static int translucent_db_open(BackendDB *be, ConfigReply *cr) {
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ int rc;
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_db_open\n" );
+
+ /* need to inherit something from the original database... */
+ ov->db.be_def_limit = be->be_def_limit;
+ ov->db.be_limits = be->be_limits;
+ ov->db.be_acl = be->be_acl;
+ ov->db.be_dfltaccess = be->be_dfltaccess;
+
+ if ( ov->defer_db_open )
+ return 0;
+
+ rc = backend_startup_one( &ov->db, cr );
+
+ if(rc) Debug(LDAP_DEBUG_TRACE,
+ "translucent: bi_db_open() returned error %d\n", rc );
+
+ return(rc);
+}
+
+/*
+** translucent_db_close()
+** if the captive backend has a close() method, call it
+**
+*/
+
+static int
+translucent_db_close( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ int rc = 0;
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_db_close\n" );
+
+ if ( ov && ov->db.bd_info && ov->db.bd_info->bi_db_close ) {
+ rc = ov->db.bd_info->bi_db_close(&ov->db, NULL);
+ }
+
+ return(rc);
+}
+
+/*
+** translucent_db_destroy()
+** if the captive backend has a db_destroy() method, call it;
+** free any config data
+**
+*/
+
+static int
+translucent_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ slap_overinst *on = (slap_overinst *) be->bd_info;
+ translucent_info *ov = on->on_bi.bi_private;
+ int rc = 0;
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_db_destroy\n" );
+
+ if ( ov ) {
+ if ( ov->remote )
+ anlist_free( ov->remote, 1, NULL );
+ if ( ov->local )
+ anlist_free( ov->local, 1, NULL );
+ if ( ov->db.be_private != NULL ) {
+ backend_stopdown_one( &ov->db );
+ }
+
+ ldap_pvt_thread_mutex_destroy( &ov->db.be_pcsn_st.be_pcsn_mutex );
+ ch_free(ov);
+ on->on_bi.bi_private = NULL;
+ }
+
+ return(rc);
+}
+
+/*
+** translucent_initialize()
+** initialize the slap_overinst with our entry points;
+**
+*/
+
+int translucent_initialize() {
+
+ int rc;
+
+ /* olcDatabaseDummy is defined in slapd, and Windows
+ will not let us initialize a struct element with a data pointer
+ from another library, so we have to initialize this element
+ "by hand". */
+ translucentocs[1].co_table = olcDatabaseDummy;
+
+ Debug(LDAP_DEBUG_TRACE, "==> translucent_initialize\n" );
+
+ translucent.on_bi.bi_type = "translucent";
+ translucent.on_bi.bi_db_init = translucent_db_init;
+ translucent.on_bi.bi_db_config = translucent_db_config;
+ translucent.on_bi.bi_db_open = translucent_db_open;
+ translucent.on_bi.bi_db_close = translucent_db_close;
+ translucent.on_bi.bi_db_destroy = translucent_db_destroy;
+ translucent.on_bi.bi_op_bind = translucent_bind;
+ translucent.on_bi.bi_op_add = translucent_add;
+ translucent.on_bi.bi_op_modify = translucent_modify;
+ translucent.on_bi.bi_op_modrdn = translucent_modrdn;
+ translucent.on_bi.bi_op_delete = translucent_delete;
+ translucent.on_bi.bi_op_search = translucent_search;
+ translucent.on_bi.bi_op_compare = translucent_compare;
+ translucent.on_bi.bi_connection_destroy = translucent_connection_destroy;
+ translucent.on_bi.bi_extended = translucent_exop;
+
+ translucent.on_bi.bi_cf_ocs = translucentocs;
+ rc = config_register_schema ( translucentcfg, translucentocs );
+ if ( rc ) return rc;
+
+ return(overlay_register(&translucent));
+}
+
+#if SLAPD_OVER_TRANSLUCENT == SLAPD_MOD_DYNAMIC && defined(PIC)
+int init_module(int argc, char *argv[]) {
+ return translucent_initialize();
+}
+#endif
+
+#endif /* SLAPD_OVER_TRANSLUCENT */
diff --git a/servers/slapd/overlays/unique.c b/servers/slapd/overlays/unique.c
new file mode 100644
index 0000000..7a7c8fb
--- /dev/null
+++ b/servers/slapd/overlays/unique.c
@@ -0,0 +1,1548 @@
+/* unique.c - attribute uniqueness module */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2004,2006-2007 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Symas Corporation for
+ * inclusion in OpenLDAP Software, with subsequent enhancements by
+ * Emily Backes at Symas Corporation. This work was sponsored by
+ * Hewlett-Packard.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_UNIQUE
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "slap-config.h"
+
+#define UNIQUE_DEFAULT_URI ("ldap:///??sub")
+
+static slap_overinst unique;
+
+typedef struct unique_attrs_s {
+ struct unique_attrs_s *next; /* list of attrs */
+ AttributeDescription *attr;
+} unique_attrs;
+
+typedef struct unique_domain_uri_s {
+ struct unique_domain_uri_s *next;
+ struct berval dn;
+ struct berval ndn;
+ struct berval filter;
+ Filter *f;
+ struct unique_attrs_s *attrs;
+ int scope;
+} unique_domain_uri;
+
+typedef struct unique_domain_s {
+ struct unique_domain_s *next;
+ struct berval domain_spec;
+ struct unique_domain_uri_s *uri;
+ char ignore; /* polarity of attributes */
+ char strict; /* null considered unique too */
+ char serial; /* serialize execution */
+} unique_domain;
+
+typedef struct unique_data_s {
+ struct unique_domain_s *domains;
+ struct unique_domain_s *legacy;
+ char legacy_strict_set;
+ ldap_pvt_thread_mutex_t serial_mutex;
+} unique_data;
+
+typedef struct unique_counter_s {
+ struct berval *ndn;
+ int count;
+} unique_counter;
+
+enum {
+ UNIQUE_BASE = 1,
+ UNIQUE_IGNORE,
+ UNIQUE_ATTR,
+ UNIQUE_STRICT,
+ UNIQUE_URI,
+};
+
+static ConfigDriver unique_cf_base;
+static ConfigDriver unique_cf_attrs;
+static ConfigDriver unique_cf_strict;
+static ConfigDriver unique_cf_uri;
+
+static ConfigTable uniquecfg[] = {
+ { "unique_base", "basedn", 2, 2, 0, ARG_DN|ARG_QUOTE|ARG_MAGIC|UNIQUE_BASE,
+ unique_cf_base, "( OLcfgOvAt:10.1 NAME 'olcUniqueBase' "
+ "DESC 'Subtree for uniqueness searches' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
+ { "unique_ignore", "attribute...", 2, 0, 0, ARG_MAGIC|UNIQUE_IGNORE,
+ unique_cf_attrs, "( OLcfgOvAt:10.2 NAME 'olcUniqueIgnore' "
+ "DESC 'Attributes for which uniqueness shall not be enforced' "
+ "EQUALITY caseIgnoreMatch "
+ "ORDERING caseIgnoreOrderingMatch "
+ "SUBSTR caseIgnoreSubstringsMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "unique_attributes", "attribute...", 2, 0, 0, ARG_MAGIC|UNIQUE_ATTR,
+ unique_cf_attrs, "( OLcfgOvAt:10.3 NAME 'olcUniqueAttribute' "
+ "DESC 'Attributes for which uniqueness shall be enforced' "
+ "EQUALITY caseIgnoreMatch "
+ "ORDERING caseIgnoreOrderingMatch "
+ "SUBSTR caseIgnoreSubstringsMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "unique_strict", "on|off", 1, 2, 0, ARG_MAGIC|UNIQUE_STRICT,
+ unique_cf_strict, "( OLcfgOvAt:10.4 NAME 'olcUniqueStrict' "
+ "DESC 'Enforce uniqueness of null values' "
+ "EQUALITY booleanMatch "
+ "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
+ { "unique_uri", "ldapuri", 2, 3, 0, ARG_MAGIC|UNIQUE_URI,
+ unique_cf_uri, "( OLcfgOvAt:10.5 NAME 'olcUniqueURI' "
+ "DESC 'List of keywords and LDAP URIs for a uniqueness domain' "
+ "EQUALITY caseExactMatch "
+ "ORDERING caseExactOrderingMatch "
+ "SUBSTR caseExactSubstringsMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs uniqueocs[] = {
+ { "( OLcfgOvOc:10.1 "
+ "NAME 'olcUniqueConfig' "
+ "DESC 'Attribute value uniqueness configuration' "
+ "SUP olcOverlayConfig "
+ "MAY ( olcUniqueBase $ olcUniqueIgnore $ "
+ "olcUniqueAttribute $ olcUniqueStrict $ "
+ "olcUniqueURI ) )",
+ Cft_Overlay, uniquecfg },
+ { NULL, 0, NULL }
+};
+
+static void
+unique_free_domain_uri ( unique_domain_uri *uri )
+{
+ unique_domain_uri *next_uri = NULL;
+ unique_attrs *attr, *next_attr = NULL;
+
+ while ( uri ) {
+ next_uri = uri->next;
+ ch_free ( uri->dn.bv_val );
+ ch_free ( uri->ndn.bv_val );
+ ch_free ( uri->filter.bv_val );
+ filter_free( uri->f );
+ attr = uri->attrs;
+ while ( attr ) {
+ next_attr = attr->next;
+ ch_free (attr);
+ attr = next_attr;
+ }
+ ch_free ( uri );
+ uri = next_uri;
+ }
+}
+
+/* free an entire stack of domains */
+static void
+unique_free_domain ( unique_domain *domain )
+{
+ unique_domain *next_domain = NULL;
+
+ while ( domain ) {
+ next_domain = domain->next;
+ ch_free ( domain->domain_spec.bv_val );
+ unique_free_domain_uri ( domain->uri );
+ ch_free ( domain );
+ domain = next_domain;
+ }
+}
+
+static int
+unique_new_domain_uri ( unique_domain_uri **urip,
+ const LDAPURLDesc *url_desc,
+ ConfigArgs *c )
+{
+ int i, rc = LDAP_SUCCESS;
+ unique_domain_uri *uri;
+ struct berval bv = {0, NULL};
+ BackendDB *be = (BackendDB *)c->be;
+ char ** attr_str;
+ AttributeDescription * ad;
+ const char * text;
+
+ uri = ch_calloc ( 1, sizeof ( unique_domain_uri ) );
+
+ if ( url_desc->lud_host && url_desc->lud_host[0] ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "host <%s> not allowed in URI",
+ url_desc->lud_host );
+ rc = ARG_BAD_CONF;
+ goto exit;
+ }
+
+ if ( url_desc->lud_dn && url_desc->lud_dn[0] ) {
+ ber_str2bv( url_desc->lud_dn, 0, 0, &bv );
+ rc = dnPrettyNormal( NULL,
+ &bv,
+ &uri->dn,
+ &uri->ndn,
+ NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "<%s> invalid DN %d (%s)",
+ url_desc->lud_dn, rc, ldap_err2string( rc ));
+ rc = ARG_BAD_CONF;
+ goto exit;
+ }
+
+ if ( be->be_nsuffix == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "suffix must be set" );
+ Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
+ c->cr_msg );
+ rc = ARG_BAD_CONF;
+ goto exit;
+ }
+
+ if ( !dnIsSuffix ( &uri->ndn, &be->be_nsuffix[0] ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "dn <%s> is not a suffix of backend base dn <%s>",
+ uri->dn.bv_val,
+ be->be_nsuffix[0].bv_val );
+ rc = ARG_BAD_CONF;
+ goto exit;
+ }
+
+ if ( BER_BVISNULL( &be->be_rootndn ) || BER_BVISEMPTY( &be->be_rootndn ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "slapo-unique needs a rootdn; "
+ "backend <%s> has none, YMMV.\n",
+ be->be_nsuffix[0].bv_val );
+ }
+ }
+
+ attr_str = url_desc->lud_attrs;
+ if ( attr_str ) {
+ for ( i=0; attr_str[i]; ++i ) {
+ unique_attrs * attr;
+ ad = NULL;
+ if ( slap_str2ad ( attr_str[i], &ad, &text )
+ == LDAP_SUCCESS) {
+ attr = ch_calloc ( 1,
+ sizeof ( unique_attrs ) );
+ attr->attr = ad;
+ attr->next = uri->attrs;
+ uri->attrs = attr;
+ } else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unique: attribute: %s: %s",
+ attr_str[i], text );
+ rc = ARG_BAD_CONF;
+ goto exit;
+ }
+ }
+ }
+
+ uri->scope = url_desc->lud_scope;
+ if ( !uri->scope ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unique: uri with base scope will always be unique");
+ rc = ARG_BAD_CONF;
+ goto exit;
+ }
+
+ if (url_desc->lud_filter) {
+ char *ptr;
+ uri->f = str2filter( url_desc->lud_filter );
+ if ( !uri->f ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unique: bad filter");
+ rc = ARG_BAD_CONF;
+ goto exit;
+ }
+ /* make sure the strfilter is in normal form (ITS#5581) */
+ filter2bv( uri->f, &uri->filter );
+ ptr = strstr( uri->filter.bv_val, "(?=" /*)*/ );
+ if ( ptr != NULL && ptr <= ( uri->filter.bv_val - STRLENOF( "(?=" /*)*/ ) + uri->filter.bv_len ) )
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unique: bad filter");
+ rc = ARG_BAD_CONF;
+ goto exit;
+ }
+ }
+exit:
+ uri->next = *urip;
+ *urip = uri;
+ if ( rc ) {
+ Debug ( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg );
+ unique_free_domain_uri ( uri );
+ *urip = NULL;
+ }
+ return rc;
+}
+
+static int
+unique_new_domain_uri_basic ( unique_domain_uri **urip,
+ ConfigArgs *c )
+{
+ LDAPURLDesc *url_desc = NULL;
+ int rc;
+
+ rc = ldap_url_parse ( UNIQUE_DEFAULT_URI, &url_desc );
+ if ( rc ) return rc;
+ rc = unique_new_domain_uri ( urip, url_desc, c );
+ ldap_free_urldesc ( url_desc );
+ return rc;
+}
+
+/* if *domain is non-null, it's pushed down the stack.
+ * note that the entire stack is freed if there is an error,
+ * so build added domains in a separate stack before adding them
+ *
+ * domain_specs look like
+ *
+ * [strict ][ignore ][serialize ]uri[[ uri]...]
+ * e.g. "ldap:///ou=foo,o=bar?uid?sub ldap:///ou=baz,o=bar?uid?sub"
+ * "strict ldap:///ou=accounts,o=bar?uid,uidNumber?one"
+ * etc
+ *
+ * so finally strictness is per-domain
+ * but so is ignore-state, and that would be better as a per-url thing
+ */
+static int
+unique_new_domain ( unique_domain **domainp,
+ char *domain_spec,
+ ConfigArgs *c )
+{
+ char *uri_start;
+ int rc = LDAP_SUCCESS;
+ int uri_err = 0;
+ unique_domain * domain;
+ LDAPURLDesc *url_desc, *url_descs = NULL;
+
+ Debug(LDAP_DEBUG_TRACE, "==> unique_new_domain <%s>\n",
+ domain_spec );
+
+ domain = ch_calloc ( 1, sizeof (unique_domain) );
+ ber_str2bv( domain_spec, 0, 1, &domain->domain_spec );
+
+ uri_start = domain_spec;
+ if ( strncasecmp ( uri_start, "ignore ",
+ STRLENOF( "ignore " ) ) == 0 ) {
+ domain->ignore = 1;
+ uri_start += STRLENOF( "ignore " );
+ }
+ if ( strncasecmp ( uri_start, "serialize ",
+ STRLENOF( "serialize " ) ) == 0 ) {
+ domain->serial = 1;
+ uri_start += STRLENOF( "serialize " );
+ }
+ if ( strncasecmp ( uri_start, "strict ",
+ STRLENOF( "strict " ) ) == 0 ) {
+ domain->strict = 1;
+ uri_start += STRLENOF( "strict " );
+ if ( !domain->ignore
+ && strncasecmp ( uri_start, "ignore ",
+ STRLENOF( "ignore " ) ) == 0 ) {
+ domain->ignore = 1;
+ uri_start += STRLENOF( "ignore " );
+ }
+ }
+ rc = ldap_url_parselist_ext ( &url_descs, uri_start, " ", 0 );
+ if ( rc ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "<%s> invalid ldap urilist",
+ uri_start );
+ rc = ARG_BAD_CONF;
+ goto exit;
+ }
+
+ for ( url_desc = url_descs;
+ url_desc;
+ url_desc = url_desc->lud_next ) {
+ rc = unique_new_domain_uri ( &domain->uri,
+ url_desc,
+ c );
+ if ( rc ) {
+ rc = ARG_BAD_CONF;
+ uri_err = 1;
+ goto exit;
+ }
+ }
+
+exit:
+ if ( url_descs ) ldap_free_urldesc ( url_descs );
+ domain->next = *domainp;
+ *domainp = domain;
+ if ( rc ) {
+ Debug ( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg );
+ unique_free_domain ( domain );
+ *domainp = NULL;
+ }
+ return rc;
+}
+
+static int
+unique_cf_base( ConfigArgs *c )
+{
+ BackendDB *be = (BackendDB *)c->be;
+ slap_overinst *on = (slap_overinst *)c->bi;
+ unique_data *private = (unique_data *) on->on_bi.bi_private;
+ unique_domain *domains = private->domains;
+ unique_domain *legacy = private->legacy;
+ int rc = ARG_BAD_CONF;
+
+ switch ( c->op ) {
+ case SLAP_CONFIG_EMIT:
+ rc = 0;
+ if ( legacy && legacy->uri && legacy->uri->dn.bv_val ) {
+ rc = value_add_one ( &c->rvalue_vals,
+ &legacy->uri->dn );
+ if ( rc ) return rc;
+ rc = value_add_one ( &c->rvalue_nvals,
+ &legacy->uri->ndn );
+ if ( rc ) return rc;
+ }
+ break;
+ case LDAP_MOD_DELETE:
+ assert ( legacy && legacy->uri && legacy->uri->dn.bv_val );
+ rc = 0;
+ ch_free ( legacy->uri->dn.bv_val );
+ ch_free ( legacy->uri->ndn.bv_val );
+ BER_BVZERO( &legacy->uri->dn );
+ BER_BVZERO( &legacy->uri->ndn );
+ if ( !legacy->uri->attrs ) {
+ unique_free_domain_uri ( legacy->uri );
+ legacy->uri = NULL;
+ }
+ if ( !legacy->uri && !private->legacy_strict_set ) {
+ unique_free_domain ( legacy );
+ private->legacy = legacy = NULL;
+ }
+ break;
+ case LDAP_MOD_ADD:
+ case SLAP_CONFIG_ADD:
+ if ( domains ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "cannot set legacy attrs when URIs are present" );
+ Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
+ c->cr_msg );
+ rc = ARG_BAD_CONF;
+ break;
+ }
+ if ( be->be_nsuffix == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "suffix must be set" );
+ Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
+ c->cr_msg );
+ rc = ARG_BAD_CONF;
+ break;
+ }
+ if ( !dnIsSuffix ( &c->value_ndn,
+ &be->be_nsuffix[0] ) ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "dn is not a suffix of backend base" );
+ Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
+ c->cr_msg );
+ rc = ARG_BAD_CONF;
+ break;
+ }
+ if ( !legacy ) {
+ unique_new_domain ( &private->legacy,
+ UNIQUE_DEFAULT_URI,
+ c );
+ legacy = private->legacy;
+ }
+ if ( !legacy->uri )
+ unique_new_domain_uri_basic ( &legacy->uri, c );
+ ch_free ( legacy->uri->dn.bv_val );
+ ch_free ( legacy->uri->ndn.bv_val );
+ legacy->uri->dn = c->value_dn;
+ legacy->uri->ndn = c->value_ndn;
+ rc = 0;
+ break;
+ default:
+ abort();
+ }
+
+ if ( rc ) {
+ ch_free( c->value_dn.bv_val );
+ BER_BVZERO( &c->value_dn );
+ ch_free( c->value_ndn.bv_val );
+ BER_BVZERO( &c->value_ndn );
+ }
+
+ return rc;
+}
+
+static int
+unique_cf_attrs( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ unique_data *private = (unique_data *) on->on_bi.bi_private;
+ unique_domain *domains = private->domains;
+ unique_domain *legacy = private->legacy;
+ unique_attrs *new_attrs = NULL;
+ unique_attrs *attr, *next_attr, *reverse_attrs;
+ unique_attrs **attrp;
+ int rc = ARG_BAD_CONF;
+ int i;
+
+ switch ( c->op ) {
+ case SLAP_CONFIG_EMIT:
+ if ( legacy
+ && (c->type == UNIQUE_IGNORE) == legacy->ignore
+ && legacy->uri )
+ for ( attr = legacy->uri->attrs;
+ attr;
+ attr = attr->next )
+ value_add_one( &c->rvalue_vals,
+ &attr->attr->ad_cname );
+ rc = 0;
+ break;
+ case LDAP_MOD_DELETE:
+ if ( legacy
+ && (c->type == UNIQUE_IGNORE) == legacy->ignore
+ && legacy->uri
+ && legacy->uri->attrs) {
+ if ( c->valx < 0 ) { /* delete all */
+ for ( attr = legacy->uri->attrs;
+ attr;
+ attr = next_attr ) {
+ next_attr = attr->next;
+ ch_free ( attr );
+ }
+ legacy->uri->attrs = NULL;
+ } else { /* delete by index */
+ attrp = &legacy->uri->attrs;
+ for ( i=0; i < c->valx; ++i )
+ attrp = &(*attrp)->next;
+ attr = *attrp;
+ *attrp = attr->next;
+ ch_free (attr);
+ }
+ if ( !legacy->uri->attrs
+ && !legacy->uri->dn.bv_val ) {
+ unique_free_domain_uri ( legacy->uri );
+ legacy->uri = NULL;
+ }
+ if ( !legacy->uri && !private->legacy_strict_set ) {
+ unique_free_domain ( legacy );
+ private->legacy = legacy = NULL;
+ }
+ }
+ rc = 0;
+ break;
+ case LDAP_MOD_ADD:
+ if ( c->argc > 2 ) {
+ Debug ( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "unique config: "
+ "Supplying multiple names in a single %s value is unsupported "
+ "and will be disallowed in a future version\n",
+ c->argv[0] );
+ }
+ /* FALLTHRU */
+ case SLAP_CONFIG_ADD:
+ if ( domains ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "cannot set legacy attrs when URIs are present" );
+ Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
+ c->cr_msg );
+ rc = ARG_BAD_CONF;
+ break;
+ }
+ if ( legacy
+ && legacy->uri
+ && legacy->uri->attrs
+ && (c->type == UNIQUE_IGNORE) != legacy->ignore ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "cannot set both attrs and ignore-attrs" );
+ Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
+ c->cr_msg );
+ rc = ARG_BAD_CONF;
+ break;
+ }
+ if ( !legacy ) {
+ unique_new_domain ( &private->legacy,
+ UNIQUE_DEFAULT_URI,
+ c );
+ legacy = private->legacy;
+ }
+ if ( !legacy->uri )
+ unique_new_domain_uri_basic ( &legacy->uri, c );
+ rc = 0;
+ for ( i=1; c->argv[i]; ++i ) {
+ AttributeDescription * ad = NULL;
+ const char * text;
+ if ( slap_str2ad ( c->argv[i], &ad, &text )
+ == LDAP_SUCCESS) {
+
+ attr = ch_calloc ( 1,
+ sizeof ( unique_attrs ) );
+ attr->attr = ad;
+ attr->next = new_attrs;
+ new_attrs = attr;
+ } else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "unique: attribute: %s: %s",
+ c->argv[i], text );
+ for ( attr = new_attrs;
+ attr;
+ attr=next_attr ) {
+ next_attr = attr->next;
+ ch_free ( attr );
+ }
+ rc = ARG_BAD_CONF;
+ break;
+ }
+ }
+ if ( rc ) break;
+
+ /* (nconc legacy->uri->attrs (nreverse new_attrs)) */
+ reverse_attrs = NULL;
+ for ( attr = new_attrs;
+ attr;
+ attr = next_attr ) {
+ next_attr = attr->next;
+ attr->next = reverse_attrs;
+ reverse_attrs = attr;
+ }
+ for ( attrp = &legacy->uri->attrs;
+ *attrp;
+ attrp = &(*attrp)->next ) ;
+ *attrp = reverse_attrs;
+
+ legacy->ignore = ( c->type == UNIQUE_IGNORE );
+ break;
+ default:
+ abort();
+ }
+
+ if ( rc ) {
+ Debug ( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s: %s\n", c->log, c->cr_msg );
+ }
+ return rc;
+}
+
+static int
+unique_cf_strict( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ unique_data *private = (unique_data *) on->on_bi.bi_private;
+ unique_domain *domains = private->domains;
+ unique_domain *legacy = private->legacy;
+ int rc = ARG_BAD_CONF;
+
+ switch ( c->op ) {
+ case SLAP_CONFIG_EMIT:
+ /* We process the boolean manually instead of using
+ * ARG_ON_OFF so that we can three-state it;
+ * olcUniqueStrict is either TRUE, FALSE, or missing,
+ * and missing is necessary to add olcUniqueURIs...
+ */
+ if ( private->legacy_strict_set ) {
+ struct berval bv = legacy->strict ? slap_true_bv : slap_false_bv;
+ value_add_one ( &c->rvalue_vals, &bv );
+ }
+ rc = 0;
+ break;
+ case LDAP_MOD_DELETE:
+ if ( legacy ) {
+ legacy->strict = 0;
+ if ( ! legacy->uri ) {
+ unique_free_domain ( legacy );
+ private->legacy = NULL;
+ }
+ }
+ private->legacy_strict_set = 0;
+ rc = 0;
+ break;
+ case LDAP_MOD_ADD:
+ case SLAP_CONFIG_ADD:
+ if ( domains ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "cannot set legacy attrs when URIs are present" );
+ Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
+ c->cr_msg );
+ rc = ARG_BAD_CONF;
+ break;
+ }
+ if ( ! legacy ) {
+ unique_new_domain ( &private->legacy,
+ UNIQUE_DEFAULT_URI,
+ c );
+ legacy = private->legacy;
+ }
+ /* ... not using ARG_ON_OFF makes this necessary too */
+ assert ( c->argc == 2 );
+ legacy->strict = (strcasecmp ( c->argv[1], "TRUE" ) == 0);
+ private->legacy_strict_set = 1;
+ rc = 0;
+ break;
+ default:
+ abort();
+ }
+
+ return rc;
+}
+
+static int
+unique_cf_uri( ConfigArgs *c )
+{
+ slap_overinst *on = (slap_overinst *)c->bi;
+ unique_data *private = (unique_data *) on->on_bi.bi_private;
+ unique_domain *domains = private->domains;
+ unique_domain *legacy = private->legacy;
+ unique_domain *domain = NULL, **domainp = NULL;
+ int rc = ARG_BAD_CONF;
+ int i;
+
+ switch ( c->op ) {
+ case SLAP_CONFIG_EMIT:
+ for ( domain = domains;
+ domain;
+ domain = domain->next ) {
+ rc = value_add_one ( &c->rvalue_vals,
+ &domain->domain_spec );
+ if ( rc ) break;
+ }
+ break;
+ case LDAP_MOD_DELETE:
+ if ( c->valx < 0 ) { /* delete them all! */
+ unique_free_domain ( domains );
+ private->domains = NULL;
+ } else { /* delete just one */
+ domainp = &private->domains;
+ for ( i=0; i < c->valx && *domainp; ++i )
+ domainp = &(*domainp)->next;
+
+ /* If *domainp is null, we walked off the end
+ * of the list. This happens when back-config
+ * and the overlay are out-of-sync, like when
+ * rejecting changes before ITS#4752 gets
+ * fixed.
+ *
+ * This should never happen, but will appear
+ * if you backport this version of
+ * slapo-unique without the config-undo fixes
+ *
+ * test024 Will hit this case in such a
+ * situation.
+ */
+ assert (*domainp != NULL);
+
+ domain = *domainp;
+ *domainp = domain->next;
+ domain->next = NULL;
+ unique_free_domain ( domain );
+ }
+ rc = 0;
+ break;
+
+ case SLAP_CONFIG_ADD: /* fallthru */
+ case LDAP_MOD_ADD:
+ if ( legacy ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "cannot set Uri when legacy attrs are present" );
+ Debug ( LDAP_DEBUG_CONFIG, "unique config: %s\n",
+ c->cr_msg );
+ rc = ARG_BAD_CONF;
+ break;
+ }
+ rc = 0;
+ if ( c->line ) rc = unique_new_domain ( &domain, c->line, c );
+ else rc = unique_new_domain ( &domain, c->argv[1], c );
+ if ( rc ) break;
+ assert ( domain->next == NULL );
+ for ( domainp = &private->domains;
+ *domainp;
+ domainp = &(*domainp)->next ) ;
+ *domainp = domain;
+
+ break;
+
+ default:
+ abort ();
+ }
+
+ return rc;
+}
+
+/*
+** allocate new unique_data;
+** initialize, copy basedn;
+** store in on_bi.bi_private;
+**
+*/
+
+static int
+unique_db_init(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ unique_data *private;
+
+ Debug(LDAP_DEBUG_TRACE, "==> unique_db_init\n" );
+
+ private = ch_calloc ( 1, sizeof ( unique_data ) );
+ ldap_pvt_thread_mutex_init( &private->serial_mutex );
+ on->on_bi.bi_private = private;
+
+ return 0;
+}
+
+static int
+unique_db_destroy(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ unique_data *private = on->on_bi.bi_private;
+
+ Debug(LDAP_DEBUG_TRACE, "==> unique_db_destroy\n" );
+
+ if ( private ) {
+ unique_domain *domains = private->domains;
+ unique_domain *legacy = private->legacy;
+
+ unique_free_domain ( domains );
+ unique_free_domain ( legacy );
+ ldap_pvt_thread_mutex_destroy( &private->serial_mutex );
+ ch_free ( private );
+ on->on_bi.bi_private = NULL;
+ }
+
+ return 0;
+}
+
+
+/*
+** search callback
+** if this is a REP_SEARCH, count++;
+**
+*/
+
+static int count_attr_cb(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ unique_counter *uc;
+
+ /* because you never know */
+ if(!op || !rs) return(0);
+
+ /* Only search entries are interesting */
+ if(rs->sr_type != REP_SEARCH) return(0);
+
+ uc = op->o_callback->sc_private;
+
+ /* Ignore the current entry */
+ if ( dn_match( uc->ndn, &rs->sr_entry->e_nname )) return(0);
+
+ Debug(LDAP_DEBUG_TRACE, "==> count_attr_cb <%s>\n",
+ rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN" );
+
+ uc->count++;
+
+ return(0);
+}
+
+/* count the length of one attribute ad
+ * (and all of its values b)
+ * in the proposed filter
+ */
+static int
+count_filter_len(
+ unique_domain *domain,
+ unique_domain_uri *uri,
+ AttributeDescription *ad,
+ BerVarray b
+)
+{
+ unique_attrs *attr;
+ int i;
+ int ks = 0;
+
+ while ( !is_at_operational( ad->ad_type ) ) {
+ if ( uri->attrs ) {
+ for ( attr = uri->attrs; attr; attr = attr->next ) {
+ if ( ad == attr->attr ) {
+ break;
+ }
+ }
+ if ( ( domain->ignore && attr )
+ || (!domain->ignore && !attr )) {
+ break;
+ }
+ }
+ if ( b && b[0].bv_val ) {
+ for (i = 0; b[i].bv_val; i++ ) {
+ /* note: make room for filter escaping... */
+ ks += ( 3 * b[i].bv_len ) + ad->ad_cname.bv_len + STRLENOF( "(=)" );
+ }
+ } else if ( domain->strict ) {
+ ks += ad->ad_cname.bv_len + STRLENOF( "(=*)" ); /* (attr=*) */
+ }
+ break;
+ }
+
+ return ks;
+}
+
+static char *
+build_filter(
+ unique_domain *domain,
+ unique_domain_uri *uri,
+ AttributeDescription *ad,
+ BerVarray b,
+ char *kp,
+ int ks,
+ void *ctx
+)
+{
+ unique_attrs *attr;
+ int i;
+
+ while ( !is_at_operational( ad->ad_type ) ) {
+ if ( uri->attrs ) {
+ for ( attr = uri->attrs; attr; attr = attr->next ) {
+ if ( ad == attr->attr ) {
+ break;
+ }
+ }
+ if ( ( domain->ignore && attr )
+ || (!domain->ignore && !attr )) {
+ break;
+ }
+ }
+ if ( b && b[0].bv_val ) {
+ for ( i = 0; b[i].bv_val; i++ ) {
+ struct berval bv;
+ int len;
+
+ ldap_bv2escaped_filter_value_x( &b[i], &bv, 1, ctx );
+ if (!b[i].bv_len)
+ bv.bv_val = b[i].bv_val;
+ len = snprintf( kp, ks, "(%s=%s)", ad->ad_cname.bv_val, bv.bv_val );
+ assert( len >= 0 && len < ks );
+ kp += len;
+ if ( bv.bv_val != b[i].bv_val ) {
+ ber_memfree_x( bv.bv_val, ctx );
+ }
+ }
+ } else if ( domain->strict ) {
+ int len;
+ len = snprintf( kp, ks, "(%s=*)", ad->ad_cname.bv_val );
+ assert( len >= 0 && len < ks );
+ kp += len;
+ }
+ break;
+ }
+ return kp;
+}
+
+static int
+unique_search(
+ Operation *op,
+ Operation *nop,
+ struct berval * dn,
+ int scope,
+ SlapReply *rs,
+ struct berval *key
+)
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ SlapReply nrs = { REP_RESULT };
+ slap_callback cb = { NULL, NULL, NULL, NULL }; /* XXX */
+ unique_counter uq = { NULL, 0 };
+ int rc;
+ char *errmsg;
+ int errmsgsize;
+
+ Debug(LDAP_DEBUG_TRACE, "==> unique_search %s\n", key->bv_val );
+
+ nop->ors_filter = str2filter_x(nop, key->bv_val);
+ if(nop->ors_filter == NULL) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_OTHER,
+ "unique_search invalid filter");
+ return(rs->sr_err);
+ }
+
+ nop->ors_filterstr = *key;
+
+ cb.sc_response = (slap_response*)count_attr_cb;
+ cb.sc_private = &uq;
+ nop->o_callback = &cb;
+ nop->o_tag = LDAP_REQ_SEARCH;
+ nop->ors_scope = scope;
+ nop->ors_deref = LDAP_DEREF_NEVER;
+ nop->ors_limit = NULL;
+ nop->ors_slimit = SLAP_NO_LIMIT;
+ nop->ors_tlimit = SLAP_NO_LIMIT;
+ nop->ors_attrs = slap_anlist_no_attrs;
+ nop->ors_attrsonly = 1;
+
+ uq.ndn = &op->o_req_ndn;
+
+ nop->o_req_ndn = *dn;
+ nop->o_ndn = op->o_bd->be_rootndn;
+
+ nop->o_bd = on->on_info->oi_origdb;
+ rc = nop->o_bd->be_search(nop, &nrs);
+ filter_free_x(nop, nop->ors_filter, 1);
+
+ if(rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, rc, "unique_search failed");
+ rc = rs->sr_err;
+ } else if(uq.count) {
+ Debug(LDAP_DEBUG_TRACE, "=> unique_search found %d records\n", uq.count );
+
+ errmsgsize = sizeof("non-unique attributes found with ") + key->bv_len;
+ errmsg = op->o_tmpalloc(errmsgsize, op->o_tmpmemctx);
+ snprintf( errmsg, errmsgsize, "non-unique attributes found with %s", key->bv_val );
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, errmsg);
+ op->o_tmpfree(errmsg, op->o_tmpmemctx);
+ rc = rs->sr_err;
+ } else {
+ Debug(LDAP_DEBUG_TRACE, "=> unique_search found no records\n" );
+ rc = SLAP_CB_CONTINUE;
+ }
+
+ op->o_tmpfree( key->bv_val, op->o_tmpmemctx );
+
+ return(rc);
+}
+
+static int
+unique_unlock(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ slap_callback *sc = op->o_callback;
+ unique_data *private = sc->sc_private;
+
+ ldap_pvt_thread_mutex_unlock( &private->serial_mutex );
+ op->o_callback = sc->sc_next;
+ op->o_tmpfree( sc, op->o_tmpmemctx );
+ return 0;
+}
+
+static int
+unique_add(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ unique_data *private = (unique_data *) on->on_bi.bi_private;
+ unique_domain *domains = private->domains;
+ unique_domain *legacy = private->legacy;
+ unique_domain *domain;
+ Operation nop = *op;
+ Attribute *a;
+ char *key, *kp;
+ struct berval bvkey;
+ int rc = SLAP_CB_CONTINUE;
+ int locked = 0;
+
+ Debug(LDAP_DEBUG_TRACE, "==> unique_add <%s>\n",
+ op->o_req_dn.bv_val );
+
+ if ( SLAPD_SYNC_IS_SYNCCONN( op->o_connid ) || (
+ get_relax(op) > SLAP_CONTROL_IGNORED
+ && access_allowed( op, op->ora_e,
+ slap_schema.si_ad_entry, NULL,
+ ACL_MANAGE, NULL ) ) ) {
+ return rc;
+ }
+
+ for ( domain = legacy ? legacy : domains;
+ domain;
+ domain = domain->next )
+ {
+ unique_domain_uri *uri;
+
+ for ( uri = domain->uri;
+ uri;
+ uri = uri->next )
+ {
+ int len;
+ int ks = 0;
+
+ if ( uri->ndn.bv_val
+ && !dnIsSuffix( &op->o_req_ndn, &uri->ndn ))
+ continue;
+
+ if ( uri->f ) {
+ if ( test_filter( NULL, op->ora_e, uri->f )
+ == LDAP_COMPARE_FALSE )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "==> unique_add_skip<%s>\n",
+ op->o_req_dn.bv_val );
+ continue;
+ }
+ }
+
+ if(!(a = op->ora_e->e_attrs)) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
+ "unique_add() got null op.ora_e.e_attrs");
+ rc = rs->sr_err;
+ break;
+
+ } else {
+ for(; a; a = a->a_next) {
+ ks += count_filter_len ( domain,
+ uri,
+ a->a_desc,
+ a->a_vals);
+ }
+ }
+
+ /* skip this domain-uri if it isn't involved */
+ if ( !ks ) continue;
+
+ if ( domain->serial && !locked ) {
+ ldap_pvt_thread_mutex_lock( &private->serial_mutex );
+ locked = 1;
+ }
+
+ /* terminating NUL */
+ ks += sizeof("(|)");
+
+ if ( uri->filter.bv_val && uri->filter.bv_len )
+ ks += uri->filter.bv_len + STRLENOF ("(&)");
+ kp = key = op->o_tmpalloc(ks, op->o_tmpmemctx);
+
+ if ( uri->filter.bv_val && uri->filter.bv_len ) {
+ len = snprintf (kp, ks, "(&%s", uri->filter.bv_val);
+ assert( len >= 0 && len < ks );
+ kp += len;
+ }
+ len = snprintf(kp, ks - (kp - key), "(|");
+ assert( len >= 0 && len < ks - (kp - key) );
+ kp += len;
+
+ for(a = op->ora_e->e_attrs; a; a = a->a_next)
+ kp = build_filter(domain,
+ uri,
+ a->a_desc,
+ a->a_vals,
+ kp,
+ ks - ( kp - key ),
+ op->o_tmpmemctx);
+
+ len = snprintf(kp, ks - (kp - key), ")");
+ assert( len >= 0 && len < ks - (kp - key) );
+ kp += len;
+ if ( uri->filter.bv_val && uri->filter.bv_len ) {
+ len = snprintf(kp, ks - (kp - key), ")");
+ assert( len >= 0 && len < ks - (kp - key) );
+ kp += len;
+ }
+ bvkey.bv_val = key;
+ bvkey.bv_len = kp - key;
+
+ rc = unique_search ( op,
+ &nop,
+ uri->ndn.bv_val ?
+ &uri->ndn :
+ &op->o_bd->be_nsuffix[0],
+ uri->scope,
+ rs,
+ &bvkey);
+
+ if ( rc != SLAP_CB_CONTINUE ) break;
+ }
+ if ( rc != SLAP_CB_CONTINUE ) break;
+ }
+
+ if ( locked ) {
+ if ( rc != SLAP_CB_CONTINUE ) {
+ ldap_pvt_thread_mutex_unlock( &private->serial_mutex );
+ } else {
+ slap_callback *cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
+ cb->sc_cleanup = unique_unlock;
+ cb->sc_private = private;
+ cb->sc_next = op->o_callback;
+ op->o_callback = cb;
+ }
+ }
+ return rc;
+}
+
+
+static int
+unique_modify(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ unique_data *private = (unique_data *) on->on_bi.bi_private;
+ unique_domain *domains = private->domains;
+ unique_domain *legacy = private->legacy;
+ unique_domain *domain;
+ Operation nop = *op;
+ Modifications *m;
+ Entry *e = NULL;
+ char *key, *kp;
+ struct berval bvkey;
+ int rc = SLAP_CB_CONTINUE;
+ int locked = 0;
+
+ Debug(LDAP_DEBUG_TRACE, "==> unique_modify <%s>\n",
+ op->o_req_dn.bv_val );
+
+ if ( !op->orm_modlist ) {
+ Debug(LDAP_DEBUG_TRACE, "unique_modify: got empty modify op\n" );
+ return rc;
+ }
+
+ if ( SLAPD_SYNC_IS_SYNCCONN( op->o_connid ) ) {
+ return rc;
+ }
+ if ( get_relax(op) > SLAP_CONTROL_IGNORED
+ && overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) == LDAP_SUCCESS
+ && e
+ && access_allowed( op, e,
+ slap_schema.si_ad_entry, NULL,
+ ACL_MANAGE, NULL ) ) {
+ overlay_entry_release_ov( op, e, 0, on );
+ return rc;
+ }
+ if ( e ) {
+ overlay_entry_release_ov( op, e, 0, on );
+ }
+
+ for ( domain = legacy ? legacy : domains;
+ domain;
+ domain = domain->next )
+ {
+ unique_domain_uri *uri;
+
+ for ( uri = domain->uri;
+ uri;
+ uri = uri->next )
+ {
+ int len;
+ int ks = 0;
+
+ if ( uri->ndn.bv_val
+ && !dnIsSuffix( &op->o_req_ndn, &uri->ndn ))
+ continue;
+
+ for ( m = op->orm_modlist; m; m = m->sml_next)
+ if ( (m->sml_op & LDAP_MOD_OP)
+ != LDAP_MOD_DELETE )
+ ks += count_filter_len
+ ( domain,
+ uri,
+ m->sml_desc,
+ m->sml_values);
+
+ /* skip this domain-uri if it isn't involved */
+ if ( !ks ) continue;
+
+ if ( domain->serial && !locked ) {
+ ldap_pvt_thread_mutex_lock( &private->serial_mutex );
+ locked = 1;
+ }
+
+ /* terminating NUL */
+ ks += sizeof("(|)");
+
+ if ( uri->filter.bv_val && uri->filter.bv_len )
+ ks += uri->filter.bv_len + STRLENOF ("(&)");
+ kp = key = op->o_tmpalloc(ks, op->o_tmpmemctx);
+
+ if ( uri->filter.bv_val && uri->filter.bv_len ) {
+ len = snprintf(kp, ks, "(&%s", uri->filter.bv_val);
+ assert( len >= 0 && len < ks );
+ kp += len;
+ }
+ len = snprintf(kp, ks - (kp - key), "(|");
+ assert( len >= 0 && len < ks - (kp - key) );
+ kp += len;
+
+ for(m = op->orm_modlist; m; m = m->sml_next)
+ if ( (m->sml_op & LDAP_MOD_OP)
+ != LDAP_MOD_DELETE )
+ kp = build_filter ( domain,
+ uri,
+ m->sml_desc,
+ m->sml_values,
+ kp,
+ ks - (kp - key),
+ op->o_tmpmemctx );
+
+ len = snprintf(kp, ks - (kp - key), ")");
+ assert( len >= 0 && len < ks - (kp - key) );
+ kp += len;
+ if ( uri->filter.bv_val && uri->filter.bv_len ) {
+ len = snprintf (kp, ks - (kp - key), ")");
+ assert( len >= 0 && len < ks - (kp - key) );
+ kp += len;
+ }
+ bvkey.bv_val = key;
+ bvkey.bv_len = kp - key;
+
+ rc = unique_search ( op,
+ &nop,
+ uri->ndn.bv_val ?
+ &uri->ndn :
+ &op->o_bd->be_nsuffix[0],
+ uri->scope,
+ rs,
+ &bvkey);
+
+ if ( rc != SLAP_CB_CONTINUE ) break;
+ }
+ if ( rc != SLAP_CB_CONTINUE ) break;
+ }
+
+ if ( locked ) {
+ if ( rc != SLAP_CB_CONTINUE ) {
+ ldap_pvt_thread_mutex_unlock( &private->serial_mutex );
+ } else {
+ slap_callback *cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
+ cb->sc_cleanup = unique_unlock;
+ cb->sc_private = private;
+ cb->sc_next = op->o_callback;
+ op->o_callback = cb;
+ }
+ }
+ return rc;
+}
+
+
+static int
+unique_modrdn(
+ Operation *op,
+ SlapReply *rs
+)
+{
+ slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
+ unique_data *private = (unique_data *) on->on_bi.bi_private;
+ unique_domain *domains = private->domains;
+ unique_domain *legacy = private->legacy;
+ unique_domain *domain;
+ Operation nop = *op;
+ Entry *e = NULL;
+ char *key, *kp;
+ struct berval bvkey;
+ LDAPRDN newrdn;
+ struct berval bv[2];
+ int rc = SLAP_CB_CONTINUE;
+ int locked = 0;
+
+ Debug(LDAP_DEBUG_TRACE, "==> unique_modrdn <%s> <%s>\n",
+ op->o_req_dn.bv_val, op->orr_newrdn.bv_val );
+
+ if ( SLAPD_SYNC_IS_SYNCCONN( op->o_connid ) ) {
+ return rc;
+ }
+ if ( get_relax(op) > SLAP_CONTROL_IGNORED
+ && overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) == LDAP_SUCCESS
+ && e
+ && access_allowed( op, e,
+ slap_schema.si_ad_entry, NULL,
+ ACL_MANAGE, NULL ) ) {
+ overlay_entry_release_ov( op, e, 0, on );
+ return rc;
+ }
+ if ( e ) {
+ overlay_entry_release_ov( op, e, 0, on );
+ }
+
+ for ( domain = legacy ? legacy : domains;
+ domain;
+ domain = domain->next )
+ {
+ unique_domain_uri *uri;
+
+ for ( uri = domain->uri;
+ uri;
+ uri = uri->next )
+ {
+ int i, len;
+ int ks = 0;
+
+ if ( uri->ndn.bv_val
+ && !dnIsSuffix( &op->o_req_ndn, &uri->ndn )
+ && (!op->orr_nnewSup
+ || !dnIsSuffix( op->orr_nnewSup, &uri->ndn )))
+ continue;
+
+ if ( ldap_bv2rdn_x ( &op->oq_modrdn.rs_newrdn,
+ &newrdn,
+ (char **)&rs->sr_text,
+ LDAP_DN_FORMAT_LDAP,
+ op->o_tmpmemctx ) ) {
+ op->o_bd->bd_info = (BackendInfo *) on->on_info;
+ send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
+ "unknown type(s) used in RDN");
+ rc = rs->sr_err;
+ break;
+ }
+
+ rc = SLAP_CB_CONTINUE;
+ for ( i=0; newrdn[i]; i++) {
+ AttributeDescription *ad = NULL;
+ if ( slap_bv2ad( &newrdn[i]->la_attr, &ad, &rs->sr_text )) {
+ ldap_rdnfree_x( newrdn, op->o_tmpmemctx );
+ rs->sr_err = LDAP_INVALID_SYNTAX;
+ send_ldap_result( op, rs );
+ rc = rs->sr_err;
+ break;
+ }
+ newrdn[i]->la_private = ad;
+ }
+ if ( rc != SLAP_CB_CONTINUE ) break;
+
+ bv[1].bv_val = NULL;
+ bv[1].bv_len = 0;
+
+ for ( i=0; newrdn[i]; i++ ) {
+ bv[0] = newrdn[i]->la_value;
+ ks += count_filter_len ( domain,
+ uri,
+ newrdn[i]->la_private,
+ bv);
+ }
+
+ /* skip this domain if it isn't involved */
+ if ( !ks ) continue;
+
+ if ( domain->serial && !locked ) {
+ ldap_pvt_thread_mutex_lock( &private->serial_mutex );
+ locked = 1;
+ }
+
+ /* terminating NUL */
+ ks += sizeof("(|)");
+
+ if ( uri->filter.bv_val && uri->filter.bv_len )
+ ks += uri->filter.bv_len + STRLENOF ("(&)");
+ kp = key = op->o_tmpalloc(ks, op->o_tmpmemctx);
+
+ if ( uri->filter.bv_val && uri->filter.bv_len ) {
+ len = snprintf(kp, ks, "(&%s", uri->filter.bv_val);
+ assert( len >= 0 && len < ks );
+ kp += len;
+ }
+ len = snprintf(kp, ks - (kp - key), "(|");
+ assert( len >= 0 && len < ks - (kp - key) );
+ kp += len;
+
+ for ( i=0; newrdn[i]; i++) {
+ bv[0] = newrdn[i]->la_value;
+ kp = build_filter ( domain,
+ uri,
+ newrdn[i]->la_private,
+ bv,
+ kp,
+ ks - (kp - key ),
+ op->o_tmpmemctx);
+ }
+
+ len = snprintf(kp, ks - (kp - key), ")");
+ assert( len >= 0 && len < ks - (kp - key) );
+ kp += len;
+ if ( uri->filter.bv_val && uri->filter.bv_len ) {
+ len = snprintf (kp, ks - (kp - key), ")");
+ assert( len >= 0 && len < ks - (kp - key) );
+ kp += len;
+ }
+ bvkey.bv_val = key;
+ bvkey.bv_len = kp - key;
+
+ rc = unique_search ( op,
+ &nop,
+ uri->ndn.bv_val ?
+ &uri->ndn :
+ &op->o_bd->be_nsuffix[0],
+ uri->scope,
+ rs,
+ &bvkey);
+
+ if ( rc != SLAP_CB_CONTINUE ) break;
+ }
+ if ( rc != SLAP_CB_CONTINUE ) break;
+ }
+
+ if ( locked ) {
+ if ( rc != SLAP_CB_CONTINUE ) {
+ ldap_pvt_thread_mutex_unlock( &private->serial_mutex );
+ } else {
+ slap_callback *cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
+ cb->sc_cleanup = unique_unlock;
+ cb->sc_private = private;
+ cb->sc_next = op->o_callback;
+ op->o_callback = cb;
+ }
+ }
+ return rc;
+}
+
+/*
+** init_module is last so the symbols resolve "for free" --
+** it expects to be called automagically during dynamic module initialization
+*/
+
+int
+unique_initialize()
+{
+ int rc;
+
+ /* statically declared just after the #includes at top */
+ memset (&unique, 0, sizeof(unique));
+
+ unique.on_bi.bi_type = "unique";
+ unique.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ unique.on_bi.bi_db_init = unique_db_init;
+ unique.on_bi.bi_db_destroy = unique_db_destroy;
+ unique.on_bi.bi_op_add = unique_add;
+ unique.on_bi.bi_op_modify = unique_modify;
+ unique.on_bi.bi_op_modrdn = unique_modrdn;
+
+ unique.on_bi.bi_cf_ocs = uniqueocs;
+ rc = config_register_schema( uniquecfg, uniqueocs );
+ if ( rc ) return rc;
+
+ return(overlay_register(&unique));
+}
+
+#if SLAPD_OVER_UNIQUE == SLAPD_MOD_DYNAMIC && defined(PIC)
+int init_module(int argc, char *argv[]) {
+ return unique_initialize();
+}
+#endif
+
+#endif /* SLAPD_OVER_UNIQUE */
diff --git a/servers/slapd/overlays/valsort.c b/servers/slapd/overlays/valsort.c
new file mode 100644
index 0000000..3d998e2
--- /dev/null
+++ b/servers/slapd/overlays/valsort.c
@@ -0,0 +1,585 @@
+/* valsort.c - sort attribute values */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2022 The OpenLDAP Foundation.
+ * Portions copyright 2005 Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion in
+ * OpenLDAP Software. This work was sponsored by Stanford University.
+ */
+
+/*
+ * This overlay sorts the values of multi-valued attributes when returning
+ * them in a search response.
+ */
+#include "portable.h"
+
+#ifdef SLAPD_OVER_VALSORT
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/ctype.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#define VALSORT_ASCEND 0
+#define VALSORT_DESCEND 1
+
+#define VALSORT_ALPHA 2
+#define VALSORT_NUMERIC 4
+
+#define VALSORT_WEIGHTED 8
+
+typedef struct valsort_info {
+ struct valsort_info *vi_next;
+ struct berval vi_dn;
+ AttributeDescription *vi_ad;
+ slap_mask_t vi_sort;
+} valsort_info;
+
+static int valsort_cid;
+
+static ConfigDriver valsort_cf_func;
+
+static ConfigTable valsort_cfats[] = {
+ { "valsort-attr", "attribute> <dn> <sort-type", 4, 5, 0, ARG_MAGIC,
+ valsort_cf_func, "( OLcfgOvAt:5.1 NAME 'olcValSortAttr' "
+ "DESC 'Sorting rule for attribute under given DN' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { NULL }
+};
+
+static ConfigOCs valsort_cfocs[] = {
+ { "( OLcfgOvOc:5.1 "
+ "NAME 'olcValSortConfig' "
+ "DESC 'Value Sorting configuration' "
+ "SUP olcOverlayConfig "
+ "MUST olcValSortAttr )",
+ Cft_Overlay, valsort_cfats },
+ { NULL }
+};
+
+static slap_verbmasks sorts[] = {
+ { BER_BVC("alpha-ascend"), VALSORT_ASCEND|VALSORT_ALPHA },
+ { BER_BVC("alpha-descend"), VALSORT_DESCEND|VALSORT_ALPHA },
+ { BER_BVC("numeric-ascend"), VALSORT_ASCEND|VALSORT_NUMERIC },
+ { BER_BVC("numeric-descend"), VALSORT_DESCEND|VALSORT_NUMERIC },
+ { BER_BVC("weighted"), VALSORT_WEIGHTED },
+ { BER_BVNULL, 0 }
+};
+
+static Syntax *syn_numericString;
+
+static int
+valsort_cf_func(ConfigArgs *c) {
+ slap_overinst *on = (slap_overinst *)c->bi;
+ valsort_info vitmp, *vi, **vip;
+ const char *text = NULL;
+ int i, is_numeric;
+ struct berval bv = BER_BVNULL;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ for ( vi = on->on_bi.bi_private; vi; vi = vi->vi_next ) {
+ struct berval bv2 = BER_BVNULL, bvret;
+ char *ptr;
+ int len;
+
+ len = vi->vi_ad->ad_cname.bv_len + 1 + vi->vi_dn.bv_len + 2;
+ i = vi->vi_sort;
+ if ( i & VALSORT_WEIGHTED ) {
+ enum_to_verb( sorts, VALSORT_WEIGHTED, &bv2 );
+ len += bv2.bv_len + 1;
+ i ^= VALSORT_WEIGHTED;
+ }
+ if ( i ) {
+ enum_to_verb( sorts, i, &bv );
+ len += bv.bv_len + 1;
+ }
+ bvret.bv_val = ch_malloc( len+1 );
+ bvret.bv_len = len;
+
+ ptr = lutil_strcopy( bvret.bv_val, vi->vi_ad->ad_cname.bv_val );
+ *ptr++ = ' ';
+ *ptr++ = '"';
+ ptr = lutil_strcopy( ptr, vi->vi_dn.bv_val );
+ *ptr++ = '"';
+ if ( vi->vi_sort & VALSORT_WEIGHTED ) {
+ *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, bv2.bv_val );
+ }
+ if ( i ) {
+ *ptr++ = ' ';
+ strcpy( ptr, bv.bv_val );
+ }
+ ber_bvarray_add( &c->rvalue_vals, &bvret );
+ }
+ i = ( c->rvalue_vals != NULL ) ? 0 : 1;
+ return i;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ if ( c->valx < 0 ) {
+ for ( vi = on->on_bi.bi_private; vi; vi = on->on_bi.bi_private ) {
+ on->on_bi.bi_private = vi->vi_next;
+ ch_free( vi->vi_dn.bv_val );
+ ch_free( vi );
+ }
+ } else {
+ valsort_info **prev;
+
+ for (i=0, prev = (valsort_info **)&on->on_bi.bi_private,
+ vi = *prev; vi && i<c->valx;
+ prev = &vi->vi_next, vi = vi->vi_next, i++ );
+ (*prev)->vi_next = vi->vi_next;
+ ch_free( vi->vi_dn.bv_val );
+ ch_free( vi );
+ }
+ return 0;
+ }
+ vitmp.vi_ad = NULL;
+ i = slap_str2ad( c->argv[1], &vitmp.vi_ad, &text );
+ if ( i ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg), "<%s> %s", c->argv[0], text );
+ Debug( LDAP_DEBUG_ANY, "%s: %s (%s)!\n",
+ c->log, c->cr_msg, c->argv[1] );
+ return(1);
+ }
+ if ( is_at_single_value( vitmp.vi_ad->ad_type )) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> %s is single-valued, ignoring", c->argv[0],
+ vitmp.vi_ad->ad_cname.bv_val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s (%s)!\n",
+ c->log, c->cr_msg, c->argv[1] );
+ return(0);
+ }
+ is_numeric = ( vitmp.vi_ad->ad_type->sat_syntax == syn_numericString ||
+ vitmp.vi_ad->ad_type->sat_syntax == slap_schema.si_syn_integer ) ? 1
+ : 0;
+ ber_str2bv( c->argv[2], 0, 0, &bv );
+ i = dnNormalize( 0, NULL, NULL, &bv, &vitmp.vi_dn, NULL );
+ if ( i ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unable to normalize DN", c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s (%s)!\n",
+ c->log, c->cr_msg, c->argv[2] );
+ return(1);
+ }
+ i = verb_to_mask( c->argv[3], sorts );
+ if ( BER_BVISNULL( &sorts[i].word )) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unrecognized sort type", c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s (%s)!\n",
+ c->log, c->cr_msg, c->argv[3] );
+ return(1);
+ }
+ vitmp.vi_sort = sorts[i].mask;
+ if ( sorts[i].mask == VALSORT_WEIGHTED && c->argc == 5 ) {
+ i = verb_to_mask( c->argv[4], sorts );
+ if ( BER_BVISNULL( &sorts[i].word )) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> unrecognized sort type", c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s (%s)!\n",
+ c->log, c->cr_msg, c->argv[4] );
+ return(1);
+ }
+ vitmp.vi_sort |= sorts[i].mask;
+ }
+ if (( vitmp.vi_sort & VALSORT_NUMERIC ) && !is_numeric ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "<%s> numeric sort specified for non-numeric syntax",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s (%s)!\n",
+ c->log, c->cr_msg, c->argv[1] );
+ return(1);
+ }
+
+ for ( vip = &on->on_bi.bi_private; *vip; vip = &(*vip)->vi_next )
+ /* Get to the end */ ;
+
+ vi = ch_malloc( sizeof(valsort_info) );
+ *vi = vitmp;
+ vi->vi_next = *vip;
+ *vip = vi;
+ return 0;
+}
+
+/* Use Insertion Sort algorithm on selected values */
+static void
+do_sort( Operation *op, Attribute *a, int beg, int num, slap_mask_t sort )
+{
+ int i, j, gotnvals;
+ struct berval tmp, ntmp, *vals = NULL, *nvals;
+
+ gotnvals = (a->a_vals != a->a_nvals );
+
+ nvals = a->a_nvals + beg;
+ if ( gotnvals )
+ vals = a->a_vals + beg;
+
+ if ( sort & VALSORT_NUMERIC ) {
+ long *numbers = op->o_tmpalloc( num * sizeof(long), op->o_tmpmemctx ),
+ idx;
+ for (i=0; i<num; i++)
+ numbers[i] = strtol( nvals[i].bv_val, NULL, 0 );
+
+ for (i=1; i<num; i++) {
+ idx = numbers[i];
+ ntmp = nvals[i];
+ if ( gotnvals ) tmp = vals[i];
+ j = i;
+ while ( j>0 ) {
+ int cmp = (sort & VALSORT_DESCEND) ? numbers[j-1] < idx :
+ numbers[j-1] > idx;
+ if ( !cmp ) break;
+ numbers[j] = numbers[j-1];
+ nvals[j] = nvals[j-1];
+ if ( gotnvals ) vals[j] = vals[j-1];
+ j--;
+ }
+ numbers[j] = idx;
+ nvals[j] = ntmp;
+ if ( gotnvals ) vals[j] = tmp;
+ }
+ op->o_tmpfree( numbers, op->o_tmpmemctx );
+ } else {
+ for (i=1; i<num; i++) {
+ ntmp = nvals[i];
+ if ( gotnvals ) tmp = vals[i];
+ j = i;
+ while ( j>0 ) {
+ int cmp = strcmp( nvals[j-1].bv_val, ntmp.bv_val );
+ cmp = (sort & VALSORT_DESCEND) ? (cmp < 0) : (cmp > 0);
+ if ( !cmp ) break;
+
+ nvals[j] = nvals[j-1];
+ if ( gotnvals ) vals[j] = vals[j-1];
+ j--;
+ }
+ nvals[j] = ntmp;
+ if ( gotnvals ) vals[j] = tmp;
+ }
+ }
+}
+
+static int
+valsort_response( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on;
+ valsort_info *vi;
+ Attribute *a;
+
+ /* If this is not a search response, or it is a syncrepl response,
+ * or the valsort control wants raw results, pass thru unmodified.
+ */
+ if ( rs->sr_type != REP_SEARCH ||
+ ( _SCM(op->o_sync) > SLAP_CONTROL_IGNORED ) ||
+ ( op->o_ctrlflag[valsort_cid] & SLAP_CONTROL_DATA0))
+ return SLAP_CB_CONTINUE;
+
+ on = (slap_overinst *) op->o_bd->bd_info;
+ vi = on->on_bi.bi_private;
+
+ /* And we must have something configured */
+ if ( !vi ) return SLAP_CB_CONTINUE;
+
+ /* Find a rule whose baseDN matches this entry */
+ for (; vi; vi = vi->vi_next ) {
+ int i, n;
+
+ if ( !dnIsSuffix( &rs->sr_entry->e_nname, &vi->vi_dn ))
+ continue;
+
+ /* Find attr that this rule affects */
+ a = attr_find( rs->sr_entry->e_attrs, vi->vi_ad );
+ if ( !a ) continue;
+
+ if ( rs_entry2modifiable( op, rs, on )) {
+ a = attr_find( rs->sr_entry->e_attrs, vi->vi_ad );
+ }
+
+ n = a->a_numvals;
+ if ( vi->vi_sort & VALSORT_WEIGHTED ) {
+ int j, gotnvals;
+ long *index = op->o_tmpalloc( n * sizeof(long), op->o_tmpmemctx );
+
+ gotnvals = (a->a_vals != a->a_nvals );
+
+ for (i=0; i<n; i++) {
+ char *ptr = ber_bvchr( &a->a_nvals[i], '{' );
+ char *end = NULL;
+ if ( !ptr ) {
+ Debug(LDAP_DEBUG_TRACE, "weights missing from attr %s "
+ "in entry %s\n", vi->vi_ad->ad_cname.bv_val,
+ rs->sr_entry->e_name.bv_val );
+ break;
+ }
+ index[i] = strtol( ptr+1, &end, 0 );
+ if ( *end != '}' ) {
+ Debug(LDAP_DEBUG_TRACE, "weights misformatted "
+ "in entry %s\n",
+ rs->sr_entry->e_name.bv_val );
+ break;
+ }
+ /* Strip out weights */
+ ptr = a->a_nvals[i].bv_val;
+ end++;
+ for (;*end;)
+ *ptr++ = *end++;
+ *ptr = '\0';
+ a->a_nvals[i].bv_len = ptr - a->a_nvals[i].bv_val;
+
+ if ( a->a_vals != a->a_nvals ) {
+ ptr = a->a_vals[i].bv_val;
+ end = ber_bvchr( &a->a_vals[i], '}' );
+ assert( end != NULL );
+ end++;
+ for (;*end;)
+ *ptr++ = *end++;
+ *ptr = '\0';
+ a->a_vals[i].bv_len = ptr - a->a_vals[i].bv_val;
+ }
+ }
+ /* An attr was missing weights here, ignore it */
+ if ( i<n ) {
+ op->o_tmpfree( index, op->o_tmpmemctx );
+ continue;
+ }
+ /* Insertion sort */
+ for ( i=1; i<n; i++) {
+ long idx = index[i];
+ struct berval tmp = a->a_vals[i], ntmp;
+ if ( gotnvals ) ntmp = a->a_nvals[i];
+ j = i;
+ while (( j>0 ) && (index[j-1] > idx )) {
+ index[j] = index[j-1];
+ a->a_vals[j] = a->a_vals[j-1];
+ if ( gotnvals ) a->a_nvals[j] = a->a_nvals[j-1];
+ j--;
+ }
+ index[j] = idx;
+ a->a_vals[j] = tmp;
+ if ( gotnvals ) a->a_nvals[j] = ntmp;
+ }
+ /* Check for secondary sort */
+ if ( vi->vi_sort ^ VALSORT_WEIGHTED ) {
+ for ( i=0; i<n;) {
+ for (j=i+1; j<n; j++) {
+ if (index[i] != index[j])
+ break;
+ }
+ if( j-i > 1 )
+ do_sort( op, a, i, j-i, vi->vi_sort );
+ i = j;
+ }
+ }
+ op->o_tmpfree( index, op->o_tmpmemctx );
+ } else {
+ do_sort( op, a, 0, n, vi->vi_sort );
+ }
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+valsort_add( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ valsort_info *vi = on->on_bi.bi_private;
+
+ Attribute *a;
+ int i;
+ char *ptr, *end;
+
+ /* See if any weighted sorting applies to this entry */
+ for ( ;vi;vi=vi->vi_next ) {
+ if ( !dnIsSuffix( &op->o_req_ndn, &vi->vi_dn ))
+ continue;
+ if ( !(vi->vi_sort & VALSORT_WEIGHTED ))
+ continue;
+ a = attr_find( op->ora_e->e_attrs, vi->vi_ad );
+ if ( !a )
+ continue;
+ for (i=0; !BER_BVISNULL( &a->a_vals[i] ); i++) {
+ ptr = ber_bvchr(&a->a_vals[i], '{' );
+ if ( !ptr ) {
+ Debug(LDAP_DEBUG_TRACE, "weight missing from attribute %s\n",
+ vi->vi_ad->ad_cname.bv_val );
+ send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
+ "weight missing from attribute" );
+ return rs->sr_err;
+ }
+ strtol( ptr+1, &end, 0 );
+ if ( *end != '}' ) {
+ Debug(LDAP_DEBUG_TRACE, "weight is misformatted in %s\n",
+ vi->vi_ad->ad_cname.bv_val );
+ send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
+ "weight is misformatted" );
+ return rs->sr_err;
+ }
+ }
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+valsort_modify( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ valsort_info *vi = on->on_bi.bi_private;
+
+ Modifications *ml;
+ int i;
+ char *ptr, *end;
+
+ /* See if any weighted sorting applies to this entry */
+ for ( ;vi;vi=vi->vi_next ) {
+ if ( !dnIsSuffix( &op->o_req_ndn, &vi->vi_dn ))
+ continue;
+ if ( !(vi->vi_sort & VALSORT_WEIGHTED ))
+ continue;
+ for (ml = op->orm_modlist; ml; ml=ml->sml_next ) {
+ /* Must be a Delete Attr op, so no values to consider */
+ if ( !ml->sml_values )
+ continue;
+ if ( ml->sml_desc == vi->vi_ad )
+ break;
+ }
+ if ( !ml )
+ continue;
+ for (i=0; !BER_BVISNULL( &ml->sml_values[i] ); i++) {
+ ptr = ber_bvchr(&ml->sml_values[i], '{' );
+ if ( !ptr ) {
+ Debug(LDAP_DEBUG_TRACE, "weight missing from attribute %s\n",
+ vi->vi_ad->ad_cname.bv_val );
+ send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
+ "weight missing from attribute" );
+ return rs->sr_err;
+ }
+ strtol( ptr+1, &end, 0 );
+ if ( *end != '}' ) {
+ Debug(LDAP_DEBUG_TRACE, "weight is misformatted in %s\n",
+ vi->vi_ad->ad_cname.bv_val );
+ send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
+ "weight is misformatted" );
+ return rs->sr_err;
+ }
+ }
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+valsort_db_open(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ return overlay_register_control( be, LDAP_CONTROL_VALSORT );
+}
+
+static int
+valsort_destroy(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ slap_overinst *on = (slap_overinst *)be->bd_info;
+ valsort_info *vi = on->on_bi.bi_private, *next;
+
+#ifdef SLAP_CONFIG_DELETE
+ overlay_unregister_control( be, LDAP_CONTROL_VALSORT );
+#endif /* SLAP_CONFIG_DELETE */
+
+ for (; vi; vi = next) {
+ next = vi->vi_next;
+ ch_free( vi->vi_dn.bv_val );
+ ch_free( vi );
+ }
+
+ return 0;
+}
+
+static int
+valsort_parseCtrl(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ ber_tag_t tag;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_int_t flag = 0;
+
+ if ( BER_BVISNULL( &ctrl->ldctl_value )) {
+ rs->sr_text = "valSort control value is absent";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( BER_BVISEMPTY( &ctrl->ldctl_value )) {
+ rs->sr_text = "valSort control value is empty";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ ber_init2( ber, &ctrl->ldctl_value, 0 );
+ if (( tag = ber_scanf( ber, "{b}", &flag )) == LBER_ERROR ) {
+ rs->sr_text = "valSort control: flag decoding error";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ op->o_ctrlflag[valsort_cid] = ctrl->ldctl_iscritical ?
+ SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL;
+ if ( flag )
+ op->o_ctrlflag[valsort_cid] |= SLAP_CONTROL_DATA0;
+
+ return LDAP_SUCCESS;
+}
+
+static slap_overinst valsort;
+
+int valsort_initialize( void )
+{
+ int rc;
+
+ valsort.on_bi.bi_type = "valsort";
+ valsort.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
+ valsort.on_bi.bi_db_destroy = valsort_destroy;
+ valsort.on_bi.bi_db_open = valsort_db_open;
+
+ valsort.on_bi.bi_op_add = valsort_add;
+ valsort.on_bi.bi_op_modify = valsort_modify;
+
+ valsort.on_response = valsort_response;
+
+ valsort.on_bi.bi_cf_ocs = valsort_cfocs;
+
+ rc = register_supported_control( LDAP_CONTROL_VALSORT,
+ SLAP_CTRL_SEARCH | SLAP_CTRL_HIDE, NULL, valsort_parseCtrl,
+ &valsort_cid );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "Failed to register control %d\n", rc );
+ return rc;
+ }
+
+ syn_numericString = syn_find( "1.3.6.1.4.1.1466.115.121.1.36" );
+
+ rc = config_register_schema( valsort_cfats, valsort_cfocs );
+ if ( rc ) return rc;
+
+ return overlay_register(&valsort);
+}
+
+#if SLAPD_OVER_VALSORT == SLAPD_MOD_DYNAMIC
+int init_module( int argc, char *argv[]) {
+ return valsort_initialize();
+}
+#endif
+
+#endif /* SLAPD_OVER_VALSORT */
diff --git a/servers/slapd/passwd.c b/servers/slapd/passwd.c
new file mode 100644
index 0000000..eef5b09
--- /dev/null
+++ b/servers/slapd/passwd.c
@@ -0,0 +1,657 @@
+/* passwd.c - password extended operation routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#ifdef SLAPD_CRYPT
+#ifdef HAVE_CRYPT_R
+#define __USE_GNU
+#endif /* HAVE_CRYPT_R */
+#include <ac/crypt.h>
+#endif /* SLAPD_CRYPT */
+
+#include "slap.h"
+
+#include <lber_pvt.h>
+#include <lutil.h>
+#include <lutil_sha1.h>
+
+const struct berval slap_EXOP_MODIFY_PASSWD = BER_BVC(LDAP_EXOP_MODIFY_PASSWD);
+
+static const char *defhash[] = {
+#ifdef LUTIL_SHA1_BYTES
+ "{SSHA}",
+#else
+ "{SMD5}",
+#endif
+ NULL
+};
+
+int passwd_extop(
+ Operation *op,
+ SlapReply *rs )
+{
+ struct berval id = {0, NULL}, hash, *rsp = NULL;
+ req_pwdexop_s *qpw = &op->oq_pwdexop;
+ req_extended_s qext = op->oq_extended;
+ Modifications *ml;
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+ int i, nhash;
+ char **hashes, idNul;
+ int rc;
+ BackendDB *op_be;
+ int freenewpw = 0;
+ struct berval dn = BER_BVNULL, ndn = BER_BVNULL;
+
+ assert( ber_bvcmp( &slap_EXOP_MODIFY_PASSWD, &op->ore_reqoid ) == 0 );
+
+ if( op->o_dn.bv_len == 0 ) {
+ Debug( LDAP_DEBUG_STATS, "%s PASSMOD\n",
+ op->o_log_prefix );
+ rs->sr_text = "only authenticated users may change passwords";
+ return LDAP_STRONG_AUTH_REQUIRED;
+ }
+
+ qpw->rs_old.bv_len = 0;
+ qpw->rs_old.bv_val = NULL;
+ qpw->rs_new.bv_len = 0;
+ qpw->rs_new.bv_val = NULL;
+ qpw->rs_mods = NULL;
+ qpw->rs_modtail = NULL;
+
+ rs->sr_err = slap_passwd_parse( op->ore_reqdata, &id,
+ &qpw->rs_old, &qpw->rs_new, &rs->sr_text );
+
+ if ( !BER_BVISNULL( &id )) {
+ idNul = id.bv_val[id.bv_len];
+ id.bv_val[id.bv_len] = '\0';
+ }
+ if ( rs->sr_err == LDAP_SUCCESS && !BER_BVISEMPTY( &id ) ) {
+ Debug( LDAP_DEBUG_STATS, "%s PASSMOD id=\"%s\"%s%s\n",
+ op->o_log_prefix, id.bv_val,
+ qpw->rs_old.bv_val ? " old" : "",
+ qpw->rs_new.bv_val ? " new" : "" );
+ } else {
+ Debug( LDAP_DEBUG_STATS, "%s PASSMOD%s%s\n",
+ op->o_log_prefix,
+ qpw->rs_old.bv_val ? " old" : "",
+ qpw->rs_new.bv_val ? " new" : "" );
+ }
+
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ if ( !BER_BVISNULL( &id ))
+ id.bv_val[id.bv_len] = idNul;
+ return rs->sr_err;
+ }
+
+ if ( !BER_BVISEMPTY( &id ) ) {
+ rs->sr_err = dnPrettyNormal( NULL, &id, &dn, &ndn, op->o_tmpmemctx );
+ id.bv_val[id.bv_len] = idNul;
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ rs->sr_text = "Invalid DN";
+ rc = rs->sr_err;
+ goto error_return;
+ }
+ op->o_req_dn = dn;
+ op->o_req_ndn = ndn;
+ op->o_bd = select_backend( &op->o_req_ndn, 1 );
+
+ } else {
+ ber_dupbv_x( &dn, &op->o_dn, op->o_tmpmemctx );
+ ber_dupbv_x( &ndn, &op->o_ndn, op->o_tmpmemctx );
+ op->o_req_dn = dn;
+ op->o_req_ndn = ndn;
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+ op->o_bd = op->o_conn->c_authz_backend;
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+ }
+
+ if( op->o_bd == NULL ) {
+ if ( qpw->rs_old.bv_val != NULL ) {
+ rs->sr_text = "unwilling to verify old password";
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto error_return;
+ }
+
+#ifdef HAVE_CYRUS_SASL
+ rc = slap_sasl_setpass( op, rs );
+#else
+ rs->sr_text = "no authz backend";
+ rc = LDAP_OTHER;
+#endif
+ goto error_return;
+ }
+
+ if ( op->o_req_ndn.bv_len == 0 ) {
+ rs->sr_text = "no password is associated with the Root DSE";
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto error_return;
+ }
+
+ /* If we've got a glued backend, check the real backend */
+ op_be = op->o_bd;
+ if ( SLAP_GLUE_INSTANCE( op->o_bd )) {
+ op->o_bd = select_backend( &op->o_req_ndn, 0 );
+ }
+
+ if (backend_check_restrictions( op, rs,
+ (struct berval *)&slap_EXOP_MODIFY_PASSWD ) != LDAP_SUCCESS) {
+ rc = rs->sr_err;
+ goto error_return;
+ }
+
+ /* check for referrals */
+ if ( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) {
+ rc = rs->sr_err;
+ goto error_return;
+ }
+
+ /* This does not apply to multi-provider case */
+ if(!( !SLAP_SINGLE_SHADOW( op->o_bd ) || be_isupdate( op ))) {
+ /* we SHOULD return a referral in this case */
+ BerVarray defref = op->o_bd->be_update_refs
+ ? op->o_bd->be_update_refs : default_referral;
+
+ if( defref != NULL ) {
+ rs->sr_ref = referral_rewrite( op->o_bd->be_update_refs,
+ NULL, NULL, LDAP_SCOPE_DEFAULT );
+ if(rs->sr_ref) {
+ rs->sr_flags |= REP_REF_MUSTBEFREED;
+ } else {
+ rs->sr_ref = defref;
+ }
+ rc = LDAP_REFERRAL;
+ goto error_return;
+
+ }
+
+ rs->sr_text = "shadow context; no update referral";
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto error_return;
+ }
+
+ /* generate a new password if none was provided */
+ if ( qpw->rs_new.bv_len == 0 ) {
+ slap_passwd_generate( &qpw->rs_new );
+ if ( qpw->rs_new.bv_len ) {
+ rsp = slap_passwd_return( &qpw->rs_new );
+ freenewpw = 1;
+ }
+ }
+ if ( qpw->rs_new.bv_len == 0 ) {
+ rs->sr_text = "password generation failed";
+ rc = LDAP_OTHER;
+ goto error_return;
+ }
+
+ if ( op->o_txnSpec ) {
+ rc = txn_preop( op, rs );
+ goto error_return;
+ }
+
+ op->o_bd = op_be;
+
+ /* Give the backend a chance to handle this itself */
+ if ( op->o_bd->be_extended ) {
+ rs->sr_err = op->o_bd->be_extended( op, rs );
+ if ( rs->sr_err != LDAP_UNWILLING_TO_PERFORM &&
+ rs->sr_err != SLAP_CB_CONTINUE )
+ {
+ rc = rs->sr_err;
+ if ( rsp ) {
+ rs->sr_rspdata = rsp;
+ rsp = NULL;
+ }
+ goto error_return;
+ }
+ }
+
+ /* The backend didn't handle it, so try it here */
+ if( op->o_bd && !op->o_bd->be_modify ) {
+ rs->sr_text = "operation not supported for current user";
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto error_return;
+ }
+
+ if ( qpw->rs_old.bv_val != NULL ) {
+ Entry *e = NULL;
+
+ rc = be_entry_get_rw( op, &op->o_req_ndn, NULL,
+ slap_schema.si_ad_userPassword, 0, &e );
+ if ( rc == LDAP_SUCCESS && e ) {
+ Attribute *a = attr_find( e->e_attrs,
+ slap_schema.si_ad_userPassword );
+ if ( a )
+ rc = slap_passwd_check( op, e, a, &qpw->rs_old, &rs->sr_text );
+ else
+ rc = 1;
+ be_entry_release_r( op, e );
+ if ( rc == LDAP_SUCCESS )
+ goto old_good;
+ }
+ rs->sr_text = "unwilling to verify old password";
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto error_return;
+ }
+
+old_good:
+ ml = ch_malloc( sizeof(Modifications) );
+ if ( !qpw->rs_modtail ) qpw->rs_modtail = &ml->sml_next;
+
+ if ( default_passwd_hash ) {
+ for ( nhash = 0; default_passwd_hash[nhash]; nhash++ );
+ hashes = default_passwd_hash;
+ } else {
+ nhash = 1;
+ hashes = (char **)defhash;
+ }
+ ml->sml_numvals = nhash;
+ ml->sml_values = ch_malloc( (nhash+1)*sizeof(struct berval) );
+ for ( i=0; hashes[i]; i++ ) {
+ slap_passwd_hash_type( &qpw->rs_new, &hash, hashes[i], &rs->sr_text );
+ if ( hash.bv_len == 0 ) {
+ if ( !rs->sr_text ) {
+ rs->sr_text = "password hash failed";
+ }
+ break;
+ }
+ ml->sml_values[i] = hash;
+ }
+ ml->sml_values[i].bv_val = NULL;
+ ml->sml_nvalues = NULL;
+ ml->sml_desc = slap_schema.si_ad_userPassword;
+ ml->sml_type = ml->sml_desc->ad_cname;
+ ml->sml_op = LDAP_MOD_REPLACE;
+ ml->sml_flags = 0;
+ ml->sml_next = qpw->rs_mods;
+ qpw->rs_mods = ml;
+
+ if ( hashes[i] ) {
+ rs->sr_err = LDAP_OTHER;
+
+ } else {
+ slap_callback **sc;
+
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->o_callback = &cb;
+ op->orm_modlist = qpw->rs_mods;
+ op->orm_no_opattrs = 0;
+
+ cb.sc_private = qpw; /* let Modify know this was pwdMod,
+ * if it cares... */
+
+ rs->sr_err = op->o_bd->be_modify( op, rs );
+
+ /* be_modify() might have shuffled modifications */
+ qpw->rs_mods = op->orm_modlist;
+
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ rs->sr_rspdata = rsp;
+
+ } else if ( rsp ) {
+ ber_bvfree( rsp );
+ rsp = NULL;
+ }
+ op->o_tag = LDAP_REQ_EXTENDED;
+ for ( sc = &op->o_callback; *sc; sc = &(*sc)->sc_next ) {
+ if ( *sc == &cb ) {
+ *sc = cb.sc_next;
+ break;
+ }
+ }
+ }
+
+ rc = rs->sr_err;
+ op->oq_extended = qext;
+
+error_return:;
+ if ( qpw->rs_mods ) {
+ slap_mods_free( qpw->rs_mods, 1 );
+ }
+ if ( freenewpw ) {
+ free( qpw->rs_new.bv_val );
+ }
+ if ( !BER_BVISNULL( &dn ) ) {
+ op->o_tmpfree( dn.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &op->o_req_dn );
+ }
+ if ( !BER_BVISNULL( &ndn ) ) {
+ op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &op->o_req_ndn );
+ }
+
+ return rc;
+}
+
+/* NOTE: The DN in *id is NOT NUL-terminated here. dnNormalize will
+ * reject it in this condition, the caller must NUL-terminate it.
+ * FIXME: should dnNormalize still be complaining about that?
+ */
+int slap_passwd_parse( struct berval *reqdata,
+ struct berval *id,
+ struct berval *oldpass,
+ struct berval *newpass,
+ const char **text )
+{
+ int rc = LDAP_SUCCESS;
+ ber_tag_t tag;
+ ber_len_t len = -1;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+
+ if( reqdata == NULL ) {
+ return LDAP_SUCCESS;
+ }
+
+ if( reqdata->bv_len == 0 ) {
+ *text = "empty request data field";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+ ber_init2( ber, reqdata, 0 );
+
+ tag = ber_skip_tag( ber, &len );
+
+ if( tag != LBER_SEQUENCE ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "slap_passwd_parse: decoding error\n" );
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_ID ) {
+ if( id == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: ID not allowed.\n" );
+
+ *text = "user must change own password";
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ tag = ber_get_stringbv( ber, id, LBER_BV_NOTERM );
+
+ if( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: ID parse failed.\n" );
+
+ goto decoding_error;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_OLD ) {
+ if( oldpass == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD not allowed.\n" );
+
+ *text = "use bind to verify old password";
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ tag = ber_get_stringbv( ber, oldpass, LBER_BV_NOTERM );
+
+ if( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD parse failed.\n" );
+
+ goto decoding_error;
+ }
+
+ if( oldpass->bv_len == 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: OLD empty.\n" );
+
+ *text = "old password value is empty";
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if( tag == LDAP_TAG_EXOP_MODIFY_PASSWD_NEW ) {
+ if( newpass == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: NEW not allowed.\n" );
+
+ *text = "user specified passwords disallowed";
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ tag = ber_get_stringbv( ber, newpass, LBER_BV_NOTERM );
+
+ if( tag == LBER_ERROR ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: NEW parse failed.\n" );
+
+ goto decoding_error;
+ }
+
+ if( newpass->bv_len == 0 ) {
+ Debug( LDAP_DEBUG_TRACE, "slap_passwd_parse: NEW empty.\n" );
+
+ *text = "new password value is empty";
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if( len != 0 ) {
+decoding_error:
+ Debug( LDAP_DEBUG_TRACE,
+ "slap_passwd_parse: decoding error, len=%ld\n",
+ (long) len );
+
+ *text = "data decoding error";
+ rc = LDAP_PROTOCOL_ERROR;
+ }
+
+done:
+ return rc;
+}
+
+struct berval * slap_passwd_return(
+ struct berval *cred )
+{
+ int rc;
+ struct berval *bv = NULL;
+ BerElementBuffer berbuf;
+ /* opaque structure, size unknown but smaller than berbuf */
+ BerElement *ber = (BerElement *)&berbuf;
+
+ assert( cred != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, "slap_passwd_return: %ld\n",
+ (long) cred->bv_len );
+
+ ber_init_w_nullc( ber, LBER_USE_DER );
+
+ rc = ber_printf( ber, "{tON}",
+ LDAP_TAG_EXOP_MODIFY_PASSWD_GEN, cred );
+
+ if( rc >= 0 ) {
+ (void) ber_flatten( ber, &bv );
+ }
+
+ ber_free_buf( ber );
+
+ return bv;
+}
+
+/*
+ * if "e" is provided, access to each value of the password is checked first
+ */
+int
+slap_passwd_check(
+ Operation *op,
+ Entry *e,
+ Attribute *a,
+ struct berval *cred,
+ const char **text )
+{
+ int result = 1;
+ struct berval *bv;
+ AccessControlState acl_state = ACL_STATE_INIT;
+ char credNul = cred->bv_val[cred->bv_len];
+
+#ifdef SLAPD_SPASSWD
+ void *old_authctx = NULL;
+
+ ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)slap_sasl_bind,
+ op->o_conn->c_sasl_authctx, 0, &old_authctx, NULL );
+#endif
+
+ if ( credNul ) cred->bv_val[cred->bv_len] = 0;
+
+ for ( bv = a->a_vals; bv->bv_val != NULL; bv++ ) {
+ /* if e is provided, check access */
+ if ( e && access_allowed( op, e, a->a_desc, bv,
+ ACL_AUTH, &acl_state ) == 0 )
+ {
+ continue;
+ }
+
+ if ( !lutil_passwd( bv, cred, NULL, text ) ) {
+ result = 0;
+ break;
+ }
+ }
+
+ if ( credNul ) cred->bv_val[cred->bv_len] = credNul;
+
+#ifdef SLAPD_SPASSWD
+ ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)slap_sasl_bind,
+ old_authctx, 0, NULL, NULL );
+#endif
+
+ return result;
+}
+
+void
+slap_passwd_generate( struct berval *pass )
+{
+ Debug( LDAP_DEBUG_TRACE, "slap_passwd_generate\n" );
+ BER_BVZERO( pass );
+
+ /*
+ * generate passwords of only 8 characters as some getpass(3)
+ * implementations truncate at 8 characters.
+ */
+ lutil_passwd_generate( pass, 8 );
+}
+
+void
+slap_passwd_hash_type(
+ struct berval * cred,
+ struct berval * new,
+ char *hash,
+ const char **text )
+{
+ new->bv_len = 0;
+ new->bv_val = NULL;
+
+ assert( hash != NULL );
+
+ lutil_passwd_hash( cred , hash, new, text );
+}
+void
+slap_passwd_hash(
+ struct berval * cred,
+ struct berval * new,
+ const char **text )
+{
+ char *hash = NULL;
+ if ( default_passwd_hash ) {
+ hash = default_passwd_hash[0];
+ }
+ if ( !hash ) {
+ hash = (char *)defhash[0];
+ }
+
+ slap_passwd_hash_type( cred, new, hash, text );
+}
+
+#ifdef SLAPD_CRYPT
+static ldap_pvt_thread_mutex_t passwd_mutex;
+static lutil_cryptfunc slapd_crypt;
+
+#ifdef HAVE_CRYPT_R
+static int slapd_crypt( const char *key, const char *salt, char **hash )
+{
+ char *cr;
+ int rc;
+ struct crypt_data data;
+
+ data.initialized = 0;
+ cr = crypt_r( key, salt, &data );
+ if ( cr == NULL || cr[0] == '\0' ) {
+ /* salt must have been invalid */
+ rc = LUTIL_PASSWD_ERR;
+ } else {
+ if ( hash ) {
+ *hash = ber_strdup( cr );
+ rc = LUTIL_PASSWD_OK;
+ } else {
+ rc = strcmp( salt, cr ) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+ }
+ }
+
+ return rc;
+}
+#else
+static int slapd_crypt( const char *key, const char *salt, char **hash )
+{
+ char *cr;
+ int rc;
+
+ ldap_pvt_thread_mutex_lock( &passwd_mutex );
+
+ cr = crypt( key, salt );
+ if ( cr == NULL || cr[0] == '\0' ) {
+ /* salt must have been invalid */
+ rc = LUTIL_PASSWD_ERR;
+ } else {
+ if ( hash ) {
+ *hash = ber_strdup( cr );
+ rc = LUTIL_PASSWD_OK;
+
+ } else {
+ rc = strcmp( salt, cr ) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+ }
+ }
+
+ ldap_pvt_thread_mutex_unlock( &passwd_mutex );
+ return rc;
+}
+#endif /* HAVE_CRYPT_R */
+
+#endif /* SLAPD_CRYPT */
+
+void slap_passwd_init()
+{
+#ifdef SLAPD_CRYPT
+ ldap_pvt_thread_mutex_init( &passwd_mutex );
+ lutil_cryptptr = slapd_crypt;
+#endif
+}
+
diff --git a/servers/slapd/phonetic.c b/servers/slapd/phonetic.c
new file mode 100644
index 0000000..ed3641c
--- /dev/null
+++ b/servers/slapd/phonetic.c
@@ -0,0 +1,459 @@
+/* phonetic.c - routines to do phonetic matching */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/time.h>
+
+#include "slap.h"
+
+#if !defined(SLAPD_METAPHONE) && !defined(SLAPD_PHONETIC)
+#define SLAPD_METAPHONE
+#endif
+
+#define iswordbreak(x) (!isascii(x) || isspace((unsigned char) (x)) || \
+ ispunct((unsigned char) (x)) || \
+ isdigit((unsigned char) (x)) || (x) == '\0')
+
+#if 0
+static char *
+first_word( char *s )
+{
+ if ( s == NULL ) {
+ return( NULL );
+ }
+
+ while ( iswordbreak( *s ) ) {
+ if ( *s == '\0' ) {
+ return( NULL );
+ } else {
+ s++;
+ }
+ }
+
+ return( s );
+}
+
+static char *
+next_word( char *s )
+{
+ if ( s == NULL ) {
+ return( NULL );
+ }
+
+ while ( ! iswordbreak( *s ) ) {
+ s++;
+ }
+
+ while ( iswordbreak( *s ) ) {
+ if ( *s == '\0' ) {
+ return( NULL );
+ } else {
+ s++;
+ }
+ }
+
+ return( s );
+}
+
+static char *
+word_dup( char *w )
+{
+ char *s, *ret;
+ char save;
+
+ for ( s = w; !iswordbreak( *s ); s++ )
+ ; /* NULL */
+ save = *s;
+ *s = '\0';
+ ret = ch_strdup( w );
+ *s = save;
+
+ return( ret );
+}
+#endif /* 0 */
+
+#ifndef MAXPHONEMELEN
+#define MAXPHONEMELEN 4
+#endif
+
+#if defined(SLAPD_PHONETIC)
+
+/* lifted from isode-8.0 */
+char *
+phonetic( char *s )
+{
+ char code, adjacent, ch;
+ char *p;
+ int i;
+ char phoneme[MAXPHONEMELEN + 1];
+
+ p = s;
+ if ( p == NULL || *p == '\0' ) {
+ return( NULL );
+ }
+
+ adjacent = '0';
+ phoneme[0] = TOUPPER((unsigned char)*p);
+
+ phoneme[1] = '\0';
+ for ( i = 0; i < 99 && (! iswordbreak(*p)); p++ ) {
+ ch = TOUPPER ((unsigned char)*p);
+
+ code = '0';
+
+ switch (ch) {
+ case 'B':
+ case 'F':
+ case 'P':
+ case 'V':
+ code = (adjacent != '1') ? '1' : '0';
+ break;
+ case 'S':
+ case 'C':
+ case 'G':
+ case 'J':
+ case 'K':
+ case 'Q':
+ case 'X':
+ case 'Z':
+ code = (adjacent != '2') ? '2' : '0';
+ break;
+ case 'D':
+ case 'T':
+ code = (adjacent != '3') ? '3' : '0';
+ break;
+ case 'L':
+ code = (adjacent != '4') ? '4' : '0';
+ break;
+ case 'M':
+ case 'N':
+ code = (adjacent != '5') ? '5' : '0';
+ break;
+ case 'R':
+ code = (adjacent != '6') ? '6' : '0';
+ break;
+ default:
+ adjacent = '0';
+ }
+
+ if ( i == 0 ) {
+ adjacent = code;
+ i++;
+ } else if ( code != '0' ) {
+ if ( i == MAXPHONEMELEN )
+ break;
+ adjacent = phoneme[i] = code;
+ i++;
+ }
+ }
+
+ if ( i > 0 )
+ phoneme[i] = '\0';
+
+ return( ch_strdup( phoneme ) );
+}
+
+#elif defined(SLAPD_METAPHONE)
+
+/*
+ * Metaphone was originally developed by Lawrence Philips and
+ * published in the "Computer Language" magazine in 1990.
+ */
+/*
+ * Metaphone copied from C Gazette, June/July 1991, pp 56-57,
+ * author Gary A. Parker, with changes by Bernard Tiffany of the
+ * University of Michigan, and more changes by Tim Howes of the
+ * University of Michigan.
+ */
+
+/* Character coding array */
+static const char vsvfn[26] = {
+ 1, 16, 4, 16, 9, 2, 4, 16, 9, 2, 0, 2, 2,
+ /* A B C D E F G H I J K L M */
+ 2, 1, 4, 0, 2, 4, 4, 1, 0, 0, 0, 8, 0};
+ /* N O P Q R S T U V W X Y Z */
+
+/* Macros to access character coding array */
+#define vowel(x) ((x) != '\0' && vsvfn[(x) - 'A'] & 1) /* AEIOU */
+#define same(x) ((x) != '\0' && vsvfn[(x) - 'A'] & 2) /* FJLMNR */
+#define varson(x) ((x) != '\0' && vsvfn[(x) - 'A'] & 4) /* CGPST */
+#define frontv(x) ((x) != '\0' && vsvfn[(x) - 'A'] & 8) /* EIY */
+#define noghf(x) ((x) != '\0' && vsvfn[(x) - 'A'] & 16) /* BDH */
+
+char *
+phonetic( char *Word )
+{
+ char *n, *n_start, *n_end; /* pointers to string */
+ char *metaph_end; /* pointers to metaph */
+ char ntrans[40]; /* word with uppercase letters */
+ int KSflag; /* state flag for X -> KS */
+ char buf[MAXPHONEMELEN + 2];
+ char *Metaph;
+
+ /*
+ * Copy Word to internal buffer, dropping non-alphabetic characters
+ * and converting to upper case
+ */
+
+ for (n = ntrans + 4, n_end = ntrans + 35; !iswordbreak( *Word ) &&
+ n < n_end; Word++) {
+ if (isalpha((unsigned char)*Word))
+ *n++ = TOUPPER((unsigned char)*Word);
+ }
+ Metaph = buf;
+ *Metaph = '\0';
+ if (n == ntrans + 4) {
+ return( ch_strdup( buf ) ); /* Return if null */
+ }
+ n_end = n; /* Set n_end to end of string */
+
+ /* ntrans[0] will always be == 0 */
+ ntrans[0] = '\0';
+ ntrans[1] = '\0';
+ ntrans[2] = '\0';
+ ntrans[3] = '\0';
+ *n++ = 0;
+ *n++ = 0;
+ *n++ = 0;
+ *n = 0; /* Pad with nulls */
+ n = ntrans + 4; /* Assign pointer to start */
+
+ /* Check for PN, KN, GN, AE, WR, WH, and X at start */
+ switch (*n) {
+ case 'P':
+ case 'K':
+ case 'G':
+ /* 'PN', 'KN', 'GN' becomes 'N' */
+ if (*(n + 1) == 'N')
+ *n++ = 0;
+ break;
+ case 'A':
+ /* 'AE' becomes 'E' */
+ if (*(n + 1) == 'E')
+ *n++ = 0;
+ break;
+ case 'W':
+ /* 'WR' becomes 'R', and 'WH' to 'H' */
+ if (*(n + 1) == 'R')
+ *n++ = 0;
+ else if (*(n + 1) == 'H') {
+ *(n + 1) = *n;
+ *n++ = 0;
+ }
+ break;
+ case 'X':
+ /* 'X' becomes 'S' */
+ *n = 'S';
+ break;
+ }
+
+ /*
+ * Now, loop step through string, stopping at end of string or when
+ * the computed 'metaph' is MAXPHONEMELEN characters long
+ */
+
+ KSflag = 0; /* state flag for KS translation */
+ for (metaph_end = Metaph + MAXPHONEMELEN, n_start = n;
+ n <= n_end && Metaph < metaph_end; n++) {
+ if (KSflag) {
+ KSflag = 0;
+ *Metaph++ = 'S';
+ } else {
+ /* Drop duplicates except for CC */
+ if (*(n - 1) == *n && *n != 'C')
+ continue;
+ /* Check for F J L M N R or first letter vowel */
+ if (same(*n) || (n == n_start && vowel(*n)))
+ *Metaph++ = *n;
+ else
+ switch (*n) {
+ case 'B':
+
+ /*
+ * B unless in -MB
+ */
+ if (n == (n_end - 1) && *(n - 1) != 'M')
+ *Metaph++ = *n;
+ break;
+ case 'C':
+
+ /*
+ * X if in -CIA-, -CH- else S if in
+ * -CI-, -CE-, -CY- else dropped if
+ * in -SCI-, -SCE-, -SCY- else K
+ */
+ if (*(n - 1) != 'S' || !frontv(*(n + 1))) {
+ if (*(n + 1) == 'I' && *(n + 2) == 'A')
+ *Metaph++ = 'X';
+ else if (frontv(*(n + 1)))
+ *Metaph++ = 'S';
+ else if (*(n + 1) == 'H')
+ *Metaph++ = ((n == n_start && !vowel(*(n + 2)))
+ || *(n - 1) == 'S')
+ ? (char) 'K' : (char) 'X';
+ else
+ *Metaph++ = 'K';
+ }
+ break;
+ case 'D':
+
+ /*
+ * J if in DGE or DGI or DGY else T
+ */
+ *Metaph++ = (*(n + 1) == 'G' && frontv(*(n + 2)))
+ ? (char) 'J' : (char) 'T';
+ break;
+ case 'G':
+
+ /*
+ * F if in -GH and not B--GH, D--GH,
+ * -H--GH, -H---GH else dropped if
+ * -GNED, -GN, -DGE-, -DGI-, -DGY-
+ * else J if in -GE-, -GI-, -GY- and
+ * not GG else K
+ */
+ if ((*(n + 1) != 'J' || vowel(*(n + 2))) &&
+ (*(n + 1) != 'N' || ((n + 1) < n_end &&
+ (*(n + 2) != 'E' || *(n + 3) != 'D'))) &&
+ (*(n - 1) != 'D' || !frontv(*(n + 1))))
+ *Metaph++ = (frontv(*(n + 1)) &&
+ *(n + 2) != 'G') ? (char) 'G' : (char) 'K';
+ else if (*(n + 1) == 'H' && !noghf(*(n - 3)) &&
+ *(n - 4) != 'H')
+ *Metaph++ = 'F';
+ break;
+ case 'H':
+
+ /*
+ * H if before a vowel and not after
+ * C, G, P, S, T else dropped
+ */
+ if (!varson(*(n - 1)) && (!vowel(*(n - 1)) ||
+ vowel(*(n + 1))))
+ *Metaph++ = 'H';
+ break;
+ case 'K':
+
+ /*
+ * dropped if after C else K
+ */
+ if (*(n - 1) != 'C')
+ *Metaph++ = 'K';
+ break;
+ case 'P':
+
+ /*
+ * F if before H, else P
+ */
+ *Metaph++ = *(n + 1) == 'H' ?
+ (char) 'F' : (char) 'P';
+ break;
+ case 'Q':
+
+ /*
+ * K
+ */
+ *Metaph++ = 'K';
+ break;
+ case 'S':
+
+ /*
+ * X in -SH-, -SIO- or -SIA- else S
+ */
+ *Metaph++ = (*(n + 1) == 'H' ||
+ (*(n + 1) == 'I' && (*(n + 2) == 'O' ||
+ *(n + 2) == 'A')))
+ ? (char) 'X' : (char) 'S';
+ break;
+ case 'T':
+
+ /*
+ * X in -TIA- or -TIO- else 0 (zero)
+ * before H else dropped if in -TCH-
+ * else T
+ */
+ if (*(n + 1) == 'I' && (*(n + 2) == 'O' ||
+ *(n + 2) == 'A'))
+ *Metaph++ = 'X';
+ else if (*(n + 1) == 'H')
+ *Metaph++ = '0';
+ else if (*(n + 1) != 'C' || *(n + 2) != 'H')
+ *Metaph++ = 'T';
+ break;
+ case 'V':
+
+ /*
+ * F
+ */
+ *Metaph++ = 'F';
+ break;
+ case 'W':
+
+ /*
+ * W after a vowel, else dropped
+ */
+ case 'Y':
+
+ /*
+ * Y unless followed by a vowel
+ */
+ if (vowel(*(n + 1)))
+ *Metaph++ = *n;
+ break;
+ case 'X':
+
+ /*
+ * KS
+ */
+ if (n == n_start)
+ *Metaph++ = 'S';
+ else {
+ *Metaph++ = 'K'; /* Insert K, then S */
+ KSflag = 1;
+ }
+ break;
+ case 'Z':
+
+ /*
+ * S
+ */
+ *Metaph++ = 'S';
+ break;
+ }
+ }
+ }
+
+ *Metaph = 0; /* Null terminate */
+ return( ch_strdup( buf ) );
+}
+
+#endif /* SLAPD_METAPHONE */
diff --git a/servers/slapd/proto-slap.h b/servers/slapd/proto-slap.h
new file mode 100644
index 0000000..d7073d7
--- /dev/null
+++ b/servers/slapd/proto-slap.h
@@ -0,0 +1,2247 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#ifndef PROTO_SLAP_H
+#define PROTO_SLAP_H
+
+#include <ldap_cdefs.h>
+#include "ldap_pvt.h"
+
+LDAP_BEGIN_DECL
+
+struct config_args_s; /* slap-config.h */
+struct config_reply_s; /* slap-config.h */
+
+/*
+ * aci.c
+ */
+#ifdef SLAP_DYNACL
+#ifdef SLAPD_ACI_ENABLED
+LDAP_SLAPD_F (int) dynacl_aci_init LDAP_P(( void ));
+#endif /* SLAPD_ACI_ENABLED */
+#endif /* SLAP_DYNACL */
+
+/*
+ * acl.c
+ */
+LDAP_SLAPD_F (int) access_allowed_mask LDAP_P((
+ Operation *op,
+ Entry *e, AttributeDescription *desc, struct berval *val,
+ slap_access_t access,
+ AccessControlState *state,
+ slap_mask_t *mask ));
+#define access_allowed(op,e,desc,val,access,state) access_allowed_mask(op,e,desc,val,access,state,NULL)
+LDAP_SLAPD_F (int) slap_access_allowed LDAP_P((
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ slap_access_t access,
+ AccessControlState *state,
+ slap_mask_t *maskp ));
+LDAP_SLAPD_F (int) slap_access_always_allowed LDAP_P((
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ slap_access_t access,
+ AccessControlState *state,
+ slap_mask_t *maskp ));
+
+LDAP_SLAPD_F (int) acl_check_modlist LDAP_P((
+ Operation *op, Entry *e, Modifications *ml ));
+
+LDAP_SLAPD_F (void) acl_append( AccessControl **l, AccessControl *a, int pos );
+
+#ifdef SLAP_DYNACL
+LDAP_SLAPD_F (int) slap_dynacl_register LDAP_P(( slap_dynacl_t *da ));
+LDAP_SLAPD_F (slap_dynacl_t *) slap_dynacl_get LDAP_P(( const char *name ));
+#endif /* SLAP_DYNACL */
+LDAP_SLAPD_F (int) acl_init LDAP_P(( void ));
+
+LDAP_SLAPD_F (int) acl_get_part LDAP_P((
+ struct berval *list,
+ int ix,
+ char sep,
+ struct berval *bv ));
+LDAP_SLAPD_F (int) acl_match_set LDAP_P((
+ struct berval *subj,
+ Operation *op,
+ Entry *e,
+ struct berval *default_set_attribute ));
+LDAP_SLAPD_F (int) acl_string_expand LDAP_P((
+ struct berval *newbuf, struct berval *pattern,
+ struct berval *dnmatch, struct berval *valmatch, AclRegexMatches *matches ));
+
+/*
+ * aclparse.c
+ */
+LDAP_SLAPD_V (LDAP_CONST char *) style_strings[];
+
+LDAP_SLAPD_F (int) parse_acl LDAP_P(( Backend *be,
+ const char *fname, int lineno,
+ int argc, char **argv, int pos ));
+
+LDAP_SLAPD_F (char *) access2str LDAP_P(( slap_access_t access ));
+LDAP_SLAPD_F (slap_access_t) str2access LDAP_P(( const char *str ));
+
+#define ACCESSMASK_MAXLEN sizeof("unknown (+wrscan)")
+LDAP_SLAPD_F (char *) accessmask2str LDAP_P(( slap_mask_t mask, char*, int debug ));
+LDAP_SLAPD_F (slap_mask_t) str2accessmask LDAP_P(( const char *str ));
+LDAP_SLAPD_F (void) acl_unparse LDAP_P(( AccessControl*, struct berval* ));
+LDAP_SLAPD_F (void) acl_destroy LDAP_P(( AccessControl* ));
+LDAP_SLAPD_F (void) acl_free LDAP_P(( AccessControl *a ));
+
+
+/*
+ * ad.c
+ */
+LDAP_SLAPD_F (int) slap_str2ad LDAP_P((
+ const char *,
+ AttributeDescription **ad,
+ const char **text ));
+
+LDAP_SLAPD_F (int) slap_bv2ad LDAP_P((
+ struct berval *bv,
+ AttributeDescription **ad,
+ const char **text ));
+
+LDAP_SLAPD_F (void) ad_destroy LDAP_P(( AttributeDescription * ));
+LDAP_SLAPD_F (int) ad_keystring LDAP_P(( struct berval *bv ));
+
+#define ad_cmp(l,r) (((l)->ad_cname.bv_len < (r)->ad_cname.bv_len) \
+ ? -1 : (((l)->ad_cname.bv_len > (r)->ad_cname.bv_len) \
+ ? 1 : strcasecmp((l)->ad_cname.bv_val, (r)->ad_cname.bv_val )))
+
+LDAP_SLAPD_F (int) is_ad_subtype LDAP_P((
+ AttributeDescription *sub,
+ AttributeDescription *super ));
+
+LDAP_SLAPD_F (int) ad_inlist LDAP_P((
+ AttributeDescription *desc,
+ AttributeName *attrs ));
+
+LDAP_SLAPD_F (int) slap_str2undef_ad LDAP_P((
+ const char *,
+ AttributeDescription **ad,
+ const char **text,
+ unsigned proxied ));
+
+LDAP_SLAPD_F (int) slap_bv2undef_ad LDAP_P((
+ struct berval *bv,
+ AttributeDescription **ad,
+ const char **text,
+ unsigned proxied ));
+
+LDAP_SLAPD_F (AttributeDescription *) slap_bv2tmp_ad LDAP_P((
+ struct berval *bv,
+ void *memctx ));
+
+LDAP_SLAPD_F (int) slap_ad_undef_promote LDAP_P((
+ char *name,
+ AttributeType *nat ));
+
+LDAP_SLAPD_F (AttributeDescription *) ad_find_tags LDAP_P((
+ AttributeType *type,
+ struct berval *tags ));
+
+LDAP_SLAPD_F (AttributeName *) str2anlist LDAP_P(( AttributeName *an,
+ char *str, const char *brkstr ));
+LDAP_SLAPD_F (void) anlist_free LDAP_P(( AttributeName *an,
+ int freename, void *ctx ));
+
+LDAP_SLAPD_F (char **) anlist2charray_x LDAP_P((
+ AttributeName *an, int dup, void *ctx ));
+LDAP_SLAPD_F (char **) anlist2charray LDAP_P(( AttributeName *an, int dup ));
+LDAP_SLAPD_F (char **) anlist2attrs LDAP_P(( AttributeName *anlist ));
+LDAP_SLAPD_F (AttributeName *) file2anlist LDAP_P((
+ AttributeName *, const char *, const char * ));
+LDAP_SLAPD_F (int) an_find LDAP_P(( AttributeName *a, struct berval *s ));
+LDAP_SLAPD_F (int) ad_define_option LDAP_P(( const char *name,
+ const char *fname, int lineno ));
+LDAP_SLAPD_F (void) ad_unparse_options LDAP_P(( BerVarray *res ));
+
+LDAP_SLAPD_F (MatchingRule *) ad_mr(
+ AttributeDescription *ad,
+ unsigned usage );
+
+LDAP_SLAPD_V( AttributeName * ) slap_anlist_no_attrs;
+LDAP_SLAPD_V( AttributeName * ) slap_anlist_all_user_attributes;
+LDAP_SLAPD_V( AttributeName * ) slap_anlist_all_operational_attributes;
+LDAP_SLAPD_V( AttributeName * ) slap_anlist_all_attributes;
+
+LDAP_SLAPD_V( struct berval * ) slap_bv_no_attrs;
+LDAP_SLAPD_V( struct berval * ) slap_bv_all_user_attrs;
+LDAP_SLAPD_V( struct berval * ) slap_bv_all_operational_attrs;
+
+/* deprecated; only defined for backward compatibility */
+#define NoAttrs (*slap_bv_no_attrs)
+#define AllUser (*slap_bv_all_user_attrs)
+#define AllOper (*slap_bv_all_operational_attrs)
+
+/*
+ * add.c
+ */
+LDAP_SLAPD_F (int) slap_mods2entry LDAP_P(( Modifications *mods, Entry **e,
+ int initial, int dup, const char **text, char *textbuf, size_t textlen ));
+
+LDAP_SLAPD_F (int) slap_entry2mods LDAP_P(( Entry *e,
+ Modifications **mods, const char **text,
+ char *textbuf, size_t textlen ));
+LDAP_SLAPD_F( int ) slap_add_opattrs(
+ Operation *op,
+ const char **text,
+ char *textbuf, size_t textlen,
+ int manage_ctxcsn );
+
+
+/*
+ * at.c
+ */
+LDAP_SLAPD_V(int) at_oc_cache;
+LDAP_SLAPD_F (void) at_config LDAP_P((
+ const char *fname, int lineno,
+ int argc, char **argv ));
+LDAP_SLAPD_F (AttributeType *) at_find LDAP_P((
+ const char *name ));
+LDAP_SLAPD_F (AttributeType *) at_bvfind LDAP_P((
+ struct berval *name ));
+LDAP_SLAPD_F (int) at_find_in_list LDAP_P((
+ AttributeType *sat, AttributeType **list ));
+LDAP_SLAPD_F (int) at_append_to_list LDAP_P((
+ AttributeType *sat, AttributeType ***listp ));
+LDAP_SLAPD_F (int) at_delete_from_list LDAP_P((
+ int pos, AttributeType ***listp ));
+LDAP_SLAPD_F (int) at_schema_info LDAP_P(( Entry *e ));
+LDAP_SLAPD_F (int) at_add LDAP_P((
+ LDAPAttributeType *at, int user,
+ AttributeType **sat, AttributeType *prev, const char **err ));
+LDAP_SLAPD_F (void) at_destroy LDAP_P(( void ));
+
+LDAP_SLAPD_F (int) is_at_subtype LDAP_P((
+ AttributeType *sub,
+ AttributeType *super ));
+
+LDAP_SLAPD_F (const char *) at_syntax LDAP_P((
+ AttributeType *at ));
+LDAP_SLAPD_F (int) is_at_syntax LDAP_P((
+ AttributeType *at,
+ const char *oid ));
+
+LDAP_SLAPD_F (int) at_start LDAP_P(( AttributeType **at ));
+LDAP_SLAPD_F (int) at_next LDAP_P(( AttributeType **at ));
+LDAP_SLAPD_F (void) at_delete LDAP_P(( AttributeType *at ));
+
+LDAP_SLAPD_F (void) at_unparse LDAP_P((
+ BerVarray *bva, AttributeType *start, AttributeType *end, int system ));
+
+LDAP_SLAPD_F (int) register_at LDAP_P((
+ const char *at,
+ AttributeDescription **ad,
+ int dupok ));
+
+/*
+ * attr.c
+ */
+LDAP_SLAPD_F (void) attr_free LDAP_P(( Attribute *a ));
+LDAP_SLAPD_F (Attribute *) attr_dup LDAP_P(( Attribute *a ));
+
+#ifdef LDAP_COMP_MATCH
+LDAP_SLAPD_F (void) comp_tree_free LDAP_P(( Attribute *a ));
+#endif
+
+#define attr_mergeit( e, d, v ) attr_merge( e, d, v, NULL /* FIXME */ )
+#define attr_mergeit_one( e, d, v ) attr_merge_one( e, d, v, NULL /* FIXME */ )
+
+LDAP_SLAPD_F (Attribute *) attr_alloc LDAP_P(( AttributeDescription *ad ));
+LDAP_SLAPD_F (Attribute *) attrs_alloc LDAP_P(( int num ));
+LDAP_SLAPD_F (int) attr_prealloc LDAP_P(( int num ));
+LDAP_SLAPD_F (int) attr_valfind LDAP_P(( Attribute *a,
+ unsigned flags,
+ struct berval *val,
+ unsigned *slot,
+ void *ctx ));
+LDAP_SLAPD_F (int) attr_valadd LDAP_P(( Attribute *a,
+ BerVarray vals,
+ BerVarray nvals,
+ int num ));
+LDAP_SLAPD_F (int) attr_merge LDAP_P(( Entry *e,
+ AttributeDescription *desc,
+ BerVarray vals,
+ BerVarray nvals ));
+LDAP_SLAPD_F (int) attr_merge_one LDAP_P(( Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ struct berval *nval ));
+LDAP_SLAPD_F (int) attr_normalize LDAP_P(( AttributeDescription *desc,
+ BerVarray vals, BerVarray *nvalsp, void *memctx ));
+LDAP_SLAPD_F (int) attr_normalize_one LDAP_P(( AttributeDescription *desc,
+ struct berval *val, struct berval *nval, void *memctx ));
+LDAP_SLAPD_F (int) attr_merge_normalize LDAP_P(( Entry *e,
+ AttributeDescription *desc,
+ BerVarray vals, void *memctx ));
+LDAP_SLAPD_F (int) attr_merge_normalize_one LDAP_P(( Entry *e,
+ AttributeDescription *desc,
+ struct berval *val, void *memctx ));
+LDAP_SLAPD_F (Attribute *) attrs_find LDAP_P((
+ Attribute *a, AttributeDescription *desc ));
+LDAP_SLAPD_F (Attribute *) attr_find LDAP_P((
+ Attribute *a, AttributeDescription *desc ));
+LDAP_SLAPD_F (int) attr_delete LDAP_P((
+ Attribute **attrs, AttributeDescription *desc ));
+
+LDAP_SLAPD_F (void) attrs_free LDAP_P(( Attribute *a ));
+LDAP_SLAPD_F (Attribute *) attrs_dup LDAP_P(( Attribute *a ));
+LDAP_SLAPD_F (int) attr_init LDAP_P(( void ));
+LDAP_SLAPD_F (int) attr_destroy LDAP_P(( void ));
+
+
+/*
+ * ava.c
+ */
+LDAP_SLAPD_F (int) get_ava LDAP_P((
+ Operation *op,
+ BerElement *ber,
+ Filter *f,
+ unsigned usage,
+ const char **text ));
+LDAP_SLAPD_F (void) ava_free LDAP_P((
+ Operation *op,
+ AttributeAssertion *ava,
+ int freeit ));
+
+/*
+ * backend.c
+ */
+
+#define be_match( be1, be2 ) ( (be1) == (be2) || \
+ ( (be1) && (be2) && (be1)->be_nsuffix == (be2)->be_nsuffix ) )
+
+LDAP_SLAPD_F (int) backend_init LDAP_P((void));
+LDAP_SLAPD_F (int) backend_add LDAP_P((BackendInfo *aBackendInfo));
+LDAP_SLAPD_F (int) backend_num LDAP_P((Backend *be));
+LDAP_SLAPD_F (int) backend_startup LDAP_P((Backend *be));
+LDAP_SLAPD_F (int) backend_startup_one LDAP_P((Backend *be, struct config_reply_s *cr));
+LDAP_SLAPD_F (int) backend_sync LDAP_P((Backend *be));
+LDAP_SLAPD_F (int) backend_shutdown LDAP_P((Backend *be));
+LDAP_SLAPD_F (int) backend_destroy LDAP_P((void));
+LDAP_SLAPD_F (void) backend_stopdown_one LDAP_P((BackendDB *bd ));
+LDAP_SLAPD_F (void) backend_destroy_one LDAP_P((BackendDB *bd, int dynamic));
+
+LDAP_SLAPD_F (BackendInfo *) backend_info LDAP_P(( const char *type ));
+LDAP_SLAPD_F (BackendDB *) backend_db_init LDAP_P(( const char *type,
+ BackendDB *be, int idx, struct config_reply_s *cr ));
+LDAP_SLAPD_F (void) backend_db_insert LDAP_P((BackendDB *bd, int idx));
+LDAP_SLAPD_F (void) backend_db_move LDAP_P((BackendDB *bd, int idx));
+
+LDAP_SLAPD_F (BackendDB *) select_backend LDAP_P((
+ struct berval * dn,
+ int noSubordinates ));
+
+LDAP_SLAPD_F (int) be_issuffix LDAP_P(( Backend *be,
+ struct berval *suffix ));
+LDAP_SLAPD_F (int) be_issubordinate LDAP_P(( Backend *be,
+ struct berval *subordinate ));
+LDAP_SLAPD_F (int) be_isroot LDAP_P(( Operation *op ));
+LDAP_SLAPD_F (int) be_isroot_dn LDAP_P(( Backend *be, struct berval *ndn ));
+LDAP_SLAPD_F (int) be_isroot_pw LDAP_P(( Operation *op ));
+LDAP_SLAPD_F (int) be_rootdn_bind LDAP_P(( Operation *op, SlapReply *rs ));
+LDAP_SLAPD_F (int) be_slurp_update LDAP_P(( Operation *op ));
+#define be_isupdate( op ) be_slurp_update( (op) )
+LDAP_SLAPD_F (int) be_shadow_update LDAP_P(( Operation *op ));
+LDAP_SLAPD_F (int) be_isupdate_dn LDAP_P(( Backend *be, struct berval *ndn ));
+LDAP_SLAPD_F (struct berval *) be_root_dn LDAP_P(( Backend *be ));
+LDAP_SLAPD_F (int) be_entry_get_rw LDAP_P(( Operation *o,
+ struct berval *ndn, ObjectClass *oc,
+ AttributeDescription *at, int rw, Entry **e ));
+
+/* "backend->ophandler(op,rs)" wrappers, applied by contrib:wrap_slap_ops */
+#define SLAP_OP(which, op, rs) slap_bi_op((op)->o_bd->bd_info, which, op, rs)
+#define slap_be_op(be, which, op, rs) slap_bi_op((be)->bd_info, which, op, rs)
+#if !(defined(USE_RS_ASSERT) && (USE_RS_ASSERT))
+#define slap_bi_op(bi, which, op, rs) ((&(bi)->bi_op_bind)[which](op, rs))
+#endif
+LDAP_SLAPD_F (int) (slap_bi_op) LDAP_P(( BackendInfo *bi,
+ slap_operation_t which, Operation *op, SlapReply *rs ));
+
+LDAP_SLAPD_F (int) be_entry_release_rw LDAP_P((
+ Operation *o, Entry *e, int rw ));
+#define be_entry_release_r( o, e ) be_entry_release_rw( o, e, 0 )
+#define be_entry_release_w( o, e ) be_entry_release_rw( o, e, 1 )
+
+LDAP_SLAPD_F (int) backend_unbind LDAP_P((Operation *op, SlapReply *rs));
+LDAP_SLAPD_F (int) backend_connection_init LDAP_P((Connection *conn));
+LDAP_SLAPD_F (int) backend_connection_destroy LDAP_P((Connection *conn));
+
+LDAP_SLAPD_F( int ) backend_check_controls LDAP_P((
+ Operation *op,
+ SlapReply *rs ));
+LDAP_SLAPD_F( int ) backend_check_restrictions LDAP_P((
+ Operation *op,
+ SlapReply *rs,
+ struct berval *opdata ));
+
+LDAP_SLAPD_F( int ) backend_check_referrals LDAP_P((
+ Operation *op,
+ SlapReply *rs ));
+
+LDAP_SLAPD_F (int) backend_group LDAP_P((
+ Operation *op,
+ Entry *target,
+ struct berval *gr_ndn,
+ struct berval *op_ndn,
+ ObjectClass *group_oc,
+ AttributeDescription *group_at
+));
+
+LDAP_SLAPD_F (int) backend_attribute LDAP_P((
+ Operation *op,
+ Entry *target,
+ struct berval *entry_ndn,
+ AttributeDescription *entry_at,
+ BerVarray *vals,
+ slap_access_t access
+));
+
+LDAP_SLAPD_F (int) backend_access LDAP_P((
+ Operation *op,
+ Entry *target,
+ struct berval *edn,
+ AttributeDescription *entry_at,
+ struct berval *nval,
+ slap_access_t access,
+ slap_mask_t *mask ));
+
+LDAP_SLAPD_F (int) backend_operational LDAP_P((
+ Operation *op,
+ SlapReply *rs
+));
+
+LDAP_SLAPD_F (ID) backend_tool_entry_first LDAP_P(( BackendDB *be ));
+
+LDAP_SLAPD_V(BackendInfo) slap_binfo[];
+
+/*
+ * backglue.c
+ */
+
+LDAP_SLAPD_F (int) glue_sub_init( void );
+LDAP_SLAPD_F (int) glue_sub_attach( int online );
+LDAP_SLAPD_F (int) glue_sub_add( BackendDB *be, int advert, int online );
+LDAP_SLAPD_F (int) glue_sub_del( BackendDB *be );
+
+/*
+ * backover.c
+ */
+LDAP_SLAPD_F (int) overlay_register LDAP_P(( slap_overinst *on ));
+LDAP_SLAPD_F (int) overlay_config LDAP_P(( BackendDB *be, const char *ov,
+ int idx, BackendInfo **res, ConfigReply *cr ));
+LDAP_SLAPD_F (void) overlay_destroy_one LDAP_P((
+ BackendDB *be,
+ slap_overinst *on ));
+LDAP_SLAPD_F (slap_overinst *) overlay_next LDAP_P(( slap_overinst *on ));
+LDAP_SLAPD_F (slap_overinst *) overlay_find LDAP_P(( const char *name ));
+LDAP_SLAPD_F (int) overlay_is_over LDAP_P(( BackendDB *be ));
+LDAP_SLAPD_F (int) overlay_is_inst LDAP_P(( BackendDB *be, const char *name ));
+LDAP_SLAPD_F (int) overlay_register_control LDAP_P((
+ BackendDB *be,
+ const char *oid ));
+LDAP_SLAPD_F (int) overlay_op_walk LDAP_P((
+ Operation *op,
+ SlapReply *rs,
+ slap_operation_t which,
+ slap_overinfo *oi,
+ slap_overinst *on ));
+LDAP_SLAPD_F (int) overlay_entry_get_ov LDAP_P((
+ Operation *op,
+ struct berval *dn,
+ ObjectClass *oc,
+ AttributeDescription *ad,
+ int rw,
+ Entry **e,
+ slap_overinst *ov ));
+LDAP_SLAPD_F (int) overlay_entry_release_ov LDAP_P((
+ Operation *op,
+ Entry *e,
+ int rw,
+ slap_overinst *ov ));
+LDAP_SLAPD_F (void) overlay_insert LDAP_P((
+ BackendDB *be, slap_overinst *on, slap_overinst ***prev, int idx ));
+LDAP_SLAPD_F (void) overlay_move LDAP_P((
+ BackendDB *be, slap_overinst *on, int idx ));
+#ifdef SLAP_CONFIG_DELETE
+LDAP_SLAPD_F (void) overlay_remove LDAP_P((
+ BackendDB *be, slap_overinst *on, Operation *op ));
+LDAP_SLAPD_F (void) overlay_unregister_control LDAP_P((
+ BackendDB *be,
+ const char *oid ));
+#endif /* SLAP_CONFIG_DELETE */
+LDAP_SLAPD_F (int) overlay_callback_after_backover LDAP_P((
+ Operation *op, slap_callback *sc, int append ));
+
+/*
+ * bconfig.c
+ */
+LDAP_SLAPD_F (int) slap_loglevel_register LDAP_P (( slap_mask_t m, struct berval *s ));
+LDAP_SLAPD_F (int) slap_loglevel_get LDAP_P(( struct berval *s, int *l ));
+LDAP_SLAPD_F (int) str2loglevel LDAP_P(( const char *s, int *l ));
+LDAP_SLAPD_F (int) loglevel2bvarray LDAP_P(( int l, BerVarray *bva ));
+LDAP_SLAPD_F (const char *) loglevel2str LDAP_P(( int l ));
+LDAP_SLAPD_F (int) loglevel2bv LDAP_P(( int l, struct berval *bv ));
+LDAP_SLAPD_F (int) loglevel_print LDAP_P(( FILE *out ));
+LDAP_SLAPD_F (int) slap_cf_aux_table_parse LDAP_P(( const char *word, void *bc, slap_cf_aux_table *tab0, LDAP_CONST char *tabmsg ));
+LDAP_SLAPD_F (int) slap_cf_aux_table_unparse LDAP_P(( void *bc, struct berval *bv, slap_cf_aux_table *tab0 ));
+
+/*
+ * ch_malloc.c
+ */
+LDAP_SLAPD_V (BerMemoryFunctions) ch_mfuncs;
+LDAP_SLAPD_F (void *) ch_malloc LDAP_P(( ber_len_t size ));
+LDAP_SLAPD_F (void *) ch_realloc LDAP_P(( void *block, ber_len_t size ));
+LDAP_SLAPD_F (void *) ch_calloc LDAP_P(( ber_len_t nelem, ber_len_t size ));
+LDAP_SLAPD_F (char *) ch_strdup LDAP_P(( const char *string ));
+LDAP_SLAPD_F (void) ch_free LDAP_P(( void * ));
+
+#ifndef CH_FREE
+#undef free
+#define free ch_free
+#endif
+
+/*
+ * compare.c
+ */
+
+LDAP_SLAPD_F (int) slap_compare_entry LDAP_P((
+ Operation *op,
+ Entry *e,
+ AttributeAssertion *ava ));
+
+/*
+ * component.c
+ */
+#ifdef LDAP_COMP_MATCH
+struct comp_attribute_aliasing;
+
+LDAP_SLAPD_F (int) test_comp_filter_entry LDAP_P((
+ Operation* op,
+ Entry* e,
+ MatchingRuleAssertion* mr));
+
+LDAP_SLAPD_F (int) dup_comp_filter LDAP_P((
+ Operation* op,
+ struct berval *bv,
+ ComponentFilter *in_f,
+ ComponentFilter **out_f ));
+
+LDAP_SLAPD_F (int) get_aliased_filter_aa LDAP_P((
+ Operation* op,
+ AttributeAssertion* a_assert,
+ struct comp_attribute_aliasing* aa,
+ const char** text ));
+
+LDAP_SLAPD_F (int) get_aliased_filter LDAP_P((
+ Operation* op,
+ MatchingRuleAssertion* ma,
+ struct comp_attribute_aliasing* aa,
+ const char** text ));
+
+LDAP_SLAPD_F (int) get_comp_filter LDAP_P((
+ Operation* op,
+ BerValue* bv,
+ ComponentFilter** filt,
+ const char **text ));
+
+LDAP_SLAPD_F (int) insert_component_reference LDAP_P((
+ ComponentReference *cr,
+ ComponentReference** cr_list ));
+
+LDAP_SLAPD_F (int) is_component_reference LDAP_P((
+ char *attr ));
+
+LDAP_SLAPD_F (int) extract_component_reference LDAP_P((
+ char* attr,
+ ComponentReference** cr ));
+
+LDAP_SLAPD_F (int) componentFilterMatch LDAP_P((
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue ));
+
+LDAP_SLAPD_F (int) directoryComponentsMatch LDAP_P((
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue ));
+
+LDAP_SLAPD_F (int) allComponentsMatch LDAP_P((
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue ));
+
+LDAP_SLAPD_F (ComponentReference*) dup_comp_ref LDAP_P((
+ Operation *op,
+ ComponentReference *cr ));
+
+LDAP_SLAPD_F (int) componentFilterValidate LDAP_P((
+ Syntax *syntax,
+ struct berval* bv ));
+
+LDAP_SLAPD_F (int) allComponentsValidate LDAP_P((
+ Syntax *syntax,
+ struct berval* bv ));
+
+LDAP_SLAPD_F (void) component_free LDAP_P((
+ ComponentFilter *f ));
+
+LDAP_SLAPD_F (void) free_ComponentData LDAP_P((
+ Attribute *a ));
+
+LDAP_SLAPD_V (test_membership_func*) is_aliased_attribute;
+
+LDAP_SLAPD_V (free_component_func*) component_destructor;
+
+LDAP_SLAPD_V (get_component_info_func*) get_component_description;
+
+LDAP_SLAPD_V (component_encoder_func*) component_encoder;
+
+LDAP_SLAPD_V (convert_attr_to_comp_func*) attr_converter;
+
+LDAP_SLAPD_V (alloc_nibble_func*) nibble_mem_allocator;
+
+LDAP_SLAPD_V (free_nibble_func*) nibble_mem_free;
+#endif
+
+/*
+ * controls.c
+ */
+LDAP_SLAPD_V( struct slap_control_ids ) slap_cids;
+LDAP_SLAPD_F (void) slap_free_ctrls LDAP_P((
+ Operation *op,
+ LDAPControl **ctrls ));
+LDAP_SLAPD_F (int) slap_add_ctrls LDAP_P((
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl **ctrls ));
+LDAP_SLAPD_F (int) slap_parse_ctrl LDAP_P((
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *control,
+ const char **text ));
+LDAP_SLAPD_F (int) get_ctrls LDAP_P((
+ Operation *op,
+ SlapReply *rs,
+ int senderrors ));
+LDAP_SLAPD_F (int) get_ctrls2 LDAP_P((
+ Operation *op,
+ SlapReply *rs,
+ int senderrors,
+ ber_tag_t ctag ));
+LDAP_SLAPD_F (int) register_supported_control2 LDAP_P((
+ const char *controloid,
+ slap_mask_t controlmask,
+ char **controlexops,
+ SLAP_CTRL_PARSE_FN *controlparsefn,
+ unsigned flags,
+ int *controlcid ));
+#define register_supported_control(oid, mask, exops, fn, cid) \
+ register_supported_control2((oid), (mask), (exops), (fn), 0, (cid))
+#ifdef SLAP_CONFIG_DELETE
+LDAP_SLAPD_F (int) unregister_supported_control LDAP_P((
+ const char* controloid ));
+#endif /* SLAP_CONFIG_DELETE */
+LDAP_SLAPD_F (int) register_control_exop LDAP_P (( const char *controloid, char *exopoid ));
+LDAP_SLAPD_F (int) slap_controls_init LDAP_P ((void));
+LDAP_SLAPD_F (void) controls_destroy LDAP_P ((void));
+LDAP_SLAPD_F (int) controls_root_dse_info LDAP_P ((Entry *e));
+LDAP_SLAPD_F (int) get_supported_controls LDAP_P ((
+ char ***ctrloidsp, slap_mask_t **ctrlmasks ));
+LDAP_SLAPD_F (int) slap_find_control_id LDAP_P ((
+ const char *oid, int *cid ));
+LDAP_SLAPD_F (int) slap_global_control LDAP_P ((
+ Operation *op, const char *oid, int *cid ));
+LDAP_SLAPD_F (int) slap_remove_control LDAP_P((
+ Operation *op,
+ SlapReply *rs,
+ int ctrl,
+ BI_chk_controls fnc ));
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+LDAP_SLAPD_F (int)
+slap_ctrl_session_tracking_add LDAP_P((
+ Operation *op,
+ SlapReply *rs,
+ struct berval *ip,
+ struct berval *name,
+ struct berval *id,
+ LDAPControl *ctrl ));
+LDAP_SLAPD_F (int)
+slap_ctrl_session_tracking_request_add LDAP_P((
+ Operation *op, SlapReply *rs, LDAPControl *ctrl ));
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+#ifdef SLAP_CONTROL_X_WHATFAILED
+LDAP_SLAPD_F (int)
+slap_ctrl_whatFailed_add LDAP_P((
+ Operation *op,
+ SlapReply *rs,
+ char **oids ));
+#endif /* SLAP_CONTROL_X_WHATFAILED */
+
+/*
+ * config.c
+ */
+LDAP_SLAPD_F (int) read_config LDAP_P(( const char *fname, const char *dir ));
+LDAP_SLAPD_F (void) config_destroy LDAP_P ((void));
+LDAP_SLAPD_F (char **) slap_str2clist LDAP_P((
+ char ***, char *, const char * ));
+LDAP_SLAPD_F (int) bverb_to_mask LDAP_P((
+ struct berval *bword, slap_verbmasks *v ));
+LDAP_SLAPD_F (int) verb_to_mask LDAP_P((
+ const char *word, slap_verbmasks *v ));
+LDAP_SLAPD_F (int) verbs_to_mask LDAP_P((
+ int argc, char *argv[], slap_verbmasks *v, slap_mask_t *m ));
+LDAP_SLAPD_F (int) mask_to_verbs LDAP_P((
+ slap_verbmasks *v, slap_mask_t m, BerVarray *bva ));
+LDAP_SLAPD_F (int) mask_to_verbstring LDAP_P((
+ slap_verbmasks *v, slap_mask_t m, char delim, struct berval *bv ));
+LDAP_SLAPD_F (int) verbstring_to_mask LDAP_P((
+ slap_verbmasks *v, char *str, char delim, slap_mask_t *m ));
+LDAP_SLAPD_F (int) enum_to_verb LDAP_P((
+ slap_verbmasks *v, slap_mask_t m, struct berval *bv ));
+LDAP_SLAPD_F (int) slap_verbmasks_init LDAP_P(( slap_verbmasks **vp, slap_verbmasks *v ));
+LDAP_SLAPD_F (int) slap_verbmasks_destroy LDAP_P(( slap_verbmasks *v ));
+LDAP_SLAPD_F (int) slap_verbmasks_append LDAP_P(( slap_verbmasks **vp,
+ slap_mask_t m, struct berval *v, slap_mask_t *ignore ));
+LDAP_SLAPD_F (int) slap_tls_get_config LDAP_P((
+ LDAP *ld, int opt, char **val ));
+LDAP_SLAPD_F (void) bindconf_tls_defaults LDAP_P(( slap_bindconf *bc ));
+LDAP_SLAPD_F (int) bindconf_tls_parse LDAP_P((
+ const char *word, slap_bindconf *bc ));
+LDAP_SLAPD_F (int) bindconf_tls_unparse LDAP_P((
+ slap_bindconf *bc, struct berval *bv ));
+LDAP_SLAPD_F (int) bindconf_parse LDAP_P((
+ const char *word, slap_bindconf *bc ));
+LDAP_SLAPD_F (int) bindconf_unparse LDAP_P((
+ slap_bindconf *bc, struct berval *bv ));
+LDAP_SLAPD_F (int) bindconf_tls_set LDAP_P((
+ slap_bindconf *bc, LDAP *ld ));
+LDAP_SLAPD_F (void) bindconf_free LDAP_P(( slap_bindconf *bc ));
+LDAP_SLAPD_F (void) slap_client_keepalive LDAP_P(( LDAP *ld, slap_keepalive *sk ));
+LDAP_SLAPD_F (int) slap_client_connect LDAP_P(( LDAP **ldp, slap_bindconf *sb ));
+LDAP_SLAPD_F (int) config_generic_wrapper LDAP_P(( Backend *be,
+ const char *fname, int lineno, int argc, char **argv ));
+LDAP_SLAPD_F (char *) anlist_unparse LDAP_P(( AttributeName *, char *, ber_len_t buflen ));
+LDAP_SLAPD_F (int) slap_bv_x_ordered_unparse LDAP_P(( BerVarray in, BerVarray *out ));
+LDAP_SLAPD_F (int) slap_keepalive_parse( struct berval *val, void *bc,
+ slap_cf_aux_table *tab0, const char *tabmsg, int unparse );
+
+#ifdef LDAP_SLAPI
+LDAP_SLAPD_V (int) slapi_plugins_used;
+#endif
+
+/*
+ * connection.c
+ */
+LDAP_SLAPD_F (int) connections_init LDAP_P((void));
+LDAP_SLAPD_F (int) connections_shutdown LDAP_P((void));
+LDAP_SLAPD_F (int) connections_destroy LDAP_P((void));
+LDAP_SLAPD_F (int) connections_timeout_idle LDAP_P((time_t));
+LDAP_SLAPD_F (void) connections_drop LDAP_P((void));
+
+LDAP_SLAPD_F (Connection *) connection_client_setup LDAP_P((
+ ber_socket_t s,
+ ldap_pvt_thread_start_t *func,
+ void *arg ));
+LDAP_SLAPD_F (void) connection_client_enable LDAP_P(( Connection *c ));
+LDAP_SLAPD_F (void) connection_client_stop LDAP_P(( Connection *c ));
+
+#ifdef LDAP_PF_LOCAL_SENDMSG
+#define LDAP_PF_LOCAL_SENDMSG_ARG(arg) , arg
+#else
+#define LDAP_PF_LOCAL_SENDMSG_ARG(arg)
+#endif
+
+LDAP_SLAPD_F (Connection *) connection_init LDAP_P((
+ ber_socket_t s,
+ Listener* url,
+ const char* dnsname,
+ const char* peername,
+ int use_tls,
+ slap_ssf_t ssf,
+ struct berval *id
+ LDAP_PF_LOCAL_SENDMSG_ARG(struct berval *peerbv)));
+
+LDAP_SLAPD_F (void) operation_counter_init LDAP_P(( Operation *op, void *threadctx ));
+LDAP_SLAPD_F (void) connection_closing LDAP_P((
+ Connection *c, const char *why ));
+LDAP_SLAPD_F (int) connection_is_active LDAP_P(( ber_socket_t s ));
+LDAP_SLAPD_F (int) connection_valid LDAP_P(( Connection *c ));
+LDAP_SLAPD_F (const char *) connection_state2str LDAP_P(( int state ))
+ LDAP_GCCATTR((const));
+
+LDAP_SLAPD_F (int) connection_read_activate LDAP_P((ber_socket_t s));
+LDAP_SLAPD_F (int) connection_write LDAP_P((ber_socket_t s));
+LDAP_SLAPD_F (void) connection_write_resume LDAP_P((Connection *c));
+
+LDAP_SLAPD_F (void) connection_op_finish LDAP_P((
+ Operation *op ));
+
+LDAP_SLAPD_F (unsigned long) connections_nextid(void);
+
+LDAP_SLAPD_F (Connection *) connection_first LDAP_P(( ber_socket_t * ));
+LDAP_SLAPD_F (Connection *) connection_next LDAP_P((
+ Connection *, ber_socket_t *));
+LDAP_SLAPD_F (void) connection_done LDAP_P((Connection *));
+
+LDAP_SLAPD_F (void) connection2anonymous LDAP_P((Connection *));
+LDAP_SLAPD_F (void) connection_fake_init LDAP_P((
+ Connection *conn,
+ OperationBuffer *opbuf,
+ void *threadctx ));
+LDAP_SLAPD_F (void) connection_fake_init2 LDAP_P((
+ Connection *conn,
+ OperationBuffer *opbuf,
+ void *threadctx,
+ int newmem ));
+LDAP_SLAPD_F (void) operation_fake_init LDAP_P((
+ Connection *conn,
+ Operation *op,
+ void *threadctx,
+ int newmem ));
+LDAP_SLAPD_F (void) connection_assign_nextid LDAP_P((Connection *));
+
+/*
+ * cr.c
+ */
+LDAP_SLAPD_F (int) cr_schema_info( Entry *e );
+LDAP_SLAPD_F (void) cr_unparse LDAP_P((
+ BerVarray *bva, ContentRule *start, ContentRule *end, int system ));
+
+LDAP_SLAPD_F (int) cr_add LDAP_P((
+ LDAPContentRule *oc,
+ int user,
+ ContentRule **scr,
+ const char **err));
+
+LDAP_SLAPD_F (void) cr_destroy LDAP_P(( void ));
+
+LDAP_SLAPD_F (ContentRule *) cr_find LDAP_P((
+ const char *crname));
+LDAP_SLAPD_F (ContentRule *) cr_bvfind LDAP_P((
+ struct berval *crname));
+
+/*
+ * ctxcsn.c
+ */
+
+LDAP_SLAPD_V( int ) slap_serverID;
+LDAP_SLAPD_V( const struct berval ) slap_ldapsync_bv;
+LDAP_SLAPD_V( const struct berval ) slap_ldapsync_cn_bv;
+LDAP_SLAPD_F (void) slap_get_commit_csn LDAP_P((
+ Operation *, struct berval *maxcsn, int *foundit ));
+LDAP_SLAPD_F (void) slap_rewind_commit_csn LDAP_P(( Operation * ));
+LDAP_SLAPD_F (void) slap_graduate_commit_csn LDAP_P(( Operation * ));
+LDAP_SLAPD_F (Entry *) slap_create_context_csn_entry LDAP_P(( Backend *, struct berval *));
+LDAP_SLAPD_F (int) slap_get_csn LDAP_P(( Operation *, struct berval *, int ));
+LDAP_SLAPD_F (void) slap_queue_csn LDAP_P(( Operation *, struct berval * ));
+
+/*
+ * daemon.c
+ */
+LDAP_SLAPD_F (void) slapd_add_internal(ber_socket_t s, int isactive);
+LDAP_SLAPD_F (int) slapd_daemon_init( const char *urls );
+LDAP_SLAPD_F (int) slapd_daemon_resize( int newnum );
+LDAP_SLAPD_F (int) slapd_daemon_destroy(void);
+LDAP_SLAPD_F (int) slapd_daemon(void);
+LDAP_SLAPD_F (Listener **) slapd_get_listeners LDAP_P((void));
+LDAP_SLAPD_F (void) slapd_remove LDAP_P((ber_socket_t s, Sockbuf *sb,
+ int wasactive, int wake, int locked ));
+
+LDAP_SLAPD_F (RETSIGTYPE) slap_sig_shutdown LDAP_P((int sig));
+LDAP_SLAPD_F (RETSIGTYPE) slap_sig_wake LDAP_P((int sig));
+LDAP_SLAPD_F (void) slap_wake_listener LDAP_P((void));
+
+LDAP_SLAPD_F (void) slap_suspend_listeners LDAP_P((void));
+LDAP_SLAPD_F (void) slap_resume_listeners LDAP_P((void));
+
+LDAP_SLAPD_F (int) slap_pause_server LDAP_P((void));
+LDAP_SLAPD_F (int) slap_unpause_server LDAP_P((void));
+
+LDAP_SLAPD_F (void) slapd_set_write LDAP_P((ber_socket_t s, int wake));
+LDAP_SLAPD_F (void) slapd_clr_write LDAP_P((ber_socket_t s, int wake));
+LDAP_SLAPD_F (void) slapd_set_read LDAP_P((ber_socket_t s, int wake));
+LDAP_SLAPD_F (int) slapd_clr_read LDAP_P((ber_socket_t s, int wake));
+LDAP_SLAPD_F (int) slapd_wait_writer( ber_socket_t sd );
+LDAP_SLAPD_F (void) slapd_shutsock( ber_socket_t sd );
+
+LDAP_SLAPD_V (volatile sig_atomic_t) slapd_abrupt_shutdown;
+LDAP_SLAPD_V (volatile sig_atomic_t) slapd_shutdown;
+LDAP_SLAPD_V (int) slapd_register_slp;
+LDAP_SLAPD_V (const char *) slapd_slp_attrs;
+LDAP_SLAPD_V (slap_ssf_t) local_ssf;
+LDAP_SLAPD_V (struct runqueue_s) slapd_rq;
+LDAP_SLAPD_V (int) slapd_daemon_threads;
+LDAP_SLAPD_V (int) slapd_daemon_mask;
+#ifdef LDAP_TCP_BUFFER
+LDAP_SLAPD_V (int) slapd_tcp_rmem;
+LDAP_SLAPD_V (int) slapd_tcp_wmem;
+#endif /* LDAP_TCP_BUFFER */
+
+#ifdef HAVE_WINSOCK
+LDAP_SLAPD_F (ber_socket_t) slapd_socknew(ber_socket_t s);
+LDAP_SLAPD_F (ber_socket_t) slapd_sock2fd(ber_socket_t s);
+LDAP_SLAPD_V (SOCKET *) slapd_ws_sockets;
+#define SLAP_FD2SOCK(s) slapd_ws_sockets[s]
+#define SLAP_SOCK2FD(s) slapd_sock2fd(s)
+#define SLAP_SOCKNEW(s) slapd_socknew(s)
+#else
+#define SLAP_FD2SOCK(s) s
+#define SLAP_SOCK2FD(s) s
+#define SLAP_SOCKNEW(s) s
+#endif
+
+LDAP_SLAPD_V (ldap_pvt_thread_mutex_t) slapd_init_mutex;
+LDAP_SLAPD_V (ldap_pvt_thread_cond_t) slapd_init_cond;
+
+/*
+ * dn.c
+ */
+
+#define dn_match(dn1, dn2) ( ber_bvcmp((dn1), (dn2)) == 0 )
+#define bvmatch(bv1, bv2) ( ((bv1)->bv_len == (bv2)->bv_len) && (memcmp((bv1)->bv_val, (bv2)->bv_val, (bv1)->bv_len) == 0) )
+
+LDAP_SLAPD_F (int) dnValidate LDAP_P((
+ Syntax *syntax,
+ struct berval *val ));
+LDAP_SLAPD_F (int) rdnValidate LDAP_P((
+ Syntax *syntax,
+ struct berval *val ));
+
+LDAP_SLAPD_F (slap_mr_normalize_func) dnNormalize;
+
+LDAP_SLAPD_F (slap_mr_normalize_func) rdnNormalize;
+
+LDAP_SLAPD_F (slap_syntax_transform_func) dnPretty;
+
+LDAP_SLAPD_F (slap_syntax_transform_func) rdnPretty;
+
+LDAP_SLAPD_F (int) dnPrettyNormal LDAP_P((
+ Syntax *syntax,
+ struct berval *val,
+ struct berval *pretty,
+ struct berval *normal,
+ void *ctx ));
+
+LDAP_SLAPD_F (int) dnMatch LDAP_P((
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue ));
+
+LDAP_SLAPD_F (int) dnRelativeMatch LDAP_P((
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue ));
+
+LDAP_SLAPD_F (int) rdnMatch LDAP_P((
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue ));
+
+
+LDAP_SLAPD_F (int) dnIsSuffix LDAP_P((
+ const struct berval *dn, const struct berval *suffix ));
+
+LDAP_SLAPD_F (int) dnIsWithinScope LDAP_P((
+ struct berval *ndn, struct berval *nbase, int scope ));
+
+LDAP_SLAPD_F (int) dnIsSuffixScope LDAP_P((
+ struct berval *ndn, struct berval *nbase, int scope ));
+
+LDAP_SLAPD_F (int) dnIsOneLevelRDN LDAP_P(( struct berval *rdn ));
+
+LDAP_SLAPD_F (int) dnExtractRdn LDAP_P((
+ struct berval *dn, struct berval *rdn, void *ctx ));
+
+LDAP_SLAPD_F (int) rdn_validate LDAP_P(( struct berval * rdn ));
+
+LDAP_SLAPD_F (ber_len_t) dn_rdnlen LDAP_P(( Backend *be, struct berval *dn ));
+
+LDAP_SLAPD_F (void) build_new_dn LDAP_P((
+ struct berval * new_dn,
+ struct berval * parent_dn,
+ struct berval * newrdn,
+ void *memctx ));
+
+LDAP_SLAPD_F (void) dnParent LDAP_P(( struct berval *dn, struct berval *pdn ));
+LDAP_SLAPD_F (void) dnRdn LDAP_P(( struct berval *dn, struct berval *rdn ));
+
+LDAP_SLAPD_F (int) dnX509normalize LDAP_P(( void *x509_name, struct berval *out ));
+
+LDAP_SLAPD_F (int) dnX509peerNormalize LDAP_P(( void *ssl, struct berval *dn ));
+
+LDAP_SLAPD_F (int) dnPrettyNormalDN LDAP_P(( Syntax *syntax, struct berval *val, LDAPDN *dn, int flags, void *ctx ));
+#define dnPrettyDN(syntax, val, dn, ctx) \
+ dnPrettyNormalDN((syntax),(val),(dn), SLAP_LDAPDN_PRETTY, ctx)
+#define dnNormalDN(syntax, val, dn, ctx) \
+ dnPrettyNormalDN((syntax),(val),(dn), 0, ctx)
+
+typedef int (SLAP_CERT_MAP_FN) LDAP_P(( void *ssl, struct berval *dn ));
+LDAP_SLAPD_F (int) register_certificate_map_function LDAP_P(( SLAP_CERT_MAP_FN *fn ));
+
+/*
+ * entry.c
+ */
+LDAP_SLAPD_V (const Entry) slap_entry_root;
+
+LDAP_SLAPD_F (int) entry_init LDAP_P((void));
+LDAP_SLAPD_F (int) entry_destroy LDAP_P((void));
+
+LDAP_SLAPD_F (Entry *) str2entry LDAP_P(( char *s ));
+LDAP_SLAPD_F (Entry *) str2entry2 LDAP_P(( char *s, int checkvals ));
+LDAP_SLAPD_F (char *) entry2str LDAP_P(( Entry *e, int *len ));
+LDAP_SLAPD_F (char *) entry2str_wrap LDAP_P(( Entry *e, int *len, ber_len_t wrap ));
+
+LDAP_SLAPD_F (ber_len_t) entry_flatsize LDAP_P(( Entry *e, int norm ));
+LDAP_SLAPD_F (void) entry_partsize LDAP_P(( Entry *e, ber_len_t *len,
+ int *nattrs, int *nvals, int norm ));
+
+LDAP_SLAPD_F (int) entry_header LDAP_P(( EntryHeader *eh ));
+LDAP_SLAPD_F (int) entry_decode_dn LDAP_P((
+ EntryHeader *eh, struct berval *dn, struct berval *ndn ));
+#ifdef SLAP_ZONE_ALLOC
+LDAP_SLAPD_F (int) entry_decode LDAP_P((
+ EntryHeader *eh, Entry **e, void *ctx ));
+#else
+LDAP_SLAPD_F (int) entry_decode LDAP_P((
+ EntryHeader *eh, Entry **e ));
+#endif
+LDAP_SLAPD_F (int) entry_encode LDAP_P(( Entry *e, struct berval *bv ));
+
+LDAP_SLAPD_F (void) entry_clean LDAP_P(( Entry *e ));
+LDAP_SLAPD_F (void) entry_free LDAP_P(( Entry *e ));
+LDAP_SLAPD_F (int) entry_cmp LDAP_P(( Entry *a, Entry *b ));
+LDAP_SLAPD_F (int) entry_dn_cmp LDAP_P(( const void *v_a, const void *v_b ));
+LDAP_SLAPD_F (int) entry_id_cmp LDAP_P(( const void *v_a, const void *v_b ));
+LDAP_SLAPD_F (Entry *) entry_dup LDAP_P(( Entry *e ));
+LDAP_SLAPD_F (Entry *) entry_dup2 LDAP_P(( Entry *dest, Entry *src ));
+LDAP_SLAPD_F (Entry *) entry_dup_bv LDAP_P(( Entry *e ));
+LDAP_SLAPD_F (Entry *) entry_alloc LDAP_P((void));
+LDAP_SLAPD_F (int) entry_prealloc LDAP_P((int num));
+
+/*
+ * extended.c
+ */
+LDAP_SLAPD_F (int) exop_root_dse_info LDAP_P ((Entry *e));
+
+#define exop_is_write( op ) ((op->ore_flags & SLAP_EXOP_WRITES) != 0)
+
+LDAP_SLAPD_V( const struct berval ) slap_EXOP_CANCEL;
+LDAP_SLAPD_V( const struct berval ) slap_EXOP_WHOAMI;
+LDAP_SLAPD_V( const struct berval ) slap_EXOP_MODIFY_PASSWD;
+LDAP_SLAPD_V( const struct berval ) slap_EXOP_START_TLS;
+LDAP_SLAPD_V( const struct berval ) slap_EXOP_TXN_START;
+LDAP_SLAPD_V( const struct berval ) slap_EXOP_TXN_END;
+
+typedef int (SLAP_EXTOP_MAIN_FN) LDAP_P(( Operation *op, SlapReply *rs ));
+
+typedef int (SLAP_EXTOP_GETOID_FN) LDAP_P((
+ int index, struct berval *oid, int blen ));
+
+LDAP_SLAPD_F (int) load_extop2 LDAP_P((
+ const struct berval *ext_oid,
+ slap_mask_t flags,
+ SLAP_EXTOP_MAIN_FN *ext_main,
+ unsigned tmpflags ));
+#define load_extop(ext_oid, flags, ext_main) \
+ load_extop2((ext_oid), (flags), (ext_main), 0)
+LDAP_SLAPD_F (int) unload_extop LDAP_P((
+ const struct berval *ext_oid,
+ SLAP_EXTOP_MAIN_FN *ext_main,
+ unsigned tmpflags ));
+
+LDAP_SLAPD_F (int) extops_init LDAP_P(( void ));
+
+LDAP_SLAPD_F (int) extops_kill LDAP_P(( void ));
+
+LDAP_SLAPD_F (struct berval *) get_supported_extop LDAP_P((int index));
+
+/*
+ * txn.c
+ */
+LDAP_SLAPD_F ( SLAP_CTRL_PARSE_FN ) txn_spec_ctrl;
+LDAP_SLAPD_F ( SLAP_EXTOP_MAIN_FN ) txn_start_extop;
+LDAP_SLAPD_F ( SLAP_EXTOP_MAIN_FN ) txn_end_extop;
+LDAP_SLAPD_F ( int ) txn_preop LDAP_P(( Operation *op, SlapReply *rs ));
+
+/*
+ * cancel.c
+ */
+LDAP_SLAPD_F ( SLAP_EXTOP_MAIN_FN ) cancel_extop;
+
+/*
+ * filter.c
+ */
+LDAP_SLAPD_F (int) get_filter LDAP_P((
+ Operation *op,
+ BerElement *ber,
+ Filter **filt,
+ const char **text ));
+
+LDAP_SLAPD_F (void) filter_free LDAP_P(( Filter *f ));
+LDAP_SLAPD_F (void) filter_free_x LDAP_P(( Operation *op, Filter *f, int freeme ));
+LDAP_SLAPD_F (void) filter2bv LDAP_P(( Filter *f, struct berval *bv ));
+LDAP_SLAPD_F (void) filter2bv_x LDAP_P(( Operation *op, Filter *f, struct berval *bv ));
+LDAP_SLAPD_F (void) filter2bv_undef LDAP_P(( Filter *f, int noundef, struct berval *bv ));
+LDAP_SLAPD_F (void) filter2bv_undef_x LDAP_P(( Operation *op, Filter *f, int noundef, struct berval *bv ));
+LDAP_SLAPD_F (Filter *) filter_dup LDAP_P(( Filter *f, void *memctx ));
+
+LDAP_SLAPD_F (int) get_vrFilter LDAP_P(( Operation *op, BerElement *ber,
+ ValuesReturnFilter **f,
+ const char **text ));
+
+LDAP_SLAPD_F (void) vrFilter_free LDAP_P(( Operation *op, ValuesReturnFilter *f ));
+LDAP_SLAPD_F (void) vrFilter2bv LDAP_P(( Operation *op, ValuesReturnFilter *f, struct berval *fstr ));
+
+LDAP_SLAPD_F (int) filter_has_subordinates LDAP_P(( Filter *filter ));
+#define filter_escape_value( in, out ) ldap_bv2escaped_filter_value_x( (in), (out), 0, NULL )
+#define filter_escape_value_x( in, out, ctx ) ldap_bv2escaped_filter_value_x( (in), (out), 0, ctx )
+
+LDAP_SLAPD_V (const Filter *) slap_filter_objectClass_pres;
+LDAP_SLAPD_V (const struct berval *) slap_filterstr_objectClass_pres;
+
+LDAP_SLAPD_F (int) filter_init LDAP_P(( void ));
+LDAP_SLAPD_F (void) filter_destroy LDAP_P(( void ));
+/*
+ * filterentry.c
+ */
+
+LDAP_SLAPD_F (int) test_filter LDAP_P(( Operation *op, Entry *e, Filter *f ));
+
+/*
+ * frontend.c
+ */
+LDAP_SLAPD_F (int) frontend_init LDAP_P(( void ));
+
+/*
+ * globals.c
+ */
+
+LDAP_SLAPD_V( const struct berval ) slap_empty_bv;
+LDAP_SLAPD_V( const struct berval ) slap_unknown_bv;
+LDAP_SLAPD_V( const struct berval ) slap_true_bv;
+LDAP_SLAPD_V( const struct berval ) slap_false_bv;
+LDAP_SLAPD_V( struct slap_sync_cookie_s ) slap_sync_cookie;
+LDAP_SLAPD_V( void * ) slap_tls_ctx;
+LDAP_SLAPD_V( LDAP * ) slap_tls_ld;
+
+/*
+ * index.c
+ */
+LDAP_SLAPD_F (int) slap_str2index LDAP_P(( const char *str, slap_mask_t *idx ));
+LDAP_SLAPD_F (void) slap_index2bvlen LDAP_P(( slap_mask_t idx, struct berval *bv ));
+LDAP_SLAPD_F (void) slap_index2bv LDAP_P(( slap_mask_t idx, struct berval *bv ));
+
+/*
+ * init.c
+ */
+LDAP_SLAPD_F (int) slap_init LDAP_P((int mode, const char* name));
+LDAP_SLAPD_F (int) slap_startup LDAP_P(( Backend *be ));
+LDAP_SLAPD_F (int) slap_shutdown LDAP_P(( Backend *be ));
+LDAP_SLAPD_F (int) slap_destroy LDAP_P((void));
+LDAP_SLAPD_F (void) slap_counters_init LDAP_P((slap_counters_t *sc));
+LDAP_SLAPD_F (void) slap_counters_destroy LDAP_P((slap_counters_t *sc));
+
+LDAP_SLAPD_V (char *) slap_known_controls[];
+
+/*
+ * ldapsync.c
+ */
+LDAP_SLAPD_F (void) slap_compose_sync_cookie LDAP_P((
+ Operation *, struct berval *, BerVarray, int, int, struct berval * ));
+LDAP_SLAPD_F (void) slap_sync_cookie_free LDAP_P((
+ struct sync_cookie *, int free_cookie ));
+LDAP_SLAPD_F (int) slap_parse_csn_sid LDAP_P((
+ struct berval * ));
+LDAP_SLAPD_F (int *) slap_parse_csn_sids LDAP_P((
+ BerVarray, int, void *memctx ));
+LDAP_SLAPD_F (int) slap_sort_csn_sids LDAP_P((
+ BerVarray, int *, int, void *memctx ));
+LDAP_SLAPD_F (void) slap_insert_csn_sids LDAP_P((
+ struct sync_cookie *ck, int, int, struct berval * ));
+LDAP_SLAPD_F (int) slap_parse_sync_cookie LDAP_P((
+ struct sync_cookie *, void *memctx ));
+LDAP_SLAPD_F (void) slap_reparse_sync_cookie LDAP_P((
+ struct sync_cookie *, void *memctx ));
+LDAP_SLAPD_F (int) slap_init_sync_cookie_ctxcsn LDAP_P((
+ struct sync_cookie * ));
+LDAP_SLAPD_F (struct sync_cookie *) slap_dup_sync_cookie LDAP_P((
+ struct sync_cookie *, struct sync_cookie * ));
+LDAP_SLAPD_F (int) slap_build_syncUUID_set LDAP_P((
+ Operation *, BerVarray *, Entry * ));
+
+/*
+ * limits.c
+ */
+LDAP_SLAPD_F (int) limits_parse LDAP_P((
+ Backend *be, const char *fname, int lineno,
+ int argc, char **argv ));
+LDAP_SLAPD_F (int) limits_parse_one LDAP_P(( const char *arg,
+ struct slap_limits_set *limit ));
+LDAP_SLAPD_F (int) limits_check LDAP_P((
+ Operation *op, SlapReply *rs ));
+LDAP_SLAPD_F (int) limits_unparse_one LDAP_P((
+ struct slap_limits_set *limit, int which, struct berval *bv, ber_len_t buflen ));
+LDAP_SLAPD_F (int) limits_unparse LDAP_P((
+ struct slap_limits *limit, struct berval *bv, ber_len_t buflen ));
+LDAP_SLAPD_F (void) limits_free_one LDAP_P((
+ struct slap_limits *lm ));
+LDAP_SLAPD_F (void) limits_destroy LDAP_P(( struct slap_limits **lm ));
+
+/*
+ * lock.c
+ */
+LDAP_SLAPD_F (FILE *) lock_fopen LDAP_P(( const char *fname,
+ const char *type, FILE **lfp ));
+LDAP_SLAPD_F (int) lock_fclose LDAP_P(( FILE *fp, FILE *lfp ));
+
+/*
+ * main.c
+ */
+LDAP_SLAPD_F (int)
+parse_debug_level LDAP_P(( const char *arg, int *levelp, char ***unknowns ));
+LDAP_SLAPD_F (int)
+parse_syslog_level LDAP_P(( const char *arg, int *levelp ));
+LDAP_SLAPD_F (int)
+parse_syslog_user LDAP_P(( const char *arg, int *syslogUser ));
+LDAP_SLAPD_F (int)
+parse_debug_unknowns LDAP_P(( char **unknowns, int *levelp ));
+LDAP_SLAPD_F (void)
+slap_check_unknown_level LDAP_P(( char *levelstr, int level ));
+
+/*
+ * matchedValues.c
+ */
+LDAP_SLAPD_F (int) filter_matched_values(
+ Operation *op,
+ Attribute *a,
+ char ***e_flags );
+
+/*
+ * modrdn.c
+ */
+LDAP_SLAPD_F (int) slap_modrdn2mods LDAP_P((
+ Operation *op,
+ SlapReply *rs ));
+
+/*
+ * modify.c
+ */
+LDAP_SLAPD_F( int ) slap_mods_obsolete_check(
+ Operation *op,
+ Modifications *ml,
+ const char **text,
+ char *textbuf, size_t textlen );
+
+LDAP_SLAPD_F( int ) slap_mods_no_user_mod_check(
+ Operation *op,
+ Modifications *ml,
+ const char **text,
+ char *textbuf, size_t textlen );
+
+LDAP_SLAPD_F ( int ) slap_mods_no_repl_user_mod_check(
+ Operation *op,
+ Modifications *ml,
+ const char **text,
+ char *textbuf,
+ size_t textlen );
+
+LDAP_SLAPD_F( int ) slap_mods_check(
+ Operation *op,
+ Modifications *ml,
+ const char **text,
+ char *textbuf, size_t textlen, void *ctx );
+
+LDAP_SLAPD_F( int ) slap_sort_vals(
+ Modifications *ml,
+ const char **text,
+ int *dup,
+ void *ctx );
+
+LDAP_SLAPD_F( void ) slap_timestamp(
+ time_t *tm,
+ struct berval *bv );
+
+LDAP_SLAPD_F( void ) slap_mods_opattrs(
+ Operation *op,
+ Modifications **modsp,
+ int manage_ctxcsn );
+
+LDAP_SLAPD_F( int ) slap_parse_modlist(
+ Operation *op,
+ SlapReply *rs,
+ BerElement *ber,
+ req_modify_s *ms );
+
+/*
+ * mods.c
+ */
+LDAP_SLAPD_F( int ) modify_add_values( Entry *e,
+ Modification *mod,
+ int permissive,
+ const char **text, char *textbuf, size_t textlen );
+LDAP_SLAPD_F( int ) modify_delete_values( Entry *e,
+ Modification *mod,
+ int permissive,
+ const char **text, char *textbuf, size_t textlen );
+LDAP_SLAPD_F( int ) modify_delete_vindex( Entry *e,
+ Modification *mod,
+ int permissive,
+ const char **text, char *textbuf, size_t textlen, int *idx );
+LDAP_SLAPD_F( int ) modify_replace_values( Entry *e,
+ Modification *mod,
+ int permissive,
+ const char **text, char *textbuf, size_t textlen );
+LDAP_SLAPD_F( int ) modify_increment_values( Entry *e,
+ Modification *mod,
+ int permissive,
+ const char **text, char *textbuf, size_t textlen );
+
+LDAP_SLAPD_F( void ) slap_mod_free( Modification *mod, int freeit );
+LDAP_SLAPD_F( void ) slap_mods_free( Modifications *mods, int freevals );
+LDAP_SLAPD_F( void ) slap_modlist_free( LDAPModList *ml );
+
+/*
+ * module.c
+ */
+#ifdef SLAPD_MODULES
+
+LDAP_SLAPD_F (int) module_init LDAP_P(( void ));
+LDAP_SLAPD_F (int) module_kill LDAP_P(( void ));
+
+LDAP_SLAPD_F (int) load_null_module(
+ const void *module, const char *file_name);
+LDAP_SLAPD_F (int) load_extop_module(
+ const void *module, const char *file_name);
+
+LDAP_SLAPD_F (int) module_load LDAP_P((
+ const char* file_name,
+ int argc, char *argv[] ));
+LDAP_SLAPD_F (int) module_path LDAP_P(( const char* path ));
+LDAP_SLAPD_F (int) module_unload LDAP_P(( const char* file_name ));
+
+LDAP_SLAPD_F (void *) module_handle LDAP_P(( const char* file_name ));
+
+LDAP_SLAPD_F (void *) module_resolve LDAP_P((
+ const void *module, const char *name));
+
+#endif /* SLAPD_MODULES */
+
+/* mr.c */
+LDAP_SLAPD_F (MatchingRule *) mr_bvfind LDAP_P((struct berval *mrname));
+LDAP_SLAPD_F (MatchingRule *) mr_find LDAP_P((const char *mrname));
+LDAP_SLAPD_F (int) mr_add LDAP_P(( LDAPMatchingRule *mr,
+ slap_mrule_defs_rec *def,
+ MatchingRule * associated,
+ const char **err ));
+LDAP_SLAPD_F (void) mr_destroy LDAP_P(( void ));
+
+LDAP_SLAPD_F (int) register_matching_rule LDAP_P((
+ slap_mrule_defs_rec *def ));
+
+LDAP_SLAPD_F (void) mru_destroy LDAP_P(( void ));
+LDAP_SLAPD_F (int) matching_rule_use_init LDAP_P(( void ));
+
+LDAP_SLAPD_F (int) mr_schema_info LDAP_P(( Entry *e ));
+LDAP_SLAPD_F (int) mru_schema_info LDAP_P(( Entry *e ));
+
+LDAP_SLAPD_F (int) mr_usable_with_at LDAP_P(( MatchingRule *mr,
+ AttributeType *at ));
+LDAP_SLAPD_F (int) mr_make_syntax_compat_with_mr LDAP_P((
+ Syntax *syn,
+ MatchingRule *mr ));
+LDAP_SLAPD_F (int) mr_make_syntax_compat_with_mrs LDAP_P((
+ const char *syntax,
+ char *const *mrs ));
+
+/*
+ * mra.c
+ */
+LDAP_SLAPD_F (int) get_mra LDAP_P((
+ Operation *op,
+ BerElement *ber,
+ Filter *f,
+ const char **text ));
+LDAP_SLAPD_F (void) mra_free LDAP_P((
+ Operation *op,
+ MatchingRuleAssertion *mra,
+ int freeit ));
+
+/* oc.c */
+LDAP_SLAPD_F (int) oc_add LDAP_P((
+ LDAPObjectClass *oc,
+ int user,
+ ObjectClass **soc,
+ ObjectClass *prev,
+ const char **err));
+LDAP_SLAPD_F (void) oc_destroy LDAP_P(( void ));
+
+LDAP_SLAPD_F (ObjectClass *) oc_find LDAP_P((
+ const char *ocname));
+LDAP_SLAPD_F (ObjectClass *) oc_bvfind LDAP_P((
+ struct berval *ocname));
+LDAP_SLAPD_F (ObjectClass *) oc_bvfind_undef LDAP_P((
+ struct berval *ocname));
+LDAP_SLAPD_F (int) is_object_subclass LDAP_P((
+ ObjectClass *sup,
+ ObjectClass *sub ));
+
+LDAP_SLAPD_F (int) is_entry_objectclass LDAP_P((
+ Entry *, ObjectClass *oc, unsigned flags ));
+#define is_entry_objectclass_or_sub(e,oc) \
+ (is_entry_objectclass((e),(oc),SLAP_OCF_CHECK_SUP))
+#define is_entry_alias(e) \
+ (((e)->e_ocflags & SLAP_OC__END) \
+ ? (((e)->e_ocflags & SLAP_OC_ALIAS) != 0) \
+ : is_entry_objectclass((e), slap_schema.si_oc_alias, SLAP_OCF_SET_FLAGS))
+#define is_entry_referral(e) \
+ (((e)->e_ocflags & SLAP_OC__END) \
+ ? (((e)->e_ocflags & SLAP_OC_REFERRAL) != 0) \
+ : is_entry_objectclass((e), slap_schema.si_oc_referral, SLAP_OCF_SET_FLAGS))
+#define is_entry_subentry(e) \
+ (((e)->e_ocflags & SLAP_OC__END) \
+ ? (((e)->e_ocflags & SLAP_OC_SUBENTRY) != 0) \
+ : is_entry_objectclass((e), slap_schema.si_oc_subentry, SLAP_OCF_SET_FLAGS))
+#define is_entry_collectiveAttributeSubentry(e) \
+ (((e)->e_ocflags & SLAP_OC__END) \
+ ? (((e)->e_ocflags & SLAP_OC_COLLECTIVEATTRIBUTESUBENTRY) != 0) \
+ : is_entry_objectclass((e), slap_schema.si_oc_collectiveAttributeSubentry, SLAP_OCF_SET_FLAGS))
+#define is_entry_dynamicObject(e) \
+ (((e)->e_ocflags & SLAP_OC__END) \
+ ? (((e)->e_ocflags & SLAP_OC_DYNAMICOBJECT) != 0) \
+ : is_entry_objectclass((e), slap_schema.si_oc_dynamicObject, SLAP_OCF_SET_FLAGS))
+#define is_entry_glue(e) \
+ (((e)->e_ocflags & SLAP_OC__END) \
+ ? (((e)->e_ocflags & SLAP_OC_GLUE) != 0) \
+ : is_entry_objectclass((e), slap_schema.si_oc_glue, SLAP_OCF_SET_FLAGS))
+#define is_entry_syncProviderSubentry(e) \
+ (((e)->e_ocflags & SLAP_OC__END) \
+ ? (((e)->e_ocflags & SLAP_OC_SYNCPROVIDERSUBENTRY) != 0) \
+ : is_entry_objectclass((e), slap_schema.si_oc_syncProviderSubentry, SLAP_OCF_SET_FLAGS))
+#define is_entry_syncConsumerSubentry(e) \
+ (((e)->e_ocflags & SLAP_OC__END) \
+ ? (((e)->e_ocflags & SLAP_OC_SYNCCONSUMERSUBENTRY) != 0) \
+ : is_entry_objectclass((e), slap_schema.si_oc_syncConsumerSubentry, SLAP_OCF_SET_FLAGS))
+
+LDAP_SLAPD_F (int) oc_schema_info( Entry *e );
+
+LDAP_SLAPD_F (int) oc_start LDAP_P(( ObjectClass **oc ));
+LDAP_SLAPD_F (int) oc_next LDAP_P(( ObjectClass **oc ));
+LDAP_SLAPD_F (void) oc_delete LDAP_P(( ObjectClass *oc ));
+
+LDAP_SLAPD_F (void) oc_unparse LDAP_P((
+ BerVarray *bva, ObjectClass *start, ObjectClass *end, int system ));
+
+LDAP_SLAPD_F (int) register_oc LDAP_P((
+ const char *desc,
+ ObjectClass **oc,
+ int dupok ));
+
+/*
+ * oidm.c
+ */
+LDAP_SLAPD_F(char *) oidm_find(char *oid);
+LDAP_SLAPD_F (void) oidm_destroy LDAP_P(( void ));
+LDAP_SLAPD_F (void) oidm_unparse LDAP_P((
+ BerVarray *bva, OidMacro *start, OidMacro *end, int system ));
+LDAP_SLAPD_F (int) parse_oidm LDAP_P((
+ struct config_args_s *ca, int user, OidMacro **om ));
+
+/*
+ * operation.c
+ */
+LDAP_SLAPD_F (void) slap_op_init LDAP_P(( void ));
+LDAP_SLAPD_F (void) slap_op_destroy LDAP_P(( void ));
+LDAP_SLAPD_F (void) slap_op_groups_free LDAP_P(( Operation *op ));
+LDAP_SLAPD_F (void) slap_op_free LDAP_P(( Operation *op, void *ctx ));
+LDAP_SLAPD_F (void) slap_op_time LDAP_P(( time_t *t, int *n ));
+LDAP_SLAPD_F (Operation *) slap_op_alloc LDAP_P((
+ BerElement *ber, ber_int_t msgid,
+ ber_tag_t tag, ber_int_t id, void *ctx ));
+
+LDAP_SLAPD_F (slap_op_t) slap_req2op LDAP_P(( ber_tag_t tag ));
+
+/*
+ * operational.c
+ */
+LDAP_SLAPD_F (Attribute *) slap_operational_subschemaSubentry( Backend *be );
+LDAP_SLAPD_F (Attribute *) slap_operational_entryDN( Entry *e );
+LDAP_SLAPD_F (Attribute *) slap_operational_hasSubordinate( int has );
+
+/*
+ * overlays.c
+ */
+LDAP_SLAPD_F (int) overlay_init( void );
+
+/*
+ * passwd.c
+ */
+LDAP_SLAPD_F (SLAP_EXTOP_MAIN_FN) passwd_extop;
+
+LDAP_SLAPD_F (int) slap_passwd_check(
+ Operation *op,
+ Entry *e,
+ Attribute *a,
+ struct berval *cred,
+ const char **text );
+
+LDAP_SLAPD_F (void) slap_passwd_generate( struct berval * );
+
+LDAP_SLAPD_F (void) slap_passwd_hash(
+ struct berval *cred,
+ struct berval *hash,
+ const char **text );
+
+LDAP_SLAPD_F (void) slap_passwd_hash_type(
+ struct berval *cred,
+ struct berval *hash,
+ char *htype,
+ const char **text );
+
+LDAP_SLAPD_F (struct berval *) slap_passwd_return(
+ struct berval *cred );
+
+LDAP_SLAPD_F (int) slap_passwd_parse(
+ struct berval *reqdata,
+ struct berval *id,
+ struct berval *oldpass,
+ struct berval *newpass,
+ const char **text );
+
+LDAP_SLAPD_F (void) slap_passwd_init (void);
+
+/*
+ * phonetic.c
+ */
+LDAP_SLAPD_F (char *) phonetic LDAP_P(( char *s ));
+
+/*
+ * proxyp.c
+ */
+LDAP_SLAPD_F (int) proxyp LDAP_P((ber_socket_t sfd, Sockaddr *from));
+
+/*
+ * referral.c
+ */
+LDAP_SLAPD_F (int) validate_global_referral LDAP_P((
+ const char *url ));
+
+LDAP_SLAPD_F (BerVarray) get_entry_referrals LDAP_P((
+ Operation *op, Entry *e ));
+
+LDAP_SLAPD_F (BerVarray) referral_rewrite LDAP_P((
+ BerVarray refs,
+ struct berval *base,
+ struct berval *target,
+ int scope ));
+
+LDAP_SLAPD_F (int) get_alias_dn LDAP_P((
+ Entry *e,
+ struct berval *ndn,
+ int *err,
+ const char **text ));
+
+/*
+ * result.c
+ */
+#if USE_RS_ASSERT /*defined(USE_RS_ASSERT)?(USE_RS_ASSERT):defined(LDAP_TEST)*/
+#ifdef __GNUC__
+# define RS_FUNC_ __FUNCTION__
+#elif defined(__STDC_VERSION__) && (__STDC_VERSION__) >= 199901L
+# define RS_FUNC_ __func__
+#else
+# define rs_assert_(file, line, func, cond) rs_assert__(file, line, cond)
+#endif
+LDAP_SLAPD_V(int) rs_suppress_assert;
+LDAP_SLAPD_F(void) rs_assert_(const char*, unsigned, const char*, const char*);
+# define RS_ASSERT(cond) ((rs_suppress_assert > 0 || (cond)) \
+ ? (void) 0 : rs_assert_(__FILE__, __LINE__, RS_FUNC_, #cond))
+#else
+# define RS_ASSERT(cond) ((void) 0)
+# define rs_assert_ok(rs) ((void) (rs))
+# define rs_assert_ready(rs) ((void) (rs))
+# define rs_assert_done(rs) ((void) (rs))
+#endif
+LDAP_SLAPD_F (void) (rs_assert_ok) LDAP_P(( const SlapReply *rs ));
+LDAP_SLAPD_F (void) (rs_assert_ready) LDAP_P(( const SlapReply *rs ));
+LDAP_SLAPD_F (void) (rs_assert_done) LDAP_P(( const SlapReply *rs ));
+
+#define rs_reinit(rs, type) do { \
+ SlapReply *const rsRI = (rs); \
+ rs_assert_done( rsRI ); \
+ rsRI->sr_type = (type); \
+ /* Got type before memset in case of rs_reinit(rs, rs->sr_type) */ \
+ assert( !offsetof( SlapReply, sr_type ) ); \
+ memset( (slap_reply_t *) rsRI + 1, 0, \
+ sizeof(*rsRI) - sizeof(slap_reply_t) ); \
+ } while ( 0 )
+LDAP_SLAPD_F (void) (rs_reinit) LDAP_P(( SlapReply *rs, slap_reply_t type ));
+LDAP_SLAPD_F (void) rs_flush_entry LDAP_P(( Operation *op,
+ SlapReply *rs, slap_overinst *on ));
+LDAP_SLAPD_F (void) rs_replace_entry LDAP_P(( Operation *op,
+ SlapReply *rs, slap_overinst *on, Entry *e ));
+LDAP_SLAPD_F (int) rs_entry2modifiable LDAP_P(( Operation *op,
+ SlapReply *rs, slap_overinst *on ));
+#define rs_ensure_entry_modifiable rs_entry2modifiable /* older name */
+LDAP_SLAPD_F (void) slap_send_ldap_result LDAP_P(( Operation *op, SlapReply *rs ));
+LDAP_SLAPD_F (void) send_ldap_sasl LDAP_P(( Operation *op, SlapReply *rs ));
+LDAP_SLAPD_F (void) send_ldap_disconnect LDAP_P(( Operation *op, SlapReply *rs ));
+LDAP_SLAPD_F (void) slap_send_ldap_extended LDAP_P(( Operation *op, SlapReply *rs ));
+LDAP_SLAPD_F (void) slap_send_ldap_intermediate LDAP_P(( Operation *op, SlapReply *rs ));
+LDAP_SLAPD_F (void) slap_send_search_result LDAP_P(( Operation *op, SlapReply *rs ));
+LDAP_SLAPD_F (int) slap_send_search_reference LDAP_P(( Operation *op, SlapReply *rs ));
+LDAP_SLAPD_F (int) slap_send_search_entry LDAP_P(( Operation *op, SlapReply *rs ));
+LDAP_SLAPD_F (int) slap_null_cb LDAP_P(( Operation *op, SlapReply *rs ));
+LDAP_SLAPD_F (int) slap_freeself_cb LDAP_P(( Operation *op, SlapReply *rs ));
+
+LDAP_SLAPD_V( const struct berval ) slap_pre_read_bv;
+LDAP_SLAPD_V( const struct berval ) slap_post_read_bv;
+LDAP_SLAPD_F (int) slap_read_controls LDAP_P(( Operation *op, SlapReply *rs,
+ Entry *e, const struct berval *oid, LDAPControl **ctrl ));
+
+LDAP_SLAPD_F (int) str2result LDAP_P(( char *s,
+ int *code, char **matched, char **info ));
+LDAP_SLAPD_F (int) slap_map_api2result LDAP_P(( SlapReply *rs ));
+LDAP_SLAPD_F (slap_mask_t) slap_attr_flags LDAP_P(( AttributeName *an ));
+LDAP_SLAPD_F (ber_tag_t) slap_req2res LDAP_P(( ber_tag_t tag ));
+
+LDAP_SLAPD_V( const struct berval ) slap_dummy_bv;
+
+/*
+ * root_dse.c
+ */
+LDAP_SLAPD_F (int) root_dse_init LDAP_P(( void ));
+LDAP_SLAPD_F (int) root_dse_destroy LDAP_P(( void ));
+
+LDAP_SLAPD_F (int) root_dse_info LDAP_P((
+ Connection *conn,
+ Entry **e,
+ const char **text ));
+
+LDAP_SLAPD_F (int) root_dse_read_file LDAP_P((
+ const char *file));
+
+LDAP_SLAPD_F (int) slap_discover_feature LDAP_P((
+ slap_bindconf *sb,
+ const char *attr,
+ const char *val ));
+
+LDAP_SLAPD_F (int) supported_feature_load LDAP_P(( struct berval *f ));
+LDAP_SLAPD_F (int) supported_feature_destroy LDAP_P(( void ));
+
+LDAP_SLAPD_F (int) entry_info_register LDAP_P(( SLAP_ENTRY_INFO_FN func, void *arg ));
+LDAP_SLAPD_F (int) entry_info_unregister LDAP_P(( SLAP_ENTRY_INFO_FN func, void *arg ));
+LDAP_SLAPD_F (void) entry_info_destroy LDAP_P(( void ));
+
+/*
+ * sasl.c
+ */
+LDAP_SLAPD_F (int) slap_sasl_init(void);
+LDAP_SLAPD_F (char *) slap_sasl_secprops( const char * );
+LDAP_SLAPD_F (void) slap_sasl_secprops_unparse( struct berval * );
+LDAP_SLAPD_F (int) slap_sasl_destroy(void);
+
+LDAP_SLAPD_F (int) slap_sasl_open( Connection *c, int reopen );
+LDAP_SLAPD_F (char **) slap_sasl_mechs( Connection *c );
+
+LDAP_SLAPD_F (int) slap_sasl_external( Connection *c,
+ slap_ssf_t ssf, /* relative strength of external security */
+ struct berval *authid ); /* asserted authentication id */
+
+LDAP_SLAPD_F (int) slap_sasl_cbinding( Connection *c, void *ssl );
+
+LDAP_SLAPD_F (int) slap_sasl_reset( Connection *c );
+LDAP_SLAPD_F (int) slap_sasl_close( Connection *c );
+
+LDAP_SLAPD_F (int) slap_sasl_bind LDAP_P(( Operation *op, SlapReply *rs ));
+
+LDAP_SLAPD_F (int) slap_sasl_setpass(
+ Operation *op,
+ SlapReply *rs );
+
+LDAP_SLAPD_F (int) slap_sasl_getdn( Connection *conn, Operation *op,
+ struct berval *id, char *user_realm, struct berval *dn, int flags );
+
+/*
+ * saslauthz.c
+ */
+LDAP_SLAPD_F (int) slap_parse_user LDAP_P((
+ struct berval *id, struct berval *user,
+ struct berval *realm, struct berval *mech ));
+LDAP_SLAPD_F (int) slap_sasl_matches LDAP_P((
+ Operation *op, BerVarray rules,
+ struct berval *assertDN, struct berval *authc ));
+LDAP_SLAPD_F (void) slap_sasl2dn LDAP_P((
+ Operation *op,
+ struct berval *saslname,
+ struct berval *dn,
+ int flags ));
+LDAP_SLAPD_F (int) slap_sasl_authorized LDAP_P((
+ Operation *op,
+ struct berval *authcid,
+ struct berval *authzid ));
+LDAP_SLAPD_F (int) slap_sasl_regexp_config LDAP_P((
+ const char *match, const char *replace, int valx ));
+LDAP_SLAPD_F (void) slap_sasl_regexp_unparse LDAP_P(( BerVarray *bva ));
+LDAP_SLAPD_F (int) slap_sasl_setpolicy LDAP_P(( const char * ));
+LDAP_SLAPD_F (const char *) slap_sasl_getpolicy LDAP_P(( void ));
+LDAP_SLAPD_F (int) slap_sasl_rewrite_config LDAP_P((
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv,
+ int valx ));
+LDAP_SLAPD_F (int) slap_sasl_rewrite_delete LDAP_P(( int valx ));
+LDAP_SLAPD_F (int) slap_sasl_rewrite_unparse LDAP_P(( BerVarray *bva ));
+LDAP_SLAPD_F (void) slap_sasl_regexp_destroy LDAP_P(( void ));
+LDAP_SLAPD_F (int) slap_sasl_regexp_delete LDAP_P(( int valx ));
+LDAP_SLAPD_F (int) authzValidate LDAP_P((
+ Syntax *syn, struct berval *in ));
+#if 0
+LDAP_SLAPD_F (int) authzMatch LDAP_P((
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue ));
+#endif
+LDAP_SLAPD_F (int) authzPretty LDAP_P((
+ Syntax *syntax,
+ struct berval *val,
+ struct berval *out,
+ void *ctx ));
+LDAP_SLAPD_F (int) authzNormalize LDAP_P((
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx ));
+
+/*
+ * schema.c
+ */
+LDAP_SLAPD_F (int) schema_info LDAP_P(( Entry **entry, const char **text ));
+
+/*
+ * schema_check.c
+ */
+LDAP_SLAPD_F( int ) oc_check_allowed(
+ AttributeType *type,
+ ObjectClass **socs,
+ ObjectClass *sc );
+
+LDAP_SLAPD_F( int ) structural_class(
+ BerVarray ocs,
+ ObjectClass **sc,
+ ObjectClass ***socs,
+ const char **text,
+ char *textbuf, size_t textlen, void *ctx );
+
+LDAP_SLAPD_F( int ) entry_schema_check(
+ Operation *op,
+ Entry *e,
+ Attribute *attrs,
+ int manage,
+ int add,
+ Attribute **socp,
+ const char** text,
+ char *textbuf, size_t textlen );
+
+LDAP_SLAPD_F( int ) mods_structural_class(
+ Modifications *mods,
+ struct berval *oc,
+ const char** text,
+ char *textbuf, size_t textlen, void *ctx );
+
+/*
+ * schema_init.c
+ */
+LDAP_SLAPD_V( int ) schema_init_done;
+LDAP_SLAPD_F (int) slap_schema_init LDAP_P((void));
+LDAP_SLAPD_F (void) schema_destroy LDAP_P(( void ));
+
+LDAP_SLAPD_F (int) slap_hash64 LDAP_P((int));
+
+LDAP_SLAPD_F( slap_mr_indexer_func ) octetStringIndexer;
+LDAP_SLAPD_F( slap_mr_filter_func ) octetStringFilter;
+
+LDAP_SLAPD_F( int ) numericoidValidate LDAP_P((
+ Syntax *syntax,
+ struct berval *in ));
+LDAP_SLAPD_F( int ) numericStringValidate LDAP_P((
+ Syntax *syntax,
+ struct berval *in ));
+LDAP_SLAPD_F( int ) octetStringMatch LDAP_P((
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue ));
+LDAP_SLAPD_F( int ) octetStringOrderingMatch LDAP_P((
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue ));
+
+/*
+ * schema_prep.c
+ */
+LDAP_SLAPD_V( struct slap_internal_schema ) slap_schema;
+LDAP_SLAPD_F (int) slap_schema_load LDAP_P((void));
+LDAP_SLAPD_F (int) slap_schema_check LDAP_P((void));
+
+/*
+ * schemaparse.c
+ */
+LDAP_SLAPD_F( int ) slap_valid_descr( const char * );
+
+LDAP_SLAPD_F (int) parse_cr LDAP_P((
+ struct config_args_s *ca, ContentRule **scr ));
+LDAP_SLAPD_F (int) parse_oc LDAP_P((
+ struct config_args_s *ca, ObjectClass **soc, ObjectClass *prev ));
+LDAP_SLAPD_F (int) parse_at LDAP_P((
+ struct config_args_s *ca, AttributeType **sat, AttributeType *prev ));
+LDAP_SLAPD_F (char *) scherr2str LDAP_P((int code)) LDAP_GCCATTR((const));
+LDAP_SLAPD_F (int) dscompare LDAP_P(( const char *s1, const char *s2del,
+ char delim ));
+LDAP_SLAPD_F (int) parse_syn LDAP_P((
+ struct config_args_s *ca, Syntax **sat, Syntax *prev ));
+
+/*
+ * sessionlog.c
+ */
+LDAP_SLAPD_F (int) slap_send_session_log LDAP_P((
+ Operation *, Operation *, SlapReply *));
+LDAP_SLAPD_F (int) slap_add_session_log LDAP_P((
+ Operation *, Operation *, Entry * ));
+
+/*
+ * sl_malloc.c
+ */
+LDAP_SLAPD_F (void *) slap_sl_malloc LDAP_P((
+ ber_len_t size, void *ctx ));
+LDAP_SLAPD_F (void *) slap_sl_realloc LDAP_P((
+ void *block, ber_len_t size, void *ctx ));
+LDAP_SLAPD_F (void *) slap_sl_calloc LDAP_P((
+ ber_len_t nelem, ber_len_t size, void *ctx ));
+LDAP_SLAPD_F (void) slap_sl_free LDAP_P((
+ void *, void *ctx ));
+LDAP_SLAPD_F (void) slap_sl_release LDAP_P((
+ void *, void *ctx ));
+LDAP_SLAPD_F (void *) slap_sl_mark LDAP_P((
+ void *ctx ));
+
+
+LDAP_SLAPD_V (BerMemoryFunctions) slap_sl_mfuncs;
+
+LDAP_SLAPD_F (void) slap_sl_mem_init LDAP_P(( void ));
+LDAP_SLAPD_F (void *) slap_sl_mem_create LDAP_P((
+ ber_len_t size, int stack, void *ctx, int flag ));
+LDAP_SLAPD_F (void) slap_sl_mem_setctx LDAP_P(( void *ctx, void *memctx ));
+LDAP_SLAPD_F (void) slap_sl_mem_destroy LDAP_P(( void *key, void *data ));
+LDAP_SLAPD_F (void *) slap_sl_context LDAP_P(( void *ptr ));
+
+/*
+ * starttls.c
+ */
+LDAP_SLAPD_F (SLAP_EXTOP_MAIN_FN) starttls_extop;
+
+/*
+ * str2filter.c
+ */
+LDAP_SLAPD_F (Filter *) str2filter LDAP_P(( const char *str ));
+LDAP_SLAPD_F (Filter *) str2filter_x LDAP_P(( Operation *op, const char *str ));
+
+/*
+ * syncrepl.c
+ */
+
+LDAP_SLAPD_F (int) syncrepl_add_glue LDAP_P((
+ Operation*, Entry* ));
+LDAP_SLAPD_F (void) syncrepl_diff_entry LDAP_P((
+ Operation *op, Attribute *old, Attribute *anew,
+ Modifications **mods, Modifications **ml, int is_ctx ));
+LDAP_SLAPD_F (void) syncinfo_free LDAP_P(( struct syncinfo_s *, int all ));
+
+/* syntax.c */
+LDAP_SLAPD_F (int) syn_is_sup LDAP_P((
+ Syntax *syn,
+ Syntax *sup ));
+LDAP_SLAPD_F (Syntax *) syn_find LDAP_P((
+ const char *synname ));
+LDAP_SLAPD_F (Syntax *) syn_find_desc LDAP_P((
+ const char *syndesc, int *slen ));
+LDAP_SLAPD_F (int) syn_add LDAP_P((
+ LDAPSyntax *syn,
+ int user,
+ slap_syntax_defs_rec *def,
+ Syntax **ssyn,
+ Syntax *prev,
+ const char **err ));
+LDAP_SLAPD_F (void) syn_destroy LDAP_P(( void ));
+
+LDAP_SLAPD_F (int) register_syntax LDAP_P((
+ slap_syntax_defs_rec *def ));
+
+LDAP_SLAPD_F (int) syn_schema_info( Entry *e );
+
+LDAP_SLAPD_F (int) syn_start LDAP_P(( Syntax **at ));
+LDAP_SLAPD_F (int) syn_next LDAP_P(( Syntax **at ));
+LDAP_SLAPD_F (void) syn_delete LDAP_P(( Syntax *at ));
+
+LDAP_SLAPD_F (void) syn_unparse LDAP_P((
+ BerVarray *bva, Syntax *start, Syntax *end, int system ));
+
+/*
+ * user.c
+ */
+#if defined(HAVE_PWD_H) && defined(HAVE_GRP_H)
+LDAP_SLAPD_F (void) slap_init_user LDAP_P(( char *username, char *groupname ));
+#endif
+
+/*
+ * value.c
+ */
+LDAP_SLAPD_F (int) asserted_value_validate_normalize LDAP_P((
+ AttributeDescription *ad,
+ MatchingRule *mr,
+ unsigned usage,
+ struct berval *in,
+ struct berval *out,
+ const char ** text,
+ void *ctx ));
+
+LDAP_SLAPD_F (int) value_match LDAP_P((
+ int *match,
+ AttributeDescription *ad,
+ MatchingRule *mr,
+ unsigned flags,
+ struct berval *v1,
+ void *v2,
+ const char ** text ));
+LDAP_SLAPD_F (int) value_find_ex LDAP_P((
+ AttributeDescription *ad,
+ unsigned flags,
+ BerVarray values,
+ struct berval *value,
+ void *ctx ));
+
+LDAP_SLAPD_F (int) ordered_value_add LDAP_P((
+ Entry *e,
+ AttributeDescription *ad,
+ Attribute *a,
+ BerVarray vals,
+ BerVarray nvals ));
+
+LDAP_SLAPD_F (int) ordered_value_validate LDAP_P((
+ AttributeDescription *ad,
+ struct berval *in,
+ int mop ));
+
+LDAP_SLAPD_F (int) ordered_value_pretty LDAP_P((
+ AttributeDescription *ad,
+ struct berval *val,
+ struct berval *out,
+ void *ctx ));
+
+LDAP_SLAPD_F (int) ordered_value_normalize LDAP_P((
+ slap_mask_t usage,
+ AttributeDescription *ad,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx ));
+
+LDAP_SLAPD_F (int) ordered_value_match LDAP_P((
+ int *match,
+ AttributeDescription *ad,
+ MatchingRule *mr,
+ unsigned flags,
+ struct berval *v1,
+ struct berval *v2,
+ const char ** text ));
+
+LDAP_SLAPD_F (void) ordered_value_renumber LDAP_P((
+ Attribute *a ));
+
+LDAP_SLAPD_F (int) ordered_value_sort LDAP_P((
+ Attribute *a,
+ int do_renumber ));
+
+LDAP_SLAPD_F (int) value_add LDAP_P((
+ BerVarray *vals,
+ BerVarray addvals ));
+LDAP_SLAPD_F (int) value_add_one LDAP_P((
+ BerVarray *vals,
+ struct berval *addval ));
+
+/* assumes (x) > (y) returns 1 if true, 0 otherwise */
+#define SLAP_PTRCMP(x, y) ((x) < (y) ? -1 : (x) > (y))
+
+#ifdef SLAP_ZONE_ALLOC
+/*
+ * zn_malloc.c
+ */
+LDAP_SLAPD_F (void *) slap_zn_malloc LDAP_P((ber_len_t, void *));
+LDAP_SLAPD_F (void *) slap_zn_realloc LDAP_P((void *, ber_len_t, void *));
+LDAP_SLAPD_F (void *) slap_zn_calloc LDAP_P((ber_len_t, ber_len_t, void *));
+LDAP_SLAPD_F (void) slap_zn_free LDAP_P((void *, void *));
+
+LDAP_SLAPD_F (void *) slap_zn_mem_create LDAP_P((
+ ber_len_t, ber_len_t, ber_len_t, ber_len_t));
+LDAP_SLAPD_F (void) slap_zn_mem_destroy LDAP_P((void *));
+LDAP_SLAPD_F (int) slap_zn_validate LDAP_P((void *, void *, int));
+LDAP_SLAPD_F (int) slap_zn_invalidate LDAP_P((void *, void *));
+LDAP_SLAPD_F (int) slap_zh_rlock LDAP_P((void*));
+LDAP_SLAPD_F (int) slap_zh_runlock LDAP_P((void*));
+LDAP_SLAPD_F (int) slap_zh_wlock LDAP_P((void*));
+LDAP_SLAPD_F (int) slap_zh_wunlock LDAP_P((void*));
+LDAP_SLAPD_F (int) slap_zn_rlock LDAP_P((void*, void*));
+LDAP_SLAPD_F (int) slap_zn_runlock LDAP_P((void*, void*));
+LDAP_SLAPD_F (int) slap_zn_wlock LDAP_P((void*, void*));
+LDAP_SLAPD_F (int) slap_zn_wunlock LDAP_P((void*, void*));
+#endif
+
+/*
+ * Other...
+ */
+LDAP_SLAPD_V (unsigned int) index_substr_if_minlen;
+LDAP_SLAPD_V (unsigned int) index_substr_if_maxlen;
+LDAP_SLAPD_V (unsigned int) index_substr_any_len;
+LDAP_SLAPD_V (unsigned int) index_substr_any_step;
+LDAP_SLAPD_V (unsigned int) index_intlen;
+/* all signed integers from strings of this size need more than intlen bytes */
+/* i.e. log(10)*(index_intlen_strlen-2) > log(2)*(8*(index_intlen)-1) */
+LDAP_SLAPD_V (unsigned int) index_intlen_strlen;
+#define SLAP_INDEX_INTLEN_STRLEN(intlen) ((8*(intlen)-1) * 146/485 + 3)
+
+LDAP_SLAPD_V (ber_len_t) sockbuf_max_incoming;
+LDAP_SLAPD_V (ber_len_t) sockbuf_max_incoming_auth;
+LDAP_SLAPD_V (int) slap_conn_max_pending;
+LDAP_SLAPD_V (int) slap_conn_max_pending_auth;
+LDAP_SLAPD_V (int) slap_max_filter_depth;
+
+LDAP_SLAPD_V (slap_mask_t) global_allows;
+LDAP_SLAPD_V (slap_mask_t) global_disallows;
+
+LDAP_SLAPD_V (BerVarray) default_referral;
+LDAP_SLAPD_V (const char) Versionstr[];
+
+LDAP_SLAPD_V (int) global_gentlehup;
+LDAP_SLAPD_V (int) global_idletimeout;
+LDAP_SLAPD_V (int) global_writetimeout;
+LDAP_SLAPD_V (char *) global_host;
+LDAP_SLAPD_V (struct berval) global_host_bv;
+LDAP_SLAPD_V (char *) global_realm;
+LDAP_SLAPD_V (char *) sasl_host;
+LDAP_SLAPD_V (char *) sasl_cbinding;
+LDAP_SLAPD_V (char *) slap_sasl_auxprops;
+#ifdef SLAP_AUXPROP_DONTUSECOPY
+LDAP_SLAPD_V (int) slap_dontUseCopy_ignore;
+LDAP_SLAPD_V (BerVarray) slap_dontUseCopy_propnames;
+#endif /* SLAP_AUXPROP_DONTUSECOPY */
+LDAP_SLAPD_V (char **) default_passwd_hash;
+LDAP_SLAPD_V (int) lber_debug;
+LDAP_SLAPD_V (int) ldap_syslog;
+LDAP_SLAPD_V (struct berval) default_search_base;
+LDAP_SLAPD_V (struct berval) default_search_nbase;
+
+LDAP_SLAPD_V (slap_counters_t) slap_counters;
+
+LDAP_SLAPD_V (char *) slapd_pid_file;
+LDAP_SLAPD_V (char *) slapd_args_file;
+LDAP_SLAPD_V (time_t) starttime;
+
+/* use time(3) -- no mutex */
+#define slap_get_time() time( NULL )
+
+LDAP_SLAPD_V (ldap_pvt_thread_pool_t) connection_pool;
+LDAP_SLAPD_V (int) connection_pool_max;
+LDAP_SLAPD_V (int) connection_pool_queues;
+LDAP_SLAPD_V (int) slap_tool_thread_max;
+
+LDAP_SLAPD_V (ldap_pvt_thread_mutex_t) entry2str_mutex;
+
+LDAP_SLAPD_V (ldap_pvt_thread_mutex_t) ad_index_mutex;
+LDAP_SLAPD_V (ldap_pvt_thread_mutex_t) ad_undef_mutex;
+LDAP_SLAPD_V (ldap_pvt_thread_mutex_t) oc_undef_mutex;
+
+LDAP_SLAPD_V (ber_socket_t) dtblsize;
+
+LDAP_SLAPD_V (int) use_reverse_lookup;
+
+/*
+ * operations
+ */
+LDAP_SLAPD_F (int) do_abandon LDAP_P((Operation *op, SlapReply *rs));
+LDAP_SLAPD_F (int) do_add LDAP_P((Operation *op, SlapReply *rs));
+LDAP_SLAPD_F (int) do_bind LDAP_P((Operation *op, SlapReply *rs));
+LDAP_SLAPD_F (int) do_compare LDAP_P((Operation *op, SlapReply *rs));
+LDAP_SLAPD_F (int) do_delete LDAP_P((Operation *op, SlapReply *rs));
+LDAP_SLAPD_F (int) do_modify LDAP_P((Operation *op, SlapReply *rs));
+LDAP_SLAPD_F (int) do_modrdn LDAP_P((Operation *op, SlapReply *rs));
+LDAP_SLAPD_F (int) do_search LDAP_P((Operation *op, SlapReply *rs));
+LDAP_SLAPD_F (int) do_unbind LDAP_P((Operation *op, SlapReply *rs));
+LDAP_SLAPD_F (int) do_extended LDAP_P((Operation *op, SlapReply *rs));
+
+/*
+ * frontend operations
+ */
+LDAP_SLAPD_F (int) fe_op_abandon LDAP_P((Operation *op, SlapReply *rs));
+LDAP_SLAPD_F (int) fe_op_add LDAP_P((Operation *op, SlapReply *rs));
+LDAP_SLAPD_F (int) fe_op_bind LDAP_P((Operation *op, SlapReply *rs));
+LDAP_SLAPD_F (int) fe_op_bind_success LDAP_P(( Operation *op, SlapReply *rs ));
+LDAP_SLAPD_F (int) fe_op_compare LDAP_P((Operation *op, SlapReply *rs));
+LDAP_SLAPD_F (int) fe_op_delete LDAP_P((Operation *op, SlapReply *rs));
+LDAP_SLAPD_F (int) fe_op_modify LDAP_P((Operation *op, SlapReply *rs));
+LDAP_SLAPD_F (int) fe_op_modrdn LDAP_P((Operation *op, SlapReply *rs));
+LDAP_SLAPD_F (int) fe_op_search LDAP_P((Operation *op, SlapReply *rs));
+LDAP_SLAPD_F (int) fe_aux_operational LDAP_P((Operation *op, SlapReply *rs));
+#if 0
+LDAP_SLAPD_F (int) fe_op_unbind LDAP_P((Operation *op, SlapReply *rs));
+#endif
+LDAP_SLAPD_F (int) fe_extended LDAP_P((Operation *op, SlapReply *rs));
+LDAP_SLAPD_F (int) fe_acl_group LDAP_P((
+ Operation *op,
+ Entry *target,
+ struct berval *gr_ndn,
+ struct berval *op_ndn,
+ ObjectClass *group_oc,
+ AttributeDescription *group_at ));
+LDAP_SLAPD_F (int) fe_acl_attribute LDAP_P((
+ Operation *op,
+ Entry *target,
+ struct berval *edn,
+ AttributeDescription *entry_at,
+ BerVarray *vals,
+ slap_access_t access ));
+LDAP_SLAPD_F (int) fe_access_allowed LDAP_P((
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ slap_access_t access,
+ AccessControlState *state,
+ slap_mask_t *maskp ));
+
+/* NOTE: this macro assumes that bv has been allocated
+ * by ber_* malloc functions or is { 0L, NULL } */
+#ifdef USE_MP_BIGNUM
+# define UI2BVX(bv,ui,ctx) \
+ do { \
+ char *val; \
+ ber_len_t len; \
+ val = BN_bn2dec(ui); \
+ if (val) { \
+ len = strlen(val); \
+ if ( len > (bv)->bv_len ) { \
+ (bv)->bv_val = ber_memrealloc_x( (bv)->bv_val, len + 1, (ctx) ); \
+ } \
+ AC_MEMCPY((bv)->bv_val, val, len + 1); \
+ (bv)->bv_len = len; \
+ OPENSSL_free(val); \
+ } else { \
+ ber_memfree_x( (bv)->bv_val, (ctx) ); \
+ BER_BVZERO( (bv) ); \
+ } \
+ } while ( 0 )
+
+#elif defined( USE_MP_GMP )
+/* NOTE: according to the documentation, the result
+ * of mpz_sizeinbase() can exceed the length of the
+ * string representation of the number by 1
+ */
+# define UI2BVX(bv,ui,ctx) \
+ do { \
+ ber_len_t len = mpz_sizeinbase( (ui), 10 ); \
+ if ( len > (bv)->bv_len ) { \
+ (bv)->bv_val = ber_memrealloc_x( (bv)->bv_val, len + 1, (ctx) ); \
+ } \
+ (void)mpz_get_str( (bv)->bv_val, 10, (ui) ); \
+ if ( (bv)->bv_val[ len - 1 ] == '\0' ) { \
+ len--; \
+ } \
+ (bv)->bv_len = len; \
+ } while ( 0 )
+
+#else
+# ifdef USE_MP_LONG_LONG
+# define UI2BV_FORMAT "%llu"
+# elif defined USE_MP_LONG
+# define UI2BV_FORMAT "%lu"
+# elif defined HAVE_LONG_LONG
+# define UI2BV_FORMAT "%llu"
+# else
+# define UI2BV_FORMAT "%lu"
+# endif
+
+# define UI2BVX(bv,ui,ctx) \
+ do { \
+ char buf[LDAP_PVT_INTTYPE_CHARS(long)]; \
+ ber_len_t len; \
+ len = snprintf( buf, sizeof( buf ), UI2BV_FORMAT, (ui) ); \
+ if ( len > (bv)->bv_len ) { \
+ (bv)->bv_val = ber_memrealloc_x( (bv)->bv_val, len + 1, (ctx) ); \
+ } \
+ (bv)->bv_len = len; \
+ AC_MEMCPY( (bv)->bv_val, buf, len + 1 ); \
+ } while ( 0 )
+#endif
+
+#define UI2BV(bv,ui) UI2BVX(bv,ui,NULL)
+
+LDAP_END_DECL
+
+#endif /* PROTO_SLAP_H */
diff --git a/servers/slapd/proxyp.c b/servers/slapd/proxyp.c
new file mode 100644
index 0000000..c548bca
--- /dev/null
+++ b/servers/slapd/proxyp.c
@@ -0,0 +1,226 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+#include "slap.h"
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#include <lber_types.h>
+#include <ac/string.h>
+#include <ac/errno.h>
+
+typedef struct {
+ uint8_t sig[12]; /* hex 0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a */
+ uint8_t ver_cmd; /* protocol version and command */
+ uint8_t fam; /* protocol family and address */
+ uint16_t len; /* length of address data */
+} proxyp_header;
+
+typedef union {
+ struct { /* for TCP/UDP over IPv4, len = 12 */
+ uint32_t src_addr;
+ uint32_t dst_addr;
+ uint16_t src_port;
+ uint16_t dst_port;
+ } ip4;
+ struct { /* for TCP/UDP over IPv6, len = 36 */
+ uint8_t src_addr[16];
+ uint8_t dst_addr[16];
+ uint16_t src_port;
+ uint16_t dst_port;
+ } ip6;
+ struct { /* for AF_UNIX sockets, len = 216 */
+ uint8_t src_addr[108];
+ uint8_t dst_addr[108];
+ } unx;
+} proxyp_addr;
+
+static const uint8_t proxyp_sig[12] = {
+ 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, 0x0a,
+};
+
+int
+proxyp( ber_socket_t sfd, Sockaddr *from ) {
+ proxyp_header pph;
+ proxyp_addr ppa;
+ char peername[LDAP_IPADDRLEN];
+ struct berval peerbv = BER_BVC(peername);
+ /* Maximum size of header minus static component size is max option size */
+ uint8_t proxyp_options[536 - 16];
+ int pph_len;
+ int ret;
+
+ peername[0] = '\0';
+
+ do {
+ ret = tcp_read( SLAP_FD2SOCK( sfd ), &pph, sizeof(pph) );
+ } while ( ret == -1 && errno == EINTR );
+
+ if ( ret == -1 ) {
+ char ebuf[128];
+ int save_errno = errno;
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "header read failed %d (%s)\n",
+ (long)sfd, save_errno,
+ AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+ return 0;
+ } else if ( ret != sizeof(pph) ) {
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "header read insufficient data %d\n",
+ (long)sfd, ret );
+ return 0;
+ }
+
+ if ( memcmp( pph.sig, proxyp_sig, 12 ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "invalid header signature\n", (long)sfd );
+ return 0;
+ }
+
+ if ( ( pph.ver_cmd & 0xF0 ) != 0x20 ) {
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "invalid header version %x\n",
+ (long)sfd, pph.ver_cmd & 0xF0 );
+ return 0;
+ }
+
+ pph_len = ntohs( pph.len );
+ if ( ( pph.ver_cmd & 0x0F ) == 0x01 ) { /* PROXY command */
+ int addr_len;
+ switch ( pph.fam ) {
+ case 0x11: /* TCPv4 */
+ addr_len = sizeof( ppa.ip4 );
+ break;
+ case 0x21: /* TCPv6 */
+ addr_len = sizeof( ppa.ip6 );
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "unsupported protocol %x\n",
+ (long)sfd, pph.fam );
+ return 0;
+ }
+
+ if ( pph_len < addr_len ) {
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "address length %d too small, expecting %d\n",
+ (long)sfd, pph_len, addr_len );
+ return 0;
+ }
+
+ do {
+ ret = tcp_read( SLAP_FD2SOCK (sfd), &ppa, addr_len );
+ } while ( ret == -1 && errno == EINTR );
+
+ if ( ret == -1 ) {
+ char ebuf[128];
+ int save_errno = errno;
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "address read failed %d (%s)\n",
+ (long)sfd, save_errno,
+ AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+ return 0;
+ } else if ( ret != addr_len ) {
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "address read insufficient data, expecting %d, read %d\n",
+ (long)sfd, addr_len, ret );
+ return 0;
+ }
+
+ pph_len -= addr_len;
+ }
+
+ switch ( pph.ver_cmd & 0x0F ) {
+ case 0x01: /* PROXY command */
+ switch ( pph.fam ) {
+ case 0x11: /* TCPv4 */
+ ldap_pvt_sockaddrstr( from, &peerbv );
+ Debug( LDAP_DEBUG_STATS, "proxyp(%ld): via %s\n",
+ (long)sfd, peername );
+
+ from->sa_in_addr.sin_family = AF_INET;
+ from->sa_in_addr.sin_addr.s_addr = ppa.ip4.src_addr;
+ from->sa_in_addr.sin_port = ppa.ip4.src_port;
+ break;
+
+ case 0x21: /* TCPv6 */
+#ifdef LDAP_PF_INET6
+ ldap_pvt_sockaddrstr( from, &peerbv );
+ Debug( LDAP_DEBUG_STATS, "proxyp(%ld): via %s\n",
+ (long)sfd, peername );
+ from->sa_in6_addr.sin6_family = AF_INET6;
+ memcpy( &from->sa_in6_addr.sin6_addr, ppa.ip6.src_addr,
+ sizeof(ppa.ip6.src_addr) );
+ from->sa_in6_addr.sin6_port = ppa.ip6.src_port;
+#else
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "IPv6 proxied addresses disabled\n",
+ (long)sfd );
+ return 0;
+#endif
+ break;
+ }
+
+ break;
+
+ case 0x00: /* LOCAL command */
+ Debug( LDAP_DEBUG_CONNS, "proxyp(%ld): "
+ "local connection, ignoring proxy data\n",
+ (long)sfd );
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): invalid command %x\n",
+ (long)sfd, pph.ver_cmd & 0x0F );
+ return 0;
+ }
+
+ /* Clear out any options left in proxy packet */
+ if ( pph_len > 0 ) {
+ if (pph_len > sizeof( proxyp_options ) ) {
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "options size %d too big\n",
+ (long)sfd, pph_len );
+ return 0;
+ }
+
+ do {
+ ret = tcp_read( SLAP_FD2SOCK (sfd), &proxyp_options, pph_len );
+ } while ( ret == -1 && errno == EINTR );
+
+ if ( ret == -1 ) {
+ char ebuf[128];
+ int save_errno = errno;
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "options read failed %d (%s)\n",
+ (long)sfd, save_errno,
+ AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+ return 0;
+ } else if ( ret != pph_len ) {
+ Debug( LDAP_DEBUG_ANY, "proxyp(%ld): "
+ "options read insufficient data, expecting %d, read %d\n",
+ (long)sfd, pph_len, ret );
+ return 0;
+ }
+ }
+
+ return 1;
+}
diff --git a/servers/slapd/pwmods/Makefile.in b/servers/slapd/pwmods/Makefile.in
new file mode 100644
index 0000000..e8d76b8
--- /dev/null
+++ b/servers/slapd/pwmods/Makefile.in
@@ -0,0 +1,59 @@
+# Makefile.in for overlays
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2003-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SRCS = argon2.c
+
+LTONLY_MOD = $(LTONLY_mod)
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+MOD_DEFS = -DSLAPD_IMPORT
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+
+LIBRARY = dummyvalue
+PROGRAMS = @SLAPD_DYNAMIC_PWMODS@
+
+XINCPATH = -I.. -I$(srcdir)/..
+XDEFS = $(MODULES_CPPFLAGS)
+
+dynamic: $(PROGRAMS)
+
+argon2.la : argon2.lo version.lo
+ $(LTLINK_MOD) -module -o $@ argon2.lo version.lo $(ARGON2_LIBS) $(LINK_LIBS) $(MODULES_LIBS)
+
+install-local: $(PROGRAMS)
+ @if test -n "$?" ; then \
+ $(MKDIR) $(DESTDIR)$(moduledir); \
+ $(LTINSTALL) $(INSTALLFLAGS) -m 755 $? $(DESTDIR)$(moduledir);\
+ fi
+
+MKDEPFLAG = -l
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LTCOMPILE_MOD) $<
+
+# Must fixup depends for non-libtool objects
+depend-local: depend-common
+ @if test -n "$(OBJS)"; then \
+ OBJ2=`echo $(OBJS) $(OBJDEP) | $(SED) -e 's/\.o//g'`; \
+ SCR=''; for i in $$OBJ2; do SCR="$$SCR -e s/^$$i.lo:/$$i.o:/"; done; \
+ mv Makefile Makefile.bak; $(SED) $$SCR Makefile.bak > Makefile && \
+ $(RM) Makefile.bak; fi
+
diff --git a/servers/slapd/pwmods/README.argon2 b/servers/slapd/pwmods/README.argon2
new file mode 100644
index 0000000..595c50d
--- /dev/null
+++ b/servers/slapd/pwmods/README.argon2
@@ -0,0 +1,109 @@
+Argon2 OpenLDAP support
+----------------------
+
+argon2.c provides support for ARGON2 hashed passwords in OpenLDAP. For
+instance, one could have the LDAP attribute:
+
+userPassword: {ARGON2}$argon2i$v=19$m=4096,t=3,p=1$c2FsdHNhbHQ$DKlexoEJUoZTmkAAC3SaMWk30El9/RvVhlqGo6afIng
+
+or:
+
+userPassword: {ARGON2}$argon2i$v=19$m=4096,t=3,p=1$c2FsdHNhbHRzYWx0$qOCkx9nMeFlaGOO4DUmPDgrlUbgMMuO9T1+vQCFuyzw
+
+Both hash the password "secret", the first using the salt "saltsalt", the second using the salt "saltsaltsalt"
+
+Building
+--------
+
+1) Customize the OPENLDAP variable in Makefile to point to the OpenLDAP
+source root.
+
+For initial testing you might also want to edit DEFS to define
+SLAPD_ARGON2_DEBUG, which enables logging to stderr (don't leave this on
+in production, as it prints passwords in cleartext).
+
+2) Run 'make' to produce argon2.so
+
+3) Copy argon2.so somewhere permanent.
+
+4) Edit your slapd.conf (eg. /etc/ldap/slapd.conf), and add:
+
+moduleload ...path/to/argon2.so
+
+5) Restart slapd.
+
+
+Configuring
+-----------
+
+The {ARGON2} password scheme should now be recognised.
+
+You can also tell OpenLDAP to use one of this scheme when processing LDAP
+Password Modify Extended Operations, thanks to the password-hash option in
+slapd.conf:
+
+password-hash {ARGON2}
+
+
+Testing
+-------
+
+A quick way to test whether it's working is to customize the rootdn and
+rootpw in slapd.conf, eg:
+
+rootdn "cn=admin,dc=example,dc=com"
+
+# This hashes the string 'secret', with a random salt
+rootpw {ARGON2}$argon2i$v=19$m=4096,t=3,p=1$uJyf0UfB25SQTfX7oCyK2w$U45DJqEFwD0yFaLvTVyACHLvGMwzNGf19dvzPR8XvGc
+
+
+Then to test, run something like:
+
+ldapsearch -b "dc=example,dc=com" -D "cn=admin,dc=example,dc=com" -x -w secret
+
+
+-- Test hashes:
+
+Test hashes can be generated with argon2:
+$ echo -n "secret" | argon2 "saltsalt" -e
+$argon2i$v=19$m=4096,t=3,p=1$c2FsdHNhbHQ$DKlexoEJUoZTmkAAC3SaMWk30El9/RvVhlqGo6afIng
+
+$ echo -n "secret" | argon2 "saltsaltsalt" -e
+$argon2i$v=19$m=4096,t=3,p=1$c2FsdHNhbHRzYWx0$qOCkx9nMeFlaGOO4DUmPDgrlUbgMMuO9T1+vQCFuyzw
+
+$ echo -n "secretsecret" | argon2 "saltsalt" -e
+$argon2i$v=19$m=4096,t=3,p=1$c2FsdHNhbHQ$U0Pd/wEsssZ9bHezDA8oxHnWe01xftykEy+7ehM2vic
+
+$ echo -n "secretsecret" | argon2 "saltsaltsalt" -e
+$argon2i$v=19$m=4096,t=3,p=1$c2FsdHNhbHRzYWx0$fkvoOwKgVtlX9ZDqcHFyyArBvqnAM0Igca8SScB4Jsc
+
+
+
+Alternatively we could modify an existing user's password with
+ldappasswd, and then test binding as that user:
+
+$ ldappasswd -D "cn=admin,dc=example,dc=com" -x -W -S uid=jturner,ou=People,dc=example,dc=com
+New password: secret
+Re-enter new password: secret
+Enter LDAP Password: <cn=admin's password>
+
+$ ldapsearch -b "dc=example,dc=com" -D "uid=jturner,ou=People,dc=example,dc=com" -x -w secret
+
+
+
+---
+
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2017-2022 The OpenLDAP Foundation.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License.
+
+A copy of this license is available in the file LICENSE in the
+top-level directory of the distribution or, alternatively, at
+<http://www.OpenLDAP.org/license.html>.
+
+---
diff --git a/servers/slapd/pwmods/argon2.c b/servers/slapd/pwmods/argon2.c
new file mode 100644
index 0000000..f3f7b4c
--- /dev/null
+++ b/servers/slapd/pwmods/argon2.c
@@ -0,0 +1,240 @@
+/* argon2.c - Password module for argon2 */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2017-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+#ifdef SLAPD_PWMOD_PW_ARGON2
+#include "ac/string.h"
+#include "lber_pvt.h"
+#include "lutil.h"
+
+#include "slap.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#ifdef HAVE_LIBARGON2
+#include <argon2.h>
+
+/*
+ * For now, we hardcode the default values from the argon2 command line tool
+ * (as of argon2 release 20161029)
+ */
+#define SLAPD_ARGON2_ITERATIONS 3
+#define SLAPD_ARGON2_MEMORY (1 << 12)
+#define SLAPD_ARGON2_PARALLELISM 1
+#define SLAPD_ARGON2_SALT_LENGTH 16
+#define SLAPD_ARGON2_HASH_LENGTH 32
+
+#else /* !HAVE_LIBARGON2 */
+#include <sodium.h>
+
+/*
+ * Or libsodium interactive settings
+ */
+#define SLAPD_ARGON2_ITERATIONS crypto_pwhash_argon2id_OPSLIMIT_INTERACTIVE
+#define SLAPD_ARGON2_MEMORY (crypto_pwhash_argon2id_MEMLIMIT_INTERACTIVE / 1024)
+#define SLAPD_ARGON2_PARALLELISM 1
+#define SLAPD_ARGON2_SALT_LENGTH crypto_pwhash_argon2id_SALTBYTES
+#define SLAPD_ARGON2_HASH_LENGTH 32
+
+#endif
+
+static unsigned long iterations = SLAPD_ARGON2_ITERATIONS;
+static unsigned long memory = SLAPD_ARGON2_MEMORY;
+static unsigned long parallelism = SLAPD_ARGON2_PARALLELISM;
+
+const struct berval slapd_argon2_scheme = BER_BVC("{ARGON2}");
+
+static int
+slapd_argon2_hash(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+
+ /*
+ * Duplicate these values here so future code which allows
+ * configuration has an easier time.
+ */
+ uint32_t salt_length, hash_length;
+ char *p;
+ int rc = LUTIL_PASSWD_ERR;
+
+#ifdef HAVE_LIBARGON2
+ struct berval salt;
+ size_t encoded_length;
+
+ salt_length = SLAPD_ARGON2_SALT_LENGTH;
+ hash_length = SLAPD_ARGON2_HASH_LENGTH;
+
+ encoded_length = argon2_encodedlen( iterations, memory, parallelism,
+ salt_length, hash_length, Argon2_id );
+
+ salt.bv_len = salt_length;
+ salt.bv_val = ber_memalloc( salt.bv_len );
+
+ if ( salt.bv_val == NULL ) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ if ( lutil_entropy( (unsigned char*)salt.bv_val, salt.bv_len ) ) {
+ ber_memfree( salt.bv_val );
+ return LUTIL_PASSWD_ERR;
+ }
+
+ p = hash->bv_val = ber_memalloc( scheme->bv_len + encoded_length );
+ if ( p == NULL ) {
+ ber_memfree( salt.bv_val );
+ return LUTIL_PASSWD_ERR;
+ }
+
+ AC_MEMCPY( p, scheme->bv_val, scheme->bv_len );
+ p += scheme->bv_len;
+
+ /*
+ * Do the actual heavy lifting
+ */
+ if ( argon2i_hash_encoded( iterations, memory, parallelism,
+ passwd->bv_val, passwd->bv_len,
+ salt.bv_val, salt_length, hash_length,
+ p, encoded_length ) == 0 ) {
+ rc = LUTIL_PASSWD_OK;
+ }
+ hash->bv_len = scheme->bv_len + encoded_length;
+ ber_memfree( salt.bv_val );
+
+#else /* !HAVE_LIBARGON2 */
+ /* Not exposed by libsodium
+ salt_length = SLAPD_ARGON2_SALT_LENGTH;
+ hash_length = SLAPD_ARGON2_HASH_LENGTH;
+ */
+
+ p = hash->bv_val = ber_memalloc( scheme->bv_len + crypto_pwhash_STRBYTES );
+ if ( p == NULL ) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ AC_MEMCPY( hash->bv_val, scheme->bv_val, scheme->bv_len );
+ p += scheme->bv_len;
+
+ if ( crypto_pwhash_str_alg( p, passwd->bv_val, passwd->bv_len,
+ iterations, memory * 1024,
+ crypto_pwhash_ALG_ARGON2ID13 ) == 0 ) {
+ hash->bv_len = strlen( hash->bv_val );
+ rc = LUTIL_PASSWD_OK;
+ }
+#endif
+
+ if ( rc ) {
+ ber_memfree( hash->bv_val );
+ return LUTIL_PASSWD_ERR;
+ }
+
+ return LUTIL_PASSWD_OK;
+}
+
+static int
+slapd_argon2_verify(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text )
+{
+ int rc = LUTIL_PASSWD_ERR;
+
+#ifdef HAVE_LIBARGON2
+ if ( strncmp( passwd->bv_val, "$argon2i$", STRLENOF("$argon2i$") ) == 0 ) {
+ rc = argon2i_verify( passwd->bv_val, cred->bv_val, cred->bv_len );
+ } else if ( strncmp( passwd->bv_val, "$argon2d$", STRLENOF("$argon2d$") ) == 0 ) {
+ rc = argon2d_verify( passwd->bv_val, cred->bv_val, cred->bv_len );
+ } else if ( strncmp( passwd->bv_val, "$argon2id$", STRLENOF("$argon2id$") ) == 0 ) {
+ rc = argon2id_verify( passwd->bv_val, cred->bv_val, cred->bv_len );
+ }
+#else /* !HAVE_LIBARGON2 */
+ rc = crypto_pwhash_str_verify( passwd->bv_val, cred->bv_val, cred->bv_len );
+#endif
+
+ if ( rc ) {
+ return LUTIL_PASSWD_ERR;
+ }
+ return LUTIL_PASSWD_OK;
+}
+
+int init_module( int argc, char *argv[] )
+{
+ int i;
+
+#ifdef HAVE_LIBSODIUM
+ if ( sodium_init() == -1 ) {
+ return -1;
+ }
+#endif
+
+ for ( i=0; i < argc; i++ ) {
+ char *p;
+ unsigned long value;
+
+ switch ( *argv[i] ) {
+ case 'm':
+ p = strchr( argv[i], '=' );
+ if ( !p || lutil_atoulx( &value, p+1, 0 ) ) {
+ return -1;
+ }
+ memory = value;
+ break;
+
+ case 't':
+ p = strchr( argv[i], '=' );
+ if ( !p || lutil_atoulx( &value, p+1, 0 ) ) {
+ return -1;
+ }
+ iterations = value;
+ break;
+
+ case 'p':
+ p = strchr( argv[i], '=' );
+ if ( !p || lutil_atoulx( &value, p+1, 0 ) ) {
+ return -1;
+ }
+ parallelism = value;
+ break;
+
+ default:
+ return -1;
+ }
+ }
+
+#ifndef HAVE_LIBARGON2
+ /* At the moment, we can only use libargon2 to set parallelism for new
+ * hashes */
+ if ( parallelism != SLAPD_ARGON2_PARALLELISM ) {
+ Debug( LDAP_DEBUG_ANY, "pw-argon2: "
+ "non-default parallelism only supported when linked with "
+ "libargon2, got p=%lu\n",
+ parallelism );
+
+ if ( (slapMode & SLAP_MODE) != SLAP_TOOL_MODE ||
+ slapTool == SLAPPASSWD || slapTool == SLAPTEST ) {
+ return 1;
+ }
+ }
+#endif
+
+ return lutil_passwd_add( (struct berval *)&slapd_argon2_scheme,
+ slapd_argon2_verify, slapd_argon2_hash );
+}
+#endif /* SLAPD_OVER_PW_ARGON2 */
diff --git a/servers/slapd/referral.c b/servers/slapd/referral.c
new file mode 100644
index 0000000..57465c0
--- /dev/null
+++ b/servers/slapd/referral.c
@@ -0,0 +1,363 @@
+/* referral.c - muck with referrals */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include "slap.h"
+
+/*
+ * This routine generates the DN appropriate to return in
+ * an LDAP referral.
+ */
+static char * referral_dn_muck(
+ const char * refDN,
+ struct berval * baseDN,
+ struct berval * targetDN )
+{
+ int rc;
+ struct berval bvin;
+ struct berval nrefDN = BER_BVNULL;
+ struct berval nbaseDN = BER_BVNULL;
+ struct berval ntargetDN = BER_BVNULL;
+
+ if( !baseDN ) {
+ /* no base, return target */
+ return targetDN ? ch_strdup( targetDN->bv_val ) : NULL;
+ }
+
+ if( refDN ) {
+ bvin.bv_val = (char *)refDN;
+ bvin.bv_len = strlen( refDN );
+
+ rc = dnPretty( NULL, &bvin, &nrefDN, NULL );
+ if( rc != LDAP_SUCCESS ) {
+ /* Invalid refDN */
+ return NULL;
+ }
+ }
+
+ if( !targetDN ) {
+ /* continuation reference
+ * if refDN present return refDN
+ * else return baseDN
+ */
+ return nrefDN.bv_len ? nrefDN.bv_val : ch_strdup( baseDN->bv_val );
+ }
+
+ rc = dnPretty( NULL, targetDN, &ntargetDN, NULL );
+ if( rc != LDAP_SUCCESS ) {
+ /* Invalid targetDN */
+ ch_free( nrefDN.bv_val );
+ return NULL;
+ }
+
+ if( nrefDN.bv_len ) {
+ rc = dnPretty( NULL, baseDN, &nbaseDN, NULL );
+ if( rc != LDAP_SUCCESS ) {
+ /* Invalid baseDN */
+ ch_free( nrefDN.bv_val );
+ ch_free( ntargetDN.bv_val );
+ return NULL;
+ }
+
+ if( dn_match( &nbaseDN, &nrefDN ) ) {
+ ch_free( nrefDN.bv_val );
+ ch_free( nbaseDN.bv_val );
+ return ntargetDN.bv_val;
+ }
+
+ {
+ struct berval muck;
+
+ if( ntargetDN.bv_len < nbaseDN.bv_len ) {
+ ch_free( nrefDN.bv_val );
+ ch_free( nbaseDN.bv_val );
+ return ntargetDN.bv_val;
+ }
+
+ rc = strcasecmp(
+ &ntargetDN.bv_val[ntargetDN.bv_len-nbaseDN.bv_len],
+ nbaseDN.bv_val );
+ if( rc ) {
+ /* target not subordinate to base */
+ ch_free( nrefDN.bv_val );
+ ch_free( nbaseDN.bv_val );
+ return ntargetDN.bv_val;
+ }
+
+ muck.bv_len = ntargetDN.bv_len + nrefDN.bv_len - nbaseDN.bv_len;
+ muck.bv_val = ch_malloc( muck.bv_len + 1 );
+
+ strncpy( muck.bv_val, ntargetDN.bv_val,
+ ntargetDN.bv_len-nbaseDN.bv_len );
+ strcpy( &muck.bv_val[ntargetDN.bv_len-nbaseDN.bv_len],
+ nrefDN.bv_val );
+
+ ch_free( nrefDN.bv_val );
+ ch_free( nbaseDN.bv_val );
+ ch_free( ntargetDN.bv_val );
+
+ return muck.bv_val;
+ }
+ }
+
+ ch_free( nrefDN.bv_val );
+ return ntargetDN.bv_val;
+}
+
+
+/* validate URL for global referral use
+ * LDAP URLs must not have:
+ * DN, attrs, scope, nor filter
+ * Any non-LDAP URL is okay
+ *
+ * XXYYZ: should return an error string
+ */
+int validate_global_referral( const char *url )
+{
+ int rc;
+ LDAPURLDesc *lurl;
+
+ rc = ldap_url_parse_ext( url, &lurl, LDAP_PVT_URL_PARSE_NONE );
+
+ switch( rc ) {
+ case LDAP_URL_SUCCESS:
+ break;
+
+ case LDAP_URL_ERR_BADSCHEME:
+ /* not LDAP hence valid */
+ Debug( LDAP_DEBUG_CONFIG, "referral \"%s\": not LDAP.\n", url );
+ return 0;
+
+ default:
+ /* other error, bail */
+ Debug( LDAP_DEBUG_ANY,
+ "referral: invalid URL (%s): %s (%d)\n",
+ url, "" /* ldap_url_error2str(rc) */, rc );
+ return 1;
+ }
+
+ rc = 0;
+
+ if( lurl->lud_dn && *lurl->lud_dn ) {
+ Debug( LDAP_DEBUG_ANY,
+ "referral: URL (%s): contains DN\n",
+ url );
+ rc = 1;
+
+ } else if( lurl->lud_attrs ) {
+ Debug( LDAP_DEBUG_ANY,
+ "referral: URL (%s): requests attributes\n",
+ url );
+ rc = 1;
+
+ } else if( lurl->lud_scope != LDAP_SCOPE_DEFAULT ) {
+ Debug( LDAP_DEBUG_ANY,
+ "referral: URL (%s): contains explicit scope\n",
+ url );
+ rc = 1;
+
+ } else if( lurl->lud_filter ) {
+ Debug( LDAP_DEBUG_ANY,
+ "referral: URL (%s): contains explicit filter\n",
+ url );
+ rc = 1;
+ }
+
+ ldap_free_urldesc( lurl );
+ return rc;
+}
+
+BerVarray referral_rewrite(
+ BerVarray in,
+ struct berval *base,
+ struct berval *target,
+ int scope )
+{
+ int i;
+ BerVarray refs;
+ struct berval *iv, *jv;
+
+ if ( in == NULL ) {
+ return NULL;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &in[i] ); i++ ) {
+ /* just count them */
+ }
+
+ if ( i < 1 ) {
+ return NULL;
+ }
+
+ refs = ch_malloc( ( i + 1 ) * sizeof( struct berval ) );
+
+ for ( iv = in, jv = refs; !BER_BVISNULL( iv ); iv++ ) {
+ LDAPURLDesc *url;
+ char *dn;
+ int rc;
+
+ rc = ldap_url_parse_ext( iv->bv_val, &url, LDAP_PVT_URL_PARSE_NONE );
+ if ( rc == LDAP_URL_ERR_BADSCHEME ) {
+ ber_dupbv( jv++, iv );
+ continue;
+
+ } else if ( rc != LDAP_URL_SUCCESS ) {
+ continue;
+ }
+
+ dn = url->lud_dn;
+ url->lud_dn = referral_dn_muck( ( dn && *dn ) ? dn : NULL,
+ base, target );
+ ldap_memfree( dn );
+
+ if ( url->lud_scope == LDAP_SCOPE_DEFAULT ) {
+ url->lud_scope = scope;
+ }
+
+ jv->bv_val = ldap_url_desc2str( url );
+ if ( jv->bv_val != NULL ) {
+ jv->bv_len = strlen( jv->bv_val );
+
+ } else {
+ ber_dupbv( jv, iv );
+ }
+ jv++;
+
+ ldap_free_urldesc( url );
+ }
+
+ if ( jv == refs ) {
+ ch_free( refs );
+ refs = NULL;
+
+ } else {
+ BER_BVZERO( jv );
+ }
+
+ return refs;
+}
+
+
+BerVarray get_entry_referrals(
+ Operation *op,
+ Entry *e )
+{
+ Attribute *attr;
+ BerVarray refs;
+ unsigned i;
+ struct berval *iv, *jv;
+
+ AttributeDescription *ad_ref = slap_schema.si_ad_ref;
+
+ attr = attr_find( e->e_attrs, ad_ref );
+
+ if( attr == NULL ) return NULL;
+
+ for( i=0; attr->a_vals[i].bv_val != NULL; i++ ) {
+ /* count references */
+ }
+
+ if( i < 1 ) return NULL;
+
+ refs = ch_malloc( (i + 1) * sizeof(struct berval));
+
+ for( iv=attr->a_vals, jv=refs; iv->bv_val != NULL; iv++ ) {
+ unsigned k;
+ ber_dupbv( jv, iv );
+
+ /* trim the label */
+ for( k=0; k<jv->bv_len; k++ ) {
+ if( isspace( (unsigned char) jv->bv_val[k] ) ) {
+ jv->bv_val[k] = '\0';
+ jv->bv_len = k;
+ break;
+ }
+ }
+
+ if( jv->bv_len > 0 ) {
+ jv++;
+ } else {
+ free( jv->bv_val );
+ }
+ }
+
+ if( jv == refs ) {
+ free( refs );
+ refs = NULL;
+
+ } else {
+ jv->bv_val = NULL;
+ }
+
+ /* we should check that a referral value exists... */
+ return refs;
+}
+
+
+int get_alias_dn(
+ Entry *e,
+ struct berval *ndn,
+ int *err,
+ const char **text )
+{
+ Attribute *a;
+ AttributeDescription *aliasedObjectName
+ = slap_schema.si_ad_aliasedObjectName;
+
+ a = attr_find( e->e_attrs, aliasedObjectName );
+
+ if( a == NULL ) {
+ /*
+ * there was an aliasedobjectname defined but no data.
+ */
+ *err = LDAP_ALIAS_PROBLEM;
+ *text = "alias missing aliasedObjectName attribute";
+ return -1;
+ }
+
+ /*
+ * aliasedObjectName should be SINGLE-VALUED with a single value.
+ */
+ if ( a->a_vals[0].bv_val == NULL ) {
+ /*
+ * there was an aliasedobjectname defined but no data.
+ */
+ *err = LDAP_ALIAS_PROBLEM;
+ *text = "alias missing aliasedObjectName value";
+ return -1;
+ }
+
+ if( a->a_nvals[1].bv_val != NULL ) {
+ *err = LDAP_ALIAS_PROBLEM;
+ *text = "alias has multivalued aliasedObjectName";
+ return -1;
+ }
+
+ *ndn = a->a_nvals[0];
+
+ return 0;
+}
+
diff --git a/servers/slapd/result.c b/servers/slapd/result.c
new file mode 100644
index 0000000..9da5e7e
--- /dev/null
+++ b/servers/slapd/result.c
@@ -0,0 +1,1916 @@
+/* result.c - routines to send ldap results, errors, and referrals */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include "slap.h"
+
+#if SLAP_STATS_ETIME
+#define ETIME_SETUP \
+ struct timeval now; \
+ char timestr[64]; \
+ (void) gettimeofday( &now, NULL ); \
+ now.tv_sec -= op->o_time; \
+ now.tv_usec -= op->o_tusec; \
+ if ( now.tv_usec < 0 ) { \
+ --now.tv_sec; now.tv_usec += 1000000; \
+ } \
+ sprintf(timestr, "qtime=%d.%06d etime=%d.%06d", \
+ (int)op->o_qtime.tv_sec, (int)op->o_qtime.tv_usec, \
+ (int)now.tv_sec, (int)now.tv_usec);
+#define ETIME_LOGFMT "%s "
+#define StatslogEtime(lvl,fmt,pfx,tag,err,...) \
+ Debug(lvl,fmt,pfx,tag,err,timestr,__VA_ARGS__)
+#else
+#define ETIME_SETUP
+#define ETIME_LOGFMT ""
+#define StatslogEtime Debug
+#endif /* SLAP_STATS_ETIME */
+
+const struct berval slap_dummy_bv = BER_BVNULL;
+
+int slap_null_cb( Operation *op, SlapReply *rs )
+{
+ return 0;
+}
+
+int slap_freeself_cb( Operation *op, SlapReply *rs )
+{
+ assert( op->o_callback != NULL );
+
+ op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
+ op->o_callback = NULL;
+
+ return SLAP_CB_CONTINUE;
+}
+
+static char *v2ref( BerVarray ref, const char *text )
+{
+ size_t len = 0, i = 0;
+ char *v2;
+
+ if(ref == NULL) {
+ if (text) {
+ return ch_strdup(text);
+ } else {
+ return NULL;
+ }
+ }
+
+ if ( text != NULL ) {
+ len = strlen( text );
+ if (text[len-1] != '\n') {
+ i = 1;
+ }
+ }
+
+ v2 = ch_malloc( len+i+sizeof("Referral:") );
+
+ if( text != NULL ) {
+ strcpy(v2, text);
+ if( i ) {
+ v2[len++] = '\n';
+ }
+ }
+ strcpy( v2+len, "Referral:" );
+ len += sizeof("Referral:");
+
+ for( i=0; ref[i].bv_val != NULL; i++ ) {
+ v2 = ch_realloc( v2, len + ref[i].bv_len + 1 );
+ v2[len-1] = '\n';
+ AC_MEMCPY(&v2[len], ref[i].bv_val, ref[i].bv_len );
+ len += ref[i].bv_len;
+ if (ref[i].bv_val[ref[i].bv_len-1] != '/') {
+ ++len;
+ }
+ }
+
+ v2[len-1] = '\0';
+ return v2;
+}
+
+ber_tag_t
+slap_req2res( ber_tag_t tag )
+{
+ switch( tag ) {
+ case LDAP_REQ_ADD:
+ case LDAP_REQ_BIND:
+ case LDAP_REQ_COMPARE:
+ case LDAP_REQ_EXTENDED:
+ case LDAP_REQ_MODIFY:
+ case LDAP_REQ_MODRDN:
+ tag++;
+ break;
+
+ case LDAP_REQ_DELETE:
+ tag = LDAP_RES_DELETE;
+ break;
+
+ case LDAP_REQ_ABANDON:
+ case LDAP_REQ_UNBIND:
+ tag = LBER_SEQUENCE;
+ break;
+
+ case LDAP_REQ_SEARCH:
+ tag = LDAP_RES_SEARCH_RESULT;
+ break;
+
+ default:
+ tag = LBER_SEQUENCE;
+ }
+
+ return tag;
+}
+
+/*
+ * SlapReply debugging enabled by USE_RS_ASSERT.
+ *
+ * Disabled by default, but compiled in (but still unused) when
+ * LDAP_TEST. #define USE_RS_ASSERT as nonzero to enable some
+ * assertions which check the SlapReply. USE_RS_ASSERT = 2 or higher
+ * check aggressively, currently some code fail these tests.
+ *
+ * Environment variable $NO_RS_ASSERT controls how USE_RS_ASSERT handles
+ * errors. > 0: ignore errors, 0: abort (the default), < 0: just warn.
+ *
+ * Wrap LDAP operation calls in macros SLAP_OP() & co from proto-slap.h
+ * to check the SlapReply. contrib/slapd-tools/wrap_slap_ops converts
+ * source code to use the macros.
+ */
+#if defined(LDAP_TEST) || (defined(USE_RS_ASSERT) && (USE_RS_ASSERT))
+
+int rs_suppress_assert = 0;
+
+/* RS_ASSERT() helper function */
+void rs_assert_(const char*file, unsigned line, const char*fn, const char*cond)
+{
+ int no_assert = rs_suppress_assert, save_errno = errno;
+ const char *s;
+
+ if ( no_assert >= 0 ) {
+ if ( no_assert == 0 && (s = getenv( "NO_RS_ASSERT" )) && *s ) {
+ no_assert = rs_suppress_assert = atoi( s );
+ }
+ if ( no_assert > 0 ) {
+ errno = save_errno;
+ return;
+ }
+ }
+
+#ifdef rs_assert_ /* proto-slap.h #defined away the fn parameter */
+ fprintf( stderr,"%s:%u: " "RS_ASSERT(%s) failed.\n", file,line,cond );
+#else
+ fprintf( stderr,"%s:%u: %s: RS_ASSERT(%s) failed.\n", file,line,fn,cond );
+#endif
+ fflush( stderr );
+
+ errno = save_errno;
+ /* $NO_RS_ASSERT > 0: ignore rs_asserts, 0: abort, < 0: just warn */
+ if ( !no_assert /* from $NO_RS_ASSERT */ ) abort();
+}
+
+/* SlapReply is consistent */
+void
+(rs_assert_ok)( const SlapReply *rs )
+{
+ const slap_mask_t flags = rs->sr_flags;
+
+ if ( flags & REP_ENTRY_MASK ) {
+ RS_ASSERT( !(flags & REP_ENTRY_MUSTRELEASE)
+ || !(flags & (REP_ENTRY_MASK ^ REP_ENTRY_MUSTRELEASE)) );
+ RS_ASSERT( rs->sr_entry != NULL );
+ RS_ASSERT( (1 << rs->sr_type) &
+ ((1 << REP_SEARCH) | (1 << REP_SEARCHREF) |
+ (1 << REP_RESULT) | (1 << REP_GLUE_RESULT)) );
+ }
+#if defined(USE_RS_ASSERT) && (USE_RS_ASSERT) > 1 /* TODO: Enable when safe */
+ if ( (flags & (REP_MATCHED_MASK | REP_REF_MASK | REP_CTRLS_MASK)) ) {
+ RS_ASSERT( !(flags & REP_MATCHED_MASK) || rs->sr_matched );
+ RS_ASSERT( !(flags & REP_CTRLS_MASK ) || rs->sr_ctrls );
+ /* Note: LDAP_REFERRAL + !sr_ref is OK, becomes LDAP_NO_SUCH_OBJECT */
+ }
+#if (USE_RS_ASSERT) > 2
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ RS_ASSERT( rs->sr_text == NULL );
+ RS_ASSERT( rs->sr_matched == NULL );
+ }
+#endif
+#endif
+}
+
+/* Ready for calling a new backend operation */
+void
+(rs_assert_ready)( const SlapReply *rs )
+{
+ RS_ASSERT( !rs->sr_entry );
+#if defined(USE_RS_ASSERT) && (USE_RS_ASSERT) > 1 /* TODO: Enable when safe */
+ RS_ASSERT( !rs->sr_text );
+ RS_ASSERT( !rs->sr_ref );
+ RS_ASSERT( !rs->sr_matched );
+ RS_ASSERT( !rs->sr_ctrls );
+ RS_ASSERT( !rs->sr_flags );
+#if (USE_RS_ASSERT) > 2
+ RS_ASSERT( rs->sr_err == LDAP_SUCCESS );
+#endif
+#else
+ RS_ASSERT( !(rs->sr_flags & REP_ENTRY_MASK) );
+#endif
+}
+
+/* Backend operation done */
+void
+(rs_assert_done)( const SlapReply *rs )
+{
+#if defined(USE_RS_ASSERT) && (USE_RS_ASSERT) > 1 /* TODO: Enable when safe */
+ RS_ASSERT( !(rs->sr_flags & ~(REP_ENTRY_MODIFIABLE|REP_NO_OPERATIONALS)) );
+ rs_assert_ok( rs );
+#else
+ RS_ASSERT( !(rs->sr_flags & REP_ENTRY_MUSTFLUSH) );
+#endif
+}
+
+#endif /* LDAP_TEST || USE_RS_ASSERT */
+
+/* Reset a used SlapReply whose contents has been flushed (freed/released) */
+void
+(rs_reinit)( SlapReply *rs, slap_reply_t type )
+{
+ rs_reinit( rs, type ); /* proto-slap.h macro */
+}
+
+/* Obey and clear rs->sr_flags & REP_ENTRY_MASK. Clear sr_entry if freed. */
+void
+rs_flush_entry( Operation *op, SlapReply *rs, slap_overinst *on )
+{
+ rs_assert_ok( rs );
+
+ if ( (rs->sr_flags & REP_ENTRY_MUSTFLUSH) && rs->sr_entry != NULL ) {
+ if ( !(rs->sr_flags & REP_ENTRY_MUSTRELEASE) ) {
+ entry_free( rs->sr_entry );
+ } else if ( on != NULL ) {
+ overlay_entry_release_ov( op, rs->sr_entry, 0, on );
+ } else {
+ be_entry_release_rw( op, rs->sr_entry, 0 );
+ }
+ rs->sr_entry = NULL;
+ }
+
+ rs->sr_flags &= ~REP_ENTRY_MASK;
+}
+
+/* Set rs->sr_entry after obeying and clearing sr_flags & REP_ENTRY_MASK. */
+void
+rs_replace_entry( Operation *op, SlapReply *rs, slap_overinst *on, Entry *e )
+{
+ rs_flush_entry( op, rs, on );
+ rs->sr_entry = e;
+}
+
+/*
+ * Ensure rs->sr_entry is modifiable, by duplicating it if necessary.
+ * Obey sr_flags. Set REP_ENTRY_<MODIFIABLE, and MUSTBEFREED if duplicated>.
+ * Return nonzero if rs->sr_entry was replaced.
+ */
+int
+rs_entry2modifiable( Operation *op, SlapReply *rs, slap_overinst *on )
+{
+ if ( rs->sr_flags & REP_ENTRY_MODIFIABLE ) {
+ rs_assert_ok( rs );
+ return 0;
+ }
+ rs_replace_entry( op, rs, on, entry_dup( rs->sr_entry ));
+ rs->sr_flags |= REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED;
+ return 1;
+}
+
+/* Check for any callbacks that want to be informed about being blocked
+ * on output. These callbacks are expected to leave the callback list
+ * unmodified. Their result is ignored.
+ */
+static void
+slap_writewait_play(
+ Operation *op )
+{
+ slap_callback *sc = op->o_callback;
+
+ for ( ; sc; sc = sc->sc_next ) {
+ if ( sc->sc_writewait )
+ sc->sc_writewait( op, sc );
+ }
+}
+
+static long send_ldap_ber(
+ Operation *op,
+ BerElement *ber )
+{
+ Connection *conn = op->o_conn;
+ ber_len_t bytes;
+ long ret = 0;
+ char *close_reason;
+ int do_resume = 0;
+
+ ber_get_option( ber, LBER_OPT_BER_BYTES_TO_WRITE, &bytes );
+
+ /* write only one pdu at a time - wait til it's our turn */
+ ldap_pvt_thread_mutex_lock( &conn->c_write1_mutex );
+ if (( op->o_abandon && !op->o_cancel ) || !connection_valid( conn ) ||
+ conn->c_writers < 0 ) {
+ ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex );
+ return 0;
+ }
+
+ conn->c_writers++;
+
+ while ( conn->c_writers > 0 && conn->c_writing ) {
+ ldap_pvt_thread_pool_idle( &connection_pool );
+ ldap_pvt_thread_cond_wait( &conn->c_write1_cv, &conn->c_write1_mutex );
+ ldap_pvt_thread_pool_unidle( &connection_pool );
+ }
+
+ /* connection was closed under us */
+ if ( conn->c_writers < 0 ) {
+ /* we're the last waiter, let the closer continue */
+ if ( conn->c_writers == -1 )
+ ldap_pvt_thread_cond_signal( &conn->c_write1_cv );
+ conn->c_writers++;
+ ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex );
+ return 0;
+ }
+
+ /* Our turn */
+ conn->c_writing = 1;
+
+ /* write the pdu */
+ while( 1 ) {
+ int err;
+ char ebuf[128];
+
+ if ( ber_flush2( conn->c_sb, ber, LBER_FLUSH_FREE_NEVER ) == 0 ) {
+ ret = bytes;
+ break;
+ }
+
+ err = sock_errno();
+
+ /*
+ * we got an error. if it's ewouldblock, we need to
+ * wait on the socket being writable. otherwise, figure
+ * it's a hard error and return.
+ */
+
+ Debug( LDAP_DEBUG_CONNS, "ber_flush2 failed errno=%d reason=\"%s\"\n",
+ err, sock_errstr(err, ebuf, sizeof(ebuf)) );
+
+ if ( err != EWOULDBLOCK && err != EAGAIN ) {
+ close_reason = "connection lost on write";
+fail:
+ conn->c_writers--;
+ conn->c_writing = 0;
+ ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex );
+ ldap_pvt_thread_mutex_lock( &conn->c_mutex );
+ connection_closing( conn, close_reason );
+ ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
+ return -1;
+ }
+
+ /* wait for socket to be write-ready */
+ do_resume = 1;
+ conn->c_writewaiter = 1;
+ ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex );
+ ldap_pvt_thread_pool_idle( &connection_pool );
+ slap_writewait_play( op );
+ err = slapd_wait_writer( conn->c_sd );
+ conn->c_writewaiter = 0;
+ ldap_pvt_thread_pool_unidle( &connection_pool );
+ ldap_pvt_thread_mutex_lock( &conn->c_write1_mutex );
+ /* 0 is timeout, so we close it.
+ * -1 is an error, close it.
+ */
+ if ( err <= 0 ) {
+ if ( err == 0 )
+ close_reason = "writetimeout";
+ else
+ close_reason = "connection lost on writewait";
+ goto fail;
+ }
+
+ if ( conn->c_writers < 0 ) {
+ ret = 0;
+ break;
+ }
+ }
+
+ conn->c_writing = 0;
+ if ( conn->c_writers < 0 ) {
+ /* shutting down, don't resume any ops */
+ do_resume = 0;
+ conn->c_writers++;
+ if ( !conn->c_writers )
+ ldap_pvt_thread_cond_signal( &conn->c_write1_cv );
+ } else {
+ conn->c_writers--;
+ /* other writers are waiting, don't resume any ops */
+ if ( conn->c_writers )
+ do_resume = 0;
+ ldap_pvt_thread_cond_signal( &conn->c_write1_cv );
+ }
+ ldap_pvt_thread_mutex_unlock( &conn->c_write1_mutex );
+
+ /* If there are no more writers, release a pending op */
+ if ( do_resume )
+ connection_write_resume( conn );
+
+ return ret;
+}
+
+static int
+send_ldap_control( BerElement *ber, LDAPControl *c )
+{
+ int rc;
+
+ assert( c != NULL );
+
+ rc = ber_printf( ber, "{s" /*}*/, c->ldctl_oid );
+
+ if( c->ldctl_iscritical ) {
+ rc = ber_printf( ber, "b",
+ (ber_int_t) c->ldctl_iscritical ) ;
+ if( rc == -1 ) return rc;
+ }
+
+ if( c->ldctl_value.bv_val != NULL ) {
+ rc = ber_printf( ber, "O", &c->ldctl_value );
+ if( rc == -1 ) return rc;
+ }
+
+ rc = ber_printf( ber, /*{*/"N}" );
+ if( rc == -1 ) return rc;
+
+ return 0;
+}
+
+static int
+send_ldap_controls( Operation *o, BerElement *ber, LDAPControl **c )
+{
+ int rc;
+
+ if( c == NULL )
+ return 0;
+
+ rc = ber_printf( ber, "t{"/*}*/, LDAP_TAG_CONTROLS );
+ if( rc == -1 ) return rc;
+
+ for( ; *c != NULL; c++) {
+ rc = send_ldap_control( ber, *c );
+ if( rc == -1 ) return rc;
+ }
+
+#ifdef SLAP_CONTROL_X_SORTEDRESULTS
+ /* this is a hack to avoid having to modify op->s_ctrls */
+ if( o->o_sortedresults ) {
+ BerElementBuffer berbuf;
+ BerElement *sber = (BerElement *) &berbuf;
+ LDAPControl sorted;
+ BER_BVZERO( &sorted.ldctl_value );
+ sorted.ldctl_oid = LDAP_CONTROL_SORTRESPONSE;
+ sorted.ldctl_iscritical = 0;
+
+ ber_init2( sber, NULL, LBER_USE_DER );
+
+ ber_printf( sber, "{e}", LDAP_UNWILLING_TO_PERFORM );
+
+ if( ber_flatten2( sber, &sorted.ldctl_value, 0 ) == -1 ) {
+ return -1;
+ }
+
+ (void) ber_free_buf( sber );
+
+ rc = send_ldap_control( ber, &sorted );
+ if( rc == -1 ) return rc;
+ }
+#endif
+
+ rc = ber_printf( ber, /*{*/"N}" );
+
+ return rc;
+}
+
+/*
+ * slap_response_play()
+ *
+ * plays the callback list; rationale: a callback can
+ * - remove itself from the list, by setting op->o_callback = NULL;
+ * malloc()'ed callbacks should free themselves from inside the
+ * sc_response() function.
+ * - replace itself with another (list of) callback(s), by setting
+ * op->o_callback = a new (list of) callback(s); in this case, it
+ * is the callback's responsibility to to append existing subsequent
+ * callbacks to the end of the list that is passed to the sc_response()
+ * function.
+ * - modify the list of subsequent callbacks by modifying the value
+ * of the sc_next field from inside the sc_response() function; this
+ * case does not require any handling from inside slap_response_play()
+ *
+ * To stop execution of the playlist, the sc_response() function must return
+ * a value different from SLAP_SC_CONTINUE.
+ *
+ * The same applies to slap_cleanup_play(); only, there is no means to stop
+ * execution of the playlist, since all cleanup functions must be called.
+ */
+static int
+slap_response_play(
+ Operation *op,
+ SlapReply *rs )
+{
+ int rc;
+
+ slap_callback *sc = op->o_callback, **scp;
+
+ rc = SLAP_CB_CONTINUE;
+ for ( scp = &sc; *scp; ) {
+ slap_callback *sc_next = (*scp)->sc_next, **sc_nextp = &(*scp)->sc_next;
+
+ op->o_callback = *scp;
+ if ( op->o_callback->sc_response ) {
+ rc = op->o_callback->sc_response( op, rs );
+ if ( op->o_callback == NULL ) {
+ /* the callback has been removed;
+ * repair the list */
+ *scp = sc_next;
+ sc_nextp = scp;
+
+ } else if ( op->o_callback != *scp ) {
+ /* a new callback has been inserted
+ * in place of the existing one; repair the list */
+ *scp = op->o_callback;
+ sc_nextp = scp;
+ }
+ if ( rc != SLAP_CB_CONTINUE ) break;
+ }
+ scp = sc_nextp;
+ }
+
+ op->o_callback = sc;
+ return rc;
+}
+
+static int
+slap_cleanup_play(
+ Operation *op,
+ SlapReply *rs )
+{
+ slap_callback *sc = op->o_callback, **scp;
+
+ for ( scp = &sc; *scp; ) {
+ slap_callback *sc_next = (*scp)->sc_next, **sc_nextp = &(*scp)->sc_next;
+
+ op->o_callback = *scp;
+ if ( op->o_callback->sc_cleanup ) {
+ (void)op->o_callback->sc_cleanup( op, rs );
+ if ( op->o_callback == NULL ) {
+ /* the callback has been removed;
+ * repair the list */
+ *scp = sc_next;
+ sc_nextp = scp;
+
+ } else if ( op->o_callback != *scp ) {
+ /* a new callback has been inserted
+ * after the existing one; repair the list */
+ /* a new callback has been inserted
+ * in place of the existing one; repair the list */
+ *scp = op->o_callback;
+ sc_nextp = scp;
+ }
+ /* don't care about the result; do all cleanup */
+ }
+ scp = sc_nextp;
+ }
+
+ op->o_callback = sc;
+ return LDAP_SUCCESS;
+}
+
+static int
+send_ldap_response(
+ Operation *op,
+ SlapReply *rs )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *) &berbuf;
+ int rc = LDAP_SUCCESS;
+ long bytes;
+
+ /* op was actually aborted, bypass everything if client didn't Cancel */
+ if (( rs->sr_err == SLAPD_ABANDON ) && !op->o_cancel ) {
+ rc = SLAPD_ABANDON;
+ goto clean2;
+ }
+
+ if ( op->o_callback ) {
+ rc = slap_response_play( op, rs );
+ if ( rc != SLAP_CB_CONTINUE ) {
+ goto clean2;
+ }
+ }
+
+ /* op completed, connection aborted, bypass sending response */
+ if ( op->o_abandon && !op->o_cancel ) {
+ rc = SLAPD_ABANDON;
+ goto clean2;
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ if (op->o_conn && op->o_conn->c_is_udp)
+ ber = op->o_res_ber;
+ else
+#endif
+ {
+ ber_init_w_nullc( ber, LBER_USE_DER );
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+ }
+
+ rc = rs->sr_err;
+ if ( rc == SLAPD_ABANDON && op->o_cancel )
+ rc = LDAP_CANCELLED;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "send_ldap_response: msgid=%d tag=%lu err=%d\n",
+ rs->sr_msgid, rs->sr_tag, rc );
+
+ if( rs->sr_ref ) {
+ Debug( LDAP_DEBUG_ARGS, "send_ldap_response: ref=\"%s\"\n",
+ rs->sr_ref[0].bv_val ? rs->sr_ref[0].bv_val : "NULL" );
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ if (op->o_conn && op->o_conn->c_is_udp &&
+ op->o_protocol == LDAP_VERSION2 )
+ {
+ rc = ber_printf( ber, "t{ess" /*"}"*/,
+ rs->sr_tag, rc,
+ rs->sr_matched == NULL ? "" : rs->sr_matched,
+ rs->sr_text == NULL ? "" : rs->sr_text );
+ } else
+#endif
+ if ( rs->sr_type == REP_INTERMEDIATE ) {
+ rc = ber_printf( ber, "{it{" /*"}}"*/,
+ rs->sr_msgid, rs->sr_tag );
+
+ } else {
+ rc = ber_printf( ber, "{it{ess" /*"}}"*/,
+ rs->sr_msgid, rs->sr_tag, rc,
+ rs->sr_matched == NULL ? "" : rs->sr_matched,
+ rs->sr_text == NULL ? "" : rs->sr_text );
+ }
+
+ if( rc != -1 ) {
+ if ( rs->sr_ref != NULL ) {
+ assert( rs->sr_err == LDAP_REFERRAL );
+ rc = ber_printf( ber, "t{W}",
+ LDAP_TAG_REFERRAL, rs->sr_ref );
+ } else {
+ assert( rs->sr_err != LDAP_REFERRAL );
+ }
+ }
+
+ if( rc != -1 && rs->sr_type == REP_SASL && rs->sr_sasldata != NULL ) {
+ rc = ber_printf( ber, "tO",
+ LDAP_TAG_SASL_RES_CREDS, rs->sr_sasldata );
+ }
+
+ if( rc != -1 &&
+ ( rs->sr_type == REP_EXTENDED || rs->sr_type == REP_INTERMEDIATE ))
+ {
+ if ( rs->sr_rspoid != NULL ) {
+ rc = ber_printf( ber, "ts",
+ rs->sr_type == REP_EXTENDED
+ ? LDAP_TAG_EXOP_RES_OID : LDAP_TAG_IM_RES_OID,
+ rs->sr_rspoid );
+ }
+ if( rc != -1 && rs->sr_rspdata != NULL ) {
+ rc = ber_printf( ber, "tO",
+ rs->sr_type == REP_EXTENDED
+ ? LDAP_TAG_EXOP_RES_VALUE : LDAP_TAG_IM_RES_VALUE,
+ rs->sr_rspdata );
+ }
+ }
+
+ if( rc != -1 ) {
+ rc = ber_printf( ber, /*"{"*/ "N}" );
+ }
+
+ if( rc != -1 ) {
+ rc = send_ldap_controls( op, ber, rs->sr_ctrls );
+ }
+
+ if( rc != -1 ) {
+ rc = ber_printf( ber, /*"{"*/ "N}" );
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ if( op->o_conn && op->o_conn->c_is_udp && op->o_protocol == LDAP_VERSION2
+ && rc != -1 )
+ {
+ rc = ber_printf( ber, /*"{"*/ "N}" );
+ }
+#endif
+
+ if ( rc == -1 ) {
+ Debug( LDAP_DEBUG_ANY, "ber_printf failed\n" );
+
+#ifdef LDAP_CONNECTIONLESS
+ if (!op->o_conn || op->o_conn->c_is_udp == 0)
+#endif
+ {
+ ber_free_buf( ber );
+ }
+ goto cleanup;
+ }
+
+ /* send BER */
+ bytes = send_ldap_ber( op, ber );
+#ifdef LDAP_CONNECTIONLESS
+ if (!op->o_conn || op->o_conn->c_is_udp == 0)
+#endif
+ {
+ ber_free_buf( ber );
+ }
+
+ if ( bytes < 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "send_ldap_response: ber write failed\n" );
+
+ goto cleanup;
+ }
+
+ ldap_pvt_thread_mutex_lock( &op->o_counters->sc_mutex );
+ ldap_pvt_mp_add_ulong( op->o_counters->sc_pdu, 1 );
+ ldap_pvt_mp_add_ulong( op->o_counters->sc_bytes, (unsigned long)bytes );
+ ldap_pvt_thread_mutex_unlock( &op->o_counters->sc_mutex );
+
+cleanup:;
+ /* Tell caller that we did this for real, as opposed to being
+ * overridden by a callback
+ */
+ rc = SLAP_CB_CONTINUE;
+
+clean2:;
+ if ( op->o_callback ) {
+ (void)slap_cleanup_play( op, rs );
+ }
+
+ if ( rs->sr_flags & REP_MATCHED_MUSTBEFREED ) {
+ rs->sr_flags ^= REP_MATCHED_MUSTBEFREED; /* paranoia */
+ if ( rs->sr_matched ) {
+ free( (char *)rs->sr_matched );
+ rs->sr_matched = NULL;
+ }
+ }
+
+ if ( rs->sr_flags & REP_REF_MUSTBEFREED ) {
+ rs->sr_flags ^= REP_REF_MUSTBEFREED; /* paranoia */
+ if ( rs->sr_ref ) {
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ }
+ }
+
+ if ( rs->sr_flags & REP_CTRLS_MUSTBEFREED ) {
+ rs->sr_flags ^= REP_CTRLS_MUSTBEFREED; /* paranoia */
+ if ( rs->sr_ctrls ) {
+ slap_free_ctrls( op, rs->sr_ctrls );
+ rs->sr_ctrls = NULL;
+ }
+ }
+
+ return rc;
+}
+
+
+void
+send_ldap_disconnect( Operation *op, SlapReply *rs )
+{
+#define LDAP_UNSOLICITED_ERROR(e) \
+ ( (e) == LDAP_PROTOCOL_ERROR \
+ || (e) == LDAP_STRONG_AUTH_REQUIRED \
+ || (e) == LDAP_UNAVAILABLE )
+
+ Debug( LDAP_DEBUG_TRACE,
+ "send_ldap_disconnect %d:%s\n",
+ rs->sr_err, rs->sr_text ? rs->sr_text : "" );
+ assert( LDAP_UNSOLICITED_ERROR( rs->sr_err ) );
+
+ /* TODO: Flush the entry if sr_type == REP_SEARCH/REP_SEARCHREF? */
+ RS_ASSERT( !(rs->sr_flags & REP_ENTRY_MASK) );
+ rs->sr_flags &= ~REP_ENTRY_MASK; /* paranoia */
+
+ rs->sr_type = REP_EXTENDED;
+ rs->sr_rspdata = NULL;
+
+ if ( op->o_protocol < LDAP_VERSION3 ) {
+ rs->sr_rspoid = NULL;
+ rs->sr_tag = slap_req2res( op->o_tag );
+ rs->sr_msgid = (rs->sr_tag != LBER_SEQUENCE) ? op->o_msgid : 0;
+
+ } else {
+ rs->sr_rspoid = LDAP_NOTICE_DISCONNECT;
+ rs->sr_tag = LDAP_RES_EXTENDED;
+ rs->sr_msgid = LDAP_RES_UNSOLICITED;
+ }
+
+ if ( send_ldap_response( op, rs ) == SLAP_CB_CONTINUE ) {
+ ETIME_SETUP;
+ StatslogEtime( LDAP_DEBUG_STATS,
+ "%s DISCONNECT tag=%lu err=%d "ETIME_LOGFMT"text=%s\n",
+ op->o_log_prefix, rs->sr_tag, rs->sr_err,
+ rs->sr_text ? rs->sr_text : "" );
+ }
+}
+
+void
+slap_send_ldap_result( Operation *op, SlapReply *rs )
+{
+ char *tmp = NULL;
+ const char *otext = rs->sr_text;
+ BerVarray oref = rs->sr_ref;
+
+ rs->sr_type = REP_RESULT;
+
+ /* Propagate Abandons so that cleanup callbacks can be processed */
+ if ( rs->sr_err == SLAPD_ABANDON || op->o_abandon )
+ goto abandon;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "send_ldap_result: %s p=%d\n",
+ op->o_log_prefix, op->o_protocol );
+ Debug( LDAP_DEBUG_ARGS,
+ "send_ldap_result: err=%d matched=\"%s\" text=\"%s\"\n",
+ rs->sr_err, rs->sr_matched ? rs->sr_matched : "",
+ rs->sr_text ? rs->sr_text : "" );
+ if( rs->sr_ref ) {
+ Debug( LDAP_DEBUG_ARGS,
+ "send_ldap_result: referral=\"%s\"\n",
+ rs->sr_ref[0].bv_val ? rs->sr_ref[0].bv_val : "NULL" );
+ }
+ assert( !LDAP_API_ERROR( rs->sr_err ) );
+ assert( rs->sr_err != LDAP_PARTIAL_RESULTS );
+
+ if ( rs->sr_err == LDAP_REFERRAL ) {
+ if( op->o_domain_scope ) rs->sr_ref = NULL;
+
+ if( rs->sr_ref == NULL ) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ } else if ( op->o_protocol < LDAP_VERSION3 ) {
+ rs->sr_err = LDAP_PARTIAL_RESULTS;
+ }
+ }
+
+ if ( op->o_protocol < LDAP_VERSION3 ) {
+ tmp = v2ref( rs->sr_ref, rs->sr_text );
+ rs->sr_text = tmp;
+ rs->sr_ref = NULL;
+ }
+
+abandon:
+ rs->sr_tag = slap_req2res( op->o_tag );
+ rs->sr_msgid = (rs->sr_tag != LBER_SEQUENCE) ? op->o_msgid : 0;
+
+ if ( rs->sr_flags & REP_REF_MUSTBEFREED ) {
+ if ( rs->sr_ref == NULL ) {
+ rs->sr_flags ^= REP_REF_MUSTBEFREED;
+ ber_bvarray_free( oref );
+ }
+ oref = NULL; /* send_ldap_response() will free rs->sr_ref if != NULL */
+ }
+
+ if ( send_ldap_response( op, rs ) == SLAP_CB_CONTINUE ) {
+ ETIME_SETUP;
+ if ( op->o_tag == LDAP_REQ_SEARCH ) {
+ StatslogEtime( LDAP_DEBUG_STATS,
+ "%s SEARCH RESULT tag=%lu err=%d "ETIME_LOGFMT"nentries=%d text=%s\n",
+ op->o_log_prefix, rs->sr_tag, rs->sr_err,
+ rs->sr_nentries, rs->sr_text ? rs->sr_text : "" );
+ } else {
+ StatslogEtime( LDAP_DEBUG_STATS,
+ "%s RESULT tag=%lu err=%d "ETIME_LOGFMT"text=%s\n",
+ op->o_log_prefix, rs->sr_tag, rs->sr_err,
+ rs->sr_text ? rs->sr_text : "" );
+ }
+ }
+
+ if( tmp != NULL ) ch_free(tmp);
+ rs->sr_text = otext;
+ rs->sr_ref = oref;
+}
+
+void
+send_ldap_sasl( Operation *op, SlapReply *rs )
+{
+ Debug( LDAP_DEBUG_TRACE, "send_ldap_sasl: err=%d len=%ld\n",
+ rs->sr_err,
+ rs->sr_sasldata ? (long) rs->sr_sasldata->bv_len : -1 );
+
+ RS_ASSERT( !(rs->sr_flags & REP_ENTRY_MASK) );
+ rs->sr_flags &= ~REP_ENTRY_MASK; /* paranoia */
+
+ rs->sr_type = REP_SASL;
+ rs->sr_tag = slap_req2res( op->o_tag );
+ rs->sr_msgid = (rs->sr_tag != LBER_SEQUENCE) ? op->o_msgid : 0;
+
+ if ( send_ldap_response( op, rs ) == SLAP_CB_CONTINUE ) {
+ ETIME_SETUP;
+ StatslogEtime( LDAP_DEBUG_STATS,
+ "%s RESULT tag=%lu err=%d "ETIME_LOGFMT"text=%s\n",
+ op->o_log_prefix, rs->sr_tag, rs->sr_err,
+ rs->sr_text ? rs->sr_text : "" );
+ }
+}
+
+void
+slap_send_ldap_extended( Operation *op, SlapReply *rs )
+{
+ Debug( LDAP_DEBUG_TRACE,
+ "send_ldap_extended: err=%d oid=%s len=%ld\n",
+ rs->sr_err,
+ rs->sr_rspoid ? rs->sr_rspoid : "",
+ rs->sr_rspdata != NULL ? rs->sr_rspdata->bv_len : 0 );
+
+ RS_ASSERT( !(rs->sr_flags & REP_ENTRY_MASK) );
+ rs->sr_flags &= ~REP_ENTRY_MASK; /* paranoia */
+
+ rs->sr_type = REP_EXTENDED;
+ rs->sr_tag = slap_req2res( op->o_tag );
+ rs->sr_msgid = (rs->sr_tag != LBER_SEQUENCE) ? op->o_msgid : 0;
+
+ if ( send_ldap_response( op, rs ) == SLAP_CB_CONTINUE ) {
+ ETIME_SETUP;
+ StatslogEtime( LDAP_DEBUG_STATS,
+ "%s RESULT oid=%s err=%d "ETIME_LOGFMT"text=%s\n",
+ op->o_log_prefix, rs->sr_rspoid ? rs->sr_rspoid : "",
+ rs->sr_err, rs->sr_text ? rs->sr_text : "" );
+ }
+}
+
+void
+slap_send_ldap_intermediate( Operation *op, SlapReply *rs )
+{
+ Debug( LDAP_DEBUG_TRACE,
+ "send_ldap_intermediate: err=%d oid=%s len=%ld\n",
+ rs->sr_err,
+ rs->sr_rspoid ? rs->sr_rspoid : "",
+ rs->sr_rspdata != NULL ? rs->sr_rspdata->bv_len : 0 );
+
+ RS_ASSERT( !(rs->sr_flags & REP_ENTRY_MASK) );
+ rs->sr_flags &= ~REP_ENTRY_MASK; /* paranoia */
+
+ rs->sr_type = REP_INTERMEDIATE;
+ rs->sr_tag = LDAP_RES_INTERMEDIATE;
+ rs->sr_msgid = op->o_msgid;
+ if ( send_ldap_response( op, rs ) == SLAP_CB_CONTINUE ) {
+ Debug( LDAP_DEBUG_STATS2,
+ "%s INTERMEDIATE oid=%s\n",
+ op->o_log_prefix,
+ rs->sr_rspoid ? rs->sr_rspoid : "" );
+ }
+}
+
+#define set_ldap_error( rs, err, text ) do { \
+ (rs)->sr_err = err; (rs)->sr_text = text; } while(0)
+
+/*
+ * returns:
+ *
+ * LDAP_SUCCESS entry sent
+ * LDAP_OTHER entry not sent (other)
+ * LDAP_INSUFFICIENT_ACCESS entry not sent (ACL)
+ * LDAP_UNAVAILABLE entry not sent (connection closed)
+ * LDAP_SIZELIMIT_EXCEEDED entry not sent (caller must send sizelimitExceeded)
+ */
+
+int
+slap_send_search_entry( Operation *op, SlapReply *rs )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *) &berbuf;
+ Attribute *a;
+ int i, j, rc = LDAP_UNAVAILABLE, bytes;
+ int userattrs;
+ AccessControlState acl_state = ACL_STATE_INIT;
+ int attrsonly;
+ AttributeDescription *ad_entry = slap_schema.si_ad_entry;
+
+ /* a_flags: array of flags telling if the i-th element will be
+ * returned or filtered out
+ * e_flags: array of a_flags
+ */
+ char **e_flags = NULL;
+
+ rs->sr_type = REP_SEARCH;
+
+ if ( op->ors_slimit >= 0 && rs->sr_nentries >= op->ors_slimit ) {
+ rc = LDAP_SIZELIMIT_EXCEEDED;
+ goto error_return;
+ }
+
+ /* Every 64 entries, check for thread pool pause */
+ if ( ( ( rs->sr_nentries & 0x3f ) == 0x3f ) &&
+ ldap_pvt_thread_pool_pausing( &connection_pool ) > 0 )
+ {
+ rc = LDAP_BUSY;
+ goto error_return;
+ }
+
+ /* eventually will loop through generated operational attribute types
+ * currently implemented types include:
+ * entryDN, subschemaSubentry, and hasSubordinates */
+ /* NOTE: moved before overlays callback circling because
+ * they may modify entry and other stuff in rs */
+ /* check for special all operational attributes ("+") type */
+ /* FIXME: maybe we could set this flag at the operation level;
+ * however, in principle the caller of send_search_entry() may
+ * change the attribute list at each call */
+ rs->sr_attr_flags = slap_attr_flags( rs->sr_attrs );
+
+ rc = backend_operational( op, rs );
+ if ( rc ) {
+ goto error_return;
+ }
+
+ if ( op->o_callback ) {
+ rc = slap_response_play( op, rs );
+ if ( rc != SLAP_CB_CONTINUE ) {
+ goto error_return;
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "=> send_search_entry: conn %lu dn=\"%s\"%s\n",
+ op->o_connid, rs->sr_entry->e_name.bv_val,
+ op->ors_attrsonly ? " (attrsOnly)" : "" );
+
+ attrsonly = op->ors_attrsonly;
+
+ if ( !access_allowed( op, rs->sr_entry, ad_entry, NULL, ACL_READ, NULL )) {
+ Debug( LDAP_DEBUG_ACL,
+ "send_search_entry: conn %lu access to entry (%s) not allowed\n",
+ op->o_connid, rs->sr_entry->e_name.bv_val );
+
+ rc = LDAP_INSUFFICIENT_ACCESS;
+ goto error_return;
+ }
+
+ if ( op->o_res_ber ) {
+ /* read back control or LDAP_CONNECTIONLESS */
+ ber = op->o_res_ber;
+ } else {
+ struct berval bv;
+
+ bv.bv_len = entry_flatsize( rs->sr_entry, 0 );
+ bv.bv_val = op->o_tmpalloc( bv.bv_len, op->o_tmpmemctx );
+
+ ber_init2( ber, &bv, LBER_USE_DER );
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ if ( op->o_conn && op->o_conn->c_is_udp ) {
+ /* CONNECTIONLESS */
+ if ( op->o_protocol == LDAP_VERSION2 ) {
+ rc = ber_printf(ber, "t{O{" /*}}*/,
+ LDAP_RES_SEARCH_ENTRY, &rs->sr_entry->e_name );
+ } else {
+ rc = ber_printf( ber, "{it{O{" /*}}}*/, op->o_msgid,
+ LDAP_RES_SEARCH_ENTRY, &rs->sr_entry->e_name );
+ }
+ } else
+#endif
+ if ( op->o_res_ber ) {
+ /* read back control */
+ rc = ber_printf( ber, "t{O{" /*}}*/,
+ LDAP_RES_SEARCH_ENTRY, &rs->sr_entry->e_name );
+ } else {
+ rc = ber_printf( ber, "{it{O{" /*}}}*/, op->o_msgid,
+ LDAP_RES_SEARCH_ENTRY, &rs->sr_entry->e_name );
+ }
+
+ if ( rc == -1 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "send_search_entry: conn %lu ber_printf failed\n",
+ op->o_connid );
+
+ if ( op->o_res_ber == NULL ) ber_free_buf( ber );
+ set_ldap_error( rs, LDAP_OTHER, "encoding DN error" );
+ rc = rs->sr_err;
+ goto error_return;
+ }
+
+ /* check for special all user attributes ("*") type */
+ userattrs = SLAP_USERATTRS( rs->sr_attr_flags );
+
+ /* create an array of arrays of flags. Each flag corresponds
+ * to particular value of attribute and equals 1 if value matches
+ * to ValuesReturnFilter or 0 if not
+ */
+ if ( op->o_vrFilter != NULL ) {
+ int k = 0;
+ size_t size;
+
+ for ( a = rs->sr_entry->e_attrs, i=0; a != NULL; a = a->a_next, i++ ) {
+ for ( j = 0; a->a_vals[j].bv_val != NULL; j++ ) k++;
+ }
+
+ size = i * sizeof(char *) + k;
+ if ( size > 0 ) {
+ char *a_flags;
+ e_flags = slap_sl_calloc ( 1, i * sizeof(char *) + k, op->o_tmpmemctx );
+ if( e_flags == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "send_search_entry: conn %lu slap_sl_calloc failed\n",
+ op->o_connid );
+ ber_free( ber, 1 );
+
+ set_ldap_error( rs, LDAP_OTHER, "out of memory" );
+ goto error_return;
+ }
+ a_flags = (char *)(e_flags + i);
+ memset( a_flags, 0, k );
+ for ( a=rs->sr_entry->e_attrs, i=0; a != NULL; a=a->a_next, i++ ) {
+ for ( j = 0; a->a_vals[j].bv_val != NULL; j++ );
+ e_flags[i] = a_flags;
+ a_flags += j;
+ }
+
+ rc = filter_matched_values(op, rs->sr_entry->e_attrs, &e_flags) ;
+ if ( rc == -1 ) {
+ Debug( LDAP_DEBUG_ANY, "send_search_entry: "
+ "conn %lu matched values filtering failed\n",
+ op->o_connid );
+ if ( op->o_res_ber == NULL ) ber_free_buf( ber );
+ set_ldap_error( rs, LDAP_OTHER,
+ "matched values filtering error" );
+ rc = rs->sr_err;
+ goto error_return;
+ }
+ }
+ }
+
+ for ( a = rs->sr_entry->e_attrs, j = 0; a != NULL; a = a->a_next, j++ ) {
+ AttributeDescription *desc = a->a_desc;
+ int finish = 0;
+
+ if ( rs->sr_attrs == NULL ) {
+ /* all user attrs request, skip operational attributes */
+ if( is_at_operational( desc->ad_type ) ) {
+ continue;
+ }
+
+ } else {
+ /* specific attrs requested */
+ if ( is_at_operational( desc->ad_type ) ) {
+ /* if not explicitly requested */
+ if ( !ad_inlist( desc, rs->sr_attrs )) {
+ /* if not all op attrs requested, skip */
+ if ( !SLAP_OPATTRS( rs->sr_attr_flags ))
+ continue;
+ /* if DSA-specific and replicating, skip */
+ if ( op->o_sync != SLAP_CONTROL_NONE &&
+ desc->ad_type->sat_usage == LDAP_SCHEMA_DSA_OPERATION )
+ continue;
+ }
+ } else {
+ if ( !userattrs && !ad_inlist( desc, rs->sr_attrs ) ) {
+ continue;
+ }
+ }
+ }
+
+ if ( attrsonly ) {
+ if ( ! access_allowed( op, rs->sr_entry, desc, NULL,
+ ACL_READ, &acl_state ) )
+ {
+ Debug( LDAP_DEBUG_ACL, "send_search_entry: "
+ "conn %lu access to attribute %s not allowed\n",
+ op->o_connid, desc->ad_cname.bv_val );
+ continue;
+ }
+
+ if (( rc = ber_printf( ber, "{O[" /*]}*/ , &desc->ad_cname )) == -1 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "send_search_entry: conn %lu ber_printf failed\n",
+ op->o_connid );
+
+ if ( op->o_res_ber == NULL ) ber_free_buf( ber );
+ set_ldap_error( rs, LDAP_OTHER,
+ "encoding description error");
+ rc = rs->sr_err;
+ goto error_return;
+ }
+ finish = 1;
+
+ } else {
+ int first = 1;
+ for ( i = 0; a->a_nvals[i].bv_val != NULL; i++ ) {
+ if ( ! access_allowed( op, rs->sr_entry,
+ desc, &a->a_nvals[i], ACL_READ, &acl_state ) )
+ {
+ Debug( LDAP_DEBUG_ACL,
+ "send_search_entry: conn %lu "
+ "access to attribute %s, value #%d not allowed\n",
+ op->o_connid, desc->ad_cname.bv_val, i );
+
+ continue;
+ }
+
+ if ( op->o_vrFilter && e_flags[j][i] == 0 ){
+ continue;
+ }
+
+ if ( first ) {
+ first = 0;
+ finish = 1;
+ if (( rc = ber_printf( ber, "{O[" /*]}*/ , &desc->ad_cname )) == -1 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "send_search_entry: conn %lu ber_printf failed\n",
+ op->o_connid );
+
+ if ( op->o_res_ber == NULL ) ber_free_buf( ber );
+ set_ldap_error( rs, LDAP_OTHER,
+ "encoding description error");
+ rc = rs->sr_err;
+ goto error_return;
+ }
+ }
+ if (( rc = ber_printf( ber, "O", &a->a_vals[i] )) == -1 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "send_search_entry: conn %lu "
+ "ber_printf failed.\n", op->o_connid );
+
+ if ( op->o_res_ber == NULL ) ber_free_buf( ber );
+ set_ldap_error( rs, LDAP_OTHER,
+ "encoding values error" );
+ rc = rs->sr_err;
+ goto error_return;
+ }
+ }
+ }
+
+ if ( finish && ( rc = ber_printf( ber, /*{[*/ "]N}" )) == -1 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "send_search_entry: conn %lu ber_printf failed\n",
+ op->o_connid );
+
+ if ( op->o_res_ber == NULL ) ber_free_buf( ber );
+ set_ldap_error( rs, LDAP_OTHER, "encode end error" );
+ rc = rs->sr_err;
+ goto error_return;
+ }
+ }
+
+ /* NOTE: moved before overlays callback circling because
+ * they may modify entry and other stuff in rs */
+ if ( rs->sr_operational_attrs != NULL && op->o_vrFilter != NULL ) {
+ int k = 0;
+ size_t size;
+
+ for ( a = rs->sr_operational_attrs, i=0; a != NULL; a = a->a_next, i++ ) {
+ for ( j = 0; a->a_vals[j].bv_val != NULL; j++ ) k++;
+ }
+
+ size = i * sizeof(char *) + k;
+ if ( size > 0 ) {
+ char *a_flags, **tmp;
+
+ /*
+ * Reuse previous memory - we likely need less space
+ * for operational attributes
+ */
+ tmp = slap_sl_realloc( e_flags, i * sizeof(char *) + k,
+ op->o_tmpmemctx );
+ if ( tmp == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "send_search_entry: conn %lu "
+ "not enough memory "
+ "for matched values filtering\n",
+ op->o_connid );
+ if ( op->o_res_ber == NULL ) ber_free_buf( ber );
+ set_ldap_error( rs, LDAP_OTHER,
+ "not enough memory for matched values filtering" );
+ goto error_return;
+ }
+ e_flags = tmp;
+ a_flags = (char *)(e_flags + i);
+ memset( a_flags, 0, k );
+ for ( a = rs->sr_operational_attrs, i=0; a != NULL; a = a->a_next, i++ ) {
+ for ( j = 0; a->a_vals[j].bv_val != NULL; j++ );
+ e_flags[i] = a_flags;
+ a_flags += j;
+ }
+ rc = filter_matched_values(op, rs->sr_operational_attrs, &e_flags) ;
+
+ if ( rc == -1 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "send_search_entry: conn %lu "
+ "matched values filtering failed\n",
+ op->o_connid );
+ if ( op->o_res_ber == NULL ) ber_free_buf( ber );
+ set_ldap_error( rs, LDAP_OTHER,
+ "matched values filtering error" );
+ rc = rs->sr_err;
+ goto error_return;
+ }
+ }
+ }
+
+ for (a = rs->sr_operational_attrs, j=0; a != NULL; a = a->a_next, j++ ) {
+ AttributeDescription *desc = a->a_desc;
+
+ if ( rs->sr_attrs == NULL ) {
+ /* all user attrs request, skip operational attributes */
+ if( is_at_operational( desc->ad_type ) ) {
+ continue;
+ }
+
+ } else {
+ /* specific attrs requested */
+ if( is_at_operational( desc->ad_type ) ) {
+ if ( !SLAP_OPATTRS( rs->sr_attr_flags ) &&
+ !ad_inlist( desc, rs->sr_attrs ) )
+ {
+ continue;
+ }
+ /* if DSA-specific and replicating, skip */
+ if ( op->o_sync != SLAP_CONTROL_NONE &&
+ desc->ad_type->sat_usage == LDAP_SCHEMA_DSA_OPERATION )
+ continue;
+ } else {
+ if ( !userattrs && !ad_inlist( desc, rs->sr_attrs ) ) {
+ continue;
+ }
+ }
+ }
+
+ if ( ! access_allowed( op, rs->sr_entry, desc, NULL,
+ ACL_READ, &acl_state ) )
+ {
+ Debug( LDAP_DEBUG_ACL,
+ "send_search_entry: conn %lu "
+ "access to attribute %s not allowed\n",
+ op->o_connid, desc->ad_cname.bv_val );
+
+ continue;
+ }
+
+ rc = ber_printf( ber, "{O[" /*]}*/ , &desc->ad_cname );
+ if ( rc == -1 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "send_search_entry: conn %lu "
+ "ber_printf failed\n", op->o_connid );
+
+ if ( op->o_res_ber == NULL ) ber_free_buf( ber );
+ set_ldap_error( rs, LDAP_OTHER,
+ "encoding description error" );
+ rc = rs->sr_err;
+ goto error_return;
+ }
+
+ if ( ! attrsonly ) {
+ for ( i = 0; a->a_vals[i].bv_val != NULL; i++ ) {
+ if ( ! access_allowed( op, rs->sr_entry,
+ desc, &a->a_vals[i], ACL_READ, &acl_state ) )
+ {
+ Debug( LDAP_DEBUG_ACL,
+ "send_search_entry: conn %lu "
+ "access to %s, value %d not allowed\n",
+ op->o_connid, desc->ad_cname.bv_val, i );
+
+ continue;
+ }
+
+ if ( op->o_vrFilter && e_flags[j][i] == 0 ){
+ continue;
+ }
+
+ if (( rc = ber_printf( ber, "O", &a->a_vals[i] )) == -1 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "send_search_entry: conn %lu ber_printf failed\n",
+ op->o_connid );
+
+ if ( op->o_res_ber == NULL ) ber_free_buf( ber );
+ set_ldap_error( rs, LDAP_OTHER,
+ "encoding values error" );
+ rc = rs->sr_err;
+ goto error_return;
+ }
+ }
+ }
+
+ if (( rc = ber_printf( ber, /*{[*/ "]N}" )) == -1 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "send_search_entry: conn %lu ber_printf failed\n",
+ op->o_connid );
+
+ if ( op->o_res_ber == NULL ) ber_free_buf( ber );
+ set_ldap_error( rs, LDAP_OTHER, "encode end error" );
+ rc = rs->sr_err;
+ goto error_return;
+ }
+ }
+
+ /* free e_flags */
+ if ( e_flags ) {
+ slap_sl_free( e_flags, op->o_tmpmemctx );
+ e_flags = NULL;
+ }
+
+ rc = ber_printf( ber, /*{{*/ "}N}" );
+
+ if( rc != -1 ) {
+ rc = send_ldap_controls( op, ber, rs->sr_ctrls );
+ }
+
+ if( rc != -1 ) {
+#ifdef LDAP_CONNECTIONLESS
+ if( op->o_conn && op->o_conn->c_is_udp ) {
+ if ( op->o_protocol != LDAP_VERSION2 ) {
+ rc = ber_printf( ber, /*{*/ "N}" );
+ }
+ } else
+#endif
+ if ( op->o_res_ber == NULL ) {
+ rc = ber_printf( ber, /*{*/ "N}" );
+ }
+ }
+
+ if ( rc == -1 ) {
+ Debug( LDAP_DEBUG_ANY, "ber_printf failed\n" );
+
+ if ( op->o_res_ber == NULL ) ber_free_buf( ber );
+ set_ldap_error( rs, LDAP_OTHER, "encode entry end error" );
+ rc = rs->sr_err;
+ goto error_return;
+ }
+
+ Debug( LDAP_DEBUG_STATS2, "%s ENTRY dn=\"%s\"\n",
+ op->o_log_prefix, rs->sr_entry->e_nname.bv_val );
+
+ rs_flush_entry( op, rs, NULL );
+
+ if ( op->o_res_ber == NULL ) {
+ bytes = send_ldap_ber( op, ber );
+ ber_free_buf( ber );
+
+ if ( bytes < 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "send_search_entry: conn %lu ber write failed.\n",
+ op->o_connid );
+
+ rc = LDAP_UNAVAILABLE;
+ goto error_return;
+ }
+ rs->sr_nentries++;
+
+ ldap_pvt_thread_mutex_lock( &op->o_counters->sc_mutex );
+ ldap_pvt_mp_add_ulong( op->o_counters->sc_bytes, (unsigned long)bytes );
+ ldap_pvt_mp_add_ulong( op->o_counters->sc_entries, 1 );
+ ldap_pvt_mp_add_ulong( op->o_counters->sc_pdu, 1 );
+ ldap_pvt_thread_mutex_unlock( &op->o_counters->sc_mutex );
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<= send_search_entry: conn %lu exit.\n", op->o_connid );
+
+ rc = LDAP_SUCCESS;
+
+error_return:;
+ if ( op->o_callback ) {
+ (void)slap_cleanup_play( op, rs );
+ }
+
+ if ( e_flags ) {
+ slap_sl_free( e_flags, op->o_tmpmemctx );
+ }
+
+ /* FIXME: Can break if rs now contains an extended response */
+ if ( rs->sr_operational_attrs ) {
+ attrs_free( rs->sr_operational_attrs );
+ rs->sr_operational_attrs = NULL;
+ }
+ rs->sr_attr_flags = SLAP_ATTRS_UNDEFINED;
+
+ if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH ) {
+ rs_flush_entry( op, rs, NULL );
+ } else {
+ RS_ASSERT( (rs->sr_flags & REP_ENTRY_MASK) == 0 );
+ }
+
+ if ( rs->sr_flags & REP_CTRLS_MUSTBEFREED ) {
+ rs->sr_flags ^= REP_CTRLS_MUSTBEFREED; /* paranoia */
+ if ( rs->sr_ctrls ) {
+ slap_free_ctrls( op, rs->sr_ctrls );
+ rs->sr_ctrls = NULL;
+ }
+ }
+
+ return( rc );
+}
+
+int
+slap_send_search_reference( Operation *op, SlapReply *rs )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *) &berbuf;
+ int rc = 0;
+ int bytes;
+ char *edn = rs->sr_entry ? rs->sr_entry->e_name.bv_val : "(null)";
+
+ AttributeDescription *ad_ref = slap_schema.si_ad_ref;
+ AttributeDescription *ad_entry = slap_schema.si_ad_entry;
+
+ rs->sr_type = REP_SEARCHREF;
+ if ( op->o_callback ) {
+ rc = slap_response_play( op, rs );
+ if ( rc != SLAP_CB_CONTINUE ) {
+ goto rel;
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> send_search_reference: dn=\"%s\"\n",
+ edn );
+
+ if ( rs->sr_entry && ! access_allowed( op, rs->sr_entry,
+ ad_entry, NULL, ACL_READ, NULL ) )
+ {
+ Debug( LDAP_DEBUG_ACL,
+ "send_search_reference: access to entry not allowed\n" );
+ rc = 1;
+ goto rel;
+ }
+
+ if ( rs->sr_entry && ! access_allowed( op, rs->sr_entry,
+ ad_ref, NULL, ACL_READ, NULL ) )
+ {
+ Debug( LDAP_DEBUG_ACL,
+ "send_search_reference: access "
+ "to reference not allowed\n" );
+ rc = 1;
+ goto rel;
+ }
+
+ if( op->o_domain_scope ) {
+ Debug( LDAP_DEBUG_ANY,
+ "send_search_reference: domainScope control in (%s)\n",
+ edn );
+ rc = 0;
+ goto rel;
+ }
+
+ if( rs->sr_ref == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "send_search_reference: null ref in (%s)\n",
+ edn );
+ rc = 1;
+ goto rel;
+ }
+
+ if( op->o_protocol < LDAP_VERSION3 ) {
+ rc = 0;
+ /* save the references for the result */
+ if( rs->sr_ref[0].bv_val != NULL ) {
+ if( value_add( &rs->sr_v2ref, rs->sr_ref ) )
+ rc = LDAP_OTHER;
+ }
+ goto rel;
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ if( op->o_conn && op->o_conn->c_is_udp ) {
+ ber = op->o_res_ber;
+ } else
+#endif
+ {
+ ber_init_w_nullc( ber, LBER_USE_DER );
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+ }
+
+ rc = ber_printf( ber, "{it{W}" /*"}"*/ , op->o_msgid,
+ LDAP_RES_SEARCH_REFERENCE, rs->sr_ref );
+
+ if( rc != -1 ) {
+ rc = send_ldap_controls( op, ber, rs->sr_ctrls );
+ }
+
+ if( rc != -1 ) {
+ rc = ber_printf( ber, /*"{"*/ "N}" );
+ }
+
+ if ( rc == -1 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "send_search_reference: ber_printf failed\n" );
+
+#ifdef LDAP_CONNECTIONLESS
+ if (!op->o_conn || op->o_conn->c_is_udp == 0)
+#endif
+ ber_free_buf( ber );
+ set_ldap_error( rs, LDAP_OTHER, "encode DN error" );
+ goto rel;
+ }
+
+ rc = 0;
+ rs_flush_entry( op, rs, NULL );
+
+#ifdef LDAP_CONNECTIONLESS
+ if (!op->o_conn || op->o_conn->c_is_udp == 0) {
+#endif
+ bytes = send_ldap_ber( op, ber );
+ ber_free_buf( ber );
+
+ if ( bytes < 0 ) {
+ rc = LDAP_UNAVAILABLE;
+ } else {
+ ldap_pvt_thread_mutex_lock( &op->o_counters->sc_mutex );
+ ldap_pvt_mp_add_ulong( op->o_counters->sc_bytes, (unsigned long)bytes );
+ ldap_pvt_mp_add_ulong( op->o_counters->sc_refs, 1 );
+ ldap_pvt_mp_add_ulong( op->o_counters->sc_pdu, 1 );
+ ldap_pvt_thread_mutex_unlock( &op->o_counters->sc_mutex );
+ }
+#ifdef LDAP_CONNECTIONLESS
+ }
+#endif
+ if ( rs->sr_ref != NULL ) {
+ int r;
+
+ for ( r = 0; !BER_BVISNULL( &rs->sr_ref[ r ] ); r++ ) {
+ Debug( LDAP_DEBUG_STATS2, "%s REF #%d \"%s\"\n",
+ op->o_log_prefix, r, rs->sr_ref[0].bv_val );
+ }
+
+ } else {
+ Debug( LDAP_DEBUG_STATS2, "%s REF \"(null)\"\n",
+ op->o_log_prefix );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<= send_search_reference\n" );
+
+ if ( 0 ) {
+rel:
+ rs_flush_entry( op, rs, NULL );
+ }
+
+ if ( op->o_callback ) {
+ (void)slap_cleanup_play( op, rs );
+ }
+
+ if ( rs->sr_flags & REP_CTRLS_MUSTBEFREED ) {
+ rs->sr_flags ^= REP_CTRLS_MUSTBEFREED; /* paranoia */
+ if ( rs->sr_ctrls ) {
+ slap_free_ctrls( op, rs->sr_ctrls );
+ rs->sr_ctrls = NULL;
+ }
+ }
+
+ return rc;
+}
+
+int
+str2result(
+ char *s,
+ int *code,
+ char **matched,
+ char **info )
+{
+ int rc;
+ char *c;
+
+ *code = LDAP_SUCCESS;
+ *matched = NULL;
+ *info = NULL;
+
+ if ( strncasecmp( s, "RESULT", STRLENOF( "RESULT" ) ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "str2result (%s) expecting \"RESULT\"\n",
+ s );
+
+ return( -1 );
+ }
+
+ rc = 0;
+ while ( (s = strchr( s, '\n' )) != NULL ) {
+ *s++ = '\0';
+ if ( *s == '\0' ) {
+ break;
+ }
+ if ( (c = strchr( s, ':' )) != NULL ) {
+ c++;
+ }
+
+ if ( strncasecmp( s, "code", STRLENOF( "code" ) ) == 0 ) {
+ char *next = NULL;
+ long retcode;
+
+ if ( c == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "str2result (%s) missing value\n",
+ s );
+ rc = -1;
+ continue;
+ }
+
+ while ( isspace( (unsigned char) c[ 0 ] ) ) c++;
+ if ( c[ 0 ] == '\0' ) {
+ Debug( LDAP_DEBUG_ANY, "str2result (%s) missing or empty value\n",
+ s );
+ rc = -1;
+ continue;
+ }
+
+ retcode = strtol( c, &next, 10 );
+ if ( next == NULL || next == c ) {
+ Debug( LDAP_DEBUG_ANY, "str2result (%s) unable to parse value\n",
+ s );
+ rc = -1;
+ continue;
+ }
+
+ while ( isspace( (unsigned char) next[ 0 ] ) && next[ 0 ] != '\n' )
+ next++;
+ if ( next[ 0 ] != '\0' && next[ 0 ] != '\n' ) {
+ Debug( LDAP_DEBUG_ANY, "str2result (%s) extra cruft after value\n",
+ s );
+ rc = -1;
+ continue;
+ }
+
+ /* FIXME: what if it's larger than max int? */
+ *code = (int)retcode;
+
+ } else if ( strncasecmp( s, "matched", STRLENOF( "matched" ) ) == 0 ) {
+ if ( c != NULL ) {
+ *matched = c;
+ }
+ } else if ( strncasecmp( s, "info", STRLENOF( "info" ) ) == 0 ) {
+ if ( c != NULL ) {
+ *info = c;
+ }
+ } else {
+ Debug( LDAP_DEBUG_ANY, "str2result (%s) unknown\n",
+ s );
+
+ rc = -1;
+ }
+ }
+
+ return( rc );
+}
+
+int slap_read_controls(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ const struct berval *oid,
+ LDAPControl **ctrl )
+{
+ int rc;
+ struct berval bv;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *) &berbuf;
+ LDAPControl c;
+ Operation myop;
+
+ Debug( LDAP_DEBUG_ANY, "%s slap_read_controls: (%s) %s\n",
+ op->o_log_prefix, oid->bv_val, e->e_dn );
+
+ rs->sr_entry = e;
+ rs->sr_attrs = ( oid == &slap_pre_read_bv ) ?
+ op->o_preread_attrs : op->o_postread_attrs;
+
+ bv.bv_len = entry_flatsize( rs->sr_entry, 0 );
+ bv.bv_val = op->o_tmpalloc( bv.bv_len, op->o_tmpmemctx );
+
+ ber_init2( ber, &bv, LBER_USE_DER );
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+
+ /* create new operation */
+ myop = *op;
+ /* FIXME: o_bd needed for ACL */
+ myop.o_bd = op->o_bd;
+ myop.o_res_ber = ber;
+ myop.o_callback = NULL;
+ myop.ors_slimit = 1;
+ myop.ors_attrsonly = 0;
+
+ rc = slap_send_search_entry( &myop, rs );
+ if( rc ) return rc;
+
+ rc = ber_flatten2( ber, &c.ldctl_value, 0 );
+
+ if( rc == -1 ) return LDAP_OTHER;
+
+ c.ldctl_oid = oid->bv_val;
+ c.ldctl_iscritical = 0;
+
+ if ( *ctrl == NULL ) {
+ /* first try */
+ *ctrl = (LDAPControl *) slap_sl_calloc( 1, sizeof(LDAPControl), NULL );
+ } else {
+ /* retry: free previous try */
+ slap_sl_free( (*ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ }
+
+ **ctrl = c;
+ return LDAP_SUCCESS;
+}
+
+/* Map API errors to protocol errors... */
+int
+slap_map_api2result( SlapReply *rs )
+{
+ switch(rs->sr_err) {
+ case LDAP_SERVER_DOWN:
+ return LDAP_UNAVAILABLE;
+ case LDAP_LOCAL_ERROR:
+ return LDAP_OTHER;
+ case LDAP_ENCODING_ERROR:
+ case LDAP_DECODING_ERROR:
+ return LDAP_PROTOCOL_ERROR;
+ case LDAP_TIMEOUT:
+ return LDAP_UNAVAILABLE;
+ case LDAP_AUTH_UNKNOWN:
+ return LDAP_AUTH_METHOD_NOT_SUPPORTED;
+ case LDAP_FILTER_ERROR:
+ rs->sr_text = "Filter error";
+ return LDAP_OTHER;
+ case LDAP_USER_CANCELLED:
+ rs->sr_text = "User cancelled";
+ return LDAP_OTHER;
+ case LDAP_PARAM_ERROR:
+ return LDAP_PROTOCOL_ERROR;
+ case LDAP_NO_MEMORY:
+ return LDAP_OTHER;
+ case LDAP_CONNECT_ERROR:
+ return LDAP_UNAVAILABLE;
+ case LDAP_NOT_SUPPORTED:
+ return LDAP_UNWILLING_TO_PERFORM;
+ case LDAP_CONTROL_NOT_FOUND:
+ return LDAP_PROTOCOL_ERROR;
+ case LDAP_NO_RESULTS_RETURNED:
+ return LDAP_NO_SUCH_OBJECT;
+ case LDAP_MORE_RESULTS_TO_RETURN:
+ rs->sr_text = "More results to return";
+ return LDAP_OTHER;
+ case LDAP_CLIENT_LOOP:
+ case LDAP_REFERRAL_LIMIT_EXCEEDED:
+ return LDAP_LOOP_DETECT;
+ default:
+ if ( LDAP_API_ERROR(rs->sr_err) ) return LDAP_OTHER;
+ return rs->sr_err;
+ }
+}
+
+
+slap_mask_t
+slap_attr_flags( AttributeName *an )
+{
+ slap_mask_t flags = SLAP_ATTRS_UNDEFINED;
+
+ if ( an == NULL ) {
+ flags |= ( SLAP_OPATTRS_NO | SLAP_USERATTRS_YES );
+
+ } else {
+ flags |= an_find( an, slap_bv_all_operational_attrs )
+ ? SLAP_OPATTRS_YES : SLAP_OPATTRS_NO;
+ flags |= an_find( an, slap_bv_all_user_attrs )
+ ? SLAP_USERATTRS_YES : SLAP_USERATTRS_NO;
+ }
+
+ return flags;
+}
diff --git a/servers/slapd/root_dse.c b/servers/slapd/root_dse.c
new file mode 100644
index 0000000..368bd9b
--- /dev/null
+++ b/servers/slapd/root_dse.c
@@ -0,0 +1,542 @@
+/* root_dse.c - Provides the Root DSA-Specific Entry */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include <ldif.h>
+#include "lber_pvt.h"
+
+#ifdef LDAP_SLAPI
+#include "slapi/slapi.h"
+#endif
+
+static struct berval builtin_supportedFeatures[] = {
+ BER_BVC(LDAP_FEATURE_MODIFY_INCREMENT), /* Modify/increment */
+ BER_BVC(LDAP_FEATURE_ALL_OP_ATTRS), /* All Op Attrs (+) */
+ BER_BVC(LDAP_FEATURE_OBJECTCLASS_ATTRS), /* OCs in Attrs List (@class) */
+ BER_BVC(LDAP_FEATURE_ABSOLUTE_FILTERS), /* (&) and (|) search filters */
+ BER_BVC(LDAP_FEATURE_LANGUAGE_TAG_OPTIONS), /* Language Tag Options */
+ BER_BVC(LDAP_FEATURE_LANGUAGE_RANGE_OPTIONS), /* Language Range Options */
+#ifdef LDAP_DEVEL
+ BER_BVC(LDAP_FEATURE_SUBORDINATE_SCOPE), /* "children" search scope */
+#endif
+ BER_BVNULL
+};
+static struct berval *supportedFeatures;
+
+static Entry *usr_attr = NULL;
+
+/*
+ * allow modules to register functions that muck with the root DSE entry
+ */
+
+typedef struct entry_info_t {
+ SLAP_ENTRY_INFO_FN func;
+ void *arg;
+ struct entry_info_t *next;
+} entry_info_t;
+
+static entry_info_t *extra_info;
+
+int
+entry_info_register( SLAP_ENTRY_INFO_FN func, void *arg )
+{
+ entry_info_t *ei = ch_calloc( 1, sizeof( entry_info_t ) );
+
+ ei->func = func;
+ ei->arg = arg;
+
+ ei->next = extra_info;
+ extra_info = ei;
+
+ return 0;
+}
+
+int
+entry_info_unregister( SLAP_ENTRY_INFO_FN func, void *arg )
+{
+ entry_info_t **eip;
+
+ for ( eip = &extra_info; *eip != NULL; eip = &(*eip)->next ) {
+ if ( (*eip)->func == func && (*eip)->arg == arg ) {
+ entry_info_t *ei = *eip;
+
+ *eip = ei->next;
+
+ ch_free( ei );
+
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+void
+entry_info_destroy( void )
+{
+ entry_info_t **eip;
+
+ for ( eip = &extra_info; *eip != NULL; ) {
+ entry_info_t *ei = *eip;
+
+ eip = &(*eip)->next;
+
+ ch_free( ei );
+ }
+}
+
+/*
+ * Allow modules to register supported features
+ */
+
+static int
+supported_feature_init( void )
+{
+ int i;
+
+ if ( supportedFeatures != NULL ) {
+ return 0;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &builtin_supportedFeatures[ i ] ); i++ )
+ ;
+
+ supportedFeatures = ch_calloc( sizeof( struct berval ), i + 1 );
+ if ( supportedFeatures == NULL ) {
+ return -1;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &builtin_supportedFeatures[ i ] ); i++ ) {
+ ber_dupbv( &supportedFeatures[ i ], &builtin_supportedFeatures[ i ] );
+ }
+ BER_BVZERO( &supportedFeatures[ i ] );
+
+ return 0;
+}
+
+int
+supported_feature_destroy( void )
+{
+ int i;
+
+ if ( supportedFeatures == NULL ) {
+ return 0;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &supportedFeatures[ i ] ); i++ ) {
+ ch_free( supportedFeatures[ i ].bv_val );
+ }
+
+ ch_free( supportedFeatures );
+ supportedFeatures = NULL;
+
+ return 0;
+}
+
+int
+supported_feature_load( struct berval *f )
+{
+ struct berval *tmp;
+ int i;
+
+ supported_feature_init();
+
+ for ( i = 0; !BER_BVISNULL( &supportedFeatures[ i ] ); i++ )
+ ;
+
+ tmp = ch_realloc( supportedFeatures, sizeof( struct berval ) * ( i + 2 ) );
+ if ( tmp == NULL ) {
+ return -1;
+ }
+ supportedFeatures = tmp;
+
+ ber_dupbv( &supportedFeatures[ i ], f );
+ BER_BVZERO( &supportedFeatures[ i + 1 ] );
+
+ return 0;
+}
+
+int
+root_dse_info(
+ Connection *conn,
+ Entry **entry,
+ const char **text )
+{
+ Entry *e;
+ struct berval val;
+#ifdef LDAP_SLAPI
+ struct berval *bv;
+#endif
+ int i, j;
+ char ** supportedSASLMechanisms;
+ BackendDB *be;
+
+ AttributeDescription *ad_structuralObjectClass
+ = slap_schema.si_ad_structuralObjectClass;
+ AttributeDescription *ad_objectClass
+ = slap_schema.si_ad_objectClass;
+ AttributeDescription *ad_namingContexts
+ = slap_schema.si_ad_namingContexts;
+#ifdef LDAP_SLAPI
+ AttributeDescription *ad_supportedExtension
+ = slap_schema.si_ad_supportedExtension;
+#endif
+ AttributeDescription *ad_supportedLDAPVersion
+ = slap_schema.si_ad_supportedLDAPVersion;
+ AttributeDescription *ad_supportedSASLMechanisms
+ = slap_schema.si_ad_supportedSASLMechanisms;
+ AttributeDescription *ad_supportedFeatures
+ = slap_schema.si_ad_supportedFeatures;
+ AttributeDescription *ad_monitorContext
+ = slap_schema.si_ad_monitorContext;
+ AttributeDescription *ad_configContext
+ = slap_schema.si_ad_configContext;
+ AttributeDescription *ad_ref
+ = slap_schema.si_ad_ref;
+
+ e = entry_alloc();
+ if( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "root_dse_info: entry_alloc failed" );
+ return LDAP_OTHER;
+ }
+
+ e->e_attrs = NULL;
+ e->e_name.bv_val = ch_strdup( LDAP_ROOT_DSE );
+ e->e_name.bv_len = sizeof( LDAP_ROOT_DSE )-1;
+ e->e_nname.bv_val = ch_strdup( LDAP_ROOT_DSE );
+ e->e_nname.bv_len = sizeof( LDAP_ROOT_DSE )-1;
+
+ /* the DN is an empty string so no pretty/normalization is needed */
+ assert( !e->e_name.bv_len );
+ assert( !e->e_nname.bv_len );
+
+ e->e_private = NULL;
+
+ /* FIXME: is this really needed? */
+ BER_BVSTR( &val, "top" );
+ if( attr_merge_one( e, ad_objectClass, &val, NULL ) ) {
+fail:
+ entry_free( e );
+ return LDAP_OTHER;
+ }
+
+ BER_BVSTR( &val, "OpenLDAProotDSE" );
+ if( attr_merge_one( e, ad_objectClass, &val, NULL ) ) {
+ goto fail;
+ }
+ if( attr_merge_one( e, ad_structuralObjectClass, &val, NULL ) ) {
+ goto fail;
+ }
+
+ LDAP_STAILQ_FOREACH( be, &backendDB, be_next ) {
+ if ( be->be_suffix == NULL
+ || be->be_nsuffix == NULL ) {
+ /* no suffix! */
+ continue;
+ }
+ if ( SLAP_DBHIDDEN( be )) {
+ continue;
+ }
+ if ( SLAP_MONITOR( be )) {
+ if( attr_merge_one( e, ad_monitorContext,
+ &be->be_suffix[0],
+ &be->be_nsuffix[0] ) )
+ {
+ goto fail;
+ }
+ continue;
+ }
+ if ( SLAP_CONFIG( be )) {
+ if( attr_merge_one( e, ad_configContext,
+ &be->be_suffix[0],
+ & be->be_nsuffix[0] ) )
+ {
+ goto fail;
+ }
+ continue;
+ }
+ if ( SLAP_GLUE_SUBORDINATE( be ) && !SLAP_GLUE_ADVERTISE( be ) ) {
+ continue;
+ }
+ if ( attr_merge( e, ad_namingContexts,
+ be->be_suffix, be->be_nsuffix ) ) {
+ goto fail;
+ }
+ }
+
+ /* altServer unsupported */
+
+ /* supportedControl */
+ if ( controls_root_dse_info( e ) != 0 ) {
+ goto fail;
+ }
+
+ /* supportedExtension */
+ if ( exop_root_dse_info( e ) != 0 ) {
+ goto fail;
+ }
+
+#ifdef LDAP_SLAPI
+ /* netscape supportedExtension */
+ for ( i = 0; (bv = slapi_int_get_supported_extop(i)) != NULL; i++ ) {
+ if( attr_merge_one( e, ad_supportedExtension, bv, NULL ) ) {
+ goto fail;
+ }
+ }
+#endif /* LDAP_SLAPI */
+
+ /* supportedFeatures */
+ if ( supportedFeatures == NULL ) {
+ supported_feature_init();
+ }
+
+ if( attr_merge( e, ad_supportedFeatures, supportedFeatures, NULL ) ) {
+ goto fail;
+ }
+
+ /* supportedLDAPVersion */
+ /* don't publish version 2 as we don't really support it
+ * (even when configured to accept version 2 Bind requests)
+ * and the value would never be used by true LDAPv2 (or LDAPv3)
+ * clients.
+ */
+ for ( i=LDAP_VERSION3; i<=LDAP_VERSION_MAX; i++ ) {
+ char buf[sizeof("255")];
+ snprintf(buf, sizeof buf, "%d", i);
+ val.bv_val = buf;
+ val.bv_len = strlen( val.bv_val );
+ if( attr_merge_one( e, ad_supportedLDAPVersion, &val, NULL ) ) {
+ goto fail;
+ }
+ }
+
+ /* supportedSASLMechanism */
+ supportedSASLMechanisms = slap_sasl_mechs( conn );
+
+ if( supportedSASLMechanisms != NULL ) {
+ for ( i=0; supportedSASLMechanisms[i] != NULL; i++ ) {
+ val.bv_val = supportedSASLMechanisms[i];
+ val.bv_len = strlen( val.bv_val );
+ if( attr_merge_one( e, ad_supportedSASLMechanisms, &val, NULL ) ) {
+ ldap_charray_free( supportedSASLMechanisms );
+ goto fail;
+ }
+ }
+ ldap_charray_free( supportedSASLMechanisms );
+ }
+
+ if ( default_referral != NULL ) {
+ if( attr_merge( e, ad_ref, default_referral, NULL /* FIXME */ ) ) {
+ goto fail;
+ }
+ }
+
+ if( usr_attr != NULL) {
+ Attribute *a;
+ for( a = usr_attr->e_attrs; a != NULL; a = a->a_next ) {
+ if( attr_merge( e, a->a_desc, a->a_vals,
+ (a->a_nvals == a->a_vals) ? NULL : a->a_nvals ) )
+ {
+ goto fail;
+ }
+ }
+ }
+
+ if ( extra_info ) {
+ entry_info_t *ei = extra_info;
+
+ for ( ; ei; ei = ei->next ) {
+ ei->func( ei->arg, e );
+ }
+ }
+
+ *entry = e;
+ return LDAP_SUCCESS;
+}
+
+int
+root_dse_init( void )
+{
+ return 0;
+}
+
+int
+root_dse_destroy( void )
+{
+ if ( usr_attr ) {
+ entry_free( usr_attr );
+ usr_attr = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * Read the entries specified in fname and merge the attributes
+ * to the user defined rootDSE. Note thaat if we find any errors
+ * what so ever, we will discard the entire entries, print an
+ * error message and return.
+ */
+int
+root_dse_read_file( const char *fname )
+{
+ struct LDIFFP *fp;
+ int rc = 0, lmax = 0, ldifrc;
+ unsigned long lineno = 0;
+ char *buf = NULL;
+
+ if ( (fp = ldif_open( fname, "r" )) == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "root_dse_read_file: could not open rootdse attr file \"%s\" - absolute path?\n",
+ fname );
+ perror( fname );
+ return EXIT_FAILURE;
+ }
+
+ usr_attr = entry_alloc();
+ if( usr_attr == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "root_dse_read_file: entry_alloc failed" );
+ ldif_close( fp );
+ return LDAP_OTHER;
+ }
+ usr_attr->e_attrs = NULL;
+
+ while(( ldifrc = ldif_read_record( fp, &lineno, &buf, &lmax )) > 0 ) {
+ Entry *e = str2entry( buf );
+ Attribute *a;
+
+ if( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "root_dse_read_file: "
+ "could not parse entry (file=\"%s\" line=%lu)\n",
+ fname, lineno );
+ rc = LDAP_OTHER;
+ break;
+ }
+
+ /* make sure the DN is the empty DN */
+ if( e->e_nname.bv_len ) {
+ Debug( LDAP_DEBUG_ANY,
+ "root_dse_read_file: invalid rootDSE "
+ "- dn=\"%s\" (file=\"%s\" line=%lu)\n",
+ e->e_dn, fname, lineno );
+ entry_free( e );
+ rc = LDAP_OTHER;
+ break;
+ }
+
+ /*
+ * we found a valid entry, so walk thru all the attributes in the
+ * entry, and add each attribute type and description to the
+ * usr_attr entry
+ */
+
+ for(a = e->e_attrs; a != NULL; a = a->a_next) {
+ if( attr_merge( usr_attr, a->a_desc, a->a_vals,
+ (a->a_nvals == a->a_vals) ? NULL : a->a_nvals ) )
+ {
+ rc = LDAP_OTHER;
+ break;
+ }
+ }
+
+ entry_free( e );
+ if (rc) break;
+ }
+
+ if ( ldifrc < 0 )
+ rc = LDAP_OTHER;
+
+ if (rc) {
+ entry_free( usr_attr );
+ usr_attr = NULL;
+ }
+
+ ch_free( buf );
+
+ ldif_close( fp );
+
+ Debug(LDAP_DEBUG_CONFIG, "rootDSE file=\"%s\" read.\n", fname );
+ return rc;
+}
+
+int
+slap_discover_feature(
+ slap_bindconf *sb,
+ const char *attr,
+ const char *val )
+{
+ LDAP *ld = NULL;
+ LDAPMessage *res = NULL, *entry;
+ int rc, i;
+ struct berval bv_val,
+ **values = NULL;
+ char *attrs[ 2 ] = { NULL, NULL };
+
+ rc = slap_client_connect( &ld, sb );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ attrs[ 0 ] = (char *) attr;
+ rc = ldap_search_ext_s( ld, "", LDAP_SCOPE_BASE, "(objectClass=*)",
+ attrs, 0, NULL, NULL, NULL, 0, &res );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ entry = ldap_first_entry( ld, res );
+ if ( entry == NULL ) {
+ goto done;
+ }
+
+ values = ldap_get_values_len( ld, entry, attrs[ 0 ] );
+ if ( values == NULL ) {
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ goto done;
+ }
+
+ ber_str2bv( val, 0, 0, &bv_val );
+ for ( i = 0; values[ i ] != NULL; i++ ) {
+ if ( bvmatch( &bv_val, values[ i ] ) ) {
+ rc = LDAP_COMPARE_TRUE;
+ goto done;
+ }
+ }
+
+ rc = LDAP_COMPARE_FALSE;
+
+done:;
+ if ( values != NULL ) {
+ ldap_value_free_len( values );
+ }
+
+ if ( res != NULL ) {
+ ldap_msgfree( res );
+ }
+
+ ldap_unbind_ext( ld, NULL, NULL );
+
+ return rc;
+}
+
diff --git a/servers/slapd/sasl.c b/servers/slapd/sasl.c
new file mode 100644
index 0000000..1e17304
--- /dev/null
+++ b/servers/slapd/sasl.c
@@ -0,0 +1,2050 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include <ac/stdlib.h>
+#include <ac/string.h>
+
+#include <lber.h>
+#include <ldap_log.h>
+
+#include "slap.h"
+
+#include <rewrite.h>
+
+#ifdef HAVE_CYRUS_SASL
+# ifdef HAVE_SASL_SASL_H
+# include <sasl/sasl.h>
+# include <sasl/saslplug.h>
+# else
+# include <sasl.h>
+# include <saslplug.h>
+# endif
+
+# define SASL_CONST const
+
+#define SASL_VERSION_FULL ((SASL_VERSION_MAJOR << 16) |\
+ (SASL_VERSION_MINOR << 8) | SASL_VERSION_STEP)
+
+#if SASL_VERSION_FULL >= 0x020119 /* 2.1.25 */
+typedef sasl_callback_ft slap_sasl_cb_ft;
+#else
+typedef int (*slap_sasl_cb_ft)();
+#endif
+
+static sasl_security_properties_t sasl_secprops;
+#elif defined( SLAP_BUILTIN_SASL )
+/*
+ * built-in SASL implementation
+ * only supports EXTERNAL
+ */
+typedef struct sasl_ctx {
+ slap_ssf_t sc_external_ssf;
+ struct berval sc_external_id;
+} SASL_CTX;
+
+#endif
+
+#include <lutil.h>
+
+static struct berval ext_bv = BER_BVC( "EXTERNAL" );
+
+char *slap_sasl_auxprops;
+
+#ifdef HAVE_CYRUS_SASL
+
+/* Just use our internal auxprop by default */
+static int
+slap_sasl_getopt(
+ void *context,
+ const char *plugin_name,
+ const char *option,
+ const char **result,
+ unsigned *len)
+{
+ if ( strcmp( option, "auxprop_plugin" )) {
+ return SASL_FAIL;
+ }
+ if ( slap_sasl_auxprops )
+ *result = slap_sasl_auxprops;
+ else
+ *result = "slapd";
+ return SASL_OK;
+}
+
+int
+slap_sasl_log(
+ void *context,
+ int priority,
+ const char *message)
+{
+ Connection *conn = context;
+ int level;
+ const char * label;
+
+ if ( message == NULL ) {
+ return SASL_BADPARAM;
+ }
+
+ switch (priority) {
+ case SASL_LOG_NONE:
+ level = LDAP_DEBUG_NONE;
+ label = "None";
+ break;
+ case SASL_LOG_ERR:
+ level = LDAP_DEBUG_ANY;
+ label = "Error";
+ break;
+ case SASL_LOG_FAIL:
+ level = LDAP_DEBUG_ANY;
+ label = "Failure";
+ break;
+ case SASL_LOG_WARN:
+ level = LDAP_DEBUG_TRACE;
+ label = "Warning";
+ break;
+ case SASL_LOG_NOTE:
+ level = LDAP_DEBUG_TRACE;
+ label = "Notice";
+ break;
+ case SASL_LOG_DEBUG:
+ level = LDAP_DEBUG_TRACE;
+ label = "Debug";
+ break;
+ case SASL_LOG_TRACE:
+ level = LDAP_DEBUG_TRACE;
+ label = "Trace";
+ break;
+ case SASL_LOG_PASS:
+ level = LDAP_DEBUG_TRACE;
+ label = "Password Trace";
+ break;
+ default:
+ return SASL_BADPARAM;
+ }
+
+ Debug( level, "SASL [conn=%ld] %s: %s\n",
+ conn ? (long) conn->c_connid: -1L,
+ label, message );
+
+
+ return SASL_OK;
+}
+
+static const char *slap_propnames[] = {
+ "*slapConn", "*slapAuthcDNlen", "*slapAuthcDN",
+ "*slapAuthzDNlen", "*slapAuthzDN", NULL };
+
+#ifdef SLAP_AUXPROP_DONTUSECOPY
+int slap_dontUseCopy_ignore;
+BerVarray slap_dontUseCopy_propnames;
+#endif /* SLAP_AUXPROP_DONTUSECOPY */
+
+static Filter generic_filter = { LDAP_FILTER_PRESENT, { 0 }, NULL };
+static struct berval generic_filterstr = BER_BVC("(objectclass=*)");
+
+#define SLAP_SASL_PROP_CONN 0
+#define SLAP_SASL_PROP_AUTHCLEN 1
+#define SLAP_SASL_PROP_AUTHC 2
+#define SLAP_SASL_PROP_AUTHZLEN 3
+#define SLAP_SASL_PROP_AUTHZ 4
+#define SLAP_SASL_PROP_COUNT 5 /* Number of properties we used */
+
+typedef struct lookup_info {
+ int flags;
+ const struct propval *list;
+ sasl_server_params_t *sparams;
+} lookup_info;
+
+static slap_response sasl_ap_lookup;
+
+static struct berval sc_cleartext = BER_BVC("{CLEARTEXT}");
+
+static int
+sasl_ap_lookup( Operation *op, SlapReply *rs )
+{
+ BerVarray bv;
+ AttributeDescription *ad;
+ Attribute *a;
+ const char *text;
+ int rc, i;
+ lookup_info *sl = (lookup_info *)op->o_callback->sc_private;
+
+ /* return the actual error code,
+ * to allow caller to handle specific errors
+ */
+ if (rs->sr_type != REP_SEARCH) return rs->sr_err;
+
+ for( i = 0; sl->list[i].name; i++ ) {
+ const char *name = sl->list[i].name;
+
+ if ( name[0] == '*' ) {
+ if ( sl->flags & SASL_AUXPROP_AUTHZID ) continue;
+ /* Skip our private properties */
+ if ( !strcmp( name, slap_propnames[0] )) {
+ i += SLAP_SASL_PROP_COUNT - 1;
+ continue;
+ }
+ name++;
+ } else if ( !(sl->flags & SASL_AUXPROP_AUTHZID ) )
+ continue;
+
+ if ( sl->list[i].values ) {
+ if ( !(sl->flags & SASL_AUXPROP_OVERRIDE) ) continue;
+ }
+ ad = NULL;
+ rc = slap_str2ad( name, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "slap_ap_lookup: str2ad(%s): %s\n", name, text );
+ continue;
+ }
+
+ /* If it's the rootdn and a rootpw was present, we already set
+ * it so don't override it here.
+ */
+ if ( ad == slap_schema.si_ad_userPassword && sl->list[i].values &&
+ be_isroot_dn( op->o_bd, &op->o_req_ndn ))
+ continue;
+
+ a = attr_find( rs->sr_entry->e_attrs, ad );
+ if ( !a ) continue;
+ if ( ! access_allowed( op, rs->sr_entry, ad, NULL, ACL_AUTH, NULL ) ) {
+ continue;
+ }
+ if ( sl->list[i].values && ( sl->flags & SASL_AUXPROP_OVERRIDE ) ) {
+ sl->sparams->utils->prop_erase( sl->sparams->propctx,
+ sl->list[i].name );
+ }
+ for ( bv = a->a_vals; bv->bv_val; bv++ ) {
+ /* ITS#3846 don't give hashed passwords to SASL */
+ if ( ad == slap_schema.si_ad_userPassword &&
+ bv->bv_val[0] == '{' /*}*/ )
+ {
+ if ( lutil_passwd_scheme( bv->bv_val ) ) {
+ /* If it's not a recognized scheme, just assume it's
+ * a cleartext password that happened to include brackets.
+ *
+ * If it's a recognized scheme, skip this value, unless the
+ * scheme is {CLEARTEXT}. In that case, skip over the
+ * scheme name and use the remainder. If there is nothing
+ * past the scheme name, skip this value.
+ */
+#ifdef SLAPD_CLEARTEXT
+ if ( !strncasecmp( bv->bv_val, sc_cleartext.bv_val,
+ sc_cleartext.bv_len )) {
+ struct berval cbv;
+ cbv.bv_len = bv->bv_len - sc_cleartext.bv_len;
+ if ( cbv.bv_len > 0 ) {
+ cbv.bv_val = bv->bv_val + sc_cleartext.bv_len;
+ sl->sparams->utils->prop_set( sl->sparams->propctx,
+ sl->list[i].name, cbv.bv_val, cbv.bv_len );
+ }
+ }
+#endif
+ continue;
+ }
+ }
+ sl->sparams->utils->prop_set( sl->sparams->propctx,
+ sl->list[i].name, bv->bv_val, bv->bv_len );
+ }
+ }
+ return LDAP_SUCCESS;
+}
+
+#if SASL_VERSION_FULL >= 0x020118
+static int
+#else
+static void
+#endif
+slap_auxprop_lookup(
+ void *glob_context,
+ sasl_server_params_t *sparams,
+ unsigned flags,
+ const char *user,
+ unsigned ulen)
+{
+ OperationBuffer opbuf = {{ NULL }};
+ Operation *op = (Operation *)&opbuf;
+ int i, doit = 0;
+ Connection *conn = NULL;
+ lookup_info sl;
+ int rc = LDAP_SUCCESS;
+#ifdef SLAP_AUXPROP_DONTUSECOPY
+ int dontUseCopy = 0;
+ BackendDB *dontUseCopy_bd = NULL;
+#endif /* SLAP_AUXPROP_DONTUSECOPY */
+
+ sl.list = sparams->utils->prop_get( sparams->propctx );
+ sl.sparams = sparams;
+ sl.flags = flags;
+
+ /* Find our DN and conn first */
+ for( i = 0; sl.list[i].name; i++ ) {
+ if ( sl.list[i].name[0] == '*' ) {
+ if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_CONN] ) ) {
+ if ( sl.list[i].values && sl.list[i].values[0] )
+ AC_MEMCPY( &conn, sl.list[i].values[0], sizeof( conn ) );
+ continue;
+ }
+ if ( flags & SASL_AUXPROP_AUTHZID ) {
+ if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_AUTHZLEN] )) {
+ if ( sl.list[i].values && sl.list[i].values[0] )
+ AC_MEMCPY( &op->o_req_ndn.bv_len, sl.list[i].values[0],
+ sizeof( op->o_req_ndn.bv_len ) );
+ } else if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_AUTHZ] )) {
+ if ( sl.list[i].values )
+ op->o_req_ndn.bv_val = (char *)sl.list[i].values[0];
+ break;
+ }
+ }
+
+ if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_AUTHCLEN] )) {
+ if ( sl.list[i].values && sl.list[i].values[0] )
+ AC_MEMCPY( &op->o_req_ndn.bv_len, sl.list[i].values[0],
+ sizeof( op->o_req_ndn.bv_len ) );
+ } else if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_AUTHC] ) ) {
+ if ( sl.list[i].values ) {
+ op->o_req_ndn.bv_val = (char *)sl.list[i].values[0];
+ if ( !(flags & SASL_AUXPROP_AUTHZID) )
+ break;
+ }
+ }
+#ifdef SLAP_AUXPROP_DONTUSECOPY
+ if ( slap_dontUseCopy_propnames != NULL ) {
+ int j;
+ struct berval bv;
+ ber_str2bv( &sl.list[i].name[1], 0, 1, &bv );
+ for ( j = 0; !BER_BVISNULL( &slap_dontUseCopy_propnames[ j ]); j++ ) {
+ if ( bvmatch( &bv, &slap_dontUseCopy_propnames[ j ] ) ) {
+ dontUseCopy = 1;
+ break;
+ }
+ }
+ }
+#endif /* SLAP_AUXPROP_DONTUSECOPY */
+ }
+ }
+
+ /* we don't know anything about this, ignore it */
+ if ( !conn ) {
+ rc = LDAP_SUCCESS;
+ goto done;
+ }
+
+ /* Now see what else needs to be fetched */
+ for( i = 0; sl.list[i].name; i++ ) {
+ const char *name = sl.list[i].name;
+
+ if ( name[0] == '*' ) {
+ if ( flags & SASL_AUXPROP_AUTHZID ) continue;
+ /* Skip our private properties */
+ if ( !strcmp( name, slap_propnames[0] )) {
+ i += SLAP_SASL_PROP_COUNT - 1;
+ continue;
+ }
+ name++;
+ } else if ( !(flags & SASL_AUXPROP_AUTHZID ) )
+ continue;
+
+ if ( sl.list[i].values ) {
+ if ( !(flags & SASL_AUXPROP_OVERRIDE) ) continue;
+ }
+ doit = 1;
+ break;
+ }
+
+ if (doit) {
+ slap_callback cb = { NULL, sasl_ap_lookup, NULL, NULL };
+
+ cb.sc_private = &sl;
+
+ op->o_bd = select_backend( &op->o_req_ndn, 1 );
+
+ if ( op->o_bd ) {
+ /* For rootdn, see if we can use the rootpw */
+ if ( be_isroot_dn( op->o_bd, &op->o_req_ndn ) &&
+ !BER_BVISEMPTY( &op->o_bd->be_rootpw )) {
+ struct berval cbv = BER_BVNULL;
+
+ /* If there's a recognized scheme, see if it's CLEARTEXT */
+ if ( lutil_passwd_scheme( op->o_bd->be_rootpw.bv_val )) {
+ if ( !strncasecmp( op->o_bd->be_rootpw.bv_val,
+ sc_cleartext.bv_val, sc_cleartext.bv_len )) {
+
+ /* If it's CLEARTEXT, skip past scheme spec */
+ cbv.bv_len = op->o_bd->be_rootpw.bv_len -
+ sc_cleartext.bv_len;
+ if ( cbv.bv_len ) {
+ cbv.bv_val = op->o_bd->be_rootpw.bv_val +
+ sc_cleartext.bv_len;
+ }
+ }
+ /* No scheme, use the whole value */
+ } else {
+ cbv = op->o_bd->be_rootpw;
+ }
+ if ( !BER_BVISEMPTY( &cbv )) {
+ for( i = 0; sl.list[i].name; i++ ) {
+ const char *name = sl.list[i].name;
+
+ if ( name[0] == '*' ) {
+ if ( flags & SASL_AUXPROP_AUTHZID ) continue;
+ name++;
+ } else if ( !(flags & SASL_AUXPROP_AUTHZID ) )
+ continue;
+
+ if ( !strcasecmp(name,"userPassword") ) {
+ sl.sparams->utils->prop_set( sl.sparams->propctx,
+ sl.list[i].name, cbv.bv_val, cbv.bv_len );
+ break;
+ }
+ }
+ }
+ }
+
+#ifdef SLAP_AUXPROP_DONTUSECOPY
+ if ( SLAP_SHADOW( op->o_bd ) && dontUseCopy ) {
+ dontUseCopy_bd = op->o_bd;
+ op->o_bd = frontendDB;
+ }
+
+retry_dontUseCopy:;
+#endif /* SLAP_AUXPROP_DONTUSECOPY */
+
+ if ( op->o_bd->be_search ) {
+ SlapReply rs = {REP_RESULT};
+#ifdef SLAP_AUXPROP_DONTUSECOPY
+ LDAPControl **save_ctrls = NULL, c;
+ int save_dontUseCopy;
+#endif /* SLAP_AUXPROP_DONTUSECOPY */
+
+ op->o_hdr = conn->c_sasl_bindop->o_hdr;
+ op->o_controls = opbuf.ob_controls;
+ op->o_tag = LDAP_REQ_SEARCH;
+ op->o_dn = conn->c_ndn;
+ op->o_ndn = conn->c_ndn;
+ op->o_callback = &cb;
+ slap_op_time( &op->o_time, &op->o_tincr );
+ op->o_do_not_cache = 1;
+ op->o_is_auth_check = 1;
+ op->o_req_dn = op->o_req_ndn;
+ op->ors_scope = LDAP_SCOPE_BASE;
+ op->ors_deref = LDAP_DEREF_NEVER;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_slimit = 1;
+ op->ors_filter = &generic_filter;
+ op->ors_filterstr = generic_filterstr;
+ op->o_authz = conn->c_authz;
+ /* FIXME: we want all attributes, right? */
+ op->ors_attrs = NULL;
+
+#ifdef SLAP_AUXPROP_DONTUSECOPY
+ if ( dontUseCopy ) {
+ save_dontUseCopy = op->o_dontUseCopy;
+ if ( !op->o_dontUseCopy ) {
+ int cnt = 0;
+ save_ctrls = op->o_ctrls;
+ if ( op->o_ctrls ) {
+ for ( ; op->o_ctrls[ cnt ]; cnt++ )
+ ;
+ }
+ op->o_ctrls = op->o_tmpcalloc( sizeof(LDAPControl *), cnt + 2, op->o_tmpmemctx );
+ if ( cnt ) {
+ for ( cnt = 0; save_ctrls[ cnt ]; cnt++ ) {
+ op->o_ctrls[ cnt ] = save_ctrls[ cnt ];
+ }
+ }
+ c.ldctl_oid = LDAP_CONTROL_DONTUSECOPY;
+ c.ldctl_iscritical = 1;
+ BER_BVZERO( &c.ldctl_value );
+ op->o_ctrls[ cnt ] = &c;
+ }
+ op->o_dontUseCopy = SLAP_CONTROL_CRITICAL;
+ }
+#endif /* SLAP_AUXPROP_DONTUSECOPY */
+
+ rc = op->o_bd->be_search( op, &rs );
+
+#ifdef SLAP_AUXPROP_DONTUSECOPY
+ if ( dontUseCopy ) {
+ if ( save_ctrls != op->o_ctrls ) {
+ op->o_tmpfree( op->o_ctrls, op->o_tmpmemctx );
+ op->o_ctrls = save_ctrls;
+ op->o_dontUseCopy = save_dontUseCopy;
+ }
+
+ if ( rs.sr_err == LDAP_UNAVAILABLE && slap_dontUseCopy_ignore )
+ {
+ op->o_bd = dontUseCopy_bd;
+ dontUseCopy = 0;
+ goto retry_dontUseCopy;
+ }
+ }
+#endif /* SLAP_AUXPROP_DONTUSECOPY */
+ }
+ }
+ }
+done:;
+#if SASL_VERSION_FULL >= 0x020118
+ return rc != LDAP_SUCCESS ? SASL_FAIL : SASL_OK;
+#endif
+}
+
+#if SASL_VERSION_FULL >= 0x020110
+static int
+slap_auxprop_store(
+ void *glob_context,
+ sasl_server_params_t *sparams,
+ struct propctx *prctx,
+ const char *user,
+ unsigned ulen)
+{
+ Operation op = {0};
+ Opheader oph;
+ int rc, i;
+ unsigned j;
+ Connection *conn = NULL;
+ const struct propval *pr;
+ Modifications *modlist = NULL, **modtail = &modlist, *mod;
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+ char textbuf[SLAP_TEXT_BUFLEN];
+ const char *text;
+ size_t textlen = sizeof(textbuf);
+#ifdef SLAP_AUXPROP_DONTUSECOPY
+ int dontUseCopy = 0;
+ BackendDB *dontUseCopy_bd = NULL;
+#endif /* SLAP_AUXPROP_DONTUSECOPY */
+
+ /* just checking if we are enabled */
+ if (!prctx) return SASL_OK;
+
+ if (!sparams || !user) return SASL_BADPARAM;
+
+ pr = sparams->utils->prop_get( sparams->propctx );
+
+ /* Find our DN and conn first */
+ for( i = 0; pr[i].name; i++ ) {
+ if ( pr[i].name[0] == '*' ) {
+ if ( !strcmp( pr[i].name, slap_propnames[SLAP_SASL_PROP_CONN] ) ) {
+ if ( pr[i].values && pr[i].values[0] )
+ AC_MEMCPY( &conn, pr[i].values[0], sizeof( conn ) );
+ continue;
+ }
+ if ( !strcmp( pr[i].name, slap_propnames[SLAP_SASL_PROP_AUTHCLEN] )) {
+ if ( pr[i].values && pr[i].values[0] )
+ AC_MEMCPY( &op.o_req_ndn.bv_len, pr[i].values[0],
+ sizeof( op.o_req_ndn.bv_len ) );
+ } else if ( !strcmp( pr[i].name, slap_propnames[SLAP_SASL_PROP_AUTHC] ) ) {
+ if ( pr[i].values )
+ op.o_req_ndn.bv_val = (char *)pr[i].values[0];
+ }
+#ifdef SLAP_AUXPROP_DONTUSECOPY
+ if ( slap_dontUseCopy_propnames != NULL ) {
+ struct berval bv;
+ ber_str2bv( &pr[i].name[1], 0, 1, &bv );
+ for ( j = 0; !BER_BVISNULL( &slap_dontUseCopy_propnames[ j ] ); j++ ) {
+ if ( bvmatch( &bv, &slap_dontUseCopy_propnames[ j ] ) ) {
+ dontUseCopy = 1;
+ break;
+ }
+ }
+ }
+#endif /* SLAP_AUXPROP_DONTUSECOPY */
+ }
+ }
+ if (!conn || !op.o_req_ndn.bv_val) return SASL_BADPARAM;
+
+ op.o_bd = select_backend( &op.o_req_ndn, 1 );
+
+ if ( !op.o_bd || !op.o_bd->be_modify ) return SASL_FAIL;
+
+#ifdef SLAP_AUXPROP_DONTUSECOPY
+ if ( SLAP_SHADOW( op.o_bd ) && dontUseCopy ) {
+ dontUseCopy_bd = op.o_bd;
+ op.o_bd = frontendDB;
+ op.o_dontUseCopy = SLAP_CONTROL_CRITICAL;
+ }
+#endif /* SLAP_AUXPROP_DONTUSECOPY */
+
+ pr = sparams->utils->prop_get( prctx );
+ if (!pr) return SASL_BADPARAM;
+
+ for (i=0; pr[i].name; i++);
+ if (!i) return SASL_BADPARAM;
+
+ for (i=0; pr[i].name; i++) {
+ mod = (Modifications *)ch_malloc( sizeof(Modifications) );
+ mod->sml_op = LDAP_MOD_REPLACE;
+ mod->sml_flags = 0;
+ ber_str2bv( pr[i].name, 0, 0, &mod->sml_type );
+ mod->sml_numvals = pr[i].nvalues;
+ mod->sml_values = (struct berval *)ch_malloc( (pr[i].nvalues + 1) *
+ sizeof(struct berval));
+ for (j=0; j<pr[i].nvalues; j++) {
+ ber_str2bv( pr[i].values[j], 0, 1, &mod->sml_values[j]);
+ }
+ BER_BVZERO( &mod->sml_values[j] );
+ mod->sml_nvalues = NULL;
+ mod->sml_desc = NULL;
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ }
+ *modtail = NULL;
+
+ rc = slap_mods_check( &op, modlist, &text, textbuf, textlen, NULL );
+
+ if ( rc == LDAP_SUCCESS ) {
+ rc = slap_mods_no_user_mod_check( &op, modlist,
+ &text, textbuf, textlen );
+
+ if ( rc == LDAP_SUCCESS ) {
+ if ( conn->c_sasl_bindop ) {
+ op.o_hdr = conn->c_sasl_bindop->o_hdr;
+ } else {
+ op.o_hdr = &oph;
+ memset( &oph, 0, sizeof(oph) );
+ operation_fake_init( conn, &op, ldap_pvt_thread_pool_context(), 0 );
+ }
+ op.o_tag = LDAP_REQ_MODIFY;
+ op.o_ndn = op.o_req_ndn;
+ op.o_callback = &cb;
+ slap_op_time( &op.o_time, &op.o_tincr );
+ op.o_do_not_cache = 1;
+ op.o_is_auth_check = 1;
+ op.o_req_dn = op.o_req_ndn;
+ op.orm_modlist = modlist;
+
+ for (;;) {
+ SlapReply rs = {REP_RESULT};
+ rc = op.o_bd->be_modify( &op, &rs );
+
+#ifdef SLAP_AUXPROP_DONTUSECOPY
+ if ( dontUseCopy &&
+ rs.sr_err == LDAP_UNAVAILABLE &&
+ slap_dontUseCopy_ignore )
+ {
+ op.o_bd = dontUseCopy_bd;
+ op.o_dontUseCopy = SLAP_CONTROL_NONE;
+ dontUseCopy = 0;
+ continue;
+ }
+#endif /* SLAP_AUXPROP_DONTUSECOPY */
+ break;
+ }
+ }
+ }
+ slap_mods_free( modlist, 1 );
+ return rc != LDAP_SUCCESS ? SASL_FAIL : SASL_OK;
+}
+#endif /* SASL_VERSION_FULL >= 2.1.16 */
+
+static sasl_auxprop_plug_t slap_auxprop_plugin = {
+ 0, /* Features */
+ 0, /* spare */
+ NULL, /* glob_context */
+ NULL, /* auxprop_free */
+ slap_auxprop_lookup,
+ "slapd", /* name */
+#if SASL_VERSION_FULL >= 0x020110
+ slap_auxprop_store /* the declaration of this member changed
+ * in cyrus SASL from 2.1.15 to 2.1.16 */
+#else
+ NULL
+#endif
+};
+
+static int
+slap_auxprop_init(
+ const sasl_utils_t *utils,
+ int max_version,
+ int *out_version,
+ sasl_auxprop_plug_t **plug,
+ const char *plugname)
+{
+ if ( !out_version || !plug ) return SASL_BADPARAM;
+
+ if ( max_version < SASL_AUXPROP_PLUG_VERSION ) return SASL_BADVERS;
+
+ *out_version = SASL_AUXPROP_PLUG_VERSION;
+ *plug = &slap_auxprop_plugin;
+ return SASL_OK;
+}
+
+/* Convert a SASL authcid or authzid into a DN. Store the DN in an
+ * auxiliary property, so that we can refer to it in sasl_authorize
+ * without interfering with anything else. Also, the SASL username
+ * buffer is constrained to 256 characters, and our DNs could be
+ * much longer (SLAP_LDAPDN_MAXLEN, currently set to 8192)
+ */
+static int
+slap_sasl_canonicalize(
+ sasl_conn_t *sconn,
+ void *context,
+ const char *in,
+ unsigned inlen,
+ unsigned flags,
+ const char *user_realm,
+ char *out,
+ unsigned out_max,
+ unsigned *out_len)
+{
+ Connection *conn = (Connection *)context;
+ struct propctx *props = sasl_auxprop_getctx( sconn );
+ struct propval auxvals[ SLAP_SASL_PROP_COUNT ] = { { 0 } };
+ struct berval dn;
+ int rc, which;
+ const char *names[2];
+ struct berval bvin;
+
+ *out_len = 0;
+
+ Debug( LDAP_DEBUG_ARGS, "SASL Canonicalize [conn=%ld]: %s=\"%s\"\n",
+ conn ? (long) conn->c_connid : -1L,
+ (flags & SASL_CU_AUTHID) ? "authcid" : "authzid",
+ in ? in : "<empty>");
+
+ /* If name is too big, just truncate. We don't care, we're
+ * using DNs, not the usernames.
+ */
+ if ( inlen > out_max )
+ inlen = out_max-1;
+
+ /* This is a Simple Bind using SPASSWD. That means the in-directory
+ * userPassword of the Binding user already points at SASL, so it
+ * cannot be used to actually satisfy a password comparison. Just
+ * ignore it, some other mech will process it.
+ */
+ if ( !conn->c_sasl_bindop ||
+ conn->c_sasl_bindop->orb_method != LDAP_AUTH_SASL ) goto done;
+
+ /* See if we need to add request, can only do it once */
+ prop_getnames( props, slap_propnames, auxvals );
+ if ( !auxvals[0].name )
+ prop_request( props, slap_propnames );
+
+ if ( flags & SASL_CU_AUTHID )
+ which = SLAP_SASL_PROP_AUTHCLEN;
+ else
+ which = SLAP_SASL_PROP_AUTHZLEN;
+
+ /* Need to store the Connection for auxprop_lookup */
+ if ( !auxvals[SLAP_SASL_PROP_CONN].values ) {
+ names[0] = slap_propnames[SLAP_SASL_PROP_CONN];
+ names[1] = NULL;
+ prop_set( props, names[0], (char *)&conn, sizeof( conn ) );
+ }
+
+ /* Already been here? */
+ if ( auxvals[which].values )
+ goto done;
+
+ /* Normally we require an authzID to have a u: or dn: prefix.
+ * However, SASL frequently gives us an authzID that is just
+ * an exact copy of the authcID, without a prefix. We need to
+ * detect and allow this condition. If SASL calls canonicalize
+ * with SASL_CU_AUTHID|SASL_CU_AUTHZID this is a no-brainer.
+ * But if it's broken into two calls, we need to remember the
+ * authcID so that we can compare the authzID later. We store
+ * the authcID temporarily in conn->c_sasl_dn. We necessarily
+ * finish Canonicalizing before Authorizing, so there is no
+ * conflict with slap_sasl_authorize's use of this temp var.
+ *
+ * The SASL EXTERNAL mech is backwards from all the other mechs,
+ * it does authzID before the authcID. If we see that authzID
+ * has already been done, don't do anything special with authcID.
+ */
+ if ( flags == SASL_CU_AUTHID && !auxvals[SLAP_SASL_PROP_AUTHZ].values ) {
+ conn->c_sasl_dn.bv_val = (char *) in;
+ conn->c_sasl_dn.bv_len = 0;
+ } else if ( flags == SASL_CU_AUTHZID && conn->c_sasl_dn.bv_val ) {
+ rc = strcmp( in, conn->c_sasl_dn.bv_val );
+ conn->c_sasl_dn.bv_val = NULL;
+ /* They were equal, no work needed */
+ if ( !rc ) goto done;
+ }
+
+ bvin.bv_val = (char *)in;
+ bvin.bv_len = inlen;
+ rc = slap_sasl_getdn( conn, NULL, &bvin, (char *)user_realm, &dn,
+ (flags & SASL_CU_AUTHID) ? SLAP_GETDN_AUTHCID : SLAP_GETDN_AUTHZID );
+ if ( rc != LDAP_SUCCESS ) {
+ sasl_seterror( sconn, 0, ldap_err2string( rc ) );
+ return SASL_NOAUTHZ;
+ }
+
+ names[0] = slap_propnames[which];
+ names[1] = NULL;
+ prop_set( props, names[0], (char *)&dn.bv_len, sizeof( dn.bv_len ) );
+
+ which++;
+ names[0] = slap_propnames[which];
+ prop_set( props, names[0], dn.bv_val, dn.bv_len );
+
+ Debug( LDAP_DEBUG_ARGS, "SASL Canonicalize [conn=%ld]: %s=\"%s\"\n",
+ conn ? (long) conn->c_connid : -1L, names[0]+1,
+ dn.bv_val ? dn.bv_val : "<EMPTY>" );
+
+ /* Not needed any more, SASL has copied it */
+ if ( conn && conn->c_sasl_bindop )
+ conn->c_sasl_bindop->o_tmpfree( dn.bv_val, conn->c_sasl_bindop->o_tmpmemctx );
+
+done:
+ AC_MEMCPY( out, in, inlen );
+ out[inlen] = '\0';
+
+ *out_len = inlen;
+
+ return SASL_OK;
+}
+
+static int
+slap_sasl_authorize(
+ sasl_conn_t *sconn,
+ void *context,
+ char *requested_user,
+ unsigned rlen,
+ char *auth_identity,
+ unsigned alen,
+ const char *def_realm,
+ unsigned urlen,
+ struct propctx *props)
+{
+ Connection *conn = (Connection *)context;
+ /* actually:
+ * (SLAP_SASL_PROP_COUNT - 1) because we skip "conn",
+ * + 1 for NULL termination?
+ */
+ struct propval auxvals[ SLAP_SASL_PROP_COUNT ] = { { 0 } };
+ struct berval authcDN, authzDN = BER_BVNULL;
+ int rc;
+
+ /* Simple Binds don't support proxy authorization, ignore it */
+ if ( !conn->c_sasl_bindop ||
+ conn->c_sasl_bindop->orb_method != LDAP_AUTH_SASL ) return SASL_OK;
+
+ Debug( LDAP_DEBUG_ARGS, "SASL proxy authorize [conn=%ld]: "
+ "authcid=\"%s\" authzid=\"%s\"\n",
+ conn ? (long) conn->c_connid : -1L, auth_identity, requested_user );
+ if ( conn->c_sasl_dn.bv_val ) {
+ BER_BVZERO( &conn->c_sasl_dn );
+ }
+
+ /* Skip SLAP_SASL_PROP_CONN */
+ prop_getnames( props, slap_propnames+1, auxvals );
+
+ /* Should not happen */
+ if ( !auxvals[0].values ) {
+ sasl_seterror( sconn, 0, "invalid authcid" );
+ return SASL_NOAUTHZ;
+ }
+
+ AC_MEMCPY( &authcDN.bv_len, auxvals[0].values[0], sizeof(authcDN.bv_len) );
+ authcDN.bv_val = auxvals[1].values ? (char *)auxvals[1].values[0] : NULL;
+ conn->c_sasl_dn = authcDN;
+
+ /* Nothing to do if no authzID was given */
+ if ( !auxvals[2].name || !auxvals[2].values ) {
+ goto ok;
+ }
+
+ AC_MEMCPY( &authzDN.bv_len, auxvals[2].values[0], sizeof(authzDN.bv_len) );
+ authzDN.bv_val = auxvals[3].values ? (char *)auxvals[3].values[0] : NULL;
+
+ rc = slap_sasl_authorized( conn->c_sasl_bindop, &authcDN, &authzDN );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE, "SASL Proxy Authorize [conn=%ld]: "
+ "proxy authorization disallowed (%d)\n",
+ conn ? (long) conn->c_connid : -1L, rc );
+
+ sasl_seterror( sconn, 0, "not authorized" );
+ return SASL_NOAUTHZ;
+ }
+
+ /* FIXME: we need yet another dup because slap_sasl_getdn()
+ * is using the bind operation slab */
+ ber_dupbv( &conn->c_sasl_authz_dn, &authzDN );
+
+ok:
+ if (conn->c_sasl_bindop) {
+ Debug( LDAP_DEBUG_STATS,
+ "%s BIND authcid=\"%s\" authzid=\"%s\"\n",
+ conn->c_sasl_bindop->o_log_prefix,
+ auth_identity, requested_user );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "SASL Authorize [conn=%ld]: "
+ " proxy authorization allowed authzDN=\"%s\"\n",
+ conn ? (long) conn->c_connid : -1L,
+ authzDN.bv_val ? authzDN.bv_val : "" );
+ return SASL_OK;
+}
+
+static int
+slap_sasl_err2ldap( int saslerr )
+{
+ int rc;
+
+ /* map SASL errors to LDAP resultCode returned by:
+ * sasl_server_new()
+ * SASL_OK, SASL_NOMEM
+ * sasl_server_step()
+ * SASL_OK, SASL_CONTINUE, SASL_TRANS, SASL_BADPARAM, SASL_BADPROT,
+ * ...
+ * sasl_server_start()
+ * + SASL_NOMECH
+ * sasl_setprop()
+ * SASL_OK, SASL_BADPARAM
+ */
+
+ switch (saslerr) {
+ case SASL_OK:
+ rc = LDAP_SUCCESS;
+ break;
+ case SASL_CONTINUE:
+ rc = LDAP_SASL_BIND_IN_PROGRESS;
+ break;
+ case SASL_FAIL:
+ case SASL_NOMEM:
+ rc = LDAP_OTHER;
+ break;
+ case SASL_NOMECH:
+ rc = LDAP_AUTH_METHOD_NOT_SUPPORTED;
+ break;
+ case SASL_BADAUTH:
+ case SASL_NOUSER:
+ case SASL_TRANS:
+ case SASL_EXPIRED:
+ rc = LDAP_INVALID_CREDENTIALS;
+ break;
+ case SASL_NOAUTHZ:
+ rc = LDAP_INSUFFICIENT_ACCESS;
+ break;
+ case SASL_TOOWEAK:
+ case SASL_ENCRYPT:
+ rc = LDAP_INAPPROPRIATE_AUTH;
+ break;
+ case SASL_UNAVAIL:
+ case SASL_TRYAGAIN:
+ rc = LDAP_UNAVAILABLE;
+ break;
+ case SASL_DISABLED:
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ break;
+ default:
+ rc = LDAP_OTHER;
+ break;
+ }
+
+ return rc;
+}
+
+#ifdef SLAPD_SPASSWD
+
+static struct berval sasl_pwscheme = BER_BVC("{SASL}");
+
+static int chk_sasl(
+ const struct berval *sc,
+ const struct berval * passwd,
+ const struct berval * cred,
+ const char **text )
+{
+ unsigned int i;
+ int rtn;
+ void *ctx, *sconn = NULL;
+
+ for( i=0; i<cred->bv_len; i++) {
+ if(cred->bv_val[i] == '\0') {
+ return LUTIL_PASSWD_ERR; /* NUL character in password */
+ }
+ }
+
+ if( cred->bv_val[i] != '\0' ) {
+ return LUTIL_PASSWD_ERR; /* cred must behave like a string */
+ }
+
+ for( i=0; i<passwd->bv_len; i++) {
+ if(passwd->bv_val[i] == '\0') {
+ return LUTIL_PASSWD_ERR; /* NUL character in password */
+ }
+ }
+
+ if( passwd->bv_val[i] != '\0' ) {
+ return LUTIL_PASSWD_ERR; /* passwd must behave like a string */
+ }
+
+ rtn = LUTIL_PASSWD_ERR;
+
+ ctx = ldap_pvt_thread_pool_context();
+ ldap_pvt_thread_pool_getkey( ctx, (void *)slap_sasl_bind, &sconn, NULL );
+
+ if( sconn != NULL ) {
+ int sc;
+ sc = sasl_checkpass( sconn,
+ passwd->bv_val, passwd->bv_len,
+ cred->bv_val, cred->bv_len );
+ rtn = ( sc != SASL_OK ) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+ }
+
+ return rtn;
+}
+#endif /* SLAPD_SPASSWD */
+
+#endif /* HAVE_CYRUS_SASL */
+
+typedef struct slapd_map_data {
+ struct berval base;
+ struct berval filter;
+ AttributeName attrs[2];
+ int scope;
+} slapd_map_data;
+
+static void *
+slapd_rw_config( const char *fname, int lineno, int argc, char **argv )
+{
+ slapd_map_data *ret = NULL;
+ LDAPURLDesc *lud = NULL;
+ char *uri;
+ AttributeDescription *ad = NULL;
+ int rc, flen = 0;
+ struct berval dn, ndn;
+
+ if ( argc != 1 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] slapd map needs URI\n",
+ fname, lineno );
+ return NULL;
+ }
+
+ uri = argv[0];
+ if ( strncasecmp( uri, "uri=", STRLENOF( "uri=" ) ) == 0 ) {
+ uri += STRLENOF( "uri=" );
+ }
+
+ if ( ldap_url_parse( uri, &lud ) != LDAP_URL_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] illegal URI '%s'\n",
+ fname, lineno, uri );
+ return NULL;
+ }
+
+ if ( strcasecmp( lud->lud_scheme, "ldap" )) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] illegal URI scheme '%s'\n",
+ fname, lineno, lud->lud_scheme );
+ goto done;
+ }
+
+ if (( lud->lud_host && lud->lud_host[0] ) || lud->lud_exts
+ || !lud->lud_dn ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] illegal URI '%s'\n",
+ fname, lineno, uri );
+ goto done;
+ }
+
+ if ( lud->lud_attrs ) {
+ if ( lud->lud_attrs[1] ) {
+ Debug( LDAP_DEBUG_ANY,
+ "[%s:%d] only one attribute allowed in URI\n",
+ fname, lineno );
+ goto done;
+ }
+ if ( strcasecmp( lud->lud_attrs[0], "dn" ) &&
+ strcasecmp( lud->lud_attrs[0], "entryDN" )) {
+ const char *text;
+ rc = slap_str2ad( lud->lud_attrs[0], &ad, &text );
+ if ( rc )
+ goto done;
+ }
+ }
+ ber_str2bv( lud->lud_dn, 0, 0, &dn );
+ if ( dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL ))
+ goto done;
+
+ if ( lud->lud_filter ) {
+ flen = strlen( lud->lud_filter ) + 1;
+ }
+ ret = ch_malloc( sizeof( slapd_map_data ) + flen );
+ ret->base = ndn;
+ if ( flen ) {
+ ret->filter.bv_val = (char *)(ret+1);
+ ret->filter.bv_len = flen - 1;
+ strcpy( ret->filter.bv_val, lud->lud_filter );
+ } else {
+ BER_BVZERO( &ret->filter );
+ }
+ ret->scope = lud->lud_scope;
+ if ( ad ) {
+ ret->attrs[0].an_name = ad->ad_cname;
+ } else {
+ BER_BVZERO( &ret->attrs[0].an_name );
+ }
+ ret->attrs[0].an_desc = ad;
+ BER_BVZERO( &ret->attrs[1].an_name );
+done:
+ ldap_free_urldesc( lud );
+ return ret;
+}
+
+struct slapd_rw_info {
+ slapd_map_data *si_data;
+ struct berval si_val;
+};
+
+static int
+slapd_rw_cb( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_SEARCH ) {
+ struct slapd_rw_info *si = op->o_callback->sc_private;
+
+ if ( si->si_data->attrs[0].an_desc ) {
+ Attribute *a;
+
+ a = attr_find( rs->sr_entry->e_attrs,
+ si->si_data->attrs[0].an_desc );
+ if ( a ) {
+ ber_dupbv( &si->si_val, a->a_vals );
+ }
+ } else {
+ ber_dupbv( &si->si_val, &rs->sr_entry->e_name );
+ }
+ }
+ return LDAP_SUCCESS;
+}
+
+static int
+slapd_rw_apply( void *private, const char *filter, struct berval *val )
+{
+ slapd_map_data *sl = private;
+ slap_callback cb = { NULL };
+ Connection conn = {0};
+ OperationBuffer opbuf;
+ Operation *op;
+ void *thrctx;
+ SlapReply rs = {REP_RESULT};
+ struct slapd_rw_info si;
+ char *ptr;
+ int rc;
+
+ thrctx = ldap_pvt_thread_pool_context();
+ connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+ op = &opbuf.ob_op;
+
+ op->o_tag = LDAP_REQ_SEARCH;
+ op->o_req_dn = op->o_req_ndn = sl->base;
+ op->o_bd = select_backend( &op->o_req_ndn, 1 );
+ if ( !op->o_bd ) {
+ return REWRITE_ERR;
+ }
+ si.si_data = sl;
+ BER_BVZERO( &si.si_val );
+ op->ors_scope = sl->scope;
+ op->ors_deref = LDAP_DEREF_NEVER;
+ op->ors_slimit = 1;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ if ( sl->attrs[0].an_desc ) {
+ op->ors_attrs = sl->attrs;
+ } else {
+ op->ors_attrs = slap_anlist_no_attrs;
+ }
+ if ( filter ) {
+ rc = strlen( filter );
+ } else {
+ rc = 0;
+ }
+ rc += sl->filter.bv_len;
+ ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( rc + 1, op->o_tmpmemctx );
+ if ( sl->filter.bv_len ) {
+ ptr = lutil_strcopy( ptr, sl->filter.bv_val );
+ } else {
+ *ptr = '\0';
+ }
+ if ( filter ) {
+ strcpy( ptr, filter );
+ }
+ op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
+ if ( !op->ors_filter ) {
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ return REWRITE_ERR;
+ }
+
+ op->ors_attrsonly = 0;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ op->o_do_not_cache = 1;
+
+ cb.sc_response = slapd_rw_cb;
+ cb.sc_private = &si;
+ op->o_callback = &cb;
+
+ rc = op->o_bd->be_search( op, &rs );
+ if ( rc == LDAP_SUCCESS && !BER_BVISNULL( &si.si_val )) {
+ *val = si.si_val;
+ rc = REWRITE_SUCCESS;
+ } else {
+ if ( !BER_BVISNULL( &si.si_val )) {
+ ch_free( si.si_val.bv_val );
+ }
+ rc = REWRITE_ERR;
+ }
+ filter_free_x( op, op->ors_filter, 1 );
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ return rc;
+}
+
+static int
+slapd_rw_destroy( void *private )
+{
+ slapd_map_data *md = private;
+
+ assert( private != NULL );
+
+ ch_free( md->base.bv_val );
+ ch_free( md );
+
+ return 0;
+}
+
+static const rewrite_mapper slapd_mapper = {
+ "slapd",
+ slapd_rw_config,
+ slapd_rw_apply,
+ slapd_rw_destroy
+};
+
+int slap_sasl_init( void )
+{
+#ifdef HAVE_CYRUS_SASL
+ int rc;
+ static sasl_callback_t server_callbacks[] = {
+ { SASL_CB_LOG, (slap_sasl_cb_ft)&slap_sasl_log, NULL },
+ { SASL_CB_GETOPT, (slap_sasl_cb_ft)&slap_sasl_getopt, NULL },
+ { SASL_CB_LIST_END, NULL, NULL }
+ };
+#endif
+
+ rewrite_mapper_register( &slapd_mapper );
+
+#ifdef HAVE_CYRUS_SASL
+#ifdef HAVE_SASL_VERSION
+ /* stringify the version number, sasl.h doesn't do it for us */
+#define VSTR0(maj, min, pat) #maj "." #min "." #pat
+#define VSTR(maj, min, pat) VSTR0(maj, min, pat)
+#define SASL_VERSION_STRING VSTR(SASL_VERSION_MAJOR, SASL_VERSION_MINOR, \
+ SASL_VERSION_STEP)
+
+ sasl_version( NULL, &rc );
+ if ( ((rc >> 16) != ((SASL_VERSION_MAJOR << 8)|SASL_VERSION_MINOR)) ||
+ (rc & 0xffff) < SASL_VERSION_STEP)
+ {
+ char version[sizeof("xxx.xxx.xxxxx")];
+ sprintf( version, "%u.%d.%d", (unsigned)rc >> 24, (rc >> 16) & 0xff,
+ rc & 0xffff );
+ Debug( LDAP_DEBUG_ANY, "slap_sasl_init: SASL library version mismatch:"
+ " expected %s, got %s\n",
+ SASL_VERSION_STRING, version );
+ return -1;
+ }
+#endif
+
+ sasl_set_mutex(
+ ldap_pvt_sasl_mutex_new,
+ ldap_pvt_sasl_mutex_lock,
+ ldap_pvt_sasl_mutex_unlock,
+ ldap_pvt_sasl_mutex_dispose );
+
+ generic_filter.f_desc = slap_schema.si_ad_objectClass;
+
+ rc = sasl_auxprop_add_plugin( "slapd", slap_auxprop_init );
+ if( rc != SASL_OK ) {
+ Debug( LDAP_DEBUG_ANY, "slap_sasl_init: auxprop add plugin failed\n" );
+ return -1;
+ }
+
+ /* should provide callbacks for logging */
+ /* server name should be configurable */
+ rc = sasl_server_init( server_callbacks, "slapd" );
+
+ if( rc != SASL_OK ) {
+ Debug( LDAP_DEBUG_ANY, "slap_sasl_init: server init failed\n" );
+
+ return -1;
+ }
+
+#ifdef SLAPD_SPASSWD
+ lutil_passwd_add( &sasl_pwscheme, chk_sasl, NULL );
+#endif
+
+ Debug( LDAP_DEBUG_TRACE, "slap_sasl_init: initialized!\n" );
+
+ /* default security properties */
+ memset( &sasl_secprops, '\0', sizeof(sasl_secprops) );
+ sasl_secprops.max_ssf = INT_MAX;
+ sasl_secprops.maxbufsize = 65536;
+ sasl_secprops.security_flags = SASL_SEC_NOPLAINTEXT|SASL_SEC_NOANONYMOUS;
+#endif
+
+ return 0;
+}
+
+int slap_sasl_destroy( void )
+{
+#ifdef HAVE_CYRUS_SASL
+ sasl_done();
+
+#ifdef SLAP_AUXPROP_DONTUSECOPY
+ if ( slap_dontUseCopy_propnames ) {
+ ber_bvarray_free( slap_dontUseCopy_propnames );
+ slap_dontUseCopy_propnames = NULL;
+ }
+#endif /* SLAP_AUXPROP_DONTUSECOPY */
+#endif
+ free( sasl_host );
+ sasl_host = NULL;
+ free( sasl_cbinding );
+ sasl_cbinding = NULL;
+
+ return 0;
+}
+
+static char *
+slap_sasl_peer2ipport( struct berval *peer )
+{
+ int isv6 = 0;
+ char *ipport, *p,
+ *addr = &peer->bv_val[ STRLENOF( "IP=" ) ];
+ ber_len_t plen = peer->bv_len - STRLENOF( "IP=" );
+
+ /* IPv6? */
+ if ( addr[0] == '[' ) {
+ isv6 = 1;
+ plen--;
+ }
+ ipport = ch_strdup( &addr[isv6] );
+
+ /* Convert IPv6/IPv4 addresses to address;port syntax. */
+ p = strrchr( ipport, ':' );
+ if ( p != NULL ) {
+ *p = ';';
+ if ( isv6 ) {
+ assert( p[-1] == ']' );
+ AC_MEMCPY( &p[-1], p, plen - ( p - ipport ) + 1 );
+ }
+
+ } else if ( isv6 ) {
+ /* trim ']' */
+ plen--;
+ assert( addr[plen] == ']' );
+ addr[plen] = '\0';
+ }
+
+ return ipport;
+}
+
+int slap_sasl_open( Connection *conn, int reopen )
+{
+ int sc = LDAP_SUCCESS;
+#ifdef HAVE_CYRUS_SASL
+ int cb;
+
+ sasl_conn_t *ctx = NULL;
+ sasl_callback_t *session_callbacks;
+ char *ipremoteport = NULL, *iplocalport = NULL;
+
+ assert( conn->c_sasl_authctx == NULL );
+
+ if ( !reopen ) {
+ assert( conn->c_sasl_extra == NULL );
+
+ session_callbacks =
+ SLAP_CALLOC( 5, sizeof(sasl_callback_t));
+ if( session_callbacks == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "slap_sasl_open: SLAP_MALLOC failed" );
+ return -1;
+ }
+ conn->c_sasl_extra = session_callbacks;
+
+ session_callbacks[cb=0].id = SASL_CB_LOG;
+ session_callbacks[cb].proc = (slap_sasl_cb_ft)&slap_sasl_log;
+ session_callbacks[cb++].context = conn;
+
+ session_callbacks[cb].id = SASL_CB_PROXY_POLICY;
+ session_callbacks[cb].proc = (slap_sasl_cb_ft)&slap_sasl_authorize;
+ session_callbacks[cb++].context = conn;
+
+ session_callbacks[cb].id = SASL_CB_CANON_USER;
+ session_callbacks[cb].proc = (slap_sasl_cb_ft)&slap_sasl_canonicalize;
+ session_callbacks[cb++].context = conn;
+
+ session_callbacks[cb].id = SASL_CB_LIST_END;
+ session_callbacks[cb].proc = NULL;
+ session_callbacks[cb++].context = NULL;
+ } else {
+ session_callbacks = conn->c_sasl_extra;
+ }
+
+ conn->c_sasl_layers = 0;
+
+ /* create new SASL context */
+ if ( conn->c_sock_name.bv_len != 0 &&
+ strncmp( conn->c_sock_name.bv_val, "IP=", STRLENOF( "IP=" ) ) == 0 )
+ {
+ iplocalport = slap_sasl_peer2ipport( &conn->c_sock_name );
+ }
+
+ if ( conn->c_peer_name.bv_len != 0 &&
+ strncmp( conn->c_peer_name.bv_val, "IP=", STRLENOF( "IP=" ) ) == 0 )
+ {
+ ipremoteport = slap_sasl_peer2ipport( &conn->c_peer_name );
+ }
+
+ sc = sasl_server_new( "ldap", sasl_host, global_realm,
+ iplocalport, ipremoteport, session_callbacks, SASL_SUCCESS_DATA, &ctx );
+ if ( iplocalport != NULL ) {
+ ch_free( iplocalport );
+ }
+ if ( ipremoteport != NULL ) {
+ ch_free( ipremoteport );
+ }
+
+ if( sc != SASL_OK ) {
+ Debug( LDAP_DEBUG_ANY, "sasl_server_new failed: %d\n",
+ sc );
+
+ return -1;
+ }
+
+ conn->c_sasl_authctx = ctx;
+
+ if( sc == SASL_OK ) {
+ sc = sasl_setprop( ctx,
+ SASL_SEC_PROPS, &sasl_secprops );
+
+ if( sc != SASL_OK ) {
+ Debug( LDAP_DEBUG_ANY, "sasl_setprop failed: %d\n",
+ sc );
+
+ slap_sasl_close( conn );
+ return -1;
+ }
+ }
+
+ sc = slap_sasl_err2ldap( sc );
+
+#elif defined(SLAP_BUILTIN_SASL)
+ /* built-in SASL implementation */
+ SASL_CTX *ctx = (SASL_CTX *) SLAP_MALLOC(sizeof(SASL_CTX));
+ if( ctx == NULL ) return -1;
+
+ ctx->sc_external_ssf = 0;
+ BER_BVZERO( &ctx->sc_external_id );
+
+ conn->c_sasl_authctx = ctx;
+#endif
+
+ return sc;
+}
+
+int slap_sasl_external(
+ Connection *conn,
+ slap_ssf_t ssf,
+ struct berval *auth_id )
+{
+#ifdef HAVE_CYRUS_SASL
+ int sc;
+ sasl_conn_t *ctx = conn->c_sasl_authctx;
+ sasl_ssf_t sasl_ssf = ssf;
+
+ if ( ctx == NULL ) {
+ return LDAP_UNAVAILABLE;
+ }
+
+ sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf );
+
+ if ( sc != SASL_OK ) {
+ return LDAP_OTHER;
+ }
+
+ sc = sasl_setprop( ctx, SASL_AUTH_EXTERNAL,
+ auth_id ? auth_id->bv_val : NULL );
+
+ if ( sc != SASL_OK ) {
+ return LDAP_OTHER;
+ }
+#elif defined(SLAP_BUILTIN_SASL)
+ /* built-in SASL implementation */
+ SASL_CTX *ctx = conn->c_sasl_authctx;
+ if ( ctx == NULL ) return LDAP_UNAVAILABLE;
+
+ ctx->sc_external_ssf = ssf;
+ if( auth_id ) {
+ ctx->sc_external_id = *auth_id;
+ BER_BVZERO( auth_id );
+ } else {
+ BER_BVZERO( &ctx->sc_external_id );
+ }
+#endif
+
+ return LDAP_SUCCESS;
+}
+
+int slap_sasl_cbinding( Connection *conn, void *ssl )
+{
+#ifdef SASL_CHANNEL_BINDING
+ void *cb;
+ int i;
+
+ if ( sasl_cbinding == NULL )
+ return LDAP_SUCCESS;
+
+ i = ldap_pvt_sasl_cbinding_parse( sasl_cbinding );
+ if ( i < 0 )
+ return LDAP_SUCCESS;
+
+ cb = ldap_pvt_sasl_cbinding( ssl, i, 1 );
+ if ( cb != NULL ) {
+ sasl_setprop( conn->c_sasl_authctx, SASL_CHANNEL_BINDING, cb );
+ conn->c_sasl_cbind = cb;
+ }
+#endif
+ return LDAP_SUCCESS;
+}
+
+int slap_sasl_reset( Connection *conn )
+{
+ return LDAP_SUCCESS;
+}
+
+char ** slap_sasl_mechs( Connection *conn )
+{
+ char **mechs = NULL;
+
+#ifdef HAVE_CYRUS_SASL
+ sasl_conn_t *ctx = conn->c_sasl_authctx;
+
+ if( ctx == NULL ) ctx = conn->c_sasl_sockctx;
+
+ if( ctx != NULL ) {
+ int sc;
+ SASL_CONST char *mechstr;
+
+ sc = sasl_listmech( ctx,
+ NULL, NULL, ",", NULL,
+ &mechstr, NULL, NULL );
+
+ if( sc != SASL_OK ) {
+ Debug( LDAP_DEBUG_ANY, "slap_sasl_listmech failed: %d\n",
+ sc );
+
+ return NULL;
+ }
+
+ mechs = ldap_str2charray( mechstr, "," );
+ }
+#elif defined(SLAP_BUILTIN_SASL)
+ /* builtin SASL implementation */
+ SASL_CTX *ctx = conn->c_sasl_authctx;
+ if ( ctx != NULL && ctx->sc_external_id.bv_val ) {
+ /* should check ssf */
+ mechs = ldap_str2charray( "EXTERNAL", "," );
+ }
+#endif
+
+ return mechs;
+}
+
+int slap_sasl_close( Connection *conn )
+{
+#ifdef HAVE_CYRUS_SASL
+ sasl_conn_t *ctx = conn->c_sasl_authctx;
+
+ if( ctx != NULL ) {
+ sasl_dispose( &ctx );
+ }
+ if ( conn->c_sasl_sockctx &&
+ conn->c_sasl_authctx != conn->c_sasl_sockctx )
+ {
+ ctx = conn->c_sasl_sockctx;
+ sasl_dispose( &ctx );
+ }
+
+ conn->c_sasl_authctx = NULL;
+ conn->c_sasl_sockctx = NULL;
+ conn->c_sasl_done = 0;
+
+ free( conn->c_sasl_extra );
+ conn->c_sasl_extra = NULL;
+
+ free( conn->c_sasl_cbind );
+ conn->c_sasl_cbind = NULL;
+
+#elif defined(SLAP_BUILTIN_SASL)
+ SASL_CTX *ctx = conn->c_sasl_authctx;
+ if( ctx ) {
+ if( ctx->sc_external_id.bv_val ) {
+ free( ctx->sc_external_id.bv_val );
+ BER_BVZERO( &ctx->sc_external_id );
+ }
+ free( ctx );
+ conn->c_sasl_authctx = NULL;
+ }
+#endif
+
+ return LDAP_SUCCESS;
+}
+
+int slap_sasl_bind( Operation *op, SlapReply *rs )
+{
+#ifdef HAVE_CYRUS_SASL
+ sasl_conn_t *ctx = op->o_conn->c_sasl_authctx;
+ struct berval response;
+ unsigned reslen = 0;
+ int sc;
+
+ Debug(LDAP_DEBUG_ARGS,
+ "==> sasl_bind: dn=\"%s\" mech=%s datalen=%ld\n",
+ op->o_req_dn.bv_len ? op->o_req_dn.bv_val : "",
+ op->o_conn->c_sasl_bind_in_progress ? "<continuing>" :
+ op->o_conn->c_sasl_bind_mech.bv_val,
+ op->orb_cred.bv_len );
+
+ if( ctx == NULL ) {
+ send_ldap_error( op, rs, LDAP_UNAVAILABLE,
+ "SASL unavailable on this session" );
+ return rs->sr_err;
+ }
+
+#define START( ctx, mech, cred, clen, resp, rlen, err ) \
+ sasl_server_start( ctx, mech, cred, clen, resp, rlen )
+#define STEP( ctx, cred, clen, resp, rlen, err ) \
+ sasl_server_step( ctx, cred, clen, resp, rlen )
+
+ if ( !op->o_conn->c_sasl_bind_in_progress ) {
+ /* If we already authenticated once, must use a new context */
+ if ( op->o_conn->c_sasl_done ) {
+ sasl_ssf_t ssf = 0;
+ sasl_ssf_t *ssfp = NULL;
+ const char *authid = NULL;
+
+ sasl_getprop( ctx, SASL_SSF_EXTERNAL, (void *)&ssfp );
+ if ( ssfp ) ssf = *ssfp;
+
+ sasl_getprop( ctx, SASL_AUTH_EXTERNAL, (void *)&authid );
+ if ( authid ) authid = ch_strdup( authid );
+
+ if ( ctx != op->o_conn->c_sasl_sockctx ) {
+ sasl_dispose( &ctx );
+ }
+ op->o_conn->c_sasl_authctx = NULL;
+
+ slap_sasl_open( op->o_conn, 1 );
+ ctx = op->o_conn->c_sasl_authctx;
+ sasl_setprop( ctx, SASL_SSF_EXTERNAL, &ssf );
+ if ( authid ) {
+ sasl_setprop( ctx, SASL_AUTH_EXTERNAL, authid );
+ ch_free( (char *)authid );
+ }
+ }
+ sc = START( ctx,
+ op->o_conn->c_sasl_bind_mech.bv_val,
+ op->orb_cred.bv_val, op->orb_cred.bv_len,
+ (SASL_CONST char **)&response.bv_val, &reslen, &rs->sr_text );
+
+ } else {
+ sc = STEP( ctx,
+ op->orb_cred.bv_val, op->orb_cred.bv_len,
+ (SASL_CONST char **)&response.bv_val, &reslen, &rs->sr_text );
+ }
+
+ response.bv_len = reslen;
+
+ if ( sc == SASL_OK ) {
+ sasl_ssf_t *ssf = NULL;
+
+ ber_dupbv_x( &op->orb_edn, &op->o_conn->c_sasl_dn, op->o_tmpmemctx );
+ BER_BVZERO( &op->o_conn->c_sasl_dn );
+ op->o_conn->c_sasl_done = 1;
+
+ rs->sr_err = LDAP_SUCCESS;
+
+ (void) sasl_getprop( ctx, SASL_SSF, (void *)&ssf );
+ op->orb_ssf = ssf ? *ssf : 0;
+
+ ctx = NULL;
+ if( op->orb_ssf ) {
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+ op->o_conn->c_sasl_layers++;
+
+ /* If there's an old layer, set sockctx to NULL to
+ * tell connection_read() to wait for us to finish.
+ * Otherwise there is a race condition: we have to
+ * send the Bind response using the old security
+ * context and then remove it before reading any
+ * new messages.
+ */
+ if ( op->o_conn->c_sasl_sockctx ) {
+ ctx = op->o_conn->c_sasl_sockctx;
+ op->o_conn->c_sasl_sockctx = NULL;
+ } else {
+ op->o_conn->c_sasl_sockctx = op->o_conn->c_sasl_authctx;
+ }
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+ }
+
+ /* Must send response using old security layer */
+ rs->sr_sasldata = (response.bv_len ? &response : NULL);
+ send_ldap_sasl( op, rs );
+
+ /* Now dispose of the old security layer.
+ */
+ if ( ctx ) {
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+ ldap_pvt_sasl_remove( op->o_conn->c_sb );
+ op->o_conn->c_sasl_sockctx = op->o_conn->c_sasl_authctx;
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+ sasl_dispose( &ctx );
+ }
+ } else if ( sc == SASL_CONTINUE ) {
+ rs->sr_err = LDAP_SASL_BIND_IN_PROGRESS,
+ rs->sr_text = sasl_errdetail( ctx );
+ rs->sr_sasldata = &response;
+ send_ldap_sasl( op, rs );
+
+ } else {
+ BER_BVZERO( &op->o_conn->c_sasl_dn );
+ rs->sr_text = sasl_errdetail( ctx );
+ rs->sr_err = slap_sasl_err2ldap( sc ),
+ send_ldap_result( op, rs );
+ }
+
+ Debug(LDAP_DEBUG_TRACE, "<== slap_sasl_bind: rc=%d\n", rs->sr_err );
+
+#elif defined(SLAP_BUILTIN_SASL)
+ /* built-in SASL implementation */
+ SASL_CTX *ctx = op->o_conn->c_sasl_authctx;
+
+ if ( ctx == NULL ) {
+ send_ldap_error( op, rs, LDAP_OTHER,
+ "Internal SASL Error" );
+
+ } else if ( bvmatch( &ext_bv, &op->o_conn->c_sasl_bind_mech ) ) {
+ /* EXTERNAL */
+
+ if( op->orb_cred.bv_len ) {
+ rs->sr_text = "proxy authorization not supported";
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ send_ldap_result( op, rs );
+
+ } else {
+ op->orb_edn = ctx->sc_external_id;
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_sasldata = NULL;
+ send_ldap_sasl( op, rs );
+ }
+
+ } else {
+ send_ldap_error( op, rs, LDAP_AUTH_METHOD_NOT_SUPPORTED,
+ "requested SASL mechanism not supported" );
+ }
+#else
+ send_ldap_error( op, rs, LDAP_AUTH_METHOD_NOT_SUPPORTED,
+ "SASL not supported" );
+#endif
+
+ return rs->sr_err;
+}
+
+char* slap_sasl_secprops( const char *in )
+{
+#ifdef HAVE_CYRUS_SASL
+ int rc = ldap_pvt_sasl_secprops( in, &sasl_secprops );
+
+ return rc == LDAP_SUCCESS ? NULL : "Invalid security properties";
+#else
+ return "SASL not supported";
+#endif
+}
+
+void slap_sasl_secprops_unparse( struct berval *bv )
+{
+#ifdef HAVE_CYRUS_SASL
+ ldap_pvt_sasl_secprops_unparse( &sasl_secprops, bv );
+#endif
+}
+
+#ifdef HAVE_CYRUS_SASL
+int
+slap_sasl_setpass( Operation *op, SlapReply *rs )
+{
+ struct berval id = BER_BVNULL; /* needs to come from connection */
+ struct berval new = BER_BVNULL;
+ struct berval old = BER_BVNULL;
+
+ assert( ber_bvcmp( &slap_EXOP_MODIFY_PASSWD, &op->ore_reqoid ) == 0 );
+
+ rs->sr_err = sasl_getprop( op->o_conn->c_sasl_authctx, SASL_USERNAME,
+ (SASL_CONST void **)(char *)&id.bv_val );
+
+ if( rs->sr_err != SASL_OK ) {
+ rs->sr_text = "unable to retrieve SASL username";
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_ARGS, "==> slap_sasl_setpass: \"%s\"\n",
+ id.bv_val ? id.bv_val : "" );
+
+ rs->sr_err = slap_passwd_parse( op->ore_reqdata,
+ NULL, &old, &new, &rs->sr_text );
+
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ if( new.bv_len == 0 ) {
+ slap_passwd_generate(&new);
+
+ if( new.bv_len == 0 ) {
+ rs->sr_text = "password generation failed.";
+ rs->sr_err = LDAP_OTHER;
+ goto done;
+ }
+
+ rs->sr_rspdata = slap_passwd_return( &new );
+ }
+
+ rs->sr_err = sasl_setpass( op->o_conn->c_sasl_authctx, id.bv_val,
+ new.bv_val, new.bv_len, old.bv_val, old.bv_len, 0 );
+ if( rs->sr_err != SASL_OK ) {
+ rs->sr_text = sasl_errdetail( op->o_conn->c_sasl_authctx );
+ }
+ switch(rs->sr_err) {
+ case SASL_OK:
+ rs->sr_err = LDAP_SUCCESS;
+ break;
+
+ case SASL_NOCHANGE:
+ case SASL_NOMECH:
+ case SASL_DISABLED:
+ case SASL_PWLOCK:
+ case SASL_FAIL:
+ case SASL_BADPARAM:
+ default:
+ rs->sr_err = LDAP_OTHER;
+ }
+
+done:
+ return rs->sr_err;
+}
+#endif /* HAVE_CYRUS_SASL */
+
+/* Take any sort of identity string and return a DN with the "dn:" prefix. The
+ * string returned in *dn is in its own allocated memory, and must be free'd
+ * by the calling process. -Mark Adamson, Carnegie Mellon
+ *
+ * The "dn:" prefix is no longer used anywhere inside slapd. It is only used
+ * on strings passed in directly from SASL. -Howard Chu, Symas Corp.
+ */
+
+#define SET_NONE 0
+#define SET_DN 1
+#define SET_U 2
+
+int slap_sasl_getdn( Connection *conn, Operation *op, struct berval *id,
+ char *user_realm, struct berval *dn, int flags )
+{
+ int rc, is_dn = SET_NONE, do_norm = 1;
+ struct berval dn2, *mech;
+
+ assert( conn != NULL );
+ assert( id != NULL );
+
+ Debug( LDAP_DEBUG_ARGS, "slap_sasl_getdn: conn %lu id=%s [len=%lu]\n",
+ conn->c_connid,
+ BER_BVISNULL( id ) ? "NULL" : ( BER_BVISEMPTY( id ) ? "<empty>" : id->bv_val ),
+ BER_BVISNULL( id ) ? 0 : ( BER_BVISEMPTY( id ) ? 0 :
+ (unsigned long) id->bv_len ) );
+
+ if ( !op ) {
+ op = conn->c_sasl_bindop;
+ }
+ assert( op != NULL );
+
+ BER_BVZERO( dn );
+
+ if ( !BER_BVISNULL( id ) ) {
+ /* Blatantly anonymous ID */
+ static struct berval bv_anonymous = BER_BVC( "anonymous" );
+
+ if ( ber_bvstrcasecmp( id, &bv_anonymous ) == 0 ) {
+ return( LDAP_SUCCESS );
+ }
+
+ } else {
+ /* FIXME: if empty, should we stop? */
+ BER_BVSTR( id, "" );
+ }
+
+ if ( !BER_BVISEMPTY( &conn->c_sasl_bind_mech ) ) {
+ mech = &conn->c_sasl_bind_mech;
+ } else {
+ mech = &conn->c_authmech;
+ }
+
+ /* An authcID needs to be converted to authzID form. Set the
+ * values directly into *dn; they will be normalized later. (and
+ * normalizing always makes a new copy.) An ID from a TLS certificate
+ * is already normalized, so copy it and skip normalization.
+ */
+ if( flags & SLAP_GETDN_AUTHCID ) {
+ if( bvmatch( mech, &ext_bv )) {
+ /* EXTERNAL DNs are already normalized */
+ assert( !BER_BVISNULL( id ) );
+
+ do_norm = 0;
+ is_dn = SET_DN;
+ ber_dupbv_x( dn, id, op->o_tmpmemctx );
+
+ } else {
+ /* convert to u:<username> form */
+ is_dn = SET_U;
+ *dn = *id;
+ }
+ }
+
+ if( is_dn == SET_NONE ) {
+ if( !strncasecmp( id->bv_val, "u:", STRLENOF( "u:" ) ) ) {
+ is_dn = SET_U;
+ dn->bv_val = id->bv_val + STRLENOF( "u:" );
+ dn->bv_len = id->bv_len - STRLENOF( "u:" );
+
+ } else if ( !strncasecmp( id->bv_val, "dn:", STRLENOF( "dn:" ) ) ) {
+ is_dn = SET_DN;
+ dn->bv_val = id->bv_val + STRLENOF( "dn:" );
+ dn->bv_len = id->bv_len - STRLENOF( "dn:" );
+ }
+ }
+
+ /* No other possibilities from here */
+ if( is_dn == SET_NONE ) {
+ BER_BVZERO( dn );
+ return( LDAP_INAPPROPRIATE_AUTH );
+ }
+
+ /* Username strings */
+ if( is_dn == SET_U ) {
+ /* ITS#3419: values may need escape */
+ LDAPRDN DN[ 5 ];
+ LDAPAVA *RDNs[ 4 ][ 2 ];
+ LDAPAVA AVAs[ 4 ];
+ int irdn;
+
+ irdn = 0;
+ DN[ irdn ] = RDNs[ irdn ];
+ RDNs[ irdn ][ 0 ] = &AVAs[ irdn ];
+ AVAs[ irdn ].la_attr = slap_schema.si_ad_uid->ad_cname;
+ AVAs[ irdn ].la_value = *dn;
+ AVAs[ irdn ].la_flags = LDAP_AVA_NULL;
+ AVAs[ irdn ].la_private = NULL;
+ RDNs[ irdn ][ 1 ] = NULL;
+
+ if ( user_realm && *user_realm ) {
+ irdn++;
+ DN[ irdn ] = RDNs[ irdn ];
+ RDNs[ irdn ][ 0 ] = &AVAs[ irdn ];
+ AVAs[ irdn ].la_attr = slap_schema.si_ad_cn->ad_cname;
+ ber_str2bv( user_realm, 0, 0, &AVAs[ irdn ].la_value );
+ AVAs[ irdn ].la_flags = LDAP_AVA_NULL;
+ AVAs[ irdn ].la_private = NULL;
+ RDNs[ irdn ][ 1 ] = NULL;
+ }
+
+ if ( !BER_BVISNULL( mech ) ) {
+ irdn++;
+ DN[ irdn ] = RDNs[ irdn ];
+ RDNs[ irdn ][ 0 ] = &AVAs[ irdn ];
+ AVAs[ irdn ].la_attr = slap_schema.si_ad_cn->ad_cname;
+ AVAs[ irdn ].la_value = *mech;
+ AVAs[ irdn ].la_flags = LDAP_AVA_NULL;
+ AVAs[ irdn ].la_private = NULL;
+ RDNs[ irdn ][ 1 ] = NULL;
+ }
+
+ irdn++;
+ DN[ irdn ] = RDNs[ irdn ];
+ RDNs[ irdn ][ 0 ] = &AVAs[ irdn ];
+ AVAs[ irdn ].la_attr = slap_schema.si_ad_cn->ad_cname;
+ BER_BVSTR( &AVAs[ irdn ].la_value, "auth" );
+ AVAs[ irdn ].la_flags = LDAP_AVA_NULL;
+ AVAs[ irdn ].la_private = NULL;
+ RDNs[ irdn ][ 1 ] = NULL;
+
+ irdn++;
+ DN[ irdn ] = NULL;
+
+ rc = ldap_dn2bv_x( DN, dn, LDAP_DN_FORMAT_LDAPV3,
+ op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ BER_BVZERO( dn );
+ return rc;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "slap_sasl_getdn: u:id converted to %s\n",
+ dn->bv_val );
+
+ } else {
+
+ /* Dup the DN in any case, so we don't risk
+ * leaks or dangling pointers later,
+ * and the DN value is '\0' terminated */
+ ber_dupbv_x( &dn2, dn, op->o_tmpmemctx );
+ dn->bv_val = dn2.bv_val;
+ }
+
+ /* All strings are in DN form now. Normalize if needed. */
+ if ( do_norm ) {
+ rc = dnNormalize( 0, NULL, NULL, dn, &dn2, op->o_tmpmemctx );
+
+ /* User DNs were constructed above and must be freed now */
+ slap_sl_free( dn->bv_val, op->o_tmpmemctx );
+
+ if ( rc != LDAP_SUCCESS ) {
+ BER_BVZERO( dn );
+ return rc;
+ }
+ *dn = dn2;
+ }
+
+ /* Run thru regexp */
+ slap_sasl2dn( op, dn, &dn2, flags );
+ if( !BER_BVISNULL( &dn2 ) ) {
+ slap_sl_free( dn->bv_val, op->o_tmpmemctx );
+ *dn = dn2;
+ Debug( LDAP_DEBUG_TRACE,
+ "slap_sasl_getdn: dn:id converted to %s\n",
+ dn->bv_val );
+ }
+
+ return( LDAP_SUCCESS );
+}
diff --git a/servers/slapd/saslauthz.c b/servers/slapd/saslauthz.c
new file mode 100644
index 0000000..797f287
--- /dev/null
+++ b/servers/slapd/saslauthz.c
@@ -0,0 +1,2193 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2000 Mark Adamson, Carnegie Mellon.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+
+#include "slap.h"
+
+#include "lutil.h"
+#include "slap-config.h"
+
+#define SASLREGEX_REPLACE 10
+
+#define LDAP_X_SCOPE_EXACT ((ber_int_t) 0x0010)
+#define LDAP_X_SCOPE_REGEX ((ber_int_t) 0x0020)
+#define LDAP_X_SCOPE_CHILDREN ((ber_int_t) 0x0030)
+#define LDAP_X_SCOPE_SUBTREE ((ber_int_t) 0x0040)
+#define LDAP_X_SCOPE_ONELEVEL ((ber_int_t) 0x0050)
+#define LDAP_X_SCOPE_GROUP ((ber_int_t) 0x0060)
+#define LDAP_X_SCOPE_USERS ((ber_int_t) 0x0070)
+
+/*
+ * IDs in DNauthzid form can now have a type specifier, that
+ * influences how they are used in related operations.
+ *
+ * syntax: dn[.{exact|regex}]:<val>
+ *
+ * dn.exact: the value must pass normalization and is used
+ * in exact DN match.
+ * dn.regex: the value is treated as a regular expression
+ * in matching DN values in authz{To|From}
+ * attributes.
+ * dn: for backwards compatibility reasons, the value
+ * is treated as a regular expression, and thus
+ * it is not normalized nor validated; it is used
+ * in exact or regex comparisons based on the
+ * context.
+ *
+ * IDs in DNauthzid form can now have a type specifier, that
+ * influences how they are used in related operations.
+ *
+ * syntax: u[.mech[/realm]]:<val>
+ *
+ * where mech is a SIMPLE, AUTHZ, or a SASL mechanism name
+ * and realm is mechanism specific realm (separate to those
+ * which are representable as part of the principal).
+ */
+
+typedef struct sasl_regexp {
+ char *sr_match; /* regexp match pattern */
+ char *sr_replace; /* regexp replace pattern */
+} SaslRegexp_t;
+
+static int nSaslRegexp = 0;
+static SaslRegexp_t *SaslRegexp = NULL;
+
+#include "rewrite.h"
+struct rewrite_info *sasl_rwinfo = NULL;
+#define AUTHID_CONTEXT "authid"
+static BerVarray authz_rewrites = NULL;
+
+/* What SASL proxy authorization policies are allowed? */
+#define SASL_AUTHZ_NONE 0x00
+#define SASL_AUTHZ_FROM 0x01
+#define SASL_AUTHZ_TO 0x02
+#define SASL_AUTHZ_AND 0x10
+
+static const char *policy_txt[] = {
+ "none", "from", "to", "any"
+};
+
+static int authz_policy = SASL_AUTHZ_NONE;
+
+static int
+slap_sasl_match( Operation *opx, struct berval *rule,
+ struct berval *assertDN, struct berval *authc );
+
+int slap_sasl_setpolicy( const char *arg )
+{
+ int rc = LDAP_SUCCESS;
+
+ if ( strcasecmp( arg, "none" ) == 0 ) {
+ authz_policy = SASL_AUTHZ_NONE;
+ } else if ( strcasecmp( arg, "from" ) == 0 ) {
+ authz_policy = SASL_AUTHZ_FROM;
+ } else if ( strcasecmp( arg, "to" ) == 0 ) {
+ authz_policy = SASL_AUTHZ_TO;
+ } else if ( strcasecmp( arg, "both" ) == 0 || strcasecmp( arg, "any" ) == 0 ) {
+ authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO;
+ } else if ( strcasecmp( arg, "all" ) == 0 ) {
+ authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND;
+ } else {
+ rc = LDAP_OTHER;
+ }
+ return rc;
+}
+
+const char * slap_sasl_getpolicy()
+{
+ if ( authz_policy == (SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND) )
+ return "all";
+ else
+ return policy_txt[authz_policy];
+}
+
+int slap_parse_user( struct berval *id, struct berval *user,
+ struct berval *realm, struct berval *mech )
+{
+ char u;
+
+ assert( id != NULL );
+ assert( !BER_BVISNULL( id ) );
+ assert( user != NULL );
+ assert( realm != NULL );
+ assert( mech != NULL );
+
+ u = id->bv_val[ 0 ];
+
+ if ( u != 'u' && u != 'U' ) {
+ /* called with something other than u: */
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ /* uauthzid form:
+ * u[.mech[/realm]]:user
+ */
+
+ user->bv_val = ber_bvchr( id, ':' );
+ if ( BER_BVISNULL( user ) ) {
+ return LDAP_PROTOCOL_ERROR;
+ }
+ user->bv_val[ 0 ] = '\0';
+ user->bv_val++;
+ user->bv_len = id->bv_len - ( user->bv_val - id->bv_val );
+
+ if ( id->bv_val[1] == '.' ) {
+ id->bv_val[1] = '\0';
+ mech->bv_val = id->bv_val + 2;
+ mech->bv_len = user->bv_val - mech->bv_val - 1;
+
+ realm->bv_val = ber_bvchr( mech, '/' );
+
+ if ( !BER_BVISNULL( realm ) ) {
+ realm->bv_val[ 0 ] = '\0';
+ realm->bv_val++;
+ mech->bv_len = realm->bv_val - mech->bv_val - 1;
+ realm->bv_len = user->bv_val - realm->bv_val - 1;
+ }
+
+ } else {
+ BER_BVZERO( mech );
+ BER_BVZERO( realm );
+ }
+
+ if ( id->bv_val[ 1 ] != '\0' ) {
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( !BER_BVISNULL( mech ) ) {
+ if ( mech->bv_val != id->bv_val + 2 )
+ return LDAP_PROTOCOL_ERROR;
+
+ AC_MEMCPY( mech->bv_val - 2, mech->bv_val, mech->bv_len + 1 );
+ mech->bv_val -= 2;
+ }
+
+ if ( !BER_BVISNULL( realm ) ) {
+ if ( realm->bv_val < id->bv_val + 2 )
+ return LDAP_PROTOCOL_ERROR;
+
+ AC_MEMCPY( realm->bv_val - 2, realm->bv_val, realm->bv_len + 1 );
+ realm->bv_val -= 2;
+ }
+
+ /* leave "u:" before user */
+ user->bv_val -= 2;
+ user->bv_len += 2;
+ user->bv_val[ 0 ] = u;
+ user->bv_val[ 1 ] = ':';
+
+ return LDAP_SUCCESS;
+}
+
+int
+authzValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ struct berval bv;
+ int rc = LDAP_INVALID_SYNTAX;
+ LDAPURLDesc *ludp = NULL;
+ int scope = -1;
+
+ /*
+ * 1) <DN>
+ * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
+ * 3) dn.regex:<pattern>
+ * 4) u[.mech[/realm]]:<ID>
+ * 5) group[/<groupClass>[/<memberAttr>]]:<DN>
+ * 6) <URL>
+ */
+
+ assert( in != NULL );
+ assert( !BER_BVISNULL( in ) );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "authzValidate: parsing %s\n", in->bv_val );
+
+ /*
+ * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
+ * 3) dn.regex:<pattern>
+ *
+ * <DN> must pass DN normalization
+ */
+ if ( !strncasecmp( in->bv_val, "dn", STRLENOF( "dn" ) ) ) {
+ bv.bv_val = in->bv_val + STRLENOF( "dn" );
+
+ if ( bv.bv_val[ 0 ] == '.' ) {
+ bv.bv_val++;
+
+ if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
+ bv.bv_val += STRLENOF( "exact:" );
+ scope = LDAP_X_SCOPE_EXACT;
+
+ } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
+ bv.bv_val += STRLENOF( "regex:" );
+ scope = LDAP_X_SCOPE_REGEX;
+
+ } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
+ bv.bv_val += STRLENOF( "children:" );
+ scope = LDAP_X_SCOPE_CHILDREN;
+
+ } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
+ bv.bv_val += STRLENOF( "subtree:" );
+ scope = LDAP_X_SCOPE_SUBTREE;
+
+ } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
+ bv.bv_val += STRLENOF( "onelevel:" );
+ scope = LDAP_X_SCOPE_ONELEVEL;
+
+ } else {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ } else {
+ if ( bv.bv_val[ 0 ] != ':' ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ scope = LDAP_X_SCOPE_EXACT;
+ bv.bv_val++;
+ }
+
+ bv.bv_val += strspn( bv.bv_val, " " );
+ /* jump here in case no type specification was present
+ * and uri was not an URI... HEADS-UP: assuming EXACT */
+is_dn: bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val );
+
+ /* a single '*' means any DN without using regexes */
+ if ( ber_bvccmp( &bv, '*' ) ) {
+ /* LDAP_X_SCOPE_USERS */
+ return LDAP_SUCCESS;
+ }
+
+ switch ( scope ) {
+ case LDAP_X_SCOPE_EXACT:
+ case LDAP_X_SCOPE_CHILDREN:
+ case LDAP_X_SCOPE_SUBTREE:
+ case LDAP_X_SCOPE_ONELEVEL:
+ return dnValidate( NULL, &bv );
+
+ case LDAP_X_SCOPE_REGEX:
+ return LDAP_SUCCESS;
+ }
+
+ return rc;
+
+ /*
+ * 4) u[.mech[/realm]]:<ID>
+ */
+ } else if ( ( in->bv_val[ 0 ] == 'u' || in->bv_val[ 0 ] == 'U' )
+ && ( in->bv_val[ 1 ] == ':'
+ || in->bv_val[ 1 ] == '/'
+ || in->bv_val[ 1 ] == '.' ) )
+ {
+ char buf[ SLAP_LDAPDN_MAXLEN ];
+ struct berval id,
+ user = BER_BVNULL,
+ realm = BER_BVNULL,
+ mech = BER_BVNULL;
+
+ if ( sizeof( buf ) <= in->bv_len ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ id.bv_len = in->bv_len;
+ id.bv_val = buf;
+ strncpy( buf, in->bv_val, sizeof( buf ) );
+
+ rc = slap_parse_user( &id, &user, &realm, &mech );
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ return rc;
+
+ /*
+ * 5) group[/groupClass[/memberAttr]]:<DN>
+ *
+ * <groupClass> defaults to "groupOfNames"
+ * <memberAttr> defaults to "member"
+ *
+ * <DN> must pass DN normalization
+ */
+ } else if ( strncasecmp( in->bv_val, "group", STRLENOF( "group" ) ) == 0 )
+ {
+ struct berval group_dn = BER_BVNULL,
+ group_oc = BER_BVNULL,
+ member_at = BER_BVNULL;
+
+ bv.bv_val = in->bv_val + STRLENOF( "group" );
+ bv.bv_len = in->bv_len - STRLENOF( "group" );
+ group_dn.bv_val = ber_bvchr( &bv, ':' );
+ if ( group_dn.bv_val == NULL ) {
+ /* last chance: assume it's a(n exact) DN ... */
+ bv.bv_val = in->bv_val;
+ scope = LDAP_X_SCOPE_EXACT;
+ goto is_dn;
+ }
+
+ /*
+ * FIXME: we assume that "member" and "groupOfNames"
+ * are present in schema...
+ */
+ if ( bv.bv_val[ 0 ] == '/' ) {
+ group_oc.bv_val = &bv.bv_val[ 1 ];
+ group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
+
+ member_at.bv_val = ber_bvchr( &group_oc, '/' );
+ if ( member_at.bv_val ) {
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+
+ group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
+ member_at.bv_val++;
+ member_at.bv_len = group_dn.bv_val - member_at.bv_val;
+ rc = slap_bv2ad( &member_at, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+ }
+
+ if ( oc_bvfind( &group_oc ) == NULL ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ group_dn.bv_val++;
+ group_dn.bv_len = in->bv_len - ( group_dn.bv_val - in->bv_val );
+
+ rc = dnValidate( NULL, &group_dn );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ return rc;
+ }
+
+ /*
+ * ldap:///<base>??<scope>?<filter>
+ * <scope> ::= {base|one|subtree}
+ *
+ * <scope> defaults to "base"
+ * <base> must pass DN normalization
+ * <filter> must pass str2filter()
+ */
+ rc = ldap_url_parse( in->bv_val, &ludp );
+ switch ( rc ) {
+ case LDAP_URL_SUCCESS:
+ /* FIXME: the check is pedantic, but I think it's necessary,
+ * because people tend to use things like ldaps:// which
+ * gives the idea SSL is being used. Maybe we could
+ * accept ldapi:// as well, but the point is that we use
+ * an URL as an easy means to define bits of a search with
+ * little parsing.
+ */
+ if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
+ /*
+ * must be ldap:///
+ */
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+ break;
+
+ case LDAP_URL_ERR_BADSCHEME:
+ /*
+ * last chance: assume it's a(n exact) DN ...
+ *
+ * NOTE: must pass DN normalization
+ */
+ ldap_free_urldesc( ludp );
+ bv.bv_val = in->bv_val;
+ scope = LDAP_X_SCOPE_EXACT;
+ goto is_dn;
+
+ default:
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ if ( ( ludp->lud_host && *ludp->lud_host )
+ || ludp->lud_attrs || ludp->lud_exts )
+ {
+ /* host part must be empty */
+ /* attrs and extensions parts must be empty */
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ /* Grab the filter */
+ if ( ludp->lud_filter ) {
+ Filter *f = str2filter( ludp->lud_filter );
+ if ( f == NULL ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+ filter_free( f );
+ }
+
+ /* Grab the searchbase */
+ if ( ludp->lud_dn != NULL ) {
+ ber_str2bv( ludp->lud_dn, 0, 0, &bv );
+ rc = dnValidate( NULL, &bv );
+ } else {
+ rc = LDAP_INVALID_SYNTAX;
+ }
+
+done:
+ ldap_free_urldesc( ludp );
+ return( rc );
+}
+
+static int
+authzPrettyNormal(
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx,
+ int normalize )
+{
+ struct berval bv;
+ int rc = LDAP_INVALID_SYNTAX;
+ LDAPURLDesc *ludp = NULL;
+ char *lud_dn = NULL,
+ *lud_filter = NULL;
+ int scope = -1;
+
+ /*
+ * 1) <DN>
+ * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
+ * 3) dn.regex:<pattern>
+ * 4) u[.mech[/realm]]:<ID>
+ * 5) group[/<groupClass>[/<memberAttr>]]:<DN>
+ * 6) <URL>
+ */
+
+ assert( val != NULL );
+ assert( !BER_BVISNULL( val ) );
+ BER_BVZERO( normalized );
+
+ /*
+ * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
+ * 3) dn.regex:<pattern>
+ *
+ * <DN> must pass DN normalization
+ */
+ if ( !strncasecmp( val->bv_val, "dn", STRLENOF( "dn" ) ) ) {
+ struct berval out = BER_BVNULL,
+ prefix = BER_BVNULL;
+ char *ptr;
+
+ bv.bv_val = val->bv_val + STRLENOF( "dn" );
+
+ if ( bv.bv_val[ 0 ] == '.' ) {
+ bv.bv_val++;
+
+ if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
+ bv.bv_val += STRLENOF( "exact:" );
+ scope = LDAP_X_SCOPE_EXACT;
+
+ } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
+ bv.bv_val += STRLENOF( "regex:" );
+ scope = LDAP_X_SCOPE_REGEX;
+
+ } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
+ bv.bv_val += STRLENOF( "children:" );
+ scope = LDAP_X_SCOPE_CHILDREN;
+
+ } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
+ bv.bv_val += STRLENOF( "subtree:" );
+ scope = LDAP_X_SCOPE_SUBTREE;
+
+ } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
+ bv.bv_val += STRLENOF( "onelevel:" );
+ scope = LDAP_X_SCOPE_ONELEVEL;
+
+ } else {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ } else {
+ if ( bv.bv_val[ 0 ] != ':' ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ scope = LDAP_X_SCOPE_EXACT;
+ bv.bv_val++;
+ }
+
+ bv.bv_val += strspn( bv.bv_val, " " );
+ /* jump here in case no type specification was present
+ * and uri was not an URI... HEADS-UP: assuming EXACT */
+is_dn: bv.bv_len = val->bv_len - ( bv.bv_val - val->bv_val );
+
+ /* a single '*' means any DN without using regexes */
+ if ( ber_bvccmp( &bv, '*' ) ) {
+ ber_str2bv_x( "dn:*", STRLENOF( "dn:*" ), 1, normalized, ctx );
+ return LDAP_SUCCESS;
+ }
+
+ switch ( scope ) {
+ case LDAP_X_SCOPE_EXACT:
+ case LDAP_X_SCOPE_CHILDREN:
+ case LDAP_X_SCOPE_SUBTREE:
+ case LDAP_X_SCOPE_ONELEVEL:
+ if ( normalize ) {
+ rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
+ } else {
+ rc = dnPretty( NULL, &bv, &out, ctx );
+ }
+ if( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ break;
+
+ case LDAP_X_SCOPE_REGEX:
+ normalized->bv_len = STRLENOF( "dn.regex:" ) + bv.bv_len;
+ normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
+ ptr = lutil_strcopy( normalized->bv_val, "dn.regex:" );
+ ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
+ ptr[ 0 ] = '\0';
+ return LDAP_SUCCESS;
+
+ default:
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* prepare prefix */
+ switch ( scope ) {
+ case LDAP_X_SCOPE_EXACT:
+ BER_BVSTR( &prefix, "dn:" );
+ break;
+
+ case LDAP_X_SCOPE_CHILDREN:
+ BER_BVSTR( &prefix, "dn.children:" );
+ break;
+
+ case LDAP_X_SCOPE_SUBTREE:
+ BER_BVSTR( &prefix, "dn.subtree:" );
+ break;
+
+ case LDAP_X_SCOPE_ONELEVEL:
+ BER_BVSTR( &prefix, "dn.onelevel:" );
+ break;
+
+ default:
+ assert( 0 );
+ break;
+ }
+
+ normalized->bv_len = prefix.bv_len + out.bv_len;
+ normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
+
+ ptr = lutil_strcopy( normalized->bv_val, prefix.bv_val );
+ ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
+ ptr[ 0 ] = '\0';
+ ber_memfree_x( out.bv_val, ctx );
+
+ return LDAP_SUCCESS;
+
+ /*
+ * 4) u[.mech[/realm]]:<ID>
+ */
+ } else if ( ( val->bv_val[ 0 ] == 'u' || val->bv_val[ 0 ] == 'U' )
+ && ( val->bv_val[ 1 ] == ':'
+ || val->bv_val[ 1 ] == '/'
+ || val->bv_val[ 1 ] == '.' ) )
+ {
+ char buf[ SLAP_LDAPDN_MAXLEN ];
+ struct berval id,
+ user = BER_BVNULL,
+ realm = BER_BVNULL,
+ mech = BER_BVNULL;
+
+ if ( sizeof( buf ) <= val->bv_len ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ id.bv_len = val->bv_len;
+ id.bv_val = buf;
+ strncpy( buf, val->bv_val, sizeof( buf ) );
+
+ rc = slap_parse_user( &id, &user, &realm, &mech );
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ ber_dupbv_x( normalized, val, ctx );
+
+ return rc;
+
+ /*
+ * 5) group[/groupClass[/memberAttr]]:<DN>
+ *
+ * <groupClass> defaults to "groupOfNames"
+ * <memberAttr> defaults to "member"
+ *
+ * <DN> must pass DN normalization
+ */
+ } else if ( strncasecmp( val->bv_val, "group", STRLENOF( "group" ) ) == 0 )
+ {
+ struct berval group_dn = BER_BVNULL,
+ group_oc = BER_BVNULL,
+ member_at = BER_BVNULL,
+ out = BER_BVNULL;
+ char *ptr;
+
+ bv.bv_val = val->bv_val + STRLENOF( "group" );
+ bv.bv_len = val->bv_len - STRLENOF( "group" );
+ group_dn.bv_val = ber_bvchr( &bv, ':' );
+ if ( group_dn.bv_val == NULL ) {
+ /* last chance: assume it's a(n exact) DN ... */
+ bv.bv_val = val->bv_val;
+ scope = LDAP_X_SCOPE_EXACT;
+ goto is_dn;
+ }
+
+ /*
+ * FIXME: we assume that "member" and "groupOfNames"
+ * are present in schema...
+ */
+ if ( bv.bv_val[ 0 ] == '/' ) {
+ ObjectClass *oc = NULL;
+
+ group_oc.bv_val = &bv.bv_val[ 1 ];
+ group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
+
+ member_at.bv_val = ber_bvchr( &group_oc, '/' );
+ if ( member_at.bv_val ) {
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+
+ group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
+ member_at.bv_val++;
+ member_at.bv_len = group_dn.bv_val - member_at.bv_val;
+ rc = slap_bv2ad( &member_at, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ member_at = ad->ad_cname;
+
+ }
+
+ oc = oc_bvfind( &group_oc );
+ if ( oc == NULL ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ group_oc = oc->soc_cname;
+ }
+
+ group_dn.bv_val++;
+ group_dn.bv_len = val->bv_len - ( group_dn.bv_val - val->bv_val );
+
+ if ( normalize ) {
+ rc = dnNormalize( 0, NULL, NULL, &group_dn, &out, ctx );
+ } else {
+ rc = dnPretty( NULL, &group_dn, &out, ctx );
+ }
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ normalized->bv_len = STRLENOF( "group" ":" ) + out.bv_len;
+ if ( !BER_BVISNULL( &group_oc ) ) {
+ normalized->bv_len += STRLENOF( "/" ) + group_oc.bv_len;
+ if ( !BER_BVISNULL( &member_at ) ) {
+ normalized->bv_len += STRLENOF( "/" ) + member_at.bv_len;
+ }
+ }
+
+ normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
+ ptr = lutil_strcopy( normalized->bv_val, "group" );
+ if ( !BER_BVISNULL( &group_oc ) ) {
+ ptr[ 0 ] = '/';
+ ptr++;
+ ptr = lutil_strncopy( ptr, group_oc.bv_val, group_oc.bv_len );
+ if ( !BER_BVISNULL( &member_at ) ) {
+ ptr[ 0 ] = '/';
+ ptr++;
+ ptr = lutil_strncopy( ptr, member_at.bv_val, member_at.bv_len );
+ }
+ }
+ ptr[ 0 ] = ':';
+ ptr++;
+ ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
+ ptr[ 0 ] = '\0';
+ ber_memfree_x( out.bv_val, ctx );
+
+ return rc;
+ }
+
+ /*
+ * ldap:///<base>??<scope>?<filter>
+ * <scope> ::= {base|one|subtree}
+ *
+ * <scope> defaults to "base"
+ * <base> must pass DN normalization
+ * <filter> must pass str2filter()
+ */
+ rc = ldap_url_parse( val->bv_val, &ludp );
+ switch ( rc ) {
+ case LDAP_URL_SUCCESS:
+ /* FIXME: the check is pedantic, but I think it's necessary,
+ * because people tend to use things like ldaps:// which
+ * gives the idea SSL is being used. Maybe we could
+ * accept ldapi:// as well, but the point is that we use
+ * an URL as an easy means to define bits of a search with
+ * little parsing.
+ */
+ if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
+ /*
+ * must be ldap:///
+ */
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ AC_MEMCPY( ludp->lud_scheme, "ldap", STRLENOF( "ldap" ) );
+ break;
+
+ case LDAP_URL_ERR_BADSCHEME:
+ /*
+ * last chance: assume it's a(n exact) DN ...
+ *
+ * NOTE: must pass DN normalization
+ */
+ ldap_free_urldesc( ludp );
+ bv.bv_val = val->bv_val;
+ scope = LDAP_X_SCOPE_EXACT;
+ goto is_dn;
+
+ default:
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ if ( ( ludp->lud_host && *ludp->lud_host )
+ || ludp->lud_attrs || ludp->lud_exts )
+ {
+ /* host part must be empty */
+ /* attrs and extensions parts must be empty */
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ /* Grab the filter */
+ if ( ludp->lud_filter ) {
+ struct berval filterstr;
+ Filter *f;
+
+ lud_filter = ludp->lud_filter;
+
+ f = str2filter( lud_filter );
+ if ( f == NULL ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+ filter2bv( f, &filterstr );
+ filter_free( f );
+ if ( BER_BVISNULL( &filterstr ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ ludp->lud_filter = filterstr.bv_val;
+ }
+
+ /* Grab the searchbase */
+ if ( ludp->lud_dn ) {
+ struct berval out = BER_BVNULL;
+
+ lud_dn = ludp->lud_dn;
+
+ ber_str2bv( lud_dn, 0, 0, &bv );
+ if ( normalize ) {
+ rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
+ } else {
+ rc = dnPretty( NULL, &bv, &out, ctx );
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ ludp->lud_dn = out.bv_val;
+ } else {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ ludp->lud_port = 0;
+ normalized->bv_val = ldap_url_desc2str( ludp );
+ if ( normalized->bv_val ) {
+ normalized->bv_len = strlen( normalized->bv_val );
+
+ } else {
+ rc = LDAP_INVALID_SYNTAX;
+ }
+
+done:
+ if ( lud_filter ) {
+ if ( ludp->lud_filter != lud_filter ) {
+ ber_memfree( ludp->lud_filter );
+ }
+ ludp->lud_filter = lud_filter;
+ }
+
+ if ( lud_dn ) {
+ if ( ludp->lud_dn != lud_dn ) {
+ slap_sl_free( ludp->lud_dn, ctx );
+ }
+ ludp->lud_dn = lud_dn;
+ }
+
+ ldap_free_urldesc( ludp );
+
+ return( rc );
+}
+
+int
+authzNormalize(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ int rc;
+
+ Debug( LDAP_DEBUG_TRACE, ">>> authzNormalize: <%s>\n",
+ val->bv_val );
+
+ rc = authzPrettyNormal( val, normalized, ctx, 1 );
+
+ Debug( LDAP_DEBUG_TRACE, "<<< authzNormalize: <%s> (%d)\n",
+ normalized->bv_val, rc );
+
+ return rc;
+}
+
+int
+authzPretty(
+ Syntax *syntax,
+ struct berval *val,
+ struct berval *out,
+ void *ctx)
+{
+ int rc;
+
+ Debug( LDAP_DEBUG_TRACE, ">>> authzPretty: <%s>\n",
+ val->bv_val );
+
+ rc = authzPrettyNormal( val, out, ctx, 0 );
+
+ Debug( LDAP_DEBUG_TRACE, "<<< authzPretty: <%s> (%d)\n",
+ out->bv_val ? out->bv_val : "(null)" , rc );
+
+ return rc;
+}
+
+
+static int
+slap_parseURI(
+ Operation *op,
+ struct berval *uri,
+ struct berval *base,
+ struct berval *nbase,
+ int *scope,
+ Filter **filter,
+ struct berval *fstr,
+ int normalize )
+{
+ struct berval bv;
+ int rc;
+ LDAPURLDesc *ludp;
+
+ struct berval idx;
+
+ assert( uri != NULL && !BER_BVISNULL( uri ) );
+ BER_BVZERO( base );
+ BER_BVZERO( nbase );
+ BER_BVZERO( fstr );
+ *scope = -1;
+ *filter = NULL;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "slap_parseURI: parsing %s\n", uri->bv_val );
+
+ rc = LDAP_PROTOCOL_ERROR;
+
+ idx = *uri;
+ if ( idx.bv_val[ 0 ] == '{' ) {
+ char *ptr;
+
+ ptr = ber_bvchr( &idx, '}' ) + 1;
+
+ assert( ptr != (void *)1 );
+
+ idx.bv_len -= ptr - idx.bv_val;
+ idx.bv_val = ptr;
+ uri = &idx;
+ }
+
+ /*
+ * dn[.<dnstyle>]:<dnpattern>
+ * <dnstyle> ::= {exact|regex|children|subtree|onelevel}
+ *
+ * <dnstyle> defaults to "exact"
+ * if <dnstyle> is not "regex", <dnpattern> must pass DN normalization
+ */
+ if ( !strncasecmp( uri->bv_val, "dn", STRLENOF( "dn" ) ) ) {
+ bv.bv_val = uri->bv_val + STRLENOF( "dn" );
+
+ if ( bv.bv_val[ 0 ] == '.' ) {
+ bv.bv_val++;
+
+ if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
+ bv.bv_val += STRLENOF( "exact:" );
+ *scope = LDAP_X_SCOPE_EXACT;
+
+ } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
+ bv.bv_val += STRLENOF( "regex:" );
+ *scope = LDAP_X_SCOPE_REGEX;
+
+ } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
+ bv.bv_val += STRLENOF( "children:" );
+ *scope = LDAP_X_SCOPE_CHILDREN;
+
+ } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
+ bv.bv_val += STRLENOF( "subtree:" );
+ *scope = LDAP_X_SCOPE_SUBTREE;
+
+ } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
+ bv.bv_val += STRLENOF( "onelevel:" );
+ *scope = LDAP_X_SCOPE_ONELEVEL;
+
+ } else {
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ } else {
+ if ( bv.bv_val[ 0 ] != ':' ) {
+ return LDAP_PROTOCOL_ERROR;
+ }
+ *scope = LDAP_X_SCOPE_EXACT;
+ bv.bv_val++;
+ }
+
+ bv.bv_val += strspn( bv.bv_val, " " );
+ /* jump here in case no type specification was present
+ * and uri was not an URI... HEADS-UP: assuming EXACT */
+is_dn: bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
+
+ /* a single '*' means any DN without using regexes */
+ if ( ber_bvccmp( &bv, '*' ) ) {
+ *scope = LDAP_X_SCOPE_USERS;
+ }
+
+ switch ( *scope ) {
+ case LDAP_X_SCOPE_EXACT:
+ case LDAP_X_SCOPE_CHILDREN:
+ case LDAP_X_SCOPE_SUBTREE:
+ case LDAP_X_SCOPE_ONELEVEL:
+ if ( normalize ) {
+ rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
+ if( rc != LDAP_SUCCESS ) {
+ *scope = -1;
+ }
+ } else {
+ ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
+ rc = LDAP_SUCCESS;
+ }
+ break;
+
+ case LDAP_X_SCOPE_REGEX:
+ ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
+
+ case LDAP_X_SCOPE_USERS:
+ rc = LDAP_SUCCESS;
+ break;
+
+ default:
+ *scope = -1;
+ break;
+ }
+
+ return rc;
+
+ /*
+ * u:<uid>
+ */
+ } else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' )
+ && ( uri->bv_val[ 1 ] == ':'
+ || uri->bv_val[ 1 ] == '/'
+ || uri->bv_val[ 1 ] == '.' ) )
+ {
+ Connection c = *op->o_conn;
+ char buf[ SLAP_LDAPDN_MAXLEN ];
+ struct berval id,
+ user = BER_BVNULL,
+ realm = BER_BVNULL,
+ mech = BER_BVNULL;
+
+ if ( sizeof( buf ) <= uri->bv_len ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ id.bv_len = uri->bv_len;
+ id.bv_val = buf;
+ strncpy( buf, uri->bv_val, sizeof( buf ) );
+
+ rc = slap_parse_user( &id, &user, &realm, &mech );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( !BER_BVISNULL( &mech ) ) {
+ c.c_sasl_bind_mech = mech;
+ } else {
+ BER_BVSTR( &c.c_sasl_bind_mech, "AUTHZ" );
+ }
+
+ rc = slap_sasl_getdn( &c, op, &user,
+ realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
+
+ if ( rc == LDAP_SUCCESS ) {
+ *scope = LDAP_X_SCOPE_EXACT;
+ }
+
+ return rc;
+
+ /*
+ * group[/<groupoc>[/<groupat>]]:<groupdn>
+ *
+ * groupoc defaults to "groupOfNames"
+ * groupat defaults to "member"
+ *
+ * <groupdn> must pass DN normalization
+ */
+ } else if ( strncasecmp( uri->bv_val, "group", STRLENOF( "group" ) ) == 0 )
+ {
+ struct berval group_dn = BER_BVNULL,
+ group_oc = BER_BVNULL,
+ member_at = BER_BVNULL;
+ char *tmp;
+
+ bv.bv_val = uri->bv_val + STRLENOF( "group" );
+ bv.bv_len = uri->bv_len - STRLENOF( "group" );
+ group_dn.bv_val = ber_bvchr( &bv, ':' );
+ if ( group_dn.bv_val == NULL ) {
+ /* last chance: assume it's a(n exact) DN ... */
+ bv.bv_val = uri->bv_val;
+ *scope = LDAP_X_SCOPE_EXACT;
+ goto is_dn;
+ }
+
+ if ( bv.bv_val[ 0 ] == '/' ) {
+ group_oc.bv_val = &bv.bv_val[ 1 ];
+ group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
+
+ member_at.bv_val = ber_bvchr( &group_oc, '/' );
+ if ( member_at.bv_val ) {
+ group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
+ member_at.bv_val++;
+ member_at.bv_len = group_dn.bv_val - member_at.bv_val;
+
+ } else {
+ BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
+ }
+
+ } else {
+ BER_BVSTR( &group_oc, SLAPD_GROUP_CLASS );
+ BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
+ }
+ group_dn.bv_val++;
+ group_dn.bv_len = uri->bv_len - ( group_dn.bv_val - uri->bv_val );
+
+ if ( normalize ) {
+ rc = dnNormalize( 0, NULL, NULL, &group_dn, nbase, op->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ *scope = -1;
+ return rc;
+ }
+ } else {
+ ber_dupbv_x( nbase, &group_dn, op->o_tmpmemctx );
+ rc = LDAP_SUCCESS;
+ }
+ *scope = LDAP_X_SCOPE_GROUP;
+
+ /* FIXME: caller needs to add value of member attribute
+ * and close brackets twice */
+ fstr->bv_len = STRLENOF( "(&(objectClass=)(=" /* )) */ )
+ + group_oc.bv_len + member_at.bv_len;
+ fstr->bv_val = ch_malloc( fstr->bv_len + 1 );
+
+ tmp = lutil_strncopy( fstr->bv_val, "(&(objectClass=" /* )) */ ,
+ STRLENOF( "(&(objectClass=" /* )) */ ) );
+ tmp = lutil_strncopy( tmp, group_oc.bv_val, group_oc.bv_len );
+ tmp = lutil_strncopy( tmp, /* ( */ ")(" /* ) */ ,
+ STRLENOF( /* ( */ ")(" /* ) */ ) );
+ tmp = lutil_strncopy( tmp, member_at.bv_val, member_at.bv_len );
+ tmp = lutil_strncopy( tmp, "=", STRLENOF( "=" ) );
+
+ return rc;
+ }
+
+ /*
+ * ldap:///<base>??<scope>?<filter>
+ * <scope> ::= {base|one|subtree}
+ *
+ * <scope> defaults to "base"
+ * <base> must pass DN normalization
+ * <filter> must pass str2filter()
+ */
+ rc = ldap_url_parse( uri->bv_val, &ludp );
+ switch ( rc ) {
+ case LDAP_URL_SUCCESS:
+ /* FIXME: the check is pedantic, but I think it's necessary,
+ * because people tend to use things like ldaps:// which
+ * gives the idea SSL is being used. Maybe we could
+ * accept ldapi:// as well, but the point is that we use
+ * an URL as an easy means to define bits of a search with
+ * little parsing.
+ */
+ if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
+ /*
+ * must be ldap:///
+ */
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+ break;
+
+ case LDAP_URL_ERR_BADSCHEME:
+ /*
+ * last chance: assume it's a(n exact) DN ...
+ *
+ * NOTE: must pass DN normalization
+ */
+ ldap_free_urldesc( ludp );
+ bv.bv_val = uri->bv_val;
+ *scope = LDAP_X_SCOPE_EXACT;
+ goto is_dn;
+
+ default:
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ if ( ( ludp->lud_host && *ludp->lud_host )
+ || ludp->lud_attrs || ludp->lud_exts )
+ {
+ /* host part must be empty */
+ /* attrs and extensions parts must be empty */
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+
+ /* Grab the scope */
+ *scope = ludp->lud_scope;
+
+ /* Grab the filter */
+ if ( ludp->lud_filter ) {
+ *filter = str2filter_x( op, ludp->lud_filter );
+ if ( *filter == NULL ) {
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+ ber_str2bv( ludp->lud_filter, 0, 0, fstr );
+ }
+
+ /* Grab the searchbase */
+ ber_str2bv( ludp->lud_dn, 0, 0, base );
+ if ( normalize ) {
+ rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
+ } else {
+ ber_dupbv_x( nbase, base, op->o_tmpmemctx );
+ rc = LDAP_SUCCESS;
+ }
+
+done:
+ if( rc != LDAP_SUCCESS ) {
+ if( *filter ) {
+ filter_free_x( op, *filter, 1 );
+ *filter = NULL;
+ }
+ BER_BVZERO( base );
+ BER_BVZERO( fstr );
+ } else {
+ /* Don't free these, return them to caller */
+ ludp->lud_filter = NULL;
+ ludp->lud_dn = NULL;
+ }
+
+ ldap_free_urldesc( ludp );
+ return( rc );
+}
+
+static int slap_sasl_rewrite_config_argv(
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv
+)
+{
+ int rc;
+ char *argv0 = NULL;
+
+ if ( strncasecmp( argv[0], "authid-", STRLENOF( "authid-" ) ) == 0 ) {
+ /* strip "authid-" prefix for parsing */
+ argv0 = argv[0];
+ argv[0] = &argv0[ STRLENOF( "authid-" ) ];
+ }
+
+ /* init at first call */
+ if ( sasl_rwinfo == NULL ) {
+ sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
+ }
+
+ rc = rewrite_parse( sasl_rwinfo, fname, lineno, argc, argv );
+
+ if ( argv0 )
+ argv[0] = argv0;
+
+ return rc;
+}
+
+static int slap_sasl_rewrite_config_bv(
+ const char *fname,
+ int lineno,
+ struct berval bv
+)
+{
+ int rc;
+ ConfigArgs ca = { 0 };
+
+ ca.line = bv.bv_val;
+ ca.argc = 0;
+ config_fp_parse_line( &ca );
+
+ rc = slap_sasl_rewrite_config_argv( fname, lineno, ca.argc, ca.argv );
+
+ ch_free( ca.tline );
+ ch_free( ca.argv );
+
+ return rc;
+}
+
+static void
+slap_sasl_rewrite_bva_add(
+ BerVarray *bva,
+ int idx,
+ int argc,
+ char **argv
+)
+{
+ char *line, *s;
+ struct berval bv;
+
+ if ( argc > 1 ) {
+ /* quote all args but the first */
+ line = ldap_charray2str( argv, "\" \"" );
+ ber_str2bv( line, 0, 0, &bv );
+ s = ber_bvchr( &bv, '"' );
+ assert( s != NULL );
+
+ /* move the trailing quote of argv[0] to the end */
+ AC_MEMCPY( s, s + 1, bv.bv_len - ( s - bv.bv_val ) );
+ bv.bv_val[ bv.bv_len - 1 ] = '"';
+ } else {
+ ber_str2bv( argv[ 0 ], 0, 1, &bv );
+ }
+
+ if ( idx == -1 ) {
+ ber_bvarray_add( bva, &bv );
+ } else {
+ (*bva)[ idx ] = bv;
+ }
+}
+
+static int
+slap_sasl_rewrite_destroy( void )
+{
+ if ( sasl_rwinfo ) {
+ rewrite_info_delete( &sasl_rwinfo );
+ sasl_rwinfo = NULL;
+ }
+
+ return 0;
+}
+
+int slap_sasl_rewrite_config(
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv,
+ int valx
+)
+{
+ int rc, i, last;
+ char *line;
+ struct berval bv;
+ struct rewrite_info *rw = sasl_rwinfo;
+
+ for ( last = 0; authz_rewrites && !BER_BVISNULL( &authz_rewrites[ last ] ); last++ )
+ /* count'em */ ;
+
+ if ( valx == -1 || valx >= last ) {
+ valx = -1;
+ rc = slap_sasl_rewrite_config_argv( fname, lineno, argc, argv );
+ if ( rc == 0 ) {
+ slap_sasl_rewrite_bva_add( &authz_rewrites, valx, argc, argv );
+ }
+ return rc;
+ }
+
+ sasl_rwinfo = NULL;
+
+ for ( i = 0; i < valx; i++ )
+ {
+ rc = slap_sasl_rewrite_config_bv( fname, lineno, authz_rewrites[ i ] );
+ assert( rc == 0 );
+ }
+
+ rc = slap_sasl_rewrite_config_argv( fname, lineno, argc, argv );
+ if ( rc != 0 ) {
+ slap_sasl_rewrite_destroy();
+ sasl_rwinfo = rw;
+ return 1;
+ }
+
+ for ( i = valx; authz_rewrites && !BER_BVISNULL( &authz_rewrites[ i ] ); i++ )
+ {
+ rc = slap_sasl_rewrite_config_bv( fname, lineno, authz_rewrites[ i ] );
+ assert( rc == 0 );
+ }
+
+ authz_rewrites = ch_realloc( authz_rewrites,
+ ( last + 2 )*sizeof( struct berval ) );
+ BER_BVZERO( &authz_rewrites[ last + 1 ] );
+
+ for ( i = last - 1; i >= valx; i-- )
+ {
+ authz_rewrites[ i + 1 ] = authz_rewrites[ i ];
+ }
+
+ slap_sasl_rewrite_bva_add( &authz_rewrites, valx, argc, argv );
+
+ if ( rw )
+ rewrite_info_delete( &rw );
+
+ return rc;
+}
+
+int slap_sasl_rewrite_delete( int valx ) {
+ int rc, i;
+
+ if ( valx == -1 ) {
+ slap_sasl_rewrite_destroy();
+ if ( authz_rewrites ) {
+ ber_bvarray_free( authz_rewrites );
+ authz_rewrites = NULL;
+ }
+ return 0;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &authz_rewrites[ i ] ); i++ )
+ /* count'em */ ;
+
+ if ( valx >= i ) {
+ return 1;
+ }
+
+ ber_memfree( authz_rewrites[ i ].bv_val );
+ for ( i = valx; !BER_BVISNULL( &authz_rewrites[ i + 1 ] ); i++ )
+ {
+ authz_rewrites[ i ] = authz_rewrites[ i + 1 ];
+ }
+ BER_BVZERO( &authz_rewrites[ i ] );
+
+ slap_sasl_rewrite_destroy();
+
+ for ( i = 0; !BER_BVISNULL( &authz_rewrites[ i ] ); i++ )
+ {
+ rc = slap_sasl_rewrite_config_bv( "slapd", 0, authz_rewrites[ i ] );
+ assert( rc == 0 );
+ }
+
+ return rc;
+}
+
+int slap_sasl_rewrite_unparse( BerVarray *bva ) {
+ if ( authz_rewrites ) {
+ return slap_bv_x_ordered_unparse( authz_rewrites, bva );
+ }
+ return 0;
+}
+
+static int
+slap_sasl_regexp_rewrite_config(
+ struct rewrite_info **rwinfo,
+ const char *fname,
+ int lineno,
+ const char *match,
+ const char *replace,
+ const char *context )
+{
+ int rc;
+ char *argvRule[] = { "rewriteRule", NULL, NULL, ":@", NULL };
+ struct rewrite_info *rw = *rwinfo;
+
+ /* init at first call */
+ if ( rw == NULL ) {
+ char *argvEngine[] = { "rewriteEngine", "on", NULL };
+ char *argvContext[] = { "rewriteContext", NULL, NULL };
+
+ /* initialize rewrite engine */
+ rw = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
+
+ /* switch on rewrite engine */
+ rc = rewrite_parse( rw, fname, lineno, 2, argvEngine );
+ if (rc != LDAP_SUCCESS) {
+ goto out;
+ }
+
+ /* create generic authid context */
+ argvContext[1] = AUTHID_CONTEXT;
+ rc = rewrite_parse( rw, fname, lineno, 2, argvContext );
+ if (rc != LDAP_SUCCESS) {
+ goto out;
+ }
+ }
+
+ argvRule[1] = (char *)match;
+ argvRule[2] = (char *)replace;
+ rc = rewrite_parse( rw, fname, lineno, 4, argvRule );
+out:
+ if (rc == LDAP_SUCCESS) {
+ *rwinfo = rw;
+ } else {
+ rewrite_info_delete( &rw );
+ }
+
+ return rc;
+}
+
+int slap_sasl_regexp_config( const char *match, const char *replace, int valx )
+{
+ int i, rc;
+ SaslRegexp_t sr;
+ struct rewrite_info *rw = NULL;
+
+ if ( valx < 0 || valx > nSaslRegexp )
+ valx = nSaslRegexp;
+
+ for ( i = 0; i < valx; i++) {
+ rc = slap_sasl_regexp_rewrite_config( &rw, "sasl-regexp", 0,
+ SaslRegexp[i].sr_match,
+ SaslRegexp[i].sr_replace,
+ AUTHID_CONTEXT);
+ assert( rc == 0 );
+ }
+
+ rc = slap_sasl_regexp_rewrite_config( &rw, "sasl-regexp", 0,
+ match, replace, AUTHID_CONTEXT );
+
+ if ( rc == LDAP_SUCCESS ) {
+ SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
+ (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
+
+ for ( i = nSaslRegexp; i > valx; i-- ) {
+ SaslRegexp[i] = SaslRegexp[i - 1];
+ }
+
+ SaslRegexp[i] = sr;
+ SaslRegexp[i].sr_match = ch_strdup( match );
+ SaslRegexp[i].sr_replace = ch_strdup( replace );
+
+ nSaslRegexp++;
+
+ for ( i = valx + 1; i < nSaslRegexp; i++ ) {
+ rc = slap_sasl_regexp_rewrite_config( &rw, "sasl-regexp", 0,
+ SaslRegexp[i].sr_match,
+ SaslRegexp[i].sr_replace,
+ AUTHID_CONTEXT);
+ assert( rc == 0 );
+ }
+
+ slap_sasl_rewrite_destroy();
+ sasl_rwinfo = rw;
+ } else if ( rw ) {
+ rewrite_info_delete( &rw );
+ }
+
+ return rc;
+}
+
+static void
+slap_sasl_regexp_destroy_one( int n )
+{
+ ch_free( SaslRegexp[ n ].sr_match );
+ ch_free( SaslRegexp[ n ].sr_replace );
+}
+
+void
+slap_sasl_regexp_destroy( void )
+{
+ if ( SaslRegexp ) {
+ int n;
+
+ for ( n = 0; n < nSaslRegexp; n++ ) {
+ slap_sasl_regexp_destroy_one( n );
+ }
+
+ ch_free( SaslRegexp );
+ SaslRegexp = NULL;
+ nSaslRegexp = 0;
+ }
+
+ slap_sasl_rewrite_destroy();
+}
+
+int slap_sasl_regexp_delete( int valx )
+{
+ int rc = 0;
+
+ if ( valx >= nSaslRegexp ) {
+ rc = 1;
+ } else if ( valx < 0 || nSaslRegexp == 1 ) {
+ slap_sasl_regexp_destroy();
+ } else {
+ int i;
+
+ slap_sasl_regexp_destroy_one( valx );
+ nSaslRegexp--;
+
+ for ( i = valx; i < nSaslRegexp; i++ ) {
+ SaslRegexp[ i ] = SaslRegexp[ i + 1 ];
+ }
+
+ slap_sasl_rewrite_destroy();
+ for ( i = 0; i < nSaslRegexp; i++ ) {
+ rc = slap_sasl_regexp_rewrite_config( &sasl_rwinfo, "sasl-regexp", 0,
+ SaslRegexp[ i ].sr_match,
+ SaslRegexp[ i ].sr_replace,
+ AUTHID_CONTEXT );
+ assert( rc == 0 );
+ }
+ }
+
+ return rc;
+}
+
+void slap_sasl_regexp_unparse( BerVarray *out )
+{
+ int i;
+ BerVarray bva = NULL;
+ char ibuf[32], *ptr;
+ struct berval idx;
+
+ if ( !nSaslRegexp ) return;
+
+ idx.bv_val = ibuf;
+ bva = ch_malloc( (nSaslRegexp+1) * sizeof(struct berval) );
+ BER_BVZERO(bva+nSaslRegexp);
+ for ( i=0; i<nSaslRegexp; i++ ) {
+ idx.bv_len = sprintf( idx.bv_val, "{%d}", i);
+ bva[i].bv_len = idx.bv_len + strlen( SaslRegexp[i].sr_match ) +
+ strlen( SaslRegexp[i].sr_replace ) + 5;
+ bva[i].bv_val = ch_malloc( bva[i].bv_len+1 );
+ ptr = lutil_strcopy( bva[i].bv_val, ibuf );
+ *ptr++ = '"';
+ ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_match );
+ ptr = lutil_strcopy( ptr, "\" \"" );
+ ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_replace );
+ *ptr++ = '"';
+ *ptr = '\0';
+ }
+ *out = bva;
+}
+
+/* Take the passed in SASL name and attempt to convert it into an
+ LDAP URI to find the matching LDAP entry, using the pattern matching
+ strings given in the saslregexp config file directive(s) */
+
+static int slap_authz_regexp( struct berval *in, struct berval *out,
+ int flags, void *ctx )
+{
+ const char *context = AUTHID_CONTEXT;
+
+ if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) {
+ return 0;
+ }
+
+ /* FIXME: if aware of authc/authz mapping,
+ * we could use different contexts ... */
+ switch ( rewrite_session( sasl_rwinfo, context, in->bv_val, NULL,
+ &out->bv_val ) )
+ {
+ case REWRITE_REGEXEC_OK:
+ if ( !BER_BVISNULL( out ) ) {
+ char *val = out->bv_val;
+ ber_str2bv_x( val, 0, 1, out, ctx );
+ if ( val != in->bv_val ) {
+ free( val );
+ }
+ } else {
+ ber_dupbv_x( out, in, ctx );
+ }
+ Debug( LDAP_DEBUG_ARGS,
+ "[rw] %s: \"%s\" -> \"%s\"\n",
+ context, in->bv_val, out->bv_val );
+ return 1;
+
+ case REWRITE_REGEXEC_UNWILLING:
+ case REWRITE_REGEXEC_ERR:
+ default:
+ return 0;
+ }
+
+}
+
+/* This callback actually does some work...*/
+static int sasl_sc_sasl2dn( Operation *op, SlapReply *rs )
+{
+ struct berval *ndn = op->o_callback->sc_private;
+
+ if ( rs->sr_type != REP_SEARCH ) return LDAP_SUCCESS;
+
+ /* We only want to be called once */
+ if ( !BER_BVISNULL( ndn ) ) {
+ op->o_tmpfree( ndn->bv_val, op->o_tmpmemctx );
+ BER_BVZERO( ndn );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "%s: slap_sc_sasl2dn: search DN returned more than 1 entry\n",
+ op->o_log_prefix );
+ return LDAP_UNAVAILABLE; /* short-circuit the search */
+ }
+
+ ber_dupbv_x( ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
+ return LDAP_SUCCESS;
+}
+
+
+typedef struct smatch_info {
+ struct berval *dn;
+ int match;
+} smatch_info;
+
+static int sasl_sc_smatch( Operation *o, SlapReply *rs )
+{
+ smatch_info *sm = o->o_callback->sc_private;
+
+ if (rs->sr_type != REP_SEARCH) return 0;
+
+ if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
+ sm->match = 1;
+ return LDAP_UNAVAILABLE; /* short-circuit the search */
+ }
+
+ return 0;
+}
+
+int
+slap_sasl_matches( Operation *op, BerVarray rules,
+ struct berval *assertDN, struct berval *authc )
+{
+ int rc = LDAP_INAPPROPRIATE_AUTH;
+
+ if ( rules != NULL ) {
+ int i;
+
+ for( i = 0; !BER_BVISNULL( &rules[i] ); i++ ) {
+ rc = slap_sasl_match( op, &rules[i], assertDN, authc );
+ if ( rc == LDAP_SUCCESS ) break;
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
+ * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
+ * the rule must be used as an internal search for entries. If that search
+ * returns the *assertDN entry, the match is successful.
+ *
+ * The assertDN should not have the dn: prefix
+ */
+
+static int
+slap_sasl_match( Operation *opx, struct berval *rule,
+ struct berval *assertDN, struct berval *authc )
+{
+ int rc;
+ regex_t reg;
+ smatch_info sm;
+ slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
+ Operation op = {0};
+ SlapReply rs = {REP_RESULT};
+ struct berval base = BER_BVNULL;
+
+ sm.dn = assertDN;
+ sm.match = 0;
+ cb.sc_private = &sm;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "===>slap_sasl_match: comparing DN %s to rule %s\n",
+ assertDN->bv_len ? assertDN->bv_val : "(null)", rule->bv_val );
+
+ /* NOTE: don't normalize rule if authz syntax is enabled */
+ rc = slap_parseURI( opx, rule, &base, &op.o_req_ndn,
+ &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 0 );
+
+ if( rc != LDAP_SUCCESS ) goto CONCLUDED;
+
+ switch ( op.ors_scope ) {
+ case LDAP_X_SCOPE_EXACT:
+exact_match:
+ if ( dn_match( &op.o_req_ndn, assertDN ) ) {
+ rc = LDAP_SUCCESS;
+ } else {
+ rc = LDAP_INAPPROPRIATE_AUTH;
+ }
+ goto CONCLUDED;
+
+ case LDAP_X_SCOPE_CHILDREN:
+ case LDAP_X_SCOPE_SUBTREE:
+ case LDAP_X_SCOPE_ONELEVEL:
+ {
+ int d = assertDN->bv_len - op.o_req_ndn.bv_len;
+
+ rc = LDAP_INAPPROPRIATE_AUTH;
+
+ if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) {
+ goto exact_match;
+
+ } else if ( d > 0 ) {
+ struct berval bv;
+
+ /* leave room for at least one char of attributeType,
+ * one for '=' and one for ',' */
+ if ( d < (int) STRLENOF( "x=,") ) {
+ goto CONCLUDED;
+ }
+
+ bv.bv_len = op.o_req_ndn.bv_len;
+ bv.bv_val = assertDN->bv_val + d;
+
+ if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
+ switch ( op.ors_scope ) {
+ case LDAP_X_SCOPE_SUBTREE:
+ case LDAP_X_SCOPE_CHILDREN:
+ rc = LDAP_SUCCESS;
+ break;
+
+ case LDAP_X_SCOPE_ONELEVEL:
+ {
+ struct berval pdn;
+
+ dnParent( assertDN, &pdn );
+ /* the common portion of the DN
+ * already matches, so only check
+ * if parent DN of assertedDN
+ * is all the pattern */
+ if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
+ rc = LDAP_SUCCESS;
+ }
+ break;
+ }
+ default:
+ /* at present, impossible */
+ assert( 0 );
+ }
+ }
+ }
+ goto CONCLUDED;
+ }
+
+ case LDAP_X_SCOPE_REGEX:
+ rc = regcomp(&reg, op.o_req_ndn.bv_val,
+ REG_EXTENDED|REG_ICASE|REG_NOSUB);
+ if ( rc == 0 ) {
+ rc = regexec(&reg, assertDN->bv_val, 0, NULL, 0);
+ regfree( &reg );
+ }
+ if ( rc == 0 ) {
+ rc = LDAP_SUCCESS;
+ } else {
+ rc = LDAP_INAPPROPRIATE_AUTH;
+ }
+ goto CONCLUDED;
+
+ case LDAP_X_SCOPE_GROUP: {
+ char *tmp;
+
+ /* Now filterstr looks like "(&(objectClass=<group_oc>)(<member_at>="
+ * we need to append the <assertDN> so that the <group_dn> is searched
+ * with scope "base", and the filter ensures that <assertDN> is
+ * member of the group */
+ tmp = ch_realloc( op.ors_filterstr.bv_val, op.ors_filterstr.bv_len +
+ assertDN->bv_len + STRLENOF( /*"(("*/ "))" ) + 1 );
+ if ( tmp == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto CONCLUDED;
+ }
+ op.ors_filterstr.bv_val = tmp;
+
+ tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val );
+ tmp = lutil_strcopy( tmp, /*"(("*/ "))" );
+
+ /* pass opx because str2filter_x may (and does) use o_tmpmfuncs */
+ op.ors_filter = str2filter_x( opx, op.ors_filterstr.bv_val );
+ if ( op.ors_filter == NULL ) {
+ rc = LDAP_PROTOCOL_ERROR;
+ goto CONCLUDED;
+ }
+ op.ors_scope = LDAP_SCOPE_BASE;
+
+ /* hijack match DN: use that of the group instead of the assertDN;
+ * assertDN is now in the filter */
+ sm.dn = &op.o_req_ndn;
+
+ /* do the search */
+ break;
+ }
+
+ case LDAP_X_SCOPE_USERS:
+ if ( !BER_BVISEMPTY( assertDN ) ) {
+ rc = LDAP_SUCCESS;
+ } else {
+ rc = LDAP_INAPPROPRIATE_AUTH;
+ }
+ goto CONCLUDED;
+
+ default:
+ break;
+ }
+
+ /* Must run an internal search. */
+ if ( op.ors_filter == NULL ) {
+ rc = LDAP_FILTER_ERROR;
+ goto CONCLUDED;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
+ op.o_req_ndn.bv_val, op.ors_scope );
+
+ op.o_bd = select_backend( &op.o_req_ndn, 1 );
+ if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
+ rc = LDAP_INAPPROPRIATE_AUTH;
+ goto CONCLUDED;
+ }
+
+ op.o_hdr = opx->o_hdr;
+ op.o_tag = LDAP_REQ_SEARCH;
+ op.o_ndn = *authc;
+ op.o_callback = &cb;
+ slap_op_time( &op.o_time, &op.o_tincr );
+ op.o_do_not_cache = 1;
+ op.o_is_auth_check = 1;
+ /* use req_ndn as req_dn instead of non-pretty base of uri */
+ if( !BER_BVISNULL( &base ) ) {
+ ch_free( base.bv_val );
+ /* just in case... */
+ BER_BVZERO( &base );
+ }
+ ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
+ op.ors_deref = LDAP_DEREF_NEVER;
+ op.ors_slimit = 1;
+ op.ors_tlimit = SLAP_NO_LIMIT;
+ op.ors_attrs = slap_anlist_no_attrs;
+ op.ors_attrsonly = 1;
+
+ op.o_bd->be_search( &op, &rs );
+
+ if (sm.match) {
+ rc = LDAP_SUCCESS;
+ } else {
+ rc = LDAP_INAPPROPRIATE_AUTH;
+ }
+
+CONCLUDED:
+ if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
+ if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
+ if( op.ors_filter ) filter_free_x( opx, op.ors_filter, 1 );
+ if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<===slap_sasl_match: comparison returned %d\n", rc );
+
+ return( rc );
+}
+
+
+/*
+ * This function answers the question, "Can this ID authorize to that ID?",
+ * based on authorization rules. The rules are stored in the *searchDN, in the
+ * attribute named by *attr. If any of those rules map to the *assertDN, the
+ * authorization is approved.
+ *
+ * The DNs should not have the dn: prefix
+ */
+static int
+slap_sasl_check_authz( Operation *op,
+ struct berval *searchDN,
+ struct berval *assertDN,
+ AttributeDescription *ad,
+ struct berval *authc )
+{
+ int rc,
+ do_not_cache = op->o_do_not_cache;
+ BerVarray vals = NULL;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
+ assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
+
+ /* ITS#4760: don't cache group access */
+ op->o_do_not_cache = 1;
+ rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH );
+ op->o_do_not_cache = do_not_cache;
+ if( rc != LDAP_SUCCESS ) goto COMPLETE;
+
+ /* Check if the *assertDN matches any *vals */
+ rc = slap_sasl_matches( op, vals, assertDN, authc );
+
+COMPLETE:
+ if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<==slap_sasl_check_authz: %s check returning %d\n",
+ ad->ad_cname.bv_val, rc );
+
+ return( rc );
+}
+
+/*
+ * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
+ * return the LDAP DN to which it matches. The SASL regexp rules in the config
+ * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
+ * search with scope=base), just return the URI (or its searchbase). Otherwise
+ * an internal search must be done, and if that search returns exactly one
+ * entry, return the DN of that one entry.
+ */
+void
+slap_sasl2dn(
+ Operation *opx,
+ struct berval *saslname,
+ struct berval *sasldn,
+ int flags )
+{
+ int rc;
+ slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
+ Operation op = {0};
+ SlapReply rs = {REP_RESULT};
+ struct berval regout = BER_BVNULL;
+ struct berval base = BER_BVNULL;
+
+ Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
+ "converting SASL name %s to a DN\n",
+ saslname->bv_val );
+
+ BER_BVZERO( sasldn );
+ cb.sc_private = sasldn;
+
+ /* Convert the SASL name into a minimal URI */
+ if( !slap_authz_regexp( saslname, &regout, flags, opx->o_tmpmemctx ) ) {
+ goto FINISHED;
+ }
+
+ /* NOTE: always normalize regout because it results
+ * from string submatch expansion */
+ rc = slap_parseURI( opx, &regout, &base, &op.o_req_ndn,
+ &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 1 );
+ if ( !BER_BVISNULL( &regout ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx );
+ if ( rc != LDAP_SUCCESS ) {
+ goto FINISHED;
+ }
+
+ /* Must do an internal search */
+ op.o_bd = select_backend( &op.o_req_ndn, 1 );
+
+ switch ( op.ors_scope ) {
+ case LDAP_X_SCOPE_EXACT:
+ *sasldn = op.o_req_ndn;
+ BER_BVZERO( &op.o_req_ndn );
+ /* intentionally continue to next case */
+
+ case LDAP_X_SCOPE_REGEX:
+ case LDAP_X_SCOPE_SUBTREE:
+ case LDAP_X_SCOPE_CHILDREN:
+ case LDAP_X_SCOPE_ONELEVEL:
+ case LDAP_X_SCOPE_GROUP:
+ case LDAP_X_SCOPE_USERS:
+ /* correctly parsed, but illegal */
+ goto FINISHED;
+
+ case LDAP_SCOPE_BASE:
+ case LDAP_SCOPE_ONELEVEL:
+ case LDAP_SCOPE_SUBTREE:
+ case LDAP_SCOPE_SUBORDINATE:
+ /* do a search */
+ break;
+
+ default:
+ /* catch unhandled cases (there shouldn't be) */
+ assert( 0 );
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
+ op.o_req_ndn.bv_val, op.ors_scope );
+
+ if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) {
+ goto FINISHED;
+ }
+
+ /* Must run an internal search. */
+ if ( op.ors_filter == NULL ) {
+ rc = LDAP_FILTER_ERROR;
+ goto FINISHED;
+ }
+
+ op.o_hdr = opx->o_hdr;
+ op.o_tag = LDAP_REQ_SEARCH;
+ op.o_ndn = opx->o_conn->c_ndn;
+ op.o_callback = &cb;
+ slap_op_time( &op.o_time, &op.o_tincr );
+ op.o_do_not_cache = 1;
+ op.o_is_auth_check = 1;
+ op.ors_deref = LDAP_DEREF_NEVER;
+ op.ors_slimit = 1;
+ op.ors_tlimit = SLAP_NO_LIMIT;
+ op.ors_attrs = slap_anlist_no_attrs;
+ op.ors_attrsonly = 1;
+ /* use req_ndn as req_dn instead of non-pretty base of uri */
+ if( !BER_BVISNULL( &base ) ) {
+ ch_free( base.bv_val );
+ /* just in case... */
+ BER_BVZERO( &base );
+ }
+ ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
+
+ op.o_bd->be_search( &op, &rs );
+
+FINISHED:
+ if( opx == opx->o_conn->c_sasl_bindop && !BER_BVISEMPTY( sasldn ) ) {
+ opx->o_conn->c_authz_backend = op.o_bd;
+ }
+ if( !BER_BVISNULL( &op.o_req_dn ) ) {
+ slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
+ }
+ if( !BER_BVISNULL( &op.o_req_ndn ) ) {
+ slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
+ }
+ if( op.ors_filter ) {
+ filter_free_x( opx, op.ors_filter, 1 );
+ }
+ if( !BER_BVISNULL( &op.ors_filterstr ) ) {
+ ch_free( op.ors_filterstr.bv_val );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
+ !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>" );
+
+ return;
+}
+
+
+/* Check if a bind can SASL authorize to another identity.
+ * The DNs should not have the dn: prefix
+ */
+
+int slap_sasl_authorized( Operation *op,
+ struct berval *authcDN, struct berval *authzDN )
+{
+ int rc = LDAP_INAPPROPRIATE_AUTH;
+
+ /* User binding as anonymous */
+ if ( !authzDN || !authzDN->bv_len || !authzDN->bv_val ) {
+ rc = LDAP_SUCCESS;
+ goto DONE;
+ }
+
+ /* User is anonymous */
+ if ( !authcDN || !authcDN->bv_len || !authcDN->bv_val ) {
+ goto DONE;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "==>slap_sasl_authorized: can %s become %s?\n",
+ authcDN->bv_len ? authcDN->bv_val : "(null)",
+ authzDN->bv_len ? authzDN->bv_val : "(null)" );
+
+ /* If person is authorizing to self, succeed */
+ if ( dn_match( authcDN, authzDN ) ) {
+ rc = LDAP_SUCCESS;
+ goto DONE;
+ }
+
+ /* Allow the manager to authorize as any DN in its own DBs. */
+ {
+ Backend *zbe = select_backend( authzDN, 1 );
+ if ( zbe && be_isroot_dn( zbe, authcDN )) {
+ rc = LDAP_SUCCESS;
+ goto DONE;
+ }
+ }
+
+ /* Check source rules */
+ if( authz_policy & SASL_AUTHZ_TO ) {
+ rc = slap_sasl_check_authz( op, authcDN, authzDN,
+ slap_schema.si_ad_saslAuthzTo, authcDN );
+ if(( rc == LDAP_SUCCESS ) ^ (( authz_policy & SASL_AUTHZ_AND) != 0)) {
+ if( rc != LDAP_SUCCESS )
+ rc = LDAP_INAPPROPRIATE_AUTH;
+ goto DONE;
+ }
+ }
+
+ /* Check destination rules */
+ if( authz_policy & SASL_AUTHZ_FROM ) {
+ rc = slap_sasl_check_authz( op, authzDN, authcDN,
+ slap_schema.si_ad_saslAuthzFrom, authcDN );
+ if( rc == LDAP_SUCCESS ) {
+ goto DONE;
+ }
+ }
+
+ rc = LDAP_INAPPROPRIATE_AUTH;
+
+DONE:
+
+ Debug( LDAP_DEBUG_TRACE,
+ "<== slap_sasl_authorized: return %d\n", rc );
+
+ return( rc );
+}
diff --git a/servers/slapd/schema.c b/servers/slapd/schema.c
new file mode 100644
index 0000000..a98f0c6
--- /dev/null
+++ b/servers/slapd/schema.c
@@ -0,0 +1,167 @@
+/* schema.c - routines to manage schema definitions */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "lutil.h"
+
+
+int
+schema_info( Entry **entry, const char **text )
+{
+ AttributeDescription *ad_structuralObjectClass
+ = slap_schema.si_ad_structuralObjectClass;
+ AttributeDescription *ad_objectClass
+ = slap_schema.si_ad_objectClass;
+ AttributeDescription *ad_createTimestamp
+ = slap_schema.si_ad_createTimestamp;
+ AttributeDescription *ad_modifyTimestamp
+ = slap_schema.si_ad_modifyTimestamp;
+
+ Entry *e;
+ struct berval vals[5];
+ struct berval nvals[5];
+
+ e = entry_alloc();
+ if( e == NULL ) {
+ /* Out of memory, do something about it */
+ Debug( LDAP_DEBUG_ANY,
+ "schema_info: entry_alloc failed - out of memory.\n" );
+ *text = "out of memory";
+ return LDAP_OTHER;
+ }
+
+ e->e_attrs = NULL;
+ /* backend-specific schema info should be created by the
+ * backend itself
+ */
+ ber_dupbv( &e->e_name, &frontendDB->be_schemadn );
+ ber_dupbv( &e->e_nname, &frontendDB->be_schemandn );
+ e->e_private = NULL;
+
+ BER_BVSTR( &vals[0], "subentry" );
+ if( attr_merge_one( e, ad_structuralObjectClass, vals, NULL ) ) {
+ /* Out of memory, do something about it */
+ entry_free( e );
+ *text = "out of memory";
+ return LDAP_OTHER;
+ }
+
+ BER_BVSTR( &vals[0], "top" );
+ BER_BVSTR( &vals[1], "subentry" );
+ BER_BVSTR( &vals[2], "subschema" );
+ BER_BVSTR( &vals[3], "extensibleObject" );
+ BER_BVZERO( &vals[4] );
+ if ( attr_merge( e, ad_objectClass, vals, NULL ) ) {
+ /* Out of memory, do something about it */
+ entry_free( e );
+ *text = "out of memory";
+ return LDAP_OTHER;
+ }
+
+ {
+ int rc;
+ AttributeDescription *desc = NULL;
+ struct berval rdn = frontendDB->be_schemadn;
+ vals[0].bv_val = ber_bvchr( &rdn, '=' );
+
+ if( vals[0].bv_val == NULL ) {
+ *text = "improperly configured subschema subentry";
+ return LDAP_OTHER;
+ }
+
+ vals[0].bv_val++;
+ vals[0].bv_len = rdn.bv_len - (vals[0].bv_val - rdn.bv_val);
+ rdn.bv_len -= vals[0].bv_len + 1;
+
+ rc = slap_bv2ad( &rdn, &desc, text );
+
+ if( rc != LDAP_SUCCESS ) {
+ entry_free( e );
+ *text = "improperly configured subschema subentry";
+ return LDAP_OTHER;
+ }
+
+ nvals[0].bv_val = ber_bvchr( &frontendDB->be_schemandn, '=' );
+ assert( nvals[0].bv_val != NULL );
+ nvals[0].bv_val++;
+ nvals[0].bv_len = frontendDB->be_schemandn.bv_len -
+ (nvals[0].bv_val - frontendDB->be_schemandn.bv_val);
+
+ if ( attr_merge_one( e, desc, vals, nvals ) ) {
+ /* Out of memory, do something about it */
+ entry_free( e );
+ *text = "out of memory";
+ return LDAP_OTHER;
+ }
+ }
+
+ {
+ char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+
+ /*
+ * According to RFC 4512:
+
+ Servers SHOULD maintain the 'creatorsName', 'createTimestamp',
+ 'modifiersName', and 'modifyTimestamp' attributes for all entries of
+ the DIT.
+
+ * to be conservative, we declare schema created
+ * AND modified at server startup time ...
+ */
+
+ vals[0].bv_val = timebuf;
+ vals[0].bv_len = sizeof( timebuf );
+
+ slap_timestamp( &starttime, vals );
+
+ if( attr_merge_one( e, ad_createTimestamp, vals, NULL ) ) {
+ /* Out of memory, do something about it */
+ entry_free( e );
+ *text = "out of memory";
+ return LDAP_OTHER;
+ }
+ if( attr_merge_one( e, ad_modifyTimestamp, vals, NULL ) ) {
+ /* Out of memory, do something about it */
+ entry_free( e );
+ *text = "out of memory";
+ return LDAP_OTHER;
+ }
+ }
+
+ if ( syn_schema_info( e )
+ || mr_schema_info( e )
+ || mru_schema_info( e )
+ || at_schema_info( e )
+ || oc_schema_info( e )
+ || cr_schema_info( e ) )
+ {
+ /* Out of memory, do something about it */
+ entry_free( e );
+ *text = "out of memory";
+ return LDAP_OTHER;
+ }
+
+ *entry = e;
+ return LDAP_SUCCESS;
+}
diff --git a/servers/slapd/schema/README b/servers/slapd/schema/README
new file mode 100644
index 0000000..e2db4b8
--- /dev/null
+++ b/servers/slapd/schema/README
@@ -0,0 +1,78 @@
+This directory contains user application schema definitions for use
+with slapd(8).
+
+File Description
+---- -----------
+collective.schema Collective attributes (experimental)
+corba.schema Corba Object
+core.schema OpenLDAP "core"
+cosine.schema COSINE Pilot
+duaconf.schema Client Configuration (work in progress)
+dyngroup.schema Dynamic Group (experimental)
+inetorgperson.schema InetOrgPerson
+java.schema Java Object
+misc.schema Miscellaneous Schema (experimental)
+nis.schema Network Information Service (experimental)
+openldap.schema OpenLDAP Project (FYI)
+
+Additional "generally useful" schema definitions can be submitted
+using the OpenLDAP Issue Tracking System <http://www.openldap.org/its/>.
+Submissions should include a stable reference to a mature, open
+technical specification (e.g., an RFC) for the schema.
+
+The core.ldif and openldap.ldif files are equivalent to their
+corresponding .schema files. They have been provided as examples
+for use with the dynamic configuration backend. These example files
+are not actually necessary since slapd will automatically convert any
+included *.schema files into LDIF when converting a slapd.conf file
+to a configuration database, but they serve as a model of how to
+convert schema files in general.
+
+---
+
+This notice applies to all files in this directory.
+
+Copyright 1998-2022 The OpenLDAP Foundation, Redwood City, California, USA
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted only as authorized by the OpenLDAP
+Public License. A copy of this license is available at
+http://www.OpenLDAP.org/license.html or in file LICENSE in the
+top-level directory of the distribution.
+
+---
+
+This notice applies to all schema in this directory which are derived
+from RFCs and other IETF documents.
+
+Portions Copyright 1991-2004, The Internet Society. All Rights Reserved.
+
+This document and translations of it may be copied and furnished
+to others, and derivative works that comment on or otherwise explain
+it or assist in its implementation may be prepared, copied, published
+and distributed, in whole or in part, without restriction of any
+kind, provided that the above copyright notice and this paragraph
+are included on all such copies and derivative works. However,
+this document itself may not be modified in any way, such as by
+removing the copyright notice or references to the Internet Society
+or other Internet organizations, except as needed for the purpose
+of developing Internet standards in which case the procedures for
+copyrights defined in the Internet Standards process must be
+followed, or as required to translate it into languages other than
+English.
+
+The limited permissions granted above are perpetual and will not
+be revoked by the Internet Society or its successors or assigns.
+
+This document and the information contained herein is provided on
+an "AS IS" basis and THE AUTHORS, THE INTERNET SOCIETY, AND THE
+INTERNET ENGINEERING TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE
+OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY
+IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR
+PURPOSE.
+
+
+---
+$OpenLDAP$
diff --git a/servers/slapd/schema/collective.ldif b/servers/slapd/schema/collective.ldif
new file mode 100644
index 0000000..fa62326
--- /dev/null
+++ b/servers/slapd/schema/collective.ldif
@@ -0,0 +1,48 @@
+# collective.ldif -- Collective attribute schema
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+#
+## Portions Copyright (C) The Internet Society (2003).
+## Please see full copyright statement below.
+#
+# From RFC 3671 [portions trimmed]:
+# Collective Attributes in LDAP
+#
+# This file was automatically generated from collective.schema; see that file
+# for complete references.
+#
+dn: cn=collective,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: collective
+olcAttributeTypes: {0}( 2.5.4.7.1 NAME 'c-l' SUP l COLLECTIVE )
+olcAttributeTypes: {1}( 2.5.4.8.1 NAME 'c-st' SUP st COLLECTIVE )
+olcAttributeTypes: {2}( 2.5.4.9.1 NAME 'c-street' SUP street COLLECTIVE )
+olcAttributeTypes: {3}( 2.5.4.10.1 NAME 'c-o' SUP o COLLECTIVE )
+olcAttributeTypes: {4}( 2.5.4.11.1 NAME 'c-ou' SUP ou COLLECTIVE )
+olcAttributeTypes: {5}( 2.5.4.16.1 NAME 'c-PostalAddress' SUP postalAddress CO
+ LLECTIVE )
+olcAttributeTypes: {6}( 2.5.4.17.1 NAME 'c-PostalCode' SUP postalCode COLLECTI
+ VE )
+olcAttributeTypes: {7}( 2.5.4.18.1 NAME 'c-PostOfficeBox' SUP postOfficeBox CO
+ LLECTIVE )
+olcAttributeTypes: {8}( 2.5.4.19.1 NAME 'c-PhysicalDeliveryOfficeName' SUP phy
+ sicalDeliveryOfficeName COLLECTIVE )
+olcAttributeTypes: {9}( 2.5.4.20.1 NAME 'c-TelephoneNumber' SUP telephoneNumbe
+ r COLLECTIVE )
+olcAttributeTypes: {10}( 2.5.4.21.1 NAME 'c-TelexNumber' SUP telexNumber COLLE
+ CTIVE )
+olcAttributeTypes: {11}( 2.5.4.23.1 NAME 'c-FacsimileTelephoneNumber' SUP facs
+ imileTelephoneNumber COLLECTIVE )
+olcAttributeTypes: {12}( 2.5.4.25.1 NAME 'c-InternationalISDNNumber' SUP inter
+ nationalISDNNumber COLLECTIVE )
diff --git a/servers/slapd/schema/corba.ldif b/servers/slapd/schema/corba.ldif
new file mode 100644
index 0000000..2933932
--- /dev/null
+++ b/servers/slapd/schema/corba.ldif
@@ -0,0 +1,42 @@
+# corba.ldif -- Corba Object Schema
+# depends upon core.ldif
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+#
+## Portions Copyright (C) The Internet Society (1999).
+## Please see full copyright statement below.
+#
+# From RFC 2714 [portions trimmed]:
+# Schema for Representing CORBA Object References in an LDAP Directory
+#
+# This file was automatically generated from corba.schema; see that file
+# for complete references.
+#
+dn: cn=corba,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: corba
+olcAttributeTypes: {0}( 1.3.6.1.4.1.42.2.27.4.1.14 NAME 'corbaIor' DESC 'Strin
+ gified interoperable object reference of a CORBA object' EQUALITY caseIgnoreI
+ A5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {1}( 1.3.6.1.4.1.42.2.27.4.1.15 NAME 'corbaRepositoryId' DE
+ SC 'Repository ids of interfaces implemented by a CORBA object' EQUALITY case
+ ExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+olcObjectClasses: {0}( 1.3.6.1.4.1.42.2.27.4.2.10 NAME 'corbaContainer' DESC '
+ Container for a CORBA object' SUP top STRUCTURAL MUST cn )
+olcObjectClasses: {1}( 1.3.6.1.4.1.42.2.27.4.2.9 NAME 'corbaObject' DESC 'CORB
+ A object representation' SUP top ABSTRACT MAY ( corbaRepositoryId $ descripti
+ on ) )
+olcObjectClasses: {2}( 1.3.6.1.4.1.42.2.27.4.2.11 NAME 'corbaObjectReference'
+ DESC 'CORBA interoperable object reference' SUP corbaObject AUXILIARY MUST co
+ rbaIor )
diff --git a/servers/slapd/schema/cosine.ldif b/servers/slapd/schema/cosine.ldif
new file mode 100644
index 0000000..311d9ce
--- /dev/null
+++ b/servers/slapd/schema/cosine.ldif
@@ -0,0 +1,200 @@
+# RFC1274: Cosine and Internet X.500 schema
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+#
+# RFC1274: Cosine and Internet X.500 schema
+#
+# This file contains LDAPv3 schema derived from X.500 COSINE "pilot"
+# schema. As this schema was defined for X.500(89), some
+# oddities were introduced in the mapping to LDAPv3. The
+# mappings were based upon: draft-ietf-asid-ldapv3-attributes-03.txt
+# (a work in progress)
+#
+# Note: It seems that the pilot schema evolved beyond what was
+# described in RFC1274. However, this document attempts to describes
+# RFC1274 as published.
+#
+# Depends on core.ldif
+#
+# This file was automatically generated from cosine.schema; see that
+# file for complete background.
+#
+dn: cn=cosine,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: cosine
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.2 NAME 'textEncodedORAddress'
+ EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.
+ 1466.115.121.1.15{256} )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.4 NAME 'info' DESC 'RFC1274: g
+ eneral information' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{2048} )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.5 NAME ( 'drink' 'favouriteDri
+ nk' ) DESC 'RFC1274: favorite drink' EQUALITY caseIgnoreMatch SUBSTR caseIgno
+ reSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.6 NAME 'roomNumber' DESC 'RFC1
+ 274: room number' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch S
+ YNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.7 NAME 'photo' DESC 'RFC1274:
+ photo (G3 fax)' SYNTAX 1.3.6.1.4.1.1466.115.121.1.23{25000} )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.8 NAME 'userClass' DESC 'RFC12
+ 74: category of user' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMat
+ ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.9 NAME 'host' DESC 'RFC1274: h
+ ost computer' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTA
+ X 1.3.6.1.4.1.1466.115.121.1.15{256} )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.10 NAME 'manager' DESC 'RFC127
+ 4: DN of manager' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115
+ .121.1.12 )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.11 NAME 'documentIdentifier' D
+ ESC 'RFC1274: unique identifier of document' EQUALITY caseIgnoreMatch SUBSTR
+ caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.12 NAME 'documentTitle' DESC '
+ RFC1274: title of document' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstri
+ ngsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.13 NAME 'documentVersion' DES
+ C 'RFC1274: version of document' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSu
+ bstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.14 NAME 'documentAuthor' DESC
+ 'RFC1274: DN of author of document' EQUALITY distinguishedNameMatch SYNTAX 1
+ .3.6.1.4.1.1466.115.121.1.12 )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.15 NAME 'documentLocation' DE
+ SC 'RFC1274: location of document original' EQUALITY caseIgnoreMatch SUBSTR c
+ aseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.20 NAME ( 'homePhone' 'homeTe
+ lephoneNumber' ) DESC 'RFC1274: home telephone number' EQUALITY telephoneNumb
+ erMatch SUBSTR telephoneNumberSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121
+ .1.50 )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.21 NAME 'secretary' DESC 'RFC
+ 1274: DN of secretary' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.146
+ 6.115.121.1.12 )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.22 NAME 'otherMailbox' SYNTAX
+ 1.3.6.1.4.1.1466.115.121.1.39 )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.26 NAME 'aRecord' EQUALITY ca
+ seIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.27 NAME 'mDRecord' EQUALITY c
+ aseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.28 NAME 'mXRecord' EQUALITY c
+ aseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.29 NAME 'nSRecord' EQUALITY c
+ aseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.30 NAME 'sOARecord' EQUALITY
+ caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.31 NAME 'cNAMERecord' EQUALIT
+ Y caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.38 NAME 'associatedName' DESC
+ 'RFC1274: DN of entry associated with domain' EQUALITY distinguishedNameMatc
+ h SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.39 NAME 'homePostalAddress' D
+ ESC 'RFC1274: home postal address' EQUALITY caseIgnoreListMatch SUBSTR caseIg
+ noreListSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.40 NAME 'personalTitle' DESC
+ 'RFC1274: personal title' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstring
+ sMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.41 NAME ( 'mobile' 'mobileTel
+ ephoneNumber' ) DESC 'RFC1274: mobile telephone number' EQUALITY telephoneNum
+ berMatch SUBSTR telephoneNumberSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.12
+ 1.1.50 )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.42 NAME ( 'pager' 'pagerTelep
+ honeNumber' ) DESC 'RFC1274: pager telephone number' EQUALITY telephoneNumber
+ Match SUBSTR telephoneNumberSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1
+ .50 )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.43 NAME ( 'co' 'friendlyCount
+ ryName' ) DESC 'RFC1274: friendly country name' EQUALITY caseIgnoreMatch SUBS
+ TR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.44 NAME 'uniqueIdentifier' DE
+ SC 'RFC1274: unique identifer' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.14
+ 66.115.121.1.15{256} )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.45 NAME 'organizationalStatus
+ ' DESC 'RFC1274: organizational status' EQUALITY caseIgnoreMatch SUBSTR caseI
+ gnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.46 NAME 'janetMailbox' DESC '
+ RFC1274: Janet mailbox' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5Subst
+ ringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.47 NAME 'mailPreferenceOption
+ ' DESC 'RFC1274: mail preference option' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.48 NAME 'buildingName' DESC '
+ RFC1274: name of building' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstrin
+ gsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.49 NAME 'dSAQuality' DESC 'RF
+ C1274: DSA Quality' SYNTAX 1.3.6.1.4.1.1466.115.121.1.19 SINGLE-VALUE )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.50 NAME 'singleLevelQuality'
+ DESC 'RFC1274: Single Level Quality' SYNTAX 1.3.6.1.4.1.1466.115.121.1.13 SIN
+ GLE-VALUE )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.51 NAME 'subtreeMinimumQualit
+ y' DESC 'RFC1274: Subtree Minimum Quality' SYNTAX 1.3.6.1.4.1.1466.115.121.1.
+ 13 SINGLE-VALUE )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.52 NAME 'subtreeMaximumQualit
+ y' DESC 'RFC1274: Subtree Maximum Quality' SYNTAX 1.3.6.1.4.1.1466.115.121.1.
+ 13 SINGLE-VALUE )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.53 NAME 'personalSignature' D
+ ESC 'RFC1274: Personal Signature (G3 fax)' SYNTAX 1.3.6.1.4.1.1466.115.121.1.
+ 23 )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.54 NAME 'dITRedirect' DESC 'R
+ FC1274: DIT Redirect' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466
+ .115.121.1.12 )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.55 NAME 'audio' DESC 'RFC1274
+ : audio (u-law)' SYNTAX 1.3.6.1.4.1.1466.115.121.1.4{25000} )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.56 NAME 'documentPublisher' D
+ ESC 'RFC1274: publisher of document' EQUALITY caseIgnoreMatch SUBSTR caseIgno
+ reSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+olcObjectClasses: ( 0.9.2342.19200300.100.4.4 NAME ( 'pilotPerson' 'newPilo
+ tPerson' ) SUP person STRUCTURAL MAY ( userid $ textEncodedORAddress $ rfc822
+ Mailbox $ favouriteDrink $ roomNumber $ userClass $ homeTelephoneNumber $ hom
+ ePostalAddress $ secretary $ personalTitle $ preferredDeliveryMethod $ busine
+ ssCategory $ janetMailbox $ otherMailbox $ mobileTelephoneNumber $ pagerTelep
+ honeNumber $ organizationalStatus $ mailPreferenceOption $ personalSignature
+ ) )
+olcObjectClasses: ( 0.9.2342.19200300.100.4.5 NAME 'account' SUP top STRUCT
+ URAL MUST userid MAY ( description $ seeAlso $ localityName $ organizationNam
+ e $ organizationalUnitName $ host ) )
+olcObjectClasses: ( 0.9.2342.19200300.100.4.6 NAME 'document' SUP top STRUC
+ TURAL MUST documentIdentifier MAY ( commonName $ description $ seeAlso $ loca
+ lityName $ organizationName $ organizationalUnitName $ documentTitle $ docume
+ ntVersion $ documentAuthor $ documentLocation $ documentPublisher ) )
+olcObjectClasses: ( 0.9.2342.19200300.100.4.7 NAME 'room' SUP top STRUCTURA
+ L MUST commonName MAY ( roomNumber $ description $ seeAlso $ telephoneNumber
+ ) )
+olcObjectClasses: ( 0.9.2342.19200300.100.4.9 NAME 'documentSeries' SUP top
+ STRUCTURAL MUST commonName MAY ( description $ seeAlso $ telephonenumber $ l
+ ocalityName $ organizationName $ organizationalUnitName ) )
+olcObjectClasses: ( 0.9.2342.19200300.100.4.13 NAME 'domain' SUP top STRUCT
+ URAL MUST domainComponent MAY ( associatedName $ organizationName $ descripti
+ on $ businessCategory $ seeAlso $ searchGuide $ userPassword $ localityName $
+ stateOrProvinceName $ streetAddress $ physicalDeliveryOfficeName $ postalAdd
+ ress $ postalCode $ postOfficeBox $ streetAddress $ facsimileTelephoneNumber
+ $ internationalISDNNumber $ telephoneNumber $ teletexTerminalIdentifier $ tel
+ exNumber $ preferredDeliveryMethod $ destinationIndicator $ registeredAddress
+ $ x121Address ) )
+olcObjectClasses: ( 0.9.2342.19200300.100.4.14 NAME 'RFC822localPart' SUP d
+ omain STRUCTURAL MAY ( commonName $ surname $ description $ seeAlso $ telepho
+ neNumber $ physicalDeliveryOfficeName $ postalAddress $ postalCode $ postOffi
+ ceBox $ streetAddress $ facsimileTelephoneNumber $ internationalISDNNumber $
+ telephoneNumber $ teletexTerminalIdentifier $ telexNumber $ preferredDelivery
+ Method $ destinationIndicator $ registeredAddress $ x121Address ) )
+olcObjectClasses: ( 0.9.2342.19200300.100.4.15 NAME 'dNSDomain' SUP domain
+ STRUCTURAL MAY ( ARecord $ MDRecord $ MXRecord $ NSRecord $ SOARecord $ CNAME
+ Record ) )
+olcObjectClasses: ( 0.9.2342.19200300.100.4.17 NAME 'domainRelatedObject' D
+ ESC 'RFC1274: an object related to an domain' SUP top AUXILIARY MUST associat
+ edDomain )
+olcObjectClasses: ( 0.9.2342.19200300.100.4.18 NAME 'friendlyCountry' SUP c
+ ountry STRUCTURAL MUST friendlyCountryName )
+olcObjectClasses: ( 0.9.2342.19200300.100.4.20 NAME 'pilotOrganization' SU
+ P ( organization $ organizationalUnit ) STRUCTURAL MAY buildingName )
+olcObjectClasses: ( 0.9.2342.19200300.100.4.21 NAME 'pilotDSA' SUP dsa STR
+ UCTURAL MAY dSAQuality )
+olcObjectClasses: ( 0.9.2342.19200300.100.4.22 NAME 'qualityLabelledData'
+ SUP top AUXILIARY MUST dsaQuality MAY ( subtreeMinimumQuality $ subtreeMaximu
+ mQuality ) )
diff --git a/servers/slapd/schema/dsee.ldif b/servers/slapd/schema/dsee.ldif
new file mode 100644
index 0000000..798fe51
--- /dev/null
+++ b/servers/slapd/schema/dsee.ldif
@@ -0,0 +1,113 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2019-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+#
+# This file is provided for informational purposes only.
+#
+# These definitions are from Sun DSEE 7's cn=schema subentry.
+# None of the attributes had matching rules defined; we've
+# inserted usable ones as needed.
+#
+# Some of these attributes are defined with NO-USER-MODIFICATION,
+# but slapd won't load such definitions from user-modifiable schema
+# files. So that designation has been removed, and commented accordingly.
+#
+dn: cn=dsee,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: dsee
+#
+olcObjectIdentifier: NetscapeRoot 2.16.840.1.113730
+olcObjectIdentifier: NetscapeDS NetscapeRoot:3
+olcObjectIdentifier: NSDSat NetscapeDS:1
+olcObjectIdentifier: NSDSoc NetscapeDS:2
+olcObjectIdentifier: SunRoot 1.3.6.1.4.1.42
+olcObjectIdentifier: SunDS SunRoot:2.27
+#
+olcAttributeTypes: ( NSDSat:5
+ NAME 'changeNumber'
+ DESC 'Changelog attribute type'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ X-ORIGIN 'Changelog Internet Draft' )
+#
+olcAttributeTypes: ( NSDSat:6
+ NAME 'targetDn'
+ DESC 'Changelog attribute type'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ X-ORIGIN 'Changelog Internet Draft' )
+#
+olcAttributeTypes: ( NSDSat:7
+ NAME 'changeType'
+ DESC 'Changelog attribute type'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ X-ORIGIN 'Changelog Internet Draft' )
+#
+# They claim Binary syntax but it's really octetString
+olcAttributeTypes: ( NSDSat:8
+ NAME 'changes'
+ DESC 'Changelog attribute type'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.5
+ X-ORIGIN 'Changelog Internet Draft' )
+#
+olcAttributeTypes: ( NSDSat:9
+ NAME 'newRdn'
+ DESC 'Changelog attribute type'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ X-ORIGIN 'Changelog Internet Draft' )
+#
+olcAttributeTypes: ( NSDSat:10
+ NAME 'deleteOldRdn'
+ DESC 'Changelog attribute type'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+ X-ORIGIN 'Changelog Internet Draft' )
+#
+olcAttributeTypes: ( NSDSat:11
+ NAME 'newSuperior'
+ DESC 'Changelog attribute type'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ X-ORIGIN 'Changelog Internet Draft' )
+#
+# should be generalizedTime, but they used directoryString instead...
+olcAttributeTypes: ( NSDSat:77
+ NAME 'changeTime'
+ DESC 'Sun ONE defined attribute type'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ X-ORIGIN 'Sun ONE Directory Server' )
+#
+# These are UUIDs, but (of course) hyphenated differently than ours.
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( NSDSat:542
+ NAME 'nsUniqueId'
+ DESC 'Sun ONE defined attribute type'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ SINGLE-VALUE
+ X-ORIGIN 'Sun ONE Directory Server' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( SunDS:9.1.596
+ NAME 'targetUniqueId'
+ DESC 'RetroChangelog attribute type'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ SINGLE-VALUE
+ X-ORIGIN 'Sun Directory Server' )
+#
+olcObjectclasses: ( NSDSoc:1
+ NAME 'changeLogEntry'
+ DESC 'LDAP changelog objectclass'
+ SUP top STRUCTURAL
+ MUST ( targetDn $ changeTime $ changeNumber $ changeType )
+ MAY ( changes $ newRdn $ deleteOldRdn $ newSuperior )
+ X-ORIGIN 'Changelog Internet Draft' )
diff --git a/servers/slapd/schema/dsee.schema b/servers/slapd/schema/dsee.schema
new file mode 100644
index 0000000..18a9190
--- /dev/null
+++ b/servers/slapd/schema/dsee.schema
@@ -0,0 +1,109 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2019-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+# This file is provided for informational purposes only.
+
+# These definitions are from Sun DSEE 7's cn=schema subentry.
+# None of the attributes had matching rules defined; we've
+# inserted usable ones as needed.
+
+# Some of these attributes are defined with NO-USER-MODIFICATION,
+# but slapd won't load such definitions from user-modifiable schema
+# files. So that designation has been removed, and commented accordingly.
+
+objectidentifier NetscapeRoot 2.16.840.1.113730
+objectidentifier NetscapeDS NetscapeRoot:3
+objectidentifier NSDSat NetscapeDS:1
+objectidentifier NSDSoc NetscapeDS:2
+objectidentifier SunRoot 1.3.6.1.4.1.42
+objectidentifier SunDS SunRoot:2.27
+
+attributetype ( NSDSat:5
+ NAME 'changeNumber'
+ DESC 'Changelog attribute type'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+ X-ORIGIN 'Changelog Internet Draft' )
+
+attributetype ( NSDSat:6
+ NAME 'targetDn'
+ DESC 'Changelog attribute type'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ X-ORIGIN 'Changelog Internet Draft' )
+
+attributetype ( NSDSat:7
+ NAME 'changeType'
+ DESC 'Changelog attribute type'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ X-ORIGIN 'Changelog Internet Draft' )
+
+# They claim Binary syntax but it's really octetString
+attributetype ( NSDSat:8
+ NAME 'changes'
+ DESC 'Changelog attribute type'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.5
+ X-ORIGIN 'Changelog Internet Draft' )
+
+attributetype ( NSDSat:9
+ NAME 'newRdn'
+ DESC 'Changelog attribute type'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ X-ORIGIN 'Changelog Internet Draft' )
+
+attributetype ( NSDSat:10
+ NAME 'deleteOldRdn'
+ DESC 'Changelog attribute type'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.7
+ X-ORIGIN 'Changelog Internet Draft' )
+
+attributetype ( NSDSat:11
+ NAME 'newSuperior'
+ DESC 'Changelog attribute type'
+ EQUALITY distinguishedNameMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
+ X-ORIGIN 'Changelog Internet Draft' )
+
+# should be generalizedTime, but they used directoryString instead...
+attributeType ( NSDSat:77
+ NAME 'changeTime'
+ DESC 'Sun ONE defined attribute type'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ X-ORIGIN 'Sun ONE Directory Server' )
+
+# These are UUIDs, but (of course) hyphenated differently than ours.
+# NO-USER-MODIFICATION
+attributetype ( NSDSat:542
+ NAME 'nsUniqueId'
+ DESC 'Sun ONE defined attribute type'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ SINGLE-VALUE
+ X-ORIGIN 'Sun ONE Directory Server' )
+
+# NO-USER-MODIFICATION
+attributeype ( SunDS:9.1.596
+ NAME 'targetUniqueId'
+ DESC 'RetroChangelog attribute type'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+ SINGLE-VALUE
+ X-ORIGIN 'Sun Directory Server' )
+
+objectclass ( NSDSoc:1
+ NAME 'changeLogEntry'
+ DESC 'LDAP changelog objectclass'
+ SUP top STRUCTURAL
+ MUST ( targetDn $ changeTime $ changeNumber $ changeType )
+ MAY ( changes $ newRdn $ deleteOldRdn $ newSuperior )
+ X-ORIGIN 'Changelog Internet Draft' )
diff --git a/servers/slapd/schema/duaconf.ldif b/servers/slapd/schema/duaconf.ldif
new file mode 100644
index 0000000..2bfd5a0
--- /dev/null
+++ b/servers/slapd/schema/duaconf.ldif
@@ -0,0 +1,83 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+#
+# DUA schema from draft-joslin-config-schema (a work in progress)
+#
+# This file was automatically generated from duaconf.schema; see that file
+# for complete references.
+#
+dn: cn=duaconf,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: duaconf
+olcObjectIdentifier: {0}DUAConfSchemaOID 1.3.6.1.4.1.11.1.3.1
+olcAttributeTypes: {0}( DUAConfSchemaOID:1.0 NAME 'defaultServerList' DESC 'De
+ fault LDAP server host address used by a DUA' EQUALITY caseIgnoreMatch SYNTAX
+ 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
+olcAttributeTypes: {1}( DUAConfSchemaOID:1.1 NAME 'defaultSearchBase' DESC 'De
+ fault LDAP base DN used by a DUA' EQUALITY distinguishedNameMatch SYNTAX 1.3.
+ 6.1.4.1.1466.115.121.1.12 SINGLE-VALUE )
+olcAttributeTypes: {2}( DUAConfSchemaOID:1.2 NAME 'preferredServerList' DESC '
+ Preferred LDAP server host addresses to be used by a DUA' EQUALITY
+ caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
+olcAttributeTypes: {3}( DUAConfSchemaOID:1.3 NAME 'searchTimeLimit' DESC 'Maxi
+ mum time in seconds a DUA should allow for a search to complete' E
+ QUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: {4}( DUAConfSchemaOID:1.4 NAME 'bindTimeLimit' DESC 'Maximu
+ m time in seconds a DUA should allow for the bind operation to com
+ plete' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALU
+ E )
+olcAttributeTypes: {5}( DUAConfSchemaOID:1.5 NAME 'followReferrals' DESC 'Tell
+ s DUA if it should follow referrals returned by a DSA search resul
+ t' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
+olcAttributeTypes: {6}( DUAConfSchemaOID:1.16 NAME 'dereferenceAliases' DESC '
+ Tells DUA if it should dereference aliases' EQUALITY booleanMatch SYNTAX 1.3.
+ 6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
+olcAttributeTypes: {7}( DUAConfSchemaOID:1.6 NAME 'authenticationMethod' DESC
+ 'A keystring which identifies the type of authentication method us
+ ed to contact the DSA' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.1
+ 21.1.15 SINGLE-VALUE )
+olcAttributeTypes: {8}( DUAConfSchemaOID:1.7 NAME 'profileTTL' DESC 'Time to l
+ ive, in seconds, before a client DUA should re-read this configura
+ tion profile' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SING
+ LE-VALUE )
+olcAttributeTypes: {9}( DUAConfSchemaOID:1.14 NAME 'serviceSearchDescriptor' D
+ ESC 'LDAP search descriptor list used by a DUA' EQUALITY caseExactMatch SYNTA
+ X 1.3.6.1.4.1.1466.115.121.1.15 )
+olcAttributeTypes: {10}( DUAConfSchemaOID:1.9 NAME 'attributeMap' DESC 'Attrib
+ ute mappings used by a DUA' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.14
+ 66.115.121.1.26 )
+olcAttributeTypes: {11}( DUAConfSchemaOID:1.10 NAME 'credentialLevel' DESC 'Id
+ entifies type of credentials a DUA should use when binding to the
+ LDAP server' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26
+ SINGLE-VALUE )
+olcAttributeTypes: {12}( DUAConfSchemaOID:1.11 NAME 'objectclassMap' DESC 'Obj
+ ectclass mappings used by a DUA' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4
+ .1.1466.115.121.1.26 )
+olcAttributeTypes: {13}( DUAConfSchemaOID:1.12 NAME 'defaultSearchScope' DESC
+ 'Default search scope used by a DUA' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6
+ .1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: {14}( DUAConfSchemaOID:1.13 NAME 'serviceCredentialLevel' D
+ ESC 'Identifies type of credentials a DUA should use when binding
+ to the LDAP server for a specific service' EQUALITY caseIgnoreIA5M
+ atch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {15}( DUAConfSchemaOID:1.15 NAME 'serviceAuthenticationMeth
+ od' DESC 'Authentication method used by a service of the DUA' EQUALITY caseIg
+ noreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+olcObjectClasses: {0}( DUAConfSchemaOID:2.5 NAME 'DUAConfigProfile' DESC 'Abst
+ raction of a base configuration for a DUA' SUP top STRUCTURAL MUST cn MAY ( d
+ efaultServerList $ preferredServerList $ defaultSearchBase $ defaultSearchSco
+ pe $ searchTimeLimit $ bindTimeLimit $ credentialLevel $ authenticationMethod
+ $ followReferrals $ dereferenceAliases $ serviceSearchDescriptor $ serviceCr
+ edentialLevel $ serviceAuthenticationMethod $ objectclassMap $ attributeMap $
+ profileTTL ) )
diff --git a/servers/slapd/schema/dyngroup.ldif b/servers/slapd/schema/dyngroup.ldif
new file mode 100644
index 0000000..7383091
--- /dev/null
+++ b/servers/slapd/schema/dyngroup.ldif
@@ -0,0 +1,74 @@
+# dyngroup.schema -- Dynamic Group schema
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+#
+# Dynamic Group schema (experimental), as defined by Netscape. See
+# http://www.redhat.com/docs/manuals/ent-server/pdf/esadmin611.pdf
+# page 70 for details on how these groups were used.
+#
+# A description of the objectclass definition is available here:
+# http://www.redhat.com/docs/manuals/dir-server/schema/7.1/oc_dir.html#1303745
+#
+# depends upon:
+# core.schema
+#
+# These definitions are considered experimental due to the lack of
+# a formal specification (e.g., RFC).
+#
+# NOT RECOMMENDED FOR PRODUCTION USE! USE WITH CAUTION!
+#
+# The Netscape documentation describes this as an auxiliary objectclass
+# but their implementations have always defined it as a structural class.
+# The sloppiness here is because Netscape-derived servers don't actually
+# implement the X.500 data model, and they don't honor the distinction
+# between structural and auxiliary classes. This fact is noted here:
+# http://forum.java.sun.com/thread.jspa?threadID=5016864&messageID=9034636
+#
+# In accordance with other existing implementations, we define it as a
+# structural class.
+#
+# Our definition of memberURL also does not match theirs but again
+# their published definition and what works in practice do not agree.
+# In other words, the Netscape definitions are broken and interoperability
+# is not guaranteed.
+#
+# Also see the new DynGroup proposed spec at
+# http://tools.ietf.org/html/draft-haripriya-dynamicgroup-02
+dn: cn=dyngroup,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: dyngroup
+olcObjectIdentifier: {0}NetscapeRoot 2.16.840.1.113730
+olcObjectIdentifier: {1}NetscapeLDAP NetscapeRoot:3
+olcObjectIdentifier: {2}NetscapeLDAPattributeType NetscapeLDAP:1
+olcObjectIdentifier: {3}NetscapeLDAPobjectClass NetscapeLDAP:2
+olcObjectIdentifier: {4}OpenLDAPExp11 1.3.6.1.4.1.4203.666.11
+olcObjectIdentifier: {5}DynGroupBase OpenLDAPExp11:8
+olcObjectIdentifier: {6}DynGroupAttr DynGroupBase:1
+olcObjectIdentifier: {7}DynGroupOC DynGroupBase:2
+olcAttributeTypes: {0}( NetscapeLDAPattributeType:198 NAME 'memberURL' DESC 'I
+ dentifies an URL associated with each member of a group. Any type of labeled
+ URL can be used.' SUP labeledURI )
+olcAttributeTypes: {1}( DynGroupAttr:1 NAME 'dgIdentity' DESC 'Identity to use
+ when processing the memberURL' SUP distinguishedName SINGLE-VALUE )
+olcAttributeTypes: {2}( DynGroupAttr:2 NAME 'dgAuthz' DESC 'Optional authoriza
+ tion rules that determine who is allowed to assume the dgIdentity' EQUALITY a
+ uthzMatch SYNTAX 1.3.6.1.4.1.4203.666.2.7 X-ORDERED 'VALUES' )
+olcAttributeTypes: {3}( DynGroupAttr:3 NAME 'dgMemberOf' DESC 'Group that the
+ entry belongs to' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115
+ .121.1.12 )
+olcObjectClasses: {0}( NetscapeLDAPobjectClass:33 NAME 'groupOfURLs' SUP top S
+ TRUCTURAL MUST cn MAY ( memberURL $ businessCategory $ description $ o $ ou $
+ owner $ seeAlso ) )
+olcObjectClasses: {1}( DynGroupOC:1 NAME 'dgIdentityAux' SUP top AUXILIARY MAY
+ ( dgIdentity $ dgAuthz ) )
diff --git a/servers/slapd/schema/dyngroup.schema b/servers/slapd/schema/dyngroup.schema
new file mode 100644
index 0000000..f756c33
--- /dev/null
+++ b/servers/slapd/schema/dyngroup.schema
@@ -0,0 +1,98 @@
+# dyngroup.schema -- Dynamic Group schema
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+#
+# Dynamic Group schema (experimental), as defined by Netscape. See
+# http://www.redhat.com/docs/manuals/ent-server/pdf/esadmin611.pdf
+# page 70 for details on how these groups were used.
+#
+# A description of the objectclass definition is available here:
+# http://www.redhat.com/docs/manuals/dir-server/schema/7.1/oc_dir.html#1303745
+#
+# depends upon:
+# core.schema
+#
+# These definitions are considered experimental due to the lack of
+# a formal specification (e.g., RFC).
+#
+# NOT RECOMMENDED FOR PRODUCTION USE! USE WITH CAUTION!
+#
+# The Netscape documentation describes this as an auxiliary objectclass
+# but their implementations have always defined it as a structural class.
+# The sloppiness here is because Netscape-derived servers don't actually
+# implement the X.500 data model, and they don't honor the distinction
+# between structural and auxiliary classes. This fact is noted here:
+# http://forum.java.sun.com/thread.jspa?threadID=5016864&messageID=9034636
+#
+# In accordance with other existing implementations, we define it as a
+# structural class.
+#
+# Our definition of memberURL also does not match theirs but again
+# their published definition and what works in practice do not agree.
+# In other words, the Netscape definitions are broken and interoperability
+# is not guaranteed.
+#
+# Also see the new DynGroup proposed spec at
+# http://tools.ietf.org/html/draft-haripriya-dynamicgroup-02
+
+objectIdentifier NetscapeRoot 2.16.840.1.113730
+
+objectIdentifier NetscapeLDAP NetscapeRoot:3
+objectIdentifier NetscapeLDAPattributeType NetscapeLDAP:1
+objectIdentifier NetscapeLDAPobjectClass NetscapeLDAP:2
+
+objectIdentifier OpenLDAPExp11 1.3.6.1.4.1.4203.666.11
+objectIdentifier DynGroupBase OpenLDAPExp11:8
+objectIdentifier DynGroupAttr DynGroupBase:1
+objectIdentifier DynGroupOC DynGroupBase:2
+
+attributetype ( NetscapeLDAPattributeType:198
+ NAME 'memberURL'
+ DESC 'Identifies an URL associated with each member of a group. Any type of labeled URL can be used.'
+ SUP labeledURI )
+
+attributetype ( DynGroupAttr:1
+ NAME 'dgIdentity'
+ DESC 'Identity to use when processing the memberURL'
+ SUP distinguishedName SINGLE-VALUE )
+
+attributeType ( DynGroupAttr:2
+ NAME 'dgAuthz'
+ DESC 'Optional authorization rules that determine who is allowed to assume the dgIdentity'
+ EQUALITY authzMatch
+ SYNTAX 1.3.6.1.4.1.4203.666.2.7
+ X-ORDERED 'VALUES' )
+
+# Just for testing. The actual memberOf is an operational attribute
+attributeType ( DynGroupAttr:3
+ NAME 'dgMemberOf'
+ DESC 'Group that the entry belongs to'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ EQUALITY distinguishedNameMatch )
+
+objectClass ( NetscapeLDAPobjectClass:33
+ NAME 'groupOfURLs'
+ SUP top STRUCTURAL
+ MUST cn
+ MAY ( memberURL $ businessCategory $ description $ o $ ou $
+ owner $ seeAlso ) )
+
+# The Haripriya dyngroup schema still needs a lot of work.
+# We're just adding support for the dgIdentity attribute for now...
+objectClass ( DynGroupOC:1
+ NAME 'dgIdentityAux'
+ SUP top AUXILIARY
+ MAY ( dgIdentity $ dgAuthz ) )
+
+
diff --git a/servers/slapd/schema/inetorgperson.ldif b/servers/slapd/schema/inetorgperson.ldif
new file mode 100644
index 0000000..780f848
--- /dev/null
+++ b/servers/slapd/schema/inetorgperson.ldif
@@ -0,0 +1,69 @@
+# InetOrgPerson (RFC2798)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+#
+# InetOrgPerson (RFC2798)
+#
+# Depends upon
+# Definition of an X.500 Attribute Type and an Object Class to Hold
+# Uniform Resource Identifiers (URIs) [RFC2079]
+# (core.ldif)
+#
+# A Summary of the X.500(96) User Schema for use with LDAPv3 [RFC2256]
+# (core.ldif)
+#
+# The COSINE and Internet X.500 Schema [RFC1274] (cosine.ldif)
+#
+# This file was automatically generated from inetorgperson.schema; see
+# that file for complete references.
+#
+dn: cn=inetorgperson,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: inetorgperson
+olcAttributeTypes: ( 2.16.840.1.113730.3.1.1 NAME 'carLicense' DESC 'RFC279
+ 8: vehicle license or registration plate' EQUALITY caseIgnoreMatch SUBSTR cas
+ eIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+olcAttributeTypes: ( 2.16.840.1.113730.3.1.2 NAME 'departmentNumber' DESC '
+ RFC2798: identifies a department within an organization' EQUALITY caseIgnoreM
+ atch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+olcAttributeTypes: ( 2.16.840.1.113730.3.1.241 NAME 'displayName' DESC 'RFC
+ 2798: preferred name to be used when displaying entries' EQUALITY caseIgnoreM
+ atch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SI
+ NGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113730.3.1.3 NAME 'employeeNumber' DESC 'RF
+ C2798: numerically identifies an employee within an organization' EQUALITY ca
+ seIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.12
+ 1.1.15 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113730.3.1.4 NAME 'employeeType' DESC 'RFC2
+ 798: type of employment for a person' EQUALITY caseIgnoreMatch SUBSTR caseIgn
+ oreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+olcAttributeTypes: ( 0.9.2342.19200300.100.1.60 NAME 'jpegPhoto' DESC 'RFC2
+ 798: a JPEG image' SYNTAX 1.3.6.1.4.1.1466.115.121.1.28 )
+olcAttributeTypes: ( 2.16.840.1.113730.3.1.39 NAME 'preferredLanguage' DESC
+ 'RFC2798: preferred written or spoken language for a person' EQUALITY caseIg
+ noreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.
+ 15 SINGLE-VALUE )
+olcAttributeTypes: ( 2.16.840.1.113730.3.1.40 NAME 'userSMIMECertificate' D
+ ESC 'RFC2798: PKCS#7 SignedData used to support S/MIME' SYNTAX 1.3.6.1.4.1.14
+ 66.115.121.1.5 )
+olcAttributeTypes: ( 2.16.840.1.113730.3.1.216 NAME 'userPKCS12' DESC 'RFC2
+ 798: personal identity information, a PKCS #12 PFX' SYNTAX 1.3.6.1.4.1.1466.1
+ 15.121.1.5 )
+olcObjectClasses: ( 2.16.840.1.113730.3.2.2 NAME 'inetOrgPerson' DESC 'RFC2
+ 798: Internet Organizational Person' SUP organizationalPerson STRUCTURAL MAY
+ ( audio $ businessCategory $ carLicense $ departmentNumber $ displayName $ em
+ ployeeNumber $ employeeType $ givenName $ homePhone $ homePostalAddress $ ini
+ tials $ jpegPhoto $ labeledURI $ mail $ manager $ mobile $ o $ pager $ photo
+ $ roomNumber $ secretary $ uid $ userCertificate $ x500uniqueIdentifier $ pre
+ ferredLanguage $ userSMIMECertificate $ userPKCS12 ) )
diff --git a/servers/slapd/schema/java.ldif b/servers/slapd/schema/java.ldif
new file mode 100644
index 0000000..74f202e
--- /dev/null
+++ b/servers/slapd/schema/java.ldif
@@ -0,0 +1,59 @@
+# java.ldif -- Java Object Schema
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+#
+# Java Object Schema (defined in RFC 2713)
+# depends upon core.ldif
+#
+# This file was automatically generated from java.schema; see that file
+# for complete references.
+#
+dn: cn=java,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: java
+olcAttributeTypes: {0}( 1.3.6.1.4.1.42.2.27.4.1.6 NAME 'javaClassName' DESC 'F
+ ully qualified name of distinguished Java class or interface' EQUALITY caseEx
+ actMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
+olcAttributeTypes: {1}( 1.3.6.1.4.1.42.2.27.4.1.7 NAME 'javaCodebase' DESC 'UR
+ L(s) specifying the location of class definition' EQUALITY caseExactIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: {2}( 1.3.6.1.4.1.42.2.27.4.1.13 NAME 'javaClassNames' DESC
+ 'Fully qualified Java class or interface name' EQUALITY caseExactMatch SYNTAX
+ 1.3.6.1.4.1.1466.115.121.1.15 )
+olcAttributeTypes: {3}( 1.3.6.1.4.1.42.2.27.4.1.8 NAME 'javaSerializedData' DE
+ SC 'Serialized form of a Java object' SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 SI
+ NGLE-VALUE )
+olcAttributeTypes: {4}( 1.3.6.1.4.1.42.2.27.4.1.10 NAME 'javaFactory' DESC 'Fu
+ lly qualified Java class name of a JNDI object factory' EQUALITY caseExactMat
+ ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )
+olcAttributeTypes: {5}( 1.3.6.1.4.1.42.2.27.4.1.11 NAME 'javaReferenceAddress'
+ DESC 'Addresses associated with a JNDI Reference' EQUALITY caseExactMatch SY
+ NTAX 1.3.6.1.4.1.1466.115.121.1.15 )
+olcAttributeTypes: {6}( 1.3.6.1.4.1.42.2.27.4.1.12 NAME 'javaDoc' DESC 'The Ja
+ va documentation for the class' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1
+ .1466.115.121.1.26 )
+olcObjectClasses: {0}( 1.3.6.1.4.1.42.2.27.4.2.1 NAME 'javaContainer' DESC 'Co
+ ntainer for a Java object' SUP top STRUCTURAL MUST cn )
+olcObjectClasses: {1}( 1.3.6.1.4.1.42.2.27.4.2.4 NAME 'javaObject' DESC 'Java
+ object representation' SUP top ABSTRACT MUST javaClassName MAY ( javaClassNam
+ es $ javaCodebase $ javaDoc $ description ) )
+olcObjectClasses: {2}( 1.3.6.1.4.1.42.2.27.4.2.5 NAME 'javaSerializedObject' D
+ ESC 'Java serialized object' SUP javaObject AUXILIARY MUST javaSerializedData
+ )
+olcObjectClasses: {3}( 1.3.6.1.4.1.42.2.27.4.2.8 NAME 'javaMarshalledObject' D
+ ESC 'Java marshalled object' SUP javaObject AUXILIARY MUST javaSerializedData
+ )
+olcObjectClasses: {4}( 1.3.6.1.4.1.42.2.27.4.2.7 NAME 'javaNamingReference' DE
+ SC 'JNDI reference' SUP javaObject AUXILIARY MAY ( javaReferenceAddress $ jav
+ aFactory ) )
diff --git a/servers/slapd/schema/misc.ldif b/servers/slapd/schema/misc.ldif
new file mode 100644
index 0000000..a77a7e3
--- /dev/null
+++ b/servers/slapd/schema/misc.ldif
@@ -0,0 +1,45 @@
+# misc.ldif -- assorted schema definitions
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+#
+# Assorted definitions from several sources, including
+# ''works in progress''. Contents of this file are
+# subject to change (including deletion) without notice.
+#
+# Not recommended for production use!
+# Use with extreme caution!
+#
+# This file was automatically generated from misc.schema; see that file
+# for complete references.
+#
+dn: cn=misc,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: misc
+olcAttributeTypes: {0}( 2.16.840.1.113730.3.1.13 NAME 'mailLocalAddress' DESC
+ 'RFC822 email address of this recipient' EQUALITY caseIgnoreIA5Match SYNTAX 1
+ .3.6.1.4.1.1466.115.121.1.26{256} )
+olcAttributeTypes: {1}( 2.16.840.1.113730.3.1.18 NAME 'mailHost' DESC 'FQDN of
+ the SMTP/MTA of this recipient' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4
+ .1.1466.115.121.1.26{256} SINGLE-VALUE )
+olcAttributeTypes: {2}( 2.16.840.1.113730.3.1.47 NAME 'mailRoutingAddress' DES
+ C 'RFC822 routing address of this recipient' EQUALITY caseIgnoreIA5Match SYNT
+ AX 1.3.6.1.4.1.1466.115.121.1.26{256} SINGLE-VALUE )
+olcAttributeTypes: {3}( 1.3.6.1.4.1.42.2.27.2.1.15 NAME 'rfc822MailMember' DES
+ C 'rfc822 mail address of group member(s)' EQUALITY caseIgnoreIA5Match SYNTAX
+ 1.3.6.1.4.1.1466.115.121.1.26 )
+olcObjectClasses: {0}( 2.16.840.1.113730.3.2.147 NAME 'inetLocalMailRecipient'
+ DESC 'Internet local mail recipient' SUP top AUXILIARY MAY ( mailLocalAddres
+ s $ mailHost $ mailRoutingAddress ) )
+olcObjectClasses: {1}( 1.3.6.1.4.1.42.2.27.1.2.5 NAME 'nisMailAlias' DESC 'NIS
+ mail alias' SUP top STRUCTURAL MUST cn MAY rfc822MailMember )
diff --git a/servers/slapd/schema/misc.schema b/servers/slapd/schema/misc.schema
new file mode 100644
index 0000000..150abbc
--- /dev/null
+++ b/servers/slapd/schema/misc.schema
@@ -0,0 +1,75 @@
+# misc.schema -- assorted schema definitions
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+#
+# Assorted definitions from several sources, including
+# ''works in progress''. Contents of this file are
+# subject to change (including deletion) without notice.
+#
+# Not recommended for production use!
+# Use with extreme caution!
+
+#-----------------------------------------------------------
+# draft-lachman-laser-ldap-mail-routing-02.txt !!!EXPIRED!!!
+# (a work in progress)
+#
+attributetype ( 2.16.840.1.113730.3.1.13
+ NAME 'mailLocalAddress'
+ DESC 'RFC822 email address of this recipient'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
+
+attributetype ( 2.16.840.1.113730.3.1.18
+ NAME 'mailHost'
+ DESC 'FQDN of the SMTP/MTA of this recipient'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256}
+ SINGLE-VALUE )
+
+attributetype ( 2.16.840.1.113730.3.1.47
+ NAME 'mailRoutingAddress'
+ DESC 'RFC822 routing address of this recipient'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256}
+ SINGLE-VALUE )
+
+# I-D leaves this OID TBD.
+# iPlanet uses 2.16.840.1.113.730.3.2.147 but that is an
+# improperly delegated OID. A typo is likely.
+objectclass ( 2.16.840.1.113730.3.2.147
+ NAME 'inetLocalMailRecipient'
+ DESC 'Internet local mail recipient'
+ SUP top AUXILIARY
+ MAY ( mailLocalAddress $ mailHost $ mailRoutingAddress ) )
+
+#-----------------------------------------------------------
+# draft-srivastava-ldap-mail-00.txt !!!EXPIRED!!!
+# (a work in progress)
+#
+attributetype ( 1.3.6.1.4.1.42.2.27.2.1.15
+ NAME 'rfc822MailMember'
+ DESC 'rfc822 mail address of group member(s)'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+#-----------------------------------------------------------
+# !!!no I-D!!!
+# (a work in progress)
+#
+objectclass ( 1.3.6.1.4.1.42.2.27.1.2.5
+ NAME 'nisMailAlias'
+ DESC 'NIS mail alias'
+ SUP top STRUCTURAL
+ MUST cn
+ MAY rfc822MailMember )
diff --git a/servers/slapd/schema/msuser.ldif b/servers/slapd/schema/msuser.ldif
new file mode 100644
index 0000000..9211c91
--- /dev/null
+++ b/servers/slapd/schema/msuser.ldif
@@ -0,0 +1,4299 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2018-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+#
+# This file is provided for informational purposes only.
+#
+# These definitions are from Microsoft's Active Directory.
+# They were first gathered using the subschemaSubentry object of
+# Windows 2000 Advanced Server Release Candidate 2.
+# Additional definitions were gathered from Windows Server 2012
+# and adjusted to ease compatibility issues.
+#
+# These object classes and attributes are rooted at OID
+# 1.2.840.113556.1, Microsoft's Directory Enabled networks
+# OID. OID Macros are used throughout, to make mapping of
+# incompatible OIDs easier.
+#
+# A number of custom syntaxes have been replaced with generic
+# syntaxes. The original syntax OIDs are commented out below.
+#
+# Many of these attributes are defined with NO-USER-MODIFICATION,
+# but slapd won't load such definitions from user-modifiable schema
+# files. So that designation has been removed, and commented accordingly.
+#
+# Only the subset of Windows 2012 attributes needed to make the
+# user and group objectclasses work has been added to the previously
+# retrieved definitions.
+#
+# If an AD definition clashes with existing standardized schema,
+# the AD definition has been commented out.
+#
+dn: cn=msuser,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: msuser
+#
+olcObjectIdentifier: MicrosoftRoot 1.2.840.113556
+olcObjectIdentifier: MSAD MicrosoftRoot:1
+olcObjectIdentifier: MSADat2 MSAD:2
+olcObjectIdentifier: MSADat4 MSAD:4
+olcObjectIdentifier: MSADat6 MSAD:6
+olcObjectIdentifier: MSADoc3 MSAD:3
+olcObjectIdentifier: MSADoc5 MSAD:5
+olcObjectIdentifier: MSADdnWithOctetString 1.3.6.1.4.1.1466.115.121.1.40
+olcObjectIdentifier: MSADdnWithString 1.3.6.1.4.1.1466.115.121.1.40
+olcObjectIdentifier: MSADtelex 1.3.6.1.4.1.1466.115.121.1.44
+olcObjectIdentifier: MSADlargeInteger 1.3.6.1.4.1.1466.115.121.1.27
+olcObjectIdentifier: MSADcis 1.3.6.1.4.1.1466.115.121.1.44
+olcObjectIdentifier: MSADsecDesc 1.3.6.1.4.1.1466.115.121.1.40
+# original OIDs commented out below
+#olcObjectIdentifier: MSADdnWithOctetString MSADat4:903
+#olcObjectIdentifier: MSADdnWithString MSADat4:904
+#olcObjectIdentifier: MSADtelex MSADat4:905
+#olcObjectIdentifier: MSADlargeInteger MSADat4:906
+#olcObjectIdentifier: MSADcis MSADat4:907
+#olcObjectIdentifier: MSADsecDesc MSADat4:907
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:104
+ NAME 'ownerBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1424
+ NAME 'msCOM-PartitionSetLink'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1425
+ NAME 'msCOM-UserLink'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1669
+ NAME 'msDS-Approx-Immed-Subordinates'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1704
+ NAME 'msDS-NCReplCursors'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:1705
+ NAME 'msDS-NCReplInboundNeighbors'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:1706
+ NAME 'msDS-NCReplOutboundNeighbors'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:1707
+ NAME 'msDS-ReplAttributeMetaData'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:1708
+ NAME 'msDS-ReplValueMetaData'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:1793
+ NAME 'msDS-NonMembers'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1794
+ NAME 'msDS-NonMembersBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:1806
+ NAME 'msDS-MembersForAzRole'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1807
+ NAME 'msDS-MembersForAzRoleBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:1808 NAME 'msDS-OperationsForAzTask' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1809 NAME 'msDS-OperationsForAzTaskBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+olcAttributeTypes: ( MSADat4:1810 NAME 'msDS-TasksForAzTask' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1811 NAME 'msDS-TasksForAzTaskBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+olcAttributeTypes: ( MSADat4:1812 NAME 'msDS-OperationsForAzRole' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1813 NAME 'msDS-OperationsForAzRoleBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+olcAttributeTypes: ( MSADat4:1814 NAME 'msDS-TasksForAzRole' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1815 NAME 'msDS-TasksForAzRoleBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1837 NAME 'msDs-masteredBy' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+olcAttributeTypes: ( MSADat4:1840 NAME 'msDS-ObjectReference' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1841 NAME 'msDS-ObjectReferenceBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:1865 NAME 'msDS-PrincipalName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1930 NAME 'msDS-RevealedDSAs' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1931 NAME 'msDS-KrbTgtLinkBl' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1932 NAME 'msDS-IsFullReplicaFor' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1933 NAME 'msDS-IsDomainFor' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1934 NAME 'msDS-IsPartialReplicaFor' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1957 NAME 'msDS-AuthenticatedToAccountlist' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+olcAttributeTypes: ( MSADat4:1958 NAME 'msDS-AuthenticatedAtDC' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1975 NAME 'msDS-RevealedListBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:1968 NAME 'msDS-NC-RO-Replica-Locations-BL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:2021 NAME 'msDS-PSOApplied' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:2024 NAME 'msDS-NcType' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:2052 NAME 'msDS-OIDToGroupLinkBl' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+olcAttributeTypes: ( MSADat4:2057 NAME 'msDS-HostServiceAccountBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:2058 NAME 'isRecycled' SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:2059 NAME 'msDS-LocalEffectiveDeletionTime' SYNTAX '1.3.6.1.4.1.1466.115.121.1.24' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:2060 NAME 'msDS-LocalEffectiveRecycleTime' SYNTAX '1.3.6.1.4.1.1466.115.121.1.24' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:2067 NAME 'msDS-LastKnownRDN' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:2069 NAME 'msDS-EnabledFeatureBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+olcAttributeTypes: ( MSADat4:2102 NAME 'msDS-ClaimSharesPossibleValuesWithBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+olcAttributeTypes: ( MSADat4:2104 NAME 'msDS-MembersOfResourcePropertyListBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+olcAttributeTypes: ( MSADat4:2168 NAME 'msDS-IsPrimaryComputerFor' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:2188 NAME 'msDS-ValueTypeReferenceBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:2193 NAME 'msDS-TDOIngressBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:2194 NAME 'msDS-TDOEgressBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:2203 NAME 'msDS-parentdistname' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:2235 NAME 'msDS-ReplValueMetaDataExt' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:2236 NAME 'msds-memberOfTransitive' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:2238 NAME 'msds-memberTransitive' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+olcAttributeTypes: ( MSADat6:18.1.347 NAME 'msSFU30PosixMemberOf' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+olcAttributeTypes: ( MSADat6:13.3.102 NAME 'msDFSR-MemberReferenceBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+olcAttributeTypes: ( MSADat6:13.3.103 NAME 'msDFSR-ComputerReferenceBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+#
+olcAttributeTypes: ( MSADat4:1792 NAME 'msDS-AzLDAPQuery' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1801 NAME 'msDS-AzBizRule' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1802 NAME 'msDS-AzBizRuleLanguage' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1803 NAME 'msDS-AzLastImportedBizRulePath' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1819 NAME 'msDS-AzApplicationData' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1949 NAME 'msDS-AzObjectGuid' SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1950 NAME 'msDS-AzGenericData' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:2167 NAME 'msDS-PrimaryComputer' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+olcAttributeTypes: ( MSADat6:18.1.309 NAME 'msSFU30Name' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat6:18.1.339 NAME 'msSFU30NisDomain' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat6:18.1.346 NAME 'msSFU30PosixMember' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:1426 NAME 'msCOM-UserPartitionSetLink' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1441 NAME 'msDS-Cached-Membership' SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1442 NAME 'msDS-Cached-Membership-Time-Stamp' SYNTAX 'MSADlargeInteger' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1443 NAME 'msDS-Site-Affinity' SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+olcAttributeTypes: ( MSADat4:1460 NAME 'msDS-User-Account-Control-Computed' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1696 NAME 'lastLogonTimestamp' SYNTAX 'MSADlargeInteger' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1785 NAME 'msIIS-FTPRoot' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1786 NAME 'msIIS-FTPDir' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1843 NAME 'msDRM-IdentityCertificate' SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+olcAttributeTypes: ( MSADat4:1879 NAME 'msDS-SourceObjectDN' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1892 NAME 'msPKIRoamingTimeStamp' SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1893 NAME 'msPKIDPAPIMasterKeys' SYNTAX 'MSADdnWithOctetString' )
+olcAttributeTypes: ( MSADat4:1894 NAME 'msPKIAccountCredentials' SYNTAX 'MSADdnWithOctetString' )
+olcAttributeTypes: ( MSADat4:1913 NAME 'msRADIUS-FramedInterfaceId' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1914 NAME 'msRADIUS-SavedFramedInterfaceId' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1915 NAME 'msRADIUS-FramedIpv6Prefix' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1916 NAME 'msRADIUS-SavedFramedIpv6Prefix' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1917 NAME 'msRADIUS-FramedIpv6Route' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
+olcAttributeTypes: ( MSADat4:1918 NAME 'msRADIUS-SavedFramedIpv6Route' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1929 NAME 'msDS-SecondaryKrbTgtNumber' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1963 NAME 'msDS-SupportedEncryptionTypes' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1970 NAME 'msDS-LastSuccessfulInteractiveLogonTime' SYNTAX 'MSADlargeInteger' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1971 NAME 'msDS-LastFailedInteractiveLogonTime' SYNTAX 'MSADlargeInteger' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1972 NAME 'msDS-FailedInteractiveLogonCount' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1973 NAME 'msDS-FailedInteractiveLogonCountAtLastSuccessfulLogon' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1976 NAME 'msTSProfilePath' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1977 NAME 'msTSHomeDirectory' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1978 NAME 'msTSHomeDrive' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1979 NAME 'msTSAllowLogon' SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1980 NAME 'msTSRemoteControl' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1981 NAME 'msTSMaxDisconnectionTime' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1982 NAME 'msTSMaxConnectionTime' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1983 NAME 'msTSMaxIdleTime' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1984 NAME 'msTSReconnectionAction' SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1985 NAME 'msTSBrokenConnectionAction' SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1986 NAME 'msTSConnectClientDrives' SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1987 NAME 'msTSConnectPrinterDrives' SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1988 NAME 'msTSDefaultToMainPrinter' SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1989 NAME 'msTSWorkDirectory' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1990 NAME 'msTSInitialProgram' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1991 NAME 'msTSProperty01' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+olcAttributeTypes: ( MSADat4:1992 NAME 'msTSProperty02' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+olcAttributeTypes: ( MSADat4:1993 NAME 'msTSExpireDate' SYNTAX '1.3.6.1.4.1.1466.115.121.1.24' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1994 NAME 'msTSLicenseVersion' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1995 NAME 'msTSManagingLS' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1996 NAME 'msDS-UserPasswordExpiryTimeComputed' SYNTAX 'MSADlargeInteger' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:2008 NAME 'msTSManagingLS4' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:2005 NAME 'msTSManagingLS3' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:2002 NAME 'msTSManagingLS2' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:2006 NAME 'msTSExpireDate4' SYNTAX '1.3.6.1.4.1.1466.115.121.1.24' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:2003 NAME 'msTSExpireDate3' SYNTAX '1.3.6.1.4.1.1466.115.121.1.24' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:2000 NAME 'msTSExpireDate2' SYNTAX '1.3.6.1.4.1.1466.115.121.1.24' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:2004 NAME 'msTSLicenseVersion3' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:2001 NAME 'msTSLicenseVersion2' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:2007 NAME 'msTSLicenseVersion4' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:2009 NAME 'msTSLSProperty01' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+olcAttributeTypes: ( MSADat4:2010 NAME 'msTSLSProperty02' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:2022 NAME 'msDS-ResultantPSO' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:2050 NAME 'msPKI-CredentialRoamingTokens' SYNTAX 'MSADdnWithOctetString' )
+olcAttributeTypes: ( MSADat4:2073 NAME 'msTSPrimaryDesktop' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:2075 NAME 'msTSSecondaryDesktops' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+olcAttributeTypes: ( MSADat4:2276 NAME 'msDS-SyncServerUrl' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+olcAttributeTypes: ( MSADat4:2285 NAME 'msDS-AssignedAuthNPolicySilo' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:2288 NAME 'msDS-AuthNPolicySiloMembersBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+olcAttributeTypes: ( MSADat4:2295 NAME 'msDS-AssignedAuthNPolicy' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )
+#
+## NO-USER-MODIFICATION
+#olcAttributeTypes: ( MSADat4:1459 NAME 'msDS-Behavior-Version' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+#olcAttributeTypes: ( MSADat4:1788 NAME 'msDS-PerUserTrustQuota' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+#olcAttributeTypes: ( MSADat4:1789 NAME 'msDS-AllUsersTrustQuota' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+#olcAttributeTypes: ( MSADat4:1790 NAME 'msDS-PerUserTrustTombstonesQuota' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1717 NAME 'msDS-AdditionalDnsHostName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+## NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1718 NAME 'msDS-AdditionalSamAccountName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1783 NAME 'msDS-ExecuteScriptPassword' SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1923 NAME 'msDS-KrbTgtLink' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1924 NAME 'msDS-RevealedUsers' SYNTAX 'MSADdnWithOctetString' )
+olcAttributeTypes: ( MSADat4:1926 NAME 'msDS-NeverRevealGroup' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+olcAttributeTypes: ( MSADat4:1928 NAME 'msDS-RevealOnDemandGroup' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1940 NAME 'msDS-RevealedList' SYNTAX 'MSADdnWithString' )
+olcAttributeTypes: ( MSADat4:1959 NAME 'msDS-isGC' SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1960 NAME 'msDS-isRODC' SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1961 NAME 'msDS-SiteName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1962 NAME 'msDS-PromotionSettings' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:1966 NAME 'msTPM-OwnerInformation' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:2025 NAME 'msDS-IsUserCachableAtRodc' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:2056 NAME 'msDS-HostServiceAccount' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+olcAttributeTypes: ( MSADat4:2070 NAME 'msTSEndpointData' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:2071 NAME 'msTSEndpointType' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:2072 NAME 'msTSEndpointPlugin' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:2074 NAME 'msTSPrimaryDesktopBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:2078 NAME 'msTSSecondaryDesktopBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+olcAttributeTypes: ( MSADat4:2109 NAME 'msTPM-TpmInformationForComputer' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:2166 NAME 'msDS-GenerationId' SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:2180 NAME 'msImaging-ThumbprintHash' SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:2181 NAME 'msImaging-HashAlgorithm' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat4:2234 NAME 'netbootDUID' SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' SINGLE-VALUE )
+olcAttributeTypes: ( MSADat6:18.1.323 NAME 'msSFU30Aliases' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
+#olcAttributeTypes: ( 1.3.6.1.1.1.1.26 NAME 'nisMapName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
+#
+#
+olcAttributeTypes: ( MSADat4:856
+ NAME 'netbootNewMachineOU'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:13
+ NAME 'builtinCreationTime'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1335
+ NAME 'pKIEnrollmentAccess'
+ SYNTAX 'MSADcis' )
+#
+olcAttributeTypes: ( MSADat4:1333
+ NAME 'pKIExtendedKeyUsage'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:1123
+ NAME 'msNPCalledStationID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
+#
+olcAttributeTypes: ( MSADat4:539
+ NAME 'initialAuthIncoming'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:370
+ NAME 'objectClassCategory'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:41
+ NAME 'generatedConnection'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:911
+ NAME 'allowedChildClasses'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+#
+olcAttributeTypes: ( MSADat4:68
+ NAME 'machineArchitecture'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' )
+#
+olcAttributeTypes: ( MSADat4:767
+ NAME 'aCSMaxPeakBandwidth'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:72
+ NAME 'marshalledInterface'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:368
+ NAME 'rIDManagerReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:770
+ NAME 'aCSEnableACSService'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1237
+ NAME 'mSMQRoutingService'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1405
+ NAME 'mS-SQL-AllowQueuedUpdatingSubscription'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:648
+ NAME 'primaryTelexNumber'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:8
+ NAME 'userAccountControl'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:563
+ NAME 'shellPropertyPages'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:4
+ NAME 'replUpToDateVector'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:484
+ NAME 'fRSDirectoryFilter'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:230
+ NAME 'printSeparatorFile'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1329
+ NAME 'pKIMaxIssuingDepth'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1307
+ NAME 'accountNameHistory'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:1386
+ NAME 'mS-SQL-GPSLongitude'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:562
+ NAME 'adminPropertyPages'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:121
+ NAME 'securityIdentifier'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:166
+ NAME 'groupMembershipSAM'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:659
+ NAME 'serviceDNSNameType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:585
+ NAME 'meetingIsEncrypted'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1400
+ NAME 'mS-SQL-Applications'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:330
+ NAME 'lastUpdateSequence'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:50
+ NAME 'lastContentIndexed'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:567
+ NAME 'meetingDescription'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+#olcAttributeTypes: ( MSADat4:880
+# NAME 'fRSTimeLastCommand'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.53'
+# SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:83
+ NAME 'monikerDisplayName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:321
+ NAME 'requiredCategories'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:813
+ NAME 'upgradeProductCode'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:774
+ NAME 'aCSMaxNoOfLogFiles'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1370
+ NAME 'mS-SQL-CharacterSet'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:578
+ NAME 'meetingContactInfo'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1397
+ NAME 'mS-SQL-CreationDate'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:32
+ NAME 'domainPolicyObject'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:703
+ NAME 'dhcpObjDescription'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:573
+ NAME 'meetingApplication'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:518
+ NAME 'defaultHidingValue'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:875
+ NAME 'fRSMemberReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:701
+ NAME 'dhcpIdentification'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:135
+ NAME 'trustAuthOutgoing'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:197
+ NAME 'systemMustContain'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1412
+ NAME 'primaryGroupToken'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:118
+ NAME 'rpcNsProfileEntry'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:129
+ NAME 'trustAuthIncoming'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1225
+ NAME 'mSMQPrevSiteGates'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:607
+ NAME 'queryPolicyObject'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:712
+ NAME 'optionDescription'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:1314
+ NAME 'aCSMaximumSDUSize'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:530
+ NAME 'nonSecurityMember'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:31
+ NAME 'fRSReplicaSetType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:763
+ NAME 'aCSTotalNoOfFlows'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:915
+ NAME 'possibleInferiors'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+#
+olcAttributeTypes: ( MSADat4:851
+ NAME 'netbootMaxClients'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1385
+ NAME 'mS-SQL-GPSLatitude'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:765
+ NAME 'aCSPermissionBits'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:926
+ NAME 'mSMQTransactional'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1390
+ NAME 'mS-SQL-Description'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:913
+ NAME 'allowedAttributes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+#
+olcAttributeTypes: ( MSADat4:491
+ NAME 'fRSFaultCondition'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:54
+ NAME 'tombstoneLifetime'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:809
+ NAME 'remoteStorageGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:644
+ NAME 'showInAddressBook'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:213
+ NAME 'defaultClassStore'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:577
+ NAME 'meetingOriginator'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:656
+ NAME 'userPrincipalName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1316
+ NAME 'aCSMinimumLatency'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+#olcAttributeTypes: ( MSADat2:617
+# NAME 'homePostalAddress'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:638
+ NAME 'isPrivilegeHolder'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:533
+ NAME 'fRSReplicaSetGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:371
+ NAME 'rIDAllocationPool'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1327
+ NAME 'pKIDefaultKeySpec'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:537
+ NAME 'dynamicLDAPServer'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:516
+ NAME 'serverReferenceBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:500
+ NAME 'fRSServiceCommand'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1304
+ NAME 'sDRightsEffective'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1249
+ NAME 'proxiedObjectName'
+ SYNTAX 'MSADdnWithOctetString'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:586
+ NAME 'meetingRecurrence'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:251
+ NAME 'cOMTreatAsClassId'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1245
+ NAME 'globalAddressList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:908
+ NAME 'extendedClassInfo'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:459
+ NAME 'machineWidePolicy'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:356
+ NAME 'foreignIdentifier'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1242
+ NAME 'dNReferenceUpdate'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:134
+ NAME 'trustPosixOffset'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:36
+ NAME 'enabledConnection'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:627
+ NAME 'ipsecNFAReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:86
+ NAME 'userWorkstations'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:301
+ NAME 'garbageCollPeriod'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:933
+ NAME 'mSMQComputerType'
+ SYNTAX 'MSADtelex'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:65
+ NAME 'logonWorkstation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:921
+ NAME 'mSMQJournalQuota'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:108
+ NAME 'remoteSourceType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:95
+ NAME 'pwdHistoryLength'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:920
+ NAME 'mSMQBasePriority'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:196
+ NAME 'systemMayContain'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+#
+olcAttributeTypes: ( MSADat4:1407
+ NAME 'mS-SQL-ThirdParty'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1243
+ NAME 'mSMQQueueNameExt'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:485
+ NAME 'fRSUpdateTimeout'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:924
+ NAME 'mSMQPrivacyLevel'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:615
+ NAME 'shellContextMenu'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:618
+ NAME 'wellKnownObjects'
+ SYNTAX 'MSADdnWithOctetString' )
+#
+olcAttributeTypes: ( MSADat4:789
+ NAME 'transportDLLName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:458
+ NAME 'qualityOfService'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:73
+ NAME 'lockoutThreshold'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:105
+ NAME 'remoteServerName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:694
+ NAME 'previousParentCA'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:1345
+ NAME 'dSUIShellMaximum'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:303
+ NAME 'notificationList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1244
+ NAME 'addressBookRoots'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:878
+ NAME 'fRSPrimaryMember'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+#olcAttributeTypes: ( MSADat4:587
+# NAME 'meetingStartTime'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.53' )
+#
+olcAttributeTypes: ( MSADat4:1310
+ NAME 'mSMQSiteGatesMig'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:709
+ NAME 'dhcpReservations'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44' )
+#
+olcAttributeTypes: ( MSADat4:614
+ NAME 'adminContextMenu'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:1332
+ NAME 'pKIOverlapPeriod'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:142
+ NAME 'winsockAddresses'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:923
+ NAME 'mSMQAuthenticate'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1344
+ NAME 'dSUIAdminMaximum'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:848
+ NAME 'appSchemaVersion'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:123
+ NAME 'serviceClassInfo'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:769
+ NAME 'aCSEventLogLevel'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:751
+ NAME 'userSharedFolder'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:421
+ NAME 'domainWidePolicy'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:669
+ NAME 'rIDSetReferences'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:815
+ NAME 'canUpgradeScript'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:610
+ NAME 'classDisplayName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat2:226
+ NAME 'adminDescription'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:67
+ NAME 'lSAModifiedCount'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:509
+ NAME 'serviceClassName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:56
+ NAME 'localPolicyFlags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:115
+ NAME 'rpcNsInterfaceID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:194
+ NAME 'adminDisplayName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:753
+ NAME 'nameServiceFlags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:589
+ NAME 'meetingBandwidth'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:755
+ NAME 'domainIdentifier'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:370
+ NAME 'rIDAvailablePool'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:655
+ NAME 'legacyExchangeDN'
+ SYNTAX 'MSADtelex'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:470
+ NAME 'trustAttributes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:535
+ NAME 'fRSRootSecurity'
+ SYNTAX 'MSADcis'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:532
+ NAME 'superiorDNSRoot'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:278
+ NAME 'printMaxYExtent'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:277
+ NAME 'printMaxXExtent'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:280
+ NAME 'printMinYExtent'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:279
+ NAME 'printMinXExtent'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:32
+ NAME 'attributeSyntax'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:247
+ NAME 'printAttributes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:152
+ NAME 'groupAttributes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:816
+ NAME 'fileExtPriority'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:930
+ NAME 'mSMQServiceType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:363
+ NAME 'operatingSystem'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1371
+ NAME 'mS-SQL-SortOrder'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:329
+ NAME 'versionNumberLo'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:884
+ NAME 'msRRASAttribute'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:781
+ NAME 'lastKnownParent'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1209
+ NAME 'shortServerName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:60
+ NAME 'lockoutDuration'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:232
+ NAME 'defaultPriority'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:754
+ NAME 'rpcNsEntryFlags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:713
+ NAME 'optionsLocation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44' )
+#
+olcAttributeTypes: ( MSADat4:328
+ NAME 'versionNumberHi'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:366
+ NAME 'rpcNsAnnotation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:886
+ NAME 'purportedSearch'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:776
+ NAME 'aCSDSBMPriority'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:961
+ NAME 'mSMQSiteForeign'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' )
+#
+olcAttributeTypes: ( MSADat4:335
+ NAME 'currentLocation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:570
+ NAME 'meetingProtocol'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:420
+ NAME 'publicKeyPolicy'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1402
+ NAME 'mS-SQL-Publisher'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:812
+ NAME 'createWizardExt'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:1373
+ NAME 'mS-SQL-Clustered'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:334
+ NAME 'volTableIdxGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:696
+ NAME 'currentParentCA'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:504
+ NAME 'seqNotification'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:515
+ NAME 'serverReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1119
+ NAME 'msNPAllowDialin'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1387
+ NAME 'mS-SQL-GPSHeight'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1378
+ NAME 'mS-SQL-AppleTalk'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:269
+ NAME 'linkTrackSecret'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:378
+ NAME 'dnsAllowDynamic'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:49
+ NAME 'badPasswordTime'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:637
+ NAME 'privilegeHolder'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:289
+ NAME 'printMediaReady'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:288
+ NAME 'printMACAddress'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:66
+ NAME 'lSACreationTime'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:569
+ NAME 'meetingLocation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:784
+ NAME 'aCSIdentityName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1410
+ NAME 'mS-DS-CreatorSID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1374
+ NAME 'mS-SQL-NamedPipe'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:843
+ NAME 'lDAPAdminLimits'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat2:460
+ NAME 'lDAPDisplayName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:218
+ NAME 'applicationName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:695
+ NAME 'pendingParentCA'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:779
+ NAME 'aCSCacheTimeout'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:574
+ NAME 'meetingLanguage'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:778
+ NAME 'aCSDSBMDeadTime'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:697
+ NAME 'cACertificateDN'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:138
+ NAME 'userParameters'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:132
+ NAME 'trustDirection'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:962
+ NAME 'mSMQQueueQuota'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:936
+ NAME 'mSMQEncryptKey'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:885
+ NAME 'terminalServer'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:233
+ NAME 'printStartTime'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:664
+ NAME 'syncWithObject'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:344
+ NAME 'groupsToIgnore'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:665
+ NAME 'syncMembership'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:666
+ NAME 'syncAttributes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:214
+ NAME 'nextLevelStore'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:302
+ NAME 'sAMAccountType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1401
+ NAME 'mS-SQL-Keywords'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat2:210
+ NAME 'proxyAddresses'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:284
+ NAME 'bytesPerMinute'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:241
+ NAME 'printMaxCopies'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:98
+ NAME 'primaryGroupID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:89
+ NAME 'nTGroupMembers'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:1228
+ NAME 'mSMQDsServices'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:43
+ NAME 'fRSVersionGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:486
+ NAME 'fRSWorkingPath'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:18
+ NAME 'otherTelephone'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat2:277
+ NAME 'otherHomePhone'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:151
+ NAME 'oEMInformation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:459
+ NAME 'networkAddress'
+ SYNTAX 'MSADtelex' )
+#
+olcAttributeTypes: ( MSADat4:966
+ NAME 'mSMQDigestsMig'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:568
+ NAME 'meetingKeyword'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:844
+ NAME 'lDAPIPDenyList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:847
+ NAME 'installUiLevel'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:894
+ NAME 'gPCFileSysPath'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:488
+ NAME 'fRSStagingPath'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:351
+ NAME 'auxiliaryClass'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+#
+olcAttributeTypes: ( MSADat4:159
+ NAME 'accountExpires'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:718
+ NAME 'dhcpProperties'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:346
+ NAME 'desktopProfile'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:762
+ NAME 'aCSServiceType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+#olcAttributeTypes: ( MSADat2:610
+# NAME 'employeeNumber'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1213
+ NAME 'assocNTAccount'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:498
+ NAME 'creationWizard'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:253
+ NAME 'cOMOtherProgId'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:202
+ NAME 'auditingPolicy'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:635
+ NAME 'privilegeValue'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1366
+ NAME 'mS-SQL-Location'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1334
+ NAME 'pKIDefaultCSPs'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:270
+ NAME 'printShareName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:33
+ NAME 'isSingleValued'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:472
+ NAME 'domainCrossRef'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1240
+ NAME 'netbootSIFFile'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:250
+ NAME 'cOMUniqueLIBID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:657
+ NAME 'serviceDNSName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:782
+ NAME 'objectCategory'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:122
+ NAME 'serviceClassID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:720
+ NAME 'dhcpUpdateTime'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:221
+ NAME 'sAMAccountName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+#olcAttributeTypes: ( MSADat4:588
+# NAME 'meetingEndTime'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.53' )
+#
+olcAttributeTypes: ( MSADat4:1389
+ NAME 'mS-SQL-Language'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:777
+ NAME 'aCSDSBMRefresh'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1393
+ NAME 'mS-SQL-Database'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:20
+ NAME 'cOMInterfaceID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:1403
+ NAME 'mS-SQL-AllowKnownPullSubscription'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1394
+ NAME 'mS-SQL-AllowAnonymousSubscription'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:654
+ NAME 'managedObjects'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat2:8
+ NAME 'possSuperiors'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+#
+olcAttributeTypes: ( MSADat4:791
+ NAME 'transportType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:345
+ NAME 'groupPriority'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:117
+ NAME 'rpcNsPriority'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' )
+#
+olcAttributeTypes: ( MSADat4:917
+ NAME 'mSMQQueueType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:141
+ NAME 'versionNumber'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:121
+ NAME 'uSNLastObjRem'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1346
+ NAME 'templateRoots'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:93
+ NAME 'pwdProperties'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:290
+ NAME 'printNumberUp'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:536
+ NAME 'fRSExtensions'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:286
+ NAME 'printRateUnit'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:846
+ NAME 'msiScriptSize'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:274
+ NAME 'printSpooling'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:608
+ NAME 'queryPolicyBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:103
+ NAME 'proxyLifetime'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:144
+ NAME 'operatorCount'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:860
+ NAME 'netbootServer'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:369
+ NAME 'fSMORoleOwner'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:276
+ NAME 'driverVersion'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1388
+ NAME 'mS-SQL-Version'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:939
+ NAME 'mSMQNameStyle'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:471
+ NAME 'schemaVersion'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:436
+ NAME 'directReports'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat2:255
+ NAME 'addressSyntax'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:235
+ NAME 'printFormName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:15
+ NAME 'msiScriptPath'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1312
+ NAME 'aCSServerList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+#olcAttributeTypes: ( MSADat2:615
+# NAME 'personalTitle'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1305
+ NAME 'moveTreeState'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:945
+ NAME 'mSMQSiteGates'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:1238
+ NAME 'mSMQDsService'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:76
+ NAME 'objectVersion'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1414
+ NAME 'dNSTombstoned'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:941
+ NAME 'mSMQLongLived'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:534
+ NAME 'fRSLevelLimit'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:845
+ NAME 'msiScriptName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+#olcAttributeTypes: ( MSADat4:44
+# NAME 'homeDirectory'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:698
+ NAME 'dhcpUniqueKey'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:227
+ NAME 'extensionName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+#olcAttributeTypes: ( MSADat2:256
+# NAME 'streetAddress'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:113
+ NAME 'rpcNsBindings'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:237
+ NAME 'printBinNames'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:109
+ NAME 'replicaSource'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:246
+ NAME 'printLanguage'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:1365
+ NAME 'mS-SQL-Contact'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:357
+ NAME 'nTMixedDomain'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:483
+ NAME 'fRSFileFilter'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:332
+ NAME 'birthLocation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:682
+ NAME 'friendlyNames'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:622
+ NAME 'ipsecDataType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:584
+ NAME 'meetingRating'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:681
+ NAME 'indexedScopes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:312
+ NAME 'rpcNsObjectID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:168
+ NAME 'modifiedCount'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:218
+ NAME 'oMObjectClass'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:772
+ NAME 'aCSPolicyName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:502
+ NAME 'timeVolChange'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:337
+ NAME 'currMachineId'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:120
+ NAME 'schemaFlagsEx'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1356
+ NAME 'validAccesses'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:158
+ NAME 'domainReplica'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1309
+ NAME 'mSMQInterval2'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1308
+ NAME 'mSMQInterval1'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:916
+ NAME 'canonicalName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:94
+ NAME 'ntPwdHistory'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:133
+ NAME 'trustPartner'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:160
+ NAME 'lmPwdHistory'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:1380
+ NAME 'mS-SQL-Status'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:469
+ NAME 'USNIntersite'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:858
+ NAME 'netbootTools'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:99
+ NAME 'priorSetTime'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1367
+ NAME 'mS-SQL-Memory'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:950
+ NAME 'mSMQServices'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+#olcAttributeTypes: ( MSADat2:613
+# NAME 'employeeType'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:27
+ NAME 'currentValue'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:822
+ NAME 'siteLinkList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:107
+ NAME 'remoteSource'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:325
+ NAME 'setupCommand'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:212
+ NAME 'dSHeuristics'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1336
+ NAME 'replInterval'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:234
+ NAME 'printEndTime'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:1
+ NAME 'instanceType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:722
+ NAME 'otherIpPhone'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:965
+ NAME 'mSMQSiteName'
+ SYNTAX 'MSADtelex'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:579
+ NAME 'meetingOwner'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:242
+ NAME 'printCollate'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:480
+ NAME 'defaultGroup'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:79
+ NAME 'minPwdLength'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:864
+ NAME 'netbootSCPBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:650
+ NAME 'mhsORAddress'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+#olcAttributeTypes: ( MSADat4:651
+# NAME 'otherMailbox'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:367
+ NAME 'rpcNsCodeset'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:14
+ NAME 'hasMasterNCs'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:952
+ NAME 'mSMQMigrated'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:74
+ NAME 'dSASignature'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:115
+ NAME 'invocationId'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:254
+ NAME 'cOMTypelibId'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:26
+ NAME 'creationTime'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:581
+ NAME 'meetingScope'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:336
+ NAME 'volTableGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:513
+ NAME 'siteObjectBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:756
+ NAME 'aCSTimeOfDay'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:757
+ NAME 'aCSDirection'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:77
+ NAME 'maxTicketAge'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:481
+ NAME 'schemaUpdate'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.24'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:80
+ NAME 'minTicketAge'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:628
+ NAME 'ipsecNegotiationPolicyReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:327
+ NAME 'helpFileName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:148
+ NAME 'schemaIDGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:810
+ NAME 'createDialog'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:964
+ NAME 'mSMQNt4Flags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:327
+ NAME 'packageFlags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:464
+ NAME 'wWWHomePage'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:507
+ NAME 'volumeCount'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:273
+ NAME 'printStatus'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:890
+ NAME 'uPNSuffixes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:471
+ NAME 'trustParent'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1301
+ NAME 'tokenGroups'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:375
+ NAME 'systemFlags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:667
+ NAME 'syncWithSID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1306
+ NAME 'dNSProperty'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:710
+ NAME 'superScopes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44' )
+#
+olcAttributeTypes: ( MSADat4:1347
+ NAME 'sPNMappings'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:272
+ NAME 'printNotify'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:282
+ NAME 'printMemory'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:154
+ NAME 'serverState'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:942
+ NAME 'mSMQVersion'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:373
+ NAME 'rIDUsedPool'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1355
+ NAME 'queryFilter'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:300
+ NAME 'printerName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:97
+ NAME 'preferredOU'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:649
+ NAME 'primaryInternationalISDNNumber'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:333
+ NAME 'oMTIndxGuid'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1337
+ NAME 'mSMQUserSid'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:487
+ NAME 'fRSRootPath'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:918
+ NAME 'mSMQJournal'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:499
+ NAME 'contextMenu'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:764
+ NAME 'aCSPriority'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:937
+ NAME 'mSMQSignKey'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:359
+ NAME 'netbootGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:925
+ NAME 'mSMQOwnerID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:24
+ NAME 'mustContain'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+#
+olcAttributeTypes: ( MSADat4:379
+ NAME 'dnsAllowXFR'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1379
+ NAME 'mS-SQL-Vines'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:948
+ NAME 'mSMQDigests'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:662
+ NAME 'lockoutTime'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:53
+ NAME 'lastSetTime'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:25
+ NAME 'countryCode'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1377
+ NAME 'mS-SQL-TCPIP'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:934
+ NAME 'mSMQForeign'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:571
+ NAME 'meetingType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:714
+ NAME 'dhcpOptions'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:704
+ NAME 'dhcpServers'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44' )
+#
+olcAttributeTypes: ( MSADat4:283
+ NAME 'assetNumber'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:350
+ NAME 'addressType'
+ SYNTAX 'MSADtelex'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:940
+ NAME 'mSMQCSPName'
+ SYNTAX 'MSADtelex'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:671
+ NAME 'msiFileList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:619
+ NAME 'dNSHostName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:705
+ NAME 'dhcpSubnets'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44' )
+#
+olcAttributeTypes: ( MSADat4:1328
+ NAME 'pKIKeyUsage'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:30
+ NAME 'attributeID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:506
+ NAME 'objectCount'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:503
+ NAME 'timeRefresh'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:139
+ NAME 'profilePath'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:818
+ NAME 'productCode'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:647
+ NAME 'otherMobile'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:12
+ NAME 'badPwdCount'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1368
+ NAME 'mS-SQL-Build'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+#olcAttributeTypes: ( MSADat2:13
+# NAME 'displayName'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:87
+ NAME 'nETBIOSName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1395
+ NAME 'mS-SQL-Alias'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:75
+ NAME 'maxRenewAge'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:806
+ NAME 'treatAsLeaf'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:960
+ NAME 'mSMQNt4Stub'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' )
+#
+olcAttributeTypes: ( MSADat4:324
+ NAME 'packageType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1212
+ NAME 'isEphemeral'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:36
+ NAME 'dMDLocation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:715
+ NAME 'dhcpClasses'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:39
+ NAME 'forceLogoff'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:2
+ NAME 'whenCreated'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.24'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:566
+ NAME 'meetingName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:786
+ NAME 'mailAddress'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:590
+ NAME 'meetingBlob'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:71
+ NAME 'machineRole'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:334
+ NAME 'searchFlags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:3
+ NAME 'whenChanged'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.24'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:702
+ NAME 'dhcpObjName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:897
+ NAME 'aCSMaxAggregatePeakRatePerUser'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:326
+ NAME 'packageName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:170
+ NAME 'systemOnly'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:935
+ NAME 'mSMQOSType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:680
+ NAME 'queryPoint'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:271
+ NAME 'printOwner'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:19
+ NAME 'uSNCreated'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:494
+ NAME 'siteServer'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:114
+ NAME 'rpcNsGroup'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:609
+ NAME 'sIDHistory'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:882
+ NAME 'fRSVersion'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:64
+ NAME 'logonHours'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:854
+ NAME 'netbootAnswerOnlyValidClients'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:96
+ NAME 'pwdLastSet'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:243
+ NAME 'printColor'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1391
+ NAME 'mS-SQL-Type'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:40
+ NAME 'fromServer'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:157
+ NAME 'serverRole'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:100
+ NAME 'priorValue'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:169
+ NAME 'logonCount'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:90
+ NAME 'unicodePwd'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:21
+ NAME 'subClassOf'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1396
+ NAME 'mS-SQL-Size'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:101
+ NAME 'privateKey'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:512
+ NAME 'siteObject'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:62
+ NAME 'scriptPath'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:223
+ NAME 'serverName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:953
+ NAME 'mSMQSiteID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:340
+ NAME 'rightsGuid'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:374
+ NAME 'rIDNextRID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:583
+ NAME 'meetingURL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat2:400
+ NAME 'addressEntryDisplayTableMSDOS'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:76
+ NAME 'maxStorage'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:35
+ NAME 'rangeUpper'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:34
+ NAME 'rangeLower'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:118
+ NAME 'otherPager'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:639
+ NAME 'isMemberOfPartialAttributeSet'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1224
+ NAME 'parentGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:141
+ NAME 'department'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:25
+ NAME 'mayContain'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+#
+olcAttributeTypes: ( MSADat4:150
+ NAME 'adminCount'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:51
+ NAME 'lastLogoff'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1409
+ NAME 'masteredBy'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:35
+ NAME 'employeeID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:719
+ NAME 'dhcpMaxKey'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:229
+ NAME 'driverName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1363
+ NAME 'mS-SQL-Name'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:322
+ NAME 'categoryId'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:889
+ NAME 'additionalTrustedServiceNames'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:1354
+ NAME 'scopeFlags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:672
+ NAME 'categories'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:855
+ NAME 'netbootNewMachineNamingPolicy'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:19
+ NAME 'cOMClassID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:120
+ NAME 'uSNChanged'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:2
+ NAME 'objectGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:707
+ NAME 'dhcpRanges'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1358
+ NAME 'schemaInfo'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:646
+ NAME 'otherFacsimileTelephoneNumber'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:520
+ NAME 'machinePasswordChangeInterval'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:674
+ NAME 'rootTrust'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:136
+ NAME 'trustType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:750
+ NAME 'groupType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:896
+ NAME 'uSNSource'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:919
+ NAME 'mSMQQuota'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:927
+ NAME 'mSMQSites'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:910
+ NAME 'fromEntry'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' )
+#
+olcAttributeTypes: ( MSADat4:1376
+ NAME 'mS-SQL-SPX'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:892
+ NAME 'gPOptions'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:814
+ NAME 'msiScript'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:285
+ NAME 'printRate'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:683
+ NAME 'cRLPartitionedRevocationList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:652
+ NAME 'assistant'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:490
+ NAME 'fRSDSPoll'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:663
+ NAME 'partialAttributeDeletionList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:52
+ NAME 'lastLogon'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:22
+ NAME 'governsID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:341
+ NAME 'appliesTo'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:268
+ NAME 'eFSPolicy'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:155
+ NAME 'uASCompat'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:538
+ NAME 'prefixMap'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:661
+ NAME 'isDefunct'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:708
+ NAME 'dhcpSites'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44' )
+#
+olcAttributeTypes: ( MSADat4:888
+ NAME 'iPSECNegotiationPolicyAction'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:382
+ NAME 'dnsRecord'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:21
+ NAME 'cOMProgID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:45
+ NAME 'homeDrive'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:580
+ NAME 'meetingIP'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1321
+ NAME 'aCSNonReservedMinPolicedSize'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:717
+ NAME 'dhcpState'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44' )
+#
+olcAttributeTypes: ( MSADat4:922
+ NAME 'mSMQLabel'
+ SYNTAX 'MSADtelex'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:74
+ NAME 'maxPwdAge'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:78
+ NAME 'minPwdAge'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:689
+ NAME 'cRLObject'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:146
+ NAME 'objectSid'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:565
+ NAME 'meetingID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:620
+ NAME 'ipsecName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:48
+ NAME 'isDeleted'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:760
+ NAME 'aCSAggregateTokenRatePerUser'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:623
+ NAME 'ipsecData'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:668
+ NAME 'domainCAs'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:687
+ NAME 'cAConnect'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:238
+ NAME 'printMaxResolutionSupported'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:700
+ NAME 'dhcpFlags'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:402
+ NAME 'helpData16'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:653
+ NAME 'managedBy'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:9
+ NAME 'helpData32'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:944
+ NAME 'mSMQSite2'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:943
+ NAME 'mSMQSite1'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:677
+ NAME 'replTopologyStayOfExecution'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:912
+ NAME 'allowedChildClassesEffective'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:231
+ NAME 'oMSyntax'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:231
+ NAME 'priority'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:48
+ NAME 'keywords'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:946
+ NAME 'mSMQCost'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:821
+ NAME 'siteList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:145
+ NAME 'revision'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:91
+ NAME 'repsFrom'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:645
+ NAME 'userCert'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:951
+ NAME 'mSMQQMID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:228
+ NAME 'portName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:859
+ NAME 'netbootLocallyInstalledOSes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:261
+ NAME 'division'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:902
+ NAME 'aCSMaxSizeOfRSVPAccountFile'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:699
+ NAME 'dhcpType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:301
+ NAME 'wbemPath'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:362
+ NAME 'siteGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:26
+ NAME 'rDNAttID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:900
+ NAME 'aCSRSVPAccountFilesLocation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1226
+ NAME 'mSMQDependentClientServices'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:222
+ NAME 'location'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:874
+ NAME 'fRSFlags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:219
+ NAME 'iconPath'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:688
+ NAME 'cAWEBURL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:716
+ NAME 'mscopeId'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:660
+ NAME 'treeName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:211
+ NAME 'schedule'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:557
+ NAME 'parentCA'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:249
+ NAME 'cOMCLSID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:675
+ NAME 'catalogs'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:102
+ NAME 'memberOf'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:690
+ NAME 'cAUsages'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:706
+ NAME 'dhcpMask'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44' )
+#
+olcAttributeTypes: ( MSADat4:511
+ NAME 'flatName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:686
+ NAME 'domainID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:58
+ NAME 'localeID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' )
+#
+olcAttributeTypes: ( MSADat4:16
+ NAME 'codePage'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:768
+ NAME 'aCSEnableRSVPMessageLogging'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:240
+ NAME 'printOrientationsSupported'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:883
+ NAME 'msRRASVendorAttributeEntry'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:1246
+ NAME 'interSiteTopologyGenerator'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:307
+ NAME 'options'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:28
+ NAME 'dnsRoot'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:887
+ NAME 'iPSECNegotiationPolicyType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1392
+ NAME 'mS-SQL-InformationDirectory'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:365
+ NAME 'operatingSystemServicePack'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:88
+ NAME 'nextRid'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:865
+ NAME 'pekList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:7
+ NAME 'subRefs'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:505
+ NAME 'oMTGuid'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:205
+ NAME 'pKTGuid'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:146
+ NAME 'company'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:82
+ NAME 'moniker'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:156
+ NAME 'comment'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:721
+ NAME 'ipPhone'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1361
+ NAME 'mS-DS-ConsistencyChildCount'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:679
+ NAME 'creator'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:137
+ NAME 'uNCName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:55
+ NAME 'dBCSPwd'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1239
+ NAME 'mSMQDependentClientService'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:684
+ NAME 'certificateAuthorityObject'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:621
+ NAME 'ipsecID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:914
+ NAME 'allowedAttributesEffective'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+#
+#olcAttributeTypes: ( MSADat2:598
+# NAME 'dmdName'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:759
+ NAME 'aCSMaxPeakBandwidthPerFlow'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:557
+ NAME 'Enabled'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:326
+ NAME 'perRecipDialogDisplayTable'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1248
+ NAME 'interSiteTopologyFailover'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:895
+ NAME 'transportAddressAttribute'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:852
+ NAME 'netbootCurrentClientCount'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:372
+ NAME 'rIDPreviousAllocationPool'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:83
+ NAME 'repsTo'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:224
+ NAME 'defaultSecurityDescriptor'
+ SYNTAX 'MSADsecDesc'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:519
+ NAME 'lastBackupRestorationTime'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:873
+ NAME 'fRSControlOutboundBacklog'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:255
+ NAME 'vendor'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:891
+ NAME 'gPLink'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:214
+ NAME 'originalDisplayTableMSDOS'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:50
+ NAME 'linkID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1130
+ NAME 'msNPSavedCallingStationID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:49
+ NAME 'mAPIID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:510
+ NAME 'serviceBindingInformation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:16
+ NAME 'nCName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1303
+ NAME 'tokenGroupsNoGCAcceptable'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:1418
+ NAME 'tokenGroupsGlobalAndUniversal'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:1190
+ NAME 'msRASSavedFramedIPAddress'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:766
+ NAME 'aCSAllocableRSVPBandwidth'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:61
+ NAME 'lockOutObservationWindow'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:857
+ NAME 'netbootIntelliMirrorOSes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:1320
+ NAME 'aCSNonReservedMaxSDUSize'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:265
+ NAME 'notes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:673
+ NAME 'retiredReplDSASignatures'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1313
+ NAME 'aCSMaxTokenBucketPerFlow'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:324
+ NAME 'addressEntryDisplayTable'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1317
+ NAME 'aCSMinimumDelayVariation'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:872
+ NAME 'fRSControlInboundBacklog'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:38
+ NAME 'flags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1399
+ NAME 'mS-SQL-LastDiagnosticDate'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1348
+ NAME 'gPCMachineExtensionNames'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1411
+ NAME 'ms-DS-MachineAccountQuota'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:325
+ NAME 'perMsgDialogDisplayTable'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:57
+ NAME 'defaultLocalPolicyObject'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1189
+ NAME 'msRASSavedCallbackNumber'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.26'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:685
+ NAME 'parentCACertificateChain'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:893
+ NAME 'gPCFunctionalityVersion'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:879
+ NAME 'fRSServiceCommandStatus'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1319
+ NAME 'aCSNonReservedTokenSize'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:775
+ NAME 'aCSMaxSizeOfRSVPLogFile'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:135
+ NAME 'cost'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:81
+ NAME 'modifiedCountAtLastProm'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:773
+ NAME 'aCSRSVPLogFilesLocation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+#olcAttributeTypes: ( MSADat2:81
+# NAME 'info'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:125
+ NAME 'supplementalCredentials'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:819
+ NAME 'bridgeheadTransportList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:967
+ NAME 'mSMQSignCertificatesMig'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+## NO-USER-MODIFICATION
+#olcAttributeTypes: ( MSADat4:1
+# NAME 'name'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1153
+ NAME 'msRADIUSFramedIPAddress'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1408
+ NAME 'mS-DS-ReplicatesNCReason'
+ SYNTAX 'MSADdnWithOctetString' )
+#
+olcAttributeTypes: ( MSADat4:899
+ NAME 'aCSEnableRSVPAccounting'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+#olcAttributeTypes: ( MSADat4:881
+# NAME 'fRSTimeLastConfigChange'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.53'
+# SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:281
+ NAME 'printStaplingSupported'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1247
+ NAME 'interSiteTopologyRenew'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:364
+ NAME 'operatingSystemVersion'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:91
+ NAME 'otherLoginWorkstations'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:849
+ NAME 'netbootAllowNewClients'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1372
+ NAME 'mS-SQL-UnicodeSortOrder'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:749
+ NAME 'url'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:206
+ NAME 'pKT'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:199
+ NAME 'serviceInstanceVersion'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:169
+ NAME 'showInAdvancedViewOnly'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:758
+ NAME 'aCSMaxTokenRatePerFlow'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:868
+ NAME 'isCriticalSystemObject'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:576
+ NAME 'meetingMaxParticipants'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1208
+ NAME 'aNR'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:153
+ NAME 'rid'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:523
+ NAME 'proxyGenerationEnabled'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:871
+ NAME 'fRSControlDataCreation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:692
+ NAME 'previousCACertificates'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:24
+ NAME 'contentIndexingAllowed'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:633
+ NAME 'policyReplicationFlags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:870
+ NAME 'frsComputerReferenceBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:1318
+ NAME 'aCSNonReservedPeakRate'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:901
+ NAME 'aCSMaxNoOfAccountFiles'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:514
+ NAME 'physicalLocationObject'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:928
+ NAME 'mSMQOutRoutingServers'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:820
+ NAME 'bridgeheadServerListBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:1145
+ NAME 'msRADIUSCallbackNumber'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.26'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:361
+ NAME 'netbootMachineFilePath'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:963
+ NAME 'mSMQQueueJournalQuota'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:853
+ NAME 'netbootAnswerRequests'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:415
+ NAME 'operatingSystemHotfix'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:149
+ NAME 'attributeSecurityGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:711
+ NAME 'superScopeDescription'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:1359
+ NAME 'otherWellKnownObjects'
+ SYNTAX 'MSADdnWithOctetString' )
+#
+olcAttributeTypes: ( MSADat4:780
+ NAME 'aCSNonReservedTxLimit'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:11
+ NAME 'authenticationOptions'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:867
+ NAME 'altSecurityIdentities'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:1349
+ NAME 'gPCUserExtensionNames'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:358
+ NAME 'netbootInitialization'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1364
+ NAME 'mS-SQL-RegisteredOwner'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:761
+ NAME 'aCSMaxDurationPerFlow'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1330
+ NAME 'pKICriticalExtensions'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:748
+ NAME 'attributeDisplayNames'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:1404
+ NAME 'mS-SQL-AllowImmediateUpdatingSubscription'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1191
+ NAME 'msRASSavedFramedRoute'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
+#
+olcAttributeTypes: ( MSADat4:752
+ NAME 'userSharedFolderOther'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+#olcAttributeTypes: ( MSADat2:131
+# NAME 'co'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:909
+ NAME 'extendedAttributeInfo'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:1241
+ NAME 'netbootMirrorDataFile'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:1315
+ NAME 'aCSMinimumPolicedSize'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1353
+ NAME 'localizationDisplayId'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:582
+ NAME 'meetingAdvertiseScope'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1343
+ NAME 'dSUIAdminNotification'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:1381
+ NAME 'mS-SQL-LastUpdatedDate'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:1357
+ NAME 'dSCorePropagationData'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.24' )
+#
+olcAttributeTypes: ( MSADat4:320
+ NAME 'implementedCategories'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcAttributeTypes: ( MSADat4:783
+ NAME 'defaultObjectCategory'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:422
+ NAME 'domainPolicyReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:929
+ NAME 'mSMQInRoutingServers'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:1311
+ NAME 'printDuplexSupported'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:693
+ NAME 'pendingCACertificates'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:281
+ NAME 'nTSecurityDescriptor'
+ SYNTAX 'MSADsecDesc'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:198
+ NAME 'systemAuxiliaryClass'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+#
+olcAttributeTypes: ( MSADat4:898
+ NAME 'aCSNonReservedTxSize'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1382
+ NAME 'mS-SQL-InformationURL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:3
+ NAME 'replPropertyMetaData'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1384
+ NAME 'mS-SQL-PublicationURL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:275
+ NAME 'printKeepPrintedJobs'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:267
+ NAME 'uSNDSALastObjRemoved'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:381
+ NAME 'dnsNotifySecondaries'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' )
+#
+olcAttributeTypes: ( MSADat4:1360
+ NAME 'mS-DS-ConsistencyGuid'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:869
+ NAME 'frsComputerReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1369
+ NAME 'mS-SQL-ServiceAccount'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1124
+ NAME 'msNPCallingStationID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
+#
+olcAttributeTypes: ( MSADat4:947
+ NAME 'mSMQSignCertificates'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:624
+ NAME 'ipsecOwnersReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:14
+ NAME 'builtinModifiedCount'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:634
+ NAME 'privilegeDisplayName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:380
+ NAME 'dnsSecureSecondaries'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' )
+#
+olcAttributeTypes: ( MSADat4:817
+ NAME 'localizedDescription'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:195
+ NAME 'systemPossSuperiors'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+#
+olcAttributeTypes: ( MSADat2:353
+ NAME 'displayNamePrintable'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:771
+ NAME 'servicePrincipalName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:866
+ NAME 'pekKeyChangeInterval'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat2:445
+ NAME 'originalDisplayTable'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1398
+ NAME 'mS-SQL-LastBackupDate'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:517
+ NAME 'ipsecPolicyReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:823
+ NAME 'certificateTemplates'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:15
+ NAME 'hasPartialReplicaNCs'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:457
+ NAME 'localPolicyReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat2:380
+ NAME 'extendedCharsAllowed'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:629
+ NAME 'ipsecFilterReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:626
+ NAME 'ipsecISAKMPReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:876
+ NAME 'fRSMemberReferenceBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:314
+ NAME 'rpcNsTransferSyntax'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1227
+ NAME 'mSMQRoutingServices'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1375
+ NAME 'mS-SQL-MultiProtocol'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:825
+ NAME 'enrollmentProviders'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:287
+ NAME 'printNetworkAddress'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1171
+ NAME 'msRADIUSServiceType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:631
+ NAME 'printPagesPerMinute'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:299
+ NAME 'printMediaSupported'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+#
+olcAttributeTypes: ( MSADat4:824
+ NAME 'signatureAlgorithms'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:877
+ NAME 'fRSPartnerAuthLevel'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:636
+ NAME 'privilegeAttributes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:640
+ NAME 'partialAttributeSet'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:850
+ NAME 'netbootLimitClients'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1383
+ NAME 'mS-SQL-ConnectionURL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1406
+ NAME 'mS-SQL-AllowSnapshotFilesFTPDownloading'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1331
+ NAME 'pKIExpirationPeriod'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+#
+# NO-USER-MODIFICATION
+olcAttributeTypes: ( MSADat4:531
+ NAME 'nonSecurityMemberBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+#
+olcAttributeTypes: ( MSADat4:540
+ NAME 'initialAuthOutgoing'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+#
+olcAttributeTypes: ( MSADat4:1158
+ NAME 'msRADIUSFramedRoute'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
+#
+olcAttributeTypes: ( MSADat4:200
+ NAME 'controlAccessRights'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+#
+olcObjectClasses: ( MSADoc5:0 NAME 'mstop' SUP top ABSTRACT
+ MUST (objectClass $ instanceType
+ $ nTSecurityDescriptor $ objectCategory ) MAY (cn $ description $ distinguishedName
+ $ whenCreated $ whenChanged $ subRefs $ displayName $ uSNCreated $ isDeleted
+ $ dSASignature $ objectVersion $ repsTo $ repsFrom $ memberOf $ ownerBL
+ $ uSNChanged $ uSNLastObjRem $ showInAdvancedViewOnly $ adminDisplayName
+ $ proxyAddresses $ adminDescription $ extensionName $ uSNDSALastObjRemoved
+ $ displayNamePrintable $ directReports $ wWWHomePage $ USNIntersite $ name
+ $ objectGUID $ replPropertyMetaData $ replUpToDateVector $ flags $ revision
+ $ wbemPath $ fSMORoleOwner $ systemFlags $ siteObjectBL $ serverReferenceBL
+ $ nonSecurityMemberBL $ queryPolicyBL $ wellKnownObjects $ isPrivilegeHolder
+ $ partialAttributeSet $ managedObjects $ partialAttributeDeletionList $ url
+ $ lastKnownParent $ bridgeheadServerListBL $ netbootSCPBL $ isCriticalSystemObject
+ $ frsComputerReferenceBL $ fRSMemberReferenceBL $ uSNSource $ fromEntry
+ $ allowedChildClasses $ allowedChildClassesEffective $ allowedAttributes
+ $ allowedAttributesEffective $ possibleInferiors $ canonicalName $ proxiedObjectName
+ $ sDRightsEffective $ dSCorePropagationData $ otherWellKnownObjects
+ $ mS-DS-ConsistencyGuid $ mS-DS-ConsistencyChildCount $ masteredBy
+ $ msCOM-PartitionSetLink $ msCOM-UserLink $ msDS-Approx-Immed-Subordinates
+ $ msDS-NCReplCursors $ msDS-NCReplInboundNeighbors $ msDS-NCReplOutboundNeighbors
+ $ msDS-ReplAttributeMetaData $ msDS-ReplValueMetaData $ msDS-NonMembersBL
+ $ msDS-MembersForAzRoleBL $ msDS-OperationsForAzTaskBL $ msDS-TasksForAzTaskBL
+ $ msDS-OperationsForAzRoleBL $ msDS-TasksForAzRoleBL $ msDs-masteredBy
+ $ msDS-ObjectReferenceBL $ msDS-PrincipalName $ msDS-RevealedDSAs $ msDS-KrbTgtLinkBl
+ $ msDS-IsFullReplicaFor $ msDS-IsDomainFor $ msDS-IsPartialReplicaFor
+ $ msDS-AuthenticatedToAccountlist $ msDS-NC-RO-Replica-Locations-BL $ msDS-RevealedListBL
+ $ msDS-PSOApplied $ msDS-NcType $ msDS-OIDToGroupLinkBl $ msDS-HostServiceAccountBL
+ $ isRecycled $ msDS-LocalEffectiveDeletionTime $ msDS-LocalEffectiveRecycleTime
+ $ msDS-LastKnownRDN $ msDS-EnabledFeatureBL $ msDS-ClaimSharesPossibleValuesWithBL
+ $ msDS-MembersOfResourcePropertyListBL $ msDS-IsPrimaryComputerFor
+ $ msDS-ValueTypeReferenceBL $ msDS-TDOIngressBL $ msDS-TDOEgressBL $ msDS-parentdistname
+ $ msDS-ReplValueMetaDataExt $ msds-memberOfTransitive $ msds-memberTransitive
+ $ msSFU30PosixMemberOf $ msDFSR-MemberReferenceBL $ msDFSR-ComputerReferenceBL ) )
+olcObjectClasses: ( MSADoc5:8 NAME 'group' SUP mstop STRUCTURAL
+ MUST (groupType ) MAY (member $ nTGroupMembers $ operatorCount $ adminCount
+ $ groupAttributes $ groupMembershipSAM $ controlAccessRights $ desktopProfile
+ $ nonSecurityMember $ managedBy $ primaryGroupToken $ msDS-AzLDAPQuery
+ $ msDS-NonMembers $ msDS-AzBizRule $ msDS-AzBizRuleLanguage $ msDS-AzLastImportedBizRulePath
+ $ msDS-AzApplicationData $ msDS-AzObjectGuid $ msDS-AzGenericData
+ $ msDS-PrimaryComputer $ mail $ msSFU30Name $ msSFU30NisDomain $ msSFU30PosixMember ) )
+olcObjectClasses: ( MSADoc5:9 NAME 'user'
+ SUP ( mstop $ organizationalPerson ) STRUCTURAL
+ MAY (o $ businessCategory $ userCertificate $ givenName $ initials
+ $ x500uniqueIdentifier $ displayName $ networkAddress $ employeeNumber
+ $ employeeType $ homePostalAddress $ userAccountControl $ badPwdCount
+ $ codePage $ homeDirectory $ homeDrive $ badPasswordTime $ lastLogoff
+ $ lastLogon $ dBCSPwd $ localeID $ scriptPath $ logonHours $ logonWorkstation
+ $ maxStorage $ userWorkstations $ unicodePwd $ otherLoginWorkstations
+ $ ntPwdHistory $ pwdLastSet $ preferredOU $ primaryGroupID $ userParameters
+ $ profilePath $ operatorCount $ adminCount $ accountExpires $ lmPwdHistory
+ $ groupMembershipSAM $ logonCount $ controlAccessRights $ defaultClassStore
+ $ groupsToIgnore $ groupPriority $ desktopProfile $ dynamicLDAPServer
+ $ userPrincipalName $ lockoutTime $ userSharedFolder $ userSharedFolderOther
+ $ servicePrincipalName $ aCSPolicyName $ terminalServer $ mSMQSignCertificates
+ $ mSMQDigests $ mSMQDigestsMig $ mSMQSignCertificatesMig $ msNPAllowDialin
+ $ msNPCallingStationID $ msNPSavedCallingStationID $ msRADIUSCallbackNumber
+ $ msRADIUSFramedIPAddress $ msRADIUSFramedRoute $ msRADIUSServiceType
+ $ msRASSavedCallbackNumber $ msRASSavedFramedIPAddress
+ $ msRASSavedFramedRoute $ mS-DS-CreatorSID $ msCOM-UserPartitionSetLink
+ $ msDS-Cached-Membership $ msDS-Cached-Membership-Time-Stamp
+ $ msDS-Site-Affinity $ msDS-User-Account-Control-Computed $ lastLogonTimestamp
+ $ msIIS-FTPRoot $ msIIS-FTPDir $ msDRM-IdentityCertificate $ msDS-SourceObjectDN
+ $ msPKIRoamingTimeStamp $ msPKIDPAPIMasterKeys $ msPKIAccountCredentials
+ $ msRADIUS-FramedInterfaceId $ msRADIUS-SavedFramedInterfaceId $ msRADIUS-FramedIpv6Prefix
+ $ msRADIUS-SavedFramedIpv6Prefix $ msRADIUS-FramedIpv6Route $ msRADIUS-SavedFramedIpv6Route
+ $ msDS-SecondaryKrbTgtNumber $ msDS-AuthenticatedAtDC $ msDS-SupportedEncryptionTypes
+ $ msDS-LastSuccessfulInteractiveLogonTime $ msDS-LastFailedInteractiveLogonTime
+ $ msDS-FailedInteractiveLogonCount $ msDS-FailedInteractiveLogonCountAtLastSuccessfulLogon
+ $ msTSProfilePath $ msTSHomeDirectory $ msTSHomeDrive $ msTSAllowLogon
+ $ msTSRemoteControl $ msTSMaxDisconnectionTime $ msTSMaxConnectionTime
+ $ msTSMaxIdleTime $ msTSReconnectionAction $ msTSBrokenConnectionAction
+ $ msTSConnectClientDrives $ msTSConnectPrinterDrives $ msTSDefaultToMainPrinter
+ $ msTSWorkDirectory $ msTSInitialProgram $ msTSProperty01 $ msTSProperty02
+ $ msTSExpireDate $ msTSLicenseVersion $ msTSManagingLS
+ $ msDS-UserPasswordExpiryTimeComputed $ msTSExpireDate2 $ msTSLicenseVersion2
+ $ msTSManagingLS2 $ msTSExpireDate3 $ msTSLicenseVersion3 $ msTSManagingLS3
+ $ msTSExpireDate4 $ msTSLicenseVersion4 $ msTSManagingLS4 $ msTSLSProperty01
+ $ msTSLSProperty02 $ msDS-ResultantPSO $ msPKI-CredentialRoamingTokens
+ $ msTSPrimaryDesktop $ msTSSecondaryDesktops $ msDS-PrimaryComputer $ msDS-SyncServerUrl
+ $ msDS-AssignedAuthNPolicySilo $ msDS-AuthNPolicySiloMembersBL
+ $ msDS-AssignedAuthNPolicy $ userSMIMECertificate $ uid $ mail $ roomNumber
+ $ photo $ manager $ homePhone $ secretary $ mobile $ pager $ audio $ jpegPhoto
+ $ carLicense $ departmentNumber $ preferredLanguage $ userPKCS12 $ labeledURI
+ $ msSFU30Name $ msSFU30NisDomain ) )
+#objectclass ( MSADoc3:23 NAME 'container' SUP mstop STRUCTURAL
+# MUST (cn ) MAY (schemaVersion $ defaultClassStore $ msDS-ObjectReference ) )
+olcObjectClasses: ( 1.2.840.113556.1.3.30 NAME 'computer' SUP user STRUCTURAL
+ MAY (cn $ networkAddress $ localPolicyFlags $ defaultLocalPolicyObject $ machineRole
+ $ location $ netbootInitialization $ netbootGUID $ netbootMachineFilePath
+ $ siteGUID $ operatingSystem $ operatingSystemVersion $ operatingSystemServicePack
+ $ operatingSystemHotfix $ volumeCount $ physicalLocationObject $ dNSHostName
+ $ policyReplicationFlags $ managedBy $ rIDSetReferences $ catalogs
+ $ netbootSIFFile $ netbootMirrorDataFile $ msDS-AdditionalDnsHostName
+ $ msDS-AdditionalSamAccountName $ msDS-ExecuteScriptPassword $ msDS-KrbTgtLink
+ $ msDS-RevealedUsers $ msDS-NeverRevealGroup $ msDS-RevealOnDemandGroup
+ $ msDS-RevealedList $ msDS-AuthenticatedAtDC $ msDS-isGC $ msDS-isRODC $ msDS-SiteName
+ $ msDS-PromotionSettings $ msTPM-OwnerInformation $ msTSProperty01 $ msTSProperty02
+ $ msDS-IsUserCachableAtRodc $ msDS-HostServiceAccount $ msTSEndpointData
+ $ msTSEndpointType $ msTSEndpointPlugin $ msTSPrimaryDesktopBL $ msTSSecondaryDesktopBL
+ $ msTPM-TpmInformationForComputer $ msDS-GenerationId $ msImaging-ThumbprintHash
+ $ msImaging-HashAlgorithm $ netbootDUID $ msSFU30Name $ msSFU30Aliases $ msSFU30NisDomain $ nisMapName ) )
diff --git a/servers/slapd/schema/msuser.schema b/servers/slapd/schema/msuser.schema
new file mode 100644
index 0000000..4cce48f
--- /dev/null
+++ b/servers/slapd/schema/msuser.schema
@@ -0,0 +1,4295 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2018-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+# This file is provided for informational purposes only.
+
+# These definitions are from Microsoft's Active Directory.
+# They were first gathered using the subschemaSubentry object of
+# Windows 2000 Advanced Server Release Candidate 2.
+# Additional definitions were gathered from Windows Server 2012
+# and adjusted to ease compatibility issues.
+
+# These object classes and attributes are rooted at OID
+# 1.2.840.113556.1, Microsoft's Directory Enabled networks
+# OID. OID Macros are used throughout, to make mapping of
+# incompatible OIDs easier.
+
+# A number of custom syntaxes have been replaced with generic
+# syntaxes. The original syntax OIDs are commented out below.
+
+# Many of these attributes are defined with NO-USER-MODIFICATION,
+# but slapd won't load such definitions from user-modifiable schema
+# files. So that designation has been removed, and commented accordingly.
+
+# Only the subset of Windows 2012 attributes needed to make the
+# user and group objectclasses work has been added to the previously
+# retrieved definitions.
+
+# If an AD definition clashes with existing standardized schema,
+# the AD definition has been commented out.
+
+objectidentifier MicrosoftRoot 1.2.840.113556
+objectidentifier MSAD MicrosoftRoot:1
+objectidentifier MSADat2 MSAD:2
+objectidentifier MSADat4 MSAD:4
+objectidentifier MSADat6 MSAD:6
+objectidentifier MSADoc3 MSAD:3
+objectidentifier MSADoc5 MSAD:5
+
+#objectidentifier MSADdnWithOctetString MSADat4:903
+#objectidentifier MSADdnWithString MSADat4:904
+#objectidentifier MSADtelex MSADat4:905
+#objectidentifier MSADlargeInteger MSADat4:906
+#objectidentifier MSADcis MSADat4:907
+#objectidentifier MSADsecDesc MSADat4:907
+objectidentifier MSADdnWithOctetString 1.3.6.1.4.1.1466.115.121.1.40
+objectidentifier MSADdnWithString 1.3.6.1.4.1.1466.115.121.1.40
+objectidentifier MSADtelex 1.3.6.1.4.1.1466.115.121.1.44
+objectidentifier MSADlargeInteger 1.3.6.1.4.1.1466.115.121.1.27
+objectidentifier MSADcis 1.3.6.1.4.1.1466.115.121.1.44
+objectidentifier MSADsecDesc 1.3.6.1.4.1.1466.115.121.1.40
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:104
+ NAME 'ownerBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1424
+ NAME 'msCOM-PartitionSetLink'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1425
+ NAME 'msCOM-UserLink'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1669
+ NAME 'msDS-Approx-Immed-Subordinates'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1704
+ NAME 'msDS-NCReplCursors'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:1705
+ NAME 'msDS-NCReplInboundNeighbors'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:1706
+ NAME 'msDS-NCReplOutboundNeighbors'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:1707
+ NAME 'msDS-ReplAttributeMetaData'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:1708
+ NAME 'msDS-ReplValueMetaData'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:1793
+ NAME 'msDS-NonMembers'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1794
+ NAME 'msDS-NonMembersBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:1806
+ NAME 'msDS-MembersForAzRole'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1807
+ NAME 'msDS-MembersForAzRoleBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:1808 NAME 'msDS-OperationsForAzTask' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1809 NAME 'msDS-OperationsForAzTaskBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+attributetype ( MSADat4:1810 NAME 'msDS-TasksForAzTask' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1811 NAME 'msDS-TasksForAzTaskBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+attributetype ( MSADat4:1812 NAME 'msDS-OperationsForAzRole' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1813 NAME 'msDS-OperationsForAzRoleBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+attributetype ( MSADat4:1814 NAME 'msDS-TasksForAzRole' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1815 NAME 'msDS-TasksForAzRoleBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1837 NAME 'msDs-masteredBy' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+attributeType ( MSADat4:1840 NAME 'msDS-ObjectReference' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+attributeType ( MSADat4:1841 NAME 'msDS-ObjectReferenceBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributeType ( MSADat4:1865 NAME 'msDS-PrincipalName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributeType ( MSADat4:1930 NAME 'msDS-RevealedDSAs' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+attributeType ( MSADat4:1931 NAME 'msDS-KrbTgtLinkBl' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+# NO-USER-MODIFICATION
+attributeType ( MSADat4:1932 NAME 'msDS-IsFullReplicaFor' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+attributeType ( MSADat4:1933 NAME 'msDS-IsDomainFor' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+attributeType ( MSADat4:1934 NAME 'msDS-IsPartialReplicaFor' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+# NO-USER-MODIFICATION
+attributeType ( MSADat4:1957 NAME 'msDS-AuthenticatedToAccountlist' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+attributeType ( MSADat4:1958 NAME 'msDS-AuthenticatedAtDC' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+# NO-USER-MODIFICATION
+attributeType ( MSADat4:1975 NAME 'msDS-RevealedListBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:1968 NAME 'msDS-NC-RO-Replica-Locations-BL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:2021 NAME 'msDS-PSOApplied' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:2024 NAME 'msDS-NcType' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:2052 NAME 'msDS-OIDToGroupLinkBl' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+attributetype ( MSADat4:2057 NAME 'msDS-HostServiceAccountBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:2058 NAME 'isRecycled' SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:2059 NAME 'msDS-LocalEffectiveDeletionTime' SYNTAX '1.3.6.1.4.1.1466.115.121.1.24' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:2060 NAME 'msDS-LocalEffectiveRecycleTime' SYNTAX '1.3.6.1.4.1.1466.115.121.1.24' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:2067 NAME 'msDS-LastKnownRDN' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:2069 NAME 'msDS-EnabledFeatureBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+attributetype ( MSADat4:2102 NAME 'msDS-ClaimSharesPossibleValuesWithBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+attributetype ( MSADat4:2104 NAME 'msDS-MembersOfResourcePropertyListBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+attributetype ( MSADat4:2168 NAME 'msDS-IsPrimaryComputerFor' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:2188 NAME 'msDS-ValueTypeReferenceBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:2193 NAME 'msDS-TDOIngressBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:2194 NAME 'msDS-TDOEgressBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:2203 NAME 'msDS-parentdistname' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )
+attributetype ( MSADat4:2235 NAME 'msDS-ReplValueMetaDataExt' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:2236 NAME 'msds-memberOfTransitive' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:2238 NAME 'msds-memberTransitive' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+attributetype ( MSADat6:18.1.347 NAME 'msSFU30PosixMemberOf' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+attributetype ( MSADat6:13.3.102 NAME 'msDFSR-MemberReferenceBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+attributetype ( MSADat6:13.3.103 NAME 'msDFSR-ComputerReferenceBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+
+attributetype ( MSADat4:1792 NAME 'msDS-AzLDAPQuery' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:1801 NAME 'msDS-AzBizRule' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:1802 NAME 'msDS-AzBizRuleLanguage' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:1803 NAME 'msDS-AzLastImportedBizRulePath' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:1819 NAME 'msDS-AzApplicationData' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1949 NAME 'msDS-AzObjectGuid' SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' SINGLE-VALUE )
+attributetype ( MSADat4:1950 NAME 'msDS-AzGenericData' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:2167 NAME 'msDS-PrimaryComputer' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+attributetype ( MSADat6:18.1.309 NAME 'msSFU30Name' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
+attributetype ( MSADat6:18.1.339 NAME 'msSFU30NisDomain' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
+attributetype ( MSADat6:18.1.346 NAME 'msSFU30PosixMember' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:1426 NAME 'msCOM-UserPartitionSetLink' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )
+attributetype ( MSADat4:1441 NAME 'msDS-Cached-Membership' SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' SINGLE-VALUE )
+attributetype ( MSADat4:1442 NAME 'msDS-Cached-Membership-Time-Stamp' SYNTAX 'MSADlargeInteger' SINGLE-VALUE )
+attributetype ( MSADat4:1443 NAME 'msDS-Site-Affinity' SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+attributetype ( MSADat4:1460 NAME 'msDS-User-Account-Control-Computed' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+attributetype ( MSADat4:1696 NAME 'lastLogonTimestamp' SYNTAX 'MSADlargeInteger' SINGLE-VALUE )
+attributetype ( MSADat4:1785 NAME 'msIIS-FTPRoot' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:1786 NAME 'msIIS-FTPDir' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:1843 NAME 'msDRM-IdentityCertificate' SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+attributetype ( MSADat4:1879 NAME 'msDS-SourceObjectDN' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:1892 NAME 'msPKIRoamingTimeStamp' SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' SINGLE-VALUE )
+attributetype ( MSADat4:1893 NAME 'msPKIDPAPIMasterKeys' SYNTAX 'MSADdnWithOctetString' )
+attributetype ( MSADat4:1894 NAME 'msPKIAccountCredentials' SYNTAX 'MSADdnWithOctetString' )
+attributetype ( MSADat4:1913 NAME 'msRADIUS-FramedInterfaceId' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
+attributetype ( MSADat4:1914 NAME 'msRADIUS-SavedFramedInterfaceId' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
+attributetype ( MSADat4:1915 NAME 'msRADIUS-FramedIpv6Prefix' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
+attributetype ( MSADat4:1916 NAME 'msRADIUS-SavedFramedIpv6Prefix' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
+attributetype ( MSADat4:1917 NAME 'msRADIUS-FramedIpv6Route' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
+attributetype ( MSADat4:1918 NAME 'msRADIUS-SavedFramedIpv6Route' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1929 NAME 'msDS-SecondaryKrbTgtNumber' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+attributetype ( MSADat4:1963 NAME 'msDS-SupportedEncryptionTypes' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1970 NAME 'msDS-LastSuccessfulInteractiveLogonTime' SYNTAX 'MSADlargeInteger' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1971 NAME 'msDS-LastFailedInteractiveLogonTime' SYNTAX 'MSADlargeInteger' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1972 NAME 'msDS-FailedInteractiveLogonCount' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1973 NAME 'msDS-FailedInteractiveLogonCountAtLastSuccessfulLogon' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+attributetype ( MSADat4:1976 NAME 'msTSProfilePath' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:1977 NAME 'msTSHomeDirectory' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:1978 NAME 'msTSHomeDrive' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:1979 NAME 'msTSAllowLogon' SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )
+attributetype ( MSADat4:1980 NAME 'msTSRemoteControl' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+attributetype ( MSADat4:1981 NAME 'msTSMaxDisconnectionTime' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+attributetype ( MSADat4:1982 NAME 'msTSMaxConnectionTime' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+attributetype ( MSADat4:1983 NAME 'msTSMaxIdleTime' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+attributetype ( MSADat4:1984 NAME 'msTSReconnectionAction' SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )
+attributetype ( MSADat4:1985 NAME 'msTSBrokenConnectionAction' SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )
+attributetype ( MSADat4:1986 NAME 'msTSConnectClientDrives' SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )
+attributetype ( MSADat4:1987 NAME 'msTSConnectPrinterDrives' SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )
+attributetype ( MSADat4:1988 NAME 'msTSDefaultToMainPrinter' SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )
+attributetype ( MSADat4:1989 NAME 'msTSWorkDirectory' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:1990 NAME 'msTSInitialProgram' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:1991 NAME 'msTSProperty01' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+attributetype ( MSADat4:1992 NAME 'msTSProperty02' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+attributetype ( MSADat4:1993 NAME 'msTSExpireDate' SYNTAX '1.3.6.1.4.1.1466.115.121.1.24' SINGLE-VALUE )
+attributetype ( MSADat4:1994 NAME 'msTSLicenseVersion' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:1995 NAME 'msTSManagingLS' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:1996 NAME 'msDS-UserPasswordExpiryTimeComputed' SYNTAX 'MSADlargeInteger' SINGLE-VALUE )
+attributetype ( MSADat4:2008 NAME 'msTSManagingLS4' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:2005 NAME 'msTSManagingLS3' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:2002 NAME 'msTSManagingLS2' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:2006 NAME 'msTSExpireDate4' SYNTAX '1.3.6.1.4.1.1466.115.121.1.24' SINGLE-VALUE )
+attributetype ( MSADat4:2003 NAME 'msTSExpireDate3' SYNTAX '1.3.6.1.4.1.1466.115.121.1.24' SINGLE-VALUE )
+attributetype ( MSADat4:2000 NAME 'msTSExpireDate2' SYNTAX '1.3.6.1.4.1.1466.115.121.1.24' SINGLE-VALUE )
+attributetype ( MSADat4:2004 NAME 'msTSLicenseVersion3' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:2001 NAME 'msTSLicenseVersion2' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:2007 NAME 'msTSLicenseVersion4' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:2009 NAME 'msTSLSProperty01' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+attributetype ( MSADat4:2010 NAME 'msTSLSProperty02' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:2022 NAME 'msDS-ResultantPSO' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )
+attributetype ( MSADat4:2050 NAME 'msPKI-CredentialRoamingTokens' SYNTAX 'MSADdnWithOctetString' )
+attributetype ( MSADat4:2073 NAME 'msTSPrimaryDesktop' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )
+attributetype ( MSADat4:2075 NAME 'msTSSecondaryDesktops' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+attributetype ( MSADat4:2276 NAME 'msDS-SyncServerUrl' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+attributetype ( MSADat4:2285 NAME 'msDS-AssignedAuthNPolicySilo' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:2288 NAME 'msDS-AuthNPolicySiloMembersBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+attributetype ( MSADat4:2295 NAME 'msDS-AssignedAuthNPolicy' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )
+
+## NO-USER-MODIFICATION
+#attributetype ( MSADat4:1459 NAME 'msDS-Behavior-Version' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+#attributetype ( MSADat4:1788 NAME 'msDS-PerUserTrustQuota' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+#attributetype ( MSADat4:1789 NAME 'msDS-AllUsersTrustQuota' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+#attributetype ( MSADat4:1790 NAME 'msDS-PerUserTrustTombstonesQuota' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1717 NAME 'msDS-AdditionalDnsHostName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+## NO-USER-MODIFICATION
+attributetype ( MSADat4:1718 NAME 'msDS-AdditionalSamAccountName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1783 NAME 'msDS-ExecuteScriptPassword' SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' SINGLE-VALUE )
+attributetype ( MSADat4:1923 NAME 'msDS-KrbTgtLink' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1924 NAME 'msDS-RevealedUsers' SYNTAX 'MSADdnWithOctetString' )
+attributetype ( MSADat4:1926 NAME 'msDS-NeverRevealGroup' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+attributetype ( MSADat4:1928 NAME 'msDS-RevealOnDemandGroup' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1940 NAME 'msDS-RevealedList' SYNTAX 'MSADdnWithString' )
+attributetype ( MSADat4:1959 NAME 'msDS-isGC' SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )
+attributetype ( MSADat4:1960 NAME 'msDS-isRODC' SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' SINGLE-VALUE )
+attributetype ( MSADat4:1961 NAME 'msDS-SiteName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1962 NAME 'msDS-PromotionSettings' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:1966 NAME 'msTPM-OwnerInformation' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:2025 NAME 'msDS-IsUserCachableAtRodc' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+attributetype ( MSADat4:2056 NAME 'msDS-HostServiceAccount' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+attributetype ( MSADat4:2070 NAME 'msTSEndpointData' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:2071 NAME 'msTSEndpointType' SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' SINGLE-VALUE )
+attributetype ( MSADat4:2072 NAME 'msTSEndpointPlugin' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:2074 NAME 'msTSPrimaryDesktopBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:2078 NAME 'msTSSecondaryDesktopBL' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+attributetype ( MSADat4:2109 NAME 'msTPM-TpmInformationForComputer' SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' SINGLE-VALUE )
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:2166 NAME 'msDS-GenerationId' SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' SINGLE-VALUE )
+attributetype ( MSADat4:2180 NAME 'msImaging-ThumbprintHash' SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' SINGLE-VALUE )
+attributetype ( MSADat4:2181 NAME 'msImaging-HashAlgorithm' SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' SINGLE-VALUE )
+attributetype ( MSADat4:2234 NAME 'netbootDUID' SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' SINGLE-VALUE )
+attributetype ( MSADat6:18.1.323 NAME 'msSFU30Aliases' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
+#attributetype ( 1.3.6.1.1.1.1.26 NAME 'nisMapName' SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' SINGLE-VALUE )
+
+
+attributetype ( MSADat4:856
+ NAME 'netbootNewMachineOU'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:13
+ NAME 'builtinCreationTime'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1335
+ NAME 'pKIEnrollmentAccess'
+ SYNTAX 'MSADcis' )
+
+attributetype ( MSADat4:1333
+ NAME 'pKIExtendedKeyUsage'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:1123
+ NAME 'msNPCalledStationID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
+
+attributetype ( MSADat4:539
+ NAME 'initialAuthIncoming'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:370
+ NAME 'objectClassCategory'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:41
+ NAME 'generatedConnection'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:911
+ NAME 'allowedChildClasses'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+
+attributetype ( MSADat4:68
+ NAME 'machineArchitecture'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' )
+
+attributetype ( MSADat4:767
+ NAME 'aCSMaxPeakBandwidth'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:72
+ NAME 'marshalledInterface'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:368
+ NAME 'rIDManagerReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:770
+ NAME 'aCSEnableACSService'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1237
+ NAME 'mSMQRoutingService'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1405
+ NAME 'mS-SQL-AllowQueuedUpdatingSubscription'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:648
+ NAME 'primaryTelexNumber'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:8
+ NAME 'userAccountControl'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:563
+ NAME 'shellPropertyPages'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:4
+ NAME 'replUpToDateVector'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:484
+ NAME 'fRSDirectoryFilter'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:230
+ NAME 'printSeparatorFile'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1329
+ NAME 'pKIMaxIssuingDepth'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1307
+ NAME 'accountNameHistory'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:1386
+ NAME 'mS-SQL-GPSLongitude'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:562
+ NAME 'adminPropertyPages'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:121
+ NAME 'securityIdentifier'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:166
+ NAME 'groupMembershipSAM'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:659
+ NAME 'serviceDNSNameType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:585
+ NAME 'meetingIsEncrypted'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1400
+ NAME 'mS-SQL-Applications'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:330
+ NAME 'lastUpdateSequence'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:50
+ NAME 'lastContentIndexed'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:567
+ NAME 'meetingDescription'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+#attributetype ( MSADat4:880
+# NAME 'fRSTimeLastCommand'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.53'
+# SINGLE-VALUE )
+
+attributetype ( MSADat4:83
+ NAME 'monikerDisplayName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:321
+ NAME 'requiredCategories'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:813
+ NAME 'upgradeProductCode'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:774
+ NAME 'aCSMaxNoOfLogFiles'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1370
+ NAME 'mS-SQL-CharacterSet'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:578
+ NAME 'meetingContactInfo'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1397
+ NAME 'mS-SQL-CreationDate'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:32
+ NAME 'domainPolicyObject'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:703
+ NAME 'dhcpObjDescription'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:573
+ NAME 'meetingApplication'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:518
+ NAME 'defaultHidingValue'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:875
+ NAME 'fRSMemberReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:701
+ NAME 'dhcpIdentification'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:135
+ NAME 'trustAuthOutgoing'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:197
+ NAME 'systemMustContain'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1412
+ NAME 'primaryGroupToken'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:118
+ NAME 'rpcNsProfileEntry'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:129
+ NAME 'trustAuthIncoming'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1225
+ NAME 'mSMQPrevSiteGates'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:607
+ NAME 'queryPolicyObject'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:712
+ NAME 'optionDescription'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:1314
+ NAME 'aCSMaximumSDUSize'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:530
+ NAME 'nonSecurityMember'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:31
+ NAME 'fRSReplicaSetType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:763
+ NAME 'aCSTotalNoOfFlows'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:915
+ NAME 'possibleInferiors'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+
+attributetype ( MSADat4:851
+ NAME 'netbootMaxClients'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1385
+ NAME 'mS-SQL-GPSLatitude'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:765
+ NAME 'aCSPermissionBits'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:926
+ NAME 'mSMQTransactional'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1390
+ NAME 'mS-SQL-Description'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:913
+ NAME 'allowedAttributes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+
+attributetype ( MSADat4:491
+ NAME 'fRSFaultCondition'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:54
+ NAME 'tombstoneLifetime'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:809
+ NAME 'remoteStorageGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:644
+ NAME 'showInAddressBook'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:213
+ NAME 'defaultClassStore'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:577
+ NAME 'meetingOriginator'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:656
+ NAME 'userPrincipalName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1316
+ NAME 'aCSMinimumLatency'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+#attributetype ( MSADat2:617
+# NAME 'homePostalAddress'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:638
+ NAME 'isPrivilegeHolder'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:533
+ NAME 'fRSReplicaSetGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:371
+ NAME 'rIDAllocationPool'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1327
+ NAME 'pKIDefaultKeySpec'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:537
+ NAME 'dynamicLDAPServer'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:516
+ NAME 'serverReferenceBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:500
+ NAME 'fRSServiceCommand'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1304
+ NAME 'sDRightsEffective'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1249
+ NAME 'proxiedObjectName'
+ SYNTAX 'MSADdnWithOctetString'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:586
+ NAME 'meetingRecurrence'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:251
+ NAME 'cOMTreatAsClassId'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1245
+ NAME 'globalAddressList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:908
+ NAME 'extendedClassInfo'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:459
+ NAME 'machineWidePolicy'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:356
+ NAME 'foreignIdentifier'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1242
+ NAME 'dNReferenceUpdate'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:134
+ NAME 'trustPosixOffset'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:36
+ NAME 'enabledConnection'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:627
+ NAME 'ipsecNFAReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:86
+ NAME 'userWorkstations'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:301
+ NAME 'garbageCollPeriod'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:933
+ NAME 'mSMQComputerType'
+ SYNTAX 'MSADtelex'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:65
+ NAME 'logonWorkstation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:921
+ NAME 'mSMQJournalQuota'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:108
+ NAME 'remoteSourceType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:95
+ NAME 'pwdHistoryLength'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:920
+ NAME 'mSMQBasePriority'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:196
+ NAME 'systemMayContain'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+
+attributetype ( MSADat4:1407
+ NAME 'mS-SQL-ThirdParty'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1243
+ NAME 'mSMQQueueNameExt'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:485
+ NAME 'fRSUpdateTimeout'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:924
+ NAME 'mSMQPrivacyLevel'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:615
+ NAME 'shellContextMenu'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:618
+ NAME 'wellKnownObjects'
+ SYNTAX 'MSADdnWithOctetString' )
+
+attributetype ( MSADat4:789
+ NAME 'transportDLLName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:458
+ NAME 'qualityOfService'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:73
+ NAME 'lockoutThreshold'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:105
+ NAME 'remoteServerName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:694
+ NAME 'previousParentCA'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:1345
+ NAME 'dSUIShellMaximum'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:303
+ NAME 'notificationList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1244
+ NAME 'addressBookRoots'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:878
+ NAME 'fRSPrimaryMember'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+#attributetype ( MSADat4:587
+# NAME 'meetingStartTime'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.53' )
+
+attributetype ( MSADat4:1310
+ NAME 'mSMQSiteGatesMig'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:709
+ NAME 'dhcpReservations'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44' )
+
+attributetype ( MSADat4:614
+ NAME 'adminContextMenu'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:1332
+ NAME 'pKIOverlapPeriod'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:142
+ NAME 'winsockAddresses'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:923
+ NAME 'mSMQAuthenticate'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1344
+ NAME 'dSUIAdminMaximum'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:848
+ NAME 'appSchemaVersion'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:123
+ NAME 'serviceClassInfo'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:769
+ NAME 'aCSEventLogLevel'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:751
+ NAME 'userSharedFolder'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:421
+ NAME 'domainWidePolicy'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:669
+ NAME 'rIDSetReferences'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:815
+ NAME 'canUpgradeScript'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:610
+ NAME 'classDisplayName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat2:226
+ NAME 'adminDescription'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:67
+ NAME 'lSAModifiedCount'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:509
+ NAME 'serviceClassName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:56
+ NAME 'localPolicyFlags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:115
+ NAME 'rpcNsInterfaceID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:194
+ NAME 'adminDisplayName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:753
+ NAME 'nameServiceFlags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:589
+ NAME 'meetingBandwidth'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:755
+ NAME 'domainIdentifier'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:370
+ NAME 'rIDAvailablePool'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:655
+ NAME 'legacyExchangeDN'
+ SYNTAX 'MSADtelex'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:470
+ NAME 'trustAttributes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:535
+ NAME 'fRSRootSecurity'
+ SYNTAX 'MSADcis'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:532
+ NAME 'superiorDNSRoot'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:278
+ NAME 'printMaxYExtent'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:277
+ NAME 'printMaxXExtent'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:280
+ NAME 'printMinYExtent'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:279
+ NAME 'printMinXExtent'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:32
+ NAME 'attributeSyntax'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:247
+ NAME 'printAttributes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:152
+ NAME 'groupAttributes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:816
+ NAME 'fileExtPriority'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:930
+ NAME 'mSMQServiceType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:363
+ NAME 'operatingSystem'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1371
+ NAME 'mS-SQL-SortOrder'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:329
+ NAME 'versionNumberLo'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:884
+ NAME 'msRRASAttribute'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:781
+ NAME 'lastKnownParent'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1209
+ NAME 'shortServerName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:60
+ NAME 'lockoutDuration'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:232
+ NAME 'defaultPriority'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:754
+ NAME 'rpcNsEntryFlags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:713
+ NAME 'optionsLocation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44' )
+
+attributetype ( MSADat4:328
+ NAME 'versionNumberHi'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:366
+ NAME 'rpcNsAnnotation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:886
+ NAME 'purportedSearch'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:776
+ NAME 'aCSDSBMPriority'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:961
+ NAME 'mSMQSiteForeign'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' )
+
+attributetype ( MSADat4:335
+ NAME 'currentLocation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:570
+ NAME 'meetingProtocol'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:420
+ NAME 'publicKeyPolicy'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1402
+ NAME 'mS-SQL-Publisher'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:812
+ NAME 'createWizardExt'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:1373
+ NAME 'mS-SQL-Clustered'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:334
+ NAME 'volTableIdxGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:696
+ NAME 'currentParentCA'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:504
+ NAME 'seqNotification'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:515
+ NAME 'serverReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1119
+ NAME 'msNPAllowDialin'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1387
+ NAME 'mS-SQL-GPSHeight'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1378
+ NAME 'mS-SQL-AppleTalk'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:269
+ NAME 'linkTrackSecret'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:378
+ NAME 'dnsAllowDynamic'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:49
+ NAME 'badPasswordTime'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:637
+ NAME 'privilegeHolder'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:289
+ NAME 'printMediaReady'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:288
+ NAME 'printMACAddress'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:66
+ NAME 'lSACreationTime'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:569
+ NAME 'meetingLocation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:784
+ NAME 'aCSIdentityName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1410
+ NAME 'mS-DS-CreatorSID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1374
+ NAME 'mS-SQL-NamedPipe'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:843
+ NAME 'lDAPAdminLimits'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat2:460
+ NAME 'lDAPDisplayName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:218
+ NAME 'applicationName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:695
+ NAME 'pendingParentCA'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:779
+ NAME 'aCSCacheTimeout'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:574
+ NAME 'meetingLanguage'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:778
+ NAME 'aCSDSBMDeadTime'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:697
+ NAME 'cACertificateDN'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:138
+ NAME 'userParameters'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:132
+ NAME 'trustDirection'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:962
+ NAME 'mSMQQueueQuota'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:936
+ NAME 'mSMQEncryptKey'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:885
+ NAME 'terminalServer'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:233
+ NAME 'printStartTime'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:664
+ NAME 'syncWithObject'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:344
+ NAME 'groupsToIgnore'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:665
+ NAME 'syncMembership'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:666
+ NAME 'syncAttributes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:214
+ NAME 'nextLevelStore'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:302
+ NAME 'sAMAccountType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1401
+ NAME 'mS-SQL-Keywords'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat2:210
+ NAME 'proxyAddresses'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:284
+ NAME 'bytesPerMinute'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:241
+ NAME 'printMaxCopies'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:98
+ NAME 'primaryGroupID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:89
+ NAME 'nTGroupMembers'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:1228
+ NAME 'mSMQDsServices'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:43
+ NAME 'fRSVersionGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:486
+ NAME 'fRSWorkingPath'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:18
+ NAME 'otherTelephone'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat2:277
+ NAME 'otherHomePhone'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:151
+ NAME 'oEMInformation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:459
+ NAME 'networkAddress'
+ SYNTAX 'MSADtelex' )
+
+attributetype ( MSADat4:966
+ NAME 'mSMQDigestsMig'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:568
+ NAME 'meetingKeyword'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:844
+ NAME 'lDAPIPDenyList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:847
+ NAME 'installUiLevel'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:894
+ NAME 'gPCFileSysPath'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:488
+ NAME 'fRSStagingPath'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:351
+ NAME 'auxiliaryClass'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+
+attributetype ( MSADat4:159
+ NAME 'accountExpires'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:718
+ NAME 'dhcpProperties'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:346
+ NAME 'desktopProfile'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:762
+ NAME 'aCSServiceType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+#attributetype ( MSADat2:610
+# NAME 'employeeNumber'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+
+attributetype ( MSADat4:1213
+ NAME 'assocNTAccount'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:498
+ NAME 'creationWizard'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:253
+ NAME 'cOMOtherProgId'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:202
+ NAME 'auditingPolicy'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:635
+ NAME 'privilegeValue'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1366
+ NAME 'mS-SQL-Location'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1334
+ NAME 'pKIDefaultCSPs'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:270
+ NAME 'printShareName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:33
+ NAME 'isSingleValued'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:472
+ NAME 'domainCrossRef'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1240
+ NAME 'netbootSIFFile'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:250
+ NAME 'cOMUniqueLIBID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:657
+ NAME 'serviceDNSName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:782
+ NAME 'objectCategory'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:122
+ NAME 'serviceClassID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:720
+ NAME 'dhcpUpdateTime'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:221
+ NAME 'sAMAccountName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+#attributetype ( MSADat4:588
+# NAME 'meetingEndTime'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.53' )
+
+attributetype ( MSADat4:1389
+ NAME 'mS-SQL-Language'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:777
+ NAME 'aCSDSBMRefresh'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1393
+ NAME 'mS-SQL-Database'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:20
+ NAME 'cOMInterfaceID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:1403
+ NAME 'mS-SQL-AllowKnownPullSubscription'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1394
+ NAME 'mS-SQL-AllowAnonymousSubscription'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:654
+ NAME 'managedObjects'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat2:8
+ NAME 'possSuperiors'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+
+attributetype ( MSADat4:791
+ NAME 'transportType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:345
+ NAME 'groupPriority'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:117
+ NAME 'rpcNsPriority'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' )
+
+attributetype ( MSADat4:917
+ NAME 'mSMQQueueType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:141
+ NAME 'versionNumber'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:121
+ NAME 'uSNLastObjRem'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1346
+ NAME 'templateRoots'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:93
+ NAME 'pwdProperties'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:290
+ NAME 'printNumberUp'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:536
+ NAME 'fRSExtensions'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:286
+ NAME 'printRateUnit'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:846
+ NAME 'msiScriptSize'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:274
+ NAME 'printSpooling'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:608
+ NAME 'queryPolicyBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:103
+ NAME 'proxyLifetime'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:144
+ NAME 'operatorCount'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:860
+ NAME 'netbootServer'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:369
+ NAME 'fSMORoleOwner'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:276
+ NAME 'driverVersion'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1388
+ NAME 'mS-SQL-Version'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:939
+ NAME 'mSMQNameStyle'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:471
+ NAME 'schemaVersion'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:436
+ NAME 'directReports'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat2:255
+ NAME 'addressSyntax'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:235
+ NAME 'printFormName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:15
+ NAME 'msiScriptPath'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1312
+ NAME 'aCSServerList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+#attributetype ( MSADat2:615
+# NAME 'personalTitle'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+
+attributetype ( MSADat4:1305
+ NAME 'moveTreeState'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:945
+ NAME 'mSMQSiteGates'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:1238
+ NAME 'mSMQDsService'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:76
+ NAME 'objectVersion'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1414
+ NAME 'dNSTombstoned'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:941
+ NAME 'mSMQLongLived'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:534
+ NAME 'fRSLevelLimit'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:845
+ NAME 'msiScriptName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+#attributetype ( MSADat4:44
+# NAME 'homeDirectory'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+
+attributetype ( MSADat4:698
+ NAME 'dhcpUniqueKey'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:227
+ NAME 'extensionName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+#attributetype ( MSADat2:256
+# NAME 'streetAddress'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+
+attributetype ( MSADat4:113
+ NAME 'rpcNsBindings'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:237
+ NAME 'printBinNames'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:109
+ NAME 'replicaSource'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:246
+ NAME 'printLanguage'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:1365
+ NAME 'mS-SQL-Contact'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:357
+ NAME 'nTMixedDomain'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:483
+ NAME 'fRSFileFilter'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:332
+ NAME 'birthLocation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:682
+ NAME 'friendlyNames'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:622
+ NAME 'ipsecDataType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:584
+ NAME 'meetingRating'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:681
+ NAME 'indexedScopes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:312
+ NAME 'rpcNsObjectID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:168
+ NAME 'modifiedCount'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:218
+ NAME 'oMObjectClass'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:772
+ NAME 'aCSPolicyName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:502
+ NAME 'timeVolChange'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:337
+ NAME 'currMachineId'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:120
+ NAME 'schemaFlagsEx'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1356
+ NAME 'validAccesses'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:158
+ NAME 'domainReplica'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1309
+ NAME 'mSMQInterval2'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1308
+ NAME 'mSMQInterval1'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:916
+ NAME 'canonicalName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:94
+ NAME 'ntPwdHistory'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:133
+ NAME 'trustPartner'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:160
+ NAME 'lmPwdHistory'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:1380
+ NAME 'mS-SQL-Status'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:469
+ NAME 'USNIntersite'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:858
+ NAME 'netbootTools'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:99
+ NAME 'priorSetTime'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1367
+ NAME 'mS-SQL-Memory'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:950
+ NAME 'mSMQServices'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+#attributetype ( MSADat2:613
+# NAME 'employeeType'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+
+attributetype ( MSADat4:27
+ NAME 'currentValue'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:822
+ NAME 'siteLinkList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:107
+ NAME 'remoteSource'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:325
+ NAME 'setupCommand'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:212
+ NAME 'dSHeuristics'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1336
+ NAME 'replInterval'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:234
+ NAME 'printEndTime'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:1
+ NAME 'instanceType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:722
+ NAME 'otherIpPhone'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:965
+ NAME 'mSMQSiteName'
+ SYNTAX 'MSADtelex'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:579
+ NAME 'meetingOwner'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:242
+ NAME 'printCollate'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:480
+ NAME 'defaultGroup'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:79
+ NAME 'minPwdLength'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:864
+ NAME 'netbootSCPBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:650
+ NAME 'mhsORAddress'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+#attributetype ( MSADat4:651
+# NAME 'otherMailbox'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:367
+ NAME 'rpcNsCodeset'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:14
+ NAME 'hasMasterNCs'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:952
+ NAME 'mSMQMigrated'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:74
+ NAME 'dSASignature'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:115
+ NAME 'invocationId'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:254
+ NAME 'cOMTypelibId'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:26
+ NAME 'creationTime'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:581
+ NAME 'meetingScope'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:336
+ NAME 'volTableGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:513
+ NAME 'siteObjectBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:756
+ NAME 'aCSTimeOfDay'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:757
+ NAME 'aCSDirection'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:77
+ NAME 'maxTicketAge'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:481
+ NAME 'schemaUpdate'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.24'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:80
+ NAME 'minTicketAge'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:628
+ NAME 'ipsecNegotiationPolicyReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:327
+ NAME 'helpFileName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:148
+ NAME 'schemaIDGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:810
+ NAME 'createDialog'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:964
+ NAME 'mSMQNt4Flags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:327
+ NAME 'packageFlags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:464
+ NAME 'wWWHomePage'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:507
+ NAME 'volumeCount'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:273
+ NAME 'printStatus'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:890
+ NAME 'uPNSuffixes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:471
+ NAME 'trustParent'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1301
+ NAME 'tokenGroups'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:375
+ NAME 'systemFlags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:667
+ NAME 'syncWithSID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1306
+ NAME 'dNSProperty'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:710
+ NAME 'superScopes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44' )
+
+attributetype ( MSADat4:1347
+ NAME 'sPNMappings'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:272
+ NAME 'printNotify'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:282
+ NAME 'printMemory'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:154
+ NAME 'serverState'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:942
+ NAME 'mSMQVersion'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:373
+ NAME 'rIDUsedPool'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1355
+ NAME 'queryFilter'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:300
+ NAME 'printerName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:97
+ NAME 'preferredOU'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:649
+ NAME 'primaryInternationalISDNNumber'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:333
+ NAME 'oMTIndxGuid'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1337
+ NAME 'mSMQUserSid'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:487
+ NAME 'fRSRootPath'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:918
+ NAME 'mSMQJournal'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:499
+ NAME 'contextMenu'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:764
+ NAME 'aCSPriority'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:937
+ NAME 'mSMQSignKey'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:359
+ NAME 'netbootGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:925
+ NAME 'mSMQOwnerID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:24
+ NAME 'mustContain'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+
+attributetype ( MSADat4:379
+ NAME 'dnsAllowXFR'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1379
+ NAME 'mS-SQL-Vines'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:948
+ NAME 'mSMQDigests'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:662
+ NAME 'lockoutTime'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:53
+ NAME 'lastSetTime'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:25
+ NAME 'countryCode'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1377
+ NAME 'mS-SQL-TCPIP'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:934
+ NAME 'mSMQForeign'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:571
+ NAME 'meetingType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:714
+ NAME 'dhcpOptions'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:704
+ NAME 'dhcpServers'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44' )
+
+attributetype ( MSADat4:283
+ NAME 'assetNumber'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:350
+ NAME 'addressType'
+ SYNTAX 'MSADtelex'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:940
+ NAME 'mSMQCSPName'
+ SYNTAX 'MSADtelex'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:671
+ NAME 'msiFileList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:619
+ NAME 'dNSHostName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:705
+ NAME 'dhcpSubnets'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44' )
+
+attributetype ( MSADat4:1328
+ NAME 'pKIKeyUsage'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:30
+ NAME 'attributeID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:506
+ NAME 'objectCount'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:503
+ NAME 'timeRefresh'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:139
+ NAME 'profilePath'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:818
+ NAME 'productCode'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:647
+ NAME 'otherMobile'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:12
+ NAME 'badPwdCount'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1368
+ NAME 'mS-SQL-Build'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+#attributetype ( MSADat2:13
+# NAME 'displayName'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+
+attributetype ( MSADat4:87
+ NAME 'nETBIOSName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1395
+ NAME 'mS-SQL-Alias'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:75
+ NAME 'maxRenewAge'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:806
+ NAME 'treatAsLeaf'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:960
+ NAME 'mSMQNt4Stub'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' )
+
+attributetype ( MSADat4:324
+ NAME 'packageType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1212
+ NAME 'isEphemeral'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:36
+ NAME 'dMDLocation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:715
+ NAME 'dhcpClasses'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:39
+ NAME 'forceLogoff'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:2
+ NAME 'whenCreated'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.24'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:566
+ NAME 'meetingName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:786
+ NAME 'mailAddress'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:590
+ NAME 'meetingBlob'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:71
+ NAME 'machineRole'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:334
+ NAME 'searchFlags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:3
+ NAME 'whenChanged'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.24'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:702
+ NAME 'dhcpObjName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:897
+ NAME 'aCSMaxAggregatePeakRatePerUser'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:326
+ NAME 'packageName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:170
+ NAME 'systemOnly'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:935
+ NAME 'mSMQOSType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:680
+ NAME 'queryPoint'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:271
+ NAME 'printOwner'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:19
+ NAME 'uSNCreated'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:494
+ NAME 'siteServer'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:114
+ NAME 'rpcNsGroup'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:609
+ NAME 'sIDHistory'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:882
+ NAME 'fRSVersion'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:64
+ NAME 'logonHours'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:854
+ NAME 'netbootAnswerOnlyValidClients'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:96
+ NAME 'pwdLastSet'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:243
+ NAME 'printColor'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1391
+ NAME 'mS-SQL-Type'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:40
+ NAME 'fromServer'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:157
+ NAME 'serverRole'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:100
+ NAME 'priorValue'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:169
+ NAME 'logonCount'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:90
+ NAME 'unicodePwd'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:21
+ NAME 'subClassOf'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1396
+ NAME 'mS-SQL-Size'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:101
+ NAME 'privateKey'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:512
+ NAME 'siteObject'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:62
+ NAME 'scriptPath'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:223
+ NAME 'serverName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:953
+ NAME 'mSMQSiteID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:340
+ NAME 'rightsGuid'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:374
+ NAME 'rIDNextRID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:583
+ NAME 'meetingURL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat2:400
+ NAME 'addressEntryDisplayTableMSDOS'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:76
+ NAME 'maxStorage'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:35
+ NAME 'rangeUpper'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:34
+ NAME 'rangeLower'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:118
+ NAME 'otherPager'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:639
+ NAME 'isMemberOfPartialAttributeSet'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1224
+ NAME 'parentGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:141
+ NAME 'department'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:25
+ NAME 'mayContain'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+
+attributetype ( MSADat4:150
+ NAME 'adminCount'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:51
+ NAME 'lastLogoff'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1409
+ NAME 'masteredBy'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:35
+ NAME 'employeeID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:719
+ NAME 'dhcpMaxKey'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:229
+ NAME 'driverName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1363
+ NAME 'mS-SQL-Name'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:322
+ NAME 'categoryId'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:889
+ NAME 'additionalTrustedServiceNames'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:1354
+ NAME 'scopeFlags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:672
+ NAME 'categories'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:855
+ NAME 'netbootNewMachineNamingPolicy'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:19
+ NAME 'cOMClassID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:120
+ NAME 'uSNChanged'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:2
+ NAME 'objectGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:707
+ NAME 'dhcpRanges'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1358
+ NAME 'schemaInfo'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:646
+ NAME 'otherFacsimileTelephoneNumber'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:520
+ NAME 'machinePasswordChangeInterval'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:674
+ NAME 'rootTrust'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:136
+ NAME 'trustType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:750
+ NAME 'groupType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:896
+ NAME 'uSNSource'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:919
+ NAME 'mSMQQuota'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:927
+ NAME 'mSMQSites'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:910
+ NAME 'fromEntry'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7' )
+
+attributetype ( MSADat4:1376
+ NAME 'mS-SQL-SPX'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:892
+ NAME 'gPOptions'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:814
+ NAME 'msiScript'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:285
+ NAME 'printRate'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:683
+ NAME 'cRLPartitionedRevocationList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:652
+ NAME 'assistant'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:490
+ NAME 'fRSDSPoll'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:663
+ NAME 'partialAttributeDeletionList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:52
+ NAME 'lastLogon'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:22
+ NAME 'governsID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:341
+ NAME 'appliesTo'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:268
+ NAME 'eFSPolicy'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:155
+ NAME 'uASCompat'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:538
+ NAME 'prefixMap'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:661
+ NAME 'isDefunct'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:708
+ NAME 'dhcpSites'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44' )
+
+attributetype ( MSADat4:888
+ NAME 'iPSECNegotiationPolicyAction'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:382
+ NAME 'dnsRecord'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:21
+ NAME 'cOMProgID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:45
+ NAME 'homeDrive'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:580
+ NAME 'meetingIP'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1321
+ NAME 'aCSNonReservedMinPolicedSize'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:717
+ NAME 'dhcpState'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44' )
+
+attributetype ( MSADat4:922
+ NAME 'mSMQLabel'
+ SYNTAX 'MSADtelex'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:74
+ NAME 'maxPwdAge'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:78
+ NAME 'minPwdAge'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:689
+ NAME 'cRLObject'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:146
+ NAME 'objectSid'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:565
+ NAME 'meetingID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:620
+ NAME 'ipsecName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:48
+ NAME 'isDeleted'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:760
+ NAME 'aCSAggregateTokenRatePerUser'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:623
+ NAME 'ipsecData'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:668
+ NAME 'domainCAs'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:687
+ NAME 'cAConnect'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:238
+ NAME 'printMaxResolutionSupported'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:700
+ NAME 'dhcpFlags'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:402
+ NAME 'helpData16'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:653
+ NAME 'managedBy'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:9
+ NAME 'helpData32'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:944
+ NAME 'mSMQSite2'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:943
+ NAME 'mSMQSite1'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:677
+ NAME 'replTopologyStayOfExecution'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:912
+ NAME 'allowedChildClassesEffective'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:231
+ NAME 'oMSyntax'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:231
+ NAME 'priority'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:48
+ NAME 'keywords'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:946
+ NAME 'mSMQCost'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:821
+ NAME 'siteList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:145
+ NAME 'revision'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:91
+ NAME 'repsFrom'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:645
+ NAME 'userCert'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:951
+ NAME 'mSMQQMID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:228
+ NAME 'portName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:859
+ NAME 'netbootLocallyInstalledOSes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:261
+ NAME 'division'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:902
+ NAME 'aCSMaxSizeOfRSVPAccountFile'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:699
+ NAME 'dhcpType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:301
+ NAME 'wbemPath'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:362
+ NAME 'siteGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:26
+ NAME 'rDNAttID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:900
+ NAME 'aCSRSVPAccountFilesLocation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1226
+ NAME 'mSMQDependentClientServices'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:222
+ NAME 'location'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:874
+ NAME 'fRSFlags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:219
+ NAME 'iconPath'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:688
+ NAME 'cAWEBURL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:716
+ NAME 'mscopeId'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:660
+ NAME 'treeName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:211
+ NAME 'schedule'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:557
+ NAME 'parentCA'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:249
+ NAME 'cOMCLSID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:675
+ NAME 'catalogs'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:102
+ NAME 'memberOf'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:690
+ NAME 'cAUsages'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:706
+ NAME 'dhcpMask'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44' )
+
+attributetype ( MSADat4:511
+ NAME 'flatName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:686
+ NAME 'domainID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:58
+ NAME 'localeID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' )
+
+attributetype ( MSADat4:16
+ NAME 'codePage'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:768
+ NAME 'aCSEnableRSVPMessageLogging'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:240
+ NAME 'printOrientationsSupported'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:883
+ NAME 'msRRASVendorAttributeEntry'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:1246
+ NAME 'interSiteTopologyGenerator'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:307
+ NAME 'options'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:28
+ NAME 'dnsRoot'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:887
+ NAME 'iPSECNegotiationPolicyType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1392
+ NAME 'mS-SQL-InformationDirectory'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:365
+ NAME 'operatingSystemServicePack'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:88
+ NAME 'nextRid'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:865
+ NAME 'pekList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:7
+ NAME 'subRefs'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:505
+ NAME 'oMTGuid'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:205
+ NAME 'pKTGuid'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:146
+ NAME 'company'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:82
+ NAME 'moniker'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:156
+ NAME 'comment'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:721
+ NAME 'ipPhone'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1361
+ NAME 'mS-DS-ConsistencyChildCount'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:679
+ NAME 'creator'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:137
+ NAME 'uNCName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:55
+ NAME 'dBCSPwd'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1239
+ NAME 'mSMQDependentClientService'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:684
+ NAME 'certificateAuthorityObject'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:621
+ NAME 'ipsecID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:914
+ NAME 'allowedAttributesEffective'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+
+#attributetype ( MSADat2:598
+# NAME 'dmdName'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+
+attributetype ( MSADat4:759
+ NAME 'aCSMaxPeakBandwidthPerFlow'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:557
+ NAME 'Enabled'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:326
+ NAME 'perRecipDialogDisplayTable'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1248
+ NAME 'interSiteTopologyFailover'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:895
+ NAME 'transportAddressAttribute'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:852
+ NAME 'netbootCurrentClientCount'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:372
+ NAME 'rIDPreviousAllocationPool'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:83
+ NAME 'repsTo'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:224
+ NAME 'defaultSecurityDescriptor'
+ SYNTAX 'MSADsecDesc'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:519
+ NAME 'lastBackupRestorationTime'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:873
+ NAME 'fRSControlOutboundBacklog'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:255
+ NAME 'vendor'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:891
+ NAME 'gPLink'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:214
+ NAME 'originalDisplayTableMSDOS'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:50
+ NAME 'linkID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1130
+ NAME 'msNPSavedCallingStationID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:49
+ NAME 'mAPIID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:510
+ NAME 'serviceBindingInformation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:16
+ NAME 'nCName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1303
+ NAME 'tokenGroupsNoGCAcceptable'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:1418
+ NAME 'tokenGroupsGlobalAndUniversal'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:1190
+ NAME 'msRASSavedFramedIPAddress'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:766
+ NAME 'aCSAllocableRSVPBandwidth'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:61
+ NAME 'lockOutObservationWindow'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:857
+ NAME 'netbootIntelliMirrorOSes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:1320
+ NAME 'aCSNonReservedMaxSDUSize'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:265
+ NAME 'notes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:673
+ NAME 'retiredReplDSASignatures'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1313
+ NAME 'aCSMaxTokenBucketPerFlow'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:324
+ NAME 'addressEntryDisplayTable'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1317
+ NAME 'aCSMinimumDelayVariation'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:872
+ NAME 'fRSControlInboundBacklog'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:38
+ NAME 'flags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1399
+ NAME 'mS-SQL-LastDiagnosticDate'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1348
+ NAME 'gPCMachineExtensionNames'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1411
+ NAME 'ms-DS-MachineAccountQuota'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:325
+ NAME 'perMsgDialogDisplayTable'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:57
+ NAME 'defaultLocalPolicyObject'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1189
+ NAME 'msRASSavedCallbackNumber'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.26'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:685
+ NAME 'parentCACertificateChain'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:893
+ NAME 'gPCFunctionalityVersion'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:879
+ NAME 'fRSServiceCommandStatus'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1319
+ NAME 'aCSNonReservedTokenSize'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:775
+ NAME 'aCSMaxSizeOfRSVPLogFile'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:135
+ NAME 'cost'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:81
+ NAME 'modifiedCountAtLastProm'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:773
+ NAME 'aCSRSVPLogFilesLocation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+#attributetype ( MSADat2:81
+# NAME 'info'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+
+attributetype ( MSADat4:125
+ NAME 'supplementalCredentials'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:819
+ NAME 'bridgeheadTransportList'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:967
+ NAME 'mSMQSignCertificatesMig'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+## NO-USER-MODIFICATION
+#attributetype ( MSADat4:1
+# NAME 'name'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+
+attributetype ( MSADat4:1153
+ NAME 'msRADIUSFramedIPAddress'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1408
+ NAME 'mS-DS-ReplicatesNCReason'
+ SYNTAX 'MSADdnWithOctetString' )
+
+attributetype ( MSADat4:899
+ NAME 'aCSEnableRSVPAccounting'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+#attributetype ( MSADat4:881
+# NAME 'fRSTimeLastConfigChange'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.53'
+# SINGLE-VALUE )
+
+attributetype ( MSADat4:281
+ NAME 'printStaplingSupported'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1247
+ NAME 'interSiteTopologyRenew'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:364
+ NAME 'operatingSystemVersion'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:91
+ NAME 'otherLoginWorkstations'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:849
+ NAME 'netbootAllowNewClients'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1372
+ NAME 'mS-SQL-UnicodeSortOrder'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:749
+ NAME 'url'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:206
+ NAME 'pKT'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:199
+ NAME 'serviceInstanceVersion'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:169
+ NAME 'showInAdvancedViewOnly'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:758
+ NAME 'aCSMaxTokenRatePerFlow'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:868
+ NAME 'isCriticalSystemObject'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:576
+ NAME 'meetingMaxParticipants'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1208
+ NAME 'aNR'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:153
+ NAME 'rid'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:523
+ NAME 'proxyGenerationEnabled'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:871
+ NAME 'fRSControlDataCreation'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:692
+ NAME 'previousCACertificates'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:24
+ NAME 'contentIndexingAllowed'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:633
+ NAME 'policyReplicationFlags'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:870
+ NAME 'frsComputerReferenceBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:1318
+ NAME 'aCSNonReservedPeakRate'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:901
+ NAME 'aCSMaxNoOfAccountFiles'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:514
+ NAME 'physicalLocationObject'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:928
+ NAME 'mSMQOutRoutingServers'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:820
+ NAME 'bridgeheadServerListBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:1145
+ NAME 'msRADIUSCallbackNumber'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.26'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:361
+ NAME 'netbootMachineFilePath'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:963
+ NAME 'mSMQQueueJournalQuota'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:853
+ NAME 'netbootAnswerRequests'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:415
+ NAME 'operatingSystemHotfix'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:149
+ NAME 'attributeSecurityGUID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:711
+ NAME 'superScopeDescription'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:1359
+ NAME 'otherWellKnownObjects'
+ SYNTAX 'MSADdnWithOctetString' )
+
+attributetype ( MSADat4:780
+ NAME 'aCSNonReservedTxLimit'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:11
+ NAME 'authenticationOptions'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:867
+ NAME 'altSecurityIdentities'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:1349
+ NAME 'gPCUserExtensionNames'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:358
+ NAME 'netbootInitialization'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1364
+ NAME 'mS-SQL-RegisteredOwner'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:761
+ NAME 'aCSMaxDurationPerFlow'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1330
+ NAME 'pKICriticalExtensions'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:748
+ NAME 'attributeDisplayNames'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:1404
+ NAME 'mS-SQL-AllowImmediateUpdatingSubscription'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1191
+ NAME 'msRASSavedFramedRoute'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
+
+attributetype ( MSADat4:752
+ NAME 'userSharedFolderOther'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+#attributetype ( MSADat2:131
+# NAME 'co'
+# SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+# SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:909
+ NAME 'extendedAttributeInfo'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:1241
+ NAME 'netbootMirrorDataFile'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:1315
+ NAME 'aCSMinimumPolicedSize'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1353
+ NAME 'localizationDisplayId'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:582
+ NAME 'meetingAdvertiseScope'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1343
+ NAME 'dSUIAdminNotification'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:1381
+ NAME 'mS-SQL-LastUpdatedDate'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:1357
+ NAME 'dSCorePropagationData'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.24' )
+
+attributetype ( MSADat4:320
+ NAME 'implementedCategories'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+attributetype ( MSADat4:783
+ NAME 'defaultObjectCategory'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:422
+ NAME 'domainPolicyReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:929
+ NAME 'mSMQInRoutingServers'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:1311
+ NAME 'printDuplexSupported'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:693
+ NAME 'pendingCACertificates'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:281
+ NAME 'nTSecurityDescriptor'
+ SYNTAX 'MSADsecDesc'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:198
+ NAME 'systemAuxiliaryClass'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+
+attributetype ( MSADat4:898
+ NAME 'aCSNonReservedTxSize'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1382
+ NAME 'mS-SQL-InformationURL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:3
+ NAME 'replPropertyMetaData'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1384
+ NAME 'mS-SQL-PublicationURL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:275
+ NAME 'printKeepPrintedJobs'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:267
+ NAME 'uSNDSALastObjRemoved'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:381
+ NAME 'dnsNotifySecondaries'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' )
+
+attributetype ( MSADat4:1360
+ NAME 'mS-DS-ConsistencyGuid'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:869
+ NAME 'frsComputerReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1369
+ NAME 'mS-SQL-ServiceAccount'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1124
+ NAME 'msNPCallingStationID'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
+
+attributetype ( MSADat4:947
+ NAME 'mSMQSignCertificates'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:624
+ NAME 'ipsecOwnersReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:14
+ NAME 'builtinModifiedCount'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:634
+ NAME 'privilegeDisplayName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:380
+ NAME 'dnsSecureSecondaries'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27' )
+
+attributetype ( MSADat4:817
+ NAME 'localizedDescription'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:195
+ NAME 'systemPossSuperiors'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.38' )
+
+attributetype ( MSADat2:353
+ NAME 'displayNamePrintable'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.44'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:771
+ NAME 'servicePrincipalName'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:866
+ NAME 'pekKeyChangeInterval'
+ SYNTAX 'MSADlargeInteger'
+ SINGLE-VALUE )
+
+attributetype ( MSADat2:445
+ NAME 'originalDisplayTable'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1398
+ NAME 'mS-SQL-LastBackupDate'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:517
+ NAME 'ipsecPolicyReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:823
+ NAME 'certificateTemplates'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:15
+ NAME 'hasPartialReplicaNCs'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:457
+ NAME 'localPolicyReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat2:380
+ NAME 'extendedCharsAllowed'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:629
+ NAME 'ipsecFilterReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:626
+ NAME 'ipsecISAKMPReference'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:876
+ NAME 'fRSMemberReferenceBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:314
+ NAME 'rpcNsTransferSyntax'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1227
+ NAME 'mSMQRoutingServices'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1375
+ NAME 'mS-SQL-MultiProtocol'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:825
+ NAME 'enrollmentProviders'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:287
+ NAME 'printNetworkAddress'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1171
+ NAME 'msRADIUSServiceType'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:631
+ NAME 'printPagesPerMinute'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:299
+ NAME 'printMediaSupported'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' )
+
+attributetype ( MSADat4:824
+ NAME 'signatureAlgorithms'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:877
+ NAME 'fRSPartnerAuthLevel'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:636
+ NAME 'privilegeAttributes'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.27'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:640
+ NAME 'partialAttributeSet'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:850
+ NAME 'netbootLimitClients'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1383
+ NAME 'mS-SQL-ConnectionURL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1406
+ NAME 'mS-SQL-AllowSnapshotFilesFTPDownloading'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.7'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1331
+ NAME 'pKIExpirationPeriod'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40'
+ SINGLE-VALUE )
+
+# NO-USER-MODIFICATION
+attributetype ( MSADat4:531
+ NAME 'nonSecurityMemberBL'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.12' )
+
+attributetype ( MSADat4:540
+ NAME 'initialAuthOutgoing'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.15'
+ SINGLE-VALUE )
+
+attributetype ( MSADat4:1158
+ NAME 'msRADIUSFramedRoute'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.26' )
+
+attributetype ( MSADat4:200
+ NAME 'controlAccessRights'
+ SYNTAX '1.3.6.1.4.1.1466.115.121.1.40' )
+
+objectclass ( MSADoc5:0 NAME 'mstop' SUP top ABSTRACT
+ MUST (objectClass $ instanceType
+ $ nTSecurityDescriptor $ objectCategory ) MAY (cn $ description $ distinguishedName
+ $ whenCreated $ whenChanged $ subRefs $ displayName $ uSNCreated $ isDeleted
+ $ dSASignature $ objectVersion $ repsTo $ repsFrom $ memberOf $ ownerBL
+ $ uSNChanged $ uSNLastObjRem $ showInAdvancedViewOnly $ adminDisplayName
+ $ proxyAddresses $ adminDescription $ extensionName $ uSNDSALastObjRemoved
+ $ displayNamePrintable $ directReports $ wWWHomePage $ USNIntersite $ name
+ $ objectGUID $ replPropertyMetaData $ replUpToDateVector $ flags $ revision
+ $ wbemPath $ fSMORoleOwner $ systemFlags $ siteObjectBL $ serverReferenceBL
+ $ nonSecurityMemberBL $ queryPolicyBL $ wellKnownObjects $ isPrivilegeHolder
+ $ partialAttributeSet $ managedObjects $ partialAttributeDeletionList $ url
+ $ lastKnownParent $ bridgeheadServerListBL $ netbootSCPBL $ isCriticalSystemObject
+ $ frsComputerReferenceBL $ fRSMemberReferenceBL $ uSNSource $ fromEntry
+ $ allowedChildClasses $ allowedChildClassesEffective $ allowedAttributes
+ $ allowedAttributesEffective $ possibleInferiors $ canonicalName $ proxiedObjectName
+ $ sDRightsEffective $ dSCorePropagationData $ otherWellKnownObjects
+ $ mS-DS-ConsistencyGuid $ mS-DS-ConsistencyChildCount $ masteredBy
+ $ msCOM-PartitionSetLink $ msCOM-UserLink $ msDS-Approx-Immed-Subordinates
+ $ msDS-NCReplCursors $ msDS-NCReplInboundNeighbors $ msDS-NCReplOutboundNeighbors
+ $ msDS-ReplAttributeMetaData $ msDS-ReplValueMetaData $ msDS-NonMembersBL
+ $ msDS-MembersForAzRoleBL $ msDS-OperationsForAzTaskBL $ msDS-TasksForAzTaskBL
+ $ msDS-OperationsForAzRoleBL $ msDS-TasksForAzRoleBL $ msDs-masteredBy
+ $ msDS-ObjectReferenceBL $ msDS-PrincipalName $ msDS-RevealedDSAs $ msDS-KrbTgtLinkBl
+ $ msDS-IsFullReplicaFor $ msDS-IsDomainFor $ msDS-IsPartialReplicaFor
+ $ msDS-AuthenticatedToAccountlist $ msDS-NC-RO-Replica-Locations-BL $ msDS-RevealedListBL
+ $ msDS-PSOApplied $ msDS-NcType $ msDS-OIDToGroupLinkBl $ msDS-HostServiceAccountBL
+ $ isRecycled $ msDS-LocalEffectiveDeletionTime $ msDS-LocalEffectiveRecycleTime
+ $ msDS-LastKnownRDN $ msDS-EnabledFeatureBL $ msDS-ClaimSharesPossibleValuesWithBL
+ $ msDS-MembersOfResourcePropertyListBL $ msDS-IsPrimaryComputerFor
+ $ msDS-ValueTypeReferenceBL $ msDS-TDOIngressBL $ msDS-TDOEgressBL $ msDS-parentdistname
+ $ msDS-ReplValueMetaDataExt $ msds-memberOfTransitive $ msds-memberTransitive
+ $ msSFU30PosixMemberOf $ msDFSR-MemberReferenceBL $ msDFSR-ComputerReferenceBL ) )
+objectclass ( MSADoc5:8 NAME 'group' SUP mstop STRUCTURAL
+ MUST (groupType ) MAY (member $ nTGroupMembers $ operatorCount $ adminCount
+ $ groupAttributes $ groupMembershipSAM $ controlAccessRights $ desktopProfile
+ $ nonSecurityMember $ managedBy $ primaryGroupToken $ msDS-AzLDAPQuery
+ $ msDS-NonMembers $ msDS-AzBizRule $ msDS-AzBizRuleLanguage $ msDS-AzLastImportedBizRulePath
+ $ msDS-AzApplicationData $ msDS-AzObjectGuid $ msDS-AzGenericData
+ $ msDS-PrimaryComputer $ mail $ msSFU30Name $ msSFU30NisDomain $ msSFU30PosixMember ) )
+objectclass ( MSADoc5:9 NAME 'user'
+ SUP ( mstop $ organizationalPerson ) STRUCTURAL
+ MAY (o $ businessCategory $ userCertificate $ givenName $ initials
+ $ x500uniqueIdentifier $ displayName $ networkAddress $ employeeNumber
+ $ employeeType $ homePostalAddress $ userAccountControl $ badPwdCount
+ $ codePage $ homeDirectory $ homeDrive $ badPasswordTime $ lastLogoff
+ $ lastLogon $ dBCSPwd $ localeID $ scriptPath $ logonHours $ logonWorkstation
+ $ maxStorage $ userWorkstations $ unicodePwd $ otherLoginWorkstations
+ $ ntPwdHistory $ pwdLastSet $ preferredOU $ primaryGroupID $ userParameters
+ $ profilePath $ operatorCount $ adminCount $ accountExpires $ lmPwdHistory
+ $ groupMembershipSAM $ logonCount $ controlAccessRights $ defaultClassStore
+ $ groupsToIgnore $ groupPriority $ desktopProfile $ dynamicLDAPServer
+ $ userPrincipalName $ lockoutTime $ userSharedFolder $ userSharedFolderOther
+ $ servicePrincipalName $ aCSPolicyName $ terminalServer $ mSMQSignCertificates
+ $ mSMQDigests $ mSMQDigestsMig $ mSMQSignCertificatesMig $ msNPAllowDialin
+ $ msNPCallingStationID $ msNPSavedCallingStationID $ msRADIUSCallbackNumber
+ $ msRADIUSFramedIPAddress $ msRADIUSFramedRoute $ msRADIUSServiceType
+ $ msRASSavedCallbackNumber $ msRASSavedFramedIPAddress
+ $ msRASSavedFramedRoute $ mS-DS-CreatorSID $ msCOM-UserPartitionSetLink
+ $ msDS-Cached-Membership $ msDS-Cached-Membership-Time-Stamp
+ $ msDS-Site-Affinity $ msDS-User-Account-Control-Computed $ lastLogonTimestamp
+ $ msIIS-FTPRoot $ msIIS-FTPDir $ msDRM-IdentityCertificate $ msDS-SourceObjectDN
+ $ msPKIRoamingTimeStamp $ msPKIDPAPIMasterKeys $ msPKIAccountCredentials
+ $ msRADIUS-FramedInterfaceId $ msRADIUS-SavedFramedInterfaceId $ msRADIUS-FramedIpv6Prefix
+ $ msRADIUS-SavedFramedIpv6Prefix $ msRADIUS-FramedIpv6Route $ msRADIUS-SavedFramedIpv6Route
+ $ msDS-SecondaryKrbTgtNumber $ msDS-AuthenticatedAtDC $ msDS-SupportedEncryptionTypes
+ $ msDS-LastSuccessfulInteractiveLogonTime $ msDS-LastFailedInteractiveLogonTime
+ $ msDS-FailedInteractiveLogonCount $ msDS-FailedInteractiveLogonCountAtLastSuccessfulLogon
+ $ msTSProfilePath $ msTSHomeDirectory $ msTSHomeDrive $ msTSAllowLogon
+ $ msTSRemoteControl $ msTSMaxDisconnectionTime $ msTSMaxConnectionTime
+ $ msTSMaxIdleTime $ msTSReconnectionAction $ msTSBrokenConnectionAction
+ $ msTSConnectClientDrives $ msTSConnectPrinterDrives $ msTSDefaultToMainPrinter
+ $ msTSWorkDirectory $ msTSInitialProgram $ msTSProperty01 $ msTSProperty02
+ $ msTSExpireDate $ msTSLicenseVersion $ msTSManagingLS
+ $ msDS-UserPasswordExpiryTimeComputed $ msTSExpireDate2 $ msTSLicenseVersion2
+ $ msTSManagingLS2 $ msTSExpireDate3 $ msTSLicenseVersion3 $ msTSManagingLS3
+ $ msTSExpireDate4 $ msTSLicenseVersion4 $ msTSManagingLS4 $ msTSLSProperty01
+ $ msTSLSProperty02 $ msDS-ResultantPSO $ msPKI-CredentialRoamingTokens
+ $ msTSPrimaryDesktop $ msTSSecondaryDesktops $ msDS-PrimaryComputer $ msDS-SyncServerUrl
+ $ msDS-AssignedAuthNPolicySilo $ msDS-AuthNPolicySiloMembersBL
+ $ msDS-AssignedAuthNPolicy $ userSMIMECertificate $ uid $ mail $ roomNumber
+ $ photo $ manager $ homePhone $ secretary $ mobile $ pager $ audio $ jpegPhoto
+ $ carLicense $ departmentNumber $ preferredLanguage $ userPKCS12 $ labeledURI
+ $ msSFU30Name $ msSFU30NisDomain ) )
+#objectclass ( MSADoc3:23 NAME 'container' SUP mstop STRUCTURAL
+# MUST (cn ) MAY (schemaVersion $ defaultClassStore $ msDS-ObjectReference ) )
+objectclass ( 1.2.840.113556.1.3.30 NAME 'computer' SUP user STRUCTURAL
+ MAY (cn $ networkAddress $ localPolicyFlags $ defaultLocalPolicyObject $ machineRole
+ $ location $ netbootInitialization $ netbootGUID $ netbootMachineFilePath
+ $ siteGUID $ operatingSystem $ operatingSystemVersion $ operatingSystemServicePack
+ $ operatingSystemHotfix $ volumeCount $ physicalLocationObject $ dNSHostName
+ $ policyReplicationFlags $ managedBy $ rIDSetReferences $ catalogs
+ $ netbootSIFFile $ netbootMirrorDataFile $ msDS-AdditionalDnsHostName
+ $ msDS-AdditionalSamAccountName $ msDS-ExecuteScriptPassword $ msDS-KrbTgtLink
+ $ msDS-RevealedUsers $ msDS-NeverRevealGroup $ msDS-RevealOnDemandGroup
+ $ msDS-RevealedList $ msDS-AuthenticatedAtDC $ msDS-isGC $ msDS-isRODC $ msDS-SiteName
+ $ msDS-PromotionSettings $ msTPM-OwnerInformation $ msTSProperty01 $ msTSProperty02
+ $ msDS-IsUserCachableAtRodc $ msDS-HostServiceAccount $ msTSEndpointData
+ $ msTSEndpointType $ msTSEndpointPlugin $ msTSPrimaryDesktopBL $ msTSSecondaryDesktopBL
+ $ msTPM-TpmInformationForComputer $ msDS-GenerationId $ msImaging-ThumbprintHash
+ $ msImaging-HashAlgorithm $ netbootDUID $ msSFU30Name $ msSFU30Aliases $ msSFU30NisDomain $ nisMapName ) )
diff --git a/servers/slapd/schema/namedobject.ldif b/servers/slapd/schema/namedobject.ldif
new file mode 100644
index 0000000..d2c2e24
--- /dev/null
+++ b/servers/slapd/schema/namedobject.ldif
@@ -0,0 +1,32 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2021-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## Definitions from draft Structural Object Classes for Named Objects
+## https://tools.ietf.org/html/draft-stroeder-namedobject
+##
+## Portions Copyright (c) 2013 IETF Trust and the persons identified as the
+## document authors. All rights reserved.
+#
+# Depends upon core.ldif and cosine.ldif
+#
+# This file was automatically generated from namedobject.schema; see
+# that file for complete references.
+#
+dn: cn=namedobject,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: namedobject
+olcObjectClasses: ( 1.3.6.1.4.1.5427.1.389.6.20 NAME 'namedObject' SUP top
+ STRUCTURAL MUST cn MAY ( uniqueIdentifier $ description ) )
+olcObjectClasses: ( 1.3.6.1.4.1.5427.1.389.6.21 NAME 'namedPolicy' SUP name
+ dObject STRUCTURAL )
diff --git a/servers/slapd/schema/nis.ldif b/servers/slapd/schema/nis.ldif
new file mode 100644
index 0000000..f623e29
--- /dev/null
+++ b/servers/slapd/schema/nis.ldif
@@ -0,0 +1,120 @@
+# NIS (RFC2307)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+#
+# Definitions from RFC2307 (Experimental)
+# An Approach for Using LDAP as a Network Information Service
+#
+# Depends upon core.ldif and cosine.ldif
+#
+# This file was automatically generated from nis.schema; see that file
+# for complete references.
+#
+dn: cn=nis,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: nis
+olcAttributeTypes: ( 1.3.6.1.1.1.1.2 NAME 'gecos' DESC 'The GECOS field; th
+ e common name' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatc
+ h SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.3 NAME 'homeDirectory' DESC 'The absolut
+ e path to the home directory' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1
+ 466.115.121.1.26 SINGLE-VALUE )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.4 NAME 'loginShell' DESC 'The path to th
+ e login shell' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.2
+ 6 SINGLE-VALUE )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.5 NAME 'shadowLastChange' EQUALITY integ
+ erMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.6 NAME 'shadowMin' EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.7 NAME 'shadowMax' EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.8 NAME 'shadowWarning' EQUALITY integerM
+ atch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.9 NAME 'shadowInactive' EQUALITY integer
+ Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.10 NAME 'shadowExpire' EQUALITY integerM
+ atch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.11 NAME 'shadowFlag' EQUALITY integerMat
+ ch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.12 NAME 'memberUid' EQUALITY caseExactI
+ A5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.
+ 26 )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.13 NAME 'memberNisNetgroup' EQUALITY ca
+ seExactIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.11
+ 5.121.1.26 )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.14 NAME 'nisNetgroupTriple' DESC 'Netgr
+ oup triple' SYNTAX 1.3.6.1.1.1.0.0 )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.15 NAME 'ipServicePort' EQUALITY intege
+ rMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.16 NAME 'ipServiceProtocol' SUP name )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.17 NAME 'ipProtocolNumber' EQUALITY int
+ egerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.18 NAME 'oncRpcNumber' EQUALITY integer
+ Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.19 NAME 'ipHostNumber' DESC 'IP address
+ ' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.20 NAME 'ipNetworkNumber' DESC 'IP netw
+ ork' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} SI
+ NGLE-VALUE )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.21 NAME 'ipNetmaskNumber' DESC 'IP netm
+ ask' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} SI
+ NGLE-VALUE )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.22 NAME 'macAddress' DESC 'MAC address'
+ EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.23 NAME 'bootParameter' DESC 'rpc.bootp
+ aramd parameter' SYNTAX 1.3.6.1.1.1.0.1 )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.24 NAME 'bootFile' DESC 'Boot image nam
+ e' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.26 NAME 'nisMapName' SUP name )
+olcAttributeTypes: ( 1.3.6.1.1.1.1.27 NAME 'nisMapEntry' EQUALITY caseExac
+ tIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.
+ 1.26{1024} SINGLE-VALUE )
+olcObjectClasses: ( 1.3.6.1.1.1.2.0 NAME 'posixAccount' DESC 'Abstraction o
+ f an account with POSIX attributes' SUP top AUXILIARY MUST ( cn $ uid $ uidNu
+ mber $ gidNumber $ homeDirectory ) MAY ( userPassword $ loginShell $ gecos $
+ description ) )
+olcObjectClasses: ( 1.3.6.1.1.1.2.1 NAME 'shadowAccount' DESC 'Additional a
+ ttributes for shadow passwords' SUP top AUXILIARY MUST uid MAY ( userPassword
+ $ shadowLastChange $ shadowMin $ shadowMax $ shadowWarning $ shadowInactive
+ $ shadowExpire $ shadowFlag $ description ) )
+olcObjectClasses: ( 1.3.6.1.1.1.2.2 NAME 'posixGroup' DESC 'Abstraction of
+ a group of accounts' SUP top STRUCTURAL MUST ( cn $ gidNumber ) MAY ( userPas
+ sword $ memberUid $ description ) )
+olcObjectClasses: ( 1.3.6.1.1.1.2.3 NAME 'ipService' DESC 'Abstraction an I
+ nternet Protocol service' SUP top STRUCTURAL MUST ( cn $ ipServicePort $ ipSe
+ rviceProtocol ) MAY description )
+olcObjectClasses: ( 1.3.6.1.1.1.2.4 NAME 'ipProtocol' DESC 'Abstraction of
+ an IP protocol' SUP top STRUCTURAL MUST ( cn $ ipProtocolNumber $ description
+ ) MAY description )
+olcObjectClasses: ( 1.3.6.1.1.1.2.5 NAME 'oncRpc' DESC 'Abstraction of an O
+ NC/RPC binding' SUP top STRUCTURAL MUST ( cn $ oncRpcNumber $ description ) M
+ AY description )
+olcObjectClasses: ( 1.3.6.1.1.1.2.6 NAME 'ipHost' DESC 'Abstraction of a ho
+ st, an IP device' SUP top AUXILIARY MUST ( cn $ ipHostNumber ) MAY ( l $ desc
+ ription $ manager ) )
+olcObjectClasses: ( 1.3.6.1.1.1.2.7 NAME 'ipNetwork' DESC 'Abstraction of a
+ n IP network' SUP top STRUCTURAL MUST ( cn $ ipNetworkNumber ) MAY ( ipNetmas
+ kNumber $ l $ description $ manager ) )
+olcObjectClasses: ( 1.3.6.1.1.1.2.8 NAME 'nisNetgroup' DESC 'Abstraction of
+ a netgroup' SUP top STRUCTURAL MUST cn MAY ( nisNetgroupTriple $ memberNisNe
+ tgroup $ description ) )
+olcObjectClasses: ( 1.3.6.1.1.1.2.9 NAME 'nisMap' DESC 'A generic abstracti
+ on of a NIS map' SUP top STRUCTURAL MUST nisMapName MAY description )
+olcObjectClasses: ( 1.3.6.1.1.1.2.10 NAME 'nisObject' DESC 'An entry in a
+ NIS map' SUP top STRUCTURAL MUST ( cn $ nisMapEntry $ nisMapName ) MAY descri
+ ption )
+olcObjectClasses: ( 1.3.6.1.1.1.2.11 NAME 'ieee802Device' DESC 'A device w
+ ith a MAC address' SUP top AUXILIARY MAY macAddress )
+olcObjectClasses: ( 1.3.6.1.1.1.2.12 NAME 'bootableDevice' DESC 'A device
+ with boot parameters' SUP top AUXILIARY MAY ( bootFile $ bootParameter ) )
diff --git a/servers/slapd/schema/nis.schema b/servers/slapd/schema/nis.schema
new file mode 100644
index 0000000..1322fe5
--- /dev/null
+++ b/servers/slapd/schema/nis.schema
@@ -0,0 +1,237 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+# Definitions from RFC2307 (Experimental)
+# An Approach for Using LDAP as a Network Information Service
+
+# Depends upon core.schema and cosine.schema
+
+# Note: The definitions in RFC2307 are given in syntaxes closely related
+# to those in RFC2252, however, some liberties are taken that are not
+# supported by RFC2252. This file has been written following RFC2252
+# strictly.
+
+# OID Base is iso(1) org(3) dod(6) internet(1) directory(1) nisSchema(1).
+# i.e. nisSchema in RFC2307 is 1.3.6.1.1.1
+#
+# Syntaxes are under 1.3.6.1.1.1.0 (two new syntaxes are defined)
+# validaters for these syntaxes are incomplete, they only
+# implement printable string validation (which is good as the
+# common use of these syntaxes violates the specification).
+# Attribute types are under 1.3.6.1.1.1.1
+# Object classes are under 1.3.6.1.1.1.2
+
+# Attribute Type Definitions
+
+# builtin
+#attributetype ( 1.3.6.1.1.1.1.0 NAME 'uidNumber'
+# DESC 'An integer uniquely identifying a user in an administrative domain'
+# EQUALITY integerMatch
+# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+# builtin
+#attributetype ( 1.3.6.1.1.1.1.1 NAME 'gidNumber'
+# DESC 'An integer uniquely identifying a group in an administrative domain'
+# EQUALITY integerMatch
+# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.2 NAME 'gecos'
+ DESC 'The GECOS field; the common name'
+ EQUALITY caseIgnoreIA5Match
+ SUBSTR caseIgnoreIA5SubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.3 NAME 'homeDirectory'
+ DESC 'The absolute path to the home directory'
+ EQUALITY caseExactIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.4 NAME 'loginShell'
+ DESC 'The path to the login shell'
+ EQUALITY caseExactIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.5 NAME 'shadowLastChange'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.6 NAME 'shadowMin'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.7 NAME 'shadowMax'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.8 NAME 'shadowWarning'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.9 NAME 'shadowInactive'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.10 NAME 'shadowExpire'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.11 NAME 'shadowFlag'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.12 NAME 'memberUid'
+ EQUALITY caseExactIA5Match
+ SUBSTR caseExactIA5SubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 1.3.6.1.1.1.1.13 NAME 'memberNisNetgroup'
+ EQUALITY caseExactIA5Match
+ SUBSTR caseExactIA5SubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 1.3.6.1.1.1.1.14 NAME 'nisNetgroupTriple'
+ DESC 'Netgroup triple'
+ SYNTAX 1.3.6.1.1.1.0.0 )
+
+attributetype ( 1.3.6.1.1.1.1.15 NAME 'ipServicePort'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.16 NAME 'ipServiceProtocol'
+ SUP name )
+
+attributetype ( 1.3.6.1.1.1.1.17 NAME 'ipProtocolNumber'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.18 NAME 'oncRpcNumber'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.19 NAME 'ipHostNumber'
+ DESC 'IP address'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
+
+attributetype ( 1.3.6.1.1.1.1.20 NAME 'ipNetworkNumber'
+ DESC 'IP network'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.21 NAME 'ipNetmaskNumber'
+ DESC 'IP netmask'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.1.1.1.22 NAME 'macAddress'
+ DESC 'MAC address'
+ EQUALITY caseIgnoreIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
+
+attributetype ( 1.3.6.1.1.1.1.23 NAME 'bootParameter'
+ DESC 'rpc.bootparamd parameter'
+ SYNTAX 1.3.6.1.1.1.0.1 )
+
+attributetype ( 1.3.6.1.1.1.1.24 NAME 'bootFile'
+ DESC 'Boot image name'
+ EQUALITY caseExactIA5Match
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
+
+attributetype ( 1.3.6.1.1.1.1.26 NAME 'nisMapName'
+ SUP name )
+
+attributetype ( 1.3.6.1.1.1.1.27 NAME 'nisMapEntry'
+ EQUALITY caseExactIA5Match
+ SUBSTR caseExactIA5SubstringsMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{1024} SINGLE-VALUE )
+
+# Object Class Definitions
+
+objectclass ( 1.3.6.1.1.1.2.0 NAME 'posixAccount'
+ DESC 'Abstraction of an account with POSIX attributes'
+ SUP top AUXILIARY
+ MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
+ MAY ( userPassword $ loginShell $ gecos $ description ) )
+
+objectclass ( 1.3.6.1.1.1.2.1 NAME 'shadowAccount'
+ DESC 'Additional attributes for shadow passwords'
+ SUP top AUXILIARY
+ MUST uid
+ MAY ( userPassword $ shadowLastChange $ shadowMin $
+ shadowMax $ shadowWarning $ shadowInactive $
+ shadowExpire $ shadowFlag $ description ) )
+
+objectclass ( 1.3.6.1.1.1.2.2 NAME 'posixGroup'
+ DESC 'Abstraction of a group of accounts'
+ SUP top STRUCTURAL
+ MUST ( cn $ gidNumber )
+ MAY ( userPassword $ memberUid $ description ) )
+
+objectclass ( 1.3.6.1.1.1.2.3 NAME 'ipService'
+ DESC 'Abstraction an Internet Protocol service'
+ SUP top STRUCTURAL
+ MUST ( cn $ ipServicePort $ ipServiceProtocol )
+ MAY ( description ) )
+
+objectclass ( 1.3.6.1.1.1.2.4 NAME 'ipProtocol'
+ DESC 'Abstraction of an IP protocol'
+ SUP top STRUCTURAL
+ MUST ( cn $ ipProtocolNumber $ description )
+ MAY description )
+
+objectclass ( 1.3.6.1.1.1.2.5 NAME 'oncRpc'
+ DESC 'Abstraction of an ONC/RPC binding'
+ SUP top STRUCTURAL
+ MUST ( cn $ oncRpcNumber $ description )
+ MAY description )
+
+objectclass ( 1.3.6.1.1.1.2.6 NAME 'ipHost'
+ DESC 'Abstraction of a host, an IP device'
+ SUP top AUXILIARY
+ MUST ( cn $ ipHostNumber )
+ MAY ( l $ description $ manager ) )
+
+objectclass ( 1.3.6.1.1.1.2.7 NAME 'ipNetwork'
+ DESC 'Abstraction of an IP network'
+ SUP top STRUCTURAL
+ MUST ( cn $ ipNetworkNumber )
+ MAY ( ipNetmaskNumber $ l $ description $ manager ) )
+
+objectclass ( 1.3.6.1.1.1.2.8 NAME 'nisNetgroup'
+ DESC 'Abstraction of a netgroup'
+ SUP top STRUCTURAL
+ MUST cn
+ MAY ( nisNetgroupTriple $ memberNisNetgroup $ description ) )
+
+objectclass ( 1.3.6.1.1.1.2.9 NAME 'nisMap'
+ DESC 'A generic abstraction of a NIS map'
+ SUP top STRUCTURAL
+ MUST nisMapName
+ MAY description )
+
+objectclass ( 1.3.6.1.1.1.2.10 NAME 'nisObject'
+ DESC 'An entry in a NIS map'
+ SUP top STRUCTURAL
+ MUST ( cn $ nisMapEntry $ nisMapName )
+ MAY description )
+
+objectclass ( 1.3.6.1.1.1.2.11 NAME 'ieee802Device'
+ DESC 'A device with a MAC address'
+ SUP top AUXILIARY
+ MAY macAddress )
+
+objectclass ( 1.3.6.1.1.1.2.12 NAME 'bootableDevice'
+ DESC 'A device with boot parameters'
+ SUP top AUXILIARY
+ MAY ( bootFile $ bootParameter ) )
diff --git a/servers/slapd/schema/openldap.ldif b/servers/slapd/schema/openldap.ldif
new file mode 100644
index 0000000..ef44da3
--- /dev/null
+++ b/servers/slapd/schema/openldap.ldif
@@ -0,0 +1,88 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+#
+#
+# OpenLDAP Project's directory schema items
+#
+# depends upon:
+# core.schema
+# cosine.schema
+# inetorgperson.schema
+#
+# These are provided for informational purposes only.
+#
+# This openldap.ldif file is provided as a demonstration of how to
+# convert a *.schema file into *.ldif format. The key points:
+# In LDIF, a blank line terminates an entry. Blank lines in a *.schema
+# file should be replaced with a single '#' to turn them into
+# comments, or they should just be removed.
+# In addition to the actual schema directives, the file needs a small
+# header to make it a valid LDAP entry. This header must provide the
+# dn of the entry, the objectClass, and the cn, as shown here:
+#
+dn: cn=openldap,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: openldap
+#
+# The schema directives need to be changed to LDAP Attributes.
+# First a basic string substitution can be done on each of the keywords:
+# objectIdentifier -> olcObjectIdentifier:
+# objectClass -> olcObjectClasses:
+# attributeType -> olcAttributeTypes:
+# Then leading whitespace must be fixed. The slapd.conf format allows
+# tabs or spaces to denote line continuation, while LDIF only allows
+# the space character.
+# Also slapd.conf preserves the continuation character, while LDIF strips
+# it out. So a single TAB/SPACE in slapd.conf must be replaced with
+# two SPACEs in LDIF, otherwise the continued text may get joined as
+# a single word.
+# The directives must be listed in a proper sequence:
+# All olcObjectIdentifiers must be first, so they may be referenced by
+# any following definitions.
+# All olcAttributeTypes must be next, so they may be referenced by any
+# following objectClass definitions.
+# All olcObjectClasses must be after the olcAttributeTypes.
+# And of course, any superior must occur before anything that inherits
+# from it.
+#
+olcObjectIdentifier: OpenLDAProot 1.3.6.1.4.1.4203
+#
+olcObjectIdentifier: OpenLDAP OpenLDAProot:1
+olcObjectIdentifier: OpenLDAPattributeType OpenLDAP:3
+olcObjectIdentifier: OpenLDAPobjectClass OpenLDAP:4
+#
+olcObjectClasses: ( OpenLDAPobjectClass:3
+ NAME 'OpenLDAPorg'
+ DESC 'OpenLDAP Organizational Object'
+ SUP organization
+ MAY ( buildingName $ displayName $ labeledURI ) )
+#
+olcObjectClasses: ( OpenLDAPobjectClass:4
+ NAME 'OpenLDAPou'
+ DESC 'OpenLDAP Organizational Unit Object'
+ SUP organizationalUnit
+ MAY ( buildingName $ displayName $ labeledURI $ o ) )
+#
+olcObjectClasses: ( OpenLDAPobjectClass:5
+ NAME 'OpenLDAPperson'
+ DESC 'OpenLDAP Person'
+ SUP ( pilotPerson $ inetOrgPerson )
+ MUST ( uid $ cn )
+ MAY ( givenName $ labeledURI $ o ) )
+#
+olcObjectClasses: ( OpenLDAPobjectClass:6
+ NAME 'OpenLDAPdisplayableObject'
+ DESC 'OpenLDAP Displayable Object'
+ AUXILIARY
+ MAY displayName )
diff --git a/servers/slapd/schema/openldap.schema b/servers/slapd/schema/openldap.schema
new file mode 100644
index 0000000..ae27205
--- /dev/null
+++ b/servers/slapd/schema/openldap.schema
@@ -0,0 +1,54 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+#
+# OpenLDAP Project's directory schema items
+#
+# depends upon:
+# core.schema
+# cosine.schema
+# inetorgperson.schema
+#
+# These are provided for informational purposes only.
+
+objectIdentifier OpenLDAProot 1.3.6.1.4.1.4203
+
+objectIdentifier OpenLDAP OpenLDAProot:1
+objectIdentifier OpenLDAPattributeType OpenLDAP:3
+objectIdentifier OpenLDAPobjectClass OpenLDAP:4
+
+objectClass ( OpenLDAPobjectClass:3
+ NAME 'OpenLDAPorg'
+ DESC 'OpenLDAP Organizational Object'
+ SUP organization
+ MAY ( buildingName $ displayName $ labeledURI ) )
+
+objectClass ( OpenLDAPobjectClass:4
+ NAME 'OpenLDAPou'
+ DESC 'OpenLDAP Organizational Unit Object'
+ SUP organizationalUnit
+ MAY ( buildingName $ displayName $ labeledURI $ o ) )
+
+objectClass ( OpenLDAPobjectClass:5
+ NAME 'OpenLDAPperson'
+ DESC 'OpenLDAP Person'
+ SUP ( pilotPerson $ inetOrgPerson )
+ MUST ( uid $ cn )
+ MAY ( givenName $ labeledURI $ o ) )
+
+objectClass ( OpenLDAPobjectClass:6
+ NAME 'OpenLDAPdisplayableObject'
+ DESC 'OpenLDAP Displayable Object'
+ AUXILIARY
+ MAY displayName )
diff --git a/servers/slapd/schema/pmi.ldif b/servers/slapd/schema/pmi.ldif
new file mode 100644
index 0000000..c6d9cc3
--- /dev/null
+++ b/servers/slapd/schema/pmi.ldif
@@ -0,0 +1,123 @@
+# OpenLDAP X.509 PMI schema
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+#
+## Portions Copyright (C) The Internet Society (1997-2006).
+## All Rights Reserved.
+#
+# Includes LDAPv3 schema items from:
+# ITU X.509 (08/2005)
+#
+# This file was automatically generated from pmi.schema; see that file
+# for complete references.
+#
+dn: cn=pmi,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: pmi
+olcObjectIdentifier: {0}id-oc-pmiUser 2.5.6.24
+olcObjectIdentifier: {1}id-oc-pmiAA 2.5.6.25
+olcObjectIdentifier: {2}id-oc-pmiSOA 2.5.6.26
+olcObjectIdentifier: {3}id-oc-attCertCRLDistributionPts 2.5.6.27
+olcObjectIdentifier: {4}id-oc-privilegePolicy 2.5.6.32
+olcObjectIdentifier: {5}id-oc-pmiDelegationPath 2.5.6.33
+olcObjectIdentifier: {6}id-oc-protectedPrivilegePolicy 2.5.6.34
+olcObjectIdentifier: {7}id-at-attributeCertificate 2.5.4.58
+olcObjectIdentifier: {8}id-at-attributeCertificateRevocationList 2.5.4.59
+olcObjectIdentifier: {9}id-at-aACertificate 2.5.4.61
+olcObjectIdentifier: {10}id-at-attributeDescriptorCertificate 2.5.4.62
+olcObjectIdentifier: {11}id-at-attributeAuthorityRevocationList 2.5.4.63
+olcObjectIdentifier: {12}id-at-privPolicy 2.5.4.71
+olcObjectIdentifier: {13}id-at-role 2.5.4.72
+olcObjectIdentifier: {14}id-at-delegationPath 2.5.4.73
+olcObjectIdentifier: {15}id-at-protPrivPolicy 2.5.4.74
+olcObjectIdentifier: {16}id-at-xMLPrivilegeInfo 2.5.4.75
+olcObjectIdentifier: {17}id-at-xMLPprotPrivPolicy 2.5.4.76
+olcObjectIdentifier: {18}id-mr 2.5.13
+olcObjectIdentifier: {19}id-mr-attributeCertificateMatch id-mr:42
+olcObjectIdentifier: {20}id-mr-attributeCertificateExactMatch id-mr:45
+olcObjectIdentifier: {21}id-mr-holderIssuerMatch id-mr:46
+olcObjectIdentifier: {22}id-mr-authAttIdMatch id-mr:53
+olcObjectIdentifier: {23}id-mr-roleSpecCertIdMatch id-mr:54
+olcObjectIdentifier: {24}id-mr-basicAttConstraintsMatch id-mr:55
+olcObjectIdentifier: {25}id-mr-delegatedNameConstraintsMatch id-mr:56
+olcObjectIdentifier: {26}id-mr-timeSpecMatch id-mr:57
+olcObjectIdentifier: {27}id-mr-attDescriptorMatch id-mr:58
+olcObjectIdentifier: {28}id-mr-acceptableCertPoliciesMatch id-mr:59
+olcObjectIdentifier: {29}id-mr-delegationPathMatch id-mr:61
+olcObjectIdentifier: {30}id-mr-sOAIdentifierMatch id-mr:66
+olcObjectIdentifier: {31}id-mr-indirectIssuerMatch id-mr:67
+olcObjectIdentifier: {32}AttributeCertificate 1.3.6.1.4.1.4203.666.11.10.2.1
+olcObjectIdentifier: {33}CertificateList 1.3.6.1.4.1.1466.115.121.1.9
+olcObjectIdentifier: {34}AttCertPath 1.3.6.1.4.1.4203.666.11.10.2.4
+olcObjectIdentifier: {35}PolicySyntax 1.3.6.1.4.1.4203.666.11.10.2.5
+olcObjectIdentifier: {36}RoleSyntax 1.3.6.1.4.1.4203.666.11.10.2.6
+olcLdapSyntaxes: {0}( 1.3.6.1.4.1.4203.666.11.10.2.4 DESC 'X.509 PMI attribute
+ certificate path: SEQUENCE OF AttributeCertificate' X-SUBST '1.3.6.1.4.1.146
+ 6.115.121.1.15' )
+olcLdapSyntaxes: {1}( 1.3.6.1.4.1.4203.666.11.10.2.5 DESC 'X.509 PMI policy sy
+ ntax' X-SUBST '1.3.6.1.4.1.1466.115.121.1.15' )
+olcLdapSyntaxes: {2}( 1.3.6.1.4.1.4203.666.11.10.2.6 DESC 'X.509 PMI role synt
+ ax' X-SUBST '1.3.6.1.4.1.1466.115.121.1.15' )
+olcAttributeTypes: {0}( id-at-role NAME 'role' DESC 'X.509 Role attribute, use
+ ;binary' SYNTAX RoleSyntax )
+olcAttributeTypes: {1}( id-at-xMLPrivilegeInfo NAME 'xmlPrivilegeInfo' DESC 'X
+ .509 XML privilege information attribute' SYNTAX 1.3.6.1.4.1.1466.115.121.1.1
+ 5 )
+olcAttributeTypes: {2}( id-at-attributeCertificate NAME 'attributeCertificateA
+ ttribute' DESC 'X.509 Attribute certificate attribute, use ;binary' EQUALITY
+ attributeCertificateExactMatch SYNTAX AttributeCertificate )
+olcAttributeTypes: {3}( id-at-aACertificate NAME 'aACertificate' DESC 'X.509 A
+ A certificate attribute, use ;binary' EQUALITY attributeCertificateExactMatch
+ SYNTAX AttributeCertificate )
+olcAttributeTypes: {4}( id-at-attributeDescriptorCertificate NAME 'attributeDe
+ scriptorCertificate' DESC 'X.509 Attribute descriptor certificate attribute,
+ use ;binary' EQUALITY attributeCertificateExactMatch SYNTAX AttributeCertific
+ ate )
+olcAttributeTypes: {5}( id-at-attributeCertificateRevocationList NAME 'attribu
+ teCertificateRevocationList' DESC 'X.509 Attribute certificate revocation lis
+ t attribute, use ;binary' SYNTAX CertificateList X-EQUALITY 'certificateListE
+ xactMatch, not implemented yet' )
+olcAttributeTypes: {6}( id-at-attributeAuthorityRevocationList NAME 'attribute
+ AuthorityRevocationList' DESC 'X.509 AA certificate revocation list attribute
+ , use ;binary' SYNTAX CertificateList X-EQUALITY 'certificateListExactMatch,
+ not implemented yet' )
+olcAttributeTypes: {7}( id-at-delegationPath NAME 'delegationPath' DESC 'X.509
+ Delegation path attribute, use ;binary' SYNTAX AttCertPath )
+olcAttributeTypes: {8}( id-at-privPolicy NAME 'privPolicy' DESC 'X.509 Privile
+ ge policy attribute, use ;binary' SYNTAX PolicySyntax )
+olcAttributeTypes: {9}( id-at-protPrivPolicy NAME 'protPrivPolicy' DESC 'X.509
+ Protected privilege policy attribute, use ;binary' EQUALITY attributeCertifi
+ cateExactMatch SYNTAX AttributeCertificate )
+olcAttributeTypes: {10}( id-at-xMLPprotPrivPolicy NAME 'xmlPrivPolicy' DESC 'X
+ .509 XML Protected privilege policy attribute' SYNTAX 1.3.6.1.4.1.1466.115.12
+ 1.1.15 )
+olcObjectClasses: {0}( id-oc-pmiUser NAME 'pmiUser' DESC 'X.509 PMI user objec
+ t class' SUP top AUXILIARY MAY attributeCertificateAttribute )
+olcObjectClasses: {1}( id-oc-pmiAA NAME 'pmiAA' DESC 'X.509 PMI AA object clas
+ s' SUP top AUXILIARY MAY ( aACertificate $ attributeCertificateRevocationList
+ $ attributeAuthorityRevocationList ) )
+olcObjectClasses: {2}( id-oc-pmiSOA NAME 'pmiSOA' DESC 'X.509 PMI SOA object c
+ lass' SUP top AUXILIARY MAY ( attributeCertificateRevocationList $ attributeA
+ uthorityRevocationList $ attributeDescriptorCertificate ) )
+olcObjectClasses: {3}( id-oc-attCertCRLDistributionPts NAME 'attCertCRLDistrib
+ utionPt' DESC 'X.509 Attribute certificate CRL distribution point object clas
+ s' SUP top AUXILIARY MAY ( attributeCertificateRevocationList $ attributeAuth
+ orityRevocationList ) )
+olcObjectClasses: {4}( id-oc-pmiDelegationPath NAME 'pmiDelegationPath' DESC '
+ X.509 PMI delegation path' SUP top AUXILIARY MAY delegationPath )
+olcObjectClasses: {5}( id-oc-privilegePolicy NAME 'privilegePolicy' DESC 'X.50
+ 9 Privilege policy object class' SUP top AUXILIARY MAY privPolicy )
+olcObjectClasses: {6}( id-oc-protectedPrivilegePolicy NAME 'protectedPrivilege
+ Policy' DESC 'X.509 Protected privilege policy object class' SUP top AUXILIAR
+ Y MAY protPrivPolicy )
diff --git a/servers/slapd/schema_check.c b/servers/slapd/schema_check.c
new file mode 100644
index 0000000..b6090be
--- /dev/null
+++ b/servers/slapd/schema_check.c
@@ -0,0 +1,938 @@
+/* schema_check.c - routines to enforce schema definitions */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+static char * oc_check_required(
+ Entry *e,
+ ObjectClass *oc,
+ struct berval *ocname );
+
+static int entry_naming_check(
+ Entry *e,
+ int manage,
+ int add_naming,
+ const char** text,
+ char *textbuf, size_t textlen );
+/*
+ * entry_schema_check - check that entry e conforms to the schema required
+ * by its object class(es).
+ *
+ * returns 0 if so, non-zero otherwise.
+ */
+
+int
+entry_schema_check(
+ Operation *op,
+ Entry *e,
+ Attribute *oldattrs,
+ int manage,
+ int add,
+ Attribute **socp,
+ const char** text,
+ char *textbuf, size_t textlen )
+{
+ Attribute *a, *asc = NULL, *aoc = NULL;
+ ObjectClass *sc, *oc, **socs = NULL;
+ AttributeType *at;
+ ContentRule *cr;
+ int rc, i;
+ AttributeDescription *ad_structuralObjectClass
+ = slap_schema.si_ad_structuralObjectClass;
+ AttributeDescription *ad_objectClass
+ = slap_schema.si_ad_objectClass;
+ int extensible = 0;
+ int subentry = is_entry_subentry( e );
+ int collectiveSubentry = 0;
+
+ if ( SLAP_NO_SCHEMA_CHECK( op->o_bd )) {
+ return LDAP_SUCCESS;
+ }
+
+ if ( get_no_schema_check( op ) ) {
+ return LDAP_SUCCESS;
+ }
+
+ if( subentry ) {
+ collectiveSubentry = is_entry_collectiveAttributeSubentry( e );
+ }
+
+ *text = textbuf;
+
+ /* misc attribute checks */
+ for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
+ const char *type = a->a_desc->ad_cname.bv_val;
+
+ /* there should be at least one value */
+ assert( a->a_vals != NULL );
+ assert( a->a_vals[0].bv_val != NULL );
+
+ if( a->a_desc->ad_type->sat_check ) {
+ rc = (a->a_desc->ad_type->sat_check)(
+ op->o_bd, e, a, text, textbuf, textlen );
+ if( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+ }
+
+ if( a->a_desc == ad_structuralObjectClass )
+ asc = a;
+ else if ( a->a_desc == ad_objectClass )
+ aoc = a;
+
+ if( !collectiveSubentry && is_at_collective( a->a_desc->ad_type ) ) {
+ snprintf( textbuf, textlen,
+ "'%s' can only appear in collectiveAttributeSubentry",
+ type );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ /* if single value type, check for multiple values */
+ if( is_at_single_value( a->a_desc->ad_type ) &&
+ a->a_vals[1].bv_val != NULL )
+ {
+ snprintf( textbuf, textlen,
+ "attribute '%s' cannot have multiple values",
+ type );
+
+ Debug( LDAP_DEBUG_ANY,
+ "Entry (%s), %s\n",
+ e->e_dn, textbuf );
+
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ }
+
+ /* check the object class attribute */
+ if ( aoc == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "No objectClass for entry (%s)\n",
+ e->e_dn );
+
+ *text = "no objectClass attribute";
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ assert( aoc->a_vals != NULL );
+ assert( aoc->a_vals[0].bv_val != NULL );
+
+ /* check the structural object class attribute */
+ if ( asc == NULL && !add ) {
+ Debug( LDAP_DEBUG_ANY,
+ "No structuralObjectClass for entry (%s)\n",
+ e->e_dn );
+
+ *text = "no structuralObjectClass operational attribute";
+ return LDAP_OTHER;
+ }
+
+ rc = structural_class( aoc->a_vals, &oc, &socs, text, textbuf, textlen,
+ op->o_tmpmemctx );
+ if( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( asc == NULL && add ) {
+ attr_merge_one( e, ad_structuralObjectClass, &oc->soc_cname, NULL );
+ asc = attr_find( e->e_attrs, ad_structuralObjectClass );
+ sc = oc;
+ goto got_soc;
+ }
+
+ assert( asc->a_vals != NULL );
+ assert( asc->a_vals[0].bv_val != NULL );
+ assert( asc->a_vals[1].bv_val == NULL );
+
+ sc = oc_bvfind( &asc->a_vals[0] );
+ if( sc == NULL ) {
+ snprintf( textbuf, textlen,
+ "unrecognized structuralObjectClass '%s'",
+ asc->a_vals[0].bv_val );
+
+ Debug( LDAP_DEBUG_ANY,
+ "entry_check_schema(%s): %s\n",
+ e->e_dn, textbuf );
+
+ rc = LDAP_OBJECT_CLASS_VIOLATION;
+ goto done;
+ }
+
+ if( sc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
+ snprintf( textbuf, textlen,
+ "structuralObjectClass '%s' is not STRUCTURAL",
+ asc->a_vals[0].bv_val );
+
+ Debug( LDAP_DEBUG_ANY,
+ "entry_check_schema(%s): %s\n",
+ e->e_dn, textbuf );
+
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+got_soc:
+ if( !manage && sc->soc_obsolete ) {
+ snprintf( textbuf, textlen,
+ "structuralObjectClass '%s' is OBSOLETE",
+ asc->a_vals[0].bv_val );
+
+ Debug( LDAP_DEBUG_ANY,
+ "entry_check_schema(%s): %s\n",
+ e->e_dn, textbuf );
+
+ rc = LDAP_OBJECT_CLASS_VIOLATION;
+ goto done;
+ }
+
+ *text = textbuf;
+
+ if ( oc == NULL ) {
+ snprintf( textbuf, textlen,
+ "unrecognized objectClass '%s'",
+ aoc->a_vals[0].bv_val );
+ rc = LDAP_OBJECT_CLASS_VIOLATION;
+ goto done;
+
+ } else if ( sc != oc ) {
+ if ( !manage && sc != slap_schema.si_oc_glue ) {
+ snprintf( textbuf, textlen,
+ "structural object class modification "
+ "from '%s' to '%s' not allowed",
+ asc->a_vals[0].bv_val, oc->soc_cname.bv_val );
+ rc = LDAP_NO_OBJECT_CLASS_MODS;
+ goto done;
+ }
+
+ assert( asc->a_vals != NULL );
+ assert( !BER_BVISNULL( &asc->a_vals[0] ) );
+ assert( BER_BVISNULL( &asc->a_vals[1] ) );
+ assert( asc->a_nvals == asc->a_vals );
+
+ /* draft-zeilenga-ldap-relax: automatically modify
+ * structuralObjectClass if changed with relax */
+ sc = oc;
+ ber_bvreplace( &asc->a_vals[ 0 ], &sc->soc_cname );
+ if ( socp ) {
+ *socp = asc;
+ }
+ }
+
+ /* naming check */
+ if ( !is_entry_glue ( e ) ) {
+ rc = entry_naming_check( e, manage, add, text, textbuf, textlen );
+ if( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ } else {
+ /* Glue Entry */
+ }
+
+ /* find the content rule for the structural class */
+ cr = cr_find( sc->soc_oid );
+
+ /* the cr must be same as the structural class */
+ assert( !cr || !strcmp( cr->scr_oid, sc->soc_oid ) );
+
+ /* check that the entry has required attrs of the content rule */
+ if( cr ) {
+ if( !manage && cr->scr_obsolete ) {
+ snprintf( textbuf, textlen,
+ "content rule '%s' is obsolete",
+ ldap_contentrule2name( &cr->scr_crule ));
+
+ Debug( LDAP_DEBUG_ANY,
+ "Entry (%s): %s\n",
+ e->e_dn, textbuf );
+
+ rc = LDAP_OBJECT_CLASS_VIOLATION;
+ goto done;
+ }
+
+ if( cr->scr_required ) for( i=0; cr->scr_required[i]; i++ ) {
+ at = cr->scr_required[i];
+
+ for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
+ if( a->a_desc->ad_type == at ) {
+ break;
+ }
+ }
+
+ /* not there => schema violation */
+ if ( a == NULL ) {
+ snprintf( textbuf, textlen,
+ "content rule '%s' requires attribute '%s'",
+ ldap_contentrule2name( &cr->scr_crule ),
+ at->sat_cname.bv_val );
+
+ Debug( LDAP_DEBUG_ANY,
+ "Entry (%s): %s\n",
+ e->e_dn, textbuf );
+
+ rc = LDAP_OBJECT_CLASS_VIOLATION;
+ goto done;
+ }
+ }
+
+ if( cr->scr_precluded ) for( i=0; cr->scr_precluded[i]; i++ ) {
+ at = cr->scr_precluded[i];
+
+ for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
+ if( a->a_desc->ad_type == at ) {
+ break;
+ }
+ }
+
+ /* there => schema violation */
+ if ( a != NULL ) {
+ snprintf( textbuf, textlen,
+ "content rule '%s' precluded attribute '%s'",
+ ldap_contentrule2name( &cr->scr_crule ),
+ at->sat_cname.bv_val );
+
+ Debug( LDAP_DEBUG_ANY,
+ "Entry (%s): %s\n",
+ e->e_dn, textbuf );
+
+ rc = LDAP_OBJECT_CLASS_VIOLATION;
+ goto done;
+ }
+ }
+ }
+
+ /* check that the entry has required attrs for each oc */
+ for ( i = 0; socs[i]; i++ ) {
+ oc = socs[i];
+ if ( !manage && oc->soc_obsolete ) {
+ /* disallow obsolete classes */
+ snprintf( textbuf, textlen,
+ "objectClass '%s' is OBSOLETE",
+ aoc->a_vals[i].bv_val );
+
+ Debug( LDAP_DEBUG_ANY,
+ "entry_check_schema(%s): %s\n",
+ e->e_dn, textbuf );
+
+ rc = LDAP_OBJECT_CLASS_VIOLATION;
+ goto done;
+ }
+
+ if ( oc->soc_check ) {
+ rc = (oc->soc_check)( op->o_bd, e, oc,
+ text, textbuf, textlen );
+ if( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ }
+
+ if ( oc->soc_kind == LDAP_SCHEMA_ABSTRACT ) {
+ /* object class is abstract */
+ if ( oc != slap_schema.si_oc_top &&
+ !is_object_subclass( oc, sc ))
+ {
+ int j;
+ ObjectClass *xc = NULL;
+ for( j=0; socs[j]; j++ ) {
+ if( i != j ) {
+ xc = socs[j];
+
+ /* since we previous check against the
+ * structural object of this entry, the
+ * abstract class must be a (direct or indirect)
+ * superclass of one of the auxiliary classes of
+ * the entry.
+ */
+ if ( xc->soc_kind == LDAP_SCHEMA_AUXILIARY &&
+ is_object_subclass( oc, xc ) )
+ {
+ xc = NULL;
+ break;
+ }
+ }
+ }
+
+ if( xc != NULL ) {
+ snprintf( textbuf, textlen, "instantiation of "
+ "abstract objectClass '%s' not allowed",
+ aoc->a_vals[i].bv_val );
+
+ Debug( LDAP_DEBUG_ANY,
+ "entry_check_schema(%s): %s\n",
+ e->e_dn, textbuf );
+
+ rc = LDAP_OBJECT_CLASS_VIOLATION;
+ goto done;
+ }
+ }
+
+ } else if ( oc->soc_kind != LDAP_SCHEMA_STRUCTURAL || oc == sc ) {
+ char *s;
+
+ if( oc->soc_kind == LDAP_SCHEMA_AUXILIARY ) {
+ int k;
+
+ if( cr ) {
+ int j;
+
+ k = -1;
+ if( cr->scr_auxiliaries ) {
+ for( j = 0; cr->scr_auxiliaries[j]; j++ ) {
+ if( cr->scr_auxiliaries[j] == oc ) {
+ k = 0;
+ break;
+ }
+ }
+ }
+ if ( k ) {
+ snprintf( textbuf, textlen,
+ "class '%s' not allowed by content rule '%s'",
+ oc->soc_cname.bv_val,
+ ldap_contentrule2name( &cr->scr_crule ) );
+ }
+ } else if ( global_disallows & SLAP_DISALLOW_AUX_WO_CR ) {
+ k = -1;
+ snprintf( textbuf, textlen,
+ "class '%s' not allowed by any content rule",
+ oc->soc_cname.bv_val );
+ } else {
+ k = 0;
+ }
+
+ if( k == -1 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "Entry (%s): %s\n",
+ e->e_dn, textbuf );
+
+ rc = LDAP_OBJECT_CLASS_VIOLATION;
+ goto done;
+ }
+ }
+
+ s = oc_check_required( e, oc, &aoc->a_vals[i] );
+ if (s != NULL) {
+ snprintf( textbuf, textlen,
+ "object class '%s' requires attribute '%s'",
+ aoc->a_vals[i].bv_val, s );
+
+ Debug( LDAP_DEBUG_ANY,
+ "Entry (%s): %s\n",
+ e->e_dn, textbuf );
+
+ rc = LDAP_OBJECT_CLASS_VIOLATION;
+ goto done;
+ }
+
+ if( oc == slap_schema.si_oc_extensibleObject ) {
+ extensible=1;
+ }
+ }
+ }
+
+ if( extensible ) {
+ *text = NULL;
+ rc = LDAP_SUCCESS;
+ goto done;
+ }
+
+ /* check that each attr in the entry is allowed by some oc */
+ for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
+ rc = LDAP_OBJECT_CLASS_VIOLATION;
+
+ if( cr && cr->scr_required ) {
+ for( i=0; cr->scr_required[i]; i++ ) {
+ if( cr->scr_required[i] == a->a_desc->ad_type ) {
+ rc = LDAP_SUCCESS;
+ break;
+ }
+ }
+ }
+
+ if( rc != LDAP_SUCCESS && cr && cr->scr_allowed ) {
+ for( i=0; cr->scr_allowed[i]; i++ ) {
+ if( cr->scr_allowed[i] == a->a_desc->ad_type ) {
+ rc = LDAP_SUCCESS;
+ break;
+ }
+ }
+ }
+
+ if( rc != LDAP_SUCCESS )
+ {
+ rc = oc_check_allowed( a->a_desc->ad_type, socs, sc );
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ char *type = a->a_desc->ad_cname.bv_val;
+
+ snprintf( textbuf, textlen,
+ "attribute '%s' not allowed",
+ type );
+
+ Debug( LDAP_DEBUG_ANY,
+ "Entry (%s), %s\n",
+ e->e_dn, textbuf );
+
+ goto done;
+ }
+ }
+
+ *text = NULL;
+done:
+ slap_sl_free( socs, op->o_tmpmemctx );
+ return rc;
+}
+
+static char *
+oc_check_required(
+ Entry *e,
+ ObjectClass *oc,
+ struct berval *ocname )
+{
+ AttributeType *at;
+ int i;
+ Attribute *a;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "oc_check_required entry (%s), objectClass \"%s\"\n",
+ e->e_dn?e->e_dn:"(null)", ocname->bv_val );
+
+
+ /* check for empty oc_required */
+ if(oc->soc_required == NULL) {
+ return NULL;
+ }
+
+ /* for each required attribute */
+ for ( i = 0; oc->soc_required[i] != NULL; i++ ) {
+ at = oc->soc_required[i];
+ /* see if it's in the entry */
+ for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
+ if( a->a_desc->ad_type == at ) {
+ break;
+ }
+ }
+ /* not there => schema violation */
+ if ( a == NULL ) {
+ return at->sat_cname.bv_val;
+ }
+ }
+
+ return( NULL );
+}
+
+int oc_check_allowed(
+ AttributeType *at,
+ ObjectClass **socs,
+ ObjectClass *sc )
+{
+ int i, j;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "oc_check_allowed type \"%s\"\n",
+ at->sat_cname.bv_val );
+
+ /* always allow objectClass attribute */
+ if ( strcasecmp( at->sat_cname.bv_val, "objectClass" ) == 0 ) {
+ return LDAP_SUCCESS;
+ }
+
+ /*
+ * All operational attributions are allowed by schema rules.
+ */
+ if( is_at_operational(at) ) {
+ return LDAP_SUCCESS;
+ }
+
+ /* check to see if its allowed by the structuralObjectClass */
+ if( sc ) {
+ /* does it require the type? */
+ for ( j = 0; sc->soc_required != NULL &&
+ sc->soc_required[j] != NULL; j++ )
+ {
+ if( at == sc->soc_required[j] ) {
+ return LDAP_SUCCESS;
+ }
+ }
+
+ /* does it allow the type? */
+ for ( j = 0; sc->soc_allowed != NULL &&
+ sc->soc_allowed[j] != NULL; j++ )
+ {
+ if( at == sc->soc_allowed[j] ) {
+ return LDAP_SUCCESS;
+ }
+ }
+ }
+
+ /* check that the type appears as req or opt in at least one oc */
+ for ( i = 0; socs[i]; i++ ) {
+ /* if we know about the oc */
+ ObjectClass *oc = socs[i];
+ /* extensibleObject allows all */
+ if ( oc == slap_schema.si_oc_extensibleObject ) {
+ return LDAP_SUCCESS;
+ }
+ if ( oc != NULL && oc->soc_kind != LDAP_SCHEMA_ABSTRACT &&
+ ( sc == NULL || oc->soc_kind == LDAP_SCHEMA_AUXILIARY ))
+ {
+ /* does it require the type? */
+ for ( j = 0; oc->soc_required != NULL &&
+ oc->soc_required[j] != NULL; j++ )
+ {
+ if( at == oc->soc_required[j] ) {
+ return LDAP_SUCCESS;
+ }
+ }
+ /* does it allow the type? */
+ for ( j = 0; oc->soc_allowed != NULL &&
+ oc->soc_allowed[j] != NULL; j++ )
+ {
+ if( at == oc->soc_allowed[j] ) {
+ return LDAP_SUCCESS;
+ }
+ }
+ }
+ }
+
+ /* not allowed by any oc */
+ return LDAP_OBJECT_CLASS_VIOLATION;
+}
+
+/*
+ * Determine the structural object class from a set of OIDs
+ */
+int structural_class(
+ BerVarray ocs,
+ ObjectClass **scp,
+ ObjectClass ***socsp,
+ const char **text,
+ char *textbuf, size_t textlen,
+ void *ctx )
+{
+ int i, nocs;
+ ObjectClass *oc, **socs;
+ ObjectClass *sc = NULL;
+ int scn = -1;
+
+ *text = "structural_class: internal error";
+
+ /* count them */
+ for( i=0; ocs[i].bv_val; i++ ) ;
+ nocs = i;
+
+ socs = slap_sl_malloc( (nocs+1) * sizeof(ObjectClass *), ctx );
+
+ for( i=0; ocs[i].bv_val; i++ ) {
+ socs[i] = oc_bvfind( &ocs[i] );
+
+ if( socs[i] == NULL ) {
+ snprintf( textbuf, textlen,
+ "unrecognized objectClass '%s'",
+ ocs[i].bv_val );
+ *text = textbuf;
+ goto fail;
+ }
+ }
+ socs[i] = NULL;
+
+ for( i=0; ocs[i].bv_val; i++ ) {
+ oc = socs[i];
+ if( oc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) {
+ if( sc == NULL || is_object_subclass( sc, oc ) ) {
+ sc = oc;
+ scn = i;
+
+ } else if ( !is_object_subclass( oc, sc ) ) {
+ int j;
+ ObjectClass *xc = NULL;
+
+ /* find common superior */
+ for( j=i+1; ocs[j].bv_val; j++ ) {
+ xc = socs[j];
+
+ if( xc == NULL ) {
+ snprintf( textbuf, textlen,
+ "unrecognized objectClass '%s'",
+ ocs[j].bv_val );
+ *text = textbuf;
+ goto fail;
+ }
+
+ if( xc->soc_kind != LDAP_SCHEMA_STRUCTURAL ) {
+ xc = NULL;
+ continue;
+ }
+
+ if( is_object_subclass( sc, xc ) &&
+ is_object_subclass( oc, xc ) )
+ {
+ /* found common subclass */
+ break;
+ }
+
+ xc = NULL;
+ }
+
+ if( xc == NULL ) {
+ /* no common subclass */
+ snprintf( textbuf, textlen,
+ "invalid structural object class chain (%s/%s)",
+ ocs[scn].bv_val, ocs[i].bv_val );
+ *text = textbuf;
+ goto fail;
+ }
+ }
+ }
+ }
+
+ if( scp ) {
+ *scp = sc;
+ }
+
+ if( sc == NULL ) {
+ *text = "no structural object class provided";
+ goto fail;
+ }
+
+ if( scn < 0 ) {
+ *text = "invalid structural object class";
+ goto fail;
+ }
+
+ if ( socsp ) {
+ *socsp = socs;
+ } else {
+ slap_sl_free( socs, ctx );
+ }
+ *text = NULL;
+
+ return LDAP_SUCCESS;
+
+fail:
+ slap_sl_free( socs, ctx );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+}
+
+/*
+ * Return structural object class from list of modifications
+ */
+int mods_structural_class(
+ Modifications *mods,
+ struct berval *sc,
+ const char **text,
+ char *textbuf, size_t textlen, void *ctx )
+{
+ Modifications *ocmod = NULL;
+ ObjectClass *ssc;
+ int rc;
+
+ for( ; mods != NULL; mods = mods->sml_next ) {
+ if( mods->sml_desc == slap_schema.si_ad_objectClass ) {
+ if( ocmod != NULL ) {
+ *text = "entry has multiple objectClass attributes";
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+ ocmod = mods;
+ }
+ }
+
+ if( ocmod == NULL ) {
+ *text = "entry has no objectClass attribute";
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ if( ocmod->sml_values == NULL || ocmod->sml_values[0].bv_val == NULL ) {
+ *text = "objectClass attribute has no values";
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ rc = structural_class( ocmod->sml_values, &ssc, NULL,
+ text, textbuf, textlen, ctx );
+ if ( rc == LDAP_SUCCESS )
+ *sc = ssc->soc_cname;
+ return rc;
+}
+
+
+static int
+entry_naming_check(
+ Entry *e,
+ int manage,
+ int add_naming,
+ const char** text,
+ char *textbuf, size_t textlen )
+{
+ /* naming check */
+ LDAPRDN rdn = NULL;
+ const char *p = NULL;
+ ber_len_t cnt;
+ int rc = LDAP_SUCCESS;
+
+ if ( BER_BVISEMPTY( &e->e_name )) {
+ return LDAP_SUCCESS;
+ }
+
+ /*
+ * Get attribute type(s) and attribute value(s) of our RDN
+ */
+ if ( ldap_bv2rdn( &e->e_name, &rdn, (char **)&p,
+ LDAP_DN_FORMAT_LDAP ) )
+ {
+ *text = "unrecognized attribute type(s) in RDN";
+ return LDAP_INVALID_DN_SYNTAX;
+ }
+
+ /* Check that each AVA of the RDN is present in the entry */
+ /* FIXME: Should also check that each AVA lists a distinct type */
+ for ( cnt = 0; rdn[cnt]; cnt++ ) {
+ LDAPAVA *ava = rdn[cnt];
+ AttributeDescription *desc = NULL;
+ Attribute *attr;
+ const char *errtext;
+ int add = 0;
+
+ if( ava->la_flags & LDAP_AVA_BINARY ) {
+ snprintf( textbuf, textlen,
+ "value of naming attribute '%s' in unsupported BER form",
+ ava->la_attr.bv_val );
+ rc = LDAP_NAMING_VIOLATION;
+ break;
+ }
+
+ rc = slap_bv2ad( &ava->la_attr, &desc, &errtext );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( textbuf, textlen, "%s (in RDN)", errtext );
+ break;
+ }
+
+ if( desc->ad_type->sat_usage ) {
+ snprintf( textbuf, textlen,
+ "naming attribute '%s' is operational",
+ ava->la_attr.bv_val );
+ rc = LDAP_NAMING_VIOLATION;
+ break;
+ }
+
+ if( desc->ad_type->sat_collective ) {
+ snprintf( textbuf, textlen,
+ "naming attribute '%s' is collective",
+ ava->la_attr.bv_val );
+ rc = LDAP_NAMING_VIOLATION;
+ break;
+ }
+
+ if( !manage && desc->ad_type->sat_obsolete ) {
+ snprintf( textbuf, textlen,
+ "naming attribute '%s' is obsolete",
+ ava->la_attr.bv_val );
+ rc = LDAP_NAMING_VIOLATION;
+ break;
+ }
+
+ if( !desc->ad_type->sat_equality ) {
+ snprintf( textbuf, textlen,
+ "naming attribute '%s' has no equality matching rule",
+ ava->la_attr.bv_val );
+ rc = LDAP_NAMING_VIOLATION;
+ break;
+ }
+
+ if( !desc->ad_type->sat_equality->smr_match ) {
+ snprintf( textbuf, textlen,
+ "naming attribute '%s' has unsupported equality matching rule",
+ ava->la_attr.bv_val );
+ rc = LDAP_NAMING_VIOLATION;
+ break;
+ }
+
+ /* find the naming attribute */
+ attr = attr_find( e->e_attrs, desc );
+ if ( attr == NULL ) {
+ snprintf( textbuf, textlen,
+ "naming attribute '%s' is not present in entry",
+ ava->la_attr.bv_val );
+ if ( add_naming ) {
+ add = 1;
+
+ } else {
+ rc = LDAP_NAMING_VIOLATION;
+ }
+
+ } else {
+ rc = attr_valfind( attr, SLAP_MR_VALUE_OF_ASSERTION_SYNTAX|
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH,
+ &ava->la_value, NULL, NULL );
+
+ if ( rc != 0 ) {
+ switch( rc ) {
+ case LDAP_INAPPROPRIATE_MATCHING:
+ snprintf( textbuf, textlen,
+ "inappropriate matching for naming attribute '%s'",
+ ava->la_attr.bv_val );
+ break;
+ case LDAP_INVALID_SYNTAX:
+ snprintf( textbuf, textlen,
+ "value of naming attribute '%s' is invalid",
+ ava->la_attr.bv_val );
+ break;
+ case LDAP_NO_SUCH_ATTRIBUTE:
+ if ( add_naming ) {
+ if ( is_at_single_value( desc->ad_type ) ) {
+ snprintf( textbuf, textlen,
+ "value of single-valued naming attribute '%s' conflicts with value present in entry",
+ ava->la_attr.bv_val );
+
+ } else {
+ add = 1;
+ rc = LDAP_SUCCESS;
+ }
+
+ } else {
+ snprintf( textbuf, textlen,
+ "value of naming attribute '%s' is not present in entry",
+ ava->la_attr.bv_val );
+ }
+ break;
+ default:
+ snprintf( textbuf, textlen,
+ "naming attribute '%s' is inappropriate",
+ ava->la_attr.bv_val );
+ }
+
+ if ( !add ) {
+ rc = LDAP_NAMING_VIOLATION;
+ }
+ }
+ }
+
+ if ( add ) {
+ attr_merge_normalize_one( e, desc, &ava->la_value, NULL );
+
+ } else if ( rc != LDAP_SUCCESS ) {
+ break;
+ }
+ }
+
+ ldap_rdnfree( rdn );
+ return rc;
+}
+
diff --git a/servers/slapd/schema_init.c b/servers/slapd/schema_init.c
new file mode 100644
index 0000000..d753936
--- /dev/null
+++ b/servers/slapd/schema_init.c
@@ -0,0 +1,6978 @@
+/* schema_init.c - init builtin schema */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * Syntaxes - implementation notes:
+ *
+ * Validate function(syntax, value):
+ * Called before the other functions here to check if the value
+ * is valid according to the syntax.
+ *
+ * Pretty function(syntax, input value, output prettified...):
+ * If it exists, maps different notations of the same value to a
+ * unique representation which can be stored in the directory and
+ * possibly be passed to the Match/Indexer/Filter() functions.
+ *
+ * E.g. DN "2.5.4.3 = foo\,bar, o = BAZ" -> "cn=foo\2Cbar,o=BAZ",
+ * but unlike DN normalization, "BAZ" is not mapped to "baz".
+ */
+
+/*
+ * Matching rules - implementation notes:
+ *
+ * Matching rules match an attribute value (often from the directory)
+ * against an asserted value (e.g. from a filter).
+ *
+ * Invoked with validated and commonly pretty/normalized arguments, thus
+ * a number of matching rules can simply use the octetString functions.
+ *
+ * Normalize function(...input value, output normalized...):
+ * If it exists, maps matching values to a unique representation
+ * which is passed to the Match/Indexer/Filter() functions.
+ *
+ * Different matching rules can normalize values of the same syntax
+ * differently. E.g. caseIgnore rules normalize to lowercase,
+ * caseExact rules do not.
+ *
+ * Match function(*output matchp, ...value, asserted value):
+ * On success, set *matchp. 0 means match. For ORDERING/most EQUALITY,
+ * less/greater than 0 means value less/greater than asserted. However:
+ *
+ * In extensible match filters, ORDERING rules match if value<asserted.
+ *
+ * EQUALITY rules may order values differently than ORDERING rules for
+ * speed, since EQUALITY ordering is only used for SLAP_AT_SORTED_VAL.
+ * Some EQUALITY rules do not order values (ITS#6722).
+ *
+ * Indexer function(...attribute values, *output keysp,...):
+ * Generates index keys for the attribute values. Backends can store
+ * them in an index, a {key->entry ID set} mapping, for the attribute.
+ *
+ * A search can look up the DN/scope and asserted values in the
+ * indexes, if any, to narrow down the number of entries to check
+ * against the search criteria.
+ *
+ * Filter function(...asserted value, *output keysp,...):
+ * Generates index key(s) for the asserted value, to be looked up in
+ * the index from the Indexer function. *keysp is an array because
+ * substring matching rules can generate multiple lookup keys.
+ *
+ * Index keys:
+ * A key is usually a hash of match type, attribute value and schema
+ * info, because one index can contain keys for many filtering types.
+ *
+ * Some indexes instead have EQUALITY keys ordered so that if
+ * key(val1) < key(val2), then val1 < val2 by the ORDERING rule.
+ * That way the ORDERING rule can use the EQUALITY index.
+ *
+ * Substring indexing:
+ * This chops the attribute values up in small chunks and indexes all
+ * possible chunks of certain sizes. Substring filtering looks up
+ * SOME of the asserted value's chunks, and the caller uses the
+ * intersection of the resulting entry ID sets.
+ * See the index_substr_* keywords in slapd.conf(5).
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include <ac/ctype.h>
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "../../libraries/liblber/lber-int.h" /* get ber_ptrlen() */
+
+#include "ldap_utf8.h"
+
+#include "lutil.h"
+#include "lutil_hash.h"
+
+#ifdef LUTIL_HASH64_BYTES
+#define HASH_BYTES LUTIL_HASH64_BYTES
+#define HASH_LEN hashlen
+static void (*hashinit)(lutil_HASH_CTX *ctx) = lutil_HASHInit;
+static void (*hashupdate)(lutil_HASH_CTX *ctx,unsigned char const *buf, ber_len_t len) = lutil_HASHUpdate;
+static void (*hashfinal)(unsigned char digest[HASH_BYTES], lutil_HASH_CTX *ctx) = lutil_HASHFinal;
+static int hashlen = LUTIL_HASH_BYTES;
+#define HASH_Init(c) hashinit(c)
+#define HASH_Update(c,buf,len) hashupdate(c,buf,len)
+#define HASH_Final(d,c) hashfinal(d,c)
+
+/* Toggle between 32 and 64 bit hashing, default to 32 for compatibility
+ -1 to query, returns 1 if 64 bit, 0 if 32.
+ 0/1 to set 32/64, returns 0 on success, -1 on failure */
+int slap_hash64( int onoff )
+{
+ if ( onoff < 0 ) {
+ return hashlen == LUTIL_HASH64_BYTES;
+ } else if ( onoff ) {
+ hashinit = lutil_HASH64Init;
+ hashupdate = lutil_HASH64Update;
+ hashfinal = lutil_HASH64Final;
+ hashlen = LUTIL_HASH64_BYTES;
+ } else {
+ hashinit = lutil_HASHInit;
+ hashupdate = lutil_HASHUpdate;
+ hashfinal = lutil_HASHFinal;
+ hashlen = LUTIL_HASH_BYTES;
+ }
+ return 0;
+}
+
+#else
+#define HASH_BYTES LUTIL_HASH_BYTES
+#define HASH_LEN HASH_BYTES
+#define HASH_Init(c) lutil_HASHInit(c)
+#define HASH_Update(c,buf,len) lutil_HASHUpdate(c,buf,len)
+#define HASH_Final(d,c) lutil_HASHFinal(d,c)
+
+int slap_has64( int onoff )
+{
+ if ( onoff < 0 )
+ return 0;
+ else
+ return onoff ? -1 : 0;
+}
+
+#endif
+#define HASH_CONTEXT lutil_HASH_CTX
+
+/* approx matching rules */
+#define directoryStringApproxMatchOID "1.3.6.1.4.1.4203.666.4.4"
+#define directoryStringApproxMatch approxMatch
+#define directoryStringApproxIndexer approxIndexer
+#define directoryStringApproxFilter approxFilter
+#define IA5StringApproxMatchOID "1.3.6.1.4.1.4203.666.4.5"
+#define IA5StringApproxMatch approxMatch
+#define IA5StringApproxIndexer approxIndexer
+#define IA5StringApproxFilter approxFilter
+
+/* Change Sequence Number (CSN) - much of this will change */
+#define csnMatch octetStringMatch
+#define csnOrderingMatch octetStringOrderingMatch
+#define csnIndexer generalizedTimeIndexer
+#define csnFilter generalizedTimeFilter
+
+#define authzMatch octetStringMatch
+
+/* X.509 PMI ldapSyntaxes */
+/* FIXME: need to create temporary OIDs under OpenLDAP's arc;
+ * these are currently hijacked
+ *
+ * 1.3.6.1.4.1.4203.666 OpenLDAP
+ * 1.3.6.1.4.1.4203.666.11 self-contained works
+ * 1.3.6.1.4.1.4203.666.11.10 X.509 PMI
+ * 1.3.6.1.4.1.4203.666.11.10.2 X.509 PMI ldapSyntaxes
+ * 1.3.6.1.4.1.4203.666.11.10.2.1 AttributeCertificate (supported)
+ * 1.3.6.1.4.1.4203.666.11.10.2.2 AttributeCertificateExactAssertion (supported)
+ * 1.3.6.1.4.1.4203.666.11.10.2.3 AttributeCertificateAssertion (not supported)
+ * 1.3.6.1.4.1.4203.666.11.10.2.4 AttCertPath (X-SUBST'ed right now in pmi.schema)
+ * 1.3.6.1.4.1.4203.666.11.10.2.5 PolicySyntax (X-SUBST'ed right now in pmi.schema)
+ * 1.3.6.1.4.1.4203.666.11.10.2.6 RoleSyntax (X-SUBST'ed right now in pmi.schema)
+ */
+#if 0 /* from <draft-ietf-pkix-ldap-schema-02.txt> (expired) */
+#define attributeCertificateSyntaxOID "1.2.826.0.1.3344810.7.5"
+#define attributeCertificateExactAssertionSyntaxOID "1.2.826.0.1.3344810.7.6"
+#define attributeCertificateAssertionSyntaxOID "1.2.826.0.1.3344810.7.7"
+#else /* from OpenLDAP's experimental oid arc */
+#define X509_PMI_SyntaxOID "1.3.6.1.4.1.4203.666.11.10.2"
+#define attributeCertificateSyntaxOID X509_PMI_SyntaxOID ".1"
+#define attributeCertificateExactAssertionSyntaxOID X509_PMI_SyntaxOID ".2"
+#define attributeCertificateAssertionSyntaxOID X509_PMI_SyntaxOID ".3"
+#endif
+
+unsigned int index_substr_if_minlen = SLAP_INDEX_SUBSTR_IF_MINLEN_DEFAULT;
+unsigned int index_substr_if_maxlen = SLAP_INDEX_SUBSTR_IF_MAXLEN_DEFAULT;
+unsigned int index_substr_any_len = SLAP_INDEX_SUBSTR_ANY_LEN_DEFAULT;
+unsigned int index_substr_any_step = SLAP_INDEX_SUBSTR_ANY_STEP_DEFAULT;
+
+unsigned int index_intlen = SLAP_INDEX_INTLEN_DEFAULT;
+unsigned int index_intlen_strlen = SLAP_INDEX_INTLEN_STRLEN(
+ SLAP_INDEX_INTLEN_DEFAULT );
+
+ldap_pvt_thread_mutex_t ad_index_mutex;
+ldap_pvt_thread_mutex_t ad_undef_mutex;
+ldap_pvt_thread_mutex_t oc_undef_mutex;
+
+static int
+generalizedTimeValidate(
+ Syntax *syntax,
+ struct berval *in );
+
+#ifdef SUPPORT_OBSOLETE_UTC_SYNTAX
+static int
+utcTimeValidate(
+ Syntax *syntax,
+ struct berval *in );
+#endif /* SUPPORT_OBSOLETE_UTC_SYNTAX */
+
+static int
+inValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ /* no value allowed */
+ return LDAP_INVALID_SYNTAX;
+}
+
+static int
+blobValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ /* any value allowed */
+ return LDAP_SUCCESS;
+}
+
+#define berValidate blobValidate
+
+static int
+sequenceValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ if ( in->bv_len < 2 ) return LDAP_INVALID_SYNTAX;
+ if ( in->bv_val[0] != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+
+ return LDAP_SUCCESS;
+}
+
+/* X.509 related stuff */
+
+enum {
+ SLAP_X509_V1 = 0,
+ SLAP_X509_V2 = 1,
+ SLAP_X509_V3 = 2
+};
+
+enum {
+ SLAP_TAG_UTCTIME = 0x17U,
+ SLAP_TAG_GENERALIZEDTIME = 0x18U
+};
+
+
+#define SLAP_X509_OPTION (LBER_CLASS_CONTEXT|LBER_CONSTRUCTED)
+
+enum {
+ SLAP_X509_OPT_C_VERSION = SLAP_X509_OPTION + 0,
+ SLAP_X509_OPT_C_ISSUERUNIQUEID = LBER_CLASS_CONTEXT + 1,
+ SLAP_X509_OPT_C_SUBJECTUNIQUEID = LBER_CLASS_CONTEXT + 2,
+ SLAP_X509_OPT_C_EXTENSIONS = SLAP_X509_OPTION + 3
+};
+
+enum {
+ SLAP_X509_OPT_CL_CRLEXTENSIONS = SLAP_X509_OPTION + 0
+};
+
+/*
+GeneralName ::= CHOICE {
+ otherName [0] INSTANCE OF OTHER-NAME,
+ rfc822Name [1] IA5String,
+ dNSName [2] IA5String,
+ x400Address [3] ORAddress,
+ directoryName [4] Name,
+ ediPartyName [5] EDIPartyName,
+ uniformResourceIdentifier [6] IA5String,
+ iPAddress [7] OCTET STRING,
+ registeredID [8] OBJECT IDENTIFIER }
+*/
+enum {
+ SLAP_X509_GN_OTHERNAME = SLAP_X509_OPTION + 0,
+ SLAP_X509_GN_RFC822NAME = SLAP_X509_OPTION + 1,
+ SLAP_X509_GN_DNSNAME = SLAP_X509_OPTION + 2,
+ SLAP_X509_GN_X400ADDRESS = SLAP_X509_OPTION + 3,
+ SLAP_X509_GN_DIRECTORYNAME = SLAP_X509_OPTION + 4,
+ SLAP_X509_GN_EDIPARTYNAME = SLAP_X509_OPTION + 5,
+ SLAP_X509_GN_URI = SLAP_X509_OPTION + 6,
+ SLAP_X509_GN_IPADDRESS = SLAP_X509_OPTION + 7,
+ SLAP_X509_GN_REGISTEREDID = SLAP_X509_OPTION + 8
+};
+
+/* X.509 PMI related stuff */
+enum {
+ SLAP_X509AC_V1 = 0,
+ SLAP_X509AC_V2 = 1
+};
+
+enum {
+ SLAP_X509AC_ISSUER = SLAP_X509_OPTION + 0
+};
+
+/* X.509 certificate validation */
+static int
+certificateValidate( Syntax *syntax, struct berval *in )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len;
+ ber_int_t version = SLAP_X509_V1;
+
+ if ( BER_BVISNULL( in ) || BER_BVISEMPTY( in ))
+ return LDAP_INVALID_SYNTAX;
+
+ ber_init2( ber, in, LBER_USE_DER );
+ tag = ber_skip_tag( ber, &len ); /* Signed wrapper */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ tag = ber_skip_tag( ber, &len ); /* Sequence */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ tag = ber_peek_tag( ber, &len );
+ /* Optional version */
+ if ( tag == SLAP_X509_OPT_C_VERSION ) {
+ tag = ber_skip_tag( ber, &len );
+ tag = ber_get_int( ber, &version );
+ if ( tag != LBER_INTEGER ) return LDAP_INVALID_SYNTAX;
+ }
+ /* NOTE: don't try to parse Serial, because it might be longer
+ * than sizeof(ber_int_t); deferred to certificateExactNormalize() */
+ tag = ber_skip_tag( ber, &len ); /* Serial */
+ if ( tag != LBER_INTEGER ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len ); /* Signature Algorithm */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len ); /* Issuer DN */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len ); /* Validity */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len ); /* Subject DN */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len ); /* Subject PublicKeyInfo */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len );
+ if ( tag == SLAP_X509_OPT_C_ISSUERUNIQUEID ) { /* issuerUniqueID */
+ if ( version < SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len );
+ }
+ if ( tag == SLAP_X509_OPT_C_SUBJECTUNIQUEID ) { /* subjectUniqueID */
+ if ( version < SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len );
+ }
+ if ( tag == SLAP_X509_OPT_C_EXTENSIONS ) { /* Extensions */
+ if ( version < SLAP_X509_V3 ) return LDAP_INVALID_SYNTAX;
+ tag = ber_skip_tag( ber, &len );
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len );
+ }
+ /* signatureAlgorithm */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len );
+ /* Signature */
+ if ( tag != LBER_BITSTRING ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len );
+ /* Must be at end now */
+ if ( len || tag != LBER_DEFAULT ) return LDAP_INVALID_SYNTAX;
+ return LDAP_SUCCESS;
+}
+
+/* X.509 certificate list validation */
+static int
+checkTime( struct berval *in, struct berval *out );
+
+static int
+certificateListValidate( Syntax *syntax, struct berval *in )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len, wrapper_len;
+ char *wrapper_start;
+ int wrapper_ok = 0;
+ ber_int_t version = SLAP_X509_V1;
+ struct berval bvdn, bvtu;
+
+ ber_init2( ber, in, LBER_USE_DER );
+ tag = ber_skip_tag( ber, &wrapper_len ); /* Signed wrapper */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ wrapper_start = ber->ber_ptr;
+ tag = ber_skip_tag( ber, &len ); /* Sequence */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ tag = ber_peek_tag( ber, &len );
+ /* Optional version */
+ if ( tag == LBER_INTEGER ) {
+ tag = ber_get_int( ber, &version );
+ if ( tag != LBER_INTEGER || version != SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX;
+ }
+ tag = ber_skip_tag( ber, &len ); /* Signature Algorithm */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+ tag = ber_peek_tag( ber, &len ); /* Issuer DN */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ len = ber_ptrlen( ber );
+ bvdn.bv_val = in->bv_val + len;
+ bvdn.bv_len = in->bv_len - len;
+ tag = ber_skip_tag( ber, &len );
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len ); /* thisUpdate */
+ /* Time is a CHOICE { UTCTime, GeneralizedTime } */
+ if ( tag != SLAP_TAG_UTCTIME && tag != SLAP_TAG_GENERALIZEDTIME ) return LDAP_INVALID_SYNTAX;
+ bvtu.bv_val = (char *)ber->ber_ptr;
+ bvtu.bv_len = len;
+ ber_skip_data( ber, len );
+ /* Optional nextUpdate */
+ tag = ber_skip_tag( ber, &len );
+ if ( tag == SLAP_TAG_UTCTIME || tag == SLAP_TAG_GENERALIZEDTIME ) {
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len );
+ }
+ /* revokedCertificates - Sequence of Sequence, Optional */
+ if ( tag == LBER_SEQUENCE ) {
+ ber_len_t seqlen;
+ ber_tag_t stag;
+ stag = ber_peek_tag( ber, &seqlen );
+ if ( stag == LBER_SEQUENCE || !len ) {
+ /* RFC5280 requires non-empty, but X.509(2005) allows empty. */
+ if ( len )
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len );
+ }
+ }
+ /* Optional Extensions - Sequence of Sequence */
+ if ( tag == SLAP_X509_OPT_CL_CRLEXTENSIONS ) { /* ? */
+ ber_len_t seqlen;
+ if ( version != SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX;
+ tag = ber_peek_tag( ber, &seqlen );
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len );
+ }
+ /* signatureAlgorithm */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len );
+ /* Signature */
+ if ( tag != LBER_BITSTRING ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+ if ( ber->ber_ptr == wrapper_start + wrapper_len ) wrapper_ok = 1;
+ tag = ber_skip_tag( ber, &len );
+ /* Must be at end now */
+ /* NOTE: OpenSSL tolerates CL with garbage past the end */
+ if ( len || tag != LBER_DEFAULT ) {
+ struct berval issuer_dn = BER_BVNULL, thisUpdate;
+ char tubuf[STRLENOF("YYYYmmddHHMMSSZ") + 1];
+ int rc;
+
+ if ( ! wrapper_ok ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ rc = dnX509normalize( &bvdn, &issuer_dn );
+ if ( rc != LDAP_SUCCESS ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ thisUpdate.bv_val = tubuf;
+ thisUpdate.bv_len = sizeof(tubuf);
+ if ( checkTime( &bvtu, &thisUpdate ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_ANY,
+ "certificateListValidate issuer=\"%s\", thisUpdate=%s: extra cruft past end of certificateList\n",
+ issuer_dn.bv_val, thisUpdate.bv_val );
+
+done:;
+ if ( ! BER_BVISNULL( &issuer_dn ) ) {
+ ber_memfree( issuer_dn.bv_val );
+ }
+
+ return rc;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/* X.509 PMI Attribute Certificate Validate */
+static int
+attributeCertificateValidate( Syntax *syntax, struct berval *in )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len;
+ ber_int_t version;
+ int cont = 0;
+
+ ber_init2( ber, in, LBER_USE_DER );
+
+ tag = ber_skip_tag( ber, &len ); /* Signed wrapper */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+
+ tag = ber_skip_tag( ber, &len ); /* Sequence */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+
+ tag = ber_peek_tag( ber, &len ); /* Version */
+ if ( tag != LBER_INTEGER ) return LDAP_INVALID_SYNTAX;
+ tag = ber_get_int( ber, &version ); /* X.509 only allows v2 */
+ if ( version != SLAP_X509AC_V2 ) return LDAP_INVALID_SYNTAX;
+
+ tag = ber_skip_tag( ber, &len ); /* Holder */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+
+ tag = ber_skip_tag( ber, &len ); /* Issuer */
+ if ( tag != SLAP_X509AC_ISSUER ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+
+ tag = ber_skip_tag( ber, &len ); /* Signature */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+
+ tag = ber_skip_tag( ber, &len ); /* Serial number */
+ if ( tag != LBER_INTEGER ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+
+ tag = ber_skip_tag( ber, &len ); /* AttCertValidityPeriod */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+
+ tag = ber_skip_tag( ber, &len ); /* Attributes */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+
+ tag = ber_peek_tag( ber, &len );
+
+ if ( tag == LBER_BITSTRING ) { /* issuerUniqueID */
+ tag = ber_skip_tag( ber, &len );
+ ber_skip_data( ber, len );
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag == LBER_SEQUENCE ) { /* extensions or signatureAlgorithm */
+ tag = ber_skip_tag( ber, &len );
+ ber_skip_data( ber, len );
+ cont++;
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag == LBER_SEQUENCE ) { /* signatureAlgorithm */
+ tag = ber_skip_tag( ber, &len );
+ ber_skip_data( ber, len );
+ cont++;
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if ( tag == LBER_BITSTRING ) { /* Signature */
+ tag = ber_skip_tag( ber, &len );
+ ber_skip_data( ber, len );
+ cont++;
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ /* Must be at end now */
+ if ( len != 0 || tag != LBER_DEFAULT || cont < 2 ) return LDAP_INVALID_SYNTAX;
+
+ return LDAP_SUCCESS;
+}
+
+/* accept a PKCS#8 private key */
+static int
+privateKeyValidate(
+ Syntax *syntax,
+ struct berval *val )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len;
+ ber_int_t version;
+
+ ber_init2( ber, val, LBER_USE_DER );
+ tag = ber_skip_tag( ber, &len ); /* Sequence */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ tag = ber_peek_tag( ber, &len );
+ if ( tag != LBER_INTEGER ) {
+ /* might be an encrypted key */
+ if ( tag == LBER_SEQUENCE ) { /* encryptionAlgorithm */
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len ); /* encryptedData */
+ if ( tag != LBER_OCTETSTRING ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+ } else
+ return LDAP_INVALID_SYNTAX;
+ } else {
+ tag = ber_get_int( ber, &version );
+ tag = ber_skip_tag( ber, &len ); /* AlgorithmIdentifier */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len ); /* PrivateKey */
+ if ( tag != LBER_OCTETSTRING ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len );
+ if ( tag == LBER_SET ) { /* Optional Attributes */
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len );
+ }
+ }
+
+ /* Must be at end now */
+ if ( len || tag != LBER_DEFAULT ) return LDAP_INVALID_SYNTAX;
+ return LDAP_SUCCESS;
+}
+
+int
+octetStringMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ struct berval *asserted = (struct berval *) assertedValue;
+ ber_slen_t d = (ber_slen_t) value->bv_len - (ber_slen_t) asserted->bv_len;
+
+ /* For speed, order first by length, then by contents */
+ *matchp = d ? (sizeof(d) == sizeof(int) ? d : d < 0 ? -1 : 1)
+ : memcmp( value->bv_val, asserted->bv_val, value->bv_len );
+
+ return LDAP_SUCCESS;
+}
+
+int
+octetStringOrderingMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ struct berval *asserted = (struct berval *) assertedValue;
+ ber_len_t v_len = value->bv_len;
+ ber_len_t av_len = asserted->bv_len;
+
+ int match = memcmp( value->bv_val, asserted->bv_val,
+ (v_len < av_len ? v_len : av_len) );
+
+ if( match == 0 )
+ match = sizeof(v_len) == sizeof(int)
+ ? (int) v_len - (int) av_len
+ : v_len < av_len ? -1 : v_len > av_len;
+
+ /* If used in extensible match filter, match if value < asserted */
+ if ( flags & SLAP_MR_EXT )
+ match = (match >= 0);
+
+ *matchp = match;
+ return LDAP_SUCCESS;
+}
+
+/* Initialize HASHcontext from match type and schema info */
+static void
+hashPreset(
+ HASH_CONTEXT *HASHcontext,
+ struct berval *prefix,
+ char pre,
+ Syntax *syntax,
+ MatchingRule *mr)
+{
+ HASH_Init(HASHcontext);
+ if(prefix && prefix->bv_len > 0) {
+ HASH_Update(HASHcontext,
+ (unsigned char *)prefix->bv_val, prefix->bv_len);
+ }
+ if(pre) HASH_Update(HASHcontext, (unsigned char*)&pre, sizeof(pre));
+ HASH_Update(HASHcontext, (unsigned char*)syntax->ssyn_oid, syntax->ssyn_oidlen);
+ HASH_Update(HASHcontext, (unsigned char*)mr->smr_oid, mr->smr_oidlen);
+ return;
+}
+
+/* Set HASHdigest from HASHcontext and value:len */
+static void
+hashIter(
+ HASH_CONTEXT *HASHcontext,
+ unsigned char *HASHdigest,
+ unsigned char *value,
+ int len)
+{
+ HASH_CONTEXT ctx = *HASHcontext;
+ HASH_Update( &ctx, value, len );
+ HASH_Final( HASHdigest, &ctx );
+}
+
+/* Index generation function: Attribute values -> index hash keys */
+int octetStringIndexer(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ BerVarray values,
+ BerVarray *keysp,
+ void *ctx )
+{
+ int i;
+ BerVarray keys;
+ HASH_CONTEXT HASHcontext;
+ unsigned char HASHdigest[HASH_BYTES];
+ struct berval digest;
+ digest.bv_val = (char *)HASHdigest;
+ digest.bv_len = HASH_LEN;
+
+ for( i=0; !BER_BVISNULL( &values[i] ); i++ ) {
+ /* just count them */
+ }
+
+ /* we should have at least one value at this point */
+ assert( i > 0 );
+
+ keys = slap_sl_malloc( sizeof( struct berval ) * (i+1), ctx );
+
+ hashPreset( &HASHcontext, prefix, 0, syntax, mr);
+ for( i=0; !BER_BVISNULL( &values[i] ); i++ ) {
+ hashIter( &HASHcontext, HASHdigest,
+ (unsigned char *)values[i].bv_val, values[i].bv_len );
+ ber_dupbv_x( &keys[i], &digest, ctx );
+ }
+
+ BER_BVZERO( &keys[i] );
+
+ *keysp = keys;
+
+ return LDAP_SUCCESS;
+}
+
+/* Index generation function: Asserted value -> index hash key */
+int octetStringFilter(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ void * assertedValue,
+ BerVarray *keysp,
+ void *ctx )
+{
+ BerVarray keys;
+ HASH_CONTEXT HASHcontext;
+ unsigned char HASHdigest[HASH_BYTES];
+ struct berval *value = (struct berval *) assertedValue;
+ struct berval digest;
+ digest.bv_val = (char *)HASHdigest;
+ digest.bv_len = HASH_LEN;
+
+ keys = slap_sl_malloc( sizeof( struct berval ) * 2, ctx );
+
+ hashPreset( &HASHcontext, prefix, 0, syntax, mr );
+ hashIter( &HASHcontext, HASHdigest,
+ (unsigned char *)value->bv_val, value->bv_len );
+
+ ber_dupbv_x( keys, &digest, ctx );
+ BER_BVZERO( &keys[1] );
+
+ *keysp = keys;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+octetStringSubstringsMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ int match = 0;
+ SubstringsAssertion *sub = assertedValue;
+ struct berval left = *value;
+ int i;
+ ber_len_t inlen = 0;
+
+ /* Add up asserted input length */
+ if ( !BER_BVISNULL( &sub->sa_initial ) ) {
+ inlen += sub->sa_initial.bv_len;
+ }
+ if ( sub->sa_any ) {
+ for ( i = 0; !BER_BVISNULL( &sub->sa_any[i] ); i++ ) {
+ inlen += sub->sa_any[i].bv_len;
+ }
+ }
+ if ( !BER_BVISNULL( &sub->sa_final ) ) {
+ inlen += sub->sa_final.bv_len;
+ }
+
+ if ( !BER_BVISNULL( &sub->sa_initial ) ) {
+ if ( inlen > left.bv_len ) {
+ match = 1;
+ goto done;
+ }
+
+ match = memcmp( sub->sa_initial.bv_val, left.bv_val,
+ sub->sa_initial.bv_len );
+
+ if ( match != 0 ) {
+ goto done;
+ }
+
+ left.bv_val += sub->sa_initial.bv_len;
+ left.bv_len -= sub->sa_initial.bv_len;
+ inlen -= sub->sa_initial.bv_len;
+ }
+
+ if ( !BER_BVISNULL( &sub->sa_final ) ) {
+ if ( inlen > left.bv_len ) {
+ match = 1;
+ goto done;
+ }
+
+ match = memcmp( sub->sa_final.bv_val,
+ &left.bv_val[left.bv_len - sub->sa_final.bv_len],
+ sub->sa_final.bv_len );
+
+ if ( match != 0 ) {
+ goto done;
+ }
+
+ left.bv_len -= sub->sa_final.bv_len;
+ inlen -= sub->sa_final.bv_len;
+ }
+
+ if ( sub->sa_any ) {
+ for ( i = 0; !BER_BVISNULL( &sub->sa_any[i] ); i++ ) {
+ ber_len_t idx;
+ char *p;
+
+retry:
+ if ( inlen > left.bv_len ) {
+ /* not enough length */
+ match = 1;
+ goto done;
+ }
+
+ if ( BER_BVISEMPTY( &sub->sa_any[i] ) ) {
+ continue;
+ }
+
+ p = memchr( left.bv_val, *sub->sa_any[i].bv_val, left.bv_len );
+
+ if( p == NULL ) {
+ match = 1;
+ goto done;
+ }
+
+ idx = p - left.bv_val;
+
+ if ( idx >= left.bv_len ) {
+ /* this shouldn't happen */
+ return LDAP_OTHER;
+ }
+
+ left.bv_val = p;
+ left.bv_len -= idx;
+
+ if ( sub->sa_any[i].bv_len > left.bv_len ) {
+ /* not enough left */
+ match = 1;
+ goto done;
+ }
+
+ match = memcmp( left.bv_val,
+ sub->sa_any[i].bv_val,
+ sub->sa_any[i].bv_len );
+
+ if ( match != 0 ) {
+ left.bv_val++;
+ left.bv_len--;
+ goto retry;
+ }
+
+ left.bv_val += sub->sa_any[i].bv_len;
+ left.bv_len -= sub->sa_any[i].bv_len;
+ inlen -= sub->sa_any[i].bv_len;
+ }
+ }
+
+done:
+ *matchp = match;
+ return LDAP_SUCCESS;
+}
+
+/* Substring index generation function: Attribute values -> index hash keys */
+static int
+octetStringSubstringsIndexer(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ BerVarray values,
+ BerVarray *keysp,
+ void *ctx )
+{
+ ber_len_t i, nkeys;
+ BerVarray keys;
+
+ HASH_CONTEXT HCany, HCini, HCfin;
+ unsigned char HASHdigest[HASH_BYTES];
+ struct berval digest;
+ digest.bv_val = (char *)HASHdigest;
+ digest.bv_len = HASH_LEN;
+
+ nkeys = 0;
+
+ for ( i = 0; !BER_BVISNULL( &values[i] ); i++ ) {
+ /* count number of indices to generate */
+ if( flags & SLAP_INDEX_SUBSTR_INITIAL ) {
+ if( values[i].bv_len >= index_substr_if_maxlen ) {
+ nkeys += index_substr_if_maxlen -
+ (index_substr_if_minlen - 1);
+ } else if( values[i].bv_len >= index_substr_if_minlen ) {
+ nkeys += values[i].bv_len - (index_substr_if_minlen - 1);
+ }
+ }
+
+ if( flags & SLAP_INDEX_SUBSTR_ANY ) {
+ if( values[i].bv_len >= index_substr_any_len ) {
+ nkeys += values[i].bv_len - (index_substr_any_len - 1);
+ }
+ }
+
+ if( flags & SLAP_INDEX_SUBSTR_FINAL ) {
+ if( values[i].bv_len >= index_substr_if_maxlen ) {
+ nkeys += index_substr_if_maxlen -
+ (index_substr_if_minlen - 1);
+ } else if( values[i].bv_len >= index_substr_if_minlen ) {
+ nkeys += values[i].bv_len - (index_substr_if_minlen - 1);
+ }
+ }
+ }
+
+ if( nkeys == 0 ) {
+ /* no keys to generate */
+ *keysp = NULL;
+ return LDAP_SUCCESS;
+ }
+
+ keys = slap_sl_malloc( sizeof( struct berval ) * (nkeys+1), ctx );
+
+ if ( flags & SLAP_INDEX_SUBSTR_ANY )
+ hashPreset( &HCany, prefix, SLAP_INDEX_SUBSTR_PREFIX, syntax, mr );
+ if( flags & SLAP_INDEX_SUBSTR_INITIAL )
+ hashPreset( &HCini, prefix, SLAP_INDEX_SUBSTR_INITIAL_PREFIX, syntax, mr );
+ if( flags & SLAP_INDEX_SUBSTR_FINAL )
+ hashPreset( &HCfin, prefix, SLAP_INDEX_SUBSTR_FINAL_PREFIX, syntax, mr );
+
+ nkeys = 0;
+ for ( i = 0; !BER_BVISNULL( &values[i] ); i++ ) {
+ ber_len_t j,max;
+
+ if( ( flags & SLAP_INDEX_SUBSTR_ANY ) &&
+ ( values[i].bv_len >= index_substr_any_len ) )
+ {
+ max = values[i].bv_len - (index_substr_any_len - 1);
+
+ for( j=0; j<max; j++ ) {
+ hashIter( &HCany, HASHdigest,
+ (unsigned char *)&values[i].bv_val[j],
+ index_substr_any_len );
+ ber_dupbv_x( &keys[nkeys++], &digest, ctx );
+ }
+ }
+
+ /* skip if too short */
+ if( values[i].bv_len < index_substr_if_minlen ) continue;
+
+ max = index_substr_if_maxlen < values[i].bv_len
+ ? index_substr_if_maxlen : values[i].bv_len;
+
+ for( j=index_substr_if_minlen; j<=max; j++ ) {
+
+ if( flags & SLAP_INDEX_SUBSTR_INITIAL ) {
+ hashIter( &HCini, HASHdigest,
+ (unsigned char *)values[i].bv_val, j );
+ ber_dupbv_x( &keys[nkeys++], &digest, ctx );
+ }
+
+ if( flags & SLAP_INDEX_SUBSTR_FINAL ) {
+ hashIter( &HCfin, HASHdigest,
+ (unsigned char *)&values[i].bv_val[values[i].bv_len-j], j );
+ ber_dupbv_x( &keys[nkeys++], &digest, ctx );
+ }
+
+ }
+ }
+
+ if( nkeys > 0 ) {
+ BER_BVZERO( &keys[nkeys] );
+ *keysp = keys;
+ } else {
+ ch_free( keys );
+ *keysp = NULL;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/* Substring index generation function: Assertion value -> index hash keys */
+static int
+octetStringSubstringsFilter (
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ void * assertedValue,
+ BerVarray *keysp,
+ void *ctx)
+{
+ SubstringsAssertion *sa;
+ char pre;
+ ber_len_t nkeys = 0;
+ size_t klen;
+ BerVarray keys;
+ HASH_CONTEXT HASHcontext;
+ unsigned char HASHdigest[HASH_BYTES];
+ struct berval *value;
+ struct berval digest;
+
+ sa = (SubstringsAssertion *) assertedValue;
+
+ if( flags & SLAP_INDEX_SUBSTR_INITIAL &&
+ !BER_BVISNULL( &sa->sa_initial ) &&
+ sa->sa_initial.bv_len >= index_substr_if_minlen )
+ {
+ nkeys++;
+ if ( sa->sa_initial.bv_len > index_substr_if_maxlen &&
+ ( flags & SLAP_INDEX_SUBSTR_ANY ))
+ {
+ nkeys += 1 + (sa->sa_initial.bv_len - index_substr_if_maxlen) / index_substr_any_step;
+ }
+ }
+
+ if ( flags & SLAP_INDEX_SUBSTR_ANY && sa->sa_any != NULL ) {
+ ber_len_t i;
+ for( i=0; !BER_BVISNULL( &sa->sa_any[i] ); i++ ) {
+ if( sa->sa_any[i].bv_len >= index_substr_any_len ) {
+ /* don't bother accounting with stepping */
+ nkeys += sa->sa_any[i].bv_len -
+ ( index_substr_any_len - 1 );
+ }
+ }
+ }
+
+ if( flags & SLAP_INDEX_SUBSTR_FINAL &&
+ !BER_BVISNULL( &sa->sa_final ) &&
+ sa->sa_final.bv_len >= index_substr_if_minlen )
+ {
+ nkeys++;
+ if ( sa->sa_final.bv_len > index_substr_if_maxlen &&
+ ( flags & SLAP_INDEX_SUBSTR_ANY ))
+ {
+ nkeys += 1 + (sa->sa_final.bv_len - index_substr_if_maxlen) / index_substr_any_step;
+ }
+ }
+
+ if( nkeys == 0 ) {
+ *keysp = NULL;
+ return LDAP_SUCCESS;
+ }
+
+ digest.bv_val = (char *)HASHdigest;
+ digest.bv_len = HASH_LEN;
+
+ keys = slap_sl_malloc( sizeof( struct berval ) * (nkeys+1), ctx );
+ nkeys = 0;
+
+ if( flags & SLAP_INDEX_SUBSTR_INITIAL &&
+ !BER_BVISNULL( &sa->sa_initial ) &&
+ sa->sa_initial.bv_len >= index_substr_if_minlen )
+ {
+ pre = SLAP_INDEX_SUBSTR_INITIAL_PREFIX;
+ value = &sa->sa_initial;
+
+ klen = index_substr_if_maxlen < value->bv_len
+ ? index_substr_if_maxlen : value->bv_len;
+
+ hashPreset( &HASHcontext, prefix, pre, syntax, mr );
+ hashIter( &HASHcontext, HASHdigest,
+ (unsigned char *)value->bv_val, klen );
+ ber_dupbv_x( &keys[nkeys++], &digest, ctx );
+
+ /* If initial is too long and we have subany indexed, use it
+ * to match the excess...
+ */
+ if (value->bv_len > index_substr_if_maxlen && (flags & SLAP_INDEX_SUBSTR_ANY))
+ {
+ ber_len_t j;
+ pre = SLAP_INDEX_SUBSTR_PREFIX;
+ hashPreset( &HASHcontext, prefix, pre, syntax, mr);
+ for ( j=index_substr_if_maxlen-1; j <= value->bv_len - index_substr_any_len; j+=index_substr_any_step )
+ {
+ hashIter( &HASHcontext, HASHdigest,
+ (unsigned char *)&value->bv_val[j], index_substr_any_len );
+ ber_dupbv_x( &keys[nkeys++], &digest, ctx );
+ }
+ }
+ }
+
+ if( flags & SLAP_INDEX_SUBSTR_ANY && sa->sa_any != NULL ) {
+ ber_len_t i, j;
+ pre = SLAP_INDEX_SUBSTR_PREFIX;
+ klen = index_substr_any_len;
+
+ for( i=0; !BER_BVISNULL( &sa->sa_any[i] ); i++ ) {
+ if( sa->sa_any[i].bv_len < index_substr_any_len ) {
+ continue;
+ }
+
+ value = &sa->sa_any[i];
+
+ hashPreset( &HASHcontext, prefix, pre, syntax, mr);
+ for(j=0;
+ j <= value->bv_len - index_substr_any_len;
+ j += index_substr_any_step )
+ {
+ hashIter( &HASHcontext, HASHdigest,
+ (unsigned char *)&value->bv_val[j], klen );
+ ber_dupbv_x( &keys[nkeys++], &digest, ctx );
+ }
+ }
+ }
+
+ if( flags & SLAP_INDEX_SUBSTR_FINAL &&
+ !BER_BVISNULL( &sa->sa_final ) &&
+ sa->sa_final.bv_len >= index_substr_if_minlen )
+ {
+ pre = SLAP_INDEX_SUBSTR_FINAL_PREFIX;
+ value = &sa->sa_final;
+
+ klen = index_substr_if_maxlen < value->bv_len
+ ? index_substr_if_maxlen : value->bv_len;
+
+ hashPreset( &HASHcontext, prefix, pre, syntax, mr );
+ hashIter( &HASHcontext, HASHdigest,
+ (unsigned char *)&value->bv_val[value->bv_len-klen], klen );
+ ber_dupbv_x( &keys[nkeys++], &digest, ctx );
+
+ /* If final is too long and we have subany indexed, use it
+ * to match the excess...
+ */
+ if (value->bv_len > index_substr_if_maxlen && (flags & SLAP_INDEX_SUBSTR_ANY))
+ {
+ ber_len_t j;
+ pre = SLAP_INDEX_SUBSTR_PREFIX;
+ hashPreset( &HASHcontext, prefix, pre, syntax, mr);
+ for ( j=0; j <= value->bv_len - index_substr_if_maxlen; j+=index_substr_any_step )
+ {
+ hashIter( &HASHcontext, HASHdigest,
+ (unsigned char *)&value->bv_val[j], index_substr_any_len );
+ ber_dupbv_x( &keys[nkeys++], &digest, ctx );
+ }
+ }
+ }
+
+ if( nkeys > 0 ) {
+ BER_BVZERO( &keys[nkeys] );
+ *keysp = keys;
+ } else {
+ ch_free( keys );
+ *keysp = NULL;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+bitStringValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ ber_len_t i;
+
+ /* very unforgiving validation, requires no normalization
+ * before simplistic matching
+ */
+ if( in->bv_len < 3 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* RFC 4517 Section 3.3.2 Bit String:
+ * BitString = SQUOTE *binary-digit SQUOTE "B"
+ * binary-digit = "0" / "1"
+ *
+ * where SQUOTE [RFC4512] is
+ * SQUOTE = %x27 ; single quote ("'")
+ *
+ * Example: '0101111101'B
+ */
+
+ if( in->bv_val[0] != '\'' ||
+ in->bv_val[in->bv_len - 2] != '\'' ||
+ in->bv_val[in->bv_len - 1] != 'B' )
+ {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ for( i = in->bv_len - 3; i > 0; i-- ) {
+ if( in->bv_val[i] != '0' && in->bv_val[i] != '1' ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Syntaxes from RFC 4517
+ *
+
+3.3.2. Bit String
+
+ A value of the Bit String syntax is a sequence of binary digits. The
+ LDAP-specific encoding of a value of this syntax is defined by the
+ following ABNF:
+
+ BitString = SQUOTE *binary-digit SQUOTE "B"
+
+ binary-digit = "0" / "1"
+
+ The <SQUOTE> rule is defined in [MODELS].
+
+ Example:
+ '0101111101'B
+
+ The LDAP definition for the Bit String syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.6 DESC 'Bit String' )
+
+ This syntax corresponds to the BIT STRING ASN.1 type from [ASN.1].
+
+ ...
+
+3.3.21. Name and Optional UID
+
+ A value of the Name and Optional UID syntax is the distinguished name
+ [MODELS] of an entity optionally accompanied by a unique identifier
+ that serves to differentiate the entity from others with an identical
+ distinguished name.
+
+ The LDAP-specific encoding of a value of this syntax is defined by
+ the following ABNF:
+
+ NameAndOptionalUID = distinguishedName [ SHARP BitString ]
+
+ The <BitString> rule is defined in Section 3.3.2. The
+ <distinguishedName> rule is defined in [LDAPDN]. The <SHARP> rule is
+ defined in [MODELS].
+
+ Note that although the '#' character may occur in the string
+ representation of a distinguished name, no additional escaping of
+ this character is performed when a <distinguishedName> is encoded in
+ a <NameAndOptionalUID>.
+
+ Example:
+ 1.3.6.1.4.1.1466.0=#04024869,O=Test,C=GB#'0101'B
+
+ The LDAP definition for the Name and Optional UID syntax is:
+
+ ( 1.3.6.1.4.1.1466.115.121.1.34 DESC 'Name And Optional UID' )
+
+ This syntax corresponds to the NameAndOptionalUID ASN.1 type from
+ [X.520].
+
+ *
+ * RFC 4512 says:
+ *
+
+1.4. Common ABNF Productions
+
+ ...
+ SHARP = %x23 ; octothorpe (or sharp sign) ("#")
+ ...
+ SQUOTE = %x27 ; single quote ("'")
+ ...
+
+ *
+ * Note:
+ * RFC 4514 clarifies that SHARP, i.e. "#", doesn't have to
+ * be escaped except when at the beginning of a value, the
+ * definition of Name and Optional UID appears to be flawed,
+ * because there is no clear means to determine whether the
+ * UID part is present or not.
+ *
+ * Example:
+ *
+ * cn=Someone,dc=example,dc=com#'1'B
+ *
+ * could be either a NameAndOptionalUID with trailing UID, i.e.
+ *
+ * DN = "cn=Someone,dc=example,dc=com"
+ * UID = "'1'B"
+ *
+ * or a NameAndOptionalUID with no trailing UID, and the AVA
+ * in the last RDN made of
+ *
+ * attributeType = dc
+ * attributeValue = com#'1'B
+ *
+ * in fact "com#'1'B" is a valid IA5 string.
+ *
+ * As a consequence, current slapd code takes the presence of
+ * #<valid BitString> at the end of the string representation
+ * of a NameAndOptionalUID to mean this is indeed a BitString.
+ * This is quite arbitrary - it has changed the past and might
+ * change in the future.
+ */
+
+
+static int
+nameUIDValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ int rc;
+ struct berval dn, uid;
+
+ if( BER_BVISEMPTY( in ) ) return LDAP_SUCCESS;
+
+ ber_dupbv( &dn, in );
+ if( !dn.bv_val ) return LDAP_OTHER;
+
+ /* if there's a "#", try bitStringValidate()... */
+ uid.bv_val = strrchr( dn.bv_val, '#' );
+ if ( !BER_BVISNULL( &uid ) ) {
+ uid.bv_val++;
+ uid.bv_len = dn.bv_len - ( uid.bv_val - dn.bv_val );
+
+ rc = bitStringValidate( NULL, &uid );
+ if ( rc == LDAP_SUCCESS ) {
+ /* in case of success, trim the UID,
+ * otherwise treat it as part of the DN */
+ dn.bv_len -= uid.bv_len + 1;
+ uid.bv_val[-1] = '\0';
+ }
+ }
+
+ rc = dnValidate( NULL, &dn );
+
+ ber_memfree( dn.bv_val );
+ return rc;
+}
+
+int
+nameUIDPretty(
+ Syntax *syntax,
+ struct berval *val,
+ struct berval *out,
+ void *ctx )
+{
+ assert( val != NULL );
+ assert( out != NULL );
+
+
+ Debug( LDAP_DEBUG_TRACE, ">>> nameUIDPretty: <%s>\n", val->bv_val );
+
+ if( BER_BVISEMPTY( val ) ) {
+ ber_dupbv_x( out, val, ctx );
+
+ } else if ( val->bv_len > SLAP_LDAPDN_MAXLEN ) {
+ return LDAP_INVALID_SYNTAX;
+
+ } else {
+ int rc;
+ struct berval dnval = *val;
+ struct berval uidval = BER_BVNULL;
+
+ uidval.bv_val = strrchr( val->bv_val, '#' );
+ if ( !BER_BVISNULL( &uidval ) ) {
+ uidval.bv_val++;
+ uidval.bv_len = val->bv_len - ( uidval.bv_val - val->bv_val );
+
+ rc = bitStringValidate( NULL, &uidval );
+
+ if ( rc == LDAP_SUCCESS ) {
+ ber_dupbv_x( &dnval, val, ctx );
+ uidval.bv_val--;
+ dnval.bv_len -= ++uidval.bv_len;
+ dnval.bv_val[dnval.bv_len] = '\0';
+
+ } else {
+ BER_BVZERO( &uidval );
+ }
+ }
+
+ rc = dnPretty( syntax, &dnval, out, ctx );
+ if ( dnval.bv_val != val->bv_val ) {
+ slap_sl_free( dnval.bv_val, ctx );
+ }
+ if( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if( !BER_BVISNULL( &uidval ) ) {
+ char *tmp;
+
+ tmp = slap_sl_realloc( out->bv_val, out->bv_len
+ + uidval.bv_len + 1,
+ ctx );
+ if( tmp == NULL ) {
+ ber_memfree_x( out->bv_val, ctx );
+ return LDAP_OTHER;
+ }
+ out->bv_val = tmp;
+ memcpy( out->bv_val + out->bv_len, uidval.bv_val, uidval.bv_len );
+ out->bv_len += uidval.bv_len;
+ out->bv_val[out->bv_len] = '\0';
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<<< nameUIDPretty: <%s>\n", out->bv_val );
+
+ return LDAP_SUCCESS;
+}
+
+static int
+uniqueMemberNormalize(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ struct berval out;
+ int rc;
+
+ assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 );
+
+ ber_dupbv_x( &out, val, ctx );
+ if ( BER_BVISEMPTY( &out ) ) {
+ *normalized = out;
+
+ } else {
+ struct berval uid = BER_BVNULL;
+
+ uid.bv_val = strrchr( out.bv_val, '#' );
+ if ( !BER_BVISNULL( &uid ) ) {
+ uid.bv_val++;
+ uid.bv_len = out.bv_len - ( uid.bv_val - out.bv_val );
+
+ rc = bitStringValidate( NULL, &uid );
+ if ( rc == LDAP_SUCCESS ) {
+ uid.bv_val[-1] = '\0';
+ out.bv_len -= uid.bv_len + 1;
+ } else {
+ BER_BVZERO( &uid );
+ }
+ }
+
+ rc = dnNormalize( 0, NULL, NULL, &out, normalized, ctx );
+
+ if( rc != LDAP_SUCCESS ) {
+ slap_sl_free( out.bv_val, ctx );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if( !BER_BVISNULL( &uid ) ) {
+ char *tmp;
+
+ tmp = ch_realloc( normalized->bv_val,
+ normalized->bv_len + uid.bv_len
+ + STRLENOF("#") + 1 );
+ if ( tmp == NULL ) {
+ ber_memfree_x( normalized->bv_val, ctx );
+ return LDAP_OTHER;
+ }
+
+ normalized->bv_val = tmp;
+
+ /* insert the separator */
+ normalized->bv_val[normalized->bv_len++] = '#';
+
+ /* append the UID */
+ AC_MEMCPY( &normalized->bv_val[normalized->bv_len],
+ uid.bv_val, uid.bv_len );
+ normalized->bv_len += uid.bv_len;
+
+ /* terminate */
+ normalized->bv_val[normalized->bv_len] = '\0';
+ }
+
+ slap_sl_free( out.bv_val, ctx );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+uniqueMemberMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ int match;
+ struct berval *asserted = (struct berval *) assertedValue;
+ struct berval assertedDN = *asserted;
+ struct berval assertedUID = BER_BVNULL;
+ struct berval valueDN = *value;
+ struct berval valueUID = BER_BVNULL;
+ int approx = ((flags & SLAP_MR_EQUALITY_APPROX) == SLAP_MR_EQUALITY_APPROX);
+
+ if ( !BER_BVISEMPTY( asserted ) ) {
+ assertedUID.bv_val = strrchr( assertedDN.bv_val, '#' );
+ if ( !BER_BVISNULL( &assertedUID ) ) {
+ assertedUID.bv_val++;
+ assertedUID.bv_len = assertedDN.bv_len
+ - ( assertedUID.bv_val - assertedDN.bv_val );
+
+ if ( bitStringValidate( NULL, &assertedUID ) == LDAP_SUCCESS ) {
+ assertedDN.bv_len -= assertedUID.bv_len + 1;
+
+ } else {
+ BER_BVZERO( &assertedUID );
+ }
+ }
+ }
+
+ if ( !BER_BVISEMPTY( value ) ) {
+
+ valueUID.bv_val = strrchr( valueDN.bv_val, '#' );
+ if ( !BER_BVISNULL( &valueUID ) ) {
+ valueUID.bv_val++;
+ valueUID.bv_len = valueDN.bv_len
+ - ( valueUID.bv_val - valueDN.bv_val );
+
+ if ( bitStringValidate( NULL, &valueUID ) == LDAP_SUCCESS ) {
+ valueDN.bv_len -= valueUID.bv_len + 1;
+
+ } else {
+ BER_BVZERO( &valueUID );
+ }
+ }
+ }
+
+ if( valueUID.bv_len && assertedUID.bv_len ) {
+ ber_slen_t d;
+ d = (ber_slen_t) valueUID.bv_len - (ber_slen_t) assertedUID.bv_len;
+ if ( d ) {
+ *matchp = sizeof(d) == sizeof(int) ? d : d < 0 ? -1 : 1;
+ return LDAP_SUCCESS;
+ }
+
+ match = memcmp( valueUID.bv_val, assertedUID.bv_val, valueUID.bv_len );
+ if( match ) {
+ *matchp = match;
+ return LDAP_SUCCESS;
+ }
+
+ } else if ( !approx && valueUID.bv_len ) {
+ match = -1;
+ *matchp = match;
+ return LDAP_SUCCESS;
+
+ } else if ( !approx && assertedUID.bv_len ) {
+ match = 1;
+ *matchp = match;
+ return LDAP_SUCCESS;
+ }
+
+ return dnMatch( matchp, flags, syntax, mr, &valueDN, &assertedDN );
+}
+
+static int
+uniqueMemberIndexer(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ BerVarray values,
+ BerVarray *keysp,
+ void *ctx )
+{
+ BerVarray dnvalues;
+ int rc;
+ int i;
+ for( i=0; !BER_BVISNULL( &values[i] ); i++ ) {
+ /* just count them */
+ }
+ assert( i > 0 );
+
+ dnvalues = slap_sl_malloc( sizeof( struct berval ) * (i+1), ctx );
+
+ for( i=0; !BER_BVISNULL( &values[i] ); i++ ) {
+ struct berval assertedDN = values[i];
+ struct berval assertedUID = BER_BVNULL;
+
+ if ( !BER_BVISEMPTY( &assertedDN ) ) {
+ assertedUID.bv_val = strrchr( assertedDN.bv_val, '#' );
+ if ( !BER_BVISNULL( &assertedUID ) ) {
+ assertedUID.bv_val++;
+ assertedUID.bv_len = assertedDN.bv_len
+ - ( assertedUID.bv_val - assertedDN.bv_val );
+
+ if ( bitStringValidate( NULL, &assertedUID ) == LDAP_SUCCESS ) {
+ assertedDN.bv_len -= assertedUID.bv_len + 1;
+
+ } else {
+ BER_BVZERO( &assertedUID );
+ }
+ }
+ }
+
+ dnvalues[i] = assertedDN;
+ }
+ BER_BVZERO( &dnvalues[i] );
+
+ rc = octetStringIndexer( use, flags, syntax, mr, prefix,
+ dnvalues, keysp, ctx );
+
+ slap_sl_free( dnvalues, ctx );
+ return rc;
+}
+
+static int
+uniqueMemberFilter(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ void * assertedValue,
+ BerVarray *keysp,
+ void *ctx )
+{
+ struct berval *asserted = (struct berval *) assertedValue;
+ struct berval assertedDN = *asserted;
+ struct berval assertedUID = BER_BVNULL;
+
+ if ( !BER_BVISEMPTY( asserted ) ) {
+ assertedUID.bv_val = strrchr( assertedDN.bv_val, '#' );
+ if ( !BER_BVISNULL( &assertedUID ) ) {
+ assertedUID.bv_val++;
+ assertedUID.bv_len = assertedDN.bv_len
+ - ( assertedUID.bv_val - assertedDN.bv_val );
+
+ if ( bitStringValidate( NULL, &assertedUID ) == LDAP_SUCCESS ) {
+ assertedDN.bv_len -= assertedUID.bv_len + 1;
+
+ } else {
+ BER_BVZERO( &assertedUID );
+ }
+ }
+ }
+
+ return octetStringFilter( use, flags, syntax, mr, prefix,
+ &assertedDN, keysp, ctx );
+}
+
+
+/*
+ * Handling boolean syntax and matching is quite rigid.
+ * A more flexible approach would be to allow a variety
+ * of strings to be normalized and prettied into TRUE
+ * and FALSE.
+ */
+static int
+booleanValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ /* very unforgiving validation, requires no normalization
+ * before simplistic matching
+ */
+
+ if( in->bv_len == 4 ) {
+ if( bvmatch( in, &slap_true_bv ) ) {
+ return LDAP_SUCCESS;
+ }
+ } else if( in->bv_len == 5 ) {
+ if( bvmatch( in, &slap_false_bv ) ) {
+ return LDAP_SUCCESS;
+ }
+ }
+
+ return LDAP_INVALID_SYNTAX;
+}
+
+static int
+booleanMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ /* simplistic matching allowed by rigid validation */
+ struct berval *asserted = (struct berval *) assertedValue;
+ *matchp = (int) asserted->bv_len - (int) value->bv_len;
+ return LDAP_SUCCESS;
+}
+
+/*-------------------------------------------------------------------
+LDAP/X.500 string syntax / matching rules have a few oddities. This
+comment attempts to detail how slapd(8) treats them.
+
+Summary:
+ StringSyntax X.500 LDAP Matching/Comments
+ DirectoryString CHOICE UTF8 i/e + ignore insignificant spaces
+ PrintableString subset subset i/e + ignore insignificant spaces
+ PrintableString subset subset i/e + ignore insignificant spaces
+ NumericString subset subset ignore all spaces
+ IA5String ASCII ASCII i/e + ignore insignificant spaces
+ TeletexString T.61 T.61 i/e + ignore insignificant spaces
+
+ TelephoneNumber subset subset i + ignore all spaces and "-"
+
+ See RFC 4518 for details.
+
+
+Directory String -
+ In X.500(93), a directory string can be either a PrintableString,
+ a bmpString, or a UniversalString (e.g., UCS (a subset of Unicode)).
+ In later versions, more CHOICEs were added. In all cases the string
+ must be non-empty.
+
+ In LDAPv3, a directory string is a UTF-8 encoded UCS string.
+ A directory string cannot be zero length.
+
+ For matching, there are both case ignore and exact rules. Both
+ also require that "insignificant" spaces be ignored.
+ spaces before the first non-space are ignored;
+ spaces after the last non-space are ignored;
+ spaces after a space are ignored.
+ Note: by these rules (and as clarified in X.520), a string of only
+ spaces is to be treated as if held one space, not empty (which
+ would be a syntax error).
+
+NumericString
+ In ASN.1, numeric string is just a string of digits and spaces
+ and could be empty. However, in X.500, all attribute values of
+ numeric string carry a non-empty constraint. For example:
+
+ internationalISDNNumber ATTRIBUTE ::= {
+ WITH SYNTAX InternationalISDNNumber
+ EQUALITY MATCHING RULE numericStringMatch
+ SUBSTRINGS MATCHING RULE numericStringSubstringsMatch
+ ID id-at-internationalISDNNumber }
+ InternationalISDNNumber ::=
+ NumericString (SIZE(1..ub-international-isdn-number))
+
+ Unfortunately, some assertion values are don't carry the same
+ constraint (but its unclear how such an assertion could ever
+ be true). In LDAP, there is one syntax (numericString) not two
+ (numericString with constraint, numericString without constraint).
+ This should be treated as numericString with non-empty constraint.
+ Note that while someone may have no ISDN number, there are no ISDN
+ numbers which are zero length.
+
+ In matching, spaces are ignored.
+
+PrintableString
+ In ASN.1, Printable string is just a string of printable characters
+ and can be empty. In X.500, semantics much like NumericString (see
+ serialNumber for a like example) excepting uses insignificant space
+ handling instead of ignore all spaces. They must be non-empty.
+
+IA5String
+ Basically same as PrintableString. There are no examples in X.500,
+ but same logic applies. Empty strings are allowed.
+
+-------------------------------------------------------------------*/
+
+static int
+UTF8StringValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ int len;
+ unsigned char *u = (unsigned char *)in->bv_val, *end = (unsigned char *)in->bv_val + in->bv_len;
+
+ if( BER_BVISEMPTY( in ) && syntax == slap_schema.si_syn_directoryString ) {
+ /* directory strings cannot be empty */
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ for( ; u < end; u += len ) {
+ /* get the length indicated by the first byte */
+ len = LDAP_UTF8_CHARLEN2( u, len );
+
+ /* very basic checks */
+ switch( len ) {
+ case 6:
+ if( (u[5] & 0xC0) != 0x80 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ case 5:
+ if( (u[4] & 0xC0) != 0x80 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ case 4:
+ if( (u[3] & 0xC0) != 0x80 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ case 3:
+ if( (u[2] & 0xC0 )!= 0x80 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ case 2:
+ if( (u[1] & 0xC0) != 0x80 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ case 1:
+ /* CHARLEN already validated it */
+ break;
+ default:
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* make sure len corresponds with the offset
+ to the next character */
+ if( LDAP_UTF8_OFFSET( (char *)u ) != len ) return LDAP_INVALID_SYNTAX;
+ }
+
+ if( u > end ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+UTF8StringNormalize(
+ slap_mask_t use,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ struct berval tmp, nvalue;
+ int flags, wasspace;
+ ber_len_t i;
+
+ assert( SLAP_MR_IS_VALUE_OF_SYNTAX( use ) != 0 );
+
+ if( BER_BVISNULL( val ) ) {
+ /* assume we're dealing with a syntax (e.g., UTF8String)
+ * which allows empty strings
+ */
+ BER_BVZERO( normalized );
+ return LDAP_SUCCESS;
+ }
+
+ flags = SLAP_MR_ASSOCIATED( mr, slap_schema.si_mr_caseExactMatch )
+ ? LDAP_UTF8_NOCASEFOLD : LDAP_UTF8_CASEFOLD;
+ flags |= ( ( use & SLAP_MR_EQUALITY_APPROX ) == SLAP_MR_EQUALITY_APPROX )
+ ? LDAP_UTF8_APPROX : 0;
+
+ val = UTF8bvnormalize( val, &tmp, flags, ctx );
+ /* out of memory or syntax error, the former is unlikely */
+ if( val == NULL ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* collapse spaces (in place) */
+ nvalue.bv_len = 0;
+ nvalue.bv_val = tmp.bv_val;
+
+ /* trim leading spaces? */
+ wasspace = !((( use & SLAP_MR_SUBSTR_ANY ) == SLAP_MR_SUBSTR_ANY ) ||
+ (( use & SLAP_MR_SUBSTR_FINAL ) == SLAP_MR_SUBSTR_FINAL ));
+
+ for( i = 0; i < tmp.bv_len; i++) {
+ if ( ASCII_SPACE( tmp.bv_val[i] )) {
+ if( wasspace++ == 0 ) {
+ /* trim repeated spaces */
+ nvalue.bv_val[nvalue.bv_len++] = tmp.bv_val[i];
+ }
+ } else {
+ wasspace = 0;
+ nvalue.bv_val[nvalue.bv_len++] = tmp.bv_val[i];
+ }
+ }
+
+ if( !BER_BVISEMPTY( &nvalue ) ) {
+ /* trim trailing space? */
+ if( wasspace && (
+ (( use & SLAP_MR_SUBSTR_INITIAL ) != SLAP_MR_SUBSTR_INITIAL ) &&
+ ( use & SLAP_MR_SUBSTR_ANY ) != SLAP_MR_SUBSTR_ANY ))
+ {
+ --nvalue.bv_len;
+ }
+ nvalue.bv_val[nvalue.bv_len] = '\0';
+
+ } else if ( tmp.bv_len ) {
+ /* string of all spaces is treated as one space */
+ nvalue.bv_val[0] = ' ';
+ nvalue.bv_val[1] = '\0';
+ nvalue.bv_len = 1;
+ } /* should never be entered with 0-length val */
+
+ *normalized = nvalue;
+ return LDAP_SUCCESS;
+}
+
+static int
+directoryStringSubstringsMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ int match = 0;
+ SubstringsAssertion *sub = assertedValue;
+ struct berval left = *value;
+ ber_len_t i;
+ int priorspace=0;
+
+ if ( !BER_BVISNULL( &sub->sa_initial ) ) {
+ if ( sub->sa_initial.bv_len > left.bv_len ) {
+ /* not enough left */
+ match = 1;
+ goto done;
+ }
+
+ match = memcmp( sub->sa_initial.bv_val, left.bv_val,
+ sub->sa_initial.bv_len );
+
+ if ( match != 0 ) {
+ goto done;
+ }
+
+ left.bv_val += sub->sa_initial.bv_len;
+ left.bv_len -= sub->sa_initial.bv_len;
+
+ priorspace = ASCII_SPACE(
+ sub->sa_initial.bv_val[sub->sa_initial.bv_len] );
+ }
+
+ if ( sub->sa_any ) {
+ for ( i = 0; !BER_BVISNULL( &sub->sa_any[i] ); i++ ) {
+ ber_len_t idx;
+ char *p;
+
+ if( priorspace && !BER_BVISEMPTY( &sub->sa_any[i] )
+ && ASCII_SPACE( sub->sa_any[i].bv_val[0] ))
+ {
+ /* allow next space to match */
+ left.bv_val--;
+ left.bv_len++;
+ }
+ priorspace=0;
+
+retry:
+ if ( BER_BVISEMPTY( &sub->sa_any[i] ) ) {
+ continue;
+ }
+
+ if ( sub->sa_any[i].bv_len > left.bv_len ) {
+ /* not enough left */
+ match = 1;
+ goto done;
+ }
+
+ p = memchr( left.bv_val, *sub->sa_any[i].bv_val, left.bv_len );
+
+ if( p == NULL ) {
+ match = 1;
+ goto done;
+ }
+
+ idx = p - left.bv_val;
+
+ if ( idx >= left.bv_len ) {
+ /* this shouldn't happen */
+ return LDAP_OTHER;
+ }
+
+ left.bv_val = p;
+ left.bv_len -= idx;
+
+ if ( sub->sa_any[i].bv_len > left.bv_len ) {
+ /* not enough left */
+ match = 1;
+ goto done;
+ }
+
+ match = memcmp( left.bv_val,
+ sub->sa_any[i].bv_val,
+ sub->sa_any[i].bv_len );
+
+ if ( match != 0 ) {
+ left.bv_val++;
+ left.bv_len--;
+ goto retry;
+ }
+
+ left.bv_val += sub->sa_any[i].bv_len;
+ left.bv_len -= sub->sa_any[i].bv_len;
+
+ priorspace = ASCII_SPACE(
+ sub->sa_any[i].bv_val[sub->sa_any[i].bv_len] );
+ }
+ }
+
+ if ( !BER_BVISNULL( &sub->sa_final ) ) {
+ if( priorspace && !BER_BVISEMPTY( &sub->sa_final )
+ && ASCII_SPACE( sub->sa_final.bv_val[0] ))
+ {
+ /* allow next space to match */
+ left.bv_val--;
+ left.bv_len++;
+ }
+
+ if ( sub->sa_final.bv_len > left.bv_len ) {
+ /* not enough left */
+ match = 1;
+ goto done;
+ }
+
+ match = memcmp( sub->sa_final.bv_val,
+ &left.bv_val[left.bv_len - sub->sa_final.bv_len],
+ sub->sa_final.bv_len );
+
+ if ( match != 0 ) {
+ goto done;
+ }
+ }
+
+done:
+ *matchp = match;
+ return LDAP_SUCCESS;
+}
+
+#if defined(SLAPD_APPROX_INITIALS)
+# define SLAPD_APPROX_DELIMITER "._ "
+# define SLAPD_APPROX_WORDLEN 2
+#else
+# define SLAPD_APPROX_DELIMITER " "
+# define SLAPD_APPROX_WORDLEN 1
+#endif
+
+static int
+approxMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ struct berval *nval, *assertv;
+ char *val, **values, **words, *c;
+ int i, count, len, nextchunk=0, nextavail=0;
+
+ /* Yes, this is necessary */
+ nval = UTF8bvnormalize( value, NULL, LDAP_UTF8_APPROX, NULL );
+ if( nval == NULL ) {
+ *matchp = 1;
+ return LDAP_SUCCESS;
+ }
+
+ /* Yes, this is necessary */
+ assertv = UTF8bvnormalize( ((struct berval *)assertedValue),
+ NULL, LDAP_UTF8_APPROX, NULL );
+ if( assertv == NULL ) {
+ ber_bvfree( nval );
+ *matchp = 1;
+ return LDAP_SUCCESS;
+ }
+
+ /* Isolate how many words there are */
+ for ( c = nval->bv_val, count = 1; *c; c++ ) {
+ c = strpbrk( c, SLAPD_APPROX_DELIMITER );
+ if ( c == NULL ) break;
+ *c = '\0';
+ count++;
+ }
+
+ /* Get a phonetic copy of each word */
+ words = (char **)ch_malloc( count * sizeof(char *) );
+ values = (char **)ch_malloc( count * sizeof(char *) );
+ for ( c = nval->bv_val, i = 0; i < count; i++, c += strlen(c) + 1 ) {
+ words[i] = c;
+ values[i] = phonetic(c);
+ }
+
+ /* Work through the asserted value's words, to see if at least some
+ * of the words are there, in the same order. */
+ len = 0;
+ while ( (ber_len_t) nextchunk < assertv->bv_len ) {
+ len = strcspn( assertv->bv_val + nextchunk, SLAPD_APPROX_DELIMITER);
+ if( len == 0 ) {
+ nextchunk++;
+ continue;
+ }
+#if defined(SLAPD_APPROX_INITIALS)
+ else if( len == 1 ) {
+ /* Single letter words need to at least match one word's initial */
+ for( i=nextavail; i<count; i++ )
+ if( !strncasecmp( assertv->bv_val + nextchunk, words[i], 1 )) {
+ nextavail=i+1;
+ break;
+ }
+ }
+#endif
+ else {
+ /* Isolate the next word in the asserted value and phonetic it */
+ assertv->bv_val[nextchunk+len] = '\0';
+ val = phonetic( assertv->bv_val + nextchunk );
+
+ /* See if this phonetic chunk is in the remaining words of *value */
+ for( i=nextavail; i<count; i++ ){
+ if( !strcmp( val, values[i] ) ){
+ nextavail = i+1;
+ break;
+ }
+ }
+ ch_free( val );
+ }
+
+ /* This chunk in the asserted value was NOT within the *value. */
+ if( i >= count ) {
+ nextavail=-1;
+ break;
+ }
+
+ /* Go on to the next word in the asserted value */
+ nextchunk += len+1;
+ }
+
+ /* If some of the words were seen, call it a match */
+ if( nextavail > 0 ) {
+ *matchp = 0;
+ }
+ else {
+ *matchp = 1;
+ }
+
+ /* Cleanup allocs */
+ ber_bvfree( assertv );
+ for( i=0; i<count; i++ ) {
+ ch_free( values[i] );
+ }
+ ch_free( values );
+ ch_free( words );
+ ber_bvfree( nval );
+
+ return LDAP_SUCCESS;
+}
+
+static int
+approxIndexer(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ BerVarray values,
+ BerVarray *keysp,
+ void *ctx )
+{
+ char *c;
+ int i,j, len, wordcount, keycount=0;
+ struct berval *newkeys;
+ BerVarray keys=NULL;
+
+ for( j = 0; !BER_BVISNULL( &values[j] ); j++ ) {
+ struct berval val = BER_BVNULL;
+ /* Yes, this is necessary */
+ UTF8bvnormalize( &values[j], &val, LDAP_UTF8_APPROX, NULL );
+ assert( !BER_BVISNULL( &val ) );
+
+ /* Isolate how many words there are. There will be a key for each */
+ for( wordcount = 0, c = val.bv_val; *c; c++) {
+ len = strcspn(c, SLAPD_APPROX_DELIMITER);
+ if( len >= SLAPD_APPROX_WORDLEN ) wordcount++;
+ c+= len;
+ if (*c == '\0') break;
+ *c = '\0';
+ }
+
+ /* Allocate/increase storage to account for new keys */
+ newkeys = (struct berval *)ch_malloc( (keycount + wordcount + 1)
+ * sizeof(struct berval) );
+ AC_MEMCPY( newkeys, keys, keycount * sizeof(struct berval) );
+ if( keys ) ch_free( keys );
+ keys = newkeys;
+
+ /* Get a phonetic copy of each word */
+ for( c = val.bv_val, i = 0; i < wordcount; c += len + 1 ) {
+ len = strlen( c );
+ if( len < SLAPD_APPROX_WORDLEN ) continue;
+ ber_str2bv( phonetic( c ), 0, 0, &keys[keycount] );
+ if( keys[keycount].bv_len ) {
+ keycount++;
+ } else {
+ ch_free( keys[keycount].bv_val );
+ }
+ i++;
+ }
+
+ ber_memfree( val.bv_val );
+ }
+ BER_BVZERO( &keys[keycount] );
+ *keysp = keys;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+approxFilter(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ void * assertedValue,
+ BerVarray *keysp,
+ void *ctx )
+{
+ char *c;
+ int i, count, len;
+ struct berval *val;
+ BerVarray keys;
+
+ /* Yes, this is necessary */
+ val = UTF8bvnormalize( ((struct berval *)assertedValue),
+ NULL, LDAP_UTF8_APPROX, NULL );
+ if( val == NULL || BER_BVISNULL( val ) ) {
+ keys = (struct berval *)ch_malloc( sizeof(struct berval) );
+ BER_BVZERO( &keys[0] );
+ *keysp = keys;
+ ber_bvfree( val );
+ return LDAP_SUCCESS;
+ }
+
+ /* Isolate how many words there are. There will be a key for each */
+ for( count = 0,c = val->bv_val; *c; c++) {
+ len = strcspn(c, SLAPD_APPROX_DELIMITER);
+ if( len >= SLAPD_APPROX_WORDLEN ) count++;
+ c+= len;
+ if (*c == '\0') break;
+ *c = '\0';
+ }
+
+ /* Allocate storage for new keys */
+ keys = (struct berval *)ch_malloc( (count + 1) * sizeof(struct berval) );
+
+ /* Get a phonetic copy of each word */
+ for( c = val->bv_val, i = 0; i < count; c += len + 1 ) {
+ len = strlen(c);
+ if( len < SLAPD_APPROX_WORDLEN ) continue;
+ ber_str2bv( phonetic( c ), 0, 0, &keys[i] );
+ i++;
+ }
+
+ ber_bvfree( val );
+
+ BER_BVZERO( &keys[count] );
+ *keysp = keys;
+
+ return LDAP_SUCCESS;
+}
+
+/* Remove all spaces and '-' characters, unless the result would be empty */
+static int
+telephoneNumberNormalize(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ char *q;
+ ber_len_t c;
+
+ assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 );
+
+ /* Ensure q is big enough, though validator should have caught this */
+ if ( BER_BVISEMPTY( val )) {
+ BER_BVZERO( normalized );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ q = normalized->bv_val = slap_sl_malloc( val->bv_len + 1, ctx );
+
+ for( c = 0; c < val->bv_len; c++ ) {
+ if ( ! ( ASCII_SPACE( val->bv_val[c] ) || val->bv_val[c] == '-' )) {
+ *q++ = val->bv_val[c];
+ }
+ }
+ if ( q == normalized->bv_val ) {
+ *q++ = ' ';
+ }
+ *q = '\0';
+
+ normalized->bv_len = q - normalized->bv_val;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+postalAddressValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ struct berval bv = *in;
+ ber_len_t c;
+
+ for ( c = 0; c < in->bv_len; c++ ) {
+ if ( in->bv_val[c] == '\\' ) {
+ c++;
+ if ( strncasecmp( &in->bv_val[c], "24", STRLENOF( "24" ) ) != 0
+ && strncasecmp( &in->bv_val[c], "5C", STRLENOF( "5C" ) ) != 0 )
+ {
+ return LDAP_INVALID_SYNTAX;
+ }
+ continue;
+ }
+
+ if ( in->bv_val[c] == '$' ) {
+ bv.bv_len = &in->bv_val[c] - bv.bv_val;
+ if ( UTF8StringValidate( NULL, &bv ) != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ bv.bv_val = &in->bv_val[c] + 1;
+ }
+ }
+
+ bv.bv_len = &in->bv_val[c] - bv.bv_val;
+ return UTF8StringValidate( NULL, &bv );
+}
+
+static int
+postalAddressNormalize(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ BerVarray lines = NULL, nlines = NULL;
+ ber_len_t l, c;
+ int rc = LDAP_SUCCESS;
+ MatchingRule *xmr = NULL;
+ char *p;
+
+ if ( SLAP_MR_ASSOCIATED( mr, slap_schema.si_mr_caseIgnoreListMatch ) ) {
+ xmr = slap_schema.si_mr_caseIgnoreMatch;
+
+ } else {
+ xmr = slap_schema.si_mr_caseExactMatch;
+ }
+
+ for ( l = 0, c = 0; c < val->bv_len; c++ ) {
+ if ( val->bv_val[c] == '$' ) {
+ l++;
+ }
+ }
+
+ lines = slap_sl_calloc( sizeof( struct berval ), 2 * ( l + 2 ), ctx );
+ nlines = &lines[l + 2];
+
+ lines[0].bv_val = val->bv_val;
+ for ( l = 0, c = 0; c < val->bv_len; c++ ) {
+ if ( val->bv_val[c] == '$' ) {
+ lines[l].bv_len = &val->bv_val[c] - lines[l].bv_val;
+ l++;
+ lines[l].bv_val = &val->bv_val[c + 1];
+ }
+ }
+ lines[l].bv_len = &val->bv_val[c] - lines[l].bv_val;
+
+ normalized->bv_len = c = l;
+
+ for ( l = 0; l <= c; l++ ) {
+ /* NOTE: we directly normalize each line,
+ * without unescaping the values, since the special
+ * values '\24' ('$') and '\5C' ('\') are not affected
+ * by normalization */
+ if ( !lines[l].bv_len ) {
+ nlines[l].bv_len = 0;
+ nlines[l].bv_val = NULL;
+ continue;
+ }
+ rc = UTF8StringNormalize( usage, NULL, xmr, &lines[l], &nlines[l], ctx );
+ if ( rc != LDAP_SUCCESS ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ normalized->bv_len += nlines[l].bv_len;
+ }
+
+ normalized->bv_val = slap_sl_malloc( normalized->bv_len + 1, ctx );
+
+ p = normalized->bv_val;
+ for ( l = 0; l <= c ; l++ ) {
+ p = lutil_strbvcopy( p, &nlines[l] );
+ *p++ = '$';
+ }
+ *--p = '\0';
+
+ assert( p == &normalized->bv_val[normalized->bv_len] );
+
+done:;
+ if ( nlines != NULL ) {
+ for ( l = 0; !BER_BVISNULL( &nlines[ l ] ); l++ ) {
+ slap_sl_free( nlines[l].bv_val, ctx );
+ }
+
+ slap_sl_free( lines, ctx );
+ }
+
+ return rc;
+}
+
+int
+numericoidValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ struct berval val = *in;
+
+ if( BER_BVISEMPTY( &val ) ) {
+ /* disallow empty strings */
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ while( OID_LEADCHAR( val.bv_val[0] ) ) {
+ if ( val.bv_len == 1 ) {
+ return LDAP_SUCCESS;
+ }
+
+ if ( val.bv_val[0] == '0' && !OID_SEPARATOR( val.bv_val[1] )) {
+ break;
+ }
+
+ val.bv_val++;
+ val.bv_len--;
+
+ while ( OID_LEADCHAR( val.bv_val[0] )) {
+ val.bv_val++;
+ val.bv_len--;
+
+ if ( val.bv_len == 0 ) {
+ return LDAP_SUCCESS;
+ }
+ }
+
+ if( !OID_SEPARATOR( val.bv_val[0] )) {
+ break;
+ }
+
+ val.bv_val++;
+ val.bv_len--;
+ }
+
+ return LDAP_INVALID_SYNTAX;
+}
+
+static int
+integerValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ ber_len_t i;
+ struct berval val = *in;
+
+ if ( BER_BVISEMPTY( &val ) ) return LDAP_INVALID_SYNTAX;
+
+ if ( val.bv_val[0] == '-' ) {
+ val.bv_len--;
+ val.bv_val++;
+
+ if( BER_BVISEMPTY( &val ) ) { /* bare "-" */
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if( val.bv_val[0] == '0' ) { /* "-0" */
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ } else if ( val.bv_val[0] == '0' ) {
+ if( val.bv_len > 1 ) { /* "0<more>" */
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ return LDAP_SUCCESS;
+ }
+
+ for( i=0; i < val.bv_len; i++ ) {
+ if( !ASCII_DIGIT(val.bv_val[i]) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+integerMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ struct berval *asserted = (struct berval *) assertedValue;
+ int vsign = 1, asign = 1; /* default sign = '+' */
+ struct berval v, a;
+ int match;
+
+ v = *value;
+ if( v.bv_val[0] == '-' ) {
+ vsign = -1;
+ v.bv_val++;
+ v.bv_len--;
+ }
+
+ if( BER_BVISEMPTY( &v ) ) vsign = 0;
+
+ a = *asserted;
+ if( a.bv_val[0] == '-' ) {
+ asign = -1;
+ a.bv_val++;
+ a.bv_len--;
+ }
+
+ if( BER_BVISEMPTY( &a ) ) vsign = 0;
+
+ match = vsign - asign;
+ if( match == 0 ) {
+ match = ( v.bv_len != a.bv_len
+ ? ( v.bv_len < a.bv_len ? -1 : 1 )
+ : memcmp( v.bv_val, a.bv_val, v.bv_len ));
+ if( vsign < 0 ) match = -match;
+ }
+
+ /* Ordering rule used in extensible match filter? */
+ if ( (flags & SLAP_MR_EXT) && (mr->smr_usage & SLAP_MR_ORDERING) )
+ match = (match >= 0);
+
+ *matchp = match;
+ return LDAP_SUCCESS;
+}
+
+/* 10**Chop < 256**Chopbytes and Chop > Chopbytes<<1 (for sign bit and itmp) */
+#define INDEX_INTLEN_CHOP 7
+#define INDEX_INTLEN_CHOPBYTES 3
+
+static int
+integerVal2Key(
+ struct berval *in,
+ struct berval *key,
+ struct berval *tmp,
+ void *ctx )
+{
+ /* Integer index key format, designed for memcmp to collate correctly:
+ * if too large: one's complement sign*<approx exponent=chopped bytes>,
+ * two's complement value (sign-extended or chopped as needed),
+ * however in first byte above, the top <number of exponent-bytes + 1>
+ * bits are the inverse sign and next bit is the sign as delimiter.
+ */
+ ber_slen_t k = index_intlen_strlen;
+ ber_len_t chop = 0;
+ unsigned signmask = ~0x7fU;
+ unsigned char lenbuf[sizeof(k) + 2], *lenp, neg = 0xff;
+ struct berval val = *in, itmp = *tmp;
+
+ if ( val.bv_val[0] != '-' ) {
+ neg = 0;
+ --k;
+ }
+
+ /* Chop least significant digits, increase length instead */
+ if ( val.bv_len > (ber_len_t) k ) {
+ chop = (val.bv_len-k+2)/INDEX_INTLEN_CHOP; /* 2 fewer digits */
+ val.bv_len -= chop * INDEX_INTLEN_CHOP; /* #digits chopped */
+ chop *= INDEX_INTLEN_CHOPBYTES; /* #bytes added */
+ }
+
+ if ( lutil_str2bin( &val, &itmp, ctx )) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* Omit leading sign byte */
+ if ( itmp.bv_val[0] == neg ) {
+ itmp.bv_val++;
+ itmp.bv_len--;
+ }
+
+ k = (ber_slen_t) index_intlen - (ber_slen_t) (itmp.bv_len + chop);
+ if ( k > 0 ) {
+ assert( chop == 0 );
+ memset( key->bv_val, neg, k ); /* sign-extend */
+ } else if ( k != 0 || ((itmp.bv_val[0] ^ neg) & 0xc0) ) {
+ /* Got exponent -k, or no room for 2 sign bits */
+ lenp = lenbuf + sizeof(lenbuf);
+ chop = - (ber_len_t) k;
+ do {
+ *--lenp = ((unsigned char) chop & 0xff) ^ neg;
+ signmask >>= 1;
+ } while ( (chop >>= 8) != 0 || (signmask >> 1) & (*lenp ^ neg) );
+ /* With n bytes in lenbuf, the top n+1 bits of (signmask&0xff)
+ * are 1, and the top n+2 bits of lenp[0] are the sign bit. */
+ k = (lenbuf + sizeof(lenbuf)) - lenp;
+ if ( k > (ber_slen_t) index_intlen )
+ k = index_intlen;
+ memcpy( key->bv_val, lenp, k );
+ itmp.bv_len = index_intlen - k;
+ }
+ memcpy( key->bv_val + k, itmp.bv_val, itmp.bv_len );
+ key->bv_val[0] ^= (unsigned char) signmask & 0xff; /* invert sign */
+ return 0;
+}
+
+/* Index generation function: Ordered index */
+static int
+integerIndexer(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ BerVarray values,
+ BerVarray *keysp,
+ void *ctx )
+{
+ char ibuf[64];
+ struct berval itmp;
+ BerVarray keys;
+ ber_len_t vlen;
+ int i, rc;
+ unsigned maxstrlen = index_intlen_strlen + INDEX_INTLEN_CHOP-1;
+
+ /* count the values and find max needed length */
+ vlen = 0;
+ for( i = 0; !BER_BVISNULL( &values[i] ); i++ ) {
+ if ( vlen < values[i].bv_len )
+ vlen = values[i].bv_len;
+ }
+ if ( vlen > maxstrlen )
+ vlen = maxstrlen;
+
+ /* we should have at least one value at this point */
+ assert( i > 0 );
+
+ keys = slap_sl_malloc( sizeof( struct berval ) * (i+1), ctx );
+ for ( i = 0; !BER_BVISNULL( &values[i] ); i++ ) {
+ keys[i].bv_len = index_intlen;
+ keys[i].bv_val = slap_sl_malloc( index_intlen, ctx );
+ }
+ keys[i].bv_len = 0;
+ keys[i].bv_val = NULL;
+
+ if ( vlen > sizeof(ibuf) ) {
+ itmp.bv_val = slap_sl_malloc( vlen, ctx );
+ } else {
+ itmp.bv_val = ibuf;
+ }
+ itmp.bv_len = sizeof(ibuf);
+
+ for ( i=0; !BER_BVISNULL( &values[i] ); i++ ) {
+ if ( itmp.bv_val != ibuf ) {
+ itmp.bv_len = values[i].bv_len;
+ if ( itmp.bv_len <= sizeof(ibuf) )
+ itmp.bv_len = sizeof(ibuf);
+ else if ( itmp.bv_len > maxstrlen )
+ itmp.bv_len = maxstrlen;
+ }
+ rc = integerVal2Key( &values[i], &keys[i], &itmp, ctx );
+ if ( rc ) {
+ slap_sl_free( keys, ctx );
+ goto func_leave;
+ }
+ }
+ *keysp = keys;
+func_leave:
+ if ( itmp.bv_val != ibuf ) {
+ slap_sl_free( itmp.bv_val, ctx );
+ }
+ return rc;
+}
+
+/* Index generation function: Ordered index */
+static int
+integerFilter(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ void * assertedValue,
+ BerVarray *keysp,
+ void *ctx )
+{
+ char ibuf[64];
+ struct berval iv;
+ BerVarray keys;
+ struct berval *value;
+ int rc;
+
+ value = (struct berval *) assertedValue;
+
+ keys = slap_sl_malloc( sizeof( struct berval ) * 2, ctx );
+
+ keys[0].bv_len = index_intlen;
+ keys[0].bv_val = slap_sl_malloc( index_intlen, ctx );
+ keys[1].bv_len = 0;
+ keys[1].bv_val = NULL;
+
+ iv.bv_len = value->bv_len < index_intlen_strlen + INDEX_INTLEN_CHOP-1
+ ? value->bv_len : index_intlen_strlen + INDEX_INTLEN_CHOP-1;
+ if ( iv.bv_len > (int) sizeof(ibuf) ) {
+ iv.bv_val = slap_sl_malloc( iv.bv_len, ctx );
+ } else {
+ iv.bv_val = ibuf;
+ iv.bv_len = sizeof(ibuf);
+ }
+
+ rc = integerVal2Key( value, keys, &iv, ctx );
+
+ if ( iv.bv_val != ibuf ) {
+ slap_sl_free( iv.bv_val, ctx );
+ }
+
+ if ( rc == 0 )
+ *keysp = keys;
+ else
+ slap_sl_free( keys, ctx );
+
+ return rc;
+}
+
+static int
+countryStringValidate(
+ Syntax *syntax,
+ struct berval *val )
+{
+ if( val->bv_len != 2 ) return LDAP_INVALID_SYNTAX;
+
+ if( !SLAP_PRINTABLE(val->bv_val[0]) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ if( !SLAP_PRINTABLE(val->bv_val[1]) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+printableStringValidate(
+ Syntax *syntax,
+ struct berval *val )
+{
+ ber_len_t i;
+
+ if( BER_BVISEMPTY( val ) ) return LDAP_INVALID_SYNTAX;
+
+ for(i=0; i < val->bv_len; i++) {
+ if( !SLAP_PRINTABLE(val->bv_val[i]) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+printablesStringValidate(
+ Syntax *syntax,
+ struct berval *val )
+{
+ ber_len_t i, len;
+
+ if( BER_BVISEMPTY( val ) ) return LDAP_INVALID_SYNTAX;
+
+ for(i=0,len=0; i < val->bv_len; i++) {
+ int c = val->bv_val[i];
+
+ if( c == '$' ) {
+ if( len == 0 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ len = 0;
+
+ } else if ( SLAP_PRINTABLE(c) ) {
+ len++;
+ } else {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ if( len == 0 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+IA5StringValidate(
+ Syntax *syntax,
+ struct berval *val )
+{
+ ber_len_t i;
+
+ for(i=0; i < val->bv_len; i++) {
+ if( !LDAP_ASCII(val->bv_val[i]) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+IA5StringNormalize(
+ slap_mask_t use,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ char *p, *q, *end;
+ int casefold = !SLAP_MR_ASSOCIATED( mr,
+ slap_schema.si_mr_caseExactIA5Match );
+
+ assert( SLAP_MR_IS_VALUE_OF_SYNTAX( use ) != 0 );
+
+ p = val->bv_val;
+ end = val->bv_val + val->bv_len;
+
+ /* Ignore initial whitespace */
+ while ( p < end && ASCII_SPACE( *p ) ) p++;
+
+ normalized->bv_len = p < end ? (val->bv_len - ( p - val->bv_val )) : 0;
+ normalized->bv_val = slap_sl_malloc( normalized->bv_len + 1, ctx );
+ AC_MEMCPY( normalized->bv_val, p, normalized->bv_len );
+ normalized->bv_val[normalized->bv_len] = '\0';
+
+ p = q = normalized->bv_val;
+
+ while ( *p ) {
+ if ( ASCII_SPACE( *p ) ) {
+ *q++ = *p++;
+
+ /* Ignore the extra whitespace */
+ while ( ASCII_SPACE( *p ) ) {
+ p++;
+ }
+
+ } else if ( casefold ) {
+ /* Most IA5 rules require casefolding */
+ *q++ = TOLOWER(*p); p++;
+
+ } else {
+ *q++ = *p++;
+ }
+ }
+
+ assert( normalized->bv_val <= p );
+ assert( q <= p );
+
+ /*
+ * If the string ended in space, backup the pointer one
+ * position. One is enough because the above loop collapsed
+ * all whitespace to a single space.
+ */
+ if ( q > normalized->bv_val && ASCII_SPACE( q[-1] ) ) --q;
+
+ /* null terminate */
+ *q = '\0';
+
+ normalized->bv_len = q - normalized->bv_val;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+UUIDValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ int i;
+ if( in->bv_len != 36 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ for( i=0; i<36; i++ ) {
+ switch(i) {
+ case 8:
+ case 13:
+ case 18:
+ case 23:
+ if( in->bv_val[i] != '-' ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ break;
+ default:
+ if( !ASCII_HEX( in->bv_val[i]) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+UUIDPretty(
+ Syntax *syntax,
+ struct berval *in,
+ struct berval *out,
+ void *ctx )
+{
+ int i;
+ int rc=LDAP_INVALID_SYNTAX;
+
+ assert( in != NULL );
+ assert( out != NULL );
+
+ if( in->bv_len != 36 ) return LDAP_INVALID_SYNTAX;
+
+ out->bv_len = 36;
+ out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
+
+ for( i=0; i<36; i++ ) {
+ switch(i) {
+ case 8:
+ case 13:
+ case 18:
+ case 23:
+ if( in->bv_val[i] != '-' ) {
+ goto handle_error;
+ }
+ out->bv_val[i] = '-';
+ break;
+
+ default:
+ if( !ASCII_HEX( in->bv_val[i]) ) {
+ goto handle_error;
+ }
+ out->bv_val[i] = TOLOWER( in->bv_val[i] );
+ }
+ }
+
+ rc = LDAP_SUCCESS;
+ out->bv_val[ out->bv_len ] = '\0';
+
+ if( 0 ) {
+handle_error:
+ slap_sl_free( out->bv_val, ctx );
+ out->bv_val = NULL;
+ }
+
+ return rc;
+}
+
+int
+UUIDNormalize(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ unsigned char octet = '\0';
+ int i;
+ int j;
+
+ if ( SLAP_MR_IS_DENORMALIZE( usage ) ) {
+ /* NOTE: must be a normalized UUID */
+ if( val->bv_len != 16 )
+ return LDAP_INVALID_SYNTAX;
+
+ normalized->bv_val = slap_sl_malloc( LDAP_LUTIL_UUIDSTR_BUFSIZE, ctx );
+ normalized->bv_len = lutil_uuidstr_from_normalized( val->bv_val,
+ val->bv_len, normalized->bv_val, LDAP_LUTIL_UUIDSTR_BUFSIZE );
+ if( normalized->bv_len != STRLENOF( "BADBADBA-DBAD-0123-4567-BADBADBADBAD" ) )
+ return LDAP_INVALID_SYNTAX;
+
+ return LDAP_SUCCESS;
+ }
+
+ normalized->bv_len = 16;
+ normalized->bv_val = slap_sl_malloc( normalized->bv_len + 1, ctx );
+
+ for( i=0, j=0; i<36; i++ ) {
+ unsigned char nibble;
+ if( val->bv_val[i] == '-' ) {
+ continue;
+
+ } else if( ASCII_DIGIT( val->bv_val[i] ) ) {
+ nibble = val->bv_val[i] - '0';
+
+ } else if( ASCII_HEXLOWER( val->bv_val[i] ) ) {
+ nibble = val->bv_val[i] - ('a'-10);
+
+ } else if( ASCII_HEXUPPER( val->bv_val[i] ) ) {
+ nibble = val->bv_val[i] - ('A'-10);
+
+ } else {
+ slap_sl_free( normalized->bv_val, ctx );
+ BER_BVZERO( normalized );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if( j & 1 ) {
+ octet |= nibble;
+ normalized->bv_val[j>>1] = octet;
+ } else {
+ octet = nibble << 4;
+ }
+ j++;
+ }
+
+ normalized->bv_val[normalized->bv_len] = 0;
+ return LDAP_SUCCESS;
+}
+
+
+
+int
+numericStringValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ ber_len_t i;
+
+ if( BER_BVISEMPTY( in ) ) return LDAP_INVALID_SYNTAX;
+
+ for(i=0; i < in->bv_len; i++) {
+ if( !SLAP_NUMERIC(in->bv_val[i]) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+numericStringNormalize(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ /* removal all spaces */
+ char *p, *q;
+
+ assert( !BER_BVISEMPTY( val ) );
+
+ normalized->bv_val = slap_sl_malloc( val->bv_len + 1, ctx );
+
+ p = val->bv_val;
+ q = normalized->bv_val;
+
+ while ( *p ) {
+ if ( ASCII_SPACE( *p ) ) {
+ /* Ignore whitespace */
+ p++;
+ } else {
+ *q++ = *p++;
+ }
+ }
+
+ /* we should have copied no more than is in val */
+ assert( (q - normalized->bv_val) <= (p - val->bv_val) );
+
+ /* null terminate */
+ *q = '\0';
+
+ normalized->bv_len = q - normalized->bv_val;
+
+ if( BER_BVISEMPTY( normalized ) ) {
+ normalized->bv_val = slap_sl_realloc( normalized->bv_val, 2, ctx );
+ normalized->bv_val[0] = ' ';
+ normalized->bv_val[1] = '\0';
+ normalized->bv_len = 1;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Integer conversion macros that will use the largest available
+ * type.
+ */
+#if defined(HAVE_STRTOLL) && defined(HAVE_LONG_LONG)
+# define SLAP_STRTOL(n,e,b) strtoll(n,e,b)
+# define SLAP_LONG long long
+#else
+# define SLAP_STRTOL(n,e,b) strtol(n,e,b)
+# define SLAP_LONG long
+#endif /* HAVE_STRTOLL ... */
+
+static int
+integerBitAndMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ SLAP_LONG lValue, lAssertedValue;
+
+ errno = 0;
+ /* safe to assume integers are NUL terminated? */
+ lValue = SLAP_STRTOL(value->bv_val, NULL, 10);
+ if( errno == ERANGE )
+ {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ lAssertedValue = SLAP_STRTOL(((struct berval *)assertedValue)->bv_val,
+ NULL, 10);
+ if( errno == ERANGE )
+ {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ *matchp = ((lValue & lAssertedValue) == lAssertedValue) ? 0 : 1;
+ return LDAP_SUCCESS;
+}
+
+static int
+integerBitOrMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ SLAP_LONG lValue, lAssertedValue;
+
+ errno = 0;
+ /* safe to assume integers are NUL terminated? */
+ lValue = SLAP_STRTOL(value->bv_val, NULL, 10);
+ if( errno == ERANGE )
+ {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ lAssertedValue = SLAP_STRTOL( ((struct berval *)assertedValue)->bv_val,
+ NULL, 10);
+ if( errno == ERANGE )
+ {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ *matchp = ((lValue & lAssertedValue) != 0) ? 0 : -1;
+ return LDAP_SUCCESS;
+}
+
+static int
+checkNum( struct berval *in, struct berval *out )
+{
+ /* parse serialNumber */
+ ber_len_t neg = 0, extra = 0;
+ char first = '\0';
+
+ out->bv_val = in->bv_val;
+ out->bv_len = 0;
+
+ if ( out->bv_val[0] == '-' ) {
+ neg++;
+ out->bv_len++;
+ }
+
+ if ( strncasecmp( out->bv_val, "0x", STRLENOF("0x") ) == 0 ) {
+ first = out->bv_val[2];
+ extra = 2;
+
+ out->bv_len += STRLENOF("0x");
+ for ( ; out->bv_len < in->bv_len; out->bv_len++ ) {
+ if ( !ASCII_HEX( out->bv_val[out->bv_len] ) ) break;
+ }
+
+ } else if ( out->bv_val[0] == '\'' ) {
+ first = out->bv_val[1];
+ extra = 3;
+
+ out->bv_len += STRLENOF("'");
+
+ for ( ; out->bv_len < in->bv_len; out->bv_len++ ) {
+ if ( !ASCII_HEX( out->bv_val[out->bv_len] ) ) break;
+ }
+ if ( strncmp( &out->bv_val[out->bv_len], "'H", STRLENOF("'H") ) != 0 ) {
+ return -1;
+ }
+ out->bv_len += STRLENOF("'H");
+
+ } else {
+ first = out->bv_val[0];
+ for ( ; out->bv_len < in->bv_len; out->bv_len++ ) {
+ if ( !ASCII_DIGIT( out->bv_val[out->bv_len] ) ) break;
+ }
+ }
+
+ if ( !( out->bv_len > neg ) ) {
+ return -1;
+ }
+
+ if ( ( out->bv_len > extra + 1 + neg ) && ( first == '0' ) ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+serialNumberAndIssuerCheck(
+ struct berval *in,
+ struct berval *sn,
+ struct berval *is,
+ void *ctx )
+{
+ ber_len_t n;
+
+ if( in->bv_len < 3 ) return LDAP_INVALID_SYNTAX;
+
+ if( in->bv_val[0] != '{' || in->bv_val[in->bv_len-1] != '}' ) {
+ /* Parse old format */
+ is->bv_val = ber_bvchr( in, '$' );
+ if( BER_BVISNULL( is ) ) return LDAP_INVALID_SYNTAX;
+
+ sn->bv_val = in->bv_val;
+ sn->bv_len = is->bv_val - in->bv_val;
+
+ is->bv_val++;
+ is->bv_len = in->bv_len - (sn->bv_len + 1);
+
+ /* eat leading zeros */
+ for( n=0; n < (sn->bv_len-1); n++ ) {
+ if( sn->bv_val[n] != '0' ) break;
+ }
+ sn->bv_val += n;
+ sn->bv_len -= n;
+
+ for( n=0; n < sn->bv_len; n++ ) {
+ if( !ASCII_DIGIT(sn->bv_val[n]) ) return LDAP_INVALID_SYNTAX;
+ }
+
+ } else {
+ /* Parse GSER format */
+ enum {
+ HAVE_NONE = 0x0,
+ HAVE_ISSUER = 0x1,
+ HAVE_SN = 0x2,
+ HAVE_ALL = ( HAVE_ISSUER | HAVE_SN )
+ } have = HAVE_NONE;
+
+ int numdquotes = 0, gotquote;
+ struct berval x = *in;
+ struct berval ni;
+ x.bv_val++;
+ x.bv_len -= 2;
+
+ do {
+ /* eat leading spaces */
+ for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
+ /* empty */;
+ }
+
+ /* should be at issuer or serialNumber NamedValue */
+ if ( strncasecmp( x.bv_val, "issuer", STRLENOF("issuer") ) == 0 ) {
+ if ( have & HAVE_ISSUER ) return LDAP_INVALID_SYNTAX;
+
+ /* parse issuer */
+ x.bv_val += STRLENOF("issuer");
+ x.bv_len -= STRLENOF("issuer");
+
+ if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
+ x.bv_val++;
+ x.bv_len--;
+
+ /* eat leading spaces */
+ for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
+ /* empty */;
+ }
+
+ /* For backward compatibility, this part is optional */
+ if ( strncasecmp( x.bv_val, "rdnSequence:", STRLENOF("rdnSequence:") ) == 0 ) {
+ x.bv_val += STRLENOF("rdnSequence:");
+ x.bv_len -= STRLENOF("rdnSequence:");
+ }
+
+ if ( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX;
+ x.bv_val++;
+ x.bv_len--;
+
+ is->bv_val = x.bv_val;
+ is->bv_len = 0;
+
+ for ( gotquote=0; is->bv_len < x.bv_len; ) {
+ if ( is->bv_val[is->bv_len] != '"' ) {
+ is->bv_len++;
+ continue;
+ }
+ gotquote = 1;
+ if ( is->bv_val[is->bv_len+1] == '"' ) {
+ /* double dquote */
+ numdquotes++;
+ is->bv_len += 2;
+ continue;
+ }
+ break;
+ }
+ if ( !gotquote ) return LDAP_INVALID_SYNTAX;
+
+ x.bv_val += is->bv_len + 1;
+ x.bv_len -= is->bv_len + 1;
+
+ have |= HAVE_ISSUER;
+
+ } else if ( strncasecmp( x.bv_val, "serialNumber", STRLENOF("serialNumber") ) == 0 )
+ {
+ if ( have & HAVE_SN ) return LDAP_INVALID_SYNTAX;
+
+ /* parse serialNumber */
+ x.bv_val += STRLENOF("serialNumber");
+ x.bv_len -= STRLENOF("serialNumber");
+
+ if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
+ x.bv_val++;
+ x.bv_len--;
+
+ /* eat leading spaces */
+ for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
+ /* empty */;
+ }
+
+ if ( checkNum( &x, sn ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ x.bv_val += sn->bv_len;
+ x.bv_len -= sn->bv_len;
+
+ have |= HAVE_SN;
+
+ } else {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* eat leading spaces */
+ for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
+ /* empty */;
+ }
+
+ if ( have == HAVE_ALL ) {
+ break;
+ }
+
+ if ( x.bv_val[0] != ',' ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ x.bv_val++;
+ x.bv_len--;
+ } while ( 1 );
+
+ /* should have no characters left... */
+ if ( x.bv_len ) return LDAP_INVALID_SYNTAX;
+
+ if ( numdquotes == 0 ) {
+ ber_dupbv_x( &ni, is, ctx );
+
+ } else {
+ ber_len_t src, dst;
+
+ ni.bv_len = is->bv_len - numdquotes;
+ ni.bv_val = slap_sl_malloc( ni.bv_len + 1, ctx );
+ for ( src = 0, dst = 0; src < is->bv_len; src++, dst++ ) {
+ if ( is->bv_val[src] == '"' ) {
+ src++;
+ }
+ ni.bv_val[dst] = is->bv_val[src];
+ }
+ ni.bv_val[dst] = '\0';
+ }
+
+ *is = ni;
+ }
+
+ return 0;
+}
+
+static int
+serialNumberAndIssuerValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ int rc;
+ struct berval sn, i;
+
+ Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerValidate: <%s>\n",
+ in->bv_val );
+
+ rc = serialNumberAndIssuerCheck( in, &sn, &i, NULL );
+ if ( rc ) {
+ goto done;
+ }
+
+ /* validate DN -- doesn't handle double dquote */
+ rc = dnValidate( NULL, &i );
+ if ( rc ) {
+ rc = LDAP_INVALID_SYNTAX;
+ }
+
+ if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
+ slap_sl_free( i.bv_val, NULL );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerValidate: <%s> err=%d\n",
+ in->bv_val, rc );
+
+done:;
+ return rc;
+}
+
+static int
+serialNumberAndIssuerPretty(
+ Syntax *syntax,
+ struct berval *in,
+ struct berval *out,
+ void *ctx )
+{
+ int rc;
+ struct berval sn, i, ni = BER_BVNULL;
+ char *p;
+
+ assert( in != NULL );
+ assert( out != NULL );
+
+ BER_BVZERO( out );
+
+ Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerPretty: <%s>\n",
+ in->bv_val );
+
+ rc = serialNumberAndIssuerCheck( in, &sn, &i, ctx );
+ if ( rc ) {
+ goto done;
+ }
+
+ rc = dnPretty( syntax, &i, &ni, ctx );
+
+ if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
+ slap_sl_free( i.bv_val, ctx );
+ }
+
+ if ( rc ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ /* make room from sn + "$" */
+ out->bv_len = STRLENOF("{ serialNumber , issuer rdnSequence:\"\" }")
+ + sn.bv_len + ni.bv_len;
+ out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
+
+ if ( out->bv_val == NULL ) {
+ out->bv_len = 0;
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ p = out->bv_val;
+ p = lutil_strcopy( p, "{ serialNumber " /*}*/ );
+ p = lutil_strbvcopy( p, &sn );
+ p = lutil_strcopy( p, ", issuer rdnSequence:\"" );
+ p = lutil_strbvcopy( p, &ni );
+ p = lutil_strcopy( p, /*{*/ "\" }" );
+
+ assert( p == &out->bv_val[out->bv_len] );
+
+done:;
+ Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerPretty: <%s> => <%s>\n",
+ in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)" );
+
+ slap_sl_free( ni.bv_val, ctx );
+
+ return LDAP_SUCCESS;
+}
+
+static int
+slap_bin2hex(
+ struct berval *in,
+ struct berval *out,
+ void *ctx )
+
+{
+ /* Use hex format. '123456789abcdef'H */
+ unsigned char *ptr, zero = '\0';
+ char *sptr;
+ int first;
+ ber_len_t i, len, nlen;
+
+ assert( in != NULL );
+ assert( !BER_BVISNULL( in ) );
+ assert( out != NULL );
+ assert( !BER_BVISNULL( out ) );
+
+ ptr = (unsigned char *)in->bv_val;
+ len = in->bv_len;
+
+ /* Check for minimal encodings */
+ if ( len > 1 ) {
+ if ( ptr[0] & 0x80 ) {
+ if ( ( ptr[0] == 0xff ) && ( ptr[1] & 0x80 ) ) {
+ return -1;
+ }
+
+ } else if ( ptr[0] == 0 ) {
+ if ( !( ptr[1] & 0x80 ) ) {
+ return -1;
+ }
+ len--;
+ ptr++;
+ }
+
+ } else if ( len == 0 ) {
+ /* FIXME: this should not be possible,
+ * since a value of zero would have length 1 */
+ len = 1;
+ ptr = &zero;
+ }
+
+ first = !( ptr[0] & 0xf0U );
+ nlen = len * 2 - first + STRLENOF("''H"); /* quotes, H */
+ if ( nlen >= out->bv_len ) {
+ out->bv_val = slap_sl_malloc( nlen + 1, ctx );
+ }
+ sptr = out->bv_val;
+ *sptr++ = '\'';
+ i = 0;
+ if ( first ) {
+ sprintf( sptr, "%01X", ( ptr[0] & 0x0fU ) );
+ sptr++;
+ i = 1;
+ }
+ for ( ; i < len; i++ ) {
+ sprintf( sptr, "%02X", ptr[i] );
+ sptr += 2;
+ }
+ *sptr++ = '\'';
+ *sptr++ = 'H';
+ *sptr = '\0';
+
+ assert( sptr == &out->bv_val[nlen] );
+
+ out->bv_len = nlen;
+
+ return 0;
+}
+
+#define SLAP_SN_BUFLEN (64)
+
+/*
+ * This routine is called by certificateExactNormalize when
+ * certificateExactNormalize receives a search string instead of
+ * a certificate. This routine checks if the search value is valid
+ * and then returns the normalized value
+ */
+static int
+serialNumberAndIssuerNormalize(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *in,
+ struct berval *out,
+ void *ctx )
+{
+ struct berval sn, sn2, sn3, i, ni;
+ char sbuf2[SLAP_SN_BUFLEN];
+ char sbuf3[SLAP_SN_BUFLEN];
+ char *p;
+ int rc;
+
+ assert( in != NULL );
+ assert( out != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerNormalize: <%s>\n",
+ in->bv_val );
+
+ rc = serialNumberAndIssuerCheck( in, &sn, &i, ctx );
+ if ( rc ) {
+ return rc;
+ }
+
+ rc = dnNormalize( usage, syntax, mr, &i, &ni, ctx );
+
+ if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
+ slap_sl_free( i.bv_val, ctx );
+ }
+
+ if ( rc ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* Convert sn to canonical hex */
+ sn2.bv_val = sbuf2;
+ if ( sn.bv_len > sizeof( sbuf2 ) ) {
+ sn2.bv_val = slap_sl_malloc( sn.bv_len, ctx );
+ }
+ sn2.bv_len = sn.bv_len;
+ sn3.bv_val = sbuf3;
+ sn3.bv_len = sizeof(sbuf3);
+ if ( lutil_str2bin( &sn, &sn2, ctx ) || slap_bin2hex( &sn2, &sn3, ctx ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto func_leave;
+ }
+
+ out->bv_len = STRLENOF( "{ serialNumber , issuer rdnSequence:\"\" }" )
+ + sn3.bv_len + ni.bv_len;
+ out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
+ if ( out->bv_val == NULL ) {
+ out->bv_len = 0;
+ rc = LDAP_OTHER;
+ goto func_leave;
+ }
+
+ p = out->bv_val;
+
+ p = lutil_strcopy( p, "{ serialNumber " /*}*/ );
+ p = lutil_strbvcopy( p, &sn3 );
+ p = lutil_strcopy( p, ", issuer rdnSequence:\"" );
+ p = lutil_strbvcopy( p, &ni );
+ p = lutil_strcopy( p, /*{*/ "\" }" );
+
+ assert( p == &out->bv_val[out->bv_len] );
+
+func_leave:
+ Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerNormalize: <%s> => <%s>\n",
+ in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)" );
+
+ if ( sn2.bv_val != sbuf2 ) {
+ slap_sl_free( sn2.bv_val, ctx );
+ }
+
+ if ( sn3.bv_val != sbuf3 ) {
+ slap_sl_free( sn3.bv_val, ctx );
+ }
+
+ slap_sl_free( ni.bv_val, ctx );
+
+ return rc;
+}
+
+static int
+certificateExactNormalize(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len;
+ ber_int_t i;
+ char serialbuf2[SLAP_SN_BUFLEN];
+ struct berval sn, sn2 = BER_BVNULL;
+ struct berval issuer_dn = BER_BVNULL, bvdn;
+ char *p;
+ int rc = LDAP_INVALID_SYNTAX;
+
+ assert( val != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, ">>> certificateExactNormalize: <%p, %lu>\n",
+ val->bv_val, val->bv_len );
+
+ if ( BER_BVISEMPTY( val ) ) goto done;
+
+ if ( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) {
+ return serialNumberAndIssuerNormalize( 0, NULL, NULL, val, normalized, ctx );
+ }
+
+ assert( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX(usage) != 0 );
+
+ ber_init2( ber, val, LBER_USE_DER );
+ tag = ber_skip_tag( ber, &len ); /* Signed Sequence */
+ tag = ber_skip_tag( ber, &len ); /* Sequence */
+ tag = ber_peek_tag( ber, &len ); /* Optional version? */
+ if ( tag == SLAP_X509_OPT_C_VERSION ) {
+ tag = ber_skip_tag( ber, &len );
+ tag = ber_get_int( ber, &i ); /* version */
+ }
+
+ /* NOTE: move the test here from certificateValidate,
+ * so that we can validate certs with serial longer
+ * than sizeof(ber_int_t) */
+ tag = ber_skip_tag( ber, &len ); /* serial */
+ sn.bv_len = len;
+ sn.bv_val = (char *)ber->ber_ptr;
+ sn2.bv_val = serialbuf2;
+ sn2.bv_len = sizeof(serialbuf2);
+ if ( slap_bin2hex( &sn, &sn2, ctx ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+ ber_skip_data( ber, len );
+
+ tag = ber_skip_tag( ber, &len ); /* SignatureAlg */
+ ber_skip_data( ber, len );
+ tag = ber_peek_tag( ber, &len ); /* IssuerDN */
+ if ( len ) {
+ len = ber_ptrlen( ber );
+ bvdn.bv_val = val->bv_val + len;
+ bvdn.bv_len = val->bv_len - len;
+
+ rc = dnX509normalize( &bvdn, &issuer_dn );
+ if ( rc != LDAP_SUCCESS ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+ }
+
+ normalized->bv_len = STRLENOF( "{ serialNumber , issuer rdnSequence:\"\" }" )
+ + sn2.bv_len + issuer_dn.bv_len;
+ normalized->bv_val = ch_malloc( normalized->bv_len + 1 );
+
+ p = normalized->bv_val;
+
+ p = lutil_strcopy( p, "{ serialNumber " /*}*/ );
+ p = lutil_strbvcopy( p, &sn2 );
+ p = lutil_strcopy( p, ", issuer rdnSequence:\"" );
+ p = lutil_strbvcopy( p, &issuer_dn );
+ p = lutil_strcopy( p, /*{*/ "\" }" );
+
+ rc = LDAP_SUCCESS;
+
+done:
+ Debug( LDAP_DEBUG_TRACE, "<<< certificateExactNormalize: <%p, %lu> => <%s>\n",
+ val->bv_val, val->bv_len, rc == LDAP_SUCCESS ? normalized->bv_val : "(err)" );
+
+ if ( issuer_dn.bv_val ) ber_memfree( issuer_dn.bv_val );
+ if ( sn2.bv_val != serialbuf2 ) ber_memfree_x( sn2.bv_val, ctx );
+
+ return rc;
+}
+
+/* X.509 PKI certificateList stuff */
+static int
+checkTime( struct berval *in, struct berval *out )
+{
+ int rc;
+ ber_len_t i;
+ char buf[STRLENOF("YYYYmmddHHMMSSZ") + 1];
+ struct berval bv;
+
+ assert( in != NULL );
+ assert( !BER_BVISNULL( in ) );
+ assert( !BER_BVISEMPTY( in ) );
+
+ if ( in->bv_len < STRLENOF( "YYmmddHHMMSSZ" ) ) {
+ return -1;
+ }
+
+ if ( out != NULL ) {
+ assert( !BER_BVISNULL( out ) );
+ assert( out->bv_len >= sizeof( buf ) );
+ bv.bv_val = out->bv_val;
+
+ } else {
+ bv.bv_val = buf;
+ }
+
+ for ( i = 0; i < STRLENOF( "YYYYmmddHHMMSS" ); i++ ) {
+ if ( !ASCII_DIGIT( in->bv_val[i] ) ) break;
+ }
+
+ if ( in->bv_val[i] != 'Z' ) {
+ return -1;
+ }
+ i++;
+
+ if ( i != in->bv_len ) {
+ return -1;
+ }
+
+ if ( i == STRLENOF( "YYYYmmddHHMMSSZ" ) ) {
+ lutil_strncopy( bv.bv_val, in->bv_val, i );
+ bv.bv_len = i;
+
+ } else if ( i == STRLENOF( "YYmmddHHMMSSZ" ) ) {
+ char *p = bv.bv_val;
+ if ( in->bv_val[0] < '7' ) {
+ p = lutil_strcopy( p, "20" );
+
+ } else {
+ p = lutil_strcopy( p, "19" );
+ }
+ lutil_strncopy( p, in->bv_val, i );
+ bv.bv_len = 2 + i;
+
+ } else {
+ return -1;
+ }
+
+ rc = generalizedTimeValidate( NULL, &bv );
+ if ( rc == LDAP_SUCCESS && out != NULL ) {
+ if ( out->bv_len > bv.bv_len ) {
+ out->bv_val[ bv.bv_len ] = '\0';
+ }
+ out->bv_len = bv.bv_len;
+ }
+
+ return rc != LDAP_SUCCESS;
+}
+
+static int
+issuerAndThisUpdateCheck(
+ struct berval *in,
+ struct berval *is,
+ struct berval *tu,
+ void *ctx )
+{
+ int numdquotes = 0;
+ struct berval x = *in;
+ struct berval ni = BER_BVNULL;
+ /* Parse GSER format */
+ enum {
+ HAVE_NONE = 0x0,
+ HAVE_ISSUER = 0x1,
+ HAVE_THISUPDATE = 0x2,
+ HAVE_ALL = ( HAVE_ISSUER | HAVE_THISUPDATE )
+ } have = HAVE_NONE;
+
+
+ if ( in->bv_len < STRLENOF( "{issuer \"\",thisUpdate \"YYMMDDhhmmssZ\"}" ) ) return LDAP_INVALID_SYNTAX;
+
+ if ( in->bv_val[0] != '{' || in->bv_val[in->bv_len-1] != '}' ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ x.bv_val++;
+ x.bv_len -= STRLENOF("{}");
+
+ do {
+ /* eat leading spaces */
+ for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
+ /* empty */;
+ }
+
+ /* should be at issuer or thisUpdate */
+ if ( strncasecmp( x.bv_val, "issuer", STRLENOF("issuer") ) == 0 ) {
+ if ( have & HAVE_ISSUER ) return LDAP_INVALID_SYNTAX;
+
+ /* parse issuer */
+ x.bv_val += STRLENOF("issuer");
+ x.bv_len -= STRLENOF("issuer");
+
+ if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
+ x.bv_val++;
+ x.bv_len--;
+
+ /* eat leading spaces */
+ for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
+ /* empty */;
+ }
+
+ /* For backward compatibility, this part is optional */
+ if ( strncasecmp( x.bv_val, "rdnSequence:", STRLENOF("rdnSequence:") ) != 0 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ x.bv_val += STRLENOF("rdnSequence:");
+ x.bv_len -= STRLENOF("rdnSequence:");
+
+ if ( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX;
+ x.bv_val++;
+ x.bv_len--;
+
+ is->bv_val = x.bv_val;
+ is->bv_len = 0;
+
+ for ( ; is->bv_len < x.bv_len; ) {
+ if ( is->bv_val[is->bv_len] != '"' ) {
+ is->bv_len++;
+ continue;
+ }
+ if ( is->bv_val[is->bv_len+1] == '"' ) {
+ /* double dquote */
+ numdquotes++;
+ is->bv_len += 2;
+ continue;
+ }
+ break;
+ }
+ x.bv_val += is->bv_len + 1;
+ x.bv_len -= is->bv_len + 1;
+
+ have |= HAVE_ISSUER;
+
+ } else if ( strncasecmp( x.bv_val, "thisUpdate", STRLENOF("thisUpdate") ) == 0 )
+ {
+ if ( have & HAVE_THISUPDATE ) return LDAP_INVALID_SYNTAX;
+
+ /* parse thisUpdate */
+ x.bv_val += STRLENOF("thisUpdate");
+ x.bv_len -= STRLENOF("thisUpdate");
+
+ if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
+ x.bv_val++;
+ x.bv_len--;
+
+ /* eat leading spaces */
+ for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
+ /* empty */;
+ }
+
+ if ( !x.bv_len || x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX;
+ x.bv_val++;
+ x.bv_len--;
+
+ tu->bv_val = x.bv_val;
+ tu->bv_len = 0;
+
+ for ( ; tu->bv_len < x.bv_len; tu->bv_len++ ) {
+ if ( tu->bv_val[tu->bv_len] == '"' ) {
+ break;
+ }
+ }
+ if ( tu->bv_len < STRLENOF("YYYYmmddHHmmssZ") ) return LDAP_INVALID_SYNTAX;
+
+ x.bv_val += tu->bv_len + 1;
+ x.bv_len -= tu->bv_len + 1;
+
+ have |= HAVE_THISUPDATE;
+
+ } else {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* eat leading spaces */
+ for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
+ /* empty */;
+ }
+
+ if ( have == HAVE_ALL ) {
+ break;
+ }
+
+ if ( x.bv_val[0] != ',' ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ x.bv_val++;
+ x.bv_len--;
+ } while ( 1 );
+
+ /* should have no characters left... */
+ if ( x.bv_len ) return LDAP_INVALID_SYNTAX;
+
+ if ( numdquotes == 0 ) {
+ ber_dupbv_x( &ni, is, ctx );
+
+ } else {
+ ber_len_t src, dst;
+
+ ni.bv_len = is->bv_len - numdquotes;
+ ni.bv_val = slap_sl_malloc( ni.bv_len + 1, ctx );
+ for ( src = 0, dst = 0; src < is->bv_len; src++, dst++ ) {
+ if ( is->bv_val[src] == '"' ) {
+ src++;
+ }
+ ni.bv_val[dst] = is->bv_val[src];
+ }
+ ni.bv_val[dst] = '\0';
+ }
+
+ *is = ni;
+
+ return 0;
+}
+
+static int
+issuerAndThisUpdateValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ int rc;
+ struct berval i, tu;
+
+ Debug( LDAP_DEBUG_TRACE, ">>> issuerAndThisUpdateValidate: <%s>\n",
+ in->bv_val );
+
+ rc = issuerAndThisUpdateCheck( in, &i, &tu, NULL );
+ if ( rc ) {
+ goto done;
+ }
+
+ /* validate DN -- doesn't handle double dquote */
+ rc = dnValidate( NULL, &i );
+ if ( rc ) {
+ rc = LDAP_INVALID_SYNTAX;
+
+ } else if ( checkTime( &tu, NULL ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ }
+
+ if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
+ slap_sl_free( i.bv_val, NULL );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "<<< issuerAndThisUpdateValidate: <%s> err=%d\n",
+ in->bv_val, rc );
+
+done:;
+ return rc;
+}
+
+static int
+issuerAndThisUpdatePretty(
+ Syntax *syntax,
+ struct berval *in,
+ struct berval *out,
+ void *ctx )
+{
+ int rc;
+ struct berval i, tu, ni = BER_BVNULL;
+ char *p;
+
+ assert( in != NULL );
+ assert( out != NULL );
+
+ BER_BVZERO( out );
+
+ Debug( LDAP_DEBUG_TRACE, ">>> issuerAndThisUpdatePretty: <%s>\n",
+ in->bv_val );
+
+ rc = issuerAndThisUpdateCheck( in, &i, &tu, ctx );
+ if ( rc ) {
+ goto done;
+ }
+
+ rc = dnPretty( syntax, &i, &ni, ctx );
+
+ if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
+ slap_sl_free( i.bv_val, ctx );
+ }
+
+ if ( rc || checkTime( &tu, NULL ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ /* make room */
+ out->bv_len = STRLENOF("{ issuer rdnSequence:\"\", thisUpdate \"\" }")
+ + ni.bv_len + tu.bv_len;
+ out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
+
+ if ( out->bv_val == NULL ) {
+ out->bv_len = 0;
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ p = out->bv_val;
+ p = lutil_strcopy( p, "{ issuer rdnSequence:\"" /*}*/ );
+ p = lutil_strbvcopy( p, &ni );
+ p = lutil_strcopy( p, "\", thisUpdate \"" );
+ p = lutil_strbvcopy( p, &tu );
+ p = lutil_strcopy( p, /*{*/ "\" }" );
+
+ assert( p == &out->bv_val[out->bv_len] );
+
+done:;
+ Debug( LDAP_DEBUG_TRACE, "<<< issuerAndThisUpdatePretty: <%s> => <%s>\n",
+ in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)" );
+
+ slap_sl_free( ni.bv_val, ctx );
+
+ return rc;
+}
+
+static int
+issuerAndThisUpdateNormalize(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *in,
+ struct berval *out,
+ void *ctx )
+{
+ struct berval i, ni, tu, tu2;
+ char sbuf[STRLENOF("YYYYmmddHHMMSSZ") + 1];
+ char *p;
+ int rc;
+
+ assert( in != NULL );
+ assert( out != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, ">>> issuerAndThisUpdateNormalize: <%s>\n",
+ in->bv_val );
+
+ rc = issuerAndThisUpdateCheck( in, &i, &tu, ctx );
+ if ( rc ) {
+ return rc;
+ }
+
+ rc = dnNormalize( usage, syntax, mr, &i, &ni, ctx );
+
+ if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
+ slap_sl_free( i.bv_val, ctx );
+ }
+
+ tu2.bv_val = sbuf;
+ tu2.bv_len = sizeof( sbuf );
+ if ( rc || checkTime( &tu, &tu2 ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ out->bv_len = STRLENOF( "{ issuer rdnSequence:\"\", thisUpdate \"\" }" )
+ + ni.bv_len + tu2.bv_len;
+ out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
+
+ if ( out->bv_val == NULL ) {
+ out->bv_len = 0;
+ rc = LDAP_OTHER;
+ goto func_leave;
+ }
+
+ p = out->bv_val;
+
+ p = lutil_strcopy( p, "{ issuer rdnSequence:\"" /*}*/ );
+ p = lutil_strbvcopy( p, &ni );
+ p = lutil_strcopy( p, "\", thisUpdate \"" );
+ p = lutil_strbvcopy( p, &tu2 );
+ p = lutil_strcopy( p, /*{*/ "\" }" );
+
+ assert( p == &out->bv_val[out->bv_len] );
+
+func_leave:
+ Debug( LDAP_DEBUG_TRACE, "<<< issuerAndThisUpdateNormalize: <%s> => <%s>\n",
+ in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)" );
+
+ slap_sl_free( ni.bv_val, ctx );
+
+ return rc;
+}
+
+static int
+certificateListExactNormalize(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len;
+ ber_int_t version;
+ struct berval issuer_dn = BER_BVNULL, bvdn,
+ thisUpdate, bvtu;
+ char *p, tubuf[STRLENOF("YYYYmmddHHMMSSZ") + 1];
+ int rc = LDAP_INVALID_SYNTAX;
+
+ assert( val != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, ">>> certificateListExactNormalize: <%p, %lu>\n",
+ val->bv_val, val->bv_len );
+
+ if ( BER_BVISEMPTY( val ) ) goto done;
+
+ if ( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) {
+ return issuerAndThisUpdateNormalize( 0, NULL, NULL, val, normalized, ctx );
+ }
+
+ assert( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX(usage) != 0 );
+
+ ber_init2( ber, val, LBER_USE_DER );
+ tag = ber_skip_tag( ber, &len ); /* Signed wrapper */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ tag = ber_skip_tag( ber, &len ); /* Sequence */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ tag = ber_peek_tag( ber, &len );
+ /* Optional version */
+ if ( tag == LBER_INTEGER ) {
+ tag = ber_get_int( ber, &version );
+ assert( tag == LBER_INTEGER );
+ if ( version != SLAP_X509_V2 ) return LDAP_INVALID_SYNTAX;
+ }
+ tag = ber_skip_tag( ber, &len ); /* Signature Algorithm */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ ber_skip_data( ber, len );
+
+ tag = ber_peek_tag( ber, &len ); /* IssuerDN */
+ if ( tag != LBER_SEQUENCE ) return LDAP_INVALID_SYNTAX;
+ len = ber_ptrlen( ber );
+ bvdn.bv_val = val->bv_val + len;
+ bvdn.bv_len = val->bv_len - len;
+ tag = ber_skip_tag( ber, &len );
+ ber_skip_data( ber, len );
+
+ tag = ber_skip_tag( ber, &len ); /* thisUpdate */
+ /* Time is a CHOICE { UTCTime, GeneralizedTime } */
+ if ( tag != SLAP_TAG_UTCTIME && tag != SLAP_TAG_GENERALIZEDTIME ) return LDAP_INVALID_SYNTAX;
+ bvtu.bv_val = (char *)ber->ber_ptr;
+ bvtu.bv_len = len;
+
+ rc = dnX509normalize( &bvdn, &issuer_dn );
+ if ( rc != LDAP_SUCCESS ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ thisUpdate.bv_val = tubuf;
+ thisUpdate.bv_len = sizeof(tubuf);
+ if ( checkTime( &bvtu, &thisUpdate ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ normalized->bv_len = STRLENOF( "{ issuer rdnSequence:\"\", thisUpdate \"\" }" )
+ + issuer_dn.bv_len + thisUpdate.bv_len;
+ normalized->bv_val = ch_malloc( normalized->bv_len + 1 );
+
+ p = normalized->bv_val;
+
+ p = lutil_strcopy( p, "{ issuer rdnSequence:\"" );
+ p = lutil_strbvcopy( p, &issuer_dn );
+ p = lutil_strcopy( p, "\", thisUpdate \"" );
+ p = lutil_strbvcopy( p, &thisUpdate );
+ p = lutil_strcopy( p, /*{*/ "\" }" );
+
+ rc = LDAP_SUCCESS;
+
+done:
+ Debug( LDAP_DEBUG_TRACE, "<<< certificateListExactNormalize: <%p, %lu> => <%s>\n",
+ val->bv_val, val->bv_len, rc == LDAP_SUCCESS ? normalized->bv_val : "(err)" );
+
+ if ( issuer_dn.bv_val ) ber_memfree( issuer_dn.bv_val );
+
+ return rc;
+}
+
+/* X.509 PMI serialNumberAndIssuerSerialCheck
+
+AttributeCertificateExactAssertion ::= SEQUENCE {
+ serialNumber CertificateSerialNumber,
+ issuer AttCertIssuer }
+
+CertificateSerialNumber ::= INTEGER
+
+AttCertIssuer ::= [0] SEQUENCE {
+issuerName GeneralNames OPTIONAL,
+baseCertificateID [0] IssuerSerial OPTIONAL,
+objectDigestInfo [1] ObjectDigestInfo OPTIONAL }
+-- At least one component shall be present
+
+GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+
+GeneralName ::= CHOICE {
+ otherName [0] INSTANCE OF OTHER-NAME,
+ rfc822Name [1] IA5String,
+ dNSName [2] IA5String,
+ x400Address [3] ORAddress,
+ directoryName [4] Name,
+ ediPartyName [5] EDIPartyName,
+ uniformResourceIdentifier [6] IA5String,
+ iPAddress [7] OCTET STRING,
+ registeredID [8] OBJECT IDENTIFIER }
+
+IssuerSerial ::= SEQUENCE {
+ issuer GeneralNames,
+ serial CertificateSerialNumber,
+ issuerUID UniqueIdentifier OPTIONAL }
+
+ObjectDigestInfo ::= SEQUENCE {
+ digestedObjectType ENUMERATED {
+ publicKey (0),
+ publicKeyCert (1),
+ otherObjectTypes (2) },
+ otherObjectTypeID OBJECT IDENTIFIER OPTIONAL,
+ digestAlgorithm AlgorithmIdentifier,
+ objectDigest BIT STRING }
+
+ * The way I interpret it, an assertion should look like
+
+ { serialNumber 'dd'H,
+ issuer { issuerName { directoryName:rdnSequence:"cn=yyy" }, -- optional
+ baseCertificateID { serial '1d'H,
+ issuer { directoryName:rdnSequence:"cn=zzz" },
+ issuerUID <value> -- optional
+ }, -- optional
+ objectDigestInfo { ... } -- optional
+ }
+ }
+
+ * with issuerName, baseCertificateID and objectDigestInfo optional,
+ * at least one present; the way it's currently implemented, it is
+
+ { serialNumber 'dd'H,
+ issuer { baseCertificateID { serial '1d'H,
+ issuer { directoryName:rdnSequence:"cn=zzz" }
+ }
+ }
+ }
+
+ * with all the above parts mandatory.
+ */
+static int
+serialNumberAndIssuerSerialCheck(
+ struct berval *in,
+ struct berval *sn,
+ struct berval *is,
+ struct berval *i_sn, /* contain serial of baseCertificateID */
+ void *ctx )
+{
+ /* Parse GSER format */
+ enum {
+ HAVE_NONE = 0x0,
+ HAVE_SN = 0x1,
+ HAVE_ISSUER = 0x2,
+ HAVE_ALL = ( HAVE_SN | HAVE_ISSUER )
+ } have = HAVE_NONE, have2 = HAVE_NONE;
+ int numdquotes = 0;
+ struct berval x = *in;
+ struct berval ni;
+
+ if ( in->bv_len < 3 ) return LDAP_INVALID_SYNTAX;
+
+ /* no old format */
+ if ( in->bv_val[0] != '{' || in->bv_val[in->bv_len-1] != '}' ) return LDAP_INVALID_SYNTAX;
+
+ x.bv_val++;
+ x.bv_len -= 2;
+
+ do {
+
+ /* eat leading spaces */
+ for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
+ /* empty */;
+ }
+
+ /* should be at issuer or serialNumber NamedValue */
+ if ( strncasecmp( x.bv_val, "issuer", STRLENOF("issuer") ) == 0 ) {
+ if ( have & HAVE_ISSUER ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* parse IssuerSerial */
+ x.bv_val += STRLENOF("issuer");
+ x.bv_len -= STRLENOF("issuer");
+
+ if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
+ x.bv_val++;
+ x.bv_len--;
+
+ /* eat leading spaces */
+ for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
+ /* empty */;
+ }
+
+ if ( x.bv_val[0] != '{' /*}*/ ) return LDAP_INVALID_SYNTAX;
+ x.bv_val++;
+ x.bv_len--;
+
+ /* eat leading spaces */
+ for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
+ /* empty */;
+ }
+
+ if ( strncasecmp( x.bv_val, "baseCertificateID ", STRLENOF("baseCertificateID ") ) != 0 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ x.bv_val += STRLENOF("baseCertificateID ");
+ x.bv_len -= STRLENOF("baseCertificateID ");
+
+ /* eat leading spaces */
+ for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
+ /* empty */;
+ }
+
+ if ( x.bv_val[0] != '{' /*}*/ ) return LDAP_INVALID_SYNTAX;
+ x.bv_val++;
+ x.bv_len--;
+
+ do {
+ /* eat leading spaces */
+ for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
+ /* empty */;
+ }
+
+ /* parse issuer of baseCertificateID */
+ if ( strncasecmp( x.bv_val, "issuer ", STRLENOF("issuer ") ) == 0 ) {
+ if ( have2 & HAVE_ISSUER ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ x.bv_val += STRLENOF("issuer ");
+ x.bv_len -= STRLENOF("issuer ");
+
+ /* eat leading spaces */
+ for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
+ /* empty */;
+ }
+
+ if ( x.bv_val[0] != '{' /*}*/ ) return LDAP_INVALID_SYNTAX;
+ x.bv_val++;
+ x.bv_len--;
+
+ /* eat leading spaces */
+ for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
+ /* empty */;
+ }
+
+ if ( strncasecmp( x.bv_val, "directoryName:rdnSequence:", STRLENOF("directoryName:rdnSequence:") ) != 0 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ x.bv_val += STRLENOF("directoryName:rdnSequence:");
+ x.bv_len -= STRLENOF("directoryName:rdnSequence:");
+
+ if ( x.bv_val[0] != '"' ) return LDAP_INVALID_SYNTAX;
+ x.bv_val++;
+ x.bv_len--;
+
+ is->bv_val = x.bv_val;
+ is->bv_len = 0;
+
+ for ( ; is->bv_len < x.bv_len; ) {
+ if ( is->bv_val[is->bv_len] != '"' ) {
+ is->bv_len++;
+ continue;
+ }
+ if ( is->bv_val[is->bv_len + 1] == '"' ) {
+ /* double dquote */
+ numdquotes++;
+ is->bv_len += 2;
+ continue;
+ }
+ break;
+ }
+ x.bv_val += is->bv_len + 1;
+ x.bv_len -= is->bv_len + 1;
+
+ /* eat leading spaces */
+ for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
+ /* empty */;
+ }
+
+ if ( x.bv_val[0] != /*{*/ '}' ) return LDAP_INVALID_SYNTAX;
+ x.bv_val++;
+ x.bv_len--;
+
+ have2 |= HAVE_ISSUER;
+
+ } else if ( strncasecmp( x.bv_val, "serial ", STRLENOF("serial ") ) == 0 ) {
+ if ( have2 & HAVE_SN ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ x.bv_val += STRLENOF("serial ");
+ x.bv_len -= STRLENOF("serial ");
+
+ /* eat leading spaces */
+ for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len--) {
+ /* empty */;
+ }
+
+ if ( checkNum( &x, i_sn ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ x.bv_val += i_sn->bv_len;
+ x.bv_len -= i_sn->bv_len;
+
+ have2 |= HAVE_SN;
+
+ } else {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* eat leading spaces */
+ for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
+ /* empty */;
+ }
+
+ if ( have2 == HAVE_ALL ) {
+ break;
+ }
+
+ if ( x.bv_val[0] != ',' ) return LDAP_INVALID_SYNTAX;
+ x.bv_val++;
+ x.bv_len--;
+ } while ( 1 );
+
+ if ( x.bv_val[0] != /*{*/ '}' ) return LDAP_INVALID_SYNTAX;
+ x.bv_val++;
+ x.bv_len--;
+
+ /* eat leading spaces */
+ for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
+ /* empty */;
+ }
+
+ if ( x.bv_val[0] != /*{*/ '}' ) return LDAP_INVALID_SYNTAX;
+ x.bv_val++;
+ x.bv_len--;
+
+ have |= HAVE_ISSUER;
+
+ } else if ( strncasecmp( x.bv_val, "serialNumber", STRLENOF("serialNumber") ) == 0 ) {
+ if ( have & HAVE_SN ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* parse serialNumber */
+ x.bv_val += STRLENOF("serialNumber");
+ x.bv_len -= STRLENOF("serialNumber");
+
+ if ( x.bv_val[0] != ' ' ) return LDAP_INVALID_SYNTAX;
+ x.bv_val++;
+ x.bv_len--;
+
+ /* eat leading spaces */
+ for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
+ /* empty */;
+ }
+
+ if ( checkNum( &x, sn ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ x.bv_val += sn->bv_len;
+ x.bv_len -= sn->bv_len;
+
+ have |= HAVE_SN;
+
+ } else {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* eat spaces */
+ for ( ; (x.bv_val[0] == ' ') && x.bv_len; x.bv_val++, x.bv_len-- ) {
+ /* empty */;
+ }
+
+ if ( have == HAVE_ALL ) {
+ break;
+ }
+
+ if ( x.bv_val[0] != ',' ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ x.bv_val++ ;
+ x.bv_len--;
+ } while ( 1 );
+
+ /* should have no characters left... */
+ if( x.bv_len ) return LDAP_INVALID_SYNTAX;
+
+ if ( numdquotes == 0 ) {
+ ber_dupbv_x( &ni, is, ctx );
+
+ } else {
+ ber_len_t src, dst;
+
+ ni.bv_len = is->bv_len - numdquotes;
+ ni.bv_val = slap_sl_malloc( ni.bv_len + 1, ctx );
+ for ( src = 0, dst = 0; src < is->bv_len; src++, dst++ ) {
+ if ( is->bv_val[src] == '"' ) {
+ src++;
+ }
+ ni.bv_val[dst] = is->bv_val[src];
+ }
+ ni.bv_val[dst] = '\0';
+ }
+
+ *is = ni;
+
+ /* need to handle double dquotes here */
+ return 0;
+}
+
+/* X.509 PMI serialNumberAndIssuerSerialValidate */
+static int
+serialNumberAndIssuerSerialValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ int rc;
+ struct berval sn, i, i_sn;
+
+ Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerSerialValidate: <%s>\n",
+ in->bv_val );
+
+ rc = serialNumberAndIssuerSerialCheck( in, &sn, &i, &i_sn, NULL );
+ if ( rc ) {
+ goto done;
+ }
+
+ /* validate DN -- doesn't handle double dquote */
+ rc = dnValidate( NULL, &i );
+ if ( rc ) {
+ rc = LDAP_INVALID_SYNTAX;
+ }
+
+ if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
+ slap_sl_free( i.bv_val, NULL );
+ }
+
+done:;
+ Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerSerialValidate: <%s> err=%d\n",
+ in->bv_val, rc );
+
+ return rc;
+}
+
+/* X.509 PMI serialNumberAndIssuerSerialPretty */
+static int
+serialNumberAndIssuerSerialPretty(
+ Syntax *syntax,
+ struct berval *in,
+ struct berval *out,
+ void *ctx )
+{
+ struct berval sn, i, i_sn, ni = BER_BVNULL;
+ char *p;
+ int rc;
+
+ assert( in != NULL );
+ assert( out != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerSerialPretty: <%s>\n",
+ in->bv_val );
+
+ rc = serialNumberAndIssuerSerialCheck( in, &sn, &i, &i_sn, ctx );
+ if ( rc ) {
+ goto done;
+ }
+
+ rc = dnPretty( syntax, &i, &ni, ctx );
+
+ if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
+ slap_sl_free( i.bv_val, ctx );
+ }
+
+ if ( rc ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ /* make room from sn + "$" */
+ out->bv_len = STRLENOF("{ serialNumber , issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"\" }, serial } } }")
+ + sn.bv_len + ni.bv_len + i_sn.bv_len;
+ out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
+
+ if ( out->bv_val == NULL ) {
+ out->bv_len = 0;
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ p = out->bv_val;
+ p = lutil_strcopy( p, "{ serialNumber " );
+ p = lutil_strbvcopy( p, &sn );
+ p = lutil_strcopy( p, ", issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"" );
+ p = lutil_strbvcopy( p, &ni );
+ p = lutil_strcopy( p, "\" }, serial " );
+ p = lutil_strbvcopy( p, &i_sn );
+ p = lutil_strcopy( p, " } } }" );
+
+ assert( p == &out->bv_val[out->bv_len] );
+
+done:;
+ Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerSerialPretty: <%s> => <%s>\n",
+ in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)" );
+
+ slap_sl_free( ni.bv_val, ctx );
+
+ return rc;
+}
+
+/* X.509 PMI serialNumberAndIssuerSerialNormalize */
+/*
+ * This routine is called by attributeCertificateExactNormalize
+ * when attributeCertificateExactNormalize receives a search
+ * string instead of a attribute certificate. This routine
+ * checks if the search value is valid and then returns the
+ * normalized value
+ */
+static int
+serialNumberAndIssuerSerialNormalize(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *in,
+ struct berval *out,
+ void *ctx )
+{
+ struct berval i, ni = BER_BVNULL,
+ sn, sn2 = BER_BVNULL, sn3 = BER_BVNULL,
+ i_sn, i_sn2 = BER_BVNULL, i_sn3 = BER_BVNULL;
+ char sbuf2[SLAP_SN_BUFLEN], i_sbuf2[SLAP_SN_BUFLEN],
+ sbuf3[SLAP_SN_BUFLEN], i_sbuf3[SLAP_SN_BUFLEN];
+ char *p;
+ int rc;
+
+ assert( in != NULL );
+ assert( out != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, ">>> serialNumberAndIssuerSerialNormalize: <%s>\n",
+ in->bv_val );
+
+ rc = serialNumberAndIssuerSerialCheck( in, &sn, &i, &i_sn, ctx );
+ if ( rc ) {
+ goto func_leave;
+ }
+
+ rc = dnNormalize( usage, syntax, mr, &i, &ni, ctx );
+
+ if ( in->bv_val[0] == '{' && in->bv_val[in->bv_len-1] == '}' ) {
+ slap_sl_free( i.bv_val, ctx );
+ }
+
+ if ( rc ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto func_leave;
+ }
+
+ /* Convert sn to canonical hex */
+ sn2.bv_val = sbuf2;
+ sn2.bv_len = sn.bv_len;
+ if ( sn.bv_len > sizeof( sbuf2 ) ) {
+ sn2.bv_val = slap_sl_malloc( sn.bv_len, ctx );
+ }
+ if ( lutil_str2bin( &sn, &sn2, ctx ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto func_leave;
+ }
+
+ /* Convert i_sn to canonical hex */
+ i_sn2.bv_val = i_sbuf2;
+ i_sn2.bv_len = i_sn.bv_len;
+ if ( i_sn.bv_len > sizeof( i_sbuf2 ) ) {
+ i_sn2.bv_val = slap_sl_malloc( i_sn.bv_len, ctx );
+ }
+ if ( lutil_str2bin( &i_sn, &i_sn2, ctx ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto func_leave;
+ }
+
+ sn3.bv_val = sbuf3;
+ sn3.bv_len = sizeof(sbuf3);
+ if ( slap_bin2hex( &sn2, &sn3, ctx ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto func_leave;
+ }
+
+ i_sn3.bv_val = i_sbuf3;
+ i_sn3.bv_len = sizeof(i_sbuf3);
+ if ( slap_bin2hex( &i_sn2, &i_sn3, ctx ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto func_leave;
+ }
+
+ out->bv_len = STRLENOF("{ serialNumber , issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"\" }, serial } } }")
+ + sn3.bv_len + ni.bv_len + i_sn3.bv_len;
+ out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
+
+ if ( out->bv_val == NULL ) {
+ out->bv_len = 0;
+ rc = LDAP_OTHER;
+ goto func_leave;
+ }
+
+ p = out->bv_val;
+
+ p = lutil_strcopy( p, "{ serialNumber " );
+ p = lutil_strbvcopy( p, &sn3 );
+ p = lutil_strcopy( p, ", issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"" );
+ p = lutil_strbvcopy( p, &ni );
+ p = lutil_strcopy( p, "\" }, serial " );
+ p = lutil_strbvcopy( p, &i_sn3 );
+ p = lutil_strcopy( p, " } } }" );
+
+ assert( p == &out->bv_val[out->bv_len] );
+
+func_leave:
+ Debug( LDAP_DEBUG_TRACE, "<<< serialNumberAndIssuerSerialNormalize: <%s> => <%s>\n",
+ in->bv_val, rc == LDAP_SUCCESS ? out->bv_val : "(err)" );
+
+ if ( sn2.bv_val != sbuf2 ) {
+ slap_sl_free( sn2.bv_val, ctx );
+ }
+
+ if ( i_sn2.bv_val != i_sbuf2 ) {
+ slap_sl_free( i_sn2.bv_val, ctx );
+ }
+
+ if ( sn3.bv_val != sbuf3 ) {
+ slap_sl_free( sn3.bv_val, ctx );
+ }
+
+ if ( i_sn3.bv_val != i_sbuf3 ) {
+ slap_sl_free( i_sn3.bv_val, ctx );
+ }
+
+ slap_sl_free( ni.bv_val, ctx );
+
+ return rc;
+}
+
+/* X.509 PMI attributeCertificateExactNormalize */
+static int
+attributeCertificateExactNormalize(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len;
+ char issuer_serialbuf[SLAP_SN_BUFLEN], serialbuf[SLAP_SN_BUFLEN];
+ struct berval sn, i_sn, sn2 = BER_BVNULL, i_sn2 = BER_BVNULL;
+ struct berval issuer_dn = BER_BVNULL, bvdn;
+ char *p;
+ int rc = LDAP_INVALID_SYNTAX;
+
+ if ( BER_BVISEMPTY( val ) ) {
+ return rc;
+ }
+
+ if ( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) {
+ return serialNumberAndIssuerSerialNormalize( 0, NULL, NULL, val, normalized, ctx );
+ }
+
+ assert( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX(usage) != 0 );
+
+ ber_init2( ber, val, LBER_USE_DER );
+ tag = ber_skip_tag( ber, &len ); /* Signed Sequence */
+ tag = ber_skip_tag( ber, &len ); /* Sequence */
+ tag = ber_skip_tag( ber, &len ); /* (Mandatory) version; must be v2(1) */
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len ); /* Holder Sequence */
+ ber_skip_data( ber, len );
+
+ /* Issuer */
+ tag = ber_skip_tag( ber, &len ); /* Sequence */
+ /* issuerName (GeneralNames sequence; optional)? */
+ tag = ber_skip_tag( ber, &len ); /* baseCertificateID (sequence; optional)? */
+ tag = ber_skip_tag( ber, &len ); /* GeneralNames (sequence) */
+ tag = ber_skip_tag( ber, &len ); /* directoryName (we only accept this form of GeneralName) */
+ if ( tag != SLAP_X509_GN_DIRECTORYNAME ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ tag = ber_peek_tag( ber, &len ); /* sequence of RDN */
+ len = ber_ptrlen( ber );
+ bvdn.bv_val = val->bv_val + len;
+ bvdn.bv_len = val->bv_len - len;
+ rc = dnX509normalize( &bvdn, &issuer_dn );
+ if ( rc != LDAP_SUCCESS ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+
+ tag = ber_skip_tag( ber, &len ); /* sequence of RDN */
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len ); /* serial number */
+ if ( tag != LBER_INTEGER ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+ i_sn.bv_val = (char *)ber->ber_ptr;
+ i_sn.bv_len = len;
+ i_sn2.bv_val = issuer_serialbuf;
+ i_sn2.bv_len = sizeof(issuer_serialbuf);
+ if ( slap_bin2hex( &i_sn, &i_sn2, ctx ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+ ber_skip_data( ber, len );
+
+ /* issuerUID (bitstring; optional)? */
+ /* objectDigestInfo (sequence; optional)? */
+
+ tag = ber_skip_tag( ber, &len ); /* Signature (sequence) */
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len ); /* serial number */
+ if ( tag != LBER_INTEGER ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+ sn.bv_val = (char *)ber->ber_ptr;
+ sn.bv_len = len;
+ sn2.bv_val = serialbuf;
+ sn2.bv_len = sizeof(serialbuf);
+ if ( slap_bin2hex( &sn, &sn2, ctx ) ) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto done;
+ }
+ ber_skip_data( ber, len );
+
+ normalized->bv_len = STRLENOF( "{ serialNumber , issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"\" }, serial } } }" )
+ + sn2.bv_len + issuer_dn.bv_len + i_sn2.bv_len;
+ normalized->bv_val = ch_malloc( normalized->bv_len + 1 );
+
+ p = normalized->bv_val;
+
+ p = lutil_strcopy( p, "{ serialNumber " );
+ p = lutil_strbvcopy( p, &sn2 );
+ p = lutil_strcopy( p, ", issuer { baseCertificateID { issuer { directoryName:rdnSequence:\"" );
+ p = lutil_strbvcopy( p, &issuer_dn );
+ p = lutil_strcopy( p, "\" }, serial " );
+ p = lutil_strbvcopy( p, &i_sn2 );
+ p = lutil_strcopy( p, " } } }" );
+
+ Debug( LDAP_DEBUG_TRACE, "attributeCertificateExactNormalize: %s\n",
+ normalized->bv_val );
+
+ rc = LDAP_SUCCESS;
+
+done:
+ if ( issuer_dn.bv_val ) ber_memfree( issuer_dn.bv_val );
+ if ( i_sn2.bv_val != issuer_serialbuf ) ber_memfree_x( i_sn2.bv_val, ctx );
+ if ( sn2.bv_val != serialbuf ) ber_memfree_x( sn2.bv_val, ctx );
+
+ return rc;
+}
+
+
+static int
+hexValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ ber_len_t i;
+
+ assert( in != NULL );
+ assert( !BER_BVISNULL( in ) );
+
+ for ( i = 0; i < in->bv_len; i++ ) {
+ if ( !ASCII_HEX( in->bv_val[ i ] ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/* Normalize a SID as used inside a CSN:
+ * three-digit numeric string */
+static int
+hexNormalize(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ ber_len_t i;
+
+ assert( val != NULL );
+ assert( normalized != NULL );
+
+ ber_dupbv_x( normalized, val, ctx );
+
+ for ( i = 0; i < normalized->bv_len; i++ ) {
+ if ( !ASCII_HEX( normalized->bv_val[ i ] ) ) {
+ ber_memfree_x( normalized->bv_val, ctx );
+ BER_BVZERO( normalized );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ normalized->bv_val[ i ] = TOLOWER( normalized->bv_val[ i ] );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+sidValidate (
+ Syntax *syntax,
+ struct berval *in )
+{
+ assert( in != NULL );
+ assert( !BER_BVISNULL( in ) );
+
+ if ( in->bv_len != 3 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ return hexValidate( NULL, in );
+}
+
+/* Normalize a SID as used inside a CSN:
+ * three-digit numeric string */
+static int
+sidNormalize(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ if ( val->bv_len != 3 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ return hexNormalize( 0, NULL, NULL, val, normalized, ctx );
+}
+
+static int
+sidPretty(
+ Syntax *syntax,
+ struct berval *val,
+ struct berval *out,
+ void *ctx )
+{
+ return sidNormalize( SLAP_MR_VALUE_OF_SYNTAX, NULL, NULL, val, out, ctx );
+}
+
+/* Normalize a SID as used inside a CSN, either as-is
+ * (assertion value) or extracted from the CSN
+ * (attribute value) */
+static int
+csnSidNormalize(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ struct berval bv;
+ char *ptr,
+ buf[ 4 ];
+
+
+ if ( BER_BVISEMPTY( val ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX(usage) ) {
+ return sidNormalize( 0, NULL, NULL, val, normalized, ctx );
+ }
+
+ assert( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX(usage) != 0 );
+
+ ptr = ber_bvchr( val, '#' );
+ if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ bv.bv_val = ptr + 1;
+ bv.bv_len = val->bv_len - ( ptr + 1 - val->bv_val );
+
+ ptr = ber_bvchr( &bv, '#' );
+ if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ bv.bv_val = ptr + 1;
+ bv.bv_len = val->bv_len - ( ptr + 1 - val->bv_val );
+
+ ptr = ber_bvchr( &bv, '#' );
+ if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ bv.bv_len = ptr - bv.bv_val;
+
+ if ( bv.bv_len == 2 ) {
+ /* OpenLDAP 2.3 SID */
+ buf[ 0 ] = '0';
+ buf[ 1 ] = bv.bv_val[ 0 ];
+ buf[ 2 ] = bv.bv_val[ 1 ];
+ buf[ 3 ] = '\0';
+
+ bv.bv_val = buf;
+ bv.bv_len = 3;
+ }
+
+ return sidNormalize( 0, NULL, NULL, &bv, normalized, ctx );
+}
+
+static int
+csnValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ struct berval bv;
+ char *ptr;
+ int rc;
+
+ assert( in != NULL );
+
+ if ( BER_BVISNULL( in ) || BER_BVISEMPTY( in ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ bv = *in;
+
+ ptr = ber_bvchr( &bv, '#' );
+ if ( ptr == NULL || ptr == &bv.bv_val[bv.bv_len] ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ bv.bv_len = ptr - bv.bv_val;
+ if ( bv.bv_len != STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ" ) &&
+ bv.bv_len != STRLENOF( "YYYYmmddHHMMSSZ" ) )
+ {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ rc = generalizedTimeValidate( NULL, &bv );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ bv.bv_val = ptr + 1;
+ bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val );
+
+ ptr = ber_bvchr( &bv, '#' );
+ if ( ptr == NULL || ptr == &in->bv_val[in->bv_len] ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ bv.bv_len = ptr - bv.bv_val;
+ if ( bv.bv_len != 6 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ rc = hexValidate( NULL, &bv );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ bv.bv_val = ptr + 1;
+ bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val );
+
+ ptr = ber_bvchr( &bv, '#' );
+ if ( ptr == NULL || ptr == &in->bv_val[in->bv_len] ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ bv.bv_len = ptr - bv.bv_val;
+ if ( bv.bv_len == 2 ) {
+ /* tolerate old 2-digit replica-id */
+ rc = hexValidate( NULL, &bv );
+
+ } else {
+ rc = sidValidate( NULL, &bv );
+ }
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ bv.bv_val = ptr + 1;
+ bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val );
+
+ if ( bv.bv_len != 6 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ return hexValidate( NULL, &bv );
+}
+
+/* Normalize a CSN in OpenLDAP 2.1 format */
+static int
+csnNormalize21(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ struct berval gt, cnt, sid, mod;
+ struct berval bv;
+ char buf[ STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" ) + 1 ];
+ char *ptr;
+ ber_len_t i;
+
+ assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 );
+ assert( !BER_BVISEMPTY( val ) );
+
+ gt = *val;
+
+ ptr = ber_bvchr( &gt, '#' );
+ if ( ptr == NULL || ptr == &gt.bv_val[gt.bv_len] ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ gt.bv_len = ptr - gt.bv_val;
+ if ( gt.bv_len != STRLENOF( "YYYYmmddHH:MM:SSZ" ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( gt.bv_val[ 10 ] != ':' || gt.bv_val[ 13 ] != ':' ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ cnt.bv_val = ptr + 1;
+ cnt.bv_len = val->bv_len - ( cnt.bv_val - val->bv_val );
+
+ ptr = ber_bvchr( &cnt, '#' );
+ if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ cnt.bv_len = ptr - cnt.bv_val;
+ if ( cnt.bv_len != STRLENOF( "0x0000" ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( strncmp( cnt.bv_val, "0x", STRLENOF( "0x" ) ) != 0 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ cnt.bv_val += STRLENOF( "0x" );
+ cnt.bv_len -= STRLENOF( "0x" );
+
+ sid.bv_val = ptr + 1;
+ sid.bv_len = val->bv_len - ( sid.bv_val - val->bv_val );
+
+ ptr = ber_bvchr( &sid, '#' );
+ if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ sid.bv_len = ptr - sid.bv_val;
+ if ( sid.bv_len != STRLENOF( "0" ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ mod.bv_val = ptr + 1;
+ mod.bv_len = val->bv_len - ( mod.bv_val - val->bv_val );
+ if ( mod.bv_len != STRLENOF( "0000" ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ bv.bv_len = STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" );
+ bv.bv_val = buf;
+
+ ptr = bv.bv_val;
+ ptr = lutil_strncopy( ptr, gt.bv_val, STRLENOF( "YYYYmmddHH" ) );
+ ptr = lutil_strncopy( ptr, &gt.bv_val[ STRLENOF( "YYYYmmddHH:" ) ],
+ STRLENOF( "MM" ) );
+ ptr = lutil_strncopy( ptr, &gt.bv_val[ STRLENOF( "YYYYmmddHH:MM:" ) ],
+ STRLENOF( "SS" ) );
+ ptr = lutil_strcopy( ptr, ".000000Z#00" );
+ ptr = lutil_strbvcopy( ptr, &cnt );
+ *ptr++ = '#';
+ *ptr++ = '0';
+ *ptr++ = '0';
+ *ptr++ = sid.bv_val[ 0 ];
+ *ptr++ = '#';
+ *ptr++ = '0';
+ *ptr++ = '0';
+ for ( i = 0; i < mod.bv_len; i++ ) {
+ *ptr++ = TOLOWER( mod.bv_val[ i ] );
+ }
+ *ptr = '\0';
+
+ assert( ptr == &bv.bv_val[bv.bv_len] );
+
+ if ( csnValidate( syntax, &bv ) != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ ber_dupbv_x( normalized, &bv, ctx );
+
+ return LDAP_SUCCESS;
+}
+
+/* Normalize a CSN in OpenLDAP 2.3 format */
+static int
+csnNormalize23(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ struct berval gt, cnt, sid, mod;
+ struct berval bv;
+ char buf[ STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" ) + 1 ];
+ char *ptr;
+ ber_len_t i;
+
+ assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 );
+ assert( !BER_BVISEMPTY( val ) );
+
+ gt = *val;
+
+ ptr = ber_bvchr( &gt, '#' );
+ if ( ptr == NULL || ptr == &gt.bv_val[gt.bv_len] ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ gt.bv_len = ptr - gt.bv_val;
+ if ( gt.bv_len != STRLENOF( "YYYYmmddHHMMSSZ" ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ cnt.bv_val = ptr + 1;
+ cnt.bv_len = val->bv_len - ( cnt.bv_val - val->bv_val );
+
+ ptr = ber_bvchr( &cnt, '#' );
+ if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ cnt.bv_len = ptr - cnt.bv_val;
+ if ( cnt.bv_len != STRLENOF( "000000" ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ sid.bv_val = ptr + 1;
+ sid.bv_len = val->bv_len - ( sid.bv_val - val->bv_val );
+
+ ptr = ber_bvchr( &sid, '#' );
+ if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ sid.bv_len = ptr - sid.bv_val;
+ if ( sid.bv_len != STRLENOF( "00" ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ mod.bv_val = ptr + 1;
+ mod.bv_len = val->bv_len - ( mod.bv_val - val->bv_val );
+ if ( mod.bv_len != STRLENOF( "000000" ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ bv.bv_len = STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" );
+ bv.bv_val = buf;
+
+ ptr = bv.bv_val;
+ ptr = lutil_strncopy( ptr, gt.bv_val, gt.bv_len - 1 );
+ ptr = lutil_strcopy( ptr, ".000000Z#" );
+ ptr = lutil_strbvcopy( ptr, &cnt );
+ *ptr++ = '#';
+ *ptr++ = '0';
+ for ( i = 0; i < sid.bv_len; i++ ) {
+ *ptr++ = TOLOWER( sid.bv_val[ i ] );
+ }
+ *ptr++ = '#';
+ for ( i = 0; i < mod.bv_len; i++ ) {
+ *ptr++ = TOLOWER( mod.bv_val[ i ] );
+ }
+ *ptr = '\0';
+
+ if ( ptr != &bv.bv_val[bv.bv_len] ||
+ csnValidate( syntax, &bv ) != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ ber_dupbv_x( normalized, &bv, ctx );
+
+ return LDAP_SUCCESS;
+}
+
+/* Normalize a CSN */
+static int
+csnNormalize(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ struct berval cnt, sid, mod;
+ char *ptr;
+ ber_len_t i;
+
+ assert( val != NULL );
+ assert( normalized != NULL );
+
+ assert( SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) != 0 );
+
+ if ( BER_BVISEMPTY( val ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( val->bv_len == STRLENOF( "YYYYmmddHHMMSSZ#SSSSSS#ID#ssssss" ) ) {
+ /* Openldap <= 2.3 */
+
+ return csnNormalize23( usage, syntax, mr, val, normalized, ctx );
+ }
+
+ if ( val->bv_len == STRLENOF( "YYYYmmddHH:MM:SSZ#0xSSSS#I#ssss" ) ) {
+ /* Openldap 2.1 */
+
+ return csnNormalize21( usage, syntax, mr, val, normalized, ctx );
+ }
+
+ if ( val->bv_len != STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#SID#ssssss" ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ ptr = ber_bvchr( val, '#' );
+ if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( ptr - val->bv_val != STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ" ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ cnt.bv_val = ptr + 1;
+ cnt.bv_len = val->bv_len - ( cnt.bv_val - val->bv_val );
+
+ ptr = ber_bvchr( &cnt, '#' );
+ if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( ptr - cnt.bv_val != STRLENOF( "000000" ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ sid.bv_val = ptr + 1;
+ sid.bv_len = val->bv_len - ( sid.bv_val - val->bv_val );
+
+ ptr = ber_bvchr( &sid, '#' );
+ if ( ptr == NULL || ptr == &val->bv_val[val->bv_len] ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ sid.bv_len = ptr - sid.bv_val;
+ if ( sid.bv_len != STRLENOF( "000" ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ mod.bv_val = ptr + 1;
+ mod.bv_len = val->bv_len - ( mod.bv_val - val->bv_val );
+
+ if ( mod.bv_len != STRLENOF( "000000" ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ ber_dupbv_x( normalized, val, ctx );
+
+ for ( i = STRLENOF( "YYYYmmddHHMMSS.uuuuuuZ#SSSSSS#" );
+ i < normalized->bv_len; i++ )
+ {
+ /* assume it's already validated that's all hex digits */
+ normalized->bv_val[ i ] = TOLOWER( normalized->bv_val[ i ] );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+csnPretty(
+ Syntax *syntax,
+ struct berval *val,
+ struct berval *out,
+ void *ctx )
+{
+ return csnNormalize( SLAP_MR_VALUE_OF_SYNTAX, NULL, NULL, val, out, ctx );
+}
+
+#ifndef SUPPORT_OBSOLETE_UTC_SYNTAX
+/* slight optimization - does not need the start parameter */
+#define check_time_syntax(v, start, p, f) (check_time_syntax)(v, p, f)
+enum { start = 0 };
+#endif
+
+static int
+check_time_syntax (struct berval *val,
+ int start,
+ int *parts,
+ struct berval *fraction)
+{
+ /*
+ * start=0 GeneralizedTime YYYYmmddHH[MM[SS]][(./,)d...](Z|(+/-)HH[MM])
+ * start=1 UTCTime YYmmddHHMM[SS][Z|(+/-)HHMM]
+ * GeneralizedTime supports leap seconds, UTCTime does not.
+ */
+ static const int ceiling[9] = { 100, 100, 12, 31, 24, 60, 60, 24, 60 };
+ static const int mdays[2][12] = {
+ /* non-leap years */
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ /* leap years */
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+ };
+ char *p, *e;
+ int part, c, c1, c2, tzoffset, leapyear = 0;
+
+ p = val->bv_val;
+ e = p + val->bv_len;
+
+#ifdef SUPPORT_OBSOLETE_UTC_SYNTAX
+ parts[0] = 20; /* century - any multiple of 4 from 04 to 96 */
+#endif
+ for (part = start; part < 7 && p < e; part++) {
+ c1 = *p;
+ if (!ASCII_DIGIT(c1)) {
+ break;
+ }
+ p++;
+ if (p == e) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ c = *p++;
+ if (!ASCII_DIGIT(c)) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ c += c1 * 10 - '0' * 11;
+ if ((part | 1) == 3) {
+ --c;
+ if (c < 0) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+ if (c >= ceiling[part]) {
+ if (! (c == 60 && part == 6 && start == 0))
+ return LDAP_INVALID_SYNTAX;
+ }
+ parts[part] = c;
+ }
+ if (part < 5 + start) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ for (; part < 9; part++) {
+ parts[part] = 0;
+ }
+
+ /* leapyear check for the Gregorian calendar (year>1581) */
+ if (parts[parts[1] == 0 ? 0 : 1] % 4 == 0) {
+ leapyear = 1;
+ }
+
+ if (parts[3] >= mdays[leapyear][parts[2]]) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if (start == 0) {
+ fraction->bv_val = p;
+ fraction->bv_len = 0;
+ if (p < e && (*p == '.' || *p == ',')) {
+ char *end_num;
+ while (++p < e && ASCII_DIGIT(*p)) {
+ /* EMPTY */;
+ }
+ if (p - fraction->bv_val == 1) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ for (end_num = p; end_num[-1] == '0'; --end_num) {
+ /* EMPTY */;
+ }
+ c = end_num - fraction->bv_val;
+ if (c != 1) fraction->bv_len = c;
+ }
+ }
+
+ if (p == e) {
+ /* no time zone */
+ return start == 0 ? LDAP_INVALID_SYNTAX : LDAP_SUCCESS;
+ }
+
+ tzoffset = *p++;
+ switch (tzoffset) {
+ default:
+ return LDAP_INVALID_SYNTAX;
+ case 'Z':
+ /* UTC */
+ break;
+ case '+':
+ case '-':
+ for (part = 7; part < 9 && p < e; part++) {
+ c1 = *p;
+ if (!ASCII_DIGIT(c1)) {
+ break;
+ }
+ p++;
+ if (p == e) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ c2 = *p++;
+ if (!ASCII_DIGIT(c2)) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ parts[part] = c1 * 10 + c2 - '0' * 11;
+ if (parts[part] >= ceiling[part]) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+ if (part < 8 + start) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if (tzoffset == '-') {
+ /* negative offset to UTC, ie west of Greenwich */
+ parts[4] += parts[7];
+ parts[5] += parts[8];
+ /* offset is just hhmm, no seconds */
+ for (part = 6; --part >= 0; ) {
+ if (part != 3) {
+ c = ceiling[part];
+ } else {
+ c = mdays[leapyear][parts[2]];
+ }
+ if (parts[part] >= c) {
+ if (part == 0) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ parts[part] -= c;
+ parts[part - 1]++;
+ continue;
+ } else if (part != 5) {
+ break;
+ }
+ }
+ } else {
+ /* positive offset to UTC, ie east of Greenwich */
+ parts[4] -= parts[7];
+ parts[5] -= parts[8];
+ for (part = 6; --part >= 0; ) {
+ if (parts[part] < 0) {
+ if (part == 0) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ if (part != 3) {
+ c = ceiling[part];
+ } else {
+ /* make first arg to % non-negative */
+ c = mdays[leapyear][(parts[2] - 1 + 12) % 12];
+ }
+ parts[part] += c;
+ parts[part - 1]--;
+ continue;
+ } else if (part != 5) {
+ break;
+ }
+ }
+ }
+ }
+
+ return p != e ? LDAP_INVALID_SYNTAX : LDAP_SUCCESS;
+}
+
+#ifdef SUPPORT_OBSOLETE_UTC_SYNTAX
+
+#if 0
+static int
+xutcTimeNormalize(
+ Syntax *syntax,
+ struct berval *val,
+ struct berval *normalized )
+{
+ int parts[9], rc;
+
+ rc = check_time_syntax(val, 1, parts, NULL);
+ if (rc != LDAP_SUCCESS) {
+ return rc;
+ }
+
+ normalized->bv_val = ch_malloc( 14 );
+ if ( normalized->bv_val == NULL ) {
+ return LBER_ERROR_MEMORY;
+ }
+
+ sprintf( normalized->bv_val, "%02d%02d%02d%02d%02d%02dZ",
+ parts[1], parts[2] + 1, parts[3] + 1,
+ parts[4], parts[5], parts[6] );
+ normalized->bv_len = 13;
+
+ return LDAP_SUCCESS;
+}
+#endif /* 0 */
+
+static int
+utcTimeValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ int parts[9];
+ return check_time_syntax(in, 1, parts, NULL);
+}
+
+#endif /* SUPPORT_OBSOLETE_UTC_SYNTAX */
+
+static int
+generalizedTimeValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ int parts[9];
+ struct berval fraction;
+ return check_time_syntax(in, 0, parts, &fraction);
+}
+
+static int
+generalizedTimeNormalize(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ int parts[9], rc;
+ unsigned int len;
+ struct berval fraction;
+
+ rc = check_time_syntax(val, 0, parts, &fraction);
+ if (rc != LDAP_SUCCESS) {
+ return rc;
+ }
+
+ len = STRLENOF("YYYYmmddHHMMSSZ") + fraction.bv_len;
+ normalized->bv_val = slap_sl_malloc( len + 1, ctx );
+ if ( BER_BVISNULL( normalized ) ) {
+ return LBER_ERROR_MEMORY;
+ }
+
+ sprintf( normalized->bv_val, "%02d%02d%02d%02d%02d%02d%02d",
+ parts[0], parts[1], parts[2] + 1, parts[3] + 1,
+ parts[4], parts[5], parts[6] );
+ if ( !BER_BVISEMPTY( &fraction ) ) {
+ memcpy( normalized->bv_val + STRLENOF("YYYYmmddHHMMSSZ")-1,
+ fraction.bv_val, fraction.bv_len );
+ normalized->bv_val[STRLENOF("YYYYmmddHHMMSSZ")-1] = '.';
+ }
+ strcpy( normalized->bv_val + len-1, "Z" );
+ normalized->bv_len = len;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+generalizedTimeOrderingMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ struct berval *asserted = (struct berval *) assertedValue;
+ ber_len_t v_len = value->bv_len;
+ ber_len_t av_len = asserted->bv_len;
+
+ /* ignore trailing 'Z' when comparing */
+ int match = memcmp( value->bv_val, asserted->bv_val,
+ (v_len < av_len ? v_len : av_len) - 1 );
+ if ( match == 0 ) match = v_len - av_len;
+
+ /* If used in extensible match filter, match if value < asserted */
+ if ( flags & SLAP_MR_EXT )
+ match = (match >= 0);
+
+ *matchp = match;
+ return LDAP_SUCCESS;
+}
+
+/* Index generation function: Ordered index */
+int generalizedTimeIndexer(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ BerVarray values,
+ BerVarray *keysp,
+ void *ctx )
+{
+ int i, j;
+ BerVarray keys;
+ char tmp[5];
+ BerValue bvtmp; /* 40 bit index */
+ struct lutil_tm tm;
+ struct lutil_timet tt;
+
+ bvtmp.bv_len = sizeof(tmp);
+ bvtmp.bv_val = tmp;
+ for( i=0; values[i].bv_val != NULL; i++ ) {
+ /* just count them */
+ }
+
+ /* we should have at least one value at this point */
+ assert( i > 0 );
+
+ keys = slap_sl_malloc( sizeof( struct berval ) * (i+1), ctx );
+
+ /* GeneralizedTime YYYYmmddHH[MM[SS]][(./,)d...](Z|(+/-)HH[MM]) */
+ for( i=0, j=0; values[i].bv_val != NULL; i++ ) {
+ assert(values[i].bv_val != NULL && values[i].bv_len >= 10);
+ /* Use 40 bits of time for key */
+ if ( lutil_parsetime( values[i].bv_val, &tm ) == 0 ) {
+ lutil_tm2gtime( &tm, &tt );
+ tmp[0] = tt.tt_gsec & 0xff;
+ tmp[4] = tt.tt_sec & 0xff;
+ tt.tt_sec >>= 8;
+ tmp[3] = tt.tt_sec & 0xff;
+ tt.tt_sec >>= 8;
+ tmp[2] = tt.tt_sec & 0xff;
+ tt.tt_sec >>= 8;
+ tmp[1] = tt.tt_sec & 0xff;
+
+ ber_dupbv_x(&keys[j++], &bvtmp, ctx );
+ }
+ }
+
+ keys[j].bv_val = NULL;
+ keys[j].bv_len = 0;
+
+ *keysp = keys;
+
+ return LDAP_SUCCESS;
+}
+
+/* Index generation function: Ordered index */
+int generalizedTimeFilter(
+ slap_mask_t use,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ void * assertedValue,
+ BerVarray *keysp,
+ void *ctx )
+{
+ BerVarray keys;
+ char tmp[5];
+ BerValue bvtmp; /* 40 bit index */
+ BerValue *value = (BerValue *) assertedValue;
+ struct lutil_tm tm;
+ struct lutil_timet tt;
+
+ bvtmp.bv_len = sizeof(tmp);
+ bvtmp.bv_val = tmp;
+ /* GeneralizedTime YYYYmmddHH[MM[SS]][(./,)d...](Z|(+/-)HH[MM]) */
+ /* Use 40 bits of time for key */
+ if ( value->bv_val && value->bv_len >= 10 &&
+ lutil_parsetime( value->bv_val, &tm ) == 0 ) {
+
+ lutil_tm2gtime( &tm, &tt );
+ tmp[0] = tt.tt_gsec & 0xff;
+ tmp[4] = tt.tt_sec & 0xff;
+ tt.tt_sec >>= 8;
+ tmp[3] = tt.tt_sec & 0xff;
+ tt.tt_sec >>= 8;
+ tmp[2] = tt.tt_sec & 0xff;
+ tt.tt_sec >>= 8;
+ tmp[1] = tt.tt_sec & 0xff;
+
+ keys = slap_sl_malloc( sizeof( struct berval ) * 2, ctx );
+ ber_dupbv_x(keys, &bvtmp, ctx );
+ keys[1].bv_val = NULL;
+ keys[1].bv_len = 0;
+ } else {
+ keys = NULL;
+ }
+
+ *keysp = keys;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+deliveryMethodValidate(
+ Syntax *syntax,
+ struct berval *val )
+{
+#undef LENOF
+#define LENOF(s) (sizeof(s)-1)
+ struct berval tmp = *val;
+ /*
+ * DeliveryMethod = pdm *( WSP DOLLAR WSP DeliveryMethod )
+ * pdm = "any" / "mhs" / "physical" / "telex" / "teletex" /
+ * "g3fax" / "g4fax" / "ia5" / "videotex" / "telephone"
+ */
+again:
+ if( tmp.bv_len < 3 ) return LDAP_INVALID_SYNTAX;
+
+ switch( tmp.bv_val[0] ) {
+ case 'a':
+ case 'A':
+ if(( tmp.bv_len >= LENOF("any") ) &&
+ ( strncasecmp(tmp.bv_val, "any", LENOF("any")) == 0 ))
+ {
+ tmp.bv_len -= LENOF("any");
+ tmp.bv_val += LENOF("any");
+ break;
+ }
+ return LDAP_INVALID_SYNTAX;
+
+ case 'm':
+ case 'M':
+ if(( tmp.bv_len >= LENOF("mhs") ) &&
+ ( strncasecmp(tmp.bv_val, "mhs", LENOF("mhs")) == 0 ))
+ {
+ tmp.bv_len -= LENOF("mhs");
+ tmp.bv_val += LENOF("mhs");
+ break;
+ }
+ return LDAP_INVALID_SYNTAX;
+
+ case 'p':
+ case 'P':
+ if(( tmp.bv_len >= LENOF("physical") ) &&
+ ( strncasecmp(tmp.bv_val, "physical", LENOF("physical")) == 0 ))
+ {
+ tmp.bv_len -= LENOF("physical");
+ tmp.bv_val += LENOF("physical");
+ break;
+ }
+ return LDAP_INVALID_SYNTAX;
+
+ case 't':
+ case 'T': /* telex or teletex or telephone */
+ if(( tmp.bv_len >= LENOF("telex") ) &&
+ ( strncasecmp(tmp.bv_val, "telex", LENOF("telex")) == 0 ))
+ {
+ tmp.bv_len -= LENOF("telex");
+ tmp.bv_val += LENOF("telex");
+ break;
+ }
+ if(( tmp.bv_len >= LENOF("teletex") ) &&
+ ( strncasecmp(tmp.bv_val, "teletex", LENOF("teletex")) == 0 ))
+ {
+ tmp.bv_len -= LENOF("teletex");
+ tmp.bv_val += LENOF("teletex");
+ break;
+ }
+ if(( tmp.bv_len >= LENOF("telephone") ) &&
+ ( strncasecmp(tmp.bv_val, "telephone", LENOF("telephone")) == 0 ))
+ {
+ tmp.bv_len -= LENOF("telephone");
+ tmp.bv_val += LENOF("telephone");
+ break;
+ }
+ return LDAP_INVALID_SYNTAX;
+
+ case 'g':
+ case 'G': /* g3fax or g4fax */
+ if(( tmp.bv_len >= LENOF("g3fax") ) && (
+ ( strncasecmp(tmp.bv_val, "g3fax", LENOF("g3fax")) == 0 ) ||
+ ( strncasecmp(tmp.bv_val, "g4fax", LENOF("g4fax")) == 0 )))
+ {
+ tmp.bv_len -= LENOF("g3fax");
+ tmp.bv_val += LENOF("g3fax");
+ break;
+ }
+ return LDAP_INVALID_SYNTAX;
+
+ case 'i':
+ case 'I':
+ if(( tmp.bv_len >= LENOF("ia5") ) &&
+ ( strncasecmp(tmp.bv_val, "ia5", LENOF("ia5")) == 0 ))
+ {
+ tmp.bv_len -= LENOF("ia5");
+ tmp.bv_val += LENOF("ia5");
+ break;
+ }
+ return LDAP_INVALID_SYNTAX;
+
+ case 'v':
+ case 'V':
+ if(( tmp.bv_len >= LENOF("videotex") ) &&
+ ( strncasecmp(tmp.bv_val, "videotex", LENOF("videotex")) == 0 ))
+ {
+ tmp.bv_len -= LENOF("videotex");
+ tmp.bv_val += LENOF("videotex");
+ break;
+ }
+ return LDAP_INVALID_SYNTAX;
+
+ default:
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if( BER_BVISEMPTY( &tmp ) ) return LDAP_SUCCESS;
+
+ while( !BER_BVISEMPTY( &tmp ) && ( tmp.bv_val[0] == ' ' ) ) {
+ tmp.bv_len--;
+ tmp.bv_val++;
+ }
+ if( !BER_BVISEMPTY( &tmp ) && ( tmp.bv_val[0] == '$' ) ) {
+ tmp.bv_len--;
+ tmp.bv_val++;
+ } else {
+ return LDAP_INVALID_SYNTAX;
+ }
+ while( !BER_BVISEMPTY( &tmp ) && ( tmp.bv_val[0] == ' ' ) ) {
+ tmp.bv_len--;
+ tmp.bv_val++;
+ }
+
+ goto again;
+}
+
+static int
+nisNetgroupTripleValidate(
+ Syntax *syntax,
+ struct berval *val )
+{
+ char *p, *e;
+ int commas = 0;
+
+ if ( BER_BVISEMPTY( val ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ p = (char *)val->bv_val;
+ e = p + val->bv_len;
+
+ if ( *p != '(' /*')'*/ ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ for ( p++; ( p < e ) && ( *p != /*'('*/ ')' ); p++ ) {
+ if ( *p == ',' ) {
+ commas++;
+ if ( commas > 2 ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ } else if ( !AD_CHAR( *p ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ if ( ( commas != 2 ) || ( *p != /*'('*/ ')' ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ p++;
+
+ if (p != e) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+bootParameterValidate(
+ Syntax *syntax,
+ struct berval *val )
+{
+ char *p, *e;
+
+ if ( BER_BVISEMPTY( val ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ p = (char *)val->bv_val;
+ e = p + val->bv_len;
+
+ /* key */
+ for (; ( p < e ) && ( *p != '=' ); p++ ) {
+ if ( !AD_CHAR( *p ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ if ( *p != '=' ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* server */
+ for ( p++; ( p < e ) && ( *p != ':' ); p++ ) {
+ if ( !AD_CHAR( *p ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ if ( *p != ':' ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* path */
+ for ( p++; p < e; p++ ) {
+ if ( !SLAP_PRINTABLE( *p ) ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+firstComponentNormalize(
+ slap_mask_t usage,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ int rc;
+ struct berval comp;
+ ber_len_t len;
+
+ if( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX( usage )) {
+ ber_dupbv_x( normalized, val, ctx );
+ return LDAP_SUCCESS;
+ }
+
+ if( val->bv_len < 3 ) return LDAP_INVALID_SYNTAX;
+
+ if( ! ( val->bv_val[0] == '(' /*')'*/
+ && val->bv_val[val->bv_len - 1] == /*'('*/ ')' )
+ && ! ( val->bv_val[0] == '{' /*'}'*/
+ && val->bv_val[val->bv_len - 1] == /*'('*/ '}' ) )
+ {
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ /* trim leading white space */
+ for( len=1;
+ len < val->bv_len && ASCII_SPACE(val->bv_val[len]);
+ len++ )
+ {
+ /* empty */
+ }
+
+ /* grab next word */
+ comp.bv_val = &val->bv_val[len];
+ len = val->bv_len - len - STRLENOF(/*"{"*/ "}");
+ for( comp.bv_len = 0;
+ !ASCII_SPACE(comp.bv_val[comp.bv_len]) && comp.bv_len < len;
+ comp.bv_len++ )
+ {
+ /* empty */
+ }
+
+ if( mr == slap_schema.si_mr_objectIdentifierFirstComponentMatch ) {
+ rc = numericoidValidate( NULL, &comp );
+ } else if( mr == slap_schema.si_mr_integerFirstComponentMatch ) {
+ rc = integerValidate( NULL, &comp );
+ } else {
+ rc = LDAP_INVALID_SYNTAX;
+ }
+
+
+ if( rc == LDAP_SUCCESS ) {
+ ber_dupbv_x( normalized, &comp, ctx );
+ }
+
+ return rc;
+}
+
+static char *country_gen_syn[] = {
+ "1.3.6.1.4.1.1466.115.121.1.15", /* Directory String */
+ "1.3.6.1.4.1.1466.115.121.1.26", /* IA5 String */
+ "1.3.6.1.4.1.1466.115.121.1.44", /* Printable String */
+ NULL
+};
+
+#define X_BINARY "X-BINARY-TRANSFER-REQUIRED 'TRUE' "
+#define X_NOT_H_R "X-NOT-HUMAN-READABLE 'TRUE' "
+
+static slap_syntax_defs_rec syntax_defs[] = {
+ {"( 1.3.6.1.4.1.1466.115.121.1.1 DESC 'ACI Item' "
+ X_BINARY X_NOT_H_R ")",
+ SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.2 DESC 'Access Point' " X_NOT_H_R ")",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.3 DESC 'Attribute Type Description' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.4 DESC 'Audio' "
+ X_NOT_H_R ")",
+ SLAP_SYNTAX_BLOB, NULL, blobValidate, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.5 DESC 'Binary' "
+ X_NOT_H_R ")",
+ SLAP_SYNTAX_BER, NULL, berValidate, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.6 DESC 'Bit String' )",
+ 0, NULL, bitStringValidate, NULL },
+ {"( 1.3.6.1.4.1.1466.115.121.1.7 DESC 'Boolean' )",
+ 0, NULL, booleanValidate, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.8 DESC 'Certificate' "
+ X_BINARY X_NOT_H_R ")",
+ SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER,
+ NULL, certificateValidate, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.9 DESC 'Certificate List' "
+ X_BINARY X_NOT_H_R ")",
+ SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER,
+ NULL, certificateListValidate, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.10 DESC 'Certificate Pair' "
+ X_BINARY X_NOT_H_R ")",
+ SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER,
+ NULL, sequenceValidate, NULL},
+ {"( " attributeCertificateSyntaxOID " DESC 'X.509 AttributeCertificate' "
+ X_BINARY X_NOT_H_R ")",
+ SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER,
+ NULL, attributeCertificateValidate, NULL},
+#if 0 /* need to go __after__ printableString */
+ {"( 1.3.6.1.4.1.1466.115.121.1.11 DESC 'Country String' )",
+ 0, "1.3.6.1.4.1.1466.115.121.1.44",
+ countryStringValidate, NULL},
+#endif
+ {"( 1.3.6.1.4.1.1466.115.121.1.12 DESC 'Distinguished Name' )",
+ SLAP_SYNTAX_DN, NULL, dnValidate, dnPretty},
+ {"( 1.2.36.79672281.1.5.0 DESC 'RDN' )",
+ 0, NULL, rdnValidate, rdnPretty},
+#ifdef LDAP_COMP_MATCH
+ {"( 1.2.36.79672281.1.5.3 DESC 'allComponents' )",
+ 0, NULL, allComponentsValidate, NULL},
+ {"( 1.2.36.79672281.1.5.2 DESC 'componentFilterMatch assertion') ",
+ 0, NULL, componentFilterValidate, NULL},
+#endif
+ {"( 1.3.6.1.4.1.1466.115.121.1.13 DESC 'Data Quality' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.14 DESC 'Delivery Method' )",
+ 0, NULL, deliveryMethodValidate, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.15 DESC 'Directory String' )",
+ 0, NULL, UTF8StringValidate, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.16 DESC 'DIT Content Rule Description' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.17 DESC 'DIT Structure Rule Description' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.19 DESC 'DSA Quality' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.20 DESC 'DSE Type' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.21 DESC 'Enhanced Guide' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.22 DESC 'Facsimile Telephone Number' )",
+ 0, NULL, printablesStringValidate, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.23 DESC 'Fax' " X_NOT_H_R ")",
+ SLAP_SYNTAX_BLOB, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.24 DESC 'Generalized Time' )",
+ 0, NULL, generalizedTimeValidate, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.25 DESC 'Guide' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.26 DESC 'IA5 String' )",
+ 0, NULL, IA5StringValidate, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.27 DESC 'Integer' )",
+ 0, NULL, integerValidate, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.28 DESC 'JPEG' " X_NOT_H_R ")",
+ SLAP_SYNTAX_BLOB, NULL, blobValidate, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.29 DESC 'Master And Shadow Access Points' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.30 DESC 'Matching Rule Description' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.31 DESC 'Matching Rule Use Description' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.32 DESC 'Mail Preference' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.33 DESC 'MHS OR Address' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.34 DESC 'Name And Optional UID' )",
+ SLAP_SYNTAX_DN, NULL, nameUIDValidate, nameUIDPretty },
+ {"( 1.3.6.1.4.1.1466.115.121.1.35 DESC 'Name Form Description' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.36 DESC 'Numeric String' )",
+ 0, NULL, numericStringValidate, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.37 DESC 'Object Class Description' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.38 DESC 'OID' )",
+ 0, NULL, numericoidValidate, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.39 DESC 'Other Mailbox' )",
+ 0, NULL, IA5StringValidate, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.40 DESC 'Octet String' )",
+ 0, NULL, blobValidate, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.41 DESC 'Postal Address' )",
+ 0, NULL, postalAddressValidate, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.42 DESC 'Protocol Information' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.43 DESC 'Presentation Address' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.44 DESC 'Printable String' )",
+ 0, NULL, printableStringValidate, NULL},
+ /* moved here because now depends on Directory String, IA5 String
+ * and Printable String */
+ {"( 1.3.6.1.4.1.1466.115.121.1.11 DESC 'Country String' )",
+ 0, country_gen_syn, countryStringValidate, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.45 DESC 'SubtreeSpecification' )",
+#define subtreeSpecificationValidate UTF8StringValidate /* FIXME */
+ 0, NULL, subtreeSpecificationValidate, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.49 DESC 'Supported Algorithm' "
+ X_BINARY X_NOT_H_R ")",
+ SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, NULL, berValidate, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.50 DESC 'Telephone Number' )",
+ 0, NULL, printableStringValidate, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.51 DESC 'Teletex Terminal Identifier' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.52 DESC 'Telex Number' )",
+ 0, NULL, printablesStringValidate, NULL},
+#ifdef SUPPORT_OBSOLETE_UTC_SYNTAX
+ {"( 1.3.6.1.4.1.1466.115.121.1.53 DESC 'UTC Time' )",
+ 0, NULL, utcTimeValidate, NULL},
+#endif
+ {"( 1.3.6.1.4.1.1466.115.121.1.54 DESC 'LDAP Syntax Description' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.55 DESC 'Modify Rights' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.56 DESC 'LDAP Schema Definition' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.57 DESC 'LDAP Schema Description' )",
+ 0, NULL, NULL, NULL},
+ {"( 1.3.6.1.4.1.1466.115.121.1.58 DESC 'Substring Assertion' )",
+ 0, NULL, NULL, NULL},
+
+ /* RFC 2307 NIS Syntaxes */
+ {"( 1.3.6.1.1.1.0.0 DESC 'RFC2307 NIS Netgroup Triple' )",
+ 0, NULL, nisNetgroupTripleValidate, NULL},
+ {"( 1.3.6.1.1.1.0.1 DESC 'RFC2307 Boot Parameter' )",
+ 0, NULL, bootParameterValidate, NULL},
+
+ /* draft-zeilenga-ldap-x509 */
+ {"( 1.3.6.1.1.15.1 DESC 'Certificate Exact Assertion' )",
+ SLAP_SYNTAX_HIDE, NULL,
+ serialNumberAndIssuerValidate,
+ serialNumberAndIssuerPretty},
+ {"( 1.3.6.1.1.15.2 DESC 'Certificate Assertion' )",
+ SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
+ {"( 1.3.6.1.1.15.3 DESC 'Certificate Pair Exact Assertion' )",
+ SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
+ {"( 1.3.6.1.1.15.4 DESC 'Certificate Pair Assertion' )",
+ SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
+ {"( 1.3.6.1.1.15.5 DESC 'Certificate List Exact Assertion' )",
+ SLAP_SYNTAX_HIDE, NULL,
+ issuerAndThisUpdateValidate,
+ issuerAndThisUpdatePretty},
+ {"( 1.3.6.1.1.15.6 DESC 'Certificate List Assertion' )",
+ SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
+ {"( 1.3.6.1.1.15.7 DESC 'Algorithm Identifier' )",
+ SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
+ {"( " attributeCertificateExactAssertionSyntaxOID " DESC 'AttributeCertificate Exact Assertion' )",
+ SLAP_SYNTAX_HIDE, NULL,
+ serialNumberAndIssuerSerialValidate,
+ serialNumberAndIssuerSerialPretty},
+ {"( " attributeCertificateAssertionSyntaxOID " DESC 'AttributeCertificate Assertion' )",
+ SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
+
+#ifdef SLAPD_AUTHPASSWD
+ /* needs updating */
+ {"( 1.3.6.1.4.1.4203.666.2.2 DESC 'OpenLDAP authPassword' )",
+ SLAP_SYNTAX_HIDE, NULL, NULL, NULL},
+#endif
+
+ {"( 1.3.6.1.1.16.1 DESC 'UUID' )",
+ 0, NULL, UUIDValidate, UUIDPretty},
+
+ {"( 1.3.6.1.4.1.4203.666.11.2.1 DESC 'CSN' )",
+ SLAP_SYNTAX_HIDE, NULL, csnValidate, csnPretty },
+
+ {"( 1.3.6.1.4.1.4203.666.11.2.4 DESC 'CSN SID' )",
+ SLAP_SYNTAX_HIDE, NULL, sidValidate, sidPretty },
+
+ /* OpenLDAP Void Syntax */
+ {"( 1.3.6.1.4.1.4203.1.1.1 DESC 'OpenLDAP void' )" ,
+ SLAP_SYNTAX_HIDE, NULL, inValidate, NULL},
+
+ /* FIXME: OID is unused, but not registered yet */
+ {"( 1.3.6.1.4.1.4203.666.2.7 DESC 'OpenLDAP authz' )",
+ SLAP_SYNTAX_HIDE, NULL, authzValidate, authzPretty},
+
+ /* PKCS#8 Private Keys for X.509 certificates */
+ {"( 1.2.840.113549.1.8.1.1 DESC 'PKCS#8 PrivateKeyInfo' )",
+ SLAP_SYNTAX_BINARY|SLAP_SYNTAX_BER, NULL, privateKeyValidate, NULL},
+ {NULL, 0, NULL, NULL, NULL}
+};
+
+char *csnSIDMatchSyntaxes[] = {
+ "1.3.6.1.4.1.4203.666.11.2.1" /* csn */,
+ NULL
+};
+char *certificateExactMatchSyntaxes[] = {
+ "1.3.6.1.4.1.1466.115.121.1.8" /* certificate */,
+ NULL
+};
+char *certificateListExactMatchSyntaxes[] = {
+ "1.3.6.1.4.1.1466.115.121.1.9" /* certificateList */,
+ NULL
+};
+char *attributeCertificateExactMatchSyntaxes[] = {
+ attributeCertificateSyntaxOID /* attributeCertificate */,
+ NULL
+};
+
+#ifdef LDAP_COMP_MATCH
+char *componentFilterMatchSyntaxes[] = {
+ "1.3.6.1.4.1.1466.115.121.1.8" /* certificate */,
+ "1.3.6.1.4.1.1466.115.121.1.9" /* certificateList */,
+ attributeCertificateSyntaxOID /* attributeCertificate */,
+ NULL
+};
+#endif
+
+char *directoryStringSyntaxes[] = {
+ "1.3.6.1.4.1.1466.115.121.1.11" /* countryString */,
+ "1.3.6.1.4.1.1466.115.121.1.44" /* printableString */,
+ "1.3.6.1.4.1.1466.115.121.1.50" /* telephoneNumber */,
+ NULL
+};
+char *integerFirstComponentMatchSyntaxes[] = {
+ "1.3.6.1.4.1.1466.115.121.1.27" /* INTEGER */,
+ "1.3.6.1.4.1.1466.115.121.1.17" /* dITStructureRuleDescription */,
+ NULL
+};
+char *objectIdentifierFirstComponentMatchSyntaxes[] = {
+ "1.3.6.1.4.1.1466.115.121.1.38" /* OID */,
+ "1.3.6.1.4.1.1466.115.121.1.3" /* attributeTypeDescription */,
+ "1.3.6.1.4.1.1466.115.121.1.16" /* dITContentRuleDescription */,
+ "1.3.6.1.4.1.1466.115.121.1.54" /* ldapSyntaxDescription */,
+ "1.3.6.1.4.1.1466.115.121.1.30" /* matchingRuleDescription */,
+ "1.3.6.1.4.1.1466.115.121.1.31" /* matchingRuleUseDescription */,
+ "1.3.6.1.4.1.1466.115.121.1.35" /* nameFormDescription */,
+ "1.3.6.1.4.1.1466.115.121.1.37" /* objectClassDescription */,
+ NULL
+};
+
+/*
+ * Other matching rules in X.520 that we do not use (yet):
+ *
+ * 2.5.13.25 uTCTimeMatch
+ * 2.5.13.26 uTCTimeOrderingMatch
+ * 2.5.13.31* directoryStringFirstComponentMatch
+ * 2.5.13.32* wordMatch
+ * 2.5.13.33* keywordMatch
+ * 2.5.13.36+ certificatePairExactMatch
+ * 2.5.13.37+ certificatePairMatch
+ * 2.5.13.40+ algorithmIdentifierMatch
+ * 2.5.13.41* storedPrefixMatch
+ * 2.5.13.42 attributeCertificateMatch
+ * 2.5.13.43 readerAndKeyIDMatch
+ * 2.5.13.44 attributeIntegrityMatch
+ *
+ * (*) described in RFC 3698 (LDAP: Additional Matching Rules)
+ * (+) described in draft-zeilenga-ldap-x509
+ */
+static slap_mrule_defs_rec mrule_defs[] = {
+ /*
+ * EQUALITY matching rules must be listed after associated APPROX
+ * matching rules. So, we list all APPROX matching rules first.
+ */
+ {"( " directoryStringApproxMatchOID " NAME 'directoryStringApproxMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
+ SLAP_MR_HIDE | SLAP_MR_EQUALITY_APPROX | SLAP_MR_EXT, NULL,
+ NULL, NULL, directoryStringApproxMatch,
+ directoryStringApproxIndexer, directoryStringApproxFilter,
+ NULL},
+
+ {"( " IA5StringApproxMatchOID " NAME 'IA5StringApproxMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
+ SLAP_MR_HIDE | SLAP_MR_EQUALITY_APPROX | SLAP_MR_EXT, NULL,
+ NULL, NULL, IA5StringApproxMatch,
+ IA5StringApproxIndexer, IA5StringApproxFilter,
+ NULL},
+
+ /*
+ * Other matching rules
+ */
+
+ {"( 2.5.13.0 NAME 'objectIdentifierMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )",
+ SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
+ NULL, NULL, octetStringMatch,
+ octetStringIndexer, octetStringFilter,
+ NULL },
+
+ {"( 2.5.13.1 NAME 'distinguishedNameMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
+ SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
+ NULL, dnNormalize, dnMatch,
+ octetStringIndexer, octetStringFilter,
+ NULL },
+
+ {"( 1.3.6.1.4.1.4203.666.4.9 NAME 'dnSubtreeMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
+ SLAP_MR_HIDE | SLAP_MR_EXT, NULL,
+ NULL, dnNormalize, dnRelativeMatch,
+ NULL, NULL,
+ NULL },
+
+ {"( 1.3.6.1.4.1.4203.666.4.8 NAME 'dnOneLevelMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
+ SLAP_MR_HIDE | SLAP_MR_EXT, NULL,
+ NULL, dnNormalize, dnRelativeMatch,
+ NULL, NULL,
+ NULL },
+
+ {"( 1.3.6.1.4.1.4203.666.4.10 NAME 'dnSubordinateMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
+ SLAP_MR_HIDE | SLAP_MR_EXT, NULL,
+ NULL, dnNormalize, dnRelativeMatch,
+ NULL, NULL,
+ NULL },
+
+ {"( 1.3.6.1.4.1.4203.666.4.11 NAME 'dnSuperiorMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
+ SLAP_MR_HIDE | SLAP_MR_EXT, NULL,
+ NULL, dnNormalize, dnRelativeMatch,
+ NULL, NULL,
+ NULL },
+
+ {"( 1.2.36.79672281.1.13.3 NAME 'rdnMatch' "
+ "SYNTAX 1.2.36.79672281.1.5.0 )",
+ SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
+ NULL, rdnNormalize, rdnMatch,
+ octetStringIndexer, octetStringFilter,
+ NULL },
+
+#ifdef LDAP_COMP_MATCH
+ {"( 1.2.36.79672281.1.13.2 NAME 'componentFilterMatch' "
+ "SYNTAX 1.2.36.79672281.1.5.2 )", /* componentFilterMatch assertion */
+ SLAP_MR_EXT|SLAP_MR_COMPONENT, componentFilterMatchSyntaxes,
+ NULL, NULL , componentFilterMatch,
+ octetStringIndexer, octetStringFilter,
+ NULL },
+
+ {"( 1.2.36.79672281.1.13.6 NAME 'allComponentsMatch' "
+ "SYNTAX 1.2.36.79672281.1.5.3 )", /* allComponents */
+ SLAP_MR_EQUALITY|SLAP_MR_EXT|SLAP_MR_COMPONENT, NULL,
+ NULL, NULL , allComponentsMatch,
+ octetStringIndexer, octetStringFilter,
+ NULL },
+
+ {"( 1.2.36.79672281.1.13.7 NAME 'directoryComponentsMatch' "
+ "SYNTAX 1.2.36.79672281.1.5.3 )", /* allComponents */
+ SLAP_MR_EQUALITY|SLAP_MR_EXT|SLAP_MR_COMPONENT, NULL,
+ NULL, NULL , directoryComponentsMatch,
+ octetStringIndexer, octetStringFilter,
+ NULL },
+#endif
+
+ {"( 2.5.13.2 NAME 'caseIgnoreMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
+ SLAP_MR_EQUALITY | SLAP_MR_EXT, directoryStringSyntaxes,
+ NULL, UTF8StringNormalize, octetStringMatch,
+ octetStringIndexer, octetStringFilter,
+ directoryStringApproxMatchOID },
+
+ {"( 2.5.13.3 NAME 'caseIgnoreOrderingMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
+ SLAP_MR_ORDERING | SLAP_MR_EXT, directoryStringSyntaxes,
+ NULL, UTF8StringNormalize, octetStringOrderingMatch,
+ NULL, NULL,
+ "caseIgnoreMatch" },
+
+ {"( 2.5.13.4 NAME 'caseIgnoreSubstringsMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */
+ SLAP_MR_SUBSTR, directoryStringSyntaxes,
+ NULL, UTF8StringNormalize, directoryStringSubstringsMatch,
+ octetStringSubstringsIndexer, octetStringSubstringsFilter,
+ "caseIgnoreMatch" },
+
+ {"( 2.5.13.5 NAME 'caseExactMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
+ SLAP_MR_EQUALITY | SLAP_MR_EXT, directoryStringSyntaxes,
+ NULL, UTF8StringNormalize, octetStringMatch,
+ octetStringIndexer, octetStringFilter,
+ directoryStringApproxMatchOID },
+
+ {"( 2.5.13.6 NAME 'caseExactOrderingMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
+ SLAP_MR_ORDERING | SLAP_MR_EXT, directoryStringSyntaxes,
+ NULL, UTF8StringNormalize, octetStringOrderingMatch,
+ NULL, NULL,
+ "caseExactMatch" },
+
+ {"( 2.5.13.7 NAME 'caseExactSubstringsMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */
+ SLAP_MR_SUBSTR, directoryStringSyntaxes,
+ NULL, UTF8StringNormalize, directoryStringSubstringsMatch,
+ octetStringSubstringsIndexer, octetStringSubstringsFilter,
+ "caseExactMatch" },
+
+ {"( 2.5.13.8 NAME 'numericStringMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )",
+ SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
+ NULL, numericStringNormalize, octetStringMatch,
+ octetStringIndexer, octetStringFilter,
+ NULL },
+
+ {"( 2.5.13.9 NAME 'numericStringOrderingMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 )",
+ SLAP_MR_ORDERING | SLAP_MR_EXT, NULL,
+ NULL, numericStringNormalize, octetStringOrderingMatch,
+ NULL, NULL,
+ "numericStringMatch" },
+
+ {"( 2.5.13.10 NAME 'numericStringSubstringsMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */
+ SLAP_MR_SUBSTR, NULL,
+ NULL, numericStringNormalize, octetStringSubstringsMatch,
+ octetStringSubstringsIndexer, octetStringSubstringsFilter,
+ "numericStringMatch" },
+
+ {"( 2.5.13.11 NAME 'caseIgnoreListMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )", /* Postal Address */
+ SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
+ NULL, postalAddressNormalize, octetStringMatch,
+ octetStringIndexer, octetStringFilter,
+ NULL },
+
+ {"( 2.5.13.12 NAME 'caseIgnoreListSubstringsMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */
+ SLAP_MR_SUBSTR, NULL,
+ NULL, postalAddressNormalize, directoryStringSubstringsMatch,
+ octetStringSubstringsIndexer, octetStringSubstringsFilter,
+ "caseIgnoreListMatch" },
+
+ {"( 2.5.13.13 NAME 'booleanMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )",
+ SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
+ NULL, NULL, booleanMatch,
+ octetStringIndexer, octetStringFilter,
+ NULL },
+
+ {"( 2.5.13.14 NAME 'integerMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
+ SLAP_MR_EQUALITY | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL,
+ NULL, NULL, integerMatch,
+ integerIndexer, integerFilter,
+ NULL },
+
+ {"( 2.5.13.15 NAME 'integerOrderingMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )",
+ SLAP_MR_ORDERING | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL,
+ NULL, NULL, integerMatch,
+ NULL, NULL,
+ "integerMatch" },
+
+ {"( 2.5.13.16 NAME 'bitStringMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.6 )",
+ SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
+ NULL, NULL, octetStringMatch,
+ octetStringIndexer, octetStringFilter,
+ NULL },
+
+ {"( 2.5.13.17 NAME 'octetStringMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )",
+ SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
+ NULL, NULL, octetStringMatch,
+ octetStringIndexer, octetStringFilter,
+ NULL },
+
+ {"( 2.5.13.18 NAME 'octetStringOrderingMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )",
+ SLAP_MR_ORDERING | SLAP_MR_EXT, NULL,
+ NULL, NULL, octetStringOrderingMatch,
+ NULL, NULL,
+ "octetStringMatch" },
+
+ {"( 2.5.13.19 NAME 'octetStringSubstringsMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )",
+ SLAP_MR_SUBSTR, NULL,
+ NULL, NULL, octetStringSubstringsMatch,
+ octetStringSubstringsIndexer, octetStringSubstringsFilter,
+ "octetStringMatch" },
+
+ {"( 2.5.13.20 NAME 'telephoneNumberMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )",
+ SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
+ NULL,
+ telephoneNumberNormalize, octetStringMatch,
+ octetStringIndexer, octetStringFilter,
+ NULL },
+
+ {"( 2.5.13.21 NAME 'telephoneNumberSubstringsMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.58 )", /* Substring Assertion */
+ SLAP_MR_SUBSTR, NULL,
+ NULL, telephoneNumberNormalize, octetStringSubstringsMatch,
+ octetStringSubstringsIndexer, octetStringSubstringsFilter,
+ "telephoneNumberMatch" },
+
+ {"( 2.5.13.22 NAME 'presentationAddressMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.43 )",
+ SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL },
+
+ {"( 2.5.13.23 NAME 'uniqueMemberMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.34 )", /* Name And Optional UID */
+ SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
+ NULL, uniqueMemberNormalize, uniqueMemberMatch,
+ uniqueMemberIndexer, uniqueMemberFilter,
+ NULL },
+
+ {"( 2.5.13.24 NAME 'protocolInformationMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.42 )",
+ SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL },
+
+ {"( 2.5.13.27 NAME 'generalizedTimeMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )",
+ SLAP_MR_EQUALITY | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL,
+ NULL, generalizedTimeNormalize, octetStringMatch,
+ generalizedTimeIndexer, generalizedTimeFilter,
+ NULL },
+
+ {"( 2.5.13.28 NAME 'generalizedTimeOrderingMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 )",
+ SLAP_MR_ORDERING | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL,
+ NULL, generalizedTimeNormalize, generalizedTimeOrderingMatch,
+ NULL, NULL,
+ "generalizedTimeMatch" },
+
+ {"( 2.5.13.29 NAME 'integerFirstComponentMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", /* Integer */
+ SLAP_MR_EQUALITY | SLAP_MR_EXT,
+ integerFirstComponentMatchSyntaxes,
+ NULL, firstComponentNormalize, integerMatch,
+ octetStringIndexer, octetStringFilter,
+ NULL },
+
+ {"( 2.5.13.30 NAME 'objectIdentifierFirstComponentMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )", /* OID */
+ SLAP_MR_EQUALITY | SLAP_MR_EXT,
+ objectIdentifierFirstComponentMatchSyntaxes,
+ NULL, firstComponentNormalize, octetStringMatch,
+ octetStringIndexer, octetStringFilter,
+ NULL },
+
+ {"( 2.5.13.34 NAME 'certificateExactMatch' "
+ "SYNTAX 1.3.6.1.1.15.1 )", /* Certificate Exact Assertion */
+ SLAP_MR_EQUALITY | SLAP_MR_EXT, certificateExactMatchSyntaxes,
+ NULL, certificateExactNormalize, octetStringMatch,
+ octetStringIndexer, octetStringFilter,
+ NULL },
+
+ {"( 2.5.13.35 NAME 'certificateMatch' "
+ "SYNTAX 1.3.6.1.1.15.2 )", /* Certificate Assertion */
+ SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ NULL },
+
+ {"( 2.5.13.38 NAME 'certificateListExactMatch' "
+ "SYNTAX 1.3.6.1.1.15.5 )", /* Certificate List Exact Assertion */
+ SLAP_MR_EQUALITY | SLAP_MR_EXT, certificateListExactMatchSyntaxes,
+ NULL, certificateListExactNormalize, octetStringMatch,
+ octetStringIndexer, octetStringFilter,
+ NULL },
+
+ {"( 2.5.13.39 NAME 'certificateListMatch' "
+ "SYNTAX 1.3.6.1.1.15.6 )", /* Certificate List Assertion */
+ SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ NULL },
+
+ {"( 2.5.13.45 NAME 'attributeCertificateExactMatch' "
+ "SYNTAX " attributeCertificateExactAssertionSyntaxOID " )",
+ SLAP_MR_EQUALITY | SLAP_MR_EXT | SLAP_MR_HIDE, attributeCertificateExactMatchSyntaxes,
+ NULL, attributeCertificateExactNormalize, octetStringMatch,
+ octetStringIndexer, octetStringFilter,
+ NULL },
+
+ {"( 2.5.13.46 NAME 'attributeCertificateMatch' "
+ "SYNTAX " attributeCertificateAssertionSyntaxOID " )",
+ SLAP_MR_EQUALITY | SLAP_MR_EXT | SLAP_MR_HIDE, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ NULL },
+
+ {"( 1.3.6.1.4.1.1466.109.114.1 NAME 'caseExactIA5Match' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
+ SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
+ NULL, IA5StringNormalize, octetStringMatch,
+ octetStringIndexer, octetStringFilter,
+ IA5StringApproxMatchOID },
+
+ {"( 1.3.6.1.4.1.1466.109.114.2 NAME 'caseIgnoreIA5Match' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
+ SLAP_MR_EQUALITY | SLAP_MR_EXT, NULL,
+ NULL, IA5StringNormalize, octetStringMatch,
+ octetStringIndexer, octetStringFilter,
+ IA5StringApproxMatchOID },
+
+ {"( 1.3.6.1.4.1.1466.109.114.3 NAME 'caseIgnoreIA5SubstringsMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
+ SLAP_MR_SUBSTR, NULL,
+ NULL, IA5StringNormalize, directoryStringSubstringsMatch,
+ octetStringSubstringsIndexer, octetStringSubstringsFilter,
+ "caseIgnoreIA5Match" },
+
+ {"( 1.3.6.1.4.1.4203.1.2.1 NAME 'caseExactIA5SubstringsMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )",
+ SLAP_MR_SUBSTR, NULL,
+ NULL, IA5StringNormalize, directoryStringSubstringsMatch,
+ octetStringSubstringsIndexer, octetStringSubstringsFilter,
+ "caseExactIA5Match" },
+
+#ifdef SLAPD_AUTHPASSWD
+ /* needs updating */
+ {"( 1.3.6.1.4.1.4203.666.4.1 NAME 'authPasswordMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )", /* Octet String */
+ SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
+ NULL, NULL, authPasswordMatch,
+ NULL, NULL,
+ NULL},
+#endif
+
+ {"( 1.2.840.113556.1.4.803 NAME 'integerBitAndMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", /* Integer */
+ SLAP_MR_EXT, NULL,
+ NULL, NULL, integerBitAndMatch,
+ NULL, NULL,
+ "integerMatch" },
+
+ {"( 1.2.840.113556.1.4.804 NAME 'integerBitOrMatch' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", /* Integer */
+ SLAP_MR_EXT, NULL,
+ NULL, NULL, integerBitOrMatch,
+ NULL, NULL,
+ "integerMatch" },
+
+ {"( 1.3.6.1.1.16.2 NAME 'UUIDMatch' "
+ "SYNTAX 1.3.6.1.1.16.1 )",
+ SLAP_MR_EQUALITY | SLAP_MR_MUTATION_NORMALIZER, NULL,
+ NULL, UUIDNormalize, octetStringMatch,
+ octetStringIndexer, octetStringFilter,
+ NULL},
+
+ {"( 1.3.6.1.1.16.3 NAME 'UUIDOrderingMatch' "
+ "SYNTAX 1.3.6.1.1.16.1 )",
+ SLAP_MR_ORDERING | SLAP_MR_MUTATION_NORMALIZER, NULL,
+ NULL, UUIDNormalize, octetStringOrderingMatch,
+ octetStringIndexer, octetStringFilter,
+ "UUIDMatch"},
+
+ {"( 1.3.6.1.4.1.4203.666.11.2.2 NAME 'CSNMatch' "
+ "SYNTAX 1.3.6.1.4.1.4203.666.11.2.1 )",
+ SLAP_MR_HIDE | SLAP_MR_EQUALITY | SLAP_MR_ORDERED_INDEX, NULL,
+ NULL, csnNormalize, csnMatch,
+ csnIndexer, csnFilter,
+ NULL},
+
+ {"( 1.3.6.1.4.1.4203.666.11.2.3 NAME 'CSNOrderingMatch' "
+ "SYNTAX 1.3.6.1.4.1.4203.666.11.2.1 )",
+ SLAP_MR_HIDE | SLAP_MR_ORDERING | SLAP_MR_EXT | SLAP_MR_ORDERED_INDEX, NULL,
+ NULL, csnNormalize, csnOrderingMatch,
+ NULL, NULL,
+ "CSNMatch" },
+
+ {"( 1.3.6.1.4.1.4203.666.11.2.5 NAME 'CSNSIDMatch' "
+ "SYNTAX 1.3.6.1.4.1.4203.666.11.2.4 )",
+ SLAP_MR_HIDE | SLAP_MR_EQUALITY | SLAP_MR_EXT, csnSIDMatchSyntaxes,
+ NULL, csnSidNormalize, octetStringMatch,
+ octetStringIndexer, octetStringFilter,
+ NULL },
+
+ /* FIXME: OID is unused, but not registered yet */
+ {"( 1.3.6.1.4.1.4203.666.4.12 NAME 'authzMatch' "
+ "SYNTAX 1.3.6.1.4.1.4203.666.2.7 )", /* OpenLDAP authz */
+ SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
+ NULL, authzNormalize, authzMatch,
+ NULL, NULL,
+ NULL},
+
+ {"( 1.3.6.1.4.1.4203.666.4.13 NAME 'privateKeyMatch' "
+ "SYNTAX 1.2.840.113549.1.8.1.1 )", /* PKCS#8 privateKey */
+ SLAP_MR_HIDE | SLAP_MR_EQUALITY, NULL,
+ NULL, NULL, octetStringMatch,
+ NULL, NULL,
+ NULL},
+
+ {NULL, SLAP_MR_NONE, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ NULL }
+};
+
+int
+slap_schema_init( void )
+{
+ int res;
+ int i;
+
+ /* we should only be called once (from main) */
+ assert( schema_init_done == 0 );
+
+ for ( i=0; syntax_defs[i].sd_desc != NULL; i++ ) {
+ res = register_syntax( &syntax_defs[i] );
+
+ if ( res ) {
+ fprintf( stderr, "slap_schema_init: Error registering syntax %s\n",
+ syntax_defs[i].sd_desc );
+ return LDAP_OTHER;
+ }
+ }
+
+ for ( i=0; mrule_defs[i].mrd_desc != NULL; i++ ) {
+ if( mrule_defs[i].mrd_usage == SLAP_MR_NONE &&
+ mrule_defs[i].mrd_compat_syntaxes == NULL )
+ {
+ fprintf( stderr,
+ "slap_schema_init: Ignoring unusable matching rule %s\n",
+ mrule_defs[i].mrd_desc );
+ continue;
+ }
+
+ res = register_matching_rule( &mrule_defs[i] );
+
+ if ( res ) {
+ fprintf( stderr,
+ "slap_schema_init: Error registering matching rule %s\n",
+ mrule_defs[i].mrd_desc );
+ return LDAP_OTHER;
+ }
+ }
+
+ res = slap_schema_load();
+ schema_init_done = 1;
+ return res;
+}
+
+void
+schema_destroy( void )
+{
+ oidm_destroy();
+ oc_destroy();
+ at_destroy();
+ mr_destroy();
+ mru_destroy();
+ syn_destroy();
+
+ if( schema_init_done ) {
+ ldap_pvt_thread_mutex_destroy( &ad_index_mutex );
+ ldap_pvt_thread_mutex_destroy( &ad_undef_mutex );
+ ldap_pvt_thread_mutex_destroy( &oc_undef_mutex );
+ }
+}
diff --git a/servers/slapd/schema_prep.c b/servers/slapd/schema_prep.c
new file mode 100644
index 0000000..2c55790
--- /dev/null
+++ b/servers/slapd/schema_prep.c
@@ -0,0 +1,1638 @@
+/* schema_prep.c - load builtin schema */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+#define OCDEBUG 0
+
+int schema_init_done = 0;
+
+struct slap_internal_schema slap_schema;
+
+static int
+oidValidate(
+ Syntax *syntax,
+ struct berval *in )
+{
+ struct berval val = *in;
+
+ if( val.bv_len == 0 ) {
+ /* disallow empty strings */
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if( DESC_LEADCHAR( val.bv_val[0] ) ) {
+ val.bv_val++;
+ val.bv_len--;
+ if ( val.bv_len == 0 ) return LDAP_SUCCESS;
+
+ while( DESC_CHAR( val.bv_val[0] ) ) {
+ val.bv_val++;
+ val.bv_len--;
+
+ if ( val.bv_len == 0 ) return LDAP_SUCCESS;
+ }
+
+ } else {
+ int sep = 0;
+ while( OID_LEADCHAR( val.bv_val[0] ) ) {
+ val.bv_val++;
+ val.bv_len--;
+
+ if ( val.bv_val[-1] != '0' ) {
+ while ( OID_LEADCHAR( val.bv_val[0] )) {
+ val.bv_val++;
+ val.bv_len--;
+ }
+ }
+
+ if( val.bv_len == 0 ) {
+ if( sep == 0 ) break;
+ return LDAP_SUCCESS;
+ }
+
+ if( !OID_SEPARATOR( val.bv_val[0] )) break;
+
+ sep++;
+ val.bv_val++;
+ val.bv_len--;
+ }
+ }
+
+ return LDAP_INVALID_SYNTAX;
+}
+
+
+static int objectClassPretty(
+ Syntax *syntax,
+ struct berval *in,
+ struct berval *out,
+ void *ctx )
+{
+ ObjectClass *oc;
+
+ if( oidValidate( NULL, in )) return LDAP_INVALID_SYNTAX;
+
+ oc = oc_bvfind( in );
+ if( oc == NULL ) return LDAP_INVALID_SYNTAX;
+
+ ber_dupbv_x( out, &oc->soc_cname, ctx );
+ return LDAP_SUCCESS;
+}
+
+static int
+attributeTypeMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ struct berval *a = (struct berval *) assertedValue;
+ AttributeType *at = at_bvfind( value );
+ AttributeType *asserted = at_bvfind( a );
+
+ if( asserted == NULL ) {
+ if( OID_LEADCHAR( *a->bv_val ) ) {
+ /* OID form, return FALSE */
+ *matchp = 1;
+ return LDAP_SUCCESS;
+ }
+
+ /* desc form, return undefined */
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( at == NULL ) {
+ /* unrecognized stored value */
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ *matchp = ( asserted != at );
+ return LDAP_SUCCESS;
+}
+
+static int
+matchingRuleMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ struct berval *a = (struct berval *) assertedValue;
+ MatchingRule *mrv = mr_bvfind( value );
+ MatchingRule *asserted = mr_bvfind( a );
+
+ if( asserted == NULL ) {
+ if( OID_LEADCHAR( *a->bv_val ) ) {
+ /* OID form, return FALSE */
+ *matchp = 1;
+ return LDAP_SUCCESS;
+ }
+
+ /* desc form, return undefined */
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( mrv == NULL ) {
+ /* unrecognized stored value */
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ *matchp = ( asserted != mrv );
+ return LDAP_SUCCESS;
+}
+
+static int
+objectClassMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ struct berval *a = (struct berval *) assertedValue;
+ ObjectClass *oc = oc_bvfind( value );
+ ObjectClass *asserted = oc_bvfind( a );
+
+ if( asserted == NULL ) {
+ if( OID_LEADCHAR( *a->bv_val ) ) {
+ /* OID form, return FALSE */
+ *matchp = 1;
+ return LDAP_SUCCESS;
+ }
+
+ /* desc form, return undefined */
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( oc == NULL ) {
+ /* unrecognized stored value */
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ *matchp = ( asserted != oc );
+ return LDAP_SUCCESS;
+}
+
+static int
+objectSubClassMatch(
+ int *matchp,
+ slap_mask_t flags,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertedValue )
+{
+ struct berval *a = (struct berval *) assertedValue;
+ ObjectClass *oc = oc_bvfind( value );
+ ObjectClass *asserted = oc_bvfind( a );
+
+ if( asserted == NULL ) {
+ if( OID_LEADCHAR( *a->bv_val ) ) {
+ /* OID form, return FALSE */
+ *matchp = 1;
+ return LDAP_SUCCESS;
+ }
+
+ /* desc form, return undefined */
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( oc == NULL ) {
+ /* unrecognized stored value */
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if( SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX( flags ) ) {
+ *matchp = ( asserted != oc );
+ } else {
+ *matchp = !is_object_subclass( asserted, oc );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int objectSubClassIndexer(
+ slap_mask_t use,
+ slap_mask_t mask,
+ Syntax *syntax,
+ MatchingRule *mr,
+ struct berval *prefix,
+ BerVarray values,
+ BerVarray *keysp,
+ void *ctx )
+{
+ int rc, noc, i;
+ BerVarray ocvalues;
+ ObjectClass **socs;
+
+ for( noc=0; values[noc].bv_val != NULL; noc++ ) {
+ /* just count em */;
+ }
+
+ /* over allocate */
+ socs = slap_sl_malloc( (noc+16) * sizeof( ObjectClass * ), ctx );
+
+ /* initialize */
+ for( i=0; i<noc; i++ ) {
+ socs[i] = oc_bvfind( &values[i] );
+ }
+
+ /* expand values */
+ for( i=0; i<noc; i++ ) {
+ int j;
+ ObjectClass *oc = socs[i];
+ if( oc == NULL || oc->soc_sups == NULL ) continue;
+
+ for( j=0; oc->soc_sups[j] != NULL; j++ ) {
+ int found = 0;
+ ObjectClass *sup = oc->soc_sups[j];
+ int k;
+
+ for( k=0; k<noc; k++ ) {
+ if( sup == socs[k] ) {
+ found++;
+ break;
+ }
+ }
+
+ if( !found ) {
+ socs = slap_sl_realloc( socs,
+ sizeof( ObjectClass * ) * (noc+2), ctx );
+
+ assert( k == noc );
+ socs[noc++] = sup;
+ }
+ }
+ }
+
+ ocvalues = slap_sl_malloc( sizeof( struct berval ) * (noc+1), ctx );
+ /* copy values */
+ for( i=0; i<noc; i++ ) {
+ if ( socs[i] )
+ ocvalues[i] = socs[i]->soc_cname;
+ else
+ ocvalues[i] = values[i];
+ }
+ BER_BVZERO( &ocvalues[i] );
+
+ rc = octetStringIndexer( use, mask, syntax, mr,
+ prefix, ocvalues, keysp, ctx );
+
+ slap_sl_free( ocvalues, ctx );
+ slap_sl_free( socs, ctx );
+ return rc;
+}
+
+#define objectSubClassFilter octetStringFilter
+
+static ObjectClassSchemaCheckFN rootDseObjectClass;
+static ObjectClassSchemaCheckFN aliasObjectClass;
+static ObjectClassSchemaCheckFN referralObjectClass;
+static ObjectClassSchemaCheckFN subentryObjectClass;
+#ifdef LDAP_DYNAMIC_OBJECTS
+static ObjectClassSchemaCheckFN dynamicObjectClass;
+#endif
+
+static struct slap_schema_oc_map {
+ char *ssom_name;
+ char *ssom_defn;
+ ObjectClassSchemaCheckFN *ssom_check;
+ slap_mask_t ssom_flags;
+ size_t ssom_offset;
+} oc_map[] = {
+ { "top", "( 2.5.6.0 NAME 'top' "
+ "DESC 'top of the superclass chain' "
+ "ABSTRACT MUST objectClass )",
+ 0, 0, offsetof(struct slap_internal_schema, si_oc_top) },
+ { "extensibleObject", "( 1.3.6.1.4.1.1466.101.120.111 "
+ "NAME 'extensibleObject' "
+ "DESC 'RFC4512: extensible object' "
+ "SUP top AUXILIARY )",
+ 0, SLAP_OC_OPERATIONAL,
+ offsetof(struct slap_internal_schema, si_oc_extensibleObject) },
+ { "alias", "( 2.5.6.1 NAME 'alias' "
+ "DESC 'RFC4512: an alias' "
+ "SUP top STRUCTURAL "
+ "MUST aliasedObjectName )",
+ aliasObjectClass, SLAP_OC_ALIAS|SLAP_OC_OPERATIONAL,
+ offsetof(struct slap_internal_schema, si_oc_alias) },
+ { "referral", "( 2.16.840.1.113730.3.2.6 NAME 'referral' "
+ "DESC 'namedref: named subordinate referral' "
+ "SUP top STRUCTURAL MUST ref )",
+ referralObjectClass, SLAP_OC_REFERRAL|SLAP_OC_OPERATIONAL,
+ offsetof(struct slap_internal_schema, si_oc_referral) },
+ { "LDAProotDSE", "( 1.3.6.1.4.1.4203.1.4.1 "
+ "NAME ( 'OpenLDAProotDSE' 'LDAProotDSE' ) "
+ "DESC 'OpenLDAP Root DSE object' "
+ "SUP top STRUCTURAL MAY cn )",
+ rootDseObjectClass, SLAP_OC_OPERATIONAL,
+ offsetof(struct slap_internal_schema, si_oc_rootdse) },
+ { "subentry", "( 2.5.17.0 NAME 'subentry' "
+ "DESC 'RFC3672: subentry' "
+ "SUP top STRUCTURAL "
+ "MUST ( cn $ subtreeSpecification ) )",
+ subentryObjectClass, SLAP_OC_SUBENTRY|SLAP_OC_OPERATIONAL,
+ offsetof(struct slap_internal_schema, si_oc_subentry) },
+ { "subschema", "( 2.5.20.1 NAME 'subschema' "
+ "DESC 'RFC4512: controlling subschema (sub)entry' "
+ "AUXILIARY "
+ "MAY ( dITStructureRules $ nameForms $ dITContentRules $ "
+ "objectClasses $ attributeTypes $ matchingRules $ "
+ "matchingRuleUse ) )",
+ subentryObjectClass, SLAP_OC_OPERATIONAL,
+ offsetof(struct slap_internal_schema, si_oc_subschema) },
+#ifdef LDAP_COLLECTIVE_ATTRIBUTES
+ { "collectiveAttributeSubentry", "( 2.5.17.2 "
+ "NAME 'collectiveAttributeSubentry' "
+ "DESC 'RFC3671: collective attribute subentry' "
+ "AUXILIARY )",
+ subentryObjectClass,
+ SLAP_OC_COLLECTIVEATTRIBUTESUBENTRY|SLAP_OC_OPERATIONAL|SLAP_OC_HIDE,
+ offsetof( struct slap_internal_schema,
+ si_oc_collectiveAttributeSubentry) },
+#endif
+#ifdef LDAP_DYNAMIC_OBJECTS
+ { "dynamicObject", "( 1.3.6.1.4.1.1466.101.119.2 "
+ "NAME 'dynamicObject' "
+ "DESC 'RFC2589: Dynamic Object' "
+ "SUP top AUXILIARY )",
+ dynamicObjectClass, SLAP_OC_DYNAMICOBJECT,
+ offsetof(struct slap_internal_schema, si_oc_dynamicObject) },
+#endif
+ { "glue", "( 1.3.6.1.4.1.4203.666.3.4 "
+ "NAME 'glue' "
+ "DESC 'Glue Entry' "
+ "SUP top STRUCTURAL )",
+ 0, SLAP_OC_GLUE|SLAP_OC_OPERATIONAL|SLAP_OC_HIDE,
+ offsetof(struct slap_internal_schema, si_oc_glue) },
+ { "syncConsumerSubentry", "( 1.3.6.1.4.1.4203.666.3.5 "
+ "NAME 'syncConsumerSubentry' "
+ "DESC 'Persistent Info for SyncRepl Consumer' "
+ "AUXILIARY "
+ "MAY syncreplCookie )",
+ 0, SLAP_OC_SYNCCONSUMERSUBENTRY|SLAP_OC_OPERATIONAL|SLAP_OC_HIDE,
+ offsetof(struct slap_internal_schema, si_oc_syncConsumerSubentry) },
+ { "syncProviderSubentry", "( 1.3.6.1.4.1.4203.666.3.6 "
+ "NAME 'syncProviderSubentry' "
+ "DESC 'Persistent Info for SyncRepl Producer' "
+ "AUXILIARY "
+ "MAY contextCSN )",
+ 0, SLAP_OC_SYNCPROVIDERSUBENTRY|SLAP_OC_OPERATIONAL|SLAP_OC_HIDE,
+ offsetof(struct slap_internal_schema, si_oc_syncProviderSubentry) },
+
+ { NULL, NULL, NULL, 0, 0 }
+};
+
+static AttributeTypeSchemaCheckFN rootDseAttribute;
+static AttributeTypeSchemaCheckFN aliasAttribute;
+static AttributeTypeSchemaCheckFN referralAttribute;
+static AttributeTypeSchemaCheckFN subentryAttribute;
+static AttributeTypeSchemaCheckFN administrativeRoleAttribute;
+#ifdef LDAP_DYNAMIC_OBJECTS
+static AttributeTypeSchemaCheckFN dynamicAttribute;
+#endif
+
+static struct slap_schema_ad_map {
+ char *ssam_name;
+ char *ssam_defn;
+ AttributeTypeSchemaCheckFN *ssam_check;
+ slap_mask_t ssam_flags;
+ slap_syntax_validate_func *ssam_syn_validate;
+ slap_syntax_transform_func *ssam_syn_pretty;
+ slap_mr_convert_func *ssam_mr_convert;
+ slap_mr_normalize_func *ssam_mr_normalize;
+ slap_mr_match_func *ssam_mr_match;
+ slap_mr_indexer_func *ssam_mr_indexer;
+ slap_mr_filter_func *ssam_mr_filter;
+ size_t ssam_offset;
+} ad_map[] = {
+ { "objectClass", "( 2.5.4.0 NAME 'objectClass' "
+ "DESC 'RFC4512: object classes of the entity' "
+ "EQUALITY objectIdentifierMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )",
+ NULL, SLAP_AT_FINAL,
+ oidValidate, objectClassPretty,
+ NULL, NULL, objectSubClassMatch,
+ objectSubClassIndexer, objectSubClassFilter,
+ offsetof(struct slap_internal_schema, si_ad_objectClass) },
+
+ /* user entry operational attributes */
+ { "structuralObjectClass", "( 2.5.21.9 NAME 'structuralObjectClass' "
+ "DESC 'RFC4512: structural object class of entry' "
+ "EQUALITY objectIdentifierMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
+ NULL, 0,
+ oidValidate, objectClassPretty,
+ NULL, NULL, objectSubClassMatch,
+ objectSubClassIndexer, objectSubClassFilter,
+ offsetof(struct slap_internal_schema, si_ad_structuralObjectClass) },
+ { "createTimestamp", "( 2.5.18.1 NAME 'createTimestamp' "
+ "DESC 'RFC4512: time which object was created' "
+ "EQUALITY generalizedTimeMatch "
+ "ORDERING generalizedTimeOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
+ NULL, SLAP_AT_MANAGEABLE,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_createTimestamp) },
+ { "modifyTimestamp", "( 2.5.18.2 NAME 'modifyTimestamp' "
+ "DESC 'RFC4512: time which object was last modified' "
+ "EQUALITY generalizedTimeMatch "
+ "ORDERING generalizedTimeOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
+ NULL, SLAP_AT_MANAGEABLE,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_modifyTimestamp) },
+ { "creatorsName", "( 2.5.18.3 NAME 'creatorsName' "
+ "DESC 'RFC4512: name of creator' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
+ NULL, SLAP_AT_MANAGEABLE,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_creatorsName) },
+ { "modifiersName", "( 2.5.18.4 NAME 'modifiersName' "
+ "DESC 'RFC4512: name of last modifier' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
+ NULL, SLAP_AT_MANAGEABLE,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_modifiersName) },
+ { "hasSubordinates", "( 2.5.18.9 NAME 'hasSubordinates' "
+ "DESC 'X.501: entry has children' "
+ "EQUALITY booleanMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
+ NULL, SLAP_AT_DYNAMIC,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_hasSubordinates) },
+ { "subschemaSubentry", "( 2.5.18.10 NAME 'subschemaSubentry' "
+ "DESC 'RFC4512: name of controlling subschema entry' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE "
+ "NO-USER-MODIFICATION USAGE directoryOperation )",
+ NULL, SLAP_AT_DYNAMIC,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_subschemaSubentry) },
+#ifdef LDAP_COLLECTIVE_ATTRIBUTES
+ { "collectiveAttributeSubentries", "( 2.5.18.12 "
+ "NAME 'collectiveAttributeSubentries' "
+ "DESC 'RFC3671: collective attribute subentries' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
+ "NO-USER-MODIFICATION USAGE directoryOperation )",
+ NULL, SLAP_AT_HIDE,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_collectiveSubentries) },
+ { "collectiveExclusions", "( 2.5.18.7 NAME 'collectiveExclusions' "
+ "DESC 'RFC3671: collective attribute exclusions' "
+ "EQUALITY objectIdentifierMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
+ "USAGE directoryOperation )",
+ NULL, SLAP_AT_HIDE,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_collectiveExclusions) },
+#endif
+
+ { "entryDN", "( 1.3.6.1.1.20 NAME 'entryDN' "
+ "DESC 'DN of the entry' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
+ NULL, SLAP_AT_DYNAMIC,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_entryDN) },
+ { "entryUUID", "( 1.3.6.1.1.16.4 NAME 'entryUUID' "
+ "DESC 'UUID of the entry' "
+ "EQUALITY UUIDMatch "
+ "ORDERING UUIDOrderingMatch "
+ "SYNTAX 1.3.6.1.1.16.1 "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
+ NULL, SLAP_AT_MANAGEABLE,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_entryUUID) },
+ { "entryCSN", "( 1.3.6.1.4.1.4203.666.1.7 NAME 'entryCSN' "
+ "DESC 'change sequence number of the entry content' "
+ "EQUALITY CSNMatch "
+ "ORDERING CSNOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.4203.666.11.2.1{64} "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
+ NULL, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_entryCSN) },
+ { "namingCSN", "( 1.3.6.1.4.1.4203.666.1.13 NAME 'namingCSN' "
+ "DESC 'change sequence number of the entry naming (RDN)' "
+ "EQUALITY CSNMatch "
+ "ORDERING CSNOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.4203.666.11.2.1{64} "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
+ NULL, SLAP_AT_HIDE,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_namingCSN) },
+
+#ifdef LDAP_SUPERIOR_UUID
+ { "superiorUUID", "( 1.3.6.1.4.1.4203.666.1.11 NAME 'superiorUUID' "
+ "DESC 'UUID of the superior entry' "
+ "EQUALITY UUIDMatch "
+ "ORDERING UUIDOrderingMatch "
+ "SYNTAX 1.3.6.1.1.16.1 "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )",
+ NULL, SLAP_AT_HIDE,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_superiorUUID) },
+#endif
+
+ { "syncreplCookie", "( 1.3.6.1.4.1.4203.666.1.23 "
+ "NAME 'syncreplCookie' "
+ "DESC 'syncrepl Cookie for shadow copy' "
+ "EQUALITY octetStringMatch "
+ "ORDERING octetStringOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation )",
+ NULL, SLAP_AT_HIDE,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_syncreplCookie) },
+
+ { "contextCSN", "( 1.3.6.1.4.1.4203.666.1.25 "
+ "NAME 'contextCSN' "
+ "DESC 'the largest committed CSN of a context' "
+ "EQUALITY CSNMatch "
+ "ORDERING CSNOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.4203.666.11.2.1{64} "
+ "NO-USER-MODIFICATION USAGE dSAOperation )",
+ NULL, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_contextCSN) },
+
+#ifdef LDAP_SYNC_TIMESTAMP
+ { "syncTimestamp", "( 1.3.6.1.4.1.4203.666.1.26 NAME 'syncTimestamp' "
+ "DESC 'Time which object was replicated' "
+ "EQUALITY generalizedTimeMatch "
+ "ORDERING generalizedTimeOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation )",
+ NULL, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_syncTimestamp) },
+#endif
+
+ /* root DSE attributes */
+ { "altServer", "( 1.3.6.1.4.1.1466.101.120.6 NAME 'altServer' "
+ "DESC 'RFC4512: alternative servers' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 USAGE dSAOperation )",
+ rootDseAttribute, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_altServer) },
+ { "namingContexts", "( 1.3.6.1.4.1.1466.101.120.5 "
+ "NAME 'namingContexts' "
+ "DESC 'RFC4512: naming contexts' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 USAGE dSAOperation )",
+ rootDseAttribute, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_namingContexts) },
+ { "supportedControl", "( 1.3.6.1.4.1.1466.101.120.13 "
+ "NAME 'supportedControl' "
+ "DESC 'RFC4512: supported controls' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 USAGE dSAOperation )",
+ rootDseAttribute, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_supportedControl) },
+ { "supportedExtension", "( 1.3.6.1.4.1.1466.101.120.7 "
+ "NAME 'supportedExtension' "
+ "DESC 'RFC4512: supported extended operations' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 USAGE dSAOperation )",
+ rootDseAttribute, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_supportedExtension) },
+ { "supportedLDAPVersion", "( 1.3.6.1.4.1.1466.101.120.15 "
+ "NAME 'supportedLDAPVersion' "
+ "DESC 'RFC4512: supported LDAP versions' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 USAGE dSAOperation )",
+ rootDseAttribute, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_supportedLDAPVersion) },
+ { "supportedSASLMechanisms", "( 1.3.6.1.4.1.1466.101.120.14 "
+ "NAME 'supportedSASLMechanisms' "
+ "DESC 'RFC4512: supported SASL mechanisms'"
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 USAGE dSAOperation )",
+ rootDseAttribute, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_supportedSASLMechanisms) },
+ { "supportedFeatures", "( 1.3.6.1.4.1.4203.1.3.5 "
+ "NAME 'supportedFeatures' "
+ "DESC 'RFC4512: features supported by the server' "
+ "EQUALITY objectIdentifierMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
+ "USAGE dSAOperation )",
+ rootDseAttribute, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_supportedFeatures) },
+ { "monitorContext", "( 1.3.6.1.4.1.4203.666.1.10 "
+ "NAME 'monitorContext' "
+ "DESC 'monitor context' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
+ "EQUALITY distinguishedNameMatch "
+ "SINGLE-VALUE NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ rootDseAttribute, SLAP_AT_HIDE,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_monitorContext) },
+ { "configContext", "( 1.3.6.1.4.1.4203.1.12.2.1 "
+ "NAME 'configContext' "
+ "DESC 'config context' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
+ "EQUALITY distinguishedNameMatch "
+ "SINGLE-VALUE NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ rootDseAttribute, SLAP_AT_HIDE,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_configContext) },
+ { "vendorName", "( 1.3.6.1.1.4 NAME 'vendorName' "
+ "DESC 'RFC3045: name of implementation vendor' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
+ "SINGLE-VALUE NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ rootDseAttribute, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_vendorName) },
+ { "vendorVersion", "( 1.3.6.1.1.5 NAME 'vendorVersion' "
+ "DESC 'RFC3045: version of implementation' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
+ "SINGLE-VALUE NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ rootDseAttribute, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_vendorVersion) },
+
+ /* subentry attributes */
+ { "administrativeRole", "( 2.5.18.5 NAME 'administrativeRole' "
+ "DESC 'RFC3672: administrative role' "
+ "EQUALITY objectIdentifierMatch "
+ "USAGE directoryOperation "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )",
+ administrativeRoleAttribute, SLAP_AT_HIDE,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_administrativeRole) },
+ { "subtreeSpecification", "( 2.5.18.6 NAME 'subtreeSpecification' "
+ "DESC 'RFC3672: subtree specification' "
+ "SINGLE-VALUE "
+ "USAGE directoryOperation "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.45 )",
+ subentryAttribute, SLAP_AT_HIDE,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_subtreeSpecification) },
+
+ /* subschema subentry attributes */
+ { "dITStructureRules", "( 2.5.21.1 NAME 'dITStructureRules' "
+ "DESC 'RFC4512: DIT structure rules' "
+ "EQUALITY integerFirstComponentMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.17 "
+ "USAGE directoryOperation ) ",
+ subentryAttribute, SLAP_AT_HIDE,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_ditStructureRules) },
+ { "dITContentRules", "( 2.5.21.2 NAME 'dITContentRules' "
+ "DESC 'RFC4512: DIT content rules' "
+ "EQUALITY objectIdentifierFirstComponentMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.16 USAGE directoryOperation )",
+ subentryAttribute, SLAP_AT_HIDE,
+ oidValidate, NULL,
+ NULL, NULL, objectClassMatch, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_ditContentRules) },
+ { "matchingRules", "( 2.5.21.4 NAME 'matchingRules' "
+ "DESC 'RFC4512: matching rules' "
+ "EQUALITY objectIdentifierFirstComponentMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.30 USAGE directoryOperation )",
+ subentryAttribute, 0,
+ oidValidate, NULL,
+ NULL, NULL, matchingRuleMatch, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_matchingRules) },
+ { "attributeTypes", "( 2.5.21.5 NAME 'attributeTypes' "
+ "DESC 'RFC4512: attribute types' "
+ "EQUALITY objectIdentifierFirstComponentMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.3 USAGE directoryOperation )",
+ subentryAttribute, 0,
+ oidValidate, NULL,
+ NULL, NULL, attributeTypeMatch, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_attributeTypes) },
+ { "objectClasses", "( 2.5.21.6 NAME 'objectClasses' "
+ "DESC 'RFC4512: object classes' "
+ "EQUALITY objectIdentifierFirstComponentMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.37 USAGE directoryOperation )",
+ subentryAttribute, 0,
+ oidValidate, NULL,
+ NULL, NULL, objectClassMatch, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_objectClasses) },
+ { "nameForms", "( 2.5.21.7 NAME 'nameForms' "
+ "DESC 'RFC4512: name forms ' "
+ "EQUALITY objectIdentifierFirstComponentMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.35 USAGE directoryOperation )",
+ subentryAttribute, SLAP_AT_HIDE,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_nameForms) },
+ { "matchingRuleUse", "( 2.5.21.8 NAME 'matchingRuleUse' "
+ "DESC 'RFC4512: matching rule uses' "
+ "EQUALITY objectIdentifierFirstComponentMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.31 USAGE directoryOperation )",
+ subentryAttribute, 0,
+ oidValidate, NULL,
+ NULL, NULL, matchingRuleMatch, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_matchingRuleUse) },
+
+ { "ldapSyntaxes", "( 1.3.6.1.4.1.1466.101.120.16 NAME 'ldapSyntaxes' "
+ "DESC 'RFC4512: LDAP syntaxes' "
+ "EQUALITY objectIdentifierFirstComponentMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.54 USAGE directoryOperation )",
+ subentryAttribute, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_ldapSyntaxes) },
+
+ /* knowledge information */
+ { "aliasedObjectName", "( 2.5.4.1 "
+ "NAME ( 'aliasedObjectName' 'aliasedEntryName' ) "
+ "DESC 'RFC4512: name of aliased object' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE )",
+ aliasAttribute, SLAP_AT_FINAL,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_aliasedObjectName) },
+ { "ref", "( 2.16.840.1.113730.3.1.34 NAME 'ref' "
+ "DESC 'RFC3296: subordinate referral URL' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
+ "USAGE distributedOperation )",
+ referralAttribute, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_ref) },
+
+ /* access control internals */
+ { "entry", "( 1.3.6.1.4.1.4203.1.3.1 "
+ "NAME 'entry' "
+ "DESC 'OpenLDAP ACL entry pseudo-attribute' "
+ "SYNTAX 1.3.6.1.4.1.4203.1.1.1 "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation )",
+ NULL, SLAP_AT_HIDE,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_entry) },
+ { "children", "( 1.3.6.1.4.1.4203.1.3.2 "
+ "NAME 'children' "
+ "DESC 'OpenLDAP ACL children pseudo-attribute' "
+ "SYNTAX 1.3.6.1.4.1.4203.1.1.1 "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation )",
+ NULL, SLAP_AT_HIDE,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_children) },
+
+ /* access control externals */
+ { "authzTo", "( 1.3.6.1.4.1.4203.666.1.8 "
+ "NAME ( 'authzTo' 'saslAuthzTo' ) "
+ "DESC 'proxy authorization targets' "
+ "EQUALITY authzMatch "
+ "SYNTAX 1.3.6.1.4.1.4203.666.2.7 "
+ "X-ORDERED 'VALUES' "
+ "USAGE distributedOperation )",
+ NULL, SLAP_AT_HIDE,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_saslAuthzTo) },
+ { "authzFrom", "( 1.3.6.1.4.1.4203.666.1.9 "
+ "NAME ( 'authzFrom' 'saslAuthzFrom' ) "
+ "DESC 'proxy authorization sources' "
+ "EQUALITY authzMatch "
+ "SYNTAX 1.3.6.1.4.1.4203.666.2.7 "
+ "X-ORDERED 'VALUES' "
+ "USAGE distributedOperation )",
+ NULL, SLAP_AT_HIDE,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_saslAuthzFrom) },
+
+#ifdef LDAP_DYNAMIC_OBJECTS
+ { "entryTtl", "( 1.3.6.1.4.1.1466.101.119.3 NAME 'entryTtl' "
+ "DESC 'RFC2589: entry time-to-live' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE "
+ "NO-USER-MODIFICATION USAGE dSAOperation )",
+ dynamicAttribute, SLAP_AT_MANAGEABLE,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_entryTtl) },
+ { "dynamicSubtrees", "( 1.3.6.1.4.1.1466.101.119.4 "
+ "NAME 'dynamicSubtrees' "
+ "DESC 'RFC2589: dynamic subtrees' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ rootDseAttribute, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_dynamicSubtrees) },
+#endif
+
+ /* userApplication attributes (which system schema depends upon) */
+ { "distinguishedName", "( 2.5.4.49 NAME 'distinguishedName' "
+ "DESC 'RFC4519: common supertype of DN attributes' "
+ "EQUALITY distinguishedNameMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )",
+ NULL, SLAP_AT_ABSTRACT,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_distinguishedName) },
+ { "name", "( 2.5.4.41 NAME 'name' "
+ "DESC 'RFC4519: common supertype of name attributes' "
+ "EQUALITY caseIgnoreMatch "
+ "SUBSTR caseIgnoreSubstringsMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )",
+ NULL, SLAP_AT_ABSTRACT,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_name) },
+ { "cn", "( 2.5.4.3 NAME ( 'cn' 'commonName' ) "
+ "DESC 'RFC4519: common name(s) for which the entity is known by' "
+ "SUP name )",
+ NULL, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_cn) },
+ { "uid", "( 0.9.2342.19200300.100.1.1 NAME ( 'uid' 'userid' ) "
+ "DESC 'RFC4519: user identifier' "
+ "EQUALITY caseIgnoreMatch "
+ "SUBSTR caseIgnoreSubstringsMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )",
+ NULL, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_uid) },
+ { "uidNumber", /* for ldapi:// */
+ "( 1.3.6.1.1.1.1.0 NAME 'uidNumber' "
+ "DESC 'RFC2307: An integer uniquely identifying a user "
+ "in an administrative domain' "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )",
+ NULL, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_uidNumber) },
+ { "gidNumber", /* for ldapi:// */
+ "( 1.3.6.1.1.1.1.1 NAME 'gidNumber' "
+ "DESC 'RFC2307: An integer uniquely identifying a group "
+ "in an administrative domain' "
+ "EQUALITY integerMatch "
+ "ORDERING integerOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )",
+ NULL, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_gidNumber) },
+ { "userPassword", "( 2.5.4.35 NAME 'userPassword' "
+ "DESC 'RFC4519/2307: password of user' "
+ "EQUALITY octetStringMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{128} )",
+ NULL, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_userPassword) },
+
+ { "labeledURI", "( 1.3.6.1.4.1.250.1.57 NAME 'labeledURI' "
+ "DESC 'RFC2079: Uniform Resource Identifier with optional label' "
+ "EQUALITY caseExactMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
+ NULL, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_labeledURI) },
+
+#ifdef SLAPD_AUTHPASSWD
+ { "authPassword", "( 1.3.6.1.4.1.4203.1.3.4 "
+ "NAME 'authPassword' "
+ "DESC 'RFC3112: authentication password attribute' "
+ "EQUALITY 1.3.6.1.4.1.4203.1.2.2 "
+ "SYNTAX 1.3.6.1.4.1.4203.1.1.2 )",
+ NULL, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_authPassword) },
+ { "supportedAuthPasswordSchemes", "( 1.3.6.1.4.1.4203.1.3.3 "
+ "NAME 'supportedAuthPasswordSchemes' "
+ "DESC 'RFC3112: supported authPassword schemes' "
+ "EQUALITY caseExactIA5Match "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} "
+ "USAGE dSAOperation )",
+ subschemaAttribute, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_authPasswordSchemes) },
+#endif
+
+ { "description", "( 2.5.4.13 NAME 'description' "
+ "DESC 'RFC4519: descriptive information' "
+ "EQUALITY caseIgnoreMatch "
+ "SUBSTR caseIgnoreSubstringsMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )",
+ NULL, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_description) },
+
+ { "seeAlso", "( 2.5.4.34 NAME 'seeAlso' "
+ "DESC 'RFC4519: DN of related object' "
+ "SUP distinguishedName )",
+ NULL, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_seeAlso) },
+
+ { "pKCS8PrivateKey", "( 1.3.6.1.4.1.4203.666.1.60 "
+ "NAME 'pKCS8PrivateKey' "
+ "DESC 'PKCS#8 PrivateKeyInfo, use ;binary' "
+ "EQUALITY privateKeyMatch "
+ "SYNTAX 1.2.840.113549.1.8.1.1 )",
+ NULL, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_pKCS8PrivateKey) },
+
+ { "pwdLastSuccess", "( 1.3.6.1.4.1.42.2.27.8.1.29 NAME 'pwdLastSuccess' "
+ "DESC 'The timestamp of the last successful authentication' "
+ "EQUALITY generalizedTimeMatch "
+ "ORDERING generalizedTimeOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION "
+ "USAGE directoryOperation )",
+ NULL, 0,
+ NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ offsetof(struct slap_internal_schema, si_ad_pwdLastSuccess) },
+
+ { NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0 }
+};
+
+static AttributeType slap_at_undefined = {
+ { "1.1.1", NULL, "Catchall for undefined attribute types", 1, NULL,
+ NULL, NULL, NULL, NULL,
+ 0, 0, 0, 1, LDAP_SCHEMA_DSA_OPERATION, NULL }, /* LDAPAttributeType */
+ BER_BVC("UNDEFINED"), /* cname */
+ NULL, /* sup */
+ NULL, /* subtypes */
+ NULL, NULL, NULL, NULL, /* matching rules routines */
+ NULL, /* syntax (will be set later to "octetString") */
+ NULL, /* schema check function */
+ NULL, /* oidmacro */
+ NULL, /* soidmacro */
+ SLAP_AT_ABSTRACT|SLAP_AT_FINAL, /* mask */
+ { NULL }, /* next */
+ NULL /* attribute description */
+ /* mutex (don't know how to initialize it :) */
+};
+
+static AttributeType slap_at_proxied = {
+ { "1.1.1", NULL, "Catchall for undefined proxied attribute types", 1, NULL,
+ NULL, NULL, NULL, NULL,
+ 0, 0, 0, 0, LDAP_SCHEMA_USER_APPLICATIONS, NULL }, /* LDAPAttributeType */
+ BER_BVC("PROXIED"), /* cname */
+ NULL, /* sup */
+ NULL, /* subtypes */
+ NULL, NULL, NULL, NULL, /* matching rules routines (will be set later) */
+ NULL, /* syntax (will be set later to "octetString") */
+ NULL, /* schema check function */
+ NULL, /* oidmacro */
+ NULL, /* soidmacro */
+ SLAP_AT_ABSTRACT|SLAP_AT_FINAL, /* mask */
+ { NULL }, /* next */
+ NULL /* attribute description */
+ /* mutex (don't know how to initialize it :) */
+};
+
+static struct slap_schema_mr_map {
+ char *ssmm_name;
+ size_t ssmm_offset;
+} mr_map[] = {
+ { "caseExactIA5Match",
+ offsetof(struct slap_internal_schema, si_mr_caseExactIA5Match) },
+ { "caseExactMatch",
+ offsetof(struct slap_internal_schema, si_mr_caseExactMatch) },
+ { "caseExactSubstringsMatch",
+ offsetof(struct slap_internal_schema, si_mr_caseExactSubstringsMatch) },
+ { "distinguishedNameMatch",
+ offsetof(struct slap_internal_schema, si_mr_distinguishedNameMatch) },
+ { "dnSubtreeMatch",
+ offsetof(struct slap_internal_schema, si_mr_dnSubtreeMatch) },
+ { "dnOneLevelMatch",
+ offsetof(struct slap_internal_schema, si_mr_dnOneLevelMatch) },
+ { "dnSubordinateMatch",
+ offsetof(struct slap_internal_schema, si_mr_dnSubordinateMatch) },
+ { "dnSuperiorMatch",
+ offsetof(struct slap_internal_schema, si_mr_dnSuperiorMatch) },
+ { "integerMatch",
+ offsetof(struct slap_internal_schema, si_mr_integerMatch) },
+ { "integerFirstComponentMatch",
+ offsetof(struct slap_internal_schema,
+ si_mr_integerFirstComponentMatch) },
+ { "objectIdentifierFirstComponentMatch",
+ offsetof(struct slap_internal_schema,
+ si_mr_objectIdentifierFirstComponentMatch) },
+ { "caseIgnoreMatch",
+ offsetof(struct slap_internal_schema, si_mr_caseIgnoreMatch) },
+ { "caseIgnoreListMatch",
+ offsetof(struct slap_internal_schema, si_mr_caseIgnoreListMatch) },
+ { NULL, 0 }
+};
+
+static struct slap_schema_syn_map {
+ char *sssm_name;
+ size_t sssm_offset;
+} syn_map[] = {
+ { "1.3.6.1.4.1.1466.115.121.1.15",
+ offsetof(struct slap_internal_schema, si_syn_directoryString) },
+ { "1.3.6.1.4.1.1466.115.121.1.12",
+ offsetof(struct slap_internal_schema, si_syn_distinguishedName) },
+ { "1.3.6.1.4.1.1466.115.121.1.27",
+ offsetof(struct slap_internal_schema, si_syn_integer) },
+ { "1.3.6.1.4.1.1466.115.121.1.40",
+ offsetof(struct slap_internal_schema, si_syn_octetString) },
+ { "1.3.6.1.4.1.1466.115.121.1.3",
+ offsetof(struct slap_internal_schema, si_syn_attributeTypeDesc) },
+ { "1.3.6.1.4.1.1466.115.121.1.16",
+ offsetof(struct slap_internal_schema, si_syn_ditContentRuleDesc) },
+ { "1.3.6.1.4.1.1466.115.121.1.54",
+ offsetof(struct slap_internal_schema, si_syn_ldapSyntaxDesc) },
+ { "1.3.6.1.4.1.1466.115.121.1.30",
+ offsetof(struct slap_internal_schema, si_syn_matchingRuleDesc) },
+ { "1.3.6.1.4.1.1466.115.121.1.31",
+ offsetof(struct slap_internal_schema, si_syn_matchingRuleUseDesc) },
+ { "1.3.6.1.4.1.1466.115.121.1.35",
+ offsetof(struct slap_internal_schema, si_syn_nameFormDesc) },
+ { "1.3.6.1.4.1.1466.115.121.1.37",
+ offsetof(struct slap_internal_schema, si_syn_objectClassDesc) },
+ { "1.3.6.1.4.1.1466.115.121.1.17",
+ offsetof(struct slap_internal_schema, si_syn_ditStructureRuleDesc) },
+ { NULL, 0 }
+};
+
+int
+slap_schema_load( void )
+{
+ int i;
+
+ for( i=0; syn_map[i].sssm_name; i++ ) {
+ Syntax ** synp = (Syntax **)
+ &(((char *) &slap_schema)[syn_map[i].sssm_offset]);
+
+ assert( *synp == NULL );
+
+ *synp = syn_find( syn_map[i].sssm_name );
+
+ if( *synp == NULL ) {
+ fprintf( stderr, "slap_schema_load: Syntax: "
+ "No syntax \"%s\" defined in schema\n",
+ syn_map[i].sssm_name );
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ for( i=0; mr_map[i].ssmm_name; i++ ) {
+ MatchingRule ** mrp = (MatchingRule **)
+ &(((char *) &slap_schema)[mr_map[i].ssmm_offset]);
+
+ assert( *mrp == NULL );
+
+ *mrp = mr_find( mr_map[i].ssmm_name );
+
+ if( *mrp == NULL ) {
+ fprintf( stderr, "slap_schema_load: MatchingRule: "
+ "No matching rule \"%s\" defined in schema\n",
+ mr_map[i].ssmm_name );
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+ }
+
+ slap_at_undefined.sat_syntax = slap_schema.si_syn_octetString;
+ slap_schema.si_at_undefined = &slap_at_undefined;
+
+ slap_at_proxied.sat_equality = mr_find( "octetStringMatch" );
+ slap_at_proxied.sat_approx = mr_find( "octetStringMatch" );
+ slap_at_proxied.sat_ordering = mr_find( "octetStringOrderingMatch" );
+ slap_at_proxied.sat_substr = mr_find( "octetStringSubstringsMatch" );
+ slap_at_proxied.sat_syntax = slap_schema.si_syn_octetString;
+ slap_schema.si_at_proxied = &slap_at_proxied;
+
+ ldap_pvt_thread_mutex_init( &ad_index_mutex );
+ ldap_pvt_thread_mutex_init( &ad_undef_mutex );
+ ldap_pvt_thread_mutex_init( &oc_undef_mutex );
+
+ for( i=0; ad_map[i].ssam_name; i++ ) {
+ assert( ad_map[i].ssam_defn != NULL );
+ {
+ LDAPAttributeType *at;
+ int code;
+ const char *err;
+
+ at = ldap_str2attributetype( ad_map[i].ssam_defn,
+ &code, &err, LDAP_SCHEMA_ALLOW_ALL );
+ if ( !at ) {
+ fprintf( stderr,
+ "slap_schema_load: AttributeType \"%s\": %s before %s\n",
+ ad_map[i].ssam_name, ldap_scherr2str(code), err );
+ return code;
+ }
+
+ if ( at->at_oid == NULL ) {
+ fprintf( stderr, "slap_schema_load: "
+ "AttributeType \"%s\": no OID\n",
+ ad_map[i].ssam_name );
+ ldap_attributetype_free( at );
+ return LDAP_OTHER;
+ }
+
+ code = at_add( at, 0, NULL, NULL, &err );
+ if ( code ) {
+ ldap_attributetype_free( at );
+ fprintf( stderr, "slap_schema_load: AttributeType "
+ "\"%s\": %s: \"%s\"\n",
+ ad_map[i].ssam_name, scherr2str(code), err );
+ return code;
+ }
+ ldap_memfree( at );
+ }
+ {
+ int rc;
+ const char *text;
+ Syntax *syntax = NULL;
+
+ AttributeDescription ** adp = (AttributeDescription **)
+ &(((char *) &slap_schema)[ad_map[i].ssam_offset]);
+
+ assert( *adp == NULL );
+
+ rc = slap_str2ad( ad_map[i].ssam_name, adp, &text );
+ if( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "slap_schema_load: AttributeType \"%s\": "
+ "not defined in schema\n",
+ ad_map[i].ssam_name );
+ return rc;
+ }
+
+ if( ad_map[i].ssam_check ) {
+ /* install check routine */
+ (*adp)->ad_type->sat_check = ad_map[i].ssam_check;
+ }
+ /* install flags */
+ (*adp)->ad_type->sat_flags |= ad_map[i].ssam_flags;
+
+ /* install custom syntax routines */
+ if( ad_map[i].ssam_syn_validate ||
+ ad_map[i].ssam_syn_pretty )
+ {
+ Syntax *syn;
+
+ syntax = (*adp)->ad_type->sat_syntax;
+
+ syn = ch_malloc( sizeof( Syntax ) );
+ *syn = *syntax;
+
+ if( ad_map[i].ssam_syn_validate ) {
+ syn->ssyn_validate = ad_map[i].ssam_syn_validate;
+ }
+ if( ad_map[i].ssam_syn_pretty ) {
+ syn->ssyn_pretty = ad_map[i].ssam_syn_pretty;
+ }
+
+ (*adp)->ad_type->sat_syntax = syn;
+ }
+
+ /* install custom rule routines */
+ if( syntax != NULL ||
+ ad_map[i].ssam_mr_convert ||
+ ad_map[i].ssam_mr_normalize ||
+ ad_map[i].ssam_mr_match ||
+ ad_map[i].ssam_mr_indexer ||
+ ad_map[i].ssam_mr_filter )
+ {
+ MatchingRule *mr = ch_malloc( sizeof( MatchingRule ) );
+ *mr = *(*adp)->ad_type->sat_equality;
+
+ if ( syntax != NULL ) {
+ mr->smr_syntax = (*adp)->ad_type->sat_syntax;
+ }
+ if ( ad_map[i].ssam_mr_convert ) {
+ mr->smr_convert = ad_map[i].ssam_mr_convert;
+ }
+ if ( ad_map[i].ssam_mr_normalize ) {
+ mr->smr_normalize = ad_map[i].ssam_mr_normalize;
+ }
+ if ( ad_map[i].ssam_mr_match ) {
+ mr->smr_match = ad_map[i].ssam_mr_match;
+ }
+ if ( ad_map[i].ssam_mr_indexer ) {
+ mr->smr_indexer = ad_map[i].ssam_mr_indexer;
+ }
+ if ( ad_map[i].ssam_mr_filter ) {
+ mr->smr_filter = ad_map[i].ssam_mr_filter;
+ }
+
+ (*adp)->ad_type->sat_equality = mr;
+ }
+ }
+ }
+
+ for( i=0; oc_map[i].ssom_name; i++ ) {
+ assert( oc_map[i].ssom_defn != NULL );
+ {
+ LDAPObjectClass *oc;
+ int code;
+ const char *err;
+
+ oc = ldap_str2objectclass( oc_map[i].ssom_defn, &code, &err,
+ LDAP_SCHEMA_ALLOW_ALL );
+ if ( !oc ) {
+ fprintf( stderr, "slap_schema_load: ObjectClass "
+ "\"%s\": %s before %s\n",
+ oc_map[i].ssom_name, ldap_scherr2str(code), err );
+ return code;
+ }
+
+ if ( oc->oc_oid == NULL ) {
+ fprintf( stderr, "slap_schema_load: ObjectClass "
+ "\"%s\": no OID\n",
+ oc_map[i].ssom_name );
+ ldap_objectclass_free( oc );
+ return LDAP_OTHER;
+ }
+
+ code = oc_add(oc,0,NULL,NULL,&err);
+ if ( code ) {
+ ldap_objectclass_free( oc );
+ fprintf( stderr, "slap_schema_load: ObjectClass "
+ "\"%s\": %s: \"%s\"\n",
+ oc_map[i].ssom_name, scherr2str(code), err);
+ return code;
+ }
+ ldap_memfree(oc);
+
+ }
+ {
+ ObjectClass ** ocp = (ObjectClass **)
+ &(((char *) &slap_schema)[oc_map[i].ssom_offset]);
+
+ assert( *ocp == NULL );
+
+ *ocp = oc_find( oc_map[i].ssom_name );
+ if( *ocp == NULL ) {
+ fprintf( stderr, "slap_schema_load: "
+ "ObjectClass \"%s\": not defined in schema\n",
+ oc_map[i].ssom_name );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ if( oc_map[i].ssom_check ) {
+ /* install check routine */
+ (*ocp)->soc_check = oc_map[i].ssom_check;
+ }
+ /* install flags */
+ (*ocp)->soc_flags |= oc_map[i].ssom_flags;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int
+slap_schema_check( void )
+{
+ /* we should only be called once after schema_init() was called */
+ assert( schema_init_done == 1 );
+
+ /*
+ * cycle thru attributeTypes to build matchingRuleUse
+ */
+ if ( matching_rule_use_init() ) {
+ return LDAP_OTHER;
+ }
+
+ ++schema_init_done;
+ return LDAP_SUCCESS;
+}
+
+static int rootDseObjectClass (
+ Backend *be,
+ Entry *e,
+ ObjectClass *oc,
+ const char** text,
+ char *textbuf, size_t textlen )
+{
+ *text = textbuf;
+
+ if( e->e_nname.bv_len ) {
+ snprintf( textbuf, textlen,
+ "objectClass \"%s\" only allowed in the root DSE",
+ oc->soc_oid );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ /* we should not be called for the root DSE */
+ assert( 0 );
+ return LDAP_SUCCESS;
+}
+
+static int aliasObjectClass (
+ Backend *be,
+ Entry *e,
+ ObjectClass *oc,
+ const char** text,
+ char *textbuf, size_t textlen )
+{
+ *text = textbuf;
+
+ if( !SLAP_ALIASES(be) ) {
+ snprintf( textbuf, textlen,
+ "objectClass \"%s\" not supported in context",
+ oc->soc_oid );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int referralObjectClass (
+ Backend *be,
+ Entry *e,
+ ObjectClass *oc,
+ const char** text,
+ char *textbuf, size_t textlen )
+{
+ *text = textbuf;
+
+ if( !SLAP_REFERRALS(be) ) {
+ snprintf( textbuf, textlen,
+ "objectClass \"%s\" not supported in context",
+ oc->soc_oid );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int subentryObjectClass (
+ Backend *be,
+ Entry *e,
+ ObjectClass *oc,
+ const char** text,
+ char *textbuf, size_t textlen )
+{
+ *text = textbuf;
+
+ if( !SLAP_SUBENTRIES(be) ) {
+ snprintf( textbuf, textlen,
+ "objectClass \"%s\" not supported in context",
+ oc->soc_oid );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ if( oc != slap_schema.si_oc_subentry && !is_entry_subentry( e ) ) {
+ snprintf( textbuf, textlen,
+ "objectClass \"%s\" only allowed in subentries",
+ oc->soc_oid );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+#ifdef LDAP_DYNAMIC_OBJECTS
+static int dynamicObjectClass (
+ Backend *be,
+ Entry *e,
+ ObjectClass *oc,
+ const char** text,
+ char *textbuf, size_t textlen )
+{
+ *text = textbuf;
+
+ if( !SLAP_DYNAMIC(be) ) {
+ snprintf( textbuf, textlen,
+ "objectClass \"%s\" not supported in context",
+ oc->soc_oid );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ return LDAP_SUCCESS;
+}
+#endif /* LDAP_DYNAMIC_OBJECTS */
+
+static int rootDseAttribute (
+ Backend *be,
+ Entry *e,
+ Attribute *attr,
+ const char** text,
+ char *textbuf, size_t textlen )
+{
+ *text = textbuf;
+
+ if( e->e_nname.bv_len ) {
+ snprintf( textbuf, textlen,
+ "attribute \"%s\" only allowed in the root DSE",
+ attr->a_desc->ad_cname.bv_val );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ /* we should not be called for the root DSE */
+ assert( 0 );
+ return LDAP_SUCCESS;
+}
+
+static int aliasAttribute (
+ Backend *be,
+ Entry *e,
+ Attribute *attr,
+ const char** text,
+ char *textbuf, size_t textlen )
+{
+ *text = textbuf;
+
+ if( !SLAP_ALIASES(be) ) {
+ snprintf( textbuf, textlen,
+ "attribute \"%s\" not supported in context",
+ attr->a_desc->ad_cname.bv_val );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ if( !is_entry_alias( e ) ) {
+ snprintf( textbuf, textlen,
+ "attribute \"%s\" only allowed in the alias",
+ attr->a_desc->ad_cname.bv_val );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int referralAttribute (
+ Backend *be,
+ Entry *e,
+ Attribute *attr,
+ const char** text,
+ char *textbuf, size_t textlen )
+{
+ *text = textbuf;
+
+ if( !SLAP_REFERRALS(be) ) {
+ snprintf( textbuf, textlen,
+ "attribute \"%s\" not supported in context",
+ attr->a_desc->ad_cname.bv_val );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ if( !is_entry_referral( e ) ) {
+ snprintf( textbuf, textlen,
+ "attribute \"%s\" only allowed in the referral",
+ attr->a_desc->ad_cname.bv_val );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int subentryAttribute (
+ Backend *be,
+ Entry *e,
+ Attribute *attr,
+ const char** text,
+ char *textbuf, size_t textlen )
+{
+ *text = textbuf;
+
+ if( !SLAP_SUBENTRIES(be) ) {
+ snprintf( textbuf, textlen,
+ "attribute \"%s\" not supported in context",
+ attr->a_desc->ad_cname.bv_val );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ if( !is_entry_subentry( e ) ) {
+ snprintf( textbuf, textlen,
+ "attribute \"%s\" only allowed in the subentry",
+ attr->a_desc->ad_cname.bv_val );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int administrativeRoleAttribute (
+ Backend *be,
+ Entry *e,
+ Attribute *attr,
+ const char** text,
+ char *textbuf, size_t textlen )
+{
+ *text = textbuf;
+
+ if( !SLAP_SUBENTRIES(be) ) {
+ snprintf( textbuf, textlen,
+ "attribute \"%s\" not supported in context",
+ attr->a_desc->ad_cname.bv_val );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ snprintf( textbuf, textlen,
+ "attribute \"%s\" not supported!",
+ attr->a_desc->ad_cname.bv_val );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+}
+
+#ifdef LDAP_DYNAMIC_OBJECTS
+static int dynamicAttribute (
+ Backend *be,
+ Entry *e,
+ Attribute *attr,
+ const char** text,
+ char *textbuf, size_t textlen )
+{
+ *text = textbuf;
+
+ if( !SLAP_DYNAMIC(be) ) {
+ snprintf( textbuf, textlen,
+ "attribute \"%s\" not supported in context",
+ attr->a_desc->ad_cname.bv_val );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ if( !is_entry_dynamicObject( e ) ) {
+ snprintf( textbuf, textlen,
+ "attribute \"%s\" only allowed in dynamic object",
+ attr->a_desc->ad_cname.bv_val );
+ return LDAP_OBJECT_CLASS_VIOLATION;
+ }
+
+ return LDAP_SUCCESS;
+}
+#endif /* LDAP_DYNAMIC_OBJECTS */
diff --git a/servers/slapd/schemaparse.c b/servers/slapd/schemaparse.c
new file mode 100644
index 0000000..a13c89c
--- /dev/null
+++ b/servers/slapd/schemaparse.c
@@ -0,0 +1,400 @@
+/* schemaparse.c - routines to parse config file objectclass definitions */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "ldap_schema.h"
+#include "slap-config.h"
+
+static void oc_usage(void);
+static void at_usage(void);
+
+static char *const err2text[] = {
+ "Success",
+ "Out of memory",
+ "ObjectClass not found",
+ "user-defined ObjectClass includes operational attributes",
+ "user-defined ObjectClass has inappropriate SUPerior",
+ "Duplicate objectClass",
+ "Inconsistent duplicate objectClass",
+ "AttributeType not found",
+ "AttributeType inappropriate matching rule",
+ "AttributeType inappropriate USAGE",
+ "AttributeType inappropriate SUPerior",
+ "AttributeType SYNTAX or SUPerior required",
+ "Duplicate attributeType",
+ "Inconsistent duplicate attributeType",
+ "MatchingRule not found",
+ "MatchingRule incomplete",
+ "Duplicate matchingRule",
+ "Syntax not found",
+ "Duplicate ldapSyntax",
+ "Superior syntax not found",
+ "Substitute syntax not specified",
+ "Substitute syntax not found",
+ "OID or name required",
+ "Qualifier not supported",
+ "Invalid NAME",
+ "OID could not be expanded",
+ "Duplicate Content Rule",
+ "Content Rule not for STRUCTURAL object class",
+ "Content Rule AUX contains inappropriate object class",
+ "Content Rule attribute type list contains duplicate",
+ NULL
+};
+
+char *
+scherr2str(int code)
+{
+ if ( code < 0 || SLAP_SCHERR_LAST <= code ) {
+ return "Unknown error";
+ } else {
+ return err2text[code];
+ }
+}
+
+/* check schema descr validity */
+int slap_valid_descr( const char *descr )
+{
+ int i=0;
+
+ if( !DESC_LEADCHAR( descr[i] ) ) {
+ return 0;
+ }
+
+ while( descr[++i] ) {
+ if( !DESC_CHAR( descr[i] ) ) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+/* OID Macros */
+
+/* String compare with delimiter check. Return 0 if not
+ * matched, otherwise return length matched.
+ */
+int
+dscompare(const char *s1, const char *s2, char delim)
+{
+ const char *orig = s1;
+ while (*s1++ == *s2++)
+ if (!s1[-1]) break;
+ --s1;
+ --s2;
+ if (!*s1 && (!*s2 || *s2 == delim))
+ return s1 - orig;
+ return 0;
+}
+
+static void
+cr_usage( void )
+{
+ fprintf( stderr,
+ "DITContentRuleDescription = \"(\" whsp\n"
+ " numericoid whsp ; StructuralObjectClass identifier\n"
+ " [ \"NAME\" qdescrs ]\n"
+ " [ \"DESC\" qdstring ]\n"
+ " [ \"OBSOLETE\" whsp ]\n"
+ " [ \"AUX\" oids ] ; Auxiliary ObjectClasses\n"
+ " [ \"MUST\" oids ] ; AttributeTypes\n"
+ " [ \"MAY\" oids ] ; AttributeTypes\n"
+ " [ \"NOT\" oids ] ; AttributeTypes\n"
+ " whsp \")\"\n" );
+}
+
+int
+parse_cr(
+ struct config_args_s *c,
+ ContentRule **scr )
+{
+ LDAPContentRule *cr;
+ int code;
+ const char *err;
+ char *line = strchr( c->line, '(' );
+
+ cr = ldap_str2contentrule( line, &code, &err, LDAP_SCHEMA_ALLOW_ALL );
+ if ( !cr ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: %s before %s",
+ c->argv[0], ldap_scherr2str( code ), err );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s %s\n", c->log, c->cr_msg );
+ cr_usage();
+ return 1;
+ }
+
+ if ( cr->cr_oid == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: OID is missing",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s %s\n", c->log, c->cr_msg );
+ cr_usage();
+ code = 1;
+ goto done;
+ }
+
+ code = cr_add( cr, 1, scr, &err );
+ if ( code ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: %s: \"%s\"",
+ c->argv[0], scherr2str(code), err);
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s %s\n", c->log, c->cr_msg );
+ code = 1;
+ goto done;
+ }
+
+done:;
+ if ( code ) {
+ ldap_contentrule_free( cr );
+
+ } else {
+ ldap_memfree( cr );
+ }
+
+ return code;
+}
+
+int
+parse_oc(
+ struct config_args_s *c,
+ ObjectClass **soc,
+ ObjectClass *prev )
+{
+ LDAPObjectClass *oc;
+ int code;
+ const char *err;
+ char *line = strchr( c->line, '(' );
+
+ oc = ldap_str2objectclass(line, &code, &err, LDAP_SCHEMA_ALLOW_ALL );
+ if ( !oc ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: %s before %s",
+ c->argv[0], ldap_scherr2str( code ), err );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s %s\n", c->log, c->cr_msg );
+ oc_usage();
+ return 1;
+ }
+
+ if ( oc->oc_oid == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: OID is missing",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s %s\n", c->log, c->cr_msg );
+ oc_usage();
+ code = 1;
+ goto done;
+ }
+
+ code = oc_add( oc, 1, soc, prev, &err );
+ if ( code ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: %s: \"%s\"",
+ c->argv[0], scherr2str(code), err);
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s %s\n", c->log, c->cr_msg );
+ code = 1;
+ goto done;
+ }
+
+done:;
+ if ( code ) {
+ ldap_objectclass_free( oc );
+
+ } else {
+ ldap_memfree( oc );
+ }
+
+ return code;
+}
+
+static void
+oc_usage( void )
+{
+ fprintf( stderr,
+ "ObjectClassDescription = \"(\" whsp\n"
+ " numericoid whsp ; ObjectClass identifier\n"
+ " [ \"NAME\" qdescrs ]\n"
+ " [ \"DESC\" qdstring ]\n"
+ " [ \"OBSOLETE\" whsp ]\n"
+ " [ \"SUP\" oids ] ; Superior ObjectClasses\n"
+ " [ ( \"ABSTRACT\" / \"STRUCTURAL\" / \"AUXILIARY\" ) whsp ]\n"
+ " ; default structural\n"
+ " [ \"MUST\" oids ] ; AttributeTypes\n"
+ " [ \"MAY\" oids ] ; AttributeTypes\n"
+ " whsp \")\"\n" );
+}
+
+static void
+at_usage( void )
+{
+ fprintf( stderr, "%s%s%s",
+ "AttributeTypeDescription = \"(\" whsp\n"
+ " numericoid whsp ; AttributeType identifier\n"
+ " [ \"NAME\" qdescrs ] ; name used in AttributeType\n"
+ " [ \"DESC\" qdstring ] ; description\n"
+ " [ \"OBSOLETE\" whsp ]\n"
+ " [ \"SUP\" woid ] ; derived from this other\n"
+ " ; AttributeType\n",
+ " [ \"EQUALITY\" woid ] ; Matching Rule name\n"
+ " [ \"ORDERING\" woid ] ; Matching Rule name\n"
+ " [ \"SUBSTR\" woid ] ; Matching Rule name\n"
+ " [ \"SYNTAX\" whsp noidlen whsp ] ; see section 4.3\n"
+ " [ \"SINGLE-VALUE\" whsp ] ; default multi-valued\n"
+ " [ \"COLLECTIVE\" whsp ] ; default not collective\n",
+ " [ \"NO-USER-MODIFICATION\" whsp ]; default user modifiable\n"
+ " [ \"USAGE\" whsp AttributeUsage ]; default userApplications\n"
+ " ; userApplications\n"
+ " ; directoryOperation\n"
+ " ; distributedOperation\n"
+ " ; dSAOperation\n"
+ " whsp \")\"\n");
+}
+
+int
+parse_at(
+ struct config_args_s *c,
+ AttributeType **sat,
+ AttributeType *prev )
+{
+ LDAPAttributeType *at;
+ int code;
+ const char *err;
+ char *line = strchr( c->line, '(' );
+
+ at = ldap_str2attributetype( line, &code, &err, LDAP_SCHEMA_ALLOW_ALL );
+ if ( !at ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: %s before %s",
+ c->argv[0], ldap_scherr2str(code), err );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s %s\n", c->log, c->cr_msg );
+ at_usage();
+ return 1;
+ }
+
+ if ( at->at_oid == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: OID is missing",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s %s\n", c->log, c->cr_msg );
+ at_usage();
+ code = 1;
+ goto done;
+ }
+
+ /* operational attributes should be defined internally */
+ if ( at->at_usage ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: \"%s\" is operational",
+ c->argv[0], at->at_oid );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s %s\n", c->log, c->cr_msg );
+ code = 1;
+ goto done;
+ }
+
+ code = at_add( at, 1, sat, prev, &err);
+ if ( code ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: %s: \"%s\"",
+ c->argv[0], scherr2str(code), err);
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s %s\n", c->log, c->cr_msg );
+ code = 1;
+ goto done;
+ }
+
+done:;
+ if ( code ) {
+ ldap_attributetype_free( at );
+
+ } else {
+ ldap_memfree( at );
+ }
+
+ return code;
+}
+
+static void
+syn_usage( void )
+{
+ fprintf( stderr, "%s",
+ "SyntaxDescription = \"(\" whsp\n"
+ " numericoid whsp ; object identifier\n"
+ " [ whsp \"DESC\" whsp qdstring ] ; description\n"
+ " extensions whsp \")\" ; extensions\n"
+ " whsp \")\"\n");
+}
+
+int
+parse_syn(
+ struct config_args_s *c,
+ Syntax **ssyn,
+ Syntax *prev )
+{
+ LDAPSyntax *syn;
+ slap_syntax_defs_rec def = { 0 };
+ int code;
+ const char *err;
+ char *line = strchr( c->line, '(' );
+
+ syn = ldap_str2syntax( line, &code, &err, LDAP_SCHEMA_ALLOW_ALL );
+ if ( !syn ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: %s before %s",
+ c->argv[0], ldap_scherr2str(code), err );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s %s\n", c->log, c->cr_msg );
+ syn_usage();
+ return 1;
+ }
+
+ if ( syn->syn_oid == NULL ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: OID is missing",
+ c->argv[0] );
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s %s\n", c->log, c->cr_msg );
+ syn_usage();
+ code = 1;
+ goto done;
+ }
+
+ code = syn_add( syn, 1, &def, ssyn, prev, &err );
+ if ( code ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: %s: \"%s\"",
+ c->argv[0], scherr2str(code), err);
+ Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
+ "%s %s\n", c->log, c->cr_msg );
+ code = 1;
+ goto done;
+ }
+
+done:;
+ if ( code ) {
+ ldap_syntax_free( syn );
+
+ } else {
+ ldap_memfree( syn );
+ }
+
+ return code;
+}
+
diff --git a/servers/slapd/search.c b/servers/slapd/search.c
new file mode 100644
index 0000000..7846f0f
--- /dev/null
+++ b/servers/slapd/search.c
@@ -0,0 +1,439 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "lutil.h"
+#include "slap.h"
+
+int
+do_search(
+ Operation *op, /* info about the op to which we're responding */
+ SlapReply *rs /* all the response data we'll send */ )
+{
+ struct berval base = BER_BVNULL;
+ ber_len_t siz, off, i;
+
+ Debug( LDAP_DEBUG_TRACE, "%s do_search\n",
+ op->o_log_prefix );
+ /*
+ * Parse the search request. It looks like this:
+ *
+ * SearchRequest := [APPLICATION 3] SEQUENCE {
+ * baseObject DistinguishedName,
+ * scope ENUMERATED {
+ * baseObject (0),
+ * singleLevel (1),
+ * wholeSubtree (2),
+ * subordinate (3) -- OpenLDAP extension
+ * },
+ * derefAliases ENUMERATED {
+ * neverDerefaliases (0),
+ * derefInSearching (1),
+ * derefFindingBaseObj (2),
+ * alwaysDerefAliases (3)
+ * },
+ * sizelimit INTEGER (0 .. 65535),
+ * timelimit INTEGER (0 .. 65535),
+ * attrsOnly BOOLEAN,
+ * filter Filter,
+ * attributes SEQUENCE OF AttributeType
+ * }
+ */
+
+ /* baseObject, scope, derefAliases, sizelimit, timelimit, attrsOnly */
+ if ( ber_scanf( op->o_ber, "{miiiib" /*}*/,
+ &base, &op->ors_scope, &op->ors_deref, &op->ors_slimit,
+ &op->ors_tlimit, &op->ors_attrsonly ) == LBER_ERROR )
+ {
+ send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
+ rs->sr_err = SLAPD_DISCONNECT;
+ goto return_results;
+ }
+
+ if ( op->ors_tlimit < 0 || op->ors_tlimit > SLAP_MAX_LIMIT ) {
+ send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR, "invalid time limit" );
+ goto return_results;
+ }
+
+ if ( op->ors_slimit < 0 || op->ors_slimit > SLAP_MAX_LIMIT ) {
+ send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR, "invalid size limit" );
+ goto return_results;
+ }
+
+ switch( op->ors_scope ) {
+ case LDAP_SCOPE_BASE:
+ case LDAP_SCOPE_ONELEVEL:
+ case LDAP_SCOPE_SUBTREE:
+ case LDAP_SCOPE_SUBORDINATE:
+ break;
+ default:
+ send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR, "invalid scope" );
+ goto return_results;
+ }
+
+ switch( op->ors_deref ) {
+ case LDAP_DEREF_NEVER:
+ case LDAP_DEREF_FINDING:
+ case LDAP_DEREF_SEARCHING:
+ case LDAP_DEREF_ALWAYS:
+ break;
+ default:
+ send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR, "invalid deref" );
+ goto return_results;
+ }
+
+ rs->sr_err = dnPrettyNormal( NULL, &base, &op->o_req_dn, &op->o_req_ndn, op->o_tmpmemctx );
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_search: invalid dn: \"%s\"\n",
+ op->o_log_prefix, base.bv_val );
+ send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid DN" );
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_ARGS, "SRCH \"%s\" %d %d %d %d %d\n",
+ base.bv_val, op->ors_scope, op->ors_deref,
+ op->ors_slimit, op->ors_tlimit, op->ors_attrsonly);
+
+ /* filter - returns a "normalized" version */
+ rs->sr_err = get_filter( op, op->o_ber, &op->ors_filter, &rs->sr_text );
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ if( rs->sr_err == SLAPD_DISCONNECT ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ send_ldap_disconnect( op, rs );
+ rs->sr_err = SLAPD_DISCONNECT;
+ } else {
+ send_ldap_result( op, rs );
+ }
+ goto return_results;
+ }
+ filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
+
+ Debug( LDAP_DEBUG_ARGS, " filter: %s\n",
+ !BER_BVISEMPTY( &op->ors_filterstr ) ? op->ors_filterstr.bv_val : "empty" );
+
+ /* attributes */
+ siz = sizeof(AttributeName);
+ off = offsetof(AttributeName,an_name);
+ if ( ber_scanf( op->o_ber, "{M}}", &op->ors_attrs, &siz, off ) == LBER_ERROR ) {
+ send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding attrs error" );
+ rs->sr_err = SLAPD_DISCONNECT;
+ goto return_results;
+ }
+ for ( i=0; i<siz; i++ ) {
+ const char *dummy; /* ignore msgs from bv2ad */
+ op->ors_attrs[i].an_desc = NULL;
+ op->ors_attrs[i].an_oc = NULL;
+ op->ors_attrs[i].an_flags = 0;
+ if ( slap_bv2ad( &op->ors_attrs[i].an_name,
+ &op->ors_attrs[i].an_desc, &dummy ) != LDAP_SUCCESS )
+ {
+ if ( slap_bv2undef_ad( &op->ors_attrs[i].an_name,
+ &op->ors_attrs[i].an_desc, &dummy,
+ SLAP_AD_PROXIED|SLAP_AD_NOINSERT ) )
+ {
+ struct berval *bv = &op->ors_attrs[i].an_name;
+
+ /* RFC 4511 LDAPv3: All User Attributes */
+ if ( bvmatch( bv, slap_bv_all_user_attrs ) ) {
+ continue;
+ }
+
+ /* RFC 3673 LDAPv3: All Operational Attributes */
+ if ( bvmatch( bv, slap_bv_all_operational_attrs ) ) {
+ continue;
+ }
+
+ /* RFC 4529 LDAP: Requesting Attributes by Object Class */
+ if ( bv->bv_len > 1 && bv->bv_val[0] == '@' ) {
+ /* FIXME: check if remaining is valid oc name? */
+ continue;
+ }
+
+ /* add more "exceptions" to RFC 4511 4.5.1.8. */
+
+ /* invalid attribute description? remove */
+ if ( ad_keystring( bv ) ) {
+ /* NOTE: parsed in-place, don't modify;
+ * rather add "1.1", which must be ignored */
+ BER_BVSTR( &op->ors_attrs[i].an_name, LDAP_NO_ATTRS );
+ }
+
+ /* otherwise leave in place... */
+ }
+ }
+ }
+
+ if( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "%s do_search: get_ctrls failed\n",
+ op->o_log_prefix );
+ goto return_results;
+ }
+
+ if (LogTest( LDAP_DEBUG_ARGS ) ) {
+ char abuf[BUFSIZ/2], *ptr = abuf;
+ unsigned len = 0, alen;
+
+ if ( !siz ) {
+ len = 1;
+ abuf[0] = '\0';
+ }
+ for ( i = 0; i<siz; i++ ) {
+ alen = op->ors_attrs[i].an_name.bv_len;
+ if (alen >= sizeof(abuf)) {
+ alen = sizeof(abuf)-1;
+ }
+ if (len && (len + 1 + alen >= sizeof(abuf))) {
+ Debug( LDAP_DEBUG_ARGS, " attrs: %s\n", abuf );
+ len = 0;
+ ptr = abuf;
+ }
+ if (len) {
+ *ptr++ = ' ';
+ len++;
+ }
+ ptr = lutil_strncopy(ptr, op->ors_attrs[i].an_name.bv_val, alen);
+ len += alen;
+ *ptr = '\0';
+ }
+ if (len) {
+ Debug( LDAP_DEBUG_ARGS, " attrs: %s\n", abuf );
+ }
+ }
+
+ if (LogTest( LDAP_DEBUG_STATS ) ) {
+ char abuf[BUFSIZ/2], *ptr = abuf;
+ unsigned len = 0, alen;
+
+ sprintf(abuf, "scope=%d deref=%d", op->ors_scope, op->ors_deref);
+ Debug( LDAP_DEBUG_STATS,
+ "%s SRCH base=\"%s\" %s filter=\"%s\"\n",
+ op->o_log_prefix, op->o_req_dn.bv_val, abuf,
+ op->ors_filterstr.bv_val );
+
+ for ( i = 0; i<siz; i++ ) {
+ alen = op->ors_attrs[i].an_name.bv_len;
+ if (alen >= sizeof(abuf)) {
+ alen = sizeof(abuf)-1;
+ }
+ if (len && (len + 1 + alen >= sizeof(abuf))) {
+ Debug( LDAP_DEBUG_STATS, "%s SRCH attr=%s\n",
+ op->o_log_prefix, abuf );
+ len = 0;
+ ptr = abuf;
+ }
+ if (len) {
+ *ptr++ = ' ';
+ len++;
+ }
+ ptr = lutil_strncopy(ptr, op->ors_attrs[i].an_name.bv_val, alen);
+ len += alen;
+ *ptr = '\0';
+ }
+ if (len) {
+ Debug( LDAP_DEBUG_STATS, "%s SRCH attr=%s\n",
+ op->o_log_prefix, abuf );
+ }
+ }
+
+ op->o_bd = frontendDB;
+ rs->sr_err = frontendDB->be_search( op, rs );
+ if ( rs->sr_err == SLAPD_ASYNCOP ) {
+ /* skip cleanup */
+ return rs->sr_err;
+ }
+
+return_results:;
+ if ( !BER_BVISNULL( &op->o_req_dn ) ) {
+ slap_sl_free( op->o_req_dn.bv_val, op->o_tmpmemctx );
+ }
+ if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
+ slap_sl_free( op->o_req_ndn.bv_val, op->o_tmpmemctx );
+ }
+ if ( !BER_BVISNULL( &op->ors_filterstr ) ) {
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ }
+ if ( op->ors_filter != NULL) {
+ filter_free_x( op, op->ors_filter, 1 );
+ }
+ if ( op->ors_attrs != NULL ) {
+ op->o_tmpfree( op->ors_attrs, op->o_tmpmemctx );
+ }
+
+ return rs->sr_err;
+}
+
+int
+fe_op_search( Operation *op, SlapReply *rs )
+{
+ BackendDB *bd = op->o_bd;
+
+ if ( op->ors_scope == LDAP_SCOPE_BASE ) {
+ Entry *entry = NULL;
+
+ if ( BER_BVISEMPTY( &op->o_req_ndn ) ) {
+#ifdef LDAP_CONNECTIONLESS
+ /* Ignore LDAPv2 CLDAP Root DSE queries */
+ if (op->o_protocol == LDAP_VERSION2 && op->o_conn->c_is_udp) {
+ goto return_results;
+ }
+#endif
+ /* check restrictions */
+ if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto return_results;
+ }
+
+ rs->sr_err = root_dse_info( op->o_conn, &entry, &rs->sr_text );
+
+ } else if ( bvmatch( &op->o_req_ndn, &frontendDB->be_schemandn ) ) {
+ /* check restrictions */
+ if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto return_results;
+ }
+
+ rs->sr_err = schema_info( &entry, &rs->sr_text );
+ }
+
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto return_results;
+
+ } else if ( entry != NULL ) {
+ if ( get_assert( op ) &&
+ ( test_filter( op, entry, get_assertion( op )) != LDAP_COMPARE_TRUE )) {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto fail1;
+ }
+
+ rs->sr_err = test_filter( op, entry, op->ors_filter );
+
+ if( rs->sr_err == LDAP_COMPARE_TRUE ) {
+ /* note: we set no limits because either
+ * no limit is specified, or at least 1
+ * is specified, and we're going to return
+ * at most one entry */
+ op->ors_slimit = SLAP_NO_LIMIT;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+
+ rs->sr_entry = entry;
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_operational_attrs = NULL;
+ rs->sr_flags = 0;
+ send_search_entry( op, rs );
+ rs->sr_entry = NULL;
+ rs->sr_operational_attrs = NULL;
+ }
+ rs->sr_err = LDAP_SUCCESS;
+fail1:
+ entry_free( entry );
+ send_ldap_result( op, rs );
+ goto return_results;
+ }
+ }
+
+ if( BER_BVISEMPTY( &op->o_req_ndn ) && !BER_BVISEMPTY( &default_search_nbase ) ) {
+ slap_sl_free( op->o_req_dn.bv_val, op->o_tmpmemctx );
+ slap_sl_free( op->o_req_ndn.bv_val, op->o_tmpmemctx );
+
+ ber_dupbv_x( &op->o_req_dn, &default_search_base, op->o_tmpmemctx );
+ ber_dupbv_x( &op->o_req_ndn, &default_search_nbase, op->o_tmpmemctx );
+ }
+
+ /*
+ * We could be serving multiple database backends. Select the
+ * appropriate one, or send a referral to our "referral server"
+ * if we don't hold it.
+ */
+
+ op->o_bd = select_backend( &op->o_req_ndn, 1 );
+ if ( op->o_bd == NULL ) {
+ rs->sr_ref = referral_rewrite( default_referral,
+ NULL, &op->o_req_dn, op->ors_scope );
+
+ if (!rs->sr_ref) rs->sr_ref = default_referral;
+ rs->sr_err = LDAP_REFERRAL;
+ op->o_bd = bd;
+ send_ldap_result( op, rs );
+
+ if (rs->sr_ref != default_referral)
+ ber_bvarray_free( rs->sr_ref );
+ rs->sr_ref = NULL;
+ goto return_results;
+ }
+
+ /* check restrictions */
+ if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
+ send_ldap_result( op, rs );
+ goto return_results;
+ }
+
+ /* check for referrals */
+ if( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) {
+ goto return_results;
+ }
+
+ if ( SLAP_SHADOW(op->o_bd) && get_dontUseCopy(op) ) {
+ /* don't use shadow copy */
+ BerVarray defref = op->o_bd->be_update_refs
+ ? op->o_bd->be_update_refs : default_referral;
+
+ if( defref != NULL ) {
+ rs->sr_ref = referral_rewrite( defref,
+ NULL, &op->o_req_dn, op->ors_scope );
+ if( !rs->sr_ref) rs->sr_ref = defref;
+ rs->sr_err = LDAP_REFERRAL;
+ send_ldap_result( op, rs );
+
+ if (rs->sr_ref != defref) ber_bvarray_free( rs->sr_ref );
+
+ } else {
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "copy not used; no referral information available" );
+ }
+
+ } else if ( op->o_bd->be_search ) {
+ if ( limits_check( op, rs ) == 0 ) {
+ /* actually do the search and send the result(s) */
+ (op->o_bd->be_search)( op, rs );
+ }
+ /* else limits_check() sends error */
+
+ } else {
+ send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+ "operation not supported within namingContext" );
+ }
+
+return_results:;
+ op->o_bd = bd;
+ return rs->sr_err;
+}
+
diff --git a/servers/slapd/sets.c b/servers/slapd/sets.c
new file mode 100644
index 0000000..9ab2b36
--- /dev/null
+++ b/servers/slapd/sets.c
@@ -0,0 +1,832 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2000-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "sets.h"
+
+static BerVarray set_chase( SLAP_SET_GATHER gatherer,
+ SetCookie *cookie, BerVarray set, AttributeDescription *desc, int closure );
+
+/* Count the array members */
+static long
+slap_set_size( BerVarray set )
+{
+ long i = 0;
+
+ if ( set != NULL ) {
+ while ( !BER_BVISNULL( &set[ i ] ) ) {
+ i++;
+ }
+ }
+
+ return i;
+}
+
+/* Return 0 if there is at least one array member, non-zero otherwise */
+static int
+slap_set_isempty( BerVarray set )
+{
+ if ( set == NULL ) {
+ return 1;
+ }
+
+ if ( !BER_BVISNULL( &set[ 0 ] ) ) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Dispose of the contents of the array and the array itself according
+ * to the flags value. If SLAP_SET_REFVAL, don't dispose of values;
+ * if SLAP_SET_REFARR, don't dispose of the array itself. In case of
+ * binary operators, there are LEFT flags and RIGHT flags, referring to
+ * the first and the second operator arguments, respectively. In this
+ * case, flags must be transformed using macros SLAP_SET_LREF2REF() and
+ * SLAP_SET_RREF2REF() before calling this function.
+ */
+static void
+slap_set_dispose( SetCookie *cp, BerVarray set, unsigned flags )
+{
+ if ( flags & SLAP_SET_REFVAL ) {
+ if ( ! ( flags & SLAP_SET_REFARR ) ) {
+ cp->set_op->o_tmpfree( set, cp->set_op->o_tmpmemctx );
+ }
+
+ } else {
+ ber_bvarray_free_x( set, cp->set_op->o_tmpmemctx );
+ }
+}
+
+/* Duplicate a set. If SLAP_SET_REFARR, is not set, the original array
+ * with the original values is returned, otherwise the array is duplicated;
+ * if SLAP_SET_REFVAL is set, also the values are duplicated.
+ */
+static BerVarray
+set_dup( SetCookie *cp, BerVarray set, unsigned flags )
+{
+ BerVarray newset = NULL;
+
+ if ( set == NULL ) {
+ return NULL;
+ }
+
+ if ( flags & SLAP_SET_REFARR ) {
+ int i;
+
+ for ( i = 0; !BER_BVISNULL( &set[ i ] ); i++ )
+ ;
+ newset = cp->set_op->o_tmpcalloc( i + 1,
+ sizeof( struct berval ),
+ cp->set_op->o_tmpmemctx );
+ if ( newset == NULL ) {
+ return NULL;
+ }
+
+ if ( flags & SLAP_SET_REFVAL ) {
+ for ( i = 0; !BER_BVISNULL( &set[ i ] ); i++ ) {
+ ber_dupbv_x( &newset[ i ], &set[ i ],
+ cp->set_op->o_tmpmemctx );
+ }
+
+ } else {
+ AC_MEMCPY( newset, set, ( i + 1 ) * sizeof( struct berval ) );
+ }
+
+ } else {
+ newset = set;
+ }
+
+ return newset;
+}
+
+/* Join two sets according to operator op and flags op_flags.
+ * op can be:
+ * '|' (or): the union between the two sets is returned,
+ * eliminating duplicates
+ * '&' (and): the intersection between the two sets
+ * is returned
+ * '+' (add): the inner product of the two sets is returned,
+ * namely a set containing the concatenation of
+ * all combinations of the two sets members,
+ * except for duplicates.
+ * The two sets are disposed of according to the flags as described
+ * for slap_set_dispose().
+ */
+BerVarray
+slap_set_join(
+ SetCookie *cp,
+ BerVarray lset,
+ unsigned op_flags,
+ BerVarray rset )
+{
+ BerVarray set;
+ long i, j, last, rlast;
+ unsigned op = ( op_flags & SLAP_SET_OPMASK );
+
+ set = NULL;
+ switch ( op ) {
+ case '|': /* union */
+ if ( lset == NULL || BER_BVISNULL( &lset[ 0 ] ) ) {
+ if ( rset == NULL ) {
+ if ( lset == NULL ) {
+ set = cp->set_op->o_tmpcalloc( 1,
+ sizeof( struct berval ),
+ cp->set_op->o_tmpmemctx );
+ BER_BVZERO( &set[ 0 ] );
+ goto done2;
+ }
+ set = set_dup( cp, lset, SLAP_SET_LREF2REF( op_flags ) );
+ goto done2;
+ }
+ slap_set_dispose( cp, lset, SLAP_SET_LREF2REF( op_flags ) );
+ set = set_dup( cp, rset, SLAP_SET_RREF2REF( op_flags ) );
+ goto done2;
+ }
+ if ( rset == NULL || BER_BVISNULL( &rset[ 0 ] ) ) {
+ slap_set_dispose( cp, rset, SLAP_SET_RREF2REF( op_flags ) );
+ set = set_dup( cp, lset, SLAP_SET_LREF2REF( op_flags ) );
+ goto done2;
+ }
+
+ /* worst scenario: no duplicates */
+ rlast = slap_set_size( rset );
+ i = slap_set_size( lset ) + rlast + 1;
+ set = cp->set_op->o_tmpcalloc( i, sizeof( struct berval ), cp->set_op->o_tmpmemctx );
+ if ( set != NULL ) {
+ /* set_chase() depends on this routine to
+ * keep the first elements of the result
+ * set the same (and in the same order)
+ * as the left-set.
+ */
+ for ( i = 0; !BER_BVISNULL( &lset[ i ] ); i++ ) {
+ if ( op_flags & SLAP_SET_LREFVAL ) {
+ ber_dupbv_x( &set[ i ], &lset[ i ], cp->set_op->o_tmpmemctx );
+
+ } else {
+ set[ i ] = lset[ i ];
+ }
+ }
+
+ /* pointers to values have been used in set - don't free twice */
+ op_flags |= SLAP_SET_LREFVAL;
+
+ last = i;
+
+ for ( i = 0; !BER_BVISNULL( &rset[ i ] ); i++ ) {
+ int exists = 0;
+
+ for ( j = 0; !BER_BVISNULL( &set[ j ] ); j++ ) {
+ if ( bvmatch( &rset[ i ], &set[ j ] ) )
+ {
+ if ( !( op_flags & SLAP_SET_RREFVAL ) ) {
+ cp->set_op->o_tmpfree( rset[ i ].bv_val, cp->set_op->o_tmpmemctx );
+ rset[ i ] = rset[ --rlast ];
+ BER_BVZERO( &rset[ rlast ] );
+ i--;
+ }
+ exists = 1;
+ break;
+ }
+ }
+
+ if ( !exists ) {
+ if ( op_flags & SLAP_SET_RREFVAL ) {
+ ber_dupbv_x( &set[ last ], &rset[ i ], cp->set_op->o_tmpmemctx );
+
+ } else {
+ set[ last ] = rset[ i ];
+ }
+ last++;
+ }
+ }
+
+ /* pointers to values have been used in set - don't free twice */
+ op_flags |= SLAP_SET_RREFVAL;
+
+ BER_BVZERO( &set[ last ] );
+ }
+ break;
+
+ case '&': /* intersection */
+ if ( lset == NULL || BER_BVISNULL( &lset[ 0 ] )
+ || rset == NULL || BER_BVISNULL( &rset[ 0 ] ) )
+ {
+ set = cp->set_op->o_tmpcalloc( 1, sizeof( struct berval ),
+ cp->set_op->o_tmpmemctx );
+ BER_BVZERO( &set[ 0 ] );
+ break;
+
+ } else {
+ long llen, rlen;
+ BerVarray sset;
+
+ llen = slap_set_size( lset );
+ rlen = slap_set_size( rset );
+
+ /* dup the shortest */
+ if ( llen < rlen ) {
+ last = llen;
+ set = set_dup( cp, lset, SLAP_SET_LREF2REF( op_flags ) );
+ lset = NULL;
+ sset = rset;
+
+ } else {
+ last = rlen;
+ set = set_dup( cp, rset, SLAP_SET_RREF2REF( op_flags ) );
+ rset = NULL;
+ sset = lset;
+ }
+
+ if ( set == NULL ) {
+ break;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &set[ i ] ); i++ ) {
+ for ( j = 0; !BER_BVISNULL( &sset[ j ] ); j++ ) {
+ if ( bvmatch( &set[ i ], &sset[ j ] ) ) {
+ break;
+ }
+ }
+
+ if ( BER_BVISNULL( &sset[ j ] ) ) {
+ cp->set_op->o_tmpfree( set[ i ].bv_val, cp->set_op->o_tmpmemctx );
+ set[ i ] = set[ --last ];
+ BER_BVZERO( &set[ last ] );
+ i--;
+ }
+ }
+ }
+ break;
+
+ case '+': /* string concatenation */
+ i = slap_set_size( rset );
+ j = slap_set_size( lset );
+
+ /* handle empty set cases */
+ if ( i == 0 || j == 0 ) {
+ set = cp->set_op->o_tmpcalloc( 1, sizeof( struct berval ),
+ cp->set_op->o_tmpmemctx );
+ if ( set == NULL ) {
+ break;
+ }
+ BER_BVZERO( &set[ 0 ] );
+ break;
+ }
+
+ set = cp->set_op->o_tmpcalloc( i * j + 1, sizeof( struct berval ),
+ cp->set_op->o_tmpmemctx );
+ if ( set == NULL ) {
+ break;
+ }
+
+ for ( last = 0, i = 0; !BER_BVISNULL( &lset[ i ] ); i++ ) {
+ for ( j = 0; !BER_BVISNULL( &rset[ j ] ); j++ ) {
+ struct berval bv;
+ long k;
+
+ /* don't concatenate with the empty string */
+ if ( BER_BVISEMPTY( &lset[ i ] ) ) {
+ ber_dupbv_x( &bv, &rset[ j ], cp->set_op->o_tmpmemctx );
+ if ( bv.bv_val == NULL ) {
+ ber_bvarray_free_x( set, cp->set_op->o_tmpmemctx );
+ set = NULL;
+ goto done;
+ }
+
+ } else if ( BER_BVISEMPTY( &rset[ j ] ) ) {
+ ber_dupbv_x( &bv, &lset[ i ], cp->set_op->o_tmpmemctx );
+ if ( bv.bv_val == NULL ) {
+ ber_bvarray_free_x( set, cp->set_op->o_tmpmemctx );
+ set = NULL;
+ goto done;
+ }
+
+ } else {
+ bv.bv_len = lset[ i ].bv_len + rset[ j ].bv_len;
+ bv.bv_val = cp->set_op->o_tmpalloc( bv.bv_len + 1,
+ cp->set_op->o_tmpmemctx );
+ if ( bv.bv_val == NULL ) {
+ ber_bvarray_free_x( set, cp->set_op->o_tmpmemctx );
+ set = NULL;
+ goto done;
+ }
+ AC_MEMCPY( bv.bv_val, lset[ i ].bv_val, lset[ i ].bv_len );
+ AC_MEMCPY( &bv.bv_val[ lset[ i ].bv_len ], rset[ j ].bv_val, rset[ j ].bv_len );
+ bv.bv_val[ bv.bv_len ] = '\0';
+ }
+
+ for ( k = 0; k < last; k++ ) {
+ if ( bvmatch( &set[ k ], &bv ) ) {
+ cp->set_op->o_tmpfree( bv.bv_val, cp->set_op->o_tmpmemctx );
+ break;
+ }
+ }
+
+ if ( k == last ) {
+ set[ last++ ] = bv;
+ }
+ }
+ }
+ BER_BVZERO( &set[ last ] );
+ break;
+
+ default:
+ break;
+ }
+
+done:;
+ if ( lset ) slap_set_dispose( cp, lset, SLAP_SET_LREF2REF( op_flags ) );
+ if ( rset ) slap_set_dispose( cp, rset, SLAP_SET_RREF2REF( op_flags ) );
+
+done2:;
+ if ( LogTest( LDAP_DEBUG_ACL ) ) {
+ if ( !set || BER_BVISNULL( set ) ) {
+ Debug( LDAP_DEBUG_ACL, " ACL set: empty\n" );
+
+ } else {
+ for ( i = 0; !BER_BVISNULL( &set[ i ] ); i++ ) {
+ Debug( LDAP_DEBUG_ACL, " ACL set[%ld]=%s\n", i, set[i].bv_val );
+ }
+ }
+ }
+
+ return set;
+}
+
+static BerVarray
+set_chase( SLAP_SET_GATHER gatherer,
+ SetCookie *cp, BerVarray set, AttributeDescription *desc, int closure )
+{
+ BerVarray vals, nset;
+ int i;
+
+ if ( set == NULL ) {
+ set = cp->set_op->o_tmpcalloc( 1, sizeof( struct berval ),
+ cp->set_op->o_tmpmemctx );
+ if ( set != NULL ) {
+ BER_BVZERO( &set[ 0 ] );
+ }
+ return set;
+ }
+
+ if ( BER_BVISNULL( set ) ) {
+ return set;
+ }
+
+ nset = cp->set_op->o_tmpcalloc( 1, sizeof( struct berval ), cp->set_op->o_tmpmemctx );
+ if ( nset == NULL ) {
+ ber_bvarray_free_x( set, cp->set_op->o_tmpmemctx );
+ return NULL;
+ }
+ for ( i = 0; !BER_BVISNULL( &set[ i ] ); i++ ) {
+ vals = gatherer( cp, &set[ i ], desc );
+ if ( vals != NULL ) {
+ nset = slap_set_join( cp, nset, '|', vals );
+ }
+ }
+ ber_bvarray_free_x( set, cp->set_op->o_tmpmemctx );
+
+ if ( closure ) {
+ for ( i = 0; !BER_BVISNULL( &nset[ i ] ); i++ ) {
+ vals = gatherer( cp, &nset[ i ], desc );
+ if ( vals != NULL ) {
+ nset = slap_set_join( cp, nset, '|', vals );
+ if ( nset == NULL ) {
+ break;
+ }
+ }
+ }
+ }
+
+ return nset;
+}
+
+
+static BerVarray
+set_parents( SetCookie *cp, BerVarray set )
+{
+ int i, j, last;
+ struct berval bv, pbv;
+ BerVarray nset, vals;
+
+ if ( set == NULL ) {
+ set = cp->set_op->o_tmpcalloc( 1, sizeof( struct berval ),
+ cp->set_op->o_tmpmemctx );
+ if ( set != NULL ) {
+ BER_BVZERO( &set[ 0 ] );
+ }
+ return set;
+ }
+
+ if ( BER_BVISNULL( &set[ 0 ] ) ) {
+ return set;
+ }
+
+ nset = cp->set_op->o_tmpcalloc( 1, sizeof( struct berval ), cp->set_op->o_tmpmemctx );
+ if ( nset == NULL ) {
+ ber_bvarray_free_x( set, cp->set_op->o_tmpmemctx );
+ return NULL;
+ }
+
+ BER_BVZERO( &nset[ 0 ] );
+
+ for ( i = 0; !BER_BVISNULL( &set[ i ] ); i++ ) {
+ int level = 1;
+
+ pbv = bv = set[ i ];
+ for ( ; !BER_BVISEMPTY( &pbv ); dnParent( &bv, &pbv ) ) {
+ level++;
+ bv = pbv;
+ }
+
+ vals = cp->set_op->o_tmpcalloc( level + 1, sizeof( struct berval ), cp->set_op->o_tmpmemctx );
+ if ( vals == NULL ) {
+ ber_bvarray_free_x( set, cp->set_op->o_tmpmemctx );
+ ber_bvarray_free_x( nset, cp->set_op->o_tmpmemctx );
+ return NULL;
+ }
+ BER_BVZERO( &vals[ 0 ] );
+ last = 0;
+
+ bv = set[ i ];
+ for ( j = 0 ; j < level ; j++ ) {
+ ber_dupbv_x( &vals[ last ], &bv, cp->set_op->o_tmpmemctx );
+ last++;
+ dnParent( &bv, &bv );
+ }
+ BER_BVZERO( &vals[ last ] );
+
+ nset = slap_set_join( cp, nset, '|', vals );
+ }
+
+ ber_bvarray_free_x( set, cp->set_op->o_tmpmemctx );
+
+ return nset;
+}
+
+
+
+static BerVarray
+set_parent( SetCookie *cp, BerVarray set, int level )
+{
+ int i, j, last;
+ struct berval bv;
+ BerVarray nset;
+
+ if ( set == NULL ) {
+ set = cp->set_op->o_tmpcalloc( 1, sizeof( struct berval ),
+ cp->set_op->o_tmpmemctx );
+ if ( set != NULL ) {
+ BER_BVZERO( &set[ 0 ] );
+ }
+ return set;
+ }
+
+ if ( BER_BVISNULL( &set[ 0 ] ) ) {
+ return set;
+ }
+
+ nset = cp->set_op->o_tmpcalloc( slap_set_size( set ) + 1, sizeof( struct berval ), cp->set_op->o_tmpmemctx );
+ if ( nset == NULL ) {
+ ber_bvarray_free_x( set, cp->set_op->o_tmpmemctx );
+ return NULL;
+ }
+
+ BER_BVZERO( &nset[ 0 ] );
+ last = 0;
+
+ for ( i = 0; !BER_BVISNULL( &set[ i ] ); i++ ) {
+ bv = set[ i ];
+
+ for ( j = 0 ; j < level ; j++ ) {
+ dnParent( &bv, &bv );
+ }
+
+ for ( j = 0; !BER_BVISNULL( &nset[ j ] ); j++ ) {
+ if ( bvmatch( &bv, &nset[ j ] ) )
+ {
+ break;
+ }
+ }
+
+ if ( BER_BVISNULL( &nset[ j ] ) ) {
+ ber_dupbv_x( &nset[ last ], &bv, cp->set_op->o_tmpmemctx );
+ last++;
+ }
+ }
+
+ BER_BVZERO( &nset[ last ] );
+
+ ber_bvarray_free_x( set, cp->set_op->o_tmpmemctx );
+
+ return nset;
+}
+
+int
+slap_set_filter( SLAP_SET_GATHER gatherer,
+ SetCookie *cp, struct berval *fbv,
+ struct berval *user, struct berval *target, BerVarray *results )
+{
+#define STACK_SIZE 64
+#define IS_SET(x) ( (unsigned long)(x) >= 256 )
+#define IS_OP(x) ( (unsigned long)(x) < 256 )
+#define SF_ERROR(x) do { rc = -1; goto _error; } while ( 0 )
+#define SF_TOP() ( (BerVarray)( ( stp < 0 ) ? 0 : stack[ stp ] ) )
+#define SF_POP() ( (BerVarray)( ( stp < 0 ) ? 0 : stack[ stp-- ] ) )
+#define SF_PUSH(x) do { \
+ if ( stp >= ( STACK_SIZE - 1 ) ) SF_ERROR( overflow ); \
+ stack[ ++stp ] = (BerVarray)(long)(x); \
+ } while ( 0 )
+
+ BerVarray set, lset;
+ BerVarray stack[ STACK_SIZE ] = { 0 };
+ int len, rc, stp;
+ unsigned long op;
+ char c, *filter = fbv->bv_val;
+
+ if ( results ) {
+ *results = NULL;
+ }
+
+ stp = -1;
+ while ( ( c = *filter++ ) ) {
+ set = NULL;
+ switch ( c ) {
+ case ' ':
+ case '\t':
+ case '\x0A':
+ case '\x0D':
+ break;
+
+ case '(' /* ) */ :
+ if ( IS_SET( SF_TOP() ) ) {
+ SF_ERROR( syntax );
+ }
+ SF_PUSH( c );
+ break;
+
+ case /* ( */ ')':
+ set = SF_POP();
+ if ( IS_OP( set ) ) {
+ SF_ERROR( syntax );
+ }
+ if ( SF_TOP() == (void *)'(' /* ) */ ) {
+ SF_POP();
+ SF_PUSH( set );
+ set = NULL;
+
+ } else if ( IS_OP( SF_TOP() ) ) {
+ op = (unsigned long)SF_POP();
+ lset = SF_POP();
+ SF_POP();
+ set = slap_set_join( cp, lset, op, set );
+ if ( set == NULL ) {
+ SF_ERROR( memory );
+ }
+ SF_PUSH( set );
+ set = NULL;
+
+ } else {
+ SF_ERROR( syntax );
+ }
+ break;
+
+ case '|': /* union */
+ case '&': /* intersection */
+ case '+': /* string concatenation */
+ set = SF_POP();
+ if ( IS_OP( set ) ) {
+ SF_ERROR( syntax );
+ }
+ if ( SF_TOP() == 0 || SF_TOP() == (void *)'(' /* ) */ ) {
+ SF_PUSH( set );
+ set = NULL;
+
+ } else if ( IS_OP( SF_TOP() ) ) {
+ op = (unsigned long)SF_POP();
+ lset = SF_POP();
+ set = slap_set_join( cp, lset, op, set );
+ if ( set == NULL ) {
+ SF_ERROR( memory );
+ }
+ SF_PUSH( set );
+ set = NULL;
+
+ } else {
+ SF_ERROR( syntax );
+ }
+ SF_PUSH( c );
+ break;
+
+ case '[' /* ] */:
+ if ( ( SF_TOP() == (void *)'/' ) || IS_SET( SF_TOP() ) ) {
+ SF_ERROR( syntax );
+ }
+ for ( len = 0; ( c = *filter++ ) && ( c != /* [ */ ']' ); len++ )
+ ;
+ if ( c == 0 ) {
+ SF_ERROR( syntax );
+ }
+
+ set = cp->set_op->o_tmpcalloc( 2, sizeof( struct berval ),
+ cp->set_op->o_tmpmemctx );
+ if ( set == NULL ) {
+ SF_ERROR( memory );
+ }
+ set->bv_val = cp->set_op->o_tmpcalloc( len + 1, sizeof( char ),
+ cp->set_op->o_tmpmemctx );
+ if ( BER_BVISNULL( set ) ) {
+ SF_ERROR( memory );
+ }
+ AC_MEMCPY( set->bv_val, &filter[ - len - 1 ], len );
+ set->bv_len = len;
+ SF_PUSH( set );
+ set = NULL;
+ break;
+
+ case '-':
+ if ( ( SF_TOP() == (void *)'/' )
+ && ( *filter == '*' || ASCII_DIGIT( *filter ) ) )
+ {
+ SF_POP();
+
+ if ( *filter == '*' ) {
+ set = set_parents( cp, SF_POP() );
+ filter++;
+
+ } else {
+ char *next = NULL;
+ long parent = strtol( filter, &next, 10 );
+
+ if ( next == filter ) {
+ SF_ERROR( syntax );
+ }
+
+ set = SF_POP();
+ if ( parent != 0 ) {
+ set = set_parent( cp, set, parent );
+ }
+ filter = next;
+ }
+
+ if ( set == NULL ) {
+ SF_ERROR( memory );
+ }
+
+ SF_PUSH( set );
+ set = NULL;
+ break;
+ } else {
+ c = *filter++;
+ if ( c != '>' ) {
+ SF_ERROR( syntax );
+ }
+ /* fall through to next case */
+ }
+
+ case '/':
+ if ( IS_OP( SF_TOP() ) ) {
+ SF_ERROR( syntax );
+ }
+ SF_PUSH( '/' );
+ break;
+
+ default:
+ if ( !AD_LEADCHAR( c ) ) {
+ SF_ERROR( syntax );
+ }
+ filter--;
+ for ( len = 1;
+ ( c = filter[ len ] ) && AD_CHAR( c );
+ len++ )
+ {
+ /* count */
+ if ( c == '-' && !AD_CHAR( filter[ len + 1 ] ) ) {
+ break;
+ }
+ }
+ if ( len == 4
+ && memcmp( "this", filter, len ) == 0 )
+ {
+ assert( !BER_BVISNULL( target ) );
+ if ( ( SF_TOP() == (void *)'/' ) || IS_SET( SF_TOP() ) ) {
+ SF_ERROR( syntax );
+ }
+ set = cp->set_op->o_tmpcalloc( 2, sizeof( struct berval ),
+ cp->set_op->o_tmpmemctx );
+ if ( set == NULL ) {
+ SF_ERROR( memory );
+ }
+ ber_dupbv_x( set, target, cp->set_op->o_tmpmemctx );
+ if ( BER_BVISNULL( set ) ) {
+ SF_ERROR( memory );
+ }
+ BER_BVZERO( &set[ 1 ] );
+
+ } else if ( len == 4
+ && memcmp( "user", filter, len ) == 0 )
+ {
+ if ( ( SF_TOP() == (void *)'/' ) || IS_SET( SF_TOP() ) ) {
+ SF_ERROR( syntax );
+ }
+ if ( BER_BVISNULL( user ) ) {
+ SF_ERROR( memory );
+ }
+ set = cp->set_op->o_tmpcalloc( 2, sizeof( struct berval ),
+ cp->set_op->o_tmpmemctx );
+ if ( set == NULL ) {
+ SF_ERROR( memory );
+ }
+ ber_dupbv_x( set, user, cp->set_op->o_tmpmemctx );
+ BER_BVZERO( &set[ 1 ] );
+
+ } else if ( SF_TOP() != (void *)'/' ) {
+ SF_ERROR( syntax );
+
+ } else {
+ struct berval fb2;
+ AttributeDescription *ad = NULL;
+ const char *text = NULL;
+
+ SF_POP();
+ fb2.bv_val = filter;
+ fb2.bv_len = len;
+
+ if ( slap_bv2ad( &fb2, &ad, &text ) != LDAP_SUCCESS ) {
+ SF_ERROR( syntax );
+ }
+
+ /* NOTE: ad must have distinguishedName syntax
+ * or expand in an LDAP URI if c == '*'
+ */
+
+ set = set_chase( gatherer,
+ cp, SF_POP(), ad, c == '*' );
+ if ( set == NULL ) {
+ SF_ERROR( memory );
+ }
+ if ( c == '*' ) {
+ len++;
+ }
+ }
+ filter += len;
+ SF_PUSH( set );
+ set = NULL;
+ break;
+ }
+ }
+
+ set = SF_POP();
+ if ( IS_OP( set ) ) {
+ SF_ERROR( syntax );
+ }
+ if ( SF_TOP() == 0 ) {
+ /* FIXME: ok ? */ ;
+
+ } else if ( IS_OP( SF_TOP() ) ) {
+ op = (unsigned long)SF_POP();
+ lset = SF_POP();
+ set = slap_set_join( cp, lset, op, set );
+ if ( set == NULL ) {
+ SF_ERROR( memory );
+ }
+
+ } else {
+ SF_ERROR( syntax );
+ }
+
+ rc = slap_set_isempty( set ) ? 0 : 1;
+ if ( results ) {
+ *results = set;
+ set = NULL;
+ }
+
+_error:
+ if ( IS_SET( set ) ) {
+ ber_bvarray_free_x( set, cp->set_op->o_tmpmemctx );
+ }
+ while ( ( set = SF_POP() ) ) {
+ if ( IS_SET( set ) ) {
+ ber_bvarray_free_x( set, cp->set_op->o_tmpmemctx );
+ }
+ }
+ return rc;
+}
diff --git a/servers/slapd/sets.h b/servers/slapd/sets.h
new file mode 100644
index 0000000..c2b7673
--- /dev/null
+++ b/servers/slapd/sets.h
@@ -0,0 +1,75 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef SLAP_SETS_H_
+#define SLAP_SETS_H_
+
+#include <ldap_cdefs.h>
+
+LDAP_BEGIN_DECL
+
+typedef struct slap_set_cookie {
+ Operation *set_op;
+} SetCookie;
+
+/* this routine needs to return the bervals instead of
+ * plain strings, since syntax is not known. It should
+ * also return the syntax or some "comparison cookie"
+ * that is used by set_filter.
+ */
+typedef BerVarray (SLAP_SET_GATHER)( SetCookie *cookie,
+ struct berval *name, AttributeDescription *ad);
+
+LDAP_SLAPD_F (int) slap_set_filter(
+ SLAP_SET_GATHER gatherer,
+ SetCookie *cookie, struct berval *filter,
+ struct berval *user, struct berval *target, BerVarray *results);
+
+LDAP_SLAPD_F (BerVarray) slap_set_join(SetCookie *cp,
+ BerVarray lset, unsigned op, BerVarray rset);
+
+#define SLAP_SET_OPMASK 0x00FF
+
+#define SLAP_SET_REFARR 0x0100
+#define SLAP_SET_REFVAL 0x0200
+#define SLAP_SET_REF (SLAP_SET_REFARR|SLAP_SET_REFVAL)
+
+/* The unsigned "op" can be ORed with the flags below;
+ * - if the rset's values must not be freed, or must be copied if kept,
+ * it is ORed with SLAP_SET_RREFVAL
+ * - if the rset array must not be freed, or must be copied if kept,
+ * it is ORed with SLAP_SET_RREFARR
+ * - the same applies to the lset with SLAP_SET_LREFVAL and SLAP_SET_LREFARR
+ * - it is assumed that SLAP_SET_REFVAL implies SLAP_SET_REFARR,
+ * i.e. the former is checked only if the latter is set.
+ */
+
+#define SLAP_SET_RREFARR SLAP_SET_REFARR
+#define SLAP_SET_RREFVAL SLAP_SET_REFVAL
+#define SLAP_SET_RREF SLAP_SET_REF
+#define SLAP_SET_RREFMASK 0x0F00
+
+#define SLAP_SET_RREF2REF(r) ((r) & SLAP_SET_RREFMASK)
+
+#define SLAP_SET_LREFARR 0x1000
+#define SLAP_SET_LREFVAL 0x2000
+#define SLAP_SET_LREF (SLAP_SET_LREFARR|SLAP_SET_LREFVAL)
+#define SLAP_SET_LREFMASK 0xF000
+
+#define SLAP_SET_LREF2REF(r) (((r) & SLAP_SET_LREFMASK) >> 4)
+
+LDAP_END_DECL
+
+#endif
diff --git a/servers/slapd/sl_malloc.c b/servers/slapd/sl_malloc.c
new file mode 100644
index 0000000..9765bc3
--- /dev/null
+++ b/servers/slapd/sl_malloc.c
@@ -0,0 +1,739 @@
+/* sl_malloc.c - malloc routines using a per-thread slab */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "slap.h"
+
+#ifdef USE_VALGRIND
+/* Get debugging help from Valgrind */
+#include <valgrind/memcheck.h>
+#define VGMEMP_MARK(m,s) VALGRIND_MAKE_MEM_NOACCESS(m,s)
+#define VGMEMP_CREATE(h,r,z) VALGRIND_CREATE_MEMPOOL(h,r,z)
+#define VGMEMP_TRIM(h,a,s) VALGRIND_MEMPOOL_TRIM(h,a,s)
+#define VGMEMP_ALLOC(h,a,s) VALGRIND_MEMPOOL_ALLOC(h,a,s)
+#define VGMEMP_CHANGE(h,a,b,s) VALGRIND_MEMPOOL_CHANGE(h,a,b,s)
+#else
+#define VGMEMP_MARK(m,s)
+#define VGMEMP_CREATE(h,r,z)
+#define VGMEMP_TRIM(h,a,s)
+#define VGMEMP_ALLOC(h,a,s)
+#define VGMEMP_CHANGE(h,a,b,s)
+#endif
+
+/*
+ * This allocator returns temporary memory from a slab in a given memory
+ * context, aligned on a 2-int boundary. It cannot be used for data
+ * which will outlive the task allocating it.
+ *
+ * A new memory context attaches to the creator's thread context, if any.
+ * Threads cannot use other threads' memory contexts; there are no locks.
+ *
+ * The caller of slap_sl_malloc, usually a thread pool task, must
+ * slap_sl_free the memory before finishing: New tasks reuse the context
+ * and normally reset it, reclaiming memory left over from last task.
+ *
+ * The allocator helps memory fragmentation, speed and memory leaks.
+ * It is not (yet) reliable as a garbage collector:
+ *
+ * It falls back to context NULL - plain ber_memalloc() - when the
+ * context's slab is full. A reset does not reclaim such memory.
+ * Conversely, free/realloc of data not from the given context assumes
+ * context NULL. The data must not belong to another memory context.
+ *
+ * Code which has lost track of the current memory context can try
+ * slap_sl_context() or ch_malloc.c:ch_free/ch_realloc().
+ *
+ * Allocations cannot yet return failure. Like ch_malloc, they succeed
+ * or abort slapd. This will change, do fix code which assumes success.
+ */
+
+/*
+ * The stack-based allocator stores (ber_len_t)sizeof(head+block) at
+ * allocated blocks' head - and in freed blocks also at the tail, marked
+ * by ORing *next* block's head with 1. Freed blocks are only reclaimed
+ * from the last block forward. This is fast, but when a block is never
+ * freed, older blocks will not be reclaimed until the slab is reset...
+ */
+
+#ifdef SLAP_NO_SL_MALLOC /* Useful with memory debuggers like Valgrind */
+enum { No_sl_malloc = 1 };
+#else
+enum { No_sl_malloc = 0 };
+#endif
+
+#define SLAP_SLAB_SOBLOCK 64
+
+struct slab_object {
+ void *so_ptr;
+ int so_blockhead;
+ LDAP_LIST_ENTRY(slab_object) so_link;
+};
+
+struct slab_heap {
+ void *sh_base;
+ void *sh_last;
+ void *sh_end;
+ int sh_stack;
+ int sh_maxorder;
+ unsigned char **sh_map;
+ LDAP_LIST_HEAD(sh_freelist, slab_object) *sh_free;
+ LDAP_LIST_HEAD(sh_so, slab_object) sh_sopool;
+};
+
+enum {
+ Align = sizeof(ber_len_t) > 2*sizeof(int)
+ ? sizeof(ber_len_t) : 2*sizeof(int),
+ Align_log2 = 1 + (Align>2) + (Align>4) + (Align>8) + (Align>16),
+ order_start = Align_log2 - 1,
+ pad = Align - 1
+};
+
+static struct slab_object * slap_replenish_sopool(struct slab_heap* sh);
+#ifdef SLAPD_UNUSED
+static void print_slheap(int level, void *ctx);
+#endif
+
+/* Keep memory context in a thread-local var */
+# define memctx_key ((void *) slap_sl_mem_init)
+# define SET_MEMCTX(thrctx, memctx, kfree) \
+ ldap_pvt_thread_pool_setkey(thrctx,memctx_key, memctx,kfree, NULL,NULL)
+# define GET_MEMCTX(thrctx, memctxp) \
+ ((void) (*(memctxp) = NULL), \
+ (void) ldap_pvt_thread_pool_getkey(thrctx,memctx_key, memctxp,NULL), \
+ *(memctxp))
+
+/* Destroy the context, or if key==NULL clean it up for reuse. */
+void
+slap_sl_mem_destroy(
+ void *key,
+ void *data
+)
+{
+ struct slab_heap *sh = data;
+ struct slab_object *so;
+ int i;
+
+ if (!sh)
+ return;
+
+ if (!sh->sh_stack) {
+ for (i = 0; i <= sh->sh_maxorder - order_start; i++) {
+ so = LDAP_LIST_FIRST(&sh->sh_free[i]);
+ while (so) {
+ struct slab_object *so_tmp = so;
+ so = LDAP_LIST_NEXT(so, so_link);
+ LDAP_LIST_INSERT_HEAD(&sh->sh_sopool, so_tmp, so_link);
+ }
+ ch_free(sh->sh_map[i]);
+ }
+ ch_free(sh->sh_free);
+ ch_free(sh->sh_map);
+
+ so = LDAP_LIST_FIRST(&sh->sh_sopool);
+ while (so) {
+ struct slab_object *so_tmp = so;
+ so = LDAP_LIST_NEXT(so, so_link);
+ if (!so_tmp->so_blockhead) {
+ LDAP_LIST_REMOVE(so_tmp, so_link);
+ }
+ }
+ so = LDAP_LIST_FIRST(&sh->sh_sopool);
+ while (so) {
+ struct slab_object *so_tmp = so;
+ so = LDAP_LIST_NEXT(so, so_link);
+ ch_free(so_tmp);
+ }
+ }
+
+ if (key != NULL) {
+ ber_memfree_x(sh->sh_base, NULL);
+ ber_memfree_x(sh, NULL);
+ }
+}
+
+BerMemoryFunctions slap_sl_mfuncs =
+ { slap_sl_malloc, slap_sl_calloc, slap_sl_realloc, slap_sl_free };
+
+void
+slap_sl_mem_init()
+{
+ assert( Align == 1 << Align_log2 );
+
+ ber_set_option( NULL, LBER_OPT_MEMORY_FNS, &slap_sl_mfuncs );
+}
+
+/* Create, reset or just return the memory context of the current thread. */
+void *
+slap_sl_mem_create(
+ ber_len_t size,
+ int stack,
+ void *thrctx,
+ int new
+)
+{
+ void *memctx;
+ struct slab_heap *sh;
+ ber_len_t size_shift;
+ struct slab_object *so;
+ char *base, *newptr;
+ enum { Base_offset = (unsigned) -sizeof(ber_len_t) % Align };
+
+ sh = GET_MEMCTX(thrctx, &memctx);
+ if ( sh && !new )
+ return sh;
+
+ /* Round up to doubleword boundary, then make room for initial
+ * padding, preserving expected available size for pool version */
+ size = ((size + Align-1) & -Align) + Base_offset;
+
+ if (!sh) {
+ sh = ch_malloc(sizeof(struct slab_heap));
+ base = ch_malloc(size);
+ SET_MEMCTX(thrctx, sh, slap_sl_mem_destroy);
+ VGMEMP_MARK(base, size);
+ VGMEMP_CREATE(sh, 0, 0);
+ } else {
+ slap_sl_mem_destroy(NULL, sh);
+ base = sh->sh_base;
+ if (size > (ber_len_t) ((char *) sh->sh_end - base)) {
+ newptr = ch_realloc(base, size);
+ if ( newptr == NULL ) return NULL;
+ VGMEMP_CHANGE(sh, base, newptr, size);
+ base = newptr;
+ }
+ VGMEMP_TRIM(sh, base, 0);
+ }
+ sh->sh_base = base;
+ sh->sh_end = base + size;
+
+ /* Align (base + head of first block) == first returned block */
+ base += Base_offset;
+ size -= Base_offset;
+
+ sh->sh_stack = stack;
+ if (stack) {
+ sh->sh_last = base;
+
+ } else {
+ int i, order = -1, order_end = -1;
+
+ size_shift = size - 1;
+ do {
+ order_end++;
+ } while (size_shift >>= 1);
+ order = order_end - order_start + 1;
+ sh->sh_maxorder = order_end;
+
+ sh->sh_free = (struct sh_freelist *)
+ ch_malloc(order * sizeof(struct sh_freelist));
+ for (i = 0; i < order; i++) {
+ LDAP_LIST_INIT(&sh->sh_free[i]);
+ }
+
+ LDAP_LIST_INIT(&sh->sh_sopool);
+
+ if (LDAP_LIST_EMPTY(&sh->sh_sopool)) {
+ slap_replenish_sopool(sh);
+ }
+ so = LDAP_LIST_FIRST(&sh->sh_sopool);
+ LDAP_LIST_REMOVE(so, so_link);
+ so->so_ptr = base;
+
+ LDAP_LIST_INSERT_HEAD(&sh->sh_free[order-1], so, so_link);
+
+ sh->sh_map = (unsigned char **)
+ ch_malloc(order * sizeof(unsigned char *));
+ for (i = 0; i < order; i++) {
+ int shiftamt = order_start + 1 + i;
+ int nummaps = size >> shiftamt;
+ assert(nummaps);
+ nummaps >>= 3;
+ if (!nummaps) nummaps = 1;
+ sh->sh_map[i] = (unsigned char *) ch_malloc(nummaps);
+ memset(sh->sh_map[i], 0, nummaps);
+ }
+ }
+
+ return sh;
+}
+
+/*
+ * Assign memory context to thread context. Use NULL to detach
+ * current memory context from thread. Future users must
+ * know the context, since ch_free/slap_sl_context() cannot find it.
+ */
+void
+slap_sl_mem_setctx(
+ void *thrctx,
+ void *memctx
+)
+{
+ SET_MEMCTX(thrctx, memctx, slap_sl_mem_destroy);
+}
+
+void *
+slap_sl_malloc(
+ ber_len_t size,
+ void *ctx
+)
+{
+ struct slab_heap *sh = ctx;
+ ber_len_t *ptr, *newptr;
+
+ /* ber_set_option calls us like this */
+ if (No_sl_malloc || !ctx) {
+ newptr = ber_memalloc_x( size, NULL );
+ if ( newptr ) return newptr;
+ Debug(LDAP_DEBUG_ANY, "slap_sl_malloc of %lu bytes failed\n",
+ (unsigned long) size );
+ assert( 0 );
+ exit( EXIT_FAILURE );
+ }
+
+ /* Add room for head, ensure room for tail when freed, and
+ * round up to doubleword boundary. */
+ size = (size + sizeof(ber_len_t) + Align-1 + !size) & -Align;
+
+ if (sh->sh_stack) {
+ if (size < (ber_len_t) ((char *) sh->sh_end - (char *) sh->sh_last)) {
+ newptr = sh->sh_last;
+ sh->sh_last = (char *) sh->sh_last + size;
+ VGMEMP_ALLOC(sh, newptr, size);
+ *newptr++ = size;
+ return( (void *)newptr );
+ }
+
+ size -= sizeof(ber_len_t);
+
+ } else {
+ struct slab_object *so_new, *so_left, *so_right;
+ ber_len_t size_shift;
+ unsigned long diff;
+ int i, j, order = -1;
+
+ size_shift = size - 1;
+ do {
+ order++;
+ } while (size_shift >>= 1);
+
+ size -= sizeof(ber_len_t);
+
+ for (i = order; i <= sh->sh_maxorder &&
+ LDAP_LIST_EMPTY(&sh->sh_free[i-order_start]); i++);
+
+ if (i == order) {
+ so_new = LDAP_LIST_FIRST(&sh->sh_free[i-order_start]);
+ LDAP_LIST_REMOVE(so_new, so_link);
+ ptr = so_new->so_ptr;
+ diff = (unsigned long)((char*)ptr -
+ (char*)sh->sh_base) >> (order + 1);
+ sh->sh_map[order-order_start][diff>>3] |= (1 << (diff & 0x7));
+ *ptr++ = size;
+ LDAP_LIST_INSERT_HEAD(&sh->sh_sopool, so_new, so_link);
+ return((void*)ptr);
+ } else if (i <= sh->sh_maxorder) {
+ for (j = i; j > order; j--) {
+ so_left = LDAP_LIST_FIRST(&sh->sh_free[j-order_start]);
+ LDAP_LIST_REMOVE(so_left, so_link);
+ if (LDAP_LIST_EMPTY(&sh->sh_sopool)) {
+ slap_replenish_sopool(sh);
+ }
+ so_right = LDAP_LIST_FIRST(&sh->sh_sopool);
+ LDAP_LIST_REMOVE(so_right, so_link);
+ so_right->so_ptr = (void *)((char *)so_left->so_ptr + (1 << j));
+ if (j == order + 1) {
+ ptr = so_left->so_ptr;
+ diff = (unsigned long)((char*)ptr -
+ (char*)sh->sh_base) >> (order+1);
+ sh->sh_map[order-order_start][diff>>3] |=
+ (1 << (diff & 0x7));
+ *ptr++ = size;
+ LDAP_LIST_INSERT_HEAD(
+ &sh->sh_free[j-1-order_start], so_right, so_link);
+ LDAP_LIST_INSERT_HEAD(&sh->sh_sopool, so_left, so_link);
+ return((void*)ptr);
+ } else {
+ LDAP_LIST_INSERT_HEAD(
+ &sh->sh_free[j-1-order_start], so_right, so_link);
+ LDAP_LIST_INSERT_HEAD(
+ &sh->sh_free[j-1-order_start], so_left, so_link);
+ }
+ }
+ }
+ /* FIXME: missing return; guessing we failed... */
+ }
+
+ Debug(LDAP_DEBUG_TRACE,
+ "sl_malloc %lu: ch_malloc\n",
+ (unsigned long) size );
+ return ch_malloc(size);
+}
+
+#define LIM_SQRT(t) /* some value < sqrt(max value of unsigned type t) */ \
+ ((0UL|(t)-1) >>31>>31 > 1 ? ((t)1 <<32) - 1 : \
+ (0UL|(t)-1) >>31 ? 65535U : (0UL|(t)-1) >>15 ? 255U : 15U)
+
+void *
+slap_sl_calloc( ber_len_t n, ber_len_t size, void *ctx )
+{
+ void *newptr;
+ ber_len_t total = n * size;
+
+ /* The sqrt test is a slight optimization: often avoids the division */
+ if ((n | size) <= LIM_SQRT(ber_len_t) || n == 0 || total/n == size) {
+ newptr = slap_sl_malloc( total, ctx );
+ memset( newptr, 0, n*size );
+ } else {
+ Debug(LDAP_DEBUG_ANY, "slap_sl_calloc(%lu,%lu) out of range\n",
+ (unsigned long) n, (unsigned long) size );
+ assert(0);
+ exit(EXIT_FAILURE);
+ }
+ return newptr;
+}
+
+void *
+slap_sl_realloc(void *ptr, ber_len_t size, void *ctx)
+{
+ struct slab_heap *sh = ctx;
+ ber_len_t oldsize, *p = (ber_len_t *) ptr, *nextp;
+ void *newptr;
+
+ if (ptr == NULL)
+ return slap_sl_malloc(size, ctx);
+
+ /* Not our memory? */
+ if (No_sl_malloc || !sh || ptr < sh->sh_base || ptr >= sh->sh_end) {
+ /* Like ch_realloc(), except not trying a new context */
+ newptr = ber_memrealloc_x(ptr, size, NULL);
+ if (newptr) {
+ return newptr;
+ }
+ Debug(LDAP_DEBUG_ANY, "slap_sl_realloc of %lu bytes failed\n",
+ (unsigned long) size );
+ assert(0);
+ exit( EXIT_FAILURE );
+ }
+
+ if (size == 0) {
+ slap_sl_free(ptr, ctx);
+ return NULL;
+ }
+
+ oldsize = p[-1];
+
+ if (sh->sh_stack) {
+ /* Add room for head, round up to doubleword boundary */
+ size = (size + sizeof(ber_len_t) + Align-1) & -Align;
+
+ p--;
+
+ /* Never shrink blocks */
+ if (size <= oldsize) {
+ return ptr;
+ }
+
+ oldsize &= -2;
+ nextp = (ber_len_t *) ((char *) p + oldsize);
+
+ /* If reallocing the last block, try to grow it */
+ if (nextp == sh->sh_last) {
+ if (size < (ber_len_t) ((char *) sh->sh_end - (char *) p)) {
+ sh->sh_last = (char *) p + size;
+ p[0] = (p[0] & 1) | size;
+ return ptr;
+ }
+
+ /* Nowhere to grow, need to alloc and copy */
+ } else {
+ /* Slight optimization of the final realloc variant */
+ newptr = slap_sl_malloc(size-sizeof(ber_len_t), ctx);
+ AC_MEMCPY(newptr, ptr, oldsize-sizeof(ber_len_t));
+ /* Not last block, can just mark old region as free */
+ nextp[-1] = oldsize;
+ nextp[0] |= 1;
+ return newptr;
+ }
+
+ size -= sizeof(ber_len_t);
+ oldsize -= sizeof(ber_len_t);
+
+ } else if (oldsize > size) {
+ oldsize = size;
+ }
+
+ newptr = slap_sl_malloc(size, ctx);
+ AC_MEMCPY(newptr, ptr, oldsize);
+ slap_sl_free(ptr, ctx);
+ return newptr;
+}
+
+void
+slap_sl_free(void *ptr, void *ctx)
+{
+ struct slab_heap *sh = ctx;
+ ber_len_t size;
+ ber_len_t *p = ptr, *nextp, *tmpp;
+
+ if (!ptr)
+ return;
+
+ if (No_sl_malloc || !sh || ptr < sh->sh_base || ptr >= sh->sh_end) {
+ ber_memfree_x(ptr, NULL);
+ return;
+ }
+
+ size = *(--p);
+
+ if (sh->sh_stack) {
+ size &= -2;
+ nextp = (ber_len_t *) ((char *) p + size);
+ if (sh->sh_last != nextp) {
+ /* Mark it free: tail = size, head of next block |= 1 */
+ nextp[-1] = size;
+ nextp[0] |= 1;
+ /* We can't tell Valgrind about it yet, because we
+ * still need read/write access to this block for
+ * when we eventually get to reclaim it.
+ */
+ } else {
+ /* Reclaim freed block(s) off tail */
+ while (*p & 1) {
+ p = (ber_len_t *) ((char *) p - p[-1]);
+ }
+ sh->sh_last = p;
+ VGMEMP_TRIM(sh, sh->sh_base,
+ (char *) sh->sh_last - (char *) sh->sh_base);
+ }
+
+ } else {
+ int size_shift, order_size;
+ struct slab_object *so;
+ unsigned long diff;
+ int i, inserted = 0, order = -1;
+
+ size_shift = size + sizeof(ber_len_t) - 1;
+ do {
+ order++;
+ } while (size_shift >>= 1);
+
+ for (i = order, tmpp = p; i <= sh->sh_maxorder; i++) {
+ order_size = 1 << (i+1);
+ diff = (unsigned long)((char*)tmpp - (char*)sh->sh_base) >> (i+1);
+ sh->sh_map[i-order_start][diff>>3] &= (~(1 << (diff & 0x7)));
+ if (diff == ((diff>>1)<<1)) {
+ if (!(sh->sh_map[i-order_start][(diff+1)>>3] &
+ (1<<((diff+1)&0x7)))) {
+ so = LDAP_LIST_FIRST(&sh->sh_free[i-order_start]);
+ while (so) {
+ if ((char*)so->so_ptr == (char*)tmpp) {
+ LDAP_LIST_REMOVE( so, so_link );
+ } else if ((char*)so->so_ptr ==
+ (char*)tmpp + order_size) {
+ LDAP_LIST_REMOVE(so, so_link);
+ break;
+ }
+ so = LDAP_LIST_NEXT(so, so_link);
+ }
+ if (so) {
+ if (i < sh->sh_maxorder) {
+ inserted = 1;
+ so->so_ptr = tmpp;
+ LDAP_LIST_INSERT_HEAD(&sh->sh_free[i-order_start+1],
+ so, so_link);
+ }
+ continue;
+ } else {
+ if (LDAP_LIST_EMPTY(&sh->sh_sopool)) {
+ slap_replenish_sopool(sh);
+ }
+ so = LDAP_LIST_FIRST(&sh->sh_sopool);
+ LDAP_LIST_REMOVE(so, so_link);
+ so->so_ptr = tmpp;
+ LDAP_LIST_INSERT_HEAD(&sh->sh_free[i-order_start],
+ so, so_link);
+ break;
+
+ Debug(LDAP_DEBUG_TRACE, "slap_sl_free: "
+ "free object not found while bit is clear.\n" );
+ assert(so != NULL);
+
+ }
+ } else {
+ if (!inserted) {
+ if (LDAP_LIST_EMPTY(&sh->sh_sopool)) {
+ slap_replenish_sopool(sh);
+ }
+ so = LDAP_LIST_FIRST(&sh->sh_sopool);
+ LDAP_LIST_REMOVE(so, so_link);
+ so->so_ptr = tmpp;
+ LDAP_LIST_INSERT_HEAD(&sh->sh_free[i-order_start],
+ so, so_link);
+ }
+ break;
+ }
+ } else {
+ if (!(sh->sh_map[i-order_start][(diff-1)>>3] &
+ (1<<((diff-1)&0x7)))) {
+ so = LDAP_LIST_FIRST(&sh->sh_free[i-order_start]);
+ while (so) {
+ if ((char*)so->so_ptr == (char*)tmpp) {
+ LDAP_LIST_REMOVE(so, so_link);
+ } else if ((char*)tmpp == (char *)so->so_ptr + order_size) {
+ LDAP_LIST_REMOVE(so, so_link);
+ tmpp = so->so_ptr;
+ break;
+ }
+ so = LDAP_LIST_NEXT(so, so_link);
+ }
+ if (so) {
+ if (i < sh->sh_maxorder) {
+ inserted = 1;
+ LDAP_LIST_INSERT_HEAD(&sh->sh_free[i-order_start+1], so, so_link);
+ continue;
+ }
+ } else {
+ if (LDAP_LIST_EMPTY(&sh->sh_sopool)) {
+ slap_replenish_sopool(sh);
+ }
+ so = LDAP_LIST_FIRST(&sh->sh_sopool);
+ LDAP_LIST_REMOVE(so, so_link);
+ so->so_ptr = tmpp;
+ LDAP_LIST_INSERT_HEAD(&sh->sh_free[i-order_start],
+ so, so_link);
+ break;
+
+ Debug(LDAP_DEBUG_TRACE, "slap_sl_free: "
+ "free object not found while bit is clear.\n" );
+ assert(so != NULL);
+
+ }
+ } else {
+ if ( !inserted ) {
+ if (LDAP_LIST_EMPTY(&sh->sh_sopool)) {
+ slap_replenish_sopool(sh);
+ }
+ so = LDAP_LIST_FIRST(&sh->sh_sopool);
+ LDAP_LIST_REMOVE(so, so_link);
+ so->so_ptr = tmpp;
+ LDAP_LIST_INSERT_HEAD(&sh->sh_free[i-order_start],
+ so, so_link);
+ }
+ break;
+ }
+ }
+ }
+ }
+}
+
+void
+slap_sl_release( void *ptr, void *ctx )
+{
+ struct slab_heap *sh = ctx;
+ if ( sh && ptr >= sh->sh_base && ptr <= sh->sh_end )
+ sh->sh_last = ptr;
+}
+
+void *
+slap_sl_mark( void *ctx )
+{
+ struct slab_heap *sh = ctx;
+ return sh->sh_last;
+}
+
+/*
+ * Return the memory context of the current thread if the given block of
+ * memory belongs to it, otherwise return NULL.
+ */
+void *
+slap_sl_context( void *ptr )
+{
+ void *memctx;
+ struct slab_heap *sh;
+
+ if ( slapMode & SLAP_TOOL_MODE ) return NULL;
+
+ sh = GET_MEMCTX(ldap_pvt_thread_pool_context(), &memctx);
+ if (sh && ptr >= sh->sh_base && ptr <= sh->sh_end) {
+ return sh;
+ }
+ return NULL;
+}
+
+static struct slab_object *
+slap_replenish_sopool(
+ struct slab_heap* sh
+)
+{
+ struct slab_object *so_block;
+ int i;
+
+ so_block = (struct slab_object *)ch_malloc(
+ SLAP_SLAB_SOBLOCK * sizeof(struct slab_object));
+
+ if ( so_block == NULL ) {
+ return NULL;
+ }
+
+ so_block[0].so_blockhead = 1;
+ LDAP_LIST_INSERT_HEAD(&sh->sh_sopool, &so_block[0], so_link);
+ for (i = 1; i < SLAP_SLAB_SOBLOCK; i++) {
+ so_block[i].so_blockhead = 0;
+ LDAP_LIST_INSERT_HEAD(&sh->sh_sopool, &so_block[i], so_link );
+ }
+
+ return so_block;
+}
+
+#ifdef SLAPD_UNUSED
+static void
+print_slheap(int level, void *ctx)
+{
+ struct slab_heap *sh = ctx;
+ struct slab_object *so;
+ int i, j, once = 0;
+
+ if (!ctx) {
+ Debug(level, "NULL memctx\n" );
+ return;
+ }
+
+ Debug(level, "sh->sh_maxorder=%d\n", sh->sh_maxorder );
+
+ for (i = order_start; i <= sh->sh_maxorder; i++) {
+ once = 0;
+ Debug(level, "order=%d\n", i );
+ for (j = 0; j < (1<<(sh->sh_maxorder-i))/8; j++) {
+ Debug(level, "%02x ", sh->sh_map[i-order_start][j] );
+ once = 1;
+ }
+ if (!once) {
+ Debug(level, "%02x ", sh->sh_map[i-order_start][0] );
+ }
+ Debug(level, "\n" );
+ Debug(level, "free list:\n" );
+ so = LDAP_LIST_FIRST(&sh->sh_free[i-order_start]);
+ while (so) {
+ Debug(level, "%p\n", so->so_ptr );
+ so = LDAP_LIST_NEXT(so, so_link);
+ }
+ }
+}
+#endif
diff --git a/servers/slapd/slap-config.h b/servers/slapd/slap-config.h
new file mode 100644
index 0000000..3252fd7
--- /dev/null
+++ b/servers/slapd/slap-config.h
@@ -0,0 +1,240 @@
+/* slap-config.h - configuration abstraction structure */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include<ac/string.h>
+
+LDAP_BEGIN_DECL
+
+typedef union config_values_u {
+ /* Drop-in to make existing "notify" initialisers quietly work */
+ void *dummy;
+ int v_int;
+ unsigned v_uint;
+ long v_long;
+ size_t v_ulong;
+ ber_len_t v_ber_t;
+ char *v_string;
+ struct berval v_bv;
+ struct {
+ struct berval vdn_dn;
+ struct berval vdn_ndn;
+ } v_dn;
+ AttributeDescription *v_ad;
+} ConfigValues;
+
+typedef struct ConfigTable {
+ const char *name;
+ const char *what;
+ int min_args;
+ int max_args;
+ int length;
+ unsigned int arg_type;
+ void *arg_item;
+ const char *attribute;
+ AttributeDescription *ad;
+ ConfigValues arg_default;
+} ConfigTable;
+
+/* search entries are returned according to this order */
+typedef enum {
+ Cft_Abstract = 0,
+ Cft_Global,
+ Cft_Module,
+ Cft_Schema,
+ Cft_Backend,
+ Cft_Database,
+ Cft_Overlay,
+ Cft_Misc /* backend/overlay defined */
+} ConfigType;
+
+#define ARGS_USERLAND 0x00000fff
+
+/* types are enumerated, not a bitmask */
+#define ARGS_TYPES 0x0000f000
+#define ARG_INT 0x00001000
+#define ARG_LONG 0x00002000
+#define ARG_BER_LEN_T 0x00003000
+#define ARG_ON_OFF 0x00004000
+#define ARG_STRING 0x00005000
+#define ARG_BERVAL 0x00006000
+#define ARG_DN 0x00007000
+#define ARG_UINT 0x00008000
+#define ARG_ATDESC 0x00009000
+#define ARG_ULONG 0x0000a000
+#define ARG_BINARY 0x0000b000
+
+#define ARGS_SYNTAX 0xffff0000
+#define ARG_IGNORED 0x00080000
+#define ARG_PRE_BI 0x00100000
+#define ARG_PRE_DB 0x00200000
+#define ARG_DB 0x00400000 /* Only applies to DB */
+#define ARG_MAY_DB 0x00800000 /* May apply to DB */
+#define ARG_PAREN 0x01000000
+#define ARG_NONZERO 0x02000000
+#define ARG_NO_INSERT 0x04000000 /* no arbitrary inserting */
+#define ARG_NO_DELETE 0x08000000 /* no runtime deletes */
+#define ARG_UNIQUE 0x10000000
+#define ARG_QUOTE 0x20000000 /* wrap with quotes before parsing */
+#define ARG_OFFSET 0x40000000
+#define ARG_MAGIC 0x80000000
+
+#define ARG_BAD_CONF 0xdead0000 /* overload return values */
+
+/* This is a config entry's e_private data */
+typedef struct CfEntryInfo {
+ struct CfEntryInfo *ce_parent;
+ struct CfEntryInfo *ce_sibs;
+ struct CfEntryInfo *ce_kids;
+ Entry *ce_entry;
+ ConfigType ce_type;
+ BackendInfo *ce_bi;
+ BackendDB *ce_be;
+ void *ce_private;
+} CfEntryInfo;
+
+struct config_args_s;
+
+/* Check if the child is allowed to be LDAPAdd'd to the parent */
+typedef int (ConfigLDAPadd)(
+ CfEntryInfo *parent, Entry *child, struct config_args_s *ca);
+
+/* Let the object create children out of slapd.conf */
+typedef int (ConfigCfAdd)(
+ Operation *op, SlapReply *rs, Entry *parent, struct config_args_s *ca );
+
+#ifdef SLAP_CONFIG_DELETE
+/* Called when deleting a Cft_Misc Child object from cn=config */
+typedef int (ConfigLDAPdel)(
+ CfEntryInfo *ce, Operation *op );
+#endif
+
+typedef struct ConfigOCs {
+ const char *co_def;
+ ConfigType co_type;
+ ConfigTable *co_table;
+ ConfigLDAPadd *co_ldadd;
+ ConfigCfAdd *co_cfadd;
+#ifdef SLAP_CONFIG_DELETE
+ ConfigLDAPdel *co_lddel;
+#endif
+ ObjectClass *co_oc;
+ struct berval *co_name;
+} ConfigOCs;
+
+typedef int (ConfigDriver)(struct config_args_s *c);
+
+struct config_reply_s {
+ int err;
+ char msg[SLAP_TEXT_BUFLEN];
+};
+
+typedef struct config_args_s {
+ int argc;
+ char **argv;
+ int argv_size;
+ char *line;
+ char *tline;
+ const char *fname;
+ int lineno;
+ int linelen;
+ char log[MAXPATHLEN + STRLENOF(": line ") + LDAP_PVT_INTTYPE_CHARS(unsigned long)];
+#define cr_msg reply.msg
+ ConfigReply reply;
+ int depth;
+ int valx; /* multi-valued value index */
+ /* parsed first val for simple cases */
+ ConfigValues values;
+ /* return values for emit mode */
+ BerVarray rvalue_vals;
+ BerVarray rvalue_nvals;
+#define SLAP_CONFIG_EMIT 0x2000 /* emit instead of set */
+#define SLAP_CONFIG_ADD 0x4000 /* config file add vs LDAP add */
+ int op;
+ int type; /* ConfigTable.arg_type & ARGS_USERLAND */
+ Operation *ca_op;
+ BackendDB *be;
+ BackendInfo *bi;
+ Entry *ca_entry; /* entry being modified */
+ void *ca_private; /* anything */
+#ifndef SLAP_CONFIG_CLEANUP_MAX
+#define SLAP_CONFIG_CLEANUP_MAX 16
+#endif
+ ConfigDriver *cleanups[SLAP_CONFIG_CLEANUP_MAX];
+ ConfigType table; /* which config table did we come from */
+ int num_cleanups;
+} ConfigArgs;
+
+/* If lineno is zero, we have an actual LDAP Add request from a client.
+ * Otherwise, we're reading a config file or a config dir.
+ */
+#define CONFIG_ONLINE_ADD(ca) (!((ca)->lineno))
+
+#define value_int values.v_int
+#define value_uint values.v_uint
+#define value_long values.v_long
+#define value_ulong values.v_ulong
+#define value_ber_t values.v_ber_t
+#define value_string values.v_string
+#define value_bv values.v_bv
+#define value_dn values.v_dn.vdn_dn
+#define value_ndn values.v_dn.vdn_ndn
+#define value_ad values.v_ad
+
+int config_fp_parse_line(ConfigArgs *c);
+
+int config_register_schema(ConfigTable *ct, ConfigOCs *co);
+int config_del_vals(ConfigTable *cf, ConfigArgs *c);
+int config_get_vals(ConfigTable *ct, ConfigArgs *c);
+int config_add_vals(ConfigTable *ct, ConfigArgs *c);
+
+int config_push_cleanup(ConfigArgs *c, ConfigDriver *cleanup);
+int config_run_cleanup(ConfigArgs *c);
+
+void init_config_argv( ConfigArgs *c );
+int init_config_attrs(ConfigTable *ct);
+int init_config_ocs( ConfigOCs *ocs );
+void config_parse_ldif( ConfigArgs *c );
+int config_parse_vals(ConfigTable *ct, ConfigArgs *c, int valx);
+int config_parse_add(ConfigTable *ct, ConfigArgs *c, int valx);
+int read_config_file(const char *fname, int depth, ConfigArgs *cf,
+ ConfigTable *cft );
+
+ConfigTable * config_find_keyword(ConfigTable *ct, ConfigArgs *c);
+Entry * config_build_entry( Operation *op, SlapReply *rs, CfEntryInfo *parent,
+ ConfigArgs *c, struct berval *rdn, ConfigOCs *main, ConfigOCs *extra );
+
+Listener *config_check_my_url(const char *url, LDAPURLDesc *lud);
+int config_shadow( ConfigArgs *c, slap_mask_t flag );
+#define config_slurp_shadow(c) config_shadow((c), SLAP_DBFLAG_SLURP_SHADOW)
+#define config_sync_shadow(c) config_shadow((c), SLAP_DBFLAG_SYNC_SHADOW)
+
+ /* Make sure we don't exceed the bits reserved for userland */
+#define config_check_userland(last) \
+ assert( ( ( (last) - 1 ) & ARGS_USERLAND ) == ( (last) - 1 ) );
+
+#define SLAP_X_ORDERED_FMT "{%d}"
+
+LDAP_SLAPD_V (slap_verbmasks *) slap_ldap_response_code;
+extern int slap_ldap_response_code_register( struct berval *bv, int err );
+
+LDAP_SLAPD_V (ConfigTable) olcDatabaseDummy[];
+
+LDAP_END_DECL
+
+#endif /* CONFIG_H */
diff --git a/servers/slapd/slap.h b/servers/slapd/slap.h
new file mode 100644
index 0000000..5cf2f46
--- /dev/null
+++ b/servers/slapd/slap.h
@@ -0,0 +1,3377 @@
+/* slap.h - stand alone ldap server include file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#ifndef _SLAP_H_
+#define _SLAP_H_
+
+#include "ldap_defaults.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <sys/types.h>
+#include <ac/syslog.h>
+#include <ac/regex.h>
+#include <ac/signal.h>
+#include <ac/socket.h>
+#include <ac/time.h>
+#include <ac/param.h>
+
+#include "ldap_avl.h"
+
+#ifndef ldap_debug
+#define ldap_debug slap_debug
+#endif
+
+#include "ldap_log.h"
+
+#include <ldap.h>
+#include <ldap_schema.h>
+
+#include "lber_pvt.h"
+#include "ldap_pvt.h"
+#include "ldap_pvt_thread.h"
+#include "ldap_queue.h"
+
+#include "lutil.h"
+
+LDAP_BEGIN_DECL
+
+#ifdef LDAP_DEVEL
+#define LDAP_COLLECTIVE_ATTRIBUTES
+#define LDAP_COMP_MATCH
+#define LDAP_SYNC_TIMESTAMP
+#define SLAP_CONTROL_X_WHATFAILED
+#ifndef SLAP_SCHEMA_EXPOSE
+#define SLAP_SCHEMA_EXPOSE
+#endif
+#endif
+
+#define SLAP_CONFIG_DELETE
+#define SLAP_AUXPROP_DONTUSECOPY
+#define LDAP_DYNAMIC_OBJECTS
+#define SLAP_CONTROL_X_TREE_DELETE LDAP_CONTROL_X_TREE_DELETE
+#define SLAP_CONTROL_X_SESSION_TRACKING
+#define SLAP_DISTPROC
+#define SLAP_CONTROL_X_LAZY_COMMIT
+
+#ifndef SLAP_STATS_ETIME
+#define SLAP_STATS_ETIME 1 /* microsecond op timing */
+#endif
+
+/*
+ * SLAPD Memory allocation macros
+ *
+ * Unlike ch_*() routines, these routines do not assert() upon
+ * allocation error. They are intended to be used instead of
+ * ch_*() routines where the caller has implemented proper
+ * checking for and handling of allocation errors.
+ *
+ * Patches to convert ch_*() calls to SLAP_*() calls welcomed.
+ */
+#define SLAP_MALLOC(s) ber_memalloc((s))
+#define SLAP_CALLOC(n,s) ber_memcalloc((n),(s))
+#define SLAP_REALLOC(p,s) ber_memrealloc((p),(s))
+#define SLAP_FREE(p) ber_memfree((p))
+#define SLAP_VFREE(v) ber_memvfree((void**)(v))
+#define SLAP_STRDUP(s) ber_strdup((s))
+#define SLAP_STRNDUP(s,l) ber_strndup((s),(l))
+
+#ifdef f_next
+#undef f_next /* name conflict between sys/file.h on SCO and struct filter */
+#endif
+
+#define SERVICE_NAME OPENLDAP_PACKAGE "-slapd"
+#define SLAPD_ANONYMOUS ""
+
+#ifdef HAVE_TCPD
+# include <tcpd.h>
+# define SLAP_STRING_UNKNOWN STRING_UNKNOWN
+#else /* ! TCP Wrappers */
+# define SLAP_STRING_UNKNOWN "unknown"
+#endif /* ! TCP Wrappers */
+
+/* LDAPMod.mod_op value ===> Must be kept in sync with ldap.h! */
+/* These values are used internally by the backends. */
+/* SLAP_MOD_SOFTADD allows adding values that already exist without getting
+ * an error as required by modrdn when the new rdn was already an attribute
+ * value itself.
+ */
+#define SLAP_MOD_SOFTADD 0x1000
+/* SLAP_MOD_SOFTDEL allows deleting values if they exist without getting
+ * an error otherwise.
+ */
+#define SLAP_MOD_SOFTDEL 0x1001
+/* SLAP_MOD_ADD_IF_NOT_PRESENT allows adding values unless the attribute
+ * is already present without getting an error.
+ */
+#define SLAP_MOD_ADD_IF_NOT_PRESENT 0x1002
+/* SLAP_MOD_DEL_IF_PRESENT allows deleting values if the attribute
+ * is present, without getting an error otherwise.
+ * The semantics can be obtained using SLAP_MOD_SOFTDEL with NULL values.
+ */
+
+#define MAXREMATCHES (100)
+
+#define SLAP_MAX_WORKER_THREADS (16)
+
+#define SLAP_SB_MAX_INCOMING_DEFAULT ((1<<18) - 1)
+#define SLAP_SB_MAX_INCOMING_AUTH ((1<<24) - 1)
+
+#define SLAP_CONN_MAX_PENDING_DEFAULT 100
+#define SLAP_CONN_MAX_PENDING_AUTH 1000
+#define SLAP_MAX_FILTER_DEPTH_DEFAULT 1000
+
+#define SLAP_TEXT_BUFLEN (256)
+
+/* pseudo error code indicating abandoned operation */
+#define SLAPD_ABANDON (-1024)
+
+/* pseudo error code indicating disconnect */
+#define SLAPD_DISCONNECT (-1025)
+
+/* unknown config file directive */
+#define SLAP_CONF_UNKNOWN (-1026)
+
+/* pseudo error code indicating async operation */
+#define SLAPD_ASYNCOP (-1027)
+
+/* We assume "C" locale, that is US-ASCII */
+#define ASCII_SPACE(c) ( (c) == ' ' )
+#define ASCII_LOWER(c) ( (c) >= 'a' && (c) <= 'z' )
+#define ASCII_UPPER(c) ( (c) >= 'A' && (c) <= 'Z' )
+#define ASCII_ALPHA(c) ( ASCII_LOWER(c) || ASCII_UPPER(c) )
+#define ASCII_DIGIT(c) ( (c) >= '0' && (c) <= '9' )
+#define ASCII_HEXLOWER(c) ( (c) >= 'a' && (c) <= 'f' )
+#define ASCII_HEXUPPER(c) ( (c) >= 'A' && (c) <= 'F' )
+#define ASCII_HEX(c) ( ASCII_DIGIT(c) || \
+ ASCII_HEXLOWER(c) || ASCII_HEXUPPER(c) )
+#define ASCII_ALNUM(c) ( ASCII_ALPHA(c) || ASCII_DIGIT(c) )
+#define ASCII_PRINTABLE(c) ( (c) >= ' ' && (c) <= '~' )
+
+#define SLAP_NIBBLE(c) ((c)&0x0f)
+#define SLAP_ESCAPE_CHAR ('\\')
+#define SLAP_ESCAPE_LO(c) ( "0123456789ABCDEF"[SLAP_NIBBLE(c)] )
+#define SLAP_ESCAPE_HI(c) ( SLAP_ESCAPE_LO((c)>>4) )
+
+#define FILTER_ESCAPE(c) ( (c) == '*' || (c) == '\\' \
+ || (c) == '(' || (c) == ')' || !ASCII_PRINTABLE(c) )
+
+#define DN_ESCAPE(c) ((c) == SLAP_ESCAPE_CHAR)
+/* NOTE: for consistency, this macro must only operate
+ * on normalized/pretty DN, such that ';' is never used
+ * as RDN separator, and all occurrences of ';' must be escaped */
+#define DN_SEPARATOR(c) ((c) == ',')
+#define RDN_ATTRTYPEANDVALUE_SEPARATOR(c) ((c) == '+') /* RFC 4514 */
+#define RDN_SEPARATOR(c) (DN_SEPARATOR(c) || RDN_ATTRTYPEANDVALUE_SEPARATOR(c))
+#define RDN_NEEDSESCAPE(c) ((c) == '\\' || (c) == '"')
+
+#define DESC_LEADCHAR(c) ( ASCII_ALPHA(c) )
+#define DESC_CHAR(c) ( ASCII_ALNUM(c) || (c) == '-' )
+#define OID_LEADCHAR(c) ( ASCII_DIGIT(c) )
+#define OID_SEPARATOR(c) ( (c) == '.' )
+#define OID_CHAR(c) ( OID_LEADCHAR(c) || OID_SEPARATOR(c) )
+
+#define ATTR_LEADCHAR(c) ( DESC_LEADCHAR(c) || OID_LEADCHAR(c) )
+#define ATTR_CHAR(c) ( DESC_CHAR((c)) || OID_SEPARATOR(c) )
+
+#define AD_LEADCHAR(c) ( ATTR_LEADCHAR(c) )
+#define AD_CHAR(c) ( ATTR_CHAR(c) || (c) == ';' )
+
+#define SLAP_NUMERIC(c) ( ASCII_DIGIT(c) || ASCII_SPACE(c) )
+
+#define SLAP_PRINTABLE(c) ( ASCII_ALNUM(c) || (c) == '\'' || \
+ (c) == '(' || (c) == ')' || (c) == '+' || (c) == ',' || \
+ (c) == '-' || (c) == '.' || (c) == '/' || (c) == ':' || \
+ (c) == '?' || (c) == ' ' || (c) == '=' )
+#define SLAP_PRINTABLES(c) ( SLAP_PRINTABLE(c) || (c) == '$' )
+
+/* must match in schema_init.c */
+#define SLAPD_DN_SYNTAX "1.3.6.1.4.1.1466.115.121.1.12"
+#define SLAPD_NAMEUID_SYNTAX "1.3.6.1.4.1.1466.115.121.1.34"
+#define SLAPD_INTEGER_SYNTAX "1.3.6.1.4.1.1466.115.121.1.27"
+#define SLAPD_GROUP_ATTR "member"
+#define SLAPD_GROUP_CLASS "groupOfNames"
+#define SLAPD_ROLE_ATTR "roleOccupant"
+#define SLAPD_ROLE_CLASS "organizationalRole"
+
+#define SLAPD_TOP_OID "2.5.6.0"
+
+LDAP_SLAPD_V (int) slap_debug;
+
+typedef unsigned long slap_mask_t;
+
+/* Security Strength Factor */
+typedef unsigned slap_ssf_t;
+
+typedef struct slap_ssf_set {
+ slap_ssf_t sss_ssf;
+ slap_ssf_t sss_transport;
+ slap_ssf_t sss_tls;
+ slap_ssf_t sss_sasl;
+ slap_ssf_t sss_update_ssf;
+ slap_ssf_t sss_update_transport;
+ slap_ssf_t sss_update_tls;
+ slap_ssf_t sss_update_sasl;
+ slap_ssf_t sss_simple_bind;
+} slap_ssf_set_t;
+
+/* Flags for telling slap_sasl_getdn() what type of identity is being passed */
+#define SLAP_GETDN_AUTHCID 2
+#define SLAP_GETDN_AUTHZID 4
+
+/*
+ * Index types
+ */
+#define SLAP_INDEX_TYPE 0x00FFUL
+#define SLAP_INDEX_UNDEFINED 0x0001UL
+#define SLAP_INDEX_PRESENT 0x0002UL
+#define SLAP_INDEX_EQUALITY 0x0004UL
+#define SLAP_INDEX_APPROX 0x0008UL
+#define SLAP_INDEX_SUBSTR 0x0010UL
+#define SLAP_INDEX_EXTENDED 0x0020UL
+
+#define SLAP_INDEX_DEFAULT SLAP_INDEX_EQUALITY
+
+#define IS_SLAP_INDEX(mask, type) (((mask) & (type)) == (type))
+
+#define SLAP_INDEX_SUBSTR_TYPE 0x0F00UL
+
+#define SLAP_INDEX_SUBSTR_INITIAL ( SLAP_INDEX_SUBSTR | 0x0100UL )
+#define SLAP_INDEX_SUBSTR_ANY ( SLAP_INDEX_SUBSTR | 0x0200UL )
+#define SLAP_INDEX_SUBSTR_FINAL ( SLAP_INDEX_SUBSTR | 0x0400UL )
+#define SLAP_INDEX_SUBSTR_DEFAULT \
+ ( SLAP_INDEX_SUBSTR \
+ | SLAP_INDEX_SUBSTR_INITIAL \
+ | SLAP_INDEX_SUBSTR_ANY \
+ | SLAP_INDEX_SUBSTR_FINAL )
+
+/* defaults for initial/final substring indices */
+#define SLAP_INDEX_SUBSTR_IF_MINLEN_DEFAULT 2
+#define SLAP_INDEX_SUBSTR_IF_MAXLEN_DEFAULT 4
+
+/* defaults for any substring indices */
+#define SLAP_INDEX_SUBSTR_ANY_LEN_DEFAULT 4
+#define SLAP_INDEX_SUBSTR_ANY_STEP_DEFAULT 2
+
+/* default for ordered integer index keys */
+#define SLAP_INDEX_INTLEN_DEFAULT 4
+
+#define SLAP_INDEX_FLAGS 0xF000UL
+#define SLAP_INDEX_NOSUBTYPES 0x1000UL /* don't use index w/ subtypes */
+#define SLAP_INDEX_NOTAGS 0x2000UL /* don't use index w/ tags */
+
+/*
+ * there is a single index for each attribute. these prefixes ensure
+ * that there is no collision among keys.
+ */
+#define SLAP_INDEX_EQUALITY_PREFIX '=' /* prefix for equality keys */
+#define SLAP_INDEX_APPROX_PREFIX '~' /* prefix for approx keys */
+#define SLAP_INDEX_SUBSTR_PREFIX '*' /* prefix for substring keys */
+#define SLAP_INDEX_SUBSTR_INITIAL_PREFIX '^'
+#define SLAP_INDEX_SUBSTR_FINAL_PREFIX '$'
+#define SLAP_INDEX_CONT_PREFIX '.' /* prefix for continuation keys */
+
+#define SLAP_SYNTAX_MATCHINGRULES_OID "1.3.6.1.4.1.1466.115.121.1.30"
+#define SLAP_SYNTAX_ATTRIBUTETYPES_OID "1.3.6.1.4.1.1466.115.121.1.3"
+#define SLAP_SYNTAX_OBJECTCLASSES_OID "1.3.6.1.4.1.1466.115.121.1.37"
+#define SLAP_SYNTAX_MATCHINGRULEUSES_OID "1.3.6.1.4.1.1466.115.121.1.31"
+#define SLAP_SYNTAX_CONTENTRULE_OID "1.3.6.1.4.1.1466.115.121.1.16"
+
+/*
+ * represents schema information for a database
+ */
+enum {
+ SLAP_SCHERR_OUTOFMEM = 1,
+ SLAP_SCHERR_CLASS_NOT_FOUND,
+ SLAP_SCHERR_CLASS_BAD_USAGE,
+ SLAP_SCHERR_CLASS_BAD_SUP,
+ SLAP_SCHERR_CLASS_DUP,
+ SLAP_SCHERR_CLASS_INCONSISTENT,
+ SLAP_SCHERR_ATTR_NOT_FOUND,
+ SLAP_SCHERR_ATTR_BAD_MR,
+ SLAP_SCHERR_ATTR_BAD_USAGE,
+ SLAP_SCHERR_ATTR_BAD_SUP,
+ SLAP_SCHERR_ATTR_INCOMPLETE,
+ SLAP_SCHERR_ATTR_DUP,
+ SLAP_SCHERR_ATTR_INCONSISTENT,
+ SLAP_SCHERR_MR_NOT_FOUND,
+ SLAP_SCHERR_MR_INCOMPLETE,
+ SLAP_SCHERR_MR_DUP,
+ SLAP_SCHERR_SYN_NOT_FOUND,
+ SLAP_SCHERR_SYN_DUP,
+ SLAP_SCHERR_SYN_SUP_NOT_FOUND,
+ SLAP_SCHERR_SYN_SUBST_NOT_SPECIFIED,
+ SLAP_SCHERR_SYN_SUBST_NOT_FOUND,
+ SLAP_SCHERR_NO_NAME,
+ SLAP_SCHERR_NOT_SUPPORTED,
+ SLAP_SCHERR_BAD_DESCR,
+ SLAP_SCHERR_OIDM,
+ SLAP_SCHERR_CR_DUP,
+ SLAP_SCHERR_CR_BAD_STRUCT,
+ SLAP_SCHERR_CR_BAD_AUX,
+ SLAP_SCHERR_CR_BAD_AT,
+
+ SLAP_SCHERR_LAST
+};
+
+/* forward declarations */
+typedef struct Syntax Syntax;
+typedef struct MatchingRule MatchingRule;
+typedef struct MatchingRuleUse MatchingRuleUse;
+typedef struct MatchingRuleAssertion MatchingRuleAssertion;
+typedef struct OidMacro OidMacro;
+typedef struct ObjectClass ObjectClass;
+typedef struct AttributeType AttributeType;
+typedef struct AttributeDescription AttributeDescription;
+typedef struct AttributeName AttributeName;
+typedef struct ContentRule ContentRule;
+
+typedef struct AttributeAssertion AttributeAssertion;
+typedef struct SubstringsAssertion SubstringsAssertion;
+typedef struct Filter Filter;
+typedef struct ValuesReturnFilter ValuesReturnFilter;
+typedef struct Attribute Attribute;
+#ifdef LDAP_COMP_MATCH
+typedef struct ComponentData ComponentData;
+typedef struct ComponentFilter ComponentFilter;
+#endif
+
+typedef struct Entry Entry;
+typedef struct Modification Modification;
+typedef struct Modifications Modifications;
+typedef struct LDAPModList LDAPModList;
+
+typedef struct BackendInfo BackendInfo; /* per backend type */
+typedef struct BackendDB BackendDB; /* per backend database */
+
+typedef struct Connection Connection;
+typedef struct Operation Operation;
+typedef struct SlapReply SlapReply;
+/* end of forward declarations */
+
+extern int slap_inet4or6;
+
+struct OidMacro {
+ struct berval som_oid;
+ BerVarray som_names;
+ BerVarray som_subs;
+#define SLAP_OM_HARDCODE 0x10000U /* This is hardcoded schema */
+ int som_flags;
+ LDAP_STAILQ_ENTRY(OidMacro) som_next;
+};
+
+typedef int slap_syntax_validate_func LDAP_P((
+ Syntax *syntax,
+ struct berval * in));
+
+typedef int slap_syntax_transform_func LDAP_P((
+ Syntax *syntax,
+ struct berval * in,
+ struct berval * out,
+ void *memctx));
+
+#ifdef LDAP_COMP_MATCH
+typedef void* slap_component_transform_func LDAP_P((
+ struct berval * in ));
+struct ComponentDesc;
+#endif
+
+struct Syntax {
+ LDAPSyntax ssyn_syn;
+#define ssyn_oid ssyn_syn.syn_oid
+#define ssyn_desc ssyn_syn.syn_desc
+#define ssyn_extensions ssyn_syn.syn_extensions
+ /*
+ * Note: the former
+ ber_len_t ssyn_oidlen;
+ * has been replaced by a struct berval that uses the value
+ * provided by ssyn_syn.syn_oid; a macro that expands to
+ * the bv_len field of the berval is provided for backward
+ * compatibility. CAUTION: NEVER FREE THE BERVAL
+ */
+ struct berval ssyn_bvoid;
+#define ssyn_oidlen ssyn_bvoid.bv_len
+
+ unsigned int ssyn_flags;
+
+#define SLAP_SYNTAX_NONE 0x0000U
+#define SLAP_SYNTAX_BLOB 0x0001U /* syntax treated as blob (audio) */
+#define SLAP_SYNTAX_BINARY 0x0002U /* binary transfer required (certificate) */
+#define SLAP_SYNTAX_BER 0x0004U /* stored in BER encoding (certificate) */
+#ifdef SLAP_SCHEMA_EXPOSE
+#define SLAP_SYNTAX_HIDE 0x0000U /* publish everything */
+#else
+#define SLAP_SYNTAX_HIDE 0x8000U /* hide (do not publish) */
+#endif
+#define SLAP_SYNTAX_HARDCODE 0x10000U /* This is hardcoded schema */
+#define SLAP_SYNTAX_DN 0x20000U /* Treat like a DN */
+
+ Syntax **ssyn_sups;
+
+ slap_syntax_validate_func *ssyn_validate;
+ slap_syntax_transform_func *ssyn_pretty;
+
+#ifdef SLAPD_BINARY_CONVERSION
+ /* convert to and from binary */
+ slap_syntax_transform_func *ssyn_ber2str;
+ slap_syntax_transform_func *ssyn_str2ber;
+#endif
+#ifdef LDAP_COMP_MATCH
+ slap_component_transform_func *ssyn_attr2comp;
+ struct ComponentDesc* ssync_comp_syntax;
+#endif
+
+ LDAP_STAILQ_ENTRY(Syntax) ssyn_next;
+};
+
+#define slap_syntax_is_flag(s,flag) ((int)((s)->ssyn_flags & (flag)) ? 1 : 0)
+#define slap_syntax_is_blob(s) slap_syntax_is_flag((s),SLAP_SYNTAX_BLOB)
+#define slap_syntax_is_binary(s) slap_syntax_is_flag((s),SLAP_SYNTAX_BINARY)
+#define slap_syntax_is_ber(s) slap_syntax_is_flag((s),SLAP_SYNTAX_BER)
+#define slap_syntax_is_hidden(s) slap_syntax_is_flag((s),SLAP_SYNTAX_HIDE)
+
+typedef struct slap_syntax_defs_rec {
+ char *sd_desc;
+ int sd_flags;
+ char **sd_sups;
+ slap_syntax_validate_func *sd_validate;
+ slap_syntax_transform_func *sd_pretty;
+#ifdef SLAPD_BINARY_CONVERSION
+ slap_syntax_transform_func *sd_ber2str;
+ slap_syntax_transform_func *sd_str2ber;
+#endif
+} slap_syntax_defs_rec;
+
+/* X -> Y Converter */
+typedef int slap_mr_convert_func LDAP_P((
+ struct berval * in,
+ struct berval * out,
+ void *memctx ));
+
+/* Normalizer */
+typedef int slap_mr_normalize_func LDAP_P((
+ slap_mask_t use,
+ Syntax *syntax, /* NULL if in is asserted value */
+ MatchingRule *mr,
+ struct berval *in,
+ struct berval *out,
+ void *memctx ));
+
+/* Match (compare) function */
+typedef int slap_mr_match_func LDAP_P((
+ int *match,
+ slap_mask_t use,
+ Syntax *syntax, /* syntax of stored value */
+ MatchingRule *mr,
+ struct berval *value,
+ void *assertValue ));
+
+/* Index generation function */
+typedef int slap_mr_indexer_func LDAP_P((
+ slap_mask_t use,
+ slap_mask_t mask,
+ Syntax *syntax, /* syntax of stored value */
+ MatchingRule *mr,
+ struct berval *prefix,
+ BerVarray values,
+ BerVarray *keys,
+ void *memctx ));
+
+/* Filter index function */
+typedef int slap_mr_filter_func LDAP_P((
+ slap_mask_t use,
+ slap_mask_t mask,
+ Syntax *syntax, /* syntax of stored value */
+ MatchingRule *mr,
+ struct berval *prefix,
+ void *assertValue,
+ BerVarray *keys,
+ void *memctx ));
+
+struct MatchingRule {
+ LDAPMatchingRule smr_mrule;
+ MatchingRuleUse *smr_mru;
+ /* RFC 4512 string representation */
+ struct berval smr_str;
+ /*
+ * Note: the former
+ * ber_len_t smr_oidlen;
+ * has been replaced by a struct berval that uses the value
+ * provided by smr_mrule.mr_oid; a macro that expands to
+ * the bv_len field of the berval is provided for backward
+ * compatibility. CAUTION: NEVER FREE THE BERVAL
+ */
+ struct berval smr_bvoid;
+#define smr_oidlen smr_bvoid.bv_len
+
+ slap_mask_t smr_usage;
+
+#ifdef SLAP_SCHEMA_EXPOSE
+#define SLAP_MR_HIDE 0x0000U
+#else
+#define SLAP_MR_HIDE 0x8000U
+#endif
+
+#define SLAP_MR_MUTATION_NORMALIZER 0x4000U
+
+#define SLAP_MR_TYPE_MASK 0x0F00U
+#define SLAP_MR_SUBTYPE_MASK 0x00F0U
+#define SLAP_MR_USAGE 0x000FU
+
+#define SLAP_MR_NONE 0x0000U
+#define SLAP_MR_EQUALITY 0x0100U
+#define SLAP_MR_ORDERING 0x0200U
+#define SLAP_MR_SUBSTR 0x0400U
+#define SLAP_MR_EXT 0x0800U /* implicitly extensible */
+#define SLAP_MR_ORDERED_INDEX 0x1000U
+#ifdef LDAP_COMP_MATCH
+#define SLAP_MR_COMPONENT 0x2000U
+#endif
+
+#define SLAP_MR_EQUALITY_APPROX ( SLAP_MR_EQUALITY | 0x0010U )
+
+#define SLAP_MR_SUBSTR_INITIAL ( SLAP_MR_SUBSTR | 0x0010U )
+#define SLAP_MR_SUBSTR_ANY ( SLAP_MR_SUBSTR | 0x0020U )
+#define SLAP_MR_SUBSTR_FINAL ( SLAP_MR_SUBSTR | 0x0040U )
+
+
+/*
+ * The asserted value, depending on the particular usage,
+ * is expected to conform to either the assertion syntax
+ * or the attribute syntax. In some cases, the syntax of
+ * the value is known. If so, these flags indicate which
+ * syntax the value is expected to conform to. If not,
+ * neither of these flags is set (until the syntax of the
+ * provided value is determined). If the value is of the
+ * attribute syntax, the flag is changed once a value of
+ * the assertion syntax is derived from the provided value.
+ */
+#define SLAP_MR_VALUE_OF_ASSERTION_SYNTAX 0x0001U
+#define SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX 0x0002U
+#define SLAP_MR_VALUE_OF_SYNTAX (SLAP_MR_VALUE_OF_ASSERTION_SYNTAX|SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX)
+#define SLAP_MR_DENORMALIZE (SLAP_MR_MUTATION_NORMALIZER)
+
+#define SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX( usage ) \
+ ((usage) & SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX )
+#define SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX( usage ) \
+ ((usage) & SLAP_MR_VALUE_OF_ASSERTION_SYNTAX )
+#ifdef LDAP_DEBUG
+#define SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) \
+ ((usage) & SLAP_MR_VALUE_OF_SYNTAX)
+#else
+#define SLAP_MR_IS_VALUE_OF_SYNTAX( usage ) (1)
+#endif
+#define SLAP_MR_IS_DENORMALIZE( usage ) \
+ ((usage) & SLAP_MR_DENORMALIZE )
+
+/* either or both the asserted value or attribute value
+ * may be provided in normalized form
+ */
+#define SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH 0x0004U
+#define SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH 0x0008U
+
+#define SLAP_IS_MR_ASSERTION_SYNTAX_MATCH( usage ) \
+ (!((usage) & SLAP_MR_ATTRIBUTE_SYNTAX_MATCH))
+#define SLAP_IS_MR_ATTRIBUTE_SYNTAX_MATCH( usage ) \
+ ((usage) & SLAP_MR_ATTRIBUTE_SYNTAX_MATCH)
+
+#define SLAP_IS_MR_ATTRIBUTE_SYNTAX_CONVERTED_MATCH( usage ) \
+ (((usage) & SLAP_MR_ATTRIBUTE_SYNTAX_CONVERTED_MATCH) \
+ == SLAP_MR_ATTRIBUTE_SYNTAX_CONVERTED_MATCH)
+#define SLAP_IS_MR_ATTRIBUTE_SYNTAX_NONCONVERTED_MATCH( usage ) \
+ (((usage) & SLAP_MR_ATTRIBUTE_SYNTAX_CONVERTED_MATCH) \
+ == SLAP_MR_ATTRIBUTE_SYNTAX_MATCH)
+
+#define SLAP_IS_MR_ASSERTED_VALUE_NORMALIZED_MATCH( usage ) \
+ ((usage) & SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH )
+#define SLAP_IS_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH( usage ) \
+ ((usage) & SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH )
+
+ Syntax *smr_syntax;
+ slap_mr_convert_func *smr_convert;
+ slap_mr_normalize_func *smr_normalize;
+ slap_mr_match_func *smr_match;
+ slap_mr_indexer_func *smr_indexer;
+ slap_mr_filter_func *smr_filter;
+
+ /*
+ * null terminated array of syntaxes compatible with this syntax
+ * note: when MS_EXT is set, this MUST NOT contain the assertion
+ * syntax of the rule. When MS_EXT is not set, it MAY.
+ */
+ Syntax **smr_compat_syntaxes;
+
+ /*
+ * For equality rules, refers to an associated approximate rule.
+ * For non-equality rules, refers to an associated equality rule.
+ */
+ MatchingRule *smr_associated;
+
+#define SLAP_MR_ASSOCIATED(mr,amr) \
+ (((mr) == (amr)) || ((mr)->smr_associated == (amr)))
+
+ LDAP_SLIST_ENTRY(MatchingRule) smr_next;
+
+#define smr_oid smr_mrule.mr_oid
+#define smr_names smr_mrule.mr_names
+#define smr_desc smr_mrule.mr_desc
+#define smr_obsolete smr_mrule.mr_obsolete
+#define smr_syntax_oid smr_mrule.mr_syntax_oid
+#define smr_extensions smr_mrule.mr_extensions
+};
+
+struct MatchingRuleUse {
+ LDAPMatchingRuleUse smru_mruleuse;
+ MatchingRule *smru_mr;
+ /* RFC 4512 string representation */
+ struct berval smru_str;
+
+ LDAP_SLIST_ENTRY(MatchingRuleUse) smru_next;
+
+#define smru_oid smru_mruleuse.mru_oid
+#define smru_names smru_mruleuse.mru_names
+#define smru_desc smru_mruleuse.mru_desc
+#define smru_obsolete smru_mruleuse.mru_obsolete
+#define smru_applies_oids smru_mruleuse.mru_applies_oids
+
+#define smru_usage smru_mr->smr_usage
+} /* MatchingRuleUse */ ;
+
+typedef struct slap_mrule_defs_rec {
+ char * mrd_desc;
+ slap_mask_t mrd_usage;
+ char ** mrd_compat_syntaxes;
+ slap_mr_convert_func * mrd_convert;
+ slap_mr_normalize_func * mrd_normalize;
+ slap_mr_match_func * mrd_match;
+ slap_mr_indexer_func * mrd_indexer;
+ slap_mr_filter_func * mrd_filter;
+
+ /* For equality rule, this may refer to an associated approximate rule */
+ /* For non-equality rule, this may refer to an associated equality rule */
+ char * mrd_associated;
+} slap_mrule_defs_rec;
+
+typedef int (AttributeTypeSchemaCheckFN)(
+ BackendDB *be,
+ Entry *e,
+ Attribute *attr,
+ const char** text,
+ char *textbuf, size_t textlen );
+
+struct AttributeType {
+ LDAPAttributeType sat_atype;
+ struct berval sat_cname;
+ AttributeType *sat_sup;
+ AttributeType **sat_subtypes;
+ MatchingRule *sat_equality;
+ MatchingRule *sat_approx;
+ MatchingRule *sat_ordering;
+ MatchingRule *sat_substr;
+ Syntax *sat_syntax;
+
+ AttributeTypeSchemaCheckFN *sat_check;
+ char *sat_oidmacro; /* attribute OID */
+ char *sat_soidmacro; /* syntax OID */
+
+#define SLAP_AT_NONE 0x0000U
+#define SLAP_AT_ABSTRACT 0x0100U /* cannot be instantiated */
+#define SLAP_AT_FINAL 0x0200U /* cannot be subtyped */
+#ifdef SLAP_SCHEMA_EXPOSE
+#define SLAP_AT_HIDE 0x0000U /* publish everything */
+#else
+#define SLAP_AT_HIDE 0x8000U /* hide attribute */
+#endif
+#define SLAP_AT_DYNAMIC 0x0400U /* dynamically generated */
+
+#define SLAP_AT_MANAGEABLE 0x0800U /* no-user-mod can be by-passed */
+
+/* Note: ORDERED values have an ordering specifically set by the
+ * user, denoted by the {x} ordering prefix on the values.
+ *
+ * SORTED values are simply sorted by memcmp. SORTED values can
+ * be efficiently located by binary search. ORDERED values have no
+ * such advantage. An attribute cannot have both properties.
+ */
+#define SLAP_AT_ORDERED_VAL 0x0001U /* values are ordered */
+#define SLAP_AT_ORDERED_SIB 0x0002U /* siblings are ordered */
+#define SLAP_AT_ORDERED 0x0003U /* value has order index */
+
+#define SLAP_AT_SORTED_VAL 0x0010U /* values should be sorted */
+
+#define SLAP_AT_HARDCODE 0x10000U /* hardcoded schema */
+#define SLAP_AT_DELETED 0x20000U
+
+ slap_mask_t sat_flags;
+
+ LDAP_STAILQ_ENTRY(AttributeType) sat_next;
+
+#define sat_oid sat_atype.at_oid
+#define sat_names sat_atype.at_names
+#define sat_desc sat_atype.at_desc
+#define sat_obsolete sat_atype.at_obsolete
+#define sat_sup_oid sat_atype.at_sup_oid
+#define sat_equality_oid sat_atype.at_equality_oid
+#define sat_ordering_oid sat_atype.at_ordering_oid
+#define sat_substr_oid sat_atype.at_substr_oid
+#define sat_syntax_oid sat_atype.at_syntax_oid
+#define sat_single_value sat_atype.at_single_value
+#define sat_collective sat_atype.at_collective
+#define sat_no_user_mod sat_atype.at_no_user_mod
+#define sat_usage sat_atype.at_usage
+#define sat_extensions sat_atype.at_extensions
+
+ AttributeDescription *sat_ad;
+ ldap_pvt_thread_mutex_t sat_ad_mutex;
+};
+
+#define is_at_operational(at) ((at)->sat_usage)
+#define is_at_single_value(at) ((at)->sat_single_value)
+#define is_at_collective(at) ((at)->sat_collective)
+#define is_at_obsolete(at) ((at)->sat_obsolete)
+#define is_at_no_user_mod(at) ((at)->sat_no_user_mod)
+
+typedef int (ObjectClassSchemaCheckFN)(
+ BackendDB *be,
+ Entry *e,
+ ObjectClass *oc,
+ const char** text,
+ char *textbuf, size_t textlen );
+
+struct ObjectClass {
+ LDAPObjectClass soc_oclass;
+ struct berval soc_cname;
+ ObjectClass **soc_sups;
+ AttributeType **soc_required;
+ AttributeType **soc_allowed;
+ ObjectClassSchemaCheckFN *soc_check;
+ char *soc_oidmacro;
+ slap_mask_t soc_flags;
+#define soc_oid soc_oclass.oc_oid
+#define soc_names soc_oclass.oc_names
+#define soc_desc soc_oclass.oc_desc
+#define soc_obsolete soc_oclass.oc_obsolete
+#define soc_sup_oids soc_oclass.oc_sup_oids
+#define soc_kind soc_oclass.oc_kind
+#define soc_at_oids_must soc_oclass.oc_at_oids_must
+#define soc_at_oids_may soc_oclass.oc_at_oids_may
+#define soc_extensions soc_oclass.oc_extensions
+
+ LDAP_STAILQ_ENTRY(ObjectClass) soc_next;
+};
+
+#define SLAP_OCF_SET_FLAGS 0x1
+#define SLAP_OCF_CHECK_SUP 0x2
+#define SLAP_OCF_MASK (SLAP_OCF_SET_FLAGS|SLAP_OCF_CHECK_SUP)
+
+#define SLAP_OC_ALIAS 0x0001
+#define SLAP_OC_REFERRAL 0x0002
+#define SLAP_OC_SUBENTRY 0x0004
+#define SLAP_OC_DYNAMICOBJECT 0x0008
+#define SLAP_OC_COLLECTIVEATTRIBUTESUBENTRY 0x0010
+#define SLAP_OC_GLUE 0x0020
+#define SLAP_OC_SYNCPROVIDERSUBENTRY 0x0040
+#define SLAP_OC_SYNCCONSUMERSUBENTRY 0x0080
+#define SLAP_OC__MASK 0x00FF
+#define SLAP_OC__END 0x0100
+#define SLAP_OC_OPERATIONAL 0x4000
+#ifdef SLAP_SCHEMA_EXPOSE
+#define SLAP_OC_HIDE 0x0000
+#else
+#define SLAP_OC_HIDE 0x8000
+#endif
+#define SLAP_OC_HARDCODE 0x10000U /* This is hardcoded schema */
+#define SLAP_OC_DELETED 0x20000U
+
+/*
+ * DIT content rule
+ */
+struct ContentRule {
+ LDAPContentRule scr_crule;
+ ObjectClass *scr_sclass;
+ ObjectClass **scr_auxiliaries; /* optional */
+ AttributeType **scr_required; /* optional */
+ AttributeType **scr_allowed; /* optional */
+ AttributeType **scr_precluded; /* optional */
+#define scr_oid scr_crule.cr_oid
+#define scr_names scr_crule.cr_names
+#define scr_desc scr_crule.cr_desc
+#define scr_obsolete scr_crule.cr_obsolete
+#define scr_oc_oids_aux scr_crule.cr_oc_oids_aux
+#define scr_at_oids_must scr_crule.cr_at_oids_must
+#define scr_at_oids_may scr_crule.cr_at_oids_may
+#define scr_at_oids_not scr_crule.cr_at_oids_not
+
+ char *scr_oidmacro;
+#define SLAP_CR_HARDCODE 0x10000U
+ int scr_flags;
+
+ LDAP_STAILQ_ENTRY( ContentRule ) scr_next;
+};
+
+/* Represents a recognized attribute description ( type + options ). */
+struct AttributeDescription {
+ AttributeDescription *ad_next;
+ AttributeType *ad_type; /* attribute type, must be specified */
+ struct berval ad_cname; /* canonical name, must be specified */
+ struct berval ad_tags; /* empty if no tagging options */
+ unsigned ad_flags;
+#define SLAP_DESC_NONE 0x00U
+#define SLAP_DESC_BINARY 0x01U
+#define SLAP_DESC_TAG_RANGE 0x80U
+#define SLAP_DESC_TEMPORARY 0x1000U
+ unsigned ad_index;
+};
+
+/* flags to slap_*2undef_ad to register undefined (0, the default)
+ * or proxied (SLAP_AD_PROXIED) AttributeDescriptions; the additional
+ * SLAP_AD_NOINSERT is to lookup without insert */
+#define SLAP_AD_UNDEF 0x00U
+#define SLAP_AD_PROXIED 0x01U
+#define SLAP_AD_NOINSERT 0x02U
+
+#define SLAP_AN_OCEXCLUDE 0x01
+#define SLAP_AN_OCINITED 0x02
+
+struct AttributeName {
+ struct berval an_name;
+ AttributeDescription *an_desc;
+ int an_flags;
+ ObjectClass *an_oc;
+};
+
+#define slap_ad_is_tagged(ad) ( (ad)->ad_tags.bv_len != 0 )
+#define slap_ad_is_tag_range(ad) \
+ ( ((ad)->ad_flags & SLAP_DESC_TAG_RANGE) ? 1 : 0 )
+#define slap_ad_is_binary(ad) \
+ ( ((ad)->ad_flags & SLAP_DESC_BINARY) ? 1 : 0 )
+
+/*
+ * pointers to schema elements used internally
+ */
+struct slap_internal_schema {
+ /* objectClass */
+ ObjectClass *si_oc_top;
+ ObjectClass *si_oc_extensibleObject;
+ ObjectClass *si_oc_alias;
+ ObjectClass *si_oc_referral;
+ ObjectClass *si_oc_rootdse;
+ ObjectClass *si_oc_subentry;
+ ObjectClass *si_oc_subschema;
+ ObjectClass *si_oc_collectiveAttributeSubentry;
+ ObjectClass *si_oc_dynamicObject;
+
+ ObjectClass *si_oc_glue;
+ ObjectClass *si_oc_syncConsumerSubentry;
+ ObjectClass *si_oc_syncProviderSubentry;
+
+ /* objectClass attribute descriptions */
+ AttributeDescription *si_ad_objectClass;
+
+ /* operational attribute descriptions */
+ AttributeDescription *si_ad_structuralObjectClass;
+ AttributeDescription *si_ad_creatorsName;
+ AttributeDescription *si_ad_createTimestamp;
+ AttributeDescription *si_ad_modifiersName;
+ AttributeDescription *si_ad_modifyTimestamp;
+ AttributeDescription *si_ad_hasSubordinates;
+ AttributeDescription *si_ad_subschemaSubentry;
+ AttributeDescription *si_ad_collectiveSubentries;
+ AttributeDescription *si_ad_collectiveExclusions;
+ AttributeDescription *si_ad_entryDN;
+ AttributeDescription *si_ad_entryUUID;
+ AttributeDescription *si_ad_entryCSN;
+ AttributeDescription *si_ad_namingCSN;
+
+ AttributeDescription *si_ad_dseType;
+ AttributeDescription *si_ad_syncreplCookie;
+ AttributeDescription *si_ad_syncTimestamp;
+ AttributeDescription *si_ad_contextCSN;
+
+ /* root DSE attribute descriptions */
+ AttributeDescription *si_ad_altServer;
+ AttributeDescription *si_ad_namingContexts;
+ AttributeDescription *si_ad_supportedControl;
+ AttributeDescription *si_ad_supportedExtension;
+ AttributeDescription *si_ad_supportedLDAPVersion;
+ AttributeDescription *si_ad_supportedSASLMechanisms;
+ AttributeDescription *si_ad_supportedFeatures;
+ AttributeDescription *si_ad_monitorContext;
+ AttributeDescription *si_ad_vendorName;
+ AttributeDescription *si_ad_vendorVersion;
+ AttributeDescription *si_ad_configContext;
+
+ /* subentry attribute descriptions */
+ AttributeDescription *si_ad_administrativeRole;
+ AttributeDescription *si_ad_subtreeSpecification;
+
+ /* subschema subentry attribute descriptions */
+ AttributeDescription *si_ad_attributeTypes;
+ AttributeDescription *si_ad_ditContentRules;
+ AttributeDescription *si_ad_ditStructureRules;
+ AttributeDescription *si_ad_ldapSyntaxes;
+ AttributeDescription *si_ad_matchingRules;
+ AttributeDescription *si_ad_matchingRuleUse;
+ AttributeDescription *si_ad_nameForms;
+ AttributeDescription *si_ad_objectClasses;
+
+ /* Aliases & Referrals */
+ AttributeDescription *si_ad_aliasedObjectName;
+ AttributeDescription *si_ad_ref;
+
+ /* Access Control Internals */
+ AttributeDescription *si_ad_entry;
+ AttributeDescription *si_ad_children;
+ AttributeDescription *si_ad_saslAuthzTo;
+ AttributeDescription *si_ad_saslAuthzFrom;
+
+ /* dynamic entries */
+ AttributeDescription *si_ad_entryTtl;
+ AttributeDescription *si_ad_dynamicSubtrees;
+
+ /* Other attributes descriptions */
+ AttributeDescription *si_ad_distinguishedName;
+ AttributeDescription *si_ad_name;
+ AttributeDescription *si_ad_cn;
+ AttributeDescription *si_ad_uid;
+ AttributeDescription *si_ad_uidNumber;
+ AttributeDescription *si_ad_gidNumber;
+ AttributeDescription *si_ad_userPassword;
+ AttributeDescription *si_ad_labeledURI;
+#ifdef SLAPD_AUTHPASSWD
+ AttributeDescription *si_ad_authPassword;
+ AttributeDescription *si_ad_authPasswordSchemes;
+#endif
+ AttributeDescription *si_ad_description;
+ AttributeDescription *si_ad_seeAlso;
+
+ /* privateKeys */
+ AttributeDescription *si_ad_pKCS8PrivateKey;
+
+ /* ppolicy lastbind equivalent */
+ AttributeDescription *si_ad_pwdLastSuccess;
+
+ /* Undefined Attribute Type */
+ AttributeType *si_at_undefined;
+
+ /* "Proxied" Attribute Type */
+ AttributeType *si_at_proxied;
+
+ /* Matching Rules */
+ MatchingRule *si_mr_distinguishedNameMatch;
+ MatchingRule *si_mr_dnSubtreeMatch;
+ MatchingRule *si_mr_dnOneLevelMatch;
+ MatchingRule *si_mr_dnSubordinateMatch;
+ MatchingRule *si_mr_dnSuperiorMatch;
+ MatchingRule *si_mr_caseExactMatch;
+ MatchingRule *si_mr_caseExactSubstringsMatch;
+ MatchingRule *si_mr_caseExactIA5Match;
+ MatchingRule *si_mr_integerMatch;
+ MatchingRule *si_mr_integerFirstComponentMatch;
+ MatchingRule *si_mr_objectIdentifierFirstComponentMatch;
+ MatchingRule *si_mr_caseIgnoreMatch;
+ MatchingRule *si_mr_caseIgnoreListMatch;
+
+ /* Syntaxes */
+ Syntax *si_syn_directoryString;
+ Syntax *si_syn_distinguishedName;
+ Syntax *si_syn_integer;
+ Syntax *si_syn_octetString;
+
+ /* Schema Syntaxes */
+ Syntax *si_syn_attributeTypeDesc;
+ Syntax *si_syn_ditContentRuleDesc;
+ Syntax *si_syn_ditStructureRuleDesc;
+ Syntax *si_syn_ldapSyntaxDesc;
+ Syntax *si_syn_matchingRuleDesc;
+ Syntax *si_syn_matchingRuleUseDesc;
+ Syntax *si_syn_nameFormDesc;
+ Syntax *si_syn_objectClassDesc;
+};
+
+struct AttributeAssertion {
+ AttributeDescription *aa_desc;
+ struct berval aa_value;
+#ifdef LDAP_COMP_MATCH
+ ComponentFilter *aa_cf; /* for attribute aliasing */
+#endif
+};
+#ifdef LDAP_COMP_MATCH
+#define ATTRIBUTEASSERTION_INIT { NULL, BER_BVNULL, NULL }
+#else
+#define ATTRIBUTEASSERTION_INIT { NULL, BER_BVNULL }
+#endif
+
+struct SubstringsAssertion {
+ AttributeDescription *sa_desc;
+ struct berval sa_initial;
+ struct berval *sa_any;
+ struct berval sa_final;
+};
+
+struct MatchingRuleAssertion {
+ AttributeDescription *ma_desc; /* optional */
+ struct berval ma_value; /* required */
+ MatchingRule *ma_rule; /* optional */
+ struct berval ma_rule_text; /* optional */
+ int ma_dnattrs; /* boolean */
+#ifdef LDAP_COMP_MATCH
+ ComponentFilter *ma_cf; /* component filter */
+#endif
+};
+
+/*
+ * represents a search filter
+ */
+struct Filter {
+ ber_tag_t f_choice; /* values taken from ldap.h, plus: */
+#define SLAPD_FILTER_COMPUTED 0
+#define SLAPD_FILTER_MASK 0x7fff
+#define SLAPD_FILTER_UNDEFINED 0x8000
+
+ union f_un_u {
+ /* precomputed result */
+ ber_int_t f_un_result;
+
+ /* present */
+ AttributeDescription *f_un_desc;
+
+ /* simple value assertion */
+ AttributeAssertion *f_un_ava;
+
+ /* substring assertion */
+ SubstringsAssertion *f_un_ssa;
+
+ /* matching rule assertion */
+ MatchingRuleAssertion *f_un_mra;
+
+#define f_desc f_un.f_un_desc
+#define f_ava f_un.f_un_ava
+#define f_av_desc f_un.f_un_ava->aa_desc
+#define f_av_value f_un.f_un_ava->aa_value
+#define f_sub f_un.f_un_ssa
+#define f_sub_desc f_un.f_un_ssa->sa_desc
+#define f_sub_initial f_un.f_un_ssa->sa_initial
+#define f_sub_any f_un.f_un_ssa->sa_any
+#define f_sub_final f_un.f_un_ssa->sa_final
+#define f_mra f_un.f_un_mra
+#define f_mr_rule f_un.f_un_mra->ma_rule
+#define f_mr_rule_text f_un.f_un_mra->ma_rule_text
+#define f_mr_desc f_un.f_un_mra->ma_desc
+#define f_mr_value f_un.f_un_mra->ma_value
+#define f_mr_dnattrs f_un.f_un_mra->ma_dnattrs
+
+ /* and, or, not */
+ Filter *f_un_complex;
+ } f_un;
+
+#define f_result f_un.f_un_result
+#define f_and f_un.f_un_complex
+#define f_or f_un.f_un_complex
+#define f_not f_un.f_un_complex
+#define f_list f_un.f_un_complex
+
+ Filter *f_next;
+};
+
+/* compare routines can return undefined */
+#define SLAPD_COMPARE_UNDEFINED ((ber_int_t) -1)
+
+struct ValuesReturnFilter {
+ ber_tag_t vrf_choice;
+
+ union vrf_un_u {
+ /* precomputed result */
+ ber_int_t vrf_un_result;
+
+ /* DN */
+ char *vrf_un_dn;
+
+ /* present */
+ AttributeDescription *vrf_un_desc;
+
+ /* simple value assertion */
+ AttributeAssertion *vrf_un_ava;
+
+ /* substring assertion */
+ SubstringsAssertion *vrf_un_ssa;
+
+ /* matching rule assertion */
+ MatchingRuleAssertion *vrf_un_mra;
+
+#define vrf_result vrf_un.vrf_un_result
+#define vrf_dn vrf_un.vrf_un_dn
+#define vrf_desc vrf_un.vrf_un_desc
+#define vrf_ava vrf_un.vrf_un_ava
+#define vrf_av_desc vrf_un.vrf_un_ava->aa_desc
+#define vrf_av_value vrf_un.vrf_un_ava->aa_value
+#define vrf_ssa vrf_un.vrf_un_ssa
+#define vrf_sub vrf_un.vrf_un_ssa
+#define vrf_sub_desc vrf_un.vrf_un_ssa->sa_desc
+#define vrf_sub_initial vrf_un.vrf_un_ssa->sa_initial
+#define vrf_sub_any vrf_un.vrf_un_ssa->sa_any
+#define vrf_sub_final vrf_un.vrf_un_ssa->sa_final
+#define vrf_mra vrf_un.vrf_un_mra
+#define vrf_mr_rule vrf_un.vrf_un_mra->ma_rule
+#define vrf_mr_rule_text vrf_un.vrf_un_mra->ma_rule_text
+#define vrf_mr_desc vrf_un.vrf_un_mra->ma_desc
+#define vrf_mr_value vrf_un.vrf_un_mra->ma_value
+#define vrf_mr_dnattrs vrf_un.vrf_un_mra->ma_dnattrs
+
+
+ } vrf_un;
+
+ ValuesReturnFilter *vrf_next;
+};
+
+/*
+ * represents an attribute (description + values)
+ * desc, vals, nvals, numvals fields must align with Modification
+ */
+struct Attribute {
+ AttributeDescription *a_desc;
+ BerVarray a_vals; /* preserved values */
+ BerVarray a_nvals; /* normalized values */
+ unsigned a_numvals; /* number of vals */
+ unsigned a_flags;
+#define SLAP_ATTR_IXADD 0x1U
+#define SLAP_ATTR_IXDEL 0x2U
+#define SLAP_ATTR_DONT_FREE_DATA 0x4U
+#define SLAP_ATTR_DONT_FREE_VALS 0x8U
+#define SLAP_ATTR_SORTED_VALS 0x10U /* values are sorted */
+#define SLAP_ATTR_BIG_MULTI 0x20U /* for backends */
+
+/* These flags persist across an attr_dup() */
+#define SLAP_ATTR_PERSISTENT_FLAGS \
+ (SLAP_ATTR_SORTED_VALS|SLAP_ATTR_BIG_MULTI)
+
+ Attribute *a_next;
+#ifdef LDAP_COMP_MATCH
+ ComponentData *a_comp_data; /* component values */
+#endif
+};
+
+
+/*
+ * the id used in the indexes to refer to an entry
+ */
+typedef unsigned long ID;
+#define NOID ((ID)~0)
+
+typedef struct EntryHeader {
+ struct berval bv;
+ char *data;
+ int nattrs;
+ int nvals;
+} EntryHeader;
+
+/*
+ * represents an entry in core
+ */
+struct Entry {
+ /*
+ * The ID field should only be changed before entry is
+ * inserted into a cache. The ID value is backend
+ * specific.
+ */
+ ID e_id;
+
+ struct berval e_name; /* name (DN) of this entry */
+ struct berval e_nname; /* normalized name (DN) of this entry */
+
+ /* for migration purposes */
+#define e_dn e_name.bv_val
+#define e_ndn e_nname.bv_val
+
+ Attribute *e_attrs; /* list of attributes + values */
+
+ slap_mask_t e_ocflags;
+
+ struct berval e_bv; /* For entry_encode/entry_decode */
+
+ /* for use by the backend for any purpose */
+ void* e_private;
+};
+
+/*
+ * A list of LDAPMods
+ * desc, values, nvalues, numvals must align with Attribute
+ */
+struct Modification {
+ AttributeDescription *sm_desc;
+ BerVarray sm_values;
+ BerVarray sm_nvalues;
+ unsigned sm_numvals;
+ short sm_op;
+ short sm_flags;
+/* Set for internal mods, will bypass ACL checks. Only needed when
+ * running as non-root user, for user modifiable attributes.
+ */
+#define SLAP_MOD_INTERNAL 0x01
+#define SLAP_MOD_MANAGING 0x02
+ struct berval sm_type;
+};
+
+struct Modifications {
+ Modification sml_mod;
+#define sml_op sml_mod.sm_op
+#define sml_flags sml_mod.sm_flags
+#define sml_desc sml_mod.sm_desc
+#define sml_type sml_mod.sm_type
+#define sml_values sml_mod.sm_values
+#define sml_nvalues sml_mod.sm_nvalues
+#define sml_numvals sml_mod.sm_numvals
+ Modifications *sml_next;
+};
+
+/*
+ * represents an access control list
+ */
+typedef enum slap_access_t {
+ ACL_INVALID_ACCESS = -1,
+ ACL_NONE = 0,
+ ACL_DISCLOSE,
+ ACL_AUTH,
+ ACL_COMPARE,
+ ACL_SEARCH,
+ ACL_READ,
+ ACL_WRITE_,
+ ACL_MANAGE,
+
+ /* always leave at end of levels but not greater than ACL_LEVEL_MASK */
+ ACL_LAST,
+
+ /* ACL level mask and modifiers */
+ ACL_LEVEL_MASK = 0x000f,
+ ACL_QUALIFIER1 = 0x0100,
+ ACL_QUALIFIER2 = 0x0200,
+ ACL_QUALIFIER3 = 0x0400,
+ ACL_QUALIFIER4 = 0x0800,
+ ACL_QUALIFIER_MASK = 0x0f00,
+
+ /* write granularity */
+ ACL_WADD = ACL_WRITE_|ACL_QUALIFIER1,
+ ACL_WDEL = ACL_WRITE_|ACL_QUALIFIER2,
+
+ ACL_WRITE = ACL_WADD|ACL_WDEL
+} slap_access_t;
+
+typedef enum slap_control_e {
+ ACL_INVALID_CONTROL = 0,
+ ACL_STOP,
+ ACL_CONTINUE,
+ ACL_BREAK
+} slap_control_t;
+
+typedef enum slap_style_e {
+ ACL_STYLE_REGEX = 0,
+ ACL_STYLE_EXPAND,
+ ACL_STYLE_BASE,
+ ACL_STYLE_ONE,
+ ACL_STYLE_SUBTREE,
+ ACL_STYLE_CHILDREN,
+ ACL_STYLE_LEVEL,
+ ACL_STYLE_ATTROF,
+ ACL_STYLE_ANONYMOUS,
+ ACL_STYLE_USERS,
+ ACL_STYLE_SELF,
+ ACL_STYLE_IP,
+ ACL_STYLE_IPV6,
+ ACL_STYLE_PATH,
+
+ ACL_STYLE_NONE
+} slap_style_t;
+
+typedef struct AuthorizationInformation {
+ ber_tag_t sai_method; /* LDAP_AUTH_* from <ldap.h> */
+ struct berval sai_mech; /* SASL Mechanism */
+ struct berval sai_dn; /* DN for reporting purposes */
+ struct berval sai_ndn; /* Normalized DN */
+
+ /* Security Strength Factors */
+ slap_ssf_t sai_ssf; /* Overall SSF */
+ slap_ssf_t sai_transport_ssf; /* Transport SSF */
+ slap_ssf_t sai_tls_ssf; /* TLS SSF */
+ slap_ssf_t sai_sasl_ssf; /* SASL SSF */
+} AuthorizationInformation;
+
+#ifdef SLAP_DYNACL
+
+/*
+ * "dynamic" ACL infrastructure (for ACIs and more)
+ */
+typedef int (slap_dynacl_parse) LDAP_P(( const char *fname, int lineno,
+ const char *opts, slap_style_t, const char *, void **privp ));
+typedef int (slap_dynacl_unparse) LDAP_P(( void *priv, struct berval *bv ));
+typedef int (slap_dynacl_mask) LDAP_P((
+ void *priv,
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ int nmatch,
+ regmatch_t *matches,
+ slap_access_t *grant,
+ slap_access_t *deny ));
+typedef int (slap_dynacl_destroy) LDAP_P(( void *priv ));
+
+typedef struct slap_dynacl_t {
+ char *da_name;
+ slap_dynacl_parse *da_parse;
+ slap_dynacl_unparse *da_unparse;
+ slap_dynacl_mask *da_mask;
+ slap_dynacl_destroy *da_destroy;
+
+ void *da_private;
+ struct slap_dynacl_t *da_next;
+} slap_dynacl_t;
+#endif /* SLAP_DYNACL */
+
+/* the DN portion of the "by" part */
+typedef struct slap_dn_access {
+ /* DN pattern */
+ AuthorizationInformation a_dnauthz;
+#define a_pat a_dnauthz.sai_dn
+
+ slap_style_t a_style;
+ int a_level;
+ int a_self_level;
+ AttributeDescription *a_at;
+ int a_self;
+ int a_expand;
+} slap_dn_access;
+
+/* the "by" part */
+typedef struct Access {
+ slap_control_t a_type;
+
+/* strip qualifiers */
+#define ACL_LEVEL(p) ((p) & ACL_LEVEL_MASK)
+#define ACL_QUALIFIERS(p) ((p) & ~ACL_LEVEL_MASK)
+
+#define ACL_ACCESS2PRIV(access) ((0x01U << ACL_LEVEL((access))) | ACL_QUALIFIERS((access)))
+
+#define ACL_PRIV_NONE ACL_ACCESS2PRIV( ACL_NONE )
+#define ACL_PRIV_DISCLOSE ACL_ACCESS2PRIV( ACL_DISCLOSE )
+#define ACL_PRIV_AUTH ACL_ACCESS2PRIV( ACL_AUTH )
+#define ACL_PRIV_COMPARE ACL_ACCESS2PRIV( ACL_COMPARE )
+#define ACL_PRIV_SEARCH ACL_ACCESS2PRIV( ACL_SEARCH )
+#define ACL_PRIV_READ ACL_ACCESS2PRIV( ACL_READ )
+#define ACL_PRIV_WADD ACL_ACCESS2PRIV( ACL_WADD )
+#define ACL_PRIV_WDEL ACL_ACCESS2PRIV( ACL_WDEL )
+#define ACL_PRIV_WRITE ( ACL_PRIV_WADD | ACL_PRIV_WDEL )
+#define ACL_PRIV_MANAGE ACL_ACCESS2PRIV( ACL_MANAGE )
+
+/* NOTE: always use the highest level; current: 0x00ffUL */
+#define ACL_PRIV_MASK ((ACL_ACCESS2PRIV(ACL_LAST) - 1) | ACL_QUALIFIER_MASK)
+
+/* priv flags */
+#define ACL_PRIV_LEVEL 0x1000UL
+#define ACL_PRIV_ADDITIVE 0x2000UL
+#define ACL_PRIV_SUBSTRACTIVE 0x4000UL
+
+/* invalid privs */
+#define ACL_PRIV_INVALID 0x0UL
+
+#define ACL_PRIV_ISSET(m,p) (((m) & (p)) == (p))
+#define ACL_PRIV_ASSIGN(m,p) do { (m) = (p); } while(0)
+#define ACL_PRIV_SET(m,p) do { (m) |= (p); } while(0)
+#define ACL_PRIV_CLR(m,p) do { (m) &= ~(p); } while(0)
+
+#define ACL_INIT(m) ACL_PRIV_ASSIGN((m), ACL_PRIV_NONE)
+#define ACL_INVALIDATE(m) ACL_PRIV_ASSIGN((m), ACL_PRIV_INVALID)
+
+#define ACL_GRANT(m,a) ACL_PRIV_ISSET((m),ACL_ACCESS2PRIV(a))
+
+#define ACL_IS_INVALID(m) ((m) == ACL_PRIV_INVALID)
+
+#define ACL_IS_LEVEL(m) ACL_PRIV_ISSET((m),ACL_PRIV_LEVEL)
+#define ACL_IS_ADDITIVE(m) ACL_PRIV_ISSET((m),ACL_PRIV_ADDITIVE)
+#define ACL_IS_SUBTRACTIVE(m) ACL_PRIV_ISSET((m),ACL_PRIV_SUBSTRACTIVE)
+
+#define ACL_LVL_NONE (ACL_PRIV_NONE|ACL_PRIV_LEVEL)
+#define ACL_LVL_DISCLOSE (ACL_PRIV_DISCLOSE|ACL_LVL_NONE)
+#define ACL_LVL_AUTH (ACL_PRIV_AUTH|ACL_LVL_DISCLOSE)
+#define ACL_LVL_COMPARE (ACL_PRIV_COMPARE|ACL_LVL_AUTH)
+#define ACL_LVL_SEARCH (ACL_PRIV_SEARCH|ACL_LVL_COMPARE)
+#define ACL_LVL_READ (ACL_PRIV_READ|ACL_LVL_SEARCH)
+#define ACL_LVL_WADD (ACL_PRIV_WADD|ACL_LVL_READ)
+#define ACL_LVL_WDEL (ACL_PRIV_WDEL|ACL_LVL_READ)
+#define ACL_LVL_WRITE (ACL_PRIV_WRITE|ACL_LVL_READ)
+#define ACL_LVL_MANAGE (ACL_PRIV_MANAGE|ACL_LVL_WRITE)
+
+#define ACL_LVL(m,l) (((m)&ACL_PRIV_MASK) == ((l)&ACL_PRIV_MASK))
+#define ACL_LVL_IS_NONE(m) ACL_LVL((m),ACL_LVL_NONE)
+#define ACL_LVL_IS_DISCLOSE(m) ACL_LVL((m),ACL_LVL_DISCLOSE)
+#define ACL_LVL_IS_AUTH(m) ACL_LVL((m),ACL_LVL_AUTH)
+#define ACL_LVL_IS_COMPARE(m) ACL_LVL((m),ACL_LVL_COMPARE)
+#define ACL_LVL_IS_SEARCH(m) ACL_LVL((m),ACL_LVL_SEARCH)
+#define ACL_LVL_IS_READ(m) ACL_LVL((m),ACL_LVL_READ)
+#define ACL_LVL_IS_WADD(m) ACL_LVL((m),ACL_LVL_WADD)
+#define ACL_LVL_IS_WDEL(m) ACL_LVL((m),ACL_LVL_WDEL)
+#define ACL_LVL_IS_WRITE(m) ACL_LVL((m),ACL_LVL_WRITE)
+#define ACL_LVL_IS_MANAGE(m) ACL_LVL((m),ACL_LVL_MANAGE)
+
+#define ACL_LVL_ASSIGN_NONE(m) ACL_PRIV_ASSIGN((m),ACL_LVL_NONE)
+#define ACL_LVL_ASSIGN_DISCLOSE(m) ACL_PRIV_ASSIGN((m),ACL_LVL_DISCLOSE)
+#define ACL_LVL_ASSIGN_AUTH(m) ACL_PRIV_ASSIGN((m),ACL_LVL_AUTH)
+#define ACL_LVL_ASSIGN_COMPARE(m) ACL_PRIV_ASSIGN((m),ACL_LVL_COMPARE)
+#define ACL_LVL_ASSIGN_SEARCH(m) ACL_PRIV_ASSIGN((m),ACL_LVL_SEARCH)
+#define ACL_LVL_ASSIGN_READ(m) ACL_PRIV_ASSIGN((m),ACL_LVL_READ)
+#define ACL_LVL_ASSIGN_WADD(m) ACL_PRIV_ASSIGN((m),ACL_LVL_WADD)
+#define ACL_LVL_ASSIGN_WDEL(m) ACL_PRIV_ASSIGN((m),ACL_LVL_WDEL)
+#define ACL_LVL_ASSIGN_WRITE(m) ACL_PRIV_ASSIGN((m),ACL_LVL_WRITE)
+#define ACL_LVL_ASSIGN_MANAGE(m) ACL_PRIV_ASSIGN((m),ACL_LVL_MANAGE)
+
+ slap_mask_t a_access_mask;
+
+ /* DN pattern */
+ slap_dn_access a_dn;
+#define a_dn_pat a_dn.a_dnauthz.sai_dn
+#define a_dn_at a_dn.a_at
+#define a_dn_self a_dn.a_self
+
+ /* real DN pattern */
+ slap_dn_access a_realdn;
+#define a_realdn_pat a_realdn.a_dnauthz.sai_dn
+#define a_realdn_at a_realdn.a_at
+#define a_realdn_self a_realdn.a_self
+
+ /* used for ssf stuff
+ * NOTE: the ssf stuff in a_realdn is ignored */
+#define a_authz a_dn.a_dnauthz
+
+ /* connection related stuff */
+ slap_style_t a_peername_style;
+ struct berval a_peername_pat;
+#ifdef LDAP_PF_INET6
+ union {
+ struct in6_addr ax6;
+ unsigned long ax;
+ } ax_peername_addr,
+ ax_peername_mask;
+#define a_peername_addr6 ax_peername_addr.ax6
+#define a_peername_addr ax_peername_addr.ax
+#define a_peername_mask6 ax_peername_mask.ax6
+#define a_peername_mask ax_peername_mask.ax
+/* apparently, only s6_addr is portable;
+ * define a portable address mask comparison */
+#define slap_addr6_mask(val, msk, asr) ( \
+ (((val)->s6_addr[0] & (msk)->s6_addr[0]) == (asr)->s6_addr[0]) \
+ && (((val)->s6_addr[1] & (msk)->s6_addr[1]) == (asr)->s6_addr[1]) \
+ && (((val)->s6_addr[2] & (msk)->s6_addr[2]) == (asr)->s6_addr[2]) \
+ && (((val)->s6_addr[3] & (msk)->s6_addr[3]) == (asr)->s6_addr[3]) \
+ && (((val)->s6_addr[4] & (msk)->s6_addr[4]) == (asr)->s6_addr[4]) \
+ && (((val)->s6_addr[5] & (msk)->s6_addr[5]) == (asr)->s6_addr[5]) \
+ && (((val)->s6_addr[6] & (msk)->s6_addr[6]) == (asr)->s6_addr[6]) \
+ && (((val)->s6_addr[7] & (msk)->s6_addr[7]) == (asr)->s6_addr[7]) \
+ && (((val)->s6_addr[8] & (msk)->s6_addr[8]) == (asr)->s6_addr[8]) \
+ && (((val)->s6_addr[9] & (msk)->s6_addr[9]) == (asr)->s6_addr[9]) \
+ && (((val)->s6_addr[10] & (msk)->s6_addr[10]) == (asr)->s6_addr[10]) \
+ && (((val)->s6_addr[11] & (msk)->s6_addr[11]) == (asr)->s6_addr[11]) \
+ && (((val)->s6_addr[12] & (msk)->s6_addr[12]) == (asr)->s6_addr[12]) \
+ && (((val)->s6_addr[13] & (msk)->s6_addr[13]) == (asr)->s6_addr[13]) \
+ && (((val)->s6_addr[14] & (msk)->s6_addr[14]) == (asr)->s6_addr[14]) \
+ && (((val)->s6_addr[15] & (msk)->s6_addr[15]) == (asr)->s6_addr[15]) \
+ )
+#else /* ! LDAP_PF_INET6 */
+ unsigned long a_peername_addr,
+ a_peername_mask;
+#endif /* ! LDAP_PF_INET6 */
+ int a_peername_port;
+
+ slap_style_t a_sockname_style;
+ struct berval a_sockname_pat;
+
+ slap_style_t a_domain_style;
+ struct berval a_domain_pat;
+ int a_domain_expand;
+
+ slap_style_t a_sockurl_style;
+ struct berval a_sockurl_pat;
+ slap_style_t a_set_style;
+ struct berval a_set_pat;
+
+#ifdef SLAP_DYNACL
+ slap_dynacl_t *a_dynacl;
+#endif /* SLAP_DYNACL */
+
+ /* ACL Groups */
+ slap_style_t a_group_style;
+ struct berval a_group_pat;
+ ObjectClass *a_group_oc;
+ AttributeDescription *a_group_at;
+
+ struct Access *a_next;
+} Access;
+
+/* the "to" part */
+typedef struct AccessControl {
+ /* "to" part: the entries this acl applies to */
+ Filter *acl_filter;
+ slap_style_t acl_dn_style;
+ regex_t acl_dn_re;
+ struct berval acl_dn_pat;
+ AttributeName *acl_attrs;
+ MatchingRule *acl_attrval_mr;
+ slap_style_t acl_attrval_style;
+ regex_t acl_attrval_re;
+ struct berval acl_attrval;
+
+ /* "by" part: list of who has what access to the entries */
+ Access *acl_access;
+
+ struct AccessControl *acl_next;
+} AccessControl;
+
+typedef struct AccessControlState {
+ /* Access state */
+
+ /* The stored state is valid when requesting as_access access
+ * to the as_desc attributes. */
+ AttributeDescription *as_desc;
+ slap_access_t as_access;
+
+ /* Value dependent acl where processing can restart */
+ AccessControl *as_vd_acl;
+ int as_vd_acl_present;
+ int as_vd_acl_count;
+ slap_mask_t as_vd_mask;
+
+ /* The cached result after evaluating a value independent attr.
+ * Only valid when != -1 and as_vd_acl == NULL */
+ int as_result;
+
+ /* True if started to process frontend ACLs */
+ int as_fe_done;
+} AccessControlState;
+#define ACL_STATE_INIT { NULL, ACL_NONE, NULL, 0, 0, ACL_PRIV_NONE, -1, 0 }
+
+typedef struct AclRegexMatches {
+ int dn_count;
+ regmatch_t dn_data[MAXREMATCHES];
+ int val_count;
+ regmatch_t val_data[MAXREMATCHES];
+} AclRegexMatches;
+
+/*
+ * Backend-info
+ * represents a backend
+ */
+
+typedef LDAP_STAILQ_HEAD(BeI, BackendInfo) slap_bi_head;
+typedef LDAP_STAILQ_HEAD(BeDB, BackendDB) slap_be_head;
+
+LDAP_SLAPD_V (int) nBackendInfo;
+LDAP_SLAPD_V (int) nBackendDB;
+LDAP_SLAPD_V (slap_bi_head) backendInfo;
+LDAP_SLAPD_V (slap_be_head) backendDB;
+LDAP_SLAPD_V (BackendDB *) frontendDB;
+
+LDAP_SLAPD_V (int) slapMode;
+#define SLAP_UNDEFINED_MODE 0x0000
+#define SLAP_SERVER_MODE 0x0001
+#define SLAP_TOOL_MODE 0x0002
+#define SLAP_MODE 0x0003
+
+#define SLAP_TRUNCATE_MODE 0x0100
+#define SLAP_TOOL_READMAIN 0x0200
+#define SLAP_TOOL_READONLY 0x0400
+#define SLAP_TOOL_QUICK 0x0800
+#define SLAP_TOOL_NO_SCHEMA_CHECK 0x1000
+#define SLAP_TOOL_VALUE_CHECK 0x2000
+
+#define SLAP_SERVER_RUNNING 0x8000
+
+#define SB_TLS_DEFAULT (-1)
+#define SB_TLS_OFF 0
+#define SB_TLS_ON 1
+#define SB_TLS_CRITICAL 2
+
+enum slaptool {
+ SLAPADD=1, /* LDIF -> database tool */
+ SLAPCAT, /* database -> LDIF tool */
+ SLAPDN, /* DN check w/ syntax tool */
+ SLAPINDEX, /* database index tool */
+ SLAPMODIFY, /* database modify tool */
+ SLAPPASSWD, /* password generation tool */
+ SLAPSCHEMA, /* schema checking tool */
+ SLAPTEST, /* slapd.conf test tool */
+ SLAPAUTH, /* test authz-regexp and authc/authz stuff */
+ SLAPACL, /* test acl */
+ SLAPLAST
+};
+
+LDAP_SLAPD_V(enum slaptool) slapTool;
+
+typedef struct slap_keepalive {
+ int sk_idle;
+ int sk_probes;
+ int sk_interval;
+} slap_keepalive;
+
+typedef struct slap_bindconf {
+ struct berval sb_uri;
+ int sb_version;
+ int sb_tls;
+ int sb_method;
+ int sb_timeout_api;
+ int sb_timeout_net;
+ struct berval sb_binddn;
+ struct berval sb_cred;
+ struct berval sb_saslmech;
+ char *sb_secprops;
+ struct berval sb_realm;
+ struct berval sb_authcId;
+ struct berval sb_authzId;
+ slap_keepalive sb_keepalive;
+ unsigned int sb_tcp_user_timeout;
+#ifdef HAVE_TLS
+ void *sb_tls_ctx;
+ char *sb_tls_cert;
+ char *sb_tls_key;
+ char *sb_tls_cacert;
+ char *sb_tls_cacertdir;
+ char *sb_tls_reqcert;
+ char *sb_tls_reqsan;
+ char *sb_tls_cipher_suite;
+ char *sb_tls_protocol_min;
+ char *sb_tls_ecname;
+#ifdef HAVE_OPENSSL
+ char *sb_tls_crlcheck;
+#endif
+ int sb_tls_int_reqcert;
+ int sb_tls_int_reqsan;
+ int sb_tls_do_init;
+#endif
+} slap_bindconf;
+
+typedef struct slap_verbmasks {
+ struct berval word;
+ const slap_mask_t mask;
+} slap_verbmasks;
+
+typedef struct slap_cf_aux_table {
+ struct berval key;
+ int off;
+ char type;
+ char quote;
+ void *aux;
+} slap_cf_aux_table;
+
+typedef int
+slap_cf_aux_table_parse_x LDAP_P((
+ struct berval *val,
+ void *bc,
+ slap_cf_aux_table *tab0,
+ const char *tabmsg,
+ int unparse ));
+
+#define SLAP_LIMIT_TIME 1
+#define SLAP_LIMIT_SIZE 2
+
+struct slap_limits_set {
+ /* time limits */
+ int lms_t_soft;
+ int lms_t_hard;
+
+ /* size limits */
+ int lms_s_soft;
+ int lms_s_hard;
+ int lms_s_unchecked;
+ int lms_s_pr;
+ int lms_s_pr_hide;
+ int lms_s_pr_total;
+};
+
+/* Note: this is different from LDAP_NO_LIMIT (0); slapd internal use only */
+#define SLAP_NO_LIMIT -1
+#define SLAP_MAX_LIMIT 2147483647
+
+struct slap_limits {
+ unsigned lm_flags; /* type of pattern */
+ /* Values must match lmpats[] in limits.c */
+#define SLAP_LIMITS_UNDEFINED 0x0000U
+#define SLAP_LIMITS_EXACT 0x0001U
+#define SLAP_LIMITS_BASE SLAP_LIMITS_EXACT
+#define SLAP_LIMITS_ONE 0x0002U
+#define SLAP_LIMITS_SUBTREE 0x0003U
+#define SLAP_LIMITS_CHILDREN 0x0004U
+#define SLAP_LIMITS_REGEX 0x0005U
+#define SLAP_LIMITS_ANONYMOUS 0x0006U
+#define SLAP_LIMITS_USERS 0x0007U
+#define SLAP_LIMITS_ANY 0x0008U
+#define SLAP_LIMITS_MASK 0x000FU
+
+#define SLAP_LIMITS_TYPE_SELF 0x0000U
+#define SLAP_LIMITS_TYPE_DN SLAP_LIMITS_TYPE_SELF
+#define SLAP_LIMITS_TYPE_GROUP 0x0010U
+#define SLAP_LIMITS_TYPE_THIS 0x0020U
+#define SLAP_LIMITS_TYPE_MASK 0x00F0U
+
+ regex_t lm_regex; /* regex data for REGEX */
+
+ /*
+ * normalized DN for EXACT, BASE, ONE, SUBTREE, CHILDREN;
+ * pattern for REGEX; NULL for ANONYMOUS, USERS
+ */
+ struct berval lm_pat;
+
+ /* if lm_flags & SLAP_LIMITS_TYPE_MASK == SLAP_LIMITS_GROUP,
+ * lm_group_oc is objectClass and lm_group_at is attributeType
+ * of member in oc for match; then lm_flags & SLAP_LIMITS_MASK
+ * can only be SLAP_LIMITS_EXACT */
+ ObjectClass *lm_group_oc;
+ AttributeDescription *lm_group_ad;
+
+ struct slap_limits_set lm_limits;
+};
+
+/* temporary aliases */
+typedef BackendDB Backend;
+#define nbackends nBackendDB
+#define backends backendDB
+
+/*
+ * syncinfo structure for syncrepl
+ */
+
+struct syncinfo_s;
+
+#define SLAP_SYNC_RID_MAX 999
+#define SLAP_SYNC_SID_MAX 4095 /* based on liblutil/csn.c field width */
+
+/* fake conn connid constructed as rid; real connids start
+ * at SLAPD_SYNC_CONN_OFFSET */
+#define SLAPD_SYNC_SYNCCONN_OFFSET (SLAP_SYNC_RID_MAX + 1)
+#define SLAPD_SYNC_IS_SYNCCONN(connid) ((connid) < SLAPD_SYNC_SYNCCONN_OFFSET)
+#define SLAPD_SYNC_RID2SYNCCONN(rid) (rid)
+
+#define SLAP_SYNCUUID_SET_SIZE 256
+
+struct sync_cookie {
+ BerVarray ctxcsn;
+ int *sids;
+ int numcsns;
+ int rid;
+ struct berval octet_str;
+ struct berval delcsn;
+ int sid;
+ LDAP_STAILQ_ENTRY(sync_cookie) sc_next;
+};
+
+LDAP_STAILQ_HEAD( slap_sync_cookie_s, sync_cookie );
+
+/* Defs for pending_csn_list */
+LDAP_TAILQ_HEAD( be_pclh, slap_csn_entry );
+
+typedef struct be_pcsn {
+ struct be_pclh be_pcsn_list;
+ ldap_pvt_thread_mutex_t be_pcsn_mutex;
+} be_pcsn;
+
+#ifndef SLAP_MAX_CIDS
+#define SLAP_MAX_CIDS 32 /* Maximum number of supported controls */
+#endif
+
+struct ConfigOCs; /* slap-config.h */
+
+struct BackendDB {
+ BackendInfo *bd_info; /* pointer to shared backend info */
+ BackendDB *bd_self; /* pointer to this struct */
+
+ /* fields in this structure (and routines acting on this structure)
+ should be renamed from be_ to bd_ */
+
+ /* BackendInfo accessors */
+#define be_config bd_info->bi_db_config
+#define be_type bd_info->bi_type
+
+#define be_bind bd_info->bi_op_bind
+#define be_unbind bd_info->bi_op_unbind
+#define be_add bd_info->bi_op_add
+#define be_compare bd_info->bi_op_compare
+#define be_delete bd_info->bi_op_delete
+#define be_modify bd_info->bi_op_modify
+#define be_modrdn bd_info->bi_op_modrdn
+#define be_search bd_info->bi_op_search
+#define be_abandon bd_info->bi_op_abandon
+
+#define be_extended bd_info->bi_extended
+#define be_cancel bd_info->bi_op_cancel
+
+#define be_chk_referrals bd_info->bi_chk_referrals
+#define be_chk_controls bd_info->bi_chk_controls
+#define be_fetch bd_info->bi_entry_get_rw
+#define be_release bd_info->bi_entry_release_rw
+#define be_group bd_info->bi_acl_group
+#define be_attribute bd_info->bi_acl_attribute
+#define be_operational bd_info->bi_operational
+
+/*
+ * define to honor hasSubordinates operational attribute in search filters
+ */
+#define be_has_subordinates bd_info->bi_has_subordinates
+
+#define be_connection_init bd_info->bi_connection_init
+#define be_connection_destroy bd_info->bi_connection_destroy
+
+#ifdef SLAPD_TOOLS
+#define be_entry_open bd_info->bi_tool_entry_open
+#define be_entry_close bd_info->bi_tool_entry_close
+#define be_entry_first bd_info->bi_tool_entry_first
+#define be_entry_first_x bd_info->bi_tool_entry_first_x
+#define be_entry_next bd_info->bi_tool_entry_next
+#define be_entry_reindex bd_info->bi_tool_entry_reindex
+#define be_entry_get bd_info->bi_tool_entry_get
+#define be_entry_put bd_info->bi_tool_entry_put
+#define be_sync bd_info->bi_tool_sync
+#define be_dn2id_get bd_info->bi_tool_dn2id_get
+#define be_entry_modify bd_info->bi_tool_entry_modify
+#define be_entry_delete bd_info->bi_tool_entry_delete
+#endif
+
+ /* supported controls */
+ /* note: set to 0 if the database does not support the control;
+ * be_ctrls[SLAP_MAX_CIDS] is set to 1 if initialized */
+ char be_ctrls[SLAP_MAX_CIDS + 1];
+
+/* Database flags */
+#define SLAP_DBFLAG_NOLASTMOD 0x0001U
+#define SLAP_DBFLAG_NO_SCHEMA_CHECK 0x0002U
+#define SLAP_DBFLAG_HIDDEN 0x0004U
+#define SLAP_DBFLAG_ONE_SUFFIX 0x0008U
+#define SLAP_DBFLAG_GLUE_INSTANCE 0x0010U /* a glue backend */
+#define SLAP_DBFLAG_GLUE_SUBORDINATE 0x0020U /* child of a glue hierarchy */
+#define SLAP_DBFLAG_GLUE_LINKED 0x0040U /* child is connected to parent */
+#define SLAP_DBFLAG_GLUE_ADVERTISE 0x0080U /* advertise in rootDSE */
+#define SLAP_DBFLAG_OVERLAY 0x0100U /* this db struct is an overlay */
+#define SLAP_DBFLAG_GLOBAL_OVERLAY 0x0200U /* this db struct is a global overlay */
+#define SLAP_DBFLAG_DYNAMIC 0x0400U /* this db allows dynamicObjects */
+#define SLAP_DBFLAG_MONITORING 0x0800U /* custom monitoring enabled */
+#define SLAP_DBFLAG_SHADOW 0x8000U /* a shadow */
+#define SLAP_DBFLAG_SINGLE_SHADOW 0x4000U /* a single-provider shadow */
+#define SLAP_DBFLAG_SYNC_SHADOW 0x1000U /* a sync shadow */
+#define SLAP_DBFLAG_SLURP_SHADOW 0x2000U /* a slurp shadow */
+#define SLAP_DBFLAG_SHADOW_MASK (SLAP_DBFLAG_SHADOW|SLAP_DBFLAG_SINGLE_SHADOW|SLAP_DBFLAG_SYNC_SHADOW|SLAP_DBFLAG_SLURP_SHADOW)
+#define SLAP_DBFLAG_CLEAN 0x10000U /* was cleanly shutdown */
+#define SLAP_DBFLAG_ACL_ADD 0x20000U /* check attr ACLs on adds */
+#define SLAP_DBFLAG_SYNC_SUBENTRY 0x40000U /* use subentry for context */
+#define SLAP_DBFLAG_MULTI_SHADOW 0x80000U /* uses multi-provider */
+#define SLAP_DBFLAG_DISABLED 0x100000U
+#define SLAP_DBFLAG_LASTBIND 0x200000U
+#define SLAP_DBFLAG_OPEN 0x400000U /* db is currently open */
+ slap_mask_t be_flags;
+#define SLAP_DBFLAGS(be) ((be)->be_flags)
+#define SLAP_NOLASTMOD(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_NOLASTMOD)
+#define SLAP_LASTMOD(be) (!SLAP_NOLASTMOD(be))
+#define SLAP_LASTBIND(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_LASTBIND)
+#define SLAP_DBHIDDEN(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_HIDDEN)
+#define SLAP_DBDISABLED(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_DISABLED)
+#define SLAP_DB_ONE_SUFFIX(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_ONE_SUFFIX)
+#define SLAP_ISOVERLAY(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_OVERLAY)
+#define SLAP_ISGLOBALOVERLAY(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_GLOBAL_OVERLAY)
+#define SLAP_DBMONITORING(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_MONITORING)
+#define SLAP_NO_SCHEMA_CHECK(be) \
+ (SLAP_DBFLAGS(be) & SLAP_DBFLAG_NO_SCHEMA_CHECK)
+#define SLAP_GLUE_INSTANCE(be) \
+ (SLAP_DBFLAGS(be) & SLAP_DBFLAG_GLUE_INSTANCE)
+#define SLAP_GLUE_SUBORDINATE(be) \
+ (SLAP_DBFLAGS(be) & SLAP_DBFLAG_GLUE_SUBORDINATE)
+#define SLAP_GLUE_LINKED(be) \
+ (SLAP_DBFLAGS(be) & SLAP_DBFLAG_GLUE_LINKED)
+#define SLAP_GLUE_ADVERTISE(be) \
+ (SLAP_DBFLAGS(be) & SLAP_DBFLAG_GLUE_ADVERTISE)
+#define SLAP_SHADOW(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_SHADOW)
+#define SLAP_SYNC_SHADOW(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_SYNC_SHADOW)
+#define SLAP_SLURP_SHADOW(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_SLURP_SHADOW)
+#define SLAP_SINGLE_SHADOW(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_SINGLE_SHADOW)
+#define SLAP_MULTIPROVIDER(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_MULTI_SHADOW)
+#define SLAP_DBCLEAN(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_CLEAN)
+#define SLAP_DBOPEN(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_OPEN)
+#define SLAP_DBACL_ADD(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_ACL_ADD)
+#define SLAP_SYNC_SUBENTRY(be) (SLAP_DBFLAGS(be) & SLAP_DBFLAG_SYNC_SUBENTRY)
+
+ slap_mask_t be_restrictops; /* restriction operations */
+#define SLAP_RESTRICT_OP_ADD 0x0001U
+#define SLAP_RESTRICT_OP_BIND 0x0002U
+#define SLAP_RESTRICT_OP_COMPARE 0x0004U
+#define SLAP_RESTRICT_OP_DELETE 0x0008U
+#define SLAP_RESTRICT_OP_EXTENDED 0x0010U
+#define SLAP_RESTRICT_OP_MODIFY 0x0020U
+#define SLAP_RESTRICT_OP_RENAME 0x0040U
+#define SLAP_RESTRICT_OP_SEARCH 0x0080U
+#define SLAP_RESTRICT_OP_MASK 0x00FFU
+
+#define SLAP_RESTRICT_READONLY 0x80000000U
+
+#define SLAP_RESTRICT_EXOP_START_TLS 0x0100U
+#define SLAP_RESTRICT_EXOP_MODIFY_PASSWD 0x0200U
+#define SLAP_RESTRICT_EXOP_WHOAMI 0x0400U
+#define SLAP_RESTRICT_EXOP_CANCEL 0x0800U
+#define SLAP_RESTRICT_EXOP_MASK 0xFF00U
+
+#define SLAP_RESTRICT_OP_READS \
+ ( SLAP_RESTRICT_OP_COMPARE \
+ | SLAP_RESTRICT_OP_SEARCH )
+#define SLAP_RESTRICT_OP_WRITES \
+ ( SLAP_RESTRICT_OP_ADD \
+ | SLAP_RESTRICT_OP_DELETE \
+ | SLAP_RESTRICT_OP_MODIFY \
+ | SLAP_RESTRICT_OP_RENAME )
+#define SLAP_RESTRICT_OP_ALL \
+ ( SLAP_RESTRICT_OP_READS \
+ | SLAP_RESTRICT_OP_WRITES \
+ | SLAP_RESTRICT_OP_BIND \
+ | SLAP_RESTRICT_OP_EXTENDED )
+
+#define SLAP_ALLOW_BIND_V2 0x0001U /* LDAPv2 bind */
+#define SLAP_ALLOW_BIND_ANON_CRED 0x0002U /* cred should be empty */
+#define SLAP_ALLOW_BIND_ANON_DN 0x0004U /* dn should be empty */
+
+#define SLAP_ALLOW_UPDATE_ANON 0x0008U /* allow anonymous updates */
+#define SLAP_ALLOW_PROXY_AUTHZ_ANON 0x0010U /* allow anonymous proxyAuthz */
+
+#define SLAP_DISALLOW_BIND_ANON 0x0001U /* no anonymous */
+#define SLAP_DISALLOW_BIND_SIMPLE 0x0002U /* simple authentication */
+
+#define SLAP_DISALLOW_TLS_2_ANON 0x0010U /* StartTLS -> Anonymous */
+#define SLAP_DISALLOW_TLS_AUTHC 0x0020U /* TLS while authenticated */
+
+#define SLAP_DISALLOW_PROXY_AUTHZ_N_CRIT 0x0100U
+#define SLAP_DISALLOW_DONTUSECOPY_N_CRIT 0x0200U
+
+#define SLAP_DISALLOW_AUX_WO_CR 0x4000U
+
+ slap_mask_t be_requires; /* pre-operation requirements */
+#define SLAP_REQUIRE_BIND 0x0001U /* bind before op */
+#define SLAP_REQUIRE_LDAP_V3 0x0002U /* LDAPv3 before op */
+#define SLAP_REQUIRE_AUTHC 0x0004U /* authentication before op */
+#define SLAP_REQUIRE_SASL 0x0008U /* SASL before op */
+#define SLAP_REQUIRE_STRONG 0x0010U /* strong authentication before op */
+
+ /* Required Security Strength Factor */
+ slap_ssf_set_t be_ssf_set;
+
+ BerVarray be_suffix; /* the DN suffixes of data in this backend */
+ BerVarray be_nsuffix; /* the normalized DN suffixes in this backend */
+ struct berval be_schemadn; /* per-backend subschema subentry DN */
+ struct berval be_schemandn; /* normalized subschema DN */
+ struct berval be_rootdn; /* the magic "root" name (DN) for this db */
+ struct berval be_rootndn; /* the magic "root" normalized name (DN) for this db */
+ struct berval be_rootpw; /* the magic "root" password for this db */
+ unsigned int be_max_deref_depth; /* limit for depth of an alias deref */
+#define be_sizelimit be_def_limit.lms_s_soft
+#define be_timelimit be_def_limit.lms_t_soft
+ struct slap_limits_set be_def_limit; /* default limits */
+ struct slap_limits **be_limits; /* regex-based size and time limits */
+ AccessControl *be_acl; /* access control list for this backend */
+ slap_access_t be_dfltaccess; /* access given if no acl matches */
+ AttributeName *be_extra_anlist; /* attributes that need to be added to search requests (ITS#6513) */
+
+ /* Consumer Information */
+ struct berval be_update_ndn; /* allowed to make changes (in replicas) */
+ BerVarray be_update_refs; /* where to refer modifying clients to */
+ be_pcsn be_pcsn_st; /* be_pending_csn_list now inside this */
+ be_pcsn *be_pcsn_p;
+ struct syncinfo_s *be_syncinfo; /* For syncrepl */
+
+ void *be_pb; /* Netscape plugin */
+ struct ConfigOCs *be_cf_ocs;
+
+ void *be_private; /* anything the backend database needs */
+ LDAP_STAILQ_ENTRY(BackendDB) be_next;
+};
+
+/* Backend function typedefs */
+typedef int (BI_bi_func) LDAP_P((BackendInfo *bi));
+typedef BI_bi_func BI_init;
+typedef BI_bi_func BI_open;
+typedef BI_bi_func BI_pause;
+typedef BI_bi_func BI_unpause;
+typedef BI_bi_func BI_close;
+typedef BI_bi_func BI_destroy;
+typedef int (BI_config) LDAP_P((BackendInfo *bi,
+ const char *fname, int lineno,
+ int argc, char **argv));
+
+typedef struct config_reply_s ConfigReply; /* slap-config.h */
+typedef int (BI_db_func) LDAP_P((Backend *bd, ConfigReply *cr));
+typedef BI_db_func BI_db_init;
+typedef BI_db_func BI_db_open;
+typedef BI_db_func BI_db_close;
+typedef BI_db_func BI_db_destroy;
+typedef int (BI_db_config) LDAP_P((Backend *bd,
+ const char *fname, int lineno,
+ int argc, char **argv));
+
+typedef struct req_bind_s {
+ int rb_method;
+ struct berval rb_cred;
+ struct berval rb_edn;
+ slap_ssf_t rb_ssf;
+ struct berval rb_mech;
+} req_bind_s;
+
+typedef struct req_search_s {
+ int rs_scope;
+ int rs_deref;
+ int rs_slimit;
+ int rs_tlimit;
+ /* NULL means be_isroot evaluated to TRUE */
+ struct slap_limits_set *rs_limit;
+ int rs_attrsonly;
+ AttributeName *rs_attrs;
+ Filter *rs_filter;
+ struct berval rs_filterstr;
+} req_search_s;
+
+typedef struct req_compare_s {
+ AttributeAssertion *rs_ava;
+} req_compare_s;
+
+typedef struct req_modifications_s {
+ Modifications *rs_modlist;
+ char rs_no_opattrs; /* don't att modify operational attrs */
+} req_modifications_s;
+
+typedef struct req_modify_s {
+ req_modifications_s rs_mods; /* NOTE: must be first in req_modify_s & req_modrdn_s */
+ int rs_increment;
+} req_modify_s;
+
+typedef struct req_modrdn_s {
+ req_modifications_s rs_mods; /* NOTE: must be first in req_modify_s & req_modrdn_s */
+ int rs_deleteoldrdn;
+ struct berval rs_newrdn;
+ struct berval rs_nnewrdn;
+ struct berval *rs_newSup;
+ struct berval *rs_nnewSup;
+} req_modrdn_s;
+
+typedef struct req_add_s {
+ Modifications *rs_modlist;
+ Entry *rs_e;
+} req_add_s;
+
+typedef struct req_abandon_s {
+ ber_int_t rs_msgid;
+} req_abandon_s;
+
+#ifdef SLAP_SCHEMA_EXPOSE
+#define SLAP_EXOP_HIDE 0x0000
+#else
+#define SLAP_EXOP_HIDE 0x8000
+#endif
+#define SLAP_EXOP_WRITES 0x0001 /* Exop does writes */
+
+typedef struct req_extended_s {
+ struct berval rs_reqoid;
+ int rs_flags;
+ struct berval *rs_reqdata;
+} req_extended_s;
+
+typedef struct req_pwdexop_s {
+ struct req_extended_s rs_extended;
+ struct berval rs_old;
+ struct berval rs_new;
+ Modifications *rs_mods;
+ Modifications **rs_modtail;
+} req_pwdexop_s;
+
+typedef enum slap_reply_e {
+ REP_RESULT,
+ REP_SASL,
+ REP_EXTENDED,
+ REP_SEARCH,
+ REP_SEARCHREF,
+ REP_INTERMEDIATE,
+ REP_GLUE_RESULT
+} slap_reply_t;
+
+typedef struct rep_sasl_s {
+ struct berval *r_sasldata;
+} rep_sasl_s;
+
+typedef struct rep_extended_s {
+ const char *r_rspoid;
+ struct berval *r_rspdata;
+} rep_extended_s;
+
+typedef struct rep_search_s {
+ Entry *r_entry;
+ slap_mask_t r_attr_flags;
+#define SLAP_ATTRS_UNDEFINED (0x00U)
+#define SLAP_OPATTRS_NO (0x01U)
+#define SLAP_OPATTRS_YES (0x02U)
+#define SLAP_USERATTRS_NO (0x10U)
+#define SLAP_USERATTRS_YES (0x20U)
+#define SLAP_OPATTRS_MASK(f) ((f) & (SLAP_OPATTRS_NO|SLAP_OPATTRS_YES))
+#define SLAP_OPATTRS(f) (((f) & SLAP_OPATTRS_YES) == SLAP_OPATTRS_YES)
+#define SLAP_USERATTRS_MASK(f) ((f) & (SLAP_USERATTRS_NO|SLAP_USERATTRS_YES))
+#define SLAP_USERATTRS(f) \
+ (((f) & SLAP_USERATTRS_YES) == SLAP_USERATTRS_YES)
+
+ Attribute *r_operational_attrs;
+ AttributeName *r_attrs;
+ int r_nentries;
+ BerVarray r_v2ref;
+} rep_search_s;
+
+struct SlapReply {
+ slap_reply_t sr_type;
+ ber_tag_t sr_tag;
+ ber_int_t sr_msgid;
+ ber_int_t sr_err;
+ const char *sr_matched;
+ const char *sr_text;
+ BerVarray sr_ref;
+ LDAPControl **sr_ctrls;
+ union sr_u {
+ rep_search_s sru_search;
+ rep_sasl_s sru_sasl;
+ rep_extended_s sru_extended;
+ } sr_un;
+ slap_mask_t sr_flags;
+#define REP_ENTRY_MODIFIABLE ((slap_mask_t) 0x0001U)
+#define REP_ENTRY_MUSTBEFREED ((slap_mask_t) 0x0002U)
+#define REP_ENTRY_MUSTRELEASE ((slap_mask_t) 0x0004U)
+#define REP_ENTRY_MASK (REP_ENTRY_MODIFIABLE|REP_ENTRY_MUSTFLUSH)
+#define REP_ENTRY_MUSTFLUSH (REP_ENTRY_MUSTBEFREED|REP_ENTRY_MUSTRELEASE)
+
+#define REP_MATCHED_MUSTBEFREED ((slap_mask_t) 0x0010U)
+#define REP_MATCHED_MASK (REP_MATCHED_MUSTBEFREED)
+
+#define REP_REF_MUSTBEFREED ((slap_mask_t) 0x0020U)
+#define REP_REF_MASK (REP_REF_MUSTBEFREED)
+
+#define REP_CTRLS_MUSTBEFREED ((slap_mask_t) 0x0040U)
+#define REP_CTRLS_MASK (REP_CTRLS_MUSTBEFREED)
+
+#define REP_NO_ENTRYDN ((slap_mask_t) 0x1000U)
+#define REP_NO_SUBSCHEMA ((slap_mask_t) 0x2000U)
+#define REP_NO_OPERATIONALS (REP_NO_ENTRYDN|REP_NO_SUBSCHEMA)
+};
+
+/* short hands for response members */
+#define sr_attrs sr_un.sru_search.r_attrs
+#define sr_entry sr_un.sru_search.r_entry
+#define sr_operational_attrs sr_un.sru_search.r_operational_attrs
+#define sr_attr_flags sr_un.sru_search.r_attr_flags
+#define sr_v2ref sr_un.sru_search.r_v2ref
+#define sr_nentries sr_un.sru_search.r_nentries
+#define sr_rspoid sr_un.sru_extended.r_rspoid
+#define sr_rspdata sr_un.sru_extended.r_rspdata
+#define sr_sasldata sr_un.sru_sasl.r_sasldata
+
+typedef int (BI_op_func) LDAP_P(( Operation *op, SlapReply *rs ));
+typedef BI_op_func BI_op_bind;
+typedef BI_op_func BI_op_unbind;
+typedef BI_op_func BI_op_search;
+typedef BI_op_func BI_op_compare;
+typedef BI_op_func BI_op_modify;
+typedef BI_op_func BI_op_modrdn;
+typedef BI_op_func BI_op_add;
+typedef BI_op_func BI_op_delete;
+typedef BI_op_func BI_op_abandon;
+typedef BI_op_func BI_op_extended;
+typedef BI_op_func BI_op_cancel;
+typedef BI_op_func BI_chk_referrals;
+typedef BI_op_func BI_chk_controls;
+typedef int (BI_entry_release_rw)
+ LDAP_P(( Operation *op, Entry *e, int rw ));
+typedef int (BI_entry_get_rw) LDAP_P(( Operation *op, struct berval *ndn,
+ ObjectClass *oc, AttributeDescription *at, int rw, Entry **e ));
+typedef int (BI_operational) LDAP_P(( Operation *op, SlapReply *rs ));
+typedef int (BI_has_subordinates) LDAP_P(( Operation *op,
+ Entry *e, int *hasSubs ));
+typedef int (BI_access_allowed) LDAP_P(( Operation *op, Entry *e,
+ AttributeDescription *desc, struct berval *val, slap_access_t access,
+ AccessControlState *state, slap_mask_t *maskp ));
+typedef int (BI_acl_group) LDAP_P(( Operation *op, Entry *target,
+ struct berval *gr_ndn, struct berval *op_ndn,
+ ObjectClass *group_oc, AttributeDescription *group_at ));
+typedef int (BI_acl_attribute) LDAP_P(( Operation *op, Entry *target,
+ struct berval *entry_ndn, AttributeDescription *entry_at,
+ BerVarray *vals, slap_access_t access ));
+struct OpExtra;
+typedef int (BI_op_txn) LDAP_P(( Operation *op, int txnop, struct OpExtra **ptr ));
+#define SLAP_TXN_BEGIN 1
+#define SLAP_TXN_COMMIT 2
+#define SLAP_TXN_ABORT 3
+
+typedef int (BI_conn_func) LDAP_P(( BackendDB *bd, Connection *c ));
+typedef BI_conn_func BI_connection_init;
+typedef BI_conn_func BI_connection_destroy;
+
+typedef int (BI_tool_entry_open) LDAP_P(( BackendDB *be, int mode ));
+typedef int (BI_tool_entry_close) LDAP_P(( BackendDB *be ));
+typedef ID (BI_tool_entry_first) LDAP_P(( BackendDB *be ));
+typedef ID (BI_tool_entry_first_x) LDAP_P(( BackendDB *be, struct berval *base, int scope, Filter *f ));
+typedef ID (BI_tool_entry_next) LDAP_P(( BackendDB *be ));
+typedef Entry* (BI_tool_entry_get) LDAP_P(( BackendDB *be, ID id ));
+typedef ID (BI_tool_entry_put) LDAP_P(( BackendDB *be, Entry *e,
+ struct berval *text ));
+typedef int (BI_tool_entry_reindex) LDAP_P(( BackendDB *be, ID id, AttributeDescription **adv ));
+typedef int (BI_tool_sync) LDAP_P(( BackendDB *be ));
+typedef ID (BI_tool_dn2id_get) LDAP_P(( BackendDB *be, struct berval *dn ));
+typedef ID (BI_tool_entry_modify) LDAP_P(( BackendDB *be, Entry *e,
+ struct berval *text ));
+typedef int (BI_tool_entry_delete) LDAP_P(( BackendDB *be, struct berval *ndn,
+ struct berval *text ));
+
+struct BackendInfo {
+ char *bi_type; /* type of backend */
+
+ /*
+ * per backend type routines:
+ * bi_init: called to allocate a backend_info structure,
+ * called once BEFORE configuration file is read.
+ * bi_init() initializes this structure hence is
+ * called directly from be_initialize()
+ * bi_config: called per 'backend' specific option
+ * all such options must before any 'database' options
+ * bi_config() is called only from read_config()
+ * bi_open: called to open each database, called
+ * once AFTER configuration file is read but
+ * BEFORE any bi_db_open() calls.
+ * bi_open() is called from backend_startup()
+ * bi_close: called to close each database, called
+ * once during shutdown after all bi_db_close calls.
+ * bi_close() is called from backend_shutdown()
+ * bi_destroy: called to destroy each database, called
+ * once during shutdown after all bi_db_destroy calls.
+ * bi_destroy() is called from backend_destroy()
+ */
+ BI_init *bi_init;
+ BI_config *bi_config;
+ BI_open *bi_open;
+ BI_pause *bi_pause;
+ BI_unpause *bi_unpause;
+ BI_close *bi_close;
+ BI_destroy *bi_destroy;
+
+ /*
+ * per database routines:
+ * bi_db_init: called to initialize each database,
+ * called upon reading 'database <type>'
+ * called only from backend_db_init()
+ * bi_db_config: called to configure each database,
+ * called per database to handle per database options
+ * called only from read_config()
+ * bi_db_open: called to open each database
+ * called once per database immediately AFTER bi_open()
+ * calls but before daemon startup.
+ * called only by backend_startup()
+ * bi_db_close: called to close each database
+ * called once per database during shutdown but BEFORE
+ * any bi_close call.
+ * called only by backend_shutdown()
+ * bi_db_destroy: called to destroy each database
+ * called once per database during shutdown AFTER all
+ * bi_close calls but before bi_destroy calls.
+ * called only by backend_destroy()
+ */
+ BI_db_init *bi_db_init;
+ BI_db_config *bi_db_config;
+ BI_db_open *bi_db_open;
+ BI_db_close *bi_db_close;
+ BI_db_destroy *bi_db_destroy;
+
+ /* LDAP Operations Handling Routines */
+ BI_op_bind *bi_op_bind;
+ BI_op_unbind *bi_op_unbind;
+ BI_op_search *bi_op_search;
+ BI_op_compare *bi_op_compare;
+ BI_op_modify *bi_op_modify;
+ BI_op_modrdn *bi_op_modrdn;
+ BI_op_add *bi_op_add;
+ BI_op_delete *bi_op_delete;
+ BI_op_abandon *bi_op_abandon;
+
+ /* Extended Operations Helper */
+ BI_op_extended *bi_extended;
+ BI_op_cancel *bi_op_cancel;
+
+ /* Auxiliary Functions */
+ BI_operational *bi_operational;
+ BI_chk_referrals *bi_chk_referrals;
+ BI_chk_controls *bi_chk_controls;
+ BI_op_txn *bi_op_txn;
+ BI_entry_get_rw *bi_entry_get_rw;
+ BI_entry_release_rw *bi_entry_release_rw;
+
+ BI_has_subordinates *bi_has_subordinates;
+ BI_access_allowed *bi_access_allowed;
+ BI_acl_group *bi_acl_group;
+ BI_acl_attribute *bi_acl_attribute;
+
+ BI_connection_init *bi_connection_init;
+ BI_connection_destroy *bi_connection_destroy;
+
+ /* hooks for slap tools */
+ BI_tool_entry_open *bi_tool_entry_open;
+ BI_tool_entry_close *bi_tool_entry_close;
+ BI_tool_entry_first *bi_tool_entry_first; /* deprecated */
+ BI_tool_entry_first_x *bi_tool_entry_first_x;
+ BI_tool_entry_next *bi_tool_entry_next;
+ BI_tool_entry_get *bi_tool_entry_get;
+ BI_tool_entry_put *bi_tool_entry_put;
+ BI_tool_entry_reindex *bi_tool_entry_reindex;
+ BI_tool_sync *bi_tool_sync;
+ BI_tool_dn2id_get *bi_tool_dn2id_get;
+ BI_tool_entry_modify *bi_tool_entry_modify;
+ BI_tool_entry_delete *bi_tool_entry_delete;
+
+#define SLAP_INDEX_ADD_OP 0x0001
+#define SLAP_INDEX_DELETE_OP 0x0002
+
+ slap_mask_t bi_flags; /* backend flags */
+#define SLAP_BFLAG_MONITOR 0x0001U /* a monitor backend */
+#define SLAP_BFLAG_CONFIG 0x0002U /* a config backend */
+#define SLAP_BFLAG_FRONTEND 0x0004U /* the frontendDB */
+#define SLAP_BFLAG_NOLASTMODCMD 0x0010U
+#define SLAP_BFLAG_INCREMENT 0x0100U
+#define SLAP_BFLAG_ALIASES 0x1000U
+#define SLAP_BFLAG_REFERRALS 0x2000U
+#define SLAP_BFLAG_SUBENTRIES 0x4000U
+#define SLAP_BFLAG_DYNAMIC 0x8000U
+#define SLAP_BFLAG_STANDALONE 0x10000U /* started up regardless of whether any databases use it */
+#define SLAP_BFLAG_TXNS 0x20000U /* supports LDAP transactions */
+
+/* overlay specific */
+#define SLAPO_BFLAG_SINGLE 0x01000000U
+#define SLAPO_BFLAG_DBONLY 0x02000000U
+#define SLAPO_BFLAG_GLOBONLY 0x04000000U
+#define SLAPO_BFLAG_DISABLED 0x08000000U
+#define SLAPO_BFLAG_MASK 0xFF000000U
+
+#define SLAP_BFLAGS(be) ((be)->bd_info->bi_flags)
+#define SLAP_MONITOR(be) (SLAP_BFLAGS(be) & SLAP_BFLAG_MONITOR)
+#define SLAP_CONFIG(be) (SLAP_BFLAGS(be) & SLAP_BFLAG_CONFIG)
+#define SLAP_FRONTEND(be) (SLAP_BFLAGS(be) & SLAP_BFLAG_FRONTEND)
+#define SLAP_INCREMENT(be) (SLAP_BFLAGS(be) & SLAP_BFLAG_INCREMENT)
+#define SLAP_ALIASES(be) (SLAP_BFLAGS(be) & SLAP_BFLAG_ALIASES)
+#define SLAP_REFERRALS(be) (SLAP_BFLAGS(be) & SLAP_BFLAG_REFERRALS)
+#define SLAP_SUBENTRIES(be) (SLAP_BFLAGS(be) & SLAP_BFLAG_SUBENTRIES)
+#define SLAP_DYNAMIC(be) ((SLAP_BFLAGS(be) & SLAP_BFLAG_DYNAMIC) || (SLAP_DBFLAGS(be) & SLAP_DBFLAG_DYNAMIC))
+#define SLAP_NOLASTMODCMD(be) (SLAP_BFLAGS(be) & SLAP_BFLAG_NOLASTMODCMD)
+#define SLAP_LASTMODCMD(be) (!SLAP_NOLASTMODCMD(be))
+#define SLAP_TXNS(be) (SLAP_BFLAGS(be) & SLAP_BFLAG_TXNS)
+
+/* overlay specific */
+#define SLAPO_SINGLE(be) (SLAP_BFLAGS(be) & SLAPO_BFLAG_SINGLE)
+#define SLAPO_DBONLY(be) (SLAP_BFLAGS(be) & SLAPO_BFLAG_DBONLY)
+#define SLAPO_GLOBONLY(be) (SLAP_BFLAGS(be) & SLAPO_BFLAG_GLOBONLY)
+#define SLAPO_DISABLED(be) (SLAP_BFLAGS(be) & SLAPO_BFLAG_DISABLED)
+
+ char **bi_controls; /* supported controls */
+ char bi_ctrls[SLAP_MAX_CIDS + 1];
+
+ unsigned int bi_nDB; /* number of databases of this type */
+ struct ConfigOCs *bi_cf_ocs;
+ char **bi_obsolete_names;
+ void *bi_extra; /* backend type-specific APIs */
+ void *bi_private; /* backend type-specific config data */
+ LDAP_STAILQ_ENTRY(BackendInfo) bi_next ;
+};
+
+#define c_authtype c_authz.sai_method
+#define c_authmech c_authz.sai_mech
+#define c_dn c_authz.sai_dn
+#define c_ndn c_authz.sai_ndn
+#define c_ssf c_authz.sai_ssf
+#define c_transport_ssf c_authz.sai_transport_ssf
+#define c_tls_ssf c_authz.sai_tls_ssf
+#define c_sasl_ssf c_authz.sai_sasl_ssf
+
+#define o_authtype o_authz.sai_method
+#define o_authmech o_authz.sai_mech
+#define o_dn o_authz.sai_dn
+#define o_ndn o_authz.sai_ndn
+#define o_ssf o_authz.sai_ssf
+#define o_transport_ssf o_authz.sai_transport_ssf
+#define o_tls_ssf o_authz.sai_tls_ssf
+#define o_sasl_ssf o_authz.sai_sasl_ssf
+
+typedef int (slap_response)( Operation *, SlapReply * );
+
+struct slap_callback;
+typedef void (slap_writewait)( Operation *, struct slap_callback * );
+
+typedef struct slap_callback {
+ struct slap_callback *sc_next;
+ slap_response *sc_response;
+ slap_response *sc_cleanup;
+ void *sc_private;
+ slap_writewait *sc_writewait;
+} slap_callback;
+
+struct slap_overinfo;
+
+typedef enum slap_operation_e {
+ op_bind = 0,
+ op_unbind,
+ op_search,
+ op_compare,
+ op_modify,
+ op_modrdn,
+ op_add,
+ op_delete,
+ op_abandon,
+ op_extended,
+ op_cancel,
+ op_aux_operational,
+ op_aux_chk_referrals,
+ op_aux_chk_controls,
+ op_txn,
+ op_last
+} slap_operation_t;
+
+typedef struct slap_overinst {
+ BackendInfo on_bi;
+ slap_response *on_response;
+ struct slap_overinfo *on_info;
+ struct slap_overinst *on_next;
+} slap_overinst;
+
+typedef struct slap_overinfo {
+ BackendInfo oi_bi;
+ BackendInfo *oi_orig;
+ BackendDB *oi_origdb;
+ struct slap_overinst *oi_list;
+} slap_overinfo;
+
+/* Should successive callbacks in a chain be processed? */
+#define SLAP_CB_BYPASS 0x08800
+#define SLAP_CB_CONTINUE 0x08000
+
+/*
+ * Paged Results state
+ */
+typedef unsigned long PagedResultsCookie;
+typedef struct PagedResultsState {
+ Backend *ps_be;
+ ber_int_t ps_size;
+ int ps_count;
+ PagedResultsCookie ps_cookie;
+ struct berval ps_cookieval;
+} PagedResultsState;
+
+struct slap_csn_entry {
+ Operation *ce_op;
+ struct berval ce_csn;
+ int ce_sid;
+#define SLAP_CSN_PENDING 1
+#define SLAP_CSN_COMMIT 2
+ long ce_state;
+ LDAP_TAILQ_ENTRY (slap_csn_entry) ce_csn_link;
+};
+
+/*
+ * Caches the result of a backend_group check for ACL evaluation
+ */
+typedef struct GroupAssertion {
+ struct GroupAssertion *ga_next;
+ Backend *ga_be;
+ ObjectClass *ga_oc;
+ AttributeDescription *ga_at;
+ int ga_res;
+ ber_len_t ga_len;
+ char ga_ndn[1];
+} GroupAssertion;
+
+struct slap_control_ids {
+ int sc_LDAPsync;
+ int sc_assert;
+ int sc_domainScope;
+ int sc_dontUseCopy;
+ int sc_manageDSAit;
+ int sc_modifyIncrement;
+ int sc_noOp;
+ int sc_pagedResults;
+ int sc_permissiveModify;
+ int sc_postRead;
+ int sc_preRead;
+ int sc_proxyAuthz;
+ int sc_relax;
+ int sc_searchOptions;
+#ifdef SLAP_CONTROL_X_SORTEDRESULTS
+ int sc_sortedResults;
+#endif
+ int sc_subentries;
+#ifdef SLAP_CONTROL_X_TREE_DELETE
+ int sc_treeDelete;
+#endif
+ int sc_txnSpec;
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+ int sc_sessionTracking;
+#endif
+ int sc_valuesReturnFilter;
+#ifdef SLAP_CONTROL_X_WHATFAILED
+ int sc_whatFailed;
+#endif
+#ifdef LDAP_CONTROL_X_LAZY_COMMIT
+ int sc_lazyCommit;
+#endif
+};
+
+/*
+ * Operation indices
+ */
+typedef enum {
+ SLAP_OP_BIND = 0,
+ SLAP_OP_UNBIND,
+ SLAP_OP_SEARCH,
+ SLAP_OP_COMPARE,
+ SLAP_OP_MODIFY,
+ SLAP_OP_MODRDN,
+ SLAP_OP_ADD,
+ SLAP_OP_DELETE,
+ SLAP_OP_ABANDON,
+ SLAP_OP_EXTENDED,
+ SLAP_OP_LAST
+} slap_op_t;
+
+typedef struct slap_counters_t {
+ struct slap_counters_t *sc_next;
+ ldap_pvt_thread_mutex_t sc_mutex;
+ ldap_pvt_mp_t sc_bytes;
+ ldap_pvt_mp_t sc_pdu;
+ ldap_pvt_mp_t sc_entries;
+ ldap_pvt_mp_t sc_refs;
+
+ ldap_pvt_mp_t sc_ops_completed;
+ ldap_pvt_mp_t sc_ops_initiated;
+ ldap_pvt_mp_t sc_ops_completed_[SLAP_OP_LAST];
+ ldap_pvt_mp_t sc_ops_initiated_[SLAP_OP_LAST];
+} slap_counters_t;
+
+/*
+ * represents an operation pending from an ldap client
+ */
+typedef struct Opheader {
+ unsigned long oh_opid; /* id of this operation */
+ unsigned long oh_connid; /* id of conn initiating this op */
+ Connection *oh_conn; /* connection spawning this op */
+
+ ber_int_t oh_msgid; /* msgid of the request */
+ ber_int_t oh_protocol; /* version of the LDAP protocol used by client */
+
+ ldap_pvt_thread_t oh_tid; /* thread handling this op */
+
+ void *oh_threadctx; /* thread pool thread context */
+ void *oh_tmpmemctx; /* slab malloc context */
+ BerMemoryFunctions *oh_tmpmfuncs;
+
+ slap_counters_t *oh_counters;
+
+ char oh_log_prefix[ /* sizeof("conn= op=") + 2*LDAP_PVT_INTTYPE_CHARS(unsigned long) */ SLAP_TEXT_BUFLEN ];
+
+#ifdef LDAP_SLAPI
+ void *oh_extensions; /* NS-SLAPI plugin */
+#endif
+} Opheader;
+
+typedef union OpRequest {
+ req_add_s oq_add;
+ req_bind_s oq_bind;
+ req_compare_s oq_compare;
+ req_modify_s oq_modify;
+ req_modrdn_s oq_modrdn;
+ req_search_s oq_search;
+ req_abandon_s oq_abandon;
+ req_abandon_s oq_cancel;
+ req_extended_s oq_extended;
+ req_pwdexop_s oq_pwdexop;
+} OpRequest;
+
+/* This is only a header. Actual users should define their own
+ * structs with the oe_next / oe_key fields at the top and
+ * whatever else they need following.
+ */
+typedef struct OpExtra {
+ LDAP_SLIST_ENTRY(OpExtra) oe_next;
+ void *oe_key;
+} OpExtra;
+
+typedef struct OpExtraDB {
+ OpExtra oe;
+ BackendDB *oe_db;
+} OpExtraDB;
+
+struct Operation {
+ Opheader *o_hdr;
+
+#define o_opid o_hdr->oh_opid
+#define o_connid o_hdr->oh_connid
+#define o_conn o_hdr->oh_conn
+#define o_msgid o_hdr->oh_msgid
+#define o_protocol o_hdr->oh_protocol
+#define o_tid o_hdr->oh_tid
+#define o_threadctx o_hdr->oh_threadctx
+#define o_tmpmemctx o_hdr->oh_tmpmemctx
+#define o_tmpmfuncs o_hdr->oh_tmpmfuncs
+#define o_counters o_hdr->oh_counters
+
+#define o_tmpalloc o_tmpmfuncs->bmf_malloc
+#define o_tmpcalloc o_tmpmfuncs->bmf_calloc
+#define o_tmprealloc o_tmpmfuncs->bmf_realloc
+#define o_tmpfree o_tmpmfuncs->bmf_free
+
+#define o_log_prefix o_hdr->oh_log_prefix
+
+ ber_tag_t o_tag; /* tag of the request */
+ time_t o_time; /* time op was initiated */
+ int o_tincr; /* counter for multiple ops with same o_time */
+ int o_tusec; /* microsecond timestamp */
+ struct timeval o_qtime; /* time spent in queues before execution */
+
+ BackendDB *o_bd; /* backend DB processing this op */
+ struct berval o_req_dn; /* DN of target of request */
+ struct berval o_req_ndn;
+
+ OpRequest o_request;
+
+/* short hands for union members */
+#define oq_add o_request.oq_add
+#define oq_bind o_request.oq_bind
+#define oq_compare o_request.oq_compare
+#define oq_modify o_request.oq_modify
+#define oq_modrdn o_request.oq_modrdn
+#define oq_search o_request.oq_search
+#define oq_abandon o_request.oq_abandon
+#define oq_cancel o_request.oq_cancel
+#define oq_extended o_request.oq_extended
+#define oq_pwdexop o_request.oq_pwdexop
+
+/* short hands for inner request members */
+#define orb_method oq_bind.rb_method
+#define orb_cred oq_bind.rb_cred
+#define orb_edn oq_bind.rb_edn
+#define orb_ssf oq_bind.rb_ssf
+#define orb_mech oq_bind.rb_mech
+
+#define ors_scope oq_search.rs_scope
+#define ors_deref oq_search.rs_deref
+#define ors_slimit oq_search.rs_slimit
+#define ors_tlimit oq_search.rs_tlimit
+#define ors_limit oq_search.rs_limit
+#define ors_attrsonly oq_search.rs_attrsonly
+#define ors_attrs oq_search.rs_attrs
+#define ors_filter oq_search.rs_filter
+#define ors_filterstr oq_search.rs_filterstr
+
+#define orr_modlist oq_modrdn.rs_mods.rs_modlist
+#define orr_no_opattrs oq_modrdn.rs_mods.rs_no_opattrs
+#define orr_deleteoldrdn oq_modrdn.rs_deleteoldrdn
+#define orr_newrdn oq_modrdn.rs_newrdn
+#define orr_nnewrdn oq_modrdn.rs_nnewrdn
+#define orr_newSup oq_modrdn.rs_newSup
+#define orr_nnewSup oq_modrdn.rs_nnewSup
+
+#define orc_ava oq_compare.rs_ava
+
+#define ora_e oq_add.rs_e
+#define ora_modlist oq_add.rs_modlist
+
+#define orn_msgid oq_abandon.rs_msgid
+
+#define orm_modlist oq_modify.rs_mods.rs_modlist
+#define orm_no_opattrs oq_modify.rs_mods.rs_no_opattrs
+#define orm_increment oq_modify.rs_increment
+
+#define ore_reqoid oq_extended.rs_reqoid
+#define ore_flags oq_extended.rs_flags
+#define ore_reqdata oq_extended.rs_reqdata
+ volatile sig_atomic_t o_abandon; /* abandon flag */
+ volatile sig_atomic_t o_cancel; /* cancel flag */
+#define SLAP_CANCEL_NONE 0x00
+#define SLAP_CANCEL_REQ 0x01
+#define SLAP_CANCEL_ACK 0x02
+#define SLAP_CANCEL_DONE 0x03
+
+ GroupAssertion *o_groups;
+ char o_do_not_cache; /* don't cache groups from this op */
+ char o_is_auth_check; /* authorization in progress */
+ char o_dont_replicate;
+ slap_access_t o_acl_priv;
+
+ char o_nocaching;
+ char o_delete_glue_parent;
+ char o_no_schema_check;
+#define get_no_schema_check(op) ((op)->o_no_schema_check)
+ char o_no_subordinate_glue;
+#define get_no_subordinate_glue(op) ((op)->o_no_subordinate_glue)
+
+#define SLAP_CONTROL_NONE 0
+#define SLAP_CONTROL_IGNORED 1
+#define SLAP_CONTROL_NONCRITICAL 2
+#define SLAP_CONTROL_CRITICAL 3
+#define SLAP_CONTROL_MASK 3
+
+/* spare bits for simple flags */
+#define SLAP_CONTROL_SHIFT 4 /* shift to reach data bits */
+#define SLAP_CONTROL_DATA0 0x10
+#define SLAP_CONTROL_DATA1 0x20
+#define SLAP_CONTROL_DATA2 0x40
+#define SLAP_CONTROL_DATA3 0x80
+
+#define _SCM(x) ((x) & SLAP_CONTROL_MASK)
+
+ char o_ctrlflag[SLAP_MAX_CIDS]; /* per-control flags */
+ void **o_controls; /* per-control state */
+
+#define o_dontUseCopy o_ctrlflag[slap_cids.sc_dontUseCopy]
+#define get_dontUseCopy(op) _SCM((op)->o_dontUseCopy)
+
+#define o_relax o_ctrlflag[slap_cids.sc_relax]
+#define get_relax(op) _SCM((op)->o_relax)
+
+#define o_managedsait o_ctrlflag[slap_cids.sc_manageDSAit]
+#define get_manageDSAit(op) _SCM((op)->o_managedsait)
+
+#define o_noop o_ctrlflag[slap_cids.sc_noOp]
+#define o_proxy_authz o_ctrlflag[slap_cids.sc_proxyAuthz]
+#define o_subentries o_ctrlflag[slap_cids.sc_subentries]
+
+#define get_subentries(op) _SCM((op)->o_subentries)
+#define o_subentries_visibility o_ctrlflag[slap_cids.sc_subentries]
+
+#define set_subentries_visibility(op) ((op)->o_subentries |= SLAP_CONTROL_DATA0)
+#define get_subentries_visibility(op) (((op)->o_subentries & SLAP_CONTROL_DATA0) != 0)
+
+#define o_assert o_ctrlflag[slap_cids.sc_assert]
+#define get_assert(op) ((int)(op)->o_assert)
+#define o_assertion o_controls[slap_cids.sc_assert]
+#define get_assertion(op) ((op)->o_assertion)
+
+#define o_valuesreturnfilter o_ctrlflag[slap_cids.sc_valuesReturnFilter]
+#define o_vrFilter o_controls[slap_cids.sc_valuesReturnFilter]
+
+#define o_permissive_modify o_ctrlflag[slap_cids.sc_permissiveModify]
+#define get_permissiveModify(op) ((int)(op)->o_permissive_modify)
+
+#define o_domain_scope o_ctrlflag[slap_cids.sc_domainScope]
+#define get_domainScope(op) ((int)(op)->o_domain_scope)
+
+#ifdef SLAP_CONTROL_X_TREE_DELETE
+#define o_tree_delete o_ctrlflag[slap_cids.sc_treeDelete]
+#define get_treeDelete(op) ((int)(op)->o_tree_delete)
+#endif
+
+#define o_preread o_ctrlflag[slap_cids.sc_preRead]
+#define o_postread o_ctrlflag[slap_cids.sc_postRead]
+
+#define o_preread_attrs o_controls[slap_cids.sc_preRead]
+#define o_postread_attrs o_controls[slap_cids.sc_postRead]
+
+#define o_pagedresults o_ctrlflag[slap_cids.sc_pagedResults]
+#define o_pagedresults_state o_controls[slap_cids.sc_pagedResults]
+#define get_pagedresults(op) ((int)(op)->o_pagedresults)
+
+#ifdef SLAP_CONTROL_X_SORTEDRESULTS
+#define o_sortedresults o_ctrlflag[slap_cids.sc_sortedResults]
+#endif
+
+#define o_txnSpec o_ctrlflag[slap_cids.sc_txnSpec]
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+#define o_session_tracking o_ctrlflag[slap_cids.sc_sessionTracking]
+#define o_tracked_sessions o_controls[slap_cids.sc_sessionTracking]
+#define get_sessionTracking(op) ((int)(op)->o_session_tracking)
+#endif
+
+#ifdef SLAP_CONTROL_X_WHATFAILED
+#define o_whatFailed o_ctrlflag[slap_cids.sc_whatFailed]
+#define get_whatFailed(op) _SCM((op)->o_whatFailed)
+#endif
+
+#ifdef SLAP_CONTROL_X_LAZY_COMMIT
+#define o_lazyCommit o_ctrlflag[slap_cids.sc_lazyCommit]
+#define get_lazyCommit(op) _SCM((op)->o_lazyCommit)
+#endif
+
+#define o_sync o_ctrlflag[slap_cids.sc_LDAPsync]
+
+ AuthorizationInformation o_authz;
+
+ BerElement *o_ber; /* ber of the request */
+ BerElement *o_res_ber; /* ber of the CLDAP reply or readback control */
+ slap_callback *o_callback; /* callback pointers */
+ LDAPControl **o_ctrls; /* controls */
+ struct berval o_csn;
+
+ /* DEPRECATE o_private - use o_extra instead */
+ void *o_private; /* anything the backend needs */
+ LDAP_SLIST_HEAD(o_e, OpExtra) o_extra; /* anything the backend needs */
+
+ LDAP_STAILQ_ENTRY(Operation) o_next; /* next operation in list */
+};
+
+typedef struct OperationBuffer {
+ Operation ob_op;
+ Opheader ob_hdr;
+ void *ob_controls[SLAP_MAX_CIDS];
+} OperationBuffer;
+
+#define send_ldap_error( op, rs, err, text ) do { \
+ (rs)->sr_err = err; (rs)->sr_text = text; \
+ ((op)->o_conn->c_send_ldap_result)( op, rs ); \
+ } while (0)
+#define send_ldap_discon( op, rs, err, text ) do { \
+ (rs)->sr_err = err; (rs)->sr_text = text; \
+ send_ldap_disconnect( op, rs ); \
+ } while (0)
+
+typedef void (SEND_LDAP_RESULT)(
+ Operation *op, SlapReply *rs);
+typedef int (SEND_SEARCH_ENTRY)(
+ Operation *op, SlapReply *rs);
+typedef int (SEND_SEARCH_REFERENCE)(
+ Operation *op, SlapReply *rs);
+typedef void (SEND_LDAP_EXTENDED)(
+ Operation *op, SlapReply *rs);
+typedef void (SEND_LDAP_INTERMEDIATE)(
+ Operation *op, SlapReply *rs);
+
+#define send_ldap_result( op, rs ) \
+ ((op)->o_conn->c_send_ldap_result)( op, rs )
+#define send_search_entry( op, rs ) \
+ ((op)->o_conn->c_send_search_entry)( op, rs )
+#define send_search_reference( op, rs ) \
+ ((op)->o_conn->c_send_search_reference)( op, rs )
+#define send_ldap_extended( op, rs ) \
+ ((op)->o_conn->c_send_ldap_extended)( op, rs )
+#define send_ldap_intermediate( op, rs ) \
+ ((op)->o_conn->c_send_ldap_intermediate)( op, rs )
+
+typedef struct Listener Listener;
+
+/*
+ * represents a connection from an ldap client
+ */
+/* connection state (protected by c_mutex ) */
+enum sc_conn_state {
+ SLAP_C_INVALID = 0, /* MUST BE ZERO (0) */
+ SLAP_C_INACTIVE, /* zero threads */
+ SLAP_C_CLOSING, /* closing */
+ SLAP_C_ACTIVE, /* one or more threads */
+ SLAP_C_BINDING, /* binding */
+ SLAP_C_CLIENT /* outbound client conn */
+};
+struct Connection {
+ enum sc_conn_state c_conn_state; /* connection state */
+ int c_conn_idx; /* slot in connections array */
+ ber_socket_t c_sd;
+ const char *c_close_reason; /* why connection is closing */
+
+ ldap_pvt_thread_mutex_t c_mutex; /* protect the connection */
+ Sockbuf *c_sb; /* ber connection stuff */
+
+ /* only can be changed by connect_init */
+ time_t c_starttime; /* when the connection was opened */
+ time_t c_activitytime; /* when the connection was last used */
+ unsigned long c_connid; /* id of this connection for stats*/
+
+ struct berval c_peer_domain; /* DNS name of client */
+ struct berval c_peer_name; /* peer name (trans=addr:port) */
+ Listener *c_listener;
+#define c_listener_url c_listener->sl_url /* listener URL */
+#define c_sock_name c_listener->sl_name /* sock name (trans=addr:port) */
+
+ /* only can be changed by binding thread */
+ struct berval c_sasl_bind_mech; /* mech in progress */
+ struct berval c_sasl_dn; /* temporary storage */
+ struct berval c_sasl_authz_dn; /* SASL proxy authz */
+
+ /* authorization backend */
+ Backend *c_authz_backend;
+ void *c_authz_cookie;
+#define SLAP_IS_AUTHZ_BACKEND( op ) \
+ ( (op)->o_bd != NULL \
+ && (op)->o_bd->be_private != NULL \
+ && (op)->o_conn != NULL \
+ && (op)->o_conn->c_authz_backend != NULL \
+ && ( (op)->o_bd->be_private == (op)->o_conn->c_authz_backend->be_private \
+ || (op)->o_bd->be_private == (op)->o_conn->c_authz_cookie ) )
+
+ AuthorizationInformation c_authz;
+
+ ber_int_t c_protocol; /* version of the LDAP protocol used by client */
+
+ LDAP_STAILQ_HEAD(c_o, Operation) c_ops; /* list of operations being processed */
+ LDAP_STAILQ_HEAD(c_po, Operation) c_pending_ops; /* list of pending operations */
+
+ ldap_pvt_thread_mutex_t c_write1_mutex; /* only one pdu written at a time */
+ ldap_pvt_thread_cond_t c_write1_cv; /* only one pdu written at a time */
+
+ BerElement *c_currentber; /* ber we're attempting to read */
+ int c_writers; /* number of writers waiting */
+ char c_writing; /* someone is writing */
+
+ char c_sasl_bind_in_progress; /* multi-op bind in progress */
+ char c_writewaiter; /* true if blocked on write */
+
+
+#define CONN_IS_TLS 1
+#define CONN_IS_UDP 2
+#define CONN_IS_CLIENT 4
+#define CONN_IS_IPC 8
+
+#ifdef LDAP_CONNECTIONLESS
+ char c_is_udp; /* true if this is (C)LDAP over UDP */
+#endif
+#ifdef HAVE_TLS
+ char c_is_tls; /* true if this LDAP over raw TLS */
+ char c_needs_tls_accept; /* true if SSL_accept should be called */
+#endif
+ char c_sasl_layers; /* true if we need to install SASL i/o handlers */
+ char c_sasl_done; /* SASL completed once */
+ void *c_sasl_authctx; /* SASL authentication context */
+ void *c_sasl_sockctx; /* SASL security layer context */
+ void *c_sasl_extra; /* SASL session extra stuff */
+ void *c_sasl_cbind; /* SASL channel binding */
+ Operation *c_sasl_bindop; /* set to current op if it's a bind */
+
+#define CONN_TXN_INACTIVE 0
+#define CONN_TXN_SPECIFY 1
+#define CONN_TXN_SETTLE -1
+ int c_txn;
+
+ Backend *c_txn_backend;
+ LDAP_STAILQ_HEAD(c_to, Operation) c_txn_ops; /* list of operations in txn */
+
+ PagedResultsState c_pagedresults_state; /* paged result state */
+
+ long c_n_ops_received; /* num of ops received (next op_id) */
+ long c_n_ops_executing; /* num of ops currently executing */
+ long c_n_ops_pending; /* num of ops pending execution */
+ long c_n_ops_completed; /* num of ops completed */
+ long c_n_ops_async; /* mum of ops currently executing asynchronously */
+
+ long c_n_get; /* num of get calls */
+ long c_n_read; /* num of read calls */
+ long c_n_write; /* num of write calls */
+
+ void *c_extensions; /* Netscape plugin */
+
+ /*
+ * Client connection handling
+ */
+ ldap_pvt_thread_start_t *c_clientfunc;
+ void *c_clientarg;
+
+ /*
+ * These are the "callbacks" that are available for back-ends to
+ * supply data back to connected clients that are connected
+ * through the "front-end".
+ */
+ SEND_LDAP_RESULT *c_send_ldap_result;
+ SEND_SEARCH_ENTRY *c_send_search_entry;
+ SEND_SEARCH_REFERENCE *c_send_search_reference;
+ SEND_LDAP_EXTENDED *c_send_ldap_extended;
+ SEND_LDAP_INTERMEDIATE *c_send_ldap_intermediate;
+};
+
+#ifdef LDAP_DEBUG
+#ifdef LDAP_SYSLOG
+#ifdef LOG_LOCAL4
+#define SLAP_DEFAULT_SYSLOG_USER LOG_LOCAL4
+#endif /* LOG_LOCAL4 */
+#endif /* !LDAP_SYSLOG */
+#endif /* !LDAP_DEBUG */
+
+/*
+ * listener; need to access it from monitor backend
+ */
+struct Listener {
+ struct berval sl_url;
+ struct berval sl_name;
+ mode_t sl_perms;
+#ifdef HAVE_TLS
+ int sl_is_tls;
+#endif
+#ifdef LDAP_CONNECTIONLESS
+ int sl_is_udp; /* UDP listener is also data port */
+#endif
+ int sl_is_proxied;
+ int sl_mute; /* Listener is temporarily disabled due to emfile */
+ int sl_busy; /* Listener is busy (accept thread activated) */
+ ber_socket_t sl_sd;
+ Sockaddr sl_sa;
+#define sl_addr sl_sa.sa_in_addr
+#define LDAP_TCP_BUFFER
+#ifdef LDAP_TCP_BUFFER
+ int sl_tcp_rmem; /* custom TCP read buffer size */
+ int sl_tcp_wmem; /* custom TCP write buffer size */
+#endif
+};
+
+/*
+ * Better know these all around slapd
+ */
+#define SLAP_LDAPDN_PRETTY 0x1
+#define SLAP_LDAPDN_MAXLEN 8192
+
+/* number of response controls supported */
+#define SLAP_MAX_RESPONSE_CONTROLS 6
+
+#ifdef SLAP_SCHEMA_EXPOSE
+#define SLAP_CTRL_HIDE 0x00000000U
+#else
+#define SLAP_CTRL_HIDE 0x80000000U
+#endif
+
+#define SLAP_CTRL_REQUIRES_ROOT 0x40000000U /* for Relax */
+
+#define SLAP_CTRL_GLOBAL 0x00800000U
+#define SLAP_CTRL_GLOBAL_SEARCH 0x00010000U /* for NOOP */
+
+#define SLAP_CTRL_OPFLAGS 0x0000FFFFU
+#define SLAP_CTRL_ABANDON 0x00000001U
+#define SLAP_CTRL_ADD 0x00002002U
+#define SLAP_CTRL_BIND 0x00000004U
+#define SLAP_CTRL_COMPARE 0x00001008U
+#define SLAP_CTRL_DELETE 0x00002010U
+#define SLAP_CTRL_MODIFY 0x00002020U
+#define SLAP_CTRL_RENAME 0x00002040U
+#define SLAP_CTRL_SEARCH 0x00001080U
+#define SLAP_CTRL_UNBIND 0x00000100U
+
+#define SLAP_CTRL_INTROGATE (SLAP_CTRL_COMPARE|SLAP_CTRL_SEARCH)
+#define SLAP_CTRL_UPDATE \
+ (SLAP_CTRL_ADD|SLAP_CTRL_DELETE|SLAP_CTRL_MODIFY|SLAP_CTRL_RENAME)
+#define SLAP_CTRL_ACCESS (SLAP_CTRL_INTROGATE|SLAP_CTRL_UPDATE)
+
+typedef int (SLAP_CTRL_PARSE_FN) LDAP_P((
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl ));
+
+typedef int (*SLAP_ENTRY_INFO_FN) LDAP_P(( void *arg, Entry *e ));
+
+#define SLAP_SLAB_SIZE (1024*1024)
+#define SLAP_SLAB_STACK 1
+
+#define SLAP_ZONE_ALLOC 1
+#undef SLAP_ZONE_ALLOC
+
+#ifdef LDAP_COMP_MATCH
+/*
+ * Extensible Filter Definition
+ *
+ * MatchingRuleAssertion := SEQUENCE {
+ * matchingRule [1] MatchingRuleId OPTIONAL,
+ * type [2] AttributeDescription OPTIONAL,
+ * matchValue [3] AssertionValue,
+ * dnAttributes [4] BOOLEAN DEFAULT FALSE }
+ *
+ * Following ComponentFilter is contained in matchValue
+ *
+ * ComponentAssertion ::= SEQUENCE {
+ * component ComponentReference (SIZE(1..MAX)) OPTIONAL
+ * useDefaultValues BOOLEAN DEFAULT TRUE,
+ * rule MATCHING-RULE.&id,
+ * value MATCHING-RULE.&AssertionType }
+ *
+ * ComponentFilter ::= CHOICE {
+ * item [0] ComponentAssertion,
+ * and [1] SEQUENCE OF ComponentFilter,
+ * or [2] SEQUENCE OF ComponentFilter,
+ * not [3] ComponentFilter }
+ */
+
+#define LDAP_COMPREF_IDENTIFIER ((ber_tag_t) 0x80U)
+#define LDAP_COMPREF_FROM_BEGINNING ((ber_tag_t) 0x81U)
+#define LDAP_COMPREF_COUNT ((ber_tag_t) 0x82U)
+#define LDAP_COMPREF_FROM_END ((ber_tag_t) 0x83U)
+#define LDAP_COMPREF_CONTENT ((ber_tag_t) 0x84U)
+#define LDAP_COMPREF_SELECT ((ber_tag_t) 0x85U)
+#define LDAP_COMPREF_ALL ((ber_tag_t) 0x86U)
+#define LDAP_COMPREF_DEFINED ((ber_tag_t) 0x87U)
+#define LDAP_COMPREF_UNDEFINED ((ber_tag_t) 0x88U)
+
+#define LDAP_COMP_FILTER_AND ((ber_tag_t) 0xa0U)
+#define LDAP_COMP_FILTER_OR ((ber_tag_t) 0xa1U)
+#define LDAP_COMP_FILTER_NOT ((ber_tag_t) 0xa2U)
+#define LDAP_COMP_FILTER_ITEM ((ber_tag_t) 0xa3U)
+#define LDAP_COMP_FILTER_UNDEFINED ((ber_tag_t) 0xa4U)
+
+typedef struct ComponentId ComponentId;
+typedef struct ComponentReference ComponentReference;
+typedef struct ComponentAssertion ComponentAssertion;
+typedef struct ComponentAssertionValue ComponentAssertionValue;
+typedef struct ComponentSyntaxInfo ComponentSyntaxInfo;
+typedef struct ComponentDesc ComponentDesc;
+
+struct ComponentData {
+ void *cd_mem_op; /* nibble memory handler */
+ ComponentSyntaxInfo** cd_tree; /* component tree */
+};
+
+struct ComponentId {
+ int ci_type;
+ ComponentId *ci_next;
+
+ union comp_id_value{
+ BerValue ci_identifier;
+ ber_int_t ci_from_beginning;
+ ber_int_t ci_count;
+ ber_int_t ci_from_end;
+ ber_int_t ci_content;
+ BerValue ci_select_value;
+ char ci_all;
+ } ci_val;
+};
+
+struct ComponentReference {
+ ComponentId *cr_list;
+ ComponentId *cr_curr;
+ struct berval cr_string;
+ int cr_len;
+ /* Component Indexing */
+ int cr_asn_type_id;
+ slap_mask_t cr_indexmask;
+ AttributeDescription* cr_ad;
+ BerVarray cr_nvals;
+ ComponentReference* cr_next;
+};
+
+struct ComponentAssertion {
+ ComponentReference *ca_comp_ref;
+ ber_int_t ca_use_def;
+ MatchingRule *ca_ma_rule;
+ struct berval ca_ma_value;
+ ComponentData ca_comp_data; /* componentized assertion */
+ ComponentFilter *ca_cf;
+ MatchingRuleAssertion *ca_mra;
+};
+
+struct ComponentFilter {
+ ber_tag_t cf_choice;
+ union cf_un_u {
+ ber_int_t cf_un_result;
+ ComponentAssertion *cf_un_ca;
+ ComponentFilter *cf_un_complex;
+ } cf_un;
+
+#define cf_ca cf_un.cf_un_ca
+#define cf_result cf_un.cf_un_result
+#define cf_and cf_un.cf_un_complex
+#define cf_or cf_un.cf_un_complex
+#define cf_not cf_un.cf_un_complex
+#define cf_any cf_un.cf_un_complex
+
+ ComponentFilter *cf_next;
+};
+
+struct ComponentAssertionValue {
+ char* cav_buf;
+ char* cav_ptr;
+ char* cav_end;
+};
+
+typedef int encoder_func LDAP_P((
+ void* b,
+ void* comp));
+
+typedef int gser_decoder_func LDAP_P((
+ void* mem_op,
+ void* b,
+ ComponentSyntaxInfo** comp_syn_info,
+ int* len,
+ int mode));
+
+typedef int comp_free_func LDAP_P((
+ void* b));
+
+typedef int ber_decoder_func LDAP_P((
+ void* mem_op,
+ void* b,
+ int tag,
+ int elmtLen,
+ ComponentSyntaxInfo* comp_syn_info,
+ int* len,
+ int mode));
+
+typedef int ber_tag_decoder_func LDAP_P((
+ void* mem_op,
+ void* b,
+ ComponentSyntaxInfo* comp_syn_info,
+ int* len,
+ int mode));
+
+typedef void* extract_component_from_id_func LDAP_P((
+ void* mem_op,
+ ComponentReference* cr,
+ void* comp ));
+
+typedef void* convert_attr_to_comp_func LDAP_P ((
+ Attribute* a,
+ Syntax* syn,
+ struct berval* bv ));
+
+typedef void* alloc_nibble_func LDAP_P ((
+ int initial_size,
+ int increment_size ));
+
+typedef void free_nibble_func LDAP_P ((
+ void* nm ));
+
+typedef void convert_assert_to_comp_func LDAP_P ((
+ void *mem_op,
+ ComponentSyntaxInfo* csi_attr,
+ struct berval* bv,
+ ComponentSyntaxInfo** csi,
+ int* len,
+ int mode ));
+
+typedef int convert_asn_to_ldap_func LDAP_P ((
+ ComponentSyntaxInfo* csi,
+ struct berval *bv ));
+
+typedef void free_component_func LDAP_P ((
+ void* mem_op));
+
+typedef int test_component_func LDAP_P ((
+ void* attr_mem_op,
+ void* assert_mem_op,
+ ComponentSyntaxInfo* csi,
+ ComponentAssertion* ca));
+
+typedef void* test_membership_func LDAP_P ((
+ void* in ));
+
+typedef void* get_component_info_func LDAP_P ((
+ int in ));
+
+typedef int component_encoder_func LDAP_P ((
+ void* mem_op,
+ ComponentSyntaxInfo* csi,
+ struct berval* nvals ));
+
+typedef int allcomponent_matching_func LDAP_P((
+ char* oid,
+ ComponentSyntaxInfo* comp1,
+ ComponentSyntaxInfo* comp));
+
+struct ComponentDesc {
+ /* Don't change the order of following four fields */
+ int cd_tag;
+ AttributeType *cd_comp_type;
+ struct berval cd_ad_type; /* ad_type, ad_cname */
+ struct berval cd_ad_cname; /* ad_type, ad_cname */
+ unsigned cd_flags; /* ad_flags */
+ int cd_type;
+ int cd_type_id;
+ encoder_func *cd_ldap_encoder;
+ encoder_func *cd_gser_encoder;
+ encoder_func *cd_ber_encoder;
+ gser_decoder_func *cd_gser_decoder;
+ ber_decoder_func *cd_ber_decoder;
+ comp_free_func *cd_free;
+ extract_component_from_id_func* cd_extract_i;
+ allcomponent_matching_func *cd_all_match;
+};
+
+struct ComponentSyntaxInfo {
+ Syntax *csi_syntax;
+ ComponentDesc *csi_comp_desc;
+};
+
+#endif /* LDAP_COMP_MATCH */
+
+#ifdef SLAP_ZONE_ALLOC
+#define SLAP_ZONE_SIZE 0x80000 /* 512KB */
+#define SLAP_ZONE_SHIFT 19
+#define SLAP_ZONE_INITSIZE 0x800000 /* 8MB */
+#define SLAP_ZONE_MAXSIZE 0x80000000/* 2GB */
+#define SLAP_ZONE_DELTA 0x800000 /* 8MB */
+#define SLAP_ZONE_ZOBLOCK 256
+
+struct zone_object {
+ void *zo_ptr;
+ int zo_siz;
+ int zo_idx;
+ int zo_blockhead;
+ LDAP_LIST_ENTRY(zone_object) zo_link;
+};
+
+struct zone_latency_history {
+ double zlh_latency;
+ LDAP_STAILQ_ENTRY(zone_latency_history) zlh_next;
+};
+
+struct zone_heap {
+ int zh_fd;
+ int zh_zonesize;
+ int zh_zoneorder;
+ int zh_numzones;
+ int zh_maxzones;
+ int zh_deltazones;
+ void **zh_zones;
+ ldap_pvt_thread_rdwr_t *zh_znlock;
+ Avlnode *zh_zonetree;
+ unsigned char ***zh_maps;
+ int *zh_seqno;
+ LDAP_LIST_HEAD( zh_freelist, zone_object ) *zh_free;
+ LDAP_LIST_HEAD( zh_so, zone_object ) zh_zopool;
+ ldap_pvt_thread_mutex_t zh_mutex;
+ ldap_pvt_thread_rdwr_t zh_lock;
+ double zh_ema_latency;
+ unsigned long zh_ema_samples;
+ LDAP_STAILQ_HEAD( zh_latency_history, zone_latency_history )
+ zh_latency_history_queue;
+ int zh_latency_history_qlen;
+ int zh_latency_jump;
+ int zh_swapping;
+};
+#endif
+
+#define SLAP_BACKEND_INIT_MODULE(b) \
+ static BackendInfo bi; \
+ int \
+ init_module( int argc, char *argv[] ) \
+ { \
+ bi.bi_type = #b ; \
+ bi.bi_init = b ## _back_initialize; \
+ backend_add( &bi ); \
+ return 0; \
+ }
+
+typedef int (OV_init)(void);
+typedef struct slap_oinit_t {
+ const char *ov_type;
+ OV_init *ov_init;
+} OverlayInit;
+
+LDAP_END_DECL
+
+#include "proto-slap.h"
+
+#endif /* _SLAP_H_ */
diff --git a/servers/slapd/slapacl.c b/servers/slapd/slapacl.c
new file mode 100644
index 0000000..f5bdd09
--- /dev/null
+++ b/servers/slapd/slapacl.c
@@ -0,0 +1,411 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2004 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+
+#include <lber.h>
+#include <ldif.h>
+#include <lutil.h>
+
+#include "slapcommon.h"
+
+static int
+print_access(
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ struct berval *nval )
+{
+ int rc;
+ slap_mask_t mask;
+ char accessmaskbuf[ACCESSMASK_MAXLEN];
+
+ rc = access_allowed_mask( op, e, desc, nval, ACL_AUTH, NULL, &mask );
+
+ fprintf( stderr, "%s%s%s: %s\n",
+ desc->ad_cname.bv_val,
+ ( val && !BER_BVISNULL( val ) ) ? "=" : "",
+ ( val && !BER_BVISNULL( val ) ) ?
+ ( desc == slap_schema.si_ad_userPassword ?
+ "****" : val->bv_val ) : "",
+ accessmask2str( mask, accessmaskbuf, 1 ) );
+
+ return rc;
+}
+
+int
+slapacl( int argc, char **argv )
+{
+ int rc = EXIT_SUCCESS;
+ const char *progname = "slapacl";
+ Connection conn = { 0 };
+ Listener listener;
+ OperationBuffer opbuf;
+ Operation *op = NULL;
+ Entry e = { 0 }, *ep = &e;
+ char *attr = NULL;
+ int doclose = 0;
+ BackendDB *bd;
+ void *thrctx;
+
+ slap_tool_init( progname, SLAPACL, argc, argv );
+
+ if ( !dryrun ) {
+ int i = 0;
+
+ LDAP_STAILQ_FOREACH( bd, &backendDB, be_next ) {
+ if ( bd != be && backend_startup( bd ) ) {
+ fprintf( stderr, "backend_startup(#%d%s%s) failed\n",
+ i,
+ bd->be_suffix ? ": " : "",
+ bd->be_suffix ? bd->be_suffix[0].bv_val : "" );
+ rc = 1;
+ goto destroy;
+ }
+
+ i++;
+ }
+ }
+
+ argv = &argv[ optind ];
+ argc -= optind;
+
+ thrctx = ldap_pvt_thread_pool_context();
+ connection_fake_init( &conn, &opbuf, thrctx );
+ op = &opbuf.ob_op;
+ op->o_tmpmemctx = NULL;
+
+ conn.c_listener = &listener;
+ conn.c_listener_url = listener_url;
+ conn.c_peer_domain = peer_domain;
+ conn.c_peer_name = peer_name;
+ conn.c_sock_name = sock_name;
+ op->o_ssf = ssf;
+ op->o_transport_ssf = transport_ssf;
+ op->o_tls_ssf = tls_ssf;
+ op->o_sasl_ssf = sasl_ssf;
+
+ if ( !BER_BVISNULL( &authcID ) ) {
+ if ( !BER_BVISNULL( &authcDN ) ) {
+ fprintf( stderr, "both authcID=\"%s\" "
+ "and authcDN=\"%s\" provided\n",
+ authcID.bv_val, authcDN.bv_val );
+ rc = 1;
+ goto destroy;
+ }
+
+ rc = slap_sasl_getdn( &conn, op, &authcID, NULL,
+ &authcDN, SLAP_GETDN_AUTHCID );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "authcID: <%s> check failed %d (%s)\n",
+ authcID.bv_val, rc,
+ ldap_err2string( rc ) );
+ rc = 1;
+ goto destroy;
+ }
+
+ } else if ( !BER_BVISNULL( &authcDN ) ) {
+ struct berval ndn;
+
+ rc = dnNormalize( 0, NULL, NULL, &authcDN, &ndn, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "autchDN=\"%s\" normalization failed %d (%s)\n",
+ authcDN.bv_val, rc,
+ ldap_err2string( rc ) );
+ rc = 1;
+ goto destroy;
+ }
+ ch_free( authcDN.bv_val );
+ authcDN = ndn;
+ }
+
+ if ( !BER_BVISNULL( &authzID ) ) {
+ if ( !BER_BVISNULL( &authzDN ) ) {
+ fprintf( stderr, "both authzID=\"%s\" "
+ "and authzDN=\"%s\" provided\n",
+ authzID.bv_val, authzDN.bv_val );
+ rc = 1;
+ goto destroy;
+ }
+
+ rc = slap_sasl_getdn( &conn, op, &authzID, NULL,
+ &authzDN, SLAP_GETDN_AUTHZID );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "authzID: <%s> check failed %d (%s)\n",
+ authzID.bv_val, rc,
+ ldap_err2string( rc ) );
+ rc = 1;
+ goto destroy;
+ }
+
+ } else if ( !BER_BVISNULL( &authzDN ) ) {
+ struct berval ndn;
+
+ rc = dnNormalize( 0, NULL, NULL, &authzDN, &ndn, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "autchDN=\"%s\" normalization failed %d (%s)\n",
+ authzDN.bv_val, rc,
+ ldap_err2string( rc ) );
+ rc = 1;
+ goto destroy;
+ }
+ ch_free( authzDN.bv_val );
+ authzDN = ndn;
+ }
+
+
+ if ( !BER_BVISNULL( &authcDN ) ) {
+ fprintf( stderr, "authcDN: \"%s\"\n", authcDN.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &authzDN ) ) {
+ fprintf( stderr, "authzDN: \"%s\"\n", authzDN.bv_val );
+ }
+
+ if ( !BER_BVISNULL( &authzDN ) ) {
+ op->o_dn = authzDN;
+ op->o_ndn = authzDN;
+
+ if ( !BER_BVISNULL( &authcDN ) ) {
+ op->o_conn->c_dn = authcDN;
+ op->o_conn->c_ndn = authcDN;
+
+ } else {
+ op->o_conn->c_dn = authzDN;
+ op->o_conn->c_ndn = authzDN;
+ }
+
+ } else if ( !BER_BVISNULL( &authcDN ) ) {
+ op->o_conn->c_dn = authcDN;
+ op->o_conn->c_ndn = authcDN;
+ op->o_dn = authcDN;
+ op->o_ndn = authcDN;
+ }
+
+ assert( !BER_BVISNULL( &baseDN ) );
+ rc = dnPrettyNormal( NULL, &baseDN, &e.e_name, &e.e_nname, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "base=\"%s\" normalization failed %d (%s)\n",
+ baseDN.bv_val, rc,
+ ldap_err2string( rc ) );
+ rc = 1;
+ goto destroy;
+ }
+
+ op->o_bd = be;
+ if ( op->o_bd == NULL ) {
+ /* NOTE: if no database could be found (e.g. because
+ * accessing the rootDSE or so), use the frontendDB
+ * rules; might need work */
+ op->o_bd = frontendDB;
+ }
+
+ if ( !dryrun ) {
+ ID id;
+
+ if ( be == NULL ) {
+ fprintf( stderr, "%s: no target database "
+ "has been found for baseDN=\"%s\"; "
+ "you may try with \"-u\" (dry run).\n",
+ baseDN.bv_val, progname );
+ rc = 1;
+ goto destroy;
+ }
+
+ if ( !be->be_entry_open ||
+ !be->be_entry_close ||
+ !be->be_dn2id_get ||
+ !be->be_entry_get )
+ {
+ fprintf( stderr, "%s: target database "
+ "doesn't support necessary operations; "
+ "you may try with \"-u\" (dry run).\n",
+ progname );
+ rc = 1;
+ goto destroy;
+ }
+
+ if ( be->be_entry_open( be, 0 ) != 0 ) {
+ fprintf( stderr, "%s: could not open database.\n",
+ progname );
+ rc = 1;
+ goto destroy;
+ }
+
+ doclose = 1;
+
+ id = be->be_dn2id_get( be, &e.e_nname );
+ if ( id == NOID ) {
+ fprintf( stderr, "%s: unable to fetch ID of DN \"%s\"\n",
+ progname, e.e_nname.bv_val );
+ rc = 1;
+ goto destroy;
+ }
+ ep = be->be_entry_get( be, id );
+ if ( ep == NULL ) {
+ fprintf( stderr, "%s: unable to fetch entry \"%s\" (%lu)\n",
+ progname, e.e_nname.bv_val, id );
+ rc = 1;
+ goto destroy;
+
+ }
+
+ if ( argc == 0 ) {
+ Attribute *a;
+
+ (void)print_access( op, ep, slap_schema.si_ad_entry, NULL, NULL );
+ (void)print_access( op, ep, slap_schema.si_ad_children, NULL, NULL );
+
+ for ( a = ep->e_attrs; a; a = a->a_next ) {
+ int i;
+
+ for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
+ (void)print_access( op, ep, a->a_desc,
+ &a->a_vals[ i ],
+ &a->a_nvals[ i ] );
+ }
+ }
+ }
+ }
+
+ for ( ; argc--; argv++ ) {
+ slap_mask_t mask;
+ AttributeDescription *desc = NULL;
+ struct berval val = BER_BVNULL,
+ *valp = NULL;
+ const char *text;
+ char accessmaskbuf[ACCESSMASK_MAXLEN];
+ char *accessstr;
+ slap_access_t access = ACL_AUTH;
+
+ if ( attr == NULL ) {
+ attr = argv[ 0 ];
+ }
+
+ val.bv_val = strchr( attr, ':' );
+ if ( val.bv_val != NULL ) {
+ val.bv_val[0] = '\0';
+ val.bv_val++;
+ val.bv_len = strlen( val.bv_val );
+ valp = &val;
+ }
+
+ accessstr = strchr( attr, '/' );
+ if ( accessstr != NULL ) {
+ int invalid = 0;
+
+ accessstr[0] = '\0';
+ accessstr++;
+ access = str2access( accessstr );
+ switch ( access ) {
+ case ACL_INVALID_ACCESS:
+ fprintf( stderr, "unknown access \"%s\" for attribute \"%s\"\n",
+ accessstr, attr );
+ invalid = 1;
+ break;
+
+ case ACL_NONE:
+ fprintf( stderr, "\"none\" not allowed for attribute \"%s\"\n",
+ attr );
+ invalid = 1;
+ break;
+
+ default:
+ break;
+ }
+
+ if ( invalid ) {
+ if ( continuemode ) {
+ continue;
+ }
+ break;
+ }
+ }
+
+ rc = slap_str2ad( attr, &desc, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "slap_str2ad(%s) failed %d (%s)\n",
+ attr, rc, ldap_err2string( rc ) );
+ if ( continuemode ) {
+ continue;
+ }
+ break;
+ }
+
+ rc = access_allowed_mask( op, ep, desc, valp, access,
+ NULL, &mask );
+
+ if ( accessstr ) {
+ fprintf( stderr, "%s access to %s%s%s: %s\n",
+ accessstr,
+ desc->ad_cname.bv_val,
+ val.bv_val ? "=" : "",
+ val.bv_val ? val.bv_val : "",
+ rc ? "ALLOWED" : "DENIED" );
+
+ } else {
+ fprintf( stderr, "%s%s%s: %s\n",
+ desc->ad_cname.bv_val,
+ val.bv_val ? "=" : "",
+ val.bv_val ? val.bv_val : "",
+ accessmask2str( mask, accessmaskbuf, 1 ) );
+ }
+ rc = 0;
+ attr = NULL;
+ }
+
+destroy:;
+ if ( !BER_BVISNULL( &e.e_name ) ) {
+ ber_memfree( e.e_name.bv_val );
+ }
+ if ( !BER_BVISNULL( &e.e_nname ) ) {
+ ber_memfree( e.e_nname.bv_val );
+ }
+ if ( !dryrun && be ) {
+ if ( ep && ep != &e ) {
+ be_entry_release_r( op, ep );
+ }
+ if ( doclose ) {
+ be->be_entry_close( be );
+ }
+
+ LDAP_STAILQ_FOREACH( bd, &backendDB, be_next ) {
+ if ( bd != be ) {
+ backend_shutdown( bd );
+ }
+ }
+ }
+
+ if ( slap_tool_destroy())
+ rc = EXIT_FAILURE;
+
+ return rc;
+}
+
diff --git a/servers/slapd/slapadd.c b/servers/slapd/slapadd.c
new file mode 100644
index 0000000..e29c3b0
--- /dev/null
+++ b/servers/slapd/slapadd.c
@@ -0,0 +1,528 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1998-2003 Kurt D. Zeilenga.
+ * Portions Copyright 2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Kurt Zeilenga for inclusion
+ * in OpenLDAP Software. Additional significant contributors include
+ * Jong Hyuk Choi
+ * Pierangelo Masarati
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+
+#include <lber.h>
+#include <ldif.h>
+#include <lutil.h>
+#include <lutil_meter.h>
+#include <sys/stat.h>
+
+#include "slapcommon.h"
+
+#ifdef _WIN32
+# ifdef __WIN64__
+# define ftello(fp) _ftelli64(fp)
+# else
+/* Ideally we would use _ftelli64 but that was only available
+ * starting in MSVCR80.DLL. The approach used here is inaccurate
+ * because returning the underlying file handle's file pointer
+ * doesn't take the stdio buffer offset into account. But, it
+ * works with all versions of MSVCRT.
+ */
+# define ftello(fp) _telli64(fileno(fp))
+# endif
+#endif
+
+extern int slap_DN_strict; /* dn.c */
+
+static char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE ];
+
+typedef struct Erec {
+ Entry *e;
+ unsigned long lineno;
+ unsigned long nextline;
+} Erec;
+
+typedef struct Trec {
+ Entry *e;
+ unsigned long lineno;
+ unsigned long nextline;
+ int rc;
+ int ready;
+} Trec;
+
+static Trec trec;
+static unsigned long sid = SLAP_SYNC_SID_MAX + 1;
+static int checkvals;
+static int enable_meter;
+static lutil_meter_t meter;
+static const char *progname = "slapadd";
+static OperationBuffer opbuf;
+static char *buf;
+static int lmax;
+
+static ldap_pvt_thread_mutex_t add_mutex;
+static ldap_pvt_thread_cond_t add_cond;
+static int add_stop;
+
+/* returns:
+ * 1: got a record
+ * 0: EOF
+ * -1: read failure
+ * -2: parse failure
+ */
+static int
+getrec0(Erec *erec)
+{
+ const char *text;
+ int ldifrc;
+ char textbuf[SLAP_TEXT_BUFLEN] = { '\0' };
+ size_t textlen = sizeof textbuf;
+ struct berval csn;
+ Operation *op = &opbuf.ob_op;
+ op->o_hdr = &opbuf.ob_hdr;
+
+again:
+ erec->lineno = erec->nextline+1;
+ /* nextline is the line number of the end of the current entry */
+ ldifrc = ldif_read_record( ldiffp, &erec->nextline, &buf, &lmax );
+ if (ldifrc < 1)
+ return ldifrc < 0 ? -1 : 0;
+ {
+ BackendDB *bd;
+ Entry *e;
+ int prev_DN_strict;
+
+ if ( erec->lineno < jumpline )
+ goto again;
+
+ if ( !dbnum ) {
+ prev_DN_strict = slap_DN_strict;
+ slap_DN_strict = 0;
+ }
+ e = str2entry2( buf, checkvals );
+ if ( !dbnum ) {
+ slap_DN_strict = prev_DN_strict;
+ }
+
+ if ( enable_meter )
+ lutil_meter_update( &meter,
+ ftello( ldiffp->fp ),
+ 0);
+
+ if( e == NULL ) {
+ fprintf( stderr, "%s: could not parse entry (line=%lu)\n",
+ progname, erec->lineno );
+ return -2;
+ }
+
+ /* make sure the DN is not empty */
+ if( BER_BVISEMPTY( &e->e_nname ) &&
+ !BER_BVISEMPTY( be->be_nsuffix ))
+ {
+ fprintf( stderr, "%s: line %lu: "
+ "cannot add entry with empty dn=\"%s\"",
+ progname, erec->lineno, e->e_dn );
+ bd = select_backend( &e->e_nname, nosubordinates );
+ if ( bd ) {
+ BackendDB *bdtmp;
+ int dbidx = 0;
+ LDAP_STAILQ_FOREACH( bdtmp, &backendDB, be_next ) {
+ if ( bdtmp == bd ) break;
+ dbidx++;
+ }
+
+ assert( bdtmp != NULL );
+
+ fprintf( stderr, "; did you mean to use database #%d (%s)?",
+ dbidx,
+ bd->be_suffix[0].bv_val );
+
+ }
+ fprintf( stderr, "\n" );
+ entry_free( e );
+ return -2;
+ }
+
+ /* check backend */
+ bd = select_backend( &e->e_nname, nosubordinates );
+ if ( bd != be ) {
+ fprintf( stderr, "%s: line %lu: "
+ "database #%d (%s) not configured to hold \"%s\"",
+ progname, erec->lineno,
+ dbnum,
+ be->be_suffix[0].bv_val,
+ e->e_dn );
+ if ( bd ) {
+ BackendDB *bdtmp;
+ int dbidx = 0;
+ LDAP_STAILQ_FOREACH( bdtmp, &backendDB, be_next ) {
+ if ( bdtmp == bd ) break;
+ dbidx++;
+ }
+
+ assert( bdtmp != NULL );
+
+ fprintf( stderr, "; did you mean to use database #%d (%s)?",
+ dbidx,
+ bd->be_suffix[0].bv_val );
+
+ } else {
+ fprintf( stderr, "; no database configured for that naming context" );
+ }
+ fprintf( stderr, "\n" );
+ entry_free( e );
+ return -2;
+ }
+
+ if ( slap_tool_entry_check( progname, op, e, erec->lineno, &text, textbuf, textlen ) !=
+ LDAP_SUCCESS ) {
+ entry_free( e );
+ return -2;
+ }
+
+ if ( SLAP_LASTMOD(be) ) {
+ time_t now = slap_get_time();
+ char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
+ struct berval vals[ 2 ];
+
+ struct berval name, timestamp;
+
+ struct berval nvals[ 2 ];
+ struct berval nname;
+ char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+
+ enum {
+ GOT_NONE = 0x0,
+ GOT_CSN = 0x1,
+ GOT_UUID = 0x2,
+ GOT_ALL = (GOT_CSN|GOT_UUID)
+ } got = GOT_ALL;
+
+ vals[1].bv_len = 0;
+ vals[1].bv_val = NULL;
+
+ nvals[1].bv_len = 0;
+ nvals[1].bv_val = NULL;
+
+ csn.bv_len = ldap_pvt_csnstr( csnbuf, sizeof( csnbuf ), csnsid, 0 );
+ csn.bv_val = csnbuf;
+
+ timestamp.bv_val = timebuf;
+ timestamp.bv_len = sizeof(timebuf);
+
+ slap_timestamp( &now, &timestamp );
+
+ if ( BER_BVISEMPTY( &be->be_rootndn ) ) {
+ BER_BVSTR( &name, SLAPD_ANONYMOUS );
+ nname = name;
+ } else {
+ name = be->be_rootdn;
+ nname = be->be_rootndn;
+ }
+
+ if( attr_find( e->e_attrs, slap_schema.si_ad_entryUUID )
+ == NULL )
+ {
+ got &= ~GOT_UUID;
+ vals[0].bv_len = lutil_uuidstr( uuidbuf, sizeof( uuidbuf ) );
+ vals[0].bv_val = uuidbuf;
+ attr_merge_normalize_one( e, slap_schema.si_ad_entryUUID, vals, NULL );
+ }
+
+ if( attr_find( e->e_attrs, slap_schema.si_ad_creatorsName )
+ == NULL )
+ {
+ vals[0] = name;
+ nvals[0] = nname;
+ attr_merge( e, slap_schema.si_ad_creatorsName, vals, nvals );
+ }
+
+ if( attr_find( e->e_attrs, slap_schema.si_ad_createTimestamp )
+ == NULL )
+ {
+ vals[0] = timestamp;
+ attr_merge( e, slap_schema.si_ad_createTimestamp, vals, NULL );
+ }
+
+ if( attr_find( e->e_attrs, slap_schema.si_ad_entryCSN )
+ == NULL )
+ {
+ got &= ~GOT_CSN;
+ vals[0] = csn;
+ attr_merge( e, slap_schema.si_ad_entryCSN, vals, NULL );
+ }
+
+ if( attr_find( e->e_attrs, slap_schema.si_ad_modifiersName )
+ == NULL )
+ {
+ vals[0] = name;
+ nvals[0] = nname;
+ attr_merge( e, slap_schema.si_ad_modifiersName, vals, nvals );
+ }
+
+ if( attr_find( e->e_attrs, slap_schema.si_ad_modifyTimestamp )
+ == NULL )
+ {
+ vals[0] = timestamp;
+ attr_merge( e, slap_schema.si_ad_modifyTimestamp, vals, NULL );
+ }
+
+ if ( SLAP_SINGLE_SHADOW(be) && got != GOT_ALL ) {
+ Debug(LDAP_DEBUG_ANY,
+ "%s: warning, missing attrs %s%s%s from entry dn=\"%s\"\n",
+ progname,
+ (!(got & GOT_UUID) ? slap_schema.si_ad_entryUUID->ad_cname.bv_val : ""),
+ (!(got & GOT_CSN) ? "," : ""),
+ (!(got & GOT_CSN) ? slap_schema.si_ad_entryCSN->ad_cname.bv_val : ""),
+ e->e_name.bv_val );
+ }
+
+ sid = slap_tool_update_ctxcsn_check( progname, e );
+ }
+ erec->e = e;
+ }
+ return 1;
+}
+
+static void *
+getrec_thr(void *ctx)
+{
+ ldap_pvt_thread_mutex_lock( &add_mutex );
+ while (!add_stop) {
+ trec.rc = getrec0((Erec *)&trec);
+ trec.ready = 1;
+ while (trec.ready)
+ ldap_pvt_thread_cond_wait( &add_cond, &add_mutex );
+ /* eof or read failure */
+ if ( trec.rc == 0 || trec.rc == -1 )
+ break;
+ }
+ ldap_pvt_thread_mutex_unlock( &add_mutex );
+ return NULL;
+}
+
+static int ldif_threaded;
+
+static int
+getrec(Erec *erec)
+{
+ int rc;
+ if ( !ldif_threaded )
+ return getrec0(erec);
+
+ while (!trec.ready)
+ ldap_pvt_thread_yield();
+ erec->e = trec.e;
+ erec->lineno = trec.lineno;
+ erec->nextline = trec.nextline;
+ trec.ready = 0;
+ rc = trec.rc;
+ ldap_pvt_thread_mutex_lock( &add_mutex );
+ ldap_pvt_thread_mutex_unlock( &add_mutex );
+ ldap_pvt_thread_cond_signal( &add_cond );
+ return rc;
+}
+
+int
+slapadd( int argc, char **argv )
+{
+ char textbuf[SLAP_TEXT_BUFLEN] = { '\0' };
+ size_t textlen = sizeof textbuf;
+ Erec erec;
+ struct berval bvtext;
+ ldap_pvt_thread_t thr;
+ ID id;
+ Entry *prev = NULL;
+
+ int ldifrc;
+ int rc = EXIT_SUCCESS;
+
+ struct stat stat_buf;
+
+ /* default "000" */
+ csnsid = 0;
+
+ if ( isatty (2) ) enable_meter = 1;
+ slap_tool_init( progname, SLAPADD, argc, argv );
+
+ if( !be->be_entry_open ||
+ !be->be_entry_close ||
+ !be->be_entry_put ||
+ (update_ctxcsn &&
+ (!be->be_dn2id_get ||
+ !be->be_entry_get ||
+ !be->be_entry_modify)) )
+ {
+ fprintf( stderr, "%s: database doesn't support necessary operations.\n",
+ progname );
+ if ( dryrun ) {
+ fprintf( stderr, "\t(dry) continuing...\n" );
+
+ } else {
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ checkvals = (slapMode & SLAP_TOOL_QUICK) ? 0 : 1;
+
+ /* do not check values in quick mode */
+ if ( slapMode & SLAP_TOOL_QUICK ) {
+ if ( slapMode & SLAP_TOOL_VALUE_CHECK ) {
+ fprintf( stderr, "%s: value-check incompatible with quick mode; disabled.\n", progname );
+ slapMode &= ~SLAP_TOOL_VALUE_CHECK;
+ }
+ }
+
+ /* enforce schema checking unless not disabled */
+ if ( (slapMode & SLAP_TOOL_NO_SCHEMA_CHECK) == 0) {
+ SLAP_DBFLAGS(be) &= ~(SLAP_DBFLAG_NO_SCHEMA_CHECK);
+ }
+
+ if( !dryrun && be->be_entry_open( be, 1 ) != 0 ) {
+ fprintf( stderr, "%s: could not open database.\n",
+ progname );
+ exit( EXIT_FAILURE );
+ }
+
+ (void)slap_tool_update_ctxcsn_init();
+
+ if ( enable_meter
+#ifdef LDAP_DEBUG
+ /* tools default to "none" */
+ && slap_debug == LDAP_DEBUG_NONE
+#endif
+ && !fstat ( fileno ( ldiffp->fp ), &stat_buf )
+ && S_ISREG(stat_buf.st_mode) ) {
+ enable_meter = !lutil_meter_open(
+ &meter,
+ &lutil_meter_text_display,
+ &lutil_meter_linear_estimator,
+ stat_buf.st_size);
+ } else {
+ enable_meter = 0;
+ }
+
+ if ( slap_tool_thread_max > 1 ) {
+ ldap_pvt_thread_mutex_init( &add_mutex );
+ ldap_pvt_thread_cond_init( &add_cond );
+ ldap_pvt_thread_create( &thr, 0, getrec_thr, NULL );
+ ldif_threaded = 1;
+ }
+
+ erec.nextline = 0;
+ erec.e = NULL;
+
+ for (;;) {
+ ldifrc = getrec( &erec );
+ if ( ldifrc < 1 ) {
+ if ( ldifrc == -2 && continuemode )
+ continue;
+ break;
+ }
+
+ if ( !dryrun ) {
+ /*
+ * Initialize text buffer
+ */
+ bvtext.bv_len = textlen;
+ bvtext.bv_val = textbuf;
+ bvtext.bv_val[0] = '\0';
+
+ id = be->be_entry_put( be, erec.e, &bvtext );
+ if( id == NOID ) {
+ fprintf( stderr, "%s: could not add entry dn=\"%s\" "
+ "(line=%lu): %s\n", progname, erec.e->e_dn,
+ erec.lineno, bvtext.bv_val );
+ rc = EXIT_FAILURE;
+ if( continuemode ) {
+ if ( prev ) entry_free( prev );
+ prev = erec.e;
+ continue;
+ }
+ break;
+ }
+ if ( verbose )
+ fprintf( stderr, "added: \"%s\" (%08lx)\n",
+ erec.e->e_dn, (long) id );
+ } else {
+ if ( verbose )
+ fprintf( stderr, "added: \"%s\"\n",
+ erec.e->e_dn );
+ }
+
+ if ( prev ) entry_free( prev );
+ prev = erec.e;
+ }
+
+ if ( ldif_threaded ) {
+ ldap_pvt_thread_mutex_lock( &add_mutex );
+ add_stop = 1;
+ trec.ready = 0;
+ ldap_pvt_thread_cond_signal( &add_cond );
+ ldap_pvt_thread_mutex_unlock( &add_mutex );
+ ldap_pvt_thread_join( thr, NULL );
+ }
+ if ( erec.e ) entry_free( erec.e );
+
+ if ( ldifrc < 0 )
+ rc = EXIT_FAILURE;
+
+ bvtext.bv_len = textlen;
+ bvtext.bv_val = textbuf;
+ bvtext.bv_val[0] = '\0';
+
+ if ( enable_meter ) {
+ lutil_meter_update( &meter, ftello( ldiffp->fp ), 1);
+ lutil_meter_close( &meter );
+ }
+
+ if ( rc == EXIT_SUCCESS ) {
+ rc = slap_tool_update_ctxcsn( progname, sid, &bvtext );
+ }
+
+ ch_free( buf );
+
+ if ( !dryrun ) {
+ if ( enable_meter ) {
+ fprintf( stderr, "Closing DB..." );
+ }
+ if( be->be_entry_close( be ) ) {
+ rc = EXIT_FAILURE;
+ }
+
+ if( be->be_sync ) {
+ be->be_sync( be );
+ }
+ if ( enable_meter ) {
+ fprintf( stderr, "\n" );
+ }
+ }
+
+ if ( slap_tool_destroy())
+ rc = EXIT_FAILURE;
+
+ return rc;
+}
+
diff --git a/servers/slapd/slapauth.c b/servers/slapd/slapauth.c
new file mode 100644
index 0000000..19cdcaf
--- /dev/null
+++ b/servers/slapd/slapauth.c
@@ -0,0 +1,177 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2004 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+
+#include <lber.h>
+#include <ldif.h>
+#include <lutil.h>
+
+#include "slapcommon.h"
+
+static int
+do_check( Connection *c, Operation *op, struct berval *id )
+{
+ struct berval authcdn;
+ int rc;
+
+ rc = slap_sasl_getdn( c, op, id, realm, &authcdn, SLAP_GETDN_AUTHCID );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "ID: <%s> check failed %d (%s)\n",
+ id->bv_val, rc,
+ ldap_err2string( rc ) );
+ rc = 1;
+
+ } else {
+ if ( !BER_BVISNULL( &authzID ) ) {
+ rc = slap_sasl_authorized( op, &authcdn, &authzID );
+
+ fprintf( stderr,
+ "ID: <%s>\n"
+ "authcDN: <%s>\n"
+ "authzDN: <%s>\n"
+ "authorization %s\n",
+ id->bv_val,
+ authcdn.bv_val,
+ authzID.bv_val,
+ rc == LDAP_SUCCESS ? "OK" : "failed" );
+
+ } else {
+ fprintf( stderr, "ID: <%s> check succeeded\n"
+ "authcID: <%s>\n",
+ id->bv_val,
+ authcdn.bv_val );
+ op->o_tmpfree( authcdn.bv_val, op->o_tmpmemctx );
+ }
+ rc = 0;
+ }
+
+ return rc;
+}
+
+int
+slapauth( int argc, char **argv )
+{
+ int rc = EXIT_SUCCESS;
+ const char *progname = "slapauth";
+ Connection conn = {0};
+ OperationBuffer opbuf;
+ Operation *op;
+ void *thrctx;
+
+ slap_tool_init( progname, SLAPAUTH, argc, argv );
+
+ argv = &argv[ optind ];
+ argc -= optind;
+
+ thrctx = ldap_pvt_thread_pool_context();
+ connection_fake_init( &conn, &opbuf, thrctx );
+ op = &opbuf.ob_op;
+
+ conn.c_sasl_bind_mech = mech;
+
+ if ( !BER_BVISNULL( &authzID ) ) {
+ struct berval authzdn;
+
+ rc = slap_sasl_getdn( &conn, op, &authzID, NULL, &authzdn,
+ SLAP_GETDN_AUTHZID );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "authzID: <%s> check failed %d (%s)\n",
+ authzID.bv_val, rc,
+ ldap_err2string( rc ) );
+ rc = 1;
+ BER_BVZERO( &authzID );
+ goto destroy;
+ }
+
+ authzID = authzdn;
+ }
+
+
+ if ( !BER_BVISNULL( &authcID ) ) {
+ if ( !BER_BVISNULL( &authzID ) || argc == 0 ) {
+ rc = do_check( &conn, op, &authcID );
+ goto destroy;
+ }
+
+ for ( ; argc--; argv++ ) {
+ struct berval authzdn;
+
+ ber_str2bv( argv[ 0 ], 0, 0, &authzID );
+
+ rc = slap_sasl_getdn( &conn, op, &authzID, NULL, &authzdn,
+ SLAP_GETDN_AUTHZID );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "authzID: <%s> check failed %d (%s)\n",
+ authzID.bv_val, rc,
+ ldap_err2string( rc ) );
+ rc = -1;
+ BER_BVZERO( &authzID );
+ if ( !continuemode ) {
+ goto destroy;
+ }
+ }
+
+ authzID = authzdn;
+
+ rc = do_check( &conn, op, &authcID );
+
+ op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &authzID );
+
+ if ( rc && !continuemode ) {
+ goto destroy;
+ }
+ }
+
+ goto destroy;
+ }
+
+ for ( ; argc--; argv++ ) {
+ struct berval id;
+
+ ber_str2bv( argv[ 0 ], 0, 0, &id );
+
+ rc = do_check( &conn, op, &id );
+
+ if ( rc && !continuemode ) {
+ goto destroy;
+ }
+ }
+
+destroy:;
+ if ( !BER_BVISNULL( &authzID ) ) {
+ op->o_tmpfree( authzID.bv_val, op->o_tmpmemctx );
+ }
+ if ( slap_tool_destroy())
+ rc = EXIT_FAILURE;
+
+ return rc;
+}
+
diff --git a/servers/slapd/slapcat.c b/servers/slapd/slapcat.c
new file mode 100644
index 0000000..6f580e7
--- /dev/null
+++ b/servers/slapd/slapcat.c
@@ -0,0 +1,175 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1998-2003 Kurt D. Zeilenga.
+ * Portions Copyright 2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Kurt Zeilenga for inclusion
+ * in OpenLDAP Software. Additional significant contributors include
+ * Jong Hyuk Choi
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/ctype.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slapcommon.h"
+#include "ldif.h"
+
+static volatile sig_atomic_t gotsig;
+
+static RETSIGTYPE
+slapcat_sig( int sig )
+{
+ gotsig=1;
+}
+
+int
+slapcat( int argc, char **argv )
+{
+ ID id;
+ int rc = EXIT_SUCCESS;
+ Operation op = {0};
+ const char *progname = "slapcat";
+ int requestBSF;
+ int doBSF = 0;
+
+ slap_tool_init( progname, SLAPCAT, argc, argv );
+
+ requestBSF = ( sub_ndn.bv_len || filter );
+
+#ifdef SIGPIPE
+ (void) SIGNAL( SIGPIPE, slapcat_sig );
+#endif
+#ifdef SIGHUP
+ (void) SIGNAL( SIGHUP, slapcat_sig );
+#endif
+ (void) SIGNAL( SIGINT, slapcat_sig );
+ (void) SIGNAL( SIGTERM, slapcat_sig );
+
+ if( !be->be_entry_open ||
+ !be->be_entry_close ||
+ !( be->be_entry_first_x || be->be_entry_first ) ||
+ !be->be_entry_next ||
+ !be->be_entry_get )
+ {
+ fprintf( stderr, "%s: database doesn't support necessary operations.\n",
+ progname );
+ exit( EXIT_FAILURE );
+ }
+
+ if( be->be_entry_open( be, 0 ) != 0 ) {
+ fprintf( stderr, "%s: could not open database.\n",
+ progname );
+ exit( EXIT_FAILURE );
+ }
+
+ op.o_bd = be;
+ if ( !requestBSF && be->be_entry_first ) {
+ id = be->be_entry_first( be );
+
+ } else {
+ if ( be->be_entry_first_x ) {
+ id = be->be_entry_first_x( be,
+ sub_ndn.bv_len ? &sub_ndn : NULL, scope, filter );
+
+ } else {
+ assert( be->be_entry_first != NULL );
+ doBSF = 1;
+ id = be->be_entry_first( be );
+ }
+ }
+
+ for ( ; id != NOID; id = be->be_entry_next( be ) )
+ {
+ char *data;
+ int len;
+ Entry* e;
+
+ if ( gotsig )
+ break;
+
+ e = be->be_entry_get( be, id );
+ if ( e == NULL ) {
+ printf("# no data for entry id=%08lx\n\n", (long) id );
+ rc = EXIT_FAILURE;
+ if ( continuemode == 0 ) {
+ break;
+
+ } else if ( continuemode == 1 ) {
+ continue;
+ }
+
+ /* this is a last resort: linearly scan all ids
+ * trying to recover as much as possible (ITS#6482) */
+ while ( ++id != NOID ) {
+ e = be->be_entry_get( be, id );
+ if ( e != NULL ) break;
+ printf("# no data for entry id=%08lx\n\n", (long) id );
+ }
+
+ if ( e == NULL ) break;
+ }
+
+ if ( doBSF ) {
+ if ( sub_ndn.bv_len && !dnIsSuffixScope( &e->e_nname, &sub_ndn, scope ) )
+ {
+ be_entry_release_r( &op, e );
+ continue;
+ }
+
+
+ if ( filter != NULL ) {
+ int rc = test_filter( NULL, e, filter );
+ if ( rc != LDAP_COMPARE_TRUE ) {
+ be_entry_release_r( &op, e );
+ continue;
+ }
+ }
+ }
+
+ if ( verbose ) {
+ printf( "# id=%08lx\n", (long) id );
+ }
+
+ data = entry2str_wrap( e, &len, ldif_wrap );
+ be_entry_release_r( &op, e );
+
+ if ( data == NULL ) {
+ printf("# bad data for entry id=%08lx\n\n", (long) id );
+ rc = EXIT_FAILURE;
+ if( continuemode ) continue;
+ break;
+ }
+
+ if ( fputs( data, ldiffp->fp ) == EOF ||
+ fputs( "\n", ldiffp->fp ) == EOF ) {
+ fprintf(stderr, "%s: error writing output.\n",
+ progname);
+ rc = EXIT_FAILURE;
+ break;
+ }
+ }
+
+ be->be_entry_close( be );
+
+ if ( slap_tool_destroy())
+ rc = EXIT_FAILURE;
+ return rc;
+}
diff --git a/servers/slapd/slapcommon.c b/servers/slapd/slapcommon.c
new file mode 100644
index 0000000..e58c8be
--- /dev/null
+++ b/servers/slapd/slapcommon.c
@@ -0,0 +1,1228 @@
+/* slapcommon.c - common routine for the slap tools */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1998-2003 Kurt D. Zeilenga.
+ * Portions Copyright 2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Kurt Zeilenga for inclusion
+ * in OpenLDAP Software. Additional significant contributors include
+ * Jong Hyuk Choi
+ * Hallvard B. Furuseth
+ * Howard Chu
+ * Pierangelo Masarati
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+
+#include "slapcommon.h"
+#include "lutil.h"
+#include "ldif.h"
+
+tool_vars tool_globals;
+enum slaptool slapTool;
+
+#ifdef CSRIMALLOC
+static char *leakfilename;
+static FILE *leakfile;
+#endif
+
+static LDIFFP dummy;
+
+#if defined(LDAP_SYSLOG) && defined(LDAP_DEBUG)
+int start_syslog;
+static char **syslog_unknowns;
+#ifdef LOG_LOCAL4
+static int syslogUser = SLAP_DEFAULT_SYSLOG_USER;
+#endif /* LOG_LOCAL4 */
+#endif /* LDAP_DEBUG && LDAP_SYSLOG */
+
+static void
+usage( int tool, const char *progname )
+{
+ char *options = NULL;
+ fprintf( stderr,
+ "usage: %s [-v] [-d debuglevel] [-f configfile] [-F configdir] [-o <name>[=<value>]]",
+ progname );
+
+ switch( tool ) {
+ case SLAPACL:
+ options = "\n\t[-U authcID | -D authcDN] [-X authzID | -o authzDN=<DN>]"
+ "\n\t-b DN [-u] [attr[/access][:value]] [...]\n";
+ break;
+
+ case SLAPADD:
+ options = " [-c]\n\t[-g] [-n databasenumber | -b suffix]\n"
+ "\t[-l ldiffile] [-j linenumber] [-q] [-u] [-s] [-w]\n";
+ break;
+
+ case SLAPAUTH:
+ options = "\n\t[-U authcID] [-X authzID] [-R realm] [-M mech] ID [...]\n";
+ break;
+
+ case SLAPCAT:
+ options = " [-c]\n\t[-g] [-n databasenumber | -b suffix]"
+ " [-l ldiffile] [-a filter] [-s subtree] [-H url]\n";
+ break;
+
+ case SLAPDN:
+ options = "\n\t[-N | -P] DN [...]\n";
+ break;
+
+ case SLAPINDEX:
+ options = " [-c]\n\t[-g] [-n databasenumber | -b suffix] [attr ...] [-q] [-t]\n";
+ break;
+
+ case SLAPMODIFY:
+ options = " [-c]\n\t[-g] [-n databasenumber | -b suffix]\n"
+ "\t[-l ldiffile] [-j linenumber] [-q] [-u] [-s] [-w]\n";
+ break;
+
+ case SLAPTEST:
+ options = " [-n databasenumber] [-u] [-Q]\n";
+ break;
+
+ case SLAPSCHEMA:
+ options = " [-c]\n\t[-g] [-n databasenumber | -b suffix]"
+ " [-l errorfile] [-a filter] [-s subtree] [-H url]\n";
+ break;
+ }
+
+ if ( options != NULL ) {
+ fputs( options, stderr );
+ }
+ exit( EXIT_FAILURE );
+}
+
+static int
+parse_slapopt( int tool, int *mode )
+{
+ size_t len = 0;
+ char *p;
+
+ p = strchr( optarg, '=' );
+ if ( p != NULL ) {
+ len = p - optarg;
+ p++;
+ }
+
+ if ( strncasecmp( optarg, "sockurl", len ) == 0 ) {
+ if ( !BER_BVISNULL( &listener_url ) ) {
+ ber_memfree( listener_url.bv_val );
+ }
+ ber_str2bv( p, 0, 1, &listener_url );
+
+ } else if ( strncasecmp( optarg, "domain", len ) == 0 ) {
+ if ( !BER_BVISNULL( &peer_domain ) ) {
+ ber_memfree( peer_domain.bv_val );
+ }
+ ber_str2bv( p, 0, 1, &peer_domain );
+
+ } else if ( strncasecmp( optarg, "peername", len ) == 0 ) {
+ if ( !BER_BVISNULL( &peer_name ) ) {
+ ber_memfree( peer_name.bv_val );
+ }
+ ber_str2bv( p, 0, 1, &peer_name );
+
+ } else if ( strncasecmp( optarg, "sockname", len ) == 0 ) {
+ if ( !BER_BVISNULL( &sock_name ) ) {
+ ber_memfree( sock_name.bv_val );
+ }
+ ber_str2bv( p, 0, 1, &sock_name );
+
+ } else if ( strncasecmp( optarg, "ssf", len ) == 0 ) {
+ if ( lutil_atou( &ssf, p ) ) {
+ Debug( LDAP_DEBUG_ANY, "unable to parse ssf=\"%s\".\n", p );
+ return -1;
+ }
+
+ } else if ( strncasecmp( optarg, "transport_ssf", len ) == 0 ) {
+ if ( lutil_atou( &transport_ssf, p ) ) {
+ Debug( LDAP_DEBUG_ANY, "unable to parse transport_ssf=\"%s\".\n", p );
+ return -1;
+ }
+
+ } else if ( strncasecmp( optarg, "tls_ssf", len ) == 0 ) {
+ if ( lutil_atou( &tls_ssf, p ) ) {
+ Debug( LDAP_DEBUG_ANY, "unable to parse tls_ssf=\"%s\".\n", p );
+ return -1;
+ }
+
+ } else if ( strncasecmp( optarg, "sasl_ssf", len ) == 0 ) {
+ if ( lutil_atou( &sasl_ssf, p ) ) {
+ Debug( LDAP_DEBUG_ANY, "unable to parse sasl_ssf=\"%s\".\n", p );
+ return -1;
+ }
+
+ } else if ( strncasecmp( optarg, "authzDN", len ) == 0 ) {
+ ber_str2bv( p, 0, 1, &authzDN );
+
+#if defined(LDAP_SYSLOG) && defined(LDAP_DEBUG)
+ } else if ( strncasecmp( optarg, "syslog", len ) == 0 ) {
+ if ( parse_debug_level( p, &ldap_syslog, &syslog_unknowns ) ) {
+ return -1;
+ }
+ start_syslog = 1;
+
+ } else if ( strncasecmp( optarg, "syslog-level", len ) == 0 ) {
+ if ( parse_syslog_level( p, &ldap_syslog_level ) ) {
+ return -1;
+ }
+ start_syslog = 1;
+
+#ifdef LOG_LOCAL4
+ } else if ( strncasecmp( optarg, "syslog-user", len ) == 0 ) {
+ if ( parse_syslog_user( p, &syslogUser ) ) {
+ return -1;
+ }
+ start_syslog = 1;
+#endif /* LOG_LOCAL4 */
+#endif /* LDAP_DEBUG && LDAP_SYSLOG */
+
+ } else if ( strncasecmp( optarg, "schema-check", len ) == 0 ) {
+ switch ( tool ) {
+ case SLAPADD:
+ if ( strcasecmp( p, "yes" ) == 0 ) {
+ *mode &= ~SLAP_TOOL_NO_SCHEMA_CHECK;
+ } else if ( strcasecmp( p, "no" ) == 0 ) {
+ *mode |= SLAP_TOOL_NO_SCHEMA_CHECK;
+ } else {
+ Debug( LDAP_DEBUG_ANY, "unable to parse schema-check=\"%s\".\n", p );
+ return -1;
+ }
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY, "schema-check meaningless for tool.\n" );
+ break;
+ }
+
+ } else if ( strncasecmp( optarg, "value-check", len ) == 0 ) {
+ switch ( tool ) {
+ case SLAPADD:
+ if ( strcasecmp( p, "yes" ) == 0 ) {
+ *mode |= SLAP_TOOL_VALUE_CHECK;
+ } else if ( strcasecmp( p, "no" ) == 0 ) {
+ *mode &= ~SLAP_TOOL_VALUE_CHECK;
+ } else {
+ Debug( LDAP_DEBUG_ANY, "unable to parse value-check=\"%s\".\n", p );
+ return -1;
+ }
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY, "value-check meaningless for tool.\n" );
+ break;
+ }
+
+ } else if ( ( strncasecmp( optarg, "ldif_wrap", len ) == 0 ) ||
+ ( strncasecmp( optarg, "ldif-wrap", len ) == 0 ) ) {
+ switch ( tool ) {
+ case SLAPCAT:
+ if ( strcasecmp( p, "no" ) == 0 ) {
+ ldif_wrap = LDIF_LINE_WIDTH_MAX;
+
+ } else {
+ unsigned int u;
+ if ( lutil_atou( &u, p ) ) {
+ Debug( LDAP_DEBUG_ANY, "unable to parse ldif_wrap=\"%s\".\n", p );
+ return -1;
+ }
+ ldif_wrap = (ber_len_t)u;
+ }
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY, "ldif-wrap meaningless for tool.\n" );
+ break;
+ }
+
+ } else {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * slap_tool_init - initialize slap utility, handle program options.
+ * arguments:
+ * name program name
+ * tool tool code
+ * argc, argv command line arguments
+ */
+
+static int need_shutdown;
+
+void
+slap_tool_init(
+ const char* progname,
+ int tool,
+ int argc, char **argv )
+{
+ char *options;
+ char *conffile = NULL;
+ char *confdir = NULL;
+ struct berval base = BER_BVNULL;
+ char *filterstr = NULL;
+ char *subtree = NULL;
+ char *ldiffile = NULL;
+ char **debug_unknowns = NULL;
+ int rc, i;
+ int mode = SLAP_TOOL_MODE;
+ int truncatemode = 0;
+ int use_glue = 1;
+ int writer;
+
+#ifdef LDAP_DEBUG
+ /* tools default to "none", so that at least LDAP_DEBUG_ANY
+ * messages show up; use -d 0 to reset */
+ slap_debug = LDAP_DEBUG_NONE;
+ ldif_debug = slap_debug;
+#endif
+ ldap_syslog = 0;
+ /* make sure libldap gets init'd */
+ ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &slap_debug );
+
+#ifdef CSRIMALLOC
+ leakfilename = malloc( strlen( progname ) + STRLENOF( ".leak" ) + 1 );
+ sprintf( leakfilename, "%s.leak", progname );
+ if( ( leakfile = fopen( leakfilename, "w" )) == NULL ) {
+ leakfile = stderr;
+ }
+ free( leakfilename );
+ leakfilename = NULL;
+#endif
+
+ ldif_wrap = LDIF_LINE_WIDTH;
+
+ scope = LDAP_SCOPE_DEFAULT;
+
+ switch( tool ) {
+ case SLAPADD:
+ options = "b:cd:f:F:gj:l:n:o:qsS:uvw";
+ break;
+
+ case SLAPCAT:
+ options = "a:b:cd:f:F:gH:l:n:o:s:v";
+ mode |= SLAP_TOOL_READMAIN | SLAP_TOOL_READONLY;
+ break;
+
+ case SLAPDN:
+ options = "d:f:F:No:Pv";
+ mode |= SLAP_TOOL_READMAIN | SLAP_TOOL_READONLY;
+ break;
+
+ case SLAPMODIFY:
+ options = "b:cd:f:F:gj:l:n:o:qsS:uvw";
+ break;
+
+ case SLAPSCHEMA:
+ options = "a:b:cd:f:F:gH:l:n:o:s:v";
+ mode |= SLAP_TOOL_READMAIN | SLAP_TOOL_READONLY;
+ break;
+
+ case SLAPTEST:
+ options = "d:f:F:n:o:Quv";
+ mode |= SLAP_TOOL_READMAIN | SLAP_TOOL_READONLY;
+ break;
+
+ case SLAPAUTH:
+ options = "d:f:F:M:o:R:U:vX:";
+ mode |= SLAP_TOOL_READMAIN | SLAP_TOOL_READONLY;
+ break;
+
+ case SLAPINDEX:
+ options = "b:cd:f:F:gn:o:qtv";
+ mode |= SLAP_TOOL_READMAIN;
+ break;
+
+ case SLAPACL:
+ options = "b:D:d:f:F:o:uU:vX:";
+ mode |= SLAP_TOOL_READMAIN | SLAP_TOOL_READONLY;
+ break;
+
+ default:
+ fprintf( stderr, "%s: unknown tool mode (%d)\n", progname, tool );
+ exit( EXIT_FAILURE );
+ }
+
+ dbnum = -1;
+ while ( (i = getopt( argc, argv, options )) != EOF ) {
+ switch ( i ) {
+ case 'a':
+ filterstr = optarg;
+ break;
+
+ case 'b':
+ ber_str2bv( optarg, 0, 1, &base );
+ break;
+
+ case 'c': /* enable continue mode */
+ continuemode++;
+ break;
+
+ case 'd': { /* turn on debugging */
+ int level = 0;
+
+ if ( parse_debug_level( optarg, &level, &debug_unknowns ) ) {
+ usage( tool, progname );
+ }
+#ifdef LDAP_DEBUG
+ if ( level == 0 ) {
+ /* allow to reset log level */
+ slap_debug = 0;
+
+ } else {
+ slap_debug |= level;
+ }
+#else
+ if ( level != 0 )
+ fputs( "must compile with LDAP_DEBUG for debugging\n",
+ stderr );
+#endif
+ } break;
+
+ case 'D':
+ ber_str2bv( optarg, 0, 1, &authcDN );
+ break;
+
+ case 'f': /* specify a conf file */
+ conffile = optarg;
+ break;
+
+ case 'F': /* specify a conf dir */
+ confdir = optarg;
+ break;
+
+ case 'g': /* disable subordinate glue */
+ use_glue = 0;
+ break;
+
+ case 'H': {
+ LDAPURLDesc *ludp;
+ int rc;
+
+ rc = ldap_url_parse_ext( optarg, &ludp,
+ LDAP_PVT_URL_PARSE_NOEMPTY_HOST | LDAP_PVT_URL_PARSE_NOEMPTY_DN );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ usage( tool, progname );
+ }
+
+ /* don't accept host, port, attrs, extensions */
+ if ( ldap_pvt_url_scheme2proto( ludp->lud_scheme ) != LDAP_PROTO_TCP ) {
+ usage( tool, progname );
+ }
+
+ if ( ludp->lud_host != NULL ) {
+ usage( tool, progname );
+ }
+
+ if ( ludp->lud_port != 0 ) {
+ usage( tool, progname );
+ }
+
+ if ( ludp->lud_attrs != NULL ) {
+ usage( tool, progname );
+ }
+
+ if ( ludp->lud_exts != NULL ) {
+ usage( tool, progname );
+ }
+
+ if ( ludp->lud_dn != NULL && ludp->lud_dn[0] != '\0' ) {
+ ch_free( subtree );
+ subtree = ludp->lud_dn;
+ ludp->lud_dn = NULL;
+ }
+
+ if ( ludp->lud_filter != NULL && ludp->lud_filter[0] != '\0' ) {
+ filterstr = ludp->lud_filter;
+ ludp->lud_filter = NULL;
+ }
+
+ scope = ludp->lud_scope;
+
+ ldap_free_urldesc( ludp );
+ } break;
+
+ case 'j': /* jump to linenumber */
+ if ( lutil_atoul( &jumpline, optarg ) ) {
+ usage( tool, progname );
+ }
+ break;
+
+ case 'l': /* LDIF file */
+ ldiffile = optarg;
+ break;
+
+ case 'M':
+ ber_str2bv( optarg, 0, 0, &mech );
+ break;
+
+ case 'N':
+ if ( dn_mode && dn_mode != SLAP_TOOL_LDAPDN_NORMAL ) {
+ usage( tool, progname );
+ }
+ dn_mode = SLAP_TOOL_LDAPDN_NORMAL;
+ break;
+
+ case 'n': /* which config file db to index */
+ if ( lutil_atoi( &dbnum, optarg ) || dbnum < 0 ) {
+ usage( tool, progname );
+ }
+ break;
+
+ case 'o':
+ if ( parse_slapopt( tool, &mode ) ) {
+ usage( tool, progname );
+ }
+ break;
+
+ case 'P':
+ if ( dn_mode && dn_mode != SLAP_TOOL_LDAPDN_PRETTY ) {
+ usage( tool, progname );
+ }
+ dn_mode = SLAP_TOOL_LDAPDN_PRETTY;
+ break;
+
+ case 'Q':
+ quiet++;
+ slap_debug = 0;
+ break;
+
+ case 'q': /* turn on quick */
+ mode |= SLAP_TOOL_QUICK;
+ break;
+
+ case 'R':
+ realm = optarg;
+ break;
+
+ case 'S':
+ if ( lutil_atou( &csnsid, optarg )
+ || csnsid > SLAP_SYNC_SID_MAX )
+ {
+ usage( tool, progname );
+ }
+ break;
+
+ case 's':
+ switch ( tool ) {
+ case SLAPADD:
+ case SLAPMODIFY:
+ /* no schema check */
+ mode |= SLAP_TOOL_NO_SCHEMA_CHECK;
+ break;
+
+ case SLAPCAT:
+ case SLAPSCHEMA:
+ /* dump subtree */
+ ch_free( subtree );
+ subtree = optarg;
+ break;
+ }
+ break;
+
+ case 't': /* turn on truncate */
+ truncatemode++;
+ mode |= SLAP_TRUNCATE_MODE;
+ break;
+
+ case 'U':
+ ber_str2bv( optarg, 0, 0, &authcID );
+ break;
+
+ case 'u': /* dry run */
+ dryrun++;
+ break;
+
+ case 'v': /* turn on verbose */
+ verbose++;
+ break;
+
+ case 'w': /* write context csn at the end */
+ update_ctxcsn++;
+ break;
+
+ case 'X':
+ ber_str2bv( optarg, 0, 0, &authzID );
+ break;
+
+ default:
+ usage( tool, progname );
+ break;
+ }
+ }
+
+#if defined(LDAP_SYSLOG) && defined(LDAP_DEBUG)
+ if ( start_syslog ) {
+ char *logName;
+#ifdef HAVE_EBCDIC
+ logName = ch_strdup( progname );
+ __atoe( logName );
+#else
+ logName = (char *)progname;
+#endif
+
+#ifdef LOG_LOCAL4
+ openlog( logName, OPENLOG_OPTIONS, syslogUser );
+#elif defined LOG_DEBUG
+ openlog( logName, OPENLOG_OPTIONS );
+#endif
+#ifdef HAVE_EBCDIC
+ free( logName );
+ logName = NULL;
+#endif
+ }
+#endif /* LDAP_DEBUG && LDAP_SYSLOG */
+
+ switch ( tool ) {
+ case SLAPCAT:
+ case SLAPSCHEMA:
+ writer = 1;
+ break;
+
+ default:
+ writer = 0;
+ break;
+ }
+
+ switch ( tool ) {
+ case SLAPADD:
+ case SLAPCAT:
+ case SLAPMODIFY:
+ case SLAPSCHEMA:
+ if ( ( argc != optind ) || (dbnum >= 0 && base.bv_val != NULL ) ) {
+ usage( tool, progname );
+ }
+
+ break;
+
+ case SLAPINDEX:
+ if ( dbnum >= 0 && base.bv_val != NULL ) {
+ usage( tool, progname );
+ }
+
+ break;
+
+ case SLAPDN:
+ if ( argc == optind ) {
+ usage( tool, progname );
+ }
+ break;
+
+ case SLAPAUTH:
+ if ( argc == optind && BER_BVISNULL( &authcID ) ) {
+ usage( tool, progname );
+ }
+ break;
+
+ case SLAPTEST:
+ if ( argc != optind ) {
+ usage( tool, progname );
+ }
+ break;
+
+ case SLAPACL:
+ if ( !BER_BVISNULL( &authcDN ) && !BER_BVISNULL( &authcID ) ) {
+ usage( tool, progname );
+ }
+ if ( BER_BVISNULL( &base ) ) {
+ usage( tool, progname );
+ }
+ ber_dupbv( &baseDN, &base );
+ break;
+
+ default:
+ break;
+ }
+
+ if ( ldiffile == NULL ) {
+ dummy.fp = writer ? stdout : stdin;
+ ldiffp = &dummy;
+
+ } else if ((ldiffp = ldif_open( ldiffile, writer ? "w" : "r" ))
+ == NULL )
+ {
+ perror( ldiffile );
+ exit( EXIT_FAILURE );
+ }
+
+ /*
+ * initialize stuff and figure out which backend we're dealing with
+ */
+
+ slapTool = tool;
+ rc = slap_init( mode, progname );
+ if ( rc != 0 ) {
+ fprintf( stderr, "%s: slap_init failed!\n", progname );
+ exit( EXIT_FAILURE );
+ }
+
+ rc = read_config( conffile, confdir );
+
+ if ( rc != 0 ) {
+ fprintf( stderr, "%s: bad configuration %s!\n",
+ progname, confdir ? "directory" : "file" );
+ exit( EXIT_FAILURE );
+ }
+
+ if ( debug_unknowns ) {
+ rc = parse_debug_unknowns( debug_unknowns, &slap_debug );
+ ldap_charray_free( debug_unknowns );
+ debug_unknowns = NULL;
+ if ( rc )
+ exit( EXIT_FAILURE );
+ }
+
+#if defined(LDAP_SYSLOG) && defined(LDAP_DEBUG)
+ if ( syslog_unknowns ) {
+ rc = parse_debug_unknowns( syslog_unknowns, &ldap_syslog );
+ ldap_charray_free( syslog_unknowns );
+ syslog_unknowns = NULL;
+ if ( rc )
+ exit( EXIT_FAILURE );
+ }
+#endif
+
+ at_oc_cache = 1;
+
+ switch ( tool ) {
+ case SLAPADD:
+ case SLAPCAT:
+ case SLAPINDEX:
+ case SLAPMODIFY:
+ case SLAPSCHEMA:
+ if ( !nbackends ) {
+ fprintf( stderr, "No databases found "
+ "in config file\n" );
+ exit( EXIT_FAILURE );
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if ( use_glue ) {
+ rc = glue_sub_attach( 0 );
+
+ if ( rc != 0 ) {
+ fprintf( stderr,
+ "%s: subordinate configuration error\n", progname );
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ rc = slap_schema_check();
+
+ if ( rc != 0 ) {
+ fprintf( stderr, "%s: slap_schema_prep failed!\n", progname );
+ exit( EXIT_FAILURE );
+ }
+
+ switch ( tool ) {
+ case SLAPTEST:
+ if ( dbnum >= 0 )
+ goto get_db;
+ /* FALLTHRU */
+ case SLAPDN:
+ case SLAPAUTH:
+ be = NULL;
+ goto startup;
+
+ default:
+ break;
+ }
+
+ if( filterstr ) {
+ filter = str2filter( filterstr );
+
+ if( filter == NULL ) {
+ fprintf( stderr, "Invalid filter '%s'\n", filterstr );
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ if( subtree ) {
+ struct berval val;
+ ber_str2bv( subtree, 0, 0, &val );
+ rc = dnNormalize( 0, NULL, NULL, &val, &sub_ndn, NULL );
+ if( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "Invalid subtree DN '%s'\n", subtree );
+ exit( EXIT_FAILURE );
+ }
+
+ if ( BER_BVISNULL( &base ) && dbnum == -1 ) {
+ base = val;
+ } else {
+ free( subtree );
+ subtree = NULL;
+ }
+ }
+
+ if( base.bv_val != NULL ) {
+ struct berval nbase;
+
+ rc = dnNormalize( 0, NULL, NULL, &base, &nbase, NULL );
+ if( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s: slap_init invalid suffix (\"%s\")\n",
+ progname, base.bv_val );
+ exit( EXIT_FAILURE );
+ }
+
+ be = select_backend( &nbase, 0 );
+ ber_memfree( nbase.bv_val );
+ BER_BVZERO( &nbase );
+
+ if( be == NULL ) {
+ fprintf( stderr, "%s: slap_init no backend for \"%s\"\n",
+ progname, base.bv_val );
+ exit( EXIT_FAILURE );
+ }
+ switch ( tool ) {
+ case SLAPACL:
+ goto startup;
+
+ default:
+ break;
+ }
+
+ /* If the named base is a glue primary, operate on the
+ * entire context
+ */
+ if ( SLAP_GLUE_INSTANCE( be ) ) {
+ nosubordinates = 1;
+ }
+
+ ch_free( base.bv_val );
+ BER_BVZERO( &base );
+
+ } else if ( dbnum == -1 ) {
+ /* no suffix and no dbnum specified, just default to
+ * the first available database
+ */
+ if ( nbackends <= 0 ) {
+ fprintf( stderr, "No available databases\n" );
+ exit( EXIT_FAILURE );
+ }
+ LDAP_STAILQ_FOREACH( be, &backendDB, be_next ) {
+ dbnum++;
+
+ /* db #0 is cn=config, don't select it as a default */
+ if ( dbnum < 1 ) continue;
+
+ if ( SLAP_MONITOR(be))
+ continue;
+
+ /* If just doing the first by default and it is a
+ * glue subordinate, find the primary.
+ */
+ if ( SLAP_GLUE_SUBORDINATE(be) ) {
+ nosubordinates = 1;
+ continue;
+ }
+ break;
+ }
+
+ if ( !be ) {
+ fprintf( stderr, "Available database(s) "
+ "do not allow %s\n", progname );
+ exit( EXIT_FAILURE );
+ }
+
+ if ( nosubordinates == 0 && dbnum > 1 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "The first database does not allow %s;"
+ " using the first available one (%d)\n",
+ progname, dbnum );
+ }
+
+ } else if ( dbnum >= nbackends ) {
+ fprintf( stderr,
+ "Database number selected via -n is out of range\n"
+ "Must be in the range 0 to %d"
+ " (the number of configured databases)\n",
+ nbackends - 1 );
+ exit( EXIT_FAILURE );
+
+ } else {
+get_db:
+ LDAP_STAILQ_FOREACH( be, &backendDB, be_next ) {
+ if ( dbnum == 0 ) break;
+ dbnum--;
+ }
+ }
+
+ if ( scope != LDAP_SCOPE_DEFAULT && BER_BVISNULL( &sub_ndn ) ) {
+ if ( be && be->be_nsuffix ) {
+ ber_dupbv( &sub_ndn, be->be_nsuffix );
+
+ } else {
+ fprintf( stderr,
+ "<scope> needs a DN or a valid database\n" );
+ exit( EXIT_FAILURE );
+ }
+ }
+
+startup:;
+ if ( be ) {
+ BackendDB *bdtmp;
+
+ dbnum = 0;
+ LDAP_STAILQ_FOREACH( bdtmp, &backendDB, be_next ) {
+ if ( bdtmp == be ) break;
+ dbnum++;
+ }
+ }
+
+#ifdef CSRIMALLOC
+ mal_leaktrace(1);
+#endif
+
+
+ /* slapdn doesn't specify a backend to startup */
+ if ( !dryrun && tool != SLAPDN ) {
+ need_shutdown = 1;
+
+ if ( slap_startup( be ) ) {
+ switch ( tool ) {
+ case SLAPTEST:
+ fprintf( stderr, "slap_startup failed "
+ "(test would succeed using "
+ "the -u switch)\n" );
+ break;
+
+ default:
+ fprintf( stderr, "slap_startup failed\n" );
+ break;
+ }
+
+ exit( EXIT_FAILURE );
+ }
+ }
+}
+
+int slap_tool_destroy( void )
+{
+ int rc = 0;
+ if ( !dryrun ) {
+ if ( need_shutdown ) {
+ if ( slap_shutdown( be ))
+ rc = EXIT_FAILURE;
+ }
+ if ( slap_destroy())
+ rc = EXIT_FAILURE;
+ }
+#ifdef SLAPD_MODULES
+ if ( slapMode == SLAP_SERVER_MODE ) {
+ /* always false. just pulls in necessary symbol references. */
+ lutil_uuidstr(NULL, 0);
+ }
+ module_kill();
+#endif
+ schema_destroy();
+#ifdef HAVE_TLS
+ ldap_pvt_tls_destroy();
+#endif
+ config_destroy();
+
+#ifdef CSRIMALLOC
+ mal_dumpleaktrace( leakfile );
+#endif
+
+ if ( !BER_BVISNULL( &authcDN ) ) {
+ ch_free( authcDN.bv_val );
+ BER_BVZERO( &authcDN );
+ }
+
+ if ( ldiffp && ldiffp != &dummy ) {
+ ldif_close( ldiffp );
+ }
+ return rc;
+}
+
+int
+slap_tool_update_ctxcsn(
+ const char *progname,
+ unsigned long sid,
+ struct berval *bvtext )
+{
+ struct berval ctxdn;
+ ID ctxcsn_id;
+ Entry *ctxcsn_e;
+ int rc = EXIT_SUCCESS;
+
+ if ( !(update_ctxcsn && !dryrun && sid != SLAP_SYNC_SID_MAX + 1) ) {
+ return rc;
+ }
+
+ if ( SLAP_SYNC_SUBENTRY( be )) {
+ build_new_dn( &ctxdn, &be->be_nsuffix[0],
+ (struct berval *)&slap_ldapsync_cn_bv, NULL );
+ } else {
+ ctxdn = be->be_nsuffix[0];
+ }
+ ctxcsn_id = be->be_dn2id_get( be, &ctxdn );
+ if ( ctxcsn_id == NOID ) {
+ if ( SLAP_SYNC_SUBENTRY( be )) {
+ ctxcsn_e = slap_create_context_csn_entry( be, NULL );
+ for ( sid = 0; sid <= SLAP_SYNC_SID_MAX; sid++ ) {
+ if ( maxcsn[ sid ].bv_len ) {
+ attr_merge_one( ctxcsn_e, slap_schema.si_ad_contextCSN,
+ &maxcsn[ sid ], NULL );
+ }
+ }
+ ctxcsn_id = be->be_entry_put( be, ctxcsn_e, bvtext );
+ if ( ctxcsn_id == NOID ) {
+ fprintf( stderr, "%s: couldn't create context entry\n", progname );
+ rc = EXIT_FAILURE;
+ }
+ entry_free( ctxcsn_e );
+ } else {
+ fprintf( stderr, "%s: context entry is missing\n", progname );
+ rc = EXIT_FAILURE;
+ }
+ } else {
+ ctxcsn_e = be->be_entry_get( be, ctxcsn_id );
+ if ( ctxcsn_e != NULL ) {
+ Operation op = { 0 };
+ Entry *e = entry_dup( ctxcsn_e );
+ Attribute *attr = attr_find( e->e_attrs, slap_schema.si_ad_contextCSN );
+
+ int change;
+ op.o_bd = be;
+ be_entry_release_r( &op, ctxcsn_e );
+
+ if ( attr ) {
+ int i;
+
+ change = 0;
+
+ for ( i = 0; !BER_BVISNULL( &attr->a_nvals[ i ] ); i++ ) {
+ int rc_sid;
+ int match;
+ const char *text = NULL;
+
+ rc_sid = slap_parse_csn_sid( &attr->a_nvals[ i ] );
+ if ( rc_sid < 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: unable to extract SID "
+ "from #%d contextCSN=%s\n",
+ progname, i,
+ attr->a_nvals[ i ].bv_val );
+ continue;
+ }
+
+ assert( rc_sid <= SLAP_SYNC_SID_MAX );
+
+ sid = (unsigned)rc_sid;
+
+ if ( maxcsn[ sid ].bv_len == 0 ) {
+ match = -1;
+
+ } else {
+ value_match( &match, slap_schema.si_ad_entryCSN,
+ slap_schema.si_ad_entryCSN->ad_type->sat_ordering,
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ &maxcsn[ sid ], &attr->a_nvals[i], &text );
+ }
+
+ if ( match > 0 ) {
+ change = 1;
+ } else {
+ AC_MEMCPY( maxcsn[ sid ].bv_val,
+ attr->a_nvals[ i ].bv_val,
+ attr->a_nvals[ i ].bv_len );
+ maxcsn[ sid ].bv_val[ attr->a_nvals[ i ].bv_len ] = '\0';
+ maxcsn[ sid ].bv_len = attr->a_nvals[ i ].bv_len;
+ }
+ }
+
+ if ( change ) {
+ if ( attr->a_nvals != attr->a_vals ) {
+ ber_bvarray_free( attr->a_nvals );
+ }
+ attr->a_nvals = NULL;
+ ber_bvarray_free( attr->a_vals );
+ attr->a_vals = NULL;
+ attr->a_numvals = 0;
+ }
+ } else {
+ change = 1;
+ }
+
+ if ( change ) {
+ for ( sid = 0; sid <= SLAP_SYNC_SID_MAX; sid++ ) {
+ if ( maxcsn[ sid ].bv_len ) {
+ attr_merge_one( e, slap_schema.si_ad_contextCSN,
+ &maxcsn[ sid], NULL );
+ }
+ }
+
+ ctxcsn_id = be->be_entry_modify( be, e, bvtext );
+ if( ctxcsn_id == NOID ) {
+ fprintf( stderr, "%s: could not modify ctxcsn (%s)\n",
+ progname, bvtext->bv_val ? bvtext->bv_val : "" );
+ rc = EXIT_FAILURE;
+ } else if ( verbose ) {
+ fprintf( stderr, "modified: \"%s\" (%08lx)\n",
+ e->e_dn, (long) ctxcsn_id );
+ }
+ }
+ entry_free( e );
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * return value:
+ * -1: update_ctxcsn == 0
+ * SLAP_SYNC_SID_MAX + 1: unable to extract SID
+ * 0 <= SLAP_SYNC_SID_MAX: the SID
+ */
+unsigned long
+slap_tool_update_ctxcsn_check(
+ const char *progname,
+ Entry *e )
+{
+ if ( update_ctxcsn ) {
+ unsigned long sid = SLAP_SYNC_SID_MAX + 1;
+ int rc_sid;
+ Attribute *attr;
+
+ attr = attr_find( e->e_attrs, slap_schema.si_ad_entryCSN );
+ assert( attr != NULL );
+
+ rc_sid = slap_parse_csn_sid( &attr->a_nvals[ 0 ] );
+ if ( rc_sid < 0 ) {
+ Debug( LDAP_DEBUG_ANY, "%s: could not "
+ "extract SID from entryCSN=%s, entry dn=\"%s\"\n",
+ progname, attr->a_nvals[ 0 ].bv_val, e->e_name.bv_val );
+ return (unsigned long)(-1);
+
+ } else {
+ int match;
+ const char *text = NULL;
+
+ assert( rc_sid <= SLAP_SYNC_SID_MAX );
+
+ sid = (unsigned)rc_sid;
+ if ( maxcsn[ sid ].bv_len != 0 ) {
+ match = 0;
+ value_match( &match, slap_schema.si_ad_entryCSN,
+ slap_schema.si_ad_entryCSN->ad_type->sat_ordering,
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ &maxcsn[ sid ], &attr->a_nvals[0], &text );
+ } else {
+ match = -1;
+ }
+ if ( match < 0 ) {
+ strcpy( maxcsn[ sid ].bv_val, attr->a_nvals[0].bv_val );
+ maxcsn[ sid ].bv_len = attr->a_nvals[0].bv_len;
+ }
+ }
+ }
+
+ return (unsigned long)(-1);
+}
+
+int
+slap_tool_update_ctxcsn_init(void)
+{
+ if ( update_ctxcsn ) {
+ unsigned long sid;
+ maxcsn[ 0 ].bv_val = maxcsnbuf;
+ for ( sid = 1; sid <= SLAP_SYNC_SID_MAX; sid++ ) {
+ maxcsn[ sid ].bv_val = maxcsn[ sid - 1 ].bv_val + LDAP_PVT_CSNSTR_BUFSIZE;
+ maxcsn[ sid ].bv_len = 0;
+ }
+ }
+
+ return 0;
+}
+
+int
+slap_tool_entry_check(
+ const char *progname,
+ Operation *op,
+ Entry *e,
+ int lineno,
+ const char **text,
+ char *textbuf,
+ size_t textlen )
+{
+ /* NOTE: we may want to conditionally enable manage */
+ int manage = 0;
+
+ Attribute *oc = attr_find( e->e_attrs,
+ slap_schema.si_ad_objectClass );
+
+ if( oc == NULL ) {
+ fprintf( stderr, "%s: dn=\"%s\" (line=%d): %s\n",
+ progname, e->e_dn, lineno,
+ "no objectClass attribute");
+ return LDAP_NO_SUCH_ATTRIBUTE;
+ }
+
+ /* check schema */
+ op->o_bd = be;
+
+ if ( (slapMode & SLAP_TOOL_NO_SCHEMA_CHECK) == 0) {
+ int rc = entry_schema_check( op, e, NULL, manage, 1, NULL,
+ text, textbuf, textlen );
+
+ if( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s: dn=\"%s\" (line=%d): (%d) %s\n",
+ progname, e->e_dn, lineno, rc, *text );
+ return rc;
+ }
+ textbuf[ 0 ] = '\0';
+ }
+
+ if ( (slapMode & SLAP_TOOL_VALUE_CHECK) != 0) {
+ Modifications *ml = NULL;
+
+ int rc = slap_entry2mods( e, &ml, text, textbuf, textlen );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s: dn=\"%s\" (line=%d): (%d) %s\n",
+ progname, e->e_dn, lineno, rc, *text );
+ return rc;
+ }
+ textbuf[ 0 ] = '\0';
+
+ rc = slap_mods_check( op, ml, text, textbuf, textlen, NULL );
+ slap_mods_free( ml, 1 );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s: dn=\"%s\" (line=%d): (%d) %s\n",
+ progname, e->e_dn, lineno, rc, *text );
+ return rc;
+ }
+ textbuf[ 0 ] = '\0';
+ }
+
+ return LDAP_SUCCESS;
+}
+
diff --git a/servers/slapd/slapcommon.h b/servers/slapd/slapcommon.h
new file mode 100644
index 0000000..f0de178
--- /dev/null
+++ b/servers/slapd/slapcommon.h
@@ -0,0 +1,125 @@
+/* slapcommon.h - common definitions for the slap tools */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef SLAPCOMMON_H_
+#define SLAPCOMMON_H_ 1
+
+#define SLAPD_TOOLS 1
+#include "slap.h"
+
+typedef struct tool_vars {
+ Backend *tv_be;
+ int tv_dbnum;
+ int tv_verbose;
+ int tv_quiet;
+ int tv_update_ctxcsn;
+ int tv_continuemode;
+ int tv_nosubordinates;
+ int tv_dryrun;
+ int tv_scope;
+ unsigned long tv_jumpline;
+ struct berval tv_sub_ndn;
+ Filter *tv_filter;
+ struct LDIFFP *tv_ldiffp;
+ struct berval tv_baseDN;
+ struct berval tv_authcDN;
+ struct berval tv_authzDN;
+ struct berval tv_authcID;
+ struct berval tv_authzID;
+ struct berval tv_mech;
+ char *tv_realm;
+ struct berval tv_listener_url;
+ struct berval tv_peer_domain;
+ struct berval tv_peer_name;
+ struct berval tv_sock_name;
+ slap_ssf_t tv_ssf;
+ slap_ssf_t tv_transport_ssf;
+ slap_ssf_t tv_tls_ssf;
+ slap_ssf_t tv_sasl_ssf;
+ unsigned tv_dn_mode;
+ unsigned int tv_csnsid;
+ ber_len_t tv_ldif_wrap;
+ char tv_maxcsnbuf[ LDAP_PVT_CSNSTR_BUFSIZE * ( SLAP_SYNC_SID_MAX + 1 ) ];
+ struct berval tv_maxcsn[ SLAP_SYNC_SID_MAX + 1 ];
+} tool_vars;
+
+extern tool_vars tool_globals;
+
+#define be tool_globals.tv_be
+#define dbnum tool_globals.tv_dbnum
+#define verbose tool_globals.tv_verbose
+#define quiet tool_globals.tv_quiet
+#define jumpline tool_globals.tv_jumpline
+#define update_ctxcsn tool_globals.tv_update_ctxcsn
+#define continuemode tool_globals.tv_continuemode
+#define nosubordinates tool_globals.tv_nosubordinates
+#define dryrun tool_globals.tv_dryrun
+#define sub_ndn tool_globals.tv_sub_ndn
+#define scope tool_globals.tv_scope
+#define filter tool_globals.tv_filter
+#define ldiffp tool_globals.tv_ldiffp
+#define baseDN tool_globals.tv_baseDN
+#define authcDN tool_globals.tv_authcDN
+#define authzDN tool_globals.tv_authzDN
+#define authcID tool_globals.tv_authcID
+#define authzID tool_globals.tv_authzID
+#define mech tool_globals.tv_mech
+#define realm tool_globals.tv_realm
+#define listener_url tool_globals.tv_listener_url
+#define peer_domain tool_globals.tv_peer_domain
+#define peer_name tool_globals.tv_peer_name
+#define sock_name tool_globals.tv_sock_name
+#define ssf tool_globals.tv_ssf
+#define transport_ssf tool_globals.tv_transport_ssf
+#define tls_ssf tool_globals.tv_tls_ssf
+#define sasl_ssf tool_globals.tv_sasl_ssf
+#define dn_mode tool_globals.tv_dn_mode
+#define csnsid tool_globals.tv_csnsid
+#define ldif_wrap tool_globals.tv_ldif_wrap
+#define maxcsn tool_globals.tv_maxcsn
+#define maxcsnbuf tool_globals.tv_maxcsnbuf
+
+#define SLAP_TOOL_LDAPDN_PRETTY SLAP_LDAPDN_PRETTY
+#define SLAP_TOOL_LDAPDN_NORMAL (SLAP_LDAPDN_PRETTY << 1)
+
+void slap_tool_init LDAP_P((
+ const char* name,
+ int tool,
+ int argc, char **argv ));
+
+int slap_tool_destroy LDAP_P((void));
+
+int slap_tool_update_ctxcsn LDAP_P((
+ const char *progname,
+ unsigned long sid,
+ struct berval *bvtext ));
+
+unsigned long slap_tool_update_ctxcsn_check LDAP_P((
+ const char *progname,
+ Entry *e ));
+
+int slap_tool_update_ctxcsn_init LDAP_P((void));
+
+int slap_tool_entry_check LDAP_P((
+ const char *progname,
+ Operation *op,
+ Entry *e,
+ int lineno,
+ const char **text,
+ char *textbuf,
+ size_t textlen ));
+
+#endif /* SLAPCOMMON_H_ */
diff --git a/servers/slapd/slapd.conf b/servers/slapd/slapd.conf
new file mode 100644
index 0000000..a868793
--- /dev/null
+++ b/servers/slapd/slapd.conf
@@ -0,0 +1,79 @@
+#
+# See slapd.conf(5) for details on configuration options.
+# This file should NOT be world readable.
+#
+include %SYSCONFDIR%/schema/core.schema
+
+# Define global ACLs to disable default read access.
+
+# Do not enable referrals until AFTER you have a working directory
+# service AND an understanding of referrals.
+#referral ldap://root.openldap.org
+
+pidfile %LOCALSTATEDIR%/run/slapd.pid
+argsfile %LOCALSTATEDIR%/run/slapd.args
+
+# Load dynamic backend modules:
+# modulepath %MODULEDIR%
+# moduleload back_mdb.la
+# moduleload back_ldap.la
+
+# Sample security restrictions
+# Require integrity protection (prevent hijacking)
+# Require 112-bit (3DES or better) encryption for updates
+# Require 63-bit encryption for simple bind
+# security ssf=1 update_ssf=112 simple_bind=64
+
+# Sample access control policy:
+# Root DSE: allow anyone to read it
+# Subschema (sub)entry DSE: allow anyone to read it
+# Other DSEs:
+# Allow self write access
+# Allow authenticated users read access
+# Allow anonymous users to authenticate
+# Directives needed to implement policy:
+# access to dn.base="" by * read
+# access to dn.base="cn=Subschema" by * read
+# access to *
+# by self write
+# by users read
+# by anonymous auth
+#
+# if no access controls are present, the default policy
+# allows anyone and everyone to read anything but restricts
+# updates to rootdn. (e.g., "access to * by * read")
+#
+# rootdn can always read and write EVERYTHING!
+
+#######################################################################
+# config database definitions
+#######################################################################
+database config
+# Uncomment the rootpw line to allow binding as the cn=config
+# rootdn so that temporary modifications to the configuration can be made
+# while slapd is running. They will not persist across a restart.
+# rootpw secret
+
+#######################################################################
+# MDB database definitions
+#######################################################################
+
+database mdb
+maxsize 1073741824
+suffix "dc=my-domain,dc=com"
+rootdn "cn=Manager,dc=my-domain,dc=com"
+# Cleartext passwords, especially for the rootdn, should
+# be avoid. See slappasswd(8) and slapd.conf(5) for details.
+# Use of strong authentication encouraged.
+rootpw secret
+# The database directory MUST exist prior to running slapd AND
+# should only be accessible by the slapd and slap tools.
+# Mode 700 recommended.
+directory %LOCALSTATEDIR%/openldap-data
+# Indices to maintain
+index objectClass eq
+
+#######################################################################
+# monitor database definitions
+#######################################################################
+database monitor
diff --git a/servers/slapd/slapd.ldif b/servers/slapd/slapd.ldif
new file mode 100644
index 0000000..0182a7c
--- /dev/null
+++ b/servers/slapd/slapd.ldif
@@ -0,0 +1,101 @@
+#
+# See slapd-config(5) for details on configuration options.
+# This file should NOT be world readable.
+#
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+#
+#
+# Define global ACLs to disable default read access.
+#
+olcArgsFile: %LOCALSTATEDIR%/run/slapd.args
+olcPidFile: %LOCALSTATEDIR%/run/slapd.pid
+#
+# Do not enable referrals until AFTER you have a working directory
+# service AND an understanding of referrals.
+#olcReferral: ldap://root.openldap.org
+#
+# Sample security restrictions
+# Require integrity protection (prevent hijacking)
+# Require 112-bit (3DES or better) encryption for updates
+# Require 64-bit encryption for simple bind
+#olcSecurity: ssf=1 update_ssf=112 simple_bind=64
+
+
+#
+# Load dynamic backend modules:
+#
+#dn: cn=module,cn=config
+#objectClass: olcModuleList
+#cn: module
+#olcModulepath: %MODULEDIR%
+#olcModuleload: back_mdb.la
+#olcModuleload: back_ldap.la
+#olcModuleload: back_passwd.la
+#olcModuleload: back_shell.la
+
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file://%SYSCONFDIR%/schema/core.ldif
+
+# Frontend settings
+#
+dn: olcDatabase=frontend,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcFrontendConfig
+olcDatabase: frontend
+#
+# Sample global access control policy:
+# Root DSE: allow anyone to read it
+# Subschema (sub)entry DSE: allow anyone to read it
+# Other DSEs:
+# Allow self write access
+# Allow authenticated users read access
+# Allow anonymous users to authenticate
+#
+#olcAccess: to dn.base="" by * read
+#olcAccess: to dn.base="cn=Subschema" by * read
+#olcAccess: to *
+# by self write
+# by users read
+# by anonymous auth
+#
+# if no access controls are present, the default policy
+# allows anyone and everyone to read anything but restricts
+# updates to rootdn. (e.g., "access to * by * read")
+#
+# rootdn can always read and write EVERYTHING!
+#
+
+
+#######################################################################
+# LMDB database definitions
+#######################################################################
+#
+dn: olcDatabase=mdb,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcMdbConfig
+olcDatabase: mdb
+olcDbMaxSize: 1073741824
+olcSuffix: dc=my-domain,dc=com
+olcRootDN: cn=Manager,dc=my-domain,dc=com
+# Cleartext passwords, especially for the rootdn, should
+# be avoided. See slappasswd(8) and slapd-config(5) for details.
+# Use of strong authentication encouraged.
+olcRootPW: secret
+# The database directory MUST exist prior to running slapd AND
+# should only be accessible by the slapd and slap tools.
+# Mode 700 recommended.
+olcDbDirectory: %LOCALSTATEDIR%/openldap-data
+# Indices to maintain
+olcDbIndex: objectClass eq
+
+dn: olcDatabase=monitor,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: monitor
+olcRootDN: cn=config
+olcMonitoring: FALSE
diff --git a/servers/slapd/slapd.service b/servers/slapd/slapd.service
new file mode 100644
index 0000000..a0599b8
--- /dev/null
+++ b/servers/slapd/slapd.service
@@ -0,0 +1,15 @@
+[Unit]
+Description=OpenLDAP Server Daemon
+After=syslog.target network-online.target
+Documentation=man:slapd
+Documentation=man:slapd-config
+Documentation=man:slapd-mdb
+
+[Service]
+Type=notify
+Environment="SLAPD_URLS=ldap:/// ldapi:///" "SLAPD_OPTIONS="
+EnvironmentFile=/etc/sysconfig/slapd
+ExecStart=%LIBEXECDIR%/slapd -d 0 -h ${SLAPD_URLS} $SLAPD_OPTIONS
+
+[Install]
+WantedBy=multi-user.target
diff --git a/servers/slapd/slapdn.c b/servers/slapd/slapdn.c
new file mode 100644
index 0000000..d38322e
--- /dev/null
+++ b/servers/slapd/slapdn.c
@@ -0,0 +1,107 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2004 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+
+#include <lber.h>
+#include <ldif.h>
+#include <lutil.h>
+
+#include "slapcommon.h"
+
+int
+slapdn( int argc, char **argv )
+{
+ int rc = 0;
+ const char *progname = "slapdn";
+
+ slap_tool_init( progname, SLAPDN, argc, argv );
+
+ argv = &argv[ optind ];
+ argc -= optind;
+
+ for ( ; argc--; argv++ ) {
+ struct berval dn,
+ pdn = BER_BVNULL,
+ ndn = BER_BVNULL;
+
+ ber_str2bv( argv[ 0 ], 0, 0, &dn );
+
+ switch ( dn_mode ) {
+ case SLAP_TOOL_LDAPDN_PRETTY:
+ rc = dnPretty( NULL, &dn, &pdn, NULL );
+ break;
+
+ case SLAP_TOOL_LDAPDN_NORMAL:
+ rc = dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL );
+ break;
+
+ default:
+ rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, NULL );
+ break;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "DN: <%s> check failed %d (%s)\n",
+ dn.bv_val, rc,
+ ldap_err2string( rc ) );
+ if ( !continuemode ) {
+ rc = -1;
+ break;
+ }
+
+ } else {
+ switch ( dn_mode ) {
+ case SLAP_TOOL_LDAPDN_PRETTY:
+ printf( "%s\n", pdn.bv_val );
+ break;
+
+ case SLAP_TOOL_LDAPDN_NORMAL:
+ printf( "%s\n", ndn.bv_val );
+ break;
+
+ default:
+ printf( "DN: <%s> check succeeded\n"
+ "normalized: <%s>\n"
+ "pretty: <%s>\n",
+ dn.bv_val,
+ ndn.bv_val, pdn.bv_val );
+ break;
+ }
+
+ ch_free( ndn.bv_val );
+ ch_free( pdn.bv_val );
+ }
+ }
+
+ if ( slap_tool_destroy())
+ rc = EXIT_FAILURE;
+
+ return rc;
+}
diff --git a/servers/slapd/slapi/Makefile.in b/servers/slapd/slapi/Makefile.in
new file mode 100644
index 0000000..0e46f78
--- /dev/null
+++ b/servers/slapd/slapi/Makefile.in
@@ -0,0 +1,51 @@
+# Makefile.in for SLAPI
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## Portions Copyright IBM Corp. 1997,2002,2003
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+LIBRARY = libslapi.la
+
+#all-common: $(LIBRARY) $(PROGRAMS)
+# @touch plugin.c slapi_pblock.c slapi_utils.c slapi_ops.c slapi_ext.c
+
+NT_SRCS = nt_err.c
+NT_OBJS = nt_err.lo
+
+LIB_DEFS = -DSLAPI_LIBRARY
+
+SRCS= plugin.c slapi_pblock.c slapi_utils.c printmsg.c slapi_ops.c slapi_dn.c slapi_ext.c slapi_overlay.c \
+ $(@PLAT@_SRCS)
+OBJS= plugin.lo slapi_pblock.lo slapi_utils.lo printmsg.lo slapi_ops.lo slapi_dn.lo slapi_ext.lo slapi_overlay.lo \
+ $(@PLAT@_SRCS)
+
+XSRCS= version.c
+
+LDAP_INCDIR= ../../../include -I.. -I.
+LDAP_LIBDIR= ../../../libraries
+
+XLIBS = $(LIBRARY)
+XXLIBS =
+NT_LINK_LIBS = $(AC_LIBS)
+
+XINCPATH = -I$(srcdir)/.. -I$(srcdir)
+XDEFS = $(MODULES_CPPFLAGS)
+
+BUILD_MOD = @BUILD_SLAPI@
+
+install-local: FORCE
+ if test "$(BUILD_MOD)" = "yes"; then \
+ $(MKDIR) $(DESTDIR)$(libdir); \
+ $(LTINSTALL) $(INSTALLFLAGS) -m 644 $(LIBRARY) $(DESTDIR)$(libdir); \
+ fi
+
diff --git a/servers/slapd/slapi/TODO b/servers/slapd/slapi/TODO
new file mode 100644
index 0000000..8916488
--- /dev/null
+++ b/servers/slapd/slapi/TODO
@@ -0,0 +1,16 @@
+- de-IBM SLAPI
+- add a config statement, or redefine the dynamic backend one,
+ "modulepath", to set/modify the load path also for plugins
+ (both plugins and modules use ltdl, so "modulepath" suffices ...)
+- improve slapi logging (use some [v]s[n]printf function)
+- add a config statement to set the log file name, or better
+- use syslog where available?
+- add some plugin monitoring stuff in back-monitor (e.g. a subentry
+ for each plugin with data from struct Slapi_PluginDesc)
+- This is a very tough task: try to implement a sandbox to execute
+ plugins in, trap deadly signals and possibly disable unsafe plugins
+ without crashing slapd (fork from inside thread? trap signals
+ and longjump to next plugin execution? Brrr).
+
+---
+$OpenLDAP$
diff --git a/servers/slapd/slapi/plugin.c b/servers/slapd/slapi/plugin.c
new file mode 100644
index 0000000..de8c60d
--- /dev/null
+++ b/servers/slapd/slapi/plugin.c
@@ -0,0 +1,833 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1997,2002-2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by IBM Corporation for use in
+ * IBM products and subsequently ported to OpenLDAP Software by
+ * Steve Omrani. Additional significant contributors include:
+ * Luke Howard
+ */
+
+#include "portable.h"
+
+/*
+ * Note: if ltdl.h is not available, slapi should not be compiled
+ */
+
+#ifdef HAVE_LTDL_H
+#include "ldap_pvt_thread.h"
+#include "slap.h"
+#include "slap-config.h"
+#include "slapi.h"
+#include "lutil.h"
+
+#include <ltdl.h>
+
+static int slapi_int_load_plugin( Slapi_PBlock *, const char *, const char *, int,
+ SLAPI_FUNC *, lt_dlhandle * );
+
+/* pointer to link list of extended objects */
+static ExtendedOp *pGExtendedOps = NULL;
+
+/*********************************************************************
+ * Function Name: plugin_pblock_new
+ *
+ * Description: This routine creates a new Slapi_PBlock structure,
+ * loads in the plugin module and executes the init
+ * function provided by the module.
+ *
+ * Input: type - type of the plugin, such as SASL, database, etc.
+ * path - the loadpath to load the module in
+ * initfunc - name of the plugin function to execute first
+ * argc - number of arguments
+ * argv[] - an array of char pointers point to
+ * the arguments passed in via
+ * the configuration file.
+ *
+ * Output:
+ *
+ * Return Values: a pointer to a newly created Slapi_PBlock structure or
+ * NULL - function failed
+ *
+ * Messages: None
+ *********************************************************************/
+
+static Slapi_PBlock *
+plugin_pblock_new(
+ int type,
+ int argc,
+ char *argv[] )
+{
+ Slapi_PBlock *pPlugin = NULL;
+ Slapi_PluginDesc *pPluginDesc = NULL;
+ lt_dlhandle hdLoadHandle;
+ int rc;
+ char **av2 = NULL, **ppPluginArgv;
+ char *path = argv[2];
+ char *initfunc = argv[3];
+
+ pPlugin = slapi_pblock_new();
+ if ( pPlugin == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto done;
+ }
+
+ slapi_pblock_set( pPlugin, SLAPI_PLUGIN_TYPE, (void *)&type );
+ slapi_pblock_set( pPlugin, SLAPI_PLUGIN_ARGC, (void *)&argc );
+
+ av2 = ldap_charray_dup( argv );
+ if ( av2 == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto done;
+ }
+
+ if ( argc > 0 ) {
+ ppPluginArgv = &av2[4];
+ } else {
+ ppPluginArgv = NULL;
+ }
+
+ slapi_pblock_set( pPlugin, SLAPI_PLUGIN_ARGV, (void *)ppPluginArgv );
+ slapi_pblock_set( pPlugin, SLAPI_X_CONFIG_ARGV, (void *)av2 );
+
+ rc = slapi_int_load_plugin( pPlugin, path, initfunc, 1, NULL, &hdLoadHandle );
+ if ( rc != 0 ) {
+ goto done;
+ }
+
+ if ( slapi_pblock_get( pPlugin, SLAPI_PLUGIN_DESCRIPTION, (void **)&pPluginDesc ) == 0 &&
+ pPluginDesc != NULL ) {
+ slapi_log_error(SLAPI_LOG_TRACE, "plugin_pblock_new",
+ "Registered plugin %s %s [%s] (%s)\n",
+ pPluginDesc->spd_id,
+ pPluginDesc->spd_version,
+ pPluginDesc->spd_vendor,
+ pPluginDesc->spd_description);
+ }
+
+done:
+ if ( rc != 0 && pPlugin != NULL ) {
+ slapi_pblock_destroy( pPlugin );
+ pPlugin = NULL;
+ if ( av2 != NULL ) {
+ ldap_charray_free( av2 );
+ }
+ }
+
+ return pPlugin;
+}
+
+/*********************************************************************
+ * Function Name: slapi_int_register_plugin
+ *
+ * Description: insert the slapi_pblock structure to a given position the end of the plugin
+ * list
+ *
+ * Input: a pointer to a plugin slapi_pblock structure to be added to
+ * the list
+ *
+ * Output: none
+ *
+ * Return Values: LDAP_SUCCESS - successfully inserted.
+ * LDAP_LOCAL_ERROR.
+ *
+ * Messages: None
+ *********************************************************************/
+int
+slapi_int_register_plugin_index(
+ Backend *be,
+ Slapi_PBlock *pPB,
+ int index )
+{
+ Slapi_PBlock *pTmpPB;
+ Slapi_PBlock *pSavePB;
+ int pos = 0, rc = LDAP_SUCCESS;
+
+ assert( be != NULL );
+
+ pTmpPB = SLAPI_BACKEND_PBLOCK( be );
+ if ( pTmpPB == NULL || index == 0 ) {
+ SLAPI_BACKEND_PBLOCK( be ) = pPB;
+ } else {
+ while ( pTmpPB != NULL && rc == LDAP_SUCCESS &&
+ ( index < 0 || pos++ < index ) ) {
+ pSavePB = pTmpPB;
+ rc = slapi_pblock_get( pTmpPB, SLAPI_IBM_PBLOCK, &pTmpPB );
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ rc = slapi_pblock_set( pSavePB, SLAPI_IBM_PBLOCK, (void *)pPB );
+ }
+ }
+
+ if ( index >= 0 && rc == LDAP_SUCCESS ) {
+ rc = slapi_pblock_set( pPB, SLAPI_IBM_PBLOCK, (void *)pTmpPB );
+ }
+
+ return ( rc != LDAP_SUCCESS ) ? LDAP_OTHER : LDAP_SUCCESS;
+}
+
+int
+slapi_int_register_plugin(
+ Backend *be,
+ Slapi_PBlock *pPB )
+{
+ return slapi_int_register_plugin_index( be, pPB, -1 );
+}
+
+/*********************************************************************
+ * Function Name: slapi_int_get_plugins
+ *
+ * Description: get the desired type of function pointers defined
+ * in all the plugins
+ *
+ * Input: the type of the functions to get, such as pre-operation,etc.
+ *
+ * Output: none
+ *
+ * Return Values: this routine returns a pointer to an array of function
+ * pointers containing backend-specific plugin functions
+ * followed by global plugin functions
+ *
+ * Messages: None
+ *********************************************************************/
+int
+slapi_int_get_plugins(
+ Backend *be,
+ int functype,
+ SLAPI_FUNC **ppFuncPtrs )
+{
+
+ Slapi_PBlock *pCurrentPB;
+ SLAPI_FUNC FuncPtr;
+ SLAPI_FUNC *pTmpFuncPtr;
+ int numPB = 0;
+ int rc = LDAP_SUCCESS;
+
+ assert( ppFuncPtrs != NULL );
+ *ppFuncPtrs = NULL;
+
+ if ( be == NULL ) {
+ goto done;
+ }
+
+ pCurrentPB = SLAPI_BACKEND_PBLOCK( be );
+
+ while ( pCurrentPB != NULL && rc == LDAP_SUCCESS ) {
+ rc = slapi_pblock_get( pCurrentPB, functype, &FuncPtr );
+ if ( rc == LDAP_SUCCESS ) {
+ if ( FuncPtr != NULL ) {
+ numPB++;
+ }
+ rc = slapi_pblock_get( pCurrentPB,
+ SLAPI_IBM_PBLOCK, &pCurrentPB );
+ }
+ }
+
+ if ( numPB == 0 ) {
+ rc = LDAP_SUCCESS;
+ goto done;
+ }
+
+ /*
+ * Now, build the function pointer array of backend-specific
+ * plugins followed by global plugins.
+ */
+ *ppFuncPtrs = pTmpFuncPtr =
+ (SLAPI_FUNC *)ch_malloc( ( numPB + 1 ) * sizeof(SLAPI_FUNC) );
+ if ( ppFuncPtrs == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto done;
+ }
+
+ pCurrentPB = SLAPI_BACKEND_PBLOCK( be );
+
+ while ( pCurrentPB != NULL && rc == LDAP_SUCCESS ) {
+ rc = slapi_pblock_get( pCurrentPB, functype, &FuncPtr );
+ if ( rc == LDAP_SUCCESS ) {
+ if ( FuncPtr != NULL ) {
+ *pTmpFuncPtr = FuncPtr;
+ pTmpFuncPtr++;
+ }
+ rc = slapi_pblock_get( pCurrentPB,
+ SLAPI_IBM_PBLOCK, &pCurrentPB );
+ }
+ }
+
+ *pTmpFuncPtr = NULL;
+
+
+done:
+ if ( rc != LDAP_SUCCESS && *ppFuncPtrs != NULL ) {
+ ch_free( *ppFuncPtrs );
+ *ppFuncPtrs = NULL;
+ }
+
+ return rc;
+}
+
+/*********************************************************************
+ * Function Name: createExtendedOp
+ *
+ * Description: Creates an extended operation structure and
+ * initializes the fields
+ *
+ * Return value: A newly allocated structure or NULL
+ ********************************************************************/
+ExtendedOp *
+createExtendedOp()
+{
+ ExtendedOp *ret;
+
+ ret = (ExtendedOp *)slapi_ch_malloc(sizeof(ExtendedOp));
+ ret->ext_oid.bv_val = NULL;
+ ret->ext_oid.bv_len = 0;
+ ret->ext_func = NULL;
+ ret->ext_be = NULL;
+ ret->ext_next = NULL;
+
+ return ret;
+}
+
+
+/*********************************************************************
+ * Function Name: slapi_int_unregister_extop
+ *
+ * Description: This routine removes the ExtendedOp structures
+ * asscoiated with a particular extended operation
+ * plugin.
+ *
+ * Input: pBE - pointer to a backend structure
+ * opList - pointer to a linked list of extended
+ * operation structures
+ * pPB - pointer to a slapi parameter block
+ *
+ * Output:
+ *
+ * Return Value: none
+ *
+ * Messages: None
+ *********************************************************************/
+void
+slapi_int_unregister_extop(
+ Backend *pBE,
+ ExtendedOp **opList,
+ Slapi_PBlock *pPB )
+{
+ ExtendedOp *pTmpExtOp, *backExtOp;
+ char **pTmpOIDs;
+ int i;
+
+#if 0
+ assert( pBE != NULL); /* unused */
+#endif /* 0 */
+ assert( opList != NULL );
+ assert( pPB != NULL );
+
+ if ( *opList == NULL ) {
+ return;
+ }
+
+ slapi_pblock_get( pPB, SLAPI_PLUGIN_EXT_OP_OIDLIST, &pTmpOIDs );
+ if ( pTmpOIDs == NULL ) {
+ return;
+ }
+
+ for ( i = 0; pTmpOIDs[i] != NULL; i++ ) {
+ backExtOp = NULL;
+ pTmpExtOp = *opList;
+ for ( ; pTmpExtOp != NULL; pTmpExtOp = pTmpExtOp->ext_next) {
+ int rc;
+ rc = strcasecmp( pTmpExtOp->ext_oid.bv_val,
+ pTmpOIDs[ i ] );
+ if ( rc == 0 ) {
+ if ( backExtOp == NULL ) {
+ *opList = pTmpExtOp->ext_next;
+ } else {
+ backExtOp->ext_next
+ = pTmpExtOp->ext_next;
+ }
+
+ ch_free( pTmpExtOp );
+ break;
+ }
+ backExtOp = pTmpExtOp;
+ }
+ }
+}
+
+
+/*********************************************************************
+ * Function Name: slapi_int_register_extop
+ *
+ * Description: This routine creates a new ExtendedOp structure, loads
+ * in the extended op module and put the extended op function address
+ * in the structure. The function will not be executed in
+ * this routine.
+ *
+ * Input: pBE - pointer to a backend structure
+ * opList - pointer to a linked list of extended
+ * operation structures
+ * pPB - pointer to a slapi parameter block
+ *
+ * Output:
+ *
+ * Return Value: an LDAP return code
+ *
+ * Messages: None
+ *********************************************************************/
+int
+slapi_int_register_extop(
+ Backend *pBE,
+ ExtendedOp **opList,
+ Slapi_PBlock *pPB )
+{
+ ExtendedOp *pTmpExtOp = NULL;
+ SLAPI_FUNC tmpFunc;
+ char **pTmpOIDs;
+ int rc = LDAP_OTHER;
+ int i;
+
+ if ( (*opList) == NULL ) {
+ *opList = createExtendedOp();
+ if ( (*opList) == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto error_return;
+ }
+ pTmpExtOp = *opList;
+
+ } else { /* Find the end of the list */
+ for ( pTmpExtOp = *opList; pTmpExtOp->ext_next != NULL;
+ pTmpExtOp = pTmpExtOp->ext_next )
+ ; /* EMPTY */
+ pTmpExtOp->ext_next = createExtendedOp();
+ if ( pTmpExtOp->ext_next == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto error_return;
+ }
+ pTmpExtOp = pTmpExtOp->ext_next;
+ }
+
+ rc = slapi_pblock_get( pPB,SLAPI_PLUGIN_EXT_OP_OIDLIST, &pTmpOIDs );
+ if ( rc != 0 ) {
+ rc = LDAP_OTHER;
+ goto error_return;
+ }
+
+ rc = slapi_pblock_get(pPB,SLAPI_PLUGIN_EXT_OP_FN, &tmpFunc);
+ if ( rc != 0 ) {
+ rc = LDAP_OTHER;
+ goto error_return;
+ }
+
+ if ( (pTmpOIDs == NULL) || (tmpFunc == NULL) ) {
+ rc = LDAP_OTHER;
+ goto error_return;
+ }
+
+ for ( i = 0; pTmpOIDs[i] != NULL; i++ ) {
+ pTmpExtOp->ext_oid.bv_val = pTmpOIDs[i];
+ pTmpExtOp->ext_oid.bv_len = strlen( pTmpOIDs[i] );
+ pTmpExtOp->ext_func = tmpFunc;
+ pTmpExtOp->ext_be = pBE;
+ if ( pTmpOIDs[i + 1] != NULL ) {
+ pTmpExtOp->ext_next = createExtendedOp();
+ if ( pTmpExtOp->ext_next == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ break;
+ }
+ pTmpExtOp = pTmpExtOp->ext_next;
+ }
+ }
+
+error_return:
+ return rc;
+}
+
+/*********************************************************************
+ * Function Name: slapi_int_get_extop_plugin
+ *
+ * Description: This routine gets the function address for a given function
+ * name.
+ *
+ * Input:
+ * funcName - name of the extended op function, ie. an OID.
+ *
+ * Output: pFuncAddr - the function address of the requested function name.
+ *
+ * Return Values: a pointer to a newly created ExtendOp structure or
+ * NULL - function failed
+ *
+ * Messages: None
+ *********************************************************************/
+int
+slapi_int_get_extop_plugin(
+ struct berval *reqoid,
+ SLAPI_FUNC *pFuncAddr )
+{
+ ExtendedOp *pTmpExtOp;
+
+ assert( reqoid != NULL );
+ assert( pFuncAddr != NULL );
+
+ *pFuncAddr = NULL;
+
+ if ( pGExtendedOps == NULL ) {
+ return LDAP_OTHER;
+ }
+
+ pTmpExtOp = pGExtendedOps;
+ while ( pTmpExtOp != NULL ) {
+ int rc;
+
+ rc = strcasecmp( reqoid->bv_val, pTmpExtOp->ext_oid.bv_val );
+ if ( rc == 0 ) {
+ *pFuncAddr = pTmpExtOp->ext_func;
+ break;
+ }
+ pTmpExtOp = pTmpExtOp->ext_next;
+ }
+
+ return ( *pFuncAddr == NULL ? 1 : 0 );
+}
+
+/***************************************************************************
+ * This function is similar to slapi_int_get_extop_plugin above. except it returns one OID
+ * per call. It is called from root_dse_info (root_dse.c).
+ * The function is a modified version of get_supported_extop (file extended.c).
+ ***************************************************************************/
+struct berval *
+slapi_int_get_supported_extop( int index )
+{
+ ExtendedOp *ext;
+
+ for ( ext = pGExtendedOps ; ext != NULL && --index >= 0;
+ ext = ext->ext_next) {
+ ; /* empty */
+ }
+
+ if ( ext == NULL ) {
+ return NULL;
+ }
+
+ return &ext->ext_oid ;
+}
+
+/*********************************************************************
+ * Function Name: slapi_int_load_plugin
+ *
+ * Description: This routine loads the specified DLL, gets and executes the init function
+ * if requested.
+ *
+ * Input:
+ * pPlugin - a pointer to a Slapi_PBlock struct which will be passed to
+ * the DLL init function.
+ * path - path name of the DLL to be load.
+ * initfunc - either the DLL initialization function or an OID of the
+ * loaded extended operation.
+ * doInit - if it is TRUE, execute the init function, otherwise, save the
+ * function address but not execute it.
+ *
+ * Output: pInitFunc - the function address of the loaded function. This param
+ * should be not be null if doInit is FALSE.
+ * pLdHandle - handle returned by lt_dlopen()
+ *
+ * Return Values: LDAP_SUCCESS, LDAP_LOCAL_ERROR
+ *
+ * Messages: None
+ *********************************************************************/
+
+static int
+slapi_int_load_plugin(
+ Slapi_PBlock *pPlugin,
+ const char *path,
+ const char *initfunc,
+ int doInit,
+ SLAPI_FUNC *pInitFunc,
+ lt_dlhandle *pLdHandle )
+{
+ int rc = LDAP_SUCCESS;
+ SLAPI_FUNC fpInitFunc = NULL;
+
+ assert( pLdHandle != NULL );
+
+ if ( lt_dlinit() ) {
+ return LDAP_LOCAL_ERROR;
+ }
+
+ /* load in the module */
+ *pLdHandle = lt_dlopen( path );
+ if ( *pLdHandle == NULL ) {
+ fprintf( stderr, "failed to load plugin %s: %s\n",
+ path, lt_dlerror() );
+ return LDAP_LOCAL_ERROR;
+ }
+
+ fpInitFunc = (SLAPI_FUNC)lt_dlsym( *pLdHandle, initfunc );
+ if ( fpInitFunc == NULL ) {
+ fprintf( stderr, "failed to find symbol %s in plugin %s: %s\n",
+ initfunc, path, lt_dlerror() );
+ lt_dlclose( *pLdHandle );
+ return LDAP_LOCAL_ERROR;
+ }
+
+ if ( doInit ) {
+ rc = ( *fpInitFunc )( pPlugin );
+ if ( rc != LDAP_SUCCESS ) {
+ lt_dlclose( *pLdHandle );
+ }
+
+ } else {
+ *pInitFunc = fpInitFunc;
+ }
+
+ return rc;
+}
+
+/*
+ * Special support for computed attribute plugins
+ */
+int
+slapi_int_call_plugins(
+ Backend *be,
+ int funcType,
+ Slapi_PBlock *pPB )
+{
+
+ int rc = 0;
+ SLAPI_FUNC *pGetPlugin = NULL, *tmpPlugin = NULL;
+
+ if ( pPB == NULL ) {
+ return 1;
+ }
+
+ rc = slapi_int_get_plugins( be, funcType, &tmpPlugin );
+ if ( rc != LDAP_SUCCESS || tmpPlugin == NULL ) {
+ /* Nothing to do, front-end should ignore. */
+ return rc;
+ }
+
+ for ( pGetPlugin = tmpPlugin ; *pGetPlugin != NULL; pGetPlugin++ ) {
+ rc = (*pGetPlugin)(pPB);
+
+ /*
+ * Only non-postoperation plugins abort processing on
+ * failure (confirmed with SLAPI specification).
+ */
+ if ( !SLAPI_PLUGIN_IS_POST_FN( funcType ) && rc != 0 ) {
+ /*
+ * Plugins generally return negative error codes
+ * to indicate failure, although in the case of
+ * bind plugins they may return SLAPI_BIND_xxx
+ */
+ break;
+ }
+ }
+
+ slapi_ch_free( (void **)&tmpPlugin );
+
+ return rc;
+}
+
+int
+slapi_int_read_config(
+ Backend *be,
+ const char *fname,
+ int lineno,
+ int argc,
+ char **argv,
+ int index )
+{
+ int iType = -1;
+ int numPluginArgc = 0;
+
+ if ( argc < 4 ) {
+ fprintf( stderr,
+ "%s: line %d: missing arguments "
+ "in \"plugin <plugin_type> <lib_path> "
+ "<init_function> [<arguments>]\" line\n",
+ fname, lineno );
+ return 1;
+ }
+
+ /* automatically instantiate overlay if necessary */
+ if ( !slapi_over_is_inst( be ) ) {
+ ConfigReply cr = { 0 };
+ if ( slapi_over_config( be, &cr ) != 0 ) {
+ fprintf( stderr, "Failed to instantiate SLAPI overlay: "
+ "err=%d msg=\"%s\"\n", cr.err, cr.msg );
+ return -1;
+ }
+ }
+
+ if ( strcasecmp( argv[1], "preoperation" ) == 0 ) {
+ iType = SLAPI_PLUGIN_PREOPERATION;
+ } else if ( strcasecmp( argv[1], "postoperation" ) == 0 ) {
+ iType = SLAPI_PLUGIN_POSTOPERATION;
+ } else if ( strcasecmp( argv[1], "extendedop" ) == 0 ) {
+ iType = SLAPI_PLUGIN_EXTENDEDOP;
+ } else if ( strcasecmp( argv[1], "object" ) == 0 ) {
+ iType = SLAPI_PLUGIN_OBJECT;
+ } else {
+ fprintf( stderr, "%s: line %d: invalid plugin type \"%s\".\n",
+ fname, lineno, argv[1] );
+ return 1;
+ }
+
+ numPluginArgc = argc - 4;
+
+ if ( iType == SLAPI_PLUGIN_PREOPERATION ||
+ iType == SLAPI_PLUGIN_EXTENDEDOP ||
+ iType == SLAPI_PLUGIN_POSTOPERATION ||
+ iType == SLAPI_PLUGIN_OBJECT ) {
+ int rc;
+ Slapi_PBlock *pPlugin;
+
+ pPlugin = plugin_pblock_new( iType, numPluginArgc, argv );
+ if (pPlugin == NULL) {
+ return 1;
+ }
+
+ if (iType == SLAPI_PLUGIN_EXTENDEDOP) {
+ rc = slapi_int_register_extop(be, &pGExtendedOps, pPlugin);
+ if ( rc != LDAP_SUCCESS ) {
+ slapi_pblock_destroy( pPlugin );
+ return 1;
+ }
+ }
+
+ rc = slapi_int_register_plugin_index( be, pPlugin, index );
+ if ( rc != LDAP_SUCCESS ) {
+ if ( iType == SLAPI_PLUGIN_EXTENDEDOP ) {
+ slapi_int_unregister_extop( be, &pGExtendedOps, pPlugin );
+ }
+ slapi_pblock_destroy( pPlugin );
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int
+slapi_int_unregister_plugin(
+ Backend *be,
+ Slapi_PBlock *pPlugin,
+ Slapi_PBlock *pPrev
+)
+{
+ int type;
+
+ assert( pPlugin != NULL );
+
+ slapi_pblock_get( pPlugin, SLAPI_PLUGIN_TYPE, (void *)&type );
+ if ( type == SLAPI_PLUGIN_EXTENDEDOP ) {
+ slapi_int_unregister_extop( be, &pGExtendedOps, pPlugin );
+ }
+
+ if ( pPrev != NULL ) {
+ Slapi_PBlock *pNext = NULL;
+
+ slapi_pblock_get( pPlugin, SLAPI_IBM_PBLOCK, &pNext );
+ slapi_pblock_set( pPrev, SLAPI_IBM_PBLOCK, &pNext );
+ }
+ slapi_pblock_destroy( pPlugin );
+
+ return LDAP_SUCCESS;
+}
+
+int
+slapi_int_unregister_plugins(
+ Backend *be,
+ int index
+)
+{
+ Slapi_PBlock *pTmpPB = NULL;
+ Slapi_PBlock *pSavePB = NULL;
+ int rc = LDAP_SUCCESS;
+
+ pTmpPB = SLAPI_BACKEND_PBLOCK( be );
+ if ( pTmpPB == NULL ) {
+ return ( index < 0 ) ? LDAP_SUCCESS : LDAP_OTHER;
+ }
+
+ if ( index < 0 ) {
+ /* All plugins must go */
+ while ( pTmpPB != NULL && rc == LDAP_SUCCESS ) {
+ pSavePB = pTmpPB;
+ rc = slapi_pblock_get( pTmpPB, SLAPI_IBM_PBLOCK, &pTmpPB );
+ if ( pSavePB != NULL ) {
+ slapi_int_unregister_plugin( be, pSavePB, NULL );
+ }
+ }
+ } else if ( index == 0 ) {
+ slapi_pblock_get( pTmpPB, SLAPI_IBM_PBLOCK, &pSavePB );
+ SLAPI_BACKEND_PBLOCK( be ) = pSavePB;
+ slapi_int_unregister_plugin( be, pTmpPB, NULL );
+ } else {
+ int pos = -1;
+ while ( pTmpPB != NULL && rc == LDAP_SUCCESS && ++pos < index ) {
+ pSavePB = pTmpPB;
+ rc = slapi_pblock_get( pTmpPB, SLAPI_IBM_PBLOCK, &pTmpPB );
+ }
+ if ( pos == index ) {
+ slapi_int_unregister_plugin( be, pTmpPB, pSavePB );
+ }
+ }
+ return rc;
+}
+
+void
+slapi_int_plugin_unparse(
+ Backend *be,
+ BerVarray *out
+)
+{
+ Slapi_PBlock *pp;
+ int i, j;
+ char **argv, ibuf[32], *ptr;
+ struct berval idx, bv;
+
+ *out = NULL;
+ idx.bv_val = ibuf;
+ i = 0;
+
+ for ( pp = SLAPI_BACKEND_PBLOCK( be );
+ pp != NULL;
+ slapi_pblock_get( pp, SLAPI_IBM_PBLOCK, &pp ) )
+ {
+ slapi_pblock_get( pp, SLAPI_X_CONFIG_ARGV, &argv );
+ if ( argv == NULL ) /* could be dynamic plugin */
+ continue;
+ idx.bv_len = snprintf( idx.bv_val, sizeof( ibuf ), "{%d}", i );
+ if ( idx.bv_len >= sizeof( ibuf ) ) {
+ /* FIXME: just truncating by now */
+ idx.bv_len = sizeof( ibuf ) - 1;
+ }
+ bv.bv_len = idx.bv_len;
+ for (j=1; argv[j]; j++) {
+ bv.bv_len += strlen(argv[j]);
+ if ( j ) bv.bv_len++;
+ }
+ bv.bv_val = ch_malloc( bv.bv_len + 1 );
+ ptr = lutil_strcopy( bv.bv_val, ibuf );
+ for (j=1; argv[j]; j++) {
+ if ( j ) *ptr++ = ' ';
+ ptr = lutil_strcopy( ptr, argv[j] );
+ }
+ ber_bvarray_add( out, &bv );
+ }
+}
+#endif /* HAVE_LTDL_H */
diff --git a/servers/slapd/slapi/printmsg.c b/servers/slapd/slapi/printmsg.c
new file mode 100644
index 0000000..15b6004
--- /dev/null
+++ b/servers/slapd/slapi/printmsg.c
@@ -0,0 +1,100 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1997,2002-2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by IBM Corporation for use in
+ * IBM products and subsequently ported to OpenLDAP Software by
+ * Steve Omrani.
+ */
+
+#include <portable.h>
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/stdarg.h>
+#include <ac/unistd.h>
+#include <fcntl.h>
+#include <ac/errno.h>
+
+#include <ldap.h>
+#include <ldap_config.h>
+#include <slap.h>
+#include <slapi.h>
+
+#include <ldap_pvt_thread.h>
+
+/* Single threads access to routine */
+ldap_pvt_thread_mutex_t slapi_printmessage_mutex;
+char *slapi_log_file = NULL;
+int slapi_log_level = SLAPI_LOG_PLUGIN;
+
+int
+slapi_int_log_error(
+ int level,
+ char *subsystem,
+ char *fmt,
+ va_list arglist )
+{
+ int rc = 0;
+ FILE *fp = NULL;
+
+ char timeStr[100];
+ struct tm *ltm;
+ time_t currentTime;
+
+ assert( subsystem != NULL );
+ assert( fmt != NULL );
+
+ ldap_pvt_thread_mutex_lock( &slapi_printmessage_mutex ) ;
+
+ /* for now, we log all severities */
+ if ( level <= slapi_log_level ) {
+ fp = fopen( slapi_log_file, "a" );
+ if ( fp == NULL) {
+ rc = -1;
+ goto done;
+ }
+
+ /*
+ * FIXME: could block
+ */
+ while ( lockf( fileno( fp ), F_LOCK, 0 ) != 0 ) {
+ /* DO NOTHING */ ;
+ }
+
+ time( &currentTime );
+ ltm = localtime( &currentTime );
+ strftime( timeStr, sizeof(timeStr), "%x %X", ltm );
+ fputs( timeStr, fp );
+
+ fprintf( fp, " %s: ", subsystem );
+ vfprintf( fp, fmt, arglist );
+ if ( fmt[ strlen( fmt ) - 1 ] != '\n' ) {
+ fputs( "\n", fp );
+ }
+ fflush( fp );
+
+ lockf( fileno( fp ), F_ULOCK, 0 );
+
+ fclose( fp );
+
+ } else {
+ rc = -1;
+ }
+
+done:
+ ldap_pvt_thread_mutex_unlock( &slapi_printmessage_mutex );
+
+ return rc;
+}
diff --git a/servers/slapd/slapi/proto-slapi.h b/servers/slapd/slapi/proto-slapi.h
new file mode 100644
index 0000000..9d52510
--- /dev/null
+++ b/servers/slapd/slapi/proto-slapi.h
@@ -0,0 +1,93 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1997,2002-2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by IBM Corporation for use in
+ * IBM products and subsequently ported to OpenLDAP Software by
+ * Steve Omrani. Additional significant contributors include:
+ * Luke Howard
+ */
+
+#ifndef _PROTO_SLAPI_H
+#define _PROTO_SLAPI_H
+
+LDAP_BEGIN_DECL
+
+/* slapi_utils.c */
+LDAP_SLAPI_F (LDAPMod **) slapi_int_modifications2ldapmods LDAP_P(( Modifications * ));
+LDAP_SLAPI_F (Modifications *) slapi_int_ldapmods2modifications LDAP_P(( Operation *op, LDAPMod ** ));
+LDAP_SLAPI_F (int) slapi_int_count_controls LDAP_P(( LDAPControl **ctrls ));
+LDAP_SLAPI_F (char **) slapi_get_supported_extended_ops LDAP_P((void));
+LDAP_SLAPI_F (int) slapi_int_access_allowed LDAP_P((Operation *op, Entry *entry, AttributeDescription *desc, struct berval *val, slap_access_t access, AccessControlState *state ));
+
+/* slapi_ops.c */
+LDAP_SLAPI_F (int) slapi_int_response LDAP_P(( Slapi_Operation *op, SlapReply *rs ));
+LDAP_SLAPI_F (void) slapi_int_connection_init_pb LDAP_P(( Slapi_PBlock *pb, ber_tag_t OpType ));
+LDAP_SLAPI_F (void) slapi_int_connection_done_pb LDAP_P(( Slapi_PBlock *pb ));
+
+/* slapi_pblock.c */
+LDAP_SLAPI_F (int) slapi_pblock_delete_param LDAP_P(( Slapi_PBlock *p, int param ));
+LDAP_SLAPI_F (void) slapi_pblock_clear LDAP_P(( Slapi_PBlock *pb ));
+
+LDAP_SLAPI_F (int) slapi_int_pblock_get_first LDAP_P(( Backend *be, Slapi_PBlock **pb ));
+LDAP_SLAPI_F (int) slapi_int_pblock_get_next LDAP_P(( Slapi_PBlock **pb ));
+
+#define PBLOCK_ASSERT_CONN( _pb ) do { \
+ assert( (_pb) != NULL ); \
+ assert( (_pb)->pb_conn != NULL ); \
+ } while (0)
+
+#define PBLOCK_ASSERT_OP( _pb, _tag ) do { \
+ PBLOCK_ASSERT_CONN( _pb ); \
+ assert( (_pb)->pb_op != NULL ); \
+ assert( (_pb)->pb_rs != NULL ); \
+ if ( _tag != 0 ) \
+ assert( (_pb)->pb_op->o_tag == (_tag)); \
+ } while (0)
+
+#define PBLOCK_ASSERT_INTOP( _pb, _tag ) do { \
+ PBLOCK_ASSERT_OP( _pb, _tag ); \
+ assert( (_pb)->pb_intop ); \
+ assert( (_pb)->pb_op == (Operation *)pb->pb_conn->c_pending_ops.stqh_first ); \
+ } while (0)
+
+/* plugin.c */
+LDAP_SLAPI_F (int) slapi_int_register_plugin LDAP_P((Backend *be, Slapi_PBlock *pPB));
+LDAP_SLAPI_F (int) slapi_int_register_plugin_index LDAP_P((Backend *be, Slapi_PBlock *pPB, int index));
+LDAP_SLAPI_F (int) slapi_int_call_plugins LDAP_P((Backend *be, int funcType, Slapi_PBlock * pPB));
+LDAP_SLAPI_F (int) slapi_int_get_plugins LDAP_P((Backend *be, int functype, SLAPI_FUNC **ppFuncPtrs));
+LDAP_SLAPI_F (int) slapi_int_register_extop LDAP_P((Backend *pBE, ExtendedOp **opList, Slapi_PBlock *pPB));
+LDAP_SLAPI_F (int) slapi_int_get_extop_plugin LDAP_P((struct berval *reqoid, SLAPI_FUNC *pFuncAddr ));
+LDAP_SLAPI_F (struct berval *) slapi_int_get_supported_extop LDAP_P(( int ));
+LDAP_SLAPI_F (int) slapi_int_unregister_plugins LDAP_P((Backend *be, int index));
+LDAP_SLAPI_F (int) slapi_int_read_config LDAP_P((Backend *be, const char *fname, int lineno,
+ int argc, char **argv, int index ));
+LDAP_SLAPI_F (void) slapi_int_plugin_unparse LDAP_P((Backend *be, BerVarray *out ));
+LDAP_SLAPI_F (int) slapi_int_initialize LDAP_P((void));
+
+/* slapi_ext.c */
+LDAP_SLAPI_F (int) slapi_int_init_object_extensions LDAP_P((void));
+LDAP_SLAPI_F (int) slapi_int_free_object_extensions LDAP_P((int objecttype, void *object));
+LDAP_SLAPI_F (int) slapi_int_create_object_extensions LDAP_P((int objecttype, void *object));
+LDAP_SLAPI_F (int) slapi_int_clear_object_extensions LDAP_P((int objecttype, void *object));
+
+/* slapi_overlay.c */
+LDAP_SLAPI_F (int) slapi_over_is_inst LDAP_P((BackendDB *));
+LDAP_SLAPI_F (int) slapi_over_config LDAP_P((BackendDB *, ConfigReply *));
+
+LDAP_END_DECL
+
+#endif /* _PROTO_SLAPI_H */
+
diff --git a/servers/slapd/slapi/slapi.h b/servers/slapd/slapi/slapi.h
new file mode 100644
index 0000000..14d6bc1
--- /dev/null
+++ b/servers/slapd/slapi/slapi.h
@@ -0,0 +1,204 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1997,2002-2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by IBM Corporation for use in
+ * IBM products and subsequently ported to OpenLDAP Software by
+ * Steve Omrani. Additional significant contributors include:
+ * Luke Howard
+ */
+
+#ifdef LDAP_SLAPI /* SLAPI is OPTIONAL */
+
+#ifndef _SLAPI_H
+#define _SLAPI_H
+
+LDAP_BEGIN_DECL
+
+/*
+ * Quick 'n' dirty to make struct slapi_* in slapi-plugin.h opaque
+ */
+#define slapi_entry Entry
+#define slapi_attr Attribute
+#define slapi_value berval
+#define slapi_valueset berval *
+#define slapi_filter Filter
+
+LDAP_END_DECL
+
+#include <slapi-plugin.h>
+
+LDAP_BEGIN_DECL
+
+#define SLAPI_OVERLAY_NAME "slapi"
+
+#define SLAPI_OPERATION_PBLOCK(_op) ((_op)->o_callback->sc_private)
+#define SLAPI_BACKEND_PBLOCK(_be) ((_be)->be_pb)
+
+#define SLAPI_OPERATION_EXTENSIONS(_op) ((_op)->o_hdr->oh_extensions)
+#define SLAPI_CONNECTION_EXTENSIONS(_conn) ((_conn)->c_extensions)
+
+#define SLAPI_CONTROL_MANAGEDSAIT_OID LDAP_CONTROL_MANAGEDSAIT
+#define SLAPI_CONTROL_SORTEDSEARCH_OID LDAP_CONTROL_SORTREQUEST
+#define SLAPI_CONTROL_PAGED_RESULTS_OID LDAP_CONTROL_PAGEDRESULTS
+
+typedef int (*SLAPI_FUNC)( Slapi_PBlock *pb );
+
+typedef struct _slapi_control {
+ int s_ctrl_num;
+ char **s_ctrl_oids;
+ unsigned long *s_ctrl_ops;
+} Slapi_Control;
+
+typedef struct _ExtendedOp {
+ struct berval ext_oid;
+ SLAPI_FUNC ext_func;
+ Backend *ext_be;
+ struct _ExtendedOp *ext_next;
+} ExtendedOp;
+
+/* Computed attribute support */
+struct _computed_attr_context {
+ Slapi_PBlock *cac_pb;
+ Operation *cac_op;
+ void *cac_private;
+};
+
+/* for slapi_attr_type_cmp() */
+#define SLAPI_TYPE_CMP_EXACT 0
+#define SLAPI_TYPE_CMP_BASE 1
+#define SLAPI_TYPE_CMP_SUBTYPE 2
+
+typedef enum slapi_extension_e {
+ SLAPI_X_EXT_CONNECTION = 0,
+ SLAPI_X_EXT_OPERATION = 1,
+ SLAPI_X_EXT_MAX = 2
+} slapi_extension_t;
+
+struct slapi_dn {
+ unsigned char flag;
+ struct berval dn;
+ struct berval ndn;
+};
+
+struct slapi_rdn {
+ unsigned char flag;
+ struct berval bv;
+ LDAPRDN rdn;
+};
+
+/*
+ * Was: slapi_pblock.h
+ */
+
+#ifndef NO_PBLOCK_CLASS /* where's this test from? */
+
+typedef enum slapi_pblock_class_e {
+ PBLOCK_CLASS_INVALID = 0,
+ PBLOCK_CLASS_INTEGER,
+ PBLOCK_CLASS_LONG_INTEGER,
+ PBLOCK_CLASS_POINTER,
+ PBLOCK_CLASS_FUNCTION_POINTER
+} slapi_pblock_class_t;
+
+#define PBLOCK_SUCCESS (0)
+#define PBLOCK_ERROR (-1)
+#define PBLOCK_MAX_PARAMS 100
+
+union slapi_pblock_value {
+ int pv_integer;
+ long pv_long_integer;
+ void *pv_pointer;
+ int (*pv_function_pointer)();
+};
+
+struct slapi_pblock {
+ ldap_pvt_thread_mutex_t pb_mutex;
+ int pb_nParams;
+ int pb_params[PBLOCK_MAX_PARAMS];
+ union slapi_pblock_value pb_values[PBLOCK_MAX_PARAMS];
+ /* native types */
+ Connection *pb_conn;
+ Operation *pb_op;
+ SlapReply *pb_rs;
+ int pb_intop;
+ char pb_textbuf[ SLAP_TEXT_BUFLEN ];
+};
+
+#endif /* !NO_PBLOCK_CLASS */
+
+/*
+ * Was: plugin.h
+ */
+
+#define SLAPI_PLUGIN_IS_POST_FN(x) ((x) >= SLAPI_PLUGIN_POST_BIND_FN && (x) <= SLAPI_PLUGIN_BE_POST_DELETE_FN)
+
+#define SLAPI_IBM_PBLOCK -3
+
+#define SLAPI_ENTRY_PRE_OP 52
+#define SLAPI_ENTRY_POST_OP 53
+
+/* This is the spelling in the SunOne 5.2 docs */
+#define SLAPI_RES_CONTROLS SLAPI_RESCONTROLS
+
+#define SLAPI_ABANDON_MSGID 120
+
+#define SLAPI_OPERATION_PARAMETERS 138
+
+#define SLAPI_SEQ_TYPE 150
+#define SLAPI_SEQ_ATTRNAME 151
+#define SLAPI_SEQ_VAL 152
+
+#define SLAPI_MR_FILTER_ENTRY 170
+#define SLAPI_MR_FILTER_TYPE 171
+#define SLAPI_MR_FILTER_VALUE 172
+#define SLAPI_MR_FILTER_OID 173
+#define SLAPI_MR_FILTER_DNATTRS 174
+
+#define SLAPI_LDIF2DB_FILE 180
+#define SLAPI_LDIF2DB_REMOVEDUPVALS 185
+
+#define SLAPI_DB2LDIF_PRINTKEY 183
+
+#define SLAPI_CHANGENUMBER 197
+#define SLAPI_LOG_OPERATION 198
+
+#define SLAPI_DBSIZE 199
+
+#define SLAPI_PLUGIN_DB_TEST_FN 227
+#define SLAPI_PLUGIN_DB_NO_ACL 250
+
+/* OpenLDAP private parameters */
+#define SLAPI_PLUGIN_COMPUTE_EVALUATOR_FN 1200
+#define SLAPI_PLUGIN_COMPUTE_SEARCH_REWRITER_FN 1201
+
+#define SLAPI_X_CONFIG_ARGV 1400
+#define SLAPI_X_INTOP_FLAGS 1401
+#define SLAPI_X_INTOP_RESULT_CALLBACK 1402
+#define SLAPI_X_INTOP_SEARCH_ENTRY_CALLBACK 1403
+#define SLAPI_X_INTOP_REFERRAL_ENTRY_CALLBACK 1404
+#define SLAPI_X_INTOP_CALLBACK_DATA 1405
+#define SLAPI_X_OLD_RESCONTROLS 1406
+
+LDAP_SLAPI_V (ldap_pvt_thread_mutex_t) slapi_hn_mutex;
+LDAP_SLAPI_V (ldap_pvt_thread_mutex_t) slapi_time_mutex;
+LDAP_SLAPI_V (ldap_pvt_thread_mutex_t) slapi_printmessage_mutex;
+LDAP_SLAPI_V (char *) slapi_log_file;
+LDAP_SLAPI_V (int) slapi_log_level;
+
+#include "proto-slapi.h"
+
+#endif /* _SLAPI_H */
+#endif /* LDAP_SLAPI */
diff --git a/servers/slapd/slapi/slapi_dn.c b/servers/slapd/slapi/slapi_dn.c
new file mode 100644
index 0000000..32d04d2
--- /dev/null
+++ b/servers/slapd/slapi/slapi_dn.c
@@ -0,0 +1,669 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Luke Howard for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <ac/string.h>
+#include <ac/stdarg.h>
+#include <ac/ctype.h>
+#include <ac/unistd.h>
+#include <ldap_pvt.h>
+
+#include <slap.h>
+#include <slapi.h>
+
+#ifdef LDAP_SLAPI
+#define FLAG_DN 0x1
+#define FLAG_NDN 0x2
+
+void slapi_sdn_init( Slapi_DN *sdn )
+{
+ sdn->flag = 0;
+ BER_BVZERO( &sdn->dn );
+ BER_BVZERO( &sdn->ndn );
+}
+
+Slapi_DN *slapi_sdn_new( void )
+{
+ Slapi_DN *sdn;
+
+ sdn = (Slapi_DN *)slapi_ch_malloc( sizeof(*sdn ));
+ slapi_sdn_init( sdn );
+
+ return sdn;
+}
+
+void slapi_sdn_done( Slapi_DN *sdn )
+{
+ if ( sdn == NULL )
+ return;
+
+ if ( sdn->flag & FLAG_DN ) {
+ slapi_ch_free_string( &sdn->dn.bv_val );
+ }
+ if ( sdn->flag & FLAG_NDN ) {
+ slapi_ch_free_string( &sdn->ndn.bv_val );
+ }
+
+ slapi_sdn_init( sdn );
+}
+
+void slapi_sdn_free( Slapi_DN **sdn )
+{
+ slapi_sdn_done( *sdn );
+ slapi_ch_free( (void **)sdn );
+}
+
+const char *slapi_sdn_get_dn( const Slapi_DN *sdn )
+{
+ if ( !BER_BVISNULL( &sdn->dn ) )
+ return sdn->dn.bv_val;
+ else
+ return sdn->ndn.bv_val;
+}
+
+const char *slapi_sdn_get_ndn( const Slapi_DN *sdn )
+{
+ if ( BER_BVISNULL( &sdn->ndn ) ) {
+ dnNormalize( 0, NULL, NULL,
+ (struct berval *)&sdn->dn, (struct berval *)&sdn->ndn, NULL );
+ ((Slapi_DN *)sdn)->flag |= FLAG_NDN;
+ }
+
+ return sdn->ndn.bv_val;
+}
+
+Slapi_DN *slapi_sdn_new_dn_byval( const char *dn )
+{
+ Slapi_DN *sdn;
+
+ sdn = slapi_sdn_new();
+ return slapi_sdn_set_dn_byval( sdn, dn );
+}
+
+Slapi_DN *slapi_sdn_new_ndn_byval( const char *ndn )
+{
+ Slapi_DN *sdn;
+
+ sdn = slapi_sdn_new();
+ return slapi_sdn_set_ndn_byval( sdn, ndn );
+}
+
+Slapi_DN *slapi_sdn_new_dn_byref( const char *dn )
+{
+ Slapi_DN *sdn;
+
+ sdn = slapi_sdn_new();
+ return slapi_sdn_set_dn_byref( sdn, dn );
+}
+
+Slapi_DN *slapi_sdn_new_ndn_byref( const char *ndn )
+{
+ Slapi_DN *sdn;
+
+ sdn = slapi_sdn_new();
+ return slapi_sdn_set_ndn_byref( sdn, ndn );
+}
+
+Slapi_DN *slapi_sdn_new_dn_passin( const char *dn )
+{
+ Slapi_DN *sdn;
+
+ sdn = slapi_sdn_new();
+ return slapi_sdn_set_dn_passin( sdn, dn );
+}
+
+Slapi_DN *slapi_sdn_set_dn_byval( Slapi_DN *sdn, const char *dn )
+{
+ if ( sdn == NULL ) {
+ return NULL;
+ }
+
+ slapi_sdn_done( sdn );
+ if ( dn != NULL ) {
+ sdn->dn.bv_val = slapi_ch_strdup( dn );
+ sdn->dn.bv_len = strlen( dn );
+ }
+ sdn->flag |= FLAG_DN;
+
+ return sdn;
+}
+
+Slapi_DN *slapi_sdn_set_dn_byref( Slapi_DN *sdn, const char *dn )
+{
+ if ( sdn == NULL )
+ return NULL;
+
+ slapi_sdn_done( sdn );
+ if ( dn != NULL ) {
+ sdn->dn.bv_val = (char *)dn;
+ sdn->dn.bv_len = strlen( dn );
+ }
+
+ return sdn;
+}
+
+Slapi_DN *slapi_sdn_set_dn_passin( Slapi_DN *sdn, const char *dn )
+{
+ if ( sdn == NULL )
+ return NULL;
+
+ slapi_sdn_set_dn_byref( sdn, dn );
+ sdn->flag |= FLAG_DN;
+
+ return sdn;
+}
+
+Slapi_DN *slapi_sdn_set_ndn_byval( Slapi_DN *sdn, const char *ndn )
+{
+ if ( sdn == NULL ) {
+ return NULL;
+ }
+
+ slapi_sdn_done( sdn );
+ if ( ndn != NULL ) {
+ sdn->ndn.bv_val = slapi_ch_strdup( ndn );
+ sdn->ndn.bv_len = strlen( ndn );
+ }
+ sdn->flag |= FLAG_NDN;
+
+ return sdn;
+}
+
+Slapi_DN *slapi_sdn_set_ndn_byref( Slapi_DN *sdn, const char *ndn )
+{
+ if ( sdn == NULL )
+ return NULL;
+
+ slapi_sdn_done( sdn );
+ if ( ndn != NULL ) {
+ sdn->ndn.bv_val = (char *)ndn;
+ sdn->ndn.bv_len = strlen( ndn );
+ }
+
+ return sdn;
+}
+
+Slapi_DN *slapi_sdn_set_ndn_passin( Slapi_DN *sdn, const char *ndn )
+{
+ if ( sdn == NULL )
+ return NULL;
+
+ slapi_sdn_set_ndn_byref( sdn, ndn );
+ sdn->flag |= FLAG_NDN;
+
+ return sdn;
+}
+
+void slapi_sdn_get_parent( const Slapi_DN *sdn, Slapi_DN *sdn_parent )
+{
+ struct berval parent_dn;
+
+ if ( !(sdn->flag & FLAG_DN) ) {
+ dnParent( (struct berval *)&sdn->ndn, &parent_dn );
+ slapi_sdn_set_ndn_byval( sdn_parent, parent_dn.bv_val );
+ } else {
+ dnParent( (struct berval *)&sdn->dn, &parent_dn );
+ slapi_sdn_set_dn_byval( sdn_parent, parent_dn.bv_val );
+ }
+}
+
+void slapi_sdn_get_backend_parent( const Slapi_DN *sdn,
+ Slapi_DN *sdn_parent,
+ const Slapi_Backend *backend )
+{
+ slapi_sdn_get_ndn( sdn );
+
+ if ( backend == NULL ||
+ be_issuffix( (Slapi_Backend *)backend, (struct berval *)&sdn->ndn ) == 0 ) {
+ slapi_sdn_get_parent( sdn, sdn_parent );
+ }
+
+}
+
+Slapi_DN * slapi_sdn_dup( const Slapi_DN *sdn )
+{
+ Slapi_DN *new_sdn;
+
+ new_sdn = slapi_sdn_new();
+ slapi_sdn_copy( sdn, new_sdn );
+
+ return new_sdn;
+}
+
+void slapi_sdn_copy( const Slapi_DN *from, Slapi_DN *to )
+{
+ slapi_sdn_set_dn_byval( to, from->dn.bv_val );
+}
+
+int slapi_sdn_compare( const Slapi_DN *sdn1, const Slapi_DN *sdn2 )
+{
+ int match = -1;
+
+ slapi_sdn_get_ndn( sdn1 );
+ slapi_sdn_get_ndn( sdn2 );
+
+ dnMatch( &match, 0, slap_schema.si_syn_distinguishedName, NULL,
+ (struct berval *)&sdn1->ndn, (void *)&sdn2->ndn );
+
+ return match;
+}
+
+int slapi_sdn_isempty( const Slapi_DN *sdn)
+{
+ return ( BER_BVISEMPTY( &sdn->dn ) && BER_BVISEMPTY( &sdn->ndn ) );
+}
+
+int slapi_sdn_issuffix( const Slapi_DN *sdn, const Slapi_DN *suffix_sdn )
+{
+ slapi_sdn_get_ndn( sdn );
+ slapi_sdn_get_ndn( suffix_sdn );
+
+ return dnIsSuffix( &sdn->ndn, &suffix_sdn->ndn );
+}
+
+int slapi_sdn_isparent( const Slapi_DN *parent, const Slapi_DN *child )
+{
+ Slapi_DN child_parent;
+
+ slapi_sdn_get_ndn( child );
+
+ slapi_sdn_init( &child_parent );
+ dnParent( (struct berval *)&child->ndn, &child_parent.ndn );
+
+ return ( slapi_sdn_compare( parent, &child_parent ) == 0 );
+}
+
+int slapi_sdn_isgrandparent( const Slapi_DN *parent, const Slapi_DN *child )
+{
+ Slapi_DN child_grandparent;
+
+ slapi_sdn_get_ndn( child );
+
+ slapi_sdn_init( &child_grandparent );
+ dnParent( (struct berval *)&child->ndn, &child_grandparent.ndn );
+ if ( child_grandparent.ndn.bv_len == 0 ) {
+ return 0;
+ }
+
+ dnParent( &child_grandparent.ndn, &child_grandparent.ndn );
+
+ return ( slapi_sdn_compare( parent, &child_grandparent ) == 0 );
+}
+
+int slapi_sdn_get_ndn_len( const Slapi_DN *sdn )
+{
+ slapi_sdn_get_ndn( sdn );
+
+ return sdn->ndn.bv_len;
+}
+
+int slapi_sdn_scope_test( const Slapi_DN *dn, const Slapi_DN *base, int scope )
+{
+ int rc;
+
+ switch ( scope ) {
+ case LDAP_SCOPE_BASE:
+ rc = ( slapi_sdn_compare( dn, base ) == 0 );
+ break;
+ case LDAP_SCOPE_ONELEVEL:
+ rc = slapi_sdn_isparent( base, dn );
+ break;
+ case LDAP_SCOPE_SUBTREE:
+ rc = slapi_sdn_issuffix( dn, base );
+ break;
+ default:
+ rc = 0;
+ break;
+ }
+
+ return rc;
+}
+
+void slapi_rdn_init( Slapi_RDN *rdn )
+{
+ rdn->flag = 0;
+ BER_BVZERO( &rdn->bv );
+ rdn->rdn = NULL;
+}
+
+Slapi_RDN *slapi_rdn_new( void )
+{
+ Slapi_RDN *rdn;
+
+ rdn = (Slapi_RDN *)slapi_ch_malloc( sizeof(*rdn ));
+ slapi_rdn_init( rdn );
+
+ return rdn;
+}
+
+Slapi_RDN *slapi_rdn_new_dn( const char *dn )
+{
+ Slapi_RDN *rdn;
+
+ rdn = slapi_rdn_new();
+ slapi_rdn_init_dn( rdn, dn );
+ return rdn;
+}
+
+Slapi_RDN *slapi_rdn_new_sdn( const Slapi_DN *sdn )
+{
+ return slapi_rdn_new_dn( slapi_sdn_get_dn( sdn ) );
+}
+
+Slapi_RDN *slapi_rdn_new_rdn( const Slapi_RDN *fromrdn )
+{
+ return slapi_rdn_new_dn( fromrdn->bv.bv_val );
+}
+
+void slapi_rdn_init_dn( Slapi_RDN *rdn, const char *dn )
+{
+ slapi_rdn_init( rdn );
+ slapi_rdn_set_dn( rdn, dn );
+}
+
+void slapi_rdn_init_sdn( Slapi_RDN *rdn, const Slapi_DN *sdn )
+{
+ slapi_rdn_init( rdn );
+ slapi_rdn_set_sdn( rdn, sdn );
+}
+
+void slapi_rdn_init_rdn( Slapi_RDN *rdn, const Slapi_RDN *fromrdn )
+{
+ slapi_rdn_init( rdn );
+ slapi_rdn_set_rdn( rdn, fromrdn );
+}
+
+void slapi_rdn_set_dn( Slapi_RDN *rdn, const char *dn )
+{
+ struct berval bv;
+
+ slapi_rdn_done( rdn );
+
+ BER_BVZERO( &bv );
+
+ if ( dn != NULL ) {
+ bv.bv_val = (char *)dn;
+ bv.bv_len = strlen( dn );
+ }
+
+ dnExtractRdn( &bv, &rdn->bv, NULL );
+ rdn->flag |= FLAG_DN;
+}
+
+void slapi_rdn_set_sdn( Slapi_RDN *rdn, const Slapi_DN *sdn )
+{
+ slapi_rdn_set_dn( rdn, slapi_sdn_get_dn( sdn ) );
+}
+
+void slapi_rdn_set_rdn( Slapi_RDN *rdn, const Slapi_RDN *fromrdn )
+{
+ slapi_rdn_set_dn( rdn, fromrdn->bv.bv_val );
+}
+
+void slapi_rdn_free( Slapi_RDN **rdn )
+{
+ slapi_rdn_done( *rdn );
+ slapi_ch_free( (void **)rdn );
+}
+
+void slapi_rdn_done( Slapi_RDN *rdn )
+{
+ if ( rdn->rdn != NULL ) {
+ ldap_rdnfree( rdn->rdn );
+ rdn->rdn = NULL;
+ }
+ slapi_ch_free_string( &rdn->bv.bv_val );
+ slapi_rdn_init( rdn );
+}
+
+const char *slapi_rdn_get_rdn( const Slapi_RDN *rdn )
+{
+ return rdn->bv.bv_val;
+}
+
+static int slapi_int_rdn_explode( Slapi_RDN *rdn )
+{
+ char *next;
+
+ if ( rdn->rdn != NULL ) {
+ return LDAP_SUCCESS;
+ }
+
+ return ldap_bv2rdn( &rdn->bv, &rdn->rdn, &next, LDAP_DN_FORMAT_LDAP );
+}
+
+static int slapi_int_rdn_implode( Slapi_RDN *rdn )
+{
+ struct berval bv;
+ int rc;
+
+ if ( rdn->rdn == NULL ) {
+ return LDAP_SUCCESS;
+ }
+
+ rc = ldap_rdn2bv( rdn->rdn, &bv, LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PRETTY );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ slapi_ch_free_string( &rdn->bv.bv_val );
+ rdn->bv = bv;
+
+ return 0;
+}
+
+int slapi_rdn_get_num_components( Slapi_RDN *rdn )
+{
+ int i;
+
+ if ( slapi_int_rdn_explode( rdn ) != LDAP_SUCCESS )
+ return 0;
+
+ for ( i = 0; rdn->rdn[i] != NULL; i++ )
+ ;
+
+ return i;
+}
+
+int slapi_rdn_get_first( Slapi_RDN *rdn, char **type, char **value )
+{
+ return slapi_rdn_get_next( rdn, 0, type, value );
+}
+
+int slapi_rdn_get_next( Slapi_RDN *rdn, int index, char **type, char **value )
+{
+ slapi_int_rdn_explode( rdn );
+
+ if ( rdn->rdn == NULL || rdn->rdn[index] == NULL )
+ return -1;
+
+ *type = rdn->rdn[index]->la_attr.bv_val;
+ *value = rdn->rdn[index]->la_value.bv_val;
+
+ return index + 1;
+}
+
+int slapi_rdn_get_index( Slapi_RDN *rdn, const char *type, const char *value, size_t length )
+{
+ int i, match;
+ struct berval bv;
+ AttributeDescription *ad = NULL;
+ const char *text;
+
+ slapi_int_rdn_explode( rdn );
+
+ if ( slap_str2ad( type, &ad, &text ) != LDAP_SUCCESS ) {
+ return -1;
+ }
+
+ bv.bv_val = (char *)value;
+ bv.bv_len = length;
+
+ for ( i = 0; rdn->rdn[i] != NULL; i++ ) {
+ if ( !slapi_attr_types_equivalent( ad->ad_cname.bv_val, type ))
+ continue;
+
+ if ( value_match( &match, ad, ad->ad_type->sat_equality, 0,
+ &rdn->rdn[i]->la_value, (void *)&bv, &text ) != LDAP_SUCCESS )
+ match = -1;
+
+ if ( match == 0 )
+ return i;
+ }
+
+ return -1;
+}
+
+int slapi_rdn_get_index_attr( Slapi_RDN *rdn, const char *type, char **value )
+{
+ int i;
+
+ for ( i = 0; rdn->rdn[i] != NULL; i++ ) {
+ if ( slapi_attr_types_equivalent( rdn->rdn[i]->la_attr.bv_val, type ) ) {
+ *value = rdn->rdn[i]->la_value.bv_val;
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+int slapi_rdn_contains( Slapi_RDN *rdn, const char *type, const char *value, size_t length )
+{
+ return ( slapi_rdn_get_index( rdn, type, value, length ) != -1 );
+}
+
+int slapi_rdn_contains_attr( Slapi_RDN *rdn, const char *type, char **value )
+{
+ return ( slapi_rdn_get_index_attr( rdn, type, value ) != -1 );
+}
+
+int slapi_rdn_compare( Slapi_RDN *rdn1, Slapi_RDN *rdn2 )
+{
+ struct berval nrdn1 = BER_BVNULL;
+ struct berval nrdn2 = BER_BVNULL;
+ int match;
+
+ rdnNormalize( 0, NULL, NULL, (struct berval *)&rdn1->bv, &nrdn1, NULL );
+ rdnNormalize( 0, NULL, NULL, (struct berval *)&rdn2->bv, &nrdn2, NULL );
+
+ if ( rdnMatch( &match, 0, NULL, NULL, &nrdn1, (void *)&nrdn2 ) != LDAP_SUCCESS) {
+ match = -1;
+ }
+
+ return match;
+}
+
+int slapi_rdn_isempty( const Slapi_RDN *rdn )
+{
+ return ( BER_BVISEMPTY( &rdn->bv ) );
+}
+
+int slapi_rdn_add( Slapi_RDN *rdn, const char *type, const char *value )
+{
+ char *s;
+ size_t len;
+
+ len = strlen(type) + 1 + strlen( value );
+ if ( !BER_BVISEMPTY( &rdn->bv ) ) {
+ len += 1 + rdn->bv.bv_len;
+ }
+
+ s = slapi_ch_malloc( len + 1 );
+
+ if ( BER_BVISEMPTY( &rdn->bv ) ) {
+ snprintf( s, len + 1, "%s=%s", type, value );
+ } else {
+ snprintf( s, len + 1, "%s=%s+%s", type, value, rdn->bv.bv_val );
+ }
+
+ slapi_rdn_done( rdn );
+
+ rdn->bv.bv_len = len;
+ rdn->bv.bv_val = s;
+
+ return 1;
+}
+
+int slapi_rdn_remove_index( Slapi_RDN *rdn, int atindex )
+{
+ int count, i;
+
+ count = slapi_rdn_get_num_components( rdn );
+
+ if ( atindex < 0 || atindex >= count )
+ return 0;
+
+ if ( rdn->rdn == NULL )
+ return 0;
+
+ slapi_ch_free_string( &rdn->rdn[atindex]->la_attr.bv_val );
+ slapi_ch_free_string( &rdn->rdn[atindex]->la_value.bv_val );
+
+ for ( i = atindex; i < count; i++ ) {
+ rdn->rdn[i] = rdn->rdn[i + 1];
+ }
+
+ if ( slapi_int_rdn_implode( rdn ) != LDAP_SUCCESS )
+ return 0;
+
+ return 1;
+}
+
+int slapi_rdn_remove( Slapi_RDN *rdn, const char *type, const char *value, size_t length )
+{
+ int index = slapi_rdn_get_index( rdn, type, value, length );
+
+ return slapi_rdn_remove_index( rdn, index );
+}
+
+int slapi_rdn_remove_attr( Slapi_RDN *rdn, const char *type )
+{
+ char *value;
+ int index = slapi_rdn_get_index_attr( rdn, type, &value );
+
+ return slapi_rdn_remove_index( rdn, index );
+}
+
+Slapi_DN *slapi_sdn_add_rdn( Slapi_DN *sdn, const Slapi_RDN *rdn )
+{
+ struct berval bv;
+
+ build_new_dn( &bv, &sdn->dn, (struct berval *)&rdn->bv, NULL );
+
+ slapi_sdn_done( sdn );
+ sdn->dn = bv;
+
+ return sdn;
+}
+
+Slapi_DN *slapi_sdn_set_parent( Slapi_DN *sdn, const Slapi_DN *parentdn )
+{
+ Slapi_RDN rdn;
+
+ slapi_rdn_init_sdn( &rdn, sdn );
+ slapi_sdn_set_dn_byref( sdn, slapi_sdn_get_dn( parentdn ) );
+ slapi_sdn_add_rdn( sdn, &rdn );
+ slapi_rdn_done( &rdn );
+
+ return sdn;
+}
+
+#endif /* LDAP_SLAPI */
diff --git a/servers/slapd/slapi/slapi_ext.c b/servers/slapd/slapi/slapi_ext.c
new file mode 100644
index 0000000..81a50f1
--- /dev/null
+++ b/servers/slapd/slapi/slapi_ext.c
@@ -0,0 +1,349 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* (C) Copyright PADL Software Pty Ltd. 2003
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that this notice is preserved
+ * and that due credit is given to PADL Software Pty Ltd. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Luke Howard for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <ac/string.h>
+#include <ac/stdarg.h>
+#include <ac/ctype.h>
+#include <ac/unistd.h>
+
+#ifdef LDAP_SLAPI
+
+#include <slap.h>
+#include <slapi.h>
+
+/*
+ * Object extensions
+ *
+ * We only support two types -- connection and operation extensions.
+ * Define more types in slapi.h
+ */
+
+/* global state */
+struct slapi_registered_extension_set {
+ ldap_pvt_thread_mutex_t mutex;
+ struct slapi_registered_extension {
+ int active;
+ int count;
+ slapi_extension_constructor_fnptr *constructors;
+ slapi_extension_destructor_fnptr *destructors;
+ } extensions[SLAPI_X_EXT_MAX];
+} registered_extensions;
+
+/* per-object state */
+struct slapi_extension_block {
+ void **extensions;
+};
+
+static int get_extension_block(int objecttype, void *object, struct slapi_extension_block **eblock, void **parent)
+{
+ switch ((slapi_extension_t) objecttype) {
+ case SLAPI_X_EXT_CONNECTION:
+ *eblock = ((Connection *)object)->c_extensions;
+ *parent = NULL;
+ break;
+ case SLAPI_X_EXT_OPERATION:
+ *eblock = ((Operation *)object)->o_hdr->oh_extensions;
+ *parent = ((Operation *)object)->o_conn;
+ break;
+ default:
+ return -1;
+ break;
+ }
+
+ if ( *eblock == NULL ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int map_extension_type(const char *objectname, slapi_extension_t *type)
+{
+ if ( strcasecmp( objectname, SLAPI_EXT_CONNECTION ) == 0 ) {
+ *type = SLAPI_X_EXT_CONNECTION;
+ } else if ( strcasecmp( objectname, SLAPI_EXT_OPERATION ) == 0 ) {
+ *type = SLAPI_X_EXT_OPERATION;
+ } else {
+ return -1;
+ }
+
+ return 0;
+}
+
+static void new_extension(struct slapi_extension_block *eblock,
+ int objecttype, void *object, void *parent,
+ int extensionhandle )
+{
+ slapi_extension_constructor_fnptr constructor;
+
+ assert( objecttype < SLAPI_X_EXT_MAX );
+ assert( extensionhandle < registered_extensions.extensions[objecttype].count );
+
+ assert( registered_extensions.extensions[objecttype].constructors != NULL );
+ constructor = registered_extensions.extensions[objecttype].constructors[extensionhandle];
+
+ assert( eblock->extensions[extensionhandle] == NULL );
+
+ if ( constructor != NULL ) {
+ eblock->extensions[extensionhandle] = (*constructor)( object, parent );
+ } else {
+ eblock->extensions[extensionhandle] = NULL;
+ }
+}
+
+static void free_extension(struct slapi_extension_block *eblock, int objecttype, void *object, void *parent, int extensionhandle )
+{
+ slapi_extension_destructor_fnptr destructor;
+
+ assert( objecttype < SLAPI_X_EXT_MAX );
+ assert( extensionhandle < registered_extensions.extensions[objecttype].count );
+
+ if ( eblock->extensions[extensionhandle] != NULL ) {
+ assert( registered_extensions.extensions[objecttype].destructors != NULL );
+ destructor = registered_extensions.extensions[objecttype].destructors[extensionhandle];
+ if ( destructor != NULL ) {
+ (*destructor)( eblock->extensions[extensionhandle], object, parent );
+ }
+ eblock->extensions[extensionhandle] = NULL;
+ }
+}
+
+void *slapi_get_object_extension(int objecttype, void *object, int extensionhandle)
+{
+ struct slapi_extension_block *eblock;
+ void *parent;
+
+ if ( get_extension_block( objecttype, object, &eblock, &parent ) != 0 ) {
+ return NULL;
+ }
+
+ if ( extensionhandle < registered_extensions.extensions[objecttype].count ) {
+ return eblock->extensions[extensionhandle];
+ }
+
+ return NULL;
+}
+
+void slapi_set_object_extension(int objecttype, void *object, int extensionhandle, void *extension)
+{
+ struct slapi_extension_block *eblock;
+ void *parent;
+
+ if ( get_extension_block( objecttype, object, &eblock, &parent ) != 0 ) {
+ return;
+ }
+
+ if ( extensionhandle < registered_extensions.extensions[objecttype].count ) {
+ /* free the old one */
+ free_extension( eblock, objecttype, object, parent, extensionhandle );
+
+ /* constructed by caller */
+ eblock->extensions[extensionhandle] = extension;
+ }
+}
+
+int slapi_register_object_extension(
+ const char *pluginname,
+ const char *objectname,
+ slapi_extension_constructor_fnptr constructor,
+ slapi_extension_destructor_fnptr destructor,
+ int *objecttype,
+ int *extensionhandle)
+{
+ int rc;
+ slapi_extension_t type;
+ struct slapi_registered_extension *re;
+
+ ldap_pvt_thread_mutex_lock( &registered_extensions.mutex );
+
+ rc = map_extension_type( objectname, &type );
+ if ( rc != 0 ) {
+ ldap_pvt_thread_mutex_unlock( &registered_extensions.mutex );
+ return rc;
+ }
+
+ *objecttype = (int)type;
+
+ re = &registered_extensions.extensions[*objecttype];
+
+ *extensionhandle = re->count;
+
+ if ( re->active ) {
+ /* can't add new extensions after objects have been created */
+ ldap_pvt_thread_mutex_unlock( &registered_extensions.mutex );
+ return -1;
+ }
+
+ re->count++;
+
+ if ( re->constructors == NULL ) {
+ re->constructors = (slapi_extension_constructor_fnptr *)slapi_ch_calloc( re->count,
+ sizeof( slapi_extension_constructor_fnptr ) );
+ } else {
+ re->constructors = (slapi_extension_constructor_fnptr *)slapi_ch_realloc( (char *)re->constructors,
+ re->count * sizeof( slapi_extension_constructor_fnptr ) );
+ }
+ re->constructors[*extensionhandle] = constructor;
+
+ if ( re->destructors == NULL ) {
+ re->destructors = (slapi_extension_destructor_fnptr *)slapi_ch_calloc( re->count,
+ sizeof( slapi_extension_destructor_fnptr ) );
+ } else {
+ re->destructors = (slapi_extension_destructor_fnptr *)slapi_ch_realloc( (char *)re->destructors,
+ re->count * sizeof( slapi_extension_destructor_fnptr ) );
+ }
+ re->destructors[*extensionhandle] = destructor;
+
+ ldap_pvt_thread_mutex_unlock( &registered_extensions.mutex );
+
+ return 0;
+}
+
+int slapi_int_create_object_extensions(int objecttype, void *object)
+{
+ int i;
+ struct slapi_extension_block *eblock;
+ void **peblock;
+ void *parent;
+
+ switch ((slapi_extension_t) objecttype) {
+ case SLAPI_X_EXT_CONNECTION:
+ peblock = &(((Connection *)object)->c_extensions);
+ parent = NULL;
+ break;
+ case SLAPI_X_EXT_OPERATION:
+ peblock = &(((Operation *)object)->o_hdr->oh_extensions);
+ parent = ((Operation *)object)->o_conn;
+ break;
+ default:
+ return -1;
+ break;
+ }
+
+ *peblock = NULL;
+
+ ldap_pvt_thread_mutex_lock( &registered_extensions.mutex );
+ if ( registered_extensions.extensions[objecttype].active == 0 ) {
+ /*
+ * once we've created some extensions, no new extensions can
+ * be registered.
+ */
+ registered_extensions.extensions[objecttype].active = 1;
+ }
+ ldap_pvt_thread_mutex_unlock( &registered_extensions.mutex );
+
+ eblock = (struct slapi_extension_block *)slapi_ch_calloc( 1, sizeof(*eblock) );
+
+ if ( registered_extensions.extensions[objecttype].count ) {
+ eblock->extensions = (void **)slapi_ch_calloc( registered_extensions.extensions[objecttype].count, sizeof(void *) );
+ for ( i = 0; i < registered_extensions.extensions[objecttype].count; i++ ) {
+ new_extension( eblock, objecttype, object, parent, i );
+ }
+ } else {
+ eblock->extensions = NULL;
+ }
+
+ *peblock = eblock;
+
+ return 0;
+}
+
+int slapi_int_free_object_extensions(int objecttype, void *object)
+{
+ int i;
+ struct slapi_extension_block *eblock;
+ void **peblock;
+ void *parent;
+
+ switch ((slapi_extension_t) objecttype) {
+ case SLAPI_X_EXT_CONNECTION:
+ peblock = &(((Connection *)object)->c_extensions);
+ parent = NULL;
+ break;
+ case SLAPI_X_EXT_OPERATION:
+ peblock = &(((Operation *)object)->o_hdr->oh_extensions);
+ parent = ((Operation *)object)->o_conn;
+ break;
+ default:
+ return -1;
+ break;
+ }
+
+ eblock = (struct slapi_extension_block *)*peblock;
+
+ if ( eblock != NULL && eblock->extensions != NULL ) {
+ for ( i = registered_extensions.extensions[objecttype].count - 1; i >= 0; --i ) {
+ free_extension( eblock, objecttype, object, parent, i );
+ }
+
+ slapi_ch_free( (void **)&eblock->extensions );
+ }
+
+ slapi_ch_free( peblock );
+
+ return 0;
+}
+
+/* for reusable object types */
+int slapi_int_clear_object_extensions(int objecttype, void *object)
+{
+ int i;
+ struct slapi_extension_block *eblock;
+ void *parent;
+
+ if ( get_extension_block( objecttype, object, &eblock, &parent ) != 0 ) {
+ return -1;
+ }
+
+ if ( eblock->extensions == NULL ) {
+ /* no extensions */
+ return 0;
+ }
+
+ for ( i = registered_extensions.extensions[objecttype].count - 1; i >= 0; --i ) {
+ free_extension( eblock, objecttype, object, parent, i );
+ }
+
+ for ( i = 0; i < registered_extensions.extensions[objecttype].count; i++ ) {
+ new_extension( eblock, objecttype, object, parent, i );
+ }
+
+ return 0;
+}
+
+int slapi_int_init_object_extensions(void)
+{
+ memset( &registered_extensions, 0, sizeof( registered_extensions ) );
+
+ if ( ldap_pvt_thread_mutex_init( &registered_extensions.mutex ) != 0 ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif /* LDAP_SLAPI */
diff --git a/servers/slapd/slapi/slapi_ops.c b/servers/slapd/slapi/slapi_ops.c
new file mode 100644
index 0000000..e079189
--- /dev/null
+++ b/servers/slapd/slapi/slapi_ops.c
@@ -0,0 +1,950 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1997,2002-2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by IBM Corporation for use in
+ * IBM products and subsequently ported to OpenLDAP Software by
+ * Steve Omrani. Additional significant contributors include:
+ * Luke Howard
+ */
+
+#include "portable.h"
+
+#include <ac/string.h>
+#include <ac/stdarg.h>
+#include <ac/ctype.h>
+#include <ac/unistd.h>
+
+#include <slap.h>
+#include <lber_pvt.h>
+#include <slapi.h>
+
+#ifdef LDAP_SLAPI
+
+static struct Listener slapi_listener = {
+ BER_BVC("slapi://"),
+ BER_BVC("slapi://")
+};
+
+static LDAPControl **
+slapi_int_dup_controls( LDAPControl **controls )
+{
+ LDAPControl **c;
+ size_t i;
+
+ if ( controls == NULL )
+ return NULL;
+
+ for ( i = 0; controls[i] != NULL; i++ )
+ ;
+
+ c = (LDAPControl **) slapi_ch_calloc( i + 1, sizeof(LDAPControl *) );
+
+ for ( i = 0; controls[i] != NULL; i++ ) {
+ c[i] = slapi_dup_control( controls[i] );
+ }
+
+ return c;
+}
+
+static int
+slapi_int_result(
+ Operation *op,
+ SlapReply *rs )
+{
+ Slapi_PBlock *pb = SLAPI_OPERATION_PBLOCK( op );
+ plugin_result_callback prc = NULL;
+ void *callback_data = NULL;
+ LDAPControl **ctrls = NULL;
+
+ assert( pb != NULL );
+
+ slapi_pblock_get( pb, SLAPI_X_INTOP_RESULT_CALLBACK, (void **)&prc );
+ slapi_pblock_get( pb, SLAPI_X_INTOP_CALLBACK_DATA, &callback_data );
+
+ /* we need to duplicate controls because they might go out of scope */
+ ctrls = slapi_int_dup_controls( rs->sr_ctrls );
+ slapi_pblock_set( pb, SLAPI_RESCONTROLS, ctrls );
+
+ if ( prc != NULL ) {
+ (*prc)( rs->sr_err, callback_data );
+ }
+
+ return rs->sr_err;
+}
+
+static int
+slapi_int_search_entry(
+ Operation *op,
+ SlapReply *rs )
+{
+ Slapi_PBlock *pb = SLAPI_OPERATION_PBLOCK( op );
+ plugin_search_entry_callback psec = NULL;
+ void *callback_data = NULL;
+ int rc = LDAP_SUCCESS;
+
+ assert( pb != NULL );
+
+ slapi_pblock_get( pb, SLAPI_X_INTOP_SEARCH_ENTRY_CALLBACK, (void **)&psec );
+ slapi_pblock_get( pb, SLAPI_X_INTOP_CALLBACK_DATA, &callback_data );
+
+ if ( psec != NULL ) {
+ rc = (*psec)( rs->sr_entry, callback_data );
+ }
+
+ return rc;
+}
+
+static int
+slapi_int_search_reference(
+ Operation *op,
+ SlapReply *rs )
+{
+ int i, rc = LDAP_SUCCESS;
+ plugin_referral_entry_callback prec = NULL;
+ void *callback_data = NULL;
+ Slapi_PBlock *pb = SLAPI_OPERATION_PBLOCK( op );
+
+ assert( pb != NULL );
+
+ slapi_pblock_get( pb, SLAPI_X_INTOP_REFERRAL_ENTRY_CALLBACK, (void **)&prec );
+ slapi_pblock_get( pb, SLAPI_X_INTOP_CALLBACK_DATA, &callback_data );
+
+ if ( prec != NULL ) {
+ for ( i = 0; rs->sr_ref[i].bv_val != NULL; i++ ) {
+ rc = (*prec)( rs->sr_ref[i].bv_val, callback_data );
+ if ( rc != LDAP_SUCCESS ) {
+ break;
+ }
+ }
+ }
+
+ return rc;
+}
+
+int
+slapi_int_response( Slapi_Operation *op, SlapReply *rs )
+{
+ int rc;
+
+ switch ( rs->sr_type ) {
+ case REP_RESULT:
+ rc = slapi_int_result( op, rs );
+ break;
+ case REP_SEARCH:
+ rc = slapi_int_search_entry( op, rs );
+ break;
+ case REP_SEARCHREF:
+ rc = slapi_int_search_reference( op, rs );
+ break;
+ default:
+ rc = LDAP_OTHER;
+ break;
+ }
+
+ assert( rc != SLAP_CB_CONTINUE ); /* never try to send a wire response */
+
+ return rc;
+}
+
+static int
+slapi_int_get_ctrls( Slapi_PBlock *pb )
+{
+ LDAPControl **c;
+ int rc = LDAP_SUCCESS;
+
+ if ( pb->pb_op->o_ctrls != NULL ) {
+ for ( c = pb->pb_op->o_ctrls; *c != NULL; c++ ) {
+ rc = slap_parse_ctrl( pb->pb_op, pb->pb_rs, *c, &pb->pb_rs->sr_text );
+ if ( rc != LDAP_SUCCESS )
+ break;
+ }
+ }
+
+ return rc;
+}
+
+void
+slapi_int_connection_init_pb( Slapi_PBlock *pb, ber_tag_t tag )
+{
+ Connection *conn;
+ Operation *op;
+ ber_len_t max = sockbuf_max_incoming;
+
+ conn = (Connection *) slapi_ch_calloc( 1, sizeof(Connection) );
+
+ LDAP_STAILQ_INIT( &conn->c_pending_ops );
+
+ op = (Operation *) slapi_ch_calloc( 1, sizeof(OperationBuffer) );
+ op->o_hdr = &((OperationBuffer *) op)->ob_hdr;
+ op->o_controls = ((OperationBuffer *) op)->ob_controls;
+
+ op->o_callback = (slap_callback *) slapi_ch_calloc( 1, sizeof(slap_callback) );
+ op->o_callback->sc_response = slapi_int_response;
+ op->o_callback->sc_cleanup = NULL;
+ op->o_callback->sc_private = pb;
+ op->o_callback->sc_next = NULL;
+
+ conn->c_pending_ops.stqh_first = op;
+
+ /* connection object authorization information */
+ conn->c_authtype = LDAP_AUTH_NONE;
+ BER_BVZERO( &conn->c_authmech );
+ BER_BVZERO( &conn->c_dn );
+ BER_BVZERO( &conn->c_ndn );
+
+ conn->c_listener = &slapi_listener;
+ ber_dupbv( &conn->c_peer_domain, (struct berval *)&slap_unknown_bv );
+ ber_dupbv( &conn->c_peer_name, (struct berval *)&slap_unknown_bv );
+
+ LDAP_STAILQ_INIT( &conn->c_ops );
+
+ BER_BVZERO( &conn->c_sasl_bind_mech );
+ conn->c_sasl_authctx = NULL;
+ conn->c_sasl_sockctx = NULL;
+ conn->c_sasl_extra = NULL;
+
+ conn->c_sb = ber_sockbuf_alloc();
+
+ ber_sockbuf_ctrl( conn->c_sb, LBER_SB_OPT_SET_MAX_INCOMING, &max );
+
+ conn->c_currentber = NULL;
+
+ /* should check status of thread calls */
+ ldap_pvt_thread_mutex_init( &conn->c_mutex );
+ ldap_pvt_thread_mutex_init( &conn->c_write1_mutex );
+ ldap_pvt_thread_cond_init( &conn->c_write1_cv );
+
+ ldap_pvt_thread_mutex_lock( &conn->c_mutex );
+
+ conn->c_n_ops_received = 0;
+ conn->c_n_ops_executing = 0;
+ conn->c_n_ops_pending = 0;
+ conn->c_n_ops_completed = 0;
+ conn->c_n_ops_async = 0;
+
+ conn->c_n_get = 0;
+ conn->c_n_read = 0;
+ conn->c_n_write = 0;
+
+ conn->c_protocol = LDAP_VERSION3;
+
+ conn->c_activitytime = conn->c_starttime = slap_get_time();
+
+ /*
+ * A real connection ID is required, because syncrepl associates
+ * pending CSNs with unique ( connection, operation ) tuples.
+ * Setting a fake connection ID will cause slap_get_commit_csn()
+ * to return a stale value.
+ */
+ connection_assign_nextid( conn );
+
+ conn->c_conn_state = SLAP_C_ACTIVE;
+
+ conn->c_ssf = conn->c_transport_ssf = local_ssf;
+ conn->c_tls_ssf = 0;
+
+ backend_connection_init( conn );
+
+ conn->c_send_ldap_result = slap_send_ldap_result;
+ conn->c_send_search_entry = slap_send_search_entry;
+ conn->c_send_ldap_extended = slap_send_ldap_extended;
+ conn->c_send_search_reference = slap_send_search_reference;
+
+ /* operation object */
+ op->o_tag = tag;
+ op->o_protocol = LDAP_VERSION3;
+ BER_BVZERO( &op->o_authmech );
+ op->o_time = slap_get_time();
+ op->o_do_not_cache = 1;
+ op->o_threadctx = ldap_pvt_thread_pool_context();
+ op->o_tmpmemctx = NULL;
+ op->o_tmpmfuncs = &ch_mfuncs;
+ op->o_conn = conn;
+ op->o_connid = conn->c_connid;
+ op->o_bd = frontendDB;
+
+ /* extensions */
+ slapi_int_create_object_extensions( SLAPI_X_EXT_OPERATION, op );
+ slapi_int_create_object_extensions( SLAPI_X_EXT_CONNECTION, conn );
+
+ pb->pb_rs = (SlapReply *)slapi_ch_calloc( 1, sizeof(SlapReply) );
+ pb->pb_op = op;
+ pb->pb_conn = conn;
+ pb->pb_intop = 1;
+
+ ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
+}
+
+static void
+slapi_int_set_operation_dn( Slapi_PBlock *pb )
+{
+ Backend *be;
+ Operation *op = pb->pb_op;
+
+ if ( BER_BVISNULL( &op->o_ndn ) ) {
+ /* set to root DN */
+ be = select_backend( &op->o_req_ndn, 1 );
+ if ( be != NULL ) {
+ ber_dupbv( &op->o_dn, &be->be_rootdn );
+ ber_dupbv( &op->o_ndn, &be->be_rootndn );
+ }
+ }
+}
+
+void
+slapi_int_connection_done_pb( Slapi_PBlock *pb )
+{
+ Connection *conn;
+ Operation *op;
+
+ PBLOCK_ASSERT_INTOP( pb, 0 );
+
+ conn = pb->pb_conn;
+ op = pb->pb_op;
+
+ /* free allocated DNs */
+ if ( !BER_BVISNULL( &op->o_dn ) )
+ op->o_tmpfree( op->o_dn.bv_val, op->o_tmpmemctx );
+ if ( !BER_BVISNULL( &op->o_ndn ) )
+ op->o_tmpfree( op->o_ndn.bv_val, op->o_tmpmemctx );
+
+ if ( !BER_BVISNULL( &op->o_req_dn ) )
+ op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx );
+ if ( !BER_BVISNULL( &op->o_req_ndn ) )
+ op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
+
+ switch ( op->o_tag ) {
+ case LDAP_REQ_MODRDN:
+ if ( !BER_BVISNULL( &op->orr_newrdn ))
+ op->o_tmpfree( op->orr_newrdn.bv_val, op->o_tmpmemctx );
+ if ( !BER_BVISNULL( &op->orr_nnewrdn ))
+ op->o_tmpfree( op->orr_nnewrdn.bv_val, op->o_tmpmemctx );
+ if ( op->orr_newSup != NULL ) {
+ assert( !BER_BVISNULL( op->orr_newSup ) );
+ op->o_tmpfree( op->orr_newSup->bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( op->orr_newSup, op->o_tmpmemctx );
+ }
+ if ( op->orr_nnewSup != NULL ) {
+ assert( !BER_BVISNULL( op->orr_nnewSup ) );
+ op->o_tmpfree( op->orr_nnewSup->bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( op->orr_nnewSup, op->o_tmpmemctx );
+ }
+ slap_mods_free( op->orr_modlist, 1 );
+ break;
+ case LDAP_REQ_ADD:
+ slap_mods_free( op->ora_modlist, 0 );
+ break;
+ case LDAP_REQ_MODIFY:
+ slap_mods_free( op->orm_modlist, 1 );
+ break;
+ case LDAP_REQ_SEARCH:
+ if ( op->ors_attrs != NULL ) {
+ op->o_tmpfree( op->ors_attrs, op->o_tmpmemctx );
+ op->ors_attrs = NULL;
+ }
+ break;
+ default:
+ break;
+ }
+
+ slapi_ch_free_string( &conn->c_authmech.bv_val );
+ slapi_ch_free_string( &conn->c_dn.bv_val );
+ slapi_ch_free_string( &conn->c_ndn.bv_val );
+ slapi_ch_free_string( &conn->c_peer_domain.bv_val );
+ slapi_ch_free_string( &conn->c_peer_name.bv_val );
+
+ if ( conn->c_sb != NULL ) {
+ ber_sockbuf_free( conn->c_sb );
+ }
+
+ slapi_int_free_object_extensions( SLAPI_X_EXT_OPERATION, op );
+ slapi_int_free_object_extensions( SLAPI_X_EXT_CONNECTION, conn );
+
+ slapi_ch_free( (void **)&pb->pb_op->o_callback );
+ slapi_ch_free( (void **)&pb->pb_op );
+ slapi_ch_free( (void **)&pb->pb_conn );
+ slapi_ch_free( (void **)&pb->pb_rs );
+}
+
+static int
+slapi_int_func_internal_pb( Slapi_PBlock *pb, slap_operation_t which )
+{
+ SlapReply *rs = pb->pb_rs;
+ int rc;
+
+ PBLOCK_ASSERT_INTOP( pb, 0 );
+
+ rc = slapi_int_get_ctrls( pb );
+ if ( rc != LDAP_SUCCESS ) {
+ rs->sr_err = rc;
+ return rc;
+ }
+
+ pb->pb_op->o_bd = frontendDB;
+ return (&frontendDB->be_bind)[which]( pb->pb_op, pb->pb_rs );
+}
+
+int
+slapi_delete_internal_pb( Slapi_PBlock *pb )
+{
+ if ( pb == NULL ) {
+ return -1;
+ }
+
+ PBLOCK_ASSERT_INTOP( pb, LDAP_REQ_DELETE );
+
+ slapi_int_func_internal_pb( pb, op_delete );
+
+ return 0;
+}
+
+int
+slapi_add_internal_pb( Slapi_PBlock *pb )
+{
+ SlapReply *rs;
+ Slapi_Entry *entry_orig = NULL;
+ OpExtraDB oex;
+ int rc;
+
+ if ( pb == NULL ) {
+ return -1;
+ }
+
+ PBLOCK_ASSERT_INTOP( pb, LDAP_REQ_ADD );
+
+ rs = pb->pb_rs;
+
+ entry_orig = pb->pb_op->ora_e;
+ pb->pb_op->ora_e = NULL;
+
+ /*
+ * The caller can specify a new entry, or a target DN and set
+ * of modifications, but not both.
+ */
+ if ( entry_orig != NULL ) {
+ if ( pb->pb_op->ora_modlist != NULL || !BER_BVISNULL( &pb->pb_op->o_req_ndn )) {
+ rs->sr_err = LDAP_PARAM_ERROR;
+ goto cleanup;
+ }
+
+ assert( BER_BVISNULL( &pb->pb_op->o_req_dn ) ); /* shouldn't get set */
+ ber_dupbv( &pb->pb_op->o_req_dn, &entry_orig->e_name );
+ ber_dupbv( &pb->pb_op->o_req_ndn, &entry_orig->e_nname );
+ } else if ( pb->pb_op->ora_modlist == NULL || BER_BVISNULL( &pb->pb_op->o_req_ndn )) {
+ rs->sr_err = LDAP_PARAM_ERROR;
+ goto cleanup;
+ }
+
+ pb->pb_op->ora_e = (Entry *)slapi_ch_calloc( 1, sizeof(Entry) );
+ ber_dupbv( &pb->pb_op->ora_e->e_name, &pb->pb_op->o_req_dn );
+ ber_dupbv( &pb->pb_op->ora_e->e_nname, &pb->pb_op->o_req_ndn );
+
+ if ( entry_orig != NULL ) {
+ assert( pb->pb_op->ora_modlist == NULL );
+
+ rs->sr_err = slap_entry2mods( entry_orig, &pb->pb_op->ora_modlist,
+ &rs->sr_text, pb->pb_textbuf, sizeof( pb->pb_textbuf ) );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto cleanup;
+ }
+ } else {
+ assert( pb->pb_op->ora_modlist != NULL );
+ }
+
+ rs->sr_err = slap_mods_check( pb->pb_op, pb->pb_op->ora_modlist, &rs->sr_text,
+ pb->pb_textbuf, sizeof( pb->pb_textbuf ), NULL );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto cleanup;
+ }
+
+ /* Duplicate the values, because we may call slapi_entry_free() */
+ rs->sr_err = slap_mods2entry( pb->pb_op->ora_modlist, &pb->pb_op->ora_e,
+ 1, 0, &rs->sr_text, pb->pb_textbuf, sizeof( pb->pb_textbuf ) );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto cleanup;
+ }
+
+ oex.oe.oe_key = (void *)do_add;
+ oex.oe_db = NULL;
+ LDAP_SLIST_INSERT_HEAD(&pb->pb_op->o_extra, &oex.oe, oe_next);
+ rc = slapi_int_func_internal_pb( pb, op_add );
+ LDAP_SLIST_REMOVE(&pb->pb_op->o_extra, &oex.oe, OpExtra, oe_next);
+
+ if ( !rc ) {
+ if ( pb->pb_op->ora_e != NULL && oex.oe_db != NULL ) {
+ BackendDB *bd = pb->pb_op->o_bd;
+
+ pb->pb_op->o_bd = oex.oe_db;
+ be_entry_release_w( pb->pb_op, pb->pb_op->ora_e );
+ pb->pb_op->ora_e = NULL;
+ pb->pb_op->o_bd = bd;
+ }
+ }
+
+cleanup:
+
+ if ( pb->pb_op->ora_e != NULL ) {
+ slapi_entry_free( pb->pb_op->ora_e );
+ pb->pb_op->ora_e = NULL;
+ }
+ if ( entry_orig != NULL ) {
+ pb->pb_op->ora_e = entry_orig;
+ slap_mods_free( pb->pb_op->ora_modlist, 1 );
+ pb->pb_op->ora_modlist = NULL;
+ }
+
+ return 0;
+}
+
+int
+slapi_modrdn_internal_pb( Slapi_PBlock *pb )
+{
+ if ( pb == NULL ) {
+ return -1;
+ }
+
+ PBLOCK_ASSERT_INTOP( pb, LDAP_REQ_MODRDN );
+
+ if ( BER_BVISEMPTY( &pb->pb_op->o_req_ndn ) ) {
+ pb->pb_rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ goto cleanup;
+ }
+
+ slapi_int_func_internal_pb( pb, op_modrdn );
+
+cleanup:
+
+ return 0;
+}
+
+int
+slapi_modify_internal_pb( Slapi_PBlock *pb )
+{
+ SlapReply *rs;
+
+ if ( pb == NULL ) {
+ return -1;
+ }
+
+ PBLOCK_ASSERT_INTOP( pb, LDAP_REQ_MODIFY );
+
+ rs = pb->pb_rs;
+
+ if ( pb->pb_op->orm_modlist == NULL ) {
+ rs->sr_err = LDAP_PARAM_ERROR;
+ goto cleanup;
+ }
+
+ if ( BER_BVISEMPTY( &pb->pb_op->o_req_ndn ) ) {
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ goto cleanup;
+ }
+
+ rs->sr_err = slap_mods_check( pb->pb_op, pb->pb_op->orm_modlist,
+ &rs->sr_text, pb->pb_textbuf, sizeof( pb->pb_textbuf ), NULL );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ goto cleanup;
+ }
+
+ slapi_int_func_internal_pb( pb, op_modify );
+
+cleanup:
+
+ return 0;
+}
+
+static int
+slapi_int_search_entry_callback( Slapi_Entry *entry, void *callback_data )
+{
+ int nentries = 0, i = 0;
+ Slapi_Entry **head = NULL, **tp;
+ Slapi_PBlock *pb = (Slapi_PBlock *)callback_data;
+
+ PBLOCK_ASSERT_INTOP( pb, LDAP_REQ_SEARCH );
+
+ entry = slapi_entry_dup( entry );
+ if ( entry == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+
+ slapi_pblock_get( pb, SLAPI_NENTRIES, &nentries );
+ slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &head );
+
+ i = nentries + 1;
+ if ( nentries == 0 ) {
+ tp = (Slapi_Entry **)slapi_ch_malloc( 2 * sizeof(Slapi_Entry *) );
+ if ( tp == NULL ) {
+ slapi_entry_free( entry );
+ return LDAP_NO_MEMORY;
+ }
+
+ tp[0] = entry;
+ } else {
+ tp = (Slapi_Entry **)slapi_ch_realloc( (char *)head,
+ sizeof(Slapi_Entry *) * ( i + 1 ) );
+ if ( tp == NULL ) {
+ slapi_entry_free( entry );
+ return LDAP_NO_MEMORY;
+ }
+ tp[i - 1] = entry;
+ }
+ tp[i] = NULL;
+
+ slapi_pblock_set( pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, (void *)tp );
+ slapi_pblock_set( pb, SLAPI_NENTRIES, (void *)&i );
+
+ return LDAP_SUCCESS;
+}
+
+int
+slapi_search_internal_pb( Slapi_PBlock *pb )
+{
+ return slapi_search_internal_callback_pb( pb,
+ (void *)pb,
+ NULL,
+ slapi_int_search_entry_callback,
+ NULL );
+}
+
+int
+slapi_search_internal_callback_pb( Slapi_PBlock *pb,
+ void *callback_data,
+ plugin_result_callback prc,
+ plugin_search_entry_callback psec,
+ plugin_referral_entry_callback prec )
+{
+ int free_filter = 0;
+ SlapReply *rs;
+
+ if ( pb == NULL ) {
+ return -1;
+ }
+
+ PBLOCK_ASSERT_INTOP( pb, LDAP_REQ_SEARCH );
+
+ rs = pb->pb_rs;
+
+ /* search callback and arguments */
+ slapi_pblock_set( pb, SLAPI_X_INTOP_RESULT_CALLBACK, (void *)prc );
+ slapi_pblock_set( pb, SLAPI_X_INTOP_SEARCH_ENTRY_CALLBACK, (void *)psec );
+ slapi_pblock_set( pb, SLAPI_X_INTOP_REFERRAL_ENTRY_CALLBACK, (void *)prec );
+ slapi_pblock_set( pb, SLAPI_X_INTOP_CALLBACK_DATA, (void *)callback_data );
+
+ if ( BER_BVISEMPTY( &pb->pb_op->ors_filterstr )) {
+ rs->sr_err = LDAP_PARAM_ERROR;
+ goto cleanup;
+ }
+
+ if ( pb->pb_op->ors_filter == NULL ) {
+ pb->pb_op->ors_filter = slapi_str2filter( pb->pb_op->ors_filterstr.bv_val );
+ if ( pb->pb_op->ors_filter == NULL ) {
+ rs->sr_err = LDAP_PROTOCOL_ERROR;
+ goto cleanup;
+ }
+
+ free_filter = 1;
+ }
+
+ slapi_int_func_internal_pb( pb, op_search );
+
+cleanup:
+ if ( free_filter ) {
+ slapi_filter_free( pb->pb_op->ors_filter, 1 );
+ pb->pb_op->ors_filter = NULL;
+ }
+
+ slapi_pblock_delete_param( pb, SLAPI_X_INTOP_RESULT_CALLBACK );
+ slapi_pblock_delete_param( pb, SLAPI_X_INTOP_SEARCH_ENTRY_CALLBACK );
+ slapi_pblock_delete_param( pb, SLAPI_X_INTOP_REFERRAL_ENTRY_CALLBACK );
+ slapi_pblock_delete_param( pb, SLAPI_X_INTOP_CALLBACK_DATA );
+
+ return 0;
+}
+
+/* Wrappers for old API */
+
+void
+slapi_search_internal_set_pb( Slapi_PBlock *pb,
+ const char *base,
+ int scope,
+ const char *filter,
+ char **attrs,
+ int attrsonly,
+ LDAPControl **controls,
+ const char *uniqueid,
+ Slapi_ComponentId *plugin_identity,
+ int operation_flags )
+{
+ int no_limit = SLAP_NO_LIMIT;
+ int deref = LDAP_DEREF_NEVER;
+
+ slapi_int_connection_init_pb( pb, LDAP_REQ_SEARCH );
+ slapi_pblock_set( pb, SLAPI_SEARCH_TARGET, (void *)base );
+ slapi_pblock_set( pb, SLAPI_SEARCH_SCOPE, (void *)&scope );
+ slapi_pblock_set( pb, SLAPI_SEARCH_FILTER, (void *)0 );
+ slapi_pblock_set( pb, SLAPI_SEARCH_STRFILTER, (void *)filter );
+ slapi_pblock_set( pb, SLAPI_SEARCH_ATTRS, (void *)attrs );
+ slapi_pblock_set( pb, SLAPI_SEARCH_ATTRSONLY, (void *)&attrsonly );
+ slapi_pblock_set( pb, SLAPI_REQCONTROLS, (void *)controls );
+ slapi_pblock_set( pb, SLAPI_TARGET_UNIQUEID, (void *)uniqueid );
+ slapi_pblock_set( pb, SLAPI_PLUGIN_IDENTITY, (void *)plugin_identity );
+ slapi_pblock_set( pb, SLAPI_X_INTOP_FLAGS, (void *)&operation_flags );
+ slapi_pblock_set( pb, SLAPI_SEARCH_DEREF, (void *)&deref );
+ slapi_pblock_set( pb, SLAPI_SEARCH_SIZELIMIT, (void *)&no_limit );
+ slapi_pblock_set( pb, SLAPI_SEARCH_TIMELIMIT, (void *)&no_limit );
+
+ slapi_int_set_operation_dn( pb );
+}
+
+Slapi_PBlock *
+slapi_search_internal(
+ char *ldn,
+ int scope,
+ char *filStr,
+ LDAPControl **controls,
+ char **attrs,
+ int attrsonly )
+{
+ Slapi_PBlock *pb;
+
+ pb = slapi_pblock_new();
+
+ slapi_search_internal_set_pb( pb, ldn, scope, filStr,
+ attrs, attrsonly,
+ controls, NULL, NULL, 0 );
+
+ slapi_search_internal_pb( pb );
+
+ return pb;
+}
+
+void
+slapi_modify_internal_set_pb( Slapi_PBlock *pb,
+ const char *dn,
+ LDAPMod **mods,
+ LDAPControl **controls,
+ const char *uniqueid,
+ Slapi_ComponentId *plugin_identity,
+ int operation_flags )
+{
+ slapi_int_connection_init_pb( pb, LDAP_REQ_MODIFY );
+ slapi_pblock_set( pb, SLAPI_MODIFY_TARGET, (void *)dn );
+ slapi_pblock_set( pb, SLAPI_MODIFY_MODS, (void *)mods );
+ slapi_pblock_set( pb, SLAPI_REQCONTROLS, (void *)controls );
+ slapi_pblock_set( pb, SLAPI_TARGET_UNIQUEID, (void *)uniqueid );
+ slapi_pblock_set( pb, SLAPI_PLUGIN_IDENTITY, (void *)plugin_identity );
+ slapi_pblock_set( pb, SLAPI_X_INTOP_FLAGS, (void *)&operation_flags );
+ slapi_int_set_operation_dn( pb );
+}
+
+/* Function : slapi_modify_internal
+ *
+ * Description: Plugin functions call this routine to modify an entry
+ * in the backend directly
+ * Return values : LDAP_SUCCESS
+ * LDAP_PARAM_ERROR
+ * LDAP_NO_MEMORY
+ * LDAP_OTHER
+ * LDAP_UNWILLING_TO_PERFORM
+*/
+Slapi_PBlock *
+slapi_modify_internal(
+ char *ldn,
+ LDAPMod **mods,
+ LDAPControl **controls,
+ int log_change )
+{
+ Slapi_PBlock *pb;
+
+ pb = slapi_pblock_new();
+
+ slapi_modify_internal_set_pb( pb, ldn, mods, controls, NULL, NULL, 0 );
+ slapi_pblock_set( pb, SLAPI_LOG_OPERATION, (void *)&log_change );
+ slapi_modify_internal_pb( pb );
+
+ return pb;
+}
+
+int
+slapi_add_internal_set_pb( Slapi_PBlock *pb,
+ const char *dn,
+ LDAPMod **attrs,
+ LDAPControl **controls,
+ Slapi_ComponentId *plugin_identity,
+ int operation_flags )
+{
+ slapi_int_connection_init_pb( pb, LDAP_REQ_ADD );
+ slapi_pblock_set( pb, SLAPI_ADD_TARGET, (void *)dn );
+ slapi_pblock_set( pb, SLAPI_MODIFY_MODS, (void *)attrs );
+ slapi_pblock_set( pb, SLAPI_REQCONTROLS, (void *)controls );
+ slapi_pblock_set( pb, SLAPI_PLUGIN_IDENTITY, (void *)plugin_identity );
+ slapi_pblock_set( pb, SLAPI_X_INTOP_FLAGS, (void *)&operation_flags );
+ slapi_int_set_operation_dn( pb );
+
+ return 0;
+}
+
+Slapi_PBlock *
+slapi_add_internal(
+ char * dn,
+ LDAPMod **attrs,
+ LDAPControl **controls,
+ int log_change )
+{
+ Slapi_PBlock *pb;
+
+ pb = slapi_pblock_new();
+
+ slapi_add_internal_set_pb( pb, dn, attrs, controls, NULL, 0);
+ slapi_pblock_set( pb, SLAPI_LOG_OPERATION, (void *)&log_change );
+ slapi_add_internal_pb( pb );
+
+ return pb;
+}
+
+void
+slapi_add_entry_internal_set_pb( Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ LDAPControl **controls,
+ Slapi_ComponentId *plugin_identity,
+ int operation_flags )
+{
+ slapi_int_connection_init_pb( pb, LDAP_REQ_ADD );
+ slapi_pblock_set( pb, SLAPI_ADD_ENTRY, (void *)e );
+ slapi_pblock_set( pb, SLAPI_REQCONTROLS, (void *)controls );
+ slapi_pblock_set( pb, SLAPI_PLUGIN_IDENTITY, (void *)plugin_identity );
+ slapi_pblock_set( pb, SLAPI_X_INTOP_FLAGS, (void *)&operation_flags );
+ slapi_int_set_operation_dn( pb );
+}
+
+Slapi_PBlock *
+slapi_add_entry_internal(
+ Slapi_Entry *e,
+ LDAPControl **controls,
+ int log_change )
+{
+ Slapi_PBlock *pb;
+
+ pb = slapi_pblock_new();
+
+ slapi_add_entry_internal_set_pb( pb, e, controls, NULL, 0 );
+ slapi_pblock_set( pb, SLAPI_LOG_OPERATION, (void *)&log_change );
+ slapi_add_internal_pb( pb );
+
+ return pb;
+}
+
+void
+slapi_rename_internal_set_pb( Slapi_PBlock *pb,
+ const char *olddn,
+ const char *newrdn,
+ const char *newsuperior,
+ int deloldrdn,
+ LDAPControl **controls,
+ const char *uniqueid,
+ Slapi_ComponentId *plugin_identity,
+ int operation_flags )
+{
+ slapi_int_connection_init_pb( pb, LDAP_REQ_MODRDN );
+ slapi_pblock_set( pb, SLAPI_MODRDN_TARGET, (void *)olddn );
+ slapi_pblock_set( pb, SLAPI_MODRDN_NEWRDN, (void *)newrdn );
+ slapi_pblock_set( pb, SLAPI_MODRDN_NEWSUPERIOR, (void *)newsuperior );
+ slapi_pblock_set( pb, SLAPI_MODRDN_DELOLDRDN, (void *)&deloldrdn );
+ slapi_pblock_set( pb, SLAPI_REQCONTROLS, (void *)controls );
+ slapi_pblock_set( pb, SLAPI_TARGET_UNIQUEID, (void *)uniqueid );
+ slapi_pblock_set( pb, SLAPI_PLUGIN_IDENTITY, (void *)plugin_identity );
+ slapi_pblock_set( pb, SLAPI_X_INTOP_FLAGS, (void *)&operation_flags );
+ slap_modrdn2mods( pb->pb_op, pb->pb_rs );
+ slapi_int_set_operation_dn( pb );
+}
+
+/* Function : slapi_modrdn_internal
+ *
+ * Description : Plugin functions call this routine to modify the rdn
+ * of an entry in the backend directly
+ * Return values : LDAP_SUCCESS
+ * LDAP_PARAM_ERROR
+ * LDAP_NO_MEMORY
+ * LDAP_OTHER
+ * LDAP_UNWILLING_TO_PERFORM
+ *
+ * NOTE: This function does not support the "newSuperior" option from LDAP V3.
+ */
+Slapi_PBlock *
+slapi_modrdn_internal(
+ char *olddn,
+ char *lnewrdn,
+ int deloldrdn,
+ LDAPControl **controls,
+ int log_change )
+{
+ Slapi_PBlock *pb;
+
+ pb = slapi_pblock_new ();
+
+ slapi_rename_internal_set_pb( pb, olddn, lnewrdn, NULL,
+ deloldrdn, controls, NULL, NULL, 0 );
+ slapi_pblock_set( pb, SLAPI_LOG_OPERATION, (void *)&log_change );
+ slapi_modrdn_internal_pb( pb );
+
+ return pb;
+}
+
+void
+slapi_delete_internal_set_pb( Slapi_PBlock *pb,
+ const char *dn,
+ LDAPControl **controls,
+ const char *uniqueid,
+ Slapi_ComponentId *plugin_identity,
+ int operation_flags )
+{
+ slapi_int_connection_init_pb( pb, LDAP_REQ_DELETE );
+ slapi_pblock_set( pb, SLAPI_TARGET_DN, (void *)dn );
+ slapi_pblock_set( pb, SLAPI_REQCONTROLS, (void *)controls );
+ slapi_pblock_set( pb, SLAPI_TARGET_UNIQUEID, (void *)uniqueid );
+ slapi_pblock_set( pb, SLAPI_PLUGIN_IDENTITY, (void *)plugin_identity );
+ slapi_pblock_set( pb, SLAPI_X_INTOP_FLAGS, (void *)&operation_flags );
+ slapi_int_set_operation_dn( pb );
+}
+
+/* Function : slapi_delete_internal
+ *
+ * Description : Plugin functions call this routine to delete an entry
+ * in the backend directly
+ * Return values : LDAP_SUCCESS
+ * LDAP_PARAM_ERROR
+ * LDAP_NO_MEMORY
+ * LDAP_OTHER
+ * LDAP_UNWILLING_TO_PERFORM
+*/
+Slapi_PBlock *
+slapi_delete_internal(
+ char *ldn,
+ LDAPControl **controls,
+ int log_change )
+{
+ Slapi_PBlock *pb;
+
+ pb = slapi_pblock_new();
+
+ slapi_delete_internal_set_pb( pb, ldn, controls, NULL, NULL, 0 );
+ slapi_pblock_set( pb, SLAPI_LOG_OPERATION, (void *)&log_change );
+ slapi_delete_internal_pb( pb );
+
+ return pb;
+}
+
+#endif /* LDAP_SLAPI */
diff --git a/servers/slapd/slapi/slapi_overlay.c b/servers/slapd/slapi/slapi_overlay.c
new file mode 100644
index 0000000..b01793b
--- /dev/null
+++ b/servers/slapd/slapi/slapi_overlay.c
@@ -0,0 +1,952 @@
+/* slapi_overlay.c - SLAPI overlay */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2001-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Luke Howard for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+#include "slapi.h"
+#include "slap-config.h"
+
+#ifdef LDAP_SLAPI
+
+static slap_overinst slapi;
+static int slapi_over_initialized = 0;
+
+static int slapi_over_response( Operation *op, SlapReply *rs );
+static int slapi_over_cleanup( Operation *op, SlapReply *rs );
+
+static Slapi_PBlock *
+slapi_over_pblock_new( Operation *op, SlapReply *rs )
+{
+ Slapi_PBlock *pb;
+
+ pb = slapi_pblock_new();
+ pb->pb_op = op;
+ pb->pb_conn = op->o_conn;
+ pb->pb_rs = rs;
+ pb->pb_intop = 0;
+
+ PBLOCK_ASSERT_OP( pb, op->o_tag );
+
+ return pb;
+}
+
+static int
+slapi_op_internal_p( Operation *op, SlapReply *rs, slap_callback *cb )
+{
+ int internal_op = 0;
+ Slapi_PBlock *pb = NULL;
+ slap_callback *pcb;
+
+ /*
+ * Abstraction violating check for SLAPI internal operations
+ * allows pblock to remain consistent when invoking internal
+ * op plugins
+ */
+ for ( pcb = op->o_callback; pcb != NULL; pcb = pcb->sc_next ) {
+ if ( pcb->sc_response == slapi_int_response ) {
+ pb = (Slapi_PBlock *)pcb->sc_private;
+ PBLOCK_ASSERT_INTOP( pb, 0 );
+ internal_op = 1;
+ break;
+ }
+ }
+
+ if ( cb != NULL ) {
+ if ( pb == NULL ) {
+ pb = slapi_over_pblock_new( op, rs );
+ }
+
+ cb->sc_response = slapi_over_response;
+ cb->sc_cleanup = slapi_over_cleanup;
+ cb->sc_private = pb;
+ cb->sc_writewait = 0;
+ cb->sc_next = op->o_callback;
+ op->o_callback = cb;
+ }
+
+ return internal_op;
+}
+
+static int
+slapi_over_compute_output(
+ computed_attr_context *c,
+ Slapi_Attr *attribute,
+ Slapi_Entry *entry
+)
+{
+ Attribute **a;
+ AttributeDescription *desc;
+ SlapReply *rs;
+
+ if ( c == NULL || attribute == NULL || entry == NULL ) {
+ return 0;
+ }
+
+ rs = (SlapReply *)c->cac_private;
+
+ assert( rs->sr_entry == entry );
+
+ desc = attribute->a_desc;
+
+ if ( rs->sr_attrs == NULL ) {
+ /* All attrs request, skip operational attributes */
+ if ( is_at_operational( desc->ad_type ) ) {
+ return 0;
+ }
+ } else {
+ /* Specific attributes requested */
+ if ( is_at_operational( desc->ad_type ) ) {
+ if ( !SLAP_OPATTRS( rs->sr_attr_flags ) &&
+ !ad_inlist( desc, rs->sr_attrs ) ) {
+ return 0;
+ }
+ } else {
+ if ( !SLAP_USERATTRS( rs->sr_attr_flags ) &&
+ !ad_inlist( desc, rs->sr_attrs ) ) {
+ return 0;
+ }
+ }
+ }
+
+ /* XXX perhaps we should check for existing attributes and merge */
+ for ( a = &rs->sr_operational_attrs; *a != NULL; a = &(*a)->a_next )
+ ;
+
+ *a = slapi_attr_dup( attribute );
+
+ return 0;
+}
+
+static int
+slapi_over_aux_operational( Operation *op, SlapReply *rs )
+{
+ /* Support for computed attribute plugins */
+ computed_attr_context ctx;
+ AttributeName *anp;
+
+ if ( slapi_op_internal_p( op, rs, NULL ) ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ ctx.cac_pb = slapi_over_pblock_new( op, rs );
+ ctx.cac_op = op;
+ ctx.cac_private = rs;
+
+ if ( rs->sr_entry != NULL ) {
+ /*
+ * For each client requested attribute, call the plugins.
+ */
+ if ( rs->sr_attrs != NULL ) {
+ for ( anp = rs->sr_attrs; anp->an_name.bv_val != NULL; anp++ ) {
+ if ( compute_evaluator( &ctx, anp->an_name.bv_val,
+ rs->sr_entry, slapi_over_compute_output ) == 1 ) {
+ break;
+ }
+ }
+ } else {
+ /*
+ * Technically we shouldn't be returning operational attributes
+ * when the user requested only user attributes. We'll let the
+ * plugin decide whether to be naughty or not.
+ */
+ compute_evaluator( &ctx, "*", rs->sr_entry, slapi_over_compute_output );
+ }
+ }
+
+ slapi_pblock_destroy( ctx.cac_pb );
+
+ return SLAP_CB_CONTINUE;
+}
+
+/*
+ * We need this function to call frontendDB (global) plugins before
+ * database plugins, if we are invoked by a slap_callback.
+ */
+static int
+slapi_over_call_plugins( Slapi_PBlock *pb, int type )
+{
+ int rc = 1; /* means no plugins called */
+ Operation *op;
+
+ PBLOCK_ASSERT_OP( pb, 0 );
+ op = pb->pb_op;
+
+ if ( !be_match( op->o_bd, frontendDB ) ) {
+ rc = slapi_int_call_plugins( frontendDB, type, pb );
+ }
+ if ( rc >= 0 ) {
+ rc = slapi_int_call_plugins( op->o_bd, type, pb );
+ }
+
+ return rc;
+}
+
+static int
+slapi_over_search( Operation *op, SlapReply *rs, int type )
+{
+ int rc;
+ Slapi_PBlock *pb;
+
+ assert( rs->sr_type == REP_SEARCH || rs->sr_type == REP_SEARCHREF );
+
+ /* create a new pblock to not trample on result controls */
+ pb = slapi_over_pblock_new( op, rs );
+
+ rc = slapi_over_call_plugins( pb, type );
+ if ( rc >= 0 ) /* 1 means no plugins called */
+ rc = SLAP_CB_CONTINUE;
+ else
+ rc = LDAP_SUCCESS; /* confusing: don't abort, but don't send */
+
+ slapi_pblock_destroy(pb);
+
+ return rc;
+}
+
+/*
+ * Call pre- and post-result plugins
+ */
+static int
+slapi_over_result( Operation *op, SlapReply *rs, int type )
+{
+ Slapi_PBlock *pb = SLAPI_OPERATION_PBLOCK( op );
+
+ assert( rs->sr_type == REP_RESULT || rs->sr_type == REP_SASL || rs->sr_type == REP_EXTENDED );
+
+ slapi_over_call_plugins( pb, type );
+
+ return SLAP_CB_CONTINUE;
+}
+
+
+static int
+slapi_op_bind_callback( Operation *op, SlapReply *rs, int prc )
+{
+ switch ( prc ) {
+ case SLAPI_BIND_SUCCESS:
+ /* Continue with backend processing */
+ break;
+ case SLAPI_BIND_FAIL:
+ /* Failure, frontend (that's us) sends result */
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+ break;
+ case SLAPI_BIND_ANONYMOUS: /* undocumented */
+ default: /* plugin sent result or no plugins called */
+ BER_BVZERO( &op->orb_edn );
+
+ if ( rs->sr_err == LDAP_SUCCESS ) {
+ /*
+ * Plugin will have called slapi_pblock_set(LDAP_CONN_DN) which
+ * will have set conn->c_dn and conn->c_ndn
+ */
+ if ( BER_BVISNULL( &op->o_conn->c_ndn ) && prc == 1 ) {
+ /* No plugins were called; continue processing */
+ return LDAP_SUCCESS;
+ }
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+ if ( !BER_BVISEMPTY( &op->o_conn->c_ndn ) ) {
+ ber_len_t max = sockbuf_max_incoming_auth;
+ ber_sockbuf_ctrl( op->o_conn->c_sb,
+ LBER_SB_OPT_SET_MAX_INCOMING, &max );
+ }
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+
+ /* log authorization identity */
+ Debug( LDAP_DEBUG_STATS,
+ "%s BIND dn=\"%s\" mech=%s (SLAPI) ssf=0\n",
+ op->o_log_prefix,
+ BER_BVISNULL( &op->o_conn->c_dn )
+ ? "<empty>" : op->o_conn->c_dn.bv_val,
+ BER_BVISNULL( &op->orb_mech )
+ ? "<empty>" : op->orb_mech.bv_val );
+
+ return -1;
+ }
+ break;
+ }
+
+ return rs->sr_err;
+}
+
+static int
+slapi_op_search_callback( Operation *op, SlapReply *rs, int prc )
+{
+ Slapi_PBlock *pb = SLAPI_OPERATION_PBLOCK( op );
+ Filter *f = op->ors_filter;
+
+ /* check preoperation result code */
+ if ( prc < 0 ) {
+ return rs->sr_err;
+ }
+
+ rs->sr_err = LDAP_SUCCESS;
+
+ if ( pb->pb_intop == 0 &&
+ slapi_int_call_plugins( op->o_bd, SLAPI_PLUGIN_COMPUTE_SEARCH_REWRITER_FN, pb ) == 0 ) {
+ /*
+ * The plugin can set the SLAPI_SEARCH_FILTER.
+ * SLAPI_SEARCH_STRFILER is not normative.
+ */
+ if (f != op->ors_filter) {
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+struct slapi_op_info {
+ int soi_preop; /* preoperation plugin parameter */
+ int soi_postop; /* postoperation plugin parameter */
+ int soi_internal_preop; /* internal preoperation plugin parameter */
+ int soi_internal_postop; /* internal postoperation plugin parameter */
+ int (*soi_callback)(Operation *, SlapReply *, int); /* preoperation result handler */
+} slapi_op_dispatch_table[] = {
+ {
+ SLAPI_PLUGIN_PRE_BIND_FN,
+ SLAPI_PLUGIN_POST_BIND_FN,
+ SLAPI_PLUGIN_INTERNAL_PRE_BIND_FN,
+ SLAPI_PLUGIN_INTERNAL_POST_BIND_FN,
+ slapi_op_bind_callback
+ },
+ {
+ SLAPI_PLUGIN_PRE_UNBIND_FN,
+ SLAPI_PLUGIN_POST_UNBIND_FN,
+ SLAPI_PLUGIN_INTERNAL_PRE_UNBIND_FN,
+ SLAPI_PLUGIN_INTERNAL_POST_UNBIND_FN,
+ NULL
+ },
+ {
+ SLAPI_PLUGIN_PRE_SEARCH_FN,
+ SLAPI_PLUGIN_POST_SEARCH_FN,
+ SLAPI_PLUGIN_INTERNAL_PRE_SEARCH_FN,
+ SLAPI_PLUGIN_INTERNAL_POST_SEARCH_FN,
+ slapi_op_search_callback
+ },
+ {
+ SLAPI_PLUGIN_PRE_COMPARE_FN,
+ SLAPI_PLUGIN_POST_COMPARE_FN,
+ SLAPI_PLUGIN_INTERNAL_PRE_COMPARE_FN,
+ SLAPI_PLUGIN_INTERNAL_POST_COMPARE_FN,
+ NULL
+ },
+ {
+ SLAPI_PLUGIN_PRE_MODIFY_FN,
+ SLAPI_PLUGIN_POST_MODIFY_FN,
+ SLAPI_PLUGIN_INTERNAL_PRE_MODIFY_FN,
+ SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN,
+ NULL
+ },
+ {
+ SLAPI_PLUGIN_PRE_MODRDN_FN,
+ SLAPI_PLUGIN_POST_MODRDN_FN,
+ SLAPI_PLUGIN_INTERNAL_PRE_MODRDN_FN,
+ SLAPI_PLUGIN_INTERNAL_POST_MODRDN_FN,
+ NULL
+ },
+ {
+ SLAPI_PLUGIN_PRE_ADD_FN,
+ SLAPI_PLUGIN_POST_ADD_FN,
+ SLAPI_PLUGIN_INTERNAL_PRE_ADD_FN,
+ SLAPI_PLUGIN_INTERNAL_POST_ADD_FN,
+ NULL
+ },
+ {
+ SLAPI_PLUGIN_PRE_DELETE_FN,
+ SLAPI_PLUGIN_POST_DELETE_FN,
+ SLAPI_PLUGIN_INTERNAL_PRE_DELETE_FN,
+ SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN,
+ NULL
+ },
+ {
+ SLAPI_PLUGIN_PRE_ABANDON_FN,
+ SLAPI_PLUGIN_POST_ABANDON_FN,
+ SLAPI_PLUGIN_INTERNAL_PRE_ABANDON_FN,
+ SLAPI_PLUGIN_INTERNAL_POST_ABANDON_FN,
+ NULL
+ },
+ {
+ 0,
+ 0,
+ 0,
+ 0,
+ NULL
+ }
+};
+
+slap_operation_t
+slapi_tag2op( ber_tag_t tag )
+{
+ slap_operation_t op;
+
+ switch ( tag ) {
+ case LDAP_REQ_BIND:
+ op = op_bind;
+ break;
+ case LDAP_REQ_ADD:
+ op = op_add;
+ break;
+ case LDAP_REQ_DELETE:
+ op = op_delete;
+ break;
+ case LDAP_REQ_MODRDN:
+ op = op_modrdn;
+ break;
+ case LDAP_REQ_MODIFY:
+ op = op_modify;
+ break;
+ case LDAP_REQ_COMPARE:
+ op = op_compare;
+ break;
+ case LDAP_REQ_SEARCH:
+ op = op_search;
+ break;
+ case LDAP_REQ_UNBIND:
+ op = op_unbind;
+ break;
+ default:
+ op = op_last;
+ break;
+ }
+
+ return op;
+}
+
+/* Add SLAPI_RESCONTROLS to rs->sr_ctrls, with care, because
+ * rs->sr_ctrls could be allocated on the stack */
+static int
+slapi_over_merge_controls( Operation *op, SlapReply *rs )
+{
+ Slapi_PBlock *pb = SLAPI_OPERATION_PBLOCK( op );
+ LDAPControl **ctrls = NULL;
+ LDAPControl **slapi_ctrls = NULL;
+ size_t n_slapi_ctrls = 0;
+ size_t n_rs_ctrls = 0;
+ size_t i;
+
+ slapi_pblock_get( pb, SLAPI_RESCONTROLS, (void **)&slapi_ctrls );
+
+ n_slapi_ctrls = slapi_int_count_controls( slapi_ctrls );
+ n_rs_ctrls = slapi_int_count_controls( rs->sr_ctrls );
+
+ if ( n_slapi_ctrls == 0 )
+ return LDAP_SUCCESS; /* no SLAPI controls */
+
+ slapi_pblock_set( pb, SLAPI_X_OLD_RESCONTROLS, (void *)rs->sr_ctrls );
+
+ ctrls = (LDAPControl **) op->o_tmpalloc(
+ ( n_slapi_ctrls + n_rs_ctrls + 1 ) * sizeof(LDAPControl *),
+ op->o_tmpmemctx );
+
+ for ( i = 0; i < n_slapi_ctrls; i++ ) {
+ ctrls[i] = slapi_ctrls[i];
+ }
+ if ( rs->sr_ctrls != NULL ) {
+ for ( i = 0; i < n_rs_ctrls; i++ ) {
+ ctrls[n_slapi_ctrls + i] = rs->sr_ctrls[i];
+ }
+ }
+ ctrls[n_slapi_ctrls + n_rs_ctrls] = NULL;
+
+ rs->sr_ctrls = ctrls;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+slapi_over_unmerge_controls( Operation *op, SlapReply *rs )
+{
+ Slapi_PBlock *pb = SLAPI_OPERATION_PBLOCK( op );
+ LDAPControl **rs_ctrls = NULL;
+
+ slapi_pblock_get( pb, SLAPI_X_OLD_RESCONTROLS, (void **)&rs_ctrls );
+
+ if ( rs_ctrls == NULL || rs->sr_ctrls == rs_ctrls ) {
+ /* no copying done */
+ return LDAP_SUCCESS;
+ }
+
+ op->o_tmpfree( rs->sr_ctrls, op->o_tmpmemctx );
+ rs->sr_ctrls = rs_ctrls;
+
+ return LDAP_SUCCESS;
+}
+
+static int
+slapi_over_response( Operation *op, SlapReply *rs )
+{
+ Slapi_PBlock *pb = SLAPI_OPERATION_PBLOCK( op );
+ int rc = SLAP_CB_CONTINUE;
+
+ if ( pb->pb_intop == 0 ) {
+ switch ( rs->sr_type ) {
+ case REP_RESULT:
+ case REP_SASL:
+ case REP_EXTENDED:
+ rc = slapi_over_result( op, rs, SLAPI_PLUGIN_PRE_RESULT_FN );
+ break;
+ case REP_SEARCH:
+ rc = slapi_over_search( op, rs, SLAPI_PLUGIN_PRE_ENTRY_FN );
+ break;
+ case REP_SEARCHREF:
+ rc = slapi_over_search( op, rs, SLAPI_PLUGIN_PRE_REFERRAL_FN );
+ break;
+ default:
+ break;
+ }
+ }
+
+ slapi_over_merge_controls( op, rs );
+
+ return rc;
+}
+
+static int
+slapi_over_cleanup( Operation *op, SlapReply *rs )
+{
+ Slapi_PBlock *pb = SLAPI_OPERATION_PBLOCK( op );
+ int rc = SLAP_CB_CONTINUE;
+
+ slapi_over_unmerge_controls( op, rs );
+
+ if ( pb->pb_intop == 0 ) {
+ switch ( rs->sr_type ) {
+ case REP_RESULT:
+ case REP_SASL:
+ case REP_EXTENDED:
+ rc = slapi_over_result( op, rs, SLAPI_PLUGIN_POST_RESULT_FN );
+ break;
+ case REP_SEARCH:
+ rc = slapi_over_search( op, rs, SLAPI_PLUGIN_POST_ENTRY_FN );
+ break;
+ case REP_SEARCHREF:
+ rc = slapi_over_search( op, rs, SLAPI_PLUGIN_POST_REFERRAL_FN );
+ break;
+ default:
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static int
+slapi_op_func( Operation *op, SlapReply *rs )
+{
+ Slapi_PBlock *pb;
+ slap_operation_t which;
+ struct slapi_op_info *opinfo;
+ int rc;
+ slap_overinfo *oi;
+ slap_overinst *on;
+ slap_callback cb;
+ int internal_op;
+ int preop_type, postop_type;
+ BackendDB *be;
+
+ if ( !slapi_plugins_used )
+ return SLAP_CB_CONTINUE;
+
+ /*
+ * Find the SLAPI operation information for this LDAP
+ * operation; this will contain the preop and postop
+ * plugin types, as well as optional callbacks for
+ * setting up the SLAPI environment.
+ */
+ which = slapi_tag2op( op->o_tag );
+ if ( which >= op_last ) {
+ /* invalid operation, but let someone else deal with it */
+ return SLAP_CB_CONTINUE;
+ }
+
+ opinfo = &slapi_op_dispatch_table[which];
+ if ( opinfo == NULL ) {
+ /* no SLAPI plugin types for this operation */
+ return SLAP_CB_CONTINUE;
+ }
+
+ internal_op = slapi_op_internal_p( op, rs, &cb );
+
+ if ( internal_op ) {
+ preop_type = opinfo->soi_internal_preop;
+ postop_type = opinfo->soi_internal_postop;
+ } else {
+ preop_type = opinfo->soi_preop;
+ postop_type = opinfo->soi_postop;
+ }
+
+ if ( preop_type == 0 ) {
+ /* no SLAPI plugin types for this operation */
+ pb = NULL;
+ rc = SLAP_CB_CONTINUE;
+ goto cleanup;
+ }
+
+ pb = SLAPI_OPERATION_PBLOCK( op );
+
+ /* cache backend so we call correct postop plugins */
+ be = pb->pb_op->o_bd;
+
+ rc = slapi_int_call_plugins( be, preop_type, pb );
+
+ /*
+ * soi_callback is responsible for examining the result code
+ * of the preoperation plugin and determining whether to
+ * abort. This is needed because of special SLAPI behaviour
+ e with bind preoperation plugins.
+ *
+ * The soi_callback function is also used to reset any values
+ * returned from the preoperation plugin before calling the
+ * backend (for the success case).
+ */
+ if ( opinfo->soi_callback == NULL ) {
+ /* default behaviour is preop plugin can abort operation */
+ if ( rc < 0 ) {
+ rc = rs->sr_err;
+ goto cleanup;
+ }
+ } else {
+ rc = (opinfo->soi_callback)( op, rs, rc );
+ if ( rc )
+ goto cleanup;
+ }
+
+ /*
+ * Call actual backend (or next overlay in stack). We need to
+ * do this rather than returning SLAP_CB_CONTINUE and calling
+ * postoperation plugins in a response handler to match the
+ * behaviour of SLAPI in OpenLDAP 2.2, where postoperation
+ * plugins are called after the backend has completely
+ * finished processing the operation.
+ */
+ on = (slap_overinst *)op->o_bd->bd_info;
+ oi = on->on_info;
+
+ rc = overlay_op_walk( op, rs, which, oi, on->on_next );
+
+ /*
+ * Call postoperation plugins
+ */
+ slapi_int_call_plugins( be, postop_type, pb );
+
+cleanup:
+ if ( !internal_op ) {
+ slapi_pblock_destroy(pb);
+ cb.sc_private = NULL;
+ }
+
+ op->o_callback = cb.sc_next;
+
+ return rc;
+}
+
+static int
+slapi_over_extended( Operation *op, SlapReply *rs )
+{
+ Slapi_PBlock *pb;
+ SLAPI_FUNC callback;
+ int rc;
+ int internal_op;
+ slap_callback cb;
+
+ slapi_int_get_extop_plugin( &op->ore_reqoid, &callback );
+ if ( callback == NULL ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ internal_op = slapi_op_internal_p( op, rs, &cb );
+ if ( internal_op ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ pb = SLAPI_OPERATION_PBLOCK( op );
+
+ rc = (*callback)( pb );
+ if ( rc == SLAPI_PLUGIN_EXTENDED_SENT_RESULT ) {
+ goto cleanup;
+ } else if ( rc == SLAPI_PLUGIN_EXTENDED_NOT_HANDLED ) {
+ rc = SLAP_CB_CONTINUE;
+ goto cleanup;
+ }
+
+ assert( rs->sr_rspoid != NULL );
+
+ send_ldap_extended( op, rs );
+
+#if 0
+ slapi_ch_free_string( (char **)&rs->sr_rspoid );
+#endif
+
+ if ( rs->sr_rspdata != NULL )
+ ber_bvfree( rs->sr_rspdata );
+
+ rc = rs->sr_err;
+
+cleanup:
+ slapi_pblock_destroy( pb );
+ op->o_callback = cb.sc_next;
+
+ return rc;
+}
+
+static int
+slapi_over_access_allowed(
+ Operation *op,
+ Entry *e,
+ AttributeDescription *desc,
+ struct berval *val,
+ slap_access_t access,
+ AccessControlState *state,
+ slap_mask_t *maskp )
+{
+ int rc;
+ Slapi_PBlock *pb;
+ slap_callback cb;
+ int internal_op;
+ SlapReply rs = { REP_RESULT };
+
+ internal_op = slapi_op_internal_p( op, &rs, &cb );
+
+ cb.sc_response = NULL;
+ cb.sc_cleanup = NULL;
+ cb.sc_writewait = NULL;
+
+ pb = SLAPI_OPERATION_PBLOCK( op );
+
+ rc = slapi_int_access_allowed( op, e, desc, val, access, state );
+ if ( rc ) {
+ rc = SLAP_CB_CONTINUE;
+ }
+
+ if ( !internal_op ) {
+ slapi_pblock_destroy( pb );
+ }
+
+ op->o_callback = cb.sc_next;
+
+ return rc;
+}
+
+static int
+slapi_over_acl_group(
+ Operation *op,
+ Entry *target,
+ struct berval *gr_ndn,
+ struct berval *op_ndn,
+ ObjectClass *group_oc,
+ AttributeDescription *group_at )
+{
+ Slapi_Entry *e;
+ int rc;
+ Slapi_PBlock *pb;
+ BackendDB *be = op->o_bd;
+ GroupAssertion *g;
+ SlapReply rs = { REP_RESULT };
+
+ op->o_bd = select_backend( gr_ndn, 0 );
+
+ for ( g = op->o_groups; g; g = g->ga_next ) {
+ if ( g->ga_be != op->o_bd || g->ga_oc != group_oc ||
+ g->ga_at != group_at || g->ga_len != gr_ndn->bv_len )
+ {
+ continue;
+ }
+ if ( strcmp( g->ga_ndn, gr_ndn->bv_val ) == 0 ) {
+ break;
+ }
+ }
+ if ( g != NULL ) {
+ rc = g->ga_res;
+ goto done;
+ }
+
+ if ( target != NULL && dn_match( &target->e_nname, gr_ndn ) ) {
+ e = target;
+ rc = 0;
+ } else {
+ rc = be_entry_get_rw( op, gr_ndn, group_oc, group_at, 0, &e );
+ }
+ if ( e != NULL ) {
+ int internal_op;
+ slap_callback cb;
+
+ internal_op = slapi_op_internal_p( op, &rs, &cb );
+
+ cb.sc_response = NULL;
+ cb.sc_cleanup = NULL;
+ cb.sc_writewait = NULL;
+
+ pb = SLAPI_OPERATION_PBLOCK( op );
+
+ slapi_pblock_set( pb, SLAPI_X_GROUP_ENTRY, (void *)e );
+ slapi_pblock_set( pb, SLAPI_X_GROUP_OPERATION_DN, (void *)op_ndn->bv_val );
+ slapi_pblock_set( pb, SLAPI_X_GROUP_ATTRIBUTE, (void *)group_at->ad_cname.bv_val );
+ slapi_pblock_set( pb, SLAPI_X_GROUP_TARGET_ENTRY, (void *)target );
+
+ rc = slapi_over_call_plugins( pb, SLAPI_X_PLUGIN_PRE_GROUP_FN );
+ if ( rc >= 0 ) /* 1 means no plugins called */
+ rc = SLAP_CB_CONTINUE;
+ else
+ rc = pb->pb_rs->sr_err;
+
+ slapi_pblock_delete_param( pb, SLAPI_X_GROUP_ENTRY );
+ slapi_pblock_delete_param( pb, SLAPI_X_GROUP_OPERATION_DN );
+ slapi_pblock_delete_param( pb, SLAPI_X_GROUP_ATTRIBUTE );
+ slapi_pblock_delete_param( pb, SLAPI_X_GROUP_TARGET_ENTRY );
+
+ if ( !internal_op )
+ slapi_pblock_destroy( pb );
+
+ if ( e != target ) {
+ be_entry_release_r( op, e );
+ }
+
+ op->o_callback = cb.sc_next;
+ } else {
+ rc = LDAP_NO_SUCH_OBJECT; /* return SLAP_CB_CONTINUE for correctness? */
+ }
+
+ if ( op->o_tag != LDAP_REQ_BIND && !op->o_do_not_cache &&
+ rc != SLAP_CB_CONTINUE ) {
+ g = op->o_tmpalloc( sizeof( GroupAssertion ) + gr_ndn->bv_len,
+ op->o_tmpmemctx );
+ g->ga_be = op->o_bd;
+ g->ga_oc = group_oc;
+ g->ga_at = group_at;
+ g->ga_res = rc;
+ g->ga_len = gr_ndn->bv_len;
+ strcpy( g->ga_ndn, gr_ndn->bv_val );
+ g->ga_next = op->o_groups;
+ op->o_groups = g;
+ }
+ /*
+ * XXX don't call POST_GROUP_FN, I have no idea what the point of
+ * that plugin function was anyway
+ */
+done:
+ op->o_bd = be;
+ return rc;
+}
+
+static int
+slapi_over_db_open(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ Slapi_PBlock *pb;
+ int rc;
+
+ pb = slapi_pblock_new();
+
+ rc = slapi_int_call_plugins( be, SLAPI_PLUGIN_START_FN, pb );
+
+ slapi_pblock_destroy( pb );
+
+ return rc;
+}
+
+static int
+slapi_over_db_close(
+ BackendDB *be,
+ ConfigReply *cr )
+{
+ Slapi_PBlock *pb;
+ int rc;
+
+ pb = slapi_pblock_new();
+
+ rc = slapi_int_call_plugins( be, SLAPI_PLUGIN_CLOSE_FN, pb );
+
+ slapi_pblock_destroy( pb );
+
+ return rc;
+}
+
+static int
+slapi_over_init()
+{
+ memset( &slapi, 0, sizeof(slapi) );
+
+ slapi.on_bi.bi_type = SLAPI_OVERLAY_NAME;
+
+ slapi.on_bi.bi_op_bind = slapi_op_func;
+ slapi.on_bi.bi_op_unbind = slapi_op_func;
+ slapi.on_bi.bi_op_search = slapi_op_func;
+ slapi.on_bi.bi_op_compare = slapi_op_func;
+ slapi.on_bi.bi_op_modify = slapi_op_func;
+ slapi.on_bi.bi_op_modrdn = slapi_op_func;
+ slapi.on_bi.bi_op_add = slapi_op_func;
+ slapi.on_bi.bi_op_delete = slapi_op_func;
+ slapi.on_bi.bi_op_abandon = slapi_op_func;
+ slapi.on_bi.bi_op_cancel = slapi_op_func;
+
+ slapi.on_bi.bi_db_open = slapi_over_db_open;
+ slapi.on_bi.bi_db_close = slapi_over_db_close;
+
+ slapi.on_bi.bi_extended = slapi_over_extended;
+ slapi.on_bi.bi_access_allowed = slapi_over_access_allowed;
+ slapi.on_bi.bi_operational = slapi_over_aux_operational;
+ slapi.on_bi.bi_acl_group = slapi_over_acl_group;
+
+ return overlay_register( &slapi );
+}
+
+int slapi_over_is_inst( BackendDB *be )
+{
+ return overlay_is_inst( be, SLAPI_OVERLAY_NAME );
+}
+
+int slapi_over_config( BackendDB *be, ConfigReply *cr )
+{
+ if ( slapi_over_initialized == 0 ) {
+ int rc;
+
+ /* do global initialization */
+ ldap_pvt_thread_mutex_init( &slapi_hn_mutex );
+ ldap_pvt_thread_mutex_init( &slapi_time_mutex );
+ ldap_pvt_thread_mutex_init( &slapi_printmessage_mutex );
+
+ if ( slapi_log_file == NULL )
+ slapi_log_file = slapi_ch_strdup( LDAP_RUNDIR LDAP_DIRSEP "errors" );
+
+ rc = slapi_int_init_object_extensions();
+ if ( rc != 0 )
+ return rc;
+
+ rc = slapi_over_init();
+ if ( rc != 0 )
+ return rc;
+
+ slapi_over_initialized = 1;
+ }
+
+ return overlay_config( be, SLAPI_OVERLAY_NAME, -1, NULL, cr );
+}
+
+#endif /* LDAP_SLAPI */
diff --git a/servers/slapd/slapi/slapi_pblock.c b/servers/slapd/slapi/slapi_pblock.c
new file mode 100644
index 0000000..2dc67ef
--- /dev/null
+++ b/servers/slapd/slapi/slapi_pblock.c
@@ -0,0 +1,1426 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1997,2002-2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by IBM Corporation for use in
+ * IBM products and subsequently ported to OpenLDAP Software by
+ * Steve Omrani. Additional significant contributors include:
+ * Luke Howard
+ */
+
+#include "portable.h"
+#include <slap.h>
+#include <slapi.h>
+
+#ifdef LDAP_SLAPI
+
+/* some parameters require a valid connection and operation */
+#define PBLOCK_LOCK_CONN( _pb ) do { \
+ ldap_pvt_thread_mutex_lock( &(_pb)->pb_conn->c_mutex ); \
+ } while (0)
+
+#define PBLOCK_UNLOCK_CONN( _pb ) do { \
+ ldap_pvt_thread_mutex_unlock( &(_pb)->pb_conn->c_mutex ); \
+ } while (0)
+
+/* some parameters are only settable for internal operations */
+#define PBLOCK_VALIDATE_IS_INTOP( _pb ) do { if ( (_pb)->pb_intop == 0 ) break; } while ( 0 )
+
+static slapi_pblock_class_t
+pblock_get_param_class( int param )
+{
+ switch ( param ) {
+ case SLAPI_PLUGIN_TYPE:
+ case SLAPI_PLUGIN_ARGC:
+ case SLAPI_PLUGIN_OPRETURN:
+ case SLAPI_PLUGIN_INTOP_RESULT:
+ case SLAPI_CONFIG_LINENO:
+ case SLAPI_CONFIG_ARGC:
+ case SLAPI_BIND_METHOD:
+ case SLAPI_MODRDN_DELOLDRDN:
+ case SLAPI_SEARCH_SCOPE:
+ case SLAPI_SEARCH_DEREF:
+ case SLAPI_SEARCH_SIZELIMIT:
+ case SLAPI_SEARCH_TIMELIMIT:
+ case SLAPI_SEARCH_ATTRSONLY:
+ case SLAPI_NENTRIES:
+ case SLAPI_CHANGENUMBER:
+ case SLAPI_DBSIZE:
+ case SLAPI_REQUESTOR_ISROOT:
+ case SLAPI_BE_READONLY:
+ case SLAPI_BE_LASTMOD:
+ case SLAPI_DB2LDIF_PRINTKEY:
+ case SLAPI_LDIF2DB_REMOVEDUPVALS:
+ case SLAPI_MANAGEDSAIT:
+ case SLAPI_X_RELAX:
+ case SLAPI_X_OPERATION_NO_SCHEMA_CHECK:
+ case SLAPI_IS_REPLICATED_OPERATION:
+ case SLAPI_X_CONN_IS_UDP:
+ case SLAPI_X_CONN_SSF:
+ case SLAPI_RESULT_CODE:
+ case SLAPI_LOG_OPERATION:
+ case SLAPI_IS_INTERNAL_OPERATION:
+ return PBLOCK_CLASS_INTEGER;
+ break;
+
+ case SLAPI_CONN_ID:
+ case SLAPI_OPERATION_ID:
+ case SLAPI_OPINITIATED_TIME:
+ case SLAPI_ABANDON_MSGID:
+ case SLAPI_X_OPERATION_DELETE_GLUE_PARENT:
+ case SLAPI_OPERATION_MSGID:
+ return PBLOCK_CLASS_LONG_INTEGER;
+ break;
+
+ case SLAPI_PLUGIN_DESTROY_FN:
+ case SLAPI_PLUGIN_DB_BIND_FN:
+ case SLAPI_PLUGIN_DB_UNBIND_FN:
+ case SLAPI_PLUGIN_DB_SEARCH_FN:
+ case SLAPI_PLUGIN_DB_COMPARE_FN:
+ case SLAPI_PLUGIN_DB_MODIFY_FN:
+ case SLAPI_PLUGIN_DB_MODRDN_FN:
+ case SLAPI_PLUGIN_DB_ADD_FN:
+ case SLAPI_PLUGIN_DB_DELETE_FN:
+ case SLAPI_PLUGIN_DB_ABANDON_FN:
+ case SLAPI_PLUGIN_DB_CONFIG_FN:
+ case SLAPI_PLUGIN_CLOSE_FN:
+ case SLAPI_PLUGIN_DB_FLUSH_FN:
+ case SLAPI_PLUGIN_START_FN:
+ case SLAPI_PLUGIN_DB_SEQ_FN:
+ case SLAPI_PLUGIN_DB_ENTRY_FN:
+ case SLAPI_PLUGIN_DB_REFERRAL_FN:
+ case SLAPI_PLUGIN_DB_RESULT_FN:
+ case SLAPI_PLUGIN_DB_LDIF2DB_FN:
+ case SLAPI_PLUGIN_DB_DB2LDIF_FN:
+ case SLAPI_PLUGIN_DB_BEGIN_FN:
+ case SLAPI_PLUGIN_DB_COMMIT_FN:
+ case SLAPI_PLUGIN_DB_ABORT_FN:
+ case SLAPI_PLUGIN_DB_ARCHIVE2DB_FN:
+ case SLAPI_PLUGIN_DB_DB2ARCHIVE_FN:
+ case SLAPI_PLUGIN_DB_NEXT_SEARCH_ENTRY_FN:
+ case SLAPI_PLUGIN_DB_FREE_RESULT_SET_FN:
+ case SLAPI_PLUGIN_DB_SIZE_FN:
+ case SLAPI_PLUGIN_DB_TEST_FN:
+ case SLAPI_PLUGIN_DB_NO_ACL:
+ case SLAPI_PLUGIN_EXT_OP_FN:
+ case SLAPI_PLUGIN_EXT_OP_OIDLIST:
+ case SLAPI_PLUGIN_PRE_BIND_FN:
+ case SLAPI_PLUGIN_PRE_UNBIND_FN:
+ case SLAPI_PLUGIN_PRE_SEARCH_FN:
+ case SLAPI_PLUGIN_PRE_COMPARE_FN:
+ case SLAPI_PLUGIN_PRE_MODIFY_FN:
+ case SLAPI_PLUGIN_PRE_MODRDN_FN:
+ case SLAPI_PLUGIN_PRE_ADD_FN:
+ case SLAPI_PLUGIN_PRE_DELETE_FN:
+ case SLAPI_PLUGIN_PRE_ABANDON_FN:
+ case SLAPI_PLUGIN_PRE_ENTRY_FN:
+ case SLAPI_PLUGIN_PRE_REFERRAL_FN:
+ case SLAPI_PLUGIN_PRE_RESULT_FN:
+ case SLAPI_PLUGIN_INTERNAL_PRE_ADD_FN:
+ case SLAPI_PLUGIN_INTERNAL_PRE_MODIFY_FN:
+ case SLAPI_PLUGIN_INTERNAL_PRE_MODRDN_FN:
+ case SLAPI_PLUGIN_INTERNAL_PRE_DELETE_FN:
+ case SLAPI_PLUGIN_BE_PRE_ADD_FN:
+ case SLAPI_PLUGIN_BE_PRE_MODIFY_FN:
+ case SLAPI_PLUGIN_BE_PRE_MODRDN_FN:
+ case SLAPI_PLUGIN_BE_PRE_DELETE_FN:
+ case SLAPI_PLUGIN_POST_BIND_FN:
+ case SLAPI_PLUGIN_POST_UNBIND_FN:
+ case SLAPI_PLUGIN_POST_SEARCH_FN:
+ case SLAPI_PLUGIN_POST_COMPARE_FN:
+ case SLAPI_PLUGIN_POST_MODIFY_FN:
+ case SLAPI_PLUGIN_POST_MODRDN_FN:
+ case SLAPI_PLUGIN_POST_ADD_FN:
+ case SLAPI_PLUGIN_POST_DELETE_FN:
+ case SLAPI_PLUGIN_POST_ABANDON_FN:
+ case SLAPI_PLUGIN_POST_ENTRY_FN:
+ case SLAPI_PLUGIN_POST_REFERRAL_FN:
+ case SLAPI_PLUGIN_POST_RESULT_FN:
+ case SLAPI_PLUGIN_INTERNAL_POST_ADD_FN:
+ case SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN:
+ case SLAPI_PLUGIN_INTERNAL_POST_MODRDN_FN:
+ case SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN:
+ case SLAPI_PLUGIN_BE_POST_ADD_FN:
+ case SLAPI_PLUGIN_BE_POST_MODIFY_FN:
+ case SLAPI_PLUGIN_BE_POST_MODRDN_FN:
+ case SLAPI_PLUGIN_BE_POST_DELETE_FN:
+ case SLAPI_PLUGIN_MR_FILTER_CREATE_FN:
+ case SLAPI_PLUGIN_MR_INDEXER_CREATE_FN:
+ case SLAPI_PLUGIN_MR_FILTER_MATCH_FN:
+ case SLAPI_PLUGIN_MR_FILTER_INDEX_FN:
+ case SLAPI_PLUGIN_MR_FILTER_RESET_FN:
+ case SLAPI_PLUGIN_MR_INDEX_FN:
+ case SLAPI_PLUGIN_COMPUTE_EVALUATOR_FN:
+ case SLAPI_PLUGIN_COMPUTE_SEARCH_REWRITER_FN:
+ case SLAPI_PLUGIN_ACL_ALLOW_ACCESS:
+ case SLAPI_X_PLUGIN_PRE_GROUP_FN:
+ case SLAPI_X_PLUGIN_POST_GROUP_FN:
+ case SLAPI_PLUGIN_AUDIT_FN:
+ case SLAPI_PLUGIN_INTERNAL_PRE_BIND_FN:
+ case SLAPI_PLUGIN_INTERNAL_PRE_UNBIND_FN:
+ case SLAPI_PLUGIN_INTERNAL_PRE_SEARCH_FN:
+ case SLAPI_PLUGIN_INTERNAL_PRE_COMPARE_FN:
+ case SLAPI_PLUGIN_INTERNAL_PRE_ABANDON_FN:
+ case SLAPI_PLUGIN_INTERNAL_POST_BIND_FN:
+ case SLAPI_PLUGIN_INTERNAL_POST_UNBIND_FN:
+ case SLAPI_PLUGIN_INTERNAL_POST_SEARCH_FN:
+ case SLAPI_PLUGIN_INTERNAL_POST_COMPARE_FN:
+ case SLAPI_PLUGIN_INTERNAL_POST_ABANDON_FN:
+ return PBLOCK_CLASS_FUNCTION_POINTER;
+ break;
+
+ case SLAPI_BACKEND:
+ case SLAPI_CONNECTION:
+ case SLAPI_OPERATION:
+ case SLAPI_OPERATION_PARAMETERS:
+ case SLAPI_OPERATION_TYPE:
+ case SLAPI_OPERATION_AUTHTYPE:
+ case SLAPI_BE_MONITORDN:
+ case SLAPI_BE_TYPE:
+ case SLAPI_REQUESTOR_DN:
+ case SLAPI_CONN_DN:
+ case SLAPI_CONN_CLIENTIP:
+ case SLAPI_CONN_SERVERIP:
+ case SLAPI_CONN_AUTHTYPE:
+ case SLAPI_CONN_AUTHMETHOD:
+ case SLAPI_CONN_CERT:
+ case SLAPI_X_CONN_CLIENTPATH:
+ case SLAPI_X_CONN_SERVERPATH:
+ case SLAPI_X_CONN_SASL_CONTEXT:
+ case SLAPI_X_CONFIG_ARGV:
+ case SLAPI_X_INTOP_FLAGS:
+ case SLAPI_X_INTOP_RESULT_CALLBACK:
+ case SLAPI_X_INTOP_SEARCH_ENTRY_CALLBACK:
+ case SLAPI_X_INTOP_REFERRAL_ENTRY_CALLBACK:
+ case SLAPI_X_INTOP_CALLBACK_DATA:
+ case SLAPI_PLUGIN_MR_OID:
+ case SLAPI_PLUGIN_MR_TYPE:
+ case SLAPI_PLUGIN_MR_VALUE:
+ case SLAPI_PLUGIN_MR_VALUES:
+ case SLAPI_PLUGIN_MR_KEYS:
+ case SLAPI_PLUGIN:
+ case SLAPI_PLUGIN_PRIVATE:
+ case SLAPI_PLUGIN_ARGV:
+ case SLAPI_PLUGIN_OBJECT:
+ case SLAPI_PLUGIN_DESCRIPTION:
+ case SLAPI_PLUGIN_IDENTITY:
+ case SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES:
+ case SLAPI_PLUGIN_INTOP_SEARCH_REFERRALS:
+ case SLAPI_PLUGIN_MR_FILTER_REUSABLE:
+ case SLAPI_PLUGIN_MR_QUERY_OPERATOR:
+ case SLAPI_PLUGIN_MR_USAGE:
+ case SLAPI_OP_LESS:
+ case SLAPI_OP_LESS_OR_EQUAL:
+ case SLAPI_PLUGIN_MR_USAGE_INDEX:
+ case SLAPI_PLUGIN_SYNTAX_FILTER_AVA:
+ case SLAPI_PLUGIN_SYNTAX_FILTER_SUB:
+ case SLAPI_PLUGIN_SYNTAX_VALUES2KEYS:
+ case SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_AVA:
+ case SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_SUB:
+ case SLAPI_PLUGIN_SYNTAX_NAMES:
+ case SLAPI_PLUGIN_SYNTAX_OID:
+ case SLAPI_PLUGIN_SYNTAX_FLAGS:
+ case SLAPI_PLUGIN_SYNTAX_COMPARE:
+ case SLAPI_CONFIG_FILENAME:
+ case SLAPI_CONFIG_ARGV:
+ case SLAPI_TARGET_ADDRESS:
+ case SLAPI_TARGET_UNIQUEID:
+ case SLAPI_TARGET_DN:
+ case SLAPI_REQCONTROLS:
+ case SLAPI_ENTRY_PRE_OP:
+ case SLAPI_ENTRY_POST_OP:
+ case SLAPI_RESCONTROLS:
+ case SLAPI_X_OLD_RESCONTROLS:
+ case SLAPI_ADD_RESCONTROL:
+ case SLAPI_CONTROLS_ARG:
+ case SLAPI_ADD_ENTRY:
+ case SLAPI_ADD_EXISTING_DN_ENTRY:
+ case SLAPI_ADD_PARENT_ENTRY:
+ case SLAPI_ADD_PARENT_UNIQUEID:
+ case SLAPI_ADD_EXISTING_UNIQUEID_ENTRY:
+ case SLAPI_BIND_CREDENTIALS:
+ case SLAPI_BIND_SASLMECHANISM:
+ case SLAPI_BIND_RET_SASLCREDS:
+ case SLAPI_COMPARE_TYPE:
+ case SLAPI_COMPARE_VALUE:
+ case SLAPI_MODIFY_MODS:
+ case SLAPI_MODRDN_NEWRDN:
+ case SLAPI_MODRDN_NEWSUPERIOR:
+ case SLAPI_MODRDN_PARENT_ENTRY:
+ case SLAPI_MODRDN_NEWPARENT_ENTRY:
+ case SLAPI_MODRDN_TARGET_ENTRY:
+ case SLAPI_MODRDN_NEWSUPERIOR_ADDRESS:
+ case SLAPI_SEARCH_FILTER:
+ case SLAPI_SEARCH_STRFILTER:
+ case SLAPI_SEARCH_ATTRS:
+ case SLAPI_SEQ_TYPE:
+ case SLAPI_SEQ_ATTRNAME:
+ case SLAPI_SEQ_VAL:
+ case SLAPI_EXT_OP_REQ_OID:
+ case SLAPI_EXT_OP_REQ_VALUE:
+ case SLAPI_EXT_OP_RET_OID:
+ case SLAPI_EXT_OP_RET_VALUE:
+ case SLAPI_MR_FILTER_ENTRY:
+ case SLAPI_MR_FILTER_TYPE:
+ case SLAPI_MR_FILTER_VALUE:
+ case SLAPI_MR_FILTER_OID:
+ case SLAPI_MR_FILTER_DNATTRS:
+ case SLAPI_LDIF2DB_FILE:
+ case SLAPI_PARENT_TXN:
+ case SLAPI_TXN:
+ case SLAPI_SEARCH_RESULT_SET:
+ case SLAPI_SEARCH_RESULT_ENTRY:
+ case SLAPI_SEARCH_REFERRALS:
+ case SLAPI_RESULT_TEXT:
+ case SLAPI_RESULT_MATCHED:
+ case SLAPI_X_GROUP_ENTRY:
+ case SLAPI_X_GROUP_ATTRIBUTE:
+ case SLAPI_X_GROUP_OPERATION_DN:
+ case SLAPI_X_GROUP_TARGET_ENTRY:
+ case SLAPI_X_ADD_STRUCTURAL_CLASS:
+ case SLAPI_PLUGIN_AUDIT_DATA:
+ case SLAPI_IBM_PBLOCK:
+ case SLAPI_PLUGIN_VERSION:
+ return PBLOCK_CLASS_POINTER;
+ break;
+ default:
+ break;
+ }
+
+ return PBLOCK_CLASS_INVALID;
+}
+
+static void
+pblock_lock( Slapi_PBlock *pb )
+{
+ ldap_pvt_thread_mutex_lock( &pb->pb_mutex );
+}
+
+static void
+pblock_unlock( Slapi_PBlock *pb )
+{
+ ldap_pvt_thread_mutex_unlock( &pb->pb_mutex );
+}
+
+static int
+pblock_get_default( Slapi_PBlock *pb, int param, void **value )
+{
+ int i;
+ slapi_pblock_class_t pbClass;
+
+ pbClass = pblock_get_param_class( param );
+ if ( pbClass == PBLOCK_CLASS_INVALID ) {
+ return PBLOCK_ERROR;
+ }
+
+ switch ( pbClass ) {
+ case PBLOCK_CLASS_INTEGER:
+ *((int *)value) = 0;
+ break;
+ case PBLOCK_CLASS_LONG_INTEGER:
+ *((long *)value) = 0L;
+ break;
+ case PBLOCK_CLASS_POINTER:
+ case PBLOCK_CLASS_FUNCTION_POINTER:
+ *value = NULL;
+ break;
+ case PBLOCK_CLASS_INVALID:
+ return PBLOCK_ERROR;
+ }
+
+ for ( i = 0; i < pb->pb_nParams; i++ ) {
+ if ( pb->pb_params[i] == param ) {
+ switch ( pbClass ) {
+ case PBLOCK_CLASS_INTEGER:
+ *((int *)value) = pb->pb_values[i].pv_integer;
+ break;
+ case PBLOCK_CLASS_LONG_INTEGER:
+ *((long *)value) = pb->pb_values[i].pv_long_integer;
+ break;
+ case PBLOCK_CLASS_POINTER:
+ *value = pb->pb_values[i].pv_pointer;
+ break;
+ case PBLOCK_CLASS_FUNCTION_POINTER:
+ *value = pb->pb_values[i].pv_function_pointer;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+
+ return PBLOCK_SUCCESS;
+}
+
+static char *
+pblock_get_authtype( AuthorizationInformation *authz, int is_tls )
+{
+ char *authType;
+
+ switch ( authz->sai_method ) {
+ case LDAP_AUTH_SASL:
+ authType = SLAPD_AUTH_SASL;
+ break;
+ case LDAP_AUTH_SIMPLE:
+ authType = SLAPD_AUTH_SIMPLE;
+ break;
+ case LDAP_AUTH_NONE:
+ authType = SLAPD_AUTH_NONE;
+ break;
+ default:
+ authType = NULL;
+ break;
+ }
+
+ if ( is_tls && authType == NULL ) {
+ authType = SLAPD_AUTH_SSL;
+ }
+
+ return authType;
+}
+
+static int
+pblock_set_default( Slapi_PBlock *pb, int param, void *value )
+{
+ slapi_pblock_class_t pbClass;
+ int i;
+
+ pbClass = pblock_get_param_class( param );
+ if ( pbClass == PBLOCK_CLASS_INVALID ) {
+ return PBLOCK_ERROR;
+ }
+
+ if ( pb->pb_nParams == PBLOCK_MAX_PARAMS ) {
+ return PBLOCK_ERROR;
+ }
+
+ for ( i = 0; i < pb->pb_nParams; i++ ) {
+ if ( pb->pb_params[i] == param )
+ break;
+ }
+ if ( i >= pb->pb_nParams ) {
+ pb->pb_params[i] = param;
+ pb->pb_nParams++;
+ }
+
+ switch ( pbClass ) {
+ case PBLOCK_CLASS_INTEGER:
+ pb->pb_values[i].pv_integer = (*((int *)value));
+ break;
+ case PBLOCK_CLASS_LONG_INTEGER:
+ pb->pb_values[i].pv_long_integer = (*((long *)value));
+ break;
+ case PBLOCK_CLASS_POINTER:
+ pb->pb_values[i].pv_pointer = value;
+ break;
+ case PBLOCK_CLASS_FUNCTION_POINTER:
+ pb->pb_values[i].pv_function_pointer = value;
+ break;
+ default:
+ break;
+ }
+
+ return PBLOCK_SUCCESS;
+}
+
+static int
+pblock_be_call( Slapi_PBlock *pb, int (*bep)(Operation *) )
+{
+ BackendDB *be_orig;
+ Operation *op;
+ int rc;
+
+ PBLOCK_ASSERT_OP( pb, 0 );
+ op = pb->pb_op;
+
+ be_orig = op->o_bd;
+ op->o_bd = select_backend( &op->o_req_ndn, 0 );
+ rc = (*bep)( op );
+ op->o_bd = be_orig;
+
+ return rc;
+}
+
+static int
+pblock_get( Slapi_PBlock *pb, int param, void **value )
+{
+ int rc = PBLOCK_SUCCESS;
+
+ pblock_lock( pb );
+
+ switch ( param ) {
+ case SLAPI_OPERATION:
+ *value = pb->pb_op;
+ break;
+ case SLAPI_OPINITIATED_TIME:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ *((long *)value) = pb->pb_op->o_time;
+ break;
+ case SLAPI_OPERATION_ID:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ *((long *)value) = pb->pb_op->o_opid;
+ break;
+ case SLAPI_OPERATION_TYPE:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ *((ber_tag_t *)value) = pb->pb_op->o_tag;
+ break;
+ case SLAPI_OPERATION_MSGID:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ *((long *)value) = pb->pb_op->o_msgid;
+ break;
+ case SLAPI_X_OPERATION_DELETE_GLUE_PARENT:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ *((int *)value) = pb->pb_op->o_delete_glue_parent;
+ break;
+ case SLAPI_X_OPERATION_NO_SCHEMA_CHECK:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ *((int *)value) = get_no_schema_check( pb->pb_op );
+ break;
+ case SLAPI_X_ADD_STRUCTURAL_CLASS:
+ PBLOCK_ASSERT_OP( pb, 0 );
+
+ if ( pb->pb_op->o_tag == LDAP_REQ_ADD ) {
+ struct berval tmpval = BER_BVNULL;
+
+ rc = mods_structural_class( pb->pb_op->ora_modlist,
+ &tmpval, &pb->pb_rs->sr_text,
+ pb->pb_textbuf, sizeof( pb->pb_textbuf ),
+ pb->pb_op->o_tmpmemctx );
+ *((char **)value) = tmpval.bv_val;
+ } else {
+ rc = PBLOCK_ERROR;
+ }
+ break;
+ case SLAPI_X_OPERATION_NO_SUBORDINATE_GLUE:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ *((int *)value) = pb->pb_op->o_no_subordinate_glue;
+ break;
+ case SLAPI_REQCONTROLS:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ *((LDAPControl ***)value) = pb->pb_op->o_ctrls;
+ break;
+ case SLAPI_REQUESTOR_DN:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ *((char **)value) = pb->pb_op->o_dn.bv_val;
+ break;
+ case SLAPI_MANAGEDSAIT:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ *((int *)value) = get_manageDSAit( pb->pb_op );
+ break;
+ case SLAPI_X_RELAX:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ *((int *)value) = get_relax( pb->pb_op );
+ break;
+ case SLAPI_BACKEND:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ *((BackendDB **)value) = select_backend( &pb->pb_op->o_req_ndn, 0 );
+ break;
+ case SLAPI_BE_TYPE:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ if ( pb->pb_op->o_bd != NULL )
+ *((char **)value) = pb->pb_op->o_bd->bd_info->bi_type;
+ else
+ *value = NULL;
+ break;
+ case SLAPI_CONNECTION:
+ *value = pb->pb_conn;
+ break;
+ case SLAPI_X_CONN_SSF:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ *((slap_ssf_t *)value) = pb->pb_conn->c_ssf;
+ break;
+ case SLAPI_X_CONN_SASL_CONTEXT:
+ PBLOCK_ASSERT_CONN( pb );
+ if ( pb->pb_conn->c_sasl_authctx != NULL )
+ *value = pb->pb_conn->c_sasl_authctx;
+ else
+ *value = pb->pb_conn->c_sasl_sockctx;
+ break;
+ case SLAPI_TARGET_DN:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ *((char **)value) = pb->pb_op->o_req_dn.bv_val;
+ break;
+ case SLAPI_REQUESTOR_ISROOT:
+ *((int *)value) = pblock_be_call( pb, be_isroot );
+ break;
+ case SLAPI_IS_REPLICATED_OPERATION:
+ *((int *)value) = pblock_be_call( pb, be_slurp_update );
+ break;
+ case SLAPI_CONN_AUTHTYPE:
+ case SLAPI_CONN_AUTHMETHOD: /* XXX should return SASL mech */
+ PBLOCK_ASSERT_CONN( pb );
+ *((char **)value) = pblock_get_authtype( &pb->pb_conn->c_authz,
+#ifdef HAVE_TLS
+ pb->pb_conn->c_is_tls
+#else
+ 0
+#endif
+ );
+ break;
+ case SLAPI_IS_INTERNAL_OPERATION:
+ *((int *)value) = pb->pb_intop;
+ break;
+ case SLAPI_X_CONN_IS_UDP:
+ PBLOCK_ASSERT_CONN( pb );
+#ifdef LDAP_CONNECTIONLESS
+ *((int *)value) = pb->pb_conn->c_is_udp;
+#else
+ *((int *)value) = 0;
+#endif
+ break;
+ case SLAPI_CONN_ID:
+ PBLOCK_ASSERT_CONN( pb );
+ *((long *)value) = pb->pb_conn->c_connid;
+ break;
+ case SLAPI_CONN_DN:
+ PBLOCK_ASSERT_CONN( pb );
+#if 0
+ /* This would be necessary to keep plugin compat after the fix in ITS#4158 */
+ if ( pb->pb_op->o_tag == LDAP_REQ_BIND && pb->pb_rs->sr_err == LDAP_SUCCESS )
+ *((char **)value) = pb->pb_op->orb_edn.bv_val;
+ else
+#endif
+ *((char **)value) = pb->pb_conn->c_dn.bv_val;
+ break;
+ case SLAPI_CONN_CLIENTIP:
+ PBLOCK_ASSERT_CONN( pb );
+ if ( strncmp( pb->pb_conn->c_peer_name.bv_val, "IP=", 3 ) == 0 )
+ *((char **)value) = &pb->pb_conn->c_peer_name.bv_val[3];
+ else
+ *value = NULL;
+ break;
+ case SLAPI_X_CONN_CLIENTPATH:
+ PBLOCK_ASSERT_CONN( pb );
+ if ( strncmp( pb->pb_conn->c_peer_name.bv_val, "PATH=", 3 ) == 0 )
+ *((char **)value) = &pb->pb_conn->c_peer_name.bv_val[5];
+ else
+ *value = NULL;
+ break;
+ case SLAPI_CONN_SERVERIP:
+ PBLOCK_ASSERT_CONN( pb );
+ if ( strncmp( pb->pb_conn->c_sock_name.bv_val, "IP=", 3 ) == 0 )
+ *((char **)value) = &pb->pb_conn->c_sock_name.bv_val[3];
+ else
+ *value = NULL;
+ break;
+ case SLAPI_X_CONN_SERVERPATH:
+ PBLOCK_ASSERT_CONN( pb );
+ if ( strncmp( pb->pb_conn->c_sock_name.bv_val, "PATH=", 3 ) == 0 )
+ *((char **)value) = &pb->pb_conn->c_sock_name.bv_val[5];
+ else
+ *value = NULL;
+ break;
+ case SLAPI_RESULT_CODE:
+ case SLAPI_PLUGIN_INTOP_RESULT:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ *((int *)value) = pb->pb_rs->sr_err;
+ break;
+ case SLAPI_RESULT_TEXT:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ *((const char **)value) = pb->pb_rs->sr_text;
+ break;
+ case SLAPI_RESULT_MATCHED:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ *((const char **)value) = pb->pb_rs->sr_matched;
+ break;
+ case SLAPI_ADD_ENTRY:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ if ( pb->pb_op->o_tag == LDAP_REQ_ADD )
+ *((Slapi_Entry **)value) = pb->pb_op->ora_e;
+ else
+ *value = NULL;
+ break;
+ case SLAPI_MODIFY_MODS: {
+ LDAPMod **mods = NULL;
+ Modifications *ml = NULL;
+
+ pblock_get_default( pb, param, (void **)&mods );
+ if ( mods == NULL && pb->pb_intop == 0 ) {
+ switch ( pb->pb_op->o_tag ) {
+ case LDAP_REQ_MODIFY:
+ ml = pb->pb_op->orm_modlist;
+ break;
+ case LDAP_REQ_MODRDN:
+ ml = pb->pb_op->orr_modlist;
+ break;
+ default:
+ rc = PBLOCK_ERROR;
+ break;
+ }
+ if ( rc != PBLOCK_ERROR ) {
+ mods = slapi_int_modifications2ldapmods( ml );
+ pblock_set_default( pb, param, (void *)mods );
+ }
+ }
+ *((LDAPMod ***)value) = mods;
+ break;
+ }
+ case SLAPI_MODRDN_NEWRDN:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ if ( pb->pb_op->o_tag == LDAP_REQ_MODRDN )
+ *((char **)value) = pb->pb_op->orr_newrdn.bv_val;
+ else
+ *value = NULL;
+ break;
+ case SLAPI_MODRDN_NEWSUPERIOR:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ if ( pb->pb_op->o_tag == LDAP_REQ_MODRDN && pb->pb_op->orr_newSup != NULL )
+ *((char **)value) = pb->pb_op->orr_newSup->bv_val;
+ else
+ *value = NULL;
+ break;
+ case SLAPI_MODRDN_DELOLDRDN:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ if ( pb->pb_op->o_tag == LDAP_REQ_MODRDN )
+ *((int *)value) = pb->pb_op->orr_deleteoldrdn;
+ else
+ *((int *)value) = 0;
+ break;
+ case SLAPI_SEARCH_SCOPE:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ if ( pb->pb_op->o_tag == LDAP_REQ_SEARCH )
+ *((int *)value) = pb->pb_op->ors_scope;
+ else
+ *((int *)value) = 0;
+ break;
+ case SLAPI_SEARCH_DEREF:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ if ( pb->pb_op->o_tag == LDAP_REQ_SEARCH )
+ *((int *)value) = pb->pb_op->ors_deref;
+ else
+ *((int *)value) = 0;
+ break;
+ case SLAPI_SEARCH_SIZELIMIT:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ if ( pb->pb_op->o_tag == LDAP_REQ_SEARCH )
+ *((int *)value) = pb->pb_op->ors_slimit;
+ else
+ *((int *)value) = 0;
+ break;
+ case SLAPI_SEARCH_TIMELIMIT:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ if ( pb->pb_op->o_tag == LDAP_REQ_SEARCH )
+ *((int *)value) = pb->pb_op->ors_tlimit;
+ else
+ *((int *)value) = 0;
+ break;
+ case SLAPI_SEARCH_FILTER:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ if ( pb->pb_op->o_tag == LDAP_REQ_SEARCH )
+ *((Slapi_Filter **)value) = pb->pb_op->ors_filter;
+ else
+ *((Slapi_Filter **)value) = NULL;
+ break;
+ case SLAPI_SEARCH_STRFILTER:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ if ( pb->pb_op->o_tag == LDAP_REQ_SEARCH )
+ *((char **)value) = pb->pb_op->ors_filterstr.bv_val;
+ else
+ *((char **)value) = NULL;
+ break;
+ case SLAPI_SEARCH_ATTRS: {
+ char **attrs = NULL;
+
+ PBLOCK_ASSERT_OP( pb, 0 );
+ if ( pb->pb_op->o_tag != LDAP_REQ_SEARCH ) {
+ rc = PBLOCK_ERROR;
+ break;
+ }
+ pblock_get_default( pb, param, (void **)&attrs );
+ if ( attrs == NULL && pb->pb_intop == 0 ) {
+ attrs = anlist2charray_x( pb->pb_op->ors_attrs, 0, pb->pb_op->o_tmpmemctx );
+ pblock_set_default( pb, param, (void *)attrs );
+ }
+ *((char ***)value) = attrs;
+ break;
+ }
+ case SLAPI_SEARCH_ATTRSONLY:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ if ( pb->pb_op->o_tag == LDAP_REQ_SEARCH )
+ *((int *)value) = pb->pb_op->ors_attrsonly;
+ else
+ *((int *)value) = 0;
+ break;
+ case SLAPI_SEARCH_RESULT_ENTRY:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ *((Slapi_Entry **)value) = pb->pb_rs->sr_entry;
+ break;
+ case SLAPI_BIND_RET_SASLCREDS:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ *((struct berval **)value) = pb->pb_rs->sr_sasldata;
+ break;
+ case SLAPI_EXT_OP_REQ_OID:
+ *((const char **)value) = pb->pb_op->ore_reqoid.bv_val;
+ break;
+ case SLAPI_EXT_OP_REQ_VALUE:
+ *((struct berval **)value) = pb->pb_op->ore_reqdata;
+ break;
+ case SLAPI_EXT_OP_RET_OID:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ *((const char **)value) = pb->pb_rs->sr_rspoid;
+ break;
+ case SLAPI_EXT_OP_RET_VALUE:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ *((struct berval **)value) = pb->pb_rs->sr_rspdata;
+ break;
+ case SLAPI_BIND_METHOD:
+ if ( pb->pb_op->o_tag == LDAP_REQ_BIND )
+ *((int *)value) = pb->pb_op->orb_method;
+ else
+ *((int *)value) = 0;
+ break;
+ case SLAPI_BIND_CREDENTIALS:
+ if ( pb->pb_op->o_tag == LDAP_REQ_BIND )
+ *((struct berval **)value) = &pb->pb_op->orb_cred;
+ else
+ *value = NULL;
+ break;
+ case SLAPI_COMPARE_TYPE:
+ if ( pb->pb_op->o_tag == LDAP_REQ_COMPARE )
+ *((char **)value) = pb->pb_op->orc_ava->aa_desc->ad_cname.bv_val;
+ else
+ *value = NULL;
+ break;
+ case SLAPI_COMPARE_VALUE:
+ if ( pb->pb_op->o_tag == LDAP_REQ_COMPARE )
+ *((struct berval **)value) = &pb->pb_op->orc_ava->aa_value;
+ else
+ *value = NULL;
+ break;
+ case SLAPI_ABANDON_MSGID:
+ if ( pb->pb_op->o_tag == LDAP_REQ_ABANDON )
+ *((int *)value) = pb->pb_op->orn_msgid;
+ else
+ *((int *)value) = 0;
+ break;
+ default:
+ rc = pblock_get_default( pb, param, value );
+ break;
+ }
+
+ pblock_unlock( pb );
+
+ return rc;
+}
+
+static int
+pblock_add_control( Slapi_PBlock *pb, LDAPControl *control )
+{
+ LDAPControl **controls = NULL;
+ size_t i;
+
+ pblock_get_default( pb, SLAPI_RESCONTROLS, (void **)&controls );
+
+ if ( controls != NULL ) {
+ for ( i = 0; controls[i] != NULL; i++ )
+ ;
+ } else {
+ i = 0;
+ }
+
+ controls = (LDAPControl **)slapi_ch_realloc( (char *)controls,
+ ( i + 2 ) * sizeof(LDAPControl *));
+ controls[i++] = slapi_dup_control( control );
+ controls[i] = NULL;
+
+ return pblock_set_default( pb, SLAPI_RESCONTROLS, (void *)controls );
+}
+
+static int
+pblock_set_dn( void *value, struct berval *dn, struct berval *ndn, void *memctx )
+{
+ struct berval bv;
+
+ if ( !BER_BVISNULL( dn )) {
+ slap_sl_free( dn->bv_val, memctx );
+ BER_BVZERO( dn );
+ }
+ if ( !BER_BVISNULL( ndn )) {
+ slap_sl_free( ndn->bv_val, memctx );
+ BER_BVZERO( ndn );
+ }
+
+ bv.bv_val = (char *)value;
+ bv.bv_len = ( value != NULL ) ? strlen( bv.bv_val ) : 0;
+
+ return dnPrettyNormal( NULL, &bv, dn, ndn, memctx );
+}
+
+static int
+pblock_set( Slapi_PBlock *pb, int param, void *value )
+{
+ int rc = PBLOCK_SUCCESS;
+
+ pblock_lock( pb );
+
+ switch ( param ) {
+ case SLAPI_OPERATION:
+ pb->pb_op = (Operation *)value;
+ break;
+ case SLAPI_OPINITIATED_TIME:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ pb->pb_op->o_time = *((long *)value);
+ break;
+ case SLAPI_OPERATION_ID:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ pb->pb_op->o_opid = *((long *)value);
+ break;
+ case SLAPI_OPERATION_TYPE:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ pb->pb_op->o_tag = *((ber_tag_t *)value);
+ break;
+ case SLAPI_OPERATION_MSGID:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ pb->pb_op->o_msgid = *((long *)value);
+ break;
+ case SLAPI_X_OPERATION_DELETE_GLUE_PARENT:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ pb->pb_op->o_delete_glue_parent = *((int *)value);
+ break;
+ case SLAPI_X_OPERATION_NO_SCHEMA_CHECK:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ pb->pb_op->o_no_schema_check = *((int *)value);
+ break;
+ case SLAPI_X_OPERATION_NO_SUBORDINATE_GLUE:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ pb->pb_op->o_no_subordinate_glue = *((int *)value);
+ break;
+ case SLAPI_REQCONTROLS:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ pb->pb_op->o_ctrls = (LDAPControl **)value;
+ break;
+ case SLAPI_RESCONTROLS: {
+ LDAPControl **ctrls = NULL;
+
+ pblock_get_default( pb, param, (void **)&ctrls );
+ if ( ctrls != NULL ) {
+ /* free old ones first */
+ ldap_controls_free( ctrls );
+ }
+ rc = pblock_set_default( pb, param, value );
+ break;
+ }
+ case SLAPI_ADD_RESCONTROL:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ rc = pblock_add_control( pb, (LDAPControl *)value );
+ break;
+ case SLAPI_REQUESTOR_DN:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ rc = pblock_set_dn( value, &pb->pb_op->o_dn, &pb->pb_op->o_ndn, pb->pb_op->o_tmpmemctx );
+ break;
+ case SLAPI_MANAGEDSAIT:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ pb->pb_op->o_managedsait = *((int *)value);
+ break;
+ case SLAPI_X_RELAX:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ pb->pb_op->o_relax = *((int *)value);
+ break;
+ case SLAPI_BACKEND:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ pb->pb_op->o_bd = (BackendDB *)value;
+ break;
+ case SLAPI_CONNECTION:
+ pb->pb_conn = (Connection *)value;
+ break;
+ case SLAPI_X_CONN_SSF:
+ PBLOCK_ASSERT_CONN( pb );
+ PBLOCK_LOCK_CONN( pb );
+ pb->pb_conn->c_ssf = (slap_ssf_t)(long)value;
+ PBLOCK_UNLOCK_CONN( pb );
+ break;
+ case SLAPI_X_CONN_SASL_CONTEXT:
+ PBLOCK_ASSERT_CONN( pb );
+ PBLOCK_LOCK_CONN( pb );
+ pb->pb_conn->c_sasl_authctx = value;
+ PBLOCK_UNLOCK_CONN( pb );
+ break;
+ case SLAPI_TARGET_DN:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ rc = pblock_set_dn( value, &pb->pb_op->o_req_dn, &pb->pb_op->o_req_ndn, pb->pb_op->o_tmpmemctx );
+ break;
+ case SLAPI_CONN_ID:
+ PBLOCK_ASSERT_CONN( pb );
+ PBLOCK_LOCK_CONN( pb );
+ pb->pb_conn->c_connid = *((long *)value);
+ PBLOCK_UNLOCK_CONN( pb );
+ break;
+ case SLAPI_CONN_DN:
+ PBLOCK_ASSERT_CONN( pb );
+ PBLOCK_LOCK_CONN( pb );
+ rc = pblock_set_dn( value, &pb->pb_conn->c_dn, &pb->pb_conn->c_ndn, NULL );
+ PBLOCK_UNLOCK_CONN( pb );
+ break;
+ case SLAPI_RESULT_CODE:
+ case SLAPI_PLUGIN_INTOP_RESULT:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ pb->pb_rs->sr_err = *((int *)value);
+ break;
+ case SLAPI_RESULT_TEXT:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ snprintf( pb->pb_textbuf, sizeof( pb->pb_textbuf ), "%s", (char *)value );
+ pb->pb_rs->sr_text = pb->pb_textbuf;
+ break;
+ case SLAPI_RESULT_MATCHED:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ pb->pb_rs->sr_matched = (char *)value; /* XXX should dup? */
+ break;
+ case SLAPI_ADD_ENTRY:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ if ( pb->pb_op->o_tag == LDAP_REQ_ADD )
+ pb->pb_op->ora_e = (Slapi_Entry *)value;
+ else
+ rc = PBLOCK_ERROR;
+ break;
+ case SLAPI_MODIFY_MODS: {
+ Modifications **mlp;
+ Modifications *newmods;
+
+ PBLOCK_ASSERT_OP( pb, 0 );
+ rc = pblock_set_default( pb, param, value );
+ if ( rc != PBLOCK_SUCCESS ) {
+ break;
+ }
+
+ if ( pb->pb_op->o_tag == LDAP_REQ_MODIFY ) {
+ mlp = &pb->pb_op->orm_modlist;
+ } else if ( pb->pb_op->o_tag == LDAP_REQ_ADD ) {
+ mlp = &pb->pb_op->ora_modlist;
+ } else if ( pb->pb_op->o_tag == LDAP_REQ_MODRDN ) {
+ mlp = &pb->pb_op->orr_modlist;
+ } else {
+ break;
+ }
+
+ newmods = slapi_int_ldapmods2modifications( pb->pb_op, (LDAPMod **)value );
+ if ( newmods != NULL ) {
+ slap_mods_free( *mlp, 1 );
+ *mlp = newmods;
+ }
+ break;
+ }
+ case SLAPI_MODRDN_NEWRDN:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ PBLOCK_VALIDATE_IS_INTOP( pb );
+ if ( pb->pb_op->o_tag == LDAP_REQ_MODRDN ) {
+ rc = pblock_set_dn( value, &pb->pb_op->orr_newrdn, &pb->pb_op->orr_nnewrdn, pb->pb_op->o_tmpmemctx );
+ if ( rc == LDAP_SUCCESS )
+ rc = rdn_validate( &pb->pb_op->orr_nnewrdn );
+ } else {
+ rc = PBLOCK_ERROR;
+ }
+ break;
+ case SLAPI_MODRDN_NEWSUPERIOR:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ PBLOCK_VALIDATE_IS_INTOP( pb );
+ if ( pb->pb_op->o_tag == LDAP_REQ_MODRDN ) {
+ if ( value == NULL ) {
+ if ( pb->pb_op->orr_newSup != NULL ) {
+ pb->pb_op->o_tmpfree( pb->pb_op->orr_newSup, pb->pb_op->o_tmpmemctx );
+ BER_BVZERO( pb->pb_op->orr_newSup );
+ pb->pb_op->orr_newSup = NULL;
+ }
+ if ( pb->pb_op->orr_newSup != NULL ) {
+ pb->pb_op->o_tmpfree( pb->pb_op->orr_nnewSup, pb->pb_op->o_tmpmemctx );
+ BER_BVZERO( pb->pb_op->orr_nnewSup );
+ pb->pb_op->orr_nnewSup = NULL;
+ }
+ } else {
+ if ( pb->pb_op->orr_newSup == NULL ) {
+ pb->pb_op->orr_newSup = (struct berval *)pb->pb_op->o_tmpalloc(
+ sizeof(struct berval), pb->pb_op->o_tmpmemctx );
+ BER_BVZERO( pb->pb_op->orr_newSup );
+ }
+ if ( pb->pb_op->orr_nnewSup == NULL ) {
+ pb->pb_op->orr_nnewSup = (struct berval *)pb->pb_op->o_tmpalloc(
+ sizeof(struct berval), pb->pb_op->o_tmpmemctx );
+ BER_BVZERO( pb->pb_op->orr_nnewSup );
+ }
+ rc = pblock_set_dn( value, pb->pb_op->orr_newSup, pb->pb_op->orr_nnewSup, pb->pb_op->o_tmpmemctx );
+ }
+ } else {
+ rc = PBLOCK_ERROR;
+ }
+ break;
+ case SLAPI_MODRDN_DELOLDRDN:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ PBLOCK_VALIDATE_IS_INTOP( pb );
+ if ( pb->pb_op->o_tag == LDAP_REQ_MODRDN )
+ pb->pb_op->orr_deleteoldrdn = *((int *)value);
+ else
+ rc = PBLOCK_ERROR;
+ break;
+ case SLAPI_SEARCH_SCOPE: {
+ int scope = *((int *)value);
+
+ PBLOCK_ASSERT_OP( pb, 0 );
+ if ( pb->pb_op->o_tag == LDAP_REQ_SEARCH ) {
+ switch ( *((int *)value) ) {
+ case LDAP_SCOPE_BASE:
+ case LDAP_SCOPE_ONELEVEL:
+ case LDAP_SCOPE_SUBTREE:
+ case LDAP_SCOPE_SUBORDINATE:
+ pb->pb_op->ors_scope = scope;
+ break;
+ default:
+ rc = PBLOCK_ERROR;
+ break;
+ }
+ } else {
+ rc = PBLOCK_ERROR;
+ }
+ break;
+ }
+ case SLAPI_SEARCH_DEREF:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ if ( pb->pb_op->o_tag == LDAP_REQ_SEARCH )
+ pb->pb_op->ors_deref = *((int *)value);
+ else
+ rc = PBLOCK_ERROR;
+ break;
+ case SLAPI_SEARCH_SIZELIMIT:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ if ( pb->pb_op->o_tag == LDAP_REQ_SEARCH )
+ pb->pb_op->ors_slimit = *((int *)value);
+ else
+ rc = PBLOCK_ERROR;
+ break;
+ case SLAPI_SEARCH_TIMELIMIT:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ if ( pb->pb_op->o_tag == LDAP_REQ_SEARCH )
+ pb->pb_op->ors_tlimit = *((int *)value);
+ else
+ rc = PBLOCK_ERROR;
+ break;
+ case SLAPI_SEARCH_FILTER:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ if ( pb->pb_op->o_tag == LDAP_REQ_SEARCH )
+ pb->pb_op->ors_filter = (Slapi_Filter *)value;
+ else
+ rc = PBLOCK_ERROR;
+ break;
+ case SLAPI_SEARCH_STRFILTER:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ if ( pb->pb_op->o_tag == LDAP_REQ_SEARCH ) {
+ pb->pb_op->ors_filterstr.bv_val = (char *)value;
+ pb->pb_op->ors_filterstr.bv_len = strlen((char *)value);
+ } else {
+ rc = PBLOCK_ERROR;
+ }
+ break;
+ case SLAPI_SEARCH_ATTRS: {
+ AttributeName *an = NULL;
+ size_t i = 0, j = 0;
+ char **attrs = (char **)value;
+
+ PBLOCK_ASSERT_OP( pb, 0 );
+ PBLOCK_VALIDATE_IS_INTOP( pb );
+
+ if ( pb->pb_op->o_tag != LDAP_REQ_SEARCH ) {
+ rc = PBLOCK_ERROR;
+ break;
+ }
+ /* also set mapped attrs */
+ rc = pblock_set_default( pb, param, value );
+ if ( rc != PBLOCK_SUCCESS ) {
+ break;
+ }
+ if ( pb->pb_op->ors_attrs != NULL ) {
+ pb->pb_op->o_tmpfree( pb->pb_op->ors_attrs, pb->pb_op->o_tmpmemctx );
+ pb->pb_op->ors_attrs = NULL;
+ }
+ if ( attrs != NULL ) {
+ for ( i = 0; attrs[i] != NULL; i++ )
+ ;
+ }
+ if ( i ) {
+ an = (AttributeName *)pb->pb_op->o_tmpcalloc( i + 1,
+ sizeof(AttributeName), pb->pb_op->o_tmpmemctx );
+ for ( i = 0; attrs[i] != NULL; i++ ) {
+ an[j].an_desc = NULL;
+ an[j].an_oc = NULL;
+ an[j].an_flags = 0;
+ an[j].an_name.bv_val = attrs[i];
+ an[j].an_name.bv_len = strlen( attrs[i] );
+ if ( slap_bv2ad( &an[j].an_name, &an[j].an_desc, &pb->pb_rs->sr_text ) == LDAP_SUCCESS ) {
+ j++;
+ }
+ }
+ an[j].an_name.bv_val = NULL;
+ an[j].an_name.bv_len = 0;
+ }
+ pb->pb_op->ors_attrs = an;
+ break;
+ }
+ case SLAPI_SEARCH_ATTRSONLY:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ PBLOCK_VALIDATE_IS_INTOP( pb );
+
+ if ( pb->pb_op->o_tag == LDAP_REQ_SEARCH )
+ pb->pb_op->ors_attrsonly = *((int *)value);
+ else
+ rc = PBLOCK_ERROR;
+ break;
+ case SLAPI_SEARCH_RESULT_ENTRY:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ rs_replace_entry( pb->pb_op, pb->pb_rs, NULL, (Slapi_Entry *)value );
+ /* TODO: Should REP_ENTRY_MODIFIABLE be set? */
+ pb->pb_rs->sr_flags |= REP_ENTRY_MUSTBEFREED;
+ break;
+ case SLAPI_BIND_RET_SASLCREDS:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ pb->pb_rs->sr_sasldata = (struct berval *)value;
+ break;
+ case SLAPI_EXT_OP_REQ_OID:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ PBLOCK_VALIDATE_IS_INTOP( pb );
+
+ if ( pb->pb_op->o_tag == LDAP_REQ_EXTENDED ) {
+ pb->pb_op->ore_reqoid.bv_val = (char *)value;
+ pb->pb_op->ore_reqoid.bv_len = strlen((char *)value);
+ } else {
+ rc = PBLOCK_ERROR;
+ }
+ break;
+ case SLAPI_EXT_OP_REQ_VALUE:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ PBLOCK_VALIDATE_IS_INTOP( pb );
+
+ if ( pb->pb_op->o_tag == LDAP_REQ_EXTENDED )
+ pb->pb_op->ore_reqdata = (struct berval *)value;
+ else
+ rc = PBLOCK_ERROR;
+ break;
+ case SLAPI_EXT_OP_RET_OID:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ pb->pb_rs->sr_rspoid = (char *)value;
+ break;
+ case SLAPI_EXT_OP_RET_VALUE:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ pb->pb_rs->sr_rspdata = (struct berval *)value;
+ break;
+ case SLAPI_BIND_METHOD:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ PBLOCK_VALIDATE_IS_INTOP( pb );
+
+ if ( pb->pb_op->o_tag == LDAP_REQ_BIND )
+ pb->pb_op->orb_method = *((int *)value);
+ else
+ rc = PBLOCK_ERROR;
+ break;
+ case SLAPI_BIND_CREDENTIALS:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ PBLOCK_VALIDATE_IS_INTOP( pb );
+
+ if ( pb->pb_op->o_tag == LDAP_REQ_BIND )
+ pb->pb_op->orb_cred = *((struct berval *)value);
+ else
+ rc = PBLOCK_ERROR;
+ break;
+ case SLAPI_COMPARE_TYPE:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ PBLOCK_VALIDATE_IS_INTOP( pb );
+
+ if ( pb->pb_op->o_tag == LDAP_REQ_COMPARE ) {
+ const char *text;
+
+ pb->pb_op->orc_ava->aa_desc = NULL;
+ rc = slap_str2ad( (char *)value, &pb->pb_op->orc_ava->aa_desc, &text );
+ } else {
+ rc = PBLOCK_ERROR;
+ }
+ break;
+ case SLAPI_COMPARE_VALUE:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ PBLOCK_VALIDATE_IS_INTOP( pb );
+
+ if ( pb->pb_op->o_tag == LDAP_REQ_COMPARE )
+ pb->pb_op->orc_ava->aa_value = *((struct berval *)value);
+ else
+ rc = PBLOCK_ERROR;
+ break;
+ case SLAPI_ABANDON_MSGID:
+ PBLOCK_ASSERT_OP( pb, 0 );
+ PBLOCK_VALIDATE_IS_INTOP( pb );
+
+ if ( pb->pb_op->o_tag == LDAP_REQ_ABANDON)
+ pb->pb_op->orn_msgid = *((int *)value);
+ else
+ rc = PBLOCK_ERROR;
+ break;
+ case SLAPI_REQUESTOR_ISROOT:
+ case SLAPI_IS_REPLICATED_OPERATION:
+ case SLAPI_CONN_AUTHTYPE:
+ case SLAPI_CONN_AUTHMETHOD:
+ case SLAPI_IS_INTERNAL_OPERATION:
+ case SLAPI_X_CONN_IS_UDP:
+ case SLAPI_CONN_CLIENTIP:
+ case SLAPI_X_CONN_CLIENTPATH:
+ case SLAPI_CONN_SERVERIP:
+ case SLAPI_X_CONN_SERVERPATH:
+ case SLAPI_X_ADD_STRUCTURAL_CLASS:
+ /* These parameters cannot be set */
+ rc = PBLOCK_ERROR;
+ break;
+ default:
+ rc = pblock_set_default( pb, param, value );
+ break;
+ }
+
+ pblock_unlock( pb );
+
+ return rc;
+}
+
+static void
+pblock_clear( Slapi_PBlock *pb )
+{
+ pb->pb_nParams = 1;
+}
+
+static int
+pblock_delete_param( Slapi_PBlock *p, int param )
+{
+ int i;
+
+ pblock_lock(p);
+
+ for ( i = 0; i < p->pb_nParams; i++ ) {
+ if ( p->pb_params[i] == param ) {
+ break;
+ }
+ }
+
+ if (i >= p->pb_nParams ) {
+ pblock_unlock( p );
+ return PBLOCK_ERROR;
+ }
+
+ /* move last parameter to index of deleted parameter */
+ if ( p->pb_nParams > 1 ) {
+ p->pb_params[i] = p->pb_params[p->pb_nParams - 1];
+ p->pb_values[i] = p->pb_values[p->pb_nParams - 1];
+ }
+ p->pb_nParams--;
+
+ pblock_unlock( p );
+
+ return PBLOCK_SUCCESS;
+}
+
+Slapi_PBlock *
+slapi_pblock_new(void)
+{
+ Slapi_PBlock *pb;
+
+ pb = (Slapi_PBlock *) ch_calloc( 1, sizeof(Slapi_PBlock) );
+ if ( pb != NULL ) {
+ ldap_pvt_thread_mutex_init( &pb->pb_mutex );
+
+ pb->pb_params[0] = SLAPI_IBM_PBLOCK;
+ pb->pb_values[0].pv_pointer = NULL;
+ pb->pb_nParams = 1;
+ pb->pb_conn = NULL;
+ pb->pb_op = NULL;
+ pb->pb_rs = NULL;
+ pb->pb_intop = 0;
+ }
+ return pb;
+}
+
+static void
+pblock_destroy( Slapi_PBlock *pb )
+{
+ LDAPControl **controls = NULL;
+ LDAPMod **mods = NULL;
+ char **attrs = NULL;
+
+ assert( pb != NULL );
+
+ pblock_get_default( pb, SLAPI_RESCONTROLS, (void **)&controls );
+ if ( controls != NULL ) {
+ ldap_controls_free( controls );
+ }
+
+ if ( pb->pb_intop ) {
+ slapi_int_connection_done_pb( pb );
+ } else {
+ pblock_get_default( pb, SLAPI_MODIFY_MODS, (void **)&mods );
+ ldap_mods_free( mods, 1 );
+
+ pblock_get_default( pb, SLAPI_SEARCH_ATTRS, (void **)&attrs );
+ if ( attrs != NULL )
+ pb->pb_op->o_tmpfree( attrs, pb->pb_op->o_tmpmemctx );
+ }
+
+ ldap_pvt_thread_mutex_destroy( &pb->pb_mutex );
+ slapi_ch_free( (void **)&pb );
+}
+
+void
+slapi_pblock_destroy( Slapi_PBlock *pb )
+{
+ if ( pb != NULL ) {
+ pblock_destroy( pb );
+ }
+}
+
+int
+slapi_pblock_get( Slapi_PBlock *pb, int arg, void *value )
+{
+ return pblock_get( pb, arg, (void **)value );
+}
+
+int
+slapi_pblock_set( Slapi_PBlock *pb, int arg, void *value )
+{
+ return pblock_set( pb, arg, value );
+}
+
+void
+slapi_pblock_clear( Slapi_PBlock *pb )
+{
+ pblock_clear( pb );
+}
+
+int
+slapi_pblock_delete_param( Slapi_PBlock *p, int param )
+{
+ return pblock_delete_param( p, param );
+}
+
+/*
+ * OpenLDAP extension
+ */
+int
+slapi_int_pblock_get_first( Backend *be, Slapi_PBlock **pb )
+{
+ assert( pb != NULL );
+ *pb = SLAPI_BACKEND_PBLOCK( be );
+ return (*pb == NULL ? LDAP_OTHER : LDAP_SUCCESS);
+}
+
+/*
+ * OpenLDAP extension
+ */
+int
+slapi_int_pblock_get_next( Slapi_PBlock **pb )
+{
+ assert( pb != NULL );
+ return slapi_pblock_get( *pb, SLAPI_IBM_PBLOCK, pb );
+}
+
+#endif /* LDAP_SLAPI */
diff --git a/servers/slapd/slapi/slapi_utils.c b/servers/slapd/slapi/slapi_utils.c
new file mode 100644
index 0000000..6c1cecf
--- /dev/null
+++ b/servers/slapd/slapi/slapi_utils.c
@@ -0,0 +1,3473 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1997,2002-2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by IBM Corporation for use in
+ * IBM products and subsequently ported to OpenLDAP Software by
+ * Steve Omrani. Additional significant contributors include:
+ * Luke Howard
+ */
+
+#include "portable.h"
+
+#include <ac/string.h>
+#include <ac/stdarg.h>
+#include <ac/ctype.h>
+#include <ac/unistd.h>
+#include <lutil.h>
+
+#include <slap.h>
+#include <slapi.h>
+
+#include <netdb.h>
+
+#ifdef LDAP_SLAPI
+
+/*
+ * server start time (should we use a struct timeval also in slapd?
+ */
+static struct timeval base_time;
+ldap_pvt_thread_mutex_t slapi_hn_mutex;
+ldap_pvt_thread_mutex_t slapi_time_mutex;
+
+struct slapi_mutex {
+ ldap_pvt_thread_mutex_t mutex;
+};
+
+struct slapi_condvar {
+ ldap_pvt_thread_cond_t cond;
+ ldap_pvt_thread_mutex_t mutex;
+};
+
+static int checkBVString(const struct berval *bv)
+{
+ ber_len_t i;
+
+ for ( i = 0; i < bv->bv_len; i++ ) {
+ if ( bv->bv_val[i] == '\0' )
+ return 0;
+ }
+ if ( bv->bv_val[i] != '\0' )
+ return 0;
+
+ return 1;
+}
+
+/*
+ * This function converts an array of pointers to berval objects to
+ * an array of berval objects.
+ */
+
+int
+bvptr2obj(
+ struct berval **bvptr,
+ BerVarray *bvobj,
+ unsigned *num )
+{
+ int rc = LDAP_SUCCESS;
+ int i;
+ BerVarray tmpberval;
+
+ if ( bvptr == NULL || *bvptr == NULL ) {
+ return LDAP_OTHER;
+ }
+
+ for ( i = 0; bvptr != NULL && bvptr[i] != NULL; i++ ) {
+ ; /* EMPTY */
+ }
+ if ( num )
+ *num = i;
+
+ tmpberval = (BerVarray)slapi_ch_malloc( (i + 1)*sizeof(struct berval));
+ if ( tmpberval == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+
+ for ( i = 0; bvptr[i] != NULL; i++ ) {
+ tmpberval[i].bv_val = bvptr[i]->bv_val;
+ tmpberval[i].bv_len = bvptr[i]->bv_len;
+ }
+ tmpberval[i].bv_val = NULL;
+ tmpberval[i].bv_len = 0;
+
+ if ( rc == LDAP_SUCCESS ) {
+ *bvobj = tmpberval;
+ }
+
+ return rc;
+}
+
+Slapi_Entry *
+slapi_str2entry(
+ char *s,
+ int flags )
+{
+ return str2entry( s );
+}
+
+char *
+slapi_entry2str(
+ Slapi_Entry *e,
+ int *len )
+{
+ char *ret = NULL;
+ char *s;
+
+ ldap_pvt_thread_mutex_lock( &entry2str_mutex );
+ s = entry2str( e, len );
+ if ( s != NULL )
+ ret = slapi_ch_strdup( s );
+ ldap_pvt_thread_mutex_unlock( &entry2str_mutex );
+
+ return ret;
+}
+
+char *
+slapi_entry_get_dn( Slapi_Entry *e )
+{
+ return e->e_name.bv_val;
+}
+
+int
+slapi_x_entry_get_id( Slapi_Entry *e )
+{
+ return e->e_id;
+}
+
+static int
+slapi_int_dn_pretty( struct berval *in, struct berval *out )
+{
+ Syntax *syntax = slap_schema.si_syn_distinguishedName;
+
+ assert( syntax != NULL );
+
+ return (syntax->ssyn_pretty)( syntax, in, out, NULL );
+}
+
+static int
+slapi_int_dn_normalize( struct berval *in, struct berval *out )
+{
+ MatchingRule *mr = slap_schema.si_mr_distinguishedNameMatch;
+ Syntax *syntax = slap_schema.si_syn_distinguishedName;
+
+ assert( mr != NULL );
+
+ return (mr->smr_normalize)( 0, syntax, mr, in, out, NULL );
+}
+
+void
+slapi_entry_set_dn(
+ Slapi_Entry *e,
+ char *ldn )
+{
+ struct berval dn = BER_BVNULL;
+
+ dn.bv_val = ldn;
+ dn.bv_len = strlen( ldn );
+
+ slapi_int_dn_pretty( &dn, &e->e_name );
+ slapi_int_dn_normalize( &dn, &e->e_nname );
+}
+
+Slapi_Entry *
+slapi_entry_dup( Slapi_Entry *e )
+{
+ return entry_dup( e );
+}
+
+int
+slapi_entry_attr_delete(
+ Slapi_Entry *e,
+ char *type )
+{
+ AttributeDescription *ad = NULL;
+ const char *text;
+
+ if ( slap_str2ad( type, &ad, &text ) != LDAP_SUCCESS ) {
+ return 1; /* LDAP_NO_SUCH_ATTRIBUTE */
+ }
+
+ if ( attr_delete( &e->e_attrs, ad ) == LDAP_SUCCESS ) {
+ return 0; /* attribute is deleted */
+ } else {
+ return -1; /* something went wrong */
+ }
+}
+
+Slapi_Entry *
+slapi_entry_alloc( void )
+{
+ return (Slapi_Entry *)entry_alloc();
+}
+
+void
+slapi_entry_free( Slapi_Entry *e )
+{
+ if ( e != NULL )
+ entry_free( e );
+}
+
+int
+slapi_entry_attr_merge(
+ Slapi_Entry *e,
+ char *type,
+ struct berval **vals )
+{
+ AttributeDescription *ad = NULL;
+ const char *text;
+ BerVarray bv;
+ int rc;
+
+ rc = slap_str2ad( type, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return -1;
+ }
+
+ rc = bvptr2obj( vals, &bv, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ return -1;
+ }
+
+ rc = attr_merge_normalize( e, ad, bv, NULL );
+ ch_free( bv );
+
+ return rc;
+}
+
+int
+slapi_entry_attr_find(
+ Slapi_Entry *e,
+ char *type,
+ Slapi_Attr **attr )
+{
+ AttributeDescription *ad = NULL;
+ const char *text;
+ int rc;
+
+ rc = slap_str2ad( type, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return -1;
+ }
+
+ *attr = attr_find( e->e_attrs, ad );
+ if ( *attr == NULL ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+char *
+slapi_entry_attr_get_charptr( const Slapi_Entry *e, const char *type )
+{
+ AttributeDescription *ad = NULL;
+ const char *text;
+ int rc;
+ Attribute *attr;
+
+ rc = slap_str2ad( type, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return NULL;
+ }
+
+ attr = attr_find( e->e_attrs, ad );
+ if ( attr == NULL ) {
+ return NULL;
+ }
+
+ if ( attr->a_vals != NULL && attr->a_vals[0].bv_len != 0 ) {
+ const char *p;
+
+ p = slapi_value_get_string( &attr->a_vals[0] );
+ if ( p != NULL ) {
+ return slapi_ch_strdup( p );
+ }
+ }
+
+ return NULL;
+}
+
+int
+slapi_entry_attr_get_int( const Slapi_Entry *e, const char *type )
+{
+ AttributeDescription *ad = NULL;
+ const char *text;
+ int rc;
+ Attribute *attr;
+
+ rc = slap_str2ad( type, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ attr = attr_find( e->e_attrs, ad );
+ if ( attr == NULL ) {
+ return 0;
+ }
+
+ return slapi_value_get_int( attr->a_vals );
+}
+
+long
+slapi_entry_attr_get_long( const Slapi_Entry *e, const char *type )
+{
+ AttributeDescription *ad = NULL;
+ const char *text;
+ int rc;
+ Attribute *attr;
+
+ rc = slap_str2ad( type, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ attr = attr_find( e->e_attrs, ad );
+ if ( attr == NULL ) {
+ return 0;
+ }
+
+ return slapi_value_get_long( attr->a_vals );
+}
+
+unsigned int
+slapi_entry_attr_get_uint( const Slapi_Entry *e, const char *type )
+{
+ AttributeDescription *ad = NULL;
+ const char *text;
+ int rc;
+ Attribute *attr;
+
+ rc = slap_str2ad( type, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ attr = attr_find( e->e_attrs, ad );
+ if ( attr == NULL ) {
+ return 0;
+ }
+
+ return slapi_value_get_uint( attr->a_vals );
+}
+
+unsigned long
+slapi_entry_attr_get_ulong( const Slapi_Entry *e, const char *type )
+{
+ AttributeDescription *ad = NULL;
+ const char *text;
+ int rc;
+ Attribute *attr;
+
+ rc = slap_str2ad( type, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ attr = attr_find( e->e_attrs, ad );
+ if ( attr == NULL ) {
+ return 0;
+ }
+
+ return slapi_value_get_ulong( attr->a_vals );
+}
+
+int
+slapi_entry_attr_hasvalue( Slapi_Entry *e, const char *type, const char *value )
+{
+ struct berval bv;
+ AttributeDescription *ad = NULL;
+ const char *text;
+ int rc;
+ Attribute *attr;
+
+ rc = slap_str2ad( type, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ attr = attr_find( e->e_attrs, ad );
+ if ( attr == NULL ) {
+ return 0;
+ }
+
+ bv.bv_val = (char *)value;
+ bv.bv_len = strlen( value );
+
+ return ( slapi_attr_value_find( attr, &bv ) != -1 );
+}
+
+void
+slapi_entry_attr_set_charptr(Slapi_Entry* e, const char *type, const char *value)
+{
+ AttributeDescription *ad = NULL;
+ const char *text;
+ int rc;
+ struct berval bv;
+
+ rc = slap_str2ad( type, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return;
+ }
+
+ attr_delete ( &e->e_attrs, ad );
+ if ( value != NULL ) {
+ bv.bv_val = (char *)value;
+ bv.bv_len = strlen(value);
+ attr_merge_normalize_one( e, ad, &bv, NULL );
+ }
+}
+
+void
+slapi_entry_attr_set_int( Slapi_Entry* e, const char *type, int l)
+{
+ char buf[64];
+
+ snprintf( buf, sizeof( buf ), "%d", l );
+ slapi_entry_attr_set_charptr( e, type, buf );
+}
+
+void
+slapi_entry_attr_set_uint( Slapi_Entry* e, const char *type, unsigned int l)
+{
+ char buf[64];
+
+ snprintf( buf, sizeof( buf ), "%u", l );
+ slapi_entry_attr_set_charptr( e, type, buf );
+}
+
+void
+slapi_entry_attr_set_long(Slapi_Entry* e, const char *type, long l)
+{
+ char buf[64];
+
+ snprintf( buf, sizeof( buf ), "%ld", l );
+ slapi_entry_attr_set_charptr( e, type, buf );
+}
+
+void
+slapi_entry_attr_set_ulong(Slapi_Entry* e, const char *type, unsigned long l)
+{
+ char buf[64];
+
+ snprintf( buf, sizeof( buf ), "%lu", l );
+ slapi_entry_attr_set_charptr( e, type, buf );
+}
+
+int
+slapi_is_rootdse( const char *dn )
+{
+ return ( dn == NULL || dn[0] == '\0' );
+}
+
+int
+slapi_entry_has_children( const Slapi_Entry *e )
+{
+ Slapi_PBlock *pb;
+ Backend *be = select_backend( (struct berval *)&e->e_nname, 0 );
+ int rc, hasSubordinates = 0;
+
+ if ( be == NULL || be->be_has_subordinates == 0 ) {
+ return 0;
+ }
+
+ pb = slapi_pblock_new();
+ if ( pb == NULL ) {
+ return 0;
+ }
+ slapi_int_connection_init_pb( pb, LDAP_REQ_SEARCH );
+
+ rc = slapi_pblock_set( pb, SLAPI_TARGET_DN, slapi_entry_get_dn(
+ (Entry *) e ));
+ if ( rc == LDAP_SUCCESS ) {
+ pb->pb_op->o_bd = be;
+ rc = be->be_has_subordinates( pb->pb_op, (Entry *) e,
+ &hasSubordinates );
+ }
+
+ slapi_pblock_destroy( pb );
+
+ return ( rc == LDAP_SUCCESS && hasSubordinates == LDAP_COMPARE_TRUE );
+}
+
+/*
+ * Return approximate size of the entry rounded to the nearest
+ * 1K. Only the size of the attribute values are counted in the
+ * Sun implementation.
+ *
+ * http://docs.sun.com/source/816-6701-10/funcref.html#1017388
+ */
+size_t slapi_entry_size(Slapi_Entry *e)
+{
+ size_t size;
+ Attribute *a;
+ int i;
+
+ for ( size = 0, a = e->e_attrs; a != NULL; a = a->a_next ) {
+ for ( i = 0; a->a_vals[i].bv_val != NULL; i++ ) {
+ size += a->a_vals[i].bv_len + 1;
+ }
+ }
+
+ size += 1023;
+ size -= (size % 1024);
+
+ return size;
+}
+
+/*
+ * Add values to entry.
+ *
+ * Returns:
+ * LDAP_SUCCESS Values added to entry
+ * LDAP_TYPE_OR_VALUE_EXISTS One or more values exist in entry already
+ * LDAP_CONSTRAINT_VIOLATION Any other error (odd, but it's the spec)
+ */
+int
+slapi_entry_add_values( Slapi_Entry *e, const char *type, struct berval **vals )
+{
+ Modification mod;
+ const char *text;
+ int rc;
+ char textbuf[SLAP_TEXT_BUFLEN];
+
+ mod.sm_op = LDAP_MOD_ADD;
+ mod.sm_flags = 0;
+ mod.sm_desc = NULL;
+ mod.sm_type.bv_val = (char *)type;
+ mod.sm_type.bv_len = strlen( type );
+
+ rc = slap_str2ad( type, &mod.sm_desc, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( vals == NULL ) {
+ /* Apparently vals can be NULL
+ * FIXME: sm_values = NULL ? */
+ mod.sm_values = (BerVarray)ch_malloc( sizeof(struct berval) );
+ mod.sm_values->bv_val = NULL;
+ mod.sm_numvals = 0;
+
+ } else {
+ rc = bvptr2obj( vals, &mod.sm_values, &mod.sm_numvals );
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ }
+ mod.sm_nvalues = NULL;
+
+ rc = modify_add_values( e, &mod, 0, &text, textbuf, sizeof(textbuf) );
+
+ slapi_ch_free( (void **)&mod.sm_values );
+
+ return (rc == LDAP_SUCCESS) ? LDAP_SUCCESS : LDAP_CONSTRAINT_VIOLATION;
+}
+
+int
+slapi_entry_add_values_sv( Slapi_Entry *e, const char *type, Slapi_Value **vals )
+{
+ return slapi_entry_add_values( e, type, vals );
+}
+
+int
+slapi_entry_add_valueset(Slapi_Entry *e, const char *type, Slapi_ValueSet *vs)
+{
+ AttributeDescription *ad = NULL;
+ const char *text;
+ int rc;
+
+ rc = slap_str2ad( type, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return -1;
+ }
+
+ return attr_merge_normalize( e, ad, *vs, NULL );
+}
+
+int
+slapi_entry_delete_values( Slapi_Entry *e, const char *type, struct berval **vals )
+{
+ Modification mod;
+ const char *text;
+ int rc;
+ char textbuf[SLAP_TEXT_BUFLEN];
+
+ mod.sm_op = LDAP_MOD_DELETE;
+ mod.sm_flags = 0;
+ mod.sm_desc = NULL;
+ mod.sm_type.bv_val = (char *)type;
+ mod.sm_type.bv_len = strlen( type );
+
+ if ( vals == NULL ) {
+ /* If vals is NULL, this is a NOOP. */
+ return LDAP_SUCCESS;
+ }
+
+ rc = slap_str2ad( type, &mod.sm_desc, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( vals[0] == NULL ) {
+ /* SLAPI doco says LDApb_opERATIONS_ERROR but LDAP_OTHER is better */
+ return attr_delete( &e->e_attrs, mod.sm_desc ) ? LDAP_OTHER : LDAP_SUCCESS;
+ }
+
+ rc = bvptr2obj( vals, &mod.sm_values, &mod.sm_numvals );
+ if ( rc != LDAP_SUCCESS ) {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ mod.sm_nvalues = NULL;
+
+ rc = modify_delete_values( e, &mod, 0, &text, textbuf, sizeof(textbuf) );
+
+ slapi_ch_free( (void **)&mod.sm_values );
+
+ return rc;
+}
+
+int
+slapi_entry_delete_values_sv( Slapi_Entry *e, const char *type, Slapi_Value **vals )
+{
+ return slapi_entry_delete_values( e, type, vals );
+}
+
+int
+slapi_entry_merge_values_sv( Slapi_Entry *e, const char *type, Slapi_Value **vals )
+{
+ return slapi_entry_attr_merge( e, (char *)type, vals );
+}
+
+int
+slapi_entry_add_value(Slapi_Entry *e, const char *type, const Slapi_Value *value)
+{
+ AttributeDescription *ad = NULL;
+ int rc;
+ const char *text;
+
+ rc = slap_str2ad( type, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return -1;
+ }
+
+ rc = attr_merge_normalize_one( e, ad, (Slapi_Value *)value, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+slapi_entry_add_string(Slapi_Entry *e, const char *type, const char *value)
+{
+ Slapi_Value val;
+
+ val.bv_val = (char *)value;
+ val.bv_len = strlen( value );
+
+ return slapi_entry_add_value( e, type, &val );
+}
+
+int
+slapi_entry_delete_string(Slapi_Entry *e, const char *type, const char *value)
+{
+ Slapi_Value *vals[2];
+ Slapi_Value val;
+
+ val.bv_val = (char *)value;
+ val.bv_len = strlen( value );
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ return slapi_entry_delete_values_sv( e, type, vals );
+}
+
+int
+slapi_entry_attr_merge_sv( Slapi_Entry *e, const char *type, Slapi_Value **vals )
+{
+ return slapi_entry_attr_merge( e, (char *)type, vals );
+}
+
+int
+slapi_entry_first_attr( const Slapi_Entry *e, Slapi_Attr **attr )
+{
+ if ( e == NULL ) {
+ return -1;
+ }
+
+ *attr = e->e_attrs;
+
+ return ( *attr != NULL ) ? 0 : -1;
+}
+
+int
+slapi_entry_next_attr( const Slapi_Entry *e, Slapi_Attr *prevattr, Slapi_Attr **attr )
+{
+ if ( e == NULL ) {
+ return -1;
+ }
+
+ if ( prevattr == NULL ) {
+ return -1;
+ }
+
+ *attr = prevattr->a_next;
+
+ return ( *attr != NULL ) ? 0 : -1;
+}
+
+int
+slapi_entry_attr_replace_sv( Slapi_Entry *e, const char *type, Slapi_Value **vals )
+{
+ AttributeDescription *ad = NULL;
+ const char *text;
+ int rc;
+ BerVarray bv;
+
+ rc = slap_str2ad( type, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ attr_delete( &e->e_attrs, ad );
+
+ rc = bvptr2obj( vals, &bv, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ return -1;
+ }
+
+ rc = attr_merge_normalize( e, ad, bv, NULL );
+ slapi_ch_free( (void **)&bv );
+ if ( rc != LDAP_SUCCESS ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * FIXME -- The caller must free the allocated memory.
+ * In Netscape they do not have to.
+ */
+int
+slapi_attr_get_values(
+ Slapi_Attr *attr,
+ struct berval ***vals )
+{
+ int i, j;
+ struct berval **bv;
+
+ if ( attr == NULL ) {
+ return 1;
+ }
+
+ for ( i = 0; attr->a_vals[i].bv_val != NULL; i++ ) {
+ ; /* EMPTY */
+ }
+
+ bv = (struct berval **)ch_malloc( (i + 1) * sizeof(struct berval *) );
+ for ( j = 0; j < i; j++ ) {
+ bv[j] = ber_dupbv( NULL, &attr->a_vals[j] );
+ }
+ bv[j] = NULL;
+
+ *vals = (struct berval **)bv;
+
+ return 0;
+}
+
+char *
+slapi_dn_normalize( char *dn )
+{
+ struct berval bdn;
+ struct berval pdn;
+
+ assert( dn != NULL );
+
+ bdn.bv_val = dn;
+ bdn.bv_len = strlen( dn );
+
+ if ( slapi_int_dn_pretty( &bdn, &pdn ) != LDAP_SUCCESS ) {
+ return NULL;
+ }
+
+ return pdn.bv_val;
+}
+
+char *
+slapi_dn_normalize_case( char *dn )
+{
+ struct berval bdn;
+ struct berval ndn;
+
+ assert( dn != NULL );
+
+ bdn.bv_val = dn;
+ bdn.bv_len = strlen( dn );
+
+ if ( slapi_int_dn_normalize( &bdn, &ndn ) != LDAP_SUCCESS ) {
+ return NULL;
+ }
+
+ return ndn.bv_val;
+}
+
+int
+slapi_dn_issuffix(
+ char *dn,
+ char *suffix )
+{
+ struct berval bdn, ndn;
+ struct berval bsuffix, nsuffix;
+ int rc;
+
+ assert( dn != NULL );
+ assert( suffix != NULL );
+
+ bdn.bv_val = dn;
+ bdn.bv_len = strlen( dn );
+
+ bsuffix.bv_val = suffix;
+ bsuffix.bv_len = strlen( suffix );
+
+ if ( dnNormalize( 0, NULL, NULL, &bdn, &ndn, NULL ) != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ if ( dnNormalize( 0, NULL, NULL, &bsuffix, &nsuffix, NULL )
+ != LDAP_SUCCESS )
+ {
+ slapi_ch_free( (void **)&ndn.bv_val );
+ return 0;
+ }
+
+ rc = dnIsSuffix( &ndn, &nsuffix );
+
+ slapi_ch_free( (void **)&ndn.bv_val );
+ slapi_ch_free( (void **)&nsuffix.bv_val );
+
+ return rc;
+}
+
+int
+slapi_dn_isparent(
+ const char *parentdn,
+ const char *childdn )
+{
+ struct berval assertedParentDN, normalizedAssertedParentDN;
+ struct berval childDN, normalizedChildDN;
+ struct berval normalizedParentDN;
+ int match;
+
+ assert( parentdn != NULL );
+ assert( childdn != NULL );
+
+ assertedParentDN.bv_val = (char *)parentdn;
+ assertedParentDN.bv_len = strlen( parentdn );
+
+ if ( dnNormalize( 0, NULL, NULL, &assertedParentDN,
+ &normalizedAssertedParentDN, NULL ) != LDAP_SUCCESS )
+ {
+ return 0;
+ }
+
+ childDN.bv_val = (char *)childdn;
+ childDN.bv_len = strlen( childdn );
+
+ if ( dnNormalize( 0, NULL, NULL, &childDN,
+ &normalizedChildDN, NULL ) != LDAP_SUCCESS )
+ {
+ slapi_ch_free( (void **)&normalizedAssertedParentDN.bv_val );
+ return 0;
+ }
+
+ dnParent( &normalizedChildDN, &normalizedParentDN );
+
+ if ( dnMatch( &match, 0, slap_schema.si_syn_distinguishedName, NULL,
+ &normalizedParentDN, (void *)&normalizedAssertedParentDN ) != LDAP_SUCCESS )
+ {
+ match = -1;
+ }
+
+ slapi_ch_free( (void **)&normalizedAssertedParentDN.bv_val );
+ slapi_ch_free( (void **)&normalizedChildDN.bv_val );
+
+ return ( match == 0 );
+}
+
+/*
+ * Returns DN of the parent entry, or NULL if the DN is
+ * an empty string or NULL, or has no parent.
+ */
+char *
+slapi_dn_parent( const char *_dn )
+{
+ struct berval dn, prettyDN;
+ struct berval parentDN;
+ char *ret;
+
+ if ( _dn == NULL ) {
+ return NULL;
+ }
+
+ dn.bv_val = (char *)_dn;
+ dn.bv_len = strlen( _dn );
+
+ if ( dn.bv_len == 0 ) {
+ return NULL;
+ }
+
+ if ( dnPretty( NULL, &dn, &prettyDN, NULL ) != LDAP_SUCCESS ) {
+ return NULL;
+ }
+
+ dnParent( &prettyDN, &parentDN ); /* in-place */
+
+ if ( parentDN.bv_len == 0 ) {
+ slapi_ch_free_string( &prettyDN.bv_val );
+ return NULL;
+ }
+
+ ret = slapi_ch_strdup( parentDN.bv_val );
+ slapi_ch_free_string( &prettyDN.bv_val );
+
+ return ret;
+}
+
+int slapi_dn_isbesuffix( Slapi_PBlock *pb, char *ldn )
+{
+ struct berval ndn;
+ Backend *be;
+
+ if ( slapi_is_rootdse( ldn ) ) {
+ return 0;
+ }
+
+ /* according to spec should already be normalized */
+ ndn.bv_len = strlen( ldn );
+ ndn.bv_val = ldn;
+
+ be = select_backend( &pb->pb_op->o_req_ndn, 0 );
+ if ( be == NULL ) {
+ return 0;
+ }
+
+ return be_issuffix( be, &ndn );
+}
+
+/*
+ * Returns DN of the parent entry; or NULL if the DN is
+ * an empty string, if the DN has no parent, or if the
+ * DN is the suffix of the backend database
+ */
+char *slapi_dn_beparent( Slapi_PBlock *pb, const char *ldn )
+{
+ Backend *be;
+ struct berval dn, prettyDN;
+ struct berval normalizedDN, parentDN;
+ char *parent = NULL;
+
+ if ( pb == NULL ) {
+ return NULL;
+ }
+
+ PBLOCK_ASSERT_OP( pb, 0 );
+
+ if ( slapi_is_rootdse( ldn ) ) {
+ return NULL;
+ }
+
+ dn.bv_val = (char *)ldn;
+ dn.bv_len = strlen( ldn );
+
+ if ( dnPrettyNormal( NULL, &dn, &prettyDN, &normalizedDN, NULL ) != LDAP_SUCCESS ) {
+ return NULL;
+ }
+
+ be = select_backend( &pb->pb_op->o_req_ndn, 0 );
+
+ if ( be == NULL || be_issuffix( be, &normalizedDN ) == 0 ) {
+ dnParent( &prettyDN, &parentDN );
+
+ if ( parentDN.bv_len != 0 )
+ parent = slapi_ch_strdup( parentDN.bv_val );
+ }
+
+ slapi_ch_free_string( &prettyDN.bv_val );
+ slapi_ch_free_string( &normalizedDN.bv_val );
+
+ return parent;
+}
+
+char *
+slapi_dn_ignore_case( char *dn )
+{
+ return slapi_dn_normalize_case( dn );
+}
+
+char *
+slapi_ch_malloc( unsigned long size )
+{
+ return ch_malloc( size );
+}
+
+void
+slapi_ch_free( void **ptr )
+{
+ if ( ptr == NULL || *ptr == NULL )
+ return;
+ ch_free( *ptr );
+ *ptr = NULL;
+}
+
+void
+slapi_ch_free_string( char **ptr )
+{
+ slapi_ch_free( (void **)ptr );
+}
+
+void
+slapi_ch_array_free( char **arrayp )
+{
+ char **p;
+
+ if ( arrayp != NULL ) {
+ for ( p = arrayp; *p != NULL; p++ ) {
+ slapi_ch_free( (void **)p );
+ }
+ slapi_ch_free( (void **)&arrayp );
+ }
+}
+
+struct berval *
+slapi_ch_bvdup(const struct berval *v)
+{
+ return ber_dupbv(NULL, (struct berval *)v);
+}
+
+struct berval **
+slapi_ch_bvecdup(const struct berval **v)
+{
+ int i;
+ struct berval **rv;
+
+ if ( v == NULL ) {
+ return NULL;
+ }
+
+ for ( i = 0; v[i] != NULL; i++ )
+ ;
+
+ rv = (struct berval **) slapi_ch_malloc( (i + 1) * sizeof(struct berval *) );
+
+ for ( i = 0; v[i] != NULL; i++ ) {
+ rv[i] = slapi_ch_bvdup( v[i] );
+ }
+ rv[i] = NULL;
+
+ return rv;
+}
+
+char *
+slapi_ch_calloc(
+ unsigned long nelem,
+ unsigned long size )
+{
+ return ch_calloc( nelem, size );
+}
+
+char *
+slapi_ch_realloc(
+ char *block,
+ unsigned long size )
+{
+ return ch_realloc( block, size );
+}
+
+char *
+slapi_ch_strdup( const char *s )
+{
+ return ch_strdup( s );
+}
+
+size_t
+slapi_ch_stlen( const char *s )
+{
+ return strlen( s );
+}
+
+int
+slapi_control_present(
+ LDAPControl **controls,
+ char *oid,
+ struct berval **val,
+ int *iscritical )
+{
+ int i;
+ int rc = 0;
+
+ if ( val ) {
+ *val = NULL;
+ }
+
+ if ( iscritical ) {
+ *iscritical = 0;
+ }
+
+ for ( i = 0; controls != NULL && controls[i] != NULL; i++ ) {
+ if ( strcmp( controls[i]->ldctl_oid, oid ) != 0 ) {
+ continue;
+ }
+
+ rc = 1;
+ if ( controls[i]->ldctl_value.bv_len != 0 ) {
+ if ( val ) {
+ *val = &controls[i]->ldctl_value;
+ }
+ }
+
+ if ( iscritical ) {
+ *iscritical = controls[i]->ldctl_iscritical;
+ }
+
+ break;
+ }
+
+ return rc;
+}
+
+static void
+slapControlMask2SlapiControlOp(slap_mask_t slap_mask,
+ unsigned long *slapi_mask)
+{
+ *slapi_mask = SLAPI_OPERATION_NONE;
+
+ if ( slap_mask & SLAP_CTRL_ABANDON )
+ *slapi_mask |= SLAPI_OPERATION_ABANDON;
+
+ if ( slap_mask & SLAP_CTRL_ADD )
+ *slapi_mask |= SLAPI_OPERATION_ADD;
+
+ if ( slap_mask & SLAP_CTRL_BIND )
+ *slapi_mask |= SLAPI_OPERATION_BIND;
+
+ if ( slap_mask & SLAP_CTRL_COMPARE )
+ *slapi_mask |= SLAPI_OPERATION_COMPARE;
+
+ if ( slap_mask & SLAP_CTRL_DELETE )
+ *slapi_mask |= SLAPI_OPERATION_DELETE;
+
+ if ( slap_mask & SLAP_CTRL_MODIFY )
+ *slapi_mask |= SLAPI_OPERATION_MODIFY;
+
+ if ( slap_mask & SLAP_CTRL_RENAME )
+ *slapi_mask |= SLAPI_OPERATION_MODDN;
+
+ if ( slap_mask & SLAP_CTRL_SEARCH )
+ *slapi_mask |= SLAPI_OPERATION_SEARCH;
+
+ if ( slap_mask & SLAP_CTRL_UNBIND )
+ *slapi_mask |= SLAPI_OPERATION_UNBIND;
+}
+
+static void
+slapiControlOp2SlapControlMask(unsigned long slapi_mask,
+ slap_mask_t *slap_mask)
+{
+ *slap_mask = 0;
+
+ if ( slapi_mask & SLAPI_OPERATION_BIND )
+ *slap_mask |= SLAP_CTRL_BIND;
+
+ if ( slapi_mask & SLAPI_OPERATION_UNBIND )
+ *slap_mask |= SLAP_CTRL_UNBIND;
+
+ if ( slapi_mask & SLAPI_OPERATION_SEARCH )
+ *slap_mask |= SLAP_CTRL_SEARCH;
+
+ if ( slapi_mask & SLAPI_OPERATION_MODIFY )
+ *slap_mask |= SLAP_CTRL_MODIFY;
+
+ if ( slapi_mask & SLAPI_OPERATION_ADD )
+ *slap_mask |= SLAP_CTRL_ADD;
+
+ if ( slapi_mask & SLAPI_OPERATION_DELETE )
+ *slap_mask |= SLAP_CTRL_DELETE;
+
+ if ( slapi_mask & SLAPI_OPERATION_MODDN )
+ *slap_mask |= SLAP_CTRL_RENAME;
+
+ if ( slapi_mask & SLAPI_OPERATION_COMPARE )
+ *slap_mask |= SLAP_CTRL_COMPARE;
+
+ if ( slapi_mask & SLAPI_OPERATION_ABANDON )
+ *slap_mask |= SLAP_CTRL_ABANDON;
+
+ *slap_mask |= SLAP_CTRL_GLOBAL;
+}
+
+static int
+slapi_int_parse_control(
+ Operation *op,
+ SlapReply *rs,
+ LDAPControl *ctrl )
+{
+ /* Plugins must deal with controls themselves. */
+
+ return LDAP_SUCCESS;
+}
+
+void
+slapi_register_supported_control(
+ char *controloid,
+ unsigned long controlops )
+{
+ slap_mask_t controlmask;
+
+ slapiControlOp2SlapControlMask( controlops, &controlmask );
+
+ register_supported_control( controloid, controlmask, NULL, slapi_int_parse_control, NULL );
+}
+
+int
+slapi_get_supported_controls(
+ char ***ctrloidsp,
+ unsigned long **ctrlopsp )
+{
+ int i, rc;
+
+ rc = get_supported_controls( ctrloidsp, (slap_mask_t **)ctrlopsp );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ for ( i = 0; (*ctrloidsp)[i] != NULL; i++ ) {
+ /* In place, naughty. */
+ slapControlMask2SlapiControlOp( (*ctrlopsp)[i], &((*ctrlopsp)[i]) );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+LDAPControl *
+slapi_dup_control( LDAPControl *ctrl )
+{
+ LDAPControl *ret;
+
+ ret = (LDAPControl *)slapi_ch_malloc( sizeof(*ret) );
+ ret->ldctl_oid = slapi_ch_strdup( ctrl->ldctl_oid );
+ ber_dupbv( &ret->ldctl_value, &ctrl->ldctl_value );
+ ret->ldctl_iscritical = ctrl->ldctl_iscritical;
+
+ return ret;
+}
+
+void
+slapi_register_supported_saslmechanism( char *mechanism )
+{
+ /* FIXME -- can not add saslmechanism to OpenLDAP dynamically */
+ slapi_log_error( SLAPI_LOG_FATAL, "slapi_register_supported_saslmechanism",
+ "OpenLDAP does not support dynamic registration of SASL mechanisms\n" );
+}
+
+char **
+slapi_get_supported_saslmechanisms( void )
+{
+ /* FIXME -- can not get the saslmechanism without a connection. */
+ slapi_log_error( SLAPI_LOG_FATAL, "slapi_get_supported_saslmechanisms",
+ "can not get the SASL mechanism list "
+ "without a connection\n" );
+ return NULL;
+}
+
+char **
+slapi_get_supported_extended_ops( void )
+{
+ int i, j, k;
+ char **ppExtOpOID = NULL;
+ int numExtOps = 0;
+
+ for ( i = 0; get_supported_extop( i ) != NULL; i++ ) {
+ ;
+ }
+
+ for ( j = 0; slapi_int_get_supported_extop( j ) != NULL; j++ ) {
+ ;
+ }
+
+ numExtOps = i + j;
+ if ( numExtOps == 0 ) {
+ return NULL;
+ }
+
+ ppExtOpOID = (char **)slapi_ch_malloc( (numExtOps + 1) * sizeof(char *) );
+ for ( k = 0; k < i; k++ ) {
+ struct berval *bv;
+
+ bv = get_supported_extop( k );
+ assert( bv != NULL );
+
+ ppExtOpOID[ k ] = bv->bv_val;
+ }
+
+ for ( ; k < j; k++ ) {
+ struct berval *bv;
+
+ bv = slapi_int_get_supported_extop( k );
+ assert( bv != NULL );
+
+ ppExtOpOID[ i + k ] = bv->bv_val;
+ }
+ ppExtOpOID[ i + k ] = NULL;
+
+ return ppExtOpOID;
+}
+
+void
+slapi_send_ldap_result(
+ Slapi_PBlock *pb,
+ int err,
+ char *matched,
+ char *text,
+ int nentries,
+ struct berval **urls )
+{
+ SlapReply *rs;
+
+ PBLOCK_ASSERT_OP( pb, 0 );
+
+ rs = pb->pb_rs;
+
+ rs->sr_err = err;
+ rs->sr_matched = matched;
+ rs->sr_text = text;
+ rs->sr_ref = NULL;
+
+ if ( err == LDAP_SASL_BIND_IN_PROGRESS ) {
+ send_ldap_sasl( pb->pb_op, rs );
+ } else if ( rs->sr_rspoid != NULL ) {
+ send_ldap_extended( pb->pb_op, rs );
+ } else {
+ if ( pb->pb_op->o_tag == LDAP_REQ_SEARCH )
+ rs->sr_nentries = nentries;
+ if ( urls != NULL )
+ bvptr2obj( urls, &rs->sr_ref, NULL );
+
+ send_ldap_result( pb->pb_op, rs );
+
+ if ( urls != NULL )
+ slapi_ch_free( (void **)&rs->sr_ref );
+ }
+}
+
+int
+slapi_send_ldap_search_entry(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ LDAPControl **ectrls,
+ char **attrs,
+ int attrsonly )
+{
+ SlapReply rs = { REP_SEARCH };
+ int i = 0, j = 0;
+ AttributeName *an = NULL;
+ const char *text;
+ int rc;
+
+ assert( pb->pb_op != NULL );
+
+ if ( attrs != NULL ) {
+ for ( i = 0; attrs[ i ] != NULL; i++ ) {
+ ; /* empty */
+ }
+ }
+
+ if ( i ) {
+ an = (AttributeName *) slapi_ch_calloc( i + 1, sizeof(AttributeName) );
+ for ( i = 0; attrs[i] != NULL; i++ ) {
+ an[j].an_name.bv_val = attrs[i];
+ an[j].an_name.bv_len = strlen( attrs[i] );
+ an[j].an_desc = NULL;
+ if ( slap_bv2ad( &an[j].an_name, &an[j].an_desc, &text ) == LDAP_SUCCESS) {
+ j++;
+ }
+ }
+ an[j].an_name.bv_len = 0;
+ an[j].an_name.bv_val = NULL;
+ }
+
+ rs.sr_err = LDAP_SUCCESS;
+ rs.sr_matched = NULL;
+ rs.sr_text = NULL;
+ rs.sr_ref = NULL;
+ rs.sr_ctrls = ectrls;
+ rs.sr_attrs = an;
+ rs.sr_operational_attrs = NULL;
+ rs.sr_entry = e;
+ rs.sr_v2ref = NULL;
+ rs.sr_flags = 0;
+
+ rc = send_search_entry( pb->pb_op, &rs );
+
+ slapi_ch_free( (void **)&an );
+
+ return rc;
+}
+
+int
+slapi_send_ldap_search_reference(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ struct berval **references,
+ LDAPControl **ectrls,
+ struct berval **v2refs
+ )
+{
+ SlapReply rs = { REP_SEARCHREF };
+ int rc;
+
+ rs.sr_err = LDAP_SUCCESS;
+ rs.sr_matched = NULL;
+ rs.sr_text = NULL;
+
+ rc = bvptr2obj( references, &rs.sr_ref, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ rs.sr_ctrls = ectrls;
+ rs.sr_attrs = NULL;
+ rs.sr_operational_attrs = NULL;
+ rs.sr_entry = e;
+
+ if ( v2refs != NULL ) {
+ rc = bvptr2obj( v2refs, &rs.sr_v2ref, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ slapi_ch_free( (void **)&rs.sr_ref );
+ return rc;
+ }
+ } else {
+ rs.sr_v2ref = NULL;
+ }
+
+ rc = send_search_reference( pb->pb_op, &rs );
+
+ slapi_ch_free( (void **)&rs.sr_ref );
+ slapi_ch_free( (void **)&rs.sr_v2ref );
+
+ return rc;
+}
+
+Slapi_Filter *
+slapi_str2filter( char *str )
+{
+ return str2filter( str );
+}
+
+void
+slapi_filter_free(
+ Slapi_Filter *f,
+ int recurse )
+{
+ filter_free( f );
+}
+
+Slapi_Filter *
+slapi_filter_dup( Slapi_Filter *filter )
+{
+ return filter_dup( filter, NULL );
+}
+
+int
+slapi_filter_get_choice( Slapi_Filter *f )
+{
+ int rc;
+
+ if ( f != NULL ) {
+ rc = f->f_choice;
+ } else {
+ rc = 0;
+ }
+
+ return rc;
+}
+
+int
+slapi_filter_get_ava(
+ Slapi_Filter *f,
+ char **type,
+ struct berval **bval )
+{
+ int ftype;
+ int rc = LDAP_SUCCESS;
+
+ assert( type != NULL );
+ assert( bval != NULL );
+
+ *type = NULL;
+ *bval = NULL;
+
+ ftype = f->f_choice;
+ if ( ftype == LDAP_FILTER_EQUALITY
+ || ftype == LDAP_FILTER_GE
+ || ftype == LDAP_FILTER_LE
+ || ftype == LDAP_FILTER_APPROX ) {
+ /*
+ * According to the SLAPI Reference Manual these are
+ * not duplicated.
+ */
+ *type = f->f_un.f_un_ava->aa_desc->ad_cname.bv_val;
+ *bval = &f->f_un.f_un_ava->aa_value;
+ } else { /* filter type not supported */
+ rc = -1;
+ }
+
+ return rc;
+}
+
+Slapi_Filter *
+slapi_filter_list_first( Slapi_Filter *f )
+{
+ int ftype;
+
+ if ( f == NULL ) {
+ return NULL;
+ }
+
+ ftype = f->f_choice;
+ if ( ftype == LDAP_FILTER_AND
+ || ftype == LDAP_FILTER_OR
+ || ftype == LDAP_FILTER_NOT ) {
+ return (Slapi_Filter *)f->f_list;
+ } else {
+ return NULL;
+ }
+}
+
+Slapi_Filter *
+slapi_filter_list_next(
+ Slapi_Filter *f,
+ Slapi_Filter *fprev )
+{
+ int ftype;
+
+ if ( f == NULL ) {
+ return NULL;
+ }
+
+ ftype = f->f_choice;
+ if ( ftype == LDAP_FILTER_AND
+ || ftype == LDAP_FILTER_OR
+ || ftype == LDAP_FILTER_NOT )
+ {
+ return fprev->f_next;
+ }
+
+ return NULL;
+}
+
+int
+slapi_filter_get_attribute_type( Slapi_Filter *f, char **type )
+{
+ if ( f == NULL ) {
+ return -1;
+ }
+
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_APPROX:
+ *type = f->f_av_desc->ad_cname.bv_val;
+ break;
+ case LDAP_FILTER_SUBSTRINGS:
+ *type = f->f_sub_desc->ad_cname.bv_val;
+ break;
+ case LDAP_FILTER_PRESENT:
+ *type = f->f_desc->ad_cname.bv_val;
+ break;
+ case LDAP_FILTER_EXT:
+ *type = f->f_mr_desc->ad_cname.bv_val;
+ break;
+ default:
+ /* Complex filters need not apply. */
+ *type = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+slapi_x_filter_set_attribute_type( Slapi_Filter *f, const char *type )
+{
+ AttributeDescription **adp, *ad = NULL;
+ const char *text;
+ int rc;
+
+ if ( f == NULL ) {
+ return -1;
+ }
+
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_APPROX:
+ adp = &f->f_av_desc;
+ break;
+ case LDAP_FILTER_SUBSTRINGS:
+ adp = &f->f_sub_desc;
+ break;
+ case LDAP_FILTER_PRESENT:
+ adp = &f->f_desc;
+ break;
+ case LDAP_FILTER_EXT:
+ adp = &f->f_mr_desc;
+ break;
+ default:
+ /* Complex filters need not apply. */
+ return -1;
+ }
+
+ rc = slap_str2ad( type, &ad, &text );
+ if ( rc == LDAP_SUCCESS )
+ *adp = ad;
+
+ return ( rc == LDAP_SUCCESS ) ? 0 : -1;
+}
+
+int
+slapi_filter_get_subfilt( Slapi_Filter *f, char **type, char **initial,
+ char ***any, char **final )
+{
+ int i;
+
+ if ( f->f_choice != LDAP_FILTER_SUBSTRINGS ) {
+ return -1;
+ }
+
+ /*
+ * The caller shouldn't free but we can't return an
+ * array of char *s from an array of bervals without
+ * allocating memory, so we may as well be consistent.
+ * XXX
+ */
+ *type = f->f_sub_desc->ad_cname.bv_val;
+ *initial = f->f_sub_initial.bv_val ? slapi_ch_strdup(f->f_sub_initial.bv_val) : NULL;
+ if ( f->f_sub_any != NULL ) {
+ for ( i = 0; f->f_sub_any[i].bv_val != NULL; i++ )
+ ;
+ *any = (char **)slapi_ch_malloc( (i + 1) * sizeof(char *) );
+ for ( i = 0; f->f_sub_any[i].bv_val != NULL; i++ ) {
+ (*any)[i] = slapi_ch_strdup(f->f_sub_any[i].bv_val);
+ }
+ (*any)[i] = NULL;
+ } else {
+ *any = NULL;
+ }
+ *final = f->f_sub_final.bv_val ? slapi_ch_strdup(f->f_sub_final.bv_val) : NULL;
+
+ return 0;
+}
+
+Slapi_Filter *
+slapi_filter_join( int ftype, Slapi_Filter *f1, Slapi_Filter *f2 )
+{
+ Slapi_Filter *f = NULL;
+
+ if ( ftype == LDAP_FILTER_AND ||
+ ftype == LDAP_FILTER_OR ||
+ ftype == LDAP_FILTER_NOT )
+ {
+ f = (Slapi_Filter *)slapi_ch_malloc( sizeof(*f) );
+ f->f_choice = ftype;
+ f->f_list = f1;
+ f->f_list->f_next = f2;
+ f->f_next = NULL;
+ }
+
+ return f;
+}
+
+int
+slapi_x_filter_append( int ftype,
+ Slapi_Filter **pContainingFilter, /* NULL on first call */
+ Slapi_Filter **pNextFilter,
+ Slapi_Filter *filterToAppend )
+{
+ if ( ftype == LDAP_FILTER_AND ||
+ ftype == LDAP_FILTER_OR ||
+ ftype == LDAP_FILTER_NOT )
+ {
+ if ( *pContainingFilter == NULL ) {
+ *pContainingFilter = (Slapi_Filter *)slapi_ch_malloc( sizeof(Slapi_Filter) );
+ (*pContainingFilter)->f_choice = ftype;
+ (*pContainingFilter)->f_list = filterToAppend;
+ (*pContainingFilter)->f_next = NULL;
+ } else {
+ if ( (*pContainingFilter)->f_choice != ftype ) {
+ /* Sanity check */
+ return -1;
+ }
+ (*pNextFilter)->f_next = filterToAppend;
+ }
+ *pNextFilter = filterToAppend;
+
+ return 0;
+ }
+ return -1;
+}
+
+int
+slapi_filter_test( Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Filter *f,
+ int verify_access )
+{
+ Operation *op;
+ int rc;
+
+ if ( f == NULL ) {
+ /* spec says return zero if no filter. */
+ return 0;
+ }
+
+ if ( verify_access ) {
+ op = pb->pb_op;
+ if ( op == NULL )
+ return LDAP_PARAM_ERROR;
+ } else {
+ op = NULL;
+ }
+
+ /*
+ * According to acl.c it is safe to call test_filter() with
+ * NULL arguments...
+ */
+ rc = test_filter( op, e, f );
+ switch (rc) {
+ case LDAP_COMPARE_TRUE:
+ rc = 0;
+ break;
+ case LDAP_COMPARE_FALSE:
+ break;
+ case SLAPD_COMPARE_UNDEFINED:
+ rc = LDAP_OTHER;
+ break;
+ case LDAP_PROTOCOL_ERROR:
+ /* filter type unknown: spec says return -1 */
+ rc = -1;
+ break;
+ }
+
+ return rc;
+}
+
+int
+slapi_filter_test_simple( Slapi_Entry *e, Slapi_Filter *f)
+{
+ return slapi_filter_test( NULL, e, f, 0 );
+}
+
+int
+slapi_filter_apply( Slapi_Filter *f, FILTER_APPLY_FN fn, void *arg, int *error_code )
+{
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_NOT:
+ case LDAP_FILTER_OR: {
+ int rc;
+
+ /*
+ * FIXME: altering f; should we use a temporary?
+ */
+ for ( f = f->f_list; f != NULL; f = f->f_next ) {
+ rc = slapi_filter_apply( f, fn, arg, error_code );
+ if ( rc != 0 ) {
+ return rc;
+ }
+ if ( *error_code == SLAPI_FILTER_SCAN_NOMORE ) {
+ break;
+ }
+ }
+ break;
+ }
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_SUBSTRINGS:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_PRESENT:
+ case LDAP_FILTER_APPROX:
+ case LDAP_FILTER_EXT:
+ *error_code = fn( f, arg );
+ break;
+ default:
+ *error_code = SLAPI_FILTER_UNKNOWN_FILTER_TYPE;
+ }
+
+ if ( *error_code == SLAPI_FILTER_SCAN_NOMORE ||
+ *error_code == SLAPI_FILTER_SCAN_CONTINUE ) {
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+slapi_pw_find(
+ struct berval **vals,
+ struct berval *v )
+{
+ int i;
+
+ if( ( vals == NULL ) || ( v == NULL ) )
+ return 1;
+
+ for ( i = 0; vals[i] != NULL; i++ ) {
+ if ( !lutil_passwd( vals[i], v, NULL, NULL ) )
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Get connected client IP address.
+ *
+ * The user must free the returned client IP after its use.
+ * Compatible with IBM Tivoli call.
+ *
+ * Errors:
+ * * LDAP_PARAM_ERROR - If the pb parameter is null.
+ * * LDAP_OPERATIONS_ERROR - If the API encounters error processing the request.
+ * * LDAP_NO_MEMORY - Failed to allocate required memory.
+ */
+int
+slapi_get_client_ip(Slapi_PBlock *pb, char **clientIP)
+{
+ char *s = NULL;
+
+ if(pb == NULL || pb->pb_conn == NULL) return(LDAP_PARAM_ERROR);
+ if((s = (char *) slapi_ch_malloc(pb->pb_conn->c_peer_name.bv_len + 1)) == NULL) {
+ return(LDAP_NO_MEMORY);
+ }
+
+ memcpy(s, pb->pb_conn->c_peer_name.bv_val, pb->pb_conn->c_peer_name.bv_len);
+
+ s[pb->pb_conn->c_peer_name.bv_len] = 0;
+
+ *clientIP = s;
+
+ return(LDAP_SUCCESS);
+}
+
+/* Free previously allocated client IP address. */
+void
+slapi_free_client_ip(char **clientIP)
+{
+ slapi_ch_free((void **) clientIP);
+}
+
+#define MAX_HOSTNAME 512
+
+char *
+slapi_get_hostname( void )
+{
+ char *hn = NULL;
+ static int been_here = 0;
+ static char *static_hn = NULL;
+
+ ldap_pvt_thread_mutex_lock( &slapi_hn_mutex );
+ if ( !been_here ) {
+ static_hn = (char *)slapi_ch_malloc( MAX_HOSTNAME );
+ if ( static_hn == NULL) {
+ slapi_log_error( SLAPI_LOG_FATAL, "slapi_get_hostname",
+ "Cannot allocate memory for hostname\n" );
+ static_hn = NULL;
+ ldap_pvt_thread_mutex_unlock( &slapi_hn_mutex );
+
+ return hn;
+
+ } else {
+ if ( gethostname( static_hn, MAX_HOSTNAME ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL,
+ "SLAPI",
+ "can't get hostname\n" );
+ slapi_ch_free( (void **)&static_hn );
+ static_hn = NULL;
+ ldap_pvt_thread_mutex_unlock( &slapi_hn_mutex );
+
+ return hn;
+
+ } else {
+ been_here = 1;
+ }
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &slapi_hn_mutex );
+
+ hn = ch_strdup( static_hn );
+
+ return hn;
+}
+
+/*
+ * FIXME: this should go in an appropriate header ...
+ */
+extern int slapi_int_log_error( int level, char *subsystem, char *fmt, va_list arglist );
+
+int
+slapi_log_error(
+ int severity,
+ char *subsystem,
+ char *fmt,
+ ... )
+{
+ int rc = LDAP_SUCCESS;
+ va_list arglist;
+
+ va_start( arglist, fmt );
+ rc = slapi_int_log_error( severity, subsystem, fmt, arglist );
+ va_end( arglist );
+
+ return rc;
+}
+
+
+unsigned long
+slapi_timer_current_time( void )
+{
+ static int first_time = 1;
+#if !defined (_WIN32)
+ struct timeval now;
+ unsigned long ret;
+
+ ldap_pvt_thread_mutex_lock( &slapi_time_mutex );
+ if (first_time) {
+ first_time = 0;
+ gettimeofday( &base_time, NULL );
+ }
+ gettimeofday( &now, NULL );
+ ret = ( now.tv_sec - base_time.tv_sec ) * 1000000 +
+ (now.tv_usec - base_time.tv_usec);
+ ldap_pvt_thread_mutex_unlock( &slapi_time_mutex );
+
+ return ret;
+
+ /*
+ * Ain't it better?
+ return (slap_get_time() - starttime) * 1000000;
+ */
+#else /* _WIN32 */
+ LARGE_INTEGER now;
+
+ if ( first_time ) {
+ first_time = 0;
+ performance_counter_present = QueryPerformanceCounter( &base_time );
+ QueryPerformanceFrequency( &performance_freq );
+ }
+
+ if ( !performance_counter_present )
+ return 0;
+
+ QueryPerformanceCounter( &now );
+ return (1000000*(now.QuadPart-base_time.QuadPart))/performance_freq.QuadPart;
+#endif /* _WIN32 */
+}
+
+/*
+ * FIXME ?
+ */
+unsigned long
+slapi_timer_get_time( char *label )
+{
+ unsigned long start = slapi_timer_current_time();
+ printf("%10ld %10d usec %s\n", start, 0, label);
+ return start;
+}
+
+/*
+ * FIXME ?
+ */
+void
+slapi_timer_elapsed_time(
+ char *label,
+ unsigned long start )
+{
+ unsigned long stop = slapi_timer_current_time();
+ printf ("%10ld %10ld usec %s\n", stop, stop - start, label);
+}
+
+void
+slapi_free_search_results_internal( Slapi_PBlock *pb )
+{
+ Slapi_Entry **entries;
+ int k = 0, nEnt = 0;
+
+ slapi_pblock_get( pb, SLAPI_NENTRIES, &nEnt );
+ slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries );
+ if ( nEnt == 0 || entries == NULL ) {
+ return;
+ }
+
+ for ( k = 0; k < nEnt; k++ ) {
+ slapi_entry_free( entries[k] );
+ entries[k] = NULL;
+ }
+
+ slapi_ch_free( (void **)&entries );
+}
+
+int slapi_is_connection_ssl( Slapi_PBlock *pb, int *isSSL )
+{
+ if ( pb == NULL )
+ return LDAP_PARAM_ERROR;
+
+ if ( pb->pb_conn == NULL )
+ return LDAP_PARAM_ERROR;
+
+#ifdef HAVE_TLS
+ *isSSL = pb->pb_conn->c_is_tls;
+#else
+ *isSSL = 0;
+#endif
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * DS 5.x compatibility API follow
+ */
+
+int slapi_attr_get_flags( const Slapi_Attr *attr, unsigned long *flags )
+{
+ AttributeType *at;
+
+ if ( attr == NULL )
+ return LDAP_PARAM_ERROR;
+
+ at = attr->a_desc->ad_type;
+
+ *flags = SLAPI_ATTR_FLAG_STD_ATTR;
+
+ if ( is_at_single_value( at ) )
+ *flags |= SLAPI_ATTR_FLAG_SINGLE;
+ if ( is_at_operational( at ) )
+ *flags |= SLAPI_ATTR_FLAG_OPATTR;
+ if ( is_at_obsolete( at ) )
+ *flags |= SLAPI_ATTR_FLAG_OBSOLETE;
+ if ( is_at_collective( at ) )
+ *flags |= SLAPI_ATTR_FLAG_COLLECTIVE;
+ if ( is_at_no_user_mod( at ) )
+ *flags |= SLAPI_ATTR_FLAG_NOUSERMOD;
+
+ return LDAP_SUCCESS;
+}
+
+int slapi_attr_flag_is_set( const Slapi_Attr *attr, unsigned long flag )
+{
+ unsigned long flags;
+
+ if ( slapi_attr_get_flags( attr, &flags ) != 0 )
+ return 0;
+ return (flags & flag) ? 1 : 0;
+}
+
+Slapi_Attr *slapi_attr_new( void )
+{
+ Attribute *ad;
+
+ ad = (Attribute *)slapi_ch_calloc( 1, sizeof(*ad) );
+
+ return ad;
+}
+
+Slapi_Attr *slapi_attr_init( Slapi_Attr *a, const char *type )
+{
+ const char *text;
+ AttributeDescription *ad = NULL;
+
+ if( slap_str2ad( type, &ad, &text ) != LDAP_SUCCESS ) {
+ return NULL;
+ }
+
+ a->a_desc = ad;
+ a->a_vals = NULL;
+ a->a_nvals = NULL;
+ a->a_next = NULL;
+ a->a_flags = 0;
+
+ return a;
+}
+
+void slapi_attr_free( Slapi_Attr **a )
+{
+ attr_free( *a );
+ *a = NULL;
+}
+
+Slapi_Attr *slapi_attr_dup( const Slapi_Attr *attr )
+{
+ return attr_dup( (Slapi_Attr *)attr );
+}
+
+int slapi_attr_add_value( Slapi_Attr *a, const Slapi_Value *v )
+{
+ struct berval nval;
+ struct berval *nvalp;
+ int rc;
+ AttributeDescription *desc = a->a_desc;
+
+ if ( desc->ad_type->sat_equality &&
+ desc->ad_type->sat_equality->smr_normalize ) {
+ rc = (*desc->ad_type->sat_equality->smr_normalize)(
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ desc->ad_type->sat_syntax,
+ desc->ad_type->sat_equality,
+ (Slapi_Value *)v, &nval, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+ nvalp = &nval;
+ } else {
+ nvalp = NULL;
+ }
+
+ rc = attr_valadd( a, (Slapi_Value *)v, nvalp, 1 );
+
+ if ( nvalp != NULL ) {
+ slapi_ch_free_string( &nval.bv_val );
+ }
+
+ return rc;
+}
+
+int slapi_attr_type2plugin( const char *type, void **pi )
+{
+ *pi = NULL;
+
+ return LDAP_OTHER;
+}
+
+int slapi_attr_get_type( const Slapi_Attr *attr, char **type )
+{
+ if ( attr == NULL ) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ *type = attr->a_desc->ad_cname.bv_val;
+
+ return LDAP_SUCCESS;
+}
+
+int slapi_attr_get_oid_copy( const Slapi_Attr *attr, char **oidp )
+{
+ if ( attr == NULL ) {
+ return LDAP_PARAM_ERROR;
+ }
+ *oidp = attr->a_desc->ad_type->sat_oid;
+
+ return LDAP_SUCCESS;
+}
+
+int slapi_attr_value_cmp( const Slapi_Attr *a, const struct berval *v1, const struct berval *v2 )
+{
+ MatchingRule *mr;
+ int ret;
+ int rc;
+ const char *text;
+
+ mr = a->a_desc->ad_type->sat_equality;
+ rc = value_match( &ret, a->a_desc, mr,
+ SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
+ (struct berval *)v1, (void *)v2, &text );
+ if ( rc != LDAP_SUCCESS )
+ return -1;
+
+ return ( ret == 0 ) ? 0 : -1;
+}
+
+int slapi_attr_value_find( const Slapi_Attr *a, struct berval *v )
+{
+ int rc;
+
+ if ( a ->a_vals == NULL ) {
+ return -1;
+ }
+ rc = attr_valfind( (Attribute *)a, SLAP_MR_VALUE_OF_ASSERTION_SYNTAX, v,
+ NULL, NULL );
+ return rc == 0 ? 0 : -1;
+}
+
+int slapi_attr_type_cmp( const char *t1, const char *t2, int opt )
+{
+ AttributeDescription *a1 = NULL;
+ AttributeDescription *a2 = NULL;
+ const char *text;
+ int ret;
+
+ if ( slap_str2ad( t1, &a1, &text ) != LDAP_SUCCESS ) {
+ return -1;
+ }
+
+ if ( slap_str2ad( t2, &a2, &text ) != LDAP_SUCCESS ) {
+ return 1;
+ }
+
+#define ad_base_cmp(l,r) (((l)->ad_type->sat_cname.bv_len < (r)->ad_type->sat_cname.bv_len) \
+ ? -1 : (((l)->ad_type->sat_cname.bv_len > (r)->ad_type->sat_cname.bv_len) \
+ ? 1 : strcasecmp((l)->ad_type->sat_cname.bv_val, (r)->ad_type->sat_cname.bv_val )))
+
+ switch ( opt ) {
+ case SLAPI_TYPE_CMP_EXACT:
+ ret = ad_cmp( a1, a2 );
+ break;
+ case SLAPI_TYPE_CMP_BASE:
+ ret = ad_base_cmp( a1, a2 );
+ break;
+ case SLAPI_TYPE_CMP_SUBTYPE:
+ ret = is_ad_subtype( a2, a2 );
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+
+ return ret;
+}
+
+int slapi_attr_types_equivalent( const char *t1, const char *t2 )
+{
+ return ( slapi_attr_type_cmp( t1, t2, SLAPI_TYPE_CMP_EXACT ) == 0 );
+}
+
+int slapi_attr_first_value( Slapi_Attr *a, Slapi_Value **v )
+{
+ return slapi_valueset_first_value( &a->a_vals, v );
+}
+
+int slapi_attr_next_value( Slapi_Attr *a, int hint, Slapi_Value **v )
+{
+ return slapi_valueset_next_value( &a->a_vals, hint, v );
+}
+
+int slapi_attr_get_numvalues( const Slapi_Attr *a, int *numValues )
+{
+ *numValues = slapi_valueset_count( &a->a_vals );
+
+ return 0;
+}
+
+int slapi_attr_get_valueset( const Slapi_Attr *a, Slapi_ValueSet **vs )
+{
+ *vs = &((Slapi_Attr *)a)->a_vals;
+
+ return 0;
+}
+
+int slapi_attr_get_bervals_copy( Slapi_Attr *a, struct berval ***vals )
+{
+ return slapi_attr_get_values( a, vals );
+}
+
+char *slapi_attr_syntax_normalize( const char *s )
+{
+ AttributeDescription *ad = NULL;
+ const char *text;
+
+ if ( slap_str2ad( s, &ad, &text ) != LDAP_SUCCESS ) {
+ return NULL;
+ }
+
+ return ad->ad_cname.bv_val;
+}
+
+Slapi_Value *slapi_value_new( void )
+{
+ struct berval *bv;
+
+ bv = (struct berval *)slapi_ch_malloc( sizeof(*bv) );
+
+ return bv;
+}
+
+Slapi_Value *slapi_value_new_berval(const struct berval *bval)
+{
+ return ber_dupbv( NULL, (struct berval *)bval );
+}
+
+Slapi_Value *slapi_value_new_value(const Slapi_Value *v)
+{
+ return slapi_value_new_berval( v );
+}
+
+Slapi_Value *slapi_value_new_string(const char *s)
+{
+ struct berval bv;
+
+ bv.bv_val = (char *)s;
+ bv.bv_len = strlen( s );
+
+ return slapi_value_new_berval( &bv );
+}
+
+Slapi_Value *slapi_value_init(Slapi_Value *val)
+{
+ val->bv_val = NULL;
+ val->bv_len = 0;
+
+ return val;
+}
+
+Slapi_Value *slapi_value_init_berval(Slapi_Value *v, struct berval *bval)
+{
+ return ber_dupbv( v, bval );
+}
+
+Slapi_Value *slapi_value_init_string(Slapi_Value *v, const char *s)
+{
+ v->bv_val = slapi_ch_strdup( s );
+ v->bv_len = strlen( s );
+
+ return v;
+}
+
+Slapi_Value *slapi_value_dup(const Slapi_Value *v)
+{
+ return slapi_value_new_value( v );
+}
+
+void slapi_value_free(Slapi_Value **value)
+{
+ if ( value == NULL ) {
+ return;
+ }
+
+ if ( (*value) != NULL ) {
+ slapi_ch_free( (void **)&(*value)->bv_val );
+ slapi_ch_free( (void **)value );
+ }
+}
+
+const struct berval *slapi_value_get_berval( const Slapi_Value *value )
+{
+ return value;
+}
+
+Slapi_Value *slapi_value_set_berval( Slapi_Value *value, const struct berval *bval )
+{
+ if ( value == NULL ) {
+ return NULL;
+ }
+ if ( value->bv_val != NULL ) {
+ slapi_ch_free( (void **)&value->bv_val );
+ }
+ slapi_value_init_berval( value, (struct berval *)bval );
+
+ return value;
+}
+
+Slapi_Value *slapi_value_set_value( Slapi_Value *value, const Slapi_Value *vfrom)
+{
+ if ( value == NULL ) {
+ return NULL;
+ }
+ return slapi_value_set_berval( value, vfrom );
+}
+
+Slapi_Value *slapi_value_set( Slapi_Value *value, void *val, unsigned long len)
+{
+ if ( value == NULL ) {
+ return NULL;
+ }
+ if ( value->bv_val != NULL ) {
+ slapi_ch_free( (void **)&value->bv_val );
+ }
+ value->bv_val = slapi_ch_malloc( len );
+ value->bv_len = len;
+ AC_MEMCPY( value->bv_val, val, len );
+
+ return value;
+}
+
+int slapi_value_set_string(Slapi_Value *value, const char *strVal)
+{
+ if ( value == NULL ) {
+ return -1;
+ }
+ slapi_value_set( value, (void *)strVal, strlen( strVal ) );
+ return 0;
+}
+
+int slapi_value_set_int(Slapi_Value *value, int intVal)
+{
+ char buf[64];
+
+ snprintf( buf, sizeof( buf ), "%d", intVal );
+
+ return slapi_value_set_string( value, buf );
+}
+
+const char *slapi_value_get_string(const Slapi_Value *value)
+{
+ if ( value == NULL ) return NULL;
+ if ( value->bv_val == NULL ) return NULL;
+ if ( !checkBVString( value ) ) return NULL;
+
+ return value->bv_val;
+}
+
+int slapi_value_get_int(const Slapi_Value *value)
+{
+ if ( value == NULL ) return 0;
+ if ( value->bv_val == NULL ) return 0;
+ if ( !checkBVString( value ) ) return 0;
+
+ return (int)strtol( value->bv_val, NULL, 10 );
+}
+
+unsigned int slapi_value_get_uint(const Slapi_Value *value)
+{
+ if ( value == NULL ) return 0;
+ if ( value->bv_val == NULL ) return 0;
+ if ( !checkBVString( value ) ) return 0;
+
+ return (unsigned int)strtoul( value->bv_val, NULL, 10 );
+}
+
+long slapi_value_get_long(const Slapi_Value *value)
+{
+ if ( value == NULL ) return 0;
+ if ( value->bv_val == NULL ) return 0;
+ if ( !checkBVString( value ) ) return 0;
+
+ return strtol( value->bv_val, NULL, 10 );
+}
+
+unsigned long slapi_value_get_ulong(const Slapi_Value *value)
+{
+ if ( value == NULL ) return 0;
+ if ( value->bv_val == NULL ) return 0;
+ if ( !checkBVString( value ) ) return 0;
+
+ return strtoul( value->bv_val, NULL, 10 );
+}
+
+size_t slapi_value_get_length(const Slapi_Value *value)
+{
+ if ( value == NULL )
+ return 0;
+
+ return (size_t) value->bv_len;
+}
+
+int slapi_value_compare(const Slapi_Attr *a, const Slapi_Value *v1, const Slapi_Value *v2)
+{
+ return slapi_attr_value_cmp( a, v1, v2 );
+}
+
+/* A ValueSet is a container for a BerVarray. */
+Slapi_ValueSet *slapi_valueset_new( void )
+{
+ Slapi_ValueSet *vs;
+
+ vs = (Slapi_ValueSet *)slapi_ch_malloc( sizeof( *vs ) );
+ *vs = NULL;
+
+ return vs;
+}
+
+void slapi_valueset_free(Slapi_ValueSet *vs)
+{
+ if ( vs != NULL ) {
+ BerVarray vp = *vs;
+
+ ber_bvarray_free( vp );
+ vp = NULL;
+
+ slapi_ch_free( (void **)&vp );
+ }
+}
+
+void slapi_valueset_init(Slapi_ValueSet *vs)
+{
+ if ( vs != NULL && *vs == NULL ) {
+ *vs = (Slapi_ValueSet)slapi_ch_calloc( 1, sizeof(struct berval) );
+ (*vs)->bv_val = NULL;
+ (*vs)->bv_len = 0;
+ }
+}
+
+void slapi_valueset_done(Slapi_ValueSet *vs)
+{
+ BerVarray vp;
+
+ if ( vs == NULL )
+ return;
+
+ for ( vp = *vs; vp->bv_val != NULL; vp++ ) {
+ vp->bv_len = 0;
+ slapi_ch_free( (void **)&vp->bv_val );
+ }
+ /* but don't free *vs or vs */
+}
+
+void slapi_valueset_add_value(Slapi_ValueSet *vs, const Slapi_Value *addval)
+{
+ struct berval bv;
+
+ ber_dupbv( &bv, (Slapi_Value *)addval );
+ ber_bvarray_add( vs, &bv );
+}
+
+int slapi_valueset_first_value( Slapi_ValueSet *vs, Slapi_Value **v )
+{
+ return slapi_valueset_next_value( vs, 0, v );
+}
+
+int slapi_valueset_next_value( Slapi_ValueSet *vs, int index, Slapi_Value **v)
+{
+ int i;
+ BerVarray vp;
+
+ if ( vs == NULL )
+ return -1;
+
+ vp = *vs;
+
+ for ( i = 0; vp[i].bv_val != NULL; i++ ) {
+ if ( i == index ) {
+ *v = &vp[i];
+ return index + 1;
+ }
+ }
+
+ return -1;
+}
+
+int slapi_valueset_count( const Slapi_ValueSet *vs )
+{
+ int i;
+ BerVarray vp;
+
+ if ( vs == NULL )
+ return 0;
+
+ vp = *vs;
+
+ if ( vp == NULL )
+ return 0;
+
+ for ( i = 0; vp[i].bv_val != NULL; i++ )
+ ;
+
+ return i;
+
+}
+
+void slapi_valueset_set_valueset(Slapi_ValueSet *vs1, const Slapi_ValueSet *vs2)
+{
+ BerVarray vp;
+
+ for ( vp = *vs2; vp->bv_val != NULL; vp++ ) {
+ slapi_valueset_add_value( vs1, vp );
+ }
+}
+
+int slapi_access_allowed( Slapi_PBlock *pb, Slapi_Entry *e, char *attr,
+ struct berval *val, int access )
+{
+ int rc;
+ slap_access_t slap_access;
+ AttributeDescription *ad = NULL;
+ const char *text;
+
+ rc = slap_str2ad( attr, &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ /*
+ * Whilst the SLAPI access types are arranged as a bitmask, the
+ * documentation indicates that they are to be used separately.
+ */
+ switch ( access & SLAPI_ACL_ALL ) {
+ case SLAPI_ACL_COMPARE:
+ slap_access = ACL_COMPARE;
+ break;
+ case SLAPI_ACL_SEARCH:
+ slap_access = ACL_SEARCH;
+ break;
+ case SLAPI_ACL_READ:
+ slap_access = ACL_READ;
+ break;
+ case SLAPI_ACL_WRITE:
+ slap_access = ACL_WRITE;
+ break;
+ case SLAPI_ACL_DELETE:
+ slap_access = ACL_WDEL;
+ break;
+ case SLAPI_ACL_ADD:
+ slap_access = ACL_WADD;
+ break;
+ case SLAPI_ACL_SELF: /* not documented */
+ case SLAPI_ACL_PROXY: /* not documented */
+ default:
+ return LDAP_INSUFFICIENT_ACCESS;
+ break;
+ }
+
+ assert( pb->pb_op != NULL );
+
+ if ( access_allowed( pb->pb_op, e, ad, val, slap_access, NULL ) ) {
+ return LDAP_SUCCESS;
+ }
+
+ return LDAP_INSUFFICIENT_ACCESS;
+}
+
+int slapi_acl_check_mods(Slapi_PBlock *pb, Slapi_Entry *e, LDAPMod **mods, char **errbuf)
+{
+ int rc = LDAP_SUCCESS;
+ Modifications *ml;
+
+ if ( pb == NULL || pb->pb_op == NULL )
+ return LDAP_PARAM_ERROR;
+
+ ml = slapi_int_ldapmods2modifications( pb->pb_op, mods );
+ if ( ml == NULL ) {
+ return LDAP_OTHER;
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ rc = acl_check_modlist( pb->pb_op, e, ml ) ? LDAP_SUCCESS : LDAP_INSUFFICIENT_ACCESS;
+ }
+
+ slap_mods_free( ml, 1 );
+
+ return rc;
+}
+
+/*
+ * Synthesise an LDAPMod array from a Modifications list to pass
+ * to SLAPI.
+ */
+LDAPMod **slapi_int_modifications2ldapmods( Modifications *modlist )
+{
+ Modifications *ml;
+ LDAPMod **mods, *modp;
+ int i, j;
+
+ for( i = 0, ml = modlist; ml != NULL; i++, ml = ml->sml_next )
+ ;
+
+ mods = (LDAPMod **)slapi_ch_malloc( (i + 1) * sizeof(LDAPMod *) );
+
+ for( i = 0, ml = modlist; ml != NULL; ml = ml->sml_next ) {
+ mods[i] = (LDAPMod *)slapi_ch_malloc( sizeof(LDAPMod) );
+ modp = mods[i];
+ modp->mod_op = ml->sml_op | LDAP_MOD_BVALUES;
+ if ( BER_BVISNULL( &ml->sml_type ) ) {
+ /* may happen for internally generated mods */
+ assert( ml->sml_desc != NULL );
+ modp->mod_type = slapi_ch_strdup( ml->sml_desc->ad_cname.bv_val );
+ } else {
+ modp->mod_type = slapi_ch_strdup( ml->sml_type.bv_val );
+ }
+
+ if ( ml->sml_values != NULL ) {
+ for( j = 0; ml->sml_values[j].bv_val != NULL; j++ )
+ ;
+ modp->mod_bvalues = (struct berval **)slapi_ch_malloc( (j + 1) *
+ sizeof(struct berval *) );
+ for( j = 0; ml->sml_values[j].bv_val != NULL; j++ ) {
+ modp->mod_bvalues[j] = (struct berval *)slapi_ch_malloc(
+ sizeof(struct berval) );
+ ber_dupbv( modp->mod_bvalues[j], &ml->sml_values[j] );
+ }
+ modp->mod_bvalues[j] = NULL;
+ } else {
+ modp->mod_bvalues = NULL;
+ }
+ i++;
+ }
+
+ mods[i] = NULL;
+
+ return mods;
+}
+
+/*
+ * Convert a potentially modified array of LDAPMods back to a
+ * Modification list. Unfortunately the values need to be
+ * duplicated because slap_mods_check() will try to free them
+ * before prettying (and we can't easily get out of calling
+ * slap_mods_check() because we need normalized values).
+ */
+Modifications *slapi_int_ldapmods2modifications ( Operation *op, LDAPMod **mods )
+{
+ Modifications *modlist = NULL, **modtail;
+ LDAPMod **modp;
+ char textbuf[SLAP_TEXT_BUFLEN];
+ const char *text;
+
+ if ( mods == NULL ) {
+ return NULL;
+ }
+
+ modtail = &modlist;
+
+ for ( modp = mods; *modp != NULL; modp++ ) {
+ Modifications *mod;
+ LDAPMod *lmod = *modp;
+ int i;
+ const char *text;
+ AttributeDescription *ad = NULL;
+
+ if ( slap_str2ad( lmod->mod_type, &ad, &text ) != LDAP_SUCCESS ) {
+ continue;
+ }
+
+ mod = (Modifications *) slapi_ch_malloc( sizeof(Modifications) );
+ mod->sml_op = lmod->mod_op & ~(LDAP_MOD_BVALUES);
+ mod->sml_flags = 0;
+ mod->sml_type = ad->ad_cname;
+ mod->sml_desc = ad;
+ mod->sml_next = NULL;
+
+ i = 0;
+ if ( lmod->mod_op & LDAP_MOD_BVALUES ) {
+ if ( lmod->mod_bvalues != NULL ) {
+ while ( lmod->mod_bvalues[i] != NULL )
+ i++;
+ }
+ } else {
+ if ( lmod->mod_values != NULL ) {
+ while ( lmod->mod_values[i] != NULL )
+ i++;
+ }
+ }
+ mod->sml_numvals = i;
+
+ if ( i == 0 ) {
+ mod->sml_values = NULL;
+ } else {
+ mod->sml_values = (BerVarray) slapi_ch_malloc( (i + 1) * sizeof(struct berval) );
+
+ /* NB: This implicitly trusts a plugin to return valid modifications. */
+ if ( lmod->mod_op & LDAP_MOD_BVALUES ) {
+ for ( i = 0; lmod->mod_bvalues[i] != NULL; i++ ) {
+ ber_dupbv( &mod->sml_values[i], lmod->mod_bvalues[i] );
+ }
+ } else {
+ for ( i = 0; lmod->mod_values[i] != NULL; i++ ) {
+ mod->sml_values[i].bv_val = slapi_ch_strdup( lmod->mod_values[i] );
+ mod->sml_values[i].bv_len = strlen( lmod->mod_values[i] );
+ }
+ }
+ mod->sml_values[i].bv_val = NULL;
+ mod->sml_values[i].bv_len = 0;
+ }
+ mod->sml_nvalues = NULL;
+
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ }
+
+ if ( slap_mods_check( op, modlist, &text, textbuf, sizeof( textbuf ), NULL ) != LDAP_SUCCESS ) {
+ slap_mods_free( modlist, 1 );
+ modlist = NULL;
+ }
+
+ return modlist;
+}
+
+/*
+ * Sun ONE DS 5.x computed attribute support. Computed attributes
+ * allow for dynamically generated operational attributes, a very
+ * useful thing indeed.
+ */
+
+/*
+ * For some reason Sun don't use the normal plugin mechanism
+ * registration path to register an "evaluator" function (an
+ * "evaluator" is responsible for adding computed attributes;
+ * the nomenclature is somewhat confusing).
+ *
+ * As such slapi_compute_add_evaluator() registers the
+ * function directly.
+ */
+int slapi_compute_add_evaluator(slapi_compute_callback_t function)
+{
+ Slapi_PBlock *pPlugin = NULL;
+ int rc;
+ int type = SLAPI_PLUGIN_OBJECT;
+
+ pPlugin = slapi_pblock_new();
+ if ( pPlugin == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto done;
+ }
+
+ rc = slapi_pblock_set( pPlugin, SLAPI_PLUGIN_TYPE, (void *)&type );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ rc = slapi_pblock_set( pPlugin, SLAPI_PLUGIN_COMPUTE_EVALUATOR_FN, (void *)function );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ rc = slapi_int_register_plugin( frontendDB, pPlugin );
+ if ( rc != 0 ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+done:
+ if ( rc != LDAP_SUCCESS ) {
+ if ( pPlugin != NULL ) {
+ slapi_pblock_destroy( pPlugin );
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * See notes above regarding slapi_compute_add_evaluator().
+ */
+int slapi_compute_add_search_rewriter(slapi_search_rewrite_callback_t function)
+{
+ Slapi_PBlock *pPlugin = NULL;
+ int rc;
+ int type = SLAPI_PLUGIN_OBJECT;
+
+ pPlugin = slapi_pblock_new();
+ if ( pPlugin == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto done;
+ }
+
+ rc = slapi_pblock_set( pPlugin, SLAPI_PLUGIN_TYPE, (void *)&type );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ rc = slapi_pblock_set( pPlugin, SLAPI_PLUGIN_COMPUTE_SEARCH_REWRITER_FN, (void *)function );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ rc = slapi_int_register_plugin( frontendDB, pPlugin );
+ if ( rc != 0 ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+done:
+ if ( rc != LDAP_SUCCESS ) {
+ if ( pPlugin != NULL ) {
+ slapi_pblock_destroy( pPlugin );
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Call compute evaluators
+ */
+int compute_evaluator(computed_attr_context *c, char *type, Slapi_Entry *e, slapi_compute_output_t outputfn)
+{
+ int rc = 0;
+ slapi_compute_callback_t *pGetPlugin, *tmpPlugin;
+
+ rc = slapi_int_get_plugins( frontendDB, SLAPI_PLUGIN_COMPUTE_EVALUATOR_FN, (SLAPI_FUNC **)&tmpPlugin );
+ if ( rc != LDAP_SUCCESS || tmpPlugin == NULL ) {
+ /* Nothing to do; front-end should ignore. */
+ return 0;
+ }
+
+ for ( pGetPlugin = tmpPlugin; *pGetPlugin != NULL; pGetPlugin++ ) {
+ /*
+ * -1: no attribute matched requested type
+ * 0: one attribute matched
+ * >0: error happened
+ */
+ rc = (*pGetPlugin)( c, type, e, outputfn );
+ if ( rc > 0 ) {
+ break;
+ }
+ }
+
+ slapi_ch_free( (void **)&tmpPlugin );
+
+ return rc;
+}
+
+int
+compute_rewrite_search_filter( Slapi_PBlock *pb )
+{
+ if ( pb == NULL || pb->pb_op == NULL )
+ return LDAP_PARAM_ERROR;
+
+ return slapi_int_call_plugins( pb->pb_op->o_bd, SLAPI_PLUGIN_COMPUTE_SEARCH_REWRITER_FN, pb );
+}
+
+/*
+ * New API to provide the plugin with access to the search
+ * pblock. Have informed Sun DS team.
+ */
+int
+slapi_x_compute_get_pblock(computed_attr_context *c, Slapi_PBlock **pb)
+{
+ if ( c == NULL )
+ return -1;
+
+ if ( c->cac_pb == NULL )
+ return -1;
+
+ *pb = c->cac_pb;
+
+ return 0;
+}
+
+Slapi_Mutex *slapi_new_mutex( void )
+{
+ Slapi_Mutex *m;
+
+ m = (Slapi_Mutex *)slapi_ch_malloc( sizeof(*m) );
+ if ( ldap_pvt_thread_mutex_init( &m->mutex ) != 0 ) {
+ slapi_ch_free( (void **)&m );
+ return NULL;
+ }
+
+ return m;
+}
+
+void slapi_destroy_mutex( Slapi_Mutex *mutex )
+{
+ if ( mutex != NULL ) {
+ ldap_pvt_thread_mutex_destroy( &mutex->mutex );
+ slapi_ch_free( (void **)&mutex);
+ }
+}
+
+void slapi_lock_mutex( Slapi_Mutex *mutex )
+{
+ ldap_pvt_thread_mutex_lock( &mutex->mutex );
+}
+
+int slapi_unlock_mutex( Slapi_Mutex *mutex )
+{
+ return ldap_pvt_thread_mutex_unlock( &mutex->mutex );
+}
+
+Slapi_CondVar *slapi_new_condvar( Slapi_Mutex *mutex )
+{
+ Slapi_CondVar *cv;
+
+ if ( mutex == NULL ) {
+ return NULL;
+ }
+
+ cv = (Slapi_CondVar *)slapi_ch_malloc( sizeof(*cv) );
+ if ( ldap_pvt_thread_cond_init( &cv->cond ) != 0 ) {
+ slapi_ch_free( (void **)&cv );
+ return NULL;
+ }
+
+ cv->mutex = mutex->mutex;
+
+ return cv;
+}
+
+void slapi_destroy_condvar( Slapi_CondVar *cvar )
+{
+ if ( cvar != NULL ) {
+ ldap_pvt_thread_cond_destroy( &cvar->cond );
+ slapi_ch_free( (void **)&cvar );
+ }
+}
+
+int slapi_wait_condvar( Slapi_CondVar *cvar, struct timeval *timeout )
+{
+ if ( cvar == NULL ) {
+ return -1;
+ }
+
+ return ldap_pvt_thread_cond_wait( &cvar->cond, &cvar->mutex );
+}
+
+int slapi_notify_condvar( Slapi_CondVar *cvar, int notify_all )
+{
+ if ( cvar == NULL ) {
+ return -1;
+ }
+
+ if ( notify_all ) {
+ return ldap_pvt_thread_cond_broadcast( &cvar->cond );
+ }
+
+ return ldap_pvt_thread_cond_signal( &cvar->cond );
+}
+
+int slapi_int_access_allowed( Operation *op,
+ Entry *entry,
+ AttributeDescription *desc,
+ struct berval *val,
+ slap_access_t access,
+ AccessControlState *state )
+{
+ int rc, slap_access = 0;
+ slapi_acl_callback_t *pGetPlugin, *tmpPlugin;
+ Slapi_PBlock *pb;
+
+ pb = SLAPI_OPERATION_PBLOCK( op );
+ if ( pb == NULL ) {
+ /* internal operation */
+ return 1;
+ }
+
+ switch ( access ) {
+ case ACL_COMPARE:
+ slap_access |= SLAPI_ACL_COMPARE;
+ break;
+ case ACL_SEARCH:
+ slap_access |= SLAPI_ACL_SEARCH;
+ break;
+ case ACL_READ:
+ slap_access |= SLAPI_ACL_READ;
+ break;
+ case ACL_WRITE:
+ slap_access |= SLAPI_ACL_WRITE;
+ break;
+ case ACL_WDEL:
+ slap_access |= SLAPI_ACL_DELETE;
+ break;
+ case ACL_WADD:
+ slap_access |= SLAPI_ACL_ADD;
+ break;
+ default:
+ break;
+ }
+
+ rc = slapi_int_get_plugins( frontendDB, SLAPI_PLUGIN_ACL_ALLOW_ACCESS, (SLAPI_FUNC **)&tmpPlugin );
+ if ( rc != LDAP_SUCCESS || tmpPlugin == NULL ) {
+ /* nothing to do; allowed access */
+ return 1;
+ }
+
+ rc = 1; /* default allow policy */
+
+ for ( pGetPlugin = tmpPlugin; *pGetPlugin != NULL; pGetPlugin++ ) {
+ /*
+ * 0 access denied
+ * 1 access granted
+ */
+ rc = (*pGetPlugin)( pb, entry, desc->ad_cname.bv_val,
+ val, slap_access, (void *)state );
+ if ( rc == 0 ) {
+ break;
+ }
+ }
+
+ slapi_ch_free( (void **)&tmpPlugin );
+
+ return rc;
+}
+
+/*
+ * There is no documentation for this.
+ */
+int slapi_rdn2typeval( char *rdn, char **type, struct berval *bv )
+{
+ LDAPRDN lrdn;
+ LDAPAVA *ava;
+ int rc;
+ char *p;
+
+ *type = NULL;
+
+ bv->bv_len = 0;
+ bv->bv_val = NULL;
+
+ rc = ldap_str2rdn( rdn, &lrdn, &p, LDAP_DN_FORMAT_LDAPV3 );
+ if ( rc != LDAP_SUCCESS ) {
+ return -1;
+ }
+
+ if ( lrdn[1] != NULL ) {
+ return -1; /* not single valued */
+ }
+
+ ava = lrdn[0];
+
+ *type = slapi_ch_strdup( ava->la_attr.bv_val );
+ ber_dupbv( bv, &ava->la_value );
+
+ ldap_rdnfree(lrdn);
+
+ return 0;
+}
+
+char *slapi_dn_plus_rdn( const char *dn, const char *rdn )
+{
+ struct berval new_dn, parent_dn, newrdn;
+
+ new_dn.bv_val = NULL;
+
+ parent_dn.bv_val = (char *)dn;
+ parent_dn.bv_len = strlen( dn );
+
+ newrdn.bv_val = (char *)rdn;
+ newrdn.bv_len = strlen( rdn );
+
+ build_new_dn( &new_dn, &parent_dn, &newrdn, NULL );
+
+ return new_dn.bv_val;
+}
+
+int slapi_entry_schema_check( Slapi_PBlock *pb, Slapi_Entry *e )
+{
+ Backend *be_orig;
+ const char *text;
+ char textbuf[SLAP_TEXT_BUFLEN] = { '\0' };
+ size_t textlen = sizeof textbuf;
+ int rc = LDAP_SUCCESS;
+
+ PBLOCK_ASSERT_OP( pb, 0 );
+
+ be_orig = pb->pb_op->o_bd;
+
+ pb->pb_op->o_bd = select_backend( &e->e_nname, 0 );
+ if ( pb->pb_op->o_bd != NULL ) {
+ rc = entry_schema_check( pb->pb_op, e, NULL, 0, 0, NULL,
+ &text, textbuf, textlen );
+ }
+ pb->pb_op->o_bd = be_orig;
+
+ return ( rc == LDAP_SUCCESS ) ? 0 : 1;
+}
+
+int slapi_entry_rdn_values_present( const Slapi_Entry *e )
+{
+ LDAPDN dn;
+ int rc;
+ int i = 0, match = 0;
+
+ rc = ldap_bv2dn( &((Entry *)e)->e_name, &dn, LDAP_DN_FORMAT_LDAPV3 );
+ if ( rc != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ if ( dn[0] != NULL ) {
+ LDAPRDN rdn = dn[0];
+
+ for ( i = 0; rdn[i] != NULL; i++ ) {
+ LDAPAVA *ava = &rdn[0][i];
+ Slapi_Attr *a = NULL;
+
+ if ( slapi_entry_attr_find( (Slapi_Entry *)e, ava->la_attr.bv_val, &a ) == 0 &&
+ slapi_attr_value_find( a, &ava->la_value ) == 0 )
+ match++;
+ }
+ }
+
+ ldap_dnfree( dn );
+
+ return ( i == match );
+}
+
+int slapi_entry_add_rdn_values( Slapi_Entry *e )
+{
+ LDAPDN dn;
+ int i, rc;
+
+ rc = ldap_bv2dn( &e->e_name, &dn, LDAP_DN_FORMAT_LDAPV3 );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( dn[0] != NULL ) {
+ LDAPRDN rdn = dn[0];
+ struct berval *vals[2];
+
+ for ( i = 0; rdn[i] != NULL; i++ ) {
+ LDAPAVA *ava = &rdn[0][i];
+ Slapi_Attr *a = NULL;
+
+ if ( slapi_entry_attr_find( e, ava->la_attr.bv_val, &a ) == 0 &&
+ slapi_attr_value_find( a, &ava->la_value ) == 0 )
+ continue;
+
+ vals[0] = &ava->la_value;
+ vals[1] = NULL;
+
+ slapi_entry_attr_merge( e, ava->la_attr.bv_val, vals );
+ }
+ }
+
+ ldap_dnfree( dn );
+
+ return LDAP_SUCCESS;
+}
+
+const char *slapi_entry_get_uniqueid( const Slapi_Entry *e )
+{
+ Attribute *attr;
+
+ attr = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
+ if ( attr == NULL ) {
+ return NULL;
+ }
+
+ if ( attr->a_vals != NULL && attr->a_vals[0].bv_len != 0 ) {
+ return slapi_value_get_string( &attr->a_vals[0] );
+ }
+
+ return NULL;
+}
+
+void slapi_entry_set_uniqueid( Slapi_Entry *e, char *uniqueid )
+{
+ struct berval bv;
+
+ attr_delete ( &e->e_attrs, slap_schema.si_ad_entryUUID );
+
+ bv.bv_val = uniqueid;
+ bv.bv_len = strlen( uniqueid );
+ attr_merge_normalize_one( e, slap_schema.si_ad_entryUUID, &bv, NULL );
+}
+
+LDAP *slapi_ldap_init( char *ldaphost, int ldapport, int secure, int shared )
+{
+ LDAP *ld;
+ char *url;
+ size_t size;
+ int rc;
+
+ size = sizeof("ldap:///");
+ if ( secure ) {
+ size++;
+ }
+ size += strlen( ldaphost );
+ if ( ldapport != 0 ) {
+ size += 32;
+ }
+
+ url = slapi_ch_malloc( size );
+
+ if ( ldapport != 0 ) {
+ rc = snprintf( url, size, "ldap%s://%s:%d/", ( secure ? "s" : "" ), ldaphost, ldapport );
+ } else {
+ rc = snprintf( url, size, "ldap%s://%s/", ( secure ? "s" : "" ), ldaphost );
+ }
+
+ if ( rc > 0 && (size_t) rc < size ) {
+ rc = ldap_initialize( &ld, url );
+ } else {
+ ld = NULL;
+ }
+
+ slapi_ch_free_string( &url );
+
+ return ( rc == LDAP_SUCCESS ) ? ld : NULL;
+}
+
+void slapi_ldap_unbind( LDAP *ld )
+{
+ ldap_unbind_ext_s( ld, NULL, NULL );
+}
+
+int slapi_x_backend_get_flags( const Slapi_Backend *be, unsigned long *flags )
+{
+ if ( be == NULL )
+ return LDAP_PARAM_ERROR;
+
+ *flags = SLAP_DBFLAGS(be);
+
+ return LDAP_SUCCESS;
+}
+
+int
+slapi_int_count_controls( LDAPControl **ctrls )
+{
+ size_t i;
+
+ if ( ctrls == NULL )
+ return 0;
+
+ for ( i = 0; ctrls[i] != NULL; i++ )
+ ;
+
+ return i;
+}
+
+int
+slapi_op_abandoned( Slapi_PBlock *pb )
+{
+ if ( pb->pb_op == NULL )
+ return 0;
+
+ return ( pb->pb_op->o_abandon );
+}
+
+char *
+slapi_op_type_to_string(unsigned long type)
+{
+ char *str;
+
+ switch (type) {
+ case SLAPI_OPERATION_BIND:
+ str = "bind";
+ break;
+ case SLAPI_OPERATION_UNBIND:
+ str = "unbind";
+ break;
+ case SLAPI_OPERATION_SEARCH:
+ str = "search";
+ break;
+ case SLAPI_OPERATION_MODIFY:
+ str = "modify";
+ break;
+ case SLAPI_OPERATION_ADD:
+ str = "add";
+ break;
+ case SLAPI_OPERATION_DELETE:
+ str = "delete";
+ break;
+ case SLAPI_OPERATION_MODDN:
+ str = "modrdn";
+ break;
+ case SLAPI_OPERATION_COMPARE:
+ str = "compare";
+ break;
+ case SLAPI_OPERATION_ABANDON:
+ str = "abandon";
+ break;
+ case SLAPI_OPERATION_EXTENDED:
+ str = "extended";
+ break;
+ default:
+ str = "unknown operation type";
+ break;
+ }
+ return str;
+}
+
+unsigned long
+slapi_op_get_type(Slapi_Operation * op)
+{
+ unsigned long type;
+
+ switch ( op->o_tag ) {
+ case LDAP_REQ_BIND:
+ type = SLAPI_OPERATION_BIND;
+ break;
+ case LDAP_REQ_UNBIND:
+ type = SLAPI_OPERATION_UNBIND;
+ break;
+ case LDAP_REQ_SEARCH:
+ type = SLAPI_OPERATION_SEARCH;
+ break;
+ case LDAP_REQ_MODIFY:
+ type = SLAPI_OPERATION_MODIFY;
+ break;
+ case LDAP_REQ_ADD:
+ type = SLAPI_OPERATION_ADD;
+ break;
+ case LDAP_REQ_DELETE:
+ type = SLAPI_OPERATION_DELETE;
+ break;
+ case LDAP_REQ_MODRDN:
+ type = SLAPI_OPERATION_MODDN;
+ break;
+ case LDAP_REQ_COMPARE:
+ type = SLAPI_OPERATION_COMPARE;
+ break;
+ case LDAP_REQ_ABANDON:
+ type = SLAPI_OPERATION_ABANDON;
+ break;
+ case LDAP_REQ_EXTENDED:
+ type = SLAPI_OPERATION_EXTENDED;
+ break;
+ default:
+ type = SLAPI_OPERATION_NONE;
+ break;
+ }
+ return type;
+}
+
+void slapi_be_set_readonly( Slapi_Backend *be, int readonly )
+{
+ if ( be == NULL )
+ return;
+
+ if ( readonly )
+ be->be_restrictops |= SLAP_RESTRICT_OP_WRITES;
+ else
+ be->be_restrictops &= ~(SLAP_RESTRICT_OP_WRITES);
+}
+
+int slapi_be_get_readonly( Slapi_Backend *be )
+{
+ if ( be == NULL )
+ return 0;
+
+ return ( (be->be_restrictops & SLAP_RESTRICT_OP_WRITES) == SLAP_RESTRICT_OP_WRITES );
+}
+
+const char *slapi_x_be_get_updatedn( Slapi_Backend *be )
+{
+ if ( be == NULL )
+ return NULL;
+
+ return be->be_update_ndn.bv_val;
+}
+
+Slapi_Backend *slapi_be_select( const Slapi_DN *sdn )
+{
+ Slapi_Backend *be;
+
+ slapi_sdn_get_ndn( sdn );
+
+ be = select_backend( (struct berval *)&sdn->ndn, 0 );
+
+ return be;
+}
+
+#if 0
+void
+slapi_operation_set_flag(Slapi_Operation *op, unsigned long flag)
+{
+}
+
+void
+slapi_operation_clear_flag(Slapi_Operation *op, unsigned long flag)
+{
+}
+
+int
+slapi_operation_is_flag_set(Slapi_Operation *op, unsigned long flag)
+{
+}
+#endif
+
+#endif /* LDAP_SLAPI */
+
diff --git a/servers/slapd/slapindex.c b/servers/slapd/slapindex.c
new file mode 100644
index 0000000..8400e82
--- /dev/null
+++ b/servers/slapd/slapindex.c
@@ -0,0 +1,110 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1998-2003 Kurt D. Zeilenga.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Kurt Zeilenga for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/unistd.h>
+
+#include "slapcommon.h"
+
+int
+slapindex( int argc, char **argv )
+{
+ ID id;
+ int rc = EXIT_SUCCESS;
+ const char *progname = "slapindex";
+ AttributeDescription *ad, **adv = NULL;
+
+ slap_tool_init( progname, SLAPINDEX, argc, argv );
+
+ if( !be->be_entry_open ||
+ !be->be_entry_close ||
+ !( be->be_entry_first || be->be_entry_first_x ) ||
+ !be->be_entry_next ||
+ !be->be_entry_reindex )
+ {
+ fprintf( stderr, "%s: database doesn't support necessary operations.\n",
+ progname );
+ exit( EXIT_FAILURE );
+ }
+
+ argc -= optind;
+ if ( argc > 0 ) {
+ const char *text;
+ int i;
+
+ argv = &argv[optind];
+ adv = (AttributeDescription **)argv;
+
+ for (i = 0; i < argc; i++ ) {
+ ad = NULL;
+ rc = slap_str2ad( argv[i], &ad, &text );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "slap_str2ad(%s) failed %d (%s)\n",
+ argv[i], rc, ldap_err2string( rc ));
+ exit( EXIT_FAILURE );
+ }
+ adv[i] = ad;
+ }
+ }
+
+ if( be->be_entry_open( be, 0 ) != 0 ) {
+ fprintf( stderr, "%s: could not open database.\n",
+ progname );
+ exit( EXIT_FAILURE );
+ }
+
+ if ( be->be_entry_first ) {
+ id = be->be_entry_first( be );
+
+ } else {
+ assert( be->be_entry_first_x != NULL );
+ id = be->be_entry_first_x( be, NULL, LDAP_SCOPE_DEFAULT, NULL );
+ }
+
+ for ( ; id != NOID; id = be->be_entry_next( be ) ) {
+ int rtn;
+
+ if( verbose ) {
+ printf("indexing id=%08lx\n", (long) id );
+ }
+
+ rtn = be->be_entry_reindex( be, id, adv );
+
+ if( rtn != LDAP_SUCCESS ) {
+ rc = EXIT_FAILURE;
+ if( continuemode ) continue;
+ break;
+ }
+ }
+
+ (void) be->be_entry_close( be );
+
+ if ( slap_tool_destroy())
+ rc = EXIT_FAILURE;
+ return( rc );
+}
diff --git a/servers/slapd/slapmodify.c b/servers/slapd/slapmodify.c
new file mode 100644
index 0000000..d59eba6
--- /dev/null
+++ b/servers/slapd/slapmodify.c
@@ -0,0 +1,650 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1998-2003 Kurt D. Zeilenga.
+ * Portions Copyright 2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "ac/stdlib.h"
+
+#include "ac/ctype.h"
+#include "ac/string.h"
+#include "ac/socket.h"
+#include "ac/unistd.h"
+
+#include "lber.h"
+#include "ldif.h"
+#include "lutil.h"
+#include "lutil_meter.h"
+#include <sys/stat.h>
+
+#include "slapcommon.h"
+
+extern int slap_DN_strict; /* dn.c */
+
+static char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE ];
+
+int
+slapmodify( int argc, char **argv )
+{
+ char *buf = NULL;
+ const char *text;
+ char textbuf[SLAP_TEXT_BUFLEN] = { '\0' };
+ size_t textlen = sizeof textbuf;
+ const char *progname = "slapmodify";
+
+ struct berval csn;
+ unsigned long sid;
+ struct berval bvtext;
+ ID id;
+ OperationBuffer opbuf;
+ Operation *op;
+
+ int checkvals, ldifrc;
+ unsigned long lineno, nextline;
+ int lmax;
+ int rc = EXIT_SUCCESS;
+
+ int enable_meter = 0;
+ lutil_meter_t meter;
+ struct stat stat_buf;
+
+ /* default "000" */
+ csnsid = 0;
+
+ if ( isatty (2) ) enable_meter = 1;
+ slap_tool_init( progname, SLAPMODIFY, argc, argv );
+
+ memset( &opbuf, 0, sizeof(opbuf) );
+ op = &opbuf.ob_op;
+ op->o_hdr = &opbuf.ob_hdr;
+ op->o_bd = be;
+
+ if ( !be->be_entry_open ||
+ !be->be_entry_close ||
+ !be->be_entry_put ||
+ !be->be_dn2id_get ||
+ !be->be_entry_get ||
+ !be->be_entry_modify )
+ {
+ fprintf( stderr, "%s: database doesn't support necessary operations.\n",
+ progname );
+ if ( dryrun ) {
+ fprintf( stderr, "\t(dry) continuing...\n" );
+
+ } else {
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ checkvals = (slapMode & SLAP_TOOL_QUICK) ? 0 : 1;
+
+ lmax = 0;
+ nextline = 0;
+
+ /* enforce schema checking unless not disabled and allow unknown
+ * attributes otherwise */
+ if ( (slapMode & SLAP_TOOL_NO_SCHEMA_CHECK) == 0) {
+ SLAP_DBFLAGS(be) &= ~(SLAP_DBFLAG_NO_SCHEMA_CHECK);
+ } else {
+ slap_DN_strict = 0;
+ }
+
+ if( !dryrun && be->be_entry_open( be, 1 ) != 0 ) {
+ fprintf( stderr, "%s: could not open database.\n",
+ progname );
+ exit( EXIT_FAILURE );
+ }
+
+ (void)slap_tool_update_ctxcsn_init();
+
+ if ( enable_meter
+#ifdef LDAP_DEBUG
+ /* tools default to "none" */
+ && slap_debug == LDAP_DEBUG_NONE
+#endif
+ && !fstat ( fileno ( ldiffp->fp ), &stat_buf )
+ && S_ISREG(stat_buf.st_mode) ) {
+ enable_meter = !lutil_meter_open(
+ &meter,
+ &lutil_meter_text_display,
+ &lutil_meter_linear_estimator,
+ stat_buf.st_size);
+ } else {
+ enable_meter = 0;
+ }
+
+ /* nextline is the line number of the end of the current entry */
+ for( lineno=1; ( ldifrc = ldif_read_record( ldiffp, &nextline, &buf, &lmax )) > 0;
+ lineno=nextline+1 )
+ {
+ BackendDB *bd;
+ Entry *e_orig = NULL, *e = NULL;
+ struct berval rbuf;
+ LDIFRecord lr;
+ struct berval ndn = BER_BVNULL;
+ int n;
+ int is_oc = 0;
+ int local_rc;
+ int mod_err = 0;
+ char *request = "(unknown)";
+
+ ber_str2bv( buf, 0, 0, &rbuf );
+
+ if ( lineno < jumpline )
+ continue;
+
+ if ( enable_meter )
+ lutil_meter_update( &meter,
+ ftell( ldiffp->fp ),
+ 0);
+
+ /*
+ * Initialize text buffer
+ */
+ bvtext.bv_len = textlen;
+ bvtext.bv_val = textbuf;
+ bvtext.bv_val[0] = '\0';
+
+ local_rc = ldap_parse_ldif_record( &rbuf, lineno, &lr,
+ "slapmodify", LDIF_NO_CONTROLS );
+
+ if ( local_rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s: could not parse entry (line=%lu)\n",
+ progname, lineno );
+ rc = EXIT_FAILURE;
+ if( continuemode ) continue;
+ break;
+ }
+
+ switch ( lr.lr_op ) {
+ case LDAP_REQ_ADD:
+ request = "add";
+ break;
+
+ case LDAP_REQ_MODIFY:
+ request = "modify";
+ break;
+
+ case LDAP_REQ_DELETE:
+ if ( be->be_entry_delete )
+ {
+ request = "delete";
+ break;
+ }
+ /* backend does not support delete, fallthru */
+
+ case LDAP_REQ_MODRDN:
+ fprintf( stderr, "%s: request 0x%lx not supported (line=%lu)\n",
+ progname, (unsigned long)lr.lr_op, lineno );
+ rc = EXIT_FAILURE;
+ goto cleanup;
+
+ default:
+ /* record skipped e.g. version: or comment or something we don't handle yet */
+ goto cleanup;
+ }
+
+ local_rc = dnNormalize( 0, NULL, NULL, &lr.lr_dn, &ndn, NULL );
+ if ( local_rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s: DN=\"%s\" normalization failed (line=%lu)\n",
+ progname, lr.lr_dn.bv_val, lineno );
+ rc = EXIT_FAILURE;
+ goto cleanup;
+ }
+
+ /* make sure the DN is not empty */
+ if( BER_BVISEMPTY( &ndn ) &&
+ !BER_BVISEMPTY( be->be_nsuffix ))
+ {
+ fprintf( stderr, "%s: line %lu: "
+ "%s entry with empty dn=\"\"",
+ progname, lineno, request );
+ bd = select_backend( &ndn, nosubordinates );
+ if ( bd ) {
+ BackendDB *bdtmp;
+ int dbidx = 0;
+ LDAP_STAILQ_FOREACH( bdtmp, &backendDB, be_next ) {
+ if ( bdtmp == bd ) break;
+ dbidx++;
+ }
+
+ assert( bdtmp != NULL );
+
+ fprintf( stderr, "; did you mean to use database #%d (%s)?",
+ dbidx,
+ bd->be_suffix[0].bv_val );
+
+ }
+ fprintf( stderr, "\n" );
+ rc = EXIT_FAILURE;
+ goto cleanup;
+ }
+
+ /* check backend */
+ bd = select_backend( &ndn, nosubordinates );
+ if ( bd != be ) {
+ fprintf( stderr, "%s: line %lu: "
+ "database #%d (%s) not configured to hold \"%s\"",
+ progname, lineno,
+ dbnum,
+ be->be_suffix[0].bv_val,
+ lr.lr_dn.bv_val );
+ if ( bd ) {
+ BackendDB *bdtmp;
+ int dbidx = 0;
+ LDAP_STAILQ_FOREACH( bdtmp, &backendDB, be_next ) {
+ if ( bdtmp == bd ) break;
+ dbidx++;
+ }
+
+ assert( bdtmp != NULL );
+
+ fprintf( stderr, "; did you mean to use database #%d (%s)?",
+ dbidx,
+ bd->be_suffix[0].bv_val );
+
+ } else {
+ fprintf( stderr, "; no database configured for that naming context" );
+ }
+ fprintf( stderr, "\n" );
+ rc = EXIT_FAILURE;
+ goto cleanup;
+ }
+
+ /* get id and/or entry */
+ switch ( lr.lr_op ) {
+ case LDAP_REQ_ADD:
+ e = entry_alloc();
+ ber_dupbv( &e->e_name, &lr.lr_dn );
+ ber_dupbv( &e->e_nname, &ndn );
+ break;
+
+ //case LDAP_REQ_MODRDN:
+ case LDAP_REQ_DELETE:
+ case LDAP_REQ_MODIFY:
+ id = be->be_dn2id_get( be, &ndn );
+ rc = (id == NOID);
+ if ( rc == LDAP_SUCCESS && lr.lr_op != LDAP_REQ_DELETE ) {
+ e_orig = be->be_entry_get( be, id );
+ if ( e_orig )
+ e = entry_dup( e_orig );
+ rc = (e == NULL);
+ }
+ break;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s: no such entry \"%s\" in database (lineno=%lu)\n",
+ progname, ndn.bv_val, lineno );
+ rc = EXIT_FAILURE;
+ goto cleanup;
+ }
+
+ if ( lr.lrop_mods ) {
+ for ( n = 0; lr.lrop_mods && lr.lrop_mods[ n ] != NULL; n++ ) {
+ LDAPMod *mod = lr.lrop_mods[ n ];
+ Modification mods = { 0 };
+ unsigned i = 0;
+ int bin = (mod->mod_op & LDAP_MOD_BVALUES);
+ int pretty = 0;
+ int normalize = 0;
+
+ local_rc = slap_str2ad( mod->mod_type, &mods.sm_desc, &text );
+ /*
+ * Usually this would be a bad idea (way too dangerous, risks
+ * corrupting the DB), but ITS#7786 documents this as a last
+ * resort to fix cn=config and missing attributes are one of
+ * the possible issues we might encounter.
+ */
+ if ( local_rc == LDAP_UNDEFINED_TYPE &&
+ (slapMode & SLAP_TOOL_NO_SCHEMA_CHECK) ) {
+ local_rc = slap_str2undef_ad( mod->mod_type, &mods.sm_desc, &text, 0 );
+ }
+ if ( local_rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s: slap_str2ad(\"%s\") failed for entry \"%s\" (%d: %s, lineno=%lu)\n",
+ progname, mod->mod_type, lr.lr_dn.bv_val, local_rc, text, lineno );
+ rc = EXIT_FAILURE;
+ goto cleanup;
+ }
+
+ mods.sm_type = mods.sm_desc->ad_cname;
+
+ if ( mods.sm_desc->ad_type->sat_syntax->ssyn_pretty ) {
+ pretty = 1;
+
+ } else {
+ assert( mods.sm_desc->ad_type->sat_syntax->ssyn_validate != NULL );
+ }
+
+ if ( mods.sm_desc->ad_type->sat_equality &&
+ mods.sm_desc->ad_type->sat_equality->smr_normalize )
+ {
+ normalize = 1;
+ }
+
+ if ( bin && mod->mod_bvalues ) {
+ for ( i = 0; mod->mod_bvalues[ i ] != NULL; i++ )
+ ;
+
+ } else if ( !bin && mod->mod_values ) {
+ for ( i = 0; mod->mod_values[ i ] != NULL; i++ )
+ ;
+ }
+
+ if ( i != 0 )
+ {
+ mods.sm_values = ch_calloc( sizeof( struct berval ), i + 1 );
+ if ( normalize ) {
+ mods.sm_nvalues = ch_calloc( sizeof( struct berval ), i + 1 );
+ } else {
+ mods.sm_nvalues = NULL;
+ }
+ }
+ mods.sm_numvals = i;
+
+ for ( i = 0; i < mods.sm_numvals; i++ ) {
+ struct berval bv;
+
+ if ( bin ) {
+ bv = *mod->mod_bvalues[ i ];
+ } else {
+ ber_str2bv( mod->mod_values[ i ], 0, 0, &bv );
+ }
+
+ if ( pretty ) {
+ local_rc = ordered_value_pretty( mods.sm_desc,
+ &bv, &mods.sm_values[i], NULL );
+
+ } else {
+ local_rc = ordered_value_validate( mods.sm_desc,
+ &bv, 0 );
+ }
+
+ if ( local_rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s: DN=\"%s\": unable to %s attr=%s value #%d\n",
+ progname, e->e_dn, pretty ? "prettify" : "validate",
+ mods.sm_desc->ad_cname.bv_val, i );
+ /* handle error */
+ rc = EXIT_FAILURE;
+ ber_bvarray_free( mods.sm_values );
+ ber_bvarray_free( mods.sm_nvalues );
+ goto cleanup;
+ }
+
+ if ( !pretty ) {
+ ber_dupbv( &mods.sm_values[i], &bv );
+ }
+
+ if ( normalize ) {
+ local_rc = ordered_value_normalize(
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ mods.sm_desc,
+ mods.sm_desc->ad_type->sat_equality,
+ &mods.sm_values[i], &mods.sm_nvalues[i],
+ NULL );
+ if ( local_rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s: DN=\"%s\": unable to normalize attr=%s value #%d\n",
+ progname, e->e_dn, mods.sm_desc->ad_cname.bv_val, i );
+ /* handle error */
+ rc = EXIT_FAILURE;
+ ber_bvarray_free( mods.sm_values );
+ ber_bvarray_free( mods.sm_nvalues );
+ goto cleanup;
+ }
+ }
+ }
+
+ mods.sm_op = (mod->mod_op & ~LDAP_MOD_BVALUES);
+ mods.sm_flags = 0;
+
+ if ( mods.sm_desc == slap_schema.si_ad_objectClass ) {
+ is_oc = 1;
+ }
+
+ switch ( mods.sm_op ) {
+ case LDAP_MOD_ADD:
+ local_rc = modify_add_values( e, &mods,
+ 0, &text, textbuf, textlen );
+ break;
+
+ case LDAP_MOD_DELETE:
+ local_rc = modify_delete_values( e, &mods,
+ 0, &text, textbuf, textlen );
+ break;
+
+ case LDAP_MOD_REPLACE:
+ local_rc = modify_replace_values( e, &mods,
+ 0, &text, textbuf, textlen );
+ break;
+
+ case LDAP_MOD_INCREMENT:
+ local_rc = modify_increment_values( e, &mods,
+ 0, &text, textbuf, textlen );
+ break;
+ }
+
+ ber_bvarray_free( mods.sm_values );
+ ber_bvarray_free( mods.sm_nvalues );
+
+ if ( local_rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s: DN=\"%s\": unable to modify attr=%s\n",
+ progname, e->e_dn, mods.sm_desc->ad_cname.bv_val );
+ rc = EXIT_FAILURE;
+ goto cleanup;
+ }
+ }
+
+ rc = slap_tool_entry_check( progname, op, e, lineno, &text, textbuf, textlen );
+ if ( rc != LDAP_SUCCESS ) {
+ rc = EXIT_FAILURE;
+ goto cleanup;
+ }
+ }
+
+ if ( SLAP_LASTMOD(be) && e != NULL ) {
+ time_t now = slap_get_time();
+ char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
+ struct berval vals[ 2 ];
+
+ struct berval name, timestamp;
+
+ struct berval nvals[ 2 ];
+ struct berval nname;
+ char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+
+ Attribute *a;
+
+ vals[1].bv_len = 0;
+ vals[1].bv_val = NULL;
+
+ nvals[1].bv_len = 0;
+ nvals[1].bv_val = NULL;
+
+ csn.bv_len = ldap_pvt_csnstr( csnbuf, sizeof( csnbuf ), csnsid, 0 );
+ csn.bv_val = csnbuf;
+
+ timestamp.bv_val = timebuf;
+ timestamp.bv_len = sizeof(timebuf);
+
+ slap_timestamp( &now, &timestamp );
+
+ if ( BER_BVISEMPTY( &be->be_rootndn ) ) {
+ BER_BVSTR( &name, SLAPD_ANONYMOUS );
+ nname = name;
+ } else {
+ name = be->be_rootdn;
+ nname = be->be_rootndn;
+ }
+
+ a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
+ if ( a != NULL ) {
+ if ( a->a_vals != a->a_nvals ) {
+ SLAP_FREE( a->a_nvals[0].bv_val );
+ SLAP_FREE( a->a_nvals );
+ }
+ SLAP_FREE( a->a_vals[0].bv_val );
+ SLAP_FREE( a->a_vals );
+ a->a_vals = NULL;
+ a->a_nvals = NULL;
+ a->a_numvals = 0;
+ }
+ vals[0].bv_len = lutil_uuidstr( uuidbuf, sizeof( uuidbuf ) );
+ vals[0].bv_val = uuidbuf;
+ attr_merge_normalize_one( e, slap_schema.si_ad_entryUUID, vals, NULL );
+
+ a = attr_find( e->e_attrs, slap_schema.si_ad_creatorsName );
+ if ( a == NULL ) {
+ vals[0] = name;
+ nvals[0] = nname;
+ attr_merge( e, slap_schema.si_ad_creatorsName, vals, nvals );
+
+ } else {
+ ber_bvreplace( &a->a_vals[0], &name );
+ ber_bvreplace( &a->a_nvals[0], &nname );
+ }
+
+ a = attr_find( e->e_attrs, slap_schema.si_ad_createTimestamp );
+ if ( a == NULL ) {
+ vals[0] = timestamp;
+ attr_merge( e, slap_schema.si_ad_createTimestamp, vals, NULL );
+
+ } else {
+ ber_bvreplace( &a->a_vals[0], &timestamp );
+ }
+
+ a = attr_find( e->e_attrs, slap_schema.si_ad_entryCSN );
+ if ( a == NULL ) {
+ vals[0] = csn;
+ attr_merge( e, slap_schema.si_ad_entryCSN, vals, NULL );
+
+ } else {
+ ber_bvreplace( &a->a_vals[0], &csn );
+ }
+
+ a = attr_find( e->e_attrs, slap_schema.si_ad_modifiersName );
+ if ( a == NULL ) {
+ vals[0] = name;
+ nvals[0] = nname;
+ attr_merge( e, slap_schema.si_ad_modifiersName, vals, nvals );
+
+ } else {
+ ber_bvreplace( &a->a_vals[0], &name );
+ ber_bvreplace( &a->a_nvals[0], &nname );
+ }
+
+ a = attr_find( e->e_attrs, slap_schema.si_ad_modifyTimestamp );
+ if ( a == NULL ) {
+ vals[0] = timestamp;
+ attr_merge( e, slap_schema.si_ad_modifyTimestamp, vals, NULL );
+
+ } else {
+ ber_bvreplace( &a->a_vals[0], &timestamp );
+ }
+ }
+
+ /* check schema, objectClass etc */
+
+ if ( !dryrun ) {
+ switch ( lr.lr_op ) {
+ case LDAP_REQ_ADD:
+ id = be->be_entry_put( be, e, &bvtext );
+ rc = (id == NOID);
+ break;
+
+ case LDAP_REQ_MODIFY:
+ id = be->be_entry_modify( be, e, &bvtext );
+ rc = (id == NOID);
+ break;
+
+ case LDAP_REQ_DELETE:
+ rc = be->be_entry_delete( be, &ndn, &bvtext );
+ break;
+
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s: could not %s entry dn=\"%s\" "
+ "(line=%lu): %s\n", progname, request, ndn.bv_val,
+ lineno, bvtext.bv_val );
+ rc = EXIT_FAILURE;
+ goto cleanup;
+ }
+
+ sid = slap_tool_update_ctxcsn_check( progname, e );
+
+ if ( verbose )
+ fprintf( stderr, "%s: \"%s\" (%08lx)\n",
+ request, ndn.bv_val, (long) id );
+ } else {
+ if ( verbose )
+ fprintf( stderr, "%s: \"%s\"\n",
+ request, ndn.bv_val );
+ }
+
+cleanup:;
+ ldap_ldif_record_done( &lr );
+ SLAP_FREE( ndn.bv_val );
+ if ( e ) entry_free( e );
+ if ( e_orig ) be_entry_release_w( op, e_orig );
+ if ( rc != LDAP_SUCCESS && !continuemode ) break;
+ }
+
+ if ( ldifrc < 0 )
+ rc = EXIT_FAILURE;
+
+ bvtext.bv_len = textlen;
+ bvtext.bv_val = textbuf;
+ bvtext.bv_val[0] = '\0';
+
+ if ( enable_meter ) {
+ lutil_meter_update( &meter, ftell( ldiffp->fp ), 1);
+ lutil_meter_close( &meter );
+ }
+
+ if ( rc == EXIT_SUCCESS ) {
+ rc = slap_tool_update_ctxcsn( progname, sid, &bvtext );
+ }
+
+ ch_free( buf );
+
+ if ( !dryrun ) {
+ if ( enable_meter ) {
+ fprintf( stderr, "Closing DB..." );
+ }
+ if( be->be_entry_close( be ) ) {
+ rc = EXIT_FAILURE;
+ }
+
+ if( be->be_sync ) {
+ be->be_sync( be );
+ }
+ if ( enable_meter ) {
+ fprintf( stderr, "\n" );
+ }
+ }
+
+ if ( slap_tool_destroy())
+ rc = EXIT_FAILURE;
+
+ return rc;
+}
+
diff --git a/servers/slapd/slappasswd.c b/servers/slapd/slappasswd.c
new file mode 100644
index 0000000..9c2adef
--- /dev/null
+++ b/servers/slapd/slappasswd.c
@@ -0,0 +1,301 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1998-2003 Kurt D. Zeilenga.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Kurt Zeilenga for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/signal.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include <ldap.h>
+#include <lber_pvt.h>
+#include <lutil.h>
+#include <lutil_sha1.h>
+
+#include "ldap_defaults.h"
+
+#include "slap.h"
+#include "slap-config.h"
+#include "slapcommon.h"
+
+static char *modulepath = NULL;
+static char *moduleload = NULL;
+static int moduleargc = 0;
+static char **moduleargv = NULL;
+
+static void
+usage(const char *s)
+{
+ fprintf(stderr,
+ "Usage: %s [options]\n"
+ " -c format\tcrypt(3) salt format\n"
+ " -g\t\tgenerate random password\n"
+ " -h hash\tpassword scheme\n"
+ " -n\t\tomit trailing newline\n"
+ " -o <opt>[=val] specify an option with a(n optional) value\n"
+ " \tmodule-path=<pathspec>\n"
+ " \tmodule-load=<filename>\n"
+ " -s secret\tnew password\n"
+ " -u\t\tgenerate RFC2307 values (default)\n"
+ " -v\t\tincrease verbosity\n"
+ " -T file\tread file for new password\n"
+ , s );
+
+ exit( EXIT_FAILURE );
+}
+
+static int
+parse_slappasswdopt( void )
+{
+ size_t len = 0;
+ char *p;
+
+ p = strchr( optarg, '=' );
+ if ( p != NULL ) {
+ len = p - optarg;
+ p++;
+ }
+
+ if ( strncasecmp( optarg, "module-path", len ) == 0 ) {
+ modulepath = p;
+
+ } else if ( strncasecmp( optarg, "module-load", len ) == 0 ) {
+ ConfigArgs c = { .line = p };
+
+ if ( config_fp_parse_line( &c ) ) {
+ return -1;
+ }
+ moduleload = c.argv[0];
+
+ moduleargc = c.argc - 1;
+ if ( moduleargc ) {
+ moduleargv = c.argv+1;
+ }
+
+ } else {
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+slappasswd( int argc, char *argv[] )
+{
+ int rc = EXIT_SUCCESS;
+#ifdef LUTIL_SHA1_BYTES
+ char *default_scheme = "{SSHA}";
+#else
+ char *default_scheme = "{SMD5}";
+#endif
+ char *scheme = default_scheme;
+
+ char *newpw = NULL;
+ char *pwfile = NULL;
+ const char *text;
+ const char *progname = "slappasswd";
+
+ int i;
+ char *newline = "\n";
+ struct berval passwd = BER_BVNULL;
+ struct berval hash = BER_BVNULL;
+
+#ifdef LDAP_DEBUG
+ /* tools default to "none", so that at least LDAP_DEBUG_ANY
+ * messages show up; use -d 0 to reset */
+ slap_debug = LDAP_DEBUG_NONE;
+#endif
+ ldap_syslog = 0;
+
+ while( (i = getopt( argc, argv,
+ "c:d:gh:no:s:T:vu" )) != EOF )
+ {
+ switch (i) {
+ case 'c': /* crypt salt format */
+ scheme = "{CRYPT}";
+ lutil_salt_format( optarg );
+ break;
+
+ case 'g': /* new password (generate) */
+ if ( pwfile != NULL ) {
+ fprintf( stderr, "Option -g incompatible with -T\n" );
+ return EXIT_FAILURE;
+
+ } else if ( newpw != NULL ) {
+ fprintf( stderr, "New password already provided\n" );
+ return EXIT_FAILURE;
+
+ } else if ( lutil_passwd_generate( &passwd, 8 )) {
+ fprintf( stderr, "Password generation failed\n" );
+ return EXIT_FAILURE;
+ }
+ break;
+
+ case 'h': /* scheme */
+ if ( scheme != default_scheme ) {
+ fprintf( stderr, "Scheme already provided\n" );
+ return EXIT_FAILURE;
+
+ } else {
+ scheme = optarg;
+ }
+ break;
+
+ case 'n':
+ newline = "";
+ break;
+
+ case 'o':
+ if ( parse_slappasswdopt() ) {
+ usage ( progname );
+ }
+ break;
+
+ case 's': /* new password (secret) */
+ if ( pwfile != NULL ) {
+ fprintf( stderr, "Option -s incompatible with -T\n" );
+ return EXIT_FAILURE;
+
+ } else if ( newpw != NULL ) {
+ fprintf( stderr, "New password already provided\n" );
+ return EXIT_FAILURE;
+
+ } else {
+ char* p;
+ newpw = ch_strdup( optarg );
+
+ for( p = optarg; *p != '\0'; p++ ) {
+ *p = '\0';
+ }
+ }
+ break;
+
+ case 'T': /* password file */
+ if ( pwfile != NULL ) {
+ fprintf( stderr, "Password file already provided\n" );
+ return EXIT_FAILURE;
+
+ } else if ( newpw != NULL ) {
+ fprintf( stderr, "Option -T incompatible with -s/-g\n" );
+ return EXIT_FAILURE;
+
+ }
+ pwfile = optarg;
+ break;
+
+ case 'u': /* RFC2307 userPassword */
+ break;
+
+ case 'v': /* verbose */
+ verbose++;
+ break;
+
+ default:
+ usage ( progname );
+ }
+ }
+ slapTool = SLAPPASSWD;
+
+ if( argc - optind != 0 ) {
+ usage( progname );
+ }
+
+#ifdef SLAPD_MODULES
+ if ( module_init() != 0 ) {
+ fprintf( stderr, "%s: module_init failed\n", progname );
+ return EXIT_FAILURE;
+ }
+
+ if ( modulepath && module_path(modulepath) ) {
+ rc = EXIT_FAILURE;
+ goto destroy;
+ }
+
+ if ( moduleload && module_load(moduleload, moduleargc, moduleargv) ) {
+ rc = EXIT_FAILURE;
+ goto destroy;
+ }
+#endif
+
+ if( pwfile != NULL ) {
+ if( lutil_get_filed_password( pwfile, &passwd )) {
+ rc = EXIT_FAILURE;
+ goto destroy;
+ }
+ } else if ( BER_BVISEMPTY( &passwd )) {
+ if( newpw == NULL ) {
+ /* prompt for new password */
+ char *cknewpw;
+ newpw = ch_strdup(getpassphrase("New password: "));
+ cknewpw = getpassphrase("Re-enter new password: ");
+
+ if( strcmp( newpw, cknewpw )) {
+ fprintf( stderr, "Password values do not match\n" );
+ rc = EXIT_FAILURE;
+ goto destroy;
+ }
+ }
+
+ passwd.bv_val = newpw;
+ passwd.bv_len = strlen(passwd.bv_val);
+ } else {
+ hash = passwd;
+ goto print_pw;
+ }
+
+ lutil_passwd_hash( &passwd, scheme, &hash, &text );
+ if ( BER_BVISNULL( &hash ) ) {
+ fprintf( stderr,
+ "Password generation failed for scheme %s: %s\n",
+ scheme, text ? text : "" );
+ rc = EXIT_FAILURE;
+ goto destroy;
+ }
+
+ if( lutil_passwd( &hash, &passwd, NULL, &text ) ) {
+ fprintf( stderr, "Password verification failed. %s\n",
+ text ? text : "" );
+ rc = EXIT_FAILURE;
+ goto destroy;
+ }
+
+print_pw:;
+ printf( "%s%s" , hash.bv_val, newline );
+
+destroy:;
+#ifdef SLAPD_MODULES
+ module_kill();
+#endif
+ if ( !BER_BVISNULL( &hash ) ) {
+ ber_memfree( hash.bv_val );
+ }
+ if ( passwd.bv_val != hash.bv_val && !BER_BVISNULL( &passwd ) ) {
+ ber_memfree( passwd.bv_val );
+ }
+
+ return rc;
+}
diff --git a/servers/slapd/slapschema.c b/servers/slapd/slapschema.c
new file mode 100644
index 0000000..f1d1d18
--- /dev/null
+++ b/servers/slapd/slapschema.c
@@ -0,0 +1,165 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1998-2003 Kurt D. Zeilenga.
+ * Portions Copyright 2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software. Code portions borrowed from slapcat.c;
+ * contributors are Kurt Zeilenga and Jong Hyuk Choi
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "ac/stdlib.h"
+#include "ac/ctype.h"
+#include "ac/socket.h"
+#include "ac/string.h"
+
+#include "slapcommon.h"
+#include "ldif.h"
+
+static volatile sig_atomic_t gotsig;
+
+static RETSIGTYPE
+slapcat_sig( int sig )
+{
+ gotsig=1;
+}
+
+int
+slapschema( int argc, char **argv )
+{
+ ID id;
+ int rc = EXIT_SUCCESS;
+ const char *progname = "slapschema";
+ Connection conn = { 0 };
+ OperationBuffer opbuf;
+ Operation *op = NULL;
+ void *thrctx;
+ int requestBSF = 0;
+ int doBSF = 0;
+
+ slap_tool_init( progname, SLAPSCHEMA, argc, argv );
+
+ requestBSF = ( sub_ndn.bv_len || filter );
+
+#ifdef SIGPIPE
+ (void) SIGNAL( SIGPIPE, slapcat_sig );
+#endif
+#ifdef SIGHUP
+ (void) SIGNAL( SIGHUP, slapcat_sig );
+#endif
+ (void) SIGNAL( SIGINT, slapcat_sig );
+ (void) SIGNAL( SIGTERM, slapcat_sig );
+
+ if( !be->be_entry_open ||
+ !be->be_entry_close ||
+ !( be->be_entry_first || be->be_entry_first_x ) ||
+ !be->be_entry_next ||
+ !be->be_entry_get )
+ {
+ fprintf( stderr, "%s: database doesn't support necessary operations.\n",
+ progname );
+ exit( EXIT_FAILURE );
+ }
+
+ if( be->be_entry_open( be, 0 ) != 0 ) {
+ fprintf( stderr, "%s: could not open database.\n",
+ progname );
+ exit( EXIT_FAILURE );
+ }
+
+ thrctx = ldap_pvt_thread_pool_context();
+ connection_fake_init( &conn, &opbuf, thrctx );
+ op = &opbuf.ob_op;
+ op->o_tmpmemctx = NULL;
+ op->o_bd = be;
+
+
+ if ( !requestBSF && be->be_entry_first ) {
+ id = be->be_entry_first( be );
+
+ } else {
+ if ( be->be_entry_first_x ) {
+ id = be->be_entry_first_x( be,
+ sub_ndn.bv_len ? &sub_ndn : NULL, scope, filter );
+
+ } else {
+ assert( be->be_entry_first != NULL );
+ doBSF = 1;
+ id = be->be_entry_first( be );
+ }
+ }
+
+ for ( ; id != NOID; id = be->be_entry_next( be ) ) {
+ Entry* e;
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof(textbuf);
+ const char *text = NULL;
+
+ if ( gotsig )
+ break;
+
+ e = be->be_entry_get( be, id );
+ if ( e == NULL ) {
+ printf("# no data for entry id=%08lx\n\n", (long) id );
+ rc = EXIT_FAILURE;
+ if( continuemode ) continue;
+ break;
+ }
+
+ if ( doBSF ) {
+ if ( sub_ndn.bv_len && !dnIsSuffixScope( &e->e_nname, &sub_ndn, scope ) )
+ {
+ be_entry_release_r( op, e );
+ continue;
+ }
+
+
+ if ( filter != NULL ) {
+ int rc = test_filter( NULL, e, filter );
+ if ( rc != LDAP_COMPARE_TRUE ) {
+ be_entry_release_r( op, e );
+ continue;
+ }
+ }
+ }
+
+ if( verbose ) {
+ printf( "# id=%08lx\n", (long) id );
+ }
+
+ rc = entry_schema_check( op, e, NULL, 0, 0, NULL,
+ &text, textbuf, textlen );
+ if ( rc != LDAP_SUCCESS ) {
+ fprintf( ldiffp->fp, "# (%d) %s%s%s\n",
+ rc, ldap_err2string( rc ),
+ text ? ": " : "",
+ text ? text : "" );
+ fprintf( ldiffp->fp, "dn: %s\n\n", e->e_name.bv_val );
+ }
+
+ be_entry_release_r( op, e );
+ }
+
+ be->be_entry_close( be );
+
+ if ( slap_tool_destroy() )
+ rc = EXIT_FAILURE;
+
+ return rc;
+}
diff --git a/servers/slapd/slaptest.c b/servers/slapd/slaptest.c
new file mode 100644
index 0000000..4992147
--- /dev/null
+++ b/servers/slapd/slaptest.c
@@ -0,0 +1,121 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2004 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ac/unistd.h>
+#include <ac/errno.h>
+
+#include <lber.h>
+#include <ldif.h>
+#include <lutil.h>
+
+#include "slapcommon.h"
+
+#ifndef S_IWRITE
+#define S_IWRITE S_IWUSR
+#endif
+
+static int
+test_file( const char *fname, const char *ftype )
+{
+ struct stat st;
+ char ebuf[128];
+ int save_errno;
+
+ switch ( stat( fname, &st ) ) {
+ case 0:
+ if ( !( st.st_mode & S_IWRITE ) ) {
+ Debug( LDAP_DEBUG_ANY, "%s file "
+ "\"%s\" exists, but user does not have access\n",
+ ftype, fname );
+ return -1;
+ }
+ break;
+
+ case -1:
+ default:
+ save_errno = errno;
+ if ( save_errno == ENOENT ) {
+ FILE *fp = fopen( fname, "w" );
+
+ if ( fp == NULL ) {
+ save_errno = errno;
+
+ Debug( LDAP_DEBUG_ANY, "unable to open file "
+ "\"%s\": %d (%s)\n",
+ fname,
+ save_errno, AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+
+ return -1;
+ }
+ fclose( fp );
+ unlink( fname );
+ break;
+ }
+
+ Debug( LDAP_DEBUG_ANY, "unable to stat file "
+ "\"%s\": %d (%s)\n",
+ slapd_pid_file,
+ save_errno, AC_STRERROR_R( save_errno, ebuf, sizeof(ebuf) ) );
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+slaptest( int argc, char **argv )
+{
+ int rc = EXIT_SUCCESS;
+ const char *progname = "slaptest";
+
+ slap_tool_init( progname, SLAPTEST, argc, argv );
+
+ if ( slapd_pid_file != NULL ) {
+ if ( test_file( slapd_pid_file, "pid" ) ) {
+ return EXIT_FAILURE;
+ }
+ }
+
+ if ( slapd_args_file != NULL ) {
+ if ( test_file( slapd_args_file, "args" ) ) {
+ return EXIT_FAILURE;
+ }
+ }
+
+ if ( !quiet ) {
+ fprintf( stderr, "config file testing succeeded\n");
+ }
+
+ if ( slap_tool_destroy())
+ rc = EXIT_FAILURE;
+
+ return rc;
+}
diff --git a/servers/slapd/starttls.c b/servers/slapd/starttls.c
new file mode 100644
index 0000000..6a3c90b
--- /dev/null
+++ b/servers/slapd/starttls.c
@@ -0,0 +1,112 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "lber_pvt.h"
+
+const struct berval slap_EXOP_START_TLS = BER_BVC(LDAP_EXOP_START_TLS);
+
+#ifdef HAVE_TLS
+int
+starttls_extop ( Operation *op, SlapReply *rs )
+{
+ int rc;
+
+ Debug( LDAP_DEBUG_STATS, "%s STARTTLS\n",
+ op->o_log_prefix );
+
+ if ( op->ore_reqdata != NULL ) {
+ /* no request data should be provided */
+ rs->sr_text = "no request data expected";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ /* acquire connection lock */
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+
+ /* can't start TLS if it is already started */
+ if (op->o_conn->c_is_tls != 0) {
+ rs->sr_text = "TLS already started";
+ rc = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ /* can't start TLS if there are other op's around */
+ if (( !LDAP_STAILQ_EMPTY(&op->o_conn->c_ops) &&
+ (LDAP_STAILQ_FIRST(&op->o_conn->c_ops) != op ||
+ LDAP_STAILQ_NEXT(op, o_next) != NULL)) ||
+ ( !LDAP_STAILQ_EMPTY(&op->o_conn->c_pending_ops) ))
+ {
+ rs->sr_text = "cannot start TLS when operations are outstanding";
+ rc = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ if ( !( global_disallows & SLAP_DISALLOW_TLS_2_ANON ) &&
+ ( op->o_conn->c_dn.bv_len != 0 ) )
+ {
+ Debug( LDAP_DEBUG_STATS,
+ "%s AUTHZ anonymous mech=starttls ssf=0\n",
+ op->o_log_prefix );
+
+ /* force to anonymous */
+ connection2anonymous( op->o_conn );
+ }
+
+ if ( ( global_disallows & SLAP_DISALLOW_TLS_AUTHC ) &&
+ ( op->o_conn->c_dn.bv_len != 0 ) )
+ {
+ rs->sr_text = "cannot start TLS after authentication";
+ rc = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ /* fail if TLS could not be initialized */
+ if ( slap_tls_ctx == NULL ) {
+ if (default_referral != NULL) {
+ /* caller will put the referral in the result */
+ rc = LDAP_REFERRAL;
+ goto done;
+ }
+
+ rs->sr_text = "Could not initialize TLS";
+ rc = LDAP_UNAVAILABLE;
+ goto done;
+ }
+
+ op->o_conn->c_is_tls = 1;
+ op->o_conn->c_needs_tls_accept = 1;
+
+ rc = LDAP_SUCCESS;
+
+done:
+ /* give up connection lock */
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+
+ /* FIXME: RACE CONDITION! we give up lock before sending result
+ * Should be resolved by reworking connection state, not
+ * by moving send here (so as to ensure proper TLS sequencing)
+ */
+
+ return rc;
+}
+
+#endif /* HAVE_TLS */
diff --git a/servers/slapd/str2filter.c b/servers/slapd/str2filter.c
new file mode 100644
index 0000000..8642ec3
--- /dev/null
+++ b/servers/slapd/str2filter.c
@@ -0,0 +1,84 @@
+/* str2filter.c - parse an RFC 4515 string filter */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+
+Filter *
+str2filter_x( Operation *op, const char *str )
+{
+ int rc;
+ Filter *f = NULL;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ const char *text = NULL;
+
+ Debug( LDAP_DEBUG_FILTER, "str2filter \"%s\"\n", str );
+
+ if ( str == NULL || *str == '\0' ) {
+ return NULL;
+ }
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+ if ( op->o_tmpmemctx ) {
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+ }
+
+ rc = ldap_pvt_put_filter( ber, str );
+ if( rc < 0 ) {
+ goto done;
+ }
+
+ ber_reset( ber, 1 );
+
+ rc = get_filter( op, ber, &f, &text );
+
+done:
+ ber_free_buf( ber );
+
+ return f;
+}
+
+Filter *
+str2filter( const char *str )
+{
+ Operation op = {0};
+ Opheader ohdr = {0};
+
+ op.o_hdr = &ohdr;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ return str2filter_x( &op, str );
+}
diff --git a/servers/slapd/syncrepl.c b/servers/slapd/syncrepl.c
new file mode 100644
index 0000000..52e67e4
--- /dev/null
+++ b/servers/slapd/syncrepl.c
@@ -0,0 +1,7625 @@
+/* syncrepl.c -- Replication Engine which uses the LDAP Sync protocol */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2003 by IBM Corporation.
+ * Portions Copyright 2003-2008 by Howard Chu, Symas Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "lutil.h"
+#include "slap.h"
+#include "lutil_ldap.h"
+
+#include "slap-config.h"
+
+#include "ldap_rq.h"
+
+#include "rewrite.h"
+
+#include "back-monitor/back-monitor.h"
+
+#define SUFFIXM_CTX "<suffix massage>"
+
+#ifdef LDAP_CONTROL_X_DIRSYNC
+#define MSAD_DIRSYNC 0x04
+#define MSAD_DIRSYNC_MODIFY 0x10
+
+static AttributeDescription *sy_ad_objectGUID;
+static AttributeDescription *sy_ad_instanceType;
+static AttributeDescription *sy_ad_isDeleted;
+static AttributeDescription *sy_ad_whenCreated;
+static AttributeDescription *sy_ad_dirSyncCookie;
+
+static struct berval msad_addval = BER_BVC("range=1-1");
+static struct berval msad_delval = BER_BVC("range=0-0");
+#endif
+
+static AttributeDescription *sy_ad_nsUniqueId;
+static AttributeDescription *sy_ad_dseeLastChange;
+
+#define DSEE_SYNC_ADD 0x20
+
+#define UUIDLEN 16
+
+struct syncinfo_s;
+
+struct nonpresent_entry {
+ struct berval *npe_name;
+ struct berval *npe_nname;
+ LDAP_LIST_ENTRY(nonpresent_entry) npe_link;
+};
+
+typedef struct cookie_vals {
+ struct berval *cv_vals;
+ int *cv_sids;
+ int cv_num;
+} cookie_vals;
+
+typedef struct cookie_state {
+ ldap_pvt_thread_mutex_t cs_mutex;
+ ldap_pvt_thread_cond_t cs_cond;
+ struct berval *cs_vals;
+ int *cs_sids;
+ int cs_num;
+ int cs_age;
+ int cs_ref;
+ int cs_updating;
+
+ /* pending changes, not yet committed */
+ ldap_pvt_thread_mutex_t cs_pmutex;
+ struct berval *cs_pvals;
+ int *cs_psids;
+ int cs_pnum;
+
+ /* serialize multi-consumer refreshes */
+ ldap_pvt_thread_mutex_t cs_refresh_mutex;
+ struct syncinfo_s *cs_refreshing;
+} cookie_state;
+
+#define SYNC_TIMEOUT 0
+#define SYNC_SHUTDOWN -100
+#define SYNC_ERROR -101
+#define SYNC_REPOLL -102
+#define SYNC_PAUSED -103
+#define SYNC_BUSY -104
+
+#define SYNCDATA_DEFAULT 0 /* entries are plain LDAP entries */
+#define SYNCDATA_ACCESSLOG 1 /* entries are accesslog format */
+#define SYNCDATA_CHANGELOG 2 /* entries are changelog format */
+
+#define SYNCLOG_LOGGING 0 /* doing a log-based update */
+#define SYNCLOG_FALLBACK 1 /* doing a full refresh */
+
+#define RETRYNUM_FOREVER (-1) /* retry forever */
+#define RETRYNUM_TAIL (-2) /* end of retrynum array */
+#define RETRYNUM_VALID(n) ((n) >= RETRYNUM_FOREVER) /* valid retrynum */
+#define RETRYNUM_FINITE(n) ((n) > RETRYNUM_FOREVER) /* not forever */
+
+typedef struct syncinfo_s {
+ struct syncinfo_s *si_next;
+ BackendDB *si_be;
+ BackendDB *si_wbe;
+ struct re_s *si_re;
+ int si_rid;
+ char si_ridtxt[ STRLENOF("rid=999") + 1 ];
+ slap_bindconf si_bindconf;
+ struct berval si_base;
+ struct berval si_logbase;
+ struct berval si_filterstr;
+ struct berval si_logfilterstr;
+ Filter *si_filter;
+ Filter *si_logfilter;
+ struct berval si_contextdn;
+ int si_scope;
+ int si_attrsonly;
+ char *si_anfile;
+ AttributeName *si_anlist;
+ AttributeName *si_exanlist;
+ char **si_attrs;
+ char **si_exattrs;
+ int si_allattrs;
+ int si_allopattrs;
+ int si_schemachecking;
+ int si_type; /* the active type */
+ int si_ctype; /* the configured type */
+ time_t si_interval;
+ time_t *si_retryinterval;
+ int *si_retrynum_init;
+ int *si_retrynum;
+ struct sync_cookie si_syncCookie;
+ cookie_state *si_cookieState;
+ int si_cookieAge;
+ int si_manageDSAit;
+ int si_slimit;
+ int si_tlimit;
+ int si_refreshDelete;
+ int si_refreshPresent;
+ int si_refreshDone;
+ int si_paused;
+ int si_syncdata;
+ int si_logstate;
+ int si_lazyCommit;
+ int si_got;
+ int si_strict_refresh; /* stop listening during fallback refresh */
+ int si_too_old;
+ int si_is_configdb;
+ ber_int_t si_msgid;
+ Avlnode *si_presentlist;
+ LDAP *si_ld;
+ Connection *si_conn;
+ LDAP_LIST_HEAD(np, nonpresent_entry) si_nonpresentlist;
+ struct rewrite_info *si_rewrite;
+ struct berval si_suffixm;
+#ifdef LDAP_CONTROL_X_DIRSYNC
+ struct berval si_dirSyncCookie;
+#endif
+ unsigned long si_prevchange;
+ unsigned long si_lastchange;
+
+ /* monitor info */
+ int si_monitorInited;
+ time_t si_lastconnect;
+ time_t si_lastcontact;
+ struct berval si_connaddr;
+ struct berval si_lastCookieRcvd;
+ struct berval si_lastCookieSent;
+ struct berval si_monitor_ndn;
+ char si_connaddrbuf[LDAP_IPADDRLEN];
+
+ ldap_pvt_thread_mutex_t si_monitor_mutex;
+ ldap_pvt_thread_mutex_t si_mutex;
+} syncinfo_t;
+
+static int syncuuid_cmp( const void *, const void * );
+static int presentlist_insert( syncinfo_t* si, struct berval *syncUUID );
+static void presentlist_delete( Avlnode **av, struct berval *syncUUID );
+static char *presentlist_find( Avlnode *av, struct berval *syncUUID );
+static int presentlist_free( Avlnode *av );
+static void syncrepl_del_nonpresent( Operation *, syncinfo_t *, BerVarray, struct sync_cookie *, int );
+static int syncrepl_message_to_op(
+ syncinfo_t *, Operation *, LDAPMessage *, int );
+static int syncrepl_message_to_entry(
+ syncinfo_t *, Operation *, LDAPMessage *,
+ Modifications **, Entry **, int, struct berval* );
+static int syncrepl_entry(
+ syncinfo_t *, Operation*, Entry*,
+ Modifications**,int, struct berval*,
+ struct berval *cookieCSN );
+static int syncrepl_updateCookie(
+ syncinfo_t *, Operation *,
+ struct sync_cookie *, int save );
+static struct berval * slap_uuidstr_from_normalized(
+ struct berval *, struct berval *, void * );
+static int syncrepl_add_glue_ancestors(
+ Operation* op, Entry *e );
+
+#ifdef LDAP_CONTROL_X_DIRSYNC
+static int syncrepl_dirsync_message(
+ syncinfo_t *, Operation *, LDAPMessage *,
+ Modifications **, Entry **, int *, struct berval* );
+static int syncrepl_dirsync_cookie(
+ syncinfo_t *, Operation *, LDAPControl ** );
+#endif
+
+static int syncrepl_dsee_update( syncinfo_t *si, Operation *op ) ;
+
+/* delta-mpr overlay handler */
+static int syncrepl_op_modify( Operation *op, SlapReply *rs );
+
+/* callback functions */
+static int dn_callback( Operation *, SlapReply * );
+static int nonpresent_callback( Operation *, SlapReply * );
+
+static AttributeDescription *sync_descs[4];
+
+static AttributeDescription *dsee_descs[7];
+
+/* delta-mpr */
+static AttributeDescription *ad_reqMod, *ad_reqDN;
+
+typedef struct logschema {
+ struct berval ls_dn;
+ struct berval ls_req;
+ struct berval ls_mod;
+ struct berval ls_newRdn;
+ struct berval ls_delRdn;
+ struct berval ls_newSup;
+ struct berval ls_controls;
+ struct berval ls_uuid;
+ struct berval ls_changenum;
+} logschema;
+
+static logschema changelog_sc = {
+ BER_BVC("targetDN"),
+ BER_BVC("changeType"),
+ BER_BVC("changes"),
+ BER_BVC("newRDN"),
+ BER_BVC("deleteOldRDN"),
+ BER_BVC("newSuperior"),
+ BER_BVNULL,
+ BER_BVC("targetUniqueId"),
+ BER_BVC("changeNumber")
+};
+
+static logschema accesslog_sc = {
+ BER_BVC("reqDN"),
+ BER_BVC("reqType"),
+ BER_BVC("reqMod"),
+ BER_BVC("reqNewRDN"),
+ BER_BVC("reqDeleteOldRDN"),
+ BER_BVC("reqNewSuperior"),
+ BER_BVC("reqControls")
+};
+
+static const char *
+syncrepl_state2str( int state )
+{
+ switch ( state ) {
+ case LDAP_SYNC_PRESENT:
+ return "PRESENT";
+
+ case LDAP_SYNC_ADD:
+ return "ADD";
+
+ case LDAP_SYNC_MODIFY:
+ return "MODIFY";
+
+ case LDAP_SYNC_DELETE:
+ return "DELETE";
+#ifdef LDAP_CONTROL_X_DIRSYNC
+ case MSAD_DIRSYNC_MODIFY:
+ return "DIRSYNC_MOD";
+#endif
+ case DSEE_SYNC_ADD:
+ return "DSEE_ADD";
+ }
+
+ return "UNKNOWN";
+}
+
+static slap_overinst syncrepl_ov;
+
+static void
+init_syncrepl(syncinfo_t *si)
+{
+ int i, j, k, l, n;
+ char **attrs, **exattrs;
+
+ if ( !syncrepl_ov.on_bi.bi_type ) {
+ syncrepl_ov.on_bi.bi_type = "syncrepl";
+ syncrepl_ov.on_bi.bi_op_modify = syncrepl_op_modify;
+ overlay_register( &syncrepl_ov );
+ }
+
+ /* delta-MPR needs the overlay, nothing else does.
+ * This must happen before accesslog overlay is configured.
+ */
+ if ( si->si_syncdata &&
+ !overlay_is_inst( si->si_be, syncrepl_ov.on_bi.bi_type )) {
+ overlay_config( si->si_be, syncrepl_ov.on_bi.bi_type, -1, NULL, NULL );
+ if ( !ad_reqMod ) {
+ const char *text;
+ logschema *ls = &accesslog_sc;
+
+ slap_bv2ad( &ls->ls_mod, &ad_reqMod, &text );
+ slap_bv2ad( &ls->ls_dn, &ad_reqDN, &text );
+ }
+ }
+
+ if ( !sync_descs[0] ) {
+ sync_descs[0] = slap_schema.si_ad_objectClass;
+ sync_descs[1] = slap_schema.si_ad_structuralObjectClass;
+ sync_descs[2] = slap_schema.si_ad_entryCSN;
+ sync_descs[3] = NULL;
+ }
+
+ if ( si->si_syncdata == SYNCDATA_CHANGELOG ) {
+ /* DSEE doesn't support allopattrs */
+ si->si_allopattrs = 0;
+ if ( !dsee_descs[0] ) {
+ dsee_descs[0] = slap_schema.si_ad_objectClass;
+ dsee_descs[1] = slap_schema.si_ad_creatorsName;
+ dsee_descs[2] = slap_schema.si_ad_createTimestamp;
+ dsee_descs[3] = slap_schema.si_ad_modifiersName;
+ dsee_descs[4] = slap_schema.si_ad_modifyTimestamp;
+ dsee_descs[5] = sy_ad_nsUniqueId;
+ dsee_descs[6] = NULL;
+ }
+ }
+
+ if ( si->si_allattrs && si->si_allopattrs )
+ attrs = NULL;
+ else
+ attrs = anlist2attrs( si->si_anlist );
+
+ if ( attrs ) {
+ if ( si->si_allattrs ) {
+ i = 0;
+ while ( attrs[i] ) {
+ if ( !is_at_operational( at_find( attrs[i] ) ) ) {
+ for ( j = i; attrs[j] != NULL; j++ ) {
+ if ( j == i )
+ ch_free( attrs[i] );
+ attrs[j] = attrs[j+1];
+ }
+ } else {
+ i++;
+ }
+ }
+ attrs = ( char ** ) ch_realloc( attrs, (i + 2)*sizeof( char * ) );
+ attrs[i] = ch_strdup("*");
+ attrs[i + 1] = NULL;
+
+ } else if ( si->si_allopattrs ) {
+ i = 0;
+ while ( attrs[i] ) {
+ if ( is_at_operational( at_find( attrs[i] ) ) ) {
+ for ( j = i; attrs[j] != NULL; j++ ) {
+ if ( j == i )
+ ch_free( attrs[i] );
+ attrs[j] = attrs[j+1];
+ }
+ } else {
+ i++;
+ }
+ }
+ attrs = ( char ** ) ch_realloc( attrs, (i + 2)*sizeof( char * ) );
+ attrs[i] = ch_strdup("+");
+ attrs[i + 1] = NULL;
+ }
+
+ for ( i = 0; sync_descs[i] != NULL; i++ ) {
+ j = 0;
+ while ( attrs[j] ) {
+ if ( !strcmp( attrs[j], sync_descs[i]->ad_cname.bv_val ) ) {
+ for ( k = j; attrs[k] != NULL; k++ ) {
+ if ( k == j )
+ ch_free( attrs[k] );
+ attrs[k] = attrs[k+1];
+ }
+ } else {
+ j++;
+ }
+ }
+ }
+
+ for ( n = 0; attrs[ n ] != NULL; n++ ) /* empty */;
+
+ if ( si->si_allopattrs ) {
+ attrs = ( char ** ) ch_realloc( attrs, (n + 2)*sizeof( char * ) );
+ } else {
+ attrs = ( char ** ) ch_realloc( attrs, (n + 4)*sizeof( char * ) );
+ }
+
+ /* Add Attributes */
+ if ( si->si_allopattrs ) {
+ attrs[n++] = ch_strdup( sync_descs[0]->ad_cname.bv_val );
+ } else {
+ if ( si->si_syncdata != SYNCDATA_CHANGELOG ) {
+ for ( i = 0; sync_descs[ i ] != NULL; i++ ) {
+ attrs[ n++ ] = ch_strdup ( sync_descs[i]->ad_cname.bv_val );
+ }
+ }
+ }
+ attrs[ n ] = NULL;
+
+ } else {
+
+ i = 0;
+ if ( si->si_allattrs == si->si_allopattrs ) {
+ attrs = (char**) ch_malloc( 3 * sizeof(char*) );
+ attrs[i++] = ch_strdup( "*" );
+ attrs[i++] = ch_strdup( "+" );
+ si->si_allattrs = si->si_allopattrs = 1;
+ } else if ( si->si_allattrs && !si->si_allopattrs ) {
+ for ( n = 0; sync_descs[ n ] != NULL; n++ ) ;
+ attrs = (char**) ch_malloc( (n+1)* sizeof(char*) );
+ attrs[i++] = ch_strdup( "*" );
+ for ( j = 1; sync_descs[ j ] != NULL; j++ ) {
+ attrs[i++] = ch_strdup ( sync_descs[j]->ad_cname.bv_val );
+ }
+ } else if ( !si->si_allattrs && si->si_allopattrs ) {
+ attrs = (char**) ch_malloc( 3 * sizeof(char*) );
+ attrs[i++] = ch_strdup( "+" );
+ attrs[i++] = ch_strdup( sync_descs[0]->ad_cname.bv_val );
+ }
+ attrs[i] = NULL;
+ }
+
+ if ( si->si_syncdata == SYNCDATA_CHANGELOG ) {
+ for ( n = 0; attrs[ n ] != NULL; n++ ) /* empty */;
+ attrs = ( char ** ) ch_realloc( attrs, (n + 6)*sizeof( char * ) );
+ for ( i = 0; dsee_descs[ i ] != NULL; i++ ) {
+ attrs[ n++ ] = ch_strdup ( dsee_descs[i]->ad_cname.bv_val );
+ }
+ attrs[n] = NULL;
+ }
+
+ si->si_attrs = attrs;
+
+ exattrs = anlist2attrs( si->si_exanlist );
+
+ if ( exattrs ) {
+ for ( n = 0; exattrs[n] != NULL; n++ ) ;
+
+ for ( i = 0; sync_descs[i] != NULL; i++ ) {
+ j = 0;
+ while ( exattrs[j] != NULL ) {
+ if ( !strcmp( exattrs[j], sync_descs[i]->ad_cname.bv_val ) ) {
+ ch_free( exattrs[j] );
+ for ( k = j; exattrs[k] != NULL; k++ ) {
+ exattrs[k] = exattrs[k+1];
+ }
+ } else {
+ j++;
+ }
+ }
+ }
+
+ for ( i = 0; exattrs[i] != NULL; i++ ) {
+ for ( j = 0; si->si_anlist[j].an_name.bv_val; j++ ) {
+ ObjectClass *oc;
+ if ( ( oc = si->si_anlist[j].an_oc ) ) {
+ k = 0;
+ while ( oc->soc_required[k] ) {
+ if ( !strcmp( exattrs[i],
+ oc->soc_required[k]->sat_cname.bv_val ) ) {
+ ch_free( exattrs[i] );
+ for ( l = i; exattrs[l]; l++ ) {
+ exattrs[l] = exattrs[l+1];
+ }
+ } else {
+ k++;
+ }
+ }
+ }
+ }
+ }
+
+ for ( i = 0; exattrs[i] != NULL; i++ ) ;
+
+ if ( i != n )
+ exattrs = (char **) ch_realloc( exattrs, (i + 1)*sizeof(char *) );
+ }
+
+ si->si_exattrs = exattrs;
+}
+
+static int
+start_refresh(syncinfo_t *si)
+{
+ ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_refresh_mutex );
+ if ( si->si_cookieState->cs_refreshing ) {
+ struct re_s* rtask = si->si_re;
+
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+
+ si->si_paused = 1;
+ Debug( LDAP_DEBUG_SYNC, "start_refresh: %s "
+ "a refresh on %s in progress, pausing\n",
+ si->si_ridtxt, si->si_cookieState->cs_refreshing->si_ridtxt );
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_refresh_mutex );
+ return SYNC_BUSY;
+ }
+ si->si_cookieState->cs_refreshing = si;
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_refresh_mutex );
+
+ return LDAP_SUCCESS;
+}
+
+static int
+refresh_finished(syncinfo_t *si)
+{
+ syncinfo_t *sie;
+ int removed = 0;
+
+ ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_refresh_mutex );
+ if ( si->si_cookieState->cs_refreshing == si ) {
+ si->si_cookieState->cs_refreshing = NULL;
+ removed = 1;
+ }
+
+ if ( removed ) {
+ for ( sie = si->si_be->be_syncinfo; sie; sie = sie->si_next ) {
+ if ( sie->si_paused ) {
+ struct re_s* rtask = sie->si_re;
+
+ Debug( LDAP_DEBUG_SYNC, "refresh_finished: %s "
+ "rescheduling refresh on %s\n",
+ si->si_ridtxt, sie->si_ridtxt );
+ sie->si_paused = 0;
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ rtask->interval.tv_sec = 0;
+ ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 );
+ rtask->interval.tv_sec = si->si_interval;
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ break;
+ }
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_refresh_mutex );
+ return removed;
+}
+
+static struct berval generic_filterstr = BER_BVC("(objectclass=*)");
+
+static int
+ldap_sync_search(
+ syncinfo_t *si,
+ void *ctx )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ LDAPControl c[3], *ctrls[4];
+ int rc;
+ int rhint;
+ char *base;
+ char **attrs, *lattrs[9];
+ char *filter;
+ int attrsonly;
+ int scope;
+ char filterbuf[sizeof("(changeNumber>=18446744073709551615)")];
+
+ /* setup LDAP SYNC control */
+ ber_init2( ber, NULL, LBER_USE_DER );
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &ctx );
+
+ si->si_msgid = 0;
+
+ /* If we're using a log but we have no state, then fallback to
+ * normal mode for a full refresh.
+ */
+ if ( si->si_syncdata ) {
+ if ( si->si_syncdata == SYNCDATA_CHANGELOG ) {
+ LDAPMessage *res, *msg;
+ unsigned long first = 0, last = 0;
+ int gotfirst = 0, gotlast = 0;
+
+ if ( (rc = start_refresh( si )) ) {
+ return rc;
+ }
+
+ /* See if we're new enough for the remote server */
+ lattrs[0] = "firstchangenumber";
+ lattrs[1] = "lastchangenumber";
+ lattrs[2] = NULL;
+ rc = ldap_search_ext_s( si->si_ld, "", LDAP_SCOPE_BASE, generic_filterstr.bv_val, lattrs, 0,
+ NULL, NULL, NULL, si->si_slimit, &res );
+ if ( rc )
+ return rc;
+ msg = ldap_first_message( si->si_ld, res );
+ if ( msg && ldap_msgtype( msg ) == LDAP_RES_SEARCH_ENTRY ) {
+ BerElement *ber = NULL;
+ struct berval bv, *bvals, **bvp = &bvals;;
+ rc = ldap_get_dn_ber( si->si_ld, msg, &ber, &bv );
+ for ( rc = ldap_get_attribute_ber( si->si_ld, msg, ber, &bv, bvp );
+ rc == LDAP_SUCCESS;
+ rc = ldap_get_attribute_ber( si->si_ld, msg, ber, &bv, bvp ) ) {
+ if ( bv.bv_val == NULL )
+ break;
+ if ( !strcasecmp( bv.bv_val, "firstchangenumber" )) {
+ first = strtoul( bvals[0].bv_val, NULL, 0 );
+ gotfirst = 1;
+ } else if ( !strcasecmp( bv.bv_val, "lastchangenumber" )) {
+ last = strtoul( bvals[0].bv_val, NULL, 0 );
+ gotlast = 1;
+ }
+ }
+ }
+ ldap_msgfree( res );
+ if ( gotfirst && gotlast ) {
+ if ( si->si_lastchange < first || (!si->si_lastchange && !si->si_refreshDone ))
+ si->si_logstate = SYNCLOG_FALLBACK;
+ /* if we're in logging mode, it will update si_lastchange itself */
+ if ( si->si_logstate == SYNCLOG_FALLBACK )
+ si->si_lastchange = last;
+ } else {
+ /* should be an error; changelog plugin not enabled on provider */
+ si->si_logstate = SYNCLOG_FALLBACK;
+ }
+ } else
+ if ( si->si_logstate == SYNCLOG_LOGGING && !si->si_syncCookie.numcsns &&
+ !si->si_refreshDone ) {
+ si->si_logstate = SYNCLOG_FALLBACK;
+ }
+ }
+
+ /* Use the log parameters if we're in log mode */
+ if ( si->si_syncdata && si->si_logstate == SYNCLOG_LOGGING ) {
+ logschema *ls;
+ if ( si->si_syncdata == SYNCDATA_ACCESSLOG )
+ ls = &accesslog_sc;
+ else
+ ls = &changelog_sc;
+ lattrs[0] = ls->ls_dn.bv_val;
+ lattrs[1] = ls->ls_req.bv_val;
+ lattrs[2] = ls->ls_mod.bv_val;
+ lattrs[3] = ls->ls_newRdn.bv_val;
+ lattrs[4] = ls->ls_delRdn.bv_val;
+ lattrs[5] = ls->ls_newSup.bv_val;
+ if ( si->si_syncdata == SYNCDATA_ACCESSLOG ) {
+ lattrs[6] = ls->ls_controls.bv_val;
+ lattrs[7] = slap_schema.si_ad_entryCSN->ad_cname.bv_val;
+ lattrs[8] = NULL;
+ filter = si->si_logfilterstr.bv_val;
+ scope = LDAP_SCOPE_SUBTREE;
+ } else {
+ lattrs[6] = ls->ls_uuid.bv_val;
+ lattrs[7] = ls->ls_changenum.bv_val;
+ lattrs[8] = NULL;
+ sprintf( filterbuf, "(changeNumber>=%lu)", si->si_lastchange+1 );
+ filter = filterbuf;
+ scope = LDAP_SCOPE_ONELEVEL;
+ }
+
+ rhint = 0;
+ base = si->si_logbase.bv_val;
+ attrs = lattrs;
+ attrsonly = 0;
+ } else {
+ if ( (rc = start_refresh( si )) ) {
+ return rc;
+ }
+
+ rhint = 1;
+ base = si->si_base.bv_val;
+ filter = si->si_filterstr.bv_val;
+ attrs = si->si_attrs;
+ attrsonly = si->si_attrsonly;
+ scope = si->si_scope;
+ }
+ if ( si->si_syncdata && si->si_logstate == SYNCLOG_FALLBACK ) {
+ si->si_type = LDAP_SYNC_REFRESH_ONLY;
+ } else {
+ si->si_type = si->si_ctype;
+ }
+
+#ifdef LDAP_CONTROL_X_DIRSYNC
+ if ( si->si_ctype == MSAD_DIRSYNC ) {
+ ber_printf( ber, "{iiO}", LDAP_CONTROL_X_DIRSYNC_INCREMENTAL_VALUES, 0, &si->si_dirSyncCookie );
+
+ if ( (rc = ber_flatten2( ber, &c[0].ldctl_value, 0 ) ) == -1 ) {
+ ber_free_buf( ber );
+ return rc;
+ }
+ c[0].ldctl_oid = LDAP_CONTROL_X_DIRSYNC;
+ c[0].ldctl_iscritical = 1;
+ ctrls[0] = &c[0];
+
+ if ( !BER_BVISEMPTY( &si->si_dirSyncCookie )) {
+ c[1].ldctl_oid = LDAP_CONTROL_X_SHOW_DELETED;
+ BER_BVZERO( &c[1].ldctl_value );
+ c[1].ldctl_iscritical = 1;
+ ctrls[1] = &c[1];
+ ctrls[2] = NULL;
+ } else {
+ ctrls[1] = NULL;
+ }
+ } else
+#endif
+ if ( si->si_syncdata == SYNCDATA_CHANGELOG ) {
+ if ( si->si_logstate == SYNCLOG_LOGGING && si->si_type == LDAP_SYNC_REFRESH_AND_PERSIST ) {
+ c[0].ldctl_oid = LDAP_CONTROL_PERSIST_REQUEST;
+ c[0].ldctl_iscritical = 0;
+ rc = ldap_create_persistentsearch_control_value( si->si_ld, LDAP_CONTROL_PERSIST_ENTRY_CHANGE_ADD,
+ 0, 1, &c[0].ldctl_value );
+ ctrls[0] = &c[0];
+ ctrls[1] = NULL;
+ } else {
+ ctrls[0] = NULL;
+ }
+ } else
+ {
+ if ( !BER_BVISNULL( &si->si_syncCookie.octet_str ) )
+ {
+ ber_printf( ber, "{eOb}",
+ abs(si->si_type), &si->si_syncCookie.octet_str, rhint );
+ } else {
+ ber_printf( ber, "{eb}",
+ abs(si->si_type), rhint );
+ }
+
+ if ( (rc = ber_flatten2( ber, &c[0].ldctl_value, 0 ) ) == -1 ) {
+ ber_free_buf( ber );
+ return rc;
+ }
+
+ c[0].ldctl_oid = LDAP_CONTROL_SYNC;
+ c[0].ldctl_iscritical = si->si_type < 0;
+ ctrls[0] = &c[0];
+
+ c[1].ldctl_oid = LDAP_CONTROL_MANAGEDSAIT;
+ BER_BVZERO( &c[1].ldctl_value );
+ c[1].ldctl_iscritical = 1;
+ ctrls[1] = &c[1];
+
+ if ( !BER_BVISNULL( &si->si_bindconf.sb_authzId ) ) {
+ c[2].ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
+ c[2].ldctl_value = si->si_bindconf.sb_authzId;
+ c[2].ldctl_iscritical = 1;
+ ctrls[2] = &c[2];
+ ctrls[3] = NULL;
+ } else {
+ ctrls[2] = NULL;
+ }
+ }
+
+ si->si_refreshDone = 0;
+ si->si_refreshPresent = 0;
+ si->si_refreshDelete = 0;
+
+ rc = ldap_search_ext( si->si_ld, base, scope, filter, attrs, attrsonly,
+ ctrls, NULL, NULL, si->si_slimit, &si->si_msgid );
+ ber_free_buf( ber );
+ return rc;
+}
+
+/* #define DEBUG_MERGE_STATE 1 */
+
+static int
+merge_state( syncinfo_t *si, struct sync_cookie *sc1, struct sync_cookie *sc2 )
+{
+ int i, j, k, changed = 0;
+ int ei, ej;
+ int *newsids;
+ struct berval *newcsns;
+
+ ei = sc1->numcsns;
+ ej = sc2->numcsns;
+#ifdef DEBUG_MERGE_STATE
+ for ( i=0; i<ei; i++ ) {
+ fprintf(stderr, "merge_state: %s si_syncCookie [%d] %d %s\n",
+ si->si_ridtxt, i, sc1->sids[i], sc1->ctxcsn[i].bv_val );
+ }
+ for ( i=0; i<ej; i++ ) {
+ fprintf(stderr, "merge_state: %s si_cookieState [%d] %d %s\n",
+ si->si_ridtxt, i, sc2->sids[i], sc2->ctxcsn[i].bv_val );
+ }
+#endif
+ /* see if they cover the same SIDs */
+ if ( ei == ej ) {
+ for ( i = 0; i < ei; i++ ) {
+ if ( sc1->sids[i] != sc2->sids[i] ) {
+ changed = 1;
+ break;
+ }
+ }
+ /* SIDs are the same, take fast path */
+ if ( !changed ) {
+ for ( i = 0; i < ei; i++ ) {
+ if ( ber_bvcmp( &sc1->ctxcsn[i], &sc2->ctxcsn[i] ) < 0 ) {
+ ber_bvreplace( &sc1->ctxcsn[i], &sc2->ctxcsn[i] );
+ changed = 1;
+ }
+ }
+ return changed;
+ }
+ changed = 0;
+ }
+
+ i = ei + ej;
+ newsids = ch_malloc( sizeof(int) * i );
+ newcsns = ch_malloc( sizeof(struct berval) * ( i + 1 ));
+
+ for ( i=0, j=0, k=0; i < ei || j < ej ; ) {
+ if ( i < ei && sc1->sids[i] == -1 ) {
+ i++;
+ continue;
+ }
+ if ( j >= ej || (i < ei && sc1->sids[i] < sc2->sids[j] )) {
+ newsids[k] = sc1->sids[i];
+ ber_dupbv( &newcsns[k], &sc1->ctxcsn[i] );
+ i++; k++;
+ continue;
+ }
+ if ( i < ei && sc1->sids[i] == sc2->sids[j] ) {
+ newsids[k] = sc1->sids[i];
+ if ( ber_bvcmp( &sc1->ctxcsn[i], &sc2->ctxcsn[j] ) < 0 ) {
+ changed = 1;
+ ber_dupbv( &newcsns[k], &sc2->ctxcsn[j] );
+ } else {
+ ber_dupbv( &newcsns[k], &sc1->ctxcsn[i] );
+ }
+ i++; j++; k++;
+ continue;
+ }
+ if ( j < ej ) {
+ if ( sc2->sids[j] == -1 ) {
+ j++;
+ continue;
+ }
+ newsids[k] = sc2->sids[j];
+ ber_dupbv( &newcsns[k], &sc2->ctxcsn[j] );
+ changed = 1;
+ j++; k++;
+ }
+ }
+
+ ber_bvarray_free( sc1->ctxcsn );
+ ch_free( sc1->sids );
+ sc1->numcsns = k;
+ sc1->sids = ch_realloc( newsids, sizeof(int) * k );
+ sc1->ctxcsn = ch_realloc( newcsns, sizeof(struct berval) * (k+1) );
+ BER_BVZERO( &sc1->ctxcsn[k] );
+#ifdef DEBUG_MERGE_STATE
+ for ( i=0; i<sc1->numcsns; i++ ) {
+ fprintf(stderr, "merge_state: %s si_syncCookie2 [%d] %d %s\n",
+ si->si_ridtxt, i, sc1->sids[i], sc1->ctxcsn[i].bv_val );
+ }
+#endif
+
+ return changed;
+}
+
+#ifdef DEBUG_MERGE_STATE
+static void
+merge_test( syncinfo_t *si ) {
+ struct sync_cookie sc1, sc2;
+ int ret;
+
+ sc1.numcsns = 4;
+ sc1.sids = malloc( sizeof( int ) * sc1.numcsns );
+ sc1.ctxcsn = malloc( sizeof( struct berval ) * ( sc1.numcsns + 1 ));
+ sc1.sids[0] = 1;
+ sc1.sids[1] = 3;
+ sc1.sids[2] = 4;
+ sc1.sids[3] = 5;
+ { struct berval bv = BER_BVC("20200101000000.100000Z#sc1#001#000000"); /* unique */
+ ber_dupbv( &sc1.ctxcsn[0], &bv ); }
+ { struct berval bv = BER_BVC("20200101000000.100000Z#sc1#003#000000"); /* lower */
+ ber_dupbv( &sc1.ctxcsn[1], &bv ); }
+ { struct berval bv = BER_BVC("20201231000000.100000Z#sc1#004#000000"); /* higher */
+ ber_dupbv( &sc1.ctxcsn[2], &bv ); }
+ { struct berval bv = BER_BVC("20200228000000.100000Z#sc1#005#000000"); /* unique */
+ ber_dupbv( &sc1.ctxcsn[3], &bv ); }
+ BER_BVZERO( &sc1.ctxcsn[sc1.numcsns] );
+
+ sc2.numcsns = 4;
+ sc2.sids = malloc( sizeof( int ) * sc2.numcsns );
+ sc2.ctxcsn = malloc( sizeof( struct berval ) * ( sc2.numcsns + 1 ));
+ sc2.sids[0] = 2;
+ sc2.sids[1] = 3;
+ sc2.sids[2] = 4;
+ sc2.sids[3] = 6;
+ { struct berval bv = BER_BVC("20200101000000.100000Z#sc2#002#000000"); /* unique */
+ ber_dupbv( &sc2.ctxcsn[0], &bv ); }
+ { struct berval bv = BER_BVC("20200331000000.100000Z#sc2#003#000000"); /* higher */
+ ber_dupbv( &sc2.ctxcsn[1], &bv ); }
+ { struct berval bv = BER_BVC("20200501000000.100000Z#sc2#004#000000"); /* lower */
+ ber_dupbv( &sc2.ctxcsn[2], &bv ); }
+ { struct berval bv = BER_BVC("20200628000000.100000Z#sc2#006#000000"); /* unique */
+ ber_dupbv( &sc2.ctxcsn[3], &bv ); }
+ BER_BVZERO( &sc2.ctxcsn[sc2.numcsns] );
+
+ ret = merge_state( si, &sc1, &sc2 );
+}
+#endif
+
+static int
+check_syncprov(
+ Operation *op,
+ syncinfo_t *si )
+{
+ AttributeName at[2];
+ Attribute a = {0};
+ Entry e = {0};
+ SlapReply rs = {REP_SEARCH};
+ int i, j, changed = 0;
+
+ /* Look for contextCSN from syncprov overlay. If
+ * there's no overlay, this will be a no-op. That means
+ * this is a pure consumer, so local changes will not be
+ * allowed, and all changes will already be reflected in
+ * the cookieState.
+ */
+ a.a_desc = slap_schema.si_ad_contextCSN;
+ e.e_attrs = &a;
+ e.e_name = si->si_contextdn;
+ e.e_nname = si->si_contextdn;
+ at[0].an_name = a.a_desc->ad_cname;
+ at[0].an_desc = a.a_desc;
+ BER_BVZERO( &at[1].an_name );
+ rs.sr_entry = &e;
+ rs.sr_flags = REP_ENTRY_MODIFIABLE;
+ rs.sr_attrs = at;
+ op->o_req_dn = e.e_name;
+ op->o_req_ndn = e.e_nname;
+
+ ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_mutex );
+ i = backend_operational( op, &rs );
+ if ( i == LDAP_SUCCESS && a.a_nvals ) {
+ int num = a.a_numvals;
+ /* check for differences */
+ if ( num != si->si_cookieState->cs_num ) {
+ changed = 1;
+ } else {
+ for ( i=0; i<num; i++ ) {
+ if ( ber_bvcmp( &a.a_nvals[i],
+ &si->si_cookieState->cs_vals[i] )) {
+ changed = 1;
+ break;
+ }
+ }
+ }
+ if ( changed ) {
+ ber_bvarray_free( si->si_cookieState->cs_vals );
+ ch_free( si->si_cookieState->cs_sids );
+ si->si_cookieState->cs_num = num;
+ si->si_cookieState->cs_vals = a.a_nvals;
+ si->si_cookieState->cs_sids = slap_parse_csn_sids( a.a_nvals,
+ num, NULL );
+ si->si_cookieState->cs_age++;
+ } else {
+ ber_bvarray_free( a.a_nvals );
+ }
+ ber_bvarray_free( a.a_vals );
+ }
+ /* See if the cookieState has changed due to anything outside
+ * this particular consumer. That includes other consumers in
+ * the same context, or local changes detected above.
+ */
+ if ( si->si_cookieState->cs_num > 0 && si->si_cookieAge !=
+ si->si_cookieState->cs_age ) {
+ if ( !si->si_syncCookie.numcsns ) {
+ ber_bvarray_free( si->si_syncCookie.ctxcsn );
+ ber_bvarray_dup_x( &si->si_syncCookie.ctxcsn,
+ si->si_cookieState->cs_vals, NULL );
+ changed = 1;
+ } else {
+ changed = merge_state( si, &si->si_syncCookie,
+ (struct sync_cookie *)&si->si_cookieState->cs_vals );
+ }
+ }
+ if ( changed ) {
+ si->si_cookieAge = si->si_cookieState->cs_age;
+ ch_free( si->si_syncCookie.octet_str.bv_val );
+ slap_compose_sync_cookie( NULL, &si->si_syncCookie.octet_str,
+ si->si_syncCookie.ctxcsn, si->si_syncCookie.rid,
+ si->si_syncCookie.sid, NULL );
+ ch_free( si->si_syncCookie.sids );
+ slap_reparse_sync_cookie( &si->si_syncCookie, op->o_tmpmemctx );
+ }
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
+ return changed;
+}
+
+static int
+do_syncrep1(
+ Operation *op,
+ syncinfo_t *si )
+{
+ int rc;
+ int cmdline_cookie_found = 0;
+
+ struct sync_cookie *sc = NULL;
+#ifdef HAVE_TLS
+ void *ssl;
+#endif
+
+ si->si_lastconnect = slap_get_time();
+ rc = slap_client_connect( &si->si_ld, &si->si_bindconf );
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+ op->o_protocol = LDAP_VERSION3;
+
+ /* Set SSF to strongest of TLS, SASL SSFs */
+ op->o_sasl_ssf = 0;
+ op->o_tls_ssf = 0;
+ op->o_transport_ssf = 0;
+#ifdef HAVE_TLS
+ if ( ldap_get_option( si->si_ld, LDAP_OPT_X_TLS_SSL_CTX, &ssl )
+ == LDAP_SUCCESS && ssl != NULL )
+ {
+ op->o_tls_ssf = ldap_pvt_tls_get_strength( ssl );
+ }
+#endif /* HAVE_TLS */
+ {
+ ber_len_t ssf; /* ITS#5403, 3864 LDAP_OPT_X_SASL_SSF probably ought
+ to use sasl_ssf_t but currently uses ber_len_t */
+ if ( ldap_get_option( si->si_ld, LDAP_OPT_X_SASL_SSF, &ssf )
+ == LDAP_SUCCESS )
+ op->o_sasl_ssf = ssf;
+ }
+ op->o_ssf = ( op->o_sasl_ssf > op->o_tls_ssf )
+ ? op->o_sasl_ssf : op->o_tls_ssf;
+
+ ldap_set_option( si->si_ld, LDAP_OPT_TIMELIMIT, &si->si_tlimit );
+
+ rc = LDAP_DEREF_NEVER; /* actually could allow DEREF_FINDING */
+ ldap_set_option( si->si_ld, LDAP_OPT_DEREF, &rc );
+
+ ldap_set_option( si->si_ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF );
+
+ si->si_syncCookie.rid = si->si_rid;
+
+ /* whenever there are multiple data sources possible, advertise sid */
+ si->si_syncCookie.sid = ( SLAP_MULTIPROVIDER( si->si_be ) || si->si_be != si->si_wbe ) ?
+ slap_serverID : -1;
+
+#ifdef LDAP_CONTROL_X_DIRSYNC
+ if ( si->si_ctype == MSAD_DIRSYNC ) {
+ if ( BER_BVISEMPTY( &si->si_dirSyncCookie )) {
+ BerVarray cookies = NULL;
+ void *ctx = op->o_tmpmemctx;
+
+ op->o_req_ndn = si->si_contextdn;
+ op->o_req_dn = op->o_req_ndn;
+
+ /* try to read stored cookie */
+ op->o_tmpmemctx = NULL;
+ backend_attribute( op, NULL, &op->o_req_ndn,
+ sy_ad_dirSyncCookie, &cookies, ACL_READ );
+ op->o_tmpmemctx = ctx;
+ if ( cookies )
+ si->si_dirSyncCookie = cookies[0];
+ }
+ } else
+#endif
+ if ( si->si_syncdata == SYNCDATA_CHANGELOG ) {
+ if ( !si->si_lastchange ) {
+ BerVarray vals = NULL;
+
+ op->o_req_ndn = si->si_contextdn;
+ op->o_req_dn = op->o_req_ndn;
+ /* try to read last change number */
+ backend_attribute( op, NULL, &op->o_req_ndn,
+ sy_ad_dseeLastChange, &vals, ACL_READ );
+ if ( vals ) {
+ si->si_lastchange = strtoul( vals[0].bv_val, NULL, 0 );
+ si->si_prevchange = si->si_lastchange;
+ }
+ }
+ } else
+ {
+
+ /* We've just started up, or the remote server hasn't sent us
+ * any meaningful state.
+ */
+ if ( !si->si_syncCookie.ctxcsn ) {
+ int i;
+
+ LDAP_STAILQ_FOREACH( sc, &slap_sync_cookie, sc_next ) {
+ if ( si->si_rid == sc->rid ) {
+ cmdline_cookie_found = 1;
+ break;
+ }
+ }
+
+ if ( cmdline_cookie_found ) {
+ /* cookie is supplied in the command line */
+
+ LDAP_STAILQ_REMOVE( &slap_sync_cookie, sc, sync_cookie, sc_next );
+
+ slap_sync_cookie_free( &si->si_syncCookie, 0 );
+ si->si_syncCookie.octet_str = sc->octet_str;
+ ch_free( sc );
+ /* ctxcsn wasn't parsed yet, do it now */
+ slap_parse_sync_cookie( &si->si_syncCookie, NULL );
+ } else {
+ ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_mutex );
+ if ( !si->si_cookieState->cs_num ) {
+ /* get contextCSN shadow replica from database */
+ BerVarray csn = NULL;
+ void *ctx = op->o_tmpmemctx;
+
+ op->o_req_ndn = si->si_contextdn;
+ op->o_req_dn = op->o_req_ndn;
+
+ /* try to read stored contextCSN */
+ op->o_tmpmemctx = NULL;
+ backend_attribute( op, NULL, &op->o_req_ndn,
+ slap_schema.si_ad_contextCSN, &csn, ACL_READ );
+ op->o_tmpmemctx = ctx;
+ if ( csn ) {
+ si->si_cookieState->cs_vals = csn;
+ for (i=0; !BER_BVISNULL( &csn[i] ); i++);
+ si->si_cookieState->cs_num = i;
+ si->si_cookieState->cs_sids = slap_parse_csn_sids( csn, i, NULL );
+ slap_sort_csn_sids( csn, si->si_cookieState->cs_sids, i, NULL );
+ }
+ }
+ if ( si->si_cookieState->cs_num ) {
+ ber_bvarray_free( si->si_syncCookie.ctxcsn );
+ if ( ber_bvarray_dup_x( &si->si_syncCookie.ctxcsn,
+ si->si_cookieState->cs_vals, NULL )) {
+ rc = LDAP_NO_MEMORY;
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
+ goto done;
+ }
+ si->si_syncCookie.numcsns = si->si_cookieState->cs_num;
+ si->si_syncCookie.sids = ch_malloc( si->si_cookieState->cs_num *
+ sizeof(int) );
+ for ( i=0; i<si->si_syncCookie.numcsns; i++ )
+ si->si_syncCookie.sids[i] = si->si_cookieState->cs_sids[i];
+ }
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
+ }
+ }
+
+ if ( !cmdline_cookie_found ) {
+ /* ITS#6367: recreate the cookie so it has our SID, not our peer's */
+ ch_free( si->si_syncCookie.octet_str.bv_val );
+ BER_BVZERO( &si->si_syncCookie.octet_str );
+ /* Look for contextCSN from syncprov overlay. */
+ check_syncprov( op, si );
+ if ( BER_BVISNULL( &si->si_syncCookie.octet_str ))
+ slap_compose_sync_cookie( NULL, &si->si_syncCookie.octet_str,
+ si->si_syncCookie.ctxcsn, si->si_syncCookie.rid,
+ si->si_syncCookie.sid, NULL );
+ }
+ }
+
+ Debug( LDAP_DEBUG_SYNC, "do_syncrep1: %s starting refresh (sending cookie=%s)\n",
+ si->si_ridtxt, si->si_syncCookie.octet_str.bv_val ?
+ si->si_syncCookie.octet_str.bv_val : "" );
+
+ if ( si->si_syncCookie.octet_str.bv_val ) {
+ ldap_pvt_thread_mutex_lock( &si->si_monitor_mutex );
+ ber_bvreplace( &si->si_lastCookieSent, &si->si_syncCookie.octet_str );
+ ldap_pvt_thread_mutex_unlock( &si->si_monitor_mutex );
+ }
+
+ rc = ldap_sync_search( si, op->o_tmpmemctx );
+
+ if ( rc == SYNC_BUSY ) {
+ return rc;
+ } else if ( rc != LDAP_SUCCESS ) {
+ refresh_finished( si );
+ Debug( LDAP_DEBUG_ANY, "do_syncrep1: %s "
+ "ldap_search_ext: %s (%d)\n",
+ si->si_ridtxt, ldap_err2string( rc ), rc );
+ }
+
+done:
+ if ( rc ) {
+ if ( si->si_ld ) {
+ ldap_unbind_ext( si->si_ld, NULL, NULL );
+ si->si_ld = NULL;
+ }
+ }
+
+ return rc;
+}
+
+static int
+compare_csns( struct sync_cookie *sc1, struct sync_cookie *sc2, int *which )
+{
+ int i, j, match = 0;
+ const char *text;
+
+ *which = 0;
+
+ if ( sc1->numcsns < sc2->numcsns ) {
+ *which = sc1->numcsns;
+ return -1;
+ }
+
+ for (j=0; j<sc2->numcsns; j++) {
+ for (i=0; i<sc1->numcsns; i++) {
+ if ( sc1->sids[i] != sc2->sids[j] )
+ continue;
+ value_match( &match, slap_schema.si_ad_entryCSN,
+ slap_schema.si_ad_entryCSN->ad_type->sat_ordering,
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ &sc1->ctxcsn[i], &sc2->ctxcsn[j], &text );
+ if ( match < 0 ) {
+ *which = j;
+ return match;
+ }
+ break;
+ }
+ if ( i == sc1->numcsns ) {
+ /* sc2 has a sid sc1 lacks */
+ *which = j;
+ return -1;
+ }
+ }
+ return match;
+}
+
+#define CV_CSN_OK 0
+#define CV_CSN_OLD 1
+#define CV_SID_NEW 2
+
+static int
+check_csn_age(
+ syncinfo_t *si,
+ struct berval *dn,
+ struct berval *csn,
+ int sid,
+ cookie_vals *cv,
+ int *slot )
+{
+ int i, rc = CV_SID_NEW;
+
+ for ( i =0; i<cv->cv_num; i++ ) {
+#ifdef CHATTY_SYNCLOG
+ Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s CSN for sid %d: %s\n",
+ si->si_ridtxt, i, cv->cv_vals[i].bv_val );
+#endif
+ /* new SID */
+ if ( sid < cv->cv_sids[i] )
+ break;
+ if ( cv->cv_sids[i] == sid ) {
+ if ( ber_bvcmp( csn, &cv->cv_vals[i] ) <= 0 ) {
+ dn->bv_val[dn->bv_len] = '\0';
+ Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s CSN too old, ignoring %s (%s)\n",
+ si->si_ridtxt, csn->bv_val, dn->bv_val );
+ return CV_CSN_OLD;
+ }
+ rc = CV_CSN_OK;
+ break;
+ }
+ }
+ if ( slot )
+ *slot = i;
+ return rc;
+}
+
+static int
+get_pmutex(
+ syncinfo_t *si
+)
+{
+ if ( !si->si_is_configdb ) {
+ ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_pmutex );
+ } else {
+ /* avoid deadlock when replicating cn=config */
+ while ( ldap_pvt_thread_mutex_trylock( &si->si_cookieState->cs_pmutex )) {
+ if ( slapd_shutdown )
+ return SYNC_SHUTDOWN;
+ if ( !ldap_pvt_thread_pool_pausecheck( &connection_pool ))
+ ldap_pvt_thread_yield();
+ }
+ }
+
+ return 0;
+}
+
+static int
+do_syncrep2(
+ Operation *op,
+ syncinfo_t *si )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+
+ LDAPMessage *msg = NULL;
+
+ struct sync_cookie syncCookie = { NULL };
+ struct sync_cookie syncCookie_req = { NULL };
+
+ int rc,
+ err = LDAP_SUCCESS;
+
+ Modifications *modlist = NULL;
+
+ int m;
+
+ struct timeval tout = { 0, 0 };
+
+ int refreshDeletes = 0;
+ int refreshing = !si->si_refreshDone &&
+ !( si->si_syncdata && si->si_logstate == SYNCLOG_LOGGING );
+ char empty[6] = "empty";
+
+ if ( slapd_shutdown ) {
+ rc = SYNC_SHUTDOWN;
+ goto done;
+ }
+
+ ber_init2( ber, NULL, LBER_USE_DER );
+ ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
+
+ Debug( LDAP_DEBUG_TRACE, "=>do_syncrep2 %s\n", si->si_ridtxt );
+
+ slap_dup_sync_cookie( &syncCookie_req, &si->si_syncCookie );
+
+ if ( abs(si->si_type) == LDAP_SYNC_REFRESH_AND_PERSIST && si->si_refreshDone ) {
+ tout.tv_sec = 0;
+ } else {
+ /* Give some time for refresh response to arrive */
+ tout.tv_sec = si->si_bindconf.sb_timeout_api;
+ }
+
+ while ( ( rc = ldap_result( si->si_ld, si->si_msgid, LDAP_MSG_ONE,
+ &tout, &msg ) ) > 0 )
+ {
+ int match, punlock, syncstate;
+ struct berval *retdata, syncUUID[2], cookie = BER_BVNULL;
+ char *retoid;
+ LDAPControl **rctrls = NULL, *rctrlp = NULL;
+ BerVarray syncUUIDs;
+ ber_len_t len;
+ ber_tag_t si_tag;
+ Entry *entry;
+ struct berval bdn;
+
+ if ( slapd_shutdown ) {
+ rc = SYNC_SHUTDOWN;
+ goto done;
+ }
+ si->si_lastcontact = slap_get_time();
+ switch( ldap_msgtype( msg ) ) {
+ case LDAP_RES_SEARCH_ENTRY:
+#ifdef LDAP_CONTROL_X_DIRSYNC
+ if ( si->si_ctype == MSAD_DIRSYNC ) {
+ BER_BVZERO( &syncUUID[0] );
+ rc = syncrepl_dirsync_message( si, op, msg, &modlist, &entry, &syncstate, syncUUID );
+ if ( rc == 0 )
+ rc = syncrepl_entry( si, op, entry, &modlist, syncstate, syncUUID, NULL );
+ op->o_tmpfree( syncUUID[0].bv_val, op->o_tmpmemctx );
+ if ( modlist )
+ slap_mods_free( modlist, 1);
+ if ( rc )
+ goto done;
+ break;
+ }
+#endif
+ punlock = -1;
+ ldap_get_entry_controls( si->si_ld, msg, &rctrls );
+ ldap_get_dn_ber( si->si_ld, msg, NULL, &bdn );
+ if (!bdn.bv_len) {
+ bdn.bv_val = empty;
+ bdn.bv_len = sizeof(empty)-1;
+ }
+ if ( si->si_syncdata == SYNCDATA_CHANGELOG ) {
+ if ( si->si_logstate == SYNCLOG_LOGGING ) {
+ rc = syncrepl_message_to_op( si, op, msg, 1 );
+ if ( rc )
+ goto logerr;
+ if ( si->si_type == LDAP_SYNC_REFRESH_AND_PERSIST && rctrls ) {
+ LDAPControl **next = NULL;
+ /* The notification control is only sent during persist phase */
+ rctrlp = ldap_control_find( LDAP_CONTROL_PERSIST_ENTRY_CHANGE_NOTICE, rctrls, &next );
+ if ( rctrlp ) {
+ if ( si->si_refreshDone )
+ syncrepl_dsee_update( si, op );
+ }
+ }
+
+ } else {
+ syncstate = DSEE_SYNC_ADD;
+ rc = syncrepl_message_to_entry( si, op, msg,
+ &modlist, &entry, syncstate, syncUUID );
+ if ( rc == 0 )
+ rc = syncrepl_entry( si, op, entry, &modlist, syncstate, syncUUID, NULL );
+ op->o_tmpfree( syncUUID[0].bv_val, op->o_tmpmemctx );
+ if ( modlist )
+ slap_mods_free( modlist, 1);
+ }
+ if ( rc )
+ goto done;
+ break;
+ }
+ /* we can't work without the control */
+ if ( rctrls ) {
+ LDAPControl **next = NULL;
+ /* NOTE: make sure we use the right one;
+ * a better approach would be to run thru
+ * the whole list and take care of all */
+ /* NOTE: since we issue the search request,
+ * we should know what controls to expect,
+ * and there should be none apart from the
+ * sync-related control */
+ rctrlp = ldap_control_find( LDAP_CONTROL_SYNC_STATE, rctrls, &next );
+ if ( next && ldap_control_find( LDAP_CONTROL_SYNC_STATE, next, NULL ) )
+ {
+ bdn.bv_val[bdn.bv_len] = '\0';
+ Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
+ "got search entry with multiple "
+ "Sync State control (%s)\n", si->si_ridtxt, bdn.bv_val );
+ ldap_controls_free( rctrls );
+ rc = -1;
+ goto done;
+ }
+ }
+ if ( rctrlp == NULL ) {
+ bdn.bv_val[bdn.bv_len] = '\0';
+ Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
+ "got search entry without "
+ "Sync State control (%s)\n", si->si_ridtxt, bdn.bv_val );
+ rc = -1;
+ goto done;
+ }
+ ber_init2( ber, &rctrlp->ldctl_value, LBER_USE_DER );
+ if ( ber_scanf( ber, "{em" /*"}"*/, &syncstate, &syncUUID[0] )
+ == LBER_ERROR ) {
+ bdn.bv_val[bdn.bv_len] = '\0';
+ Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s malformed message (%s)\n",
+ si->si_ridtxt, bdn.bv_val );
+ ldap_controls_free( rctrls );
+ rc = -1;
+ goto done;
+ }
+ /* FIXME: what if syncUUID is NULL or empty?
+ * (happens with back-sql...) */
+ if ( syncUUID[0].bv_len != UUIDLEN ) {
+ bdn.bv_val[bdn.bv_len] = '\0';
+ Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
+ "got empty or invalid syncUUID with LDAP_SYNC_%s (%s)\n",
+ si->si_ridtxt,
+ syncrepl_state2str( syncstate ), bdn.bv_val );
+ ldap_controls_free( rctrls );
+ rc = -1;
+ goto done;
+ }
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
+ if ( ber_scanf( ber, /*"{"*/ "m}", &cookie ) != LBER_ERROR ) {
+
+ Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s cookie=%s\n",
+ si->si_ridtxt,
+ BER_BVISNULL( &cookie ) ? "" : cookie.bv_val );
+
+ if ( !BER_BVISNULL( &cookie ) ) {
+ ch_free( syncCookie.octet_str.bv_val );
+ ber_dupbv( &syncCookie.octet_str, &cookie );
+
+ ldap_pvt_thread_mutex_lock( &si->si_monitor_mutex );
+ ber_bvreplace( &si->si_lastCookieRcvd, &cookie );
+ ldap_pvt_thread_mutex_unlock( &si->si_monitor_mutex );
+ }
+ if ( !BER_BVISNULL( &syncCookie.octet_str ) )
+ {
+ slap_parse_sync_cookie( &syncCookie, NULL );
+ if ( syncCookie.ctxcsn ) {
+ int i, slot, sid = slap_parse_csn_sid( syncCookie.ctxcsn );
+ check_syncprov( op, si );
+ ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_mutex );
+ i = check_csn_age( si, &bdn, syncCookie.ctxcsn, sid, (cookie_vals *)&si->si_cookieState->cs_vals, NULL );
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
+ if ( i == CV_CSN_OLD ) {
+ si->si_too_old = 1;
+ ldap_controls_free( rctrls );
+ rc = 0;
+ goto done;
+ }
+ si->si_too_old = 0;
+
+ /* check pending CSNs too */
+ if (( rc = get_pmutex( si )))
+ goto done;
+
+ i = check_csn_age( si, &bdn, syncCookie.ctxcsn, sid, (cookie_vals *)&si->si_cookieState->cs_pvals, &slot );
+ if ( i == CV_CSN_OK ) {
+ ber_bvreplace( &si->si_cookieState->cs_pvals[slot],
+ syncCookie.ctxcsn );
+ } else if ( i == CV_CSN_OLD ) {
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_pmutex );
+ ldap_controls_free( rctrls );
+ rc = 0;
+ goto done;
+ } else {
+ /* new SID, add it */
+ slap_insert_csn_sids(
+ (struct sync_cookie *)&si->si_cookieState->cs_pvals,
+ slot, sid, syncCookie.ctxcsn );
+ }
+ assert( punlock < 0 );
+ punlock = slot;
+ } else if (si->si_too_old) {
+ bdn.bv_val[bdn.bv_len] = '\0';
+ Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s CSN too old, ignoring (%s)\n",
+ si->si_ridtxt, bdn.bv_val );
+ ldap_controls_free( rctrls );
+ rc = 0;
+ goto done;
+ }
+ op->o_controls[slap_cids.sc_LDAPsync] = &syncCookie;
+ }
+ }
+ }
+ rc = 0;
+ if ( si->si_syncdata && si->si_logstate == SYNCLOG_LOGGING ) {
+ modlist = NULL;
+ if ( ( rc = syncrepl_message_to_op( si, op, msg, punlock < 0 ) ) == LDAP_SUCCESS &&
+ syncCookie.ctxcsn )
+ {
+ rc = syncrepl_updateCookie( si, op, &syncCookie, 0 );
+ } else
+logerr:
+ switch ( rc ) {
+ case LDAP_ALREADY_EXISTS:
+ case LDAP_NO_SUCH_OBJECT:
+ case LDAP_NO_SUCH_ATTRIBUTE:
+ case LDAP_TYPE_OR_VALUE_EXISTS:
+ case LDAP_NOT_ALLOWED_ON_NONLEAF:
+ rc = LDAP_SYNC_REFRESH_REQUIRED;
+ si->si_logstate = SYNCLOG_FALLBACK;
+ ldap_abandon_ext( si->si_ld, si->si_msgid, NULL, NULL );
+ bdn.bv_val[bdn.bv_len] = '\0';
+ Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s delta-sync lost sync on (%s), switching to REFRESH\n",
+ si->si_ridtxt, bdn.bv_val );
+ if (si->si_strict_refresh) {
+ slap_suspend_listeners();
+ connections_drop();
+ }
+ break;
+ default:
+ break;
+ }
+ } else if ( ( rc = syncrepl_message_to_entry( si, op, msg,
+ &modlist, &entry, syncstate, syncUUID ) ) == LDAP_SUCCESS )
+ {
+ if ( punlock < 0 ) {
+ if (( rc = get_pmutex( si )))
+ goto done;
+ }
+ if ( ( rc = syncrepl_entry( si, op, entry, &modlist,
+ syncstate, syncUUID, syncCookie.ctxcsn ) ) == LDAP_SUCCESS &&
+ syncCookie.ctxcsn )
+ {
+ rc = syncrepl_updateCookie( si, op, &syncCookie, 0 );
+ }
+ if ( punlock < 0 )
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_pmutex );
+ }
+ if ( punlock >= 0 ) {
+ /* on failure, revert pending CSN */
+ if ( rc != LDAP_SUCCESS ) {
+ int i;
+ ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_mutex );
+ for ( i = 0; i<si->si_cookieState->cs_num; i++ ) {
+ if ( si->si_cookieState->cs_sids[i] == si->si_cookieState->cs_psids[punlock] ) {
+ ber_bvreplace( &si->si_cookieState->cs_pvals[punlock],
+ &si->si_cookieState->cs_vals[i] );
+ break;
+ }
+ }
+ if ( i == si->si_cookieState->cs_num )
+ si->si_cookieState->cs_pvals[punlock].bv_val[0] = '\0';
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
+ }
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_pmutex );
+ }
+ ldap_controls_free( rctrls );
+ if ( modlist ) {
+ slap_mods_free( modlist, 1 );
+ }
+ if ( rc )
+ goto done;
+ break;
+
+ case LDAP_RES_SEARCH_REFERENCE:
+ Debug( LDAP_DEBUG_ANY,
+ "do_syncrep2: %s reference received error\n",
+ si->si_ridtxt );
+ break;
+
+ case LDAP_RES_SEARCH_RESULT:
+ Debug( LDAP_DEBUG_SYNC,
+ "do_syncrep2: %s LDAP_RES_SEARCH_RESULT\n",
+ si->si_ridtxt );
+ err = LDAP_OTHER; /* FIXME check parse result properly */
+ ldap_parse_result( si->si_ld, msg, &err, NULL, NULL, NULL,
+ &rctrls, 0 );
+#ifdef LDAP_X_SYNC_REFRESH_REQUIRED
+ if ( err == LDAP_X_SYNC_REFRESH_REQUIRED ) {
+ /* map old result code to registered code */
+ err = LDAP_SYNC_REFRESH_REQUIRED;
+ }
+#endif
+ if ( err == LDAP_SYNC_REFRESH_REQUIRED ) {
+ if ( si->si_logstate == SYNCLOG_LOGGING ) {
+ si->si_logstate = SYNCLOG_FALLBACK;
+ Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s delta-sync lost sync, switching to REFRESH\n",
+ si->si_ridtxt );
+ if (si->si_strict_refresh) {
+ slap_suspend_listeners();
+ connections_drop();
+ }
+ }
+ rc = err;
+ goto done;
+ }
+ if ( err ) {
+ Debug( LDAP_DEBUG_ANY,
+ "do_syncrep2: %s LDAP_RES_SEARCH_RESULT (%d) %s\n",
+ si->si_ridtxt, err, ldap_err2string( err ) );
+ }
+ if ( si->si_syncdata == SYNCDATA_CHANGELOG && err == LDAP_SUCCESS ) {
+ rc = syncrepl_dsee_update( si, op );
+ if ( rc == LDAP_SUCCESS ) {
+ if ( si->si_logstate == SYNCLOG_FALLBACK ) {
+ si->si_logstate = SYNCLOG_LOGGING;
+ si->si_refreshDone = 1;
+ rc = LDAP_SYNC_REFRESH_REQUIRED;
+ } else {
+ rc = SYNC_REPOLL;
+ }
+ }
+ goto done;
+ }
+ if ( rctrls ) {
+ LDAPControl **next = NULL;
+#ifdef LDAP_CONTROL_X_DIRSYNC
+ if ( si->si_ctype == MSAD_DIRSYNC ) {
+ rc = syncrepl_dirsync_cookie( si, op, rctrls );
+ if ( rc == LDAP_SUCCESS )
+ rc = SYNC_REPOLL; /* schedule a re-poll */
+ goto done;
+ }
+#endif
+ /* NOTE: make sure we use the right one;
+ * a better approach would be to run thru
+ * the whole list and take care of all */
+ /* NOTE: since we issue the search request,
+ * we should know what controls to expect,
+ * and there should be none apart from the
+ * sync-related control */
+ rctrlp = ldap_control_find( LDAP_CONTROL_SYNC_DONE, rctrls, &next );
+ if ( next && ldap_control_find( LDAP_CONTROL_SYNC_DONE, next, NULL ) )
+ {
+ Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
+ "got search result with multiple "
+ "Sync State control\n", si->si_ridtxt );
+ ldap_controls_free( rctrls );
+ rc = SYNC_ERROR;
+ goto done;
+ }
+ }
+ if ( rctrlp ) {
+ ber_init2( ber, &rctrlp->ldctl_value, LBER_USE_DER );
+
+ ber_scanf( ber, "{" /*"}"*/);
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
+ ber_scanf( ber, "m", &cookie );
+
+ Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s cookie=%s\n",
+ si->si_ridtxt,
+ BER_BVISNULL( &cookie ) ? "" : cookie.bv_val );
+
+ if ( !BER_BVISNULL( &cookie ) ) {
+ ch_free( syncCookie.octet_str.bv_val );
+ ber_dupbv( &syncCookie.octet_str, &cookie);
+
+ ldap_pvt_thread_mutex_lock( &si->si_monitor_mutex );
+ ber_bvreplace( &si->si_lastCookieRcvd, &cookie );
+ ldap_pvt_thread_mutex_unlock( &si->si_monitor_mutex );
+ }
+ if ( !BER_BVISNULL( &syncCookie.octet_str ) )
+ {
+ slap_parse_sync_cookie( &syncCookie, NULL );
+ op->o_controls[slap_cids.sc_LDAPsync] = &syncCookie;
+ }
+ }
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES )
+ {
+ ber_scanf( ber, "b", &refreshDeletes );
+ }
+ ber_scanf( ber, /*"{"*/ "}" );
+ }
+ if ( SLAP_MULTIPROVIDER( op->o_bd ) && check_syncprov( op, si )) {
+ slap_sync_cookie_free( &syncCookie_req, 0 );
+ slap_dup_sync_cookie( &syncCookie_req, &si->si_syncCookie );
+ }
+ if ( !syncCookie.ctxcsn ) {
+ match = 1;
+ } else if ( !syncCookie_req.ctxcsn ) {
+ match = -1;
+ m = 0;
+ } else {
+ match = compare_csns( &syncCookie_req, &syncCookie, &m );
+ }
+ if ( rctrls ) {
+ ldap_controls_free( rctrls );
+ }
+ if (si->si_type != LDAP_SYNC_REFRESH_AND_PERSIST) {
+ /* FIXME : different error behaviors according to
+ * 1) err code : LDAP_BUSY ...
+ * 2) on err policy : stop service, stop sync, retry
+ */
+ if ( refreshDeletes == 0 && match < 0 && err == LDAP_SUCCESS )
+ {
+ syncrepl_del_nonpresent( op, si, NULL,
+ &syncCookie, m );
+ } else if ( si->si_presentlist ) {
+ presentlist_free( si->si_presentlist );
+ si->si_presentlist = NULL;
+ }
+ }
+ if ( syncCookie.ctxcsn && match < 0 && err == LDAP_SUCCESS )
+ {
+ rc = syncrepl_updateCookie( si, op, &syncCookie, 1 );
+ }
+ if ( err == LDAP_SUCCESS
+ && si->si_logstate == SYNCLOG_FALLBACK ) {
+ si->si_logstate = SYNCLOG_LOGGING;
+ si->si_refreshDone = 1;
+ rc = LDAP_SYNC_REFRESH_REQUIRED;
+ slap_resume_listeners();
+ } else {
+ /* for persist, we shouldn't get a SearchResult so this is an error */
+ if ( si->si_type == LDAP_SYNC_REFRESH_AND_PERSIST )
+ rc = SYNC_ERROR;
+ else
+ rc = SYNC_REPOLL;
+ }
+ goto done;
+
+ case LDAP_RES_INTERMEDIATE:
+ retoid = NULL;
+ retdata = NULL;
+ rc = ldap_parse_intermediate( si->si_ld, msg,
+ &retoid, &retdata, NULL, 0 );
+ if ( !rc && !strcmp( retoid, LDAP_SYNC_INFO ) ) {
+ ber_init2( ber, retdata, LBER_USE_DER );
+
+ switch ( si_tag = ber_peek_tag( ber, &len ) ) {
+ ber_tag_t tag;
+ case LDAP_TAG_SYNC_NEW_COOKIE:
+ Debug( LDAP_DEBUG_SYNC,
+ "do_syncrep2: %s %s - %s\n",
+ si->si_ridtxt,
+ "LDAP_RES_INTERMEDIATE",
+ "NEW_COOKIE" );
+ ber_scanf( ber, "tm", &tag, &cookie );
+ Debug( LDAP_DEBUG_SYNC,
+ "do_syncrep2: %s NEW_COOKIE: %s\n",
+ si->si_ridtxt,
+ cookie.bv_val );
+ if ( !BER_BVISNULL( &cookie ) ) {
+ ch_free( syncCookie.octet_str.bv_val );
+ ber_dupbv( &syncCookie.octet_str, &cookie );
+
+ ldap_pvt_thread_mutex_lock( &si->si_monitor_mutex );
+ ber_bvreplace( &si->si_lastCookieRcvd, &cookie );
+ ldap_pvt_thread_mutex_unlock( &si->si_monitor_mutex );
+ }
+ if (!BER_BVISNULL( &syncCookie.octet_str ) ) {
+ slap_parse_sync_cookie( &syncCookie, NULL );
+ op->o_controls[slap_cids.sc_LDAPsync] = &syncCookie;
+ }
+ break;
+ case LDAP_TAG_SYNC_REFRESH_DELETE:
+ case LDAP_TAG_SYNC_REFRESH_PRESENT:
+ Debug( LDAP_DEBUG_SYNC,
+ "do_syncrep2: %s %s - %s\n",
+ si->si_ridtxt,
+ "LDAP_RES_INTERMEDIATE",
+ si_tag == LDAP_TAG_SYNC_REFRESH_PRESENT ?
+ "REFRESH_PRESENT" : "REFRESH_DELETE" );
+ if ( si->si_refreshDone ) {
+ Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
+ "server sent multiple refreshDone "
+ "messages? Ending session\n",
+ si->si_ridtxt );
+ rc = LDAP_PROTOCOL_ERROR;
+ goto done;
+ }
+ if ( si_tag == LDAP_TAG_SYNC_REFRESH_DELETE ) {
+ si->si_refreshDelete = 1;
+ } else {
+ si->si_refreshPresent = 1;
+ }
+ ber_scanf( ber, "t{" /*"}"*/, &tag );
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE )
+ {
+ ber_scanf( ber, "m", &cookie );
+
+ Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s cookie=%s\n",
+ si->si_ridtxt,
+ BER_BVISNULL( &cookie ) ? "" : cookie.bv_val );
+
+ if ( !BER_BVISNULL( &cookie ) ) {
+ ch_free( syncCookie.octet_str.bv_val );
+ ber_dupbv( &syncCookie.octet_str, &cookie );
+
+ ldap_pvt_thread_mutex_lock( &si->si_monitor_mutex );
+ ber_bvreplace( &si->si_lastCookieRcvd, &cookie );
+ ldap_pvt_thread_mutex_unlock( &si->si_monitor_mutex );
+ }
+ if ( !BER_BVISNULL( &syncCookie.octet_str ) )
+ {
+ slap_parse_sync_cookie( &syncCookie, NULL );
+ op->o_controls[slap_cids.sc_LDAPsync] = &syncCookie;
+ }
+ }
+ /* Defaults to TRUE */
+ if ( ber_peek_tag( ber, &len ) ==
+ LDAP_TAG_REFRESHDONE )
+ {
+ ber_scanf( ber, "b", &si->si_refreshDone );
+ } else
+ {
+ si->si_refreshDone = 1;
+ }
+ ber_scanf( ber, /*"{"*/ "}" );
+ if ( refreshing && si->si_refreshDone ) {
+ refresh_finished( si );
+ refreshing = 0;
+ }
+ break;
+ case LDAP_TAG_SYNC_ID_SET:
+ Debug( LDAP_DEBUG_SYNC,
+ "do_syncrep2: %s %s - %s\n",
+ si->si_ridtxt,
+ "LDAP_RES_INTERMEDIATE",
+ "SYNC_ID_SET" );
+ ber_scanf( ber, "t{" /*"}"*/, &tag );
+ if ( ber_peek_tag( ber, &len ) ==
+ LDAP_TAG_SYNC_COOKIE )
+ {
+ ber_scanf( ber, "m", &cookie );
+
+ Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s cookie=%s\n",
+ si->si_ridtxt,
+ BER_BVISNULL( &cookie ) ? "" : cookie.bv_val );
+
+ if ( !BER_BVISNULL( &cookie ) ) {
+ ch_free( syncCookie.octet_str.bv_val );
+ ber_dupbv( &syncCookie.octet_str, &cookie );
+
+ ldap_pvt_thread_mutex_lock( &si->si_monitor_mutex );
+ ber_bvreplace( &si->si_lastCookieRcvd, &cookie );
+ ldap_pvt_thread_mutex_unlock( &si->si_monitor_mutex );
+ }
+ if ( !BER_BVISNULL( &syncCookie.octet_str ) )
+ {
+ slap_parse_sync_cookie( &syncCookie, NULL );
+ op->o_controls[slap_cids.sc_LDAPsync] = &syncCookie;
+ compare_csns( &syncCookie_req, &syncCookie, &m );
+ }
+ }
+ if ( ber_peek_tag( ber, &len ) ==
+ LDAP_TAG_REFRESHDELETES )
+ {
+ ber_scanf( ber, "b", &refreshDeletes );
+ }
+ syncUUIDs = NULL;
+ rc = ber_scanf( ber, "[W]", &syncUUIDs );
+ ber_scanf( ber, /*"{"*/ "}" );
+ if ( rc != LBER_ERROR ) {
+ if ( refreshDeletes ) {
+ syncrepl_del_nonpresent( op, si, syncUUIDs,
+ &syncCookie, m );
+ ber_bvarray_free_x( syncUUIDs, op->o_tmpmemctx );
+ } else {
+ int i;
+ for ( i = 0; !BER_BVISNULL( &syncUUIDs[i] ); i++ ) {
+ (void)presentlist_insert( si, &syncUUIDs[i] );
+ slap_sl_free( syncUUIDs[i].bv_val, op->o_tmpmemctx );
+ }
+ slap_sl_free( syncUUIDs, op->o_tmpmemctx );
+ }
+ }
+ rc = 0;
+ slap_sync_cookie_free( &syncCookie, 0 );
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "do_syncrep2: %s unknown syncinfo tag (%ld)\n",
+ si->si_ridtxt, (long) si_tag );
+ ldap_memfree( retoid );
+ ber_bvfree( retdata );
+ continue;
+ }
+
+ if ( SLAP_MULTIPROVIDER( op->o_bd ) && check_syncprov( op, si )) {
+ slap_sync_cookie_free( &syncCookie_req, 0 );
+ slap_dup_sync_cookie( &syncCookie_req, &si->si_syncCookie );
+ }
+ if ( !syncCookie.ctxcsn ) {
+ match = 1;
+ } else if ( !syncCookie_req.ctxcsn ) {
+ match = -1;
+ m = 0;
+ } else {
+ match = compare_csns( &syncCookie_req, &syncCookie, &m );
+ }
+
+ if ( match < 0 ) {
+ if ( si->si_refreshPresent == 1 &&
+ si_tag != LDAP_TAG_SYNC_NEW_COOKIE ) {
+ syncrepl_del_nonpresent( op, si, NULL,
+ &syncCookie, m );
+ }
+
+ if ( syncCookie.ctxcsn )
+ {
+ rc = syncrepl_updateCookie( si, op, &syncCookie, 1 );
+ }
+ if ( si->si_presentlist ) {
+ presentlist_free( si->si_presentlist );
+ si->si_presentlist = NULL;
+ }
+ }
+
+ ldap_memfree( retoid );
+ ber_bvfree( retdata );
+
+ if ( rc )
+ goto done;
+
+ } else {
+ Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
+ "unknown intermediate response (%d)\n",
+ si->si_ridtxt, rc );
+ ldap_memfree( retoid );
+ ber_bvfree( retdata );
+ }
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_ANY, "do_syncrep2: %s "
+ "unknown message (0x%02lx)\n",
+ si->si_ridtxt,
+ (unsigned long)ldap_msgtype( msg ) );
+ break;
+
+ }
+ if ( !BER_BVISNULL( &syncCookie.octet_str ) ) {
+ slap_sync_cookie_free( &syncCookie_req, 0 );
+ syncCookie_req = syncCookie;
+ memset( &syncCookie, 0, sizeof( syncCookie ));
+ }
+ ldap_msgfree( msg );
+ msg = NULL;
+ if ( ldap_pvt_thread_pool_pausing( &connection_pool )) {
+ slap_sync_cookie_free( &syncCookie, 0 );
+ slap_sync_cookie_free( &syncCookie_req, 0 );
+ return SYNC_PAUSED;
+ }
+ }
+
+ if ( rc == SYNC_ERROR ) {
+ rc = LDAP_OTHER;
+ ldap_get_option( si->si_ld, LDAP_OPT_ERROR_NUMBER, &rc );
+ err = rc;
+ }
+
+done:
+ if ( err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "do_syncrep2: %s (%d) %s\n",
+ si->si_ridtxt, err, ldap_err2string( err ) );
+ }
+ if ( refreshing && ( rc || si->si_refreshDone ) ) {
+ refresh_finished( si );
+ }
+
+ slap_sync_cookie_free( &syncCookie, 0 );
+ slap_sync_cookie_free( &syncCookie_req, 0 );
+
+ if ( msg ) ldap_msgfree( msg );
+
+ if ( rc ) {
+ if ( rc == LDAP_SYNC_REFRESH_REQUIRED && si->si_logstate == SYNCLOG_LOGGING && si->si_ld )
+ return rc;
+ /* never reuse existing connection */
+ if ( si->si_conn ) {
+ connection_client_stop( si->si_conn );
+ si->si_conn = NULL;
+ }
+ ldap_unbind_ext( si->si_ld, NULL, NULL );
+ si->si_ld = NULL;
+ }
+
+ return rc;
+}
+
+static int
+syncrepl_monitor_add( syncinfo_t *si );
+
+static int
+syncrepl_monitor_del( syncinfo_t *si );
+
+static void *
+do_syncrepl(
+ void *ctx,
+ void *arg )
+{
+ struct re_s* rtask = arg;
+ syncinfo_t *si = ( syncinfo_t * ) rtask->arg;
+ Connection conn = {0};
+ OperationBuffer opbuf;
+ Operation *op;
+ int rc = LDAP_SUCCESS;
+ int dostop = 0;
+ ber_socket_t s;
+ int i, fail = 0, freeinfo = 0;
+ Backend *be;
+
+ if ( si == NULL )
+ return NULL;
+ if ( slapd_shutdown )
+ return NULL;
+
+ if ( !si->si_monitorInited ) {
+ syncrepl_monitor_add( si );
+ si->si_monitorInited = 1;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "=>do_syncrepl %s\n", si->si_ridtxt );
+
+ /* Don't get stuck here while a pause is initiated */
+ while ( ldap_pvt_thread_mutex_trylock( &si->si_mutex )) {
+ if ( slapd_shutdown )
+ return NULL;
+ if ( !ldap_pvt_thread_pool_pausecheck( &connection_pool ))
+ ldap_pvt_thread_yield();
+ }
+
+ si->si_too_old = 0;
+
+ if ( si->si_ctype < 1 ) {
+ goto deleted;
+ }
+
+ switch( abs( si->si_type ) ) {
+ case LDAP_SYNC_REFRESH_ONLY:
+ case LDAP_SYNC_REFRESH_AND_PERSIST:
+#ifdef LDAP_CONTROL_X_DIRSYNC
+ case MSAD_DIRSYNC:
+#endif
+ break;
+ default:
+ ldap_pvt_thread_mutex_unlock( &si->si_mutex );
+ return NULL;
+ }
+
+ if ( slapd_shutdown ) {
+ if ( si->si_ld ) {
+ if ( si->si_conn ) {
+ connection_client_stop( si->si_conn );
+ si->si_conn = NULL;
+ }
+ ldap_unbind_ext( si->si_ld, NULL, NULL );
+ si->si_ld = NULL;
+ }
+ ldap_pvt_thread_mutex_unlock( &si->si_mutex );
+ return NULL;
+ }
+
+ connection_fake_init( &conn, &opbuf, ctx );
+ op = &opbuf.ob_op;
+ /* o_connids must be unique for slap_graduate_commit_csn */
+ op->o_connid = SLAPD_SYNC_RID2SYNCCONN(si->si_rid);
+
+ op->o_managedsait = SLAP_CONTROL_NONCRITICAL;
+ be = si->si_be;
+
+ /* Coordinate contextCSN updates with any syncprov overlays
+ * in use. This may be complicated by the use of the glue
+ * overlay.
+ *
+ * Typically there is a single syncprov controlling the entire
+ * glued tree. In that case, our contextCSN updates should
+ * go to the primary DB. But if there is no syncprov on the
+ * primary DB, then nothing special is needed here.
+ *
+ * Alternatively, there may be individual syncprov overlays
+ * on each glued branch. In that case, each syncprov only
+ * knows about changes within its own branch. And so our
+ * contextCSN updates should only go to the local DB.
+ */
+ if ( !si->si_wbe ) {
+ if ( SLAP_GLUE_SUBORDINATE( be ) && !overlay_is_inst( be, "syncprov" )) {
+ BackendDB * top_be = select_backend( &be->be_nsuffix[0], 1 );
+ if ( overlay_is_inst( top_be, "syncprov" ))
+ si->si_wbe = top_be;
+ else
+ si->si_wbe = be;
+ } else {
+ si->si_wbe = be;
+ }
+ if ( SLAP_SYNC_SUBENTRY( si->si_wbe )) {
+ build_new_dn( &si->si_contextdn, &si->si_wbe->be_nsuffix[0],
+ (struct berval *)&slap_ldapsync_cn_bv, NULL );
+ } else {
+ si->si_contextdn = si->si_wbe->be_nsuffix[0];
+ }
+ }
+ if ( !si->si_schemachecking )
+ op->o_no_schema_check = 1;
+
+ /* Establish session, do search */
+ if ( !si->si_ld ) {
+ if ( si->si_presentlist ) {
+ presentlist_free( si->si_presentlist );
+ si->si_presentlist = NULL;
+ }
+
+ /* use main DB when retrieving contextCSN */
+ op->o_bd = si->si_wbe;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ rc = do_syncrep1( op, si );
+ } else if ( !si->si_msgid ) {
+ /* We got a SYNC_BUSY, now told to resume */
+ rc = ldap_sync_search( si, op->o_tmpmemctx );
+ }
+ if ( rc == SYNC_BUSY ) {
+ ldap_pvt_thread_mutex_unlock( &si->si_mutex );
+ return NULL;
+ }
+
+reload:
+ /* Process results */
+ if ( rc == LDAP_SUCCESS ) {
+ ldap_get_option( si->si_ld, LDAP_OPT_DESC, &s );
+
+ if ( !BER_BVISEMPTY( &si->si_monitor_ndn ))
+ {
+ Sockaddr addr;
+ socklen_t len = sizeof( addr );
+ if ( !getsockname( s, &addr.sa_addr, &len )) {
+ si->si_connaddr.bv_val = si->si_connaddrbuf;
+ si->si_connaddr.bv_len = sizeof( si->si_connaddrbuf );
+ ldap_pvt_sockaddrstr( &addr, &si->si_connaddr );
+ }
+ }
+
+ /* use current DB */
+ op->o_bd = be;
+ op->o_dn = op->o_bd->be_rootdn;
+ op->o_ndn = op->o_bd->be_rootndn;
+ rc = do_syncrep2( op, si );
+ if ( rc == LDAP_SYNC_REFRESH_REQUIRED ) {
+ if ( si->si_logstate == SYNCLOG_LOGGING ) {
+ if ( BER_BVISNULL( &si->si_syncCookie.octet_str ))
+ slap_compose_sync_cookie( NULL, &si->si_syncCookie.octet_str,
+ si->si_syncCookie.ctxcsn, si->si_syncCookie.rid,
+ si->si_syncCookie.sid, NULL );
+ rc = ldap_sync_search( si, op->o_tmpmemctx );
+ goto reload;
+ }
+ /* give up but schedule an immedite retry */
+ rc = SYNC_PAUSED;
+ }
+
+deleted:
+ /* We got deleted while running on cn=config */
+ if ( si->si_ctype < 1 ) {
+ if ( si->si_ctype == -1 ) {
+ si->si_ctype = 0;
+ freeinfo = 1;
+ }
+ if ( si->si_conn )
+ dostop = 1;
+ rc = SYNC_SHUTDOWN;
+ }
+
+ if ( rc != SYNC_PAUSED ) {
+ if ( rc == SYNC_TIMEOUT ) {
+ /* there was nothing to read, try to listen for more */
+ if ( si->si_conn ) {
+ connection_client_enable( si->si_conn );
+ } else {
+ si->si_conn = connection_client_setup( s, do_syncrepl, arg );
+ }
+ } else if ( si->si_conn ) {
+ dostop = 1;
+ }
+ }
+ }
+
+ /* At this point, we have 5 cases:
+ * 1) for any hard failure, give up and remove this task
+ * 2) for ServerDown, reschedule this task to run later
+ * 3) for threadpool pause, reschedule to run immediately
+ * 4) for SYNC_REPOLL, reschedule to run later
+ * 5) for SYNC_TIMEOUT, reschedule to defer
+ */
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask ) ) {
+ ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
+ }
+
+ if ( dostop ) {
+ connection_client_stop( si->si_conn );
+ si->si_conn = NULL;
+ }
+
+ if ( rc == SYNC_PAUSED ) {
+ rtask->interval.tv_sec = 0;
+ ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 );
+ rtask->interval.tv_sec = si->si_interval;
+ rc = 0;
+ } else if ( rc == SYNC_TIMEOUT ) {
+ ldap_pvt_runqueue_resched( &slapd_rq, rtask, 1 );
+ } else if ( rc == SYNC_REPOLL ) {
+ rtask->interval.tv_sec = si->si_interval;
+ ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 );
+ if ( si->si_retrynum ) {
+ for ( i = 0; si->si_retrynum_init[i] != RETRYNUM_TAIL; i++ ) {
+ si->si_retrynum[i] = si->si_retrynum_init[i];
+ }
+ si->si_retrynum[i] = RETRYNUM_TAIL;
+ }
+ slap_wake_listener();
+ rc = 0;
+ } else {
+ for ( i = 0; si->si_retrynum && si->si_retrynum[i] <= 0; i++ ) {
+ if ( si->si_retrynum[i] == RETRYNUM_FOREVER || si->si_retrynum[i] == RETRYNUM_TAIL )
+ break;
+ }
+
+ if ( si->si_ctype < 1 || rc == SYNC_SHUTDOWN
+ || !si->si_retrynum || si->si_retrynum[i] == RETRYNUM_TAIL ) {
+ if ( si->si_re ) {
+ ldap_pvt_runqueue_remove( &slapd_rq, rtask );
+ si->si_re = NULL;
+ }
+ fail = RETRYNUM_TAIL;
+ } else if ( RETRYNUM_VALID( si->si_retrynum[i] ) ) {
+ if ( si->si_retrynum[i] > 0 )
+ si->si_retrynum[i]--;
+ fail = si->si_retrynum[i];
+ rtask->interval.tv_sec = si->si_retryinterval[i];
+ ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 );
+ slap_wake_listener();
+ }
+ }
+
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ ldap_pvt_thread_mutex_unlock( &si->si_mutex );
+
+ if ( rc ) {
+ if ( fail == RETRYNUM_TAIL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "do_syncrepl: %s rc %d quitting\n",
+ si->si_ridtxt, rc );
+ } else if ( fail > 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "do_syncrepl: %s rc %d retrying (%d retries left)\n",
+ si->si_ridtxt, rc, fail );
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "do_syncrepl: %s rc %d retrying\n",
+ si->si_ridtxt, rc );
+ }
+ }
+
+ /* Do final delete cleanup */
+ if ( freeinfo ) {
+ syncinfo_free( si, 0 );
+ }
+ return NULL;
+}
+
+static int
+syncrepl_rewrite_dn(
+ syncinfo_t *si,
+ struct berval *dn,
+ struct berval *sdn )
+{
+ char nul;
+ int rc;
+
+ nul = dn->bv_val[dn->bv_len];
+ dn->bv_val[dn->bv_len] = 0;
+ rc = rewrite( si->si_rewrite, SUFFIXM_CTX, dn->bv_val, &sdn->bv_val );
+ dn->bv_val[dn->bv_len] = nul;
+
+ if ( sdn->bv_val == dn->bv_val )
+ sdn->bv_val = NULL;
+ else if ( rc == REWRITE_REGEXEC_OK && sdn->bv_val )
+ sdn->bv_len = strlen( sdn->bv_val );
+ return rc;
+}
+#define REWRITE_VAL(si, ad, bv, bv2) \
+ BER_BVZERO( &bv2 ); \
+ if ( si->si_rewrite && ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName) \
+ syncrepl_rewrite_dn( si, &bv, &bv2); \
+ if ( BER_BVISNULL( &bv2 )) \
+ ber_dupbv( &bv2, &bv )
+#define REWRITE_DN(si, bv, bv2, dn, ndn) \
+ BER_BVZERO( &bv2 ); \
+ if (si->si_rewrite) \
+ syncrepl_rewrite_dn(si, &bv, &bv2); \
+ rc = dnPrettyNormal( NULL, bv2.bv_val ? &bv2 : &bv, &dn, &ndn, op->o_tmpmemctx ); \
+ ch_free(bv2.bv_val)
+
+static slap_verbmasks modops[] = {
+ { BER_BVC("add"), LDAP_REQ_ADD },
+ { BER_BVC("delete"), LDAP_REQ_DELETE },
+ { BER_BVC("modify"), LDAP_REQ_MODIFY },
+ { BER_BVC("modrdn"), LDAP_REQ_MODRDN},
+ { BER_BVNULL, 0 }
+};
+
+static int
+syncrepl_accesslog_mods(
+ syncinfo_t *si,
+ struct berval *vals,
+ struct Modifications **modres
+)
+{
+ char *colon;
+ const char *text;
+ AttributeDescription *ad;
+ struct berval bv, bv2;
+ short op;
+ Modifications *mod = NULL, *modlist = NULL, **modtail;
+ int i, rc = 0;
+
+ modtail = &modlist;
+
+ for (i=0; !BER_BVISNULL( &vals[i] ); i++) {
+ ad = NULL;
+ bv = vals[i];
+
+ colon = ber_bvchr( &bv, ':' );
+ if ( !colon ) {
+ /* Invalid */
+ continue;
+ } else if ( colon == bv.bv_val ) {
+ /* ITS#6545: An empty attribute signals that a new mod
+ * is about to start */
+ mod = NULL;
+ continue;
+ }
+
+ bv.bv_len = colon - bv.bv_val;
+ if ( slap_bv2ad( &bv, &ad, &text ) ) {
+ /* Invalid */
+ Debug( LDAP_DEBUG_ANY, "syncrepl_accesslog_mods: %s "
+ "Invalid attribute %s, %s\n",
+ si->si_ridtxt, bv.bv_val, text );
+ slap_mods_free( modlist, 1 );
+ modlist = NULL;
+ rc = -1;
+ break;
+ }
+
+ /* Ignore dynamically generated attrs */
+ if ( ad->ad_type->sat_flags & SLAP_AT_DYNAMIC ) {
+ continue;
+ }
+
+ /* Ignore excluded attrs */
+ if ( ldap_charray_inlist( si->si_exattrs,
+ ad->ad_type->sat_cname.bv_val ) )
+ {
+ continue;
+ }
+
+ switch(colon[1]) {
+ case '+': op = LDAP_MOD_ADD; break;
+ case '-': op = LDAP_MOD_DELETE; break;
+ case '=': op = LDAP_MOD_REPLACE; break;
+ case '#': op = LDAP_MOD_INCREMENT; break;
+ default: continue;
+ }
+
+ if ( !mod || ad != mod->sml_desc || op != mod->sml_op ) {
+ mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+ mod->sml_flags = 0;
+ mod->sml_op = op;
+ mod->sml_next = NULL;
+ mod->sml_desc = ad;
+ mod->sml_type = ad->ad_cname;
+ mod->sml_values = NULL;
+ mod->sml_nvalues = NULL;
+ mod->sml_numvals = 0;
+
+ if ( is_at_single_value( ad->ad_type ) ) {
+ if ( op == LDAP_MOD_ADD ) {
+ /* ITS#9295 an ADD might conflict with an existing value */
+ mod->sml_op = LDAP_MOD_REPLACE;
+ } else if ( op == LDAP_MOD_DELETE ) {
+ /* ITS#9295 the above REPLACE could invalidate subsequent
+ * DELETEs */
+ mod->sml_op = SLAP_MOD_SOFTDEL;
+ }
+ }
+
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ }
+ if ( colon[2] == ' ' ) {
+ bv.bv_val = colon + 3;
+ bv.bv_len = vals[i].bv_len - ( bv.bv_val - vals[i].bv_val );
+ REWRITE_VAL( si, ad, bv, bv2 );
+ ber_bvarray_add( &mod->sml_values, &bv2 );
+ mod->sml_numvals++;
+ }
+ }
+ *modres = modlist;
+ return rc;
+}
+
+static int
+syncrepl_dsee_uuid(
+ struct berval *dseestr,
+ struct berval *syncUUID,
+ void *ctx
+)
+{
+ slap_mr_normalize_func *normf;
+ /* DSEE UUID is of form 12345678-12345678-12345678-12345678 */
+ if ( dseestr->bv_len != 35 )
+ return -1;
+ dseestr->bv_len++;
+ dseestr->bv_val[35] = '-';
+ normf = slap_schema.si_ad_entryUUID->ad_type->sat_equality->smr_normalize;
+ if ( normf( SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX, NULL, NULL,
+ dseestr, &syncUUID[0], ctx ))
+ return -1;
+ (void)slap_uuidstr_from_normalized( &syncUUID[1], &syncUUID[0], ctx );
+ return LDAP_SUCCESS;
+}
+
+static int
+syncrepl_changelog_mods(
+ syncinfo_t *si,
+ ber_tag_t req,
+ struct berval *vals,
+ struct Modifications **modres,
+ struct berval *uuid,
+ void *ctx
+)
+{
+ LDIFRecord lr;
+ struct berval rbuf = vals[0];
+ int i, rc;
+ int lrflags = LDIF_NO_DN;
+ Modifications *mod = NULL, *modlist = NULL, **modtail = &modlist;
+
+ if ( req == LDAP_REQ_ADD )
+ lrflags |= LDIF_ENTRIES_ONLY|LDIF_DEFAULT_ADD;
+ else
+ lrflags |= LDIF_MODS_ONLY;
+
+ rc = ldap_parse_ldif_record_x( &rbuf, 0, &lr, "syncrepl", lrflags, ctx );
+ for (i = 0; lr.lrop_mods[i] != NULL; i++) {
+ AttributeDescription *ad = NULL;
+ const char *text;
+ int j;
+ if ( slap_str2ad( lr.lrop_mods[i]->mod_type, &ad, &text ) ) {
+ /* Invalid */
+ Debug( LDAP_DEBUG_ANY, "syncrepl_changelog_mods: %s "
+ "Invalid attribute %s, %s\n",
+ si->si_ridtxt, lr.lrop_mods[i]->mod_type, text );
+ slap_mods_free( modlist, 1 );
+ modlist = NULL;
+ rc = -1;
+ break;
+ }
+ mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+ mod->sml_flags = 0;
+ mod->sml_op = lr.lrop_mods[i]->mod_op ^ LDAP_MOD_BVALUES;
+ mod->sml_next = NULL;
+ mod->sml_desc = ad;
+ mod->sml_type = ad->ad_cname;
+ mod->sml_values = NULL;
+ mod->sml_nvalues = NULL;
+ j = 0;
+ if ( lr.lrop_mods[i]->mod_bvalues != NULL ) {
+ for (; lr.lrop_mods[i]->mod_bvalues[j] != NULL; j++ ) {
+ struct berval bv, bv2;
+ bv = *(lr.lrop_mods[i]->mod_bvalues[j]);
+ REWRITE_VAL( si, ad, bv, bv2 );
+ ber_bvarray_add( &mod->sml_values, &bv2 );
+ }
+ }
+ mod->sml_numvals = j;
+
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ }
+ ldap_ldif_record_done( &lr );
+
+ if ( req == LDAP_REQ_ADD && !BER_BVISNULL( uuid )) {
+ struct berval uuids[2];
+ if ( !syncrepl_dsee_uuid( uuid, uuids, ctx )) {
+ mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+ mod->sml_flags = 0;
+ mod->sml_op = LDAP_MOD_ADD;
+ mod->sml_next = NULL;
+ mod->sml_desc = slap_schema.si_ad_entryUUID;
+ mod->sml_type = slap_schema.si_ad_entryUUID->ad_cname;
+ mod->sml_values = ch_malloc( 2 * sizeof(struct berval));
+ mod->sml_nvalues = NULL;
+ ber_dupbv( &mod->sml_values[0], &uuids[1] );
+ BER_BVZERO( &mod->sml_values[1] );
+ slap_sl_free( uuids[0].bv_val, ctx );
+ slap_sl_free( uuids[1].bv_val, ctx );
+ mod->sml_numvals = 1;
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ }
+ }
+
+ *modres = modlist;
+ return rc;
+}
+
+typedef struct OpExtraSync {
+ OpExtra oe;
+ syncinfo_t *oe_si;
+} OpExtraSync;
+
+/* Copy the original modlist, split Replace ops into Delete/Add,
+ * and drop mod opattrs since this modification is in the past.
+ */
+static Modifications *mods_dup( Operation *op, Modifications *modlist, int match )
+{
+ Modifications *mod, *modnew = NULL, *modtail = NULL;
+ int size;
+ for ( ; modlist; modlist = modlist->sml_next ) {
+ /* older ops */
+ if ( match < 0 ) {
+ if ( modlist->sml_desc == slap_schema.si_ad_modifiersName ||
+ modlist->sml_desc == slap_schema.si_ad_modifyTimestamp ||
+ modlist->sml_desc == slap_schema.si_ad_entryCSN )
+ continue;
+ if ( modlist->sml_values == NULL && modlist->sml_op == LDAP_MOD_REPLACE ) {
+ /* ITS#9359 This adds no values, just change to a delete op */
+ modlist->sml_op = LDAP_MOD_DELETE;
+ } else if ( modlist->sml_op == LDAP_MOD_REPLACE ) {
+ mod = op->o_tmpalloc( sizeof(Modifications), op->o_tmpmemctx );
+ mod->sml_desc = modlist->sml_desc;
+ mod->sml_values = NULL;
+ mod->sml_nvalues = NULL;
+ mod->sml_op = LDAP_MOD_DELETE;
+ mod->sml_numvals = 0;
+ mod->sml_flags = 0;
+ if ( !modnew )
+ modnew = mod;
+ if ( modtail )
+ modtail->sml_next = mod;
+ modtail = mod;
+ }
+ }
+ if ( modlist->sml_numvals ) {
+ size = (modlist->sml_numvals+1) * sizeof(struct berval);
+ if ( modlist->sml_nvalues ) size *= 2;
+ } else {
+ size = 0;
+ }
+ size += sizeof(Modifications);
+ mod = op->o_tmpalloc( size, op->o_tmpmemctx );
+ if ( !modnew )
+ modnew = mod;
+ if ( modtail )
+ modtail->sml_next = mod;
+ modtail = mod;
+ mod->sml_desc = modlist->sml_desc;
+ mod->sml_numvals = modlist->sml_numvals;
+ mod->sml_flags = 0;
+ if ( modlist->sml_numvals ) {
+ int i;
+ mod->sml_values = (BerVarray)(mod+1);
+ for (i=0; i<mod->sml_numvals; i++)
+ mod->sml_values[i] = modlist->sml_values[i];
+ BER_BVZERO(&mod->sml_values[i]);
+ if ( modlist->sml_nvalues ) {
+ mod->sml_nvalues = mod->sml_values + mod->sml_numvals + 1;
+ for (i=0; i<mod->sml_numvals; i++)
+ mod->sml_nvalues[i] = modlist->sml_nvalues[i];
+ BER_BVZERO(&mod->sml_nvalues[i]);
+ } else {
+ mod->sml_nvalues = NULL;
+ }
+ } else {
+ mod->sml_values = NULL;
+ mod->sml_nvalues = NULL;
+ }
+ if ( match < 0 && modlist->sml_op == LDAP_MOD_REPLACE )
+ mod->sml_op = LDAP_MOD_ADD;
+ else
+ mod->sml_op = modlist->sml_op;
+ mod->sml_next = NULL;
+ }
+ return modnew;
+}
+
+typedef struct resolve_ctxt {
+ syncinfo_t *rx_si;
+ Entry *rx_entry;
+ Modifications *rx_mods;
+} resolve_ctxt;
+
+static void
+compare_vals( Modifications *m1, Modifications *m2 )
+{
+ int i, j;
+ struct berval *bv1, *bv2;
+
+ if ( m2->sml_nvalues ) {
+ bv2 = m2->sml_nvalues;
+ bv1 = m1->sml_nvalues;
+ } else {
+ bv2 = m2->sml_values;
+ bv1 = m1->sml_values;
+ }
+ for ( j=0; j<m2->sml_numvals; j++ ) {
+ for ( i=0; i<m1->sml_numvals; i++ ) {
+ if ( !ber_bvcmp( &bv1[i], &bv2[j] )) {
+ int k;
+ for ( k=i; k<m1->sml_numvals-1; k++ ) {
+ m1->sml_values[k] = m1->sml_values[k+1];
+ if ( m1->sml_nvalues )
+ m1->sml_nvalues[k] = m1->sml_nvalues[k+1];
+ }
+ BER_BVZERO(&m1->sml_values[k]);
+ if ( m1->sml_nvalues ) {
+ BER_BVZERO(&m1->sml_nvalues[k]);
+ }
+ m1->sml_numvals--;
+ i--;
+ }
+ }
+ }
+}
+
+static int
+syncrepl_resolve_cb( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_type == REP_SEARCH ) {
+ resolve_ctxt *rx = op->o_callback->sc_private;
+ Attribute *a = attr_find( rs->sr_entry->e_attrs, ad_reqMod );
+ if ( a ) {
+ Modifications *oldmods, *newmods, *m1, *m2, **prev;
+ Entry *e = rx->rx_entry;
+ oldmods = rx->rx_mods;
+ syncrepl_accesslog_mods( rx->rx_si, a->a_vals, &newmods );
+ for ( m2 = newmods; m2; m2=m2->sml_next ) {
+ for ( prev = &oldmods, m1 = *prev; m1; m1 = *prev ) {
+ if ( m1->sml_desc != m2->sml_desc ) {
+ prev = &m1->sml_next;
+ continue;
+ }
+ if ( m2->sml_op == LDAP_MOD_DELETE ||
+ m2->sml_op == SLAP_MOD_SOFTDEL ||
+ m2->sml_op == LDAP_MOD_REPLACE ) {
+ int numvals = m2->sml_numvals;
+ if ( m2->sml_op == LDAP_MOD_REPLACE )
+ numvals = 0;
+ /* New delete All cancels everything */
+ if ( numvals == 0 ) {
+drop:
+ *prev = m1->sml_next;
+ op->o_tmpfree( m1, op->o_tmpmemctx );
+ continue;
+ }
+ if ( m1->sml_op == LDAP_MOD_DELETE ||
+ m1->sml_op == SLAP_MOD_SOFTDEL ) {
+ if ( m1->sml_numvals == 0 ) {
+ /* turn this to SOFTDEL later */
+ m1->sml_flags = SLAP_MOD_INTERNAL;
+ } else {
+ compare_vals( m1, m2 );
+ if ( !m1->sml_numvals )
+ goto drop;
+ }
+ } else if ( m1->sml_op == LDAP_MOD_ADD ) {
+ compare_vals( m1, m2 );
+ if ( !m1->sml_numvals )
+ goto drop;
+ }
+ }
+
+ if ( m2->sml_op == LDAP_MOD_ADD ||
+ m2->sml_op == LDAP_MOD_REPLACE ) {
+ if ( m2->sml_desc->ad_type->sat_atype.at_single_value )
+ goto drop;
+ if ( m1->sml_op == LDAP_MOD_DELETE ) {
+ if ( m2->sml_op == LDAP_MOD_REPLACE ) {
+ goto drop;
+ }
+ if ( !m1->sml_numvals ) {
+ Modifications *m;
+ unsigned int size, i;
+ /*
+ * ITS#9751 An ADD might supersede parts of
+ * this delete, but we still need to honour the
+ * rest. Keep resolving as if it was deleting
+ * specific values
+ */
+ a = attr_find( e->e_attrs, m1->sml_desc );
+ if ( !a ) {
+ goto drop;
+ }
+
+ size = (a->a_numvals+1) * sizeof(struct berval);
+ if ( a->a_nvals ) size *= 2;
+ size += sizeof(Modifications);
+ m = op->o_tmpalloc( size, op->o_tmpmemctx );
+ *m = *m1;
+
+ m->sml_numvals = a->a_numvals;
+ m->sml_values = (BerVarray)(m+1);
+
+ for ( i=0; i < a->a_numvals; i++ )
+ m->sml_values[i] = a->a_vals[i];
+ BER_BVZERO( &m->sml_values[i] );
+
+ if ( a->a_nvals ) {
+ m->sml_nvalues = m->sml_values + m->sml_numvals + 1;
+ for ( i=0; i < a->a_numvals; i++ )
+ m->sml_nvalues[i] = a->a_nvals[i];
+ BER_BVZERO( &m->sml_nvalues[i] );
+ } else {
+ m->sml_nvalues = NULL;
+ }
+ op->o_tmpfree( m1, op->o_tmpmemctx );
+ *prev = m1 = m;
+ }
+ }
+ compare_vals( m1, m2 );
+ if ( !m1->sml_numvals )
+ goto drop;
+ }
+ prev = &m1->sml_next;
+ }
+ }
+ slap_mods_free( newmods, 1 );
+ rx->rx_mods = oldmods;
+ }
+ }
+ return LDAP_SUCCESS;
+}
+
+typedef struct modify_ctxt {
+ Modifications *mx_orig;
+ Modifications *mx_free;
+ Entry *mx_entry;
+} modify_ctxt;
+
+static int
+syncrepl_modify_cb( Operation *op, SlapReply *rs )
+{
+ slap_callback *sc = op->o_callback;
+ modify_ctxt *mx = sc->sc_private;
+ Modifications *ml;
+
+ op->orm_no_opattrs = 0;
+ op->orm_modlist = mx->mx_orig;
+ for ( ml = mx->mx_free; ml; ml = mx->mx_free ) {
+ mx->mx_free = ml->sml_next;
+ op->o_tmpfree( ml, op->o_tmpmemctx );
+ }
+ if ( mx->mx_entry ) {
+ entry_free( mx->mx_entry );
+ }
+ op->o_callback = sc->sc_next;
+ op->o_tmpfree( sc, op->o_tmpmemctx );
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+syncrepl_op_modify( Operation *op, SlapReply *rs )
+{
+ slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+ OpExtra *oex;
+ syncinfo_t *si;
+ Entry *e, *e_dup;
+ int rc, match = 0;
+ Modifications *mod, *newlist;
+
+ LDAP_SLIST_FOREACH( oex, &op->o_extra, oe_next ) {
+ if ( oex->oe_key == (void *)syncrepl_message_to_op )
+ break;
+ }
+ if ( !oex )
+ return SLAP_CB_CONTINUE;
+
+ si = ((OpExtraSync *)oex)->oe_si;
+
+ /* Check if entryCSN in modlist is newer than entryCSN in entry.
+ * We do it here because the op has been serialized by accesslog
+ * by the time we get here. If the CSN is new enough, just do the
+ * mod. If not, we need to resolve conflicts.
+ */
+
+ for ( mod = op->orm_modlist; mod; mod=mod->sml_next ) {
+ if ( mod->sml_desc == slap_schema.si_ad_entryCSN ) break;
+ }
+ /* FIXME: what should we do if entryCSN is missing from the mod? */
+ if ( !mod )
+ return SLAP_CB_CONTINUE;
+
+ {
+ int sid = slap_parse_csn_sid( &mod->sml_nvalues[0] );
+ ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_mutex );
+ rc = check_csn_age( si, &op->o_req_dn, &mod->sml_nvalues[0],
+ sid, (cookie_vals *)&si->si_cookieState->cs_vals, NULL );
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
+ if ( rc == CV_CSN_OLD ) {
+ slap_graduate_commit_csn( op );
+ /* tell accesslog this was a failure */
+ rs->sr_err = LDAP_TYPE_OR_VALUE_EXISTS;
+ return LDAP_SUCCESS;
+ }
+ }
+
+ rc = overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on );
+ if ( rc == 0 ) {
+ Attribute *a;
+ const char *text;
+ a = attr_find( e->e_attrs, slap_schema.si_ad_entryCSN );
+ if ( a ) {
+ value_match( &match, slap_schema.si_ad_entryCSN,
+ slap_schema.si_ad_entryCSN->ad_type->sat_ordering,
+ SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
+ &mod->sml_nvalues[0], &a->a_nvals[0], &text );
+ } else {
+ /* no entryCSN? shouldn't happen. assume mod is newer. */
+ match = 1;
+ }
+ e_dup = entry_dup( e );
+ overlay_entry_release_ov( op, e, 0, on );
+ } else {
+ return SLAP_CB_CONTINUE;
+ }
+
+ /* equal? Should never happen */
+ if ( match == 0 ) {
+ slap_graduate_commit_csn( op );
+ /* tell accesslog this was a failure */
+ rs->sr_err = LDAP_TYPE_OR_VALUE_EXISTS;
+ entry_free( e_dup );
+ return LDAP_SUCCESS;
+ }
+
+ /* mod is older: resolve conflicts...
+ * 1. Save/copy original modlist. Split Replace to Del/Add.
+ * 2. Find all mods to this reqDN newer than the mod stamp.
+ * 3. Resolve any mods in this request that affect attributes
+ * touched by newer mods.
+ * old new
+ * delete all delete all drop
+ * delete all delete X SOFTDEL
+ * delete X delete all drop
+ * delete X delete X drop
+ * delete X delete Y OK
+ * delete all add X convert to delete current values,
+ * drop delete X from it
+ * delete X add X drop
+ * delete X add Y OK
+ * add X delete all drop
+ * add X delete X drop
+ * add X add X drop
+ * add X add Y if SV, drop else OK
+ *
+ * 4. Swap original modlist back in response callback so
+ * that accesslog logs the original mod.
+ *
+ * Even if the mod is newer, other out-of-order changes may
+ * have been committed, forcing us to tweak the modlist:
+ * 1. Save/copy original modlist.
+ * 2. Change deletes to soft deletes.
+ * 3. Change Adds of single-valued attrs to Replace.
+ */
+
+ newlist = mods_dup( op, op->orm_modlist, match );
+
+ /* mod is older */
+ if ( match < 0 ) {
+ Operation op2 = *op;
+ AttributeName an[2];
+ struct berval bv;
+ int size;
+ SlapReply rs1 = {0};
+ resolve_ctxt rx;
+ slap_callback cb = { NULL, syncrepl_resolve_cb, NULL, NULL };
+ Filter lf[3] = {0};
+ AttributeAssertion aa[2] = {0};
+
+ rx.rx_si = si;
+ rx.rx_entry = e_dup;
+ rx.rx_mods = newlist;
+ cb.sc_private = &rx;
+
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2.ors_scope = LDAP_SCOPE_SUBTREE;
+ op2.ors_deref = LDAP_DEREF_NEVER;
+ op2.o_req_dn = si->si_logbase;
+ op2.o_req_ndn = si->si_logbase;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+ op2.ors_slimit = SLAP_NO_LIMIT;
+ op2.ors_limit = NULL;
+ memset( an, 0, sizeof(an));
+ an[0].an_desc = ad_reqMod;
+ an[0].an_name = ad_reqMod->ad_cname;
+ op2.ors_attrs = an;
+ op2.ors_attrsonly = 0;
+
+ bv = mod->sml_nvalues[0];
+
+ size = sizeof("(&(entryCSN>=)(reqDN=))");
+ size += bv.bv_len + op->o_req_ndn.bv_len + si->si_logfilterstr.bv_len;
+ op2.ors_filterstr.bv_val = op->o_tmpalloc( size, op->o_tmpmemctx );
+ op2.ors_filterstr.bv_len = sprintf(op2.ors_filterstr.bv_val,
+ "(&(entryCSN>=%s)(reqDN=%s)%s)",
+ bv.bv_val, op->o_req_ndn.bv_val, si->si_logfilterstr.bv_val );
+
+ lf[0].f_choice = LDAP_FILTER_AND;
+ lf[0].f_and = lf+1;
+ lf[1].f_choice = LDAP_FILTER_GE;
+ lf[1].f_ava = aa;
+ lf[1].f_av_desc = slap_schema.si_ad_entryCSN;
+ lf[1].f_av_value = bv;
+ lf[1].f_next = lf+2;
+ lf[2].f_choice = LDAP_FILTER_EQUALITY;
+ lf[2].f_ava = aa+1;
+ lf[2].f_av_desc = ad_reqDN;
+ lf[2].f_av_value = op->o_req_ndn;
+ lf[2].f_next = si->si_logfilter;
+
+ op2.ors_filter = lf;
+
+ op2.o_callback = &cb;
+ op2.o_bd = select_backend( &op2.o_req_ndn, 1 );
+ op2.o_bd->be_search( &op2, &rs1 );
+ newlist = rx.rx_mods;
+ }
+
+ {
+ slap_callback *sc = op->o_tmpalloc( sizeof(slap_callback) +
+ sizeof(modify_ctxt), op->o_tmpmemctx );
+ modify_ctxt *mx = (modify_ctxt *)(sc+1);
+ Modifications *ml;
+
+ sc->sc_response = syncrepl_modify_cb;
+ sc->sc_private = mx;
+ sc->sc_next = op->o_callback;
+ sc->sc_cleanup = NULL;
+ sc->sc_writewait = NULL;
+ op->o_callback = sc;
+ op->orm_no_opattrs = 1;
+ mx->mx_orig = op->orm_modlist;
+ mx->mx_free = newlist;
+ mx->mx_entry = e_dup;
+ for ( ml = newlist; ml; ml=ml->sml_next ) {
+ if ( ml->sml_flags == SLAP_MOD_INTERNAL ) {
+ ml->sml_flags = 0;
+ ml->sml_op = SLAP_MOD_SOFTDEL;
+ }
+ else if ( ml->sml_op == LDAP_MOD_DELETE )
+ ml->sml_op = SLAP_MOD_SOFTDEL;
+ else if ( ml->sml_op == LDAP_MOD_ADD &&
+ ml->sml_desc->ad_type->sat_atype.at_single_value )
+ ml->sml_op = LDAP_MOD_REPLACE;
+ }
+ op->orm_modlist = newlist;
+ op->o_csn = mod->sml_nvalues[0];
+ }
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+syncrepl_null_callback(
+ Operation *op,
+ SlapReply *rs )
+{
+ /* If we're not the last callback in the chain, move to the end */
+ if ( op->o_callback->sc_next ) {
+ slap_callback **sc, *s1;
+ s1 = op->o_callback;
+ op->o_callback = op->o_callback->sc_next;
+ for ( sc = &op->o_callback; *sc; sc = &(*sc)->sc_next ) ;
+ *sc = s1;
+ s1->sc_next = NULL;
+ return SLAP_CB_CONTINUE;
+ }
+ if ( rs->sr_err != LDAP_SUCCESS &&
+ rs->sr_err != LDAP_REFERRAL &&
+ rs->sr_err != LDAP_ALREADY_EXISTS &&
+ rs->sr_err != LDAP_NO_SUCH_OBJECT &&
+ rs->sr_err != LDAP_NOT_ALLOWED_ON_NONLEAF )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "syncrepl_null_callback : error code 0x%x\n",
+ rs->sr_err );
+ }
+ return LDAP_SUCCESS;
+}
+
+static int
+syncrepl_message_to_op(
+ syncinfo_t *si,
+ Operation *op,
+ LDAPMessage *msg,
+ int do_lock
+)
+{
+ BerElement *ber = NULL;
+ Modifications *modlist = NULL;
+ logschema *ls;
+ SlapReply rs = { REP_RESULT };
+ slap_callback cb = { NULL, syncrepl_null_callback, NULL, NULL };
+
+ const char *text;
+ char txtbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof txtbuf;
+
+ struct berval bdn, dn = BER_BVNULL, ndn;
+ struct berval bv, bv2, *bvals = NULL;
+ struct berval rdn = BER_BVNULL, sup = BER_BVNULL,
+ prdn = BER_BVNULL, nrdn = BER_BVNULL,
+ psup = BER_BVNULL, nsup = BER_BVNULL;
+ struct berval dsee_uuid = BER_BVNULL, dsee_mods = BER_BVNULL;
+ int rc, deleteOldRdn = 0, freeReqDn = 0;
+ int do_graduate = 0, do_unlock = 0;
+ unsigned long changenum = 0;
+
+ if ( ldap_msgtype( msg ) != LDAP_RES_SEARCH_ENTRY ) {
+ Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_op: %s "
+ "Message type should be entry (%d)",
+ si->si_ridtxt, ldap_msgtype( msg ) );
+ return -1;
+ }
+
+ if ( si->si_syncdata == SYNCDATA_ACCESSLOG )
+ ls = &accesslog_sc;
+ else
+ ls = &changelog_sc;
+
+ rc = ldap_get_dn_ber( si->si_ld, msg, &ber, &bdn );
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "syncrepl_message_to_op: %s dn get failed (%d)",
+ si->si_ridtxt, rc );
+ return rc;
+ }
+
+ op->o_tag = LBER_DEFAULT;
+ op->o_bd = si->si_wbe;
+
+ if ( BER_BVISEMPTY( &bdn )) {
+ Debug( LDAP_DEBUG_ANY,
+ "syncrepl_message_to_op: %s got empty dn",
+ si->si_ridtxt );
+ return LDAP_OTHER;
+ }
+
+ while (( rc = ldap_get_attribute_ber( si->si_ld, msg, ber, &bv, &bvals ) )
+ == LDAP_SUCCESS ) {
+ if ( bv.bv_val == NULL )
+ break;
+
+ if ( !ber_bvstrcasecmp( &bv, &ls->ls_dn ) ) {
+ bdn = bvals[0];
+ REWRITE_DN( si, bdn, bv2, dn, ndn );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "syncrepl_message_to_op: %s "
+ "dn \"%s\" normalization failed (%d)",
+ si->si_ridtxt, bdn.bv_val, rc );
+ rc = -1;
+ ch_free( bvals );
+ goto done;
+ }
+ op->o_req_dn = dn;
+ op->o_req_ndn = ndn;
+ freeReqDn = 1;
+ } else if ( !ber_bvstrcasecmp( &bv, &ls->ls_req ) ) {
+ int i = verb_to_mask( bvals[0].bv_val, modops );
+ if ( i < 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "syncrepl_message_to_op: %s unknown op %s",
+ si->si_ridtxt, bvals[0].bv_val );
+ ch_free( bvals );
+ rc = -1;
+ goto done;
+ }
+ op->o_tag = modops[i].mask;
+ } else if ( !ber_bvstrcasecmp( &bv, &ls->ls_mod ) ) {
+ /* Parse attribute into modlist */
+ if ( si->si_syncdata == SYNCDATA_ACCESSLOG ) {
+ rc = syncrepl_accesslog_mods( si, bvals, &modlist );
+ } else {
+ dsee_mods = bvals[0];
+ }
+ if ( rc ) goto done;
+ } else if ( !ber_bvstrcasecmp( &bv, &ls->ls_newRdn ) ) {
+ rdn = bvals[0];
+ } else if ( !ber_bvstrcasecmp( &bv, &ls->ls_delRdn ) ) {
+ if ( !ber_bvstrcasecmp( &slap_true_bv, bvals ) ) {
+ deleteOldRdn = 1;
+ }
+ } else if ( !ber_bvstrcasecmp( &bv, &ls->ls_newSup ) ) {
+ sup = bvals[0];
+ } else if ( !ber_bvstrcasecmp( &bv, &ls->ls_controls ) ) {
+ int i;
+ struct berval rel_ctrl_bv;
+
+ (void)ber_str2bv( "{" LDAP_CONTROL_RELAX, 0, 0, &rel_ctrl_bv );
+ for ( i = 0; bvals[i].bv_val; i++ ) {
+ struct berval cbv, tmp;
+
+ ber_bvchr_post( &cbv, &bvals[i], '}' );
+ ber_bvchr_post( &tmp, &cbv, '{' );
+ ber_bvchr_pre( &cbv, &tmp, ' ' );
+ if ( cbv.bv_len == tmp.bv_len ) /* control w/o value */
+ ber_bvchr_pre( &cbv, &tmp, '}' );
+ if ( !ber_bvcmp( &cbv, &rel_ctrl_bv ) )
+ op->o_relax = SLAP_CONTROL_CRITICAL;
+ }
+ } else if ( !ber_bvstrcasecmp( &bv, &ls->ls_uuid ) ) {
+ dsee_uuid = bvals[0];
+ } else if ( !ber_bvstrcasecmp( &bv, &ls->ls_changenum ) ) {
+ changenum = strtoul( bvals->bv_val, NULL, 0 );
+ } else if ( !ber_bvstrcasecmp( &bv,
+ &slap_schema.si_ad_entryCSN->ad_cname ) )
+ {
+ int i, sid = slap_parse_csn_sid( bvals );
+ ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_mutex );
+ i = check_csn_age( si, &bdn, bvals, sid,
+ (cookie_vals *)&si->si_cookieState->cs_vals, NULL );
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
+ if ( i == CV_CSN_OLD ) {
+ goto done;
+ }
+ slap_queue_csn( op, bvals );
+ do_graduate = 1;
+ }
+ ch_free( bvals );
+ }
+
+ /* don't parse mods until we've gotten the uuid */
+ if ( si->si_syncdata == SYNCDATA_CHANGELOG && !BER_BVISNULL( &dsee_mods )) {
+ rc = syncrepl_changelog_mods( si, op->o_tag,
+ &dsee_mods, &modlist, &dsee_uuid, op->o_tmpmemctx );
+ if ( rc )
+ goto done;
+ }
+
+ /* If we didn't get a mod type or a target DN, bail out */
+ if ( op->o_tag == LBER_DEFAULT || BER_BVISNULL( &dn ) ) {
+ rc = -1;
+ goto done;
+ }
+
+ if ( do_lock ) {
+ if (( rc = get_pmutex( si )))
+ goto done;
+ do_unlock = 1;
+ }
+
+ op->o_callback = &cb;
+ slap_op_time( &op->o_time, &op->o_tincr );
+
+ Debug( LDAP_DEBUG_SYNC, "syncrepl_message_to_op: %s tid %p\n",
+ si->si_ridtxt, (void *)op->o_tid );
+
+ switch( op->o_tag ) {
+ case LDAP_REQ_ADD:
+ case LDAP_REQ_MODIFY:
+ /* If we didn't get required data, bail */
+ if ( !modlist ) goto done;
+
+ rc = slap_mods_check( op, modlist, &text, txtbuf, textlen, NULL );
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_op: %s "
+ "mods check (%s)\n",
+ si->si_ridtxt, text );
+ goto done;
+ }
+
+ if ( op->o_tag == LDAP_REQ_ADD ) {
+ Entry *e = entry_alloc();
+ op->ora_e = e;
+ ber_dupbv( &op->ora_e->e_name, &op->o_req_dn );
+ ber_dupbv( &op->ora_e->e_nname, &op->o_req_ndn );
+ rc = slap_mods2entry( modlist, &op->ora_e, 1, 0, &text, txtbuf, textlen);
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_op: %s "
+ "mods2entry (%s)\n",
+ si->si_ridtxt, text );
+ } else {
+ rc = op->o_bd->be_add( op, &rs );
+ Debug( LDAP_DEBUG_SYNC,
+ "syncrepl_message_to_op: %s be_add %s (%d)\n",
+ si->si_ridtxt, op->o_req_dn.bv_val, rc );
+ do_graduate = 0;
+ if ( rc == LDAP_ALREADY_EXISTS ) {
+ Attribute *a = attr_find( e->e_attrs, slap_schema.si_ad_entryCSN );
+ struct berval *vals;
+ if ( a && backend_attribute( op, NULL, &op->o_req_ndn,
+ slap_schema.si_ad_entryCSN, &vals, ACL_READ ) == LDAP_SUCCESS ) {
+ if ( ber_bvcmp( &vals[0], &a->a_vals[0] ) >= 0 )
+ rc = LDAP_SUCCESS;
+ ber_bvarray_free_x( vals, op->o_tmpmemctx );
+ }
+ }
+ }
+ if ( e == op->ora_e )
+ be_entry_release_w( op, op->ora_e );
+ } else {
+ OpExtraSync oes;
+ op->orm_modlist = modlist;
+ op->o_bd = si->si_wbe;
+ /* delta-mpr needs additional checks in syncrepl_op_modify */
+ if ( SLAP_MULTIPROVIDER( op->o_bd )) {
+ oes.oe.oe_key = (void *)syncrepl_message_to_op;
+ oes.oe_si = si;
+ LDAP_SLIST_INSERT_HEAD( &op->o_extra, &oes.oe, oe_next );
+ }
+ rc = op->o_bd->be_modify( op, &rs );
+ if ( SLAP_MULTIPROVIDER( op->o_bd )) {
+ LDAP_SLIST_REMOVE( &op->o_extra, &oes.oe, OpExtra, oe_next );
+ BER_BVZERO( &op->o_csn );
+ }
+ modlist = op->orm_modlist;
+ Debug( rc ? LDAP_DEBUG_ANY : LDAP_DEBUG_SYNC,
+ "syncrepl_message_to_op: %s be_modify %s (%d)\n",
+ si->si_ridtxt, op->o_req_dn.bv_val, rc );
+ op->o_bd = si->si_be;
+ do_graduate = 0;
+ }
+ break;
+ case LDAP_REQ_MODRDN:
+ if ( BER_BVISNULL( &rdn ) ) goto done;
+
+ if ( rdnPretty( NULL, &rdn, &prdn, NULL ) ) {
+ goto done;
+ }
+ if ( rdnNormalize( 0, NULL, NULL, &rdn, &nrdn, NULL ) ) {
+ goto done;
+ }
+ if ( !BER_BVISNULL( &sup ) ) {
+ REWRITE_DN( si, sup, bv2, psup, nsup );
+ if ( rc )
+ goto done;
+ op->orr_newSup = &psup;
+ op->orr_nnewSup = &nsup;
+ } else {
+ op->orr_newSup = NULL;
+ op->orr_nnewSup = NULL;
+ }
+ op->orr_newrdn = prdn;
+ op->orr_nnewrdn = nrdn;
+ op->orr_deleteoldrdn = deleteOldRdn;
+ op->orr_modlist = NULL;
+ if ( slap_modrdn2mods( op, &rs ) ) {
+ goto done;
+ }
+
+ /* Append modlist for operational attrs */
+ {
+ Modifications *m;
+
+ for ( m = op->orr_modlist; m->sml_next; m = m->sml_next )
+ ;
+ m->sml_next = modlist;
+ modlist = NULL;
+ }
+ rc = op->o_bd->be_modrdn( op, &rs );
+ slap_mods_free( op->orr_modlist, 1 );
+ Debug( rc ? LDAP_DEBUG_ANY : LDAP_DEBUG_SYNC,
+ "syncrepl_message_to_op: %s be_modrdn %s (%d)\n",
+ si->si_ridtxt, op->o_req_dn.bv_val, rc );
+ do_graduate = 0;
+ break;
+ case LDAP_REQ_DELETE:
+ rc = op->o_bd->be_delete( op, &rs );
+ Debug( rc ? LDAP_DEBUG_ANY : LDAP_DEBUG_SYNC,
+ "syncrepl_message_to_op: %s be_delete %s (%d)\n",
+ si->si_ridtxt, op->o_req_dn.bv_val, rc );
+ /* silently ignore this */
+ if ( rc == LDAP_NO_SUCH_OBJECT )
+ rc = LDAP_SUCCESS;
+ do_graduate = 0;
+ break;
+ }
+ if ( si->si_syncdata == SYNCDATA_CHANGELOG && !rc )
+ si->si_lastchange = changenum;
+
+done:
+ if ( do_graduate )
+ slap_graduate_commit_csn( op );
+ if ( do_unlock )
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_pmutex );
+ op->o_bd = si->si_be;
+ op->o_tmpfree( op->o_csn.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &op->o_csn );
+ if ( modlist ) {
+ slap_mods_free( modlist, op->o_tag != LDAP_REQ_ADD );
+ }
+ if ( !BER_BVISNULL( &rdn ) ) {
+ if ( !BER_BVISNULL( &nsup ) ) {
+ ch_free( nsup.bv_val );
+ }
+ if ( !BER_BVISNULL( &psup ) ) {
+ ch_free( psup.bv_val );
+ }
+ if ( !BER_BVISNULL( &nrdn ) ) {
+ ch_free( nrdn.bv_val );
+ }
+ if ( !BER_BVISNULL( &prdn ) ) {
+ ch_free( prdn.bv_val );
+ }
+ }
+ if ( freeReqDn ) {
+ op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx );
+ }
+ ber_free( ber, 0 );
+ return rc;
+}
+
+static int
+syncrepl_message_to_entry(
+ syncinfo_t *si,
+ Operation *op,
+ LDAPMessage *msg,
+ Modifications **modlist,
+ Entry **entry,
+ int syncstate,
+ struct berval *syncUUID
+)
+{
+ Entry *e = NULL;
+ BerElement *ber = NULL;
+ Modifications tmp;
+ Modifications *mod;
+ Modifications **modtail = modlist;
+
+ const char *text;
+ char txtbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof txtbuf;
+
+ struct berval bdn = BER_BVNULL, dn, ndn, bv2;
+ int rc, is_ctx;
+
+ *modlist = NULL;
+
+ if ( ldap_msgtype( msg ) != LDAP_RES_SEARCH_ENTRY ) {
+ Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_entry: %s "
+ "Message type should be entry (%d)",
+ si->si_ridtxt, ldap_msgtype( msg ) );
+ return -1;
+ }
+
+ op->o_tag = LDAP_REQ_ADD;
+
+ rc = ldap_get_dn_ber( si->si_ld, msg, &ber, &bdn );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "syncrepl_message_to_entry: %s dn get failed (%d)",
+ si->si_ridtxt, rc );
+ return rc;
+ }
+
+ if ( BER_BVISEMPTY( &bdn ) && !BER_BVISEMPTY( &op->o_bd->be_nsuffix[0] ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "syncrepl_message_to_entry: %s got empty dn",
+ si->si_ridtxt );
+ return LDAP_OTHER;
+ }
+
+ if ( si->si_syncdata != SYNCDATA_CHANGELOG ) {
+ /* syncUUID[0] is normalized UUID received over the wire
+ * syncUUID[1] is denormalized UUID, generated here
+ */
+ (void)slap_uuidstr_from_normalized( &syncUUID[1], &syncUUID[0], op->o_tmpmemctx );
+ Debug( LDAP_DEBUG_SYNC,
+ "syncrepl_message_to_entry: %s DN: %s, UUID: %s\n",
+ si->si_ridtxt, bdn.bv_val, syncUUID[1].bv_val );
+ }
+
+ if ( syncstate == LDAP_SYNC_PRESENT || syncstate == LDAP_SYNC_DELETE ) {
+ /* NOTE: this could be done even before decoding the DN,
+ * although encoding errors wouldn't be detected */
+ rc = LDAP_SUCCESS;
+ goto done;
+ }
+
+ if ( entry == NULL ) {
+ return -1;
+ }
+
+ REWRITE_DN( si, bdn, bv2, dn, ndn );
+ if ( rc != LDAP_SUCCESS ) {
+ /* One of the things that could happen is that the schema
+ * is not lined-up; this could result in unknown attributes.
+ * A value non conformant to the syntax should be unlikely,
+ * except when replicating between different versions
+ * of the software, or when syntax validation bugs are fixed
+ */
+ Debug( LDAP_DEBUG_ANY,
+ "syncrepl_message_to_entry: "
+ "%s dn \"%s\" normalization failed (%d)",
+ si->si_ridtxt, bdn.bv_val, rc );
+ return rc;
+ }
+
+ ber_dupbv( &op->o_req_dn, &dn );
+ ber_dupbv( &op->o_req_ndn, &ndn );
+ slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
+ slap_sl_free( dn.bv_val, op->o_tmpmemctx );
+
+ is_ctx = dn_match( &op->o_req_ndn, &op->o_bd->be_nsuffix[0] );
+
+ e = entry_alloc();
+ e->e_name = op->o_req_dn;
+ e->e_nname = op->o_req_ndn;
+
+ while ( ber_remaining( ber ) ) {
+ if ( (ber_scanf( ber, "{mW}", &tmp.sml_type, &tmp.sml_values ) ==
+ LBER_ERROR ) || BER_BVISNULL( &tmp.sml_type ) )
+ {
+ break;
+ }
+
+ /* Drop all updates to the contextCSN of the context entry
+ * (ITS#4622, etc.)
+ */
+ if ( is_ctx && !strcasecmp( tmp.sml_type.bv_val,
+ slap_schema.si_ad_contextCSN->ad_cname.bv_val )) {
+ ber_bvarray_free( tmp.sml_values );
+ continue;
+ }
+
+ /* map nsUniqueId to entryUUID, drop nsUniqueId */
+ if ( si->si_syncdata == SYNCDATA_CHANGELOG &&
+ !strcasecmp( tmp.sml_type.bv_val, sy_ad_nsUniqueId->ad_cname.bv_val )) {
+ rc = syncrepl_dsee_uuid( &tmp.sml_values[0], syncUUID, op->o_tmpmemctx );
+ ber_bvarray_free( tmp.sml_values );
+ if ( rc )
+ goto done;
+ continue;
+ }
+
+ mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+
+ mod->sml_op = LDAP_MOD_REPLACE;
+ mod->sml_flags = 0;
+ mod->sml_next = NULL;
+ mod->sml_desc = NULL;
+ mod->sml_type = tmp.sml_type;
+ mod->sml_values = tmp.sml_values;
+ mod->sml_nvalues = NULL;
+ mod->sml_numvals = 0; /* slap_mods_check will set this */
+
+ if (si->si_rewrite) {
+ AttributeDescription *ad = NULL;
+ slap_bv2ad( &tmp.sml_type, &ad, &text );
+ if ( ad ) {
+ mod->sml_desc = ad;
+ mod->sml_type = ad->ad_cname;
+ if ( ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) {
+ int i;
+ for ( i = 0; tmp.sml_values[i].bv_val; i++ ) {
+ syncrepl_rewrite_dn( si, &tmp.sml_values[i], &bv2);
+ if ( !BER_BVISNULL( &bv2 )) {
+ ber_memfree( tmp.sml_values[i].bv_val );
+ tmp.sml_values[i] = bv2;
+ }
+ }
+ }
+ }
+ }
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ }
+
+ if ( *modlist == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_entry: %s no attributes\n",
+ si->si_ridtxt );
+ rc = -1;
+ goto done;
+ }
+
+ rc = slap_mods_check( op, *modlist, &text, txtbuf, textlen, NULL );
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_entry: %s mods check (%s)\n",
+ si->si_ridtxt, text );
+ goto done;
+ }
+
+ /* Strip out dynamically generated attrs */
+ for ( modtail = modlist; *modtail ; ) {
+ mod = *modtail;
+ if ( mod->sml_desc->ad_type->sat_flags & SLAP_AT_DYNAMIC ) {
+ *modtail = mod->sml_next;
+ slap_mod_free( &mod->sml_mod, 0 );
+ ch_free( mod );
+ } else {
+ modtail = &mod->sml_next;
+ }
+ }
+
+ /* Strip out attrs in exattrs list */
+ for ( modtail = modlist; *modtail ; ) {
+ mod = *modtail;
+ if ( ldap_charray_inlist( si->si_exattrs,
+ mod->sml_desc->ad_type->sat_cname.bv_val ) )
+ {
+ *modtail = mod->sml_next;
+ slap_mod_free( &mod->sml_mod, 0 );
+ ch_free( mod );
+ } else {
+ modtail = &mod->sml_next;
+ }
+ }
+
+ rc = slap_mods2entry( *modlist, &e, 1, 1, &text, txtbuf, textlen);
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_entry: %s mods2entry (%s)\n",
+ si->si_ridtxt, text );
+ }
+
+done:
+ ber_free( ber, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ if ( e ) {
+ entry_free( e );
+ e = NULL;
+ }
+ }
+ if ( entry )
+ *entry = e;
+
+ return rc;
+}
+
+#ifdef LDAP_CONTROL_X_DIRSYNC
+static int
+syncrepl_dirsync_message(
+ syncinfo_t *si,
+ Operation *op,
+ LDAPMessage *msg,
+ Modifications **modlist,
+ Entry **entry,
+ int *syncstate,
+ struct berval *syncUUID
+)
+{
+ Entry *e = NULL;
+ BerElement *ber = NULL;
+ Modifications tmp;
+ Modifications *mod, *rangeMod = NULL;
+ Modifications **modtail = modlist;
+
+ const char *text;
+ char txtbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof txtbuf;
+
+ struct berval bdn = BER_BVNULL, dn, ndn, bv2;
+ int rc;
+
+ *modlist = NULL;
+ *syncstate = MSAD_DIRSYNC_MODIFY;
+
+ if ( ldap_msgtype( msg ) != LDAP_RES_SEARCH_ENTRY ) {
+ Debug( LDAP_DEBUG_ANY, "syncrepl_dirsync_message: %s "
+ "Message type should be entry (%d)\n",
+ si->si_ridtxt, ldap_msgtype( msg ) );
+ return -1;
+ }
+
+ rc = ldap_get_dn_ber( si->si_ld, msg, &ber, &bdn );
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "syncrepl_dirsync_message: %s dn get failed (%d)\n",
+ si->si_ridtxt, rc );
+ return rc;
+ }
+
+ if ( BER_BVISEMPTY( &bdn ) && !BER_BVISEMPTY( &op->o_bd->be_nsuffix[0] ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "syncrepl_dirsync_message: %s got empty dn\n",
+ si->si_ridtxt );
+ return LDAP_OTHER;
+ }
+
+ while ( ber_remaining( ber ) ) {
+ AttributeDescription *ad = NULL;
+
+ if ( (ber_scanf( ber, "{mW}", &tmp.sml_type, &tmp.sml_values ) ==
+ LBER_ERROR ) || BER_BVISNULL( &tmp.sml_type ) )
+ {
+ break;
+ }
+ if ( tmp.sml_values == NULL )
+ continue;
+
+ mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+
+ mod->sml_op = LDAP_MOD_REPLACE;
+ mod->sml_flags = 0;
+ mod->sml_next = NULL;
+ mod->sml_desc = NULL;
+ mod->sml_type = tmp.sml_type;
+ mod->sml_values = tmp.sml_values;
+ mod->sml_nvalues = NULL;
+ mod->sml_numvals = 0; /* slap_mods_check will set this */
+
+ rc = slap_bv2ad( &tmp.sml_type, &ad, &text );
+ if ( !ad ) {
+ Debug( LDAP_DEBUG_ANY,
+ "syncrepl_dirsync_message: %s unknown attributeType %s\n",
+ si->si_ridtxt, tmp.sml_type.bv_val );
+ return rc;
+ }
+ mod->sml_desc = ad;
+ mod->sml_type = ad->ad_cname;
+ if (( ad->ad_flags & SLAP_DESC_TAG_RANGE ) && rangeMod == NULL)
+ rangeMod = mod;
+ if (si->si_rewrite) {
+ if ( ad->ad_type->sat_syntax == slap_schema.si_syn_distinguishedName ) {
+ int i;
+ for ( i = 0; tmp.sml_values[i].bv_val; i++ ) {
+ syncrepl_rewrite_dn( si, &tmp.sml_values[i], &bv2);
+ if ( !BER_BVISNULL( &bv2 )) {
+ ber_memfree( tmp.sml_values[i].bv_val );
+ tmp.sml_values[i] = bv2;
+ }
+ }
+ }
+ }
+ if ( mod->sml_desc == sy_ad_objectGUID ) {
+ ber_dupbv_x( &syncUUID[0], &tmp.sml_values[0], op->o_tmpmemctx );
+ /* syncUUID[0] is normalized UUID received over the wire
+ * syncUUID[1] is denormalized UUID, generated here
+ */
+ (void)slap_uuidstr_from_normalized( &syncUUID[1], &syncUUID[0], op->o_tmpmemctx );
+ Debug( LDAP_DEBUG_SYNC,
+ "syncrepl_dirsync_message: %s DN: %s, UUID: %s\n",
+ si->si_ridtxt, bdn.bv_val, syncUUID[1].bv_val );
+ } else if ( mod->sml_desc == sy_ad_isDeleted ) {
+ *syncstate = LDAP_SYNC_DELETE;
+ } else if ( mod->sml_desc == sy_ad_whenCreated ) {
+ *syncstate = LDAP_SYNC_ADD;
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
+
+ mod->sml_op = LDAP_MOD_REPLACE;
+ mod->sml_flags = 0;
+ mod->sml_next = NULL;
+ mod->sml_desc = slap_schema.si_ad_createTimestamp;
+ mod->sml_type = mod->sml_desc->ad_cname;
+ ber_bvarray_dup_x( &mod->sml_values, tmp.sml_values, NULL );
+ mod->sml_nvalues = NULL;
+ mod->sml_numvals = 0; /* slap_mods_check will set this */
+ } /* else is a modify or modrdn */
+
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ }
+
+ if ( *modlist == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "syncrepl_dirsync_message: %s no attributes\n",
+ si->si_ridtxt );
+ rc = -1;
+ goto done;
+ }
+
+ if ( *syncstate == LDAP_SYNC_DELETE ) {
+ e = NULL;
+ slap_mods_free( *modlist, 1 );
+ *modlist = NULL;
+ } else {
+ /* check for incremental multival mods */
+ if ( *syncstate == MSAD_DIRSYNC_MODIFY && rangeMod != NULL ) {
+ for (; rangeMod; rangeMod = rangeMod->sml_next) {
+ if ( rangeMod->sml_desc->ad_flags & SLAP_DESC_TAG_RANGE ) {
+ if ( bvmatch( &rangeMod->sml_desc->ad_tags, &msad_addval ))
+ rangeMod->sml_op = SLAP_MOD_SOFTADD;
+ else if ( bvmatch( &rangeMod->sml_desc->ad_tags, &msad_delval ))
+ rangeMod->sml_op = SLAP_MOD_SOFTDEL;
+ /* turn the tagged attr into a normal one */
+ if ( rangeMod->sml_op != LDAP_MOD_REPLACE ) {
+ AttributeDescription *ad = NULL;
+ slap_bv2ad( &rangeMod->sml_desc->ad_type->sat_cname, &ad, &text );
+ rangeMod->sml_desc = ad;
+ }
+ }
+ }
+ }
+ rc = slap_mods_check( op, *modlist, &text, txtbuf, textlen, NULL );
+
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "syncrepl_dirsync_message: %s mods check (%s)\n",
+ si->si_ridtxt, text );
+ goto done;
+ }
+
+ REWRITE_DN( si, bdn, bv2, dn, ndn );
+ if ( rc != LDAP_SUCCESS ) {
+ /* One of the things that could happen is that the schema
+ * is not lined-up; this could result in unknown attributes.
+ * A value non conformant to the syntax should be unlikely,
+ * except when replicating between different versions
+ * of the software, or when syntax validation bugs are fixed
+ */
+ Debug( LDAP_DEBUG_ANY,
+ "syncrepl_dirsync_message: "
+ "%s dn \"%s\" normalization failed (%d)",
+ si->si_ridtxt, bdn.bv_val, rc );
+ return rc;
+ }
+
+ ber_dupbv( &op->o_req_dn, &dn );
+ ber_dupbv( &op->o_req_ndn, &ndn );
+ slap_sl_free( ndn.bv_val, op->o_tmpmemctx );
+ slap_sl_free( dn.bv_val, op->o_tmpmemctx );
+
+ e = entry_alloc();
+ e->e_name = op->o_req_dn;
+ e->e_nname = op->o_req_ndn;
+
+ /* Strip out redundant attrs */
+ if ( *syncstate == MSAD_DIRSYNC_MODIFY ) {
+ for ( modtail = modlist; *modtail ; ) {
+ mod = *modtail;
+ if ( mod->sml_desc == sy_ad_objectGUID ||
+ mod->sml_desc == sy_ad_instanceType ) {
+ *modtail = mod->sml_next;
+ slap_mod_free( &mod->sml_mod, 0 );
+ ch_free( mod );
+ } else {
+ modtail = &mod->sml_next;
+ }
+ }
+ }
+
+ /* Strip out dynamically generated attrs */
+ for ( modtail = modlist; *modtail ; ) {
+ mod = *modtail;
+ if ( mod->sml_desc->ad_type->sat_flags & SLAP_AT_DYNAMIC ) {
+ *modtail = mod->sml_next;
+ slap_mod_free( &mod->sml_mod, 0 );
+ ch_free( mod );
+ } else {
+ modtail = &mod->sml_next;
+ }
+ }
+
+ /* Strip out attrs in exattrs list */
+ for ( modtail = modlist; *modtail ; ) {
+ mod = *modtail;
+ if ( ldap_charray_inlist( si->si_exattrs,
+ mod->sml_desc->ad_type->sat_cname.bv_val ) )
+ {
+ *modtail = mod->sml_next;
+ slap_mod_free( &mod->sml_mod, 0 );
+ ch_free( mod );
+ } else {
+ modtail = &mod->sml_next;
+ }
+ }
+
+ rc = slap_mods2entry( *modlist, &e, 1, 1, &text, txtbuf, textlen);
+ if( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "syncrepl_dirsync_message: %s mods2entry (%s)\n",
+ si->si_ridtxt, text );
+ }
+ }
+
+done:
+ ber_free( ber, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ if ( e ) {
+ entry_free( e );
+ e = NULL;
+ }
+ }
+ if ( entry )
+ *entry = e;
+
+ return rc;
+}
+
+static int
+syncrepl_dirsync_cookie(
+ syncinfo_t *si,
+ Operation *op,
+ LDAPControl **ctrls
+)
+{
+ LDAPControl *ctrl, **next;
+ Backend *be = op->o_bd;
+ Modifications mod;
+ struct berval vals[2];
+
+ int rc, continueFlag;
+
+ slap_callback cb = { NULL };
+ SlapReply rs_modify = {REP_RESULT};
+
+ ctrl = ldap_control_find( LDAP_CONTROL_X_DIRSYNC, ctrls, &next );
+ if ( ctrl == NULL ) {
+ ldap_controls_free( ctrls );
+ return -1;
+ }
+ rc = ldap_parse_dirsync_control( si->si_ld, ctrl, &continueFlag, &vals[0] );
+ if ( !bvmatch( &vals[0], &si->si_dirSyncCookie )) {
+
+ BER_BVZERO( &vals[1] );
+ mod.sml_op = LDAP_MOD_REPLACE;
+ mod.sml_desc = sy_ad_dirSyncCookie;
+ mod.sml_type = mod.sml_desc->ad_cname;
+ mod.sml_flags = SLAP_MOD_INTERNAL;
+ mod.sml_nvalues = NULL;
+ mod.sml_next = NULL;
+
+ op->o_bd = si->si_wbe;
+ op->o_tag = LDAP_REQ_MODIFY;
+
+ cb.sc_response = syncrepl_null_callback;
+ cb.sc_private = si;
+
+ op->o_callback = &cb;
+ op->o_req_dn = si->si_contextdn;
+ op->o_req_ndn = si->si_contextdn;
+
+ op->o_dont_replicate = 0;
+
+ slap_op_time( &op->o_time, &op->o_tincr );
+
+ mod.sml_numvals = 1;
+ mod.sml_values = vals;
+
+ op->orm_modlist = &mod;
+ op->orm_no_opattrs = 1;
+ rc = op->o_bd->be_modify( op, &rs_modify );
+ op->orm_no_opattrs = 0;
+
+ op->o_bd = be;
+ if ( mod.sml_next ) slap_mods_free( mod.sml_next, 1 );
+
+ if ( rc == LDAP_SUCCESS ) {
+ ber_bvreplace( &si->si_dirSyncCookie, &vals[0] );
+ /* there are more changes still remaining */
+ if ( continueFlag )
+ rc = LDAP_SYNC_REFRESH_REQUIRED;
+ }
+ }
+
+ ch_free( vals[0].bv_val );
+ ldap_controls_free( ctrls );
+ return rc;
+}
+
+static int syncrepl_dirsync_schema()
+{
+ const char *text;
+ int rc;
+
+ rc = slap_str2ad( "objectGUID", &sy_ad_objectGUID, &text );
+ if ( rc )
+ return rc;
+ rc = slap_str2ad( "instanceType", &sy_ad_instanceType, &text );
+ if ( rc )
+ return rc;
+ rc = slap_str2ad( "isDeleted", &sy_ad_isDeleted, &text );
+ if ( rc )
+ return rc;
+ rc = slap_str2ad( "whenCreated", &sy_ad_whenCreated, &text );
+ if ( rc )
+ return rc;
+ return register_at( "( 1.3.6.1.4.1.4203.666.1.27 " /* OpenLDAP-specific */
+ "NAME 'dirSyncCookie' "
+ "DESC 'DirSync Cookie for shadow copy' "
+ "EQUALITY octetStringMatch "
+ "ORDERING octetStringOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation )", &sy_ad_dirSyncCookie, 0);
+}
+#endif /* LDAP_CONTROL_X_DIRSYNC */
+
+static int syncrepl_dsee_schema()
+{
+ const char *text;
+ int rc;
+
+ rc = slap_str2ad( "nsUniqueId", &sy_ad_nsUniqueId, &text );
+ if ( rc )
+ return rc;
+ return register_at( "( 1.3.6.1.4.1.4203.666.1.28 " /* OpenLDAP-specific */
+ "NAME 'lastChangeNumber' "
+ "DESC 'RetroChangelog latest change record' "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation )", &sy_ad_dseeLastChange, 0);
+}
+
+/* During a refresh, we may get an LDAP_SYNC_ADD for an already existing
+ * entry if a previous refresh was interrupted before sending us a new
+ * context state. We try to compare the new entry to the existing entry
+ * and ignore the new entry if they are the same.
+ *
+ * Also, we may get an update where the entryDN has changed, due to
+ * a ModDn on the provider. We detect this as well, so we can issue
+ * the corresponding operation locally.
+ *
+ * In the case of a modify, we get a list of all the attributes
+ * in the original entry. Rather than deleting the entry and re-adding it,
+ * we issue a Modify request that deletes all the attributes and adds all
+ * the new ones. This avoids the issue of trying to delete/add a non-leaf
+ * entry.
+ *
+ * We otherwise distinguish ModDN from Modify; in the case of
+ * a ModDN we just use the CSN, modifyTimestamp and modifiersName
+ * operational attributes from the entry, and do a regular ModDN.
+ */
+typedef struct dninfo {
+ syncinfo_t *si;
+ Entry *new_entry;
+ struct berval dn;
+ struct berval ndn;
+ struct berval nnewSup;
+ int syncstate;
+ int renamed; /* Was an existing entry renamed? */
+ int delOldRDN; /* Was old RDN deleted? */
+ Modifications **modlist; /* the modlist we received */
+ Modifications *mods; /* the modlist we compared */
+ int oldNcount; /* #values of old naming attr */
+ AttributeDescription *oldDesc; /* for renames */
+ AttributeDescription *newDesc; /* for renames */
+} dninfo;
+
+#define HASHUUID 1
+
+/* return 1 if inserted, 0 otherwise */
+static int
+presentlist_insert(
+ syncinfo_t* si,
+ struct berval *syncUUID )
+{
+ char *val;
+
+#ifdef HASHUUID
+ Avlnode **av;
+ unsigned short s;
+
+ if ( !si->si_presentlist )
+ si->si_presentlist = ch_calloc(65536, sizeof( Avlnode * ));
+
+ av = (Avlnode **)si->si_presentlist;
+
+ val = ch_malloc(UUIDLEN-2);
+ memcpy(&s, syncUUID->bv_val, 2);
+ memcpy(val, syncUUID->bv_val+2, UUIDLEN-2);
+
+ if ( ldap_avl_insert( &av[s], val,
+ syncuuid_cmp, ldap_avl_dup_error ) )
+ {
+ ch_free( val );
+ return 0;
+ }
+#else
+ val = ch_malloc(UUIDLEN);
+
+ AC_MEMCPY( val, syncUUID->bv_val, UUIDLEN );
+
+ if ( ldap_avl_insert( &si->si_presentlist, val,
+ syncuuid_cmp, ldap_avl_dup_error ) )
+ {
+ ch_free( val );
+ return 0;
+ }
+#endif
+
+ return 1;
+}
+
+static char *
+presentlist_find(
+ Avlnode *av,
+ struct berval *val )
+{
+#ifdef HASHUUID
+ Avlnode **a2 = (Avlnode **)av;
+ unsigned short s;
+
+ if (!av)
+ return NULL;
+
+ memcpy(&s, val->bv_val, 2);
+ return ldap_avl_find( a2[s], val->bv_val+2, syncuuid_cmp );
+#else
+ return ldap_avl_find( av, val->bv_val, syncuuid_cmp );
+#endif
+}
+
+static int
+presentlist_free( Avlnode *av )
+{
+#ifdef HASHUUID
+ Avlnode **a2 = (Avlnode **)av;
+ int i, count = 0;
+
+ if ( av ) {
+ for (i=0; i<65536; i++) {
+ if (a2[i])
+ count += ldap_avl_free( a2[i], ch_free );
+ }
+ ch_free( av );
+ }
+ return count;
+#else
+ return ldap_avl_free( av, ch_free );
+#endif
+}
+
+static void
+presentlist_delete(
+ Avlnode **av,
+ struct berval *val )
+{
+#ifdef HASHUUID
+ Avlnode **a2 = *(Avlnode ***)av;
+ unsigned short s;
+
+ memcpy(&s, val->bv_val, 2);
+ ldap_avl_delete( &a2[s], val->bv_val+2, syncuuid_cmp );
+#else
+ ldap_avl_delete( av, val->bv_val, syncuuid_cmp );
+#endif
+}
+
+static int
+syncrepl_entry(
+ syncinfo_t* si,
+ Operation *op,
+ Entry* entry,
+ Modifications** modlist,
+ int syncstate,
+ struct berval* syncUUID,
+ struct berval* syncCSN )
+{
+ Backend *be = op->o_bd;
+ slap_callback cb = { NULL, NULL, NULL, NULL };
+ int syncuuid_inserted = 0;
+
+ SlapReply rs_search = {REP_RESULT};
+ Filter f = {0};
+ AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
+ int rc = LDAP_SUCCESS;
+
+ struct berval pdn = BER_BVNULL;
+ dninfo dni = {0};
+ int retry = 1;
+ int freecsn = 1;
+
+ Debug( LDAP_DEBUG_SYNC,
+ "syncrepl_entry: %s LDAP_RES_SEARCH_ENTRY(LDAP_SYNC_%s) csn=%s tid %p\n",
+ si->si_ridtxt, syncrepl_state2str( syncstate ), syncCSN ? syncCSN->bv_val : "(none)", (void *)op->o_tid );
+
+ if (( syncstate == LDAP_SYNC_PRESENT || syncstate == LDAP_SYNC_ADD ) ) {
+ if ( !si->si_refreshPresent && !si->si_refreshDone ) {
+ syncuuid_inserted = presentlist_insert( si, syncUUID );
+ }
+ }
+
+ if ( syncstate == LDAP_SYNC_PRESENT ) {
+ return 0;
+ } else if ( syncstate != LDAP_SYNC_DELETE ) {
+ if ( entry == NULL ) {
+ return 0;
+ }
+ }
+
+ if ( syncstate != LDAP_SYNC_DELETE ) {
+ Attribute *a = attr_find( entry->e_attrs, slap_schema.si_ad_entryUUID );
+
+ if ( a == NULL ) {
+ /* add if missing */
+ attr_merge_one( entry, slap_schema.si_ad_entryUUID,
+ &syncUUID[1], syncUUID );
+
+ } else if ( !bvmatch( &a->a_nvals[0], syncUUID ) ) {
+ /* replace only if necessary */
+ if ( a->a_nvals != a->a_vals ) {
+ ber_memfree( a->a_nvals[0].bv_val );
+ ber_dupbv( &a->a_nvals[0], syncUUID );
+ }
+ ber_memfree( a->a_vals[0].bv_val );
+ ber_dupbv( &a->a_vals[0], &syncUUID[1] );
+ }
+ }
+
+ f.f_choice = LDAP_FILTER_EQUALITY;
+ f.f_ava = &ava;
+ ava.aa_desc = slap_schema.si_ad_entryUUID;
+ ava.aa_value = *syncUUID;
+
+ if ( syncuuid_inserted ) {
+ Debug( LDAP_DEBUG_SYNC, "syncrepl_entry: %s inserted UUID %s\n",
+ si->si_ridtxt, syncUUID[1].bv_val );
+ }
+ op->ors_filter = &f;
+
+ op->ors_filterstr.bv_len = STRLENOF( "(entryUUID=)" ) + syncUUID[1].bv_len;
+ op->ors_filterstr.bv_val = (char *) slap_sl_malloc(
+ op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
+ AC_MEMCPY( op->ors_filterstr.bv_val, "(entryUUID=", STRLENOF( "(entryUUID=" ) );
+ AC_MEMCPY( &op->ors_filterstr.bv_val[STRLENOF( "(entryUUID=" )],
+ syncUUID[1].bv_val, syncUUID[1].bv_len );
+ op->ors_filterstr.bv_val[op->ors_filterstr.bv_len - 1] = ')';
+ op->ors_filterstr.bv_val[op->ors_filterstr.bv_len] = '\0';
+
+ op->o_tag = LDAP_REQ_SEARCH;
+ op->ors_scope = LDAP_SCOPE_SUBTREE;
+ op->ors_deref = LDAP_DEREF_NEVER;
+
+ /* get the entry for this UUID */
+ if ( si->si_rewrite ) {
+ op->o_req_dn = si->si_suffixm;
+ op->o_req_ndn = si->si_suffixm;
+ } else
+ {
+ op->o_req_dn = si->si_base;
+ op->o_req_ndn = si->si_base;
+ }
+
+ op->o_time = slap_get_time();
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_slimit = 1;
+ op->ors_limit = NULL;
+
+ op->ors_attrs = slap_anlist_all_attributes;
+ op->ors_attrsonly = 0;
+
+ /* set callback function */
+ op->o_callback = &cb;
+ cb.sc_response = dn_callback;
+ cb.sc_private = &dni;
+ dni.si = si;
+ dni.new_entry = entry;
+ dni.modlist = modlist;
+ dni.syncstate = syncstate;
+
+ rc = be->be_search( op, &rs_search );
+ Debug( LDAP_DEBUG_SYNC,
+ "syncrepl_entry: %s be_search (%d)\n",
+ si->si_ridtxt, rc );
+
+ if ( !BER_BVISNULL( &op->ors_filterstr ) ) {
+ slap_sl_free( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ }
+
+ cb.sc_response = syncrepl_null_callback;
+ cb.sc_private = si;
+
+ if ( entry && !BER_BVISNULL( &entry->e_name ) ) {
+ Debug( LDAP_DEBUG_SYNC,
+ "syncrepl_entry: %s %s\n",
+ si->si_ridtxt, entry->e_name.bv_val );
+ } else {
+ Debug( LDAP_DEBUG_SYNC,
+ "syncrepl_entry: %s %s\n",
+ si->si_ridtxt, dni.dn.bv_val ? dni.dn.bv_val : "(null)" );
+ }
+
+ assert( BER_BVISNULL( &op->o_csn ) );
+ if ( syncCSN ) {
+ slap_queue_csn( op, syncCSN );
+ }
+
+#ifdef SLAP_CONTROL_X_LAZY_COMMIT
+ if ( !si->si_refreshDone && si->si_lazyCommit )
+ op->o_lazyCommit = SLAP_CONTROL_NONCRITICAL;
+#endif
+
+ slap_op_time( &op->o_time, &op->o_tincr );
+ switch ( syncstate ) {
+ case LDAP_SYNC_ADD:
+ case LDAP_SYNC_MODIFY:
+ case DSEE_SYNC_ADD:
+ if ( BER_BVISNULL( &op->o_csn ))
+ {
+
+ Attribute *a = attr_find( entry->e_attrs, slap_schema.si_ad_entryCSN );
+ if ( a ) {
+ /* FIXME: op->o_csn is assumed to be
+ * on the thread's slab; this needs
+ * to be cleared ASAP.
+ */
+ op->o_csn = a->a_vals[0];
+ freecsn = 0;
+ }
+ }
+retry_add:;
+ if ( !BER_BVISNULL( &op->o_csn ) ) {
+ /* Check we're not covered by current contextCSN */
+ int i, sid = slap_parse_csn_sid( &op->o_csn );
+ ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_mutex );
+ for ( i=0;
+ i < si->si_cookieState->cs_num &&
+ sid <= si->si_cookieState->cs_sids[i];
+ i++ ) {
+ if ( si->si_cookieState->cs_sids[i] == sid &&
+ ber_bvcmp( &op->o_csn, &si->si_cookieState->cs_vals[i] ) <= 0 ) {
+ Debug( LDAP_DEBUG_SYNC, "syncrepl_entry: %s "
+ "entry '%s' csn=%s not new enough, ignored\n",
+ si->si_ridtxt, entry->e_name.bv_val, op->o_csn.bv_val );
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
+ goto done;
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
+ }
+ if ( BER_BVISNULL( &dni.dn ) ) {
+ SlapReply rs_add = {REP_RESULT};
+
+ op->o_req_dn = entry->e_name;
+ op->o_req_ndn = entry->e_nname;
+ op->o_tag = LDAP_REQ_ADD;
+ op->ora_e = entry;
+ op->o_bd = si->si_wbe;
+
+ rc = op->o_bd->be_add( op, &rs_add );
+ Debug( LDAP_DEBUG_SYNC,
+ "syncrepl_entry: %s be_add %s (%d)\n",
+ si->si_ridtxt, op->o_req_dn.bv_val, rc );
+ switch ( rs_add.sr_err ) {
+ case LDAP_SUCCESS:
+ if ( op->ora_e == entry ) {
+ be_entry_release_w( op, entry );
+ }
+ entry = NULL;
+ break;
+
+ case LDAP_REFERRAL:
+ /* we assume that LDAP_NO_SUCH_OBJECT is returned
+ * only if the suffix entry is not present.
+ * This should not happen during Persist phase.
+ */
+ case LDAP_NO_SUCH_OBJECT:
+ if ( abs(si->si_type) == LDAP_SYNC_REFRESH_AND_PERSIST &&
+ si->si_refreshDone ) {
+ /* Something's wrong, start over */
+ ber_bvarray_free( si->si_syncCookie.ctxcsn );
+ si->si_syncCookie.ctxcsn = NULL;
+ entry_free( entry );
+ ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_mutex );
+ ber_bvarray_free( si->si_cookieState->cs_vals );
+ ch_free( si->si_cookieState->cs_sids );
+ si->si_cookieState->cs_vals = NULL;
+ si->si_cookieState->cs_sids = 0;
+ si->si_cookieState->cs_num = 0;
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
+ return LDAP_NO_SUCH_OBJECT;
+ }
+ rc = syncrepl_add_glue( op, entry );
+ entry = NULL;
+ break;
+
+ /* if an entry was added via syncrepl_add_glue(),
+ * it likely has no entryUUID, so the previous
+ * be_search() doesn't find it. In this case,
+ * give syncrepl a chance to modify it. Also
+ * allow for entries that were recreated with the
+ * same DN but a different entryUUID.
+ */
+ case LDAP_ALREADY_EXISTS:
+ if ( retry ) {
+ Operation op2 = *op;
+ SlapReply rs2 = { REP_RESULT };
+ slap_callback cb2 = { 0 };
+
+ op2.o_bd = be;
+ op2.o_tag = LDAP_REQ_SEARCH;
+ op2.o_req_dn = entry->e_name;
+ op2.o_req_ndn = entry->e_nname;
+ op2.ors_scope = LDAP_SCOPE_BASE;
+ op2.ors_deref = LDAP_DEREF_NEVER;
+ op2.ors_attrs = slap_anlist_all_attributes;
+ op2.ors_attrsonly = 0;
+ op2.ors_limit = NULL;
+ op2.ors_slimit = 1;
+ op2.ors_tlimit = SLAP_NO_LIMIT;
+
+ f.f_choice = LDAP_FILTER_PRESENT;
+ f.f_desc = slap_schema.si_ad_objectClass;
+ op2.ors_filter = &f;
+ op2.ors_filterstr = generic_filterstr;
+
+ op2.o_callback = &cb2;
+ cb2.sc_response = dn_callback;
+ cb2.sc_private = &dni;
+
+ rc = be->be_search( &op2, &rs2 );
+ if ( rc ) goto done;
+
+ retry = 0;
+ slap_op_time( &op->o_time, &op->o_tincr );
+ goto retry_add;
+ }
+ /* FALLTHRU */
+
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "syncrepl_entry: %s be_add %s failed (%d)\n",
+ si->si_ridtxt, op->o_req_dn.bv_val, rs_add.sr_err );
+ break;
+ }
+ syncCSN = NULL;
+ op->o_bd = be;
+ goto done;
+ }
+ /* FALLTHRU */
+#ifdef LDAP_CONTROL_X_DIRSYNC
+ case MSAD_DIRSYNC_MODIFY:
+#endif
+ op->o_req_dn = dni.dn;
+ op->o_req_ndn = dni.ndn;
+ if ( dni.renamed ) {
+ struct berval noldp, newp;
+ Modifications *mod, **modtail, **ml, *m2 = NULL;
+ int i, got_replace = 0, just_rename = 0;
+ SlapReply rs_modify = {REP_RESULT};
+
+ op->o_tag = LDAP_REQ_MODRDN;
+ dnRdn( &entry->e_name, &op->orr_newrdn );
+ dnRdn( &entry->e_nname, &op->orr_nnewrdn );
+
+ if ( !BER_BVISNULL( &dni.nnewSup )) {
+ dnParent( &entry->e_name, &newp );
+ op->orr_newSup = &newp;
+ op->orr_nnewSup = &dni.nnewSup;
+ } else {
+ op->orr_newSup = NULL;
+ op->orr_nnewSup = NULL;
+ }
+ op->orr_deleteoldrdn = dni.delOldRDN;
+ op->orr_modlist = NULL;
+#ifdef LDAP_CONTROL_X_DIRSYNC
+ if ( syncstate != MSAD_DIRSYNC_MODIFY )
+#endif
+ {
+ if ( ( rc = slap_modrdn2mods( op, &rs_modify ) ) ) {
+ goto done;
+ }
+ }
+
+ /* Drop the RDN-related mods from this op, because their
+ * equivalents were just setup by slap_modrdn2mods.
+ *
+ * If delOldRDN is TRUE then we should see a delete modop
+ * for oldDesc. We might see a replace instead.
+ * delete with no values: therefore newDesc != oldDesc.
+ * if oldNcount == 1, then Drop this op.
+ * delete with 1 value: can only be the oldRDN value. Drop op.
+ * delete with N values: Drop oldRDN value, keep remainder.
+ * replace with 1 value: if oldNcount == 1 and
+ * newDesc == oldDesc, Drop this op.
+ * Any other cases must be left intact.
+ *
+ * We should also see an add modop for newDesc. (But not if
+ * we got a replace modop due to delOldRDN.) If it has
+ * multiple values, we'll have to drop the new RDN value.
+ */
+ modtail = &op->orr_modlist;
+ if ( dni.delOldRDN ) {
+ for ( ml = &dni.mods; *ml; ml = &(*ml)->sml_next ) {
+ if ( (*ml)->sml_desc == dni.oldDesc ) {
+ mod = *ml;
+ if ( mod->sml_op == LDAP_MOD_REPLACE &&
+ dni.oldDesc != dni.newDesc ) {
+ /* This Replace is due to other Mods.
+ * Just let it ride.
+ */
+ continue;
+ }
+ if ( mod->sml_numvals <= 1 &&
+ dni.oldNcount == 1 &&
+ ( mod->sml_op == LDAP_MOD_DELETE ||
+ mod->sml_op == LDAP_MOD_REPLACE )) {
+ if ( mod->sml_op == LDAP_MOD_REPLACE )
+ got_replace = 1;
+ /* Drop this op */
+ *ml = mod->sml_next;
+ mod->sml_next = NULL;
+ slap_mods_free( mod, 1 );
+ break;
+ }
+ if ( mod->sml_op != LDAP_MOD_DELETE || mod->sml_numvals == 0 )
+ continue;
+ for ( m2 = op->orr_modlist; m2; m2=m2->sml_next ) {
+ if ( m2->sml_desc == dni.oldDesc &&
+ m2->sml_op == LDAP_MOD_DELETE ) break;
+ }
+ for ( i=0; i<mod->sml_numvals; i++ ) {
+ if ( bvmatch( &mod->sml_values[i], &m2->sml_values[0] )) {
+ mod->sml_numvals--;
+ ch_free( mod->sml_values[i].bv_val );
+ mod->sml_values[i] = mod->sml_values[mod->sml_numvals];
+ BER_BVZERO( &mod->sml_values[mod->sml_numvals] );
+ if ( mod->sml_nvalues ) {
+ ch_free( mod->sml_nvalues[i].bv_val );
+ mod->sml_nvalues[i] = mod->sml_nvalues[mod->sml_numvals];
+ BER_BVZERO( &mod->sml_nvalues[mod->sml_numvals] );
+ }
+ break;
+ }
+ }
+ if ( !mod->sml_numvals ) {
+ /* Drop this op */
+ *ml = mod->sml_next;
+ mod->sml_next = NULL;
+ slap_mods_free( mod, 1 );
+ }
+ break;
+ }
+ }
+ }
+ if ( !got_replace ) {
+ for ( ml = &dni.mods; *ml; ml = &(*ml)->sml_next ) {
+ if ( (*ml)->sml_desc == dni.newDesc ) {
+ mod = *ml;
+ if ( mod->sml_op != LDAP_MOD_ADD )
+ continue;
+ if ( mod->sml_numvals == 1 ) {
+ /* Drop this op */
+ *ml = mod->sml_next;
+ mod->sml_next = NULL;
+ slap_mods_free( mod, 1 );
+ break;
+ }
+ for ( m2 = op->orr_modlist; m2; m2=m2->sml_next ) {
+ if ( m2->sml_desc == dni.oldDesc &&
+ m2->sml_op == SLAP_MOD_SOFTADD ) break;
+ }
+ for ( i=0; i<mod->sml_numvals; i++ ) {
+ if ( bvmatch( &mod->sml_values[i], &m2->sml_values[0] )) {
+ mod->sml_numvals--;
+ ch_free( mod->sml_values[i].bv_val );
+ mod->sml_values[i] = mod->sml_values[mod->sml_numvals];
+ BER_BVZERO( &mod->sml_values[mod->sml_numvals] );
+ if ( mod->sml_nvalues ) {
+ ch_free( mod->sml_nvalues[i].bv_val );
+ mod->sml_nvalues[i] = mod->sml_nvalues[mod->sml_numvals];
+ BER_BVZERO( &mod->sml_nvalues[mod->sml_numvals] );
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ /* RDNs must be NUL-terminated for back-ldap */
+ noldp = op->orr_newrdn;
+ ber_dupbv_x( &op->orr_newrdn, &noldp, op->o_tmpmemctx );
+ noldp = op->orr_nnewrdn;
+ ber_dupbv_x( &op->orr_nnewrdn, &noldp, op->o_tmpmemctx );
+
+ /* Setup opattrs too */
+ {
+ static AttributeDescription *nullattr = NULL;
+ static AttributeDescription **const opattrs[] = {
+ &slap_schema.si_ad_entryCSN,
+ &slap_schema.si_ad_modifiersName,
+ &slap_schema.si_ad_modifyTimestamp,
+ &nullattr
+ };
+ AttributeDescription *opattr;
+ int i;
+
+ modtail = &m2;
+ /* pull mod off incoming modlist */
+ for ( i = 0; (opattr = *opattrs[i]) != NULL; i++ ) {
+ for ( ml = &dni.mods; *ml; ml = &(*ml)->sml_next )
+ {
+ if ( (*ml)->sml_desc == opattr ) {
+ mod = *ml;
+ *ml = mod->sml_next;
+ mod->sml_next = NULL;
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ break;
+ }
+ }
+ }
+ /* If there are still Modifications left, put the opattrs
+ * back, and let be_modify run. Otherwise, append the opattrs
+ * to the orr_modlist.
+ */
+ if ( dni.mods ) {
+ mod = dni.mods;
+ /* don't set a CSN for the rename op */
+ if ( syncCSN )
+ slap_graduate_commit_csn( op );
+ } else {
+ mod = op->orr_modlist;
+ just_rename = 1;
+ }
+ for ( ; mod->sml_next; mod=mod->sml_next );
+ mod->sml_next = m2;
+ }
+ op->o_bd = si->si_wbe;
+retry_modrdn:;
+ rs_reinit( &rs_modify, REP_RESULT );
+ rc = op->o_bd->be_modrdn( op, &rs_modify );
+
+ /* NOTE: noSuchObject should result because the new superior
+ * has not been added yet (ITS#6472) */
+ if ( rc == LDAP_NO_SUCH_OBJECT && op->orr_nnewSup != NULL ) {
+ Operation op2 = *op;
+ rc = syncrepl_add_glue_ancestors( &op2, entry );
+ if ( rc == LDAP_SUCCESS ) {
+ goto retry_modrdn;
+ }
+ }
+
+ op->o_tmpfree( op->orr_nnewrdn.bv_val, op->o_tmpmemctx );
+ op->o_tmpfree( op->orr_newrdn.bv_val, op->o_tmpmemctx );
+
+ slap_mods_free( op->orr_modlist, 1 );
+ Debug( LDAP_DEBUG_SYNC,
+ "syncrepl_entry: %s be_modrdn %s (%d)\n",
+ si->si_ridtxt, op->o_req_dn.bv_val, rc );
+ op->o_bd = be;
+ /* Renamed entries may still have other mods so just fallthru */
+ op->o_req_dn = entry->e_name;
+ op->o_req_ndn = entry->e_nname;
+ /* Use CSN on the modify */
+ if ( just_rename )
+ syncCSN = NULL;
+ else if ( syncCSN )
+ slap_queue_csn( op, syncCSN );
+ }
+ if ( dni.mods ) {
+ SlapReply rs_modify = {REP_RESULT};
+
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->orm_modlist = dni.mods;
+ op->orm_no_opattrs = 1;
+ op->o_bd = si->si_wbe;
+
+ rc = op->o_bd->be_modify( op, &rs_modify );
+ slap_mods_free( op->orm_modlist, 1 );
+ op->orm_no_opattrs = 0;
+ Debug( LDAP_DEBUG_SYNC,
+ "syncrepl_entry: %s be_modify %s (%d)\n",
+ si->si_ridtxt, op->o_req_dn.bv_val, rc );
+ if ( rs_modify.sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "syncrepl_entry: %s be_modify failed (%d)\n",
+ si->si_ridtxt, rs_modify.sr_err );
+ }
+ syncCSN = NULL;
+ op->o_bd = be;
+ } else if ( !dni.renamed ) {
+ Debug( LDAP_DEBUG_SYNC,
+ "syncrepl_entry: %s entry unchanged, ignored (%s)\n",
+ si->si_ridtxt, op->o_req_dn.bv_val );
+ if ( syncCSN ) {
+ slap_graduate_commit_csn( op );
+ syncCSN = NULL;
+ }
+ }
+ goto done;
+ case LDAP_SYNC_DELETE :
+ if ( !BER_BVISNULL( &dni.dn ) ) {
+ SlapReply rs_delete = {REP_RESULT};
+ op->o_req_dn = dni.dn;
+ op->o_req_ndn = dni.ndn;
+ op->o_tag = LDAP_REQ_DELETE;
+ op->o_bd = si->si_wbe;
+ if ( !syncCSN && si->si_syncCookie.ctxcsn ) {
+ slap_queue_csn( op, si->si_syncCookie.ctxcsn );
+ }
+ rc = op->o_bd->be_delete( op, &rs_delete );
+ Debug( LDAP_DEBUG_SYNC,
+ "syncrepl_entry: %s be_delete %s (%d)\n",
+ si->si_ridtxt, op->o_req_dn.bv_val, rc );
+ if ( rc == LDAP_NO_SUCH_OBJECT )
+ rc = LDAP_SUCCESS;
+
+ while ( rs_delete.sr_err == LDAP_SUCCESS
+ && op->o_delete_glue_parent ) {
+ op->o_delete_glue_parent = 0;
+ if ( !be_issuffix( be, &op->o_req_ndn ) ) {
+ slap_callback cb = { NULL };
+ cb.sc_response = syncrepl_null_callback;
+ dnParent( &op->o_req_ndn, &pdn );
+ op->o_req_dn = pdn;
+ op->o_req_ndn = pdn;
+ op->o_callback = &cb;
+ rs_reinit( &rs_delete, REP_RESULT );
+ op->o_bd->be_delete( op, &rs_delete );
+ } else {
+ break;
+ }
+ }
+ syncCSN = NULL;
+ op->o_bd = be;
+ }
+ goto done;
+
+ default :
+ Debug( LDAP_DEBUG_ANY,
+ "syncrepl_entry: %s unknown syncstate\n", si->si_ridtxt );
+ goto done;
+ }
+
+done:
+ slap_sl_free( syncUUID[1].bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &syncUUID[1] );
+ if ( !BER_BVISNULL( &dni.ndn ) ) {
+ op->o_tmpfree( dni.ndn.bv_val, op->o_tmpmemctx );
+ }
+ if ( !BER_BVISNULL( &dni.dn ) ) {
+ op->o_tmpfree( dni.dn.bv_val, op->o_tmpmemctx );
+ }
+ if ( entry ) {
+ entry_free( entry );
+ }
+ if ( syncCSN ) {
+ slap_graduate_commit_csn( op );
+ }
+ if ( !BER_BVISNULL( &op->o_csn ) && freecsn ) {
+ op->o_tmpfree( op->o_csn.bv_val, op->o_tmpmemctx );
+ }
+ BER_BVZERO( &op->o_csn );
+ return rc;
+}
+
+static struct berval gcbva[] = {
+ BER_BVC("top"),
+ BER_BVC("glue"),
+ BER_BVNULL
+};
+
+#define NP_DELETE_ONE 2
+
+static void
+syncrepl_del_nonpresent(
+ Operation *op,
+ syncinfo_t *si,
+ BerVarray uuids,
+ struct sync_cookie *sc,
+ int m )
+{
+ Backend* be = op->o_bd;
+ slap_callback cb = { NULL };
+ struct nonpresent_entry *np_list, *np_prev;
+ int rc;
+ AttributeName an[3]; /* entryUUID, entryCSN, NULL */
+
+ struct berval pdn = BER_BVNULL;
+ struct berval csn;
+
+ if ( si->si_rewrite ) {
+ op->o_req_dn = si->si_suffixm;
+ op->o_req_ndn = si->si_suffixm;
+ } else
+ {
+ op->o_req_dn = si->si_base;
+ op->o_req_ndn = si->si_base;
+ }
+
+ cb.sc_response = nonpresent_callback;
+ cb.sc_private = si;
+
+ op->o_callback = &cb;
+ op->o_tag = LDAP_REQ_SEARCH;
+ op->ors_scope = si->si_scope;
+ op->ors_deref = LDAP_DEREF_NEVER;
+ op->o_time = slap_get_time();
+ op->ors_tlimit = SLAP_NO_LIMIT;
+
+
+ if ( uuids ) {
+ Filter uf;
+ AttributeAssertion eq = ATTRIBUTEASSERTION_INIT;
+ int i;
+
+ op->ors_attrsonly = 1;
+ op->ors_attrs = slap_anlist_no_attrs;
+ op->ors_limit = NULL;
+ op->ors_filter = &uf;
+
+ uf.f_ava = &eq;
+ uf.f_av_desc = slap_schema.si_ad_entryUUID;
+ uf.f_next = NULL;
+ uf.f_choice = LDAP_FILTER_EQUALITY;
+ si->si_refreshDelete |= NP_DELETE_ONE;
+
+ for (i=0; uuids[i].bv_val; i++) {
+ SlapReply rs_search = {REP_RESULT};
+
+ op->ors_slimit = 1;
+ uf.f_av_value = uuids[i];
+ filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
+ Debug( LDAP_DEBUG_SYNC, "syncrepl_del_nonpresent: %s "
+ "checking non-present filter=%s\n",
+ si->si_ridtxt, op->ors_filterstr.bv_val );
+ rc = be->be_search( op, &rs_search );
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ }
+ si->si_refreshDelete ^= NP_DELETE_ONE;
+ } else {
+ Filter *cf, *of;
+ Filter mmf[2];
+ AttributeAssertion mmaa;
+ SlapReply rs_search = {REP_RESULT};
+
+ memset( &an[0], 0, 3 * sizeof( AttributeName ) );
+ an[0].an_name = slap_schema.si_ad_entryUUID->ad_cname;
+ an[0].an_desc = slap_schema.si_ad_entryUUID;
+ an[1].an_name = slap_schema.si_ad_entryCSN->ad_cname;
+ an[1].an_desc = slap_schema.si_ad_entryCSN;
+ op->ors_attrs = an;
+ op->ors_slimit = SLAP_NO_LIMIT;
+ op->ors_tlimit = SLAP_NO_LIMIT;
+ op->ors_limit = NULL;
+ op->ors_attrsonly = 0;
+ op->ors_filter = filter_dup( si->si_filter, op->o_tmpmemctx );
+ /* In multi-provider, updates can continue to arrive while
+ * we're searching. Limit the search result to entries
+ * older than our newest cookie CSN.
+ */
+ if ( SLAP_MULTIPROVIDER( op->o_bd )) {
+ Filter *f;
+ int i;
+
+ f = mmf;
+ f->f_choice = LDAP_FILTER_AND;
+ f->f_next = op->ors_filter;
+ f->f_and = f+1;
+ of = f->f_and;
+ f = of;
+ f->f_choice = LDAP_FILTER_LE;
+ f->f_ava = &mmaa;
+ f->f_av_desc = slap_schema.si_ad_entryCSN;
+ f->f_next = NULL;
+ BER_BVZERO( &f->f_av_value );
+ for ( i=0; i<sc->numcsns; i++ ) {
+ if ( ber_bvcmp( &sc->ctxcsn[i], &f->f_av_value ) > 0 )
+ f->f_av_value = sc->ctxcsn[i];
+ }
+ of = op->ors_filter;
+ op->ors_filter = mmf;
+ filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
+ } else {
+ cf = NULL;
+ op->ors_filterstr = si->si_filterstr;
+ }
+ op->o_nocaching = 1;
+
+
+ rc = be->be_search( op, &rs_search );
+ if ( SLAP_MULTIPROVIDER( op->o_bd )) {
+ op->ors_filter = of;
+ }
+ if ( op->ors_filter ) filter_free_x( op, op->ors_filter, 1 );
+ if ( op->ors_filterstr.bv_val != si->si_filterstr.bv_val ) {
+ op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+ }
+
+ }
+
+ op->o_nocaching = 0;
+
+ if ( !LDAP_LIST_EMPTY( &si->si_nonpresentlist ) ) {
+
+ if ( !BER_BVISNULL( &sc->delcsn ) ) {
+ Debug( LDAP_DEBUG_SYNC, "syncrepl_del_nonpresent: %s "
+ "using delcsn=%s\n",
+ si->si_ridtxt, sc->delcsn.bv_val );
+ csn = sc->delcsn;
+ } else if ( sc->ctxcsn && !BER_BVISNULL( &sc->ctxcsn[m] ) ) {
+ csn = sc->ctxcsn[m];
+ } else {
+ csn = si->si_syncCookie.ctxcsn[0];
+ }
+
+ op->o_bd = si->si_wbe;
+ slap_queue_csn( op, &csn );
+
+ np_list = LDAP_LIST_FIRST( &si->si_nonpresentlist );
+ while ( np_list != NULL ) {
+ SlapReply rs_delete = {REP_RESULT};
+
+ LDAP_LIST_REMOVE( np_list, npe_link );
+ np_prev = np_list;
+ np_list = LDAP_LIST_NEXT( np_list, npe_link );
+ op->o_tag = LDAP_REQ_DELETE;
+ op->o_callback = &cb;
+ cb.sc_response = syncrepl_null_callback;
+ cb.sc_private = si;
+ op->o_req_dn = *np_prev->npe_name;
+ op->o_req_ndn = *np_prev->npe_nname;
+ rc = op->o_bd->be_delete( op, &rs_delete );
+ Debug( LDAP_DEBUG_SYNC,
+ "syncrepl_del_nonpresent: %s be_delete %s (%d)\n",
+ si->si_ridtxt, op->o_req_dn.bv_val, rc );
+
+ if ( rs_delete.sr_err == LDAP_NOT_ALLOWED_ON_NONLEAF ) {
+ SlapReply rs_modify = {REP_RESULT};
+ Modifications mod1, mod2;
+ mod1.sml_op = LDAP_MOD_REPLACE;
+ mod1.sml_flags = 0;
+ mod1.sml_desc = slap_schema.si_ad_objectClass;
+ mod1.sml_type = mod1.sml_desc->ad_cname;
+ mod1.sml_numvals = 2;
+ mod1.sml_values = &gcbva[0];
+ mod1.sml_nvalues = NULL;
+ mod1.sml_next = &mod2;
+
+ mod2.sml_op = LDAP_MOD_REPLACE;
+ mod2.sml_flags = 0;
+ mod2.sml_desc = slap_schema.si_ad_structuralObjectClass;
+ mod2.sml_type = mod2.sml_desc->ad_cname;
+ mod2.sml_numvals = 1;
+ mod2.sml_values = &gcbva[1];
+ mod2.sml_nvalues = NULL;
+ mod2.sml_next = NULL;
+
+ op->o_tag = LDAP_REQ_MODIFY;
+ op->orm_modlist = &mod1;
+
+ rc = op->o_bd->be_modify( op, &rs_modify );
+ if ( mod2.sml_next ) slap_mods_free( mod2.sml_next, 1 );
+ }
+
+ while ( rs_delete.sr_err == LDAP_SUCCESS &&
+ op->o_delete_glue_parent ) {
+ op->o_delete_glue_parent = 0;
+ if ( !be_issuffix( be, &op->o_req_ndn ) ) {
+ slap_callback cb = { NULL };
+ cb.sc_response = syncrepl_null_callback;
+ dnParent( &op->o_req_ndn, &pdn );
+ op->o_req_dn = pdn;
+ op->o_req_ndn = pdn;
+ op->o_callback = &cb;
+ rs_reinit( &rs_delete, REP_RESULT );
+ /* give it a root privil ? */
+ op->o_bd->be_delete( op, &rs_delete );
+ } else {
+ break;
+ }
+ }
+
+ op->o_delete_glue_parent = 0;
+
+ ber_bvfree( np_prev->npe_name );
+ ber_bvfree( np_prev->npe_nname );
+ ch_free( np_prev );
+
+ if ( slapd_shutdown ) {
+ break;
+ }
+ }
+
+ slap_graduate_commit_csn( op );
+ op->o_bd = be;
+
+ op->o_tmpfree( op->o_csn.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &op->o_csn );
+ }
+
+ return;
+}
+
+static int
+syncrepl_add_glue_ancestors(
+ Operation* op,
+ Entry *e )
+{
+ Backend *be = op->o_bd;
+ slap_callback cb = { NULL };
+ Attribute *a;
+ int rc = LDAP_SUCCESS;
+ int suffrdns;
+ int i;
+ struct berval dn = BER_BVNULL;
+ struct berval ndn = BER_BVNULL;
+ Entry *glue;
+ struct berval ptr, nptr;
+ char *comma;
+
+ op->o_tag = LDAP_REQ_ADD;
+ op->o_callback = &cb;
+ cb.sc_response = syncrepl_null_callback;
+ cb.sc_private = NULL;
+
+ dn = e->e_name;
+ ndn = e->e_nname;
+
+ /* count RDNs in suffix */
+ if ( !BER_BVISEMPTY( &be->be_nsuffix[0] ) ) {
+ for ( i = 0, ptr = be->be_nsuffix[0], comma = ptr.bv_val; comma != NULL; comma = ber_bvchr( &ptr, ',' ) ) {
+ comma++;
+ ptr.bv_len -= comma - ptr.bv_val;
+ ptr.bv_val = comma;
+ i++;
+ }
+ suffrdns = i;
+ } else {
+ /* suffix is "" */
+ suffrdns = 0;
+ }
+
+ /* Start with BE suffix */
+ ptr = dn;
+ for ( i = 0; i < suffrdns; i++ ) {
+ comma = ber_bvrchr( &ptr, ',' );
+ if ( comma != NULL ) {
+ ptr.bv_len = comma - ptr.bv_val;
+ } else {
+ ptr.bv_len = 0;
+ break;
+ }
+ }
+
+ if ( !BER_BVISEMPTY( &ptr ) ) {
+ dn.bv_len -= ptr.bv_len + ( suffrdns != 0 );
+ dn.bv_val += ptr.bv_len + ( suffrdns != 0 );
+ }
+
+ /* the normalizedDNs are always the same length, no counting
+ * required.
+ */
+ nptr = ndn;
+ if ( ndn.bv_len > be->be_nsuffix[0].bv_len ) {
+ ndn.bv_val += ndn.bv_len - be->be_nsuffix[0].bv_len;
+ ndn.bv_len = be->be_nsuffix[0].bv_len;
+
+ nptr.bv_len = ndn.bv_val - nptr.bv_val - 1;
+
+ } else {
+ nptr.bv_len = 0;
+ }
+
+ while ( ndn.bv_val > e->e_nname.bv_val ) {
+ SlapReply rs_add = {REP_RESULT};
+
+ glue = entry_alloc();
+ ber_dupbv( &glue->e_name, &dn );
+ ber_dupbv( &glue->e_nname, &ndn );
+
+ a = attr_alloc( slap_schema.si_ad_objectClass );
+
+ a->a_numvals = 2;
+ a->a_vals = ch_calloc( 3, sizeof( struct berval ) );
+ ber_dupbv( &a->a_vals[0], &gcbva[0] );
+ ber_dupbv( &a->a_vals[1], &gcbva[1] );
+ ber_dupbv( &a->a_vals[2], &gcbva[2] );
+
+ a->a_nvals = a->a_vals;
+
+ a->a_next = glue->e_attrs;
+ glue->e_attrs = a;
+
+ a = attr_alloc( slap_schema.si_ad_structuralObjectClass );
+
+ a->a_numvals = 1;
+ a->a_vals = ch_calloc( 2, sizeof( struct berval ) );
+ ber_dupbv( &a->a_vals[0], &gcbva[1] );
+ ber_dupbv( &a->a_vals[1], &gcbva[2] );
+
+ a->a_nvals = a->a_vals;
+
+ a->a_next = glue->e_attrs;
+ glue->e_attrs = a;
+
+ op->o_req_dn = glue->e_name;
+ op->o_req_ndn = glue->e_nname;
+ op->ora_e = glue;
+ rc = be->be_add ( op, &rs_add );
+ if ( rs_add.sr_err == LDAP_SUCCESS ) {
+ if ( op->ora_e == glue )
+ be_entry_release_w( op, glue );
+ } else {
+ /* incl. ALREADY EXIST */
+ entry_free( glue );
+ if ( rs_add.sr_err != LDAP_ALREADY_EXISTS ) {
+ entry_free( e );
+ return rc;
+ }
+ }
+
+ /* Move to next child */
+ comma = ber_bvrchr( &ptr, ',' );
+ if ( comma == NULL ) {
+ break;
+ }
+ ptr.bv_len = comma - ptr.bv_val;
+
+ dn.bv_val = ++comma;
+ dn.bv_len = e->e_name.bv_len - (dn.bv_val - e->e_name.bv_val);
+
+ comma = ber_bvrchr( &nptr, ',' );
+ assert( comma != NULL );
+ nptr.bv_len = comma - nptr.bv_val;
+
+ ndn.bv_val = ++comma;
+ ndn.bv_len = e->e_nname.bv_len - (ndn.bv_val - e->e_nname.bv_val);
+ }
+
+ return rc;
+}
+
+int
+syncrepl_add_glue(
+ Operation* op,
+ Entry *e )
+{
+ slap_callback cb = { NULL };
+ int rc;
+ Backend *be = op->o_bd;
+ SlapReply rs_add = {REP_RESULT};
+
+ rc = syncrepl_add_glue_ancestors( op, e );
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ case LDAP_ALREADY_EXISTS:
+ break;
+
+ default:
+ return rc;
+ }
+
+ op->o_tag = LDAP_REQ_ADD;
+ op->o_callback = &cb;
+ cb.sc_response = syncrepl_null_callback;
+ cb.sc_private = NULL;
+
+ op->o_req_dn = e->e_name;
+ op->o_req_ndn = e->e_nname;
+ op->ora_e = e;
+ rc = be->be_add ( op, &rs_add );
+ if ( rs_add.sr_err == LDAP_SUCCESS ) {
+ if ( op->ora_e == e )
+ be_entry_release_w( op, e );
+ } else {
+ entry_free( e );
+ }
+
+ return rc;
+}
+
+static int
+syncrepl_dsee_update(
+ syncinfo_t *si,
+ Operation *op
+)
+{
+ Backend *be = op->o_bd;
+ Modifications mod;
+ struct berval first = BER_BVNULL;
+ slap_callback cb = { NULL };
+ SlapReply rs_modify = {REP_RESULT};
+ char valbuf[sizeof("18446744073709551615")];
+ struct berval bvals[2];
+ int rc;
+
+ if ( si->si_lastchange == si->si_prevchange )
+ return 0;
+
+ mod.sml_op = LDAP_MOD_REPLACE;
+ mod.sml_desc = sy_ad_dseeLastChange;
+ mod.sml_type = mod.sml_desc->ad_cname;
+ mod.sml_flags = SLAP_MOD_INTERNAL;
+ mod.sml_nvalues = NULL;
+ mod.sml_values = bvals;
+ mod.sml_numvals = 1;
+ mod.sml_next = NULL;
+ bvals[0].bv_val = valbuf;
+ bvals[0].bv_len = sprintf( valbuf, "%lu", si->si_lastchange );
+ BER_BVZERO( &bvals[1] );
+
+ op->o_bd = si->si_wbe;
+
+ op->o_tag = LDAP_REQ_MODIFY;
+
+ cb.sc_response = syncrepl_null_callback;
+ cb.sc_private = si;
+
+ op->o_callback = &cb;
+ op->o_req_dn = si->si_contextdn;
+ op->o_req_ndn = si->si_contextdn;
+
+ /* update contextCSN */
+ op->o_dont_replicate = 1;
+
+ /* avoid timestamp collisions */
+ slap_op_time( &op->o_time, &op->o_tincr );
+
+ op->orm_modlist = &mod;
+ op->orm_no_opattrs = 1;
+ rc = op->o_bd->be_modify( op, &rs_modify );
+
+ op->o_bd = be;
+ si->si_prevchange = si->si_lastchange;
+
+ return rc;
+}
+
+static int
+syncrepl_updateCookie(
+ syncinfo_t *si,
+ Operation *op,
+ struct sync_cookie *syncCookie,
+ int save )
+{
+ Backend *be = op->o_bd;
+ Modifications mod;
+ struct berval first = BER_BVNULL;
+ struct sync_cookie sc;
+#ifdef CHECK_CSN
+ Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax;
+#endif
+
+ int rc, i, j, changed = 0;
+ ber_len_t len;
+
+ slap_callback cb = { NULL };
+ SlapReply rs_modify = {REP_RESULT};
+
+ mod.sml_op = LDAP_MOD_REPLACE;
+ mod.sml_desc = slap_schema.si_ad_contextCSN;
+ mod.sml_type = mod.sml_desc->ad_cname;
+ mod.sml_flags = SLAP_MOD_INTERNAL;
+ mod.sml_nvalues = NULL;
+ mod.sml_next = NULL;
+
+ ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_mutex );
+ while ( si->si_cookieState->cs_updating )
+ ldap_pvt_thread_cond_wait( &si->si_cookieState->cs_cond, &si->si_cookieState->cs_mutex );
+
+#ifdef CHECK_CSN
+ for ( i=0; i<syncCookie->numcsns; i++ ) {
+ assert( !syn->ssyn_validate( syn, syncCookie->ctxcsn+i ));
+ }
+ for ( i=0; i<si->si_cookieState->cs_num; i++ ) {
+ assert( !syn->ssyn_validate( syn, si->si_cookieState->cs_vals+i ));
+ }
+#endif
+
+ /* clone the cookieState CSNs so we can Replace the whole thing */
+ sc.numcsns = si->si_cookieState->cs_num;
+ if ( sc.numcsns ) {
+ ber_bvarray_dup_x( &sc.ctxcsn, si->si_cookieState->cs_vals, NULL );
+ sc.sids = ch_malloc( sc.numcsns * sizeof(int));
+ for ( i=0; i<sc.numcsns; i++ )
+ sc.sids[i] = si->si_cookieState->cs_sids[i];
+ } else {
+ sc.ctxcsn = NULL;
+ sc.sids = NULL;
+ }
+
+ /* find any CSNs in the syncCookie that are newer than the cookieState */
+ for ( i=0; i<syncCookie->numcsns; i++ ) {
+ for ( j=0; j<sc.numcsns; j++ ) {
+ if ( syncCookie->sids[i] < sc.sids[j] )
+ break;
+ if ( syncCookie->sids[i] != sc.sids[j] )
+ continue;
+ len = syncCookie->ctxcsn[i].bv_len;
+ if ( len > sc.ctxcsn[j].bv_len )
+ len = sc.ctxcsn[j].bv_len;
+ if ( memcmp( syncCookie->ctxcsn[i].bv_val,
+ sc.ctxcsn[j].bv_val, len ) > 0 ) {
+ ber_bvreplace( &sc.ctxcsn[j], &syncCookie->ctxcsn[i] );
+ changed = 1;
+ if ( BER_BVISNULL( &first ) ||
+ memcmp( syncCookie->ctxcsn[i].bv_val, first.bv_val, first.bv_len ) > 0 ) {
+ first = syncCookie->ctxcsn[i];
+ }
+ }
+ break;
+ }
+ /* there was no match for this SID, it's a new CSN */
+ if ( j == sc.numcsns ||
+ syncCookie->sids[i] != sc.sids[j] ) {
+ slap_insert_csn_sids( &sc, j, syncCookie->sids[i],
+ &syncCookie->ctxcsn[i] );
+ if ( BER_BVISNULL( &first ) ||
+ memcmp( syncCookie->ctxcsn[i].bv_val, first.bv_val, first.bv_len ) > 0 ) {
+ first = syncCookie->ctxcsn[i];
+ }
+ changed = 1;
+ }
+ }
+ /* Should never happen, ITS#5065 */
+ if ( BER_BVISNULL( &first ) || !changed ) {
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
+ ber_bvarray_free( sc.ctxcsn );
+ ch_free( sc.sids );
+ return 0;
+ }
+
+ si->si_cookieState->cs_updating = 1;
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
+
+ op->o_bd = si->si_wbe;
+ slap_queue_csn( op, &first );
+
+ op->o_tag = LDAP_REQ_MODIFY;
+
+ cb.sc_response = syncrepl_null_callback;
+ cb.sc_private = si;
+
+ op->o_callback = &cb;
+ op->o_req_dn = si->si_contextdn;
+ op->o_req_ndn = si->si_contextdn;
+
+ /* update contextCSN */
+ op->o_dont_replicate = !save;
+
+ /* avoid timestamp collisions */
+ if ( save )
+ slap_op_time( &op->o_time, &op->o_tincr );
+
+ mod.sml_numvals = sc.numcsns;
+ mod.sml_values = sc.ctxcsn;
+
+ op->orm_modlist = &mod;
+ op->orm_no_opattrs = 1;
+ rc = op->o_bd->be_modify( op, &rs_modify );
+
+ if ( rs_modify.sr_err == LDAP_NO_SUCH_OBJECT &&
+ SLAP_SYNC_SUBENTRY( op->o_bd )) {
+ const char *text;
+ char txtbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof txtbuf;
+ Entry *e = slap_create_context_csn_entry( op->o_bd, NULL );
+ rs_reinit( &rs_modify, REP_RESULT );
+ rc = slap_mods2entry( &mod, &e, 0, 1, &text, txtbuf, textlen);
+ slap_queue_csn( op, &first );
+ op->o_tag = LDAP_REQ_ADD;
+ op->ora_e = e;
+ rc = op->o_bd->be_add( op, &rs_modify );
+ if ( e == op->ora_e )
+ be_entry_release_w( op, op->ora_e );
+ }
+
+ op->orm_no_opattrs = 0;
+ op->o_dont_replicate = 0;
+ ldap_pvt_thread_mutex_lock( &si->si_cookieState->cs_mutex );
+
+ if ( rs_modify.sr_err == LDAP_SUCCESS ) {
+ slap_sync_cookie_free( &si->si_syncCookie, 0 );
+ ber_bvarray_free( si->si_cookieState->cs_vals );
+ ch_free( si->si_cookieState->cs_sids );
+ si->si_cookieState->cs_vals = sc.ctxcsn;
+ si->si_cookieState->cs_sids = sc.sids;
+ si->si_cookieState->cs_num = sc.numcsns;
+
+ /* Don't just dup the provider's cookie, recreate it */
+ si->si_syncCookie.numcsns = si->si_cookieState->cs_num;
+ ber_bvarray_dup_x( &si->si_syncCookie.ctxcsn, si->si_cookieState->cs_vals, NULL );
+ si->si_syncCookie.sids = ch_malloc( si->si_cookieState->cs_num * sizeof(int) );
+ for ( i=0; i<si->si_cookieState->cs_num; i++ )
+ si->si_syncCookie.sids[i] = si->si_cookieState->cs_sids[i];
+
+ si->si_cookieState->cs_age++;
+ si->si_cookieAge = si->si_cookieState->cs_age;
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "syncrepl_updateCookie: %s be_modify failed (%d)\n",
+ si->si_ridtxt, rs_modify.sr_err );
+ ch_free( sc.sids );
+ ber_bvarray_free( sc.ctxcsn );
+ }
+
+#ifdef CHECK_CSN
+ for ( i=0; i<si->si_cookieState->cs_num; i++ ) {
+ assert( !syn->ssyn_validate( syn, si->si_cookieState->cs_vals+i ));
+ }
+#endif
+
+ si->si_cookieState->cs_updating = 0;
+ ldap_pvt_thread_cond_broadcast( &si->si_cookieState->cs_cond );
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
+
+ op->o_bd = be;
+ op->o_tmpfree( op->o_csn.bv_val, op->o_tmpmemctx );
+ BER_BVZERO( &op->o_csn );
+ if ( mod.sml_next ) slap_mods_free( mod.sml_next, 1 );
+
+ return rc;
+}
+
+/* Compare the attribute from the old entry to the one in the new
+ * entry. The Modifications from the new entry will either be left
+ * in place, or changed to an Add or Delete as needed.
+ */
+static void
+attr_cmp( Operation *op, Attribute *old, Attribute *new,
+ Modifications ***mret, Modifications ***mcur )
+{
+ int i, j;
+ Modifications *mod, **modtail;
+
+ modtail = *mret;
+
+ if ( old ) {
+ int n, o, nn, no;
+ struct berval **adds, **dels;
+ /* count old and new */
+ for ( o=0; old->a_vals[o].bv_val; o++ ) ;
+ for ( n=0; new->a_vals[n].bv_val; n++ ) ;
+
+ /* there MUST be both old and new values */
+ assert( o != 0 );
+ assert( n != 0 );
+ j = 0;
+
+ adds = op->o_tmpalloc( sizeof(struct berval *) * n, op->o_tmpmemctx );
+ dels = op->o_tmpalloc( sizeof(struct berval *) * o, op->o_tmpmemctx );
+
+ for ( i=0; i<o; i++ ) dels[i] = &old->a_vals[i];
+ for ( i=0; i<n; i++ ) adds[i] = &new->a_vals[i];
+
+ nn = n; no = o;
+
+ for ( i=0; i<o; i++ ) {
+ for ( j=0; j<n; j++ ) {
+ if ( !adds[j] )
+ continue;
+ if ( bvmatch( dels[i], adds[j] ) ) {
+ no--;
+ nn--;
+ adds[j] = NULL;
+ dels[i] = NULL;
+ break;
+ }
+ }
+ }
+
+ /* Don't delete/add an objectClass, always use the replace op.
+ * Modify would fail if provider has replaced entry with a new,
+ * and the new explicitly includes a superior of a class that was
+ * only included implicitly in the old entry. Ref ITS#5517.
+ *
+ * Also use replace op if attr has no equality matching rule.
+ * (ITS#5781)
+ */
+ if ( ( nn || ( no > 0 && no < o ) ) &&
+ ( old->a_desc == slap_schema.si_ad_objectClass ||
+ !old->a_desc->ad_type->sat_equality ) )
+ {
+ no = o;
+ }
+
+ i = j;
+ /* all old values were deleted, just use the replace op */
+ if ( no == o ) {
+ i = j-1;
+ } else if ( no ) {
+ /* delete some values */
+ mod = ch_malloc( sizeof( Modifications ) );
+ mod->sml_op = LDAP_MOD_DELETE;
+ mod->sml_flags = 0;
+ mod->sml_desc = old->a_desc;
+ mod->sml_type = mod->sml_desc->ad_cname;
+ mod->sml_numvals = no;
+ mod->sml_values = ch_malloc( ( no + 1 ) * sizeof(struct berval) );
+ if ( old->a_vals != old->a_nvals ) {
+ mod->sml_nvalues = ch_malloc( ( no + 1 ) * sizeof(struct berval) );
+ } else {
+ mod->sml_nvalues = NULL;
+ }
+ j = 0;
+ for ( i = 0; i < o; i++ ) {
+ if ( !dels[i] ) continue;
+ ber_dupbv( &mod->sml_values[j], &old->a_vals[i] );
+ if ( mod->sml_nvalues ) {
+ ber_dupbv( &mod->sml_nvalues[j], &old->a_nvals[i] );
+ }
+ j++;
+ }
+ BER_BVZERO( &mod->sml_values[j] );
+ if ( mod->sml_nvalues ) {
+ BER_BVZERO( &mod->sml_nvalues[j] );
+ }
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ i = j;
+ }
+ op->o_tmpfree( dels, op->o_tmpmemctx );
+ /* some values were added */
+ if ( nn && no < o ) {
+ mod = ch_malloc( sizeof( Modifications ) );
+ if ( is_at_single_value( old->a_desc->ad_type ))
+ mod->sml_op = LDAP_MOD_REPLACE;
+ else
+ mod->sml_op = LDAP_MOD_ADD;
+ mod->sml_flags = 0;
+ mod->sml_desc = old->a_desc;
+ mod->sml_type = mod->sml_desc->ad_cname;
+ mod->sml_numvals = nn;
+ mod->sml_values = ch_malloc( ( nn + 1 ) * sizeof(struct berval) );
+ if ( old->a_vals != old->a_nvals ) {
+ mod->sml_nvalues = ch_malloc( ( nn + 1 ) * sizeof(struct berval) );
+ } else {
+ mod->sml_nvalues = NULL;
+ }
+ j = 0;
+ for ( i = 0; i < n; i++ ) {
+ if ( !adds[i] ) continue;
+ ber_dupbv( &mod->sml_values[j], &new->a_vals[i] );
+ if ( mod->sml_nvalues ) {
+ ber_dupbv( &mod->sml_nvalues[j], &new->a_nvals[i] );
+ }
+ j++;
+ }
+ BER_BVZERO( &mod->sml_values[j] );
+ if ( mod->sml_nvalues ) {
+ BER_BVZERO( &mod->sml_nvalues[j] );
+ }
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ i = j;
+ }
+ op->o_tmpfree( adds, op->o_tmpmemctx );
+ } else {
+ /* new attr, just use the new mod */
+ i = 0;
+ j = 1;
+ }
+ /* advance to next element */
+ mod = **mcur;
+ if ( mod ) {
+ if ( i != j ) {
+ **mcur = mod->sml_next;
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ } else {
+ *mcur = &mod->sml_next;
+ }
+ }
+ *mret = modtail;
+}
+
+/* Generate a set of modifications to change the old entry into the
+ * new one. On input ml is a list of modifications equivalent to
+ * the new entry. It will be massaged and the result will be stored
+ * in mods.
+ */
+void syncrepl_diff_entry( Operation *op, Attribute *old, Attribute *new,
+ Modifications **mods, Modifications **ml, int is_ctx)
+{
+ Modifications **modtail = mods;
+
+ /* We assume that attributes are saved in the same order
+ * in the remote and local databases. So if we walk through
+ * the attributeDescriptions one by one they should match in
+ * lock step. If not, look for an add or delete.
+ */
+ while ( old && new )
+ {
+ /* If we've seen this before, use its mod now */
+ if ( new->a_flags & SLAP_ATTR_IXADD ) {
+ attr_cmp( op, NULL, new, &modtail, &ml );
+ new = new->a_next;
+ continue;
+ }
+ /* Skip contextCSN */
+ if ( is_ctx && old->a_desc ==
+ slap_schema.si_ad_contextCSN ) {
+ old = old->a_next;
+ continue;
+ }
+
+ if ( old->a_desc != new->a_desc ) {
+ Modifications *mod;
+ Attribute *tmp;
+
+ /* If it's just been re-added later,
+ * remember that we've seen it.
+ */
+ tmp = attr_find( new, old->a_desc );
+ if ( tmp ) {
+ tmp->a_flags |= SLAP_ATTR_IXADD;
+ } else {
+ /* If it's a new attribute, pull it in.
+ */
+ tmp = attr_find( old, new->a_desc );
+ if ( !tmp ) {
+ attr_cmp( op, NULL, new, &modtail, &ml );
+ new = new->a_next;
+ continue;
+ }
+ /* Delete old attr */
+ mod = ch_malloc( sizeof( Modifications ) );
+ mod->sml_op = LDAP_MOD_DELETE;
+ mod->sml_flags = 0;
+ mod->sml_desc = old->a_desc;
+ mod->sml_type = mod->sml_desc->ad_cname;
+ mod->sml_numvals = 0;
+ mod->sml_values = NULL;
+ mod->sml_nvalues = NULL;
+ *modtail = mod;
+ modtail = &mod->sml_next;
+ }
+ old = old->a_next;
+ continue;
+ }
+ /* kludge - always update modifiersName so that it
+ * stays co-located with the other mod opattrs. But only
+ * if we know there are other valid mods.
+ */
+ if ( *mods && ( old->a_desc == slap_schema.si_ad_modifiersName ||
+ old->a_desc == slap_schema.si_ad_modifyTimestamp ))
+ attr_cmp( op, NULL, new, &modtail, &ml );
+ else
+ attr_cmp( op, old, new, &modtail, &ml );
+ new = new->a_next;
+ old = old->a_next;
+ }
+
+ /* These are all missing from provider */
+ while ( old ) {
+ Modifications *mod = ch_malloc( sizeof( Modifications ) );
+
+ mod->sml_op = LDAP_MOD_DELETE;
+ mod->sml_flags = 0;
+ mod->sml_desc = old->a_desc;
+ mod->sml_type = mod->sml_desc->ad_cname;
+ mod->sml_numvals = 0;
+ mod->sml_values = NULL;
+ mod->sml_nvalues = NULL;
+
+ *modtail = mod;
+ modtail = &mod->sml_next;
+
+ old = old->a_next;
+ }
+
+ /* Newly added attributes */
+ while ( new ) {
+ attr_cmp( op, NULL, new, &modtail, &ml );
+
+ new = new->a_next;
+ }
+
+ *modtail = *ml;
+ *ml = NULL;
+}
+
+/* shallow copy attrs, excluding non-replicated attrs */
+static Attribute *
+attrs_exdup( Operation *op, dninfo *dni, Attribute *attrs )
+{
+ int i;
+ Attribute *tmp, *anew;
+
+ if ( attrs == NULL ) return NULL;
+
+ /* count attrs */
+ for ( tmp = attrs,i=0; tmp; tmp=tmp->a_next ) i++;
+
+ anew = op->o_tmpalloc( i * sizeof(Attribute), op->o_tmpmemctx );
+ for ( tmp = anew; attrs; attrs=attrs->a_next ) {
+ int flag = is_at_operational( attrs->a_desc->ad_type ) ? dni->si->si_allopattrs :
+ dni->si->si_allattrs;
+ if ( !flag && !ad_inlist( attrs->a_desc, dni->si->si_anlist ))
+ continue;
+ if ( dni->si->si_exattrs && ad_inlist( attrs->a_desc, dni->si->si_exanlist ))
+ continue;
+ *tmp = *attrs;
+ tmp->a_next = tmp+1;
+ tmp++;
+ }
+ if ( tmp == anew ) {
+ /* excluded everything */
+ op->o_tmpfree( anew, op->o_tmpmemctx );
+ return NULL;
+ }
+ tmp[-1].a_next = NULL;
+ return anew;
+}
+
+static int
+dn_callback(
+ Operation* op,
+ SlapReply* rs )
+{
+ dninfo *dni = op->o_callback->sc_private;
+
+ if ( rs->sr_type == REP_SEARCH ) {
+ if ( !BER_BVISNULL( &dni->dn ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "dn_callback : consistency error - "
+ "entryUUID is not unique\n" );
+ } else {
+ ber_dupbv_x( &dni->dn, &rs->sr_entry->e_name, op->o_tmpmemctx );
+ ber_dupbv_x( &dni->ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
+ /* If there is a new entry, see if it differs from the old.
+ * We compare the non-normalized values so that cosmetic changes
+ * in the provider are always propagated.
+ */
+ if ( dni->new_entry ) {
+ Attribute *old, *new;
+ struct berval old_rdn, new_rdn;
+ struct berval old_p, new_p;
+ int is_ctx, new_sup = 0;
+
+#ifdef LDAP_CONTROL_X_DIRSYNC
+ if ( dni->syncstate != MSAD_DIRSYNC_MODIFY )
+#endif
+ {
+ /* If old entry is not a glue entry, make sure new entry
+ * is actually newer than old entry
+ */
+ if ( !is_entry_glue( rs->sr_entry )) {
+ old = attr_find( rs->sr_entry->e_attrs,
+ slap_schema.si_ad_entryCSN );
+ new = attr_find( dni->new_entry->e_attrs,
+ slap_schema.si_ad_entryCSN );
+ if ( new && old ) {
+ int rc;
+ ber_len_t len = old->a_vals[0].bv_len;
+ if ( len > new->a_vals[0].bv_len )
+ len = new->a_vals[0].bv_len;
+ rc = memcmp( old->a_vals[0].bv_val,
+ new->a_vals[0].bv_val, len );
+ if ( rc > 0 ) {
+ Debug( LDAP_DEBUG_SYNC,
+ "dn_callback : new entry is older than ours "
+ "%s ours %s, new %s\n",
+ rs->sr_entry->e_name.bv_val,
+ old->a_vals[0].bv_val,
+ new->a_vals[0].bv_val );
+ return LDAP_SUCCESS;
+ } else if ( rc == 0 ) {
+ Debug( LDAP_DEBUG_SYNC,
+ "dn_callback : entries have identical CSN "
+ "%s %s\n",
+ rs->sr_entry->e_name.bv_val,
+ old->a_vals[0].bv_val );
+ return LDAP_SUCCESS;
+ }
+ }
+ }
+
+ is_ctx = dn_match( &rs->sr_entry->e_nname,
+ &op->o_bd->be_nsuffix[0] );
+ }
+
+ /* Did the DN change?
+ * case changes in the parent are ignored,
+ * we only want to know if the RDN was
+ * actually changed.
+ */
+ dnRdn( &rs->sr_entry->e_name, &old_rdn );
+ dnRdn( &dni->new_entry->e_name, &new_rdn );
+ dnParent( &rs->sr_entry->e_nname, &old_p );
+ dnParent( &dni->new_entry->e_nname, &new_p );
+
+ new_sup = !dn_match( &old_p, &new_p );
+ if ( !dn_match( &old_rdn, &new_rdn ) || new_sup )
+ {
+ struct berval oldRDN, oldVal;
+ AttributeDescription *ad = NULL;
+ int oldpos, newpos;
+ Attribute *a;
+
+ dni->renamed = 1;
+ if ( new_sup )
+ dni->nnewSup = new_p;
+
+ /* See if the oldRDN was deleted */
+ dnRdn( &rs->sr_entry->e_nname, &oldRDN );
+ oldVal.bv_val = strchr(oldRDN.bv_val, '=') + 1;
+ oldVal.bv_len = oldRDN.bv_len - ( oldVal.bv_val -
+ oldRDN.bv_val );
+ oldRDN.bv_len -= oldVal.bv_len + 1;
+ slap_bv2ad( &oldRDN, &ad, &rs->sr_text );
+ dni->oldDesc = ad;
+ for ( oldpos=0, a=rs->sr_entry->e_attrs;
+ a && a->a_desc != ad; oldpos++, a=a->a_next );
+ /* a should not be NULL but apparently it happens.
+ * ITS#7144
+ */
+ if ( a ) {
+ dni->oldNcount = a->a_numvals;
+ for ( newpos=0, a=dni->new_entry->e_attrs;
+ a && a->a_desc != ad; newpos++, a=a->a_next );
+ if ( !a || oldpos != newpos || attr_valfind( a,
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_VALUE_OF_SYNTAX,
+ &oldVal, NULL, op->o_tmpmemctx ) != LDAP_SUCCESS )
+ {
+ dni->delOldRDN = 1;
+ }
+ }
+ /* Get the newRDN's desc */
+ dnRdn( &dni->new_entry->e_nname, &oldRDN );
+ oldVal.bv_val = strchr(oldRDN.bv_val, '=');
+ oldRDN.bv_len = oldVal.bv_val - oldRDN.bv_val;
+ ad = NULL;
+ slap_bv2ad( &oldRDN, &ad, &rs->sr_text );
+ dni->newDesc = ad;
+
+ /* A ModDN has happened, but in Refresh mode other
+ * changes may have occurred before we picked it up.
+ * So fallthru to regular Modify processing.
+ */
+ }
+
+#ifdef LDAP_CONTROL_X_DIRSYNC
+ if ( dni->syncstate == MSAD_DIRSYNC_MODIFY ) {
+ /* DirSync actually sends a diff already, mostly.
+ * It has no way to indicate deletion of single-valued attrs.
+ * FIXME: should do an auxiliary search to get the true
+ * entry contents.
+ */
+ dni->mods = *dni->modlist;
+ *dni->modlist = NULL;
+ } else
+#endif
+ {
+ Attribute *old = attrs_exdup( op, dni, rs->sr_entry->e_attrs );
+ syncrepl_diff_entry( op, old,
+ dni->new_entry->e_attrs, &dni->mods, dni->modlist,
+ is_ctx );
+ op->o_tmpfree( old, op->o_tmpmemctx );
+ }
+ }
+ }
+ } else if ( rs->sr_type == REP_RESULT ) {
+ if ( rs->sr_err == LDAP_SIZELIMIT_EXCEEDED ) {
+ Debug( LDAP_DEBUG_ANY,
+ "dn_callback : consistency error - "
+ "entryUUID is not unique\n" );
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+nonpresent_callback(
+ Operation* op,
+ SlapReply* rs )
+{
+ syncinfo_t *si = op->o_callback->sc_private;
+ Attribute *a;
+ int count = 0;
+ char *present_uuid = NULL;
+ struct nonpresent_entry *np_entry;
+ struct sync_cookie *syncCookie = op->o_controls[slap_cids.sc_LDAPsync];
+
+ if ( rs->sr_type == REP_RESULT ) {
+ count = presentlist_free( si->si_presentlist );
+ si->si_presentlist = NULL;
+ Debug( LDAP_DEBUG_SYNC, "nonpresent_callback: %s "
+ "had %d items left in the list\n", si->si_ridtxt, count );
+
+ } else if ( rs->sr_type == REP_SEARCH ) {
+ if ( !( si->si_refreshDelete & NP_DELETE_ONE ) ) {
+ a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryUUID );
+
+ if ( a ) {
+ present_uuid = presentlist_find( si->si_presentlist, &a->a_nvals[0] );
+ }
+
+ Debug(LDAP_DEBUG_SYNC, "nonpresent_callback: "
+ "%s %spresent UUID %s, dn %s\n",
+ si->si_ridtxt,
+ present_uuid ? "" : "non",
+ a ? a->a_vals[0].bv_val : "<missing>",
+ rs->sr_entry->e_name.bv_val );
+
+ if ( a == NULL ) return 0;
+ }
+
+ if ( present_uuid == NULL ) {
+ int covered = 1; /* covered by our new contextCSN? */
+
+ if ( !syncCookie )
+ syncCookie = &si->si_syncCookie;
+
+ /* TODO: This can go once we can build a filter that takes care of
+ * the check for us */
+ a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryCSN );
+ if ( a ) {
+ int i, sid = slap_parse_csn_sid( &a->a_nvals[0] );
+ if ( sid != -1 ) {
+ covered = 0;
+ for ( i=0; i < syncCookie->numcsns && syncCookie->sids[i] <= sid; i++ ) {
+ if ( syncCookie->sids[i] == sid &&
+ ber_bvcmp( &a->a_nvals[0], &syncCookie->ctxcsn[i] ) <= 0 ) {
+ covered = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ if ( covered ) {
+ np_entry = (struct nonpresent_entry *)
+ ch_calloc( 1, sizeof( struct nonpresent_entry ) );
+ np_entry->npe_name = ber_dupbv( NULL, &rs->sr_entry->e_name );
+ np_entry->npe_nname = ber_dupbv( NULL, &rs->sr_entry->e_nname );
+ LDAP_LIST_INSERT_HEAD( &si->si_nonpresentlist, np_entry, npe_link );
+ Debug( LDAP_DEBUG_SYNC, "nonpresent_callback: %s "
+ "adding entry %s to non-present list\n",
+ si->si_ridtxt, np_entry->npe_name->bv_val );
+ }
+
+ } else {
+ presentlist_delete( &si->si_presentlist, &a->a_nvals[0] );
+ ch_free( present_uuid );
+ }
+ }
+ return LDAP_SUCCESS;
+}
+
+static struct berval *
+slap_uuidstr_from_normalized(
+ struct berval* uuidstr,
+ struct berval* normalized,
+ void *ctx )
+{
+#if 0
+ struct berval *new;
+ unsigned char nibble;
+ int i, d = 0;
+
+ if ( normalized == NULL ) return NULL;
+ if ( normalized->bv_len != 16 ) return NULL;
+
+ if ( uuidstr ) {
+ new = uuidstr;
+ } else {
+ new = (struct berval *)slap_sl_malloc( sizeof(struct berval), ctx );
+ if ( new == NULL ) {
+ return NULL;
+ }
+ }
+
+ new->bv_len = 36;
+
+ if ( ( new->bv_val = slap_sl_malloc( new->bv_len + 1, ctx ) ) == NULL ) {
+ if ( new != uuidstr ) {
+ slap_sl_free( new, ctx );
+ }
+ return NULL;
+ }
+
+ for ( i = 0; i < 16; i++ ) {
+ if ( i == 4 || i == 6 || i == 8 || i == 10 ) {
+ new->bv_val[(i<<1)+d] = '-';
+ d += 1;
+ }
+
+ nibble = (normalized->bv_val[i] >> 4) & 0xF;
+ if ( nibble < 10 ) {
+ new->bv_val[(i<<1)+d] = nibble + '0';
+ } else {
+ new->bv_val[(i<<1)+d] = nibble - 10 + 'a';
+ }
+
+ nibble = (normalized->bv_val[i]) & 0xF;
+ if ( nibble < 10 ) {
+ new->bv_val[(i<<1)+d+1] = nibble + '0';
+ } else {
+ new->bv_val[(i<<1)+d+1] = nibble - 10 + 'a';
+ }
+ }
+
+ new->bv_val[new->bv_len] = '\0';
+ return new;
+#endif
+
+ struct berval *new;
+ int rc = 0;
+
+ if ( normalized == NULL ) return NULL;
+ if ( normalized->bv_len != 16 ) return NULL;
+
+ if ( uuidstr ) {
+ new = uuidstr;
+
+ } else {
+ new = (struct berval *)slap_sl_malloc( sizeof(struct berval), ctx );
+ if ( new == NULL ) {
+ return NULL;
+ }
+ }
+
+ new->bv_len = 36;
+
+ if ( ( new->bv_val = slap_sl_malloc( new->bv_len + 1, ctx ) ) == NULL ) {
+ rc = 1;
+ goto done;
+ }
+
+ rc = lutil_uuidstr_from_normalized( normalized->bv_val,
+ normalized->bv_len, new->bv_val, new->bv_len + 1 );
+
+done:;
+ if ( rc == -1 ) {
+ if ( new != NULL ) {
+ if ( new->bv_val != NULL ) {
+ slap_sl_free( new->bv_val, ctx );
+ }
+
+ if ( new != uuidstr ) {
+ slap_sl_free( new, ctx );
+ }
+ }
+ new = NULL;
+
+ } else {
+ new->bv_len = rc;
+ }
+
+ return new;
+}
+
+static int
+syncuuid_cmp( const void* v_uuid1, const void* v_uuid2 )
+{
+#ifdef HASHUUID
+ return ( memcmp( v_uuid1, v_uuid2, UUIDLEN-2 ));
+#else
+ return ( memcmp( v_uuid1, v_uuid2, UUIDLEN ));
+#endif
+}
+
+void
+syncinfo_free( syncinfo_t *sie, int free_all )
+{
+ syncinfo_t *si_next;
+
+ Debug( LDAP_DEBUG_TRACE, "syncinfo_free: %s\n",
+ sie->si_ridtxt );
+
+ do {
+ si_next = sie->si_next;
+
+ if ( !BER_BVISEMPTY( &sie->si_monitor_ndn )) {
+ syncrepl_monitor_del( sie );
+ }
+
+ if ( sie->si_ld ) {
+ if ( sie->si_conn ) {
+ connection_client_stop( sie->si_conn );
+ sie->si_conn = NULL;
+ }
+ ldap_unbind_ext( sie->si_ld, NULL, NULL );
+ }
+
+ if ( sie->si_re ) {
+ struct re_s *re = sie->si_re;
+ sie->si_re = NULL;
+
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, re ) )
+ ldap_pvt_runqueue_stoptask( &slapd_rq, re );
+ ldap_pvt_runqueue_remove( &slapd_rq, re );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ }
+
+ ldap_pvt_thread_mutex_destroy( &sie->si_mutex );
+ ldap_pvt_thread_mutex_destroy( &sie->si_monitor_mutex );
+
+ bindconf_free( &sie->si_bindconf );
+
+ if ( sie->si_filterstr.bv_val ) {
+ ch_free( sie->si_filterstr.bv_val );
+ }
+ if ( sie->si_filter ) {
+ filter_free( sie->si_filter );
+ }
+ if ( sie->si_logfilterstr.bv_val ) {
+ ch_free( sie->si_logfilterstr.bv_val );
+ }
+ if ( sie->si_logfilter ) {
+ filter_free( sie->si_logfilter );
+ }
+ if ( sie->si_base.bv_val ) {
+ ch_free( sie->si_base.bv_val );
+ }
+ if ( sie->si_logbase.bv_val ) {
+ ch_free( sie->si_logbase.bv_val );
+ }
+ if ( sie->si_be && SLAP_SYNC_SUBENTRY( sie->si_be )) {
+ ch_free( sie->si_contextdn.bv_val );
+ }
+ if ( sie->si_attrs ) {
+ int i = 0;
+ while ( sie->si_attrs[i] != NULL ) {
+ ch_free( sie->si_attrs[i] );
+ i++;
+ }
+ ch_free( sie->si_attrs );
+ }
+ if ( sie->si_exattrs ) {
+ int i = 0;
+ while ( sie->si_exattrs[i] != NULL ) {
+ ch_free( sie->si_exattrs[i] );
+ i++;
+ }
+ ch_free( sie->si_exattrs );
+ }
+ if ( sie->si_anlist ) {
+ int i = 0;
+ while ( sie->si_anlist[i].an_name.bv_val != NULL ) {
+ ch_free( sie->si_anlist[i].an_name.bv_val );
+ i++;
+ }
+ ch_free( sie->si_anlist );
+ }
+ if ( sie->si_exanlist ) {
+ int i = 0;
+ while ( sie->si_exanlist[i].an_name.bv_val != NULL ) {
+ ch_free( sie->si_exanlist[i].an_name.bv_val );
+ i++;
+ }
+ ch_free( sie->si_exanlist );
+ }
+ if ( sie->si_retryinterval ) {
+ ch_free( sie->si_retryinterval );
+ }
+ if ( sie->si_retrynum ) {
+ ch_free( sie->si_retrynum );
+ }
+ if ( sie->si_retrynum_init ) {
+ ch_free( sie->si_retrynum_init );
+ }
+ slap_sync_cookie_free( &sie->si_syncCookie, 0 );
+#ifdef LDAP_CONTROL_X_DIRSYNC
+ if ( sie->si_dirSyncCookie.bv_val ) {
+ ch_free( sie->si_dirSyncCookie.bv_val );
+ }
+#endif
+ if ( sie->si_presentlist ) {
+ presentlist_free( sie->si_presentlist );
+ }
+ while ( !LDAP_LIST_EMPTY( &sie->si_nonpresentlist ) ) {
+ struct nonpresent_entry* npe;
+ npe = LDAP_LIST_FIRST( &sie->si_nonpresentlist );
+ LDAP_LIST_REMOVE( npe, npe_link );
+ if ( npe->npe_name ) {
+ if ( npe->npe_name->bv_val ) {
+ ch_free( npe->npe_name->bv_val );
+ }
+ ch_free( npe->npe_name );
+ }
+ if ( npe->npe_nname ) {
+ if ( npe->npe_nname->bv_val ) {
+ ch_free( npe->npe_nname->bv_val );
+ }
+ ch_free( npe->npe_nname );
+ }
+ ch_free( npe );
+ }
+ if ( sie->si_cookieState ) {
+ /* Could be called from do_syncrepl (server unpaused) */
+ refresh_finished( sie );
+
+ sie->si_cookieState->cs_ref--;
+ if ( !sie->si_cookieState->cs_ref ) {
+ ch_free( sie->si_cookieState->cs_sids );
+ ber_bvarray_free( sie->si_cookieState->cs_vals );
+ ldap_pvt_thread_cond_destroy( &sie->si_cookieState->cs_cond );
+ ldap_pvt_thread_mutex_destroy( &sie->si_cookieState->cs_mutex );
+ ch_free( sie->si_cookieState->cs_psids );
+ ber_bvarray_free( sie->si_cookieState->cs_pvals );
+ ldap_pvt_thread_mutex_destroy( &sie->si_cookieState->cs_pmutex );
+ ldap_pvt_thread_mutex_destroy( &sie->si_cookieState->cs_refresh_mutex );
+ assert( sie->si_cookieState->cs_refreshing == NULL );
+ ch_free( sie->si_cookieState );
+ }
+ }
+ if ( sie->si_rewrite )
+ rewrite_info_delete( &sie->si_rewrite );
+ if ( sie->si_suffixm.bv_val )
+ ch_free( sie->si_suffixm.bv_val );
+ ch_free( sie );
+ sie = si_next;
+ } while ( free_all && si_next );
+}
+
+static int
+config_suffixm( ConfigArgs *c, syncinfo_t *si )
+{
+ char *argvEngine[] = { "rewriteEngine", "on", NULL };
+ char *argvContext[] = { "rewriteContext", SUFFIXM_CTX, NULL };
+ char *argvRule[] = { "rewriteRule", NULL, NULL, ":", NULL };
+ char *vnc, *rnc;
+ int rc;
+
+ if ( si->si_rewrite )
+ rewrite_info_delete( &si->si_rewrite );
+ si->si_rewrite = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
+
+ rc = rewrite_parse( si->si_rewrite, c->fname, c->lineno, 2, argvEngine );
+ if ( rc != LDAP_SUCCESS )
+ return rc;
+
+ rc = rewrite_parse( si->si_rewrite, c->fname, c->lineno, 2, argvContext );
+ if ( rc != LDAP_SUCCESS )
+ return rc;
+
+ vnc = ch_malloc( si->si_base.bv_len + 6 );
+ strcpy( vnc, "(.*)" );
+ lutil_strcopy( lutil_strcopy( vnc+4, si->si_base.bv_val ), "$" );
+ argvRule[1] = vnc;
+
+ rnc = ch_malloc( si->si_suffixm.bv_len + 3 );
+ strcpy( rnc, "%1" );
+ strcpy( rnc+2, si->si_suffixm.bv_val );
+ argvRule[2] = rnc;
+
+ rc = rewrite_parse( si->si_rewrite, c->fname, c->lineno, 4, argvRule );
+ ch_free( vnc );
+ ch_free( rnc );
+ return rc;
+}
+
+/* NOTE: used & documented in slapd.conf(5) */
+#define IDSTR "rid"
+#define PROVIDERSTR "provider"
+#define SCHEMASTR "schemachecking"
+#define FILTERSTR "filter"
+#define SEARCHBASESTR "searchbase"
+#define SCOPESTR "scope"
+#define ATTRSONLYSTR "attrsonly"
+#define ATTRSSTR "attrs"
+#define TYPESTR "type"
+#define INTERVALSTR "interval"
+#define RETRYSTR "retry"
+#define SLIMITSTR "sizelimit"
+#define TLIMITSTR "timelimit"
+#define SYNCDATASTR "syncdata"
+#define LOGBASESTR "logbase"
+#define LOGFILTERSTR "logfilter"
+#define SUFFIXMSTR "suffixmassage"
+#define STRICT_REFRESH "strictrefresh"
+#define LAZY_COMMIT "lazycommit"
+
+/* FIXME: undocumented */
+#define EXATTRSSTR "exattrs"
+#define MANAGEDSAITSTR "manageDSAit"
+
+/* mandatory */
+enum {
+ GOT_RID = 0x00000001U,
+ GOT_PROVIDER = 0x00000002U,
+ GOT_SCHEMACHECKING = 0x00000004U,
+ GOT_FILTER = 0x00000008U,
+ GOT_SEARCHBASE = 0x00000010U,
+ GOT_SCOPE = 0x00000020U,
+ GOT_ATTRSONLY = 0x00000040U,
+ GOT_ATTRS = 0x00000080U,
+ GOT_TYPE = 0x00000100U,
+ GOT_INTERVAL = 0x00000200U,
+ GOT_RETRY = 0x00000400U,
+ GOT_SLIMIT = 0x00000800U,
+ GOT_TLIMIT = 0x00001000U,
+ GOT_SYNCDATA = 0x00002000U,
+ GOT_LOGBASE = 0x00004000U,
+ GOT_LOGFILTER = 0x00008000U,
+ GOT_EXATTRS = 0x00010000U,
+ GOT_MANAGEDSAIT = 0x00020000U,
+ GOT_BINDCONF = 0x00040000U,
+ GOT_SUFFIXM = 0x00080000U,
+
+/* check */
+ GOT_REQUIRED = (GOT_RID|GOT_PROVIDER|GOT_SEARCHBASE)
+};
+
+static slap_verbmasks datamodes[] = {
+ { BER_BVC("default"), SYNCDATA_DEFAULT },
+ { BER_BVC("accesslog"), SYNCDATA_ACCESSLOG },
+ { BER_BVC("changelog"), SYNCDATA_CHANGELOG },
+ { BER_BVNULL, 0 }
+};
+
+static int
+parse_syncrepl_retry(
+ ConfigArgs *c,
+ char *arg,
+ syncinfo_t *si )
+{
+ char **retry_list;
+ int j, k, n;
+ int use_default = 0;
+
+ char *val = arg + STRLENOF( RETRYSTR "=" );
+ if ( strcasecmp( val, "undefined" ) == 0 ) {
+ val = "3600 +";
+ use_default = 1;
+ }
+
+ retry_list = (char **) ch_calloc( 1, sizeof( char * ) );
+ retry_list[0] = NULL;
+
+ slap_str2clist( &retry_list, val, " ,\t" );
+
+ for ( k = 0; retry_list && retry_list[k]; k++ ) ;
+ n = k / 2;
+ if ( k % 2 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Error: incomplete syncrepl retry list" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ for ( k = 0; retry_list && retry_list[k]; k++ ) {
+ ch_free( retry_list[k] );
+ }
+ ch_free( retry_list );
+ return 1;
+ }
+ si->si_retryinterval = (time_t *) ch_calloc( n + 1, sizeof( time_t ) );
+ si->si_retrynum = (int *) ch_calloc( n + 1, sizeof( int ) );
+ si->si_retrynum_init = (int *) ch_calloc( n + 1, sizeof( int ) );
+ for ( j = 0; j < n; j++ ) {
+ unsigned long t;
+ if ( lutil_atoul( &t, retry_list[j*2] ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Error: invalid retry interval \"%s\" (#%d)",
+ retry_list[j*2], j );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ /* do some cleanup */
+ return 1;
+ }
+ si->si_retryinterval[j] = (time_t)t;
+ if ( *retry_list[j*2+1] == '+' ) {
+ si->si_retrynum_init[j] = RETRYNUM_FOREVER;
+ si->si_retrynum[j] = RETRYNUM_FOREVER;
+ j++;
+ break;
+ } else {
+ if ( lutil_atoi( &si->si_retrynum_init[j], retry_list[j*2+1] ) != 0
+ || si->si_retrynum_init[j] <= 0 )
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Error: invalid initial retry number \"%s\" (#%d)",
+ retry_list[j*2+1], j );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ /* do some cleanup */
+ return 1;
+ }
+ if ( lutil_atoi( &si->si_retrynum[j], retry_list[j*2+1] ) != 0
+ || si->si_retrynum[j] <= 0 )
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Error: invalid retry number \"%s\" (#%d)",
+ retry_list[j*2+1], j );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ /* do some cleanup */
+ return 1;
+ }
+ }
+ }
+ if ( j < 1 || si->si_retrynum_init[j-1] != RETRYNUM_FOREVER ) {
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: syncrepl will eventually stop retrying; the \"retry\" parameter should end with a '+'.\n",
+ c->log );
+ }
+
+ si->si_retrynum_init[j] = RETRYNUM_TAIL;
+ si->si_retrynum[j] = RETRYNUM_TAIL;
+ si->si_retryinterval[j] = 0;
+
+ for ( k = 0; retry_list && retry_list[k]; k++ ) {
+ ch_free( retry_list[k] );
+ }
+ ch_free( retry_list );
+ if ( !use_default ) {
+ si->si_got |= GOT_RETRY;
+ }
+
+ return 0;
+}
+
+static int
+parse_syncrepl_line(
+ ConfigArgs *c,
+ syncinfo_t *si )
+{
+ int i;
+ char *val;
+
+ for ( i = 1; i < c->argc; i++ ) {
+ if ( !strncasecmp( c->argv[ i ], IDSTR "=",
+ STRLENOF( IDSTR "=" ) ) )
+ {
+ int tmp;
+ /* '\0' string terminator accounts for '=' */
+ val = c->argv[ i ] + STRLENOF( IDSTR "=" );
+ if ( lutil_atoi( &tmp, val ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Error: parse_syncrepl_line: "
+ "unable to parse syncrepl id \"%s\"", val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return -1;
+ }
+ if ( tmp > SLAP_SYNC_RID_MAX || tmp < 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Error: parse_syncrepl_line: "
+ "syncrepl id %d is out of range [0..%d]", tmp, SLAP_SYNC_RID_MAX );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return -1;
+ }
+ si->si_rid = tmp;
+ sprintf( si->si_ridtxt, IDSTR "=%03d", si->si_rid );
+ si->si_got |= GOT_RID;
+ } else if ( !strncasecmp( c->argv[ i ], PROVIDERSTR "=",
+ STRLENOF( PROVIDERSTR "=" ) ) )
+ {
+ val = c->argv[ i ] + STRLENOF( PROVIDERSTR "=" );
+ ber_str2bv( val, 0, 1, &si->si_bindconf.sb_uri );
+#ifdef HAVE_TLS
+ if ( ldap_is_ldaps_url( val ))
+ si->si_bindconf.sb_tls_do_init = 1;
+#endif
+ si->si_got |= GOT_PROVIDER;
+ } else if ( !strncasecmp( c->argv[ i ], SCHEMASTR "=",
+ STRLENOF( SCHEMASTR "=" ) ) )
+ {
+ val = c->argv[ i ] + STRLENOF( SCHEMASTR "=" );
+ if ( !strncasecmp( val, "on", STRLENOF( "on" ) ) ) {
+ si->si_schemachecking = 1;
+ } else if ( !strncasecmp( val, "off", STRLENOF( "off" ) ) ) {
+ si->si_schemachecking = 0;
+ } else {
+ si->si_schemachecking = 1;
+ }
+ si->si_got |= GOT_SCHEMACHECKING;
+ } else if ( !strncasecmp( c->argv[ i ], FILTERSTR "=",
+ STRLENOF( FILTERSTR "=" ) ) )
+ {
+ val = c->argv[ i ] + STRLENOF( FILTERSTR "=" );
+ if ( si->si_filterstr.bv_val )
+ ch_free( si->si_filterstr.bv_val );
+ ber_str2bv( val, 0, 1, &si->si_filterstr );
+ si->si_got |= GOT_FILTER;
+ } else if ( !strncasecmp( c->argv[ i ], LOGFILTERSTR "=",
+ STRLENOF( LOGFILTERSTR "=" ) ) )
+ {
+ val = c->argv[ i ] + STRLENOF( LOGFILTERSTR "=" );
+ if ( si->si_logfilterstr.bv_val )
+ ch_free( si->si_logfilterstr.bv_val );
+ ber_str2bv( val, 0, 1, &si->si_logfilterstr );
+ si->si_got |= GOT_LOGFILTER;
+ } else if ( !strncasecmp( c->argv[ i ], SEARCHBASESTR "=",
+ STRLENOF( SEARCHBASESTR "=" ) ) )
+ {
+ struct berval bv;
+ int rc;
+
+ val = c->argv[ i ] + STRLENOF( SEARCHBASESTR "=" );
+ if ( si->si_base.bv_val ) {
+ ch_free( si->si_base.bv_val );
+ }
+ ber_str2bv( val, 0, 0, &bv );
+ rc = dnNormalize( 0, NULL, NULL, &bv, &si->si_base, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Invalid base DN \"%s\": %d (%s)",
+ val, rc, ldap_err2string( rc ) );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return -1;
+ }
+ si->si_got |= GOT_SEARCHBASE;
+ } else if ( !strncasecmp( c->argv[ i ], SUFFIXMSTR "=",
+ STRLENOF( SUFFIXMSTR "=" ) ) )
+ {
+ struct berval bv;
+ int rc;
+
+ val = c->argv[ i ] + STRLENOF( SUFFIXMSTR "=" );
+ if ( si->si_suffixm.bv_val ) {
+ ch_free( si->si_suffixm.bv_val );
+ }
+ ber_str2bv( val, 0, 0, &bv );
+ rc = dnNormalize( 0, NULL, NULL, &bv, &si->si_suffixm, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Invalid massage DN \"%s\": %d (%s)",
+ val, rc, ldap_err2string( rc ) );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return -1;
+ }
+ if ( !be_issubordinate( c->be, &si->si_suffixm )) {
+ ch_free( si->si_suffixm.bv_val );
+ BER_BVZERO( &si->si_suffixm );
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Massage DN \"%s\" is not within the database naming context",
+ val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return -1;
+ }
+ si->si_got |= GOT_SUFFIXM;
+ } else if ( !strncasecmp( c->argv[ i ], LOGBASESTR "=",
+ STRLENOF( LOGBASESTR "=" ) ) )
+ {
+ struct berval bv;
+ int rc;
+
+ val = c->argv[ i ] + STRLENOF( LOGBASESTR "=" );
+ if ( si->si_logbase.bv_val ) {
+ ch_free( si->si_logbase.bv_val );
+ }
+ ber_str2bv( val, 0, 0, &bv );
+ rc = dnNormalize( 0, NULL, NULL, &bv, &si->si_logbase, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Invalid logbase DN \"%s\": %d (%s)",
+ val, rc, ldap_err2string( rc ) );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return -1;
+ }
+ si->si_got |= GOT_LOGBASE;
+ } else if ( !strncasecmp( c->argv[ i ], SCOPESTR "=",
+ STRLENOF( SCOPESTR "=" ) ) )
+ {
+ int j;
+ val = c->argv[ i ] + STRLENOF( SCOPESTR "=" );
+ j = ldap_pvt_str2scope( val );
+ if ( j < 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Error: parse_syncrepl_line: "
+ "unknown scope \"%s\"", val);
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return -1;
+ }
+ si->si_scope = j;
+ si->si_got |= GOT_SCOPE;
+ } else if ( !strncasecmp( c->argv[ i ], ATTRSONLYSTR,
+ STRLENOF( ATTRSONLYSTR ) ) )
+ {
+ si->si_attrsonly = 1;
+ si->si_got |= GOT_ATTRSONLY;
+ } else if ( !strncasecmp( c->argv[ i ], ATTRSSTR "=",
+ STRLENOF( ATTRSSTR "=" ) ) )
+ {
+ val = c->argv[ i ] + STRLENOF( ATTRSSTR "=" );
+ if ( !strncasecmp( val, ":include:", STRLENOF(":include:") ) ) {
+ char *attr_fname;
+ attr_fname = ch_strdup( val + STRLENOF(":include:") );
+ si->si_anlist = file2anlist( si->si_anlist, attr_fname, " ,\t" );
+ if ( si->si_anlist == NULL ) {
+ ch_free( attr_fname );
+ return -1;
+ }
+ si->si_anfile = attr_fname;
+ } else {
+ char *str, *s, *next;
+ const char *delimstr = " ,\t";
+ str = ch_strdup( val );
+ for ( s = ldap_pvt_strtok( str, delimstr, &next );
+ s != NULL;
+ s = ldap_pvt_strtok( NULL, delimstr, &next ) )
+ {
+ if ( strlen(s) == 1 && *s == '*' ) {
+ si->si_allattrs = 1;
+ val[ s - str ] = delimstr[0];
+ }
+ if ( strlen(s) == 1 && *s == '+' ) {
+ si->si_allopattrs = 1;
+ val [ s - str ] = delimstr[0];
+ }
+ }
+ ch_free( str );
+ si->si_anlist = str2anlist( si->si_anlist, val, " ,\t" );
+ if ( si->si_anlist == NULL ) {
+ return -1;
+ }
+ }
+ si->si_got |= GOT_ATTRS;
+ } else if ( !strncasecmp( c->argv[ i ], EXATTRSSTR "=",
+ STRLENOF( EXATTRSSTR "=" ) ) )
+ {
+ val = c->argv[ i ] + STRLENOF( EXATTRSSTR "=" );
+ if ( !strncasecmp( val, ":include:", STRLENOF(":include:") ) ) {
+ char *attr_fname;
+ attr_fname = ch_strdup( val + STRLENOF(":include:") );
+ si->si_exanlist = file2anlist(
+ si->si_exanlist, attr_fname, " ,\t" );
+ if ( si->si_exanlist == NULL ) {
+ ch_free( attr_fname );
+ return -1;
+ }
+ ch_free( attr_fname );
+ } else {
+ si->si_exanlist = str2anlist( si->si_exanlist, val, " ,\t" );
+ if ( si->si_exanlist == NULL ) {
+ return -1;
+ }
+ }
+ si->si_got |= GOT_EXATTRS;
+ } else if ( !strncasecmp( c->argv[ i ], TYPESTR "=",
+ STRLENOF( TYPESTR "=" ) ) )
+ {
+ val = c->argv[ i ] + STRLENOF( TYPESTR "=" );
+ if ( !strncasecmp( val, "refreshOnly",
+ STRLENOF("refreshOnly") ) )
+ {
+ si->si_type = si->si_ctype = LDAP_SYNC_REFRESH_ONLY;
+ } else if ( !strncasecmp( val, "refreshAndPersist",
+ STRLENOF("refreshAndPersist") ) )
+ {
+ si->si_type = si->si_ctype = LDAP_SYNC_REFRESH_AND_PERSIST;
+ si->si_interval = 60;
+#ifdef LDAP_CONTROL_X_DIRSYNC
+ } else if ( !strncasecmp( val, "dirSync",
+ STRLENOF("dirSync") ) )
+ {
+ if ( sy_ad_objectGUID == NULL && syncrepl_dirsync_schema()) {
+ sprintf( c->cr_msg, "Error: dirSync schema is missing" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return -1;
+ }
+ /* MS DirSync is refreshOnly, no persist */
+ si->si_type = si->si_ctype = MSAD_DIRSYNC;
+#endif
+ } else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Error: parse_syncrepl_line: "
+ "unknown sync type \"%s\"", val);
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return -1;
+ }
+ si->si_got |= GOT_TYPE;
+ } else if ( !strncasecmp( c->argv[ i ], INTERVALSTR "=",
+ STRLENOF( INTERVALSTR "=" ) ) )
+ {
+ val = c->argv[ i ] + STRLENOF( INTERVALSTR "=" );
+ if ( si->si_type == LDAP_SYNC_REFRESH_AND_PERSIST ) {
+ si->si_interval = 0;
+ } else if ( strchr( val, ':' ) != NULL ) {
+ char *next, *ptr = val;
+ int dd, hh, mm, ss;
+
+ dd = strtol( ptr, &next, 10 );
+ if ( next == ptr || next[0] != ':' || dd < 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Error: parse_syncrepl_line: "
+ "invalid interval \"%s\", unable to parse days", val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return -1;
+ }
+ ptr = next + 1;
+ hh = strtol( ptr, &next, 10 );
+ if ( next == ptr || next[0] != ':' || hh < 0 || hh > 24 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Error: parse_syncrepl_line: "
+ "invalid interval \"%s\", unable to parse hours", val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return -1;
+ }
+ ptr = next + 1;
+ mm = strtol( ptr, &next, 10 );
+ if ( next == ptr || next[0] != ':' || mm < 0 || mm > 60 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Error: parse_syncrepl_line: "
+ "invalid interval \"%s\", unable to parse minutes", val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return -1;
+ }
+ ptr = next + 1;
+ ss = strtol( ptr, &next, 10 );
+ if ( next == ptr || next[0] != '\0' || ss < 0 || ss > 60 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Error: parse_syncrepl_line: "
+ "invalid interval \"%s\", unable to parse seconds", val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return -1;
+ }
+ si->si_interval = (( dd * 24 + hh ) * 60 + mm ) * 60 + ss;
+ } else {
+ unsigned long t;
+
+ if ( lutil_parse_time( val, &t ) != 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Error: parse_syncrepl_line: "
+ "invalid interval \"%s\"", val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return -1;
+ }
+ si->si_interval = (time_t)t;
+ }
+ if ( si->si_interval < 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Error: parse_syncrepl_line: "
+ "invalid interval \"%ld\"",
+ (long) si->si_interval);
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return -1;
+ }
+ si->si_got |= GOT_INTERVAL;
+ } else if ( !strncasecmp( c->argv[ i ], RETRYSTR "=",
+ STRLENOF( RETRYSTR "=" ) ) )
+ {
+ if ( parse_syncrepl_retry( c, c->argv[ i ], si ) ) {
+ return 1;
+ }
+ } else if ( !strncasecmp( c->argv[ i ], MANAGEDSAITSTR "=",
+ STRLENOF( MANAGEDSAITSTR "=" ) ) )
+ {
+ val = c->argv[ i ] + STRLENOF( MANAGEDSAITSTR "=" );
+ if ( lutil_atoi( &si->si_manageDSAit, val ) != 0
+ || si->si_manageDSAit < 0 || si->si_manageDSAit > 1 )
+ {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "invalid manageDSAit value \"%s\".\n",
+ val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ si->si_got |= GOT_MANAGEDSAIT;
+ } else if ( !strncasecmp( c->argv[ i ], SLIMITSTR "=",
+ STRLENOF( SLIMITSTR "=") ) )
+ {
+ val = c->argv[ i ] + STRLENOF( SLIMITSTR "=" );
+ if ( strcasecmp( val, "unlimited" ) == 0 ) {
+ si->si_slimit = 0;
+
+ } else if ( lutil_atoi( &si->si_slimit, val ) != 0 || si->si_slimit < 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "invalid size limit value \"%s\".\n",
+ val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ si->si_got |= GOT_SLIMIT;
+ } else if ( !strncasecmp( c->argv[ i ], TLIMITSTR "=",
+ STRLENOF( TLIMITSTR "=" ) ) )
+ {
+ val = c->argv[ i ] + STRLENOF( TLIMITSTR "=" );
+ if ( strcasecmp( val, "unlimited" ) == 0 ) {
+ si->si_tlimit = 0;
+
+ } else if ( lutil_atoi( &si->si_tlimit, val ) != 0 || si->si_tlimit < 0 ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "invalid time limit value \"%s\".\n",
+ val );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ si->si_got |= GOT_TLIMIT;
+ } else if ( !strncasecmp( c->argv[ i ], SYNCDATASTR "=",
+ STRLENOF( SYNCDATASTR "=" ) ) )
+ {
+ val = c->argv[ i ] + STRLENOF( SYNCDATASTR "=" );
+ si->si_syncdata = verb_to_mask( val, datamodes );
+ si->si_got |= GOT_SYNCDATA;
+ if ( si->si_syncdata == SYNCDATA_CHANGELOG ) {
+ if ( sy_ad_nsUniqueId == NULL ) {
+ int rc = syncrepl_dsee_schema();
+ if ( rc ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "changelog schema problem (%d)\n", rc );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return 1;
+ }
+ }
+ }
+ } else if ( !strncasecmp( c->argv[ i ], STRICT_REFRESH,
+ STRLENOF( STRICT_REFRESH ) ) )
+ {
+ si->si_strict_refresh = 1;
+ } else if ( !strncasecmp( c->argv[ i ], LAZY_COMMIT,
+ STRLENOF( LAZY_COMMIT ) ) )
+ {
+ si->si_lazyCommit = 1;
+ } else if ( !bindconf_parse( c->argv[i], &si->si_bindconf ) ) {
+ si->si_got |= GOT_BINDCONF;
+ } else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Error: parse_syncrepl_line: "
+ "unable to parse \"%s\"\n", c->argv[ i ] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return -1;
+ }
+ }
+
+ if ( ( si->si_got & GOT_REQUIRED ) != GOT_REQUIRED ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Error: Malformed \"syncrepl\" line in slapd config file, missing%s%s%s",
+ si->si_got & GOT_RID ? "" : " "IDSTR,
+ si->si_got & GOT_PROVIDER ? "" : " "PROVIDERSTR,
+ si->si_got & GOT_SEARCHBASE ? "" : " "SEARCHBASESTR );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return -1;
+ }
+
+ if ( !be_issubordinate( c->be, &si->si_base ) && !( si->si_got & GOT_SUFFIXM )) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Base DN \"%s\" is not within the database naming context",
+ si->si_base.bv_val );
+ ch_free( si->si_base.bv_val );
+ BER_BVZERO( &si->si_base );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return -1;
+ }
+
+ if ( si->si_got & GOT_SUFFIXM ) {
+ if (config_suffixm( c, si )) {
+ ch_free( si->si_suffixm.bv_val );
+ BER_BVZERO( &si->si_suffixm );
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "Error configuring rewrite engine" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg );
+ return -1;
+ }
+ }
+
+ if ( !( si->si_got & GOT_RETRY ) ) {
+ Debug( LDAP_DEBUG_ANY, "syncrepl %s " SEARCHBASESTR "=\"%s\": no retry defined, using default\n",
+ si->si_ridtxt, c->be->be_suffix ? c->be->be_suffix[ 0 ].bv_val : "(null)" );
+ if ( si->si_retryinterval == NULL ) {
+ if ( parse_syncrepl_retry( c, "retry=undefined", si ) ) {
+ return 1;
+ }
+ }
+ }
+
+ si->si_filter = str2filter( si->si_filterstr.bv_val );
+ if ( si->si_filter == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "syncrepl %s " SEARCHBASESTR "=\"%s\": unable to parse filter=\"%s\"\n",
+ si->si_ridtxt, c->be->be_suffix ? c->be->be_suffix[ 0 ].bv_val : "(null)", si->si_filterstr.bv_val );
+ return 1;
+ }
+
+ if ( si->si_got & GOT_LOGFILTER ) {
+ si->si_logfilter = str2filter( si->si_logfilterstr.bv_val );
+ if ( si->si_logfilter == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "syncrepl %s " SEARCHBASESTR "=\"%s\": unable to parse logfilter=\"%s\"\n",
+ si->si_ridtxt, c->be->be_suffix ? c->be->be_suffix[ 0 ].bv_val : "(null)", si->si_logfilterstr.bv_val );
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* monitor entry contains:
+ provider URLs
+ timestamp of last contact
+ cookievals
+ */
+
+static ObjectClass *oc_olmSyncRepl;
+static AttributeDescription *ad_olmProviderURIList,
+ *ad_olmConnection, *ad_olmSyncPhase,
+ *ad_olmNextConnect, *ad_olmLastConnect, *ad_olmLastContact,
+ *ad_olmLastCookieRcvd, *ad_olmLastCookieSent;
+
+static struct {
+ char *name;
+ char *oid;
+} s_oid[] = {
+ { "olmSyncReplAttributes", "olmOverlayAttributes:1" },
+ { "olmSyncReplObjectClasses", "olmOverlayObjectClasses:1" },
+ { NULL }
+};
+
+static struct {
+ char *desc;
+ AttributeDescription **ad;
+} s_at[] = {
+ { "( olmSyncReplAttributes:1 "
+ "NAME ( 'olmSRProviderURIList' ) "
+ "DESC 'List of provider URIs for this consumer instance' "
+ "SUP monitoredInfo "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmProviderURIList },
+ { "( olmSyncReplAttributes:2 "
+ "NAME ( 'olmSRConnection' ) "
+ "DESC 'Local address:port of connection to provider' "
+ "SUP monitoredInfo "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmConnection },
+ { "( olmSyncReplAttributes:3 "
+ "NAME ( 'olmSRSyncPhase' ) "
+ "DESC 'Current syncrepl mode' "
+ "SUP monitoredInfo "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmSyncPhase },
+ { "( olmSyncReplAttributes:4 "
+ "NAME ( 'olmSRNextConnect' ) "
+ "DESC 'Scheduled time of next connection attempt' "
+ "SUP monitorTimestamp "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmNextConnect },
+ { "( olmSyncReplAttributes:5 "
+ "NAME ( 'olmSRLastConnect' ) "
+ "DESC 'Time last connected to provider' "
+ "SUP monitorTimestamp "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmLastConnect },
+ { "( olmSyncReplAttributes:6 "
+ "NAME ( 'olmSRLastContact' ) "
+ "DESC 'Time last message received from provider' "
+ "SUP monitorTimestamp "
+ "SINGLE-VALUE "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmLastContact },
+ { "( olmSyncReplAttributes:7 "
+ "NAME ( 'olmSRLastCookieRcvd' ) "
+ "DESC 'Last sync cookie received from provider' "
+ "SUP monitoredInfo "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmLastCookieRcvd },
+ { "( olmSyncReplAttributes:8 "
+ "NAME ( 'olmSRLastCookieSent' ) "
+ "DESC 'Last sync cookie sent to provider' "
+ "SUP monitoredInfo "
+ "NO-USER-MODIFICATION "
+ "USAGE dSAOperation )",
+ &ad_olmLastCookieSent },
+ { NULL }
+};
+
+static struct {
+ char *desc;
+ ObjectClass **oc;
+} s_oc[] = {
+ { "( olmSyncReplObjectClasses:1 "
+ "NAME ( 'olmSyncReplInstance' ) "
+ "SUP monitoredObject STRUCTURAL "
+ "MAY ( "
+ "olmSRProviderURIList "
+ "$ olmSRConnection "
+ "$ olmSRSyncPhase "
+ "$ olmSRNextConnect "
+ "$ olmSRLastConnect "
+ "$ olmSRLastContact "
+ "$ olmSRLastCookieRcvd "
+ "$ olmSRLastCookieSent "
+ ") )",
+ &oc_olmSyncRepl },
+ { NULL }
+};
+
+static int
+syncrepl_monitor_initialized;
+
+int
+syncrepl_monitor_init( void )
+{
+ int i, code;
+
+ if ( syncrepl_monitor_initialized )
+ return 0;
+
+ if ( backend_info( "monitor" ) == NULL )
+ return -1;
+
+ {
+ ConfigArgs c;
+ char *argv[3];
+
+ argv[ 0 ] = "syncrepl monitor";
+ c.argv = argv;
+ c.argc = 2;
+ c.fname = argv[0];
+ for ( i=0; s_oid[i].name; i++ ) {
+ argv[1] = s_oid[i].name;
+ argv[2] = s_oid[i].oid;
+ if ( parse_oidm( &c, 0, NULL )) {
+ Debug( LDAP_DEBUG_ANY,
+ "syncrepl_monitor_init: unable to add "
+ "objectIdentifier \"%s=%s\"\n",
+ s_oid[i].name, s_oid[i].oid );
+ return 2;
+ }
+ }
+ }
+
+ for ( i=0; s_at[i].desc != NULL; i++ ) {
+ code = register_at( s_at[i].desc, s_at[i].ad, 1 );
+ if ( code != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "syncrepl_monitor_init: register_at failed for attributeType (%s)\n",
+ s_at[i].desc );
+ return 3;
+ } else {
+ (*s_at[i].ad)->ad_type->sat_flags |= SLAP_AT_HIDE;
+ }
+ }
+
+ for ( i=0; s_oc[i].desc != NULL; i++ ) {
+ code = register_oc( s_oc[i].desc, s_oc[i].oc, 1 );
+ if ( code != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "syncrepl_monitor_init: register_oc failed for objectClass (%s)\n",
+ s_oc[i].desc );
+ return 4;
+ } else {
+ (*s_oc[i].oc)->soc_flags |= SLAP_OC_HIDE;
+ }
+ }
+ syncrepl_monitor_initialized = 1;
+
+ return 0;
+}
+
+static const struct berval zerotime = BER_BVC("00000101000000Z");
+
+static int
+syncrepl_monitor_update(
+ Operation *op,
+ SlapReply *rs,
+ Entry *e,
+ void *priv )
+{
+ syncinfo_t *si = (syncinfo_t *)priv;
+ Attribute *a;
+ int isConnected = 0;
+
+ a = attr_find( e->e_attrs, ad_olmConnection );
+ if ( !a )
+ return SLAP_CB_CONTINUE;
+ if ( si->si_ld ) {
+ if (!bvmatch( &a->a_vals[0], &si->si_connaddr )) {
+ AC_MEMCPY( a->a_vals[0].bv_val, si->si_connaddr.bv_val, si->si_connaddr.bv_len );
+ a->a_vals[0].bv_len = si->si_connaddr.bv_len;
+ }
+ isConnected = 1;
+ } else {
+ a->a_vals[0].bv_val[0] = '\0';
+ a->a_vals[0].bv_len = 0;
+ }
+
+ a = a->a_next;
+ if ( a->a_desc != ad_olmSyncPhase )
+ return SLAP_CB_CONTINUE;
+
+ if ( si->si_refreshDone ) {
+ struct berval bv = BER_BVC("Persist");
+ ber_bvreplace( &a->a_vals[0], &bv );
+ } else {
+ if ( si->si_syncdata && si->si_logstate == SYNCLOG_FALLBACK ) {
+ struct berval bv = BER_BVC("Fallback Refresh");
+ ber_bvreplace( &a->a_vals[0], &bv );
+ } else {
+ struct berval bv = BER_BVC("Refresh");
+ ber_bvreplace( &a->a_vals[0], &bv );
+ }
+ }
+
+ {
+ struct tm tm;
+ char tmbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+ ber_len_t len;
+
+ a = a->a_next;
+ if ( a->a_desc != ad_olmNextConnect )
+ return SLAP_CB_CONTINUE;
+
+ if ( !isConnected && si->si_re && si->si_re->next_sched.tv_sec ) {
+ time_t next_sched = si->si_re->next_sched.tv_sec;
+ ldap_pvt_gmtime( &next_sched, &tm );
+ lutil_gentime( tmbuf, sizeof( tmbuf ), &tm );
+ len = strlen( tmbuf );
+ assert( len == a->a_vals[0].bv_len );
+ AC_MEMCPY( a->a_vals[0].bv_val, tmbuf, len );
+ } else {
+ AC_MEMCPY( a->a_vals[0].bv_val, zerotime.bv_val, zerotime.bv_len );
+ }
+
+ a = a->a_next;
+ if ( a->a_desc != ad_olmLastConnect )
+ return SLAP_CB_CONTINUE;
+
+ if ( si->si_lastconnect ) {
+ ldap_pvt_gmtime( &si->si_lastconnect, &tm );
+ lutil_gentime( tmbuf, sizeof( tmbuf ), &tm );
+ len = strlen( tmbuf );
+ assert( len == a->a_vals[0].bv_len );
+ AC_MEMCPY( a->a_vals[0].bv_val, tmbuf, len );
+ }
+
+ a = a->a_next;
+ if ( a->a_desc != ad_olmLastContact )
+ return SLAP_CB_CONTINUE;
+
+ if ( si->si_lastcontact ) {
+ ldap_pvt_gmtime( &si->si_lastcontact, &tm );
+ lutil_gentime( tmbuf, sizeof( tmbuf ), &tm );
+ len = strlen( tmbuf );
+ assert( len == a->a_vals[0].bv_len );
+ AC_MEMCPY( a->a_vals[0].bv_val, tmbuf, len );
+ }
+ }
+
+ a = a->a_next;
+ if ( a->a_desc != ad_olmLastCookieRcvd )
+ return SLAP_CB_CONTINUE;
+
+ ldap_pvt_thread_mutex_lock( &si->si_monitor_mutex );
+ if ( !BER_BVISEMPTY( &si->si_lastCookieRcvd ) &&
+ !bvmatch( &a->a_vals[0], &si->si_lastCookieRcvd ))
+ ber_bvreplace( &a->a_vals[0], &si->si_lastCookieRcvd );
+
+ a = a->a_next;
+ if ( a->a_desc != ad_olmLastCookieSent ) {
+ ldap_pvt_thread_mutex_unlock( &si->si_monitor_mutex );
+ return SLAP_CB_CONTINUE;
+ }
+
+ if ( !BER_BVISEMPTY( &si->si_lastCookieSent ) &&
+ !bvmatch( &a->a_vals[0], &si->si_lastCookieSent ))
+ ber_bvreplace( &a->a_vals[0], &si->si_lastCookieSent );
+ ldap_pvt_thread_mutex_unlock( &si->si_monitor_mutex );
+
+ return SLAP_CB_CONTINUE;
+}
+
+static int
+syncrepl_monitor_add(
+ syncinfo_t *si
+)
+{
+ BackendInfo *mi;
+ monitor_extra_t *mbe;
+ struct berval pndn, pdn, rdn, bv;
+ char rdnbuf[sizeof("cn=Consumer 999")];
+ Entry *e, *p;
+ int rc;
+
+ if ( !syncrepl_monitor_initialized )
+ return -1;
+
+ mi = backend_info( "monitor" );
+ if ( !mi || !mi->bi_extra ) {
+ SLAP_DBFLAGS( si->si_be ) ^= SLAP_DBFLAG_MONITORING;
+ return 0;
+ }
+ mbe = mi->bi_extra;
+
+ if ( !mbe->is_configured() ) {
+ return 0;
+ }
+
+ rc = mbe->register_database( si->si_be, &pndn );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY, "syncrepl_monitor_add: "
+ "failed to register the database with back-monitor\n" );
+ return rc;
+ }
+ rdn.bv_len = sprintf(rdnbuf, "cn=Consumer %03d", si->si_rid );
+ rdn.bv_val = rdnbuf;
+ p = mbe->entry_get_unlocked( &pndn );
+ if ( p ) {
+ pdn = p->e_name;
+ } else {
+ pdn = pndn;
+ }
+
+ e = mbe->entry_stub( &pdn, &pndn, &rdn,
+ oc_olmSyncRepl, NULL, NULL );
+ if ( e == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "syncrepl_monitor_add: "
+ "unable to create entry \"%s,%s\"\n",
+ rdn.bv_val, pndn.bv_val );
+ return -1;
+ }
+
+ attr_merge_normalize_one( e, ad_olmProviderURIList,
+ &si->si_bindconf.sb_uri, NULL );
+
+ {
+ si->si_connaddr.bv_val = si->si_connaddrbuf;
+ si->si_connaddr.bv_len = sizeof( si->si_connaddrbuf );
+ si->si_connaddrbuf[0] = '\0';
+ attr_merge_normalize_one( e, ad_olmConnection, &si->si_connaddr, NULL );
+ }
+ {
+ struct berval bv = BER_BVC("Refresh");
+ attr_merge_normalize_one( e, ad_olmSyncPhase, &bv, NULL );
+ }
+ {
+ attr_merge_normalize_one( e, ad_olmNextConnect, (struct berval *)&zerotime, NULL );
+ attr_merge_normalize_one( e, ad_olmLastConnect, (struct berval *)&zerotime, NULL );
+ attr_merge_normalize_one( e, ad_olmLastContact, (struct berval *)&zerotime, NULL );
+ }
+ {
+ struct berval bv = BER_BVC("");
+ attr_merge_normalize_one( e, ad_olmLastCookieRcvd, &bv, NULL );
+ attr_merge_normalize_one( e, ad_olmLastCookieSent, &bv, NULL );
+ }
+ {
+ monitor_callback_t *cb = ch_calloc( sizeof( monitor_callback_t ), 1 );
+ cb->mc_update = syncrepl_monitor_update;
+ cb->mc_private = si;
+ rc = mbe->register_entry( e, cb, NULL, 0 );
+ }
+
+ si->si_monitor_ndn = e->e_nname;
+ BER_BVZERO( &e->e_nname );
+ entry_free( e );
+
+ return rc;
+}
+
+static int
+syncrepl_monitor_del(
+ syncinfo_t *si
+)
+{
+ BackendInfo *mi;
+
+ mi = backend_info( "monitor" );
+ if ( mi && mi->bi_extra ) {
+ monitor_extra_t *mbe = mi->bi_extra;
+ mbe->unregister_entry( &si->si_monitor_ndn );
+ }
+ ch_free( si->si_lastCookieSent.bv_val );
+ ch_free( si->si_lastCookieRcvd.bv_val );
+ ch_free( si->si_monitor_ndn.bv_val );
+ return 0;
+}
+
+static int
+add_syncrepl(
+ ConfigArgs *c )
+{
+ syncinfo_t *si;
+ int i, rc = 0;
+
+ if ( !( c->be->be_search && c->be->be_add && c->be->be_modify && c->be->be_delete ) ) {
+ snprintf( c->cr_msg, sizeof(c->cr_msg), "database %s does not support "
+ "operations required for syncrepl", c->be->be_type );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
+ return 1;
+ }
+ if ( BER_BVISEMPTY( &c->be->be_rootdn ) ) {
+ strcpy( c->cr_msg, "rootDN must be defined before syncrepl may be used" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s\n", c->log, c->cr_msg );
+ return 1;
+ }
+ si = (syncinfo_t *) ch_calloc( 1, sizeof( syncinfo_t ) );
+
+ if ( si == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "out of memory in add_syncrepl\n" );
+ return 1;
+ }
+
+ si->si_bindconf.sb_tls = SB_TLS_OFF;
+ si->si_bindconf.sb_method = LDAP_AUTH_SIMPLE;
+ si->si_schemachecking = 0;
+ ber_str2bv( "(objectclass=*)", STRLENOF("(objectclass=*)"), 1,
+ &si->si_filterstr );
+ si->si_base.bv_val = NULL;
+ si->si_scope = LDAP_SCOPE_SUBTREE;
+ si->si_attrsonly = 0;
+ si->si_anlist = (AttributeName *) ch_calloc( 1, sizeof( AttributeName ) );
+ si->si_exanlist = (AttributeName *) ch_calloc( 1, sizeof( AttributeName ) );
+ si->si_attrs = NULL;
+ si->si_allattrs = 0;
+ si->si_allopattrs = 0;
+ si->si_exattrs = NULL;
+ si->si_type = si->si_ctype = LDAP_SYNC_REFRESH_ONLY;
+ si->si_interval = 86400;
+ si->si_retryinterval = NULL;
+ si->si_retrynum_init = NULL;
+ si->si_retrynum = NULL;
+ si->si_manageDSAit = 0;
+ si->si_tlimit = 0;
+ si->si_slimit = 0;
+
+ si->si_presentlist = NULL;
+ LDAP_LIST_INIT( &si->si_nonpresentlist );
+ ldap_pvt_thread_mutex_init( &si->si_monitor_mutex );
+ ldap_pvt_thread_mutex_init( &si->si_mutex );
+
+ si->si_is_configdb = strcmp( c->be->be_suffix[0].bv_val, "cn=config" ) == 0;
+
+ rc = parse_syncrepl_line( c, si );
+
+ if ( rc == 0 ) {
+ LDAPURLDesc *lud;
+
+ /* Must be LDAPv3 because we need controls */
+ switch ( si->si_bindconf.sb_version ) {
+ case 0:
+ /* not explicitly set */
+ si->si_bindconf.sb_version = LDAP_VERSION3;
+ break;
+ case 3:
+ /* explicitly set */
+ break;
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "version %d incompatible with syncrepl\n",
+ si->si_bindconf.sb_version );
+ syncinfo_free( si, 0 );
+ return 1;
+ }
+
+ if ( ldap_url_parse( si->si_bindconf.sb_uri.bv_val, &lud )) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "<%s> invalid URL", c->argv[0] );
+ Debug( LDAP_DEBUG_ANY, "%s: %s %s\n",
+ c->log, c->cr_msg, si->si_bindconf.sb_uri.bv_val );
+ return 1;
+ }
+
+ si->si_be = c->be;
+ if ( slapMode & SLAP_SERVER_MODE ) {
+ int isMe = 0;
+ /* check if consumer points to current server and database.
+ * If so, ignore this configuration.
+ */
+ if ( !SLAP_DBHIDDEN( c->be ) ) {
+ int i;
+ /* if searchbase doesn't match current DB suffix,
+ * assume it's different
+ */
+ for ( i=0; !BER_BVISNULL( &c->be->be_nsuffix[i] ); i++ ) {
+ if ( bvmatch( &si->si_base, &c->be->be_nsuffix[i] )) {
+ isMe = 1;
+ break;
+ }
+ }
+ /* if searchbase matches, see if URLs match */
+ if ( isMe && config_check_my_url( si->si_bindconf.sb_uri.bv_val,
+ lud ) == NULL )
+ isMe = 0;
+ }
+
+ if ( !isMe ) {
+ init_syncrepl( si );
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ si->si_re = ldap_pvt_runqueue_insert( &slapd_rq,
+ si->si_interval, do_syncrepl, si, "do_syncrepl",
+ si->si_ridtxt );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+ if ( si->si_re )
+ rc = config_sync_shadow( c ) ? -1 : 0;
+ else
+ rc = -1;
+ }
+ } else {
+ /* multiprovider still needs to see this flag in tool mode */
+ rc = config_sync_shadow( c ) ? -1 : 0;
+ }
+ ldap_free_urldesc( lud );
+ }
+
+#ifdef HAVE_TLS
+ /* Use main slapd defaults */
+ bindconf_tls_defaults( &si->si_bindconf );
+#endif
+ if ( rc != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "failed to add syncinfo\n" );
+ syncinfo_free( si, 0 );
+ return 1;
+ } else {
+ Debug( LDAP_DEBUG_CONFIG,
+ "Config: ** successfully added syncrepl %s \"%s\"\n",
+ si->si_ridtxt,
+ BER_BVISNULL( &si->si_bindconf.sb_uri ) ?
+ "(null)" : si->si_bindconf.sb_uri.bv_val );
+ if ( c->be->be_syncinfo ) {
+ syncinfo_t **sip;
+
+ si->si_cookieState = c->be->be_syncinfo->si_cookieState;
+
+ for ( i = 0, sip = &c->be->be_syncinfo;
+ (*sip)->si_next && ( c->valx < 0 || i < c->valx );
+ sip = &(*sip)->si_next, i++ )
+ /* advance to the desired position */ ;
+ si->si_next = *sip;
+ *sip = si;
+
+ } else {
+ si->si_cookieState = ch_calloc( 1, sizeof( cookie_state ));
+ ldap_pvt_thread_mutex_init( &si->si_cookieState->cs_mutex );
+ ldap_pvt_thread_mutex_init( &si->si_cookieState->cs_pmutex );
+ ldap_pvt_thread_mutex_init( &si->si_cookieState->cs_refresh_mutex );
+ ldap_pvt_thread_cond_init( &si->si_cookieState->cs_cond );
+
+ c->be->be_syncinfo = si;
+ si->si_next = NULL;
+ }
+ si->si_cookieState->cs_ref++;
+
+ syncrepl_monitor_init();
+
+ return 0;
+ }
+}
+
+static void
+syncrepl_unparse( syncinfo_t *si, struct berval *bv )
+{
+ struct berval bc, uri, bs;
+ char buf[BUFSIZ*2], *ptr;
+ ber_len_t len;
+ int i;
+# define WHATSLEFT ((ber_len_t) (&buf[sizeof( buf )] - ptr))
+
+ BER_BVZERO( bv );
+
+ /* temporarily inhibit bindconf from printing URI */
+ uri = si->si_bindconf.sb_uri;
+ BER_BVZERO( &si->si_bindconf.sb_uri );
+ si->si_bindconf.sb_version = 0;
+ bindconf_unparse( &si->si_bindconf, &bc );
+ si->si_bindconf.sb_uri = uri;
+ si->si_bindconf.sb_version = LDAP_VERSION3;
+
+ ptr = buf;
+ assert( si->si_rid >= 0 && si->si_rid <= SLAP_SYNC_RID_MAX );
+ len = snprintf( ptr, WHATSLEFT, IDSTR "=%03d " PROVIDERSTR "=%s",
+ si->si_rid, si->si_bindconf.sb_uri.bv_val );
+ if ( len >= sizeof( buf ) ) return;
+ ptr += len;
+ if ( !BER_BVISNULL( &bc ) ) {
+ if ( WHATSLEFT <= bc.bv_len ) {
+ free( bc.bv_val );
+ return;
+ }
+ ptr = lutil_strcopy( ptr, bc.bv_val );
+ free( bc.bv_val );
+ }
+ if ( !BER_BVISEMPTY( &si->si_filterstr ) ) {
+ if ( WHATSLEFT <= STRLENOF( " " FILTERSTR "=\"" "\"" ) + si->si_filterstr.bv_len ) return;
+ ptr = lutil_strcopy( ptr, " " FILTERSTR "=\"" );
+ ptr = lutil_strcopy( ptr, si->si_filterstr.bv_val );
+ *ptr++ = '"';
+ }
+ if ( !BER_BVISNULL( &si->si_base ) ) {
+ if ( WHATSLEFT <= STRLENOF( " " SEARCHBASESTR "=\"" "\"" ) + si->si_base.bv_len ) return;
+ ptr = lutil_strcopy( ptr, " " SEARCHBASESTR "=\"" );
+ ptr = lutil_strcopy( ptr, si->si_base.bv_val );
+ *ptr++ = '"';
+ }
+ if ( !BER_BVISNULL( &si->si_suffixm ) ) {
+ if ( WHATSLEFT <= STRLENOF( " " SUFFIXMSTR "=\"" "\"" ) + si->si_suffixm.bv_len ) return;
+ ptr = lutil_strcopy( ptr, " " SUFFIXMSTR "=\"" );
+ ptr = lutil_strcopy( ptr, si->si_suffixm.bv_val );
+ *ptr++ = '"';
+ }
+ if ( !BER_BVISEMPTY( &si->si_logfilterstr ) ) {
+ if ( WHATSLEFT <= STRLENOF( " " LOGFILTERSTR "=\"" "\"" ) + si->si_logfilterstr.bv_len ) return;
+ ptr = lutil_strcopy( ptr, " " LOGFILTERSTR "=\"" );
+ ptr = lutil_strcopy( ptr, si->si_logfilterstr.bv_val );
+ *ptr++ = '"';
+ }
+ if ( !BER_BVISNULL( &si->si_logbase ) ) {
+ if ( WHATSLEFT <= STRLENOF( " " LOGBASESTR "=\"" "\"" ) + si->si_logbase.bv_len ) return;
+ ptr = lutil_strcopy( ptr, " " LOGBASESTR "=\"" );
+ ptr = lutil_strcopy( ptr, si->si_logbase.bv_val );
+ *ptr++ = '"';
+ }
+ if ( ldap_pvt_scope2bv( si->si_scope, &bs ) == LDAP_SUCCESS ) {
+ if ( WHATSLEFT <= STRLENOF( " " SCOPESTR "=" ) + bs.bv_len ) return;
+ ptr = lutil_strcopy( ptr, " " SCOPESTR "=" );
+ ptr = lutil_strcopy( ptr, bs.bv_val );
+ }
+ if ( si->si_attrsonly ) {
+ if ( WHATSLEFT <= STRLENOF( " " ATTRSONLYSTR "=\"" "\"" ) ) return;
+ ptr = lutil_strcopy( ptr, " " ATTRSONLYSTR );
+ }
+ if ( si->si_anfile ) {
+ if ( WHATSLEFT <= STRLENOF( " " ATTRSSTR "=\":include:" "\"" ) + strlen( si->si_anfile ) ) return;
+ ptr = lutil_strcopy( ptr, " " ATTRSSTR "=:include:\"" );
+ ptr = lutil_strcopy( ptr, si->si_anfile );
+ *ptr++ = '"';
+ } else if ( si->si_allattrs || si->si_allopattrs ||
+ ( si->si_anlist && !BER_BVISNULL(&si->si_anlist[0].an_name) ) )
+ {
+ char *old;
+
+ if ( WHATSLEFT <= STRLENOF( " " ATTRSONLYSTR "=\"" "\"" ) ) return;
+ ptr = lutil_strcopy( ptr, " " ATTRSSTR "=\"" );
+ old = ptr;
+ ptr = anlist_unparse( si->si_anlist, ptr, WHATSLEFT );
+ if ( ptr == NULL ) return;
+ if ( si->si_allattrs ) {
+ if ( WHATSLEFT <= STRLENOF( ",*\"" ) ) return;
+ if ( old != ptr ) *ptr++ = ',';
+ *ptr++ = '*';
+ }
+ if ( si->si_allopattrs ) {
+ if ( WHATSLEFT <= STRLENOF( ",+\"" ) ) return;
+ if ( old != ptr ) *ptr++ = ',';
+ *ptr++ = '+';
+ }
+ *ptr++ = '"';
+ }
+ if ( si->si_exanlist && !BER_BVISNULL(&si->si_exanlist[0].an_name) ) {
+ if ( WHATSLEFT <= STRLENOF( " " EXATTRSSTR "=" ) ) return;
+ ptr = lutil_strcopy( ptr, " " EXATTRSSTR "=" );
+ ptr = anlist_unparse( si->si_exanlist, ptr, WHATSLEFT );
+ if ( ptr == NULL ) return;
+ }
+ if ( WHATSLEFT <= STRLENOF( " " SCHEMASTR "=" ) + STRLENOF( "off" ) ) return;
+ ptr = lutil_strcopy( ptr, " " SCHEMASTR "=" );
+ ptr = lutil_strcopy( ptr, si->si_schemachecking ? "on" : "off" );
+
+ if ( WHATSLEFT <= STRLENOF( " " TYPESTR "=" ) + STRLENOF( "refreshAndPersist" ) ) return;
+ ptr = lutil_strcopy( ptr, " " TYPESTR "=" );
+#ifdef LDAP_CONTROL_X_DIRSYNC
+ if ( si->si_type == MSAD_DIRSYNC )
+ ptr = lutil_strcopy( ptr, "dirSync" );
+ else
+#endif
+ ptr = lutil_strcopy( ptr, si->si_type == LDAP_SYNC_REFRESH_AND_PERSIST ?
+ "refreshAndPersist" : "refreshOnly" );
+
+ if ( si->si_type == LDAP_SYNC_REFRESH_ONLY
+#ifdef LDAP_CONTROL_X_DIRSYNC
+ || si->si_type == MSAD_DIRSYNC
+#endif
+ ) {
+ int dd, hh, mm, ss;
+
+ dd = si->si_interval;
+ ss = dd % 60;
+ dd /= 60;
+ mm = dd % 60;
+ dd /= 60;
+ hh = dd % 24;
+ dd /= 24;
+ len = snprintf( ptr, WHATSLEFT, " %s=%02d:%02d:%02d:%02d",
+ INTERVALSTR, dd, hh, mm, ss );
+ if ( len >= WHATSLEFT ) return;
+ ptr += len;
+ }
+
+ if ( si->si_got & GOT_RETRY ) {
+ const char *space = "";
+ if ( WHATSLEFT <= STRLENOF( " " RETRYSTR "=\"" "\"" ) ) return;
+ ptr = lutil_strcopy( ptr, " " RETRYSTR "=\"" );
+ for (i=0; si->si_retryinterval[i]; i++) {
+ len = snprintf( ptr, WHATSLEFT, "%s%ld ", space,
+ (long) si->si_retryinterval[i] );
+ space = " ";
+ if ( WHATSLEFT - 1 <= len ) return;
+ ptr += len;
+ if ( si->si_retrynum_init[i] == RETRYNUM_FOREVER )
+ *ptr++ = '+';
+ else {
+ len = snprintf( ptr, WHATSLEFT, "%d", si->si_retrynum_init[i] );
+ if ( WHATSLEFT <= len ) return;
+ ptr += len;
+ }
+ }
+ if ( WHATSLEFT <= STRLENOF( "\"" ) ) return;
+ *ptr++ = '"';
+ } else {
+ ptr = lutil_strcopy( ptr, " " RETRYSTR "=undefined" );
+ }
+
+ if ( si->si_slimit ) {
+ len = snprintf( ptr, WHATSLEFT, " " SLIMITSTR "=%d", si->si_slimit );
+ if ( WHATSLEFT <= len ) return;
+ ptr += len;
+ }
+
+ if ( si->si_tlimit ) {
+ len = snprintf( ptr, WHATSLEFT, " " TLIMITSTR "=%d", si->si_tlimit );
+ if ( WHATSLEFT <= len ) return;
+ ptr += len;
+ }
+
+ if ( si->si_syncdata ) {
+ if ( enum_to_verb( datamodes, si->si_syncdata, &bc ) >= 0 ) {
+ if ( WHATSLEFT <= STRLENOF( " " SYNCDATASTR "=" ) + bc.bv_len ) return;
+ ptr = lutil_strcopy( ptr, " " SYNCDATASTR "=" );
+ ptr = lutil_strcopy( ptr, bc.bv_val );
+ }
+ }
+
+ if ( si->si_lazyCommit ) {
+ ptr = lutil_strcopy( ptr, " " LAZY_COMMIT );
+ }
+
+ bc.bv_len = ptr - buf;
+ bc.bv_val = buf;
+ ber_dupbv( bv, &bc );
+}
+
+int
+syncrepl_config( ConfigArgs *c )
+{
+ if (c->op == SLAP_CONFIG_EMIT) {
+ if ( c->be->be_syncinfo ) {
+ struct berval bv;
+ syncinfo_t *si;
+
+ for ( si = c->be->be_syncinfo; si; si=si->si_next ) {
+ syncrepl_unparse( si, &bv );
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ }
+ return 0;
+ }
+ return 1;
+ } else if ( c->op == LDAP_MOD_DELETE ) {
+ int isrunning = 0;
+ if ( c->be->be_syncinfo ) {
+ syncinfo_t *si, **sip;
+ int i;
+
+ for ( sip = &c->be->be_syncinfo, i=0; *sip; i++ ) {
+ si = *sip;
+ if ( c->valx == -1 || i == c->valx ) {
+ *sip = si->si_next;
+ si->si_ctype = -1;
+ si->si_next = NULL;
+ /* If the task is currently active, we have to leave
+ * it running. It will exit on its own. This will only
+ * happen when running on the cn=config DB.
+ */
+ if ( si->si_re ) {
+ if ( si->si_be == c->ca_op->o_bd ||
+ ldap_pvt_thread_mutex_trylock( &si->si_mutex )) {
+ isrunning = 1;
+ } else {
+ /* There is no active thread, but we must still
+ * ensure that no thread is (or will be) queued
+ * while we removes the task.
+ */
+ struct re_s *re = si->si_re;
+ si->si_re = NULL;
+
+ if ( si->si_conn ) {
+ connection_client_stop( si->si_conn );
+ si->si_conn = NULL;
+ }
+
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, re ) ) {
+ ldap_pvt_runqueue_stoptask( &slapd_rq, re );
+ isrunning = 1;
+ }
+ if ( !re->pool_cookie || ldap_pvt_thread_pool_retract( re->pool_cookie ) > 0 )
+ isrunning = 0;
+
+ ldap_pvt_runqueue_remove( &slapd_rq, re );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+
+ ldap_pvt_thread_mutex_unlock( &si->si_mutex );
+ }
+ }
+ if ( !isrunning ) {
+ syncinfo_free( si, 0 );
+ }
+ if ( i == c->valx )
+ break;
+ } else {
+ sip = &si->si_next;
+ }
+ }
+ }
+ if ( !c->be->be_syncinfo ) {
+ SLAP_DBFLAGS( c->be ) &= ~SLAP_DBFLAG_SHADOW_MASK;
+ }
+ return 0;
+ }
+ if ( SLAP_SLURP_SHADOW( c->be ) ) {
+ Debug(LDAP_DEBUG_ANY, "%s: "
+ "syncrepl: database already shadowed.\n",
+ c->log );
+ return(1);
+ } else {
+ return add_syncrepl( c );
+ }
+}
diff --git a/servers/slapd/syntax.c b/servers/slapd/syntax.c
new file mode 100644
index 0000000..0400011
--- /dev/null
+++ b/servers/slapd/syntax.c
@@ -0,0 +1,457 @@
+/* syntax.c - routines to manage syntax definitions */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "slap.h"
+
+struct sindexrec {
+ char *sir_name;
+ Syntax *sir_syn;
+};
+
+static Avlnode *syn_index = NULL;
+static LDAP_STAILQ_HEAD(SyntaxList, Syntax) syn_list
+ = LDAP_STAILQ_HEAD_INITIALIZER(syn_list);
+
+/* Last hardcoded attribute registered */
+Syntax *syn_sys_tail;
+
+static int
+syn_index_cmp(
+ const void *v_sir1,
+ const void *v_sir2
+)
+{
+ const struct sindexrec *sir1 = v_sir1, *sir2 = v_sir2;
+ return (strcmp( sir1->sir_name, sir2->sir_name ));
+}
+
+static int
+syn_index_name_cmp(
+ const void *name,
+ const void *sir
+)
+{
+ return (strcmp( name, ((const struct sindexrec *)sir)->sir_name ));
+}
+
+Syntax *
+syn_find( const char *synname )
+{
+ struct sindexrec *sir = NULL;
+
+ if ( (sir = ldap_avl_find( syn_index, synname, syn_index_name_cmp )) != NULL ) {
+ return( sir->sir_syn );
+ }
+ return( NULL );
+}
+
+Syntax *
+syn_find_desc( const char *syndesc, int *len )
+{
+ Syntax *synp;
+
+ LDAP_STAILQ_FOREACH(synp, &syn_list, ssyn_next) {
+ if ((*len = dscompare( synp->ssyn_syn.syn_desc, syndesc, '{' /*'}'*/ ))) {
+ return synp;
+ }
+ }
+ return( NULL );
+}
+
+int
+syn_is_sup( Syntax *syn, Syntax *sup )
+{
+ int i;
+
+ assert( syn != NULL );
+ assert( sup != NULL );
+
+ if ( syn == sup ) {
+ return 1;
+ }
+
+ if ( syn->ssyn_sups == NULL ) {
+ return 0;
+ }
+
+ for ( i = 0; syn->ssyn_sups[i]; i++ ) {
+ if ( syn->ssyn_sups[i] == sup ) {
+ return 1;
+ }
+
+ if ( syn_is_sup( syn->ssyn_sups[i], sup ) ) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void
+syn_destroy( void )
+{
+ Syntax *s;
+
+ ldap_avl_free( syn_index, ldap_memfree );
+ while( !LDAP_STAILQ_EMPTY( &syn_list ) ) {
+ s = LDAP_STAILQ_FIRST( &syn_list );
+ LDAP_STAILQ_REMOVE_HEAD( &syn_list, ssyn_next );
+ if ( s->ssyn_sups ) {
+ SLAP_FREE( s->ssyn_sups );
+ }
+ ldap_syntax_free( (LDAPSyntax *)s );
+ }
+}
+
+static int
+syn_insert(
+ Syntax *ssyn,
+ Syntax *prev,
+ const char **err )
+{
+ struct sindexrec *sir;
+
+ LDAP_STAILQ_NEXT( ssyn, ssyn_next ) = NULL;
+
+ if ( ssyn->ssyn_oid ) {
+ sir = (struct sindexrec *)
+ SLAP_CALLOC( 1, sizeof(struct sindexrec) );
+ if( sir == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "SLAP_CALLOC Error\n" );
+ return LDAP_OTHER;
+ }
+ sir->sir_name = ssyn->ssyn_oid;
+ sir->sir_syn = ssyn;
+ if ( ldap_avl_insert( &syn_index, (caddr_t) sir,
+ syn_index_cmp, ldap_avl_dup_error ) ) {
+ *err = ssyn->ssyn_oid;
+ ldap_memfree(sir);
+ return SLAP_SCHERR_SYN_DUP;
+ }
+ /* FIX: temporal consistency check */
+ syn_find(sir->sir_name);
+ }
+
+ if ( ssyn->ssyn_flags & SLAP_AT_HARDCODE ) {
+ prev = syn_sys_tail;
+ syn_sys_tail = ssyn;
+ }
+
+ if ( prev ) {
+ LDAP_STAILQ_INSERT_AFTER( &syn_list, prev, ssyn, ssyn_next );
+ } else {
+ LDAP_STAILQ_INSERT_TAIL( &syn_list, ssyn, ssyn_next );
+ }
+ return 0;
+}
+
+int
+syn_add(
+ LDAPSyntax *syn,
+ int user,
+ slap_syntax_defs_rec *def,
+ Syntax **ssynp,
+ Syntax *prev,
+ const char **err )
+{
+ Syntax *ssyn;
+ int code = 0;
+
+ if ( ssynp != NULL ) {
+ *ssynp = NULL;
+ }
+
+ ssyn = (Syntax *) SLAP_CALLOC( 1, sizeof(Syntax) );
+ if ( ssyn == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "SLAP_CALLOC Error\n" );
+ return SLAP_SCHERR_OUTOFMEM;
+ }
+
+ AC_MEMCPY( &ssyn->ssyn_syn, syn, sizeof(LDAPSyntax) );
+
+ LDAP_STAILQ_NEXT(ssyn,ssyn_next) = NULL;
+
+ /*
+ * note: ssyn_bvoid uses the same memory of ssyn_syn.syn_oid;
+ * ssyn_oidlen is #defined as ssyn_bvoid.bv_len
+ */
+ ssyn->ssyn_bvoid.bv_val = ssyn->ssyn_syn.syn_oid;
+ ssyn->ssyn_oidlen = strlen(syn->syn_oid);
+ ssyn->ssyn_flags = def->sd_flags;
+ ssyn->ssyn_validate = def->sd_validate;
+ ssyn->ssyn_pretty = def->sd_pretty;
+
+ ssyn->ssyn_sups = NULL;
+
+#ifdef SLAPD_BINARY_CONVERSION
+ ssyn->ssyn_ber2str = def->sd_ber2str;
+ ssyn->ssyn_str2ber = def->sd_str2ber;
+#endif
+
+ if ( def->sd_validate == NULL && def->sd_pretty == NULL && syn->syn_extensions != NULL ) {
+ LDAPSchemaExtensionItem **lsei;
+ Syntax *subst = NULL;
+
+ for ( lsei = syn->syn_extensions; *lsei != NULL; lsei++) {
+ if ( strcmp( (*lsei)->lsei_name, "X-SUBST" ) != 0 ) {
+ continue;
+ }
+
+ assert( (*lsei)->lsei_values != NULL );
+ if ( (*lsei)->lsei_values[0] == NULL
+ || (*lsei)->lsei_values[1] != NULL )
+ {
+ Debug( LDAP_DEBUG_ANY, "syn_add(%s): exactly one substitute syntax must be present\n",
+ ssyn->ssyn_syn.syn_oid );
+ SLAP_FREE( ssyn );
+ return SLAP_SCHERR_SYN_SUBST_NOT_SPECIFIED;
+ }
+
+ subst = syn_find( (*lsei)->lsei_values[0] );
+ if ( subst == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "syn_add(%s): substitute syntax %s not found\n",
+ ssyn->ssyn_syn.syn_oid, (*lsei)->lsei_values[0] );
+ SLAP_FREE( ssyn );
+ return SLAP_SCHERR_SYN_SUBST_NOT_FOUND;
+ }
+ break;
+ }
+
+ if ( subst != NULL ) {
+ ssyn->ssyn_flags = subst->ssyn_flags;
+ ssyn->ssyn_validate = subst->ssyn_validate;
+ ssyn->ssyn_pretty = subst->ssyn_pretty;
+
+ ssyn->ssyn_sups = NULL;
+
+#ifdef SLAPD_BINARY_CONVERSION
+ ssyn->ssyn_ber2str = subst->ssyn_ber2str;
+ ssyn->ssyn_str2ber = subst->ssyn_str2ber;
+#endif
+ }
+ }
+
+ if ( def->sd_sups != NULL ) {
+ int cnt;
+
+ for ( cnt = 0; def->sd_sups[cnt] != NULL; cnt++ )
+ ;
+
+ ssyn->ssyn_sups = (Syntax **)SLAP_CALLOC( cnt + 1,
+ sizeof( Syntax * ) );
+ if ( ssyn->ssyn_sups == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "SLAP_CALLOC Error\n" );
+ code = SLAP_SCHERR_OUTOFMEM;
+
+ } else {
+ for ( cnt = 0; def->sd_sups[cnt] != NULL; cnt++ ) {
+ ssyn->ssyn_sups[cnt] = syn_find( def->sd_sups[cnt] );
+ if ( ssyn->ssyn_sups[cnt] == NULL ) {
+ *err = def->sd_sups[cnt];
+ code = SLAP_SCHERR_SYN_SUP_NOT_FOUND;
+ }
+ }
+ }
+ }
+
+ if ( !user )
+ ssyn->ssyn_flags |= SLAP_SYNTAX_HARDCODE;
+
+ if ( code == 0 ) {
+ code = syn_insert( ssyn, prev, err );
+ }
+
+ if ( code != 0 && ssyn != NULL ) {
+ if ( ssyn->ssyn_sups != NULL ) {
+ SLAP_FREE( ssyn->ssyn_sups );
+ }
+ SLAP_FREE( ssyn );
+ ssyn = NULL;
+ }
+
+ if (ssynp ) {
+ *ssynp = ssyn;
+ }
+
+ return code;
+}
+
+int
+register_syntax(
+ slap_syntax_defs_rec *def )
+{
+ LDAPSyntax *syn;
+ int code;
+ const char *err;
+
+ syn = ldap_str2syntax( def->sd_desc, &code, &err, LDAP_SCHEMA_ALLOW_ALL);
+ if ( !syn ) {
+ Debug( LDAP_DEBUG_ANY, "Error in register_syntax: %s before %s in %s\n",
+ ldap_scherr2str(code), err, def->sd_desc );
+
+ return( -1 );
+ }
+
+ code = syn_add( syn, 0, def, NULL, NULL, &err );
+
+ if ( code ) {
+ Debug( LDAP_DEBUG_ANY, "Error in register_syntax: %s %s in %s\n",
+ scherr2str(code), err, def->sd_desc );
+ ldap_syntax_free( syn );
+
+ return( -1 );
+ }
+
+ ldap_memfree( syn );
+
+ return( 0 );
+}
+
+int
+syn_schema_info( Entry *e )
+{
+ AttributeDescription *ad_ldapSyntaxes = slap_schema.si_ad_ldapSyntaxes;
+ Syntax *syn;
+ struct berval val;
+ struct berval nval;
+
+ LDAP_STAILQ_FOREACH(syn, &syn_list, ssyn_next ) {
+ if ( ! syn->ssyn_validate ) {
+ /* skip syntaxes without validators */
+ continue;
+ }
+ if ( syn->ssyn_flags & SLAP_SYNTAX_HIDE ) {
+ /* hide syntaxes */
+ continue;
+ }
+
+ if ( ldap_syntax2bv( &syn->ssyn_syn, &val ) == NULL ) {
+ return -1;
+ }
+#if 0
+ Debug( LDAP_DEBUG_TRACE, "Merging syn [%ld] %s\n",
+ (long) val.bv_len, val.bv_val );
+#endif
+
+ nval.bv_val = syn->ssyn_oid;
+ nval.bv_len = strlen(syn->ssyn_oid);
+
+ if( attr_merge_one( e, ad_ldapSyntaxes, &val, &nval ) )
+ {
+ return -1;
+ }
+ ldap_memfree( val.bv_val );
+ }
+ return 0;
+}
+
+void
+syn_delete( Syntax *syn )
+{
+ LDAP_STAILQ_REMOVE(&syn_list, syn, Syntax, ssyn_next);
+}
+
+int
+syn_start( Syntax **syn )
+{
+ assert( syn != NULL );
+
+ *syn = LDAP_STAILQ_FIRST(&syn_list);
+
+ return (*syn != NULL);
+}
+
+int
+syn_next( Syntax **syn )
+{
+ assert( syn != NULL );
+
+#if 0 /* pedantic check: don't use this */
+ {
+ Syntax *tmp = NULL;
+
+ LDAP_STAILQ_FOREACH(tmp,&syn_list,ssyn_next) {
+ if ( tmp == *syn ) {
+ break;
+ }
+ }
+
+ assert( tmp != NULL );
+ }
+#endif
+
+ *syn = LDAP_STAILQ_NEXT(*syn,ssyn_next);
+
+ return (*syn != NULL);
+}
+
+void
+syn_unparse( BerVarray *res, Syntax *start, Syntax *end, int sys )
+{
+ Syntax *syn;
+ int i, num;
+ struct berval bv, *bva = NULL, idx;
+ char ibuf[32];
+
+ if ( !start )
+ start = LDAP_STAILQ_FIRST( &syn_list );
+
+ /* count the result size */
+ i = 0;
+ for ( syn = start; syn; syn = LDAP_STAILQ_NEXT( syn, ssyn_next ) ) {
+ if ( sys && !( syn->ssyn_flags & SLAP_SYNTAX_HARDCODE ) ) break;
+ i++;
+ if ( syn == end ) break;
+ }
+ if ( !i ) return;
+
+ num = i;
+ bva = ch_malloc( (num+1) * sizeof(struct berval) );
+ BER_BVZERO( bva );
+ idx.bv_val = ibuf;
+ if ( sys ) {
+ idx.bv_len = 0;
+ ibuf[0] = '\0';
+ }
+ i = 0;
+ for ( syn = start; syn; syn = LDAP_STAILQ_NEXT( syn, ssyn_next ) ) {
+ if ( sys && !( syn->ssyn_flags & SLAP_SYNTAX_HARDCODE ) ) break;
+ if ( ldap_syntax2bv( &syn->ssyn_syn, &bv ) == NULL ) {
+ ber_bvarray_free( bva );
+ }
+ if ( !sys ) {
+ idx.bv_len = sprintf(idx.bv_val, "{%d}", i);
+ }
+ bva[i].bv_len = idx.bv_len + bv.bv_len;
+ bva[i].bv_val = ch_malloc( bva[i].bv_len + 1 );
+ strcpy( bva[i].bv_val, ibuf );
+ strcpy( bva[i].bv_val + idx.bv_len, bv.bv_val );
+ i++;
+ bva[i].bv_val = NULL;
+ ldap_memfree( bv.bv_val );
+ if ( syn == end ) break;
+ }
+ *res = bva;
+}
+
diff --git a/servers/slapd/syslog.c b/servers/slapd/syslog.c
new file mode 100644
index 0000000..47ea1c6
--- /dev/null
+++ b/servers/slapd/syslog.c
@@ -0,0 +1,289 @@
+/* $OpenBSD: syslog.c,v 1.29 2007/11/09 18:40:19 millert Exp $ */
+/*
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "portable.h"
+
+#include <sys/types.h>
+#include <ac/socket.h>
+#include <ac/syslog.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <netdb.h>
+
+#include <ac/errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+#include <ac/stdarg.h>
+
+#include "slap.h"
+#include "lutil.h"
+
+static int LogType = SOCK_DGRAM; /* type of socket connection */
+static int LogFile = -1; /* fd for log */
+static int connected; /* have done connect */
+static int LogStat; /* status bits, set by openlog() */
+static const char *LogTag; /* string to tag the entry with */
+static int LogFacility = LOG_USER; /* default facility code */
+
+static void disconnectlog(void);
+static void connectlog(void);
+
+static void my_localtime(const time_t *t, struct tm *tm);
+
+/*
+ * syslog
+ * print message on log file; output is intended for syslogd(8).
+ */
+void
+syslog(int pri, const char *fmt, ...)
+{
+ va_list ap;
+ char *p, *pend;
+#define TBUF_LEN 2048
+#define FMT_LEN 1024
+ char tbuf[TBUF_LEN];
+ int cnt;
+ int error;
+ int tbuf_left, prlen;
+
+ va_start(ap, fmt);
+
+ /* Check for invalid bits. */
+ if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
+ if (LogTest(LOG_ERR))
+ lutil_debug(slap_debug, LOG_ERR,
+ "syslog: unknown facility/priority: %x", pri);
+ pri &= LOG_PRIMASK|LOG_FACMASK;
+ }
+
+ /* Set default facility if none specified. */
+ if ((pri & LOG_FACMASK) == 0)
+ pri |= LogFacility;
+
+ p = tbuf;
+ pend = p + TBUF_LEN;
+
+ *p++ = '<';
+ p += sprintf(p, "%d", pri);
+ *p++ = '>';
+
+#if 0
+ (void)time(&now);
+ my_localtime(&now, &tm);
+ p += strftime(p, tbuf_left, "%h %e %T ", &tm);
+#endif
+
+ if (LogTag != NULL) {
+ p = lutil_strcopy(p, LogTag);
+ }
+ if (LogStat & LOG_PID) {
+ *p++ = '[';
+ p += sprintf(p, "%ld", (long)getpid());
+ *p++ = ']';
+ }
+ if (LogTag != NULL) {
+ *p++ = ':';
+ *p++ = ' ';
+ }
+
+ tbuf_left = pend - p;
+ prlen = vsnprintf(p, tbuf_left, fmt, ap);
+ va_end(ap);
+ if (prlen < 0)
+ prlen = 0;
+ else if (prlen >= tbuf_left)
+ prlen = tbuf_left - 1;
+ p += prlen;
+ cnt = p - tbuf;
+
+ /* Get connected, output the message to the local logger. */
+ if (LogFile == -1)
+ openlog(LogTag, LogStat, 0);
+ connectlog();
+
+ /*
+ * If the send() failed, there are two likely scenarios:
+ * 1) syslogd was restarted
+ * 2) /dev/log is out of socket buffer space
+ * We attempt to reconnect to /dev/log to take care of
+ * case #1 and keep send()ing data to cover case #2
+ * to give syslogd a chance to empty its socket buffer.
+ */
+ if ((error = send(LogFile, tbuf, cnt, 0)) < 0) {
+ if (errno != ENOBUFS) {
+ disconnectlog();
+ connectlog();
+ }
+ do {
+ usleep(1);
+ if ((error = send(LogFile, tbuf, cnt, 0)) >= 0)
+ break;
+ } while (errno == ENOBUFS);
+ }
+}
+
+static void
+disconnectlog(void)
+{
+ /*
+ * If the user closed the FD and opened another in the same slot,
+ * that's their problem. They should close it before calling on
+ * system services.
+ */
+ if (LogFile != -1) {
+ close(LogFile);
+ LogFile = -1;
+ }
+ connected = 0; /* retry connect */
+}
+
+static void
+connectlog(void)
+{
+ struct sockaddr_un SyslogAddr; /* AF_UNIX address of local logger */
+
+ if (LogFile == -1) {
+ if ((LogFile = socket(AF_UNIX, LogType, 0)) == -1)
+ return;
+ (void)fcntl(LogFile, F_SETFD, FD_CLOEXEC);
+ }
+ if (LogFile != -1 && !connected) {
+ memset(&SyslogAddr, '\0', sizeof(SyslogAddr));
+#ifdef _BSD
+ SyslogAddr.sun_len = sizeof(SyslogAddr);
+#endif
+ SyslogAddr.sun_family = AF_UNIX;
+ strncpy(SyslogAddr.sun_path, _PATH_LOG,
+ sizeof(SyslogAddr.sun_path));
+ if (connect(LogFile, (struct sockaddr *)&SyslogAddr,
+ sizeof(SyslogAddr)) == -1) {
+ (void)close(LogFile);
+ LogFile = -1;
+ } else
+ connected = 1;
+ }
+}
+
+void
+openlog(const char *ident, int logstat, int logfac)
+{
+ if (ident != NULL)
+ LogTag = ident;
+ LogStat = logstat;
+ if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
+ LogFacility = logfac;
+
+ if (LogStat & LOG_NDELAY) /* open immediately */
+ connectlog();
+}
+
+void
+closelog()
+{
+ (void)close(LogFile);
+ LogFile = -1;
+ connected = 0;
+ LogTag = NULL;
+}
+
+#if 0
+#define SECS_PER_HOUR (60 * 60)
+#define SECS_PER_DAY (SECS_PER_HOUR * 24)
+
+/* How many days come before each month (0-12). */
+static const unsigned short int __mon_yday[2][13] =
+ {
+ /* Normal years. */
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+ /* Leap years. */
+ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+ };
+
+/* Compute the `struct tm' representation of *T,
+ and store year, yday, mon, mday, wday, hour, min, sec into *TP */
+static void my_localtime(const time_t *t, struct tm *tm)
+{
+ time_t days, rem, y;
+ const unsigned short int *ip;
+ int leap;
+
+ days = *t / SECS_PER_DAY;
+ rem = *t % SECS_PER_DAY;
+ rem -= timezone;
+ while (rem < 0)
+ {
+ rem += SECS_PER_DAY;
+ --days;
+ }
+ while (rem >= SECS_PER_DAY)
+ {
+ rem -= SECS_PER_DAY;
+ ++days;
+ }
+ tm->tm_hour = rem / SECS_PER_HOUR;
+ rem %= SECS_PER_HOUR;
+ tm->tm_min = rem / 60;
+ tm->tm_sec = rem % 60;
+ /* January 1, 1970 was a Thursday. */
+ tm->tm_wday = (4 + days) % 7;
+ if (tm->tm_wday < 0)
+ tm->tm_wday += 7;
+ y = 1970;
+
+#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
+#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
+#define ISLEAP(y) ((y) % 4 == 0 && ((y) % 100 != 0 || (y) % 400 == 0))
+
+ leap = ISLEAP(y);
+ while (days < 0 || days >= (leap ? 366 : 365))
+ {
+ /* Guess a corrected year, assuming 365 days per year. */
+ time_t yg = y + days / 365 - (days % 365 < 0);
+
+ /* Adjust DAYS and Y to match the guessed year. */
+ days -= ((yg - y) * 365
+ + LEAPS_THRU_END_OF (yg - 1)
+ - LEAPS_THRU_END_OF (y - 1));
+ y = yg;
+ }
+ tm->tm_year = y - 1900;
+ tm->tm_yday = days;
+ ip = __mon_yday[leap];
+ for (y = 11; days < (long int) ip[y]; --y)
+ continue;
+ days -= ip[y];
+ tm->tm_mon = y;
+ tm->tm_mday = days + 1;
+}
+#endif
diff --git a/servers/slapd/txn.c b/servers/slapd/txn.c
new file mode 100644
index 0000000..d81de95
--- /dev/null
+++ b/servers/slapd/txn.c
@@ -0,0 +1,363 @@
+/* txn.c - LDAP Transactions */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include "slap.h"
+
+#include <lber_pvt.h>
+#include <lutil.h>
+
+const struct berval slap_EXOP_TXN_START = BER_BVC(LDAP_EXOP_TXN_START);
+const struct berval slap_EXOP_TXN_END = BER_BVC(LDAP_EXOP_TXN_END);
+
+int txn_start_extop(
+ Operation *op, SlapReply *rs )
+{
+ int rc;
+ struct berval *bv;
+
+ Debug( LDAP_DEBUG_STATS, "%s TXN START\n",
+ op->o_log_prefix );
+
+ if( op->ore_reqdata != NULL ) {
+ rs->sr_text = "no request data expected";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ op->o_bd = op->o_conn->c_authz_backend;
+ if( backend_check_restrictions( op, rs,
+ (struct berval *)&slap_EXOP_TXN_START ) != LDAP_SUCCESS )
+ {
+ return rs->sr_err;
+ }
+
+ /* acquire connection lock */
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+
+ if( op->o_conn->c_txn != CONN_TXN_INACTIVE ) {
+ rs->sr_text = "Too many transactions";
+ rc = LDAP_BUSY;
+ goto done;
+ }
+
+ assert( op->o_conn->c_txn_backend == NULL );
+ op->o_conn->c_txn = CONN_TXN_SPECIFY;
+
+ bv = (struct berval *) ch_malloc( sizeof (struct berval) );
+ bv->bv_len = 0;
+ bv->bv_val = NULL;
+
+ rs->sr_rspdata = bv;
+ rc = LDAP_SUCCESS;
+
+done:
+ /* release connection lock */
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+ return rc;
+}
+
+int txn_spec_ctrl(
+ Operation *op, SlapReply *rs, LDAPControl *ctrl )
+{
+ if ( !ctrl->ldctl_iscritical ) {
+ rs->sr_text = "txnSpec control must be marked critical";
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( op->o_txnSpec ) {
+ rs->sr_text = "txnSpec control provided multiple times";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if ( ctrl->ldctl_value.bv_val == NULL ) {
+ rs->sr_text = "no transaction identifier provided";
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if ( ctrl->ldctl_value.bv_len != 0 ) {
+ rs->sr_text = "invalid transaction identifier";
+ return LDAP_TXN_ID_INVALID;
+ }
+
+ if ( op->o_preread ) { /* temporary limitation */
+ rs->sr_text = "cannot perform pre-read in transaction";
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+ if ( op->o_postread ) { /* temporary limitation */
+ rs->sr_text = "cannot perform post-read in transaction";
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ op->o_txnSpec = SLAP_CONTROL_CRITICAL;
+ return LDAP_SUCCESS;
+}
+
+typedef struct txn_rctrls {
+ struct txn_rctrls *tr_next;
+ ber_int_t tr_msgid;
+ LDAPControl ** tr_ctrls;
+} txn_rctrls;
+
+static int txn_result( Operation *op, SlapReply *rs )
+{
+ if ( rs->sr_ctrls ) {
+ txn_rctrls **t0, *tr;
+ for ( t0 = (txn_rctrls **) &op->o_callback->sc_private; *t0;
+ t0 = &(*t0)->tr_next )
+ ;
+ tr = op->o_tmpalloc( sizeof( txn_rctrls ), op->o_tmpmemctx );
+ tr->tr_next = NULL;
+ *t0 = tr;
+ tr->tr_msgid = op->o_msgid;
+ tr->tr_ctrls = ldap_controls_dup( rs->sr_ctrls );
+ }
+ return rs->sr_err;
+}
+
+static int txn_put_ctrls( Operation *op, BerElement *ber, txn_rctrls *tr )
+{
+ txn_rctrls *next;
+ int i;
+ ber_printf( ber, "{" );
+ for ( ; tr; tr = next ) {
+ next = tr->tr_next;
+ ber_printf( ber, "{it{", tr->tr_msgid, LDAP_TAG_CONTROLS );
+ for ( i = 0; tr->tr_ctrls[i]; i++ )
+ ldap_pvt_put_control( tr->tr_ctrls[i], ber );
+ ber_printf( ber, "}}" );
+ ldap_controls_free( tr->tr_ctrls );
+ op->o_tmpfree( tr, op->o_tmpmemctx );
+ }
+ ber_printf( ber, "}" );
+ return 0;
+}
+
+int txn_end_extop(
+ Operation *op, SlapReply *rs )
+{
+ int rc;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len;
+ ber_int_t commit=1;
+ struct berval txnid;
+ Operation *o, *p;
+ Connection *c = op->o_conn;
+
+ Debug( LDAP_DEBUG_STATS, "%s TXN END\n",
+ op->o_log_prefix );
+
+ if( op->ore_reqdata == NULL ) {
+ rs->sr_text = "request data expected";
+ return LDAP_PROTOCOL_ERROR;
+ }
+ if( op->ore_reqdata->bv_len == 0 ) {
+ rs->sr_text = "empty request data";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ op->o_bd = c->c_authz_backend;
+ if( backend_check_restrictions( op, rs,
+ (struct berval *)&slap_EXOP_TXN_END ) != LDAP_SUCCESS )
+ {
+ return rs->sr_err;
+ }
+
+ ber_init2( ber, op->ore_reqdata, 0 );
+
+ tag = ber_scanf( ber, "{" /*}*/ );
+ if( tag == LBER_ERROR ) {
+ rs->sr_text = "request data decoding error";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if( tag == LBER_BOOLEAN ) {
+ tag = ber_scanf( ber, "b", &commit );
+ if( tag == LBER_ERROR ) {
+ rs->sr_text = "request data decoding error";
+ return LDAP_PROTOCOL_ERROR;
+ }
+ }
+
+ tag = ber_scanf( ber, /*{*/ "m}", &txnid );
+ if( tag == LBER_ERROR ) {
+ rs->sr_text = "request data decoding error";
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ if( txnid.bv_len ) {
+ rs->sr_text = "invalid transaction identifier";
+ return LDAP_TXN_ID_INVALID;
+ }
+
+ /* acquire connection lock */
+ ldap_pvt_thread_mutex_lock( &c->c_mutex );
+
+ if( c->c_txn != CONN_TXN_SPECIFY ) {
+ rs->sr_text = "invalid transaction identifier";
+ rc = LDAP_TXN_ID_INVALID;
+ goto done;
+ }
+ c->c_txn = CONN_TXN_SETTLE;
+
+ if( commit ) {
+ slap_callback cb = {0};
+ OpExtra *txn = NULL;
+ if ( op->o_abandon ) {
+ goto drain;
+ }
+
+ if( LDAP_STAILQ_EMPTY(&c->c_txn_ops) ) {
+ /* no updates to commit */
+ rs->sr_text = "no updates to commit";
+ rc = LDAP_OPERATIONS_ERROR;
+ goto settled;
+ }
+
+ cb.sc_response = txn_result;
+ LDAP_STAILQ_FOREACH( o, &c->c_txn_ops, o_next ) {
+ o->o_bd = c->c_txn_backend;
+ p = o;
+ if ( !txn ) {
+ rc = o->o_bd->bd_info->bi_op_txn(o, SLAP_TXN_BEGIN, &txn );
+ if ( rc ) {
+ rs->sr_text = "couldn't start DB transaction";
+ rc = LDAP_OTHER;
+ goto drain;
+ }
+ } else {
+ LDAP_SLIST_INSERT_HEAD( &o->o_extra, txn, oe_next );
+ }
+ cb.sc_next = o->o_callback;
+ o->o_callback = &cb;
+ {
+ SlapReply rs = {REP_RESULT};
+ int opidx = slap_req2op( o->o_tag );
+ assert( opidx != SLAP_OP_LAST );
+ o->o_threadctx = op->o_threadctx;
+ o->o_tid = op->o_tid;
+ ldap_pvt_thread_mutex_unlock( &c->c_mutex );
+ rc = (&o->o_bd->bd_info->bi_op_bind)[opidx]( o, &rs );
+ ldap_pvt_thread_mutex_lock( &c->c_mutex );
+ }
+ if ( rc ) {
+ struct berval *bv = NULL;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+
+ ber_init_w_nullc( ber, LBER_USE_DER );
+ ber_printf( ber, "{i", o->o_msgid );
+ if ( cb.sc_private )
+ txn_put_ctrls( op, ber, cb.sc_private );
+ ber_printf( ber, "}" );
+ ber_flatten( ber, &bv );
+ ber_free_buf( ber );
+ rs->sr_rspdata = bv;
+ o->o_bd->bd_info->bi_op_txn(o, SLAP_TXN_ABORT, &txn );
+ goto drain;
+ }
+ }
+ if ( cb.sc_private ) {
+ struct berval *bv = NULL;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+
+ ber_init_w_nullc( ber, LBER_USE_DER );
+ ber_printf( ber, "{" );
+ txn_put_ctrls( op, ber, cb.sc_private );
+ ber_printf( ber, "}" );
+ ber_flatten( ber, &bv );
+ ber_free_buf( ber );
+ rs->sr_rspdata = bv;
+ }
+ o = p;
+ rc = o->o_bd->bd_info->bi_op_txn(o, SLAP_TXN_COMMIT, &txn );
+ if ( rc ) {
+ rs->sr_text = "transaction commit failed";
+ rc = LDAP_OTHER;
+ }
+ } else {
+ rs->sr_text = "transaction aborted";
+ rc = LDAP_SUCCESS;
+ }
+
+drain:
+ /* drain txn ops list */
+ while (( o = LDAP_STAILQ_FIRST( &c->c_txn_ops )) != NULL ) {
+ LDAP_STAILQ_REMOVE_HEAD( &c->c_txn_ops, o_next );
+ LDAP_STAILQ_NEXT( o, o_next ) = NULL;
+ slap_op_free( o, NULL );
+ }
+
+settled:
+ assert( LDAP_STAILQ_EMPTY(&c->c_txn_ops) );
+ assert( c->c_txn == CONN_TXN_SETTLE );
+ c->c_txn = CONN_TXN_INACTIVE;
+ c->c_txn_backend = NULL;
+
+done:
+ /* release connection lock */
+ ldap_pvt_thread_mutex_unlock( &c->c_mutex );
+
+ return rc;
+}
+
+int txn_preop( Operation *op, SlapReply *rs )
+{
+ /* acquire connection lock */
+ ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
+ if( op->o_conn->c_txn == CONN_TXN_INACTIVE ) {
+ rs->sr_text = "invalid transaction identifier";
+ rs->sr_err = LDAP_TXN_ID_INVALID;
+ goto txnReturn;
+ }
+
+ if( op->o_conn->c_txn_backend == NULL ) {
+ op->o_conn->c_txn_backend = op->o_bd;
+
+ } else if( op->o_conn->c_txn_backend != op->o_bd ) {
+ rs->sr_text = "transaction cannot span multiple database contexts";
+ rs->sr_err = LDAP_AFFECTS_MULTIPLE_DSAS;
+ goto txnReturn;
+ }
+
+ if ( !SLAP_TXNS( op->o_bd )) {
+ rs->sr_text = "backend doesn't support transactions";
+ rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+ goto txnReturn;
+ }
+
+ /* insert operation into transaction */
+ LDAP_STAILQ_REMOVE( &op->o_conn->c_ops, op, Operation, o_next );
+ LDAP_STAILQ_INSERT_TAIL( &op->o_conn->c_txn_ops, op, o_next );
+
+txnReturn:
+ /* release connection lock */
+ ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
+
+ if ( op->o_tag != LDAP_REQ_EXTENDED )
+ send_ldap_result( op, rs );
+ if ( !rs->sr_err )
+ rs->sr_err = LDAP_TXN_SPECIFY_OKAY;
+ return rs->sr_err;
+}
diff --git a/servers/slapd/unbind.c b/servers/slapd/unbind.c
new file mode 100644
index 0000000..7995ac2
--- /dev/null
+++ b/servers/slapd/unbind.c
@@ -0,0 +1,61 @@
+/* unbind.c - decode an ldap unbind operation and pass it to a backend db */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ *
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+
+#include "slap.h"
+
+int
+do_unbind( Operation *op, SlapReply *rs )
+{
+ Debug( LDAP_DEBUG_TRACE, "%s do_unbind\n",
+ op->o_log_prefix );
+
+ /*
+ * Parse the unbind request. It looks like this:
+ *
+ * UnBindRequest ::= NULL
+ */
+
+ Debug( LDAP_DEBUG_STATS, "%s UNBIND\n", op->o_log_prefix );
+
+ if ( frontendDB->be_unbind ) {
+ op->o_bd = frontendDB;
+ (void)frontendDB->be_unbind( op, rs );
+ op->o_bd = NULL;
+ }
+
+ /* pass the unbind to all backends */
+ (void)backend_unbind( op, rs );
+
+ return 0;
+}
+
diff --git a/servers/slapd/user.c b/servers/slapd/user.c
new file mode 100644
index 0000000..8adee4f
--- /dev/null
+++ b/servers/slapd/user.c
@@ -0,0 +1,171 @@
+/* user.c - set user id, group id and group access list */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * Portions Copyright 1999 PM Lashley.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#if defined(HAVE_SETUID) && defined(HAVE_SETGID)
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#include <ac/ctype.h>
+#include <ac/unistd.h>
+
+#include "slap.h"
+#include "lutil.h"
+
+/*
+ * Set real and effective user id and group id, and group access list
+ */
+
+void
+slap_init_user( char *user, char *group )
+{
+ uid_t uid = 0;
+ gid_t gid = 0;
+ int got_uid = 0, got_gid = 0;
+
+ if ( user ) {
+ struct passwd *pwd;
+ if ( isdigit( (unsigned char) *user ) ) {
+ unsigned u;
+
+ got_uid = 1;
+ if ( lutil_atou( &u, user ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "Unble to parse user %s\n",
+ user );
+
+ exit( EXIT_FAILURE );
+ }
+ uid = (uid_t)u;
+#ifdef HAVE_GETPWUID
+ pwd = getpwuid( uid );
+ goto did_getpw;
+#else
+ user = NULL;
+#endif
+ } else {
+ pwd = getpwnam( user );
+ did_getpw:
+ if ( pwd == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "No passwd entry for user %s\n",
+ user );
+
+ exit( EXIT_FAILURE );
+ }
+ if ( got_uid ) {
+ user = (pwd != NULL ? pwd->pw_name : NULL);
+ } else {
+ got_uid = 1;
+ uid = pwd->pw_uid;
+ }
+ got_gid = 1;
+ gid = pwd->pw_gid;
+#ifdef HAVE_ENDPWENT
+ endpwent();
+#endif
+ }
+ }
+
+ if ( group ) {
+ struct group *grp;
+ if ( isdigit( (unsigned char) *group )) {
+ unsigned g;
+
+ if ( lutil_atou( &g, group ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "Unble to parse group %s\n",
+ group );
+
+ exit( EXIT_FAILURE );
+ }
+ gid = (uid_t)g;
+#ifdef HAVE_GETGRGID
+ grp = getgrgid( gid );
+ goto did_group;
+#endif
+ } else {
+ grp = getgrnam( group );
+ if ( grp != NULL )
+ gid = grp->gr_gid;
+ did_group:
+ if ( grp == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "No group entry for group %s\n",
+ group );
+
+ exit( EXIT_FAILURE );
+ }
+ }
+ got_gid = 1;
+ }
+
+ if ( user ) {
+ if ( getuid() == 0 && initgroups( user, gid ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "Could not set the group access (gid) list\n" );
+
+ exit( EXIT_FAILURE );
+ }
+ }
+
+#ifdef HAVE_ENDGRENT
+ endgrent();
+#endif
+
+ if ( got_gid ) {
+ if ( setgid( gid ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "Could not set real group id to %d\n",
+ (int) gid );
+
+ exit( EXIT_FAILURE );
+ }
+#ifdef HAVE_SETEGID
+ if ( setegid( gid ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "Could not set effective group id to %d\n",
+ (int) gid );
+
+ exit( EXIT_FAILURE );
+ }
+#endif
+ }
+
+ if ( got_uid ) {
+ if ( setuid( uid ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "Could not set real user id to %d\n",
+ (int) uid );
+
+ exit( EXIT_FAILURE );
+ }
+#ifdef HAVE_SETEUID
+ if ( seteuid( uid ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "Could not set effective user id to %d\n",
+ (int) uid );
+
+ exit( EXIT_FAILURE );
+ }
+#endif
+ }
+}
+
+#endif /* HAVE_PWD_H && HAVE_GRP_H */
diff --git a/servers/slapd/value.c b/servers/slapd/value.c
new file mode 100644
index 0000000..0f0cffc
--- /dev/null
+++ b/servers/slapd/value.c
@@ -0,0 +1,798 @@
+/* value.c - routines for dealing with values */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/*
+ * Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include <sys/stat.h>
+
+#include "slap.h"
+
+int
+value_add(
+ BerVarray *vals,
+ BerVarray addvals )
+{
+ int n, nn = 0;
+ BerVarray v2;
+
+ if ( addvals != NULL ) {
+ for ( ; !BER_BVISNULL( &addvals[nn] ); nn++ )
+ ; /* NULL */
+ }
+
+ if ( *vals == NULL ) {
+ *vals = (BerVarray) SLAP_MALLOC( (nn + 1)
+ * sizeof(struct berval) );
+ if( *vals == NULL ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "value_add: SLAP_MALLOC failed.\n" );
+ return LBER_ERROR_MEMORY;
+ }
+ n = 0;
+
+ } else {
+ for ( n = 0; !BER_BVISNULL( &(*vals)[n] ); n++ ) {
+ ; /* Empty */
+ }
+ *vals = (BerVarray) SLAP_REALLOC( (char *) *vals,
+ (n + nn + 1) * sizeof(struct berval) );
+ if( *vals == NULL ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "value_add: SLAP_MALLOC failed.\n" );
+ return LBER_ERROR_MEMORY;
+ }
+ }
+
+ v2 = &(*vals)[n];
+ for ( n = 0 ; n < nn; v2++, addvals++ ) {
+ ber_dupbv( v2, addvals );
+ if ( BER_BVISNULL( v2 ) ) break;
+ }
+ BER_BVZERO( v2 );
+
+ return LDAP_SUCCESS;
+}
+
+int
+value_add_one(
+ BerVarray *vals,
+ struct berval *addval )
+{
+ int n;
+ BerVarray v2;
+
+ if ( *vals == NULL ) {
+ *vals = (BerVarray) SLAP_MALLOC( 2 * sizeof(struct berval) );
+ if( *vals == NULL ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "value_add_one: SLAP_MALLOC failed.\n" );
+ return LBER_ERROR_MEMORY;
+ }
+ n = 0;
+
+ } else {
+ for ( n = 0; !BER_BVISNULL( &(*vals)[n] ); n++ ) {
+ ; /* Empty */
+ }
+ *vals = (BerVarray) SLAP_REALLOC( (char *) *vals,
+ (n + 2) * sizeof(struct berval) );
+ if( *vals == NULL ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "value_add_one: SLAP_MALLOC failed.\n" );
+ return LBER_ERROR_MEMORY;
+ }
+ }
+
+ v2 = &(*vals)[n];
+ ber_dupbv(v2, addval);
+
+ v2++;
+ BER_BVZERO( v2 );
+
+ return LDAP_SUCCESS;
+}
+
+int asserted_value_validate_normalize(
+ AttributeDescription *ad,
+ MatchingRule *mr,
+ unsigned usage,
+ struct berval *in,
+ struct berval *out,
+ const char ** text,
+ void *ctx )
+{
+ int rc;
+ struct berval pval;
+ pval.bv_val = NULL;
+
+ /* we expect the value to be in the assertion syntax */
+ assert( !SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX(usage) );
+
+ if( mr == NULL ) {
+ *text = "inappropriate matching request";
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+
+ if( !mr->smr_match ) {
+ *text = "requested matching rule not supported";
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+
+ if( mr->smr_syntax->ssyn_pretty ) {
+ rc = (mr->smr_syntax->ssyn_pretty)( mr->smr_syntax, in, &pval, ctx );
+ in = &pval;
+
+ } else if ( mr->smr_syntax->ssyn_validate ) {
+ rc = (mr->smr_syntax->ssyn_validate)( mr->smr_syntax, in );
+
+ } else {
+ *text = "inappropriate matching request";
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ *text = "value does not conform to assertion syntax";
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if( mr->smr_normalize ) {
+ rc = (mr->smr_normalize)(
+ usage|SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
+ ad ? ad->ad_type->sat_syntax : NULL,
+ mr, in, out, ctx );
+
+ if( pval.bv_val ) ber_memfree_x( pval.bv_val, ctx );
+
+ if( rc != LDAP_SUCCESS ) {
+ *text = "unable to normalize value for matching";
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ } else if ( pval.bv_val != NULL ) {
+ *out = pval;
+
+ } else {
+ ber_dupbv_x( out, in, ctx );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int
+value_match(
+ int *match,
+ AttributeDescription *ad,
+ MatchingRule *mr,
+ unsigned flags,
+ struct berval *v1, /* stored value */
+ void *v2, /* assertion */
+ const char ** text )
+{
+ int rc;
+
+ assert( mr != NULL );
+
+ if( !mr->smr_match ) {
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+
+ rc = (mr->smr_match)( match, flags,
+ ad->ad_type->sat_syntax, mr, v1, v2 );
+
+ return rc;
+}
+
+int value_find_ex(
+ AttributeDescription *ad,
+ unsigned flags,
+ BerVarray vals,
+ struct berval *val,
+ void *ctx )
+{
+ int i;
+ int rc;
+ struct berval nval = BER_BVNULL;
+ MatchingRule *mr = ad->ad_type->sat_equality;
+
+ if( mr == NULL || !mr->smr_match ) {
+ return LDAP_INAPPROPRIATE_MATCHING;
+ }
+
+ assert( SLAP_IS_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH( flags ) != 0 );
+
+ if( !SLAP_IS_MR_ASSERTED_VALUE_NORMALIZED_MATCH( flags ) &&
+ mr->smr_normalize )
+ {
+ rc = (mr->smr_normalize)(
+ flags & (SLAP_MR_TYPE_MASK|SLAP_MR_SUBTYPE_MASK|SLAP_MR_VALUE_OF_SYNTAX),
+ ad->ad_type->sat_syntax,
+ mr, val, &nval, ctx );
+
+ if( rc != LDAP_SUCCESS ) {
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ for ( i = 0; vals[i].bv_val != NULL; i++ ) {
+ int match;
+ const char *text;
+
+ rc = value_match( &match, ad, mr, flags,
+ &vals[i], nval.bv_val == NULL ? val : &nval, &text );
+
+ if( rc == LDAP_SUCCESS && match == 0 ) {
+ slap_sl_free( nval.bv_val, ctx );
+ return rc;
+ }
+ }
+
+ slap_sl_free( nval.bv_val, ctx );
+ return LDAP_NO_SUCH_ATTRIBUTE;
+}
+
+/* assign new indexes to an attribute's ordered values */
+void
+ordered_value_renumber( Attribute *a )
+{
+ char *ptr, ibuf[64]; /* many digits */
+ struct berval ibv, tmp, vtmp;
+ unsigned i;
+
+ ibv.bv_val = ibuf;
+
+ for (i=0; i<a->a_numvals; i++) {
+ ibv.bv_len = sprintf(ibv.bv_val, "{%u}", i);
+ vtmp = a->a_vals[i];
+ if ( vtmp.bv_val[0] == '{' ) {
+ ptr = ber_bvchr(&vtmp, '}');
+ assert( ptr != NULL );
+ ++ptr;
+ vtmp.bv_len -= ptr - vtmp.bv_val;
+ vtmp.bv_val = ptr;
+ }
+ tmp.bv_len = ibv.bv_len + vtmp.bv_len;
+ tmp.bv_val = ch_malloc( tmp.bv_len + 1 );
+ strcpy( tmp.bv_val, ibv.bv_val );
+ AC_MEMCPY( tmp.bv_val + ibv.bv_len, vtmp.bv_val, vtmp.bv_len );
+ tmp.bv_val[tmp.bv_len] = '\0';
+ ch_free( a->a_vals[i].bv_val );
+ a->a_vals[i] = tmp;
+
+ if ( a->a_nvals && a->a_nvals != a->a_vals ) {
+ vtmp = a->a_nvals[i];
+ if ( vtmp.bv_val[0] == '{' ) {
+ ptr = ber_bvchr(&vtmp, '}');
+ assert( ptr != NULL );
+ ++ptr;
+ vtmp.bv_len -= ptr - vtmp.bv_val;
+ vtmp.bv_val = ptr;
+ }
+ tmp.bv_len = ibv.bv_len + vtmp.bv_len;
+ tmp.bv_val = ch_malloc( tmp.bv_len + 1 );
+ strcpy( tmp.bv_val, ibv.bv_val );
+ AC_MEMCPY( tmp.bv_val + ibv.bv_len, vtmp.bv_val, vtmp.bv_len );
+ tmp.bv_val[tmp.bv_len] = '\0';
+ ch_free( a->a_nvals[i].bv_val );
+ a->a_nvals[i] = tmp;
+ }
+ }
+}
+
+/* Sort the values in an X-ORDERED VALUES attribute.
+ * If the values have no index, index them in their given order.
+ * If the values have indexes, sort them.
+ * If some are indexed and some are not, return Error.
+ */
+int
+ordered_value_sort( Attribute *a, int do_renumber )
+{
+ int i, vals;
+ int index = 0, noindex = 0, renumber = 0, gotnvals = 0;
+ struct berval tmp;
+
+ if ( a->a_nvals && a->a_nvals != a->a_vals )
+ gotnvals = 1;
+
+ /* count attrs, look for index */
+ for (i=0; a->a_vals[i].bv_val; i++) {
+ if ( a->a_vals[i].bv_val[0] == '{' ) {
+ char *ptr;
+ index = 1;
+ ptr = ber_bvchr( &a->a_vals[i], '}' );
+ if ( !ptr )
+ return LDAP_INVALID_SYNTAX;
+ if ( noindex )
+ return LDAP_INVALID_SYNTAX;
+ } else {
+ noindex = 1;
+ if ( index )
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+ vals = i;
+
+ /* If values have indexes, sort the values */
+ if ( index ) {
+ int *indexes, j, idx;
+ struct berval ntmp;
+
+#if 0
+ /* Strip index from normalized values */
+ if ( !a->a_nvals || a->a_vals == a->a_nvals ) {
+ a->a_nvals = ch_malloc( (vals+1)*sizeof(struct berval));
+ BER_BVZERO(a->a_nvals+vals);
+ for ( i=0; i<vals; i++ ) {
+ char *ptr = ber_bvchr(&a->a_vals[i], '}') + 1;
+ a->a_nvals[i].bv_len = a->a_vals[i].bv_len -
+ (ptr - a->a_vals[i].bv_val);
+ a->a_nvals[i].bv_val = ch_malloc( a->a_nvals[i].bv_len + 1);
+ strcpy(a->a_nvals[i].bv_val, ptr );
+ }
+ } else {
+ for ( i=0; i<vals; i++ ) {
+ char *ptr = ber_bvchr(&a->a_nvals[i], '}') + 1;
+ a->a_nvals[i].bv_len -= ptr - a->a_nvals[i].bv_val;
+ strcpy(a->a_nvals[i].bv_val, ptr);
+ }
+ }
+#endif
+
+ indexes = ch_malloc( vals * sizeof(int) );
+ for ( i=0; i<vals; i++) {
+ char *ptr;
+ indexes[i] = strtol(a->a_vals[i].bv_val+1, &ptr, 0);
+ if ( *ptr != '}' ) {
+ ch_free( indexes );
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+
+ /* Insertion sort */
+ for ( i=1; i<vals; i++ ) {
+ idx = indexes[i];
+ tmp = a->a_vals[i];
+ if ( gotnvals ) ntmp = a->a_nvals[i];
+ j = i;
+ while ((j > 0) && (indexes[j-1] > idx)) {
+ indexes[j] = indexes[j-1];
+ a->a_vals[j] = a->a_vals[j-1];
+ if ( gotnvals ) a->a_nvals[j] = a->a_nvals[j-1];
+ j--;
+ }
+ indexes[j] = idx;
+ a->a_vals[j] = tmp;
+ if ( gotnvals ) a->a_nvals[j] = ntmp;
+ }
+
+ /* If range is not contiguous, must renumber */
+ if ( indexes[0] != 0 || indexes[vals-1] != vals-1 ) {
+ renumber = 1;
+ }
+ ch_free( indexes );
+ } else {
+ renumber = 1;
+ }
+
+ if ( do_renumber && renumber )
+ ordered_value_renumber( a );
+
+ return 0;
+}
+
+/*
+ * wrapper for validate function
+ * uses the validate function of the syntax after removing
+ * the index, if allowed and present
+ */
+int
+ordered_value_validate(
+ AttributeDescription *ad,
+ struct berval *in,
+ int mop )
+{
+ struct berval bv = *in;
+
+ assert( ad->ad_type->sat_syntax != NULL );
+ assert( ad->ad_type->sat_syntax->ssyn_validate != NULL );
+
+ if ( ad->ad_type->sat_flags & SLAP_AT_ORDERED ) {
+
+ /* Skip past the assertion index */
+ if ( bv.bv_val[0] == '{' ) {
+ char *ptr;
+
+ ptr = ber_bvchr( &bv, '}' );
+ if ( ptr != NULL ) {
+ struct berval ns;
+
+ ns.bv_val = bv.bv_val + 1;
+ ns.bv_len = ptr - ns.bv_val;
+
+ if ( numericStringValidate( NULL, &ns ) == LDAP_SUCCESS ) {
+ ptr++;
+ bv.bv_len -= ptr - bv.bv_val;
+ bv.bv_val = ptr;
+ in = &bv;
+ /* If deleting by index, just succeed */
+ if ( mop == LDAP_MOD_DELETE && BER_BVISEMPTY( &bv ) ) {
+ return LDAP_SUCCESS;
+ }
+ }
+ }
+ }
+ }
+
+ return ad->ad_type->sat_syntax->ssyn_validate( ad->ad_type->sat_syntax, in );
+}
+
+/*
+ * wrapper for pretty function
+ * uses the pretty function of the syntax after removing
+ * the index, if allowed and present; in case, it's prepended
+ * to the pretty value
+ */
+int
+ordered_value_pretty(
+ AttributeDescription *ad,
+ struct berval *val,
+ struct berval *out,
+ void *ctx )
+{
+ struct berval bv,
+ idx = BER_BVNULL;
+ int rc;
+
+ assert( ad->ad_type->sat_syntax != NULL );
+ assert( ad->ad_type->sat_syntax->ssyn_pretty != NULL );
+ assert( val != NULL );
+ assert( out != NULL );
+
+ bv = *val;
+
+ if ( ad->ad_type->sat_flags & SLAP_AT_ORDERED ) {
+
+ /* Skip past the assertion index */
+ if ( bv.bv_val[0] == '{' ) {
+ char *ptr;
+
+ ptr = ber_bvchr( &bv, '}' );
+ if ( ptr != NULL ) {
+ struct berval ns;
+
+ ns.bv_val = bv.bv_val + 1;
+ ns.bv_len = ptr - ns.bv_val;
+
+ if ( numericStringValidate( NULL, &ns ) == LDAP_SUCCESS ) {
+ ptr++;
+
+ idx = bv;
+ idx.bv_len = ptr - bv.bv_val;
+
+ bv.bv_len -= idx.bv_len;
+ bv.bv_val = ptr;
+
+ val = &bv;
+ }
+ }
+ }
+ }
+
+ rc = ad->ad_type->sat_syntax->ssyn_pretty( ad->ad_type->sat_syntax, val, out, ctx );
+
+ if ( rc == LDAP_SUCCESS && !BER_BVISNULL( &idx ) ) {
+ bv = *out;
+
+ out->bv_len = idx.bv_len + bv.bv_len;
+ out->bv_val = slap_sl_malloc( out->bv_len + 1, ctx );
+
+ AC_MEMCPY( out->bv_val, idx.bv_val, idx.bv_len );
+ AC_MEMCPY( &out->bv_val[ idx.bv_len ], bv.bv_val, bv.bv_len + 1 );
+
+ ber_memfree_x( bv.bv_val, ctx );
+ }
+
+ return rc;
+}
+
+/*
+ * wrapper for normalize function
+ * uses the normalize function of the attribute description equality rule
+ * after removing the index, if allowed and present; in case, it's
+ * prepended to the value
+ */
+int
+ordered_value_normalize(
+ slap_mask_t usage,
+ AttributeDescription *ad,
+ MatchingRule *mr,
+ struct berval *val,
+ struct berval *normalized,
+ void *ctx )
+{
+ struct berval bv,
+ idx = BER_BVNULL;
+ int rc;
+
+ assert( ad->ad_type->sat_equality != NULL );
+ assert( ad->ad_type->sat_equality->smr_normalize != NULL );
+ assert( val != NULL );
+ assert( normalized != NULL );
+
+ bv = *val;
+
+ if ( ad->ad_type->sat_flags & SLAP_AT_ORDERED ) {
+
+ /* Skip past the assertion index */
+ if ( bv.bv_val[ 0 ] == '{' ) {
+ char *ptr;
+
+ ptr = ber_bvchr( &bv, '}' );
+ if ( ptr != NULL ) {
+ struct berval ns;
+
+ ns.bv_val = bv.bv_val + 1;
+ ns.bv_len = ptr - ns.bv_val;
+
+ if ( numericStringValidate( NULL, &ns ) == LDAP_SUCCESS ) {
+ ptr++;
+
+ idx = bv;
+ idx.bv_len = ptr - bv.bv_val;
+
+ bv.bv_len -= idx.bv_len;
+ bv.bv_val = ptr;
+
+ /* validator will already prevent this for Adds */
+ if ( BER_BVISEMPTY( &bv )) {
+ ber_dupbv_x( normalized, &idx, ctx );
+ return LDAP_SUCCESS;
+ }
+ val = &bv;
+ }
+ }
+ }
+ }
+
+ rc = ad->ad_type->sat_equality->smr_normalize( usage,
+ ad->ad_type->sat_syntax, mr, val, normalized, ctx );
+
+ if ( rc == LDAP_SUCCESS && !BER_BVISNULL( &idx ) ) {
+ bv = *normalized;
+
+ normalized->bv_len = idx.bv_len + bv.bv_len;
+ normalized->bv_val = slap_sl_malloc( normalized->bv_len + 1, ctx );
+
+ AC_MEMCPY( normalized->bv_val, idx.bv_val, idx.bv_len );
+ AC_MEMCPY( &normalized->bv_val[ idx.bv_len ], bv.bv_val, bv.bv_len + 1 );
+
+ ber_memfree_x( bv.bv_val, ctx );
+ }
+
+ return rc;
+}
+
+/* A wrapper for value match, handles Equality matches for attributes
+ * with ordered values.
+ */
+int
+ordered_value_match(
+ int *match,
+ AttributeDescription *ad,
+ MatchingRule *mr,
+ unsigned flags,
+ struct berval *v1, /* stored value */
+ struct berval *v2, /* assertion */
+ const char ** text )
+{
+ struct berval bv1, bv2;
+
+ /* X-ORDERED VALUES equality matching:
+ * If (SLAP_MR_IS_VALUE_OF_ATTRIBUTE_SYNTAX) that means we are
+ * comparing two attribute values. In this case, we want to ignore
+ * the ordering index of both values, we just want to know if their
+ * main values are equal.
+ *
+ * If (SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX) then we are comparing
+ * an assertion against an attribute value.
+ * If the assertion has no index, the index of the value is ignored.
+ * If the assertion has only an index, the remainder of the value is
+ * ignored.
+ * If the assertion has index and value, both are compared.
+ */
+ if ( ad->ad_type->sat_flags & SLAP_AT_ORDERED ) {
+ char *ptr;
+ struct berval ns1 = BER_BVNULL, ns2 = BER_BVNULL;
+
+ bv1 = *v1;
+ bv2 = *v2;
+
+ /* Skip past the assertion index */
+ if ( bv2.bv_val[0] == '{' ) {
+ ptr = ber_bvchr( &bv2, '}' );
+ if ( ptr != NULL ) {
+ ns2.bv_val = bv2.bv_val + 1;
+ ns2.bv_len = ptr - ns2.bv_val;
+
+ if ( numericStringValidate( NULL, &ns2 ) == LDAP_SUCCESS ) {
+ ptr++;
+ bv2.bv_len -= ptr - bv2.bv_val;
+ bv2.bv_val = ptr;
+ v2 = &bv2;
+ }
+ }
+ }
+
+ /* Skip past the attribute index */
+ if ( bv1.bv_val[0] == '{' ) {
+ ptr = ber_bvchr( &bv1, '}' );
+ if ( ptr != NULL ) {
+ ns1.bv_val = bv1.bv_val + 1;
+ ns1.bv_len = ptr - ns1.bv_val;
+
+ if ( numericStringValidate( NULL, &ns1 ) == LDAP_SUCCESS ) {
+ ptr++;
+ bv1.bv_len -= ptr - bv1.bv_val;
+ bv1.bv_val = ptr;
+ v1 = &bv1;
+ }
+ }
+ }
+
+ if ( SLAP_MR_IS_VALUE_OF_ASSERTION_SYNTAX( flags )) {
+ if ( !BER_BVISNULL( &ns2 ) && !BER_BVISNULL( &ns1 ) ) {
+ /* compare index values first */
+ (void)octetStringOrderingMatch( match, 0, NULL, NULL, &ns1, &ns2 );
+
+ /* If not equal, or we're only comparing the index,
+ * return result now.
+ */
+ if ( *match != 0 || BER_BVISEMPTY( &bv2 ) ) {
+ return LDAP_SUCCESS;
+ }
+ }
+ }
+
+ }
+
+ if ( !mr || !mr->smr_match ) {
+ *match = ber_bvcmp( v1, v2 );
+ return LDAP_SUCCESS;
+ }
+
+ return value_match( match, ad, mr, flags, v1, v2, text );
+}
+
+int
+ordered_value_add(
+ Entry *e,
+ AttributeDescription *ad,
+ Attribute *a,
+ BerVarray vals,
+ BerVarray nvals
+)
+{
+ int i, j, k, anum, vnum;
+ BerVarray new, nnew = NULL;
+
+ /* count new vals */
+ for (i=0; !BER_BVISNULL( vals+i ); i++) ;
+ vnum = i;
+
+ if ( a ) {
+ ordered_value_sort( a, 0 );
+ } else {
+ Attribute **ap;
+ for ( ap=&e->e_attrs; *ap; ap = &(*ap)->a_next ) ;
+ a = attr_alloc( ad );
+ *ap = a;
+ }
+ anum = a->a_numvals;
+
+ new = ch_malloc( (anum+vnum+1) * sizeof(struct berval));
+
+ /* sanity check: if normalized modifications come in, either
+ * no values are present or normalized existing values differ
+ * from non-normalized; if no normalized modifications come in,
+ * either no values are present or normalized existing values
+ * don't differ from non-normalized */
+ if ( nvals != NULL ) {
+ assert( nvals != vals );
+ assert( a->a_nvals == NULL || a->a_nvals != a->a_vals );
+
+ } else {
+ assert( a->a_nvals == NULL || a->a_nvals == a->a_vals );
+ }
+
+ if ( ( a->a_nvals && a->a_nvals != a->a_vals ) || nvals != NULL ) {
+ nnew = ch_malloc( (anum+vnum+1) * sizeof(struct berval));
+ /* Shouldn't happen... */
+ if ( !nvals ) nvals = vals;
+ }
+ if ( anum ) {
+ AC_MEMCPY( new, a->a_vals, anum * sizeof(struct berval));
+ if ( nnew && a->a_nvals )
+ AC_MEMCPY( nnew, a->a_nvals, anum * sizeof(struct berval));
+ }
+
+ for (i=0; i<vnum; i++) {
+ char *next;
+
+ k = -1;
+ if ( vals[i].bv_val[0] == '{' ) {
+ /* FIXME: strtol() could go past end... */
+ k = strtol( vals[i].bv_val + 1, &next, 0 );
+ if ( next == vals[i].bv_val + 1 ||
+ next[ 0 ] != '}' ||
+ (ber_len_t) (next - vals[i].bv_val) > vals[i].bv_len )
+ {
+ ch_free( nnew );
+ ch_free( new );
+ return -1;
+ }
+ if ( k > anum ) k = -1;
+ }
+ /* No index, or index is greater than current number of
+ * values, just tack onto the end
+ */
+ if ( k < 0 ) {
+ ber_dupbv( new+anum, vals+i );
+ if ( nnew ) ber_dupbv( nnew+anum, nvals+i );
+
+ /* Indexed, push everything else down one and insert */
+ } else {
+ for (j=anum; j>k; j--) {
+ new[j] = new[j-1];
+ if ( nnew ) nnew[j] = nnew[j-1];
+ }
+ ber_dupbv( new+k, vals+i );
+ if ( nnew ) ber_dupbv( nnew+k, nvals+i );
+ }
+ anum++;
+ }
+ BER_BVZERO( new+anum );
+ ch_free( a->a_vals );
+ a->a_vals = new;
+ if ( nnew ) {
+ BER_BVZERO( nnew+anum );
+ ch_free( a->a_nvals );
+ a->a_nvals = nnew;
+ } else {
+ a->a_nvals = a->a_vals;
+ }
+
+ a->a_numvals = anum;
+ ordered_value_renumber( a );
+
+ return 0;
+}
diff --git a/servers/slapd/zn_malloc.c b/servers/slapd/zn_malloc.c
new file mode 100644
index 0000000..e2cdc21
--- /dev/null
+++ b/servers/slapd/zn_malloc.c
@@ -0,0 +1,970 @@
+/* zn_malloc.c - zone-based malloc routines */
+/* $OpenLDAP$*/
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright 2004 IBM Corporation
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ */
+/* ACKNOWLEDGEMENTS
+ * This work originally developed by Jong-Hyuk Choi for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "slap.h"
+
+#ifdef SLAP_ZONE_ALLOC
+
+#include <sys/mman.h>
+
+static int slap_zone_cmp(const void *v1, const void *v2);
+void * slap_replenish_zopool(void *ctx);
+
+static void
+slap_zo_release(void *data)
+{
+ struct zone_object *zo = (struct zone_object *)data;
+ ch_free( zo );
+}
+
+void
+slap_zn_mem_destroy(
+ void *ctx
+)
+{
+ struct zone_heap *zh = ctx;
+ int pad = 2*sizeof(int)-1, pad_shift;
+ int order_start = -1, i, j;
+ struct zone_object *zo;
+
+ pad_shift = pad - 1;
+ do {
+ order_start++;
+ } while (pad_shift >>= 1);
+
+ ldap_pvt_thread_mutex_lock( &zh->zh_mutex );
+ for (i = 0; i < zh->zh_zoneorder - order_start + 1; i++) {
+ zo = LDAP_LIST_FIRST(&zh->zh_free[i]);
+ while (zo) {
+ struct zone_object *zo_tmp = zo;
+ zo = LDAP_LIST_NEXT(zo, zo_link);
+ LDAP_LIST_REMOVE(zo_tmp, zo_link);
+ LDAP_LIST_INSERT_HEAD(&zh->zh_zopool, zo_tmp, zo_link);
+ }
+ }
+ ch_free(zh->zh_free);
+
+ for (i = 0; i < zh->zh_numzones; i++) {
+ for (j = 0; j < zh->zh_zoneorder - order_start + 1; j++) {
+ ch_free(zh->zh_maps[i][j]);
+ }
+ ch_free(zh->zh_maps[i]);
+ munmap(zh->zh_zones[i], zh->zh_zonesize);
+ ldap_pvt_thread_rdwr_destroy(&zh->zh_znlock[i]);
+ }
+ ch_free(zh->zh_maps);
+ ch_free(zh->zh_zones);
+ ch_free(zh->zh_seqno);
+ ch_free(zh->zh_znlock);
+
+ ldap_avl_free(zh->zh_zonetree, slap_zo_release);
+
+ zo = LDAP_LIST_FIRST(&zh->zh_zopool);
+ while (zo) {
+ struct zone_object *zo_tmp = zo;
+ zo = LDAP_LIST_NEXT(zo, zo_link);
+ if (!zo_tmp->zo_blockhead) {
+ LDAP_LIST_REMOVE(zo_tmp, zo_link);
+ }
+ }
+ zo = LDAP_LIST_FIRST(&zh->zh_zopool);
+ while (zo) {
+ struct zone_object *zo_tmp = zo;
+ zo = LDAP_LIST_NEXT(zo, zo_link);
+ ch_free(zo_tmp);
+ }
+ ldap_pvt_thread_mutex_unlock(&zh->zh_mutex);
+ ldap_pvt_thread_rdwr_destroy(&zh->zh_lock);
+ ldap_pvt_thread_mutex_destroy(&zh->zh_mutex);
+ ch_free(zh);
+}
+
+void *
+slap_zn_mem_create(
+ ber_len_t initsize,
+ ber_len_t maxsize,
+ ber_len_t deltasize,
+ ber_len_t zonesize
+)
+{
+ struct zone_heap *zh = NULL;
+ ber_len_t zpad;
+ int pad = 2*sizeof(int)-1, pad_shift;
+ int size_shift;
+ int order = -1, order_start = -1, order_end = -1;
+ int i, j;
+ struct zone_object *zo;
+
+ Debug(LDAP_DEBUG_NONE,
+ "--> slap_zn_mem_create: initsize=%d, maxsize=%d\n",
+ initsize, maxsize );
+ Debug(LDAP_DEBUG_NONE,
+ "++> slap_zn_mem_create: deltasize=%d, zonesize=%d\n",
+ deltasize, zonesize );
+
+ zh = (struct zone_heap *)ch_calloc(1, sizeof(struct zone_heap));
+
+ zh->zh_fd = open("/dev/zero", O_RDWR);
+
+ if ( zonesize ) {
+ zh->zh_zonesize = zonesize;
+ } else {
+ zh->zh_zonesize = SLAP_ZONE_SIZE;
+ }
+
+ zpad = zh->zh_zonesize - 1;
+ zh->zh_numzones = ((initsize + zpad) & ~zpad) / zh->zh_zonesize;
+
+ if ( maxsize && maxsize >= initsize ) {
+ zh->zh_maxzones = ((maxsize + zpad) & ~zpad) / zh->zh_zonesize;
+ } else {
+ zh->zh_maxzones = ((initsize + zpad) & ~zpad) / zh->zh_zonesize;
+ }
+
+ if ( deltasize ) {
+ zh->zh_deltazones = ((deltasize + zpad) & ~zpad) / zh->zh_zonesize;
+ } else {
+ zh->zh_deltazones = ((SLAP_ZONE_DELTA+zpad) & ~zpad) / zh->zh_zonesize;
+ }
+
+ size_shift = zh->zh_zonesize - 1;
+ do {
+ order_end++;
+ } while (size_shift >>= 1);
+
+ pad_shift = pad - 1;
+ do {
+ order_start++;
+ } while (pad_shift >>= 1);
+
+ order = order_end - order_start + 1;
+
+ zh->zh_zones = (void **)ch_malloc(zh->zh_maxzones * sizeof(void*));
+ zh->zh_znlock = (ldap_pvt_thread_rdwr_t *)ch_malloc(
+ zh->zh_maxzones * sizeof(ldap_pvt_thread_rdwr_t *));
+ zh->zh_maps = (unsigned char ***)ch_malloc(
+ zh->zh_maxzones * sizeof(unsigned char**));
+
+ zh->zh_zoneorder = order_end;
+ zh->zh_free = (struct zh_freelist *)
+ ch_malloc(order * sizeof(struct zh_freelist));
+ zh->zh_seqno = (unsigned long *)ch_calloc(zh->zh_maxzones,
+ sizeof(unsigned long));
+ for (i = 0; i < order; i++) {
+ LDAP_LIST_INIT(&zh->zh_free[i]);
+ }
+ LDAP_LIST_INIT(&zh->zh_zopool);
+
+ for (i = 0; i < zh->zh_numzones; i++) {
+ zh->zh_zones[i] = mmap(0, zh->zh_zonesize, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, zh->zh_fd, 0);
+ zh->zh_maps[i] = (unsigned char **)
+ ch_malloc(order * sizeof(unsigned char *));
+ for (j = 0; j < order; j++) {
+ int shiftamt = order_start + 1 + j;
+ int nummaps = zh->zh_zonesize >> shiftamt;
+ assert(nummaps);
+ nummaps >>= 3;
+ if (!nummaps) nummaps = 1;
+ zh->zh_maps[i][j] = (unsigned char *)ch_malloc(nummaps);
+ memset(zh->zh_maps[i][j], 0, nummaps);
+ }
+
+ if (LDAP_LIST_EMPTY(&zh->zh_zopool)) {
+ slap_replenish_zopool(zh);
+ }
+ zo = LDAP_LIST_FIRST(&zh->zh_zopool);
+ LDAP_LIST_REMOVE(zo, zo_link);
+ zo->zo_ptr = zh->zh_zones[i];
+ zo->zo_idx = i;
+ LDAP_LIST_INSERT_HEAD(&zh->zh_free[order-1], zo, zo_link);
+
+ if (LDAP_LIST_EMPTY(&zh->zh_zopool)) {
+ slap_replenish_zopool(zh);
+ }
+ zo = LDAP_LIST_FIRST(&zh->zh_zopool);
+ LDAP_LIST_REMOVE(zo, zo_link);
+ zo->zo_ptr = zh->zh_zones[i];
+ zo->zo_siz = zh->zh_zonesize;
+ zo->zo_idx = i;
+ ldap_avl_insert(&zh->zh_zonetree, zo, slap_zone_cmp, ldap_avl_dup_error);
+ ldap_pvt_thread_rdwr_init(&zh->zh_znlock[i]);
+ }
+
+ LDAP_STAILQ_INIT(&zh->zh_latency_history_queue);
+ ldap_pvt_thread_mutex_init(&zh->zh_mutex);
+ ldap_pvt_thread_rdwr_init(&zh->zh_lock);
+
+ return zh;
+}
+
+void *
+slap_zn_malloc(
+ ber_len_t size,
+ void *ctx
+)
+{
+ struct zone_heap *zh = ctx;
+ ber_len_t size_shift;
+ int pad = 2*sizeof(int)-1, pad_shift;
+ int order = -1, order_start = -1;
+ struct zone_object *zo, *zo_new, *zo_left, *zo_right;
+ ber_len_t *ptr, *new;
+ int idx;
+ unsigned long diff;
+ int i, j, k;
+
+ Debug(LDAP_DEBUG_NONE,
+ "--> slap_zn_malloc: size=%d\n", size );
+
+ if (!zh) return ber_memalloc_x(size, NULL);
+
+ /* round up to doubleword boundary */
+ size += 2*sizeof(ber_len_t) + pad;
+ size &= ~pad;
+
+ size_shift = size - 1;
+ do {
+ order++;
+ } while (size_shift >>= 1);
+
+ pad_shift = pad - 1;
+ do {
+ order_start++;
+ } while (pad_shift >>= 1);
+
+retry:
+
+ ldap_pvt_thread_mutex_lock( &zh->zh_mutex );
+ for (i = order; i <= zh->zh_zoneorder &&
+ LDAP_LIST_EMPTY(&zh->zh_free[i-order_start]); i++);
+
+ if (i == order) {
+ zo_new = LDAP_LIST_FIRST(&zh->zh_free[i-order_start]);
+ LDAP_LIST_REMOVE(zo_new, zo_link);
+ ptr = zo_new->zo_ptr;
+ idx = zo_new->zo_idx;
+ diff = (unsigned long)((char*)ptr -
+ (char*)zh->zh_zones[idx]) >> (order + 1);
+ zh->zh_maps[idx][order-order_start][diff>>3] |= (1 << (diff & 0x7));
+ *ptr++ = zh->zh_seqno[idx];
+ *ptr++ = size - 2*sizeof(ber_len_t);
+ zo_new->zo_ptr = NULL;
+ zo_new->zo_idx = -1;
+ LDAP_LIST_INSERT_HEAD(&zh->zh_zopool, zo_new, zo_link);
+ ldap_pvt_thread_mutex_unlock( &zh->zh_mutex );
+ Debug(LDAP_DEBUG_NONE, "slap_zn_malloc: returning 0x%x, 0x%x\n",
+ ptr, (int)ptr>>(zh->zh_zoneorder+1) );
+ return((void*)ptr);
+ } else if (i <= zh->zh_zoneorder) {
+ for (j = i; j > order; j--) {
+ zo_left = LDAP_LIST_FIRST(&zh->zh_free[j-order_start]);
+ LDAP_LIST_REMOVE(zo_left, zo_link);
+ if (LDAP_LIST_EMPTY(&zh->zh_zopool)) {
+ slap_replenish_zopool(zh);
+ }
+ zo_right = LDAP_LIST_FIRST(&zh->zh_zopool);
+ LDAP_LIST_REMOVE(zo_right, zo_link);
+ zo_right->zo_ptr = zo_left->zo_ptr + (1 << j);
+ zo_right->zo_idx = zo_left->zo_idx;
+ Debug(LDAP_DEBUG_NONE,
+ "slap_zn_malloc: split (left=0x%x, right=0x%x)\n",
+ zo_left->zo_ptr, zo_right->zo_ptr );
+ if (j == order + 1) {
+ ptr = zo_left->zo_ptr;
+ diff = (unsigned long)((char*)ptr -
+ (char*)zh->zh_zones[zo_left->zo_idx]) >> (order+1);
+ zh->zh_maps[zo_left->zo_idx][order-order_start][diff>>3] |=
+ (1 << (diff & 0x7));
+ *ptr++ = zh->zh_seqno[zo_left->zo_idx];
+ *ptr++ = size - 2*sizeof(ber_len_t);
+ LDAP_LIST_INSERT_HEAD(
+ &zh->zh_free[j-1-order_start], zo_right, zo_link);
+ LDAP_LIST_INSERT_HEAD(&zh->zh_zopool, zo_left, zo_link);
+ ldap_pvt_thread_mutex_unlock( &zh->zh_mutex );
+ Debug(LDAP_DEBUG_NONE,
+ "slap_zn_malloc: returning 0x%x, 0x%x\n",
+ ptr, (int)ptr>>(zh->zh_zoneorder+1) );
+ return((void*)ptr);
+ } else {
+ LDAP_LIST_INSERT_HEAD(
+ &zh->zh_free[j-1-order_start], zo_right, zo_link);
+ LDAP_LIST_INSERT_HEAD(
+ &zh->zh_free[j-1-order_start], zo_left, zo_link);
+ }
+ }
+ assert(0);
+ } else {
+
+ if ( zh->zh_maxzones < zh->zh_numzones + zh->zh_deltazones ) {
+ ldap_pvt_thread_mutex_unlock( &zh->zh_mutex );
+ Debug( LDAP_DEBUG_TRACE,
+ "zn_malloc %lu: ch_malloc\n",
+ (long)size );
+ Debug(LDAP_DEBUG_NONE,
+ "slap_zn_malloc: returning 0x%x, 0x%x\n",
+ ptr, (int)ptr>>(zh->zh_zoneorder+1) );
+ return (void*)ch_malloc(size);
+ }
+
+ for (i = zh->zh_numzones; i < zh->zh_numzones+zh->zh_deltazones; i++) {
+ zh->zh_zones[i] = mmap(0, zh->zh_zonesize, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, zh->zh_fd, 0);
+ zh->zh_maps[i] = (unsigned char **)
+ ch_malloc((zh->zh_zoneorder - order_start + 1) *
+ sizeof(unsigned char *));
+ for (j = 0; j < zh->zh_zoneorder-order_start+1; j++) {
+ int shiftamt = order_start + 1 + j;
+ int nummaps = zh->zh_zonesize >> shiftamt;
+ assert(nummaps);
+ nummaps >>= 3;
+ if (!nummaps) nummaps = 1;
+ zh->zh_maps[i][j] = (unsigned char *)ch_malloc(nummaps);
+ memset(zh->zh_maps[i][j], 0, nummaps);
+ }
+
+ if (LDAP_LIST_EMPTY(&zh->zh_zopool)) {
+ slap_replenish_zopool(zh);
+ }
+ zo = LDAP_LIST_FIRST(&zh->zh_zopool);
+ LDAP_LIST_REMOVE(zo, zo_link);
+ zo->zo_ptr = zh->zh_zones[i];
+ zo->zo_idx = i;
+ LDAP_LIST_INSERT_HEAD(&zh->
+ zh_free[zh->zh_zoneorder-order_start],zo,zo_link);
+
+ if (LDAP_LIST_EMPTY(&zh->zh_zopool)) {
+ slap_replenish_zopool(zh);
+ }
+ zo = LDAP_LIST_FIRST(&zh->zh_zopool);
+ LDAP_LIST_REMOVE(zo, zo_link);
+ zo->zo_ptr = zh->zh_zones[i];
+ zo->zo_siz = zh->zh_zonesize;
+ zo->zo_idx = i;
+ ldap_avl_insert(&zh->zh_zonetree, zo, slap_zone_cmp, ldap_avl_dup_error);
+ ldap_pvt_thread_rdwr_init(&zh->zh_znlock[i]);
+ }
+ zh->zh_numzones += zh->zh_deltazones;
+ ldap_pvt_thread_mutex_unlock( &zh->zh_mutex );
+ goto retry;
+ }
+}
+
+void *
+slap_zn_calloc( ber_len_t n, ber_len_t size, void *ctx )
+{
+ void *new;
+
+ new = slap_zn_malloc( n*size, ctx );
+ if ( new ) {
+ memset( new, 0, n*size );
+ }
+ return new;
+}
+
+void *
+slap_zn_realloc(void *ptr, ber_len_t size, void *ctx)
+{
+ struct zone_heap *zh = ctx;
+ int pad = 2*sizeof(int)-1, pad_shift;
+ int order_start = -1, order = -1;
+ struct zone_object zoi, *zoo;
+ ber_len_t *p = (ber_len_t *)ptr, *new;
+ unsigned long diff;
+ int i;
+ void *newptr = NULL;
+ struct zone_heap *zone = NULL;
+
+ Debug(LDAP_DEBUG_NONE,
+ "--> slap_zn_realloc: ptr=0x%x, size=%d\n", ptr, size );
+
+ if (ptr == NULL)
+ return slap_zn_malloc(size, zh);
+
+ zoi.zo_ptr = p;
+ zoi.zo_idx = -1;
+
+ if (zh) {
+ ldap_pvt_thread_mutex_lock( &zh->zh_mutex );
+ zoo = ldap_avl_find(zh->zh_zonetree, &zoi, slap_zone_cmp);
+ ldap_pvt_thread_mutex_unlock( &zh->zh_mutex );
+ }
+
+ /* Not our memory? */
+ if (!zoo) {
+ /* duplicate of realloc behavior, oh well */
+ new = ber_memrealloc_x(ptr, size, NULL);
+ if (new) {
+ return new;
+ }
+ Debug(LDAP_DEBUG_ANY, "ch_realloc of %lu bytes failed\n",
+ (long) size );
+ assert(0);
+ exit( EXIT_FAILURE );
+ }
+
+ assert(zoo->zo_idx != -1);
+
+ zone = zh->zh_zones[zoo->zo_idx];
+
+ if (size == 0) {
+ slap_zn_free(ptr, zh);
+ return NULL;
+ }
+
+ newptr = slap_zn_malloc(size, zh);
+ if (size < p[-1]) {
+ AC_MEMCPY(newptr, ptr, size);
+ } else {
+ AC_MEMCPY(newptr, ptr, p[-1]);
+ }
+ slap_zn_free(ptr, zh);
+ return newptr;
+}
+
+void
+slap_zn_free(void *ptr, void *ctx)
+{
+ struct zone_heap *zh = ctx;
+ int size, size_shift, order_size;
+ int pad = 2*sizeof(int)-1, pad_shift;
+ ber_len_t *p = (ber_len_t *)ptr, *tmpp;
+ int order_start = -1, order = -1;
+ struct zone_object zoi, *zoo, *zo;
+ unsigned long diff;
+ int i, k, inserted = 0, idx;
+ struct zone_heap *zone = NULL;
+
+ zoi.zo_ptr = p;
+ zoi.zo_idx = -1;
+
+ Debug(LDAP_DEBUG_NONE, "--> slap_zn_free: ptr=0x%x\n", ptr );
+
+ if (zh) {
+ ldap_pvt_thread_mutex_lock( &zh->zh_mutex );
+ zoo = ldap_avl_find(zh->zh_zonetree, &zoi, slap_zone_cmp);
+ ldap_pvt_thread_mutex_unlock( &zh->zh_mutex );
+ }
+
+ if (!zoo) {
+ ber_memfree_x(ptr, NULL);
+ } else {
+ idx = zoo->zo_idx;
+ assert(idx != -1);
+ zone = zh->zh_zones[idx];
+
+ size = *(--p);
+ size_shift = size + 2*sizeof(ber_len_t) - 1;
+ do {
+ order++;
+ } while (size_shift >>= 1);
+
+ pad_shift = pad - 1;
+ do {
+ order_start++;
+ } while (pad_shift >>= 1);
+
+ ldap_pvt_thread_mutex_lock( &zh->zh_mutex );
+ for (i = order, tmpp = p; i <= zh->zh_zoneorder; i++) {
+ order_size = 1 << (i+1);
+ diff = (unsigned long)((char*)tmpp - (char*)zone) >> (i+1);
+ zh->zh_maps[idx][i-order_start][diff>>3] &= (~(1 << (diff & 0x7)));
+ if (diff == ((diff>>1)<<1)) {
+ if (!(zh->zh_maps[idx][i-order_start][(diff+1)>>3] &
+ (1<<((diff+1)&0x7)))) {
+ zo = LDAP_LIST_FIRST(&zh->zh_free[i-order_start]);
+ while (zo) {
+ if ((char*)zo->zo_ptr == (char*)tmpp) {
+ LDAP_LIST_REMOVE( zo, zo_link );
+ } else if ((char*)zo->zo_ptr ==
+ (char*)tmpp + order_size) {
+ LDAP_LIST_REMOVE(zo, zo_link);
+ break;
+ }
+ zo = LDAP_LIST_NEXT(zo, zo_link);
+ }
+ if (zo) {
+ if (i < zh->zh_zoneorder) {
+ inserted = 1;
+ zo->zo_ptr = tmpp;
+ Debug(LDAP_DEBUG_NONE,
+ "slap_zn_free: merging 0x%x\n",
+ zo->zo_ptr );
+ LDAP_LIST_INSERT_HEAD(&zh->zh_free[i-order_start+1],
+ zo, zo_link);
+ }
+ continue;
+ } else {
+ if (LDAP_LIST_EMPTY(&zh->zh_zopool)) {
+ slap_replenish_zopool(zh);
+ }
+ zo = LDAP_LIST_FIRST(&zh->zh_zopool);
+ LDAP_LIST_REMOVE(zo, zo_link);
+ zo->zo_ptr = tmpp;
+ zo->zo_idx = idx;
+ Debug(LDAP_DEBUG_NONE,
+ "slap_zn_free: merging 0x%x\n",
+ zo->zo_ptr );
+ LDAP_LIST_INSERT_HEAD(&zh->zh_free[i-order_start],
+ zo, zo_link);
+ break;
+
+ Debug(LDAP_DEBUG_ANY, "slap_zn_free: "
+ "free object not found while bit is clear.\n" );
+ assert(zo != NULL);
+
+ }
+ } else {
+ if (!inserted) {
+ if (LDAP_LIST_EMPTY(&zh->zh_zopool)) {
+ slap_replenish_zopool(zh);
+ }
+ zo = LDAP_LIST_FIRST(&zh->zh_zopool);
+ LDAP_LIST_REMOVE(zo, zo_link);
+ zo->zo_ptr = tmpp;
+ zo->zo_idx = idx;
+ Debug(LDAP_DEBUG_NONE,
+ "slap_zn_free: merging 0x%x\n",
+ zo->zo_ptr );
+ LDAP_LIST_INSERT_HEAD(&zh->zh_free[i-order_start],
+ zo, zo_link);
+ }
+ break;
+ }
+ } else {
+ if (!(zh->zh_maps[idx][i-order_start][(diff-1)>>3] &
+ (1<<((diff-1)&0x7)))) {
+ zo = LDAP_LIST_FIRST(&zh->zh_free[i-order_start]);
+ while (zo) {
+ if ((char*)zo->zo_ptr == (char*)tmpp) {
+ LDAP_LIST_REMOVE(zo, zo_link);
+ } else if ((char*)tmpp == zo->zo_ptr + order_size) {
+ LDAP_LIST_REMOVE(zo, zo_link);
+ tmpp = zo->zo_ptr;
+ break;
+ }
+ zo = LDAP_LIST_NEXT(zo, zo_link);
+ }
+ if (zo) {
+ if (i < zh->zh_zoneorder) {
+ inserted = 1;
+ Debug(LDAP_DEBUG_NONE,
+ "slap_zn_free: merging 0x%x\n",
+ zo->zo_ptr );
+ LDAP_LIST_INSERT_HEAD(&zh->zh_free[i-order_start+1],
+ zo, zo_link);
+ continue;
+ }
+ } else {
+ if (LDAP_LIST_EMPTY(&zh->zh_zopool)) {
+ slap_replenish_zopool(zh);
+ }
+ zo = LDAP_LIST_FIRST(&zh->zh_zopool);
+ LDAP_LIST_REMOVE(zo, zo_link);
+ zo->zo_ptr = tmpp;
+ zo->zo_idx = idx;
+ Debug(LDAP_DEBUG_NONE,
+ "slap_zn_free: merging 0x%x\n",
+ zo->zo_ptr );
+ LDAP_LIST_INSERT_HEAD(&zh->zh_free[i-order_start],
+ zo, zo_link);
+ break;
+
+ Debug(LDAP_DEBUG_ANY, "slap_zn_free: "
+ "free object not found while bit is clear.\n" );
+ assert(zo != NULL);
+
+ }
+ } else {
+ if ( !inserted ) {
+ if (LDAP_LIST_EMPTY(&zh->zh_zopool)) {
+ slap_replenish_zopool(zh);
+ }
+ zo = LDAP_LIST_FIRST(&zh->zh_zopool);
+ LDAP_LIST_REMOVE(zo, zo_link);
+ zo->zo_ptr = tmpp;
+ zo->zo_idx = idx;
+ Debug(LDAP_DEBUG_NONE,
+ "slap_zn_free: merging 0x%x\n",
+ zo->zo_ptr );
+ LDAP_LIST_INSERT_HEAD(&zh->zh_free[i-order_start],
+ zo, zo_link);
+ }
+ break;
+ }
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &zh->zh_mutex );
+ }
+}
+
+static int
+slap_zone_cmp(const void *v1, const void *v2)
+{
+ const struct zone_object *zo1 = v1;
+ const struct zone_object *zo2 = v2;
+ char *ptr1;
+ char *ptr2;
+ ber_len_t zpad;
+
+ zpad = zo2->zo_siz - 1;
+ ptr1 = (char*)(((unsigned long)zo1->zo_ptr + zpad) & ~zpad);
+ ptr2 = (char*)zo2->zo_ptr + ((char*)ptr1 - (char*)zo1->zo_ptr);
+ ptr2 = (char*)(((unsigned long)ptr2 + zpad) & ~zpad);
+ return (int)((char*)ptr1 - (char*)ptr2);
+}
+
+void *
+slap_replenish_zopool(
+ void *ctx
+)
+{
+ struct zone_heap* zh = ctx;
+ struct zone_object *zo_block;
+ int i;
+
+ zo_block = (struct zone_object *)ch_malloc(
+ SLAP_ZONE_ZOBLOCK * sizeof(struct zone_object));
+
+ if ( zo_block == NULL ) {
+ return NULL;
+ }
+
+ zo_block[0].zo_blockhead = 1;
+ LDAP_LIST_INSERT_HEAD(&zh->zh_zopool, &zo_block[0], zo_link);
+ for (i = 1; i < SLAP_ZONE_ZOBLOCK; i++) {
+ zo_block[i].zo_blockhead = 0;
+ LDAP_LIST_INSERT_HEAD(&zh->zh_zopool, &zo_block[i], zo_link );
+ }
+
+ return zo_block;
+}
+
+int
+slap_zn_invalidate(
+ void *ctx,
+ void *ptr
+)
+{
+ struct zone_heap* zh = ctx;
+ struct zone_object zoi, *zoo;
+ struct zone_heap *zone = NULL;
+ int seqno = *((ber_len_t*)ptr - 2);
+ int idx = -1, rc = 0;
+ int pad = 2*sizeof(int)-1, pad_shift;
+ int order_start = -1, i;
+ struct zone_object *zo;
+
+ pad_shift = pad - 1;
+ do {
+ order_start++;
+ } while (pad_shift >>= 1);
+
+ zoi.zo_ptr = ptr;
+ zoi.zo_idx = -1;
+
+ ldap_pvt_thread_mutex_lock( &zh->zh_mutex );
+ zoo = ldap_avl_find(zh->zh_zonetree, &zoi, slap_zone_cmp);
+
+ if (zoo) {
+ idx = zoo->zo_idx;
+ assert(idx != -1);
+ madvise(zh->zh_zones[idx], zh->zh_zonesize, MADV_DONTNEED);
+ for (i = 0; i < zh->zh_zoneorder - order_start + 1; i++) {
+ int shiftamt = order_start + 1 + i;
+ int nummaps = zh->zh_zonesize >> shiftamt;
+ assert(nummaps);
+ nummaps >>= 3;
+ if (!nummaps) nummaps = 1;
+ memset(zh->zh_maps[idx][i], 0, nummaps);
+ zo = LDAP_LIST_FIRST(&zh->zh_free[i]);
+ while (zo) {
+ struct zone_object *zo_tmp = zo;
+ zo = LDAP_LIST_NEXT(zo, zo_link);
+ if (zo_tmp && zo_tmp->zo_idx == idx) {
+ LDAP_LIST_REMOVE(zo_tmp, zo_link);
+ LDAP_LIST_INSERT_HEAD(&zh->zh_zopool, zo_tmp, zo_link);
+ }
+ }
+ }
+ if (LDAP_LIST_EMPTY(&zh->zh_zopool)) {
+ slap_replenish_zopool(zh);
+ }
+ zo = LDAP_LIST_FIRST(&zh->zh_zopool);
+ LDAP_LIST_REMOVE(zo, zo_link);
+ zo->zo_ptr = zh->zh_zones[idx];
+ zo->zo_idx = idx;
+ LDAP_LIST_INSERT_HEAD(&zh->zh_free[zh->zh_zoneorder-order_start],
+ zo, zo_link);
+ zh->zh_seqno[idx]++;
+ } else {
+ Debug(LDAP_DEBUG_NONE, "zone not found for (ctx=0x%x, ptr=0x%x) !\n",
+ ctx, ptr );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &zh->zh_mutex );
+ Debug(LDAP_DEBUG_NONE, "zone %d invalidate\n", idx );
+ return rc;
+}
+
+int
+slap_zn_validate(
+ void *ctx,
+ void *ptr,
+ int seqno
+)
+{
+ struct zone_heap* zh = ctx;
+ struct zone_object zoi, *zoo;
+ struct zone_heap *zone = NULL;
+ int idx, rc = 0;
+
+ zoi.zo_ptr = ptr;
+ zoi.zo_idx = -1;
+
+ zoo = ldap_avl_find(zh->zh_zonetree, &zoi, slap_zone_cmp);
+
+ if (zoo) {
+ idx = zoo->zo_idx;
+ assert(idx != -1);
+ assert(seqno <= zh->zh_seqno[idx]);
+ rc = (seqno == zh->zh_seqno[idx]);
+ }
+
+ return rc;
+}
+
+int slap_zh_rlock(
+ void *ctx
+)
+{
+ struct zone_heap* zh = ctx;
+ ldap_pvt_thread_rdwr_rlock(&zh->zh_lock);
+}
+
+int slap_zh_runlock(
+ void *ctx
+)
+{
+ struct zone_heap* zh = ctx;
+ ldap_pvt_thread_rdwr_runlock(&zh->zh_lock);
+}
+
+int slap_zh_wlock(
+ void *ctx
+)
+{
+ struct zone_heap* zh = ctx;
+ ldap_pvt_thread_rdwr_wlock(&zh->zh_lock);
+}
+
+int slap_zh_wunlock(
+ void *ctx
+)
+{
+ struct zone_heap* zh = ctx;
+ ldap_pvt_thread_rdwr_wunlock(&zh->zh_lock);
+}
+
+int slap_zn_rlock(
+ void *ctx,
+ void *ptr
+)
+{
+ struct zone_heap* zh = ctx;
+ struct zone_object zoi, *zoo;
+ struct zone_heap *zone = NULL;
+ int idx;
+
+ zoi.zo_ptr = ptr;
+ zoi.zo_idx = -1;
+
+ ldap_pvt_thread_mutex_lock( &zh->zh_mutex );
+ zoo = ldap_avl_find(zh->zh_zonetree, &zoi, slap_zone_cmp);
+ ldap_pvt_thread_mutex_unlock( &zh->zh_mutex );
+
+ if (zoo) {
+ idx = zoo->zo_idx;
+ assert(idx != -1);
+ ldap_pvt_thread_rdwr_rlock(&zh->zh_znlock[idx]);
+ }
+}
+
+int slap_zn_runlock(
+ void *ctx,
+ void *ptr
+)
+{
+ struct zone_heap* zh = ctx;
+ struct zone_object zoi, *zoo;
+ struct zone_heap *zone = NULL;
+ int idx;
+
+ zoi.zo_ptr = ptr;
+ zoi.zo_idx = -1;
+
+ ldap_pvt_thread_mutex_lock( &zh->zh_mutex );
+ zoo = ldap_avl_find(zh->zh_zonetree, &zoi, slap_zone_cmp);
+ ldap_pvt_thread_mutex_unlock( &zh->zh_mutex );
+
+ if (zoo) {
+ idx = zoo->zo_idx;
+ assert(idx != -1);
+ ldap_pvt_thread_rdwr_runlock(&zh->zh_znlock[idx]);
+ }
+}
+
+int slap_zn_wlock(
+ void *ctx,
+ void *ptr
+)
+{
+ struct zone_heap* zh = ctx;
+ struct zone_object zoi, *zoo;
+ struct zone_heap *zone = NULL;
+ int idx;
+
+ zoi.zo_ptr = ptr;
+ zoi.zo_idx = -1;
+
+ ldap_pvt_thread_mutex_lock( &zh->zh_mutex );
+ zoo = ldap_avl_find(zh->zh_zonetree, &zoi, slap_zone_cmp);
+ ldap_pvt_thread_mutex_unlock( &zh->zh_mutex );
+
+ if (zoo) {
+ idx = zoo->zo_idx;
+ assert(idx != -1);
+ ldap_pvt_thread_rdwr_wlock(&zh->zh_znlock[idx]);
+ }
+}
+
+int slap_zn_wunlock(
+ void *ctx,
+ void *ptr
+)
+{
+ struct zone_heap* zh = ctx;
+ struct zone_object zoi, *zoo;
+ struct zone_heap *zone = NULL;
+ int idx;
+
+ zoi.zo_ptr = ptr;
+ zoi.zo_idx = -1;
+
+ ldap_pvt_thread_mutex_lock( &zh->zh_mutex );
+ zoo = ldap_avl_find(zh->zh_zonetree, &zoi, slap_zone_cmp);
+ ldap_pvt_thread_mutex_unlock( &zh->zh_mutex );
+
+ if (zoo) {
+ idx = zoo->zo_idx;
+ assert(idx != -1);
+ ldap_pvt_thread_rdwr_wunlock(&zh->zh_znlock[idx]);
+ }
+}
+
+#define T_SEC_IN_USEC 1000000
+
+static int
+slap_timediff(struct timeval *tv_begin, struct timeval *tv_end)
+{
+ uint64_t t_begin, t_end, t_diff;
+
+ t_begin = T_SEC_IN_USEC * tv_begin->tv_sec + tv_begin->tv_usec;
+ t_end = T_SEC_IN_USEC * tv_end->tv_sec + tv_end->tv_usec;
+ t_diff = t_end - t_begin;
+
+ if ( t_diff < 0 )
+ t_diff = 0;
+
+ return (int)t_diff;
+}
+
+void
+slap_set_timing(struct timeval *tv_set)
+{
+ gettimeofday(tv_set, (struct timezone *)NULL);
+}
+
+int
+slap_measure_timing(struct timeval *tv_set, struct timeval *tv_measure)
+{
+ gettimeofday(tv_measure, (struct timezone *)NULL);
+ return(slap_timediff(tv_set, tv_measure));
+}
+
+#define EMA_WEIGHT 0.999000
+#define SLAP_ZN_LATENCY_HISTORY_QLEN 500
+int
+slap_zn_latency_history(void* ctx, int ea_latency)
+{
+/* TODO: monitor /proc/stat (swap) as well */
+ struct zone_heap* zh = ctx;
+ double t_diff = 0.0;
+
+ zh->zh_ema_latency = (double)ea_latency * (1.0 - EMA_WEIGHT)
+ + zh->zh_ema_latency * EMA_WEIGHT;
+ if (!zh->zh_swapping && zh->zh_ema_samples++ % 100 == 99) {
+ struct zone_latency_history *zlh_entry;
+ zlh_entry = ch_calloc(1, sizeof(struct zone_latency_history));
+ zlh_entry->zlh_latency = zh->zh_ema_latency;
+ LDAP_STAILQ_INSERT_TAIL(
+ &zh->zh_latency_history_queue, zlh_entry, zlh_next);
+ zh->zh_latency_history_qlen++;
+ while (zh->zh_latency_history_qlen > SLAP_ZN_LATENCY_HISTORY_QLEN) {
+ struct zone_latency_history *zlh;
+ zlh = LDAP_STAILQ_FIRST(&zh->zh_latency_history_queue);
+ LDAP_STAILQ_REMOVE_HEAD(
+ &zh->zh_latency_history_queue, zlh_next);
+ zh->zh_latency_history_qlen--;
+ ch_free(zlh);
+ }
+ if (zh->zh_latency_history_qlen == SLAP_ZN_LATENCY_HISTORY_QLEN) {
+ struct zone_latency_history *zlh_first, *zlh_last;
+ zlh_first = LDAP_STAILQ_FIRST(&zh->zh_latency_history_queue);
+ zlh_last = LDAP_STAILQ_LAST(&zh->zh_latency_history_queue,
+ zone_latency_history, zlh_next);
+ t_diff = zlh_last->zlh_latency - zlh_first->zlh_latency;
+ }
+ if (t_diff >= 2000) {
+ zh->zh_latency_jump++;
+ } else {
+ zh->zh_latency_jump = 0;
+ }
+ if (zh->zh_latency_jump > 3) {
+ zh->zh_latency_jump = 0;
+ zh->zh_swapping = 1;
+ }
+ }
+ return zh->zh_swapping;
+}
+#endif /* SLAP_ZONE_ALLOC */
diff --git a/tests/Makefile.in b/tests/Makefile.in
new file mode 100644
index 0000000..f9b5679
--- /dev/null
+++ b/tests/Makefile.in
@@ -0,0 +1,94 @@
+# Makefile.in for tests
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+RUN=./run
+SUBDIRS= progs
+
+BUILD_MDB=@BUILD_MDB@
+BUILD_SQL=@BUILD_SQL@
+BUILD_SLAPD=@BUILD_SLAPD@
+BUILD_BALANCER=@BUILD_BALANCER@
+BUILD_WT=@BUILD_WT@
+
+# test primary backends (default)
+test tests:
+ @$(MAKE) mdb
+ @$(MAKE) lloadd
+
+# test all backends
+alltests: tests
+ @$(MAKE) sql
+ @$(MAKE) ldif
+ @$(MAKE) wt
+
+mdb test-mdb: mdb-$(BUILD_MDB)
+mdb-no:
+ @echo "run configure with --enable-mdb to run MDB tests"
+
+mdb-yes mdb-mod: FORCE
+ @echo "Initiating LDAP tests for MDB..."
+ @$(RUN) -b mdb all
+
+sql test-sql: sql-$(BUILD_SQL)
+sql-no:
+ @echo "run configure with --enable-sql to run SQL tests"
+
+sql-yes sql-mod: FORCE
+ @echo "Initiating LDAP tests for SQL..."
+ @$(RUN) -b sql sql-all
+
+ldif test-ldif: FORCE
+ @echo "Initiating LDAP tests for LDIF..."
+ @$(RUN) -b ldif all
+
+wt test-wt: wt-$(BUILD_WT)
+wt-no:
+ @echo "run configure with --enable-wt to run back-wt tests"
+
+wt-yes wt-mod: FORCE
+ @$(RUN) -b wt all
+
+lloadd test-lloadd: lloadd-$(BUILD_BALANCER)
+lloadd-no:
+ @echo "run configure with --enable-balancer to run the Load Balancer tests"
+
+lloadd-yes lloadd-mod: lloadd-slapd-$(BUILD_SLAPD)
+
+lloadd-slapd-no:
+ @echo "run configure with --enable-slapd to run the Load Balancer tests"
+
+lloadd-slapd-yes: FORCE
+ @echo "Initiating LDAP tests for the Load Balancer..."
+ @$(RUN) lloadd-all
+
+regressions: FORCE
+ @echo "Testing (available) ITS regressions"
+ @$(MAKE) mdb-its
+
+its: regressions
+
+mdb-its: mdb-its-$(BUILD_MDB)
+mdb-its-no:
+ @echo "run configure with --enable-mdb to run MDB ITS regressions"
+
+mdb-its-yes mdb-its-mod: FORCE
+ @$(RUN) -b mdb its-all
+
+clean-local: FORCE
+ -$(RM) -r testrun configpw configpw.conf *leak *gmon *core
+
+veryclean-local: FORCE
+ @-$(RM) run testdata schema ucdata
+
diff --git a/tests/README b/tests/README
new file mode 100644
index 0000000..e7a8a92
--- /dev/null
+++ b/tests/README
@@ -0,0 +1,23 @@
+This directory contains a series of test scripts which are used to
+verify basic functionality of the LDAP libraries and slapd.
+
+ To run all of the tests, type "make test".
+ To run MDB tests, type "make mdb".
+ To run SQL tests, define SLAPD_USE_SQL=<rdbms> and type
+ "make sql"; define SLAPD_USE_SQLWRITE=yes
+ to enable write tests as well.
+ To run WT tests, type "make wt".
+ To run regression tests, type "make regressions"
+
+The test scripts depends on a number of tools commonly available on
+Unix (and Unix-like) systems. While attempts have been made to make
+these scripts reasonably portable, they may not run properly on your
+system. You may have to adjust your path so that compatible versions
+of tools used are available to the scripts and/or you may have to
+install replacement tools. Platform specific hints may be found at:
+ http://www.openldap.org/faq/index.cgi?file=9
+
+To modify the debug level the tests run slapd with, set the SLAPD_DEBUG
+environment variable.
+ env SLAPD_DEBUG=1 make test
+
diff --git a/tests/data/aci.out b/tests/data/aci.out
new file mode 100644
index 0000000..dd550ef
--- /dev/null
+++ b/tests/data/aci.out
@@ -0,0 +1,68 @@
+# Searching "dc=example,dc=com" (should fail)...
+# Searching "dc=example,dc=com" (should succeed with no results)...
+# Searching "dc=example,dc=com" as "cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" (should succeed)...
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+# Searching "ou=Groups,dc=example,dc=com" as "cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com" (should succeed)...
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+# Searching "ou=Groups,dc=example,dc=com" as "cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" (should succeed with no results)...
diff --git a/tests/data/acl.out.provider b/tests/data/acl.out.provider
new file mode 100644
index 0000000..8fd99a6
--- /dev/null
+++ b/tests/data/acl.out.provider
@@ -0,0 +1,367 @@
+# Try to read an entry inside the Alumni Association container.
+# It should give us noSuchObject if we're not bound...
+# ... and should return all attributes if we're bound as anyone
+# under Example.
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+
+# Checking exact/regex attrval clause
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+cn: Mark A Elliot
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+cn: Mark Elliot
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+cn: John Doe
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+cn: Jonathon Doe
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+cn: Bjorn Jensen
+cn: Biiff Jensen
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+cn: Barbara Jensen
+cn: Babs Jensen
+
+# Using ldapsearch to retrieve all the entries...
+dn: ou=Add & Delete,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Add & Delete
+
+dn: cn=Added by Bjorn (must succeed),ou=Add & Delete,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Added by Bjorn (must succeed)
+sn: None
+description: this attribute value has been added __after__entry creation
+description: Bjorn will try to delete this attribute value (should fail)
+
+dn: cn=Added by Bjorn (renamed by Jaj),ou=Add & Delete,dc=example,dc=com
+objectClass: inetOrgPerson
+sn: None
+cn: Added by Bjorn (renamed by Jaj)
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+description: added by jaj (should succeed)
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+homePhone: +1 313 555 5444
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+uniqueMember: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc
+ =com
+ou: Groups
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+
+dn: cn=Manager,dc=example,dc=com
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
diff --git a/tests/data/certificate.out b/tests/data/certificate.out
new file mode 100644
index 0000000..d30c778
--- /dev/null
+++ b/tests/data/certificate.out
@@ -0,0 +1,103 @@
+# (userCertificate;binary=*)
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: strongAuthenticationUser
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+userCertificate;binary:: MIIDjDCCAvWgAwIBAgIBAzANBgkqhkiG9w0BAQQFADB3MQswCQYDV
+ QQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTH
+ RkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhc
+ NMDMxMDE3MTYzNTM1WhcNMDQxMDE2MTYzNTM1WjCBnjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE1p
+ Y2hpZ2FuMR8wHQYDVQQKExZPcGVuTERBUCBFeGFtcGxlLCBMdGQuMRswGQYDVQQLExJBbHVtbmkgQ
+ XNzb2ljYXRpb24xEjAQBgNVBAMTCUplbiBTbWl0aDEqMCgGCSqGSIb3DQEJARYbamVuQG1haWwuYW
+ x1bW5pLmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDpnXWAL0VkROGO1Rg
+ 8J3u6F4F7yMqQCbUMsV9rxQisYj45+pmqiHV5urogvT4MGD6eLNFZKBn+0KRni++uu7gbartzpmBa
+ HOlzRII9ZdVMFfrT2xYNgAlkne6pb6IZIN9UONuH/httENCDJ5WEpjZ48D1Lrml/HYO/W+SAMkpEq
+ QIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIE
+ NlcnRpZmljYXRlMB0GA1UdDgQWBBTB2saht/od/nis76b9m+pjxfhSPjCBoQYDVR0jBIGZMIGWgBR
+ LbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3Ju
+ aWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExH
+ TAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAMA0GCSqGSIb3DQEBBAUAA4GBAIoGPc/AS0
+ cNkMRDNoMIzcFdF9lONMduKBiSuFvv+x8nCek+LUdXxF59V2NPKh2V5gFh5xbAchyv6FVBnpVtPdB
+ 5akCr5tdFQhuBLUXXDk/tTHGpIWt7OAjEmpuMzsz3GUB8Zf9rioHOs1DMw+GpzWdnFITxXhAqEDc3
+ quqPrpxZ
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: strongAuthenticationUser
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+userCertificate;binary:: MIIDazCCAtSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADB3MQswCQYDV
+ QQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTH
+ RkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhc
+ NMDMxMDE3MTYzMzE5WhcNMDQxMDE2MTYzMzE5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs
+ aWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjEYMBYGA1UEAxMPVXJzdWxhI
+ EhhbXBzdGVyMR8wHQYJKoZIhvcNAQkBFhB1aGFtQGV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQ
+ UAA4GNADCBiQKBgQDuxgp5ELV9LmhxWMpV7qc4028QQT3+zzFDXhruuXE7ji2n3S3ea8bOwDtJh+q
+ nsDe561DhHHHlgIjMKCiDEizYMpxvJPYEXmvp0huRkMgpKZgmel95BSkt6TYmJ0erS3aoimOHLEFi
+ mmnTLolNRMiWqNBvqwobx940PGwUWEePKQIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJYIZIAYb4Q
+ gENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBSjI94TbBmuDEeUUO
+ iC37EK0Uf0XjCBoQYDVR0jBIGZMIGWgBRLbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1U
+ EBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0
+ ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAM
+ A0GCSqGSIb3DQEBBAUAA4GBAIgUcARb3OlWYNbmr1nmqESuxLn16uqI1Ot6WkcICvpkdQ+Bo+R9AP
+ 05xpoXocZtKdNvBu3FNxB/jFkiOcLU2lX7Px1Ijnsjh60qVRy9HOsHCungIKlGcnXLKHmKu0y//5j
+ ds/HnaJsGcHI5JRG7CBJbW+wrwge3trJ1xHJI8prN
+
+# (cAcertificate=*)
+dn: dc=example,dc=com
+dc: example
+objectClass: organization
+objectClass: domainRelatedObject
+objectclass: dcobject
+objectClass: extensibleObject
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+cACertificate;binary:: MIIDVDCCAr2gAwIBAgIBADANBgkqhkiG9w0BAQQFADB3MQswCQYDVQQ
+ GEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRk
+ LjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhcNM
+ DMxMDE3MTYzMDQxWhcNMDQxMDE2MTYzMDQxWjB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaW
+ Zvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjETMBEGA1UEAxMKRXhhbXBsZSB
+ DQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ
+ AoGBANljUGxiisAzEiALukzt3Gj/24MRw1J0AZx6GncXLhpNJsAFyA0bYZdAzgvydKeq/uX0i5o/4
+ Byc3G71XAAcbJZxDPtrLwpDAdMNOBvKV2r67yTgnpatFLfGRt/FWazj5EbFYkorWWTe+4eEBd9VPz
+ ebHdIm+DPHipUfIAzRoNejAgMBAAGjge8wgewwHQYDVR0OBBYEFEtvIRo2JNKQ+UOwU0ctfeHA5pg
+ jMIGhBgNVHSMEgZkwgZaAFEtvIRo2JNKQ+UOwU0ctfeHA5pgjoXukeTB3MQswCQYDVQQGEwJVUzET
+ MBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjETMBEGA
+ 1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb22CAQAwDAYDVR0TBA
+ UwAwEB/zAZBgNVHREEEjAQgQ5jYUBleGFtcGxlLmNvbTANBgkqhkiG9w0BAQQFAAOBgQCgXD/+28E
+ l3GXi/uxMNEKqtnIhQdTnNU4il0fZ6pcmHPFC+61Bddow90ZZZh5Gbg5ZBxFRhDXN8K/fix3ewRSj
+ ASt40dGlEODkE+FsLMt04sYl6kX7RGKg9a46DkeG+uzZnN/3252uCgh+rjNMFAglueUTERv3EtUB1
+ iXEoU3GyA==
+
+# (userCertificate=2$EMAIL=ca@example.com,CN=Example CA,O=Openldap Example\5C,
+ Ltd.,ST=California,C=US)
diff --git a/tests/data/certificate.tls b/tests/data/certificate.tls
new file mode 100644
index 0000000..8404943
--- /dev/null
+++ b/tests/data/certificate.tls
@@ -0,0 +1,240 @@
+# (userCertificate;binary=*)
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: strongAuthenticationUser
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+userCertificate;binary:: MIIDjDCCAvWgAwIBAgIBAzANBgkqhkiG9w0BAQQFADB3MQswCQYDV
+ QQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTH
+ RkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhc
+ NMDMxMDE3MTYzNTM1WhcNMDQxMDE2MTYzNTM1WjCBnjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE1p
+ Y2hpZ2FuMR8wHQYDVQQKExZPcGVuTERBUCBFeGFtcGxlLCBMdGQuMRswGQYDVQQLExJBbHVtbmkgQ
+ XNzb2ljYXRpb24xEjAQBgNVBAMTCUplbiBTbWl0aDEqMCgGCSqGSIb3DQEJARYbamVuQG1haWwuYW
+ x1bW5pLmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDpnXWAL0VkROGO1Rg
+ 8J3u6F4F7yMqQCbUMsV9rxQisYj45+pmqiHV5urogvT4MGD6eLNFZKBn+0KRni++uu7gbartzpmBa
+ HOlzRII9ZdVMFfrT2xYNgAlkne6pb6IZIN9UONuH/httENCDJ5WEpjZ48D1Lrml/HYO/W+SAMkpEq
+ QIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIE
+ NlcnRpZmljYXRlMB0GA1UdDgQWBBTB2saht/od/nis76b9m+pjxfhSPjCBoQYDVR0jBIGZMIGWgBR
+ LbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3Ju
+ aWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExH
+ TAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAMA0GCSqGSIb3DQEBBAUAA4GBAIoGPc/AS0
+ cNkMRDNoMIzcFdF9lONMduKBiSuFvv+x8nCek+LUdXxF59V2NPKh2V5gFh5xbAchyv6FVBnpVtPdB
+ 5akCr5tdFQhuBLUXXDk/tTHGpIWt7OAjEmpuMzsz3GUB8Zf9rioHOs1DMw+GpzWdnFITxXhAqEDc3
+ quqPrpxZ
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: strongAuthenticationUser
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+userCertificate;binary:: MIIDazCCAtSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADB3MQswCQYDV
+ QQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTH
+ RkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhc
+ NMDMxMDE3MTYzMzE5WhcNMDQxMDE2MTYzMzE5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs
+ aWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjEYMBYGA1UEAxMPVXJzdWxhI
+ EhhbXBzdGVyMR8wHQYJKoZIhvcNAQkBFhB1aGFtQGV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQ
+ UAA4GNADCBiQKBgQDuxgp5ELV9LmhxWMpV7qc4028QQT3+zzFDXhruuXE7ji2n3S3ea8bOwDtJh+q
+ nsDe561DhHHHlgIjMKCiDEizYMpxvJPYEXmvp0huRkMgpKZgmel95BSkt6TYmJ0erS3aoimOHLEFi
+ mmnTLolNRMiWqNBvqwobx940PGwUWEePKQIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJYIZIAYb4Q
+ gENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBSjI94TbBmuDEeUUO
+ iC37EK0Uf0XjCBoQYDVR0jBIGZMIGWgBRLbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1U
+ EBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0
+ ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAM
+ A0GCSqGSIb3DQEBBAUAA4GBAIgUcARb3OlWYNbmr1nmqESuxLn16uqI1Ot6WkcICvpkdQ+Bo+R9AP
+ 05xpoXocZtKdNvBu3FNxB/jFkiOcLU2lX7Px1Ijnsjh60qVRy9HOsHCungIKlGcnXLKHmKu0y//5j
+ ds/HnaJsGcHI5JRG7CBJbW+wrwge3trJ1xHJI8prN
+
+# (cAcertificate=*)
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+objectClass: extensibleObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+cACertificate;binary:: MIIDVDCCAr2gAwIBAgIBADANBgkqhkiG9w0BAQQFADB3MQswCQYDVQQ
+ GEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRk
+ LjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhcNM
+ DMxMDE3MTYzMDQxWhcNMDQxMDE2MTYzMDQxWjB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaW
+ Zvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjETMBEGA1UEAxMKRXhhbXBsZSB
+ DQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ
+ AoGBANljUGxiisAzEiALukzt3Gj/24MRw1J0AZx6GncXLhpNJsAFyA0bYZdAzgvydKeq/uX0i5o/4
+ Byc3G71XAAcbJZxDPtrLwpDAdMNOBvKV2r67yTgnpatFLfGRt/FWazj5EbFYkorWWTe+4eEBd9VPz
+ ebHdIm+DPHipUfIAzRoNejAgMBAAGjge8wgewwHQYDVR0OBBYEFEtvIRo2JNKQ+UOwU0ctfeHA5pg
+ jMIGhBgNVHSMEgZkwgZaAFEtvIRo2JNKQ+UOwU0ctfeHA5pgjoXukeTB3MQswCQYDVQQGEwJVUzET
+ MBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjETMBEGA
+ 1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb22CAQAwDAYDVR0TBA
+ UwAwEB/zAZBgNVHREEEjAQgQ5jYUBleGFtcGxlLmNvbTANBgkqhkiG9w0BAQQFAAOBgQCgXD/+28E
+ l3GXi/uxMNEKqtnIhQdTnNU4il0fZ6pcmHPFC+61Bddow90ZZZh5Gbg5ZBxFRhDXN8K/fix3ewRSj
+ ASt40dGlEODkE+FsLMt04sYl6kX7RGKg9a46DkeG+uzZnN/3252uCgh+rjNMFAglueUTERv3EtUB1
+ iXEoU3GyA==
+
+# (userCertificate=2$EMAIL=ca@example.com,CN=Example CA,O=Openldap Example\5C, Ltd.,ST=California,C=US)
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: strongAuthenticationUser
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+userCertificate;binary:: MIIDazCCAtSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADB3MQswCQYDV
+ QQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTH
+ RkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhc
+ NMDMxMDE3MTYzMzE5WhcNMDQxMDE2MTYzMzE5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs
+ aWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjEYMBYGA1UEAxMPVXJzdWxhI
+ EhhbXBzdGVyMR8wHQYJKoZIhvcNAQkBFhB1aGFtQGV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQ
+ UAA4GNADCBiQKBgQDuxgp5ELV9LmhxWMpV7qc4028QQT3+zzFDXhruuXE7ji2n3S3ea8bOwDtJh+q
+ nsDe561DhHHHlgIjMKCiDEizYMpxvJPYEXmvp0huRkMgpKZgmel95BSkt6TYmJ0erS3aoimOHLEFi
+ mmnTLolNRMiWqNBvqwobx940PGwUWEePKQIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJYIZIAYb4Q
+ gENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBSjI94TbBmuDEeUUO
+ iC37EK0Uf0XjCBoQYDVR0jBIGZMIGWgBRLbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1U
+ EBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0
+ ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAM
+ A0GCSqGSIb3DQEBBAUAA4GBAIgUcARb3OlWYNbmr1nmqESuxLn16uqI1Ot6WkcICvpkdQ+Bo+R9AP
+ 05xpoXocZtKdNvBu3FNxB/jFkiOcLU2lX7Px1Ijnsjh60qVRy9HOsHCungIKlGcnXLKHmKu0y//5j
+ ds/HnaJsGcHI5JRG7CBJbW+wrwge3trJ1xHJI8prN
+
+# (userCertificate={ serialNumber 2, issuer "EMAIL=ca@example.com,CN=Example CA,O=Openldap Example\5C, Ltd.,ST=California,C=US" })
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: strongAuthenticationUser
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+userCertificate;binary:: MIIDazCCAtSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADB3MQswCQYDV
+ QQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTH
+ RkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhc
+ NMDMxMDE3MTYzMzE5WhcNMDQxMDE2MTYzMzE5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs
+ aWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjEYMBYGA1UEAxMPVXJzdWxhI
+ EhhbXBzdGVyMR8wHQYJKoZIhvcNAQkBFhB1aGFtQGV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQ
+ UAA4GNADCBiQKBgQDuxgp5ELV9LmhxWMpV7qc4028QQT3+zzFDXhruuXE7ji2n3S3ea8bOwDtJh+q
+ nsDe561DhHHHlgIjMKCiDEizYMpxvJPYEXmvp0huRkMgpKZgmel95BSkt6TYmJ0erS3aoimOHLEFi
+ mmnTLolNRMiWqNBvqwobx940PGwUWEePKQIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJYIZIAYb4Q
+ gENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBSjI94TbBmuDEeUUO
+ iC37EK0Uf0XjCBoQYDVR0jBIGZMIGWgBRLbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1U
+ EBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0
+ ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAM
+ A0GCSqGSIb3DQEBBAUAA4GBAIgUcARb3OlWYNbmr1nmqESuxLn16uqI1Ot6WkcICvpkdQ+Bo+R9AP
+ 05xpoXocZtKdNvBu3FNxB/jFkiOcLU2lX7Px1Ijnsjh60qVRy9HOsHCungIKlGcnXLKHmKu0y//5j
+ ds/HnaJsGcHI5JRG7CBJbW+wrwge3trJ1xHJI8prN
+
+# (userCertificate:certificateExactMatch:=3$EMAIL=ca@example.com,CN=Example CA,O=Openldap Example\5C, Ltd.,ST=California,C=US)
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: strongAuthenticationUser
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+userCertificate;binary:: MIIDjDCCAvWgAwIBAgIBAzANBgkqhkiG9w0BAQQFADB3MQswCQYDV
+ QQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTH
+ RkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhc
+ NMDMxMDE3MTYzNTM1WhcNMDQxMDE2MTYzNTM1WjCBnjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE1p
+ Y2hpZ2FuMR8wHQYDVQQKExZPcGVuTERBUCBFeGFtcGxlLCBMdGQuMRswGQYDVQQLExJBbHVtbmkgQ
+ XNzb2ljYXRpb24xEjAQBgNVBAMTCUplbiBTbWl0aDEqMCgGCSqGSIb3DQEJARYbamVuQG1haWwuYW
+ x1bW5pLmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDpnXWAL0VkROGO1Rg
+ 8J3u6F4F7yMqQCbUMsV9rxQisYj45+pmqiHV5urogvT4MGD6eLNFZKBn+0KRni++uu7gbartzpmBa
+ HOlzRII9ZdVMFfrT2xYNgAlkne6pb6IZIN9UONuH/httENCDJ5WEpjZ48D1Lrml/HYO/W+SAMkpEq
+ QIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIE
+ NlcnRpZmljYXRlMB0GA1UdDgQWBBTB2saht/od/nis76b9m+pjxfhSPjCBoQYDVR0jBIGZMIGWgBR
+ LbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3Ju
+ aWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExH
+ TAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAMA0GCSqGSIb3DQEBBAUAA4GBAIoGPc/AS0
+ cNkMRDNoMIzcFdF9lONMduKBiSuFvv+x8nCek+LUdXxF59V2NPKh2V5gFh5xbAchyv6FVBnpVtPdB
+ 5akCr5tdFQhuBLUXXDk/tTHGpIWt7OAjEmpuMzsz3GUB8Zf9rioHOs1DMw+GpzWdnFITxXhAqEDc3
+ quqPrpxZ
+
+# (userCertificate:certificateExactMatch:={ issuer "EMAIL=ca@example.com,CN=Example CA,O=Openldap Example\5C, Ltd.,ST=California,C=US", serialNumber 3 })
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: strongAuthenticationUser
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+userCertificate;binary:: MIIDjDCCAvWgAwIBAgIBAzANBgkqhkiG9w0BAQQFADB3MQswCQYDV
+ QQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTH
+ RkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhc
+ NMDMxMDE3MTYzNTM1WhcNMDQxMDE2MTYzNTM1WjCBnjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE1p
+ Y2hpZ2FuMR8wHQYDVQQKExZPcGVuTERBUCBFeGFtcGxlLCBMdGQuMRswGQYDVQQLExJBbHVtbmkgQ
+ XNzb2ljYXRpb24xEjAQBgNVBAMTCUplbiBTbWl0aDEqMCgGCSqGSIb3DQEJARYbamVuQG1haWwuYW
+ x1bW5pLmV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDpnXWAL0VkROGO1Rg
+ 8J3u6F4F7yMqQCbUMsV9rxQisYj45+pmqiHV5urogvT4MGD6eLNFZKBn+0KRni++uu7gbartzpmBa
+ HOlzRII9ZdVMFfrT2xYNgAlkne6pb6IZIN9UONuH/httENCDJ5WEpjZ48D1Lrml/HYO/W+SAMkpEq
+ QIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIE
+ NlcnRpZmljYXRlMB0GA1UdDgQWBBTB2saht/od/nis76b9m+pjxfhSPjCBoQYDVR0jBIGZMIGWgBR
+ LbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3Ju
+ aWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExH
+ TAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAMA0GCSqGSIb3DQEBBAUAA4GBAIoGPc/AS0
+ cNkMRDNoMIzcFdF9lONMduKBiSuFvv+x8nCek+LUdXxF59V2NPKh2V5gFh5xbAchyv6FVBnpVtPdB
+ 5akCr5tdFQhuBLUXXDk/tTHGpIWt7OAjEmpuMzsz3GUB8Zf9rioHOs1DMw+GpzWdnFITxXhAqEDc3
+ quqPrpxZ
+
diff --git a/tests/data/chain.out b/tests/data/chain.out
new file mode 100644
index 0000000..985f5dd
--- /dev/null
+++ b/tests/data/chain.out
@@ -0,0 +1,414 @@
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+
+dn: cn=Manager,dc=example,dc=com
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+
+dn: ou=Other,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Other
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+
diff --git a/tests/data/chainmod.out b/tests/data/chainmod.out
new file mode 100644
index 0000000..67970af
--- /dev/null
+++ b/tests/data/chainmod.out
@@ -0,0 +1,393 @@
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+
+dn: cn=Manager,dc=example,dc=com
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+
+dn: ou=Other,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Other
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: cn=Renamed Group,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+description: testing chain overlay writes...
+member: cn=New Group,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: Renamed Group
+
+dn: cn=Renamed User,ou=People,dc=example,dc=com
+objectClass: person
+sn: User
+description: testing chain overlay writes...
+seeAlso: cn=Renamed Group,ou=Groups,dc=example,dc=com
+cn: Renamed User
+
diff --git a/tests/data/chainref.out b/tests/data/chainref.out
new file mode 100644
index 0000000..bec3250
--- /dev/null
+++ b/tests/data/chainref.out
@@ -0,0 +1,4 @@
+dn: ou=Other,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Other
+
diff --git a/tests/data/compsearch.out b/tests/data/compsearch.out
new file mode 100644
index 0000000..e07c9d4
--- /dev/null
+++ b/tests/data/compsearch.out
@@ -0,0 +1,1558 @@
+dn: cn=beta,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: beta
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+certificateRevocationList;binary:: MIIP0TCCDrkCAQEwDQYJKoZIhvcNAQEFBQAwgZMxCzA
+ JBgNVBAYTAkFVMSswKQYDVQQKEyJDZXJ0aWZpY2F0ZXMgQXVzdHJhbGlhIFB0eSBMaW1pdGVkMSUw
+ IwYDVQQDExxDQVBMIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTAwLgYKCZImiZPyLGQBAxQgY2FAY
+ 2VydGlmaWNhdGVzLWF1c3RyYWxpYS5jb20uYXUXDTAzMDcyMjAxMzAyMFoXDTAzMTEwMzAxMzUyMF
+ owgg27MCMCBDi/biUXDTAwMDMwNjA2MjEzM1owDDAKBgNVHRUEAwoBBDAjAgQ5Il0KFw0wMDA1MjM
+ wODAwNDNaMAwwCgYDVR0VBAMKAQQwIwIEOSo6ZxcNMDAwNTI5MDIyNTQzWjAMMAoGA1UdFQQDCgEE
+ MCMCBDkx1QAXDTAwMDUyOTAzMzYwMVowDDAKBgNVHRUEAwoBBDAjAgQ5Pd7GFw0wMDA2MDcwNTM3M
+ jRaMAwwCgYDVR0VBAMKAQQwIwIEOUcavBcNMDAwNjE0MDc0MjExWjAMMAoGA1UdFQQDCgEEMCMCBD
+ lIlLYXDTAwMDYxNTA4MzY1NlowDDAKBgNVHRUEAwoBBDAjAgQ5SeOkFw0wMDA2MTYwODIzMDVaMAw
+ wCgYDVR0VBAMKAQQwIwIEOUiGjRcNMDAwNjE2MDgyMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ30oX
+ DTAwMDYyOTA4MDQyM1owDDAKBgNVHRUEAwoBBDAjAgQ5SdUjFw0wMDA2MjkwODA1NDVaMAwwCgYDV
+ R0VBAMKAQQwIwIEOTHlfRcNMDAwNjMwMDYwNjA1WjAMMAoGA1UdFQQDCgEEMCMCBDkzV6EXDTAwMD
+ YzMDA2MDYxMVowDDAKBgNVHRUEAwoBBDAjAgQ5SIFOFw0wMDA2MzAwNjA2MjFaMAwwCgYDVR0VBAM
+ KAQQwIwIEOUiCbBcNMDAwNjMwMDYwNjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlIgzkXDTAwMDYzMDA2
+ MDYzNlowDDAKBgNVHRUEAwoBBDAjAgQ5SIQEFw0wMDA2MzAwNjA2NDFaMAwwCgYDVR0VBAMKAQQwI
+ wIEOUiFBBcNMDAwNjMwMDYwNjQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlIhfQXDTAwMDYzMDA2MDY1NV
+ owDDAKBgNVHRUEAwoBBDAjAgQ5SIcmFw0wMDA2MzAwNjA3MDJaMAwwCgYDVR0VBAMKAQQwIwIEOUi
+ H4hcNMDAwNjMwMDYwNzA4WjAMMAoGA1UdFQQDCgEEMCMCBDlIiGUXDTAwMDYzMDA2MDcxNFowDDAK
+ BgNVHRUEAwoBBDAjAgQ5SIjaFw0wMDA2MzAwNjA3NDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiJhRcNM
+ DAwNjMwMDYwNzU3WjAMMAoGA1UdFQQDCgEEMCMCBDlIjoIXDTAwMDYzMDA2MDgwNFowDDAKBgNVHR
+ UEAwoBBDAjAgQ5SI89Fw0wMDA2MzAwNjA4MTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiP1RcNMDAwNjM
+ wMDYwODE1WjAMMAoGA1UdFQQDCgEEMCMCBDlIkEoXDTAwMDYzMDA2MDg0NVowDDAKBgNVHRUEAwoB
+ BDAjAgQ5SJC7Fw0wMDA2MzAwNjA4NTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiReRcNMDAwNjMwMDYwO
+ DU2WjAMMAoGA1UdFQQDCgEEMCMCBDlIkgMXDTAwMDYzMDA2MDkwNFowDDAKBgNVHRUEAwoBBDAjAg
+ Q5SJKqFw0wMDA2MzAwNjA5MDlaMAwwCgYDVR0VBAMKAQQwIwIEOUiTJhcNMDAwNjMwMDYwOTE2WjA
+ MMAoGA1UdFQQDCgEEMCMCBDlIk5AXDTAwMDYzMDA2MDkyMVowDDAKBgNVHRUEAwoBBDAjAgQ5SJQ3
+ Fw0wMDA2MzAwNjA5MjZaMAwwCgYDVR0VBAMKAQQwIwIEOUiVXhcNMDAwNjMwMDYwOTMyWjAMMAoGA
+ 1UdFQQDCgEEMCMCBDlIlgcXDTAwMDYzMDA2MDkzOFowDDAKBgNVHRUEAwoBBDAjAgQ5SJazFw0wMD
+ A2MzAwNjA5NDZaMAwwCgYDVR0VBAMKAQQwIwIEOUiXPxcNMDAwNjMwMDYwOTUxWjAMMAoGA1UdFQQ
+ DCgEEMCMCBDlIl7IXDTAwMDYzMDA2MDk1OFowDDAKBgNVHRUEAwoBBDAjAgQ5SJg0Fw0wMDA2MzAw
+ NjEwMDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiZBBcNMDAwNjMwMDYxMDA5WjAMMAoGA1UdFQQDCgEEM
+ CMCBDlJzksXDTAwMDYzMDA2MTAxNVowDDAKBgNVHRUEAwoBBDAjAgQ5Sc64Fw0wMDA2MzAwNjEwMj
+ FaMAwwCgYDVR0VBAMKAQQwIwIEOUnPVxcNMDAwNjMwMDYxMDI3WjAMMAoGA1UdFQQDCgEEMCMCBDl
+ J0BAXDTAwMDYzMDA2MTAzNVowDDAKBgNVHRUEAwoBBDAjAgQ5SdDKFw0wMDA2MzAwNjEwNDNaMAww
+ CgYDVR0VBAMKAQQwIwIEOUnRZRcNMDAwNjMwMDYxMDQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ0d0XD
+ TAwMDYzMDA2MTA1N1owDDAKBgNVHRUEAwoBBDAjAgQ5SdJ4Fw0wMDA2MzAwNjExMTVaMAwwCgYDVR
+ 0VBAMKAQQwIwIEOUnTDBcNMDAwNjMwMDYxMTIxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ04oXDTAwMDY
+ zMDA2MTEyN1owDDAKBgNVHRUEAwoBBDAjAgQ5SdQSFw0wMDA2MzAwNjExMzNaMAwwCgYDVR0VBAMK
+ AQQwIwIEOUnUoBcNMDAwNjMwMDYxMTM5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ2SQXDTAwMDYzMDA2M
+ TE1M1owDDAKBgNVHRUEAwoBBDAjAgQ5SdmwFw0wMDA2MzAwNjEyMDVaMAwwCgYDVR0VBAMKAQQwIw
+ IEOUnaTBcNMDAwNjMwMDYxMjExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ2vYXDTAwMDYzMDA2MTIxN1o
+ wDDAKBgNVHRUEAwoBBDAjAgQ5SducFw0wMDA2MzAwNjEyMjNaMAwwCgYDVR0VBAMKAQQwIwIEOUnc
+ IRcNMDAwNjMwMDYxMjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3KQXDTAwMDYzMDA2MTIzM1owDDAKB
+ gNVHRUEAwoBBDAjAgQ5Sd2xFw0wMDA2MzAwNjEyNDBaMAwwCgYDVR0VBAMKAQQwIwIEOUneRBcNMD
+ AwNjMwMDYxMjQ1WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3skXDTAwMDYzMDA2MTI1MVowDDAKBgNVHRU
+ EAwoBBDAjAgQ5Sd/IFw0wMDA2MzAwNjEzMDJaMAwwCgYDVR0VBAMKAQQwIwIEOUngPRcNMDAwNjMw
+ MDYxMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4M8XDTAwMDYzMDA2MTMyMFowDDAKBgNVHRUEAwoBB
+ DAjAgQ5SeE/Fw0wMDA2MzAwNjEzMjVaMAwwCgYDVR0VBAMKAQQwIwIEOUnh2BcNMDAwNjMwMDYxMz
+ MxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4mgXDTAwMDYzMDA2MTMzOVowDDAKBgNVHRUEAwoBBDAjAgQ
+ 5SeQvFw0wMDA2MzAwNjEzNDRaMAwwCgYDVR0VBAMKAQQwIwIEOVsGJRcNMDAwNjMwMDYxMzUwWjAM
+ MAoGA1UdFQQDCgEEMCMCBDlbBusXDTAwMDYzMDA2MTM1NlowDDAKBgNVHRUEAwoBBDAjAgQ5XEKPF
+ w0wMDA3MTMwOTAwMzhaMAwwCgYDVR0VBAMKAQQwIwIEOVxEKRcNMDAwNzEzMDkwMDQ1WjAMMAoGA1
+ UdFQQDCgEEMCMCBDlcRukXDTAwMDcyNjA2MjkyN1owDDAKBgNVHRUEAwoBBDAjAgQ5fohgFw0wMDA
+ 3MjYwNjQ2NTFaMAwwCgYDVR0VBAMKAQQwIwIEOaNqPBcNMDAwODIzMDYwOTQxWjAMMAoGA1UdFQQD
+ CgEFMCMCBDlcX2QXDTAwMDgzMTA3MTM1OFowDDAKBgNVHRUEAwoBBDAjAgQ5YsflFw0wMDA5MDEwM
+ TQwMjRaMAwwCgYDVR0VBAMKAQQwIwIEOWGHDRcNMDAwOTA2MDcwMTE2WjAMMAoGA1UdFQQDCgEEMC
+ MCBDliz/4XDTAwMDkwNjA3MDcwNVowDDAKBgNVHRUEAwoBBDAjAgQ5m3S6Fw0wMDA5MjAwNzA2NTd
+ aMAwwCgYDVR0VBAMKAQQwIwIEOy6/hhcNMDEwNzAzMDYxMDQyWjAMMAoGA1UdFQQDCgEEMCMCBDtB
+ Yw4XDTAxMDcwMzA2MTkxNlowDDAKBgNVHRUEAwoBBDAjAgQ7MEG6Fw0wMTA3MTAwODA5NTNaMAwwC
+ gYDVR0VBAMKAQQwIwIEOy68CxcNMDEwNzExMDYxMzI5WjAMMAoGA1UdFQQDCgEEMCMCBDswSOsXDT
+ AxMDgwMTA0MTkyM1owDDAKBgNVHRUEAwoBBTAjAgQ7MYgeFw0wMTA4MDEwNDIwMDJaMAwwCgYDVR0
+ VBAMKAQQwIwIEOzGHeBcNMDEwODAyMDI0NTM4WjAMMAoGA1UdFQQDCgEEMCMCBDsuveEXDTAxMDgz
+ MDA2MjIwOFowDDAKBgNVHRUEAwoBBDAjAgQ7jdxLFw0wMTA4MzAwNjQzMjRaMAwwCgYDVR0VBAMKA
+ QQwIwIEOy67QxcNMDExMTIxMDYyMDUzWjAMMAoGA1UdFQQDCgEEMCMCBDsDNXcXDTAyMDUxNzA4ND
+ Y0MlowDDAKBgNVHRUEAwoBBDAjAgQ7AzXMFw0wMjA1MTcwODQ2NTdaMAwwCgYDVR0VBAMKAQSgMjA
+ wMAsGA1UdFAQEAgIQoDATBgNVHSMEDDAKgAhISAKVrWisNzAMBgNVHRwBAf8EAjAAMA0GCSqGSIb3
+ DQEBBQUAA4IBAQA1xNXgyrtVB5LSOc76JF+aJzf8IfJGqF04CMzbo4lDpec/LgOrTSFV223ccJzuq
+ cnxGUfDbXFfSWDHGnj9HLLTCkrS3clL1TPVjGXg5mFu1l6DCfcP2v4i4dlradNYDQg/AVBoJsYa3l
+ efSFHw8RFXNHJWwIjJA6J0CBJ/8Uq2ywr8umdndb10RLtPWp66A7wxu7OvTjt68d3LgSniQ0mIJCn
+ 4ooE30oF/ew0EznbxlSCNRPpB8jYYJTibGrTUVU43lr8h3URIgBkA4InOhuDv0ePMSCDSxBUhY0+G
+ eKo+YiXHy4SGUGLakahuq/hlGTRJJUddqFA1dNZdOUl23nVE
+
+dn: cn=charlie,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: charlie
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+
+dn: cn=beta,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: beta
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+certificateRevocationList;binary:: MIIP0TCCDrkCAQEwDQYJKoZIhvcNAQEFBQAwgZMxCzA
+ JBgNVBAYTAkFVMSswKQYDVQQKEyJDZXJ0aWZpY2F0ZXMgQXVzdHJhbGlhIFB0eSBMaW1pdGVkMSUw
+ IwYDVQQDExxDQVBMIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTAwLgYKCZImiZPyLGQBAxQgY2FAY
+ 2VydGlmaWNhdGVzLWF1c3RyYWxpYS5jb20uYXUXDTAzMDcyMjAxMzAyMFoXDTAzMTEwMzAxMzUyMF
+ owgg27MCMCBDi/biUXDTAwMDMwNjA2MjEzM1owDDAKBgNVHRUEAwoBBDAjAgQ5Il0KFw0wMDA1MjM
+ wODAwNDNaMAwwCgYDVR0VBAMKAQQwIwIEOSo6ZxcNMDAwNTI5MDIyNTQzWjAMMAoGA1UdFQQDCgEE
+ MCMCBDkx1QAXDTAwMDUyOTAzMzYwMVowDDAKBgNVHRUEAwoBBDAjAgQ5Pd7GFw0wMDA2MDcwNTM3M
+ jRaMAwwCgYDVR0VBAMKAQQwIwIEOUcavBcNMDAwNjE0MDc0MjExWjAMMAoGA1UdFQQDCgEEMCMCBD
+ lIlLYXDTAwMDYxNTA4MzY1NlowDDAKBgNVHRUEAwoBBDAjAgQ5SeOkFw0wMDA2MTYwODIzMDVaMAw
+ wCgYDVR0VBAMKAQQwIwIEOUiGjRcNMDAwNjE2MDgyMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ30oX
+ DTAwMDYyOTA4MDQyM1owDDAKBgNVHRUEAwoBBDAjAgQ5SdUjFw0wMDA2MjkwODA1NDVaMAwwCgYDV
+ R0VBAMKAQQwIwIEOTHlfRcNMDAwNjMwMDYwNjA1WjAMMAoGA1UdFQQDCgEEMCMCBDkzV6EXDTAwMD
+ YzMDA2MDYxMVowDDAKBgNVHRUEAwoBBDAjAgQ5SIFOFw0wMDA2MzAwNjA2MjFaMAwwCgYDVR0VBAM
+ KAQQwIwIEOUiCbBcNMDAwNjMwMDYwNjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlIgzkXDTAwMDYzMDA2
+ MDYzNlowDDAKBgNVHRUEAwoBBDAjAgQ5SIQEFw0wMDA2MzAwNjA2NDFaMAwwCgYDVR0VBAMKAQQwI
+ wIEOUiFBBcNMDAwNjMwMDYwNjQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlIhfQXDTAwMDYzMDA2MDY1NV
+ owDDAKBgNVHRUEAwoBBDAjAgQ5SIcmFw0wMDA2MzAwNjA3MDJaMAwwCgYDVR0VBAMKAQQwIwIEOUi
+ H4hcNMDAwNjMwMDYwNzA4WjAMMAoGA1UdFQQDCgEEMCMCBDlIiGUXDTAwMDYzMDA2MDcxNFowDDAK
+ BgNVHRUEAwoBBDAjAgQ5SIjaFw0wMDA2MzAwNjA3NDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiJhRcNM
+ DAwNjMwMDYwNzU3WjAMMAoGA1UdFQQDCgEEMCMCBDlIjoIXDTAwMDYzMDA2MDgwNFowDDAKBgNVHR
+ UEAwoBBDAjAgQ5SI89Fw0wMDA2MzAwNjA4MTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiP1RcNMDAwNjM
+ wMDYwODE1WjAMMAoGA1UdFQQDCgEEMCMCBDlIkEoXDTAwMDYzMDA2MDg0NVowDDAKBgNVHRUEAwoB
+ BDAjAgQ5SJC7Fw0wMDA2MzAwNjA4NTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiReRcNMDAwNjMwMDYwO
+ DU2WjAMMAoGA1UdFQQDCgEEMCMCBDlIkgMXDTAwMDYzMDA2MDkwNFowDDAKBgNVHRUEAwoBBDAjAg
+ Q5SJKqFw0wMDA2MzAwNjA5MDlaMAwwCgYDVR0VBAMKAQQwIwIEOUiTJhcNMDAwNjMwMDYwOTE2WjA
+ MMAoGA1UdFQQDCgEEMCMCBDlIk5AXDTAwMDYzMDA2MDkyMVowDDAKBgNVHRUEAwoBBDAjAgQ5SJQ3
+ Fw0wMDA2MzAwNjA5MjZaMAwwCgYDVR0VBAMKAQQwIwIEOUiVXhcNMDAwNjMwMDYwOTMyWjAMMAoGA
+ 1UdFQQDCgEEMCMCBDlIlgcXDTAwMDYzMDA2MDkzOFowDDAKBgNVHRUEAwoBBDAjAgQ5SJazFw0wMD
+ A2MzAwNjA5NDZaMAwwCgYDVR0VBAMKAQQwIwIEOUiXPxcNMDAwNjMwMDYwOTUxWjAMMAoGA1UdFQQ
+ DCgEEMCMCBDlIl7IXDTAwMDYzMDA2MDk1OFowDDAKBgNVHRUEAwoBBDAjAgQ5SJg0Fw0wMDA2MzAw
+ NjEwMDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiZBBcNMDAwNjMwMDYxMDA5WjAMMAoGA1UdFQQDCgEEM
+ CMCBDlJzksXDTAwMDYzMDA2MTAxNVowDDAKBgNVHRUEAwoBBDAjAgQ5Sc64Fw0wMDA2MzAwNjEwMj
+ FaMAwwCgYDVR0VBAMKAQQwIwIEOUnPVxcNMDAwNjMwMDYxMDI3WjAMMAoGA1UdFQQDCgEEMCMCBDl
+ J0BAXDTAwMDYzMDA2MTAzNVowDDAKBgNVHRUEAwoBBDAjAgQ5SdDKFw0wMDA2MzAwNjEwNDNaMAww
+ CgYDVR0VBAMKAQQwIwIEOUnRZRcNMDAwNjMwMDYxMDQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ0d0XD
+ TAwMDYzMDA2MTA1N1owDDAKBgNVHRUEAwoBBDAjAgQ5SdJ4Fw0wMDA2MzAwNjExMTVaMAwwCgYDVR
+ 0VBAMKAQQwIwIEOUnTDBcNMDAwNjMwMDYxMTIxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ04oXDTAwMDY
+ zMDA2MTEyN1owDDAKBgNVHRUEAwoBBDAjAgQ5SdQSFw0wMDA2MzAwNjExMzNaMAwwCgYDVR0VBAMK
+ AQQwIwIEOUnUoBcNMDAwNjMwMDYxMTM5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ2SQXDTAwMDYzMDA2M
+ TE1M1owDDAKBgNVHRUEAwoBBDAjAgQ5SdmwFw0wMDA2MzAwNjEyMDVaMAwwCgYDVR0VBAMKAQQwIw
+ IEOUnaTBcNMDAwNjMwMDYxMjExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ2vYXDTAwMDYzMDA2MTIxN1o
+ wDDAKBgNVHRUEAwoBBDAjAgQ5SducFw0wMDA2MzAwNjEyMjNaMAwwCgYDVR0VBAMKAQQwIwIEOUnc
+ IRcNMDAwNjMwMDYxMjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3KQXDTAwMDYzMDA2MTIzM1owDDAKB
+ gNVHRUEAwoBBDAjAgQ5Sd2xFw0wMDA2MzAwNjEyNDBaMAwwCgYDVR0VBAMKAQQwIwIEOUneRBcNMD
+ AwNjMwMDYxMjQ1WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3skXDTAwMDYzMDA2MTI1MVowDDAKBgNVHRU
+ EAwoBBDAjAgQ5Sd/IFw0wMDA2MzAwNjEzMDJaMAwwCgYDVR0VBAMKAQQwIwIEOUngPRcNMDAwNjMw
+ MDYxMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4M8XDTAwMDYzMDA2MTMyMFowDDAKBgNVHRUEAwoBB
+ DAjAgQ5SeE/Fw0wMDA2MzAwNjEzMjVaMAwwCgYDVR0VBAMKAQQwIwIEOUnh2BcNMDAwNjMwMDYxMz
+ MxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4mgXDTAwMDYzMDA2MTMzOVowDDAKBgNVHRUEAwoBBDAjAgQ
+ 5SeQvFw0wMDA2MzAwNjEzNDRaMAwwCgYDVR0VBAMKAQQwIwIEOVsGJRcNMDAwNjMwMDYxMzUwWjAM
+ MAoGA1UdFQQDCgEEMCMCBDlbBusXDTAwMDYzMDA2MTM1NlowDDAKBgNVHRUEAwoBBDAjAgQ5XEKPF
+ w0wMDA3MTMwOTAwMzhaMAwwCgYDVR0VBAMKAQQwIwIEOVxEKRcNMDAwNzEzMDkwMDQ1WjAMMAoGA1
+ UdFQQDCgEEMCMCBDlcRukXDTAwMDcyNjA2MjkyN1owDDAKBgNVHRUEAwoBBDAjAgQ5fohgFw0wMDA
+ 3MjYwNjQ2NTFaMAwwCgYDVR0VBAMKAQQwIwIEOaNqPBcNMDAwODIzMDYwOTQxWjAMMAoGA1UdFQQD
+ CgEFMCMCBDlcX2QXDTAwMDgzMTA3MTM1OFowDDAKBgNVHRUEAwoBBDAjAgQ5YsflFw0wMDA5MDEwM
+ TQwMjRaMAwwCgYDVR0VBAMKAQQwIwIEOWGHDRcNMDAwOTA2MDcwMTE2WjAMMAoGA1UdFQQDCgEEMC
+ MCBDliz/4XDTAwMDkwNjA3MDcwNVowDDAKBgNVHRUEAwoBBDAjAgQ5m3S6Fw0wMDA5MjAwNzA2NTd
+ aMAwwCgYDVR0VBAMKAQQwIwIEOy6/hhcNMDEwNzAzMDYxMDQyWjAMMAoGA1UdFQQDCgEEMCMCBDtB
+ Yw4XDTAxMDcwMzA2MTkxNlowDDAKBgNVHRUEAwoBBDAjAgQ7MEG6Fw0wMTA3MTAwODA5NTNaMAwwC
+ gYDVR0VBAMKAQQwIwIEOy68CxcNMDEwNzExMDYxMzI5WjAMMAoGA1UdFQQDCgEEMCMCBDswSOsXDT
+ AxMDgwMTA0MTkyM1owDDAKBgNVHRUEAwoBBTAjAgQ7MYgeFw0wMTA4MDEwNDIwMDJaMAwwCgYDVR0
+ VBAMKAQQwIwIEOzGHeBcNMDEwODAyMDI0NTM4WjAMMAoGA1UdFQQDCgEEMCMCBDsuveEXDTAxMDgz
+ MDA2MjIwOFowDDAKBgNVHRUEAwoBBDAjAgQ7jdxLFw0wMTA4MzAwNjQzMjRaMAwwCgYDVR0VBAMKA
+ QQwIwIEOy67QxcNMDExMTIxMDYyMDUzWjAMMAoGA1UdFQQDCgEEMCMCBDsDNXcXDTAyMDUxNzA4ND
+ Y0MlowDDAKBgNVHRUEAwoBBDAjAgQ7AzXMFw0wMjA1MTcwODQ2NTdaMAwwCgYDVR0VBAMKAQSgMjA
+ wMAsGA1UdFAQEAgIQoDATBgNVHSMEDDAKgAhISAKVrWisNzAMBgNVHRwBAf8EAjAAMA0GCSqGSIb3
+ DQEBBQUAA4IBAQA1xNXgyrtVB5LSOc76JF+aJzf8IfJGqF04CMzbo4lDpec/LgOrTSFV223ccJzuq
+ cnxGUfDbXFfSWDHGnj9HLLTCkrS3clL1TPVjGXg5mFu1l6DCfcP2v4i4dlradNYDQg/AVBoJsYa3l
+ efSFHw8RFXNHJWwIjJA6J0CBJ/8Uq2ywr8umdndb10RLtPWp66A7wxu7OvTjt68d3LgSniQ0mIJCn
+ 4ooE30oF/ew0EznbxlSCNRPpB8jYYJTibGrTUVU43lr8h3URIgBkA4InOhuDv0ePMSCDSxBUhY0+G
+ eKo+YiXHy4SGUGLakahuq/hlGTRJJUddqFA1dNZdOUl23nVE
+
+dn: cn=charlie,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: charlie
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+
+dn: cn=beta,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: beta
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+certificateRevocationList;binary:: MIIP0TCCDrkCAQEwDQYJKoZIhvcNAQEFBQAwgZMxCzA
+ JBgNVBAYTAkFVMSswKQYDVQQKEyJDZXJ0aWZpY2F0ZXMgQXVzdHJhbGlhIFB0eSBMaW1pdGVkMSUw
+ IwYDVQQDExxDQVBMIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTAwLgYKCZImiZPyLGQBAxQgY2FAY
+ 2VydGlmaWNhdGVzLWF1c3RyYWxpYS5jb20uYXUXDTAzMDcyMjAxMzAyMFoXDTAzMTEwMzAxMzUyMF
+ owgg27MCMCBDi/biUXDTAwMDMwNjA2MjEzM1owDDAKBgNVHRUEAwoBBDAjAgQ5Il0KFw0wMDA1MjM
+ wODAwNDNaMAwwCgYDVR0VBAMKAQQwIwIEOSo6ZxcNMDAwNTI5MDIyNTQzWjAMMAoGA1UdFQQDCgEE
+ MCMCBDkx1QAXDTAwMDUyOTAzMzYwMVowDDAKBgNVHRUEAwoBBDAjAgQ5Pd7GFw0wMDA2MDcwNTM3M
+ jRaMAwwCgYDVR0VBAMKAQQwIwIEOUcavBcNMDAwNjE0MDc0MjExWjAMMAoGA1UdFQQDCgEEMCMCBD
+ lIlLYXDTAwMDYxNTA4MzY1NlowDDAKBgNVHRUEAwoBBDAjAgQ5SeOkFw0wMDA2MTYwODIzMDVaMAw
+ wCgYDVR0VBAMKAQQwIwIEOUiGjRcNMDAwNjE2MDgyMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ30oX
+ DTAwMDYyOTA4MDQyM1owDDAKBgNVHRUEAwoBBDAjAgQ5SdUjFw0wMDA2MjkwODA1NDVaMAwwCgYDV
+ R0VBAMKAQQwIwIEOTHlfRcNMDAwNjMwMDYwNjA1WjAMMAoGA1UdFQQDCgEEMCMCBDkzV6EXDTAwMD
+ YzMDA2MDYxMVowDDAKBgNVHRUEAwoBBDAjAgQ5SIFOFw0wMDA2MzAwNjA2MjFaMAwwCgYDVR0VBAM
+ KAQQwIwIEOUiCbBcNMDAwNjMwMDYwNjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlIgzkXDTAwMDYzMDA2
+ MDYzNlowDDAKBgNVHRUEAwoBBDAjAgQ5SIQEFw0wMDA2MzAwNjA2NDFaMAwwCgYDVR0VBAMKAQQwI
+ wIEOUiFBBcNMDAwNjMwMDYwNjQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlIhfQXDTAwMDYzMDA2MDY1NV
+ owDDAKBgNVHRUEAwoBBDAjAgQ5SIcmFw0wMDA2MzAwNjA3MDJaMAwwCgYDVR0VBAMKAQQwIwIEOUi
+ H4hcNMDAwNjMwMDYwNzA4WjAMMAoGA1UdFQQDCgEEMCMCBDlIiGUXDTAwMDYzMDA2MDcxNFowDDAK
+ BgNVHRUEAwoBBDAjAgQ5SIjaFw0wMDA2MzAwNjA3NDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiJhRcNM
+ DAwNjMwMDYwNzU3WjAMMAoGA1UdFQQDCgEEMCMCBDlIjoIXDTAwMDYzMDA2MDgwNFowDDAKBgNVHR
+ UEAwoBBDAjAgQ5SI89Fw0wMDA2MzAwNjA4MTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiP1RcNMDAwNjM
+ wMDYwODE1WjAMMAoGA1UdFQQDCgEEMCMCBDlIkEoXDTAwMDYzMDA2MDg0NVowDDAKBgNVHRUEAwoB
+ BDAjAgQ5SJC7Fw0wMDA2MzAwNjA4NTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiReRcNMDAwNjMwMDYwO
+ DU2WjAMMAoGA1UdFQQDCgEEMCMCBDlIkgMXDTAwMDYzMDA2MDkwNFowDDAKBgNVHRUEAwoBBDAjAg
+ Q5SJKqFw0wMDA2MzAwNjA5MDlaMAwwCgYDVR0VBAMKAQQwIwIEOUiTJhcNMDAwNjMwMDYwOTE2WjA
+ MMAoGA1UdFQQDCgEEMCMCBDlIk5AXDTAwMDYzMDA2MDkyMVowDDAKBgNVHRUEAwoBBDAjAgQ5SJQ3
+ Fw0wMDA2MzAwNjA5MjZaMAwwCgYDVR0VBAMKAQQwIwIEOUiVXhcNMDAwNjMwMDYwOTMyWjAMMAoGA
+ 1UdFQQDCgEEMCMCBDlIlgcXDTAwMDYzMDA2MDkzOFowDDAKBgNVHRUEAwoBBDAjAgQ5SJazFw0wMD
+ A2MzAwNjA5NDZaMAwwCgYDVR0VBAMKAQQwIwIEOUiXPxcNMDAwNjMwMDYwOTUxWjAMMAoGA1UdFQQ
+ DCgEEMCMCBDlIl7IXDTAwMDYzMDA2MDk1OFowDDAKBgNVHRUEAwoBBDAjAgQ5SJg0Fw0wMDA2MzAw
+ NjEwMDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiZBBcNMDAwNjMwMDYxMDA5WjAMMAoGA1UdFQQDCgEEM
+ CMCBDlJzksXDTAwMDYzMDA2MTAxNVowDDAKBgNVHRUEAwoBBDAjAgQ5Sc64Fw0wMDA2MzAwNjEwMj
+ FaMAwwCgYDVR0VBAMKAQQwIwIEOUnPVxcNMDAwNjMwMDYxMDI3WjAMMAoGA1UdFQQDCgEEMCMCBDl
+ J0BAXDTAwMDYzMDA2MTAzNVowDDAKBgNVHRUEAwoBBDAjAgQ5SdDKFw0wMDA2MzAwNjEwNDNaMAww
+ CgYDVR0VBAMKAQQwIwIEOUnRZRcNMDAwNjMwMDYxMDQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ0d0XD
+ TAwMDYzMDA2MTA1N1owDDAKBgNVHRUEAwoBBDAjAgQ5SdJ4Fw0wMDA2MzAwNjExMTVaMAwwCgYDVR
+ 0VBAMKAQQwIwIEOUnTDBcNMDAwNjMwMDYxMTIxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ04oXDTAwMDY
+ zMDA2MTEyN1owDDAKBgNVHRUEAwoBBDAjAgQ5SdQSFw0wMDA2MzAwNjExMzNaMAwwCgYDVR0VBAMK
+ AQQwIwIEOUnUoBcNMDAwNjMwMDYxMTM5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ2SQXDTAwMDYzMDA2M
+ TE1M1owDDAKBgNVHRUEAwoBBDAjAgQ5SdmwFw0wMDA2MzAwNjEyMDVaMAwwCgYDVR0VBAMKAQQwIw
+ IEOUnaTBcNMDAwNjMwMDYxMjExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ2vYXDTAwMDYzMDA2MTIxN1o
+ wDDAKBgNVHRUEAwoBBDAjAgQ5SducFw0wMDA2MzAwNjEyMjNaMAwwCgYDVR0VBAMKAQQwIwIEOUnc
+ IRcNMDAwNjMwMDYxMjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3KQXDTAwMDYzMDA2MTIzM1owDDAKB
+ gNVHRUEAwoBBDAjAgQ5Sd2xFw0wMDA2MzAwNjEyNDBaMAwwCgYDVR0VBAMKAQQwIwIEOUneRBcNMD
+ AwNjMwMDYxMjQ1WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3skXDTAwMDYzMDA2MTI1MVowDDAKBgNVHRU
+ EAwoBBDAjAgQ5Sd/IFw0wMDA2MzAwNjEzMDJaMAwwCgYDVR0VBAMKAQQwIwIEOUngPRcNMDAwNjMw
+ MDYxMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4M8XDTAwMDYzMDA2MTMyMFowDDAKBgNVHRUEAwoBB
+ DAjAgQ5SeE/Fw0wMDA2MzAwNjEzMjVaMAwwCgYDVR0VBAMKAQQwIwIEOUnh2BcNMDAwNjMwMDYxMz
+ MxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4mgXDTAwMDYzMDA2MTMzOVowDDAKBgNVHRUEAwoBBDAjAgQ
+ 5SeQvFw0wMDA2MzAwNjEzNDRaMAwwCgYDVR0VBAMKAQQwIwIEOVsGJRcNMDAwNjMwMDYxMzUwWjAM
+ MAoGA1UdFQQDCgEEMCMCBDlbBusXDTAwMDYzMDA2MTM1NlowDDAKBgNVHRUEAwoBBDAjAgQ5XEKPF
+ w0wMDA3MTMwOTAwMzhaMAwwCgYDVR0VBAMKAQQwIwIEOVxEKRcNMDAwNzEzMDkwMDQ1WjAMMAoGA1
+ UdFQQDCgEEMCMCBDlcRukXDTAwMDcyNjA2MjkyN1owDDAKBgNVHRUEAwoBBDAjAgQ5fohgFw0wMDA
+ 3MjYwNjQ2NTFaMAwwCgYDVR0VBAMKAQQwIwIEOaNqPBcNMDAwODIzMDYwOTQxWjAMMAoGA1UdFQQD
+ CgEFMCMCBDlcX2QXDTAwMDgzMTA3MTM1OFowDDAKBgNVHRUEAwoBBDAjAgQ5YsflFw0wMDA5MDEwM
+ TQwMjRaMAwwCgYDVR0VBAMKAQQwIwIEOWGHDRcNMDAwOTA2MDcwMTE2WjAMMAoGA1UdFQQDCgEEMC
+ MCBDliz/4XDTAwMDkwNjA3MDcwNVowDDAKBgNVHRUEAwoBBDAjAgQ5m3S6Fw0wMDA5MjAwNzA2NTd
+ aMAwwCgYDVR0VBAMKAQQwIwIEOy6/hhcNMDEwNzAzMDYxMDQyWjAMMAoGA1UdFQQDCgEEMCMCBDtB
+ Yw4XDTAxMDcwMzA2MTkxNlowDDAKBgNVHRUEAwoBBDAjAgQ7MEG6Fw0wMTA3MTAwODA5NTNaMAwwC
+ gYDVR0VBAMKAQQwIwIEOy68CxcNMDEwNzExMDYxMzI5WjAMMAoGA1UdFQQDCgEEMCMCBDswSOsXDT
+ AxMDgwMTA0MTkyM1owDDAKBgNVHRUEAwoBBTAjAgQ7MYgeFw0wMTA4MDEwNDIwMDJaMAwwCgYDVR0
+ VBAMKAQQwIwIEOzGHeBcNMDEwODAyMDI0NTM4WjAMMAoGA1UdFQQDCgEEMCMCBDsuveEXDTAxMDgz
+ MDA2MjIwOFowDDAKBgNVHRUEAwoBBDAjAgQ7jdxLFw0wMTA4MzAwNjQzMjRaMAwwCgYDVR0VBAMKA
+ QQwIwIEOy67QxcNMDExMTIxMDYyMDUzWjAMMAoGA1UdFQQDCgEEMCMCBDsDNXcXDTAyMDUxNzA4ND
+ Y0MlowDDAKBgNVHRUEAwoBBDAjAgQ7AzXMFw0wMjA1MTcwODQ2NTdaMAwwCgYDVR0VBAMKAQSgMjA
+ wMAsGA1UdFAQEAgIQoDATBgNVHSMEDDAKgAhISAKVrWisNzAMBgNVHRwBAf8EAjAAMA0GCSqGSIb3
+ DQEBBQUAA4IBAQA1xNXgyrtVB5LSOc76JF+aJzf8IfJGqF04CMzbo4lDpec/LgOrTSFV223ccJzuq
+ cnxGUfDbXFfSWDHGnj9HLLTCkrS3clL1TPVjGXg5mFu1l6DCfcP2v4i4dlradNYDQg/AVBoJsYa3l
+ efSFHw8RFXNHJWwIjJA6J0CBJ/8Uq2ywr8umdndb10RLtPWp66A7wxu7OvTjt68d3LgSniQ0mIJCn
+ 4ooE30oF/ew0EznbxlSCNRPpB8jYYJTibGrTUVU43lr8h3URIgBkA4InOhuDv0ePMSCDSxBUhY0+G
+ eKo+YiXHy4SGUGLakahuq/hlGTRJJUddqFA1dNZdOUl23nVE
+
+dn: cn=charlie,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: charlie
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+
+dn: cn=beta,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: beta
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+certificateRevocationList;binary:: MIIP0TCCDrkCAQEwDQYJKoZIhvcNAQEFBQAwgZMxCzA
+ JBgNVBAYTAkFVMSswKQYDVQQKEyJDZXJ0aWZpY2F0ZXMgQXVzdHJhbGlhIFB0eSBMaW1pdGVkMSUw
+ IwYDVQQDExxDQVBMIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTAwLgYKCZImiZPyLGQBAxQgY2FAY
+ 2VydGlmaWNhdGVzLWF1c3RyYWxpYS5jb20uYXUXDTAzMDcyMjAxMzAyMFoXDTAzMTEwMzAxMzUyMF
+ owgg27MCMCBDi/biUXDTAwMDMwNjA2MjEzM1owDDAKBgNVHRUEAwoBBDAjAgQ5Il0KFw0wMDA1MjM
+ wODAwNDNaMAwwCgYDVR0VBAMKAQQwIwIEOSo6ZxcNMDAwNTI5MDIyNTQzWjAMMAoGA1UdFQQDCgEE
+ MCMCBDkx1QAXDTAwMDUyOTAzMzYwMVowDDAKBgNVHRUEAwoBBDAjAgQ5Pd7GFw0wMDA2MDcwNTM3M
+ jRaMAwwCgYDVR0VBAMKAQQwIwIEOUcavBcNMDAwNjE0MDc0MjExWjAMMAoGA1UdFQQDCgEEMCMCBD
+ lIlLYXDTAwMDYxNTA4MzY1NlowDDAKBgNVHRUEAwoBBDAjAgQ5SeOkFw0wMDA2MTYwODIzMDVaMAw
+ wCgYDVR0VBAMKAQQwIwIEOUiGjRcNMDAwNjE2MDgyMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ30oX
+ DTAwMDYyOTA4MDQyM1owDDAKBgNVHRUEAwoBBDAjAgQ5SdUjFw0wMDA2MjkwODA1NDVaMAwwCgYDV
+ R0VBAMKAQQwIwIEOTHlfRcNMDAwNjMwMDYwNjA1WjAMMAoGA1UdFQQDCgEEMCMCBDkzV6EXDTAwMD
+ YzMDA2MDYxMVowDDAKBgNVHRUEAwoBBDAjAgQ5SIFOFw0wMDA2MzAwNjA2MjFaMAwwCgYDVR0VBAM
+ KAQQwIwIEOUiCbBcNMDAwNjMwMDYwNjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlIgzkXDTAwMDYzMDA2
+ MDYzNlowDDAKBgNVHRUEAwoBBDAjAgQ5SIQEFw0wMDA2MzAwNjA2NDFaMAwwCgYDVR0VBAMKAQQwI
+ wIEOUiFBBcNMDAwNjMwMDYwNjQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlIhfQXDTAwMDYzMDA2MDY1NV
+ owDDAKBgNVHRUEAwoBBDAjAgQ5SIcmFw0wMDA2MzAwNjA3MDJaMAwwCgYDVR0VBAMKAQQwIwIEOUi
+ H4hcNMDAwNjMwMDYwNzA4WjAMMAoGA1UdFQQDCgEEMCMCBDlIiGUXDTAwMDYzMDA2MDcxNFowDDAK
+ BgNVHRUEAwoBBDAjAgQ5SIjaFw0wMDA2MzAwNjA3NDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiJhRcNM
+ DAwNjMwMDYwNzU3WjAMMAoGA1UdFQQDCgEEMCMCBDlIjoIXDTAwMDYzMDA2MDgwNFowDDAKBgNVHR
+ UEAwoBBDAjAgQ5SI89Fw0wMDA2MzAwNjA4MTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiP1RcNMDAwNjM
+ wMDYwODE1WjAMMAoGA1UdFQQDCgEEMCMCBDlIkEoXDTAwMDYzMDA2MDg0NVowDDAKBgNVHRUEAwoB
+ BDAjAgQ5SJC7Fw0wMDA2MzAwNjA4NTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiReRcNMDAwNjMwMDYwO
+ DU2WjAMMAoGA1UdFQQDCgEEMCMCBDlIkgMXDTAwMDYzMDA2MDkwNFowDDAKBgNVHRUEAwoBBDAjAg
+ Q5SJKqFw0wMDA2MzAwNjA5MDlaMAwwCgYDVR0VBAMKAQQwIwIEOUiTJhcNMDAwNjMwMDYwOTE2WjA
+ MMAoGA1UdFQQDCgEEMCMCBDlIk5AXDTAwMDYzMDA2MDkyMVowDDAKBgNVHRUEAwoBBDAjAgQ5SJQ3
+ Fw0wMDA2MzAwNjA5MjZaMAwwCgYDVR0VBAMKAQQwIwIEOUiVXhcNMDAwNjMwMDYwOTMyWjAMMAoGA
+ 1UdFQQDCgEEMCMCBDlIlgcXDTAwMDYzMDA2MDkzOFowDDAKBgNVHRUEAwoBBDAjAgQ5SJazFw0wMD
+ A2MzAwNjA5NDZaMAwwCgYDVR0VBAMKAQQwIwIEOUiXPxcNMDAwNjMwMDYwOTUxWjAMMAoGA1UdFQQ
+ DCgEEMCMCBDlIl7IXDTAwMDYzMDA2MDk1OFowDDAKBgNVHRUEAwoBBDAjAgQ5SJg0Fw0wMDA2MzAw
+ NjEwMDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiZBBcNMDAwNjMwMDYxMDA5WjAMMAoGA1UdFQQDCgEEM
+ CMCBDlJzksXDTAwMDYzMDA2MTAxNVowDDAKBgNVHRUEAwoBBDAjAgQ5Sc64Fw0wMDA2MzAwNjEwMj
+ FaMAwwCgYDVR0VBAMKAQQwIwIEOUnPVxcNMDAwNjMwMDYxMDI3WjAMMAoGA1UdFQQDCgEEMCMCBDl
+ J0BAXDTAwMDYzMDA2MTAzNVowDDAKBgNVHRUEAwoBBDAjAgQ5SdDKFw0wMDA2MzAwNjEwNDNaMAww
+ CgYDVR0VBAMKAQQwIwIEOUnRZRcNMDAwNjMwMDYxMDQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ0d0XD
+ TAwMDYzMDA2MTA1N1owDDAKBgNVHRUEAwoBBDAjAgQ5SdJ4Fw0wMDA2MzAwNjExMTVaMAwwCgYDVR
+ 0VBAMKAQQwIwIEOUnTDBcNMDAwNjMwMDYxMTIxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ04oXDTAwMDY
+ zMDA2MTEyN1owDDAKBgNVHRUEAwoBBDAjAgQ5SdQSFw0wMDA2MzAwNjExMzNaMAwwCgYDVR0VBAMK
+ AQQwIwIEOUnUoBcNMDAwNjMwMDYxMTM5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ2SQXDTAwMDYzMDA2M
+ TE1M1owDDAKBgNVHRUEAwoBBDAjAgQ5SdmwFw0wMDA2MzAwNjEyMDVaMAwwCgYDVR0VBAMKAQQwIw
+ IEOUnaTBcNMDAwNjMwMDYxMjExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ2vYXDTAwMDYzMDA2MTIxN1o
+ wDDAKBgNVHRUEAwoBBDAjAgQ5SducFw0wMDA2MzAwNjEyMjNaMAwwCgYDVR0VBAMKAQQwIwIEOUnc
+ IRcNMDAwNjMwMDYxMjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3KQXDTAwMDYzMDA2MTIzM1owDDAKB
+ gNVHRUEAwoBBDAjAgQ5Sd2xFw0wMDA2MzAwNjEyNDBaMAwwCgYDVR0VBAMKAQQwIwIEOUneRBcNMD
+ AwNjMwMDYxMjQ1WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3skXDTAwMDYzMDA2MTI1MVowDDAKBgNVHRU
+ EAwoBBDAjAgQ5Sd/IFw0wMDA2MzAwNjEzMDJaMAwwCgYDVR0VBAMKAQQwIwIEOUngPRcNMDAwNjMw
+ MDYxMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4M8XDTAwMDYzMDA2MTMyMFowDDAKBgNVHRUEAwoBB
+ DAjAgQ5SeE/Fw0wMDA2MzAwNjEzMjVaMAwwCgYDVR0VBAMKAQQwIwIEOUnh2BcNMDAwNjMwMDYxMz
+ MxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4mgXDTAwMDYzMDA2MTMzOVowDDAKBgNVHRUEAwoBBDAjAgQ
+ 5SeQvFw0wMDA2MzAwNjEzNDRaMAwwCgYDVR0VBAMKAQQwIwIEOVsGJRcNMDAwNjMwMDYxMzUwWjAM
+ MAoGA1UdFQQDCgEEMCMCBDlbBusXDTAwMDYzMDA2MTM1NlowDDAKBgNVHRUEAwoBBDAjAgQ5XEKPF
+ w0wMDA3MTMwOTAwMzhaMAwwCgYDVR0VBAMKAQQwIwIEOVxEKRcNMDAwNzEzMDkwMDQ1WjAMMAoGA1
+ UdFQQDCgEEMCMCBDlcRukXDTAwMDcyNjA2MjkyN1owDDAKBgNVHRUEAwoBBDAjAgQ5fohgFw0wMDA
+ 3MjYwNjQ2NTFaMAwwCgYDVR0VBAMKAQQwIwIEOaNqPBcNMDAwODIzMDYwOTQxWjAMMAoGA1UdFQQD
+ CgEFMCMCBDlcX2QXDTAwMDgzMTA3MTM1OFowDDAKBgNVHRUEAwoBBDAjAgQ5YsflFw0wMDA5MDEwM
+ TQwMjRaMAwwCgYDVR0VBAMKAQQwIwIEOWGHDRcNMDAwOTA2MDcwMTE2WjAMMAoGA1UdFQQDCgEEMC
+ MCBDliz/4XDTAwMDkwNjA3MDcwNVowDDAKBgNVHRUEAwoBBDAjAgQ5m3S6Fw0wMDA5MjAwNzA2NTd
+ aMAwwCgYDVR0VBAMKAQQwIwIEOy6/hhcNMDEwNzAzMDYxMDQyWjAMMAoGA1UdFQQDCgEEMCMCBDtB
+ Yw4XDTAxMDcwMzA2MTkxNlowDDAKBgNVHRUEAwoBBDAjAgQ7MEG6Fw0wMTA3MTAwODA5NTNaMAwwC
+ gYDVR0VBAMKAQQwIwIEOy68CxcNMDEwNzExMDYxMzI5WjAMMAoGA1UdFQQDCgEEMCMCBDswSOsXDT
+ AxMDgwMTA0MTkyM1owDDAKBgNVHRUEAwoBBTAjAgQ7MYgeFw0wMTA4MDEwNDIwMDJaMAwwCgYDVR0
+ VBAMKAQQwIwIEOzGHeBcNMDEwODAyMDI0NTM4WjAMMAoGA1UdFQQDCgEEMCMCBDsuveEXDTAxMDgz
+ MDA2MjIwOFowDDAKBgNVHRUEAwoBBDAjAgQ7jdxLFw0wMTA4MzAwNjQzMjRaMAwwCgYDVR0VBAMKA
+ QQwIwIEOy67QxcNMDExMTIxMDYyMDUzWjAMMAoGA1UdFQQDCgEEMCMCBDsDNXcXDTAyMDUxNzA4ND
+ Y0MlowDDAKBgNVHRUEAwoBBDAjAgQ7AzXMFw0wMjA1MTcwODQ2NTdaMAwwCgYDVR0VBAMKAQSgMjA
+ wMAsGA1UdFAQEAgIQoDATBgNVHSMEDDAKgAhISAKVrWisNzAMBgNVHRwBAf8EAjAAMA0GCSqGSIb3
+ DQEBBQUAA4IBAQA1xNXgyrtVB5LSOc76JF+aJzf8IfJGqF04CMzbo4lDpec/LgOrTSFV223ccJzuq
+ cnxGUfDbXFfSWDHGnj9HLLTCkrS3clL1TPVjGXg5mFu1l6DCfcP2v4i4dlradNYDQg/AVBoJsYa3l
+ efSFHw8RFXNHJWwIjJA6J0CBJ/8Uq2ywr8umdndb10RLtPWp66A7wxu7OvTjt68d3LgSniQ0mIJCn
+ 4ooE30oF/ew0EznbxlSCNRPpB8jYYJTibGrTUVU43lr8h3URIgBkA4InOhuDv0ePMSCDSxBUhY0+G
+ eKo+YiXHy4SGUGLakahuq/hlGTRJJUddqFA1dNZdOUl23nVE
+
+dn: cn=charlie,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: charlie
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+
+dn: cn=beta,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: beta
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+certificateRevocationList;binary:: MIIP0TCCDrkCAQEwDQYJKoZIhvcNAQEFBQAwgZMxCzA
+ JBgNVBAYTAkFVMSswKQYDVQQKEyJDZXJ0aWZpY2F0ZXMgQXVzdHJhbGlhIFB0eSBMaW1pdGVkMSUw
+ IwYDVQQDExxDQVBMIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTAwLgYKCZImiZPyLGQBAxQgY2FAY
+ 2VydGlmaWNhdGVzLWF1c3RyYWxpYS5jb20uYXUXDTAzMDcyMjAxMzAyMFoXDTAzMTEwMzAxMzUyMF
+ owgg27MCMCBDi/biUXDTAwMDMwNjA2MjEzM1owDDAKBgNVHRUEAwoBBDAjAgQ5Il0KFw0wMDA1MjM
+ wODAwNDNaMAwwCgYDVR0VBAMKAQQwIwIEOSo6ZxcNMDAwNTI5MDIyNTQzWjAMMAoGA1UdFQQDCgEE
+ MCMCBDkx1QAXDTAwMDUyOTAzMzYwMVowDDAKBgNVHRUEAwoBBDAjAgQ5Pd7GFw0wMDA2MDcwNTM3M
+ jRaMAwwCgYDVR0VBAMKAQQwIwIEOUcavBcNMDAwNjE0MDc0MjExWjAMMAoGA1UdFQQDCgEEMCMCBD
+ lIlLYXDTAwMDYxNTA4MzY1NlowDDAKBgNVHRUEAwoBBDAjAgQ5SeOkFw0wMDA2MTYwODIzMDVaMAw
+ wCgYDVR0VBAMKAQQwIwIEOUiGjRcNMDAwNjE2MDgyMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ30oX
+ DTAwMDYyOTA4MDQyM1owDDAKBgNVHRUEAwoBBDAjAgQ5SdUjFw0wMDA2MjkwODA1NDVaMAwwCgYDV
+ R0VBAMKAQQwIwIEOTHlfRcNMDAwNjMwMDYwNjA1WjAMMAoGA1UdFQQDCgEEMCMCBDkzV6EXDTAwMD
+ YzMDA2MDYxMVowDDAKBgNVHRUEAwoBBDAjAgQ5SIFOFw0wMDA2MzAwNjA2MjFaMAwwCgYDVR0VBAM
+ KAQQwIwIEOUiCbBcNMDAwNjMwMDYwNjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlIgzkXDTAwMDYzMDA2
+ MDYzNlowDDAKBgNVHRUEAwoBBDAjAgQ5SIQEFw0wMDA2MzAwNjA2NDFaMAwwCgYDVR0VBAMKAQQwI
+ wIEOUiFBBcNMDAwNjMwMDYwNjQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlIhfQXDTAwMDYzMDA2MDY1NV
+ owDDAKBgNVHRUEAwoBBDAjAgQ5SIcmFw0wMDA2MzAwNjA3MDJaMAwwCgYDVR0VBAMKAQQwIwIEOUi
+ H4hcNMDAwNjMwMDYwNzA4WjAMMAoGA1UdFQQDCgEEMCMCBDlIiGUXDTAwMDYzMDA2MDcxNFowDDAK
+ BgNVHRUEAwoBBDAjAgQ5SIjaFw0wMDA2MzAwNjA3NDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiJhRcNM
+ DAwNjMwMDYwNzU3WjAMMAoGA1UdFQQDCgEEMCMCBDlIjoIXDTAwMDYzMDA2MDgwNFowDDAKBgNVHR
+ UEAwoBBDAjAgQ5SI89Fw0wMDA2MzAwNjA4MTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiP1RcNMDAwNjM
+ wMDYwODE1WjAMMAoGA1UdFQQDCgEEMCMCBDlIkEoXDTAwMDYzMDA2MDg0NVowDDAKBgNVHRUEAwoB
+ BDAjAgQ5SJC7Fw0wMDA2MzAwNjA4NTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiReRcNMDAwNjMwMDYwO
+ DU2WjAMMAoGA1UdFQQDCgEEMCMCBDlIkgMXDTAwMDYzMDA2MDkwNFowDDAKBgNVHRUEAwoBBDAjAg
+ Q5SJKqFw0wMDA2MzAwNjA5MDlaMAwwCgYDVR0VBAMKAQQwIwIEOUiTJhcNMDAwNjMwMDYwOTE2WjA
+ MMAoGA1UdFQQDCgEEMCMCBDlIk5AXDTAwMDYzMDA2MDkyMVowDDAKBgNVHRUEAwoBBDAjAgQ5SJQ3
+ Fw0wMDA2MzAwNjA5MjZaMAwwCgYDVR0VBAMKAQQwIwIEOUiVXhcNMDAwNjMwMDYwOTMyWjAMMAoGA
+ 1UdFQQDCgEEMCMCBDlIlgcXDTAwMDYzMDA2MDkzOFowDDAKBgNVHRUEAwoBBDAjAgQ5SJazFw0wMD
+ A2MzAwNjA5NDZaMAwwCgYDVR0VBAMKAQQwIwIEOUiXPxcNMDAwNjMwMDYwOTUxWjAMMAoGA1UdFQQ
+ DCgEEMCMCBDlIl7IXDTAwMDYzMDA2MDk1OFowDDAKBgNVHRUEAwoBBDAjAgQ5SJg0Fw0wMDA2MzAw
+ NjEwMDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiZBBcNMDAwNjMwMDYxMDA5WjAMMAoGA1UdFQQDCgEEM
+ CMCBDlJzksXDTAwMDYzMDA2MTAxNVowDDAKBgNVHRUEAwoBBDAjAgQ5Sc64Fw0wMDA2MzAwNjEwMj
+ FaMAwwCgYDVR0VBAMKAQQwIwIEOUnPVxcNMDAwNjMwMDYxMDI3WjAMMAoGA1UdFQQDCgEEMCMCBDl
+ J0BAXDTAwMDYzMDA2MTAzNVowDDAKBgNVHRUEAwoBBDAjAgQ5SdDKFw0wMDA2MzAwNjEwNDNaMAww
+ CgYDVR0VBAMKAQQwIwIEOUnRZRcNMDAwNjMwMDYxMDQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ0d0XD
+ TAwMDYzMDA2MTA1N1owDDAKBgNVHRUEAwoBBDAjAgQ5SdJ4Fw0wMDA2MzAwNjExMTVaMAwwCgYDVR
+ 0VBAMKAQQwIwIEOUnTDBcNMDAwNjMwMDYxMTIxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ04oXDTAwMDY
+ zMDA2MTEyN1owDDAKBgNVHRUEAwoBBDAjAgQ5SdQSFw0wMDA2MzAwNjExMzNaMAwwCgYDVR0VBAMK
+ AQQwIwIEOUnUoBcNMDAwNjMwMDYxMTM5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ2SQXDTAwMDYzMDA2M
+ TE1M1owDDAKBgNVHRUEAwoBBDAjAgQ5SdmwFw0wMDA2MzAwNjEyMDVaMAwwCgYDVR0VBAMKAQQwIw
+ IEOUnaTBcNMDAwNjMwMDYxMjExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ2vYXDTAwMDYzMDA2MTIxN1o
+ wDDAKBgNVHRUEAwoBBDAjAgQ5SducFw0wMDA2MzAwNjEyMjNaMAwwCgYDVR0VBAMKAQQwIwIEOUnc
+ IRcNMDAwNjMwMDYxMjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3KQXDTAwMDYzMDA2MTIzM1owDDAKB
+ gNVHRUEAwoBBDAjAgQ5Sd2xFw0wMDA2MzAwNjEyNDBaMAwwCgYDVR0VBAMKAQQwIwIEOUneRBcNMD
+ AwNjMwMDYxMjQ1WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3skXDTAwMDYzMDA2MTI1MVowDDAKBgNVHRU
+ EAwoBBDAjAgQ5Sd/IFw0wMDA2MzAwNjEzMDJaMAwwCgYDVR0VBAMKAQQwIwIEOUngPRcNMDAwNjMw
+ MDYxMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4M8XDTAwMDYzMDA2MTMyMFowDDAKBgNVHRUEAwoBB
+ DAjAgQ5SeE/Fw0wMDA2MzAwNjEzMjVaMAwwCgYDVR0VBAMKAQQwIwIEOUnh2BcNMDAwNjMwMDYxMz
+ MxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4mgXDTAwMDYzMDA2MTMzOVowDDAKBgNVHRUEAwoBBDAjAgQ
+ 5SeQvFw0wMDA2MzAwNjEzNDRaMAwwCgYDVR0VBAMKAQQwIwIEOVsGJRcNMDAwNjMwMDYxMzUwWjAM
+ MAoGA1UdFQQDCgEEMCMCBDlbBusXDTAwMDYzMDA2MTM1NlowDDAKBgNVHRUEAwoBBDAjAgQ5XEKPF
+ w0wMDA3MTMwOTAwMzhaMAwwCgYDVR0VBAMKAQQwIwIEOVxEKRcNMDAwNzEzMDkwMDQ1WjAMMAoGA1
+ UdFQQDCgEEMCMCBDlcRukXDTAwMDcyNjA2MjkyN1owDDAKBgNVHRUEAwoBBDAjAgQ5fohgFw0wMDA
+ 3MjYwNjQ2NTFaMAwwCgYDVR0VBAMKAQQwIwIEOaNqPBcNMDAwODIzMDYwOTQxWjAMMAoGA1UdFQQD
+ CgEFMCMCBDlcX2QXDTAwMDgzMTA3MTM1OFowDDAKBgNVHRUEAwoBBDAjAgQ5YsflFw0wMDA5MDEwM
+ TQwMjRaMAwwCgYDVR0VBAMKAQQwIwIEOWGHDRcNMDAwOTA2MDcwMTE2WjAMMAoGA1UdFQQDCgEEMC
+ MCBDliz/4XDTAwMDkwNjA3MDcwNVowDDAKBgNVHRUEAwoBBDAjAgQ5m3S6Fw0wMDA5MjAwNzA2NTd
+ aMAwwCgYDVR0VBAMKAQQwIwIEOy6/hhcNMDEwNzAzMDYxMDQyWjAMMAoGA1UdFQQDCgEEMCMCBDtB
+ Yw4XDTAxMDcwMzA2MTkxNlowDDAKBgNVHRUEAwoBBDAjAgQ7MEG6Fw0wMTA3MTAwODA5NTNaMAwwC
+ gYDVR0VBAMKAQQwIwIEOy68CxcNMDEwNzExMDYxMzI5WjAMMAoGA1UdFQQDCgEEMCMCBDswSOsXDT
+ AxMDgwMTA0MTkyM1owDDAKBgNVHRUEAwoBBTAjAgQ7MYgeFw0wMTA4MDEwNDIwMDJaMAwwCgYDVR0
+ VBAMKAQQwIwIEOzGHeBcNMDEwODAyMDI0NTM4WjAMMAoGA1UdFQQDCgEEMCMCBDsuveEXDTAxMDgz
+ MDA2MjIwOFowDDAKBgNVHRUEAwoBBDAjAgQ7jdxLFw0wMTA4MzAwNjQzMjRaMAwwCgYDVR0VBAMKA
+ QQwIwIEOy67QxcNMDExMTIxMDYyMDUzWjAMMAoGA1UdFQQDCgEEMCMCBDsDNXcXDTAyMDUxNzA4ND
+ Y0MlowDDAKBgNVHRUEAwoBBDAjAgQ7AzXMFw0wMjA1MTcwODQ2NTdaMAwwCgYDVR0VBAMKAQSgMjA
+ wMAsGA1UdFAQEAgIQoDATBgNVHSMEDDAKgAhISAKVrWisNzAMBgNVHRwBAf8EAjAAMA0GCSqGSIb3
+ DQEBBQUAA4IBAQA1xNXgyrtVB5LSOc76JF+aJzf8IfJGqF04CMzbo4lDpec/LgOrTSFV223ccJzuq
+ cnxGUfDbXFfSWDHGnj9HLLTCkrS3clL1TPVjGXg5mFu1l6DCfcP2v4i4dlradNYDQg/AVBoJsYa3l
+ efSFHw8RFXNHJWwIjJA6J0CBJ/8Uq2ywr8umdndb10RLtPWp66A7wxu7OvTjt68d3LgSniQ0mIJCn
+ 4ooE30oF/ew0EznbxlSCNRPpB8jYYJTibGrTUVU43lr8h3URIgBkA4InOhuDv0ePMSCDSxBUhY0+G
+ eKo+YiXHy4SGUGLakahuq/hlGTRJJUddqFA1dNZdOUl23nVE
+
+dn: cn=charlie,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: charlie
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+
+dn: cn=beta,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: beta
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+certificateRevocationList;binary:: MIIP0TCCDrkCAQEwDQYJKoZIhvcNAQEFBQAwgZMxCzA
+ JBgNVBAYTAkFVMSswKQYDVQQKEyJDZXJ0aWZpY2F0ZXMgQXVzdHJhbGlhIFB0eSBMaW1pdGVkMSUw
+ IwYDVQQDExxDQVBMIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTAwLgYKCZImiZPyLGQBAxQgY2FAY
+ 2VydGlmaWNhdGVzLWF1c3RyYWxpYS5jb20uYXUXDTAzMDcyMjAxMzAyMFoXDTAzMTEwMzAxMzUyMF
+ owgg27MCMCBDi/biUXDTAwMDMwNjA2MjEzM1owDDAKBgNVHRUEAwoBBDAjAgQ5Il0KFw0wMDA1MjM
+ wODAwNDNaMAwwCgYDVR0VBAMKAQQwIwIEOSo6ZxcNMDAwNTI5MDIyNTQzWjAMMAoGA1UdFQQDCgEE
+ MCMCBDkx1QAXDTAwMDUyOTAzMzYwMVowDDAKBgNVHRUEAwoBBDAjAgQ5Pd7GFw0wMDA2MDcwNTM3M
+ jRaMAwwCgYDVR0VBAMKAQQwIwIEOUcavBcNMDAwNjE0MDc0MjExWjAMMAoGA1UdFQQDCgEEMCMCBD
+ lIlLYXDTAwMDYxNTA4MzY1NlowDDAKBgNVHRUEAwoBBDAjAgQ5SeOkFw0wMDA2MTYwODIzMDVaMAw
+ wCgYDVR0VBAMKAQQwIwIEOUiGjRcNMDAwNjE2MDgyMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ30oX
+ DTAwMDYyOTA4MDQyM1owDDAKBgNVHRUEAwoBBDAjAgQ5SdUjFw0wMDA2MjkwODA1NDVaMAwwCgYDV
+ R0VBAMKAQQwIwIEOTHlfRcNMDAwNjMwMDYwNjA1WjAMMAoGA1UdFQQDCgEEMCMCBDkzV6EXDTAwMD
+ YzMDA2MDYxMVowDDAKBgNVHRUEAwoBBDAjAgQ5SIFOFw0wMDA2MzAwNjA2MjFaMAwwCgYDVR0VBAM
+ KAQQwIwIEOUiCbBcNMDAwNjMwMDYwNjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlIgzkXDTAwMDYzMDA2
+ MDYzNlowDDAKBgNVHRUEAwoBBDAjAgQ5SIQEFw0wMDA2MzAwNjA2NDFaMAwwCgYDVR0VBAMKAQQwI
+ wIEOUiFBBcNMDAwNjMwMDYwNjQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlIhfQXDTAwMDYzMDA2MDY1NV
+ owDDAKBgNVHRUEAwoBBDAjAgQ5SIcmFw0wMDA2MzAwNjA3MDJaMAwwCgYDVR0VBAMKAQQwIwIEOUi
+ H4hcNMDAwNjMwMDYwNzA4WjAMMAoGA1UdFQQDCgEEMCMCBDlIiGUXDTAwMDYzMDA2MDcxNFowDDAK
+ BgNVHRUEAwoBBDAjAgQ5SIjaFw0wMDA2MzAwNjA3NDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiJhRcNM
+ DAwNjMwMDYwNzU3WjAMMAoGA1UdFQQDCgEEMCMCBDlIjoIXDTAwMDYzMDA2MDgwNFowDDAKBgNVHR
+ UEAwoBBDAjAgQ5SI89Fw0wMDA2MzAwNjA4MTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiP1RcNMDAwNjM
+ wMDYwODE1WjAMMAoGA1UdFQQDCgEEMCMCBDlIkEoXDTAwMDYzMDA2MDg0NVowDDAKBgNVHRUEAwoB
+ BDAjAgQ5SJC7Fw0wMDA2MzAwNjA4NTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiReRcNMDAwNjMwMDYwO
+ DU2WjAMMAoGA1UdFQQDCgEEMCMCBDlIkgMXDTAwMDYzMDA2MDkwNFowDDAKBgNVHRUEAwoBBDAjAg
+ Q5SJKqFw0wMDA2MzAwNjA5MDlaMAwwCgYDVR0VBAMKAQQwIwIEOUiTJhcNMDAwNjMwMDYwOTE2WjA
+ MMAoGA1UdFQQDCgEEMCMCBDlIk5AXDTAwMDYzMDA2MDkyMVowDDAKBgNVHRUEAwoBBDAjAgQ5SJQ3
+ Fw0wMDA2MzAwNjA5MjZaMAwwCgYDVR0VBAMKAQQwIwIEOUiVXhcNMDAwNjMwMDYwOTMyWjAMMAoGA
+ 1UdFQQDCgEEMCMCBDlIlgcXDTAwMDYzMDA2MDkzOFowDDAKBgNVHRUEAwoBBDAjAgQ5SJazFw0wMD
+ A2MzAwNjA5NDZaMAwwCgYDVR0VBAMKAQQwIwIEOUiXPxcNMDAwNjMwMDYwOTUxWjAMMAoGA1UdFQQ
+ DCgEEMCMCBDlIl7IXDTAwMDYzMDA2MDk1OFowDDAKBgNVHRUEAwoBBDAjAgQ5SJg0Fw0wMDA2MzAw
+ NjEwMDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiZBBcNMDAwNjMwMDYxMDA5WjAMMAoGA1UdFQQDCgEEM
+ CMCBDlJzksXDTAwMDYzMDA2MTAxNVowDDAKBgNVHRUEAwoBBDAjAgQ5Sc64Fw0wMDA2MzAwNjEwMj
+ FaMAwwCgYDVR0VBAMKAQQwIwIEOUnPVxcNMDAwNjMwMDYxMDI3WjAMMAoGA1UdFQQDCgEEMCMCBDl
+ J0BAXDTAwMDYzMDA2MTAzNVowDDAKBgNVHRUEAwoBBDAjAgQ5SdDKFw0wMDA2MzAwNjEwNDNaMAww
+ CgYDVR0VBAMKAQQwIwIEOUnRZRcNMDAwNjMwMDYxMDQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ0d0XD
+ TAwMDYzMDA2MTA1N1owDDAKBgNVHRUEAwoBBDAjAgQ5SdJ4Fw0wMDA2MzAwNjExMTVaMAwwCgYDVR
+ 0VBAMKAQQwIwIEOUnTDBcNMDAwNjMwMDYxMTIxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ04oXDTAwMDY
+ zMDA2MTEyN1owDDAKBgNVHRUEAwoBBDAjAgQ5SdQSFw0wMDA2MzAwNjExMzNaMAwwCgYDVR0VBAMK
+ AQQwIwIEOUnUoBcNMDAwNjMwMDYxMTM5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ2SQXDTAwMDYzMDA2M
+ TE1M1owDDAKBgNVHRUEAwoBBDAjAgQ5SdmwFw0wMDA2MzAwNjEyMDVaMAwwCgYDVR0VBAMKAQQwIw
+ IEOUnaTBcNMDAwNjMwMDYxMjExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ2vYXDTAwMDYzMDA2MTIxN1o
+ wDDAKBgNVHRUEAwoBBDAjAgQ5SducFw0wMDA2MzAwNjEyMjNaMAwwCgYDVR0VBAMKAQQwIwIEOUnc
+ IRcNMDAwNjMwMDYxMjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3KQXDTAwMDYzMDA2MTIzM1owDDAKB
+ gNVHRUEAwoBBDAjAgQ5Sd2xFw0wMDA2MzAwNjEyNDBaMAwwCgYDVR0VBAMKAQQwIwIEOUneRBcNMD
+ AwNjMwMDYxMjQ1WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3skXDTAwMDYzMDA2MTI1MVowDDAKBgNVHRU
+ EAwoBBDAjAgQ5Sd/IFw0wMDA2MzAwNjEzMDJaMAwwCgYDVR0VBAMKAQQwIwIEOUngPRcNMDAwNjMw
+ MDYxMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4M8XDTAwMDYzMDA2MTMyMFowDDAKBgNVHRUEAwoBB
+ DAjAgQ5SeE/Fw0wMDA2MzAwNjEzMjVaMAwwCgYDVR0VBAMKAQQwIwIEOUnh2BcNMDAwNjMwMDYxMz
+ MxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4mgXDTAwMDYzMDA2MTMzOVowDDAKBgNVHRUEAwoBBDAjAgQ
+ 5SeQvFw0wMDA2MzAwNjEzNDRaMAwwCgYDVR0VBAMKAQQwIwIEOVsGJRcNMDAwNjMwMDYxMzUwWjAM
+ MAoGA1UdFQQDCgEEMCMCBDlbBusXDTAwMDYzMDA2MTM1NlowDDAKBgNVHRUEAwoBBDAjAgQ5XEKPF
+ w0wMDA3MTMwOTAwMzhaMAwwCgYDVR0VBAMKAQQwIwIEOVxEKRcNMDAwNzEzMDkwMDQ1WjAMMAoGA1
+ UdFQQDCgEEMCMCBDlcRukXDTAwMDcyNjA2MjkyN1owDDAKBgNVHRUEAwoBBDAjAgQ5fohgFw0wMDA
+ 3MjYwNjQ2NTFaMAwwCgYDVR0VBAMKAQQwIwIEOaNqPBcNMDAwODIzMDYwOTQxWjAMMAoGA1UdFQQD
+ CgEFMCMCBDlcX2QXDTAwMDgzMTA3MTM1OFowDDAKBgNVHRUEAwoBBDAjAgQ5YsflFw0wMDA5MDEwM
+ TQwMjRaMAwwCgYDVR0VBAMKAQQwIwIEOWGHDRcNMDAwOTA2MDcwMTE2WjAMMAoGA1UdFQQDCgEEMC
+ MCBDliz/4XDTAwMDkwNjA3MDcwNVowDDAKBgNVHRUEAwoBBDAjAgQ5m3S6Fw0wMDA5MjAwNzA2NTd
+ aMAwwCgYDVR0VBAMKAQQwIwIEOy6/hhcNMDEwNzAzMDYxMDQyWjAMMAoGA1UdFQQDCgEEMCMCBDtB
+ Yw4XDTAxMDcwMzA2MTkxNlowDDAKBgNVHRUEAwoBBDAjAgQ7MEG6Fw0wMTA3MTAwODA5NTNaMAwwC
+ gYDVR0VBAMKAQQwIwIEOy68CxcNMDEwNzExMDYxMzI5WjAMMAoGA1UdFQQDCgEEMCMCBDswSOsXDT
+ AxMDgwMTA0MTkyM1owDDAKBgNVHRUEAwoBBTAjAgQ7MYgeFw0wMTA4MDEwNDIwMDJaMAwwCgYDVR0
+ VBAMKAQQwIwIEOzGHeBcNMDEwODAyMDI0NTM4WjAMMAoGA1UdFQQDCgEEMCMCBDsuveEXDTAxMDgz
+ MDA2MjIwOFowDDAKBgNVHRUEAwoBBDAjAgQ7jdxLFw0wMTA4MzAwNjQzMjRaMAwwCgYDVR0VBAMKA
+ QQwIwIEOy67QxcNMDExMTIxMDYyMDUzWjAMMAoGA1UdFQQDCgEEMCMCBDsDNXcXDTAyMDUxNzA4ND
+ Y0MlowDDAKBgNVHRUEAwoBBDAjAgQ7AzXMFw0wMjA1MTcwODQ2NTdaMAwwCgYDVR0VBAMKAQSgMjA
+ wMAsGA1UdFAQEAgIQoDATBgNVHSMEDDAKgAhISAKVrWisNzAMBgNVHRwBAf8EAjAAMA0GCSqGSIb3
+ DQEBBQUAA4IBAQA1xNXgyrtVB5LSOc76JF+aJzf8IfJGqF04CMzbo4lDpec/LgOrTSFV223ccJzuq
+ cnxGUfDbXFfSWDHGnj9HLLTCkrS3clL1TPVjGXg5mFu1l6DCfcP2v4i4dlradNYDQg/AVBoJsYa3l
+ efSFHw8RFXNHJWwIjJA6J0CBJ/8Uq2ywr8umdndb10RLtPWp66A7wxu7OvTjt68d3LgSniQ0mIJCn
+ 4ooE30oF/ew0EznbxlSCNRPpB8jYYJTibGrTUVU43lr8h3URIgBkA4InOhuDv0ePMSCDSxBUhY0+G
+ eKo+YiXHy4SGUGLakahuq/hlGTRJJUddqFA1dNZdOUl23nVE
+
+dn: cn=charlie,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: charlie
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+
+dn: cn=beta,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: beta
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+certificateRevocationList;binary:: MIIP0TCCDrkCAQEwDQYJKoZIhvcNAQEFBQAwgZMxCzA
+ JBgNVBAYTAkFVMSswKQYDVQQKEyJDZXJ0aWZpY2F0ZXMgQXVzdHJhbGlhIFB0eSBMaW1pdGVkMSUw
+ IwYDVQQDExxDQVBMIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTAwLgYKCZImiZPyLGQBAxQgY2FAY
+ 2VydGlmaWNhdGVzLWF1c3RyYWxpYS5jb20uYXUXDTAzMDcyMjAxMzAyMFoXDTAzMTEwMzAxMzUyMF
+ owgg27MCMCBDi/biUXDTAwMDMwNjA2MjEzM1owDDAKBgNVHRUEAwoBBDAjAgQ5Il0KFw0wMDA1MjM
+ wODAwNDNaMAwwCgYDVR0VBAMKAQQwIwIEOSo6ZxcNMDAwNTI5MDIyNTQzWjAMMAoGA1UdFQQDCgEE
+ MCMCBDkx1QAXDTAwMDUyOTAzMzYwMVowDDAKBgNVHRUEAwoBBDAjAgQ5Pd7GFw0wMDA2MDcwNTM3M
+ jRaMAwwCgYDVR0VBAMKAQQwIwIEOUcavBcNMDAwNjE0MDc0MjExWjAMMAoGA1UdFQQDCgEEMCMCBD
+ lIlLYXDTAwMDYxNTA4MzY1NlowDDAKBgNVHRUEAwoBBDAjAgQ5SeOkFw0wMDA2MTYwODIzMDVaMAw
+ wCgYDVR0VBAMKAQQwIwIEOUiGjRcNMDAwNjE2MDgyMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ30oX
+ DTAwMDYyOTA4MDQyM1owDDAKBgNVHRUEAwoBBDAjAgQ5SdUjFw0wMDA2MjkwODA1NDVaMAwwCgYDV
+ R0VBAMKAQQwIwIEOTHlfRcNMDAwNjMwMDYwNjA1WjAMMAoGA1UdFQQDCgEEMCMCBDkzV6EXDTAwMD
+ YzMDA2MDYxMVowDDAKBgNVHRUEAwoBBDAjAgQ5SIFOFw0wMDA2MzAwNjA2MjFaMAwwCgYDVR0VBAM
+ KAQQwIwIEOUiCbBcNMDAwNjMwMDYwNjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlIgzkXDTAwMDYzMDA2
+ MDYzNlowDDAKBgNVHRUEAwoBBDAjAgQ5SIQEFw0wMDA2MzAwNjA2NDFaMAwwCgYDVR0VBAMKAQQwI
+ wIEOUiFBBcNMDAwNjMwMDYwNjQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlIhfQXDTAwMDYzMDA2MDY1NV
+ owDDAKBgNVHRUEAwoBBDAjAgQ5SIcmFw0wMDA2MzAwNjA3MDJaMAwwCgYDVR0VBAMKAQQwIwIEOUi
+ H4hcNMDAwNjMwMDYwNzA4WjAMMAoGA1UdFQQDCgEEMCMCBDlIiGUXDTAwMDYzMDA2MDcxNFowDDAK
+ BgNVHRUEAwoBBDAjAgQ5SIjaFw0wMDA2MzAwNjA3NDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiJhRcNM
+ DAwNjMwMDYwNzU3WjAMMAoGA1UdFQQDCgEEMCMCBDlIjoIXDTAwMDYzMDA2MDgwNFowDDAKBgNVHR
+ UEAwoBBDAjAgQ5SI89Fw0wMDA2MzAwNjA4MTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiP1RcNMDAwNjM
+ wMDYwODE1WjAMMAoGA1UdFQQDCgEEMCMCBDlIkEoXDTAwMDYzMDA2MDg0NVowDDAKBgNVHRUEAwoB
+ BDAjAgQ5SJC7Fw0wMDA2MzAwNjA4NTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiReRcNMDAwNjMwMDYwO
+ DU2WjAMMAoGA1UdFQQDCgEEMCMCBDlIkgMXDTAwMDYzMDA2MDkwNFowDDAKBgNVHRUEAwoBBDAjAg
+ Q5SJKqFw0wMDA2MzAwNjA5MDlaMAwwCgYDVR0VBAMKAQQwIwIEOUiTJhcNMDAwNjMwMDYwOTE2WjA
+ MMAoGA1UdFQQDCgEEMCMCBDlIk5AXDTAwMDYzMDA2MDkyMVowDDAKBgNVHRUEAwoBBDAjAgQ5SJQ3
+ Fw0wMDA2MzAwNjA5MjZaMAwwCgYDVR0VBAMKAQQwIwIEOUiVXhcNMDAwNjMwMDYwOTMyWjAMMAoGA
+ 1UdFQQDCgEEMCMCBDlIlgcXDTAwMDYzMDA2MDkzOFowDDAKBgNVHRUEAwoBBDAjAgQ5SJazFw0wMD
+ A2MzAwNjA5NDZaMAwwCgYDVR0VBAMKAQQwIwIEOUiXPxcNMDAwNjMwMDYwOTUxWjAMMAoGA1UdFQQ
+ DCgEEMCMCBDlIl7IXDTAwMDYzMDA2MDk1OFowDDAKBgNVHRUEAwoBBDAjAgQ5SJg0Fw0wMDA2MzAw
+ NjEwMDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiZBBcNMDAwNjMwMDYxMDA5WjAMMAoGA1UdFQQDCgEEM
+ CMCBDlJzksXDTAwMDYzMDA2MTAxNVowDDAKBgNVHRUEAwoBBDAjAgQ5Sc64Fw0wMDA2MzAwNjEwMj
+ FaMAwwCgYDVR0VBAMKAQQwIwIEOUnPVxcNMDAwNjMwMDYxMDI3WjAMMAoGA1UdFQQDCgEEMCMCBDl
+ J0BAXDTAwMDYzMDA2MTAzNVowDDAKBgNVHRUEAwoBBDAjAgQ5SdDKFw0wMDA2MzAwNjEwNDNaMAww
+ CgYDVR0VBAMKAQQwIwIEOUnRZRcNMDAwNjMwMDYxMDQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ0d0XD
+ TAwMDYzMDA2MTA1N1owDDAKBgNVHRUEAwoBBDAjAgQ5SdJ4Fw0wMDA2MzAwNjExMTVaMAwwCgYDVR
+ 0VBAMKAQQwIwIEOUnTDBcNMDAwNjMwMDYxMTIxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ04oXDTAwMDY
+ zMDA2MTEyN1owDDAKBgNVHRUEAwoBBDAjAgQ5SdQSFw0wMDA2MzAwNjExMzNaMAwwCgYDVR0VBAMK
+ AQQwIwIEOUnUoBcNMDAwNjMwMDYxMTM5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ2SQXDTAwMDYzMDA2M
+ TE1M1owDDAKBgNVHRUEAwoBBDAjAgQ5SdmwFw0wMDA2MzAwNjEyMDVaMAwwCgYDVR0VBAMKAQQwIw
+ IEOUnaTBcNMDAwNjMwMDYxMjExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ2vYXDTAwMDYzMDA2MTIxN1o
+ wDDAKBgNVHRUEAwoBBDAjAgQ5SducFw0wMDA2MzAwNjEyMjNaMAwwCgYDVR0VBAMKAQQwIwIEOUnc
+ IRcNMDAwNjMwMDYxMjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3KQXDTAwMDYzMDA2MTIzM1owDDAKB
+ gNVHRUEAwoBBDAjAgQ5Sd2xFw0wMDA2MzAwNjEyNDBaMAwwCgYDVR0VBAMKAQQwIwIEOUneRBcNMD
+ AwNjMwMDYxMjQ1WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3skXDTAwMDYzMDA2MTI1MVowDDAKBgNVHRU
+ EAwoBBDAjAgQ5Sd/IFw0wMDA2MzAwNjEzMDJaMAwwCgYDVR0VBAMKAQQwIwIEOUngPRcNMDAwNjMw
+ MDYxMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4M8XDTAwMDYzMDA2MTMyMFowDDAKBgNVHRUEAwoBB
+ DAjAgQ5SeE/Fw0wMDA2MzAwNjEzMjVaMAwwCgYDVR0VBAMKAQQwIwIEOUnh2BcNMDAwNjMwMDYxMz
+ MxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4mgXDTAwMDYzMDA2MTMzOVowDDAKBgNVHRUEAwoBBDAjAgQ
+ 5SeQvFw0wMDA2MzAwNjEzNDRaMAwwCgYDVR0VBAMKAQQwIwIEOVsGJRcNMDAwNjMwMDYxMzUwWjAM
+ MAoGA1UdFQQDCgEEMCMCBDlbBusXDTAwMDYzMDA2MTM1NlowDDAKBgNVHRUEAwoBBDAjAgQ5XEKPF
+ w0wMDA3MTMwOTAwMzhaMAwwCgYDVR0VBAMKAQQwIwIEOVxEKRcNMDAwNzEzMDkwMDQ1WjAMMAoGA1
+ UdFQQDCgEEMCMCBDlcRukXDTAwMDcyNjA2MjkyN1owDDAKBgNVHRUEAwoBBDAjAgQ5fohgFw0wMDA
+ 3MjYwNjQ2NTFaMAwwCgYDVR0VBAMKAQQwIwIEOaNqPBcNMDAwODIzMDYwOTQxWjAMMAoGA1UdFQQD
+ CgEFMCMCBDlcX2QXDTAwMDgzMTA3MTM1OFowDDAKBgNVHRUEAwoBBDAjAgQ5YsflFw0wMDA5MDEwM
+ TQwMjRaMAwwCgYDVR0VBAMKAQQwIwIEOWGHDRcNMDAwOTA2MDcwMTE2WjAMMAoGA1UdFQQDCgEEMC
+ MCBDliz/4XDTAwMDkwNjA3MDcwNVowDDAKBgNVHRUEAwoBBDAjAgQ5m3S6Fw0wMDA5MjAwNzA2NTd
+ aMAwwCgYDVR0VBAMKAQQwIwIEOy6/hhcNMDEwNzAzMDYxMDQyWjAMMAoGA1UdFQQDCgEEMCMCBDtB
+ Yw4XDTAxMDcwMzA2MTkxNlowDDAKBgNVHRUEAwoBBDAjAgQ7MEG6Fw0wMTA3MTAwODA5NTNaMAwwC
+ gYDVR0VBAMKAQQwIwIEOy68CxcNMDEwNzExMDYxMzI5WjAMMAoGA1UdFQQDCgEEMCMCBDswSOsXDT
+ AxMDgwMTA0MTkyM1owDDAKBgNVHRUEAwoBBTAjAgQ7MYgeFw0wMTA4MDEwNDIwMDJaMAwwCgYDVR0
+ VBAMKAQQwIwIEOzGHeBcNMDEwODAyMDI0NTM4WjAMMAoGA1UdFQQDCgEEMCMCBDsuveEXDTAxMDgz
+ MDA2MjIwOFowDDAKBgNVHRUEAwoBBDAjAgQ7jdxLFw0wMTA4MzAwNjQzMjRaMAwwCgYDVR0VBAMKA
+ QQwIwIEOy67QxcNMDExMTIxMDYyMDUzWjAMMAoGA1UdFQQDCgEEMCMCBDsDNXcXDTAyMDUxNzA4ND
+ Y0MlowDDAKBgNVHRUEAwoBBDAjAgQ7AzXMFw0wMjA1MTcwODQ2NTdaMAwwCgYDVR0VBAMKAQSgMjA
+ wMAsGA1UdFAQEAgIQoDATBgNVHSMEDDAKgAhISAKVrWisNzAMBgNVHRwBAf8EAjAAMA0GCSqGSIb3
+ DQEBBQUAA4IBAQA1xNXgyrtVB5LSOc76JF+aJzf8IfJGqF04CMzbo4lDpec/LgOrTSFV223ccJzuq
+ cnxGUfDbXFfSWDHGnj9HLLTCkrS3clL1TPVjGXg5mFu1l6DCfcP2v4i4dlradNYDQg/AVBoJsYa3l
+ efSFHw8RFXNHJWwIjJA6J0CBJ/8Uq2ywr8umdndb10RLtPWp66A7wxu7OvTjt68d3LgSniQ0mIJCn
+ 4ooE30oF/ew0EznbxlSCNRPpB8jYYJTibGrTUVU43lr8h3URIgBkA4InOhuDv0ePMSCDSxBUhY0+G
+ eKo+YiXHy4SGUGLakahuq/hlGTRJJUddqFA1dNZdOUl23nVE
+
+dn: cn=charlie,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: charlie
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+
+dn: cn=beta,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: beta
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+certificateRevocationList;binary:: MIIP0TCCDrkCAQEwDQYJKoZIhvcNAQEFBQAwgZMxCzA
+ JBgNVBAYTAkFVMSswKQYDVQQKEyJDZXJ0aWZpY2F0ZXMgQXVzdHJhbGlhIFB0eSBMaW1pdGVkMSUw
+ IwYDVQQDExxDQVBMIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTAwLgYKCZImiZPyLGQBAxQgY2FAY
+ 2VydGlmaWNhdGVzLWF1c3RyYWxpYS5jb20uYXUXDTAzMDcyMjAxMzAyMFoXDTAzMTEwMzAxMzUyMF
+ owgg27MCMCBDi/biUXDTAwMDMwNjA2MjEzM1owDDAKBgNVHRUEAwoBBDAjAgQ5Il0KFw0wMDA1MjM
+ wODAwNDNaMAwwCgYDVR0VBAMKAQQwIwIEOSo6ZxcNMDAwNTI5MDIyNTQzWjAMMAoGA1UdFQQDCgEE
+ MCMCBDkx1QAXDTAwMDUyOTAzMzYwMVowDDAKBgNVHRUEAwoBBDAjAgQ5Pd7GFw0wMDA2MDcwNTM3M
+ jRaMAwwCgYDVR0VBAMKAQQwIwIEOUcavBcNMDAwNjE0MDc0MjExWjAMMAoGA1UdFQQDCgEEMCMCBD
+ lIlLYXDTAwMDYxNTA4MzY1NlowDDAKBgNVHRUEAwoBBDAjAgQ5SeOkFw0wMDA2MTYwODIzMDVaMAw
+ wCgYDVR0VBAMKAQQwIwIEOUiGjRcNMDAwNjE2MDgyMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ30oX
+ DTAwMDYyOTA4MDQyM1owDDAKBgNVHRUEAwoBBDAjAgQ5SdUjFw0wMDA2MjkwODA1NDVaMAwwCgYDV
+ R0VBAMKAQQwIwIEOTHlfRcNMDAwNjMwMDYwNjA1WjAMMAoGA1UdFQQDCgEEMCMCBDkzV6EXDTAwMD
+ YzMDA2MDYxMVowDDAKBgNVHRUEAwoBBDAjAgQ5SIFOFw0wMDA2MzAwNjA2MjFaMAwwCgYDVR0VBAM
+ KAQQwIwIEOUiCbBcNMDAwNjMwMDYwNjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlIgzkXDTAwMDYzMDA2
+ MDYzNlowDDAKBgNVHRUEAwoBBDAjAgQ5SIQEFw0wMDA2MzAwNjA2NDFaMAwwCgYDVR0VBAMKAQQwI
+ wIEOUiFBBcNMDAwNjMwMDYwNjQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlIhfQXDTAwMDYzMDA2MDY1NV
+ owDDAKBgNVHRUEAwoBBDAjAgQ5SIcmFw0wMDA2MzAwNjA3MDJaMAwwCgYDVR0VBAMKAQQwIwIEOUi
+ H4hcNMDAwNjMwMDYwNzA4WjAMMAoGA1UdFQQDCgEEMCMCBDlIiGUXDTAwMDYzMDA2MDcxNFowDDAK
+ BgNVHRUEAwoBBDAjAgQ5SIjaFw0wMDA2MzAwNjA3NDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiJhRcNM
+ DAwNjMwMDYwNzU3WjAMMAoGA1UdFQQDCgEEMCMCBDlIjoIXDTAwMDYzMDA2MDgwNFowDDAKBgNVHR
+ UEAwoBBDAjAgQ5SI89Fw0wMDA2MzAwNjA4MTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiP1RcNMDAwNjM
+ wMDYwODE1WjAMMAoGA1UdFQQDCgEEMCMCBDlIkEoXDTAwMDYzMDA2MDg0NVowDDAKBgNVHRUEAwoB
+ BDAjAgQ5SJC7Fw0wMDA2MzAwNjA4NTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiReRcNMDAwNjMwMDYwO
+ DU2WjAMMAoGA1UdFQQDCgEEMCMCBDlIkgMXDTAwMDYzMDA2MDkwNFowDDAKBgNVHRUEAwoBBDAjAg
+ Q5SJKqFw0wMDA2MzAwNjA5MDlaMAwwCgYDVR0VBAMKAQQwIwIEOUiTJhcNMDAwNjMwMDYwOTE2WjA
+ MMAoGA1UdFQQDCgEEMCMCBDlIk5AXDTAwMDYzMDA2MDkyMVowDDAKBgNVHRUEAwoBBDAjAgQ5SJQ3
+ Fw0wMDA2MzAwNjA5MjZaMAwwCgYDVR0VBAMKAQQwIwIEOUiVXhcNMDAwNjMwMDYwOTMyWjAMMAoGA
+ 1UdFQQDCgEEMCMCBDlIlgcXDTAwMDYzMDA2MDkzOFowDDAKBgNVHRUEAwoBBDAjAgQ5SJazFw0wMD
+ A2MzAwNjA5NDZaMAwwCgYDVR0VBAMKAQQwIwIEOUiXPxcNMDAwNjMwMDYwOTUxWjAMMAoGA1UdFQQ
+ DCgEEMCMCBDlIl7IXDTAwMDYzMDA2MDk1OFowDDAKBgNVHRUEAwoBBDAjAgQ5SJg0Fw0wMDA2MzAw
+ NjEwMDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiZBBcNMDAwNjMwMDYxMDA5WjAMMAoGA1UdFQQDCgEEM
+ CMCBDlJzksXDTAwMDYzMDA2MTAxNVowDDAKBgNVHRUEAwoBBDAjAgQ5Sc64Fw0wMDA2MzAwNjEwMj
+ FaMAwwCgYDVR0VBAMKAQQwIwIEOUnPVxcNMDAwNjMwMDYxMDI3WjAMMAoGA1UdFQQDCgEEMCMCBDl
+ J0BAXDTAwMDYzMDA2MTAzNVowDDAKBgNVHRUEAwoBBDAjAgQ5SdDKFw0wMDA2MzAwNjEwNDNaMAww
+ CgYDVR0VBAMKAQQwIwIEOUnRZRcNMDAwNjMwMDYxMDQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ0d0XD
+ TAwMDYzMDA2MTA1N1owDDAKBgNVHRUEAwoBBDAjAgQ5SdJ4Fw0wMDA2MzAwNjExMTVaMAwwCgYDVR
+ 0VBAMKAQQwIwIEOUnTDBcNMDAwNjMwMDYxMTIxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ04oXDTAwMDY
+ zMDA2MTEyN1owDDAKBgNVHRUEAwoBBDAjAgQ5SdQSFw0wMDA2MzAwNjExMzNaMAwwCgYDVR0VBAMK
+ AQQwIwIEOUnUoBcNMDAwNjMwMDYxMTM5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ2SQXDTAwMDYzMDA2M
+ TE1M1owDDAKBgNVHRUEAwoBBDAjAgQ5SdmwFw0wMDA2MzAwNjEyMDVaMAwwCgYDVR0VBAMKAQQwIw
+ IEOUnaTBcNMDAwNjMwMDYxMjExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ2vYXDTAwMDYzMDA2MTIxN1o
+ wDDAKBgNVHRUEAwoBBDAjAgQ5SducFw0wMDA2MzAwNjEyMjNaMAwwCgYDVR0VBAMKAQQwIwIEOUnc
+ IRcNMDAwNjMwMDYxMjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3KQXDTAwMDYzMDA2MTIzM1owDDAKB
+ gNVHRUEAwoBBDAjAgQ5Sd2xFw0wMDA2MzAwNjEyNDBaMAwwCgYDVR0VBAMKAQQwIwIEOUneRBcNMD
+ AwNjMwMDYxMjQ1WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3skXDTAwMDYzMDA2MTI1MVowDDAKBgNVHRU
+ EAwoBBDAjAgQ5Sd/IFw0wMDA2MzAwNjEzMDJaMAwwCgYDVR0VBAMKAQQwIwIEOUngPRcNMDAwNjMw
+ MDYxMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4M8XDTAwMDYzMDA2MTMyMFowDDAKBgNVHRUEAwoBB
+ DAjAgQ5SeE/Fw0wMDA2MzAwNjEzMjVaMAwwCgYDVR0VBAMKAQQwIwIEOUnh2BcNMDAwNjMwMDYxMz
+ MxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4mgXDTAwMDYzMDA2MTMzOVowDDAKBgNVHRUEAwoBBDAjAgQ
+ 5SeQvFw0wMDA2MzAwNjEzNDRaMAwwCgYDVR0VBAMKAQQwIwIEOVsGJRcNMDAwNjMwMDYxMzUwWjAM
+ MAoGA1UdFQQDCgEEMCMCBDlbBusXDTAwMDYzMDA2MTM1NlowDDAKBgNVHRUEAwoBBDAjAgQ5XEKPF
+ w0wMDA3MTMwOTAwMzhaMAwwCgYDVR0VBAMKAQQwIwIEOVxEKRcNMDAwNzEzMDkwMDQ1WjAMMAoGA1
+ UdFQQDCgEEMCMCBDlcRukXDTAwMDcyNjA2MjkyN1owDDAKBgNVHRUEAwoBBDAjAgQ5fohgFw0wMDA
+ 3MjYwNjQ2NTFaMAwwCgYDVR0VBAMKAQQwIwIEOaNqPBcNMDAwODIzMDYwOTQxWjAMMAoGA1UdFQQD
+ CgEFMCMCBDlcX2QXDTAwMDgzMTA3MTM1OFowDDAKBgNVHRUEAwoBBDAjAgQ5YsflFw0wMDA5MDEwM
+ TQwMjRaMAwwCgYDVR0VBAMKAQQwIwIEOWGHDRcNMDAwOTA2MDcwMTE2WjAMMAoGA1UdFQQDCgEEMC
+ MCBDliz/4XDTAwMDkwNjA3MDcwNVowDDAKBgNVHRUEAwoBBDAjAgQ5m3S6Fw0wMDA5MjAwNzA2NTd
+ aMAwwCgYDVR0VBAMKAQQwIwIEOy6/hhcNMDEwNzAzMDYxMDQyWjAMMAoGA1UdFQQDCgEEMCMCBDtB
+ Yw4XDTAxMDcwMzA2MTkxNlowDDAKBgNVHRUEAwoBBDAjAgQ7MEG6Fw0wMTA3MTAwODA5NTNaMAwwC
+ gYDVR0VBAMKAQQwIwIEOy68CxcNMDEwNzExMDYxMzI5WjAMMAoGA1UdFQQDCgEEMCMCBDswSOsXDT
+ AxMDgwMTA0MTkyM1owDDAKBgNVHRUEAwoBBTAjAgQ7MYgeFw0wMTA4MDEwNDIwMDJaMAwwCgYDVR0
+ VBAMKAQQwIwIEOzGHeBcNMDEwODAyMDI0NTM4WjAMMAoGA1UdFQQDCgEEMCMCBDsuveEXDTAxMDgz
+ MDA2MjIwOFowDDAKBgNVHRUEAwoBBDAjAgQ7jdxLFw0wMTA4MzAwNjQzMjRaMAwwCgYDVR0VBAMKA
+ QQwIwIEOy67QxcNMDExMTIxMDYyMDUzWjAMMAoGA1UdFQQDCgEEMCMCBDsDNXcXDTAyMDUxNzA4ND
+ Y0MlowDDAKBgNVHRUEAwoBBDAjAgQ7AzXMFw0wMjA1MTcwODQ2NTdaMAwwCgYDVR0VBAMKAQSgMjA
+ wMAsGA1UdFAQEAgIQoDATBgNVHSMEDDAKgAhISAKVrWisNzAMBgNVHRwBAf8EAjAAMA0GCSqGSIb3
+ DQEBBQUAA4IBAQA1xNXgyrtVB5LSOc76JF+aJzf8IfJGqF04CMzbo4lDpec/LgOrTSFV223ccJzuq
+ cnxGUfDbXFfSWDHGnj9HLLTCkrS3clL1TPVjGXg5mFu1l6DCfcP2v4i4dlradNYDQg/AVBoJsYa3l
+ efSFHw8RFXNHJWwIjJA6J0CBJ/8Uq2ywr8umdndb10RLtPWp66A7wxu7OvTjt68d3LgSniQ0mIJCn
+ 4ooE30oF/ew0EznbxlSCNRPpB8jYYJTibGrTUVU43lr8h3URIgBkA4InOhuDv0ePMSCDSxBUhY0+G
+ eKo+YiXHy4SGUGLakahuq/hlGTRJJUddqFA1dNZdOUl23nVE
+
+dn: cn=charlie,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: charlie
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+
+dn: cn=beta,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: beta
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+certificateRevocationList;binary:: MIIP0TCCDrkCAQEwDQYJKoZIhvcNAQEFBQAwgZMxCzA
+ JBgNVBAYTAkFVMSswKQYDVQQKEyJDZXJ0aWZpY2F0ZXMgQXVzdHJhbGlhIFB0eSBMaW1pdGVkMSUw
+ IwYDVQQDExxDQVBMIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTAwLgYKCZImiZPyLGQBAxQgY2FAY
+ 2VydGlmaWNhdGVzLWF1c3RyYWxpYS5jb20uYXUXDTAzMDcyMjAxMzAyMFoXDTAzMTEwMzAxMzUyMF
+ owgg27MCMCBDi/biUXDTAwMDMwNjA2MjEzM1owDDAKBgNVHRUEAwoBBDAjAgQ5Il0KFw0wMDA1MjM
+ wODAwNDNaMAwwCgYDVR0VBAMKAQQwIwIEOSo6ZxcNMDAwNTI5MDIyNTQzWjAMMAoGA1UdFQQDCgEE
+ MCMCBDkx1QAXDTAwMDUyOTAzMzYwMVowDDAKBgNVHRUEAwoBBDAjAgQ5Pd7GFw0wMDA2MDcwNTM3M
+ jRaMAwwCgYDVR0VBAMKAQQwIwIEOUcavBcNMDAwNjE0MDc0MjExWjAMMAoGA1UdFQQDCgEEMCMCBD
+ lIlLYXDTAwMDYxNTA4MzY1NlowDDAKBgNVHRUEAwoBBDAjAgQ5SeOkFw0wMDA2MTYwODIzMDVaMAw
+ wCgYDVR0VBAMKAQQwIwIEOUiGjRcNMDAwNjE2MDgyMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ30oX
+ DTAwMDYyOTA4MDQyM1owDDAKBgNVHRUEAwoBBDAjAgQ5SdUjFw0wMDA2MjkwODA1NDVaMAwwCgYDV
+ R0VBAMKAQQwIwIEOTHlfRcNMDAwNjMwMDYwNjA1WjAMMAoGA1UdFQQDCgEEMCMCBDkzV6EXDTAwMD
+ YzMDA2MDYxMVowDDAKBgNVHRUEAwoBBDAjAgQ5SIFOFw0wMDA2MzAwNjA2MjFaMAwwCgYDVR0VBAM
+ KAQQwIwIEOUiCbBcNMDAwNjMwMDYwNjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlIgzkXDTAwMDYzMDA2
+ MDYzNlowDDAKBgNVHRUEAwoBBDAjAgQ5SIQEFw0wMDA2MzAwNjA2NDFaMAwwCgYDVR0VBAMKAQQwI
+ wIEOUiFBBcNMDAwNjMwMDYwNjQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlIhfQXDTAwMDYzMDA2MDY1NV
+ owDDAKBgNVHRUEAwoBBDAjAgQ5SIcmFw0wMDA2MzAwNjA3MDJaMAwwCgYDVR0VBAMKAQQwIwIEOUi
+ H4hcNMDAwNjMwMDYwNzA4WjAMMAoGA1UdFQQDCgEEMCMCBDlIiGUXDTAwMDYzMDA2MDcxNFowDDAK
+ BgNVHRUEAwoBBDAjAgQ5SIjaFw0wMDA2MzAwNjA3NDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiJhRcNM
+ DAwNjMwMDYwNzU3WjAMMAoGA1UdFQQDCgEEMCMCBDlIjoIXDTAwMDYzMDA2MDgwNFowDDAKBgNVHR
+ UEAwoBBDAjAgQ5SI89Fw0wMDA2MzAwNjA4MTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiP1RcNMDAwNjM
+ wMDYwODE1WjAMMAoGA1UdFQQDCgEEMCMCBDlIkEoXDTAwMDYzMDA2MDg0NVowDDAKBgNVHRUEAwoB
+ BDAjAgQ5SJC7Fw0wMDA2MzAwNjA4NTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiReRcNMDAwNjMwMDYwO
+ DU2WjAMMAoGA1UdFQQDCgEEMCMCBDlIkgMXDTAwMDYzMDA2MDkwNFowDDAKBgNVHRUEAwoBBDAjAg
+ Q5SJKqFw0wMDA2MzAwNjA5MDlaMAwwCgYDVR0VBAMKAQQwIwIEOUiTJhcNMDAwNjMwMDYwOTE2WjA
+ MMAoGA1UdFQQDCgEEMCMCBDlIk5AXDTAwMDYzMDA2MDkyMVowDDAKBgNVHRUEAwoBBDAjAgQ5SJQ3
+ Fw0wMDA2MzAwNjA5MjZaMAwwCgYDVR0VBAMKAQQwIwIEOUiVXhcNMDAwNjMwMDYwOTMyWjAMMAoGA
+ 1UdFQQDCgEEMCMCBDlIlgcXDTAwMDYzMDA2MDkzOFowDDAKBgNVHRUEAwoBBDAjAgQ5SJazFw0wMD
+ A2MzAwNjA5NDZaMAwwCgYDVR0VBAMKAQQwIwIEOUiXPxcNMDAwNjMwMDYwOTUxWjAMMAoGA1UdFQQ
+ DCgEEMCMCBDlIl7IXDTAwMDYzMDA2MDk1OFowDDAKBgNVHRUEAwoBBDAjAgQ5SJg0Fw0wMDA2MzAw
+ NjEwMDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiZBBcNMDAwNjMwMDYxMDA5WjAMMAoGA1UdFQQDCgEEM
+ CMCBDlJzksXDTAwMDYzMDA2MTAxNVowDDAKBgNVHRUEAwoBBDAjAgQ5Sc64Fw0wMDA2MzAwNjEwMj
+ FaMAwwCgYDVR0VBAMKAQQwIwIEOUnPVxcNMDAwNjMwMDYxMDI3WjAMMAoGA1UdFQQDCgEEMCMCBDl
+ J0BAXDTAwMDYzMDA2MTAzNVowDDAKBgNVHRUEAwoBBDAjAgQ5SdDKFw0wMDA2MzAwNjEwNDNaMAww
+ CgYDVR0VBAMKAQQwIwIEOUnRZRcNMDAwNjMwMDYxMDQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ0d0XD
+ TAwMDYzMDA2MTA1N1owDDAKBgNVHRUEAwoBBDAjAgQ5SdJ4Fw0wMDA2MzAwNjExMTVaMAwwCgYDVR
+ 0VBAMKAQQwIwIEOUnTDBcNMDAwNjMwMDYxMTIxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ04oXDTAwMDY
+ zMDA2MTEyN1owDDAKBgNVHRUEAwoBBDAjAgQ5SdQSFw0wMDA2MzAwNjExMzNaMAwwCgYDVR0VBAMK
+ AQQwIwIEOUnUoBcNMDAwNjMwMDYxMTM5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ2SQXDTAwMDYzMDA2M
+ TE1M1owDDAKBgNVHRUEAwoBBDAjAgQ5SdmwFw0wMDA2MzAwNjEyMDVaMAwwCgYDVR0VBAMKAQQwIw
+ IEOUnaTBcNMDAwNjMwMDYxMjExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ2vYXDTAwMDYzMDA2MTIxN1o
+ wDDAKBgNVHRUEAwoBBDAjAgQ5SducFw0wMDA2MzAwNjEyMjNaMAwwCgYDVR0VBAMKAQQwIwIEOUnc
+ IRcNMDAwNjMwMDYxMjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3KQXDTAwMDYzMDA2MTIzM1owDDAKB
+ gNVHRUEAwoBBDAjAgQ5Sd2xFw0wMDA2MzAwNjEyNDBaMAwwCgYDVR0VBAMKAQQwIwIEOUneRBcNMD
+ AwNjMwMDYxMjQ1WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3skXDTAwMDYzMDA2MTI1MVowDDAKBgNVHRU
+ EAwoBBDAjAgQ5Sd/IFw0wMDA2MzAwNjEzMDJaMAwwCgYDVR0VBAMKAQQwIwIEOUngPRcNMDAwNjMw
+ MDYxMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4M8XDTAwMDYzMDA2MTMyMFowDDAKBgNVHRUEAwoBB
+ DAjAgQ5SeE/Fw0wMDA2MzAwNjEzMjVaMAwwCgYDVR0VBAMKAQQwIwIEOUnh2BcNMDAwNjMwMDYxMz
+ MxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4mgXDTAwMDYzMDA2MTMzOVowDDAKBgNVHRUEAwoBBDAjAgQ
+ 5SeQvFw0wMDA2MzAwNjEzNDRaMAwwCgYDVR0VBAMKAQQwIwIEOVsGJRcNMDAwNjMwMDYxMzUwWjAM
+ MAoGA1UdFQQDCgEEMCMCBDlbBusXDTAwMDYzMDA2MTM1NlowDDAKBgNVHRUEAwoBBDAjAgQ5XEKPF
+ w0wMDA3MTMwOTAwMzhaMAwwCgYDVR0VBAMKAQQwIwIEOVxEKRcNMDAwNzEzMDkwMDQ1WjAMMAoGA1
+ UdFQQDCgEEMCMCBDlcRukXDTAwMDcyNjA2MjkyN1owDDAKBgNVHRUEAwoBBDAjAgQ5fohgFw0wMDA
+ 3MjYwNjQ2NTFaMAwwCgYDVR0VBAMKAQQwIwIEOaNqPBcNMDAwODIzMDYwOTQxWjAMMAoGA1UdFQQD
+ CgEFMCMCBDlcX2QXDTAwMDgzMTA3MTM1OFowDDAKBgNVHRUEAwoBBDAjAgQ5YsflFw0wMDA5MDEwM
+ TQwMjRaMAwwCgYDVR0VBAMKAQQwIwIEOWGHDRcNMDAwOTA2MDcwMTE2WjAMMAoGA1UdFQQDCgEEMC
+ MCBDliz/4XDTAwMDkwNjA3MDcwNVowDDAKBgNVHRUEAwoBBDAjAgQ5m3S6Fw0wMDA5MjAwNzA2NTd
+ aMAwwCgYDVR0VBAMKAQQwIwIEOy6/hhcNMDEwNzAzMDYxMDQyWjAMMAoGA1UdFQQDCgEEMCMCBDtB
+ Yw4XDTAxMDcwMzA2MTkxNlowDDAKBgNVHRUEAwoBBDAjAgQ7MEG6Fw0wMTA3MTAwODA5NTNaMAwwC
+ gYDVR0VBAMKAQQwIwIEOy68CxcNMDEwNzExMDYxMzI5WjAMMAoGA1UdFQQDCgEEMCMCBDswSOsXDT
+ AxMDgwMTA0MTkyM1owDDAKBgNVHRUEAwoBBTAjAgQ7MYgeFw0wMTA4MDEwNDIwMDJaMAwwCgYDVR0
+ VBAMKAQQwIwIEOzGHeBcNMDEwODAyMDI0NTM4WjAMMAoGA1UdFQQDCgEEMCMCBDsuveEXDTAxMDgz
+ MDA2MjIwOFowDDAKBgNVHRUEAwoBBDAjAgQ7jdxLFw0wMTA4MzAwNjQzMjRaMAwwCgYDVR0VBAMKA
+ QQwIwIEOy67QxcNMDExMTIxMDYyMDUzWjAMMAoGA1UdFQQDCgEEMCMCBDsDNXcXDTAyMDUxNzA4ND
+ Y0MlowDDAKBgNVHRUEAwoBBDAjAgQ7AzXMFw0wMjA1MTcwODQ2NTdaMAwwCgYDVR0VBAMKAQSgMjA
+ wMAsGA1UdFAQEAgIQoDATBgNVHSMEDDAKgAhISAKVrWisNzAMBgNVHRwBAf8EAjAAMA0GCSqGSIb3
+ DQEBBQUAA4IBAQA1xNXgyrtVB5LSOc76JF+aJzf8IfJGqF04CMzbo4lDpec/LgOrTSFV223ccJzuq
+ cnxGUfDbXFfSWDHGnj9HLLTCkrS3clL1TPVjGXg5mFu1l6DCfcP2v4i4dlradNYDQg/AVBoJsYa3l
+ efSFHw8RFXNHJWwIjJA6J0CBJ/8Uq2ywr8umdndb10RLtPWp66A7wxu7OvTjt68d3LgSniQ0mIJCn
+ 4ooE30oF/ew0EznbxlSCNRPpB8jYYJTibGrTUVU43lr8h3URIgBkA4InOhuDv0ePMSCDSxBUhY0+G
+ eKo+YiXHy4SGUGLakahuq/hlGTRJJUddqFA1dNZdOUl23nVE
+
+dn: cn=charlie,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: charlie
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+
+dn: cn=beta,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: beta
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+certificateRevocationList;binary:: MIIP0TCCDrkCAQEwDQYJKoZIhvcNAQEFBQAwgZMxCzA
+ JBgNVBAYTAkFVMSswKQYDVQQKEyJDZXJ0aWZpY2F0ZXMgQXVzdHJhbGlhIFB0eSBMaW1pdGVkMSUw
+ IwYDVQQDExxDQVBMIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTAwLgYKCZImiZPyLGQBAxQgY2FAY
+ 2VydGlmaWNhdGVzLWF1c3RyYWxpYS5jb20uYXUXDTAzMDcyMjAxMzAyMFoXDTAzMTEwMzAxMzUyMF
+ owgg27MCMCBDi/biUXDTAwMDMwNjA2MjEzM1owDDAKBgNVHRUEAwoBBDAjAgQ5Il0KFw0wMDA1MjM
+ wODAwNDNaMAwwCgYDVR0VBAMKAQQwIwIEOSo6ZxcNMDAwNTI5MDIyNTQzWjAMMAoGA1UdFQQDCgEE
+ MCMCBDkx1QAXDTAwMDUyOTAzMzYwMVowDDAKBgNVHRUEAwoBBDAjAgQ5Pd7GFw0wMDA2MDcwNTM3M
+ jRaMAwwCgYDVR0VBAMKAQQwIwIEOUcavBcNMDAwNjE0MDc0MjExWjAMMAoGA1UdFQQDCgEEMCMCBD
+ lIlLYXDTAwMDYxNTA4MzY1NlowDDAKBgNVHRUEAwoBBDAjAgQ5SeOkFw0wMDA2MTYwODIzMDVaMAw
+ wCgYDVR0VBAMKAQQwIwIEOUiGjRcNMDAwNjE2MDgyMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ30oX
+ DTAwMDYyOTA4MDQyM1owDDAKBgNVHRUEAwoBBDAjAgQ5SdUjFw0wMDA2MjkwODA1NDVaMAwwCgYDV
+ R0VBAMKAQQwIwIEOTHlfRcNMDAwNjMwMDYwNjA1WjAMMAoGA1UdFQQDCgEEMCMCBDkzV6EXDTAwMD
+ YzMDA2MDYxMVowDDAKBgNVHRUEAwoBBDAjAgQ5SIFOFw0wMDA2MzAwNjA2MjFaMAwwCgYDVR0VBAM
+ KAQQwIwIEOUiCbBcNMDAwNjMwMDYwNjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlIgzkXDTAwMDYzMDA2
+ MDYzNlowDDAKBgNVHRUEAwoBBDAjAgQ5SIQEFw0wMDA2MzAwNjA2NDFaMAwwCgYDVR0VBAMKAQQwI
+ wIEOUiFBBcNMDAwNjMwMDYwNjQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlIhfQXDTAwMDYzMDA2MDY1NV
+ owDDAKBgNVHRUEAwoBBDAjAgQ5SIcmFw0wMDA2MzAwNjA3MDJaMAwwCgYDVR0VBAMKAQQwIwIEOUi
+ H4hcNMDAwNjMwMDYwNzA4WjAMMAoGA1UdFQQDCgEEMCMCBDlIiGUXDTAwMDYzMDA2MDcxNFowDDAK
+ BgNVHRUEAwoBBDAjAgQ5SIjaFw0wMDA2MzAwNjA3NDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiJhRcNM
+ DAwNjMwMDYwNzU3WjAMMAoGA1UdFQQDCgEEMCMCBDlIjoIXDTAwMDYzMDA2MDgwNFowDDAKBgNVHR
+ UEAwoBBDAjAgQ5SI89Fw0wMDA2MzAwNjA4MTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiP1RcNMDAwNjM
+ wMDYwODE1WjAMMAoGA1UdFQQDCgEEMCMCBDlIkEoXDTAwMDYzMDA2MDg0NVowDDAKBgNVHRUEAwoB
+ BDAjAgQ5SJC7Fw0wMDA2MzAwNjA4NTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiReRcNMDAwNjMwMDYwO
+ DU2WjAMMAoGA1UdFQQDCgEEMCMCBDlIkgMXDTAwMDYzMDA2MDkwNFowDDAKBgNVHRUEAwoBBDAjAg
+ Q5SJKqFw0wMDA2MzAwNjA5MDlaMAwwCgYDVR0VBAMKAQQwIwIEOUiTJhcNMDAwNjMwMDYwOTE2WjA
+ MMAoGA1UdFQQDCgEEMCMCBDlIk5AXDTAwMDYzMDA2MDkyMVowDDAKBgNVHRUEAwoBBDAjAgQ5SJQ3
+ Fw0wMDA2MzAwNjA5MjZaMAwwCgYDVR0VBAMKAQQwIwIEOUiVXhcNMDAwNjMwMDYwOTMyWjAMMAoGA
+ 1UdFQQDCgEEMCMCBDlIlgcXDTAwMDYzMDA2MDkzOFowDDAKBgNVHRUEAwoBBDAjAgQ5SJazFw0wMD
+ A2MzAwNjA5NDZaMAwwCgYDVR0VBAMKAQQwIwIEOUiXPxcNMDAwNjMwMDYwOTUxWjAMMAoGA1UdFQQ
+ DCgEEMCMCBDlIl7IXDTAwMDYzMDA2MDk1OFowDDAKBgNVHRUEAwoBBDAjAgQ5SJg0Fw0wMDA2MzAw
+ NjEwMDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiZBBcNMDAwNjMwMDYxMDA5WjAMMAoGA1UdFQQDCgEEM
+ CMCBDlJzksXDTAwMDYzMDA2MTAxNVowDDAKBgNVHRUEAwoBBDAjAgQ5Sc64Fw0wMDA2MzAwNjEwMj
+ FaMAwwCgYDVR0VBAMKAQQwIwIEOUnPVxcNMDAwNjMwMDYxMDI3WjAMMAoGA1UdFQQDCgEEMCMCBDl
+ J0BAXDTAwMDYzMDA2MTAzNVowDDAKBgNVHRUEAwoBBDAjAgQ5SdDKFw0wMDA2MzAwNjEwNDNaMAww
+ CgYDVR0VBAMKAQQwIwIEOUnRZRcNMDAwNjMwMDYxMDQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ0d0XD
+ TAwMDYzMDA2MTA1N1owDDAKBgNVHRUEAwoBBDAjAgQ5SdJ4Fw0wMDA2MzAwNjExMTVaMAwwCgYDVR
+ 0VBAMKAQQwIwIEOUnTDBcNMDAwNjMwMDYxMTIxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ04oXDTAwMDY
+ zMDA2MTEyN1owDDAKBgNVHRUEAwoBBDAjAgQ5SdQSFw0wMDA2MzAwNjExMzNaMAwwCgYDVR0VBAMK
+ AQQwIwIEOUnUoBcNMDAwNjMwMDYxMTM5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ2SQXDTAwMDYzMDA2M
+ TE1M1owDDAKBgNVHRUEAwoBBDAjAgQ5SdmwFw0wMDA2MzAwNjEyMDVaMAwwCgYDVR0VBAMKAQQwIw
+ IEOUnaTBcNMDAwNjMwMDYxMjExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ2vYXDTAwMDYzMDA2MTIxN1o
+ wDDAKBgNVHRUEAwoBBDAjAgQ5SducFw0wMDA2MzAwNjEyMjNaMAwwCgYDVR0VBAMKAQQwIwIEOUnc
+ IRcNMDAwNjMwMDYxMjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3KQXDTAwMDYzMDA2MTIzM1owDDAKB
+ gNVHRUEAwoBBDAjAgQ5Sd2xFw0wMDA2MzAwNjEyNDBaMAwwCgYDVR0VBAMKAQQwIwIEOUneRBcNMD
+ AwNjMwMDYxMjQ1WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3skXDTAwMDYzMDA2MTI1MVowDDAKBgNVHRU
+ EAwoBBDAjAgQ5Sd/IFw0wMDA2MzAwNjEzMDJaMAwwCgYDVR0VBAMKAQQwIwIEOUngPRcNMDAwNjMw
+ MDYxMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4M8XDTAwMDYzMDA2MTMyMFowDDAKBgNVHRUEAwoBB
+ DAjAgQ5SeE/Fw0wMDA2MzAwNjEzMjVaMAwwCgYDVR0VBAMKAQQwIwIEOUnh2BcNMDAwNjMwMDYxMz
+ MxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4mgXDTAwMDYzMDA2MTMzOVowDDAKBgNVHRUEAwoBBDAjAgQ
+ 5SeQvFw0wMDA2MzAwNjEzNDRaMAwwCgYDVR0VBAMKAQQwIwIEOVsGJRcNMDAwNjMwMDYxMzUwWjAM
+ MAoGA1UdFQQDCgEEMCMCBDlbBusXDTAwMDYzMDA2MTM1NlowDDAKBgNVHRUEAwoBBDAjAgQ5XEKPF
+ w0wMDA3MTMwOTAwMzhaMAwwCgYDVR0VBAMKAQQwIwIEOVxEKRcNMDAwNzEzMDkwMDQ1WjAMMAoGA1
+ UdFQQDCgEEMCMCBDlcRukXDTAwMDcyNjA2MjkyN1owDDAKBgNVHRUEAwoBBDAjAgQ5fohgFw0wMDA
+ 3MjYwNjQ2NTFaMAwwCgYDVR0VBAMKAQQwIwIEOaNqPBcNMDAwODIzMDYwOTQxWjAMMAoGA1UdFQQD
+ CgEFMCMCBDlcX2QXDTAwMDgzMTA3MTM1OFowDDAKBgNVHRUEAwoBBDAjAgQ5YsflFw0wMDA5MDEwM
+ TQwMjRaMAwwCgYDVR0VBAMKAQQwIwIEOWGHDRcNMDAwOTA2MDcwMTE2WjAMMAoGA1UdFQQDCgEEMC
+ MCBDliz/4XDTAwMDkwNjA3MDcwNVowDDAKBgNVHRUEAwoBBDAjAgQ5m3S6Fw0wMDA5MjAwNzA2NTd
+ aMAwwCgYDVR0VBAMKAQQwIwIEOy6/hhcNMDEwNzAzMDYxMDQyWjAMMAoGA1UdFQQDCgEEMCMCBDtB
+ Yw4XDTAxMDcwMzA2MTkxNlowDDAKBgNVHRUEAwoBBDAjAgQ7MEG6Fw0wMTA3MTAwODA5NTNaMAwwC
+ gYDVR0VBAMKAQQwIwIEOy68CxcNMDEwNzExMDYxMzI5WjAMMAoGA1UdFQQDCgEEMCMCBDswSOsXDT
+ AxMDgwMTA0MTkyM1owDDAKBgNVHRUEAwoBBTAjAgQ7MYgeFw0wMTA4MDEwNDIwMDJaMAwwCgYDVR0
+ VBAMKAQQwIwIEOzGHeBcNMDEwODAyMDI0NTM4WjAMMAoGA1UdFQQDCgEEMCMCBDsuveEXDTAxMDgz
+ MDA2MjIwOFowDDAKBgNVHRUEAwoBBDAjAgQ7jdxLFw0wMTA4MzAwNjQzMjRaMAwwCgYDVR0VBAMKA
+ QQwIwIEOy67QxcNMDExMTIxMDYyMDUzWjAMMAoGA1UdFQQDCgEEMCMCBDsDNXcXDTAyMDUxNzA4ND
+ Y0MlowDDAKBgNVHRUEAwoBBDAjAgQ7AzXMFw0wMjA1MTcwODQ2NTdaMAwwCgYDVR0VBAMKAQSgMjA
+ wMAsGA1UdFAQEAgIQoDATBgNVHSMEDDAKgAhISAKVrWisNzAMBgNVHRwBAf8EAjAAMA0GCSqGSIb3
+ DQEBBQUAA4IBAQA1xNXgyrtVB5LSOc76JF+aJzf8IfJGqF04CMzbo4lDpec/LgOrTSFV223ccJzuq
+ cnxGUfDbXFfSWDHGnj9HLLTCkrS3clL1TPVjGXg5mFu1l6DCfcP2v4i4dlradNYDQg/AVBoJsYa3l
+ efSFHw8RFXNHJWwIjJA6J0CBJ/8Uq2ywr8umdndb10RLtPWp66A7wxu7OvTjt68d3LgSniQ0mIJCn
+ 4ooE30oF/ew0EznbxlSCNRPpB8jYYJTibGrTUVU43lr8h3URIgBkA4InOhuDv0ePMSCDSxBUhY0+G
+ eKo+YiXHy4SGUGLakahuq/hlGTRJJUddqFA1dNZdOUl23nVE
+
+dn: cn=charlie,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: charlie
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+
+dn: cn=beta,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: beta
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+certificateRevocationList;binary:: MIIP0TCCDrkCAQEwDQYJKoZIhvcNAQEFBQAwgZMxCzA
+ JBgNVBAYTAkFVMSswKQYDVQQKEyJDZXJ0aWZpY2F0ZXMgQXVzdHJhbGlhIFB0eSBMaW1pdGVkMSUw
+ IwYDVQQDExxDQVBMIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTAwLgYKCZImiZPyLGQBAxQgY2FAY
+ 2VydGlmaWNhdGVzLWF1c3RyYWxpYS5jb20uYXUXDTAzMDcyMjAxMzAyMFoXDTAzMTEwMzAxMzUyMF
+ owgg27MCMCBDi/biUXDTAwMDMwNjA2MjEzM1owDDAKBgNVHRUEAwoBBDAjAgQ5Il0KFw0wMDA1MjM
+ wODAwNDNaMAwwCgYDVR0VBAMKAQQwIwIEOSo6ZxcNMDAwNTI5MDIyNTQzWjAMMAoGA1UdFQQDCgEE
+ MCMCBDkx1QAXDTAwMDUyOTAzMzYwMVowDDAKBgNVHRUEAwoBBDAjAgQ5Pd7GFw0wMDA2MDcwNTM3M
+ jRaMAwwCgYDVR0VBAMKAQQwIwIEOUcavBcNMDAwNjE0MDc0MjExWjAMMAoGA1UdFQQDCgEEMCMCBD
+ lIlLYXDTAwMDYxNTA4MzY1NlowDDAKBgNVHRUEAwoBBDAjAgQ5SeOkFw0wMDA2MTYwODIzMDVaMAw
+ wCgYDVR0VBAMKAQQwIwIEOUiGjRcNMDAwNjE2MDgyMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ30oX
+ DTAwMDYyOTA4MDQyM1owDDAKBgNVHRUEAwoBBDAjAgQ5SdUjFw0wMDA2MjkwODA1NDVaMAwwCgYDV
+ R0VBAMKAQQwIwIEOTHlfRcNMDAwNjMwMDYwNjA1WjAMMAoGA1UdFQQDCgEEMCMCBDkzV6EXDTAwMD
+ YzMDA2MDYxMVowDDAKBgNVHRUEAwoBBDAjAgQ5SIFOFw0wMDA2MzAwNjA2MjFaMAwwCgYDVR0VBAM
+ KAQQwIwIEOUiCbBcNMDAwNjMwMDYwNjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlIgzkXDTAwMDYzMDA2
+ MDYzNlowDDAKBgNVHRUEAwoBBDAjAgQ5SIQEFw0wMDA2MzAwNjA2NDFaMAwwCgYDVR0VBAMKAQQwI
+ wIEOUiFBBcNMDAwNjMwMDYwNjQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlIhfQXDTAwMDYzMDA2MDY1NV
+ owDDAKBgNVHRUEAwoBBDAjAgQ5SIcmFw0wMDA2MzAwNjA3MDJaMAwwCgYDVR0VBAMKAQQwIwIEOUi
+ H4hcNMDAwNjMwMDYwNzA4WjAMMAoGA1UdFQQDCgEEMCMCBDlIiGUXDTAwMDYzMDA2MDcxNFowDDAK
+ BgNVHRUEAwoBBDAjAgQ5SIjaFw0wMDA2MzAwNjA3NDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiJhRcNM
+ DAwNjMwMDYwNzU3WjAMMAoGA1UdFQQDCgEEMCMCBDlIjoIXDTAwMDYzMDA2MDgwNFowDDAKBgNVHR
+ UEAwoBBDAjAgQ5SI89Fw0wMDA2MzAwNjA4MTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiP1RcNMDAwNjM
+ wMDYwODE1WjAMMAoGA1UdFQQDCgEEMCMCBDlIkEoXDTAwMDYzMDA2MDg0NVowDDAKBgNVHRUEAwoB
+ BDAjAgQ5SJC7Fw0wMDA2MzAwNjA4NTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiReRcNMDAwNjMwMDYwO
+ DU2WjAMMAoGA1UdFQQDCgEEMCMCBDlIkgMXDTAwMDYzMDA2MDkwNFowDDAKBgNVHRUEAwoBBDAjAg
+ Q5SJKqFw0wMDA2MzAwNjA5MDlaMAwwCgYDVR0VBAMKAQQwIwIEOUiTJhcNMDAwNjMwMDYwOTE2WjA
+ MMAoGA1UdFQQDCgEEMCMCBDlIk5AXDTAwMDYzMDA2MDkyMVowDDAKBgNVHRUEAwoBBDAjAgQ5SJQ3
+ Fw0wMDA2MzAwNjA5MjZaMAwwCgYDVR0VBAMKAQQwIwIEOUiVXhcNMDAwNjMwMDYwOTMyWjAMMAoGA
+ 1UdFQQDCgEEMCMCBDlIlgcXDTAwMDYzMDA2MDkzOFowDDAKBgNVHRUEAwoBBDAjAgQ5SJazFw0wMD
+ A2MzAwNjA5NDZaMAwwCgYDVR0VBAMKAQQwIwIEOUiXPxcNMDAwNjMwMDYwOTUxWjAMMAoGA1UdFQQ
+ DCgEEMCMCBDlIl7IXDTAwMDYzMDA2MDk1OFowDDAKBgNVHRUEAwoBBDAjAgQ5SJg0Fw0wMDA2MzAw
+ NjEwMDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiZBBcNMDAwNjMwMDYxMDA5WjAMMAoGA1UdFQQDCgEEM
+ CMCBDlJzksXDTAwMDYzMDA2MTAxNVowDDAKBgNVHRUEAwoBBDAjAgQ5Sc64Fw0wMDA2MzAwNjEwMj
+ FaMAwwCgYDVR0VBAMKAQQwIwIEOUnPVxcNMDAwNjMwMDYxMDI3WjAMMAoGA1UdFQQDCgEEMCMCBDl
+ J0BAXDTAwMDYzMDA2MTAzNVowDDAKBgNVHRUEAwoBBDAjAgQ5SdDKFw0wMDA2MzAwNjEwNDNaMAww
+ CgYDVR0VBAMKAQQwIwIEOUnRZRcNMDAwNjMwMDYxMDQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ0d0XD
+ TAwMDYzMDA2MTA1N1owDDAKBgNVHRUEAwoBBDAjAgQ5SdJ4Fw0wMDA2MzAwNjExMTVaMAwwCgYDVR
+ 0VBAMKAQQwIwIEOUnTDBcNMDAwNjMwMDYxMTIxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ04oXDTAwMDY
+ zMDA2MTEyN1owDDAKBgNVHRUEAwoBBDAjAgQ5SdQSFw0wMDA2MzAwNjExMzNaMAwwCgYDVR0VBAMK
+ AQQwIwIEOUnUoBcNMDAwNjMwMDYxMTM5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ2SQXDTAwMDYzMDA2M
+ TE1M1owDDAKBgNVHRUEAwoBBDAjAgQ5SdmwFw0wMDA2MzAwNjEyMDVaMAwwCgYDVR0VBAMKAQQwIw
+ IEOUnaTBcNMDAwNjMwMDYxMjExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ2vYXDTAwMDYzMDA2MTIxN1o
+ wDDAKBgNVHRUEAwoBBDAjAgQ5SducFw0wMDA2MzAwNjEyMjNaMAwwCgYDVR0VBAMKAQQwIwIEOUnc
+ IRcNMDAwNjMwMDYxMjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3KQXDTAwMDYzMDA2MTIzM1owDDAKB
+ gNVHRUEAwoBBDAjAgQ5Sd2xFw0wMDA2MzAwNjEyNDBaMAwwCgYDVR0VBAMKAQQwIwIEOUneRBcNMD
+ AwNjMwMDYxMjQ1WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3skXDTAwMDYzMDA2MTI1MVowDDAKBgNVHRU
+ EAwoBBDAjAgQ5Sd/IFw0wMDA2MzAwNjEzMDJaMAwwCgYDVR0VBAMKAQQwIwIEOUngPRcNMDAwNjMw
+ MDYxMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4M8XDTAwMDYzMDA2MTMyMFowDDAKBgNVHRUEAwoBB
+ DAjAgQ5SeE/Fw0wMDA2MzAwNjEzMjVaMAwwCgYDVR0VBAMKAQQwIwIEOUnh2BcNMDAwNjMwMDYxMz
+ MxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4mgXDTAwMDYzMDA2MTMzOVowDDAKBgNVHRUEAwoBBDAjAgQ
+ 5SeQvFw0wMDA2MzAwNjEzNDRaMAwwCgYDVR0VBAMKAQQwIwIEOVsGJRcNMDAwNjMwMDYxMzUwWjAM
+ MAoGA1UdFQQDCgEEMCMCBDlbBusXDTAwMDYzMDA2MTM1NlowDDAKBgNVHRUEAwoBBDAjAgQ5XEKPF
+ w0wMDA3MTMwOTAwMzhaMAwwCgYDVR0VBAMKAQQwIwIEOVxEKRcNMDAwNzEzMDkwMDQ1WjAMMAoGA1
+ UdFQQDCgEEMCMCBDlcRukXDTAwMDcyNjA2MjkyN1owDDAKBgNVHRUEAwoBBDAjAgQ5fohgFw0wMDA
+ 3MjYwNjQ2NTFaMAwwCgYDVR0VBAMKAQQwIwIEOaNqPBcNMDAwODIzMDYwOTQxWjAMMAoGA1UdFQQD
+ CgEFMCMCBDlcX2QXDTAwMDgzMTA3MTM1OFowDDAKBgNVHRUEAwoBBDAjAgQ5YsflFw0wMDA5MDEwM
+ TQwMjRaMAwwCgYDVR0VBAMKAQQwIwIEOWGHDRcNMDAwOTA2MDcwMTE2WjAMMAoGA1UdFQQDCgEEMC
+ MCBDliz/4XDTAwMDkwNjA3MDcwNVowDDAKBgNVHRUEAwoBBDAjAgQ5m3S6Fw0wMDA5MjAwNzA2NTd
+ aMAwwCgYDVR0VBAMKAQQwIwIEOy6/hhcNMDEwNzAzMDYxMDQyWjAMMAoGA1UdFQQDCgEEMCMCBDtB
+ Yw4XDTAxMDcwMzA2MTkxNlowDDAKBgNVHRUEAwoBBDAjAgQ7MEG6Fw0wMTA3MTAwODA5NTNaMAwwC
+ gYDVR0VBAMKAQQwIwIEOy68CxcNMDEwNzExMDYxMzI5WjAMMAoGA1UdFQQDCgEEMCMCBDswSOsXDT
+ AxMDgwMTA0MTkyM1owDDAKBgNVHRUEAwoBBTAjAgQ7MYgeFw0wMTA4MDEwNDIwMDJaMAwwCgYDVR0
+ VBAMKAQQwIwIEOzGHeBcNMDEwODAyMDI0NTM4WjAMMAoGA1UdFQQDCgEEMCMCBDsuveEXDTAxMDgz
+ MDA2MjIwOFowDDAKBgNVHRUEAwoBBDAjAgQ7jdxLFw0wMTA4MzAwNjQzMjRaMAwwCgYDVR0VBAMKA
+ QQwIwIEOy67QxcNMDExMTIxMDYyMDUzWjAMMAoGA1UdFQQDCgEEMCMCBDsDNXcXDTAyMDUxNzA4ND
+ Y0MlowDDAKBgNVHRUEAwoBBDAjAgQ7AzXMFw0wMjA1MTcwODQ2NTdaMAwwCgYDVR0VBAMKAQSgMjA
+ wMAsGA1UdFAQEAgIQoDATBgNVHSMEDDAKgAhISAKVrWisNzAMBgNVHRwBAf8EAjAAMA0GCSqGSIb3
+ DQEBBQUAA4IBAQA1xNXgyrtVB5LSOc76JF+aJzf8IfJGqF04CMzbo4lDpec/LgOrTSFV223ccJzuq
+ cnxGUfDbXFfSWDHGnj9HLLTCkrS3clL1TPVjGXg5mFu1l6DCfcP2v4i4dlradNYDQg/AVBoJsYa3l
+ efSFHw8RFXNHJWwIjJA6J0CBJ/8Uq2ywr8umdndb10RLtPWp66A7wxu7OvTjt68d3LgSniQ0mIJCn
+ 4ooE30oF/ew0EznbxlSCNRPpB8jYYJTibGrTUVU43lr8h3URIgBkA4InOhuDv0ePMSCDSxBUhY0+G
+ eKo+YiXHy4SGUGLakahuq/hlGTRJJUddqFA1dNZdOUl23nVE
+
+dn: cn=charlie,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: charlie
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+
+dn: cn=beta,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: beta
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+certificateRevocationList;binary:: MIIP0TCCDrkCAQEwDQYJKoZIhvcNAQEFBQAwgZMxCzA
+ JBgNVBAYTAkFVMSswKQYDVQQKEyJDZXJ0aWZpY2F0ZXMgQXVzdHJhbGlhIFB0eSBMaW1pdGVkMSUw
+ IwYDVQQDExxDQVBMIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTAwLgYKCZImiZPyLGQBAxQgY2FAY
+ 2VydGlmaWNhdGVzLWF1c3RyYWxpYS5jb20uYXUXDTAzMDcyMjAxMzAyMFoXDTAzMTEwMzAxMzUyMF
+ owgg27MCMCBDi/biUXDTAwMDMwNjA2MjEzM1owDDAKBgNVHRUEAwoBBDAjAgQ5Il0KFw0wMDA1MjM
+ wODAwNDNaMAwwCgYDVR0VBAMKAQQwIwIEOSo6ZxcNMDAwNTI5MDIyNTQzWjAMMAoGA1UdFQQDCgEE
+ MCMCBDkx1QAXDTAwMDUyOTAzMzYwMVowDDAKBgNVHRUEAwoBBDAjAgQ5Pd7GFw0wMDA2MDcwNTM3M
+ jRaMAwwCgYDVR0VBAMKAQQwIwIEOUcavBcNMDAwNjE0MDc0MjExWjAMMAoGA1UdFQQDCgEEMCMCBD
+ lIlLYXDTAwMDYxNTA4MzY1NlowDDAKBgNVHRUEAwoBBDAjAgQ5SeOkFw0wMDA2MTYwODIzMDVaMAw
+ wCgYDVR0VBAMKAQQwIwIEOUiGjRcNMDAwNjE2MDgyMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ30oX
+ DTAwMDYyOTA4MDQyM1owDDAKBgNVHRUEAwoBBDAjAgQ5SdUjFw0wMDA2MjkwODA1NDVaMAwwCgYDV
+ R0VBAMKAQQwIwIEOTHlfRcNMDAwNjMwMDYwNjA1WjAMMAoGA1UdFQQDCgEEMCMCBDkzV6EXDTAwMD
+ YzMDA2MDYxMVowDDAKBgNVHRUEAwoBBDAjAgQ5SIFOFw0wMDA2MzAwNjA2MjFaMAwwCgYDVR0VBAM
+ KAQQwIwIEOUiCbBcNMDAwNjMwMDYwNjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlIgzkXDTAwMDYzMDA2
+ MDYzNlowDDAKBgNVHRUEAwoBBDAjAgQ5SIQEFw0wMDA2MzAwNjA2NDFaMAwwCgYDVR0VBAMKAQQwI
+ wIEOUiFBBcNMDAwNjMwMDYwNjQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlIhfQXDTAwMDYzMDA2MDY1NV
+ owDDAKBgNVHRUEAwoBBDAjAgQ5SIcmFw0wMDA2MzAwNjA3MDJaMAwwCgYDVR0VBAMKAQQwIwIEOUi
+ H4hcNMDAwNjMwMDYwNzA4WjAMMAoGA1UdFQQDCgEEMCMCBDlIiGUXDTAwMDYzMDA2MDcxNFowDDAK
+ BgNVHRUEAwoBBDAjAgQ5SIjaFw0wMDA2MzAwNjA3NDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiJhRcNM
+ DAwNjMwMDYwNzU3WjAMMAoGA1UdFQQDCgEEMCMCBDlIjoIXDTAwMDYzMDA2MDgwNFowDDAKBgNVHR
+ UEAwoBBDAjAgQ5SI89Fw0wMDA2MzAwNjA4MTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiP1RcNMDAwNjM
+ wMDYwODE1WjAMMAoGA1UdFQQDCgEEMCMCBDlIkEoXDTAwMDYzMDA2MDg0NVowDDAKBgNVHRUEAwoB
+ BDAjAgQ5SJC7Fw0wMDA2MzAwNjA4NTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiReRcNMDAwNjMwMDYwO
+ DU2WjAMMAoGA1UdFQQDCgEEMCMCBDlIkgMXDTAwMDYzMDA2MDkwNFowDDAKBgNVHRUEAwoBBDAjAg
+ Q5SJKqFw0wMDA2MzAwNjA5MDlaMAwwCgYDVR0VBAMKAQQwIwIEOUiTJhcNMDAwNjMwMDYwOTE2WjA
+ MMAoGA1UdFQQDCgEEMCMCBDlIk5AXDTAwMDYzMDA2MDkyMVowDDAKBgNVHRUEAwoBBDAjAgQ5SJQ3
+ Fw0wMDA2MzAwNjA5MjZaMAwwCgYDVR0VBAMKAQQwIwIEOUiVXhcNMDAwNjMwMDYwOTMyWjAMMAoGA
+ 1UdFQQDCgEEMCMCBDlIlgcXDTAwMDYzMDA2MDkzOFowDDAKBgNVHRUEAwoBBDAjAgQ5SJazFw0wMD
+ A2MzAwNjA5NDZaMAwwCgYDVR0VBAMKAQQwIwIEOUiXPxcNMDAwNjMwMDYwOTUxWjAMMAoGA1UdFQQ
+ DCgEEMCMCBDlIl7IXDTAwMDYzMDA2MDk1OFowDDAKBgNVHRUEAwoBBDAjAgQ5SJg0Fw0wMDA2MzAw
+ NjEwMDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiZBBcNMDAwNjMwMDYxMDA5WjAMMAoGA1UdFQQDCgEEM
+ CMCBDlJzksXDTAwMDYzMDA2MTAxNVowDDAKBgNVHRUEAwoBBDAjAgQ5Sc64Fw0wMDA2MzAwNjEwMj
+ FaMAwwCgYDVR0VBAMKAQQwIwIEOUnPVxcNMDAwNjMwMDYxMDI3WjAMMAoGA1UdFQQDCgEEMCMCBDl
+ J0BAXDTAwMDYzMDA2MTAzNVowDDAKBgNVHRUEAwoBBDAjAgQ5SdDKFw0wMDA2MzAwNjEwNDNaMAww
+ CgYDVR0VBAMKAQQwIwIEOUnRZRcNMDAwNjMwMDYxMDQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ0d0XD
+ TAwMDYzMDA2MTA1N1owDDAKBgNVHRUEAwoBBDAjAgQ5SdJ4Fw0wMDA2MzAwNjExMTVaMAwwCgYDVR
+ 0VBAMKAQQwIwIEOUnTDBcNMDAwNjMwMDYxMTIxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ04oXDTAwMDY
+ zMDA2MTEyN1owDDAKBgNVHRUEAwoBBDAjAgQ5SdQSFw0wMDA2MzAwNjExMzNaMAwwCgYDVR0VBAMK
+ AQQwIwIEOUnUoBcNMDAwNjMwMDYxMTM5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ2SQXDTAwMDYzMDA2M
+ TE1M1owDDAKBgNVHRUEAwoBBDAjAgQ5SdmwFw0wMDA2MzAwNjEyMDVaMAwwCgYDVR0VBAMKAQQwIw
+ IEOUnaTBcNMDAwNjMwMDYxMjExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ2vYXDTAwMDYzMDA2MTIxN1o
+ wDDAKBgNVHRUEAwoBBDAjAgQ5SducFw0wMDA2MzAwNjEyMjNaMAwwCgYDVR0VBAMKAQQwIwIEOUnc
+ IRcNMDAwNjMwMDYxMjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3KQXDTAwMDYzMDA2MTIzM1owDDAKB
+ gNVHRUEAwoBBDAjAgQ5Sd2xFw0wMDA2MzAwNjEyNDBaMAwwCgYDVR0VBAMKAQQwIwIEOUneRBcNMD
+ AwNjMwMDYxMjQ1WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3skXDTAwMDYzMDA2MTI1MVowDDAKBgNVHRU
+ EAwoBBDAjAgQ5Sd/IFw0wMDA2MzAwNjEzMDJaMAwwCgYDVR0VBAMKAQQwIwIEOUngPRcNMDAwNjMw
+ MDYxMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4M8XDTAwMDYzMDA2MTMyMFowDDAKBgNVHRUEAwoBB
+ DAjAgQ5SeE/Fw0wMDA2MzAwNjEzMjVaMAwwCgYDVR0VBAMKAQQwIwIEOUnh2BcNMDAwNjMwMDYxMz
+ MxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4mgXDTAwMDYzMDA2MTMzOVowDDAKBgNVHRUEAwoBBDAjAgQ
+ 5SeQvFw0wMDA2MzAwNjEzNDRaMAwwCgYDVR0VBAMKAQQwIwIEOVsGJRcNMDAwNjMwMDYxMzUwWjAM
+ MAoGA1UdFQQDCgEEMCMCBDlbBusXDTAwMDYzMDA2MTM1NlowDDAKBgNVHRUEAwoBBDAjAgQ5XEKPF
+ w0wMDA3MTMwOTAwMzhaMAwwCgYDVR0VBAMKAQQwIwIEOVxEKRcNMDAwNzEzMDkwMDQ1WjAMMAoGA1
+ UdFQQDCgEEMCMCBDlcRukXDTAwMDcyNjA2MjkyN1owDDAKBgNVHRUEAwoBBDAjAgQ5fohgFw0wMDA
+ 3MjYwNjQ2NTFaMAwwCgYDVR0VBAMKAQQwIwIEOaNqPBcNMDAwODIzMDYwOTQxWjAMMAoGA1UdFQQD
+ CgEFMCMCBDlcX2QXDTAwMDgzMTA3MTM1OFowDDAKBgNVHRUEAwoBBDAjAgQ5YsflFw0wMDA5MDEwM
+ TQwMjRaMAwwCgYDVR0VBAMKAQQwIwIEOWGHDRcNMDAwOTA2MDcwMTE2WjAMMAoGA1UdFQQDCgEEMC
+ MCBDliz/4XDTAwMDkwNjA3MDcwNVowDDAKBgNVHRUEAwoBBDAjAgQ5m3S6Fw0wMDA5MjAwNzA2NTd
+ aMAwwCgYDVR0VBAMKAQQwIwIEOy6/hhcNMDEwNzAzMDYxMDQyWjAMMAoGA1UdFQQDCgEEMCMCBDtB
+ Yw4XDTAxMDcwMzA2MTkxNlowDDAKBgNVHRUEAwoBBDAjAgQ7MEG6Fw0wMTA3MTAwODA5NTNaMAwwC
+ gYDVR0VBAMKAQQwIwIEOy68CxcNMDEwNzExMDYxMzI5WjAMMAoGA1UdFQQDCgEEMCMCBDswSOsXDT
+ AxMDgwMTA0MTkyM1owDDAKBgNVHRUEAwoBBTAjAgQ7MYgeFw0wMTA4MDEwNDIwMDJaMAwwCgYDVR0
+ VBAMKAQQwIwIEOzGHeBcNMDEwODAyMDI0NTM4WjAMMAoGA1UdFQQDCgEEMCMCBDsuveEXDTAxMDgz
+ MDA2MjIwOFowDDAKBgNVHRUEAwoBBDAjAgQ7jdxLFw0wMTA4MzAwNjQzMjRaMAwwCgYDVR0VBAMKA
+ QQwIwIEOy67QxcNMDExMTIxMDYyMDUzWjAMMAoGA1UdFQQDCgEEMCMCBDsDNXcXDTAyMDUxNzA4ND
+ Y0MlowDDAKBgNVHRUEAwoBBDAjAgQ7AzXMFw0wMjA1MTcwODQ2NTdaMAwwCgYDVR0VBAMKAQSgMjA
+ wMAsGA1UdFAQEAgIQoDATBgNVHSMEDDAKgAhISAKVrWisNzAMBgNVHRwBAf8EAjAAMA0GCSqGSIb3
+ DQEBBQUAA4IBAQA1xNXgyrtVB5LSOc76JF+aJzf8IfJGqF04CMzbo4lDpec/LgOrTSFV223ccJzuq
+ cnxGUfDbXFfSWDHGnj9HLLTCkrS3clL1TPVjGXg5mFu1l6DCfcP2v4i4dlradNYDQg/AVBoJsYa3l
+ efSFHw8RFXNHJWwIjJA6J0CBJ/8Uq2ywr8umdndb10RLtPWp66A7wxu7OvTjt68d3LgSniQ0mIJCn
+ 4ooE30oF/ew0EznbxlSCNRPpB8jYYJTibGrTUVU43lr8h3URIgBkA4InOhuDv0ePMSCDSxBUhY0+G
+ eKo+YiXHy4SGUGLakahuq/hlGTRJJUddqFA1dNZdOUl23nVE
+
+dn: cn=charlie,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: charlie
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+
+dn: cn=beta,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: beta
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+certificateRevocationList;binary:: MIIP0TCCDrkCAQEwDQYJKoZIhvcNAQEFBQAwgZMxCzA
+ JBgNVBAYTAkFVMSswKQYDVQQKEyJDZXJ0aWZpY2F0ZXMgQXVzdHJhbGlhIFB0eSBMaW1pdGVkMSUw
+ IwYDVQQDExxDQVBMIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTAwLgYKCZImiZPyLGQBAxQgY2FAY
+ 2VydGlmaWNhdGVzLWF1c3RyYWxpYS5jb20uYXUXDTAzMDcyMjAxMzAyMFoXDTAzMTEwMzAxMzUyMF
+ owgg27MCMCBDi/biUXDTAwMDMwNjA2MjEzM1owDDAKBgNVHRUEAwoBBDAjAgQ5Il0KFw0wMDA1MjM
+ wODAwNDNaMAwwCgYDVR0VBAMKAQQwIwIEOSo6ZxcNMDAwNTI5MDIyNTQzWjAMMAoGA1UdFQQDCgEE
+ MCMCBDkx1QAXDTAwMDUyOTAzMzYwMVowDDAKBgNVHRUEAwoBBDAjAgQ5Pd7GFw0wMDA2MDcwNTM3M
+ jRaMAwwCgYDVR0VBAMKAQQwIwIEOUcavBcNMDAwNjE0MDc0MjExWjAMMAoGA1UdFQQDCgEEMCMCBD
+ lIlLYXDTAwMDYxNTA4MzY1NlowDDAKBgNVHRUEAwoBBDAjAgQ5SeOkFw0wMDA2MTYwODIzMDVaMAw
+ wCgYDVR0VBAMKAQQwIwIEOUiGjRcNMDAwNjE2MDgyMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ30oX
+ DTAwMDYyOTA4MDQyM1owDDAKBgNVHRUEAwoBBDAjAgQ5SdUjFw0wMDA2MjkwODA1NDVaMAwwCgYDV
+ R0VBAMKAQQwIwIEOTHlfRcNMDAwNjMwMDYwNjA1WjAMMAoGA1UdFQQDCgEEMCMCBDkzV6EXDTAwMD
+ YzMDA2MDYxMVowDDAKBgNVHRUEAwoBBDAjAgQ5SIFOFw0wMDA2MzAwNjA2MjFaMAwwCgYDVR0VBAM
+ KAQQwIwIEOUiCbBcNMDAwNjMwMDYwNjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlIgzkXDTAwMDYzMDA2
+ MDYzNlowDDAKBgNVHRUEAwoBBDAjAgQ5SIQEFw0wMDA2MzAwNjA2NDFaMAwwCgYDVR0VBAMKAQQwI
+ wIEOUiFBBcNMDAwNjMwMDYwNjQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlIhfQXDTAwMDYzMDA2MDY1NV
+ owDDAKBgNVHRUEAwoBBDAjAgQ5SIcmFw0wMDA2MzAwNjA3MDJaMAwwCgYDVR0VBAMKAQQwIwIEOUi
+ H4hcNMDAwNjMwMDYwNzA4WjAMMAoGA1UdFQQDCgEEMCMCBDlIiGUXDTAwMDYzMDA2MDcxNFowDDAK
+ BgNVHRUEAwoBBDAjAgQ5SIjaFw0wMDA2MzAwNjA3NDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiJhRcNM
+ DAwNjMwMDYwNzU3WjAMMAoGA1UdFQQDCgEEMCMCBDlIjoIXDTAwMDYzMDA2MDgwNFowDDAKBgNVHR
+ UEAwoBBDAjAgQ5SI89Fw0wMDA2MzAwNjA4MTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiP1RcNMDAwNjM
+ wMDYwODE1WjAMMAoGA1UdFQQDCgEEMCMCBDlIkEoXDTAwMDYzMDA2MDg0NVowDDAKBgNVHRUEAwoB
+ BDAjAgQ5SJC7Fw0wMDA2MzAwNjA4NTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiReRcNMDAwNjMwMDYwO
+ DU2WjAMMAoGA1UdFQQDCgEEMCMCBDlIkgMXDTAwMDYzMDA2MDkwNFowDDAKBgNVHRUEAwoBBDAjAg
+ Q5SJKqFw0wMDA2MzAwNjA5MDlaMAwwCgYDVR0VBAMKAQQwIwIEOUiTJhcNMDAwNjMwMDYwOTE2WjA
+ MMAoGA1UdFQQDCgEEMCMCBDlIk5AXDTAwMDYzMDA2MDkyMVowDDAKBgNVHRUEAwoBBDAjAgQ5SJQ3
+ Fw0wMDA2MzAwNjA5MjZaMAwwCgYDVR0VBAMKAQQwIwIEOUiVXhcNMDAwNjMwMDYwOTMyWjAMMAoGA
+ 1UdFQQDCgEEMCMCBDlIlgcXDTAwMDYzMDA2MDkzOFowDDAKBgNVHRUEAwoBBDAjAgQ5SJazFw0wMD
+ A2MzAwNjA5NDZaMAwwCgYDVR0VBAMKAQQwIwIEOUiXPxcNMDAwNjMwMDYwOTUxWjAMMAoGA1UdFQQ
+ DCgEEMCMCBDlIl7IXDTAwMDYzMDA2MDk1OFowDDAKBgNVHRUEAwoBBDAjAgQ5SJg0Fw0wMDA2MzAw
+ NjEwMDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiZBBcNMDAwNjMwMDYxMDA5WjAMMAoGA1UdFQQDCgEEM
+ CMCBDlJzksXDTAwMDYzMDA2MTAxNVowDDAKBgNVHRUEAwoBBDAjAgQ5Sc64Fw0wMDA2MzAwNjEwMj
+ FaMAwwCgYDVR0VBAMKAQQwIwIEOUnPVxcNMDAwNjMwMDYxMDI3WjAMMAoGA1UdFQQDCgEEMCMCBDl
+ J0BAXDTAwMDYzMDA2MTAzNVowDDAKBgNVHRUEAwoBBDAjAgQ5SdDKFw0wMDA2MzAwNjEwNDNaMAww
+ CgYDVR0VBAMKAQQwIwIEOUnRZRcNMDAwNjMwMDYxMDQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ0d0XD
+ TAwMDYzMDA2MTA1N1owDDAKBgNVHRUEAwoBBDAjAgQ5SdJ4Fw0wMDA2MzAwNjExMTVaMAwwCgYDVR
+ 0VBAMKAQQwIwIEOUnTDBcNMDAwNjMwMDYxMTIxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ04oXDTAwMDY
+ zMDA2MTEyN1owDDAKBgNVHRUEAwoBBDAjAgQ5SdQSFw0wMDA2MzAwNjExMzNaMAwwCgYDVR0VBAMK
+ AQQwIwIEOUnUoBcNMDAwNjMwMDYxMTM5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ2SQXDTAwMDYzMDA2M
+ TE1M1owDDAKBgNVHRUEAwoBBDAjAgQ5SdmwFw0wMDA2MzAwNjEyMDVaMAwwCgYDVR0VBAMKAQQwIw
+ IEOUnaTBcNMDAwNjMwMDYxMjExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ2vYXDTAwMDYzMDA2MTIxN1o
+ wDDAKBgNVHRUEAwoBBDAjAgQ5SducFw0wMDA2MzAwNjEyMjNaMAwwCgYDVR0VBAMKAQQwIwIEOUnc
+ IRcNMDAwNjMwMDYxMjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3KQXDTAwMDYzMDA2MTIzM1owDDAKB
+ gNVHRUEAwoBBDAjAgQ5Sd2xFw0wMDA2MzAwNjEyNDBaMAwwCgYDVR0VBAMKAQQwIwIEOUneRBcNMD
+ AwNjMwMDYxMjQ1WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3skXDTAwMDYzMDA2MTI1MVowDDAKBgNVHRU
+ EAwoBBDAjAgQ5Sd/IFw0wMDA2MzAwNjEzMDJaMAwwCgYDVR0VBAMKAQQwIwIEOUngPRcNMDAwNjMw
+ MDYxMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4M8XDTAwMDYzMDA2MTMyMFowDDAKBgNVHRUEAwoBB
+ DAjAgQ5SeE/Fw0wMDA2MzAwNjEzMjVaMAwwCgYDVR0VBAMKAQQwIwIEOUnh2BcNMDAwNjMwMDYxMz
+ MxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4mgXDTAwMDYzMDA2MTMzOVowDDAKBgNVHRUEAwoBBDAjAgQ
+ 5SeQvFw0wMDA2MzAwNjEzNDRaMAwwCgYDVR0VBAMKAQQwIwIEOVsGJRcNMDAwNjMwMDYxMzUwWjAM
+ MAoGA1UdFQQDCgEEMCMCBDlbBusXDTAwMDYzMDA2MTM1NlowDDAKBgNVHRUEAwoBBDAjAgQ5XEKPF
+ w0wMDA3MTMwOTAwMzhaMAwwCgYDVR0VBAMKAQQwIwIEOVxEKRcNMDAwNzEzMDkwMDQ1WjAMMAoGA1
+ UdFQQDCgEEMCMCBDlcRukXDTAwMDcyNjA2MjkyN1owDDAKBgNVHRUEAwoBBDAjAgQ5fohgFw0wMDA
+ 3MjYwNjQ2NTFaMAwwCgYDVR0VBAMKAQQwIwIEOaNqPBcNMDAwODIzMDYwOTQxWjAMMAoGA1UdFQQD
+ CgEFMCMCBDlcX2QXDTAwMDgzMTA3MTM1OFowDDAKBgNVHRUEAwoBBDAjAgQ5YsflFw0wMDA5MDEwM
+ TQwMjRaMAwwCgYDVR0VBAMKAQQwIwIEOWGHDRcNMDAwOTA2MDcwMTE2WjAMMAoGA1UdFQQDCgEEMC
+ MCBDliz/4XDTAwMDkwNjA3MDcwNVowDDAKBgNVHRUEAwoBBDAjAgQ5m3S6Fw0wMDA5MjAwNzA2NTd
+ aMAwwCgYDVR0VBAMKAQQwIwIEOy6/hhcNMDEwNzAzMDYxMDQyWjAMMAoGA1UdFQQDCgEEMCMCBDtB
+ Yw4XDTAxMDcwMzA2MTkxNlowDDAKBgNVHRUEAwoBBDAjAgQ7MEG6Fw0wMTA3MTAwODA5NTNaMAwwC
+ gYDVR0VBAMKAQQwIwIEOy68CxcNMDEwNzExMDYxMzI5WjAMMAoGA1UdFQQDCgEEMCMCBDswSOsXDT
+ AxMDgwMTA0MTkyM1owDDAKBgNVHRUEAwoBBTAjAgQ7MYgeFw0wMTA4MDEwNDIwMDJaMAwwCgYDVR0
+ VBAMKAQQwIwIEOzGHeBcNMDEwODAyMDI0NTM4WjAMMAoGA1UdFQQDCgEEMCMCBDsuveEXDTAxMDgz
+ MDA2MjIwOFowDDAKBgNVHRUEAwoBBDAjAgQ7jdxLFw0wMTA4MzAwNjQzMjRaMAwwCgYDVR0VBAMKA
+ QQwIwIEOy67QxcNMDExMTIxMDYyMDUzWjAMMAoGA1UdFQQDCgEEMCMCBDsDNXcXDTAyMDUxNzA4ND
+ Y0MlowDDAKBgNVHRUEAwoBBDAjAgQ7AzXMFw0wMjA1MTcwODQ2NTdaMAwwCgYDVR0VBAMKAQSgMjA
+ wMAsGA1UdFAQEAgIQoDATBgNVHSMEDDAKgAhISAKVrWisNzAMBgNVHRwBAf8EAjAAMA0GCSqGSIb3
+ DQEBBQUAA4IBAQA1xNXgyrtVB5LSOc76JF+aJzf8IfJGqF04CMzbo4lDpec/LgOrTSFV223ccJzuq
+ cnxGUfDbXFfSWDHGnj9HLLTCkrS3clL1TPVjGXg5mFu1l6DCfcP2v4i4dlradNYDQg/AVBoJsYa3l
+ efSFHw8RFXNHJWwIjJA6J0CBJ/8Uq2ywr8umdndb10RLtPWp66A7wxu7OvTjt68d3LgSniQ0mIJCn
+ 4ooE30oF/ew0EznbxlSCNRPpB8jYYJTibGrTUVU43lr8h3URIgBkA4InOhuDv0ePMSCDSxBUhY0+G
+ eKo+YiXHy4SGUGLakahuq/hlGTRJJUddqFA1dNZdOUl23nVE
+
+dn: cn=charlie,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: charlie
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+
+dn: cn=beta,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: beta
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+certificateRevocationList;binary:: MIIP0TCCDrkCAQEwDQYJKoZIhvcNAQEFBQAwgZMxCzA
+ JBgNVBAYTAkFVMSswKQYDVQQKEyJDZXJ0aWZpY2F0ZXMgQXVzdHJhbGlhIFB0eSBMaW1pdGVkMSUw
+ IwYDVQQDExxDQVBMIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTAwLgYKCZImiZPyLGQBAxQgY2FAY
+ 2VydGlmaWNhdGVzLWF1c3RyYWxpYS5jb20uYXUXDTAzMDcyMjAxMzAyMFoXDTAzMTEwMzAxMzUyMF
+ owgg27MCMCBDi/biUXDTAwMDMwNjA2MjEzM1owDDAKBgNVHRUEAwoBBDAjAgQ5Il0KFw0wMDA1MjM
+ wODAwNDNaMAwwCgYDVR0VBAMKAQQwIwIEOSo6ZxcNMDAwNTI5MDIyNTQzWjAMMAoGA1UdFQQDCgEE
+ MCMCBDkx1QAXDTAwMDUyOTAzMzYwMVowDDAKBgNVHRUEAwoBBDAjAgQ5Pd7GFw0wMDA2MDcwNTM3M
+ jRaMAwwCgYDVR0VBAMKAQQwIwIEOUcavBcNMDAwNjE0MDc0MjExWjAMMAoGA1UdFQQDCgEEMCMCBD
+ lIlLYXDTAwMDYxNTA4MzY1NlowDDAKBgNVHRUEAwoBBDAjAgQ5SeOkFw0wMDA2MTYwODIzMDVaMAw
+ wCgYDVR0VBAMKAQQwIwIEOUiGjRcNMDAwNjE2MDgyMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ30oX
+ DTAwMDYyOTA4MDQyM1owDDAKBgNVHRUEAwoBBDAjAgQ5SdUjFw0wMDA2MjkwODA1NDVaMAwwCgYDV
+ R0VBAMKAQQwIwIEOTHlfRcNMDAwNjMwMDYwNjA1WjAMMAoGA1UdFQQDCgEEMCMCBDkzV6EXDTAwMD
+ YzMDA2MDYxMVowDDAKBgNVHRUEAwoBBDAjAgQ5SIFOFw0wMDA2MzAwNjA2MjFaMAwwCgYDVR0VBAM
+ KAQQwIwIEOUiCbBcNMDAwNjMwMDYwNjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlIgzkXDTAwMDYzMDA2
+ MDYzNlowDDAKBgNVHRUEAwoBBDAjAgQ5SIQEFw0wMDA2MzAwNjA2NDFaMAwwCgYDVR0VBAMKAQQwI
+ wIEOUiFBBcNMDAwNjMwMDYwNjQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlIhfQXDTAwMDYzMDA2MDY1NV
+ owDDAKBgNVHRUEAwoBBDAjAgQ5SIcmFw0wMDA2MzAwNjA3MDJaMAwwCgYDVR0VBAMKAQQwIwIEOUi
+ H4hcNMDAwNjMwMDYwNzA4WjAMMAoGA1UdFQQDCgEEMCMCBDlIiGUXDTAwMDYzMDA2MDcxNFowDDAK
+ BgNVHRUEAwoBBDAjAgQ5SIjaFw0wMDA2MzAwNjA3NDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiJhRcNM
+ DAwNjMwMDYwNzU3WjAMMAoGA1UdFQQDCgEEMCMCBDlIjoIXDTAwMDYzMDA2MDgwNFowDDAKBgNVHR
+ UEAwoBBDAjAgQ5SI89Fw0wMDA2MzAwNjA4MTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiP1RcNMDAwNjM
+ wMDYwODE1WjAMMAoGA1UdFQQDCgEEMCMCBDlIkEoXDTAwMDYzMDA2MDg0NVowDDAKBgNVHRUEAwoB
+ BDAjAgQ5SJC7Fw0wMDA2MzAwNjA4NTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiReRcNMDAwNjMwMDYwO
+ DU2WjAMMAoGA1UdFQQDCgEEMCMCBDlIkgMXDTAwMDYzMDA2MDkwNFowDDAKBgNVHRUEAwoBBDAjAg
+ Q5SJKqFw0wMDA2MzAwNjA5MDlaMAwwCgYDVR0VBAMKAQQwIwIEOUiTJhcNMDAwNjMwMDYwOTE2WjA
+ MMAoGA1UdFQQDCgEEMCMCBDlIk5AXDTAwMDYzMDA2MDkyMVowDDAKBgNVHRUEAwoBBDAjAgQ5SJQ3
+ Fw0wMDA2MzAwNjA5MjZaMAwwCgYDVR0VBAMKAQQwIwIEOUiVXhcNMDAwNjMwMDYwOTMyWjAMMAoGA
+ 1UdFQQDCgEEMCMCBDlIlgcXDTAwMDYzMDA2MDkzOFowDDAKBgNVHRUEAwoBBDAjAgQ5SJazFw0wMD
+ A2MzAwNjA5NDZaMAwwCgYDVR0VBAMKAQQwIwIEOUiXPxcNMDAwNjMwMDYwOTUxWjAMMAoGA1UdFQQ
+ DCgEEMCMCBDlIl7IXDTAwMDYzMDA2MDk1OFowDDAKBgNVHRUEAwoBBDAjAgQ5SJg0Fw0wMDA2MzAw
+ NjEwMDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiZBBcNMDAwNjMwMDYxMDA5WjAMMAoGA1UdFQQDCgEEM
+ CMCBDlJzksXDTAwMDYzMDA2MTAxNVowDDAKBgNVHRUEAwoBBDAjAgQ5Sc64Fw0wMDA2MzAwNjEwMj
+ FaMAwwCgYDVR0VBAMKAQQwIwIEOUnPVxcNMDAwNjMwMDYxMDI3WjAMMAoGA1UdFQQDCgEEMCMCBDl
+ J0BAXDTAwMDYzMDA2MTAzNVowDDAKBgNVHRUEAwoBBDAjAgQ5SdDKFw0wMDA2MzAwNjEwNDNaMAww
+ CgYDVR0VBAMKAQQwIwIEOUnRZRcNMDAwNjMwMDYxMDQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ0d0XD
+ TAwMDYzMDA2MTA1N1owDDAKBgNVHRUEAwoBBDAjAgQ5SdJ4Fw0wMDA2MzAwNjExMTVaMAwwCgYDVR
+ 0VBAMKAQQwIwIEOUnTDBcNMDAwNjMwMDYxMTIxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ04oXDTAwMDY
+ zMDA2MTEyN1owDDAKBgNVHRUEAwoBBDAjAgQ5SdQSFw0wMDA2MzAwNjExMzNaMAwwCgYDVR0VBAMK
+ AQQwIwIEOUnUoBcNMDAwNjMwMDYxMTM5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ2SQXDTAwMDYzMDA2M
+ TE1M1owDDAKBgNVHRUEAwoBBDAjAgQ5SdmwFw0wMDA2MzAwNjEyMDVaMAwwCgYDVR0VBAMKAQQwIw
+ IEOUnaTBcNMDAwNjMwMDYxMjExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ2vYXDTAwMDYzMDA2MTIxN1o
+ wDDAKBgNVHRUEAwoBBDAjAgQ5SducFw0wMDA2MzAwNjEyMjNaMAwwCgYDVR0VBAMKAQQwIwIEOUnc
+ IRcNMDAwNjMwMDYxMjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3KQXDTAwMDYzMDA2MTIzM1owDDAKB
+ gNVHRUEAwoBBDAjAgQ5Sd2xFw0wMDA2MzAwNjEyNDBaMAwwCgYDVR0VBAMKAQQwIwIEOUneRBcNMD
+ AwNjMwMDYxMjQ1WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3skXDTAwMDYzMDA2MTI1MVowDDAKBgNVHRU
+ EAwoBBDAjAgQ5Sd/IFw0wMDA2MzAwNjEzMDJaMAwwCgYDVR0VBAMKAQQwIwIEOUngPRcNMDAwNjMw
+ MDYxMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4M8XDTAwMDYzMDA2MTMyMFowDDAKBgNVHRUEAwoBB
+ DAjAgQ5SeE/Fw0wMDA2MzAwNjEzMjVaMAwwCgYDVR0VBAMKAQQwIwIEOUnh2BcNMDAwNjMwMDYxMz
+ MxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4mgXDTAwMDYzMDA2MTMzOVowDDAKBgNVHRUEAwoBBDAjAgQ
+ 5SeQvFw0wMDA2MzAwNjEzNDRaMAwwCgYDVR0VBAMKAQQwIwIEOVsGJRcNMDAwNjMwMDYxMzUwWjAM
+ MAoGA1UdFQQDCgEEMCMCBDlbBusXDTAwMDYzMDA2MTM1NlowDDAKBgNVHRUEAwoBBDAjAgQ5XEKPF
+ w0wMDA3MTMwOTAwMzhaMAwwCgYDVR0VBAMKAQQwIwIEOVxEKRcNMDAwNzEzMDkwMDQ1WjAMMAoGA1
+ UdFQQDCgEEMCMCBDlcRukXDTAwMDcyNjA2MjkyN1owDDAKBgNVHRUEAwoBBDAjAgQ5fohgFw0wMDA
+ 3MjYwNjQ2NTFaMAwwCgYDVR0VBAMKAQQwIwIEOaNqPBcNMDAwODIzMDYwOTQxWjAMMAoGA1UdFQQD
+ CgEFMCMCBDlcX2QXDTAwMDgzMTA3MTM1OFowDDAKBgNVHRUEAwoBBDAjAgQ5YsflFw0wMDA5MDEwM
+ TQwMjRaMAwwCgYDVR0VBAMKAQQwIwIEOWGHDRcNMDAwOTA2MDcwMTE2WjAMMAoGA1UdFQQDCgEEMC
+ MCBDliz/4XDTAwMDkwNjA3MDcwNVowDDAKBgNVHRUEAwoBBDAjAgQ5m3S6Fw0wMDA5MjAwNzA2NTd
+ aMAwwCgYDVR0VBAMKAQQwIwIEOy6/hhcNMDEwNzAzMDYxMDQyWjAMMAoGA1UdFQQDCgEEMCMCBDtB
+ Yw4XDTAxMDcwMzA2MTkxNlowDDAKBgNVHRUEAwoBBDAjAgQ7MEG6Fw0wMTA3MTAwODA5NTNaMAwwC
+ gYDVR0VBAMKAQQwIwIEOy68CxcNMDEwNzExMDYxMzI5WjAMMAoGA1UdFQQDCgEEMCMCBDswSOsXDT
+ AxMDgwMTA0MTkyM1owDDAKBgNVHRUEAwoBBTAjAgQ7MYgeFw0wMTA4MDEwNDIwMDJaMAwwCgYDVR0
+ VBAMKAQQwIwIEOzGHeBcNMDEwODAyMDI0NTM4WjAMMAoGA1UdFQQDCgEEMCMCBDsuveEXDTAxMDgz
+ MDA2MjIwOFowDDAKBgNVHRUEAwoBBDAjAgQ7jdxLFw0wMTA4MzAwNjQzMjRaMAwwCgYDVR0VBAMKA
+ QQwIwIEOy67QxcNMDExMTIxMDYyMDUzWjAMMAoGA1UdFQQDCgEEMCMCBDsDNXcXDTAyMDUxNzA4ND
+ Y0MlowDDAKBgNVHRUEAwoBBDAjAgQ7AzXMFw0wMjA1MTcwODQ2NTdaMAwwCgYDVR0VBAMKAQSgMjA
+ wMAsGA1UdFAQEAgIQoDATBgNVHSMEDDAKgAhISAKVrWisNzAMBgNVHRwBAf8EAjAAMA0GCSqGSIb3
+ DQEBBQUAA4IBAQA1xNXgyrtVB5LSOc76JF+aJzf8IfJGqF04CMzbo4lDpec/LgOrTSFV223ccJzuq
+ cnxGUfDbXFfSWDHGnj9HLLTCkrS3clL1TPVjGXg5mFu1l6DCfcP2v4i4dlradNYDQg/AVBoJsYa3l
+ efSFHw8RFXNHJWwIjJA6J0CBJ/8Uq2ywr8umdndb10RLtPWp66A7wxu7OvTjt68d3LgSniQ0mIJCn
+ 4ooE30oF/ew0EznbxlSCNRPpB8jYYJTibGrTUVU43lr8h3URIgBkA4InOhuDv0ePMSCDSxBUhY0+G
+ eKo+YiXHy4SGUGLakahuq/hlGTRJJUddqFA1dNZdOUl23nVE
+
+dn: cn=charlie,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: charlie
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+
+dn: cn=beta,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: extensibleObject
+uid:: Y2hhcmxpZSA=
+cn: beta
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDV
+ QQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA
+ 0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MO
+ NBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnA
+ KUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUA
+ zNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA
+ 0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTyS
+ uz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5Ue
+ dAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4Xr
+ EkBJ/Q=
+certificateRevocationList;binary:: MIIP0TCCDrkCAQEwDQYJKoZIhvcNAQEFBQAwgZMxCzA
+ JBgNVBAYTAkFVMSswKQYDVQQKEyJDZXJ0aWZpY2F0ZXMgQXVzdHJhbGlhIFB0eSBMaW1pdGVkMSUw
+ IwYDVQQDExxDQVBMIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTAwLgYKCZImiZPyLGQBAxQgY2FAY
+ 2VydGlmaWNhdGVzLWF1c3RyYWxpYS5jb20uYXUXDTAzMDcyMjAxMzAyMFoXDTAzMTEwMzAxMzUyMF
+ owgg27MCMCBDi/biUXDTAwMDMwNjA2MjEzM1owDDAKBgNVHRUEAwoBBDAjAgQ5Il0KFw0wMDA1MjM
+ wODAwNDNaMAwwCgYDVR0VBAMKAQQwIwIEOSo6ZxcNMDAwNTI5MDIyNTQzWjAMMAoGA1UdFQQDCgEE
+ MCMCBDkx1QAXDTAwMDUyOTAzMzYwMVowDDAKBgNVHRUEAwoBBDAjAgQ5Pd7GFw0wMDA2MDcwNTM3M
+ jRaMAwwCgYDVR0VBAMKAQQwIwIEOUcavBcNMDAwNjE0MDc0MjExWjAMMAoGA1UdFQQDCgEEMCMCBD
+ lIlLYXDTAwMDYxNTA4MzY1NlowDDAKBgNVHRUEAwoBBDAjAgQ5SeOkFw0wMDA2MTYwODIzMDVaMAw
+ wCgYDVR0VBAMKAQQwIwIEOUiGjRcNMDAwNjE2MDgyMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ30oX
+ DTAwMDYyOTA4MDQyM1owDDAKBgNVHRUEAwoBBDAjAgQ5SdUjFw0wMDA2MjkwODA1NDVaMAwwCgYDV
+ R0VBAMKAQQwIwIEOTHlfRcNMDAwNjMwMDYwNjA1WjAMMAoGA1UdFQQDCgEEMCMCBDkzV6EXDTAwMD
+ YzMDA2MDYxMVowDDAKBgNVHRUEAwoBBDAjAgQ5SIFOFw0wMDA2MzAwNjA2MjFaMAwwCgYDVR0VBAM
+ KAQQwIwIEOUiCbBcNMDAwNjMwMDYwNjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlIgzkXDTAwMDYzMDA2
+ MDYzNlowDDAKBgNVHRUEAwoBBDAjAgQ5SIQEFw0wMDA2MzAwNjA2NDFaMAwwCgYDVR0VBAMKAQQwI
+ wIEOUiFBBcNMDAwNjMwMDYwNjQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlIhfQXDTAwMDYzMDA2MDY1NV
+ owDDAKBgNVHRUEAwoBBDAjAgQ5SIcmFw0wMDA2MzAwNjA3MDJaMAwwCgYDVR0VBAMKAQQwIwIEOUi
+ H4hcNMDAwNjMwMDYwNzA4WjAMMAoGA1UdFQQDCgEEMCMCBDlIiGUXDTAwMDYzMDA2MDcxNFowDDAK
+ BgNVHRUEAwoBBDAjAgQ5SIjaFw0wMDA2MzAwNjA3NDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiJhRcNM
+ DAwNjMwMDYwNzU3WjAMMAoGA1UdFQQDCgEEMCMCBDlIjoIXDTAwMDYzMDA2MDgwNFowDDAKBgNVHR
+ UEAwoBBDAjAgQ5SI89Fw0wMDA2MzAwNjA4MTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiP1RcNMDAwNjM
+ wMDYwODE1WjAMMAoGA1UdFQQDCgEEMCMCBDlIkEoXDTAwMDYzMDA2MDg0NVowDDAKBgNVHRUEAwoB
+ BDAjAgQ5SJC7Fw0wMDA2MzAwNjA4NTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiReRcNMDAwNjMwMDYwO
+ DU2WjAMMAoGA1UdFQQDCgEEMCMCBDlIkgMXDTAwMDYzMDA2MDkwNFowDDAKBgNVHRUEAwoBBDAjAg
+ Q5SJKqFw0wMDA2MzAwNjA5MDlaMAwwCgYDVR0VBAMKAQQwIwIEOUiTJhcNMDAwNjMwMDYwOTE2WjA
+ MMAoGA1UdFQQDCgEEMCMCBDlIk5AXDTAwMDYzMDA2MDkyMVowDDAKBgNVHRUEAwoBBDAjAgQ5SJQ3
+ Fw0wMDA2MzAwNjA5MjZaMAwwCgYDVR0VBAMKAQQwIwIEOUiVXhcNMDAwNjMwMDYwOTMyWjAMMAoGA
+ 1UdFQQDCgEEMCMCBDlIlgcXDTAwMDYzMDA2MDkzOFowDDAKBgNVHRUEAwoBBDAjAgQ5SJazFw0wMD
+ A2MzAwNjA5NDZaMAwwCgYDVR0VBAMKAQQwIwIEOUiXPxcNMDAwNjMwMDYwOTUxWjAMMAoGA1UdFQQ
+ DCgEEMCMCBDlIl7IXDTAwMDYzMDA2MDk1OFowDDAKBgNVHRUEAwoBBDAjAgQ5SJg0Fw0wMDA2MzAw
+ NjEwMDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiZBBcNMDAwNjMwMDYxMDA5WjAMMAoGA1UdFQQDCgEEM
+ CMCBDlJzksXDTAwMDYzMDA2MTAxNVowDDAKBgNVHRUEAwoBBDAjAgQ5Sc64Fw0wMDA2MzAwNjEwMj
+ FaMAwwCgYDVR0VBAMKAQQwIwIEOUnPVxcNMDAwNjMwMDYxMDI3WjAMMAoGA1UdFQQDCgEEMCMCBDl
+ J0BAXDTAwMDYzMDA2MTAzNVowDDAKBgNVHRUEAwoBBDAjAgQ5SdDKFw0wMDA2MzAwNjEwNDNaMAww
+ CgYDVR0VBAMKAQQwIwIEOUnRZRcNMDAwNjMwMDYxMDQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ0d0XD
+ TAwMDYzMDA2MTA1N1owDDAKBgNVHRUEAwoBBDAjAgQ5SdJ4Fw0wMDA2MzAwNjExMTVaMAwwCgYDVR
+ 0VBAMKAQQwIwIEOUnTDBcNMDAwNjMwMDYxMTIxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ04oXDTAwMDY
+ zMDA2MTEyN1owDDAKBgNVHRUEAwoBBDAjAgQ5SdQSFw0wMDA2MzAwNjExMzNaMAwwCgYDVR0VBAMK
+ AQQwIwIEOUnUoBcNMDAwNjMwMDYxMTM5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ2SQXDTAwMDYzMDA2M
+ TE1M1owDDAKBgNVHRUEAwoBBDAjAgQ5SdmwFw0wMDA2MzAwNjEyMDVaMAwwCgYDVR0VBAMKAQQwIw
+ IEOUnaTBcNMDAwNjMwMDYxMjExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ2vYXDTAwMDYzMDA2MTIxN1o
+ wDDAKBgNVHRUEAwoBBDAjAgQ5SducFw0wMDA2MzAwNjEyMjNaMAwwCgYDVR0VBAMKAQQwIwIEOUnc
+ IRcNMDAwNjMwMDYxMjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3KQXDTAwMDYzMDA2MTIzM1owDDAKB
+ gNVHRUEAwoBBDAjAgQ5Sd2xFw0wMDA2MzAwNjEyNDBaMAwwCgYDVR0VBAMKAQQwIwIEOUneRBcNMD
+ AwNjMwMDYxMjQ1WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3skXDTAwMDYzMDA2MTI1MVowDDAKBgNVHRU
+ EAwoBBDAjAgQ5Sd/IFw0wMDA2MzAwNjEzMDJaMAwwCgYDVR0VBAMKAQQwIwIEOUngPRcNMDAwNjMw
+ MDYxMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4M8XDTAwMDYzMDA2MTMyMFowDDAKBgNVHRUEAwoBB
+ DAjAgQ5SeE/Fw0wMDA2MzAwNjEzMjVaMAwwCgYDVR0VBAMKAQQwIwIEOUnh2BcNMDAwNjMwMDYxMz
+ MxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4mgXDTAwMDYzMDA2MTMzOVowDDAKBgNVHRUEAwoBBDAjAgQ
+ 5SeQvFw0wMDA2MzAwNjEzNDRaMAwwCgYDVR0VBAMKAQQwIwIEOVsGJRcNMDAwNjMwMDYxMzUwWjAM
+ MAoGA1UdFQQDCgEEMCMCBDlbBusXDTAwMDYzMDA2MTM1NlowDDAKBgNVHRUEAwoBBDAjAgQ5XEKPF
+ w0wMDA3MTMwOTAwMzhaMAwwCgYDVR0VBAMKAQQwIwIEOVxEKRcNMDAwNzEzMDkwMDQ1WjAMMAoGA1
+ UdFQQDCgEEMCMCBDlcRukXDTAwMDcyNjA2MjkyN1owDDAKBgNVHRUEAwoBBDAjAgQ5fohgFw0wMDA
+ 3MjYwNjQ2NTFaMAwwCgYDVR0VBAMKAQQwIwIEOaNqPBcNMDAwODIzMDYwOTQxWjAMMAoGA1UdFQQD
+ CgEFMCMCBDlcX2QXDTAwMDgzMTA3MTM1OFowDDAKBgNVHRUEAwoBBDAjAgQ5YsflFw0wMDA5MDEwM
+ TQwMjRaMAwwCgYDVR0VBAMKAQQwIwIEOWGHDRcNMDAwOTA2MDcwMTE2WjAMMAoGA1UdFQQDCgEEMC
+ MCBDliz/4XDTAwMDkwNjA3MDcwNVowDDAKBgNVHRUEAwoBBDAjAgQ5m3S6Fw0wMDA5MjAwNzA2NTd
+ aMAwwCgYDVR0VBAMKAQQwIwIEOy6/hhcNMDEwNzAzMDYxMDQyWjAMMAoGA1UdFQQDCgEEMCMCBDtB
+ Yw4XDTAxMDcwMzA2MTkxNlowDDAKBgNVHRUEAwoBBDAjAgQ7MEG6Fw0wMTA3MTAwODA5NTNaMAwwC
+ gYDVR0VBAMKAQQwIwIEOy68CxcNMDEwNzExMDYxMzI5WjAMMAoGA1UdFQQDCgEEMCMCBDswSOsXDT
+ AxMDgwMTA0MTkyM1owDDAKBgNVHRUEAwoBBTAjAgQ7MYgeFw0wMTA4MDEwNDIwMDJaMAwwCgYDVR0
+ VBAMKAQQwIwIEOzGHeBcNMDEwODAyMDI0NTM4WjAMMAoGA1UdFQQDCgEEMCMCBDsuveEXDTAxMDgz
+ MDA2MjIwOFowDDAKBgNVHRUEAwoBBDAjAgQ7jdxLFw0wMTA4MzAwNjQzMjRaMAwwCgYDVR0VBAMKA
+ QQwIwIEOy67QxcNMDExMTIxMDYyMDUzWjAMMAoGA1UdFQQDCgEEMCMCBDsDNXcXDTAyMDUxNzA4ND
+ Y0MlowDDAKBgNVHRUEAwoBBDAjAgQ7AzXMFw0wMjA1MTcwODQ2NTdaMAwwCgYDVR0VBAMKAQSgMjA
+ wMAsGA1UdFAQEAgIQoDATBgNVHSMEDDAKgAhISAKVrWisNzAMBgNVHRwBAf8EAjAAMA0GCSqGSIb3
+ DQEBBQUAA4IBAQA1xNXgyrtVB5LSOc76JF+aJzf8IfJGqF04CMzbo4lDpec/LgOrTSFV223ccJzuq
+ cnxGUfDbXFfSWDHGnj9HLLTCkrS3clL1TPVjGXg5mFu1l6DCfcP2v4i4dlradNYDQg/AVBoJsYa3l
+ efSFHw8RFXNHJWwIjJA6J0CBJ/8Uq2ywr8umdndb10RLtPWp66A7wxu7OvTjt68d3LgSniQ0mIJCn
+ 4ooE30oF/ew0EznbxlSCNRPpB8jYYJTibGrTUVU43lr8h3URIgBkA4InOhuDv0ePMSCDSxBUhY0+G
+ eKo+YiXHy4SGUGLakahuq/hlGTRJJUddqFA1dNZdOUl23nVE
+
diff --git a/tests/data/constraint/constraint.out b/tests/data/constraint/constraint.out
new file mode 100644
index 0000000..d7bcca4
--- /dev/null
+++ b/tests/data/constraint/constraint.out
@@ -0,0 +1,31 @@
+OK
+OK
+OK
+OK
+OK
+OK
+OK
+OK
+OK
+OK
+OK
+OK
+OK
+OK
+OK
+FAIL
+FAIL
+FAIL
+FAIL
+FAIL
+FAIL
+FAIL
+FAIL
+FAIL
+FAIL
+FAIL
+FAIL
+FAIL
+FAIL
+FAIL
+FAIL
diff --git a/tests/data/constraint/root.ldif b/tests/data/constraint/root.ldif
new file mode 100644
index 0000000..6c29690
--- /dev/null
+++ b/tests/data/constraint/root.ldif
@@ -0,0 +1,25 @@
+dn: dc=example,dc=com
+objectclass: dcObject
+objectclass: organization
+dc: example
+o: My Domain corp.
+
+dn: ou=users,dc=example,dc=com
+ou: users
+objectclass: organizationalUnit
+
+dn: ou=groups,dc=example,dc=com
+ou: groups
+objectclass: organizationalUnit
+
+dn: uid=1,ou=groups,dc=example,dc=com
+objectclass: inetOrgPerson
+cn: test 1
+sn: test1
+uid: 1
+
+dn: uid=2,ou=groups,dc=example,dc=com
+objectclass: inetOrgPerson
+cn: test 2
+sn: test2
+uid: 2
diff --git a/tests/data/constraint/t_fail_01.ldif b/tests/data/constraint/t_fail_01.ldif
new file mode 100644
index 0000000..e0c82e2
--- /dev/null
+++ b/tests/data/constraint/t_fail_01.ldif
@@ -0,0 +1,6 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+add: mail
+mail: b@example.com
+mail: c@example.com
+mail: d@example.com
diff --git a/tests/data/constraint/t_fail_02.ldif b/tests/data/constraint/t_fail_02.ldif
new file mode 100644
index 0000000..462a174
--- /dev/null
+++ b/tests/data/constraint/t_fail_02.ldif
@@ -0,0 +1,8 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+add: mail
+mail: b@example.com
+mail: c@example.com
+mail: d@example.com
+mail: e@example.com
+mail: f@example.com
diff --git a/tests/data/constraint/t_fail_03.ldif b/tests/data/constraint/t_fail_03.ldif
new file mode 100644
index 0000000..471a332
--- /dev/null
+++ b/tests/data/constraint/t_fail_03.ldif
@@ -0,0 +1,8 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+add: mail
+mail: b@example.com
+mail: c@example.com
+-
+add: mail
+mail: d@example.com
diff --git a/tests/data/constraint/t_fail_04.ldif b/tests/data/constraint/t_fail_04.ldif
new file mode 100644
index 0000000..747357c
--- /dev/null
+++ b/tests/data/constraint/t_fail_04.ldif
@@ -0,0 +1,10 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+add: mail
+mail: b@example.com
+-
+add: mail
+mail: c@example.com
+-
+add: mail
+mail: d@example.com
diff --git a/tests/data/constraint/t_fail_05.ldif b/tests/data/constraint/t_fail_05.ldif
new file mode 100644
index 0000000..da48748
--- /dev/null
+++ b/tests/data/constraint/t_fail_05.ldif
@@ -0,0 +1,13 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+add: mail
+mail: b@example.com
+-
+add: mail
+mail: c@example.com
+-
+add: mail
+mail: d@example.com
+-
+add: mail
+mail: e@example.com
diff --git a/tests/data/constraint/t_fail_06.ldif b/tests/data/constraint/t_fail_06.ldif
new file mode 100644
index 0000000..950cf5b
--- /dev/null
+++ b/tests/data/constraint/t_fail_06.ldif
@@ -0,0 +1,7 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+replace: mail
+mail: a@example.com
+mail: b@example.com
+mail: c@example.com
+mail: d@example.com
diff --git a/tests/data/constraint/t_fail_07.ldif b/tests/data/constraint/t_fail_07.ldif
new file mode 100644
index 0000000..85c8c63
--- /dev/null
+++ b/tests/data/constraint/t_fail_07.ldif
@@ -0,0 +1,15 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+replace: mail
+mail: a@example.com
+mail: b@example.com
+mail: c@example.com
+mail: d@example.com
+-
+delete: mail
+-
+add: mail
+mail: w@example.com
+mail: x@example.com
+mail: y@example.com
+mail: z@example.com
diff --git a/tests/data/constraint/t_fail_08.ldif b/tests/data/constraint/t_fail_08.ldif
new file mode 100644
index 0000000..7dc94ac
--- /dev/null
+++ b/tests/data/constraint/t_fail_08.ldif
@@ -0,0 +1,12 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+add: mail
+mail: b@example.com
+mail: c@example.com
+mail: d@example.com
+-
+delete: mail
+mail: d@example.com
+-
+add: mail
+mail: f@example.com
diff --git a/tests/data/constraint/t_fail_09.ldif b/tests/data/constraint/t_fail_09.ldif
new file mode 100644
index 0000000..61c2799
--- /dev/null
+++ b/tests/data/constraint/t_fail_09.ldif
@@ -0,0 +1,10 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+add: mail
+mail: b@example.com
+mail: c@example.com
+mail: d@example.com
+mail: e@example.com
+-
+delete: mail
+mail: original@example.com
diff --git a/tests/data/constraint/t_fail_10.ldif b/tests/data/constraint/t_fail_10.ldif
new file mode 100644
index 0000000..d50f911
--- /dev/null
+++ b/tests/data/constraint/t_fail_10.ldif
@@ -0,0 +1,4 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+add: mail
+mail: example@not-allowed.com
diff --git a/tests/data/constraint/t_fail_11.ldif b/tests/data/constraint/t_fail_11.ldif
new file mode 100644
index 0000000..48bed05
--- /dev/null
+++ b/tests/data/constraint/t_fail_11.ldif
@@ -0,0 +1,7 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+delete: mail
+mail: original@example.com
+-
+add: mail
+mail: a@fail.com
diff --git a/tests/data/constraint/t_fail_12.ldif b/tests/data/constraint/t_fail_12.ldif
new file mode 100644
index 0000000..071d829
--- /dev/null
+++ b/tests/data/constraint/t_fail_12.ldif
@@ -0,0 +1,10 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+delete: mail
+mail: original@example.com
+-
+add: mail
+mail: notsooriginal@example.com
+-
+replace: cn
+cn: John Fail
diff --git a/tests/data/constraint/t_fail_13.ldif b/tests/data/constraint/t_fail_13.ldif
new file mode 100644
index 0000000..b8c2ab5
--- /dev/null
+++ b/tests/data/constraint/t_fail_13.ldif
@@ -0,0 +1,4 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+replace: givenname
+givenname: Joe
diff --git a/tests/data/constraint/t_fail_14.ldif b/tests/data/constraint/t_fail_14.ldif
new file mode 100644
index 0000000..090e48b
--- /dev/null
+++ b/tests/data/constraint/t_fail_14.ldif
@@ -0,0 +1,4 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+replace: sn
+sn: Down
diff --git a/tests/data/constraint/t_fail_15.ldif b/tests/data/constraint/t_fail_15.ldif
new file mode 100644
index 0000000..94d7dd7
--- /dev/null
+++ b/tests/data/constraint/t_fail_15.ldif
@@ -0,0 +1,5 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+replace: uid
+uid: 3
+
diff --git a/tests/data/constraint/t_fail_16.ldif b/tests/data/constraint/t_fail_16.ldif
new file mode 100644
index 0000000..055f284
--- /dev/null
+++ b/tests/data/constraint/t_fail_16.ldif
@@ -0,0 +1,4 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+replace: jpegPhoto
+jpegPhoto: AAAA
diff --git a/tests/data/constraint/t_ok_01.ldif b/tests/data/constraint/t_ok_01.ldif
new file mode 100644
index 0000000..5766461
--- /dev/null
+++ b/tests/data/constraint/t_ok_01.ldif
@@ -0,0 +1,3 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+delete: mail
diff --git a/tests/data/constraint/t_ok_02.ldif b/tests/data/constraint/t_ok_02.ldif
new file mode 100644
index 0000000..17ce4b2
--- /dev/null
+++ b/tests/data/constraint/t_ok_02.ldif
@@ -0,0 +1,5 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+replace: mail
+mail: a@example.com
+mail: b@example.com
diff --git a/tests/data/constraint/t_ok_03.ldif b/tests/data/constraint/t_ok_03.ldif
new file mode 100644
index 0000000..3d2a9d1
--- /dev/null
+++ b/tests/data/constraint/t_ok_03.ldif
@@ -0,0 +1,6 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+replace: mail
+mail: a@example.com
+mail: b@example.com
+mail: c@example.com
diff --git a/tests/data/constraint/t_ok_04.ldif b/tests/data/constraint/t_ok_04.ldif
new file mode 100644
index 0000000..5766461
--- /dev/null
+++ b/tests/data/constraint/t_ok_04.ldif
@@ -0,0 +1,3 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+delete: mail
diff --git a/tests/data/constraint/t_ok_05.ldif b/tests/data/constraint/t_ok_05.ldif
new file mode 100644
index 0000000..c30d339
--- /dev/null
+++ b/tests/data/constraint/t_ok_05.ldif
@@ -0,0 +1,5 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+add: mail
+mail: a@example.com
+mail: b@example.com
diff --git a/tests/data/constraint/t_ok_06.ldif b/tests/data/constraint/t_ok_06.ldif
new file mode 100644
index 0000000..eef34bd
--- /dev/null
+++ b/tests/data/constraint/t_ok_06.ldif
@@ -0,0 +1,7 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+delete: mail
+-
+add: mail
+mail: b@example.com
+mail: c@example.com
diff --git a/tests/data/constraint/t_ok_07.ldif b/tests/data/constraint/t_ok_07.ldif
new file mode 100644
index 0000000..f5c7333
--- /dev/null
+++ b/tests/data/constraint/t_ok_07.ldif
@@ -0,0 +1,8 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+delete: mail
+-
+add: mail
+mail: a@example.com
+mail: b@example.com
+mail: c@example.com
diff --git a/tests/data/constraint/t_ok_08.ldif b/tests/data/constraint/t_ok_08.ldif
new file mode 100644
index 0000000..40b7fa9
--- /dev/null
+++ b/tests/data/constraint/t_ok_08.ldif
@@ -0,0 +1,12 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+add: mail
+mail: b@example.com
+mail: c@example.com
+-
+delete: mail
+-
+add: mail
+mail: x@example.com
+mail: y@example.com
+mail: z@example.com
diff --git a/tests/data/constraint/t_ok_09.ldif b/tests/data/constraint/t_ok_09.ldif
new file mode 100644
index 0000000..73cec01
--- /dev/null
+++ b/tests/data/constraint/t_ok_09.ldif
@@ -0,0 +1,9 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+replace: mail
+mail: a@example.com
+mail: b@example.com
+mail: c@example.com
+mail: d@example.com
+-
+delete: mail
diff --git a/tests/data/constraint/t_ok_10.ldif b/tests/data/constraint/t_ok_10.ldif
new file mode 100644
index 0000000..367e9a8
--- /dev/null
+++ b/tests/data/constraint/t_ok_10.ldif
@@ -0,0 +1,24 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+add: mail
+mail: b@example.com
+mail: c@example.com
+-
+delete: mail
+-
+add: mail
+mail: x@example.com
+mail: y@example.com
+mail: z@example.com
+mail: u@example.com
+mail: m@example.com
+-
+replace: mail
+mail: i@example.com
+mail: j@example.com
+-
+add: mail
+mail: k@example.com
+-
+replace: description
+description: d1
diff --git a/tests/data/constraint/t_ok_11.ldif b/tests/data/constraint/t_ok_11.ldif
new file mode 100644
index 0000000..76cbb17
--- /dev/null
+++ b/tests/data/constraint/t_ok_11.ldif
@@ -0,0 +1,32 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+add: mail
+mail: b@example.com
+mail: c@example.com
+-
+delete: mail
+-
+add: mail
+mail: x@example.com
+mail: y@example.com
+mail: z@example.com
+mail: u@example.com
+mail: m@example.com
+-
+replace: mail
+mail: i@example.com
+mail: j@example.com
+-
+add: mail
+mail: k@example.com
+mail: hh@example.com
+-
+delete: mail
+-
+add: mail
+mail: k@example.com
+mail: hh@example.com
+mail: hj@example.com
+-
+replace: description
+description: d1
diff --git a/tests/data/constraint/t_ok_12.ldif b/tests/data/constraint/t_ok_12.ldif
new file mode 100644
index 0000000..ce89978
--- /dev/null
+++ b/tests/data/constraint/t_ok_12.ldif
@@ -0,0 +1,11 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+add: mail
+mail: b@example.com
+mail: c@example.com
+mail: d@example.com
+mail: e@example.com
+-
+delete: mail
+mail: original@example.com
+mail: e@example.com
diff --git a/tests/data/constraint/t_ok_13.ldif b/tests/data/constraint/t_ok_13.ldif
new file mode 100644
index 0000000..0e9257e
--- /dev/null
+++ b/tests/data/constraint/t_ok_13.ldif
@@ -0,0 +1,18 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+add: mail
+mail: b@example.com
+mail: c@example.com
+mail: d@example.com
+mail: e@example.com
+-
+delete: mail
+-
+add: mail
+mail: b@example.com
+mail: c@example.com
+mail: d@example.com
+mail: e@example.com
+-
+delete: mail
+mail: e@example.com
diff --git a/tests/data/constraint/t_ok_14.ldif b/tests/data/constraint/t_ok_14.ldif
new file mode 100644
index 0000000..96ef3a4
--- /dev/null
+++ b/tests/data/constraint/t_ok_14.ldif
@@ -0,0 +1,8 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+delete: description
+description: desc1
+-
+add: description
+description: desc1-mod
+
diff --git a/tests/data/constraint/t_ok_15.ldif b/tests/data/constraint/t_ok_15.ldif
new file mode 100644
index 0000000..9352caa
--- /dev/null
+++ b/tests/data/constraint/t_ok_15.ldif
@@ -0,0 +1,5 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+changetype: modify
+replace: uid
+uid: 2
+
diff --git a/tests/data/constraint/user.ldif b/tests/data/constraint/user.ldif
new file mode 100644
index 0000000..6150462
--- /dev/null
+++ b/tests/data/constraint/user.ldif
@@ -0,0 +1,10 @@
+dn: cn=John Doe,ou=users,dc=example,dc=com
+objectclass: inetOrgPerson
+objectclass: organizationalPerson
+cn: John Doe
+givenname: John
+sn: Doe
+mail: original@example.com
+description: desc1
+description: desc2
+uid: 1
diff --git a/tests/data/dds.out b/tests/data/dds.out
new file mode 100644
index 0000000..1f580b4
--- /dev/null
+++ b/tests/data/dds.out
@@ -0,0 +1,70 @@
+# [1] Searching the dynamic portion of the database...
+dn: cn=Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: dynamicObject
+cn: Dynamic Object
+sn: Object
+entryTtl: 120
+userPassword:: ZHluYW1pYw==
+
+dn: cn=Subordinate Dynamic Object,cn=Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: dynamicObject
+cn: Subordinate Dynamic Object
+sn: Object
+userPassword:: ZHluYW1pYw==
+entryTtl: 3600
+
+# [2] Searching the dynamic portion of the database...
+dn: cn=Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: dynamicObject
+cn: Dynamic Object
+sn: Object
+entryTtl: 120
+userPassword:: ZHluYW1pYw==
+
+dn: cn=Renamed Dynamic Object,cn=Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: dynamicObject
+sn: Object
+userPassword:: ZHluYW1pYw==
+entryTtl: 3600
+cn: Renamed Dynamic Object
+
+# [3] Searching the dynamic portion of the database...
+dn: cn=Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: dynamicObject
+cn: Dynamic Object
+sn: Object
+userPassword:: ZHluYW1pYw==
+entryTtl: 120
+
+dn: cn=Renamed Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: dynamicObject
+sn: Object
+userPassword:: ZHluYW1pYw==
+entryTtl: 3600
+cn: Renamed Dynamic Object
+
+# [4] Searching the dynamic portion of the database...
+dn: cn=Renamed Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: dynamicObject
+sn: Object
+userPassword:: ZHluYW1pYw==
+entryTtl: 3600
+cn: Renamed Dynamic Object
+
+# [5] Searching the dynamic portion of the database...
+dn: cn=Renamed Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: dynamicObject
+sn: Object
+userPassword:: ZHluYW1pYw==
+cn: Renamed Dynamic Object
+entryTtl: 10
+
+# [6] Searching the dynamic portion of the database...
diff --git a/tests/data/deref.out b/tests/data/deref.out
new file mode 100644
index 0000000..e4bc90a
--- /dev/null
+++ b/tests/data/deref.out
@@ -0,0 +1,39 @@
+dn: o=deref
+objectClass: top
+objectClass: organization
+o: deref
+description: deref test database
+
+dn: ou=users,o=deref
+objectClass: top
+objectClass: organizationalUnit
+ou: users
+description: container for test deref users
+
+dn: ou=groups,o=deref
+objectClass: top
+objectClass: organizationalUnit
+ou: users
+ou: groups
+description: container for test deref groups
+
+dn: cn=Howard Chu,ou=users,o=deref
+objectClass: inetOrgPerson
+cn: Howard Chu
+sn: Chu
+uid: hyc
+
+dn: cn=Pierangelo Masarati,ou=users,o=deref
+objectClass: inetOrgPerson
+cn: Pierangelo Masarati
+sn: Masarati
+uid: ando
+
+dn: cn=Test Group,ou=groups,o=deref
+# member: <uid=hyc>;cn=Howard Chu,ou=users,o=deref
+# member: <uid=ando>;cn=Pierangelo Masarati,ou=users,o=deref
+objectClass: groupOfNames
+cn: Test Group
+member: cn=Howard Chu,ou=users,o=deref
+member: cn=Pierangelo Masarati,ou=users,o=deref
+
diff --git a/tests/data/ditcontentrules.conf b/tests/data/ditcontentrules.conf
new file mode 100644
index 0000000..22d80cd
--- /dev/null
+++ b/tests/data/ditcontentrules.conf
@@ -0,0 +1,18 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+ditcontentrule ( 2.5.6.4 NAME 'organization' AUX ( domainRelatedObject $ dcObject ) )
+ditcontentrule ( 2.5.6.5 NAME 'organizationalUnit' AUX extensibleObject )
+ditcontentrule ( 2.5.6.9 NAME 'groupOfNames' )
+ditcontentrule ( 2.5.6.17 NAME 'groupOfUniqueNames' )
diff --git a/tests/data/dn.out b/tests/data/dn.out
new file mode 100644
index 0000000..24019e5
--- /dev/null
+++ b/tests/data/dn.out
@@ -0,0 +1,233 @@
+# Searching database...
+dn: dc=example,dc=com
+objectClass: domain
+objectClass: domainRelatedObject
+dc: example
+associatedDomain: example.com
+
+dn: ou=LDAPv2,dc=example,dc=com
+objectClass: organizationalUnit
+ou: LDAPv2
+description: RFC 1779 compliant DN string representation
+
+dn: ou=LDAPv3,dc=example,dc=com
+objectClass: organizationalUnit
+ou: LDAPv3
+description: RFC 2253 compliant DN string representation
+
+dn: cn=May Succeed 1,ou=LDAPv2,dc=example,dc=com
+objectClass: groupOfNames
+cn: May Succeed 1
+member:
+description: " " // space, quote characters (") are not part of the string
+
+dn: cn=May Succeed 3,ou=LDAPv2,dc=example,dc=com
+objectClass: groupOfNames
+cn: May Succeed 3
+member: uid=jsmith,o=example,c=US
+description: UID=jsmith, O=example, C=US // spaces
+
+dn: cn=May Succeed 4,ou=LDAPv2,dc=example,dc=com
+objectClass: groupOfNames
+cn: May Succeed 4
+member: uid=jsmith,o=example,c=US
+description: UID=jsmith;O=example;C=US // semi-colons
+
+dn: cn=May Succeed 6,ou=LDAPv2,dc=example,dc=com
+objectClass: groupOfNames
+cn: May Succeed 6
+member: cn=John Smith,o=example,c=US
+description: CN="John Smith",O=example,C=US // quotes
+
+dn: cn=Must Succeed,ou=LDAPv3,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Succeed
+member: cn=Must Succeed,ou=LDAPv3,dc=example,dc=com
+member:
+member: uid=jsmith,dc=example,dc=net
+member: cn=J. Smith+ou=Sales,dc=example,dc=net
+member: cn=John Smith\2C III,dc=example,dc=net
+member: ou=Sales\3B Data\2BAlgorithms,dc=example,dc=net
+member:: Y249QmVmb3JlDUFmdGVyLGRjPWV4YW1wbGUsZGM9bmV0
+member: cn=\23John Smith\20,dc=example,dc=net
+member:: Y249THXEjWnEhw==
+member: testUUID=597ae2f6-16a6-1027-98f4-abcdefabcdef,dc=Example
+seeAlso: cn=John Smith\2C III,dc=example,dc=net
+seeAlso: ou=Sales\3B Data\2BAlgorithms,dc=example,dc=net
+seeAlso: cn=\23John Smith\20,dc=example,dc=net
+description: "member" values contain specific DN forms;
+description: "seeAlso" values contain DN forms already defined as "member",
+description: but in a different string representation;
+description: the following "description" values contain the "member" and
+description: "seeAlso" DN string representations used above.
+description: ""
+description: UID=jsmith,DC=example,DC=net
+description: OU=Sales+CN=J. Smith,DC=example,DC=net
+description: CN=John Smith\, III,DC=example,DC=net
+description: CN=John Smith\2C III,DC=example,DC=net
+description: OU=Sales\; Data\+Algorithms,DC=example,DC=net
+description: OU=Sales\3B Data\2BAlgorithms,DC=example,DC=net
+description: CN=Before\0dAfter,DC=example,DC=net
+description: CN=\23John Smith\20,DC=example,DC=net
+description: CN=\#John Smith\ ,DC=example,DC=net
+description: CN=Lu\C4\8Di\C4\87
+description: testUUID=597ae2f6-16a6-1027-98f4-abcdefABCDEF,DC=Example
+
+dn: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
+objectClass: groupOfUniqueNames
+cn: Name and Optional UID
+uniqueMember: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
+uniqueMember: #'1'B
+uniqueMember: #'0010'B
+uniqueMember: dc=example,dc=com#'1000'B
+uniqueMember: dc=example,dc=com#''B
+description: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com //
+ only DN portion
+description: #'1'B // empty "" DN
+description: #'0010'B // empty "" DN with leading '0's
+description: dc=example,dc=com#'1000'B // with DN portion
+description: dc=example,dc=com#''B // with DN portion + bitstring with no bits
+
+dn: ou=Related Syntaxes,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Related Syntaxes
+
+dn: cn=Should Parse as DN,cn=Name and Optional UID,ou=Related Syntaxes,dc=exam
+ ple,dc=com
+objectClass: groupOfUniqueNames
+cn: Should Parse as DN
+uniqueMember: dc=example,dc=com#0'B
+uniqueMember: dc=example,dc=com#'0B
+uniqueMember: dc=example,dc=com '0'B
+description: dc=example,dc=com#0'B // malformed UID?
+description: dc=example,dc=com#'0B // malformed UID?
+description: dc=example,dc=com '0'B // malformed UID?
+
+dn: cn=Unescaped Equals,ou=LDAPv3,dc=example,dc=com
+objectClass: groupOfNames
+cn: Unescaped Equals
+member: cn=Unescaped Equals,ou=LDAPv3,dc=example,dc=com
+member: cn=A*x\3Db is a linear algebra problem,ou=LDAPv3,dc=example,dc=com
+description: cn=A*x=b is a linear algebra problem,ou=LDAPv3,dc=example,dc=com
+ // unescaped EQUALS
+
+# Searching database for DN="OU=Sales+CN=J. Smith,DC=example,DC=net"...
+dn: cn=Must Succeed,ou=LDAPv3,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Succeed
+member: cn=Must Succeed,ou=LDAPv3,dc=example,dc=com
+member:
+member: uid=jsmith,dc=example,dc=net
+member: cn=J. Smith+ou=Sales,dc=example,dc=net
+member: cn=John Smith\2C III,dc=example,dc=net
+member: ou=Sales\3B Data\2BAlgorithms,dc=example,dc=net
+member:: Y249QmVmb3JlDUFmdGVyLGRjPWV4YW1wbGUsZGM9bmV0
+member: cn=\23John Smith\20,dc=example,dc=net
+member:: Y249THXEjWnEhw==
+member: testUUID=597ae2f6-16a6-1027-98f4-abcdefabcdef,dc=Example
+seeAlso: cn=John Smith\2C III,dc=example,dc=net
+seeAlso: ou=Sales\3B Data\2BAlgorithms,dc=example,dc=net
+seeAlso: cn=\23John Smith\20,dc=example,dc=net
+description: "member" values contain specific DN forms;
+description: "seeAlso" values contain DN forms already defined as "member",
+description: but in a different string representation;
+description: the following "description" values contain the "member" and
+description: "seeAlso" DN string representations used above.
+description: ""
+description: UID=jsmith,DC=example,DC=net
+description: OU=Sales+CN=J. Smith,DC=example,DC=net
+description: CN=John Smith\, III,DC=example,DC=net
+description: CN=John Smith\2C III,DC=example,DC=net
+description: OU=Sales\; Data\+Algorithms,DC=example,DC=net
+description: OU=Sales\3B Data\2BAlgorithms,DC=example,DC=net
+description: CN=Before\0dAfter,DC=example,DC=net
+description: CN=\23John Smith\20,DC=example,DC=net
+description: CN=\#John Smith\ ,DC=example,DC=net
+description: CN=Lu\C4\8Di\C4\87
+description: testUUID=597ae2f6-16a6-1027-98f4-abcdefABCDEF,DC=Example
+
+# Searching database for entryUUID-named DN="testUUID=597ae2f6-16a6-1027-98f4-ABCDEFabcdef,DC=Example"...
+dn: cn=Must Succeed,ou=LDAPv3,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Succeed
+member: cn=Must Succeed,ou=LDAPv3,dc=example,dc=com
+member:
+member: uid=jsmith,dc=example,dc=net
+member: cn=J. Smith+ou=Sales,dc=example,dc=net
+member: cn=John Smith\2C III,dc=example,dc=net
+member: ou=Sales\3B Data\2BAlgorithms,dc=example,dc=net
+member:: Y249QmVmb3JlDUFmdGVyLGRjPWV4YW1wbGUsZGM9bmV0
+member: cn=\23John Smith\20,dc=example,dc=net
+member:: Y249THXEjWnEhw==
+member: testUUID=597ae2f6-16a6-1027-98f4-abcdefabcdef,dc=Example
+seeAlso: cn=John Smith\2C III,dc=example,dc=net
+seeAlso: ou=Sales\3B Data\2BAlgorithms,dc=example,dc=net
+seeAlso: cn=\23John Smith\20,dc=example,dc=net
+description: "member" values contain specific DN forms;
+description: "seeAlso" values contain DN forms already defined as "member",
+description: but in a different string representation;
+description: the following "description" values contain the "member" and
+description: "seeAlso" DN string representations used above.
+description: ""
+description: UID=jsmith,DC=example,DC=net
+description: OU=Sales+CN=J. Smith,DC=example,DC=net
+description: CN=John Smith\, III,DC=example,DC=net
+description: CN=John Smith\2C III,DC=example,DC=net
+description: OU=Sales\; Data\+Algorithms,DC=example,DC=net
+description: OU=Sales\3B Data\2BAlgorithms,DC=example,DC=net
+description: CN=Before\0dAfter,DC=example,DC=net
+description: CN=\23John Smith\20,DC=example,DC=net
+description: CN=\#John Smith\ ,DC=example,DC=net
+description: CN=Lu\C4\8Di\C4\87
+description: testUUID=597ae2f6-16a6-1027-98f4-abcdefABCDEF,DC=Example
+
+# Searching database for nameAndOptionalUID="dc=example,dc=com"...
+# Searching database for nameAndOptionalUID="dc=example,dc=com#'001000'B"...
+# Searching database for nameAndOptionalUID="dc=example,dc=com#'1000'B"...
+dn: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
+objectClass: groupOfUniqueNames
+cn: Name and Optional UID
+uniqueMember: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
+uniqueMember: #'1'B
+uniqueMember: #'0010'B
+uniqueMember: dc=example,dc=com#'1000'B
+uniqueMember: dc=example,dc=com#''B
+description: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com //
+ only DN portion
+description: #'1'B // empty "" DN
+description: #'0010'B // empty "" DN with leading '0's
+description: dc=example,dc=com#'1000'B // with DN portion
+description: dc=example,dc=com#''B // with DN portion + bitstring with no bits
+
+# Searching database for uniqueMember~="dc=example,dc=com" (approx)...
+dn: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
+objectClass: groupOfUniqueNames
+cn: Name and Optional UID
+uniqueMember: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
+uniqueMember: #'1'B
+uniqueMember: #'0010'B
+uniqueMember: dc=example,dc=com#'1000'B
+uniqueMember: dc=example,dc=com#''B
+description: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com //
+ only DN portion
+description: #'1'B // empty "" DN
+description: #'0010'B // empty "" DN with leading '0's
+description: dc=example,dc=com#'1000'B // with DN portion
+description: dc=example,dc=com#''B // with DN portion + bitstring with no bits
+
+# Searching database for uniqueMember~="dc=example,dc=com#'1000'B" (approx)...
+dn: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
+objectClass: groupOfUniqueNames
+cn: Name and Optional UID
+uniqueMember: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
+uniqueMember: #'1'B
+uniqueMember: #'0010'B
+uniqueMember: dc=example,dc=com#'1000'B
+uniqueMember: dc=example,dc=com#''B
+description: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com //
+ only DN portion
+description: #'1'B // empty "" DN
+description: #'0010'B // empty "" DN with leading '0's
+description: dc=example,dc=com#'1000'B // with DN portion
+description: dc=example,dc=com#''B // with DN portion + bitstring with no bits
+
diff --git a/tests/data/do_add.1 b/tests/data/do_add.1
new file mode 100644
index 0000000..54afb38
--- /dev/null
+++ b/tests/data/do_add.1
@@ -0,0 +1,18 @@
+dn: cn=James A Jones 2,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff, ou=Groups, dc=example,dc=com
+userpassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
diff --git a/tests/data/do_add.2 b/tests/data/do_add.2
new file mode 100644
index 0000000..9884c89
--- /dev/null
+++ b/tests/data/do_add.2
@@ -0,0 +1,18 @@
+dn: cn=James A Jones 3,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 3
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff, ou=Groups, dc=example,dc=com
+userpassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
diff --git a/tests/data/do_add.3 b/tests/data/do_add.3
new file mode 100644
index 0000000..2a1c59a
--- /dev/null
+++ b/tests/data/do_add.3
@@ -0,0 +1,18 @@
+dn: cn=James A Jones 4,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 4
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff, ou=Groups, dc=example,dc=com
+userpassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
diff --git a/tests/data/do_add.4 b/tests/data/do_add.4
new file mode 100644
index 0000000..c1de147
--- /dev/null
+++ b/tests/data/do_add.4
@@ -0,0 +1,18 @@
+dn: cn=James A Jones 5,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 5
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff, ou=Groups, dc=example,dc=com
+userpassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homephone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
diff --git a/tests/data/do_bind.0 b/tests/data/do_bind.0
new file mode 100644
index 0000000..ecbb872
--- /dev/null
+++ b/tests/data/do_bind.0
@@ -0,0 +1,6 @@
+cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+bjensen
+cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+bjorn
+ou=People,dc=example,dc=com
++userPassword:(userPassword=*)
diff --git a/tests/data/do_modify.0 b/tests/data/do_modify.0
new file mode 100644
index 0000000..fb919f5
--- /dev/null
+++ b/tests/data/do_modify.0
@@ -0,0 +1,8 @@
+cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+mail: bj@mailgw.example.com
+cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+cn: Björn
+cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+displayname: James Jones
+cn=ITD Staff,ou=Groups,dc=example,dc=com
+uniquemember: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
diff --git a/tests/data/do_modrdn.0 b/tests/data/do_modrdn.0
new file mode 100644
index 0000000..96620ed
--- /dev/null
+++ b/tests/data/do_modrdn.0
@@ -0,0 +1,4 @@
+cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example,dc=com
diff --git a/tests/data/do_read.0 b/tests/data/do_read.0
new file mode 100644
index 0000000..e6f0e2f
--- /dev/null
+++ b/tests/data/do_read.0
@@ -0,0 +1,5 @@
+cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+cn=ITD Staff,ou=Groups,dc=example,dc=com
+ou=Groups, dc=example,dc=com
+ou=Alumni Association, ou=People, dc=example,dc=com
+cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example,dc=com
diff --git a/tests/data/do_search.0 b/tests/data/do_search.0
new file mode 100644
index 0000000..a5235c5
--- /dev/null
+++ b/tests/data/do_search.0
@@ -0,0 +1,12 @@
+dc=example,dc=com
+(cn=Barbara Jensen)
+ou=people,dc=example,dc=com
+(cn=Bjorn Jensen)
+ou=people,dc=example,dc=com
+(cn=James A Jones 1)
+dc=example,dc=com
+(cn=Bjorn Jensen)
+dc=example,dc=com
+(cn=Alumni Assoc Staff)
+dc=example,dc=com
+(cn=James*)
diff --git a/tests/data/dynlist.out b/tests/data/dynlist.out
new file mode 100644
index 0000000..926e830
--- /dev/null
+++ b/tests/data/dynlist.out
@@ -0,0 +1,864 @@
+# Testing list search of all attrs...
+dn: cn=Dynamic List,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+cn: Dynamic List
+cn: Barbara Jensen
+cn: Babs Jensen
+cn: Bjorn Jensen
+cn: Biiff Jensen
+cn: Dorothy Stevens
+cn: Dot Stevens
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+cn: James A Jones 2
+cn: Jane Doe
+cn: Jane Alverson
+cn: Jennifer Smith
+cn: Jen Smith
+cn: John Doe
+cn: Jonathon Doe
+cn: Mark Elliot
+cn: Mark A Elliot
+cn: Ursula Hampster
+memberURL: ldap:///ou=People,dc=example,dc=com?cn,mail?sub?(objectClass=person
+ )
+mail: bjensen@mailgw.example.com
+mail: bjorn@mailgw.example.com
+mail: dots@mail.alumni.example.com
+mail: jaj@mail.alumni.example.com
+mail: jjones@mailgw.example.com
+mail: jdoe@woof.net
+mail: jen@mail.alumni.example.com
+mail: johnd@mailgw.example.com
+mail: melliot@mail.alumni.example.com
+mail: uham@mail.alumni.example.com
+
+# Testing list search of a listed attr...
+dn: cn=Dynamic List,ou=Dynamic Lists,dc=example,dc=com
+mail: bjensen@mailgw.example.com
+mail: bjorn@mailgw.example.com
+mail: dots@mail.alumni.example.com
+mail: jaj@mail.alumni.example.com
+mail: jjones@mailgw.example.com
+mail: jdoe@woof.net
+mail: jen@mail.alumni.example.com
+mail: johnd@mailgw.example.com
+mail: melliot@mail.alumni.example.com
+mail: uham@mail.alumni.example.com
+
+# Testing list search of a non-listed attr...
+dn: cn=Dynamic List,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+
+# Testing list search with (critical) manageDSAit...
+dn: cn=Dynamic List,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+cn: Dynamic List
+memberURL: ldap:///ou=People,dc=example,dc=com?cn,mail?sub?(objectClass=person
+ )
+
+# Testing filtered search with all attrs...
+dn: cn=Dynamic List,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+cn: Dynamic List
+cn: Barbara Jensen
+cn: Babs Jensen
+cn: Bjorn Jensen
+cn: Biiff Jensen
+cn: Dorothy Stevens
+cn: Dot Stevens
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+cn: James A Jones 2
+cn: Jane Doe
+cn: Jane Alverson
+cn: Jennifer Smith
+cn: Jen Smith
+cn: John Doe
+cn: Jonathon Doe
+cn: Mark Elliot
+cn: Mark A Elliot
+cn: Ursula Hampster
+memberURL: ldap:///ou=People,dc=example,dc=com?cn,mail?sub?(objectClass=person
+ )
+mail: bjensen@mailgw.example.com
+mail: bjorn@mailgw.example.com
+mail: dots@mail.alumni.example.com
+mail: jaj@mail.alumni.example.com
+mail: jjones@mailgw.example.com
+mail: jdoe@woof.net
+mail: jen@mail.alumni.example.com
+mail: johnd@mailgw.example.com
+mail: melliot@mail.alumni.example.com
+mail: uham@mail.alumni.example.com
+
+# Testing filtered search of a listed attr...
+dn: cn=Dynamic List,ou=Dynamic Lists,dc=example,dc=com
+mail: bjensen@mailgw.example.com
+mail: bjorn@mailgw.example.com
+mail: dots@mail.alumni.example.com
+mail: jaj@mail.alumni.example.com
+mail: jjones@mailgw.example.com
+mail: jdoe@woof.net
+mail: jen@mail.alumni.example.com
+mail: johnd@mailgw.example.com
+mail: melliot@mail.alumni.example.com
+mail: uham@mail.alumni.example.com
+
+# Testing filtered search of a non-listed attr...
+dn: cn=Dynamic List,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+
+# Testing filtered search of a non-present attr...
+# Testing list compare...
+TRUE
+
+# Testing list compare (should return FALSE)...
+FALSE
+
+# Testing list compare (should return UNDEFINED)...
+Compare Result: No such attribute (16)
+UNDEFINED
+
+# Testing list compare with manageDSAit...
+FALSE
+
+# Testing list search of all (mapped) attrs...
+dn: cn=Dynamic List,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+cn: Dynamic List
+memberURL: ldap:///ou=People,dc=example,dc=com?cn,mail?sub?(objectClass=person
+ )
+sn: Barbara Jensen
+sn: Babs Jensen
+sn: Bjorn Jensen
+sn: Biiff Jensen
+sn: Dorothy Stevens
+sn: Dot Stevens
+sn: James A Jones 1
+sn: James Jones
+sn: Jim Jones
+sn: James A Jones 2
+sn: Jane Doe
+sn: Jane Alverson
+sn: Jennifer Smith
+sn: Jen Smith
+sn: John Doe
+sn: Jonathon Doe
+sn: Mark Elliot
+sn: Mark A Elliot
+sn: Ursula Hampster
+mail: bjensen@mailgw.example.com
+mail: bjorn@mailgw.example.com
+mail: dots@mail.alumni.example.com
+mail: jaj@mail.alumni.example.com
+mail: jjones@mailgw.example.com
+mail: jdoe@woof.net
+mail: jen@mail.alumni.example.com
+mail: johnd@mailgw.example.com
+mail: melliot@mail.alumni.example.com
+mail: uham@mail.alumni.example.com
+
+# Testing list search of a (mapped) listed attr...
+dn: cn=Dynamic List,ou=Dynamic Lists,dc=example,dc=com
+sn: Barbara Jensen
+sn: Babs Jensen
+sn: Bjorn Jensen
+sn: Biiff Jensen
+sn: Dorothy Stevens
+sn: Dot Stevens
+sn: James A Jones 1
+sn: James Jones
+sn: Jim Jones
+sn: James A Jones 2
+sn: Jane Doe
+sn: Jane Alverson
+sn: Jennifer Smith
+sn: Jen Smith
+sn: John Doe
+sn: Jonathon Doe
+sn: Mark Elliot
+sn: Mark A Elliot
+sn: Ursula Hampster
+
+# Testing list search of a (n unmapped) listed attr...
+dn: cn=Dynamic List,ou=Dynamic Lists,dc=example,dc=com
+mail: bjensen@mailgw.example.com
+mail: bjorn@mailgw.example.com
+mail: dots@mail.alumni.example.com
+mail: jaj@mail.alumni.example.com
+mail: jjones@mailgw.example.com
+mail: jdoe@woof.net
+mail: jen@mail.alumni.example.com
+mail: johnd@mailgw.example.com
+mail: melliot@mail.alumni.example.com
+mail: uham@mail.alumni.example.com
+
+# Testing list compare (mapped attrs) ...
+TRUE
+
+# Testing list compare (mapped attrs; should return FALSE)...
+FALSE
+
+# Testing list search of all attrs...
+dn: cn=Dynamic List of Members,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+cn: Dynamic List of Members
+memberURL: ldap:///ou=People,dc=example,dc=com??sub?(objectClass=person)
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+
+# Testing list search of a listed attr...
+dn: cn=Dynamic List of Members,ou=Dynamic Lists,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+
+# Testing list search of a non-listed attr...
+dn: cn=Dynamic List of Members,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+
+# Testing list search with (critical) manageDSAit...
+dn: cn=Dynamic List of Members,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+cn: Dynamic List of Members
+memberURL: ldap:///ou=People,dc=example,dc=com??sub?(objectClass=person)
+
+# Testing list compare...
+TRUE
+
+# Testing list compare... (should return FALSE)
+FALSE
+
+# Testing list compare (should return FALSE)...
+FALSE
+
+# Testing list compare with manageDSAit (should return UNDEFINED)...
+Compare Result: No such attribute (16)
+UNDEFINED
+
+# Testing list search without dgIdentity...
+dn: cn=Dynamic List of Members,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+cn: Dynamic List of Members
+memberURL: ldap:///ou=People,dc=example,dc=com??sub?(objectClass=person)
+
+# Testing list search with dgIdentity...
+dn: cn=Dynamic List of Members,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+objectClass: dgIdentityAux
+cn: Dynamic List of Members
+memberURL: ldap:///ou=People,dc=example,dc=com??sub?(objectClass=person)
+dgIdentity: cn=Bjorn Jensen,ou=Information Technology DivisioN,ou=People,dc=ex
+ ample,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+
+# Testing list search with dgIdentity and dgAuthz anonymously...
+dn: cn=Dynamic List of Members,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+objectClass: dgIdentityAux
+cn: Dynamic List of Members
+memberURL: ldap:///ou=People,dc=example,dc=com??sub?(objectClass=person)
+dgIdentity: cn=Bjorn Jensen,ou=Information Technology DivisioN,ou=People,dc=ex
+ ample,dc=com
+dgAuthz: {0}dn:cn=Barbara Jensen,ou=Information Technology DivisioN,ou=People,
+ dc=example,dc=com
+
+# Testing list search with dgIdentity and dgAuthz as the authorized identity...
+dn: cn=Dynamic List of Members,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+objectClass: dgIdentityAux
+cn: Dynamic List of Members
+memberURL: ldap:///ou=People,dc=example,dc=com??sub?(objectClass=person)
+dgIdentity: cn=Bjorn Jensen,ou=Information Technology DivisioN,ou=People,dc=ex
+ ample,dc=com
+dgAuthz: {0}dn:cn=Barbara Jensen,ou=Information Technology DivisioN,ou=People,
+ dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+
+# Testing memberOf functionality...
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+memberOf: cn=dynamic list of members,ou=dynamic lists,dc=example,dc=com
+
+# Testing filtered memberOf functionality...
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+memberOf: cn=dynamic list of members,ou=dynamic lists,dc=example,dc=com
+
+# Testing static group memberOf functionality...
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+memberOf: cn=all staff,ou=groups,dc=example,dc=com
+memberOf: cn=alumni assoc staff,ou=groups,dc=example,dc=com
+memberOf: cn=dynamic list of members,ou=dynamic lists,dc=example,dc=com
+
+# Testing static group member compare...
+TRUE
+
+# Testing static group non-member compare (should return FALSE)...
+FALSE
+
+# Testing nested dynamic group functionality...
+dn: cn=Dynamic List,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+cn: Dynamic List
+memberURL: ldap:///ou=People,dc=example,dc=com?cn,mail?sub?(objectClass=person
+ )
+
+dn: cn=Dynamic List of Members,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+objectClass: dgIdentityAux
+cn: Dynamic List of Members
+memberURL: ldap:///ou=People,dc=example,dc=com??sub?(objectClass=person)
+dgIdentity: cn=Bjorn Jensen,ou=Information Technology DivisioN,ou=People,dc=ex
+ ample,dc=com
+dgAuthz: {0}dn:cn=Barbara Jensen,ou=Information Technology DivisioN,ou=People,
+ dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+
+dn: ou=Dynamic Lists,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Dynamic Lists
+
+dn: cn=Meta Group,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+cn: Meta Group
+memberURL: ldap:///ou=Dynamic Lists,dc=example,dc=com??sub?(description=Smith%
+ 20family)
+member: cn=The Smiths,ou=Dynamic Lists,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+
+dn: cn=The Smiths,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+cn: The Smiths
+memberURL: ldap:///ou=People,dc=example,dc=com??sub?(sn=Smith)
+description: Smith family
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+memberOf: cn=meta group,ou=dynamic lists,dc=example,dc=com
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+memberOf: cn=dynamic list of members,ou=dynamic lists,dc=example,dc=com
+
+# Testing nested static group functionality...
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+memberOf: cn=all staff,ou=groups,dc=example,dc=com
+memberOf: cn=the jensens,ou=groups,dc=example,dc=com
+memberOf: cn=jjs,ou=groups,dc=example,dc=com
+memberOf: cn=dynamic list of members,ou=dynamic lists,dc=example,dc=com
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+seeAlso: cn=itd staff,ou=groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+memberOf: cn=all staff,ou=groups,dc=example,dc=com
+memberOf: cn=the jensens,ou=groups,dc=example,dc=com
+memberOf: cn=jjs,ou=groups,dc=example,dc=com
+memberOf: cn=dynamic list of members,ou=dynamic lists,dc=example,dc=com
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+memberOf: cn=all staff,ou=groups,dc=example,dc=com
+memberOf: cn=bonus group,ou=groups,dc=example,dc=com
+memberOf: cn=alumni assoc staff,ou=groups,dc=example,dc=com
+memberOf: cn=dynamic list of members,ou=dynamic lists,dc=example,dc=com
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+seeAlso: cn=itd staff,ou=groups,dc=example,dc=com
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+memberOf: cn=jjs,ou=groups,dc=example,dc=com
+memberOf: cn=all staff,ou=groups,dc=example,dc=com
+memberOf: cn=dynamic list of members,ou=dynamic lists,dc=example,dc=com
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+memberOf: cn=all staff,ou=groups,dc=example,dc=com
+memberOf: cn=alumni assoc staff,ou=groups,dc=example,dc=com
+memberOf: cn=dynamic list of members,ou=dynamic lists,dc=example,dc=com
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+seeAlso: cn=itd staff,ou=groups,dc=example,dc=com
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+memberOf: cn=all staff,ou=groups,dc=example,dc=com
+memberOf: cn=dynamic list of members,ou=dynamic lists,dc=example,dc=com
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+memberOf: cn=all staff,ou=groups,dc=example,dc=com
+memberOf: cn=the smiths,ou=dynamic lists,dc=example,dc=com
+memberOf: cn=meta group,ou=dynamic lists,dc=example,dc=com
+memberOf: cn=bonus group,ou=groups,dc=example,dc=com
+memberOf: cn=alumni assoc staff,ou=groups,dc=example,dc=com
+memberOf: cn=dynamic list of members,ou=dynamic lists,dc=example,dc=com
+
+# Testing filtered nested memberOf functionality...
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+memberOf: cn=all staff,ou=groups,dc=example,dc=com
+memberOf: cn=the smiths,ou=dynamic lists,dc=example,dc=com
+memberOf: cn=meta group,ou=dynamic lists,dc=example,dc=com
+memberOf: cn=bonus group,ou=groups,dc=example,dc=com
+memberOf: cn=alumni assoc staff,ou=groups,dc=example,dc=com
+memberOf: cn=dynamic list of members,ou=dynamic lists,dc=example,dc=com
+
+dn: cn=Meta Group,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+cn: Meta Group
+memberURL: ldap:///ou=Dynamic Lists,dc=example,dc=com??sub?(description=Smith%
+ 20family)
+member: cn=The Smiths,ou=Dynamic Lists,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+memberOf: cn=bonus group,ou=groups,dc=example,dc=com
+
+dn: cn=The Smiths,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+cn: The Smiths
+memberURL: ldap:///ou=People,dc=example,dc=com??sub?(sn=Smith)
+description: Smith family
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+memberOf: cn=meta group,ou=dynamic lists,dc=example,dc=com
+memberOf: cn=bonus group,ou=groups,dc=example,dc=com
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+memberOf: cn=all staff,ou=groups,dc=example,dc=com
+memberOf: cn=bonus group,ou=groups,dc=example,dc=com
+memberOf: cn=alumni assoc staff,ou=groups,dc=example,dc=com
+memberOf: cn=dynamic list of members,ou=dynamic lists,dc=example,dc=com
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+uid: jjones
+
+# Testing negated filtered memberOf functionality...
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+memberOf: cn=all staff,ou=groups,dc=example,dc=com
+memberOf: cn=the jensens,ou=groups,dc=example,dc=com
+memberOf: cn=jjs,ou=groups,dc=example,dc=com
+memberOf: cn=dynamic list of members,ou=dynamic lists,dc=example,dc=com
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+seeAlso: cn=itd staff,ou=groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+memberOf: cn=all staff,ou=groups,dc=example,dc=com
+memberOf: cn=the jensens,ou=groups,dc=example,dc=com
+memberOf: cn=jjs,ou=groups,dc=example,dc=com
+memberOf: cn=dynamic list of members,ou=dynamic lists,dc=example,dc=com
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+seeAlso: cn=itd staff,ou=groups,dc=example,dc=com
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+memberOf: cn=jjs,ou=groups,dc=example,dc=com
+memberOf: cn=all staff,ou=groups,dc=example,dc=com
+memberOf: cn=dynamic list of members,ou=dynamic lists,dc=example,dc=com
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+seeAlso: cn=itd staff,ou=groups,dc=example,dc=com
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+memberOf: cn=all staff,ou=groups,dc=example,dc=com
+memberOf: cn=dynamic list of members,ou=dynamic lists,dc=example,dc=com
+
+# Testing filtered nested member functionality...
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupofnames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupofnames
+
+dn: cn=Bonus Group,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Bonus Group
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Meta Group,ou=Dynamic Lists,dc=example,dc=com
+member: cn=The Smiths,ou=Dynamic Lists,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+
+dn: cn=Dynamic List of Members,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+objectClass: dgIdentityAux
+cn: Dynamic List of Members
+memberURL: ldap:///ou=People,dc=example,dc=com??sub?(objectClass=person)
+dgIdentity: cn=Bjorn Jensen,ou=Information Technology DivisioN,ou=People,dc=ex
+ ample,dc=com
+dgAuthz: {0}dn:cn=Barbara Jensen,ou=Information Technology DivisioN,ou=People,
+ dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+
+dn: cn=Meta Group,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+cn: Meta Group
+memberURL: ldap:///ou=Dynamic Lists,dc=example,dc=com??sub?(description=Smith%
+ 20family)
+member: cn=The Smiths,ou=Dynamic Lists,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+memberOf: cn=bonus group,ou=groups,dc=example,dc=com
+
+dn: cn=The Smiths,ou=Dynamic Lists,dc=example,dc=com
+objectClass: groupOfURLs
+cn: The Smiths
+memberURL: ldap:///ou=People,dc=example,dc=com??sub?(sn=Smith)
+description: Smith family
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+memberOf: cn=meta group,ou=dynamic lists,dc=example,dc=com
+memberOf: cn=bonus group,ou=groups,dc=example,dc=com
+
diff --git a/tests/data/emptydn.out b/tests/data/emptydn.out
new file mode 100644
index 0000000..3bb9f92
--- /dev/null
+++ b/tests/data/emptydn.out
@@ -0,0 +1,28 @@
+dn: o=Esempio,c=IT
+objectClass: organization
+o: Esempio
+o: Esempio S.p.A.
+o: Example
+
+dn: o=Example,c=UK
+objectClass: organization
+o: Example
+o: Example, Ltd.
+
+dn: o=Example,c=US
+objectClass: organization
+o: Example
+o: Example, Inc.
+
+dn: c=IT
+objectClass: country
+c: IT
+
+dn: c=UK
+objectClass: country
+c: UK
+
+dn: c=US
+objectClass: country
+c: US
+
diff --git a/tests/data/emptydn.out.slapadd b/tests/data/emptydn.out.slapadd
new file mode 100644
index 0000000..a0afec8
--- /dev/null
+++ b/tests/data/emptydn.out.slapadd
@@ -0,0 +1,38 @@
+dn: o=Beispiel,c=DE
+objectClass: organization
+o: Beispiel
+o: Beispiel GmbH
+o: Example
+
+dn: c=DE
+objectClass: country
+c: DE
+
+dn: o=Esempio,c=IT
+objectClass: organization
+o: Esempio
+o: Esempio S.p.A.
+o: Example
+
+dn: o=Example,c=UK
+objectClass: organization
+o: Example
+o: Example, Ltd.
+
+dn: o=Example,c=US
+objectClass: organization
+o: Example
+o: Example, Inc.
+
+dn: c=IT
+objectClass: country
+c: IT
+
+dn: c=UK
+objectClass: country
+c: UK
+
+dn: c=US
+objectClass: country
+c: US
+
diff --git a/tests/data/gluesync.out b/tests/data/gluesync.out
new file mode 100644
index 0000000..3d53473
--- /dev/null
+++ b/tests/data/gluesync.out
@@ -0,0 +1,410 @@
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+
+dn: cn=Manager,dc=example,dc=com
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+
diff --git a/tests/data/homedir/skel/.dotfile b/tests/data/homedir/skel/.dotfile
new file mode 100644
index 0000000..ec11f7d
--- /dev/null
+++ b/tests/data/homedir/skel/.dotfile
@@ -0,0 +1 @@
+some config
diff --git a/tests/data/homedir/skel/directory/broken link b/tests/data/homedir/skel/directory/broken link
new file mode 120000
index 0000000..78bc337
--- /dev/null
+++ b/tests/data/homedir/skel/directory/broken link
@@ -0,0 +1 @@
+../target \ No newline at end of file
diff --git a/tests/data/homedir/skel/symlink b/tests/data/homedir/skel/symlink
new file mode 120000
index 0000000..6d0450c
--- /dev/null
+++ b/tests/data/homedir/skel/symlink
@@ -0,0 +1 @@
+directory \ No newline at end of file
diff --git a/tests/data/idassert.out b/tests/data/idassert.out
new file mode 100644
index 0000000..fa51c25
--- /dev/null
+++ b/tests/data/idassert.out
@@ -0,0 +1,64 @@
+dn: o=Example,c=US
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: cn=Manager,o=Example,c=US
+objectClass: inetOrgPerson
+cn: Manager
+sn: Parson
+
+dn: ou=People,o=Example,c=US
+objectClass: organizationalUnit
+ou: People
+
+dn: uid=bjorn,ou=People,o=Example,c=US
+objectClass: inetOrgPerson
+cn: Bjorn Jensen
+sn: Jensen
+uid: bjorn
+mail: bjorn@example.com
+description: ***
+
+dn: uid=bjensen,ou=People,o=Example,c=US
+objectClass: inetOrgPerson
+cn: Barbara Jensen
+sn: Jensen
+uid: bjensen
+mail: bjensen@example.com
+description: ***
+
+dn: ou=Groups,o=Example,c=US
+objectClass: organizationalUnit
+ou: Groups
+
+dn: cn=All,ou=Groups,o=Example,c=US
+objectClass: groupOfNames
+cn: All
+member: uid=bjorn,ou=People,o=Example,c=US
+member: uid=bjensen,ou=People,o=Example,c=US
+
+dn: cn=Authorizable,ou=Groups,o=Example,c=US
+objectClass: groupOfNames
+cn: Authorizable
+member: uid=bjorn,ou=People,o=Example,c=US
+
+dn: ou=Admin,o=Example,c=US
+objectClass: organizationalUnit
+ou: Admin
+
+dn: cn=Proxy US,ou=Admin,o=Example,c=US
+objectClass: applicationProcess
+objectClass: simpleSecurityObject
+cn: Proxy US
+
+dn: cn=Proxy IT,ou=Admin,o=Example,c=US
+objectClass: applicationProcess
+objectClass: simpleSecurityObject
+cn: Proxy IT
+
+dn: cn=Sandbox,ou=Admin,o=Example,c=US
+objectClass: applicationProcess
+cn: Sandbox
+
diff --git a/tests/data/krb5.conf b/tests/data/krb5.conf
new file mode 100644
index 0000000..7391137
--- /dev/null
+++ b/tests/data/krb5.conf
@@ -0,0 +1,32 @@
+[libdefaults]
+ default_realm = @KRB5REALM@
+ dns_lookup_realm = false
+ dns_lookup_kdc = false
+ default_ccache_name = FILE://@TESTDIR@/ccache
+ #udp_preference_limit = 1
+[realms]
+ @KRB5REALM@ = {
+ kdc = @KDCHOST@:@KDCPORT@
+ acl_file = @TESTDIR@/kadm.acl
+ database_name = @TESTDIR@/kdc.db
+ key_stash_file = @TESTDIR@/kdc.stash
+ }
+[kdcdefaults]
+ kdc_ports = @KDCPORT@
+ kdc_tcp_ports = @KDCPORT@
+[logging]
+ kdc = FILE:@TESTDIR@/kdc.log
+ admin_server = FILE:@TESTDIR@/kadm.log
+ default = FILE:@TESTDIR@/krb5.log
+
+#Heimdal
+[kdc]
+ database = {
+ dbname = @TESTDIR@/kdc.db
+ realm = @KRB5REALM@
+ mkey_file = @TESTDIR@/kdc.stash
+ log_file = @TESTDIR@/kdc.log
+ acl_file = @TESTDIR@/kadm.acl
+ }
+[hdb]
+ db-dir = @TESTDIR@
diff --git a/tests/data/lang-out.ldif b/tests/data/lang-out.ldif
new file mode 100644
index 0000000..3d5d1fb
--- /dev/null
+++ b/tests/data/lang-out.ldif
@@ -0,0 +1,35 @@
+dn: dc=example,dc=com
+dc: example
+objectClass: organization
+objectClass: extensibleObject
+o: Example, Inc.
+o;lang-x;lang-xx;lang-y;lang-yy;lang-z;lang-zz: Example, Inc.
+name;lang-en-us: Billy Ray
+name;lang-en-us: Billy Bob
+cn;lang-en-us: Billy Ray
+name: Billy Ray
+sn;lang-en-gb;lang-en-us: Billy Ray
+sn: Ray
+
+dn: dc=example,dc=com
+o: Example, Inc.
+o;lang-x;lang-xx;lang-y;lang-yy;lang-z;lang-zz: Example, Inc.
+name;lang-en-us: Billy Ray
+name;lang-en-us: Billy Bob
+cn;lang-en-us: Billy Ray
+name: Billy Ray
+sn;lang-en-gb;lang-en-us: Billy Ray
+sn: Ray
+
+dn: dc=example,dc=com
+name;lang-en-us: Billy Ray
+name;lang-en-us: Billy Bob
+cn;lang-en-us: Billy Ray
+sn;lang-en-gb;lang-en-us: Billy Ray
+
+dn: dc=example,dc=com
+name;lang-en-us: Billy Ray
+name;lang-en-us: Billy Bob
+cn;lang-en-us: Billy Ray
+sn;lang-en-gb;lang-en-us: Billy Ray
+
diff --git a/tests/data/ldapglue.out b/tests/data/ldapglue.out
new file mode 100644
index 0000000..e781c71
--- /dev/null
+++ b/tests/data/ldapglue.out
@@ -0,0 +1,51 @@
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: cn=All,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: All
+member: uid=bjorn,ou=People,dc=example,dc=com
+member: uid=bjensen,ou=People,dc=example,dc=com
+
+dn: cn=ITD,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: ITD
+member: uid=bjorn,ou=People,dc=example,dc=com
+
+dn: uid=proxy,ou=Groups,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Proxy
+sn: Proxy
+uid: proxy
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: People
+
+dn: uid=bjorn,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Bjorn Jensen
+sn: Jensen
+uid: bjorn
+mail: bjorn@example.com
+
+dn: uid=bjensen,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Barbara Jensen
+sn: Jensen
+uid: bjensen
+mail: bjensen@example.com
+
+dn: uid=proxy,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Proxy
+sn: Proxy
+uid: proxy
+
diff --git a/tests/data/ldapglueanonymous.out b/tests/data/ldapglueanonymous.out
new file mode 100644
index 0000000..f0b576b
--- /dev/null
+++ b/tests/data/ldapglueanonymous.out
@@ -0,0 +1,6 @@
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
diff --git a/tests/data/lloadd-anon.conf b/tests/data/lloadd-anon.conf
new file mode 100644
index 0000000..a36ab39
--- /dev/null
+++ b/tests/data/lloadd-anon.conf
@@ -0,0 +1,39 @@
+# Load balancer config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+# allow big PDUs from anonymous (for testing purposes)
+sockbuf_max_incoming_client 4194303
+sockbuf_max_incoming_upstream 4194303
+
+backend-server uri=@URI2@
+ numconns=3
+ bindconns=2
+ retry=5000
+ max-pending-ops=5
+ conn-max-pending=3
+
+backend-server uri=@URI3@
+ numconns=3
+ bindconns=2
+ retry=5000
+ max-pending-ops=5
+ conn-max-pending=3
+
+backend-server uri=@URI4@
+ numconns=3
+ bindconns=2
+ retry=5000
+ max-pending-ops=5
+ conn-max-pending=3
diff --git a/tests/data/lloadd-backend-issues.conf b/tests/data/lloadd-backend-issues.conf
new file mode 100644
index 0000000..2796304
--- /dev/null
+++ b/tests/data/lloadd-backend-issues.conf
@@ -0,0 +1,55 @@
+# Load balancer config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+# allow big PDUs from anonymous (for testing purposes)
+sockbuf_max_incoming_client 4194303
+sockbuf_max_incoming_upstream 4194303
+
+bindconf
+ bindmethod=simple
+ binddn="cn=Manager,dc=example,dc=com"
+ credentials=secret
+
+# incorrect password (DB is empty)
+backend-server uri=@URI2@
+ numconns=3
+ bindconns=2
+ retry=500
+ max-pending-ops=5
+ conn-max-pending=3
+
+# backend is often unresponsive
+backend-server uri=@URI3@
+ numconns=3
+ bindconns=2
+ retry=500
+ max-pending-ops=5
+ conn-max-pending=3
+
+# unreachable backend (not running)
+backend-server uri=@URI4@
+ numconns=3
+ bindconns=2
+ retry=500
+ max-pending-ops=5
+ conn-max-pending=3
+
+# backend that fails to resolve
+backend-server uri=ldap://does.not.resolve.example.com
+ numconns=3
+ bindconns=2
+ retry=500
+ max-pending-ops=5
+ conn-max-pending=3
diff --git a/tests/data/lloadd-empty.conf b/tests/data/lloadd-empty.conf
new file mode 100644
index 0000000..a5b0ac6
--- /dev/null
+++ b/tests/data/lloadd-empty.conf
@@ -0,0 +1,25 @@
+# Load balancer config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+# allow big PDUs from anonymous (for testing purposes)
+sockbuf_max_incoming_client 4194303
+sockbuf_max_incoming_upstream 4194303
+
+feature proxyauthz
+
+bindconf
+ bindmethod=simple
+ binddn="cn=Manager,dc=example,dc=com"
+ credentials=secret
diff --git a/tests/data/lloadd-sasl.conf b/tests/data/lloadd-sasl.conf
new file mode 100644
index 0000000..343e10c
--- /dev/null
+++ b/tests/data/lloadd-sasl.conf
@@ -0,0 +1,48 @@
+# Load balancer config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+# allow big PDUs from anonymous (for testing purposes)
+sockbuf_max_incoming_client 4194303
+sockbuf_max_incoming_upstream 4194303
+
+feature proxyauthz
+
+bindconf
+ bindmethod=sasl
+ @SASL_MECH@
+ authcid=manager
+ authzid="dn:cn=Manager,dc=example,dc=com"
+ credentials=secret
+
+backend-server uri=@URI2@
+ numconns=3
+ bindconns=3
+ retry=5000
+ max-pending-ops=20
+ conn-max-pending=3
+
+backend-server uri=@URI3@
+ numconns=3
+ bindconns=3
+ retry=5000
+ max-pending-ops=20
+ conn-max-pending=3
+
+backend-server uri=@URI4@
+ numconns=3
+ bindconns=3
+ retry=5000
+ max-pending-ops=20
+ conn-max-pending=3
diff --git a/tests/data/lloadd-tls.conf b/tests/data/lloadd-tls.conf
new file mode 100644
index 0000000..310ce8f
--- /dev/null
+++ b/tests/data/lloadd-tls.conf
@@ -0,0 +1,57 @@
+# Load balancer config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+# SSL configuration
+TLSCACertificateFile @TESTDIR@/tls/ca/certs/testsuiteCA.crt
+TLSCertificateKeyFile @TESTDIR@/tls/private/localhost.key
+TLSCertificateFile @TESTDIR@/tls/certs/localhost.crt
+TLSVerifyClient try
+
+# noop for standalone
+TLSShareSlapdCTX yes
+
+# allow big PDUs from anonymous (for testing purposes)
+sockbuf_max_incoming_client 4194303
+sockbuf_max_incoming_upstream 4194303
+
+feature proxyauthz
+
+bindconf
+ bindmethod=simple
+ binddn="cn=Manager,dc=example,dc=com"
+ credentials=secret
+ tls_cacert=@TESTDIR@/tls/ca/certs/testsuiteCA.crt
+
+backend-server uri=@URIP3@
+ starttls=critical
+ numconns=3
+ bindconns=3
+ retry=5000
+ max-pending-ops=20
+ conn-max-pending=3
+
+backend-server uri=@SURIP4@
+ numconns=3
+ bindconns=3
+ retry=5000
+ max-pending-ops=20
+ conn-max-pending=3
+
+backend-server uri=@URI5@
+ numconns=3
+ bindconns=3
+ retry=5000
+ max-pending-ops=20
+ conn-max-pending=3
diff --git a/tests/data/lloadd.conf b/tests/data/lloadd.conf
new file mode 100644
index 0000000..4544eb2
--- /dev/null
+++ b/tests/data/lloadd.conf
@@ -0,0 +1,46 @@
+# Load balancer config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+# allow big PDUs from anonymous (for testing purposes)
+sockbuf_max_incoming_client 4194303
+sockbuf_max_incoming_upstream 4194303
+
+feature proxyauthz
+
+bindconf
+ bindmethod=simple
+ binddn="cn=Manager,dc=example,dc=com"
+ credentials=secret
+
+backend-server uri=@URI2@
+ numconns=3
+ bindconns=3
+ retry=5000
+ max-pending-ops=20
+ conn-max-pending=3
+
+backend-server uri=@URI3@
+ numconns=3
+ bindconns=3
+ retry=5000
+ max-pending-ops=20
+ conn-max-pending=3
+
+backend-server uri=@URI4@
+ numconns=3
+ bindconns=3
+ retry=5000
+ max-pending-ops=20
+ conn-max-pending=3
diff --git a/tests/data/lloadd/monitor.ldif b/tests/data/lloadd/monitor.ldif
new file mode 100644
index 0000000..9e0f3ff
--- /dev/null
+++ b/tests/data/lloadd/monitor.ldif
@@ -0,0 +1,278 @@
+# empty lloadd
+dn: cn=Load Balancer,cn=Backends,cn=Monitor
+objectClass: olmBalancer
+olmIncomingConnections: 0
+olmOutgoingConnections: 0
+
+dn: cn=Incoming Connections,cn=Load Balancer,cn=Backends,cn=Monitor
+objectClass: monitorContainer
+
+dn: cn=Operations,cn=Load Balancer,cn=Backends,cn=Monitor
+objectClass: monitorContainer
+
+dn: cn=Bind,cn=Operations,cn=Load Balancer,cn=Backends,cn=Monitor
+objectClass: olmBalancerOperation
+olmReceivedOps: 1
+olmForwardedOps: 0
+olmRejectedOps: 1
+olmCompletedOps: 0
+olmFailedOps: 0
+
+dn: cn=Other,cn=Operations,cn=Load Balancer,cn=Backends,cn=Monitor
+objectClass: olmBalancerOperation
+olmReceivedOps: 1
+olmForwardedOps: 0
+olmRejectedOps: 0
+olmCompletedOps: 0
+olmFailedOps: 0
+
+dn: cn=Backend Servers,cn=Load Balancer,cn=Backends,cn=Monitor
+objectClass: monitorContainer
+
+
+# with first backend
+dn: cn=Load Balancer,cn=Backends,cn=Monitor
+objectClass: olmBalancer
+olmIncomingConnections: 0
+olmOutgoingConnections: 4
+
+dn: cn=Incoming Connections,cn=Load Balancer,cn=Backends,cn=Monitor
+objectClass: monitorContainer
+
+dn: cn=Operations,cn=Load Balancer,cn=Backends,cn=Monitor
+objectClass: monitorContainer
+
+dn: cn=Bind,cn=Operations,cn=Load Balancer,cn=Backends,cn=Monitor
+objectClass: olmBalancerOperation
+olmReceivedOps: 1
+olmForwardedOps: 0
+olmRejectedOps: 1
+olmCompletedOps: 0
+olmFailedOps: 0
+
+dn: cn=Other,cn=Operations,cn=Load Balancer,cn=Backends,cn=Monitor
+objectClass: olmBalancerOperation
+olmReceivedOps: 1
+olmForwardedOps: 0
+olmRejectedOps: 0
+olmCompletedOps: 0
+olmFailedOps: 0
+
+dn: cn=Backend Servers,cn=Load Balancer,cn=Backends,cn=Monitor
+objectClass: monitorContainer
+
+dn: cn=first,cn=Backend Servers,cn=Load Balancer,cn=Backends,cn=Monitor
+objectClass: olmBalancerServer
+olmServerURI: ldap://localhost:9012/
+olmActiveConnections: 4
+olmPendingConnections: 0
+olmPendingOps: 0
+olmReceivedOps: 0
+olmCompletedOps: 0
+olmFailedOps: 0
+
+dn: cn=Connection 1,cn=first,cn=Backend Servers,cn=Load Balancer,cn=Backends,c
+ n=Monitor
+objectClass: olmBalancerConnection
+olmConnectionType: regular
+olmPendingOps: 0
+olmReceivedOps: 0
+olmCompletedOps: 0
+olmFailedOps: 0
+
+dn: cn=Connection 3,cn=first,cn=Backend Servers,cn=Load Balancer,cn=Backends,c
+ n=Monitor
+objectClass: olmBalancerConnection
+olmConnectionType: regular
+olmPendingOps: 0
+olmReceivedOps: 0
+olmCompletedOps: 0
+olmFailedOps: 0
+
+dn: cn=Connection 2,cn=first,cn=Backend Servers,cn=Load Balancer,cn=Backends,c
+ n=Monitor
+objectClass: olmBalancerConnection
+olmConnectionType: bind
+olmPendingOps: 0
+olmReceivedOps: 0
+olmCompletedOps: 0
+olmFailedOps: 0
+
+dn: cn=Connection 4,cn=first,cn=Backend Servers,cn=Load Balancer,cn=Backends,c
+ n=Monitor
+objectClass: olmBalancerConnection
+olmConnectionType: bind
+olmPendingOps: 0
+olmReceivedOps: 0
+olmCompletedOps: 0
+olmFailedOps: 0
+
+
+# second backend and a search+WhoAmI?
+dn: cn=Load Balancer,cn=Backends,cn=Monitor
+objectClass: olmBalancer
+olmIncomingConnections: 0
+olmOutgoingConnections: 13
+
+dn: cn=Incoming Connections,cn=Load Balancer,cn=Backends,cn=Monitor
+objectClass: monitorContainer
+
+dn: cn=Operations,cn=Load Balancer,cn=Backends,cn=Monitor
+objectClass: monitorContainer
+
+dn: cn=Bind,cn=Operations,cn=Load Balancer,cn=Backends,cn=Monitor
+objectClass: olmBalancerOperation
+olmReceivedOps: 3
+olmForwardedOps: 2
+olmRejectedOps: 1
+olmCompletedOps: 2
+olmFailedOps: 0
+
+dn: cn=Other,cn=Operations,cn=Load Balancer,cn=Backends,cn=Monitor
+objectClass: olmBalancerOperation
+olmReceivedOps: 5
+olmForwardedOps: 2
+olmRejectedOps: 0
+olmCompletedOps: 2
+olmFailedOps: 0
+
+dn: cn=Backend Servers,cn=Load Balancer,cn=Backends,cn=Monitor
+objectClass: monitorContainer
+
+dn: cn=first,cn=Backend Servers,cn=Load Balancer,cn=Backends,cn=Monitor
+objectClass: olmBalancerServer
+olmServerURI: ldap://localhost:9012/
+olmActiveConnections: 4
+olmPendingConnections: 0
+olmPendingOps: 0
+olmReceivedOps: 2
+olmCompletedOps: 2
+olmFailedOps: 0
+
+dn: cn=Connection 1,cn=first,cn=Backend Servers,cn=Load Balancer,cn=Backends,c
+ n=Monitor
+objectClass: olmBalancerConnection
+olmConnectionType: regular
+olmPendingOps: 0
+olmReceivedOps: 0
+olmCompletedOps: 0
+olmFailedOps: 0
+
+dn: cn=Connection 3,cn=first,cn=Backend Servers,cn=Load Balancer,cn=Backends,c
+ n=Monitor
+objectClass: olmBalancerConnection
+olmConnectionType: regular
+olmPendingOps: 0
+olmReceivedOps: 0
+olmCompletedOps: 0
+olmFailedOps: 0
+
+dn: cn=Connection 2,cn=first,cn=Backend Servers,cn=Load Balancer,cn=Backends,c
+ n=Monitor
+objectClass: olmBalancerConnection
+olmConnectionType: bind
+olmPendingOps: 0
+olmReceivedOps: 1
+olmCompletedOps: 1
+olmFailedOps: 0
+
+dn: cn=Connection 4,cn=first,cn=Backend Servers,cn=Load Balancer,cn=Backends,c
+ n=Monitor
+objectClass: olmBalancerConnection
+olmConnectionType: bind
+olmPendingOps: 0
+olmReceivedOps: 1
+olmCompletedOps: 1
+olmFailedOps: 0
+
+dn: cn=server 2,cn=Backend Servers,cn=Load Balancer,cn=Backends,cn=Monitor
+objectClass: olmBalancerServer
+olmServerURI: ldap://localhost:9013/
+olmActiveConnections: 9
+olmPendingConnections: 0
+olmPendingOps: 0
+olmReceivedOps: 2
+olmCompletedOps: 2
+olmFailedOps: 0
+
+dn: cn=Connection 5,cn=server 2,cn=Backend Servers,cn=Load Balancer,cn=Backend
+ s,cn=Monitor
+objectClass: olmBalancerConnection
+olmConnectionType: regular
+olmPendingOps: 0
+olmReceivedOps: 1
+olmCompletedOps: 1
+olmFailedOps: 0
+
+dn: cn=Connection 7,cn=server 2,cn=Backend Servers,cn=Load Balancer,cn=Backend
+ s,cn=Monitor
+objectClass: olmBalancerConnection
+olmConnectionType: regular
+olmPendingOps: 0
+olmReceivedOps: 1
+olmCompletedOps: 1
+olmFailedOps: 0
+
+dn: cn=Connection 8,cn=server 2,cn=Backend Servers,cn=Load Balancer,cn=Backend
+ s,cn=Monitor
+objectClass: olmBalancerConnection
+olmConnectionType: regular
+olmPendingOps: 0
+olmReceivedOps: 0
+olmCompletedOps: 0
+olmFailedOps: 0
+
+dn: cn=Connection 9,cn=server 2,cn=Backend Servers,cn=Load Balancer,cn=Backend
+ s,cn=Monitor
+objectClass: olmBalancerConnection
+olmConnectionType: regular
+olmPendingOps: 0
+olmReceivedOps: 0
+olmCompletedOps: 0
+olmFailedOps: 0
+
+dn: cn=Connection 6,cn=server 2,cn=Backend Servers,cn=Load Balancer,cn=Backend
+ s,cn=Monitor
+objectClass: olmBalancerConnection
+olmConnectionType: bind
+olmPendingOps: 0
+olmReceivedOps: 0
+olmCompletedOps: 0
+olmFailedOps: 0
+
+dn: cn=Connection 10,cn=server 2,cn=Backend Servers,cn=Load Balancer,cn=Backen
+ ds,cn=Monitor
+objectClass: olmBalancerConnection
+olmConnectionType: bind
+olmPendingOps: 0
+olmReceivedOps: 0
+olmCompletedOps: 0
+olmFailedOps: 0
+
+dn: cn=Connection 11,cn=server 2,cn=Backend Servers,cn=Load Balancer,cn=Backen
+ ds,cn=Monitor
+objectClass: olmBalancerConnection
+olmConnectionType: bind
+olmPendingOps: 0
+olmReceivedOps: 0
+olmCompletedOps: 0
+olmFailedOps: 0
+
+dn: cn=Connection 12,cn=server 2,cn=Backend Servers,cn=Load Balancer,cn=Backen
+ ds,cn=Monitor
+objectClass: olmBalancerConnection
+olmConnectionType: bind
+olmPendingOps: 0
+olmReceivedOps: 0
+olmCompletedOps: 0
+olmFailedOps: 0
+
+dn: cn=Connection 13,cn=server 2,cn=Backend Servers,cn=Load Balancer,cn=Backen
+ ds,cn=Monitor
+objectClass: olmBalancerConnection
+olmConnectionType: bind
+olmPendingOps: 0
+olmReceivedOps: 0
+olmCompletedOps: 0
+olmFailedOps: 0
+
diff --git a/tests/data/manage.out b/tests/data/manage.out
new file mode 100644
index 0000000..1ec0d94
--- /dev/null
+++ b/tests/data/manage.out
@@ -0,0 +1,508 @@
+dn: cn=All Staff,dc=example,dc=com
+objectClass: groupOfNames
+cn: All Staff
+member:
+creatorsName: cn=Someone
+modifiersName: cn=Someone Else
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+creatorsName: cn=Manager,dc=example,dc=com
+modifiersName: cn=Manager,dc=example,dc=com
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+creatorsName: cn=Manager,dc=example,dc=com
+modifiersName: cn=Manager,dc=example,dc=com
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Alumni Association
+creatorsName: cn=Manager,dc=example,dc=com
+modifiersName: cn=Manager,dc=example,dc=com
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+creatorsName: cn=Manager,dc=example,dc=com
+testObsolete: TRUE
+objectClass: obsoletePerson
+objectClass: testPerson
+modifiersName: cn=Manager,dc=example,dc=com
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+creatorsName: cn=Manager,dc=example,dc=com
+objectClass: testPerson
+modifiersName: cn=Manager,dc=example,dc=com
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+creatorsName: cn=Manager,dc=example,dc=com
+modifiersName: cn=Manager,dc=example,dc=com
+
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+creatorsName: cn=Manager,dc=example,dc=com
+modifiersName: cn=Manager,dc=example,dc=com
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+creatorsName: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+modifiersName: cn=Manager,dc=example,dc=com
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+creatorsName: cn=Manager,dc=example,dc=com
+modifiersName: cn=Manager,dc=example,dc=com
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+creatorsName: cn=Manager,dc=example,dc=com
+modifiersName: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc
+ =example,dc=com
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+objectClass: testPerson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+creatorsName: cn=Manager,dc=example,dc=com
+modifiersName: cn=Manager,dc=example,dc=com
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+creatorsName: cn=Manager,dc=example,dc=com
+modifiersName: cn=Manager,dc=example,dc=com
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+creatorsName: cn=Manager,dc=example,dc=com
+modifiersName: cn=Manager,dc=example,dc=com
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+creatorsName: cn=Manager,dc=example,dc=com
+modifiersName: cn=Manager,dc=example,dc=com
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+creatorsName: cn=Manager,dc=example,dc=com
+modifiersName: cn=Manager,dc=example,dc=com
+
+dn: cn=Manager,dc=example,dc=com
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+creatorsName: cn=Manager,dc=example,dc=com
+modifiersName: cn=Manager,dc=example,dc=com
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+creatorsName: cn=Manager,dc=example,dc=com
+modifiersName: cn=Manager,dc=example,dc=com
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+creatorsName: cn=Manager,dc=example,dc=com
+modifiersName: cn=Manager,dc=example,dc=com
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+creatorsName: cn=Manager,dc=example,dc=com
+modifiersName: cn=Manager,dc=example,dc=com
+
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+creatorsName: cn=Manager,dc=example,dc=com
+modifyTimestamp: 19700101000000Z
+createTimestamp: 19700101000000Z
+modifiersName: cn=Manager,dc=example,dc=com
+
+dn: cn=All Staff,dc=example,dc=com
+objectClass: groupOfNames
+cn: All Staff
+member:
+entryUUID: badbadef-dbad-1029-92f7-badbadbadbad
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+entryUUID: badbadba-dbad-1029-92f7-badbadbadbad
+
diff --git a/tests/data/memberof-refint.out b/tests/data/memberof-refint.out
new file mode 100644
index 0000000..f63997a
--- /dev/null
+++ b/tests/data/memberof-refint.out
@@ -0,0 +1,125 @@
+# Search the entire database...
+dn: cn=Baby Herman,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Baby Herman
+sn: Herman
+memberOf: cn=Cartoonia,ou=Groups,dc=example,dc=com
+
+dn: cn=Cartoonia,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Cartoonia
+member: cn=Roger Rabbit,ou=People,dc=example,dc=com
+member: cn=Baby Herman,ou=People,dc=example,dc=com
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: People
+
+dn: cn=Roger Rabbit,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Roger Rabbit
+sn: Rabbit
+memberOf: cn=Cartoonia,ou=Groups,dc=example,dc=com
+
+# Re-search the entire database...
+dn: cn=Baby Herman,ou=Toons,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Baby Herman
+sn: Herman
+memberOf: cn=Cartoonia,ou=Groups,dc=example,dc=com
+
+dn: cn=Cartoonia,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Cartoonia
+member: cn=Roger Rabbit,ou=Toons,dc=example,dc=com
+member: cn=Baby Herman,ou=Toons,dc=example,dc=com
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: cn=Roger Rabbit,ou=Toons,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Roger Rabbit
+sn: Rabbit
+memberOf: cn=Cartoonia,ou=Groups,dc=example,dc=com
+
+dn: ou=Toons,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Toons
+
+# Re-search the entire database...
+dn: cn=Baby Herman,ou=Toons,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Baby Herman
+sn: Herman
+memberOf: cn=Cartoonia,ou=Studios,dc=example,dc=com
+
+dn: cn=Cartoonia,ou=Studios,dc=example,dc=com
+objectClass: groupOfNames
+cn: Cartoonia
+member: cn=Roger Rabbit,ou=Toons,dc=example,dc=com
+member: cn=Baby Herman,ou=Toons,dc=example,dc=com
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: cn=Roger Rabbit,ou=Toons,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Roger Rabbit
+sn: Rabbit
+memberOf: cn=Cartoonia,ou=Studios,dc=example,dc=com
+
+dn: ou=Studios,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Studios
+
+dn: ou=Toons,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Toons
+
+# Re-search the entire database...
+dn: cn=Cartoonia,ou=Studios,dc=example,dc=com
+objectClass: groupOfNames
+cn: Cartoonia
+member: cn=Roger Rabbit,ou=Toons,dc=example,dc=com
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: cn=Roger Rabbit,ou=Toons,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Roger Rabbit
+sn: Rabbit
+memberOf: cn=Cartoonia,ou=Studios,dc=example,dc=com
+
+dn: ou=Studios,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Studios
+
+dn: ou=Toons,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Toons
+
diff --git a/tests/data/memberof.out b/tests/data/memberof.out
new file mode 100644
index 0000000..82fb924
--- /dev/null
+++ b/tests/data/memberof.out
@@ -0,0 +1,341 @@
+# Search the entire database...
+dn: cn=Baby Herman,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Baby Herman
+sn: Herman
+memberOf: cn=Cartoonia,ou=Groups,dc=example,dc=com
+
+dn: cn=Cartoonia,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Cartoonia
+member: cn=Roger Rabbit,ou=People,dc=example,dc=com
+member: cn=Baby Herman,ou=People,dc=example,dc=com
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: People
+
+dn: cn=Roger Rabbit,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Roger Rabbit
+sn: Rabbit
+memberOf: cn=Cartoonia,ou=Groups,dc=example,dc=com
+
+# Re-search the entire database after adding Jessica Rabbit and Cartoonia...
+dn: cn=Baby Herman,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Baby Herman
+sn: Herman
+memberOf: cn=Cartoonia,ou=Groups,dc=example,dc=com
+
+dn: cn=Cartoonia,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Cartoonia
+member: cn=Roger Rabbit,ou=People,dc=example,dc=com
+member: cn=Baby Herman,ou=People,dc=example,dc=com
+member: cn=Jessica Rabbit,ou=People,dc=example,dc=com
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: cn=Jessica Rabbit,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Jessica Rabbit
+sn: Rabbit
+memberOf: cn=Cartoonia,ou=Groups,dc=example,dc=com
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: People
+
+dn: cn=Roger Rabbit,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Roger Rabbit
+sn: Rabbit
+memberOf: cn=Cartoonia,ou=Groups,dc=example,dc=com
+
+# Re-search the entire database after renaming Baby Herman...
+dn: cn=Baby Herman Jr,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+sn: Herman
+memberOf: cn=Cartoonia,ou=Groups,dc=example,dc=com
+cn: Baby Herman Jr
+
+dn: cn=Cartoonia,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Cartoonia
+member: cn=Roger Rabbit,ou=People,dc=example,dc=com
+member: cn=Jessica Rabbit,ou=People,dc=example,dc=com
+member: cn=Baby Herman Jr,ou=People,dc=example,dc=com
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: cn=Jessica Rabbit,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Jessica Rabbit
+sn: Rabbit
+memberOf: cn=Cartoonia,ou=Groups,dc=example,dc=com
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: People
+
+dn: cn=Roger Rabbit,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Roger Rabbit
+sn: Rabbit
+memberOf: cn=Cartoonia,ou=Groups,dc=example,dc=com
+
+# Re-search the entire database after renaming Cartoonia...
+dn: cn=Baby Herman Jr,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+sn: Herman
+memberOf: cn=Toon Town,ou=Groups,dc=example,dc=com
+cn: Baby Herman Jr
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: cn=Jessica Rabbit,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Jessica Rabbit
+sn: Rabbit
+memberOf: cn=Toon Town,ou=Groups,dc=example,dc=com
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: People
+
+dn: cn=Roger Rabbit,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Roger Rabbit
+sn: Rabbit
+memberOf: cn=Toon Town,ou=Groups,dc=example,dc=com
+
+dn: cn=Toon Town,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+member: cn=Roger Rabbit,ou=People,dc=example,dc=com
+member: cn=Jessica Rabbit,ou=People,dc=example,dc=com
+member: cn=Baby Herman Jr,ou=People,dc=example,dc=com
+cn: Toon Town
+
+# Re-search the entire database after adding Toon Town to self...
+dn: cn=Baby Herman Jr,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+sn: Herman
+memberOf: cn=Toon Town,ou=Groups,dc=example,dc=com
+cn: Baby Herman Jr
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: cn=Jessica Rabbit,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Jessica Rabbit
+sn: Rabbit
+memberOf: cn=Toon Town,ou=Groups,dc=example,dc=com
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: People
+
+dn: cn=Roger Rabbit,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Roger Rabbit
+sn: Rabbit
+memberOf: cn=Toon Town,ou=Groups,dc=example,dc=com
+
+dn: cn=Toon Town,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+member: cn=Roger Rabbit,ou=People,dc=example,dc=com
+member: cn=Jessica Rabbit,ou=People,dc=example,dc=com
+member: cn=Baby Herman Jr,ou=People,dc=example,dc=com
+member: cn=Toon Town,ou=Groups,dc=example,dc=com
+cn: Toon Town
+memberOf: cn=Toon Town,ou=Groups,dc=example,dc=com
+
+# Re-search the entire database after deleting Baby Herman...
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: cn=Jessica Rabbit,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Jessica Rabbit
+sn: Rabbit
+memberOf: cn=Toon Town,ou=Groups,dc=example,dc=com
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: People
+
+dn: cn=Roger Rabbit,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Roger Rabbit
+sn: Rabbit
+memberOf: cn=Toon Town,ou=Groups,dc=example,dc=com
+
+dn: cn=Toon Town,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+member: cn=Roger Rabbit,ou=People,dc=example,dc=com
+member: cn=Jessica Rabbit,ou=People,dc=example,dc=com
+member: cn=Toon Town,ou=Groups,dc=example,dc=com
+cn: Toon Town
+memberOf: cn=Toon Town,ou=Groups,dc=example,dc=com
+
+# Re-search the entire database after deleting Toon Town...
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: cn=Jessica Rabbit,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Jessica Rabbit
+sn: Rabbit
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: People
+
+dn: cn=Roger Rabbit,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Roger Rabbit
+sn: Rabbit
+
+# Re-search the entire database after adding groups with MAY member type schemas...
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: cn=group1,ou=Groups,dc=example,dc=com
+objectClass: groupA
+cn: group1
+
+dn: cn=group2,ou=Groups,dc=example,dc=com
+objectClass: groupB
+cn: group2
+memberB: cn=person1,ou=People,dc=example,dc=com
+memberB: cn=person2,ou=People,dc=example,dc=com
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: People
+
+dn: cn=person1,ou=People,dc=example,dc=com
+objectClass: person
+objectClass: groupMemberA
+objectClass: groupMemberB
+cn: person1
+sn: person1
+memberOfB: cn=group2,ou=Groups,dc=example,dc=com
+
+dn: cn=person2,ou=People,dc=example,dc=com
+objectClass: person
+objectClass: groupMemberA
+objectClass: groupMemberB
+cn: person2
+sn: person2
+memberOfB: cn=group2,ou=Groups,dc=example,dc=com
+
+# Re-search the entire database after updating memberof configuration...
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: cn=group1,ou=Groups,dc=example,dc=com
+objectClass: groupA
+cn: group1
+memberA: cn=person1,ou=People,dc=example,dc=com
+memberA: cn=person2,ou=People,dc=example,dc=com
+
+dn: cn=group2,ou=Groups,dc=example,dc=com
+objectClass: groupB
+cn: group2
+memberB: cn=person1,ou=People,dc=example,dc=com
+memberB: cn=person2,ou=People,dc=example,dc=com
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: People
+
+dn: cn=person1,ou=People,dc=example,dc=com
+objectClass: person
+objectClass: groupMemberA
+objectClass: groupMemberB
+cn: person1
+sn: person1
+memberOfB: cn=group2,ou=Groups,dc=example,dc=com
+memberOfC: cn=group1,ou=Groups,dc=example,dc=com
+
+dn: cn=person2,ou=People,dc=example,dc=com
+objectClass: person
+objectClass: groupMemberA
+objectClass: groupMemberB
+cn: person2
+sn: person2
+memberOfB: cn=group2,ou=Groups,dc=example,dc=com
+memberOfC: cn=group1,ou=Groups,dc=example,dc=com
+
diff --git a/tests/data/meta.out b/tests/data/meta.out
new file mode 100644
index 0000000..1ce8713
--- /dev/null
+++ b/tests/data/meta.out
@@ -0,0 +1,1450 @@
+# searching base="o=Example,c=US"...
+dn: cn=All Staff,ou=Groups,o=Example,c=US
+member: cn=Manager,o=Example,c=US
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Examp
+ le,c=US
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=John Doe,ou=Information Technology Division,ou=People,o=Example,c=U
+ S
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Exam
+ ple,c=US
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Example
+ ,c=US
+owner: cn=Manager,o=Example,c=US
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,o=Example,c=US
+member: cn=Manager,o=Example,c=US
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+owner: cn=Manager,o=Example,c=US
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Example,c
+ =US
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Example,c=U
+ S
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+dn: cn=Dan Aykroyd,ou=Meta,o=Example,c=US
+objectClass: inetOrgPerson
+cn: Dan Aykroyd
+sn: Aykroyd
+userPassword:: ZWx3b29k
+description: Elwood Blues
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+
+dn: o=Example,c=US
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=Groups,o=Example,c=US
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,o=Example,c=US
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=ITD Staff,ou=Groups,o=Example,c=US
+owner: cn=Manager,o=Example,c=US
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Example,
+ c=US
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+
+dn: cn=John Belushi,ou=Meta,o=Example,c=US
+objectClass: inetOrgPerson
+cn: John Belushi
+sn: Belushi
+userPassword:: amFjaw==
+description: Joliet Jack Blues
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+
+dn: cn=Manager,o=Example,c=US
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+
+dn: ou=Meta,o=Example,c=US
+objectClass: organizationalUnit
+ou: Meta
+seeAlso: dc=OpenLDAP,dc=org
+
+dn: ou=People,o=Example,c=US
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+
+# refldap://localhost:9016/cn=Somewhere,ou=Meta,dc=example,dc=com??sub
+
+# searching base="ou=Meta,o=Example,c=US"...
+dn: cn=Dan Aykroyd,ou=Meta,o=Example,c=US
+objectClass: inetOrgPerson
+cn: Dan Aykroyd
+sn: Aykroyd
+userPassword:: ZWx3b29k
+description: Elwood Blues
+
+dn: cn=John Belushi,ou=Meta,o=Example,c=US
+objectClass: inetOrgPerson
+cn: John Belushi
+sn: Belushi
+userPassword:: amFjaw==
+description: Joliet Jack Blues
+
+dn: ou=Meta,o=Example,c=US
+objectClass: organizationalUnit
+ou: Meta
+seeAlso: dc=OpenLDAP,dc=org
+
+# refldap://localhost:9016/cn=Somewhere,ou=Meta,dc=example,dc=com??sub
+
+# searching base="o=Example,c=US"...
+dn: cn=Added Group,ou=Groups,o=Example,c=US
+objectClass: groupOfNames
+cn: Added Group
+member: cn=Added Group,ou=Groups,o=Example,c=US
+
+dn: cn=Added User,ou=Same as above,ou=Meta,o=Example,c=US
+objectClass: inetOrgPerson
+cn: Added User
+sn: User
+userPassword:: c2VjcmV0
+
+dn: cn=All Staff,ou=Groups,o=Example,c=US
+member: cn=Manager,o=Example,c=US
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Examp
+ le,c=US
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=John Doe,ou=Information Technology Division,ou=People,o=Example,c=U
+ S
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Exam
+ ple,c=US
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Example
+ ,c=US
+owner: cn=Manager,o=Example,c=US
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,o=Example,c=US
+member: cn=Manager,o=Example,c=US
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+owner: cn=Manager,o=Example,c=US
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Another Added Group,ou=Groups,o=Example,c=US
+objectClass: groupOfNames
+objectClass: uidObject
+cn: Another Added Group
+member: cn=Added Group,ou=Groups,o=Example,c=US
+member: cn=Another Added Group,ou=Groups,o=Example,c=US
+uid: added
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Example,c
+ =US
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Example,c=U
+ S
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+dn: cn=Dan Aykroyd,ou=Meta,o=Example,c=US
+objectClass: inetOrgPerson
+cn: Dan Aykroyd
+sn: Aykroyd
+userPassword:: ZWx3b29k
+description: Elwood Blues
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+
+dn: o=Example,c=US
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=Groups,o=Example,c=US
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,o=Example,c=US
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=ITD Staff,ou=Groups,o=Example,c=US
+owner: cn=Manager,o=Example,c=US
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Example,
+ c=US
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+
+dn: cn=John Belushi,ou=Meta,o=Example,c=US
+objectClass: inetOrgPerson
+cn: John Belushi
+sn: Belushi
+userPassword:: amFjaw==
+description: Joliet Jack Blues
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+
+dn: cn=Manager,o=Example,c=US
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+
+dn: ou=Meta,o=Example,c=US
+objectClass: organizationalUnit
+ou: Meta
+seeAlso: dc=OpenLDAP,dc=org
+description: added to "ou=Meta,o=Example,c=US"
+
+dn: ou=People,o=Example,c=US
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: ou=Same as above,o=Example,c=US
+objectClass: organizationalUnit
+ou: Same as above
+description: added right after "Who's going to handle this?"
+description: will be preserved
+
+dn: ou=Same as above,ou=Meta,o=Example,c=US
+objectClass: organizationalUnit
+ou: Same as above
+description: added right after "Who's going to handle this?"
+description: will be preserved
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+
+# refldap://localhost:9016/cn=Somewhere,ou=Meta,dc=example,dc=com??sub
+
+# base="o=Example,c=US"...
+dn: cn=Added Group,ou=Groups,o=Example,c=US
+objectClass: groupOfNames
+cn: Added Group
+member: cn=Added Group,ou=Groups,o=Example,c=US
+
+dn: cn=Added User,ou=Same as above,ou=Meta,o=Example,c=US
+objectClass: inetOrgPerson
+cn: Added User
+sn: User
+userPassword:: c2VjcmV0
+
+dn: cn=All Staff,ou=Groups,o=Example,c=US
+member: cn=Manager,o=Example,c=US
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Examp
+ le,c=US
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=John Doe,ou=Information Technology Division,ou=People,o=Example,c=U
+ S
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Exam
+ ple,c=US
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Example
+ ,c=US
+owner: cn=Manager,o=Example,c=US
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,o=Example,c=US
+member: cn=Manager,o=Example,c=US
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+owner: cn=Manager,o=Example,c=US
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Another Added Group,ou=Groups,o=Example,c=US
+objectClass: groupOfNames
+objectClass: uidObject
+cn: Another Added Group
+member: cn=Added Group,ou=Groups,o=Example,c=US
+member: cn=Another Added Group,ou=Groups,o=Example,c=US
+uid: added
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Example,c
+ =US
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Example,c=U
+ S
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+dn: cn=Dan Aykroyd,ou=Meta,o=Example,c=US
+objectClass: inetOrgPerson
+cn: Dan Aykroyd
+sn: Aykroyd
+userPassword:: ZWx3b29k
+description: Elwood Blues
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+
+dn: o=Example,c=US
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=Groups,o=Example,c=US
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,o=Example,c=US
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=ITD Staff,ou=Groups,o=Example,c=US
+owner: cn=Manager,o=Example,c=US
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Example,
+ c=US
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+
+dn: cn=John Belushi,ou=Meta,o=Example,c=US
+objectClass: inetOrgPerson
+cn: John Belushi
+sn: Belushi
+userPassword:: amFjaw==
+description: Joliet Jack Blues
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+
+dn: cn=Manager,o=Example,c=US
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+
+dn: ou=Meta,o=Example,c=US
+objectClass: organizationalUnit
+ou: Meta
+seeAlso: dc=OpenLDAP,dc=org
+description: added to "ou=Meta,o=Example,c=US"
+
+dn: ou=People,o=Example,c=US
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: ou=Same as above,o=Example,c=US
+objectClass: organizationalUnit
+ou: Same as above
+description: added right after "Who's going to handle this?"
+description: will be preserved
+
+dn: ou=Same as above,ou=Meta,o=Example,c=US
+objectClass: organizationalUnit
+ou: Same as above
+description: added right after "Who's going to handle this?"
+description: will be preserved
+
+dn: cn=Somewhere,ou=Meta,o=Example,c=US
+objectClass: referral
+objectClass: extensibleObject
+cn: Somewhere
+ref: ldap://localhost:9016
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+
+# searching filter="(seeAlso=cn=all staff,ou=Groups,o=Example,c=US)"
+# attrs="seeAlso"
+# base="o=Example,c=US"...
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Example,c
+ =US
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Example,c=U
+ S
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Example,c=US
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Example,c=US
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Example,
+ c=US
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,o=Example,c=US
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Example,c=US
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,o=Example,c=US
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Example,c=US
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+
+# refldap://localhost:9016/cn=Somewhere,ou=Meta,dc=example,dc=com??sub
+
+# searching filter="(uid=example)"
+# attrs="uid"
+# base="o=Example,c=US"...
+# refldap://localhost:9016/cn=Somewhere,ou=Meta,dc=example,dc=com??sub
+
+# searching filter="(member=cn=Another Added Group,ou=Groups,o=Example,c=US)"
+# attrs="member"
+# base="o=Example,c=US"...
+dn: cn=Another Added Group,ou=Groups,o=Example,c=US
+member: cn=Added Group,ou=Groups,o=Example,c=US
+member: cn=Another Added Group,ou=Groups,o=Example,c=US
+
+# refldap://localhost:9016/cn=Somewhere,ou=Meta,dc=example,dc=com??sub
+
+# searching filter="(member=cn=Another Added Group,ou=Groups,o=Example,c=US)"
+# attrs="member"
+# base="o=Example,c=US"
+# with a timed out connection...
+dn: cn=Another Added Group,ou=Groups,o=Example,c=US
+member: cn=Added Group,ou=Groups,o=Example,c=US
+member: cn=Another Added Group,ou=Groups,o=Example,c=US
+
+# refldap://localhost:9016/cn=Somewhere,ou=Meta,dc=example,dc=com??sub
+
+# Checking server-enforced size limit...
+# Checking client-requested size limit...
diff --git a/tests/data/metaconcurrency.out b/tests/data/metaconcurrency.out
new file mode 100644
index 0000000..38c91e7
--- /dev/null
+++ b/tests/data/metaconcurrency.out
@@ -0,0 +1,431 @@
+dn: cn=All Staff,ou=Groups,o=Example,c=US
+member: cn=Manager,o=Example,c=US
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Examp
+ le,c=US
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=John Doe,ou=Information Technology Division,ou=People,o=Example,c=U
+ S
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Exam
+ ple,c=US
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Example
+ ,c=US
+owner: cn=Manager,o=Example,c=US
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,o=Example,c=US
+member: cn=Manager,o=Example,c=US
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+owner: cn=Manager,o=Example,c=US
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Example,c
+ =US
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Example,c=U
+ S
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+dn: cn=Dan Aykroyd,ou=Meta,o=Example,c=US
+objectClass: inetOrgPerson
+cn: Dan Aykroyd
+sn: Aykroyd
+userPassword:: ZWx3b29k
+description: Elwood Blues
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+
+dn: o=Example,c=US
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=Groups,o=Example,c=US
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,o=Example,c=US
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=ITD Staff,ou=Groups,o=Example,c=US
+owner: cn=Manager,o=Example,c=US
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Example,
+ c=US
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+
+dn: cn=John Belushi,ou=Meta,o=Example,c=US
+objectClass: inetOrgPerson
+cn: John Belushi
+sn: Belushi
+userPassword:: amFjaw==
+description: Joliet Jack Blues
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+
+dn: cn=Manager,o=Example,c=US
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+
+dn: ou=Meta,o=Example,c=US
+objectClass: organizationalUnit
+ou: Meta
+seeAlso: dc=OpenLDAP,dc=org
+
+dn: ou=People,o=Example,c=US
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+
+# refldap://localhost:9016/cn=Somewhere,ou=Meta,dc=example,dc=com??sub
+
diff --git a/tests/data/modify.out.provider b/tests/data/modify.out.provider
new file mode 100644
index 0000000..aed6b39
--- /dev/null
+++ b/tests/data/modify.out.provider
@@ -0,0 +1,396 @@
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+objectClass: groupOfNames
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+description: The replaced multiLineDescription $ Blah Woof.
+drink: Iced Tea
+drink: Mad Dog 20/20
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: cn=Gern Jensen,ou=Information Technology Division,ou=People,dc=example,dc=
+ com
+objectClass: testPerson
+cn: Gern Jensen
+sn: Jensen
+uid: gjensen
+title: Chief Investigator, ITD
+postalAddress: ITD $ 535 W. William St $ Anytown, MI 48103
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Coffee
+homePostalAddress: 844 Brown St. Apt. 4 $ Anytown, MI 48104
+description: Very odd
+facsimileTelephoneNumber: +1 313 555 7557
+telephoneNumber: +1 313 555 8343
+mail: gjensen@mailgw.example.com
+homePhone: +1 313 555 8844
+testTime: 20050304001801.234Z
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+uniqueMember: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc
+ =com
+uniqueMember: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc
+ =com
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+drink: Orange Juice
+sn: Jones
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+
+dn: cn=Manager,dc=example,dc=com
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 1
+gidNumber: -1
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+
diff --git a/tests/data/modrdn.out.provider.0 b/tests/data/modrdn.out.provider.0
new file mode 100644
index 0000000..77105b8
--- /dev/null
+++ b/tests/data/modrdn.out.provider.0
@@ -0,0 +1,411 @@
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=James A Jones II,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+objectClass: OpenLDAPperson
+cn: James Jones
+cn: Jim Jones
+cn: James A Jones II
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+
+dn: cn=James A Jones III,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+cn: James A Jones III
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+
+dn: cn=Manager,dc=example,dc=com
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+
diff --git a/tests/data/modrdn.out.provider.1 b/tests/data/modrdn.out.provider.1
new file mode 100644
index 0000000..5466e51
--- /dev/null
+++ b/tests/data/modrdn.out.provider.1
@@ -0,0 +1,20 @@
+dn: cn=James A Jones III,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+cn: James A Jones III
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+
diff --git a/tests/data/modrdn.out.provider.2 b/tests/data/modrdn.out.provider.2
new file mode 100644
index 0000000..057ec93
--- /dev/null
+++ b/tests/data/modrdn.out.provider.2
@@ -0,0 +1,19 @@
+dn: cn=James A Jones II,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+objectClass: OpenLDAPperson
+cn: James Jones
+cn: Jim Jones
+cn: James A Jones II
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+
diff --git a/tests/data/modrdn.out.provider.3 b/tests/data/modrdn.out.provider.3
new file mode 100644
index 0000000..67299dc
--- /dev/null
+++ b/tests/data/modrdn.out.provider.3
@@ -0,0 +1,19 @@
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+
diff --git a/tests/data/monitor1.out b/tests/data/monitor1.out
new file mode 100644
index 0000000..5919248
--- /dev/null
+++ b/tests/data/monitor1.out
@@ -0,0 +1,31 @@
+dn: cn=Connection 1001,cn=Connections,cn=Monitor
+structuralObjectClass: monitorConnection
+monitorConnectionProtocol: 3
+monitorConnectionOpsReceived: 2
+monitorConnectionOpsExecuting: 1
+monitorConnectionOpsPending: 0
+monitorConnectionOpsCompleted: 1
+monitorConnectionGet: 2
+monitorConnectionRead: 2
+monitorConnectionWrite: 0
+monitorConnectionMask: rx
+monitorConnectionListener: ldap://localhost:@PORT1@/
+monitorConnectionLocalAddress: IP=127.0.0.1:@PORT1@
+entryDN: cn=Connection 1001,cn=Connections,cn=Monitor
+
+dn: cn=Connections,cn=Monitor
+structuralObjectClass: monitorContainer
+entryDN: cn=Connections,cn=Monitor
+
+dn: cn=Current,cn=Connections,cn=Monitor
+structuralObjectClass: monitorCounterObject
+entryDN: cn=Current,cn=Connections,cn=Monitor
+
+dn: cn=Max File Descriptors,cn=Connections,cn=Monitor
+structuralObjectClass: monitorCounterObject
+entryDN: cn=Max File Descriptors,cn=Connections,cn=Monitor
+
+dn: cn=Total,cn=Connections,cn=Monitor
+structuralObjectClass: monitorCounterObject
+entryDN: cn=Total,cn=Connections,cn=Monitor
+
diff --git a/tests/data/monitor2.out b/tests/data/monitor2.out
new file mode 100644
index 0000000..20209d1
--- /dev/null
+++ b/tests/data/monitor2.out
@@ -0,0 +1,37 @@
+dn: cn=Database 0,cn=Databases,cn=Monitor
+structuralObjectClass: monitoredObject
+monitorIsShadow: FALSE
+namingContexts: cn=config
+readOnly: FALSE
+entryDN: cn=Database 0,cn=Databases,cn=Monitor
+
+dn: cn=Database 1,cn=Databases,cn=Monitor
+structuralObjectClass: monitoredObject
+monitorIsShadow: FALSE
+namingContexts: o=OpenLDAP Project,l=Internet
+readOnly: FALSE
+entryDN: cn=Database 1,cn=Databases,cn=Monitor
+
+dn: cn=Database 2,cn=Databases,cn=Monitor
+structuralObjectClass: monitoredObject
+monitorIsShadow: FALSE
+monitorContext: cn=Monitor
+readOnly: FALSE
+entryDN: cn=Database 2,cn=Databases,cn=Monitor
+
+dn: cn=Databases,cn=Monitor
+structuralObjectClass: monitorContainer
+readOnly: FALSE
+namingContexts:
+namingContexts: cn=config
+namingContexts: o=OpenLDAP Project,l=Internet
+monitorContext: cn=Monitor
+entryDN: cn=Databases,cn=Monitor
+
+dn: cn=Frontend,cn=Databases,cn=Monitor
+structuralObjectClass: monitoredObject
+monitorIsShadow: FALSE
+namingContexts:
+readOnly: FALSE
+entryDN: cn=Frontend,cn=Databases,cn=Monitor
+
diff --git a/tests/data/monitor3.out b/tests/data/monitor3.out
new file mode 100644
index 0000000..b40e98b
--- /dev/null
+++ b/tests/data/monitor3.out
@@ -0,0 +1,15 @@
+dn: cn=Entries,cn=Statistics,cn=Monitor
+structuralObjectClass: monitorCounterObject
+monitorCounter: 12
+entryDN: cn=Entries,cn=Statistics,cn=Monitor
+
+dn: cn=PDU,cn=Statistics,cn=Monitor
+structuralObjectClass: monitorCounterObject
+monitorCounter: 18
+entryDN: cn=PDU,cn=Statistics,cn=Monitor
+
+dn: cn=Referrals,cn=Statistics,cn=Monitor
+structuralObjectClass: monitorCounterObject
+monitorCounter: 0
+entryDN: cn=Referrals,cn=Statistics,cn=Monitor
+
diff --git a/tests/data/monitor4.out b/tests/data/monitor4.out
new file mode 100644
index 0000000..f6e82d3
--- /dev/null
+++ b/tests/data/monitor4.out
@@ -0,0 +1,66 @@
+dn: cn=Abandon,cn=Operations,cn=Monitor
+structuralObjectClass: monitorOperation
+monitorOpInitiated: 0
+monitorOpCompleted: 0
+entryDN: cn=Abandon,cn=Operations,cn=Monitor
+
+dn: cn=Add,cn=Operations,cn=Monitor
+structuralObjectClass: monitorOperation
+monitorOpInitiated: 0
+monitorOpCompleted: 0
+entryDN: cn=Add,cn=Operations,cn=Monitor
+
+dn: cn=Bind,cn=Operations,cn=Monitor
+structuralObjectClass: monitorOperation
+monitorOpInitiated: 5
+monitorOpCompleted: 5
+entryDN: cn=Bind,cn=Operations,cn=Monitor
+
+dn: cn=Compare,cn=Operations,cn=Monitor
+structuralObjectClass: monitorOperation
+monitorOpInitiated: 0
+monitorOpCompleted: 0
+entryDN: cn=Compare,cn=Operations,cn=Monitor
+
+dn: cn=Delete,cn=Operations,cn=Monitor
+structuralObjectClass: monitorOperation
+monitorOpInitiated: 0
+monitorOpCompleted: 0
+entryDN: cn=Delete,cn=Operations,cn=Monitor
+
+dn: cn=Extended,cn=Operations,cn=Monitor
+structuralObjectClass: monitorOperation
+monitorOpInitiated: 0
+monitorOpCompleted: 0
+entryDN: cn=Extended,cn=Operations,cn=Monitor
+
+dn: cn=Modify,cn=Operations,cn=Monitor
+structuralObjectClass: monitorOperation
+monitorOpInitiated: 0
+monitorOpCompleted: 0
+entryDN: cn=Modify,cn=Operations,cn=Monitor
+
+dn: cn=Modrdn,cn=Operations,cn=Monitor
+structuralObjectClass: monitorOperation
+monitorOpInitiated: 0
+monitorOpCompleted: 0
+entryDN: cn=Modrdn,cn=Operations,cn=Monitor
+
+dn: cn=Operations,cn=Monitor
+structuralObjectClass: monitorContainer
+monitorOpInitiated: 14
+monitorOpCompleted: 13
+entryDN: cn=Operations,cn=Monitor
+
+dn: cn=Search,cn=Operations,cn=Monitor
+structuralObjectClass: monitorOperation
+monitorOpInitiated: 5
+monitorOpCompleted: 4
+entryDN: cn=Search,cn=Operations,cn=Monitor
+
+dn: cn=Unbind,cn=Operations,cn=Monitor
+structuralObjectClass: monitorOperation
+monitorOpInitiated: 4
+monitorOpCompleted: 4
+entryDN: cn=Unbind,cn=Operations,cn=Monitor
+
diff --git a/tests/data/ndb.conf b/tests/data/ndb.conf
new file mode 100644
index 0000000..1ccdf63
--- /dev/null
+++ b/tests/data/ndb.conf
@@ -0,0 +1,23 @@
+# back-ndb boilerplate config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+dbuser root
+dbhost localhost
+dbconnect 127.0.0.1
+dbsocket /tmp/mysql.sock
+attrset extensibleObject uidNumber,gidNumber
+attrblob description
+index cn
+#index sn
diff --git a/tests/data/nis_sample.ldif b/tests/data/nis_sample.ldif
new file mode 100644
index 0000000..2f4e22b
--- /dev/null
+++ b/tests/data/nis_sample.ldif
@@ -0,0 +1,8092 @@
+dn: o=SGI, c=US
+o: SGI
+objectclass: organization
+objectclass: top
+
+dn: cn=sys, o=SGI, c=US
+cn: sys
+userPassword:
+gidNumber: 0
+memberUid: root
+memberUid: bin
+memberUid: sys
+memberUid: adm
+objectclass: posixGroup
+objectclass: top
+
+dn: cn=root, o=SGI, c=US
+cn: root
+userPassword:
+gidNumber: 0
+memberUid: root
+objectclass: posixGroup
+objectclass: top
+
+dn: cn=daemon, o=SGI, c=US
+cn: daemon
+userPassword:
+gidNumber: 1
+memberUid: root
+memberUid: daemon
+objectclass: posixGroup
+objectclass: top
+
+dn: cn=bin, o=SGI, c=US
+cn: bin
+userPassword:
+gidNumber: 2
+memberUid: root
+memberUid: bin
+memberUid: daemon
+objectclass: posixGroup
+objectclass: top
+
+dn: cn=adm, o=SGI, c=US
+cn: adm
+userPassword:
+gidNumber: 3
+memberUid: root
+memberUid: adm
+memberUid: daemon
+objectclass: posixGroup
+objectclass: top
+
+dn: cn=mail, o=SGI, c=US
+cn: mail
+userPassword:
+gidNumber: 4
+memberUid: root
+objectclass: posixGroup
+objectclass: top
+
+dn: cn=uucp, o=SGI, c=US
+cn: uucp
+userPassword:
+gidNumber: 5
+memberUid: uucp
+objectclass: posixGroup
+objectclass: top
+
+dn: cn=rje, o=SGI, c=US
+cn: rje
+userPassword:
+gidNumber: 8
+objectclass: posixGroup
+objectclass: top
+
+dn: cn=lp, o=SGI, c=US
+cn: lp
+userPassword: *
+gidNumber: 9
+objectclass: posixGroup
+objectclass: top
+
+dn: cn=nuucp, o=SGI, c=US
+cn: nuucp
+userPassword:
+gidNumber: 10
+memberUid: nuucp
+objectclass: posixGroup
+objectclass: top
+
+dn: cn=user, o=SGI, c=US
+cn: user
+userPassword:
+gidNumber: 20
+objectclass: posixGroup
+objectclass: top
+
+dn: cn=CMWlogin, o=SGI, c=US
+cn: CMWlogin
+userPassword:
+gidNumber: 994
+objectclass: posixGroup
+objectclass: top
+
+dn: cn=other, o=SGI, c=US
+cn: other
+userPassword:
+gidNumber: 995
+objectclass: posixGroup
+objectclass: top
+
+dn: cn=demos, o=SGI, c=US
+cn: demos
+userPassword: *
+gidNumber: 997
+objectclass: posixGroup
+objectclass: top
+
+dn: cn=guest, o=SGI, c=US
+cn: guest
+userPassword: *
+gidNumber: 998
+objectclass: posixGroup
+objectclass: top
+
+dn: cn=nobody, o=SGI, c=US
+cn: nobody
+userPassword: *
+gidNumber: 60001
+objectclass: posixGroup
+objectclass: top
+
+dn: cn=mt-everest, o=SGI, c=US
+cn: mt-everest
+cn: mt-everest.engr.sgi.com
+cn: mt-everest
+ipHostNumber: 150.166.97.201
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=IRIS, o=SGI, c=US
+cn: IRIS
+cn: IRIS
+ipHostNumber: 192.0.2.1
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=localhost, o=SGI, c=US
+cn: localhost
+cn: localhost
+ipHostNumber: 127.0.0.1
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=all-systems, o=SGI, c=US
+cn: all-systems
+cn: all-systems.mcast.net
+ipHostNumber: 224.0.0.1
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=all-routers, o=SGI, c=US
+cn: all-routers
+cn: all-routers.mcast.net
+ipHostNumber: 224.0.0.2
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=dvmrp, o=SGI, c=US
+cn: dvmrp
+cn: dvmrp.mcast.net
+ipHostNumber: 224.0.0.4
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=ospf-all, o=SGI, c=US
+cn: ospf-all
+cn: ospf-all.mcast.net
+ipHostNumber: 224.0.0.5
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=ospf-dsig, o=SGI, c=US
+cn: ospf-dsig
+cn: ospf-dsig.mcast.net
+ipHostNumber: 224.0.0.6
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=ntp, o=SGI, c=US
+cn: ntp
+cn: ntp.mcast.net
+ipHostNumber: 224.0.1.1
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=sgi-dog, o=SGI, c=US
+cn: sgi-dog
+cn: sgi-dog.mcast.net
+ipHostNumber: 224.0.1.2
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=rwhod, o=SGI, c=US
+cn: rwhod
+cn: rwhod.mcast.net
+ipHostNumber: 224.0.1.3
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=rwho, o=SGI, c=US
+cn: rwho
+cn: rwho.mcast.net
+ipHostNumber: 224.0.2.1
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=sun-rpc, o=SGI, c=US
+cn: sun-rpc
+cn: sun-rpc.mcast.net
+ipHostNumber: 224.0.2.2
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=localhost, o=SGI, c=US
+cn: localhost
+cn: localhost
+cn: localhost.engr.sgi.com
+cn: loghost
+ipHostNumber: 127.1
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=mcast, o=SGI, c=US
+cn: mcast
+cn: mcast.
+ipHostNumber: 224.0.0.0
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=allhosts-mcast, o=SGI, c=US
+cn: allhosts-mcast
+cn: allhosts-mcast.
+ipHostNumber: 224.0.0.1
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=allgates-mcast, o=SGI, c=US
+cn: allgates-mcast
+cn: allgates-mcast.
+ipHostNumber: 224.0.0.2
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=dvmrp-mcast, o=SGI, c=US
+cn: dvmrp-mcast
+cn: dvmrp-mcast.
+ipHostNumber: 224.0.0.4
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=ospf-all-routers-mcast, o=SGI, c=US
+cn: ospf-all-routers-mcast
+cn: ospf-all-routers-mcast.
+ipHostNumber: 224.0.0.5
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=ospf-desi-routers-mcast, o=SGI, c=US
+cn: ospf-desi-routers-mcast
+cn: ospf-desi-routers-mcast.
+ipHostNumber: 224.0.0.6
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=ntp-mcast, o=SGI, c=US
+cn: ntp-mcast
+cn: ntp-mcast.
+ipHostNumber: 224.0.1.1
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=sgi-dog-mcast, o=SGI, c=US
+cn: sgi-dog-mcast
+cn: sgi-dog-mcast.
+ipHostNumber: 224.0.1.2
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=rwhod-mcast, o=SGI, c=US
+cn: rwhod-mcast
+cn: rwhod-mcast.
+ipHostNumber: 224.0.1.3
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=rwho-mcast, o=SGI, c=US
+cn: rwho-mcast
+cn: rwho-mcast.
+ipHostNumber: 224.0.2.1
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=pmap-mcast, o=SGI, c=US
+cn: pmap-mcast
+cn: pmap-mcast.
+ipHostNumber: 224.0.2.2
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=fddi-odin, o=SGI, c=US
+cn: fddi-odin
+cn: fddi-odin.corp.sgi.com
+cn: fddi-odin
+cn: relay.sgi.com
+cn: oni
+cn: sgihub
+ipHostNumber: 198.29.75.194
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=sgigate, o=SGI, c=US
+cn: sgigate
+cn: sgigate.sgi.com
+cn: socks-proxy-server.sgi.com
+cn: sgigate
+cn: socks-proxy
+cn: socks
+ipHostNumber: 198.29.75.75
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=odin, o=SGI, c=US
+cn: odin
+cn: odin.corp.sgi.com
+cn: odin
+cn: gate-odin
+ipHostNumber: 192.26.51.194
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=relay, o=SGI, c=US
+cn: relay
+cn: relay.esd.sgi.com
+cn: ares
+cn: esd
+ipHostNumber: 130.62.72.10
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=rock, o=SGI, c=US
+cn: rock
+cn: rock.csd.sgi.com
+cn: csd.sgi.com
+cn: relay.csd.sgi.com
+cn: rock
+ipHostNumber: 150.166.101.10
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=stiletto, o=SGI, c=US
+cn: stiletto
+cn: stiletto
+ipHostNumber: 150.166.42.26
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=stiletto, o=SGI, c=US
+cn: stiletto
+cn: stiletto.engr.sgi.com
+cn: stiletto
+ipHostNumber: 150.166.42.26
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=lhola, o=SGI, c=US
+cn: lhola
+cn: lhola.engr.sgi.com
+cn: lhola
+ipHostNumber: 150.166.75.55
+objectclass: ipHost
+objectclass: device
+objectclass: top
+
+dn: cn=dhcp-166-75-76, o=SGI, c=US
+cn: dhcp-166-75-76
+cn: dhcp-166-75-76.engr.sgi.com
+cn: dhcp-166-75-76
+macAddress: 8:0:69:2:ed:b3
+ipHostNumber: 150.166.75.76
+objectclass: ipHost
+objectclass: device
+objectclass: top
+objectclass: ieee802Device
+
+dn: cn=loopback, o=SGI, c=US
+cn: loopback
+cn: loopback.sgi.com
+ipNetworkNumber: 127
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=sgicust, o=SGI, c=US
+cn: sgicust
+ipNetworkNumber: 192.26.50
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=bacbone, o=SGI, c=US
+cn: bacbone
+ipNetworkNumber: 192.26.51
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2, o=SGI, c=US
+cn: b2
+ipNetworkNumber: 192.26.52
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b4-esdvt-fddi-test-net, o=SGI, c=US
+cn: b4-esdvt-fddi-test-net
+ipNetworkNumber: 192.26.53
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b3u-engr-slip, o=SGI, c=US
+cn: b3u-engr-slip
+ipNetworkNumber: 192.26.54
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2u-isdn-fddibackbone, o=SGI, c=US
+cn: b2u-isdn-fddibackbone
+ipNetworkNumber: 192.26.55
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2u-isdn-net, o=SGI, c=US
+cn: b2u-isdn-net
+ipNetworkNumber: 192.26.56
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b4, o=SGI, c=US
+cn: b4
+ipNetworkNumber: 192.26.57
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b3u-ffdi-lab, o=SGI, c=US
+cn: b3u-ffdi-lab
+ipNetworkNumber: 192.26.59
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2-gandalf, o=SGI, c=US
+cn: b2-gandalf
+ipNetworkNumber: 192.26.60
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9U, o=SGI, c=US
+cn: b9U
+ipNetworkNumber: 192.26.61
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-vis-sim, o=SGI, c=US
+cn: b7l-vis-sim
+ipNetworkNumber: 192.26.62
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9L-rel, o=SGI, c=US
+cn: b9L-rel
+ipNetworkNumber: 192.26.63
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8-ofc, o=SGI, c=US
+cn: b8-ofc
+ipNetworkNumber: 192.26.65
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b12-mfg-lab, o=SGI, c=US
+cn: b12-mfg-lab
+ipNetworkNumber: 192.26.66
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-psd-sw, o=SGI, c=US
+cn: b1-psd-sw
+ipNetworkNumber: 192.26.67
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9L-comp-ffdi, o=SGI, c=US
+cn: b9L-comp-ffdi
+ipNetworkNumber: 192.26.68
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wpd-dless, o=SGI, c=US
+cn: wpd-dless
+ipNetworkNumber: 192.26.69
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2L-VSG-staff, o=SGI, c=US
+cn: b2L-VSG-staff
+ipNetworkNumber: 192.26.70
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2u-nei2, o=SGI, c=US
+cn: b2u-nei2
+ipNetworkNumber: 192.26.71
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7u-gfxsw, o=SGI, c=US
+cn: b7u-gfxsw
+ipNetworkNumber: 192.26.72
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7u-gfxhw, o=SGI, c=US
+cn: b7u-gfxhw
+ipNetworkNumber: 192.26.73
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9l-ids, o=SGI, c=US
+cn: b9l-ids
+ipNetworkNumber: 192.26.74
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9u-ng, o=SGI, c=US
+cn: b9u-ng
+ipNetworkNumber: 192.26.75
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9l-pfng1, o=SGI, c=US
+cn: b9l-pfng1
+ipNetworkNumber: 192.26.76
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b789atm, o=SGI, c=US
+cn: b789atm
+ipNetworkNumber: 192.26.77
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wpd-slip, o=SGI, c=US
+cn: wpd-slip
+ipNetworkNumber: 192.26.78
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9l-pubs, o=SGI, c=US
+cn: b9l-pubs
+ipNetworkNumber: 192.26.79
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=engr-fddi, o=SGI, c=US
+cn: engr-fddi
+ipNetworkNumber: 192.26.80
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-dms, o=SGI, c=US
+cn: b1-dms
+ipNetworkNumber: 192.26.81
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-os, o=SGI, c=US
+cn: b7l-os
+cn: sgi48-150.sgi.com
+ipNetworkNumber: 192.48.150
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=show, o=SGI, c=US
+cn: show
+ipNetworkNumber: 192.26.82
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=timewarner-fsn, o=SGI, c=US
+cn: timewarner-fsn
+ipNetworkNumber: 192.48.146
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=vtel-mcast-net, o=SGI, c=US
+cn: vtel-mcast-net
+ipNetworkNumber: 192.48.147
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b24u-lab, o=SGI, c=US
+cn: b24u-lab
+ipNetworkNumber: 192.48.148
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wpd-fddi2, o=SGI, c=US
+cn: wpd-fddi2
+ipNetworkNumber: 192.48.149
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b24u-lab, o=SGI, c=US
+cn: b24u-lab
+ipNetworkNumber: 192.48.151
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-polevault, o=SGI, c=US
+cn: b1-polevault
+ipNetworkNumber: 192.48.152
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=barrnet, o=SGI, c=US
+cn: barrnet
+ipNetworkNumber: 192.48.153
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b3u-eisa-lab, o=SGI, c=US
+cn: b3u-eisa-lab
+ipNetworkNumber: 192.48.154
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=hippi-net, o=SGI, c=US
+cn: hippi-net
+ipNetworkNumber: 192.48.155
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=munich-support, o=SGI, c=US
+cn: munich-support
+ipNetworkNumber: 192.48.156
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=boston, o=SGI, c=US
+cn: boston
+ipNetworkNumber: 192.48.157
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9L-pfng3, o=SGI, c=US
+cn: b9L-pfng3
+cn: sgi44
+ipNetworkNumber: 192.48.158
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9L-pfng4, o=SGI, c=US
+cn: b9L-pfng4
+cn: sgi45
+ipNetworkNumber: 192.48.159
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9L-pfng5, o=SGI, c=US
+cn: b9L-pfng5
+cn: sgi46
+ipNetworkNumber: 192.48.160
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9L-pfng6, o=SGI, c=US
+cn: b9L-pfng6
+cn: sgi47
+ipNetworkNumber: 192.48.161
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9L-pfng7, o=SGI, c=US
+cn: b9L-pfng7
+cn: sgi48
+ipNetworkNumber: 192.48.162
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9L-pfng8, o=SGI, c=US
+cn: b9L-pfng8
+cn: sgi49
+ipNetworkNumber: 192.48.163
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9u-eprise, o=SGI, c=US
+cn: b9u-eprise
+cn: sgi48-164.sgi.com
+ipNetworkNumber: 192.48.164
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-oslab, o=SGI, c=US
+cn: b7l-oslab
+cn: sgi51
+ipNetworkNumber: 192.48.165
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=add-brds_lab1, o=SGI, c=US
+cn: add-brds_lab1
+ipNetworkNumber: 192.48.166
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8u-informix, o=SGI, c=US
+cn: b8u-informix
+ipNetworkNumber: 192.48.167
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-dm-fddi, o=SGI, c=US
+cn: b1-dm-fddi
+cn: sgi48-168
+ipNetworkNumber: 192.48.168
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9l-isdn, o=SGI, c=US
+cn: b9l-isdn
+cn: sgi48-169
+ipNetworkNumber: 192.48.169
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-teleconf, o=SGI, c=US
+cn: b1-teleconf
+cn: sgi48-170.sgi.com
+ipNetworkNumber: 192.48.170
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9u-tr3, o=SGI, c=US
+cn: b9u-tr3
+cn: sgi48-171.sgi.com
+ipNetworkNumber: 192.48.171
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7-slip, o=SGI, c=US
+cn: b7-slip
+cn: sgi48-172.sgi.com
+ipNetworkNumber: 192.48.172
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9l-sqa-fddi, o=SGI, c=US
+cn: b9l-sqa-fddi
+cn: sgi48-173.sgi.com
+ipNetworkNumber: 192.48.173
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-asd-fddi, o=SGI, c=US
+cn: b7l-asd-fddi
+cn: sgi48-174
+ipNetworkNumber: 192.48.174
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=sgi48-175, o=SGI, c=US
+cn: sgi48-175
+ipNetworkNumber: 192.48.175
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=sgi48-176, o=SGI, c=US
+cn: sgi48-176
+ipNetworkNumber: 192.48.176
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=sgi48-177, o=SGI, c=US
+cn: sgi48-177
+ipNetworkNumber: 192.48.177
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=sgi48-178, o=SGI, c=US
+cn: sgi48-178
+ipNetworkNumber: 192.48.178
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=sgi48-179, o=SGI, c=US
+cn: sgi48-179
+ipNetworkNumber: 192.48.179
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=engr-ppp-2, o=SGI, c=US
+cn: engr-ppp-2
+ipNetworkNumber: 192.48.180
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=engr-ppp-3, o=SGI, c=US
+cn: engr-ppp-3
+ipNetworkNumber: 192.48.181
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-tpc3, o=SGI, c=US
+cn: b7l-tpc3
+cn: sgi48-182
+ipNetworkNumber: 192.48.182
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-tpc4, o=SGI, c=US
+cn: b7l-tpc4
+cn: sgi48-183
+ipNetworkNumber: 192.48.183
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-tpc5, o=SGI, c=US
+cn: b7l-tpc5
+cn: sgi48-184
+ipNetworkNumber: 192.48.184
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-tpc6, o=SGI, c=US
+cn: b7l-tpc6
+cn: sgi48-185
+ipNetworkNumber: 192.48.185
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-tpc7, o=SGI, c=US
+cn: b7l-tpc7
+cn: sgi48-186
+ipNetworkNumber: 192.48.186
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-tpc8, o=SGI, c=US
+cn: b7l-tpc8
+cn: sgi48-187
+ipNetworkNumber: 192.48.187
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=bldr-fddi_1, o=SGI, c=US
+cn: bldr-fddi_1
+cn: sgi48-188.sgi.com
+ipNetworkNumber: 192.48.188
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=bldr-fddi_2, o=SGI, c=US
+cn: bldr-fddi_2
+cn: sgi48-189.sgi.com
+ipNetworkNumber: 192.48.189
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=bldr-ether, o=SGI, c=US
+cn: bldr-ether
+cn: sgi48-190.sgi.com
+ipNetworkNumber: 192.48.190
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-visual, o=SGI, c=US
+cn: b1-visual
+ipNetworkNumber: 192.48.191
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b3-esd-swlabs, o=SGI, c=US
+cn: b3-esd-swlabs
+ipNetworkNumber: 192.48.192
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b4-mfg-dvt, o=SGI, c=US
+cn: b4-mfg-dvt
+ipNetworkNumber: 192.48.194
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-pppnet, o=SGI, c=US
+cn: b9-pppnet
+ipNetworkNumber: 192.48.195
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-servers, o=SGI, c=US
+cn: b7l-servers
+ipNetworkNumber: 192.48.196
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=is-slipnet, o=SGI, c=US
+cn: is-slipnet
+ipNetworkNumber: 192.48.197
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b3-esd-sw, o=SGI, c=US
+cn: b3-esd-sw
+ipNetworkNumber: 192.48.198
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=ha-net, o=SGI, c=US
+cn: ha-net
+ipNetworkNumber: 192.48.199
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b21-awmtv-200, o=SGI, c=US
+cn: b21-awmtv-200
+ipNetworkNumber: 192.48.200
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b21-awmtv-201, o=SGI, c=US
+cn: b21-awmtv-201
+ipNetworkNumber: 192.48.201
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=voxproc, o=SGI, c=US
+cn: voxproc
+ipNetworkNumber: 192.48.202
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b4-csd-repair, o=SGI, c=US
+cn: b4-csd-repair
+ipNetworkNumber: 192.48.203
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b3-swltest, o=SGI, c=US
+cn: b3-swltest
+ipNetworkNumber: 192.48.204
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2l-system-lab, o=SGI, c=US
+cn: b2l-system-lab
+ipNetworkNumber: 192.48.205
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=nawaf-home_net, o=SGI, c=US
+cn: nawaf-home_net
+ipNetworkNumber: 192.48.206
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-wpd-qa1, o=SGI, c=US
+cn: b9-wpd-qa1
+ipNetworkNumber: 192.82.162
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=fddi-net, o=SGI, c=US
+cn: fddi-net
+ipNetworkNumber: 192.82.163
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=fddi-mezz, o=SGI, c=US
+cn: fddi-mezz
+ipNetworkNumber: 192.82.164
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8-b11_fddi-test, o=SGI, c=US
+cn: b8-b11_fddi-test
+ipNetworkNumber: 192.82.165
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-mooosehead, o=SGI, c=US
+cn: b1-mooosehead
+ipNetworkNumber: 192.82.166
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-ppp-slip, o=SGI, c=US
+cn: b7l-ppp-slip
+ipNetworkNumber: 192.82.167
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-mfg, o=SGI, c=US
+cn: b11-mfg
+ipNetworkNumber: 192.82.168
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-slip_ppp, o=SGI, c=US
+cn: b1-slip_ppp
+ipNetworkNumber: 192.82.169
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2-modem-net, o=SGI, c=US
+cn: b2-modem-net
+ipNetworkNumber: 192.82.170
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-tools, o=SGI, c=US
+cn: b1-tools
+ipNetworkNumber: 192.82.171
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-asd, o=SGI, c=US
+cn: b7l-asd
+ipNetworkNumber: 192.82.172
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=mel-net, o=SGI, c=US
+cn: mel-net
+ipNetworkNumber: 192.82.173
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b12-business, o=SGI, c=US
+cn: b12-business
+ipNetworkNumber: 192.82.174
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-east, o=SGI, c=US
+cn: b11-east
+ipNetworkNumber: 192.82.175
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-west, o=SGI, c=US
+cn: b11-west
+ipNetworkNumber: 192.82.176
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=china-web_test, o=SGI, c=US
+cn: china-web_test
+ipNetworkNumber: 192.82.177
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=aw-net1, o=SGI, c=US
+cn: aw-net1
+ipNetworkNumber: 192.82.178
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-firewall-testing, o=SGI, c=US
+cn: b1-firewall-testing
+ipNetworkNumber: 192.82.179
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b3-corptest, o=SGI, c=US
+cn: b3-corptest
+ipNetworkNumber: 192.82.180
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9u-networking, o=SGI, c=US
+cn: b9u-networking
+ipNetworkNumber: 192.82.181
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=uk, o=SGI, c=US
+cn: uk
+ipNetworkNumber: 192.82.182
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8l-lab, o=SGI, c=US
+cn: b8l-lab
+ipNetworkNumber: 192.82.183
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=isdn-net, o=SGI, c=US
+cn: isdn-net
+ipNetworkNumber: 192.82.184
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2-hwlab, o=SGI, c=US
+cn: b2-hwlab
+ipNetworkNumber: 192.82.185
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2u-labs, o=SGI, c=US
+cn: b2u-labs
+ipNetworkNumber: 192.82.186
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b4-csd, o=SGI, c=US
+cn: b4-csd
+ipNetworkNumber: 192.82.187
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-mfg-cubes, o=SGI, c=US
+cn: b11-mfg-cubes
+ipNetworkNumber: 192.82.188
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9l-media-lab, o=SGI, c=US
+cn: b9l-media-lab
+ipNetworkNumber: 192.82.189
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-ort-lab, o=SGI, c=US
+cn: b11-ort-lab
+ipNetworkNumber: 192.82.190
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=meriden, o=SGI, c=US
+cn: meriden
+ipNetworkNumber: 192.82.191
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-idslabdev, o=SGI, c=US
+cn: b9-idslabdev
+ipNetworkNumber: 192.82.192
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=sylvain-siou-net, o=SGI, c=US
+cn: sylvain-siou-net
+ipNetworkNumber: 192.82.193
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7u-hwtest, o=SGI, c=US
+cn: b7u-hwtest
+ipNetworkNumber: 192.82.194
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-mktg-high_performance-lab, o=SGI, c=US
+cn: b7l-mktg-high_performance-lab
+ipNetworkNumber: 192.82.195
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wan-serial-routers, o=SGI, c=US
+cn: wan-serial-routers
+ipNetworkNumber: 192.82.196
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=gouda-net, o=SGI, c=US
+cn: gouda-net
+ipNetworkNumber: 192.82.197
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=xfs-slip, o=SGI, c=US
+cn: xfs-slip
+ipNetworkNumber: 192.82.198
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-ngs-net, o=SGI, c=US
+cn: b1-ngs-net
+ipNetworkNumber: 192.82.200
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=siapt, o=SGI, c=US
+cn: siapt
+ipNetworkNumber: 192.82.202
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-token_lab, o=SGI, c=US
+cn: b1-token_lab
+ipNetworkNumber: 192.82.203
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=asd-slipnet3, o=SGI, c=US
+cn: asd-slipnet3
+ipNetworkNumber: 192.82.204
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=newport-bch1-net, o=SGI, c=US
+cn: newport-bch1-net
+ipNetworkNumber: 192.82.205
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=newport-bch2-net, o=SGI, c=US
+cn: newport-bch2-net
+ipNetworkNumber: 192.82.206
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b12-esd, o=SGI, c=US
+cn: b12-esd
+ipNetworkNumber: 192.82.207
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=sgi-psi, o=SGI, c=US
+cn: sgi-psi
+ipNetworkNumber: 192.82.208
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2-100mb-net, o=SGI, c=US
+cn: b2-100mb-net
+ipNetworkNumber: 192.82.209
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b3-100mb-net, o=SGI, c=US
+cn: b3-100mb-net
+ipNetworkNumber: 192.82.210
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8u-mfg_engr, o=SGI, c=US
+cn: b8u-mfg_engr
+ipNetworkNumber: 192.82.211
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=vsg-esd, o=SGI, c=US
+cn: vsg-esd
+ipNetworkNumber: 192.102.96
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=mbus-fddi_1, o=SGI, c=US
+cn: mbus-fddi_1
+ipNetworkNumber: 192.102.98
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11a-endusr-net, o=SGI, c=US
+cn: b11a-endusr-net
+ipNetworkNumber: 192.102.99
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=is-test, o=SGI, c=US
+cn: is-test
+ipNetworkNumber: 192.102.100
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11b-endusr-net, o=SGI, c=US
+cn: b11b-endusr-net
+ipNetworkNumber: 192.102.101
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=tokyo-training1, o=SGI, c=US
+cn: tokyo-training1
+ipNetworkNumber: 192.102.102
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=tokyo-training2, o=SGI, c=US
+cn: tokyo-training2
+ipNetworkNumber: 192.102.103
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=kawasaki-agd, o=SGI, c=US
+cn: kawasaki-agd
+ipNetworkNumber: 192.102.104
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=mbus-fddi_2, o=SGI, c=US
+cn: mbus-fddi_2
+ipNetworkNumber: 192.102.105
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=hk-net, o=SGI, c=US
+cn: hk-net
+ipNetworkNumber: 192.102.106
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7u-hwlab, o=SGI, c=US
+cn: b7u-hwlab
+ipNetworkNumber: 192.102.107
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b6-brief, o=SGI, c=US
+cn: b6-brief
+ipNetworkNumber: 192.102.108
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=kawasaki-indy, o=SGI, c=US
+cn: kawasaki-indy
+ipNetworkNumber: 192.102.109
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11a-highend-1, o=SGI, c=US
+cn: b11a-highend-1
+ipNetworkNumber: 192.102.110
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11a-highend-2, o=SGI, c=US
+cn: b11a-highend-2
+ipNetworkNumber: 192.102.111
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b789hippi, o=SGI, c=US
+cn: b789hippi
+ipNetworkNumber: 192.102.112
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8-benchmark1, o=SGI, c=US
+cn: b8-benchmark1
+ipNetworkNumber: 192.102.114.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8-benchmark2, o=SGI, c=US
+cn: b8-benchmark2
+ipNetworkNumber: 192.102.115.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8-benchmark3, o=SGI, c=US
+cn: b8-benchmark3
+ipNetworkNumber: 192.102.116.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b3u-community1, o=SGI, c=US
+cn: b3u-community1
+ipNetworkNumber: 192.102.117
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b3u-community2, o=SGI, c=US
+cn: b3u-community2
+ipNetworkNumber: 192.102.118
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b3u-finnance-is, o=SGI, c=US
+cn: b3u-finnance-is
+ipNetworkNumber: 192.102.119
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8u-agd_lab, o=SGI, c=US
+cn: b8u-agd_lab
+ipNetworkNumber: 192.102.120
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-highend-mfg1, o=SGI, c=US
+cn: b11-highend-mfg1
+ipNetworkNumber: 192.102.122
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b6-demo-net, o=SGI, c=US
+cn: b6-demo-net
+ipNetworkNumber: 192.102.129
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11b-mfg-servernet-1, o=SGI, c=US
+cn: b11b-mfg-servernet-1
+ipNetworkNumber: 192.102.130
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-asd-net, o=SGI, c=US
+cn: b9-asd-net
+ipNetworkNumber: 192.102.131
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2-mfg-net, o=SGI, c=US
+cn: b2-mfg-net
+ipNetworkNumber: 192.102.132
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11b-mfg-servernet-2, o=SGI, c=US
+cn: b11b-mfg-servernet-2
+ipNetworkNumber: 192.102.133
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7u-syshw, o=SGI, c=US
+cn: b7u-syshw
+ipNetworkNumber: 192.102.135
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b4-csdlab, o=SGI, c=US
+cn: b4-csdlab
+ipNetworkNumber: 192.102.136
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b17u-cselabC, o=SGI, c=US
+cn: b17u-cselabC
+ipNetworkNumber: 192.102.137
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b15-websafe, o=SGI, c=US
+cn: b15-websafe
+ipNetworkNumber: 192.102.138
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b14l-engr, o=SGI, c=US
+cn: b14l-engr
+ipNetworkNumber: 192.102.141
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b14-upper, o=SGI, c=US
+cn: b14-upper
+ipNetworkNumber: 192.102.142
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b5u-finance, o=SGI, c=US
+cn: b5u-finance
+ipNetworkNumber: 192.102.143
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b14-dms1, o=SGI, c=US
+cn: b14-dms1
+ipNetworkNumber: 192.102.144
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-highend-mfg2, o=SGI, c=US
+cn: b11-highend-mfg2
+ipNetworkNumber: 192.111.1
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-totestack4, o=SGI, c=US
+cn: b11-totestack4
+ipNetworkNumber: 192.111.2
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-totestack5, o=SGI, c=US
+cn: b11-totestack5
+ipNetworkNumber: 192.111.3
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-totestack6, o=SGI, c=US
+cn: b11-totestack6
+ipNetworkNumber: 192.111.4
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8u-entrpse-mgmt, o=SGI, c=US
+cn: b8u-entrpse-mgmt
+ipNetworkNumber: 192.111.5
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-desktop2, o=SGI, c=US
+cn: b11-desktop2
+ipNetworkNumber: 192.111.6
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-desktop3, o=SGI, c=US
+cn: b11-desktop3
+ipNetworkNumber: 192.111.7
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-mfgsystest1, o=SGI, c=US
+cn: b11-mfgsystest1
+ipNetworkNumber: 192.111.8
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-datacollect, o=SGI, c=US
+cn: b11-datacollect
+ipNetworkNumber: 192.111.9
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-mips-mfg, o=SGI, c=US
+cn: b11-mips-mfg
+ipNetworkNumber: 192.111.10
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-OOBA, o=SGI, c=US
+cn: b11-OOBA
+ipNetworkNumber: 192.111.11
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-DCO, o=SGI, c=US
+cn: b11-DCO
+ipNetworkNumber: 192.111.12
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-dskcopy, o=SGI, c=US
+cn: b11-dskcopy
+ipNetworkNumber: 192.111.13
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b12-imsd1, o=SGI, c=US
+cn: b12-imsd1
+ipNetworkNumber: 192.111.14
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b12-imsd2, o=SGI, c=US
+cn: b12-imsd2
+ipNetworkNumber: 192.111.15
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b12-imsd3, o=SGI, c=US
+cn: b12-imsd3
+ipNetworkNumber: 192.111.16
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=dms-moose, o=SGI, c=US
+cn: dms-moose
+ipNetworkNumber: 192.111.17
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-video-lab, o=SGI, c=US
+cn: b7l-video-lab
+ipNetworkNumber: 192.111.18
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=corp-fddi, o=SGI, c=US
+cn: corp-fddi
+ipNetworkNumber: 192.111.21
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b16-engr-net, o=SGI, c=US
+cn: b16-engr-net
+ipNetworkNumber: 192.111.22
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-dss-hwd, o=SGI, c=US
+cn: b1-dss-hwd
+ipNetworkNumber: 192.111.23
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-dss-sft, o=SGI, c=US
+cn: b1-dss-sft
+ipNetworkNumber: 192.111.24
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-dss-mkt, o=SGI, c=US
+cn: b1-dss-mkt
+ipNetworkNumber: 192.111.25
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-dss-sftlab, o=SGI, c=US
+cn: b1-dss-sftlab
+ipNetworkNumber: 192.111.26
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-dss-hwdlab, o=SGI, c=US
+cn: b1-dss-hwdlab
+ipNetworkNumber: 192.111.27
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-dss-guinness, o=SGI, c=US
+cn: b1-dss-guinness
+ipNetworkNumber: 192.111.28
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=corp-isdn, o=SGI, c=US
+cn: corp-isdn
+ipNetworkNumber: 192.111.29
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=kodak-shutter, o=SGI, c=US
+cn: kodak-shutter
+ipNetworkNumber: 192.111.30
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=zursch-house, o=SGI, c=US
+cn: zursch-house
+ipNetworkNumber: 192.132.105
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b14u-mkt, o=SGI, c=US
+cn: b14u-mkt
+ipNetworkNumber: 192.132.108
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=atm-net1, o=SGI, c=US
+cn: atm-net1
+ipNetworkNumber: 192.132.109
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=atm-net2, o=SGI, c=US
+cn: atm-net2
+ipNetworkNumber: 192.132.110
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-corpdc, o=SGI, c=US
+cn: b1-corpdc
+ipNetworkNumber: 192.132.111
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=qa-net, o=SGI, c=US
+cn: qa-net
+ipNetworkNumber: 192.132.112
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=aw-net3, o=SGI, c=US
+cn: aw-net3
+ipNetworkNumber: 192.132.114
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8u-engr-lab, o=SGI, c=US
+cn: b8u-engr-lab
+ipNetworkNumber: 192.132.115
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=euro-mbus_1, o=SGI, c=US
+cn: euro-mbus_1
+ipNetworkNumber: 192.132.116
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=euro-mbus_2, o=SGI, c=US
+cn: euro-mbus_2
+ipNetworkNumber: 192.132.117
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=lsil-asd-dev, o=SGI, c=US
+cn: lsil-asd-dev
+ipNetworkNumber: 192.132.118
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neuchatel-slip, o=SGI, c=US
+cn: neuchatel-slip
+ipNetworkNumber: 192.132.119
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=dialup-isdn, o=SGI, c=US
+cn: dialup-isdn
+ipNetworkNumber: 192.132.120
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-dms3, o=SGI, c=US
+cn: b1-dms3
+ipNetworkNumber: 192.132.122
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-dss-cad, o=SGI, c=US
+cn: b1-dss-cad
+ipNetworkNumber: 192.132.127
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=cabmerwell, o=SGI, c=US
+cn: cabmerwell
+ipNetworkNumber: 192.132.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b21-studio-hippi, o=SGI, c=US
+cn: b21-studio-hippi
+ipNetworkNumber: 192.132.129
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=tw-wan-net, o=SGI, c=US
+cn: tw-wan-net
+ipNetworkNumber: 192.132.130
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=beijing, o=SGI, c=US
+cn: beijing
+ipNetworkNumber: 192.132.131
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=homefr, o=SGI, c=US
+cn: homefr
+ipNetworkNumber: 192.132.133
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-dms4, o=SGI, c=US
+cn: b1-dms4
+ipNetworkNumber: 192.132.134
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-mtcast-net, o=SGI, c=US
+cn: b1-mtcast-net
+ipNetworkNumber: 192.132.136
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=fire-wall-net, o=SGI, c=US
+cn: fire-wall-net
+ipNetworkNumber: 192.132.137
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=lfsh-home-isdn, o=SGI, c=US
+cn: lfsh-home-isdn
+ipNetworkNumber: 192.132.138
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2u-nei4-1, o=SGI, c=US
+cn: b2u-nei4-1
+ipNetworkNumber: 192.132.139
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2u-nei4-2, o=SGI, c=US
+cn: b2u-nei4-2
+ipNetworkNumber: 192.132.140
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=frame-lpbck, o=SGI, c=US
+cn: frame-lpbck
+ipNetworkNumber: 192.132.141
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2u-gandalf-142, o=SGI, c=US
+cn: b2u-gandalf-142
+cn: Net
+cn: 192.132.142
+ipNetworkNumber: ISDN
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=home-frame1, o=SGI, c=US
+cn: home-frame1
+ipNetworkNumber: 192.132.144
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-fdd-mzz, o=SGI, c=US
+cn: b11-fdd-mzz
+ipNetworkNumber: 192.132.146
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=corp-is, o=SGI, c=US
+cn: corp-is
+ipNetworkNumber: 192.132.148
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=munich-firewall, o=SGI, c=US
+cn: munich-firewall
+ipNetworkNumber: 192.132.149
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=taipei, o=SGI, c=US
+cn: taipei
+ipNetworkNumber: 192.72.19
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=taiwan, o=SGI, c=US
+cn: taiwan
+ipNetworkNumber: 192.132.150
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=sydney-tech_centre, o=SGI, c=US
+cn: sydney-tech_centre
+ipNetworkNumber: 192.132.151
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9U-atm, o=SGI, c=US
+cn: b9U-atm
+ipNetworkNumber: 192.132.153
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=is-fddi-test1, o=SGI, c=US
+cn: is-fddi-test1
+ipNetworkNumber: 192.132.155
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=is-fddi-test2, o=SGI, c=US
+cn: is-fddi-test2
+ipNetworkNumber: 192.132.156
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=is-fddi-test3, o=SGI, c=US
+cn: is-fddi-test3
+ipNetworkNumber: 192.132.157
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=is-fddi-test4, o=SGI, c=US
+cn: is-fddi-test4
+ipNetworkNumber: 192.132.158
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=is-fddi-test5, o=SGI, c=US
+cn: is-fddi-test5
+ipNetworkNumber: 192.132.159
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=is-fddi-test6, o=SGI, c=US
+cn: is-fddi-test6
+ipNetworkNumber: 192.132.160
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=is-fddi-test7, o=SGI, c=US
+cn: is-fddi-test7
+ipNetworkNumber: 192.132.161
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b4-evtlab, o=SGI, c=US
+cn: b4-evtlab
+ipNetworkNumber: 192.132.162
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanb8-wanb17, o=SGI, c=US
+cn: wanb8-wanb17
+ipNetworkNumber: 192.132.163
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=csd-insight, o=SGI, c=US
+cn: csd-insight
+ipNetworkNumber: 192.132.164
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b4-wan, o=SGI, c=US
+cn: b4-wan
+ipNetworkNumber: 192.132.165
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b4-csdindy, o=SGI, c=US
+cn: b4-csdindy
+ipNetworkNumber: 192.132.170
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2-hwlab2-temp, o=SGI, c=US
+cn: b2-hwlab2-temp
+ipNetworkNumber: 192.132.171
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-dss-os-1, o=SGI, c=US
+cn: b1-dss-os-1
+ipNetworkNumber: 192.132.173
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-dss-msig-1, o=SGI, c=US
+cn: b1-dss-msig-1
+ipNetworkNumber: 192.132.174
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-eng-lab, o=SGI, c=US
+cn: b11-eng-lab
+ipNetworkNumber: 192.132.175
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2u-b, o=SGI, c=US
+cn: b2u-b
+ipNetworkNumber: 192.132.176
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-token-ring, o=SGI, c=US
+cn: b11-token-ring
+cn: sgi132-177
+ipNetworkNumber: 192.132.177
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-guiness-lab, o=SGI, c=US
+cn: b1-guiness-lab
+ipNetworkNumber: 192.132.178
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=jwag-home-slip, o=SGI, c=US
+cn: jwag-home-slip
+ipNetworkNumber: 192.132.179
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=akmal-home-slip, o=SGI, c=US
+cn: akmal-home-slip
+ipNetworkNumber: 192.132.180
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=nasa-jsc, o=SGI, c=US
+cn: nasa-jsc
+ipNetworkNumber: 192.132.181
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8l-nsd, o=SGI, c=US
+cn: b8l-nsd
+ipNetworkNumber: 192.132.182
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-fddi-servers, o=SGI, c=US
+cn: b7l-fddi-servers
+ipNetworkNumber: 192.132.186
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=msdtest-fddi, o=SGI, c=US
+cn: msdtest-fddi
+ipNetworkNumber: 192.132.187
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-mfg-test, o=SGI, c=US
+cn: b11-mfg-test
+cn: sgi132-188
+ipNetworkNumber: 192.132.188
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=paris-secure3, o=SGI, c=US
+cn: paris-secure3
+ipNetworkNumber: 192.132.189
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2u-digital-media-lab, o=SGI, c=US
+cn: b2u-digital-media-lab
+ipNetworkNumber: 192.132.190
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7u-swlab, o=SGI, c=US
+cn: b7u-swlab
+ipNetworkNumber: 192.132.191
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=eng-spine, o=SGI, c=US
+cn: eng-spine
+ipNetworkNumber: 192.132.194
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-ddc, o=SGI, c=US
+cn: b11-ddc
+ipNetworkNumber: 192.132.195
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b12-totestack1, o=SGI, c=US
+cn: b12-totestack1
+ipNetworkNumber: 192.132.196
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b12-totestack2, o=SGI, c=US
+cn: b12-totestack2
+ipNetworkNumber: 192.132.197
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-mfgoven1, o=SGI, c=US
+cn: b11-mfgoven1
+ipNetworkNumber: 192.132.198
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=aw-tokyo, o=SGI, c=US
+cn: aw-tokyo
+ipNetworkNumber: 192.132.199
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b12-mfg, o=SGI, c=US
+cn: b12-mfg
+ipNetworkNumber: 192.132.204
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2-cddi-net, o=SGI, c=US
+cn: b2-cddi-net
+ipNetworkNumber: 198.29.64
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=aw-net2, o=SGI, c=US
+cn: aw-net2
+ipNetworkNumber: 198.29.65
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7-net, o=SGI, c=US
+cn: b7-net
+ipNetworkNumber: 198.29.66
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b14l-dms5, o=SGI, c=US
+cn: b14l-dms5
+ipNetworkNumber: 198.29.67
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=portlandwa, o=SGI, c=US
+cn: portlandwa
+ipNetworkNumber: 198.29.68
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-engr-net1, o=SGI, c=US
+cn: b9-engr-net1
+ipNetworkNumber: 198.29.69
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-ssd-benchlab_1, o=SGI, c=US
+cn: b7l-ssd-benchlab_1
+ipNetworkNumber: 198.29.71
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-mfgoven2, o=SGI, c=US
+cn: b11-mfgoven2
+ipNetworkNumber: 198.29.72
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-mfgsystest2, o=SGI, c=US
+cn: b11-mfgsystest2
+ipNetworkNumber: 198.29.73
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=burnham-local, o=SGI, c=US
+cn: burnham-local
+ipNetworkNumber: 198.29.74
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=dco-fddi, o=SGI, c=US
+cn: dco-fddi
+ipNetworkNumber: 198.29.75
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-engr-net2, o=SGI, c=US
+cn: b9-engr-net2
+ipNetworkNumber: 198.29.76
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b3-cddi-net, o=SGI, c=US
+cn: b3-cddi-net
+ipNetworkNumber: 198.29.77
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7u-lego-test1, o=SGI, c=US
+cn: b7u-lego-test1
+ipNetworkNumber: 198.29.78
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-networking-lab, o=SGI, c=US
+cn: b7l-networking-lab
+ipNetworkNumber: 198.29.79
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2u-gandalf-80-isdn, o=SGI, c=US
+cn: b2u-gandalf-80-isdn
+ipNetworkNumber: 198.29.80
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2u-gandalf-81-isdn, o=SGI, c=US
+cn: b2u-gandalf-81-isdn
+ipNetworkNumber: 198.29.81
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7u-syssw, o=SGI, c=US
+cn: b7u-syssw
+ipNetworkNumber: 198.29.82
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-asdlabs, o=SGI, c=US
+cn: b7l-asdlabs
+ipNetworkNumber: 198.29.83
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-compliance-lab, o=SGI, c=US
+cn: b7l-compliance-lab
+ipNetworkNumber: 198.29.84
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2u-gandalf-85-isdn, o=SGI, c=US
+cn: b2u-gandalf-85-isdn
+ipNetworkNumber: 198.29.85
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-engr-net3, o=SGI, c=US
+cn: b9-engr-net3
+ipNetworkNumber: 198.29.86
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=barna-internet, o=SGI, c=US
+cn: barna-internet
+ipNetworkNumber: 198.29.87
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2u-gandalf-88-isdn, o=SGI, c=US
+cn: b2u-gandalf-88-isdn
+ipNetworkNumber: 198.29.88
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=appletalk_net_9, o=SGI, c=US
+cn: appletalk_net_9
+ipNetworkNumber: 198.29.89
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=appletalk_net_10, o=SGI, c=US
+cn: appletalk_net_10
+ipNetworkNumber: 198.29.90
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2u-gandalf-91-isdn, o=SGI, c=US
+cn: b2u-gandalf-91-isdn
+ipNetworkNumber: 198.29.91
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2u-gandalf-92-isdn, o=SGI, c=US
+cn: b2u-gandalf-92-isdn
+ipNetworkNumber: 198.29.92
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2u-gandalf-93-isdn, o=SGI, c=US
+cn: b2u-gandalf-93-isdn
+ipNetworkNumber: 198.29.93
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=uktraining, o=SGI, c=US
+cn: uktraining
+ipNetworkNumber: 198.29.94
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=vbt-testnet, o=SGI, c=US
+cn: vbt-testnet
+ipNetworkNumber: 198.29.95
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b14-mcast, o=SGI, c=US
+cn: b14-mcast
+ipNetworkNumber: 198.29.96
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b14l-esd_mkt, o=SGI, c=US
+cn: b14l-esd_mkt
+ipNetworkNumber: 198.29.97
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b14u-apps1, o=SGI, c=US
+cn: b14u-apps1
+ipNetworkNumber: 198.29.98
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b14u-apps2, o=SGI, c=US
+cn: b14u-apps2
+ipNetworkNumber: 198.29.99
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-prod-eng1, o=SGI, c=US
+cn: b1-prod-eng1
+ipNetworkNumber: 198.29.100
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-prod-eng2, o=SGI, c=US
+cn: b1-prod-eng2
+ipNetworkNumber: 198.29.101
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-time-warner-3, o=SGI, c=US
+cn: b9-time-warner-3
+ipNetworkNumber: 198.29.102
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9u-time-warner, o=SGI, c=US
+cn: b9u-time-warner
+ipNetworkNumber: 198.29.103
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8l-prod-design, o=SGI, c=US
+cn: b8l-prod-design
+ipNetworkNumber: 198.29.104
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8l-mktg-net3, o=SGI, c=US
+cn: b8l-mktg-net3
+ipNetworkNumber: 198.29.106
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9l-enduser, o=SGI, c=US
+cn: b9l-enduser
+ipNetworkNumber: 198.29.108
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=partner-net, o=SGI, c=US
+cn: partner-net
+ipNetworkNumber: 198.29.110
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=solectron, o=SGI, c=US
+cn: solectron
+ipNetworkNumber: 198.29.111
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2-isdn-network, o=SGI, c=US
+cn: b2-isdn-network
+ipNetworkNumber: 198.29.112
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-lego-systest, o=SGI, c=US
+cn: b11-lego-systest
+ipNetworkNumber: 198.29.113
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-lego-ovens, o=SGI, c=US
+cn: b11-lego-ovens
+ipNetworkNumber: 198.29.114
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-time-warner-4, o=SGI, c=US
+cn: b9-time-warner-4
+ipNetworkNumber: 198.29.115
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=paris-secure1, o=SGI, c=US
+cn: paris-secure1
+ipNetworkNumber: 198.29.116
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=paris-secure2, o=SGI, c=US
+cn: paris-secure2
+ipNetworkNumber: 198.29.117
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2-b4backbone, o=SGI, c=US
+cn: b2-b4backbone
+ipNetworkNumber: 198.29.118
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=home-frame2, o=SGI, c=US
+cn: home-frame2
+ipNetworkNumber: 198.29.119
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=add-brds_lab2, o=SGI, c=US
+cn: add-brds_lab2
+ipNetworkNumber: 198.29.120
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-gauntlet, o=SGI, c=US
+cn: b1-gauntlet
+ipNetworkNumber: 198.29.121
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-dms-fddi, o=SGI, c=US
+cn: b1-dms-fddi
+ipNetworkNumber: 198.29.122
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b14l-nafo-lab, o=SGI, c=US
+cn: b14l-nafo-lab
+ipNetworkNumber: 198.29.124
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-comp-lab-a, o=SGI, c=US
+cn: b11-comp-lab-a
+ipNetworkNumber: 198.29.125
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-comp-lab-b, o=SGI, c=US
+cn: b11-comp-lab-b
+ipNetworkNumber: 198.29.126
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-comp-lab-c, o=SGI, c=US
+cn: b11-comp-lab-c
+ipNetworkNumber: 198.29.127
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-wpdlab1, o=SGI, c=US
+cn: b9-wpdlab1
+ipNetworkNumber: 199.74.33
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-wpdlab2, o=SGI, c=US
+cn: b9-wpdlab2
+ipNetworkNumber: 199.74.34
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-wpdlab3, o=SGI, c=US
+cn: b9-wpdlab3
+ipNetworkNumber: 199.74.35
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-wpdlab4, o=SGI, c=US
+cn: b9-wpdlab4
+ipNetworkNumber: 199.74.36
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-wpdlab5, o=SGI, c=US
+cn: b9-wpdlab5
+ipNetworkNumber: 199.74.37
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-wpdfddi3, o=SGI, c=US
+cn: b9-wpdfddi3
+ipNetworkNumber: 199.74.38
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-wpdfddi1, o=SGI, c=US
+cn: b9-wpdfddi1
+ipNetworkNumber: 199.74.39
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-wpdfddi2, o=SGI, c=US
+cn: b9-wpdfddi2
+ipNetworkNumber: 199.74.40
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=la-pri_hub, o=SGI, c=US
+cn: la-pri_hub
+ipNetworkNumber: 199.74.41
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=rpa-mtview-serial, o=SGI, c=US
+cn: rpa-mtview-serial
+ipNetworkNumber: 199.74.42
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b4-mfg-endusers, o=SGI, c=US
+cn: b4-mfg-endusers
+ipNetworkNumber: 199.74.43
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b6u-corp-bc, o=SGI, c=US
+cn: b6u-corp-bc
+ipNetworkNumber: 199.74.44
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=isdn1, o=SGI, c=US
+cn: isdn1
+ipNetworkNumber: 199.74.46
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=isdn2, o=SGI, c=US
+cn: isdn2
+ipNetworkNumber: 199.74.47
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b6-fddi-corp, o=SGI, c=US
+cn: b6-fddi-corp
+ipNetworkNumber: 199.74.48
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b18_xplx-apptlk, o=SGI, c=US
+cn: b18_xplx-apptlk
+ipNetworkNumber: 199.74.49
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=ppp-isdn-network1, o=SGI, c=US
+cn: ppp-isdn-network1
+ipNetworkNumber: 199.74.51
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=ppp-isdn-network2, o=SGI, c=US
+cn: ppp-isdn-network2
+ipNetworkNumber: 199.74.52
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=dss-isdn, o=SGI, c=US
+cn: dss-isdn
+ipNetworkNumber: 199.74.53
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b3l-community1, o=SGI, c=US
+cn: b3l-community1
+ipNetworkNumber: 199.74.54
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b3l-community2, o=SGI, c=US
+cn: b3l-community2
+ipNetworkNumber: 199.74.56
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b3l-community3, o=SGI, c=US
+cn: b3l-community3
+ipNetworkNumber: 199.74.57
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=sf-studio3, o=SGI, c=US
+cn: sf-studio3
+ipNetworkNumber: 199.74.58
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=mbus-ether, o=SGI, c=US
+cn: mbus-ether
+ipNetworkNumber: 199.74.59
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=cole-weber, o=SGI, c=US
+cn: cole-weber
+ipNetworkNumber: 199.74.60
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=tre-nei1-61-cnet, o=SGI, c=US
+cn: tre-nei1-61-cnet
+ipNetworkNumber: 199.74.61
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=japan-external, o=SGI, c=US
+cn: japan-external
+ipNetworkNumber: 199.74.62
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=nsd-oracle, o=SGI, c=US
+cn: nsd-oracle
+ipNetworkNumber: 199.74.63
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-is, o=SGI, c=US
+cn: neu-is
+ipNetworkNumber: 155.11.1.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-1d1b, o=SGI, c=US
+cn: neu-1d1b
+ipNetworkNumber: 155.11.1.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-adm, o=SGI, c=US
+cn: neu-adm
+ipNetworkNumber: 155.11.2.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-1d2b, o=SGI, c=US
+cn: neu-1d2b
+ipNetworkNumber: 155.11.2.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-csd, o=SGI, c=US
+cn: neu-csd
+ipNetworkNumber: 155.11.3.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-1d3b, o=SGI, c=US
+cn: neu-1d3b
+ipNetworkNumber: 155.11.3.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-mktg, o=SGI, c=US
+cn: neu-mktg
+ipNetworkNumber: 155.11.4.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-1p4b, o=SGI, c=US
+cn: neu-1p4b
+ipNetworkNumber: 155.11.4.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-1p5b, o=SGI, c=US
+cn: neu-1p5b
+ipNetworkNumber: 155.11.5.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-1p5a, o=SGI, c=US
+cn: neu-1p5a
+ipNetworkNumber: 155.11.5.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-lab1, o=SGI, c=US
+cn: neu-lab1
+ipNetworkNumber: 155.11.6.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-lab2, o=SGI, c=US
+cn: neu-lab2
+ipNetworkNumber: 155.11.6.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-train1, o=SGI, c=US
+cn: neu-train1
+ipNetworkNumber: 155.11.7.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-train2, o=SGI, c=US
+cn: neu-train2
+ipNetworkNumber: 155.11.7.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-mfgtst, o=SGI, c=US
+cn: neu-mfgtst
+ipNetworkNumber: 155.11.8.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-1r8b, o=SGI, c=US
+cn: neu-1r8b
+ipNetworkNumber: 155.11.8.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-mfg1, o=SGI, c=US
+cn: neu-mfg1
+ipNetworkNumber: 155.11.9.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-1r9b, o=SGI, c=US
+cn: neu-1r9b
+ipNetworkNumber: 155.11.9.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-mfg2, o=SGI, c=US
+cn: neu-mfg2
+ipNetworkNumber: 155.11.10.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-itlcomm, o=SGI, c=US
+cn: neu-itlcomm
+ipNetworkNumber: 155.11.10.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=par-bb, o=SGI, c=US
+cn: par-bb
+ipNetworkNumber: 155.11.11.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=par-csd, o=SGI, c=US
+cn: par-csd
+ipNetworkNumber: 155.11.11.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=par-sales, o=SGI, c=US
+cn: par-sales
+ipNetworkNumber: 155.11.12.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=par-train, o=SGI, c=US
+cn: par-train
+ipNetworkNumber: 155.11.12.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=par-eng, o=SGI, c=US
+cn: par-eng
+ipNetworkNumber: 155.11.13.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=par-adm, o=SGI, c=US
+cn: par-adm
+ipNetworkNumber: 155.11.13.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=aix-net, o=SGI, c=US
+cn: aix-net
+ipNetworkNumber: 155.11.14.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=par-aix-ptp, o=SGI, c=US
+cn: par-aix-ptp
+ipNetworkNumber: 155.11.14.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=tou-net, o=SGI, c=US
+cn: tou-net
+ipNetworkNumber: 155.11.15.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=par-tou-ptp, o=SGI, c=US
+cn: par-tou-ptp
+ipNetworkNumber: 155.11.15.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=lyon-net, o=SGI, c=US
+cn: lyon-net
+ipNetworkNumber: 155.11.16.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=par-lyon-ptp, o=SGI, c=US
+cn: par-lyon-ptp
+ipNetworkNumber: 155.11.16.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=rennes-net, o=SGI, c=US
+cn: rennes-net
+ipNetworkNumber: 155.11.17.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=par-rennes-ptp, o=SGI, c=US
+cn: par-rennes-ptp
+ipNetworkNumber: 155.11.17.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=milan-net1, o=SGI, c=US
+cn: milan-net1
+ipNetworkNumber: 155.11.18.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=milan-net2, o=SGI, c=US
+cn: milan-net2
+ipNetworkNumber: 155.11.18.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=geneva-ptp, o=SGI, c=US
+cn: geneva-ptp
+ipNetworkNumber: 155.11.19.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=geneva-net, o=SGI, c=US
+cn: geneva-net
+ipNetworkNumber: 155.11.19.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=rome-net1, o=SGI, c=US
+cn: rome-net1
+ipNetworkNumber: 155.11.20.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=rome-net2, o=SGI, c=US
+cn: rome-net2
+ipNetworkNumber: 155.11.20.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-h-2511, o=SGI, c=US
+cn: neu-h-2511
+ipNetworkNumber: 155.11.21.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-blazer, o=SGI, c=US
+cn: neu-blazer
+ipNetworkNumber: 155.11.21.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=helsinki-net, o=SGI, c=US
+cn: helsinki-net
+ipNetworkNumber: 155.11.22.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=FREE-1, o=SGI, c=US
+cn: FREE-1
+ipNetworkNumber: 155.11.22.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=zurich-net, o=SGI, c=US
+cn: zurich-net
+ipNetworkNumber: 155.11.23.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-zurich-ptp, o=SGI, c=US
+cn: neu-zurich-ptp
+ipNetworkNumber: 155.11.23.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-1fddi, o=SGI, c=US
+cn: neu-1fddi
+ipNetworkNumber: 155.11.24.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-2fddi, o=SGI, c=US
+cn: neu-2fddi
+ipNetworkNumber: 155.11.24.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=london1, o=SGI, c=US
+cn: london1
+ipNetworkNumber: 155.11.25.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=london2, o=SGI, c=US
+cn: london2
+ipNetworkNumber: 155.11.25.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=falkirk1, o=SGI, c=US
+cn: falkirk1
+ipNetworkNumber: 155.11.26.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=falkirk2, o=SGI, c=US
+cn: falkirk2
+ipNetworkNumber: 155.11.26.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-wh1, o=SGI, c=US
+cn: reading-wh1
+ipNetworkNumber: 155.11.27.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-wh2, o=SGI, c=US
+cn: reading-wh2
+ipNetworkNumber: 155.11.27.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=demeern-net, o=SGI, c=US
+cn: demeern-net
+ipNetworkNumber: 155.11.28.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=demeern-net2, o=SGI, c=US
+cn: demeern-net2
+ipNetworkNumber: 155.11.28.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=demeern-net3, o=SGI, c=US
+cn: demeern-net3
+ipNetworkNumber: 155.11.29.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=demeern-net4, o=SGI, c=US
+cn: demeern-net4
+ipNetworkNumber: 155.11.29.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=lausanne1, o=SGI, c=US
+cn: lausanne1
+ipNetworkNumber: 155.11.30.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=lausanne2, o=SGI, c=US
+cn: lausanne2
+ipNetworkNumber: 155.11.30.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=stockholm-net, o=SGI, c=US
+cn: stockholm-net
+ipNetworkNumber: 155.11.31.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=stockholm-ptp, o=SGI, c=US
+cn: stockholm-ptp
+ipNetworkNumber: 155.11.31.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=budapest, o=SGI, c=US
+cn: budapest
+ipNetworkNumber: 155.11.32.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=budapest-ptp, o=SGI, c=US
+cn: budapest-ptp
+ipNetworkNumber: 155.11.32.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=FREE-2, o=SGI, c=US
+cn: FREE-2
+ipNetworkNumber: 155.11.33.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=FREE-3, o=SGI, c=US
+cn: FREE-3
+ipNetworkNumber: 155.11.33.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-mfg2a, o=SGI, c=US
+cn: neu-mfg2a
+ipNetworkNumber: 155.11.34.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-mfg2b, o=SGI, c=US
+cn: neu-mfg2b
+ipNetworkNumber: 155.11.34.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=lan-tst, o=SGI, c=US
+cn: lan-tst
+ipNetworkNumber: 155.11.35.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wan-tst, o=SGI, c=US
+cn: wan-tst
+ipNetworkNumber: 155.11.35.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=demeern-net7, o=SGI, c=US
+cn: demeern-net7
+ipNetworkNumber: 155.11.36.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=demeern-net8, o=SGI, c=US
+cn: demeern-net8
+ipNetworkNumber: 155.11.36.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=basel-net, o=SGI, c=US
+cn: basel-net
+ipNetworkNumber: 155.11.37.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=basel-ptp, o=SGI, c=US
+cn: basel-ptp
+ipNetworkNumber: 155.11.37.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=brno-net, o=SGI, c=US
+cn: brno-net
+ipNetworkNumber: 155.11.38.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=brno-ptp, o=SGI, c=US
+cn: brno-ptp
+ipNetworkNumber: 155.11.38.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=warsaw-net, o=SGI, c=US
+cn: warsaw-net
+ipNetworkNumber: 155.11.39.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=warsaw-ptp, o=SGI, c=US
+cn: warsaw-ptp
+ipNetworkNumber: 155.11.39.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=moscow, o=SGI, c=US
+cn: moscow
+ipNetworkNumber: 155.11.40.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=moscow-ptp, o=SGI, c=US
+cn: moscow-ptp
+ipNetworkNumber: 155.11.40.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=munich-1, o=SGI, c=US
+cn: munich-1
+ipNetworkNumber: 155.11.41.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=munich-2, o=SGI, c=US
+cn: munich-2
+ipNetworkNumber: 155.11.41.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=Karlsruhe-1, o=SGI, c=US
+cn: Karlsruhe-1
+ipNetworkNumber: 155.11.42.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=Karlsruhe-2, o=SGI, c=US
+cn: Karlsruhe-2
+ipNetworkNumber: 155.11.42.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=Cologne-1, o=SGI, c=US
+cn: Cologne-1
+ipNetworkNumber: 155.11.43.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=Cologne-2, o=SGI, c=US
+cn: Cologne-2
+ipNetworkNumber: 155.11.43.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=Berlin-1, o=SGI, c=US
+cn: Berlin-1
+ipNetworkNumber: 155.11.44.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=Berlin-2, o=SGI, c=US
+cn: Berlin-2
+ipNetworkNumber: 155.11.44.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=Hannover-1, o=SGI, c=US
+cn: Hannover-1
+ipNetworkNumber: 155.11.45.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=Hannover-2, o=SGI, c=US
+cn: Hannover-2
+ipNetworkNumber: 155.11.45.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=munich-3, o=SGI, c=US
+cn: munich-3
+ipNetworkNumber: 155.11.46.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=munich-4, o=SGI, c=US
+cn: munich-4
+ipNetworkNumber: 155.11.46.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=demeern-5, o=SGI, c=US
+cn: demeern-5
+ipNetworkNumber: 155.11.47.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=demeern, o=SGI, c=US
+cn: demeern
+ipNetworkNumber: 155.11.47.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=telaviv1, o=SGI, c=US
+cn: telaviv1
+ipNetworkNumber: 155.11.48.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=telaviv2, o=SGI, c=US
+cn: telaviv2
+ipNetworkNumber: 155.11.48.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=cort-tst1, o=SGI, c=US
+cn: cort-tst1
+ipNetworkNumber: 155.11.49.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=cort-tst2, o=SGI, c=US
+cn: cort-tst2
+ipNetworkNumber: 155.11.49.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=copen01, o=SGI, c=US
+cn: copen01
+ipNetworkNumber: 155.11.50.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=copen02, o=SGI, c=US
+cn: copen02
+ipNetworkNumber: 155.11.50.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=oslo01, o=SGI, c=US
+cn: oslo01
+ipNetworkNumber: 155.11.51.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=oslo02, o=SGI, c=US
+cn: oslo02
+ipNetworkNumber: 155.11.51.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=brussels, o=SGI, c=US
+cn: brussels
+ipNetworkNumber: 155.11.52.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=brussels_1, o=SGI, c=US
+cn: brussels_1
+ipNetworkNumber: 155.11.53.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=brussels_2, o=SGI, c=US
+cn: brussels_2
+ipNetworkNumber: 155.11.53.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=brussels_3, o=SGI, c=US
+cn: brussels_3
+ipNetworkNumber: 155.11.54.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=brussels_4, o=SGI, c=US
+cn: brussels_4
+ipNetworkNumber: 155.11.54.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=lausanne3, o=SGI, c=US
+cn: lausanne3
+ipNetworkNumber: 155.11.55.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=lausanne4, o=SGI, c=US
+cn: lausanne4
+ipNetworkNumber: 155.11.55.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=vienna1, o=SGI, c=US
+cn: vienna1
+ipNetworkNumber: 155.11.56.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=vienna2, o=SGI, c=US
+cn: vienna2
+ipNetworkNumber: 155.11.56.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=madrid1, o=SGI, c=US
+cn: madrid1
+ipNetworkNumber: 155.11.57.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=madrid2, o=SGI, c=US
+cn: madrid2
+ipNetworkNumber: 155.11.57.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=barcelona01, o=SGI, c=US
+cn: barcelona01
+ipNetworkNumber: 155.11.58.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=barcelona02, o=SGI, c=US
+cn: barcelona02
+ipNetworkNumber: 155.11.58.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=bahrain01, o=SGI, c=US
+cn: bahrain01
+ipNetworkNumber: 155.11.59.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=bahrain02, o=SGI, c=US
+cn: bahrain02
+ipNetworkNumber: 155.11.59.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=frankfurt1, o=SGI, c=US
+cn: frankfurt1
+ipNetworkNumber: 155.11.60.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=frankfurt2, o=SGI, c=US
+cn: frankfurt2
+ipNetworkNumber: 155.11.60.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=visual-land, o=SGI, c=US
+cn: visual-land
+ipNetworkNumber: 155.11.61.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=gland-wan, o=SGI, c=US
+cn: gland-wan
+ipNetworkNumber: 155.11.61.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=gothenburg1, o=SGI, c=US
+cn: gothenburg1
+ipNetworkNumber: 155.11.62.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=gothenburg2, o=SGI, c=US
+cn: gothenburg2
+ipNetworkNumber: 155.11.62.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=munich-5, o=SGI, c=US
+cn: munich-5
+ipNetworkNumber: 155.11.63.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=munich-6, o=SGI, c=US
+cn: munich-6
+ipNetworkNumber: 155.11.63.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=munich-7, o=SGI, c=US
+cn: munich-7
+ipNetworkNumber: 155.11.64.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=munich-8, o=SGI, c=US
+cn: munich-8
+ipNetworkNumber: 155.11.64.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=zurich-ppp1, o=SGI, c=US
+cn: zurich-ppp1
+ipNetworkNumber: 155.11.65.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=zurich-ppp2, o=SGI, c=US
+cn: zurich-ppp2
+ipNetworkNumber: 155.11.65.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=stavanger01, o=SGI, c=US
+cn: stavanger01
+ipNetworkNumber: 155.11.66.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=stavanger02, o=SGI, c=US
+cn: stavanger02
+ipNetworkNumber: 155.11.66.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=jerusalem1, o=SGI, c=US
+cn: jerusalem1
+ipNetworkNumber: 155.11.67.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=jerusalem2, o=SGI, c=US
+cn: jerusalem2
+ipNetworkNumber: 155.11.67.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-debug1, o=SGI, c=US
+cn: neu-debug1
+ipNetworkNumber: 155.11.68.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-debug2, o=SGI, c=US
+cn: neu-debug2
+ipNetworkNumber: 155.11.68.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-peripheral1, o=SGI, c=US
+cn: neu-peripheral1
+ipNetworkNumber: 155.11.70.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-peripheral2, o=SGI, c=US
+cn: neu-peripheral2
+ipNetworkNumber: 155.11.70.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-15, o=SGI, c=US
+cn: reading-15
+ipNetworkNumber: 155.11.71.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-16, o=SGI, c=US
+cn: reading-16
+ipNetworkNumber: 155.11.71.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-17, o=SGI, c=US
+cn: reading-17
+ipNetworkNumber: 155.11.72.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-18, o=SGI, c=US
+cn: reading-18
+ipNetworkNumber: 155.11.72.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-19, o=SGI, c=US
+cn: reading-19
+ipNetworkNumber: 155.11.73.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-20, o=SGI, c=US
+cn: reading-20
+ipNetworkNumber: 155.11.73.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=demeern-9, o=SGI, c=US
+cn: demeern-9
+ipNetworkNumber: 155.11.74.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=demeern-10, o=SGI, c=US
+cn: demeern-10
+ipNetworkNumber: 155.11.74.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=demeern-11, o=SGI, c=US
+cn: demeern-11
+ipNetworkNumber: 155.11.75.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=demeern-12, o=SGI, c=US
+cn: demeern-12
+ipNetworkNumber: 155.11.75.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=demeern-13, o=SGI, c=US
+cn: demeern-13
+ipNetworkNumber: 155.11.76.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=demeern-14, o=SGI, c=US
+cn: demeern-14
+ipNetworkNumber: 155.11.76.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-21, o=SGI, c=US
+cn: reading-21
+ipNetworkNumber: 155.11.77.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-22, o=SGI, c=US
+cn: reading-22
+ipNetworkNumber: 155.11.77.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-23, o=SGI, c=US
+cn: reading-23
+ipNetworkNumber: 155.11.78.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-24, o=SGI, c=US
+cn: reading-24
+ipNetworkNumber: 155.11.78.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-25, o=SGI, c=US
+cn: reading-25
+ipNetworkNumber: 155.11.79.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-26, o=SGI, c=US
+cn: reading-26
+ipNetworkNumber: 155.11.79.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=praha-01, o=SGI, c=US
+cn: praha-01
+ipNetworkNumber: 155.11.80.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=praha-02, o=SGI, c=US
+cn: praha-02
+ipNetworkNumber: 155.11.80.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=strasbourg-01, o=SGI, c=US
+cn: strasbourg-01
+ipNetworkNumber: 155.11.81.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=paris-08, o=SGI, c=US
+cn: paris-08
+ipNetworkNumber: 155.11.81.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=paris-09, o=SGI, c=US
+cn: paris-09
+ipNetworkNumber: 155.11.82.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=paris-10, o=SGI, c=US
+cn: paris-10
+ipNetworkNumber: 155.11.82.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=milan-03, o=SGI, c=US
+cn: milan-03
+ipNetworkNumber: 155.11.83.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=milan-04, o=SGI, c=US
+cn: milan-04
+ipNetworkNumber: 155.11.83.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=cort-desktop1, o=SGI, c=US
+cn: cort-desktop1
+ipNetworkNumber: 155.11.84.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=cort-desktop2, o=SGI, c=US
+cn: cort-desktop2
+ipNetworkNumber: 155.11.84.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=copen-3, o=SGI, c=US
+cn: copen-3
+ipNetworkNumber: 155.11.85.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=copen-4, o=SGI, c=US
+cn: copen-4
+ipNetworkNumber: 155.11.85.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu-isdn, o=SGI, c=US
+cn: neu-isdn
+ipNetworkNumber: 155.11.86.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=home-isdn, o=SGI, c=US
+cn: home-isdn
+ipNetworkNumber: 155.11.86.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=lausanne5, o=SGI, c=US
+cn: lausanne5
+ipNetworkNumber: 155.11.87.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=lausanne6, o=SGI, c=US
+cn: lausanne6
+ipNetworkNumber: 155.11.87.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=lausanne7, o=SGI, c=US
+cn: lausanne7
+ipNetworkNumber: 155.11.88.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-01, o=SGI, c=US
+cn: reading-01
+ipNetworkNumber: 155.11.90.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-02, o=SGI, c=US
+cn: reading-02
+ipNetworkNumber: 155.11.90.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-03, o=SGI, c=US
+cn: reading-03
+ipNetworkNumber: 155.11.91.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-04, o=SGI, c=US
+cn: reading-04
+ipNetworkNumber: 155.11.91.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-05, o=SGI, c=US
+cn: reading-05
+ipNetworkNumber: 155.11.92.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-06, o=SGI, c=US
+cn: reading-06
+ipNetworkNumber: 155.11.92.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-07, o=SGI, c=US
+cn: reading-07
+ipNetworkNumber: 155.11.93.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-08, o=SGI, c=US
+cn: reading-08
+ipNetworkNumber: 155.11.93.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-09, o=SGI, c=US
+cn: reading-09
+ipNetworkNumber: 155.11.94.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-10, o=SGI, c=US
+cn: reading-10
+ipNetworkNumber: 155.11.94.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-11, o=SGI, c=US
+cn: reading-11
+ipNetworkNumber: 155.11.95.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-12, o=SGI, c=US
+cn: reading-12
+ipNetworkNumber: 155.11.95.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-13, o=SGI, c=US
+cn: reading-13
+ipNetworkNumber: 155.11.96.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-14, o=SGI, c=US
+cn: reading-14
+ipNetworkNumber: 155.11.96.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=manchester-01, o=SGI, c=US
+cn: manchester-01
+ipNetworkNumber: 155.11.97.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=manchester-02, o=SGI, c=US
+cn: manchester-02
+ipNetworkNumber: 155.11.97.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-27, o=SGI, c=US
+cn: reading-27
+ipNetworkNumber: 155.11.98.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-28, o=SGI, c=US
+cn: reading-28
+ipNetworkNumber: 155.11.98.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-29, o=SGI, c=US
+cn: reading-29
+ipNetworkNumber: 155.11.99.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neu_comms, o=SGI, c=US
+cn: neu_comms
+ipNetworkNumber: 155.11.99.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-30, o=SGI, c=US
+cn: reading-30
+ipNetworkNumber: 155.11.100.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=reading-31, o=SGI, c=US
+cn: reading-31
+ipNetworkNumber: 155.11.100.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wangate-common, o=SGI, c=US
+cn: wangate-common
+ipNetworkNumber: 155.11.254
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wangate2-wanatl, o=SGI, c=US
+cn: wangate2-wanatl
+ipNetworkNumber: 155.11.253
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wangate2-wandal, o=SGI, c=US
+cn: wangate2-wandal
+ipNetworkNumber: 155.11.252
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wangate-wanhud, o=SGI, c=US
+cn: wangate-wanhud
+ipNetworkNumber: 155.11.251
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wangate2-wanden, o=SGI, c=US
+cn: wangate2-wanden
+ipNetworkNumber: 155.11.250
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wangate2-wandiego, o=SGI, c=US
+cn: wangate2-wandiego
+ipNetworkNumber: 155.11.249
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wangate2-wanhou, o=SGI, c=US
+cn: wangate2-wanhou
+ipNetworkNumber: 155.11.248
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wangate2-wanmtv, o=SGI, c=US
+cn: wangate2-wanmtv
+ipNetworkNumber: 155.11.247
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanfarm-wanrose, o=SGI, c=US
+cn: wanfarm-wanrose
+ipNetworkNumber: 155.11.246
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanfarm-wanoak, o=SGI, c=US
+cn: wanfarm-wanoak
+ipNetworkNumber: 155.11.245
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanfarm-wanhud, o=SGI, c=US
+cn: wanfarm-wanhud
+ipNetworkNumber: 155.11.244
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanhud-wanmil, o=SGI, c=US
+cn: wanhud-wanmil
+ipNetworkNumber: 155.11.243
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanhud-wanhan, o=SGI, c=US
+cn: wanhud-wanhan
+ipNetworkNumber: 155.11.242
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanbeth-wantim, o=SGI, c=US
+cn: wanbeth-wantim
+ipNetworkNumber: 155.11.241
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanbeth-wanatl, o=SGI, c=US
+cn: wanbeth-wanatl
+ipNetworkNumber: 155.11.240
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanatl-wanmco, o=SGI, c=US
+cn: wanatl-wanmco
+ipNetworkNumber: 155.11.239
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanatl-wanlaud, o=SGI, c=US
+cn: wanatl-wanlaud
+ipNetworkNumber: 155.11.238
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wandal-wanstl, o=SGI, c=US
+cn: wandal-wanstl
+ipNetworkNumber: 155.11.237
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=aw-sydney, o=SGI, c=US
+cn: aw-sydney
+ipNetworkNumber: 155.11.236
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wantokyo-wannagoya, o=SGI, c=US
+cn: wantokyo-wannagoya
+ipNetworkNumber: 155.11.235
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=osaka, o=SGI, c=US
+cn: osaka
+ipNetworkNumber: 155.11.234
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=nagoya, o=SGI, c=US
+cn: nagoya
+ipNetworkNumber: 155.11.233
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wagner-home, o=SGI, c=US
+cn: wagner-home
+ipNetworkNumber: 155.11.232
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=ntt-nsg, o=SGI, c=US
+cn: ntt-nsg
+ipNetworkNumber: 155.11.230
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=bos-nei1-brige, o=SGI, c=US
+cn: bos-nei1-brige
+ipNetworkNumber: 155.11.231
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wsyd-wperth, o=SGI, c=US
+cn: wsyd-wperth
+ipNetworkNumber: 155.11.229
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wellington, o=SGI, c=US
+cn: wellington
+ipNetworkNumber: 155.11.228
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=connyers-atlanta, o=SGI, c=US
+cn: connyers-atlanta
+ipNetworkNumber: 155.11.227
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=sydney, o=SGI, c=US
+cn: sydney
+ipNetworkNumber: 155.11.226
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=melbourne, o=SGI, c=US
+cn: melbourne
+ipNetworkNumber: 155.11.225
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=brisbane, o=SGI, c=US
+cn: brisbane
+ipNetworkNumber: 155.11.224
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=adelaide, o=SGI, c=US
+cn: adelaide
+ipNetworkNumber: 155.11.223
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=perth, o=SGI, c=US
+cn: perth
+ipNetworkNumber: 155.11.222
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=auckland, o=SGI, c=US
+cn: auckland
+ipNetworkNumber: 155.11.221
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=tokyo-net1, o=SGI, c=US
+cn: tokyo-net1
+ipNetworkNumber: 155.11.220
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=tokyo-net2, o=SGI, c=US
+cn: tokyo-net2
+ipNetworkNumber: 155.11.219
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=hongkong, o=SGI, c=US
+cn: hongkong
+ipNetworkNumber: 155.11.218
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=milwaukee, o=SGI, c=US
+cn: milwaukee
+ipNetworkNumber: 155.11.216
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=kansas, o=SGI, c=US
+cn: kansas
+ipNetworkNumber: 155.11.215
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=honk-kong-test, o=SGI, c=US
+cn: honk-kong-test
+ipNetworkNumber: 155.11.214
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=beijing1, o=SGI, c=US
+cn: beijing1
+ipNetworkNumber: 155.11.213
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=kawasaki, o=SGI, c=US
+cn: kawasaki
+ipNetworkNumber: 155.11.212
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=shangai1, o=SGI, c=US
+cn: shangai1
+ipNetworkNumber: 155.11.211
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=guangzhou, o=SGI, c=US
+cn: guangzhou
+ipNetworkNumber: 155.11.210
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=riverside, o=SGI, c=US
+cn: riverside
+ipNetworkNumber: 155.11.206
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=minneapolis, o=SGI, c=US
+cn: minneapolis
+ipNetworkNumber: 155.11.205
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=canberra, o=SGI, c=US
+cn: canberra
+ipNetworkNumber: 155.11.204
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=newdelhi, o=SGI, c=US
+cn: newdelhi
+ipNetworkNumber: 155.11.203
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=sanantonio, o=SGI, c=US
+cn: sanantonio
+ipNetworkNumber: 155.11.202
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=frame-relay2, o=SGI, c=US
+cn: frame-relay2
+ipNetworkNumber: 155.11.201
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=pittsburgh, o=SGI, c=US
+cn: pittsburgh
+ipNetworkNumber: 155.11.200
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=albuquerque, o=SGI, c=US
+cn: albuquerque
+ipNetworkNumber: 155.11.199
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=syracuse, o=SGI, c=US
+cn: syracuse
+ipNetworkNumber: 155.11.198
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=rochester, o=SGI, c=US
+cn: rochester
+ipNetworkNumber: 155.11.197
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=tulsa, o=SGI, c=US
+cn: tulsa
+ipNetworkNumber: 155.11.196
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=leffler-home, o=SGI, c=US
+cn: leffler-home
+ipNetworkNumber: 155.11.194
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=melville, o=SGI, c=US
+cn: melville
+ipNetworkNumber: 155.11.193
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=hudson-slip-1, o=SGI, c=US
+cn: hudson-slip-1
+ipNetworkNumber: 155.11.192
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=hudson-engr, o=SGI, c=US
+cn: hudson-engr
+ipNetworkNumber: 155.11.191
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=hudson-sales, o=SGI, c=US
+cn: hudson-sales
+ipNetworkNumber: 155.11.190
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=nova, o=SGI, c=US
+cn: nova
+ipNetworkNumber: 155.11.189
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=singapore, o=SGI, c=US
+cn: singapore
+ipNetworkNumber: 155.11.188
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=allied, o=SGI, c=US
+cn: allied
+ipNetworkNumber: 155.11.187
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=manhattan, o=SGI, c=US
+cn: manhattan
+ipNetworkNumber: 155.11.186
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=mtltac, o=SGI, c=US
+cn: mtltac
+ipNetworkNumber: 155.11.185
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=cleveland, o=SGI, c=US
+cn: cleveland
+ipNetworkNumber: 155.11.184
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=sao-paolo, o=SGI, c=US
+cn: sao-paolo
+ipNetworkNumber: 155.11.183
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wangate2-wbldG, o=SGI, c=US
+cn: wangate2-wbldG
+ipNetworkNumber: 155.11.181
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=sanramon, o=SGI, c=US
+cn: sanramon
+ipNetworkNumber: 155.11.180
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=sanramon-fddi, o=SGI, c=US
+cn: sanramon-fddi
+ipNetworkNumber: 155.11.180.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=hudson-training, o=SGI, c=US
+cn: hudson-training
+ipNetworkNumber: 155.11.179
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=hudson-tech, o=SGI, c=US
+cn: hudson-tech
+ipNetworkNumber: 155.11.178
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=singapore-eptc, o=SGI, c=US
+cn: singapore-eptc
+ipNetworkNumber: 155.11.177
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=ottawa, o=SGI, c=US
+cn: ottawa
+ipNetworkNumber: 155.11.172
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=calgary, o=SGI, c=US
+cn: calgary
+ipNetworkNumber: 155.11.171
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=vancouver, o=SGI, c=US
+cn: vancouver
+ipNetworkNumber: 155.11.170
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=tampa, o=SGI, c=US
+cn: tampa
+ipNetworkNumber: 155.11.169
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=birmingham, o=SGI, c=US
+cn: birmingham
+ipNetworkNumber: 155.11.168
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=stlouis-sales, o=SGI, c=US
+cn: stlouis-sales
+ipNetworkNumber: 155.11.167
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=stlouis-service, o=SGI, c=US
+cn: stlouis-service
+ipNetworkNumber: 155.11.166
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=albany, o=SGI, c=US
+cn: albany
+ipNetworkNumber: 155.11.165
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=neworleans, o=SGI, c=US
+cn: neworleans
+ipNetworkNumber: 155.11.164
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=sacramento, o=SGI, c=US
+cn: sacramento
+ipNetworkNumber: 155.11.163
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=buffalo, o=SGI, c=US
+cn: buffalo
+ipNetworkNumber: 155.11.162
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=urbana, o=SGI, c=US
+cn: urbana
+ipNetworkNumber: 155.11.161
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=korea, o=SGI, c=US
+cn: korea
+ipNetworkNumber: 155.11.160
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=harrisburg, o=SGI, c=US
+cn: harrisburg
+ipNetworkNumber: 155.11.159
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=dallas1, o=SGI, c=US
+cn: dallas1
+ipNetworkNumber: 155.11.158
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=dallas2, o=SGI, c=US
+cn: dallas2
+ipNetworkNumber: 155.11.157
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=denver1, o=SGI, c=US
+cn: denver1
+ipNetworkNumber: 155.11.156
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=denver2, o=SGI, c=US
+cn: denver2
+ipNetworkNumber: 155.11.155
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=toronto, o=SGI, c=US
+cn: toronto
+ipNetworkNumber: 155.11.154
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=stlaurent, o=SGI, c=US
+cn: stlaurent
+ipNetworkNumber: 155.11.153
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=charlotte, o=SGI, c=US
+cn: charlotte
+ipNetworkNumber: 155.11.152
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=bothell, o=SGI, c=US
+cn: bothell
+ipNetworkNumber: 155.11.151
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=space-vision-tokyo, o=SGI, c=US
+cn: space-vision-tokyo
+ipNetworkNumber: 155.11.150
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=cray-mfg-pro-tokyo, o=SGI, c=US
+cn: cray-mfg-pro-tokyo
+ipNetworkNumber: 155.11.149
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=sandiego, o=SGI, c=US
+cn: sandiego
+ipNetworkNumber: 155.11.148
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b16-corp-avai, o=SGI, c=US
+cn: b16-corp-avai
+ipNetworkNumber: 155.11.147
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=houston, o=SGI, c=US
+cn: houston
+ipNetworkNumber: 155.11.145
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=lauderdale, o=SGI, c=US
+cn: lauderdale
+ipNetworkNumber: 155.11.144
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=huntsville, o=SGI, c=US
+cn: huntsville
+ipNetworkNumber: 155.11.143
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=jackson, o=SGI, c=US
+cn: jackson
+ipNetworkNumber: 155.11.142
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=triangle, o=SGI, c=US
+cn: triangle
+ipNetworkNumber: 155.11.141
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=knoxville-avai, o=SGI, c=US
+cn: knoxville-avai
+ipNetworkNumber: 155.11.140
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=orlando, o=SGI, c=US
+cn: orlando
+ipNetworkNumber: 155.11.139
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=pensacola, o=SGI, c=US
+cn: pensacola
+ipNetworkNumber: 155.11.138
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=atlanta-avai, o=SGI, c=US
+cn: atlanta-avai
+ipNetworkNumber: 155.11.137
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=hampton-avai, o=SGI, c=US
+cn: hampton-avai
+ipNetworkNumber: 155.11.136
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=timonium, o=SGI, c=US
+cn: timonium
+ipNetworkNumber: 155.11.135
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=clubfed-avai, o=SGI, c=US
+cn: clubfed-avai
+ipNetworkNumber: 155.11.134
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=dayton-avai, o=SGI, c=US
+cn: dayton-avai
+ipNetworkNumber: 155.11.133
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=columbus, o=SGI, c=US
+cn: columbus
+ipNetworkNumber: 155.11.132
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=chicago, o=SGI, c=US
+cn: chicago
+ipNetworkNumber: 155.11.131
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=indianapolis, o=SGI, c=US
+cn: indianapolis
+ipNetworkNumber: 155.11.130
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=detroit, o=SGI, c=US
+cn: detroit
+ipNetworkNumber: 155.11.129
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=trevose, o=SGI, c=US
+cn: trevose
+ipNetworkNumber: 155.11.128
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=parsippany, o=SGI, c=US
+cn: parsippany
+ipNetworkNumber: 155.11.127
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=meriden-avai, o=SGI, c=US
+cn: meriden-avai
+ipNetworkNumber: 155.11.126
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=richmond, o=SGI, c=US
+cn: richmond
+ipNetworkNumber: 155.11.125
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=aberdeen, o=SGI, c=US
+cn: aberdeen
+ipNetworkNumber: 155.11.124
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=hacienda, o=SGI, c=US
+cn: hacienda
+ipNetworkNumber: 155.11.123
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=hudson-dfoulser, o=SGI, c=US
+cn: hudson-dfoulser
+ipNetworkNumber: 155.11.122
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=ftworth, o=SGI, c=US
+cn: ftworth
+ipNetworkNumber: 155.11.121
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=tokyo-net3, o=SGI, c=US
+cn: tokyo-net3
+ipNetworkNumber: 155.11.120
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=tokyo-nptc2, o=SGI, c=US
+cn: tokyo-nptc2
+ipNetworkNumber: 155.11.119
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=johannesburg, o=SGI, c=US
+cn: johannesburg
+ipNetworkNumber: 155.11.118
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=tokyo-nptc1, o=SGI, c=US
+cn: tokyo-nptc1
+ipNetworkNumber: 155.11.117
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=ssi-prod, o=SGI, c=US
+cn: ssi-prod
+ipNetworkNumber: 155.11.116
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=bangalore, o=SGI, c=US
+cn: bangalore
+ipNetworkNumber: 155.11.115
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wan-atm, o=SGI, c=US
+cn: wan-atm
+ipNetworkNumber: 155.11.112
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=clearlake, o=SGI, c=US
+cn: clearlake
+ipNetworkNumber: 155.11.111
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=alamos, o=SGI, c=US
+cn: alamos
+ipNetworkNumber: 155.11.110
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=troy, o=SGI, c=US
+cn: troy
+ipNetworkNumber: 155.11.109
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=temp-ring, o=SGI, c=US
+cn: temp-ring
+ipNetworkNumber: 155.11.108
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=silicon-farm, o=SGI, c=US
+cn: silicon-farm
+ipNetworkNumber: 155.11.107
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=perftools-melb, o=SGI, c=US
+cn: perftools-melb
+ipNetworkNumber: 155.11.106
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=memphis, o=SGI, c=US
+cn: memphis
+ipNetworkNumber: 155.11.104
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=nashville, o=SGI, c=US
+cn: nashville
+ipNetworkNumber: 155.11.103
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=access-graphics, o=SGI, c=US
+cn: access-graphics
+ipNetworkNumber: 155.11.102
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=sgi-uk, o=SGI, c=US
+cn: sgi-uk
+ipNetworkNumber: 192.35.108
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=melbourne-net, o=SGI, c=US
+cn: melbourne-net
+ipNetworkNumber: 192.68.139
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=oasis, o=SGI, c=US
+cn: oasis
+ipNetworkNumber: 163.154.0.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=ids-1, o=SGI, c=US
+cn: ids-1
+ipNetworkNumber: 204.94.208
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=sgigate-net, o=SGI, c=US
+cn: sgigate-net
+ipNetworkNumber: 204.94.209
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=is-test-out, o=SGI, c=US
+cn: is-test-out
+ipNetworkNumber: 204.94.210
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=is-outside-ring, o=SGI, c=US
+cn: is-outside-ring
+ipNetworkNumber: 204.94.211
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=is-devforum, o=SGI, c=US
+cn: is-devforum
+ipNetworkNumber: 204.94.212
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=is-devline, o=SGI, c=US
+cn: is-devline
+ipNetworkNumber: 204.94.213
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=alv-temp, o=SGI, c=US
+cn: alv-temp
+ipNetworkNumber: 204.94.215
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=ids-2, o=SGI, c=US
+cn: ids-2
+ipNetworkNumber: 204.94.223
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=ssla-cidr-1, o=SGI, c=US
+cn: ssla-cidr-1
+ipNetworkNumber: 204.250.254
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=ssla-cidr-2, o=SGI, c=US
+cn: ssla-cidr-2
+ipNetworkNumber: 204.250.255
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b17u-cselabtr, o=SGI, c=US
+cn: b17u-cselabtr
+ipNetworkNumber: 150.166.1
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b17u-labiso, o=SGI, c=US
+cn: b17u-labiso
+ipNetworkNumber: 150.166.2
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b17u-cselabfddi, o=SGI, c=US
+cn: b17u-cselabfddi
+ipNetworkNumber: 150.166.3
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b17u-labenet1, o=SGI, c=US
+cn: b17u-labenet1
+ipNetworkNumber: 150.166.4
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b17u-labenet2, o=SGI, c=US
+cn: b17u-labenet2
+ipNetworkNumber: 150.166.5
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b17l-new2, o=SGI, c=US
+cn: b17l-new2
+ipNetworkNumber: 150.166.7
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b17-microwave, o=SGI, c=US
+cn: b17-microwave
+ipNetworkNumber: 150.166.9
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=etc-fiber-channel, o=SGI, c=US
+cn: etc-fiber-channel
+ipNetworkNumber: 150.166.10
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b17u-cselabmm, o=SGI, c=US
+cn: b17u-cselabmm
+ipNetworkNumber: 150.166.11
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b30l-csd, o=SGI, c=US
+cn: b30l-csd
+ipNetworkNumber: 150.166.12
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8u-tring_net1, o=SGI, c=US
+cn: b8u-tring_net1
+ipNetworkNumber: 150.166.13
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b17u-lab-conf, o=SGI, c=US
+cn: b17u-lab-conf
+ipNetworkNumber: 150.166.14
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b17u-cselabhip, o=SGI, c=US
+cn: b17u-cselabhip
+ipNetworkNumber: 150.166.15
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8u-tring_net2, o=SGI, c=US
+cn: b8u-tring_net2
+ipNetworkNumber: 150.166.16
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b24u-performance-plus_lab, o=SGI, c=US
+cn: b24u-performance-plus_lab
+ipNetworkNumber: 150.166.17
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b21-softsuite-blockhouse-network, o=SGI, c=US
+cn: b21-softsuite-blockhouse-network
+ipNetworkNumber: 150.166.24
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b14u-vsg-apps3, o=SGI, c=US
+cn: b14u-vsg-apps3
+ipNetworkNumber: 150.166.32
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b14l-corp-net5, o=SGI, c=US
+cn: b14l-corp-net5
+ipNetworkNumber: 150.166.33
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9u-lab, o=SGI, c=US
+cn: b9u-lab
+ipNetworkNumber: 150.166.34
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1dco-ntservers, o=SGI, c=US
+cn: b1dco-ntservers
+ipNetworkNumber: 150.166.35
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8u-engr-net, o=SGI, c=US
+cn: b8u-engr-net
+ipNetworkNumber: 150.166.36
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8u-core_render_net1, o=SGI, c=US
+cn: b8u-core_render_net1
+ipNetworkNumber: 150.166.37
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b10l-pandora_lab_engr2, o=SGI, c=US
+cn: b10l-pandora_lab_engr2
+ipNetworkNumber: 150.166.39
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8u-ipd, o=SGI, c=US
+cn: b8u-ipd
+ipNetworkNumber: 150.166.40
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8u-ipd_lab, o=SGI, c=US
+cn: b8u-ipd_lab
+ipNetworkNumber: 150.166.41
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8u-ipd_engr, o=SGI, c=US
+cn: b8u-ipd_engr
+ipNetworkNumber: 150.166.42
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8l-ibmi_lab, o=SGI, c=US
+cn: b8l-ibmi_lab
+ipNetworkNumber: 150.166.43
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8u-tirix_net, o=SGI, c=US
+cn: b8u-tirix_net
+ipNetworkNumber: 150.166.44
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b12-imsd5, o=SGI, c=US
+cn: b12-imsd5
+ipNetworkNumber: 150.166.45
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-dss2, o=SGI, c=US
+cn: b1-dss2
+ipNetworkNumber: 150.166.46
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-dss3, o=SGI, c=US
+cn: b1-dss3
+ipNetworkNumber: 150.166.47
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8u-add-mktg, o=SGI, c=US
+cn: b8u-add-mktg
+ipNetworkNumber: 150.166.48
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8u-ipd_lab2, o=SGI, c=US
+cn: b8u-ipd_lab2
+ipNetworkNumber: 150.166.49
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b10l-pandora_lab_engr, o=SGI, c=US
+cn: b10l-pandora_lab_engr
+ipNetworkNumber: 150.166.51
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b10-community1, o=SGI, c=US
+cn: b10-community1
+ipNetworkNumber: 150.166.52
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b10-community2, o=SGI, c=US
+cn: b10-community2
+ipNetworkNumber: 150.166.53
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b10-community3, o=SGI, c=US
+cn: b10-community3
+ipNetworkNumber: 150.166.54
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b10-community4, o=SGI, c=US
+cn: b10-community4
+ipNetworkNumber: 150.166.55
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b10-community5, o=SGI, c=US
+cn: b10-community5
+ipNetworkNumber: 150.166.56
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b10-community6, o=SGI, c=US
+cn: b10-community6
+ipNetworkNumber: 150.166.57
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b10-community7, o=SGI, c=US
+cn: b10-community7
+ipNetworkNumber: 150.166.58
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9u-tw59, o=SGI, c=US
+cn: b9u-tw59
+ipNetworkNumber: 150.166.59
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-empty60, o=SGI, c=US
+cn: b9-empty60
+ipNetworkNumber: 150.166.60
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9u-engr61, o=SGI, c=US
+cn: b9u-engr61
+ipNetworkNumber: 150.166.61
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-empty62, o=SGI, c=US
+cn: b9-empty62
+ipNetworkNumber: 150.166.62
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-empty63, o=SGI, c=US
+cn: b9-empty63
+ipNetworkNumber: 150.166.63
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-empty64, o=SGI, c=US
+cn: b9-empty64
+ipNetworkNumber: 150.166.64
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9l-sw-lab1, o=SGI, c=US
+cn: b9l-sw-lab1
+ipNetworkNumber: 150.166.65
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9l-sw-lab2, o=SGI, c=US
+cn: b9l-sw-lab2
+ipNetworkNumber: 150.166.66
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-empty68, o=SGI, c=US
+cn: b9-empty68
+ipNetworkNumber: 150.166.68
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-empty69, o=SGI, c=US
+cn: b9-empty69
+ipNetworkNumber: 150.166.69
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9u-tw-itv1, o=SGI, c=US
+cn: b9u-tw-itv1
+ipNetworkNumber: 150.166.70
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-empty71, o=SGI, c=US
+cn: b9-empty71
+ipNetworkNumber: 150.166.71
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-empty72, o=SGI, c=US
+cn: b9-empty72
+ipNetworkNumber: 150.166.72
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-empty73, o=SGI, c=US
+cn: b9-empty73
+ipNetworkNumber: 150.166.73
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-empty74, o=SGI, c=US
+cn: b9-empty74
+ipNetworkNumber: 150.166.74
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9u-engr75, o=SGI, c=US
+cn: b9u-engr75
+ipNetworkNumber: 150.166.75
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9u-engr76, o=SGI, c=US
+cn: b9u-engr76
+ipNetworkNumber: 150.166.76
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-empty77, o=SGI, c=US
+cn: b9-empty77
+ipNetworkNumber: 150.166.77
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-empty78, o=SGI, c=US
+cn: b9-empty78
+ipNetworkNumber: 150.166.78
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9l-100bt, o=SGI, c=US
+cn: b9l-100bt
+ipNetworkNumber: 150.166.79
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-empty80, o=SGI, c=US
+cn: b9-empty80
+ipNetworkNumber: 150.166.80
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9l-advnet-lab, o=SGI, c=US
+cn: b9l-advnet-lab
+ipNetworkNumber: 150.166.81
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-empty82, o=SGI, c=US
+cn: b9-empty82
+ipNetworkNumber: 150.166.82
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-webgrp, o=SGI, c=US
+cn: b9-webgrp
+ipNetworkNumber: 150.166.83
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=aes-ams-atm2, o=SGI, c=US
+cn: aes-ams-atm2
+ipNetworkNumber: 150.166.84
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=aes-ams-atm3, o=SGI, c=US
+cn: aes-ams-atm3
+ipNetworkNumber: 150.166.85
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b17l-csdtrnglab, o=SGI, c=US
+cn: b17l-csdtrnglab
+ipNetworkNumber: 150.166.87
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9l-sqa-fddi1, o=SGI, c=US
+cn: b9l-sqa-fddi1
+ipNetworkNumber: 150.166.88
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9l-sqa-fddi2, o=SGI, c=US
+cn: b9l-sqa-fddi2
+ipNetworkNumber: 150.166.89
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b20-dco-fddi, o=SGI, c=US
+cn: b20-dco-fddi
+ipNetworkNumber: 150.166.90
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b20-dco, o=SGI, c=US
+cn: b20-dco
+ipNetworkNumber: 150.166.91
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9u-atm-lab, o=SGI, c=US
+cn: b9u-atm-lab
+ipNetworkNumber: 150.166.92
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9u-atm-lab2, o=SGI, c=US
+cn: b9u-atm-lab2
+ipNetworkNumber: 150.166.67
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b21-etm3, o=SGI, c=US
+cn: b21-etm3
+ipNetworkNumber: 150.166.93
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b21-ssi2, o=SGI, c=US
+cn: b21-ssi2
+ipNetworkNumber: 150.166.94
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b22-1, o=SGI, c=US
+cn: b22-1
+ipNetworkNumber: 150.166.95
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b22-2, o=SGI, c=US
+cn: b22-2
+ipNetworkNumber: 150.166.96
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8l, o=SGI, c=US
+cn: b8l
+ipNetworkNumber: 150.166.97
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b15-micro, o=SGI, c=US
+cn: b15-micro
+ipNetworkNumber: 150.166.98
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=ss-wan-net, o=SGI, c=US
+cn: ss-wan-net
+ipNetworkNumber: 150.166.99
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=fddi-campus, o=SGI, c=US
+cn: fddi-campus
+ipNetworkNumber: 150.166.100
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b17u-cse1, o=SGI, c=US
+cn: b17u-cse1
+ipNetworkNumber: 150.166.101
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b17-b21_micro, o=SGI, c=US
+cn: b17-b21_micro
+ipNetworkNumber: 150.166.102
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b15-b20_atm, o=SGI, c=US
+cn: b15-b20_atm
+ipNetworkNumber: 150.166.103
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b14U-apps3, o=SGI, c=US
+cn: b14U-apps3
+ipNetworkNumber: 150.166.104
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9u-100bt, o=SGI, c=US
+cn: b9u-100bt
+ipNetworkNumber: 150.166.105
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b12l-mfg, o=SGI, c=US
+cn: b12l-mfg
+ipNetworkNumber: 150.166.106
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=atm-utp, o=SGI, c=US
+cn: atm-utp
+ipNetworkNumber: 150.166.107
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b5u-demo-room, o=SGI, c=US
+cn: b5u-demo-room
+ipNetworkNumber: 150.166.108
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-ssd-benchlab_2, o=SGI, c=US
+cn: b7l-ssd-benchlab_2
+ipNetworkNumber: 150.166.109
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2u-gandalf-110, o=SGI, c=US
+cn: b2u-gandalf-110
+ipNetworkNumber: 150.166.110
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-dms1, o=SGI, c=US
+cn: b1-dms1
+ipNetworkNumber: 150.166.111
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-dms2, o=SGI, c=US
+cn: b1-dms2
+ipNetworkNumber: 150.166.112
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9l-media-qa_lab, o=SGI, c=US
+cn: b9l-media-qa_lab
+ipNetworkNumber: 150.166.113
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b23-micro, o=SGI, c=US
+cn: b23-micro
+ipNetworkNumber: 150.166.116
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b23-production, o=SGI, c=US
+cn: b23-production
+ipNetworkNumber: 150.166.117
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b23-t1, o=SGI, c=US
+cn: b23-t1
+ipNetworkNumber: 150.166.118
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b23, o=SGI, c=US
+cn: b23
+ipNetworkNumber: 150.166.119
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=btl-essA, o=SGI, c=US
+cn: btl-essA
+ipNetworkNumber: 150.166.120
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=btu-essA, o=SGI, c=US
+cn: btu-essA
+ipNetworkNumber: 150.166.121
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=bt-t1-b1, o=SGI, c=US
+cn: bt-t1-b1
+ipNetworkNumber: 150.166.122
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=bt-mcast-b1, o=SGI, c=US
+cn: bt-mcast-b1
+ipNetworkNumber: 150.166.123
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=bt-hssi-b1, o=SGI, c=US
+cn: bt-hssi-b1
+ipNetworkNumber: 150.166.124
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=btl-essB, o=SGI, c=US
+cn: btl-essB
+ipNetworkNumber: 150.166.125
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=btu-essB, o=SGI, c=US
+cn: btu-essB
+ipNetworkNumber: 150.166.126
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b24l-b2_micro, o=SGI, c=US
+cn: b24l-b2_micro
+ipNetworkNumber: 150.166.127
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b1-b28-t1, o=SGI, c=US
+cn: b1-b28-t1
+ipNetworkNumber: 150.166.132
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b10-b28-micro, o=SGI, c=US
+cn: b10-b28-micro
+ipNetworkNumber: 150.166.133
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b28-b29-endusers, o=SGI, c=US
+cn: b28-b29-endusers
+ipNetworkNumber: 150.166.134
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b17u-cselabatm, o=SGI, c=US
+cn: b17u-cselabatm
+ipNetworkNumber: 150.166.135
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b16-endusers, o=SGI, c=US
+cn: b16-endusers
+ipNetworkNumber: 150.166.136
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b14u-endusers, o=SGI, c=US
+cn: b14u-endusers
+ipNetworkNumber: 150.166.137
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b14u-endusers1, o=SGI, c=US
+cn: b14u-endusers1
+ipNetworkNumber: 150.166.138
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b14l-inperson_lab, o=SGI, c=US
+cn: b14l-inperson_lab
+ipNetworkNumber: 150.166.139
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b14l-endusers, o=SGI, c=US
+cn: b14l-endusers
+ipNetworkNumber: 150.166.140
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=ntg-net, o=SGI, c=US
+cn: ntg-net
+ipNetworkNumber: 150.166.141
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9u-sw-net, o=SGI, c=US
+cn: b9u-sw-net
+ipNetworkNumber: 150.166.142
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2-b27-micro, o=SGI, c=US
+cn: b2-b27-micro
+ipNetworkNumber: 150.166.143
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b27l-csd-endusers, o=SGI, c=US
+cn: b27l-csd-endusers
+ipNetworkNumber: 150.166.144
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b27u-csd-endusers, o=SGI, c=US
+cn: b27u-csd-endusers
+ipNetworkNumber: 150.166.145
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b21-corp-users, o=SGI, c=US
+cn: b21-corp-users
+ipNetworkNumber: 150.166.146
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8u-hppi-lab, o=SGI, c=US
+cn: b8u-hppi-lab
+ipNetworkNumber: 150.166.147
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=isdn-engr, o=SGI, c=US
+cn: isdn-engr
+ipNetworkNumber: 150.166.148
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b14l-moose1, o=SGI, c=US
+cn: b14l-moose1
+ipNetworkNumber: 150.166.149
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b14l-esd-users, o=SGI, c=US
+cn: b14l-esd-users
+ipNetworkNumber: 150.166.150
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b2-b26-fddi, o=SGI, c=US
+cn: b2-b26-fddi
+ipNetworkNumber: 150.166.151
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b26u-mfg, o=SGI, c=US
+cn: b26u-mfg
+ipNetworkNumber: 150.166.152
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b26u-csd, o=SGI, c=US
+cn: b26u-csd
+ipNetworkNumber: 150.166.153
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b26l-mfg, o=SGI, c=US
+cn: b26l-mfg
+ipNetworkNumber: 150.166.154
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b26l-csd, o=SGI, c=US
+cn: b26l-csd
+ipNetworkNumber: 150.166.155
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b26-mfg-floor, o=SGI, c=US
+cn: b26-mfg-floor
+ipNetworkNumber: 150.166.156
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b26-gcs-csd1, o=SGI, c=US
+cn: b26-gcs-csd1
+ipNetworkNumber: 150.166.157
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b26-mfg-server, o=SGI, c=US
+cn: b26-mfg-server
+ipNetworkNumber: 150.166.158
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b26-gcs-csd2, o=SGI, c=US
+cn: b26-gcs-csd2
+ipNetworkNumber: 150.166.159
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b27-telecomm-net, o=SGI, c=US
+cn: b27-telecomm-net
+ipNetworkNumber: 150.166.160
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b14-isdn-esd, o=SGI, c=US
+cn: b14-isdn-esd
+ipNetworkNumber: 150.166.161
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7l-hippi, o=SGI, c=US
+cn: b7l-hippi
+ipNetworkNumber: 150.166.162
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-atm, o=SGI, c=US
+cn: b9-atm
+ipNetworkNumber: 150.166.163
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b7-hippi, o=SGI, c=US
+cn: b7-hippi
+ipNetworkNumber: 150.166.164
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b14-community, o=SGI, c=US
+cn: b14-community
+ipNetworkNumber: 150.166.165
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9-eis-hippi, o=SGI, c=US
+cn: b9-eis-hippi
+ipNetworkNumber: 150.166.166
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b9l-laddis-lab, o=SGI, c=US
+cn: b9l-laddis-lab
+ipNetworkNumber: 150.166.167
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b24u-dev-hlab, o=SGI, c=US
+cn: b24u-dev-hlab
+ipNetworkNumber: 150.166.171
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b25-1-community-net, o=SGI, c=US
+cn: b25-1-community-net
+ipNetworkNumber: 150.166.172.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b25-2-community-net, o=SGI, c=US
+cn: b25-2-community-net
+ipNetworkNumber: 150.166.173.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b25-3-community-net, o=SGI, c=US
+cn: b25-3-community-net
+ipNetworkNumber: 150.166.174.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b25-4-community-net, o=SGI, c=US
+cn: b25-4-community-net
+ipNetworkNumber: 150.166.175.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b25-noc-net, o=SGI, c=US
+cn: b25-noc-net
+ipNetworkNumber: 150.166.176.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b25-isac-net, o=SGI, c=US
+cn: b25-isac-net
+ipNetworkNumber: 150.166.177.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b25-mmedia-lab-net, o=SGI, c=US
+cn: b25-mmedia-lab-net
+ipNetworkNumber: 150.166.178.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b25-ntg-lab-net, o=SGI, c=US
+cn: b25-ntg-lab-net
+ipNetworkNumber: 150.166.179.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b25-cvt-lab-net, o=SGI, c=US
+cn: b25-cvt-lab-net
+ipNetworkNumber: 150.166.180.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=fddi-annex, o=SGI, c=US
+cn: fddi-annex
+ipNetworkNumber: 150.166.200
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=shore-nafo1, o=SGI, c=US
+cn: shore-nafo1
+ipNetworkNumber: 150.166.201
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=shore-nafo2, o=SGI, c=US
+cn: shore-nafo2
+ipNetworkNumber: 150.166.202
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=shore-nafo3, o=SGI, c=US
+cn: shore-nafo3
+ipNetworkNumber: 150.166.203
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=shore-nafo4, o=SGI, c=US
+cn: shore-nafo4
+ipNetworkNumber: 150.166.204
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=shore-nafo5, o=SGI, c=US
+cn: shore-nafo5
+ipNetworkNumber: 150.166.205
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=shore-nafo6, o=SGI, c=US
+cn: shore-nafo6
+ipNetworkNumber: 150.166.206
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=shore-nafo7, o=SGI, c=US
+cn: shore-nafo7
+ipNetworkNumber: 150.166.207
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=shore-nafo8, o=SGI, c=US
+cn: shore-nafo8
+ipNetworkNumber: 150.166.208
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=shore-nafo9, o=SGI, c=US
+cn: shore-nafo9
+ipNetworkNumber: 150.166.209
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=shore-nafo10, o=SGI, c=US
+cn: shore-nafo10
+ipNetworkNumber: 150.166.210
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=shore-nafo11, o=SGI, c=US
+cn: shore-nafo11
+ipNetworkNumber: 150.166.211
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=shore-nafo12, o=SGI, c=US
+cn: shore-nafo12
+ipNetworkNumber: 150.166.212
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=shore-nafo13, o=SGI, c=US
+cn: shore-nafo13
+ipNetworkNumber: 150.166.213
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=shore-nafo14, o=SGI, c=US
+cn: shore-nafo14
+ipNetworkNumber: 150.166.214
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=shore-nafo15, o=SGI, c=US
+cn: shore-nafo15
+ipNetworkNumber: 150.166.215
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=telecom-solutions1, o=SGI, c=US
+cn: telecom-solutions1
+ipNetworkNumber: 150.166.216
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=telecom-solutions2, o=SGI, c=US
+cn: telecom-solutions2
+ipNetworkNumber: 150.166.217
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b20l-saleslab-2, o=SGI, c=US
+cn: b20l-saleslab-2
+ipNetworkNumber: 150.166.219
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=magic-mkting, o=SGI, c=US
+cn: magic-mkting
+ipNetworkNumber: 150.166.220
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=shore-nafo21, o=SGI, c=US
+cn: shore-nafo21
+ipNetworkNumber: 150.166.221
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=shore-nafo22, o=SGI, c=US
+cn: shore-nafo22
+ipNetworkNumber: 150.166.222
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b40-leadership1, o=SGI, c=US
+cn: b40-leadership1
+ipNetworkNumber: 150.166.227
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b40-leadership2, o=SGI, c=US
+cn: b40-leadership2
+ipNetworkNumber: 150.166.228
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b17u-cseserv, o=SGI, c=US
+cn: b17u-cseserv
+ipNetworkNumber: 150.166.229
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=atm-bckbone, o=SGI, c=US
+cn: atm-bckbone
+ipNetworkNumber: 150.166.230
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=shore-nafo29, o=SGI, c=US
+cn: shore-nafo29
+ipNetworkNumber: 150.166.231
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=shore-nafo30, o=SGI, c=US
+cn: shore-nafo30
+ipNetworkNumber: 150.166.232
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=atm2-bckbone, o=SGI, c=US
+cn: atm2-bckbone
+ipNetworkNumber: 150.166.233
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=intr-fddi-3, o=SGI, c=US
+cn: intr-fddi-3
+ipNetworkNumber: 150.166.234
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=atm, o=SGI, c=US
+cn: atm
+ipNetworkNumber: 150.166.235
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-mfg-endusers, o=SGI, c=US
+cn: b11-mfg-endusers
+ipNetworkNumber: 150.166.236
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b27u-tools-lab, o=SGI, c=US
+cn: b27u-tools-lab
+ipNetworkNumber: 150.166.237
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b17u-isdn-combinet, o=SGI, c=US
+cn: b17u-isdn-combinet
+ipNetworkNumber: 150.166.238
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b17u-isdn-ppp, o=SGI, c=US
+cn: b17u-isdn-ppp
+ipNetworkNumber: 150.166.239
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b5u-visim, o=SGI, c=US
+cn: b5u-visim
+ipNetworkNumber: 150.166.241
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-5l-mfg, o=SGI, c=US
+cn: b11-5l-mfg
+ipNetworkNumber: 150.166.242
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b11-5u-mfg, o=SGI, c=US
+cn: b11-5u-mfg
+ipNetworkNumber: 150.166.243
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b8-hippi, o=SGI, c=US
+cn: b8-hippi
+ipNetworkNumber: 150.166.245
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b20l-nafo-training, o=SGI, c=US
+cn: b20l-nafo-training
+ipNetworkNumber: 150.166.246
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b21-etm2, o=SGI, c=US
+cn: b21-etm2
+ipNetworkNumber: 150.166.247
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=dialbk-project1, o=SGI, c=US
+cn: dialbk-project1
+ipNetworkNumber: 150.166.248
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=dialbk-project2, o=SGI, c=US
+cn: dialbk-project2
+ipNetworkNumber: 150.166.249
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=dialbk-project3, o=SGI, c=US
+cn: dialbk-project3
+ipNetworkNumber: 150.166.250
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=dialbk-project4, o=SGI, c=US
+cn: dialbk-project4
+ipNetworkNumber: 150.166.251
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b21-ssi4, o=SGI, c=US
+cn: b21-ssi4
+ipNetworkNumber: 150.166.252
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b21-csd1, o=SGI, c=US
+cn: b21-csd1
+ipNetworkNumber: 150.166.253
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=b21-csd2, o=SGI, c=US
+cn: b21-csd2
+ipNetworkNumber: 150.166.254
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanaber.aberdeen, o=SGI, c=US
+cn: wanaber.aberdeen
+ipNetworkNumber: 169.238.31.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanbeth.clubfed, o=SGI, c=US
+cn: wanbeth.clubfed
+ipNetworkNumber: 169.238.31.4
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanham.hampton, o=SGI, c=US
+cn: wanham.hampton
+ipNetworkNumber: 169.238.31.8
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wannova.nova, o=SGI, c=US
+cn: wannova.nova
+ipNetworkNumber: 169.238.31.12
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanrich.richmond, o=SGI, c=US
+cn: wanrich.richmond
+ipNetworkNumber: 169.238.31.16
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wantim.timonium, o=SGI, c=US
+cn: wantim.timonium
+ipNetworkNumber: 169.238.31.20
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanalb.albany, o=SGI, c=US
+cn: wanalb.albany
+ipNetworkNumber: 169.238.63.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanbuf.buffalo, o=SGI, c=US
+cn: wanbuf.buffalo
+ipNetworkNumber: 169.238.63.4
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanhar.harrisburg, o=SGI, c=US
+cn: wanhar.harrisburg
+ipNetworkNumber: 169.238.63.8
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanhud.boston, o=SGI, c=US
+cn: wanhud.boston
+ipNetworkNumber: 169.238.63.12
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanmanhat.manhattan, o=SGI, c=US
+cn: wanmanhat.manhattan
+ipNetworkNumber: 169.238.63.16
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanmel.melville, o=SGI, c=US
+cn: wanmel.melville
+ipNetworkNumber: 169.238.63.20
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=anmer.meriden, o=SGI, c=US
+cn: anmer.meriden
+ipNetworkNumber: 169.238.63.24
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanparsip.parsippany, o=SGI, c=US
+cn: wanparsip.parsippany
+ipNetworkNumber: 169.238.63.28
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanroc.rochester, o=SGI, c=US
+cn: wanroc.rochester
+ipNetworkNumber: 169.238.63.32
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wansyc.syracuse, o=SGI, c=US
+cn: wansyc.syracuse
+ipNetworkNumber: 169.238.63.36
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wantre.trevose, o=SGI, c=US
+cn: wantre.trevose
+ipNetworkNumber: 169.238.63.40
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanaccessg.boulder, o=SGI, c=US
+cn: wanaccessg.boulder
+ipNetworkNumber: 169.238.95.120
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanant.sanantonio, o=SGI, c=US
+cn: wanant.sanantonio
+cn: is
+cn: down
+cn: due
+cn: to
+cn: move>
+ipNetworkNumber: <Site
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanaus.austin, o=SGI, c=US
+cn: wanaus.austin
+ipNetworkNumber: 169.238.95.116
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanclear.clearlake, o=SGI, c=US
+cn: wanclear.clearlake
+ipNetworkNumber: 169.238.95.112
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wancosprgs.cosprings, o=SGI, c=US
+cn: wancosprgs.cosprings
+ipNetworkNumber: 169.238.95.108
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wandal.dallas, o=SGI, c=US
+cn: wandal.dallas
+ipNetworkNumber: 169.238.95.104
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanden.denver, o=SGI, c=US
+cn: wanden.denver
+ipNetworkNumber: 169.238.95.100
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanhou.houst, o=SGI, c=US
+cn: wanhou.houst
+ipNetworkNumber: 169.238.95.96
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanslc.saltlake, o=SGI, c=US
+cn: wanslc.saltlake
+ipNetworkNumber: 169.238.95.92
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wantulsa.tulsa, o=SGI, c=US
+cn: wantulsa.tulsa
+ipNetworkNumber: 169.238.95.88
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanworth.ftworth, o=SGI, c=US
+cn: wanworth.ftworth
+ipNetworkNumber: 169.238.95.84
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=cisco.chez, o=SGI, c=US
+cn: cisco.chez
+ipNetworkNumber: 169.238.127.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=router-ala-jwag.engr, o=SGI, c=US
+cn: router-ala-jwag.engr
+ipNetworkNumber: 169.238.127.4
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanalamos.losalamos, o=SGI, c=US
+cn: wanalamos.losalamos
+ipNetworkNumber: 169.238.127.8
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanalbq.albuquerque, o=SGI, c=US
+cn: wanalbq.albuquerque
+ipNetworkNumber: 169.238.127.12
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wandiego.sandiego, o=SGI, c=US
+cn: wandiego.sandiego
+ipNetworkNumber: 169.238.127.16
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanlos.losangeles, o=SGI, c=US
+cn: wanlos.losangeles
+ipNetworkNumber: 169.238.127.20
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanmtv.corp, o=SGI, c=US
+cn: wanmtv.corp
+ipNetworkNumber: 169.238.127.24
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanmtv2.corp, o=SGI, c=US
+cn: wanmtv2.corp
+ipNetworkNumber: 169.238.127.28
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wannew.newport, o=SGI, c=US
+cn: wannew.newport
+ipNetworkNumber: 169.238.127.32
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanphx.phoenix, o=SGI, c=US
+cn: wanphx.phoenix
+ipNetworkNumber: 169.238.127.36
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanport.oregon, o=SGI, c=US
+cn: wanport.oregon
+ipNetworkNumber: 169.238.127.40
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanram.sanramon, o=SGI, c=US
+cn: wanram.sanramon
+ipNetworkNumber: 169.238.127.44
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanriver.riverside, o=SGI, c=US
+cn: wanriver.riverside
+ipNetworkNumber: 169.238.127.48
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wansacto.sacramento, o=SGI, c=US
+cn: wansacto.sacramento
+ipNetworkNumber: 169.238.127.52
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wansea.seattle, o=SGI, c=US
+cn: wansea.seattle
+ipNetworkNumber: 169.238.127.56
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanspo.spokane, o=SGI, c=US
+cn: wanspo.spokane
+ipNetworkNumber: 169.238.127.60
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanstudio-sm.ssla, o=SGI, c=US
+cn: wanstudio-sm.ssla
+ipNetworkNumber: 169.238.127.64
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wantuc.tucson, o=SGI, c=US
+cn: wantuc.tucson
+ipNetworkNumber: 169.238.127.68
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanvegas.lasvegas, o=SGI, c=US
+cn: wanvegas.lasvegas
+ipNetworkNumber: 169.238.127.72
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanallp.dearborn, o=SGI, c=US
+cn: wanallp.dearborn
+ipNetworkNumber: 169.238.143.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wancleve.cleveland, o=SGI, c=US
+cn: wancleve.cleveland
+ipNetworkNumber: 169.238.143.4
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wancol.columbus, o=SGI, c=US
+cn: wancol.columbus
+ipNetworkNumber: 169.238.143.8
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanday.dayton, o=SGI, c=US
+cn: wanday.dayton
+ipNetworkNumber: 169.238.143.12
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanfarm.detroit, o=SGI, c=US
+cn: wanfarm.detroit
+ipNetworkNumber: 169.238.143.16
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanindy.indianapolis, o=SGI, c=US
+cn: wanindy.indianapolis
+ipNetworkNumber: 169.238.143.20
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanpit.pittsburgh, o=SGI, c=US
+cn: wanpit.pittsburgh
+ipNetworkNumber: 169.238.143.24
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wantroy.troy, o=SGI, c=US
+cn: wantroy.troy
+ipNetworkNumber: 169.238.143.28
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanatl.atlanta, o=SGI, c=US
+cn: wanatl.atlanta
+ipNetworkNumber: 169.238.223.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanbirm.birmingham, o=SGI, c=US
+cn: wanbirm.birmingham
+ipNetworkNumber: 169.238.223.4
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wancharl.charlotte, o=SGI, c=US
+cn: wancharl.charlotte
+ipNetworkNumber: 169.238.223.8
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wancon.conyers, o=SGI, c=US
+cn: wancon.conyers
+ipNetworkNumber: 169.238.223.12
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wandur.triangle, o=SGI, c=US
+cn: wandur.triangle
+ipNetworkNumber: 169.238.223.16
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanhunt.huntsville, o=SGI, c=US
+cn: wanhunt.huntsville
+ipNetworkNumber: 169.238.223.20
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanjack.jackson, o=SGI, c=US
+cn: wanjack.jackson
+ipNetworkNumber: 169.238.223.24
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanknox.knoxville, o=SGI, c=US
+cn: wanknox.knoxville
+ipNetworkNumber: 169.238.223.28
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanlaud.lauderdale, o=SGI, c=US
+cn: wanlaud.lauderdale
+ipNetworkNumber: 169.238.223.32
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanmco.orlando, o=SGI, c=US
+cn: wanmco.orlando
+ipNetworkNumber: 169.238.223.36
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanmem.memphis, o=SGI, c=US
+cn: wanmem.memphis
+ipNetworkNumber: 169.238.223.40
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wannash.nashville, o=SGI, c=US
+cn: wannash.nashville
+ipNetworkNumber: 169.238.223.44
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanpns.pensacola, o=SGI, c=US
+cn: wanpns.pensacola
+ipNetworkNumber: 169.238.223.48
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wantamp.tampa, o=SGI, c=US
+cn: wantamp.tampa
+ipNetworkNumber: 169.238.223.52
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wancal.calgary, o=SGI, c=US
+cn: wancal.calgary
+ipNetworkNumber: 169.238.229.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanmtl.montreal, o=SGI, c=US
+cn: wanmtl.montreal
+ipNetworkNumber: 169.238.229.4
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanott.ottawa, o=SGI, c=US
+cn: wanott.ottawa
+ipNetworkNumber: 169.238.229.8
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanvan.vancouver, o=SGI, c=US
+cn: wanvan.vancouver
+ipNetworkNumber: 169.238.229.12
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wancedar.cedar, o=SGI, c=US
+cn: wancedar.cedar
+ipNetworkNumber: 169.238.239.0
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanchi.chicago, o=SGI, c=US
+cn: wanchi.chicago
+ipNetworkNumber: 169.238.239.4
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wankansas.kansas, o=SGI, c=US
+cn: wankansas.kansas
+ipNetworkNumber: 169.238.239.8
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanmilw.milwaukee, o=SGI, c=US
+cn: wanmilw.milwaukee
+ipNetworkNumber: 169.238.239.12
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanminn.minneapolis, o=SGI, c=US
+cn: wanminn.minneapolis
+ipNetworkNumber: 169.238.239.16
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanstl.stlouis, o=SGI, c=US
+cn: wanstl.stlouis
+ipNetworkNumber: 169.238.239.20
+objectclass: ipNetwork
+objectclass: top
+
+dn: cn=wanurb.urbana, o=SGI, c=US
+cn: wanurb.urbana
+ipNetworkNumber: 169.238.239.24
+objectclass: ipNetwork
+objectclass: top
+
+dn: uid=root, o=SGI, c=US
+uid: root
+userPassword: {crypt}xZuUdcHRxN1cc
+uidNumber: 0
+gidNumber: 0
+gecos: Super-User
+homeDirectory: /
+loginShell: /usr/bin/tcsh
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=sysadm, o=SGI, c=US
+uid: sysadm
+userPassword: *
+uidNumber: 0
+gidNumber: 0
+gecos: System V Administration
+homeDirectory: /usr/admin
+loginShell: /bin/sh
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=cmwlogin, o=SGI, c=US
+uid: cmwlogin
+userPassword: *
+uidNumber: 0
+gidNumber: 994
+gecos: CMW Login UserID
+homeDirectory: /usr/CMW
+loginShell: /sbin/csh
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=diag, o=SGI, c=US
+uid: diag
+userPassword: *
+uidNumber: 0
+gidNumber: 996
+gecos: Hardware Diagnostics
+homeDirectory: /usr/diags
+loginShell: /bin/csh
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=daemon, o=SGI, c=US
+uid: daemon
+userPassword: *
+uidNumber: 1
+gidNumber: 1
+gecos: daemons
+homeDirectory: /
+loginShell: /dev/null
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=bin, o=SGI, c=US
+uid: bin
+userPassword: *
+uidNumber: 2
+gidNumber: 2
+gecos: System Tools Owner
+homeDirectory: /bin
+loginShell: /dev/null
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=uucp, o=SGI, c=US
+uid: uucp
+userPassword: *
+uidNumber: 3
+gidNumber: 5
+gecos: UUCP Owner
+homeDirectory: /usr/lib/uucp
+loginShell: /bin/csh
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=sys, o=SGI, c=US
+uid: sys
+userPassword: *
+uidNumber: 4
+gidNumber: 0
+gecos: System Activity Owner
+homeDirectory: /var/adm
+loginShell: /bin/sh
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=adm, o=SGI, c=US
+uid: adm
+userPassword: *
+uidNumber: 5
+gidNumber: 3
+gecos: Accounting Files Owner
+homeDirectory: /var/adm
+loginShell: /bin/sh
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=lp, o=SGI, c=US
+uid: lp
+userPassword:
+uidNumber: 9
+gidNumber: 9
+gecos: Print Spooler Owner
+homeDirectory: /var/spool/lp
+loginShell: /bin/sh
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=nuucp, o=SGI, c=US
+uid: nuucp
+userPassword:
+uidNumber: 10
+gidNumber: 10
+gecos: Remote UUCP User
+homeDirectory: /var/spool/uucppublic
+loginShell: /usr/lib/uucp/uucico
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=auditor, o=SGI, c=US
+uid: auditor
+userPassword: *
+uidNumber: 11
+gidNumber: 0
+gecos: Audit Activity Owner
+homeDirectory: /auditor
+loginShell: /bin/sh
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=dbadmin, o=SGI, c=US
+uid: dbadmin
+userPassword: *
+uidNumber: 12
+gidNumber: 0
+gecos: Security Database Owner
+homeDirectory: /dbadmin
+loginShell: /bin/sh
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=sgiweb, o=SGI, c=US
+uid: sgiweb
+userPassword: *
+uidNumber: 13
+gidNumber: 60001
+gecos: SGI Web Applications
+homeDirectory: /var/www/htdocs
+loginShell: /bin/csh
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=rfindd, o=SGI, c=US
+uid: rfindd
+userPassword: *
+uidNumber: 66
+gidNumber: 1
+gecos: Rfind Daemon and Fsdump
+homeDirectory: /var/rfindd
+loginShell: /bin/sh
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=EZsetup, o=SGI, c=US
+uid: EZsetup
+userPassword:
+uidNumber: 992
+gidNumber: 998
+gecos: System Setup
+homeDirectory: /var/sysadmdesktop/EZsetup
+loginShell: /bin/csh
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=demos, o=SGI, c=US
+uid: demos
+userPassword:
+uidNumber: 993
+gidNumber: 997
+gecos: Demonstration User
+homeDirectory: /usr/demos
+loginShell: /bin/csh
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=OutOfBox, o=SGI, c=US
+uid: OutOfBox
+userPassword:
+uidNumber: 995
+gidNumber: 997
+gecos: Out of Box Experience
+homeDirectory: /usr/people/OutOfBox
+loginShell: /bin/csh
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=guest, o=SGI, c=US
+uid: guest
+userPassword:
+uidNumber: 998
+gidNumber: 998
+gecos: Guest Account
+homeDirectory: /usr/people/guest
+loginShell: /bin/csh
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=4Dgifts, o=SGI, c=US
+uid: 4Dgifts
+userPassword: *
+uidNumber: 999
+gidNumber: 998
+gecos: 4Dgifts Account
+homeDirectory: /usr/people/4Dgifts
+loginShell: /bin/csh
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=nobody, o=SGI, c=US
+uid: nobody
+userPassword: *
+uidNumber: 60001
+gidNumber: 60001
+gecos: SVR4 nobody uid
+homeDirectory: /dev/null
+loginShell: /dev/null
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=noaccess, o=SGI, c=US
+uid: noaccess
+userPassword: *
+uidNumber: 60002
+gidNumber: 60002
+gecos: uid no access
+homeDirectory: /dev/null
+loginShell: /dev/null
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=nobody, o=SGI, c=US
+uid: nobody
+userPassword: *
+uidNumber: 60001
+gidNumber: 60001
+gecos: original nobody uid
+homeDirectory: /dev/null
+loginShell: /dev/null
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=gomez, o=SGI, c=US
+uid: gomez
+userPassword: fRJsjYGR3q7TE
+uidNumber: 37425
+gidNumber: 10
+gecos: Gomez
+homeDirectory: /usr/people/gomez
+loginShell: /usr/bin/tcsh
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: uid=jcgomez, o=SGI, c=US
+uid: jcgomez
+userPassword:
+uidNumber: 14427
+gidNumber: 20
+gecos: Juan Carlos Gomez
+homeDirectory: /home/people/jcgomez
+loginShell: /bin/tcsh
+objectclass: posixAccount
+objectclass: account
+objectclass: top
+
+dn: cn=ip, o=SGI, c=US
+cn: ip
+cn: IP
+ipProtocolNumber: 0
+objectclass: ipProtocol
+objectclass: top
+
+dn: cn=icmp, o=SGI, c=US
+cn: icmp
+cn: ICMP
+ipProtocolNumber: 1
+objectclass: ipProtocol
+objectclass: top
+
+dn: cn=igmp, o=SGI, c=US
+cn: igmp
+cn: IGMP
+ipProtocolNumber: 2
+objectclass: ipProtocol
+objectclass: top
+
+dn: cn=ggp, o=SGI, c=US
+cn: ggp
+cn: GGP
+ipProtocolNumber: 3
+objectclass: ipProtocol
+objectclass: top
+
+dn: cn=tcp, o=SGI, c=US
+cn: tcp
+cn: TCP
+ipProtocolNumber: 6
+objectclass: ipProtocol
+objectclass: top
+
+dn: cn=egp, o=SGI, c=US
+cn: egp
+cn: EGP
+ipProtocolNumber: 8
+objectclass: ipProtocol
+objectclass: top
+
+dn: cn=pup, o=SGI, c=US
+cn: pup
+cn: PUP
+ipProtocolNumber: 12
+objectclass: ipProtocol
+objectclass: top
+
+dn: cn=udp, o=SGI, c=US
+cn: udp
+cn: UDP
+ipProtocolNumber: 17
+objectclass: ipProtocol
+objectclass: top
+
+dn: cn=hmp, o=SGI, c=US
+cn: hmp
+cn: HMP
+ipProtocolNumber: 20
+objectclass: ipProtocol
+objectclass: top
+
+dn: cn=xns-idp, o=SGI, c=US
+cn: xns-idp
+cn: XNS-IDP
+ipProtocolNumber: 22
+objectclass: ipProtocol
+objectclass: top
+
+dn: cn=rdp, o=SGI, c=US
+cn: rdp
+cn: RDP
+ipProtocolNumber: 27
+objectclass: ipProtocol
+objectclass: top
+
+dn: cn=iso-tp4, o=SGI, c=US
+cn: iso-tp4
+cn: ISO-TP4
+ipProtocolNumber: 29
+objectclass: ipProtocol
+objectclass: top
+
+dn: cn=ipv6, o=SGI, c=US
+cn: ipv6
+cn: IPV6
+ipProtocolNumber: 41
+objectclass: ipProtocol
+objectclass: top
+
+dn: cn=rsvp, o=SGI, c=US
+cn: rsvp
+cn: RSVP
+ipProtocolNumber: 46
+objectclass: ipProtocol
+objectclass: top
+
+dn: cn=icmpv6, o=SGI, c=US
+cn: icmpv6
+cn: ICMPV6
+ipProtocolNumber: 58
+objectclass: ipProtocol
+objectclass: top
+
+dn: cn=portmapper, o=SGI, c=US
+cn: portmapper
+cn: portmap
+cn: sunrpc
+oncRpcNumber: 100000
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=rstatd, o=SGI, c=US
+cn: rstatd
+cn: rstat
+cn: rup
+cn: perfmeter
+oncRpcNumber: 100001
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=rusersd, o=SGI, c=US
+cn: rusersd
+cn: rusers
+oncRpcNumber: 100002
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=nfs, o=SGI, c=US
+cn: nfs
+cn: nfsprog
+oncRpcNumber: 100003
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=ypserv, o=SGI, c=US
+cn: ypserv
+cn: ypprog
+oncRpcNumber: 100004
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=mountd, o=SGI, c=US
+cn: mountd
+cn: mount
+cn: showmount
+oncRpcNumber: 100005
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=ypbind, o=SGI, c=US
+cn: ypbind
+oncRpcNumber: 100007
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=walld, o=SGI, c=US
+cn: walld
+cn: rwall
+cn: shutdown
+oncRpcNumber: 100008
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=yppasswdd, o=SGI, c=US
+cn: yppasswdd
+cn: yppasswd
+oncRpcNumber: 100009
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=etherstatd, o=SGI, c=US
+cn: etherstatd
+cn: etherstat
+oncRpcNumber: 100010
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=rquotad, o=SGI, c=US
+cn: rquotad
+cn: rquotaprog
+cn: quota
+cn: rquota
+oncRpcNumber: 100011
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sprayd, o=SGI, c=US
+cn: sprayd
+cn: spray
+oncRpcNumber: 100012
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=3270_mapper, o=SGI, c=US
+cn: 3270_mapper
+oncRpcNumber: 100013
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=rje_mapper, o=SGI, c=US
+cn: rje_mapper
+oncRpcNumber: 100014
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=selection_svc, o=SGI, c=US
+cn: selection_svc
+cn: selnsvc
+oncRpcNumber: 100015
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=database_svc, o=SGI, c=US
+cn: database_svc
+oncRpcNumber: 100016
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=rexd, o=SGI, c=US
+cn: rexd
+cn: rex
+oncRpcNumber: 100017
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=alis, o=SGI, c=US
+cn: alis
+oncRpcNumber: 100018
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sched, o=SGI, c=US
+cn: sched
+oncRpcNumber: 100019
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=llockmgr, o=SGI, c=US
+cn: llockmgr
+oncRpcNumber: 100020
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=nlockmgr, o=SGI, c=US
+cn: nlockmgr
+oncRpcNumber: 100021
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=x25.inr, o=SGI, c=US
+cn: x25.inr
+oncRpcNumber: 100022
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=statmon, o=SGI, c=US
+cn: statmon
+oncRpcNumber: 100023
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=status, o=SGI, c=US
+cn: status
+oncRpcNumber: 100024
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=bootparam, o=SGI, c=US
+cn: bootparam
+oncRpcNumber: 100026
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=ypupdated, o=SGI, c=US
+cn: ypupdated
+cn: ypupdate
+oncRpcNumber: 100028
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=keyserv, o=SGI, c=US
+cn: keyserv
+cn: keyserver
+oncRpcNumber: 100029
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=ttdbserverd, o=SGI, c=US
+cn: ttdbserverd
+cn: ttdbserverd
+oncRpcNumber: 100083
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=autofsd, o=SGI, c=US
+cn: autofsd
+cn: autofsd
+oncRpcNumber: 100099
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_snoopd, o=SGI, c=US
+cn: sgi_snoopd
+cn: snoopd
+cn: snoop
+oncRpcNumber: 391000
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_toolkitbus, o=SGI, c=US
+cn: sgi_toolkitbus
+oncRpcNumber: 391001
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_fam, o=SGI, c=US
+cn: sgi_fam
+oncRpcNumber: 391002
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_notepad, o=SGI, c=US
+cn: sgi_notepad
+cn: notepad
+oncRpcNumber: 391003
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_mountd, o=SGI, c=US
+cn: sgi_mountd
+cn: mount
+cn: showmount
+oncRpcNumber: 391004
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_smtd, o=SGI, c=US
+cn: sgi_smtd
+cn: smtd
+oncRpcNumber: 391005
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_pcsd, o=SGI, c=US
+cn: sgi_pcsd
+cn: pcsd
+oncRpcNumber: 391006
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_nfs, o=SGI, c=US
+cn: sgi_nfs
+oncRpcNumber: 391007
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_rfind, o=SGI, c=US
+cn: sgi_rfind
+cn: rfind
+oncRpcNumber: 391008
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_pod, o=SGI, c=US
+cn: sgi_pod
+cn: pod
+oncRpcNumber: 391009
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_iphone, o=SGI, c=US
+cn: sgi_iphone
+oncRpcNumber: 391010
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_videod, o=SGI, c=US
+cn: sgi_videod
+oncRpcNumber: 391011
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_testcd, o=SGI, c=US
+cn: sgi_testcd
+cn: testcd
+oncRpcNumber: 391012
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi.ha_hbeat, o=SGI, c=US
+cn: sgi.ha_hbeat
+cn: ha_hbeat
+oncRpcNumber: 391013
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi.ha_nc, o=SGI, c=US
+cn: sgi.ha_nc
+cn: ha_nc
+oncRpcNumber: 391014
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi.ha_appmon, o=SGI, c=US
+cn: sgi.ha_appmon
+cn: ha_appmon
+oncRpcNumber: 391015
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_xfsmd, o=SGI, c=US
+cn: sgi_xfsmd
+oncRpcNumber: 391016
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_mediad, o=SGI, c=US
+cn: sgi_mediad
+cn: mediad
+oncRpcNumber: 391017
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi.ha_orcl, o=SGI, c=US
+cn: sgi.ha_orcl
+cn: ha_orcl
+oncRpcNumber: 391018
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi.ha_ifmx, o=SGI, c=US
+cn: sgi.ha_ifmx
+cn: ha_ifmx
+oncRpcNumber: 391019
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi.ha_sybs, o=SGI, c=US
+cn: sgi.ha_sybs
+cn: ha_sybs
+oncRpcNumber: 391020
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi.ha_ifa, o=SGI, c=US
+cn: sgi.ha_ifa
+cn: ha_ifa
+oncRpcNumber: 391021
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391022
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391023
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391024
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391025
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391026
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391027
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391028
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391029
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391030
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391031
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391032
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391033
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391034
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391035
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391036
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391037
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391038
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391039
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391040
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391041
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391042
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391043
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391044
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391045
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391046
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391047
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391048
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391049
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391050
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391051
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391052
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391053
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391054
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391055
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391056
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391057
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391058
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391059
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391060
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391061
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391062
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=sgi_reserved, o=SGI, c=US
+cn: sgi_reserved
+oncRpcNumber: 391063
+objectclass: oncRpc
+objectclass: top
+
+dn: cn=tcpmux, o=SGI, c=US
+cn: tcpmux
+ipServicePort: 1
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=echo, o=SGI, c=US
+cn: echo
+ipServicePort: 7
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=echo, o=SGI, c=US
+cn: echo
+ipServicePort: 7
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=discard, o=SGI, c=US
+cn: discard
+cn: sink
+cn: null
+ipServicePort: 9
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=discard, o=SGI, c=US
+cn: discard
+cn: sink
+cn: null
+ipServicePort: 9
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=systat, o=SGI, c=US
+cn: systat
+cn: users
+ipServicePort: 11
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=daytime, o=SGI, c=US
+cn: daytime
+ipServicePort: 13
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=daytime, o=SGI, c=US
+cn: daytime
+ipServicePort: 13
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=netstat, o=SGI, c=US
+cn: netstat
+ipServicePort: 15
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=qotd, o=SGI, c=US
+cn: qotd
+cn: quote
+ipServicePort: 17
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=chargen, o=SGI, c=US
+cn: chargen
+cn: ttytst
+cn: source
+ipServicePort: 19
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=chargen, o=SGI, c=US
+cn: chargen
+cn: ttytst
+cn: source
+ipServicePort: 19
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=ftp-data, o=SGI, c=US
+cn: ftp-data
+ipServicePort: 20
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=ftp, o=SGI, c=US
+cn: ftp
+ipServicePort: 21
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=telnet, o=SGI, c=US
+cn: telnet
+ipServicePort: 23
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=smtp, o=SGI, c=US
+cn: smtp
+cn: mail
+ipServicePort: 25
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=time, o=SGI, c=US
+cn: time
+cn: timserver
+ipServicePort: 37
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=time, o=SGI, c=US
+cn: time
+cn: timserver
+ipServicePort: 37
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=rlp, o=SGI, c=US
+cn: rlp
+cn: resource
+ipServicePort: 39
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=name, o=SGI, c=US
+cn: name
+ipServicePort: 42
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=whois, o=SGI, c=US
+cn: whois
+cn: nicname
+ipServicePort: 43
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=domain, o=SGI, c=US
+cn: domain
+cn: nameserver
+ipServicePort: 53
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=domain, o=SGI, c=US
+cn: domain
+cn: nameserver
+ipServicePort: 53
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=mtp, o=SGI, c=US
+cn: mtp
+ipServicePort: 57
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=bootp, o=SGI, c=US
+cn: bootp
+cn: bootps
+ipServicePort: 67
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=bootpc, o=SGI, c=US
+cn: bootpc
+ipServicePort: 68
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=tftp, o=SGI, c=US
+cn: tftp
+ipServicePort: 69
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=rje, o=SGI, c=US
+cn: rje
+cn: netrjs
+ipServicePort: 77
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=finger, o=SGI, c=US
+cn: finger
+ipServicePort: 79
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=http, o=SGI, c=US
+cn: http
+ipServicePort: 80
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=link, o=SGI, c=US
+cn: link
+cn: ttylink
+ipServicePort: 87
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=supdup, o=SGI, c=US
+cn: supdup
+ipServicePort: 95
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=hostnames, o=SGI, c=US
+cn: hostnames
+cn: hostname
+ipServicePort: 101
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=iso-tsap, o=SGI, c=US
+cn: iso-tsap
+ipServicePort: 102
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=x400, o=SGI, c=US
+cn: x400
+ipServicePort: 103
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=x400-snd, o=SGI, c=US
+cn: x400-snd
+ipServicePort: 104
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=csnet-ns, o=SGI, c=US
+cn: csnet-ns
+ipServicePort: 105
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=pop-2, o=SGI, c=US
+cn: pop-2
+ipServicePort: 109
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=pop-3, o=SGI, c=US
+cn: pop-3
+ipServicePort: 110
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=sunrpc, o=SGI, c=US
+cn: sunrpc
+cn: rpcbind
+ipServicePort: 111
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=sunrpc, o=SGI, c=US
+cn: sunrpc
+cn: rpcbind
+ipServicePort: 111
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=auth, o=SGI, c=US
+cn: auth
+cn: authentication
+ipServicePort: 113
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=sftp, o=SGI, c=US
+cn: sftp
+ipServicePort: 115
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=uucp-path, o=SGI, c=US
+cn: uucp-path
+ipServicePort: 117
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=nntp, o=SGI, c=US
+cn: nntp
+cn: readnews
+cn: untp
+ipServicePort: 119
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=erpc, o=SGI, c=US
+cn: erpc
+ipServicePort: 121
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=ntp, o=SGI, c=US
+cn: ntp
+ipServicePort: 123
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=loc-srv, o=SGI, c=US
+cn: loc-srv
+ipServicePort: 135
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=loc-srv, o=SGI, c=US
+cn: loc-srv
+ipServicePort: 135
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=imap2, o=SGI, c=US
+cn: imap2
+ipServicePort: 143
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=snmp, o=SGI, c=US
+cn: snmp
+ipServicePort: 161
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=snmp-trap, o=SGI, c=US
+cn: snmp-trap
+cn: snmptrap
+ipServicePort: 162
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=xdmcp, o=SGI, c=US
+cn: xdmcp
+ipServicePort: 177
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=exec, o=SGI, c=US
+cn: exec
+ipServicePort: 512
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=biff, o=SGI, c=US
+cn: biff
+cn: comsat
+ipServicePort: 512
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=login, o=SGI, c=US
+cn: login
+ipServicePort: 513
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=who, o=SGI, c=US
+cn: who
+cn: whod
+ipServicePort: 513
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=shell, o=SGI, c=US
+cn: shell
+cn: cmd
+ipServicePort: 514
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=syslog, o=SGI, c=US
+cn: syslog
+ipServicePort: 514
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=printer, o=SGI, c=US
+cn: printer
+cn: spooler
+ipServicePort: 515
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=talk, o=SGI, c=US
+cn: talk
+ipServicePort: 517
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=ntalk, o=SGI, c=US
+cn: ntalk
+ipServicePort: 518
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=route, o=SGI, c=US
+cn: route
+cn: router
+cn: routed
+ipServicePort: 520
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=timed, o=SGI, c=US
+cn: timed
+cn: timeserver
+ipServicePort: 525
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=tempo, o=SGI, c=US
+cn: tempo
+cn: newdate
+ipServicePort: 526
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=courier, o=SGI, c=US
+cn: courier
+cn: rpc
+ipServicePort: 530
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=conference, o=SGI, c=US
+cn: conference
+cn: chat
+ipServicePort: 531
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=netnews, o=SGI, c=US
+cn: netnews
+cn: readnews
+ipServicePort: 532
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=netwall, o=SGI, c=US
+cn: netwall
+ipServicePort: 533
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=uucp, o=SGI, c=US
+cn: uucp
+cn: uucpd
+ipServicePort: 540
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=remotefs, o=SGI, c=US
+cn: remotefs
+cn: rfs_server
+cn: rfs
+ipServicePort: 556
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=ingreslock, o=SGI, c=US
+cn: ingreslock
+ipServicePort: 1524
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=albd, o=SGI, c=US
+cn: albd
+ipServicePort: 371
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=ta-rauth, o=SGI, c=US
+cn: ta-rauth
+cn: rauth
+ipServicePort: 601
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=kerberos, o=SGI, c=US
+cn: kerberos
+cn: kdc
+ipServicePort: 750
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=kerberos, o=SGI, c=US
+cn: kerberos
+cn: kdc
+ipServicePort: 750
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=krbupdate, o=SGI, c=US
+cn: krbupdate
+cn: kreg
+ipServicePort: 760
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=kpasswd, o=SGI, c=US
+cn: kpasswd
+cn: kpwd
+ipServicePort: 761
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=klogin, o=SGI, c=US
+cn: klogin
+ipServicePort: 543
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=nfs, o=SGI, c=US
+cn: nfs
+cn: nfs
+ipServicePort: 2049
+ipServiceProtocol: udp
+objectclass: ipService
+objectclass: top
+
+dn: cn=nfs, o=SGI, c=US
+cn: nfs
+cn: nfs
+ipServicePort: 2049
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=eklogin, o=SGI, c=US
+cn: eklogin
+ipServicePort: 2105
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=kshell, o=SGI, c=US
+cn: kshell
+cn: krcmd
+ipServicePort: 544
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=x-server, o=SGI, c=US
+cn: x-server
+ipServicePort: 6000
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=sgi-dgl, o=SGI, c=US
+cn: sgi-dgl
+ipServicePort: 5232
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=sgi-arrayd, o=SGI, c=US
+cn: sgi-arrayd
+ipServicePort: 5434
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=realaudio, o=SGI, c=US
+cn: realaudio
+cn: ra
+ipServicePort: 7070
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=wn-http, o=SGI, c=US
+cn: wn-http
+ipServicePort: 8778
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
+
+dn: cn=sgi_iphone, o=SGI, c=US
+cn: sgi_iphone
+ipServicePort: 32769
+ipServiceProtocol: tcp
+objectclass: ipService
+objectclass: top
diff --git a/tests/data/otp/hotp.ldif b/tests/data/otp/hotp.ldif
new file mode 100644
index 0000000..dfd160e
--- /dev/null
+++ b/tests/data/otp/hotp.ldif
@@ -0,0 +1,61 @@
+dn: dc=example, dc=com
+changetype: modify
+add: objectClass
+objectClass: oathHOTPParams
+-
+add: oathOTPLength
+oathOTPLength: 6
+-
+add: oathHOTPLookAhead
+oathHOTPLookAhead: 3
+-
+add: oathHMACAlgorithm
+# SHA-1
+oathHMACAlgorithm: 1.2.840.113549.2.7
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+changetype: modify
+add: objectClass
+objectclass: oathHOTPToken
+-
+add: oathHOTPParams
+oathHOTPParams: dc=example, dc=com
+-
+add: oathSecret
+oathSecret:: PcbKpIJKbSiHZ7IzHiC0MWbLhdk=
+-
+add: oathHOTPCounter
+oathHOTPCounter: 3
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+add: objectClass
+objectClass: oathHOTPParams
+-
+add: oathOTPLength
+oathOTPLength: 8
+-
+add: oathHOTPLookAhead
+oathHOTPLookAhead: 0
+-
+add: oathHMACAlgorithm
+# SHA-512
+oathHMACAlgorithm: 1.2.840.113549.2.11
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+changetype: modify
+add: objectClass
+objectClass: oathHOTPUser
+-
+add: oathHOTPToken
+oathHOTPToken: ou=Information Technology Division,ou=People,dc=example,dc=com
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+changetype: modify
+add: objectClass
+objectClass: oathHOTPUser
+-
+add: oathHOTPToken
+oathHOTPToken: ou=Information Technology Division,ou=People,dc=example,dc=com
diff --git a/tests/data/otp/test001-out.ldif b/tests/data/otp/test001-out.ldif
new file mode 100644
index 0000000..97fa931
--- /dev/null
+++ b/tests/data/otp/test001-out.ldif
@@ -0,0 +1,5 @@
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+oathSecret:: PcbKpIJKbSiHZ7IzHiC0MWbLhdk=
+oathHOTPParams: ou=Alumni Association,ou=People,dc=example,dc=com
+oathHOTPCounter: 12
+
diff --git a/tests/data/otp/totp.ldif b/tests/data/otp/totp.ldif
new file mode 100644
index 0000000..1067dfd
--- /dev/null
+++ b/tests/data/otp/totp.ldif
@@ -0,0 +1,64 @@
+dn: dc=example, dc=com
+changetype: modify
+add: objectClass
+objectClass: oathTOTPParams
+-
+add: oathOTPLength
+oathOTPLength: 6
+-
+add: oathTOTPTimeStepPeriod
+oathTOTPTimeStepPeriod: 30
+-
+add: oathTOTPTimeStepWindow
+oathTOTPTimeStepWindow: 3
+-
+add: oathHMACAlgorithm
+# SHA-1
+oathHMACAlgorithm: 1.2.840.113549.2.7
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+changetype: modify
+add: objectClass
+objectclass: oathTOTPToken
+-
+add: oathTOTPParams
+oathTOTPParams: dc=example, dc=com
+-
+add: oathSecret
+oathSecret:: PcbKpIJKbSiHZ7IzHiC0MWbLhdk=
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+add: objectClass
+objectClass: oathTOTPParams
+-
+add: oathOTPLength
+oathOTPLength: 8
+-
+add: oathTOTPTimeStepPeriod
+oathTOTPTimeStepPeriod: 30
+-
+add: oathTOTPTimeStepWindow
+oathTOTPTimeStepWindow: 0
+-
+add: oathHMACAlgorithm
+# SHA-512
+oathHMACAlgorithm: 1.2.840.113549.2.11
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+changetype: modify
+add: objectClass
+objectClass: oathTOTPUser
+-
+add: oathTOTPToken
+oathTOTPToken: ou=Information Technology Division,ou=People,dc=example,dc=com
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+changetype: modify
+add: objectClass
+objectClass: oathTOTPUser
+-
+add: oathTOTPToken
+oathTOTPToken: ou=Information Technology Division,ou=People,dc=example,dc=com
diff --git a/tests/data/passwd.ldif b/tests/data/passwd.ldif
new file mode 100644
index 0000000..1f9ecbb
--- /dev/null
+++ b/tests/data/passwd.ldif
@@ -0,0 +1,37 @@
+dn: dc=example,dc=com
+objectclass: dcobject
+dc: example
+objectclass: organization
+o: Example, Inc.
+
+dn: cn=md5,dc=example,dc=com
+objectclass: person
+cn: md5
+sn: md5
+userpassword:: e01ENX1YcjRpbE96UTRQQ09xM2FRMHFidWFRPT0=
+
+dn: cn=smd5,dc=example,dc=com
+objectclass: person
+cn: smd5
+sn: smd5
+userpassword: secret
+
+dn: cn=sha,dc=example,dc=com
+objectclass: person
+cn: sha
+sn: sha
+userpassword:: e1NIQX01ZW42RzZNZXpScm9UM1hLcWtkUE9tWS9CZlE9
+
+dn: cn=ssha,dc=example,dc=com
+objectclass: person
+cn: ssha
+sn: ssha
+userpassword: secret
+
+dn: cn=argon2,dc=example,dc=com
+objectclass: person
+cn: argon2
+sn: argon2
+userPassword:: e0FSR09OMn0kYXJnb24yaSR2PTE5JG09NDA5Nix0PTMscD0xJHZTc1orVnZjM
+ UhoZzc0WFNrdVZLOFEkd1B2UUc0blFMS2xaSkRGU0tna2k0L2NYejNLT2lOYXpwL2VDWkFWOFlt
+ Zw==
diff --git a/tests/data/ppolicy.ldif b/tests/data/ppolicy.ldif
new file mode 100644
index 0000000..d4d697d
--- /dev/null
+++ b/tests/data/ppolicy.ldif
@@ -0,0 +1,69 @@
+dn: dc=example, dc=com
+objectClass: top
+objectClass: organization
+objectClass: dcObject
+o: Example
+dc: example
+
+dn: ou=People, dc=example, dc=com
+objectClass: top
+objectClass: organizationalUnit
+ou: People
+
+dn: ou=Policies, dc=example, dc=com
+objectClass: top
+objectClass: organizationalUnit
+ou: Policies
+
+dn: cn=Standard Policy, ou=Policies, dc=example, dc=com
+objectClass: top
+objectClass: device
+objectClass: pwdPolicy
+cn: Standard Policy
+pwdAttribute: 2.5.4.35
+pwdLockoutDuration: 15
+pwdInHistory: 6
+pwdCheckQuality: 2
+pwdExpireWarning: 10
+pwdMaxAge: 30
+pwdMinLength: 5
+pwdMaxLength: 13
+pwdGraceAuthnLimit: 3
+pwdAllowUserChange: TRUE
+pwdMustChange: TRUE
+pwdMaxFailure: 3
+pwdFailureCountInterval: 120
+pwdSafeModify: TRUE
+pwdLockout: TRUE
+
+dn: uid=nd, ou=People, dc=example, dc=com
+objectClass: top
+objectClass: person
+objectClass: inetOrgPerson
+cn: Neil Dunbar
+uid: nd
+sn: Dunbar
+givenName: Neil
+userPassword: testpassword
+
+dn: uid=ndadmin, ou=People, dc=example, dc=com
+objectClass: top
+objectClass: person
+objectClass: inetOrgPerson
+cn: Neil Dunbar (Admin)
+uid: ndadmin
+sn: Dunbar
+givenName: Neil
+userPassword: testpw
+
+dn: uid=test, ou=People, dc=example, dc=com
+objectClass: top
+objectClass: person
+objectClass: inetOrgPerson
+cn: test test
+uid: test
+sn: Test
+givenName: Test
+userPassword: kfhgkjhfdgkfd
+pwdPolicySubEntry: cn=No Policy, ou=Policies, dc=example, dc=com
+
diff --git a/tests/data/proxycache.out b/tests/data/proxycache.out
new file mode 100644
index 0000000..d879fd8
--- /dev/null
+++ b/tests/data/proxycache.out
@@ -0,0 +1,258 @@
+# Query 1: filter:(sn=Jon) attrs:all (expect nothing)
+# Query 2: filter:(|(cn=*Jon*)(sn=Jon*)) attrs:cn sn title uid
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+title: Mad Cow Researcher, UM Alumni Association
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+title: Senior Manager, Information Technology Division
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+title: System Administrator, Information Technology Division
+
+# Query 3: filter:(sn=Smith*) attrs:cn sn uid
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+
+# Query 4: filter:(sn=Doe*) attrs:cn sn title uid
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+title: Senior Manager, Information Technology Division
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+title: System Administrator, Information Technology Division
+
+# Query 5: filter:(uid=johnd) attrs:mail postaladdress telephonenumber cn uid
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+cn: John Doe
+cn: Jonathon Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+mail: johnd@mailgw.example.com
+telephoneNumber: +1 313 555 9394
+
+# Query 6: filter:(mail=*@mail.alumni.example.com) attrs:cn sn title uid
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+title: Mad Cow Researcher, UM Alumni Association
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+title: Telemarketer, UM Alumni Association
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+title: Director, UM Alumni Association
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+
+# Query 7: filter:(mail=*) attrs:cn sn title uid
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+title: Director, Embedded Systems
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+title: Mad Cow Researcher, UM Alumni Association
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+title: Senior Manager, Information Technology Division
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+title: Telemarketer, UM Alumni Association
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+title: System Administrator, Information Technology Division
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+title: Director, UM Alumni Association
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+
+# Query 8: filter:(mail=*example.com) attrs:cn sn title uid
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+
+# Query 9: filter:(uid=b*) attrs:mail
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+mail: bjensen@mailgw.example.com
+
+# Query 10: filter:(|(cn=All Staff)(sn=All Staff)) attrs:sn cn title uid undefinedAttr
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+cn: All Staff
+
+# Query 11: filter:(|(cn=*Jones)(sn=Jones)) attrs:cn sn title uid
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+title: Mad Cow Researcher, UM Alumni Association
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+title: Senior Manager, Information Technology Division
+
+# Query 12: filter:(sn=Smith) attrs:cn sn title uid
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+title: Telemarketer, UM Alumni Association
+
+# Query 13: filter:(uid=bjorn) attrs:mail postaladdress telephonenumber cn uid
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+cn: Bjorn Jensen
+cn: Biiff Jensen
+uid: bjorn
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+telephoneNumber: +1 313 555 0355
+
+# Query 14: filter:(mail=jaj@mail.alumni.example.com) attrs:cn sn title uid
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+title: Mad Cow Researcher, UM Alumni Association
+
+# Query 15: filter:(mail=*example.com) attrs:cn sn title uid
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+
+# Query 16: filter:(uid=b*) attrs:mail
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+mail: bjensen@mailgw.example.com
+
+# Query 17: filter:(|(cn=All Staff)(sn=All Staff)) attrs:sn cn title uid undefinedAttr
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+cn: All Staff
+
diff --git a/tests/data/referrals.ldif b/tests/data/referrals.ldif
new file mode 100644
index 0000000..8824ae1
--- /dev/null
+++ b/tests/data/referrals.ldif
@@ -0,0 +1,29 @@
+dn: c=US
+c: US
+objectclass: country
+
+dn: o=ABC,c=US
+o: ABC
+ref: ldap://hostA/o=abc,c=us HostA
+ref: ldap://hostB HostB
+objectclass: referral
+objectclass: extensibleObject
+
+dn: o=XYZ,c=US
+o: XYZ
+ref: ldap://hostC/o=xyz,c=us HostC
+objectclass: referral
+objectclass: extensibleObject
+
+dn: o=Example,c=US
+o: Example
+objectclass: organization
+
+dn: cn=Manager,o=Example,c=US
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userpassword:: c2VjcmV0
+objectclass: person
diff --git a/tests/data/referrals.out b/tests/data/referrals.out
new file mode 100644
index 0000000..fac5b74
--- /dev/null
+++ b/tests/data/referrals.out
@@ -0,0 +1,239 @@
+# extended LDIF
+#
+# LDAPv3
+# base <c=US> with scope sub
+# filter: (objectClass=referral)
+# requesting: * ref
+# with manageDSAit critical control
+#
+
+# ABC, US
+dn: o=ABC,c=US
+o: ABC
+ref: ldap://hostA/o=abc,c=us HostA
+ref: ldap://hostB HostB
+objectClass: referral
+objectClass: extensibleObject
+
+# XYZ, US
+dn: o=XYZ,c=US
+o: XYZ
+ref: ldap://hostC/o=xyz,c=us HostC
+objectClass: referral
+objectClass: extensibleObject
+
+# search result
+search: 2
+result: 0 Success
+
+# numResponses: 3
+# numEntries: 2
+# extended LDIF
+#
+# LDAPv3
+# base <o=abc,c=US> with scope sub
+# filter: (objectClass=referral)
+# requesting: * ref
+# with manageDSAit critical control
+#
+
+# ABC, US
+dn: o=ABC,c=US
+o: ABC
+ref: ldap://hostA/o=abc,c=us HostA
+ref: ldap://hostB HostB
+objectClass: referral
+objectClass: extensibleObject
+
+# search result
+search: 2
+result: 0 Success
+
+# numResponses: 2
+# numEntries: 1
+# extended LDIF
+#
+# LDAPv3
+# base <uid=xxx,o=abc,c=US> with scope sub
+# filter: (objectClass=referral)
+# requesting: * ref
+# with manageDSAit critical control
+#
+
+# search result
+search: 2
+result: 10 Referral
+matchedDN: o=ABC,c=US
+ref: ldap://hostA/uid=xxx,o=abc,c=us??sub
+ref: ldap://hostB/uid=xxx,o=abc,c=US??sub
+
+# numResponses: 1
+# extended LDIF
+#
+# LDAPv3
+# base <c=US> with scope base
+# filter: (objectclass=*)
+# requesting: 1.1
+#
+
+# US
+dn: c=US
+
+# search result
+search: 2
+result: 0 Success
+
+# numResponses: 2
+# numEntries: 1
+# extended LDIF
+#
+# LDAPv3
+# base <c=US> with scope one
+# filter: (objectclass=*)
+# requesting: 1.1
+#
+
+# Example, Inc., US
+dn: o=Example,c=US
+
+# search reference
+ref: ldap://hostA/o=abc,c=us??base
+ref: ldap://hostB/o=ABC,c=US??base
+
+# search reference
+ref: ldap://hostC/o=xyz,c=us??base
+
+# search result
+search: 2
+result: 0 Success
+
+# numResponses: 4
+# numEntries: 1
+# numReferences: 2
+# extended LDIF
+#
+# LDAPv3
+# base <c=US> with scope sub
+# filter: (objectclass=*)
+# requesting: 1.1
+#
+
+# Example, Inc., US
+dn: o=Example,c=US
+
+# Manager, Example, Inc., US
+dn: cn=Manager,o=Example,c=US
+
+# US
+dn: c=US
+
+# search reference
+ref: ldap://hostA/o=abc,c=us??sub
+ref: ldap://hostB/o=ABC,c=US??sub
+
+# search reference
+ref: ldap://hostC/o=xyz,c=us??sub
+
+# search result
+search: 2
+result: 0 Success
+
+# numResponses: 6
+# numEntries: 3
+# numReferences: 2
+# extended LDIF
+#
+# LDAPv3
+# base <o=abc,c=US> with scope base
+# filter: (objectclass=*)
+# requesting: 1.1
+#
+
+# search result
+search: 2
+result: 10 Referral
+matchedDN: o=ABC,c=US
+ref: ldap://hostA/o=abc,c=us??base
+ref: ldap://hostB/o=abc,c=US??base
+
+# numResponses: 1
+# extended LDIF
+#
+# LDAPv3
+# base <o=abc,c=US> with scope one
+# filter: (objectclass=*)
+# requesting: 1.1
+#
+
+# search result
+search: 2
+result: 10 Referral
+matchedDN: o=ABC,c=US
+ref: ldap://hostA/o=abc,c=us??one
+ref: ldap://hostB/o=abc,c=US??one
+
+# numResponses: 1
+# extended LDIF
+#
+# LDAPv3
+# base <o=abc,c=US> with scope sub
+# filter: (objectclass=*)
+# requesting: 1.1
+#
+
+# search result
+search: 2
+result: 10 Referral
+matchedDN: o=ABC,c=US
+ref: ldap://hostA/o=abc,c=us??sub
+ref: ldap://hostB/o=abc,c=US??sub
+
+# numResponses: 1
+# extended LDIF
+#
+# LDAPv3
+# base <uid=xxx,o=abc,c=US> with scope base
+# filter: (objectclass=*)
+# requesting: 1.1
+#
+
+# search result
+search: 2
+result: 10 Referral
+matchedDN: o=ABC,c=US
+ref: ldap://hostA/uid=xxx,o=abc,c=us??base
+ref: ldap://hostB/uid=xxx,o=abc,c=US??base
+
+# numResponses: 1
+# extended LDIF
+#
+# LDAPv3
+# base <uid=xxx,o=abc,c=US> with scope one
+# filter: (objectclass=*)
+# requesting: 1.1
+#
+
+# search result
+search: 2
+result: 10 Referral
+matchedDN: o=ABC,c=US
+ref: ldap://hostA/uid=xxx,o=abc,c=us??one
+ref: ldap://hostB/uid=xxx,o=abc,c=US??one
+
+# numResponses: 1
+# extended LDIF
+#
+# LDAPv3
+# base <uid=xxx,o=abc,c=US> with scope sub
+# filter: (objectclass=*)
+# requesting: 1.1
+#
+
+# search result
+search: 2
+result: 10 Referral
+matchedDN: o=ABC,c=US
+ref: ldap://hostA/uid=xxx,o=abc,c=us??sub
+ref: ldap://hostB/uid=xxx,o=abc,c=US??sub
+
+# numResponses: 1
diff --git a/tests/data/regressions/README b/tests/data/regressions/README
new file mode 100644
index 0000000..25aed75
--- /dev/null
+++ b/tests/data/regressions/README
@@ -0,0 +1,20 @@
+This directory contains test related to regression tracking that require
+a specific setup and a complete test. Each regression test must be
+contained in a test directory whose name is "its<number>", where <number>
+is the ITS number, and it must be entirely executed by a script, contained
+in that directory and with the same name of the directory. It can exploit
+all the helpers provided for common tests (variables in scripts/defines.sh,
+data files in data/, ...), but it should simultaneously be as self contained
+and as general as possible. Warning: occasionally, data files and
+shell variables may change, so limit their use to real needs.
+
+For example, if an issue only appears with a certain database type, the
+test itself should only run when invoked for that database type;
+otherwise, if the issue appears whatever backend is used, the test should
+be parameteric, so that it is run with the backend selected at run-time
+via the "-b" switch of the "run" script.
+
+Regression tests are prepared on a voluntary basis, so don't expect all
+bugs to have a test any soon. When the issue reporter provides a simple,
+yet complete means to reproduce the bug she's reporting, this may speed up
+the process. In case, only put neutral data in bug exploitation reports.
diff --git a/tests/data/regressions/its4184/README b/tests/data/regressions/its4184/README
new file mode 100644
index 0000000..82ced51
--- /dev/null
+++ b/tests/data/regressions/its4184/README
@@ -0,0 +1 @@
+ITS#4184: fixed in 2.3.14
diff --git a/tests/data/regressions/its4184/adds.ldif b/tests/data/regressions/its4184/adds.ldif
new file mode 100644
index 0000000..439cca4
--- /dev/null
+++ b/tests/data/regressions/its4184/adds.ldif
@@ -0,0 +1,83 @@
+dn: dc=example,dc=com
+objectClass: domain
+dc: example
+
+dn: cn=Manager,dc=example,dc=com
+objectClass: organizationalRole
+cn: Manager
+description: Directory Manager
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: People
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: uid=user1,ou=people,dc=example,dc=com
+objectClass: person
+objectClass: posixAccount
+cn: User 1
+sn: User 1
+uid: user1
+uidNumber: 500
+userPassword: abc
+homeDirectory: /home/user1
+gidNumber: 10
+gecos: User 1
+
+dn: cn=A Group,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: A Group
+member: uid=user1,ou=people,dc=example,dc=com
+
+dn: cn=Another Group,ou=Groups,dc=example,dc=com
+cn: Another Group
+objectClass: groupOfNames
+member: uid=user1,ou=People,dc=example,dc=com
+member: uid=user2,ou=People,dc=example,dc=com
+
+dn: uid=user3,ou=people,dc=example,dc=com
+objectClass: person
+objectClass: posixAccount
+uid: user3
+uidNumber: 5387
+homeDirectory: /home/user3
+loginShell: /bin/false
+gecos: Consumer
+gidNumber: 100
+userPassword: abc
+cn: Consumer
+sn: Consumer
+
+dn: uid=user2,ou=people,dc=example,dc=com
+objectClass: person
+objectClass: posixAccount
+cn: User 2
+sn: User 2
+uid: user2
+uidNumber: 23071
+gecos: User 2
+loginShell: /bin/false
+homeDirectory: /home/user2
+gidNumber: 100
+userPassword: abc
+
+dn: ou=Special,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Special
+
+dn: uid=special1,ou=Special,dc=example,dc=com
+objectClass: person
+objectClass: posixAccount
+cn: Special 1
+sn: Special 1
+uid: special1
+uidNumber: 6319
+homeDirectory: /home/special1
+gecos: Special1
+loginShell: /bin/false
+userPassword: abc
+gidNumber: 100
+
diff --git a/tests/data/regressions/its4184/its4184 b/tests/data/regressions/its4184/its4184
new file mode 100755
index 0000000..1b9e216
--- /dev/null
+++ b/tests/data/regressions/its4184/its4184
@@ -0,0 +1,90 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $DBDIR1A $DBDIR2A
+
+ITS=4184
+ITSDIR=$DATADIR/regressions/its$ITS
+USER="uid=user1,ou=People,dc=example,dc=com"
+PASS="abc"
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $ITSDIR/slapd.conf > $CONF1
+$SLAPADD -f $CONF1 -l $ITSDIR/adds.ldif
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ '(objectClass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo ""
+echo " This test applies a round of updates __after__ grabbing a lock"
+echo " that, before the fix, was not correctly released; in case "
+echo " of failure, the second round of updates will deadlock."
+echo " This issue was fixed in OpenLDAP 2.3.14."
+echo ""
+
+for S in 1 2 ; do
+ FILE="${ITSDIR}/mods.ldif"
+ echo "${S}) Applying `basename ${FILE}`..."
+ $LDAPMODIFY -v -D "$USER" -w $PASS -H $URI1 \
+ -f "${FILE}" > $TESTOUT 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ sleep 1
+done
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/data/regressions/its4184/mods.ldif b/tests/data/regressions/its4184/mods.ldif
new file mode 100644
index 0000000..93fe76e
--- /dev/null
+++ b/tests/data/regressions/its4184/mods.ldif
@@ -0,0 +1,15 @@
+dn: cn=Another Group,ou=Groups,dc=example,dc=com
+changetype: modify
+add: member
+member: uid=user3,ou=People,dc=example,dc=com
+
+dn: cn=Another Group,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: member
+member: uid=user3,ou=people,dc=example,dc=com
+
+dn: uid=special1,ou=Special,dc=example,dc=com
+changetype: modify
+replace: sn
+sn: NewName
+
diff --git a/tests/data/regressions/its4184/slapd.conf b/tests/data/regressions/its4184/slapd.conf
new file mode 100644
index 0000000..ed23ef8
--- /dev/null
+++ b/tests/data/regressions/its4184/slapd.conf
@@ -0,0 +1,59 @@
+# $OpenLDAP$
+#
+# ITS 4184 slapd.conf
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/nis.schema
+include @SCHEMADIR@/misc.schema
+
+pidfile @TESTDIR@/slapd.pid
+argsfile @TESTDIR@/slapd.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+loglevel 0
+
+# ACL issue: with this ACL doesn't show up
+#access to * by * write
+
+# database access control definitions
+access to attrs=userPassword
+ by self write
+ by group="cn=A Group,ou=Groups,dc=example,dc=com" write
+ by group="cn=Another Group,ou=Groups,dc=example,dc=com" write
+ by anonymous auth
+
+access to *
+ by self write
+ by group="cn=Another Group,ou=Groups,dc=example,dc=com" write
+ by * read
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "ou=Special,dc=example,dc=com"
+subordinate
+rootdn "cn=Manager,dc=example,dc=com"
+#~null~#directory @TESTDIR@/db.2.a
+
+# Indices to maintain
+#indexdb#index default pres,eq
+#indexdb#index objectClass eq
+#indexdb#index sn pres,eq,sub
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#null#bind on
+#~null~#directory @TESTDIR@/db.1.a
+
+# Indices to maintain
+#indexdb#index default pres,eq
+#indexdb#index objectClass eq
+#indexdb#index sn pres,eq,sub
+
diff --git a/tests/data/regressions/its4326/its4326 b/tests/data/regressions/its4326/its4326
new file mode 100755
index 0000000..a7c9025
--- /dev/null
+++ b/tests/data/regressions/its4326/its4326
@@ -0,0 +1,224 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $BACKLDAP = "ldapno" ; then
+ echo "LDAP backend not available, test skipped"
+ exit 0
+fi
+
+if test $BACKEND = "ldap" ; then
+ echo "LDAP backend not valid, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+ITS=4326
+ITSDIR=$DATADIR/regressions/its$ITS
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+$SLAPADD -f $CONF1 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+SERVERPID=$!
+if test $WAIT != 0 ; then
+ echo SERVERPID $SERVERPID
+ read foo
+fi
+KILLPIDS="$SERVERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting proxy slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $ITSDIR/slapd.conf > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PROXYPID=$!
+if test $WAIT != 0 ; then
+ echo PROXYPID $PROXYPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PROXYPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that proxy slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Searching the proxy..."
+$LDAPSEARCH -b "$BASEDN" -H $URI2 \
+ '(objectClass=*)' > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Re-searching the proxy..."
+$LDAPSEARCH -b "$BASEDN" -H $URI2 \
+ '(objectClass=*)' > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+SLEEP=2
+echo "Stopping the server and sleeping $SLEEP seconds..."
+kill -HUP "$SERVERPID"
+wait $SERVERPID
+sleep $SLEEP
+KILLPIDS="$PROXYPID"
+
+echo "Searching the proxy..."
+$LDAPSEARCH -b "$BASEDN" -H $URI2 \
+ '(objectClass=*)' > /dev/null 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapsearch should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+52)
+ echo "ldapsearch failed ($RC)"
+ ;;
+*)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Re-searching the proxy..."
+$LDAPSEARCH -b "$BASEDN" -H $URI2 \
+ '(objectClass=*)' > /dev/null 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapsearch should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+52)
+ echo "ldapsearch failed ($RC)"
+ ;;
+*)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Restarting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+SERVERPID=$!
+if test $WAIT != 0 ; then
+ echo SERVERPID $SERVERPID
+ read foo
+fi
+KILLPIDS="$SERVERPID $PROXYPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Searching the proxy..."
+$LDAPSEARCH -b "$BASEDN" -H $URI2 \
+ '(objectClass=*)' > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Re-searching the proxy..."
+$LDAPSEARCH -b "$BASEDN" -H $URI2 \
+ '(objectClass=*)' > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/data/regressions/its4326/slapd.conf b/tests/data/regressions/its4326/slapd.conf
new file mode 100644
index 0000000..cbdcb70
--- /dev/null
+++ b/tests/data/regressions/its4326/slapd.conf
@@ -0,0 +1,43 @@
+# proxy slapd config -- for regression of back-ldap server unavailable issue
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+include @DATADIR@/test.schema
+
+#
+pidfile @TESTDIR@/slapd.2.pid
+argsfile @TESTDIR@/slapd.2.args
+
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database ldap
+suffix "dc=example,dc=com"
+uri @URI1@
+idassert-bind bindmethod=simple
+ binddn="cn=manager,dc=example,dc=com"
+ credentials="secret"
+ mode=self
+ flags=non-prescriptive
+
+database monitor
diff --git a/tests/data/regressions/its4336/its4336 b/tests/data/regressions/its4336/its4336
new file mode 100755
index 0000000..a3a0b12
--- /dev/null
+++ b/tests/data/regressions/its4336/its4336
@@ -0,0 +1,139 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $BACKLDAP = "ldapno" ; then
+ echo "LDAP backend not available, test skipped"
+ exit 0
+fi
+
+if test $RETCODE = retcodeno; then
+ echo "Retcode overlay not available, test skipped"
+ exit 0
+fi
+
+if test "$BACKEND" = "ldap"; then
+ echo "LDAP backend not valid, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1
+
+ITS=4336
+ITSDIR=$DATADIR/regressions/its$ITS
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $RETCODECONF > $CONF1
+$SLAPADD -f $CONF1 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+SERVERPID=$!
+if test $WAIT != 0 ; then
+ echo SERVERPID $SERVERPID
+ read foo
+fi
+KILLPIDS="$SERVERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting chain slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $ITSDIR/slapd.conf > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PROXYPID=$!
+if test $WAIT != 0 ; then
+ echo PROXYPID $PROXYPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PROXYPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that chain slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Searching the chain..."
+echo "
+ Search an entry that causes a successful result to be returned
+ with a 2 second delay; since size/time limits were erroneously
+ set to 0/0, which internally means 0 instead of unlimited, the
+ underlying back-ldap search timed out.
+"
+$LDAPSEARCH -b "cn=success w/ delay,ou=RetCodes,$BASEDN" -H $URI2 \
+ '(objectClass=*)' > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Re-searching the chain..."
+$LDAPSEARCH -b "cn=success w/ delay,ou=RetCodes,$BASEDN" -H $URI2 \
+ '(objectClass=*)' > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/data/regressions/its4336/slapd.conf b/tests/data/regressions/its4336/slapd.conf
new file mode 100644
index 0000000..3d05cf3
--- /dev/null
+++ b/tests/data/regressions/its4336/slapd.conf
@@ -0,0 +1,34 @@
+# stand-alone slapd config -- for testing (with indexing)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+include @DATADIR@/test.schema
+
+#
+pidfile @TESTDIR@/slapd.2.pid
+argsfile @TESTDIR@/slapd.2.args
+
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+
+# no database; only a referral to another DSA, with anonymous chaining
+referral "@URI1@"
+overlay chain
+
+database monitor
diff --git a/tests/data/regressions/its4448/its4448 b/tests/data/regressions/its4448/its4448
new file mode 100755
index 0000000..281bc95
--- /dev/null
+++ b/tests/data/regressions/its4448/its4448
@@ -0,0 +1,310 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+echo ""
+
+if test $BACKMETA = metano ; then
+ echo "meta backend not available, test skipped"
+ exit 0
+fi
+
+# to be removed some time...
+if test "x$TEST_META" = "xno" ; then
+ echo '### Test disabled by "TEST_META=no"; unset TEST_META to re-enable'
+ echo ""
+ exit 0
+else
+ echo "### this test is experimental; in case of problems,"
+ echo "### set \"TEST_META=no\" to disable, and report thru"
+ echo "### the Issue Tracking System <http://www.openldap.org/its/>"
+ echo ""
+fi
+
+if test x$TESTLOOPS = x ; then
+ TESTLOOPS=50
+fi
+
+rm -rf $TESTDIR
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+ITS=4448
+ITSDIR=$DATADIR/regressions/its$ITS
+ITSCONF=$ITSDIR/slapd-meta.conf
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDERED > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to add the referral..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD << EOMODS \
+ > $TESTOUT 2>&1
+dn: cn=Meta,dc=example,dc=com
+objectClass: referral
+objectClass: extensibleObject
+cn: Meta
+ref: ${URI2}ou=Meta,dc=example,dc=com
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $METACONF2 > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$METAMANAGERDN" -H $URI2 -w $PASSWD < \
+ $LDIFMETA >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT3..."
+. $CONFFILTER $BACKEND < $ITSCONF > $CONF3
+$SLAPD -f $CONF3 -h $URI3 -d $LVL > $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI3 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+mkdir -p $TESTDIR/$DATADIR
+METABASEDN="o=Example,c=US"
+#for f in $DATADIR/do_* ; do
+# sed -e "s;$BASEDN;$METABASEDN;" $f > $TESTDIR/$f
+#done
+
+# add a read that matches only the local database, but selects
+# also the remote as candidate; this should be removed to compare
+# execution times with test008...
+#for f in $TESTDIR/$DATADIR/do_read.* ; do
+# echo "ou=Meta,$METABASEDN" >> $f
+#done
+
+# add a read that matches a referral in the local database only,
+# but selects also the remote as candidate; this should be removed
+# to compare execution times with test008...
+#for f in $TESTDIR/$DATADIR/do_read.* ; do
+# echo "cn=Somewhere,ou=Meta,$METABASEDN" >> $f
+#done
+
+# add a bind that resolves to a referral
+#for f in $TESTDIR/$DATADIR/do_bind.* ; do
+# echo "cn=Foo,ou=Meta,$METABASEDN" >> $f
+# echo "bar" >> $f
+# echo "" >> $f
+# echo "" >> $f
+#done
+
+echo \
+"$METABASEDN
+(cn=John Belushi)
+$METABASEDN
+(cn=Meta)
+$METABASEDN
+(cn=Foo Bar)
+$METABASEDN
+(cn=Dan Aykroyd)
+$METABASEDN
+(cn=John Belushi)
+$METABASEDN
+(cn=Meta)
+$METABASEDN
+(cn=Foo Bar)
+$METABASEDN
+(cn=Dan Aykroyd)
+$METABASEDN
+(cn=John Belushi)
+$METABASEDN
+(cn=Meta)
+$METABASEDN
+(cn=Foo Bar)
+$METABASEDN
+(cn=Dan Aykroyd)
+$METABASEDN
+(cn=John Belushi)
+$METABASEDN
+(cn=Meta)
+$METABASEDN
+(cn=Foo Bar)
+$METABASEDN
+(cn=Dan Aykroyd)
+$METABASEDN
+(cn=John Belushi)
+$METABASEDN
+(cn=Meta)
+$METABASEDN
+(cn=Foo Bar)
+$METABASEDN
+(cn=Dan Aykroyd)
+$METABASEDN
+(cn=John Belushi)
+$METABASEDN
+(cn=Meta)
+$METABASEDN
+(cn=Foo Bar)
+$METABASEDN
+(cn=Dan Aykroyd)
+$METABASEDN
+(cn=John Belushi)
+$METABASEDN
+(cn=Meta)
+$METABASEDN
+(cn=Foo Bar)
+$METABASEDN
+(cn=Dan Aykroyd)
+$METABASEDN
+(cn=John Belushi)
+$METABASEDN
+(cn=Meta)
+$METABASEDN
+(cn=Foo Bar)
+$METABASEDN
+(cn=Dan Aykroyd)" \
+> $TESTDIR/$DATADIR/do_search.0
+
+echo "Using tester for concurrent server access..."
+$SLAPDTESTER -P "$PROGDIR" -d "$TESTDIR/$DATADIR" -H $URI3 \
+ -D "cn=Manager,$METABASEDN" -w $PASSWD -l $TESTLOOPS -r 20 -FF
+RC=$?
+
+if test $RC != 0 ; then
+ echo "slapd-tester failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to retrieve all the entries..."
+$LDAPSEARCH -S "" -b "$METABASEDN" -H $URI3 \
+ '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $METACONCURRENCYOUT > $LDIFFLT
+echo "Comparing filter output..."
+$BCMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - slapd-meta search/modification didn't succeed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/data/regressions/its4448/slapd-meta.conf b/tests/data/regressions/its4448/slapd-meta.conf
new file mode 100644
index 0000000..918a970
--- /dev/null
+++ b/tests/data/regressions/its4448/slapd-meta.conf
@@ -0,0 +1,58 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.m.pid
+argsfile @TESTDIR@/slapd.m.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#relaymod#modulepath ../servers/slapd/back-relay/
+#relaymod#moduleload back_relay.la
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+#metamod#modulepath ../servers/slapd/back-meta/
+#metamod#moduleload back_meta.la
+#rwmmod#modulepath ../servers/slapd/overlays/
+#rwmmod#moduleload rwm.la
+
+# seems to improve behavior under very heavy load
+# (i.e. it alleviates load on target systems)
+threads 8
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database meta
+suffix "o=Example,c=US"
+rootdn "cn=Manager,o=Example,c=US"
+rootpw secret
+nretries 100
+chase-referrals yes
+
+uri "@URI1@o=Example,c=US"
+suffixmassage "o=Example,c=US" "dc=example,dc=com"
+idassert-bind bindmethod=simple
+ binddn="cn=manager,dc=example,dc=com"
+ credentials=secret
+ mode=none
+idassert-authzFrom "*"
+
+database monitor
diff --git a/tests/data/regressions/its6794/its6794 b/tests/data/regressions/its6794/its6794
new file mode 100755
index 0000000..6188df6
--- /dev/null
+++ b/tests/data/regressions/its6794/its6794
@@ -0,0 +1,84 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+rm -rf $TESTDIR
+
+mkdir -p $TESTDIR $DBDIR1A $DBDIR1B $DBDIR1C
+ITS=6794
+ITSDIR=$DATADIR/regressions/its$ITS
+ITSCONF=$ITSDIR/slapd-glue.conf
+
+echo "Running multi-threaded slapadd in quick mode to build glued slapd databases..."
+. $CONFFILTER $BACKEND < $ITSCONF > $CONF1
+$SLAPADD -q -d $LVL -f $CONF1 -l $LDIFORDERED > $SLAPADDLOG1 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to retrieve all the entries..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -b "$BASEDN" -H $URI1 > $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s ldif=e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER -s ldif=e < $LDIFGLUED > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - database was not created correctly"
+ echo $SEARCHFLT $LDIFFLT
+ $DIFF $SEARCHFLT $LDIFFLT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/data/regressions/its6794/slapd-glue.conf b/tests/data/regressions/its6794/slapd-glue.conf
new file mode 100644
index 0000000..9c27168
--- /dev/null
+++ b/tests/data/regressions/its6794/slapd-glue.conf
@@ -0,0 +1,64 @@
+# stand-alone slapd config -- for backglue testing (with indexing)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+tool-threads 4
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "ou=Information Technology Division,ou=People,dc=example,dc=com"
+subordinate
+rootdn "cn=Manager, dc=example,dc=com"
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectclass eq
+#indexdb#index uid pres,eq,sub
+#indexdb#index cn,sn pres,eq,sub,subany
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+database @BACKEND@
+suffix "ou=Groups,dc=example,dc=com"
+subordinate
+rootdn "cn=Manager, dc=example,dc=com"
+#~null~#directory @TESTDIR@/db.1.b
+#indexdb#index objectclass eq
+#indexdb#index uid pres,eq,sub
+#indexdb#index cn,sn pres,eq,sub,subany
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager, dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.c
+#indexdb#index objectclass eq
+#indexdb#index uid pres,eq,sub
+#indexdb#index cn pres,eq,sub,subany
+#ndb#dbname db_3
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/regressions/its7573/its7573 b/tests/data/regressions/its7573/its7573
new file mode 100755
index 0000000..fc4d1ed
--- /dev/null
+++ b/tests/data/regressions/its7573/its7573
@@ -0,0 +1,121 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $BACKPERL = perlno; then
+ echo "Perl backend not available, test skipped"
+ exit 0
+fi
+
+CONFDIR=$TESTDIR/slapd.d
+PERLPM=$TOPSRCDIR/servers/slapd/back-perl/SampleLDAP.pm
+PERLMOD=SampleLDAP
+
+mkdir -p $TESTDIR $CONFDIR $DBDIR1
+cp $PERLPM $TESTDIR
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+echo "Starting slapd on TCP/IP port $PORT1... $PWD"
+. $CONFFILTER $BACKEND < $DYNAMICCONF > $CONFLDIF
+$SLAPADD -F $CONFDIR -n 0 -l $CONFLDIF
+cd $TESTDIR
+$SLAPD -F ./slapd.d -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+cd $TESTWD
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding schema..."
+$LDAPADD -H $URI1 -D cn=config -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+include: file://$ABS_SCHEMADIR/core.ldif
+
+include: file://$ABS_SCHEMADIR/cosine.ldif
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for schema config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if test $BACKPERL = perlmod; then
+ echo "Loading back-perl module..."
+ $LDAPADD -H $URI1 -D cn=config -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/back-perl
+olcModuleLoad: back_perl.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for module config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+echo "Adding database..."
+$LDAPADD -H $URI1 -D cn=config -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcDatabase=perl,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcDbPerlConfig
+olcDatabase: perl
+olcSuffix: $BASEDN
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+olcPerlModulePath: $TESTDIR
+olcPerlModule: $PERLMOD
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/data/regressions/its8427/its8427 b/tests/data/regressions/its8427/its8427
new file mode 100755
index 0000000..344c94d
--- /dev/null
+++ b/tests/data/regressions/its8427/its8427
@@ -0,0 +1,314 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $WITH_TLS = no ; then
+ echo "TLS support not available, test skipped"
+ exit 0
+fi
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+TMP=$TESTDIR/tmp
+
+mkdir -p $TESTDIR
+mkdir -p $TESTDIR/srv1/slapd.d $TESTDIR/srv1/db \
+ $TESTDIR/srv2/slapd.d $TESTDIR/srv2/db
+
+cp -r $DATADIR/tls $TESTDIR
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+if test x"$SYNCMODE" = x ; then
+ SYNCMODE=rp
+fi
+case "$SYNCMODE" in
+ ro)
+ SYNCTYPE="type=refreshOnly interval=00:00:00:03"
+ ;;
+ rp)
+ SYNCTYPE="type=refreshAndPersist interval=00:00:00:03"
+ ;;
+ *)
+ echo "unknown sync mode $SYNCMODE"
+ exit 1;
+ ;;
+esac
+
+nullExclude=""
+test $BACKEND = null && nullExclude="# "
+
+KILLPIDS=
+
+cat > $TMP <<EOF
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcTLSCertificateFile: $TESTDIR/tls/certs/localhost.crt
+olcTLSCertificateKeyFile: $TESTDIR/tls/private/localhost.key
+
+EOF
+
+if test "$SYNCPROV" = syncprovmod ; then
+ cat <<EOF >> $TMP
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+EOF
+ if [ "$SYNCPROV" = syncprovmod ]; then
+ echo "olcModuleLoad: syncprov.la" >> $TMP
+ fi
+ echo "" >> $TMP
+fi
+
+if [ "$BACKENDTYPE" = mod ]; then
+cat <<EOF >> $TMP
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/back-$BACKEND
+olcModuleLoad: back_$BACKEND.la
+
+EOF
+fi
+if test $INDEXDB = indexdb ; then
+INDEX1="olcDbIndex: objectClass,entryCSN,reqStart,reqDN,reqResult eq"
+INDEX2="olcDbIndex: objectClass,entryCSN,entryUUID eq"
+else
+INDEX1=
+INDEX2=
+fi
+cat >> $TMP <<EOF
+dn: cn=schema,cn=config
+objectclass: olcSchemaconfig
+cn: schema
+
+include: file://$ABS_SCHEMADIR/core.ldif
+
+include: file://$ABS_SCHEMADIR/cosine.ldif
+
+include: file://$ABS_SCHEMADIR/inetorgperson.ldif
+
+include: file://$ABS_SCHEMADIR/openldap.ldif
+
+include: file://$ABS_SCHEMADIR/nis.ldif
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW:< file://$CONFIGPWF
+
+dn: olcDatabase={2}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {2}$BACKEND
+olcSuffix: $BASEDN
+${nullExclude}olcDbDirectory: ./db
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+$INDEX2
+EOF
+
+echo "Configuring provider"
+cd $TESTDIR/srv1
+$SLAPADD -F ./slapd.d -n 0 -d-1 < $TMP > $TESTOUT 2>&1
+
+$SLAPADD -F ./slapd.d -n 0 -d-1 <<EOF >> $TESTOUT 2>&1
+dn: olcOverlay=syncprov,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+EOF
+
+echo "Starting provider..."
+$SLAPD -F ./slapd.d -h "$SURIP1 ldaps://127.0.0.2:$PORT1" -d $LVL > $LOG1 2>&1 &
+MASTERPID=$!
+if test $WAIT != 0 ; then
+ echo MASTERPID $MASTERPID
+ read foo
+fi
+KILLPIDS="$MASTERPID"
+cd $TESTWD
+
+echo "Using ldapsearch to check that provider is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -s base -b "" -H $SURIP1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Configuring consumer"
+cd $TESTDIR/srv2
+$SLAPADD -F ./slapd.d -n 0 -d-1 < $TMP >> $TESTOUT 2>&1
+
+$SLAPMODIFY -F ./slapd.d -n 0 -d-1 <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcSyncRepl
+olcSyncRepl: rid=001 provider=ldaps://127.0.0.2:$PORT1
+ binddn="$MANAGERDN" bindmethod=simple credentials=$PASSWD
+ searchbase="$BASEDN" $SYNCTYPE retry="3 +" timeout=3
+ tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt tls_reqcert=allow
+EOF
+
+echo "Starting consumer..."
+$SLAPD -F ./slapd.d -h $URI2 -d $LVL > $LOG2 2>&1 &
+SLAVEPID=$!
+if test $WAIT != 0 ; then
+ echo SLAVEPID $SLAVEPID
+ read foo
+fi
+KILLPIDS="$MASTERPID $SLAVEPID"
+cd $TESTWD
+
+echo "Using ldapsearch to check that consumer is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Populating provider"
+$LDAPADD -D "$MANAGERDN" -H $SURIP1 -w $PASSWD -o \
+ tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt \
+ -f $LDIFORDERED >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting for consumer to sync..."
+sleep $SLEEP2
+
+$LDAPSEARCH -b "$BASEDN" -H "$URI2" -D "$BABSDN" -w bjensen \
+ '(objectClass=*)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIFORDERED > $LDIFFLT
+echo "" >> $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Stopping the provider, sleeping $SLEEP2 seconds and restarting it..."
+kill -HUP "$MASTERPID"
+wait $MASTERPID
+sleep $SLEEP2
+
+echo "======================= RESTART =======================" >> $LOG1
+cd $TESTDIR/srv1
+$SLAPD -F slapd.d -h "$SURIP1 ldaps://127.0.0.2:$PORT1" -d $LVL >> $LOG1 2>&1 &
+MASTERPID=$!
+if test $WAIT != 0 ; then
+ echo MASTERPID $MASTERPID
+ read foo
+fi
+KILLPIDS="$MASTERPID $SLAVEPID"
+cd $TESTWD
+
+echo "Using ldapsearch to check that provider is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -s base -b "" -H $SURIP1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Updating provider"
+$LDAPMODRDN -H $SURIP1 -D "$MANAGERDN" -w $PASSWD \
+ -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt <<EOF
+$BABSDN
+cn=Babs
+EOF
+
+if test $RC != 0 ; then
+ echo "ldapmodrdn failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+$LDAPWHOAMI -H $URI2 \
+ -D "cn=Babs,ou=Information Technology DivisioN,ou=People,$BASEDN" \
+ -w bjensen
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/data/regressions/its8427/its8427-2 b/tests/data/regressions/its8427/its8427-2
new file mode 100755
index 0000000..ca2ef7d
--- /dev/null
+++ b/tests/data/regressions/its8427/its8427-2
@@ -0,0 +1,395 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $WITH_TLS = no ; then
+ echo "TLS support not available, test skipped"
+ exit 0
+fi
+
+if test $BACKLDAP = "ldapno" ; then
+ echo "LDAP backend not available, test skipped"
+ exit 0
+fi
+
+if test "$BACKEND" = "ldap"; then
+ echo "LDAP backend not valid, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1
+cp -r $DATADIR/tls $TESTDIR
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf
+
+ITS=8427
+ITSDIR=$DATADIR/regressions/its$ITS
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $TLSCONF > $CONF1
+$SLAPADD -f $CONF1 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "database config" >> $CONF1
+echo "include $TESTDIR/configpw.conf" >> $CONF1
+
+echo "Starting slapd listening on $URIP1 and $SURIP2..."
+$SLAPD -f $CONF1 -h "$URIP1 $SURIP2" -d $LVL > $LOG1 2>&1 &
+SERVERPID=$!
+if test $WAIT != 0 ; then
+ echo SERVERPID $SERVERPID
+ read foo
+fi
+KILLPIDS="$SERVERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "database config" >> $CONF2
+echo "include $TESTDIR/configpw.conf" >> $CONF2
+
+echo "Starting proxy slapd on TCP/IP port $PORT3..."
+. $CONFFILTER $BACKEND < $ITSDIR/slapd.conf > $CONF2
+$SLAPD -f $CONF2 -h $URI3 -d $LVL > $LOG2 2>&1 &
+PROXYPID=$!
+if test $WAIT != 0 ; then
+ echo PROXYPID $PROXYPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PROXYPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that proxy slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI3 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Configuring proxy..."
+$LDAPMODIFY -D cn=config -H $URI3 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOF
+dn: olcDatabase={2}ldap,cn=config
+changetype: add
+objectClass: olcLDAPConfig
+olcDbUri: $URI1
+olcSuffix: $BASEDN
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "modification failed ($RC)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Privileged WhoAmI (proxy uses plain ldap://)..."
+$LDAPWHOAMI -H $URI3 -D "$MANAGERDN" -w $PASSWD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "WhoAmI (proxy uses plain ldap://)..."
+$LDAPWHOAMI -H $URI3 -D "$BABSDN" -w bjensen
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Searching (proxy uses plain ldap://)..."
+echo "# Searching (proxy uses plain ldap://)..." > $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI3 \
+ -D "$BABSDN" -w bjensen \
+ '(objectClass=*)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIFORDERED > $LDIFFLT
+echo "" >> $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Reconfiguring database to only allow TLS binds..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOF
+dn: olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcAccess
+olcAccess: to attrs=userPassword by anonymous ssf=2 auth by users read
+olcAccess: to * by users read
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "modification failed ($RC)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Re-configuring proxy to use ldaps:// on privileged connections only..."
+$LDAPMODIFY -D cn=config -H $URI3 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOF
+dn: olcDatabase={2}ldap,cn=config
+changetype: delete
+
+dn: olcDatabase={2}ldap,cn=config
+changetype: add
+objectClass: olcLDAPConfig
+olcDbUri: $SURIP2
+olcSuffix: $BASEDN
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+olcDbIDAssertBind: bindmethod=simple binddn="$MANAGERDN" credentials="$PASSWD" tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "modification failed ($RC)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Privileged WhoAmI (proxy uses ldaps://)..."
+$LDAPWHOAMI -H $URI3 -D "$MANAGERDN" -w $PASSWD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "WhoAmI (proxy uses ldaps://), which should fail..."
+$LDAPWHOAMI -H $URI3 -D "$BABSDN" -w bjensen
+RC=$?
+case $RC in
+52)
+ ;;
+0)
+ echo "ldapwhoami should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+*)
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+# FIXME: just adding olcDbStartTLS to the DB doesn't have an effect, why?
+echo "Re-configuring proxy to use ldaps:// everywhere..."
+$LDAPMODIFY -D cn=config -H $URI3 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOF
+dn: olcDatabase={2}ldap,cn=config
+changetype: modify
+add: olcDbStartTLS
+olcDbStartTLS: ldaps tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "modification failed ($RC)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "WhoAmI again (proxy uses ldaps://)..."
+$LDAPWHOAMI -H $URI3 -D "$BABSDN" -w bjensen
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Searching (proxy uses ldaps://)..."
+echo "# Searching (proxy uses ldaps://)..." > $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI3 \
+ -D "$BABSDN" -w bjensen \
+ '(objectClass=*)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Re-configuring proxy to use LDAP+StartTLS correctly on privileged connections..."
+$LDAPMODIFY -D cn=config -H $URI3 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOF
+dn: olcDatabase={2}ldap,cn=config
+changetype: delete
+
+dn: olcDatabase={2}ldap,cn=config
+changetype: add
+objectClass: olcLDAPConfig
+olcDbUri: $URIP1
+olcSuffix: $BASEDN
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+olcDbIDAssertBind: bindmethod=none tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "modification failed ($RC)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Privileged WhoAmI (proxy requests StartTLS)..."
+$LDAPWHOAMI -H $URI3 -D "$MANAGERDN" -w $PASSWD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "WhoAmI (proxy requests StartTLS), which should fail..."
+$LDAPWHOAMI -H $URI3 -D "$BABSDN" -w bjensen
+RC=$?
+case $RC in
+49|52) # ACL forbids plaintext binds against userPassword
+ ;;
+0)
+ echo "ldapwhoami should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+*)
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+# FIXME: just adding olcDbStartTLS to the DB doesn't have an effect, why?
+echo "Re-configuring proxy to use ldaps:// everywhere..."
+$LDAPMODIFY -D cn=config -H $URI3 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOF
+dn: olcDatabase={2}ldap,cn=config
+changetype: modify
+add: olcDbStartTLS
+olcDbStartTLS: start tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "modification failed ($RC)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "WhoAmI (proxy requests StartTLS)..."
+$LDAPWHOAMI -H $URI3 -D "$BABSDN" -w bjensen
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Searching (proxy requests StartTLS)..."
+echo "# Searching (proxy requests StartTLS)..." > $SEARCHOUT
+$LDAPSEARCH -b "$BASEDN" -H $URI3 \
+ -D "$BABSDN" -w bjensen \
+ '(objectClass=*)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/data/regressions/its8427/slapd.conf b/tests/data/regressions/its8427/slapd.conf
new file mode 100644
index 0000000..7af8ff8
--- /dev/null
+++ b/tests/data/regressions/its8427/slapd.conf
@@ -0,0 +1,31 @@
+# stand-alone slapd config -- for testing (with indexing)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+
+pidfile @TESTDIR@/slapd.2.pid
+argsfile @TESTDIR@/slapd.2.args
+
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+
+database monitor
+
+database config
+include @TESTDIR@/configpw.conf
diff --git a/tests/data/regressions/its8444/its8444 b/tests/data/regressions/its8444/its8444
new file mode 100755
index 0000000..896ddaf
--- /dev/null
+++ b/tests/data/regressions/its8444/its8444
@@ -0,0 +1,322 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+if test $ACCESSLOG = accesslogno; then
+ echo "Accesslog overlay not available, test skipped"
+ exit 0
+fi
+if test $DYNLIST = dynlistno; then
+ echo "Dynlist overlay not available, test skipped"
+ exit 0
+fi
+if test $MEMBEROF = memberofno; then
+ echo "Memberof overlay not available, test skipped"
+ exit 0
+fi
+if test $BACKEND = ldif ; then
+ # Onelevel search does not return entries in order of creation or CSN.
+ echo "$BACKEND backend unsuitable for syncprov logdb, test skipped"
+ exit 0
+fi
+
+echo "This test tracks a case where changes are incorrectly skipped"
+echo "See https://bugs.openldap.org/show_bug.cgi?id=8444 for more information."
+
+MPR=4
+XDIR=$TESTDIR/srv
+
+mkdir -p $TESTDIR
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+ITS=8444
+ITSDIR=$DATADIR/regressions/its$ITS
+
+echo "Initializing server configurations..."
+
+n=1
+while [ $n -le $MPR ]; do
+ DBDIR=${XDIR}$n/db
+ CFDIR=${XDIR}$n/slapd.d
+
+ mkdir -p ${XDIR}$n $DBDIR.1 $DBDIR.2 $CFDIR
+ . $CONFFILTER $BACKEND < $ITSDIR/slapd-provider${n}.ldif > $CONFLDIF
+ $SLAPADD -F $CFDIR -n 0 -l $CONFLDIF
+ n=`expr $n + 1`
+done
+
+KILLPIDS=
+n=1
+while [ $n -le $MPR ]; do
+ MYURI=`eval echo '$URI'$n`
+ MYLOG=`eval echo '$LOG'$n`
+ CFDIR=${XDIR}$n/slapd.d
+
+ echo "Starting provider slapd on TCP/IP URI $MYURI"
+ $SLAPD -F $CFDIR -h $MYURI -d $LVL > $MYLOG 2>&1 &
+
+ PID=$!
+ if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+ fi
+ KILLPIDS="$PID $KILLPIDS"
+ sleep 1
+
+ echo "Using ldapsearch to check that provider slapd is running..."
+ for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $MYURI \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+ done
+
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ n=`expr $n + 1`
+done
+
+echo "Populating database on first provider..."
+$LDAPADD -D $MANAGERDN -H $URI1 -w $PASSWD << EOMODS >> $TESTOUT 2>&1
+dn: $BASEDN
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: ou=People,$BASEDN
+objectClass: organizationalUnit
+ou: People
+
+dn: ou=Groups,$BASEDN
+objectClass: organizationalUnit
+ou: Groups
+
+dn: cn=Roger Rabbit,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: Roger Rabbit
+sn: Rabbit
+
+dn: cn=Baby Herman,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: Baby Herman
+sn: Herman
+
+dn: cn=Jessica_Rabbit,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: Jessica_Rabbit
+sn: Rabbit
+
+dn: cn=Bugs_Bunny,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: Bugs_Bunny
+sn: Bunny
+
+dn: cn=Daffy_Duck,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: Daffy_Duck
+sn: Duck
+
+dn: cn=Elmer_Fudd,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: Elmer_Fudd
+sn: Fudd
+
+dn: cn=Cartoonia,ou=Groups,$BASEDN
+objectClass: groupOfNames
+cn: Cartoonia
+member: cn=Roger Rabbit,ou=People,$BASEDN
+member: cn=Baby Herman,ou=People,$BASEDN
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Sleeping 10 seconds to allow replication to initiate..."
+sleep 10
+
+echo "Looping 50 times adding and deleting members to the Cartoonia group..."
+modloop=1
+while [ $modloop -le 50 ]; do
+ echo "Adding new members to the group (${modloop}/50)..."
+ $LDAPMODIFY -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: cn=Cartoonia,ou=Groups,$BASEDN
+changetype: modify
+add: member
+member: cn=Jessica_Rabbit,ou=People,$BASEDN
+member: cn=Elmer_Fudd,ou=People,$BASEDN
+member: cn=Daffy_Duck,ou=People,$BASEDN
+member: cn=Bugs_Bunny,ou=People,$BASEDN
+EOF
+
+ sleep 3
+
+ echo "Deleting new members from the group..."
+ $LDAPMODIFY -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: cn=Cartoonia,ou=Groups,$BASEDN
+changetype: modify
+delete: member
+member: cn=Jessica_Rabbit,ou=People,$BASEDN
+member: cn=Elmer_Fudd,ou=People,$BASEDN
+member: cn=Daffy_Duck,ou=People,$BASEDN
+member: cn=Bugs_Bunny,ou=People,$BASEDN
+EOF
+ sleep 3
+
+ echo "Searching new members to see if they still have memberOf present..."
+ TOON1="cn=Jessica_Rabbit,ou=People,$BASEDN"
+ TOON2="cn=Elmer_Fudd,ou=People,$BASEDN"
+ TOON3="cn=Daffy_Duck,ou=People,$BASEDN"
+ TOON4="cn=Bugs_Bunny,ou=People,$BASEDN"
+ for member in $TOON1 $TOON2 $TOON3 $TOON4; do
+ n=1
+ while [ $n -le $MPR ]; do
+ >$SEARCHOUT
+ echo "# Searching member $member after removal from Cartoonia group, provider $n" >> $SEARCHOUT
+ MYURI=`eval echo '$URI'$n`
+ $LDAPSEARCH -S "" -b "$member" -s base -H $MYURI -D "cn=manager,$BASEDN" -w $PASSWD \
+ '(objectClass=*)' 'memberOf' >> $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ grep "memberOf:" $SEARCHOUT >/dev/null 2>&1
+ RC=$?
+
+ if test $RC != 1 ; then
+ echo "User delete failed on one or more consumer."
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ n=`expr $n + 1`
+ done
+ done
+ modloop=`expr $modloop + 1`
+done
+
+echo "Looping 50 times deleting and adding Cartoonia group..."
+modloop=1
+while [ $modloop -le 50 ]; do
+ >$SEARCHOUT
+ echo "Running ldapdelete to remove a group (${modloop}/50)..."
+ $LDAPMODIFY -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: cn=Cartoonia,ou=Groups,$BASEDN
+changetype: delete
+EOF
+
+ sleep 3
+
+ echo "Searching entire database on each provider after deleting Cartoonia group"
+
+ n=1
+ while [ $n -le $MPR ]; do
+ echo "# Searching the entire database after deleting Cartoonia, provider $n" >> $SEARCHOUT
+ MYURI=`eval echo '$URI'$n`
+ $LDAPSEARCH -S "" -b "$BASEDN" -H $MYURI -D "cn=manager,$BASEDN" -w $PASSWD \
+ '(objectClass=*)' '*' >> $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ n=`expr $n + 1`
+ done
+
+ grep "cn=Cartoonia" $SEARCHOUT >/dev/null 2>&1
+ RC=$?
+
+ if test $RC != 1 ; then
+ echo "Group delete failed on one or more consumer."
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Running ldapmodify to add the group back..."
+ $LDAPMODIFY -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w $PASSWD \
+ >> $TESTOUT 2>&1 <<EOF
+dn: cn=Cartoonia,ou=Groups,$BASEDN
+changetype: add
+objectClass: groupOfNames
+cn: Cartoonia
+member: cn=Roger Rabbit,ou=People,$BASEDN
+member: cn=Baby Herman,ou=People,$BASEDN
+EOF
+
+ sleep 3
+
+ echo "Searching entire database on each provider after re-adding Cartoonia group"
+
+ n=1
+ while [ $n -le $MPR ]; do
+ >$SEARCHOUT
+ echo "# Searching the entire database after re-adding Cartoonia, provider $n" >> $SEARCHOUT
+ MYURI=`eval echo '$URI'$n`
+ $LDAPSEARCH -S "" -b "$BASEDN" -H $MYURI -D "cn=manager,$BASEDN" -w $PASSWD \
+ '(objectClass=*)' '*' memberOf>> $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ grep "memberOf:" $SEARCHOUT >/dev/null 2>&1
+ RC=$?
+
+ if test $RC != 0 ; then
+ echo "Group add failed on one or more consumer."
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ n=`expr $n + 1`
+ done
+ modloop=`expr $modloop + 1`
+done
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/data/regressions/its8444/slapd-provider1.ldif b/tests/data/regressions/its8444/slapd-provider1.ldif
new file mode 100644
index 0000000..ebc5858
--- /dev/null
+++ b/tests/data/regressions/its8444/slapd-provider1.ldif
@@ -0,0 +1,154 @@
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcLogLevel: Sync
+olcLogLevel: Stats
+olcPidFile: @TESTDIR@/slapd.1.pid
+olcArgsFile: @TESTDIR@/slapd.1.args
+olcServerID: 1
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file://@TESTWD@/@SCHEMADIR@/core.ldif
+include: file://@TESTWD@/@SCHEMADIR@/cosine.ldif
+include: file://@TESTWD@/@SCHEMADIR@/inetorgperson.ldif
+include: file://@TESTWD@/@SCHEMADIR@/misc.ldif
+include: file://@TESTWD@/@SCHEMADIR@/nis.ldif
+include: file://@TESTWD@/@SCHEMADIR@/dyngroup.ldif
+
+#mod#dn: cn=module{0},cn=config
+#mod#objectClass: olcModuleList
+#mod#cn: module{0}
+#mod#olcModulePath: @TESTWD@/../servers/slapd/back-@BACKEND@/
+#mod#olcModuleLoad: {0}back_@BACKEND@.la
+
+#memberofmod#dn: cn=module{1},cn=config
+#memberofmod#objectClass: olcModuleList
+#memberofmod#cn: module{1}
+#memberofmod#olcModulePath: @TESTWD@/../servers/slapd/overlays/
+#memberofmod#olcModuleLoad: {0}memberof.la
+#dynlistmod#olcModuleLoad: {1}dynlist.la
+#syncprovmod#olcModuleLoad: {2}syncprov.la
+#accesslogmod#olcModuleLoad: {3}accesslog.la
+
+dn: olcDatabase={-1}frontend,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcFrontendConfig
+olcDatabase: {-1}frontend
+olcAccess: {0}to dn="" by * read
+olcAccess: {1}to * by self write by users read by anonymous auth
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcAccess: {0}to * by * none
+olcRootPW:< file://@TESTDIR@/configpw
+
+dn: olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {1}@BACKEND@
+olcSuffix: dc=example,dc=com
+olcRootDN: cn=manager,dc=example,dc=com
+olcRootPW: secret
+olcSizeLimit: unlimited
+olcTimeLimit: unlimited
+olcMultiProvider: TRUE
+olcSyncrepl: {0}rid=100 provider=@URI2@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+olcSyncrepl: {1}rid=101 provider=@URI3@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+olcSyncrepl: {2}rid=102 provider=@URI4@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+#~null~#olcDbDirectory: @TESTDIR@/srv1/db.1
+#indexdb#olcDbIndex: default eq
+#indexdb#olcDbIndex: objectClass
+#indexdb#olcDbIndex: entryUUID
+#indexdb#olcDbIndex: entryCSN
+#indexdb#olcDbIndex: cn pres,eq,sub
+#indexdb#olcDbIndex: uid pres,eq,sub
+#indexdb#olcDbIndex: uidNumber pres,eq
+#indexdb#olcDbIndex: gidNumber pres,eq
+#indexdb#olcDbIndex: mail pres,eq,sub
+#indexdb#olcDbIndex: sn pres,eq,sub
+#indexdb#olcDbIndex: memberUid
+#indexdb#olcDbIndex: uniqueMember pres,eq
+#indexdb#olcDbIndex: description pres,eq,sub
+#indexdb#olcDbIndex: title pres,eq,sub
+#indexdb#olcDbIndex: givenName pres,eq,sub
+#indexdb#olcDbIndex: member
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcOverlay={0}dynlist,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcDynListConfig
+olcOverlay: {0}dynlist
+olcDynListAttrSet: {0}groupOfURLs memberURL
+
+dn: olcOverlay={1}memberof,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcMemberOfConfig
+olcOverlay: {1}memberof
+olcMemberOfDangling: ignore
+olcMemberOfRefInt: TRUE
+olcMemberOfGroupOC: groupOfNames
+olcMemberOfMemberAD: member
+olcMemberOfMemberOfAD: memberOf
+
+dn: olcOverlay={2}syncprov,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcConfig
+objectClass: top
+objectClass: olcSyncProvConfig
+olcOverlay: {2}syncprov
+olcSpCheckpoint: 20 10
+olcSpSessionlog: 50
+
+dn: olcOverlay={3}accesslog,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcAccessLogConfig
+olcOverlay: {3}accesslog
+olcAccessLogDB: cn=accesslog
+olcAccessLogOps: writes
+olcAccessLogPurge: 07+00:00 01+00:00
+olcAccessLogSuccess: TRUE
+
+dn: olcDatabase={2}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {2}@BACKEND@
+olcSuffix: cn=accesslog
+olcSizeLimit: unlimited
+olcTimeLimit: unlimited
+olcDbIndex: default eq
+olcDbIndex: entryCSN,objectClass,reqEnd,reqResult,reqStart,reqDN
+#~null~#olcDbDirectory: @TESTDIR@/srv1/db.2
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcOverlay={0}syncprov,olcDatabase={2}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcConfig
+objectClass: top
+objectClass: olcSyncProvConfig
+olcOverlay: {0}syncprov
+olcSpNoPresent: TRUE
+olcSpReloadHint: TRUE
+
+dn: olcDatabase={3}monitor,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {3}monitor
+olcAccess: {0}to dn.subtree="cn=monitor" by * read
diff --git a/tests/data/regressions/its8444/slapd-provider2.ldif b/tests/data/regressions/its8444/slapd-provider2.ldif
new file mode 100644
index 0000000..9a5c564
--- /dev/null
+++ b/tests/data/regressions/its8444/slapd-provider2.ldif
@@ -0,0 +1,154 @@
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcLogLevel: Sync
+olcLogLevel: Stats
+olcPidFile: @TESTDIR@/slapd.2.pid
+olcArgsFile: @TESTDIR@/slapd.2.args
+olcServerID: 2
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file://@TESTWD@/@SCHEMADIR@/core.ldif
+include: file://@TESTWD@/@SCHEMADIR@/cosine.ldif
+include: file://@TESTWD@/@SCHEMADIR@/inetorgperson.ldif
+include: file://@TESTWD@/@SCHEMADIR@/misc.ldif
+include: file://@TESTWD@/@SCHEMADIR@/nis.ldif
+include: file://@TESTWD@/@SCHEMADIR@/dyngroup.ldif
+
+#mod#dn: cn=module{0},cn=config
+#mod#objectClass: olcModuleList
+#mod#cn: module{0}
+#mod#olcModulePath: @TESTWD@/../servers/slapd/back-@BACKEND@/
+#mod#olcModuleLoad: {0}back_@BACKEND@.la
+
+#memberofmod#dn: cn=module{1},cn=config
+#memberofmod#objectClass: olcModuleList
+#memberofmod#cn: module{1}
+#memberofmod#olcModulePath: @TESTWD@/../servers/slapd/overlays/
+#memberofmod#olcModuleLoad: {0}memberof.la
+#dynlistmod#olcModuleLoad: {1}dynlist.la
+#syncprovmod#olcModuleLoad: {2}syncprov.la
+#accesslogmod#olcModuleLoad: {3}accesslog.la
+
+dn: olcDatabase={-1}frontend,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcFrontendConfig
+olcDatabase: {-1}frontend
+olcAccess: {0}to dn="" by * read
+olcAccess: {1}to * by self write by users read by anonymous auth
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcAccess: {0}to * by * none
+olcRootPW:< file://@TESTDIR@/configpw
+
+dn: olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {1}@BACKEND@
+olcSuffix: dc=example,dc=com
+olcRootDN: cn=manager,dc=example,dc=com
+olcRootPW: secret
+olcSizeLimit: unlimited
+olcTimeLimit: unlimited
+olcMultiProvider: TRUE
+olcSyncrepl: {0}rid=100 provider=@URI1@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+olcSyncrepl: {1}rid=101 provider=@URI3@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+olcSyncrepl: {2}rid=102 provider=@URI4@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+#~null~#olcDbDirectory: @TESTDIR@/srv2/db.1
+#indexdb#olcDbIndex: default eq
+#indexdb#olcDbIndex: objectClass
+#indexdb#olcDbIndex: entryUUID
+#indexdb#olcDbIndex: entryCSN
+#indexdb#olcDbIndex: cn pres,eq,sub
+#indexdb#olcDbIndex: uid pres,eq,sub
+#indexdb#olcDbIndex: uidNumber pres,eq
+#indexdb#olcDbIndex: gidNumber pres,eq
+#indexdb#olcDbIndex: mail pres,eq,sub
+#indexdb#olcDbIndex: sn pres,eq,sub
+#indexdb#olcDbIndex: memberUid
+#indexdb#olcDbIndex: uniqueMember pres,eq
+#indexdb#olcDbIndex: description pres,eq,sub
+#indexdb#olcDbIndex: title pres,eq,sub
+#indexdb#olcDbIndex: givenName pres,eq,sub
+#indexdb#olcDbIndex: member
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcOverlay={0}dynlist,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcDynListConfig
+olcOverlay: {0}dynlist
+olcDynListAttrSet: {0}groupOfURLs memberURL
+
+dn: olcOverlay={1}memberof,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcMemberOfConfig
+olcOverlay: {1}memberof
+olcMemberOfDangling: ignore
+olcMemberOfRefInt: TRUE
+olcMemberOfGroupOC: groupOfNames
+olcMemberOfMemberAD: member
+olcMemberOfMemberOfAD: memberOf
+
+dn: olcOverlay={2}syncprov,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcConfig
+objectClass: top
+objectClass: olcSyncProvConfig
+olcOverlay: {2}syncprov
+olcSpCheckpoint: 20 10
+olcSpSessionlog: 50
+
+dn: olcOverlay={3}accesslog,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcAccessLogConfig
+olcOverlay: {3}accesslog
+olcAccessLogDB: cn=accesslog
+olcAccessLogOps: writes
+olcAccessLogPurge: 07+00:00 01+00:00
+olcAccessLogSuccess: TRUE
+
+dn: olcDatabase={2}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {2}@BACKEND@
+olcSuffix: cn=accesslog
+olcSizeLimit: unlimited
+olcTimeLimit: unlimited
+olcDbIndex: default eq
+olcDbIndex: entryCSN,objectClass,reqEnd,reqResult,reqStart,reqDN
+#~null~#olcDbDirectory: @TESTDIR@/srv2/db.2
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcOverlay={0}syncprov,olcDatabase={2}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcConfig
+objectClass: top
+objectClass: olcSyncProvConfig
+olcOverlay: {0}syncprov
+olcSpNoPresent: TRUE
+olcSpReloadHint: TRUE
+
+dn: olcDatabase={3}monitor,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {3}monitor
+olcAccess: {0}to dn.subtree="cn=monitor" by * read
diff --git a/tests/data/regressions/its8444/slapd-provider3.ldif b/tests/data/regressions/its8444/slapd-provider3.ldif
new file mode 100644
index 0000000..5db5819
--- /dev/null
+++ b/tests/data/regressions/its8444/slapd-provider3.ldif
@@ -0,0 +1,154 @@
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcLogLevel: Sync
+olcLogLevel: Stats
+olcPidFile: @TESTDIR@/slapd.3.pid
+olcArgsFile: @TESTDIR@/slapd.3.args
+olcServerID: 3
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file://@TESTWD@/@SCHEMADIR@/core.ldif
+include: file://@TESTWD@/@SCHEMADIR@/cosine.ldif
+include: file://@TESTWD@/@SCHEMADIR@/inetorgperson.ldif
+include: file://@TESTWD@/@SCHEMADIR@/misc.ldif
+include: file://@TESTWD@/@SCHEMADIR@/nis.ldif
+include: file://@TESTWD@/@SCHEMADIR@/dyngroup.ldif
+
+#mod#dn: cn=module{0},cn=config
+#mod#objectClass: olcModuleList
+#mod#cn: module{0}
+#mod#olcModulePath: @TESTWD@/../servers/slapd/back-@BACKEND@/
+#mod#olcModuleLoad: {0}back_@BACKEND@.la
+
+#memberofmod#dn: cn=module{1},cn=config
+#memberofmod#objectClass: olcModuleList
+#memberofmod#cn: module{1}
+#memberofmod#olcModulePath: @TESTWD@/../servers/slapd/overlays/
+#memberofmod#olcModuleLoad: {0}memberof.la
+#dynlistmod#olcModuleLoad: {1}dynlist.la
+#syncprovmod#olcModuleLoad: {2}syncprov.la
+#accesslogmod#olcModuleLoad: {3}accesslog.la
+
+dn: olcDatabase={-1}frontend,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcFrontendConfig
+olcDatabase: {-1}frontend
+olcAccess: {0}to dn="" by * read
+olcAccess: {1}to * by self write by users read by anonymous auth
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcAccess: {0}to * by * none
+olcRootPW:< file://@TESTDIR@/configpw
+
+dn: olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {1}@BACKEND@
+olcSuffix: dc=example,dc=com
+olcRootDN: cn=manager,dc=example,dc=com
+olcRootPW: secret
+olcSizeLimit: unlimited
+olcTimeLimit: unlimited
+olcMultiProvider: TRUE
+olcSyncrepl: {0}rid=100 provider=@URI2@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+olcSyncrepl: {1}rid=101 provider=@URI1@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+olcSyncrepl: {2}rid=102 provider=@URI4@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+#~null~#olcDbDirectory: @TESTDIR@/srv3/db.1
+#indexdb#olcDbIndex: default eq
+#indexdb#olcDbIndex: objectClass
+#indexdb#olcDbIndex: entryUUID
+#indexdb#olcDbIndex: entryCSN
+#indexdb#olcDbIndex: cn pres,eq,sub
+#indexdb#olcDbIndex: uid pres,eq,sub
+#indexdb#olcDbIndex: uidNumber pres,eq
+#indexdb#olcDbIndex: gidNumber pres,eq
+#indexdb#olcDbIndex: mail pres,eq,sub
+#indexdb#olcDbIndex: sn pres,eq,sub
+#indexdb#olcDbIndex: memberUid
+#indexdb#olcDbIndex: uniqueMember pres,eq
+#indexdb#olcDbIndex: description pres,eq,sub
+#indexdb#olcDbIndex: title pres,eq,sub
+#indexdb#olcDbIndex: givenName pres,eq,sub
+#indexdb#olcDbIndex: member
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcOverlay={0}dynlist,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcDynListConfig
+olcOverlay: {0}dynlist
+olcDynListAttrSet: {0}groupOfURLs memberURL
+
+dn: olcOverlay={1}memberof,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcMemberOfConfig
+olcOverlay: {1}memberof
+olcMemberOfDangling: ignore
+olcMemberOfRefInt: TRUE
+olcMemberOfGroupOC: groupOfNames
+olcMemberOfMemberAD: member
+olcMemberOfMemberOfAD: memberOf
+
+dn: olcOverlay={2}syncprov,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcConfig
+objectClass: top
+objectClass: olcSyncProvConfig
+olcOverlay: {2}syncprov
+olcSpCheckpoint: 20 10
+olcSpSessionlog: 50
+
+dn: olcOverlay={3}accesslog,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcAccessLogConfig
+olcOverlay: {3}accesslog
+olcAccessLogDB: cn=accesslog
+olcAccessLogOps: writes
+olcAccessLogPurge: 07+00:00 01+00:00
+olcAccessLogSuccess: TRUE
+
+dn: olcDatabase={2}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {2}@BACKEND@
+olcSuffix: cn=accesslog
+olcSizeLimit: unlimited
+olcTimeLimit: unlimited
+olcDbIndex: default eq
+olcDbIndex: entryCSN,objectClass,reqEnd,reqResult,reqStart,reqDN
+#~null~#olcDbDirectory: @TESTDIR@/srv3/db.2
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcOverlay={0}syncprov,olcDatabase={2}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcConfig
+objectClass: top
+objectClass: olcSyncProvConfig
+olcOverlay: {0}syncprov
+olcSpNoPresent: TRUE
+olcSpReloadHint: TRUE
+
+dn: olcDatabase={3}monitor,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {3}monitor
+olcAccess: {0}to dn.subtree="cn=monitor" by * read
diff --git a/tests/data/regressions/its8444/slapd-provider4.ldif b/tests/data/regressions/its8444/slapd-provider4.ldif
new file mode 100644
index 0000000..b795b82
--- /dev/null
+++ b/tests/data/regressions/its8444/slapd-provider4.ldif
@@ -0,0 +1,154 @@
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcLogLevel: Sync
+olcLogLevel: Stats
+olcPidFile: @TESTDIR@/slapd.4.pid
+olcArgsFile: @TESTDIR@/slapd.4.args
+olcServerID: 4
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file://@TESTWD@/@SCHEMADIR@/core.ldif
+include: file://@TESTWD@/@SCHEMADIR@/cosine.ldif
+include: file://@TESTWD@/@SCHEMADIR@/inetorgperson.ldif
+include: file://@TESTWD@/@SCHEMADIR@/misc.ldif
+include: file://@TESTWD@/@SCHEMADIR@/nis.ldif
+include: file://@TESTWD@/@SCHEMADIR@/dyngroup.ldif
+
+#mod#dn: cn=module{0},cn=config
+#mod#objectClass: olcModuleList
+#mod#cn: module{0}
+#mod#olcModulePath: @TESTWD@/../servers/slapd/back-@BACKEND@/
+#mod#olcModuleLoad: {0}back_@BACKEND@.la
+
+#memberofmod#dn: cn=module{1},cn=config
+#memberofmod#objectClass: olcModuleList
+#memberofmod#cn: module{1}
+#memberofmod#olcModulePath: @TESTWD@/../servers/slapd/overlays/
+#memberofmod#olcModuleLoad: {0}memberof.la
+#dynlistmod#olcModuleLoad: {1}dynlist.la
+#syncprovmod#olcModuleLoad: {2}syncprov.la
+#accesslogmod#olcModuleLoad: {3}accesslog.la
+
+dn: olcDatabase={-1}frontend,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcFrontendConfig
+olcDatabase: {-1}frontend
+olcAccess: {0}to dn="" by * read
+olcAccess: {1}to * by self write by users read by anonymous auth
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcAccess: {0}to * by * none
+olcRootPW:< file://@TESTDIR@/configpw
+
+dn: olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {1}@BACKEND@
+olcSuffix: dc=example,dc=com
+olcRootDN: cn=manager,dc=example,dc=com
+olcRootPW: secret
+olcSizeLimit: unlimited
+olcTimeLimit: unlimited
+olcMultiProvider: TRUE
+olcSyncrepl: {0}rid=100 provider=@URI2@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+olcSyncrepl: {1}rid=101 provider=@URI3@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+olcSyncrepl: {2}rid=102 provider=@URI1@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+#~null~#olcDbDirectory: @TESTDIR@/srv4/db.1
+#indexdb#olcDbIndex: default eq
+#indexdb#olcDbIndex: objectClass
+#indexdb#olcDbIndex: entryUUID
+#indexdb#olcDbIndex: entryCSN
+#indexdb#olcDbIndex: cn pres,eq,sub
+#indexdb#olcDbIndex: uid pres,eq,sub
+#indexdb#olcDbIndex: uidNumber pres,eq
+#indexdb#olcDbIndex: gidNumber pres,eq
+#indexdb#olcDbIndex: mail pres,eq,sub
+#indexdb#olcDbIndex: sn pres,eq,sub
+#indexdb#olcDbIndex: memberUid
+#indexdb#olcDbIndex: uniqueMember pres,eq
+#indexdb#olcDbIndex: description pres,eq,sub
+#indexdb#olcDbIndex: title pres,eq,sub
+#indexdb#olcDbIndex: givenName pres,eq,sub
+#indexdb#olcDbIndex: member
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcOverlay={0}dynlist,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcDynListConfig
+olcOverlay: {0}dynlist
+olcDynListAttrSet: {0}groupOfURLs memberURL
+
+dn: olcOverlay={1}memberof,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcMemberOfConfig
+olcOverlay: {1}memberof
+olcMemberOfDangling: ignore
+olcMemberOfRefInt: TRUE
+olcMemberOfGroupOC: groupOfNames
+olcMemberOfMemberAD: member
+olcMemberOfMemberOfAD: memberOf
+
+dn: olcOverlay={2}syncprov,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcConfig
+objectClass: top
+objectClass: olcSyncProvConfig
+olcOverlay: {2}syncprov
+olcSpCheckpoint: 20 10
+olcSpSessionlog: 50
+
+dn: olcOverlay={3}accesslog,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcAccessLogConfig
+olcOverlay: {3}accesslog
+olcAccessLogDB: cn=accesslog
+olcAccessLogOps: writes
+olcAccessLogPurge: 07+00:00 01+00:00
+olcAccessLogSuccess: TRUE
+
+dn: olcDatabase={2}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {2}@BACKEND@
+olcSuffix: cn=accesslog
+olcSizeLimit: unlimited
+olcTimeLimit: unlimited
+olcDbIndex: default eq
+olcDbIndex: entryCSN,objectClass,reqEnd,reqResult,reqStart,reqDN
+#~null~#olcDbDirectory: @TESTDIR@/srv4/db.2
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcOverlay={0}syncprov,olcDatabase={2}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcConfig
+objectClass: top
+objectClass: olcSyncProvConfig
+olcOverlay: {0}syncprov
+olcSpNoPresent: TRUE
+olcSpReloadHint: TRUE
+
+dn: olcDatabase={3}monitor,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {3}monitor
+olcAccess: {0}to dn.subtree="cn=monitor" by * read
diff --git a/tests/data/regressions/its8521/its8521 b/tests/data/regressions/its8521/its8521
new file mode 100755
index 0000000..2eb5409
--- /dev/null
+++ b/tests/data/regressions/its8521/its8521
@@ -0,0 +1,335 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "This test is no longer valid after the fixes in ITS#9015"
+echo "https://bugs.openldap.org/show_bug.cgi?id=9015"
+exit 0
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+echo ""
+echo " This test tracks a case where a consumer fails to replicate from the provider"
+echo " when the provider is dynamically configured for replication."
+echo " See https://bugs.openldap.org/show_bug.cgi?id=8521 and"
+echo " See https://bugs.openldap.org/show_bug.cgi?id=8281 for more information."
+echo ""
+
+PRODIR=$TESTDIR/pro
+CONDIR=$TESTDIR/con1
+CFPRO=$PRODIR/slapd.d
+CFCON=$CONDIR/slapd.d
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2 $PRODIR $CONDIR $CFPRO $CFCON
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+ITS=8521
+ITSDIR=$DATADIR/regressions/its$ITS
+
+echo "Starting provider slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $ITSDIR/slapd-provider.ldif > $CONFLDIF
+$SLAPADD -F $CFPRO -n 0 -l $CONFLDIF
+$SLAPD -F $CFPRO -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting consumer slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $ITSDIR/slapd-consumer.ldif > $CONFLDIF
+$SLAPADD -F $CFCON -n 0 -l $CONFLDIF
+$SLAPD -F $CFCON -h $URI2 -d $LVL > $LOG2 2>&1 &
+SLAVEPID=$!
+if test $WAIT != 0 ; then
+ echo SLAVEPID $SLAVEPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $SLAVEPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that consumer slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Setting serverID on provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=config
+changetype: modify
+add: olcServerId
+olcServerId: 1
+EOF
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Populating database on provider..."
+$LDAPADD -D $MANAGERDN -H $URI1 -w $PASSWD << EOMODS >> $TESTOUT 2>&1
+dn: dc=example,dc=com
+changetype: add
+objectClass: domain
+objectClass: top
+dc: example
+
+dn: ou=LDAPRoles,dc=example,dc=com
+objectClass: top
+objectClass: organizationalUnit
+ou: LDAPRoles
+
+dn: dc=users,dc=example,dc=com
+changetype: add
+dc: users
+objectClass: domain
+objectClass: top
+
+dn: uid=johndoe,dc=users,dc=example,dc=com
+changetype: add
+objectClass: inetOrgPerson
+objectClass: top
+sn: Doe
+cn: Johndoe
+uid: johndoe
+
+dn: cn=replicator,ou=LDAPRoles,dc=example,dc=com
+objectClass: top
+objectClass: simpleSecurityObject
+objectClass: organizationalRole
+userPassword: secret
+cn: replicator
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if [ "$SYNCPROV" = syncprovmod ]; then
+ echo "Configuring syncprov module on the provider..."
+ $LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+olcModuleLoad: syncprov.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for moduleLoad ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+echo "Configuring replication on the provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcOverlay=syncprov,olcDatabase={1}$BACKEND,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectClass: olcConfig
+objectClass: top
+objectClass: olcSyncprovConfig
+olcOverlay: syncprov
+olcSpSessionLog: 10000
+olcSpCheckpoint: 100 10
+
+dn: olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcLimits
+olcLimits: dn.exact="cn=replicator,ou=LDAPRoles,dc=example,dc=com"
+ time.soft=unlimited time.hard=unlimited size.soft=unlimited
+ size.hard=unlimited
+
+dn: olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcAccess
+olcAccess: {0}to dn.subtree="dc=example,dc=com" by self write
+ by dn.exact="cn=replicator,ou=LDAPRoles,dc=example,dc=com" read
+ by anonymous auth by * read
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Configuring replication on the consumer..."
+$LDAPMODIFY -D cn=config -H $URI2 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcSyncrepl
+olcSyncrepl: rid=100 provider=$URI1 bindmethod=simple
+ binddn="cn=replicator,ou=LDAPRoles,dc=example,dc=com" credentials=secret
+ type=refreshAndPersist searchbase="dc=example,dc=com" filter="(objectclass=*)"
+ scope=sub schemachecking=on interval=5 retry="5 +" sizeLimit=unlimited
+ timelimit=unlimited
+-
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for olcSyncrepl configuration ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Sleeping 10 seconds to allow replication to initiate..."
+sleep 10
+
+echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -D $MANAGERDN -w $PASSWD -b "$BASEDN" -H $URI1 \
+ '(objectclass=*)' '*' $OPATTRS > $MASTEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -D $MANAGERDN -w $PASSWD -b "$BASEDN" -H $URI2 \
+ '(objectclass=*)' '*' $OPATTRS > $SLAVEOUT 2>&1
+RC=$?
+
+if test $RC != 32 ; then
+ echo "ldapsearch should have failed with error 32. Got $RC instead!"
+ echo "This is a regression of ITS8281"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+echo "Filtering provider results..."
+$LDIFFILTER < $MASTEROUT > $MASTERFLT
+echo "Filtering consumer results..."
+$LDIFFILTER < $SLAVEOUT > $SLAVEFLT
+
+echo "Comparing retrieved entries from provider and consumer..."
+$CMP $MASTERFLT $SLAVEFLT > $CMPOUT
+
+if test $? = 0 ; then
+ echo "test failed - provider and consumer databases match"
+ echo "This is a regression of ITS8281"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Modifying provider to force generation of a contextCSN"
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+dn: uid=Johndoe,dc=users,dc=example,dc=com
+changetype: modify
+replace: cn
+cn: John Doe
+EOMODS
+
+echo "Sleeping 30 seconds to allow consumer to reconnect and replicate..."
+sleep 30
+
+echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -D $MANAGERDN -w $PASSWD -b "$BASEDN" -H $URI1 \
+ '(objectclass=*)' '*' $OPATTRS > $MASTEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -D $MANAGERDN -w $PASSWD -b "$BASEDN" -H $URI2 \
+ '(objectclass=*)' '*' $OPATTRS > $SLAVEOUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+echo "Filtering provider results..."
+$LDIFFILTER < $MASTEROUT > $MASTERFLT
+echo "Filtering consumer results..."
+$LDIFFILTER < $SLAVEOUT > $SLAVEFLT
+
+echo "Comparing retrieved entries from provider and consumer..."
+$CMP $MASTERFLT $SLAVEFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/data/regressions/its8521/slapd-consumer.ldif b/tests/data/regressions/its8521/slapd-consumer.ldif
new file mode 100644
index 0000000..6d31b65
--- /dev/null
+++ b/tests/data/regressions/its8521/slapd-consumer.ldif
@@ -0,0 +1,53 @@
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcLogLevel: Sync
+olcLogLevel: Stats
+olcPidFile: @TESTDIR@/slapd.3.pid
+olcArgsFile: @TESTDIR@/slapd.3.args
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file://@TESTWD@/@SCHEMADIR@/core.ldif
+include: file://@TESTWD@/@SCHEMADIR@/cosine.ldif
+include: file://@TESTWD@/@SCHEMADIR@/inetorgperson.ldif
+include: file://@TESTWD@/@SCHEMADIR@/misc.ldif
+
+#mod#dn: cn=module{0},cn=config
+#mod#objectClass: olcModuleList
+#mod#cn: module{0}
+#mod#olcModulePath: @TESTWD@/../servers/slapd/back-@BACKEND@/
+#mod#olcModuleLoad: {0}back_@BACKEND@.la
+
+dn: olcDatabase={-1}frontend,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcFrontendConfig
+olcDatabase: {-1}frontend
+olcAccess: {0}to dn="" by * read
+olcAccess: {1}to * by self write by users read by anonymous auth
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW:< file://@TESTDIR@/configpw
+olcAccess: {0}to * by * none
+
+dn: olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {1}@BACKEND@
+olcSuffix: dc=example,dc=com
+olcRootDN: cn=Manager,dc=example,dc=com
+olcRootPW: secret
+#~null~#olcDbDirectory: @TESTDIR@/db.2.a
+#indexdb#olcDbIndex: default eq
+#indexdb#olcDbIndex: objectClass
+#indexdb#olcDbIndex: cn
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcDatabase={2}monitor,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {2}monitor
+olcAccess: {0}to dn.subtree="cn=monitor" by * read
diff --git a/tests/data/regressions/its8521/slapd-provider.ldif b/tests/data/regressions/its8521/slapd-provider.ldif
new file mode 100644
index 0000000..43b2ae8
--- /dev/null
+++ b/tests/data/regressions/its8521/slapd-provider.ldif
@@ -0,0 +1,53 @@
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcLogLevel: Sync
+olcLogLevel: Stats
+olcPidFile: @TESTDIR@/slapd.2.pid
+olcArgsFile: @TESTDIR@/slapd.2.args
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file://@TESTWD@/@SCHEMADIR@/core.ldif
+include: file://@TESTWD@/@SCHEMADIR@/cosine.ldif
+include: file://@TESTWD@/@SCHEMADIR@/inetorgperson.ldif
+include: file://@TESTWD@/@SCHEMADIR@/misc.ldif
+
+#mod#dn: cn=module{0},cn=config
+#mod#objectClass: olcModuleList
+#mod#cn: module{0}
+#mod#olcModulePath: @TESTWD@/../servers/slapd/back-@BACKEND@/
+#mod#olcModuleLoad: {0}back_@BACKEND@.la
+
+dn: olcDatabase={-1}frontend,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcFrontendConfig
+olcDatabase: {-1}frontend
+olcAccess: {0}to dn="" by * read
+olcAccess: {1}to * by self write by users read by anonymous auth
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW:< file://@TESTDIR@/configpw
+olcAccess: {0}to * by * none
+
+dn: olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {1}@BACKEND@
+olcSuffix: dc=example,dc=com
+olcRootDN: cn=Manager,dc=example,dc=com
+olcRootPW: secret
+#~null~#olcDbDirectory: @TESTDIR@/db.1.a
+#indexdb#olcDbIndex: default eq
+#indexdb#olcDbIndex: objectClass
+#indexdb#olcDbIndex: cn
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcDatabase={2}monitor,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {2}monitor
+olcAccess: {0}to dn.subtree="cn=monitor" by * read
diff --git a/tests/data/regressions/its8616/its8616 b/tests/data/regressions/its8616/its8616
new file mode 100755
index 0000000..c8a1144
--- /dev/null
+++ b/tests/data/regressions/its8616/its8616
@@ -0,0 +1,259 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+echo ""
+echo " This test tracks a case where it was not possible to modify the syncprov overlay configuration"
+echo " when the provider is dynamically configured for replication."
+echo " See https://bugs.openldap.org/show_bug.cgi?id=8616 for more information"
+echo ""
+
+PRODIR=$TESTDIR/pro
+CFPRO=$PRODIR/slapd.d
+
+mkdir -p $TESTDIR $DBDIR1 $PRODIR $CFPRO
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+ITS=8616
+ITSDIR=$DATADIR/regressions/its$ITS
+
+echo "Starting provider slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $ITSDIR/slapd-provider.ldif > $CONFLDIF
+$SLAPADD -F $CFPRO -n 0 -l $CONFLDIF
+$SLAPD -F $CFPRO -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Populating database on provider..."
+$LDAPADD -D $MANAGERDN -H $URI1 -w $PASSWD << EOMODS >> $TESTOUT 2>&1
+dn: dc=example,dc=com
+changetype: add
+objectClass: domain
+objectClass: top
+dc: example
+
+dn: ou=LDAPRoles,dc=example,dc=com
+objectClass: top
+objectClass: organizationalUnit
+ou: LDAPRoles
+
+dn: dc=users,dc=example,dc=com
+changetype: add
+dc: users
+objectClass: domain
+objectClass: top
+
+dn: uid=johndoe,dc=users,dc=example,dc=com
+changetype: add
+objectClass: inetOrgPerson
+objectClass: top
+sn: Doe
+cn: Johndoe
+uid: johndoe
+
+dn: cn=replicator,ou=LDAPRoles,dc=example,dc=com
+objectClass: top
+objectClass: simpleSecurityObject
+objectClass: organizationalRole
+userPassword: secret
+cn: replicator
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if [ "$SYNCPROV" = syncprovmod ]; then
+ echo "Configuring syncprov module on the provider..."
+ $LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+olcModuleLoad: syncprov.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for moduleLoad ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+echo "Configuring replication on the provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcOverlay={0}syncprov,olcDatabase={1}$BACKEND,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectClass: olcConfig
+objectClass: top
+objectClass: olcSyncprovConfig
+olcOverlay: {0}syncprov
+olcSpSessionLog: 10000
+olcSpCheckpoint: 100 10
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1
+
+echo "Setting olcSpNoPresent to TRUE on the provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcOverlay={0}syncprov,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcSpNoPresent
+olcSpNoPresent: TRUE
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1
+
+echo "Changing olcSpNoPresent to FALSE on the provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcOverlay={0}syncprov,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcSpNoPresent
+olcSpNoPresent: FALSE
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1
+
+echo "Changing olcSpNoPresent back to TRUE on the provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcOverlay={0}syncprov,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcSpNoPresent
+olcSpNoPresent: TRUE
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1
+
+echo "Setting olcSpReloadHint to TRUE on the provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcOverlay={0}syncprov,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcSpReloadHint
+olcSpReloadHint: TRUE
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1
+
+echo "Changing olcSpReloadHint to FALSE on the provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcOverlay={0}syncprov,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcSpReloadHint
+olcSpReloadHint: FALSE
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1
+
+echo "Changing olcSpReloadHint back to TRUE on the provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcOverlay={0}syncprov,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcSpReloadHint
+olcSpReloadHint: TRUE
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/data/regressions/its8616/slapd-provider.ldif b/tests/data/regressions/its8616/slapd-provider.ldif
new file mode 100644
index 0000000..43b2ae8
--- /dev/null
+++ b/tests/data/regressions/its8616/slapd-provider.ldif
@@ -0,0 +1,53 @@
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcLogLevel: Sync
+olcLogLevel: Stats
+olcPidFile: @TESTDIR@/slapd.2.pid
+olcArgsFile: @TESTDIR@/slapd.2.args
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file://@TESTWD@/@SCHEMADIR@/core.ldif
+include: file://@TESTWD@/@SCHEMADIR@/cosine.ldif
+include: file://@TESTWD@/@SCHEMADIR@/inetorgperson.ldif
+include: file://@TESTWD@/@SCHEMADIR@/misc.ldif
+
+#mod#dn: cn=module{0},cn=config
+#mod#objectClass: olcModuleList
+#mod#cn: module{0}
+#mod#olcModulePath: @TESTWD@/../servers/slapd/back-@BACKEND@/
+#mod#olcModuleLoad: {0}back_@BACKEND@.la
+
+dn: olcDatabase={-1}frontend,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcFrontendConfig
+olcDatabase: {-1}frontend
+olcAccess: {0}to dn="" by * read
+olcAccess: {1}to * by self write by users read by anonymous auth
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW:< file://@TESTDIR@/configpw
+olcAccess: {0}to * by * none
+
+dn: olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {1}@BACKEND@
+olcSuffix: dc=example,dc=com
+olcRootDN: cn=Manager,dc=example,dc=com
+olcRootPW: secret
+#~null~#olcDbDirectory: @TESTDIR@/db.1.a
+#indexdb#olcDbIndex: default eq
+#indexdb#olcDbIndex: objectClass
+#indexdb#olcDbIndex: cn
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcDatabase={2}monitor,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {2}monitor
+olcAccess: {0}to dn.subtree="cn=monitor" by * read
diff --git a/tests/data/regressions/its8663/its8663 b/tests/data/regressions/its8663/its8663
new file mode 100755
index 0000000..2069353
--- /dev/null
+++ b/tests/data/regressions/its8663/its8663
@@ -0,0 +1,279 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $MEMBEROF = memberofno; then
+ echo "memberof overlay not available, test skipped"
+ exit 0
+fi
+
+echo ""
+echo " This test tracks a case where it was not possible to modify the memberof overlay configuration"
+echo " when the provider is dynamically configured for replication."
+echo " See https://bugs.openldap.org/show_bug.cgi?id=8663 for more information"
+echo ""
+
+PRODIR=$TESTDIR/pro
+CFPRO=$PRODIR/slapd.d
+
+mkdir -p $TESTDIR $DBDIR1 $PRODIR $CFPRO
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+ITS=8663
+ITSDIR=$DATADIR/regressions/its$ITS
+
+echo "Starting provider slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $ITSDIR/slapd-provider.ldif > $CONFLDIF
+$SLAPADD -F $CFPRO -n 0 -l $CONFLDIF
+$SLAPD -F $CFPRO -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Populating database on provider..."
+$LDAPADD -D $MANAGERDN -H $URI1 -w $PASSWD << EOMODS >> $TESTOUT 2>&1
+dn: dc=example,dc=com
+changetype: add
+objectClass: domain
+objectClass: top
+dc: example
+
+dn: ou=LDAPRoles,dc=example,dc=com
+objectClass: top
+objectClass: organizationalUnit
+ou: LDAPRoles
+
+dn: dc=users,dc=example,dc=com
+changetype: add
+dc: users
+objectClass: domain
+objectClass: top
+
+dn: uid=johndoe,dc=users,dc=example,dc=com
+changetype: add
+objectClass: inetOrgPerson
+objectClass: top
+sn: Doe
+cn: Johndoe
+uid: johndoe
+
+dn: cn=replicator,ou=LDAPRoles,dc=example,dc=com
+objectClass: top
+objectClass: simpleSecurityObject
+objectClass: organizationalRole
+userPassword: secret
+cn: replicator
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if [ "$MEMBEROF" = memberofmod ]; then
+ echo "Configuring memberof module on the provider..."
+ $LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+olcModuleLoad: memberof.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for moduleLoad ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+echo "Configuring memberof on the provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcOverlay={0}memberof,olcDatabase={1}$BACKEND,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectClass: olcConfig
+objectClass: top
+objectClass: olcMemberOfConfig
+olcMemberOfGroupOC: groupOfNames
+olcMemberOfMemberAD: member
+olcMemberOfMemberOfAD: memberOf
+olcMemberOfDN: cn=Manager,dc=example,dc=com
+olcMemberOfDangling: ignore
+olcMemberOfDanglingError: 13
+olcMemberOfRefInt: FALSE
+olcOverlay: {0}memberof
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1
+
+echo "Setting olcMemberOfRefInt to TRUE on the provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcOverlay={0}memberof,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcMemberOfRefInt
+olcMemberOfRefInt: TRUE
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1
+
+echo "Changing olcMemberOfDangling to error on the provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcOverlay={0}memberof,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcMemberOfDangling
+olcMemberOfDangling: error
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1
+
+echo "Changing olcMemberOfDanglingError to 20 on the provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcOverlay={0}memberof,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcMemberOfDanglingError
+olcMemberOfDanglingError: 20
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1
+
+echo "Setting olcMemberOfDN to cn=config on the provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcOverlay={0}memberof,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcMemberOfDN
+olcMemberOfDN: cn=config
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1
+
+echo "Changing olcMemberOfGroupOC to groupOfUniqueNames on the provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcOverlay={0}memberof,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcMemberOfGroupOC
+olcMemberOfGroupOC: groupOfNames
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1
+
+echo "Changing olcMemberOfMemberAD to uniqueMember on the provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcOverlay={0}memberof,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcMemberOfMemberAD
+olcMemberOfMemberAD: uniqueMember
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Changing olcMemberOfMemberOfAD to owner on the provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcOverlay={0}memberof,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcMemberOfMemberOfAD
+olcMemberOfMemberOfAD: owner
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/data/regressions/its8663/slapd-provider.ldif b/tests/data/regressions/its8663/slapd-provider.ldif
new file mode 100644
index 0000000..43b2ae8
--- /dev/null
+++ b/tests/data/regressions/its8663/slapd-provider.ldif
@@ -0,0 +1,53 @@
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcLogLevel: Sync
+olcLogLevel: Stats
+olcPidFile: @TESTDIR@/slapd.2.pid
+olcArgsFile: @TESTDIR@/slapd.2.args
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file://@TESTWD@/@SCHEMADIR@/core.ldif
+include: file://@TESTWD@/@SCHEMADIR@/cosine.ldif
+include: file://@TESTWD@/@SCHEMADIR@/inetorgperson.ldif
+include: file://@TESTWD@/@SCHEMADIR@/misc.ldif
+
+#mod#dn: cn=module{0},cn=config
+#mod#objectClass: olcModuleList
+#mod#cn: module{0}
+#mod#olcModulePath: @TESTWD@/../servers/slapd/back-@BACKEND@/
+#mod#olcModuleLoad: {0}back_@BACKEND@.la
+
+dn: olcDatabase={-1}frontend,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcFrontendConfig
+olcDatabase: {-1}frontend
+olcAccess: {0}to dn="" by * read
+olcAccess: {1}to * by self write by users read by anonymous auth
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW:< file://@TESTDIR@/configpw
+olcAccess: {0}to * by * none
+
+dn: olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {1}@BACKEND@
+olcSuffix: dc=example,dc=com
+olcRootDN: cn=Manager,dc=example,dc=com
+olcRootPW: secret
+#~null~#olcDbDirectory: @TESTDIR@/db.1.a
+#indexdb#olcDbIndex: default eq
+#indexdb#olcDbIndex: objectClass
+#indexdb#olcDbIndex: cn
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcDatabase={2}monitor,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {2}monitor
+olcAccess: {0}to dn.subtree="cn=monitor" by * read
diff --git a/tests/data/regressions/its8667/accounting.ldif b/tests/data/regressions/its8667/accounting.ldif
new file mode 100644
index 0000000..b4e3e6f
--- /dev/null
+++ b/tests/data/regressions/its8667/accounting.ldif
@@ -0,0 +1,63 @@
+dn: ou=Accounting,dc=example,dc=com
+objectClass: organizationalunit
+objectClass: top
+ou: Accounting
+
+dn: cn=May Gaul,ou=Accounting,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+objectClass: person
+objectClass: top
+cn: May Gaul
+sn: Gaul
+carLicense: 1BMCX31
+departmentNumber: 5148
+description: This is May Gaul's description
+employeeType: Temp
+facsimileTelephoneNumber: +1 804 673-8690
+givenName: May
+homePhone: +1 415 245-8979
+initials: M. G.
+l: Sunnyvale
+mail: May_Gaul@example.com
+manager: cn=Fqa McMannen
+mobile: +1 818 141-8493
+ou: Accounting
+pager: +1 804 480-4264
+postalAddress: example # 930
+roomNumber: 5933
+secretary: cn=Daniel Encomenderos
+telephoneNumber: +1 408 696-5756
+title: Associate Accounting Developer
+uid: May_Gaul
+userPassword:: bHVhR3lhTQ==
+
+dn: cn=Ann Tully,ou=Accounting,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+objectClass: person
+objectClass: top
+cn: Ann Tully
+sn: Tully
+carLicense: 3BM6G8F
+departmentNumber: 1672
+description: This is Ann Tully's description
+employeeType: Employee
+facsimileTelephoneNumber: +1 213 206-5551
+givenName: Ann
+homePhone: +1 213 246-2575
+initials: A. T.
+l: Cambridge
+mail: Ann_Tully@example.com
+manager: cn=Buddy Wingfield
+mobile: +1 213 889-1457
+ou: Accounting
+pager: +1 71 105-8458
+postalAddress: example # 784
+roomNumber: 538
+secretary: cn=Minny Vahdat
+telephoneNumber: +1 415 208-1794
+title: Supreme Accounting Admin
+uid: Ann_Tully
+userPassword:: eWxsdVRubkE=
+
diff --git a/tests/data/regressions/its8667/administrative.ldif b/tests/data/regressions/its8667/administrative.ldif
new file mode 100644
index 0000000..943e192
--- /dev/null
+++ b/tests/data/regressions/its8667/administrative.ldif
@@ -0,0 +1,63 @@
+dn: ou=Administrative,dc=example,dc=com
+objectClass: organizationalunit
+objectClass: top
+ou: Administrative
+
+dn: cn=Tab Danko,ou=Administrative,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+objectClass: person
+objectClass: top
+cn: Tab Danko
+sn: Danko
+carLicense: XB3M2XY
+departmentNumber: 1320
+description: This is Tab Danko's description
+employeeType: Employee
+facsimileTelephoneNumber: +1 510 538-4771
+givenName: Tab
+homePhone: +1 206 386-9666
+initials: T. D.
+l: Orem
+mail: Tab_Danko@example.com
+manager: cn=Ingaborg Burruss
+mobile: +1 206 296-7053
+ou: Administrative
+pager: +1 804 647-2654
+postalAddress: example # 669
+roomNumber: 1597
+secretary: cn=Hilde McCaugherty
+telephoneNumber: +1 415 622-6699
+title: Associate Administrative Yahoo
+uid: Tab_Danko
+userPassword:: b2tuYURiYVQ=
+
+dn: cn=Clary Mand,ou=Administrative,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+objectClass: person
+objectClass: top
+cn: Clary Mand
+sn: Mand
+carLicense: 2AVIDV3
+departmentNumber: 2566
+description: This is Clary Mand's description
+employeeType: Temp
+facsimileTelephoneNumber: +1 408 928-6670
+givenName: Clary
+homePhone: +1 213 446-5803
+initials: C. M.
+l: Alameda
+mail: Clary_Mand@example.com
+manager: cn=Ainslee Hanlan
+mobile: +1 71 687-3763
+ou: Administrative
+pager: +1 206 452-2059
+postalAddress: example # 55
+roomNumber: 8363
+secretary: cn=Mallik MacArthur
+telephoneNumber: +1 804 111-7612
+title: Senior Administrative Stooge
+uid: Clary_Mand
+userPassword:: ZG5hTXlyYWxD
+
diff --git a/tests/data/regressions/its8667/its8667 b/tests/data/regressions/its8667/its8667
new file mode 100755
index 0000000..8bcc7f0
--- /dev/null
+++ b/tests/data/regressions/its8667/its8667
@@ -0,0 +1,116 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+echo ""
+echo " This test tracks a case where the -g flag to slapcat fails to work"
+echo " correctly, exporting the subordinate dbs as well as the root db."
+echo " See https://bugs.openldap.org/show_bug.cgi?id=8667 for more information."
+echo ""
+
+DBDIR1=$TESTDIR/db.1.a
+DBDIR2=$TESTDIR/db.2.a
+DBDIR3=$TESTDIR/db.3.a
+DBDIR4=$TESTDIR/db.4.a
+CFPRO=$TESTDIR/slapd.d
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2 $DBDIR3 $DBDIR4 $CFPRO
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+ITS=8667
+ITSDIR=$DATADIR/regressions/its$ITS
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $ITSDIR/slapd.ldif > $CONFLDIF
+$SLAPADD -F $CFPRO -n 0 -l $CONFLDIF
+
+echo "Populating root database..."
+$SLAPADD -F $CFPRO -b "dc=example,dc=com" -l $ITSDIR/root.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed for root database ($RC)!"
+ exit $RC
+fi
+
+echo "Populating accounting database..."
+$SLAPADD -F $CFPRO -b "dc=accounting,dc=example,dc=com" -l $ITSDIR/accounting.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed for accounting database ($RC)!"
+ exit $RC
+fi
+
+echo "Populating administrative database..."
+$SLAPADD -F $CFPRO -b "dc=administrative,dc=example,dc=com" -l $ITSDIR/administrative.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed for administrative database ($RC)!"
+ exit $RC
+fi
+
+echo "Populating janitorial database..."
+$SLAPADD -F $CFPRO -b "dc=janitorial,dc=example,dc=com" -l $ITSDIR/janitorial.ldif >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed for janitorial database ($RC)!"
+ exit $RC
+fi
+
+echo "Using slapcat -g to export only the root database..."
+$SLAPCAT -F $CFPRO -g -b "dc=example,dc=com" -l $TESTDIR/slapcat.out >> $TESTOUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "slapcat -g failed for root databse ($RC)!"
+ exit $RC
+fi
+
+echo "Verifying Administrative subordinate is not found..."
+grep "dn: ou=Administrative,dc=example,dc=com" $TESTDIR/slapcat.out >> $TESTOUT 2>&1
+RC=$?
+
+if test $RC = 0 ; then
+ echo "test failed - Administrative subordinate found."
+ echo "This is a regression of ITS8667"
+ exit 1
+fi
+
+echo "Verifying Accounting subordinate is not found..."
+grep "dn: ou=Accounting,dc=example,dc=com" $TESTDIR/slapcat.out >> $TESTOUT 2>&1
+RC=$?
+
+if test $RC = 0 ; then
+ echo "test failed - Accounting subordinate found."
+ echo "This is a regression of ITS8667"
+ exit 1
+fi
+
+echo "Verifying Janitorial subordinate is not found..."
+grep "dn: ou=Janitorial,dc=example,dc=com" $TESTDIR/slapcat.out >> $TESTOUT 2>&1
+RC=$?
+
+if test $RC = 0 ; then
+ echo "test failed - Janitorial subordinate found."
+ echo "This is a regression of ITS8667"
+ exit 1
+fi
+
+echo
+echo ">>>>> Test succeeded"
+
+exit 0
diff --git a/tests/data/regressions/its8667/janitorial.ldif b/tests/data/regressions/its8667/janitorial.ldif
new file mode 100644
index 0000000..2b3c6ec
--- /dev/null
+++ b/tests/data/regressions/its8667/janitorial.ldif
@@ -0,0 +1,63 @@
+dn: ou=Janitorial,dc=example,dc=com
+objectClass: organizationalunit
+objectClass: top
+ou: Janitorial
+
+dn: cn=Clark Soto,ou=Janitorial,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+objectClass: person
+objectClass: top
+cn: Clark Soto
+sn: Soto
+carLicense: PPTYC3Z
+departmentNumber: 7897
+description: This is Clark Soto's description
+employeeType: Employee
+facsimileTelephoneNumber: +1 510 450-7054
+givenName: Clark
+homePhone: +1 804 304-2863
+initials: C. S.
+l: San Francisco
+mail: Clark_Soto@example.com
+manager: cn=Gerianne Skrebels
+mobile: +1 206 393-4632
+ou: Janitorial
+pager: +1 510 651-1646
+postalAddress: example # 395
+roomNumber: 5847
+secretary: cn=Vrouwerff Komenda
+telephoneNumber: +1 415 574-1770
+title: Junior Janitorial Sales Rep
+uid: Clark_Soto
+userPassword:: b3RvU2tyYWxD
+
+dn: cn=Edmx Beaty,ou=Janitorial,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: organizationalPerson
+objectClass: person
+objectClass: top
+cn: Edmx Beaty
+sn: Beaty
+carLicense: 4XOW1HI
+departmentNumber: 1279
+description: This is Edmx Beaty's description
+employeeType: Contract
+facsimileTelephoneNumber: +1 408 460-7625
+givenName: Edmx
+homePhone: +1 206 683-8029
+initials: E. B.
+l: San Mateo
+mail: Edmx_Beaty@example.com
+manager: cn=Jilleen Funston
+mobile: +1 303 400-9306
+ou: Janitorial
+pager: +1 408 324-7874
+postalAddress: example # 331
+roomNumber: 389
+secretary: cn=Chick Bulifant
+telephoneNumber: +1 303 427-6104
+title: Chief Janitorial Vice President
+uid: Edmx_Beaty
+userPassword:: eXRhZUJ4bWRF
+
diff --git a/tests/data/regressions/its8667/root.ldif b/tests/data/regressions/its8667/root.ldif
new file mode 100644
index 0000000..3220c3e
--- /dev/null
+++ b/tests/data/regressions/its8667/root.ldif
@@ -0,0 +1,46 @@
+dn: dc=example,dc=com
+objectClass: dcObject
+objectClass: organization
+objectClass: top
+dc: example
+o: example
+
+dn: ou=NonSub00,dc=example,dc=com
+objectClass: top
+objectClass: organizationalUnit
+ou: NonSub00
+
+dn: ou=NonSub01,dc=example,dc=com
+objectClass: top
+objectClass: organizationalUnit
+ou: NonSub01
+
+dn: ou=NonSub02,dc=example,dc=com
+objectClass: top
+objectClass: organizationalUnit
+ou: NonSub02
+
+dn: cn=NonSubTestPerson00,ou=NonSub00,dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: NonSubTestPerson00
+sn: TP00
+
+dn: cn=NonSubTestPerson01,ou=NonSub01,dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: NonSubTestPerson01
+sn: TP01
+
+dn: cn=NonSubTestPerson02,ou=NonSub02,dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: NonSubTestPerson02
+sn: TP02
+
diff --git a/tests/data/regressions/its8667/slapd.ldif b/tests/data/regressions/its8667/slapd.ldif
new file mode 100644
index 0000000..3f687b3
--- /dev/null
+++ b/tests/data/regressions/its8667/slapd.ldif
@@ -0,0 +1,91 @@
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcLogLevel: Sync
+olcLogLevel: Stats
+olcPidFile: @TESTDIR@/slapd.1.pid
+olcArgsFile: @TESTDIR@/slapd.1.args
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file://@TESTWD@/@SCHEMADIR@/core.ldif
+include: file://@TESTWD@/@SCHEMADIR@/cosine.ldif
+include: file://@TESTWD@/@SCHEMADIR@/inetorgperson.ldif
+
+#mod#dn: cn=module{0},cn=config
+#mod#objectClass: olcModuleList
+#mod#cn: module{0}
+#mod#olcModulePath: @TESTWD@/../servers/slapd/back-@BACKEND@/
+#mod#olcModuleLoad: {0}back_@BACKEND@.la
+
+dn: olcDatabase={-1}frontend,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcFrontendConfig
+olcDatabase: {-1}frontend
+olcAccess: {0}to dn="" by * read
+olcAccess: {1}to * by self write by users read by anonymous auth
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW:< file://@TESTDIR@/configpw
+olcAccess: {0}to * by * none
+
+dn: olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {1}@BACKEND@
+olcSuffix: ou=Accounting,dc=example,dc=com
+olcSubordinate: advertise
+olcRootDN: cn=Manager,dc=example,dc=com
+#~null~#olcDbDirectory: @TESTDIR@/db.1.a
+#indexdb#olcDbIndex: default eq
+#indexdb#olcDbIndex: objectClass
+#indexdb#olcDbIndex: cn
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcDatabase={2}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {2}@BACKEND@
+olcSuffix: ou=Administrative,dc=example,dc=com
+olcSubordinate: advertise
+olcRootDN: cn=Manager,dc=example,dc=com
+#~null~#olcDbDirectory: @TESTDIR@/db.2.a
+#indexdb#olcDbIndex: default eq
+#indexdb#olcDbIndex: objectClass
+#indexdb#olcDbIndex: cn
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcDatabase={3}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {3}@BACKEND@
+olcSuffix: ou=Janitorial,dc=example,dc=com
+olcSubordinate: advertise
+olcRootDN: cn=Manager,dc=example,dc=com
+#~null~#olcDbDirectory: @TESTDIR@/db.3.a
+#indexdb#olcDbIndex: default eq
+#indexdb#olcDbIndex: objectClass
+#indexdb#olcDbIndex: cn
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcDatabase={4}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {4}@BACKEND@
+olcSuffix: dc=example,dc=com
+olcRootDN: cn=Manager,dc=example,dc=com
+olcRootPW: secret
+#~null~#olcDbDirectory: @TESTDIR@/db.4.a
+#indexdb#olcDbIndex: default eq
+#indexdb#olcDbIndex: objectClass
+#indexdb#olcDbIndex: cn
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcOverlay={0}glue,olcDatabase={4}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcConfig
+olcOverlay: {0}glue
diff --git a/tests/data/regressions/its8721/its8721 b/tests/data/regressions/its8721/its8721
new file mode 100755
index 0000000..eb6a070
--- /dev/null
+++ b/tests/data/regressions/its8721/its8721
@@ -0,0 +1,240 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+ITS=8721
+ITSDIR=$DATADIR/regressions/its$ITS
+
+if test $BACKMETA = "metano" ; then
+ echo "Meta backend not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR2 $DBDIR3
+
+echo "This test checks that back-ldap manages quarantine retries as configured."
+
+#
+# Start slapds that act as remote LDAP servers that will be proxied
+#
+echo "Configuring the remote slapd servers..."
+sed -e 's/@ID@/2/g' $ITSDIR/slapd-backend.conf | \
+ . $CONFFILTER $BACKEND > $CONF2
+
+echo "Starting remote slapd server on TCP/IP port $PORT2..."
+$SLAPD -f $CONF2 -h "$URI2" -d $LVL > $LOG2 2>&1 &
+SERVERPID2=$!
+if test $WAIT != 0 ; then
+ echo SERVERPID2 $SERVERPID2
+ read foo
+fi
+KILLPIDS="$KILLPIDS $SERVERPID2"
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPADD -D "cn=2,$BASEDN" -H $URI2 -w $PASSWD \
+ > $TESTOUT 2>&1 <<EOF
+dn: cn=2, $BASEDN
+objectclass: device
+EOF
+
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+sed -e 's/@ID@/3/g' $ITSDIR/slapd-backend.conf | \
+ . $CONFFILTER $BACKEND > $CONF3
+
+echo "Starting remote slapd server on TCP/IP port $PORT3..."
+$SLAPD -f $CONF3 -h "$URI3" -d $LVL > $LOG3 2>&1 &
+SERVERPID3=$!
+if test $WAIT != 0 ; then
+ echo SERVERPID3 $SERVERPID3
+ read foo
+fi
+KILLPIDS="$KILLPIDS $SERVERPID3"
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI3 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPADD -D "cn=3,$BASEDN" -H $URI3 -w $PASSWD \
+ > $TESTOUT 2>&1 <<EOF
+dn: cn=3, $BASEDN
+objectclass: device
+EOF
+
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+#
+# Start ldapd that will proxy for the remote server
+#
+echo "Starting slapd proxy on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $ITSDIR/slapd-proxy.conf > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PROXYPID=$!
+if test $WAIT != 0 ; then
+ echo PROXYPID $PROXYPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PROXYPID"
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+#
+# Both servers running
+#
+
+echo "Checking proxying works..."
+$LDAPSEARCH -H $URI1 -b "$BASEDN" > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed at proxy ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Stopping backend and triggering quarantine..."
+kill -HUP $SERVERPID2
+wait $SERVERPID2
+KILLPIDS="$SERVERPID3 $PROXYPID"
+
+QUARANTINE_START=$(( `date +%s` ))
+EARLIEST_QUARANTINE_END=$(( $QUARANTINE_START + 20 ))
+$LDAPSEARCH -H $URI1 -b "cn=2,$BASEDN" -s base > $TESTOUT 2>&1
+RC=$?
+case $RC in
+52)
+ echo "ldapsearch failed ($RC)"
+ ;;
+0)
+ echo "ldapsearch should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+*)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Restarting remote slapd server on TCP/IP port $PORT2..."
+$SLAPD -f $CONF2 -h "$URI2" -d $LVL >> $LOG2 2>&1 &
+SERVERPID2=$!
+if test $WAIT != 0 ; then
+ echo SERVERPID2 $SERVERPID2
+ read foo
+fi
+KILLPIDS="$KILLPIDS $SERVERPID2"
+
+
+echo -n "Waiting for server to start up and quarantine to be lifted."
+for i in `seq 30`; do
+ $LDAPSEARCH -b "$BASEDN" -H $URI1 -z 1 > /dev/null 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ break
+ fi
+ echo -n '.'
+ sleep 1
+done
+
+MEASURED_END=`date +%s`
+case $RC in
+4)
+ echo "Server is back ($(( $MEASURED_END - $QUARANTINE_START ))s after start of quarantine)"
+ ;;
+0)
+ echo "Quarantine was never lifted!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+*)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+if test "$EARLIEST_QUARANTINE_END" -gt "$MEASURED_END" ; then
+ echo "Quarantine lifted $(( $EARLIEST_QUARANTINE_END - $MEASURED_END ))s too early"
+ exit $RC
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/data/regressions/its8721/slapd-backend.conf b/tests/data/regressions/its8721/slapd-backend.conf
new file mode 100644
index 0000000..c06935d
--- /dev/null
+++ b/tests/data/regressions/its8721/slapd-backend.conf
@@ -0,0 +1,38 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.m.pid
+argsfile @TESTDIR@/slapd.m.args
+
+#######################################################################
+# database definitions
+#######################################################################
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/:../servers/slapd/overlays
+#mod#moduleload back_@BACKEND@.la
+
+# here the proxy is not only acting as a proxy, but it also has a local database dc=local,dc=com"
+database @BACKEND@
+suffix "cn=@ID@,dc=example,dc=com"
+rootdn "cn=@ID@,dc=example,dc=com"
+rootpw "secret"
+#~null~#directory @TESTDIR@/db.@ID@.a
+
+database monitor
diff --git a/tests/data/regressions/its8721/slapd-proxy.conf b/tests/data/regressions/its8721/slapd-proxy.conf
new file mode 100644
index 0000000..10748b7
--- /dev/null
+++ b/tests/data/regressions/its8721/slapd-proxy.conf
@@ -0,0 +1,41 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.m.pid
+argsfile @TESTDIR@/slapd.m.args
+
+#######################################################################
+# database definitions
+#######################################################################
+
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+#metamod#modulepath ../servers/slapd/back-meta/
+#metamod#moduleload back_meta.la
+
+# Configure proxy
+database meta
+quarantine 20,+
+suffix "dc=example,dc=com"
+
+uri "@URI2@cn=2,dc=example,dc=com"
+uri "@URI3@cn=3,dc=example,dc=com"
+
+database monitor
diff --git a/tests/data/regressions/its8752/its8752 b/tests/data/regressions/its8752/its8752
new file mode 100755
index 0000000..02d9896
--- /dev/null
+++ b/tests/data/regressions/its8752/its8752
@@ -0,0 +1,513 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+if test $ACCESSLOG = accesslogno; then
+ echo "Accesslog overlay not available, test skipped"
+ exit 0
+fi
+if test $BACKEND = ldif ; then
+ echo "$BACKEND backend unsuitable, test skipped"
+ exit 0
+fi
+
+dtest=`date +%N|sed s/...$//`
+
+if test $dtest = N; then
+ echo "nanosecond date values not supported, test skipped"
+ exit 0
+fi
+
+# This mimics the scenario where a single server has been used until now (no
+# syncprov either, so no contextCSN) and we convert it to a delta-MPR setup:
+# 1. stop the server (note that there is likely no contextCSN in the DB at this point)
+# 2. configure all servers to delta-replicate from each other and start them up
+# - empty servers will start with a refresh of the main DB
+# - when the refresh is successful they should change over to replicating the log
+# 3. keep making changes on all servers to see things still work
+
+echo "This test tracks a case where slapd deadlocks during a significant write load"
+echo "See https://bugs.openldap.org/show_bug.cgi?id=8752 for more information."
+
+MPR=4
+iterations=20000
+check_sync_every=100
+MAPSIZE=`expr 100 \* 1024 \* 1024`
+XDIR=$TESTDIR/srv
+
+mkdir -p $TESTDIR
+
+ITS=8752
+ITSDIR=$DATADIR/regressions/its$ITS
+
+n=1
+while [ $n -le $MPR ]; do
+ DBDIR=${XDIR}$n/db
+ mkdir -p ${XDIR}$n $DBDIR.1 $DBDIR.2
+ n=`expr $n + 1`
+done
+
+KILLPIDS=
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $ITSDIR/slapd.conf > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep $SLEEP0
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Populating database on first provider..."
+$LDAPADD -D $MANAGERDN -H $URI1 -w $PASSWD << EOMODS >> $TESTOUT 2>&1
+dn: $BASEDN
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: ou=People,$BASEDN
+objectClass: organizationalUnit
+ou: People
+
+dn: ou=Groups,$BASEDN
+objectClass: organizationalUnit
+ou: Groups
+
+dn: cn=Roger Rabbit,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: Roger Rabbit
+sn: Rabbit
+
+dn: cn=Baby Herman,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: Baby Herman
+sn: Herman
+
+dn: cn=Jessica_Rabbit,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: Jessica_Rabbit
+sn: Rabbit
+
+dn: cn=Bugs_Bunny,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: Bugs_Bunny
+sn: Bunny
+
+dn: cn=Daffy_Duck,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: Daffy_Duck
+sn: Duck
+
+dn: cn=Elmer_Fudd,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: Elmer_Fudd
+sn: Fudd
+
+dn: cn=Cartoonia,ou=Groups,$BASEDN
+objectClass: groupOfNames
+cn: Cartoonia
+member: cn=Roger Rabbit,ou=People,$BASEDN
+member: cn=Baby Herman,ou=People,$BASEDN
+EOMODS
+
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Stopping slapd and reworking configuration for MPR..."
+
+kill -HUP $KILLPIDS
+wait $KILLPIDS
+
+KILLPIDS=
+n=1
+while [ $n -le $MPR ]; do
+ MYURI=`eval echo '$URI'$n`
+ MYLOG=`eval echo '$LOG'$n`
+ MYCONF=`eval echo '$CONF'$n`
+ echo "Starting provider slapd on TCP/IP URI $MYURI"
+ . $CONFFILTER $BACKEND < $ITSDIR/slapd.conf.mpr > $TESTDIR/slapd.conf
+ sed -e "s/MPR/$n/g" -e "s/wronglog/log/" -e "s/@MAPSIZE@/$MAPSIZE/" $TESTDIR/slapd.conf > $MYCONF
+ j=1
+ while [ $j -le $MPR ]; do
+ MMCURI=`eval echo '$URI'$j`
+ sed -e "s|MMC${j}|${MMCURI}|" $MYCONF > $TESTDIR/slapd.conf
+ mv $TESTDIR/slapd.conf $MYCONF
+ j=`expr $j + 1`
+ done
+ if [ -f $TESTDIR/slapd.conf ]; then
+ rm -f $TESTDIR/slapd.conf
+ fi
+ $SLAPD -f $MYCONF -h $MYURI -d $LVL >> $MYLOG 2>&1 &
+ PID=$!
+ if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+ fi
+ KILLPIDS="$PID $KILLPIDS"
+ sleep $SLEEP1
+
+ echo "Using ldapsearch to check that provider slapd is running..."
+ for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $MYURI \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+ done
+
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ n=`expr $n + 1`
+done
+
+echo "Setting up accesslog on each provider..."
+n=1
+while [ $n -le $MPR ]; do
+ echo "Modifying dn: cn=Elmer_Fudd,ou=People,$BASEDN on provider $n"
+ MYURI=`eval echo '$URI'$n`
+ $LDAPMODIFY -v -D "$MANAGERDN" -H $MYURI -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+
+dn: cn=Elmer_Fudd,ou=People,$BASEDN
+changetype: modify
+replace: sn
+sn: Fudd
+EOMODS
+ RC=$?
+ if test $RC != 0; then
+ echo "ldapmodify failed ($RC)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ sleep $SLEEP1
+ n=`expr $n + 1`
+done
+
+#echo "Letting server 1 establish its own contextCSN..."
+#echo "Modifying dn: cn=Elmer_Fudd,ou=People,$BASEDN on $URI1"
+#$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+# $TESTOUT 2>&1 << EOMODS
+#dn: cn=Elmer_Fudd,ou=People,$BASEDN
+#changetype: modify
+#replace: sn
+#sn: Fudd
+#EOMODS
+
+for i in 0 1 2 3 4 5; do
+ j=1
+ while [ $j -le $MPR ]; do
+ MYURI=`eval echo '$URI'$j`
+ $LDAPSEARCH -b "$BASEDN" -H "$MYURI" \
+ '*' '+' >"$TESTDIR/server$j.out" 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ $LDIFFILTER -s a < "$TESTDIR/server$j.out" > "$TESTDIR/server$j.flt"
+ j=`expr $j + 1`
+ done
+
+ in_sync=1
+ j=1
+ while [ $j -lt $MPR ]; do
+ k=$j
+ j=`expr $j + 1`
+ $CMP "$TESTDIR/server$k.flt" "$TESTDIR/server$j.flt" > $CMPOUT
+ if test $? != 0 ; then
+ in_sync=0
+ fi
+ done
+ if test $in_sync = 1; then
+ break
+ fi
+
+ echo "Waiting $SLEEP1 seconds for servers to catch up..."
+ sleep $SLEEP1
+done
+
+if test $in_sync = 0; then
+ echo "Servers did not replicate in time"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "The next step of the test will perform $iterations random write operations and may take some time."
+echo "As this test is for a deadlock, it will take manual intervention to exit the test if one occurs."
+
+echo "Starting random provider/entry modifications..."
+DN1="cn=Elmer_Fudd,ou=People,$BASEDN"
+VAL1="Fudd"
+
+DN2="cn=Jessica_Rabbit,ou=People,$BASEDN"
+VAL2="Rabbit"
+
+DN3="cn=Bugs_Bunny,ou=People,$BASEDN"
+VAL3="Bunny"
+
+DN4="cn=Daffy_Duck,ou=People,$BASEDN"
+VAL4="Duck"
+
+n=1
+while [ $n -le $iterations ]; do
+ seed=`date +%N|sed s/...$//`
+ rvalue=`echo|awk "BEGIN {srand($seed)
+{print int(1+rand()*$MPR)}}"`
+ MYURI=`eval echo '$URI'$rvalue`
+ seed=`date +%N|sed s/...$//`
+ rvalue=`echo|awk "BEGIN {srand($seed)
+{print int(1+rand()*4)}}"`
+ MYDN=`eval echo '$DN'$rvalue`
+ MYVAL=`eval echo '$VAL'$rvalue`
+ echo "Modifying $MYURI entry $MYDN with value $MYVAL iteration $n of $iterations"
+ $LDAPMODIFY -v -D "$MANAGERDN" -H $MYURI -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+
+dn: $MYDN
+changetype: modify
+replace: sn
+sn: $MYVAL
+EOMODS
+ RC=$?
+ if test $RC != 0; then
+ echo "ldapmodify failed ($RC)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+
+ if [ "$check_sync_every" -gt 0 ] && [ `expr $n % $check_sync_every` = 0 ]; then
+ i=1
+ echo "Checking replication status before we start iteration $n..."
+ for i in 0 1 2 3 4 5; do
+ j=1
+ while [ $j -le $MPR ]; do
+ MYURI=`eval echo '$URI'$j`
+ echo "Reading database from server $j..."
+ $LDAPSEARCH -b "$BASEDN" -H "$MYURI" \
+ '*' '+' >"$TESTDIR/server$j.out" 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ $LDIFFILTER -s a < "$TESTDIR/server$j.out" > "$TESTDIR/server$j.flt"
+ j=`expr $j + 1`
+ done
+
+ in_sync=1
+ j=1
+ while [ $j -lt $MPR ]; do
+ k=`expr $j + 1`
+ $CMP "$TESTDIR/server$j.flt" "$TESTDIR/server$k.flt" > $CMPOUT
+ if test $? != 0 ; then
+ in_sync=0
+ fi
+ j=$k
+ done
+ if test $in_sync = 1; then
+ break
+ fi
+
+ echo "Waiting $SLEEP1 seconds for servers to catch up..."
+ sleep $SLEEP1
+ done
+
+ if test $in_sync = 0; then
+ echo "Servers did not replicate in time"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ fi
+ n=`expr $n + 1`
+done
+
+kill -HUP $KILLPIDS
+wait $KILLPIDS
+
+echo "The next step of the test will perform $iterations random write operations and may take some time."
+echo "As this test is for a deadlock, it will take manual intervention to exit the test if one occurs."
+
+echo "Starting servers again, this time with the wrong logbase setting..."
+KILLPIDS=
+n=1
+while [ $n -le $MPR ]; do
+ MYURI=`eval echo '$URI'$n`
+ MYLOG=`eval echo '$LOG'$n`
+ MYCONF=`eval echo '$CONF'$n`
+ echo "Starting provider slapd on TCP/IP URI $MYURI"
+ . $CONFFILTER $BACKEND < $ITSDIR/slapd.conf.mpr > $TESTDIR/slapd.conf
+ sed -e "s/MPR/$n/g" -e "s/@MAPSIZE@/$MAPSIZE/" $TESTDIR/slapd.conf > $MYCONF
+ j=1
+ while [ $j -le $MPR ]; do
+ MMCURI=`eval echo '$URI'$j`
+ sed -e "s|MMC${j}|${MMCURI}|" $MYCONF > $TESTDIR/slapd.conf
+ mv $TESTDIR/slapd.conf $MYCONF
+ j=`expr $j + 1`
+ done
+ if [ -f $TESTDIR/slapd.conf ]; then
+ rm -f $TESTDIR/slapd.conf
+ fi
+ $SLAPD -f $MYCONF -h $MYURI -d $LVL >> $MYLOG 2>&1 &
+ PID=$!
+ if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+ fi
+ KILLPIDS="$PID $KILLPIDS"
+ sleep $SLEEP1
+
+ echo "Using ldapsearch to check that provider slapd is running..."
+ for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $MYURI \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+ done
+
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ n=`expr $n + 1`
+ done
+
+ echo "Starting random provider/entry modifications..."
+ n=1
+ while [ $n -le $iterations ]; do
+ seed=`date +%N|sed s/...$//`
+ rvalue=`echo|awk "BEGIN {srand($seed)
+ {print int(1+rand()*$MPR)}}"`
+ MYURI=`eval echo '$URI'$rvalue`
+ seed=`date +%N|sed s/...$//`
+ rvalue=`echo|awk "BEGIN {srand($seed)
+ {print int(1+rand()*4)}}"`
+ MYDN=`eval echo '$DN'$rvalue`
+ MYVAL=`eval echo '$VAL'$rvalue`
+ echo "Modifying $MYURI entry $MYDN with value $MYVAL iteration $n of $iterations"
+ $LDAPMODIFY -v -D "$MANAGERDN" -H $MYURI -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+
+dn: $MYDN
+changetype: modify
+replace: sn
+sn: $MYVAL
+EOMODS
+RC=$?
+if test $RC != 0; then
+ echo "ldapmodify failed ($RC)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+if [ "$check_sync_every" -gt 0 ] && [ `expr $n % $check_sync_every` = 0 ]; then
+ i=1
+ echo "Checking replication status before we start iteration $n..."
+ for i in 0 1 2 3 4 5; do
+ j=1
+ while [ $j -le $MPR ]; do
+ MYURI=`eval echo '$URI'$j`
+ echo "Reading database from server $j..."
+ $LDAPSEARCH -b "$BASEDN" -H "$MYURI" \
+ '*' '+' >"$TESTDIR/server$j.out" 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ $LDIFFILTER -s a < "$TESTDIR/server$j.out" > "$TESTDIR/server$j.flt"
+ j=`expr $j + 1`
+ done
+
+ in_sync=1
+ j=1
+ while [ $j -lt $MPR ]; do
+ k=`expr $j + 1`
+ $CMP "$TESTDIR/server$j.flt" "$TESTDIR/server$k.flt" > $CMPOUT
+ if test $? != 0 ; then
+ in_sync=0
+ fi
+ j=$k
+ done
+ if test $in_sync = 1; then
+ break
+ fi
+
+ echo "Waiting $SLEEP1 seconds for servers to catch up..."
+ sleep $SLEEP1
+ done
+
+ if test $in_sync = 0; then
+ echo "Servers did not replicate in time"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ fi
+ n=`expr $n + 1`
+done
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/data/regressions/its8752/slapd.conf b/tests/data/regressions/its8752/slapd.conf
new file mode 100644
index 0000000..aca705c
--- /dev/null
+++ b/tests/data/regressions/its8752/slapd.conf
@@ -0,0 +1,41 @@
+# stand-alone slapd config -- for testing (with indexing)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+include @DATADIR@/test.schema
+
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/:../servers/slapd/overlays
+#mod#moduleload back_@BACKEND@.la
+
+database config
+rootpw config
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/srv1/db.1
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+
+database monitor
diff --git a/tests/data/regressions/its8752/slapd.conf.mpr b/tests/data/regressions/its8752/slapd.conf.mpr
new file mode 100644
index 0000000..855eac8
--- /dev/null
+++ b/tests/data/regressions/its8752/slapd.conf.mpr
@@ -0,0 +1,142 @@
+# stand-alone slapd config -- for testing (with indexing)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+include @DATADIR@/test.schema
+
+#
+pidfile @TESTDIR@/slapd.MPR.pid
+argsfile @TESTDIR@/slapd.MPR.args
+
+serverid MPR
+#mod#modulepath ../servers/slapd/back-@BACKEND@/:../servers/slapd/overlays
+#mod#moduleload back_@BACKEND@.la
+#syncprovmod#modulepath ../servers/slapd/overlays/
+#syncprovmod#moduleload syncprov.la
+#accesslogmod#modulepath ../servers/slapd/overlays/
+#accesslogmod#moduleload accesslog.la
+
+database config
+rootpw config
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/srvMPR/db.1
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+
+syncrepl
+ rid=001
+ provider=MMC1
+ bindmethod=simple
+ binddn="cn=Manager,dc=example,dc=com"
+ credentials=secret
+ type=refreshAndPersist
+ searchbase="dc=example,dc=com"
+ filter="(objectclass=*)"
+ scope=sub
+ schemachecking=on
+ retry="5 10 60 +"
+ logbase="cn=log"
+ logfilter="(&(objectClass=auditWriteObject)(reqResult=0))"
+ syncdata=accesslog
+ sizeLimit=unlimited
+ timelimit=unlimited
+
+syncrepl
+ rid=002
+ provider=MMC2
+ bindmethod=simple
+ binddn="cn=Manager,dc=example,dc=com"
+ credentials=secret
+ type=refreshAndPersist
+ searchbase="dc=example,dc=com"
+ filter="(objectclass=*)"
+ scope=sub
+ schemachecking=on
+ retry="5 10 60 +"
+ logbase="cn=log"
+ logfilter="(&(objectClass=auditWriteObject)(reqResult=0))"
+ syncdata=accesslog
+ sizeLimit=unlimited
+ timelimit=unlimited
+
+syncrepl
+ rid=003
+ provider=MMC3
+ bindmethod=simple
+ binddn="cn=Manager,dc=example,dc=com"
+ credentials=secret
+ type=refreshAndPersist
+ searchbase="dc=example,dc=com"
+ filter="(objectclass=*)"
+ scope=sub
+ schemachecking=on
+ retry="5 10 60 +"
+ logbase="cn=log"
+ logfilter="(&(objectClass=auditWriteObject)(reqResult=0))"
+ syncdata=accesslog
+ sizeLimit=unlimited
+ timelimit=unlimited
+
+syncrepl
+ rid=004
+ provider=MMC4
+ bindmethod=simple
+ binddn="cn=Manager,dc=example,dc=com"
+ credentials=secret
+ type=refreshAndPersist
+ searchbase="dc=example,dc=com"
+ filter="(objectclass=*)"
+ scope=sub
+ schemachecking=on
+ retry="5 10 60 +"
+ logbase="cn=log"
+ logfilter="(&(objectClass=auditWriteObject)(reqResult=0))"
+ syncdata=accesslog
+ sizeLimit=unlimited
+ timelimit=unlimited
+
+multiprovider TRUE
+
+overlay syncprov
+syncprov-sessionlog 100
+syncprov-checkpoint 100 10
+
+overlay accesslog
+logdb cn=log
+logops writes
+logsuccess TRUE
+logpurge 24:00 01+00:00
+
+database @BACKEND@
+suffix "cn=log"
+rootdn "cn=Manager,dc=example,dc=com"
+#~null~#directory @TESTDIR@/srvMPR/db.2
+#indexdb#index objectClass eq
+#indexdb#index entryCSN,entryUUID,reqEnd,reqResult,reqStart eq
+#mdb#maxsize @MAPSIZE@
+
+overlay syncprov
+syncprov-reloadhint true
+syncprov-nopresent true
+
+database monitor
diff --git a/tests/data/regressions/its8800/db.ldif b/tests/data/regressions/its8800/db.ldif
new file mode 100644
index 0000000..4e6591c
--- /dev/null
+++ b/tests/data/regressions/its8800/db.ldif
@@ -0,0 +1,9380 @@
+dn: dc=example,dc=com
+objectClass: dcObject
+objectClass: organization
+dc: example
+o: example
+o: Example domain for holding data
+postalAddress: Example Organization, LLC$1234 Anywhere Street$Example, CA 99
+ 999
+description: Example Organization
+street: 1234 Anywhere Street
+l: Example
+st: CA
+postalCode: 99999
+telephoneNumber: +1 234 567 8910
+businessCategory: Example
+businessCategory: Examples
+structuralObjectClass: organization
+entryUUID: 156eb8cc-18e9-1027-80e5-d3f2010890dc
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20030512171533Z
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170401111056Z
+entryCSN: 20171130221813.848426Z#000000#004#000000
+contextCSN: 20171203010043.825769Z#000000#001#000000
+contextCSN: 20171130222521.056018Z#000000#002#000000
+contextCSN: 20171130222318.939265Z#000000#003#000000
+contextCSN: 20171201011219.228381Z#000000#004#000000
+
+dn: ou=user,dc=example,dc=com
+objectClass: organizationalUnit
+ou: user
+structuralObjectClass: organizationalUnit
+entryUUID: 159cc9b0-18e9-1027-80e6-d3f2010890dc
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20030512171533Z
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20030512171533Z
+entryCSN: 20171130221813.848561Z#000000#004#000000
+
+dn: uid=user.1,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.1
+mail: user.1@example.com
+o: example
+initials: u.1
+structuralObjectClass: inetOrgPerson
+uidNumber: 1005
+gidNumber: 1012
+loginShell: /bin/bash
+title: user.1
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.1
+cn: User 1
+displayName: User 1
+givenName: User
+sn: 1
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25845f6c-9eff-1037-879e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.736705Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.2,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.2
+mail: user.2@example.com
+o: example
+initials: u.2
+structuralObjectClass: inetOrgPerson
+uidNumber: 1006
+gidNumber: 1013
+loginShell: /bin/bash
+title: user.2
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.2
+cn: User 2
+displayName: User 2
+givenName: User
+sn: 2
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25846476-9eff-1037-879f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.736859Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.3,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.3
+mail: user.3@example.com
+o: example
+initials: u.3
+structuralObjectClass: inetOrgPerson
+uidNumber: 1007
+gidNumber: 1014
+loginShell: /bin/bash
+title: user.3
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.3
+cn: User 3
+displayName: User 3
+givenName: User
+sn: 3
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258467be-9eff-1037-87a0-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.736944Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.4,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.4
+mail: user.4@example.com
+o: example
+initials: u.4
+structuralObjectClass: inetOrgPerson
+uidNumber: 1008
+gidNumber: 1015
+loginShell: /bin/bash
+title: user.4
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.4
+cn: User 4
+displayName: User 4
+givenName: User
+sn: 4
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25846b10-9eff-1037-87a1-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737029Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.5,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.5
+mail: user.5@example.com
+o: example
+initials: u.5
+structuralObjectClass: inetOrgPerson
+uidNumber: 1009
+gidNumber: 1016
+loginShell: /bin/bash
+title: user.5
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.5
+cn: User 5
+displayName: User 5
+givenName: User
+sn: 5
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25846dae-9eff-1037-87a2-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737096Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.6,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.6
+mail: user.6@example.com
+o: example
+initials: u.6
+structuralObjectClass: inetOrgPerson
+uidNumber: 1010
+gidNumber: 1017
+loginShell: /bin/bash
+title: user.6
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.6
+cn: User 6
+displayName: User 6
+givenName: User
+sn: 6
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584709c-9eff-1037-87a3-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737171Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.7,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.7
+mail: user.7@example.com
+o: example
+initials: u.7
+structuralObjectClass: inetOrgPerson
+uidNumber: 1011
+gidNumber: 1018
+loginShell: /bin/bash
+title: user.7
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.7
+cn: User 7
+displayName: User 7
+givenName: User
+sn: 7
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25847312-9eff-1037-87a4-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737235Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.8,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.8
+mail: user.8@example.com
+o: example
+initials: u.8
+structuralObjectClass: inetOrgPerson
+uidNumber: 1012
+gidNumber: 1019
+loginShell: /bin/bash
+title: user.8
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.8
+cn: User 8
+displayName: User 8
+givenName: User
+sn: 8
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25847600-9eff-1037-87a5-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737309Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.9,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.9
+mail: user.9@example.com
+o: example
+initials: u.9
+structuralObjectClass: inetOrgPerson
+uidNumber: 1013
+gidNumber: 1020
+loginShell: /bin/bash
+title: user.9
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.9
+cn: User 9
+displayName: User 9
+givenName: User
+sn: 9
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25847880-9eff-1037-87a6-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737373Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.10,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.10
+mail: user.10@example.com
+o: example
+initials: u.10
+structuralObjectClass: inetOrgPerson
+uidNumber: 1014
+gidNumber: 1021
+loginShell: /bin/bash
+title: user.10
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.10
+cn: User 10
+displayName: User 10
+givenName: User
+sn: 10
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25847b14-9eff-1037-87a7-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737439Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.11,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.11
+mail: user.11@example.com
+o: example
+initials: u.11
+structuralObjectClass: inetOrgPerson
+uidNumber: 1015
+gidNumber: 1022
+loginShell: /bin/bash
+title: user.11
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.11
+cn: User 11
+displayName: User 11
+givenName: User
+sn: 11
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25847d94-9eff-1037-87a8-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737503Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.12,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.12
+mail: user.12@example.com
+o: example
+initials: u.12
+structuralObjectClass: inetOrgPerson
+uidNumber: 1016
+gidNumber: 1023
+loginShell: /bin/bash
+title: user.12
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.12
+cn: User 12
+displayName: User 12
+givenName: User
+sn: 12
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25848078-9eff-1037-87a9-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737577Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.13,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.13
+mail: user.13@example.com
+o: example
+initials: u.13
+structuralObjectClass: inetOrgPerson
+uidNumber: 1017
+gidNumber: 1024
+loginShell: /bin/bash
+title: user.13
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.13
+cn: User 13
+displayName: User 13
+givenName: User
+sn: 13
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258482ee-9eff-1037-87aa-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737641Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.14,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.14
+mail: user.14@example.com
+o: example
+initials: u.14
+structuralObjectClass: inetOrgPerson
+uidNumber: 1018
+gidNumber: 1025
+loginShell: /bin/bash
+title: user.14
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.14
+cn: User 14
+displayName: User 14
+givenName: User
+sn: 14
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584865e-9eff-1037-87ab-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737728Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.15,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.15
+mail: user.15@example.com
+o: example
+initials: u.15
+structuralObjectClass: inetOrgPerson
+uidNumber: 1019
+gidNumber: 1026
+loginShell: /bin/bash
+title: user.15
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.15
+cn: User 15
+displayName: User 15
+givenName: User
+sn: 15
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25848956-9eff-1037-87ac-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737804Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.16,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.16
+mail: user.16@example.com
+o: example
+initials: u.16
+structuralObjectClass: inetOrgPerson
+uidNumber: 1020
+gidNumber: 1027
+loginShell: /bin/bash
+title: user.16
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.16
+cn: User 16
+displayName: User 16
+givenName: User
+sn: 16
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25848bd6-9eff-1037-87ad-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737868Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.17,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.17
+mail: user.17@example.com
+o: example
+initials: u.17
+structuralObjectClass: inetOrgPerson
+uidNumber: 1021
+gidNumber: 1028
+loginShell: /bin/bash
+title: user.17
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.17
+cn: User 17
+displayName: User 17
+givenName: User
+sn: 17
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25848e38-9eff-1037-87ae-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737930Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.18,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.18
+mail: user.18@example.com
+o: example
+initials: u.18
+structuralObjectClass: inetOrgPerson
+uidNumber: 1022
+gidNumber: 1029
+loginShell: /bin/bash
+title: user.18
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.18
+cn: User 18
+displayName: User 18
+givenName: User
+sn: 18
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25849126-9eff-1037-87af-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738004Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.19,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.19
+mail: user.19@example.com
+o: example
+initials: u.19
+structuralObjectClass: inetOrgPerson
+uidNumber: 1023
+gidNumber: 1030
+loginShell: /bin/bash
+title: user.19
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.19
+cn: User 19
+displayName: User 19
+givenName: User
+sn: 19
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584939c-9eff-1037-87b0-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738067Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.20,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.20
+mail: user.20@example.com
+o: example
+initials: u.20
+structuralObjectClass: inetOrgPerson
+uidNumber: 1024
+gidNumber: 1031
+loginShell: /bin/bash
+title: user.20
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.20
+cn: User 20
+displayName: User 20
+givenName: User
+sn: 20
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258495fe-9eff-1037-87b1-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738128Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.21,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.21
+mail: user.21@example.com
+o: example
+initials: u.21
+structuralObjectClass: inetOrgPerson
+uidNumber: 1025
+gidNumber: 1032
+loginShell: /bin/bash
+title: user.21
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.21
+cn: User 21
+displayName: User 21
+givenName: User
+sn: 21
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584990a-9eff-1037-87b2-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738206Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.22,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.22
+mail: user.22@example.com
+o: example
+initials: u.22
+structuralObjectClass: inetOrgPerson
+uidNumber: 1026
+gidNumber: 1033
+loginShell: /bin/bash
+title: user.22
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.22
+cn: User 22
+displayName: User 22
+givenName: User
+sn: 22
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25849b9e-9eff-1037-87b3-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738272Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.23,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.23
+mail: user.23@example.com
+o: example
+initials: u.23
+structuralObjectClass: inetOrgPerson
+uidNumber: 1027
+gidNumber: 1034
+loginShell: /bin/bash
+title: user.23
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.23
+cn: User 23
+displayName: User 23
+givenName: User
+sn: 23
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25849e00-9eff-1037-87b4-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738334Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.24,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.24
+mail: user.24@example.com
+o: example
+initials: u.24
+structuralObjectClass: inetOrgPerson
+uidNumber: 1028
+gidNumber: 1035
+loginShell: /bin/bash
+title: user.24
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.24
+cn: User 24
+displayName: User 24
+givenName: User
+sn: 24
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584a0e4-9eff-1037-87b5-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738407Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.25,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.25
+mail: user.25@example.com
+o: example
+initials: u.25
+structuralObjectClass: inetOrgPerson
+uidNumber: 1029
+gidNumber: 1036
+loginShell: /bin/bash
+title: user.25
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.25
+cn: User 25
+displayName: User 25
+givenName: User
+sn: 25
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584a350-9eff-1037-87b6-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738469Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.26,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.26
+mail: user.26@example.com
+o: example
+initials: u.26
+structuralObjectClass: inetOrgPerson
+uidNumber: 1030
+gidNumber: 1037
+loginShell: /bin/bash
+title: user.26
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.26
+cn: User 26
+displayName: User 26
+givenName: User
+sn: 26
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584a67a-9eff-1037-87b7-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738551Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.27,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.27
+mail: user.27@example.com
+o: example
+initials: u.27
+structuralObjectClass: inetOrgPerson
+uidNumber: 1031
+gidNumber: 1038
+loginShell: /bin/bash
+title: user.27
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.27
+cn: User 27
+displayName: User 27
+givenName: User
+sn: 27
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584a940-9eff-1037-87b8-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738621Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.28,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.28
+mail: user.28@example.com
+o: example
+initials: u.28
+structuralObjectClass: inetOrgPerson
+uidNumber: 1032
+gidNumber: 1039
+loginShell: /bin/bash
+title: user.28
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.28
+cn: User 28
+displayName: User 28
+givenName: User
+sn: 28
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584ac1a-9eff-1037-87b9-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738693Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.29,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.29
+mail: user.29@example.com
+o: example
+initials: u.29
+structuralObjectClass: inetOrgPerson
+uidNumber: 1033
+gidNumber: 1040
+loginShell: /bin/bash
+title: user.29
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.29
+cn: User 29
+displayName: User 29
+givenName: User
+sn: 29
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584aecc-9eff-1037-87ba-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738763Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.30,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.30
+mail: user.30@example.com
+o: example
+initials: u.30
+structuralObjectClass: inetOrgPerson
+uidNumber: 1034
+gidNumber: 1041
+loginShell: /bin/bash
+title: user.30
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.30
+cn: User 30
+displayName: User 30
+givenName: User
+sn: 30
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584b1ba-9eff-1037-87bb-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738838Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.31,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.31
+mail: user.31@example.com
+o: example
+initials: u.31
+structuralObjectClass: inetOrgPerson
+uidNumber: 1035
+gidNumber: 1042
+loginShell: /bin/bash
+title: user.31
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.31
+cn: User 31
+displayName: User 31
+givenName: User
+sn: 31
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584b426-9eff-1037-87bc-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738900Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.32,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.32
+mail: user.32@example.com
+o: example
+initials: u.32
+structuralObjectClass: inetOrgPerson
+uidNumber: 1036
+gidNumber: 1043
+loginShell: /bin/bash
+title: user.32
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.32
+cn: User 32
+displayName: User 32
+givenName: User
+sn: 32
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584b688-9eff-1037-87bd-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738961Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.33,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.33
+mail: user.33@example.com
+o: example
+initials: u.33
+structuralObjectClass: inetOrgPerson
+uidNumber: 1037
+gidNumber: 1044
+loginShell: /bin/bash
+title: user.33
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.33
+cn: User 33
+displayName: User 33
+givenName: User
+sn: 33
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584b944-9eff-1037-87be-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739031Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.34,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.34
+mail: user.34@example.com
+o: example
+initials: u.34
+structuralObjectClass: inetOrgPerson
+uidNumber: 1038
+gidNumber: 1045
+loginShell: /bin/bash
+title: user.34
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.34
+cn: User 34
+displayName: User 34
+givenName: User
+sn: 34
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584bbc4-9eff-1037-87bf-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739095Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.35,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.35
+mail: user.35@example.com
+o: example
+initials: u.35
+structuralObjectClass: inetOrgPerson
+uidNumber: 1039
+gidNumber: 1046
+loginShell: /bin/bash
+title: user.35
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.35
+cn: User 35
+displayName: User 35
+givenName: User
+sn: 35
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584be1c-9eff-1037-87c0-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739156Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.36,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.36
+mail: user.36@example.com
+o: example
+initials: u.36
+structuralObjectClass: inetOrgPerson
+uidNumber: 1040
+gidNumber: 1047
+loginShell: /bin/bash
+title: user.36
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.36
+cn: User 36
+displayName: User 36
+givenName: User
+sn: 36
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584c150-9eff-1037-87c1-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739236Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.37,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.37
+mail: user.37@example.com
+o: example
+initials: u.37
+structuralObjectClass: inetOrgPerson
+uidNumber: 1041
+gidNumber: 1048
+loginShell: /bin/bash
+title: user.37
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.37
+cn: User 37
+displayName: User 37
+givenName: User
+sn: 37
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584c3c6-9eff-1037-87c2-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739301Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.38,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.38
+mail: user.38@example.com
+o: example
+initials: u.38
+structuralObjectClass: inetOrgPerson
+uidNumber: 1042
+gidNumber: 1049
+loginShell: /bin/bash
+title: user.38
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.38
+cn: User 38
+displayName: User 38
+givenName: User
+sn: 38
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584c740-9eff-1037-87c3-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739389Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.39,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.39
+mail: user.39@example.com
+o: example
+initials: u.39
+structuralObjectClass: inetOrgPerson
+uidNumber: 1043
+gidNumber: 1050
+loginShell: /bin/bash
+title: user.39
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.39
+cn: User 39
+displayName: User 39
+givenName: User
+sn: 39
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584ca10-9eff-1037-87c4-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739461Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.40,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.40
+mail: user.40@example.com
+o: example
+initials: u.40
+structuralObjectClass: inetOrgPerson
+uidNumber: 1044
+gidNumber: 1051
+loginShell: /bin/bash
+title: user.40
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.40
+cn: User 40
+displayName: User 40
+givenName: User
+sn: 40
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584ccf4-9eff-1037-87c5-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739535Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.41,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.41
+mail: user.41@example.com
+o: example
+initials: u.41
+structuralObjectClass: inetOrgPerson
+uidNumber: 1045
+gidNumber: 1052
+loginShell: /bin/bash
+title: user.41
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.41
+cn: User 41
+displayName: User 41
+givenName: User
+sn: 41
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584cf4c-9eff-1037-87c6-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739596Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.42,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.42
+mail: user.42@example.com
+o: example
+initials: u.42
+structuralObjectClass: inetOrgPerson
+uidNumber: 1046
+gidNumber: 1053
+loginShell: /bin/bash
+title: user.42
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.42
+cn: User 42
+displayName: User 42
+givenName: User
+sn: 42
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584d456-9eff-1037-87c7-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739723Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.43,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.43
+mail: user.43@example.com
+o: example
+initials: u.43
+structuralObjectClass: inetOrgPerson
+uidNumber: 1047
+gidNumber: 1054
+loginShell: /bin/bash
+title: user.43
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.43
+cn: User 43
+displayName: User 43
+givenName: User
+sn: 43
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584d708-9eff-1037-87c8-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739793Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.44,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.44
+mail: user.44@example.com
+o: example
+initials: u.44
+structuralObjectClass: inetOrgPerson
+uidNumber: 1048
+gidNumber: 1055
+loginShell: /bin/bash
+title: user.44
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.44
+cn: User 44
+displayName: User 44
+givenName: User
+sn: 44
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584d9c4-9eff-1037-87c9-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739863Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.45,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.45
+mail: user.45@example.com
+o: example
+initials: u.45
+structuralObjectClass: inetOrgPerson
+uidNumber: 1049
+gidNumber: 1056
+loginShell: /bin/bash
+title: user.45
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.45
+cn: User 45
+displayName: User 45
+givenName: User
+sn: 45
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584dcc6-9eff-1037-87ca-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739940Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.46,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.46
+mail: user.46@example.com
+o: example
+initials: u.46
+structuralObjectClass: inetOrgPerson
+uidNumber: 1050
+gidNumber: 1057
+loginShell: /bin/bash
+title: user.46
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.46
+cn: User 46
+displayName: User 46
+givenName: User
+sn: 46
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584df3c-9eff-1037-87cb-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740004Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.47,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.47
+mail: user.47@example.com
+o: example
+initials: u.47
+structuralObjectClass: inetOrgPerson
+uidNumber: 1051
+gidNumber: 1058
+loginShell: /bin/bash
+title: user.47
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.47
+cn: User 47
+displayName: User 47
+givenName: User
+sn: 47
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584e194-9eff-1037-87cc-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740063Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.48,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.48
+mail: user.48@example.com
+o: example
+initials: u.48
+structuralObjectClass: inetOrgPerson
+uidNumber: 1052
+gidNumber: 1059
+loginShell: /bin/bash
+title: user.48
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.48
+cn: User 48
+displayName: User 48
+givenName: User
+sn: 48
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584e450-9eff-1037-87cd-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740134Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.49,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.49
+mail: user.49@example.com
+o: example
+initials: u.49
+structuralObjectClass: inetOrgPerson
+uidNumber: 1053
+gidNumber: 1060
+loginShell: /bin/bash
+title: user.49
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.49
+cn: User 49
+displayName: User 49
+givenName: User
+sn: 49
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584e6a8-9eff-1037-87ce-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740194Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.50,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.50
+mail: user.50@example.com
+o: example
+initials: u.50
+structuralObjectClass: inetOrgPerson
+uidNumber: 1054
+gidNumber: 1061
+loginShell: /bin/bash
+title: user.50
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.50
+cn: User 50
+displayName: User 50
+givenName: User
+sn: 50
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584e914-9eff-1037-87cf-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740255Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.51,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.51
+mail: user.51@example.com
+o: example
+initials: u.51
+structuralObjectClass: inetOrgPerson
+uidNumber: 1055
+gidNumber: 1062
+loginShell: /bin/bash
+title: user.51
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.51
+cn: User 51
+displayName: User 51
+givenName: User
+sn: 51
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584ec0c-9eff-1037-87d0-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740331Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.52,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.52
+mail: user.52@example.com
+o: example
+initials: u.52
+structuralObjectClass: inetOrgPerson
+uidNumber: 1056
+gidNumber: 1063
+loginShell: /bin/bash
+title: user.52
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.52
+cn: User 52
+displayName: User 52
+givenName: User
+sn: 52
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584ee78-9eff-1037-87d1-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740394Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.53,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.53
+mail: user.53@example.com
+o: example
+initials: u.53
+structuralObjectClass: inetOrgPerson
+uidNumber: 1057
+gidNumber: 1064
+loginShell: /bin/bash
+title: user.53
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.53
+cn: User 53
+displayName: User 53
+givenName: User
+sn: 53
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584f0d0-9eff-1037-87d2-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740453Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.54,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.54
+mail: user.54@example.com
+o: example
+initials: u.54
+structuralObjectClass: inetOrgPerson
+uidNumber: 1058
+gidNumber: 1065
+loginShell: /bin/bash
+title: user.54
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.54
+cn: User 54
+displayName: User 54
+givenName: User
+sn: 54
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584f3a0-9eff-1037-87d3-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740525Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.55,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.55
+mail: user.55@example.com
+o: example
+initials: u.55
+structuralObjectClass: inetOrgPerson
+uidNumber: 1059
+gidNumber: 1066
+loginShell: /bin/bash
+title: user.55
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.55
+cn: User 55
+displayName: User 55
+givenName: User
+sn: 55
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584f602-9eff-1037-87d4-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740586Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.56,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.56
+mail: user.56@example.com
+o: example
+initials: u.56
+structuralObjectClass: inetOrgPerson
+uidNumber: 1060
+gidNumber: 1067
+loginShell: /bin/bash
+title: user.56
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.56
+cn: User 56
+displayName: User 56
+givenName: User
+sn: 56
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584f8b4-9eff-1037-87d5-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740655Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.57,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.57
+mail: user.57@example.com
+o: example
+initials: u.57
+structuralObjectClass: inetOrgPerson
+uidNumber: 1061
+gidNumber: 1068
+loginShell: /bin/bash
+title: user.57
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.57
+cn: User 57
+displayName: User 57
+givenName: User
+sn: 57
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584fba2-9eff-1037-87d6-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740730Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.58,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.58
+mail: user.58@example.com
+o: example
+initials: u.58
+structuralObjectClass: inetOrgPerson
+uidNumber: 1062
+gidNumber: 1069
+loginShell: /bin/bash
+title: user.58
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.58
+cn: User 58
+displayName: User 58
+givenName: User
+sn: 58
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584fe22-9eff-1037-87d7-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740794Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.59,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.59
+mail: user.59@example.com
+o: example
+initials: u.59
+structuralObjectClass: inetOrgPerson
+uidNumber: 1063
+gidNumber: 1070
+loginShell: /bin/bash
+title: user.59
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.59
+cn: User 59
+displayName: User 59
+givenName: User
+sn: 59
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258500c0-9eff-1037-87d8-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740862Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.60,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.60
+mail: user.60@example.com
+o: example
+initials: u.60
+structuralObjectClass: inetOrgPerson
+uidNumber: 1064
+gidNumber: 1071
+loginShell: /bin/bash
+title: user.60
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.60
+cn: User 60
+displayName: User 60
+givenName: User
+sn: 60
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585039a-9eff-1037-87d9-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740934Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.61,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.61
+mail: user.61@example.com
+o: example
+initials: u.61
+structuralObjectClass: inetOrgPerson
+uidNumber: 1065
+gidNumber: 1072
+loginShell: /bin/bash
+title: user.61
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.61
+cn: User 61
+displayName: User 61
+givenName: User
+sn: 61
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258505fc-9eff-1037-87da-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740995Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.62,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.62
+mail: user.62@example.com
+o: example
+initials: u.62
+structuralObjectClass: inetOrgPerson
+uidNumber: 1066
+gidNumber: 1073
+loginShell: /bin/bash
+title: user.62
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.62
+cn: User 62
+displayName: User 62
+givenName: User
+sn: 62
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25850872-9eff-1037-87db-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741058Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.63,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.63
+mail: user.63@example.com
+o: example
+initials: u.63
+structuralObjectClass: inetOrgPerson
+uidNumber: 1067
+gidNumber: 1074
+loginShell: /bin/bash
+title: user.63
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.63
+cn: User 63
+displayName: User 63
+givenName: User
+sn: 63
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25850b24-9eff-1037-87dc-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741127Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.64,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.64
+mail: user.64@example.com
+o: example
+initials: u.64
+structuralObjectClass: inetOrgPerson
+uidNumber: 1068
+gidNumber: 1075
+loginShell: /bin/bash
+title: user.64
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.64
+cn: User 64
+displayName: User 64
+givenName: User
+sn: 64
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25850d90-9eff-1037-87dd-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741189Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.65,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.65
+mail: user.65@example.com
+o: example
+initials: u.65
+structuralObjectClass: inetOrgPerson
+uidNumber: 1069
+gidNumber: 1076
+loginShell: /bin/bash
+title: user.65
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.65
+cn: User 65
+displayName: User 65
+givenName: User
+sn: 65
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25850fde-9eff-1037-87de-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741248Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.66,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.66
+mail: user.66@example.com
+o: example
+initials: u.66
+structuralObjectClass: inetOrgPerson
+uidNumber: 1070
+gidNumber: 1077
+loginShell: /bin/bash
+title: user.66
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.66
+cn: User 66
+displayName: User 66
+givenName: User
+sn: 66
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258512ea-9eff-1037-87df-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741326Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.67,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.67
+mail: user.67@example.com
+o: example
+initials: u.67
+structuralObjectClass: inetOrgPerson
+uidNumber: 1071
+gidNumber: 1078
+loginShell: /bin/bash
+title: user.67
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.67
+cn: User 67
+displayName: User 67
+givenName: User
+sn: 67
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585154c-9eff-1037-87e0-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741388Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.68,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.68
+mail: user.68@example.com
+o: example
+initials: u.68
+structuralObjectClass: inetOrgPerson
+uidNumber: 1072
+gidNumber: 1079
+loginShell: /bin/bash
+title: user.68
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.68
+cn: User 68
+displayName: User 68
+givenName: User
+sn: 68
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258517ae-9eff-1037-87e1-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741448Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.69,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.69
+mail: user.69@example.com
+o: example
+initials: u.69
+structuralObjectClass: inetOrgPerson
+uidNumber: 1073
+gidNumber: 1080
+loginShell: /bin/bash
+title: user.69
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.69
+cn: User 69
+displayName: User 69
+givenName: User
+sn: 69
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25851a6a-9eff-1037-87e2-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741518Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.70,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.70
+mail: user.70@example.com
+o: example
+initials: u.70
+structuralObjectClass: inetOrgPerson
+uidNumber: 1074
+gidNumber: 1081
+loginShell: /bin/bash
+title: user.70
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.70
+cn: User 70
+displayName: User 70
+givenName: User
+sn: 70
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25851cea-9eff-1037-87e3-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741582Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.71,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.71
+mail: user.71@example.com
+o: example
+initials: u.71
+structuralObjectClass: inetOrgPerson
+uidNumber: 1075
+gidNumber: 1082
+loginShell: /bin/bash
+title: user.71
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.71
+cn: User 71
+displayName: User 71
+givenName: User
+sn: 71
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25851f38-9eff-1037-87e4-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741641Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.72,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.72
+mail: user.72@example.com
+o: example
+initials: u.72
+structuralObjectClass: inetOrgPerson
+uidNumber: 1076
+gidNumber: 1083
+loginShell: /bin/bash
+title: user.72
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.72
+cn: User 72
+displayName: User 72
+givenName: User
+sn: 72
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585229e-9eff-1037-87e5-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741728Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.73,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.73
+mail: user.73@example.com
+o: example
+initials: u.73
+structuralObjectClass: inetOrgPerson
+uidNumber: 1077
+gidNumber: 1084
+loginShell: /bin/bash
+title: user.73
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.73
+cn: User 73
+displayName: User 73
+givenName: User
+sn: 73
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25852550-9eff-1037-87e6-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741797Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.74,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.74
+mail: user.74@example.com
+o: example
+initials: u.74
+structuralObjectClass: inetOrgPerson
+uidNumber: 1078
+gidNumber: 1085
+loginShell: /bin/bash
+title: user.74
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.74
+cn: User 74
+displayName: User 74
+givenName: User
+sn: 74
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258527da-9eff-1037-87e7-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741862Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.75,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.75
+mail: user.75@example.com
+o: example
+initials: u.75
+structuralObjectClass: inetOrgPerson
+uidNumber: 1079
+gidNumber: 1086
+loginShell: /bin/bash
+title: user.75
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.75
+cn: User 75
+displayName: User 75
+givenName: User
+sn: 75
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25852aa0-9eff-1037-87e8-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741933Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.76,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.76
+mail: user.76@example.com
+o: example
+initials: u.76
+structuralObjectClass: inetOrgPerson
+uidNumber: 1080
+gidNumber: 1087
+loginShell: /bin/bash
+title: user.76
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.76
+cn: User 76
+displayName: User 76
+givenName: User
+sn: 76
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25852d0c-9eff-1037-87e9-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741996Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.77,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.77
+mail: user.77@example.com
+o: example
+initials: u.77
+structuralObjectClass: inetOrgPerson
+uidNumber: 1081
+gidNumber: 1088
+loginShell: /bin/bash
+title: user.77
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.77
+cn: User 77
+displayName: User 77
+givenName: User
+sn: 77
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25852f64-9eff-1037-87ea-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742055Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.78,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.78
+mail: user.78@example.com
+o: example
+initials: u.78
+structuralObjectClass: inetOrgPerson
+uidNumber: 1082
+gidNumber: 1089
+loginShell: /bin/bash
+title: user.78
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.78
+cn: User 78
+displayName: User 78
+givenName: User
+sn: 78
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585323e-9eff-1037-87eb-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742128Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.79,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.79
+mail: user.79@example.com
+o: example
+initials: u.79
+structuralObjectClass: inetOrgPerson
+uidNumber: 1083
+gidNumber: 1090
+loginShell: /bin/bash
+title: user.79
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.79
+cn: User 79
+displayName: User 79
+givenName: User
+sn: 79
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25853496-9eff-1037-87ec-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742189Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.80,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.80
+mail: user.80@example.com
+o: example
+initials: u.80
+structuralObjectClass: inetOrgPerson
+uidNumber: 1084
+gidNumber: 1091
+loginShell: /bin/bash
+title: user.80
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.80
+cn: User 80
+displayName: User 80
+givenName: User
+sn: 80
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25853810-9eff-1037-87ed-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742277Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.81,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.81
+mail: user.81@example.com
+o: example
+initials: u.81
+structuralObjectClass: inetOrgPerson
+uidNumber: 1085
+gidNumber: 1092
+loginShell: /bin/bash
+title: user.81
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.81
+cn: User 81
+displayName: User 81
+givenName: User
+sn: 81
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25853b1c-9eff-1037-87ee-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742355Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.82,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.82
+mail: user.82@example.com
+o: example
+initials: u.82
+structuralObjectClass: inetOrgPerson
+uidNumber: 1086
+gidNumber: 1093
+loginShell: /bin/bash
+title: user.82
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.82
+cn: User 82
+displayName: User 82
+givenName: User
+sn: 82
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25853da6-9eff-1037-87ef-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742420Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.83,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.83
+mail: user.83@example.com
+o: example
+initials: u.83
+structuralObjectClass: inetOrgPerson
+uidNumber: 1087
+gidNumber: 1094
+loginShell: /bin/bash
+title: user.83
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.83
+cn: User 83
+displayName: User 83
+givenName: User
+sn: 83
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25854008-9eff-1037-87f0-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742481Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.84,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.84
+mail: user.84@example.com
+o: example
+initials: u.84
+structuralObjectClass: inetOrgPerson
+uidNumber: 1088
+gidNumber: 1095
+loginShell: /bin/bash
+title: user.84
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.84
+cn: User 84
+displayName: User 84
+givenName: User
+sn: 84
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258542ce-9eff-1037-87f1-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742553Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.85,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.85
+mail: user.85@example.com
+o: example
+initials: u.85
+structuralObjectClass: inetOrgPerson
+uidNumber: 1089
+gidNumber: 1096
+loginShell: /bin/bash
+title: user.85
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.85
+cn: User 85
+displayName: User 85
+givenName: User
+sn: 85
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25854530-9eff-1037-87f2-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742614Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.86,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.86
+mail: user.86@example.com
+o: example
+initials: u.86
+structuralObjectClass: inetOrgPerson
+uidNumber: 1090
+gidNumber: 1097
+loginShell: /bin/bash
+title: user.86
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.86
+cn: User 86
+displayName: User 86
+givenName: User
+sn: 86
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585481e-9eff-1037-87f3-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742688Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.87,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.87
+mail: user.87@example.com
+o: example
+initials: u.87
+structuralObjectClass: inetOrgPerson
+uidNumber: 1091
+gidNumber: 1098
+loginShell: /bin/bash
+title: user.87
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.87
+cn: User 87
+displayName: User 87
+givenName: User
+sn: 87
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25854aee-9eff-1037-87f4-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742761Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.88,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.88
+mail: user.88@example.com
+o: example
+initials: u.88
+structuralObjectClass: inetOrgPerson
+uidNumber: 1092
+gidNumber: 1099
+loginShell: /bin/bash
+title: user.88
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.88
+cn: User 88
+displayName: User 88
+givenName: User
+sn: 88
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25854db4-9eff-1037-87f5-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742831Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.89,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.89
+mail: user.89@example.com
+o: example
+initials: u.89
+structuralObjectClass: inetOrgPerson
+uidNumber: 1093
+gidNumber: 1100
+loginShell: /bin/bash
+title: user.89
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.89
+cn: User 89
+displayName: User 89
+givenName: User
+sn: 89
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585500c-9eff-1037-87f6-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742891Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.90,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.90
+mail: user.90@example.com
+o: example
+initials: u.90
+structuralObjectClass: inetOrgPerson
+uidNumber: 1094
+gidNumber: 1101
+loginShell: /bin/bash
+title: user.90
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.90
+cn: User 90
+displayName: User 90
+givenName: User
+sn: 90
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258552f0-9eff-1037-87f7-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742965Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.91,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.91
+mail: user.91@example.com
+o: example
+initials: u.91
+structuralObjectClass: inetOrgPerson
+uidNumber: 1095
+gidNumber: 1102
+loginShell: /bin/bash
+title: user.91
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.91
+cn: User 91
+displayName: User 91
+givenName: User
+sn: 91
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25855552-9eff-1037-87f8-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743027Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.92,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.92
+mail: user.92@example.com
+o: example
+initials: u.92
+structuralObjectClass: inetOrgPerson
+uidNumber: 1096
+gidNumber: 1103
+loginShell: /bin/bash
+title: user.92
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.92
+cn: User 92
+displayName: User 92
+givenName: User
+sn: 92
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258557b4-9eff-1037-87f9-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743087Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.93,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.93
+mail: user.93@example.com
+o: example
+initials: u.93
+structuralObjectClass: inetOrgPerson
+uidNumber: 1097
+gidNumber: 1104
+loginShell: /bin/bash
+title: user.93
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.93
+cn: User 93
+displayName: User 93
+givenName: User
+sn: 93
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25855a70-9eff-1037-87fa-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743157Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.94,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.94
+mail: user.94@example.com
+o: example
+initials: u.94
+structuralObjectClass: inetOrgPerson
+uidNumber: 1098
+gidNumber: 1105
+loginShell: /bin/bash
+title: user.94
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.94
+cn: User 94
+displayName: User 94
+givenName: User
+sn: 94
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25855d18-9eff-1037-87fb-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743225Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.95,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.95
+mail: user.95@example.com
+o: example
+initials: u.95
+structuralObjectClass: inetOrgPerson
+uidNumber: 1099
+gidNumber: 1106
+loginShell: /bin/bash
+title: user.95
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.95
+cn: User 95
+displayName: User 95
+givenName: User
+sn: 95
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25855f84-9eff-1037-87fc-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743287Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.96,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.96
+mail: user.96@example.com
+o: example
+initials: u.96
+structuralObjectClass: inetOrgPerson
+uidNumber: 1100
+gidNumber: 1107
+loginShell: /bin/bash
+title: user.96
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.96
+cn: User 96
+displayName: User 96
+givenName: User
+sn: 96
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25856308-9eff-1037-87fd-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743377Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.97,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.97
+mail: user.97@example.com
+o: example
+initials: u.97
+structuralObjectClass: inetOrgPerson
+uidNumber: 1101
+gidNumber: 1108
+loginShell: /bin/bash
+title: user.97
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.97
+cn: User 97
+displayName: User 97
+givenName: User
+sn: 97
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258565ec-9eff-1037-87fe-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743451Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.98,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.98
+mail: user.98@example.com
+o: example
+initials: u.98
+structuralObjectClass: inetOrgPerson
+uidNumber: 1102
+gidNumber: 1109
+loginShell: /bin/bash
+title: user.98
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.98
+cn: User 98
+displayName: User 98
+givenName: User
+sn: 98
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25856862-9eff-1037-87ff-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743515Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.99,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.99
+mail: user.99@example.com
+o: example
+initials: u.99
+structuralObjectClass: inetOrgPerson
+uidNumber: 1103
+gidNumber: 1110
+loginShell: /bin/bash
+title: user.99
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.99
+cn: User 99
+displayName: User 99
+givenName: User
+sn: 99
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25856b46-9eff-1037-8800-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743588Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.100,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.100
+mail: user.100@example.com
+o: example
+initials: u.100
+structuralObjectClass: inetOrgPerson
+uidNumber: 1104
+gidNumber: 1111
+loginShell: /bin/bash
+title: user.100
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.100
+cn: User 100
+displayName: User 100
+givenName: User
+sn: 100
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25856f6a-9eff-1037-8801-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743692Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.101,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.101
+mail: user.101@example.com
+o: example
+initials: u.101
+structuralObjectClass: inetOrgPerson
+uidNumber: 1105
+gidNumber: 1112
+loginShell: /bin/bash
+title: user.101
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.101
+cn: User 101
+displayName: User 101
+givenName: User
+sn: 101
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585723a-9eff-1037-8802-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743767Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.102,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.102
+mail: user.102@example.com
+o: example
+initials: u.102
+structuralObjectClass: inetOrgPerson
+uidNumber: 1106
+gidNumber: 1113
+loginShell: /bin/bash
+title: user.102
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.102
+cn: User 102
+displayName: User 102
+givenName: User
+sn: 102
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25857550-9eff-1037-8803-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743845Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.103,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.103
+mail: user.103@example.com
+o: example
+initials: u.103
+structuralObjectClass: inetOrgPerson
+uidNumber: 1107
+gidNumber: 1114
+loginShell: /bin/bash
+title: user.103
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.103
+cn: User 103
+displayName: User 103
+givenName: User
+sn: 103
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25857816-9eff-1037-8804-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743916Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.104,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.104
+mail: user.104@example.com
+o: example
+initials: u.104
+structuralObjectClass: inetOrgPerson
+uidNumber: 1108
+gidNumber: 1115
+loginShell: /bin/bash
+title: user.104
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.104
+cn: User 104
+displayName: User 104
+givenName: User
+sn: 104
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25857a96-9eff-1037-8805-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743980Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.105,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.105
+mail: user.105@example.com
+o: example
+initials: u.105
+structuralObjectClass: inetOrgPerson
+uidNumber: 1109
+gidNumber: 1116
+loginShell: /bin/bash
+title: user.105
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.105
+cn: User 105
+displayName: User 105
+givenName: User
+sn: 105
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25857d66-9eff-1037-8806-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744052Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.106,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.106
+mail: user.106@example.com
+o: example
+initials: u.106
+structuralObjectClass: inetOrgPerson
+uidNumber: 1110
+gidNumber: 1117
+loginShell: /bin/bash
+title: user.106
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.106
+cn: User 106
+displayName: User 106
+givenName: User
+sn: 106
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25857ff0-9eff-1037-8807-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744118Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.107,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.107
+mail: user.107@example.com
+o: example
+initials: u.107
+structuralObjectClass: inetOrgPerson
+uidNumber: 1111
+gidNumber: 1118
+loginShell: /bin/bash
+title: user.107
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.107
+cn: User 107
+displayName: User 107
+givenName: User
+sn: 107
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585825c-9eff-1037-8808-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744179Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.108,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.108
+mail: user.108@example.com
+o: example
+initials: u.108
+structuralObjectClass: inetOrgPerson
+uidNumber: 1112
+gidNumber: 1119
+loginShell: /bin/bash
+title: user.108
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.108
+cn: User 108
+displayName: User 108
+givenName: User
+sn: 108
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585852c-9eff-1037-8809-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744251Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.109,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.109
+mail: user.109@example.com
+o: example
+initials: u.109
+structuralObjectClass: inetOrgPerson
+uidNumber: 1113
+gidNumber: 1120
+loginShell: /bin/bash
+title: user.109
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.109
+cn: User 109
+displayName: User 109
+givenName: User
+sn: 109
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25858798-9eff-1037-880a-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744313Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.110,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.110
+mail: user.110@example.com
+o: example
+initials: u.110
+structuralObjectClass: inetOrgPerson
+uidNumber: 1114
+gidNumber: 1121
+loginShell: /bin/bash
+title: user.110
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.110
+cn: User 110
+displayName: User 110
+givenName: User
+sn: 110
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25858a18-9eff-1037-880b-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744377Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.111,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.111
+mail: user.111@example.com
+o: example
+initials: u.111
+structuralObjectClass: inetOrgPerson
+uidNumber: 1115
+gidNumber: 1122
+loginShell: /bin/bash
+title: user.111
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.111
+cn: User 111
+displayName: User 111
+givenName: User
+sn: 111
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25858d1a-9eff-1037-880c-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744455Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.112,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.112
+mail: user.112@example.com
+o: example
+initials: u.112
+structuralObjectClass: inetOrgPerson
+uidNumber: 1116
+gidNumber: 1123
+loginShell: /bin/bash
+title: user.112
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.112
+cn: User 112
+displayName: User 112
+givenName: User
+sn: 112
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25858fa4-9eff-1037-880d-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744519Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.113,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.113
+mail: user.113@example.com
+o: example
+initials: u.113
+structuralObjectClass: inetOrgPerson
+uidNumber: 1117
+gidNumber: 1124
+loginShell: /bin/bash
+title: user.113
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.113
+cn: User 113
+displayName: User 113
+givenName: User
+sn: 113
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25859206-9eff-1037-880e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744581Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.114,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.114
+mail: user.114@example.com
+o: example
+initials: u.114
+structuralObjectClass: inetOrgPerson
+uidNumber: 1118
+gidNumber: 1125
+loginShell: /bin/bash
+title: user.114
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.114
+cn: User 114
+displayName: User 114
+givenName: User
+sn: 114
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25859558-9eff-1037-880f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744664Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.115,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.115
+mail: user.115@example.com
+o: example
+initials: u.115
+structuralObjectClass: inetOrgPerson
+uidNumber: 1119
+gidNumber: 1126
+loginShell: /bin/bash
+title: user.115
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.115
+cn: User 115
+displayName: User 115
+givenName: User
+sn: 115
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258597e2-9eff-1037-8810-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744730Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.116,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.116
+mail: user.116@example.com
+o: example
+initials: u.116
+structuralObjectClass: inetOrgPerson
+uidNumber: 1120
+gidNumber: 1127
+loginShell: /bin/bash
+title: user.116
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.116
+cn: User 116
+displayName: User 116
+givenName: User
+sn: 116
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25859a4e-9eff-1037-8811-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744792Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.117,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.117
+mail: user.117@example.com
+o: example
+initials: u.117
+structuralObjectClass: inetOrgPerson
+uidNumber: 1121
+gidNumber: 1128
+loginShell: /bin/bash
+title: user.117
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.117
+cn: User 117
+displayName: User 117
+givenName: User
+sn: 117
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25859d1e-9eff-1037-8812-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744864Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.118,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.118
+mail: user.118@example.com
+o: example
+initials: u.118
+structuralObjectClass: inetOrgPerson
+uidNumber: 1122
+gidNumber: 1129
+loginShell: /bin/bash
+title: user.118
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.118
+cn: User 118
+displayName: User 118
+givenName: User
+sn: 118
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25859ff8-9eff-1037-8813-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744937Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.119,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.119
+mail: user.119@example.com
+o: example
+initials: u.119
+structuralObjectClass: inetOrgPerson
+uidNumber: 1123
+gidNumber: 1130
+loginShell: /bin/bash
+title: user.119
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.119
+cn: User 119
+displayName: User 119
+givenName: User
+sn: 119
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585a264-9eff-1037-8814-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745000Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.120,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.120
+mail: user.120@example.com
+o: example
+initials: u.120
+structuralObjectClass: inetOrgPerson
+uidNumber: 1124
+gidNumber: 1131
+loginShell: /bin/bash
+title: user.120
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.120
+cn: User 120
+displayName: User 120
+givenName: User
+sn: 120
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585a53e-9eff-1037-8815-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745072Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.121,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.121
+mail: user.121@example.com
+o: example
+initials: u.121
+structuralObjectClass: inetOrgPerson
+uidNumber: 1125
+gidNumber: 1132
+loginShell: /bin/bash
+title: user.121
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.121
+cn: User 121
+displayName: User 121
+givenName: User
+sn: 121
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585a7aa-9eff-1037-8816-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745134Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.122,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.122
+mail: user.122@example.com
+o: example
+initials: u.122
+structuralObjectClass: inetOrgPerson
+uidNumber: 1126
+gidNumber: 1133
+loginShell: /bin/bash
+title: user.122
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.122
+cn: User 122
+displayName: User 122
+givenName: User
+sn: 122
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585aa2a-9eff-1037-8817-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745198Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.123,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.123
+mail: user.123@example.com
+o: example
+initials: u.123
+structuralObjectClass: inetOrgPerson
+uidNumber: 1127
+gidNumber: 1134
+loginShell: /bin/bash
+title: user.123
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.123
+cn: User 123
+displayName: User 123
+givenName: User
+sn: 123
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585acf0-9eff-1037-8818-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745270Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.124,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.124
+mail: user.124@example.com
+o: example
+initials: u.124
+structuralObjectClass: inetOrgPerson
+uidNumber: 1128
+gidNumber: 1135
+loginShell: /bin/bash
+title: user.124
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.124
+cn: User 124
+displayName: User 124
+givenName: User
+sn: 124
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585af66-9eff-1037-8819-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745333Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.125,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.125
+mail: user.125@example.com
+o: example
+initials: u.125
+structuralObjectClass: inetOrgPerson
+uidNumber: 1129
+gidNumber: 1136
+loginShell: /bin/bash
+title: user.125
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.125
+cn: User 125
+displayName: User 125
+givenName: User
+sn: 125
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585b222-9eff-1037-881a-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745402Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.126,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.126
+mail: user.126@example.com
+o: example
+initials: u.126
+structuralObjectClass: inetOrgPerson
+uidNumber: 1130
+gidNumber: 1137
+loginShell: /bin/bash
+title: user.126
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.126
+cn: User 126
+displayName: User 126
+givenName: User
+sn: 126
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585b506-9eff-1037-881b-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745476Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.127,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.127
+mail: user.127@example.com
+o: example
+initials: u.127
+structuralObjectClass: inetOrgPerson
+uidNumber: 1131
+gidNumber: 1138
+loginShell: /bin/bash
+title: user.127
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.127
+cn: User 127
+displayName: User 127
+givenName: User
+sn: 127
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585b772-9eff-1037-881c-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745539Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.128,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.128
+mail: user.128@example.com
+o: example
+initials: u.128
+structuralObjectClass: inetOrgPerson
+uidNumber: 1132
+gidNumber: 1139
+loginShell: /bin/bash
+title: user.128
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.128
+cn: User 128
+displayName: User 128
+givenName: User
+sn: 128
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585b9e8-9eff-1037-881d-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745601Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.129,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.129
+mail: user.129@example.com
+o: example
+initials: u.129
+structuralObjectClass: inetOrgPerson
+uidNumber: 1133
+gidNumber: 1140
+loginShell: /bin/bash
+title: user.129
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.129
+cn: User 129
+displayName: User 129
+givenName: User
+sn: 129
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585bd12-9eff-1037-881e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745682Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.130,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.130
+mail: user.130@example.com
+o: example
+initials: u.130
+structuralObjectClass: inetOrgPerson
+uidNumber: 1134
+gidNumber: 1141
+loginShell: /bin/bash
+title: user.130
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.130
+cn: User 130
+displayName: User 130
+givenName: User
+sn: 130
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585bfba-9eff-1037-881f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745750Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.131,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.131
+mail: user.131@example.com
+o: example
+initials: u.131
+structuralObjectClass: inetOrgPerson
+uidNumber: 1135
+gidNumber: 1142
+loginShell: /bin/bash
+title: user.131
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.131
+cn: User 131
+displayName: User 131
+givenName: User
+sn: 131
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585c21c-9eff-1037-8820-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745811Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.132,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.132
+mail: user.132@example.com
+o: example
+initials: u.132
+structuralObjectClass: inetOrgPerson
+uidNumber: 1136
+gidNumber: 1143
+loginShell: /bin/bash
+title: user.132
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.132
+cn: User 132
+displayName: User 132
+givenName: User
+sn: 132
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585c4f6-9eff-1037-8821-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745884Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.133,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.133
+mail: user.133@example.com
+o: example
+initials: u.133
+structuralObjectClass: inetOrgPerson
+uidNumber: 1137
+gidNumber: 1144
+loginShell: /bin/bash
+title: user.133
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.133
+cn: User 133
+displayName: User 133
+givenName: User
+sn: 133
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585c7b2-9eff-1037-8822-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745954Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.134,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.134
+mail: user.134@example.com
+o: example
+initials: u.134
+structuralObjectClass: inetOrgPerson
+uidNumber: 1138
+gidNumber: 1145
+loginShell: /bin/bash
+title: user.134
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.134
+cn: User 134
+displayName: User 134
+givenName: User
+sn: 134
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585ca32-9eff-1037-8823-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746019Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.135,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.135
+mail: user.135@example.com
+o: example
+initials: u.135
+structuralObjectClass: inetOrgPerson
+uidNumber: 1139
+gidNumber: 1146
+loginShell: /bin/bash
+title: user.135
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.135
+cn: User 135
+displayName: User 135
+givenName: User
+sn: 135
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585cd02-9eff-1037-8824-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746090Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.136,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.136
+mail: user.136@example.com
+o: example
+initials: u.136
+structuralObjectClass: inetOrgPerson
+uidNumber: 1140
+gidNumber: 1147
+loginShell: /bin/bash
+title: user.136
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.136
+cn: User 136
+displayName: User 136
+givenName: User
+sn: 136
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585cf78-9eff-1037-8825-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746154Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.137,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.137
+mail: user.137@example.com
+o: example
+initials: u.137
+structuralObjectClass: inetOrgPerson
+uidNumber: 1141
+gidNumber: 1148
+loginShell: /bin/bash
+title: user.137
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.137
+cn: User 137
+displayName: User 137
+givenName: User
+sn: 137
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585d1da-9eff-1037-8826-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746214Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.138,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.138
+mail: user.138@example.com
+o: example
+initials: u.138
+structuralObjectClass: inetOrgPerson
+uidNumber: 1142
+gidNumber: 1149
+loginShell: /bin/bash
+title: user.138
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.138
+cn: User 138
+displayName: User 138
+givenName: User
+sn: 138
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585d4be-9eff-1037-8827-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746288Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.139,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.139
+mail: user.139@example.com
+o: example
+initials: u.139
+structuralObjectClass: inetOrgPerson
+uidNumber: 1143
+gidNumber: 1150
+loginShell: /bin/bash
+title: user.139
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.139
+cn: User 139
+displayName: User 139
+givenName: User
+sn: 139
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585d72a-9eff-1037-8828-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746350Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.140,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.140
+mail: user.140@example.com
+o: example
+initials: u.140
+structuralObjectClass: inetOrgPerson
+uidNumber: 1144
+gidNumber: 1151
+loginShell: /bin/bash
+title: user.140
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.140
+cn: User 140
+displayName: User 140
+givenName: User
+sn: 140
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585d9e6-9eff-1037-8829-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746420Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.141,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.141
+mail: user.141@example.com
+o: example
+initials: u.141
+structuralObjectClass: inetOrgPerson
+uidNumber: 1145
+gidNumber: 1152
+loginShell: /bin/bash
+title: user.141
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.141
+cn: User 141
+displayName: User 141
+givenName: User
+sn: 141
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585dcc0-9eff-1037-882a-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746494Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.142,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.142
+mail: user.142@example.com
+o: example
+initials: u.142
+structuralObjectClass: inetOrgPerson
+uidNumber: 1146
+gidNumber: 1153
+loginShell: /bin/bash
+title: user.142
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.142
+cn: User 142
+displayName: User 142
+givenName: User
+sn: 142
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585df54-9eff-1037-882b-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746560Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.143,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.143
+mail: user.143@example.com
+o: example
+initials: u.143
+structuralObjectClass: inetOrgPerson
+uidNumber: 1147
+gidNumber: 1154
+loginShell: /bin/bash
+title: user.143
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.143
+cn: User 143
+displayName: User 143
+givenName: User
+sn: 143
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585e1c0-9eff-1037-882c-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746622Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.144,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.144
+mail: user.144@example.com
+o: example
+initials: u.144
+structuralObjectClass: inetOrgPerson
+uidNumber: 1148
+gidNumber: 1155
+loginShell: /bin/bash
+title: user.144
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.144
+cn: User 144
+displayName: User 144
+givenName: User
+sn: 144
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585e512-9eff-1037-882d-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746706Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.145,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.145
+mail: user.145@example.com
+o: example
+initials: u.145
+structuralObjectClass: inetOrgPerson
+uidNumber: 1149
+gidNumber: 1156
+loginShell: /bin/bash
+title: user.145
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.145
+cn: User 145
+displayName: User 145
+givenName: User
+sn: 145
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585e792-9eff-1037-882e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746770Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.146,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.146
+mail: user.146@example.com
+o: example
+initials: u.146
+structuralObjectClass: inetOrgPerson
+uidNumber: 1150
+gidNumber: 1157
+loginShell: /bin/bash
+title: user.146
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.146
+cn: User 146
+displayName: User 146
+givenName: User
+sn: 146
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585ea1c-9eff-1037-882f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746835Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.147,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.147
+mail: user.147@example.com
+o: example
+initials: u.147
+structuralObjectClass: inetOrgPerson
+uidNumber: 1151
+gidNumber: 1158
+loginShell: /bin/bash
+title: user.147
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.147
+cn: User 147
+displayName: User 147
+givenName: User
+sn: 147
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585ed32-9eff-1037-8830-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746914Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.148,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.148
+mail: user.148@example.com
+o: example
+initials: u.148
+structuralObjectClass: inetOrgPerson
+uidNumber: 1152
+gidNumber: 1159
+loginShell: /bin/bash
+title: user.148
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.148
+cn: User 148
+displayName: User 148
+givenName: User
+sn: 148
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585efbc-9eff-1037-8831-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746979Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.149,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.149
+mail: user.149@example.com
+o: example
+initials: u.149
+structuralObjectClass: inetOrgPerson
+uidNumber: 1153
+gidNumber: 1160
+loginShell: /bin/bash
+title: user.149
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.149
+cn: User 149
+displayName: User 149
+givenName: User
+sn: 149
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585f21e-9eff-1037-8832-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747041Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.150,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.150
+mail: user.150@example.com
+o: example
+initials: u.150
+structuralObjectClass: inetOrgPerson
+uidNumber: 1154
+gidNumber: 1161
+loginShell: /bin/bash
+title: user.150
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.150
+cn: User 150
+displayName: User 150
+givenName: User
+sn: 150
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585f516-9eff-1037-8833-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747116Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.151,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.151
+mail: user.151@example.com
+o: example
+initials: u.151
+structuralObjectClass: inetOrgPerson
+uidNumber: 1155
+gidNumber: 1162
+loginShell: /bin/bash
+title: user.151
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.151
+cn: User 151
+displayName: User 151
+givenName: User
+sn: 151
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585f782-9eff-1037-8834-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747178Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.152,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.152
+mail: user.152@example.com
+o: example
+initials: u.152
+structuralObjectClass: inetOrgPerson
+uidNumber: 1156
+gidNumber: 1163
+loginShell: /bin/bash
+title: user.152
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.152
+cn: User 152
+displayName: User 152
+givenName: User
+sn: 152
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585fa20-9eff-1037-8835-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747245Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.153,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.153
+mail: user.153@example.com
+o: example
+initials: u.153
+structuralObjectClass: inetOrgPerson
+uidNumber: 1157
+gidNumber: 1164
+loginShell: /bin/bash
+title: user.153
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.153
+cn: User 153
+displayName: User 153
+givenName: User
+sn: 153
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585fcfa-9eff-1037-8836-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747318Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.154,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.154
+mail: user.154@example.com
+o: example
+initials: u.154
+structuralObjectClass: inetOrgPerson
+uidNumber: 1158
+gidNumber: 1165
+loginShell: /bin/bash
+title: user.154
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.154
+cn: User 154
+displayName: User 154
+givenName: User
+sn: 154
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585ff84-9eff-1037-8837-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747383Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.155,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.155
+mail: user.155@example.com
+o: example
+initials: u.155
+structuralObjectClass: inetOrgPerson
+uidNumber: 1159
+gidNumber: 1166
+loginShell: /bin/bash
+title: user.155
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.155
+cn: User 155
+displayName: User 155
+givenName: User
+sn: 155
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586059c-9eff-1037-8838-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747538Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.156,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.156
+mail: user.156@example.com
+o: example
+initials: u.156
+structuralObjectClass: inetOrgPerson
+uidNumber: 1160
+gidNumber: 1167
+loginShell: /bin/bash
+title: user.156
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.156
+cn: User 156
+displayName: User 156
+givenName: User
+sn: 156
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258608da-9eff-1037-8839-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747622Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.157,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.157
+mail: user.157@example.com
+o: example
+initials: u.157
+structuralObjectClass: inetOrgPerson
+uidNumber: 1161
+gidNumber: 1168
+loginShell: /bin/bash
+title: user.157
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.157
+cn: User 157
+displayName: User 157
+givenName: User
+sn: 157
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25860bd2-9eff-1037-883a-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747697Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.158,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.158
+mail: user.158@example.com
+o: example
+initials: u.158
+structuralObjectClass: inetOrgPerson
+uidNumber: 1162
+gidNumber: 1169
+loginShell: /bin/bash
+title: user.158
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.158
+cn: User 158
+displayName: User 158
+givenName: User
+sn: 158
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25860e66-9eff-1037-883b-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747764Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.159,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.159
+mail: user.159@example.com
+o: example
+initials: u.159
+structuralObjectClass: inetOrgPerson
+uidNumber: 1163
+gidNumber: 1170
+loginShell: /bin/bash
+title: user.159
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.159
+cn: User 159
+displayName: User 159
+givenName: User
+sn: 159
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586114a-9eff-1037-883c-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747838Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.160,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.160
+mail: user.160@example.com
+o: example
+initials: u.160
+structuralObjectClass: inetOrgPerson
+uidNumber: 1164
+gidNumber: 1171
+loginShell: /bin/bash
+title: user.160
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.160
+cn: User 160
+displayName: User 160
+givenName: User
+sn: 160
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258613ca-9eff-1037-883d-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747902Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.161,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.161
+mail: user.161@example.com
+o: example
+initials: u.161
+structuralObjectClass: inetOrgPerson
+uidNumber: 1165
+gidNumber: 1172
+loginShell: /bin/bash
+title: user.161
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.161
+cn: User 161
+displayName: User 161
+givenName: User
+sn: 161
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586162c-9eff-1037-883e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747964Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.162,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.162
+mail: user.162@example.com
+o: example
+initials: u.162
+structuralObjectClass: inetOrgPerson
+uidNumber: 1166
+gidNumber: 1173
+loginShell: /bin/bash
+title: user.162
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.162
+cn: User 162
+displayName: User 162
+givenName: User
+sn: 162
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25861960-9eff-1037-883f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748045Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.163,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.163
+mail: user.163@example.com
+o: example
+initials: u.163
+structuralObjectClass: inetOrgPerson
+uidNumber: 1167
+gidNumber: 1174
+loginShell: /bin/bash
+title: user.163
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.163
+cn: User 163
+displayName: User 163
+givenName: User
+sn: 163
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25861bd6-9eff-1037-8840-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748109Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.164,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.164
+mail: user.164@example.com
+o: example
+initials: u.164
+structuralObjectClass: inetOrgPerson
+uidNumber: 1168
+gidNumber: 1175
+loginShell: /bin/bash
+title: user.164
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.164
+cn: User 164
+displayName: User 164
+givenName: User
+sn: 164
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25861ece-9eff-1037-8841-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748184Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.165,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.165
+mail: user.165@example.com
+o: example
+initials: u.165
+structuralObjectClass: inetOrgPerson
+uidNumber: 1169
+gidNumber: 1176
+loginShell: /bin/bash
+title: user.165
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.165
+cn: User 165
+displayName: User 165
+givenName: User
+sn: 165
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586219e-9eff-1037-8842-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748256Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.166,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.166
+mail: user.166@example.com
+o: example
+initials: u.166
+structuralObjectClass: inetOrgPerson
+uidNumber: 1170
+gidNumber: 1177
+loginShell: /bin/bash
+title: user.166
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.166
+cn: User 166
+displayName: User 166
+givenName: User
+sn: 166
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25862428-9eff-1037-8843-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748321Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.167,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.167
+mail: user.167@example.com
+o: example
+initials: u.167
+structuralObjectClass: inetOrgPerson
+uidNumber: 1171
+gidNumber: 1178
+loginShell: /bin/bash
+title: user.167
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.167
+cn: User 167
+displayName: User 167
+givenName: User
+sn: 167
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586268a-9eff-1037-8844-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748383Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.168,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.168
+mail: user.168@example.com
+o: example
+initials: u.168
+structuralObjectClass: inetOrgPerson
+uidNumber: 1172
+gidNumber: 1179
+loginShell: /bin/bash
+title: user.168
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.168
+cn: User 168
+displayName: User 168
+givenName: User
+sn: 168
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586295a-9eff-1037-8845-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748455Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.169,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.169
+mail: user.169@example.com
+o: example
+initials: u.169
+structuralObjectClass: inetOrgPerson
+uidNumber: 1173
+gidNumber: 1180
+loginShell: /bin/bash
+title: user.169
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.169
+cn: User 169
+displayName: User 169
+givenName: User
+sn: 169
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25862c16-9eff-1037-8846-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748524Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.170,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.170
+mail: user.170@example.com
+o: example
+initials: u.170
+structuralObjectClass: inetOrgPerson
+uidNumber: 1174
+gidNumber: 1181
+loginShell: /bin/bash
+title: user.170
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.170
+cn: User 170
+displayName: User 170
+givenName: User
+sn: 170
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25862f5e-9eff-1037-8847-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748608Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.171,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.171
+mail: user.171@example.com
+o: example
+initials: u.171
+structuralObjectClass: inetOrgPerson
+uidNumber: 1175
+gidNumber: 1182
+loginShell: /bin/bash
+title: user.171
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.171
+cn: User 171
+displayName: User 171
+givenName: User
+sn: 171
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25863288-9eff-1037-8848-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748689Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.172,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.172
+mail: user.172@example.com
+o: example
+initials: u.172
+structuralObjectClass: inetOrgPerson
+uidNumber: 1176
+gidNumber: 1183
+loginShell: /bin/bash
+title: user.172
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.172
+cn: User 172
+displayName: User 172
+givenName: User
+sn: 172
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25863580-9eff-1037-8849-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748765Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.173,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.173
+mail: user.173@example.com
+o: example
+initials: u.173
+structuralObjectClass: inetOrgPerson
+uidNumber: 1177
+gidNumber: 1184
+loginShell: /bin/bash
+title: user.173
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.173
+cn: User 173
+displayName: User 173
+givenName: User
+sn: 173
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258637d8-9eff-1037-884a-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748826Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.174,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.174
+mail: user.174@example.com
+o: example
+initials: u.174
+structuralObjectClass: inetOrgPerson
+uidNumber: 1178
+gidNumber: 1185
+loginShell: /bin/bash
+title: user.174
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.174
+cn: User 174
+displayName: User 174
+givenName: User
+sn: 174
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25863ad0-9eff-1037-884b-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748901Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.175,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.175
+mail: user.175@example.com
+o: example
+initials: u.175
+structuralObjectClass: inetOrgPerson
+uidNumber: 1179
+gidNumber: 1186
+loginShell: /bin/bash
+title: user.175
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.175
+cn: User 175
+displayName: User 175
+givenName: User
+sn: 175
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25863d3c-9eff-1037-884c-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748963Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.176,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.176
+mail: user.176@example.com
+o: example
+initials: u.176
+structuralObjectClass: inetOrgPerson
+uidNumber: 1180
+gidNumber: 1187
+loginShell: /bin/bash
+title: user.176
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.176
+cn: User 176
+displayName: User 176
+givenName: User
+sn: 176
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25863fa8-9eff-1037-884d-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749025Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.177,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.177
+mail: user.177@example.com
+o: example
+initials: u.177
+structuralObjectClass: inetOrgPerson
+uidNumber: 1181
+gidNumber: 1188
+loginShell: /bin/bash
+title: user.177
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.177
+cn: User 177
+displayName: User 177
+givenName: User
+sn: 177
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258642a0-9eff-1037-884e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749101Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.178,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.178
+mail: user.178@example.com
+o: example
+initials: u.178
+structuralObjectClass: inetOrgPerson
+uidNumber: 1182
+gidNumber: 1189
+loginShell: /bin/bash
+title: user.178
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.178
+cn: User 178
+displayName: User 178
+givenName: User
+sn: 178
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586452a-9eff-1037-884f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749166Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.179,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.179
+mail: user.179@example.com
+o: example
+initials: u.179
+structuralObjectClass: inetOrgPerson
+uidNumber: 1183
+gidNumber: 1190
+loginShell: /bin/bash
+title: user.179
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.179
+cn: User 179
+displayName: User 179
+givenName: User
+sn: 179
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25864782-9eff-1037-8850-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749227Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.180,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.180
+mail: user.180@example.com
+o: example
+initials: u.180
+structuralObjectClass: inetOrgPerson
+uidNumber: 1184
+gidNumber: 1191
+loginShell: /bin/bash
+title: user.180
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.180
+cn: User 180
+displayName: User 180
+givenName: User
+sn: 180
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25864a52-9eff-1037-8851-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749298Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.181,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.181
+mail: user.181@example.com
+o: example
+initials: u.181
+structuralObjectClass: inetOrgPerson
+uidNumber: 1185
+gidNumber: 1192
+loginShell: /bin/bash
+title: user.181
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.181
+cn: User 181
+displayName: User 181
+givenName: User
+sn: 181
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25864cbe-9eff-1037-8852-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749360Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.182,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.182
+mail: user.182@example.com
+o: example
+initials: u.182
+structuralObjectClass: inetOrgPerson
+uidNumber: 1186
+gidNumber: 1193
+loginShell: /bin/bash
+title: user.182
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.182
+cn: User 182
+displayName: User 182
+givenName: User
+sn: 182
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25864f3e-9eff-1037-8853-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749424Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.183,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.183
+mail: user.183@example.com
+o: example
+initials: u.183
+structuralObjectClass: inetOrgPerson
+uidNumber: 1187
+gidNumber: 1194
+loginShell: /bin/bash
+title: user.183
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.183
+cn: User 183
+displayName: User 183
+givenName: User
+sn: 183
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25865204-9eff-1037-8854-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749495Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.184,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.184
+mail: user.184@example.com
+o: example
+initials: u.184
+structuralObjectClass: inetOrgPerson
+uidNumber: 1188
+gidNumber: 1195
+loginShell: /bin/bash
+title: user.184
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.184
+cn: User 184
+displayName: User 184
+givenName: User
+sn: 184
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258654c0-9eff-1037-8855-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749566Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.185,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.185
+mail: user.185@example.com
+o: example
+initials: u.185
+structuralObjectClass: inetOrgPerson
+uidNumber: 1189
+gidNumber: 1196
+loginShell: /bin/bash
+title: user.185
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.185
+cn: User 185
+displayName: User 185
+givenName: User
+sn: 185
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586572c-9eff-1037-8856-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749628Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.186,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.186
+mail: user.186@example.com
+o: example
+initials: u.186
+structuralObjectClass: inetOrgPerson
+uidNumber: 1190
+gidNumber: 1197
+loginShell: /bin/bash
+title: user.186
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.186
+cn: User 186
+displayName: User 186
+givenName: User
+sn: 186
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25865a88-9eff-1037-8857-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749713Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.187,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.187
+mail: user.187@example.com
+o: example
+initials: u.187
+structuralObjectClass: inetOrgPerson
+uidNumber: 1191
+gidNumber: 1198
+loginShell: /bin/bash
+title: user.187
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.187
+cn: User 187
+displayName: User 187
+givenName: User
+sn: 187
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25865df8-9eff-1037-8858-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749801Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.188,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.188
+mail: user.188@example.com
+o: example
+initials: u.188
+structuralObjectClass: inetOrgPerson
+uidNumber: 1192
+gidNumber: 1199
+loginShell: /bin/bash
+title: user.188
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.188
+cn: User 188
+displayName: User 188
+givenName: User
+sn: 188
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586606e-9eff-1037-8859-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749865Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.189,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.189
+mail: user.189@example.com
+o: example
+initials: u.189
+structuralObjectClass: inetOrgPerson
+uidNumber: 1193
+gidNumber: 1200
+loginShell: /bin/bash
+title: user.189
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.189
+cn: User 189
+displayName: User 189
+givenName: User
+sn: 189
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25866334-9eff-1037-885a-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749935Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.190,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.190
+mail: user.190@example.com
+o: example
+initials: u.190
+structuralObjectClass: inetOrgPerson
+uidNumber: 1194
+gidNumber: 1201
+loginShell: /bin/bash
+title: user.190
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.190
+cn: User 190
+displayName: User 190
+givenName: User
+sn: 190
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258665b4-9eff-1037-885b-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750000Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.191,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.191
+mail: user.191@example.com
+o: example
+initials: u.191
+structuralObjectClass: inetOrgPerson
+uidNumber: 1195
+gidNumber: 1202
+loginShell: /bin/bash
+title: user.191
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.191
+cn: User 191
+displayName: User 191
+givenName: User
+sn: 191
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25866816-9eff-1037-885c-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750061Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.192,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.192
+mail: user.192@example.com
+o: example
+initials: u.192
+structuralObjectClass: inetOrgPerson
+uidNumber: 1196
+gidNumber: 1203
+loginShell: /bin/bash
+title: user.192
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.192
+cn: User 192
+displayName: User 192
+givenName: User
+sn: 192
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25866b22-9eff-1037-885d-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750138Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.193,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.193
+mail: user.193@example.com
+o: example
+initials: u.193
+structuralObjectClass: inetOrgPerson
+uidNumber: 1197
+gidNumber: 1204
+loginShell: /bin/bash
+title: user.193
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.193
+cn: User 193
+displayName: User 193
+givenName: User
+sn: 193
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25866d8e-9eff-1037-885e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750201Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.194,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.194
+mail: user.194@example.com
+o: example
+initials: u.194
+structuralObjectClass: inetOrgPerson
+uidNumber: 1198
+gidNumber: 1205
+loginShell: /bin/bash
+title: user.194
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.194
+cn: User 194
+displayName: User 194
+givenName: User
+sn: 194
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586700e-9eff-1037-885f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750264Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.195,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.195
+mail: user.195@example.com
+o: example
+initials: u.195
+structuralObjectClass: inetOrgPerson
+uidNumber: 1199
+gidNumber: 1206
+loginShell: /bin/bash
+title: user.195
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.195
+cn: User 195
+displayName: User 195
+givenName: User
+sn: 195
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258672d4-9eff-1037-8860-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750335Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.196,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.196
+mail: user.196@example.com
+o: example
+initials: u.196
+structuralObjectClass: inetOrgPerson
+uidNumber: 1200
+gidNumber: 1207
+loginShell: /bin/bash
+title: user.196
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.196
+cn: User 196
+displayName: User 196
+givenName: User
+sn: 196
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586754a-9eff-1037-8861-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750398Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.197,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.197
+mail: user.197@example.com
+o: example
+initials: u.197
+structuralObjectClass: inetOrgPerson
+uidNumber: 1201
+gidNumber: 1208
+loginShell: /bin/bash
+title: user.197
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.197
+cn: User 197
+displayName: User 197
+givenName: User
+sn: 197
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258677a2-9eff-1037-8862-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750459Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.198,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.198
+mail: user.198@example.com
+o: example
+initials: u.198
+structuralObjectClass: inetOrgPerson
+uidNumber: 1202
+gidNumber: 1209
+loginShell: /bin/bash
+title: user.198
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.198
+cn: User 198
+displayName: User 198
+givenName: User
+sn: 198
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25867a86-9eff-1037-8863-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750532Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.199,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.199
+mail: user.199@example.com
+o: example
+initials: u.199
+structuralObjectClass: inetOrgPerson
+uidNumber: 1203
+gidNumber: 1210
+loginShell: /bin/bash
+title: user.199
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.199
+cn: User 199
+displayName: User 199
+givenName: User
+sn: 199
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25867d2e-9eff-1037-8864-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750601Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.200,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.200
+mail: user.200@example.com
+o: example
+initials: u.200
+structuralObjectClass: inetOrgPerson
+uidNumber: 1204
+gidNumber: 1211
+loginShell: /bin/bash
+title: user.200
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.200
+cn: User 200
+displayName: User 200
+givenName: User
+sn: 200
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25868008-9eff-1037-8865-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750673Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.201,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.201
+mail: user.201@example.com
+o: example
+initials: u.201
+structuralObjectClass: inetOrgPerson
+uidNumber: 1205
+gidNumber: 1212
+loginShell: /bin/bash
+title: user.201
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.201
+cn: User 201
+displayName: User 201
+givenName: User
+sn: 201
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258682f6-9eff-1037-8866-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750748Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.202,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.202
+mail: user.202@example.com
+o: example
+initials: u.202
+structuralObjectClass: inetOrgPerson
+uidNumber: 1206
+gidNumber: 1213
+loginShell: /bin/bash
+title: user.202
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.202
+cn: User 202
+displayName: User 202
+givenName: User
+sn: 202
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25868580-9eff-1037-8867-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750813Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.203,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.203
+mail: user.203@example.com
+o: example
+initials: u.203
+structuralObjectClass: inetOrgPerson
+uidNumber: 1207
+gidNumber: 1214
+loginShell: /bin/bash
+title: user.203
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.203
+cn: User 203
+displayName: User 203
+givenName: User
+sn: 203
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258687e2-9eff-1037-8868-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750874Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.204,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.204
+mail: user.204@example.com
+o: example
+initials: u.204
+structuralObjectClass: inetOrgPerson
+uidNumber: 1208
+gidNumber: 1215
+loginShell: /bin/bash
+title: user.204
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.204
+cn: User 204
+displayName: User 204
+givenName: User
+sn: 204
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25868ab2-9eff-1037-8869-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750946Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.205,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.205
+mail: user.205@example.com
+o: example
+initials: u.205
+structuralObjectClass: inetOrgPerson
+uidNumber: 1209
+gidNumber: 1216
+loginShell: /bin/bash
+title: user.205
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.205
+cn: User 205
+displayName: User 205
+givenName: User
+sn: 205
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25868d14-9eff-1037-886a-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751008Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.206,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.206
+mail: user.206@example.com
+o: example
+initials: u.206
+structuralObjectClass: inetOrgPerson
+uidNumber: 1210
+gidNumber: 1217
+loginShell: /bin/bash
+title: user.206
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.206
+cn: User 206
+displayName: User 206
+givenName: User
+sn: 206
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25868fda-9eff-1037-886b-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751079Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.207,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.207
+mail: user.207@example.com
+o: example
+initials: u.207
+structuralObjectClass: inetOrgPerson
+uidNumber: 1211
+gidNumber: 1218
+loginShell: /bin/bash
+title: user.207
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.207
+cn: User 207
+displayName: User 207
+givenName: User
+sn: 207
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258692aa-9eff-1037-886c-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751150Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.208,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.208
+mail: user.208@example.com
+o: example
+initials: u.208
+structuralObjectClass: inetOrgPerson
+uidNumber: 1212
+gidNumber: 1219
+loginShell: /bin/bash
+title: user.208
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.208
+cn: User 208
+displayName: User 208
+givenName: User
+sn: 208
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25869534-9eff-1037-886d-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751215Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.209,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.209
+mail: user.209@example.com
+o: example
+initials: u.209
+structuralObjectClass: inetOrgPerson
+uidNumber: 1213
+gidNumber: 1220
+loginShell: /bin/bash
+title: user.209
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.209
+cn: User 209
+displayName: User 209
+givenName: User
+sn: 209
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258697aa-9eff-1037-886e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751278Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.210,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.210
+mail: user.210@example.com
+o: example
+initials: u.210
+structuralObjectClass: inetOrgPerson
+uidNumber: 1214
+gidNumber: 1221
+loginShell: /bin/bash
+title: user.210
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.210
+cn: User 210
+displayName: User 210
+givenName: User
+sn: 210
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25869aa2-9eff-1037-886f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751354Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.211,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.211
+mail: user.211@example.com
+o: example
+initials: u.211
+structuralObjectClass: inetOrgPerson
+uidNumber: 1215
+gidNumber: 1222
+loginShell: /bin/bash
+title: user.211
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.211
+cn: User 211
+displayName: User 211
+givenName: User
+sn: 211
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25869ee4-9eff-1037-8870-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751462Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.212,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.212
+mail: user.212@example.com
+o: example
+initials: u.212
+structuralObjectClass: inetOrgPerson
+uidNumber: 1216
+gidNumber: 1223
+loginShell: /bin/bash
+title: user.212
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.212
+cn: User 212
+displayName: User 212
+givenName: User
+sn: 212
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586a182-9eff-1037-8871-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751531Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.213,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.213
+mail: user.213@example.com
+o: example
+initials: u.213
+structuralObjectClass: inetOrgPerson
+uidNumber: 1217
+gidNumber: 1224
+loginShell: /bin/bash
+title: user.213
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.213
+cn: User 213
+displayName: User 213
+givenName: User
+sn: 213
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586a470-9eff-1037-8872-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751605Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.214,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.214
+mail: user.214@example.com
+o: example
+initials: u.214
+structuralObjectClass: inetOrgPerson
+uidNumber: 1218
+gidNumber: 1225
+loginShell: /bin/bash
+title: user.214
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.214
+cn: User 214
+displayName: User 214
+givenName: User
+sn: 214
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586a7c2-9eff-1037-8873-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751690Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.215,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.215
+mail: user.215@example.com
+o: example
+initials: u.215
+structuralObjectClass: inetOrgPerson
+uidNumber: 1219
+gidNumber: 1226
+loginShell: /bin/bash
+title: user.215
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.215
+cn: User 215
+displayName: User 215
+givenName: User
+sn: 215
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586aa42-9eff-1037-8874-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751754Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.216,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.216
+mail: user.216@example.com
+o: example
+initials: u.216
+structuralObjectClass: inetOrgPerson
+uidNumber: 1220
+gidNumber: 1227
+loginShell: /bin/bash
+title: user.216
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.216
+cn: User 216
+displayName: User 216
+givenName: User
+sn: 216
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586ad26-9eff-1037-8875-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751828Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.217,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.217
+mail: user.217@example.com
+o: example
+initials: u.217
+structuralObjectClass: inetOrgPerson
+uidNumber: 1221
+gidNumber: 1228
+loginShell: /bin/bash
+title: user.217
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.217
+cn: User 217
+displayName: User 217
+givenName: User
+sn: 217
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586af92-9eff-1037-8876-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751891Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.218,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.218
+mail: user.218@example.com
+o: example
+initials: u.218
+structuralObjectClass: inetOrgPerson
+uidNumber: 1222
+gidNumber: 1229
+loginShell: /bin/bash
+title: user.218
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.218
+cn: User 218
+displayName: User 218
+givenName: User
+sn: 218
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586b21c-9eff-1037-8877-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751955Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.219,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.219
+mail: user.219@example.com
+o: example
+initials: u.219
+structuralObjectClass: inetOrgPerson
+uidNumber: 1223
+gidNumber: 1230
+loginShell: /bin/bash
+title: user.219
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.219
+cn: User 219
+displayName: User 219
+givenName: User
+sn: 219
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586b4e2-9eff-1037-8878-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752026Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.220,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.220
+mail: user.220@example.com
+o: example
+initials: u.220
+structuralObjectClass: inetOrgPerson
+uidNumber: 1224
+gidNumber: 1231
+loginShell: /bin/bash
+title: user.220
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.220
+cn: User 220
+displayName: User 220
+givenName: User
+sn: 220
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586b758-9eff-1037-8879-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752089Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.221,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.221
+mail: user.221@example.com
+o: example
+initials: u.221
+structuralObjectClass: inetOrgPerson
+uidNumber: 1225
+gidNumber: 1232
+loginShell: /bin/bash
+title: user.221
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.221
+cn: User 221
+displayName: User 221
+givenName: User
+sn: 221
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586ba00-9eff-1037-887a-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752158Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.222,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.222
+mail: user.222@example.com
+o: example
+initials: u.222
+structuralObjectClass: inetOrgPerson
+uidNumber: 1226
+gidNumber: 1233
+loginShell: /bin/bash
+title: user.222
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.222
+cn: User 222
+displayName: User 222
+givenName: User
+sn: 222
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586bcf8-9eff-1037-887b-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752233Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.223,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.223
+mail: user.223@example.com
+o: example
+initials: u.223
+structuralObjectClass: inetOrgPerson
+uidNumber: 1227
+gidNumber: 1234
+loginShell: /bin/bash
+title: user.223
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.223
+cn: User 223
+displayName: User 223
+givenName: User
+sn: 223
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586bf64-9eff-1037-887c-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752295Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.224,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.224
+mail: user.224@example.com
+o: example
+initials: u.224
+structuralObjectClass: inetOrgPerson
+uidNumber: 1228
+gidNumber: 1235
+loginShell: /bin/bash
+title: user.224
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.224
+cn: User 224
+displayName: User 224
+givenName: User
+sn: 224
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586c1da-9eff-1037-887d-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752358Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.225,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.225
+mail: user.225@example.com
+o: example
+initials: u.225
+structuralObjectClass: inetOrgPerson
+uidNumber: 1229
+gidNumber: 1236
+loginShell: /bin/bash
+title: user.225
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.225
+cn: User 225
+displayName: User 225
+givenName: User
+sn: 225
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586c4a0-9eff-1037-887e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752429Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.226,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.226
+mail: user.226@example.com
+o: example
+initials: u.226
+structuralObjectClass: inetOrgPerson
+uidNumber: 1230
+gidNumber: 1237
+loginShell: /bin/bash
+title: user.226
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.226
+cn: User 226
+displayName: User 226
+givenName: User
+sn: 226
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586c72a-9eff-1037-887f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752494Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.227,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.227
+mail: user.227@example.com
+o: example
+initials: u.227
+structuralObjectClass: inetOrgPerson
+uidNumber: 1231
+gidNumber: 1238
+loginShell: /bin/bash
+title: user.227
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.227
+cn: User 227
+displayName: User 227
+givenName: User
+sn: 227
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586c98c-9eff-1037-8880-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752555Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.228,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.228
+mail: user.228@example.com
+o: example
+initials: u.228
+structuralObjectClass: inetOrgPerson
+uidNumber: 1232
+gidNumber: 1239
+loginShell: /bin/bash
+title: user.228
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.228
+cn: User 228
+displayName: User 228
+givenName: User
+sn: 228
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586cc98-9eff-1037-8881-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752633Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.229,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.229
+mail: user.229@example.com
+o: example
+initials: u.229
+structuralObjectClass: inetOrgPerson
+uidNumber: 1233
+gidNumber: 1240
+loginShell: /bin/bash
+title: user.229
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.229
+cn: User 229
+displayName: User 229
+givenName: User
+sn: 229
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586d01c-9eff-1037-8882-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752723Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.230,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.230
+mail: user.230@example.com
+o: example
+initials: u.230
+structuralObjectClass: inetOrgPerson
+uidNumber: 1234
+gidNumber: 1241
+loginShell: /bin/bash
+title: user.230
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.230
+cn: User 230
+displayName: User 230
+givenName: User
+sn: 230
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586d2b0-9eff-1037-8883-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752789Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.231,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.231
+mail: user.231@example.com
+o: example
+initials: u.231
+structuralObjectClass: inetOrgPerson
+uidNumber: 1235
+gidNumber: 1242
+loginShell: /bin/bash
+title: user.231
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.231
+cn: User 231
+displayName: User 231
+givenName: User
+sn: 231
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586d576-9eff-1037-8884-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752860Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.232,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.232
+mail: user.232@example.com
+o: example
+initials: u.232
+structuralObjectClass: inetOrgPerson
+uidNumber: 1236
+gidNumber: 1243
+loginShell: /bin/bash
+title: user.232
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.232
+cn: User 232
+displayName: User 232
+givenName: User
+sn: 232
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586d7ec-9eff-1037-8885-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752924Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.233,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.233
+mail: user.233@example.com
+o: example
+initials: u.233
+structuralObjectClass: inetOrgPerson
+uidNumber: 1237
+gidNumber: 1244
+loginShell: /bin/bash
+title: user.233
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.233
+cn: User 233
+displayName: User 233
+givenName: User
+sn: 233
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586da58-9eff-1037-8886-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752985Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.234,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.234
+mail: user.234@example.com
+o: example
+initials: u.234
+structuralObjectClass: inetOrgPerson
+uidNumber: 1238
+gidNumber: 1245
+loginShell: /bin/bash
+title: user.234
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.234
+cn: User 234
+displayName: User 234
+givenName: User
+sn: 234
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586dd6e-9eff-1037-8887-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753064Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.235,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.235
+mail: user.235@example.com
+o: example
+initials: u.235
+structuralObjectClass: inetOrgPerson
+uidNumber: 1239
+gidNumber: 1246
+loginShell: /bin/bash
+title: user.235
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.235
+cn: User 235
+displayName: User 235
+givenName: User
+sn: 235
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586dfd0-9eff-1037-8888-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753126Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.236,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.236
+mail: user.236@example.com
+o: example
+initials: u.236
+structuralObjectClass: inetOrgPerson
+uidNumber: 1240
+gidNumber: 1247
+loginShell: /bin/bash
+title: user.236
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.236
+cn: User 236
+displayName: User 236
+givenName: User
+sn: 236
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586e282-9eff-1037-8889-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753194Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.237,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.237
+mail: user.237@example.com
+o: example
+initials: u.237
+structuralObjectClass: inetOrgPerson
+uidNumber: 1241
+gidNumber: 1248
+loginShell: /bin/bash
+title: user.237
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.237
+cn: User 237
+displayName: User 237
+givenName: User
+sn: 237
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586e548-9eff-1037-888a-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753265Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.238,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.238
+mail: user.238@example.com
+o: example
+initials: u.238
+structuralObjectClass: inetOrgPerson
+uidNumber: 1242
+gidNumber: 1249
+loginShell: /bin/bash
+title: user.238
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.238
+cn: User 238
+displayName: User 238
+givenName: User
+sn: 238
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586e7d2-9eff-1037-888b-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753330Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.239,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.239
+mail: user.239@example.com
+o: example
+initials: u.239
+structuralObjectClass: inetOrgPerson
+uidNumber: 1243
+gidNumber: 1250
+loginShell: /bin/bash
+title: user.239
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.239
+cn: User 239
+displayName: User 239
+givenName: User
+sn: 239
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586ea34-9eff-1037-888c-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753392Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.240,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.240
+mail: user.240@example.com
+o: example
+initials: u.240
+structuralObjectClass: inetOrgPerson
+uidNumber: 1244
+gidNumber: 1251
+loginShell: /bin/bash
+title: user.240
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.240
+cn: User 240
+displayName: User 240
+givenName: User
+sn: 240
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586ed0e-9eff-1037-888d-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753464Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.241,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.241
+mail: user.241@example.com
+o: example
+initials: u.241
+structuralObjectClass: inetOrgPerson
+uidNumber: 1245
+gidNumber: 1252
+loginShell: /bin/bash
+title: user.241
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.241
+cn: User 241
+displayName: User 241
+givenName: User
+sn: 241
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586ef7a-9eff-1037-888e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753527Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.242,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.242
+mail: user.242@example.com
+o: example
+initials: u.242
+structuralObjectClass: inetOrgPerson
+uidNumber: 1246
+gidNumber: 1253
+loginShell: /bin/bash
+title: user.242
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.242
+cn: User 242
+displayName: User 242
+givenName: User
+sn: 242
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586f1fa-9eff-1037-888f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753590Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.243,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.243
+mail: user.243@example.com
+o: example
+initials: u.243
+structuralObjectClass: inetOrgPerson
+uidNumber: 1247
+gidNumber: 1254
+loginShell: /bin/bash
+title: user.243
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.243
+cn: User 243
+displayName: User 243
+givenName: User
+sn: 243
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586f560-9eff-1037-8890-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753676Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.244,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.244
+mail: user.244@example.com
+o: example
+initials: u.244
+structuralObjectClass: inetOrgPerson
+uidNumber: 1248
+gidNumber: 1255
+loginShell: /bin/bash
+title: user.244
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.244
+cn: User 244
+displayName: User 244
+givenName: User
+sn: 244
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586f7f4-9eff-1037-8891-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753743Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.245,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.245
+mail: user.245@example.com
+o: example
+initials: u.245
+structuralObjectClass: inetOrgPerson
+uidNumber: 1249
+gidNumber: 1256
+loginShell: /bin/bash
+title: user.245
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.245
+cn: User 245
+displayName: User 245
+givenName: User
+sn: 245
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586fa4c-9eff-1037-8892-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753804Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.246,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.246
+mail: user.246@example.com
+o: example
+initials: u.246
+structuralObjectClass: inetOrgPerson
+uidNumber: 1250
+gidNumber: 1257
+loginShell: /bin/bash
+title: user.246
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.246
+cn: User 246
+displayName: User 246
+givenName: User
+sn: 246
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586fd3a-9eff-1037-8893-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753878Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.247,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.247
+mail: user.247@example.com
+o: example
+initials: u.247
+structuralObjectClass: inetOrgPerson
+uidNumber: 1251
+gidNumber: 1258
+loginShell: /bin/bash
+title: user.247
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.247
+cn: User 247
+displayName: User 247
+givenName: User
+sn: 247
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2587001e-9eff-1037-8894-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753952Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.248,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.248
+mail: user.248@example.com
+o: example
+initials: u.248
+structuralObjectClass: inetOrgPerson
+uidNumber: 1252
+gidNumber: 1259
+loginShell: /bin/bash
+title: user.248
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.248
+cn: User 248
+displayName: User 248
+givenName: User
+sn: 248
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258702ee-9eff-1037-8895-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754025Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.249,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.249
+mail: user.249@example.com
+o: example
+initials: u.249
+structuralObjectClass: inetOrgPerson
+uidNumber: 1253
+gidNumber: 1260
+loginShell: /bin/bash
+title: user.249
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.249
+cn: User 249
+displayName: User 249
+givenName: User
+sn: 249
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258705b4-9eff-1037-8896-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754095Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.250,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.250
+mail: user.250@example.com
+o: example
+initials: u.250
+structuralObjectClass: inetOrgPerson
+uidNumber: 1254
+gidNumber: 1261
+loginShell: /bin/bash
+title: user.250
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.250
+cn: User 250
+displayName: User 250
+givenName: User
+sn: 250
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25870956-9eff-1037-8897-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754188Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.251,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.251
+mail: user.251@example.com
+o: example
+initials: u.251
+structuralObjectClass: inetOrgPerson
+uidNumber: 1255
+gidNumber: 1262
+loginShell: /bin/bash
+title: user.251
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.251
+cn: User 251
+displayName: User 251
+givenName: User
+sn: 251
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25870bcc-9eff-1037-8898-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754252Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.252,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.252
+mail: user.252@example.com
+o: example
+initials: u.252
+structuralObjectClass: inetOrgPerson
+uidNumber: 1256
+gidNumber: 1263
+loginShell: /bin/bash
+title: user.252
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.252
+cn: User 252
+displayName: User 252
+givenName: User
+sn: 252
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25870e9c-9eff-1037-8899-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754323Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.253,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.253
+mail: user.253@example.com
+o: example
+initials: u.253
+structuralObjectClass: inetOrgPerson
+uidNumber: 1257
+gidNumber: 1264
+loginShell: /bin/bash
+title: user.253
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.253
+cn: User 253
+displayName: User 253
+givenName: User
+sn: 253
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25871108-9eff-1037-889a-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754385Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.254,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.254
+mail: user.254@example.com
+o: example
+initials: u.254
+structuralObjectClass: inetOrgPerson
+uidNumber: 1258
+gidNumber: 1265
+loginShell: /bin/bash
+title: user.254
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.254
+cn: User 254
+displayName: User 254
+givenName: User
+sn: 254
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2587136a-9eff-1037-889b-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754446Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.255,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.255
+mail: user.255@example.com
+o: example
+initials: u.255
+structuralObjectClass: inetOrgPerson
+uidNumber: 1259
+gidNumber: 1266
+loginShell: /bin/bash
+title: user.255
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.255
+cn: User 255
+displayName: User 255
+givenName: User
+sn: 255
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25871630-9eff-1037-889c-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754517Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.256,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.256
+mail: user.256@example.com
+o: example
+initials: u.256
+structuralObjectClass: inetOrgPerson
+uidNumber: 1260
+gidNumber: 1267
+loginShell: /bin/bash
+title: user.256
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.256
+cn: User 256
+displayName: User 256
+givenName: User
+sn: 256
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2587189c-9eff-1037-889d-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754579Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.257,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.257
+mail: user.257@example.com
+o: example
+initials: u.257
+structuralObjectClass: inetOrgPerson
+uidNumber: 1261
+gidNumber: 1268
+loginShell: /bin/bash
+title: user.257
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.257
+cn: User 257
+displayName: User 257
+givenName: User
+sn: 257
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25871afe-9eff-1037-889e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754640Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.258,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.258
+mail: user.258@example.com
+o: example
+initials: u.258
+structuralObjectClass: inetOrgPerson
+uidNumber: 1262
+gidNumber: 1269
+loginShell: /bin/bash
+title: user.258
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.258
+cn: User 258
+displayName: User 258
+givenName: User
+sn: 258
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25871e78-9eff-1037-889f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754729Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.259,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.259
+mail: user.259@example.com
+o: example
+initials: u.259
+structuralObjectClass: inetOrgPerson
+uidNumber: 1263
+gidNumber: 1270
+loginShell: /bin/bash
+title: user.259
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.259
+cn: User 259
+displayName: User 259
+givenName: User
+sn: 259
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258720f8-9eff-1037-88a0-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754793Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.260,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.260
+mail: user.260@example.com
+o: example
+initials: u.260
+structuralObjectClass: inetOrgPerson
+uidNumber: 1264
+gidNumber: 1271
+loginShell: /bin/bash
+title: user.260
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.260
+cn: User 260
+displayName: User 260
+givenName: User
+sn: 260
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2587235a-9eff-1037-88a1-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754854Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.261,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.261
+mail: user.261@example.com
+o: example
+initials: u.261
+structuralObjectClass: inetOrgPerson
+uidNumber: 1265
+gidNumber: 1272
+loginShell: /bin/bash
+title: user.261
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.261
+cn: User 261
+displayName: User 261
+givenName: User
+sn: 261
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25872620-9eff-1037-88a2-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754925Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.262,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.262
+mail: user.262@example.com
+o: example
+initials: u.262
+structuralObjectClass: inetOrgPerson
+uidNumber: 1266
+gidNumber: 1273
+loginShell: /bin/bash
+title: user.262
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.262
+cn: User 262
+displayName: User 262
+givenName: User
+sn: 262
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2587288c-9eff-1037-88a3-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754988Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.263,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.263
+mail: user.263@example.com
+o: example
+initials: u.263
+structuralObjectClass: inetOrgPerson
+uidNumber: 1267
+gidNumber: 1274
+loginShell: /bin/bash
+title: user.263
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.263
+cn: User 263
+displayName: User 263
+givenName: User
+sn: 263
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25872aee-9eff-1037-88a4-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755049Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.264,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.264
+mail: user.264@example.com
+o: example
+initials: u.264
+structuralObjectClass: inetOrgPerson
+uidNumber: 1268
+gidNumber: 1275
+loginShell: /bin/bash
+title: user.264
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.264
+cn: User 264
+displayName: User 264
+givenName: User
+sn: 264
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25872db4-9eff-1037-88a5-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755119Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.265,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.265
+mail: user.265@example.com
+o: example
+initials: u.265
+structuralObjectClass: inetOrgPerson
+uidNumber: 1269
+gidNumber: 1276
+loginShell: /bin/bash
+title: user.265
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.265
+cn: User 265
+displayName: User 265
+givenName: User
+sn: 265
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25873070-9eff-1037-88a6-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755189Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.266,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.266
+mail: user.266@example.com
+o: example
+initials: u.266
+structuralObjectClass: inetOrgPerson
+uidNumber: 1270
+gidNumber: 1277
+loginShell: /bin/bash
+title: user.266
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.266
+cn: User 266
+displayName: User 266
+givenName: User
+sn: 266
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258732fa-9eff-1037-88a7-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755254Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.267,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.267
+mail: user.267@example.com
+o: example
+initials: u.267
+structuralObjectClass: inetOrgPerson
+uidNumber: 1271
+gidNumber: 1278
+loginShell: /bin/bash
+title: user.267
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.267
+cn: User 267
+displayName: User 267
+givenName: User
+sn: 267
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258735ca-9eff-1037-88a8-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755326Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.268,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.268
+mail: user.268@example.com
+o: example
+initials: u.268
+structuralObjectClass: inetOrgPerson
+uidNumber: 1272
+gidNumber: 1279
+loginShell: /bin/bash
+title: user.268
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.268
+cn: User 268
+displayName: User 268
+givenName: User
+sn: 268
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25873836-9eff-1037-88a9-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755388Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.269,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.269
+mail: user.269@example.com
+o: example
+initials: u.269
+structuralObjectClass: inetOrgPerson
+uidNumber: 1273
+gidNumber: 1280
+loginShell: /bin/bash
+title: user.269
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.269
+cn: User 269
+displayName: User 269
+givenName: User
+sn: 269
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25873a8e-9eff-1037-88aa-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755449Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.270,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.270
+mail: user.270@example.com
+o: example
+initials: u.270
+structuralObjectClass: inetOrgPerson
+uidNumber: 1274
+gidNumber: 1281
+loginShell: /bin/bash
+title: user.270
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.270
+cn: User 270
+displayName: User 270
+givenName: User
+sn: 270
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25873d54-9eff-1037-88ab-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755519Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.271,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.271
+mail: user.271@example.com
+o: example
+initials: u.271
+structuralObjectClass: inetOrgPerson
+uidNumber: 1275
+gidNumber: 1282
+loginShell: /bin/bash
+title: user.271
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.271
+cn: User 271
+displayName: User 271
+givenName: User
+sn: 271
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25873fc0-9eff-1037-88ac-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755582Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.272,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.272
+mail: user.272@example.com
+o: example
+initials: u.272
+structuralObjectClass: inetOrgPerson
+uidNumber: 1276
+gidNumber: 1283
+loginShell: /bin/bash
+title: user.272
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.272
+cn: User 272
+displayName: User 272
+givenName: User
+sn: 272
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25874434-9eff-1037-88ad-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755694Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.273,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.273
+mail: user.273@example.com
+o: example
+initials: u.273
+structuralObjectClass: inetOrgPerson
+uidNumber: 1277
+gidNumber: 1284
+loginShell: /bin/bash
+title: user.273
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.273
+cn: User 273
+displayName: User 273
+givenName: User
+sn: 273
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25874786-9eff-1037-88ae-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755780Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.274,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.274
+mail: user.274@example.com
+o: example
+initials: u.274
+structuralObjectClass: inetOrgPerson
+uidNumber: 1278
+gidNumber: 1285
+loginShell: /bin/bash
+title: user.274
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.274
+cn: User 274
+displayName: User 274
+givenName: User
+sn: 274
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25874a06-9eff-1037-88af-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755844Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.275,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.275
+mail: user.275@example.com
+o: example
+initials: u.275
+structuralObjectClass: inetOrgPerson
+uidNumber: 1279
+gidNumber: 1286
+loginShell: /bin/bash
+title: user.275
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.275
+cn: User 275
+displayName: User 275
+givenName: User
+sn: 275
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25874c68-9eff-1037-88b0-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755906Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.276,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.276
+mail: user.276@example.com
+o: example
+initials: u.276
+structuralObjectClass: inetOrgPerson
+uidNumber: 1280
+gidNumber: 1287
+loginShell: /bin/bash
+title: user.276
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.276
+cn: User 276
+displayName: User 276
+givenName: User
+sn: 276
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25874f42-9eff-1037-88b1-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755978Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.277,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.277
+mail: user.277@example.com
+o: example
+initials: u.277
+structuralObjectClass: inetOrgPerson
+uidNumber: 1281
+gidNumber: 1288
+loginShell: /bin/bash
+title: user.277
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.277
+cn: User 277
+displayName: User 277
+givenName: User
+sn: 277
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258751b8-9eff-1037-88b2-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756041Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.278,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.278
+mail: user.278@example.com
+o: example
+initials: u.278
+structuralObjectClass: inetOrgPerson
+uidNumber: 1282
+gidNumber: 1289
+loginShell: /bin/bash
+title: user.278
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.278
+cn: User 278
+displayName: User 278
+givenName: User
+sn: 278
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25875410-9eff-1037-88b3-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756101Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.279,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.279
+mail: user.279@example.com
+o: example
+initials: u.279
+structuralObjectClass: inetOrgPerson
+uidNumber: 1283
+gidNumber: 1290
+loginShell: /bin/bash
+title: user.279
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.279
+cn: User 279
+displayName: User 279
+givenName: User
+sn: 279
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258756d6-9eff-1037-88b4-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756172Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.280,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.280
+mail: user.280@example.com
+o: example
+initials: u.280
+structuralObjectClass: inetOrgPerson
+uidNumber: 1284
+gidNumber: 1291
+loginShell: /bin/bash
+title: user.280
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.280
+cn: User 280
+displayName: User 280
+givenName: User
+sn: 280
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25875988-9eff-1037-88b5-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756242Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.281,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.281
+mail: user.281@example.com
+o: example
+initials: u.281
+structuralObjectClass: inetOrgPerson
+uidNumber: 1285
+gidNumber: 1292
+loginShell: /bin/bash
+title: user.281
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.281
+cn: User 281
+displayName: User 281
+givenName: User
+sn: 281
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25875bfe-9eff-1037-88b6-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756304Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.282,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.282
+mail: user.282@example.com
+o: example
+initials: u.282
+structuralObjectClass: inetOrgPerson
+uidNumber: 1286
+gidNumber: 1293
+loginShell: /bin/bash
+title: user.282
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.282
+cn: User 282
+displayName: User 282
+givenName: User
+sn: 282
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25875ec4-9eff-1037-88b7-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756375Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.283,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.283
+mail: user.283@example.com
+o: example
+initials: u.283
+structuralObjectClass: inetOrgPerson
+uidNumber: 1287
+gidNumber: 1294
+loginShell: /bin/bash
+title: user.283
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.283
+cn: User 283
+displayName: User 283
+givenName: User
+sn: 283
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25876130-9eff-1037-88b8-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756438Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.284,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.284
+mail: user.284@example.com
+o: example
+initials: u.284
+structuralObjectClass: inetOrgPerson
+uidNumber: 1288
+gidNumber: 1295
+loginShell: /bin/bash
+title: user.284
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.284
+cn: User 284
+displayName: User 284
+givenName: User
+sn: 284
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2587639c-9eff-1037-88b9-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756499Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.285,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.285
+mail: user.285@example.com
+o: example
+initials: u.285
+structuralObjectClass: inetOrgPerson
+uidNumber: 1289
+gidNumber: 1296
+loginShell: /bin/bash
+title: user.285
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.285
+cn: User 285
+displayName: User 285
+givenName: User
+sn: 285
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25876662-9eff-1037-88ba-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756570Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.286,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.286
+mail: user.286@example.com
+o: example
+initials: u.286
+structuralObjectClass: inetOrgPerson
+uidNumber: 1290
+gidNumber: 1297
+loginShell: /bin/bash
+title: user.286
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.286
+cn: User 286
+displayName: User 286
+givenName: User
+sn: 286
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258768ce-9eff-1037-88bb-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756632Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.287,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.287
+mail: user.287@example.com
+o: example
+initials: u.287
+structuralObjectClass: inetOrgPerson
+uidNumber: 1291
+gidNumber: 1298
+loginShell: /bin/bash
+title: user.287
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.287
+cn: User 287
+displayName: User 287
+givenName: User
+sn: 287
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25876bf8-9eff-1037-88bc-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756713Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.288,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.288
+mail: user.288@example.com
+o: example
+initials: u.288
+structuralObjectClass: inetOrgPerson
+uidNumber: 1292
+gidNumber: 1299
+loginShell: /bin/bash
+title: user.288
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.288
+cn: User 288
+displayName: User 288
+givenName: User
+sn: 288
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25876edc-9eff-1037-88bd-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756787Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.289,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.289
+mail: user.289@example.com
+o: example
+initials: u.289
+structuralObjectClass: inetOrgPerson
+uidNumber: 1293
+gidNumber: 1300
+loginShell: /bin/bash
+title: user.289
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.289
+cn: User 289
+displayName: User 289
+givenName: User
+sn: 289
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25877148-9eff-1037-88be-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756850Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.290,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.290
+mail: user.290@example.com
+o: example
+initials: u.290
+structuralObjectClass: inetOrgPerson
+uidNumber: 1294
+gidNumber: 1301
+loginShell: /bin/bash
+title: user.290
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.290
+cn: User 290
+displayName: User 290
+givenName: User
+sn: 290
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258773aa-9eff-1037-88bf-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756911Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.291,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.291
+mail: user.291@example.com
+o: example
+initials: u.291
+structuralObjectClass: inetOrgPerson
+uidNumber: 1295
+gidNumber: 1302
+loginShell: /bin/bash
+title: user.291
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.291
+cn: User 291
+displayName: User 291
+givenName: User
+sn: 291
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2587767a-9eff-1037-88c0-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756982Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.292,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.292
+mail: user.292@example.com
+o: example
+initials: u.292
+structuralObjectClass: inetOrgPerson
+uidNumber: 1296
+gidNumber: 1303
+loginShell: /bin/bash
+title: user.292
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.292
+cn: User 292
+displayName: User 292
+givenName: User
+sn: 292
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258778e6-9eff-1037-88c1-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.757044Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.293,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.293
+mail: user.293@example.com
+o: example
+initials: u.293
+structuralObjectClass: inetOrgPerson
+uidNumber: 1297
+gidNumber: 1304
+loginShell: /bin/bash
+title: user.293
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.293
+cn: User 293
+displayName: User 293
+givenName: User
+sn: 293
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25877b48-9eff-1037-88c2-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.757106Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.294,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.294
+mail: user.294@example.com
+o: example
+initials: u.294
+structuralObjectClass: inetOrgPerson
+uidNumber: 1298
+gidNumber: 1305
+loginShell: /bin/bash
+title: user.294
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.294
+cn: User 294
+displayName: User 294
+givenName: User
+sn: 294
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25878020-9eff-1037-88c3-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.757227Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.295,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.295
+mail: user.295@example.com
+o: example
+initials: u.295
+structuralObjectClass: inetOrgPerson
+uidNumber: 1299
+gidNumber: 1306
+loginShell: /bin/bash
+title: user.295
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.295
+cn: User 295
+displayName: User 295
+givenName: User
+sn: 295
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258782f0-9eff-1037-88c4-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.757301Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.296,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.296
+mail: user.296@example.com
+o: example
+initials: u.296
+structuralObjectClass: inetOrgPerson
+uidNumber: 1300
+gidNumber: 1307
+loginShell: /bin/bash
+title: user.296
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.296
+cn: User 296
+displayName: User 296
+givenName: User
+sn: 296
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2587855c-9eff-1037-88c5-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.757363Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.297,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.297
+mail: user.297@example.com
+o: example
+initials: u.297
+structuralObjectClass: inetOrgPerson
+uidNumber: 1301
+gidNumber: 1308
+loginShell: /bin/bash
+title: user.297
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.297
+cn: User 297
+displayName: User 297
+givenName: User
+sn: 297
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25878822-9eff-1037-88c6-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.757434Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.298,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.298
+mail: user.298@example.com
+o: example
+initials: u.298
+structuralObjectClass: inetOrgPerson
+uidNumber: 1302
+gidNumber: 1309
+loginShell: /bin/bash
+title: user.298
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.298
+cn: User 298
+displayName: User 298
+givenName: User
+sn: 298
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25878a8e-9eff-1037-88c7-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.757497Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.299,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.299
+mail: user.299@example.com
+o: example
+initials: u.299
+structuralObjectClass: inetOrgPerson
+uidNumber: 1303
+gidNumber: 1310
+loginShell: /bin/bash
+title: user.299
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.299
+cn: User 299
+displayName: User 299
+givenName: User
+sn: 299
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25878cfa-9eff-1037-88c8-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.757558Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.300,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.300
+mail: user.300@example.com
+o: example
+initials: u.300
+structuralObjectClass: inetOrgPerson
+uidNumber: 1304
+gidNumber: 1311
+loginShell: /bin/bash
+title: user.300
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.300
+cn: User 300
+displayName: User 300
+givenName: User
+sn: 300
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25878fc0-9eff-1037-88c9-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.757629Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: ou=ppolicy,dc=example,dc=com
+objectClass: organizationalUnit
+ou: ppolicy
+structuralObjectClass: organizationalUnit
+entryUUID: ab1da419-8b45-4f74-b7e1-46d1d0ea87ce
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20171201011219Z
+entryCSN: 20171201011219.211336Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20171201011219Z
+
+dn: cn=default,ou=ppolicy,dc=example,dc=com
+cn: default
+objectClass: device
+objectClass: pwdPolicy
+pwdAttribute: userPassword
+pwdLockout: TRUE
+pwdLockoutDuration: 1800
+pwdMaxFailure: 100
+pwdFailureCountInterval: 300
+structuralObjectClass: device
+entryUUID: 5b04a418-5448-4f13-acff-17b263808b67
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20171201011219Z
+entryCSN: 20171201011219.216962Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20171201011219Z
+
+dn: cn=serviceaccount,ou=ppolicy,dc=example,dc=com
+cn: serviceaccount
+objectClass: device
+objectClass: pwdPolicy
+pwdAttribute: userPassword
+structuralObjectClass: device
+entryUUID: 7830daeb-65d0-4b7f-ba9d-f02bdf908b24
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20171201011219Z
+entryCSN: 20171201011219.228381Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20171201011219Z
+
diff --git a/tests/data/regressions/its8800/its8800 b/tests/data/regressions/its8800/its8800
new file mode 100755
index 0000000..1c65a66
--- /dev/null
+++ b/tests/data/regressions/its8800/its8800
@@ -0,0 +1,208 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $PPOLICY = ppolicyno; then
+ echo "Password policy overlay not available, test skipped"
+ exit 0
+fi
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+if test $ACCESSLOG = accesslogno; then
+ echo "Accesslog overlay not available, test skipped"
+ exit 0
+fi
+if test $BACKEND = ldif ; then
+ # Onelevel search does not return entries in order of creation or CSN.
+ echo "$BACKEND backend unsuitable for syncprov logdb, test skipped"
+ exit 0
+fi
+
+echo "This test tracks a case where changes are not refreshed when an old db is reloaded"
+echo "See https://bugs.openldap.org/show_bug.cgi?id=8800 for more information."
+
+MPR=4
+XDIR=$TESTDIR/srv
+
+mkdir -p $TESTDIR
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+ITS=8800
+ITSDIR=$DATADIR/regressions/its$ITS
+
+
+n=1
+while [ $n -le $MPR ]; do
+ echo "Initializing server configuration for MPR$n..."
+ DBDIR=${XDIR}$n/db
+ CFDIR=${XDIR}$n/slapd.d
+
+ mkdir -p ${XDIR}$n $DBDIR.1 $DBDIR.2 $CFDIR
+ . $CONFFILTER $BACKEND < $ITSDIR/slapd-provider${n}.ldif > $CONFLDIF
+ $SLAPADD -F $CFDIR -n 0 -l $CONFLDIF
+ $SLAPADD -F $CFDIR -q -b $BASEDN -l $ITSDIR/db.ldif
+ n=`expr $n + 1`
+done
+
+KILLPIDS=
+n=1
+while [ $n -le $MPR ]; do
+ MYURI=`eval echo '$URI'$n`
+ MYLOG=`eval echo '$LOG'$n`
+ CFDIR=${XDIR}$n/slapd.d
+
+ echo "Starting provider slapd on TCP/IP URI $MYURI"
+ $SLAPD -F $CFDIR -h $MYURI -d $LVL > $MYLOG 2>&1 &
+
+ PID=$!
+ if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+ fi
+ KILLPIDS="$PID $KILLPIDS"
+ if [ $n = 1 ]; then
+ MPID="$PID"
+ fi
+ sleep 1
+
+ echo "Using ldapsearch to check that provider slapd is running..."
+ for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $MYURI \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+ done
+
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ n=`expr $n + 1`
+done
+
+echo "Sleeping 10 seconds to allow stabilization..."
+sleep 10
+
+echo "Looping failed authentications for 50 users 10 times each..."
+user=250
+while [ $user -le 300 ]; do
+ auths=1
+ echo -n "user $user..."
+ while [ $auths -le 10 ]; do
+ $LDAPSEARCH -x -H $URI1 -D uid=user.$user,ou=user,dc=example,dc=com -w wrongpass uid=fred >/dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ echo "ldapsearch succeeded when it should have failed"
+ exit 1
+ fi
+ auths=`expr $auths + 1`
+ done
+ echo "done"
+ user=`expr $user + 1`
+done
+
+echo -n "Sleeping 1 minute to ensure consumers catch up..."
+sleep 60
+echo "done"
+
+echo -n "Stopping MPR1 slapd..."
+kill -HUP $MPID
+wait $MPID
+KILLPIDS=`echo "$KILLPIDS " | sed -e "s/ $MPID / /"`;
+sleep $SLEEP2
+echo "done"
+
+echo -n "Wiping primary and accesslog databases for MPR1..."
+DBDIR="$TESTDIR/srv1/db"
+CFDIR="$TESTDIR/srv1/slapd.d"
+mv $DBDIR.1 $DBDIR.1.orig
+mv $DBDIR.2 $DBDIR.2.orig
+mkdir -p $DBDIR.1 $DBDIR.2
+$SLAPADD -F $CFDIR -q -b $BASEDN -l $ITSDIR/db.ldif
+echo "done"
+
+echo "Starting provider1 slapd on TCP/IP URI $URI1"
+CFDIR="$TESTDIR/srv1/slapd.d"
+$SLAPD -F $CFDIR -h $URI1 -d $LVL >> $LOG1 2>&1 &
+
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID $KILLPIDS"
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+echo "done"
+
+echo "Sleeping 5 minutes to allow databases to sync..."
+sleep 300
+
+echo "Comparing resulting databases..."
+
+n=1
+while [ $n -le 4 ]; do
+ CFDIR=${XDIR}$n/slapd.d
+ $SLAPCAT -F $CFDIR -b $BASEDN -l $TESTDIR/finaldb-$n.ldif
+ n=`expr $n + 1`
+done
+
+n=2
+while [ $n -le 4 ]; do
+ diff $TESTDIR/finaldb-1.ldif $TESTDIR/finaldb-$n.ldif > /dev/null 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ERROR: Final LDIF files differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ n=`expr $n + 1`
+done
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/data/regressions/its8800/slapd-provider1.ldif b/tests/data/regressions/its8800/slapd-provider1.ldif
new file mode 100644
index 0000000..4e0109b
--- /dev/null
+++ b/tests/data/regressions/its8800/slapd-provider1.ldif
@@ -0,0 +1,143 @@
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcLogLevel: Sync
+olcLogLevel: Stats
+olcPidFile: @TESTDIR@/slapd.1.pid
+olcArgsFile: @TESTDIR@/slapd.1.args
+olcServerID: 1
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file://@TESTWD@/@SCHEMADIR@/core.ldif
+include: file://@TESTWD@/@SCHEMADIR@/cosine.ldif
+include: file://@TESTWD@/@SCHEMADIR@/inetorgperson.ldif
+include: file://@TESTWD@/@SCHEMADIR@/misc.ldif
+include: file://@TESTWD@/@SCHEMADIR@/nis.ldif
+
+#mod#dn: cn=module{0},cn=config
+#mod#objectClass: olcModuleList
+#mod#cn: module{0}
+#mod#olcModulePath: @TESTWD@/../servers/slapd/back-@BACKEND@/
+#mod#olcModuleLoad: {0}back_@BACKEND@.la
+
+#syncprovmod#dn: cn=module{1},cn=config
+#syncprovmod#objectClass: olcModuleList
+#syncprovmod#cn: module{1}
+#syncprovmod#olcModulePath: @TESTWD@/../servers/slapd/overlays/
+#syncprovmod#olcModuleLoad: {0}syncprov.la
+#accesslogmod#olcModuleLoad: {1}accesslog.la
+#ppolicymod#olcModuleLoad: {2}ppolicy.la
+
+dn: olcDatabase={-1}frontend,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcFrontendConfig
+olcDatabase: {-1}frontend
+olcAccess: {0}to dn="" by * read
+olcAccess: {1}to * by self write by users read by anonymous auth
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcAccess: {0}to * by * none
+olcRootPW:< file://@TESTDIR@/configpw
+
+dn: olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {1}@BACKEND@
+olcSuffix: dc=example,dc=com
+olcRootDN: cn=manager,dc=example,dc=com
+olcRootPW: secret
+olcSizeLimit: unlimited
+olcTimeLimit: unlimited
+olcMultiProvider: TRUE
+olcSyncrepl: {0}rid=100 provider=@URI2@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+olcSyncrepl: {1}rid=101 provider=@URI3@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+olcSyncrepl: {2}rid=102 provider=@URI4@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+#~null~#olcDbDirectory: @TESTDIR@/srv1/db.1
+#indexdb#olcDbIndex: default eq
+#indexdb#olcDbIndex: objectClass
+#indexdb#olcDbIndex: entryUUID
+#indexdb#olcDbIndex: entryCSN
+#indexdb#olcDbIndex: cn eq,sub,approx
+#indexdb#olcDbIndex: givenName eq,sub,approx
+#indexdb#olcDbIndex: displayname eq,sub,approx
+#indexdb#olcDbIndex: uid eq,sub
+#indexdb#olcDbIndex: uidNumber eq
+#indexdb#olcDbIndex: gidNumber eq
+#indexdb#olcDbIndex: mail eq,sub
+#indexdb#olcDbIndex: sn eq,sub,approx
+#indexdb#olcDbIndex: description eq,sub
+#indexdb#olcDbIndex: title eq,sub
+#indexdb#olcDbIndex: member
+#indexdb#olcDbIndex: ou eq,sub,approx
+#indexdb#olcDbIndex: memberUid
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcOverlay={0}syncprov,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcConfig
+objectClass: top
+objectClass: olcSyncProvConfig
+olcOverlay: {0}syncprov
+olcSpCheckpoint: 1 10
+olcSpSessionlog: 50
+
+dn: olcOverlay={1}accesslog,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcAccessLogConfig
+olcOverlay: {1}accesslog
+olcAccessLogDB: cn=accesslog
+olcAccessLogOps: writes
+olcAccessLogPurge: 07+00:00 01+00:00
+olcAccessLogSuccess: TRUE
+
+dn: olcOverlay={2}ppolicy,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcPPolicyConfig
+olcOverlay: {2}ppolicy
+olcPPolicyDefault: cn=default,ou=ppolicy,dc=example,dc=com
+
+dn: olcDatabase={2}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {2}@BACKEND@
+olcSuffix: cn=accesslog
+olcSizeLimit: unlimited
+olcTimeLimit: unlimited
+olcDbIndex: default eq
+olcDbIndex: entryCSN,objectClass,reqEnd,reqResult,reqStart,reqDN
+#~null~#olcDbDirectory: @TESTDIR@/srv1/db.2
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcOverlay={0}syncprov,olcDatabase={2}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcConfig
+objectClass: top
+objectClass: olcSyncProvConfig
+olcOverlay: {0}syncprov
+olcSpNoPresent: TRUE
+olcSpReloadHint: TRUE
+
+dn: olcDatabase={3}monitor,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {3}monitor
+olcAccess: {0}to dn.subtree="cn=monitor" by * read
diff --git a/tests/data/regressions/its8800/slapd-provider2.ldif b/tests/data/regressions/its8800/slapd-provider2.ldif
new file mode 100644
index 0000000..0718f73
--- /dev/null
+++ b/tests/data/regressions/its8800/slapd-provider2.ldif
@@ -0,0 +1,143 @@
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcLogLevel: Sync
+olcLogLevel: Stats
+olcPidFile: @TESTDIR@/slapd.2.pid
+olcArgsFile: @TESTDIR@/slapd.2.args
+olcServerID: 2
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file://@TESTWD@/@SCHEMADIR@/core.ldif
+include: file://@TESTWD@/@SCHEMADIR@/cosine.ldif
+include: file://@TESTWD@/@SCHEMADIR@/inetorgperson.ldif
+include: file://@TESTWD@/@SCHEMADIR@/misc.ldif
+include: file://@TESTWD@/@SCHEMADIR@/nis.ldif
+
+#mod#dn: cn=module{0},cn=config
+#mod#objectClass: olcModuleList
+#mod#cn: module{0}
+#mod#olcModulePath: @TESTWD@/../servers/slapd/back-@BACKEND@/
+#mod#olcModuleLoad: {0}back_@BACKEND@.la
+
+#syncprovmod#dn: cn=module{1},cn=config
+#syncprovmod#objectClass: olcModuleList
+#syncprovmod#cn: module{1}
+#syncprovmod#olcModulePath: @TESTWD@/../servers/slapd/overlays/
+#syncprovmod#olcModuleLoad: {0}syncprov.la
+#accesslogmod#olcModuleLoad: {1}accesslog.la
+#ppolicymod#olcModuleLoad: {2}ppolicy.la
+
+dn: olcDatabase={-1}frontend,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcFrontendConfig
+olcDatabase: {-1}frontend
+olcAccess: {0}to dn="" by * read
+olcAccess: {1}to * by self write by users read by anonymous auth
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcAccess: {0}to * by * none
+olcRootPW:< file://@TESTDIR@/configpw
+
+dn: olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {1}@BACKEND@
+olcSuffix: dc=example,dc=com
+olcRootDN: cn=manager,dc=example,dc=com
+olcRootPW: secret
+olcSizeLimit: unlimited
+olcTimeLimit: unlimited
+olcMultiProvider: TRUE
+olcSyncrepl: {0}rid=100 provider=@URI1@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+olcSyncrepl: {1}rid=101 provider=@URI3@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+olcSyncrepl: {2}rid=102 provider=@URI4@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+#~null~#olcDbDirectory: @TESTDIR@/srv2/db.1
+#indexdb#olcDbIndex: default eq
+#indexdb#olcDbIndex: objectClass
+#indexdb#olcDbIndex: entryUUID
+#indexdb#olcDbIndex: entryCSN
+#indexdb#olcDbIndex: cn eq,sub,approx
+#indexdb#olcDbIndex: givenName eq,sub,approx
+#indexdb#olcDbIndex: displayname eq,sub,approx
+#indexdb#olcDbIndex: uid eq,sub
+#indexdb#olcDbIndex: uidNumber eq
+#indexdb#olcDbIndex: gidNumber eq
+#indexdb#olcDbIndex: mail eq,sub
+#indexdb#olcDbIndex: sn eq,sub,approx
+#indexdb#olcDbIndex: description eq,sub
+#indexdb#olcDbIndex: title eq,sub
+#indexdb#olcDbIndex: member
+#indexdb#olcDbIndex: ou eq,sub,approx
+#indexdb#olcDbIndex: memberUid
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcOverlay={0}syncprov,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcConfig
+objectClass: top
+objectClass: olcSyncProvConfig
+olcOverlay: {0}syncprov
+olcSpCheckpoint: 1 10
+olcSpSessionlog: 50
+
+dn: olcOverlay={1}accesslog,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcAccessLogConfig
+olcOverlay: {1}accesslog
+olcAccessLogDB: cn=accesslog
+olcAccessLogOps: writes
+olcAccessLogPurge: 07+00:00 01+00:00
+olcAccessLogSuccess: TRUE
+
+dn: olcOverlay={2}ppolicy,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcPPolicyConfig
+olcOverlay: {2}ppolicy
+olcPPolicyDefault: cn=default,ou=ppolicy,dc=example,dc=com
+
+dn: olcDatabase={2}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {2}@BACKEND@
+olcSuffix: cn=accesslog
+olcSizeLimit: unlimited
+olcTimeLimit: unlimited
+olcDbIndex: default eq
+olcDbIndex: entryCSN,objectClass,reqEnd,reqResult,reqStart,reqDN
+#~null~#olcDbDirectory: @TESTDIR@/srv2/db.2
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcOverlay={0}syncprov,olcDatabase={2}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcConfig
+objectClass: top
+objectClass: olcSyncProvConfig
+olcOverlay: {0}syncprov
+olcSpNoPresent: TRUE
+olcSpReloadHint: TRUE
+
+dn: olcDatabase={3}monitor,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {3}monitor
+olcAccess: {0}to dn.subtree="cn=monitor" by * read
diff --git a/tests/data/regressions/its8800/slapd-provider3.ldif b/tests/data/regressions/its8800/slapd-provider3.ldif
new file mode 100644
index 0000000..dde04c1
--- /dev/null
+++ b/tests/data/regressions/its8800/slapd-provider3.ldif
@@ -0,0 +1,143 @@
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcLogLevel: Sync
+olcLogLevel: Stats
+olcPidFile: @TESTDIR@/slapd.3.pid
+olcArgsFile: @TESTDIR@/slapd.3.args
+olcServerID: 3
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file://@TESTWD@/@SCHEMADIR@/core.ldif
+include: file://@TESTWD@/@SCHEMADIR@/cosine.ldif
+include: file://@TESTWD@/@SCHEMADIR@/inetorgperson.ldif
+include: file://@TESTWD@/@SCHEMADIR@/misc.ldif
+include: file://@TESTWD@/@SCHEMADIR@/nis.ldif
+
+#mod#dn: cn=module{0},cn=config
+#mod#objectClass: olcModuleList
+#mod#cn: module{0}
+#mod#olcModulePath: @TESTWD@/../servers/slapd/back-@BACKEND@/
+#mod#olcModuleLoad: {0}back_@BACKEND@.la
+
+#syncprovmod#dn: cn=module{1},cn=config
+#syncprovmod#objectClass: olcModuleList
+#syncprovmod#cn: module{1}
+#syncprovmod#olcModulePath: @TESTWD@/../servers/slapd/overlays/
+#syncprovmod#olcModuleLoad: {0}syncprov.la
+#accesslogmod#olcModuleLoad: {1}accesslog.la
+#ppolicymod#olcModuleLoad: {2}ppolicy.la
+
+dn: olcDatabase={-1}frontend,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcFrontendConfig
+olcDatabase: {-1}frontend
+olcAccess: {0}to dn="" by * read
+olcAccess: {1}to * by self write by users read by anonymous auth
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcAccess: {0}to * by * none
+olcRootPW:< file://@TESTDIR@/configpw
+
+dn: olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {1}@BACKEND@
+olcSuffix: dc=example,dc=com
+olcRootDN: cn=manager,dc=example,dc=com
+olcRootPW: secret
+olcSizeLimit: unlimited
+olcTimeLimit: unlimited
+olcMultiProvider: TRUE
+olcSyncrepl: {0}rid=100 provider=@URI2@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+olcSyncrepl: {1}rid=101 provider=@URI1@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+olcSyncrepl: {2}rid=102 provider=@URI4@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+#~null~#olcDbDirectory: @TESTDIR@/srv3/db.1
+#indexdb#olcDbIndex: default eq
+#indexdb#olcDbIndex: objectClass
+#indexdb#olcDbIndex: entryUUID
+#indexdb#olcDbIndex: entryCSN
+#indexdb#olcDbIndex: cn eq,sub,approx
+#indexdb#olcDbIndex: givenName eq,sub,approx
+#indexdb#olcDbIndex: displayname eq,sub,approx
+#indexdb#olcDbIndex: uid eq,sub
+#indexdb#olcDbIndex: uidNumber eq
+#indexdb#olcDbIndex: gidNumber eq
+#indexdb#olcDbIndex: mail eq,sub
+#indexdb#olcDbIndex: sn eq,sub,approx
+#indexdb#olcDbIndex: description eq,sub
+#indexdb#olcDbIndex: title eq,sub
+#indexdb#olcDbIndex: member
+#indexdb#olcDbIndex: ou eq,sub,approx
+#indexdb#olcDbIndex: memberUid
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcOverlay={0}syncprov,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcConfig
+objectClass: top
+objectClass: olcSyncProvConfig
+olcOverlay: {0}syncprov
+olcSpCheckpoint: 1 10
+olcSpSessionlog: 50
+
+dn: olcOverlay={1}accesslog,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcAccessLogConfig
+olcOverlay: {1}accesslog
+olcAccessLogDB: cn=accesslog
+olcAccessLogOps: writes
+olcAccessLogPurge: 07+00:00 01+00:00
+olcAccessLogSuccess: TRUE
+
+dn: olcOverlay={2}ppolicy,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcPPolicyConfig
+olcOverlay: {2}ppolicy
+olcPPolicyDefault: cn=default,ou=ppolicy,dc=example,dc=com
+
+dn: olcDatabase={2}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {2}@BACKEND@
+olcSuffix: cn=accesslog
+olcSizeLimit: unlimited
+olcTimeLimit: unlimited
+olcDbIndex: default eq
+olcDbIndex: entryCSN,objectClass,reqEnd,reqResult,reqStart,reqDN
+#~null~#olcDbDirectory: @TESTDIR@/srv3/db.2
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcOverlay={0}syncprov,olcDatabase={2}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcConfig
+objectClass: top
+objectClass: olcSyncProvConfig
+olcOverlay: {0}syncprov
+olcSpNoPresent: TRUE
+olcSpReloadHint: TRUE
+
+dn: olcDatabase={3}monitor,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {3}monitor
+olcAccess: {0}to dn.subtree="cn=monitor" by * read
diff --git a/tests/data/regressions/its8800/slapd-provider4.ldif b/tests/data/regressions/its8800/slapd-provider4.ldif
new file mode 100644
index 0000000..6023b30
--- /dev/null
+++ b/tests/data/regressions/its8800/slapd-provider4.ldif
@@ -0,0 +1,143 @@
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcLogLevel: Sync
+olcLogLevel: Stats
+olcPidFile: @TESTDIR@/slapd.4.pid
+olcArgsFile: @TESTDIR@/slapd.4.args
+olcServerID: 4
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file://@TESTWD@/@SCHEMADIR@/core.ldif
+include: file://@TESTWD@/@SCHEMADIR@/cosine.ldif
+include: file://@TESTWD@/@SCHEMADIR@/inetorgperson.ldif
+include: file://@TESTWD@/@SCHEMADIR@/misc.ldif
+include: file://@TESTWD@/@SCHEMADIR@/nis.ldif
+
+#mod#dn: cn=module{0},cn=config
+#mod#objectClass: olcModuleList
+#mod#cn: module{0}
+#mod#olcModulePath: @TESTWD@/../servers/slapd/back-@BACKEND@/
+#mod#olcModuleLoad: {0}back_@BACKEND@.la
+
+#syncprovmod#dn: cn=module{1},cn=config
+#syncprovmod#objectClass: olcModuleList
+#syncprovmod#cn: module{1}
+#syncprovmod#olcModulePath: @TESTWD@/../servers/slapd/overlays/
+#syncprovmod#olcModuleLoad: {0}syncprov.la
+#accesslogmod#olcModuleLoad: {1}accesslog.la
+#ppolicymod#olcModuleLoad: {2}ppolicy.la
+
+dn: olcDatabase={-1}frontend,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcFrontendConfig
+olcDatabase: {-1}frontend
+olcAccess: {0}to dn="" by * read
+olcAccess: {1}to * by self write by users read by anonymous auth
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcAccess: {0}to * by * none
+olcRootPW:< file://@TESTDIR@/configpw
+
+dn: olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {1}@BACKEND@
+olcSuffix: dc=example,dc=com
+olcRootDN: cn=manager,dc=example,dc=com
+olcRootPW: secret
+olcSizeLimit: unlimited
+olcTimeLimit: unlimited
+olcMultiProvider: TRUE
+olcSyncrepl: {0}rid=100 provider=@URI2@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+olcSyncrepl: {1}rid=101 provider=@URI3@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+olcSyncrepl: {2}rid=102 provider=@URI1@ binddn="cn=manager,dc=example,dc=com
+ " credentials=secret bindmethod=simple searchbase="dc=example,dc=com" logba
+ se="cn=accesslog" logfilter="(&(objectClass=auditWriteObject)(reqResult=0))
+ " filter="(objectClass=*)" schemachecking=off attrs="*,+" type=refreshAndPe
+ rsist retry="60 +" tls_reqcert=never timeout=0 keepalive=240:10:30 syncdata
+ =accesslog network-timeout=0 scope=sub interval=00:00:00:03
+#~null~#olcDbDirectory: @TESTDIR@/srv4/db.1
+#indexdb#olcDbIndex: default eq
+#indexdb#olcDbIndex: objectClass
+#indexdb#olcDbIndex: entryUUID
+#indexdb#olcDbIndex: entryCSN
+#indexdb#olcDbIndex: cn eq,sub,approx
+#indexdb#olcDbIndex: givenName eq,sub,approx
+#indexdb#olcDbIndex: displayname eq,sub,approx
+#indexdb#olcDbIndex: uid eq,sub
+#indexdb#olcDbIndex: uidNumber eq
+#indexdb#olcDbIndex: gidNumber eq
+#indexdb#olcDbIndex: mail eq,sub
+#indexdb#olcDbIndex: sn eq,sub,approx
+#indexdb#olcDbIndex: description eq,sub
+#indexdb#olcDbIndex: title eq,sub
+#indexdb#olcDbIndex: member
+#indexdb#olcDbIndex: ou eq,sub,approx
+#indexdb#olcDbIndex: memberUid
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcOverlay={0}syncprov,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcConfig
+objectClass: top
+objectClass: olcSyncProvConfig
+olcOverlay: {0}syncprov
+olcSpCheckpoint: 1 10
+olcSpSessionlog: 50
+
+dn: olcOverlay={1}accesslog,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcAccessLogConfig
+olcOverlay: {1}accesslog
+olcAccessLogDB: cn=accesslog
+olcAccessLogOps: writes
+olcAccessLogPurge: 07+00:00 01+00:00
+olcAccessLogSuccess: TRUE
+
+dn: olcOverlay={2}ppolicy,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcPPolicyConfig
+olcOverlay: {2}ppolicy
+olcPPolicyDefault: cn=default,ou=ppolicy,dc=example,dc=com
+
+dn: olcDatabase={2}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {2}@BACKEND@
+olcSuffix: cn=accesslog
+olcSizeLimit: unlimited
+olcTimeLimit: unlimited
+olcDbIndex: default eq
+olcDbIndex: entryCSN,objectClass,reqEnd,reqResult,reqStart,reqDN
+#~null~#olcDbDirectory: @TESTDIR@/srv4/db.2
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcOverlay={0}syncprov,olcDatabase={2}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcConfig
+objectClass: top
+objectClass: olcSyncProvConfig
+olcOverlay: {0}syncprov
+olcSpNoPresent: TRUE
+olcSpReloadHint: TRUE
+
+dn: olcDatabase={3}monitor,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {3}monitor
+olcAccess: {0}to dn.subtree="cn=monitor" by * read
diff --git a/tests/data/regressions/its9051/db.ldif b/tests/data/regressions/its9051/db.ldif
new file mode 100644
index 0000000..2d1cc2d
--- /dev/null
+++ b/tests/data/regressions/its9051/db.ldif
@@ -0,0 +1,9339 @@
+dn: dc=example,dc=com
+objectClass: dcObject
+objectClass: organization
+dc: example
+o: example
+o: Example domain for holding data
+postalAddress: Example Organization, LLC$1234 Anywhere Street$Example, CA 99
+ 999
+description: Example Organization
+street: 1234 Anywhere Street
+l: Example
+st: CA
+postalCode: 99999
+telephoneNumber: +1 234 567 8910
+businessCategory: Example
+businessCategory: Examples
+structuralObjectClass: organization
+entryUUID: 156eb8cc-18e9-1027-80e5-d3f2010890dc
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20030512171533Z
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170401111056Z
+entryCSN: 20171130221813.848426Z#000000#004#000000
+contextCSN: 20171203010043.825769Z#000000#001#000000
+contextCSN: 20171130222521.056018Z#000000#002#000000
+contextCSN: 20171130222318.939265Z#000000#003#000000
+contextCSN: 20171201011219.228381Z#000000#004#000000
+
+dn: ou=user,dc=example,dc=com
+objectClass: organizationalUnit
+ou: user
+structuralObjectClass: organizationalUnit
+entryUUID: 159cc9b0-18e9-1027-80e6-d3f2010890dc
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20030512171533Z
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20030512171533Z
+entryCSN: 20171130221813.848561Z#000000#004#000000
+
+dn: uid=user.1,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.1
+mail: user.1@example.com
+o: example
+initials: u.1
+structuralObjectClass: inetOrgPerson
+uidNumber: 1005
+gidNumber: 1012
+loginShell: /bin/bash
+title: user.1
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.1
+cn: User 1
+displayName: User 1
+givenName: User
+sn: 1
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25845f6c-9eff-1037-879e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.736705Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.2,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.2
+mail: user.2@example.com
+o: example
+initials: u.2
+structuralObjectClass: inetOrgPerson
+uidNumber: 1006
+gidNumber: 1013
+loginShell: /bin/bash
+title: user.2
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.2
+cn: User 2
+displayName: User 2
+givenName: User
+sn: 2
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25846476-9eff-1037-879f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.736859Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.3,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.3
+mail: user.3@example.com
+o: example
+initials: u.3
+structuralObjectClass: inetOrgPerson
+uidNumber: 1007
+gidNumber: 1014
+loginShell: /bin/bash
+title: user.3
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.3
+cn: User 3
+displayName: User 3
+givenName: User
+sn: 3
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258467be-9eff-1037-87a0-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.736944Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.4,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.4
+mail: user.4@example.com
+o: example
+initials: u.4
+structuralObjectClass: inetOrgPerson
+uidNumber: 1008
+gidNumber: 1015
+loginShell: /bin/bash
+title: user.4
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.4
+cn: User 4
+displayName: User 4
+givenName: User
+sn: 4
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25846b10-9eff-1037-87a1-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737029Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.5,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.5
+mail: user.5@example.com
+o: example
+initials: u.5
+structuralObjectClass: inetOrgPerson
+uidNumber: 1009
+gidNumber: 1016
+loginShell: /bin/bash
+title: user.5
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.5
+cn: User 5
+displayName: User 5
+givenName: User
+sn: 5
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25846dae-9eff-1037-87a2-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737096Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.6,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.6
+mail: user.6@example.com
+o: example
+initials: u.6
+structuralObjectClass: inetOrgPerson
+uidNumber: 1010
+gidNumber: 1017
+loginShell: /bin/bash
+title: user.6
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.6
+cn: User 6
+displayName: User 6
+givenName: User
+sn: 6
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584709c-9eff-1037-87a3-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737171Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.7,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.7
+mail: user.7@example.com
+o: example
+initials: u.7
+structuralObjectClass: inetOrgPerson
+uidNumber: 1011
+gidNumber: 1018
+loginShell: /bin/bash
+title: user.7
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.7
+cn: User 7
+displayName: User 7
+givenName: User
+sn: 7
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25847312-9eff-1037-87a4-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737235Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.8,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.8
+mail: user.8@example.com
+o: example
+initials: u.8
+structuralObjectClass: inetOrgPerson
+uidNumber: 1012
+gidNumber: 1019
+loginShell: /bin/bash
+title: user.8
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.8
+cn: User 8
+displayName: User 8
+givenName: User
+sn: 8
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25847600-9eff-1037-87a5-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737309Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.9,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.9
+mail: user.9@example.com
+o: example
+initials: u.9
+structuralObjectClass: inetOrgPerson
+uidNumber: 1013
+gidNumber: 1020
+loginShell: /bin/bash
+title: user.9
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.9
+cn: User 9
+displayName: User 9
+givenName: User
+sn: 9
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25847880-9eff-1037-87a6-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737373Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.10,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.10
+mail: user.10@example.com
+o: example
+initials: u.10
+structuralObjectClass: inetOrgPerson
+uidNumber: 1014
+gidNumber: 1021
+loginShell: /bin/bash
+title: user.10
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.10
+cn: User 10
+displayName: User 10
+givenName: User
+sn: 10
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25847b14-9eff-1037-87a7-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737439Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.11,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.11
+mail: user.11@example.com
+o: example
+initials: u.11
+structuralObjectClass: inetOrgPerson
+uidNumber: 1015
+gidNumber: 1022
+loginShell: /bin/bash
+title: user.11
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.11
+cn: User 11
+displayName: User 11
+givenName: User
+sn: 11
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25847d94-9eff-1037-87a8-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737503Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.12,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.12
+mail: user.12@example.com
+o: example
+initials: u.12
+structuralObjectClass: inetOrgPerson
+uidNumber: 1016
+gidNumber: 1023
+loginShell: /bin/bash
+title: user.12
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.12
+cn: User 12
+displayName: User 12
+givenName: User
+sn: 12
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25848078-9eff-1037-87a9-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737577Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.13,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.13
+mail: user.13@example.com
+o: example
+initials: u.13
+structuralObjectClass: inetOrgPerson
+uidNumber: 1017
+gidNumber: 1024
+loginShell: /bin/bash
+title: user.13
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.13
+cn: User 13
+displayName: User 13
+givenName: User
+sn: 13
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258482ee-9eff-1037-87aa-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737641Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.14,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.14
+mail: user.14@example.com
+o: example
+initials: u.14
+structuralObjectClass: inetOrgPerson
+uidNumber: 1018
+gidNumber: 1025
+loginShell: /bin/bash
+title: user.14
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.14
+cn: User 14
+displayName: User 14
+givenName: User
+sn: 14
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584865e-9eff-1037-87ab-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737728Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.15,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.15
+mail: user.15@example.com
+o: example
+initials: u.15
+structuralObjectClass: inetOrgPerson
+uidNumber: 1019
+gidNumber: 1026
+loginShell: /bin/bash
+title: user.15
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.15
+cn: User 15
+displayName: User 15
+givenName: User
+sn: 15
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25848956-9eff-1037-87ac-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737804Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.16,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.16
+mail: user.16@example.com
+o: example
+initials: u.16
+structuralObjectClass: inetOrgPerson
+uidNumber: 1020
+gidNumber: 1027
+loginShell: /bin/bash
+title: user.16
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.16
+cn: User 16
+displayName: User 16
+givenName: User
+sn: 16
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25848bd6-9eff-1037-87ad-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737868Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.17,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.17
+mail: user.17@example.com
+o: example
+initials: u.17
+structuralObjectClass: inetOrgPerson
+uidNumber: 1021
+gidNumber: 1028
+loginShell: /bin/bash
+title: user.17
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.17
+cn: User 17
+displayName: User 17
+givenName: User
+sn: 17
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25848e38-9eff-1037-87ae-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.737930Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.18,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.18
+mail: user.18@example.com
+o: example
+initials: u.18
+structuralObjectClass: inetOrgPerson
+uidNumber: 1022
+gidNumber: 1029
+loginShell: /bin/bash
+title: user.18
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.18
+cn: User 18
+displayName: User 18
+givenName: User
+sn: 18
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25849126-9eff-1037-87af-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738004Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.19,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.19
+mail: user.19@example.com
+o: example
+initials: u.19
+structuralObjectClass: inetOrgPerson
+uidNumber: 1023
+gidNumber: 1030
+loginShell: /bin/bash
+title: user.19
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.19
+cn: User 19
+displayName: User 19
+givenName: User
+sn: 19
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584939c-9eff-1037-87b0-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738067Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.20,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.20
+mail: user.20@example.com
+o: example
+initials: u.20
+structuralObjectClass: inetOrgPerson
+uidNumber: 1024
+gidNumber: 1031
+loginShell: /bin/bash
+title: user.20
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.20
+cn: User 20
+displayName: User 20
+givenName: User
+sn: 20
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258495fe-9eff-1037-87b1-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738128Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.21,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.21
+mail: user.21@example.com
+o: example
+initials: u.21
+structuralObjectClass: inetOrgPerson
+uidNumber: 1025
+gidNumber: 1032
+loginShell: /bin/bash
+title: user.21
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.21
+cn: User 21
+displayName: User 21
+givenName: User
+sn: 21
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584990a-9eff-1037-87b2-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738206Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.22,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.22
+mail: user.22@example.com
+o: example
+initials: u.22
+structuralObjectClass: inetOrgPerson
+uidNumber: 1026
+gidNumber: 1033
+loginShell: /bin/bash
+title: user.22
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.22
+cn: User 22
+displayName: User 22
+givenName: User
+sn: 22
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25849b9e-9eff-1037-87b3-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738272Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.23,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.23
+mail: user.23@example.com
+o: example
+initials: u.23
+structuralObjectClass: inetOrgPerson
+uidNumber: 1027
+gidNumber: 1034
+loginShell: /bin/bash
+title: user.23
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.23
+cn: User 23
+displayName: User 23
+givenName: User
+sn: 23
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25849e00-9eff-1037-87b4-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738334Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.24,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.24
+mail: user.24@example.com
+o: example
+initials: u.24
+structuralObjectClass: inetOrgPerson
+uidNumber: 1028
+gidNumber: 1035
+loginShell: /bin/bash
+title: user.24
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.24
+cn: User 24
+displayName: User 24
+givenName: User
+sn: 24
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584a0e4-9eff-1037-87b5-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738407Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.25,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.25
+mail: user.25@example.com
+o: example
+initials: u.25
+structuralObjectClass: inetOrgPerson
+uidNumber: 1029
+gidNumber: 1036
+loginShell: /bin/bash
+title: user.25
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.25
+cn: User 25
+displayName: User 25
+givenName: User
+sn: 25
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584a350-9eff-1037-87b6-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738469Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.26,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.26
+mail: user.26@example.com
+o: example
+initials: u.26
+structuralObjectClass: inetOrgPerson
+uidNumber: 1030
+gidNumber: 1037
+loginShell: /bin/bash
+title: user.26
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.26
+cn: User 26
+displayName: User 26
+givenName: User
+sn: 26
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584a67a-9eff-1037-87b7-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738551Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.27,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.27
+mail: user.27@example.com
+o: example
+initials: u.27
+structuralObjectClass: inetOrgPerson
+uidNumber: 1031
+gidNumber: 1038
+loginShell: /bin/bash
+title: user.27
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.27
+cn: User 27
+displayName: User 27
+givenName: User
+sn: 27
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584a940-9eff-1037-87b8-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738621Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.28,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.28
+mail: user.28@example.com
+o: example
+initials: u.28
+structuralObjectClass: inetOrgPerson
+uidNumber: 1032
+gidNumber: 1039
+loginShell: /bin/bash
+title: user.28
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.28
+cn: User 28
+displayName: User 28
+givenName: User
+sn: 28
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584ac1a-9eff-1037-87b9-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738693Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.29,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.29
+mail: user.29@example.com
+o: example
+initials: u.29
+structuralObjectClass: inetOrgPerson
+uidNumber: 1033
+gidNumber: 1040
+loginShell: /bin/bash
+title: user.29
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.29
+cn: User 29
+displayName: User 29
+givenName: User
+sn: 29
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584aecc-9eff-1037-87ba-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738763Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.30,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.30
+mail: user.30@example.com
+o: example
+initials: u.30
+structuralObjectClass: inetOrgPerson
+uidNumber: 1034
+gidNumber: 1041
+loginShell: /bin/bash
+title: user.30
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.30
+cn: User 30
+displayName: User 30
+givenName: User
+sn: 30
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584b1ba-9eff-1037-87bb-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738838Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.31,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.31
+mail: user.31@example.com
+o: example
+initials: u.31
+structuralObjectClass: inetOrgPerson
+uidNumber: 1035
+gidNumber: 1042
+loginShell: /bin/bash
+title: user.31
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.31
+cn: User 31
+displayName: User 31
+givenName: User
+sn: 31
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584b426-9eff-1037-87bc-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738900Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.32,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.32
+mail: user.32@example.com
+o: example
+initials: u.32
+structuralObjectClass: inetOrgPerson
+uidNumber: 1036
+gidNumber: 1043
+loginShell: /bin/bash
+title: user.32
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.32
+cn: User 32
+displayName: User 32
+givenName: User
+sn: 32
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584b688-9eff-1037-87bd-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.738961Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.33,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.33
+mail: user.33@example.com
+o: example
+initials: u.33
+structuralObjectClass: inetOrgPerson
+uidNumber: 1037
+gidNumber: 1044
+loginShell: /bin/bash
+title: user.33
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.33
+cn: User 33
+displayName: User 33
+givenName: User
+sn: 33
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584b944-9eff-1037-87be-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739031Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.34,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.34
+mail: user.34@example.com
+o: example
+initials: u.34
+structuralObjectClass: inetOrgPerson
+uidNumber: 1038
+gidNumber: 1045
+loginShell: /bin/bash
+title: user.34
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.34
+cn: User 34
+displayName: User 34
+givenName: User
+sn: 34
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584bbc4-9eff-1037-87bf-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739095Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.35,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.35
+mail: user.35@example.com
+o: example
+initials: u.35
+structuralObjectClass: inetOrgPerson
+uidNumber: 1039
+gidNumber: 1046
+loginShell: /bin/bash
+title: user.35
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.35
+cn: User 35
+displayName: User 35
+givenName: User
+sn: 35
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584be1c-9eff-1037-87c0-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739156Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.36,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.36
+mail: user.36@example.com
+o: example
+initials: u.36
+structuralObjectClass: inetOrgPerson
+uidNumber: 1040
+gidNumber: 1047
+loginShell: /bin/bash
+title: user.36
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.36
+cn: User 36
+displayName: User 36
+givenName: User
+sn: 36
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584c150-9eff-1037-87c1-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739236Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.37,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.37
+mail: user.37@example.com
+o: example
+initials: u.37
+structuralObjectClass: inetOrgPerson
+uidNumber: 1041
+gidNumber: 1048
+loginShell: /bin/bash
+title: user.37
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.37
+cn: User 37
+displayName: User 37
+givenName: User
+sn: 37
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584c3c6-9eff-1037-87c2-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739301Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.38,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.38
+mail: user.38@example.com
+o: example
+initials: u.38
+structuralObjectClass: inetOrgPerson
+uidNumber: 1042
+gidNumber: 1049
+loginShell: /bin/bash
+title: user.38
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.38
+cn: User 38
+displayName: User 38
+givenName: User
+sn: 38
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584c740-9eff-1037-87c3-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739389Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.39,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.39
+mail: user.39@example.com
+o: example
+initials: u.39
+structuralObjectClass: inetOrgPerson
+uidNumber: 1043
+gidNumber: 1050
+loginShell: /bin/bash
+title: user.39
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.39
+cn: User 39
+displayName: User 39
+givenName: User
+sn: 39
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584ca10-9eff-1037-87c4-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739461Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.40,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.40
+mail: user.40@example.com
+o: example
+initials: u.40
+structuralObjectClass: inetOrgPerson
+uidNumber: 1044
+gidNumber: 1051
+loginShell: /bin/bash
+title: user.40
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.40
+cn: User 40
+displayName: User 40
+givenName: User
+sn: 40
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584ccf4-9eff-1037-87c5-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739535Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.41,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.41
+mail: user.41@example.com
+o: example
+initials: u.41
+structuralObjectClass: inetOrgPerson
+uidNumber: 1045
+gidNumber: 1052
+loginShell: /bin/bash
+title: user.41
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.41
+cn: User 41
+displayName: User 41
+givenName: User
+sn: 41
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584cf4c-9eff-1037-87c6-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739596Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.42,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.42
+mail: user.42@example.com
+o: example
+initials: u.42
+structuralObjectClass: inetOrgPerson
+uidNumber: 1046
+gidNumber: 1053
+loginShell: /bin/bash
+title: user.42
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.42
+cn: User 42
+displayName: User 42
+givenName: User
+sn: 42
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584d456-9eff-1037-87c7-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739723Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.43,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.43
+mail: user.43@example.com
+o: example
+initials: u.43
+structuralObjectClass: inetOrgPerson
+uidNumber: 1047
+gidNumber: 1054
+loginShell: /bin/bash
+title: user.43
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.43
+cn: User 43
+displayName: User 43
+givenName: User
+sn: 43
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584d708-9eff-1037-87c8-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739793Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.44,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.44
+mail: user.44@example.com
+o: example
+initials: u.44
+structuralObjectClass: inetOrgPerson
+uidNumber: 1048
+gidNumber: 1055
+loginShell: /bin/bash
+title: user.44
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.44
+cn: User 44
+displayName: User 44
+givenName: User
+sn: 44
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584d9c4-9eff-1037-87c9-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739863Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.45,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.45
+mail: user.45@example.com
+o: example
+initials: u.45
+structuralObjectClass: inetOrgPerson
+uidNumber: 1049
+gidNumber: 1056
+loginShell: /bin/bash
+title: user.45
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.45
+cn: User 45
+displayName: User 45
+givenName: User
+sn: 45
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584dcc6-9eff-1037-87ca-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.739940Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.46,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.46
+mail: user.46@example.com
+o: example
+initials: u.46
+structuralObjectClass: inetOrgPerson
+uidNumber: 1050
+gidNumber: 1057
+loginShell: /bin/bash
+title: user.46
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.46
+cn: User 46
+displayName: User 46
+givenName: User
+sn: 46
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584df3c-9eff-1037-87cb-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740004Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.47,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.47
+mail: user.47@example.com
+o: example
+initials: u.47
+structuralObjectClass: inetOrgPerson
+uidNumber: 1051
+gidNumber: 1058
+loginShell: /bin/bash
+title: user.47
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.47
+cn: User 47
+displayName: User 47
+givenName: User
+sn: 47
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584e194-9eff-1037-87cc-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740063Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.48,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.48
+mail: user.48@example.com
+o: example
+initials: u.48
+structuralObjectClass: inetOrgPerson
+uidNumber: 1052
+gidNumber: 1059
+loginShell: /bin/bash
+title: user.48
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.48
+cn: User 48
+displayName: User 48
+givenName: User
+sn: 48
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584e450-9eff-1037-87cd-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740134Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.49,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.49
+mail: user.49@example.com
+o: example
+initials: u.49
+structuralObjectClass: inetOrgPerson
+uidNumber: 1053
+gidNumber: 1060
+loginShell: /bin/bash
+title: user.49
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.49
+cn: User 49
+displayName: User 49
+givenName: User
+sn: 49
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584e6a8-9eff-1037-87ce-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740194Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.50,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.50
+mail: user.50@example.com
+o: example
+initials: u.50
+structuralObjectClass: inetOrgPerson
+uidNumber: 1054
+gidNumber: 1061
+loginShell: /bin/bash
+title: user.50
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.50
+cn: User 50
+displayName: User 50
+givenName: User
+sn: 50
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584e914-9eff-1037-87cf-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740255Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.51,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.51
+mail: user.51@example.com
+o: example
+initials: u.51
+structuralObjectClass: inetOrgPerson
+uidNumber: 1055
+gidNumber: 1062
+loginShell: /bin/bash
+title: user.51
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.51
+cn: User 51
+displayName: User 51
+givenName: User
+sn: 51
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584ec0c-9eff-1037-87d0-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740331Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.52,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.52
+mail: user.52@example.com
+o: example
+initials: u.52
+structuralObjectClass: inetOrgPerson
+uidNumber: 1056
+gidNumber: 1063
+loginShell: /bin/bash
+title: user.52
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.52
+cn: User 52
+displayName: User 52
+givenName: User
+sn: 52
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584ee78-9eff-1037-87d1-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740394Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.53,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.53
+mail: user.53@example.com
+o: example
+initials: u.53
+structuralObjectClass: inetOrgPerson
+uidNumber: 1057
+gidNumber: 1064
+loginShell: /bin/bash
+title: user.53
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.53
+cn: User 53
+displayName: User 53
+givenName: User
+sn: 53
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584f0d0-9eff-1037-87d2-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740453Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.54,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.54
+mail: user.54@example.com
+o: example
+initials: u.54
+structuralObjectClass: inetOrgPerson
+uidNumber: 1058
+gidNumber: 1065
+loginShell: /bin/bash
+title: user.54
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.54
+cn: User 54
+displayName: User 54
+givenName: User
+sn: 54
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584f3a0-9eff-1037-87d3-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740525Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.55,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.55
+mail: user.55@example.com
+o: example
+initials: u.55
+structuralObjectClass: inetOrgPerson
+uidNumber: 1059
+gidNumber: 1066
+loginShell: /bin/bash
+title: user.55
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.55
+cn: User 55
+displayName: User 55
+givenName: User
+sn: 55
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584f602-9eff-1037-87d4-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740586Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.56,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.56
+mail: user.56@example.com
+o: example
+initials: u.56
+structuralObjectClass: inetOrgPerson
+uidNumber: 1060
+gidNumber: 1067
+loginShell: /bin/bash
+title: user.56
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.56
+cn: User 56
+displayName: User 56
+givenName: User
+sn: 56
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584f8b4-9eff-1037-87d5-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740655Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.57,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.57
+mail: user.57@example.com
+o: example
+initials: u.57
+structuralObjectClass: inetOrgPerson
+uidNumber: 1061
+gidNumber: 1068
+loginShell: /bin/bash
+title: user.57
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.57
+cn: User 57
+displayName: User 57
+givenName: User
+sn: 57
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584fba2-9eff-1037-87d6-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740730Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.58,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.58
+mail: user.58@example.com
+o: example
+initials: u.58
+structuralObjectClass: inetOrgPerson
+uidNumber: 1062
+gidNumber: 1069
+loginShell: /bin/bash
+title: user.58
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.58
+cn: User 58
+displayName: User 58
+givenName: User
+sn: 58
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2584fe22-9eff-1037-87d7-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740794Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.59,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.59
+mail: user.59@example.com
+o: example
+initials: u.59
+structuralObjectClass: inetOrgPerson
+uidNumber: 1063
+gidNumber: 1070
+loginShell: /bin/bash
+title: user.59
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.59
+cn: User 59
+displayName: User 59
+givenName: User
+sn: 59
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258500c0-9eff-1037-87d8-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740862Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.60,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.60
+mail: user.60@example.com
+o: example
+initials: u.60
+structuralObjectClass: inetOrgPerson
+uidNumber: 1064
+gidNumber: 1071
+loginShell: /bin/bash
+title: user.60
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.60
+cn: User 60
+displayName: User 60
+givenName: User
+sn: 60
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585039a-9eff-1037-87d9-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740934Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.61,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.61
+mail: user.61@example.com
+o: example
+initials: u.61
+structuralObjectClass: inetOrgPerson
+uidNumber: 1065
+gidNumber: 1072
+loginShell: /bin/bash
+title: user.61
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.61
+cn: User 61
+displayName: User 61
+givenName: User
+sn: 61
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258505fc-9eff-1037-87da-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.740995Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.62,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.62
+mail: user.62@example.com
+o: example
+initials: u.62
+structuralObjectClass: inetOrgPerson
+uidNumber: 1066
+gidNumber: 1073
+loginShell: /bin/bash
+title: user.62
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.62
+cn: User 62
+displayName: User 62
+givenName: User
+sn: 62
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25850872-9eff-1037-87db-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741058Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.63,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.63
+mail: user.63@example.com
+o: example
+initials: u.63
+structuralObjectClass: inetOrgPerson
+uidNumber: 1067
+gidNumber: 1074
+loginShell: /bin/bash
+title: user.63
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.63
+cn: User 63
+displayName: User 63
+givenName: User
+sn: 63
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25850b24-9eff-1037-87dc-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741127Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.64,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.64
+mail: user.64@example.com
+o: example
+initials: u.64
+structuralObjectClass: inetOrgPerson
+uidNumber: 1068
+gidNumber: 1075
+loginShell: /bin/bash
+title: user.64
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.64
+cn: User 64
+displayName: User 64
+givenName: User
+sn: 64
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25850d90-9eff-1037-87dd-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741189Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.65,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.65
+mail: user.65@example.com
+o: example
+initials: u.65
+structuralObjectClass: inetOrgPerson
+uidNumber: 1069
+gidNumber: 1076
+loginShell: /bin/bash
+title: user.65
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.65
+cn: User 65
+displayName: User 65
+givenName: User
+sn: 65
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25850fde-9eff-1037-87de-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741248Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.66,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.66
+mail: user.66@example.com
+o: example
+initials: u.66
+structuralObjectClass: inetOrgPerson
+uidNumber: 1070
+gidNumber: 1077
+loginShell: /bin/bash
+title: user.66
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.66
+cn: User 66
+displayName: User 66
+givenName: User
+sn: 66
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258512ea-9eff-1037-87df-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741326Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.67,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.67
+mail: user.67@example.com
+o: example
+initials: u.67
+structuralObjectClass: inetOrgPerson
+uidNumber: 1071
+gidNumber: 1078
+loginShell: /bin/bash
+title: user.67
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.67
+cn: User 67
+displayName: User 67
+givenName: User
+sn: 67
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585154c-9eff-1037-87e0-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741388Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.68,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.68
+mail: user.68@example.com
+o: example
+initials: u.68
+structuralObjectClass: inetOrgPerson
+uidNumber: 1072
+gidNumber: 1079
+loginShell: /bin/bash
+title: user.68
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.68
+cn: User 68
+displayName: User 68
+givenName: User
+sn: 68
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258517ae-9eff-1037-87e1-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741448Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.69,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.69
+mail: user.69@example.com
+o: example
+initials: u.69
+structuralObjectClass: inetOrgPerson
+uidNumber: 1073
+gidNumber: 1080
+loginShell: /bin/bash
+title: user.69
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.69
+cn: User 69
+displayName: User 69
+givenName: User
+sn: 69
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25851a6a-9eff-1037-87e2-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741518Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.70,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.70
+mail: user.70@example.com
+o: example
+initials: u.70
+structuralObjectClass: inetOrgPerson
+uidNumber: 1074
+gidNumber: 1081
+loginShell: /bin/bash
+title: user.70
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.70
+cn: User 70
+displayName: User 70
+givenName: User
+sn: 70
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25851cea-9eff-1037-87e3-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741582Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.71,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.71
+mail: user.71@example.com
+o: example
+initials: u.71
+structuralObjectClass: inetOrgPerson
+uidNumber: 1075
+gidNumber: 1082
+loginShell: /bin/bash
+title: user.71
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.71
+cn: User 71
+displayName: User 71
+givenName: User
+sn: 71
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25851f38-9eff-1037-87e4-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741641Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.72,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.72
+mail: user.72@example.com
+o: example
+initials: u.72
+structuralObjectClass: inetOrgPerson
+uidNumber: 1076
+gidNumber: 1083
+loginShell: /bin/bash
+title: user.72
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.72
+cn: User 72
+displayName: User 72
+givenName: User
+sn: 72
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585229e-9eff-1037-87e5-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741728Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.73,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.73
+mail: user.73@example.com
+o: example
+initials: u.73
+structuralObjectClass: inetOrgPerson
+uidNumber: 1077
+gidNumber: 1084
+loginShell: /bin/bash
+title: user.73
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.73
+cn: User 73
+displayName: User 73
+givenName: User
+sn: 73
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25852550-9eff-1037-87e6-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741797Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.74,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.74
+mail: user.74@example.com
+o: example
+initials: u.74
+structuralObjectClass: inetOrgPerson
+uidNumber: 1078
+gidNumber: 1085
+loginShell: /bin/bash
+title: user.74
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.74
+cn: User 74
+displayName: User 74
+givenName: User
+sn: 74
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258527da-9eff-1037-87e7-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741862Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.75,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.75
+mail: user.75@example.com
+o: example
+initials: u.75
+structuralObjectClass: inetOrgPerson
+uidNumber: 1079
+gidNumber: 1086
+loginShell: /bin/bash
+title: user.75
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.75
+cn: User 75
+displayName: User 75
+givenName: User
+sn: 75
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25852aa0-9eff-1037-87e8-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741933Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.76,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.76
+mail: user.76@example.com
+o: example
+initials: u.76
+structuralObjectClass: inetOrgPerson
+uidNumber: 1080
+gidNumber: 1087
+loginShell: /bin/bash
+title: user.76
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.76
+cn: User 76
+displayName: User 76
+givenName: User
+sn: 76
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25852d0c-9eff-1037-87e9-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.741996Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.77,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.77
+mail: user.77@example.com
+o: example
+initials: u.77
+structuralObjectClass: inetOrgPerson
+uidNumber: 1081
+gidNumber: 1088
+loginShell: /bin/bash
+title: user.77
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.77
+cn: User 77
+displayName: User 77
+givenName: User
+sn: 77
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25852f64-9eff-1037-87ea-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742055Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.78,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.78
+mail: user.78@example.com
+o: example
+initials: u.78
+structuralObjectClass: inetOrgPerson
+uidNumber: 1082
+gidNumber: 1089
+loginShell: /bin/bash
+title: user.78
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.78
+cn: User 78
+displayName: User 78
+givenName: User
+sn: 78
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585323e-9eff-1037-87eb-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742128Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.79,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.79
+mail: user.79@example.com
+o: example
+initials: u.79
+structuralObjectClass: inetOrgPerson
+uidNumber: 1083
+gidNumber: 1090
+loginShell: /bin/bash
+title: user.79
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.79
+cn: User 79
+displayName: User 79
+givenName: User
+sn: 79
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25853496-9eff-1037-87ec-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742189Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.80,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.80
+mail: user.80@example.com
+o: example
+initials: u.80
+structuralObjectClass: inetOrgPerson
+uidNumber: 1084
+gidNumber: 1091
+loginShell: /bin/bash
+title: user.80
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.80
+cn: User 80
+displayName: User 80
+givenName: User
+sn: 80
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25853810-9eff-1037-87ed-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742277Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.81,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.81
+mail: user.81@example.com
+o: example
+initials: u.81
+structuralObjectClass: inetOrgPerson
+uidNumber: 1085
+gidNumber: 1092
+loginShell: /bin/bash
+title: user.81
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.81
+cn: User 81
+displayName: User 81
+givenName: User
+sn: 81
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25853b1c-9eff-1037-87ee-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742355Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.82,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.82
+mail: user.82@example.com
+o: example
+initials: u.82
+structuralObjectClass: inetOrgPerson
+uidNumber: 1086
+gidNumber: 1093
+loginShell: /bin/bash
+title: user.82
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.82
+cn: User 82
+displayName: User 82
+givenName: User
+sn: 82
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25853da6-9eff-1037-87ef-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742420Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.83,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.83
+mail: user.83@example.com
+o: example
+initials: u.83
+structuralObjectClass: inetOrgPerson
+uidNumber: 1087
+gidNumber: 1094
+loginShell: /bin/bash
+title: user.83
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.83
+cn: User 83
+displayName: User 83
+givenName: User
+sn: 83
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25854008-9eff-1037-87f0-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742481Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.84,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.84
+mail: user.84@example.com
+o: example
+initials: u.84
+structuralObjectClass: inetOrgPerson
+uidNumber: 1088
+gidNumber: 1095
+loginShell: /bin/bash
+title: user.84
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.84
+cn: User 84
+displayName: User 84
+givenName: User
+sn: 84
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258542ce-9eff-1037-87f1-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742553Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.85,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.85
+mail: user.85@example.com
+o: example
+initials: u.85
+structuralObjectClass: inetOrgPerson
+uidNumber: 1089
+gidNumber: 1096
+loginShell: /bin/bash
+title: user.85
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.85
+cn: User 85
+displayName: User 85
+givenName: User
+sn: 85
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25854530-9eff-1037-87f2-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742614Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.86,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.86
+mail: user.86@example.com
+o: example
+initials: u.86
+structuralObjectClass: inetOrgPerson
+uidNumber: 1090
+gidNumber: 1097
+loginShell: /bin/bash
+title: user.86
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.86
+cn: User 86
+displayName: User 86
+givenName: User
+sn: 86
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585481e-9eff-1037-87f3-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742688Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.87,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.87
+mail: user.87@example.com
+o: example
+initials: u.87
+structuralObjectClass: inetOrgPerson
+uidNumber: 1091
+gidNumber: 1098
+loginShell: /bin/bash
+title: user.87
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.87
+cn: User 87
+displayName: User 87
+givenName: User
+sn: 87
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25854aee-9eff-1037-87f4-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742761Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.88,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.88
+mail: user.88@example.com
+o: example
+initials: u.88
+structuralObjectClass: inetOrgPerson
+uidNumber: 1092
+gidNumber: 1099
+loginShell: /bin/bash
+title: user.88
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.88
+cn: User 88
+displayName: User 88
+givenName: User
+sn: 88
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25854db4-9eff-1037-87f5-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742831Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.89,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.89
+mail: user.89@example.com
+o: example
+initials: u.89
+structuralObjectClass: inetOrgPerson
+uidNumber: 1093
+gidNumber: 1100
+loginShell: /bin/bash
+title: user.89
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.89
+cn: User 89
+displayName: User 89
+givenName: User
+sn: 89
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585500c-9eff-1037-87f6-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742891Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.90,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.90
+mail: user.90@example.com
+o: example
+initials: u.90
+structuralObjectClass: inetOrgPerson
+uidNumber: 1094
+gidNumber: 1101
+loginShell: /bin/bash
+title: user.90
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.90
+cn: User 90
+displayName: User 90
+givenName: User
+sn: 90
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258552f0-9eff-1037-87f7-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.742965Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.91,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.91
+mail: user.91@example.com
+o: example
+initials: u.91
+structuralObjectClass: inetOrgPerson
+uidNumber: 1095
+gidNumber: 1102
+loginShell: /bin/bash
+title: user.91
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.91
+cn: User 91
+displayName: User 91
+givenName: User
+sn: 91
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25855552-9eff-1037-87f8-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743027Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.92,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.92
+mail: user.92@example.com
+o: example
+initials: u.92
+structuralObjectClass: inetOrgPerson
+uidNumber: 1096
+gidNumber: 1103
+loginShell: /bin/bash
+title: user.92
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.92
+cn: User 92
+displayName: User 92
+givenName: User
+sn: 92
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258557b4-9eff-1037-87f9-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743087Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.93,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.93
+mail: user.93@example.com
+o: example
+initials: u.93
+structuralObjectClass: inetOrgPerson
+uidNumber: 1097
+gidNumber: 1104
+loginShell: /bin/bash
+title: user.93
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.93
+cn: User 93
+displayName: User 93
+givenName: User
+sn: 93
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25855a70-9eff-1037-87fa-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743157Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.94,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.94
+mail: user.94@example.com
+o: example
+initials: u.94
+structuralObjectClass: inetOrgPerson
+uidNumber: 1098
+gidNumber: 1105
+loginShell: /bin/bash
+title: user.94
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.94
+cn: User 94
+displayName: User 94
+givenName: User
+sn: 94
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25855d18-9eff-1037-87fb-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743225Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.95,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.95
+mail: user.95@example.com
+o: example
+initials: u.95
+structuralObjectClass: inetOrgPerson
+uidNumber: 1099
+gidNumber: 1106
+loginShell: /bin/bash
+title: user.95
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.95
+cn: User 95
+displayName: User 95
+givenName: User
+sn: 95
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25855f84-9eff-1037-87fc-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743287Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.96,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.96
+mail: user.96@example.com
+o: example
+initials: u.96
+structuralObjectClass: inetOrgPerson
+uidNumber: 1100
+gidNumber: 1107
+loginShell: /bin/bash
+title: user.96
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.96
+cn: User 96
+displayName: User 96
+givenName: User
+sn: 96
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25856308-9eff-1037-87fd-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743377Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.97,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.97
+mail: user.97@example.com
+o: example
+initials: u.97
+structuralObjectClass: inetOrgPerson
+uidNumber: 1101
+gidNumber: 1108
+loginShell: /bin/bash
+title: user.97
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.97
+cn: User 97
+displayName: User 97
+givenName: User
+sn: 97
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258565ec-9eff-1037-87fe-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743451Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.98,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.98
+mail: user.98@example.com
+o: example
+initials: u.98
+structuralObjectClass: inetOrgPerson
+uidNumber: 1102
+gidNumber: 1109
+loginShell: /bin/bash
+title: user.98
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.98
+cn: User 98
+displayName: User 98
+givenName: User
+sn: 98
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25856862-9eff-1037-87ff-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743515Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.99,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.99
+mail: user.99@example.com
+o: example
+initials: u.99
+structuralObjectClass: inetOrgPerson
+uidNumber: 1103
+gidNumber: 1110
+loginShell: /bin/bash
+title: user.99
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.99
+cn: User 99
+displayName: User 99
+givenName: User
+sn: 99
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25856b46-9eff-1037-8800-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743588Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.100,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.100
+mail: user.100@example.com
+o: example
+initials: u.100
+structuralObjectClass: inetOrgPerson
+uidNumber: 1104
+gidNumber: 1111
+loginShell: /bin/bash
+title: user.100
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.100
+cn: User 100
+displayName: User 100
+givenName: User
+sn: 100
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25856f6a-9eff-1037-8801-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743692Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.101,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.101
+mail: user.101@example.com
+o: example
+initials: u.101
+structuralObjectClass: inetOrgPerson
+uidNumber: 1105
+gidNumber: 1112
+loginShell: /bin/bash
+title: user.101
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.101
+cn: User 101
+displayName: User 101
+givenName: User
+sn: 101
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585723a-9eff-1037-8802-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743767Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.102,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.102
+mail: user.102@example.com
+o: example
+initials: u.102
+structuralObjectClass: inetOrgPerson
+uidNumber: 1106
+gidNumber: 1113
+loginShell: /bin/bash
+title: user.102
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.102
+cn: User 102
+displayName: User 102
+givenName: User
+sn: 102
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25857550-9eff-1037-8803-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743845Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.103,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.103
+mail: user.103@example.com
+o: example
+initials: u.103
+structuralObjectClass: inetOrgPerson
+uidNumber: 1107
+gidNumber: 1114
+loginShell: /bin/bash
+title: user.103
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.103
+cn: User 103
+displayName: User 103
+givenName: User
+sn: 103
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25857816-9eff-1037-8804-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743916Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.104,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.104
+mail: user.104@example.com
+o: example
+initials: u.104
+structuralObjectClass: inetOrgPerson
+uidNumber: 1108
+gidNumber: 1115
+loginShell: /bin/bash
+title: user.104
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.104
+cn: User 104
+displayName: User 104
+givenName: User
+sn: 104
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25857a96-9eff-1037-8805-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.743980Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.105,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.105
+mail: user.105@example.com
+o: example
+initials: u.105
+structuralObjectClass: inetOrgPerson
+uidNumber: 1109
+gidNumber: 1116
+loginShell: /bin/bash
+title: user.105
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.105
+cn: User 105
+displayName: User 105
+givenName: User
+sn: 105
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25857d66-9eff-1037-8806-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744052Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.106,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.106
+mail: user.106@example.com
+o: example
+initials: u.106
+structuralObjectClass: inetOrgPerson
+uidNumber: 1110
+gidNumber: 1117
+loginShell: /bin/bash
+title: user.106
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.106
+cn: User 106
+displayName: User 106
+givenName: User
+sn: 106
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25857ff0-9eff-1037-8807-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744118Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.107,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.107
+mail: user.107@example.com
+o: example
+initials: u.107
+structuralObjectClass: inetOrgPerson
+uidNumber: 1111
+gidNumber: 1118
+loginShell: /bin/bash
+title: user.107
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.107
+cn: User 107
+displayName: User 107
+givenName: User
+sn: 107
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585825c-9eff-1037-8808-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744179Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.108,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.108
+mail: user.108@example.com
+o: example
+initials: u.108
+structuralObjectClass: inetOrgPerson
+uidNumber: 1112
+gidNumber: 1119
+loginShell: /bin/bash
+title: user.108
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.108
+cn: User 108
+displayName: User 108
+givenName: User
+sn: 108
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585852c-9eff-1037-8809-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744251Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.109,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.109
+mail: user.109@example.com
+o: example
+initials: u.109
+structuralObjectClass: inetOrgPerson
+uidNumber: 1113
+gidNumber: 1120
+loginShell: /bin/bash
+title: user.109
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.109
+cn: User 109
+displayName: User 109
+givenName: User
+sn: 109
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25858798-9eff-1037-880a-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744313Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.110,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.110
+mail: user.110@example.com
+o: example
+initials: u.110
+structuralObjectClass: inetOrgPerson
+uidNumber: 1114
+gidNumber: 1121
+loginShell: /bin/bash
+title: user.110
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.110
+cn: User 110
+displayName: User 110
+givenName: User
+sn: 110
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25858a18-9eff-1037-880b-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744377Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.111,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.111
+mail: user.111@example.com
+o: example
+initials: u.111
+structuralObjectClass: inetOrgPerson
+uidNumber: 1115
+gidNumber: 1122
+loginShell: /bin/bash
+title: user.111
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.111
+cn: User 111
+displayName: User 111
+givenName: User
+sn: 111
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25858d1a-9eff-1037-880c-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744455Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.112,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.112
+mail: user.112@example.com
+o: example
+initials: u.112
+structuralObjectClass: inetOrgPerson
+uidNumber: 1116
+gidNumber: 1123
+loginShell: /bin/bash
+title: user.112
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.112
+cn: User 112
+displayName: User 112
+givenName: User
+sn: 112
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25858fa4-9eff-1037-880d-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744519Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.113,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.113
+mail: user.113@example.com
+o: example
+initials: u.113
+structuralObjectClass: inetOrgPerson
+uidNumber: 1117
+gidNumber: 1124
+loginShell: /bin/bash
+title: user.113
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.113
+cn: User 113
+displayName: User 113
+givenName: User
+sn: 113
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25859206-9eff-1037-880e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744581Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.114,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.114
+mail: user.114@example.com
+o: example
+initials: u.114
+structuralObjectClass: inetOrgPerson
+uidNumber: 1118
+gidNumber: 1125
+loginShell: /bin/bash
+title: user.114
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.114
+cn: User 114
+displayName: User 114
+givenName: User
+sn: 114
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25859558-9eff-1037-880f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744664Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.115,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.115
+mail: user.115@example.com
+o: example
+initials: u.115
+structuralObjectClass: inetOrgPerson
+uidNumber: 1119
+gidNumber: 1126
+loginShell: /bin/bash
+title: user.115
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.115
+cn: User 115
+displayName: User 115
+givenName: User
+sn: 115
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258597e2-9eff-1037-8810-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744730Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.116,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.116
+mail: user.116@example.com
+o: example
+initials: u.116
+structuralObjectClass: inetOrgPerson
+uidNumber: 1120
+gidNumber: 1127
+loginShell: /bin/bash
+title: user.116
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.116
+cn: User 116
+displayName: User 116
+givenName: User
+sn: 116
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25859a4e-9eff-1037-8811-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744792Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.117,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.117
+mail: user.117@example.com
+o: example
+initials: u.117
+structuralObjectClass: inetOrgPerson
+uidNumber: 1121
+gidNumber: 1128
+loginShell: /bin/bash
+title: user.117
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.117
+cn: User 117
+displayName: User 117
+givenName: User
+sn: 117
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25859d1e-9eff-1037-8812-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744864Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.118,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.118
+mail: user.118@example.com
+o: example
+initials: u.118
+structuralObjectClass: inetOrgPerson
+uidNumber: 1122
+gidNumber: 1129
+loginShell: /bin/bash
+title: user.118
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.118
+cn: User 118
+displayName: User 118
+givenName: User
+sn: 118
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25859ff8-9eff-1037-8813-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.744937Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.119,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.119
+mail: user.119@example.com
+o: example
+initials: u.119
+structuralObjectClass: inetOrgPerson
+uidNumber: 1123
+gidNumber: 1130
+loginShell: /bin/bash
+title: user.119
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.119
+cn: User 119
+displayName: User 119
+givenName: User
+sn: 119
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585a264-9eff-1037-8814-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745000Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.120,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.120
+mail: user.120@example.com
+o: example
+initials: u.120
+structuralObjectClass: inetOrgPerson
+uidNumber: 1124
+gidNumber: 1131
+loginShell: /bin/bash
+title: user.120
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.120
+cn: User 120
+displayName: User 120
+givenName: User
+sn: 120
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585a53e-9eff-1037-8815-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745072Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.121,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.121
+mail: user.121@example.com
+o: example
+initials: u.121
+structuralObjectClass: inetOrgPerson
+uidNumber: 1125
+gidNumber: 1132
+loginShell: /bin/bash
+title: user.121
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.121
+cn: User 121
+displayName: User 121
+givenName: User
+sn: 121
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585a7aa-9eff-1037-8816-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745134Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.122,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.122
+mail: user.122@example.com
+o: example
+initials: u.122
+structuralObjectClass: inetOrgPerson
+uidNumber: 1126
+gidNumber: 1133
+loginShell: /bin/bash
+title: user.122
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.122
+cn: User 122
+displayName: User 122
+givenName: User
+sn: 122
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585aa2a-9eff-1037-8817-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745198Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.123,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.123
+mail: user.123@example.com
+o: example
+initials: u.123
+structuralObjectClass: inetOrgPerson
+uidNumber: 1127
+gidNumber: 1134
+loginShell: /bin/bash
+title: user.123
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.123
+cn: User 123
+displayName: User 123
+givenName: User
+sn: 123
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585acf0-9eff-1037-8818-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745270Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.124,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.124
+mail: user.124@example.com
+o: example
+initials: u.124
+structuralObjectClass: inetOrgPerson
+uidNumber: 1128
+gidNumber: 1135
+loginShell: /bin/bash
+title: user.124
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.124
+cn: User 124
+displayName: User 124
+givenName: User
+sn: 124
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585af66-9eff-1037-8819-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745333Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.125,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.125
+mail: user.125@example.com
+o: example
+initials: u.125
+structuralObjectClass: inetOrgPerson
+uidNumber: 1129
+gidNumber: 1136
+loginShell: /bin/bash
+title: user.125
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.125
+cn: User 125
+displayName: User 125
+givenName: User
+sn: 125
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585b222-9eff-1037-881a-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745402Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.126,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.126
+mail: user.126@example.com
+o: example
+initials: u.126
+structuralObjectClass: inetOrgPerson
+uidNumber: 1130
+gidNumber: 1137
+loginShell: /bin/bash
+title: user.126
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.126
+cn: User 126
+displayName: User 126
+givenName: User
+sn: 126
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585b506-9eff-1037-881b-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745476Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.127,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.127
+mail: user.127@example.com
+o: example
+initials: u.127
+structuralObjectClass: inetOrgPerson
+uidNumber: 1131
+gidNumber: 1138
+loginShell: /bin/bash
+title: user.127
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.127
+cn: User 127
+displayName: User 127
+givenName: User
+sn: 127
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585b772-9eff-1037-881c-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745539Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.128,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.128
+mail: user.128@example.com
+o: example
+initials: u.128
+structuralObjectClass: inetOrgPerson
+uidNumber: 1132
+gidNumber: 1139
+loginShell: /bin/bash
+title: user.128
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.128
+cn: User 128
+displayName: User 128
+givenName: User
+sn: 128
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585b9e8-9eff-1037-881d-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745601Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.129,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.129
+mail: user.129@example.com
+o: example
+initials: u.129
+structuralObjectClass: inetOrgPerson
+uidNumber: 1133
+gidNumber: 1140
+loginShell: /bin/bash
+title: user.129
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.129
+cn: User 129
+displayName: User 129
+givenName: User
+sn: 129
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585bd12-9eff-1037-881e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745682Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.130,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.130
+mail: user.130@example.com
+o: example
+initials: u.130
+structuralObjectClass: inetOrgPerson
+uidNumber: 1134
+gidNumber: 1141
+loginShell: /bin/bash
+title: user.130
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.130
+cn: User 130
+displayName: User 130
+givenName: User
+sn: 130
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585bfba-9eff-1037-881f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745750Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.131,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.131
+mail: user.131@example.com
+o: example
+initials: u.131
+structuralObjectClass: inetOrgPerson
+uidNumber: 1135
+gidNumber: 1142
+loginShell: /bin/bash
+title: user.131
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.131
+cn: User 131
+displayName: User 131
+givenName: User
+sn: 131
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585c21c-9eff-1037-8820-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745811Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.132,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.132
+mail: user.132@example.com
+o: example
+initials: u.132
+structuralObjectClass: inetOrgPerson
+uidNumber: 1136
+gidNumber: 1143
+loginShell: /bin/bash
+title: user.132
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.132
+cn: User 132
+displayName: User 132
+givenName: User
+sn: 132
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585c4f6-9eff-1037-8821-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745884Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.133,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.133
+mail: user.133@example.com
+o: example
+initials: u.133
+structuralObjectClass: inetOrgPerson
+uidNumber: 1137
+gidNumber: 1144
+loginShell: /bin/bash
+title: user.133
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.133
+cn: User 133
+displayName: User 133
+givenName: User
+sn: 133
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585c7b2-9eff-1037-8822-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.745954Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.134,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.134
+mail: user.134@example.com
+o: example
+initials: u.134
+structuralObjectClass: inetOrgPerson
+uidNumber: 1138
+gidNumber: 1145
+loginShell: /bin/bash
+title: user.134
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.134
+cn: User 134
+displayName: User 134
+givenName: User
+sn: 134
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585ca32-9eff-1037-8823-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746019Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.135,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.135
+mail: user.135@example.com
+o: example
+initials: u.135
+structuralObjectClass: inetOrgPerson
+uidNumber: 1139
+gidNumber: 1146
+loginShell: /bin/bash
+title: user.135
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.135
+cn: User 135
+displayName: User 135
+givenName: User
+sn: 135
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585cd02-9eff-1037-8824-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746090Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.136,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.136
+mail: user.136@example.com
+o: example
+initials: u.136
+structuralObjectClass: inetOrgPerson
+uidNumber: 1140
+gidNumber: 1147
+loginShell: /bin/bash
+title: user.136
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.136
+cn: User 136
+displayName: User 136
+givenName: User
+sn: 136
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585cf78-9eff-1037-8825-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746154Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.137,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.137
+mail: user.137@example.com
+o: example
+initials: u.137
+structuralObjectClass: inetOrgPerson
+uidNumber: 1141
+gidNumber: 1148
+loginShell: /bin/bash
+title: user.137
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.137
+cn: User 137
+displayName: User 137
+givenName: User
+sn: 137
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585d1da-9eff-1037-8826-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746214Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.138,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.138
+mail: user.138@example.com
+o: example
+initials: u.138
+structuralObjectClass: inetOrgPerson
+uidNumber: 1142
+gidNumber: 1149
+loginShell: /bin/bash
+title: user.138
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.138
+cn: User 138
+displayName: User 138
+givenName: User
+sn: 138
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585d4be-9eff-1037-8827-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746288Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.139,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.139
+mail: user.139@example.com
+o: example
+initials: u.139
+structuralObjectClass: inetOrgPerson
+uidNumber: 1143
+gidNumber: 1150
+loginShell: /bin/bash
+title: user.139
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.139
+cn: User 139
+displayName: User 139
+givenName: User
+sn: 139
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585d72a-9eff-1037-8828-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746350Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.140,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.140
+mail: user.140@example.com
+o: example
+initials: u.140
+structuralObjectClass: inetOrgPerson
+uidNumber: 1144
+gidNumber: 1151
+loginShell: /bin/bash
+title: user.140
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.140
+cn: User 140
+displayName: User 140
+givenName: User
+sn: 140
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585d9e6-9eff-1037-8829-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746420Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.141,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.141
+mail: user.141@example.com
+o: example
+initials: u.141
+structuralObjectClass: inetOrgPerson
+uidNumber: 1145
+gidNumber: 1152
+loginShell: /bin/bash
+title: user.141
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.141
+cn: User 141
+displayName: User 141
+givenName: User
+sn: 141
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585dcc0-9eff-1037-882a-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746494Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.142,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.142
+mail: user.142@example.com
+o: example
+initials: u.142
+structuralObjectClass: inetOrgPerson
+uidNumber: 1146
+gidNumber: 1153
+loginShell: /bin/bash
+title: user.142
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.142
+cn: User 142
+displayName: User 142
+givenName: User
+sn: 142
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585df54-9eff-1037-882b-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746560Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.143,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.143
+mail: user.143@example.com
+o: example
+initials: u.143
+structuralObjectClass: inetOrgPerson
+uidNumber: 1147
+gidNumber: 1154
+loginShell: /bin/bash
+title: user.143
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.143
+cn: User 143
+displayName: User 143
+givenName: User
+sn: 143
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585e1c0-9eff-1037-882c-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746622Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.144,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.144
+mail: user.144@example.com
+o: example
+initials: u.144
+structuralObjectClass: inetOrgPerson
+uidNumber: 1148
+gidNumber: 1155
+loginShell: /bin/bash
+title: user.144
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.144
+cn: User 144
+displayName: User 144
+givenName: User
+sn: 144
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585e512-9eff-1037-882d-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746706Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.145,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.145
+mail: user.145@example.com
+o: example
+initials: u.145
+structuralObjectClass: inetOrgPerson
+uidNumber: 1149
+gidNumber: 1156
+loginShell: /bin/bash
+title: user.145
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.145
+cn: User 145
+displayName: User 145
+givenName: User
+sn: 145
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585e792-9eff-1037-882e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746770Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.146,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.146
+mail: user.146@example.com
+o: example
+initials: u.146
+structuralObjectClass: inetOrgPerson
+uidNumber: 1150
+gidNumber: 1157
+loginShell: /bin/bash
+title: user.146
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.146
+cn: User 146
+displayName: User 146
+givenName: User
+sn: 146
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585ea1c-9eff-1037-882f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746835Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.147,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.147
+mail: user.147@example.com
+o: example
+initials: u.147
+structuralObjectClass: inetOrgPerson
+uidNumber: 1151
+gidNumber: 1158
+loginShell: /bin/bash
+title: user.147
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.147
+cn: User 147
+displayName: User 147
+givenName: User
+sn: 147
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585ed32-9eff-1037-8830-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746914Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.148,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.148
+mail: user.148@example.com
+o: example
+initials: u.148
+structuralObjectClass: inetOrgPerson
+uidNumber: 1152
+gidNumber: 1159
+loginShell: /bin/bash
+title: user.148
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.148
+cn: User 148
+displayName: User 148
+givenName: User
+sn: 148
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585efbc-9eff-1037-8831-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.746979Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.149,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.149
+mail: user.149@example.com
+o: example
+initials: u.149
+structuralObjectClass: inetOrgPerson
+uidNumber: 1153
+gidNumber: 1160
+loginShell: /bin/bash
+title: user.149
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.149
+cn: User 149
+displayName: User 149
+givenName: User
+sn: 149
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585f21e-9eff-1037-8832-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747041Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.150,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.150
+mail: user.150@example.com
+o: example
+initials: u.150
+structuralObjectClass: inetOrgPerson
+uidNumber: 1154
+gidNumber: 1161
+loginShell: /bin/bash
+title: user.150
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.150
+cn: User 150
+displayName: User 150
+givenName: User
+sn: 150
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585f516-9eff-1037-8833-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747116Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.151,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.151
+mail: user.151@example.com
+o: example
+initials: u.151
+structuralObjectClass: inetOrgPerson
+uidNumber: 1155
+gidNumber: 1162
+loginShell: /bin/bash
+title: user.151
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.151
+cn: User 151
+displayName: User 151
+givenName: User
+sn: 151
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585f782-9eff-1037-8834-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747178Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.152,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.152
+mail: user.152@example.com
+o: example
+initials: u.152
+structuralObjectClass: inetOrgPerson
+uidNumber: 1156
+gidNumber: 1163
+loginShell: /bin/bash
+title: user.152
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.152
+cn: User 152
+displayName: User 152
+givenName: User
+sn: 152
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585fa20-9eff-1037-8835-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747245Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.153,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.153
+mail: user.153@example.com
+o: example
+initials: u.153
+structuralObjectClass: inetOrgPerson
+uidNumber: 1157
+gidNumber: 1164
+loginShell: /bin/bash
+title: user.153
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.153
+cn: User 153
+displayName: User 153
+givenName: User
+sn: 153
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585fcfa-9eff-1037-8836-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747318Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.154,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.154
+mail: user.154@example.com
+o: example
+initials: u.154
+structuralObjectClass: inetOrgPerson
+uidNumber: 1158
+gidNumber: 1165
+loginShell: /bin/bash
+title: user.154
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.154
+cn: User 154
+displayName: User 154
+givenName: User
+sn: 154
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2585ff84-9eff-1037-8837-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747383Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.155,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.155
+mail: user.155@example.com
+o: example
+initials: u.155
+structuralObjectClass: inetOrgPerson
+uidNumber: 1159
+gidNumber: 1166
+loginShell: /bin/bash
+title: user.155
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.155
+cn: User 155
+displayName: User 155
+givenName: User
+sn: 155
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586059c-9eff-1037-8838-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747538Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.156,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.156
+mail: user.156@example.com
+o: example
+initials: u.156
+structuralObjectClass: inetOrgPerson
+uidNumber: 1160
+gidNumber: 1167
+loginShell: /bin/bash
+title: user.156
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.156
+cn: User 156
+displayName: User 156
+givenName: User
+sn: 156
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258608da-9eff-1037-8839-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747622Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.157,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.157
+mail: user.157@example.com
+o: example
+initials: u.157
+structuralObjectClass: inetOrgPerson
+uidNumber: 1161
+gidNumber: 1168
+loginShell: /bin/bash
+title: user.157
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.157
+cn: User 157
+displayName: User 157
+givenName: User
+sn: 157
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25860bd2-9eff-1037-883a-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747697Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.158,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.158
+mail: user.158@example.com
+o: example
+initials: u.158
+structuralObjectClass: inetOrgPerson
+uidNumber: 1162
+gidNumber: 1169
+loginShell: /bin/bash
+title: user.158
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.158
+cn: User 158
+displayName: User 158
+givenName: User
+sn: 158
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25860e66-9eff-1037-883b-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747764Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.159,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.159
+mail: user.159@example.com
+o: example
+initials: u.159
+structuralObjectClass: inetOrgPerson
+uidNumber: 1163
+gidNumber: 1170
+loginShell: /bin/bash
+title: user.159
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.159
+cn: User 159
+displayName: User 159
+givenName: User
+sn: 159
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586114a-9eff-1037-883c-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747838Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.160,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.160
+mail: user.160@example.com
+o: example
+initials: u.160
+structuralObjectClass: inetOrgPerson
+uidNumber: 1164
+gidNumber: 1171
+loginShell: /bin/bash
+title: user.160
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.160
+cn: User 160
+displayName: User 160
+givenName: User
+sn: 160
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258613ca-9eff-1037-883d-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747902Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.161,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.161
+mail: user.161@example.com
+o: example
+initials: u.161
+structuralObjectClass: inetOrgPerson
+uidNumber: 1165
+gidNumber: 1172
+loginShell: /bin/bash
+title: user.161
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.161
+cn: User 161
+displayName: User 161
+givenName: User
+sn: 161
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586162c-9eff-1037-883e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.747964Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.162,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.162
+mail: user.162@example.com
+o: example
+initials: u.162
+structuralObjectClass: inetOrgPerson
+uidNumber: 1166
+gidNumber: 1173
+loginShell: /bin/bash
+title: user.162
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.162
+cn: User 162
+displayName: User 162
+givenName: User
+sn: 162
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25861960-9eff-1037-883f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748045Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.163,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.163
+mail: user.163@example.com
+o: example
+initials: u.163
+structuralObjectClass: inetOrgPerson
+uidNumber: 1167
+gidNumber: 1174
+loginShell: /bin/bash
+title: user.163
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.163
+cn: User 163
+displayName: User 163
+givenName: User
+sn: 163
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25861bd6-9eff-1037-8840-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748109Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.164,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.164
+mail: user.164@example.com
+o: example
+initials: u.164
+structuralObjectClass: inetOrgPerson
+uidNumber: 1168
+gidNumber: 1175
+loginShell: /bin/bash
+title: user.164
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.164
+cn: User 164
+displayName: User 164
+givenName: User
+sn: 164
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25861ece-9eff-1037-8841-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748184Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.165,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.165
+mail: user.165@example.com
+o: example
+initials: u.165
+structuralObjectClass: inetOrgPerson
+uidNumber: 1169
+gidNumber: 1176
+loginShell: /bin/bash
+title: user.165
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.165
+cn: User 165
+displayName: User 165
+givenName: User
+sn: 165
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586219e-9eff-1037-8842-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748256Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.166,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.166
+mail: user.166@example.com
+o: example
+initials: u.166
+structuralObjectClass: inetOrgPerson
+uidNumber: 1170
+gidNumber: 1177
+loginShell: /bin/bash
+title: user.166
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.166
+cn: User 166
+displayName: User 166
+givenName: User
+sn: 166
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25862428-9eff-1037-8843-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748321Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.167,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.167
+mail: user.167@example.com
+o: example
+initials: u.167
+structuralObjectClass: inetOrgPerson
+uidNumber: 1171
+gidNumber: 1178
+loginShell: /bin/bash
+title: user.167
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.167
+cn: User 167
+displayName: User 167
+givenName: User
+sn: 167
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586268a-9eff-1037-8844-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748383Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.168,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.168
+mail: user.168@example.com
+o: example
+initials: u.168
+structuralObjectClass: inetOrgPerson
+uidNumber: 1172
+gidNumber: 1179
+loginShell: /bin/bash
+title: user.168
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.168
+cn: User 168
+displayName: User 168
+givenName: User
+sn: 168
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586295a-9eff-1037-8845-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748455Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.169,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.169
+mail: user.169@example.com
+o: example
+initials: u.169
+structuralObjectClass: inetOrgPerson
+uidNumber: 1173
+gidNumber: 1180
+loginShell: /bin/bash
+title: user.169
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.169
+cn: User 169
+displayName: User 169
+givenName: User
+sn: 169
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25862c16-9eff-1037-8846-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748524Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.170,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.170
+mail: user.170@example.com
+o: example
+initials: u.170
+structuralObjectClass: inetOrgPerson
+uidNumber: 1174
+gidNumber: 1181
+loginShell: /bin/bash
+title: user.170
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.170
+cn: User 170
+displayName: User 170
+givenName: User
+sn: 170
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25862f5e-9eff-1037-8847-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748608Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.171,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.171
+mail: user.171@example.com
+o: example
+initials: u.171
+structuralObjectClass: inetOrgPerson
+uidNumber: 1175
+gidNumber: 1182
+loginShell: /bin/bash
+title: user.171
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.171
+cn: User 171
+displayName: User 171
+givenName: User
+sn: 171
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25863288-9eff-1037-8848-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748689Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.172,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.172
+mail: user.172@example.com
+o: example
+initials: u.172
+structuralObjectClass: inetOrgPerson
+uidNumber: 1176
+gidNumber: 1183
+loginShell: /bin/bash
+title: user.172
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.172
+cn: User 172
+displayName: User 172
+givenName: User
+sn: 172
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25863580-9eff-1037-8849-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748765Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.173,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.173
+mail: user.173@example.com
+o: example
+initials: u.173
+structuralObjectClass: inetOrgPerson
+uidNumber: 1177
+gidNumber: 1184
+loginShell: /bin/bash
+title: user.173
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.173
+cn: User 173
+displayName: User 173
+givenName: User
+sn: 173
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258637d8-9eff-1037-884a-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748826Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.174,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.174
+mail: user.174@example.com
+o: example
+initials: u.174
+structuralObjectClass: inetOrgPerson
+uidNumber: 1178
+gidNumber: 1185
+loginShell: /bin/bash
+title: user.174
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.174
+cn: User 174
+displayName: User 174
+givenName: User
+sn: 174
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25863ad0-9eff-1037-884b-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748901Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.175,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.175
+mail: user.175@example.com
+o: example
+initials: u.175
+structuralObjectClass: inetOrgPerson
+uidNumber: 1179
+gidNumber: 1186
+loginShell: /bin/bash
+title: user.175
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.175
+cn: User 175
+displayName: User 175
+givenName: User
+sn: 175
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25863d3c-9eff-1037-884c-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.748963Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.176,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.176
+mail: user.176@example.com
+o: example
+initials: u.176
+structuralObjectClass: inetOrgPerson
+uidNumber: 1180
+gidNumber: 1187
+loginShell: /bin/bash
+title: user.176
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.176
+cn: User 176
+displayName: User 176
+givenName: User
+sn: 176
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25863fa8-9eff-1037-884d-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749025Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.177,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.177
+mail: user.177@example.com
+o: example
+initials: u.177
+structuralObjectClass: inetOrgPerson
+uidNumber: 1181
+gidNumber: 1188
+loginShell: /bin/bash
+title: user.177
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.177
+cn: User 177
+displayName: User 177
+givenName: User
+sn: 177
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258642a0-9eff-1037-884e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749101Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.178,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.178
+mail: user.178@example.com
+o: example
+initials: u.178
+structuralObjectClass: inetOrgPerson
+uidNumber: 1182
+gidNumber: 1189
+loginShell: /bin/bash
+title: user.178
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.178
+cn: User 178
+displayName: User 178
+givenName: User
+sn: 178
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586452a-9eff-1037-884f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749166Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.179,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.179
+mail: user.179@example.com
+o: example
+initials: u.179
+structuralObjectClass: inetOrgPerson
+uidNumber: 1183
+gidNumber: 1190
+loginShell: /bin/bash
+title: user.179
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.179
+cn: User 179
+displayName: User 179
+givenName: User
+sn: 179
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25864782-9eff-1037-8850-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749227Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.180,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.180
+mail: user.180@example.com
+o: example
+initials: u.180
+structuralObjectClass: inetOrgPerson
+uidNumber: 1184
+gidNumber: 1191
+loginShell: /bin/bash
+title: user.180
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.180
+cn: User 180
+displayName: User 180
+givenName: User
+sn: 180
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25864a52-9eff-1037-8851-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749298Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.181,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.181
+mail: user.181@example.com
+o: example
+initials: u.181
+structuralObjectClass: inetOrgPerson
+uidNumber: 1185
+gidNumber: 1192
+loginShell: /bin/bash
+title: user.181
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.181
+cn: User 181
+displayName: User 181
+givenName: User
+sn: 181
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25864cbe-9eff-1037-8852-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749360Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.182,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.182
+mail: user.182@example.com
+o: example
+initials: u.182
+structuralObjectClass: inetOrgPerson
+uidNumber: 1186
+gidNumber: 1193
+loginShell: /bin/bash
+title: user.182
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.182
+cn: User 182
+displayName: User 182
+givenName: User
+sn: 182
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25864f3e-9eff-1037-8853-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749424Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.183,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.183
+mail: user.183@example.com
+o: example
+initials: u.183
+structuralObjectClass: inetOrgPerson
+uidNumber: 1187
+gidNumber: 1194
+loginShell: /bin/bash
+title: user.183
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.183
+cn: User 183
+displayName: User 183
+givenName: User
+sn: 183
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25865204-9eff-1037-8854-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749495Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.184,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.184
+mail: user.184@example.com
+o: example
+initials: u.184
+structuralObjectClass: inetOrgPerson
+uidNumber: 1188
+gidNumber: 1195
+loginShell: /bin/bash
+title: user.184
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.184
+cn: User 184
+displayName: User 184
+givenName: User
+sn: 184
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258654c0-9eff-1037-8855-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749566Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.185,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.185
+mail: user.185@example.com
+o: example
+initials: u.185
+structuralObjectClass: inetOrgPerson
+uidNumber: 1189
+gidNumber: 1196
+loginShell: /bin/bash
+title: user.185
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.185
+cn: User 185
+displayName: User 185
+givenName: User
+sn: 185
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586572c-9eff-1037-8856-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749628Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.186,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.186
+mail: user.186@example.com
+o: example
+initials: u.186
+structuralObjectClass: inetOrgPerson
+uidNumber: 1190
+gidNumber: 1197
+loginShell: /bin/bash
+title: user.186
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.186
+cn: User 186
+displayName: User 186
+givenName: User
+sn: 186
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25865a88-9eff-1037-8857-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749713Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.187,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.187
+mail: user.187@example.com
+o: example
+initials: u.187
+structuralObjectClass: inetOrgPerson
+uidNumber: 1191
+gidNumber: 1198
+loginShell: /bin/bash
+title: user.187
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.187
+cn: User 187
+displayName: User 187
+givenName: User
+sn: 187
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25865df8-9eff-1037-8858-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749801Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.188,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.188
+mail: user.188@example.com
+o: example
+initials: u.188
+structuralObjectClass: inetOrgPerson
+uidNumber: 1192
+gidNumber: 1199
+loginShell: /bin/bash
+title: user.188
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.188
+cn: User 188
+displayName: User 188
+givenName: User
+sn: 188
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586606e-9eff-1037-8859-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749865Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.189,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.189
+mail: user.189@example.com
+o: example
+initials: u.189
+structuralObjectClass: inetOrgPerson
+uidNumber: 1193
+gidNumber: 1200
+loginShell: /bin/bash
+title: user.189
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.189
+cn: User 189
+displayName: User 189
+givenName: User
+sn: 189
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25866334-9eff-1037-885a-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.749935Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.190,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.190
+mail: user.190@example.com
+o: example
+initials: u.190
+structuralObjectClass: inetOrgPerson
+uidNumber: 1194
+gidNumber: 1201
+loginShell: /bin/bash
+title: user.190
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.190
+cn: User 190
+displayName: User 190
+givenName: User
+sn: 190
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258665b4-9eff-1037-885b-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750000Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.191,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.191
+mail: user.191@example.com
+o: example
+initials: u.191
+structuralObjectClass: inetOrgPerson
+uidNumber: 1195
+gidNumber: 1202
+loginShell: /bin/bash
+title: user.191
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.191
+cn: User 191
+displayName: User 191
+givenName: User
+sn: 191
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25866816-9eff-1037-885c-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750061Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.192,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.192
+mail: user.192@example.com
+o: example
+initials: u.192
+structuralObjectClass: inetOrgPerson
+uidNumber: 1196
+gidNumber: 1203
+loginShell: /bin/bash
+title: user.192
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.192
+cn: User 192
+displayName: User 192
+givenName: User
+sn: 192
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25866b22-9eff-1037-885d-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750138Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.193,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.193
+mail: user.193@example.com
+o: example
+initials: u.193
+structuralObjectClass: inetOrgPerson
+uidNumber: 1197
+gidNumber: 1204
+loginShell: /bin/bash
+title: user.193
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.193
+cn: User 193
+displayName: User 193
+givenName: User
+sn: 193
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25866d8e-9eff-1037-885e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750201Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.194,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.194
+mail: user.194@example.com
+o: example
+initials: u.194
+structuralObjectClass: inetOrgPerson
+uidNumber: 1198
+gidNumber: 1205
+loginShell: /bin/bash
+title: user.194
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.194
+cn: User 194
+displayName: User 194
+givenName: User
+sn: 194
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586700e-9eff-1037-885f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750264Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.195,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.195
+mail: user.195@example.com
+o: example
+initials: u.195
+structuralObjectClass: inetOrgPerson
+uidNumber: 1199
+gidNumber: 1206
+loginShell: /bin/bash
+title: user.195
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.195
+cn: User 195
+displayName: User 195
+givenName: User
+sn: 195
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258672d4-9eff-1037-8860-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750335Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.196,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.196
+mail: user.196@example.com
+o: example
+initials: u.196
+structuralObjectClass: inetOrgPerson
+uidNumber: 1200
+gidNumber: 1207
+loginShell: /bin/bash
+title: user.196
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.196
+cn: User 196
+displayName: User 196
+givenName: User
+sn: 196
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586754a-9eff-1037-8861-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750398Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.197,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.197
+mail: user.197@example.com
+o: example
+initials: u.197
+structuralObjectClass: inetOrgPerson
+uidNumber: 1201
+gidNumber: 1208
+loginShell: /bin/bash
+title: user.197
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.197
+cn: User 197
+displayName: User 197
+givenName: User
+sn: 197
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258677a2-9eff-1037-8862-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750459Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.198,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.198
+mail: user.198@example.com
+o: example
+initials: u.198
+structuralObjectClass: inetOrgPerson
+uidNumber: 1202
+gidNumber: 1209
+loginShell: /bin/bash
+title: user.198
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.198
+cn: User 198
+displayName: User 198
+givenName: User
+sn: 198
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25867a86-9eff-1037-8863-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750532Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.199,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.199
+mail: user.199@example.com
+o: example
+initials: u.199
+structuralObjectClass: inetOrgPerson
+uidNumber: 1203
+gidNumber: 1210
+loginShell: /bin/bash
+title: user.199
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.199
+cn: User 199
+displayName: User 199
+givenName: User
+sn: 199
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25867d2e-9eff-1037-8864-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750601Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.200,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.200
+mail: user.200@example.com
+o: example
+initials: u.200
+structuralObjectClass: inetOrgPerson
+uidNumber: 1204
+gidNumber: 1211
+loginShell: /bin/bash
+title: user.200
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.200
+cn: User 200
+displayName: User 200
+givenName: User
+sn: 200
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25868008-9eff-1037-8865-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750673Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.201,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.201
+mail: user.201@example.com
+o: example
+initials: u.201
+structuralObjectClass: inetOrgPerson
+uidNumber: 1205
+gidNumber: 1212
+loginShell: /bin/bash
+title: user.201
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.201
+cn: User 201
+displayName: User 201
+givenName: User
+sn: 201
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258682f6-9eff-1037-8866-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750748Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.202,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.202
+mail: user.202@example.com
+o: example
+initials: u.202
+structuralObjectClass: inetOrgPerson
+uidNumber: 1206
+gidNumber: 1213
+loginShell: /bin/bash
+title: user.202
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.202
+cn: User 202
+displayName: User 202
+givenName: User
+sn: 202
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25868580-9eff-1037-8867-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750813Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.203,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.203
+mail: user.203@example.com
+o: example
+initials: u.203
+structuralObjectClass: inetOrgPerson
+uidNumber: 1207
+gidNumber: 1214
+loginShell: /bin/bash
+title: user.203
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.203
+cn: User 203
+displayName: User 203
+givenName: User
+sn: 203
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258687e2-9eff-1037-8868-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750874Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.204,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.204
+mail: user.204@example.com
+o: example
+initials: u.204
+structuralObjectClass: inetOrgPerson
+uidNumber: 1208
+gidNumber: 1215
+loginShell: /bin/bash
+title: user.204
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.204
+cn: User 204
+displayName: User 204
+givenName: User
+sn: 204
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25868ab2-9eff-1037-8869-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.750946Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.205,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.205
+mail: user.205@example.com
+o: example
+initials: u.205
+structuralObjectClass: inetOrgPerson
+uidNumber: 1209
+gidNumber: 1216
+loginShell: /bin/bash
+title: user.205
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.205
+cn: User 205
+displayName: User 205
+givenName: User
+sn: 205
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25868d14-9eff-1037-886a-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751008Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.206,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.206
+mail: user.206@example.com
+o: example
+initials: u.206
+structuralObjectClass: inetOrgPerson
+uidNumber: 1210
+gidNumber: 1217
+loginShell: /bin/bash
+title: user.206
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.206
+cn: User 206
+displayName: User 206
+givenName: User
+sn: 206
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25868fda-9eff-1037-886b-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751079Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.207,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.207
+mail: user.207@example.com
+o: example
+initials: u.207
+structuralObjectClass: inetOrgPerson
+uidNumber: 1211
+gidNumber: 1218
+loginShell: /bin/bash
+title: user.207
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.207
+cn: User 207
+displayName: User 207
+givenName: User
+sn: 207
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258692aa-9eff-1037-886c-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751150Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.208,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.208
+mail: user.208@example.com
+o: example
+initials: u.208
+structuralObjectClass: inetOrgPerson
+uidNumber: 1212
+gidNumber: 1219
+loginShell: /bin/bash
+title: user.208
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.208
+cn: User 208
+displayName: User 208
+givenName: User
+sn: 208
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25869534-9eff-1037-886d-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751215Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.209,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.209
+mail: user.209@example.com
+o: example
+initials: u.209
+structuralObjectClass: inetOrgPerson
+uidNumber: 1213
+gidNumber: 1220
+loginShell: /bin/bash
+title: user.209
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.209
+cn: User 209
+displayName: User 209
+givenName: User
+sn: 209
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258697aa-9eff-1037-886e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751278Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.210,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.210
+mail: user.210@example.com
+o: example
+initials: u.210
+structuralObjectClass: inetOrgPerson
+uidNumber: 1214
+gidNumber: 1221
+loginShell: /bin/bash
+title: user.210
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.210
+cn: User 210
+displayName: User 210
+givenName: User
+sn: 210
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25869aa2-9eff-1037-886f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751354Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.211,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.211
+mail: user.211@example.com
+o: example
+initials: u.211
+structuralObjectClass: inetOrgPerson
+uidNumber: 1215
+gidNumber: 1222
+loginShell: /bin/bash
+title: user.211
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.211
+cn: User 211
+displayName: User 211
+givenName: User
+sn: 211
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25869ee4-9eff-1037-8870-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751462Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.212,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.212
+mail: user.212@example.com
+o: example
+initials: u.212
+structuralObjectClass: inetOrgPerson
+uidNumber: 1216
+gidNumber: 1223
+loginShell: /bin/bash
+title: user.212
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.212
+cn: User 212
+displayName: User 212
+givenName: User
+sn: 212
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586a182-9eff-1037-8871-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751531Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.213,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.213
+mail: user.213@example.com
+o: example
+initials: u.213
+structuralObjectClass: inetOrgPerson
+uidNumber: 1217
+gidNumber: 1224
+loginShell: /bin/bash
+title: user.213
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.213
+cn: User 213
+displayName: User 213
+givenName: User
+sn: 213
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586a470-9eff-1037-8872-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751605Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.214,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.214
+mail: user.214@example.com
+o: example
+initials: u.214
+structuralObjectClass: inetOrgPerson
+uidNumber: 1218
+gidNumber: 1225
+loginShell: /bin/bash
+title: user.214
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.214
+cn: User 214
+displayName: User 214
+givenName: User
+sn: 214
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586a7c2-9eff-1037-8873-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751690Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.215,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.215
+mail: user.215@example.com
+o: example
+initials: u.215
+structuralObjectClass: inetOrgPerson
+uidNumber: 1219
+gidNumber: 1226
+loginShell: /bin/bash
+title: user.215
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.215
+cn: User 215
+displayName: User 215
+givenName: User
+sn: 215
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586aa42-9eff-1037-8874-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751754Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.216,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.216
+mail: user.216@example.com
+o: example
+initials: u.216
+structuralObjectClass: inetOrgPerson
+uidNumber: 1220
+gidNumber: 1227
+loginShell: /bin/bash
+title: user.216
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.216
+cn: User 216
+displayName: User 216
+givenName: User
+sn: 216
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586ad26-9eff-1037-8875-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751828Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.217,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.217
+mail: user.217@example.com
+o: example
+initials: u.217
+structuralObjectClass: inetOrgPerson
+uidNumber: 1221
+gidNumber: 1228
+loginShell: /bin/bash
+title: user.217
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.217
+cn: User 217
+displayName: User 217
+givenName: User
+sn: 217
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586af92-9eff-1037-8876-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751891Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.218,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.218
+mail: user.218@example.com
+o: example
+initials: u.218
+structuralObjectClass: inetOrgPerson
+uidNumber: 1222
+gidNumber: 1229
+loginShell: /bin/bash
+title: user.218
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.218
+cn: User 218
+displayName: User 218
+givenName: User
+sn: 218
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586b21c-9eff-1037-8877-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.751955Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.219,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.219
+mail: user.219@example.com
+o: example
+initials: u.219
+structuralObjectClass: inetOrgPerson
+uidNumber: 1223
+gidNumber: 1230
+loginShell: /bin/bash
+title: user.219
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.219
+cn: User 219
+displayName: User 219
+givenName: User
+sn: 219
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586b4e2-9eff-1037-8878-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752026Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.220,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.220
+mail: user.220@example.com
+o: example
+initials: u.220
+structuralObjectClass: inetOrgPerson
+uidNumber: 1224
+gidNumber: 1231
+loginShell: /bin/bash
+title: user.220
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.220
+cn: User 220
+displayName: User 220
+givenName: User
+sn: 220
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586b758-9eff-1037-8879-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752089Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.221,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.221
+mail: user.221@example.com
+o: example
+initials: u.221
+structuralObjectClass: inetOrgPerson
+uidNumber: 1225
+gidNumber: 1232
+loginShell: /bin/bash
+title: user.221
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.221
+cn: User 221
+displayName: User 221
+givenName: User
+sn: 221
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586ba00-9eff-1037-887a-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752158Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.222,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.222
+mail: user.222@example.com
+o: example
+initials: u.222
+structuralObjectClass: inetOrgPerson
+uidNumber: 1226
+gidNumber: 1233
+loginShell: /bin/bash
+title: user.222
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.222
+cn: User 222
+displayName: User 222
+givenName: User
+sn: 222
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586bcf8-9eff-1037-887b-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752233Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.223,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.223
+mail: user.223@example.com
+o: example
+initials: u.223
+structuralObjectClass: inetOrgPerson
+uidNumber: 1227
+gidNumber: 1234
+loginShell: /bin/bash
+title: user.223
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.223
+cn: User 223
+displayName: User 223
+givenName: User
+sn: 223
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586bf64-9eff-1037-887c-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752295Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.224,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.224
+mail: user.224@example.com
+o: example
+initials: u.224
+structuralObjectClass: inetOrgPerson
+uidNumber: 1228
+gidNumber: 1235
+loginShell: /bin/bash
+title: user.224
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.224
+cn: User 224
+displayName: User 224
+givenName: User
+sn: 224
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586c1da-9eff-1037-887d-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752358Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.225,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.225
+mail: user.225@example.com
+o: example
+initials: u.225
+structuralObjectClass: inetOrgPerson
+uidNumber: 1229
+gidNumber: 1236
+loginShell: /bin/bash
+title: user.225
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.225
+cn: User 225
+displayName: User 225
+givenName: User
+sn: 225
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586c4a0-9eff-1037-887e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752429Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.226,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.226
+mail: user.226@example.com
+o: example
+initials: u.226
+structuralObjectClass: inetOrgPerson
+uidNumber: 1230
+gidNumber: 1237
+loginShell: /bin/bash
+title: user.226
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.226
+cn: User 226
+displayName: User 226
+givenName: User
+sn: 226
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586c72a-9eff-1037-887f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752494Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.227,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.227
+mail: user.227@example.com
+o: example
+initials: u.227
+structuralObjectClass: inetOrgPerson
+uidNumber: 1231
+gidNumber: 1238
+loginShell: /bin/bash
+title: user.227
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.227
+cn: User 227
+displayName: User 227
+givenName: User
+sn: 227
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586c98c-9eff-1037-8880-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752555Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.228,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.228
+mail: user.228@example.com
+o: example
+initials: u.228
+structuralObjectClass: inetOrgPerson
+uidNumber: 1232
+gidNumber: 1239
+loginShell: /bin/bash
+title: user.228
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.228
+cn: User 228
+displayName: User 228
+givenName: User
+sn: 228
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586cc98-9eff-1037-8881-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752633Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.229,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.229
+mail: user.229@example.com
+o: example
+initials: u.229
+structuralObjectClass: inetOrgPerson
+uidNumber: 1233
+gidNumber: 1240
+loginShell: /bin/bash
+title: user.229
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.229
+cn: User 229
+displayName: User 229
+givenName: User
+sn: 229
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586d01c-9eff-1037-8882-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752723Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.230,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.230
+mail: user.230@example.com
+o: example
+initials: u.230
+structuralObjectClass: inetOrgPerson
+uidNumber: 1234
+gidNumber: 1241
+loginShell: /bin/bash
+title: user.230
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.230
+cn: User 230
+displayName: User 230
+givenName: User
+sn: 230
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586d2b0-9eff-1037-8883-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752789Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.231,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.231
+mail: user.231@example.com
+o: example
+initials: u.231
+structuralObjectClass: inetOrgPerson
+uidNumber: 1235
+gidNumber: 1242
+loginShell: /bin/bash
+title: user.231
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.231
+cn: User 231
+displayName: User 231
+givenName: User
+sn: 231
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586d576-9eff-1037-8884-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752860Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.232,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.232
+mail: user.232@example.com
+o: example
+initials: u.232
+structuralObjectClass: inetOrgPerson
+uidNumber: 1236
+gidNumber: 1243
+loginShell: /bin/bash
+title: user.232
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.232
+cn: User 232
+displayName: User 232
+givenName: User
+sn: 232
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586d7ec-9eff-1037-8885-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752924Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.233,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.233
+mail: user.233@example.com
+o: example
+initials: u.233
+structuralObjectClass: inetOrgPerson
+uidNumber: 1237
+gidNumber: 1244
+loginShell: /bin/bash
+title: user.233
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.233
+cn: User 233
+displayName: User 233
+givenName: User
+sn: 233
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586da58-9eff-1037-8886-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.752985Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.234,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.234
+mail: user.234@example.com
+o: example
+initials: u.234
+structuralObjectClass: inetOrgPerson
+uidNumber: 1238
+gidNumber: 1245
+loginShell: /bin/bash
+title: user.234
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.234
+cn: User 234
+displayName: User 234
+givenName: User
+sn: 234
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586dd6e-9eff-1037-8887-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753064Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.235,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.235
+mail: user.235@example.com
+o: example
+initials: u.235
+structuralObjectClass: inetOrgPerson
+uidNumber: 1239
+gidNumber: 1246
+loginShell: /bin/bash
+title: user.235
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.235
+cn: User 235
+displayName: User 235
+givenName: User
+sn: 235
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586dfd0-9eff-1037-8888-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753126Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.236,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.236
+mail: user.236@example.com
+o: example
+initials: u.236
+structuralObjectClass: inetOrgPerson
+uidNumber: 1240
+gidNumber: 1247
+loginShell: /bin/bash
+title: user.236
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.236
+cn: User 236
+displayName: User 236
+givenName: User
+sn: 236
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586e282-9eff-1037-8889-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753194Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.237,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.237
+mail: user.237@example.com
+o: example
+initials: u.237
+structuralObjectClass: inetOrgPerson
+uidNumber: 1241
+gidNumber: 1248
+loginShell: /bin/bash
+title: user.237
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.237
+cn: User 237
+displayName: User 237
+givenName: User
+sn: 237
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586e548-9eff-1037-888a-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753265Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.238,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.238
+mail: user.238@example.com
+o: example
+initials: u.238
+structuralObjectClass: inetOrgPerson
+uidNumber: 1242
+gidNumber: 1249
+loginShell: /bin/bash
+title: user.238
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.238
+cn: User 238
+displayName: User 238
+givenName: User
+sn: 238
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586e7d2-9eff-1037-888b-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753330Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.239,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.239
+mail: user.239@example.com
+o: example
+initials: u.239
+structuralObjectClass: inetOrgPerson
+uidNumber: 1243
+gidNumber: 1250
+loginShell: /bin/bash
+title: user.239
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.239
+cn: User 239
+displayName: User 239
+givenName: User
+sn: 239
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586ea34-9eff-1037-888c-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753392Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.240,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.240
+mail: user.240@example.com
+o: example
+initials: u.240
+structuralObjectClass: inetOrgPerson
+uidNumber: 1244
+gidNumber: 1251
+loginShell: /bin/bash
+title: user.240
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.240
+cn: User 240
+displayName: User 240
+givenName: User
+sn: 240
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586ed0e-9eff-1037-888d-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753464Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.241,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.241
+mail: user.241@example.com
+o: example
+initials: u.241
+structuralObjectClass: inetOrgPerson
+uidNumber: 1245
+gidNumber: 1252
+loginShell: /bin/bash
+title: user.241
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.241
+cn: User 241
+displayName: User 241
+givenName: User
+sn: 241
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586ef7a-9eff-1037-888e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753527Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.242,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.242
+mail: user.242@example.com
+o: example
+initials: u.242
+structuralObjectClass: inetOrgPerson
+uidNumber: 1246
+gidNumber: 1253
+loginShell: /bin/bash
+title: user.242
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.242
+cn: User 242
+displayName: User 242
+givenName: User
+sn: 242
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586f1fa-9eff-1037-888f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753590Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.243,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.243
+mail: user.243@example.com
+o: example
+initials: u.243
+structuralObjectClass: inetOrgPerson
+uidNumber: 1247
+gidNumber: 1254
+loginShell: /bin/bash
+title: user.243
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.243
+cn: User 243
+displayName: User 243
+givenName: User
+sn: 243
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586f560-9eff-1037-8890-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753676Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.244,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.244
+mail: user.244@example.com
+o: example
+initials: u.244
+structuralObjectClass: inetOrgPerson
+uidNumber: 1248
+gidNumber: 1255
+loginShell: /bin/bash
+title: user.244
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.244
+cn: User 244
+displayName: User 244
+givenName: User
+sn: 244
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586f7f4-9eff-1037-8891-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753743Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.245,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.245
+mail: user.245@example.com
+o: example
+initials: u.245
+structuralObjectClass: inetOrgPerson
+uidNumber: 1249
+gidNumber: 1256
+loginShell: /bin/bash
+title: user.245
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.245
+cn: User 245
+displayName: User 245
+givenName: User
+sn: 245
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586fa4c-9eff-1037-8892-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753804Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.246,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.246
+mail: user.246@example.com
+o: example
+initials: u.246
+structuralObjectClass: inetOrgPerson
+uidNumber: 1250
+gidNumber: 1257
+loginShell: /bin/bash
+title: user.246
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.246
+cn: User 246
+displayName: User 246
+givenName: User
+sn: 246
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2586fd3a-9eff-1037-8893-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753878Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.247,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.247
+mail: user.247@example.com
+o: example
+initials: u.247
+structuralObjectClass: inetOrgPerson
+uidNumber: 1251
+gidNumber: 1258
+loginShell: /bin/bash
+title: user.247
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.247
+cn: User 247
+displayName: User 247
+givenName: User
+sn: 247
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2587001e-9eff-1037-8894-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.753952Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.248,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.248
+mail: user.248@example.com
+o: example
+initials: u.248
+structuralObjectClass: inetOrgPerson
+uidNumber: 1252
+gidNumber: 1259
+loginShell: /bin/bash
+title: user.248
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.248
+cn: User 248
+displayName: User 248
+givenName: User
+sn: 248
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258702ee-9eff-1037-8895-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754025Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.249,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.249
+mail: user.249@example.com
+o: example
+initials: u.249
+structuralObjectClass: inetOrgPerson
+uidNumber: 1253
+gidNumber: 1260
+loginShell: /bin/bash
+title: user.249
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.249
+cn: User 249
+displayName: User 249
+givenName: User
+sn: 249
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258705b4-9eff-1037-8896-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754095Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.250,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.250
+mail: user.250@example.com
+o: example
+initials: u.250
+structuralObjectClass: inetOrgPerson
+uidNumber: 1254
+gidNumber: 1261
+loginShell: /bin/bash
+title: user.250
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.250
+cn: User 250
+displayName: User 250
+givenName: User
+sn: 250
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25870956-9eff-1037-8897-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754188Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.251,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.251
+mail: user.251@example.com
+o: example
+initials: u.251
+structuralObjectClass: inetOrgPerson
+uidNumber: 1255
+gidNumber: 1262
+loginShell: /bin/bash
+title: user.251
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.251
+cn: User 251
+displayName: User 251
+givenName: User
+sn: 251
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25870bcc-9eff-1037-8898-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754252Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.252,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.252
+mail: user.252@example.com
+o: example
+initials: u.252
+structuralObjectClass: inetOrgPerson
+uidNumber: 1256
+gidNumber: 1263
+loginShell: /bin/bash
+title: user.252
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.252
+cn: User 252
+displayName: User 252
+givenName: User
+sn: 252
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25870e9c-9eff-1037-8899-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754323Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.253,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.253
+mail: user.253@example.com
+o: example
+initials: u.253
+structuralObjectClass: inetOrgPerson
+uidNumber: 1257
+gidNumber: 1264
+loginShell: /bin/bash
+title: user.253
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.253
+cn: User 253
+displayName: User 253
+givenName: User
+sn: 253
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25871108-9eff-1037-889a-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754385Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.254,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.254
+mail: user.254@example.com
+o: example
+initials: u.254
+structuralObjectClass: inetOrgPerson
+uidNumber: 1258
+gidNumber: 1265
+loginShell: /bin/bash
+title: user.254
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.254
+cn: User 254
+displayName: User 254
+givenName: User
+sn: 254
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2587136a-9eff-1037-889b-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754446Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.255,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.255
+mail: user.255@example.com
+o: example
+initials: u.255
+structuralObjectClass: inetOrgPerson
+uidNumber: 1259
+gidNumber: 1266
+loginShell: /bin/bash
+title: user.255
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.255
+cn: User 255
+displayName: User 255
+givenName: User
+sn: 255
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25871630-9eff-1037-889c-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754517Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.256,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.256
+mail: user.256@example.com
+o: example
+initials: u.256
+structuralObjectClass: inetOrgPerson
+uidNumber: 1260
+gidNumber: 1267
+loginShell: /bin/bash
+title: user.256
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.256
+cn: User 256
+displayName: User 256
+givenName: User
+sn: 256
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2587189c-9eff-1037-889d-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754579Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.257,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.257
+mail: user.257@example.com
+o: example
+initials: u.257
+structuralObjectClass: inetOrgPerson
+uidNumber: 1261
+gidNumber: 1268
+loginShell: /bin/bash
+title: user.257
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.257
+cn: User 257
+displayName: User 257
+givenName: User
+sn: 257
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25871afe-9eff-1037-889e-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754640Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.258,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.258
+mail: user.258@example.com
+o: example
+initials: u.258
+structuralObjectClass: inetOrgPerson
+uidNumber: 1262
+gidNumber: 1269
+loginShell: /bin/bash
+title: user.258
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.258
+cn: User 258
+displayName: User 258
+givenName: User
+sn: 258
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25871e78-9eff-1037-889f-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754729Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.259,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.259
+mail: user.259@example.com
+o: example
+initials: u.259
+structuralObjectClass: inetOrgPerson
+uidNumber: 1263
+gidNumber: 1270
+loginShell: /bin/bash
+title: user.259
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.259
+cn: User 259
+displayName: User 259
+givenName: User
+sn: 259
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258720f8-9eff-1037-88a0-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754793Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.260,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.260
+mail: user.260@example.com
+o: example
+initials: u.260
+structuralObjectClass: inetOrgPerson
+uidNumber: 1264
+gidNumber: 1271
+loginShell: /bin/bash
+title: user.260
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.260
+cn: User 260
+displayName: User 260
+givenName: User
+sn: 260
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2587235a-9eff-1037-88a1-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754854Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.261,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.261
+mail: user.261@example.com
+o: example
+initials: u.261
+structuralObjectClass: inetOrgPerson
+uidNumber: 1265
+gidNumber: 1272
+loginShell: /bin/bash
+title: user.261
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.261
+cn: User 261
+displayName: User 261
+givenName: User
+sn: 261
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25872620-9eff-1037-88a2-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754925Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.262,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.262
+mail: user.262@example.com
+o: example
+initials: u.262
+structuralObjectClass: inetOrgPerson
+uidNumber: 1266
+gidNumber: 1273
+loginShell: /bin/bash
+title: user.262
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.262
+cn: User 262
+displayName: User 262
+givenName: User
+sn: 262
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2587288c-9eff-1037-88a3-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.754988Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.263,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.263
+mail: user.263@example.com
+o: example
+initials: u.263
+structuralObjectClass: inetOrgPerson
+uidNumber: 1267
+gidNumber: 1274
+loginShell: /bin/bash
+title: user.263
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.263
+cn: User 263
+displayName: User 263
+givenName: User
+sn: 263
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25872aee-9eff-1037-88a4-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755049Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.264,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.264
+mail: user.264@example.com
+o: example
+initials: u.264
+structuralObjectClass: inetOrgPerson
+uidNumber: 1268
+gidNumber: 1275
+loginShell: /bin/bash
+title: user.264
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.264
+cn: User 264
+displayName: User 264
+givenName: User
+sn: 264
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25872db4-9eff-1037-88a5-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755119Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.265,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.265
+mail: user.265@example.com
+o: example
+initials: u.265
+structuralObjectClass: inetOrgPerson
+uidNumber: 1269
+gidNumber: 1276
+loginShell: /bin/bash
+title: user.265
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.265
+cn: User 265
+displayName: User 265
+givenName: User
+sn: 265
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25873070-9eff-1037-88a6-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755189Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.266,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.266
+mail: user.266@example.com
+o: example
+initials: u.266
+structuralObjectClass: inetOrgPerson
+uidNumber: 1270
+gidNumber: 1277
+loginShell: /bin/bash
+title: user.266
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.266
+cn: User 266
+displayName: User 266
+givenName: User
+sn: 266
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258732fa-9eff-1037-88a7-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755254Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.267,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.267
+mail: user.267@example.com
+o: example
+initials: u.267
+structuralObjectClass: inetOrgPerson
+uidNumber: 1271
+gidNumber: 1278
+loginShell: /bin/bash
+title: user.267
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.267
+cn: User 267
+displayName: User 267
+givenName: User
+sn: 267
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258735ca-9eff-1037-88a8-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755326Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.268,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.268
+mail: user.268@example.com
+o: example
+initials: u.268
+structuralObjectClass: inetOrgPerson
+uidNumber: 1272
+gidNumber: 1279
+loginShell: /bin/bash
+title: user.268
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.268
+cn: User 268
+displayName: User 268
+givenName: User
+sn: 268
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25873836-9eff-1037-88a9-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755388Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.269,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.269
+mail: user.269@example.com
+o: example
+initials: u.269
+structuralObjectClass: inetOrgPerson
+uidNumber: 1273
+gidNumber: 1280
+loginShell: /bin/bash
+title: user.269
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.269
+cn: User 269
+displayName: User 269
+givenName: User
+sn: 269
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25873a8e-9eff-1037-88aa-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755449Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.270,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.270
+mail: user.270@example.com
+o: example
+initials: u.270
+structuralObjectClass: inetOrgPerson
+uidNumber: 1274
+gidNumber: 1281
+loginShell: /bin/bash
+title: user.270
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.270
+cn: User 270
+displayName: User 270
+givenName: User
+sn: 270
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25873d54-9eff-1037-88ab-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755519Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.271,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.271
+mail: user.271@example.com
+o: example
+initials: u.271
+structuralObjectClass: inetOrgPerson
+uidNumber: 1275
+gidNumber: 1282
+loginShell: /bin/bash
+title: user.271
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.271
+cn: User 271
+displayName: User 271
+givenName: User
+sn: 271
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25873fc0-9eff-1037-88ac-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755582Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.272,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.272
+mail: user.272@example.com
+o: example
+initials: u.272
+structuralObjectClass: inetOrgPerson
+uidNumber: 1276
+gidNumber: 1283
+loginShell: /bin/bash
+title: user.272
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.272
+cn: User 272
+displayName: User 272
+givenName: User
+sn: 272
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25874434-9eff-1037-88ad-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755694Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.273,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.273
+mail: user.273@example.com
+o: example
+initials: u.273
+structuralObjectClass: inetOrgPerson
+uidNumber: 1277
+gidNumber: 1284
+loginShell: /bin/bash
+title: user.273
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.273
+cn: User 273
+displayName: User 273
+givenName: User
+sn: 273
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25874786-9eff-1037-88ae-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755780Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.274,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.274
+mail: user.274@example.com
+o: example
+initials: u.274
+structuralObjectClass: inetOrgPerson
+uidNumber: 1278
+gidNumber: 1285
+loginShell: /bin/bash
+title: user.274
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.274
+cn: User 274
+displayName: User 274
+givenName: User
+sn: 274
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25874a06-9eff-1037-88af-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755844Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.275,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.275
+mail: user.275@example.com
+o: example
+initials: u.275
+structuralObjectClass: inetOrgPerson
+uidNumber: 1279
+gidNumber: 1286
+loginShell: /bin/bash
+title: user.275
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.275
+cn: User 275
+displayName: User 275
+givenName: User
+sn: 275
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25874c68-9eff-1037-88b0-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755906Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.276,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.276
+mail: user.276@example.com
+o: example
+initials: u.276
+structuralObjectClass: inetOrgPerson
+uidNumber: 1280
+gidNumber: 1287
+loginShell: /bin/bash
+title: user.276
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.276
+cn: User 276
+displayName: User 276
+givenName: User
+sn: 276
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25874f42-9eff-1037-88b1-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.755978Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.277,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.277
+mail: user.277@example.com
+o: example
+initials: u.277
+structuralObjectClass: inetOrgPerson
+uidNumber: 1281
+gidNumber: 1288
+loginShell: /bin/bash
+title: user.277
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.277
+cn: User 277
+displayName: User 277
+givenName: User
+sn: 277
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258751b8-9eff-1037-88b2-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756041Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.278,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.278
+mail: user.278@example.com
+o: example
+initials: u.278
+structuralObjectClass: inetOrgPerson
+uidNumber: 1282
+gidNumber: 1289
+loginShell: /bin/bash
+title: user.278
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.278
+cn: User 278
+displayName: User 278
+givenName: User
+sn: 278
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25875410-9eff-1037-88b3-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756101Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.279,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.279
+mail: user.279@example.com
+o: example
+initials: u.279
+structuralObjectClass: inetOrgPerson
+uidNumber: 1283
+gidNumber: 1290
+loginShell: /bin/bash
+title: user.279
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.279
+cn: User 279
+displayName: User 279
+givenName: User
+sn: 279
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258756d6-9eff-1037-88b4-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756172Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.280,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.280
+mail: user.280@example.com
+o: example
+initials: u.280
+structuralObjectClass: inetOrgPerson
+uidNumber: 1284
+gidNumber: 1291
+loginShell: /bin/bash
+title: user.280
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.280
+cn: User 280
+displayName: User 280
+givenName: User
+sn: 280
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25875988-9eff-1037-88b5-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756242Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.281,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.281
+mail: user.281@example.com
+o: example
+initials: u.281
+structuralObjectClass: inetOrgPerson
+uidNumber: 1285
+gidNumber: 1292
+loginShell: /bin/bash
+title: user.281
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.281
+cn: User 281
+displayName: User 281
+givenName: User
+sn: 281
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25875bfe-9eff-1037-88b6-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756304Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.282,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.282
+mail: user.282@example.com
+o: example
+initials: u.282
+structuralObjectClass: inetOrgPerson
+uidNumber: 1286
+gidNumber: 1293
+loginShell: /bin/bash
+title: user.282
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.282
+cn: User 282
+displayName: User 282
+givenName: User
+sn: 282
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25875ec4-9eff-1037-88b7-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756375Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.283,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.283
+mail: user.283@example.com
+o: example
+initials: u.283
+structuralObjectClass: inetOrgPerson
+uidNumber: 1287
+gidNumber: 1294
+loginShell: /bin/bash
+title: user.283
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.283
+cn: User 283
+displayName: User 283
+givenName: User
+sn: 283
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25876130-9eff-1037-88b8-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756438Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.284,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.284
+mail: user.284@example.com
+o: example
+initials: u.284
+structuralObjectClass: inetOrgPerson
+uidNumber: 1288
+gidNumber: 1295
+loginShell: /bin/bash
+title: user.284
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.284
+cn: User 284
+displayName: User 284
+givenName: User
+sn: 284
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2587639c-9eff-1037-88b9-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756499Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.285,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.285
+mail: user.285@example.com
+o: example
+initials: u.285
+structuralObjectClass: inetOrgPerson
+uidNumber: 1289
+gidNumber: 1296
+loginShell: /bin/bash
+title: user.285
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.285
+cn: User 285
+displayName: User 285
+givenName: User
+sn: 285
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25876662-9eff-1037-88ba-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756570Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.286,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.286
+mail: user.286@example.com
+o: example
+initials: u.286
+structuralObjectClass: inetOrgPerson
+uidNumber: 1290
+gidNumber: 1297
+loginShell: /bin/bash
+title: user.286
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.286
+cn: User 286
+displayName: User 286
+givenName: User
+sn: 286
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258768ce-9eff-1037-88bb-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756632Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.287,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.287
+mail: user.287@example.com
+o: example
+initials: u.287
+structuralObjectClass: inetOrgPerson
+uidNumber: 1291
+gidNumber: 1298
+loginShell: /bin/bash
+title: user.287
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.287
+cn: User 287
+displayName: User 287
+givenName: User
+sn: 287
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25876bf8-9eff-1037-88bc-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756713Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.288,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.288
+mail: user.288@example.com
+o: example
+initials: u.288
+structuralObjectClass: inetOrgPerson
+uidNumber: 1292
+gidNumber: 1299
+loginShell: /bin/bash
+title: user.288
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.288
+cn: User 288
+displayName: User 288
+givenName: User
+sn: 288
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25876edc-9eff-1037-88bd-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756787Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.289,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.289
+mail: user.289@example.com
+o: example
+initials: u.289
+structuralObjectClass: inetOrgPerson
+uidNumber: 1293
+gidNumber: 1300
+loginShell: /bin/bash
+title: user.289
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.289
+cn: User 289
+displayName: User 289
+givenName: User
+sn: 289
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25877148-9eff-1037-88be-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756850Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.290,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.290
+mail: user.290@example.com
+o: example
+initials: u.290
+structuralObjectClass: inetOrgPerson
+uidNumber: 1294
+gidNumber: 1301
+loginShell: /bin/bash
+title: user.290
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.290
+cn: User 290
+displayName: User 290
+givenName: User
+sn: 290
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258773aa-9eff-1037-88bf-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756911Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.291,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.291
+mail: user.291@example.com
+o: example
+initials: u.291
+structuralObjectClass: inetOrgPerson
+uidNumber: 1295
+gidNumber: 1302
+loginShell: /bin/bash
+title: user.291
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.291
+cn: User 291
+displayName: User 291
+givenName: User
+sn: 291
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2587767a-9eff-1037-88c0-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.756982Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.292,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.292
+mail: user.292@example.com
+o: example
+initials: u.292
+structuralObjectClass: inetOrgPerson
+uidNumber: 1296
+gidNumber: 1303
+loginShell: /bin/bash
+title: user.292
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.292
+cn: User 292
+displayName: User 292
+givenName: User
+sn: 292
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258778e6-9eff-1037-88c1-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.757044Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.293,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.293
+mail: user.293@example.com
+o: example
+initials: u.293
+structuralObjectClass: inetOrgPerson
+uidNumber: 1297
+gidNumber: 1304
+loginShell: /bin/bash
+title: user.293
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.293
+cn: User 293
+displayName: User 293
+givenName: User
+sn: 293
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25877b48-9eff-1037-88c2-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.757106Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.294,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.294
+mail: user.294@example.com
+o: example
+initials: u.294
+structuralObjectClass: inetOrgPerson
+uidNumber: 1298
+gidNumber: 1305
+loginShell: /bin/bash
+title: user.294
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.294
+cn: User 294
+displayName: User 294
+givenName: User
+sn: 294
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25878020-9eff-1037-88c3-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.757227Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.295,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.295
+mail: user.295@example.com
+o: example
+initials: u.295
+structuralObjectClass: inetOrgPerson
+uidNumber: 1299
+gidNumber: 1306
+loginShell: /bin/bash
+title: user.295
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.295
+cn: User 295
+displayName: User 295
+givenName: User
+sn: 295
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 258782f0-9eff-1037-88c4-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.757301Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.296,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.296
+mail: user.296@example.com
+o: example
+initials: u.296
+structuralObjectClass: inetOrgPerson
+uidNumber: 1300
+gidNumber: 1307
+loginShell: /bin/bash
+title: user.296
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.296
+cn: User 296
+displayName: User 296
+givenName: User
+sn: 296
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 2587855c-9eff-1037-88c5-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.757363Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.297,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.297
+mail: user.297@example.com
+o: example
+initials: u.297
+structuralObjectClass: inetOrgPerson
+uidNumber: 1301
+gidNumber: 1308
+loginShell: /bin/bash
+title: user.297
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.297
+cn: User 297
+displayName: User 297
+givenName: User
+sn: 297
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25878822-9eff-1037-88c6-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.757434Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.298,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.298
+mail: user.298@example.com
+o: example
+initials: u.298
+structuralObjectClass: inetOrgPerson
+uidNumber: 1302
+gidNumber: 1309
+loginShell: /bin/bash
+title: user.298
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.298
+cn: User 298
+displayName: User 298
+givenName: User
+sn: 298
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25878a8e-9eff-1037-88c7-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.757497Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.299,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.299
+mail: user.299@example.com
+o: example
+initials: u.299
+structuralObjectClass: inetOrgPerson
+uidNumber: 1303
+gidNumber: 1310
+loginShell: /bin/bash
+title: user.299
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.299
+cn: User 299
+displayName: User 299
+givenName: User
+sn: 299
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25878cfa-9eff-1037-88c8-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.757558Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
+dn: uid=user.300,ou=user,dc=example,dc=com
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+objectClass: posixAccount
+uid: user.300
+mail: user.300@example.com
+o: example
+initials: u.300
+structuralObjectClass: inetOrgPerson
+uidNumber: 1304
+gidNumber: 1311
+loginShell: /bin/bash
+title: user.300
+telephoneNumber: +1 234 567 8910
+facsimileTelephoneNumber: +1 234 567 8910
+homeDirectory: /user/user.300
+cn: User 300
+displayName: User 300
+givenName: User
+sn: 300
+userPassword:: dGVzdHBhc3M=
+physicalDeliveryOfficeName: 1-234
+ou: Users
+entryUUID: 25878fc0-9eff-1037-88c9-f39faf7af9b0
+creatorsName: cn=manager,dc=example,dc=com
+createTimestamp: 20170205203027Z
+entryCSN: 20170205203027.757629Z#000000#004#000000
+modifiersName: cn=manager,dc=example,dc=com
+modifyTimestamp: 20170205203027Z
+
diff --git a/tests/data/regressions/its9051/its9051 b/tests/data/regressions/its9051/its9051
new file mode 100755
index 0000000..ed7ee13
--- /dev/null
+++ b/tests/data/regressions/its9051/its9051
@@ -0,0 +1,185 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $ACCESSLOG = accesslogno; then
+ echo "Accesslog overlay not available, test skipped"
+ exit 0
+fi
+
+XDIR=$TESTDIR/srv
+
+mkdir -p $TESTDIR
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+ITS=9051
+ITSDIR=$DATADIR/regressions/its$ITS
+
+
+echo "Initializing server configuration for provider..."
+ DBDIR=${XDIR}/db
+ CFDIR=${XDIR}/slapd.d
+
+mkdir -p ${XDIR} $DBDIR.1 $DBDIR.2 $CFDIR
+. $CONFFILTER $BACKEND < $ITSDIR/slapd-provider.ldif > $CONFLDIF
+$SLAPADD -F $CFDIR -n 0 -l $CONFLDIF
+$SLAPADD -F $CFDIR -q -b $BASEDN -l $ITSDIR/db.ldif
+
+KILLPIDS=
+CFDIR=${XDIR}/slapd.d
+
+echo "Starting provider slapd on TCP/IP URI $URI1"
+$SLAPD -F $CFDIR -h $URI1 -d $LVL > $LOG1 2>&1 &
+
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID $KILLPIDS"
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running ldapsearch to generate log ops..."
+$LDAPSEARCH -D "$MANAGERDN" -w $PASSWD -s base -b $BASEDN -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Changing to read ops only..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF > $TESTOUT 2>&1 <<EOF
+dn: olcOverlay={0}accesslog,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+delete: olcAccessLogOps
+olcAccessLogOps: writes
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running ldapsearch to generate log ops..."
+$LDAPSEARCH -D "$MANAGERDN" -w $PASSWD -s base -b $BASEDN -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Changing to bind, search, compare ops only..."
+
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF >> $TESTOUT 2>&1 <<EOF
+dn: olcOverlay={0}accesslog,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcAccessLogOps
+olcAccessLogOps: bind
+olcAccessLogOps: compare
+olcAccessLogOps: search
+
+EOF
+
+echo "Running ldapsearch to generate log ops..."
+$LDAPSEARCH -D "$MANAGERDN" -w $PASSWD -s base -b $BASEDN -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running ldapcompare to generate log ops..."
+$LDAPCOMPARE -D "$MANAGERDN" -w $PASSWD -H $URI1 \
+ "uid=user.2,ou=user,$BASEDN" "uidNumber:1006" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+case $RC in
+5)
+ echo "ldapcompare returned FALSE ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+6)
+ echo "ldapcompare returned TRUE ($RC)"
+ ;;
+0)
+ echo "ldapcompare returned success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+echo "" >> $SEARCHOUT
+
+echo "Exporting accesslog database..."
+$SLAPCAT -F $CFDIR -b cn=accesslog -l $TESTDIR/accesslogdb.ldif
+RC=$?
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+# 1 root entry, 2 binds, 3 searches, 1 compare
+count=7
+if test $RC != 0 ; then
+ echo ">>>>> Test failed"
+else
+ RC=`grep '^dn:' $TESTDIR/accesslogdb.ldif | wc -l`
+ if test $RC != $count ; then
+ echo ">>>>> Test failed: expected $count entries, got" $RC
+ RC=1
+ else
+ echo ">>>>> Test succeeded"
+ RC=0
+ fi
+fi
+
+test $KILLSERVERS != no && wait
+
+exit $RC
diff --git a/tests/data/regressions/its9051/slapd-provider.ldif b/tests/data/regressions/its9051/slapd-provider.ldif
new file mode 100644
index 0000000..1872ed1
--- /dev/null
+++ b/tests/data/regressions/its9051/slapd-provider.ldif
@@ -0,0 +1,99 @@
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcLogLevel: Sync
+olcLogLevel: Stats
+olcPidFile: @TESTDIR@/slapd.1.pid
+olcArgsFile: @TESTDIR@/slapd.1.args
+olcServerID: 1
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file://@TESTWD@/@SCHEMADIR@/core.ldif
+include: file://@TESTWD@/@SCHEMADIR@/cosine.ldif
+include: file://@TESTWD@/@SCHEMADIR@/inetorgperson.ldif
+include: file://@TESTWD@/@SCHEMADIR@/misc.ldif
+include: file://@TESTWD@/@SCHEMADIR@/nis.ldif
+
+#mod#dn: cn=module{0},cn=config
+#mod#objectClass: olcModuleList
+#mod#cn: module{0}
+#mod#olcModulePath: @TESTWD@/../servers/slapd/back-@BACKEND@/
+#mod#olcModuleLoad: {0}back_@BACKEND@.la
+
+#accesslogmod#dn: cn=module{1},cn=config
+#accesslogmod#objectClass: olcModuleList
+#accesslogmod#cn: module{1}
+#accesslogmod#olcModulePath: @TESTWD@/../servers/slapd/overlays/
+#accesslogmod#olcModuleLoad: {0}accesslog.la
+
+dn: olcDatabase={-1}frontend,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcFrontendConfig
+olcDatabase: {-1}frontend
+olcAccess: {0}to dn="" by * read
+olcAccess: {1}to * by self write by users read by anonymous auth
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcAccess: {0}to * by * none
+olcRootPW:< file://@TESTDIR@/configpw
+
+dn: olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {1}@BACKEND@
+olcSuffix: dc=example,dc=com
+olcRootDN: cn=manager,dc=example,dc=com
+olcRootPW: secret
+olcSizeLimit: unlimited
+olcTimeLimit: unlimited
+#~null~#olcDbDirectory: @TESTDIR@/srv/db.1
+#indexdb#olcDbIndex: default eq
+#indexdb#olcDbIndex: objectClass
+#indexdb#olcDbIndex: entryUUID
+#indexdb#olcDbIndex: entryCSN
+#indexdb#olcDbIndex: cn eq,sub,approx
+#indexdb#olcDbIndex: givenName eq,sub,approx
+#indexdb#olcDbIndex: displayname eq,sub,approx
+#indexdb#olcDbIndex: uid eq,sub
+#indexdb#olcDbIndex: uidNumber eq
+#indexdb#olcDbIndex: gidNumber eq
+#indexdb#olcDbIndex: mail eq,sub
+#indexdb#olcDbIndex: sn eq,sub,approx
+#indexdb#olcDbIndex: description eq,sub
+#indexdb#olcDbIndex: title eq,sub
+#indexdb#olcDbIndex: member
+#indexdb#olcDbIndex: ou eq,sub,approx
+#indexdb#olcDbIndex: memberUid
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcOverlay={0}accesslog,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcAccessLogConfig
+olcOverlay: {0}accesslog
+olcAccessLogDB: cn=accesslog
+olcAccessLogOps: reads
+olcAccessLogOps: writes
+olcAccessLogPurge: 07+00:00 01+00:00
+olcAccessLogSuccess: TRUE
+
+dn: olcDatabase={2}@BACKEND@,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc@BACKEND@Config
+olcDatabase: {2}@BACKEND@
+olcSuffix: cn=accesslog
+olcSizeLimit: unlimited
+olcTimeLimit: unlimited
+olcDbIndex: default eq
+olcDbIndex: entryCSN,objectClass,reqEnd,reqResult,reqStart,reqDN
+#~null~#olcDbDirectory: @TESTDIR@/srv/db.2
+#mdb#olcDbMaxSize: 33554432
+
+dn: olcDatabase={3}monitor,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {3}monitor
+olcAccess: {0}to dn.subtree="cn=monitor" by * read
diff --git a/tests/data/regressions/its9282/config.ldif b/tests/data/regressions/its9282/config.ldif
new file mode 100644
index 0000000..7b33eb5
--- /dev/null
+++ b/tests/data/regressions/its9282/config.ldif
@@ -0,0 +1,68 @@
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcLogLevel: Sync
+olcLogLevel: Stats
+olcPidFile: @TESTDIR@/slapd.@SID@.pid
+olcArgsFile: @TESTDIR@/slapd.@SID@.args
+olcServerID: @SID@
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file://@TESTWD@/@SCHEMADIR@/core.ldif
+include: file://@TESTWD@/@SCHEMADIR@/cosine.ldif
+include: file://@TESTWD@/@SCHEMADIR@/inetorgperson.ldif
+
+#mod#dn: cn=module{0},cn=config
+#mod#objectClass: olcModuleList
+#mod#cn: module{0}
+#mod#olcModulePath: @TESTWD@/../servers/slapd/back-@BACKEND@/
+#mod#olcModuleLoad: {0}back_@BACKEND@.la
+
+#syncprovmod#dn: cn=module{1},cn=config
+#syncprovmod#objectClass: olcModuleList
+#syncprovmod#cn: module{1}
+#syncprovmod#olcModulePath: @TESTWD@/../servers/slapd/overlays/
+#syncprovmod#olcModuleLoad: {0}syncprov.la
+
+dn: olcDatabase={-1}frontend,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcFrontendConfig
+olcDatabase: {-1}frontend
+olcAddContentAcl: FALSE
+olcLastMod: TRUE
+olcMaxDerefDepth: 0
+olcReadOnly: FALSE
+olcSchemaDN: cn=Subschema
+olcSyncUseSubentry: FALSE
+olcMonitoring: FALSE
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW:< file://@TESTDIR@/configpw
+
+dn: olcDatabase={1}@BACKEND@,cn=config
+objectClass: olc@BACKEND@Config
+olcDatabase: {1}@BACKEND@
+#~null~#olcDbDirectory: @TESTDIR@/srv@SID@/db.@SID@
+olcSuffix: dc=example,dc=com
+olcRootDN: cn=manager,dc=example,dc=com
+olcRootPW: secret
+#indexdb#olcDbIndex: default eq
+#indexdb#olcDbIndex: objectClass
+#indexdb#olcDbIndex: entryUUID
+#indexdb#olcDbIndex: entryCSN
+#mdb#olcDbMaxSize: 33554432
+olcAccess: {0}to * by dn.base="cn=manager,dc=example,dc=com" read by * break
+olcSyncrepl: {0}rid=001 provider=@URI@ type=refreshAndPersist retry="10 +"
+ searchbase="dc=example,dc=com" scope=sub attrs="*,+" bindmethod=simple bind
+ dn="cn=manager,dc=example,dc=com" credentials=secret timeout=1
+olcMultiProvider: TRUE
+
+dn: olcOverlay={0}syncprov,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcSyncProvConfig
+olcOverlay: {0}syncprov
+olcSpCheckpoint: 100 10
diff --git a/tests/data/regressions/its9282/exampledb.ldif b/tests/data/regressions/its9282/exampledb.ldif
new file mode 100644
index 0000000..322a251
--- /dev/null
+++ b/tests/data/regressions/its9282/exampledb.ldif
@@ -0,0 +1,29032 @@
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: dcObject
+o: example
+dc: example
+
+dn: ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: organizationalunit
+ou: Accounting
+
+dn: ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: organizationalunit
+ou: Product Development
+
+dn: ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: organizationalunit
+ou: Product Testing
+
+dn: ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: organizationalunit
+ou: Human Resources
+
+dn: ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: organizationalunit
+ou: Payroll
+
+dn: ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: organizationalunit
+ou: Janitorial
+
+dn: ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: organizationalunit
+ou: Management
+
+dn: ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: organizationalunit
+ou: Administrative
+
+dn: ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: organizationalunit
+ou: Peons
+
+dn: ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: organizationalunit
+ou: Planning
+
+dn: ou=KerberosPrincipals, dc=example,dc=com
+objectClass: top
+objectClass: organizationalunit
+ou: KerberosPrincipals
+
+dn: cn=Katha Petree, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Katha Petree
+sn: Petree
+description: This is Katha Petree's description
+facsimileTelephoneNumber: +1 804 572-2449
+l: Milpitas
+ou: Peons
+postalAddress: example$Peons$Dept # 533
+telephoneNumber: +1 408 136-9364
+title: Supreme Peons President
+userPassword: eertePahta
+uid: Katha_Petree
+givenName: Katha
+mail: Katha_Petree@example.com
+carLicense: YIN9D3G
+departmentNumber: 2518
+employeeType: Employee
+homePhone: +1 303 811-5175
+initials: K. P.
+mobile: +1 510 729-3926
+pager: +1 804 321-1156
+manager: cn=Crissie Wayler
+secretary: cn=Mer Percy
+roomNumber: 9527
+
+dn: cn=Te-Wei Menashian, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Te-Wei Menashian
+sn: Menashian
+description: This is Te-Wei Menashian's description
+facsimileTelephoneNumber: +1 510 733-9612
+l: Emeryville
+ou: Peons
+postalAddress: example$Peons$Dept # 710
+telephoneNumber: +1 818 936-6205
+title: Senior Peons Sales Rep
+userPassword: naihsaneMi
+uid: Te-Wei_Menashian
+givenName: Te-Wei
+mail: Te-Wei_Menashian@example.com
+carLicense: E9DAV2R
+departmentNumber: 4375
+employeeType: Contract
+homePhone: +1 303 330-7311
+initials: T. M.
+mobile: +1 415 242-6860
+pager: +1 818 979-7582
+manager: cn=Deryck Gramiak
+secretary: cn=Emelyne Settels
+roomNumber: 1704
+
+dn: cn=Hung Nehring, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Hung Nehring
+sn: Nehring
+description: This is Hung Nehring's description
+facsimileTelephoneNumber: +1 804 930-6996
+l: San Jose
+ou: Product Development
+postalAddress: example$Product Development$Dept # 291
+telephoneNumber: +1 804 594-3030
+title: Associate Product Development Accountant
+userPassword: gnirheNgnu
+uid: Hung_Nehring
+givenName: Hung
+mail: Hung_Nehring@example.com
+carLicense: ZB0UL1J
+departmentNumber: 1687
+employeeType: Temp
+homePhone: +1 71 278-6297
+initials: H. N.
+mobile: +1 71 631-8088
+pager: +1 213 531-8152
+manager: cn=Mkt Silgardo
+secretary: cn=Kien-Nghiep McKeage
+roomNumber: 1611
+
+dn: cn=Grant Dransfield, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Grant Dransfield
+sn: Dransfield
+description: This is Grant Dransfield's description
+facsimileTelephoneNumber: +1 71 211-2977
+l: Fremont
+ou: Planning
+postalAddress: example$Planning$Dept # 384
+telephoneNumber: +1 206 134-2715
+title: Senior Planning Mascot
+userPassword: dleifsnarD
+uid: Grant_Dransfield
+givenName: Grant
+mail: Grant_Dransfield@example.com
+carLicense: S3E00VH
+departmentNumber: 6184
+employeeType: Employee
+homePhone: +1 510 360-6966
+initials: G. D.
+mobile: +1 71 658-3718
+pager: +1 804 923-4914
+manager: cn=Paulie Saisho
+secretary: cn=Sarette Valia
+roomNumber: 8490
+
+dn: cn=Greta Ifill, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Greta Ifill
+sn: Ifill
+description: This is Greta Ifill's description
+facsimileTelephoneNumber: +1 71 387-9510
+l: San Mateo
+ou: Product Development
+postalAddress: example$Product Development$Dept # 25
+telephoneNumber: +1 71 864-3915
+title: Chief Product Development Figurehead
+userPassword: llifIaterG
+uid: Greta_Ifill
+givenName: Greta
+mail: Greta_Ifill@example.com
+carLicense: T2TYGBB
+departmentNumber: 8443
+employeeType: Manager
+homePhone: +1 818 117-6606
+initials: G. I.
+mobile: +1 804 857-3067
+pager: +1 818 180-9782
+manager: cn=Pulak Locicero
+secretary: cn=Venkataraman Hurd
+roomNumber: 2397
+
+dn: cn=Ursa Kitzmiller, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ursa Kitzmiller
+sn: Kitzmiller
+description: This is Ursa Kitzmiller's description
+facsimileTelephoneNumber: +1 71 793-7201
+l: Redmond
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 293
+telephoneNumber: +1 818 107-3194
+title: Junior Janitorial Madonna
+userPassword: rellimztiK
+uid: Ursa_Kitzmiller
+givenName: Ursa
+mail: Ursa_Kitzmiller@example.com
+carLicense: 3WCAXAW
+departmentNumber: 4704
+employeeType: Contract
+homePhone: +1 415 399-6169
+initials: U. K.
+mobile: +1 804 365-7746
+pager: +1 408 927-5149
+manager: cn=Nedi Ashraf
+secretary: cn=Hedda Curley
+roomNumber: 1939
+
+dn: cn=Pammi Valente, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Pammi Valente
+sn: Valente
+description: This is Pammi Valente's description
+facsimileTelephoneNumber: +1 415 463-6393
+l: Milpitas
+ou: Planning
+postalAddress: example$Planning$Dept # 780
+telephoneNumber: +1 303 977-5996
+title: Elite Planning Consultant
+userPassword: etnelaVimm
+uid: Pammi_Valente
+givenName: Pammi
+mail: Pammi_Valente@example.com
+carLicense: YBQ9A2W
+departmentNumber: 5643
+employeeType: Employee
+homePhone: +1 804 224-3675
+initials: P. V.
+mobile: +1 415 431-6328
+pager: +1 213 684-9761
+manager: cn=Melisse Nelon
+secretary: cn=Gaby Ligon
+roomNumber: 3619
+
+dn: cn=Tineke Metler, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tineke Metler
+sn: Metler
+description: This is Tineke Metler's description
+facsimileTelephoneNumber: +1 510 510-1291
+l: San Mateo
+ou: Peons
+postalAddress: example$Peons$Dept # 36
+telephoneNumber: +1 415 934-5428
+title: Master Peons Admin
+userPassword: relteMeken
+uid: Tineke_Metler
+givenName: Tineke
+mail: Tineke_Metler@example.com
+carLicense: BBPPUFQ
+departmentNumber: 5007
+employeeType: Temp
+homePhone: +1 415 387-4578
+initials: T. M.
+mobile: +1 71 214-7101
+pager: +1 818 907-4547
+manager: cn=Andrei Hopley
+secretary: cn=Lian Wrigglesworth
+roomNumber: 6303
+
+dn: cn=Gleda Klamner, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gleda Klamner
+sn: Klamner
+description: This is Gleda Klamner's description
+facsimileTelephoneNumber: +1 804 506-9247
+l: Armonk
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 214
+telephoneNumber: +1 213 136-1420
+title: Chief Product Testing Figurehead
+userPassword: renmalKade
+uid: Gleda_Klamner
+givenName: Gleda
+mail: Gleda_Klamner@example.com
+carLicense: SMM8BG0
+departmentNumber: 6925
+employeeType: Temp
+homePhone: +1 213 179-6240
+initials: G. K.
+mobile: +1 303 485-9637
+pager: +1 510 470-8588
+manager: cn=Ginnie Todd
+secretary: cn=Nessie Buley
+roomNumber: 9564
+
+dn: cn=Starr Kasumovich, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Starr Kasumovich
+sn: Kasumovich
+description: This is Starr Kasumovich's description
+facsimileTelephoneNumber: +1 408 275-9420
+l: Alameda
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 113
+telephoneNumber: +1 804 885-9038
+title: Chief Janitorial Punk
+userPassword: hcivomusaK
+uid: Starr_Kasumovich
+givenName: Starr
+mail: Starr_Kasumovich@example.com
+carLicense: ED7JA85
+departmentNumber: 2291
+employeeType: Contract
+homePhone: +1 818 747-5196
+initials: S. K.
+mobile: +1 206 937-1390
+pager: +1 510 397-4977
+manager: cn=Marline Saul
+secretary: cn=Mildred Eggleton
+roomNumber: 6557
+
+dn: cn=Zhanna Briere, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Zhanna Briere
+sn: Briere
+description: This is Zhanna Briere's description
+facsimileTelephoneNumber: +1 510 732-4159
+l: San Francisco
+ou: Payroll
+postalAddress: example$Payroll$Dept # 612
+telephoneNumber: +1 206 509-2872
+title: Elite Payroll Consultant
+userPassword: ereirBanna
+uid: Zhanna_Briere
+givenName: Zhanna
+mail: Zhanna_Briere@example.com
+carLicense: JD7AX71
+departmentNumber: 4210
+employeeType: Temp
+homePhone: +1 804 227-1032
+initials: Z. B.
+mobile: +1 71 146-3040
+pager: +1 206 635-5658
+manager: cn=Katusha Brunton
+secretary: cn=Essie Hocutt
+roomNumber: 9432
+
+dn: cn=Dimitri Overby, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dimitri Overby
+sn: Overby
+description: This is Dimitri Overby's description
+facsimileTelephoneNumber: +1 71 767-6661
+l: Cupertino
+ou: Peons
+postalAddress: example$Peons$Dept # 572
+telephoneNumber: +1 303 149-9698
+title: Senior Peons Janitor
+userPassword: ybrevOirti
+uid: Dimitri_Overby
+givenName: Dimitri
+mail: Dimitri_Overby@example.com
+carLicense: LU1FF8A
+departmentNumber: 8962
+employeeType: Contract
+homePhone: +1 71 984-7586
+initials: D. O.
+mobile: +1 408 143-8879
+pager: +1 804 763-8868
+manager: cn=Reed Research
+secretary: cn=Dorice Hollandsworth
+roomNumber: 6199
+
+dn: cn=Farrand Bhandari, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Farrand Bhandari
+sn: Bhandari
+description: This is Farrand Bhandari's description
+facsimileTelephoneNumber: +1 804 494-2129
+l: Armonk
+ou: Product Development
+postalAddress: example$Product Development$Dept # 703
+telephoneNumber: +1 415 115-9048
+title: Chief Product Development Fellow
+userPassword: iradnahBdn
+uid: Farrand_Bhandari
+givenName: Farrand
+mail: Farrand_Bhandari@example.com
+carLicense: 37D7RP9
+departmentNumber: 8802
+employeeType: Contract
+homePhone: +1 408 325-2076
+initials: F. B.
+mobile: +1 303 869-5982
+pager: +1 206 701-5301
+manager: cn=Herronald Humphrey
+secretary: cn=Christel Arnon
+roomNumber: 9817
+
+dn: cn=Loralee Homayoun, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Loralee Homayoun
+sn: Homayoun
+description: This is Loralee Homayoun's description
+facsimileTelephoneNumber: +1 415 120-4563
+l: Orem
+ou: Product Development
+postalAddress: example$Product Development$Dept # 869
+telephoneNumber: +1 408 390-6261
+title: Elite Product Development Grunt
+userPassword: nuoyamoHee
+uid: Loralee_Homayoun
+givenName: Loralee
+mail: Loralee_Homayoun@example.com
+carLicense: B7UAB5A
+departmentNumber: 1016
+employeeType: Employee
+homePhone: +1 804 572-3652
+initials: L. H.
+mobile: +1 818 511-6642
+pager: +1 213 946-8035
+manager: cn=Michaella Frierson
+secretary: cn=Bernelle Hannah
+roomNumber: 761
+
+dn: cn=Ophelie Benedek, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ophelie Benedek
+sn: Benedek
+description: This is Ophelie Benedek's description
+facsimileTelephoneNumber: +1 510 372-2586
+l: Orem
+ou: Management
+postalAddress: example$Management$Dept # 307
+telephoneNumber: +1 804 720-3031
+title: Associate Management Manager
+userPassword: kedeneBeil
+uid: Ophelie_Benedek
+givenName: Ophelie
+mail: Ophelie_Benedek@example.com
+carLicense: XXB309G
+departmentNumber: 3029
+employeeType: Contract
+homePhone: +1 510 234-2942
+initials: O. B.
+mobile: +1 71 233-3279
+pager: +1 206 689-9631
+manager: cn=Noelyn Ramlogan
+secretary: cn=Alida Hanna
+roomNumber: 5444
+
+dn: cn=Justina Steeves, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Justina Steeves
+sn: Steeves
+description: This is Justina Steeves's description
+facsimileTelephoneNumber: +1 818 599-1461
+l: Cambridge
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 786
+telephoneNumber: +1 213 825-8065
+title: Senior Human Resources Consultant
+userPassword: seveetSani
+uid: Justina_Steeves
+givenName: Justina
+mail: Justina_Steeves@example.com
+carLicense: M5YX6UO
+departmentNumber: 1162
+employeeType: Temp
+homePhone: +1 510 209-9450
+initials: J. S.
+mobile: +1 71 203-4372
+pager: +1 71 508-6628
+manager: cn=Cathal Soumis
+secretary: cn=Dulce Modl
+roomNumber: 9123
+
+dn: cn=Marina Lemaire, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marina Lemaire
+sn: Lemaire
+description: This is Marina Lemaire's description
+facsimileTelephoneNumber: +1 71 485-6032
+l: Palo Alto
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 549
+telephoneNumber: +1 213 522-7876
+title: Chief Human Resources Czar
+userPassword: eriameLani
+uid: Marina_Lemaire
+givenName: Marina
+mail: Marina_Lemaire@example.com
+carLicense: RU6R1SJ
+departmentNumber: 5315
+employeeType: Manager
+homePhone: +1 818 245-4519
+initials: M. L.
+mobile: +1 213 388-6377
+pager: +1 71 509-9816
+manager: cn=Coriss Rynties
+secretary: cn=Pegeen Postlethwaite
+roomNumber: 2719
+
+dn: cn=Patches Derbyshire, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Patches Derbyshire
+sn: Derbyshire
+description: This is Patches Derbyshire's description
+facsimileTelephoneNumber: +1 415 501-8333
+l: San Jose
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 190
+telephoneNumber: +1 206 126-2236
+title: Junior Janitorial Mascot
+userPassword: erihsybreD
+uid: Patches_Derbyshire
+givenName: Patches
+mail: Patches_Derbyshire@example.com
+carLicense: TQD663V
+departmentNumber: 4594
+employeeType: Contract
+homePhone: +1 408 841-2501
+initials: P. D.
+mobile: +1 510 287-3008
+pager: +1 510 891-6326
+manager: cn=Ernestine Dias
+secretary: cn=Leanne Bivens
+roomNumber: 6527
+
+dn: cn=Tildy Hoskin, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tildy Hoskin
+sn: Hoskin
+description: This is Tildy Hoskin's description
+facsimileTelephoneNumber: +1 415 649-7690
+l: Santa Clara
+ou: Peons
+postalAddress: example$Peons$Dept # 312
+telephoneNumber: +1 408 344-5686
+title: Master Peons Yahoo
+userPassword: niksoHydli
+uid: Tildy_Hoskin
+givenName: Tildy
+mail: Tildy_Hoskin@example.com
+carLicense: QUK1330
+departmentNumber: 7288
+employeeType: Normal
+homePhone: +1 213 701-5449
+initials: T. H.
+mobile: +1 415 692-1013
+pager: +1 71 787-5332
+manager: cn=Wan Kramer
+secretary: cn=Cocos Hinchey
+roomNumber: 1357
+
+dn: cn=Lrc Melanson, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lrc Melanson
+sn: Melanson
+description: This is Lrc Melanson's description
+facsimileTelephoneNumber: +1 206 315-2540
+l: Orem
+ou: Administrative
+postalAddress: example$Administrative$Dept # 765
+telephoneNumber: +1 804 281-1079
+title: Elite Administrative President
+userPassword: nosnaleMcr
+uid: Lrc_Melanson
+givenName: Lrc
+mail: Lrc_Melanson@example.com
+carLicense: D5XAYYD
+departmentNumber: 2311
+employeeType: Contract
+homePhone: +1 804 633-5469
+initials: L. M.
+mobile: +1 303 663-9879
+pager: +1 804 308-9318
+manager: cn=Jordanna Thornton
+secretary: cn=Samuel Bawek
+roomNumber: 2439
+
+dn: cn=Bengt Quigley, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Bengt Quigley
+sn: Quigley
+description: This is Bengt Quigley's description
+facsimileTelephoneNumber: +1 213 914-4098
+l: Fremont
+ou: Planning
+postalAddress: example$Planning$Dept # 46
+telephoneNumber: +1 510 718-5158
+title: Master Planning Janitor
+userPassword: yelgiuQtgn
+uid: Bengt_Quigley
+givenName: Bengt
+mail: Bengt_Quigley@example.com
+carLicense: 9RKXD9Z
+departmentNumber: 5930
+employeeType: Employee
+homePhone: +1 303 989-2850
+initials: B. Q.
+mobile: +1 71 380-9979
+pager: +1 71 980-1170
+manager: cn=Rosina Verification
+secretary: cn=Antonie Barksdale
+roomNumber: 8018
+
+dn: cn=Jordanna FASTONE, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jordanna FASTONE
+sn: FASTONE
+description: This is Jordanna FASTONE's description
+facsimileTelephoneNumber: +1 71 158-1818
+l: Milpitas
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 637
+telephoneNumber: +1 804 928-6588
+title: Associate Human Resources Evangelist
+userPassword: ENOTSAFann
+uid: Jordanna_FASTONE
+givenName: Jordanna
+mail: Jordanna_FASTONE@example.com
+carLicense: SEPIWM6
+departmentNumber: 59
+employeeType: Normal
+homePhone: +1 71 396-4665
+initials: J. F.
+mobile: +1 213 466-1002
+pager: +1 303 261-1330
+manager: cn=Kiri Register
+secretary: cn=Wanda Urbick
+roomNumber: 1657
+
+dn: cn=Lilia Lalu, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lilia Lalu
+sn: Lalu
+description: This is Lilia Lalu's description
+facsimileTelephoneNumber: +1 804 550-2153
+l: Redwood Shores
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 436
+telephoneNumber: +1 206 165-2522
+title: Associate Product Testing Artist
+userPassword: ulaLailiL
+uid: Lilia_Lalu
+givenName: Lilia
+mail: Lilia_Lalu@example.com
+carLicense: D17UMBF
+departmentNumber: 5441
+employeeType: Temp
+homePhone: +1 71 298-3798
+initials: L. L.
+mobile: +1 510 529-2970
+pager: +1 71 655-2394
+manager: cn=Laverna Kearney
+secretary: cn=Ethelda Gaffney
+roomNumber: 6209
+
+dn: cn=Manami Varano, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Manami Varano
+sn: Varano
+description: This is Manami Varano's description
+facsimileTelephoneNumber: +1 818 724-3231
+l: Fremont
+ou: Administrative
+postalAddress: example$Administrative$Dept # 759
+telephoneNumber: +1 818 663-9466
+title: Chief Administrative Artist
+userPassword: onaraViman
+uid: Manami_Varano
+givenName: Manami
+mail: Manami_Varano@example.com
+carLicense: VDYGUJ1
+departmentNumber: 2127
+employeeType: Temp
+homePhone: +1 818 115-2547
+initials: M. V.
+mobile: +1 415 950-1894
+pager: +1 213 491-2978
+manager: cn=Ulla Sirevicius
+secretary: cn=Norma Dwyer
+roomNumber: 7220
+
+dn: cn=Nike Dorn, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Nike Dorn
+sn: Dorn
+description: This is Nike Dorn's description
+facsimileTelephoneNumber: +1 818 538-9631
+l: Emeryville
+ou: Planning
+postalAddress: example$Planning$Dept # 109
+telephoneNumber: +1 71 308-5243
+title: Associate Planning Pinhead
+userPassword: nroDekiN
+uid: Nike_Dorn
+givenName: Nike
+mail: Nike_Dorn@example.com
+carLicense: 1UAP19L
+departmentNumber: 1302
+employeeType: Normal
+homePhone: +1 510 512-3464
+initials: N. D.
+mobile: +1 71 484-2404
+pager: +1 303 101-4105
+manager: cn=Kirstie Bartholomew
+secretary: cn=Nady Shirai
+roomNumber: 2603
+
+dn: cn=Denys Cooper, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Denys Cooper
+sn: Cooper
+description: This is Denys Cooper's description
+facsimileTelephoneNumber: +1 71 560-5439
+l: Palo Alto
+ou: Payroll
+postalAddress: example$Payroll$Dept # 36
+telephoneNumber: +1 818 205-1817
+title: Elite Payroll Grunt
+userPassword: repooCsyne
+uid: Denys_Cooper
+givenName: Denys
+mail: Denys_Cooper@example.com
+carLicense: 7MYC39T
+departmentNumber: 2073
+employeeType: Contract
+homePhone: +1 408 908-8099
+initials: D. C.
+mobile: +1 71 315-8213
+pager: +1 213 589-9770
+manager: cn=Kishore Denison
+secretary: cn=Daphene Dragan
+roomNumber: 7412
+
+dn: cn=Marjan Lukassen, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marjan Lukassen
+sn: Lukassen
+description: This is Marjan Lukassen's description
+facsimileTelephoneNumber: +1 213 952-1994
+l: San Mateo
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 919
+telephoneNumber: +1 408 188-6244
+title: Elite Janitorial Developer
+userPassword: nessakuLna
+uid: Marjan_Lukassen
+givenName: Marjan
+mail: Marjan_Lukassen@example.com
+carLicense: QO7KGQI
+departmentNumber: 1650
+employeeType: Manager
+homePhone: +1 206 500-9364
+initials: M. L.
+mobile: +1 415 161-6485
+pager: +1 303 227-1193
+manager: cn=Truus Hoelscher
+secretary: cn=Jacquelyn Roseland
+roomNumber: 4938
+
+dn: cn=Salis Lundhild, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Salis Lundhild
+sn: Lundhild
+description: This is Salis Lundhild's description
+facsimileTelephoneNumber: +1 213 604-7319
+l: Orem
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 246
+telephoneNumber: +1 415 804-9623
+title: Supreme Janitorial Vice President
+userPassword: dlihdnuLsi
+uid: Salis_Lundhild
+givenName: Salis
+mail: Salis_Lundhild@example.com
+carLicense: YI55DU4
+departmentNumber: 844
+employeeType: Manager
+homePhone: +1 510 223-2526
+initials: S. L.
+mobile: +1 408 422-8947
+pager: +1 206 384-4202
+manager: cn=Shirin Cech
+secretary: cn=Cherilyn Croxford
+roomNumber: 5201
+
+dn: cn=Penang Nava, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Penang Nava
+sn: Nava
+description: This is Penang Nava's description
+facsimileTelephoneNumber: +1 818 691-4786
+l: Sunnyvale
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 718
+telephoneNumber: +1 213 146-9070
+title: Senior Human Resources Developer
+userPassword: avaNgnaneP
+uid: Penang_Nava
+givenName: Penang
+mail: Penang_Nava@example.com
+carLicense: U434FGT
+departmentNumber: 7310
+employeeType: Manager
+homePhone: +1 303 258-9102
+initials: P. N.
+mobile: +1 206 778-4551
+pager: +1 213 829-2714
+manager: cn=Julius Satkunaseelan
+secretary: cn=Kynthia Dziamba
+roomNumber: 4716
+
+dn: cn=Pieter Nilakantan, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Pieter Nilakantan
+sn: Nilakantan
+description: This is Pieter Nilakantan's description
+facsimileTelephoneNumber: +1 510 891-1705
+l: Menlo Park
+ou: Administrative
+postalAddress: example$Administrative$Dept # 730
+telephoneNumber: +1 213 227-9259
+title: Master Administrative Evangelist
+userPassword: natnakaliN
+uid: Pieter_Nilakantan
+givenName: Pieter
+mail: Pieter_Nilakantan@example.com
+carLicense: FH3QN9L
+departmentNumber: 268
+employeeType: Employee
+homePhone: +1 804 822-6008
+initials: P. N.
+mobile: +1 206 453-3297
+pager: +1 818 647-1304
+manager: cn=Claus Myroon
+secretary: cn=Rec Mickens
+roomNumber: 5653
+
+dn: cn=Nooshin Kramar, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Nooshin Kramar
+sn: Kramar
+description: This is Nooshin Kramar's description
+facsimileTelephoneNumber: +1 408 392-5438
+l: Milpitas
+ou: Planning
+postalAddress: example$Planning$Dept # 380
+telephoneNumber: +1 510 679-5782
+title: Chief Planning Architect
+userPassword: ramarKnihs
+uid: Nooshin_Kramar
+givenName: Nooshin
+mail: Nooshin_Kramar@example.com
+carLicense: TBHGVGS
+departmentNumber: 402
+employeeType: Contract
+homePhone: +1 415 977-5735
+initials: N. K.
+mobile: +1 510 410-8688
+pager: +1 804 667-5477
+manager: cn=Sam Schaller
+secretary: cn=Shandeigh Massoudian
+roomNumber: 2658
+
+dn: cn=Sky Szypulski, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sky Szypulski
+sn: Szypulski
+description: This is Sky Szypulski's description
+facsimileTelephoneNumber: +1 206 123-9549
+l: Menlo Park
+ou: Planning
+postalAddress: example$Planning$Dept # 521
+telephoneNumber: +1 303 401-9309
+title: Master Planning Vice President
+userPassword: ikslupyzSy
+uid: Sky_Szypulski
+givenName: Sky
+mail: Sky_Szypulski@example.com
+carLicense: H43RZD7
+departmentNumber: 446
+employeeType: Contract
+homePhone: +1 804 570-7137
+initials: S. S.
+mobile: +1 206 442-7052
+pager: +1 510 446-9661
+manager: cn=Quyen Hubley
+secretary: cn=Clement Unxlb
+roomNumber: 3846
+
+dn: cn=Eoin Dilen, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Eoin Dilen
+sn: Dilen
+description: This is Eoin Dilen's description
+facsimileTelephoneNumber: +1 818 800-9230
+l: Fremont
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 415
+telephoneNumber: +1 818 689-3752
+title: Supreme Human Resources Admin
+userPassword: neliDnioE
+uid: Eoin_Dilen
+givenName: Eoin
+mail: Eoin_Dilen@example.com
+carLicense: BPWEE2T
+departmentNumber: 1111
+employeeType: Employee
+homePhone: +1 71 172-1790
+initials: E. D.
+mobile: +1 510 558-9742
+pager: +1 415 406-8107
+manager: cn=Isaac Kurylyk
+secretary: cn=Nanette Guin
+roomNumber: 88
+
+dn: cn=Angie Quattrucci, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Angie Quattrucci
+sn: Quattrucci
+description: This is Angie Quattrucci's description
+facsimileTelephoneNumber: +1 818 951-9124
+l: Menlo Park
+ou: Peons
+postalAddress: example$Peons$Dept # 840
+telephoneNumber: +1 71 658-5962
+title: Elite Peons Visionary
+userPassword: iccurttauQ
+uid: Angie_Quattrucci
+givenName: Angie
+mail: Angie_Quattrucci@example.com
+carLicense: P63ZFCZ
+departmentNumber: 6809
+employeeType: Manager
+homePhone: +1 510 354-4073
+initials: A. Q.
+mobile: +1 303 915-7613
+pager: +1 510 473-5228
+manager: cn=Gwynith Corbeil
+secretary: cn=Cyb SYS
+roomNumber: 1434
+
+dn: cn=Carree Colton, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Carree Colton
+sn: Colton
+description: This is Carree Colton's description
+facsimileTelephoneNumber: +1 206 518-6934
+l: Cambridge
+ou: Administrative
+postalAddress: example$Administrative$Dept # 341
+telephoneNumber: +1 408 205-5357
+title: Elite Administrative Vice President
+userPassword: notloCeerr
+uid: Carree_Colton
+givenName: Carree
+mail: Carree_Colton@example.com
+carLicense: CHOQMJS
+departmentNumber: 1724
+employeeType: Normal
+homePhone: +1 303 706-3439
+initials: C. C.
+mobile: +1 510 333-2875
+pager: +1 408 431-1579
+manager: cn=Ernestine Wadasinghe
+secretary: cn=Zonda Logarajah
+roomNumber: 8840
+
+dn: cn=Stacey Cuffling, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Stacey Cuffling
+sn: Cuffling
+description: This is Stacey Cuffling's description
+facsimileTelephoneNumber: +1 303 959-6773
+l: Milpitas
+ou: Payroll
+postalAddress: example$Payroll$Dept # 658
+telephoneNumber: +1 206 294-7855
+title: Supreme Payroll Janitor
+userPassword: gnilffuCye
+uid: Stacey_Cuffling
+givenName: Stacey
+mail: Stacey_Cuffling@example.com
+carLicense: TUXZ3MU
+departmentNumber: 340
+employeeType: Contract
+homePhone: +1 206 830-7198
+initials: S. C.
+mobile: +1 213 199-3217
+pager: +1 206 919-2006
+manager: cn=Connie Mulqueen
+secretary: cn=Shobana Goold
+roomNumber: 2950
+
+dn: cn=Lenka Recycling, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lenka Recycling
+sn: Recycling
+description: This is Lenka Recycling's description
+facsimileTelephoneNumber: +1 415 302-8519
+l: Redwood Shores
+ou: Peons
+postalAddress: example$Peons$Dept # 285
+telephoneNumber: +1 408 465-7183
+title: Senior Peons Accountant
+userPassword: gnilcyceRa
+uid: Lenka_Recycling
+givenName: Lenka
+mail: Lenka_Recycling@example.com
+carLicense: E4K4WQS
+departmentNumber: 5130
+employeeType: Contract
+homePhone: +1 213 417-5706
+initials: L. R.
+mobile: +1 71 263-5791
+pager: +1 206 115-7598
+manager: cn=Wanids Decker
+secretary: cn=Wits ETAS
+roomNumber: 9709
+
+dn: cn=Audry Tsunoda, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Audry Tsunoda
+sn: Tsunoda
+description: This is Audry Tsunoda's description
+facsimileTelephoneNumber: +1 804 684-2111
+l: San Jose
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 145
+telephoneNumber: +1 303 267-1575
+title: Supreme Human Resources Developer
+userPassword: adonusTyrd
+uid: Audry_Tsunoda
+givenName: Audry
+mail: Audry_Tsunoda@example.com
+carLicense: AS9127F
+departmentNumber: 6349
+employeeType: Temp
+homePhone: +1 71 373-6012
+initials: A. T.
+mobile: +1 71 708-6730
+pager: +1 408 374-6781
+manager: cn=Nadim Kuhlkamp
+secretary: cn=Miguela Nyce
+roomNumber: 1979
+
+dn: cn=Marena Mastellar, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marena Mastellar
+sn: Mastellar
+description: This is Marena Mastellar's description
+facsimileTelephoneNumber: +1 804 172-1820
+l: Milpitas
+ou: Payroll
+postalAddress: example$Payroll$Dept # 550
+telephoneNumber: +1 303 414-3131
+title: Senior Payroll Architect
+userPassword: ralletsaMa
+uid: Marena_Mastellar
+givenName: Marena
+mail: Marena_Mastellar@example.com
+carLicense: PM1DW3J
+departmentNumber: 446
+employeeType: Employee
+homePhone: +1 804 379-6259
+initials: M. M.
+mobile: +1 804 138-3415
+pager: +1 510 190-7433
+manager: cn=Gino Shyu
+secretary: cn=Van Katcher
+roomNumber: 8573
+
+dn: cn=Sephira Beaudet, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sephira Beaudet
+sn: Beaudet
+description: This is Sephira Beaudet's description
+facsimileTelephoneNumber: +1 804 618-2331
+l: San Mateo
+ou: Planning
+postalAddress: example$Planning$Dept # 192
+telephoneNumber: +1 408 162-3755
+title: Junior Planning President
+userPassword: teduaeBari
+uid: Sephira_Beaudet
+givenName: Sephira
+mail: Sephira_Beaudet@example.com
+carLicense: 9ETUUM5
+departmentNumber: 7153
+employeeType: Manager
+homePhone: +1 804 313-4133
+initials: S. B.
+mobile: +1 510 749-2995
+pager: +1 206 570-8482
+manager: cn=Josey Plastina
+secretary: cn=Parkinson Cutrufello
+roomNumber: 1227
+
+dn: cn=Toyoji Zottola, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Toyoji Zottola
+sn: Zottola
+description: This is Toyoji Zottola's description
+facsimileTelephoneNumber: +1 206 989-7405
+l: Menlo Park
+ou: Product Development
+postalAddress: example$Product Development$Dept # 435
+telephoneNumber: +1 818 610-3160
+title: Supreme Product Development Sales Rep
+userPassword: alottoZijo
+uid: Toyoji_Zottola
+givenName: Toyoji
+mail: Toyoji_Zottola@example.com
+carLicense: D6D5MT9
+departmentNumber: 9953
+employeeType: Contract
+homePhone: +1 213 678-4694
+initials: T. Z.
+mobile: +1 510 885-4076
+pager: +1 71 493-4359
+manager: cn=Isl Homa
+secretary: cn=Stateson Raynard
+roomNumber: 3891
+
+dn: cn=Coors Moree, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Coors Moree
+sn: Moree
+description: This is Coors Moree's description
+facsimileTelephoneNumber: +1 408 813-6240
+l: San Mateo
+ou: Accounting
+postalAddress: example$Accounting$Dept # 646
+telephoneNumber: +1 303 836-3845
+title: Supreme Accounting Assistant
+userPassword: eeroMsrooC
+uid: Coors_Moree
+givenName: Coors
+mail: Coors_Moree@example.com
+carLicense: O89NVOS
+departmentNumber: 5578
+employeeType: Manager
+homePhone: +1 415 113-4325
+initials: C. M.
+mobile: +1 818 512-9511
+pager: +1 415 974-5164
+manager: cn=Marvell Zeidler
+secretary: cn=Ilise Dubeau
+roomNumber: 7984
+
+dn: cn=Cornelius Hazelton, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Cornelius Hazelton
+sn: Hazelton
+description: This is Cornelius Hazelton's description
+facsimileTelephoneNumber: +1 818 667-1576
+l: Cupertino
+ou: Peons
+postalAddress: example$Peons$Dept # 837
+telephoneNumber: +1 415 777-3658
+title: Chief Peons Technician
+userPassword: notlezaHsu
+uid: Cornelius_Hazelton
+givenName: Cornelius
+mail: Cornelius_Hazelton@example.com
+carLicense: 7DZ9RCQ
+departmentNumber: 7943
+employeeType: Employee
+homePhone: +1 206 366-3619
+initials: C. H.
+mobile: +1 206 627-1023
+pager: +1 213 268-2112
+manager: cn=Leia Chasse
+secretary: cn=Junia Eleftheriou
+roomNumber: 3119
+
+dn: cn=Byron Evers, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Byron Evers
+sn: Evers
+description: This is Byron Evers's description
+facsimileTelephoneNumber: +1 804 497-6758
+l: San Jose
+ou: Accounting
+postalAddress: example$Accounting$Dept # 357
+telephoneNumber: +1 206 803-9974
+title: Elite Accounting Developer
+userPassword: srevEnoryB
+uid: Byron_Evers
+givenName: Byron
+mail: Byron_Evers@example.com
+carLicense: PA9UYBD
+departmentNumber: 3329
+employeeType: Normal
+homePhone: +1 818 676-8405
+initials: B. E.
+mobile: +1 818 716-3538
+pager: +1 206 437-6113
+manager: cn=Janenna Bourque
+secretary: cn=Staci De Boer
+roomNumber: 6310
+
+dn: cn=Happy Gates, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Happy Gates
+sn: Gates
+description: This is Happy Gates's description
+facsimileTelephoneNumber: +1 303 627-9787
+l: Cupertino
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 398
+telephoneNumber: +1 510 242-2971
+title: Master Human Resources Consultant
+userPassword: setaGyppaH
+uid: Happy_Gates
+givenName: Happy
+mail: Happy_Gates@example.com
+carLicense: 35K5S1O
+departmentNumber: 2027
+employeeType: Temp
+homePhone: +1 303 955-6641
+initials: H. G.
+mobile: +1 408 167-2225
+pager: +1 213 514-1012
+manager: cn=Moyra Cascarini
+secretary: cn=Bellina Skillen
+roomNumber: 2341
+
+dn: cn=Uta McMannen, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Uta McMannen
+sn: McMannen
+description: This is Uta McMannen's description
+facsimileTelephoneNumber: +1 71 495-9676
+l: Redmond
+ou: Management
+postalAddress: example$Management$Dept # 443
+telephoneNumber: +1 213 393-1818
+title: Elite Management Writer
+userPassword: nennaMcMat
+uid: Uta_McMannen
+givenName: Uta
+mail: Uta_McMannen@example.com
+carLicense: R8MU4OO
+departmentNumber: 4555
+employeeType: Manager
+homePhone: +1 408 883-8577
+initials: U. M.
+mobile: +1 818 736-6007
+pager: +1 206 874-6837
+manager: cn=Alissa Tello
+secretary: cn=Mahesh Zenkevicius
+roomNumber: 8182
+
+dn: cn=Ty Stevens, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ty Stevens
+sn: Stevens
+description: This is Ty Stevens's description
+facsimileTelephoneNumber: +1 213 318-3148
+l: Fremont
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 699
+telephoneNumber: +1 408 250-9862
+title: Chief Human Resources Sales Rep
+userPassword: snevetSyT
+uid: Ty_Stevens
+givenName: Ty
+mail: Ty_Stevens@example.com
+carLicense: AUKD2Z3
+departmentNumber: 9795
+employeeType: Normal
+homePhone: +1 206 586-9013
+initials: T. S.
+mobile: +1 510 919-6750
+pager: +1 71 807-1102
+manager: cn=Stafani Hayes
+secretary: cn=Cassandra Applications
+roomNumber: 6916
+
+dn: cn=Kapsch Panter, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kapsch Panter
+sn: Panter
+description: This is Kapsch Panter's description
+facsimileTelephoneNumber: +1 804 892-6408
+l: Menlo Park
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 234
+telephoneNumber: +1 303 633-5806
+title: Master Product Testing President
+userPassword: retnaPhcsp
+uid: Kapsch_Panter
+givenName: Kapsch
+mail: Kapsch_Panter@example.com
+carLicense: T4QCGI8
+departmentNumber: 4234
+employeeType: Manager
+homePhone: +1 206 715-8979
+initials: K. P.
+mobile: +1 206 838-5130
+pager: +1 415 874-2777
+manager: cn=Estella Beauvais
+secretary: cn=Melisandra Gomm
+roomNumber: 6615
+
+dn: cn=Allie Linegar, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Allie Linegar
+sn: Linegar
+description: This is Allie Linegar's description
+facsimileTelephoneNumber: +1 303 669-1524
+l: Fremont
+ou: Product Development
+postalAddress: example$Product Development$Dept # 457
+telephoneNumber: +1 206 317-5935
+title: Master Product Development Artist
+userPassword: rageniLeil
+uid: Allie_Linegar
+givenName: Allie
+mail: Allie_Linegar@example.com
+carLicense: 366L4CY
+departmentNumber: 7139
+employeeType: Normal
+homePhone: +1 415 999-2443
+initials: A. L.
+mobile: +1 213 628-9505
+pager: +1 415 836-1152
+manager: cn=Viva Obermyer
+secretary: cn=Iteke Ripa
+roomNumber: 4909
+
+dn: cn=Elisa Jasmin, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Elisa Jasmin
+sn: Jasmin
+description: This is Elisa Jasmin's description
+facsimileTelephoneNumber: +1 408 968-2146
+l: Emeryville
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 988
+telephoneNumber: +1 206 270-1329
+title: Junior Product Testing Janitor
+userPassword: nimsaJasil
+uid: Elisa_Jasmin
+givenName: Elisa
+mail: Elisa_Jasmin@example.com
+carLicense: BI5VLJ8
+departmentNumber: 6992
+employeeType: Temp
+homePhone: +1 71 892-4047
+initials: E. J.
+mobile: +1 415 203-3869
+pager: +1 303 703-2221
+manager: cn=Katha Bilsborough
+secretary: cn=Beatrice Mobley
+roomNumber: 3108
+
+dn: cn=Mauro Guimond, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Mauro Guimond
+sn: Guimond
+description: This is Mauro Guimond's description
+facsimileTelephoneNumber: +1 804 786-4314
+l: Emeryville
+ou: Administrative
+postalAddress: example$Administrative$Dept # 353
+telephoneNumber: +1 415 750-7749
+title: Chief Administrative Punk
+userPassword: dnomiuGoru
+uid: Mauro_Guimond
+givenName: Mauro
+mail: Mauro_Guimond@example.com
+carLicense: CSOB28C
+departmentNumber: 7802
+employeeType: Manager
+homePhone: +1 303 155-6935
+initials: M. G.
+mobile: +1 213 293-5543
+pager: +1 818 921-7041
+manager: cn=Pete Broberg
+secretary: cn=Jagjeet Rasmus
+roomNumber: 3246
+
+dn: cn=Ermengarde Schick, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ermengarde Schick
+sn: Schick
+description: This is Ermengarde Schick's description
+facsimileTelephoneNumber: +1 510 685-2099
+l: Sunnyvale
+ou: Planning
+postalAddress: example$Planning$Dept # 642
+telephoneNumber: +1 510 765-1652
+title: Master Planning Artist
+userPassword: kcihcSedra
+uid: Ermengarde_Schick
+givenName: Ermengarde
+mail: Ermengarde_Schick@example.com
+carLicense: 1EMVOQB
+departmentNumber: 8429
+employeeType: Normal
+homePhone: +1 408 556-2369
+initials: E. S.
+mobile: +1 71 789-2658
+pager: +1 206 381-7041
+manager: cn=Marie-Josee McDowell
+secretary: cn=Tine Beaudry
+roomNumber: 8490
+
+dn: cn=Goldia Korpela, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Goldia Korpela
+sn: Korpela
+description: This is Goldia Korpela's description
+facsimileTelephoneNumber: +1 510 910-7839
+l: Cambridge
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 351
+telephoneNumber: +1 408 745-4848
+title: Associate Janitorial Admin
+userPassword: aleproKaid
+uid: Goldia_Korpela
+givenName: Goldia
+mail: Goldia_Korpela@example.com
+carLicense: US4BNF8
+departmentNumber: 5578
+employeeType: Manager
+homePhone: +1 213 513-3767
+initials: G. K.
+mobile: +1 510 821-7500
+pager: +1 71 850-8225
+manager: cn=Bertrand Davalo
+secretary: cn=Dante Dalrymple
+roomNumber: 2711
+
+dn: cn=Takis Bour, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Takis Bour
+sn: Bour
+description: This is Takis Bour's description
+facsimileTelephoneNumber: +1 206 328-7272
+l: Fremont
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 325
+telephoneNumber: +1 804 550-7887
+title: Senior Product Testing Janitor
+userPassword: ruoBsikaT
+uid: Takis_Bour
+givenName: Takis
+mail: Takis_Bour@example.com
+carLicense: JHOHQNO
+departmentNumber: 1708
+employeeType: Manager
+homePhone: +1 213 966-8094
+initials: T. B.
+mobile: +1 510 244-3496
+pager: +1 71 234-5371
+manager: cn=Shiela Puckett
+secretary: cn=Silvestro Ritter
+roomNumber: 8997
+
+dn: cn=Dixie Bourdin, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dixie Bourdin
+sn: Bourdin
+description: This is Dixie Bourdin's description
+facsimileTelephoneNumber: +1 510 585-8546
+l: Mountain View
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 457
+telephoneNumber: +1 206 634-6581
+title: Junior Human Resources Sales Rep
+userPassword: nidruoBeix
+uid: Dixie_Bourdin
+givenName: Dixie
+mail: Dixie_Bourdin@example.com
+carLicense: YK0J5T7
+departmentNumber: 9790
+employeeType: Temp
+homePhone: +1 71 462-8278
+initials: D. B.
+mobile: +1 408 510-4697
+pager: +1 213 741-2133
+manager: cn=Charyl Nava
+secretary: cn=Dolley Puglia
+roomNumber: 5734
+
+dn: cn=Keven Rychlicki, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Keven Rychlicki
+sn: Rychlicki
+description: This is Keven Rychlicki's description
+facsimileTelephoneNumber: +1 303 124-8353
+l: San Jose
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 124
+telephoneNumber: +1 804 191-1531
+title: Elite Human Resources Grunt
+userPassword: ikcilhcyRn
+uid: Keven_Rychlicki
+givenName: Keven
+mail: Keven_Rychlicki@example.com
+carLicense: ZBLG4RV
+departmentNumber: 834
+employeeType: Contract
+homePhone: +1 71 567-3412
+initials: K. R.
+mobile: +1 71 968-3996
+pager: +1 303 731-9082
+manager: cn=Mindy Goodman
+secretary: cn=Mignonne Shayanpour
+roomNumber: 7098
+
+dn: cn=Donall Rantala, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Donall Rantala
+sn: Rantala
+description: This is Donall Rantala's description
+facsimileTelephoneNumber: +1 408 303-4522
+l: Milpitas
+ou: Payroll
+postalAddress: example$Payroll$Dept # 328
+telephoneNumber: +1 71 301-6813
+title: Chief Payroll Visionary
+userPassword: alatnaRlla
+uid: Donall_Rantala
+givenName: Donall
+mail: Donall_Rantala@example.com
+carLicense: MX4TUQ6
+departmentNumber: 9947
+employeeType: Normal
+homePhone: +1 303 864-6341
+initials: D. R.
+mobile: +1 818 165-2183
+pager: +1 71 401-5780
+manager: cn=Devonne Polashock
+secretary: cn=Anabel Campeau
+roomNumber: 4308
+
+dn: cn=Marline Klotz, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marline Klotz
+sn: Klotz
+description: This is Marline Klotz's description
+facsimileTelephoneNumber: +1 510 983-5629
+l: Orem
+ou: Payroll
+postalAddress: example$Payroll$Dept # 407
+telephoneNumber: +1 818 268-9539
+title: Supreme Payroll Czar
+userPassword: ztolKenilr
+uid: Marline_Klotz
+givenName: Marline
+mail: Marline_Klotz@example.com
+carLicense: 32KPAL0
+departmentNumber: 2403
+employeeType: Temp
+homePhone: +1 71 872-9086
+initials: M. K.
+mobile: +1 415 594-1651
+pager: +1 415 917-6733
+manager: cn=Elleke Register
+secretary: cn=Milka Lesniak
+roomNumber: 7758
+
+dn: cn=Fekri Visockis, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Fekri Visockis
+sn: Visockis
+description: This is Fekri Visockis's description
+facsimileTelephoneNumber: +1 71 137-3361
+l: Orem
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 237
+telephoneNumber: +1 71 956-3730
+title: Supreme Product Testing Grunt
+userPassword: sikcosiVir
+uid: Fekri_Visockis
+givenName: Fekri
+mail: Fekri_Visockis@example.com
+carLicense: 7TMONCZ
+departmentNumber: 5655
+employeeType: Contract
+homePhone: +1 408 533-4693
+initials: F. V.
+mobile: +1 408 940-7060
+pager: +1 71 816-8956
+manager: cn=Shina Benjamin
+secretary: cn=Anallese Kupe
+roomNumber: 7880
+
+dn: cn=Candace PATCOR, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Candace PATCOR
+sn: PATCOR
+description: This is Candace PATCOR's description
+facsimileTelephoneNumber: +1 206 131-7351
+l: Redmond
+ou: Administrative
+postalAddress: example$Administrative$Dept # 864
+telephoneNumber: +1 206 845-3696
+title: Elite Administrative Warrior
+userPassword: ROCTAPecad
+uid: Candace_PATCOR
+givenName: Candace
+mail: Candace_PATCOR@example.com
+carLicense: 5KEJAI8
+departmentNumber: 3432
+employeeType: Contract
+homePhone: +1 206 601-9313
+initials: C. P.
+mobile: +1 213 775-9778
+pager: +1 206 781-9544
+manager: cn=Amir Michalos
+secretary: cn=Marieann Hearnden
+roomNumber: 4467
+
+dn: cn=Truus McKinlay, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Truus McKinlay
+sn: McKinlay
+description: This is Truus McKinlay's description
+facsimileTelephoneNumber: +1 408 195-6650
+l: Sunnyvale
+ou: Product Development
+postalAddress: example$Product Development$Dept # 17
+telephoneNumber: +1 818 325-8417
+title: Associate Product Development Accountant
+userPassword: yalniKcMsu
+uid: Truus_McKinlay
+givenName: Truus
+mail: Truus_McKinlay@example.com
+carLicense: QIEZFB2
+departmentNumber: 1541
+employeeType: Employee
+homePhone: +1 303 526-8978
+initials: T. M.
+mobile: +1 206 450-3967
+pager: +1 71 431-6521
+manager: cn=Federica Nigam
+secretary: cn=Trudey Berryhill
+roomNumber: 6635
+
+dn: cn=Marie-Josee Sanders, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marie-Josee Sanders
+sn: Sanders
+description: This is Marie-Josee Sanders's description
+facsimileTelephoneNumber: +1 71 525-1361
+l: Mountain View
+ou: Planning
+postalAddress: example$Planning$Dept # 79
+telephoneNumber: +1 510 349-7810
+title: Master Planning President
+userPassword: srednaSees
+uid: Marie-Josee_Sanders
+givenName: Marie-Josee
+mail: Marie-Josee_Sanders@example.com
+carLicense: FYWUH72
+departmentNumber: 7976
+employeeType: Temp
+homePhone: +1 213 628-9235
+initials: M. S.
+mobile: +1 408 871-9747
+pager: +1 206 539-8562
+manager: cn=Ardelle Russett
+secretary: cn=Fabienne Fetting
+roomNumber: 3962
+
+dn: cn=Alpine Mullaney, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Alpine Mullaney
+sn: Mullaney
+description: This is Alpine Mullaney's description
+facsimileTelephoneNumber: +1 510 900-8598
+l: Redwood Shores
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 226
+telephoneNumber: +1 206 993-8923
+title: Junior Product Testing Sales Rep
+userPassword: yenalluMen
+uid: Alpine_Mullaney
+givenName: Alpine
+mail: Alpine_Mullaney@example.com
+carLicense: WGBX355
+departmentNumber: 3067
+employeeType: Employee
+homePhone: +1 213 952-3838
+initials: A. M.
+mobile: +1 510 868-4334
+pager: +1 818 976-7889
+manager: cn=Mala Pepin
+secretary: cn=Mauricio Serbin
+roomNumber: 7803
+
+dn: cn=Jirina Ketterer, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jirina Ketterer
+sn: Ketterer
+description: This is Jirina Ketterer's description
+facsimileTelephoneNumber: +1 71 141-9174
+l: Palo Alto
+ou: Administrative
+postalAddress: example$Administrative$Dept # 893
+telephoneNumber: +1 510 466-4173
+title: Supreme Administrative Director
+userPassword: reretteKan
+uid: Jirina_Ketterer
+givenName: Jirina
+mail: Jirina_Ketterer@example.com
+carLicense: W3ATUY9
+departmentNumber: 3226
+employeeType: Employee
+homePhone: +1 818 218-1098
+initials: J. K.
+mobile: +1 213 679-1507
+pager: +1 303 561-2677
+manager: cn=Sher Spohn
+secretary: cn=Brande Dube
+roomNumber: 3148
+
+dn: cn=Desire Shames, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Desire Shames
+sn: Shames
+description: This is Desire Shames's description
+facsimileTelephoneNumber: +1 408 385-6057
+l: Cupertino
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 86
+telephoneNumber: +1 804 126-3732
+title: Supreme Product Testing Grunt
+userPassword: semahSeris
+uid: Desire_Shames
+givenName: Desire
+mail: Desire_Shames@example.com
+carLicense: 27NFE5O
+departmentNumber: 6240
+employeeType: Temp
+homePhone: +1 408 300-7065
+initials: D. S.
+mobile: +1 71 159-1833
+pager: +1 818 820-1159
+manager: cn=Kim-Tram Grimshaw
+secretary: cn=Randie Burgi
+roomNumber: 5357
+
+dn: cn=Farra Boles, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Farra Boles
+sn: Boles
+description: This is Farra Boles's description
+facsimileTelephoneNumber: +1 303 186-3696
+l: Cupertino
+ou: Product Development
+postalAddress: example$Product Development$Dept # 832
+telephoneNumber: +1 415 451-5315
+title: Junior Product Development Yahoo
+userPassword: seloBarraF
+uid: Farra_Boles
+givenName: Farra
+mail: Farra_Boles@example.com
+carLicense: VGCU9QH
+departmentNumber: 548
+employeeType: Normal
+homePhone: +1 818 924-1171
+initials: F. B.
+mobile: +1 213 317-5535
+pager: +1 206 429-2114
+manager: cn=Sofeya Attenborough
+secretary: cn=Kelwin Strider
+roomNumber: 4776
+
+dn: cn=Edmx Beaty, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Edmx Beaty
+sn: Beaty
+description: This is Edmx Beaty's description
+facsimileTelephoneNumber: +1 408 460-7625
+l: San Mateo
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 331
+telephoneNumber: +1 303 427-6104
+title: Chief Janitorial Vice President
+userPassword: ytaeBxmdE
+uid: Edmx_Beaty
+givenName: Edmx
+mail: Edmx_Beaty@example.com
+carLicense: 4XOW1HI
+departmentNumber: 1279
+employeeType: Contract
+homePhone: +1 206 683-8029
+initials: E. B.
+mobile: +1 303 400-9306
+pager: +1 408 324-7874
+manager: cn=Jilleen Funston
+secretary: cn=Chick Bulifant
+roomNumber: 389
+
+dn: cn=Arshad Pridgen, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Arshad Pridgen
+sn: Pridgen
+description: This is Arshad Pridgen's description
+facsimileTelephoneNumber: +1 303 515-5437
+l: Alameda
+ou: Accounting
+postalAddress: example$Accounting$Dept # 403
+telephoneNumber: +1 408 223-8594
+title: Master Accounting Architect
+userPassword: negdirPdah
+uid: Arshad_Pridgen
+givenName: Arshad
+mail: Arshad_Pridgen@example.com
+carLicense: D28KMCL
+departmentNumber: 2137
+employeeType: Temp
+homePhone: +1 303 973-8037
+initials: A. P.
+mobile: +1 213 495-7493
+pager: +1 408 755-4594
+manager: cn=Henri Moebes
+secretary: cn=Madelaine Abdo
+roomNumber: 8913
+
+dn: cn=Elfreda Lavers, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Elfreda Lavers
+sn: Lavers
+description: This is Elfreda Lavers's description
+facsimileTelephoneNumber: +1 510 469-9254
+l: San Francisco
+ou: Administrative
+postalAddress: example$Administrative$Dept # 156
+telephoneNumber: +1 206 395-9094
+title: Master Administrative Consultant
+userPassword: srevaLader
+uid: Elfreda_Lavers
+givenName: Elfreda
+mail: Elfreda_Lavers@example.com
+carLicense: ZB0NSRM
+departmentNumber: 5851
+employeeType: Contract
+homePhone: +1 415 646-9727
+initials: E. L.
+mobile: +1 415 713-2127
+pager: +1 71 459-2508
+manager: cn=Viv Beveridge
+secretary: cn=Candace Norby
+roomNumber: 4356
+
+dn: cn=Shirley-Ann Killam, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Shirley-Ann Killam
+sn: Killam
+description: This is Shirley-Ann Killam's description
+facsimileTelephoneNumber: +1 818 694-5222
+l: Santa Clara
+ou: Payroll
+postalAddress: example$Payroll$Dept # 905
+telephoneNumber: +1 818 273-7131
+title: Chief Payroll Manager
+userPassword: malliKnnA-
+uid: Shirley-Ann_Killam
+givenName: Shirley-Ann
+mail: Shirley-Ann_Killam@example.com
+carLicense: 3JDUXZN
+departmentNumber: 3391
+employeeType: Employee
+homePhone: +1 213 809-9622
+initials: S. K.
+mobile: +1 415 600-2402
+pager: +1 804 244-2185
+manager: cn=Danica Newell
+secretary: cn=Kristine Hazell
+roomNumber: 3785
+
+dn: cn=Bertrand Gause, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Bertrand Gause
+sn: Gause
+description: This is Bertrand Gause's description
+facsimileTelephoneNumber: +1 415 315-1909
+l: Alameda
+ou: Product Development
+postalAddress: example$Product Development$Dept # 464
+telephoneNumber: +1 510 847-5638
+title: Supreme Product Development Fellow
+userPassword: esuaGdnart
+uid: Bertrand_Gause
+givenName: Bertrand
+mail: Bertrand_Gause@example.com
+carLicense: 8RL8VQ7
+departmentNumber: 6491
+employeeType: Employee
+homePhone: +1 303 369-1227
+initials: B. G.
+mobile: +1 71 834-6359
+pager: +1 303 452-4725
+manager: cn=Ashoka Shamblin
+secretary: cn=Bel Network-Ops
+roomNumber: 4026
+
+dn: cn=Ronnie Naem, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ronnie Naem
+sn: Naem
+description: This is Ronnie Naem's description
+facsimileTelephoneNumber: +1 303 725-3898
+l: Cambridge
+ou: Administrative
+postalAddress: example$Administrative$Dept # 546
+telephoneNumber: +1 206 546-8134
+title: Associate Administrative Manager
+userPassword: meaNeinnoR
+uid: Ronnie_Naem
+givenName: Ronnie
+mail: Ronnie_Naem@example.com
+carLicense: 6IN2K0T
+departmentNumber: 8511
+employeeType: Manager
+homePhone: +1 408 635-4147
+initials: R. N.
+mobile: +1 206 261-5574
+pager: +1 510 247-6002
+manager: cn=Ladell Kudrewatych
+secretary: cn=Wilkin Millette
+roomNumber: 932
+
+dn: cn=Pardip Hendrickse, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Pardip Hendrickse
+sn: Hendrickse
+description: This is Pardip Hendrickse's description
+facsimileTelephoneNumber: +1 804 478-9034
+l: Palo Alto
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 944
+telephoneNumber: +1 206 746-3144
+title: Chief Product Testing Figurehead
+userPassword: eskcirdneH
+uid: Pardip_Hendrickse
+givenName: Pardip
+mail: Pardip_Hendrickse@example.com
+carLicense: J3YMHFM
+departmentNumber: 5834
+employeeType: Normal
+homePhone: +1 510 846-8385
+initials: P. H.
+mobile: +1 415 967-9127
+pager: +1 71 547-6822
+manager: cn=Nerty Hyndman
+secretary: cn=Duane Vasile
+roomNumber: 9014
+
+dn: cn=Allen Forecasting, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Allen Forecasting
+sn: Forecasting
+description: This is Allen Forecasting's description
+facsimileTelephoneNumber: +1 818 812-7241
+l: Mountain View
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 846
+telephoneNumber: +1 510 130-7532
+title: Associate Janitorial Architect
+userPassword: gnitsacero
+uid: Allen_Forecasting
+givenName: Allen
+mail: Allen_Forecasting@example.com
+carLicense: CRR07CE
+departmentNumber: 5949
+employeeType: Manager
+homePhone: +1 408 285-1016
+initials: A. F.
+mobile: +1 206 738-2006
+pager: +1 510 746-7114
+manager: cn=Catriona Sanderson
+secretary: cn=Konstance Slotnick
+roomNumber: 4639
+
+dn: cn=Fred Whitlock, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Fred Whitlock
+sn: Whitlock
+description: This is Fred Whitlock's description
+facsimileTelephoneNumber: +1 206 352-1952
+l: Redwood Shores
+ou: Accounting
+postalAddress: example$Accounting$Dept # 433
+telephoneNumber: +1 213 597-2051
+title: Junior Accounting Stooge
+userPassword: kcoltihWde
+uid: Fred_Whitlock
+givenName: Fred
+mail: Fred_Whitlock@example.com
+carLicense: V1DEMN9
+departmentNumber: 7174
+employeeType: Temp
+homePhone: +1 71 946-4846
+initials: F. W.
+mobile: +1 213 432-1110
+pager: +1 510 957-6711
+manager: cn=Shinichiro Wingar
+secretary: cn=Saeed Moorer
+roomNumber: 4124
+
+dn: cn=Mervyn Applications, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Mervyn Applications
+sn: Applications
+description: This is Mervyn Applications's description
+facsimileTelephoneNumber: +1 804 261-3157
+l: San Francisco
+ou: Administrative
+postalAddress: example$Administrative$Dept # 524
+telephoneNumber: +1 71 413-5934
+title: Associate Administrative Punk
+userPassword: snoitacilp
+uid: Mervyn_Applications
+givenName: Mervyn
+mail: Mervyn_Applications@example.com
+carLicense: 36BYATU
+departmentNumber: 4942
+employeeType: Employee
+homePhone: +1 818 207-4366
+initials: M. A.
+mobile: +1 415 655-2093
+pager: +1 213 251-3236
+manager: cn=Rafaelita Tamasi
+secretary: cn=Ineke Marcellus
+roomNumber: 2876
+
+dn: cn=Carlis Poulos, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Carlis Poulos
+sn: Poulos
+description: This is Carlis Poulos's description
+facsimileTelephoneNumber: +1 415 675-6465
+l: Orem
+ou: Product Development
+postalAddress: example$Product Development$Dept # 94
+telephoneNumber: +1 415 483-9097
+title: Supreme Product Development Developer
+userPassword: soluoPsilr
+uid: Carlis_Poulos
+givenName: Carlis
+mail: Carlis_Poulos@example.com
+carLicense: Y1KXSLV
+departmentNumber: 5357
+employeeType: Contract
+homePhone: +1 71 357-8932
+initials: C. P.
+mobile: +1 408 878-8956
+pager: +1 408 836-4803
+manager: cn=Ketti Bahoric
+secretary: cn=Hulda Ludwick
+roomNumber: 3005
+
+dn: cn=Doralyn Cracknell, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Doralyn Cracknell
+sn: Cracknell
+description: This is Doralyn Cracknell's description
+facsimileTelephoneNumber: +1 408 771-3063
+l: Armonk
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 114
+telephoneNumber: +1 213 521-1169
+title: Chief Janitorial Technician
+userPassword: llenkcarCn
+uid: Doralyn_Cracknell
+givenName: Doralyn
+mail: Doralyn_Cracknell@example.com
+carLicense: NNBPX46
+departmentNumber: 8129
+employeeType: Temp
+homePhone: +1 415 570-8371
+initials: D. C.
+mobile: +1 804 660-5466
+pager: +1 71 388-8815
+manager: cn=Sue Ching
+secretary: cn=Thomasina Zolmer
+roomNumber: 5462
+
+dn: cn=Gray Daniels, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gray Daniels
+sn: Daniels
+description: This is Gray Daniels's description
+facsimileTelephoneNumber: +1 71 326-6034
+l: San Jose
+ou: Accounting
+postalAddress: example$Accounting$Dept # 334
+telephoneNumber: +1 415 800-8060
+title: Elite Accounting Dictator
+userPassword: sleinaDyar
+uid: Gray_Daniels
+givenName: Gray
+mail: Gray_Daniels@example.com
+carLicense: DKL6Z8V
+departmentNumber: 2834
+employeeType: Employee
+homePhone: +1 804 364-6354
+initials: G. D.
+mobile: +1 804 851-3507
+pager: +1 71 155-5635
+manager: cn=Danielle Potter
+secretary: cn=Sheilah Vilmansen
+roomNumber: 586
+
+dn: cn=Shari Bhasin, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Shari Bhasin
+sn: Bhasin
+description: This is Shari Bhasin's description
+facsimileTelephoneNumber: +1 303 648-5011
+l: Cambridge
+ou: Administrative
+postalAddress: example$Administrative$Dept # 354
+telephoneNumber: +1 510 520-4823
+title: Elite Administrative Punk
+userPassword: nisahBirah
+uid: Shari_Bhasin
+givenName: Shari
+mail: Shari_Bhasin@example.com
+carLicense: T31G80F
+departmentNumber: 2088
+employeeType: Normal
+homePhone: +1 408 119-3492
+initials: S. B.
+mobile: +1 415 321-1103
+pager: +1 510 601-4952
+manager: cn=Danit Evans
+secretary: cn=Monique Wessels
+roomNumber: 3363
+
+dn: cn=Bess McNamara, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Bess McNamara
+sn: McNamara
+description: This is Bess McNamara's description
+facsimileTelephoneNumber: +1 804 814-6509
+l: Sunnyvale
+ou: Accounting
+postalAddress: example$Accounting$Dept # 671
+telephoneNumber: +1 510 411-1693
+title: Elite Accounting Janitor
+userPassword: aramaNcMss
+uid: Bess_McNamara
+givenName: Bess
+mail: Bess_McNamara@example.com
+carLicense: G12WSLR
+departmentNumber: 611
+employeeType: Manager
+homePhone: +1 71 363-1248
+initials: B. M.
+mobile: +1 206 575-4956
+pager: +1 510 613-8294
+manager: cn=Gelais Ikeda
+secretary: cn=Esam Adimari
+roomNumber: 3261
+
+dn: cn=Shaylyn Sharratt, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Shaylyn Sharratt
+sn: Sharratt
+description: This is Shaylyn Sharratt's description
+facsimileTelephoneNumber: +1 818 680-5587
+l: Orem
+ou: Administrative
+postalAddress: example$Administrative$Dept # 819
+telephoneNumber: +1 408 918-8301
+title: Associate Administrative President
+userPassword: ttarrahSny
+uid: Shaylyn_Sharratt
+givenName: Shaylyn
+mail: Shaylyn_Sharratt@example.com
+carLicense: SI9N0ZQ
+departmentNumber: 1064
+employeeType: Normal
+homePhone: +1 213 653-4304
+initials: S. S.
+mobile: +1 213 652-1817
+pager: +1 804 754-1963
+manager: cn=Doug Dyna
+secretary: cn=Marquita Engelhart
+roomNumber: 9955
+
+dn: cn=Belen Kindel, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Belen Kindel
+sn: Kindel
+description: This is Belen Kindel's description
+facsimileTelephoneNumber: +1 804 347-4747
+l: San Francisco
+ou: Peons
+postalAddress: example$Peons$Dept # 140
+telephoneNumber: +1 408 393-7079
+title: Associate Peons Grunt
+userPassword: ledniKnele
+uid: Belen_Kindel
+givenName: Belen
+mail: Belen_Kindel@example.com
+carLicense: YXBFXG6
+departmentNumber: 4615
+employeeType: Normal
+homePhone: +1 71 632-1119
+initials: B. K.
+mobile: +1 415 362-8247
+pager: +1 804 853-1114
+manager: cn=Kipp Piche
+secretary: cn=Tine Lapchak
+roomNumber: 1447
+
+dn: cn=Vivia Orders, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Vivia Orders
+sn: Orders
+description: This is Vivia Orders's description
+facsimileTelephoneNumber: +1 818 238-4580
+l: San Mateo
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 110
+telephoneNumber: +1 71 423-6695
+title: Junior Janitorial Director
+userPassword: sredrOaivi
+uid: Vivia_Orders
+givenName: Vivia
+mail: Vivia_Orders@example.com
+carLicense: N0GIZG1
+departmentNumber: 3173
+employeeType: Manager
+homePhone: +1 415 181-8474
+initials: V. O.
+mobile: +1 818 553-5255
+pager: +1 510 323-6318
+manager: cn=Jerrilyn Ragland
+secretary: cn=Jacques Gould
+roomNumber: 9210
+
+dn: cn=Ajit Haas, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ajit Haas
+sn: Haas
+description: This is Ajit Haas's description
+facsimileTelephoneNumber: +1 71 520-4965
+l: Sunnyvale
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 789
+telephoneNumber: +1 510 389-9823
+title: Elite Product Testing Vice President
+userPassword: saaHtijA
+uid: Ajit_Haas
+givenName: Ajit
+mail: Ajit_Haas@example.com
+carLicense: ZPG5SB0
+departmentNumber: 7319
+employeeType: Manager
+homePhone: +1 303 478-5253
+initials: A. H.
+mobile: +1 415 432-4102
+pager: +1 804 459-9086
+manager: cn=Adriena Nemec
+secretary: cn=Sallee Heinzman
+roomNumber: 2814
+
+dn: cn=Anastassia Kurylyk, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Anastassia Kurylyk
+sn: Kurylyk
+description: This is Anastassia Kurylyk's description
+facsimileTelephoneNumber: +1 71 624-2992
+l: Cambridge
+ou: Administrative
+postalAddress: example$Administrative$Dept # 201
+telephoneNumber: +1 71 188-9159
+title: Junior Administrative Admin
+userPassword: kylyruKais
+uid: Anastassia_Kurylyk
+givenName: Anastassia
+mail: Anastassia_Kurylyk@example.com
+carLicense: 8FP9RJ0
+departmentNumber: 6526
+employeeType: Employee
+homePhone: +1 818 915-4509
+initials: A. K.
+mobile: +1 71 614-9963
+pager: +1 206 340-8900
+manager: cn=Sherline Zubans
+secretary: cn=Lilith Gouhara
+roomNumber: 6144
+
+dn: cn=Zainab Cholette, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Zainab Cholette
+sn: Cholette
+description: This is Zainab Cholette's description
+facsimileTelephoneNumber: +1 510 897-8790
+l: Cupertino
+ou: Peons
+postalAddress: example$Peons$Dept # 731
+telephoneNumber: +1 71 723-3944
+title: Master Peons Czar
+userPassword: ettelohCba
+uid: Zainab_Cholette
+givenName: Zainab
+mail: Zainab_Cholette@example.com
+carLicense: SVQX1AW
+departmentNumber: 1204
+employeeType: Contract
+homePhone: +1 415 990-5494
+initials: Z. C.
+mobile: +1 303 827-4839
+pager: +1 408 796-1288
+manager: cn=Bethanne Palmer
+secretary: cn=Camilla Schenck
+roomNumber: 9840
+
+dn: cn=Kevina Sayer, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kevina Sayer
+sn: Sayer
+description: This is Kevina Sayer's description
+facsimileTelephoneNumber: +1 510 845-1787
+l: Redmond
+ou: Administrative
+postalAddress: example$Administrative$Dept # 24
+telephoneNumber: +1 408 848-6391
+title: Junior Administrative Director
+userPassword: reyaSanive
+uid: Kevina_Sayer
+givenName: Kevina
+mail: Kevina_Sayer@example.com
+carLicense: ZQYCZJV
+departmentNumber: 8861
+employeeType: Contract
+homePhone: +1 71 338-5161
+initials: K. S.
+mobile: +1 510 321-5364
+pager: +1 213 655-8043
+manager: cn=Karisa Geary
+secretary: cn=Vicuong Hurtado
+roomNumber: 9905
+
+dn: cn=Charissa Bourguignon, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Charissa Bourguignon
+sn: Bourguignon
+description: This is Charissa Bourguignon's description
+facsimileTelephoneNumber: +1 818 206-6968
+l: Milpitas
+ou: Product Development
+postalAddress: example$Product Development$Dept # 334
+telephoneNumber: +1 818 356-7277
+title: Master Product Development Manager
+userPassword: nongiugruo
+uid: Charissa_Bourguignon
+givenName: Charissa
+mail: Charissa_Bourguignon@example.com
+carLicense: IJIQ4FU
+departmentNumber: 3392
+employeeType: Normal
+homePhone: +1 818 329-4249
+initials: C. B.
+mobile: +1 818 990-2558
+pager: +1 415 356-6834
+manager: cn=Alfonzo Vigeant
+secretary: cn=Ruthann Costadimas
+roomNumber: 6193
+
+dn: cn=Stesha Kolk, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Stesha Kolk
+sn: Kolk
+description: This is Stesha Kolk's description
+facsimileTelephoneNumber: +1 71 625-9907
+l: Sunnyvale
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 924
+telephoneNumber: +1 71 678-2046
+title: Associate Janitorial Madonna
+userPassword: kloKahsetS
+uid: Stesha_Kolk
+givenName: Stesha
+mail: Stesha_Kolk@example.com
+carLicense: 8MM8997
+departmentNumber: 2179
+employeeType: Manager
+homePhone: +1 206 732-6542
+initials: S. K.
+mobile: +1 206 767-2832
+pager: +1 510 735-6377
+manager: cn=Naser Vucinich
+secretary: cn=Bill Hasen
+roomNumber: 8698
+
+dn: cn=Den Van Vrouwerff, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Den Van Vrouwerff
+sn: Van Vrouwerff
+description: This is Den Van Vrouwerff's description
+facsimileTelephoneNumber: +1 213 670-2974
+l: Menlo Park
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 58
+telephoneNumber: +1 213 206-7058
+title: Associate Janitorial Sales Rep
+userPassword: ffrewuorVn
+uid: Den_Van Vrouwerff
+givenName: Den
+mail: Den_Van Vrouwerff@example.com
+carLicense: 15HPUA7
+departmentNumber: 8462
+employeeType: Contract
+homePhone: +1 804 229-2581
+initials: D. V.
+mobile: +1 408 959-9170
+pager: +1 818 941-8681
+manager: cn=Usa Decker
+secretary: cn=Kippy Rutter
+roomNumber: 6499
+
+dn: cn=Phylys Tripps, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Phylys Tripps
+sn: Tripps
+description: This is Phylys Tripps's description
+facsimileTelephoneNumber: +1 213 806-1298
+l: San Francisco
+ou: Peons
+postalAddress: example$Peons$Dept # 263
+telephoneNumber: +1 510 355-3414
+title: Elite Peons President
+userPassword: sppirTsyly
+uid: Phylys_Tripps
+givenName: Phylys
+mail: Phylys_Tripps@example.com
+carLicense: 3CZN75N
+departmentNumber: 7178
+employeeType: Temp
+homePhone: +1 303 633-2086
+initials: P. T.
+mobile: +1 804 161-1091
+pager: +1 510 148-9098
+manager: cn=Onette Dalton
+secretary: cn=Jeannine Kennaday
+roomNumber: 6754
+
+dn: cn=Nana Bleile, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Nana Bleile
+sn: Bleile
+description: This is Nana Bleile's description
+facsimileTelephoneNumber: +1 415 575-4766
+l: Sunnyvale
+ou: Product Development
+postalAddress: example$Product Development$Dept # 260
+telephoneNumber: +1 206 410-7593
+title: Chief Product Development Evangelist
+userPassword: elielBanaN
+uid: Nana_Bleile
+givenName: Nana
+mail: Nana_Bleile@example.com
+carLicense: 5OD820R
+departmentNumber: 7648
+employeeType: Temp
+homePhone: +1 303 948-3739
+initials: N. B.
+mobile: +1 71 232-4963
+pager: +1 804 995-4537
+manager: cn=Ashley Sufcak
+secretary: cn=Antonia Leiker
+roomNumber: 7074
+
+dn: cn=Maryam Zegray, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Maryam Zegray
+sn: Zegray
+description: This is Maryam Zegray's description
+facsimileTelephoneNumber: +1 303 896-7882
+l: Cambridge
+ou: Administrative
+postalAddress: example$Administrative$Dept # 800
+telephoneNumber: +1 71 384-8261
+title: Associate Administrative Warrior
+userPassword: yargeZmayr
+uid: Maryam_Zegray
+givenName: Maryam
+mail: Maryam_Zegray@example.com
+carLicense: 7PE0BSL
+departmentNumber: 4858
+employeeType: Temp
+homePhone: +1 804 847-3265
+initials: M. Z.
+mobile: +1 804 537-5963
+pager: +1 408 189-4710
+manager: cn=Cindelyn Tabor
+secretary: cn=Ashley Duguay
+roomNumber: 5756
+
+dn: cn=Thomas Grafton, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Thomas Grafton
+sn: Grafton
+description: This is Thomas Grafton's description
+facsimileTelephoneNumber: +1 303 860-3939
+l: Redmond
+ou: Planning
+postalAddress: example$Planning$Dept # 685
+telephoneNumber: +1 818 525-4686
+title: Supreme Planning Artist
+userPassword: notfarGsam
+uid: Thomas_Grafton
+givenName: Thomas
+mail: Thomas_Grafton@example.com
+carLicense: CM7B2YA
+departmentNumber: 6226
+employeeType: Manager
+homePhone: +1 818 148-2665
+initials: T. G.
+mobile: +1 303 129-4914
+pager: +1 804 372-4422
+manager: cn=Duquette Langenberg
+secretary: cn=Alanah Panke
+roomNumber: 4969
+
+dn: cn=Alyse Vastine, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Alyse Vastine
+sn: Vastine
+description: This is Alyse Vastine's description
+facsimileTelephoneNumber: +1 408 728-8488
+l: Milpitas
+ou: Accounting
+postalAddress: example$Accounting$Dept # 738
+telephoneNumber: +1 415 283-5336
+title: Master Accounting Writer
+userPassword: enitsaVesy
+uid: Alyse_Vastine
+givenName: Alyse
+mail: Alyse_Vastine@example.com
+carLicense: SVFFSBQ
+departmentNumber: 8200
+employeeType: Temp
+homePhone: +1 818 115-1404
+initials: A. V.
+mobile: +1 408 624-9737
+pager: +1 213 701-5457
+manager: cn=Doyle Brunelle
+secretary: cn=Hqs Marrone
+roomNumber: 5128
+
+dn: cn=Jessika Shirley, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jessika Shirley
+sn: Shirley
+description: This is Jessika Shirley's description
+facsimileTelephoneNumber: +1 303 132-8975
+l: Menlo Park
+ou: Administrative
+postalAddress: example$Administrative$Dept # 418
+telephoneNumber: +1 415 482-9480
+title: Elite Administrative President
+userPassword: yelrihSaki
+uid: Jessika_Shirley
+givenName: Jessika
+mail: Jessika_Shirley@example.com
+carLicense: NN9B3MS
+departmentNumber: 5551
+employeeType: Manager
+homePhone: +1 510 420-8465
+initials: J. S.
+mobile: +1 206 549-8539
+pager: +1 71 246-9558
+manager: cn=Austin Wray
+secretary: cn=Dael Rao
+roomNumber: 2852
+
+dn: cn=Purnam Wilsey, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Purnam Wilsey
+sn: Wilsey
+description: This is Purnam Wilsey's description
+facsimileTelephoneNumber: +1 213 548-5517
+l: Milpitas
+ou: Planning
+postalAddress: example$Planning$Dept # 20
+telephoneNumber: +1 206 413-4448
+title: Elite Planning Assistant
+userPassword: yesliWmanr
+uid: Purnam_Wilsey
+givenName: Purnam
+mail: Purnam_Wilsey@example.com
+carLicense: 4THLTBM
+departmentNumber: 9063
+employeeType: Contract
+homePhone: +1 818 107-8788
+initials: P. W.
+mobile: +1 206 869-1238
+pager: +1 818 423-3448
+manager: cn=Goldina Van Eyk
+secretary: cn=Sheila-kathryn Teague
+roomNumber: 6208
+
+dn: cn=Diamond Dejongh, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Diamond Dejongh
+sn: Dejongh
+description: This is Diamond Dejongh's description
+facsimileTelephoneNumber: +1 415 690-7520
+l: Redmond
+ou: Peons
+postalAddress: example$Peons$Dept # 399
+telephoneNumber: +1 71 359-2616
+title: Supreme Peons Technician
+userPassword: hgnojeDdno
+uid: Diamond_Dejongh
+givenName: Diamond
+mail: Diamond_Dejongh@example.com
+carLicense: T4M7LKD
+departmentNumber: 713
+employeeType: Manager
+homePhone: +1 213 789-8779
+initials: D. D.
+mobile: +1 818 552-2619
+pager: +1 510 205-2550
+manager: cn=Seema Ong
+secretary: cn=Gabie Sturrock
+roomNumber: 1724
+
+dn: cn=Meridian Milotte, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Meridian Milotte
+sn: Milotte
+description: This is Meridian Milotte's description
+facsimileTelephoneNumber: +1 818 998-6569
+l: Milpitas
+ou: Accounting
+postalAddress: example$Accounting$Dept # 788
+telephoneNumber: +1 71 833-1231
+title: Elite Accounting Artist
+userPassword: ettoliMnai
+uid: Meridian_Milotte
+givenName: Meridian
+mail: Meridian_Milotte@example.com
+carLicense: S6PQA01
+departmentNumber: 8153
+employeeType: Temp
+homePhone: +1 206 306-3546
+initials: M. M.
+mobile: +1 510 315-6592
+pager: +1 415 677-4723
+manager: cn=Holli Wojcik
+secretary: cn=Florida Labarge
+roomNumber: 3260
+
+dn: cn=Clark Soto, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Clark Soto
+sn: Soto
+description: This is Clark Soto's description
+facsimileTelephoneNumber: +1 510 450-7054
+l: San Francisco
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 395
+telephoneNumber: +1 415 574-1770
+title: Junior Janitorial Sales Rep
+userPassword: otoSkralC
+uid: Clark_Soto
+givenName: Clark
+mail: Clark_Soto@example.com
+carLicense: PPTYC3Z
+departmentNumber: 7897
+employeeType: Employee
+homePhone: +1 804 304-2863
+initials: C. S.
+mobile: +1 206 393-4632
+pager: +1 510 651-1646
+manager: cn=Gerianne Skrebels
+secretary: cn=Vrouwerff Komenda
+roomNumber: 5847
+
+dn: cn=Anurag Gores, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Anurag Gores
+sn: Gores
+description: This is Anurag Gores's description
+facsimileTelephoneNumber: +1 415 579-3094
+l: San Mateo
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 499
+telephoneNumber: +1 415 489-1448
+title: Elite Product Testing Fellow
+userPassword: seroGgarun
+uid: Anurag_Gores
+givenName: Anurag
+mail: Anurag_Gores@example.com
+carLicense: JXH0SUV
+departmentNumber: 2859
+employeeType: Temp
+homePhone: +1 804 967-3243
+initials: A. G.
+mobile: +1 408 376-8981
+pager: +1 818 769-2160
+manager: cn=Carry Wennerstrom
+secretary: cn=Crawford Ayukawa
+roomNumber: 8359
+
+dn: cn=Priti Mathus, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Priti Mathus
+sn: Mathus
+description: This is Priti Mathus's description
+facsimileTelephoneNumber: +1 303 909-3247
+l: Fremont
+ou: Payroll
+postalAddress: example$Payroll$Dept # 139
+telephoneNumber: +1 213 812-5903
+title: Supreme Payroll Figurehead
+userPassword: suhtaMitir
+uid: Priti_Mathus
+givenName: Priti
+mail: Priti_Mathus@example.com
+carLicense: QMTZUJ6
+departmentNumber: 5971
+employeeType: Temp
+homePhone: +1 818 121-6367
+initials: P. M.
+mobile: +1 415 618-5833
+pager: +1 213 617-7627
+manager: cn=Aruna Harris
+secretary: cn=Minnie Dolan
+roomNumber: 8375
+
+dn: cn=Joann Tencer, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Joann Tencer
+sn: Tencer
+description: This is Joann Tencer's description
+facsimileTelephoneNumber: +1 818 235-3432
+l: Mountain View
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 76
+telephoneNumber: +1 206 423-4983
+title: Junior Janitorial Vice President
+userPassword: recneTnnao
+uid: Joann_Tencer
+givenName: Joann
+mail: Joann_Tencer@example.com
+carLicense: S2AWHAJ
+departmentNumber: 2653
+employeeType: Manager
+homePhone: +1 206 345-3712
+initials: J. T.
+mobile: +1 510 810-5835
+pager: +1 804 253-7958
+manager: cn=Nolie Deslandes
+secretary: cn=Aubrey Feldberg
+roomNumber: 6014
+
+dn: cn=Stephani Prevost, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Stephani Prevost
+sn: Prevost
+description: This is Stephani Prevost's description
+facsimileTelephoneNumber: +1 510 611-6202
+l: Cambridge
+ou: Payroll
+postalAddress: example$Payroll$Dept # 324
+telephoneNumber: +1 415 322-7383
+title: Chief Payroll Janitor
+userPassword: tsoverPina
+uid: Stephani_Prevost
+givenName: Stephani
+mail: Stephani_Prevost@example.com
+carLicense: NL8S1ZS
+departmentNumber: 3383
+employeeType: Contract
+homePhone: +1 213 181-6675
+initials: S. P.
+mobile: +1 213 978-9750
+pager: +1 818 996-4936
+manager: cn=Pcta Ceponis
+secretary: cn=Antoine Groleau
+roomNumber: 5810
+
+dn: cn=Anneliese Baldridge, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Anneliese Baldridge
+sn: Baldridge
+description: This is Anneliese Baldridge's description
+facsimileTelephoneNumber: +1 408 317-2040
+l: Milpitas
+ou: Product Development
+postalAddress: example$Product Development$Dept # 415
+telephoneNumber: +1 206 685-7272
+title: Associate Product Development Writer
+userPassword: egdirdlaBe
+uid: Anneliese_Baldridge
+givenName: Anneliese
+mail: Anneliese_Baldridge@example.com
+carLicense: YI622JD
+departmentNumber: 8362
+employeeType: Manager
+homePhone: +1 818 759-4260
+initials: A. B.
+mobile: +1 818 488-6138
+pager: +1 71 997-6404
+manager: cn=Jirina Knorr
+secretary: cn=Jayesh Malhotra
+roomNumber: 5925
+
+dn: cn=Katya Kimler, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Katya Kimler
+sn: Kimler
+description: This is Katya Kimler's description
+facsimileTelephoneNumber: +1 303 952-3948
+l: Menlo Park
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 937
+telephoneNumber: +1 818 356-2917
+title: Associate Human Resources Consultant
+userPassword: relmiKayta
+uid: Katya_Kimler
+givenName: Katya
+mail: Katya_Kimler@example.com
+carLicense: 9S3RWOV
+departmentNumber: 2807
+employeeType: Normal
+homePhone: +1 408 516-1513
+initials: K. K.
+mobile: +1 213 484-2095
+pager: +1 206 723-1364
+manager: cn=Perle Antinucci
+secretary: cn=Nicol Hartleb
+roomNumber: 8148
+
+dn: cn=Balaji Chilton, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Balaji Chilton
+sn: Chilton
+description: This is Balaji Chilton's description
+facsimileTelephoneNumber: +1 510 735-7327
+l: Sunnyvale
+ou: Payroll
+postalAddress: example$Payroll$Dept # 549
+telephoneNumber: +1 510 980-2807
+title: Master Payroll Developer
+userPassword: notlihCija
+uid: Balaji_Chilton
+givenName: Balaji
+mail: Balaji_Chilton@example.com
+carLicense: YGOOWNV
+departmentNumber: 3737
+employeeType: Manager
+homePhone: +1 408 239-5774
+initials: B. C.
+mobile: +1 415 976-9429
+pager: +1 510 528-4585
+manager: cn=Grant Selic
+secretary: cn=Ianthe Jonkheer
+roomNumber: 7077
+
+dn: cn=Arabella Luetchford, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Arabella Luetchford
+sn: Luetchford
+description: This is Arabella Luetchford's description
+facsimileTelephoneNumber: +1 213 471-7402
+l: Redmond
+ou: Product Development
+postalAddress: example$Product Development$Dept # 138
+telephoneNumber: +1 510 641-6260
+title: Elite Product Development Czar
+userPassword: drofhcteuL
+uid: Arabella_Luetchford
+givenName: Arabella
+mail: Arabella_Luetchford@example.com
+carLicense: SUBK5US
+departmentNumber: 1005
+employeeType: Contract
+homePhone: +1 415 445-2918
+initials: A. L.
+mobile: +1 408 308-7725
+pager: +1 804 427-9393
+manager: cn=Gaylene Wieland
+secretary: cn=Jade Fredette
+roomNumber: 3428
+
+dn: cn=Elsa Lytle, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Elsa Lytle
+sn: Lytle
+description: This is Elsa Lytle's description
+facsimileTelephoneNumber: +1 818 332-6087
+l: Santa Clara
+ou: Payroll
+postalAddress: example$Payroll$Dept # 547
+telephoneNumber: +1 415 147-3266
+title: Chief Payroll Dictator
+userPassword: eltyLaslE
+uid: Elsa_Lytle
+givenName: Elsa
+mail: Elsa_Lytle@example.com
+carLicense: SSAVPSH
+departmentNumber: 9001
+employeeType: Employee
+homePhone: +1 818 209-9459
+initials: E. L.
+mobile: +1 213 106-8761
+pager: +1 818 418-9513
+manager: cn=Benthem Patchsqa
+secretary: cn=Jaman Nomura
+roomNumber: 2275
+
+dn: cn=Blinnie MacLennan, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Blinnie MacLennan
+sn: MacLennan
+description: This is Blinnie MacLennan's description
+facsimileTelephoneNumber: +1 206 180-6916
+l: Armonk
+ou: Accounting
+postalAddress: example$Accounting$Dept # 101
+telephoneNumber: +1 804 729-5408
+title: Junior Accounting Yahoo
+userPassword: nanneLcaMe
+uid: Blinnie_MacLennan
+givenName: Blinnie
+mail: Blinnie_MacLennan@example.com
+carLicense: FBU7VSE
+departmentNumber: 8636
+employeeType: Normal
+homePhone: +1 408 502-1710
+initials: B. M.
+mobile: +1 510 120-2151
+pager: +1 804 744-7495
+manager: cn=Krzysztof McDuffie
+secretary: cn=Wiele Winlow
+roomNumber: 3835
+
+dn: cn=Bqb Testing, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Bqb Testing
+sn: Testing
+description: This is Bqb Testing's description
+facsimileTelephoneNumber: +1 206 415-1952
+l: Redmond
+ou: Administrative
+postalAddress: example$Administrative$Dept # 804
+telephoneNumber: +1 213 697-3839
+title: Senior Administrative Vice President
+userPassword: gnitseTbqB
+uid: Bqb_Testing
+givenName: Bqb
+mail: Bqb_Testing@example.com
+carLicense: JYFN7VI
+departmentNumber: 406
+employeeType: Employee
+homePhone: +1 415 246-2701
+initials: B. T.
+mobile: +1 71 591-5998
+pager: +1 818 868-3686
+manager: cn=Evangelina Goodridge
+secretary: cn=Ramez Sherk
+roomNumber: 8009
+
+dn: cn=Melosa Garcia-Molina, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Melosa Garcia-Molina
+sn: Garcia-Molina
+description: This is Melosa Garcia-Molina's description
+facsimileTelephoneNumber: +1 804 468-4833
+l: Armonk
+ou: Payroll
+postalAddress: example$Payroll$Dept # 239
+telephoneNumber: +1 206 799-4746
+title: Supreme Payroll Madonna
+userPassword: aniloM-aic
+uid: Melosa_Garcia-Molina
+givenName: Melosa
+mail: Melosa_Garcia-Molina@example.com
+carLicense: 2ZWRAH4
+departmentNumber: 3218
+employeeType: Temp
+homePhone: +1 415 586-7945
+initials: M. G.
+mobile: +1 213 388-4058
+pager: +1 206 760-4662
+manager: cn=Marrilee Prestia
+secretary: cn=Dani Schreier
+roomNumber: 9512
+
+dn: cn=Karry Youngblood, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Karry Youngblood
+sn: Youngblood
+description: This is Karry Youngblood's description
+facsimileTelephoneNumber: +1 818 100-1452
+l: Fremont
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 503
+telephoneNumber: +1 213 184-4069
+title: Master Janitorial Manager
+userPassword: doolbgnuoY
+uid: Karry_Youngblood
+givenName: Karry
+mail: Karry_Youngblood@example.com
+carLicense: YY1HBNJ
+departmentNumber: 751
+employeeType: Temp
+homePhone: +1 303 821-8818
+initials: K. Y.
+mobile: +1 303 332-3514
+pager: +1 206 212-7247
+manager: cn=Dael Bogert
+secretary: cn=Chu-Chay Ahmadi
+roomNumber: 8889
+
+dn: cn=Rebekah Roob, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rebekah Roob
+sn: Roob
+description: This is Rebekah Roob's description
+facsimileTelephoneNumber: +1 510 604-5615
+l: Alameda
+ou: Payroll
+postalAddress: example$Payroll$Dept # 357
+telephoneNumber: +1 510 721-4818
+title: Master Payroll Madonna
+userPassword: booRhakebe
+uid: Rebekah_Roob
+givenName: Rebekah
+mail: Rebekah_Roob@example.com
+carLicense: 9I1AFWY
+departmentNumber: 6681
+employeeType: Temp
+homePhone: +1 804 224-8637
+initials: R. R.
+mobile: +1 510 999-2221
+pager: +1 206 902-9608
+manager: cn=Abigail Chacko
+secretary: cn=Sonny Pacheco
+roomNumber: 9394
+
+dn: cn=Car Oates, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Car Oates
+sn: Oates
+description: This is Car Oates's description
+facsimileTelephoneNumber: +1 818 684-5067
+l: San Francisco
+ou: Planning
+postalAddress: example$Planning$Dept # 696
+telephoneNumber: +1 71 322-1117
+title: Chief Planning Warrior
+userPassword: setaOraC
+uid: Car_Oates
+givenName: Car
+mail: Car_Oates@example.com
+carLicense: 06NVVNE
+departmentNumber: 7341
+employeeType: Employee
+homePhone: +1 510 190-3084
+initials: C. O.
+mobile: +1 206 285-8142
+pager: +1 303 309-3552
+manager: cn=Sylvia Ehrenfried
+secretary: cn=Stone Apostolopoulos
+roomNumber: 7577
+
+dn: cn=Blinni Kuzemka, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Blinni Kuzemka
+sn: Kuzemka
+description: This is Blinni Kuzemka's description
+facsimileTelephoneNumber: +1 303 531-1360
+l: Cambridge
+ou: Product Development
+postalAddress: example$Product Development$Dept # 43
+telephoneNumber: +1 415 908-1379
+title: Senior Product Development Accountant
+userPassword: akmezuKinn
+uid: Blinni_Kuzemka
+givenName: Blinni
+mail: Blinni_Kuzemka@example.com
+carLicense: G8NU9ZO
+departmentNumber: 5790
+employeeType: Employee
+homePhone: +1 408 485-8799
+initials: B. K.
+mobile: +1 818 816-2078
+pager: +1 206 356-6808
+manager: cn=Lilllie Woodall
+secretary: cn=Nomi Scarffe
+roomNumber: 9555
+
+dn: cn=Ingaborg Querengesser, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ingaborg Querengesser
+sn: Querengesser
+description: This is Ingaborg Querengesser's description
+facsimileTelephoneNumber: +1 206 732-6897
+l: Redwood Shores
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 424
+telephoneNumber: +1 213 698-6472
+title: Elite Human Resources Artist
+userPassword: ressegnere
+uid: Ingaborg_Querengesser
+givenName: Ingaborg
+mail: Ingaborg_Querengesser@example.com
+carLicense: 72A9EG0
+departmentNumber: 9511
+employeeType: Normal
+homePhone: +1 408 107-9263
+initials: I. Q.
+mobile: +1 213 663-2420
+pager: +1 415 180-7950
+manager: cn=Brena Ballard
+secretary: cn=Hall Falkenstrom
+roomNumber: 8945
+
+dn: cn=Chau Wargnier, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Chau Wargnier
+sn: Wargnier
+description: This is Chau Wargnier's description
+facsimileTelephoneNumber: +1 206 814-4393
+l: Santa Clara
+ou: Administrative
+postalAddress: example$Administrative$Dept # 171
+telephoneNumber: +1 213 191-6545
+title: Senior Administrative Technician
+userPassword: reingraWua
+uid: Chau_Wargnier
+givenName: Chau
+mail: Chau_Wargnier@example.com
+carLicense: 0BK0MIA
+departmentNumber: 7972
+employeeType: Employee
+homePhone: +1 213 898-6905
+initials: C. W.
+mobile: +1 408 635-9678
+pager: +1 213 966-5147
+manager: cn=Janeta Corson
+secretary: cn=Kikelia Stenson
+roomNumber: 2391
+
+dn: cn=Shorwan Velasquez, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Shorwan Velasquez
+sn: Velasquez
+description: This is Shorwan Velasquez's description
+facsimileTelephoneNumber: +1 408 758-2210
+l: Santa Clara
+ou: Product Development
+postalAddress: example$Product Development$Dept # 545
+telephoneNumber: +1 213 899-5824
+title: Supreme Product Development Grunt
+userPassword: zeuqsaleVn
+uid: Shorwan_Velasquez
+givenName: Shorwan
+mail: Shorwan_Velasquez@example.com
+carLicense: SMQ1XWB
+departmentNumber: 1496
+employeeType: Contract
+homePhone: +1 408 261-7871
+initials: S. V.
+mobile: +1 804 604-4166
+pager: +1 804 282-3825
+manager: cn=Ken Piecaitis
+secretary: cn=Gwenni Hertler
+roomNumber: 9830
+
+dn: cn=Nicolina Carmona, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Nicolina Carmona
+sn: Carmona
+description: This is Nicolina Carmona's description
+facsimileTelephoneNumber: +1 510 185-8337
+l: San Jose
+ou: Product Development
+postalAddress: example$Product Development$Dept # 383
+telephoneNumber: +1 804 459-8566
+title: Associate Product Development Writer
+userPassword: anomraCani
+uid: Nicolina_Carmona
+givenName: Nicolina
+mail: Nicolina_Carmona@example.com
+carLicense: F8HC165
+departmentNumber: 5296
+employeeType: Contract
+homePhone: +1 206 976-1598
+initials: N. C.
+mobile: +1 71 102-2974
+pager: +1 804 315-7898
+manager: cn=Manmohan Nguyen
+secretary: cn=Edouard Rashidi
+roomNumber: 8354
+
+dn: cn=Celesta Marketing, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Celesta Marketing
+sn: Marketing
+description: This is Celesta Marketing's description
+facsimileTelephoneNumber: +1 303 138-1000
+l: Mountain View
+ou: Payroll
+postalAddress: example$Payroll$Dept # 275
+telephoneNumber: +1 415 923-5744
+title: Junior Payroll Technician
+userPassword: gnitekraMa
+uid: Celesta_Marketing
+givenName: Celesta
+mail: Celesta_Marketing@example.com
+carLicense: DQ24O7Z
+departmentNumber: 1828
+employeeType: Manager
+homePhone: +1 818 365-9314
+initials: C. M.
+mobile: +1 303 929-4566
+pager: +1 510 467-5264
+manager: cn=Kimmi Toplis
+secretary: cn=Sidonia Yuill
+roomNumber: 1977
+
+dn: cn=Alex Pinchen, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Alex Pinchen
+sn: Pinchen
+description: This is Alex Pinchen's description
+facsimileTelephoneNumber: +1 510 103-2167
+l: Menlo Park
+ou: Product Development
+postalAddress: example$Product Development$Dept # 964
+telephoneNumber: +1 415 225-2391
+title: Supreme Product Development Architect
+userPassword: nehcniPxel
+uid: Alex_Pinchen
+givenName: Alex
+mail: Alex_Pinchen@example.com
+carLicense: ME29T4Q
+departmentNumber: 4571
+employeeType: Employee
+homePhone: +1 71 596-9249
+initials: A. P.
+mobile: +1 415 171-6971
+pager: +1 818 297-6326
+manager: cn=Annemarie Tester
+secretary: cn=Myrta Latour
+roomNumber: 9463
+
+dn: cn=Ernesto Nerby, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ernesto Nerby
+sn: Nerby
+description: This is Ernesto Nerby's description
+facsimileTelephoneNumber: +1 206 652-1611
+l: Redwood Shores
+ou: Accounting
+postalAddress: example$Accounting$Dept # 773
+telephoneNumber: +1 408 139-7115
+title: Senior Accounting Vice President
+userPassword: ybreNotsen
+uid: Ernesto_Nerby
+givenName: Ernesto
+mail: Ernesto_Nerby@example.com
+carLicense: Y1PMBOP
+departmentNumber: 1241
+employeeType: Employee
+homePhone: +1 303 795-7886
+initials: E. N.
+mobile: +1 804 706-1694
+pager: +1 408 988-7072
+manager: cn=Subramaniam Sassine
+secretary: cn=Tiffanie Keane
+roomNumber: 4335
+
+dn: cn=Ibrahim Sproule, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ibrahim Sproule
+sn: Sproule
+description: This is Ibrahim Sproule's description
+facsimileTelephoneNumber: +1 408 640-2419
+l: Redwood Shores
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 214
+telephoneNumber: +1 510 190-6101
+title: Master Janitorial Madonna
+userPassword: eluorpSmih
+uid: Ibrahim_Sproule
+givenName: Ibrahim
+mail: Ibrahim_Sproule@example.com
+carLicense: 8SWCJYI
+departmentNumber: 188
+employeeType: Contract
+homePhone: +1 206 386-9152
+initials: I. S.
+mobile: +1 213 401-1321
+pager: +1 408 139-7904
+manager: cn=Blinni Rudiak
+secretary: cn=Albrecht Monfre
+roomNumber: 4370
+
+dn: cn=Ronda Cristescu, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ronda Cristescu
+sn: Cristescu
+description: This is Ronda Cristescu's description
+facsimileTelephoneNumber: +1 510 864-6492
+l: Orem
+ou: Accounting
+postalAddress: example$Accounting$Dept # 293
+telephoneNumber: +1 415 645-1547
+title: Chief Accounting Artist
+userPassword: ucsetsirCa
+uid: Ronda_Cristescu
+givenName: Ronda
+mail: Ronda_Cristescu@example.com
+carLicense: FS3QOOW
+departmentNumber: 1146
+employeeType: Temp
+homePhone: +1 510 408-1132
+initials: R. C.
+mobile: +1 213 275-3983
+pager: +1 415 344-2023
+manager: cn=Marv Bnrinfo
+secretary: cn=Arly Damena
+roomNumber: 554
+
+dn: cn=Philippine Jeronimo, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Philippine Jeronimo
+sn: Jeronimo
+description: This is Philippine Jeronimo's description
+facsimileTelephoneNumber: +1 415 716-5555
+l: Redwood Shores
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 444
+telephoneNumber: +1 510 208-6902
+title: Master Product Testing Punk
+userPassword: ominoreJen
+uid: Philippine_Jeronimo
+givenName: Philippine
+mail: Philippine_Jeronimo@example.com
+carLicense: U2WN2EY
+departmentNumber: 7466
+employeeType: Temp
+homePhone: +1 818 835-7136
+initials: P. J.
+mobile: +1 213 686-3140
+pager: +1 804 513-1137
+manager: cn=Tao Haverty
+secretary: cn=Yueh Rosson
+roomNumber: 2005
+
+dn: cn=Eadie Borodajluk, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Eadie Borodajluk
+sn: Borodajluk
+description: This is Eadie Borodajluk's description
+facsimileTelephoneNumber: +1 213 452-1906
+l: Cambridge
+ou: Product Development
+postalAddress: example$Product Development$Dept # 323
+telephoneNumber: +1 303 445-2378
+title: Elite Product Development Figurehead
+userPassword: kuljadoroB
+uid: Eadie_Borodajluk
+givenName: Eadie
+mail: Eadie_Borodajluk@example.com
+carLicense: RC1TZQP
+departmentNumber: 7767
+employeeType: Manager
+homePhone: +1 510 780-5248
+initials: E. B.
+mobile: +1 303 403-3123
+pager: +1 804 399-6145
+manager: cn=Kayle Weiser
+secretary: cn=Werner Alford
+roomNumber: 7021
+
+dn: cn=Jaclin Boehms, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jaclin Boehms
+sn: Boehms
+description: This is Jaclin Boehms's description
+facsimileTelephoneNumber: +1 510 138-5625
+l: Mountain View
+ou: Peons
+postalAddress: example$Peons$Dept # 629
+telephoneNumber: +1 71 612-4011
+title: Chief Peons Engineer
+userPassword: smheoBnilc
+uid: Jaclin_Boehms
+givenName: Jaclin
+mail: Jaclin_Boehms@example.com
+carLicense: 0HGDB1Q
+departmentNumber: 6375
+employeeType: Employee
+homePhone: +1 303 400-5548
+initials: J. B.
+mobile: +1 206 126-6173
+pager: +1 804 942-4898
+manager: cn=Annecorinne Galvin
+secretary: cn=Hideo Colquhoun
+roomNumber: 8526
+
+dn: cn=Isabelle Felder, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Isabelle Felder
+sn: Felder
+description: This is Isabelle Felder's description
+facsimileTelephoneNumber: +1 415 851-3994
+l: Armonk
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 708
+telephoneNumber: +1 510 277-1010
+title: Chief Human Resources Consultant
+userPassword: redleFelle
+uid: Isabelle_Felder
+givenName: Isabelle
+mail: Isabelle_Felder@example.com
+carLicense: SUSHAJ9
+departmentNumber: 6767
+employeeType: Temp
+homePhone: +1 206 330-9826
+initials: I. F.
+mobile: +1 408 243-7429
+pager: +1 510 113-7411
+manager: cn=Kaman Krautle
+secretary: cn=Maggee Zhelka
+roomNumber: 4898
+
+dn: cn=Mal Salkilld, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Mal Salkilld
+sn: Salkilld
+description: This is Mal Salkilld's description
+facsimileTelephoneNumber: +1 818 216-1414
+l: Redwood Shores
+ou: Administrative
+postalAddress: example$Administrative$Dept # 773
+telephoneNumber: +1 213 200-8828
+title: Chief Administrative Developer
+userPassword: dlliklaSla
+uid: Mal_Salkilld
+givenName: Mal
+mail: Mal_Salkilld@example.com
+carLicense: C25Q0HN
+departmentNumber: 7571
+employeeType: Employee
+homePhone: +1 510 225-4247
+initials: M. S.
+mobile: +1 408 370-2772
+pager: +1 804 721-9213
+manager: cn=Connie Alvarez
+secretary: cn=Mer Evans
+roomNumber: 4251
+
+dn: cn=Api-Ecm Willmore, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Api-Ecm Willmore
+sn: Willmore
+description: This is Api-Ecm Willmore's description
+facsimileTelephoneNumber: +1 408 114-1738
+l: San Mateo
+ou: Accounting
+postalAddress: example$Accounting$Dept # 700
+telephoneNumber: +1 213 647-4281
+title: Elite Accounting Dictator
+userPassword: eromlliWmc
+uid: Api-Ecm_Willmore
+givenName: Api-Ecm
+mail: Api-Ecm_Willmore@example.com
+carLicense: C6GVAPO
+departmentNumber: 3407
+employeeType: Contract
+homePhone: +1 408 676-3261
+initials: A. W.
+mobile: +1 818 655-9407
+pager: +1 408 794-3155
+manager: cn=Yu-Kai Fischer
+secretary: cn=Romina Kriegler
+roomNumber: 5062
+
+dn: cn=Prab Stover, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Prab Stover
+sn: Stover
+description: This is Prab Stover's description
+facsimileTelephoneNumber: +1 303 454-9353
+l: Redwood Shores
+ou: Administrative
+postalAddress: example$Administrative$Dept # 607
+telephoneNumber: +1 213 896-9678
+title: Supreme Administrative Warrior
+userPassword: revotSbarP
+uid: Prab_Stover
+givenName: Prab
+mail: Prab_Stover@example.com
+carLicense: WOGGB4G
+departmentNumber: 3248
+employeeType: Normal
+homePhone: +1 818 889-3903
+initials: P. S.
+mobile: +1 303 722-5331
+pager: +1 71 553-3990
+manager: cn=Maggi Deere
+secretary: cn=Lulu Feyen
+roomNumber: 7129
+
+dn: cn=Tom Spohn, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tom Spohn
+sn: Spohn
+description: This is Tom Spohn's description
+facsimileTelephoneNumber: +1 818 852-9003
+l: Redwood Shores
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 320
+telephoneNumber: +1 71 459-4079
+title: Chief Human Resources Director
+userPassword: nhopSmoT
+uid: Tom_Spohn
+givenName: Tom
+mail: Tom_Spohn@example.com
+carLicense: EXW05UD
+departmentNumber: 7720
+employeeType: Contract
+homePhone: +1 408 417-4336
+initials: T. S.
+mobile: +1 818 574-3281
+pager: +1 415 914-5974
+manager: cn=Saraann Waigh
+secretary: cn=Blanche Coucopoulos
+roomNumber: 6395
+
+dn: cn=Perla Klavkalns, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Perla Klavkalns
+sn: Klavkalns
+description: This is Perla Klavkalns's description
+facsimileTelephoneNumber: +1 408 593-3266
+l: Palo Alto
+ou: Accounting
+postalAddress: example$Accounting$Dept # 785
+telephoneNumber: +1 818 391-8407
+title: Associate Accounting Director
+userPassword: snlakvalKa
+uid: Perla_Klavkalns
+givenName: Perla
+mail: Perla_Klavkalns@example.com
+carLicense: 73RCSKR
+departmentNumber: 3270
+employeeType: Normal
+homePhone: +1 818 437-8811
+initials: P. K.
+mobile: +1 303 598-5727
+pager: +1 206 934-2727
+manager: cn=Debera Weibust
+secretary: cn=Taffy Sastry
+roomNumber: 953
+
+dn: cn=Modestia Coviensky, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Modestia Coviensky
+sn: Coviensky
+description: This is Modestia Coviensky's description
+facsimileTelephoneNumber: +1 415 883-4842
+l: San Mateo
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 978
+telephoneNumber: +1 303 686-7438
+title: Elite Janitorial Accountant
+userPassword: yksneivoCa
+uid: Modestia_Coviensky
+givenName: Modestia
+mail: Modestia_Coviensky@example.com
+carLicense: 7MZP5P0
+departmentNumber: 6588
+employeeType: Contract
+homePhone: +1 213 316-3891
+initials: M. C.
+mobile: +1 206 826-9119
+pager: +1 415 313-2422
+manager: cn=Ninno Engle
+secretary: cn=Sarajane Stanke
+roomNumber: 4252
+
+dn: cn=Rini Meier, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rini Meier
+sn: Meier
+description: This is Rini Meier's description
+facsimileTelephoneNumber: +1 213 526-7575
+l: Redmond
+ou: Peons
+postalAddress: example$Peons$Dept # 502
+telephoneNumber: +1 71 438-4729
+title: Master Peons Artist
+userPassword: reieMiniR
+uid: Rini_Meier
+givenName: Rini
+mail: Rini_Meier@example.com
+carLicense: QMB8GH5
+departmentNumber: 1103
+employeeType: Contract
+homePhone: +1 213 627-1021
+initials: R. M.
+mobile: +1 804 353-1875
+pager: +1 408 377-2005
+manager: cn=Jeroen Dunlay
+secretary: cn=Linette Surray
+roomNumber: 3593
+
+dn: cn=Ting Audet, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ting Audet
+sn: Audet
+description: This is Ting Audet's description
+facsimileTelephoneNumber: +1 408 585-2475
+l: Cambridge
+ou: Management
+postalAddress: example$Management$Dept # 23
+telephoneNumber: +1 415 456-5711
+title: Chief Management Accountant
+userPassword: teduAgniT
+uid: Ting_Audet
+givenName: Ting
+mail: Ting_Audet@example.com
+carLicense: DWXTYWJ
+departmentNumber: 8714
+employeeType: Employee
+homePhone: +1 303 890-4765
+initials: T. A.
+mobile: +1 71 180-7262
+pager: +1 206 393-9581
+manager: cn=Rocio Vezina
+secretary: cn=Gilemette Erguven
+roomNumber: 5931
+
+dn: cn=Teymour Patry, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Teymour Patry
+sn: Patry
+description: This is Teymour Patry's description
+facsimileTelephoneNumber: +1 71 906-2570
+l: San Francisco
+ou: Product Development
+postalAddress: example$Product Development$Dept # 976
+telephoneNumber: +1 206 637-9717
+title: Associate Product Development Writer
+userPassword: yrtaPruomy
+uid: Teymour_Patry
+givenName: Teymour
+mail: Teymour_Patry@example.com
+carLicense: INYJU0D
+departmentNumber: 2937
+employeeType: Temp
+homePhone: +1 415 744-9261
+initials: T. P.
+mobile: +1 510 665-2491
+pager: +1 818 935-7075
+manager: cn=Mattie Weaver
+secretary: cn=Gwendolin Hadden
+roomNumber: 6829
+
+dn: cn=Helsa Dressler, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Helsa Dressler
+sn: Dressler
+description: This is Helsa Dressler's description
+facsimileTelephoneNumber: +1 818 884-5702
+l: Redwood Shores
+ou: Peons
+postalAddress: example$Peons$Dept # 622
+telephoneNumber: +1 415 317-1038
+title: Supreme Peons Architect
+userPassword: relsserDas
+uid: Helsa_Dressler
+givenName: Helsa
+mail: Helsa_Dressler@example.com
+carLicense: FLGCRQV
+departmentNumber: 8092
+employeeType: Employee
+homePhone: +1 818 167-2407
+initials: H. D.
+mobile: +1 408 415-4273
+pager: +1 818 554-5228
+manager: cn=Linnea Van Meter
+secretary: cn=Shelbi Thomas
+roomNumber: 2280
+
+dn: cn=Jobyna Ackaouy, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jobyna Ackaouy
+sn: Ackaouy
+description: This is Jobyna Ackaouy's description
+facsimileTelephoneNumber: +1 818 499-3930
+l: Orem
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 718
+telephoneNumber: +1 510 489-5638
+title: Senior Janitorial Artist
+userPassword: yuoakcAany
+uid: Jobyna_Ackaouy
+givenName: Jobyna
+mail: Jobyna_Ackaouy@example.com
+carLicense: PWM4Z1O
+departmentNumber: 5874
+employeeType: Normal
+homePhone: +1 213 629-8161
+initials: J. A.
+mobile: +1 206 798-4549
+pager: +1 804 265-8346
+manager: cn=Joachim Rahal
+secretary: cn=Athene Zonner
+roomNumber: 8666
+
+dn: cn=Edeline Cegelski, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Edeline Cegelski
+sn: Cegelski
+description: This is Edeline Cegelski's description
+facsimileTelephoneNumber: +1 71 220-3831
+l: Cambridge
+ou: Product Development
+postalAddress: example$Product Development$Dept # 879
+telephoneNumber: +1 213 524-9029
+title: Supreme Product Development Assistant
+userPassword: ikslegeCen
+uid: Edeline_Cegelski
+givenName: Edeline
+mail: Edeline_Cegelski@example.com
+carLicense: SGAENU8
+departmentNumber: 373
+employeeType: Manager
+homePhone: +1 303 682-4481
+initials: E. C.
+mobile: +1 213 897-9227
+pager: +1 206 624-1940
+manager: cn=Zena Cottingham
+secretary: cn=Rania Brann
+roomNumber: 7985
+
+dn: cn=Sharon Bolding, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sharon Bolding
+sn: Bolding
+description: This is Sharon Bolding's description
+facsimileTelephoneNumber: +1 408 987-2312
+l: Emeryville
+ou: Accounting
+postalAddress: example$Accounting$Dept # 485
+telephoneNumber: +1 408 842-3786
+title: Junior Accounting Writer
+userPassword: gnidloBnor
+uid: Sharon_Bolding
+givenName: Sharon
+mail: Sharon_Bolding@example.com
+carLicense: KCDS8YF
+departmentNumber: 5881
+employeeType: Normal
+homePhone: +1 206 105-7175
+initials: S. B.
+mobile: +1 213 923-6505
+pager: +1 303 283-7542
+manager: cn=Deanna Lambregts
+secretary: cn=Naser Kahhale
+roomNumber: 4980
+
+dn: cn=Benoit Stampley, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Benoit Stampley
+sn: Stampley
+description: This is Benoit Stampley's description
+facsimileTelephoneNumber: +1 510 376-8381
+l: Palo Alto
+ou: Planning
+postalAddress: example$Planning$Dept # 458
+telephoneNumber: +1 206 338-1295
+title: Associate Planning Architect
+userPassword: yelpmatSti
+uid: Benoit_Stampley
+givenName: Benoit
+mail: Benoit_Stampley@example.com
+carLicense: CLK3X3D
+departmentNumber: 8307
+employeeType: Temp
+homePhone: +1 804 305-9283
+initials: B. S.
+mobile: +1 415 859-2869
+pager: +1 206 465-2730
+manager: cn=Cynde Vahary
+secretary: cn=Franc Smoot
+roomNumber: 6379
+
+dn: cn=Said Relations, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Said Relations
+sn: Relations
+description: This is Said Relations's description
+facsimileTelephoneNumber: +1 213 281-5932
+l: San Francisco
+ou: Product Development
+postalAddress: example$Product Development$Dept # 546
+telephoneNumber: +1 804 997-7533
+title: Master Product Development Figurehead
+userPassword: snoitaleRd
+uid: Said_Relations
+givenName: Said
+mail: Said_Relations@example.com
+carLicense: NA0SZ53
+departmentNumber: 2137
+employeeType: Manager
+homePhone: +1 415 960-5640
+initials: S. R.
+mobile: +1 415 307-2107
+pager: +1 510 368-5019
+manager: cn=Issy Rau
+secretary: cn=Brandais Poindexter
+roomNumber: 3833
+
+dn: cn=Deva Cakarevic, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Deva Cakarevic
+sn: Cakarevic
+description: This is Deva Cakarevic's description
+facsimileTelephoneNumber: +1 804 899-3758
+l: Fremont
+ou: Accounting
+postalAddress: example$Accounting$Dept # 825
+telephoneNumber: +1 206 319-3019
+title: Elite Accounting Punk
+userPassword: civerakaCa
+uid: Deva_Cakarevic
+givenName: Deva
+mail: Deva_Cakarevic@example.com
+carLicense: URSX167
+departmentNumber: 935
+employeeType: Manager
+homePhone: +1 818 153-4850
+initials: D. C.
+mobile: +1 408 385-9712
+pager: +1 303 126-3737
+manager: cn=Wallie Kouhi
+secretary: cn=Jobi Coggins
+roomNumber: 6423
+
+dn: cn=Ermina Lannan, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ermina Lannan
+sn: Lannan
+description: This is Ermina Lannan's description
+facsimileTelephoneNumber: +1 415 101-8882
+l: Sunnyvale
+ou: Management
+postalAddress: example$Management$Dept # 782
+telephoneNumber: +1 213 680-9698
+title: Associate Management Sales Rep
+userPassword: nannaLanim
+uid: Ermina_Lannan
+givenName: Ermina
+mail: Ermina_Lannan@example.com
+carLicense: D8L35SS
+departmentNumber: 139
+employeeType: Manager
+homePhone: +1 71 140-5966
+initials: E. L.
+mobile: +1 415 363-7285
+pager: +1 510 237-3675
+manager: cn=Ahmed Lassig
+secretary: cn=Giuseppe Downey
+roomNumber: 6461
+
+dn: cn=Bqb Marette, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Bqb Marette
+sn: Marette
+description: This is Bqb Marette's description
+facsimileTelephoneNumber: +1 804 222-9616
+l: Cupertino
+ou: Planning
+postalAddress: example$Planning$Dept # 647
+telephoneNumber: +1 804 939-1042
+title: Supreme Planning Madonna
+userPassword: etteraMbqB
+uid: Bqb_Marette
+givenName: Bqb
+mail: Bqb_Marette@example.com
+carLicense: WQ70RXI
+departmentNumber: 169
+employeeType: Contract
+homePhone: +1 303 898-7967
+initials: B. M.
+mobile: +1 510 742-2632
+pager: +1 213 378-3866
+manager: cn=Levent Hummerston
+secretary: cn=Doralin Georges
+roomNumber: 8753
+
+dn: cn=Liese Wolczanski, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Liese Wolczanski
+sn: Wolczanski
+description: This is Liese Wolczanski's description
+facsimileTelephoneNumber: +1 510 573-6007
+l: Mountain View
+ou: Planning
+postalAddress: example$Planning$Dept # 638
+telephoneNumber: +1 206 831-2154
+title: Master Planning Architect
+userPassword: iksnazcloW
+uid: Liese_Wolczanski
+givenName: Liese
+mail: Liese_Wolczanski@example.com
+carLicense: 0CLR48Z
+departmentNumber: 314
+employeeType: Temp
+homePhone: +1 804 330-5492
+initials: L. W.
+mobile: +1 804 511-1187
+pager: +1 415 616-5291
+manager: cn=Meade Talmont
+secretary: cn=Fereidoon Herscovici
+roomNumber: 7599
+
+dn: cn=Ahmet Covey, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ahmet Covey
+sn: Covey
+description: This is Ahmet Covey's description
+facsimileTelephoneNumber: +1 206 196-9623
+l: Armonk
+ou: Administrative
+postalAddress: example$Administrative$Dept # 621
+telephoneNumber: +1 213 318-6865
+title: Senior Administrative Pinhead
+userPassword: yevoCtemhA
+uid: Ahmet_Covey
+givenName: Ahmet
+mail: Ahmet_Covey@example.com
+carLicense: LLJX9BE
+departmentNumber: 1598
+employeeType: Employee
+homePhone: +1 510 515-5928
+initials: A. C.
+mobile: +1 303 574-6475
+pager: +1 71 787-5024
+manager: cn=Oren Kannel
+secretary: cn=Shiva Baer
+roomNumber: 2934
+
+dn: cn=Nona Knorr, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Nona Knorr
+sn: Knorr
+description: This is Nona Knorr's description
+facsimileTelephoneNumber: +1 510 986-6709
+l: Alameda
+ou: Payroll
+postalAddress: example$Payroll$Dept # 287
+telephoneNumber: +1 804 342-2409
+title: Junior Payroll Accountant
+userPassword: rronKanoN
+uid: Nona_Knorr
+givenName: Nona
+mail: Nona_Knorr@example.com
+carLicense: IBBEKR8
+departmentNumber: 569
+employeeType: Contract
+homePhone: +1 415 972-2312
+initials: N. K.
+mobile: +1 71 299-2716
+pager: +1 408 697-8594
+manager: cn=Sharron Kishi
+secretary: cn=Emmey Raaflaub
+roomNumber: 8189
+
+dn: cn=Alane deMontluzin, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Alane deMontluzin
+sn: deMontluzin
+description: This is Alane deMontluzin's description
+facsimileTelephoneNumber: +1 206 966-7764
+l: Redmond
+ou: Management
+postalAddress: example$Management$Dept # 994
+telephoneNumber: +1 804 268-8328
+title: Junior Management Architect
+userPassword: nizultnoMe
+uid: Alane_deMontluzin
+givenName: Alane
+mail: Alane_deMontluzin@example.com
+carLicense: Q2SBGF5
+departmentNumber: 7468
+employeeType: Manager
+homePhone: +1 408 352-4589
+initials: A. d.
+mobile: +1 71 684-3917
+pager: +1 303 840-4607
+manager: cn=Aloise Engelhart
+secretary: cn=Nicol Pancholy
+roomNumber: 8247
+
+dn: cn=Karlene Combellack, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Karlene Combellack
+sn: Combellack
+description: This is Karlene Combellack's description
+facsimileTelephoneNumber: +1 303 375-7590
+l: Cupertino
+ou: Management
+postalAddress: example$Management$Dept # 72
+telephoneNumber: +1 510 123-4455
+title: Junior Management Vice President
+userPassword: kcallebmoC
+uid: Karlene_Combellack
+givenName: Karlene
+mail: Karlene_Combellack@example.com
+carLicense: OCDZU2F
+departmentNumber: 9006
+employeeType: Employee
+homePhone: +1 804 125-5891
+initials: K. C.
+mobile: +1 408 126-4843
+pager: +1 804 602-5945
+manager: cn=Astra Durant
+secretary: cn=Petronilla Shabatura
+roomNumber: 9322
+
+dn: cn=Lowell Piotto, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lowell Piotto
+sn: Piotto
+description: This is Lowell Piotto's description
+facsimileTelephoneNumber: +1 408 485-1853
+l: Alameda
+ou: Accounting
+postalAddress: example$Accounting$Dept # 114
+telephoneNumber: +1 206 123-8513
+title: Junior Accounting Fellow
+userPassword: ottoiPllew
+uid: Lowell_Piotto
+givenName: Lowell
+mail: Lowell_Piotto@example.com
+carLicense: DRS8XSG
+departmentNumber: 2716
+employeeType: Temp
+homePhone: +1 213 404-7970
+initials: L. P.
+mobile: +1 408 924-2754
+pager: +1 71 622-1922
+manager: cn=Julietta Galluzzi
+secretary: cn=Wannell Towsley
+roomNumber: 4029
+
+dn: cn=JR Pezzullo, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: JR Pezzullo
+sn: Pezzullo
+description: This is JR Pezzullo's description
+facsimileTelephoneNumber: +1 71 460-7618
+l: San Mateo
+ou: Administrative
+postalAddress: example$Administrative$Dept # 630
+telephoneNumber: +1 818 796-1485
+title: Associate Administrative Artist
+userPassword: olluzzePRJ
+uid: JR_Pezzullo
+givenName: JR
+mail: JR_Pezzullo@example.com
+carLicense: PRQ6ADM
+departmentNumber: 5668
+employeeType: Employee
+homePhone: +1 804 363-4761
+initials: J. P.
+mobile: +1 415 187-3333
+pager: +1 510 125-1940
+manager: cn=Chastity Hamori
+secretary: cn=Conway Levin
+roomNumber: 1379
+
+dn: cn=Binni Munsey, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Binni Munsey
+sn: Munsey
+description: This is Binni Munsey's description
+facsimileTelephoneNumber: +1 510 472-4712
+l: Cambridge
+ou: Planning
+postalAddress: example$Planning$Dept # 449
+telephoneNumber: +1 303 415-8355
+title: Master Planning Consultant
+userPassword: yesnuMinni
+uid: Binni_Munsey
+givenName: Binni
+mail: Binni_Munsey@example.com
+carLicense: VH2YC4W
+departmentNumber: 3217
+employeeType: Temp
+homePhone: +1 804 302-6225
+initials: B. M.
+mobile: +1 206 459-4235
+pager: +1 213 267-7393
+manager: cn=Corissa McNerlan
+secretary: cn=Pic Kohn
+roomNumber: 1672
+
+dn: cn=Gernot Mirek, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gernot Mirek
+sn: Mirek
+description: This is Gernot Mirek's description
+facsimileTelephoneNumber: +1 71 744-5990
+l: Sunnyvale
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 491
+telephoneNumber: +1 408 323-8792
+title: Senior Human Resources Engineer
+userPassword: keriMtonre
+uid: Gernot_Mirek
+givenName: Gernot
+mail: Gernot_Mirek@example.com
+carLicense: 56PVP59
+departmentNumber: 8407
+employeeType: Normal
+homePhone: +1 818 598-3576
+initials: G. M.
+mobile: +1 206 863-2296
+pager: +1 818 403-1609
+manager: cn=Meris Omura
+secretary: cn=Kirsten Bullion
+roomNumber: 297
+
+dn: cn=Madalene Hesk, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Madalene Hesk
+sn: Hesk
+description: This is Madalene Hesk's description
+facsimileTelephoneNumber: +1 408 954-9133
+l: Sunnyvale
+ou: Payroll
+postalAddress: example$Payroll$Dept # 427
+telephoneNumber: +1 206 629-7573
+title: Senior Payroll Stooge
+userPassword: kseHenelad
+uid: Madalene_Hesk
+givenName: Madalene
+mail: Madalene_Hesk@example.com
+carLicense: JL1NDFK
+departmentNumber: 3011
+employeeType: Manager
+homePhone: +1 303 349-2087
+initials: M. H.
+mobile: +1 804 205-5440
+pager: +1 408 482-1246
+manager: cn=Yuan Mitsui
+secretary: cn=Khosro Paulin
+roomNumber: 7563
+
+dn: cn=Ketti Kehr, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ketti Kehr
+sn: Kehr
+description: This is Ketti Kehr's description
+facsimileTelephoneNumber: +1 213 436-1385
+l: Alameda
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 785
+telephoneNumber: +1 408 721-5498
+title: Junior Human Resources Architect
+userPassword: rheKitteK
+uid: Ketti_Kehr
+givenName: Ketti
+mail: Ketti_Kehr@example.com
+carLicense: Y0QLRIU
+departmentNumber: 6558
+employeeType: Employee
+homePhone: +1 303 755-9515
+initials: K. K.
+mobile: +1 71 844-4943
+pager: +1 415 558-2691
+manager: cn=Dalip Danko
+secretary: cn=Tessi Campara
+roomNumber: 7697
+
+dn: cn=Antonia Marcom, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Antonia Marcom
+sn: Marcom
+description: This is Antonia Marcom's description
+facsimileTelephoneNumber: +1 213 211-8314
+l: San Mateo
+ou: Payroll
+postalAddress: example$Payroll$Dept # 134
+telephoneNumber: +1 71 504-2602
+title: Senior Payroll Warrior
+userPassword: mocraMaino
+uid: Antonia_Marcom
+givenName: Antonia
+mail: Antonia_Marcom@example.com
+carLicense: WIZ5G1X
+departmentNumber: 7766
+employeeType: Manager
+homePhone: +1 408 112-2793
+initials: A. M.
+mobile: +1 408 113-3390
+pager: +1 818 400-6100
+manager: cn=Fuzal Yerigan
+secretary: cn=Mouna Forghani
+roomNumber: 3247
+
+dn: cn=Xuan-Lien Thoms, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Xuan-Lien Thoms
+sn: Thoms
+description: This is Xuan-Lien Thoms's description
+facsimileTelephoneNumber: +1 818 700-9243
+l: Redmond
+ou: Planning
+postalAddress: example$Planning$Dept # 279
+telephoneNumber: +1 818 695-7186
+title: Elite Planning Developer
+userPassword: smohTneiL-
+uid: Xuan-Lien_Thoms
+givenName: Xuan-Lien
+mail: Xuan-Lien_Thoms@example.com
+carLicense: RR8HW30
+departmentNumber: 5729
+employeeType: Contract
+homePhone: +1 804 759-9967
+initials: X. T.
+mobile: +1 804 976-9015
+pager: +1 206 634-9113
+manager: cn=Oralia Rajwani
+secretary: cn=Shandie Maxin
+roomNumber: 6773
+
+dn: cn=Estelle Leenher, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Estelle Leenher
+sn: Leenher
+description: This is Estelle Leenher's description
+facsimileTelephoneNumber: +1 818 222-1752
+l: San Francisco
+ou: Accounting
+postalAddress: example$Accounting$Dept # 168
+telephoneNumber: +1 415 310-8124
+title: Junior Accounting Engineer
+userPassword: rehneeLell
+uid: Estelle_Leenher
+givenName: Estelle
+mail: Estelle_Leenher@example.com
+carLicense: HZMBN5S
+departmentNumber: 9010
+employeeType: Contract
+homePhone: +1 818 281-4909
+initials: E. L.
+mobile: +1 804 354-3908
+pager: +1 510 142-5148
+manager: cn=Sayeeda Traut
+secretary: cn=Shayne Pardi
+roomNumber: 4701
+
+dn: cn=Adriana Brummitt, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Adriana Brummitt
+sn: Brummitt
+description: This is Adriana Brummitt's description
+facsimileTelephoneNumber: +1 804 719-7444
+l: Sunnyvale
+ou: Payroll
+postalAddress: example$Payroll$Dept # 779
+telephoneNumber: +1 510 344-1272
+title: Junior Payroll Fellow
+userPassword: ttimmurBan
+uid: Adriana_Brummitt
+givenName: Adriana
+mail: Adriana_Brummitt@example.com
+carLicense: W60QRX9
+departmentNumber: 6588
+employeeType: Temp
+homePhone: +1 818 413-2831
+initials: A. B.
+mobile: +1 408 192-2443
+pager: +1 303 460-9559
+manager: cn=Hendrik Lormor
+secretary: cn=Bhupinder Scammerhorn
+roomNumber: 5310
+
+dn: cn=Ivette Eckhart, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ivette Eckhart
+sn: Eckhart
+description: This is Ivette Eckhart's description
+facsimileTelephoneNumber: +1 408 149-2483
+l: San Francisco
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 744
+telephoneNumber: +1 804 294-4481
+title: Master Product Testing Accountant
+userPassword: trahkcEett
+uid: Ivette_Eckhart
+givenName: Ivette
+mail: Ivette_Eckhart@example.com
+carLicense: BOANDJ5
+departmentNumber: 1352
+employeeType: Contract
+homePhone: +1 206 867-5183
+initials: I. E.
+mobile: +1 408 229-7675
+pager: +1 303 170-9372
+manager: cn=Marylynne Kurczak
+secretary: cn=Dilpreet Putnam
+roomNumber: 5576
+
+dn: cn=Fraser Naor, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Fraser Naor
+sn: Naor
+description: This is Fraser Naor's description
+facsimileTelephoneNumber: +1 415 435-8678
+l: Armonk
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 239
+telephoneNumber: +1 818 327-8628
+title: Associate Human Resources Pinhead
+userPassword: roaNresarF
+uid: Fraser_Naor
+givenName: Fraser
+mail: Fraser_Naor@example.com
+carLicense: WR7H1ZY
+departmentNumber: 8573
+employeeType: Contract
+homePhone: +1 303 498-3230
+initials: F. N.
+mobile: +1 408 956-2720
+pager: +1 818 450-9361
+manager: cn=Abdullah Sutphen
+secretary: cn=Paulinus Henneberger
+roomNumber: 4586
+
+dn: cn=Cherice Hysler, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Cherice Hysler
+sn: Hysler
+description: This is Cherice Hysler's description
+facsimileTelephoneNumber: +1 206 989-8948
+l: Palo Alto
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 230
+telephoneNumber: +1 510 714-9547
+title: Supreme Janitorial Director
+userPassword: relsyHecir
+uid: Cherice_Hysler
+givenName: Cherice
+mail: Cherice_Hysler@example.com
+carLicense: 4XDBRVE
+departmentNumber: 6877
+employeeType: Normal
+homePhone: +1 510 836-7582
+initials: C. H.
+mobile: +1 804 125-5705
+pager: +1 804 180-1364
+manager: cn=Ardelle Dryer
+secretary: cn=Terrell D'Antonio
+roomNumber: 8269
+
+dn: cn=Rosemonde St.Germain, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rosemonde St.Germain
+sn: St.Germain
+description: This is Rosemonde St.Germain's description
+facsimileTelephoneNumber: +1 71 227-9241
+l: Redmond
+ou: Product Development
+postalAddress: example$Product Development$Dept # 207
+telephoneNumber: +1 206 310-2724
+title: Junior Product Development Stooge
+userPassword: niamreG.tS
+uid: Rosemonde_St.Germain
+givenName: Rosemonde
+mail: Rosemonde_St.Germain@example.com
+carLicense: JZ0X19C
+departmentNumber: 4957
+employeeType: Normal
+homePhone: +1 206 289-1031
+initials: R. S.
+mobile: +1 408 394-5151
+pager: +1 415 946-2203
+manager: cn=Afton Rtpbuild
+secretary: cn=Gabriella Hann
+roomNumber: 2031
+
+dn: cn=Kien-Nghiep Ranahan, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kien-Nghiep Ranahan
+sn: Ranahan
+description: This is Kien-Nghiep Ranahan's description
+facsimileTelephoneNumber: +1 71 441-9506
+l: Cupertino
+ou: Planning
+postalAddress: example$Planning$Dept # 666
+telephoneNumber: +1 213 289-1356
+title: Junior Planning Stooge
+userPassword: nahanaRpei
+uid: Kien-Nghiep_Ranahan
+givenName: Kien-Nghiep
+mail: Kien-Nghiep_Ranahan@example.com
+carLicense: ZT8PY6H
+departmentNumber: 764
+employeeType: Temp
+homePhone: +1 510 609-7714
+initials: K. R.
+mobile: +1 213 512-8114
+pager: +1 213 459-8684
+manager: cn=Jean-Yves Seay
+secretary: cn=Wynnie Matsuzawa
+roomNumber: 613
+
+dn: cn=Dorisa Batchelder, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dorisa Batchelder
+sn: Batchelder
+description: This is Dorisa Batchelder's description
+facsimileTelephoneNumber: +1 510 454-2409
+l: Fremont
+ou: Management
+postalAddress: example$Management$Dept # 751
+telephoneNumber: +1 408 813-2879
+title: Junior Management Evangelist
+userPassword: redlehctaB
+uid: Dorisa_Batchelder
+givenName: Dorisa
+mail: Dorisa_Batchelder@example.com
+carLicense: EWR304Z
+departmentNumber: 7186
+employeeType: Normal
+homePhone: +1 804 277-4974
+initials: D. B.
+mobile: +1 818 410-1396
+pager: +1 818 798-6674
+manager: cn=Barbara Swails
+secretary: cn=Clevon Miasek
+roomNumber: 115
+
+dn: cn=Dodie Australia, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dodie Australia
+sn: Australia
+description: This is Dodie Australia's description
+facsimileTelephoneNumber: +1 818 483-6600
+l: Santa Clara
+ou: Administrative
+postalAddress: example$Administrative$Dept # 310
+telephoneNumber: +1 408 630-2959
+title: Associate Administrative Writer
+userPassword: ailartsuAe
+uid: Dodie_Australia
+givenName: Dodie
+mail: Dodie_Australia@example.com
+carLicense: K6FX4EA
+departmentNumber: 3837
+employeeType: Employee
+homePhone: +1 804 791-1955
+initials: D. A.
+mobile: +1 206 881-2826
+pager: +1 213 387-7159
+manager: cn=Guglielma Oman
+secretary: cn=Catherine Coste
+roomNumber: 3434
+
+dn: cn=Sharity Overcash, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sharity Overcash
+sn: Overcash
+description: This is Sharity Overcash's description
+facsimileTelephoneNumber: +1 206 422-7168
+l: Cambridge
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 274
+telephoneNumber: +1 818 423-6507
+title: Junior Janitorial Janitor
+userPassword: hsacrevOyt
+uid: Sharity_Overcash
+givenName: Sharity
+mail: Sharity_Overcash@example.com
+carLicense: 2ZU7J65
+departmentNumber: 2845
+employeeType: Employee
+homePhone: +1 213 120-8577
+initials: S. O.
+mobile: +1 206 716-9529
+pager: +1 206 908-2942
+manager: cn=Anet Nevrela
+secretary: cn=Thanh-Hoa Hysler
+roomNumber: 7742
+
+dn: cn=Ema Decasper, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ema Decasper
+sn: Decasper
+description: This is Ema Decasper's description
+facsimileTelephoneNumber: +1 408 654-4688
+l: Orem
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 654
+telephoneNumber: +1 408 143-5292
+title: Senior Janitorial Dictator
+userPassword: repsaceDam
+uid: Ema_Decasper
+givenName: Ema
+mail: Ema_Decasper@example.com
+carLicense: MRKL93I
+departmentNumber: 4821
+employeeType: Contract
+homePhone: +1 818 820-8509
+initials: E. D.
+mobile: +1 303 830-2198
+pager: +1 303 373-8951
+manager: cn=Hubert Monterosso-Wood
+secretary: cn=Cordelie Bourland
+roomNumber: 4037
+
+dn: cn=Kinna Frankcom, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kinna Frankcom
+sn: Frankcom
+description: This is Kinna Frankcom's description
+facsimileTelephoneNumber: +1 804 988-8473
+l: Emeryville
+ou: Accounting
+postalAddress: example$Accounting$Dept # 868
+telephoneNumber: +1 818 672-5752
+title: Chief Accounting Madonna
+userPassword: mocknarFan
+uid: Kinna_Frankcom
+givenName: Kinna
+mail: Kinna_Frankcom@example.com
+carLicense: 1M5KX16
+departmentNumber: 1552
+employeeType: Normal
+homePhone: +1 818 515-6816
+initials: K. F.
+mobile: +1 213 724-2917
+pager: +1 213 853-5072
+manager: cn=Adaline Castronova
+secretary: cn=Samual Devlin
+roomNumber: 7212
+
+dn: cn=Thom Littau, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Thom Littau
+sn: Littau
+description: This is Thom Littau's description
+facsimileTelephoneNumber: +1 213 437-8097
+l: Palo Alto
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 713
+telephoneNumber: +1 303 292-3348
+title: Associate Janitorial Pinhead
+userPassword: uattiLmohT
+uid: Thom_Littau
+givenName: Thom
+mail: Thom_Littau@example.com
+carLicense: UPLF680
+departmentNumber: 1383
+employeeType: Temp
+homePhone: +1 303 484-8297
+initials: T. L.
+mobile: +1 71 551-7795
+pager: +1 206 304-3576
+manager: cn=Hack-Hoo Hopf
+secretary: cn=Pirooz Frankcom
+roomNumber: 1642
+
+dn: cn=Shana Polulack, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Shana Polulack
+sn: Polulack
+description: This is Shana Polulack's description
+facsimileTelephoneNumber: +1 510 674-7584
+l: San Mateo
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 564
+telephoneNumber: +1 206 792-8167
+title: Associate Product Testing Developer
+userPassword: kcaluloPan
+uid: Shana_Polulack
+givenName: Shana
+mail: Shana_Polulack@example.com
+carLicense: RUT1TGQ
+departmentNumber: 5436
+employeeType: Manager
+homePhone: +1 408 951-4182
+initials: S. P.
+mobile: +1 804 370-6607
+pager: +1 408 278-5227
+manager: cn=Emilda Pifko
+secretary: cn=Leena Cassar
+roomNumber: 3276
+
+dn: cn=Rivalee Letsome, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rivalee Letsome
+sn: Letsome
+description: This is Rivalee Letsome's description
+facsimileTelephoneNumber: +1 206 348-5919
+l: Palo Alto
+ou: Product Development
+postalAddress: example$Product Development$Dept # 413
+telephoneNumber: +1 408 273-4354
+title: Master Product Development Manager
+userPassword: emosteLeel
+uid: Rivalee_Letsome
+givenName: Rivalee
+mail: Rivalee_Letsome@example.com
+carLicense: 23EUZEN
+departmentNumber: 8348
+employeeType: Employee
+homePhone: +1 303 488-9887
+initials: R. L.
+mobile: +1 408 310-7111
+pager: +1 408 289-1053
+manager: cn=Ramon Marco
+secretary: cn=Nurhan Lobianco
+roomNumber: 1488
+
+dn: cn=Bernardina Juscesak, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Bernardina Juscesak
+sn: Juscesak
+description: This is Bernardina Juscesak's description
+facsimileTelephoneNumber: +1 510 197-8837
+l: Redwood Shores
+ou: Accounting
+postalAddress: example$Accounting$Dept # 327
+telephoneNumber: +1 510 946-3336
+title: Elite Accounting Dictator
+userPassword: kasecsuJan
+uid: Bernardina_Juscesak
+givenName: Bernardina
+mail: Bernardina_Juscesak@example.com
+carLicense: MV649YK
+departmentNumber: 4230
+employeeType: Contract
+homePhone: +1 303 527-7531
+initials: B. J.
+mobile: +1 818 661-5367
+pager: +1 213 301-2474
+manager: cn=Neala Bridgeford
+secretary: cn=Antonie Liew
+roomNumber: 4837
+
+dn: cn=Annadiane Axberg, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Annadiane Axberg
+sn: Axberg
+description: This is Annadiane Axberg's description
+facsimileTelephoneNumber: +1 71 559-3741
+l: Alameda
+ou: Payroll
+postalAddress: example$Payroll$Dept # 545
+telephoneNumber: +1 206 507-8005
+title: Chief Payroll Stooge
+userPassword: grebxAenai
+uid: Annadiane_Axberg
+givenName: Annadiane
+mail: Annadiane_Axberg@example.com
+carLicense: LUT4HGU
+departmentNumber: 7433
+employeeType: Contract
+homePhone: +1 303 865-6381
+initials: A. A.
+mobile: +1 415 801-6258
+pager: +1 303 733-6064
+manager: cn=Berangere Walker
+secretary: cn=Vahid Hylarides
+roomNumber: 3756
+
+dn: cn=Fu-Shin Cantlie, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Fu-Shin Cantlie
+sn: Cantlie
+description: This is Fu-Shin Cantlie's description
+facsimileTelephoneNumber: +1 510 953-3859
+l: Cambridge
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 286
+telephoneNumber: +1 206 313-2050
+title: Supreme Product Testing Sales Rep
+userPassword: eiltnaCnih
+uid: Fu-Shin_Cantlie
+givenName: Fu-Shin
+mail: Fu-Shin_Cantlie@example.com
+carLicense: M8S7K9C
+departmentNumber: 8318
+employeeType: Normal
+homePhone: +1 206 216-6429
+initials: F. C.
+mobile: +1 213 974-6973
+pager: +1 510 472-2562
+manager: cn=Jimson Brantley
+secretary: cn=Etty Castillo
+roomNumber: 7956
+
+dn: cn=Tedda Langenberg, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tedda Langenberg
+sn: Langenberg
+description: This is Tedda Langenberg's description
+facsimileTelephoneNumber: +1 71 730-8246
+l: Cupertino
+ou: Accounting
+postalAddress: example$Accounting$Dept # 929
+telephoneNumber: +1 510 358-8817
+title: Senior Accounting Manager
+userPassword: grebnegnaL
+uid: Tedda_Langenberg
+givenName: Tedda
+mail: Tedda_Langenberg@example.com
+carLicense: 21R9LZO
+departmentNumber: 9330
+employeeType: Employee
+homePhone: +1 213 457-6359
+initials: T. L.
+mobile: +1 510 400-7885
+pager: +1 804 425-3098
+manager: cn=Ron Sanzone
+secretary: cn=Zuben Lukers
+roomNumber: 91
+
+dn: cn=Brana Suda, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Brana Suda
+sn: Suda
+description: This is Brana Suda's description
+facsimileTelephoneNumber: +1 510 924-3977
+l: Emeryville
+ou: Planning
+postalAddress: example$Planning$Dept # 942
+telephoneNumber: +1 818 460-4131
+title: Master Planning Accountant
+userPassword: aduSanarB
+uid: Brana_Suda
+givenName: Brana
+mail: Brana_Suda@example.com
+carLicense: M50B1H0
+departmentNumber: 4903
+employeeType: Employee
+homePhone: +1 206 907-3010
+initials: B. S.
+mobile: +1 71 388-7659
+pager: +1 408 832-9280
+manager: cn=Wendy Amini
+secretary: cn=Mika Schmoe
+roomNumber: 5050
+
+dn: cn=Eadith Fradette, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Eadith Fradette
+sn: Fradette
+description: This is Eadith Fradette's description
+facsimileTelephoneNumber: +1 818 220-1528
+l: San Jose
+ou: Administrative
+postalAddress: example$Administrative$Dept # 889
+telephoneNumber: +1 818 970-1343
+title: Senior Administrative Vice President
+userPassword: ettedarFht
+uid: Eadith_Fradette
+givenName: Eadith
+mail: Eadith_Fradette@example.com
+carLicense: 9KWU50X
+departmentNumber: 9660
+employeeType: Contract
+homePhone: +1 804 824-9292
+initials: E. F.
+mobile: +1 415 727-1332
+pager: +1 510 128-9094
+manager: cn=Darren Talbot
+secretary: cn=Phoenix Van Phil
+roomNumber: 3334
+
+dn: cn=Clio Iyengar, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Clio Iyengar
+sn: Iyengar
+description: This is Clio Iyengar's description
+facsimileTelephoneNumber: +1 206 683-9934
+l: Cupertino
+ou: Administrative
+postalAddress: example$Administrative$Dept # 178
+telephoneNumber: +1 818 430-9658
+title: Chief Administrative Grunt
+userPassword: ragneyIoil
+uid: Clio_Iyengar
+givenName: Clio
+mail: Clio_Iyengar@example.com
+carLicense: 54FIVQO
+departmentNumber: 1660
+employeeType: Employee
+homePhone: +1 213 169-4863
+initials: C. I.
+mobile: +1 206 491-7672
+pager: +1 71 531-5105
+manager: cn=Lujanka Meisner
+secretary: cn=Natassia Perreault
+roomNumber: 2974
+
+dn: cn=Motaz Mrozinski, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Motaz Mrozinski
+sn: Mrozinski
+description: This is Motaz Mrozinski's description
+facsimileTelephoneNumber: +1 804 406-5061
+l: San Jose
+ou: Payroll
+postalAddress: example$Payroll$Dept # 894
+telephoneNumber: +1 213 539-4117
+title: Supreme Payroll Mascot
+userPassword: iksnizorMz
+uid: Motaz_Mrozinski
+givenName: Motaz
+mail: Motaz_Mrozinski@example.com
+carLicense: O98UZA3
+departmentNumber: 8943
+employeeType: Employee
+homePhone: +1 415 714-8328
+initials: M. M.
+mobile: +1 415 300-4474
+pager: +1 303 266-4788
+manager: cn=Lucien Mihan
+secretary: cn=Mirilla Sobiesiak
+roomNumber: 3718
+
+dn: cn=Jose Woodward-Jack, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jose Woodward-Jack
+sn: Woodward-Jack
+description: This is Jose Woodward-Jack's description
+facsimileTelephoneNumber: +1 303 205-2281
+l: Mountain View
+ou: Administrative
+postalAddress: example$Administrative$Dept # 346
+telephoneNumber: +1 415 177-7564
+title: Junior Administrative Manager
+userPassword: kcaJ-drawd
+uid: Jose_Woodward-Jack
+givenName: Jose
+mail: Jose_Woodward-Jack@example.com
+carLicense: 4XWHTCY
+departmentNumber: 7557
+employeeType: Employee
+homePhone: +1 415 241-9728
+initials: J. W.
+mobile: +1 415 173-2547
+pager: +1 415 609-3295
+manager: cn=Schaffer Milloy
+secretary: cn=Naser Michaels
+roomNumber: 6069
+
+dn: cn=Dyane Kluger, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dyane Kluger
+sn: Kluger
+description: This is Dyane Kluger's description
+facsimileTelephoneNumber: +1 415 234-3632
+l: Redwood Shores
+ou: Administrative
+postalAddress: example$Administrative$Dept # 64
+telephoneNumber: +1 510 634-4698
+title: Chief Administrative Admin
+userPassword: regulKenay
+uid: Dyane_Kluger
+givenName: Dyane
+mail: Dyane_Kluger@example.com
+carLicense: EWQY2L7
+departmentNumber: 7143
+employeeType: Temp
+homePhone: +1 408 442-1794
+initials: D. K.
+mobile: +1 804 340-5393
+pager: +1 213 895-5852
+manager: cn=Electra Kato
+secretary: cn=Chi Alberse
+roomNumber: 5974
+
+dn: cn=Esmeralda Mahonen, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Esmeralda Mahonen
+sn: Mahonen
+description: This is Esmeralda Mahonen's description
+facsimileTelephoneNumber: +1 71 138-4348
+l: Cambridge
+ou: Peons
+postalAddress: example$Peons$Dept # 825
+telephoneNumber: +1 206 997-5694
+title: Associate Peons Figurehead
+userPassword: nenohaMadl
+uid: Esmeralda_Mahonen
+givenName: Esmeralda
+mail: Esmeralda_Mahonen@example.com
+carLicense: GWXKRMK
+departmentNumber: 627
+employeeType: Manager
+homePhone: +1 206 483-6026
+initials: E. M.
+mobile: +1 510 679-9847
+pager: +1 408 514-7670
+manager: cn=Fouad Brydon
+secretary: cn=Lebbie Mulroney
+roomNumber: 2793
+
+dn: cn=Dominga Whitehurst, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dominga Whitehurst
+sn: Whitehurst
+description: This is Dominga Whitehurst's description
+facsimileTelephoneNumber: +1 213 256-8990
+l: Mountain View
+ou: Management
+postalAddress: example$Management$Dept # 58
+telephoneNumber: +1 408 806-4907
+title: Master Management Architect
+userPassword: tsruhetihW
+uid: Dominga_Whitehurst
+givenName: Dominga
+mail: Dominga_Whitehurst@example.com
+carLicense: Z3CGDJZ
+departmentNumber: 9137
+employeeType: Contract
+homePhone: +1 71 699-1081
+initials: D. W.
+mobile: +1 71 588-2210
+pager: +1 415 207-4707
+manager: cn=Ashlie Constantinescu
+secretary: cn=Catlee Purohit
+roomNumber: 5411
+
+dn: cn=Lusa Barsch, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lusa Barsch
+sn: Barsch
+description: This is Lusa Barsch's description
+facsimileTelephoneNumber: +1 303 992-6949
+l: Emeryville
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 545
+telephoneNumber: +1 303 543-7858
+title: Supreme Product Testing Artist
+userPassword: hcsraBasuL
+uid: Lusa_Barsch
+givenName: Lusa
+mail: Lusa_Barsch@example.com
+carLicense: AGDA0GA
+departmentNumber: 6239
+employeeType: Manager
+homePhone: +1 510 840-8962
+initials: L. B.
+mobile: +1 415 261-5608
+pager: +1 415 252-7480
+manager: cn=Pic Silwer
+secretary: cn=Shlomo Klein
+roomNumber: 70
+
+dn: cn=Vilas Pastorek, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Vilas Pastorek
+sn: Pastorek
+description: This is Vilas Pastorek's description
+facsimileTelephoneNumber: +1 818 644-9466
+l: Alameda
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 542
+telephoneNumber: +1 408 817-3212
+title: Elite Human Resources Admin
+userPassword: kerotsaPsa
+uid: Vilas_Pastorek
+givenName: Vilas
+mail: Vilas_Pastorek@example.com
+carLicense: NYOHGI6
+departmentNumber: 2558
+employeeType: Temp
+homePhone: +1 71 218-9597
+initials: V. P.
+mobile: +1 71 644-1561
+pager: +1 818 618-9594
+manager: cn=Elke Schultz
+secretary: cn=Sherryl Brys
+roomNumber: 5677
+
+dn: cn=Randene O'Toole, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Randene O'Toole
+sn: O'Toole
+description: This is Randene O'Toole's description
+facsimileTelephoneNumber: +1 303 409-7211
+l: Armonk
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 582
+telephoneNumber: +1 206 690-2493
+title: Chief Product Testing Assistant
+userPassword: elooT'Oene
+uid: Randene_O'Toole
+givenName: Randene
+mail: Randene_O'Toole@example.com
+carLicense: 0OML3RV
+departmentNumber: 5465
+employeeType: Contract
+homePhone: +1 415 321-7409
+initials: R. O.
+mobile: +1 408 116-2472
+pager: +1 303 759-3332
+manager: cn=Kai-Wai McNally
+secretary: cn=Jun Flewelling
+roomNumber: 1771
+
+dn: cn=Korrie Sist, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Korrie Sist
+sn: Sist
+description: This is Korrie Sist's description
+facsimileTelephoneNumber: +1 71 539-8066
+l: Sunnyvale
+ou: Accounting
+postalAddress: example$Accounting$Dept # 873
+telephoneNumber: +1 818 769-4028
+title: Junior Accounting Vice President
+userPassword: tsiSeirroK
+uid: Korrie_Sist
+givenName: Korrie
+mail: Korrie_Sist@example.com
+carLicense: UGBCAOW
+departmentNumber: 7471
+employeeType: Contract
+homePhone: +1 818 597-4171
+initials: K. S.
+mobile: +1 71 724-1606
+pager: +1 303 856-1901
+manager: cn=Norah Narasimhan
+secretary: cn=Helenelizabeth Rabzel
+roomNumber: 5415
+
+dn: cn=Dulci Armstrong, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dulci Armstrong
+sn: Armstrong
+description: This is Dulci Armstrong's description
+facsimileTelephoneNumber: +1 303 511-4654
+l: Menlo Park
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 272
+telephoneNumber: +1 71 431-3711
+title: Supreme Human Resources Yahoo
+userPassword: gnortsmrAi
+uid: Dulci_Armstrong
+givenName: Dulci
+mail: Dulci_Armstrong@example.com
+carLicense: 1E2YUTW
+departmentNumber: 323
+employeeType: Temp
+homePhone: +1 804 862-9466
+initials: D. A.
+mobile: +1 415 767-6314
+pager: +1 818 623-4482
+manager: cn=Ott Burleigh
+secretary: cn=Brock Guillaume
+roomNumber: 421
+
+dn: cn=Gillan Ress, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gillan Ress
+sn: Ress
+description: This is Gillan Ress's description
+facsimileTelephoneNumber: +1 818 798-5635
+l: Sunnyvale
+ou: Management
+postalAddress: example$Management$Dept # 640
+telephoneNumber: +1 415 414-9375
+title: Elite Management Technician
+userPassword: sseRnalliG
+uid: Gillan_Ress
+givenName: Gillan
+mail: Gillan_Ress@example.com
+carLicense: 0H8LH9Z
+departmentNumber: 2077
+employeeType: Contract
+homePhone: +1 818 304-3376
+initials: G. R.
+mobile: +1 818 167-9570
+pager: +1 303 338-7776
+manager: cn=Violet Ainsworth
+secretary: cn=Horst Schryburt
+roomNumber: 4160
+
+dn: cn=Conny Rufino, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Conny Rufino
+sn: Rufino
+description: This is Conny Rufino's description
+facsimileTelephoneNumber: +1 303 248-4094
+l: Redmond
+ou: Management
+postalAddress: example$Management$Dept # 833
+telephoneNumber: +1 206 482-9073
+title: Chief Management Engineer
+userPassword: onifuRynno
+uid: Conny_Rufino
+givenName: Conny
+mail: Conny_Rufino@example.com
+carLicense: 4C1O7IN
+departmentNumber: 9683
+employeeType: Employee
+homePhone: +1 818 497-4776
+initials: C. R.
+mobile: +1 206 217-3025
+pager: +1 415 462-6384
+manager: cn=Hetty Nevison
+secretary: cn=Lari Stctest
+roomNumber: 8179
+
+dn: cn=Marleen FWPtools, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marleen FWPtools
+sn: FWPtools
+description: This is Marleen FWPtools's description
+facsimileTelephoneNumber: +1 303 778-4309
+l: Mountain View
+ou: Product Development
+postalAddress: example$Product Development$Dept # 247
+telephoneNumber: +1 510 715-1783
+title: Associate Product Development President
+userPassword: slootPWFne
+uid: Marleen_FWPtools
+givenName: Marleen
+mail: Marleen_FWPtools@example.com
+carLicense: WUQ4LSG
+departmentNumber: 3881
+employeeType: Temp
+homePhone: +1 408 196-4785
+initials: M. F.
+mobile: +1 206 877-2976
+pager: +1 818 433-5574
+manager: cn=Leshia Sommerdorf
+secretary: cn=Gale Borozny
+roomNumber: 8246
+
+dn: cn=Aviva Deslandes, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Aviva Deslandes
+sn: Deslandes
+description: This is Aviva Deslandes's description
+facsimileTelephoneNumber: +1 206 995-9649
+l: Armonk
+ou: Planning
+postalAddress: example$Planning$Dept # 582
+telephoneNumber: +1 71 503-6775
+title: Associate Planning Accountant
+userPassword: sednalseDa
+uid: Aviva_Deslandes
+givenName: Aviva
+mail: Aviva_Deslandes@example.com
+carLicense: 6Q8FJHO
+departmentNumber: 5225
+employeeType: Employee
+homePhone: +1 510 122-7820
+initials: A. D.
+mobile: +1 415 883-1763
+pager: +1 804 146-9427
+manager: cn=Nertie Kechichian
+secretary: cn=Pauly Rudisill
+roomNumber: 8041
+
+dn: cn=Wileen Logarajah, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Wileen Logarajah
+sn: Logarajah
+description: This is Wileen Logarajah's description
+facsimileTelephoneNumber: +1 818 323-6643
+l: Menlo Park
+ou: Peons
+postalAddress: example$Peons$Dept # 559
+telephoneNumber: +1 510 971-5342
+title: Associate Peons Architect
+userPassword: hajaragoLn
+uid: Wileen_Logarajah
+givenName: Wileen
+mail: Wileen_Logarajah@example.com
+carLicense: VO5KKO6
+departmentNumber: 5171
+employeeType: Contract
+homePhone: +1 71 613-2879
+initials: W. L.
+mobile: +1 415 857-8255
+pager: +1 71 403-9536
+manager: cn=Brittan Maunu
+secretary: cn=Jessamyn Gallegos
+roomNumber: 4047
+
+dn: cn=Emogene Florence, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Emogene Florence
+sn: Florence
+description: This is Emogene Florence's description
+facsimileTelephoneNumber: +1 804 192-2290
+l: Redwood Shores
+ou: Product Development
+postalAddress: example$Product Development$Dept # 804
+telephoneNumber: +1 213 146-8522
+title: Senior Product Development Madonna
+userPassword: ecnerolFen
+uid: Emogene_Florence
+givenName: Emogene
+mail: Emogene_Florence@example.com
+carLicense: F3SWANH
+departmentNumber: 9790
+employeeType: Contract
+homePhone: +1 303 421-7847
+initials: E. F.
+mobile: +1 206 833-7435
+pager: +1 415 393-7829
+manager: cn=Kaitlyn Przybycien
+secretary: cn=Thea Kokoska
+roomNumber: 2817
+
+dn: cn=Emelia Mote, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Emelia Mote
+sn: Mote
+description: This is Emelia Mote's description
+facsimileTelephoneNumber: +1 818 813-7269
+l: Santa Clara
+ou: Peons
+postalAddress: example$Peons$Dept # 775
+telephoneNumber: +1 71 746-8313
+title: Chief Peons Evangelist
+userPassword: etoMailemE
+uid: Emelia_Mote
+givenName: Emelia
+mail: Emelia_Mote@example.com
+carLicense: 7CSXD60
+departmentNumber: 9064
+employeeType: Manager
+homePhone: +1 71 467-2178
+initials: E. M.
+mobile: +1 408 942-9846
+pager: +1 804 135-5252
+manager: cn=Ashok Tahir
+secretary: cn=Dita Estey
+roomNumber: 7255
+
+dn: cn=Harrietta McGarry, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Harrietta McGarry
+sn: McGarry
+description: This is Harrietta McGarry's description
+facsimileTelephoneNumber: +1 804 825-5556
+l: Sunnyvale
+ou: Product Development
+postalAddress: example$Product Development$Dept # 685
+telephoneNumber: +1 206 692-1375
+title: Junior Product Development Architect
+userPassword: yrraGcMatt
+uid: Harrietta_McGarry
+givenName: Harrietta
+mail: Harrietta_McGarry@example.com
+carLicense: UREW39H
+departmentNumber: 9511
+employeeType: Contract
+homePhone: +1 206 567-8178
+initials: H. M.
+mobile: +1 71 751-8182
+pager: +1 408 740-5933
+manager: cn=Deepak Moen
+secretary: cn=Hillary Szabo
+roomNumber: 1564
+
+dn: cn=Mardi Hosier, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Mardi Hosier
+sn: Hosier
+description: This is Mardi Hosier's description
+facsimileTelephoneNumber: +1 818 986-7690
+l: Menlo Park
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 885
+telephoneNumber: +1 510 231-5597
+title: Master Janitorial Consultant
+userPassword: reisoHidra
+uid: Mardi_Hosier
+givenName: Mardi
+mail: Mardi_Hosier@example.com
+carLicense: TG3MHCD
+departmentNumber: 1504
+employeeType: Manager
+homePhone: +1 303 436-3978
+initials: M. H.
+mobile: +1 415 703-2374
+pager: +1 510 715-9620
+manager: cn=Glynis Readling
+secretary: cn=Jastinder Sokolowski
+roomNumber: 1897
+
+dn: cn=Natasja Omura, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Natasja Omura
+sn: Omura
+description: This is Natasja Omura's description
+facsimileTelephoneNumber: +1 213 583-5434
+l: San Mateo
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 645
+telephoneNumber: +1 818 691-2933
+title: Supreme Human Resources Dictator
+userPassword: arumOajsat
+uid: Natasja_Omura
+givenName: Natasja
+mail: Natasja_Omura@example.com
+carLicense: OQSGXN6
+departmentNumber: 4997
+employeeType: Temp
+homePhone: +1 213 258-7791
+initials: N. O.
+mobile: +1 71 238-3159
+pager: +1 213 603-7155
+manager: cn=Lishe Da Gama
+secretary: cn=Der-Chang Gopaul
+roomNumber: 4402
+
+dn: cn=Ciel Ghorashy, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ciel Ghorashy
+sn: Ghorashy
+description: This is Ciel Ghorashy's description
+facsimileTelephoneNumber: +1 804 977-3660
+l: Cupertino
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 276
+telephoneNumber: +1 415 689-6453
+title: Elite Product Testing Stooge
+userPassword: yhsarohGle
+uid: Ciel_Ghorashy
+givenName: Ciel
+mail: Ciel_Ghorashy@example.com
+carLicense: OMJGBXM
+departmentNumber: 9175
+employeeType: Employee
+homePhone: +1 213 862-8793
+initials: C. G.
+mobile: +1 804 902-1620
+pager: +1 818 786-1258
+manager: cn=Mersey Morden
+secretary: cn=Hafeezah Fodell
+roomNumber: 92
+
+dn: cn=Derick McNitt, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Derick McNitt
+sn: McNitt
+description: This is Derick McNitt's description
+facsimileTelephoneNumber: +1 818 113-9552
+l: Menlo Park
+ou: Product Development
+postalAddress: example$Product Development$Dept # 463
+telephoneNumber: +1 206 910-1467
+title: Senior Product Development Engineer
+userPassword: ttiNcMkcir
+uid: Derick_McNitt
+givenName: Derick
+mail: Derick_McNitt@example.com
+carLicense: 4PDV6J7
+departmentNumber: 4317
+employeeType: Contract
+homePhone: +1 206 808-6633
+initials: D. M.
+mobile: +1 415 795-3783
+pager: +1 206 414-3820
+manager: cn=Vera Vanderhelm
+secretary: cn=Remy Friedberg
+roomNumber: 997
+
+dn: cn=Hermann Hammel, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Hermann Hammel
+sn: Hammel
+description: This is Hermann Hammel's description
+facsimileTelephoneNumber: +1 415 843-3404
+l: Menlo Park
+ou: Planning
+postalAddress: example$Planning$Dept # 124
+telephoneNumber: +1 818 874-9081
+title: Senior Planning Vice President
+userPassword: lemmaHnnam
+uid: Hermann_Hammel
+givenName: Hermann
+mail: Hermann_Hammel@example.com
+carLicense: 0ZWAXUF
+departmentNumber: 7105
+employeeType: Normal
+homePhone: +1 510 218-3986
+initials: H. H.
+mobile: +1 818 385-3986
+pager: +1 804 188-2368
+manager: cn=Kien Deugo
+secretary: cn=Izumi Berrisford
+roomNumber: 1983
+
+dn: cn=Weringh Bennefeld, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Weringh Bennefeld
+sn: Bennefeld
+description: This is Weringh Bennefeld's description
+facsimileTelephoneNumber: +1 213 292-2935
+l: Emeryville
+ou: Peons
+postalAddress: example$Peons$Dept # 10
+telephoneNumber: +1 206 879-8616
+title: Chief Peons Madonna
+userPassword: dlefenneBh
+uid: Weringh_Bennefeld
+givenName: Weringh
+mail: Weringh_Bennefeld@example.com
+carLicense: TZCORI9
+departmentNumber: 356
+employeeType: Normal
+homePhone: +1 206 290-6512
+initials: W. B.
+mobile: +1 71 378-5716
+pager: +1 818 629-7598
+manager: cn=Eveleen Coppedge
+secretary: cn=Hulda Giekes
+roomNumber: 8917
+
+dn: cn=Jackson Finley, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jackson Finley
+sn: Finley
+description: This is Jackson Finley's description
+facsimileTelephoneNumber: +1 804 681-9655
+l: Milpitas
+ou: Product Development
+postalAddress: example$Product Development$Dept # 980
+telephoneNumber: +1 213 293-1895
+title: Supreme Product Development Yahoo
+userPassword: yelniFnosk
+uid: Jackson_Finley
+givenName: Jackson
+mail: Jackson_Finley@example.com
+carLicense: 0K2SXZO
+departmentNumber: 6521
+employeeType: Temp
+homePhone: +1 213 173-2127
+initials: J. F.
+mobile: +1 213 798-3466
+pager: +1 415 648-1843
+manager: cn=Ashley Moree
+secretary: cn=Ekaterina Adhem
+roomNumber: 3539
+
+dn: cn=Fianna Herman, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Fianna Herman
+sn: Herman
+description: This is Fianna Herman's description
+facsimileTelephoneNumber: +1 206 356-2173
+l: Redmond
+ou: Planning
+postalAddress: example$Planning$Dept # 656
+telephoneNumber: +1 408 107-4282
+title: Senior Planning Dictator
+userPassword: namreHanna
+uid: Fianna_Herman
+givenName: Fianna
+mail: Fianna_Herman@example.com
+carLicense: YGE8PG8
+departmentNumber: 6047
+employeeType: Manager
+homePhone: +1 415 774-2912
+initials: F. H.
+mobile: +1 510 960-4071
+pager: +1 408 495-4476
+manager: cn=Paulina Toth
+secretary: cn=Cherlyn Diradmin
+roomNumber: 9571
+
+dn: cn=Tad Bolduc, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tad Bolduc
+sn: Bolduc
+description: This is Tad Bolduc's description
+facsimileTelephoneNumber: +1 408 468-5854
+l: Sunnyvale
+ou: Payroll
+postalAddress: example$Payroll$Dept # 385
+telephoneNumber: +1 510 466-1350
+title: Elite Payroll Janitor
+userPassword: cudloBdaT
+uid: Tad_Bolduc
+givenName: Tad
+mail: Tad_Bolduc@example.com
+carLicense: PVDSMSZ
+departmentNumber: 7001
+employeeType: Contract
+homePhone: +1 415 532-9111
+initials: T. B.
+mobile: +1 206 419-9095
+pager: +1 510 552-3827
+manager: cn=Celisse McLawhon
+secretary: cn=Ranson Robitaille
+roomNumber: 6774
+
+dn: cn=Mustafa Goodfellow, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Mustafa Goodfellow
+sn: Goodfellow
+description: This is Mustafa Goodfellow's description
+facsimileTelephoneNumber: +1 804 747-3201
+l: Mountain View
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 832
+telephoneNumber: +1 206 759-5792
+title: Elite Product Testing Visionary
+userPassword: wollefdooG
+uid: Mustafa_Goodfellow
+givenName: Mustafa
+mail: Mustafa_Goodfellow@example.com
+carLicense: T7H8WEV
+departmentNumber: 1733
+employeeType: Temp
+homePhone: +1 213 707-6191
+initials: M. G.
+mobile: +1 206 708-2540
+pager: +1 804 267-1608
+manager: cn=Georgianne Keehn
+secretary: cn=Jianli Feil
+roomNumber: 5129
+
+dn: cn=Davida Waloff, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Davida Waloff
+sn: Waloff
+description: This is Davida Waloff's description
+facsimileTelephoneNumber: +1 804 575-1950
+l: Cambridge
+ou: Planning
+postalAddress: example$Planning$Dept # 226
+telephoneNumber: +1 71 574-4002
+title: Elite Planning Czar
+userPassword: ffolaWadiv
+uid: Davida_Waloff
+givenName: Davida
+mail: Davida_Waloff@example.com
+carLicense: 72PCPKG
+departmentNumber: 935
+employeeType: Temp
+homePhone: +1 804 251-5967
+initials: D. W.
+mobile: +1 510 857-7901
+pager: +1 510 205-5810
+manager: cn=Naohiko Gostanian
+secretary: cn=Anje Borodajluk
+roomNumber: 1127
+
+dn: cn=Gupta Dantu, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gupta Dantu
+sn: Dantu
+description: This is Gupta Dantu's description
+facsimileTelephoneNumber: +1 206 711-1440
+l: Cupertino
+ou: Product Development
+postalAddress: example$Product Development$Dept # 169
+telephoneNumber: +1 415 967-4634
+title: Master Product Development Director
+userPassword: utnaDatpuG
+uid: Gupta_Dantu
+givenName: Gupta
+mail: Gupta_Dantu@example.com
+carLicense: A2GSAY5
+departmentNumber: 8255
+employeeType: Temp
+homePhone: +1 510 606-7627
+initials: G. D.
+mobile: +1 510 952-7193
+pager: +1 213 121-6362
+manager: cn=Berta Admin-mtv
+secretary: cn=Gladi IRCMTL
+roomNumber: 3448
+
+dn: cn=Benefits Mukherjee, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Benefits Mukherjee
+sn: Mukherjee
+description: This is Benefits Mukherjee's description
+facsimileTelephoneNumber: +1 213 968-8685
+l: Fremont
+ou: Management
+postalAddress: example$Management$Dept # 793
+telephoneNumber: +1 71 161-9882
+title: Supreme Management Technician
+userPassword: eejrehkuMs
+uid: Benefits_Mukherjee
+givenName: Benefits
+mail: Benefits_Mukherjee@example.com
+carLicense: OVBO65A
+departmentNumber: 6506
+employeeType: Manager
+homePhone: +1 213 925-2365
+initials: B. M.
+mobile: +1 408 801-9786
+pager: +1 415 305-8600
+manager: cn=Mala Bunzey
+secretary: cn=Anabelle Boyce
+roomNumber: 5054
+
+dn: cn=Omar Dokken, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Omar Dokken
+sn: Dokken
+description: This is Omar Dokken's description
+facsimileTelephoneNumber: +1 408 499-9111
+l: Redmond
+ou: Peons
+postalAddress: example$Peons$Dept # 540
+telephoneNumber: +1 408 352-4507
+title: Senior Peons Assistant
+userPassword: nekkoDramO
+uid: Omar_Dokken
+givenName: Omar
+mail: Omar_Dokken@example.com
+carLicense: I1UEWPP
+departmentNumber: 6095
+employeeType: Contract
+homePhone: +1 818 121-6046
+initials: O. D.
+mobile: +1 71 392-7484
+pager: +1 303 306-4750
+manager: cn=Kjell Groulx
+secretary: cn=Faustine Leavitt
+roomNumber: 3423
+
+dn: cn=Phillis Ganguly, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Phillis Ganguly
+sn: Ganguly
+description: This is Phillis Ganguly's description
+facsimileTelephoneNumber: +1 408 352-5183
+l: Menlo Park
+ou: Administrative
+postalAddress: example$Administrative$Dept # 988
+telephoneNumber: +1 303 456-1127
+title: Supreme Administrative Writer
+userPassword: ylugnaGsil
+uid: Phillis_Ganguly
+givenName: Phillis
+mail: Phillis_Ganguly@example.com
+carLicense: WGF2X9I
+departmentNumber: 1524
+employeeType: Contract
+homePhone: +1 415 632-7072
+initials: P. G.
+mobile: +1 415 963-2037
+pager: +1 804 409-2986
+manager: cn=Tova Cuddy
+secretary: cn=Willy Zwick
+roomNumber: 1992
+
+dn: cn=Vahe Schlachter, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Vahe Schlachter
+sn: Schlachter
+description: This is Vahe Schlachter's description
+facsimileTelephoneNumber: +1 71 205-6557
+l: Redmond
+ou: Administrative
+postalAddress: example$Administrative$Dept # 491
+telephoneNumber: +1 303 978-4355
+title: Chief Administrative Yahoo
+userPassword: rethcalhcS
+uid: Vahe_Schlachter
+givenName: Vahe
+mail: Vahe_Schlachter@example.com
+carLicense: BO15FKQ
+departmentNumber: 3671
+employeeType: Contract
+homePhone: +1 213 835-8797
+initials: V. S.
+mobile: +1 303 337-1436
+pager: +1 818 603-7610
+manager: cn=Melodie Beswick
+secretary: cn=Mat Madigan
+roomNumber: 4002
+
+dn: cn=Ede Abrahim, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ede Abrahim
+sn: Abrahim
+description: This is Ede Abrahim's description
+facsimileTelephoneNumber: +1 804 968-1719
+l: Cambridge
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 719
+telephoneNumber: +1 415 845-4115
+title: Senior Janitorial Developer
+userPassword: miharbAedE
+uid: Ede_Abrahim
+givenName: Ede
+mail: Ede_Abrahim@example.com
+carLicense: S4QCVN9
+departmentNumber: 8351
+employeeType: Manager
+homePhone: +1 818 833-7793
+initials: E. A.
+mobile: +1 303 314-5571
+pager: +1 408 164-1519
+manager: cn=Lilah Cuffle
+secretary: cn=Verina Letsome
+roomNumber: 3620
+
+dn: cn=Liesbeth Burkepile, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Liesbeth Burkepile
+sn: Burkepile
+description: This is Liesbeth Burkepile's description
+facsimileTelephoneNumber: +1 303 203-9722
+l: Palo Alto
+ou: Planning
+postalAddress: example$Planning$Dept # 413
+telephoneNumber: +1 303 106-5961
+title: Junior Planning Accountant
+userPassword: elipekruBh
+uid: Liesbeth_Burkepile
+givenName: Liesbeth
+mail: Liesbeth_Burkepile@example.com
+carLicense: AW7OL9W
+departmentNumber: 5512
+employeeType: Normal
+homePhone: +1 415 529-3794
+initials: L. B.
+mobile: +1 510 278-1822
+pager: +1 71 399-2751
+manager: cn=Nert Sponagle
+secretary: cn=Bela Moledina
+roomNumber: 185
+
+dn: cn=Karan Betcher, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Karan Betcher
+sn: Betcher
+description: This is Karan Betcher's description
+facsimileTelephoneNumber: +1 818 977-5803
+l: San Mateo
+ou: Management
+postalAddress: example$Management$Dept # 91
+telephoneNumber: +1 206 348-7764
+title: Associate Management Writer
+userPassword: rehcteBnar
+uid: Karan_Betcher
+givenName: Karan
+mail: Karan_Betcher@example.com
+carLicense: BVFA25X
+departmentNumber: 3472
+employeeType: Normal
+homePhone: +1 206 467-2902
+initials: K. B.
+mobile: +1 415 885-7956
+pager: +1 510 192-2376
+manager: cn=Marjan Jaworsky
+secretary: cn=Renelle Letchworth
+roomNumber: 4389
+
+dn: cn=Anup Schlobohm, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Anup Schlobohm
+sn: Schlobohm
+description: This is Anup Schlobohm's description
+facsimileTelephoneNumber: +1 206 332-8863
+l: Mountain View
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 675
+telephoneNumber: +1 71 552-9836
+title: Chief Product Testing Janitor
+userPassword: mhobolhcSp
+uid: Anup_Schlobohm
+givenName: Anup
+mail: Anup_Schlobohm@example.com
+carLicense: 0FB1HB8
+departmentNumber: 2304
+employeeType: Manager
+homePhone: +1 303 565-9859
+initials: A. S.
+mobile: +1 415 473-6448
+pager: +1 804 368-3192
+manager: cn=Fabien Lafleur
+secretary: cn=Tish Sova
+roomNumber: 2336
+
+dn: cn=Elsi Frankenberger, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Elsi Frankenberger
+sn: Frankenberger
+description: This is Elsi Frankenberger's description
+facsimileTelephoneNumber: +1 213 178-9870
+l: Sunnyvale
+ou: Management
+postalAddress: example$Management$Dept # 446
+telephoneNumber: +1 303 876-3308
+title: Master Management President
+userPassword: regrebnekn
+uid: Elsi_Frankenberger
+givenName: Elsi
+mail: Elsi_Frankenberger@example.com
+carLicense: LV0Z44B
+departmentNumber: 7834
+employeeType: Temp
+homePhone: +1 71 774-4353
+initials: E. F.
+mobile: +1 213 306-9166
+pager: +1 510 399-4748
+manager: cn=Adorne Bennison
+secretary: cn=Pauly Philion
+roomNumber: 3478
+
+dn: cn=Nasser Barcza, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Nasser Barcza
+sn: Barcza
+description: This is Nasser Barcza's description
+facsimileTelephoneNumber: +1 213 546-3662
+l: San Mateo
+ou: Management
+postalAddress: example$Management$Dept # 305
+telephoneNumber: +1 408 287-2998
+title: Supreme Management Vice President
+userPassword: azcraBress
+uid: Nasser_Barcza
+givenName: Nasser
+mail: Nasser_Barcza@example.com
+carLicense: 953UC3Y
+departmentNumber: 8332
+employeeType: Contract
+homePhone: +1 213 820-3768
+initials: N. B.
+mobile: +1 415 123-1094
+pager: +1 818 667-4905
+manager: cn=Malanie Svo
+secretary: cn=Phan Dumas
+roomNumber: 4887
+
+dn: cn=Afton Desharnais, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Afton Desharnais
+sn: Desharnais
+description: This is Afton Desharnais's description
+facsimileTelephoneNumber: +1 804 705-5987
+l: San Mateo
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 464
+telephoneNumber: +1 415 512-4395
+title: Junior Human Resources Architect
+userPassword: sianrahseD
+uid: Afton_Desharnais
+givenName: Afton
+mail: Afton_Desharnais@example.com
+carLicense: KB99IQT
+departmentNumber: 9595
+employeeType: Employee
+homePhone: +1 804 305-3482
+initials: A. D.
+mobile: +1 213 477-3182
+pager: +1 415 429-3345
+manager: cn=Saeed Derosa
+secretary: cn=Tory Dionne
+roomNumber: 2322
+
+dn: cn=Regina Pizzanelli, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Regina Pizzanelli
+sn: Pizzanelli
+description: This is Regina Pizzanelli's description
+facsimileTelephoneNumber: +1 408 259-2282
+l: San Jose
+ou: Management
+postalAddress: example$Management$Dept # 855
+telephoneNumber: +1 415 843-6135
+title: Elite Management Assistant
+userPassword: illenazziP
+uid: Regina_Pizzanelli
+givenName: Regina
+mail: Regina_Pizzanelli@example.com
+carLicense: MVNK7DJ
+departmentNumber: 6548
+employeeType: Normal
+homePhone: +1 303 719-3887
+initials: R. P.
+mobile: +1 415 853-7286
+pager: +1 303 380-5628
+manager: cn=Amrik Saulnier
+secretary: cn=Tsuyoshi Brisby
+roomNumber: 737
+
+dn: cn=Krissy Ottco, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Krissy Ottco
+sn: Ottco
+description: This is Krissy Ottco's description
+facsimileTelephoneNumber: +1 206 522-8708
+l: Milpitas
+ou: Product Development
+postalAddress: example$Product Development$Dept # 335
+telephoneNumber: +1 804 123-3823
+title: Elite Product Development Visionary
+userPassword: octtOyssir
+uid: Krissy_Ottco
+givenName: Krissy
+mail: Krissy_Ottco@example.com
+carLicense: EHTC6B4
+departmentNumber: 3193
+employeeType: Temp
+homePhone: +1 415 606-5286
+initials: K. O.
+mobile: +1 71 191-1403
+pager: +1 510 612-1380
+manager: cn=Hedi Cotuna
+secretary: cn=Rona Loggins
+roomNumber: 6504
+
+dn: cn=Yogi Maliepaard, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Yogi Maliepaard
+sn: Maliepaard
+description: This is Yogi Maliepaard's description
+facsimileTelephoneNumber: +1 206 427-2189
+l: San Jose
+ou: Product Development
+postalAddress: example$Product Development$Dept # 448
+telephoneNumber: +1 206 502-5949
+title: Supreme Product Development Stooge
+userPassword: draapeilaM
+uid: Yogi_Maliepaard
+givenName: Yogi
+mail: Yogi_Maliepaard@example.com
+carLicense: VX7BJUU
+departmentNumber: 5968
+employeeType: Normal
+homePhone: +1 804 945-2170
+initials: Y. M.
+mobile: +1 408 502-5633
+pager: +1 415 610-5572
+manager: cn=Dre Yuhanna
+secretary: cn=Marthe Stodart
+roomNumber: 2076
+
+dn: cn=Devin Colwell, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Devin Colwell
+sn: Colwell
+description: This is Devin Colwell's description
+facsimileTelephoneNumber: +1 510 857-4693
+l: Cambridge
+ou: Planning
+postalAddress: example$Planning$Dept # 252
+telephoneNumber: +1 818 459-9361
+title: Chief Planning Engineer
+userPassword: llewloCniv
+uid: Devin_Colwell
+givenName: Devin
+mail: Devin_Colwell@example.com
+carLicense: BG1NMSO
+departmentNumber: 1324
+employeeType: Manager
+homePhone: +1 213 316-4005
+initials: D. C.
+mobile: +1 213 356-6732
+pager: +1 818 366-9678
+manager: cn=Modestine Windsor
+secretary: cn=Doretta LeTarte
+roomNumber: 8706
+
+dn: cn=Alan Montelli, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Alan Montelli
+sn: Montelli
+description: This is Alan Montelli's description
+facsimileTelephoneNumber: +1 804 683-1981
+l: Cupertino
+ou: Management
+postalAddress: example$Management$Dept # 769
+telephoneNumber: +1 303 334-8615
+title: Senior Management Stooge
+userPassword: illetnoMna
+uid: Alan_Montelli
+givenName: Alan
+mail: Alan_Montelli@example.com
+carLicense: AJJKB89
+departmentNumber: 9248
+employeeType: Contract
+homePhone: +1 408 950-5244
+initials: A. M.
+mobile: +1 213 827-4891
+pager: +1 415 769-5696
+manager: cn=Murielle Mansbridge
+secretary: cn=Katrine Cwirzen
+roomNumber: 751
+
+dn: cn=Yolanda Schrang, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Yolanda Schrang
+sn: Schrang
+description: This is Yolanda Schrang's description
+facsimileTelephoneNumber: +1 303 747-5524
+l: Fremont
+ou: Payroll
+postalAddress: example$Payroll$Dept # 108
+telephoneNumber: +1 415 127-7924
+title: Master Payroll Writer
+userPassword: gnarhcSadn
+uid: Yolanda_Schrang
+givenName: Yolanda
+mail: Yolanda_Schrang@example.com
+carLicense: L6XGOUU
+departmentNumber: 3381
+employeeType: Manager
+homePhone: +1 804 245-2347
+initials: Y. S.
+mobile: +1 206 296-1523
+pager: +1 415 350-4164
+manager: cn=Qainfo Reporting
+secretary: cn=Sharri McQuarrie
+roomNumber: 3483
+
+dn: cn=Burt Bigley, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Burt Bigley
+sn: Bigley
+description: This is Burt Bigley's description
+facsimileTelephoneNumber: +1 213 394-9377
+l: Milpitas
+ou: Administrative
+postalAddress: example$Administrative$Dept # 566
+telephoneNumber: +1 415 122-3561
+title: Supreme Administrative Mascot
+userPassword: yelgiBtruB
+uid: Burt_Bigley
+givenName: Burt
+mail: Burt_Bigley@example.com
+carLicense: G7U8J5R
+departmentNumber: 8712
+employeeType: Manager
+homePhone: +1 206 649-5420
+initials: B. B.
+mobile: +1 206 352-2127
+pager: +1 818 113-1971
+manager: cn=Evey Keates
+secretary: cn=Sophey Broberg
+roomNumber: 5458
+
+dn: cn=Katja Remillard, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Katja Remillard
+sn: Remillard
+description: This is Katja Remillard's description
+facsimileTelephoneNumber: +1 408 840-3448
+l: Sunnyvale
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 157
+telephoneNumber: +1 303 728-4601
+title: Senior Human Resources Czar
+userPassword: drallimeRa
+uid: Katja_Remillard
+givenName: Katja
+mail: Katja_Remillard@example.com
+carLicense: F3J9QRE
+departmentNumber: 8810
+employeeType: Employee
+homePhone: +1 408 119-3253
+initials: K. R.
+mobile: +1 804 464-8689
+pager: +1 303 891-1853
+manager: cn=Umeko Hagerty
+secretary: cn=Farid Hemens-Davis
+roomNumber: 3521
+
+dn: cn=Michaela Creighton, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Michaela Creighton
+sn: Creighton
+description: This is Michaela Creighton's description
+facsimileTelephoneNumber: +1 206 466-3460
+l: Milpitas
+ou: Administrative
+postalAddress: example$Administrative$Dept # 262
+telephoneNumber: +1 510 639-9600
+title: Senior Administrative Figurehead
+userPassword: nothgierCa
+uid: Michaela_Creighton
+givenName: Michaela
+mail: Michaela_Creighton@example.com
+carLicense: VBGO6JL
+departmentNumber: 5642
+employeeType: Manager
+homePhone: +1 71 885-3371
+initials: M. C.
+mobile: +1 804 164-6067
+pager: +1 804 453-1117
+manager: cn=Rubina Maguire
+secretary: cn=Roberta Dennen
+roomNumber: 2566
+
+dn: cn=Lora-Lee Bowser, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lora-Lee Bowser
+sn: Bowser
+description: This is Lora-Lee Bowser's description
+facsimileTelephoneNumber: +1 510 678-3239
+l: Cupertino
+ou: Product Development
+postalAddress: example$Product Development$Dept # 323
+telephoneNumber: +1 408 614-8676
+title: Associate Product Development Janitor
+userPassword: reswoBeeL-
+uid: Lora-Lee_Bowser
+givenName: Lora-Lee
+mail: Lora-Lee_Bowser@example.com
+carLicense: KO8NPE3
+departmentNumber: 422
+employeeType: Contract
+homePhone: +1 818 558-9697
+initials: L. B.
+mobile: +1 303 206-9542
+pager: +1 303 297-2416
+manager: cn=Tilak Skalski
+secretary: cn=Lisette Chotkowski
+roomNumber: 9474
+
+dn: cn=Utpala Chaar, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Utpala Chaar
+sn: Chaar
+description: This is Utpala Chaar's description
+facsimileTelephoneNumber: +1 818 643-8405
+l: Orem
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 1
+telephoneNumber: +1 303 815-7238
+title: Elite Janitorial Czar
+userPassword: raahCalapt
+uid: Utpala_Chaar
+givenName: Utpala
+mail: Utpala_Chaar@example.com
+carLicense: I8U4SX2
+departmentNumber: 9051
+employeeType: Manager
+homePhone: +1 804 492-7075
+initials: U. C.
+mobile: +1 415 492-8050
+pager: +1 408 629-5000
+manager: cn=Thanh-Tinh Tsao
+secretary: cn=Blancha Tebbe
+roomNumber: 6257
+
+dn: cn=Dael Cowling, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dael Cowling
+sn: Cowling
+description: This is Dael Cowling's description
+facsimileTelephoneNumber: +1 303 168-3958
+l: Redwood Shores
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 834
+telephoneNumber: +1 303 695-8623
+title: Supreme Product Testing Dictator
+userPassword: gnilwoClea
+uid: Dael_Cowling
+givenName: Dael
+mail: Dael_Cowling@example.com
+carLicense: MJQ89G9
+departmentNumber: 4843
+employeeType: Normal
+homePhone: +1 71 392-3443
+initials: D. C.
+mobile: +1 206 634-1798
+pager: +1 303 658-3373
+manager: cn=Richelle Alford
+secretary: cn=Rory Sldisk
+roomNumber: 374
+
+dn: cn=Jobi Angvall, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jobi Angvall
+sn: Angvall
+description: This is Jobi Angvall's description
+facsimileTelephoneNumber: +1 510 298-7779
+l: San Jose
+ou: Management
+postalAddress: example$Management$Dept # 226
+telephoneNumber: +1 213 890-1992
+title: Senior Management Artist
+userPassword: llavgnAibo
+uid: Jobi_Angvall
+givenName: Jobi
+mail: Jobi_Angvall@example.com
+carLicense: F4NXKW4
+departmentNumber: 5133
+employeeType: Temp
+homePhone: +1 408 608-4956
+initials: J. A.
+mobile: +1 213 457-5573
+pager: +1 804 294-9687
+manager: cn=Blondy Skelly
+secretary: cn=Marylinda Epps
+roomNumber: 5391
+
+dn: cn=Ginny Hewett, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ginny Hewett
+sn: Hewett
+description: This is Ginny Hewett's description
+facsimileTelephoneNumber: +1 303 259-9993
+l: Redwood Shores
+ou: Administrative
+postalAddress: example$Administrative$Dept # 651
+telephoneNumber: +1 213 178-6055
+title: Associate Administrative Evangelist
+userPassword: tteweHynni
+uid: Ginny_Hewett
+givenName: Ginny
+mail: Ginny_Hewett@example.com
+carLicense: Y0F9PB0
+departmentNumber: 8553
+employeeType: Contract
+homePhone: +1 213 794-5847
+initials: G. H.
+mobile: +1 303 109-4919
+pager: +1 818 642-1734
+manager: cn=Carmel Milston
+secretary: cn=Ariadne Belrango
+roomNumber: 7434
+
+dn: cn=Nabil Kirn, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Nabil Kirn
+sn: Kirn
+description: This is Nabil Kirn's description
+facsimileTelephoneNumber: +1 213 412-5415
+l: San Mateo
+ou: Administrative
+postalAddress: example$Administrative$Dept # 87
+telephoneNumber: +1 213 248-7293
+title: Elite Administrative Fellow
+userPassword: nriKlibaN
+uid: Nabil_Kirn
+givenName: Nabil
+mail: Nabil_Kirn@example.com
+carLicense: CWUAKSZ
+departmentNumber: 5881
+employeeType: Employee
+homePhone: +1 71 744-6287
+initials: N. K.
+mobile: +1 415 473-2310
+pager: +1 510 538-2296
+manager: cn=Jewel McIntyre
+secretary: cn=Karly Purohit
+roomNumber: 3033
+
+dn: cn=JoAnne Hopcroft, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: JoAnne Hopcroft
+sn: Hopcroft
+description: This is JoAnne Hopcroft's description
+facsimileTelephoneNumber: +1 303 463-5145
+l: Milpitas
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 692
+telephoneNumber: +1 206 948-9516
+title: Supreme Janitorial Dictator
+userPassword: tforcpoHen
+uid: JoAnne_Hopcroft
+givenName: JoAnne
+mail: JoAnne_Hopcroft@example.com
+carLicense: Z8YN8PI
+departmentNumber: 2258
+employeeType: Contract
+homePhone: +1 303 623-3589
+initials: J. H.
+mobile: +1 510 549-9016
+pager: +1 206 359-3508
+manager: cn=Helmuth Geary
+secretary: cn=Erik Whitlock
+roomNumber: 9923
+
+dn: cn=Mila Baumberg, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Mila Baumberg
+sn: Baumberg
+description: This is Mila Baumberg's description
+facsimileTelephoneNumber: +1 818 197-9158
+l: Cupertino
+ou: Planning
+postalAddress: example$Planning$Dept # 953
+telephoneNumber: +1 818 161-5343
+title: Master Planning Madonna
+userPassword: grebmuaBal
+uid: Mila_Baumberg
+givenName: Mila
+mail: Mila_Baumberg@example.com
+carLicense: 7G67VHN
+departmentNumber: 3826
+employeeType: Contract
+homePhone: +1 510 237-2168
+initials: M. B.
+mobile: +1 213 799-8421
+pager: +1 415 857-5821
+manager: cn=Cyb Pillsworth
+secretary: cn=Morgen Beaudin
+roomNumber: 3522
+
+dn: cn=Myranda Eisenhart, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Myranda Eisenhart
+sn: Eisenhart
+description: This is Myranda Eisenhart's description
+facsimileTelephoneNumber: +1 510 527-9770
+l: San Jose
+ou: Administrative
+postalAddress: example$Administrative$Dept # 42
+telephoneNumber: +1 415 449-7845
+title: Elite Administrative Sales Rep
+userPassword: trahnesiEa
+uid: Myranda_Eisenhart
+givenName: Myranda
+mail: Myranda_Eisenhart@example.com
+carLicense: RCMB6TM
+departmentNumber: 7331
+employeeType: Contract
+homePhone: +1 71 191-8459
+initials: M. E.
+mobile: +1 303 520-1257
+pager: +1 213 887-6597
+manager: cn=Merridie Brent
+secretary: cn=Shigeru Marson
+roomNumber: 6049
+
+dn: cn=Daphne Allahyari, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Daphne Allahyari
+sn: Allahyari
+description: This is Daphne Allahyari's description
+facsimileTelephoneNumber: +1 818 642-6904
+l: Cambridge
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 479
+telephoneNumber: +1 510 656-7442
+title: Supreme Product Testing Consultant
+userPassword: irayhallAe
+uid: Daphne_Allahyari
+givenName: Daphne
+mail: Daphne_Allahyari@example.com
+carLicense: 08O0BC9
+departmentNumber: 8086
+employeeType: Employee
+homePhone: +1 818 490-2262
+initials: D. A.
+mobile: +1 206 904-3960
+pager: +1 206 732-8246
+manager: cn=Raoul Fricker
+secretary: cn=J-Francois Magnusson
+roomNumber: 5163
+
+dn: cn=Keslie Prystie, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Keslie Prystie
+sn: Prystie
+description: This is Keslie Prystie's description
+facsimileTelephoneNumber: +1 213 905-2687
+l: Armonk
+ou: Planning
+postalAddress: example$Planning$Dept # 941
+telephoneNumber: +1 408 293-6382
+title: Senior Planning Madonna
+userPassword: eitsyrPeil
+uid: Keslie_Prystie
+givenName: Keslie
+mail: Keslie_Prystie@example.com
+carLicense: ZE6AZMJ
+departmentNumber: 9532
+employeeType: Manager
+homePhone: +1 818 400-2965
+initials: K. P.
+mobile: +1 206 535-5526
+pager: +1 818 235-7279
+manager: cn=Agnese Greenstreet
+secretary: cn=Robbi Kessel
+roomNumber: 3590
+
+dn: cn=Ramakant Wolowidnyk, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ramakant Wolowidnyk
+sn: Wolowidnyk
+description: This is Ramakant Wolowidnyk's description
+facsimileTelephoneNumber: +1 303 605-7657
+l: San Francisco
+ou: Planning
+postalAddress: example$Planning$Dept # 374
+telephoneNumber: +1 206 798-6334
+title: Supreme Planning Writer
+userPassword: kyndiwoloW
+uid: Ramakant_Wolowidnyk
+givenName: Ramakant
+mail: Ramakant_Wolowidnyk@example.com
+carLicense: 3NFHPK7
+departmentNumber: 3756
+employeeType: Normal
+homePhone: +1 408 715-6209
+initials: R. W.
+mobile: +1 510 545-9954
+pager: +1 213 721-2968
+manager: cn=Jelene Kaid
+secretary: cn=Marj Gebhart
+roomNumber: 7771
+
+dn: cn=Suvanee Shireman, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Suvanee Shireman
+sn: Shireman
+description: This is Suvanee Shireman's description
+facsimileTelephoneNumber: +1 303 662-2889
+l: Santa Clara
+ou: Management
+postalAddress: example$Management$Dept # 366
+telephoneNumber: +1 415 884-9426
+title: Senior Management Architect
+userPassword: namerihSee
+uid: Suvanee_Shireman
+givenName: Suvanee
+mail: Suvanee_Shireman@example.com
+carLicense: 6QA5L94
+departmentNumber: 885
+employeeType: Normal
+homePhone: +1 206 120-8373
+initials: S. S.
+mobile: +1 804 668-8228
+pager: +1 510 356-7450
+manager: cn=Gladys Armstrong
+secretary: cn=Tilly Loyd
+roomNumber: 2692
+
+dn: cn=Ardavan Bascombe, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ardavan Bascombe
+sn: Bascombe
+description: This is Ardavan Bascombe's description
+facsimileTelephoneNumber: +1 303 549-9122
+l: Santa Clara
+ou: Product Development
+postalAddress: example$Product Development$Dept # 661
+telephoneNumber: +1 71 654-2263
+title: Elite Product Development Admin
+userPassword: ebmocsaBna
+uid: Ardavan_Bascombe
+givenName: Ardavan
+mail: Ardavan_Bascombe@example.com
+carLicense: 4C8XBAH
+departmentNumber: 914
+employeeType: Manager
+homePhone: +1 804 628-2697
+initials: A. B.
+mobile: +1 408 397-7309
+pager: +1 510 960-7193
+manager: cn=Beulah Dagg
+secretary: cn=Aurea Hobesh
+roomNumber: 7151
+
+dn: cn=Ginger Hedke, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ginger Hedke
+sn: Hedke
+description: This is Ginger Hedke's description
+facsimileTelephoneNumber: +1 415 810-7678
+l: Redwood Shores
+ou: Payroll
+postalAddress: example$Payroll$Dept # 237
+telephoneNumber: +1 206 419-7010
+title: Chief Payroll Assistant
+userPassword: ekdeHregni
+uid: Ginger_Hedke
+givenName: Ginger
+mail: Ginger_Hedke@example.com
+carLicense: 61VAMPJ
+departmentNumber: 4251
+employeeType: Manager
+homePhone: +1 71 378-8773
+initials: G. H.
+mobile: +1 206 555-7152
+pager: +1 510 725-2039
+manager: cn=Alika Gallagher
+secretary: cn=Elsey Syed
+roomNumber: 6007
+
+dn: cn=Kunie Guilfoyle, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kunie Guilfoyle
+sn: Guilfoyle
+description: This is Kunie Guilfoyle's description
+facsimileTelephoneNumber: +1 408 199-6163
+l: Redwood Shores
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 139
+telephoneNumber: +1 804 600-9134
+title: Master Product Testing Admin
+userPassword: elyofliuGe
+uid: Kunie_Guilfoyle
+givenName: Kunie
+mail: Kunie_Guilfoyle@example.com
+carLicense: A26Q6FO
+departmentNumber: 9757
+employeeType: Normal
+homePhone: +1 213 266-1884
+initials: K. G.
+mobile: +1 408 797-8829
+pager: +1 804 823-4894
+manager: cn=Massoud Jeffries
+secretary: cn=Damita Folwell
+roomNumber: 6966
+
+dn: cn=Martino Beauvais, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Martino Beauvais
+sn: Beauvais
+description: This is Martino Beauvais's description
+facsimileTelephoneNumber: +1 303 974-4718
+l: Mountain View
+ou: Payroll
+postalAddress: example$Payroll$Dept # 639
+telephoneNumber: +1 303 579-5047
+title: Supreme Payroll Sales Rep
+userPassword: siavuaeBon
+uid: Martino_Beauvais
+givenName: Martino
+mail: Martino_Beauvais@example.com
+carLicense: 4CNZX95
+departmentNumber: 516
+employeeType: Normal
+homePhone: +1 303 890-6690
+initials: M. B.
+mobile: +1 71 829-8473
+pager: +1 71 991-2362
+manager: cn=Wojciech Aidarous
+secretary: cn=Karlene Akens
+roomNumber: 7462
+
+dn: cn=Dix Krienke, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dix Krienke
+sn: Krienke
+description: This is Dix Krienke's description
+facsimileTelephoneNumber: +1 408 795-1449
+l: Cambridge
+ou: Product Development
+postalAddress: example$Product Development$Dept # 350
+telephoneNumber: +1 206 296-9826
+title: Master Product Development Stooge
+userPassword: ekneirKxiD
+uid: Dix_Krienke
+givenName: Dix
+mail: Dix_Krienke@example.com
+carLicense: 1E61ZTG
+departmentNumber: 3571
+employeeType: Employee
+homePhone: +1 213 496-7662
+initials: D. K.
+mobile: +1 408 172-8287
+pager: +1 804 262-5517
+manager: cn=Koji Kwok
+secretary: cn=Alberta Dilallo
+roomNumber: 5976
+
+dn: cn=Fscocos Quinones, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Fscocos Quinones
+sn: Quinones
+description: This is Fscocos Quinones's description
+facsimileTelephoneNumber: +1 206 994-6478
+l: Cupertino
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 755
+telephoneNumber: +1 818 186-5139
+title: Chief Product Testing Technician
+userPassword: senoniuQso
+uid: Fscocos_Quinones
+givenName: Fscocos
+mail: Fscocos_Quinones@example.com
+carLicense: XF0ZI8W
+departmentNumber: 9130
+employeeType: Manager
+homePhone: +1 804 422-1605
+initials: F. Q.
+mobile: +1 510 664-7654
+pager: +1 303 363-3484
+manager: cn=Nelle Mitchelson
+secretary: cn=Remo Doucette
+roomNumber: 9628
+
+dn: cn=Kylie Kruziak, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kylie Kruziak
+sn: Kruziak
+description: This is Kylie Kruziak's description
+facsimileTelephoneNumber: +1 415 161-9392
+l: Palo Alto
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 24
+telephoneNumber: +1 71 925-8608
+title: Supreme Product Testing President
+userPassword: kaizurKeil
+uid: Kylie_Kruziak
+givenName: Kylie
+mail: Kylie_Kruziak@example.com
+carLicense: U4SHQZ2
+departmentNumber: 9909
+employeeType: Employee
+homePhone: +1 818 320-7405
+initials: K. K.
+mobile: +1 818 310-6474
+pager: +1 818 959-3262
+manager: cn=Arlena Syrett
+secretary: cn=Coretta Chaves
+roomNumber: 9184
+
+dn: cn=Jodie Lauzon, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jodie Lauzon
+sn: Lauzon
+description: This is Jodie Lauzon's description
+facsimileTelephoneNumber: +1 213 553-3445
+l: Palo Alto
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 413
+telephoneNumber: +1 510 556-6671
+title: Supreme Human Resources Admin
+userPassword: nozuaLeido
+uid: Jodie_Lauzon
+givenName: Jodie
+mail: Jodie_Lauzon@example.com
+carLicense: YVG4UDJ
+departmentNumber: 2818
+employeeType: Normal
+homePhone: +1 206 383-7260
+initials: J. L.
+mobile: +1 213 802-7667
+pager: +1 408 908-6859
+manager: cn=Carrissa Traynor
+secretary: cn=Annemie Lugwig
+roomNumber: 9234
+
+dn: cn=Alyda Fouke, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Alyda Fouke
+sn: Fouke
+description: This is Alyda Fouke's description
+facsimileTelephoneNumber: +1 510 280-8208
+l: San Jose
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 14
+telephoneNumber: +1 206 334-6223
+title: Elite Human Resources Punk
+userPassword: ekuoFadylA
+uid: Alyda_Fouke
+givenName: Alyda
+mail: Alyda_Fouke@example.com
+carLicense: RL967Y6
+departmentNumber: 1497
+employeeType: Employee
+homePhone: +1 510 808-6888
+initials: A. F.
+mobile: +1 818 997-1396
+pager: +1 408 931-7848
+manager: cn=Anet Boggan
+secretary: cn=Berton Kinahan
+roomNumber: 8645
+
+dn: cn=Arif Thierry, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Arif Thierry
+sn: Thierry
+description: This is Arif Thierry's description
+facsimileTelephoneNumber: +1 818 513-4366
+l: Mountain View
+ou: Payroll
+postalAddress: example$Payroll$Dept # 47
+telephoneNumber: +1 510 662-1162
+title: Associate Payroll Manager
+userPassword: yrreihTfir
+uid: Arif_Thierry
+givenName: Arif
+mail: Arif_Thierry@example.com
+carLicense: MEAI3TM
+departmentNumber: 287
+employeeType: Temp
+homePhone: +1 415 989-5796
+initials: A. T.
+mobile: +1 71 284-9875
+pager: +1 510 790-6449
+manager: cn=Annabel Stanfield
+secretary: cn=Arts Filion
+roomNumber: 9951
+
+dn: cn=Heath O'Dea, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Heath O'Dea
+sn: O'Dea
+description: This is Heath O'Dea's description
+facsimileTelephoneNumber: +1 510 833-2140
+l: Redwood Shores
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 620
+telephoneNumber: +1 804 673-9849
+title: Elite Product Testing Dictator
+userPassword: aeD'OhtaeH
+uid: Heath_O'Dea
+givenName: Heath
+mail: Heath_O'Dea@example.com
+carLicense: VH3L76Y
+departmentNumber: 487
+employeeType: Employee
+homePhone: +1 71 988-5161
+initials: H. O.
+mobile: +1 804 872-5119
+pager: +1 303 249-5518
+manager: cn=Wileen MacKay
+secretary: cn=Anastasie Operator
+roomNumber: 7428
+
+dn: cn=Ofella Majury, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ofella Majury
+sn: Majury
+description: This is Ofella Majury's description
+facsimileTelephoneNumber: +1 303 349-8252
+l: San Jose
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 432
+telephoneNumber: +1 213 408-5993
+title: Chief Janitorial Visionary
+userPassword: yrujaMalle
+uid: Ofella_Majury
+givenName: Ofella
+mail: Ofella_Majury@example.com
+carLicense: WVM5S3Y
+departmentNumber: 520
+employeeType: Manager
+homePhone: +1 415 351-1419
+initials: O. M.
+mobile: +1 510 684-4488
+pager: +1 408 961-1013
+manager: cn=Maryrose Ilic
+secretary: cn=Elna Horton
+roomNumber: 9652
+
+dn: cn=Farrand Knickerbocker, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Farrand Knickerbocker
+sn: Knickerbocker
+description: This is Farrand Knickerbocker's description
+facsimileTelephoneNumber: +1 818 152-8899
+l: Menlo Park
+ou: Payroll
+postalAddress: example$Payroll$Dept # 228
+telephoneNumber: +1 415 489-9212
+title: Associate Payroll Accountant
+userPassword: rekcobrekc
+uid: Farrand_Knickerbocker
+givenName: Farrand
+mail: Farrand_Knickerbocker@example.com
+carLicense: GTJKTHK
+departmentNumber: 1737
+employeeType: Normal
+homePhone: +1 71 844-5944
+initials: F. K.
+mobile: +1 408 965-2100
+pager: +1 71 537-7105
+manager: cn=Bnrecad Horemans
+secretary: cn=Teri Coord
+roomNumber: 2153
+
+dn: cn=Liam Jesshope, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Liam Jesshope
+sn: Jesshope
+description: This is Liam Jesshope's description
+facsimileTelephoneNumber: +1 510 228-3078
+l: Palo Alto
+ou: Accounting
+postalAddress: example$Accounting$Dept # 520
+telephoneNumber: +1 510 186-8605
+title: Senior Accounting Consultant
+userPassword: epohsseJma
+uid: Liam_Jesshope
+givenName: Liam
+mail: Liam_Jesshope@example.com
+carLicense: PW6YJY7
+departmentNumber: 2447
+employeeType: Contract
+homePhone: +1 818 714-2254
+initials: L. J.
+mobile: +1 804 448-4211
+pager: +1 415 415-3323
+manager: cn=Maryjo Shute
+secretary: cn=Manish Baenziger
+roomNumber: 4774
+
+dn: cn=Adelina Shieff, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Adelina Shieff
+sn: Shieff
+description: This is Adelina Shieff's description
+facsimileTelephoneNumber: +1 408 494-5483
+l: Menlo Park
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 779
+telephoneNumber: +1 804 501-5991
+title: Associate Product Testing Assistant
+userPassword: ffeihSanil
+uid: Adelina_Shieff
+givenName: Adelina
+mail: Adelina_Shieff@example.com
+carLicense: WF07JGM
+departmentNumber: 1371
+employeeType: Normal
+homePhone: +1 408 768-8664
+initials: A. S.
+mobile: +1 818 744-3006
+pager: +1 303 268-9521
+manager: cn=Ravi Rutherford
+secretary: cn=Kusum Bourk
+roomNumber: 9415
+
+dn: cn=Franky Tull, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Franky Tull
+sn: Tull
+description: This is Franky Tull's description
+facsimileTelephoneNumber: +1 303 412-8742
+l: Redwood Shores
+ou: Planning
+postalAddress: example$Planning$Dept # 144
+telephoneNumber: +1 510 767-8721
+title: Master Planning President
+userPassword: lluTyknarF
+uid: Franky_Tull
+givenName: Franky
+mail: Franky_Tull@example.com
+carLicense: WUUSKTK
+departmentNumber: 8225
+employeeType: Temp
+homePhone: +1 804 539-4320
+initials: F. T.
+mobile: +1 804 665-1698
+pager: +1 818 227-1393
+manager: cn=Colene Parkinson
+secretary: cn=Hall Wilde
+roomNumber: 6019
+
+dn: cn=Shiroshi Dalmard, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Shiroshi Dalmard
+sn: Dalmard
+description: This is Shiroshi Dalmard's description
+facsimileTelephoneNumber: +1 804 257-4308
+l: Redwood Shores
+ou: Product Development
+postalAddress: example$Product Development$Dept # 80
+telephoneNumber: +1 415 204-4697
+title: Supreme Product Development Dictator
+userPassword: dramlaDihs
+uid: Shiroshi_Dalmard
+givenName: Shiroshi
+mail: Shiroshi_Dalmard@example.com
+carLicense: 02MM7OW
+departmentNumber: 4858
+employeeType: Contract
+homePhone: +1 303 761-2255
+initials: S. D.
+mobile: +1 408 398-9992
+pager: +1 206 239-1591
+manager: cn=Olympe Flury
+secretary: cn=Ryman Poulsen
+roomNumber: 3037
+
+dn: cn=Tandi Stewart, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tandi Stewart
+sn: Stewart
+description: This is Tandi Stewart's description
+facsimileTelephoneNumber: +1 415 796-9826
+l: San Mateo
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 882
+telephoneNumber: +1 408 569-2234
+title: Associate Human Resources Consultant
+userPassword: trawetSidn
+uid: Tandi_Stewart
+givenName: Tandi
+mail: Tandi_Stewart@example.com
+carLicense: NYPHGM3
+departmentNumber: 762
+employeeType: Temp
+homePhone: +1 510 778-1872
+initials: T. S.
+mobile: +1 303 995-4360
+pager: +1 818 989-3433
+manager: cn=Marjory Wiebe
+secretary: cn=Serene Uhl
+roomNumber: 5940
+
+dn: cn=Stephan Fradette, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Stephan Fradette
+sn: Fradette
+description: This is Stephan Fradette's description
+facsimileTelephoneNumber: +1 818 200-8162
+l: Cambridge
+ou: Administrative
+postalAddress: example$Administrative$Dept # 786
+telephoneNumber: +1 303 741-1783
+title: Senior Administrative Developer
+userPassword: ettedarFna
+uid: Stephan_Fradette
+givenName: Stephan
+mail: Stephan_Fradette@example.com
+carLicense: TFGXTS4
+departmentNumber: 2833
+employeeType: Contract
+homePhone: +1 408 362-4386
+initials: S. F.
+mobile: +1 71 219-7853
+pager: +1 408 162-7901
+manager: cn=Brittni Devera
+secretary: cn=Andras Hoadley
+roomNumber: 6187
+
+dn: cn=Merrielle Nunnally, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Merrielle Nunnally
+sn: Nunnally
+description: This is Merrielle Nunnally's description
+facsimileTelephoneNumber: +1 408 254-3956
+l: Emeryville
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 170
+telephoneNumber: +1 71 971-4713
+title: Master Product Testing Punk
+userPassword: yllannuNel
+uid: Merrielle_Nunnally
+givenName: Merrielle
+mail: Merrielle_Nunnally@example.com
+carLicense: IN6H6K5
+departmentNumber: 237
+employeeType: Manager
+homePhone: +1 818 849-8694
+initials: M. N.
+mobile: +1 415 596-4059
+pager: +1 415 837-8969
+manager: cn=Cecil Datta
+secretary: cn=Lorena Hagan
+roomNumber: 9003
+
+dn: cn=Catriona Saini, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Catriona Saini
+sn: Saini
+description: This is Catriona Saini's description
+facsimileTelephoneNumber: +1 213 727-5463
+l: Mountain View
+ou: Management
+postalAddress: example$Management$Dept # 344
+telephoneNumber: +1 818 369-8258
+title: Elite Management Admin
+userPassword: iniaSanoir
+uid: Catriona_Saini
+givenName: Catriona
+mail: Catriona_Saini@example.com
+carLicense: 0B7DRF0
+departmentNumber: 1850
+employeeType: Contract
+homePhone: +1 804 578-7448
+initials: C. S.
+mobile: +1 510 795-3588
+pager: +1 206 443-2856
+manager: cn=Sileas Glasa
+secretary: cn=Celie Schledwitz
+roomNumber: 8697
+
+dn: cn=Der-Chang Rastelli, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Der-Chang Rastelli
+sn: Rastelli
+description: This is Der-Chang Rastelli's description
+facsimileTelephoneNumber: +1 213 227-8896
+l: Cambridge
+ou: Payroll
+postalAddress: example$Payroll$Dept # 398
+telephoneNumber: +1 415 109-2123
+title: Supreme Payroll Figurehead
+userPassword: illetsaRgn
+uid: Der-Chang_Rastelli
+givenName: Der-Chang
+mail: Der-Chang_Rastelli@example.com
+carLicense: HC5MWZP
+departmentNumber: 3952
+employeeType: Contract
+homePhone: +1 510 941-8108
+initials: D. R.
+mobile: +1 408 450-2834
+pager: +1 818 510-9237
+manager: cn=Adrian Dao
+secretary: cn=Gama Coghlan
+roomNumber: 6998
+
+dn: cn=Tawnya Oswald, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tawnya Oswald
+sn: Oswald
+description: This is Tawnya Oswald's description
+facsimileTelephoneNumber: +1 206 113-6862
+l: Mountain View
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 719
+telephoneNumber: +1 818 275-3879
+title: Supreme Product Testing President
+userPassword: dlawsOaynw
+uid: Tawnya_Oswald
+givenName: Tawnya
+mail: Tawnya_Oswald@example.com
+carLicense: HY5PK6J
+departmentNumber: 9652
+employeeType: Normal
+homePhone: +1 71 112-6723
+initials: T. O.
+mobile: +1 408 587-4180
+pager: +1 71 459-1613
+manager: cn=Chi MacLaren
+secretary: cn=Milan Swact
+roomNumber: 9200
+
+dn: cn=Thad Justus, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Thad Justus
+sn: Justus
+description: This is Thad Justus's description
+facsimileTelephoneNumber: +1 818 965-2820
+l: Mountain View
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 307
+telephoneNumber: +1 804 697-3065
+title: Supreme Janitorial Artist
+userPassword: sutsuJdahT
+uid: Thad_Justus
+givenName: Thad
+mail: Thad_Justus@example.com
+carLicense: 4YTUL59
+departmentNumber: 2679
+employeeType: Contract
+homePhone: +1 303 735-5394
+initials: T. J.
+mobile: +1 818 535-7304
+pager: +1 818 767-2559
+manager: cn=Joeann Moritz
+secretary: cn=Carlee Samalot
+roomNumber: 6109
+
+dn: cn=Angie Yowell, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Angie Yowell
+sn: Yowell
+description: This is Angie Yowell's description
+facsimileTelephoneNumber: +1 408 229-5917
+l: Cupertino
+ou: Accounting
+postalAddress: example$Accounting$Dept # 655
+telephoneNumber: +1 415 494-9452
+title: Chief Accounting Visionary
+userPassword: llewoYeign
+uid: Angie_Yowell
+givenName: Angie
+mail: Angie_Yowell@example.com
+carLicense: UPO11H1
+departmentNumber: 9440
+employeeType: Manager
+homePhone: +1 408 406-9974
+initials: A. Y.
+mobile: +1 804 493-8084
+pager: +1 71 299-2553
+manager: cn=Kimberlyn Guertin
+secretary: cn=Glad Madl
+roomNumber: 7692
+
+dn: cn=Lavina Waespe, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lavina Waespe
+sn: Waespe
+description: This is Lavina Waespe's description
+facsimileTelephoneNumber: +1 804 212-2416
+l: Sunnyvale
+ou: Product Development
+postalAddress: example$Product Development$Dept # 300
+telephoneNumber: +1 213 846-6534
+title: Senior Product Development Punk
+userPassword: epseaWaniv
+uid: Lavina_Waespe
+givenName: Lavina
+mail: Lavina_Waespe@example.com
+carLicense: 7ENZE1Z
+departmentNumber: 8427
+employeeType: Manager
+homePhone: +1 804 483-6826
+initials: L. W.
+mobile: +1 415 464-1188
+pager: +1 818 347-8673
+manager: cn=Donita Peter
+secretary: cn=Anwar Petzold
+roomNumber: 5216
+
+dn: cn=Melisenda Hilberman, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Melisenda Hilberman
+sn: Hilberman
+description: This is Melisenda Hilberman's description
+facsimileTelephoneNumber: +1 213 255-6605
+l: Palo Alto
+ou: Product Development
+postalAddress: example$Product Development$Dept # 983
+telephoneNumber: +1 818 514-2332
+title: Supreme Product Development Architect
+userPassword: namrebliHa
+uid: Melisenda_Hilberman
+givenName: Melisenda
+mail: Melisenda_Hilberman@example.com
+carLicense: IZ6IJ0A
+departmentNumber: 3493
+employeeType: Contract
+homePhone: +1 510 796-9780
+initials: M. H.
+mobile: +1 804 759-3588
+pager: +1 71 367-6646
+manager: cn=Ayn MacHattie
+secretary: cn=Therine Jodoin
+roomNumber: 612
+
+dn: cn=Fancy Cadzow, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Fancy Cadzow
+sn: Cadzow
+description: This is Fancy Cadzow's description
+facsimileTelephoneNumber: +1 71 944-1379
+l: San Francisco
+ou: Payroll
+postalAddress: example$Payroll$Dept # 177
+telephoneNumber: +1 213 344-4751
+title: Master Payroll Fellow
+userPassword: wozdaCycna
+uid: Fancy_Cadzow
+givenName: Fancy
+mail: Fancy_Cadzow@example.com
+carLicense: NOTKMH9
+departmentNumber: 7054
+employeeType: Employee
+homePhone: +1 408 735-8563
+initials: F. C.
+mobile: +1 71 337-4872
+pager: +1 408 426-7911
+manager: cn=Aurea Ruel
+secretary: cn=Rhianon Loghry
+roomNumber: 6845
+
+dn: cn=Teriann Fazel, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Teriann Fazel
+sn: Fazel
+description: This is Teriann Fazel's description
+facsimileTelephoneNumber: +1 818 869-2312
+l: Sunnyvale
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 380
+telephoneNumber: +1 408 636-1109
+title: Senior Janitorial Fellow
+userPassword: lezaFnnair
+uid: Teriann_Fazel
+givenName: Teriann
+mail: Teriann_Fazel@example.com
+carLicense: OVHKQ4X
+departmentNumber: 2267
+employeeType: Manager
+homePhone: +1 818 665-1495
+initials: T. F.
+mobile: +1 206 959-2924
+pager: +1 303 921-6004
+manager: cn=Trixie Miner
+secretary: cn=Millisent Scholman
+roomNumber: 1181
+
+dn: cn=Tyne Zug, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tyne Zug
+sn: Zug
+description: This is Tyne Zug's description
+facsimileTelephoneNumber: +1 71 254-6386
+l: Orem
+ou: Planning
+postalAddress: example$Planning$Dept # 197
+telephoneNumber: +1 818 327-3956
+title: Master Planning Manager
+userPassword: guZenyT
+uid: Tyne_Zug
+givenName: Tyne
+mail: Tyne_Zug@example.com
+carLicense: 390JP4Y
+departmentNumber: 8107
+employeeType: Employee
+homePhone: +1 818 658-8996
+initials: T. Z.
+mobile: +1 818 974-4450
+pager: +1 408 886-2502
+manager: cn=Edeline Jubb
+secretary: cn=Berenice Dornback
+roomNumber: 1390
+
+dn: cn=Ly-Khanh Choi, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ly-Khanh Choi
+sn: Choi
+description: This is Ly-Khanh Choi's description
+facsimileTelephoneNumber: +1 206 125-7982
+l: Menlo Park
+ou: Payroll
+postalAddress: example$Payroll$Dept # 458
+telephoneNumber: +1 71 170-8245
+title: Supreme Payroll Czar
+userPassword: iohChnahK-
+uid: Ly-Khanh_Choi
+givenName: Ly-Khanh
+mail: Ly-Khanh_Choi@example.com
+carLicense: 9R4OQZ9
+departmentNumber: 9481
+employeeType: Employee
+homePhone: +1 818 918-2219
+initials: L. C.
+mobile: +1 415 742-5578
+pager: +1 510 793-6375
+manager: cn=Edlene Tassi
+secretary: cn=Douglas Gelo
+roomNumber: 6434
+
+dn: cn=Kat Mitalas, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kat Mitalas
+sn: Mitalas
+description: This is Kat Mitalas's description
+facsimileTelephoneNumber: +1 408 983-9652
+l: Orem
+ou: Payroll
+postalAddress: example$Payroll$Dept # 112
+telephoneNumber: +1 510 814-5888
+title: Supreme Payroll Stooge
+userPassword: salatiMtaK
+uid: Kat_Mitalas
+givenName: Kat
+mail: Kat_Mitalas@example.com
+carLicense: OQN4VP2
+departmentNumber: 7481
+employeeType: Temp
+homePhone: +1 206 722-9764
+initials: K. M.
+mobile: +1 303 400-7643
+pager: +1 303 750-7022
+manager: cn=Janenna Maginley
+secretary: cn=Rori Bothwell
+roomNumber: 532
+
+dn: cn=Klaas Queries, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Klaas Queries
+sn: Queries
+description: This is Klaas Queries's description
+facsimileTelephoneNumber: +1 818 468-6697
+l: San Jose
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 425
+telephoneNumber: +1 818 728-3329
+title: Master Human Resources Manager
+userPassword: seireuQsaa
+uid: Klaas_Queries
+givenName: Klaas
+mail: Klaas_Queries@example.com
+carLicense: 89PL5N7
+departmentNumber: 5341
+employeeType: Manager
+homePhone: +1 71 566-5992
+initials: K. Q.
+mobile: +1 408 304-7523
+pager: +1 415 924-1691
+manager: cn=Halie Haley
+secretary: cn=Doria Ault
+roomNumber: 2711
+
+dn: cn=Shanta Zoppel, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Shanta Zoppel
+sn: Zoppel
+description: This is Shanta Zoppel's description
+facsimileTelephoneNumber: +1 206 521-9564
+l: Palo Alto
+ou: Administrative
+postalAddress: example$Administrative$Dept # 823
+telephoneNumber: +1 303 691-2138
+title: Junior Administrative Punk
+userPassword: leppoZatna
+uid: Shanta_Zoppel
+givenName: Shanta
+mail: Shanta_Zoppel@example.com
+carLicense: RGDOHL0
+departmentNumber: 9602
+employeeType: Employee
+homePhone: +1 818 305-5351
+initials: S. Z.
+mobile: +1 213 530-8707
+pager: +1 206 979-9001
+manager: cn=Arline Brockmeyer
+secretary: cn=Didi Schartmann
+roomNumber: 5886
+
+dn: cn=Demeter Sezer, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Demeter Sezer
+sn: Sezer
+description: This is Demeter Sezer's description
+facsimileTelephoneNumber: +1 415 208-3431
+l: Santa Clara
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 263
+telephoneNumber: +1 71 241-1051
+title: Associate Janitorial President
+userPassword: rezeSretem
+uid: Demeter_Sezer
+givenName: Demeter
+mail: Demeter_Sezer@example.com
+carLicense: RB2GPVI
+departmentNumber: 7914
+employeeType: Manager
+homePhone: +1 206 240-8869
+initials: D. S.
+mobile: +1 71 918-7015
+pager: +1 71 577-4497
+manager: cn=Diju Wong
+secretary: cn=Caria Pickles
+roomNumber: 7244
+
+dn: cn=Glynda Wolowidnyk, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Glynda Wolowidnyk
+sn: Wolowidnyk
+description: This is Glynda Wolowidnyk's description
+facsimileTelephoneNumber: +1 206 652-7450
+l: Redmond
+ou: Peons
+postalAddress: example$Peons$Dept # 355
+telephoneNumber: +1 415 196-3248
+title: Elite Peons Figurehead
+userPassword: kyndiwoloW
+uid: Glynda_Wolowidnyk
+givenName: Glynda
+mail: Glynda_Wolowidnyk@example.com
+carLicense: 7A92E5S
+departmentNumber: 8752
+employeeType: Contract
+homePhone: +1 408 684-5926
+initials: G. W.
+mobile: +1 804 715-4376
+pager: +1 303 624-5785
+manager: cn=Bora Werth
+secretary: cn=Nurettin Heroux
+roomNumber: 2685
+
+dn: cn=Joice Viau, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Joice Viau
+sn: Viau
+description: This is Joice Viau's description
+facsimileTelephoneNumber: +1 804 225-7580
+l: Fremont
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 686
+telephoneNumber: +1 818 337-5421
+title: Master Janitorial Director
+userPassword: uaiVecioJ
+uid: Joice_Viau
+givenName: Joice
+mail: Joice_Viau@example.com
+carLicense: UGKBZPR
+departmentNumber: 5241
+employeeType: Manager
+homePhone: +1 408 801-1068
+initials: J. V.
+mobile: +1 303 511-3988
+pager: +1 818 333-1392
+manager: cn=Carley Couron
+secretary: cn=Ginette Covey
+roomNumber: 8522
+
+dn: cn=Zonda Birkett, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Zonda Birkett
+sn: Birkett
+description: This is Zonda Birkett's description
+facsimileTelephoneNumber: +1 415 549-6310
+l: Milpitas
+ou: Management
+postalAddress: example$Management$Dept # 977
+telephoneNumber: +1 804 872-7461
+title: Supreme Management Visionary
+userPassword: ttekriBadn
+uid: Zonda_Birkett
+givenName: Zonda
+mail: Zonda_Birkett@example.com
+carLicense: UYID95B
+departmentNumber: 7456
+employeeType: Employee
+homePhone: +1 71 114-2576
+initials: Z. B.
+mobile: +1 818 559-6329
+pager: +1 206 722-1833
+manager: cn=Sheryl Gaylord
+secretary: cn=Pia Heckman
+roomNumber: 4339
+
+dn: cn=Marybelle Passin, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marybelle Passin
+sn: Passin
+description: This is Marybelle Passin's description
+facsimileTelephoneNumber: +1 408 723-9290
+l: San Jose
+ou: Management
+postalAddress: example$Management$Dept # 577
+telephoneNumber: +1 206 411-3396
+title: Junior Management Dictator
+userPassword: nissaPelle
+uid: Marybelle_Passin
+givenName: Marybelle
+mail: Marybelle_Passin@example.com
+carLicense: CRW20D4
+departmentNumber: 7051
+employeeType: Manager
+homePhone: +1 71 853-1337
+initials: M. P.
+mobile: +1 804 919-4814
+pager: +1 213 853-5051
+manager: cn=Manon Cheeseman
+secretary: cn=Previn Zalite
+roomNumber: 6139
+
+dn: cn=Sharai Pezzoli, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sharai Pezzoli
+sn: Pezzoli
+description: This is Sharai Pezzoli's description
+facsimileTelephoneNumber: +1 415 830-1006
+l: Redwood Shores
+ou: Administrative
+postalAddress: example$Administrative$Dept # 189
+telephoneNumber: +1 408 590-5082
+title: Supreme Administrative Admin
+userPassword: ilozzePiar
+uid: Sharai_Pezzoli
+givenName: Sharai
+mail: Sharai_Pezzoli@example.com
+carLicense: 0PWVA7A
+departmentNumber: 7431
+employeeType: Employee
+homePhone: +1 804 420-7444
+initials: S. P.
+mobile: +1 213 339-2074
+pager: +1 510 162-6549
+manager: cn=Candice Vlahos
+secretary: cn=Sucha MacNeill
+roomNumber: 901
+
+dn: cn=Carly Smuda, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Carly Smuda
+sn: Smuda
+description: This is Carly Smuda's description
+facsimileTelephoneNumber: +1 71 366-3162
+l: Orem
+ou: Peons
+postalAddress: example$Peons$Dept # 366
+telephoneNumber: +1 213 495-5466
+title: Master Peons Architect
+userPassword: adumSylraC
+uid: Carly_Smuda
+givenName: Carly
+mail: Carly_Smuda@example.com
+carLicense: E4V6O6O
+departmentNumber: 3482
+employeeType: Normal
+homePhone: +1 804 616-3406
+initials: C. S.
+mobile: +1 415 479-8184
+pager: +1 510 594-2190
+manager: cn=Stefa Zaharychuk
+secretary: cn=Edwin Surazski
+roomNumber: 5581
+
+dn: cn=Jinny Stds, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jinny Stds
+sn: Stds
+description: This is Jinny Stds's description
+facsimileTelephoneNumber: +1 415 299-2965
+l: Redmond
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 984
+telephoneNumber: +1 71 208-9503
+title: Master Janitorial Writer
+userPassword: sdtSynniJ
+uid: Jinny_Stds
+givenName: Jinny
+mail: Jinny_Stds@example.com
+carLicense: Z24L06X
+departmentNumber: 9136
+employeeType: Normal
+homePhone: +1 408 889-9149
+initials: J. S.
+mobile: +1 818 130-1783
+pager: +1 415 320-2448
+manager: cn=Alka Liew
+secretary: cn=Lonnie Zoellner
+roomNumber: 1475
+
+dn: cn=Ellis Barsky, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ellis Barsky
+sn: Barsky
+description: This is Ellis Barsky's description
+facsimileTelephoneNumber: +1 213 614-2840
+l: San Mateo
+ou: Peons
+postalAddress: example$Peons$Dept # 503
+telephoneNumber: +1 804 887-2089
+title: Associate Peons Admin
+userPassword: yksraBsill
+uid: Ellis_Barsky
+givenName: Ellis
+mail: Ellis_Barsky@example.com
+carLicense: Q7SP1UW
+departmentNumber: 7909
+employeeType: Normal
+homePhone: +1 510 275-1317
+initials: E. B.
+mobile: +1 408 545-3968
+pager: +1 408 544-2462
+manager: cn=Cleto Ntelpac
+secretary: cn=Fifi Bostelmann
+roomNumber: 8633
+
+dn: cn=Rosette Elkington, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rosette Elkington
+sn: Elkington
+description: This is Rosette Elkington's description
+facsimileTelephoneNumber: +1 71 643-5767
+l: Redmond
+ou: Payroll
+postalAddress: example$Payroll$Dept # 76
+telephoneNumber: +1 206 974-9881
+title: Senior Payroll Dictator
+userPassword: notgniklEe
+uid: Rosette_Elkington
+givenName: Rosette
+mail: Rosette_Elkington@example.com
+carLicense: 0ISB4RP
+departmentNumber: 3157
+employeeType: Contract
+homePhone: +1 408 364-9270
+initials: R. E.
+mobile: +1 408 750-2001
+pager: +1 818 566-5697
+manager: cn=Chander Frodsham
+secretary: cn=Rowe Fiegel
+roomNumber: 6207
+
+dn: cn=Corene Rankin, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Corene Rankin
+sn: Rankin
+description: This is Corene Rankin's description
+facsimileTelephoneNumber: +1 303 988-9737
+l: San Francisco
+ou: Accounting
+postalAddress: example$Accounting$Dept # 873
+telephoneNumber: +1 818 726-7769
+title: Master Accounting Developer
+userPassword: niknaRener
+uid: Corene_Rankin
+givenName: Corene
+mail: Corene_Rankin@example.com
+carLicense: FA5KWY0
+departmentNumber: 474
+employeeType: Manager
+homePhone: +1 818 605-9872
+initials: C. R.
+mobile: +1 71 482-1976
+pager: +1 206 520-5056
+manager: cn=Cristine Widows
+secretary: cn=Parker Calistro
+roomNumber: 9503
+
+dn: cn=Marget Timm, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marget Timm
+sn: Timm
+description: This is Marget Timm's description
+facsimileTelephoneNumber: +1 213 616-9877
+l: Menlo Park
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 535
+telephoneNumber: +1 415 864-3126
+title: Junior Janitorial Writer
+userPassword: mmiTtegraM
+uid: Marget_Timm
+givenName: Marget
+mail: Marget_Timm@example.com
+carLicense: 74V4UTL
+departmentNumber: 4455
+employeeType: Normal
+homePhone: +1 206 510-5409
+initials: M. T.
+mobile: +1 408 657-8524
+pager: +1 415 884-3865
+manager: cn=Ros Poff
+secretary: cn=Kaycee Schyving
+roomNumber: 6458
+
+dn: cn=Philip Ruyant, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Philip Ruyant
+sn: Ruyant
+description: This is Philip Ruyant's description
+facsimileTelephoneNumber: +1 818 271-7057
+l: Milpitas
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 435
+telephoneNumber: +1 415 756-5089
+title: Elite Human Resources Pinhead
+userPassword: tnayuRpili
+uid: Philip_Ruyant
+givenName: Philip
+mail: Philip_Ruyant@example.com
+carLicense: RTAKY71
+departmentNumber: 7012
+employeeType: Normal
+homePhone: +1 206 258-2838
+initials: P. R.
+mobile: +1 510 228-2479
+pager: +1 415 614-6100
+manager: cn=Merrill Naugle
+secretary: cn=Trista Naro
+roomNumber: 4167
+
+dn: cn=Magdalena Noel, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Magdalena Noel
+sn: Noel
+description: This is Magdalena Noel's description
+facsimileTelephoneNumber: +1 71 932-1171
+l: Milpitas
+ou: Peons
+postalAddress: example$Peons$Dept # 406
+telephoneNumber: +1 206 159-1131
+title: Master Peons Visionary
+userPassword: leoNanelad
+uid: Magdalena_Noel
+givenName: Magdalena
+mail: Magdalena_Noel@example.com
+carLicense: 4HKGRVG
+departmentNumber: 2354
+employeeType: Normal
+homePhone: +1 804 810-2355
+initials: M. N.
+mobile: +1 804 783-5560
+pager: +1 510 772-5833
+manager: cn=Barb Heppes
+secretary: cn=Lucy Crabtree
+roomNumber: 781
+
+dn: cn=Mady Hogue, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Mady Hogue
+sn: Hogue
+description: This is Mady Hogue's description
+facsimileTelephoneNumber: +1 206 529-6732
+l: Cupertino
+ou: Administrative
+postalAddress: example$Administrative$Dept # 525
+telephoneNumber: +1 213 111-4880
+title: Master Administrative Writer
+userPassword: eugoHydaM
+uid: Mady_Hogue
+givenName: Mady
+mail: Mady_Hogue@example.com
+carLicense: BD2HXMN
+departmentNumber: 3545
+employeeType: Contract
+homePhone: +1 213 517-3203
+initials: M. H.
+mobile: +1 804 926-2015
+pager: +1 510 204-4242
+manager: cn=Lotta Geldrez
+secretary: cn=Nanni Donovan
+roomNumber: 9575
+
+dn: cn=Ivie Malee, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ivie Malee
+sn: Malee
+description: This is Ivie Malee's description
+facsimileTelephoneNumber: +1 303 588-8656
+l: San Francisco
+ou: Product Development
+postalAddress: example$Product Development$Dept # 241
+telephoneNumber: +1 408 459-3942
+title: Junior Product Development Fellow
+userPassword: eelaMeivI
+uid: Ivie_Malee
+givenName: Ivie
+mail: Ivie_Malee@example.com
+carLicense: BXL4TZB
+departmentNumber: 9117
+employeeType: Contract
+homePhone: +1 71 141-8943
+initials: I. M.
+mobile: +1 510 827-1920
+pager: +1 303 800-1158
+manager: cn=Turus Emami
+secretary: cn=Japan Layne
+roomNumber: 8232
+
+dn: cn=Coursdev Smits, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Coursdev Smits
+sn: Smits
+description: This is Coursdev Smits's description
+facsimileTelephoneNumber: +1 510 146-3293
+l: Fremont
+ou: Administrative
+postalAddress: example$Administrative$Dept # 902
+telephoneNumber: +1 206 607-6996
+title: Supreme Administrative Janitor
+userPassword: stimSvedsr
+uid: Coursdev_Smits
+givenName: Coursdev
+mail: Coursdev_Smits@example.com
+carLicense: OLALO2R
+departmentNumber: 8026
+employeeType: Normal
+homePhone: +1 415 578-1146
+initials: C. S.
+mobile: +1 206 773-1314
+pager: +1 510 963-2309
+manager: cn=Renny Naismith
+secretary: cn=Therese Hazlett
+roomNumber: 3927
+
+dn: cn=Gelais Dixon, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gelais Dixon
+sn: Dixon
+description: This is Gelais Dixon's description
+facsimileTelephoneNumber: +1 206 717-6773
+l: Santa Clara
+ou: Product Development
+postalAddress: example$Product Development$Dept # 942
+telephoneNumber: +1 206 128-1695
+title: Elite Product Development Pinhead
+userPassword: noxiDsiale
+uid: Gelais_Dixon
+givenName: Gelais
+mail: Gelais_Dixon@example.com
+carLicense: JZ7407S
+departmentNumber: 1534
+employeeType: Manager
+homePhone: +1 408 860-8272
+initials: G. D.
+mobile: +1 804 127-4751
+pager: +1 818 526-9668
+manager: cn=Cecco Plsntp
+secretary: cn=Dhanvinder Averette
+roomNumber: 8012
+
+dn: cn=Action Laberge, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Action Laberge
+sn: Laberge
+description: This is Action Laberge's description
+facsimileTelephoneNumber: +1 804 931-9330
+l: Emeryville
+ou: Product Development
+postalAddress: example$Product Development$Dept # 773
+telephoneNumber: +1 510 920-2422
+title: Junior Product Development Consultant
+userPassword: egrebaLnoi
+uid: Action_Laberge
+givenName: Action
+mail: Action_Laberge@example.com
+carLicense: 1A6ICKQ
+departmentNumber: 7424
+employeeType: Normal
+homePhone: +1 408 694-1770
+initials: A. L.
+mobile: +1 408 329-4881
+pager: +1 408 147-1193
+manager: cn=Fastmer Cusson
+secretary: cn=Salim Ruth
+roomNumber: 3048
+
+dn: cn=Sarita Fouke, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sarita Fouke
+sn: Fouke
+description: This is Sarita Fouke's description
+facsimileTelephoneNumber: +1 415 540-5752
+l: Redwood Shores
+ou: Planning
+postalAddress: example$Planning$Dept # 752
+telephoneNumber: +1 408 989-8492
+title: Elite Planning Mascot
+userPassword: ekuoFatira
+uid: Sarita_Fouke
+givenName: Sarita
+mail: Sarita_Fouke@example.com
+carLicense: 9LJNKZI
+departmentNumber: 7065
+employeeType: Employee
+homePhone: +1 818 978-8910
+initials: S. F.
+mobile: +1 818 295-5678
+pager: +1 415 723-3513
+manager: cn=Deniece Whetzel
+secretary: cn=Florella Sharma
+roomNumber: 4270
+
+dn: cn=Belle Moxley, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Belle Moxley
+sn: Moxley
+description: This is Belle Moxley's description
+facsimileTelephoneNumber: +1 818 574-4599
+l: Menlo Park
+ou: Accounting
+postalAddress: example$Accounting$Dept # 351
+telephoneNumber: +1 213 498-9024
+title: Supreme Accounting Fellow
+userPassword: yelxoMelle
+uid: Belle_Moxley
+givenName: Belle
+mail: Belle_Moxley@example.com
+carLicense: YLW046G
+departmentNumber: 8213
+employeeType: Temp
+homePhone: +1 213 615-6463
+initials: B. M.
+mobile: +1 303 800-1965
+pager: +1 510 400-4379
+manager: cn=Marion Querengesser
+secretary: cn=Pawel McMinn
+roomNumber: 4667
+
+dn: cn=MaryLou Kenlan, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: MaryLou Kenlan
+sn: Kenlan
+description: This is MaryLou Kenlan's description
+facsimileTelephoneNumber: +1 206 599-1562
+l: Fremont
+ou: Product Development
+postalAddress: example$Product Development$Dept # 569
+telephoneNumber: +1 303 508-1876
+title: Supreme Product Development Admin
+userPassword: nalneKuoLy
+uid: MaryLou_Kenlan
+givenName: MaryLou
+mail: MaryLou_Kenlan@example.com
+carLicense: N97KWQO
+departmentNumber: 2369
+employeeType: Manager
+homePhone: +1 510 671-7364
+initials: M. K.
+mobile: +1 818 866-6445
+pager: +1 415 195-3197
+manager: cn=Chandal Ibsen
+secretary: cn=Go McClure
+roomNumber: 5806
+
+dn: cn=Nettie Holthaus, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Nettie Holthaus
+sn: Holthaus
+description: This is Nettie Holthaus's description
+facsimileTelephoneNumber: +1 510 259-5175
+l: Fremont
+ou: Product Development
+postalAddress: example$Product Development$Dept # 167
+telephoneNumber: +1 804 256-5440
+title: Elite Product Development Writer
+userPassword: suahtloHei
+uid: Nettie_Holthaus
+givenName: Nettie
+mail: Nettie_Holthaus@example.com
+carLicense: NIWXD5L
+departmentNumber: 652
+employeeType: Employee
+homePhone: +1 71 941-2382
+initials: N. H.
+mobile: +1 415 582-2954
+pager: +1 71 405-2199
+manager: cn=Shellie McClure
+secretary: cn=Therese Kenlan
+roomNumber: 5899
+
+dn: cn=Linea Stansby, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Linea Stansby
+sn: Stansby
+description: This is Linea Stansby's description
+facsimileTelephoneNumber: +1 510 983-8963
+l: San Jose
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 773
+telephoneNumber: +1 415 258-7985
+title: Supreme Janitorial Warrior
+userPassword: ybsnatSaen
+uid: Linea_Stansby
+givenName: Linea
+mail: Linea_Stansby@example.com
+carLicense: DHX8UYK
+departmentNumber: 1335
+employeeType: Temp
+homePhone: +1 408 284-6360
+initials: L. S.
+mobile: +1 804 513-7755
+pager: +1 303 949-6182
+manager: cn=Goldy Osman
+secretary: cn=Ronica Brushey
+roomNumber: 1184
+
+dn: cn=Carlyn Schissel, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Carlyn Schissel
+sn: Schissel
+description: This is Carlyn Schissel's description
+facsimileTelephoneNumber: +1 818 362-5920
+l: San Jose
+ou: Administrative
+postalAddress: example$Administrative$Dept # 851
+telephoneNumber: +1 303 993-9907
+title: Associate Administrative Yahoo
+userPassword: lessihcSny
+uid: Carlyn_Schissel
+givenName: Carlyn
+mail: Carlyn_Schissel@example.com
+carLicense: NWK3GBB
+departmentNumber: 659
+employeeType: Temp
+homePhone: +1 213 480-6322
+initials: C. S.
+mobile: +1 206 604-3711
+pager: +1 510 675-3434
+manager: cn=Nerti Van Holst
+secretary: cn=Enriqueta Arvin
+roomNumber: 3203
+
+dn: cn=Marv Sponchia, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marv Sponchia
+sn: Sponchia
+description: This is Marv Sponchia's description
+facsimileTelephoneNumber: +1 213 439-6793
+l: Redmond
+ou: Management
+postalAddress: example$Management$Dept # 423
+telephoneNumber: +1 408 572-2798
+title: Elite Management Manager
+userPassword: aihcnopSvr
+uid: Marv_Sponchia
+givenName: Marv
+mail: Marv_Sponchia@example.com
+carLicense: G32423U
+departmentNumber: 5738
+employeeType: Normal
+homePhone: +1 408 219-4278
+initials: M. S.
+mobile: +1 71 646-9163
+pager: +1 510 644-3275
+manager: cn=Sotos Krull
+secretary: cn=Tally Petillion
+roomNumber: 9108
+
+dn: cn=Saman Kosarski, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Saman Kosarski
+sn: Kosarski
+description: This is Saman Kosarski's description
+facsimileTelephoneNumber: +1 206 731-9107
+l: Menlo Park
+ou: Administrative
+postalAddress: example$Administrative$Dept # 780
+telephoneNumber: +1 408 630-6494
+title: Elite Administrative Consultant
+userPassword: iksrasoKna
+uid: Saman_Kosarski
+givenName: Saman
+mail: Saman_Kosarski@example.com
+carLicense: J5160RX
+departmentNumber: 6354
+employeeType: Normal
+homePhone: +1 71 952-2587
+initials: S. K.
+mobile: +1 510 129-8844
+pager: +1 510 536-8503
+manager: cn=Bertina Marengere
+secretary: cn=Jeremy Sparksman
+roomNumber: 2245
+
+dn: cn=Guglielma Elwood, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Guglielma Elwood
+sn: Elwood
+description: This is Guglielma Elwood's description
+facsimileTelephoneNumber: +1 408 841-1943
+l: Milpitas
+ou: Product Development
+postalAddress: example$Product Development$Dept # 832
+telephoneNumber: +1 804 899-6944
+title: Chief Product Development Accountant
+userPassword: doowlEamle
+uid: Guglielma_Elwood
+givenName: Guglielma
+mail: Guglielma_Elwood@example.com
+carLicense: 3CQSO3V
+departmentNumber: 8084
+employeeType: Contract
+homePhone: +1 818 412-6174
+initials: G. E.
+mobile: +1 213 540-9204
+pager: +1 303 594-4194
+manager: cn=Myriam McNeal
+secretary: cn=Emma Brummitt
+roomNumber: 632
+
+dn: cn=Viviana Ozyetis, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Viviana Ozyetis
+sn: Ozyetis
+description: This is Viviana Ozyetis's description
+facsimileTelephoneNumber: +1 510 337-8866
+l: Cambridge
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 164
+telephoneNumber: +1 408 170-5723
+title: Associate Product Testing Architect
+userPassword: siteyzOana
+uid: Viviana_Ozyetis
+givenName: Viviana
+mail: Viviana_Ozyetis@example.com
+carLicense: 2DNLFJR
+departmentNumber: 8190
+employeeType: Employee
+homePhone: +1 415 197-6709
+initials: V. O.
+mobile: +1 408 950-8170
+pager: +1 408 719-6699
+manager: cn=Merb Streight
+secretary: cn=Metrics Wagle
+roomNumber: 1292
+
+dn: cn=Dominique Slaa, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dominique Slaa
+sn: Slaa
+description: This is Dominique Slaa's description
+facsimileTelephoneNumber: +1 818 774-9206
+l: Redwood Shores
+ou: Peons
+postalAddress: example$Peons$Dept # 2
+telephoneNumber: +1 206 412-7150
+title: Supreme Peons Punk
+userPassword: aalSeuqini
+uid: Dominique_Slaa
+givenName: Dominique
+mail: Dominique_Slaa@example.com
+carLicense: XTSKRPQ
+departmentNumber: 6917
+employeeType: Temp
+homePhone: +1 71 850-2200
+initials: D. S.
+mobile: +1 303 265-8608
+pager: +1 415 693-6163
+manager: cn=Dorothy Revis
+secretary: cn=Foad Jowett
+roomNumber: 2546
+
+dn: cn=Amalea Strandberg, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Amalea Strandberg
+sn: Strandberg
+description: This is Amalea Strandberg's description
+facsimileTelephoneNumber: +1 818 317-8408
+l: Redwood Shores
+ou: Administrative
+postalAddress: example$Administrative$Dept # 307
+telephoneNumber: +1 206 614-2202
+title: Senior Administrative Director
+userPassword: grebdnartS
+uid: Amalea_Strandberg
+givenName: Amalea
+mail: Amalea_Strandberg@example.com
+carLicense: 2Y3OUF9
+departmentNumber: 791
+employeeType: Employee
+homePhone: +1 303 580-5453
+initials: A. S.
+mobile: +1 71 225-3720
+pager: +1 408 628-3095
+manager: cn=Yves Rishy-Maharaj
+secretary: cn=Jewelle McGonigal
+roomNumber: 9591
+
+dn: cn=Ranea Crooks, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ranea Crooks
+sn: Crooks
+description: This is Ranea Crooks's description
+facsimileTelephoneNumber: +1 71 561-5330
+l: Cupertino
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 144
+telephoneNumber: +1 303 494-2601
+title: Chief Janitorial Manager
+userPassword: skoorCaena
+uid: Ranea_Crooks
+givenName: Ranea
+mail: Ranea_Crooks@example.com
+carLicense: BBV8RGI
+departmentNumber: 3493
+employeeType: Employee
+homePhone: +1 804 854-8696
+initials: R. C.
+mobile: +1 303 515-9934
+pager: +1 206 348-8147
+manager: cn=Jewelle Matatall
+secretary: cn=Jeralee Orfano
+roomNumber: 8942
+
+dn: cn=Glynnis Sobkow, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Glynnis Sobkow
+sn: Sobkow
+description: This is Glynnis Sobkow's description
+facsimileTelephoneNumber: +1 408 453-8293
+l: Palo Alto
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 181
+telephoneNumber: +1 408 620-5102
+title: Senior Product Testing Visionary
+userPassword: wokboSsinn
+uid: Glynnis_Sobkow
+givenName: Glynnis
+mail: Glynnis_Sobkow@example.com
+carLicense: ZBQ0O3V
+departmentNumber: 9925
+employeeType: Normal
+homePhone: +1 818 410-7723
+initials: G. S.
+mobile: +1 206 146-7249
+pager: +1 408 948-3112
+manager: cn=Drucill Karibian
+secretary: cn=Demi Suprick
+roomNumber: 5556
+
+dn: cn=James Peng, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: James Peng
+sn: Peng
+description: This is James Peng's description
+facsimileTelephoneNumber: +1 818 710-8953
+l: Orem
+ou: Product Development
+postalAddress: example$Product Development$Dept # 366
+telephoneNumber: +1 415 589-9938
+title: Associate Product Development Artist
+userPassword: gnePsemaJ
+uid: James_Peng
+givenName: James
+mail: James_Peng@example.com
+carLicense: 0GDFBAM
+departmentNumber: 9113
+employeeType: Employee
+homePhone: +1 213 146-3663
+initials: J. P.
+mobile: +1 804 348-4649
+pager: +1 510 893-2162
+manager: cn=Leni Petrie
+secretary: cn=Donny Tonelli
+roomNumber: 9158
+
+dn: cn=Jean-Roch Absi, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jean-Roch Absi
+sn: Absi
+description: This is Jean-Roch Absi's description
+facsimileTelephoneNumber: +1 510 378-7514
+l: Santa Clara
+ou: Product Development
+postalAddress: example$Product Development$Dept # 567
+telephoneNumber: +1 818 995-6472
+title: Master Product Development Warrior
+userPassword: isbAhcoR-n
+uid: Jean-Roch_Absi
+givenName: Jean-Roch
+mail: Jean-Roch_Absi@example.com
+carLicense: 6M8IH9T
+departmentNumber: 8148
+employeeType: Normal
+homePhone: +1 510 484-4295
+initials: J. A.
+mobile: +1 206 404-8391
+pager: +1 408 588-5896
+manager: cn=Yao Fanus
+secretary: cn=Yoshi Clites
+roomNumber: 2118
+
+dn: cn=Humberto Marco, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Humberto Marco
+sn: Marco
+description: This is Humberto Marco's description
+facsimileTelephoneNumber: +1 303 868-2565
+l: Armonk
+ou: Planning
+postalAddress: example$Planning$Dept # 402
+telephoneNumber: +1 804 569-2380
+title: Supreme Planning Visionary
+userPassword: ocraMotreb
+uid: Humberto_Marco
+givenName: Humberto
+mail: Humberto_Marco@example.com
+carLicense: 28ZPBER
+departmentNumber: 8232
+employeeType: Temp
+homePhone: +1 415 527-8669
+initials: H. M.
+mobile: +1 71 729-9850
+pager: +1 206 229-8942
+manager: cn=Gray Koelbl
+secretary: cn=Rafiq Lazure
+roomNumber: 3919
+
+dn: cn=Samual Widuch, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Samual Widuch
+sn: Widuch
+description: This is Samual Widuch's description
+facsimileTelephoneNumber: +1 206 206-3404
+l: San Jose
+ou: Product Development
+postalAddress: example$Product Development$Dept # 163
+telephoneNumber: +1 415 429-7771
+title: Chief Product Development Fellow
+userPassword: hcudiWlaum
+uid: Samual_Widuch
+givenName: Samual
+mail: Samual_Widuch@example.com
+carLicense: 9TK4361
+departmentNumber: 6412
+employeeType: Contract
+homePhone: +1 206 406-7980
+initials: S. W.
+mobile: +1 415 378-7065
+pager: +1 415 199-6764
+manager: cn=Gupta Kinch
+secretary: cn=Elsey Perreault
+roomNumber: 2163
+
+dn: cn=Devon Hamner, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Devon Hamner
+sn: Hamner
+description: This is Devon Hamner's description
+facsimileTelephoneNumber: +1 408 827-4091
+l: Milpitas
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 988
+telephoneNumber: +1 213 899-1290
+title: Elite Product Testing Developer
+userPassword: renmaHnove
+uid: Devon_Hamner
+givenName: Devon
+mail: Devon_Hamner@example.com
+carLicense: SIOACF2
+departmentNumber: 7530
+employeeType: Temp
+homePhone: +1 71 772-7250
+initials: D. H.
+mobile: +1 804 923-4986
+pager: +1 303 312-9904
+manager: cn=Stormy Stocks
+secretary: cn=Lawrence Lacasse
+roomNumber: 6125
+
+dn: cn=Wassim Seiple, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Wassim Seiple
+sn: Seiple
+description: This is Wassim Seiple's description
+facsimileTelephoneNumber: +1 804 180-7682
+l: Palo Alto
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 612
+telephoneNumber: +1 510 186-6122
+title: Senior Janitorial Punk
+userPassword: elpieSmiss
+uid: Wassim_Seiple
+givenName: Wassim
+mail: Wassim_Seiple@example.com
+carLicense: ZR6625O
+departmentNumber: 4996
+employeeType: Manager
+homePhone: +1 303 544-3306
+initials: W. S.
+mobile: +1 71 220-3481
+pager: +1 408 697-3557
+manager: cn=Radomir Beshir
+secretary: cn=Franky Sunatori
+roomNumber: 9169
+
+dn: cn=Corabelle Willette, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Corabelle Willette
+sn: Willette
+description: This is Corabelle Willette's description
+facsimileTelephoneNumber: +1 415 935-8013
+l: Armonk
+ou: Management
+postalAddress: example$Management$Dept # 853
+telephoneNumber: +1 213 698-8314
+title: Associate Management Vice President
+userPassword: ettelliWel
+uid: Corabelle_Willette
+givenName: Corabelle
+mail: Corabelle_Willette@example.com
+carLicense: 3ROV6N5
+departmentNumber: 9164
+employeeType: Temp
+homePhone: +1 818 126-4550
+initials: C. W.
+mobile: +1 206 873-9350
+pager: +1 303 411-4796
+manager: cn=Lou Basinger
+secretary: cn=Chrissy Banks
+roomNumber: 7251
+
+dn: cn=Yuko Leggett, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Yuko Leggett
+sn: Leggett
+description: This is Yuko Leggett's description
+facsimileTelephoneNumber: +1 804 885-1583
+l: San Jose
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 782
+telephoneNumber: +1 71 174-6937
+title: Junior Product Testing Writer
+userPassword: tteggeLoku
+uid: Yuko_Leggett
+givenName: Yuko
+mail: Yuko_Leggett@example.com
+carLicense: SRJ5384
+departmentNumber: 4270
+employeeType: Contract
+homePhone: +1 206 531-1475
+initials: Y. L.
+mobile: +1 408 580-9956
+pager: +1 303 653-9114
+manager: cn=Nanon Stiles
+secretary: cn=Patch Muselik
+roomNumber: 934
+
+dn: cn=Marya Eakes, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marya Eakes
+sn: Eakes
+description: This is Marya Eakes's description
+facsimileTelephoneNumber: +1 510 277-6974
+l: Sunnyvale
+ou: Product Development
+postalAddress: example$Product Development$Dept # 385
+telephoneNumber: +1 71 259-6949
+title: Junior Product Development Sales Rep
+userPassword: sekaEayraM
+uid: Marya_Eakes
+givenName: Marya
+mail: Marya_Eakes@example.com
+carLicense: D0GLPT0
+departmentNumber: 1140
+employeeType: Normal
+homePhone: +1 804 428-1056
+initials: M. E.
+mobile: +1 818 140-7212
+pager: +1 303 491-7423
+manager: cn=Henry Anolik
+secretary: cn=Atique Honbarrier
+roomNumber: 3463
+
+dn: cn=Pete Shames, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Pete Shames
+sn: Shames
+description: This is Pete Shames's description
+facsimileTelephoneNumber: +1 213 876-4561
+l: Armonk
+ou: Accounting
+postalAddress: example$Accounting$Dept # 175
+telephoneNumber: +1 71 791-7119
+title: Elite Accounting Evangelist
+userPassword: semahSeteP
+uid: Pete_Shames
+givenName: Pete
+mail: Pete_Shames@example.com
+carLicense: S3AOJS2
+departmentNumber: 2852
+employeeType: Employee
+homePhone: +1 510 160-8575
+initials: P. S.
+mobile: +1 804 489-5534
+pager: +1 818 585-5593
+manager: cn=Sibelle Wilczewski
+secretary: cn=Franklyn Asghar
+roomNumber: 1154
+
+dn: cn=Carolan Rangel, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Carolan Rangel
+sn: Rangel
+description: This is Carolan Rangel's description
+facsimileTelephoneNumber: +1 71 233-1472
+l: Sunnyvale
+ou: Peons
+postalAddress: example$Peons$Dept # 494
+telephoneNumber: +1 415 171-9753
+title: Senior Peons Dictator
+userPassword: legnaRnalo
+uid: Carolan_Rangel
+givenName: Carolan
+mail: Carolan_Rangel@example.com
+carLicense: XQIJM1O
+departmentNumber: 2990
+employeeType: Manager
+homePhone: +1 804 259-7816
+initials: C. R.
+mobile: +1 303 907-6627
+pager: +1 303 136-1379
+manager: cn=Dyanne Feeley
+secretary: cn=Kaycee Banigan
+roomNumber: 5953
+
+dn: cn=Rodrigo Howse, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rodrigo Howse
+sn: Howse
+description: This is Rodrigo Howse's description
+facsimileTelephoneNumber: +1 818 502-5698
+l: Santa Clara
+ou: Management
+postalAddress: example$Management$Dept # 401
+telephoneNumber: +1 213 824-8436
+title: Junior Management Madonna
+userPassword: eswoHogird
+uid: Rodrigo_Howse
+givenName: Rodrigo
+mail: Rodrigo_Howse@example.com
+carLicense: OBWWW45
+departmentNumber: 6277
+employeeType: Normal
+homePhone: +1 804 301-7119
+initials: R. H.
+mobile: +1 415 304-7146
+pager: +1 804 601-8474
+manager: cn=Shabbir Owen
+secretary: cn=Helmuth Preville
+roomNumber: 8577
+
+dn: cn=Shandie Tomlinson, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Shandie Tomlinson
+sn: Tomlinson
+description: This is Shandie Tomlinson's description
+facsimileTelephoneNumber: +1 213 176-3399
+l: Armonk
+ou: Product Development
+postalAddress: example$Product Development$Dept # 97
+telephoneNumber: +1 71 155-7520
+title: Elite Product Development Figurehead
+userPassword: nosnilmoTe
+uid: Shandie_Tomlinson
+givenName: Shandie
+mail: Shandie_Tomlinson@example.com
+carLicense: B46G33M
+departmentNumber: 4361
+employeeType: Employee
+homePhone: +1 303 684-3227
+initials: S. T.
+mobile: +1 71 567-8483
+pager: +1 818 555-1567
+manager: cn=Malethia Vermette
+secretary: cn=Crysta Spriggs
+roomNumber: 6587
+
+dn: cn=Ashien Breslin, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ashien Breslin
+sn: Breslin
+description: This is Ashien Breslin's description
+facsimileTelephoneNumber: +1 303 580-1145
+l: Santa Clara
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 674
+telephoneNumber: +1 71 204-9152
+title: Master Janitorial Czar
+userPassword: nilserBnei
+uid: Ashien_Breslin
+givenName: Ashien
+mail: Ashien_Breslin@example.com
+carLicense: HUSVSSW
+departmentNumber: 2283
+employeeType: Temp
+homePhone: +1 510 549-9456
+initials: A. B.
+mobile: +1 213 401-7401
+pager: +1 206 535-6074
+manager: cn=Faunie Gascho
+secretary: cn=Rebekkah Batchoun
+roomNumber: 30
+
+dn: cn=Eachelle Fabrizio, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Eachelle Fabrizio
+sn: Fabrizio
+description: This is Eachelle Fabrizio's description
+facsimileTelephoneNumber: +1 206 659-6659
+l: Armonk
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 742
+telephoneNumber: +1 510 450-3176
+title: Elite Janitorial Janitor
+userPassword: oizirbaFel
+uid: Eachelle_Fabrizio
+givenName: Eachelle
+mail: Eachelle_Fabrizio@example.com
+carLicense: D4UM5QY
+departmentNumber: 1508
+employeeType: Temp
+homePhone: +1 510 488-1339
+initials: E. F.
+mobile: +1 818 921-2770
+pager: +1 303 104-8410
+manager: cn=Stephana O'Grady
+secretary: cn=Okan Hameed
+roomNumber: 1302
+
+dn: cn=Britni Cwirzen, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Britni Cwirzen
+sn: Cwirzen
+description: This is Britni Cwirzen's description
+facsimileTelephoneNumber: +1 510 623-7079
+l: Orem
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 600
+telephoneNumber: +1 510 267-1155
+title: Supreme Janitorial Assistant
+userPassword: nezriwCint
+uid: Britni_Cwirzen
+givenName: Britni
+mail: Britni_Cwirzen@example.com
+carLicense: 1475JM0
+departmentNumber: 7848
+employeeType: Normal
+homePhone: +1 71 158-4673
+initials: B. C.
+mobile: +1 510 865-3190
+pager: +1 415 324-1704
+manager: cn=Pooh Fastpack
+secretary: cn=Edwin Hamori
+roomNumber: 5726
+
+dn: cn=Bertie MacNeill, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Bertie MacNeill
+sn: MacNeill
+description: This is Bertie MacNeill's description
+facsimileTelephoneNumber: +1 804 681-3840
+l: Cupertino
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 121
+telephoneNumber: +1 804 144-5078
+title: Senior Product Testing Dictator
+userPassword: llieNcaMei
+uid: Bertie_MacNeill
+givenName: Bertie
+mail: Bertie_MacNeill@example.com
+carLicense: Q6L3Y0O
+departmentNumber: 5832
+employeeType: Employee
+homePhone: +1 303 902-6450
+initials: B. M.
+mobile: +1 818 400-6535
+pager: +1 818 337-4870
+manager: cn=Willamina Shafik
+secretary: cn=Anatola Brockmann
+roomNumber: 8713
+
+dn: cn=Doreen Howse, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Doreen Howse
+sn: Howse
+description: This is Doreen Howse's description
+facsimileTelephoneNumber: +1 206 426-2076
+l: San Jose
+ou: Payroll
+postalAddress: example$Payroll$Dept # 477
+telephoneNumber: +1 303 633-2753
+title: Elite Payroll Visionary
+userPassword: eswoHneero
+uid: Doreen_Howse
+givenName: Doreen
+mail: Doreen_Howse@example.com
+carLicense: PX33SPM
+departmentNumber: 6990
+employeeType: Normal
+homePhone: +1 206 540-1747
+initials: D. H.
+mobile: +1 206 830-8753
+pager: +1 71 489-3580
+manager: cn=Didani Kovarik
+secretary: cn=Horacio Kunkel
+roomNumber: 6572
+
+dn: cn=Emanuel Kryski, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Emanuel Kryski
+sn: Kryski
+description: This is Emanuel Kryski's description
+facsimileTelephoneNumber: +1 818 575-1776
+l: Sunnyvale
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 401
+telephoneNumber: +1 206 302-8490
+title: Supreme Janitorial Artist
+userPassword: iksyrKleun
+uid: Emanuel_Kryski
+givenName: Emanuel
+mail: Emanuel_Kryski@example.com
+carLicense: YBDPZXD
+departmentNumber: 3185
+employeeType: Manager
+homePhone: +1 408 402-7437
+initials: E. K.
+mobile: +1 804 539-3118
+pager: +1 415 637-6578
+manager: cn=Berna Hofstetter
+secretary: cn=Ling-Zhong Ingersoll
+roomNumber: 8680
+
+dn: cn=Ingeberg Uecker, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ingeberg Uecker
+sn: Uecker
+description: This is Ingeberg Uecker's description
+facsimileTelephoneNumber: +1 510 645-1887
+l: Fremont
+ou: Peons
+postalAddress: example$Peons$Dept # 374
+telephoneNumber: +1 213 264-6668
+title: Elite Peons Developer
+userPassword: rekceUgreb
+uid: Ingeberg_Uecker
+givenName: Ingeberg
+mail: Ingeberg_Uecker@example.com
+carLicense: KKROWG7
+departmentNumber: 5843
+employeeType: Normal
+homePhone: +1 510 909-6303
+initials: I. U.
+mobile: +1 213 341-7760
+pager: +1 408 926-7613
+manager: cn=Sashenka Anderson
+secretary: cn=Gail Momtahan
+roomNumber: 7472
+
+dn: cn=Lorine Kinch, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lorine Kinch
+sn: Kinch
+description: This is Lorine Kinch's description
+facsimileTelephoneNumber: +1 408 260-7615
+l: Cupertino
+ou: Peons
+postalAddress: example$Peons$Dept # 387
+telephoneNumber: +1 303 471-6222
+title: Supreme Peons Engineer
+userPassword: hcniKeniro
+uid: Lorine_Kinch
+givenName: Lorine
+mail: Lorine_Kinch@example.com
+carLicense: EQ622AQ
+departmentNumber: 6450
+employeeType: Temp
+homePhone: +1 818 643-2710
+initials: L. K.
+mobile: +1 415 508-5241
+pager: +1 510 895-8472
+manager: cn=Grace Dieu
+secretary: cn=Micaela Naor
+roomNumber: 4015
+
+dn: cn=Tab Danko, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tab Danko
+sn: Danko
+description: This is Tab Danko's description
+facsimileTelephoneNumber: +1 510 538-4771
+l: Orem
+ou: Administrative
+postalAddress: example$Administrative$Dept # 669
+telephoneNumber: +1 415 622-6699
+title: Associate Administrative Yahoo
+userPassword: oknaDbaT
+uid: Tab_Danko
+givenName: Tab
+mail: Tab_Danko@example.com
+carLicense: XB3M2XY
+departmentNumber: 1320
+employeeType: Employee
+homePhone: +1 206 386-9666
+initials: T. D.
+mobile: +1 206 296-7053
+pager: +1 804 647-2654
+manager: cn=Ingaborg Burruss
+secretary: cn=Hilde McCaugherty
+roomNumber: 1597
+
+dn: cn=Marlon Totino, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marlon Totino
+sn: Totino
+description: This is Marlon Totino's description
+facsimileTelephoneNumber: +1 510 766-2749
+l: Orem
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 299
+telephoneNumber: +1 804 210-1623
+title: Elite Human Resources Yahoo
+userPassword: onitoTnolr
+uid: Marlon_Totino
+givenName: Marlon
+mail: Marlon_Totino@example.com
+carLicense: OZUM13H
+departmentNumber: 726
+employeeType: Employee
+homePhone: +1 818 641-7683
+initials: M. T.
+mobile: +1 510 638-7831
+pager: +1 206 109-7099
+manager: cn=Nahum Xavier
+secretary: cn=Patra Pancholy
+roomNumber: 6781
+
+dn: cn=Geri Clendening, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Geri Clendening
+sn: Clendening
+description: This is Geri Clendening's description
+facsimileTelephoneNumber: +1 804 546-8102
+l: Milpitas
+ou: Planning
+postalAddress: example$Planning$Dept # 9
+telephoneNumber: +1 206 946-7570
+title: Senior Planning Developer
+userPassword: gninednelC
+uid: Geri_Clendening
+givenName: Geri
+mail: Geri_Clendening@example.com
+carLicense: YSRDKUW
+departmentNumber: 626
+employeeType: Temp
+homePhone: +1 213 664-2462
+initials: G. C.
+mobile: +1 415 182-6899
+pager: +1 213 706-7011
+manager: cn=Pavla Sharman
+secretary: cn=Flore Piecowye
+roomNumber: 2093
+
+dn: cn=Jozsef Cricker, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jozsef Cricker
+sn: Cricker
+description: This is Jozsef Cricker's description
+facsimileTelephoneNumber: +1 213 738-2340
+l: Mountain View
+ou: Accounting
+postalAddress: example$Accounting$Dept # 378
+telephoneNumber: +1 71 289-1183
+title: Junior Accounting Director
+userPassword: rekcirCfes
+uid: Jozsef_Cricker
+givenName: Jozsef
+mail: Jozsef_Cricker@example.com
+carLicense: FMI2DBR
+departmentNumber: 3888
+employeeType: Employee
+homePhone: +1 71 155-8180
+initials: J. C.
+mobile: +1 510 173-6447
+pager: +1 303 395-8165
+manager: cn=Jodi Planting
+secretary: cn=Debi Kiernan
+roomNumber: 7477
+
+dn: cn=Brinn Cicci, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Brinn Cicci
+sn: Cicci
+description: This is Brinn Cicci's description
+facsimileTelephoneNumber: +1 818 402-7322
+l: Mountain View
+ou: Administrative
+postalAddress: example$Administrative$Dept # 18
+telephoneNumber: +1 303 766-5895
+title: Associate Administrative Manager
+userPassword: icciCnnirB
+uid: Brinn_Cicci
+givenName: Brinn
+mail: Brinn_Cicci@example.com
+carLicense: SMMAQ8X
+departmentNumber: 9613
+employeeType: Temp
+homePhone: +1 510 595-5078
+initials: B. C.
+mobile: +1 213 229-3849
+pager: +1 415 826-5456
+manager: cn=Millicent Majmudar
+secretary: cn=Truus Kot
+roomNumber: 3350
+
+dn: cn=Indiana Hedrick, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Indiana Hedrick
+sn: Hedrick
+description: This is Indiana Hedrick's description
+facsimileTelephoneNumber: +1 206 467-7225
+l: Alameda
+ou: Product Development
+postalAddress: example$Product Development$Dept # 408
+telephoneNumber: +1 510 743-2784
+title: Supreme Product Development Janitor
+userPassword: kcirdeHana
+uid: Indiana_Hedrick
+givenName: Indiana
+mail: Indiana_Hedrick@example.com
+carLicense: L1M55RO
+departmentNumber: 9323
+employeeType: Employee
+homePhone: +1 206 485-1077
+initials: I. H.
+mobile: +1 510 717-7167
+pager: +1 415 351-6790
+manager: cn=Cosetta Canavan
+secretary: cn=Cheuk Steeves
+roomNumber: 9602
+
+dn: cn=Shaw Karwowski, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Shaw Karwowski
+sn: Karwowski
+description: This is Shaw Karwowski's description
+facsimileTelephoneNumber: +1 510 254-1850
+l: San Francisco
+ou: Administrative
+postalAddress: example$Administrative$Dept # 81
+telephoneNumber: +1 818 961-8470
+title: Master Administrative Writer
+userPassword: ikswowraKw
+uid: Shaw_Karwowski
+givenName: Shaw
+mail: Shaw_Karwowski@example.com
+carLicense: WS0GFAI
+departmentNumber: 7332
+employeeType: Manager
+homePhone: +1 415 429-2328
+initials: S. K.
+mobile: +1 408 215-2303
+pager: +1 71 402-3905
+manager: cn=Lanita Tarof
+secretary: cn=Sabine Karr
+roomNumber: 7633
+
+dn: cn=Trish Hyman, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Trish Hyman
+sn: Hyman
+description: This is Trish Hyman's description
+facsimileTelephoneNumber: +1 71 536-8500
+l: San Francisco
+ou: Administrative
+postalAddress: example$Administrative$Dept # 476
+telephoneNumber: +1 510 222-5063
+title: Supreme Administrative Consultant
+userPassword: namyHhsirT
+uid: Trish_Hyman
+givenName: Trish
+mail: Trish_Hyman@example.com
+carLicense: B7LG58P
+departmentNumber: 5752
+employeeType: Contract
+homePhone: +1 510 735-3567
+initials: T. H.
+mobile: +1 213 727-1295
+pager: +1 510 825-1188
+manager: cn=Natalina Rowley
+secretary: cn=Gaynor Bragado
+roomNumber: 2395
+
+dn: cn=Biddie Reith, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Biddie Reith
+sn: Reith
+description: This is Biddie Reith's description
+facsimileTelephoneNumber: +1 303 575-3669
+l: Menlo Park
+ou: Administrative
+postalAddress: example$Administrative$Dept # 378
+telephoneNumber: +1 804 998-5469
+title: Elite Administrative Yahoo
+userPassword: htieReiddi
+uid: Biddie_Reith
+givenName: Biddie
+mail: Biddie_Reith@example.com
+carLicense: MMQ52N2
+departmentNumber: 864
+employeeType: Temp
+homePhone: +1 71 387-4945
+initials: B. R.
+mobile: +1 213 580-4713
+pager: +1 818 698-6851
+manager: cn=Chester Balascak
+secretary: cn=Mellisa Parsloe
+roomNumber: 8220
+
+dn: cn=Tracey Sgornikov, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tracey Sgornikov
+sn: Sgornikov
+description: This is Tracey Sgornikov's description
+facsimileTelephoneNumber: +1 206 740-4930
+l: Fremont
+ou: Peons
+postalAddress: example$Peons$Dept # 819
+telephoneNumber: +1 71 706-6210
+title: Chief Peons Vice President
+userPassword: vokinrogSy
+uid: Tracey_Sgornikov
+givenName: Tracey
+mail: Tracey_Sgornikov@example.com
+carLicense: 27MTRHA
+departmentNumber: 5277
+employeeType: Temp
+homePhone: +1 415 748-5665
+initials: T. S.
+mobile: +1 71 107-1408
+pager: +1 213 116-5969
+manager: cn=Sara-ann Livezey
+secretary: cn=Kathi Alfred
+roomNumber: 9674
+
+dn: cn=Maggy Maduri, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Maggy Maduri
+sn: Maduri
+description: This is Maggy Maduri's description
+facsimileTelephoneNumber: +1 213 447-2272
+l: San Mateo
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 555
+telephoneNumber: +1 818 180-2121
+title: Master Product Testing Engineer
+userPassword: irudaMygga
+uid: Maggy_Maduri
+givenName: Maggy
+mail: Maggy_Maduri@example.com
+carLicense: TNLLXG6
+departmentNumber: 7262
+employeeType: Contract
+homePhone: +1 213 175-6512
+initials: M. M.
+mobile: +1 71 714-4757
+pager: +1 415 289-2777
+manager: cn=Sarajane Beauvais
+secretary: cn=Hoa-Van Bowden
+roomNumber: 1059
+
+dn: cn=Rochell Reva, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rochell Reva
+sn: Reva
+description: This is Rochell Reva's description
+facsimileTelephoneNumber: +1 213 365-3487
+l: Emeryville
+ou: Payroll
+postalAddress: example$Payroll$Dept # 138
+telephoneNumber: +1 206 483-6808
+title: Associate Payroll Yahoo
+userPassword: aveRllehco
+uid: Rochell_Reva
+givenName: Rochell
+mail: Rochell_Reva@example.com
+carLicense: SAOEA2M
+departmentNumber: 8588
+employeeType: Manager
+homePhone: +1 408 867-6779
+initials: R. R.
+mobile: +1 510 753-5758
+pager: +1 71 116-1834
+manager: cn=Saied Kuehn
+secretary: cn=Janenna Cloutier
+roomNumber: 1439
+
+dn: cn=Karilynn Hungle, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Karilynn Hungle
+sn: Hungle
+description: This is Karilynn Hungle's description
+facsimileTelephoneNumber: +1 415 407-3988
+l: Mountain View
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 230
+telephoneNumber: +1 408 536-7299
+title: Master Janitorial Pinhead
+userPassword: elgnuHnnyl
+uid: Karilynn_Hungle
+givenName: Karilynn
+mail: Karilynn_Hungle@example.com
+carLicense: WU2Q8VC
+departmentNumber: 7102
+employeeType: Normal
+homePhone: +1 408 806-2471
+initials: K. H.
+mobile: +1 213 619-4680
+pager: +1 415 749-1643
+manager: cn=Lolly Hamelin
+secretary: cn=Marjorie Sidor
+roomNumber: 6995
+
+dn: cn=Delcine Aksel, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Delcine Aksel
+sn: Aksel
+description: This is Delcine Aksel's description
+facsimileTelephoneNumber: +1 206 563-3474
+l: Cambridge
+ou: Administrative
+postalAddress: example$Administrative$Dept # 394
+telephoneNumber: +1 213 450-8763
+title: Master Administrative Janitor
+userPassword: leskAenicl
+uid: Delcine_Aksel
+givenName: Delcine
+mail: Delcine_Aksel@example.com
+carLicense: IMAH7I3
+departmentNumber: 9679
+employeeType: Contract
+homePhone: +1 213 479-2206
+initials: D. A.
+mobile: +1 71 851-6931
+pager: +1 213 406-9050
+manager: cn=Dewi Hallman
+secretary: cn=Grant Gentes
+roomNumber: 5955
+
+dn: cn=Hpone Skerry, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Hpone Skerry
+sn: Skerry
+description: This is Hpone Skerry's description
+facsimileTelephoneNumber: +1 303 148-7675
+l: Redmond
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 458
+telephoneNumber: +1 71 568-4643
+title: Elite Janitorial Stooge
+userPassword: yrrekSenop
+uid: Hpone_Skerry
+givenName: Hpone
+mail: Hpone_Skerry@example.com
+carLicense: CZMYESU
+departmentNumber: 5364
+employeeType: Manager
+homePhone: +1 408 118-6258
+initials: H. S.
+mobile: +1 804 904-7291
+pager: +1 818 470-7449
+manager: cn=Mary-Ellen Vempati
+secretary: cn=Alvin Goetz
+roomNumber: 9655
+
+dn: cn=Tosca Belley, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tosca Belley
+sn: Belley
+description: This is Tosca Belley's description
+facsimileTelephoneNumber: +1 303 222-4953
+l: Santa Clara
+ou: Management
+postalAddress: example$Management$Dept # 491
+telephoneNumber: +1 510 584-9011
+title: Junior Management Sales Rep
+userPassword: yelleBacso
+uid: Tosca_Belley
+givenName: Tosca
+mail: Tosca_Belley@example.com
+carLicense: ZQOM5MA
+departmentNumber: 4909
+employeeType: Employee
+homePhone: +1 510 733-4242
+initials: T. B.
+mobile: +1 213 253-9601
+pager: +1 71 445-2483
+manager: cn=Radomir Kingan
+secretary: cn=Livvy Zakarow
+roomNumber: 2237
+
+dn: cn=Andree Benefits, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Andree Benefits
+sn: Benefits
+description: This is Andree Benefits's description
+facsimileTelephoneNumber: +1 818 420-5771
+l: Sunnyvale
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 593
+telephoneNumber: +1 818 366-7679
+title: Supreme Human Resources Mascot
+userPassword: stifeneBee
+uid: Andree_Benefits
+givenName: Andree
+mail: Andree_Benefits@example.com
+carLicense: 1J373OI
+departmentNumber: 8939
+employeeType: Manager
+homePhone: +1 213 865-3912
+initials: A. B.
+mobile: +1 206 379-9531
+pager: +1 408 680-8167
+manager: cn=Wen-Kai Licandro
+secretary: cn=Nick Goertzen
+roomNumber: 3822
+
+dn: cn=Joyous Vahdat, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Joyous Vahdat
+sn: Vahdat
+description: This is Joyous Vahdat's description
+facsimileTelephoneNumber: +1 71 226-5208
+l: San Francisco
+ou: Accounting
+postalAddress: example$Accounting$Dept # 451
+telephoneNumber: +1 408 841-3211
+title: Associate Accounting Mascot
+userPassword: tadhaVsuoy
+uid: Joyous_Vahdat
+givenName: Joyous
+mail: Joyous_Vahdat@example.com
+carLicense: U13IIMB
+departmentNumber: 6836
+employeeType: Contract
+homePhone: +1 213 709-2972
+initials: J. V.
+mobile: +1 71 641-5362
+pager: +1 213 960-4165
+manager: cn=Randie Bowler
+secretary: cn=Leonida Plamondon
+roomNumber: 4827
+
+dn: cn=Hermione Cooke, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Hermione Cooke
+sn: Cooke
+description: This is Hermione Cooke's description
+facsimileTelephoneNumber: +1 303 421-3165
+l: Milpitas
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 80
+telephoneNumber: +1 213 342-4314
+title: Associate Product Testing Evangelist
+userPassword: ekooCenoim
+uid: Hermione_Cooke
+givenName: Hermione
+mail: Hermione_Cooke@example.com
+carLicense: 4LFKTD3
+departmentNumber: 4715
+employeeType: Employee
+homePhone: +1 408 756-8850
+initials: H. C.
+mobile: +1 71 866-4175
+pager: +1 804 835-7472
+manager: cn=Selma Han
+secretary: cn=Geri Sandner
+roomNumber: 9347
+
+dn: cn=Adriana McFeely, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Adriana McFeely
+sn: McFeely
+description: This is Adriana McFeely's description
+facsimileTelephoneNumber: +1 804 232-3810
+l: San Francisco
+ou: Peons
+postalAddress: example$Peons$Dept # 443
+telephoneNumber: +1 206 837-4130
+title: Junior Peons Vice President
+userPassword: yleeFcMana
+uid: Adriana_McFeely
+givenName: Adriana
+mail: Adriana_McFeely@example.com
+carLicense: XVRFQ03
+departmentNumber: 5443
+employeeType: Temp
+homePhone: +1 818 186-2710
+initials: A. M.
+mobile: +1 804 152-9483
+pager: +1 206 986-9151
+manager: cn=Phuong Goyal
+secretary: cn=Jordanna Frobel
+roomNumber: 3399
+
+dn: cn=Christal Haig, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Christal Haig
+sn: Haig
+description: This is Christal Haig's description
+facsimileTelephoneNumber: +1 818 424-7571
+l: San Mateo
+ou: Management
+postalAddress: example$Management$Dept # 21
+telephoneNumber: +1 206 460-2357
+title: Chief Management Vice President
+userPassword: giaHlatsir
+uid: Christal_Haig
+givenName: Christal
+mail: Christal_Haig@example.com
+carLicense: TI8IBH4
+departmentNumber: 6729
+employeeType: Employee
+homePhone: +1 818 763-6515
+initials: C. H.
+mobile: +1 415 481-3761
+pager: +1 213 504-8977
+manager: cn=Kerrill Tufford
+secretary: cn=Phan Chacko
+roomNumber: 72
+
+dn: cn=Maitreya Kruger, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Maitreya Kruger
+sn: Kruger
+description: This is Maitreya Kruger's description
+facsimileTelephoneNumber: +1 510 972-1213
+l: Sunnyvale
+ou: Planning
+postalAddress: example$Planning$Dept # 318
+telephoneNumber: +1 71 593-4533
+title: Junior Planning Technician
+userPassword: regurKayer
+uid: Maitreya_Kruger
+givenName: Maitreya
+mail: Maitreya_Kruger@example.com
+carLicense: XU80AAG
+departmentNumber: 2132
+employeeType: Manager
+homePhone: +1 303 151-1291
+initials: M. K.
+mobile: +1 408 645-4317
+pager: +1 206 893-3658
+manager: cn=Russell Leima
+secretary: cn=Maynard Blackshire
+roomNumber: 7642
+
+dn: cn=Laurie Mcshane, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Laurie Mcshane
+sn: Mcshane
+description: This is Laurie Mcshane's description
+facsimileTelephoneNumber: +1 804 526-3497
+l: Alameda
+ou: Peons
+postalAddress: example$Peons$Dept # 188
+telephoneNumber: +1 206 401-5905
+title: Junior Peons Director
+userPassword: enahscMeir
+uid: Laurie_Mcshane
+givenName: Laurie
+mail: Laurie_Mcshane@example.com
+carLicense: MT9DAQW
+departmentNumber: 3956
+employeeType: Manager
+homePhone: +1 206 171-3450
+initials: L. M.
+mobile: +1 804 732-9316
+pager: +1 408 344-3526
+manager: cn=Nadim Hagerty
+secretary: cn=Dalenna Wagner
+roomNumber: 9656
+
+dn: cn=Lynda Mototsune, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lynda Mototsune
+sn: Mototsune
+description: This is Lynda Mototsune's description
+facsimileTelephoneNumber: +1 415 376-8738
+l: Fremont
+ou: Planning
+postalAddress: example$Planning$Dept # 894
+telephoneNumber: +1 71 892-7223
+title: Associate Planning Assistant
+userPassword: enustotoMa
+uid: Lynda_Mototsune
+givenName: Lynda
+mail: Lynda_Mototsune@example.com
+carLicense: 979VNH2
+departmentNumber: 8775
+employeeType: Temp
+homePhone: +1 303 995-8927
+initials: L. M.
+mobile: +1 206 129-4437
+pager: +1 510 486-6142
+manager: cn=Gokul Ludviksen
+secretary: cn=Corina Hutt
+roomNumber: 1940
+
+dn: cn=Lucilia Krodel, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lucilia Krodel
+sn: Krodel
+description: This is Lucilia Krodel's description
+facsimileTelephoneNumber: +1 206 202-8883
+l: Redmond
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 895
+telephoneNumber: +1 510 593-7649
+title: Master Janitorial Architect
+userPassword: ledorKaili
+uid: Lucilia_Krodel
+givenName: Lucilia
+mail: Lucilia_Krodel@example.com
+carLicense: F2XN6JO
+departmentNumber: 765
+employeeType: Normal
+homePhone: +1 510 560-9551
+initials: L. K.
+mobile: +1 804 299-5573
+pager: +1 804 157-1827
+manager: cn=Virgie Samsonenko
+secretary: cn=Nona Armenta
+roomNumber: 299
+
+dn: cn=Roger Fowlston, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Roger Fowlston
+sn: Fowlston
+description: This is Roger Fowlston's description
+facsimileTelephoneNumber: +1 303 187-8025
+l: Mountain View
+ou: Management
+postalAddress: example$Management$Dept # 112
+telephoneNumber: +1 804 451-2104
+title: Supreme Management Dictator
+userPassword: notslwoFre
+uid: Roger_Fowlston
+givenName: Roger
+mail: Roger_Fowlston@example.com
+carLicense: 9Q99OAO
+departmentNumber: 9007
+employeeType: Contract
+homePhone: +1 408 586-2902
+initials: R. F.
+mobile: +1 818 410-4661
+pager: +1 510 790-6793
+manager: cn=Natividad Hurd
+secretary: cn=Deri Falquero
+roomNumber: 3244
+
+dn: cn=Kaminsky Dennen, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kaminsky Dennen
+sn: Dennen
+description: This is Kaminsky Dennen's description
+facsimileTelephoneNumber: +1 415 344-9342
+l: Cambridge
+ou: Management
+postalAddress: example$Management$Dept # 214
+telephoneNumber: +1 415 174-6019
+title: Master Management Vice President
+userPassword: nenneDyksn
+uid: Kaminsky_Dennen
+givenName: Kaminsky
+mail: Kaminsky_Dennen@example.com
+carLicense: 1YRG7Z7
+departmentNumber: 8616
+employeeType: Manager
+homePhone: +1 71 176-2078
+initials: K. D.
+mobile: +1 303 519-5191
+pager: +1 213 184-1795
+manager: cn=Giambattista Bottoms
+secretary: cn=Ashley Rotenberg
+roomNumber: 9959
+
+dn: cn=Gilbert Vertolli, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gilbert Vertolli
+sn: Vertolli
+description: This is Gilbert Vertolli's description
+facsimileTelephoneNumber: +1 303 866-9523
+l: Mountain View
+ou: Product Development
+postalAddress: example$Product Development$Dept # 159
+telephoneNumber: +1 71 913-8787
+title: Junior Product Development President
+userPassword: illotreVtr
+uid: Gilbert_Vertolli
+givenName: Gilbert
+mail: Gilbert_Vertolli@example.com
+carLicense: IJLNM0X
+departmentNumber: 5590
+employeeType: Contract
+homePhone: +1 408 246-9967
+initials: G. V.
+mobile: +1 213 960-7422
+pager: +1 206 427-5903
+manager: cn=Marrissa Makohoniuk
+secretary: cn=Coralie Stampfl
+roomNumber: 8011
+
+dn: cn=Pierrette Stern, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Pierrette Stern
+sn: Stern
+description: This is Pierrette Stern's description
+facsimileTelephoneNumber: +1 510 153-7113
+l: Palo Alto
+ou: Management
+postalAddress: example$Management$Dept # 743
+telephoneNumber: +1 804 572-2868
+title: Master Management Visionary
+userPassword: nretSetter
+uid: Pierrette_Stern
+givenName: Pierrette
+mail: Pierrette_Stern@example.com
+carLicense: KGFVA7O
+departmentNumber: 8957
+employeeType: Normal
+homePhone: +1 408 239-1926
+initials: P. S.
+mobile: +1 303 296-2747
+pager: +1 804 168-7675
+manager: cn=Noyes Hawes
+secretary: cn=Anneliese Mattiussi
+roomNumber: 2470
+
+dn: cn=Marie-Nadine Finnighan, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marie-Nadine Finnighan
+sn: Finnighan
+description: This is Marie-Nadine Finnighan's description
+facsimileTelephoneNumber: +1 804 785-6184
+l: Cambridge
+ou: Payroll
+postalAddress: example$Payroll$Dept # 569
+telephoneNumber: +1 804 727-5093
+title: Elite Payroll Artist
+userPassword: nahginniFe
+uid: Marie-Nadine_Finnighan
+givenName: Marie-Nadine
+mail: Marie-Nadine_Finnighan@example.com
+carLicense: G7UHEG9
+departmentNumber: 9749
+employeeType: Contract
+homePhone: +1 206 551-2746
+initials: M. F.
+mobile: +1 71 171-6790
+pager: +1 818 353-2532
+manager: cn=Wai-Bun Wegrowicz
+secretary: cn=Gaston Paton
+roomNumber: 8909
+
+dn: cn=Blinnie Maidenhead, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Blinnie Maidenhead
+sn: Maidenhead
+description: This is Blinnie Maidenhead's description
+facsimileTelephoneNumber: +1 206 836-2712
+l: San Jose
+ou: Planning
+postalAddress: example$Planning$Dept # 882
+telephoneNumber: +1 408 950-9394
+title: Master Planning Figurehead
+userPassword: daehnediaM
+uid: Blinnie_Maidenhead
+givenName: Blinnie
+mail: Blinnie_Maidenhead@example.com
+carLicense: 5OGK27G
+departmentNumber: 2256
+employeeType: Temp
+homePhone: +1 206 880-6960
+initials: B. M.
+mobile: +1 303 794-9864
+pager: +1 510 571-1785
+manager: cn=Ynes Agostino
+secretary: cn=Kimberley Stodart
+roomNumber: 6736
+
+dn: cn=Dwain McCue, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dwain McCue
+sn: McCue
+description: This is Dwain McCue's description
+facsimileTelephoneNumber: +1 71 905-4092
+l: Redmond
+ou: Administrative
+postalAddress: example$Administrative$Dept # 177
+telephoneNumber: +1 510 633-3803
+title: Master Administrative Admin
+userPassword: euCcMniawD
+uid: Dwain_McCue
+givenName: Dwain
+mail: Dwain_McCue@example.com
+carLicense: YIT3VTT
+departmentNumber: 9685
+employeeType: Normal
+homePhone: +1 303 100-4605
+initials: D. M.
+mobile: +1 804 859-5183
+pager: +1 818 871-2674
+manager: cn=Nerissa Hosseini
+secretary: cn=Chelsae Zug
+roomNumber: 7909
+
+dn: cn=Jennie Millspaugh, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jennie Millspaugh
+sn: Millspaugh
+description: This is Jennie Millspaugh's description
+facsimileTelephoneNumber: +1 213 627-7126
+l: San Jose
+ou: Accounting
+postalAddress: example$Accounting$Dept # 802
+telephoneNumber: +1 818 624-9763
+title: Elite Accounting Manager
+userPassword: hguapslliM
+uid: Jennie_Millspaugh
+givenName: Jennie
+mail: Jennie_Millspaugh@example.com
+carLicense: P5FHZDG
+departmentNumber: 5435
+employeeType: Employee
+homePhone: +1 804 351-9325
+initials: J. M.
+mobile: +1 206 399-8733
+pager: +1 71 110-3934
+manager: cn=Redgie Lojewski
+secretary: cn=Ichiro Lindow
+roomNumber: 9667
+
+dn: cn=Annabel Planthara, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Annabel Planthara
+sn: Planthara
+description: This is Annabel Planthara's description
+facsimileTelephoneNumber: +1 804 783-4207
+l: Alameda
+ou: Product Development
+postalAddress: example$Product Development$Dept # 285
+telephoneNumber: +1 804 691-1923
+title: Master Product Development Assistant
+userPassword: arahtnalPl
+uid: Annabel_Planthara
+givenName: Annabel
+mail: Annabel_Planthara@example.com
+carLicense: TIMAAPO
+departmentNumber: 9779
+employeeType: Normal
+homePhone: +1 510 454-9254
+initials: A. P.
+mobile: +1 303 755-3875
+pager: +1 818 581-8706
+manager: cn=Haig De Leon
+secretary: cn=Anett Krenn
+roomNumber: 8145
+
+dn: cn=Sarajane Shtivelman, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sarajane Shtivelman
+sn: Shtivelman
+description: This is Sarajane Shtivelman's description
+facsimileTelephoneNumber: +1 804 134-3746
+l: San Francisco
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 252
+telephoneNumber: +1 71 144-2666
+title: Senior Product Testing Technician
+userPassword: namlevithS
+uid: Sarajane_Shtivelman
+givenName: Sarajane
+mail: Sarajane_Shtivelman@example.com
+carLicense: CWJ9V9X
+departmentNumber: 8381
+employeeType: Normal
+homePhone: +1 510 670-2961
+initials: S. S.
+mobile: +1 415 326-4228
+pager: +1 213 254-6548
+manager: cn=Dee Nunez
+secretary: cn=Marguerita Marshman
+roomNumber: 6517
+
+dn: cn=Matthew Kurolapnik, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Matthew Kurolapnik
+sn: Kurolapnik
+description: This is Matthew Kurolapnik's description
+facsimileTelephoneNumber: +1 71 280-4567
+l: Mountain View
+ou: Planning
+postalAddress: example$Planning$Dept # 614
+telephoneNumber: +1 71 986-5573
+title: Elite Planning Vice President
+userPassword: kinpaloruK
+uid: Matthew_Kurolapnik
+givenName: Matthew
+mail: Matthew_Kurolapnik@example.com
+carLicense: 578455X
+departmentNumber: 2947
+employeeType: Employee
+homePhone: +1 71 117-5562
+initials: M. K.
+mobile: +1 804 988-1132
+pager: +1 303 423-4026
+manager: cn=Dido Linke
+secretary: cn=Lanie MacGregor
+roomNumber: 1273
+
+dn: cn=Ardra Boyer, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ardra Boyer
+sn: Boyer
+description: This is Ardra Boyer's description
+facsimileTelephoneNumber: +1 303 930-9326
+l: San Francisco
+ou: Administrative
+postalAddress: example$Administrative$Dept # 529
+telephoneNumber: +1 510 467-7202
+title: Chief Administrative Mascot
+userPassword: reyoBardrA
+uid: Ardra_Boyer
+givenName: Ardra
+mail: Ardra_Boyer@example.com
+carLicense: U8MLFI5
+departmentNumber: 8495
+employeeType: Employee
+homePhone: +1 804 158-9355
+initials: A. B.
+mobile: +1 804 234-5788
+pager: +1 804 986-7673
+manager: cn=Belle Daudin
+secretary: cn=Aurelia Kee
+roomNumber: 5432
+
+dn: cn=Henrietta Litt, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Henrietta Litt
+sn: Litt
+description: This is Henrietta Litt's description
+facsimileTelephoneNumber: +1 408 621-6067
+l: Sunnyvale
+ou: Product Development
+postalAddress: example$Product Development$Dept # 201
+telephoneNumber: +1 213 699-1133
+title: Supreme Product Development Janitor
+userPassword: ttiLatteir
+uid: Henrietta_Litt
+givenName: Henrietta
+mail: Henrietta_Litt@example.com
+carLicense: 6BO1UC0
+departmentNumber: 122
+employeeType: Manager
+homePhone: +1 303 433-6757
+initials: H. L.
+mobile: +1 303 715-4558
+pager: +1 408 110-4443
+manager: cn=Julianna Pipkins
+secretary: cn=Addie Groetsema
+roomNumber: 8776
+
+dn: cn=Ashli Gowan, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ashli Gowan
+sn: Gowan
+description: This is Ashli Gowan's description
+facsimileTelephoneNumber: +1 71 435-8571
+l: Fremont
+ou: Peons
+postalAddress: example$Peons$Dept # 639
+telephoneNumber: +1 415 874-4530
+title: Senior Peons Grunt
+userPassword: nawoGilhsA
+uid: Ashli_Gowan
+givenName: Ashli
+mail: Ashli_Gowan@example.com
+carLicense: 9GZEW1O
+departmentNumber: 9043
+employeeType: Temp
+homePhone: +1 510 988-6319
+initials: A. G.
+mobile: +1 804 813-6018
+pager: +1 408 169-7798
+manager: cn=Nissie Hr
+secretary: cn=Fitzroy Sanche
+roomNumber: 4186
+
+dn: cn=Rosa Haren, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rosa Haren
+sn: Haren
+description: This is Rosa Haren's description
+facsimileTelephoneNumber: +1 804 627-5203
+l: Mountain View
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 444
+telephoneNumber: +1 206 874-2444
+title: Supreme Human Resources Consultant
+userPassword: neraHasoR
+uid: Rosa_Haren
+givenName: Rosa
+mail: Rosa_Haren@example.com
+carLicense: ZIAZ88H
+departmentNumber: 6486
+employeeType: Manager
+homePhone: +1 408 915-8966
+initials: R. H.
+mobile: +1 206 648-1469
+pager: +1 408 520-3881
+manager: cn=Birendra Soong
+secretary: cn=Tineke Kashani-nia
+roomNumber: 3424
+
+dn: cn=Sherryl Hubley, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sherryl Hubley
+sn: Hubley
+description: This is Sherryl Hubley's description
+facsimileTelephoneNumber: +1 415 345-9742
+l: Palo Alto
+ou: Product Development
+postalAddress: example$Product Development$Dept # 301
+telephoneNumber: +1 71 513-4816
+title: Supreme Product Development Manager
+userPassword: yelbuHlyrr
+uid: Sherryl_Hubley
+givenName: Sherryl
+mail: Sherryl_Hubley@example.com
+carLicense: QDMPWW5
+departmentNumber: 1350
+employeeType: Employee
+homePhone: +1 303 577-4676
+initials: S. H.
+mobile: +1 303 298-5535
+pager: +1 408 158-6998
+manager: cn=Pauly Marum
+secretary: cn=Calypso Jaques
+roomNumber: 9962
+
+dn: cn=Evy McCartin, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Evy McCartin
+sn: McCartin
+description: This is Evy McCartin's description
+facsimileTelephoneNumber: +1 303 501-6790
+l: Mountain View
+ou: Administrative
+postalAddress: example$Administrative$Dept # 684
+telephoneNumber: +1 818 269-1491
+title: Associate Administrative Consultant
+userPassword: nitraCcMyv
+uid: Evy_McCartin
+givenName: Evy
+mail: Evy_McCartin@example.com
+carLicense: MX13PEC
+departmentNumber: 7744
+employeeType: Normal
+homePhone: +1 303 594-3087
+initials: E. M.
+mobile: +1 303 677-6050
+pager: +1 71 977-4179
+manager: cn=Alaine Noris
+secretary: cn=Tonya Tupas
+roomNumber: 9770
+
+dn: cn=Vikki Loker, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Vikki Loker
+sn: Loker
+description: This is Vikki Loker's description
+facsimileTelephoneNumber: +1 303 803-1484
+l: Menlo Park
+ou: Product Development
+postalAddress: example$Product Development$Dept # 392
+telephoneNumber: +1 415 484-9793
+title: Associate Product Development Artist
+userPassword: rekoLikkiV
+uid: Vikki_Loker
+givenName: Vikki
+mail: Vikki_Loker@example.com
+carLicense: RPETBKN
+departmentNumber: 1086
+employeeType: Contract
+homePhone: +1 804 481-7397
+initials: V. L.
+mobile: +1 206 438-5732
+pager: +1 303 527-1871
+manager: cn=Iwan Kuo
+secretary: cn=Mersey Toulson
+roomNumber: 1350
+
+dn: cn=Gillan Enns, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gillan Enns
+sn: Enns
+description: This is Gillan Enns's description
+facsimileTelephoneNumber: +1 415 945-7529
+l: Redwood Shores
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 109
+telephoneNumber: +1 510 942-9016
+title: Supreme Product Testing Admin
+userPassword: snnEnalliG
+uid: Gillan_Enns
+givenName: Gillan
+mail: Gillan_Enns@example.com
+carLicense: Z1X1R73
+departmentNumber: 5104
+employeeType: Contract
+homePhone: +1 804 698-3875
+initials: G. E.
+mobile: +1 71 899-7262
+pager: +1 818 291-8114
+manager: cn=Ivo Chong
+secretary: cn=Gerrard Brownlie
+roomNumber: 8775
+
+dn: cn=Abigale Buggie, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Abigale Buggie
+sn: Buggie
+description: This is Abigale Buggie's description
+facsimileTelephoneNumber: +1 206 340-5088
+l: Fremont
+ou: Payroll
+postalAddress: example$Payroll$Dept # 6
+telephoneNumber: +1 206 119-9817
+title: Elite Payroll Consultant
+userPassword: eigguBelag
+uid: Abigale_Buggie
+givenName: Abigale
+mail: Abigale_Buggie@example.com
+carLicense: 5N4DTWL
+departmentNumber: 4104
+employeeType: Contract
+homePhone: +1 206 749-8565
+initials: A. B.
+mobile: +1 510 883-2759
+pager: +1 415 287-1338
+manager: cn=Larysa Dikens
+secretary: cn=Shae Schmadtke
+roomNumber: 9324
+
+dn: cn=Salomi Marconi, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Salomi Marconi
+sn: Marconi
+description: This is Salomi Marconi's description
+facsimileTelephoneNumber: +1 818 129-1241
+l: Milpitas
+ou: Administrative
+postalAddress: example$Administrative$Dept # 36
+telephoneNumber: +1 818 196-4698
+title: Supreme Administrative Figurehead
+userPassword: inocraMimo
+uid: Salomi_Marconi
+givenName: Salomi
+mail: Salomi_Marconi@example.com
+carLicense: 35YLW6D
+departmentNumber: 8455
+employeeType: Manager
+homePhone: +1 213 223-7980
+initials: S. M.
+mobile: +1 415 127-5127
+pager: +1 303 832-4116
+manager: cn=Cicily Meridew
+secretary: cn=Lincoln Mathis
+roomNumber: 219
+
+dn: cn=Lan Satta, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lan Satta
+sn: Satta
+description: This is Lan Satta's description
+facsimileTelephoneNumber: +1 818 933-5138
+l: San Mateo
+ou: Planning
+postalAddress: example$Planning$Dept # 978
+telephoneNumber: +1 303 394-9267
+title: Supreme Planning Director
+userPassword: attaSnaL
+uid: Lan_Satta
+givenName: Lan
+mail: Lan_Satta@example.com
+carLicense: H4OVY9V
+departmentNumber: 5965
+employeeType: Manager
+homePhone: +1 303 427-9685
+initials: L. S.
+mobile: +1 804 744-6028
+pager: +1 213 859-6999
+manager: cn=Keep Nerem
+secretary: cn=Silvestro Nordstrom
+roomNumber: 4454
+
+dn: cn=Millicent Kirn, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Millicent Kirn
+sn: Kirn
+description: This is Millicent Kirn's description
+facsimileTelephoneNumber: +1 213 354-4566
+l: Palo Alto
+ou: Administrative
+postalAddress: example$Administrative$Dept # 851
+telephoneNumber: +1 71 635-7932
+title: Elite Administrative Dictator
+userPassword: nriKtnecil
+uid: Millicent_Kirn
+givenName: Millicent
+mail: Millicent_Kirn@example.com
+carLicense: 2853Z9D
+departmentNumber: 7301
+employeeType: Temp
+homePhone: +1 71 807-9201
+initials: M. K.
+mobile: +1 303 730-7705
+pager: +1 415 180-3311
+manager: cn=Martita Murash
+secretary: cn=Nissy Kunecke
+roomNumber: 6255
+
+dn: cn=Kelley Cooney, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kelley Cooney
+sn: Cooney
+description: This is Kelley Cooney's description
+facsimileTelephoneNumber: +1 818 408-9273
+l: Alameda
+ou: Administrative
+postalAddress: example$Administrative$Dept # 749
+telephoneNumber: +1 71 842-3720
+title: Senior Administrative Warrior
+userPassword: yenooCyell
+uid: Kelley_Cooney
+givenName: Kelley
+mail: Kelley_Cooney@example.com
+carLicense: 2H40R3M
+departmentNumber: 396
+employeeType: Contract
+homePhone: +1 71 925-7895
+initials: K. C.
+mobile: +1 303 526-7796
+pager: +1 206 176-1833
+manager: cn=Frayda Eteminan
+secretary: cn=Cecilia McNerlan
+roomNumber: 4888
+
+dn: cn=Parveen Eller, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Parveen Eller
+sn: Eller
+description: This is Parveen Eller's description
+facsimileTelephoneNumber: +1 510 114-4717
+l: Emeryville
+ou: Administrative
+postalAddress: example$Administrative$Dept # 794
+telephoneNumber: +1 71 103-8525
+title: Associate Administrative Mascot
+userPassword: rellEneevr
+uid: Parveen_Eller
+givenName: Parveen
+mail: Parveen_Eller@example.com
+carLicense: OJOIV49
+departmentNumber: 2052
+employeeType: Manager
+homePhone: +1 71 229-8361
+initials: P. E.
+mobile: +1 415 234-3951
+pager: +1 71 377-4448
+manager: cn=StClair Copeman
+secretary: cn=Hanni Syrett
+roomNumber: 8936
+
+dn: cn=Otha Meyerink, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Otha Meyerink
+sn: Meyerink
+description: This is Otha Meyerink's description
+facsimileTelephoneNumber: +1 510 897-1978
+l: Menlo Park
+ou: Administrative
+postalAddress: example$Administrative$Dept # 364
+telephoneNumber: +1 408 923-1988
+title: Master Administrative Visionary
+userPassword: knireyeMah
+uid: Otha_Meyerink
+givenName: Otha
+mail: Otha_Meyerink@example.com
+carLicense: ITY2RL7
+departmentNumber: 4075
+employeeType: Contract
+homePhone: +1 804 593-2750
+initials: O. M.
+mobile: +1 71 479-5687
+pager: +1 818 421-2877
+manager: cn=Farzad Skopliak
+secretary: cn=Fairy Dunnett
+roomNumber: 7326
+
+dn: cn=Node Tran, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Node Tran
+sn: Tran
+description: This is Node Tran's description
+facsimileTelephoneNumber: +1 408 902-1433
+l: Redwood Shores
+ou: Product Development
+postalAddress: example$Product Development$Dept # 303
+telephoneNumber: +1 415 415-5826
+title: Junior Product Development Grunt
+userPassword: narTedoN
+uid: Node_Tran
+givenName: Node
+mail: Node_Tran@example.com
+carLicense: SFRU6LI
+departmentNumber: 7978
+employeeType: Temp
+homePhone: +1 71 941-3886
+initials: N. T.
+mobile: +1 213 931-1271
+pager: +1 818 585-6809
+manager: cn=Mahmoud Moxley
+secretary: cn=Byron Tomasetti
+roomNumber: 2684
+
+dn: cn=Ranique Eansor, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ranique Eansor
+sn: Eansor
+description: This is Ranique Eansor's description
+facsimileTelephoneNumber: +1 804 217-2312
+l: Armonk
+ou: Administrative
+postalAddress: example$Administrative$Dept # 391
+telephoneNumber: +1 206 605-5168
+title: Senior Administrative Assistant
+userPassword: rosnaEeuqi
+uid: Ranique_Eansor
+givenName: Ranique
+mail: Ranique_Eansor@example.com
+carLicense: 68GVLF9
+departmentNumber: 7152
+employeeType: Normal
+homePhone: +1 804 490-8997
+initials: R. E.
+mobile: +1 303 714-5366
+pager: +1 213 696-9564
+manager: cn=Modestia Loudiadis
+secretary: cn=Svenn-Erik Pols
+roomNumber: 5162
+
+dn: cn=Chesteen Zhong, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Chesteen Zhong
+sn: Zhong
+description: This is Chesteen Zhong's description
+facsimileTelephoneNumber: +1 804 314-7419
+l: San Mateo
+ou: Product Development
+postalAddress: example$Product Development$Dept # 643
+telephoneNumber: +1 510 427-3733
+title: Senior Product Development Writer
+userPassword: gnohZneets
+uid: Chesteen_Zhong
+givenName: Chesteen
+mail: Chesteen_Zhong@example.com
+carLicense: 97VKVD5
+departmentNumber: 4398
+employeeType: Employee
+homePhone: +1 510 910-2117
+initials: C. Z.
+mobile: +1 510 641-8237
+pager: +1 415 337-9215
+manager: cn=Xuong Reynolds
+secretary: cn=Ira Ghossein
+roomNumber: 8094
+
+dn: cn=Davis Debord, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Davis Debord
+sn: Debord
+description: This is Davis Debord's description
+facsimileTelephoneNumber: +1 408 323-5635
+l: Armonk
+ou: Product Development
+postalAddress: example$Product Development$Dept # 149
+telephoneNumber: +1 818 957-1715
+title: Associate Product Development Admin
+userPassword: drobeDsiva
+uid: Davis_Debord
+givenName: Davis
+mail: Davis_Debord@example.com
+carLicense: LDK4N4L
+departmentNumber: 9531
+employeeType: Normal
+homePhone: +1 303 244-8371
+initials: D. D.
+mobile: +1 408 909-7766
+pager: +1 415 378-5985
+manager: cn=Hiroshi Cescon
+secretary: cn=Dutch Musca
+roomNumber: 7850
+
+dn: cn=Selma Tilton, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Selma Tilton
+sn: Tilton
+description: This is Selma Tilton's description
+facsimileTelephoneNumber: +1 415 920-1214
+l: Santa Clara
+ou: Management
+postalAddress: example$Management$Dept # 938
+telephoneNumber: +1 415 792-8542
+title: Senior Management Assistant
+userPassword: notliTamle
+uid: Selma_Tilton
+givenName: Selma
+mail: Selma_Tilton@example.com
+carLicense: FJVVNUV
+departmentNumber: 6062
+employeeType: Normal
+homePhone: +1 510 189-7668
+initials: S. T.
+mobile: +1 415 680-3984
+pager: +1 415 203-7578
+manager: cn=Jeri Kotval
+secretary: cn=Penelope Clenney
+roomNumber: 5030
+
+dn: cn=Annabell Id, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Annabell Id
+sn: Id
+description: This is Annabell Id's description
+facsimileTelephoneNumber: +1 71 508-7128
+l: San Jose
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 148
+telephoneNumber: +1 510 235-2601
+title: Associate Human Resources Manager
+userPassword: dIllebannA
+uid: Annabell_Id
+givenName: Annabell
+mail: Annabell_Id@example.com
+carLicense: YO9DVM1
+departmentNumber: 874
+employeeType: Normal
+homePhone: +1 213 225-6368
+initials: A. I.
+mobile: +1 408 603-7170
+pager: +1 206 892-2838
+manager: cn=Bunny Wealch
+secretary: cn=John-Paul Kumamoto
+roomNumber: 7214
+
+dn: cn=May Rhodenizer, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: May Rhodenizer
+sn: Rhodenizer
+description: This is May Rhodenizer's description
+facsimileTelephoneNumber: +1 408 683-5414
+l: Palo Alto
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 297
+telephoneNumber: +1 71 232-2613
+title: Elite Product Testing Writer
+userPassword: rezinedohR
+uid: May_Rhodenizer
+givenName: May
+mail: May_Rhodenizer@example.com
+carLicense: U2V5OOU
+departmentNumber: 6124
+employeeType: Manager
+homePhone: +1 213 185-3444
+initials: M. R.
+mobile: +1 71 203-1683
+pager: +1 206 557-9126
+manager: cn=Serge Michaels
+secretary: cn=Kerrill Noone
+roomNumber: 9338
+
+dn: cn=Juline Dolezal, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Juline Dolezal
+sn: Dolezal
+description: This is Juline Dolezal's description
+facsimileTelephoneNumber: +1 818 920-7493
+l: Redmond
+ou: Accounting
+postalAddress: example$Accounting$Dept # 423
+telephoneNumber: +1 415 302-8192
+title: Master Accounting Pinhead
+userPassword: lazeloDeni
+uid: Juline_Dolezal
+givenName: Juline
+mail: Juline_Dolezal@example.com
+carLicense: GSVXNVK
+departmentNumber: 6333
+employeeType: Contract
+homePhone: +1 804 551-1568
+initials: J. D.
+mobile: +1 818 745-9995
+pager: +1 415 654-3636
+manager: cn=Delisle Lacelle
+secretary: cn=Godfrey Dikaitis
+roomNumber: 3440
+
+dn: cn=Katrinka Gehm, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Katrinka Gehm
+sn: Gehm
+description: This is Katrinka Gehm's description
+facsimileTelephoneNumber: +1 303 506-6410
+l: San Mateo
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 476
+telephoneNumber: +1 213 166-4215
+title: Supreme Janitorial Accountant
+userPassword: mheGaknirt
+uid: Katrinka_Gehm
+givenName: Katrinka
+mail: Katrinka_Gehm@example.com
+carLicense: C8BJ9L5
+departmentNumber: 4205
+employeeType: Employee
+homePhone: +1 213 721-7282
+initials: K. G.
+mobile: +1 415 203-8518
+pager: +1 303 140-8063
+manager: cn=Breanne Mayer
+secretary: cn=Marja Temp
+roomNumber: 7493
+
+dn: cn=Jill Izbinsky, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jill Izbinsky
+sn: Izbinsky
+description: This is Jill Izbinsky's description
+facsimileTelephoneNumber: +1 804 124-8196
+l: Cupertino
+ou: Peons
+postalAddress: example$Peons$Dept # 949
+telephoneNumber: +1 818 196-3978
+title: Supreme Peons Visionary
+userPassword: yksnibzIll
+uid: Jill_Izbinsky
+givenName: Jill
+mail: Jill_Izbinsky@example.com
+carLicense: FKQUUDE
+departmentNumber: 8916
+employeeType: Normal
+homePhone: +1 206 334-6701
+initials: J. I.
+mobile: +1 415 185-1127
+pager: +1 510 325-5233
+manager: cn=Thia Feith
+secretary: cn=Petrina Nardiello
+roomNumber: 9881
+
+dn: cn=Li Avellaneda, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Li Avellaneda
+sn: Avellaneda
+description: This is Li Avellaneda's description
+facsimileTelephoneNumber: +1 206 807-3164
+l: Cambridge
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 198
+telephoneNumber: +1 303 748-5498
+title: Senior Product Testing Mascot
+userPassword: adenallevA
+uid: Li_Avellaneda
+givenName: Li
+mail: Li_Avellaneda@example.com
+carLicense: OKU2I9B
+departmentNumber: 2635
+employeeType: Employee
+homePhone: +1 213 593-9363
+initials: L. A.
+mobile: +1 510 403-2172
+pager: +1 303 730-2192
+manager: cn=Tara Jone
+secretary: cn=Heloise Kazmierczak
+roomNumber: 5823
+
+dn: cn=Dianemarie Laughridge, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dianemarie Laughridge
+sn: Laughridge
+description: This is Dianemarie Laughridge's description
+facsimileTelephoneNumber: +1 818 504-5158
+l: Sunnyvale
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 816
+telephoneNumber: +1 804 304-8130
+title: Junior Human Resources Sales Rep
+userPassword: egdirhguaL
+uid: Dianemarie_Laughridge
+givenName: Dianemarie
+mail: Dianemarie_Laughridge@example.com
+carLicense: NYKDGE8
+departmentNumber: 1855
+employeeType: Employee
+homePhone: +1 213 616-7746
+initials: D. L.
+mobile: +1 510 674-3046
+pager: +1 213 989-5209
+manager: cn=Buford Eperjesy
+secretary: cn=Trudey Watson
+roomNumber: 5014
+
+dn: cn=Mohammad Astor, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Mohammad Astor
+sn: Astor
+description: This is Mohammad Astor's description
+facsimileTelephoneNumber: +1 818 653-9826
+l: San Mateo
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 824
+telephoneNumber: +1 510 222-2291
+title: Associate Human Resources Manager
+userPassword: rotsAdamma
+uid: Mohammad_Astor
+givenName: Mohammad
+mail: Mohammad_Astor@example.com
+carLicense: N3MF78B
+departmentNumber: 2775
+employeeType: Normal
+homePhone: +1 71 602-3440
+initials: M. A.
+mobile: +1 213 816-6357
+pager: +1 804 460-3565
+manager: cn=Didani Hawthorne
+secretary: cn=Dinah Lilleniit
+roomNumber: 4353
+
+dn: cn=Rudy Mansouri, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rudy Mansouri
+sn: Mansouri
+description: This is Rudy Mansouri's description
+facsimileTelephoneNumber: +1 303 506-6510
+l: Cupertino
+ou: Payroll
+postalAddress: example$Payroll$Dept # 800
+telephoneNumber: +1 408 845-5262
+title: Supreme Payroll Janitor
+userPassword: iruosnaMyd
+uid: Rudy_Mansouri
+givenName: Rudy
+mail: Rudy_Mansouri@example.com
+carLicense: UZQNBHL
+departmentNumber: 9176
+employeeType: Manager
+homePhone: +1 408 617-2198
+initials: R. M.
+mobile: +1 303 165-9565
+pager: +1 804 778-4649
+manager: cn=Audy Bossett
+secretary: cn=Susil Hartsell
+roomNumber: 5926
+
+dn: cn=Gursharan Rangaswami, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gursharan Rangaswami
+sn: Rangaswami
+description: This is Gursharan Rangaswami's description
+facsimileTelephoneNumber: +1 510 992-4238
+l: Fremont
+ou: Payroll
+postalAddress: example$Payroll$Dept # 358
+telephoneNumber: +1 206 648-5076
+title: Master Payroll Accountant
+userPassword: imawsagnaR
+uid: Gursharan_Rangaswami
+givenName: Gursharan
+mail: Gursharan_Rangaswami@example.com
+carLicense: UQQ60LT
+departmentNumber: 5115
+employeeType: Employee
+homePhone: +1 804 505-2501
+initials: G. R.
+mobile: +1 206 748-3690
+pager: +1 415 929-3504
+manager: cn=Santiago Santella
+secretary: cn=Sissy Crowe
+roomNumber: 8604
+
+dn: cn=Jojo Menechian, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jojo Menechian
+sn: Menechian
+description: This is Jojo Menechian's description
+facsimileTelephoneNumber: +1 206 448-9037
+l: Menlo Park
+ou: Accounting
+postalAddress: example$Accounting$Dept # 717
+telephoneNumber: +1 213 578-3384
+title: Senior Accounting President
+userPassword: naihceneMo
+uid: Jojo_Menechian
+givenName: Jojo
+mail: Jojo_Menechian@example.com
+carLicense: D923FG7
+departmentNumber: 8600
+employeeType: Normal
+homePhone: +1 206 468-6675
+initials: J. M.
+mobile: +1 408 154-6743
+pager: +1 415 885-5555
+manager: cn=Kinman Hamlin
+secretary: cn=Siouxie Valko
+roomNumber: 4150
+
+dn: cn=Subhash Petrick, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Subhash Petrick
+sn: Petrick
+description: This is Subhash Petrick's description
+facsimileTelephoneNumber: +1 818 509-7242
+l: Redmond
+ou: Management
+postalAddress: example$Management$Dept # 667
+telephoneNumber: +1 206 187-1104
+title: Chief Management Janitor
+userPassword: kcirtePhsa
+uid: Subhash_Petrick
+givenName: Subhash
+mail: Subhash_Petrick@example.com
+carLicense: BKBKQWP
+departmentNumber: 5296
+employeeType: Temp
+homePhone: +1 415 436-8437
+initials: S. P.
+mobile: +1 804 410-9723
+pager: +1 408 653-1269
+manager: cn=Lian-Hong McMurray
+secretary: cn=Fausto Averett
+roomNumber: 4509
+
+dn: cn=Yoshi Figura, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Yoshi Figura
+sn: Figura
+description: This is Yoshi Figura's description
+facsimileTelephoneNumber: +1 213 632-5838
+l: Orem
+ou: Payroll
+postalAddress: example$Payroll$Dept # 839
+telephoneNumber: +1 71 254-7900
+title: Supreme Payroll Vice President
+userPassword: arugiFihso
+uid: Yoshi_Figura
+givenName: Yoshi
+mail: Yoshi_Figura@example.com
+carLicense: IFG2GJE
+departmentNumber: 6641
+employeeType: Manager
+homePhone: +1 804 629-7643
+initials: Y. F.
+mobile: +1 510 779-5227
+pager: +1 71 858-2055
+manager: cn=Vino Gruszczynski
+secretary: cn=Nj Kyoung
+roomNumber: 5364
+
+dn: cn=Furrukh Efstration, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Furrukh Efstration
+sn: Efstration
+description: This is Furrukh Efstration's description
+facsimileTelephoneNumber: +1 510 172-2272
+l: Emeryville
+ou: Peons
+postalAddress: example$Peons$Dept # 210
+telephoneNumber: +1 510 119-6867
+title: Supreme Peons Director
+userPassword: noitartsfE
+uid: Furrukh_Efstration
+givenName: Furrukh
+mail: Furrukh_Efstration@example.com
+carLicense: BOM8Y8Q
+departmentNumber: 7218
+employeeType: Contract
+homePhone: +1 415 349-6730
+initials: F. E.
+mobile: +1 510 163-8441
+pager: +1 303 582-9024
+manager: cn=Tarrah Gorman
+secretary: cn=Kynthia Luke
+roomNumber: 820
+
+dn: cn=Emelda Cutrufello, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Emelda Cutrufello
+sn: Cutrufello
+description: This is Emelda Cutrufello's description
+facsimileTelephoneNumber: +1 206 288-8759
+l: Santa Clara
+ou: Peons
+postalAddress: example$Peons$Dept # 114
+telephoneNumber: +1 71 914-5291
+title: Master Peons Architect
+userPassword: ollefurtuC
+uid: Emelda_Cutrufello
+givenName: Emelda
+mail: Emelda_Cutrufello@example.com
+carLicense: P28KQCO
+departmentNumber: 148
+employeeType: Normal
+homePhone: +1 206 548-7516
+initials: E. C.
+mobile: +1 408 157-7825
+pager: +1 818 767-2029
+manager: cn=Robina Sudbey
+secretary: cn=Gigi Nettles
+roomNumber: 3117
+
+dn: cn=Mahmut Seagle, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Mahmut Seagle
+sn: Seagle
+description: This is Mahmut Seagle's description
+facsimileTelephoneNumber: +1 510 369-5702
+l: Palo Alto
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 282
+telephoneNumber: +1 213 380-4082
+title: Master Product Testing Developer
+userPassword: elgaeStumh
+uid: Mahmut_Seagle
+givenName: Mahmut
+mail: Mahmut_Seagle@example.com
+carLicense: TPPCCFM
+departmentNumber: 7551
+employeeType: Normal
+homePhone: +1 408 441-8718
+initials: M. S.
+mobile: +1 213 886-5489
+pager: +1 804 586-5495
+manager: cn=Florina Fuqua
+secretary: cn=Gerladina Peckel
+roomNumber: 8669
+
+dn: cn=Witold Mayfield, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Witold Mayfield
+sn: Mayfield
+description: This is Witold Mayfield's description
+facsimileTelephoneNumber: +1 408 951-8728
+l: Redmond
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 334
+telephoneNumber: +1 415 238-4018
+title: Junior Product Testing Engineer
+userPassword: dleifyaMdl
+uid: Witold_Mayfield
+givenName: Witold
+mail: Witold_Mayfield@example.com
+carLicense: 4BBQR74
+departmentNumber: 224
+employeeType: Employee
+homePhone: +1 71 811-1199
+initials: W. M.
+mobile: +1 818 417-5436
+pager: +1 303 928-5168
+manager: cn=Camellia Jalilvand
+secretary: cn=Lelah Hedman
+roomNumber: 7119
+
+dn: cn=Calvin Austin, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Calvin Austin
+sn: Austin
+description: This is Calvin Austin's description
+facsimileTelephoneNumber: +1 804 725-6802
+l: Cambridge
+ou: Planning
+postalAddress: example$Planning$Dept # 794
+telephoneNumber: +1 303 681-3693
+title: Chief Planning Evangelist
+userPassword: nitsuAnivl
+uid: Calvin_Austin
+givenName: Calvin
+mail: Calvin_Austin@example.com
+carLicense: 3AQMUIF
+departmentNumber: 7123
+employeeType: Normal
+homePhone: +1 303 186-9756
+initials: C. A.
+mobile: +1 213 867-7124
+pager: +1 206 334-8708
+manager: cn=Bedford Knecht
+secretary: cn=Dimitrios Blethen
+roomNumber: 8104
+
+dn: cn=Rea Somani, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rea Somani
+sn: Somani
+description: This is Rea Somani's description
+facsimileTelephoneNumber: +1 510 531-2640
+l: Menlo Park
+ou: Peons
+postalAddress: example$Peons$Dept # 40
+telephoneNumber: +1 804 816-1641
+title: Elite Peons Warrior
+userPassword: inamoSaeR
+uid: Rea_Somani
+givenName: Rea
+mail: Rea_Somani@example.com
+carLicense: TJ7SV9H
+departmentNumber: 2243
+employeeType: Normal
+homePhone: +1 818 223-6631
+initials: R. S.
+mobile: +1 303 820-9339
+pager: +1 510 857-4120
+manager: cn=Swee-Joo Thirugnanam
+secretary: cn=Basheer Ballou
+roomNumber: 2461
+
+dn: cn=Zilvia Welch, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Zilvia Welch
+sn: Welch
+description: This is Zilvia Welch's description
+facsimileTelephoneNumber: +1 408 485-5775
+l: Alameda
+ou: Product Development
+postalAddress: example$Product Development$Dept # 683
+telephoneNumber: +1 510 860-2192
+title: Chief Product Development Madonna
+userPassword: hcleWaivli
+uid: Zilvia_Welch
+givenName: Zilvia
+mail: Zilvia_Welch@example.com
+carLicense: 8RSUTQX
+departmentNumber: 7527
+employeeType: Manager
+homePhone: +1 510 946-1265
+initials: Z. W.
+mobile: +1 415 405-2811
+pager: +1 213 804-8281
+manager: cn=Shellie Chappuis
+secretary: cn=Randhir Avellaneda
+roomNumber: 5097
+
+dn: cn=Joshi Foest, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Joshi Foest
+sn: Foest
+description: This is Joshi Foest's description
+facsimileTelephoneNumber: +1 408 177-6240
+l: Menlo Park
+ou: Product Development
+postalAddress: example$Product Development$Dept # 322
+telephoneNumber: +1 818 203-5533
+title: Chief Product Development Accountant
+userPassword: tseoFihsoJ
+uid: Joshi_Foest
+givenName: Joshi
+mail: Joshi_Foest@example.com
+carLicense: HYF1MLE
+departmentNumber: 8340
+employeeType: Temp
+homePhone: +1 206 619-9391
+initials: J. F.
+mobile: +1 213 782-3248
+pager: +1 71 218-2782
+manager: cn=Mack Tilson
+secretary: cn=Amargo Oplinger
+roomNumber: 1210
+
+dn: cn=Elton Prymack, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Elton Prymack
+sn: Prymack
+description: This is Elton Prymack's description
+facsimileTelephoneNumber: +1 818 145-8035
+l: Redwood Shores
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 129
+telephoneNumber: +1 415 698-8490
+title: Junior Human Resources Warrior
+userPassword: kcamyrPnot
+uid: Elton_Prymack
+givenName: Elton
+mail: Elton_Prymack@example.com
+carLicense: RD9AS5V
+departmentNumber: 9654
+employeeType: Temp
+homePhone: +1 415 546-3251
+initials: E. P.
+mobile: +1 804 879-3838
+pager: +1 818 946-4816
+manager: cn=Marissa Collette
+secretary: cn=Seungchul Cantlie
+roomNumber: 8671
+
+dn: cn=Croix Flatley, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Croix Flatley
+sn: Flatley
+description: This is Croix Flatley's description
+facsimileTelephoneNumber: +1 213 892-8922
+l: Mountain View
+ou: Planning
+postalAddress: example$Planning$Dept # 84
+telephoneNumber: +1 408 339-6244
+title: Senior Planning Manager
+userPassword: yeltalFxio
+uid: Croix_Flatley
+givenName: Croix
+mail: Croix_Flatley@example.com
+carLicense: NE73BFS
+departmentNumber: 4959
+employeeType: Temp
+homePhone: +1 206 166-3370
+initials: C. F.
+mobile: +1 818 483-1276
+pager: +1 213 982-6149
+manager: cn=Vishwa Nikfarjam
+secretary: cn=Detlef Clifford
+roomNumber: 5834
+
+dn: cn=Moira Gratton, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Moira Gratton
+sn: Gratton
+description: This is Moira Gratton's description
+facsimileTelephoneNumber: +1 510 618-3622
+l: San Jose
+ou: Administrative
+postalAddress: example$Administrative$Dept # 687
+telephoneNumber: +1 213 323-6709
+title: Junior Administrative Artist
+userPassword: nottarGari
+uid: Moira_Gratton
+givenName: Moira
+mail: Moira_Gratton@example.com
+carLicense: 55V2FPP
+departmentNumber: 8374
+employeeType: Normal
+homePhone: +1 71 909-1127
+initials: M. G.
+mobile: +1 303 447-7682
+pager: +1 303 572-1884
+manager: cn=Robyn Guercioni
+secretary: cn=Turus Fiore
+roomNumber: 4886
+
+dn: cn=Balaji Juni, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Balaji Juni
+sn: Juni
+description: This is Balaji Juni's description
+facsimileTelephoneNumber: +1 804 785-3878
+l: Cambridge
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 109
+telephoneNumber: +1 303 270-8919
+title: Associate Human Resources Admin
+userPassword: inuJijalaB
+uid: Balaji_Juni
+givenName: Balaji
+mail: Balaji_Juni@example.com
+carLicense: BJC0G3I
+departmentNumber: 6640
+employeeType: Temp
+homePhone: +1 303 941-5268
+initials: B. J.
+mobile: +1 213 538-1956
+pager: +1 71 818-4960
+manager: cn=Eydie Wartman
+secretary: cn=Reza Kingston
+roomNumber: 8654
+
+dn: cn=Marlaine Hasan, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marlaine Hasan
+sn: Hasan
+description: This is Marlaine Hasan's description
+facsimileTelephoneNumber: +1 804 374-5175
+l: Redwood Shores
+ou: Management
+postalAddress: example$Management$Dept # 119
+telephoneNumber: +1 804 512-2308
+title: Master Management Punk
+userPassword: nasaHenial
+uid: Marlaine_Hasan
+givenName: Marlaine
+mail: Marlaine_Hasan@example.com
+carLicense: D01S2SI
+departmentNumber: 8147
+employeeType: Contract
+homePhone: +1 818 861-8628
+initials: M. H.
+mobile: +1 818 424-6590
+pager: +1 804 981-4753
+manager: cn=Haig Nagaraj
+secretary: cn=Enid Addona
+roomNumber: 1500
+
+dn: cn=Leonard Stirling, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Leonard Stirling
+sn: Stirling
+description: This is Leonard Stirling's description
+facsimileTelephoneNumber: +1 206 584-9006
+l: Emeryville
+ou: Peons
+postalAddress: example$Peons$Dept # 944
+telephoneNumber: +1 213 970-8571
+title: Junior Peons Visionary
+userPassword: gnilritSdr
+uid: Leonard_Stirling
+givenName: Leonard
+mail: Leonard_Stirling@example.com
+carLicense: 9BV9JCM
+departmentNumber: 6868
+employeeType: Contract
+homePhone: +1 415 337-1577
+initials: L. S.
+mobile: +1 818 378-8522
+pager: +1 818 514-6014
+manager: cn=Charis Jackman
+secretary: cn=Sibeal Wakim
+roomNumber: 6765
+
+dn: cn=Karin Kester, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Karin Kester
+sn: Kester
+description: This is Karin Kester's description
+facsimileTelephoneNumber: +1 71 287-9218
+l: San Francisco
+ou: Management
+postalAddress: example$Management$Dept # 988
+telephoneNumber: +1 510 835-4905
+title: Senior Management Evangelist
+userPassword: retseKnira
+uid: Karin_Kester
+givenName: Karin
+mail: Karin_Kester@example.com
+carLicense: BTHEO1D
+departmentNumber: 1594
+employeeType: Contract
+homePhone: +1 510 254-2955
+initials: K. K.
+mobile: +1 213 569-5127
+pager: +1 408 605-7295
+manager: cn=Elwira Sparksman
+secretary: cn=Susy Kaley
+roomNumber: 2671
+
+dn: cn=Vanny Merciline, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Vanny Merciline
+sn: Merciline
+description: This is Vanny Merciline's description
+facsimileTelephoneNumber: +1 303 808-4673
+l: Fremont
+ou: Product Development
+postalAddress: example$Product Development$Dept # 359
+telephoneNumber: +1 213 495-7834
+title: Junior Product Development Architect
+userPassword: enilicreMy
+uid: Vanny_Merciline
+givenName: Vanny
+mail: Vanny_Merciline@example.com
+carLicense: SHDRP12
+departmentNumber: 6353
+employeeType: Employee
+homePhone: +1 206 538-4173
+initials: V. M.
+mobile: +1 303 401-9774
+pager: +1 303 427-4803
+manager: cn=Vo Saberi
+secretary: cn=Karrie Wenzel
+roomNumber: 1263
+
+dn: cn=Papagena Komenda, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Papagena Komenda
+sn: Komenda
+description: This is Papagena Komenda's description
+facsimileTelephoneNumber: +1 510 321-2378
+l: Emeryville
+ou: Payroll
+postalAddress: example$Payroll$Dept # 826
+telephoneNumber: +1 71 979-9837
+title: Elite Payroll Technician
+userPassword: adnemoKane
+uid: Papagena_Komenda
+givenName: Papagena
+mail: Papagena_Komenda@example.com
+carLicense: X60QQ78
+departmentNumber: 742
+employeeType: Contract
+homePhone: +1 213 195-6047
+initials: P. K.
+mobile: +1 510 285-7873
+pager: +1 415 910-7281
+manager: cn=Zein Ong
+secretary: cn=Rurick Fok
+roomNumber: 6066
+
+dn: cn=Theresa Birkett, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Theresa Birkett
+sn: Birkett
+description: This is Theresa Birkett's description
+facsimileTelephoneNumber: +1 510 525-2392
+l: Cupertino
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 481
+telephoneNumber: +1 71 331-5423
+title: Senior Product Testing Developer
+userPassword: ttekriBase
+uid: Theresa_Birkett
+givenName: Theresa
+mail: Theresa_Birkett@example.com
+carLicense: QAWB46X
+departmentNumber: 7168
+employeeType: Contract
+homePhone: +1 818 189-1014
+initials: T. B.
+mobile: +1 510 764-7739
+pager: +1 818 764-4424
+manager: cn=America Kashani-nia
+secretary: cn=Allister Siperco
+roomNumber: 7044
+
+dn: cn=Andria Suyama, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Andria Suyama
+sn: Suyama
+description: This is Andria Suyama's description
+facsimileTelephoneNumber: +1 206 160-9576
+l: Milpitas
+ou: Management
+postalAddress: example$Management$Dept # 844
+telephoneNumber: +1 818 161-3750
+title: Supreme Management Director
+userPassword: amayuSaird
+uid: Andria_Suyama
+givenName: Andria
+mail: Andria_Suyama@example.com
+carLicense: 38HQLSZ
+departmentNumber: 2625
+employeeType: Manager
+homePhone: +1 71 617-9880
+initials: A. S.
+mobile: +1 71 927-7279
+pager: +1 206 784-9362
+manager: cn=Jacky Capindale
+secretary: cn=Doll Doda
+roomNumber: 3736
+
+dn: cn=Weber Lalu, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Weber Lalu
+sn: Lalu
+description: This is Weber Lalu's description
+facsimileTelephoneNumber: +1 415 332-9905
+l: Alameda
+ou: Management
+postalAddress: example$Management$Dept # 987
+telephoneNumber: +1 510 533-8334
+title: Associate Management Admin
+userPassword: ulaLrebeW
+uid: Weber_Lalu
+givenName: Weber
+mail: Weber_Lalu@example.com
+carLicense: NA6BWB4
+departmentNumber: 6679
+employeeType: Employee
+homePhone: +1 71 993-8133
+initials: W. L.
+mobile: +1 804 707-9054
+pager: +1 510 258-1035
+manager: cn=Annis Rogan
+secretary: cn=Giulietta Douet
+roomNumber: 8548
+
+dn: cn=Marilee Mir, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marilee Mir
+sn: Mir
+description: This is Marilee Mir's description
+facsimileTelephoneNumber: +1 213 593-6952
+l: Sunnyvale
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 463
+telephoneNumber: +1 303 321-6311
+title: Elite Human Resources Yahoo
+userPassword: riMeeliraM
+uid: Marilee_Mir
+givenName: Marilee
+mail: Marilee_Mir@example.com
+carLicense: FIX7BKH
+departmentNumber: 805
+employeeType: Contract
+homePhone: +1 213 920-3744
+initials: M. M.
+mobile: +1 206 275-2272
+pager: +1 818 114-5451
+manager: cn=Wren Csaszar
+secretary: cn=Annarbor Kusyk
+roomNumber: 2052
+
+dn: cn=Marnie Ayoubzadeh, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marnie Ayoubzadeh
+sn: Ayoubzadeh
+description: This is Marnie Ayoubzadeh's description
+facsimileTelephoneNumber: +1 804 558-6542
+l: Menlo Park
+ou: Management
+postalAddress: example$Management$Dept # 246
+telephoneNumber: +1 804 147-4120
+title: Chief Management Czar
+userPassword: hedazbuoyA
+uid: Marnie_Ayoubzadeh
+givenName: Marnie
+mail: Marnie_Ayoubzadeh@example.com
+carLicense: M3TCV65
+departmentNumber: 6689
+employeeType: Normal
+homePhone: +1 213 950-5445
+initials: M. A.
+mobile: +1 408 836-4250
+pager: +1 71 918-4622
+manager: cn=Haley Hassey
+secretary: cn=Erin McNicol
+roomNumber: 1422
+
+dn: cn=Thakor De-Boer, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Thakor De-Boer
+sn: De-Boer
+description: This is Thakor De-Boer's description
+facsimileTelephoneNumber: +1 804 940-8307
+l: San Francisco
+ou: Administrative
+postalAddress: example$Administrative$Dept # 463
+telephoneNumber: +1 804 663-7224
+title: Elite Administrative Janitor
+userPassword: reoB-eDrok
+uid: Thakor_De-Boer
+givenName: Thakor
+mail: Thakor_De-Boer@example.com
+carLicense: 2A05SHH
+departmentNumber: 6011
+employeeType: Normal
+homePhone: +1 71 417-4571
+initials: T. D.
+mobile: +1 303 769-5021
+pager: +1 804 701-8613
+manager: cn=Hester Brogden
+secretary: cn=Winston Cogan
+roomNumber: 3437
+
+dn: cn=Hpone Syed, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Hpone Syed
+sn: Syed
+description: This is Hpone Syed's description
+facsimileTelephoneNumber: +1 510 324-3423
+l: San Jose
+ou: Planning
+postalAddress: example$Planning$Dept # 568
+telephoneNumber: +1 71 960-5888
+title: Supreme Planning Assistant
+userPassword: deySenopH
+uid: Hpone_Syed
+givenName: Hpone
+mail: Hpone_Syed@example.com
+carLicense: IGP7BGY
+departmentNumber: 9648
+employeeType: Manager
+homePhone: +1 303 891-3307
+initials: H. S.
+mobile: +1 303 912-8874
+pager: +1 408 952-9101
+manager: cn=Becca Simkin
+secretary: cn=Fidelity Ervi
+roomNumber: 3880
+
+dn: cn=Leslie Hilbig, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Leslie Hilbig
+sn: Hilbig
+description: This is Leslie Hilbig's description
+facsimileTelephoneNumber: +1 818 365-5871
+l: San Jose
+ou: Accounting
+postalAddress: example$Accounting$Dept # 948
+telephoneNumber: +1 510 963-3120
+title: Junior Accounting Director
+userPassword: gibliHeils
+uid: Leslie_Hilbig
+givenName: Leslie
+mail: Leslie_Hilbig@example.com
+carLicense: J80UH5X
+departmentNumber: 6156
+employeeType: Contract
+homePhone: +1 71 234-3282
+initials: L. H.
+mobile: +1 415 168-2586
+pager: +1 71 686-7093
+manager: cn=Nha Wolski
+secretary: cn=Unreg Miksik
+roomNumber: 2475
+
+dn: cn=Nyssa Priede, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Nyssa Priede
+sn: Priede
+description: This is Nyssa Priede's description
+facsimileTelephoneNumber: +1 408 554-1398
+l: Fremont
+ou: Payroll
+postalAddress: example$Payroll$Dept # 958
+telephoneNumber: +1 804 549-8146
+title: Associate Payroll Janitor
+userPassword: edeirPassy
+uid: Nyssa_Priede
+givenName: Nyssa
+mail: Nyssa_Priede@example.com
+carLicense: 2MFWFQW
+departmentNumber: 3728
+employeeType: Temp
+homePhone: +1 303 396-6569
+initials: N. P.
+mobile: +1 510 832-6542
+pager: +1 415 658-1044
+manager: cn=Caryl Gostanian
+secretary: cn=Seana Dost
+roomNumber: 8299
+
+dn: cn=Eddy Brodowski, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Eddy Brodowski
+sn: Brodowski
+description: This is Eddy Brodowski's description
+facsimileTelephoneNumber: +1 818 179-7369
+l: Alameda
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 683
+telephoneNumber: +1 804 353-2325
+title: Chief Janitorial Assistant
+userPassword: ikswodorBy
+uid: Eddy_Brodowski
+givenName: Eddy
+mail: Eddy_Brodowski@example.com
+carLicense: GAM09AM
+departmentNumber: 2358
+employeeType: Employee
+homePhone: +1 818 416-2826
+initials: E. B.
+mobile: +1 303 373-7443
+pager: +1 408 533-8374
+manager: cn=Lance Ohmayer
+secretary: cn=Joo-Euin Waidler
+roomNumber: 9895
+
+dn: cn=Aaccf Phung, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Aaccf Phung
+sn: Phung
+description: This is Aaccf Phung's description
+facsimileTelephoneNumber: +1 408 578-7033
+l: Cambridge
+ou: Peons
+postalAddress: example$Peons$Dept # 491
+telephoneNumber: +1 510 710-4916
+title: Elite Peons Stooge
+userPassword: gnuhPfccaA
+uid: Aaccf_Phung
+givenName: Aaccf
+mail: Aaccf_Phung@example.com
+carLicense: 1SDWMRY
+departmentNumber: 912
+employeeType: Normal
+homePhone: +1 415 935-3472
+initials: A. P.
+mobile: +1 415 977-6339
+pager: +1 818 332-2001
+manager: cn=Annamarie Scarrow
+secretary: cn=Bess Newland
+roomNumber: 7129
+
+dn: cn=Ginni Fougere, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ginni Fougere
+sn: Fougere
+description: This is Ginni Fougere's description
+facsimileTelephoneNumber: +1 804 985-9211
+l: Redmond
+ou: Peons
+postalAddress: example$Peons$Dept # 361
+telephoneNumber: +1 804 686-6994
+title: Elite Peons President
+userPassword: ereguoFinn
+uid: Ginni_Fougere
+givenName: Ginni
+mail: Ginni_Fougere@example.com
+carLicense: DHV2H5A
+departmentNumber: 6789
+employeeType: Temp
+homePhone: +1 804 764-7821
+initials: G. F.
+mobile: +1 415 478-2421
+pager: +1 804 926-6211
+manager: cn=Marshal Zoppel
+secretary: cn=Fitzgerald Bernardo
+roomNumber: 8674
+
+dn: cn=Kien-Nghiep SVM-BNRMTVA, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kien-Nghiep SVM-BNRMTVA
+sn: SVM-BNRMTVA
+description: This is Kien-Nghiep SVM-BNRMTVA's description
+facsimileTelephoneNumber: +1 213 306-2758
+l: Emeryville
+ou: Accounting
+postalAddress: example$Accounting$Dept # 888
+telephoneNumber: +1 818 163-9758
+title: Senior Accounting President
+userPassword: AVTMRNB-MV
+uid: Kien-Nghiep_SVM-BNRMTVA
+givenName: Kien-Nghiep
+mail: Kien-Nghiep_SVM-BNRMTVA@example.com
+carLicense: 5LRQFJD
+departmentNumber: 5645
+employeeType: Contract
+homePhone: +1 206 674-6551
+initials: K. S.
+mobile: +1 213 667-9460
+pager: +1 804 539-3892
+manager: cn=Focus Freyermuth
+secretary: cn=Siusan Galligan
+roomNumber: 1101
+
+dn: cn=Valli Haney, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Valli Haney
+sn: Haney
+description: This is Valli Haney's description
+facsimileTelephoneNumber: +1 71 483-8348
+l: Cambridge
+ou: Peons
+postalAddress: example$Peons$Dept # 44
+telephoneNumber: +1 804 452-6380
+title: Junior Peons President
+userPassword: yenaHillaV
+uid: Valli_Haney
+givenName: Valli
+mail: Valli_Haney@example.com
+carLicense: WCRC4BV
+departmentNumber: 4252
+employeeType: Temp
+homePhone: +1 510 404-1536
+initials: V. H.
+mobile: +1 206 904-6817
+pager: +1 415 196-3277
+manager: cn=Petunia Colantonio
+secretary: cn=Pippy Gouhara
+roomNumber: 5884
+
+dn: cn=Gabbey Vieger, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gabbey Vieger
+sn: Vieger
+description: This is Gabbey Vieger's description
+facsimileTelephoneNumber: +1 303 499-1918
+l: Redwood Shores
+ou: Management
+postalAddress: example$Management$Dept # 569
+telephoneNumber: +1 415 845-4066
+title: Chief Management Technician
+userPassword: regeiVyebb
+uid: Gabbey_Vieger
+givenName: Gabbey
+mail: Gabbey_Vieger@example.com
+carLicense: Z6PRY74
+departmentNumber: 4878
+employeeType: Manager
+homePhone: +1 510 716-2017
+initials: G. V.
+mobile: +1 303 557-1453
+pager: +1 408 932-5598
+manager: cn=Teresina Banfalvi
+secretary: cn=Katharyn Fluet
+roomNumber: 5955
+
+dn: cn=Zero Kannemann, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Zero Kannemann
+sn: Kannemann
+description: This is Zero Kannemann's description
+facsimileTelephoneNumber: +1 213 777-1612
+l: Palo Alto
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 149
+telephoneNumber: +1 71 463-2081
+title: Chief Human Resources Assistant
+userPassword: nnamennaKo
+uid: Zero_Kannemann
+givenName: Zero
+mail: Zero_Kannemann@example.com
+carLicense: JBUT5UW
+departmentNumber: 5783
+employeeType: Employee
+homePhone: +1 213 857-8985
+initials: Z. K.
+mobile: +1 510 913-8725
+pager: +1 408 538-5125
+manager: cn=Lidio Halpenny
+secretary: cn=Wai-Leung Lanoue
+roomNumber: 582
+
+dn: cn=Zhengyu Shull, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Zhengyu Shull
+sn: Shull
+description: This is Zhengyu Shull's description
+facsimileTelephoneNumber: +1 415 765-3239
+l: Armonk
+ou: Peons
+postalAddress: example$Peons$Dept # 625
+telephoneNumber: +1 206 257-9486
+title: Associate Peons Fellow
+userPassword: lluhSuygne
+uid: Zhengyu_Shull
+givenName: Zhengyu
+mail: Zhengyu_Shull@example.com
+carLicense: WI63BDD
+departmentNumber: 2157
+employeeType: Manager
+homePhone: +1 408 676-2349
+initials: Z. S.
+mobile: +1 804 695-2705
+pager: +1 71 666-7092
+manager: cn=Dyke Ozer
+secretary: cn=Myriam Johnsen
+roomNumber: 29
+
+dn: cn=Kikelia Voight, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kikelia Voight
+sn: Voight
+description: This is Kikelia Voight's description
+facsimileTelephoneNumber: +1 303 739-7104
+l: Sunnyvale
+ou: Planning
+postalAddress: example$Planning$Dept # 837
+telephoneNumber: +1 804 439-7733
+title: Elite Planning Grunt
+userPassword: thgioVaile
+uid: Kikelia_Voight
+givenName: Kikelia
+mail: Kikelia_Voight@example.com
+carLicense: C23U2CJ
+departmentNumber: 3322
+employeeType: Normal
+homePhone: +1 213 469-6364
+initials: K. V.
+mobile: +1 415 698-8309
+pager: +1 303 566-3751
+manager: cn=Marybelle McCaugherty
+secretary: cn=Carlotta Tieu
+roomNumber: 852
+
+dn: cn=Rama Ploof, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rama Ploof
+sn: Ploof
+description: This is Rama Ploof's description
+facsimileTelephoneNumber: +1 206 540-1575
+l: Mountain View
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 329
+telephoneNumber: +1 510 102-9947
+title: Associate Janitorial Sales Rep
+userPassword: foolPamaR
+uid: Rama_Ploof
+givenName: Rama
+mail: Rama_Ploof@example.com
+carLicense: BIZED1P
+departmentNumber: 7650
+employeeType: Employee
+homePhone: +1 818 723-5409
+initials: R. P.
+mobile: +1 206 487-7886
+pager: +1 71 625-1587
+manager: cn=Ginelle Iantaffi
+secretary: cn=Tallie Marcotte
+roomNumber: 2931
+
+dn: cn=Shaker Marengere, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Shaker Marengere
+sn: Marengere
+description: This is Shaker Marengere's description
+facsimileTelephoneNumber: +1 408 123-7329
+l: Fremont
+ou: Payroll
+postalAddress: example$Payroll$Dept # 423
+telephoneNumber: +1 510 602-9780
+title: Elite Payroll Pinhead
+userPassword: eregneraMr
+uid: Shaker_Marengere
+givenName: Shaker
+mail: Shaker_Marengere@example.com
+carLicense: 38LRCSX
+departmentNumber: 3078
+employeeType: Manager
+homePhone: +1 510 299-9437
+initials: S. M.
+mobile: +1 415 190-2235
+pager: +1 303 401-2705
+manager: cn=Franz Gobeli
+secretary: cn=Tyronda Saward
+roomNumber: 737
+
+dn: cn=Rebecca Albea, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rebecca Albea
+sn: Albea
+description: This is Rebecca Albea's description
+facsimileTelephoneNumber: +1 303 637-1044
+l: Menlo Park
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 192
+telephoneNumber: +1 415 884-6035
+title: Junior Human Resources Madonna
+userPassword: aeblAacceb
+uid: Rebecca_Albea
+givenName: Rebecca
+mail: Rebecca_Albea@example.com
+carLicense: OWI2NJK
+departmentNumber: 1269
+employeeType: Normal
+homePhone: +1 804 739-3562
+initials: R. A.
+mobile: +1 206 745-7709
+pager: +1 71 449-9590
+manager: cn=Hailee Theis
+secretary: cn=Sydel Kirkby
+roomNumber: 8861
+
+dn: cn=Linnie Caruth, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Linnie Caruth
+sn: Caruth
+description: This is Linnie Caruth's description
+facsimileTelephoneNumber: +1 804 932-5140
+l: Santa Clara
+ou: Product Development
+postalAddress: example$Product Development$Dept # 234
+telephoneNumber: +1 213 568-5430
+title: Chief Product Development Sales Rep
+userPassword: hturaCeinn
+uid: Linnie_Caruth
+givenName: Linnie
+mail: Linnie_Caruth@example.com
+carLicense: GEW2B1F
+departmentNumber: 5657
+employeeType: Manager
+homePhone: +1 71 982-4988
+initials: L. C.
+mobile: +1 303 434-7153
+pager: +1 206 223-9585
+manager: cn=Kylynn Kuzbary
+secretary: cn=Cyrine Malhi
+roomNumber: 9028
+
+dn: cn=Kat Golaszewski, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kat Golaszewski
+sn: Golaszewski
+description: This is Kat Golaszewski's description
+facsimileTelephoneNumber: +1 206 786-8217
+l: Palo Alto
+ou: Product Development
+postalAddress: example$Product Development$Dept # 697
+telephoneNumber: +1 213 542-2548
+title: Associate Product Development Director
+userPassword: ikswezsalo
+uid: Kat_Golaszewski
+givenName: Kat
+mail: Kat_Golaszewski@example.com
+carLicense: R59IY87
+departmentNumber: 6186
+employeeType: Manager
+homePhone: +1 213 241-1609
+initials: K. G.
+mobile: +1 213 663-6489
+pager: +1 415 405-6871
+manager: cn=Lavinia Melfi
+secretary: cn=Joella Adam
+roomNumber: 700
+
+dn: cn=Inga Graessley, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Inga Graessley
+sn: Graessley
+description: This is Inga Graessley's description
+facsimileTelephoneNumber: +1 804 377-1997
+l: San Jose
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 194
+telephoneNumber: +1 415 457-6662
+title: Master Janitorial Director
+userPassword: yelssearGa
+uid: Inga_Graessley
+givenName: Inga
+mail: Inga_Graessley@example.com
+carLicense: BF65T92
+departmentNumber: 6810
+employeeType: Temp
+homePhone: +1 818 971-7748
+initials: I. G.
+mobile: +1 71 452-3308
+pager: +1 71 946-3546
+manager: cn=Orelie Hardage
+secretary: cn=Candie Watkinson
+roomNumber: 5459
+
+dn: cn=Dominica Rohal, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dominica Rohal
+sn: Rohal
+description: This is Dominica Rohal's description
+facsimileTelephoneNumber: +1 206 802-8249
+l: Mountain View
+ou: Accounting
+postalAddress: example$Accounting$Dept # 981
+telephoneNumber: +1 213 812-3517
+title: Supreme Accounting Manager
+userPassword: lahoRacini
+uid: Dominica_Rohal
+givenName: Dominica
+mail: Dominica_Rohal@example.com
+carLicense: W4KWOTP
+departmentNumber: 3781
+employeeType: Temp
+homePhone: +1 510 164-7017
+initials: D. R.
+mobile: +1 213 337-6509
+pager: +1 213 877-6481
+manager: cn=Ricca Woodley
+secretary: cn=Concordia Bedoya
+roomNumber: 6507
+
+dn: cn=Meghann Loza, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Meghann Loza
+sn: Loza
+description: This is Meghann Loza's description
+facsimileTelephoneNumber: +1 804 271-2532
+l: Santa Clara
+ou: Peons
+postalAddress: example$Peons$Dept # 87
+telephoneNumber: +1 408 804-6965
+title: Chief Peons Dictator
+userPassword: azoLnnahge
+uid: Meghann_Loza
+givenName: Meghann
+mail: Meghann_Loza@example.com
+carLicense: 6Z3292L
+departmentNumber: 7735
+employeeType: Manager
+homePhone: +1 206 361-8241
+initials: M. L.
+mobile: +1 804 398-7507
+pager: +1 804 509-3165
+manager: cn=Andras Partin
+secretary: cn=Izumi Loo
+roomNumber: 4525
+
+dn: cn=Bellanca Struzynski, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Bellanca Struzynski
+sn: Struzynski
+description: This is Bellanca Struzynski's description
+facsimileTelephoneNumber: +1 415 816-3119
+l: Emeryville
+ou: Planning
+postalAddress: example$Planning$Dept # 298
+telephoneNumber: +1 804 649-5046
+title: Associate Planning Manager
+userPassword: iksnyzurtS
+uid: Bellanca_Struzynski
+givenName: Bellanca
+mail: Bellanca_Struzynski@example.com
+carLicense: WMFGWYT
+departmentNumber: 7130
+employeeType: Manager
+homePhone: +1 213 200-9388
+initials: B. S.
+mobile: +1 415 604-8909
+pager: +1 303 834-9548
+manager: cn=Tru-Fu Uberig
+secretary: cn=Annabella Derbyshire
+roomNumber: 2343
+
+dn: cn=Agenia Kolesnik, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Agenia Kolesnik
+sn: Kolesnik
+description: This is Agenia Kolesnik's description
+facsimileTelephoneNumber: +1 206 413-2686
+l: Sunnyvale
+ou: Planning
+postalAddress: example$Planning$Dept # 879
+telephoneNumber: +1 408 352-7329
+title: Master Planning Artist
+userPassword: kinseloKai
+uid: Agenia_Kolesnik
+givenName: Agenia
+mail: Agenia_Kolesnik@example.com
+carLicense: BAY0YS9
+departmentNumber: 1898
+employeeType: Temp
+homePhone: +1 213 366-2657
+initials: A. K.
+mobile: +1 303 156-2098
+pager: +1 408 898-8410
+manager: cn=Zuzana Papageorges
+secretary: cn=Stephannie Ciccarelli
+roomNumber: 1627
+
+dn: cn=Reiko Ketley, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Reiko Ketley
+sn: Ketley
+description: This is Reiko Ketley's description
+facsimileTelephoneNumber: +1 804 650-6826
+l: Cupertino
+ou: Management
+postalAddress: example$Management$Dept # 475
+telephoneNumber: +1 71 858-6517
+title: Associate Management Punk
+userPassword: yelteKokie
+uid: Reiko_Ketley
+givenName: Reiko
+mail: Reiko_Ketley@example.com
+carLicense: BND7WEG
+departmentNumber: 7303
+employeeType: Temp
+homePhone: +1 303 926-8826
+initials: R. K.
+mobile: +1 71 708-8679
+pager: +1 303 389-5913
+manager: cn=Powell Ramseyer
+secretary: cn=Genna Antinucci
+roomNumber: 7991
+
+dn: cn=Arvin Blaauw, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Arvin Blaauw
+sn: Blaauw
+description: This is Arvin Blaauw's description
+facsimileTelephoneNumber: +1 213 297-4759
+l: Redwood Shores
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 488
+telephoneNumber: +1 303 874-9708
+title: Associate Janitorial Grunt
+userPassword: wuaalBnivr
+uid: Arvin_Blaauw
+givenName: Arvin
+mail: Arvin_Blaauw@example.com
+carLicense: 14ZBO89
+departmentNumber: 79
+employeeType: Temp
+homePhone: +1 510 946-7008
+initials: A. B.
+mobile: +1 71 503-1695
+pager: +1 206 650-1475
+manager: cn=Bobbie Magee
+secretary: cn=Evaleen Lehtinen
+roomNumber: 9404
+
+dn: cn=Chocs Puddington, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Chocs Puddington
+sn: Puddington
+description: This is Chocs Puddington's description
+facsimileTelephoneNumber: +1 804 923-8885
+l: Milpitas
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 92
+telephoneNumber: +1 415 497-1230
+title: Elite Human Resources Madonna
+userPassword: notgnidduP
+uid: Chocs_Puddington
+givenName: Chocs
+mail: Chocs_Puddington@example.com
+carLicense: 0UNFORR
+departmentNumber: 3756
+employeeType: Temp
+homePhone: +1 510 728-8035
+initials: C. P.
+mobile: +1 408 415-7132
+pager: +1 408 510-4236
+manager: cn=Austin Scss
+secretary: cn=Celinka Barnhill
+roomNumber: 9156
+
+dn: cn=Tiffany Kromer, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tiffany Kromer
+sn: Kromer
+description: This is Tiffany Kromer's description
+facsimileTelephoneNumber: +1 206 440-6347
+l: Redwood Shores
+ou: Product Development
+postalAddress: example$Product Development$Dept # 942
+telephoneNumber: +1 818 265-2550
+title: Senior Product Development Madonna
+userPassword: remorKynaf
+uid: Tiffany_Kromer
+givenName: Tiffany
+mail: Tiffany_Kromer@example.com
+carLicense: VPX7TTC
+departmentNumber: 557
+employeeType: Employee
+homePhone: +1 818 697-6303
+initials: T. K.
+mobile: +1 303 290-2555
+pager: +1 818 413-4806
+manager: cn=Sacto Finane
+secretary: cn=Dulcine McNerney
+roomNumber: 2503
+
+dn: cn=Lashonda Yanosik, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lashonda Yanosik
+sn: Yanosik
+description: This is Lashonda Yanosik's description
+facsimileTelephoneNumber: +1 804 608-5137
+l: Santa Clara
+ou: Payroll
+postalAddress: example$Payroll$Dept # 224
+telephoneNumber: +1 804 836-4936
+title: Elite Payroll Writer
+userPassword: kisonaYadn
+uid: Lashonda_Yanosik
+givenName: Lashonda
+mail: Lashonda_Yanosik@example.com
+carLicense: 8350DHV
+departmentNumber: 5724
+employeeType: Contract
+homePhone: +1 303 773-4106
+initials: L. Y.
+mobile: +1 804 760-2949
+pager: +1 206 180-6258
+manager: cn=Julita Oaks
+secretary: cn=Lonna Frodsham
+roomNumber: 4559
+
+dn: cn=Carri Scribner, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Carri Scribner
+sn: Scribner
+description: This is Carri Scribner's description
+facsimileTelephoneNumber: +1 415 802-8287
+l: Orem
+ou: Planning
+postalAddress: example$Planning$Dept # 690
+telephoneNumber: +1 415 287-5089
+title: Associate Planning Architect
+userPassword: renbircSir
+uid: Carri_Scribner
+givenName: Carri
+mail: Carri_Scribner@example.com
+carLicense: 5R3S5VE
+departmentNumber: 806
+employeeType: Employee
+homePhone: +1 206 444-3212
+initials: C. S.
+mobile: +1 408 672-4072
+pager: +1 818 239-3450
+manager: cn=Fitzroy Willis
+secretary: cn=Fernando Merrill
+roomNumber: 5624
+
+dn: cn=Irita Bartkowska, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Irita Bartkowska
+sn: Bartkowska
+description: This is Irita Bartkowska's description
+facsimileTelephoneNumber: +1 71 733-5083
+l: Emeryville
+ou: Peons
+postalAddress: example$Peons$Dept # 560
+telephoneNumber: +1 804 837-6263
+title: Supreme Peons Admin
+userPassword: akswoktraB
+uid: Irita_Bartkowska
+givenName: Irita
+mail: Irita_Bartkowska@example.com
+carLicense: ZELGQB7
+departmentNumber: 4439
+employeeType: Manager
+homePhone: +1 206 682-1413
+initials: I. B.
+mobile: +1 303 312-3304
+pager: +1 408 470-4943
+manager: cn=Laureen Swinkels
+secretary: cn=Anneliese Macoosh
+roomNumber: 3008
+
+dn: cn=Reynold Patchsqa, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Reynold Patchsqa
+sn: Patchsqa
+description: This is Reynold Patchsqa's description
+facsimileTelephoneNumber: +1 818 698-9238
+l: Orem
+ou: Administrative
+postalAddress: example$Administrative$Dept # 992
+telephoneNumber: +1 408 208-5521
+title: Associate Administrative Figurehead
+userPassword: aqshctaPdl
+uid: Reynold_Patchsqa
+givenName: Reynold
+mail: Reynold_Patchsqa@example.com
+carLicense: WIW9S5F
+departmentNumber: 4436
+employeeType: Manager
+homePhone: +1 510 909-6551
+initials: R. P.
+mobile: +1 408 577-2206
+pager: +1 213 478-8025
+manager: cn=Adie Voight
+secretary: cn=Fiore Harapiak
+roomNumber: 1042
+
+dn: cn=Rosalynd Szkarlat, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rosalynd Szkarlat
+sn: Szkarlat
+description: This is Rosalynd Szkarlat's description
+facsimileTelephoneNumber: +1 804 363-1089
+l: Santa Clara
+ou: Payroll
+postalAddress: example$Payroll$Dept # 49
+telephoneNumber: +1 510 527-9095
+title: Junior Payroll Evangelist
+userPassword: talrakzSdn
+uid: Rosalynd_Szkarlat
+givenName: Rosalynd
+mail: Rosalynd_Szkarlat@example.com
+carLicense: IY7212S
+departmentNumber: 5064
+employeeType: Temp
+homePhone: +1 71 786-1102
+initials: R. S.
+mobile: +1 818 168-3571
+pager: +1 408 130-7493
+manager: cn=Caterina Bizga
+secretary: cn=Beryl Vernon
+roomNumber: 7593
+
+dn: cn=Theodor Polulack, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Theodor Polulack
+sn: Polulack
+description: This is Theodor Polulack's description
+facsimileTelephoneNumber: +1 415 220-6236
+l: Milpitas
+ou: Administrative
+postalAddress: example$Administrative$Dept # 476
+telephoneNumber: +1 510 304-4145
+title: Master Administrative Manager
+userPassword: kcaluloPro
+uid: Theodor_Polulack
+givenName: Theodor
+mail: Theodor_Polulack@example.com
+carLicense: NJRGXQF
+departmentNumber: 6276
+employeeType: Manager
+homePhone: +1 206 881-7728
+initials: T. P.
+mobile: +1 213 546-4224
+pager: +1 206 723-6787
+manager: cn=Hesham Yearwood
+secretary: cn=Marcus Zetts
+roomNumber: 6806
+
+dn: cn=Patricia Demarest, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Patricia Demarest
+sn: Demarest
+description: This is Patricia Demarest's description
+facsimileTelephoneNumber: +1 206 711-5239
+l: Orem
+ou: Payroll
+postalAddress: example$Payroll$Dept # 164
+telephoneNumber: +1 510 918-6995
+title: Supreme Payroll Punk
+userPassword: tserameDai
+uid: Patricia_Demarest
+givenName: Patricia
+mail: Patricia_Demarest@example.com
+carLicense: CS0ECR3
+departmentNumber: 1122
+employeeType: Manager
+homePhone: +1 206 597-6824
+initials: P. D.
+mobile: +1 510 954-3686
+pager: +1 415 884-8219
+manager: cn=Makam Strayhorn
+secretary: cn=Jayendra Abi-Aad
+roomNumber: 5479
+
+dn: cn=Reagan Harrell, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Reagan Harrell
+sn: Harrell
+description: This is Reagan Harrell's description
+facsimileTelephoneNumber: +1 818 165-2427
+l: San Mateo
+ou: Payroll
+postalAddress: example$Payroll$Dept # 199
+telephoneNumber: +1 818 475-4591
+title: Master Payroll Warrior
+userPassword: llerraHnag
+uid: Reagan_Harrell
+givenName: Reagan
+mail: Reagan_Harrell@example.com
+carLicense: TPNKX1P
+departmentNumber: 3064
+employeeType: Employee
+homePhone: +1 818 613-2969
+initials: R. H.
+mobile: +1 303 622-4198
+pager: +1 206 800-9491
+manager: cn=Susy Beauvais
+secretary: cn=Merlin Comm
+roomNumber: 4109
+
+dn: cn=Famke Bugajski, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Famke Bugajski
+sn: Bugajski
+description: This is Famke Bugajski's description
+facsimileTelephoneNumber: +1 213 737-4703
+l: Armonk
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 935
+telephoneNumber: +1 206 275-2059
+title: Master Janitorial Fellow
+userPassword: iksjaguBek
+uid: Famke_Bugajski
+givenName: Famke
+mail: Famke_Bugajski@example.com
+carLicense: I3CTYL6
+departmentNumber: 1653
+employeeType: Manager
+homePhone: +1 804 701-8927
+initials: F. B.
+mobile: +1 206 853-1058
+pager: +1 510 275-9290
+manager: cn=Raju Preville
+secretary: cn=Arlen Sanks
+roomNumber: 6023
+
+dn: cn=Wenonah Dhir, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Wenonah Dhir
+sn: Dhir
+description: This is Wenonah Dhir's description
+facsimileTelephoneNumber: +1 206 919-5991
+l: San Francisco
+ou: Product Development
+postalAddress: example$Product Development$Dept # 577
+telephoneNumber: +1 71 425-6513
+title: Associate Product Development Dictator
+userPassword: rihDhanone
+uid: Wenonah_Dhir
+givenName: Wenonah
+mail: Wenonah_Dhir@example.com
+carLicense: 4ABVEJ0
+departmentNumber: 1009
+employeeType: Employee
+homePhone: +1 510 821-1590
+initials: W. D.
+mobile: +1 818 621-5714
+pager: +1 415 696-2798
+manager: cn=Manjit Torrell
+secretary: cn=Melicent Shultz
+roomNumber: 775
+
+dn: cn=Marjie Hyjek, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marjie Hyjek
+sn: Hyjek
+description: This is Marjie Hyjek's description
+facsimileTelephoneNumber: +1 818 770-4649
+l: Armonk
+ou: Payroll
+postalAddress: example$Payroll$Dept # 296
+telephoneNumber: +1 510 296-5543
+title: Supreme Payroll Mascot
+userPassword: kejyHeijra
+uid: Marjie_Hyjek
+givenName: Marjie
+mail: Marjie_Hyjek@example.com
+carLicense: UHATS76
+departmentNumber: 1195
+employeeType: Normal
+homePhone: +1 71 132-6676
+initials: M. H.
+mobile: +1 206 928-9063
+pager: +1 510 249-9787
+manager: cn=Andrei Hadirahardjo
+secretary: cn=Mougy Helgeland
+roomNumber: 970
+
+dn: cn=Wynnie Loader, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Wynnie Loader
+sn: Loader
+description: This is Wynnie Loader's description
+facsimileTelephoneNumber: +1 415 889-4966
+l: Palo Alto
+ou: Peons
+postalAddress: example$Peons$Dept # 357
+telephoneNumber: +1 415 371-3305
+title: Associate Peons Janitor
+userPassword: redaoLeinn
+uid: Wynnie_Loader
+givenName: Wynnie
+mail: Wynnie_Loader@example.com
+carLicense: HKWL0D5
+departmentNumber: 8295
+employeeType: Manager
+homePhone: +1 71 383-2582
+initials: W. L.
+mobile: +1 415 673-9559
+pager: +1 804 655-8767
+manager: cn=Marcela Wans
+secretary: cn=Kassey Wojdylo
+roomNumber: 6057
+
+dn: cn=Leilah Bridenstine, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Leilah Bridenstine
+sn: Bridenstine
+description: This is Leilah Bridenstine's description
+facsimileTelephoneNumber: +1 415 527-7283
+l: San Jose
+ou: Administrative
+postalAddress: example$Administrative$Dept # 590
+telephoneNumber: +1 818 835-4663
+title: Chief Administrative Stooge
+userPassword: enitsnedir
+uid: Leilah_Bridenstine
+givenName: Leilah
+mail: Leilah_Bridenstine@example.com
+carLicense: EBIG6UV
+departmentNumber: 3567
+employeeType: Employee
+homePhone: +1 804 749-7634
+initials: L. B.
+mobile: +1 818 252-9362
+pager: +1 213 727-6760
+manager: cn=Nicoli Ginest
+secretary: cn=Kelcey Kolappa
+roomNumber: 1920
+
+dn: cn=Kinman Meubus, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kinman Meubus
+sn: Meubus
+description: This is Kinman Meubus's description
+facsimileTelephoneNumber: +1 303 466-1091
+l: Cupertino
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 220
+telephoneNumber: +1 510 374-1473
+title: Master Product Testing Visionary
+userPassword: subueMnamn
+uid: Kinman_Meubus
+givenName: Kinman
+mail: Kinman_Meubus@example.com
+carLicense: 1XLX2DR
+departmentNumber: 4070
+employeeType: Manager
+homePhone: +1 303 971-5309
+initials: K. M.
+mobile: +1 510 182-6106
+pager: +1 408 946-6399
+manager: cn=Bethena Brasington
+secretary: cn=Rebekah Dickeson
+roomNumber: 8782
+
+dn: cn=Melissa Krikorian, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Melissa Krikorian
+sn: Krikorian
+description: This is Melissa Krikorian's description
+facsimileTelephoneNumber: +1 415 593-7969
+l: Emeryville
+ou: Planning
+postalAddress: example$Planning$Dept # 433
+telephoneNumber: +1 71 456-8371
+title: Senior Planning Fellow
+userPassword: nairokirKa
+uid: Melissa_Krikorian
+givenName: Melissa
+mail: Melissa_Krikorian@example.com
+carLicense: PS03ZCC
+departmentNumber: 1223
+employeeType: Temp
+homePhone: +1 206 449-4391
+initials: M. K.
+mobile: +1 408 821-2471
+pager: +1 415 545-2392
+manager: cn=Nguyen Mansouri
+secretary: cn=Vilma Lengel
+roomNumber: 5954
+
+dn: cn=Shay Ganguly, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Shay Ganguly
+sn: Ganguly
+description: This is Shay Ganguly's description
+facsimileTelephoneNumber: +1 818 576-2828
+l: Cambridge
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 452
+telephoneNumber: +1 510 802-9087
+title: Junior Product Testing Visionary
+userPassword: ylugnaGyah
+uid: Shay_Ganguly
+givenName: Shay
+mail: Shay_Ganguly@example.com
+carLicense: AXIBIRS
+departmentNumber: 9430
+employeeType: Normal
+homePhone: +1 415 417-9977
+initials: S. G.
+mobile: +1 415 737-5161
+pager: +1 818 408-4054
+manager: cn=Elliot Komatsu
+secretary: cn=Waneta Henderson
+roomNumber: 5102
+
+dn: cn=Lavonda Alles, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lavonda Alles
+sn: Alles
+description: This is Lavonda Alles's description
+facsimileTelephoneNumber: +1 408 766-4928
+l: Palo Alto
+ou: Management
+postalAddress: example$Management$Dept # 134
+telephoneNumber: +1 415 751-3190
+title: Master Management Visionary
+userPassword: sellAadnov
+uid: Lavonda_Alles
+givenName: Lavonda
+mail: Lavonda_Alles@example.com
+carLicense: LX81N4Y
+departmentNumber: 1269
+employeeType: Contract
+homePhone: +1 213 438-3842
+initials: L. A.
+mobile: +1 510 905-1104
+pager: +1 303 558-8324
+manager: cn=Jacek Das
+secretary: cn=Moreen Essery
+roomNumber: 3962
+
+dn: cn=Darlene Bigley, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Darlene Bigley
+sn: Bigley
+description: This is Darlene Bigley's description
+facsimileTelephoneNumber: +1 213 776-6233
+l: Santa Clara
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 424
+telephoneNumber: +1 804 132-4123
+title: Chief Janitorial Mascot
+userPassword: yelgiBenel
+uid: Darlene_Bigley
+givenName: Darlene
+mail: Darlene_Bigley@example.com
+carLicense: W94W8GP
+departmentNumber: 4108
+employeeType: Contract
+homePhone: +1 408 668-9662
+initials: D. B.
+mobile: +1 415 434-3474
+pager: +1 415 537-2839
+manager: cn=Robin Credille
+secretary: cn=Alejandra Wayling
+roomNumber: 2383
+
+dn: cn=Bruno Zunuzi, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Bruno Zunuzi
+sn: Zunuzi
+description: This is Bruno Zunuzi's description
+facsimileTelephoneNumber: +1 206 379-8074
+l: Redwood Shores
+ou: Administrative
+postalAddress: example$Administrative$Dept # 730
+telephoneNumber: +1 510 678-5567
+title: Elite Administrative Evangelist
+userPassword: izunuZonur
+uid: Bruno_Zunuzi
+givenName: Bruno
+mail: Bruno_Zunuzi@example.com
+carLicense: O0CIWCP
+departmentNumber: 5979
+employeeType: Normal
+homePhone: +1 71 818-6549
+initials: B. Z.
+mobile: +1 818 363-2572
+pager: +1 804 663-4075
+manager: cn=Alica Beaudette
+secretary: cn=Katherina Labrinos
+roomNumber: 1575
+
+dn: cn=Beana Hurteau, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Beana Hurteau
+sn: Hurteau
+description: This is Beana Hurteau's description
+facsimileTelephoneNumber: +1 303 643-8202
+l: Menlo Park
+ou: Accounting
+postalAddress: example$Accounting$Dept # 65
+telephoneNumber: +1 71 348-7436
+title: Associate Accounting Manager
+userPassword: uaetruHana
+uid: Beana_Hurteau
+givenName: Beana
+mail: Beana_Hurteau@example.com
+carLicense: MSNQDP0
+departmentNumber: 8713
+employeeType: Normal
+homePhone: +1 206 896-5863
+initials: B. H.
+mobile: +1 415 832-3335
+pager: +1 303 999-9538
+manager: cn=Dicky Kalyani
+secretary: cn=Jessamyn Verrilli
+roomNumber: 921
+
+dn: cn=Zsa zsa Arnone, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Zsa zsa Arnone
+sn: Arnone
+description: This is Zsa zsa Arnone's description
+facsimileTelephoneNumber: +1 408 524-8595
+l: Orem
+ou: Administrative
+postalAddress: example$Administrative$Dept # 885
+telephoneNumber: +1 415 818-7186
+title: Master Administrative Punk
+userPassword: enonrAasza
+uid: Zsa zsa_Arnone
+givenName: Zsa zsa
+mail: Zsa zsa_Arnone@example.com
+carLicense: JA1PTPS
+departmentNumber: 8385
+employeeType: Contract
+homePhone: +1 818 417-9630
+initials: Z. A.
+mobile: +1 510 982-3941
+pager: +1 510 245-5695
+manager: cn=Huy Mathias
+secretary: cn=Georgiana Hallenbeck
+roomNumber: 3460
+
+dn: cn=Svr Pitts, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Svr Pitts
+sn: Pitts
+description: This is Svr Pitts's description
+facsimileTelephoneNumber: +1 415 941-7757
+l: Orem
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 105
+telephoneNumber: +1 213 930-8296
+title: Elite Human Resources Admin
+userPassword: sttiPrvS
+uid: Svr_Pitts
+givenName: Svr
+mail: Svr_Pitts@example.com
+carLicense: RQVESWE
+departmentNumber: 236
+employeeType: Manager
+homePhone: +1 408 933-2797
+initials: S. P.
+mobile: +1 818 998-2127
+pager: +1 303 802-3242
+manager: cn=Susie Serre
+secretary: cn=Stergios Nuetzi
+roomNumber: 3875
+
+dn: cn=Pooh Somers, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Pooh Somers
+sn: Somers
+description: This is Pooh Somers's description
+facsimileTelephoneNumber: +1 206 313-8132
+l: San Francisco
+ou: Payroll
+postalAddress: example$Payroll$Dept # 743
+telephoneNumber: +1 818 187-7558
+title: Associate Payroll Warrior
+userPassword: sremoShooP
+uid: Pooh_Somers
+givenName: Pooh
+mail: Pooh_Somers@example.com
+carLicense: 65VVW12
+departmentNumber: 1202
+employeeType: Temp
+homePhone: +1 510 765-9719
+initials: P. S.
+mobile: +1 804 731-3541
+pager: +1 818 460-8590
+manager: cn=Eran Scates
+secretary: cn=Dede Gilstorf
+roomNumber: 1773
+
+dn: cn=Pui-Wah McHale, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Pui-Wah McHale
+sn: McHale
+description: This is Pui-Wah McHale's description
+facsimileTelephoneNumber: +1 303 673-8234
+l: Sunnyvale
+ou: Management
+postalAddress: example$Management$Dept # 469
+telephoneNumber: +1 206 982-7959
+title: Elite Management Mascot
+userPassword: elaHcMhaW-
+uid: Pui-Wah_McHale
+givenName: Pui-Wah
+mail: Pui-Wah_McHale@example.com
+carLicense: L3E2BGO
+departmentNumber: 1893
+employeeType: Normal
+homePhone: +1 303 652-2670
+initials: P. M.
+mobile: +1 71 212-4626
+pager: +1 415 890-1418
+manager: cn=Glenine Speers
+secretary: cn=Sosanna Borkowicz
+roomNumber: 6920
+
+dn: cn=Gerardo Micucci, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gerardo Micucci
+sn: Micucci
+description: This is Gerardo Micucci's description
+facsimileTelephoneNumber: +1 303 289-4435
+l: San Francisco
+ou: Accounting
+postalAddress: example$Accounting$Dept # 717
+telephoneNumber: +1 818 390-5372
+title: Master Accounting Technician
+userPassword: iccuciModr
+uid: Gerardo_Micucci
+givenName: Gerardo
+mail: Gerardo_Micucci@example.com
+carLicense: FUVFUPX
+departmentNumber: 8550
+employeeType: Employee
+homePhone: +1 804 550-2922
+initials: G. M.
+mobile: +1 206 160-5665
+pager: +1 408 103-1416
+manager: cn=Merla Mezzoiuso
+secretary: cn=Grietje Erkel
+roomNumber: 2625
+
+dn: cn=Parkinson Aldridge, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Parkinson Aldridge
+sn: Aldridge
+description: This is Parkinson Aldridge's description
+facsimileTelephoneNumber: +1 804 229-1425
+l: San Mateo
+ou: Peons
+postalAddress: example$Peons$Dept # 609
+telephoneNumber: +1 804 619-3292
+title: Junior Peons Artist
+userPassword: egdirdlAno
+uid: Parkinson_Aldridge
+givenName: Parkinson
+mail: Parkinson_Aldridge@example.com
+carLicense: OMIMFDE
+departmentNumber: 929
+employeeType: Employee
+homePhone: +1 303 596-8980
+initials: P. A.
+mobile: +1 71 871-3752
+pager: +1 510 315-9587
+manager: cn=Marit Sezer
+secretary: cn=Malgosia Deanda
+roomNumber: 1252
+
+dn: cn=Jeannine Cuddy, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jeannine Cuddy
+sn: Cuddy
+description: This is Jeannine Cuddy's description
+facsimileTelephoneNumber: +1 303 187-3373
+l: Redmond
+ou: Accounting
+postalAddress: example$Accounting$Dept # 535
+telephoneNumber: +1 206 128-3964
+title: Supreme Accounting Engineer
+userPassword: ydduCeninn
+uid: Jeannine_Cuddy
+givenName: Jeannine
+mail: Jeannine_Cuddy@example.com
+carLicense: TPO84D7
+departmentNumber: 3516
+employeeType: Temp
+homePhone: +1 206 664-2956
+initials: J. C.
+mobile: +1 213 322-4775
+pager: +1 510 451-6191
+manager: cn=Seline Dumouchel
+secretary: cn=Penelope Merritt
+roomNumber: 4118
+
+dn: cn=Wynny Neuman, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Wynny Neuman
+sn: Neuman
+description: This is Wynny Neuman's description
+facsimileTelephoneNumber: +1 303 177-8824
+l: Orem
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 746
+telephoneNumber: +1 408 469-1079
+title: Chief Janitorial Engineer
+userPassword: namueNynny
+uid: Wynny_Neuman
+givenName: Wynny
+mail: Wynny_Neuman@example.com
+carLicense: AAGR9YJ
+departmentNumber: 3517
+employeeType: Temp
+homePhone: +1 408 189-9296
+initials: W. N.
+mobile: +1 818 146-2637
+pager: +1 804 367-4966
+manager: cn=Milena Guilford
+secretary: cn=Norel Zitzmann
+roomNumber: 5614
+
+dn: cn=Minne Herrington, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Minne Herrington
+sn: Herrington
+description: This is Minne Herrington's description
+facsimileTelephoneNumber: +1 408 660-3610
+l: Orem
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 953
+telephoneNumber: +1 818 484-6125
+title: Elite Product Testing Technician
+userPassword: notgnirreH
+uid: Minne_Herrington
+givenName: Minne
+mail: Minne_Herrington@example.com
+carLicense: 64XGONI
+departmentNumber: 9271
+employeeType: Manager
+homePhone: +1 206 347-4089
+initials: M. H.
+mobile: +1 303 797-4126
+pager: +1 213 191-5947
+manager: cn=Sashenka Stratton
+secretary: cn=Corella Sills
+roomNumber: 9774
+
+dn: cn=Bruce Crowe, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Bruce Crowe
+sn: Crowe
+description: This is Bruce Crowe's description
+facsimileTelephoneNumber: +1 71 452-5871
+l: Redmond
+ou: Peons
+postalAddress: example$Peons$Dept # 764
+telephoneNumber: +1 415 742-8040
+title: Elite Peons Writer
+userPassword: eworCecurB
+uid: Bruce_Crowe
+givenName: Bruce
+mail: Bruce_Crowe@example.com
+carLicense: S47E8B5
+departmentNumber: 8154
+employeeType: Manager
+homePhone: +1 415 297-4855
+initials: B. C.
+mobile: +1 818 120-1713
+pager: +1 408 406-9183
+manager: cn=Guendolen Terwilligar
+secretary: cn=Message Beers
+roomNumber: 3612
+
+dn: cn=Amos Colbert, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Amos Colbert
+sn: Colbert
+description: This is Amos Colbert's description
+facsimileTelephoneNumber: +1 206 160-5525
+l: Milpitas
+ou: Accounting
+postalAddress: example$Accounting$Dept # 853
+telephoneNumber: +1 818 275-1824
+title: Supreme Accounting Writer
+userPassword: trebloCsom
+uid: Amos_Colbert
+givenName: Amos
+mail: Amos_Colbert@example.com
+carLicense: JIYCIV7
+departmentNumber: 5967
+employeeType: Contract
+homePhone: +1 415 400-1423
+initials: A. C.
+mobile: +1 818 589-5302
+pager: +1 818 604-2500
+manager: cn=Sashenka Aboul-Magd
+secretary: cn=Etta Calkins
+roomNumber: 5557
+
+dn: cn=Ann Tully, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ann Tully
+sn: Tully
+description: This is Ann Tully's description
+facsimileTelephoneNumber: +1 213 206-5551
+l: Cambridge
+ou: Accounting
+postalAddress: example$Accounting$Dept # 784
+telephoneNumber: +1 415 208-1794
+title: Supreme Accounting Admin
+userPassword: ylluTnnA
+uid: Ann_Tully
+givenName: Ann
+mail: Ann_Tully@example.com
+carLicense: 3BM6G8F
+departmentNumber: 1672
+employeeType: Employee
+homePhone: +1 213 246-2575
+initials: A. T.
+mobile: +1 213 889-1457
+pager: +1 71 105-8458
+manager: cn=Buddy Wingfield
+secretary: cn=Minny Vahdat
+roomNumber: 538
+
+dn: cn=Sonny Pepe, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sonny Pepe
+sn: Pepe
+description: This is Sonny Pepe's description
+facsimileTelephoneNumber: +1 303 696-1335
+l: San Mateo
+ou: Peons
+postalAddress: example$Peons$Dept # 263
+telephoneNumber: +1 818 255-2500
+title: Associate Peons Janitor
+userPassword: epePynnoS
+uid: Sonny_Pepe
+givenName: Sonny
+mail: Sonny_Pepe@example.com
+carLicense: FJ3ARDR
+departmentNumber: 8739
+employeeType: Temp
+homePhone: +1 804 260-2967
+initials: S. P.
+mobile: +1 804 996-2579
+pager: +1 71 431-1746
+manager: cn=Joon Chen
+secretary: cn=Linzie Linebarger
+roomNumber: 1155
+
+dn: cn=Laurna Barham, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Laurna Barham
+sn: Barham
+description: This is Laurna Barham's description
+facsimileTelephoneNumber: +1 804 914-7533
+l: Redwood Shores
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 27
+telephoneNumber: +1 804 746-8767
+title: Master Human Resources Grunt
+userPassword: mahraBanru
+uid: Laurna_Barham
+givenName: Laurna
+mail: Laurna_Barham@example.com
+carLicense: 9W6J8D0
+departmentNumber: 8884
+employeeType: Normal
+homePhone: +1 71 841-6532
+initials: L. B.
+mobile: +1 415 818-9707
+pager: +1 303 710-6265
+manager: cn=Mariya Mancini
+secretary: cn=Rong-Chin Bowser
+roomNumber: 7947
+
+dn: cn=Blinny Brassem, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Blinny Brassem
+sn: Brassem
+description: This is Blinny Brassem's description
+facsimileTelephoneNumber: +1 804 489-8374
+l: Redwood Shores
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 611
+telephoneNumber: +1 510 245-7901
+title: Master Human Resources Dictator
+userPassword: messarBynn
+uid: Blinny_Brassem
+givenName: Blinny
+mail: Blinny_Brassem@example.com
+carLicense: AL2I4UO
+departmentNumber: 2597
+employeeType: Manager
+homePhone: +1 408 683-5120
+initials: B. B.
+mobile: +1 818 332-4217
+pager: +1 71 342-3678
+manager: cn=Aaron Odegaard
+secretary: cn=Len Chouhan
+roomNumber: 240
+
+dn: cn=Hildagarde Naphan, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Hildagarde Naphan
+sn: Naphan
+description: This is Hildagarde Naphan's description
+facsimileTelephoneNumber: +1 206 511-6335
+l: Emeryville
+ou: Payroll
+postalAddress: example$Payroll$Dept # 487
+telephoneNumber: +1 71 139-1287
+title: Elite Payroll Figurehead
+userPassword: nahpaNedra
+uid: Hildagarde_Naphan
+givenName: Hildagarde
+mail: Hildagarde_Naphan@example.com
+carLicense: HOHZ7A7
+departmentNumber: 1836
+employeeType: Contract
+homePhone: +1 408 156-7583
+initials: H. N.
+mobile: +1 415 130-2609
+pager: +1 415 275-6325
+manager: cn=Amato Kurtz
+secretary: cn=Prue Khatri
+roomNumber: 3152
+
+dn: cn=Delfin Labarge, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Delfin Labarge
+sn: Labarge
+description: This is Delfin Labarge's description
+facsimileTelephoneNumber: +1 804 737-2330
+l: Santa Clara
+ou: Payroll
+postalAddress: example$Payroll$Dept # 541
+telephoneNumber: +1 510 882-3250
+title: Senior Payroll Technician
+userPassword: egrabaLnif
+uid: Delfin_Labarge
+givenName: Delfin
+mail: Delfin_Labarge@example.com
+carLicense: N9ZUDX9
+departmentNumber: 6524
+employeeType: Contract
+homePhone: +1 303 121-6027
+initials: D. L.
+mobile: +1 408 905-7544
+pager: +1 408 119-2332
+manager: cn=Daveen Jeronimo
+secretary: cn=Boris Lyman
+roomNumber: 4080
+
+dn: cn=Rubi MAINT, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rubi MAINT
+sn: MAINT
+description: This is Rubi MAINT's description
+facsimileTelephoneNumber: +1 415 304-4441
+l: San Francisco
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 844
+telephoneNumber: +1 213 220-5777
+title: Chief Product Testing Mascot
+userPassword: TNIAMibuR
+uid: Rubi_MAINT
+givenName: Rubi
+mail: Rubi_MAINT@example.com
+carLicense: AOO04R0
+departmentNumber: 1212
+employeeType: Normal
+homePhone: +1 415 822-4369
+initials: R. M.
+mobile: +1 408 768-7514
+pager: +1 71 699-3787
+manager: cn=Ajay Monforton
+secretary: cn=Marice Almon
+roomNumber: 3254
+
+dn: cn=Muriel Oka, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Muriel Oka
+sn: Oka
+description: This is Muriel Oka's description
+facsimileTelephoneNumber: +1 408 312-4431
+l: Santa Clara
+ou: Accounting
+postalAddress: example$Accounting$Dept # 400
+telephoneNumber: +1 804 119-4291
+title: Master Accounting Engineer
+userPassword: akOleiruM
+uid: Muriel_Oka
+givenName: Muriel
+mail: Muriel_Oka@example.com
+carLicense: SMK0IG5
+departmentNumber: 493
+employeeType: Normal
+homePhone: +1 303 362-6176
+initials: M. O.
+mobile: +1 510 446-7985
+pager: +1 71 506-3623
+manager: cn=Tarah Kastner
+secretary: cn=Mattie Gung
+roomNumber: 1631
+
+dn: cn=Blondelle Glasser, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Blondelle Glasser
+sn: Glasser
+description: This is Blondelle Glasser's description
+facsimileTelephoneNumber: +1 804 996-9926
+l: Mountain View
+ou: Management
+postalAddress: example$Management$Dept # 50
+telephoneNumber: +1 804 499-2765
+title: Supreme Management Yahoo
+userPassword: ressalGell
+uid: Blondelle_Glasser
+givenName: Blondelle
+mail: Blondelle_Glasser@example.com
+carLicense: DZUB0VG
+departmentNumber: 4548
+employeeType: Manager
+homePhone: +1 408 702-6355
+initials: B. G.
+mobile: +1 206 642-4016
+pager: +1 408 610-1359
+manager: cn=Murray Lappan
+secretary: cn=Nazib Dingman
+roomNumber: 8152
+
+dn: cn=Marge Marouchos, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marge Marouchos
+sn: Marouchos
+description: This is Marge Marouchos's description
+facsimileTelephoneNumber: +1 71 551-4436
+l: San Mateo
+ou: Product Development
+postalAddress: example$Product Development$Dept # 217
+telephoneNumber: +1 71 749-5826
+title: Junior Product Development Sales Rep
+userPassword: sohcuoraMe
+uid: Marge_Marouchos
+givenName: Marge
+mail: Marge_Marouchos@example.com
+carLicense: 72DUN23
+departmentNumber: 4093
+employeeType: Manager
+homePhone: +1 818 406-9914
+initials: M. M.
+mobile: +1 415 855-9769
+pager: +1 206 962-5794
+manager: cn=Lin Tombul
+secretary: cn=Arielle Herscovici
+roomNumber: 1055
+
+dn: cn=Chi-Keung Swartz, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Chi-Keung Swartz
+sn: Swartz
+description: This is Chi-Keung Swartz's description
+facsimileTelephoneNumber: +1 206 569-5681
+l: Palo Alto
+ou: Payroll
+postalAddress: example$Payroll$Dept # 796
+telephoneNumber: +1 213 821-1433
+title: Supreme Payroll Figurehead
+userPassword: ztrawSgnue
+uid: Chi-Keung_Swartz
+givenName: Chi-Keung
+mail: Chi-Keung_Swartz@example.com
+carLicense: 4NL27ET
+departmentNumber: 7242
+employeeType: Manager
+homePhone: +1 71 702-2781
+initials: C. S.
+mobile: +1 408 146-6036
+pager: +1 71 191-6004
+manager: cn=Valli Schlagenhauf
+secretary: cn=Rici Massonneau
+roomNumber: 2917
+
+dn: cn=Tasia SVM-BNRMTVA, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tasia SVM-BNRMTVA
+sn: SVM-BNRMTVA
+description: This is Tasia SVM-BNRMTVA's description
+facsimileTelephoneNumber: +1 408 528-5158
+l: Sunnyvale
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 231
+telephoneNumber: +1 408 323-7053
+title: Junior Janitorial Figurehead
+userPassword: AVTMRNB-MV
+uid: Tasia_SVM-BNRMTVA
+givenName: Tasia
+mail: Tasia_SVM-BNRMTVA@example.com
+carLicense: NY4N060
+departmentNumber: 2767
+employeeType: Manager
+homePhone: +1 303 603-1682
+initials: T. S.
+mobile: +1 415 219-4966
+pager: +1 303 402-4396
+manager: cn=Xylia Deitiker
+secretary: cn=Ludovika Ayres
+roomNumber: 6079
+
+dn: cn=Jack Jolliffe, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jack Jolliffe
+sn: Jolliffe
+description: This is Jack Jolliffe's description
+facsimileTelephoneNumber: +1 206 463-1810
+l: San Francisco
+ou: Peons
+postalAddress: example$Peons$Dept # 630
+telephoneNumber: +1 408 928-7358
+title: Senior Peons Consultant
+userPassword: effilloJkc
+uid: Jack_Jolliffe
+givenName: Jack
+mail: Jack_Jolliffe@example.com
+carLicense: O1Z8TV9
+departmentNumber: 6564
+employeeType: Contract
+homePhone: +1 408 399-2117
+initials: J. J.
+mobile: +1 818 236-3937
+pager: +1 408 507-7328
+manager: cn=Ross Missailidis
+secretary: cn=Jany Hanser
+roomNumber: 5644
+
+dn: cn=Kathryn Schwaderer, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kathryn Schwaderer
+sn: Schwaderer
+description: This is Kathryn Schwaderer's description
+facsimileTelephoneNumber: +1 408 322-7580
+l: Menlo Park
+ou: Planning
+postalAddress: example$Planning$Dept # 993
+telephoneNumber: +1 510 451-7822
+title: Senior Planning Technician
+userPassword: reredawhcS
+uid: Kathryn_Schwaderer
+givenName: Kathryn
+mail: Kathryn_Schwaderer@example.com
+carLicense: FME00W1
+departmentNumber: 6103
+employeeType: Employee
+homePhone: +1 213 805-9060
+initials: K. S.
+mobile: +1 213 979-5400
+pager: +1 206 230-6702
+manager: cn=Anna-maria Renfro
+secretary: cn=Quang-Trung Heybroek
+roomNumber: 9592
+
+dn: cn=Elton Churas, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Elton Churas
+sn: Churas
+description: This is Elton Churas's description
+facsimileTelephoneNumber: +1 818 132-9892
+l: Alameda
+ou: Accounting
+postalAddress: example$Accounting$Dept # 640
+telephoneNumber: +1 303 880-8926
+title: Master Accounting Mascot
+userPassword: saruhCnotl
+uid: Elton_Churas
+givenName: Elton
+mail: Elton_Churas@example.com
+carLicense: 6MSE0G4
+departmentNumber: 9944
+employeeType: Employee
+homePhone: +1 206 248-3924
+initials: E. C.
+mobile: +1 804 202-6373
+pager: +1 206 382-8956
+manager: cn=Susann Smulders
+secretary: cn=Alfreda Scribner
+roomNumber: 1279
+
+dn: cn=Lenna Masse, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lenna Masse
+sn: Masse
+description: This is Lenna Masse's description
+facsimileTelephoneNumber: +1 71 948-3740
+l: Sunnyvale
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 241
+telephoneNumber: +1 804 196-5082
+title: Senior Janitorial Pinhead
+userPassword: essaManneL
+uid: Lenna_Masse
+givenName: Lenna
+mail: Lenna_Masse@example.com
+carLicense: K63LETM
+departmentNumber: 876
+employeeType: Manager
+homePhone: +1 408 282-9524
+initials: L. M.
+mobile: +1 415 885-7002
+pager: +1 510 579-6621
+manager: cn=Cooper Hollack
+secretary: cn=Irc Schiegl
+roomNumber: 45
+
+dn: cn=Baines Jarboe, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Baines Jarboe
+sn: Jarboe
+description: This is Baines Jarboe's description
+facsimileTelephoneNumber: +1 408 493-6631
+l: Cambridge
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 273
+telephoneNumber: +1 818 514-9417
+title: Supreme Janitorial Engineer
+userPassword: eobraJseni
+uid: Baines_Jarboe
+givenName: Baines
+mail: Baines_Jarboe@example.com
+carLicense: ZVHGOKW
+departmentNumber: 205
+employeeType: Contract
+homePhone: +1 818 725-3631
+initials: B. J.
+mobile: +1 71 532-7345
+pager: +1 408 484-7840
+manager: cn=Lynette Pon
+secretary: cn=Myron Irvine
+roomNumber: 3607
+
+dn: cn=Brigitte Lappan, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Brigitte Lappan
+sn: Lappan
+description: This is Brigitte Lappan's description
+facsimileTelephoneNumber: +1 804 740-2837
+l: San Jose
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 448
+telephoneNumber: +1 408 616-4824
+title: Associate Product Testing Writer
+userPassword: nappaLetti
+uid: Brigitte_Lappan
+givenName: Brigitte
+mail: Brigitte_Lappan@example.com
+carLicense: ONIGC8W
+departmentNumber: 8371
+employeeType: Temp
+homePhone: +1 206 896-5612
+initials: B. L.
+mobile: +1 408 705-1940
+pager: +1 818 655-9906
+manager: cn=Jenine Sayed
+secretary: cn=Ernestine Zisu
+roomNumber: 7395
+
+dn: cn=Shan McNamara, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Shan McNamara
+sn: McNamara
+description: This is Shan McNamara's description
+facsimileTelephoneNumber: +1 408 278-8634
+l: San Mateo
+ou: Peons
+postalAddress: example$Peons$Dept # 828
+telephoneNumber: +1 71 428-3947
+title: Senior Peons Yahoo
+userPassword: aramaNcMna
+uid: Shan_McNamara
+givenName: Shan
+mail: Shan_McNamara@example.com
+carLicense: H800UZJ
+departmentNumber: 6157
+employeeType: Normal
+homePhone: +1 804 115-3589
+initials: S. M.
+mobile: +1 213 645-1126
+pager: +1 206 210-4418
+manager: cn=Ammamaria Rains
+secretary: cn=Tsing Millaire
+roomNumber: 5359
+
+dn: cn=Kristopher Gervaise, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kristopher Gervaise
+sn: Gervaise
+description: This is Kristopher Gervaise's description
+facsimileTelephoneNumber: +1 408 358-3860
+l: Cambridge
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 576
+telephoneNumber: +1 804 485-3937
+title: Master Product Testing Admin
+userPassword: esiavreGre
+uid: Kristopher_Gervaise
+givenName: Kristopher
+mail: Kristopher_Gervaise@example.com
+carLicense: HURNQK1
+departmentNumber: 9750
+employeeType: Temp
+homePhone: +1 510 673-8200
+initials: K. G.
+mobile: +1 206 213-1403
+pager: +1 206 853-1047
+manager: cn=Freek McElhone
+secretary: cn=Anderea Caterina
+roomNumber: 8167
+
+dn: cn=Pinecrest Nizamuddin, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Pinecrest Nizamuddin
+sn: Nizamuddin
+description: This is Pinecrest Nizamuddin's description
+facsimileTelephoneNumber: +1 213 268-6719
+l: Milpitas
+ou: Administrative
+postalAddress: example$Administrative$Dept # 431
+telephoneNumber: +1 206 997-2066
+title: Senior Administrative Architect
+userPassword: niddumaziN
+uid: Pinecrest_Nizamuddin
+givenName: Pinecrest
+mail: Pinecrest_Nizamuddin@example.com
+carLicense: 92AL2GK
+departmentNumber: 569
+employeeType: Temp
+homePhone: +1 303 221-4735
+initials: P. N.
+mobile: +1 804 451-9720
+pager: +1 213 461-2793
+manager: cn=Lab Stanke
+secretary: cn=Deborah Zagrodney
+roomNumber: 9090
+
+dn: cn=Sapphira Gurgenci, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sapphira Gurgenci
+sn: Gurgenci
+description: This is Sapphira Gurgenci's description
+facsimileTelephoneNumber: +1 408 691-8785
+l: San Francisco
+ou: Product Development
+postalAddress: example$Product Development$Dept # 501
+telephoneNumber: +1 213 267-8152
+title: Elite Product Development Consultant
+userPassword: icnegruGar
+uid: Sapphira_Gurgenci
+givenName: Sapphira
+mail: Sapphira_Gurgenci@example.com
+carLicense: AYX8O7Q
+departmentNumber: 3501
+employeeType: Contract
+homePhone: +1 408 991-3370
+initials: S. G.
+mobile: +1 206 586-3106
+pager: +1 408 257-3189
+manager: cn=Shannah Kwant
+secretary: cn=Seyma Burrows
+roomNumber: 7601
+
+dn: cn=Phebe Courchesne, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Phebe Courchesne
+sn: Courchesne
+description: This is Phebe Courchesne's description
+facsimileTelephoneNumber: +1 71 375-1979
+l: Sunnyvale
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 606
+telephoneNumber: +1 71 647-6824
+title: Master Product Testing Yahoo
+userPassword: ensehcruoC
+uid: Phebe_Courchesne
+givenName: Phebe
+mail: Phebe_Courchesne@example.com
+carLicense: FEB9JD1
+departmentNumber: 2939
+employeeType: Manager
+homePhone: +1 408 787-9165
+initials: P. C.
+mobile: +1 818 955-8666
+pager: +1 213 378-9440
+manager: cn=Erkan Godowsky
+secretary: cn=Padriac Norwood
+roomNumber: 7633
+
+dn: cn=Allsun Nolter, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Allsun Nolter
+sn: Nolter
+description: This is Allsun Nolter's description
+facsimileTelephoneNumber: +1 71 470-8445
+l: Emeryville
+ou: Accounting
+postalAddress: example$Accounting$Dept # 168
+telephoneNumber: +1 408 187-7850
+title: Elite Accounting Manager
+userPassword: retloNnusl
+uid: Allsun_Nolter
+givenName: Allsun
+mail: Allsun_Nolter@example.com
+carLicense: AANBF8M
+departmentNumber: 4139
+employeeType: Contract
+homePhone: +1 71 160-5067
+initials: A. N.
+mobile: +1 408 151-3182
+pager: +1 206 639-6024
+manager: cn=Selva Chee
+secretary: cn=Dorri Cavan
+roomNumber: 4350
+
+dn: cn=Leonelle Abdo, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Leonelle Abdo
+sn: Abdo
+description: This is Leonelle Abdo's description
+facsimileTelephoneNumber: +1 206 965-8823
+l: Emeryville
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 912
+telephoneNumber: +1 206 493-7340
+title: Junior Product Testing Stooge
+userPassword: odbAelleno
+uid: Leonelle_Abdo
+givenName: Leonelle
+mail: Leonelle_Abdo@example.com
+carLicense: FQBC5SN
+departmentNumber: 5310
+employeeType: Employee
+homePhone: +1 206 181-2683
+initials: L. A.
+mobile: +1 71 149-9722
+pager: +1 303 696-8266
+manager: cn=Bosiljka Mittleider
+secretary: cn=Umeko Papageorgiou
+roomNumber: 1575
+
+dn: cn=Amalia Crowder, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Amalia Crowder
+sn: Crowder
+description: This is Amalia Crowder's description
+facsimileTelephoneNumber: +1 71 522-1890
+l: Fremont
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 186
+telephoneNumber: +1 818 325-3439
+title: Master Janitorial Artist
+userPassword: redworCail
+uid: Amalia_Crowder
+givenName: Amalia
+mail: Amalia_Crowder@example.com
+carLicense: FC6W628
+departmentNumber: 75
+employeeType: Normal
+homePhone: +1 71 748-7975
+initials: A. C.
+mobile: +1 510 337-7154
+pager: +1 303 441-3224
+manager: cn=Angelo Lask
+secretary: cn=Adrie Kaczmarek
+roomNumber: 3772
+
+dn: cn=Lonni Schellenberger, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lonni Schellenberger
+sn: Schellenberger
+description: This is Lonni Schellenberger's description
+facsimileTelephoneNumber: +1 71 366-9151
+l: Alameda
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 279
+telephoneNumber: +1 510 193-1252
+title: Junior Human Resources Visionary
+userPassword: regrebnell
+uid: Lonni_Schellenberger
+givenName: Lonni
+mail: Lonni_Schellenberger@example.com
+carLicense: 17BC5GC
+departmentNumber: 9439
+employeeType: Normal
+homePhone: +1 415 655-4002
+initials: L. S.
+mobile: +1 206 551-9580
+pager: +1 408 847-3050
+manager: cn=Dulciana Zeller
+secretary: cn=Bailey Chaurasia
+roomNumber: 837
+
+dn: cn=Michie Stern, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Michie Stern
+sn: Stern
+description: This is Michie Stern's description
+facsimileTelephoneNumber: +1 510 805-1860
+l: Redmond
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 633
+telephoneNumber: +1 818 201-2629
+title: Chief Human Resources Fellow
+userPassword: nretSeihci
+uid: Michie_Stern
+givenName: Michie
+mail: Michie_Stern@example.com
+carLicense: VLXXKHI
+departmentNumber: 9632
+employeeType: Manager
+homePhone: +1 804 665-1017
+initials: M. S.
+mobile: +1 71 496-5888
+pager: +1 71 990-2076
+manager: cn=Domenic Racz
+secretary: cn=Adelaida Sims
+roomNumber: 3078
+
+dn: cn=Sedat Seabrook, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sedat Seabrook
+sn: Seabrook
+description: This is Sedat Seabrook's description
+facsimileTelephoneNumber: +1 510 142-5630
+l: Alameda
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 611
+telephoneNumber: +1 415 668-5765
+title: Supreme Human Resources Director
+userPassword: koorbaeSta
+uid: Sedat_Seabrook
+givenName: Sedat
+mail: Sedat_Seabrook@example.com
+carLicense: J0RFQMY
+departmentNumber: 6654
+employeeType: Temp
+homePhone: +1 71 959-4365
+initials: S. S.
+mobile: +1 213 182-7712
+pager: +1 408 834-6188
+manager: cn=Klaus Lilleniit
+secretary: cn=Gabey Rodschat
+roomNumber: 6944
+
+dn: cn=Ruben Bottomley, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ruben Bottomley
+sn: Bottomley
+description: This is Ruben Bottomley's description
+facsimileTelephoneNumber: +1 804 374-6124
+l: Fremont
+ou: Product Development
+postalAddress: example$Product Development$Dept # 721
+telephoneNumber: +1 206 168-9537
+title: Supreme Product Development Grunt
+userPassword: yelmottoBn
+uid: Ruben_Bottomley
+givenName: Ruben
+mail: Ruben_Bottomley@example.com
+carLicense: XSDZRAM
+departmentNumber: 6356
+employeeType: Employee
+homePhone: +1 415 832-6047
+initials: R. B.
+mobile: +1 206 957-3798
+pager: +1 408 940-5832
+manager: cn=Jaquelin Gros
+secretary: cn=Utilla Watters
+roomNumber: 9316
+
+dn: cn=Fernandina Sherrard, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Fernandina Sherrard
+sn: Sherrard
+description: This is Fernandina Sherrard's description
+facsimileTelephoneNumber: +1 415 583-7584
+l: Mountain View
+ou: Accounting
+postalAddress: example$Accounting$Dept # 744
+telephoneNumber: +1 71 284-3179
+title: Senior Accounting Sales Rep
+userPassword: drarrehSan
+uid: Fernandina_Sherrard
+givenName: Fernandina
+mail: Fernandina_Sherrard@example.com
+carLicense: IL4Z7WL
+departmentNumber: 1484
+employeeType: Contract
+homePhone: +1 415 848-3023
+initials: F. S.
+mobile: +1 804 352-1012
+pager: +1 408 544-7222
+manager: cn=Eugine Melton
+secretary: cn=Vania Guajardo
+roomNumber: 713
+
+dn: cn=Dasha Pallen, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dasha Pallen
+sn: Pallen
+description: This is Dasha Pallen's description
+facsimileTelephoneNumber: +1 206 791-8512
+l: Menlo Park
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 996
+telephoneNumber: +1 818 587-6679
+title: Master Product Testing Evangelist
+userPassword: nellaPahsa
+uid: Dasha_Pallen
+givenName: Dasha
+mail: Dasha_Pallen@example.com
+carLicense: C037ZQQ
+departmentNumber: 6785
+employeeType: Manager
+homePhone: +1 510 218-2016
+initials: D. P.
+mobile: +1 510 616-2869
+pager: +1 818 116-6325
+manager: cn=Bevvy Wilken
+secretary: cn=Daffi Gentes
+roomNumber: 3119
+
+dn: cn=Claribel Al-Basi, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Claribel Al-Basi
+sn: Al-Basi
+description: This is Claribel Al-Basi's description
+facsimileTelephoneNumber: +1 206 133-6183
+l: Orem
+ou: Payroll
+postalAddress: example$Payroll$Dept # 864
+telephoneNumber: +1 818 342-4711
+title: Chief Payroll Assistant
+userPassword: isaB-lAleb
+uid: Claribel_Al-Basi
+givenName: Claribel
+mail: Claribel_Al-Basi@example.com
+carLicense: G4U4HOJ
+departmentNumber: 354
+employeeType: Employee
+homePhone: +1 804 800-1021
+initials: C. A.
+mobile: +1 213 940-5595
+pager: +1 415 207-6626
+manager: cn=Felton Jarboe
+secretary: cn=Ignace Bilodeau
+roomNumber: 5491
+
+dn: cn=Kimberlyn Wegener, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kimberlyn Wegener
+sn: Wegener
+description: This is Kimberlyn Wegener's description
+facsimileTelephoneNumber: +1 206 660-8544
+l: Santa Clara
+ou: Peons
+postalAddress: example$Peons$Dept # 327
+telephoneNumber: +1 408 845-5707
+title: Associate Peons Warrior
+userPassword: renegeWnyl
+uid: Kimberlyn_Wegener
+givenName: Kimberlyn
+mail: Kimberlyn_Wegener@example.com
+carLicense: PJA64VA
+departmentNumber: 4445
+employeeType: Contract
+homePhone: +1 510 711-8510
+initials: K. W.
+mobile: +1 510 304-4281
+pager: +1 303 140-8992
+manager: cn=Lulu Dobbing
+secretary: cn=Eadie Pharr
+roomNumber: 1028
+
+dn: cn=Biddie Auld, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Biddie Auld
+sn: Auld
+description: This is Biddie Auld's description
+facsimileTelephoneNumber: +1 415 226-8217
+l: Milpitas
+ou: Accounting
+postalAddress: example$Accounting$Dept # 874
+telephoneNumber: +1 408 296-3963
+title: Senior Accounting Assistant
+userPassword: dluAeiddiB
+uid: Biddie_Auld
+givenName: Biddie
+mail: Biddie_Auld@example.com
+carLicense: SUMH0IQ
+departmentNumber: 1075
+employeeType: Contract
+homePhone: +1 206 589-6926
+initials: B. A.
+mobile: +1 408 661-2122
+pager: +1 71 378-4702
+manager: cn=Ermina Mansbridge
+secretary: cn=Phan Heikkila
+roomNumber: 7646
+
+dn: cn=Farhad Hutter, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Farhad Hutter
+sn: Hutter
+description: This is Farhad Hutter's description
+facsimileTelephoneNumber: +1 818 601-9336
+l: Sunnyvale
+ou: Accounting
+postalAddress: example$Accounting$Dept # 473
+telephoneNumber: +1 206 300-9853
+title: Associate Accounting Stooge
+userPassword: rettuHdahr
+uid: Farhad_Hutter
+givenName: Farhad
+mail: Farhad_Hutter@example.com
+carLicense: NBPV6JX
+departmentNumber: 1786
+employeeType: Normal
+homePhone: +1 408 560-7509
+initials: F. H.
+mobile: +1 303 188-3309
+pager: +1 818 683-2951
+manager: cn=Isoft Donohoe
+secretary: cn=CrystalBay Eller
+roomNumber: 828
+
+dn: cn=Vikki Williford, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Vikki Williford
+sn: Williford
+description: This is Vikki Williford's description
+facsimileTelephoneNumber: +1 510 982-7778
+l: Orem
+ou: Accounting
+postalAddress: example$Accounting$Dept # 770
+telephoneNumber: +1 303 222-3373
+title: Supreme Accounting Madonna
+userPassword: drofilliWi
+uid: Vikki_Williford
+givenName: Vikki
+mail: Vikki_Williford@example.com
+carLicense: XOKSQDZ
+departmentNumber: 1267
+employeeType: Temp
+homePhone: +1 415 548-4527
+initials: V. W.
+mobile: +1 213 627-9947
+pager: +1 804 243-3048
+manager: cn=Helga Friton
+secretary: cn=Rand MacNaughton
+roomNumber: 2412
+
+dn: cn=Jessy Blumer, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jessy Blumer
+sn: Blumer
+description: This is Jessy Blumer's description
+facsimileTelephoneNumber: +1 303 478-2053
+l: Redwood Shores
+ou: Accounting
+postalAddress: example$Accounting$Dept # 773
+telephoneNumber: +1 510 174-3564
+title: Elite Accounting Developer
+userPassword: remulBysse
+uid: Jessy_Blumer
+givenName: Jessy
+mail: Jessy_Blumer@example.com
+carLicense: 6KGX6B4
+departmentNumber: 5820
+employeeType: Contract
+homePhone: +1 818 560-4802
+initials: J. B.
+mobile: +1 818 792-8722
+pager: +1 213 882-2569
+manager: cn=Mark Rabiasz
+secretary: cn=Quon Van Mansum
+roomNumber: 1446
+
+dn: cn=Suk-Yin Zegray, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Suk-Yin Zegray
+sn: Zegray
+description: This is Suk-Yin Zegray's description
+facsimileTelephoneNumber: +1 804 763-2385
+l: San Francisco
+ou: Peons
+postalAddress: example$Peons$Dept # 862
+telephoneNumber: +1 408 276-2390
+title: Associate Peons Artist
+userPassword: yargeZniY-
+uid: Suk-Yin_Zegray
+givenName: Suk-Yin
+mail: Suk-Yin_Zegray@example.com
+carLicense: G9UYGCP
+departmentNumber: 7962
+employeeType: Temp
+homePhone: +1 71 607-8825
+initials: S. Z.
+mobile: +1 206 618-1371
+pager: +1 303 312-2783
+manager: cn=Bobbie Babalola
+secretary: cn=Robbin Demir
+roomNumber: 3119
+
+dn: cn=Lida Ocone, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lida Ocone
+sn: Ocone
+description: This is Lida Ocone's description
+facsimileTelephoneNumber: +1 415 828-6128
+l: Milpitas
+ou: Product Development
+postalAddress: example$Product Development$Dept # 27
+telephoneNumber: +1 303 610-2363
+title: Supreme Product Development Mascot
+userPassword: enocOadiL
+uid: Lida_Ocone
+givenName: Lida
+mail: Lida_Ocone@example.com
+carLicense: 3K9Z6R1
+departmentNumber: 2105
+employeeType: Contract
+homePhone: +1 206 618-9130
+initials: L. O.
+mobile: +1 804 282-7906
+pager: +1 213 849-8442
+manager: cn=Klaus Stevanovic
+secretary: cn=Durali Seale
+roomNumber: 5223
+
+dn: cn=Cherin Wirth, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Cherin Wirth
+sn: Wirth
+description: This is Cherin Wirth's description
+facsimileTelephoneNumber: +1 213 135-2429
+l: Orem
+ou: Planning
+postalAddress: example$Planning$Dept # 318
+telephoneNumber: +1 510 760-3713
+title: Senior Planning Stooge
+userPassword: htriWnireh
+uid: Cherin_Wirth
+givenName: Cherin
+mail: Cherin_Wirth@example.com
+carLicense: ZQDJ9R8
+departmentNumber: 235
+employeeType: Employee
+homePhone: +1 303 909-8841
+initials: C. W.
+mobile: +1 213 428-8896
+pager: +1 510 412-1388
+manager: cn=Amitie Kuniyasu
+secretary: cn=Myranda Yabe
+roomNumber: 4222
+
+dn: cn=Bachittar Vakili, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Bachittar Vakili
+sn: Vakili
+description: This is Bachittar Vakili's description
+facsimileTelephoneNumber: +1 303 140-2053
+l: Redwood Shores
+ou: Administrative
+postalAddress: example$Administrative$Dept # 118
+telephoneNumber: +1 303 506-4751
+title: Master Administrative Admin
+userPassword: ilikaVratt
+uid: Bachittar_Vakili
+givenName: Bachittar
+mail: Bachittar_Vakili@example.com
+carLicense: GFHKN13
+departmentNumber: 4202
+employeeType: Contract
+homePhone: +1 415 382-3217
+initials: B. V.
+mobile: +1 303 594-8319
+pager: +1 408 659-3245
+manager: cn=Vijai Gateley
+secretary: cn=Siana Lonergan
+roomNumber: 9700
+
+dn: cn=Pacific NeKueey, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Pacific NeKueey
+sn: NeKueey
+description: This is Pacific NeKueey's description
+facsimileTelephoneNumber: +1 804 283-6147
+l: Sunnyvale
+ou: Administrative
+postalAddress: example$Administrative$Dept # 103
+telephoneNumber: +1 206 208-2628
+title: Senior Administrative Figurehead
+userPassword: yeeuKeNcif
+uid: Pacific_NeKueey
+givenName: Pacific
+mail: Pacific_NeKueey@example.com
+carLicense: N6VUI6B
+departmentNumber: 5507
+employeeType: Contract
+homePhone: +1 206 158-4665
+initials: P. N.
+mobile: +1 510 433-2871
+pager: +1 206 590-7629
+manager: cn=Karissa Ciskowski
+secretary: cn=Cad Jemczyk
+roomNumber: 1934
+
+dn: cn=Mary-Ellen Mickens, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Mary-Ellen Mickens
+sn: Mickens
+description: This is Mary-Ellen Mickens's description
+facsimileTelephoneNumber: +1 303 620-6201
+l: San Jose
+ou: Product Development
+postalAddress: example$Product Development$Dept # 905
+telephoneNumber: +1 213 360-3449
+title: Junior Product Development Vice President
+userPassword: snekciMnel
+uid: Mary-Ellen_Mickens
+givenName: Mary-Ellen
+mail: Mary-Ellen_Mickens@example.com
+carLicense: P2ZDXNB
+departmentNumber: 558
+employeeType: Manager
+homePhone: +1 206 217-1707
+initials: M. M.
+mobile: +1 408 262-3493
+pager: +1 818 863-5815
+manager: cn=Hanny Leppert
+secretary: cn=Cami Marleau
+roomNumber: 2024
+
+dn: cn=Cally Spragg, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Cally Spragg
+sn: Spragg
+description: This is Cally Spragg's description
+facsimileTelephoneNumber: +1 415 903-7518
+l: Mountain View
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 679
+telephoneNumber: +1 804 741-5221
+title: Associate Product Testing Stooge
+userPassword: ggarpSylla
+uid: Cally_Spragg
+givenName: Cally
+mail: Cally_Spragg@example.com
+carLicense: YWEB6T6
+departmentNumber: 1965
+employeeType: Manager
+homePhone: +1 818 133-5730
+initials: C. S.
+mobile: +1 206 741-5733
+pager: +1 71 283-2270
+manager: cn=Kacy Scarborough
+secretary: cn=Clarey Fernandez
+roomNumber: 9511
+
+dn: cn=Maud Marmion, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Maud Marmion
+sn: Marmion
+description: This is Maud Marmion's description
+facsimileTelephoneNumber: +1 303 902-5212
+l: Mountain View
+ou: Management
+postalAddress: example$Management$Dept # 610
+telephoneNumber: +1 408 892-3797
+title: Supreme Management Madonna
+userPassword: noimraMdua
+uid: Maud_Marmion
+givenName: Maud
+mail: Maud_Marmion@example.com
+carLicense: 6DZPEI0
+departmentNumber: 6679
+employeeType: Temp
+homePhone: +1 510 696-4058
+initials: M. M.
+mobile: +1 303 502-7495
+pager: +1 415 503-1607
+manager: cn=Berni Haggarty
+secretary: cn=Niz Doda
+roomNumber: 5540
+
+dn: cn=Staci Gostanian, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Staci Gostanian
+sn: Gostanian
+description: This is Staci Gostanian's description
+facsimileTelephoneNumber: +1 71 367-9393
+l: Mountain View
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 716
+telephoneNumber: +1 804 734-3749
+title: Master Human Resources President
+userPassword: nainatsoGi
+uid: Staci_Gostanian
+givenName: Staci
+mail: Staci_Gostanian@example.com
+carLicense: VJ8F6B6
+departmentNumber: 8004
+employeeType: Employee
+homePhone: +1 408 704-8269
+initials: S. G.
+mobile: +1 408 871-9499
+pager: +1 71 568-2234
+manager: cn=Frederique Ismail
+secretary: cn=Shanna Lindow
+roomNumber: 6136
+
+dn: cn=Rahal Berna, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rahal Berna
+sn: Berna
+description: This is Rahal Berna's description
+facsimileTelephoneNumber: +1 71 671-1322
+l: Palo Alto
+ou: Peons
+postalAddress: example$Peons$Dept # 894
+telephoneNumber: +1 818 476-4058
+title: Master Peons Vice President
+userPassword: anreBlahaR
+uid: Rahal_Berna
+givenName: Rahal
+mail: Rahal_Berna@example.com
+carLicense: QMS2WG7
+departmentNumber: 6229
+employeeType: Employee
+homePhone: +1 213 187-4022
+initials: R. B.
+mobile: +1 510 819-4491
+pager: +1 510 764-8561
+manager: cn=Felicdad Elgar
+secretary: cn=Bihari Racioppi
+roomNumber: 8851
+
+dn: cn=Margery Pillars, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Margery Pillars
+sn: Pillars
+description: This is Margery Pillars's description
+facsimileTelephoneNumber: +1 206 637-6697
+l: Palo Alto
+ou: Peons
+postalAddress: example$Peons$Dept # 484
+telephoneNumber: +1 213 188-6823
+title: Senior Peons Figurehead
+userPassword: sralliPyre
+uid: Margery_Pillars
+givenName: Margery
+mail: Margery_Pillars@example.com
+carLicense: 11CFP1X
+departmentNumber: 8595
+employeeType: Normal
+homePhone: +1 818 407-8340
+initials: M. P.
+mobile: +1 408 715-7716
+pager: +1 303 913-9229
+manager: cn=Phoebe Kirkwood
+secretary: cn=Pak-Jong Widdis
+roomNumber: 7509
+
+dn: cn=Quality Amiot, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Quality Amiot
+sn: Amiot
+description: This is Quality Amiot's description
+facsimileTelephoneNumber: +1 415 879-7072
+l: San Jose
+ou: Administrative
+postalAddress: example$Administrative$Dept # 217
+telephoneNumber: +1 415 743-2151
+title: Senior Administrative Visionary
+userPassword: toimAytila
+uid: Quality_Amiot
+givenName: Quality
+mail: Quality_Amiot@example.com
+carLicense: 2A1DL33
+departmentNumber: 9035
+employeeType: Contract
+homePhone: +1 804 610-8868
+initials: Q. A.
+mobile: +1 415 544-1599
+pager: +1 213 693-7865
+manager: cn=Franky Richards
+secretary: cn=Benthem Coe
+roomNumber: 6242
+
+dn: cn=Zahir Zelenka, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Zahir Zelenka
+sn: Zelenka
+description: This is Zahir Zelenka's description
+facsimileTelephoneNumber: +1 71 701-1569
+l: Santa Clara
+ou: Payroll
+postalAddress: example$Payroll$Dept # 301
+telephoneNumber: +1 213 575-1866
+title: Junior Payroll Janitor
+userPassword: akneleZrih
+uid: Zahir_Zelenka
+givenName: Zahir
+mail: Zahir_Zelenka@example.com
+carLicense: TBES61F
+departmentNumber: 1654
+employeeType: Normal
+homePhone: +1 510 895-4340
+initials: Z. Z.
+mobile: +1 206 276-9998
+pager: +1 71 732-7615
+manager: cn=Ardelis Virani
+secretary: cn=Andriana Kelland
+roomNumber: 1564
+
+dn: cn=Antonella Fredrickson, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Antonella Fredrickson
+sn: Fredrickson
+description: This is Antonella Fredrickson's description
+facsimileTelephoneNumber: +1 510 790-1410
+l: Cupertino
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 500
+telephoneNumber: +1 818 492-3209
+title: Chief Janitorial Warrior
+userPassword: noskcirder
+uid: Antonella_Fredrickson
+givenName: Antonella
+mail: Antonella_Fredrickson@example.com
+carLicense: RX64PKO
+departmentNumber: 5689
+employeeType: Normal
+homePhone: +1 415 736-1290
+initials: A. F.
+mobile: +1 415 513-2503
+pager: +1 804 302-6510
+manager: cn=Barlas Whitton
+secretary: cn=Laurella Viau
+roomNumber: 8757
+
+dn: cn=Trinh Demchuk, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Trinh Demchuk
+sn: Demchuk
+description: This is Trinh Demchuk's description
+facsimileTelephoneNumber: +1 818 213-6650
+l: San Jose
+ou: Product Development
+postalAddress: example$Product Development$Dept # 452
+telephoneNumber: +1 213 787-7790
+title: Associate Product Development Figurehead
+userPassword: kuhcmeDhni
+uid: Trinh_Demchuk
+givenName: Trinh
+mail: Trinh_Demchuk@example.com
+carLicense: XAX9HO5
+departmentNumber: 8623
+employeeType: Manager
+homePhone: +1 804 205-4726
+initials: T. D.
+mobile: +1 408 276-8026
+pager: +1 206 127-5276
+manager: cn=Ceciley Khouderchan
+secretary: cn=Mary-Ann Andrade
+roomNumber: 2871
+
+dn: cn=Zeb VanStaalduinen, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Zeb VanStaalduinen
+sn: VanStaalduinen
+description: This is Zeb VanStaalduinen's description
+facsimileTelephoneNumber: +1 415 936-1700
+l: Redwood Shores
+ou: Administrative
+postalAddress: example$Administrative$Dept # 762
+telephoneNumber: +1 415 204-3833
+title: Junior Administrative Manager
+userPassword: neniudlaat
+uid: Zeb_VanStaalduinen
+givenName: Zeb
+mail: Zeb_VanStaalduinen@example.com
+carLicense: BSOMKQW
+departmentNumber: 6214
+employeeType: Temp
+homePhone: +1 303 341-2412
+initials: Z. V.
+mobile: +1 206 495-3240
+pager: +1 804 470-9172
+manager: cn=Stacie Overcash
+secretary: cn=Sella Hashimoto
+roomNumber: 1068
+
+dn: cn=Ted Nagle, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ted Nagle
+sn: Nagle
+description: This is Ted Nagle's description
+facsimileTelephoneNumber: +1 804 309-7376
+l: San Jose
+ou: Product Development
+postalAddress: example$Product Development$Dept # 19
+telephoneNumber: +1 408 750-3517
+title: Junior Product Development Architect
+userPassword: elgaNdeT
+uid: Ted_Nagle
+givenName: Ted
+mail: Ted_Nagle@example.com
+carLicense: 1TRKHV4
+departmentNumber: 6383
+employeeType: Manager
+homePhone: +1 818 390-2285
+initials: T. N.
+mobile: +1 303 710-5797
+pager: +1 818 474-5333
+manager: cn=Bibbie Steene
+secretary: cn=Irving Deguines
+roomNumber: 2986
+
+dn: cn=Clarissa Shankar, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Clarissa Shankar
+sn: Shankar
+description: This is Clarissa Shankar's description
+facsimileTelephoneNumber: +1 303 244-7958
+l: San Mateo
+ou: Administrative
+postalAddress: example$Administrative$Dept # 772
+telephoneNumber: +1 303 887-7308
+title: Chief Administrative Janitor
+userPassword: raknahSass
+uid: Clarissa_Shankar
+givenName: Clarissa
+mail: Clarissa_Shankar@example.com
+carLicense: ZD2DCZ2
+departmentNumber: 2933
+employeeType: Manager
+homePhone: +1 213 420-4842
+initials: C. S.
+mobile: +1 408 647-4909
+pager: +1 510 946-9346
+manager: cn=Dusan Malynowsky
+secretary: cn=Fwp Trink
+roomNumber: 4288
+
+dn: cn=Querida Kauffeldt, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Querida Kauffeldt
+sn: Kauffeldt
+description: This is Querida Kauffeldt's description
+facsimileTelephoneNumber: +1 408 372-5810
+l: Santa Clara
+ou: Peons
+postalAddress: example$Peons$Dept # 201
+telephoneNumber: +1 71 217-3988
+title: Associate Peons Manager
+userPassword: tdleffuaKa
+uid: Querida_Kauffeldt
+givenName: Querida
+mail: Querida_Kauffeldt@example.com
+carLicense: J95JMC4
+departmentNumber: 5907
+employeeType: Manager
+homePhone: +1 510 683-1922
+initials: Q. K.
+mobile: +1 818 671-3371
+pager: +1 213 139-6642
+manager: cn=Ethelin Ajersch
+secretary: cn=Muire Witzman
+roomNumber: 136
+
+dn: cn=Mireielle Snoke, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Mireielle Snoke
+sn: Snoke
+description: This is Mireielle Snoke's description
+facsimileTelephoneNumber: +1 804 621-8119
+l: Cambridge
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 123
+telephoneNumber: +1 415 937-3694
+title: Supreme Janitorial Accountant
+userPassword: ekonSellei
+uid: Mireielle_Snoke
+givenName: Mireielle
+mail: Mireielle_Snoke@example.com
+carLicense: AMBEMKN
+departmentNumber: 3851
+employeeType: Employee
+homePhone: +1 415 378-1661
+initials: M. S.
+mobile: +1 408 890-7854
+pager: +1 303 496-7013
+manager: cn=Eva Meilleur
+secretary: cn=Turgay Verch
+roomNumber: 2976
+
+dn: cn=Dpnlab Aziz, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dpnlab Aziz
+sn: Aziz
+description: This is Dpnlab Aziz's description
+facsimileTelephoneNumber: +1 510 417-7276
+l: San Francisco
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 592
+telephoneNumber: +1 206 937-8375
+title: Chief Human Resources Architect
+userPassword: zizAbalnpD
+uid: Dpnlab_Aziz
+givenName: Dpnlab
+mail: Dpnlab_Aziz@example.com
+carLicense: 9A51V0Q
+departmentNumber: 682
+employeeType: Normal
+homePhone: +1 206 790-7182
+initials: D. A.
+mobile: +1 206 122-9748
+pager: +1 206 798-7925
+manager: cn=Carol Maxsom
+secretary: cn=Noemi Antinucci
+roomNumber: 8736
+
+dn: cn=Sangman Barkley, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sangman Barkley
+sn: Barkley
+description: This is Sangman Barkley's description
+facsimileTelephoneNumber: +1 71 500-5644
+l: Fremont
+ou: Management
+postalAddress: example$Management$Dept # 996
+telephoneNumber: +1 408 311-1092
+title: Supreme Management Architect
+userPassword: yelkraBnam
+uid: Sangman_Barkley
+givenName: Sangman
+mail: Sangman_Barkley@example.com
+carLicense: Q6NXZYM
+departmentNumber: 1502
+employeeType: Temp
+homePhone: +1 206 308-5959
+initials: S. B.
+mobile: +1 510 445-5807
+pager: +1 71 371-2984
+manager: cn=Yoshiaki Teo
+secretary: cn=Faunie McDavitt
+roomNumber: 9259
+
+dn: cn=Berte Pinto-Lobo, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Berte Pinto-Lobo
+sn: Pinto-Lobo
+description: This is Berte Pinto-Lobo's description
+facsimileTelephoneNumber: +1 415 421-8614
+l: Sunnyvale
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 586
+telephoneNumber: +1 303 613-7314
+title: Elite Human Resources Consultant
+userPassword: oboL-otniP
+uid: Berte_Pinto-Lobo
+givenName: Berte
+mail: Berte_Pinto-Lobo@example.com
+carLicense: SLA93UX
+departmentNumber: 8395
+employeeType: Temp
+homePhone: +1 510 689-1651
+initials: B. P.
+mobile: +1 303 923-4172
+pager: +1 818 454-1061
+manager: cn=Vax Venier
+secretary: cn=Dorella Gabbai
+roomNumber: 6290
+
+dn: cn=Charene Lawlis, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Charene Lawlis
+sn: Lawlis
+description: This is Charene Lawlis's description
+facsimileTelephoneNumber: +1 303 652-1726
+l: Milpitas
+ou: Management
+postalAddress: example$Management$Dept # 464
+telephoneNumber: +1 303 648-1740
+title: Master Management Admin
+userPassword: silwaLener
+uid: Charene_Lawlis
+givenName: Charene
+mail: Charene_Lawlis@example.com
+carLicense: BDWA5OA
+departmentNumber: 1826
+employeeType: Normal
+homePhone: +1 213 762-9479
+initials: C. L.
+mobile: +1 71 685-9600
+pager: +1 510 925-1628
+manager: cn=Aggie Davis
+secretary: cn=Corenda Crowley
+roomNumber: 2260
+
+dn: cn=Electra McIntosh, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Electra McIntosh
+sn: McIntosh
+description: This is Electra McIntosh's description
+facsimileTelephoneNumber: +1 71 826-9150
+l: San Jose
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 324
+telephoneNumber: +1 303 780-4869
+title: Associate Janitorial Dictator
+userPassword: hsotnIcMar
+uid: Electra_McIntosh
+givenName: Electra
+mail: Electra_McIntosh@example.com
+carLicense: G2N34UI
+departmentNumber: 6666
+employeeType: Employee
+homePhone: +1 71 498-7760
+initials: E. M.
+mobile: +1 213 978-7520
+pager: +1 408 921-1366
+manager: cn=Bue Arnold
+secretary: cn=Stephane Alexander
+roomNumber: 812
+
+dn: cn=Kaleena Drwiega, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kaleena Drwiega
+sn: Drwiega
+description: This is Kaleena Drwiega's description
+facsimileTelephoneNumber: +1 213 556-1110
+l: Milpitas
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 970
+telephoneNumber: +1 213 592-2450
+title: Elite Human Resources Stooge
+userPassword: ageiwrDane
+uid: Kaleena_Drwiega
+givenName: Kaleena
+mail: Kaleena_Drwiega@example.com
+carLicense: TFWVQMD
+departmentNumber: 7193
+employeeType: Manager
+homePhone: +1 818 367-1341
+initials: K. D.
+mobile: +1 71 947-7132
+pager: +1 804 900-7534
+manager: cn=Tammara Limeina
+secretary: cn=Zeina Pickett
+roomNumber: 434
+
+dn: cn=Babs Grund, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Babs Grund
+sn: Grund
+description: This is Babs Grund's description
+facsimileTelephoneNumber: +1 510 823-7408
+l: Santa Clara
+ou: Product Development
+postalAddress: example$Product Development$Dept # 415
+telephoneNumber: +1 415 512-3661
+title: Associate Product Development Dictator
+userPassword: dnurGsbaB
+uid: Babs_Grund
+givenName: Babs
+mail: Babs_Grund@example.com
+carLicense: UUZT6F2
+departmentNumber: 1308
+employeeType: Normal
+homePhone: +1 510 358-5270
+initials: B. G.
+mobile: +1 71 620-7831
+pager: +1 71 797-5416
+manager: cn=Katuscha Plotter
+secretary: cn=Georgianne Varley
+roomNumber: 4867
+
+dn: cn=Tsing Kenyon, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tsing Kenyon
+sn: Kenyon
+description: This is Tsing Kenyon's description
+facsimileTelephoneNumber: +1 71 116-2416
+l: Emeryville
+ou: Management
+postalAddress: example$Management$Dept # 987
+telephoneNumber: +1 510 333-9492
+title: Associate Management Mascot
+userPassword: noyneKgnis
+uid: Tsing_Kenyon
+givenName: Tsing
+mail: Tsing_Kenyon@example.com
+carLicense: JA6N0PI
+departmentNumber: 5759
+employeeType: Employee
+homePhone: +1 71 783-3243
+initials: T. K.
+mobile: +1 206 751-3172
+pager: +1 415 153-1942
+manager: cn=Doris De Anda
+secretary: cn=Kimberli Rettie
+roomNumber: 1812
+
+dn: cn=Christin Franzky, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Christin Franzky
+sn: Franzky
+description: This is Christin Franzky's description
+facsimileTelephoneNumber: +1 510 121-1960
+l: Redmond
+ou: Management
+postalAddress: example$Management$Dept # 716
+telephoneNumber: +1 206 481-8825
+title: Chief Management Czar
+userPassword: ykznarFnit
+uid: Christin_Franzky
+givenName: Christin
+mail: Christin_Franzky@example.com
+carLicense: U2SBSAF
+departmentNumber: 7533
+employeeType: Contract
+homePhone: +1 818 581-4701
+initials: C. F.
+mobile: +1 213 596-2846
+pager: +1 818 655-1122
+manager: cn=Randolph Puent
+secretary: cn=Jacquie Wayling
+roomNumber: 1350
+
+dn: cn=Tammi Kempster, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tammi Kempster
+sn: Kempster
+description: This is Tammi Kempster's description
+facsimileTelephoneNumber: +1 415 692-7367
+l: Emeryville
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 77
+telephoneNumber: +1 804 494-2819
+title: Master Product Testing Punk
+userPassword: retspmeKim
+uid: Tammi_Kempster
+givenName: Tammi
+mail: Tammi_Kempster@example.com
+carLicense: 52BQDJJ
+departmentNumber: 4827
+employeeType: Temp
+homePhone: +1 510 747-2181
+initials: T. K.
+mobile: +1 804 510-5316
+pager: +1 804 698-7066
+manager: cn=Igor Houle
+secretary: cn=Nicolea Luzarraga
+roomNumber: 6294
+
+dn: cn=Carmelina Annable, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Carmelina Annable
+sn: Annable
+description: This is Carmelina Annable's description
+facsimileTelephoneNumber: +1 213 894-2569
+l: Emeryville
+ou: Payroll
+postalAddress: example$Payroll$Dept # 879
+telephoneNumber: +1 206 423-3258
+title: Senior Payroll Technician
+userPassword: elbannAani
+uid: Carmelina_Annable
+givenName: Carmelina
+mail: Carmelina_Annable@example.com
+carLicense: I9A2DDS
+departmentNumber: 4424
+employeeType: Manager
+homePhone: +1 510 458-6036
+initials: C. A.
+mobile: +1 408 135-9030
+pager: +1 303 526-5109
+manager: cn=Markus Penn
+secretary: cn=Orenzo Zurl
+roomNumber: 41
+
+dn: cn=Henriette Furmaniak, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Henriette Furmaniak
+sn: Furmaniak
+description: This is Henriette Furmaniak's description
+facsimileTelephoneNumber: +1 71 381-8641
+l: Milpitas
+ou: Payroll
+postalAddress: example$Payroll$Dept # 143
+telephoneNumber: +1 415 487-9860
+title: Associate Payroll Evangelist
+userPassword: kainamruFe
+uid: Henriette_Furmaniak
+givenName: Henriette
+mail: Henriette_Furmaniak@example.com
+carLicense: S681U1S
+departmentNumber: 7396
+employeeType: Employee
+homePhone: +1 510 759-3104
+initials: H. F.
+mobile: +1 303 359-1627
+pager: +1 303 591-8973
+manager: cn=Pankaj Rakochy
+secretary: cn=Lily Sergi
+roomNumber: 8694
+
+dn: cn=Gracie Waddell, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gracie Waddell
+sn: Waddell
+description: This is Gracie Waddell's description
+facsimileTelephoneNumber: +1 510 574-3746
+l: San Mateo
+ou: Management
+postalAddress: example$Management$Dept # 794
+telephoneNumber: +1 408 874-8719
+title: Master Management Technician
+userPassword: lleddaWeic
+uid: Gracie_Waddell
+givenName: Gracie
+mail: Gracie_Waddell@example.com
+carLicense: QO92SJI
+departmentNumber: 4612
+employeeType: Contract
+homePhone: +1 303 461-8149
+initials: G. W.
+mobile: +1 510 823-2368
+pager: +1 303 223-8915
+manager: cn=Kui Slusser
+secretary: cn=Mick Villella
+roomNumber: 1427
+
+dn: cn=Roana Subsara, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Roana Subsara
+sn: Subsara
+description: This is Roana Subsara's description
+facsimileTelephoneNumber: +1 408 331-2806
+l: Redmond
+ou: Peons
+postalAddress: example$Peons$Dept # 893
+telephoneNumber: +1 213 136-4389
+title: Master Peons Manager
+userPassword: arasbuSana
+uid: Roana_Subsara
+givenName: Roana
+mail: Roana_Subsara@example.com
+carLicense: TO5CJSA
+departmentNumber: 7488
+employeeType: Temp
+homePhone: +1 213 416-6297
+initials: R. S.
+mobile: +1 408 316-7305
+pager: +1 71 890-2897
+manager: cn=Blaine Metheny
+secretary: cn=Kirk Shackleford
+roomNumber: 5080
+
+dn: cn=Eachelle Greenfield, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Eachelle Greenfield
+sn: Greenfield
+description: This is Eachelle Greenfield's description
+facsimileTelephoneNumber: +1 804 341-9966
+l: San Jose
+ou: Payroll
+postalAddress: example$Payroll$Dept # 538
+telephoneNumber: +1 804 389-8723
+title: Chief Payroll Madonna
+userPassword: dleifneerG
+uid: Eachelle_Greenfield
+givenName: Eachelle
+mail: Eachelle_Greenfield@example.com
+carLicense: C8Q1PWA
+departmentNumber: 8187
+employeeType: Contract
+homePhone: +1 303 376-3297
+initials: E. G.
+mobile: +1 303 601-8532
+pager: +1 818 361-9990
+manager: cn=Marin Levo
+secretary: cn=Ngai Piersol
+roomNumber: 4123
+
+dn: cn=Achamma Akyurekli, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Achamma Akyurekli
+sn: Akyurekli
+description: This is Achamma Akyurekli's description
+facsimileTelephoneNumber: +1 71 296-2104
+l: Cupertino
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 797
+telephoneNumber: +1 206 754-7478
+title: Junior Product Testing Grunt
+userPassword: ilkeruykAa
+uid: Achamma_Akyurekli
+givenName: Achamma
+mail: Achamma_Akyurekli@example.com
+carLicense: M9DOR0U
+departmentNumber: 9817
+employeeType: Manager
+homePhone: +1 206 801-8117
+initials: A. A.
+mobile: +1 408 356-5818
+pager: +1 206 432-6175
+manager: cn=Cloe Heinzman
+secretary: cn=Ayda Khadbai
+roomNumber: 1703
+
+dn: cn=Yuan Dept, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Yuan Dept
+sn: Dept
+description: This is Yuan Dept's description
+facsimileTelephoneNumber: +1 213 176-6815
+l: Cambridge
+ou: Management
+postalAddress: example$Management$Dept # 726
+telephoneNumber: +1 71 938-9616
+title: Junior Management Stooge
+userPassword: tpeDnauY
+uid: Yuan_Dept
+givenName: Yuan
+mail: Yuan_Dept@example.com
+carLicense: K42HCYP
+departmentNumber: 6193
+employeeType: Employee
+homePhone: +1 206 493-9728
+initials: Y. D.
+mobile: +1 213 464-7273
+pager: +1 408 405-3776
+manager: cn=Nadeen Testagc
+secretary: cn=Brittney Lasson
+roomNumber: 3127
+
+dn: cn=Darrell Jeffries, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Darrell Jeffries
+sn: Jeffries
+description: This is Darrell Jeffries's description
+facsimileTelephoneNumber: +1 804 437-6265
+l: San Francisco
+ou: Payroll
+postalAddress: example$Payroll$Dept # 520
+telephoneNumber: +1 818 621-9829
+title: Associate Payroll Vice President
+userPassword: seirffeJll
+uid: Darrell_Jeffries
+givenName: Darrell
+mail: Darrell_Jeffries@example.com
+carLicense: 2MNPEY1
+departmentNumber: 1683
+employeeType: Normal
+homePhone: +1 303 414-7312
+initials: D. J.
+mobile: +1 303 978-9308
+pager: +1 510 100-5619
+manager: cn=Gunter Chaikowsky
+secretary: cn=Wing-Ki Hinds
+roomNumber: 9309
+
+dn: cn=Nora Kellogg, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Nora Kellogg
+sn: Kellogg
+description: This is Nora Kellogg's description
+facsimileTelephoneNumber: +1 804 339-9336
+l: Palo Alto
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 224
+telephoneNumber: +1 804 755-9678
+title: Associate Janitorial Mascot
+userPassword: ggolleKaro
+uid: Nora_Kellogg
+givenName: Nora
+mail: Nora_Kellogg@example.com
+carLicense: SBRZ9Z3
+departmentNumber: 8304
+employeeType: Normal
+homePhone: +1 71 790-5692
+initials: N. K.
+mobile: +1 408 133-4469
+pager: +1 71 995-7187
+manager: cn=Blanch Caudle
+secretary: cn=Nga Hawryluk
+roomNumber: 6925
+
+dn: cn=Lorna Maciejewski, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lorna Maciejewski
+sn: Maciejewski
+description: This is Lorna Maciejewski's description
+facsimileTelephoneNumber: +1 818 973-9335
+l: Orem
+ou: Management
+postalAddress: example$Management$Dept # 298
+telephoneNumber: +1 818 860-4997
+title: Supreme Management Technician
+userPassword: ikswejeica
+uid: Lorna_Maciejewski
+givenName: Lorna
+mail: Lorna_Maciejewski@example.com
+carLicense: 3746WMS
+departmentNumber: 8407
+employeeType: Normal
+homePhone: +1 415 927-6953
+initials: L. M.
+mobile: +1 415 162-9536
+pager: +1 415 272-3708
+manager: cn=Usa Horton
+secretary: cn=Kin Jachym
+roomNumber: 9625
+
+dn: cn=Tavis Bees, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tavis Bees
+sn: Bees
+description: This is Tavis Bees's description
+facsimileTelephoneNumber: +1 213 563-5454
+l: Fremont
+ou: Administrative
+postalAddress: example$Administrative$Dept # 831
+telephoneNumber: +1 303 132-3422
+title: Chief Administrative Stooge
+userPassword: seeBsivaT
+uid: Tavis_Bees
+givenName: Tavis
+mail: Tavis_Bees@example.com
+carLicense: ZPVAGYM
+departmentNumber: 5317
+employeeType: Manager
+homePhone: +1 818 168-1951
+initials: T. B.
+mobile: +1 206 942-2455
+pager: +1 804 951-6491
+manager: cn=Mehmet Kozlowski
+secretary: cn=Fawnia Cozart
+roomNumber: 801
+
+dn: cn=Blancha Bejar, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Blancha Bejar
+sn: Bejar
+description: This is Blancha Bejar's description
+facsimileTelephoneNumber: +1 510 638-9150
+l: Menlo Park
+ou: Administrative
+postalAddress: example$Administrative$Dept # 388
+telephoneNumber: +1 213 100-4750
+title: Senior Administrative Fellow
+userPassword: rajeBahcna
+uid: Blancha_Bejar
+givenName: Blancha
+mail: Blancha_Bejar@example.com
+carLicense: RY0J2ZW
+departmentNumber: 6859
+employeeType: Contract
+homePhone: +1 408 563-2329
+initials: B. B.
+mobile: +1 510 556-2098
+pager: +1 71 577-1582
+manager: cn=Binnie Boisvert
+secretary: cn=Anil Thuesen
+roomNumber: 8932
+
+dn: cn=Atique Hoscheid, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Atique Hoscheid
+sn: Hoscheid
+description: This is Atique Hoscheid's description
+facsimileTelephoneNumber: +1 415 301-7675
+l: Alameda
+ou: Accounting
+postalAddress: example$Accounting$Dept # 675
+telephoneNumber: +1 206 723-9273
+title: Associate Accounting Fellow
+userPassword: diehcsoHeu
+uid: Atique_Hoscheid
+givenName: Atique
+mail: Atique_Hoscheid@example.com
+carLicense: TYEQ4YU
+departmentNumber: 3523
+employeeType: Manager
+homePhone: +1 213 428-3966
+initials: A. H.
+mobile: +1 303 324-8556
+pager: +1 408 804-9687
+manager: cn=Page Katibian
+secretary: cn=Conni Kapella
+roomNumber: 409
+
+dn: cn=Garland Donner, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Garland Donner
+sn: Donner
+description: This is Garland Donner's description
+facsimileTelephoneNumber: +1 408 225-3831
+l: Emeryville
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 934
+telephoneNumber: +1 408 198-6277
+title: Junior Human Resources Dictator
+userPassword: rennoDdnal
+uid: Garland_Donner
+givenName: Garland
+mail: Garland_Donner@example.com
+carLicense: UKPQ6TP
+departmentNumber: 3525
+employeeType: Manager
+homePhone: +1 804 136-4416
+initials: G. D.
+mobile: +1 415 506-2934
+pager: +1 510 829-8068
+manager: cn=Theresita Haslach
+secretary: cn=Debbie Bennett
+roomNumber: 9856
+
+dn: cn=Pauli Spooner, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Pauli Spooner
+sn: Spooner
+description: This is Pauli Spooner's description
+facsimileTelephoneNumber: +1 213 662-8018
+l: San Francisco
+ou: Administrative
+postalAddress: example$Administrative$Dept # 565
+telephoneNumber: +1 818 810-3512
+title: Supreme Administrative President
+userPassword: renoopSilu
+uid: Pauli_Spooner
+givenName: Pauli
+mail: Pauli_Spooner@example.com
+carLicense: C7FXNKP
+departmentNumber: 8721
+employeeType: Normal
+homePhone: +1 818 905-5332
+initials: P. S.
+mobile: +1 415 269-1419
+pager: +1 71 182-9029
+manager: cn=Aloise McSheffrey
+secretary: cn=Pat Turcot
+roomNumber: 2471
+
+dn: cn=Hal Erguven, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Hal Erguven
+sn: Erguven
+description: This is Hal Erguven's description
+facsimileTelephoneNumber: +1 818 607-5047
+l: Cupertino
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 727
+telephoneNumber: +1 818 513-4628
+title: Junior Janitorial Evangelist
+userPassword: nevugrElaH
+uid: Hal_Erguven
+givenName: Hal
+mail: Hal_Erguven@example.com
+carLicense: G83WWRQ
+departmentNumber: 2708
+employeeType: Employee
+homePhone: +1 408 529-9091
+initials: H. E.
+mobile: +1 71 249-8217
+pager: +1 510 115-6064
+manager: cn=Mitch Stroud
+secretary: cn=Gracinda Schneiders
+roomNumber: 337
+
+dn: cn=Tri Streibel, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tri Streibel
+sn: Streibel
+description: This is Tri Streibel's description
+facsimileTelephoneNumber: +1 408 428-9232
+l: Cambridge
+ou: Accounting
+postalAddress: example$Accounting$Dept # 276
+telephoneNumber: +1 206 512-2449
+title: Supreme Accounting Czar
+userPassword: lebiertSir
+uid: Tri_Streibel
+givenName: Tri
+mail: Tri_Streibel@example.com
+carLicense: 5RDIPP4
+departmentNumber: 6292
+employeeType: Contract
+homePhone: +1 818 161-6473
+initials: T. S.
+mobile: +1 804 607-8237
+pager: +1 510 747-8010
+manager: cn=Dorothy Zonner
+secretary: cn=Ame Pasher
+roomNumber: 7074
+
+dn: cn=Lorianna Chern, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lorianna Chern
+sn: Chern
+description: This is Lorianna Chern's description
+facsimileTelephoneNumber: +1 303 351-4018
+l: Fremont
+ou: Accounting
+postalAddress: example$Accounting$Dept # 739
+telephoneNumber: +1 71 225-7973
+title: Elite Accounting Pinhead
+userPassword: nrehCannai
+uid: Lorianna_Chern
+givenName: Lorianna
+mail: Lorianna_Chern@example.com
+carLicense: NH1AY8N
+departmentNumber: 5809
+employeeType: Manager
+homePhone: +1 71 595-9477
+initials: L. C.
+mobile: +1 415 831-4350
+pager: +1 303 842-9088
+manager: cn=Anne-corinne Monn
+secretary: cn=Teena Vivian
+roomNumber: 2080
+
+dn: cn=Ava Rtpbuild, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ava Rtpbuild
+sn: Rtpbuild
+description: This is Ava Rtpbuild's description
+facsimileTelephoneNumber: +1 804 326-2739
+l: Mountain View
+ou: Accounting
+postalAddress: example$Accounting$Dept # 591
+telephoneNumber: +1 303 107-7347
+title: Master Accounting Architect
+userPassword: dliubptRav
+uid: Ava_Rtpbuild
+givenName: Ava
+mail: Ava_Rtpbuild@example.com
+carLicense: BCQVE7T
+departmentNumber: 4118
+employeeType: Contract
+homePhone: +1 408 349-5067
+initials: A. R.
+mobile: +1 818 415-2641
+pager: +1 804 895-6044
+manager: cn=Maxie Schmitt
+secretary: cn=Fwpreg Goller
+roomNumber: 7304
+
+dn: cn=Claudia McMasters, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Claudia McMasters
+sn: McMasters
+description: This is Claudia McMasters's description
+facsimileTelephoneNumber: +1 408 394-1817
+l: Emeryville
+ou: Product Development
+postalAddress: example$Product Development$Dept # 449
+telephoneNumber: +1 213 129-8408
+title: Chief Product Development Technician
+userPassword: sretsaMcMa
+uid: Claudia_McMasters
+givenName: Claudia
+mail: Claudia_McMasters@example.com
+carLicense: JPQO9E8
+departmentNumber: 2709
+employeeType: Employee
+homePhone: +1 415 358-9077
+initials: C. M.
+mobile: +1 206 203-7937
+pager: +1 415 233-4827
+manager: cn=Pivert Cantlie
+secretary: cn=Bennet Bilsborough
+roomNumber: 3381
+
+dn: cn=Attilio Cheshire, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Attilio Cheshire
+sn: Cheshire
+description: This is Attilio Cheshire's description
+facsimileTelephoneNumber: +1 818 795-3209
+l: Redmond
+ou: Accounting
+postalAddress: example$Accounting$Dept # 729
+telephoneNumber: +1 71 793-1301
+title: Elite Accounting Mascot
+userPassword: erihsehCoi
+uid: Attilio_Cheshire
+givenName: Attilio
+mail: Attilio_Cheshire@example.com
+carLicense: O8ONIEG
+departmentNumber: 966
+employeeType: Temp
+homePhone: +1 206 216-9992
+initials: A. C.
+mobile: +1 71 812-7193
+pager: +1 408 656-5816
+manager: cn=Ved Pittam
+secretary: cn=Tresrch Flickinger
+roomNumber: 5500
+
+dn: cn=Jolyn Sturrock, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jolyn Sturrock
+sn: Sturrock
+description: This is Jolyn Sturrock's description
+facsimileTelephoneNumber: +1 415 350-6718
+l: Redmond
+ou: Peons
+postalAddress: example$Peons$Dept # 107
+telephoneNumber: +1 818 723-8455
+title: Master Peons Dictator
+userPassword: kcorrutSny
+uid: Jolyn_Sturrock
+givenName: Jolyn
+mail: Jolyn_Sturrock@example.com
+carLicense: GWY1SNL
+departmentNumber: 62
+employeeType: Manager
+homePhone: +1 206 158-7057
+initials: J. S.
+mobile: +1 818 574-2111
+pager: +1 408 190-8741
+manager: cn=Manda Santi
+secretary: cn=Ling-Yue Smolin
+roomNumber: 6698
+
+dn: cn=Gerber Wefers, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gerber Wefers
+sn: Wefers
+description: This is Gerber Wefers's description
+facsimileTelephoneNumber: +1 213 875-4086
+l: Cupertino
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 512
+telephoneNumber: +1 818 743-7160
+title: Junior Product Testing Artist
+userPassword: srefeWrebr
+uid: Gerber_Wefers
+givenName: Gerber
+mail: Gerber_Wefers@example.com
+carLicense: 3S3J6KQ
+departmentNumber: 8016
+employeeType: Manager
+homePhone: +1 71 661-9616
+initials: G. W.
+mobile: +1 408 463-5608
+pager: +1 408 293-1049
+manager: cn=Tsugio Alms
+secretary: cn=Leonanie Gregory
+roomNumber: 94
+
+dn: cn=May Mulqueen, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: May Mulqueen
+sn: Mulqueen
+description: This is May Mulqueen's description
+facsimileTelephoneNumber: +1 213 224-4633
+l: Mountain View
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 626
+telephoneNumber: +1 408 546-2498
+title: Associate Janitorial Stooge
+userPassword: neeuqluMya
+uid: May_Mulqueen
+givenName: May
+mail: May_Mulqueen@example.com
+carLicense: W8W9FVI
+departmentNumber: 7335
+employeeType: Manager
+homePhone: +1 818 569-7514
+initials: M. M.
+mobile: +1 213 692-2302
+pager: +1 804 311-4290
+manager: cn=Joellen Volker
+secretary: cn=Katarina Alms
+roomNumber: 3635
+
+dn: cn=Klaus Wessenberg, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Klaus Wessenberg
+sn: Wessenberg
+description: This is Klaus Wessenberg's description
+facsimileTelephoneNumber: +1 510 761-3757
+l: Milpitas
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 998
+telephoneNumber: +1 213 728-9369
+title: Chief Human Resources Janitor
+userPassword: grebnesseW
+uid: Klaus_Wessenberg
+givenName: Klaus
+mail: Klaus_Wessenberg@example.com
+carLicense: 89PC05L
+departmentNumber: 9267
+employeeType: Employee
+homePhone: +1 303 177-7913
+initials: K. W.
+mobile: +1 408 302-2211
+pager: +1 303 393-9789
+manager: cn=Kien-Nghiep Chhabria
+secretary: cn=Tyke Bennatt
+roomNumber: 1510
+
+dn: cn=Zero Astorino, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Zero Astorino
+sn: Astorino
+description: This is Zero Astorino's description
+facsimileTelephoneNumber: +1 206 749-3370
+l: Sunnyvale
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 211
+telephoneNumber: +1 804 389-4563
+title: Senior Product Testing Stooge
+userPassword: onirotsAor
+uid: Zero_Astorino
+givenName: Zero
+mail: Zero_Astorino@example.com
+carLicense: MAZB0EZ
+departmentNumber: 8638
+employeeType: Contract
+homePhone: +1 206 963-3254
+initials: Z. A.
+mobile: +1 206 464-7408
+pager: +1 206 210-3740
+manager: cn=Emmalynne Van Schyndel
+secretary: cn=Muni Maidens
+roomNumber: 2686
+
+dn: cn=Lou Pezzoni, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lou Pezzoni
+sn: Pezzoni
+description: This is Lou Pezzoni's description
+facsimileTelephoneNumber: +1 213 731-5038
+l: Armonk
+ou: Management
+postalAddress: example$Management$Dept # 372
+telephoneNumber: +1 818 444-2323
+title: Chief Management Stooge
+userPassword: inozzePuoL
+uid: Lou_Pezzoni
+givenName: Lou
+mail: Lou_Pezzoni@example.com
+carLicense: 2WP98DC
+departmentNumber: 1238
+employeeType: Temp
+homePhone: +1 408 604-7030
+initials: L. P.
+mobile: +1 213 624-5253
+pager: +1 213 575-8354
+manager: cn=Alys Booker
+secretary: cn=Tan Albright
+roomNumber: 8702
+
+dn: cn=Nelie Beavis, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Nelie Beavis
+sn: Beavis
+description: This is Nelie Beavis's description
+facsimileTelephoneNumber: +1 206 769-7925
+l: Cupertino
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 318
+telephoneNumber: +1 510 542-1388
+title: Senior Product Testing Sales Rep
+userPassword: sivaeBeile
+uid: Nelie_Beavis
+givenName: Nelie
+mail: Nelie_Beavis@example.com
+carLicense: NAEH8SQ
+departmentNumber: 5361
+employeeType: Normal
+homePhone: +1 804 554-9766
+initials: N. B.
+mobile: +1 213 993-7114
+pager: +1 206 955-1047
+manager: cn=Elysha Poff
+secretary: cn=Maris Govindasamy
+roomNumber: 2030
+
+dn: cn=Dita Stinson, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dita Stinson
+sn: Stinson
+description: This is Dita Stinson's description
+facsimileTelephoneNumber: +1 71 262-1356
+l: Sunnyvale
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 386
+telephoneNumber: +1 303 869-4222
+title: Supreme Product Testing Admin
+userPassword: nosnitSati
+uid: Dita_Stinson
+givenName: Dita
+mail: Dita_Stinson@example.com
+carLicense: 5S9HDUI
+departmentNumber: 7675
+employeeType: Normal
+homePhone: +1 510 148-7418
+initials: D. S.
+mobile: +1 415 530-1870
+pager: +1 415 913-1652
+manager: cn=Kieron Desmond
+secretary: cn=Remington Kosiorska
+roomNumber: 8364
+
+dn: cn=Ijff Swails, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ijff Swails
+sn: Swails
+description: This is Ijff Swails's description
+facsimileTelephoneNumber: +1 408 230-6699
+l: Alameda
+ou: Peons
+postalAddress: example$Peons$Dept # 659
+telephoneNumber: +1 804 920-5135
+title: Elite Peons Engineer
+userPassword: sliawSffjI
+uid: Ijff_Swails
+givenName: Ijff
+mail: Ijff_Swails@example.com
+carLicense: K53PGCQ
+departmentNumber: 285
+employeeType: Employee
+homePhone: +1 408 231-4267
+initials: I. S.
+mobile: +1 818 439-9844
+pager: +1 213 838-9388
+manager: cn=Parminder Gougeon
+secretary: cn=Raleigh Gallenbeck
+roomNumber: 4580
+
+dn: cn=Leon Kostyniuk, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Leon Kostyniuk
+sn: Kostyniuk
+description: This is Leon Kostyniuk's description
+facsimileTelephoneNumber: +1 206 948-7104
+l: Armonk
+ou: Administrative
+postalAddress: example$Administrative$Dept # 510
+telephoneNumber: +1 510 228-7469
+title: Associate Administrative Madonna
+userPassword: kuinytsoKn
+uid: Leon_Kostyniuk
+givenName: Leon
+mail: Leon_Kostyniuk@example.com
+carLicense: 8FLR7LX
+departmentNumber: 4349
+employeeType: Employee
+homePhone: +1 206 216-4381
+initials: L. K.
+mobile: +1 408 315-5005
+pager: +1 818 788-4357
+manager: cn=Viqar Traylor
+secretary: cn=Gerianna Cousineau
+roomNumber: 5375
+
+dn: cn=Gabriel Isley, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gabriel Isley
+sn: Isley
+description: This is Gabriel Isley's description
+facsimileTelephoneNumber: +1 303 806-2713
+l: Sunnyvale
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 946
+telephoneNumber: +1 408 964-7589
+title: Supreme Human Resources Janitor
+userPassword: yelsIleirb
+uid: Gabriel_Isley
+givenName: Gabriel
+mail: Gabriel_Isley@example.com
+carLicense: K86YHF5
+departmentNumber: 6726
+employeeType: Normal
+homePhone: +1 303 751-6077
+initials: G. I.
+mobile: +1 206 439-1191
+pager: +1 303 691-9922
+manager: cn=Cefee Phan
+secretary: cn=Belen Champsi
+roomNumber: 9094
+
+dn: cn=Tally Yandell, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tally Yandell
+sn: Yandell
+description: This is Tally Yandell's description
+facsimileTelephoneNumber: +1 213 466-4179
+l: Cambridge
+ou: Payroll
+postalAddress: example$Payroll$Dept # 73
+telephoneNumber: +1 804 568-1143
+title: Master Payroll Figurehead
+userPassword: llednaYyll
+uid: Tally_Yandell
+givenName: Tally
+mail: Tally_Yandell@example.com
+carLicense: WA0Z8Y8
+departmentNumber: 6442
+employeeType: Employee
+homePhone: +1 408 141-4736
+initials: T. Y.
+mobile: +1 818 290-8012
+pager: +1 303 680-5641
+manager: cn=Kathryne Silva
+secretary: cn=Willy Rorie
+roomNumber: 4444
+
+dn: cn=Enis Fullager, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Enis Fullager
+sn: Fullager
+description: This is Enis Fullager's description
+facsimileTelephoneNumber: +1 804 538-2070
+l: Alameda
+ou: Product Development
+postalAddress: example$Product Development$Dept # 391
+telephoneNumber: +1 71 726-4802
+title: Chief Product Development Stooge
+userPassword: regalluFsi
+uid: Enis_Fullager
+givenName: Enis
+mail: Enis_Fullager@example.com
+carLicense: T3ACW5H
+departmentNumber: 5379
+employeeType: Contract
+homePhone: +1 206 919-9700
+initials: E. F.
+mobile: +1 818 223-9749
+pager: +1 804 988-9659
+manager: cn=Dynah Owensby
+secretary: cn=Butch Chantal
+roomNumber: 3261
+
+dn: cn=Joly Tham, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Joly Tham
+sn: Tham
+description: This is Joly Tham's description
+facsimileTelephoneNumber: +1 415 826-7425
+l: Emeryville
+ou: Payroll
+postalAddress: example$Payroll$Dept # 873
+telephoneNumber: +1 303 248-9970
+title: Chief Payroll Evangelist
+userPassword: mahTyloJ
+uid: Joly_Tham
+givenName: Joly
+mail: Joly_Tham@example.com
+carLicense: 24ZOPUT
+departmentNumber: 181
+employeeType: Manager
+homePhone: +1 303 472-7110
+initials: J. T.
+mobile: +1 408 958-3175
+pager: +1 804 283-2291
+manager: cn=Arthur Diradmin
+secretary: cn=Jessa Starsdps
+roomNumber: 7894
+
+dn: cn=Sydney Chenoweth, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sydney Chenoweth
+sn: Chenoweth
+description: This is Sydney Chenoweth's description
+facsimileTelephoneNumber: +1 804 466-1282
+l: Cupertino
+ou: Payroll
+postalAddress: example$Payroll$Dept # 941
+telephoneNumber: +1 71 235-3143
+title: Associate Payroll Czar
+userPassword: htewonehCy
+uid: Sydney_Chenoweth
+givenName: Sydney
+mail: Sydney_Chenoweth@example.com
+carLicense: WGBDPP1
+departmentNumber: 5361
+employeeType: Contract
+homePhone: +1 408 134-7132
+initials: S. C.
+mobile: +1 818 261-9758
+pager: +1 213 254-6306
+manager: cn=Helaine Hopper
+secretary: cn=JoLee Schipper
+roomNumber: 5967
+
+dn: cn=Jeffrey Silieff, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jeffrey Silieff
+sn: Silieff
+description: This is Jeffrey Silieff's description
+facsimileTelephoneNumber: +1 415 858-1535
+l: Alameda
+ou: Management
+postalAddress: example$Management$Dept # 966
+telephoneNumber: +1 415 630-5305
+title: Senior Management Dictator
+userPassword: ffeiliSyer
+uid: Jeffrey_Silieff
+givenName: Jeffrey
+mail: Jeffrey_Silieff@example.com
+carLicense: 895WWI4
+departmentNumber: 4908
+employeeType: Employee
+homePhone: +1 71 579-7062
+initials: J. S.
+mobile: +1 408 404-2467
+pager: +1 415 475-9295
+manager: cn=Sami Kimler
+secretary: cn=Faustina Sandberg
+roomNumber: 1846
+
+dn: cn=Kenny Michaels, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kenny Michaels
+sn: Michaels
+description: This is Kenny Michaels's description
+facsimileTelephoneNumber: +1 818 657-3235
+l: Cupertino
+ou: Planning
+postalAddress: example$Planning$Dept # 256
+telephoneNumber: +1 71 874-9999
+title: Senior Planning Punk
+userPassword: sleahciMyn
+uid: Kenny_Michaels
+givenName: Kenny
+mail: Kenny_Michaels@example.com
+carLicense: 7VQOB9I
+departmentNumber: 3466
+employeeType: Employee
+homePhone: +1 415 228-6489
+initials: K. M.
+mobile: +1 510 965-8849
+pager: +1 818 867-1962
+manager: cn=Guylaine Knapton
+secretary: cn=Van-King Brett
+roomNumber: 244
+
+dn: cn=Partap Heller, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Partap Heller
+sn: Heller
+description: This is Partap Heller's description
+facsimileTelephoneNumber: +1 818 220-6830
+l: Armonk
+ou: Accounting
+postalAddress: example$Accounting$Dept # 363
+telephoneNumber: +1 818 946-5877
+title: Master Accounting Madonna
+userPassword: relleHpatr
+uid: Partap_Heller
+givenName: Partap
+mail: Partap_Heller@example.com
+carLicense: H0HCYO6
+departmentNumber: 5625
+employeeType: Manager
+homePhone: +1 213 623-6672
+initials: P. H.
+mobile: +1 408 316-1890
+pager: +1 71 477-6018
+manager: cn=Idalina Yohe
+secretary: cn=Jaynell Quevillon
+roomNumber: 1482
+
+dn: cn=Mellie Ladouceur, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Mellie Ladouceur
+sn: Ladouceur
+description: This is Mellie Ladouceur's description
+facsimileTelephoneNumber: +1 206 693-4680
+l: Cambridge
+ou: Peons
+postalAddress: example$Peons$Dept # 875
+telephoneNumber: +1 510 169-8383
+title: Chief Peons Grunt
+userPassword: ruecuodaLe
+uid: Mellie_Ladouceur
+givenName: Mellie
+mail: Mellie_Ladouceur@example.com
+carLicense: NFG1QY6
+departmentNumber: 5449
+employeeType: Employee
+homePhone: +1 510 806-6400
+initials: M. L.
+mobile: +1 206 797-4296
+pager: +1 510 377-2981
+manager: cn=Micah PATCOR
+secretary: cn=Bhupinder Hiraki
+roomNumber: 9379
+
+dn: cn=Aloysia Running, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Aloysia Running
+sn: Running
+description: This is Aloysia Running's description
+facsimileTelephoneNumber: +1 415 135-9978
+l: Milpitas
+ou: Accounting
+postalAddress: example$Accounting$Dept # 203
+telephoneNumber: +1 408 872-6236
+title: Supreme Accounting Grunt
+userPassword: gninnuRais
+uid: Aloysia_Running
+givenName: Aloysia
+mail: Aloysia_Running@example.com
+carLicense: 3KTA8R0
+departmentNumber: 2824
+employeeType: Employee
+homePhone: +1 303 961-5246
+initials: A. R.
+mobile: +1 213 830-4008
+pager: +1 510 361-3893
+manager: cn=Hattie Chanco
+secretary: cn=Wilfred Stumpf
+roomNumber: 4337
+
+dn: cn=Ellissa Herling, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ellissa Herling
+sn: Herling
+description: This is Ellissa Herling's description
+facsimileTelephoneNumber: +1 206 340-9698
+l: San Mateo
+ou: Planning
+postalAddress: example$Planning$Dept # 259
+telephoneNumber: +1 303 377-6673
+title: Master Planning Engineer
+userPassword: gnilreHass
+uid: Ellissa_Herling
+givenName: Ellissa
+mail: Ellissa_Herling@example.com
+carLicense: ADZM7H9
+departmentNumber: 4088
+employeeType: Normal
+homePhone: +1 415 848-8162
+initials: E. H.
+mobile: +1 510 482-7382
+pager: +1 206 936-3580
+manager: cn=Zbignew Merrick
+secretary: cn=Jean McCurdy
+roomNumber: 147
+
+dn: cn=Valli Sigmon, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Valli Sigmon
+sn: Sigmon
+description: This is Valli Sigmon's description
+facsimileTelephoneNumber: +1 818 423-8154
+l: Redmond
+ou: Peons
+postalAddress: example$Peons$Dept # 333
+telephoneNumber: +1 415 704-4892
+title: Senior Peons Visionary
+userPassword: nomgiSilla
+uid: Valli_Sigmon
+givenName: Valli
+mail: Valli_Sigmon@example.com
+carLicense: KVLVGUN
+departmentNumber: 9890
+employeeType: Normal
+homePhone: +1 408 828-4293
+initials: V. S.
+mobile: +1 303 713-2307
+pager: +1 510 164-7951
+manager: cn=Zarrin Doyon
+secretary: cn=Denice Tufford
+roomNumber: 7410
+
+dn: cn=Karol Standards, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Karol Standards
+sn: Standards
+description: This is Karol Standards's description
+facsimileTelephoneNumber: +1 510 799-5003
+l: Santa Clara
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 812
+telephoneNumber: +1 408 771-5206
+title: Chief Human Resources Dictator
+userPassword: sdradnatSl
+uid: Karol_Standards
+givenName: Karol
+mail: Karol_Standards@example.com
+carLicense: GFT7952
+departmentNumber: 9531
+employeeType: Employee
+homePhone: +1 415 426-1737
+initials: K. S.
+mobile: +1 303 785-6701
+pager: +1 408 142-9151
+manager: cn=Nelie Ananth
+secretary: cn=Buddy Sheffey
+roomNumber: 2785
+
+dn: cn=Ronn Buchanan, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ronn Buchanan
+sn: Buchanan
+description: This is Ronn Buchanan's description
+facsimileTelephoneNumber: +1 71 663-7181
+l: San Francisco
+ou: Management
+postalAddress: example$Management$Dept # 155
+telephoneNumber: +1 408 625-1957
+title: Chief Management Technician
+userPassword: nanahcuBnn
+uid: Ronn_Buchanan
+givenName: Ronn
+mail: Ronn_Buchanan@example.com
+carLicense: NQN6XAT
+departmentNumber: 2350
+employeeType: Temp
+homePhone: +1 415 486-3839
+initials: R. B.
+mobile: +1 408 621-3994
+pager: +1 71 925-1399
+manager: cn=Dee dee Quante
+secretary: cn=Manon Hildebrand
+roomNumber: 8097
+
+dn: cn=Loreta Sherali, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Loreta Sherali
+sn: Sherali
+description: This is Loreta Sherali's description
+facsimileTelephoneNumber: +1 408 265-9408
+l: Redwood Shores
+ou: Product Development
+postalAddress: example$Product Development$Dept # 939
+telephoneNumber: +1 303 715-6655
+title: Elite Product Development Dictator
+userPassword: ilarehSate
+uid: Loreta_Sherali
+givenName: Loreta
+mail: Loreta_Sherali@example.com
+carLicense: 3I7II8B
+departmentNumber: 6786
+employeeType: Manager
+homePhone: +1 804 226-8653
+initials: L. S.
+mobile: +1 206 310-6268
+pager: +1 408 742-8228
+manager: cn=Susi Telke
+secretary: cn=Irice Paperno
+roomNumber: 1030
+
+dn: cn=Martha Soldera, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Martha Soldera
+sn: Soldera
+description: This is Martha Soldera's description
+facsimileTelephoneNumber: +1 415 437-5683
+l: San Francisco
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 809
+telephoneNumber: +1 510 553-7486
+title: Associate Janitorial Manager
+userPassword: aredloSaht
+uid: Martha_Soldera
+givenName: Martha
+mail: Martha_Soldera@example.com
+carLicense: 9WN7ZDP
+departmentNumber: 1418
+employeeType: Temp
+homePhone: +1 415 149-4197
+initials: M. S.
+mobile: +1 818 866-8309
+pager: +1 804 776-2722
+manager: cn=Lothar Meagher
+secretary: cn=Margriet Essig
+roomNumber: 9240
+
+dn: cn=Rachelle Eyers, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rachelle Eyers
+sn: Eyers
+description: This is Rachelle Eyers's description
+facsimileTelephoneNumber: +1 213 966-1936
+l: Redmond
+ou: Payroll
+postalAddress: example$Payroll$Dept # 79
+telephoneNumber: +1 804 109-3535
+title: Junior Payroll Stooge
+userPassword: sreyEelleh
+uid: Rachelle_Eyers
+givenName: Rachelle
+mail: Rachelle_Eyers@example.com
+carLicense: BOOQSXD
+departmentNumber: 1910
+employeeType: Contract
+homePhone: +1 804 444-6218
+initials: R. E.
+mobile: +1 303 515-7109
+pager: +1 415 694-1907
+manager: cn=Atl-Sales Passin
+secretary: cn=DeAnne Breuer
+roomNumber: 833
+
+dn: cn=Elyse Deatrick, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Elyse Deatrick
+sn: Deatrick
+description: This is Elyse Deatrick's description
+facsimileTelephoneNumber: +1 818 499-4169
+l: Santa Clara
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 272
+telephoneNumber: +1 71 829-8603
+title: Associate Product Testing Evangelist
+userPassword: kcirtaeDes
+uid: Elyse_Deatrick
+givenName: Elyse
+mail: Elyse_Deatrick@example.com
+carLicense: SVKWQFG
+departmentNumber: 427
+employeeType: Normal
+homePhone: +1 213 388-3585
+initials: E. D.
+mobile: +1 415 819-7678
+pager: +1 415 682-6240
+manager: cn=Danika Bailloux
+secretary: cn=Purnam Eggleton
+roomNumber: 3700
+
+dn: cn=Zola Testsds, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Zola Testsds
+sn: Testsds
+description: This is Zola Testsds's description
+facsimileTelephoneNumber: +1 415 928-4013
+l: Santa Clara
+ou: Payroll
+postalAddress: example$Payroll$Dept # 841
+telephoneNumber: +1 71 345-8158
+title: Chief Payroll Writer
+userPassword: sdstseTalo
+uid: Zola_Testsds
+givenName: Zola
+mail: Zola_Testsds@example.com
+carLicense: XD87ET6
+departmentNumber: 1258
+employeeType: Normal
+homePhone: +1 213 928-3383
+initials: Z. T.
+mobile: +1 804 903-8412
+pager: +1 510 399-2722
+manager: cn=Dwayne Firerobin
+secretary: cn=Nicolea Testtools
+roomNumber: 7297
+
+dn: cn=Wendye Stillwell, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Wendye Stillwell
+sn: Stillwell
+description: This is Wendye Stillwell's description
+facsimileTelephoneNumber: +1 213 318-7076
+l: Redwood Shores
+ou: Administrative
+postalAddress: example$Administrative$Dept # 540
+telephoneNumber: +1 206 822-6752
+title: Junior Administrative President
+userPassword: llewllitSe
+uid: Wendye_Stillwell
+givenName: Wendye
+mail: Wendye_Stillwell@example.com
+carLicense: 92QBPGR
+departmentNumber: 636
+employeeType: Manager
+homePhone: +1 303 229-4142
+initials: W. S.
+mobile: +1 510 868-2412
+pager: +1 415 633-1667
+manager: cn=Cloris Chouinard
+secretary: cn=Linh Hargadon
+roomNumber: 2645
+
+dn: cn=Beatrix Klaudt, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Beatrix Klaudt
+sn: Klaudt
+description: This is Beatrix Klaudt's description
+facsimileTelephoneNumber: +1 206 749-8513
+l: Menlo Park
+ou: Peons
+postalAddress: example$Peons$Dept # 188
+telephoneNumber: +1 213 452-3220
+title: Senior Peons Developer
+userPassword: tdualKxirt
+uid: Beatrix_Klaudt
+givenName: Beatrix
+mail: Beatrix_Klaudt@example.com
+carLicense: H0A5PNJ
+departmentNumber: 5780
+employeeType: Employee
+homePhone: +1 303 110-9650
+initials: B. K.
+mobile: +1 818 262-2202
+pager: +1 408 698-8828
+manager: cn=Maala D'Antonio
+secretary: cn=Adela Bachecongi
+roomNumber: 9361
+
+dn: cn=Susy Ojerholm, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Susy Ojerholm
+sn: Ojerholm
+description: This is Susy Ojerholm's description
+facsimileTelephoneNumber: +1 206 619-7003
+l: San Jose
+ou: Accounting
+postalAddress: example$Accounting$Dept # 612
+telephoneNumber: +1 213 469-6896
+title: Master Accounting Evangelist
+userPassword: mlohrejOys
+uid: Susy_Ojerholm
+givenName: Susy
+mail: Susy_Ojerholm@example.com
+carLicense: JRHY188
+departmentNumber: 6207
+employeeType: Contract
+homePhone: +1 408 974-6966
+initials: S. O.
+mobile: +1 415 925-7650
+pager: +1 303 841-5776
+manager: cn=Rizwan Rfa
+secretary: cn=Willette Beninger
+roomNumber: 2753
+
+dn: cn=Kare Lugwig, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kare Lugwig
+sn: Lugwig
+description: This is Kare Lugwig's description
+facsimileTelephoneNumber: +1 71 122-2378
+l: Armonk
+ou: Management
+postalAddress: example$Management$Dept # 563
+telephoneNumber: +1 213 847-7383
+title: Junior Management Dictator
+userPassword: giwguLeraK
+uid: Kare_Lugwig
+givenName: Kare
+mail: Kare_Lugwig@example.com
+carLicense: GLPVSFU
+departmentNumber: 3578
+employeeType: Normal
+homePhone: +1 213 103-9835
+initials: K. L.
+mobile: +1 71 478-8740
+pager: +1 415 773-5205
+manager: cn=Yate Venier
+secretary: cn=Tomasine Wasserman
+roomNumber: 4838
+
+dn: cn=Sula Tanner, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sula Tanner
+sn: Tanner
+description: This is Sula Tanner's description
+facsimileTelephoneNumber: +1 804 295-7586
+l: San Mateo
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 471
+telephoneNumber: +1 71 290-9074
+title: Chief Product Testing Vice President
+userPassword: rennaTaluS
+uid: Sula_Tanner
+givenName: Sula
+mail: Sula_Tanner@example.com
+carLicense: 0VQQ0HK
+departmentNumber: 737
+employeeType: Normal
+homePhone: +1 415 237-6614
+initials: S. T.
+mobile: +1 206 850-6105
+pager: +1 303 894-1732
+manager: cn=Pierrette Spencer
+secretary: cn=Svenn-Erik Kowalsky
+roomNumber: 9742
+
+dn: cn=Hermina Dickeson, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Hermina Dickeson
+sn: Dickeson
+description: This is Hermina Dickeson's description
+facsimileTelephoneNumber: +1 818 451-7579
+l: Sunnyvale
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 115
+telephoneNumber: +1 415 741-4085
+title: Junior Janitorial Czar
+userPassword: nosekciDan
+uid: Hermina_Dickeson
+givenName: Hermina
+mail: Hermina_Dickeson@example.com
+carLicense: PKCPCT9
+departmentNumber: 7530
+employeeType: Temp
+homePhone: +1 408 596-9759
+initials: H. D.
+mobile: +1 818 273-6370
+pager: +1 213 923-8962
+manager: cn=Natassia Soucie
+secretary: cn=Orden Rakotomalala
+roomNumber: 8119
+
+dn: cn=Ari Adey, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ari Adey
+sn: Adey
+description: This is Ari Adey's description
+facsimileTelephoneNumber: +1 415 543-1505
+l: Fremont
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 395
+telephoneNumber: +1 818 394-6297
+title: Master Product Testing Czar
+userPassword: yedAirA
+uid: Ari_Adey
+givenName: Ari
+mail: Ari_Adey@example.com
+carLicense: J9XM4PR
+departmentNumber: 3231
+employeeType: Normal
+homePhone: +1 408 306-4874
+initials: A. A.
+mobile: +1 213 722-8365
+pager: +1 206 325-2793
+manager: cn=Analiese McMann
+secretary: cn=Saundra Bennefeld
+roomNumber: 7650
+
+dn: cn=Victoria Leang, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Victoria Leang
+sn: Leang
+description: This is Victoria Leang's description
+facsimileTelephoneNumber: +1 303 117-4601
+l: Santa Clara
+ou: Payroll
+postalAddress: example$Payroll$Dept # 334
+telephoneNumber: +1 415 975-4679
+title: Master Payroll Writer
+userPassword: gnaeLairot
+uid: Victoria_Leang
+givenName: Victoria
+mail: Victoria_Leang@example.com
+carLicense: PSJFH9X
+departmentNumber: 9435
+employeeType: Employee
+homePhone: +1 510 209-6338
+initials: V. L.
+mobile: +1 303 857-3061
+pager: +1 206 627-9915
+manager: cn=Gwennie Klostermann
+secretary: cn=Cristine Felske
+roomNumber: 3111
+
+dn: cn=Ronda Wilkerson, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ronda Wilkerson
+sn: Wilkerson
+description: This is Ronda Wilkerson's description
+facsimileTelephoneNumber: +1 804 155-2937
+l: Emeryville
+ou: Peons
+postalAddress: example$Peons$Dept # 721
+telephoneNumber: +1 206 723-9225
+title: Junior Peons Pinhead
+userPassword: nosrekliWa
+uid: Ronda_Wilkerson
+givenName: Ronda
+mail: Ronda_Wilkerson@example.com
+carLicense: ZD3OJ20
+departmentNumber: 3672
+employeeType: Temp
+homePhone: +1 804 586-6519
+initials: R. W.
+mobile: +1 818 363-8327
+pager: +1 206 432-1897
+manager: cn=Chitra Gartley
+secretary: cn=Cecco Nordstrom
+roomNumber: 3861
+
+dn: cn=Russel Littau, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Russel Littau
+sn: Littau
+description: This is Russel Littau's description
+facsimileTelephoneNumber: +1 71 915-4910
+l: Mountain View
+ou: Peons
+postalAddress: example$Peons$Dept # 677
+telephoneNumber: +1 415 889-5335
+title: Supreme Peons President
+userPassword: uattiLless
+uid: Russel_Littau
+givenName: Russel
+mail: Russel_Littau@example.com
+carLicense: VLLESG0
+departmentNumber: 6651
+employeeType: Normal
+homePhone: +1 303 909-2504
+initials: R. L.
+mobile: +1 303 924-9552
+pager: +1 818 728-2874
+manager: cn=Jatinder Scarborough
+secretary: cn=Tara Draffin
+roomNumber: 8375
+
+dn: cn=Sorin Sherif, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sorin Sherif
+sn: Sherif
+description: This is Sorin Sherif's description
+facsimileTelephoneNumber: +1 804 118-9128
+l: Fremont
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 445
+telephoneNumber: +1 408 977-7192
+title: Master Janitorial Punk
+userPassword: firehSniro
+uid: Sorin_Sherif
+givenName: Sorin
+mail: Sorin_Sherif@example.com
+carLicense: 5VIK8EN
+departmentNumber: 1036
+employeeType: Normal
+homePhone: +1 818 136-6030
+initials: S. S.
+mobile: +1 510 792-9261
+pager: +1 71 353-9877
+manager: cn=Terrell Peacocke
+secretary: cn=Kathye Sandhar
+roomNumber: 7862
+
+dn: cn=Biplab Soldera, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Biplab Soldera
+sn: Soldera
+description: This is Biplab Soldera's description
+facsimileTelephoneNumber: +1 206 112-3230
+l: Redmond
+ou: Planning
+postalAddress: example$Planning$Dept # 603
+telephoneNumber: +1 818 275-7258
+title: Elite Planning Madonna
+userPassword: aredloSbal
+uid: Biplab_Soldera
+givenName: Biplab
+mail: Biplab_Soldera@example.com
+carLicense: FS496ZQ
+departmentNumber: 317
+employeeType: Employee
+homePhone: +1 206 807-9278
+initials: B. S.
+mobile: +1 303 703-5352
+pager: +1 71 285-6965
+manager: cn=Caryl Steen
+secretary: cn=Ardeen Rabjohn
+roomNumber: 3841
+
+dn: cn=Kirbee Fleishman, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kirbee Fleishman
+sn: Fleishman
+description: This is Kirbee Fleishman's description
+facsimileTelephoneNumber: +1 303 211-5706
+l: Armonk
+ou: Accounting
+postalAddress: example$Accounting$Dept # 681
+telephoneNumber: +1 303 348-3978
+title: Supreme Accounting Madonna
+userPassword: namhsielFe
+uid: Kirbee_Fleishman
+givenName: Kirbee
+mail: Kirbee_Fleishman@example.com
+carLicense: G4BNZ42
+departmentNumber: 3552
+employeeType: Employee
+homePhone: +1 71 104-5707
+initials: K. F.
+mobile: +1 213 604-9318
+pager: +1 71 560-2705
+manager: cn=Danielle Bambach
+secretary: cn=Dahlia Eckstein
+roomNumber: 5724
+
+dn: cn=Natassia Devarennes, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Natassia Devarennes
+sn: Devarennes
+description: This is Natassia Devarennes's description
+facsimileTelephoneNumber: +1 213 510-5460
+l: Alameda
+ou: Payroll
+postalAddress: example$Payroll$Dept # 197
+telephoneNumber: +1 818 694-3052
+title: Senior Payroll Technician
+userPassword: senneraveD
+uid: Natassia_Devarennes
+givenName: Natassia
+mail: Natassia_Devarennes@example.com
+carLicense: K873P9G
+departmentNumber: 4678
+employeeType: Manager
+homePhone: +1 415 950-5486
+initials: N. D.
+mobile: +1 408 324-4107
+pager: +1 408 261-4010
+manager: cn=Irvin Hinsdale
+secretary: cn=Penny Outram
+roomNumber: 2626
+
+dn: cn=Geza Teniola, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Geza Teniola
+sn: Teniola
+description: This is Geza Teniola's description
+facsimileTelephoneNumber: +1 510 448-5550
+l: Fremont
+ou: Management
+postalAddress: example$Management$Dept # 830
+telephoneNumber: +1 818 342-5948
+title: Senior Management Yahoo
+userPassword: aloineTaze
+uid: Geza_Teniola
+givenName: Geza
+mail: Geza_Teniola@example.com
+carLicense: 7P9IAAQ
+departmentNumber: 5892
+employeeType: Employee
+homePhone: +1 303 840-4218
+initials: G. T.
+mobile: +1 510 901-2392
+pager: +1 510 569-1997
+manager: cn=Addi Pevzner
+secretary: cn=Ruthe Hutchins
+roomNumber: 1214
+
+dn: cn=Pas Panger, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Pas Panger
+sn: Panger
+description: This is Pas Panger's description
+facsimileTelephoneNumber: +1 818 338-5910
+l: Emeryville
+ou: Payroll
+postalAddress: example$Payroll$Dept # 600
+telephoneNumber: +1 408 218-7902
+title: Chief Payroll Artist
+userPassword: regnaPsaP
+uid: Pas_Panger
+givenName: Pas
+mail: Pas_Panger@example.com
+carLicense: 720HXUD
+departmentNumber: 3584
+employeeType: Temp
+homePhone: +1 804 986-9454
+initials: P. P.
+mobile: +1 408 182-4550
+pager: +1 206 188-7087
+manager: cn=Elie Dubee
+secretary: cn=Cheslie Bostock
+roomNumber: 1025
+
+dn: cn=Gunars Trader, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gunars Trader
+sn: Trader
+description: This is Gunars Trader's description
+facsimileTelephoneNumber: +1 408 216-7540
+l: Menlo Park
+ou: Planning
+postalAddress: example$Planning$Dept # 302
+telephoneNumber: +1 510 768-6819
+title: Master Planning Madonna
+userPassword: redarTsran
+uid: Gunars_Trader
+givenName: Gunars
+mail: Gunars_Trader@example.com
+carLicense: HPYPGZJ
+departmentNumber: 4629
+employeeType: Temp
+homePhone: +1 213 584-1198
+initials: G. T.
+mobile: +1 408 347-8551
+pager: +1 71 425-9989
+manager: cn=Kata Alfred
+secretary: cn=Cariotta Zattiero
+roomNumber: 6922
+
+dn: cn=Jillayne Michelsen, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jillayne Michelsen
+sn: Michelsen
+description: This is Jillayne Michelsen's description
+facsimileTelephoneNumber: +1 415 149-4718
+l: San Jose
+ou: Management
+postalAddress: example$Management$Dept # 203
+telephoneNumber: +1 510 847-7991
+title: Supreme Management Artist
+userPassword: neslehciMe
+uid: Jillayne_Michelsen
+givenName: Jillayne
+mail: Jillayne_Michelsen@example.com
+carLicense: JW0NJLZ
+departmentNumber: 1629
+employeeType: Manager
+homePhone: +1 510 566-3934
+initials: J. M.
+mobile: +1 303 590-9559
+pager: +1 213 112-5078
+manager: cn=Condell Cranston
+secretary: cn=Xenia Chapleau
+roomNumber: 8583
+
+dn: cn=Kalie Clough, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kalie Clough
+sn: Clough
+description: This is Kalie Clough's description
+facsimileTelephoneNumber: +1 818 114-2105
+l: Mountain View
+ou: Payroll
+postalAddress: example$Payroll$Dept # 574
+telephoneNumber: +1 206 335-3186
+title: Associate Payroll Vice President
+userPassword: hguolCeila
+uid: Kalie_Clough
+givenName: Kalie
+mail: Kalie_Clough@example.com
+carLicense: BBILRZE
+departmentNumber: 5335
+employeeType: Employee
+homePhone: +1 818 830-7942
+initials: K. C.
+mobile: +1 510 402-9085
+pager: +1 206 628-8151
+manager: cn=Petra Ledford
+secretary: cn=Lauraine Paulin
+roomNumber: 3572
+
+dn: cn=Lowietje Koskinen, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lowietje Koskinen
+sn: Koskinen
+description: This is Lowietje Koskinen's description
+facsimileTelephoneNumber: +1 415 610-7184
+l: Fremont
+ou: Product Development
+postalAddress: example$Product Development$Dept # 338
+telephoneNumber: +1 818 218-4648
+title: Chief Product Development Sales Rep
+userPassword: neniksoKej
+uid: Lowietje_Koskinen
+givenName: Lowietje
+mail: Lowietje_Koskinen@example.com
+carLicense: TU079TG
+departmentNumber: 2364
+employeeType: Contract
+homePhone: +1 213 125-5802
+initials: L. K.
+mobile: +1 71 176-3460
+pager: +1 408 271-5825
+manager: cn=Jody Von Semmler
+secretary: cn=Drucill Caleta
+roomNumber: 1942
+
+dn: cn=Dolly Zarate, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dolly Zarate
+sn: Zarate
+description: This is Dolly Zarate's description
+facsimileTelephoneNumber: +1 415 249-5058
+l: Fremont
+ou: Planning
+postalAddress: example$Planning$Dept # 181
+telephoneNumber: +1 510 954-3865
+title: Associate Planning Figurehead
+userPassword: etaraZyllo
+uid: Dolly_Zarate
+givenName: Dolly
+mail: Dolly_Zarate@example.com
+carLicense: RRYYT6Y
+departmentNumber: 6066
+employeeType: Contract
+homePhone: +1 804 673-6422
+initials: D. Z.
+mobile: +1 303 263-3964
+pager: +1 303 386-5397
+manager: cn=Analise Logntp
+secretary: cn=Yodha Theriot
+roomNumber: 1677
+
+dn: cn=Kettie Roig, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kettie Roig
+sn: Roig
+description: This is Kettie Roig's description
+facsimileTelephoneNumber: +1 303 720-1619
+l: Cupertino
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 358
+telephoneNumber: +1 303 513-2397
+title: Senior Human Resources Admin
+userPassword: gioReitteK
+uid: Kettie_Roig
+givenName: Kettie
+mail: Kettie_Roig@example.com
+carLicense: 99DJIXW
+departmentNumber: 3346
+employeeType: Contract
+homePhone: +1 303 979-1218
+initials: K. R.
+mobile: +1 206 258-1558
+pager: +1 818 557-4666
+manager: cn=Lauretta Jaswal
+secretary: cn=Sophia Valenziano
+roomNumber: 4614
+
+dn: cn=Cathee Cottrell, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Cathee Cottrell
+sn: Cottrell
+description: This is Cathee Cottrell's description
+facsimileTelephoneNumber: +1 415 388-9625
+l: Redmond
+ou: Product Development
+postalAddress: example$Product Development$Dept # 773
+telephoneNumber: +1 804 855-7117
+title: Senior Product Development Figurehead
+userPassword: llerttoCee
+uid: Cathee_Cottrell
+givenName: Cathee
+mail: Cathee_Cottrell@example.com
+carLicense: A0J184M
+departmentNumber: 6207
+employeeType: Contract
+homePhone: +1 71 220-9600
+initials: C. C.
+mobile: +1 206 723-4560
+pager: +1 303 911-5420
+manager: cn=Delmar Charlino
+secretary: cn=Tetsumo McMillion
+roomNumber: 5388
+
+dn: cn=Anet Clapham, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Anet Clapham
+sn: Clapham
+description: This is Anet Clapham's description
+facsimileTelephoneNumber: +1 213 803-3646
+l: Cambridge
+ou: Peons
+postalAddress: example$Peons$Dept # 36
+telephoneNumber: +1 510 788-4921
+title: Supreme Peons Writer
+userPassword: mahpalCten
+uid: Anet_Clapham
+givenName: Anet
+mail: Anet_Clapham@example.com
+carLicense: HX3P0G2
+departmentNumber: 6210
+employeeType: Employee
+homePhone: +1 71 456-1198
+initials: A. C.
+mobile: +1 303 269-7859
+pager: +1 213 265-6142
+manager: cn=Ami Ching
+secretary: cn=Sheldon Maleski
+roomNumber: 3839
+
+dn: cn=Larkin Remers, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Larkin Remers
+sn: Remers
+description: This is Larkin Remers's description
+facsimileTelephoneNumber: +1 804 858-4790
+l: Cambridge
+ou: Administrative
+postalAddress: example$Administrative$Dept # 543
+telephoneNumber: +1 415 117-2576
+title: Master Administrative Stooge
+userPassword: sremeRnikr
+uid: Larkin_Remers
+givenName: Larkin
+mail: Larkin_Remers@example.com
+carLicense: VP6MJNV
+departmentNumber: 665
+employeeType: Temp
+homePhone: +1 213 233-7341
+initials: L. R.
+mobile: +1 408 231-2216
+pager: +1 415 524-3809
+manager: cn=Kiersten Limbaugh
+secretary: cn=Bharat Darcel
+roomNumber: 2787
+
+dn: cn=Tin Woodward-Jack, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tin Woodward-Jack
+sn: Woodward-Jack
+description: This is Tin Woodward-Jack's description
+facsimileTelephoneNumber: +1 818 177-8005
+l: Sunnyvale
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 350
+telephoneNumber: +1 818 787-2067
+title: Supreme Human Resources Architect
+userPassword: kcaJ-drawd
+uid: Tin_Woodward-Jack
+givenName: Tin
+mail: Tin_Woodward-Jack@example.com
+carLicense: 3QBISPO
+departmentNumber: 8199
+employeeType: Manager
+homePhone: +1 71 116-9381
+initials: T. W.
+mobile: +1 303 257-6901
+pager: +1 408 229-3825
+manager: cn=Ezmeralda Jago
+secretary: cn=Augustin Allaway
+roomNumber: 8846
+
+dn: cn=Soyong Sandiford, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Soyong Sandiford
+sn: Sandiford
+description: This is Soyong Sandiford's description
+facsimileTelephoneNumber: +1 71 284-4799
+l: San Jose
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 953
+telephoneNumber: +1 415 512-1280
+title: Master Janitorial Technician
+userPassword: drofidnaSg
+uid: Soyong_Sandiford
+givenName: Soyong
+mail: Soyong_Sandiford@example.com
+carLicense: U2VXM06
+departmentNumber: 875
+employeeType: Employee
+homePhone: +1 415 810-9760
+initials: S. S.
+mobile: +1 303 589-6998
+pager: +1 818 104-3267
+manager: cn=Meghan Prodmfg
+secretary: cn=Adelaide Ibach
+roomNumber: 8549
+
+dn: cn=Amii Hipson, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Amii Hipson
+sn: Hipson
+description: This is Amii Hipson's description
+facsimileTelephoneNumber: +1 415 435-7189
+l: Menlo Park
+ou: Peons
+postalAddress: example$Peons$Dept # 411
+telephoneNumber: +1 213 402-7077
+title: Elite Peons Figurehead
+userPassword: nospiHiimA
+uid: Amii_Hipson
+givenName: Amii
+mail: Amii_Hipson@example.com
+carLicense: 3C2MIWS
+departmentNumber: 8457
+employeeType: Contract
+homePhone: +1 415 931-9572
+initials: A. H.
+mobile: +1 303 208-9394
+pager: +1 206 533-6902
+manager: cn=Rhetta Liesenberg
+secretary: cn=Allx Rantala
+roomNumber: 5989
+
+dn: cn=Misti Ramseyer, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Misti Ramseyer
+sn: Ramseyer
+description: This is Misti Ramseyer's description
+facsimileTelephoneNumber: +1 510 161-7981
+l: San Mateo
+ou: Peons
+postalAddress: example$Peons$Dept # 692
+telephoneNumber: +1 818 207-5357
+title: Supreme Peons Warrior
+userPassword: reyesmaRit
+uid: Misti_Ramseyer
+givenName: Misti
+mail: Misti_Ramseyer@example.com
+carLicense: FR4H13P
+departmentNumber: 9743
+employeeType: Temp
+homePhone: +1 415 742-2788
+initials: M. R.
+mobile: +1 415 701-8185
+pager: +1 206 761-7230
+manager: cn=Saba Vennos
+secretary: cn=Kathryne Nagai
+roomNumber: 2716
+
+dn: cn=Ashli Borosch, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ashli Borosch
+sn: Borosch
+description: This is Ashli Borosch's description
+facsimileTelephoneNumber: +1 804 743-7087
+l: Cupertino
+ou: Peons
+postalAddress: example$Peons$Dept # 969
+telephoneNumber: +1 408 388-7362
+title: Senior Peons Warrior
+userPassword: hcsoroBilh
+uid: Ashli_Borosch
+givenName: Ashli
+mail: Ashli_Borosch@example.com
+carLicense: Y5M28EV
+departmentNumber: 6583
+employeeType: Manager
+homePhone: +1 303 846-8629
+initials: A. B.
+mobile: +1 213 915-9960
+pager: +1 510 836-3825
+manager: cn=Sissie MAINT
+secretary: cn=Chen Peirce
+roomNumber: 9109
+
+dn: cn=Perle Pisani, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Perle Pisani
+sn: Pisani
+description: This is Perle Pisani's description
+facsimileTelephoneNumber: +1 303 546-5638
+l: Armonk
+ou: Payroll
+postalAddress: example$Payroll$Dept # 27
+telephoneNumber: +1 213 452-3530
+title: Elite Payroll Artist
+userPassword: inasiPelre
+uid: Perle_Pisani
+givenName: Perle
+mail: Perle_Pisani@example.com
+carLicense: 95NVMCX
+departmentNumber: 1320
+employeeType: Normal
+homePhone: +1 818 370-9640
+initials: P. P.
+mobile: +1 510 256-9020
+pager: +1 818 401-8427
+manager: cn=Marj Alzofon
+secretary: cn=Bianka McCafferty
+roomNumber: 1935
+
+dn: cn=Ajit Kruziak, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ajit Kruziak
+sn: Kruziak
+description: This is Ajit Kruziak's description
+facsimileTelephoneNumber: +1 510 354-6551
+l: San Mateo
+ou: Payroll
+postalAddress: example$Payroll$Dept # 825
+telephoneNumber: +1 415 103-1822
+title: Elite Payroll Admin
+userPassword: kaizurKtij
+uid: Ajit_Kruziak
+givenName: Ajit
+mail: Ajit_Kruziak@example.com
+carLicense: DLPPITG
+departmentNumber: 1221
+employeeType: Employee
+homePhone: +1 408 261-2038
+initials: A. K.
+mobile: +1 415 101-1370
+pager: +1 213 844-4207
+manager: cn=Patti Brungardt
+secretary: cn=Zarla Kyoung
+roomNumber: 1811
+
+dn: cn=Der Salomon, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Der Salomon
+sn: Salomon
+description: This is Der Salomon's description
+facsimileTelephoneNumber: +1 408 488-7614
+l: San Francisco
+ou: Administrative
+postalAddress: example$Administrative$Dept # 53
+telephoneNumber: +1 818 130-5643
+title: Supreme Administrative Stooge
+userPassword: nomolaSreD
+uid: Der_Salomon
+givenName: Der
+mail: Der_Salomon@example.com
+carLicense: 217NI44
+departmentNumber: 1363
+employeeType: Contract
+homePhone: +1 303 356-3857
+initials: D. S.
+mobile: +1 213 816-2420
+pager: +1 71 563-2262
+manager: cn=Aaren Lessin
+secretary: cn=Tulip Adler
+roomNumber: 5756
+
+dn: cn=Anstice Chatel, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Anstice Chatel
+sn: Chatel
+description: This is Anstice Chatel's description
+facsimileTelephoneNumber: +1 804 213-6878
+l: San Mateo
+ou: Product Development
+postalAddress: example$Product Development$Dept # 69
+telephoneNumber: +1 206 992-9408
+title: Master Product Development Dictator
+userPassword: letahCecit
+uid: Anstice_Chatel
+givenName: Anstice
+mail: Anstice_Chatel@example.com
+carLicense: SIIXYR0
+departmentNumber: 1644
+employeeType: Manager
+homePhone: +1 510 234-8279
+initials: A. C.
+mobile: +1 71 326-4355
+pager: +1 818 684-4808
+manager: cn=Carran Frederick
+secretary: cn=Gwenneth Conboy
+roomNumber: 5898
+
+dn: cn=Follick Tom, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Follick Tom
+sn: Tom
+description: This is Follick Tom's description
+facsimileTelephoneNumber: +1 303 116-7145
+l: Redwood Shores
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 681
+telephoneNumber: +1 408 500-5612
+title: Elite Janitorial Admin
+userPassword: moTkcilloF
+uid: Follick_Tom
+givenName: Follick
+mail: Follick_Tom@example.com
+carLicense: TLW2Y9A
+departmentNumber: 5789
+employeeType: Normal
+homePhone: +1 510 285-6385
+initials: F. T.
+mobile: +1 818 885-1048
+pager: +1 408 951-6231
+manager: cn=Fariborz Laviolette
+secretary: cn=Liva Sebastian
+roomNumber: 9167
+
+dn: cn=Mary-Jane Breault, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Mary-Jane Breault
+sn: Breault
+description: This is Mary-Jane Breault's description
+facsimileTelephoneNumber: +1 415 995-2885
+l: Mountain View
+ou: Planning
+postalAddress: example$Planning$Dept # 757
+telephoneNumber: +1 408 647-4483
+title: Chief Planning Technician
+userPassword: tluaerBena
+uid: Mary-Jane_Breault
+givenName: Mary-Jane
+mail: Mary-Jane_Breault@example.com
+carLicense: WCWZDQI
+departmentNumber: 3957
+employeeType: Contract
+homePhone: +1 415 425-3260
+initials: M. B.
+mobile: +1 303 426-8026
+pager: +1 415 312-3046
+manager: cn=Erlene Sylvie
+secretary: cn=Marcos Schacham
+roomNumber: 733
+
+dn: cn=Grace Dmuchalsky, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Grace Dmuchalsky
+sn: Dmuchalsky
+description: This is Grace Dmuchalsky's description
+facsimileTelephoneNumber: +1 71 687-7498
+l: San Mateo
+ou: Product Development
+postalAddress: example$Product Development$Dept # 369
+telephoneNumber: +1 804 342-9553
+title: Elite Product Development Sales Rep
+userPassword: ykslahcumD
+uid: Grace_Dmuchalsky
+givenName: Grace
+mail: Grace_Dmuchalsky@example.com
+carLicense: 755OD0D
+departmentNumber: 4075
+employeeType: Employee
+homePhone: +1 818 699-6712
+initials: G. D.
+mobile: +1 213 758-9777
+pager: +1 206 195-8748
+manager: cn=Crystal Popovics
+secretary: cn=Daryn Murock
+roomNumber: 2198
+
+dn: cn=Winne Chiu, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Winne Chiu
+sn: Chiu
+description: This is Winne Chiu's description
+facsimileTelephoneNumber: +1 415 275-6077
+l: Milpitas
+ou: Peons
+postalAddress: example$Peons$Dept # 651
+telephoneNumber: +1 415 478-6184
+title: Associate Peons Director
+userPassword: uihCenniW
+uid: Winne_Chiu
+givenName: Winne
+mail: Winne_Chiu@example.com
+carLicense: CW6RJU2
+departmentNumber: 4673
+employeeType: Temp
+homePhone: +1 415 408-4113
+initials: W. C.
+mobile: +1 71 332-6219
+pager: +1 818 715-9563
+manager: cn=Elbert Erichsen
+secretary: cn=Khai Speer
+roomNumber: 5558
+
+dn: cn=Tanitansy Carmona, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tanitansy Carmona
+sn: Carmona
+description: This is Tanitansy Carmona's description
+facsimileTelephoneNumber: +1 206 581-9777
+l: Cambridge
+ou: Peons
+postalAddress: example$Peons$Dept # 545
+telephoneNumber: +1 804 655-1179
+title: Master Peons Manager
+userPassword: anomraCysn
+uid: Tanitansy_Carmona
+givenName: Tanitansy
+mail: Tanitansy_Carmona@example.com
+carLicense: V12V2BZ
+departmentNumber: 7687
+employeeType: Employee
+homePhone: +1 415 241-3229
+initials: T. C.
+mobile: +1 408 958-7516
+pager: +1 818 234-8717
+manager: cn=Laurette Legris
+secretary: cn=Germ Vaserfirer
+roomNumber: 6944
+
+dn: cn=Zouheir Fisprod, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Zouheir Fisprod
+sn: Fisprod
+description: This is Zouheir Fisprod's description
+facsimileTelephoneNumber: +1 804 206-6561
+l: Cupertino
+ou: Product Development
+postalAddress: example$Product Development$Dept # 550
+telephoneNumber: +1 206 811-7606
+title: Chief Product Development Mascot
+userPassword: dorpsiFrie
+uid: Zouheir_Fisprod
+givenName: Zouheir
+mail: Zouheir_Fisprod@example.com
+carLicense: 3PRJVCA
+departmentNumber: 6549
+employeeType: Employee
+homePhone: +1 213 621-4789
+initials: Z. F.
+mobile: +1 213 717-4374
+pager: +1 818 383-5244
+manager: cn=Norvie Eales
+secretary: cn=Millie Siehl
+roomNumber: 4490
+
+dn: cn=John-Jr Saladna, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: John-Jr Saladna
+sn: Saladna
+description: This is John-Jr Saladna's description
+facsimileTelephoneNumber: +1 71 164-5753
+l: Mountain View
+ou: Planning
+postalAddress: example$Planning$Dept # 176
+telephoneNumber: +1 510 901-9725
+title: Supreme Planning Grunt
+userPassword: andalaSrJ-
+uid: John-Jr_Saladna
+givenName: John-Jr
+mail: John-Jr_Saladna@example.com
+carLicense: 3JO3PVV
+departmentNumber: 3382
+employeeType: Employee
+homePhone: +1 206 621-2628
+initials: J. S.
+mobile: +1 71 114-6947
+pager: +1 818 601-2674
+manager: cn=Minetta Sherif
+secretary: cn=Kimberlyn Meskimen
+roomNumber: 459
+
+dn: cn=Susana Fougere, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Susana Fougere
+sn: Fougere
+description: This is Susana Fougere's description
+facsimileTelephoneNumber: +1 213 454-9358
+l: San Mateo
+ou: Management
+postalAddress: example$Management$Dept # 776
+telephoneNumber: +1 510 480-5730
+title: Master Management Janitor
+userPassword: ereguoFana
+uid: Susana_Fougere
+givenName: Susana
+mail: Susana_Fougere@example.com
+carLicense: VSP4LUY
+departmentNumber: 3606
+employeeType: Employee
+homePhone: +1 510 181-9611
+initials: S. F.
+mobile: +1 213 770-2904
+pager: +1 408 415-5120
+manager: cn=Meggi Dowding
+secretary: cn=Josine Hubal
+roomNumber: 5870
+
+dn: cn=Takehiko Smits, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Takehiko Smits
+sn: Smits
+description: This is Takehiko Smits's description
+facsimileTelephoneNumber: +1 408 346-8793
+l: San Francisco
+ou: Product Development
+postalAddress: example$Product Development$Dept # 981
+telephoneNumber: +1 804 705-8588
+title: Senior Product Development Evangelist
+userPassword: stimSokihe
+uid: Takehiko_Smits
+givenName: Takehiko
+mail: Takehiko_Smits@example.com
+carLicense: OMCXJMG
+departmentNumber: 1408
+employeeType: Contract
+homePhone: +1 213 821-6140
+initials: T. S.
+mobile: +1 303 149-7395
+pager: +1 408 574-8356
+manager: cn=Verlyn Decapua
+secretary: cn=Prafula Armentrout
+roomNumber: 7433
+
+dn: cn=Aarika Wolfman, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Aarika Wolfman
+sn: Wolfman
+description: This is Aarika Wolfman's description
+facsimileTelephoneNumber: +1 510 421-3943
+l: Cambridge
+ou: Peons
+postalAddress: example$Peons$Dept # 922
+telephoneNumber: +1 71 544-8828
+title: Supreme Peons Yahoo
+userPassword: namfloWaki
+uid: Aarika_Wolfman
+givenName: Aarika
+mail: Aarika_Wolfman@example.com
+carLicense: XZ88GIK
+departmentNumber: 2833
+employeeType: Employee
+homePhone: +1 213 859-5564
+initials: A. W.
+mobile: +1 804 713-4223
+pager: +1 303 607-7149
+manager: cn=Janio Wiltz
+secretary: cn=Alfredo Boynton
+roomNumber: 7541
+
+dn: cn=Sabra Magee, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sabra Magee
+sn: Magee
+description: This is Sabra Magee's description
+facsimileTelephoneNumber: +1 206 676-6546
+l: Cambridge
+ou: Peons
+postalAddress: example$Peons$Dept # 122
+telephoneNumber: +1 206 839-4408
+title: Associate Peons Consultant
+userPassword: eegaMarbaS
+uid: Sabra_Magee
+givenName: Sabra
+mail: Sabra_Magee@example.com
+carLicense: SMRV5GK
+departmentNumber: 3108
+employeeType: Temp
+homePhone: +1 804 265-3642
+initials: S. M.
+mobile: +1 71 651-6097
+pager: +1 213 287-8936
+manager: cn=Harmi Cobran
+secretary: cn=Valida Denomme
+roomNumber: 8087
+
+dn: cn=Marjan Kupferschmidt, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marjan Kupferschmidt
+sn: Kupferschmidt
+description: This is Marjan Kupferschmidt's description
+facsimileTelephoneNumber: +1 213 692-8541
+l: Armonk
+ou: Administrative
+postalAddress: example$Administrative$Dept # 541
+telephoneNumber: +1 408 217-7188
+title: Senior Administrative Fellow
+userPassword: tdimhcsref
+uid: Marjan_Kupferschmidt
+givenName: Marjan
+mail: Marjan_Kupferschmidt@example.com
+carLicense: QGYGGLB
+departmentNumber: 8065
+employeeType: Contract
+homePhone: +1 818 795-1037
+initials: M. K.
+mobile: +1 415 825-4455
+pager: +1 408 105-7416
+manager: cn=Magnolia Joyce
+secretary: cn=Harriott Demren
+roomNumber: 1223
+
+dn: cn=Spicer Modafferi, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Spicer Modafferi
+sn: Modafferi
+description: This is Spicer Modafferi's description
+facsimileTelephoneNumber: +1 408 328-6089
+l: San Jose
+ou: Administrative
+postalAddress: example$Administrative$Dept # 846
+telephoneNumber: +1 510 189-8308
+title: Master Administrative Grunt
+userPassword: ireffadoMr
+uid: Spicer_Modafferi
+givenName: Spicer
+mail: Spicer_Modafferi@example.com
+carLicense: 4KUKJ5D
+departmentNumber: 4785
+employeeType: Manager
+homePhone: +1 804 278-1044
+initials: S. M.
+mobile: +1 206 849-2011
+pager: +1 510 368-4746
+manager: cn=Gabriellia Clancy
+secretary: cn=Larysa Bmethods
+roomNumber: 3133
+
+dn: cn=Minnaminnie Lavers, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Minnaminnie Lavers
+sn: Lavers
+description: This is Minnaminnie Lavers's description
+facsimileTelephoneNumber: +1 818 267-6325
+l: San Jose
+ou: Accounting
+postalAddress: example$Accounting$Dept # 197
+telephoneNumber: +1 818 513-3510
+title: Master Accounting Yahoo
+userPassword: srevaLeinn
+uid: Minnaminnie_Lavers
+givenName: Minnaminnie
+mail: Minnaminnie_Lavers@example.com
+carLicense: FI0MMND
+departmentNumber: 5516
+employeeType: Contract
+homePhone: +1 206 657-8324
+initials: M. L.
+mobile: +1 71 119-4456
+pager: +1 206 776-9058
+manager: cn=Christi Minos
+secretary: cn=Juli Ayres
+roomNumber: 6299
+
+dn: cn=Davis OFCPARM, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Davis OFCPARM
+sn: OFCPARM
+description: This is Davis OFCPARM's description
+facsimileTelephoneNumber: +1 303 450-2426
+l: Fremont
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 250
+telephoneNumber: +1 818 838-6409
+title: Master Janitorial Evangelist
+userPassword: MRAPCFOsiv
+uid: Davis_OFCPARM
+givenName: Davis
+mail: Davis_OFCPARM@example.com
+carLicense: CC4Q350
+departmentNumber: 8851
+employeeType: Temp
+homePhone: +1 818 603-3620
+initials: D. O.
+mobile: +1 818 757-8353
+pager: +1 71 664-7052
+manager: cn=Lilah Marting
+secretary: cn=Hareton Piercey
+roomNumber: 7599
+
+dn: cn=Cordy Mahiger, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Cordy Mahiger
+sn: Mahiger
+description: This is Cordy Mahiger's description
+facsimileTelephoneNumber: +1 510 967-3620
+l: Redwood Shores
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 810
+telephoneNumber: +1 818 304-8895
+title: Senior Human Resources President
+userPassword: regihaMydr
+uid: Cordy_Mahiger
+givenName: Cordy
+mail: Cordy_Mahiger@example.com
+carLicense: DGIUUIF
+departmentNumber: 9385
+employeeType: Normal
+homePhone: +1 303 233-7844
+initials: C. M.
+mobile: +1 818 735-9720
+pager: +1 213 482-6716
+manager: cn=Iseabal Bryttan
+secretary: cn=Rosella Delorenzi
+roomNumber: 9567
+
+dn: cn=Sherrie Ishak, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sherrie Ishak
+sn: Ishak
+description: This is Sherrie Ishak's description
+facsimileTelephoneNumber: +1 213 222-2612
+l: San Francisco
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 55
+telephoneNumber: +1 303 465-7312
+title: Associate Product Testing Admin
+userPassword: kahsIeirre
+uid: Sherrie_Ishak
+givenName: Sherrie
+mail: Sherrie_Ishak@example.com
+carLicense: 8CJYTR1
+departmentNumber: 2845
+employeeType: Employee
+homePhone: +1 804 973-9215
+initials: S. I.
+mobile: +1 415 681-2614
+pager: +1 303 554-6124
+manager: cn=Darwin Delroy
+secretary: cn=Stirling Zargham
+roomNumber: 7665
+
+dn: cn=Andre Grills, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Andre Grills
+sn: Grills
+description: This is Andre Grills's description
+facsimileTelephoneNumber: +1 415 186-9781
+l: Redwood Shores
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 735
+telephoneNumber: +1 206 810-7100
+title: Supreme Janitorial Consultant
+userPassword: sllirGerdn
+uid: Andre_Grills
+givenName: Andre
+mail: Andre_Grills@example.com
+carLicense: 3WHP5CH
+departmentNumber: 8411
+employeeType: Normal
+homePhone: +1 303 354-3117
+initials: A. G.
+mobile: +1 71 782-9170
+pager: +1 408 974-6830
+manager: cn=Djordje Buckhoff
+secretary: cn=Jaffer Trochu
+roomNumber: 6853
+
+dn: cn=Dorothee Anker, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dorothee Anker
+sn: Anker
+description: This is Dorothee Anker's description
+facsimileTelephoneNumber: +1 213 854-9079
+l: Alameda
+ou: Management
+postalAddress: example$Management$Dept # 815
+telephoneNumber: +1 804 867-8999
+title: Senior Management Mascot
+userPassword: reknAeehto
+uid: Dorothee_Anker
+givenName: Dorothee
+mail: Dorothee_Anker@example.com
+carLicense: FLRUL35
+departmentNumber: 5718
+employeeType: Normal
+homePhone: +1 71 176-6117
+initials: D. A.
+mobile: +1 71 779-2330
+pager: +1 71 243-8423
+manager: cn=Hengameh Popowicz
+secretary: cn=Shela Mishina
+roomNumber: 8072
+
+dn: cn=Yosuf Vajentic, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Yosuf Vajentic
+sn: Vajentic
+description: This is Yosuf Vajentic's description
+facsimileTelephoneNumber: +1 206 277-7099
+l: San Jose
+ou: Accounting
+postalAddress: example$Accounting$Dept # 954
+telephoneNumber: +1 213 207-7250
+title: Junior Accounting Admin
+userPassword: citnejaVfu
+uid: Yosuf_Vajentic
+givenName: Yosuf
+mail: Yosuf_Vajentic@example.com
+carLicense: IUX1PNF
+departmentNumber: 9053
+employeeType: Temp
+homePhone: +1 510 486-8360
+initials: Y. V.
+mobile: +1 818 210-9416
+pager: +1 415 596-7033
+manager: cn=Tova Javallas
+secretary: cn=Vithit Serre
+roomNumber: 8330
+
+dn: cn=Goldie Tanglao, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Goldie Tanglao
+sn: Tanglao
+description: This is Goldie Tanglao's description
+facsimileTelephoneNumber: +1 804 831-5929
+l: Santa Clara
+ou: Accounting
+postalAddress: example$Accounting$Dept # 310
+telephoneNumber: +1 510 325-6263
+title: Junior Accounting Vice President
+userPassword: oalgnaTeid
+uid: Goldie_Tanglao
+givenName: Goldie
+mail: Goldie_Tanglao@example.com
+carLicense: 826ZCMC
+departmentNumber: 2571
+employeeType: Employee
+homePhone: +1 71 925-7934
+initials: G. T.
+mobile: +1 303 371-2678
+pager: +1 415 984-2018
+manager: cn=Rajiv Glaser
+secretary: cn=Imtiaz Winsborrow
+roomNumber: 906
+
+dn: cn=Kingsley Kawauchi, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kingsley Kawauchi
+sn: Kawauchi
+description: This is Kingsley Kawauchi's description
+facsimileTelephoneNumber: +1 408 760-5114
+l: San Francisco
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 519
+telephoneNumber: +1 206 263-5681
+title: Master Janitorial Consultant
+userPassword: ihcuawaKye
+uid: Kingsley_Kawauchi
+givenName: Kingsley
+mail: Kingsley_Kawauchi@example.com
+carLicense: WOCI58R
+departmentNumber: 6947
+employeeType: Normal
+homePhone: +1 804 395-9664
+initials: K. K.
+mobile: +1 804 216-1764
+pager: +1 510 500-3660
+manager: cn=Claudine Pipkins
+secretary: cn=Clarence Hu
+roomNumber: 5876
+
+dn: cn=Daisi Lenathen, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Daisi Lenathen
+sn: Lenathen
+description: This is Daisi Lenathen's description
+facsimileTelephoneNumber: +1 303 392-4879
+l: Orem
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 748
+telephoneNumber: +1 415 171-6359
+title: Senior Human Resources President
+userPassword: nehtaneLis
+uid: Daisi_Lenathen
+givenName: Daisi
+mail: Daisi_Lenathen@example.com
+carLicense: DQILRJE
+departmentNumber: 7522
+employeeType: Manager
+homePhone: +1 213 288-2856
+initials: D. L.
+mobile: +1 408 189-7434
+pager: +1 804 830-2227
+manager: cn=Fikre Reckhard
+secretary: cn=Chickie Swearingen
+roomNumber: 8443
+
+dn: cn=Kala Cohoe, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kala Cohoe
+sn: Cohoe
+description: This is Kala Cohoe's description
+facsimileTelephoneNumber: +1 804 464-6621
+l: Menlo Park
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 279
+telephoneNumber: +1 408 445-8566
+title: Junior Product Testing Czar
+userPassword: eohoCalaK
+uid: Kala_Cohoe
+givenName: Kala
+mail: Kala_Cohoe@example.com
+carLicense: 83O6O9C
+departmentNumber: 4001
+employeeType: Manager
+homePhone: +1 510 395-6239
+initials: K. C.
+mobile: +1 408 280-9013
+pager: +1 408 456-6256
+manager: cn=Dorice Tihanyi
+secretary: cn=Jock Wandel
+roomNumber: 1488
+
+dn: cn=Ashraf Philbeck, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ashraf Philbeck
+sn: Philbeck
+description: This is Ashraf Philbeck's description
+facsimileTelephoneNumber: +1 510 510-2082
+l: Armonk
+ou: Accounting
+postalAddress: example$Accounting$Dept # 815
+telephoneNumber: +1 303 409-9870
+title: Chief Accounting Fellow
+userPassword: kceblihPfa
+uid: Ashraf_Philbeck
+givenName: Ashraf
+mail: Ashraf_Philbeck@example.com
+carLicense: O9HJ38K
+departmentNumber: 3333
+employeeType: Contract
+homePhone: +1 213 369-2997
+initials: A. P.
+mobile: +1 206 533-4383
+pager: +1 510 627-6913
+manager: cn=Brandea Critchley
+secretary: cn=Simona Downs
+roomNumber: 1927
+
+dn: cn=Bud Luetchford, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Bud Luetchford
+sn: Luetchford
+description: This is Bud Luetchford's description
+facsimileTelephoneNumber: +1 408 815-1654
+l: Milpitas
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 375
+telephoneNumber: +1 206 938-7684
+title: Junior Janitorial Punk
+userPassword: drofhcteuL
+uid: Bud_Luetchford
+givenName: Bud
+mail: Bud_Luetchford@example.com
+carLicense: 4HOZS1C
+departmentNumber: 2862
+employeeType: Normal
+homePhone: +1 408 697-2759
+initials: B. L.
+mobile: +1 415 988-7571
+pager: +1 71 724-3491
+manager: cn=Youwen Kirkby
+secretary: cn=Wendy Orsini
+roomNumber: 4401
+
+dn: cn=Gee-Meng Howie, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gee-Meng Howie
+sn: Howie
+description: This is Gee-Meng Howie's description
+facsimileTelephoneNumber: +1 303 729-1050
+l: Redwood Shores
+ou: Administrative
+postalAddress: example$Administrative$Dept # 343
+telephoneNumber: +1 804 261-8663
+title: Junior Administrative Admin
+userPassword: eiwoHgneM-
+uid: Gee-Meng_Howie
+givenName: Gee-Meng
+mail: Gee-Meng_Howie@example.com
+carLicense: GHFQY6Z
+departmentNumber: 9631
+employeeType: Manager
+homePhone: +1 206 608-7175
+initials: G. H.
+mobile: +1 510 410-4154
+pager: +1 408 723-2555
+manager: cn=Faun Passin
+secretary: cn=Jack Xpmbld
+roomNumber: 3436
+
+dn: cn=Phedra Darrimon, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Phedra Darrimon
+sn: Darrimon
+description: This is Phedra Darrimon's description
+facsimileTelephoneNumber: +1 303 138-9353
+l: Armonk
+ou: Management
+postalAddress: example$Management$Dept # 405
+telephoneNumber: +1 818 405-8636
+title: Elite Management Vice President
+userPassword: nomirraDar
+uid: Phedra_Darrimon
+givenName: Phedra
+mail: Phedra_Darrimon@example.com
+carLicense: SH81DXM
+departmentNumber: 2202
+employeeType: Temp
+homePhone: +1 408 271-1626
+initials: P. D.
+mobile: +1 408 464-4771
+pager: +1 206 437-2201
+manager: cn=Del Ermarkaryan
+secretary: cn=Lulu Terwey
+roomNumber: 4969
+
+dn: cn=Elvira Nunn, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Elvira Nunn
+sn: Nunn
+description: This is Elvira Nunn's description
+facsimileTelephoneNumber: +1 303 321-7280
+l: Menlo Park
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 345
+telephoneNumber: +1 206 655-9226
+title: Elite Product Testing Developer
+userPassword: nnuNarivlE
+uid: Elvira_Nunn
+givenName: Elvira
+mail: Elvira_Nunn@example.com
+carLicense: G60QAK0
+departmentNumber: 2699
+employeeType: Temp
+homePhone: +1 510 573-4182
+initials: E. N.
+mobile: +1 818 935-2274
+pager: +1 818 681-9250
+manager: cn=Essie Csop
+secretary: cn=Fredra Bourgault
+roomNumber: 8155
+
+dn: cn=Patti Rasmussen, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Patti Rasmussen
+sn: Rasmussen
+description: This is Patti Rasmussen's description
+facsimileTelephoneNumber: +1 71 113-6095
+l: Redwood Shores
+ou: Management
+postalAddress: example$Management$Dept # 791
+telephoneNumber: +1 71 359-5237
+title: Master Management Madonna
+userPassword: nessumsaRi
+uid: Patti_Rasmussen
+givenName: Patti
+mail: Patti_Rasmussen@example.com
+carLicense: QWBN2VE
+departmentNumber: 4044
+employeeType: Manager
+homePhone: +1 415 861-8446
+initials: P. R.
+mobile: +1 408 602-7137
+pager: +1 415 248-6015
+manager: cn=Anver Zivilik
+secretary: cn=Marena Dorval
+roomNumber: 9158
+
+dn: cn=Kelsey Adjangba, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kelsey Adjangba
+sn: Adjangba
+description: This is Kelsey Adjangba's description
+facsimileTelephoneNumber: +1 415 446-4800
+l: Redmond
+ou: Product Development
+postalAddress: example$Product Development$Dept # 284
+telephoneNumber: +1 213 302-5397
+title: Senior Product Development Director
+userPassword: abgnajdAye
+uid: Kelsey_Adjangba
+givenName: Kelsey
+mail: Kelsey_Adjangba@example.com
+carLicense: 42L6PYH
+departmentNumber: 2664
+employeeType: Contract
+homePhone: +1 408 913-8280
+initials: K. A.
+mobile: +1 206 549-3065
+pager: +1 408 744-1860
+manager: cn=Evans Blaylock
+secretary: cn=Wilie Fields
+roomNumber: 8069
+
+dn: cn=Flor Maidens, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Flor Maidens
+sn: Maidens
+description: This is Flor Maidens's description
+facsimileTelephoneNumber: +1 818 357-8091
+l: San Francisco
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 270
+telephoneNumber: +1 408 181-7229
+title: Senior Product Testing Consultant
+userPassword: snediaMrol
+uid: Flor_Maidens
+givenName: Flor
+mail: Flor_Maidens@example.com
+carLicense: PX8C8S7
+departmentNumber: 6989
+employeeType: Employee
+homePhone: +1 510 810-7529
+initials: F. M.
+mobile: +1 213 656-3768
+pager: +1 510 985-6317
+manager: cn=Steffen Deatherage
+secretary: cn=Gretta Sherow
+roomNumber: 2598
+
+dn: cn=Derrik Branham, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Derrik Branham
+sn: Branham
+description: This is Derrik Branham's description
+facsimileTelephoneNumber: +1 71 540-4555
+l: San Mateo
+ou: Management
+postalAddress: example$Management$Dept # 489
+telephoneNumber: +1 804 653-4502
+title: Associate Management Vice President
+userPassword: mahnarBkir
+uid: Derrik_Branham
+givenName: Derrik
+mail: Derrik_Branham@example.com
+carLicense: 42X0E0L
+departmentNumber: 2307
+employeeType: Manager
+homePhone: +1 408 512-1260
+initials: D. B.
+mobile: +1 71 992-2538
+pager: +1 818 198-6606
+manager: cn=Nader Peate
+secretary: cn=Vincente Cassar
+roomNumber: 4204
+
+dn: cn=Maurice Robles, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Maurice Robles
+sn: Robles
+description: This is Maurice Robles's description
+facsimileTelephoneNumber: +1 213 175-1117
+l: Milpitas
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 802
+telephoneNumber: +1 804 335-9106
+title: Master Product Testing Consultant
+userPassword: selboRecir
+uid: Maurice_Robles
+givenName: Maurice
+mail: Maurice_Robles@example.com
+carLicense: I0RCX50
+departmentNumber: 5445
+employeeType: Manager
+homePhone: +1 71 631-6192
+initials: M. R.
+mobile: +1 415 777-4720
+pager: +1 408 898-9753
+manager: cn=Dusty Hyte
+secretary: cn=Sergei Mesquita
+roomNumber: 1159
+
+dn: cn=Radames Lentz, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Radames Lentz
+sn: Lentz
+description: This is Radames Lentz's description
+facsimileTelephoneNumber: +1 213 325-2266
+l: Redwood Shores
+ou: Administrative
+postalAddress: example$Administrative$Dept # 578
+telephoneNumber: +1 415 844-6002
+title: Supreme Administrative Visionary
+userPassword: ztneLsemad
+uid: Radames_Lentz
+givenName: Radames
+mail: Radames_Lentz@example.com
+carLicense: LIUHL8A
+departmentNumber: 9200
+employeeType: Manager
+homePhone: +1 510 835-4657
+initials: R. L.
+mobile: +1 510 755-4758
+pager: +1 71 826-9342
+manager: cn=Gerda Gulick
+secretary: cn=Earle Stallings
+roomNumber: 1670
+
+dn: cn=Shelba Ketcham, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Shelba Ketcham
+sn: Ketcham
+description: This is Shelba Ketcham's description
+facsimileTelephoneNumber: +1 71 114-1567
+l: Milpitas
+ou: Administrative
+postalAddress: example$Administrative$Dept # 14
+telephoneNumber: +1 213 887-3220
+title: Master Administrative Developer
+userPassword: mahcteKabl
+uid: Shelba_Ketcham
+givenName: Shelba
+mail: Shelba_Ketcham@example.com
+carLicense: UZAO4F6
+departmentNumber: 3536
+employeeType: Manager
+homePhone: +1 415 899-5339
+initials: S. K.
+mobile: +1 206 522-3282
+pager: +1 415 197-6248
+manager: cn=Herb McRuvie
+secretary: cn=Norikazu Tognoni
+roomNumber: 9741
+
+dn: cn=Jacki Kryski, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jacki Kryski
+sn: Kryski
+description: This is Jacki Kryski's description
+facsimileTelephoneNumber: +1 804 427-6956
+l: Redmond
+ou: Planning
+postalAddress: example$Planning$Dept # 431
+telephoneNumber: +1 415 611-5818
+title: Master Planning Artist
+userPassword: iksyrKikca
+uid: Jacki_Kryski
+givenName: Jacki
+mail: Jacki_Kryski@example.com
+carLicense: T2DOFBJ
+departmentNumber: 3473
+employeeType: Employee
+homePhone: +1 213 134-6640
+initials: J. K.
+mobile: +1 213 658-7887
+pager: +1 71 414-6419
+manager: cn=Edie Arkesteijn
+secretary: cn=Stefan Temp
+roomNumber: 8814
+
+dn: cn=Jill Claxton, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jill Claxton
+sn: Claxton
+description: This is Jill Claxton's description
+facsimileTelephoneNumber: +1 303 860-8984
+l: Emeryville
+ou: Planning
+postalAddress: example$Planning$Dept # 921
+telephoneNumber: +1 206 527-1706
+title: Supreme Planning Assistant
+userPassword: notxalClli
+uid: Jill_Claxton
+givenName: Jill
+mail: Jill_Claxton@example.com
+carLicense: VO6ZLUZ
+departmentNumber: 2000
+employeeType: Normal
+homePhone: +1 804 466-5758
+initials: J. C.
+mobile: +1 206 777-3124
+pager: +1 213 356-7164
+manager: cn=Benoit Corbitt
+secretary: cn=Ardelle Strader
+roomNumber: 6214
+
+dn: cn=Audrye Casanova, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Audrye Casanova
+sn: Casanova
+description: This is Audrye Casanova's description
+facsimileTelephoneNumber: +1 71 335-2611
+l: Santa Clara
+ou: Accounting
+postalAddress: example$Accounting$Dept # 508
+telephoneNumber: +1 510 794-3443
+title: Master Accounting Stooge
+userPassword: avonasaCey
+uid: Audrye_Casanova
+givenName: Audrye
+mail: Audrye_Casanova@example.com
+carLicense: IB8U70R
+departmentNumber: 716
+employeeType: Temp
+homePhone: +1 71 744-9481
+initials: A. C.
+mobile: +1 818 445-3413
+pager: +1 71 373-6330
+manager: cn=Marella Gehring
+secretary: cn=Lorrie StJames
+roomNumber: 7349
+
+dn: cn=Antonella Deek, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Antonella Deek
+sn: Deek
+description: This is Antonella Deek's description
+facsimileTelephoneNumber: +1 408 612-7907
+l: Santa Clara
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 440
+telephoneNumber: +1 804 159-1602
+title: Supreme Human Resources Architect
+userPassword: keeDalleno
+uid: Antonella_Deek
+givenName: Antonella
+mail: Antonella_Deek@example.com
+carLicense: ETYGM3Y
+departmentNumber: 5961
+employeeType: Contract
+homePhone: +1 206 245-7987
+initials: A. D.
+mobile: +1 415 420-2348
+pager: +1 71 855-2985
+manager: cn=Donna Rendon
+secretary: cn=Shandy Saunderson
+roomNumber: 6859
+
+dn: cn=Vishwa Systest, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Vishwa Systest
+sn: Systest
+description: This is Vishwa Systest's description
+facsimileTelephoneNumber: +1 71 985-7388
+l: Redwood Shores
+ou: Administrative
+postalAddress: example$Administrative$Dept # 99
+telephoneNumber: +1 415 236-8644
+title: Elite Administrative Yahoo
+userPassword: tsetsySawh
+uid: Vishwa_Systest
+givenName: Vishwa
+mail: Vishwa_Systest@example.com
+carLicense: F23TD75
+departmentNumber: 7245
+employeeType: Contract
+homePhone: +1 213 535-1250
+initials: V. S.
+mobile: +1 213 467-1508
+pager: +1 804 192-2068
+manager: cn=Marlie Davalo
+secretary: cn=Elena Kehler
+roomNumber: 3332
+
+dn: cn=Thuy Sorrentino, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Thuy Sorrentino
+sn: Sorrentino
+description: This is Thuy Sorrentino's description
+facsimileTelephoneNumber: +1 303 661-5865
+l: Redmond
+ou: Peons
+postalAddress: example$Peons$Dept # 563
+telephoneNumber: +1 71 241-8155
+title: Senior Peons Vice President
+userPassword: onitnerroS
+uid: Thuy_Sorrentino
+givenName: Thuy
+mail: Thuy_Sorrentino@example.com
+carLicense: XT8H4LL
+departmentNumber: 9503
+employeeType: Normal
+homePhone: +1 71 732-6008
+initials: T. S.
+mobile: +1 303 174-9452
+pager: +1 804 736-5264
+manager: cn=Ikram Somisetty
+secretary: cn=Subu D'Anjou
+roomNumber: 7065
+
+dn: cn=Mika Connelly, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Mika Connelly
+sn: Connelly
+description: This is Mika Connelly's description
+facsimileTelephoneNumber: +1 213 466-9121
+l: Santa Clara
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 195
+telephoneNumber: +1 804 881-7779
+title: Chief Human Resources Accountant
+userPassword: yllennoCak
+uid: Mika_Connelly
+givenName: Mika
+mail: Mika_Connelly@example.com
+carLicense: 6V7IYY7
+departmentNumber: 6921
+employeeType: Contract
+homePhone: +1 408 126-6367
+initials: M. C.
+mobile: +1 213 735-2246
+pager: +1 818 819-6286
+manager: cn=Redgie Hruska
+secretary: cn=Nazi Webber
+roomNumber: 7844
+
+dn: cn=Gwenette Markovich, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gwenette Markovich
+sn: Markovich
+description: This is Gwenette Markovich's description
+facsimileTelephoneNumber: +1 213 862-5730
+l: Santa Clara
+ou: Administrative
+postalAddress: example$Administrative$Dept # 281
+telephoneNumber: +1 213 710-5069
+title: Senior Administrative Grunt
+userPassword: hcivokraMe
+uid: Gwenette_Markovich
+givenName: Gwenette
+mail: Gwenette_Markovich@example.com
+carLicense: E6H2NGM
+departmentNumber: 8458
+employeeType: Manager
+homePhone: +1 206 608-7706
+initials: G. M.
+mobile: +1 213 209-5846
+pager: +1 510 218-9820
+manager: cn=Melisande Lotochinski
+secretary: cn=Barsha Gerynowicz
+roomNumber: 156
+
+dn: cn=Amjad Tavana, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Amjad Tavana
+sn: Tavana
+description: This is Amjad Tavana's description
+facsimileTelephoneNumber: +1 303 127-2675
+l: Sunnyvale
+ou: Payroll
+postalAddress: example$Payroll$Dept # 35
+telephoneNumber: +1 213 414-7581
+title: Master Payroll Developer
+userPassword: anavaTdajm
+uid: Amjad_Tavana
+givenName: Amjad
+mail: Amjad_Tavana@example.com
+carLicense: QZQTXXZ
+departmentNumber: 8566
+employeeType: Manager
+homePhone: +1 818 765-2743
+initials: A. T.
+mobile: +1 303 788-4053
+pager: +1 303 559-1680
+manager: cn=Tosca Thomlinson
+secretary: cn=Helena Leiding
+roomNumber: 169
+
+dn: cn=Glendon Glasgow, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Glendon Glasgow
+sn: Glasgow
+description: This is Glendon Glasgow's description
+facsimileTelephoneNumber: +1 415 108-6074
+l: Redmond
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 954
+telephoneNumber: +1 71 882-3826
+title: Elite Janitorial Vice President
+userPassword: wogsalGnod
+uid: Glendon_Glasgow
+givenName: Glendon
+mail: Glendon_Glasgow@example.com
+carLicense: MNCAS69
+departmentNumber: 4364
+employeeType: Employee
+homePhone: +1 804 273-6358
+initials: G. G.
+mobile: +1 415 904-2866
+pager: +1 213 813-1720
+manager: cn=Nichole Hyte
+secretary: cn=Vita Mayer
+roomNumber: 1835
+
+dn: cn=Candee Jamshidi, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Candee Jamshidi
+sn: Jamshidi
+description: This is Candee Jamshidi's description
+facsimileTelephoneNumber: +1 408 844-3640
+l: Palo Alto
+ou: Product Development
+postalAddress: example$Product Development$Dept # 858
+telephoneNumber: +1 71 295-4351
+title: Associate Product Development Punk
+userPassword: idihsmaJee
+uid: Candee_Jamshidi
+givenName: Candee
+mail: Candee_Jamshidi@example.com
+carLicense: 3RETBS2
+departmentNumber: 4681
+employeeType: Normal
+homePhone: +1 415 191-9303
+initials: C. J.
+mobile: +1 303 105-4529
+pager: +1 213 204-6497
+manager: cn=Wilhelmina Teitelbaum
+secretary: cn=Lydie Pitton
+roomNumber: 3702
+
+dn: cn=Tai-Jen Leon, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tai-Jen Leon
+sn: Leon
+description: This is Tai-Jen Leon's description
+facsimileTelephoneNumber: +1 303 881-2837
+l: San Mateo
+ou: Product Development
+postalAddress: example$Product Development$Dept # 984
+telephoneNumber: +1 303 739-3409
+title: Supreme Product Development Writer
+userPassword: noeLneJ-ia
+uid: Tai-Jen_Leon
+givenName: Tai-Jen
+mail: Tai-Jen_Leon@example.com
+carLicense: C28Y6H9
+departmentNumber: 8912
+employeeType: Normal
+homePhone: +1 213 775-3850
+initials: T. L.
+mobile: +1 71 887-3972
+pager: +1 804 229-3819
+manager: cn=Bibi Marleau
+secretary: cn=Kristan Gorfine
+roomNumber: 1495
+
+dn: cn=Phyl Arcouet, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Phyl Arcouet
+sn: Arcouet
+description: This is Phyl Arcouet's description
+facsimileTelephoneNumber: +1 408 679-4960
+l: Menlo Park
+ou: Planning
+postalAddress: example$Planning$Dept # 820
+telephoneNumber: +1 408 491-8848
+title: Chief Planning Evangelist
+userPassword: teuocrAlyh
+uid: Phyl_Arcouet
+givenName: Phyl
+mail: Phyl_Arcouet@example.com
+carLicense: N0J4XE6
+departmentNumber: 9253
+employeeType: Employee
+homePhone: +1 415 488-2625
+initials: P. A.
+mobile: +1 303 851-3126
+pager: +1 213 747-2501
+manager: cn=Renee Khosla
+secretary: cn=Erlene Granic
+roomNumber: 4682
+
+dn: cn=Lesly Torbert, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lesly Torbert
+sn: Torbert
+description: This is Lesly Torbert's description
+facsimileTelephoneNumber: +1 213 965-9953
+l: Sunnyvale
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 979
+telephoneNumber: +1 510 145-2379
+title: Elite Product Testing Accountant
+userPassword: trebroTyls
+uid: Lesly_Torbert
+givenName: Lesly
+mail: Lesly_Torbert@example.com
+carLicense: U1CQ5AP
+departmentNumber: 8198
+employeeType: Employee
+homePhone: +1 213 972-3091
+initials: L. T.
+mobile: +1 206 708-6966
+pager: +1 213 328-3883
+manager: cn=Gerianna Fadel
+secretary: cn=Jon Madigan
+roomNumber: 8472
+
+dn: cn=Wen-Kai Lilly, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Wen-Kai Lilly
+sn: Lilly
+description: This is Wen-Kai Lilly's description
+facsimileTelephoneNumber: +1 804 688-8196
+l: Redwood Shores
+ou: Management
+postalAddress: example$Management$Dept # 370
+telephoneNumber: +1 71 673-1670
+title: Junior Management Admin
+userPassword: ylliLiaK-n
+uid: Wen-Kai_Lilly
+givenName: Wen-Kai
+mail: Wen-Kai_Lilly@example.com
+carLicense: A3NP7VV
+departmentNumber: 8718
+employeeType: Manager
+homePhone: +1 510 775-7118
+initials: W. L.
+mobile: +1 71 731-2419
+pager: +1 818 695-1076
+manager: cn=Raymond Sheaffer
+secretary: cn=Guillermo Harrison
+roomNumber: 4720
+
+dn: cn=Shanti Heffernan, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Shanti Heffernan
+sn: Heffernan
+description: This is Shanti Heffernan's description
+facsimileTelephoneNumber: +1 213 440-4982
+l: Emeryville
+ou: Management
+postalAddress: example$Management$Dept # 240
+telephoneNumber: +1 804 256-5413
+title: Supreme Management Dictator
+userPassword: nanreffeHi
+uid: Shanti_Heffernan
+givenName: Shanti
+mail: Shanti_Heffernan@example.com
+carLicense: TW94LE1
+departmentNumber: 8437
+employeeType: Temp
+homePhone: +1 71 657-4760
+initials: S. H.
+mobile: +1 818 910-4409
+pager: +1 415 652-5036
+manager: cn=Estel Jacobson
+secretary: cn=Hatti Grover
+roomNumber: 4785
+
+dn: cn=Claudetta Vetrie, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Claudetta Vetrie
+sn: Vetrie
+description: This is Claudetta Vetrie's description
+facsimileTelephoneNumber: +1 213 831-3010
+l: San Francisco
+ou: Accounting
+postalAddress: example$Accounting$Dept # 408
+telephoneNumber: +1 206 564-6336
+title: Elite Accounting Mascot
+userPassword: eirteVatte
+uid: Claudetta_Vetrie
+givenName: Claudetta
+mail: Claudetta_Vetrie@example.com
+carLicense: 995LNFA
+departmentNumber: 9473
+employeeType: Temp
+homePhone: +1 818 121-9394
+initials: C. V.
+mobile: +1 804 309-9257
+pager: +1 818 127-6157
+manager: cn=Ginger Plotter
+secretary: cn=Gnni Anker
+roomNumber: 8906
+
+dn: cn=Narrima Ferraro, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Narrima Ferraro
+sn: Ferraro
+description: This is Narrima Ferraro's description
+facsimileTelephoneNumber: +1 415 604-8636
+l: Milpitas
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 940
+telephoneNumber: +1 510 988-6734
+title: Senior Product Testing Stooge
+userPassword: orarreFami
+uid: Narrima_Ferraro
+givenName: Narrima
+mail: Narrima_Ferraro@example.com
+carLicense: H1ZMMNP
+departmentNumber: 8163
+employeeType: Temp
+homePhone: +1 408 156-6261
+initials: N. F.
+mobile: +1 804 200-8860
+pager: +1 206 255-2739
+manager: cn=Syed Hooton
+secretary: cn=Wren IRCMARKET
+roomNumber: 9869
+
+dn: cn=Jagdish Intemann, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jagdish Intemann
+sn: Intemann
+description: This is Jagdish Intemann's description
+facsimileTelephoneNumber: +1 510 553-4156
+l: Redwood Shores
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 204
+telephoneNumber: +1 303 498-5597
+title: Junior Product Testing Pinhead
+userPassword: nnametnIhs
+uid: Jagdish_Intemann
+givenName: Jagdish
+mail: Jagdish_Intemann@example.com
+carLicense: SABGVXS
+departmentNumber: 1279
+employeeType: Manager
+homePhone: +1 213 960-9320
+initials: J. I.
+mobile: +1 415 127-8311
+pager: +1 804 870-4339
+manager: cn=Kalli Trevethan
+secretary: cn=Guenther Bowser
+roomNumber: 578
+
+dn: cn=Han-Co Hilliard, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Han-Co Hilliard
+sn: Hilliard
+description: This is Han-Co Hilliard's description
+facsimileTelephoneNumber: +1 415 626-3663
+l: Emeryville
+ou: Management
+postalAddress: example$Management$Dept # 970
+telephoneNumber: +1 818 237-9531
+title: Senior Management Vice President
+userPassword: drailliHoC
+uid: Han-Co_Hilliard
+givenName: Han-Co
+mail: Han-Co_Hilliard@example.com
+carLicense: Q770WJ8
+departmentNumber: 5265
+employeeType: Contract
+homePhone: +1 804 648-1132
+initials: H. H.
+mobile: +1 213 225-1074
+pager: +1 206 187-4190
+manager: cn=Varennes Donlon
+secretary: cn=Nils Bladon
+roomNumber: 1018
+
+dn: cn=Tape Lamonde, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tape Lamonde
+sn: Lamonde
+description: This is Tape Lamonde's description
+facsimileTelephoneNumber: +1 213 973-2554
+l: Cambridge
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 208
+telephoneNumber: +1 818 652-7569
+title: Junior Product Testing Mascot
+userPassword: ednomaLepa
+uid: Tape_Lamonde
+givenName: Tape
+mail: Tape_Lamonde@example.com
+carLicense: D7OUS6R
+departmentNumber: 2397
+employeeType: Manager
+homePhone: +1 213 462-7286
+initials: T. L.
+mobile: +1 408 278-1808
+pager: +1 303 141-3679
+manager: cn=Karisa Delzer
+secretary: cn=Cary Ottosson
+roomNumber: 583
+
+dn: cn=Bertina Simon, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Bertina Simon
+sn: Simon
+description: This is Bertina Simon's description
+facsimileTelephoneNumber: +1 804 334-9377
+l: Emeryville
+ou: Accounting
+postalAddress: example$Accounting$Dept # 702
+telephoneNumber: +1 303 374-4957
+title: Senior Accounting Punk
+userPassword: nomiSanitr
+uid: Bertina_Simon
+givenName: Bertina
+mail: Bertina_Simon@example.com
+carLicense: MLBFINM
+departmentNumber: 9010
+employeeType: Normal
+homePhone: +1 408 869-9392
+initials: B. S.
+mobile: +1 804 371-2530
+pager: +1 213 461-7236
+manager: cn=Ninon Suh
+secretary: cn=Rafa Moyer
+roomNumber: 9367
+
+dn: cn=Mable Scarffe, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Mable Scarffe
+sn: Scarffe
+description: This is Mable Scarffe's description
+facsimileTelephoneNumber: +1 213 193-6746
+l: Menlo Park
+ou: Administrative
+postalAddress: example$Administrative$Dept # 358
+telephoneNumber: +1 408 387-3910
+title: Elite Administrative Stooge
+userPassword: effracSelb
+uid: Mable_Scarffe
+givenName: Mable
+mail: Mable_Scarffe@example.com
+carLicense: P3H22ZQ
+departmentNumber: 2955
+employeeType: Employee
+homePhone: +1 408 680-9649
+initials: M. S.
+mobile: +1 303 821-6922
+pager: +1 213 877-2168
+manager: cn=Blanch Azarshahi
+secretary: cn=Milly Breton
+roomNumber: 1622
+
+dn: cn=Sheela Shishakly, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sheela Shishakly
+sn: Shishakly
+description: This is Sheela Shishakly's description
+facsimileTelephoneNumber: +1 71 673-7431
+l: Alameda
+ou: Product Development
+postalAddress: example$Product Development$Dept # 987
+telephoneNumber: +1 804 762-2980
+title: Supreme Product Development President
+userPassword: ylkahsihSa
+uid: Sheela_Shishakly
+givenName: Sheela
+mail: Sheela_Shishakly@example.com
+carLicense: 5IFJPQ2
+departmentNumber: 6433
+employeeType: Employee
+homePhone: +1 206 828-2857
+initials: S. S.
+mobile: +1 408 284-1761
+pager: +1 415 489-4013
+manager: cn=Tilmon Hume
+secretary: cn=Den Klutts
+roomNumber: 9152
+
+dn: cn=Barbaraanne Dantzler, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Barbaraanne Dantzler
+sn: Dantzler
+description: This is Barbaraanne Dantzler's description
+facsimileTelephoneNumber: +1 206 113-9209
+l: Menlo Park
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 251
+telephoneNumber: +1 408 773-2407
+title: Chief Human Resources Mascot
+userPassword: relztnaDen
+uid: Barbaraanne_Dantzler
+givenName: Barbaraanne
+mail: Barbaraanne_Dantzler@example.com
+carLicense: NLBLPQV
+departmentNumber: 1728
+employeeType: Normal
+homePhone: +1 213 649-4726
+initials: B. D.
+mobile: +1 818 345-9799
+pager: +1 818 812-7609
+manager: cn=Elana Oziskender
+secretary: cn=Morgen Somppi
+roomNumber: 5920
+
+dn: cn=Marijo Vilis, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marijo Vilis
+sn: Vilis
+description: This is Marijo Vilis's description
+facsimileTelephoneNumber: +1 818 987-4698
+l: Cupertino
+ou: Peons
+postalAddress: example$Peons$Dept # 180
+telephoneNumber: +1 818 596-8525
+title: Junior Peons Yahoo
+userPassword: siliVojira
+uid: Marijo_Vilis
+givenName: Marijo
+mail: Marijo_Vilis@example.com
+carLicense: GV8EVPP
+departmentNumber: 7763
+employeeType: Manager
+homePhone: +1 408 246-1705
+initials: M. V.
+mobile: +1 303 165-6594
+pager: +1 510 255-5965
+manager: cn=Carey Fronsee
+secretary: cn=Kelsey Sohns
+roomNumber: 1306
+
+dn: cn=Helaine McHale, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Helaine McHale
+sn: McHale
+description: This is Helaine McHale's description
+facsimileTelephoneNumber: +1 408 114-3298
+l: Mountain View
+ou: Planning
+postalAddress: example$Planning$Dept # 358
+telephoneNumber: +1 206 543-4860
+title: Chief Planning Evangelist
+userPassword: elaHcMenia
+uid: Helaine_McHale
+givenName: Helaine
+mail: Helaine_McHale@example.com
+carLicense: 3UBUU5W
+departmentNumber: 1841
+employeeType: Temp
+homePhone: +1 408 144-3197
+initials: H. M.
+mobile: +1 303 109-7845
+pager: +1 408 420-5460
+manager: cn=Errol Mishina
+secretary: cn=Twana McLawhon
+roomNumber: 5704
+
+dn: cn=Devonne Siegel, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Devonne Siegel
+sn: Siegel
+description: This is Devonne Siegel's description
+facsimileTelephoneNumber: +1 804 930-1674
+l: Alameda
+ou: Product Development
+postalAddress: example$Product Development$Dept # 58
+telephoneNumber: +1 71 250-2535
+title: Associate Product Development Stooge
+userPassword: legeiSenno
+uid: Devonne_Siegel
+givenName: Devonne
+mail: Devonne_Siegel@example.com
+carLicense: VEEZJRC
+departmentNumber: 5688
+employeeType: Contract
+homePhone: +1 510 539-1810
+initials: D. S.
+mobile: +1 510 474-7533
+pager: +1 408 339-2999
+manager: cn=Georgianne Tu
+secretary: cn=Yoshiko Foucault
+roomNumber: 5007
+
+dn: cn=Cherlyn Stasaski, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Cherlyn Stasaski
+sn: Stasaski
+description: This is Cherlyn Stasaski's description
+facsimileTelephoneNumber: +1 303 531-2122
+l: Cupertino
+ou: Accounting
+postalAddress: example$Accounting$Dept # 305
+telephoneNumber: +1 206 102-5841
+title: Elite Accounting Visionary
+userPassword: iksasatSny
+uid: Cherlyn_Stasaski
+givenName: Cherlyn
+mail: Cherlyn_Stasaski@example.com
+carLicense: 229221G
+departmentNumber: 6792
+employeeType: Normal
+homePhone: +1 303 950-9071
+initials: C. S.
+mobile: +1 71 163-1113
+pager: +1 415 356-6049
+manager: cn=Sabina Harte
+secretary: cn=Said Rizewiski
+roomNumber: 9510
+
+dn: cn=Bernetta Pena, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Bernetta Pena
+sn: Pena
+description: This is Bernetta Pena's description
+facsimileTelephoneNumber: +1 303 915-2046
+l: Mountain View
+ou: Payroll
+postalAddress: example$Payroll$Dept # 651
+telephoneNumber: +1 818 990-8944
+title: Supreme Payroll Assistant
+userPassword: anePattenr
+uid: Bernetta_Pena
+givenName: Bernetta
+mail: Bernetta_Pena@example.com
+carLicense: 5PEYCY4
+departmentNumber: 6975
+employeeType: Manager
+homePhone: +1 408 429-9083
+initials: B. P.
+mobile: +1 213 624-1045
+pager: +1 408 544-6654
+manager: cn=Janeen Geuder
+secretary: cn=Hengameh Gravitt
+roomNumber: 6328
+
+dn: cn=Kissiah Greszczuk, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kissiah Greszczuk
+sn: Greszczuk
+description: This is Kissiah Greszczuk's description
+facsimileTelephoneNumber: +1 510 938-8051
+l: Armonk
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 935
+telephoneNumber: +1 408 928-6796
+title: Supreme Product Testing Stooge
+userPassword: kuzczserGh
+uid: Kissiah_Greszczuk
+givenName: Kissiah
+mail: Kissiah_Greszczuk@example.com
+carLicense: T8TE7ZE
+departmentNumber: 3218
+employeeType: Normal
+homePhone: +1 510 643-8421
+initials: K. G.
+mobile: +1 408 709-4773
+pager: +1 206 551-6228
+manager: cn=Olusola Mattes
+secretary: cn=Wanda Avery
+roomNumber: 2052
+
+dn: cn=Bello Toole, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Bello Toole
+sn: Toole
+description: This is Bello Toole's description
+facsimileTelephoneNumber: +1 804 808-5332
+l: Alameda
+ou: Planning
+postalAddress: example$Planning$Dept # 580
+telephoneNumber: +1 303 144-1714
+title: Supreme Planning Director
+userPassword: elooTolleB
+uid: Bello_Toole
+givenName: Bello
+mail: Bello_Toole@example.com
+carLicense: 3G2CJKZ
+departmentNumber: 1080
+employeeType: Contract
+homePhone: +1 206 231-6545
+initials: B. T.
+mobile: +1 415 807-3208
+pager: +1 804 971-6767
+manager: cn=Sherwyn Verardi
+secretary: cn=Kylie Unkefer
+roomNumber: 5390
+
+dn: cn=Elizabeth Aydin, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Elizabeth Aydin
+sn: Aydin
+description: This is Elizabeth Aydin's description
+facsimileTelephoneNumber: +1 415 164-4898
+l: Redwood Shores
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 257
+telephoneNumber: +1 818 124-4732
+title: Supreme Product Testing Vice President
+userPassword: nidyAhteba
+uid: Elizabeth_Aydin
+givenName: Elizabeth
+mail: Elizabeth_Aydin@example.com
+carLicense: GWGJRNE
+departmentNumber: 4864
+employeeType: Contract
+homePhone: +1 206 221-2740
+initials: E. A.
+mobile: +1 818 341-5047
+pager: +1 510 567-5979
+manager: cn=Monroe Bereza
+secretary: cn=Prue Rastelli
+roomNumber: 9869
+
+dn: cn=Zahirul Holcombe, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Zahirul Holcombe
+sn: Holcombe
+description: This is Zahirul Holcombe's description
+facsimileTelephoneNumber: +1 818 324-1197
+l: San Mateo
+ou: Administrative
+postalAddress: example$Administrative$Dept # 848
+telephoneNumber: +1 408 272-3182
+title: Master Administrative Manager
+userPassword: ebmocloHlu
+uid: Zahirul_Holcombe
+givenName: Zahirul
+mail: Zahirul_Holcombe@example.com
+carLicense: N1PL49T
+departmentNumber: 7494
+employeeType: Contract
+homePhone: +1 206 459-1493
+initials: Z. H.
+mobile: +1 804 957-2062
+pager: +1 71 496-2453
+manager: cn=Mehmud McWalters
+secretary: cn=Julita Felicetti
+roomNumber: 6113
+
+dn: cn=Bellina Moledina, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Bellina Moledina
+sn: Moledina
+description: This is Bellina Moledina's description
+facsimileTelephoneNumber: +1 213 554-4858
+l: Emeryville
+ou: Product Development
+postalAddress: example$Product Development$Dept # 458
+telephoneNumber: +1 415 754-5468
+title: Junior Product Development Evangelist
+userPassword: anideloMan
+uid: Bellina_Moledina
+givenName: Bellina
+mail: Bellina_Moledina@example.com
+carLicense: HHJW54Z
+departmentNumber: 6960
+employeeType: Contract
+homePhone: +1 71 853-4918
+initials: B. M.
+mobile: +1 415 769-3122
+pager: +1 213 928-1483
+manager: cn=Astra Brownfield
+secretary: cn=Melody Rasberry
+roomNumber: 8075
+
+dn: cn=Lilin Grosse, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lilin Grosse
+sn: Grosse
+description: This is Lilin Grosse's description
+facsimileTelephoneNumber: +1 206 793-1541
+l: Cambridge
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 587
+telephoneNumber: +1 213 968-8578
+title: Elite Product Testing Technician
+userPassword: essorGnili
+uid: Lilin_Grosse
+givenName: Lilin
+mail: Lilin_Grosse@example.com
+carLicense: J3PERBY
+departmentNumber: 639
+employeeType: Contract
+homePhone: +1 71 694-2377
+initials: L. G.
+mobile: +1 510 522-4777
+pager: +1 71 836-3761
+manager: cn=Norikatsu Doskas
+secretary: cn=Garland Gilliam
+roomNumber: 9620
+
+dn: cn=Inessa Domanico, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Inessa Domanico
+sn: Domanico
+description: This is Inessa Domanico's description
+facsimileTelephoneNumber: +1 408 357-4921
+l: Milpitas
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 624
+telephoneNumber: +1 213 959-2815
+title: Chief Human Resources Fellow
+userPassword: ocinamoDas
+uid: Inessa_Domanico
+givenName: Inessa
+mail: Inessa_Domanico@example.com
+carLicense: 6YSOGV6
+departmentNumber: 5439
+employeeType: Normal
+homePhone: +1 206 847-9333
+initials: I. D.
+mobile: +1 303 433-6682
+pager: +1 206 383-4713
+manager: cn=Mercer Kantor
+secretary: cn=Dianne Janssen
+roomNumber: 4005
+
+dn: cn=Kylen Abedi, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kylen Abedi
+sn: Abedi
+description: This is Kylen Abedi's description
+facsimileTelephoneNumber: +1 510 984-9118
+l: Milpitas
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 316
+telephoneNumber: +1 303 554-8893
+title: Elite Human Resources Accountant
+userPassword: idebAnelyK
+uid: Kylen_Abedi
+givenName: Kylen
+mail: Kylen_Abedi@example.com
+carLicense: EITVNIL
+departmentNumber: 6680
+employeeType: Employee
+homePhone: +1 206 968-4087
+initials: K. A.
+mobile: +1 206 225-3502
+pager: +1 303 928-6475
+manager: cn=Norcal Monaco
+secretary: cn=Joachim Ludwick
+roomNumber: 7234
+
+dn: cn=Par Chong, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Par Chong
+sn: Chong
+description: This is Par Chong's description
+facsimileTelephoneNumber: +1 206 264-3645
+l: Redwood Shores
+ou: Accounting
+postalAddress: example$Accounting$Dept # 730
+telephoneNumber: +1 818 638-6083
+title: Elite Accounting Dictator
+userPassword: gnohCraP
+uid: Par_Chong
+givenName: Par
+mail: Par_Chong@example.com
+carLicense: B1BMTWO
+departmentNumber: 4833
+employeeType: Employee
+homePhone: +1 303 325-8280
+initials: P. C.
+mobile: +1 213 437-3268
+pager: +1 818 841-2460
+manager: cn=Chiu Yun
+secretary: cn=Shana Cuggy
+roomNumber: 60
+
+dn: cn=Roberta Liao, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Roberta Liao
+sn: Liao
+description: This is Roberta Liao's description
+facsimileTelephoneNumber: +1 818 953-9645
+l: Cupertino
+ou: Payroll
+postalAddress: example$Payroll$Dept # 317
+telephoneNumber: +1 206 400-2865
+title: Chief Payroll Developer
+userPassword: oaiLatrebo
+uid: Roberta_Liao
+givenName: Roberta
+mail: Roberta_Liao@example.com
+carLicense: SRJNE9E
+departmentNumber: 9613
+employeeType: Temp
+homePhone: +1 71 684-9809
+initials: R. L.
+mobile: +1 510 655-7108
+pager: +1 818 788-8495
+manager: cn=Canadian Bragg
+secretary: cn=Drona Adjangba
+roomNumber: 5633
+
+dn: cn=Maycel El-Hawary, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Maycel El-Hawary
+sn: El-Hawary
+description: This is Maycel El-Hawary's description
+facsimileTelephoneNumber: +1 303 399-7655
+l: San Jose
+ou: Payroll
+postalAddress: example$Payroll$Dept # 531
+telephoneNumber: +1 303 279-2842
+title: Elite Payroll Visionary
+userPassword: yrawaH-lEl
+uid: Maycel_El-Hawary
+givenName: Maycel
+mail: Maycel_El-Hawary@example.com
+carLicense: K77UEL4
+departmentNumber: 6120
+employeeType: Manager
+homePhone: +1 71 196-9815
+initials: M. E.
+mobile: +1 206 441-2049
+pager: +1 303 134-2807
+manager: cn=Huan-yu Joffe
+secretary: cn=Lyndia Baum
+roomNumber: 9476
+
+dn: cn=Jannel Chern, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jannel Chern
+sn: Chern
+description: This is Jannel Chern's description
+facsimileTelephoneNumber: +1 818 740-6505
+l: Redmond
+ou: Peons
+postalAddress: example$Peons$Dept # 986
+telephoneNumber: +1 415 118-3889
+title: Junior Peons Director
+userPassword: nrehClenna
+uid: Jannel_Chern
+givenName: Jannel
+mail: Jannel_Chern@example.com
+carLicense: FBBLADO
+departmentNumber: 3009
+employeeType: Manager
+homePhone: +1 206 822-6702
+initials: J. C.
+mobile: +1 818 789-4145
+pager: +1 818 126-3562
+manager: cn=Vrouwerff Revill
+secretary: cn=Lyda Loudiadis
+roomNumber: 8822
+
+dn: cn=Corabelle Beckie, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Corabelle Beckie
+sn: Beckie
+description: This is Corabelle Beckie's description
+facsimileTelephoneNumber: +1 510 124-8955
+l: San Francisco
+ou: Management
+postalAddress: example$Management$Dept # 233
+telephoneNumber: +1 510 412-2726
+title: Supreme Management Stooge
+userPassword: eikceBelle
+uid: Corabelle_Beckie
+givenName: Corabelle
+mail: Corabelle_Beckie@example.com
+carLicense: KLOQJP1
+departmentNumber: 4890
+employeeType: Contract
+homePhone: +1 71 673-3892
+initials: C. B.
+mobile: +1 206 554-2122
+pager: +1 415 130-5306
+manager: cn=Hilda Lyon
+secretary: cn=Sisile Behroozi
+roomNumber: 3696
+
+dn: cn=Jann Eansor, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jann Eansor
+sn: Eansor
+description: This is Jann Eansor's description
+facsimileTelephoneNumber: +1 213 522-3563
+l: Mountain View
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 40
+telephoneNumber: +1 408 879-5681
+title: Junior Janitorial Pinhead
+userPassword: rosnaEnnaJ
+uid: Jann_Eansor
+givenName: Jann
+mail: Jann_Eansor@example.com
+carLicense: Q4P4YIZ
+departmentNumber: 7675
+employeeType: Employee
+homePhone: +1 213 654-4780
+initials: J. E.
+mobile: +1 804 198-3764
+pager: +1 213 666-1076
+manager: cn=Garth Carevic
+secretary: cn=Lillie Schnupp
+roomNumber: 2879
+
+dn: cn=Berrie Qu, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Berrie Qu
+sn: Qu
+description: This is Berrie Qu's description
+facsimileTelephoneNumber: +1 213 166-4023
+l: Cupertino
+ou: Peons
+postalAddress: example$Peons$Dept # 998
+telephoneNumber: +1 510 192-3177
+title: Junior Peons Developer
+userPassword: uQeirreB
+uid: Berrie_Qu
+givenName: Berrie
+mail: Berrie_Qu@example.com
+carLicense: 3JNKHK6
+departmentNumber: 4809
+employeeType: Normal
+homePhone: +1 818 289-4386
+initials: B. Q.
+mobile: +1 408 941-4537
+pager: +1 804 660-5286
+manager: cn=Nessy Kosowan
+secretary: cn=Darleen Lalu
+roomNumber: 7362
+
+dn: cn=Martine Shabo, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Martine Shabo
+sn: Shabo
+description: This is Martine Shabo's description
+facsimileTelephoneNumber: +1 804 672-9719
+l: Alameda
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 446
+telephoneNumber: +1 213 190-7848
+title: Senior Product Testing President
+userPassword: obahSenitr
+uid: Martine_Shabo
+givenName: Martine
+mail: Martine_Shabo@example.com
+carLicense: VGJPYSK
+departmentNumber: 3001
+employeeType: Normal
+homePhone: +1 415 431-8974
+initials: M. S.
+mobile: +1 408 213-2304
+pager: +1 804 606-5737
+manager: cn=Drusie Padgett
+secretary: cn=Viviyan Strock
+roomNumber: 3728
+
+dn: cn=Antonietta Nugent, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Antonietta Nugent
+sn: Nugent
+description: This is Antonietta Nugent's description
+facsimileTelephoneNumber: +1 71 961-9430
+l: Orem
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 419
+telephoneNumber: +1 206 446-9235
+title: Chief Janitorial Consultant
+userPassword: tneguNatte
+uid: Antonietta_Nugent
+givenName: Antonietta
+mail: Antonietta_Nugent@example.com
+carLicense: R8UASV6
+departmentNumber: 5142
+employeeType: Employee
+homePhone: +1 71 824-6004
+initials: A. N.
+mobile: +1 213 985-8253
+pager: +1 415 224-2804
+manager: cn=Violetta Darrimon
+secretary: cn=Bernd Klapper
+roomNumber: 5356
+
+dn: cn=Jenn Driscoll, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jenn Driscoll
+sn: Driscoll
+description: This is Jenn Driscoll's description
+facsimileTelephoneNumber: +1 818 910-6972
+l: Alameda
+ou: Planning
+postalAddress: example$Planning$Dept # 390
+telephoneNumber: +1 213 508-9453
+title: Supreme Planning President
+userPassword: llocsirDnn
+uid: Jenn_Driscoll
+givenName: Jenn
+mail: Jenn_Driscoll@example.com
+carLicense: H223TV7
+departmentNumber: 9552
+employeeType: Temp
+homePhone: +1 510 990-8697
+initials: J. D.
+mobile: +1 510 228-3296
+pager: +1 408 175-1221
+manager: cn=Phelia Tadevich
+secretary: cn=Latashia Fixsen
+roomNumber: 9169
+
+dn: cn=Franciska Dueck, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Franciska Dueck
+sn: Dueck
+description: This is Franciska Dueck's description
+facsimileTelephoneNumber: +1 206 290-2456
+l: Fremont
+ou: Payroll
+postalAddress: example$Payroll$Dept # 881
+telephoneNumber: +1 408 913-9714
+title: Elite Payroll Punk
+userPassword: kceuDaksic
+uid: Franciska_Dueck
+givenName: Franciska
+mail: Franciska_Dueck@example.com
+carLicense: 683B79B
+departmentNumber: 7906
+employeeType: Temp
+homePhone: +1 303 334-7097
+initials: F. D.
+mobile: +1 206 141-4537
+pager: +1 71 899-7525
+manager: cn=Linnea Conley
+secretary: cn=Lorita Grills
+roomNumber: 4590
+
+dn: cn=Tape Coe, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tape Coe
+sn: Coe
+description: This is Tape Coe's description
+facsimileTelephoneNumber: +1 804 928-7334
+l: San Mateo
+ou: Payroll
+postalAddress: example$Payroll$Dept # 874
+telephoneNumber: +1 206 177-6708
+title: Junior Payroll Engineer
+userPassword: eoCepaT
+uid: Tape_Coe
+givenName: Tape
+mail: Tape_Coe@example.com
+carLicense: M7J3ES5
+departmentNumber: 1408
+employeeType: Manager
+homePhone: +1 408 777-2780
+initials: T. C.
+mobile: +1 303 182-5881
+pager: +1 415 815-6331
+manager: cn=Walley Shewchenko
+secretary: cn=Stephan Boarder
+roomNumber: 7866
+
+dn: cn=Minnnie Gould, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Minnnie Gould
+sn: Gould
+description: This is Minnnie Gould's description
+facsimileTelephoneNumber: +1 818 483-2575
+l: Redwood Shores
+ou: Administrative
+postalAddress: example$Administrative$Dept # 138
+telephoneNumber: +1 818 368-5111
+title: Chief Administrative Accountant
+userPassword: dluoGeinnn
+uid: Minnnie_Gould
+givenName: Minnnie
+mail: Minnnie_Gould@example.com
+carLicense: 2JDEEK3
+departmentNumber: 3692
+employeeType: Manager
+homePhone: +1 206 275-8932
+initials: M. G.
+mobile: +1 213 233-7285
+pager: +1 510 511-4242
+manager: cn=Anya Moledina
+secretary: cn=Hall Heybroek
+roomNumber: 77
+
+dn: cn=Tak Sherman, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tak Sherman
+sn: Sherman
+description: This is Tak Sherman's description
+facsimileTelephoneNumber: +1 818 500-7283
+l: Cambridge
+ou: Administrative
+postalAddress: example$Administrative$Dept # 888
+telephoneNumber: +1 71 854-6383
+title: Associate Administrative Visionary
+userPassword: namrehSkaT
+uid: Tak_Sherman
+givenName: Tak
+mail: Tak_Sherman@example.com
+carLicense: W5C6733
+departmentNumber: 7508
+employeeType: Contract
+homePhone: +1 804 679-3727
+initials: T. S.
+mobile: +1 818 218-6838
+pager: +1 303 333-6467
+manager: cn=Sukhendu Klingsporn
+secretary: cn=Aruna Scotti
+roomNumber: 3688
+
+dn: cn=Lourdes Changes, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lourdes Changes
+sn: Changes
+description: This is Lourdes Changes's description
+facsimileTelephoneNumber: +1 303 488-2704
+l: Fremont
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 314
+telephoneNumber: +1 415 986-5442
+title: Elite Product Testing Architect
+userPassword: segnahCsed
+uid: Lourdes_Changes
+givenName: Lourdes
+mail: Lourdes_Changes@example.com
+carLicense: 5ED3L3T
+departmentNumber: 9178
+employeeType: Temp
+homePhone: +1 206 225-7834
+initials: L. C.
+mobile: +1 408 974-5773
+pager: +1 71 650-2622
+manager: cn=Gabey Ecroyd
+secretary: cn=Eleanore Moghe
+roomNumber: 9056
+
+dn: cn=Anneliese Saward, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Anneliese Saward
+sn: Saward
+description: This is Anneliese Saward's description
+facsimileTelephoneNumber: +1 303 368-5026
+l: Orem
+ou: Peons
+postalAddress: example$Peons$Dept # 104
+telephoneNumber: +1 818 334-9722
+title: Associate Peons Engineer
+userPassword: drawaSesei
+uid: Anneliese_Saward
+givenName: Anneliese
+mail: Anneliese_Saward@example.com
+carLicense: 4L1RFGG
+departmentNumber: 601
+employeeType: Employee
+homePhone: +1 71 285-4245
+initials: A. S.
+mobile: +1 415 214-2467
+pager: +1 804 210-1228
+manager: cn=Albertine Stars
+secretary: cn=Larysa Kreiger
+roomNumber: 7429
+
+dn: cn=Shahrokh Wennerstrom, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Shahrokh Wennerstrom
+sn: Wennerstrom
+description: This is Shahrokh Wennerstrom's description
+facsimileTelephoneNumber: +1 303 968-1645
+l: Menlo Park
+ou: Payroll
+postalAddress: example$Payroll$Dept # 801
+telephoneNumber: +1 303 344-6573
+title: Chief Payroll Mascot
+userPassword: mortsrenne
+uid: Shahrokh_Wennerstrom
+givenName: Shahrokh
+mail: Shahrokh_Wennerstrom@example.com
+carLicense: 60SLXN5
+departmentNumber: 7525
+employeeType: Normal
+homePhone: +1 206 400-6719
+initials: S. W.
+mobile: +1 303 263-7783
+pager: +1 415 177-8010
+manager: cn=Ofella Drabek
+secretary: cn=Muinck Ntprel
+roomNumber: 7182
+
+dn: cn=Adriaens Szaplonczay, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Adriaens Szaplonczay
+sn: Szaplonczay
+description: This is Adriaens Szaplonczay's description
+facsimileTelephoneNumber: +1 804 690-9862
+l: Redwood Shores
+ou: Accounting
+postalAddress: example$Accounting$Dept # 911
+telephoneNumber: +1 818 908-7106
+title: Elite Accounting Technician
+userPassword: yazcnolpaz
+uid: Adriaens_Szaplonczay
+givenName: Adriaens
+mail: Adriaens_Szaplonczay@example.com
+carLicense: 50P52KS
+departmentNumber: 9124
+employeeType: Employee
+homePhone: +1 408 996-8311
+initials: A. S.
+mobile: +1 71 908-6369
+pager: +1 206 382-5829
+manager: cn=Ester Dosenbach
+secretary: cn=Clarence Patchett
+roomNumber: 3335
+
+dn: cn=Li Giekes, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Li Giekes
+sn: Giekes
+description: This is Li Giekes's description
+facsimileTelephoneNumber: +1 804 744-9005
+l: San Mateo
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 988
+telephoneNumber: +1 303 898-4507
+title: Elite Human Resources Madonna
+userPassword: sekeiGiL
+uid: Li_Giekes
+givenName: Li
+mail: Li_Giekes@example.com
+carLicense: P8CFR4F
+departmentNumber: 9012
+employeeType: Employee
+homePhone: +1 408 335-3756
+initials: L. G.
+mobile: +1 303 446-5296
+pager: +1 71 389-6623
+manager: cn=Roanne Japp
+secretary: cn=Galen Khouderchan
+roomNumber: 5981
+
+dn: cn=Jeri Shemwell, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jeri Shemwell
+sn: Shemwell
+description: This is Jeri Shemwell's description
+facsimileTelephoneNumber: +1 818 352-3008
+l: San Mateo
+ou: Administrative
+postalAddress: example$Administrative$Dept # 781
+telephoneNumber: +1 206 716-2647
+title: Chief Administrative Vice President
+userPassword: llewmehSir
+uid: Jeri_Shemwell
+givenName: Jeri
+mail: Jeri_Shemwell@example.com
+carLicense: IP0PUCY
+departmentNumber: 100
+employeeType: Manager
+homePhone: +1 415 175-9006
+initials: J. S.
+mobile: +1 510 237-9592
+pager: +1 213 682-7219
+manager: cn=Dieter Tables
+secretary: cn=Noellyn Dodds
+roomNumber: 8663
+
+dn: cn=Jennifer Croxford, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jennifer Croxford
+sn: Croxford
+description: This is Jennifer Croxford's description
+facsimileTelephoneNumber: +1 415 621-2992
+l: Orem
+ou: Peons
+postalAddress: example$Peons$Dept # 367
+telephoneNumber: +1 71 594-9347
+title: Junior Peons Admin
+userPassword: drofxorCre
+uid: Jennifer_Croxford
+givenName: Jennifer
+mail: Jennifer_Croxford@example.com
+carLicense: UWO595X
+departmentNumber: 7134
+employeeType: Normal
+homePhone: +1 303 654-1112
+initials: J. C.
+mobile: +1 408 180-5348
+pager: +1 206 720-2029
+manager: cn=Carlene Lande
+secretary: cn=Rochette Ndububa
+roomNumber: 9470
+
+dn: cn=Dee dee Gockel, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dee dee Gockel
+sn: Gockel
+description: This is Dee dee Gockel's description
+facsimileTelephoneNumber: +1 303 747-9489
+l: Cambridge
+ou: Peons
+postalAddress: example$Peons$Dept # 798
+telephoneNumber: +1 206 924-8771
+title: Master Peons Technician
+userPassword: lekcoGeede
+uid: Dee dee_Gockel
+givenName: Dee dee
+mail: Dee dee_Gockel@example.com
+carLicense: 7K3MQN8
+departmentNumber: 8805
+employeeType: Contract
+homePhone: +1 71 346-4371
+initials: D. G.
+mobile: +1 303 661-4459
+pager: +1 804 830-5591
+manager: cn=Dacey Mims
+secretary: cn=Ashly Taheri
+roomNumber: 4604
+
+dn: cn=Hoekstra Brightwell, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Hoekstra Brightwell
+sn: Brightwell
+description: This is Hoekstra Brightwell's description
+facsimileTelephoneNumber: +1 71 612-4705
+l: Palo Alto
+ou: Management
+postalAddress: example$Management$Dept # 120
+telephoneNumber: +1 408 134-7123
+title: Master Management Sales Rep
+userPassword: llewthgirB
+uid: Hoekstra_Brightwell
+givenName: Hoekstra
+mail: Hoekstra_Brightwell@example.com
+carLicense: 93EY22B
+departmentNumber: 8716
+employeeType: Employee
+homePhone: +1 804 531-8765
+initials: H. B.
+mobile: +1 213 442-4612
+pager: +1 71 358-3421
+manager: cn=France Verma
+secretary: cn=Fulvia Konomis
+roomNumber: 8922
+
+dn: cn=Jany Beaudet, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jany Beaudet
+sn: Beaudet
+description: This is Jany Beaudet's description
+facsimileTelephoneNumber: +1 206 814-4739
+l: Mountain View
+ou: Payroll
+postalAddress: example$Payroll$Dept # 104
+telephoneNumber: +1 415 968-6035
+title: Chief Payroll Visionary
+userPassword: teduaeByna
+uid: Jany_Beaudet
+givenName: Jany
+mail: Jany_Beaudet@example.com
+carLicense: OQPQ66Z
+departmentNumber: 2168
+employeeType: Contract
+homePhone: +1 213 618-9981
+initials: J. B.
+mobile: +1 213 493-5690
+pager: +1 818 385-2922
+manager: cn=De-Anna Grafton
+secretary: cn=Mohammed Pancholy
+roomNumber: 9889
+
+dn: cn=Laury Hundrieser, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Laury Hundrieser
+sn: Hundrieser
+description: This is Laury Hundrieser's description
+facsimileTelephoneNumber: +1 818 519-4730
+l: Redwood Shores
+ou: Planning
+postalAddress: example$Planning$Dept # 748
+telephoneNumber: +1 804 710-4277
+title: Chief Planning Vice President
+userPassword: reseirdnuH
+uid: Laury_Hundrieser
+givenName: Laury
+mail: Laury_Hundrieser@example.com
+carLicense: 7VHPX3R
+departmentNumber: 9176
+employeeType: Temp
+homePhone: +1 303 430-3192
+initials: L. H.
+mobile: +1 213 684-4143
+pager: +1 408 367-8589
+manager: cn=Fonnie Freeburn
+secretary: cn=Jorrie Townson
+roomNumber: 6925
+
+dn: cn=Grey Wilby, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Grey Wilby
+sn: Wilby
+description: This is Grey Wilby's description
+facsimileTelephoneNumber: +1 303 916-2498
+l: Armonk
+ou: Management
+postalAddress: example$Management$Dept # 316
+telephoneNumber: +1 213 671-2278
+title: Associate Management Consultant
+userPassword: ybliWyerG
+uid: Grey_Wilby
+givenName: Grey
+mail: Grey_Wilby@example.com
+carLicense: S1KFLPX
+departmentNumber: 6452
+employeeType: Employee
+homePhone: +1 804 140-6181
+initials: G. W.
+mobile: +1 303 558-6863
+pager: +1 510 337-8464
+manager: cn=Ally Bruin
+secretary: cn=Kerstin Howell
+roomNumber: 8168
+
+dn: cn=Elicia Mendolia, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Elicia Mendolia
+sn: Mendolia
+description: This is Elicia Mendolia's description
+facsimileTelephoneNumber: +1 804 410-3925
+l: Sunnyvale
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 447
+telephoneNumber: +1 818 715-9162
+title: Junior Janitorial Admin
+userPassword: ailodneMai
+uid: Elicia_Mendolia
+givenName: Elicia
+mail: Elicia_Mendolia@example.com
+carLicense: B82QKWB
+departmentNumber: 9572
+employeeType: Temp
+homePhone: +1 71 820-8129
+initials: E. M.
+mobile: +1 71 498-8907
+pager: +1 71 705-3694
+manager: cn=Jean-Paul Javallas-Ross
+secretary: cn=Fil Toop
+roomNumber: 3904
+
+dn: cn=Malgosia Qainfo, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Malgosia Qainfo
+sn: Qainfo
+description: This is Malgosia Qainfo's description
+facsimileTelephoneNumber: +1 415 636-7122
+l: Cupertino
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 81
+telephoneNumber: +1 303 618-2084
+title: Elite Janitorial Admin
+userPassword: ofniaQaiso
+uid: Malgosia_Qainfo
+givenName: Malgosia
+mail: Malgosia_Qainfo@example.com
+carLicense: RTLNZ9G
+departmentNumber: 4425
+employeeType: Employee
+homePhone: +1 818 160-5331
+initials: M. Q.
+mobile: +1 510 358-5904
+pager: +1 415 723-1233
+manager: cn=Shahrokh Grossman
+secretary: cn=Bin Culberson
+roomNumber: 454
+
+dn: cn=Vladimir Hsieh, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Vladimir Hsieh
+sn: Hsieh
+description: This is Vladimir Hsieh's description
+facsimileTelephoneNumber: +1 415 114-1055
+l: Palo Alto
+ou: Peons
+postalAddress: example$Peons$Dept # 736
+telephoneNumber: +1 408 767-1357
+title: Senior Peons Vice President
+userPassword: heisHrimid
+uid: Vladimir_Hsieh
+givenName: Vladimir
+mail: Vladimir_Hsieh@example.com
+carLicense: MOBSD4C
+departmentNumber: 4340
+employeeType: Employee
+homePhone: +1 206 702-2498
+initials: V. H.
+mobile: +1 804 246-5559
+pager: +1 510 826-5717
+manager: cn=Astra Bracewell
+secretary: cn=Pradyumn Welten
+roomNumber: 866
+
+dn: cn=Patching Zattiero, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Patching Zattiero
+sn: Zattiero
+description: This is Patching Zattiero's description
+facsimileTelephoneNumber: +1 303 369-5585
+l: San Mateo
+ou: Management
+postalAddress: example$Management$Dept # 227
+telephoneNumber: +1 804 545-4875
+title: Elite Management Warrior
+userPassword: oreittaZgn
+uid: Patching_Zattiero
+givenName: Patching
+mail: Patching_Zattiero@example.com
+carLicense: OGE6POK
+departmentNumber: 1747
+employeeType: Contract
+homePhone: +1 818 645-7367
+initials: P. Z.
+mobile: +1 415 684-6807
+pager: +1 408 693-8834
+manager: cn=Lynnet Smerek
+secretary: cn=Cathe Toyooka
+roomNumber: 8368
+
+dn: cn=Gretna Mansell, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gretna Mansell
+sn: Mansell
+description: This is Gretna Mansell's description
+facsimileTelephoneNumber: +1 206 470-3366
+l: Redmond
+ou: Management
+postalAddress: example$Management$Dept # 100
+telephoneNumber: +1 206 974-8773
+title: Associate Management Engineer
+userPassword: llesnaMant
+uid: Gretna_Mansell
+givenName: Gretna
+mail: Gretna_Mansell@example.com
+carLicense: LI6HVH5
+departmentNumber: 1320
+employeeType: Employee
+homePhone: +1 818 259-9827
+initials: G. M.
+mobile: +1 303 958-2838
+pager: +1 818 383-2711
+manager: cn=Jeannot Banville
+secretary: cn=Fast Homa
+roomNumber: 84
+
+dn: cn=Grady Webster, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Grady Webster
+sn: Webster
+description: This is Grady Webster's description
+facsimileTelephoneNumber: +1 804 390-7916
+l: Redmond
+ou: Administrative
+postalAddress: example$Administrative$Dept # 817
+telephoneNumber: +1 510 842-2039
+title: Junior Administrative Mascot
+userPassword: retsbeWyda
+uid: Grady_Webster
+givenName: Grady
+mail: Grady_Webster@example.com
+carLicense: DLG73CO
+departmentNumber: 5407
+employeeType: Contract
+homePhone: +1 408 506-9869
+initials: G. W.
+mobile: +1 804 739-2305
+pager: +1 804 961-7792
+manager: cn=Sami Hovinga
+secretary: cn=Chok Handschy
+roomNumber: 6308
+
+dn: cn=Codie Measures, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Codie Measures
+sn: Measures
+description: This is Codie Measures's description
+facsimileTelephoneNumber: +1 206 615-7054
+l: Armonk
+ou: Administrative
+postalAddress: example$Administrative$Dept # 28
+telephoneNumber: +1 818 361-2279
+title: Supreme Administrative Sales Rep
+userPassword: serusaeMei
+uid: Codie_Measures
+givenName: Codie
+mail: Codie_Measures@example.com
+carLicense: MPDFFCN
+departmentNumber: 2622
+employeeType: Normal
+homePhone: +1 818 910-8000
+initials: C. M.
+mobile: +1 818 431-4254
+pager: +1 71 472-2635
+manager: cn=Rosabel Devine
+secretary: cn=Wanda Langett
+roomNumber: 9489
+
+dn: cn=Fekri Chanonat, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Fekri Chanonat
+sn: Chanonat
+description: This is Fekri Chanonat's description
+facsimileTelephoneNumber: +1 818 418-6751
+l: Emeryville
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 139
+telephoneNumber: +1 206 857-9837
+title: Elite Product Testing Czar
+userPassword: tanonahCir
+uid: Fekri_Chanonat
+givenName: Fekri
+mail: Fekri_Chanonat@example.com
+carLicense: YFDP9YR
+departmentNumber: 7476
+employeeType: Normal
+homePhone: +1 71 388-6010
+initials: F. C.
+mobile: +1 206 149-2290
+pager: +1 71 405-8307
+manager: cn=Dania Eastus
+secretary: cn=Merle Rhew
+roomNumber: 4801
+
+dn: cn=Neysa Chawla, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Neysa Chawla
+sn: Chawla
+description: This is Neysa Chawla's description
+facsimileTelephoneNumber: +1 213 434-3842
+l: Redwood Shores
+ou: Product Development
+postalAddress: example$Product Development$Dept # 687
+telephoneNumber: +1 408 707-1883
+title: Master Product Development Evangelist
+userPassword: alwahCasye
+uid: Neysa_Chawla
+givenName: Neysa
+mail: Neysa_Chawla@example.com
+carLicense: OJM0TQ2
+departmentNumber: 9768
+employeeType: Temp
+homePhone: +1 303 148-6728
+initials: N. C.
+mobile: +1 804 405-3870
+pager: +1 804 196-4354
+manager: cn=Melford Circe
+secretary: cn=Fidelia Mollerus
+roomNumber: 5400
+
+dn: cn=Stacey Kendi, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Stacey Kendi
+sn: Kendi
+description: This is Stacey Kendi's description
+facsimileTelephoneNumber: +1 818 539-7566
+l: Alameda
+ou: Administrative
+postalAddress: example$Administrative$Dept # 751
+telephoneNumber: +1 213 137-8584
+title: Master Administrative Stooge
+userPassword: idneKyecat
+uid: Stacey_Kendi
+givenName: Stacey
+mail: Stacey_Kendi@example.com
+carLicense: 65O13C4
+departmentNumber: 4015
+employeeType: Normal
+homePhone: +1 408 340-6594
+initials: S. K.
+mobile: +1 510 810-5629
+pager: +1 408 320-2418
+manager: cn=Gwyn Daoud
+secretary: cn=Jillene Wendling
+roomNumber: 6886
+
+dn: cn=Jeroen Administration, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jeroen Administration
+sn: Administration
+description: This is Jeroen Administration's description
+facsimileTelephoneNumber: +1 408 924-2365
+l: Alameda
+ou: Payroll
+postalAddress: example$Payroll$Dept # 453
+telephoneNumber: +1 71 296-1085
+title: Senior Payroll Punk
+userPassword: noitartsin
+uid: Jeroen_Administration
+givenName: Jeroen
+mail: Jeroen_Administration@example.com
+carLicense: N9SRZ2O
+departmentNumber: 3186
+employeeType: Manager
+homePhone: +1 510 531-8751
+initials: J. A.
+mobile: +1 804 413-9269
+pager: +1 804 713-2431
+manager: cn=Kathe Plmcoop
+secretary: cn=Annabell Chapman
+roomNumber: 733
+
+dn: cn=Ryoung Pusztai, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ryoung Pusztai
+sn: Pusztai
+description: This is Ryoung Pusztai's description
+facsimileTelephoneNumber: +1 213 494-3334
+l: Menlo Park
+ou: Management
+postalAddress: example$Management$Dept # 390
+telephoneNumber: +1 818 549-1453
+title: Master Management Engineer
+userPassword: iatzsuPgnu
+uid: Ryoung_Pusztai
+givenName: Ryoung
+mail: Ryoung_Pusztai@example.com
+carLicense: 6HGRJKO
+departmentNumber: 332
+employeeType: Manager
+homePhone: +1 206 555-6309
+initials: R. P.
+mobile: +1 415 376-2267
+pager: +1 206 151-6274
+manager: cn=Florine Mucklow
+secretary: cn=Clarke Sy
+roomNumber: 8171
+
+dn: cn=Laine Niergarth, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Laine Niergarth
+sn: Niergarth
+description: This is Laine Niergarth's description
+facsimileTelephoneNumber: +1 510 149-5644
+l: Orem
+ou: Management
+postalAddress: example$Management$Dept # 438
+telephoneNumber: +1 408 559-7902
+title: Supreme Management Writer
+userPassword: htragreiNe
+uid: Laine_Niergarth
+givenName: Laine
+mail: Laine_Niergarth@example.com
+carLicense: 7OO6DYL
+departmentNumber: 8203
+employeeType: Employee
+homePhone: +1 415 923-1180
+initials: L. N.
+mobile: +1 818 505-1606
+pager: +1 804 506-4204
+manager: cn=Vinod Sotelo
+secretary: cn=Leese Gahr
+roomNumber: 636
+
+dn: cn=Marit Pizzimenti, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marit Pizzimenti
+sn: Pizzimenti
+description: This is Marit Pizzimenti's description
+facsimileTelephoneNumber: +1 804 381-5191
+l: Santa Clara
+ou: Accounting
+postalAddress: example$Accounting$Dept # 357
+telephoneNumber: +1 206 187-1217
+title: Junior Accounting Consultant
+userPassword: itnemizziP
+uid: Marit_Pizzimenti
+givenName: Marit
+mail: Marit_Pizzimenti@example.com
+carLicense: 7JX2WPV
+departmentNumber: 8785
+employeeType: Temp
+homePhone: +1 71 982-4609
+initials: M. P.
+mobile: +1 415 993-7109
+pager: +1 415 904-3987
+manager: cn=Eric Madl
+secretary: cn=Barton Hirshman
+roomNumber: 2991
+
+dn: cn=Mirabelle Okada, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Mirabelle Okada
+sn: Okada
+description: This is Mirabelle Okada's description
+facsimileTelephoneNumber: +1 510 943-1518
+l: Cupertino
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 3
+telephoneNumber: +1 71 344-7022
+title: Junior Human Resources Accountant
+userPassword: adakOelleb
+uid: Mirabelle_Okada
+givenName: Mirabelle
+mail: Mirabelle_Okada@example.com
+carLicense: 572GPA5
+departmentNumber: 1003
+employeeType: Contract
+homePhone: +1 71 785-3573
+initials: M. O.
+mobile: +1 415 803-4450
+pager: +1 303 543-3523
+manager: cn=Cherilyn Gowan
+secretary: cn=Robinet Biedermann
+roomNumber: 3408
+
+dn: cn=Randie Harman, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Randie Harman
+sn: Harman
+description: This is Randie Harman's description
+facsimileTelephoneNumber: +1 818 909-7619
+l: Palo Alto
+ou: Product Development
+postalAddress: example$Product Development$Dept # 167
+telephoneNumber: +1 71 560-8030
+title: Chief Product Development Grunt
+userPassword: namraHeidn
+uid: Randie_Harman
+givenName: Randie
+mail: Randie_Harman@example.com
+carLicense: O0PYC9D
+departmentNumber: 2640
+employeeType: Manager
+homePhone: +1 206 958-7794
+initials: R. H.
+mobile: +1 818 839-9054
+pager: +1 213 616-5727
+manager: cn=Vinod Wooley
+secretary: cn=Byron Sugarbroad
+roomNumber: 7615
+
+dn: cn=Pinder Mahon, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Pinder Mahon
+sn: Mahon
+description: This is Pinder Mahon's description
+facsimileTelephoneNumber: +1 71 544-3325
+l: Sunnyvale
+ou: Payroll
+postalAddress: example$Payroll$Dept # 761
+telephoneNumber: +1 206 571-9497
+title: Senior Payroll Pinhead
+userPassword: nohaMredni
+uid: Pinder_Mahon
+givenName: Pinder
+mail: Pinder_Mahon@example.com
+carLicense: APWDYIZ
+departmentNumber: 2301
+employeeType: Contract
+homePhone: +1 510 577-5126
+initials: P. M.
+mobile: +1 213 345-1103
+pager: +1 303 766-9029
+manager: cn=Codee Ibach
+secretary: cn=Siva Chiabaut
+roomNumber: 348
+
+dn: cn=Ryman Bernier, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ryman Bernier
+sn: Bernier
+description: This is Ryman Bernier's description
+facsimileTelephoneNumber: +1 71 719-1927
+l: Cupertino
+ou: Management
+postalAddress: example$Management$Dept # 242
+telephoneNumber: +1 818 860-5074
+title: Elite Management Manager
+userPassword: reinreBnam
+uid: Ryman_Bernier
+givenName: Ryman
+mail: Ryman_Bernier@example.com
+carLicense: G4YGLY5
+departmentNumber: 7920
+employeeType: Manager
+homePhone: +1 71 342-5109
+initials: R. B.
+mobile: +1 213 207-7528
+pager: +1 213 739-4730
+manager: cn=Layne Baines
+secretary: cn=Margarethe Calis
+roomNumber: 353
+
+dn: cn=France Renaud, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: France Renaud
+sn: Renaud
+description: This is France Renaud's description
+facsimileTelephoneNumber: +1 213 531-4309
+l: Cambridge
+ou: Planning
+postalAddress: example$Planning$Dept # 743
+telephoneNumber: +1 804 484-7194
+title: Elite Planning Admin
+userPassword: duaneRecna
+uid: France_Renaud
+givenName: France
+mail: France_Renaud@example.com
+carLicense: AKDBJLU
+departmentNumber: 7453
+employeeType: Temp
+homePhone: +1 818 619-1611
+initials: F. R.
+mobile: +1 71 440-9894
+pager: +1 408 610-7007
+manager: cn=Anky Beshai
+secretary: cn=Lucille Mcgehee
+roomNumber: 2514
+
+dn: cn=Hilliary Stough, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Hilliary Stough
+sn: Stough
+description: This is Hilliary Stough's description
+facsimileTelephoneNumber: +1 510 795-3980
+l: Redwood Shores
+ou: Management
+postalAddress: example$Management$Dept # 590
+telephoneNumber: +1 818 481-8140
+title: Senior Management President
+userPassword: hguotSyrai
+uid: Hilliary_Stough
+givenName: Hilliary
+mail: Hilliary_Stough@example.com
+carLicense: 9LIY8JA
+departmentNumber: 3431
+employeeType: Temp
+homePhone: +1 818 716-6533
+initials: H. S.
+mobile: +1 213 247-4780
+pager: +1 206 653-7020
+manager: cn=Mitch Sldisk
+secretary: cn=Melamie Lemaire
+roomNumber: 1679
+
+dn: cn=Ardra Whaley, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ardra Whaley
+sn: Whaley
+description: This is Ardra Whaley's description
+facsimileTelephoneNumber: +1 408 653-9993
+l: San Jose
+ou: Accounting
+postalAddress: example$Accounting$Dept # 38
+telephoneNumber: +1 818 565-9770
+title: Elite Accounting Figurehead
+userPassword: yelahWardr
+uid: Ardra_Whaley
+givenName: Ardra
+mail: Ardra_Whaley@example.com
+carLicense: 7W4STEZ
+departmentNumber: 3225
+employeeType: Employee
+homePhone: +1 818 226-5315
+initials: A. W.
+mobile: +1 71 592-5625
+pager: +1 303 420-3521
+manager: cn=Una Boulay
+secretary: cn=Carlee Bento
+roomNumber: 4072
+
+dn: cn=Tobe Vachon, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tobe Vachon
+sn: Vachon
+description: This is Tobe Vachon's description
+facsimileTelephoneNumber: +1 206 238-2362
+l: San Jose
+ou: Peons
+postalAddress: example$Peons$Dept # 136
+telephoneNumber: +1 804 613-2148
+title: Master Peons Assistant
+userPassword: nohcaVeboT
+uid: Tobe_Vachon
+givenName: Tobe
+mail: Tobe_Vachon@example.com
+carLicense: 7IRTVU6
+departmentNumber: 6022
+employeeType: Normal
+homePhone: +1 206 685-5685
+initials: T. V.
+mobile: +1 408 207-4050
+pager: +1 213 858-5396
+manager: cn=Sally Levasseur
+secretary: cn=Abbe Shiu
+roomNumber: 8565
+
+dn: cn=Weiping Baldridge, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Weiping Baldridge
+sn: Baldridge
+description: This is Weiping Baldridge's description
+facsimileTelephoneNumber: +1 213 826-7039
+l: Cupertino
+ou: Administrative
+postalAddress: example$Administrative$Dept # 207
+telephoneNumber: +1 206 872-7097
+title: Elite Administrative Manager
+userPassword: egdirdlaBg
+uid: Weiping_Baldridge
+givenName: Weiping
+mail: Weiping_Baldridge@example.com
+carLicense: MYBLA6E
+departmentNumber: 7088
+employeeType: Temp
+homePhone: +1 303 421-8110
+initials: W. B.
+mobile: +1 71 859-5629
+pager: +1 415 810-1490
+manager: cn=Brenna Ostapiw
+secretary: cn=Christian Cauthen
+roomNumber: 9714
+
+dn: cn=Joann Akbas, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Joann Akbas
+sn: Akbas
+description: This is Joann Akbas's description
+facsimileTelephoneNumber: +1 818 754-8031
+l: Cambridge
+ou: Product Development
+postalAddress: example$Product Development$Dept # 966
+telephoneNumber: +1 71 938-4824
+title: Chief Product Development Visionary
+userPassword: sabkAnnaoJ
+uid: Joann_Akbas
+givenName: Joann
+mail: Joann_Akbas@example.com
+carLicense: RQ68PLE
+departmentNumber: 2316
+employeeType: Temp
+homePhone: +1 303 127-1322
+initials: J. A.
+mobile: +1 303 478-1252
+pager: +1 510 600-2815
+manager: cn=Florencia Recycling
+secretary: cn=Sibeal Esler
+roomNumber: 133
+
+dn: cn=Franc Stansby, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Franc Stansby
+sn: Stansby
+description: This is Franc Stansby's description
+facsimileTelephoneNumber: +1 206 871-1717
+l: Orem
+ou: Administrative
+postalAddress: example$Administrative$Dept # 18
+telephoneNumber: +1 303 982-9680
+title: Junior Administrative Sales Rep
+userPassword: ybsnatScna
+uid: Franc_Stansby
+givenName: Franc
+mail: Franc_Stansby@example.com
+carLicense: TY61BMA
+departmentNumber: 8591
+employeeType: Employee
+homePhone: +1 510 351-4722
+initials: F. S.
+mobile: +1 408 471-3809
+pager: +1 415 220-4627
+manager: cn=Frantisek Druzeta
+secretary: cn=Kanu Communication
+roomNumber: 6274
+
+dn: cn=Gwynith Mealin, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gwynith Mealin
+sn: Mealin
+description: This is Gwynith Mealin's description
+facsimileTelephoneNumber: +1 213 285-9267
+l: Fremont
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 724
+telephoneNumber: +1 818 147-6410
+title: Chief Human Resources Pinhead
+userPassword: nilaeMhtin
+uid: Gwynith_Mealin
+givenName: Gwynith
+mail: Gwynith_Mealin@example.com
+carLicense: HEJU11H
+departmentNumber: 8464
+employeeType: Normal
+homePhone: +1 818 358-6634
+initials: G. M.
+mobile: +1 303 544-9949
+pager: +1 510 417-9283
+manager: cn=Wrennie Aryavong
+secretary: cn=Giang Mansourati
+roomNumber: 75
+
+dn: cn=Demetri Behler, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Demetri Behler
+sn: Behler
+description: This is Demetri Behler's description
+facsimileTelephoneNumber: +1 206 707-7983
+l: Sunnyvale
+ou: Planning
+postalAddress: example$Planning$Dept # 371
+telephoneNumber: +1 408 727-3643
+title: Senior Planning Pinhead
+userPassword: relheBirte
+uid: Demetri_Behler
+givenName: Demetri
+mail: Demetri_Behler@example.com
+carLicense: EL27ZTJ
+departmentNumber: 7720
+employeeType: Employee
+homePhone: +1 415 403-5249
+initials: D. B.
+mobile: +1 303 297-5258
+pager: +1 303 156-9503
+manager: cn=Wallace Piel
+secretary: cn=Doralynn Lebo
+roomNumber: 4863
+
+dn: cn=Saibal Naimpally, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Saibal Naimpally
+sn: Naimpally
+description: This is Saibal Naimpally's description
+facsimileTelephoneNumber: +1 303 399-4748
+l: Emeryville
+ou: Peons
+postalAddress: example$Peons$Dept # 945
+telephoneNumber: +1 206 326-1975
+title: Chief Peons Mascot
+userPassword: yllapmiaNl
+uid: Saibal_Naimpally
+givenName: Saibal
+mail: Saibal_Naimpally@example.com
+carLicense: 1IF8IE8
+departmentNumber: 4588
+employeeType: Normal
+homePhone: +1 71 675-2309
+initials: S. N.
+mobile: +1 804 155-1073
+pager: +1 818 691-9926
+manager: cn=Cristy Puelma
+secretary: cn=Farrah Shane
+roomNumber: 8812
+
+dn: cn=Valenka Caceres, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Valenka Caceres
+sn: Caceres
+description: This is Valenka Caceres's description
+facsimileTelephoneNumber: +1 206 491-1130
+l: Orem
+ou: Product Development
+postalAddress: example$Product Development$Dept # 639
+telephoneNumber: +1 510 541-1059
+title: Senior Product Development Dictator
+userPassword: serecaCakn
+uid: Valenka_Caceres
+givenName: Valenka
+mail: Valenka_Caceres@example.com
+carLicense: 7AHNYVT
+departmentNumber: 5986
+employeeType: Temp
+homePhone: +1 213 520-3759
+initials: V. C.
+mobile: +1 510 691-3715
+pager: +1 510 797-6306
+manager: cn=Lidio Channa
+secretary: cn=Caresa Cote
+roomNumber: 8463
+
+dn: cn=Vasu Watchmaker, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Vasu Watchmaker
+sn: Watchmaker
+description: This is Vasu Watchmaker's description
+facsimileTelephoneNumber: +1 804 819-2355
+l: Cupertino
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 802
+telephoneNumber: +1 71 392-7758
+title: Master Product Testing Accountant
+userPassword: rekamhctaW
+uid: Vasu_Watchmaker
+givenName: Vasu
+mail: Vasu_Watchmaker@example.com
+carLicense: 4MFLKI3
+departmentNumber: 9331
+employeeType: Manager
+homePhone: +1 206 400-5524
+initials: V. W.
+mobile: +1 408 546-6814
+pager: +1 818 176-8576
+manager: cn=Sibyl Coste
+secretary: cn=Mickey Ange
+roomNumber: 8931
+
+dn: cn=Damien Lauruhn, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Damien Lauruhn
+sn: Lauruhn
+description: This is Damien Lauruhn's description
+facsimileTelephoneNumber: +1 303 516-7904
+l: San Francisco
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 793
+telephoneNumber: +1 213 391-1756
+title: Chief Product Testing Writer
+userPassword: nhuruaLnei
+uid: Damien_Lauruhn
+givenName: Damien
+mail: Damien_Lauruhn@example.com
+carLicense: UFTSP3E
+departmentNumber: 6053
+employeeType: Manager
+homePhone: +1 213 814-5375
+initials: D. L.
+mobile: +1 71 942-4781
+pager: +1 510 909-5076
+manager: cn=Marylinda Mabuchi
+secretary: cn=Cherilynn Bcs
+roomNumber: 9558
+
+dn: cn=Udaya Sprules, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Udaya Sprules
+sn: Sprules
+description: This is Udaya Sprules's description
+facsimileTelephoneNumber: +1 818 323-6035
+l: Redmond
+ou: Product Development
+postalAddress: example$Product Development$Dept # 786
+telephoneNumber: +1 213 139-8619
+title: Supreme Product Development Manager
+userPassword: selurpSaya
+uid: Udaya_Sprules
+givenName: Udaya
+mail: Udaya_Sprules@example.com
+carLicense: JADBISJ
+departmentNumber: 988
+employeeType: Contract
+homePhone: +1 804 534-8042
+initials: U. S.
+mobile: +1 206 357-1336
+pager: +1 206 295-7277
+manager: cn=Tak Omura
+secretary: cn=Lark Gattrell
+roomNumber: 4673
+
+dn: cn=Julietta Stasaski, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Julietta Stasaski
+sn: Stasaski
+description: This is Julietta Stasaski's description
+facsimileTelephoneNumber: +1 408 519-1830
+l: Fremont
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 642
+telephoneNumber: +1 804 162-3064
+title: Junior Janitorial Developer
+userPassword: iksasatSat
+uid: Julietta_Stasaski
+givenName: Julietta
+mail: Julietta_Stasaski@example.com
+carLicense: EV5GC6N
+departmentNumber: 8746
+employeeType: Contract
+homePhone: +1 206 536-5375
+initials: J. S.
+mobile: +1 213 692-7723
+pager: +1 408 250-9081
+manager: cn=Jurek Rhattigan
+secretary: cn=Sharona Harris
+roomNumber: 1904
+
+dn: cn=Meghann Hashimoto, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Meghann Hashimoto
+sn: Hashimoto
+description: This is Meghann Hashimoto's description
+facsimileTelephoneNumber: +1 408 434-4275
+l: Milpitas
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 711
+telephoneNumber: +1 206 758-7305
+title: Master Human Resources President
+userPassword: otomihsaHn
+uid: Meghann_Hashimoto
+givenName: Meghann
+mail: Meghann_Hashimoto@example.com
+carLicense: 0UFVWSC
+departmentNumber: 3874
+employeeType: Employee
+homePhone: +1 206 678-6708
+initials: M. H.
+mobile: +1 303 871-3337
+pager: +1 213 932-9972
+manager: cn=Kartik Puddington
+secretary: cn=Daune Updt
+roomNumber: 3942
+
+dn: cn=Augustina Evraire, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Augustina Evraire
+sn: Evraire
+description: This is Augustina Evraire's description
+facsimileTelephoneNumber: +1 213 421-1985
+l: Orem
+ou: Administrative
+postalAddress: example$Administrative$Dept # 21
+telephoneNumber: +1 303 452-1137
+title: Supreme Administrative Director
+userPassword: eriarvEani
+uid: Augustina_Evraire
+givenName: Augustina
+mail: Augustina_Evraire@example.com
+carLicense: MBANHC1
+departmentNumber: 6016
+employeeType: Manager
+homePhone: +1 71 807-6498
+initials: A. E.
+mobile: +1 303 484-4308
+pager: +1 804 515-2585
+manager: cn=Marcille Roussier
+secretary: cn=Melinie Liskoff
+roomNumber: 8639
+
+dn: cn=Adorne Nguyen, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Adorne Nguyen
+sn: Nguyen
+description: This is Adorne Nguyen's description
+facsimileTelephoneNumber: +1 804 485-4709
+l: Orem
+ou: Payroll
+postalAddress: example$Payroll$Dept # 467
+telephoneNumber: +1 415 325-3305
+title: Chief Payroll Figurehead
+userPassword: neyugNenro
+uid: Adorne_Nguyen
+givenName: Adorne
+mail: Adorne_Nguyen@example.com
+carLicense: L9J6Z6W
+departmentNumber: 582
+employeeType: Contract
+homePhone: +1 213 536-2390
+initials: A. N.
+mobile: +1 510 374-4770
+pager: +1 408 508-4686
+manager: cn=Koray Tooyserkani
+secretary: cn=Ermo Holvey
+roomNumber: 3259
+
+dn: cn=Manami SonHing, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Manami SonHing
+sn: SonHing
+description: This is Manami SonHing's description
+facsimileTelephoneNumber: +1 303 715-6668
+l: Cambridge
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 790
+telephoneNumber: +1 408 490-3409
+title: Senior Janitorial Dictator
+userPassword: gniHnoSima
+uid: Manami_SonHing
+givenName: Manami
+mail: Manami_SonHing@example.com
+carLicense: COQE7XX
+departmentNumber: 8655
+employeeType: Normal
+homePhone: +1 303 507-6643
+initials: M. S.
+mobile: +1 71 672-6745
+pager: +1 804 185-9199
+manager: cn=Cheng Kilner
+secretary: cn=Marissa Traylor
+roomNumber: 4389
+
+dn: cn=Ree Cuffling, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ree Cuffling
+sn: Cuffling
+description: This is Ree Cuffling's description
+facsimileTelephoneNumber: +1 206 323-8567
+l: Redwood Shores
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 543
+telephoneNumber: +1 213 434-1937
+title: Supreme Janitorial Dictator
+userPassword: gnilffuCee
+uid: Ree_Cuffling
+givenName: Ree
+mail: Ree_Cuffling@example.com
+carLicense: 1RI83W8
+departmentNumber: 1414
+employeeType: Employee
+homePhone: +1 510 282-5019
+initials: R. C.
+mobile: +1 206 323-1264
+pager: +1 303 134-7068
+manager: cn=Asnat Falaki
+secretary: cn=Alexander Miranda
+roomNumber: 5983
+
+dn: cn=Maurene Paylor, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Maurene Paylor
+sn: Paylor
+description: This is Maurene Paylor's description
+facsimileTelephoneNumber: +1 415 120-9000
+l: Palo Alto
+ou: Payroll
+postalAddress: example$Payroll$Dept # 856
+telephoneNumber: +1 71 382-1296
+title: Chief Payroll Sales Rep
+userPassword: rolyaPener
+uid: Maurene_Paylor
+givenName: Maurene
+mail: Maurene_Paylor@example.com
+carLicense: 6RZ9N9S
+departmentNumber: 2005
+employeeType: Employee
+homePhone: +1 303 438-8363
+initials: M. P.
+mobile: +1 415 691-7892
+pager: +1 510 366-9452
+manager: cn=Henk Lindt
+secretary: cn=Smita Franco
+roomNumber: 571
+
+dn: cn=Wilmette Vidmer, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Wilmette Vidmer
+sn: Vidmer
+description: This is Wilmette Vidmer's description
+facsimileTelephoneNumber: +1 415 934-2352
+l: Sunnyvale
+ou: Management
+postalAddress: example$Management$Dept # 835
+telephoneNumber: +1 510 702-2339
+title: Chief Management Assistant
+userPassword: remdiVette
+uid: Wilmette_Vidmer
+givenName: Wilmette
+mail: Wilmette_Vidmer@example.com
+carLicense: SGXD3ZW
+departmentNumber: 179
+employeeType: Manager
+homePhone: +1 804 859-9312
+initials: W. V.
+mobile: +1 818 588-7656
+pager: +1 804 551-3717
+manager: cn=Marvette St.Denis
+secretary: cn=Guy Lind
+roomNumber: 5743
+
+dn: cn=Jessalyn Schuster, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jessalyn Schuster
+sn: Schuster
+description: This is Jessalyn Schuster's description
+facsimileTelephoneNumber: +1 206 957-7521
+l: Cupertino
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 73
+telephoneNumber: +1 415 681-3218
+title: Supreme Product Testing Admin
+userPassword: retsuhcSny
+uid: Jessalyn_Schuster
+givenName: Jessalyn
+mail: Jessalyn_Schuster@example.com
+carLicense: KEPK1IR
+departmentNumber: 5271
+employeeType: Employee
+homePhone: +1 408 485-6916
+initials: J. S.
+mobile: +1 804 348-4734
+pager: +1 415 356-6767
+manager: cn=Ofella Fitzpatrick
+secretary: cn=Patching Rambo
+roomNumber: 9469
+
+dn: cn=Willetta Cucchiaro, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Willetta Cucchiaro
+sn: Cucchiaro
+description: This is Willetta Cucchiaro's description
+facsimileTelephoneNumber: +1 804 780-7795
+l: San Jose
+ou: Product Development
+postalAddress: example$Product Development$Dept # 53
+telephoneNumber: +1 303 573-5639
+title: Senior Product Development Stooge
+userPassword: oraihccuCa
+uid: Willetta_Cucchiaro
+givenName: Willetta
+mail: Willetta_Cucchiaro@example.com
+carLicense: QGGN9EW
+departmentNumber: 9637
+employeeType: Contract
+homePhone: +1 408 210-6365
+initials: W. C.
+mobile: +1 206 290-6619
+pager: +1 415 267-9465
+manager: cn=Pamelina Schyving
+secretary: cn=Pauline Bowser
+roomNumber: 4092
+
+dn: cn=Usa Gell, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Usa Gell
+sn: Gell
+description: This is Usa Gell's description
+facsimileTelephoneNumber: +1 213 789-8081
+l: Redmond
+ou: Peons
+postalAddress: example$Peons$Dept # 562
+telephoneNumber: +1 510 487-5445
+title: Associate Peons Visionary
+userPassword: lleGasU
+uid: Usa_Gell
+givenName: Usa
+mail: Usa_Gell@example.com
+carLicense: K2KTGE7
+departmentNumber: 4216
+employeeType: Normal
+homePhone: +1 510 808-8963
+initials: U. G.
+mobile: +1 408 439-1199
+pager: +1 408 508-7793
+manager: cn=Sukey Cicci
+secretary: cn=Nara Planas
+roomNumber: 2652
+
+dn: cn=Benne Carli, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Benne Carli
+sn: Carli
+description: This is Benne Carli's description
+facsimileTelephoneNumber: +1 303 473-5030
+l: Mountain View
+ou: Peons
+postalAddress: example$Peons$Dept # 535
+telephoneNumber: +1 818 671-2060
+title: Associate Peons Director
+userPassword: ilraCenneB
+uid: Benne_Carli
+givenName: Benne
+mail: Benne_Carli@example.com
+carLicense: NG7GZK1
+departmentNumber: 8726
+employeeType: Contract
+homePhone: +1 303 685-4953
+initials: B. C.
+mobile: +1 408 293-1367
+pager: +1 206 390-3291
+manager: cn=Gilda Booker
+secretary: cn=Karlene Savarimuthu
+roomNumber: 7758
+
+dn: cn=Ike Doublesin, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ike Doublesin
+sn: Doublesin
+description: This is Ike Doublesin's description
+facsimileTelephoneNumber: +1 213 378-8634
+l: Emeryville
+ou: Peons
+postalAddress: example$Peons$Dept # 706
+telephoneNumber: +1 818 189-8843
+title: Master Peons Grunt
+userPassword: niselbuoDe
+uid: Ike_Doublesin
+givenName: Ike
+mail: Ike_Doublesin@example.com
+carLicense: FV44VG8
+departmentNumber: 6560
+employeeType: Normal
+homePhone: +1 818 773-4158
+initials: I. D.
+mobile: +1 71 416-7937
+pager: +1 804 356-6695
+manager: cn=Dinesh Krause
+secretary: cn=Michaela Plourde
+roomNumber: 1621
+
+dn: cn=Orlando Curtin, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Orlando Curtin
+sn: Curtin
+description: This is Orlando Curtin's description
+facsimileTelephoneNumber: +1 408 911-4577
+l: Redwood Shores
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 776
+telephoneNumber: +1 206 245-1538
+title: Senior Janitorial Vice President
+userPassword: nitruCodna
+uid: Orlando_Curtin
+givenName: Orlando
+mail: Orlando_Curtin@example.com
+carLicense: UY88XNS
+departmentNumber: 6202
+employeeType: Employee
+homePhone: +1 510 574-8432
+initials: O. C.
+mobile: +1 415 775-3773
+pager: +1 408 739-9073
+manager: cn=Mariele Bookings
+secretary: cn=Matti Henthorne
+roomNumber: 67
+
+dn: cn=Ignatius Tesh, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ignatius Tesh
+sn: Tesh
+description: This is Ignatius Tesh's description
+facsimileTelephoneNumber: +1 206 664-8920
+l: San Mateo
+ou: Product Development
+postalAddress: example$Product Development$Dept # 633
+telephoneNumber: +1 71 609-8325
+title: Senior Product Development Consultant
+userPassword: hseTsuitan
+uid: Ignatius_Tesh
+givenName: Ignatius
+mail: Ignatius_Tesh@example.com
+carLicense: APF5G3S
+departmentNumber: 8252
+employeeType: Contract
+homePhone: +1 818 529-4841
+initials: I. T.
+mobile: +1 303 545-1219
+pager: +1 408 305-8619
+manager: cn=Bram Salehi
+secretary: cn=Jean-Paul Courville
+roomNumber: 1986
+
+dn: cn=Giralda Fucito, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Giralda Fucito
+sn: Fucito
+description: This is Giralda Fucito's description
+facsimileTelephoneNumber: +1 303 981-2313
+l: Redwood Shores
+ou: Planning
+postalAddress: example$Planning$Dept # 931
+telephoneNumber: +1 804 654-1897
+title: Chief Planning Admin
+userPassword: oticuFadla
+uid: Giralda_Fucito
+givenName: Giralda
+mail: Giralda_Fucito@example.com
+carLicense: DB2GI65
+departmentNumber: 130
+employeeType: Temp
+homePhone: +1 415 904-4017
+initials: G. F.
+mobile: +1 415 329-1880
+pager: +1 415 920-9291
+manager: cn=Codee Montoya
+secretary: cn=Darell Salehi
+roomNumber: 7189
+
+dn: cn=Mariejeanne Karia, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Mariejeanne Karia
+sn: Karia
+description: This is Mariejeanne Karia's description
+facsimileTelephoneNumber: +1 804 761-2143
+l: San Jose
+ou: Product Development
+postalAddress: example$Product Development$Dept # 298
+telephoneNumber: +1 71 397-7501
+title: Master Product Development Czar
+userPassword: airaKennae
+uid: Mariejeanne_Karia
+givenName: Mariejeanne
+mail: Mariejeanne_Karia@example.com
+carLicense: AHMSX2M
+departmentNumber: 527
+employeeType: Manager
+homePhone: +1 213 845-9536
+initials: M. K.
+mobile: +1 408 469-7882
+pager: +1 303 204-8795
+manager: cn=Deirdre Abel
+secretary: cn=Adaline Donkers
+roomNumber: 5758
+
+dn: cn=Johanne Jammu, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Johanne Jammu
+sn: Jammu
+description: This is Johanne Jammu's description
+facsimileTelephoneNumber: +1 408 963-9729
+l: Redmond
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 306
+telephoneNumber: +1 71 194-2145
+title: Master Janitorial Warrior
+userPassword: ummaJennah
+uid: Johanne_Jammu
+givenName: Johanne
+mail: Johanne_Jammu@example.com
+carLicense: LEHOHNL
+departmentNumber: 8623
+employeeType: Contract
+homePhone: +1 303 283-4228
+initials: J. J.
+mobile: +1 415 405-3473
+pager: +1 71 355-2574
+manager: cn=Buffy McGill
+secretary: cn=Adriena Kwant
+roomNumber: 6253
+
+dn: cn=Rosemary Prunier, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rosemary Prunier
+sn: Prunier
+description: This is Rosemary Prunier's description
+facsimileTelephoneNumber: +1 415 456-8748
+l: Redwood Shores
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 182
+telephoneNumber: +1 415 283-2606
+title: Chief Janitorial Czar
+userPassword: reinurPyra
+uid: Rosemary_Prunier
+givenName: Rosemary
+mail: Rosemary_Prunier@example.com
+carLicense: V71L9G0
+departmentNumber: 6194
+employeeType: Temp
+homePhone: +1 510 635-4995
+initials: R. P.
+mobile: +1 213 429-6764
+pager: +1 71 302-1672
+manager: cn=Jasmina Barr
+secretary: cn=Bebe Vawter
+roomNumber: 8151
+
+dn: cn=Gustie Carr, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gustie Carr
+sn: Carr
+description: This is Gustie Carr's description
+facsimileTelephoneNumber: +1 213 392-3596
+l: San Mateo
+ou: Payroll
+postalAddress: example$Payroll$Dept # 730
+telephoneNumber: +1 206 202-4408
+title: Associate Payroll Punk
+userPassword: rraCeitsuG
+uid: Gustie_Carr
+givenName: Gustie
+mail: Gustie_Carr@example.com
+carLicense: TJCWFFR
+departmentNumber: 2924
+employeeType: Temp
+homePhone: +1 818 507-2904
+initials: G. C.
+mobile: +1 213 159-9090
+pager: +1 415 651-2876
+manager: cn=Zonda Schrang
+secretary: cn=Hedwig Maracle
+roomNumber: 3116
+
+dn: cn=Zulema Michailov, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Zulema Michailov
+sn: Michailov
+description: This is Zulema Michailov's description
+facsimileTelephoneNumber: +1 408 452-6849
+l: San Mateo
+ou: Administrative
+postalAddress: example$Administrative$Dept # 1
+telephoneNumber: +1 818 653-4055
+title: Elite Administrative Admin
+userPassword: voliahciMa
+uid: Zulema_Michailov
+givenName: Zulema
+mail: Zulema_Michailov@example.com
+carLicense: NDBVBLP
+departmentNumber: 2524
+employeeType: Employee
+homePhone: +1 510 670-6873
+initials: Z. M.
+mobile: +1 804 222-9552
+pager: +1 206 275-5874
+manager: cn=Ernesta Ste-Marie
+secretary: cn=Beverie Brissette
+roomNumber: 4544
+
+dn: cn=Karee Patry, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Karee Patry
+sn: Patry
+description: This is Karee Patry's description
+facsimileTelephoneNumber: +1 303 814-9240
+l: Cambridge
+ou: Administrative
+postalAddress: example$Administrative$Dept # 199
+telephoneNumber: +1 408 977-3750
+title: Senior Administrative Visionary
+userPassword: yrtaPeeraK
+uid: Karee_Patry
+givenName: Karee
+mail: Karee_Patry@example.com
+carLicense: LP9CTZC
+departmentNumber: 8345
+employeeType: Contract
+homePhone: +1 510 402-3673
+initials: K. P.
+mobile: +1 818 575-1415
+pager: +1 408 117-4516
+manager: cn=Sondra Vajentic
+secretary: cn=Jenelle Whitlock
+roomNumber: 6189
+
+dn: cn=Follick Favreau, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Follick Favreau
+sn: Favreau
+description: This is Follick Favreau's description
+facsimileTelephoneNumber: +1 213 100-7942
+l: Sunnyvale
+ou: Management
+postalAddress: example$Management$Dept # 579
+telephoneNumber: +1 415 998-6895
+title: Senior Management Engineer
+userPassword: uaervaFkci
+uid: Follick_Favreau
+givenName: Follick
+mail: Follick_Favreau@example.com
+carLicense: K0EE7GN
+departmentNumber: 1329
+employeeType: Temp
+homePhone: +1 510 803-9770
+initials: F. F.
+mobile: +1 408 431-6943
+pager: +1 804 995-6485
+manager: cn=Pratibha Sandness
+secretary: cn=Arabela Tarasewicz
+roomNumber: 8504
+
+dn: cn=Amrish McClintock, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Amrish McClintock
+sn: McClintock
+description: This is Amrish McClintock's description
+facsimileTelephoneNumber: +1 510 952-3166
+l: Sunnyvale
+ou: Peons
+postalAddress: example$Peons$Dept # 535
+telephoneNumber: +1 71 695-1035
+title: Chief Peons Pinhead
+userPassword: kcotnilCcM
+uid: Amrish_McClintock
+givenName: Amrish
+mail: Amrish_McClintock@example.com
+carLicense: TFHS4FO
+departmentNumber: 234
+employeeType: Normal
+homePhone: +1 303 246-6567
+initials: A. M.
+mobile: +1 415 144-9286
+pager: +1 804 926-6727
+manager: cn=Larissa Nabors
+secretary: cn=Angeles Horianopoulos
+roomNumber: 3099
+
+dn: cn=Diego Kluke, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Diego Kluke
+sn: Kluke
+description: This is Diego Kluke's description
+facsimileTelephoneNumber: +1 206 586-6864
+l: Cupertino
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 20
+telephoneNumber: +1 818 836-2797
+title: Supreme Product Testing Czar
+userPassword: ekulKogeiD
+uid: Diego_Kluke
+givenName: Diego
+mail: Diego_Kluke@example.com
+carLicense: SP138NT
+departmentNumber: 7942
+employeeType: Employee
+homePhone: +1 510 601-4047
+initials: D. K.
+mobile: +1 415 525-7727
+pager: +1 206 521-4177
+manager: cn=Loralyn Rosche
+secretary: cn=Edi Scapin
+roomNumber: 1925
+
+dn: cn=Soyong Weinkauf, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Soyong Weinkauf
+sn: Weinkauf
+description: This is Soyong Weinkauf's description
+facsimileTelephoneNumber: +1 71 357-3311
+l: Alameda
+ou: Management
+postalAddress: example$Management$Dept # 483
+telephoneNumber: +1 71 536-3463
+title: Elite Management Sales Rep
+userPassword: fuaknieWgn
+uid: Soyong_Weinkauf
+givenName: Soyong
+mail: Soyong_Weinkauf@example.com
+carLicense: KNZKJ7M
+departmentNumber: 9953
+employeeType: Employee
+homePhone: +1 415 252-9968
+initials: S. W.
+mobile: +1 818 902-3454
+pager: +1 415 119-8006
+manager: cn=Ermo Libov
+secretary: cn=Merunix Curmon
+roomNumber: 2825
+
+dn: cn=Demetre Bajpeyi, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Demetre Bajpeyi
+sn: Bajpeyi
+description: This is Demetre Bajpeyi's description
+facsimileTelephoneNumber: +1 71 320-8594
+l: Armonk
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 402
+telephoneNumber: +1 213 852-6001
+title: Senior Janitorial Fellow
+userPassword: iyepjaBert
+uid: Demetre_Bajpeyi
+givenName: Demetre
+mail: Demetre_Bajpeyi@example.com
+carLicense: 24RPQC7
+departmentNumber: 3714
+employeeType: Temp
+homePhone: +1 303 814-9549
+initials: D. B.
+mobile: +1 303 639-2352
+pager: +1 510 926-2787
+manager: cn=Siu-Man Sawchuk
+secretary: cn=Nikolia Mihara
+roomNumber: 2065
+
+dn: cn=Mathilde Maguire, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Mathilde Maguire
+sn: Maguire
+description: This is Mathilde Maguire's description
+facsimileTelephoneNumber: +1 213 446-2001
+l: Emeryville
+ou: Peons
+postalAddress: example$Peons$Dept # 50
+telephoneNumber: +1 71 378-1781
+title: Elite Peons Developer
+userPassword: eriugaMedl
+uid: Mathilde_Maguire
+givenName: Mathilde
+mail: Mathilde_Maguire@example.com
+carLicense: 1SL2FYB
+departmentNumber: 8475
+employeeType: Employee
+homePhone: +1 804 593-1563
+initials: M. M.
+mobile: +1 71 457-3623
+pager: +1 206 603-5930
+manager: cn=Christyna Toplis
+secretary: cn=Doc Gillon
+roomNumber: 985
+
+dn: cn=Karlon Ludwick, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Karlon Ludwick
+sn: Ludwick
+description: This is Karlon Ludwick's description
+facsimileTelephoneNumber: +1 303 861-9193
+l: Armonk
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 291
+telephoneNumber: +1 818 528-2579
+title: Chief Janitorial Warrior
+userPassword: kciwduLnol
+uid: Karlon_Ludwick
+givenName: Karlon
+mail: Karlon_Ludwick@example.com
+carLicense: FAEIUBW
+departmentNumber: 4948
+employeeType: Employee
+homePhone: +1 206 917-3938
+initials: K. L.
+mobile: +1 818 195-7948
+pager: +1 818 322-4006
+manager: cn=Logntp Bonneville
+secretary: cn=Flossy Stds
+roomNumber: 6342
+
+dn: cn=Onette Kolenda, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Onette Kolenda
+sn: Kolenda
+description: This is Onette Kolenda's description
+facsimileTelephoneNumber: +1 818 722-9149
+l: San Mateo
+ou: Accounting
+postalAddress: example$Accounting$Dept # 231
+telephoneNumber: +1 303 262-8838
+title: Associate Accounting Technician
+userPassword: adneloKett
+uid: Onette_Kolenda
+givenName: Onette
+mail: Onette_Kolenda@example.com
+carLicense: K37F6F8
+departmentNumber: 1461
+employeeType: Employee
+homePhone: +1 71 969-8949
+initials: O. K.
+mobile: +1 303 580-8045
+pager: +1 71 995-7476
+manager: cn=Janifer Leiker
+secretary: cn=Trudey Klasky
+roomNumber: 3977
+
+dn: cn=Gregg Likert, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gregg Likert
+sn: Likert
+description: This is Gregg Likert's description
+facsimileTelephoneNumber: +1 71 440-5890
+l: San Jose
+ou: Accounting
+postalAddress: example$Accounting$Dept # 315
+telephoneNumber: +1 510 254-4513
+title: Master Accounting Evangelist
+userPassword: trekiLgger
+uid: Gregg_Likert
+givenName: Gregg
+mail: Gregg_Likert@example.com
+carLicense: I575QHB
+departmentNumber: 8770
+employeeType: Manager
+homePhone: +1 818 919-8598
+initials: G. L.
+mobile: +1 408 138-7196
+pager: +1 206 642-8807
+manager: cn=Lucina Hyjek
+secretary: cn=Mauro Tischler
+roomNumber: 2908
+
+dn: cn=Maye Kolb, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Maye Kolb
+sn: Kolb
+description: This is Maye Kolb's description
+facsimileTelephoneNumber: +1 206 233-2759
+l: Santa Clara
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 145
+telephoneNumber: +1 510 465-9537
+title: Junior Human Resources Janitor
+userPassword: bloKeyaM
+uid: Maye_Kolb
+givenName: Maye
+mail: Maye_Kolb@example.com
+carLicense: PUQ2P5H
+departmentNumber: 8212
+employeeType: Employee
+homePhone: +1 510 392-3267
+initials: M. K.
+mobile: +1 206 195-4931
+pager: +1 213 337-6735
+manager: cn=Alida Sunderland
+secretary: cn=Sheela Bolgos
+roomNumber: 2443
+
+dn: cn=Chelsie Gabbai, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Chelsie Gabbai
+sn: Gabbai
+description: This is Chelsie Gabbai's description
+facsimileTelephoneNumber: +1 206 378-2872
+l: Emeryville
+ou: Peons
+postalAddress: example$Peons$Dept # 701
+telephoneNumber: +1 408 604-6116
+title: Elite Peons Janitor
+userPassword: iabbaGeisl
+uid: Chelsie_Gabbai
+givenName: Chelsie
+mail: Chelsie_Gabbai@example.com
+carLicense: 7MWMK7L
+departmentNumber: 6975
+employeeType: Manager
+homePhone: +1 213 757-6017
+initials: C. G.
+mobile: +1 804 458-4095
+pager: +1 804 733-8875
+manager: cn=Tammara Ledu
+secretary: cn=Lynde Quelch
+roomNumber: 2041
+
+dn: cn=Rosemarie Zarate, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rosemarie Zarate
+sn: Zarate
+description: This is Rosemarie Zarate's description
+facsimileTelephoneNumber: +1 71 351-4764
+l: Fremont
+ou: Payroll
+postalAddress: example$Payroll$Dept # 174
+telephoneNumber: +1 818 310-1578
+title: Senior Payroll Grunt
+userPassword: etaraZeira
+uid: Rosemarie_Zarate
+givenName: Rosemarie
+mail: Rosemarie_Zarate@example.com
+carLicense: WJ3IMXU
+departmentNumber: 6610
+employeeType: Temp
+homePhone: +1 408 168-7149
+initials: R. Z.
+mobile: +1 303 865-3015
+pager: +1 71 987-4480
+manager: cn=Raychel Lassiter
+secretary: cn=Collete Anker
+roomNumber: 9719
+
+dn: cn=Sacto McGilly, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sacto McGilly
+sn: McGilly
+description: This is Sacto McGilly's description
+facsimileTelephoneNumber: +1 510 487-4649
+l: Redwood Shores
+ou: Administrative
+postalAddress: example$Administrative$Dept # 960
+telephoneNumber: +1 818 863-3301
+title: Elite Administrative Pinhead
+userPassword: ylliGcMotc
+uid: Sacto_McGilly
+givenName: Sacto
+mail: Sacto_McGilly@example.com
+carLicense: GP46RFT
+departmentNumber: 6087
+employeeType: Manager
+homePhone: +1 510 642-2917
+initials: S. M.
+mobile: +1 818 137-5043
+pager: +1 408 524-4610
+manager: cn=Mico Degenova
+secretary: cn=Ri Sides
+roomNumber: 9001
+
+dn: cn=Sybille Beaucaire, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sybille Beaucaire
+sn: Beaucaire
+description: This is Sybille Beaucaire's description
+facsimileTelephoneNumber: +1 303 458-6926
+l: Cupertino
+ou: Administrative
+postalAddress: example$Administrative$Dept # 505
+telephoneNumber: +1 206 139-6275
+title: Chief Administrative Technician
+userPassword: eriacuaeBe
+uid: Sybille_Beaucaire
+givenName: Sybille
+mail: Sybille_Beaucaire@example.com
+carLicense: EBS2ZVQ
+departmentNumber: 5628
+employeeType: Employee
+homePhone: +1 818 327-3720
+initials: S. B.
+mobile: +1 415 717-3718
+pager: +1 206 109-7889
+manager: cn=Dorelia Abrahim
+secretary: cn=Melinda Nagy
+roomNumber: 382
+
+dn: cn=Melvin Instal, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Melvin Instal
+sn: Instal
+description: This is Melvin Instal's description
+facsimileTelephoneNumber: +1 303 567-2740
+l: Palo Alto
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 674
+telephoneNumber: +1 71 925-3834
+title: Chief Janitorial Janitor
+userPassword: latsnInivl
+uid: Melvin_Instal
+givenName: Melvin
+mail: Melvin_Instal@example.com
+carLicense: 3UM0HAK
+departmentNumber: 9649
+employeeType: Manager
+homePhone: +1 804 262-1158
+initials: M. I.
+mobile: +1 415 429-9014
+pager: +1 206 544-1033
+manager: cn=Mike Larkin
+secretary: cn=Radoslav McLenaghan
+roomNumber: 1928
+
+dn: cn=Winne Clayton, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Winne Clayton
+sn: Clayton
+description: This is Winne Clayton's description
+facsimileTelephoneNumber: +1 510 176-2473
+l: Alameda
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 817
+telephoneNumber: +1 818 436-1270
+title: Junior Janitorial Developer
+userPassword: notyalCenn
+uid: Winne_Clayton
+givenName: Winne
+mail: Winne_Clayton@example.com
+carLicense: MX6QWM2
+departmentNumber: 5119
+employeeType: Contract
+homePhone: +1 408 529-5795
+initials: W. C.
+mobile: +1 408 820-2248
+pager: +1 303 868-3000
+manager: cn=Mary-Ellen Fastpack
+secretary: cn=Marcela Wetherbee
+roomNumber: 1395
+
+dn: cn=Claire Prevatt, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Claire Prevatt
+sn: Prevatt
+description: This is Claire Prevatt's description
+facsimileTelephoneNumber: +1 213 841-7855
+l: Cambridge
+ou: Product Development
+postalAddress: example$Product Development$Dept # 451
+telephoneNumber: +1 213 221-1032
+title: Chief Product Development Developer
+userPassword: ttaverPeri
+uid: Claire_Prevatt
+givenName: Claire
+mail: Claire_Prevatt@example.com
+carLicense: VGBHLEP
+departmentNumber: 635
+employeeType: Employee
+homePhone: +1 804 474-7424
+initials: C. P.
+mobile: +1 206 485-6464
+pager: +1 213 812-5024
+manager: cn=Briney Honkakangas
+secretary: cn=Lotta Sotelo
+roomNumber: 7435
+
+dn: cn=Harvey Bycenko, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Harvey Bycenko
+sn: Bycenko
+description: This is Harvey Bycenko's description
+facsimileTelephoneNumber: +1 206 998-2600
+l: Cupertino
+ou: Management
+postalAddress: example$Management$Dept # 82
+telephoneNumber: +1 206 100-4821
+title: Master Management Writer
+userPassword: oknecyByev
+uid: Harvey_Bycenko
+givenName: Harvey
+mail: Harvey_Bycenko@example.com
+carLicense: 11NK484
+departmentNumber: 8041
+employeeType: Temp
+homePhone: +1 71 506-2067
+initials: H. B.
+mobile: +1 510 577-7693
+pager: +1 408 209-5780
+manager: cn=Patch Keseris
+secretary: cn=Silvestro Raynor
+roomNumber: 7479
+
+dn: cn=Gihan Gendron, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gihan Gendron
+sn: Gendron
+description: This is Gihan Gendron's description
+facsimileTelephoneNumber: +1 818 526-5445
+l: Emeryville
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 479
+telephoneNumber: +1 213 841-5077
+title: Chief Product Testing Pinhead
+userPassword: nordneGnah
+uid: Gihan_Gendron
+givenName: Gihan
+mail: Gihan_Gendron@example.com
+carLicense: 8CBKWNX
+departmentNumber: 1825
+employeeType: Normal
+homePhone: +1 206 253-7786
+initials: G. G.
+mobile: +1 804 812-5201
+pager: +1 510 535-5711
+manager: cn=Tonye Terzian
+secretary: cn=Wendeline Loadbuild
+roomNumber: 404
+
+dn: cn=Harm Ruban, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Harm Ruban
+sn: Ruban
+description: This is Harm Ruban's description
+facsimileTelephoneNumber: +1 818 231-4324
+l: San Jose
+ou: Product Development
+postalAddress: example$Product Development$Dept # 36
+telephoneNumber: +1 206 587-1627
+title: Junior Product Development Pinhead
+userPassword: nabuRmraH
+uid: Harm_Ruban
+givenName: Harm
+mail: Harm_Ruban@example.com
+carLicense: LA1TY56
+departmentNumber: 1814
+employeeType: Employee
+homePhone: +1 415 659-9442
+initials: H. R.
+mobile: +1 71 432-8390
+pager: +1 804 613-5224
+manager: cn=Cathyleen Beznowski
+secretary: cn=Nicholle Jeanes
+roomNumber: 6940
+
+dn: cn=Maryse Nagarur, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Maryse Nagarur
+sn: Nagarur
+description: This is Maryse Nagarur's description
+facsimileTelephoneNumber: +1 213 272-9533
+l: Redmond
+ou: Management
+postalAddress: example$Management$Dept # 576
+telephoneNumber: +1 408 263-1293
+title: Senior Management Grunt
+userPassword: ruragaNesy
+uid: Maryse_Nagarur
+givenName: Maryse
+mail: Maryse_Nagarur@example.com
+carLicense: P5TEMJC
+departmentNumber: 3563
+employeeType: Temp
+homePhone: +1 818 604-7330
+initials: M. N.
+mobile: +1 213 234-1237
+pager: +1 415 854-6281
+manager: cn=Carlen Dost
+secretary: cn=Arabella Snider
+roomNumber: 1980
+
+dn: cn=Javed Swann, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Javed Swann
+sn: Swann
+description: This is Javed Swann's description
+facsimileTelephoneNumber: +1 510 104-7877
+l: Redwood Shores
+ou: Planning
+postalAddress: example$Planning$Dept # 74
+telephoneNumber: +1 804 563-8433
+title: Junior Planning Figurehead
+userPassword: nnawSdevaJ
+uid: Javed_Swann
+givenName: Javed
+mail: Javed_Swann@example.com
+carLicense: GVQ3AFB
+departmentNumber: 1888
+employeeType: Manager
+homePhone: +1 818 771-7074
+initials: J. S.
+mobile: +1 71 475-2671
+pager: +1 303 136-6099
+manager: cn=Rungroj Riley
+secretary: cn=Jerald Reznechek
+roomNumber: 7966
+
+dn: cn=Ruchi Ficken, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ruchi Ficken
+sn: Ficken
+description: This is Ruchi Ficken's description
+facsimileTelephoneNumber: +1 804 689-4774
+l: Orem
+ou: Accounting
+postalAddress: example$Accounting$Dept # 90
+telephoneNumber: +1 804 692-5041
+title: Junior Accounting Writer
+userPassword: nekciFihcu
+uid: Ruchi_Ficken
+givenName: Ruchi
+mail: Ruchi_Ficken@example.com
+carLicense: C38WSX3
+departmentNumber: 5446
+employeeType: Employee
+homePhone: +1 206 557-3940
+initials: R. F.
+mobile: +1 408 171-1977
+pager: +1 206 141-1921
+manager: cn=Yoko Nichols
+secretary: cn=Harmi Reinboth
+roomNumber: 9620
+
+dn: cn=Yousef Simcox, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Yousef Simcox
+sn: Simcox
+description: This is Yousef Simcox's description
+facsimileTelephoneNumber: +1 213 130-1178
+l: Redwood Shores
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 611
+telephoneNumber: +1 303 449-6208
+title: Junior Janitorial Punk
+userPassword: xocmiSfesu
+uid: Yousef_Simcox
+givenName: Yousef
+mail: Yousef_Simcox@example.com
+carLicense: YJWIKVR
+departmentNumber: 590
+employeeType: Manager
+homePhone: +1 206 950-1477
+initials: Y. S.
+mobile: +1 206 523-8158
+pager: +1 510 949-5277
+manager: cn=Domenick Kay
+secretary: cn=Yolanthe Sells
+roomNumber: 2200
+
+dn: cn=Gwenda Risdal, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gwenda Risdal
+sn: Risdal
+description: This is Gwenda Risdal's description
+facsimileTelephoneNumber: +1 510 914-1810
+l: San Francisco
+ou: Payroll
+postalAddress: example$Payroll$Dept # 288
+telephoneNumber: +1 408 669-1410
+title: Associate Payroll Warrior
+userPassword: ladsiRadne
+uid: Gwenda_Risdal
+givenName: Gwenda
+mail: Gwenda_Risdal@example.com
+carLicense: WMKFV52
+departmentNumber: 4777
+employeeType: Employee
+homePhone: +1 804 236-1042
+initials: G. R.
+mobile: +1 415 766-7366
+pager: +1 206 491-2947
+manager: cn=Yatish Cregan
+secretary: cn=Du-Tuan Nash
+roomNumber: 3893
+
+dn: cn=Giselle Gorberg, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Giselle Gorberg
+sn: Gorberg
+description: This is Giselle Gorberg's description
+facsimileTelephoneNumber: +1 804 291-5293
+l: Orem
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 635
+telephoneNumber: +1 408 782-3902
+title: Elite Human Resources Accountant
+userPassword: grebroGell
+uid: Giselle_Gorberg
+givenName: Giselle
+mail: Giselle_Gorberg@example.com
+carLicense: EKN00S9
+departmentNumber: 6640
+employeeType: Manager
+homePhone: +1 408 243-4677
+initials: G. G.
+mobile: +1 213 196-2187
+pager: +1 213 229-1238
+manager: cn=Fei-Yin Jolicoeur
+secretary: cn=Cthrine DMS
+roomNumber: 5898
+
+dn: cn=Faizal Guthrie, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Faizal Guthrie
+sn: Guthrie
+description: This is Faizal Guthrie's description
+facsimileTelephoneNumber: +1 818 647-1904
+l: Orem
+ou: Administrative
+postalAddress: example$Administrative$Dept # 962
+telephoneNumber: +1 71 212-5263
+title: Master Administrative Madonna
+userPassword: eirhtuGlaz
+uid: Faizal_Guthrie
+givenName: Faizal
+mail: Faizal_Guthrie@example.com
+carLicense: GILI2G6
+departmentNumber: 3890
+employeeType: Employee
+homePhone: +1 818 319-1349
+initials: F. G.
+mobile: +1 818 935-3145
+pager: +1 818 898-9167
+manager: cn=Thia Glancey
+secretary: cn=Ora Zahn
+roomNumber: 7586
+
+dn: cn=Dannie Cholet, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dannie Cholet
+sn: Cholet
+description: This is Dannie Cholet's description
+facsimileTelephoneNumber: +1 510 141-1644
+l: Sunnyvale
+ou: Management
+postalAddress: example$Management$Dept # 674
+telephoneNumber: +1 213 254-8079
+title: Associate Management Accountant
+userPassword: telohCeinn
+uid: Dannie_Cholet
+givenName: Dannie
+mail: Dannie_Cholet@example.com
+carLicense: AEP8BN8
+departmentNumber: 4132
+employeeType: Manager
+homePhone: +1 71 879-3210
+initials: D. C.
+mobile: +1 818 991-3354
+pager: +1 206 865-9846
+manager: cn=Layla Kirouac
+secretary: cn=Xuong Giuntini
+roomNumber: 2927
+
+dn: cn=Joanna Vandervelde, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Joanna Vandervelde
+sn: Vandervelde
+description: This is Joanna Vandervelde's description
+facsimileTelephoneNumber: +1 415 327-1503
+l: San Mateo
+ou: Accounting
+postalAddress: example$Accounting$Dept # 587
+telephoneNumber: +1 71 636-6576
+title: Master Accounting Accountant
+userPassword: edlevredna
+uid: Joanna_Vandervelde
+givenName: Joanna
+mail: Joanna_Vandervelde@example.com
+carLicense: C7ZA600
+departmentNumber: 4480
+employeeType: Manager
+homePhone: +1 71 628-9440
+initials: J. V.
+mobile: +1 71 527-9619
+pager: +1 415 613-6242
+manager: cn=Cookie Vetil
+secretary: cn=Olia Vettese
+roomNumber: 1935
+
+dn: cn=Hanco Knowles, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Hanco Knowles
+sn: Knowles
+description: This is Hanco Knowles's description
+facsimileTelephoneNumber: +1 213 844-3315
+l: Milpitas
+ou: Peons
+postalAddress: example$Peons$Dept # 52
+telephoneNumber: +1 71 398-1392
+title: Supreme Peons Vice President
+userPassword: selwonKocn
+uid: Hanco_Knowles
+givenName: Hanco
+mail: Hanco_Knowles@example.com
+carLicense: TKIR393
+departmentNumber: 3066
+employeeType: Contract
+homePhone: +1 408 226-5036
+initials: H. K.
+mobile: +1 818 561-6679
+pager: +1 818 480-3156
+manager: cn=Mehdi Barel
+secretary: cn=Becki Eisnor
+roomNumber: 2059
+
+dn: cn=Ranee Boswick, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ranee Boswick
+sn: Boswick
+description: This is Ranee Boswick's description
+facsimileTelephoneNumber: +1 213 754-4670
+l: Mountain View
+ou: Product Development
+postalAddress: example$Product Development$Dept # 544
+telephoneNumber: +1 408 123-3015
+title: Junior Product Development Developer
+userPassword: kciwsoBeen
+uid: Ranee_Boswick
+givenName: Ranee
+mail: Ranee_Boswick@example.com
+carLicense: M30KC7O
+departmentNumber: 3497
+employeeType: Employee
+homePhone: +1 804 494-8119
+initials: R. B.
+mobile: +1 804 480-8588
+pager: +1 804 898-8895
+manager: cn=Carline Arnold
+secretary: cn=Abu Van Eyk
+roomNumber: 1572
+
+dn: cn=Harrison Locken, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Harrison Locken
+sn: Locken
+description: This is Harrison Locken's description
+facsimileTelephoneNumber: +1 804 911-9610
+l: Alameda
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 537
+telephoneNumber: +1 804 265-5384
+title: Chief Janitorial Yahoo
+userPassword: nekcoLnosi
+uid: Harrison_Locken
+givenName: Harrison
+mail: Harrison_Locken@example.com
+carLicense: 1O43SOX
+departmentNumber: 5333
+employeeType: Normal
+homePhone: +1 415 667-4762
+initials: H. L.
+mobile: +1 71 276-4165
+pager: +1 206 618-2207
+manager: cn=Pryor Bouroncle
+secretary: cn=Rilla Van Kessel
+roomNumber: 7155
+
+dn: cn=Petre Albers, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Petre Albers
+sn: Albers
+description: This is Petre Albers's description
+facsimileTelephoneNumber: +1 71 779-7085
+l: Redmond
+ou: Payroll
+postalAddress: example$Payroll$Dept # 388
+telephoneNumber: +1 818 731-9347
+title: Junior Payroll Dictator
+userPassword: sreblAerte
+uid: Petre_Albers
+givenName: Petre
+mail: Petre_Albers@example.com
+carLicense: 7KXGT6Y
+departmentNumber: 5870
+employeeType: Manager
+homePhone: +1 510 118-2864
+initials: P. A.
+mobile: +1 71 595-7803
+pager: +1 408 388-8342
+manager: cn=Leo Capindale
+secretary: cn=Jon Bhatti
+roomNumber: 2522
+
+dn: cn=Chantalle Paetsch, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Chantalle Paetsch
+sn: Paetsch
+description: This is Chantalle Paetsch's description
+facsimileTelephoneNumber: +1 206 893-6338
+l: Mountain View
+ou: Administrative
+postalAddress: example$Administrative$Dept # 593
+telephoneNumber: +1 818 668-3736
+title: Chief Administrative Dictator
+userPassword: hcsteaPell
+uid: Chantalle_Paetsch
+givenName: Chantalle
+mail: Chantalle_Paetsch@example.com
+carLicense: ZRYK67G
+departmentNumber: 9292
+employeeType: Contract
+homePhone: +1 71 649-9067
+initials: C. P.
+mobile: +1 415 468-3564
+pager: +1 804 441-9916
+manager: cn=Bahadir Granger
+secretary: cn=Sheela Ratnayake
+roomNumber: 6721
+
+dn: cn=Ginette Paperno, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ginette Paperno
+sn: Paperno
+description: This is Ginette Paperno's description
+facsimileTelephoneNumber: +1 206 513-5433
+l: Sunnyvale
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 894
+telephoneNumber: +1 213 133-4449
+title: Supreme Product Testing Developer
+userPassword: onrepaPett
+uid: Ginette_Paperno
+givenName: Ginette
+mail: Ginette_Paperno@example.com
+carLicense: TJG4BB1
+departmentNumber: 7981
+employeeType: Employee
+homePhone: +1 510 176-9118
+initials: G. P.
+mobile: +1 213 683-6781
+pager: +1 804 917-3048
+manager: cn=Jacque Krieg
+secretary: cn=Renata Raxter
+roomNumber: 5244
+
+dn: cn=Raina Barker, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Raina Barker
+sn: Barker
+description: This is Raina Barker's description
+facsimileTelephoneNumber: +1 415 336-8569
+l: Cupertino
+ou: Administrative
+postalAddress: example$Administrative$Dept # 877
+telephoneNumber: +1 408 352-5301
+title: Senior Administrative Manager
+userPassword: rekraBania
+uid: Raina_Barker
+givenName: Raina
+mail: Raina_Barker@example.com
+carLicense: IUX1LID
+departmentNumber: 1351
+employeeType: Temp
+homePhone: +1 804 858-1479
+initials: R. B.
+mobile: +1 818 308-5863
+pager: +1 213 420-4581
+manager: cn=Karita Misczak
+secretary: cn=Tyler Poma
+roomNumber: 2632
+
+dn: cn=Tresrch Marzella, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tresrch Marzella
+sn: Marzella
+description: This is Tresrch Marzella's description
+facsimileTelephoneNumber: +1 213 620-8363
+l: Sunnyvale
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 457
+telephoneNumber: +1 415 511-5141
+title: Supreme Human Resources Vice President
+userPassword: allezraMhc
+uid: Tresrch_Marzella
+givenName: Tresrch
+mail: Tresrch_Marzella@example.com
+carLicense: DK1YY7W
+departmentNumber: 6168
+employeeType: Temp
+homePhone: +1 804 468-7675
+initials: T. M.
+mobile: +1 206 232-3501
+pager: +1 804 810-3755
+manager: cn=Tomi Twolan
+secretary: cn=Ernie Pasvar
+roomNumber: 5947
+
+dn: cn=Henrie Bonnar, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Henrie Bonnar
+sn: Bonnar
+description: This is Henrie Bonnar's description
+facsimileTelephoneNumber: +1 415 523-3146
+l: Orem
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 758
+telephoneNumber: +1 206 495-8414
+title: Senior Human Resources Janitor
+userPassword: rannoBeirn
+uid: Henrie_Bonnar
+givenName: Henrie
+mail: Henrie_Bonnar@example.com
+carLicense: 4GZJV62
+departmentNumber: 3090
+employeeType: Employee
+homePhone: +1 206 490-8915
+initials: H. B.
+mobile: +1 408 224-9073
+pager: +1 213 989-4621
+manager: cn=Anthiathia Zwierzchowski
+secretary: cn=Doc McNulty
+roomNumber: 419
+
+dn: cn=Kat Gaudet-Montsion, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kat Gaudet-Montsion
+sn: Gaudet-Montsion
+description: This is Kat Gaudet-Montsion's description
+facsimileTelephoneNumber: +1 510 698-3658
+l: Orem
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 220
+telephoneNumber: +1 408 507-3832
+title: Senior Product Testing Artist
+userPassword: noistnoM-t
+uid: Kat_Gaudet-Montsion
+givenName: Kat
+mail: Kat_Gaudet-Montsion@example.com
+carLicense: EGS97EN
+departmentNumber: 383
+employeeType: Manager
+homePhone: +1 213 457-7658
+initials: K. G.
+mobile: +1 303 461-9680
+pager: +1 415 147-3541
+manager: cn=Carmella Dacal
+secretary: cn=Julietta McNally
+roomNumber: 2958
+
+dn: cn=Pension Oshiro, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Pension Oshiro
+sn: Oshiro
+description: This is Pension Oshiro's description
+facsimileTelephoneNumber: +1 818 965-6467
+l: Redwood Shores
+ou: Planning
+postalAddress: example$Planning$Dept # 723
+telephoneNumber: +1 510 645-9344
+title: Master Planning Visionary
+userPassword: orihsOnois
+uid: Pension_Oshiro
+givenName: Pension
+mail: Pension_Oshiro@example.com
+carLicense: 74G2D2L
+departmentNumber: 6239
+employeeType: Temp
+homePhone: +1 804 119-5081
+initials: P. O.
+mobile: +1 71 923-1480
+pager: +1 804 744-2328
+manager: cn=Symen Atrc
+secretary: cn=Michaelina Stiglitz
+roomNumber: 8991
+
+dn: cn=Fraser Kaid, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Fraser Kaid
+sn: Kaid
+description: This is Fraser Kaid's description
+facsimileTelephoneNumber: +1 71 559-4722
+l: Cambridge
+ou: Product Development
+postalAddress: example$Product Development$Dept # 28
+telephoneNumber: +1 71 669-1797
+title: Senior Product Development Developer
+userPassword: diaKresarF
+uid: Fraser_Kaid
+givenName: Fraser
+mail: Fraser_Kaid@example.com
+carLicense: OOGFDHH
+departmentNumber: 8184
+employeeType: Employee
+homePhone: +1 804 182-1705
+initials: F. K.
+mobile: +1 71 729-1487
+pager: +1 804 368-3374
+manager: cn=Lujanka Bredeck
+secretary: cn=Nikoletta Pirkey
+roomNumber: 9618
+
+dn: cn=Tyke Kolb, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tyke Kolb
+sn: Kolb
+description: This is Tyke Kolb's description
+facsimileTelephoneNumber: +1 415 472-7326
+l: Menlo Park
+ou: Product Development
+postalAddress: example$Product Development$Dept # 144
+telephoneNumber: +1 71 610-5927
+title: Associate Product Development Dictator
+userPassword: bloKekyT
+uid: Tyke_Kolb
+givenName: Tyke
+mail: Tyke_Kolb@example.com
+carLicense: K9EJ2PH
+departmentNumber: 3833
+employeeType: Temp
+homePhone: +1 213 465-6338
+initials: T. K.
+mobile: +1 510 372-4172
+pager: +1 206 155-8964
+manager: cn=Lorrie Pegler
+secretary: cn=Ainsley Mocock
+roomNumber: 9951
+
+dn: cn=Samantha Funston, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Samantha Funston
+sn: Funston
+description: This is Samantha Funston's description
+facsimileTelephoneNumber: +1 818 408-8781
+l: Mountain View
+ou: Product Development
+postalAddress: example$Product Development$Dept # 704
+telephoneNumber: +1 510 441-1713
+title: Senior Product Development Architect
+userPassword: notsnuFaht
+uid: Samantha_Funston
+givenName: Samantha
+mail: Samantha_Funston@example.com
+carLicense: OGR5NC0
+departmentNumber: 2283
+employeeType: Normal
+homePhone: +1 71 257-5561
+initials: S. F.
+mobile: +1 818 203-5965
+pager: +1 213 394-3029
+manager: cn=Aloise Auth
+secretary: cn=Reggie Coupal
+roomNumber: 9673
+
+dn: cn=Bunni Rabatich, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Bunni Rabatich
+sn: Rabatich
+description: This is Bunni Rabatich's description
+facsimileTelephoneNumber: +1 213 317-1293
+l: Alameda
+ou: Planning
+postalAddress: example$Planning$Dept # 247
+telephoneNumber: +1 206 210-4486
+title: Associate Planning Grunt
+userPassword: hcitabaRin
+uid: Bunni_Rabatich
+givenName: Bunni
+mail: Bunni_Rabatich@example.com
+carLicense: F5X9Z13
+departmentNumber: 3091
+employeeType: Contract
+homePhone: +1 206 132-2276
+initials: B. R.
+mobile: +1 818 598-6564
+pager: +1 804 727-1911
+manager: cn=Clotilda US
+secretary: cn=Judy Dutil
+roomNumber: 9364
+
+dn: cn=Kristopher Gach, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kristopher Gach
+sn: Gach
+description: This is Kristopher Gach's description
+facsimileTelephoneNumber: +1 206 537-1265
+l: Armonk
+ou: Peons
+postalAddress: example$Peons$Dept # 168
+telephoneNumber: +1 71 891-3814
+title: Master Peons Writer
+userPassword: hcaGrehpot
+uid: Kristopher_Gach
+givenName: Kristopher
+mail: Kristopher_Gach@example.com
+carLicense: CZFQ8BU
+departmentNumber: 9591
+employeeType: Manager
+homePhone: +1 818 440-4469
+initials: K. G.
+mobile: +1 804 306-7534
+pager: +1 206 569-8283
+manager: cn=Cecilla Brockhouse
+secretary: cn=Giselle Mathew
+roomNumber: 4242
+
+dn: cn=Zyg Chawla, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Zyg Chawla
+sn: Chawla
+description: This is Zyg Chawla's description
+facsimileTelephoneNumber: +1 71 476-7592
+l: Palo Alto
+ou: Accounting
+postalAddress: example$Accounting$Dept # 634
+telephoneNumber: +1 71 917-5784
+title: Supreme Accounting Janitor
+userPassword: alwahCgyZ
+uid: Zyg_Chawla
+givenName: Zyg
+mail: Zyg_Chawla@example.com
+carLicense: JWVPXTE
+departmentNumber: 2557
+employeeType: Normal
+homePhone: +1 213 402-1477
+initials: Z. C.
+mobile: +1 206 900-8420
+pager: +1 408 871-2112
+manager: cn=Malethia Van Alphen
+secretary: cn=Tilda Jarmon
+roomNumber: 1768
+
+dn: cn=Peach Whited, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Peach Whited
+sn: Whited
+description: This is Peach Whited's description
+facsimileTelephoneNumber: +1 71 488-8463
+l: Fremont
+ou: Management
+postalAddress: example$Management$Dept # 453
+telephoneNumber: +1 206 407-4377
+title: Associate Management Director
+userPassword: detihWhcae
+uid: Peach_Whited
+givenName: Peach
+mail: Peach_Whited@example.com
+carLicense: D5T79NF
+departmentNumber: 4942
+employeeType: Employee
+homePhone: +1 818 529-1218
+initials: P. W.
+mobile: +1 408 603-1099
+pager: +1 804 273-8788
+manager: cn=Kora Gargul
+secretary: cn=Rosalinde Cotuna
+roomNumber: 9895
+
+dn: cn=Abigail Filer, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Abigail Filer
+sn: Filer
+description: This is Abigail Filer's description
+facsimileTelephoneNumber: +1 213 889-2130
+l: Emeryville
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 804
+telephoneNumber: +1 510 120-7068
+title: Master Product Testing Admin
+userPassword: reliFliagi
+uid: Abigail_Filer
+givenName: Abigail
+mail: Abigail_Filer@example.com
+carLicense: H8GGDRB
+departmentNumber: 1626
+employeeType: Manager
+homePhone: +1 206 776-9048
+initials: A. F.
+mobile: +1 804 106-4595
+pager: +1 71 606-4128
+manager: cn=Koral Javallas
+secretary: cn=Career Sauls
+roomNumber: 1114
+
+dn: cn=Goldia Beton, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Goldia Beton
+sn: Beton
+description: This is Goldia Beton's description
+facsimileTelephoneNumber: +1 206 199-1710
+l: San Francisco
+ou: Payroll
+postalAddress: example$Payroll$Dept # 521
+telephoneNumber: +1 71 379-1352
+title: Master Payroll Technician
+userPassword: noteBaidlo
+uid: Goldia_Beton
+givenName: Goldia
+mail: Goldia_Beton@example.com
+carLicense: 8EZLKDZ
+departmentNumber: 2103
+employeeType: Contract
+homePhone: +1 71 194-7458
+initials: G. B.
+mobile: +1 804 180-8843
+pager: +1 510 831-4931
+manager: cn=Maxie Kadlecik
+secretary: cn=Gateway Drewes
+roomNumber: 3898
+
+dn: cn=Reginald Nessman, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Reginald Nessman
+sn: Nessman
+description: This is Reginald Nessman's description
+facsimileTelephoneNumber: +1 804 429-5489
+l: Alameda
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 155
+telephoneNumber: +1 206 763-8825
+title: Junior Janitorial Sales Rep
+userPassword: namsseNdla
+uid: Reginald_Nessman
+givenName: Reginald
+mail: Reginald_Nessman@example.com
+carLicense: BDNVUZR
+departmentNumber: 3570
+employeeType: Contract
+homePhone: +1 408 412-7967
+initials: R. N.
+mobile: +1 303 833-9104
+pager: +1 408 356-6145
+manager: cn=Joey Hagenbuck
+secretary: cn=Tessa Friton
+roomNumber: 9969
+
+dn: cn=Astra Murris, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Astra Murris
+sn: Murris
+description: This is Astra Murris's description
+facsimileTelephoneNumber: +1 804 933-8836
+l: San Jose
+ou: Management
+postalAddress: example$Management$Dept # 914
+telephoneNumber: +1 71 788-5412
+title: Supreme Management Assistant
+userPassword: sirruMarts
+uid: Astra_Murris
+givenName: Astra
+mail: Astra_Murris@example.com
+carLicense: 2XIZ7KY
+departmentNumber: 2063
+employeeType: Normal
+homePhone: +1 206 383-8712
+initials: A. M.
+mobile: +1 415 508-2174
+pager: +1 510 963-1069
+manager: cn=Rea Streng
+secretary: cn=Bernhard Pham
+roomNumber: 4108
+
+dn: cn=Bobby Bomstein, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Bobby Bomstein
+sn: Bomstein
+description: This is Bobby Bomstein's description
+facsimileTelephoneNumber: +1 510 561-1880
+l: Milpitas
+ou: Peons
+postalAddress: example$Peons$Dept # 192
+telephoneNumber: +1 303 660-3962
+title: Senior Peons Visionary
+userPassword: nietsmoByb
+uid: Bobby_Bomstein
+givenName: Bobby
+mail: Bobby_Bomstein@example.com
+carLicense: TE5KGBZ
+departmentNumber: 2175
+employeeType: Manager
+homePhone: +1 415 765-4342
+initials: B. B.
+mobile: +1 818 728-5181
+pager: +1 510 176-2561
+manager: cn=Bernardo Worsley
+secretary: cn=Nonie Noronha
+roomNumber: 371
+
+dn: cn=Kelli Dacre, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kelli Dacre
+sn: Dacre
+description: This is Kelli Dacre's description
+facsimileTelephoneNumber: +1 510 235-3372
+l: Redwood Shores
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 478
+telephoneNumber: +1 303 331-7585
+title: Junior Janitorial Artist
+userPassword: ercaDilleK
+uid: Kelli_Dacre
+givenName: Kelli
+mail: Kelli_Dacre@example.com
+carLicense: OI9MRO9
+departmentNumber: 7747
+employeeType: Contract
+homePhone: +1 303 682-6371
+initials: K. D.
+mobile: +1 303 950-8800
+pager: +1 818 474-2753
+manager: cn=Sabina Traxler
+secretary: cn=Carol Wessels
+roomNumber: 5932
+
+dn: cn=Brittney Matton, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Brittney Matton
+sn: Matton
+description: This is Brittney Matton's description
+facsimileTelephoneNumber: +1 408 809-7442
+l: Sunnyvale
+ou: Peons
+postalAddress: example$Peons$Dept # 698
+telephoneNumber: +1 213 641-2094
+title: Associate Peons Director
+userPassword: nottaMyent
+uid: Brittney_Matton
+givenName: Brittney
+mail: Brittney_Matton@example.com
+carLicense: 9ZWN92D
+departmentNumber: 6035
+employeeType: Contract
+homePhone: +1 213 963-1877
+initials: B. M.
+mobile: +1 303 413-7502
+pager: +1 415 588-4764
+manager: cn=Cleve Brombal
+secretary: cn=Sibella Vanderwel
+roomNumber: 1059
+
+dn: cn=Konstance Vandervelde, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Konstance Vandervelde
+sn: Vandervelde
+description: This is Konstance Vandervelde's description
+facsimileTelephoneNumber: +1 71 999-5812
+l: Alameda
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 593
+telephoneNumber: +1 303 384-8754
+title: Associate Janitorial Warrior
+userPassword: edlevredna
+uid: Konstance_Vandervelde
+givenName: Konstance
+mail: Konstance_Vandervelde@example.com
+carLicense: CYZFEGE
+departmentNumber: 8861
+employeeType: Manager
+homePhone: +1 415 626-6615
+initials: K. V.
+mobile: +1 303 588-8016
+pager: +1 510 553-5762
+manager: cn=Fenelia Westcott
+secretary: cn=Amil Lukic
+roomNumber: 6124
+
+dn: cn=Elva Ciskowski, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Elva Ciskowski
+sn: Ciskowski
+description: This is Elva Ciskowski's description
+facsimileTelephoneNumber: +1 510 149-9386
+l: Orem
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 614
+telephoneNumber: +1 303 853-8966
+title: Master Human Resources Accountant
+userPassword: ikswoksiCa
+uid: Elva_Ciskowski
+givenName: Elva
+mail: Elva_Ciskowski@example.com
+carLicense: RDDAVGO
+departmentNumber: 1977
+employeeType: Contract
+homePhone: +1 213 452-8366
+initials: E. C.
+mobile: +1 415 835-6597
+pager: +1 213 997-4726
+manager: cn=Almeria Rowell
+secretary: cn=Bep Hasen
+roomNumber: 70
+
+dn: cn=Bertha Schrader, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Bertha Schrader
+sn: Schrader
+description: This is Bertha Schrader's description
+facsimileTelephoneNumber: +1 408 540-4086
+l: San Francisco
+ou: Management
+postalAddress: example$Management$Dept # 416
+telephoneNumber: +1 415 934-3510
+title: Master Management Artist
+userPassword: redarhcSah
+uid: Bertha_Schrader
+givenName: Bertha
+mail: Bertha_Schrader@example.com
+carLicense: ABLEY7X
+departmentNumber: 4403
+employeeType: Contract
+homePhone: +1 408 398-5632
+initials: B. S.
+mobile: +1 408 420-3289
+pager: +1 408 822-4828
+manager: cn=Hiroshi Forrest
+secretary: cn=Meridith Chaintreuil
+roomNumber: 1615
+
+dn: cn=Kim-Tram Kennaday, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kim-Tram Kennaday
+sn: Kennaday
+description: This is Kim-Tram Kennaday's description
+facsimileTelephoneNumber: +1 818 227-4636
+l: Santa Clara
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 826
+telephoneNumber: +1 415 680-2987
+title: Supreme Human Resources Developer
+userPassword: yadanneKma
+uid: Kim-Tram_Kennaday
+givenName: Kim-Tram
+mail: Kim-Tram_Kennaday@example.com
+carLicense: FBLSUNG
+departmentNumber: 8972
+employeeType: Normal
+homePhone: +1 408 162-4428
+initials: K. K.
+mobile: +1 510 443-7611
+pager: +1 303 585-5491
+manager: cn=Ron Risto
+secretary: cn=Shandee Reich
+roomNumber: 2683
+
+dn: cn=Isl Majmudar, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Isl Majmudar
+sn: Majmudar
+description: This is Isl Majmudar's description
+facsimileTelephoneNumber: +1 510 441-3073
+l: Cupertino
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 440
+telephoneNumber: +1 213 591-4036
+title: Supreme Human Resources Assistant
+userPassword: radumjaMls
+uid: Isl_Majmudar
+givenName: Isl
+mail: Isl_Majmudar@example.com
+carLicense: FQBB9VE
+departmentNumber: 7134
+employeeType: Normal
+homePhone: +1 206 965-8556
+initials: I. M.
+mobile: +1 510 334-6507
+pager: +1 415 344-7708
+manager: cn=Natalie Narayanan
+secretary: cn=Pepita Hoadley
+roomNumber: 9783
+
+dn: cn=Ragu Moyce, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ragu Moyce
+sn: Moyce
+description: This is Ragu Moyce's description
+facsimileTelephoneNumber: +1 71 533-1173
+l: Armonk
+ou: Planning
+postalAddress: example$Planning$Dept # 296
+telephoneNumber: +1 510 839-6879
+title: Senior Planning Dictator
+userPassword: ecyoMugaR
+uid: Ragu_Moyce
+givenName: Ragu
+mail: Ragu_Moyce@example.com
+carLicense: E6K0IUP
+departmentNumber: 4746
+employeeType: Normal
+homePhone: +1 818 354-8223
+initials: R. M.
+mobile: +1 408 463-9780
+pager: +1 303 743-5872
+manager: cn=Wing-Ki Cherrier
+secretary: cn=Lanita Amalu
+roomNumber: 6173
+
+dn: cn=Marie-ann Molochko, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marie-ann Molochko
+sn: Molochko
+description: This is Marie-ann Molochko's description
+facsimileTelephoneNumber: +1 415 520-8403
+l: Palo Alto
+ou: Management
+postalAddress: example$Management$Dept # 58
+telephoneNumber: +1 303 251-4803
+title: Chief Management Architect
+userPassword: okhcoloMnn
+uid: Marie-ann_Molochko
+givenName: Marie-ann
+mail: Marie-ann_Molochko@example.com
+carLicense: K3V0NES
+departmentNumber: 7819
+employeeType: Normal
+homePhone: +1 818 760-5020
+initials: M. M.
+mobile: +1 408 810-6275
+pager: +1 804 848-4694
+manager: cn=Daron Polashock
+secretary: cn=Alica Lamers
+roomNumber: 3504
+
+dn: cn=Babbie Van Sickle, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Babbie Van Sickle
+sn: Van Sickle
+description: This is Babbie Van Sickle's description
+facsimileTelephoneNumber: +1 415 976-5668
+l: Cambridge
+ou: Management
+postalAddress: example$Management$Dept # 396
+telephoneNumber: +1 303 746-3888
+title: Elite Management Warrior
+userPassword: elkciSnaVe
+uid: Babbie_Van Sickle
+givenName: Babbie
+mail: Babbie_Van Sickle@example.com
+carLicense: UPRJEKE
+departmentNumber: 8121
+employeeType: Temp
+homePhone: +1 213 463-7970
+initials: B. V.
+mobile: +1 415 118-2179
+pager: +1 408 937-2074
+manager: cn=Karrah Kingdon
+secretary: cn=Hang-Tong Timpson
+roomNumber: 8504
+
+dn: cn=Justinn Rockley, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Justinn Rockley
+sn: Rockley
+description: This is Justinn Rockley's description
+facsimileTelephoneNumber: +1 71 712-7820
+l: Cambridge
+ou: Payroll
+postalAddress: example$Payroll$Dept # 726
+telephoneNumber: +1 510 858-7589
+title: Chief Payroll Consultant
+userPassword: yelkcoRnni
+uid: Justinn_Rockley
+givenName: Justinn
+mail: Justinn_Rockley@example.com
+carLicense: 6XXFOXU
+departmentNumber: 1007
+employeeType: Manager
+homePhone: +1 71 971-2135
+initials: J. R.
+mobile: +1 213 601-3610
+pager: +1 818 132-2519
+manager: cn=Tessa Hurteau
+secretary: cn=Caine Scholman
+roomNumber: 8447
+
+dn: cn=Azhar Sprules, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Azhar Sprules
+sn: Sprules
+description: This is Azhar Sprules's description
+facsimileTelephoneNumber: +1 408 892-8515
+l: Santa Clara
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 238
+telephoneNumber: +1 415 888-3252
+title: Elite Product Testing Director
+userPassword: selurpSrah
+uid: Azhar_Sprules
+givenName: Azhar
+mail: Azhar_Sprules@example.com
+carLicense: F7UQXK9
+departmentNumber: 9433
+employeeType: Temp
+homePhone: +1 818 208-5102
+initials: A. S.
+mobile: +1 818 605-1373
+pager: +1 206 915-2931
+manager: cn=Tuan Awadalla
+secretary: cn=Kessley Kimbrough
+roomNumber: 1754
+
+dn: cn=Kimihiko Nielson, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kimihiko Nielson
+sn: Nielson
+description: This is Kimihiko Nielson's description
+facsimileTelephoneNumber: +1 804 217-6916
+l: San Francisco
+ou: Management
+postalAddress: example$Management$Dept # 574
+telephoneNumber: +1 206 167-1806
+title: Junior Management Assistant
+userPassword: nosleiNoki
+uid: Kimihiko_Nielson
+givenName: Kimihiko
+mail: Kimihiko_Nielson@example.com
+carLicense: FF9BMUF
+departmentNumber: 2273
+employeeType: Contract
+homePhone: +1 71 845-9774
+initials: K. N.
+mobile: +1 408 468-2881
+pager: +1 303 978-7876
+manager: cn=Susil Gumbley
+secretary: cn=Previn Puent
+roomNumber: 429
+
+dn: cn=Shaib Breedlove, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Shaib Breedlove
+sn: Breedlove
+description: This is Shaib Breedlove's description
+facsimileTelephoneNumber: +1 804 607-2464
+l: Fremont
+ou: Planning
+postalAddress: example$Planning$Dept # 710
+telephoneNumber: +1 408 194-6015
+title: Master Planning Yahoo
+userPassword: evoldeerBb
+uid: Shaib_Breedlove
+givenName: Shaib
+mail: Shaib_Breedlove@example.com
+carLicense: REG24HO
+departmentNumber: 8091
+employeeType: Temp
+homePhone: +1 408 602-8188
+initials: S. B.
+mobile: +1 206 518-6401
+pager: +1 303 220-3328
+manager: cn=Naresh Abou-Ezze
+secretary: cn=Osama Islam
+roomNumber: 6427
+
+dn: cn=Vonni Stegman, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Vonni Stegman
+sn: Stegman
+description: This is Vonni Stegman's description
+facsimileTelephoneNumber: +1 818 913-2880
+l: San Mateo
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 759
+telephoneNumber: +1 303 203-7946
+title: Associate Product Testing Manager
+userPassword: namgetSinn
+uid: Vonni_Stegman
+givenName: Vonni
+mail: Vonni_Stegman@example.com
+carLicense: JM3FB9Q
+departmentNumber: 3062
+employeeType: Temp
+homePhone: +1 408 948-2295
+initials: V. S.
+mobile: +1 408 993-5048
+pager: +1 303 442-3232
+manager: cn=Allsun Pownall
+secretary: cn=Blair Aston
+roomNumber: 5732
+
+dn: cn=Delly Kuczynski, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Delly Kuczynski
+sn: Kuczynski
+description: This is Delly Kuczynski's description
+facsimileTelephoneNumber: +1 415 188-2065
+l: Armonk
+ou: Administrative
+postalAddress: example$Administrative$Dept # 66
+telephoneNumber: +1 303 287-6262
+title: Associate Administrative Janitor
+userPassword: iksnyzcuKy
+uid: Delly_Kuczynski
+givenName: Delly
+mail: Delly_Kuczynski@example.com
+carLicense: P3AXFUQ
+departmentNumber: 9130
+employeeType: Contract
+homePhone: +1 303 225-2735
+initials: D. K.
+mobile: +1 415 544-7901
+pager: +1 408 123-5545
+manager: cn=Curtis Allison
+secretary: cn=Mahmut Verrenneau
+roomNumber: 3654
+
+dn: cn=Rajinderpal Packard, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rajinderpal Packard
+sn: Packard
+description: This is Rajinderpal Packard's description
+facsimileTelephoneNumber: +1 303 372-6050
+l: Redwood Shores
+ou: Peons
+postalAddress: example$Peons$Dept # 506
+telephoneNumber: +1 71 682-5254
+title: Elite Peons Fellow
+userPassword: drakcaPlap
+uid: Rajinderpal_Packard
+givenName: Rajinderpal
+mail: Rajinderpal_Packard@example.com
+carLicense: 1T5UUB0
+departmentNumber: 7073
+employeeType: Manager
+homePhone: +1 206 505-2004
+initials: R. P.
+mobile: +1 303 552-9669
+pager: +1 206 375-9250
+manager: cn=Cheryl Silgardo
+secretary: cn=Siobhan St-Pierre
+roomNumber: 6068
+
+dn: cn=Tarte Koren, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tarte Koren
+sn: Koren
+description: This is Tarte Koren's description
+facsimileTelephoneNumber: +1 408 235-6422
+l: San Francisco
+ou: Payroll
+postalAddress: example$Payroll$Dept # 807
+telephoneNumber: +1 804 293-2694
+title: Supreme Payroll Warrior
+userPassword: neroKetraT
+uid: Tarte_Koren
+givenName: Tarte
+mail: Tarte_Koren@example.com
+carLicense: P0ZB3YN
+departmentNumber: 8246
+employeeType: Temp
+homePhone: +1 510 277-4993
+initials: T. K.
+mobile: +1 206 675-5109
+pager: +1 408 224-2113
+manager: cn=Sophi Borum
+secretary: cn=Kylen Ryan
+roomNumber: 2346
+
+dn: cn=Bevvy Roden, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Bevvy Roden
+sn: Roden
+description: This is Bevvy Roden's description
+facsimileTelephoneNumber: +1 213 863-6260
+l: Cupertino
+ou: Product Development
+postalAddress: example$Product Development$Dept # 290
+telephoneNumber: +1 408 300-1187
+title: Elite Product Development Accountant
+userPassword: nedoRyvveB
+uid: Bevvy_Roden
+givenName: Bevvy
+mail: Bevvy_Roden@example.com
+carLicense: MLXY26G
+departmentNumber: 5513
+employeeType: Manager
+homePhone: +1 510 207-1740
+initials: B. R.
+mobile: +1 818 720-2123
+pager: +1 408 440-2650
+manager: cn=Rafi Eastick
+secretary: cn=Massoud Mapile
+roomNumber: 897
+
+dn: cn=Rozett Mathis, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rozett Mathis
+sn: Mathis
+description: This is Rozett Mathis's description
+facsimileTelephoneNumber: +1 303 833-5751
+l: Mountain View
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 449
+telephoneNumber: +1 213 683-5687
+title: Associate Product Testing Consultant
+userPassword: sihtaMttez
+uid: Rozett_Mathis
+givenName: Rozett
+mail: Rozett_Mathis@example.com
+carLicense: BVHD1AO
+departmentNumber: 6038
+employeeType: Normal
+homePhone: +1 818 339-4344
+initials: R. M.
+mobile: +1 303 642-3684
+pager: +1 510 443-5679
+manager: cn=Yutaka Drummond
+secretary: cn=Mandie Results
+roomNumber: 1372
+
+dn: cn=Sunshine Glew, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sunshine Glew
+sn: Glew
+description: This is Sunshine Glew's description
+facsimileTelephoneNumber: +1 408 100-1156
+l: Alameda
+ou: Planning
+postalAddress: example$Planning$Dept # 113
+telephoneNumber: +1 213 289-2825
+title: Junior Planning Consultant
+userPassword: welGenihsn
+uid: Sunshine_Glew
+givenName: Sunshine
+mail: Sunshine_Glew@example.com
+carLicense: MZY889U
+departmentNumber: 1089
+employeeType: Temp
+homePhone: +1 206 148-4758
+initials: S. G.
+mobile: +1 415 865-3351
+pager: +1 510 522-5831
+manager: cn=King-Haut Townsend
+secretary: cn=Hillary Mcellistrem
+roomNumber: 2021
+
+dn: cn=Sherie Dba, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sherie Dba
+sn: Dba
+description: This is Sherie Dba's description
+facsimileTelephoneNumber: +1 818 389-8239
+l: Redmond
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 124
+telephoneNumber: +1 818 187-1777
+title: Junior Product Testing Director
+userPassword: abDeirehS
+uid: Sherie_Dba
+givenName: Sherie
+mail: Sherie_Dba@example.com
+carLicense: 71U7RQE
+departmentNumber: 5749
+employeeType: Temp
+homePhone: +1 206 576-1081
+initials: S. D.
+mobile: +1 303 598-7803
+pager: +1 415 538-5682
+manager: cn=Fernando Sonier
+secretary: cn=Vivianne Faley
+roomNumber: 6609
+
+dn: cn=Cherianne Batchelder, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Cherianne Batchelder
+sn: Batchelder
+description: This is Cherianne Batchelder's description
+facsimileTelephoneNumber: +1 818 665-9755
+l: Menlo Park
+ou: Planning
+postalAddress: example$Planning$Dept # 948
+telephoneNumber: +1 71 629-8969
+title: Supreme Planning Pinhead
+userPassword: redlehctaB
+uid: Cherianne_Batchelder
+givenName: Cherianne
+mail: Cherianne_Batchelder@example.com
+carLicense: 9GDOZHG
+departmentNumber: 6133
+employeeType: Normal
+homePhone: +1 510 592-1483
+initials: C. B.
+mobile: +1 303 305-8621
+pager: +1 71 483-4716
+manager: cn=Jada Kolodziej
+secretary: cn=Marcela Lahlum
+roomNumber: 7346
+
+dn: cn=Yuksel Deatrick, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Yuksel Deatrick
+sn: Deatrick
+description: This is Yuksel Deatrick's description
+facsimileTelephoneNumber: +1 804 459-3158
+l: Palo Alto
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 55
+telephoneNumber: +1 213 917-9451
+title: Junior Product Testing President
+userPassword: kcirtaeDle
+uid: Yuksel_Deatrick
+givenName: Yuksel
+mail: Yuksel_Deatrick@example.com
+carLicense: AKH9YO6
+departmentNumber: 3460
+employeeType: Contract
+homePhone: +1 415 918-8804
+initials: Y. D.
+mobile: +1 206 411-8574
+pager: +1 303 859-6762
+manager: cn=Lorelle Ormsby
+secretary: cn=Megen Merrill
+roomNumber: 2895
+
+dn: cn=Kip Zaloker, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kip Zaloker
+sn: Zaloker
+description: This is Kip Zaloker's description
+facsimileTelephoneNumber: +1 510 203-6273
+l: Mountain View
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 231
+telephoneNumber: +1 71 858-5108
+title: Senior Janitorial Dictator
+userPassword: rekolaZpiK
+uid: Kip_Zaloker
+givenName: Kip
+mail: Kip_Zaloker@example.com
+carLicense: O6T7LCS
+departmentNumber: 2501
+employeeType: Normal
+homePhone: +1 303 294-5589
+initials: K. Z.
+mobile: +1 818 939-5645
+pager: +1 415 449-3063
+manager: cn=Zitella Projects
+secretary: cn=Eden Sherif
+roomNumber: 1344
+
+dn: cn=Andaree Brady, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Andaree Brady
+sn: Brady
+description: This is Andaree Brady's description
+facsimileTelephoneNumber: +1 818 701-7411
+l: Cupertino
+ou: Product Development
+postalAddress: example$Product Development$Dept # 239
+telephoneNumber: +1 804 185-1426
+title: Supreme Product Development Manager
+userPassword: ydarBeerad
+uid: Andaree_Brady
+givenName: Andaree
+mail: Andaree_Brady@example.com
+carLicense: SQK8WWU
+departmentNumber: 4226
+employeeType: Temp
+homePhone: +1 818 663-4622
+initials: A. B.
+mobile: +1 415 868-5365
+pager: +1 510 317-1116
+manager: cn=Kelli Paone
+secretary: cn=Mil Lum
+roomNumber: 5382
+
+dn: cn=Seth Ladet, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Seth Ladet
+sn: Ladet
+description: This is Seth Ladet's description
+facsimileTelephoneNumber: +1 206 122-6688
+l: San Jose
+ou: Payroll
+postalAddress: example$Payroll$Dept # 545
+telephoneNumber: +1 206 972-3584
+title: Chief Payroll Pinhead
+userPassword: tedaLhteS
+uid: Seth_Ladet
+givenName: Seth
+mail: Seth_Ladet@example.com
+carLicense: T7WAH8Z
+departmentNumber: 3616
+employeeType: Temp
+homePhone: +1 510 149-3139
+initials: S. L.
+mobile: +1 415 192-2299
+pager: +1 303 745-8757
+manager: cn=Annemarie Hennessy
+secretary: cn=Elio Laine
+roomNumber: 7672
+
+dn: cn=Pierre-Henri Weidinger, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Pierre-Henri Weidinger
+sn: Weidinger
+description: This is Pierre-Henri Weidinger's description
+facsimileTelephoneNumber: +1 213 447-5128
+l: Armonk
+ou: Administrative
+postalAddress: example$Administrative$Dept # 514
+telephoneNumber: +1 71 533-4703
+title: Chief Administrative Technician
+userPassword: regnidieWi
+uid: Pierre-Henri_Weidinger
+givenName: Pierre-Henri
+mail: Pierre-Henri_Weidinger@example.com
+carLicense: 0190X9E
+departmentNumber: 729
+employeeType: Temp
+homePhone: +1 303 749-1223
+initials: P. W.
+mobile: +1 415 191-5113
+pager: +1 213 813-1837
+manager: cn=Manny Pitcavage
+secretary: cn=Harper Decapua
+roomNumber: 7150
+
+dn: cn=Daveen Dann, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Daveen Dann
+sn: Dann
+description: This is Daveen Dann's description
+facsimileTelephoneNumber: +1 206 874-6373
+l: Alameda
+ou: Planning
+postalAddress: example$Planning$Dept # 238
+telephoneNumber: +1 510 544-4091
+title: Junior Planning Manager
+userPassword: nnaDneevaD
+uid: Daveen_Dann
+givenName: Daveen
+mail: Daveen_Dann@example.com
+carLicense: JI065N7
+departmentNumber: 5939
+employeeType: Normal
+homePhone: +1 206 882-5957
+initials: D. D.
+mobile: +1 818 340-4499
+pager: +1 303 881-4225
+manager: cn=Ashely Goulet
+secretary: cn=Margalit Kahan
+roomNumber: 3461
+
+dn: cn=Wallis Cochran, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Wallis Cochran
+sn: Cochran
+description: This is Wallis Cochran's description
+facsimileTelephoneNumber: +1 415 370-9855
+l: Emeryville
+ou: Accounting
+postalAddress: example$Accounting$Dept # 921
+telephoneNumber: +1 818 552-8988
+title: Master Accounting Accountant
+userPassword: narhcoCsil
+uid: Wallis_Cochran
+givenName: Wallis
+mail: Wallis_Cochran@example.com
+carLicense: 0KJN84D
+departmentNumber: 1461
+employeeType: Normal
+homePhone: +1 510 729-2049
+initials: W. C.
+mobile: +1 510 579-4811
+pager: +1 510 366-4052
+manager: cn=Gerard Malkani
+secretary: cn=Matti Marette
+roomNumber: 4108
+
+dn: cn=Katalin Lommen, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Katalin Lommen
+sn: Lommen
+description: This is Katalin Lommen's description
+facsimileTelephoneNumber: +1 818 906-8383
+l: Orem
+ou: Planning
+postalAddress: example$Planning$Dept # 604
+telephoneNumber: +1 303 883-1889
+title: Elite Planning Vice President
+userPassword: nemmoLnila
+uid: Katalin_Lommen
+givenName: Katalin
+mail: Katalin_Lommen@example.com
+carLicense: 0VNJP50
+departmentNumber: 2059
+employeeType: Normal
+homePhone: +1 71 992-2035
+initials: K. L.
+mobile: +1 303 820-6903
+pager: +1 408 379-5944
+manager: cn=Ailey Wingfield
+secretary: cn=Corey Marcelissen
+roomNumber: 3676
+
+dn: cn=Kathi Kunecke, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kathi Kunecke
+sn: Kunecke
+description: This is Kathi Kunecke's description
+facsimileTelephoneNumber: +1 415 932-3783
+l: Cupertino
+ou: Management
+postalAddress: example$Management$Dept # 354
+telephoneNumber: +1 206 792-2906
+title: Chief Management President
+userPassword: ekcenuKiht
+uid: Kathi_Kunecke
+givenName: Kathi
+mail: Kathi_Kunecke@example.com
+carLicense: 1AV73X4
+departmentNumber: 2037
+employeeType: Contract
+homePhone: +1 213 250-9680
+initials: K. K.
+mobile: +1 804 752-2203
+pager: +1 408 445-1685
+manager: cn=Hari Womble
+secretary: cn=Deana Patchcor
+roomNumber: 7759
+
+dn: cn=Jun Showers, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jun Showers
+sn: Showers
+description: This is Jun Showers's description
+facsimileTelephoneNumber: +1 213 952-8464
+l: Menlo Park
+ou: Accounting
+postalAddress: example$Accounting$Dept # 219
+telephoneNumber: +1 415 138-4745
+title: Elite Accounting Engineer
+userPassword: srewohSnuJ
+uid: Jun_Showers
+givenName: Jun
+mail: Jun_Showers@example.com
+carLicense: BXM4XCL
+departmentNumber: 4228
+employeeType: Temp
+homePhone: +1 213 761-8326
+initials: J. S.
+mobile: +1 818 784-8400
+pager: +1 303 877-4036
+manager: cn=Janos Pkg
+secretary: cn=Mercy Smothers
+roomNumber: 6408
+
+dn: cn=Karisa Blethen, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Karisa Blethen
+sn: Blethen
+description: This is Karisa Blethen's description
+facsimileTelephoneNumber: +1 206 378-4638
+l: San Jose
+ou: Product Development
+postalAddress: example$Product Development$Dept # 192
+telephoneNumber: +1 818 617-8160
+title: Senior Product Development Figurehead
+userPassword: nehtelBasi
+uid: Karisa_Blethen
+givenName: Karisa
+mail: Karisa_Blethen@example.com
+carLicense: 2IOFJ9P
+departmentNumber: 4573
+employeeType: Employee
+homePhone: +1 303 934-6410
+initials: K. B.
+mobile: +1 818 353-7906
+pager: +1 818 892-1447
+manager: cn=Anallese Silverthorn
+secretary: cn=Ginnie Kiel
+roomNumber: 5237
+
+dn: cn=Arda Njo, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Arda Njo
+sn: Njo
+description: This is Arda Njo's description
+facsimileTelephoneNumber: +1 804 412-7464
+l: Armonk
+ou: Planning
+postalAddress: example$Planning$Dept # 938
+telephoneNumber: +1 510 593-9460
+title: Senior Planning Developer
+userPassword: ojNadrA
+uid: Arda_Njo
+givenName: Arda
+mail: Arda_Njo@example.com
+carLicense: XL3XF3M
+departmentNumber: 9245
+employeeType: Contract
+homePhone: +1 510 965-6740
+initials: A. N.
+mobile: +1 213 207-6189
+pager: +1 206 341-6816
+manager: cn=Berni Hahn
+secretary: cn=Heda Gattrell
+roomNumber: 4815
+
+dn: cn=GeorgeAnn Paschall, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: GeorgeAnn Paschall
+sn: Paschall
+description: This is GeorgeAnn Paschall's description
+facsimileTelephoneNumber: +1 415 131-1692
+l: Mountain View
+ou: Management
+postalAddress: example$Management$Dept # 381
+telephoneNumber: +1 818 585-8895
+title: Elite Management Mascot
+userPassword: llahcsaPnn
+uid: GeorgeAnn_Paschall
+givenName: GeorgeAnn
+mail: GeorgeAnn_Paschall@example.com
+carLicense: MQSVK2P
+departmentNumber: 5077
+employeeType: Temp
+homePhone: +1 408 318-7319
+initials: G. P.
+mobile: +1 206 141-9887
+pager: +1 408 765-2390
+manager: cn=Mid Cheal
+secretary: cn=Lacie Knittel
+roomNumber: 7796
+
+dn: cn=Manimozhi Nambride, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Manimozhi Nambride
+sn: Nambride
+description: This is Manimozhi Nambride's description
+facsimileTelephoneNumber: +1 415 950-3359
+l: Santa Clara
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 934
+telephoneNumber: +1 303 100-6826
+title: Junior Janitorial Consultant
+userPassword: edirbmaNih
+uid: Manimozhi_Nambride
+givenName: Manimozhi
+mail: Manimozhi_Nambride@example.com
+carLicense: KE3BXSB
+departmentNumber: 6895
+employeeType: Manager
+homePhone: +1 804 913-9282
+initials: M. N.
+mobile: +1 71 457-3978
+pager: +1 818 649-3056
+manager: cn=Karisa Wasitova
+secretary: cn=Edwina Dempster
+roomNumber: 7328
+
+dn: cn=Cheng Herling, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Cheng Herling
+sn: Herling
+description: This is Cheng Herling's description
+facsimileTelephoneNumber: +1 510 799-7267
+l: Santa Clara
+ou: Peons
+postalAddress: example$Peons$Dept # 643
+telephoneNumber: +1 415 732-9185
+title: Elite Peons Stooge
+userPassword: gnilreHgne
+uid: Cheng_Herling
+givenName: Cheng
+mail: Cheng_Herling@example.com
+carLicense: Y2KQCUM
+departmentNumber: 6119
+employeeType: Normal
+homePhone: +1 804 921-3267
+initials: C. H.
+mobile: +1 415 485-2002
+pager: +1 303 622-8565
+manager: cn=Neely Nickells
+secretary: cn=Darya Lucas
+roomNumber: 7426
+
+dn: cn=Rigoberto Cohea, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rigoberto Cohea
+sn: Cohea
+description: This is Rigoberto Cohea's description
+facsimileTelephoneNumber: +1 804 916-3581
+l: Emeryville
+ou: Product Development
+postalAddress: example$Product Development$Dept # 641
+telephoneNumber: +1 804 174-4795
+title: Supreme Product Development Sales Rep
+userPassword: aehoCotreb
+uid: Rigoberto_Cohea
+givenName: Rigoberto
+mail: Rigoberto_Cohea@example.com
+carLicense: CBKSEYP
+departmentNumber: 8439
+employeeType: Contract
+homePhone: +1 206 331-7116
+initials: R. C.
+mobile: +1 71 832-6927
+pager: +1 213 846-6202
+manager: cn=Delphine Astalos
+secretary: cn=Paul Asgharzadeh
+roomNumber: 3490
+
+dn: cn=Cindelyn Pedigo, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Cindelyn Pedigo
+sn: Pedigo
+description: This is Cindelyn Pedigo's description
+facsimileTelephoneNumber: +1 818 113-3409
+l: Palo Alto
+ou: Product Development
+postalAddress: example$Product Development$Dept # 468
+telephoneNumber: +1 818 371-1388
+title: Elite Product Development Developer
+userPassword: ogidePnyle
+uid: Cindelyn_Pedigo
+givenName: Cindelyn
+mail: Cindelyn_Pedigo@example.com
+carLicense: CIDGKGE
+departmentNumber: 452
+employeeType: Manager
+homePhone: +1 415 280-1529
+initials: C. P.
+mobile: +1 510 104-9328
+pager: +1 415 231-2322
+manager: cn=Uday McCaughey
+secretary: cn=Richardson Chapen
+roomNumber: 227
+
+dn: cn=Loni Navarro, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Loni Navarro
+sn: Navarro
+description: This is Loni Navarro's description
+facsimileTelephoneNumber: +1 206 824-5495
+l: Fremont
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 664
+telephoneNumber: +1 510 166-7452
+title: Chief Human Resources Artist
+userPassword: orravaNino
+uid: Loni_Navarro
+givenName: Loni
+mail: Loni_Navarro@example.com
+carLicense: HB9XT3Q
+departmentNumber: 2653
+employeeType: Contract
+homePhone: +1 804 155-8530
+initials: L. N.
+mobile: +1 213 197-5641
+pager: +1 415 400-9972
+manager: cn=Htd Wasserman
+secretary: cn=Dotty Spindler
+roomNumber: 7180
+
+dn: cn=Della Sabol, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Della Sabol
+sn: Sabol
+description: This is Della Sabol's description
+facsimileTelephoneNumber: +1 510 483-9525
+l: Emeryville
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 696
+telephoneNumber: +1 804 220-6286
+title: Junior Product Testing Dictator
+userPassword: lobaSalleD
+uid: Della_Sabol
+givenName: Della
+mail: Della_Sabol@example.com
+carLicense: YEYFCSF
+departmentNumber: 9542
+employeeType: Normal
+homePhone: +1 408 435-7591
+initials: D. S.
+mobile: +1 213 955-8935
+pager: +1 213 769-5940
+manager: cn=Kirby Coste
+secretary: cn=Peggi Rabjohn
+roomNumber: 8388
+
+dn: cn=Veleta Ganguly, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Veleta Ganguly
+sn: Ganguly
+description: This is Veleta Ganguly's description
+facsimileTelephoneNumber: +1 804 461-4636
+l: Redwood Shores
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 326
+telephoneNumber: +1 303 548-4933
+title: Master Human Resources Sales Rep
+userPassword: ylugnaGate
+uid: Veleta_Ganguly
+givenName: Veleta
+mail: Veleta_Ganguly@example.com
+carLicense: FCKFIIA
+departmentNumber: 8350
+employeeType: Manager
+homePhone: +1 213 308-6469
+initials: V. G.
+mobile: +1 415 244-5336
+pager: +1 213 180-3400
+manager: cn=Quoc-Vu Coupal
+secretary: cn=Dru Bredeck
+roomNumber: 644
+
+dn: cn=Susann Appleyard, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Susann Appleyard
+sn: Appleyard
+description: This is Susann Appleyard's description
+facsimileTelephoneNumber: +1 303 867-5090
+l: Sunnyvale
+ou: Planning
+postalAddress: example$Planning$Dept # 233
+telephoneNumber: +1 303 728-7897
+title: Junior Planning Czar
+userPassword: drayelppAn
+uid: Susann_Appleyard
+givenName: Susann
+mail: Susann_Appleyard@example.com
+carLicense: S8PYXWW
+departmentNumber: 7783
+employeeType: Normal
+homePhone: +1 415 651-8434
+initials: S. A.
+mobile: +1 408 689-6582
+pager: +1 818 885-2995
+manager: cn=Minnaminnie Pieron
+secretary: cn=Nenad Bagetakos
+roomNumber: 3714
+
+dn: cn=Hermien Purchasing, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Hermien Purchasing
+sn: Purchasing
+description: This is Hermien Purchasing's description
+facsimileTelephoneNumber: +1 408 121-9607
+l: Milpitas
+ou: Accounting
+postalAddress: example$Accounting$Dept # 545
+telephoneNumber: +1 206 385-9588
+title: Associate Accounting Evangelist
+userPassword: gnisahcruP
+uid: Hermien_Purchasing
+givenName: Hermien
+mail: Hermien_Purchasing@example.com
+carLicense: VO7L7V2
+departmentNumber: 8034
+employeeType: Normal
+homePhone: +1 213 374-3103
+initials: H. P.
+mobile: +1 71 261-3443
+pager: +1 818 791-3370
+manager: cn=Marianne Crippen
+secretary: cn=Kassi Prichard
+roomNumber: 572
+
+dn: cn=Nance Hawken, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Nance Hawken
+sn: Hawken
+description: This is Nance Hawken's description
+facsimileTelephoneNumber: +1 71 546-4303
+l: San Francisco
+ou: Management
+postalAddress: example$Management$Dept # 51
+telephoneNumber: +1 303 154-4057
+title: Associate Management Dictator
+userPassword: nekwaHecna
+uid: Nance_Hawken
+givenName: Nance
+mail: Nance_Hawken@example.com
+carLicense: I0FGJBC
+departmentNumber: 7420
+employeeType: Normal
+homePhone: +1 415 245-8639
+initials: N. H.
+mobile: +1 303 575-8328
+pager: +1 408 636-3502
+manager: cn=Narida Krieg
+secretary: cn=Kana Azizuddin
+roomNumber: 8582
+
+dn: cn=Leisha Damena, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Leisha Damena
+sn: Damena
+description: This is Leisha Damena's description
+facsimileTelephoneNumber: +1 303 780-2129
+l: Redmond
+ou: Administrative
+postalAddress: example$Administrative$Dept # 950
+telephoneNumber: +1 206 435-6314
+title: Elite Administrative Developer
+userPassword: anemaDahsi
+uid: Leisha_Damena
+givenName: Leisha
+mail: Leisha_Damena@example.com
+carLicense: 36W71OF
+departmentNumber: 974
+employeeType: Manager
+homePhone: +1 71 375-3905
+initials: L. D.
+mobile: +1 415 577-2948
+pager: +1 408 242-1941
+manager: cn=Nico Nix
+secretary: cn=Ernie Dunham
+roomNumber: 75
+
+dn: cn=Randy Hardcastle, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Randy Hardcastle
+sn: Hardcastle
+description: This is Randy Hardcastle's description
+facsimileTelephoneNumber: +1 206 976-4416
+l: Palo Alto
+ou: Accounting
+postalAddress: example$Accounting$Dept # 558
+telephoneNumber: +1 303 554-1526
+title: Senior Accounting Writer
+userPassword: eltsacdraH
+uid: Randy_Hardcastle
+givenName: Randy
+mail: Randy_Hardcastle@example.com
+carLicense: 48ADIBF
+departmentNumber: 9450
+employeeType: Employee
+homePhone: +1 818 651-2394
+initials: R. H.
+mobile: +1 818 777-2263
+pager: +1 206 705-4779
+manager: cn=Ilan Tombul
+secretary: cn=Celia Szkarlat
+roomNumber: 8487
+
+dn: cn=Charmane Zeggil, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Charmane Zeggil
+sn: Zeggil
+description: This is Charmane Zeggil's description
+facsimileTelephoneNumber: +1 510 455-4632
+l: Cambridge
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 482
+telephoneNumber: +1 206 175-7998
+title: Supreme Human Resources Visionary
+userPassword: liggeZenam
+uid: Charmane_Zeggil
+givenName: Charmane
+mail: Charmane_Zeggil@example.com
+carLicense: 0MAVAUF
+departmentNumber: 9354
+employeeType: Employee
+homePhone: +1 408 126-3636
+initials: C. Z.
+mobile: +1 303 117-6915
+pager: +1 510 270-9083
+manager: cn=Manda Suwanawongse
+secretary: cn=Ylaine Luettchau
+roomNumber: 6672
+
+dn: cn=Ansley Corner, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ansley Corner
+sn: Corner
+description: This is Ansley Corner's description
+facsimileTelephoneNumber: +1 804 958-6413
+l: Menlo Park
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 28
+telephoneNumber: +1 213 482-9036
+title: Master Product Testing Technician
+userPassword: renroCyels
+uid: Ansley_Corner
+givenName: Ansley
+mail: Ansley_Corner@example.com
+carLicense: FE4A53S
+departmentNumber: 940
+employeeType: Manager
+homePhone: +1 71 287-7267
+initials: A. C.
+mobile: +1 71 188-6134
+pager: +1 415 927-6612
+manager: cn=Pension Kotler
+secretary: cn=Christabella Zumhagen
+roomNumber: 7935
+
+dn: cn=Colette Longo, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Colette Longo
+sn: Longo
+description: This is Colette Longo's description
+facsimileTelephoneNumber: +1 206 502-4699
+l: Mountain View
+ou: Peons
+postalAddress: example$Peons$Dept # 84
+telephoneNumber: +1 415 940-4230
+title: Senior Peons Pinhead
+userPassword: ognoLettel
+uid: Colette_Longo
+givenName: Colette
+mail: Colette_Longo@example.com
+carLicense: H8VXVTT
+departmentNumber: 1994
+employeeType: Temp
+homePhone: +1 303 871-6476
+initials: C. L.
+mobile: +1 510 620-6333
+pager: +1 206 331-7790
+manager: cn=Micro Herlihy
+secretary: cn=Fahim Averett
+roomNumber: 4931
+
+dn: cn=Loretta Clason, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Loretta Clason
+sn: Clason
+description: This is Loretta Clason's description
+facsimileTelephoneNumber: +1 303 261-3535
+l: Orem
+ou: Payroll
+postalAddress: example$Payroll$Dept # 428
+telephoneNumber: +1 510 389-8623
+title: Chief Payroll Figurehead
+userPassword: nosalCatte
+uid: Loretta_Clason
+givenName: Loretta
+mail: Loretta_Clason@example.com
+carLicense: BRE3ORB
+departmentNumber: 7915
+employeeType: Employee
+homePhone: +1 206 716-6752
+initials: L. C.
+mobile: +1 415 234-1691
+pager: +1 71 636-3923
+manager: cn=Gihan Slade
+secretary: cn=Caresse Hui
+roomNumber: 1958
+
+dn: cn=Eloise Haggarty, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Eloise Haggarty
+sn: Haggarty
+description: This is Eloise Haggarty's description
+facsimileTelephoneNumber: +1 71 852-9666
+l: Milpitas
+ou: Management
+postalAddress: example$Management$Dept # 223
+telephoneNumber: +1 213 552-9784
+title: Elite Management Engineer
+userPassword: ytraggaHes
+uid: Eloise_Haggarty
+givenName: Eloise
+mail: Eloise_Haggarty@example.com
+carLicense: 9EIE7SA
+departmentNumber: 217
+employeeType: Contract
+homePhone: +1 804 871-6041
+initials: E. H.
+mobile: +1 303 695-9076
+pager: +1 408 276-3706
+manager: cn=Opalina DeNoon
+secretary: cn=Wilford Leiding
+roomNumber: 2135
+
+dn: cn=Allix Regier, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Allix Regier
+sn: Regier
+description: This is Allix Regier's description
+facsimileTelephoneNumber: +1 408 654-3843
+l: Orem
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 24
+telephoneNumber: +1 510 353-4168
+title: Chief Product Testing Developer
+userPassword: reigeRxill
+uid: Allix_Regier
+givenName: Allix
+mail: Allix_Regier@example.com
+carLicense: KPZCB05
+departmentNumber: 4046
+employeeType: Employee
+homePhone: +1 818 521-3665
+initials: A. R.
+mobile: +1 510 893-1785
+pager: +1 510 670-5202
+manager: cn=Madelaine Rahrer
+secretary: cn=Valli Townley
+roomNumber: 2766
+
+dn: cn=Brittany McConnell, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Brittany McConnell
+sn: McConnell
+description: This is Brittany McConnell's description
+facsimileTelephoneNumber: +1 303 841-2461
+l: Cambridge
+ou: Planning
+postalAddress: example$Planning$Dept # 949
+telephoneNumber: +1 818 187-1717
+title: Junior Planning Czar
+userPassword: llennoCcMy
+uid: Brittany_McConnell
+givenName: Brittany
+mail: Brittany_McConnell@example.com
+carLicense: RPJRY69
+departmentNumber: 3424
+employeeType: Manager
+homePhone: +1 303 704-2226
+initials: B. M.
+mobile: +1 213 214-3692
+pager: +1 804 757-4328
+manager: cn=Surinder Sridhar
+secretary: cn=Discover Beasley
+roomNumber: 4794
+
+dn: cn=Blythe Lathangue, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Blythe Lathangue
+sn: Lathangue
+description: This is Blythe Lathangue's description
+facsimileTelephoneNumber: +1 206 202-9249
+l: San Jose
+ou: Administrative
+postalAddress: example$Administrative$Dept # 63
+telephoneNumber: +1 408 614-1741
+title: Master Administrative Admin
+userPassword: eugnahtaLe
+uid: Blythe_Lathangue
+givenName: Blythe
+mail: Blythe_Lathangue@example.com
+carLicense: NHQ5KNV
+departmentNumber: 7548
+employeeType: Temp
+homePhone: +1 408 821-1540
+initials: B. L.
+mobile: +1 213 728-5608
+pager: +1 303 265-1876
+manager: cn=Caritta Visentin
+secretary: cn=Gayl Fastfeat
+roomNumber: 8766
+
+dn: cn=Arts Sahli, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Arts Sahli
+sn: Sahli
+description: This is Arts Sahli's description
+facsimileTelephoneNumber: +1 213 325-2328
+l: Sunnyvale
+ou: Product Development
+postalAddress: example$Product Development$Dept # 702
+telephoneNumber: +1 303 722-7085
+title: Senior Product Development Admin
+userPassword: ilhaSstrA
+uid: Arts_Sahli
+givenName: Arts
+mail: Arts_Sahli@example.com
+carLicense: 32X4B7R
+departmentNumber: 1729
+employeeType: Contract
+homePhone: +1 510 156-9430
+initials: A. S.
+mobile: +1 303 830-5331
+pager: +1 415 948-7378
+manager: cn=Ardyth Veloz
+secretary: cn=Canadian Wyble
+roomNumber: 1973
+
+dn: cn=Ryszard Finak, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ryszard Finak
+sn: Finak
+description: This is Ryszard Finak's description
+facsimileTelephoneNumber: +1 510 364-3425
+l: Mountain View
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 803
+telephoneNumber: +1 213 164-9389
+title: Elite Human Resources Technician
+userPassword: kaniFdrazs
+uid: Ryszard_Finak
+givenName: Ryszard
+mail: Ryszard_Finak@example.com
+carLicense: C2TVT5L
+departmentNumber: 684
+employeeType: Employee
+homePhone: +1 510 536-4963
+initials: R. F.
+mobile: +1 303 425-7763
+pager: +1 510 214-3756
+manager: cn=Yasmin Langer
+secretary: cn=Elsy Porterfield
+roomNumber: 5128
+
+dn: cn=Marga Heroux, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marga Heroux
+sn: Heroux
+description: This is Marga Heroux's description
+facsimileTelephoneNumber: +1 415 999-9233
+l: Emeryville
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 88
+telephoneNumber: +1 415 469-4223
+title: Master Janitorial Grunt
+userPassword: xuoreHagra
+uid: Marga_Heroux
+givenName: Marga
+mail: Marga_Heroux@example.com
+carLicense: X98UNCY
+departmentNumber: 3188
+employeeType: Normal
+homePhone: +1 415 294-9705
+initials: M. H.
+mobile: +1 71 414-5756
+pager: +1 818 491-3954
+manager: cn=Edee Ludwig
+secretary: cn=Claudetta Khorami
+roomNumber: 6829
+
+dn: cn=Warren Lingafelter, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Warren Lingafelter
+sn: Lingafelter
+description: This is Warren Lingafelter's description
+facsimileTelephoneNumber: +1 213 184-3150
+l: Cambridge
+ou: Management
+postalAddress: example$Management$Dept # 800
+telephoneNumber: +1 303 895-2893
+title: Supreme Management Architect
+userPassword: retlefagni
+uid: Warren_Lingafelter
+givenName: Warren
+mail: Warren_Lingafelter@example.com
+carLicense: SWA3JR6
+departmentNumber: 6906
+employeeType: Employee
+homePhone: +1 71 372-3359
+initials: W. L.
+mobile: +1 206 920-7077
+pager: +1 415 676-6356
+manager: cn=Ly-Khanh Biersach
+secretary: cn=Peggi Bartley
+roomNumber: 4949
+
+dn: cn=Divine Pascas, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Divine Pascas
+sn: Pascas
+description: This is Divine Pascas's description
+facsimileTelephoneNumber: +1 408 906-1374
+l: Santa Clara
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 246
+telephoneNumber: +1 206 486-4052
+title: Elite Human Resources Director
+userPassword: sacsaPeniv
+uid: Divine_Pascas
+givenName: Divine
+mail: Divine_Pascas@example.com
+carLicense: S2ZW1JU
+departmentNumber: 7609
+employeeType: Employee
+homePhone: +1 510 533-4634
+initials: D. P.
+mobile: +1 818 823-8494
+pager: +1 213 152-4421
+manager: cn=Zena Taki
+secretary: cn=Aurora DiSisto
+roomNumber: 9317
+
+dn: cn=Gavin Belcher, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gavin Belcher
+sn: Belcher
+description: This is Gavin Belcher's description
+facsimileTelephoneNumber: +1 303 583-6380
+l: Milpitas
+ou: Product Development
+postalAddress: example$Product Development$Dept # 839
+telephoneNumber: +1 213 673-3041
+title: Master Product Development Director
+userPassword: rehcleBniv
+uid: Gavin_Belcher
+givenName: Gavin
+mail: Gavin_Belcher@example.com
+carLicense: FSNYGVG
+departmentNumber: 8596
+employeeType: Contract
+homePhone: +1 213 683-9693
+initials: G. B.
+mobile: +1 818 250-9957
+pager: +1 804 713-6338
+manager: cn=Kennon Sabety
+secretary: cn=Kristen Hage
+roomNumber: 1730
+
+dn: cn=Peng-David Nash, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Peng-David Nash
+sn: Nash
+description: This is Peng-David Nash's description
+facsimileTelephoneNumber: +1 71 605-1245
+l: Palo Alto
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 997
+telephoneNumber: +1 415 446-9898
+title: Junior Janitorial Mascot
+userPassword: hsaNdivaD-
+uid: Peng-David_Nash
+givenName: Peng-David
+mail: Peng-David_Nash@example.com
+carLicense: GZ8V5YY
+departmentNumber: 1320
+employeeType: Employee
+homePhone: +1 415 480-9066
+initials: P. N.
+mobile: +1 71 404-8977
+pager: +1 818 532-1657
+manager: cn=Luigi Schnackenberg
+secretary: cn=Emlynn Aghili
+roomNumber: 9389
+
+dn: cn=Delancey Schiegl, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Delancey Schiegl
+sn: Schiegl
+description: This is Delancey Schiegl's description
+facsimileTelephoneNumber: +1 415 707-2946
+l: Mountain View
+ou: Management
+postalAddress: example$Management$Dept # 700
+telephoneNumber: +1 818 509-7560
+title: Elite Management Janitor
+userPassword: lgeihcSyec
+uid: Delancey_Schiegl
+givenName: Delancey
+mail: Delancey_Schiegl@example.com
+carLicense: K5MOA6C
+departmentNumber: 2402
+employeeType: Contract
+homePhone: +1 303 384-2162
+initials: D. S.
+mobile: +1 408 163-6633
+pager: +1 818 183-8147
+manager: cn=Chatri Xu
+secretary: cn=Rheta O'Grady
+roomNumber: 919
+
+dn: cn=Jeremy Voduc, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jeremy Voduc
+sn: Voduc
+description: This is Jeremy Voduc's description
+facsimileTelephoneNumber: +1 303 304-2896
+l: Sunnyvale
+ou: Accounting
+postalAddress: example$Accounting$Dept # 720
+telephoneNumber: +1 804 560-6668
+title: Master Accounting Vice President
+userPassword: cudoVymere
+uid: Jeremy_Voduc
+givenName: Jeremy
+mail: Jeremy_Voduc@example.com
+carLicense: O2N8VGG
+departmentNumber: 1401
+employeeType: Normal
+homePhone: +1 818 421-6153
+initials: J. V.
+mobile: +1 804 379-5643
+pager: +1 804 954-9926
+manager: cn=Katsumi Saucerman
+secretary: cn=Agnola McTiernan
+roomNumber: 9998
+
+dn: cn=Zita Kelleher, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Zita Kelleher
+sn: Kelleher
+description: This is Zita Kelleher's description
+facsimileTelephoneNumber: +1 206 173-5719
+l: Palo Alto
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 435
+telephoneNumber: +1 510 401-5342
+title: Elite Janitorial Technician
+userPassword: rehelleKat
+uid: Zita_Kelleher
+givenName: Zita
+mail: Zita_Kelleher@example.com
+carLicense: 8P4XOUC
+departmentNumber: 8096
+employeeType: Temp
+homePhone: +1 510 778-2566
+initials: Z. K.
+mobile: +1 804 403-4644
+pager: +1 303 842-1400
+manager: cn=Ania Sidor
+secretary: cn=JR Kendrick
+roomNumber: 4638
+
+dn: cn=Melva Feutlinske, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Melva Feutlinske
+sn: Feutlinske
+description: This is Melva Feutlinske's description
+facsimileTelephoneNumber: +1 303 816-7419
+l: Redmond
+ou: Payroll
+postalAddress: example$Payroll$Dept # 569
+telephoneNumber: +1 206 254-9854
+title: Supreme Payroll Developer
+userPassword: eksniltueF
+uid: Melva_Feutlinske
+givenName: Melva
+mail: Melva_Feutlinske@example.com
+carLicense: GTWW23E
+departmentNumber: 7474
+employeeType: Contract
+homePhone: +1 818 295-5341
+initials: M. F.
+mobile: +1 71 688-4474
+pager: +1 408 815-2399
+manager: cn=Tdr Chalker
+secretary: cn=Nalani Hibler
+roomNumber: 1557
+
+dn: cn=Pey-Kee Tharby, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Pey-Kee Tharby
+sn: Tharby
+description: This is Pey-Kee Tharby's description
+facsimileTelephoneNumber: +1 804 782-7676
+l: San Mateo
+ou: Planning
+postalAddress: example$Planning$Dept # 350
+telephoneNumber: +1 213 542-1425
+title: Supreme Planning Technician
+userPassword: ybrahTeeK-
+uid: Pey-Kee_Tharby
+givenName: Pey-Kee
+mail: Pey-Kee_Tharby@example.com
+carLicense: X4AIZ8B
+departmentNumber: 9292
+employeeType: Contract
+homePhone: +1 71 183-2147
+initials: P. T.
+mobile: +1 510 109-8298
+pager: +1 213 557-3084
+manager: cn=Nader Noguchi
+secretary: cn=Jessica Drayton
+roomNumber: 2936
+
+dn: cn=Harli Zagrodney, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Harli Zagrodney
+sn: Zagrodney
+description: This is Harli Zagrodney's description
+facsimileTelephoneNumber: +1 415 711-4965
+l: Alameda
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 227
+telephoneNumber: +1 206 546-5529
+title: Master Human Resources Architect
+userPassword: yendorgaZi
+uid: Harli_Zagrodney
+givenName: Harli
+mail: Harli_Zagrodney@example.com
+carLicense: JV2I693
+departmentNumber: 7952
+employeeType: Contract
+homePhone: +1 71 191-6885
+initials: H. Z.
+mobile: +1 71 223-5022
+pager: +1 303 760-1890
+manager: cn=Seamus Bonneau
+secretary: cn=Willamina Drakage
+roomNumber: 2895
+
+dn: cn=Desmond MacLennan, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Desmond MacLennan
+sn: MacLennan
+description: This is Desmond MacLennan's description
+facsimileTelephoneNumber: +1 415 637-2954
+l: Orem
+ou: Administrative
+postalAddress: example$Administrative$Dept # 822
+telephoneNumber: +1 415 168-8626
+title: Chief Administrative Writer
+userPassword: nanneLcaMd
+uid: Desmond_MacLennan
+givenName: Desmond
+mail: Desmond_MacLennan@example.com
+carLicense: EBVD8VQ
+departmentNumber: 9259
+employeeType: Temp
+homePhone: +1 818 950-6537
+initials: D. M.
+mobile: +1 408 946-9432
+pager: +1 804 975-4570
+manager: cn=Mer Testsds
+secretary: cn=Ranique Chapin
+roomNumber: 944
+
+dn: cn=Jaquith Chesterfield, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jaquith Chesterfield
+sn: Chesterfield
+description: This is Jaquith Chesterfield's description
+facsimileTelephoneNumber: +1 213 680-7831
+l: Alameda
+ou: Accounting
+postalAddress: example$Accounting$Dept # 690
+telephoneNumber: +1 804 157-8232
+title: Junior Accounting Mascot
+userPassword: dleifretse
+uid: Jaquith_Chesterfield
+givenName: Jaquith
+mail: Jaquith_Chesterfield@example.com
+carLicense: VE9DC2B
+departmentNumber: 9425
+employeeType: Contract
+homePhone: +1 206 552-1958
+initials: J. C.
+mobile: +1 510 965-7707
+pager: +1 213 517-1647
+manager: cn=Hpone Irccar
+secretary: cn=Ginette Benchimol
+roomNumber: 7035
+
+dn: cn=Shara Okon, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Shara Okon
+sn: Okon
+description: This is Shara Okon's description
+facsimileTelephoneNumber: +1 415 655-2039
+l: Santa Clara
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 828
+telephoneNumber: +1 415 390-4298
+title: Associate Human Resources Director
+userPassword: nokOarahS
+uid: Shara_Okon
+givenName: Shara
+mail: Shara_Okon@example.com
+carLicense: 6WQE2UM
+departmentNumber: 927
+employeeType: Temp
+homePhone: +1 804 178-4483
+initials: S. O.
+mobile: +1 818 381-8543
+pager: +1 408 449-7208
+manager: cn=Erina Cleary
+secretary: cn=Truman Lonsdale
+roomNumber: 7763
+
+dn: cn=Lynnelle Kausche, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lynnelle Kausche
+sn: Kausche
+description: This is Lynnelle Kausche's description
+facsimileTelephoneNumber: +1 818 434-9046
+l: Redwood Shores
+ou: Management
+postalAddress: example$Management$Dept # 442
+telephoneNumber: +1 71 157-4161
+title: Supreme Management Accountant
+userPassword: ehcsuaKell
+uid: Lynnelle_Kausche
+givenName: Lynnelle
+mail: Lynnelle_Kausche@example.com
+carLicense: E4QWHXO
+departmentNumber: 6363
+employeeType: Employee
+homePhone: +1 408 494-9482
+initials: L. K.
+mobile: +1 206 959-1722
+pager: +1 818 545-9988
+manager: cn=Mariquilla Semler
+secretary: cn=Hodge Marinos
+roomNumber: 1976
+
+dn: cn=Mirjam Lenox, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Mirjam Lenox
+sn: Lenox
+description: This is Mirjam Lenox's description
+facsimileTelephoneNumber: +1 206 835-1106
+l: San Mateo
+ou: Planning
+postalAddress: example$Planning$Dept # 979
+telephoneNumber: +1 408 690-1570
+title: Master Planning Assistant
+userPassword: xoneLmajri
+uid: Mirjam_Lenox
+givenName: Mirjam
+mail: Mirjam_Lenox@example.com
+carLicense: DL2TQCH
+departmentNumber: 5969
+employeeType: Manager
+homePhone: +1 408 678-9627
+initials: M. L.
+mobile: +1 303 556-4017
+pager: +1 206 268-6154
+manager: cn=Rennie Lightfoot
+secretary: cn=Karon Zeidler
+roomNumber: 3581
+
+dn: cn=Nayneshkumar Rylott, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Nayneshkumar Rylott
+sn: Rylott
+description: This is Nayneshkumar Rylott's description
+facsimileTelephoneNumber: +1 804 224-3420
+l: Alameda
+ou: Planning
+postalAddress: example$Planning$Dept # 839
+telephoneNumber: +1 818 795-4425
+title: Junior Planning Technician
+userPassword: ttolyRramu
+uid: Nayneshkumar_Rylott
+givenName: Nayneshkumar
+mail: Nayneshkumar_Rylott@example.com
+carLicense: CC6TJTD
+departmentNumber: 7263
+employeeType: Employee
+homePhone: +1 510 845-9141
+initials: N. R.
+mobile: +1 71 388-7333
+pager: +1 804 403-9908
+manager: cn=Lucilia Sliter
+secretary: cn=Mario Foley
+roomNumber: 5827
+
+dn: cn=Wai-Hung Hikita, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Wai-Hung Hikita
+sn: Hikita
+description: This is Wai-Hung Hikita's description
+facsimileTelephoneNumber: +1 303 692-1566
+l: San Mateo
+ou: Peons
+postalAddress: example$Peons$Dept # 297
+telephoneNumber: +1 71 340-8208
+title: Elite Peons Figurehead
+userPassword: atikiHgnuH
+uid: Wai-Hung_Hikita
+givenName: Wai-Hung
+mail: Wai-Hung_Hikita@example.com
+carLicense: 6ZUD5PW
+departmentNumber: 2205
+employeeType: Contract
+homePhone: +1 408 216-8094
+initials: W. H.
+mobile: +1 71 190-3801
+pager: +1 213 825-8550
+manager: cn=Belen Baskaran
+secretary: cn=Amabel Sizto
+roomNumber: 200
+
+dn: cn=Marguerite St.Laurent, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marguerite St.Laurent
+sn: St.Laurent
+description: This is Marguerite St.Laurent's description
+facsimileTelephoneNumber: +1 71 262-8043
+l: Cambridge
+ou: Management
+postalAddress: example$Management$Dept # 181
+telephoneNumber: +1 206 747-5983
+title: Master Management Yahoo
+userPassword: tneruaL.tS
+uid: Marguerite_St.Laurent
+givenName: Marguerite
+mail: Marguerite_St.Laurent@example.com
+carLicense: XS00HHV
+departmentNumber: 6589
+employeeType: Temp
+homePhone: +1 213 904-4673
+initials: M. S.
+mobile: +1 804 381-1347
+pager: +1 408 183-8578
+manager: cn=Armine Klapper
+secretary: cn=Josine Rowe
+roomNumber: 985
+
+dn: cn=Tessty Purson, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tessty Purson
+sn: Purson
+description: This is Tessty Purson's description
+facsimileTelephoneNumber: +1 415 685-7459
+l: Redmond
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 373
+telephoneNumber: +1 415 280-6139
+title: Elite Janitorial Vice President
+userPassword: nosruPytss
+uid: Tessty_Purson
+givenName: Tessty
+mail: Tessty_Purson@example.com
+carLicense: G5XNA9M
+departmentNumber: 254
+employeeType: Employee
+homePhone: +1 510 591-2471
+initials: T. P.
+mobile: +1 303 327-4095
+pager: +1 206 461-7570
+manager: cn=Adeline Harrison
+secretary: cn=Elfrida Netlink
+roomNumber: 489
+
+dn: cn=Shaylah Riddall, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Shaylah Riddall
+sn: Riddall
+description: This is Shaylah Riddall's description
+facsimileTelephoneNumber: +1 818 542-3574
+l: Armonk
+ou: Accounting
+postalAddress: example$Accounting$Dept # 23
+telephoneNumber: +1 213 978-1302
+title: Senior Accounting Mascot
+userPassword: lladdiRhal
+uid: Shaylah_Riddall
+givenName: Shaylah
+mail: Shaylah_Riddall@example.com
+carLicense: 5L1I6GB
+departmentNumber: 2947
+employeeType: Employee
+homePhone: +1 303 501-9242
+initials: S. R.
+mobile: +1 415 325-6243
+pager: +1 415 869-9605
+manager: cn=Aloisia Freno
+secretary: cn=Gin Mejury
+roomNumber: 2149
+
+dn: cn=Dorothy Drabek, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dorothy Drabek
+sn: Drabek
+description: This is Dorothy Drabek's description
+facsimileTelephoneNumber: +1 206 358-7831
+l: Mountain View
+ou: Planning
+postalAddress: example$Planning$Dept # 200
+telephoneNumber: +1 408 889-5119
+title: Elite Planning Architect
+userPassword: kebarDyhto
+uid: Dorothy_Drabek
+givenName: Dorothy
+mail: Dorothy_Drabek@example.com
+carLicense: AUJ0A94
+departmentNumber: 8158
+employeeType: Manager
+homePhone: +1 408 579-1212
+initials: D. D.
+mobile: +1 206 258-1611
+pager: +1 408 912-1506
+manager: cn=Jemie Varia
+secretary: cn=Cornelle Saito
+roomNumber: 8163
+
+dn: cn=Karlie Puchala, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Karlie Puchala
+sn: Puchala
+description: This is Karlie Puchala's description
+facsimileTelephoneNumber: +1 818 255-2304
+l: Cupertino
+ou: Management
+postalAddress: example$Management$Dept # 293
+telephoneNumber: +1 71 137-3502
+title: Senior Management President
+userPassword: alahcuPeil
+uid: Karlie_Puchala
+givenName: Karlie
+mail: Karlie_Puchala@example.com
+carLicense: FKN3WCH
+departmentNumber: 6877
+employeeType: Temp
+homePhone: +1 408 191-5389
+initials: K. P.
+mobile: +1 804 796-2391
+pager: +1 804 330-4034
+manager: cn=Darell Yeo
+secretary: cn=Twiggy Lumsden
+roomNumber: 6693
+
+dn: cn=Shir Steinhart, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Shir Steinhart
+sn: Steinhart
+description: This is Shir Steinhart's description
+facsimileTelephoneNumber: +1 415 441-3047
+l: Menlo Park
+ou: Administrative
+postalAddress: example$Administrative$Dept # 348
+telephoneNumber: +1 408 169-5447
+title: Master Administrative Accountant
+userPassword: trahnietSr
+uid: Shir_Steinhart
+givenName: Shir
+mail: Shir_Steinhart@example.com
+carLicense: A3YI0EV
+departmentNumber: 5362
+employeeType: Manager
+homePhone: +1 804 374-7051
+initials: S. S.
+mobile: +1 71 598-8269
+pager: +1 415 965-2419
+manager: cn=Swact Palczuk
+secretary: cn=Athena Gronwall
+roomNumber: 4292
+
+dn: cn=Gabrila Moyce, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Gabrila Moyce
+sn: Moyce
+description: This is Gabrila Moyce's description
+facsimileTelephoneNumber: +1 408 645-6645
+l: San Francisco
+ou: Accounting
+postalAddress: example$Accounting$Dept # 903
+telephoneNumber: +1 206 167-8766
+title: Master Accounting Writer
+userPassword: ecyoMalirb
+uid: Gabrila_Moyce
+givenName: Gabrila
+mail: Gabrila_Moyce@example.com
+carLicense: CFEYKKJ
+departmentNumber: 9923
+employeeType: Normal
+homePhone: +1 213 986-3628
+initials: G. M.
+mobile: +1 213 235-8211
+pager: +1 408 611-7139
+manager: cn=Priscella Barnhill
+secretary: cn=Joshi Harsham
+roomNumber: 2020
+
+dn: cn=Kyle Horak, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kyle Horak
+sn: Horak
+description: This is Kyle Horak's description
+facsimileTelephoneNumber: +1 510 730-8450
+l: San Mateo
+ou: Peons
+postalAddress: example$Peons$Dept # 520
+telephoneNumber: +1 804 647-7251
+title: Senior Peons Madonna
+userPassword: karoHelyK
+uid: Kyle_Horak
+givenName: Kyle
+mail: Kyle_Horak@example.com
+carLicense: MF80EPP
+departmentNumber: 3616
+employeeType: Employee
+homePhone: +1 408 533-1915
+initials: K. H.
+mobile: +1 303 734-7002
+pager: +1 818 491-2294
+manager: cn=Jean-Pierre Glasa
+secretary: cn=Cory Sherow
+roomNumber: 5023
+
+dn: cn=Diena Sasore, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Diena Sasore
+sn: Sasore
+description: This is Diena Sasore's description
+facsimileTelephoneNumber: +1 804 861-8442
+l: Armonk
+ou: Accounting
+postalAddress: example$Accounting$Dept # 476
+telephoneNumber: +1 71 280-8828
+title: Junior Accounting President
+userPassword: erosaSanei
+uid: Diena_Sasore
+givenName: Diena
+mail: Diena_Sasore@example.com
+carLicense: 9KCS9VP
+departmentNumber: 4238
+employeeType: Normal
+homePhone: +1 303 915-7053
+initials: D. S.
+mobile: +1 71 564-4545
+pager: +1 71 232-8994
+manager: cn=Alia Ladouceur
+secretary: cn=Suk-Yin Emmons
+roomNumber: 1667
+
+dn: cn=Nadine Terranova, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Nadine Terranova
+sn: Terranova
+description: This is Nadine Terranova's description
+facsimileTelephoneNumber: +1 408 560-2327
+l: Alameda
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 375
+telephoneNumber: +1 818 211-5277
+title: Senior Human Resources Accountant
+userPassword: avonarreTe
+uid: Nadine_Terranova
+givenName: Nadine
+mail: Nadine_Terranova@example.com
+carLicense: FQGXMUQ
+departmentNumber: 997
+employeeType: Manager
+homePhone: +1 213 967-8604
+initials: N. T.
+mobile: +1 415 685-3299
+pager: +1 408 833-5430
+manager: cn=Gerti Lavigne
+secretary: cn=Lois Senten
+roomNumber: 9709
+
+dn: cn=Shayna Samac, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Shayna Samac
+sn: Samac
+description: This is Shayna Samac's description
+facsimileTelephoneNumber: +1 804 259-8033
+l: Fremont
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 443
+telephoneNumber: +1 206 690-7507
+title: Supreme Janitorial Architect
+userPassword: camaSanyah
+uid: Shayna_Samac
+givenName: Shayna
+mail: Shayna_Samac@example.com
+carLicense: MIEQWHF
+departmentNumber: 2494
+employeeType: Normal
+homePhone: +1 818 520-9204
+initials: S. S.
+mobile: +1 71 253-6155
+pager: +1 213 387-5474
+manager: cn=Gwynne Alzofon
+secretary: cn=Rozalia Shew
+roomNumber: 8940
+
+dn: cn=Lisa Cohea, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Lisa Cohea
+sn: Cohea
+description: This is Lisa Cohea's description
+facsimileTelephoneNumber: +1 804 966-8051
+l: Fremont
+ou: Planning
+postalAddress: example$Planning$Dept # 83
+telephoneNumber: +1 415 603-3466
+title: Chief Planning Janitor
+userPassword: aehoCasiL
+uid: Lisa_Cohea
+givenName: Lisa
+mail: Lisa_Cohea@example.com
+carLicense: E801YYY
+departmentNumber: 4875
+employeeType: Normal
+homePhone: +1 818 585-3387
+initials: L. C.
+mobile: +1 415 260-5195
+pager: +1 510 498-2437
+manager: cn=Huib Harris
+secretary: cn=Fei Petrescu
+roomNumber: 3870
+
+dn: cn=Isadora Iyer, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Isadora Iyer
+sn: Iyer
+description: This is Isadora Iyer's description
+facsimileTelephoneNumber: +1 804 256-3308
+l: San Francisco
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 103
+telephoneNumber: +1 415 974-7113
+title: Junior Janitorial Madonna
+userPassword: reyIarodas
+uid: Isadora_Iyer
+givenName: Isadora
+mail: Isadora_Iyer@example.com
+carLicense: EC8KKXC
+departmentNumber: 5124
+employeeType: Normal
+homePhone: +1 415 739-9864
+initials: I. I.
+mobile: +1 213 322-6643
+pager: +1 510 234-5627
+manager: cn=Coop Cuu
+secretary: cn=Esmaria Scarffe
+roomNumber: 2485
+
+dn: cn=Daveen Gawargy, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Daveen Gawargy
+sn: Gawargy
+description: This is Daveen Gawargy's description
+facsimileTelephoneNumber: +1 510 471-7023
+l: San Jose
+ou: Accounting
+postalAddress: example$Accounting$Dept # 898
+telephoneNumber: +1 71 574-8452
+title: Senior Accounting Sales Rep
+userPassword: ygrawaGnee
+uid: Daveen_Gawargy
+givenName: Daveen
+mail: Daveen_Gawargy@example.com
+carLicense: MKM7FFR
+departmentNumber: 9162
+employeeType: Contract
+homePhone: +1 71 769-4150
+initials: D. G.
+mobile: +1 804 137-1538
+pager: +1 303 280-2981
+manager: cn=Carine Shukster
+secretary: cn=Alexina Olinyk
+roomNumber: 847
+
+dn: cn=Joellen Goodman, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Joellen Goodman
+sn: Goodman
+description: This is Joellen Goodman's description
+facsimileTelephoneNumber: +1 206 231-2887
+l: Menlo Park
+ou: Management
+postalAddress: example$Management$Dept # 938
+telephoneNumber: +1 71 651-4278
+title: Chief Management Grunt
+userPassword: namdooGnel
+uid: Joellen_Goodman
+givenName: Joellen
+mail: Joellen_Goodman@example.com
+carLicense: 86WSOEC
+departmentNumber: 9611
+employeeType: Contract
+homePhone: +1 818 874-3167
+initials: J. G.
+mobile: +1 206 211-5457
+pager: +1 71 602-6109
+manager: cn=Terrijo Willmott
+secretary: cn=Zdenek Bobar
+roomNumber: 1124
+
+dn: cn=Dareen Gittins, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dareen Gittins
+sn: Gittins
+description: This is Dareen Gittins's description
+facsimileTelephoneNumber: +1 818 280-8973
+l: San Jose
+ou: Product Development
+postalAddress: example$Product Development$Dept # 360
+telephoneNumber: +1 71 395-6697
+title: Senior Product Development Vice President
+userPassword: snittiGnee
+uid: Dareen_Gittins
+givenName: Dareen
+mail: Dareen_Gittins@example.com
+carLicense: DO3CWLS
+departmentNumber: 9326
+employeeType: Manager
+homePhone: +1 408 665-8088
+initials: D. G.
+mobile: +1 303 874-2234
+pager: +1 818 703-1757
+manager: cn=Ailyn Joshi
+secretary: cn=Clement Sollee
+roomNumber: 3397
+
+dn: cn=Hermine Cronk, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Hermine Cronk
+sn: Cronk
+description: This is Hermine Cronk's description
+facsimileTelephoneNumber: +1 510 895-1959
+l: Redwood Shores
+ou: Peons
+postalAddress: example$Peons$Dept # 658
+telephoneNumber: +1 213 962-7007
+title: Chief Peons Evangelist
+userPassword: knorCenimr
+uid: Hermine_Cronk
+givenName: Hermine
+mail: Hermine_Cronk@example.com
+carLicense: PPVI97E
+departmentNumber: 8517
+employeeType: Contract
+homePhone: +1 415 150-9419
+initials: H. C.
+mobile: +1 415 788-7483
+pager: +1 213 170-4509
+manager: cn=Juan Vosu
+secretary: cn=Mary-Ellen Donaghue
+roomNumber: 4344
+
+dn: cn=Thor Jasny, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Thor Jasny
+sn: Jasny
+description: This is Thor Jasny's description
+facsimileTelephoneNumber: +1 213 354-4590
+l: Palo Alto
+ou: Product Development
+postalAddress: example$Product Development$Dept # 965
+telephoneNumber: +1 510 279-9807
+title: Chief Product Development Grunt
+userPassword: ynsaJrohT
+uid: Thor_Jasny
+givenName: Thor
+mail: Thor_Jasny@example.com
+carLicense: O2TJ41Q
+departmentNumber: 4045
+employeeType: Manager
+homePhone: +1 818 371-7625
+initials: T. J.
+mobile: +1 213 589-7902
+pager: +1 818 264-9432
+manager: cn=Minnnie Kinos
+secretary: cn=Hensley Yong
+roomNumber: 5606
+
+dn: cn=Carlie Jaworsky, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Carlie Jaworsky
+sn: Jaworsky
+description: This is Carlie Jaworsky's description
+facsimileTelephoneNumber: +1 303 728-9050
+l: Fremont
+ou: Administrative
+postalAddress: example$Administrative$Dept # 757
+telephoneNumber: +1 206 453-8543
+title: Junior Administrative Engineer
+userPassword: yksrowaJei
+uid: Carlie_Jaworsky
+givenName: Carlie
+mail: Carlie_Jaworsky@example.com
+carLicense: 741F73C
+departmentNumber: 2795
+employeeType: Manager
+homePhone: +1 206 931-8088
+initials: C. J.
+mobile: +1 206 588-3333
+pager: +1 71 780-9748
+manager: cn=Mireielle Sayer
+secretary: cn=Gursharan Limerick
+roomNumber: 8714
+
+dn: cn=Flory Galasso, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Flory Galasso
+sn: Galasso
+description: This is Flory Galasso's description
+facsimileTelephoneNumber: +1 213 342-8878
+l: Emeryville
+ou: Payroll
+postalAddress: example$Payroll$Dept # 750
+telephoneNumber: +1 71 657-9872
+title: Senior Payroll Pinhead
+userPassword: ossalaGyro
+uid: Flory_Galasso
+givenName: Flory
+mail: Flory_Galasso@example.com
+carLicense: 8W6ALH6
+departmentNumber: 3963
+employeeType: Employee
+homePhone: +1 510 384-6589
+initials: F. G.
+mobile: +1 408 503-9620
+pager: +1 510 821-5844
+manager: cn=Turus Helfrick
+secretary: cn=Anetta Van Atta
+roomNumber: 103
+
+dn: cn=Eolanda Marcheck, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Eolanda Marcheck
+sn: Marcheck
+description: This is Eolanda Marcheck's description
+facsimileTelephoneNumber: +1 213 981-4975
+l: Mountain View
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 871
+telephoneNumber: +1 408 313-6715
+title: Senior Human Resources Evangelist
+userPassword: kcehcraMad
+uid: Eolanda_Marcheck
+givenName: Eolanda
+mail: Eolanda_Marcheck@example.com
+carLicense: 2HYT26G
+departmentNumber: 8600
+employeeType: Employee
+homePhone: +1 71 900-1527
+initials: E. M.
+mobile: +1 415 127-9226
+pager: +1 303 584-4187
+manager: cn=Jinann Tahir
+secretary: cn=Graham Partin
+roomNumber: 7980
+
+dn: cn=Cindra Wyndham, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Cindra Wyndham
+sn: Wyndham
+description: This is Cindra Wyndham's description
+facsimileTelephoneNumber: +1 415 630-4285
+l: San Jose
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 398
+telephoneNumber: +1 510 998-6945
+title: Junior Product Testing Director
+userPassword: mahdnyWard
+uid: Cindra_Wyndham
+givenName: Cindra
+mail: Cindra_Wyndham@example.com
+carLicense: G7DTZLX
+departmentNumber: 4385
+employeeType: Manager
+homePhone: +1 71 109-9080
+initials: C. W.
+mobile: +1 510 529-7198
+pager: +1 206 878-3972
+manager: cn=Whitney Diersch
+secretary: cn=How-Kee Zanga
+roomNumber: 9338
+
+dn: cn=Donald Zelenka, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Donald Zelenka
+sn: Zelenka
+description: This is Donald Zelenka's description
+facsimileTelephoneNumber: +1 71 582-5331
+l: Cupertino
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 645
+telephoneNumber: +1 206 415-7851
+title: Master Janitorial Dictator
+userPassword: akneleZdla
+uid: Donald_Zelenka
+givenName: Donald
+mail: Donald_Zelenka@example.com
+carLicense: CYKBZQM
+departmentNumber: 2861
+employeeType: Temp
+homePhone: +1 415 569-9913
+initials: D. Z.
+mobile: +1 206 517-2694
+pager: +1 510 443-7549
+manager: cn=Ali Wassel
+secretary: cn=Meggy Closson
+roomNumber: 4406
+
+dn: cn=Tehchi Decker, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Tehchi Decker
+sn: Decker
+description: This is Tehchi Decker's description
+facsimileTelephoneNumber: +1 415 757-6013
+l: Palo Alto
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 804
+telephoneNumber: +1 818 697-5325
+title: Elite Product Testing Evangelist
+userPassword: rekceDihch
+uid: Tehchi_Decker
+givenName: Tehchi
+mail: Tehchi_Decker@example.com
+carLicense: 7APFZ3G
+departmentNumber: 8712
+employeeType: Normal
+homePhone: +1 71 779-4257
+initials: T. D.
+mobile: +1 303 239-7566
+pager: +1 804 201-3465
+manager: cn=Kalpit Gilmore
+secretary: cn=Marcela Lopes
+roomNumber: 6481
+
+dn: cn=Harrison Niu, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Harrison Niu
+sn: Niu
+description: This is Harrison Niu's description
+facsimileTelephoneNumber: +1 303 951-4953
+l: Redmond
+ou: Product Development
+postalAddress: example$Product Development$Dept # 150
+telephoneNumber: +1 408 237-4758
+title: Master Product Development Fellow
+userPassword: uiNnosirra
+uid: Harrison_Niu
+givenName: Harrison
+mail: Harrison_Niu@example.com
+carLicense: P10DFW6
+departmentNumber: 9212
+employeeType: Temp
+homePhone: +1 804 549-6549
+initials: H. N.
+mobile: +1 206 162-9598
+pager: +1 303 678-3059
+manager: cn=Jordanna Pufpaff
+secretary: cn=Seiko Juers
+roomNumber: 9865
+
+dn: cn=Donnie Brissette, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Donnie Brissette
+sn: Brissette
+description: This is Donnie Brissette's description
+facsimileTelephoneNumber: +1 408 596-9307
+l: Redwood Shores
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 689
+telephoneNumber: +1 206 667-8532
+title: Junior Human Resources Artist
+userPassword: ettessirBe
+uid: Donnie_Brissette
+givenName: Donnie
+mail: Donnie_Brissette@example.com
+carLicense: XJC3KQV
+departmentNumber: 1761
+employeeType: Temp
+homePhone: +1 415 452-6922
+initials: D. B.
+mobile: +1 71 958-4191
+pager: +1 303 675-4208
+manager: cn=Terrell Witkowski
+secretary: cn=Elsey Michaelson
+roomNumber: 4992
+
+dn: cn=May Gaul, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: May Gaul
+sn: Gaul
+description: This is May Gaul's description
+facsimileTelephoneNumber: +1 804 673-8690
+l: Sunnyvale
+ou: Accounting
+postalAddress: example$Accounting$Dept # 930
+telephoneNumber: +1 408 696-5756
+title: Associate Accounting Developer
+userPassword: luaGyaM
+uid: May_Gaul
+givenName: May
+mail: May_Gaul@example.com
+carLicense: 1BMCX31
+departmentNumber: 5148
+employeeType: Temp
+homePhone: +1 415 245-8979
+initials: M. G.
+mobile: +1 818 141-8493
+pager: +1 804 480-4264
+manager: cn=Fqa McMannen
+secretary: cn=Daniel Encomenderos
+roomNumber: 5933
+
+dn: cn=Kaylee Golaszewski, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kaylee Golaszewski
+sn: Golaszewski
+description: This is Kaylee Golaszewski's description
+facsimileTelephoneNumber: +1 415 527-7345
+l: Palo Alto
+ou: Management
+postalAddress: example$Management$Dept # 358
+telephoneNumber: +1 71 161-2141
+title: Junior Management Warrior
+userPassword: ikswezsalo
+uid: Kaylee_Golaszewski
+givenName: Kaylee
+mail: Kaylee_Golaszewski@example.com
+carLicense: HLN6PNH
+departmentNumber: 5533
+employeeType: Temp
+homePhone: +1 206 212-5534
+initials: K. G.
+mobile: +1 71 947-6940
+pager: +1 408 685-6889
+manager: cn=Dulcy Runkel
+secretary: cn=Duane Debord
+roomNumber: 9891
+
+dn: cn=Teodora Bjornson, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Teodora Bjornson
+sn: Bjornson
+description: This is Teodora Bjornson's description
+facsimileTelephoneNumber: +1 408 657-3540
+l: Alameda
+ou: Planning
+postalAddress: example$Planning$Dept # 401
+telephoneNumber: +1 415 533-9032
+title: Elite Planning Manager
+userPassword: nosnrojBar
+uid: Teodora_Bjornson
+givenName: Teodora
+mail: Teodora_Bjornson@example.com
+carLicense: PKTB45Y
+departmentNumber: 7265
+employeeType: Contract
+homePhone: +1 71 620-6107
+initials: T. B.
+mobile: +1 804 609-9614
+pager: +1 206 842-3624
+manager: cn=Nagaraj Barwikowski
+secretary: cn=Charo Markell
+roomNumber: 1590
+
+dn: cn=GeorgeAnn Stanulis, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: GeorgeAnn Stanulis
+sn: Stanulis
+description: This is GeorgeAnn Stanulis's description
+facsimileTelephoneNumber: +1 415 772-7968
+l: San Jose
+ou: Accounting
+postalAddress: example$Accounting$Dept # 365
+telephoneNumber: +1 804 626-8319
+title: Senior Accounting Dictator
+userPassword: silunatSnn
+uid: GeorgeAnn_Stanulis
+givenName: GeorgeAnn
+mail: GeorgeAnn_Stanulis@example.com
+carLicense: SQ5C9MI
+departmentNumber: 3229
+employeeType: Contract
+homePhone: +1 818 351-2650
+initials: G. S.
+mobile: +1 818 765-9210
+pager: +1 415 373-6213
+manager: cn=Jean-Robert Beconovich
+secretary: cn=Rosabelle Strober
+roomNumber: 1896
+
+dn: cn=Yvette Ludwig, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Yvette Ludwig
+sn: Ludwig
+description: This is Yvette Ludwig's description
+facsimileTelephoneNumber: +1 303 306-5445
+l: San Mateo
+ou: Planning
+postalAddress: example$Planning$Dept # 481
+telephoneNumber: +1 415 811-1657
+title: Senior Planning Yahoo
+userPassword: giwduLette
+uid: Yvette_Ludwig
+givenName: Yvette
+mail: Yvette_Ludwig@example.com
+carLicense: YLN7XSN
+departmentNumber: 7920
+employeeType: Normal
+homePhone: +1 206 960-4637
+initials: Y. L.
+mobile: +1 510 634-4008
+pager: +1 818 922-2134
+manager: cn=Willa Dirilten
+secretary: cn=Tresrch Veloz
+roomNumber: 3845
+
+dn: cn=Fahim Eimer, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Fahim Eimer
+sn: Eimer
+description: This is Fahim Eimer's description
+facsimileTelephoneNumber: +1 206 871-3266
+l: Sunnyvale
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 49
+telephoneNumber: +1 206 250-7419
+title: Associate Human Resources Czar
+userPassword: remiEmihaF
+uid: Fahim_Eimer
+givenName: Fahim
+mail: Fahim_Eimer@example.com
+carLicense: QU9NIPK
+departmentNumber: 9211
+employeeType: Employee
+homePhone: +1 510 923-5012
+initials: F. E.
+mobile: +1 206 206-1510
+pager: +1 71 554-8588
+manager: cn=Cindra Williamson
+secretary: cn=Reza Rickborn
+roomNumber: 1814
+
+dn: cn=Spenser Purson, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Spenser Purson
+sn: Purson
+description: This is Spenser Purson's description
+facsimileTelephoneNumber: +1 510 270-3830
+l: Cupertino
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 827
+telephoneNumber: +1 206 703-2247
+title: Associate Human Resources President
+userPassword: nosruPresn
+uid: Spenser_Purson
+givenName: Spenser
+mail: Spenser_Purson@example.com
+carLicense: NIGEIRV
+departmentNumber: 7309
+employeeType: Employee
+homePhone: +1 71 442-4522
+initials: S. P.
+mobile: +1 804 535-5046
+pager: +1 510 949-7721
+manager: cn=Alison Burdett
+secretary: cn=Nellie Jagernauth
+roomNumber: 9391
+
+dn: cn=Ertha Calva, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ertha Calva
+sn: Calva
+description: This is Ertha Calva's description
+facsimileTelephoneNumber: +1 408 521-8377
+l: Cupertino
+ou: Administrative
+postalAddress: example$Administrative$Dept # 28
+telephoneNumber: +1 804 948-5631
+title: Senior Administrative President
+userPassword: avlaCahtrE
+uid: Ertha_Calva
+givenName: Ertha
+mail: Ertha_Calva@example.com
+carLicense: KFNBO9N
+departmentNumber: 1757
+employeeType: Employee
+homePhone: +1 818 821-5081
+initials: E. C.
+mobile: +1 206 671-1674
+pager: +1 303 461-2259
+manager: cn=Cassie Wepf
+secretary: cn=Lorette Sawchuk
+roomNumber: 386
+
+dn: cn=Rickrd Integration, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rickrd Integration
+sn: Integration
+description: This is Rickrd Integration's description
+facsimileTelephoneNumber: +1 415 264-5076
+l: Sunnyvale
+ou: Management
+postalAddress: example$Management$Dept # 753
+telephoneNumber: +1 415 941-3374
+title: Associate Management Fellow
+userPassword: noitargetn
+uid: Rickrd_Integration
+givenName: Rickrd
+mail: Rickrd_Integration@example.com
+carLicense: R9T7FZC
+departmentNumber: 3644
+employeeType: Contract
+homePhone: +1 303 751-6571
+initials: R. I.
+mobile: +1 303 201-8042
+pager: +1 213 486-4874
+manager: cn=Reine Lidster
+secretary: cn=Elayne Sutter
+roomNumber: 3015
+
+dn: cn=Starlet Cervantes, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Starlet Cervantes
+sn: Cervantes
+description: This is Starlet Cervantes's description
+facsimileTelephoneNumber: +1 303 691-3116
+l: Fremont
+ou: Accounting
+postalAddress: example$Accounting$Dept # 187
+telephoneNumber: +1 71 548-4911
+title: Senior Accounting Dictator
+userPassword: setnavreCt
+uid: Starlet_Cervantes
+givenName: Starlet
+mail: Starlet_Cervantes@example.com
+carLicense: C7YVC78
+departmentNumber: 790
+employeeType: Manager
+homePhone: +1 71 898-3034
+initials: S. C.
+mobile: +1 213 121-2578
+pager: +1 206 364-9296
+manager: cn=Xiaojing Destech
+secretary: cn=Inessa Brousseau
+roomNumber: 2153
+
+dn: cn=Blanca Perkinson, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Blanca Perkinson
+sn: Perkinson
+description: This is Blanca Perkinson's description
+facsimileTelephoneNumber: +1 804 366-1482
+l: San Mateo
+ou: Payroll
+postalAddress: example$Payroll$Dept # 811
+telephoneNumber: +1 818 448-6362
+title: Senior Payroll Technician
+userPassword: nosnikrePa
+uid: Blanca_Perkinson
+givenName: Blanca
+mail: Blanca_Perkinson@example.com
+carLicense: P3C60RZ
+departmentNumber: 4444
+employeeType: Normal
+homePhone: +1 408 517-8796
+initials: B. P.
+mobile: +1 213 603-6343
+pager: +1 71 475-5851
+manager: cn=Valerie Pearse
+secretary: cn=Oliy Yvon
+roomNumber: 9752
+
+dn: cn=Heinz Ibarra, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Heinz Ibarra
+sn: Ibarra
+description: This is Heinz Ibarra's description
+facsimileTelephoneNumber: +1 415 905-1594
+l: Palo Alto
+ou: Administrative
+postalAddress: example$Administrative$Dept # 784
+telephoneNumber: +1 206 459-4529
+title: Senior Administrative Dictator
+userPassword: arrabIznie
+uid: Heinz_Ibarra
+givenName: Heinz
+mail: Heinz_Ibarra@example.com
+carLicense: O08CDMU
+departmentNumber: 2885
+employeeType: Contract
+homePhone: +1 303 816-5503
+initials: H. I.
+mobile: +1 510 901-7355
+pager: +1 206 652-3929
+manager: cn=Maressa Janseen
+secretary: cn=Yihban Teran
+roomNumber: 4463
+
+dn: cn=Sieber Hilaire, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sieber Hilaire
+sn: Hilaire
+description: This is Sieber Hilaire's description
+facsimileTelephoneNumber: +1 415 606-3740
+l: Milpitas
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 769
+telephoneNumber: +1 408 531-5819
+title: Junior Product Testing Manager
+userPassword: erialiHreb
+uid: Sieber_Hilaire
+givenName: Sieber
+mail: Sieber_Hilaire@example.com
+carLicense: MUZ3KRL
+departmentNumber: 7832
+employeeType: Temp
+homePhone: +1 415 524-8424
+initials: S. H.
+mobile: +1 408 960-3501
+pager: +1 213 125-6590
+manager: cn=Norrie Bedi
+secretary: cn=Louis-Philippe Artspssa
+roomNumber: 1313
+
+dn: cn=Alana Depelteau, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Alana Depelteau
+sn: Depelteau
+description: This is Alana Depelteau's description
+facsimileTelephoneNumber: +1 303 767-5215
+l: San Mateo
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 431
+telephoneNumber: +1 206 516-5014
+title: Junior Human Resources Architect
+userPassword: uaetlepeDa
+uid: Alana_Depelteau
+givenName: Alana
+mail: Alana_Depelteau@example.com
+carLicense: UNQUU8E
+departmentNumber: 9573
+employeeType: Manager
+homePhone: +1 510 171-5036
+initials: A. D.
+mobile: +1 303 108-1591
+pager: +1 408 492-6874
+manager: cn=Loren Drane
+secretary: cn=Gee-Meng Pelz
+roomNumber: 8281
+
+dn: cn=Viole Wun, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Viole Wun
+sn: Wun
+description: This is Viole Wun's description
+facsimileTelephoneNumber: +1 71 734-2397
+l: Cupertino
+ou: Planning
+postalAddress: example$Planning$Dept # 462
+telephoneNumber: +1 408 178-6082
+title: Supreme Planning Fellow
+userPassword: nuWeloiV
+uid: Viole_Wun
+givenName: Viole
+mail: Viole_Wun@example.com
+carLicense: ZI7BP5X
+departmentNumber: 7758
+employeeType: Normal
+homePhone: +1 804 952-4514
+initials: V. W.
+mobile: +1 213 115-1234
+pager: +1 818 630-4157
+manager: cn=Naveen Gooley
+secretary: cn=Seamus Kruziak
+roomNumber: 2010
+
+dn: cn=Melisande Chanchlani, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Melisande Chanchlani
+sn: Chanchlani
+description: This is Melisande Chanchlani's description
+facsimileTelephoneNumber: +1 510 721-3725
+l: Redwood Shores
+ou: Accounting
+postalAddress: example$Accounting$Dept # 302
+telephoneNumber: +1 303 412-9372
+title: Elite Accounting Punk
+userPassword: inalhcnahC
+uid: Melisande_Chanchlani
+givenName: Melisande
+mail: Melisande_Chanchlani@example.com
+carLicense: FJELVY9
+departmentNumber: 1063
+employeeType: Temp
+homePhone: +1 818 751-6755
+initials: M. C.
+mobile: +1 303 611-6743
+pager: +1 818 606-2865
+manager: cn=Bud Andros
+secretary: cn=Ronna Rothwell
+roomNumber: 9661
+
+dn: cn=Nha Heyward, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Nha Heyward
+sn: Heyward
+description: This is Nha Heyward's description
+facsimileTelephoneNumber: +1 71 578-9078
+l: Emeryville
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 384
+telephoneNumber: +1 510 875-8611
+title: Junior Human Resources Yahoo
+userPassword: drawyeHahN
+uid: Nha_Heyward
+givenName: Nha
+mail: Nha_Heyward@example.com
+carLicense: L56T36P
+departmentNumber: 5119
+employeeType: Manager
+homePhone: +1 818 948-7798
+initials: N. H.
+mobile: +1 303 717-9325
+pager: +1 415 649-6984
+manager: cn=Bria O'Meara
+secretary: cn=Ida Hamid
+roomNumber: 3891
+
+dn: cn=Ragu Dearaujo, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Ragu Dearaujo
+sn: Dearaujo
+description: This is Ragu Dearaujo's description
+facsimileTelephoneNumber: +1 303 769-4641
+l: Menlo Park
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 267
+telephoneNumber: +1 804 809-1583
+title: Master Janitorial Warrior
+userPassword: ojuaraeDug
+uid: Ragu_Dearaujo
+givenName: Ragu
+mail: Ragu_Dearaujo@example.com
+carLicense: XXYHCRX
+departmentNumber: 4647
+employeeType: Contract
+homePhone: +1 408 698-4340
+initials: R. D.
+mobile: +1 213 499-7835
+pager: +1 206 382-1975
+manager: cn=Ehab Bergeson
+secretary: cn=Hot Lazarou
+roomNumber: 3535
+
+dn: cn=Rosemonde Eales, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Rosemonde Eales
+sn: Eales
+description: This is Rosemonde Eales's description
+facsimileTelephoneNumber: +1 510 556-9306
+l: Santa Clara
+ou: Management
+postalAddress: example$Management$Dept # 925
+telephoneNumber: +1 408 841-9033
+title: Elite Management Developer
+userPassword: selaEednom
+uid: Rosemonde_Eales
+givenName: Rosemonde
+mail: Rosemonde_Eales@example.com
+carLicense: PI989TL
+departmentNumber: 1473
+employeeType: Normal
+homePhone: +1 415 813-9480
+initials: R. E.
+mobile: +1 206 402-3725
+pager: +1 510 160-2175
+manager: cn=Courtenay Mansbridge
+secretary: cn=Sallie Hagan
+roomNumber: 3922
+
+dn: cn=Dieuwertje Akita, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dieuwertje Akita
+sn: Akita
+description: This is Dieuwertje Akita's description
+facsimileTelephoneNumber: +1 510 197-7938
+l: Fremont
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 192
+telephoneNumber: +1 71 432-2992
+title: Associate Product Testing Mascot
+userPassword: atikAejtre
+uid: Dieuwertje_Akita
+givenName: Dieuwertje
+mail: Dieuwertje_Akita@example.com
+carLicense: IE2ZCM2
+departmentNumber: 4084
+employeeType: Normal
+homePhone: +1 213 733-4676
+initials: D. A.
+mobile: +1 408 425-8337
+pager: +1 804 877-3834
+manager: cn=Clevon Geyer
+secretary: cn=Weitzel Bhoday
+roomNumber: 8319
+
+dn: cn=Kellyann Horton, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kellyann Horton
+sn: Horton
+description: This is Kellyann Horton's description
+facsimileTelephoneNumber: +1 415 416-3406
+l: San Mateo
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 670
+telephoneNumber: +1 818 886-5953
+title: Junior Human Resources Developer
+userPassword: notroHnnay
+uid: Kellyann_Horton
+givenName: Kellyann
+mail: Kellyann_Horton@example.com
+carLicense: 6ENFXVA
+departmentNumber: 7279
+employeeType: Contract
+homePhone: +1 213 531-8248
+initials: K. H.
+mobile: +1 510 744-5231
+pager: +1 415 231-8672
+manager: cn=Jacintha Crowe
+secretary: cn=Melosa Kirley
+roomNumber: 6502
+
+dn: cn=Truman Nilson, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Truman Nilson
+sn: Nilson
+description: This is Truman Nilson's description
+facsimileTelephoneNumber: +1 206 661-2468
+l: Redmond
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 733
+telephoneNumber: +1 71 896-8519
+title: Associate Product Testing Pinhead
+userPassword: nosliNnamu
+uid: Truman_Nilson
+givenName: Truman
+mail: Truman_Nilson@example.com
+carLicense: JC4C6XQ
+departmentNumber: 1844
+employeeType: Temp
+homePhone: +1 71 146-6637
+initials: T. N.
+mobile: +1 206 966-6795
+pager: +1 804 926-8955
+manager: cn=Eliot Paye
+secretary: cn=Elissa Steinhart
+roomNumber: 4814
+
+dn: cn=Blondie Kember, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Blondie Kember
+sn: Kember
+description: This is Blondie Kember's description
+facsimileTelephoneNumber: +1 415 399-5093
+l: Sunnyvale
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 54
+telephoneNumber: +1 804 111-2699
+title: Supreme Janitorial Manager
+userPassword: rebmeKeidn
+uid: Blondie_Kember
+givenName: Blondie
+mail: Blondie_Kember@example.com
+carLicense: QE7XZMC
+departmentNumber: 6003
+employeeType: Temp
+homePhone: +1 804 146-3460
+initials: B. K.
+mobile: +1 818 487-4717
+pager: +1 213 296-9942
+manager: cn=Anabelle Laniel
+secretary: cn=Herbert Wakabayashi
+roomNumber: 1107
+
+dn: cn=Elladine Bose, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Elladine Bose
+sn: Bose
+description: This is Elladine Bose's description
+facsimileTelephoneNumber: +1 71 289-5853
+l: Sunnyvale
+ou: Peons
+postalAddress: example$Peons$Dept # 947
+telephoneNumber: +1 303 971-6203
+title: Junior Peons Madonna
+userPassword: esoBenidal
+uid: Elladine_Bose
+givenName: Elladine
+mail: Elladine_Bose@example.com
+carLicense: QIHW6TY
+departmentNumber: 9064
+employeeType: Temp
+homePhone: +1 206 764-4911
+initials: E. B.
+mobile: +1 206 848-4223
+pager: +1 804 785-8358
+manager: cn=Canute Ryder
+secretary: cn=Hojjat Debassige
+roomNumber: 9444
+
+dn: cn=Demetria Deibert, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Demetria Deibert
+sn: Deibert
+description: This is Demetria Deibert's description
+facsimileTelephoneNumber: +1 71 704-5894
+l: San Mateo
+ou: Product Development
+postalAddress: example$Product Development$Dept # 767
+telephoneNumber: +1 415 846-3651
+title: Elite Product Development Manager
+userPassword: trebieDair
+uid: Demetria_Deibert
+givenName: Demetria
+mail: Demetria_Deibert@example.com
+carLicense: L90IKCH
+departmentNumber: 2866
+employeeType: Contract
+homePhone: +1 804 915-6346
+initials: D. D.
+mobile: +1 303 871-8249
+pager: +1 818 791-1975
+manager: cn=Meriel Dmuchalsky
+secretary: cn=Inger Dba
+roomNumber: 6308
+
+dn: cn=Chie Sallee, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Chie Sallee
+sn: Sallee
+description: This is Chie Sallee's description
+facsimileTelephoneNumber: +1 415 492-2678
+l: Cambridge
+ou: Payroll
+postalAddress: example$Payroll$Dept # 370
+telephoneNumber: +1 818 704-2633
+title: Junior Payroll Director
+userPassword: eellaSeihC
+uid: Chie_Sallee
+givenName: Chie
+mail: Chie_Sallee@example.com
+carLicense: G6MUNJZ
+departmentNumber: 7095
+employeeType: Normal
+homePhone: +1 71 113-5855
+initials: C. S.
+mobile: +1 213 701-4279
+pager: +1 818 166-2016
+manager: cn=Lalitha Archambault
+secretary: cn=Pasiedb Bazerghi
+roomNumber: 4000
+
+dn: cn=Scot Fralick, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Scot Fralick
+sn: Fralick
+description: This is Scot Fralick's description
+facsimileTelephoneNumber: +1 303 585-1223
+l: Santa Clara
+ou: Management
+postalAddress: example$Management$Dept # 530
+telephoneNumber: +1 415 442-4717
+title: Associate Management President
+userPassword: kcilarFtoc
+uid: Scot_Fralick
+givenName: Scot
+mail: Scot_Fralick@example.com
+carLicense: YH6BO4M
+departmentNumber: 6096
+employeeType: Employee
+homePhone: +1 303 598-4845
+initials: S. F.
+mobile: +1 818 165-3943
+pager: +1 510 626-8553
+manager: cn=Pde Fulk
+secretary: cn=Lowell Tweddle
+roomNumber: 6318
+
+dn: cn=Monroe Christensen, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Monroe Christensen
+sn: Christensen
+description: This is Monroe Christensen's description
+facsimileTelephoneNumber: +1 206 356-8279
+l: Mountain View
+ou: Payroll
+postalAddress: example$Payroll$Dept # 148
+telephoneNumber: +1 206 912-6866
+title: Supreme Payroll Grunt
+userPassword: nesnetsirh
+uid: Monroe_Christensen
+givenName: Monroe
+mail: Monroe_Christensen@example.com
+carLicense: R6O0NAQ
+departmentNumber: 8407
+employeeType: Contract
+homePhone: +1 213 766-9533
+initials: M. C.
+mobile: +1 303 556-6768
+pager: +1 510 286-2604
+manager: cn=Michaeline Health-Safety
+secretary: cn=Jobye Brombal
+roomNumber: 5351
+
+dn: cn=Kirsteni Finckler, ou=Janitorial, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Kirsteni Finckler
+sn: Finckler
+description: This is Kirsteni Finckler's description
+facsimileTelephoneNumber: +1 510 868-2823
+l: Orem
+ou: Janitorial
+postalAddress: example$Janitorial$Dept # 47
+telephoneNumber: +1 818 629-5584
+title: Elite Janitorial Janitor
+userPassword: relkcniFin
+uid: Kirsteni_Finckler
+givenName: Kirsteni
+mail: Kirsteni_Finckler@example.com
+carLicense: V0YDBED
+departmentNumber: 3159
+employeeType: Contract
+homePhone: +1 206 678-7419
+initials: K. F.
+mobile: +1 818 399-1514
+pager: +1 804 253-1019
+manager: cn=Jordan Rondeau
+secretary: cn=Jasver Cherrier
+roomNumber: 3602
+
+dn: cn=Audre Berenbach, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Audre Berenbach
+sn: Berenbach
+description: This is Audre Berenbach's description
+facsimileTelephoneNumber: +1 804 256-5474
+l: Emeryville
+ou: Product Development
+postalAddress: example$Product Development$Dept # 871
+telephoneNumber: +1 818 992-8711
+title: Associate Product Development Fellow
+userPassword: hcabnereBe
+uid: Audre_Berenbach
+givenName: Audre
+mail: Audre_Berenbach@example.com
+carLicense: 35TW1J9
+departmentNumber: 2112
+employeeType: Normal
+homePhone: +1 510 334-5649
+initials: A. B.
+mobile: +1 415 565-1897
+pager: +1 818 845-1036
+manager: cn=Eolande Colpitts
+secretary: cn=Garth Tse
+roomNumber: 2863
+
+dn: cn=Anabelle Ludwig, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Anabelle Ludwig
+sn: Ludwig
+description: This is Anabelle Ludwig's description
+facsimileTelephoneNumber: +1 408 855-8095
+l: Cupertino
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 26
+telephoneNumber: +1 408 822-2622
+title: Master Human Resources Fellow
+userPassword: giwduLelle
+uid: Anabelle_Ludwig
+givenName: Anabelle
+mail: Anabelle_Ludwig@example.com
+carLicense: BMWFISB
+departmentNumber: 8253
+employeeType: Temp
+homePhone: +1 71 117-2604
+initials: A. L.
+mobile: +1 408 915-1480
+pager: +1 818 457-5763
+manager: cn=Trev Zug
+secretary: cn=Cen Subasinghe
+roomNumber: 9477
+
+dn: cn=Florri Vandenheede, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Florri Vandenheede
+sn: Vandenheede
+description: This is Florri Vandenheede's description
+facsimileTelephoneNumber: +1 408 149-8781
+l: Menlo Park
+ou: Product Development
+postalAddress: example$Product Development$Dept # 167
+telephoneNumber: +1 408 952-1219
+title: Chief Product Development Consultant
+userPassword: edeehnedna
+uid: Florri_Vandenheede
+givenName: Florri
+mail: Florri_Vandenheede@example.com
+carLicense: L3JXPX6
+departmentNumber: 9177
+employeeType: Contract
+homePhone: +1 804 503-2451
+initials: F. V.
+mobile: +1 804 866-9787
+pager: +1 818 747-7775
+manager: cn=Esmail Buckalew
+secretary: cn=Snehal Jolicoeur
+roomNumber: 2265
+
+dn: cn=Sileas Gallo, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sileas Gallo
+sn: Gallo
+description: This is Sileas Gallo's description
+facsimileTelephoneNumber: +1 408 574-5500
+l: Mountain View
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 926
+telephoneNumber: +1 71 907-6499
+title: Elite Product Testing Grunt
+userPassword: ollaGsaeli
+uid: Sileas_Gallo
+givenName: Sileas
+mail: Sileas_Gallo@example.com
+carLicense: HD1VVJI
+departmentNumber: 4101
+employeeType: Temp
+homePhone: +1 818 814-1046
+initials: S. G.
+mobile: +1 303 440-5276
+pager: +1 510 369-1491
+manager: cn=Fausto Clipperton
+secretary: cn=Sylvie Zieber
+roomNumber: 2886
+
+dn: cn=Dinker Vlad, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Dinker Vlad
+sn: Vlad
+description: This is Dinker Vlad's description
+facsimileTelephoneNumber: +1 415 997-1200
+l: Sunnyvale
+ou: Accounting
+postalAddress: example$Accounting$Dept # 383
+telephoneNumber: +1 510 270-9293
+title: Chief Accounting Grunt
+userPassword: dalVrekniD
+uid: Dinker_Vlad
+givenName: Dinker
+mail: Dinker_Vlad@example.com
+carLicense: JIEK9OZ
+departmentNumber: 3320
+employeeType: Employee
+homePhone: +1 213 295-5225
+initials: D. V.
+mobile: +1 213 636-6002
+pager: +1 206 198-2064
+manager: cn=Haroon Roldan
+secretary: cn=Chand Jasrotia
+roomNumber: 6160
+
+dn: cn=Norvie Chomik, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Norvie Chomik
+sn: Chomik
+description: This is Norvie Chomik's description
+facsimileTelephoneNumber: +1 213 408-4683
+l: Fremont
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 514
+telephoneNumber: +1 206 338-6639
+title: Chief Human Resources Visionary
+userPassword: kimohCeivr
+uid: Norvie_Chomik
+givenName: Norvie
+mail: Norvie_Chomik@example.com
+carLicense: 4M0417D
+departmentNumber: 4617
+employeeType: Employee
+homePhone: +1 804 131-5356
+initials: N. C.
+mobile: +1 415 910-2510
+pager: +1 213 936-9730
+manager: cn=Herbie Andrassy
+secretary: cn=Virgina Brungardt
+roomNumber: 4408
+
+dn: cn=Eran Preston-Thomas, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Eran Preston-Thomas
+sn: Preston-Thomas
+description: This is Eran Preston-Thomas's description
+facsimileTelephoneNumber: +1 415 805-9017
+l: Orem
+ou: Management
+postalAddress: example$Management$Dept # 70
+telephoneNumber: +1 510 490-7704
+title: Chief Management Engineer
+userPassword: samohT-not
+uid: Eran_Preston-Thomas
+givenName: Eran
+mail: Eran_Preston-Thomas@example.com
+carLicense: 8PCVG00
+departmentNumber: 4339
+employeeType: Temp
+homePhone: +1 303 834-3076
+initials: E. P.
+mobile: +1 804 585-1018
+pager: +1 818 871-3707
+manager: cn=Rafaela Macquistan
+secretary: cn=Mufi Lesourd
+roomNumber: 7079
+
+dn: cn=Romulus Giese, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Romulus Giese
+sn: Giese
+description: This is Romulus Giese's description
+facsimileTelephoneNumber: +1 206 196-4952
+l: Redwood Shores
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 188
+telephoneNumber: +1 415 114-5444
+title: Junior Human Resources Sales Rep
+userPassword: eseiGsulum
+uid: Romulus_Giese
+givenName: Romulus
+mail: Romulus_Giese@example.com
+carLicense: 7CMJBHX
+departmentNumber: 8930
+employeeType: Contract
+homePhone: +1 510 460-2913
+initials: R. G.
+mobile: +1 415 107-8612
+pager: +1 303 819-4889
+manager: cn=Di Majumdar
+secretary: cn=Gerry Overton
+roomNumber: 6740
+
+dn: cn=Randal Twarog, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Randal Twarog
+sn: Twarog
+description: This is Randal Twarog's description
+facsimileTelephoneNumber: +1 804 754-9609
+l: Alameda
+ou: Administrative
+postalAddress: example$Administrative$Dept # 952
+telephoneNumber: +1 206 956-5103
+title: Master Administrative Dictator
+userPassword: gorawTladn
+uid: Randal_Twarog
+givenName: Randal
+mail: Randal_Twarog@example.com
+carLicense: RFRBHCJ
+departmentNumber: 0
+employeeType: Temp
+homePhone: +1 213 486-7709
+initials: R. T.
+mobile: +1 213 567-5657
+pager: +1 510 314-1948
+manager: cn=Patti Goupil
+secretary: cn=Sanchez Jachym
+roomNumber: 2599
+
+dn: cn=Fritz Claise, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Fritz Claise
+sn: Claise
+description: This is Fritz Claise's description
+facsimileTelephoneNumber: +1 510 531-7295
+l: San Mateo
+ou: Peons
+postalAddress: example$Peons$Dept # 298
+telephoneNumber: +1 804 683-2606
+title: Elite Peons Pinhead
+userPassword: esialCztir
+uid: Fritz_Claise
+givenName: Fritz
+mail: Fritz_Claise@example.com
+carLicense: U1PMLLY
+departmentNumber: 5506
+employeeType: Temp
+homePhone: +1 408 907-5910
+initials: F. C.
+mobile: +1 408 624-3588
+pager: +1 71 914-9489
+manager: cn=Pierrick Cline
+secretary: cn=Herve Bonner
+roomNumber: 3200
+
+dn: cn=Djenana Dumouchelle, ou=Payroll, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Djenana Dumouchelle
+sn: Dumouchelle
+description: This is Djenana Dumouchelle's description
+facsimileTelephoneNumber: +1 804 870-4350
+l: Redmond
+ou: Payroll
+postalAddress: example$Payroll$Dept # 960
+telephoneNumber: +1 408 482-7997
+title: Supreme Payroll Vice President
+userPassword: ellehcuomu
+uid: Djenana_Dumouchelle
+givenName: Djenana
+mail: Djenana_Dumouchelle@example.com
+carLicense: SCUQAST
+departmentNumber: 9280
+employeeType: Employee
+homePhone: +1 415 992-1209
+initials: D. D.
+mobile: +1 213 117-4250
+pager: +1 415 699-8888
+manager: cn=Delores Eyers
+secretary: cn=Delly Kok
+roomNumber: 7452
+
+dn: cn=La Valko, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: La Valko
+sn: Valko
+description: This is La Valko's description
+facsimileTelephoneNumber: +1 818 542-1967
+l: Redmond
+ou: Peons
+postalAddress: example$Peons$Dept # 132
+telephoneNumber: +1 415 193-4715
+title: Supreme Peons Sales Rep
+userPassword: oklaVaL
+uid: La_Valko
+givenName: La
+mail: La_Valko@example.com
+carLicense: GQU0EB6
+departmentNumber: 1170
+employeeType: Contract
+homePhone: +1 408 637-4126
+initials: L. V.
+mobile: +1 415 141-6794
+pager: +1 415 266-6615
+manager: cn=Wai-Man Arnauld
+secretary: cn=Zola Karhuniemi
+roomNumber: 922
+
+dn: cn=Clary Mand, ou=Administrative, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Clary Mand
+sn: Mand
+description: This is Clary Mand's description
+facsimileTelephoneNumber: +1 408 928-6670
+l: Alameda
+ou: Administrative
+postalAddress: example$Administrative$Dept # 55
+telephoneNumber: +1 804 111-7612
+title: Senior Administrative Stooge
+userPassword: dnaMyralC
+uid: Clary_Mand
+givenName: Clary
+mail: Clary_Mand@example.com
+carLicense: 2AVIDV3
+departmentNumber: 2566
+employeeType: Temp
+homePhone: +1 213 446-5803
+initials: C. M.
+mobile: +1 71 687-3763
+pager: +1 206 452-2059
+manager: cn=Ainslee Hanlan
+secretary: cn=Mallik MacArthur
+roomNumber: 8363
+
+dn: cn=Freida Gann, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Freida Gann
+sn: Gann
+description: This is Freida Gann's description
+facsimileTelephoneNumber: +1 415 795-8798
+l: San Jose
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 879
+telephoneNumber: +1 213 622-1643
+title: Master Product Testing Assistant
+userPassword: nnaGadierF
+uid: Freida_Gann
+givenName: Freida
+mail: Freida_Gann@example.com
+carLicense: FGJFOA8
+departmentNumber: 7796
+employeeType: Contract
+homePhone: +1 71 782-6115
+initials: F. G.
+mobile: +1 408 899-5110
+pager: +1 804 707-1253
+manager: cn=Nat Calkins
+secretary: cn=Atmane Guenette
+roomNumber: 4156
+
+dn: cn=Sharyl Wefers, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Sharyl Wefers
+sn: Wefers
+description: This is Sharyl Wefers's description
+facsimileTelephoneNumber: +1 408 943-3670
+l: Milpitas
+ou: Peons
+postalAddress: example$Peons$Dept # 451
+telephoneNumber: +1 415 853-8475
+title: Supreme Peons Yahoo
+userPassword: srefeWlyra
+uid: Sharyl_Wefers
+givenName: Sharyl
+mail: Sharyl_Wefers@example.com
+carLicense: BHQAXH8
+departmentNumber: 2071
+employeeType: Contract
+homePhone: +1 303 509-4625
+initials: S. W.
+mobile: +1 71 854-7951
+pager: +1 303 947-1628
+manager: cn=Fernanda Pachek
+secretary: cn=Katie Nicol
+roomNumber: 7430
+
+dn: cn=Riki Lahteenmaa, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Riki Lahteenmaa
+sn: Lahteenmaa
+description: This is Riki Lahteenmaa's description
+facsimileTelephoneNumber: +1 303 683-7590
+l: Fremont
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 419
+telephoneNumber: +1 408 392-8634
+title: Junior Product Testing Consultant
+userPassword: aamneethaL
+uid: Riki_Lahteenmaa
+givenName: Riki
+mail: Riki_Lahteenmaa@example.com
+carLicense: LRUSPU7
+departmentNumber: 2691
+employeeType: Normal
+homePhone: +1 303 890-6562
+initials: R. L.
+mobile: +1 206 112-6791
+pager: +1 804 284-2760
+manager: cn=Tawauna Masapati
+secretary: cn=Leddy Majury
+roomNumber: 4146
+
+dn: cn=Damon Leeson, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Damon Leeson
+sn: Leeson
+description: This is Damon Leeson's description
+facsimileTelephoneNumber: +1 818 446-8649
+l: Milpitas
+ou: Product Development
+postalAddress: example$Product Development$Dept # 709
+telephoneNumber: +1 303 822-1700
+title: Chief Product Development Yahoo
+userPassword: noseeLnoma
+uid: Damon_Leeson
+givenName: Damon
+mail: Damon_Leeson@example.com
+carLicense: NJN9C3A
+departmentNumber: 6908
+employeeType: Normal
+homePhone: +1 415 206-2910
+initials: D. L.
+mobile: +1 206 460-9897
+pager: +1 71 652-6585
+manager: cn=Sydel Bhardwaj
+secretary: cn=Nikki Kiel
+roomNumber: 2880
+
+dn: cn=Aziz Hawley, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Aziz Hawley
+sn: Hawley
+description: This is Aziz Hawley's description
+facsimileTelephoneNumber: +1 71 936-5160
+l: Menlo Park
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 238
+telephoneNumber: +1 415 139-9638
+title: Elite Human Resources Punk
+userPassword: yelwaHzizA
+uid: Aziz_Hawley
+givenName: Aziz
+mail: Aziz_Hawley@example.com
+carLicense: JJB405S
+departmentNumber: 1322
+employeeType: Contract
+homePhone: +1 206 165-8362
+initials: A. H.
+mobile: +1 804 985-9213
+pager: +1 206 444-5129
+manager: cn=Karyn Holloway
+secretary: cn=Olav OPSPLNG
+roomNumber: 4248
+
+dn: cn=Fox Receiving, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Fox Receiving
+sn: Receiving
+description: This is Fox Receiving's description
+facsimileTelephoneNumber: +1 303 372-8912
+l: Sunnyvale
+ou: Product Development
+postalAddress: example$Product Development$Dept # 522
+telephoneNumber: +1 415 455-5816
+title: Associate Product Development Pinhead
+userPassword: gnivieceRx
+uid: Fox_Receiving
+givenName: Fox
+mail: Fox_Receiving@example.com
+carLicense: 1IMTNIY
+departmentNumber: 891
+employeeType: Normal
+homePhone: +1 510 485-3318
+initials: F. R.
+mobile: +1 303 719-1736
+pager: +1 303 665-1600
+manager: cn=Anstice Smithson
+secretary: cn=Torrie Arnon
+roomNumber: 5785
+
+dn: cn=Elleke Wessel, ou=Peons, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Elleke Wessel
+sn: Wessel
+description: This is Elleke Wessel's description
+facsimileTelephoneNumber: +1 213 394-3666
+l: Alameda
+ou: Peons
+postalAddress: example$Peons$Dept # 944
+telephoneNumber: +1 71 236-5017
+title: Junior Peons Writer
+userPassword: lesseWekel
+uid: Elleke_Wessel
+givenName: Elleke
+mail: Elleke_Wessel@example.com
+carLicense: 03F3UHJ
+departmentNumber: 6509
+employeeType: Manager
+homePhone: +1 818 284-3358
+initials: E. W.
+mobile: +1 71 971-2866
+pager: +1 415 430-2905
+manager: cn=LeRoy Goodner
+secretary: cn=Brenn Silieff
+roomNumber: 944
+
+dn: cn=Jody Caie, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Jody Caie
+sn: Caie
+description: This is Jody Caie's description
+facsimileTelephoneNumber: +1 303 527-2692
+l: Mountain View
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 114
+telephoneNumber: +1 206 679-1888
+title: Chief Product Testing Developer
+userPassword: eiaCydoJ
+uid: Jody_Caie
+givenName: Jody
+mail: Jody_Caie@example.com
+carLicense: 9BJH2K7
+departmentNumber: 5758
+employeeType: Temp
+homePhone: +1 818 498-6489
+initials: J. C.
+mobile: +1 71 326-6995
+pager: +1 206 514-8361
+manager: cn=Verena Misslitz
+secretary: cn=Leandra Marschewaki
+roomNumber: 1529
+
+dn: cn=Hoa Dandurand, ou=Management, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Hoa Dandurand
+sn: Dandurand
+description: This is Hoa Dandurand's description
+facsimileTelephoneNumber: +1 510 295-6636
+l: Redwood Shores
+ou: Management
+postalAddress: example$Management$Dept # 628
+telephoneNumber: +1 71 984-5150
+title: Chief Management Pinhead
+userPassword: dnarudnaDa
+uid: Hoa_Dandurand
+givenName: Hoa
+mail: Hoa_Dandurand@example.com
+carLicense: 9YJFYL1
+departmentNumber: 499
+employeeType: Manager
+homePhone: +1 415 809-1590
+initials: H. D.
+mobile: +1 71 907-2817
+pager: +1 510 862-3357
+manager: cn=Sioux Pancholy
+secretary: cn=Julietta Briel
+roomNumber: 2015
+
+dn: cn=Vlad Hord, ou=Planning, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Vlad Hord
+sn: Hord
+description: This is Vlad Hord's description
+facsimileTelephoneNumber: +1 206 490-1246
+l: Sunnyvale
+ou: Planning
+postalAddress: example$Planning$Dept # 447
+telephoneNumber: +1 303 750-4975
+title: Senior Planning Director
+userPassword: droHdalV
+uid: Vlad_Hord
+givenName: Vlad
+mail: Vlad_Hord@example.com
+carLicense: 45D4ZMU
+departmentNumber: 7057
+employeeType: Manager
+homePhone: +1 408 217-9879
+initials: V. H.
+mobile: +1 206 674-4523
+pager: +1 818 315-5452
+manager: cn=Irita Swanson
+secretary: cn=Anker Mathewson
+roomNumber: 9259
+
+dn: cn=Logntp Yaung, ou=Accounting, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Logntp Yaung
+sn: Yaung
+description: This is Logntp Yaung's description
+facsimileTelephoneNumber: +1 804 637-1515
+l: Fremont
+ou: Accounting
+postalAddress: example$Accounting$Dept # 577
+telephoneNumber: +1 818 846-6671
+title: Chief Accounting Consultant
+userPassword: gnuaYptngo
+uid: Logntp_Yaung
+givenName: Logntp
+mail: Logntp_Yaung@example.com
+carLicense: 8X0DAD7
+departmentNumber: 9019
+employeeType: Employee
+homePhone: +1 71 243-2137
+initials: L. Y.
+mobile: +1 408 899-9752
+pager: +1 408 186-4812
+manager: cn=Aili Benton
+secretary: cn=Lilias Settles
+roomNumber: 1094
+
+dn: cn=Habeeb Rosenblum, ou=Human Resources, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Habeeb Rosenblum
+sn: Rosenblum
+description: This is Habeeb Rosenblum's description
+facsimileTelephoneNumber: +1 415 101-2600
+l: San Francisco
+ou: Human Resources
+postalAddress: example$Human Resources$Dept # 304
+telephoneNumber: +1 206 631-6108
+title: Chief Human Resources Figurehead
+userPassword: mulbnesoRb
+uid: Habeeb_Rosenblum
+givenName: Habeeb
+mail: Habeeb_Rosenblum@example.com
+carLicense: 24KN6IU
+departmentNumber: 3001
+employeeType: Employee
+homePhone: +1 206 227-6825
+initials: H. R.
+mobile: +1 804 844-7445
+pager: +1 510 896-4710
+manager: cn=Marek Percy
+secretary: cn=Colette Blanche
+roomNumber: 4961
+
+dn: cn=Enid Goridkov, ou=Product Development, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Enid Goridkov
+sn: Goridkov
+description: This is Enid Goridkov's description
+facsimileTelephoneNumber: +1 303 636-7461
+l: Cambridge
+ou: Product Development
+postalAddress: example$Product Development$Dept # 484
+telephoneNumber: +1 510 658-9835
+title: Junior Product Development Madonna
+userPassword: vokdiroGdi
+uid: Enid_Goridkov
+givenName: Enid
+mail: Enid_Goridkov@example.com
+carLicense: 0FW2NN4
+departmentNumber: 3347
+employeeType: Normal
+homePhone: +1 408 343-1126
+initials: E. G.
+mobile: +1 818 372-2038
+pager: +1 71 423-5647
+manager: cn=Mougy Mong
+secretary: cn=Nicola Paone
+roomNumber: 1860
+
+dn: cn=Marice McCaugherty, ou=Product Testing, dc=example,dc=com
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: inetOrgPerson
+cn: Marice McCaugherty
+sn: McCaugherty
+description: This is Marice McCaugherty's description
+facsimileTelephoneNumber: +1 510 519-3382
+l: Menlo Park
+ou: Product Testing
+postalAddress: example$Product Testing$Dept # 26
+telephoneNumber: +1 213 496-8242
+title: Elite Product Testing Stooge
+userPassword: ytrehguaCc
+uid: Marice_McCaugherty
+givenName: Marice
+mail: Marice_McCaugherty@example.com
+carLicense: D3P8WWY
+departmentNumber: 7100
+employeeType: Manager
+homePhone: +1 408 101-6964
+initials: M. M.
+mobile: +1 510 590-8719
+pager: +1 303 638-5731
+manager: cn=Sada McNeilly
+secretary: cn=Antonia Fallows
+roomNumber: 7891
diff --git a/tests/data/regressions/its9282/its9282 b/tests/data/regressions/its9282/its9282
new file mode 100755
index 0000000..3d55b8a
--- /dev/null
+++ b/tests/data/regressions/its9282/its9282
@@ -0,0 +1,267 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+if test $BACKEND = ldif ; then
+ # Onelevel search does not return entries in order of creation or CSN.
+ echo "$BACKEND backend unsuitable for syncprov, test skipped"
+ exit 0
+fi
+
+echo "This test tracks a case where a deleted entry gets resurrected."
+echo "See https://bugs.openldap.org/show_bug.cgi?id=9282 for more information."
+
+MMR=2
+XDIR=$TESTDIR/srv
+
+mkdir -p $TESTDIR
+
+USERDN="cn=Damon Leeson, ou=Product Development, dc=example,dc=com"
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+ITS=9282
+ITSDIR=$DATADIR/regressions/its$ITS
+
+
+n=1
+while [ $n -le $MMR ]; do
+ echo "Initializing server configuration for MMR$n..."
+ DBDIR=${XDIR}$n/db
+ CFDIR=${XDIR}$n/slapd.d
+
+ mkdir -p ${XDIR}$n $DBDIR.1 $DBDIR.2 $CFDIR
+ . $CONFFILTER $BACKEND < $ITSDIR/config.ldif > $TESTDIR/config${n}.ldif
+ if [ $n = 1 ]; then
+ MYURI=`eval echo '$URI2'`
+ else
+ MYURI=`eval echo '$URI1'`
+ fi
+ sed -e "s/@SID@/$n/g" -e "s|@URI@|$MYURI|g" $TESTDIR/config${n}.ldif > $CONFLDIF
+ $SLAPADD -F $CFDIR -n 0 -l $CONFLDIF
+ n=`expr $n + 1`
+done
+
+KILLPIDS=
+n=1
+while [ $n -le $MMR ]; do
+ MYURI=`eval echo '$URI'$n`
+ MYLOG=`eval echo '$LOG'$n`
+ CFDIR=${XDIR}$n/slapd.d
+
+ echo "Starting provider slapd on TCP/IP URI $MYURI"
+ $SLAPD -F $CFDIR -h $MYURI -d $LVL -d sync > $MYLOG 2>&1 &
+
+ PID=$!
+ if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+ fi
+ KILLPIDS="$PID $KILLPIDS"
+ if [ $n = 1 ]; then
+ MPID="$PID"
+ fi
+ sleep 1
+
+ echo "Using ldapsearch to check that provider slapd is running..."
+ for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $MYURI \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+ done
+
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ n=`expr $n + 1`
+done
+
+echo "Sleeping 15 seconds to allow stabilization..."
+sleep 15
+
+echo "Populating database on provider..."
+$LDAPADD -D $MANAGERDN -H $URI1 -w $PASSWD -f $ITSDIR/exampledb.ldif >> $TESTOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting on databases to sync..."
+loop=0
+while [ $loop -ne 1 ]; do
+ $LDAPSEARCH -D $MANAGERDN -w $PASSWD -s base -b "$BASEDN" -H $URI1 \
+ contextCSN | grep contextCSN: > $TESTDIR/server1.csn 2>&1
+ $LDAPSEARCH -D $MANAGERDN -w $PASSWD -s base -b "$BASEDN" -H $URI2 \
+ contextCSN | grep contextCSN: > $TESTDIR/server2.csn 2>&1
+ $CMP $TESTDIR/server1.csn $TESTDIR/server2.csn > $CMPOUT
+ if test $? != 0 ; then
+ echo "Still syncing..."
+ sleep 30
+ else
+ loop=1
+ fi
+done
+
+echo "Comparing entry on providers..."
+$LDAPSEARCH -D $MANAGERDN -w $PASSWD -b "$BASEDN" -H $URI2 \
+ '(cn=Damon Leeson)' '1.1' > $TESTDIR/server2.flt 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch on $URI2 failed with error $RC."
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -D $MANAGERDN -w $PASSWD -b "$BASEDN" -H $URI1 \
+ '(cn=Damon Leeson)' '1.1' > $TESTDIR/server1.flt 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch on $URI1 failed with error $RC."
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$CMP $TESTDIR/server1.flt $TESTDIR/server2.flt > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider 1 and provider 2 databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo -n "Stopping MMR1 slapd..."
+kill -HUP $MPID
+wait $MPID
+KILLPIDS=`echo "$KILLPIDS " | sed -e "s/ $MPID / /"`;
+sleep $SLEEP2
+echo "done"
+
+echo "Deleting entry from provider 2..."
+
+$LDAPDELETE -D "$MANAGERDN" -H $URI2 -w $PASSWD "$USERDN" >> $TESTOUT
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapdelete on $URI2 failed with error $RC."
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -D $MANAGERDN -w $PASSWD -b "$BASEDN" -H $URI2 \
+ '(cn=Damon Leeson)' '1.1' > $TESTDIR/server2dc.flt 2>&1
+
+echo "Starting provider1 slapd on TCP/IP URI $URI1"
+CFDIR="$TESTDIR/srv1/slapd.d"
+$SLAPD -F $CFDIR -h $URI1 -d $LVL >> $LOG1 2>&1 &
+
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID $KILLPIDS"
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+echo "done"
+
+echo "Sleeping 30 seconds to allow databases to sync..."
+sleep 30
+
+echo "Comparing entry on providers post delete..."
+$LDAPSEARCH -D $MANAGERDN -w $PASSWD -b "$BASEDN" -H $URI2 \
+ '(cn=Damon Leeson)' '1.1' > $TESTDIR/server2d.flt 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch on $URI2 failed with error $RC."
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -D $MANAGERDN -w $PASSWD -b "$BASEDN" -H $URI1 \
+ '(cn=Damon Leeson)' '1.1' > $TESTDIR/server1d.flt 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch on $URI1 failed with error $RC."
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$CMP $ITSDIR/noentry.flt $TESTDIR/server2d.flt > $CMPOUT
+if test $? != 0 ; then
+ echo "test failed - entry exists on provider 2"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+$CMP $ITSDIR/noentry.flt $TESTDIR/server1d.flt > $CMPOUT
+if test $? != 0 ; then
+ echo "test failed - entry exists on provider 1"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Checking provider 2 sent the right cookie..."
+grep "starting refresh.*csn=.*#002#.*" $LOG2 >/dev/null 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "Provider 2 did not send its CSN in the cookie"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/data/regressions/its9282/noentry.flt b/tests/data/regressions/its9282/noentry.flt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/data/regressions/its9282/noentry.flt
diff --git a/tests/data/regressions/its9288/its9288 b/tests/data/regressions/its9288/its9288
new file mode 100755
index 0000000..30e67d4
--- /dev/null
+++ b/tests/data/regressions/its9288/its9288
@@ -0,0 +1,186 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+ITS=9288
+ITSDIR=$DATADIR/regressions/its$ITS
+
+if test $BACKLDAP = "ldapno" ; then
+ echo "LDAP backend not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+cp -r $DATADIR/tls $TESTDIR
+
+echo "This test checks that back-ldap does not crash when proxy retries "
+echo "connection to remote server and the retry fails with an LDAP error."
+
+#
+# Start slapd that acts as a remote LDAP server that will be proxied
+#
+echo "Running slapadd to build database for the remote slapd server..."
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+$SLAPADD -f $CONF1 -l $LDIFORDERED
+
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+
+echo "Starting remote slapd server on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h "$URI1" -d $LVL > $LOG1 2>&1 &
+SERVERPID=$!
+if test $WAIT != 0 ; then
+ echo SERVERPID $SERVERPID
+ read foo
+fi
+
+sleep $SLEEP0
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+
+#
+# Start ldapd that will proxy for the remote server
+#
+echo "Starting slapd proxy on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $ITSDIR/slapd-proxy.conf > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PROXYPID=$!
+if test $WAIT != 0 ; then
+ echo PROXYPID $PROXYPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PROXYPID"
+
+sleep $SLEEP0
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+
+#
+# Test case:
+#
+# 1. Client establishes connection to proxy and binds
+# 2. Proxy establishes connection to remote server and passes through the bind.
+# 3. Change the user password on the remote server
+# 4. Kill and restart the remote server to invalidate the TCP connection between proxy and remote
+# 5. Make a new search from client
+# 6. Proxy notices connection is down and retries bind (rebind-as-user)
+# 7. Server responds with error: invalid credentials
+# 8. Proxy crashes
+#
+
+# Create fifo that is used to pass searches from the test case to ldapsearch without
+# disconnecting the client -> proxy connection
+rm -f $TESTDIR/ldapsearch.fifo
+mkfifo $TESTDIR/ldapsearch.fifo
+
+# Start ldapsearch on background and have it read search filters from fifo,
+# so that single client connection will persist over many searches
+echo "Make the proxy to connect the remote LDAP server..."
+$LDAPSEARCH -b "$BASEDN" -H $URI2 \
+ -D "$BABSDN" -w "bjensen" \
+ -f $TESTDIR/ldapsearch.fifo > $TESTOUT 2>&1 &
+LDAPSEARCHPID=$!
+KILLPIDS="$KILLPIDS $LDAPSEARCHPID"
+
+# Open fifo as file descriptor
+exec 3>$TESTDIR/ldapsearch.fifo
+
+# Trigger LDAP connections towards the proxy by executing a search
+echo 'objectclass=*' >&3
+
+echo "Change user's bind password on the remote server in order to make rebind-as-user fail when proxy retries"
+$LDAPPASSWD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -s "newpass" "$BABSDN" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldappasswd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS $SERVERPID
+ exit $RC
+fi
+
+# Restart the remote server to invalidate TCP connection between proxy and remote
+echo "Killing and Re-starting remote slapd server on TCP/IP port $PORT1..."
+kill -HUP $SERVERPID
+wait $SERVERPID
+
+$SLAPD -f $CONF1 -h "$URI1" -d $LVL >> $LOG1 2>&1 &
+SERVERPID=$!
+if test $WAIT != 0 ; then
+ echo SERVERPID $SERVERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $SERVERPID"
+
+sleep $SLEEP0
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+
+echo "Make new ldap search to trigger proxy retry logic"
+echo 'objectclass=*' >&3
+
+sleep $SLEEP0
+echo "Checking if proxy slapd is still up"
+$LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapd crashed!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS 2>/dev/null
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS 2>/dev/null
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/data/regressions/its9288/slapd-proxy.conf b/tests/data/regressions/its9288/slapd-proxy.conf
new file mode 100644
index 0000000..e9df3b7
--- /dev/null
+++ b/tests/data/regressions/its9288/slapd-proxy.conf
@@ -0,0 +1,41 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.m.pid
+argsfile @TESTDIR@/slapd.m.args
+
+#######################################################################
+# database definitions
+#######################################################################
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/:../servers/slapd/overlays
+#mod#moduleload back_@BACKEND@.la
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+#monitormod#modulepath ../servers/slapd/back-monitor/
+#monitormod#moduleload back_monitor.la
+
+# Configure proxy
+database ldap
+uri "@URI1@"
+suffix "dc=example,dc=com"
+rebind-as-user yes
+
+database monitor
diff --git a/tests/data/regressions/its9338/its9338 b/tests/data/regressions/its9338/its9338
new file mode 100755
index 0000000..5010910
--- /dev/null
+++ b/tests/data/regressions/its9338/its9338
@@ -0,0 +1,100 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "This test checks for pending operations to resume correctly"
+echo "after they were stopped by a writer getting blocked by a full"
+echo "socket output buffer. It uses a search with syncrepl control"
+echo "and multiple modifications to generate enough responses to"
+echo "fill the output buffer."
+echo ""
+
+#
+# Test replication:
+# - start provider
+# - populate over ldap
+# - start persistent search and ignoring responses
+# - perform some repeated adds/deletes to fill buffer
+# - abandon persistent search and issue new search
+# - if the bug is present, this will timeout.
+#
+
+echo "Starting provider slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $SRPROVIDERCONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the provider directory..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDERED > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting persistent search and modifications..."
+echo '(Ignore the "press Enter to continue" that appears below)'
+( sleep 3; echo "" 1>&2; $PROGDIR/slapd-addel -H $URI1 -D "$MANAGERDN" -w $PASSWD -l 4000 -f $DATADIR/do_add.1 1>&2; \
+echo "" ) | $LDAPSEARCH -b "$BASEDN" -H $URI1 -E sync=rp -e backlog -l $SLEEP1
+
+if test $? != 0 ; then
+ echo "ldapsearch failed - write waiter didn't resume pending ops"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/data/regressions/its9400/its9400 b/tests/data/regressions/its9400/its9400
new file mode 100755
index 0000000..1045431
--- /dev/null
+++ b/tests/data/regressions/its9400/its9400
@@ -0,0 +1,161 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+ITS=9400
+ITSDIR=$DATADIR/regressions/its$ITS
+
+if test $BACKLDAP = "ldapno" ; then
+ echo "LDAP backend not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+cp -r $DATADIR/tls $TESTDIR
+
+echo "This test checks that back-ldap does retry binds after the remote LDAP server"
+echo "has abruptly disconnected the (idle) LDAP connection."
+
+#
+# Start slapd that acts as a remote LDAP server that will be proxied
+#
+echo "Running slapadd to build database for the remote slapd server..."
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+$SLAPADD -f $CONF1 -l $LDIFORDERED
+
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+
+echo "Starting remote slapd server on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h "$URI1" -d $LVL > $LOG1 2>&1 &
+SERVERPID=$!
+if test $WAIT != 0 ; then
+ echo SERVERPID $SERVERPID
+ read foo
+fi
+
+
+#
+# Start ldapd that will proxy for the remote server
+#
+echo "Starting slapd proxy on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $ITSDIR/slapd-proxy-idassert.conf > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PROXYPID=$!
+if test $WAIT != 0 ; then
+ echo PROXYPID $PROXYPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PROXYPID"
+
+sleep 1
+
+
+#
+# Successful searches
+#
+
+echo "Using ldapsearch with bind that will be passed through to remote server..."
+$LDAPSEARCH -S "" -b "$BASEDN" \
+ -D "cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" \
+ -H $URI2 \
+ -w "bjensen" \
+ 'objectclass=*' > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed at proxy ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+echo "Using ldapsearch with idassert-bind..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "cn=Manager,dc=local,dc=com" -H $URI2 -w "secret" \
+ 'objectclass=*' >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed at proxy ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+#
+# Now kill the remote slapd that is being proxied for.
+# This will invalidate the current TCP connections that proxy has to remote.
+#
+echo "Killing remote server"
+kill $SERVERPID
+sleep 1
+
+echo "Re-starting remote slapd server on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h "$URI1" -d $LVL >> $LOG1 2>&1 &
+SERVERPID=$!
+if test $WAIT != 0 ; then
+ echo SERVERPID $SERVERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $SERVERPID"
+
+sleep 2
+
+
+echo "-------------------------------------------------" >> $TESTOUT
+echo "Searches after remote slapd server has restarted:" >> $TESTOUT
+echo "-------------------------------------------------" >> $TESTOUT
+
+#
+# Successful search
+#
+echo "Using ldapsearch with bind that will be passed through to remote server..."
+$LDAPSEARCH -S "" -b "$BASEDN" \
+ -D "cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" \
+ -H $URI2 \
+ -w "bjensen" \
+ 'objectclass=*' >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed at proxy ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#
+# UNSUCCESFUL SEARCH
+#
+echo "Using ldapsearch with idassert-bind..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "cn=Manager,dc=local,dc=com" -H $URI2 -w "secret" \
+ 'objectclass=*' >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed at proxy ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/data/regressions/its9400/slapd-proxy-idassert.conf b/tests/data/regressions/its9400/slapd-proxy-idassert.conf
new file mode 100644
index 0000000..2f2750b
--- /dev/null
+++ b/tests/data/regressions/its9400/slapd-proxy-idassert.conf
@@ -0,0 +1,52 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.m.pid
+argsfile @TESTDIR@/slapd.m.args
+
+#######################################################################
+# database definitions
+#######################################################################
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/:../servers/slapd/overlays
+#mod#moduleload back_@BACKEND@.la
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+#monitormod#modulepath ../servers/slapd/back-monitor/
+#monitormod#moduleload back_monitor.la
+
+# here the proxy is not only acting as a proxy, but it also has a local database dc=local,dc=com"
+database @BACKEND@
+suffix "dc=local,dc=com"
+rootdn "cn=Manager,dc=local,dc=com"
+rootpw "secret"
+#~null~#directory @TESTDIR@/db.2.a
+
+# Configure proxy
+# - normal user binds to "*,dc=example,dc=com" are proxied through to the remote slapd
+# - admin bind to local "cn=Manager,dc=local,dc=com" is overwritten by using idassert-bind
+database ldap
+uri "@URI1@"
+suffix "dc=example,dc=com"
+idassert-bind bindmethod=simple binddn="cn=Manager,dc=example,dc=com" credentials="secret"
+idassert-authzFrom "dn.exact:cn=Manager,dc=local,dc=com"
+rebind-as-user yes
+
+database monitor
diff --git a/tests/data/relay.out b/tests/data/relay.out
new file mode 100644
index 0000000..a17b58d
--- /dev/null
+++ b/tests/data/relay.out
@@ -0,0 +1,2585 @@
+# searching base="dc=example,dc=com"...
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+
+dn: cn=Manager,dc=example,dc=com
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+
+# searching base="o=Example,c=US"...
+dn: cn=All Staff,ou=Groups,o=Example,c=US
+member: cn=Manager,o=Example,c=US
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Examp
+ le,c=US
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=John Doe,ou=Information Technology Division,ou=People,o=Example,c=U
+ S
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Exam
+ ple,c=US
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Example
+ ,c=US
+owner: cn=Manager,o=Example,c=US
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,o=Example,c=US
+member: cn=Manager,o=Example,c=US
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+owner: cn=Manager,o=Example,c=US
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Example,c
+ =US
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Example,c=U
+ S
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+
+dn: o=Example,c=US
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: uidObject
+uid: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=Groups,o=Example,c=US
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,o=Example,c=US
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=ITD Staff,ou=Groups,o=Example,c=US
+owner: cn=Manager,o=Example,c=US
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfNames
+member: cn=Manager,o=Example,c=US
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Example
+ ,c=US
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Exam
+ ple,c=US
+member: cn=John Doe,ou=Information Technology Division,ou=People,o=Example,c=U
+ S
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Example,
+ c=US
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+
+dn: cn=Manager,o=Example,c=US
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+
+dn: ou=People,o=Example,c=US
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+
+# searching base="o=Esempio,c=IT"...
+dn: cn=All Staff,ou=Groups,o=Esempio,c=IT
+member: cn=Manager,o=Esempio,c=IT
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Esemp
+ io,c=IT
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=John Doe,ou=Information Technology Division,ou=People,o=Esempio,c=I
+ T
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Esem
+ pio,c=IT
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Esempio
+ ,c=IT
+owner: cn=Manager,o=Esempio,c=IT
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,o=Esempio,c=IT
+member: cn=Manager,o=Esempio,c=IT
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Esempio,c=IT
+owner: cn=Manager,o=Esempio,c=IT
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: ou=Alumni Association,ou=People,o=Esempio,c=IT
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Esempio,c
+ =IT
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,o=Esempio,c=IT
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Esempio,c=I
+ T
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,o=Esempio,c=IT
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Esempio,c=IT
+objectClass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Esempio,c=IT
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+
+dn: o=Esempio,c=IT
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=Groups,o=Esempio,c=IT
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,o=Esempio,c=IT
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=ITD Staff,ou=Groups,o=Esempio,c=IT
+owner: cn=Manager,o=Esempio,c=IT
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Esempio,c=IT
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Esempio,c=IT
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Esempio,
+ c=IT
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,o=Esempio,c=IT
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,o=Esempio,c=IT
+objectClass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Esempio,c=IT
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Esempio,c=IT
+objectClass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Esempio,c=IT
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,o=Esempio,c=IT
+objectClass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Esempio,c=IT
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+
+dn: cn=Manager,o=Esempio,c=IT
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Esempio,c=IT
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Esempio,c=IT
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+
+dn: ou=People,o=Esempio,c=IT
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Esempio,c=IT
+objectClass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Esempio,c=IT
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+
+# searching base="o=Beispiel,c=DE"...
+dn: cn=All Staff,ou=Groups,o=Beispiel,c=DE
+member: cn=Manager,o=Beispiel,c=DE
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Beisp
+ iel,c=DE
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=Beispiel,c=DE
+member: cn=John Doe,ou=Information Technology Division,ou=People,o=Beispiel,c=
+ DE
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Beispiel,c=DE
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Beispiel,c=DE
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Beis
+ piel,c=DE
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Beispiel,c=DE
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Beispiel,c=DE
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Beispiel,c=DE
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Beispie
+ l,c=DE
+owner: cn=Manager,o=Beispiel,c=DE
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,o=Beispiel,c=DE
+member: cn=Manager,o=Beispiel,c=DE
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Beispiel,c=DE
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Beispiel,c=DE
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=Beispiel,c=DE
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Beispiel,c=DE
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Beispiel,c=DE
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Beispiel,c=DE
+owner: cn=Manager,o=Beispiel,c=DE
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: ou=Alumni Association,ou=People,o=Beispiel,c=DE
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Beispiel,
+ c=DE
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,o=Beispiel,c=DE
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
+dn: o=Beispiel,c=DE
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Beispiel,c=
+ DE
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,o=Beispiel,c=DE
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Beispiel,c=DE
+objectClass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Beispiel,c=DE
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+
+dn: ou=Groups,o=Beispiel,c=DE
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,o=Beispiel,c=DE
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=ITD Staff,ou=Groups,o=Beispiel,c=DE
+owner: cn=Manager,o=Beispiel,c=DE
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Beispiel,c=DE
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Beispiel,c=DE
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Beispiel
+ ,c=DE
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,o=Beispiel,c=DE
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,o=Beispiel,c=DE
+objectClass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Beispiel,c=DE
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Beispiel,c=DE
+objectClass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Beispiel,c=DE
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,o=Beispiel,c=DE
+objectClass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Beispiel,c=DE
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+
+dn: cn=Manager,o=Beispiel,c=DE
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Beispiel,c=DE
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Beispiel,c=DE
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+
+dn: ou=People,o=Beispiel,c=DE
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Beispiel,c=DE
+objectClass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Beispiel,c=DE
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+
+# searching base="o=Example,c=US"...
+dn: cn=Added Group,ou=Groups,o=Example,c=US
+objectClass: groupOfNames
+cn: Added Group
+member: cn=Added Group,ou=Groups,o=Example,c=US
+
+dn: cn=Added User,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Added User
+sn: User
+uid: auser
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePhone: +49 1234567890
+drink: Beer
+mail: auser@mail.alumni.example.com
+telephoneNumber: +49 1234-567-890
+description: Just added in o=Beispiel,c=DE naming context
+
+dn: cn=All Staff,ou=Groups,o=Example,c=US
+member: cn=Manager,o=Example,c=US
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Examp
+ le,c=US
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=John Doe,ou=Information Technology Division,ou=People,o=Example,c=U
+ S
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Exam
+ ple,c=US
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Example
+ ,c=US
+owner: cn=Manager,o=Example,c=US
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,o=Example,c=US
+member: cn=Manager,o=Example,c=US
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Example,c=US
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+owner: cn=Manager,o=Example,c=US
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Another Added Group,ou=Groups,o=Example,c=US
+objectClass: groupOfNames
+objectClass: uidObject
+cn: Another Added Group
+member: cn=Added Group,ou=Groups,o=Example,c=US
+member: cn=Another Added Group,ou=Groups,o=Example,c=US
+uid: added
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Example,c
+ =US
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Example,c=U
+ S
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+
+dn: o=Example,c=US
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: uidObject
+uid: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=Groups,o=Example,c=US
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,o=Example,c=US
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=ITD Staff,ou=Groups,o=Example,c=US
+owner: cn=Manager,o=Example,c=US
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfNames
+member: cn=Manager,o=Example,c=US
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Example
+ ,c=US
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Exam
+ ple,c=US
+member: cn=John Doe,ou=Information Technology Division,ou=People,o=Example,c=U
+ S
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Example,
+ c=US
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+
+dn: cn=Jane Q. Doe,ou=Information Technology Division,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Jane Alverson
+cn: Jane Q. Doe
+cn: Jane Qissapaolo Doe
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+
+dn: cn=John P. Doe,ou=Information Technology Division,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Jonathon Doe
+cn: John P. Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+
+dn: cn=Manager,o=Example,c=US
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+dn: ou=People,o=Example,c=US
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+objectClass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+seeAlso: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+description: Just added self to seeAlso in o=Beispiel,c=DE virtual naming cont
+ ext
+
+# refldap://localhost:9012/ou=Referrals,o=Beispiel,c=DE??sub
+
+# searching base="o=Esempio,c=IT"...
+dn: cn=Added Group,ou=Groups,o=Esempio,c=IT
+objectClass: groupOfUniqueNames
+cn: Added Group
+uniqueMember: cn=Added Group,ou=Groups,dc=example,dc=com
+
+dn: cn=Added User,ou=Alumni Association,ou=People,o=Esempio,c=IT
+objectClass: OpenLDAPperson
+cn: Added User
+sn: User
+uid: auser
+seeAlso: cn=All Staff,ou=Groups,o=Esempio,c=IT
+homePhone: +49 1234567890
+drink: Beer
+mail: auser@mail.alumni.example.com
+telephoneNumber: +49 1234-567-890
+description: Just added in o=Beispiel,c=DE naming context
+
+dn: cn=All Staff,ou=Groups,o=Esempio,c=IT
+member: cn=Manager,o=Esempio,c=IT
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Esemp
+ io,c=IT
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=John Doe,ou=Information Technology Division,ou=People,o=Esempio,c=I
+ T
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Esem
+ pio,c=IT
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Esempio
+ ,c=IT
+owner: cn=Manager,o=Esempio,c=IT
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,o=Esempio,c=IT
+member: cn=Manager,o=Esempio,c=IT
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=Jane Doe,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,o=Esempio,c=IT
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Esempio,c=IT
+owner: cn=Manager,o=Esempio,c=IT
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: ou=Alumni Association,ou=People,o=Esempio,c=IT
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Another Added Group,ou=Groups,o=Esempio,c=IT
+objectClass: groupOfUniqueNames
+objectClass: dcObject
+cn: Another Added Group
+uniqueMember: cn=Added Group,ou=Groups,dc=example,dc=com
+uniqueMember: cn=Another Added Group,ou=Groups,dc=example,dc=com
+dc: added
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Esempio,c
+ =IT
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,o=Esempio,c=IT
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Esempio,c=I
+ T
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,o=Esempio,c=IT
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Esempio,c=IT
+objectClass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Esempio,c=IT
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+
+dn: o=Esempio,c=IT
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=Groups,o=Esempio,c=IT
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,o=Esempio,c=IT
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=ITD Staff,ou=Groups,o=Esempio,c=IT
+owner: cn=Manager,o=Esempio,c=IT
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Esempio,c=IT
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Esempio,c=IT
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Esempio,
+ c=IT
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,o=Esempio,c=IT
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+
+dn: cn=Jane Q. Doe,ou=Information Technology Division,ou=People,o=Esempio,c=IT
+objectClass: OpenLDAPperson
+cn: Jane Alverson
+cn: Jane Q. Doe
+cn: Jane Qissapaolo Doe
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+seeAlso: cn=All Staff,ou=Groups,o=Esempio,c=IT
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Esempio,c=IT
+objectClass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Esempio,c=IT
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+
+dn: cn=John P. Doe,ou=Information Technology Division,ou=People,o=Esempio,c=IT
+objectClass: OpenLDAPperson
+cn: Jonathon Doe
+cn: John P. Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Esempio,c=IT
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+
+dn: cn=Manager,o=Esempio,c=IT
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+dn: ou=People,o=Esempio,c=IT
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Esempio,c=IT
+objectClass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,o=Esempio,c=IT
+seeAlso: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Esempio,c=IT
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+description: Just added self to seeAlso in o=Beispiel,c=DE virtual naming cont
+ ext
+
+# refldap://localhost:9012/ou=Referrals,o=Beispiel,c=DE??sub
+
+# searching filter="(objectClass=referral)"
+# attrs="'*' ref"
+# base="dc=example,dc=com"...
+dn: ou=Referrals,dc=example,dc=com
+objectClass: referral
+objectClass: extensibleObject
+ou: Referrals
+description: Just added as ldap://localhost.localdomain:389/ou=Referrals,o=Bei
+ spiel,c=DE
+description: ...and modified as ldap://localhost:9012/ou=Referrals,o=Beispiel,
+ c=DE
+ref: ldap://localhost:9012/ou=Referrals,o=Beispiel,c=DE
+
+# base="o=Example,c=US"...
+dn: ou=Referrals,o=Example,c=US
+objectClass: referral
+objectClass: extensibleObject
+ou: Referrals
+description: Just added as ldap://localhost.localdomain:389/ou=Referrals,o=Bei
+ spiel,c=DE
+description: ...and modified as ldap://localhost:9012/ou=Referrals,o=Beispiel,
+ c=DE
+ref: ldap://localhost:9012/ou=Referrals,o=Beispiel,c=DE
+
+# base="o=Esempio,c=IT"...
+dn: ou=Referrals,o=Esempio,c=IT
+objectClass: referral
+objectClass: extensibleObject
+ou: Referrals
+description: Just added as ldap://localhost.localdomain:389/ou=Referrals,o=Bei
+ spiel,c=DE
+description: ...and modified as ldap://localhost:9012/ou=Referrals,o=Beispiel,
+ c=DE
+ref: ldap://localhost:9012/ou=Referrals,o=Beispiel,c=DE
+
+# searching filter="(seeAlso=cn=all staff,ou=Groups,o=Example,c=US)"
+# attrs="seeAlso"
+# base="o=Example,c=US"...
+dn: cn=Added User,ou=Alumni Association,ou=People,o=Example,c=US
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,o=Example,c
+ =US
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Example,c=U
+ S
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,o=Example,c=US
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,o=Example,c=US
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,o=Example,
+ c=US
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+
+dn: cn=Jane Q. Doe,ou=Information Technology Division,ou=People,o=Example,c=US
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,o=Example,c=US
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+
+dn: cn=John P. Doe,ou=Information Technology Division,ou=People,o=Example,c=US
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+seeAlso: cn=All Staff,ou=Groups,o=Example,c=US
+seeAlso: cn=Ursula Hampster,ou=Alumni Association,ou=People,o=Example,c=US
+
+# refldap://localhost:9012/ou=Referrals,o=Beispiel,c=DE??sub
+
+# searching filter="(uid=example)"
+# attrs="uid"
+# base="o=Example,c=US"...
+dn: o=Example,c=US
+uid: example
+
+# refldap://localhost:9012/ou=Referrals,o=Beispiel,c=DE??sub
+
+# searching filter="(member=cn=Another Added Group,ou=Groups,o=Example,c=US)"
+# attrs="member"
+# base="o=Example,c=US"...
+dn: cn=Another Added Group,ou=Groups,o=Example,c=US
+member: cn=Added Group,ou=Groups,o=Example,c=US
+member: cn=Another Added Group,ou=Groups,o=Example,c=US
+
+# refldap://localhost:9012/ou=Referrals,o=Beispiel,c=DE??sub
+
diff --git a/tests/data/remoteauth/config.ldif b/tests/data/remoteauth/config.ldif
new file mode 100644
index 0000000..f59351a
--- /dev/null
+++ b/tests/data/remoteauth/config.ldif
@@ -0,0 +1,21 @@
+dn: olcOverlay={0}remoteauth,olcDatabase={1}@BACKEND@,cn=config
+objectClass: olcOverlayConfig
+objectclass: olcRemoteAuthCfg
+olcOverlay: {0}remoteauth
+olcRemoteAuthRetryCount: 3
+olcRemoteAuthTLS: starttls=critical
+ tls_cert="@TESTDIR@/tls/certs/localhost.crt"
+ tls_key="@TESTDIR@/tls/private/localhost.key"
+ tls_cacert="@TESTDIR@/tls/ca/certs/testsuiteCA.crt"
+ tls_reqcert=demand tls_reqsan=allow
+#openssl# tls_crlcheck=none
+olcRemoteAuthDNAttribute: seeAlso
+olcRemoteAuthDomainAttribute: o
+olcRemoteAuthDefaultDomain: default
+olcRemoteAuthDefaultRealm: @SURIP3@
+olcRemoteAuthStore: FALSE
+olcRemoteAuthMapping: default file://@TESTDIR@/default_domain
+olcRemoteAuthMapping: working_ldaps @SURIP3@
+olcRemoteAuthMapping: failing_ldaps @SURIP2@
+olcRemoteAuthMapping: self @URIP1@
+
diff --git a/tests/data/remoteauth/default_domain b/tests/data/remoteauth/default_domain
new file mode 100644
index 0000000..6a88463
--- /dev/null
+++ b/tests/data/remoteauth/default_domain
@@ -0,0 +1,3 @@
+ldap://we/should/not/be/able/to/connect/to
+@SURIP2@
+@SURIP3@
diff --git a/tests/data/remoteauth/remoteauth.conf b/tests/data/remoteauth/remoteauth.conf
new file mode 100644
index 0000000..9f30e17
--- /dev/null
+++ b/tests/data/remoteauth/remoteauth.conf
@@ -0,0 +1,21 @@
+overlay remoteauth
+
+# defaults
+#remoteauth_retry_count 3
+#remoteauth_store off
+
+remoteauth_tls starttls=critical
+ tls_cert=@TESTDIR@/tls/certs/localhost.crt
+ tls_key=@TESTDIR@/tls/private/localhost.key
+ tls_cacert=@TESTDIR@/tls/ca/certs/testsuiteCA.crt
+
+remoteauth_dn_attribute seeAlso
+remoteauth_domain_attribute o
+remoteauth_default_domain default
+remoteauth_default_realm @SURIP3@
+
+# It's a trap! (ehm... stack) cn=config entries will be emitted in reverse order
+remoteauth_mapping self @URIP1@
+remoteauth_mapping failing_ldaps @SURIP2@
+remoteauth_mapping working_ldaps @SURIP3@
+remoteauth_mapping default file://@TESTDIR@/default_domain
diff --git a/tests/data/retcode.conf b/tests/data/retcode.conf
new file mode 100644
index 0000000..7d8b394
--- /dev/null
+++ b/tests/data/retcode.conf
@@ -0,0 +1,115 @@
+# slapo-retcode standard track response codes configuration example
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+#
+# From "ldap.h", revised as per <draft-ietf-ldapbis-protocol>
+
+retcode-item "cn=success" 0x00
+
+retcode-item "cn=success w/ delay" 0x00 sleeptime=2
+
+retcode-item "cn=operationsError" 0x01
+retcode-item "cn=protocolError" 0x02
+retcode-item "cn=timeLimitExceeded" 0x03 op=search
+retcode-item "cn=sizeLimitExceeded" 0x04 op=search
+retcode-item "cn=compareFalse" 0x05 op=compare
+retcode-item "cn=compareTrue" 0x06 op=compare
+retcode-item "cn=authMethodNotSupported" 0x07
+retcode-item "cn=strongAuthNotSupported" 0x07 text="same as authMethodNotSupported"
+retcode-item "cn=strongAuthRequired" 0x08
+retcode-item "cn=strongerAuthRequired" 0x08 text="same as strongAuthRequired"
+#retcode-item "cn=partialResults" 0x09 text="LDAPv2+ (not LDAPv3)"
+
+retcode-item "cn=referral" 0x0a text="LDAPv3" ref="ldap://:9019"
+retcode-item "cn=adminLimitExceeded" 0x0b text="LDAPv3"
+retcode-item "cn=unavailableCriticalExtension" 0x0c text="LDAPv3"
+retcode-item "cn=confidentialityRequired" 0x0d text="LDAPv3"
+retcode-item "cn=saslBindInProgress" 0x0e text="LDAPv3"
+
+# LDAP_ATTR_ERROR(n) LDAP_RANGE((n),0x10,0x15) /* 16-21 */
+
+retcode-item "cn=noSuchAttribute" 0x10
+retcode-item "cn=undefinedAttributeType" 0x11
+retcode-item "cn=inappropriateMatching" 0x12
+retcode-item "cn=constraintViolation" 0x13
+retcode-item "cn=attributeOrValueExists" 0x14
+retcode-item "cn=invalidAttributeSyntax" 0x15
+
+# LDAP_NAME_ERROR(n) LDAP_RANGE((n),0x20,0x24) /* 32-34,36 */
+
+retcode-item "cn=noSuchObject" 0x20
+retcode-item "cn=aliasProblem" 0x21
+retcode-item "cn=invalidDNSyntax" 0x22
+#retcode-item "cn=isLeaf" 0x23 text="not LDAPv3"
+retcode-item "cn=aliasDereferencingProblem" 0x24
+
+# LDAP_SECURITY_ERROR(n) LDAP_RANGE((n),0x2F,0x32) /* 47-50 */
+
+retcode-item "cn=proxyAuthzFailure" 0x2F text="LDAPv3 proxy authorization"
+retcode-item "cn=inappropriateAuthentication" 0x30
+retcode-item "cn=invalidCredentials" 0x31
+retcode-item "cn=insufficientAccessRights" 0x32
+
+# LDAP_SERVICE_ERROR(n) LDAP_RANGE((n),0x33,0x36) /* 51-54 */
+
+retcode-item "cn=busy" 0x33
+retcode-item "cn=unavailable" 0x34
+retcode-item "cn=unwillingToPerform" 0x35
+retcode-item "cn=loopDetect" 0x36
+
+# LDAP_UPDATE_ERROR(n) LDAP_RANGE((n),0x40,0x47) /* 64-69,71 */
+
+retcode-item "cn=namingViolation" 0x40
+retcode-item "cn=objectClassViolation" 0x41
+retcode-item "cn=notAllowedOnNonleaf" 0x42
+retcode-item "cn=notAllowedOnRDN" 0x43
+retcode-item "cn=entryAlreadyExists" 0x44
+retcode-item "cn=objectClassModsProhibited" 0x45
+retcode-item "cn=resultsTooLarge" 0x46 text="CLDAP"
+retcode-item "cn=affectsMultipleDSAs" 0x47 text="LDAPv3"
+
+retcode-item "cn=other" 0x50
+
+# /* LCUP operation codes (113-117) - not implemented */
+retcode-item "cn=cupResourcesExhausted" 0x71
+retcode-item "cn=cupSecurityViolation" 0x72
+retcode-item "cn=cupInvalidData" 0x73
+retcode-item "cn=cupUnsupportedScheme" 0x74
+retcode-item "cn=cupReloadRequired" 0x75
+
+# /* Cancel operation codes (118-121) */
+retcode-item "cn=cancelled" 0x76
+retcode-item "cn=noSuchOperation" 0x77
+retcode-item "cn=tooLate" 0x78
+retcode-item "cn=cannotCancel" 0x79
+
+
+# /* Experimental result codes */
+# LDAP_E_ERROR(n) LDAP_RANGE((n),0x1000,0x3FFF) /* experimental */
+# LDAP_X_ERROR(n) LDAP_RANGE((n),0x4000,0xFFFF) /* private use */
+
+# /* for the LDAP Sync operation */
+retcode-item "cn=syncRefreshRequired" 0x4100
+
+# /* for the LDAP No-Op control */
+retcode-item "cn=noOperation" 0x410e
+
+# /* for the Assertion control */
+retcode-item "cn=assertionFailed" 0x410f
+
+# /* for the Chaining Behavior control (consecutive result codes requested;
+# * see <draft-sermersheim-ldap-chaining> ) */
+retcode-item "cn=noReferralsFound" 0x4110
+retcode-item "cn=cannotChain" 0x4111
+
diff --git a/tests/data/rootdse.ldif b/tests/data/rootdse.ldif
new file mode 100644
index 0000000..6bad226
--- /dev/null
+++ b/tests/data/rootdse.ldif
@@ -0,0 +1,2 @@
+dn:
+vendorName: The OpenLDAP Project <http://www.openldap.org/>
diff --git a/tests/data/search.out.provider b/tests/data/search.out.provider
new file mode 100644
index 0000000..c48cd1f
--- /dev/null
+++ b/tests/data/search.out.provider
@@ -0,0 +1,389 @@
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+title: Mythical Manager, Research Systems
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+title: Director, Embedded Systems
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=Manager,dc=example,dc=com
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+uid: uham
+
diff --git a/tests/data/search.out.xsearch b/tests/data/search.out.xsearch
new file mode 100644
index 0000000..196bab1
--- /dev/null
+++ b/tests/data/search.out.xsearch
@@ -0,0 +1,682 @@
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+
+dn: cn=Manager,dc=example,dc=com
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+
+dn: dc=example,dc=com
+o: Example, Inc.
+
+dn: dc=example,dc=com
+dc: example
+o: Example, Inc.
+
+dn: dc=example,dc=com
+dc: example
+
+dn: cn=Subschema
+attributeTypes: ( 0.9.2342.19200300.100.1.25 NAME ( 'dc' 'domainComponent' ) D
+ ESC 'RFC1274/2247: domain component' EQUALITY caseIgnoreIA5Match SUBSTR caseI
+ gnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+
+dn: dc=example,dc=com
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+
diff --git a/tests/data/slapd-2db.conf b/tests/data/slapd-2db.conf
new file mode 100644
index 0000000..9ad7fa0
--- /dev/null
+++ b/tests/data/slapd-2db.conf
@@ -0,0 +1,50 @@
+# stand-alone slapd config -- for testing (with indexing)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "cn=Everyone,ou=Groups,dc=example,dc=com"
+subordinate
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.b
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/slapd-aci.conf b/tests/data/slapd-aci.conf
new file mode 100644
index 0000000..fac3408
--- /dev/null
+++ b/tests/data/slapd-aci.conf
@@ -0,0 +1,56 @@
+# stand-alone slapd config -- for testing (with indexing)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+include @DATADIR@/test.schema
+
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#acimod#modulepath ../servers/slapd/
+#acimod#moduleload aci.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+access to dn=""
+ by * read
+
+access to dn="cn=Subschema"
+ by * read
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+access to dn.subtree="dc=example,dc=com"
+ by dynacl/aci write
+
+database monitor
+rootdn "cn=Monitor"
diff --git a/tests/data/slapd-acl.conf b/tests/data/slapd-acl.conf
new file mode 100644
index 0000000..7afdc03
--- /dev/null
+++ b/tests/data/slapd-acl.conf
@@ -0,0 +1,144 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+# global ACLs
+#
+# normal installations should protect root dse, cn=monitor, cn=subschema
+#
+
+access to dn.exact="" attrs=objectClass
+ by users read
+access to *
+ by * read
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+add_content_acl on
+#access to attrs=objectclass dn.subtree="dc=example,dc=com"
+access to attrs=objectclass
+ by dn.exact="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" add
+ by * =rsc stop
+
+#access to filter="(objectclass=person)" attrs=userpassword dn.subtree="dc=example,dc=com"
+access to filter="(objectclass=person)" attrs=userpassword
+ by anonymous auth
+ by self =wx
+
+access to dn.exact="cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com"
+ attrs=cn val="Mark A Elliot"
+ by dn="cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" read
+ by * break
+
+access to dn.exact="cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com"
+ attrs=cn val="Mark Elliot"
+ by dn="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" read
+ by * break
+
+access to dn.exact="cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com"
+ attrs=cn
+ by * search
+
+access to dn.exact="cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com"
+ attrs=cn val.regex="^John D.+"
+ by dn="cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" read
+ by * break
+
+access to dn.exact="cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com"
+ attrs=cn val.regex="^Jonath.+"
+ by dn="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" read
+ by * break
+
+access to dn.exact="cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com"
+ attrs=cn
+ by * search
+
+access to dn.onelevel="ou=Information Technology Division,ou=People,dc=example,dc=com"
+ filter="(cn=*Jensen)"
+ attrs=cn val.regex=".*Jensen$"
+ by dn="cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" read
+ by dn="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" read
+ by * break
+
+access to dn.exact="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+ attrs=cn
+ by * search
+
+access to dn.children="ou=Alumni Association,ou=People,dc=example,dc=com"
+ by dn.regex=".+,dc=example,dc=com" +c continue
+ by dn.subtree="dc=example,dc=com" +rs continue
+ by dn.children="dc=example,dc=com" +d continue
+ by * stop
+
+#access to attrs=member,uniquemember dn.subtree="dc=example,dc=com"
+access to attrs=member,uniquemember
+ by dn.exact="cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com" selfwrite
+ by dnattr=member selfwrite
+ by dnattr=uniquemember selfwrite
+ by * read
+
+#access to attrs=member,uniquemember filter="(mail=*com)" dn.subtree="dc=example,dc=com"
+access to attrs=member,uniquemember filter="(mail=*com)"
+ by * read
+
+#access to filter="(|(objectclass=groupofnames)(objectClass=groupofuniquenames))" dn.subtree="dc=example,dc=com"
+access to filter="(|(objectclass=groupofnames)(objectClass=groupofuniquenames))"
+ by dn.exact="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" =sc continue
+ by dn.regex="^cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com$" +rw stop
+ by * break
+
+access to dn.children="ou=Information Technology Division,ou=People,dc=example,dc=com"
+ by group/groupOfUniqueNames/uniqueMember.exact="cn=ITD Staff,ou=Groups,dc=example,dc=com" write
+ by * read
+
+access to dn.exact="cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com"
+ by set="[cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com]/member* & user" write
+ by * read
+
+#access to filter="(name=X*Y*Z)" dn.subtree="dc=example,dc=com"
+access to filter="(name=X*Y*Z)"
+ by * continue
+
+access to dn.subtree="ou=Add & Delete,dc=example,dc=com"
+ by dn.exact="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" add
+ by dn.exact="cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" delete
+ by dn.exact="cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com" write
+ by * read
+
+# fall into global ACLs
+
+database monitor
diff --git a/tests/data/slapd-asyncmeta.conf b/tests/data/slapd-asyncmeta.conf
new file mode 100644
index 0000000..45a793e
--- /dev/null
+++ b/tests/data/slapd-asyncmeta.conf
@@ -0,0 +1,85 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.m.pid
+argsfile @TESTDIR@/slapd.m.args
+
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+#asyncmetamod#modulepath ../servers/slapd/back-asyncmeta/
+#asyncmetamod#moduleload back_asyncmeta.la
+
+# seems to improve behavior under very heavy load
+# (i.e. it alleviates load on target systems)
+threads 8
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database asyncmeta
+suffix "o=Example,c=US"
+rootdn "cn=Manager,o=Example,c=US"
+rootpw secret
+chase-referrals no
+#nretries forever
+nretries 100
+#norefs true
+network-timeout 500
+#max-timeout-ops 50
+#max-pending-ops 128
+#max-target-conns 16
+
+# local
+uri "@URI2@ou=Meta,o=Example,c=US"
+subtree-exclude "ou=Excluded,ou=Meta,o=Example,c=US"
+suffixmassage "ou=Meta,o=Example,c=US" "ou=Meta,dc=example,dc=com"
+###pseudorootdn "cn=manager,ou=meta,dc=example,dc=com"
+###pseudorootpw secret
+idassert-bind bindmethod=simple
+ binddn="cn=manager,ou=meta,dc=example,dc=com"
+ credentials="secret"
+ mode=self
+ flags=non-prescriptive
+idassert-authzFrom "dn.exact:cn=Manager,o=Local"
+
+# remote
+uri "@URI1@o=Example,c=US"
+subtree-include "dn.subtree:o=Example,c=US"
+suffixmassage "o=Example,c=US" "dc=example,dc=com"
+###pseudorootdn "cn=manager,dc=example,dc=com"
+###pseudorootpw secret
+idassert-bind bindmethod=simple
+ binddn="cn=manager,dc=example,dc=com"
+ credentials="secret"
+ mode=self
+ flags=non-prescriptive
+idassert-authzFrom "dn.exact:cn=Manager,o=Local"
+
+limits dn.exact="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Example,c=US" time=1 size=8
+
+# This is only for binding as the rootdn
+database asyncmeta
+suffix "o=Local"
+rootdn "cn=Manager,o=Local"
+rootpw secret
+uri "@URI6@o=Local"
+
+database monitor
diff --git a/tests/data/slapd-cache-provider-proxyauthz.conf b/tests/data/slapd-cache-provider-proxyauthz.conf
new file mode 100644
index 0000000..500fe28
--- /dev/null
+++ b/tests/data/slapd-cache-provider-proxyauthz.conf
@@ -0,0 +1,45 @@
+# provider slapd config -- for proxy cache testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+disallow bind_anon
+
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/slapd-cache-provider.conf b/tests/data/slapd-cache-provider.conf
new file mode 100644
index 0000000..ace021c
--- /dev/null
+++ b/tests/data/slapd-cache-provider.conf
@@ -0,0 +1,42 @@
+# provider slapd config -- for proxy cache testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/slapd-chain1.conf b/tests/data/slapd-chain1.conf
new file mode 100644
index 0000000..de36504
--- /dev/null
+++ b/tests/data/slapd-chain1.conf
@@ -0,0 +1,61 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+
+#
+# uses the chain overlay as global;
+# no chain-URI is configured, so the URI is parsed out of the referral
+overlay chain
+chain-uri @URI2@
+chain-idassert-bind bindmethod=simple
+ binddn="cn=Manager,dc=example,dc=com"
+ credentials=secret
+ mode=self
+ flags=non-prescriptive
+
+#######################################################################
+# database definitions
+#######################################################################
+
+#
+# normal installations should protect root dse,
+# cn=monitor, cn=schema, and cn=config
+#
+
+database @BACKEND@
+
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#null#bind on
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/slapd-chain2.conf b/tests/data/slapd-chain2.conf
new file mode 100644
index 0000000..7aee0be
--- /dev/null
+++ b/tests/data/slapd-chain2.conf
@@ -0,0 +1,60 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.2.pid
+argsfile @TESTDIR@/slapd.2.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+#
+# normal installations should protect root dse,
+# cn=monitor, cn=schema, and cn=config
+#
+
+database @BACKEND@
+
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.2.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
+
+#
+# uses the chain overlay as database specific;
+# the chain-URI is configured, so only that URI is chained
+overlay chain
+chain-uri @URI1@
+chain-idassert-bind bindmethod=simple
+ binddn="cn=Manager,dc=example,dc=com"
+ credentials=secret
+ mode=self
+ flags=non-prescriptive
+
+database monitor
diff --git a/tests/data/slapd-component.conf b/tests/data/slapd-component.conf
new file mode 100644
index 0000000..59d22e5
--- /dev/null
+++ b/tests/data/slapd-component.conf
@@ -0,0 +1,46 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+
+include @DATADIR@/test.schema
+include @DATADIR@/ditcontentrules.conf
+
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+moduleload @DATADIR@/comp_libs/compmatch.la
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/slapd-config-naked.conf b/tests/data/slapd-config-naked.conf
new file mode 100644
index 0000000..0abcff2
--- /dev/null
+++ b/tests/data/slapd-config-naked.conf
@@ -0,0 +1,12 @@
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+database monitor
+
+database config
+include @TESTDIR@/configpw.conf
diff --git a/tests/data/slapd-config-undo.conf b/tests/data/slapd-config-undo.conf
new file mode 100644
index 0000000..1319d34
--- /dev/null
+++ b/tests/data/slapd-config-undo.conf
@@ -0,0 +1,19 @@
+include @SCHEMADIR@/core.schema
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+database @BACKEND@
+suffix "o=undo"
+rootdn "cn=Manager,o=undo"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
+
+database config
+include @TESTDIR@/configpw.conf
diff --git a/tests/data/slapd-dds.conf b/tests/data/slapd-dds.conf
new file mode 100644
index 0000000..3dff7ac
--- /dev/null
+++ b/tests/data/slapd-dds.conf
@@ -0,0 +1,85 @@
+# stand-alone slapd config -- for testing (with indexing)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2005-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+include @DATADIR@/test.schema
+
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#ddsmod#modulepath ../servers/slapd/overlays/
+#ddsmod#moduleload dds.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#indexdb#index entryExpireTimestamp eq
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+add_content_acl on
+
+overlay dds
+dds-max-ttl 1d
+dds-min-ttl 10s
+dds-default-ttl 1h
+dds-interval 5s
+dds-tolerance 1s
+
+# This is to test the meeting feature
+access to attrs=userPassword
+ by self write
+ by * read
+
+access to dn.base="ou=Groups,dc=example,dc=com"
+ attrs=children
+ by users write
+
+access to dn.onelevel="ou=Groups,dc=example,dc=com"
+ attrs=entryTtl
+ by dnattr=member manage
+ by * read
+
+access to dn.onelevel="ou=Groups,dc=example,dc=com"
+ by dnattr=creatorsName write
+ by * break
+
+access to dn.onelevel="ou=Groups,dc=example,dc=com"
+ attrs=entry
+ by * read
+
+access to dn.onelevel="ou=Groups,dc=example,dc=com"
+ attrs=member
+ by users selfwrite
+ by * read
+
+access to *
+ by * read
+
+database monitor
diff --git a/tests/data/slapd-deltasync-consumer.conf b/tests/data/slapd-deltasync-consumer.conf
new file mode 100644
index 0000000..27be49b
--- /dev/null
+++ b/tests/data/slapd-deltasync-consumer.conf
@@ -0,0 +1,74 @@
+# consumer slapd config -- for testing of Delta SYNC replication
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+#
+pidfile @TESTDIR@/slapd.2.pid
+argsfile @TESTDIR@/slapd.2.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#syncprovmod#modulepath ../servers/slapd/overlays/
+#syncprovmod#moduleload syncprov.la
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+
+#ldapyes#overlay chain
+#ldapyes#chain-uri @URI1@
+#ldapyes#chain-idassert-bind bindmethod=simple binddn="cn=Manager,dc=example,dc=com" credentials=secret mode=self
+#ldapmod#overlay chain
+#ldapmod#chain-uri @URI1@
+#ldapmod#chain-idassert-bind bindmethod=simple binddn="cn=Manager,dc=example,dc=com" credentials=secret mode=self
+
+#######################################################################
+# consumer database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=consumer,dc=example,dc=com"
+rootpw secret
+#null#bind on
+#~null~#directory @TESTDIR@/db.2.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_3
+#ndb#include @DATADIR@/ndb.conf
+
+# Don't change syncrepl spec yet
+syncrepl rid=1
+ provider=@URI1@
+ binddn="cn=Manager,dc=example,dc=com"
+ bindmethod=simple
+ credentials=secret
+ searchbase="dc=example,dc=com"
+ filter="(objectClass=*)"
+ logbase="cn=log"
+ logfilter="(&(objectClass=auditWriteObject)(reqResult=0))"
+ syncdata=accesslog
+ attrs="*,+"
+ schemachecking=off
+ scope=sub
+ type=refreshAndPersist
+ retry="3 +" interval=00:00:00:03
+updateref @URI1@
+
+overlay syncprov
+
+database monitor
diff --git a/tests/data/slapd-deltasync-provider.conf b/tests/data/slapd-deltasync-provider.conf
new file mode 100644
index 0000000..14327d1
--- /dev/null
+++ b/tests/data/slapd-deltasync-provider.conf
@@ -0,0 +1,77 @@
+# provider slapd config -- for testing of Delta SYNC replication
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#syncprovmod#modulepath ../servers/slapd/overlays/
+#syncprovmod#moduleload syncprov.la
+#accesslogmod#modulepath ../servers/slapd/overlays/
+#accesslogmod#moduleload accesslog.la
+
+database config
+include @TESTDIR@/configpw.conf
+
+
+#######################################################################
+# provider database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "cn=log"
+rootdn "cn=Manager,dc=example,dc=com"
+#~null~#directory @TESTDIR@/db.1.b
+#indexdb#index objectClass eq
+#indexdb#index entryUUID,entryCSN eq
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
+
+overlay syncprov
+syncprov-reloadhint true
+syncprov-nopresent true
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#indexdb#index entryUUID,entryCSN eq
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+
+access to *
+ by users write
+ by * read
+
+overlay syncprov
+#syncprov-sessionlog 100
+
+overlay accesslog
+logdb cn=log
+logops writes
+logsuccess true
+
+database monitor
diff --git a/tests/data/slapd-deref.conf b/tests/data/slapd-deref.conf
new file mode 100644
index 0000000..b874c45
--- /dev/null
+++ b/tests/data/slapd-deref.conf
@@ -0,0 +1,48 @@
+# stand-alone slapd config -- for testing (with deref overlay)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2004-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#derefmod#moduleload ../servers/slapd/overlays/deref.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "o=deref"
+rootdn "cn=Manager,o=deref"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+overlay deref
+
+database config
+include @TESTDIR@/configpw.conf
+
+database monitor
diff --git a/tests/data/slapd-dirsync1.conf b/tests/data/slapd-dirsync1.conf
new file mode 100644
index 0000000..4a3f8b0
--- /dev/null
+++ b/tests/data/slapd-dirsync1.conf
@@ -0,0 +1,65 @@
+# consumer slapd config -- for testing of MSAD DIRSYNC replication
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/nis.schema
+include @SCHEMADIR@/msuser.schema
+#
+pidfile @TESTDIR@/slapd.2.pid
+argsfile @TESTDIR@/slapd.2.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#syncprovmod#modulepath ../servers/slapd/overlays/
+#syncprovmod#moduleload syncprov.la
+
+attributeoptions range=
+
+#######################################################################
+# consumer database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "@MSAD_SUFFIX@"
+rootdn "cn=Replica,@BASEDN@"
+rootpw secret
+#null#bind on
+#~null~#directory @TESTDIR@/db.2.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#indexdb#index entryUUID,entryCSN eq
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
+
+# Don't change syncrepl spec yet
+syncrepl rid=1
+ provider=@URI1@
+ binddn="@MSAD_ADMINDN@"
+ bindmethod=simple
+ credentials="@MSAD_ADMINPW@"
+ searchbase="@MSAD_SUFFIX@"
+ filter="(|(associatedDomain=test.openldap.org)(objectclass=inetorgperson)(objectclass=groupofnames)(objectclass=groupofuniquenames))"
+ schemachecking=off
+ scope=sub
+ type=dirSync
+ interval=00:00:00:03
+updateref @URI1@
+
+overlay syncprov
+syncprov-sessionlog 100
+
+database monitor
diff --git a/tests/data/slapd-dn.conf b/tests/data/slapd-dn.conf
new file mode 100644
index 0000000..c815ee1
--- /dev/null
+++ b/tests/data/slapd-dn.conf
@@ -0,0 +1,44 @@
+# stand-alone slapd config -- for testing DNs
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2004-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+include @DATADIR@/test.schema
+
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/slapd-dnssrv.conf b/tests/data/slapd-dnssrv.conf
new file mode 100644
index 0000000..048f82e
--- /dev/null
+++ b/tests/data/slapd-dnssrv.conf
@@ -0,0 +1,33 @@
+# DNS SRV slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+pidfile @TESTDIR@/slapd.pid
+argsfile @TESTDIR@/slapd.args
+
+sasl-secprops noanonymous
+#sasl-secprops none
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database monitor
+
+database dnssrv
+suffix ""
diff --git a/tests/data/slapd-dsee-consumer1.conf b/tests/data/slapd-dsee-consumer1.conf
new file mode 100644
index 0000000..7b27b9f
--- /dev/null
+++ b/tests/data/slapd-dsee-consumer1.conf
@@ -0,0 +1,61 @@
+# consumer slapd config -- for testing of SYNC replication
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+include @SCHEMADIR@/dsee.schema
+#
+pidfile @TESTDIR@/slapd.2.pid
+argsfile @TESTDIR@/slapd.2.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# consumer database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Replica,dc=example,dc=com"
+rootpw secret
+#null#bind on
+#~null~#directory @TESTDIR@/db.2.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#indexdb#index entryUUID,entryCSN eq
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
+
+# Don't change syncrepl spec yet
+syncrepl rid=1
+ provider=@URI1@
+ binddn="cn=Directory Manager"
+ bindmethod=simple
+ credentials=secret21
+ searchbase="dc=example,dc=com"
+ filter="(objectClass=*)"
+ schemachecking=off
+ scope=sub
+ type=refreshOnly
+ logbase="cn=changelog"
+ syncdata=changelog
+ retry="3 +" interval=00:00:00:03
+updateref @URI1@
+
+database monitor
diff --git a/tests/data/slapd-dsee-consumer2.conf b/tests/data/slapd-dsee-consumer2.conf
new file mode 100644
index 0000000..966b1ce
--- /dev/null
+++ b/tests/data/slapd-dsee-consumer2.conf
@@ -0,0 +1,61 @@
+# consumer slapd config -- for testing of SYNC replication
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+include @SCHEMADIR@/dsee.schema
+#
+pidfile @TESTDIR@/slapd.4.pid
+argsfile @TESTDIR@/slapd.4.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# consumer database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Replica,dc=example,dc=com"
+rootpw secret
+#null#bind on
+#~null~#directory @TESTDIR@/db.4.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#indexdb#index entryUUID,entryCSN eq
+#ndb#dbname db_4
+#ndb#include @DATADIR@/ndb.conf
+
+# Don't change syncrepl spec yet
+syncrepl rid=1
+ provider=@URI1@
+ binddn="cn=Directory Manager"
+ bindmethod=simple
+ credentials=secret21
+ searchbase="dc=example,dc=com"
+ filter="(objectClass=*)"
+ schemachecking=off
+ scope=sub
+ type=refreshAndPersist
+ logbase="cn=changelog"
+ syncdata=changelog
+ retry="3 5 300 5"
+updateref @URI1@
+
+database monitor
diff --git a/tests/data/slapd-dynamic.ldif b/tests/data/slapd-dynamic.ldif
new file mode 100644
index 0000000..166e58f
--- /dev/null
+++ b/tests/data/slapd-dynamic.ldif
@@ -0,0 +1,8 @@
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW:< file://@TESTDIR@/configpw
diff --git a/tests/data/slapd-dynlist.conf b/tests/data/slapd-dynlist.conf
new file mode 100644
index 0000000..25cc9aa
--- /dev/null
+++ b/tests/data/slapd-dynlist.conf
@@ -0,0 +1,54 @@
+# stand-alone slapd config -- for testing (with indexing)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+include @SCHEMADIR@/dyngroup.schema
+include @DATADIR@/test.schema
+
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#dynlistmod#modulepath ../servers/slapd/overlays/
+#dynlistmod#moduleload dynlist.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database monitor
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+# we'll reconfigure the attrset dynamically
+overlay dynlist
+dynlist-attrset groupOfURLs memberURL
+
+database config
+include @TESTDIR@/configpw.conf
diff --git a/tests/data/slapd-emptydn.conf b/tests/data/slapd-emptydn.conf
new file mode 100644
index 0000000..758b401
--- /dev/null
+++ b/tests/data/slapd-emptydn.conf
@@ -0,0 +1,77 @@
+# stand-alone slapd config -- for testing empty DNs
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2004-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+access to dn.exact=""
+ by * read
+
+access to dn.exact="cn=Subschema"
+ by * read
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+access to attrs=userPassword
+ by dn.exact="cn=Manager,c=US" write
+ by self =wx
+ by * =x
+
+access to dn.subtree="dc=example,dc=com"
+ by dn.exact="cn=Manager,c=US" write
+ by * read
+
+database monitor
+access to dn.subtree="cn=Monitor"
+ by * read
+
+database @BACKEND@
+suffix ""
+rootdn "cn=Manager,c=US"
+rootpw secret
+#~null~#directory @TESTDIR@/db.2.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
+
+access to attrs=userPassword
+ by self =wx
+ by * =x
+
+access to dn.subtree=""
+ by * read
diff --git a/tests/data/slapd-glue-ldap.conf b/tests/data/slapd-glue-ldap.conf
new file mode 100644
index 0000000..3474bd2
--- /dev/null
+++ b/tests/data/slapd-glue-ldap.conf
@@ -0,0 +1,79 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.m.pid
+argsfile @TESTDIR@/slapd.m.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#relaymod#modulepath ../servers/slapd/back-relay/
+#relaymod#moduleload back_relay.la
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+#metamod#modulepath ../servers/slapd/back-meta/
+#metamod#moduleload back_meta.la
+#rwmmod#modulepath ../servers/slapd/overlays/
+#rwmmod#moduleload rwm.la
+
+overlay rwm
+rwm-suffixmassage "o=Example,c=US" "dc=example,dc=com"
+
+#######################################################################
+# database definitions
+#######################################################################
+
+# remote
+database ldap
+suffix "ou=Meta,dc=example,dc=com"
+subordinate
+uri "@URI2@"
+rootdn "cn=Manager,dc=example,dc=com"
+chase-referrals no
+idassert-bind bindmethod=simple
+ binddn="cn=Manager,ou=Meta,dc=example,dc=com"
+ credentials="secret"
+ mode=self
+ flags=non-prescriptive
+idassert-authzfrom "dn.exact:cn=Manager,o=Local"
+
+# local
+database ldap
+suffix "dc=example,dc=com"
+uri "@URI1@"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+chase-referrals no
+idassert-bind bindmethod=simple
+ binddn="cn=Manager,dc=example,dc=com"
+ credentials="secret"
+ mode=self
+ flags=non-prescriptive
+idassert-authzfrom "dn.exact:cn=Manager,o=Local"
+
+limits dn.exact="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" time=1 size=8
+
+# This is only for binding as the rootdn
+database ldap
+suffix "o=Local"
+rootdn "cn=Manager,o=Local"
+rootpw secret
+uri "@URI6@"
+
+database monitor
diff --git a/tests/data/slapd-glue-syncrepl1.conf b/tests/data/slapd-glue-syncrepl1.conf
new file mode 100644
index 0000000..5418576
--- /dev/null
+++ b/tests/data/slapd-glue-syncrepl1.conf
@@ -0,0 +1,87 @@
+# stand-alone slapd config -- for backglue testing (with indexing)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#syncprovmod#moduleload ../servers/slapd/overlays/syncprov.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "ou=Information Technology Division,ou=People,dc=example,dc=com"
+subordinate
+rootdn "cn=Manager 1,dc=example,dc=com"
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectclass eq
+#indexdb#index uid pres,eq,sub
+#indexdb#index cn,sn pres,eq,sub,subany
+#indexdb#index entryUUID,entryCSN pres
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+overlay syncprov
+
+database @BACKEND@
+suffix "ou=Groups,dc=example,dc=com"
+subordinate
+rootdn "cn=Manager 1,dc=example,dc=com"
+#~null~#directory @TESTDIR@/db.1.b
+#indexdb#index objectclass eq
+#indexdb#index uid pres,eq,sub
+#indexdb#index cn,sn pres,eq,sub,subany
+#indexdb#index entryUUID,entryCSN pres
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
+
+syncrepl rid=1
+ provider=@URI2@
+ binddn="cn=Manager 2,dc=example,dc=com"
+ bindmethod=simple
+ credentials=secret
+ searchbase="ou=Groups,dc=example,dc=com"
+ filter="(objectClass=*)"
+ attrs="*,+"
+ schemachecking=off
+ scope=sub
+ type=refreshAndPersist
+ retry="3 10 300 5"
+updateref @URI2@
+#overlay syncprov
+
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager 1,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.c
+#indexdb#index objectclass eq
+#indexdb#index uid pres,eq,sub
+#indexdb#index cn,sn pres,eq,sub,subany
+#ndb#dbname db_3
+#ndb#include @DATADIR@/ndb.conf
+
+#overlay syncprov
+
+database monitor
diff --git a/tests/data/slapd-glue-syncrepl2.conf b/tests/data/slapd-glue-syncrepl2.conf
new file mode 100644
index 0000000..c8f2c5a
--- /dev/null
+++ b/tests/data/slapd-glue-syncrepl2.conf
@@ -0,0 +1,90 @@
+# stand-alone slapd config -- for backglue testing (with indexing)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.2.pid
+argsfile @TESTDIR@/slapd.2.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#syncprovmod#moduleload ../servers/slapd/overlays/syncprov.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "ou=Information Technology Division,ou=People,dc=example,dc=com"
+subordinate
+rootdn "cn=Manager 2,dc=example,dc=com"
+#~null~#directory @TESTDIR@/db.2.a
+#indexdb#index objectclass eq
+#indexdb#index uid pres,eq,sub
+#indexdb#index cn,sn pres,eq,sub,subany
+#indexdb#index entryUUID,entryCSN pres
+#ndb#dbname db_4
+#ndb#include @DATADIR@/ndb.conf
+
+
+syncrepl rid=2
+ provider=@URI1@
+ binddn="cn=Manager 1,dc=example,dc=com"
+ bindmethod=simple
+ credentials=secret
+ searchbase="ou=Information Technology Division,ou=People,dc=example,dc=com"
+ filter="(objectClass=*)"
+ attrs="*,+"
+ schemachecking=off
+ scope=sub
+ type=refreshAndPersist
+ retry="3 10 300 5"
+updateref @URI1@
+#overlay syncprov
+
+database @BACKEND@
+suffix "ou=Groups,dc=example,dc=com"
+subordinate
+rootdn "cn=Manager 2,dc=example,dc=com"
+#~null~#directory @TESTDIR@/db.2.b
+#indexdb#index objectclass eq
+#indexdb#index uid pres,eq,sub
+#indexdb#index cn,sn pres,eq,sub,subany
+#indexdb#index entryUUID,entryCSN pres
+#ndb#dbname db_5
+#ndb#include @DATADIR@/ndb.conf
+
+
+overlay syncprov
+
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager 2,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.2.c
+#indexdb#index objectclass eq
+#indexdb#index uid pres,eq,sub
+#indexdb#index cn,sn pres,eq,sub,subany
+#ndb#dbname db_6
+#ndb#include @DATADIR@/ndb.conf
+
+
+#overlay syncprov
+
+database monitor
diff --git a/tests/data/slapd-glue.conf b/tests/data/slapd-glue.conf
new file mode 100644
index 0000000..678b7f5
--- /dev/null
+++ b/tests/data/slapd-glue.conf
@@ -0,0 +1,64 @@
+# stand-alone slapd config -- for backglue testing (with indexing)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "ou=Information Technology Division,ou=People,dc=example,dc=com"
+subordinate
+rootdn "cn=Manager, dc=example,dc=com"
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectclass eq
+#indexdb#index uid pres,eq,sub
+#indexdb#index cn,sn pres,eq,sub,subany
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+database @BACKEND@
+suffix "ou=Groups,dc=example,dc=com"
+subordinate
+rootdn "cn=Manager, dc=example,dc=com"
+#~null~#directory @TESTDIR@/db.1.b
+#indexdb#index objectclass eq
+#indexdb#index uid pres,eq,sub
+#indexdb#index cn,sn pres,eq,sub,subany
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager, dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.c
+#indexdb#index objectclass eq
+#indexdb#index uid pres,eq,sub
+#indexdb#index cn,sn pres,eq,sub,subany
+#ndb#dbname db_3
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/slapd-homedir.conf b/tests/data/slapd-homedir.conf
new file mode 100644
index 0000000..4d1e738
--- /dev/null
+++ b/tests/data/slapd-homedir.conf
@@ -0,0 +1,57 @@
+# stand-alone slapd config -- for testing (with deref overlay)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2004-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#homedirmod#moduleload ../servers/slapd/overlays/homedir.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#null#bind on
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#mdb#maxsize 33554432
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+overlay homedir
+
+homedir-min-uidnumber @MINUID@
+homedir-skeleton-path @DATADIR@/homedir/skel
+homedir-regexp ^(/home/[-_/a-z0-9]+)$ @TESTDIR@/$1
+homedir-delete-style ARCHIVE
+homedir-archive-path @TESTDIR@/archive
+
+database config
+include @TESTDIR@/configpw.conf
+
+database monitor
diff --git a/tests/data/slapd-idassert.conf b/tests/data/slapd-idassert.conf
new file mode 100644
index 0000000..d636443
--- /dev/null
+++ b/tests/data/slapd-idassert.conf
@@ -0,0 +1,125 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+#ucdata-path ./ucdata
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+#rwmmod#modulepath ../servers/slapd/overlays/
+#rwmmod#moduleload rwm.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+authz-policy both
+authz-regexp "^uid=manager,.+" "cn=Manager,dc=example,dc=com"
+authz-regexp "^uid=admin/([^,]+),.+" "ldap:///ou=Admin,dc=example,dc=com??sub?(cn=$1)"
+authz-regexp "^uid=it/([^,]+),.+" "ldap:///ou=People,dc=example,dc=it??sub?(uid=$1)"
+authz-regexp "^uid=(us/)?([^,]+),.+" "ldap:///ou=People,dc=example,dc=com??sub?(uid=$2)"
+
+#
+# normal installations should protect root dse,
+# cn=monitor, cn=schema, and cn=config
+#
+
+access to attrs=userpassword
+ by self =wx
+ by anonymous =x
+
+access to dn.exact=""
+ by * read
+
+access to *
+ by users read
+ by * search
+
+database @BACKEND@
+
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#null#bind on
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+access to dn.exact="cn=Proxy,ou=Admin,dc=example,dc=com"
+ attrs=authzTo
+ by dn.exact="cn=Proxy,ou=Admin,dc=example,dc=com" =wx
+ by * =x
+
+database @BACKEND@
+
+suffix "dc=example,dc=it"
+rootdn "cn=Manager,dc=example,dc=it"
+rootpw secret
+#~null~#directory @TESTDIR@/db.2.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
+
+database ldap
+suffix "o=Example,c=US"
+uri "@URI1@"
+
+#sasl#idassert-bind bindmethod=sasl binddn="cn=Proxy US,ou=Admin,dc=example,dc=com" authcId="admin/proxy US" credentials="proxy" @SASL_MECH@ mode=self
+#nosasl#idassert-bind bindmethod=simple binddn="cn=Proxy US,ou=Admin,dc=example,dc=com" credentials="proxy" mode=self
+
+# authorizes database
+idassert-authzFrom "dn.subtree:dc=example,dc=it"
+
+overlay rwm
+rwm-suffixmassage "dc=example,dc=com"
+
+database ldap
+suffix "o=Esempio,c=IT"
+uri "@URI1@"
+
+acl-bind bindmethod=simple binddn="cn=Proxy IT,ou=Admin,dc=example,dc=com" credentials="proxy"
+idassert-bind bindmethod=simple binddn="cn=Proxy IT,ou=Admin,dc=example,dc=com" credentials="proxy" authzId="dn:cn=Sandbox,ou=Admin,dc=example,dc=com"
+
+# authorizes database
+idassert-authzFrom "dn.subtree:dc=example,dc=com"
+# authorizes anonymous
+idassert-authzFrom "dn.exact:"
+
+overlay rwm
+rwm-suffixmassage "dc=example,dc=com"
+
+access to attrs=entry,cn,sn,mail
+ by users read
+
+access to *
+ by dn.exact="cn=Proxy IT,ou=Admin,o=Esempio,c=IT" read
+ by group.exact="cn=Authorizable,ou=Groups,o=Esempio,c=IT" read
+ by dn.exact="cn=Sandbox,ou=Admin,dc=example,dc=com" search
+ by * none
+
+database monitor
+rootdn "cn=monitor"
+rootpw monitor
diff --git a/tests/data/slapd-ldapglue.conf b/tests/data/slapd-ldapglue.conf
new file mode 100644
index 0000000..3e7c392
--- /dev/null
+++ b/tests/data/slapd-ldapglue.conf
@@ -0,0 +1,77 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+#ucdata-path ./ucdata
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+authz-regexp "^uid=([^,]+),.*" "uid=$1,ou=People,dc=example,dc=com"
+
+#
+# normal installations should protect root dse,
+# cn=monitor, cn=schema, and cn=config
+#
+
+access to attrs=userpassword
+ by self =wx
+ by anonymous =x
+
+access to *
+ by * read
+
+# groups branch
+database ldap
+suffix "ou=Groups,dc=example,dc=com"
+subordinate
+uri "@URI3@"
+# FIXME: doesn't work with authz=native
+#sasl#idassert-bind bindmethod=sasl authcid=proxy credentials=proxy @SASL_MECH@ mode=self
+#nosasl#idassert-bind bindmethod=simple binddn="uid=proxy,ou=Groups,dc=example,dc=com" credentials=proxy mode=self
+
+# people branch
+database ldap
+suffix "ou=People,dc=example,dc=com"
+subordinate
+uri "@URI2@"
+# FIXME: doesn't work with authz=native
+#sasl#idassert-bind bindmethod=sasl authcid=proxy credentials=proxy @SASL_MECH@ mode=self
+#nosasl#idassert-bind bindmethod=simple binddn="uid=proxy,ou=People,dc=example,dc=com" credentials=proxy mode=self
+
+# root
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/slapd-ldapgluegroups.conf b/tests/data/slapd-ldapgluegroups.conf
new file mode 100644
index 0000000..9569bc3
--- /dev/null
+++ b/tests/data/slapd-ldapgluegroups.conf
@@ -0,0 +1,59 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+#ucdata-path ./ucdata
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.3.pid
+argsfile @TESTDIR@/slapd.3.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+authz-policy to
+authz-regexp "^uid=([^,]+),.*" "uid=$1,ou=Groups,dc=example,dc=com"
+
+#
+# normal installations should protect root dse,
+# cn=monitor, cn=schema, and cn=config
+#
+
+access to attrs=userpassword
+ by self =wx
+ by anonymous =x
+
+access to *
+ by users read
+ by * search
+
+# people branch
+database @BACKEND@
+suffix "ou=Groups,dc=example,dc=com"
+rootdn "cn=Manager,ou=Groups,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.3.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_6
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/slapd-ldapgluepeople.conf b/tests/data/slapd-ldapgluepeople.conf
new file mode 100644
index 0000000..328a7b7
--- /dev/null
+++ b/tests/data/slapd-ldapgluepeople.conf
@@ -0,0 +1,61 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+#ucdata-path ./ucdata
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.2.pid
+argsfile @TESTDIR@/slapd.2.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+authz-policy to
+authz-regexp "^uid=([^,]+),.*" "uid=$1,ou=People,dc=example,dc=com"
+
+#
+# normal installations should protect root dse,
+# cn=monitor, cn=schema, and cn=config
+#
+
+access to attrs=userpassword
+ by dn.exact="uid=proxy,ou=People,dc=example,dc=com" read
+ by self =wx
+ by anonymous =x
+
+access to *
+ by users read
+ by * search
+
+# people branch
+database @BACKEND@
+suffix "ou=People,dc=example,dc=com"
+rootdn "cn=Manager,ou=People,dc=example,dc=com"
+rootpw secret
+#null#bind on
+#~null~#directory @TESTDIR@/db.2.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_5
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/slapd-limits.conf b/tests/data/slapd-limits.conf
new file mode 100644
index 0000000..65a145c
--- /dev/null
+++ b/tests/data/slapd-limits.conf
@@ -0,0 +1,62 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+
+# Need quality indices on "uid" to check "unchecked" limits...
+#indexdb#index objectClass eq
+#indexdb#index uid eq
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+# Need extra limits for pagedResults on backends that support it...
+#maindb#limits dn.exact="cn=Unlimited User,ou=Paged Results Users,dc=example,dc=com" size=4 size.pr=unlimited
+#maindb#limits dn.exact="cn=Page Size Limited User,ou=Paged Results Users,dc=example,dc=com" size=4 size.pr=4
+#maindb#limits dn.exact="cn=Paged Results Disabled User,ou=Paged Results Users,dc=example,dc=com" size=4 size.prtotal=disabled
+#maindb#limits dn.exact="cn=Paged Results Limited User,ou=Paged Results Users,dc=example,dc=com" size=4 size.prtotal=10
+
+limits dn.exact="cn=Unlimited User,ou=People,dc=example,dc=com" size=unlimited time=unlimited
+limits dn.exact="cn=Soft Limited User,ou=People,dc=example,dc=com" size.soft=4 size.hard=unlimited
+limits dn.exact="cn=Hard Limited User,ou=People,dc=example,dc=com" size.soft=4 size.hard=8
+limits dn.exact="cn=Unchecked Limited User,ou=People,dc=example,dc=com" size.unchecked=4
+limits group="cn=Unchecked Limited Users,ou=Groups,dc=example,dc=com" size.unchecked=4
+limits dn.regex="^cn=Foo User,ou=[^,]+,dc=example,dc=com$" size.soft=6
+limits dn.onelevel="ou=People,dc=example,dc=com" size.soft=5
+limits dn.children="ou=Groups,dc=example,dc=com" size.soft=4
+limits dn.subtree="ou=Admin,dc=example,dc=com" size.soft=3
+limits users size.soft=2
+limits anonymous size.soft=1
+
+database monitor
diff --git a/tests/data/slapd-lload.conf b/tests/data/slapd-lload.conf
new file mode 100644
index 0000000..ab2e62f
--- /dev/null
+++ b/tests/data/slapd-lload.conf
@@ -0,0 +1,41 @@
+# stand-alone slapd config -- for testing (with indexing)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+include @DATADIR@/test.schema
+
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+# allow big PDUs from anonymous (for testing purposes)
+sockbuf_max_incoming 4194303
+
+modulepath ../servers/lloadd/
+moduleload lloadd.la
+
+backend lload
+listen "@URI1@"
+include @TESTDIR@/slapd.1.conf.lloadd
+
+database config
+include @TESTDIR@/configpw.conf
+
+database monitor
+access to * by * read
diff --git a/tests/data/slapd-meta-target1.conf b/tests/data/slapd-meta-target1.conf
new file mode 100644
index 0000000..a6356db
--- /dev/null
+++ b/tests/data/slapd-meta-target1.conf
@@ -0,0 +1,62 @@
+# stand-alone slapd config -- for testing (with indexing)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+include @DATADIR@/test.schema
+
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+# allow big PDUs from anonymous (for testing purposes)
+sockbuf_max_incoming 4194303
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#null#bind on
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+# ITS#5154: force mixed success/failure of binds using same connection
+access to dn="cn=Barbara Jensen,ou=Information Technology DivisioN,ou=People,dc=example,dc=com"
+ attrs=userPassword
+ by dn="cn=Manager,o=Local" write
+ by * =r
+
+access to attrs=userPassword
+ by dn="cn=Manager,o=Local" write
+ by * =xr
+
+access to *
+ by dn="cn=Manager,o=Local" write
+ by * read
+
+database monitor
diff --git a/tests/data/slapd-meta-target2.conf b/tests/data/slapd-meta-target2.conf
new file mode 100644
index 0000000..c4cff4b
--- /dev/null
+++ b/tests/data/slapd-meta-target2.conf
@@ -0,0 +1,56 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.2.pid
+argsfile @TESTDIR@/slapd.2.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#relaymod#modulepath ../servers/slapd/back-relay/
+#relaymod#moduleload back_relay.la
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+#metamod#modulepath ../servers/slapd/back-meta/
+#metamod#moduleload back_meta.la
+#rwmmod#modulepath ../servers/slapd/overlays/
+#rwmmod#moduleload rwm.la
+
+idletimeout 5
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "ou=Meta,dc=example,dc=com"
+rootdn "cn=Manager,ou=Meta,dc=example,dc=com"
+rootpw secret
+#null#bind on
+#~null~#directory @TESTDIR@/db.2.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
+
+access to *
+ by dn="cn=Manager,o=Local" write
+ by * read
+
+database monitor
diff --git a/tests/data/slapd-meta.conf b/tests/data/slapd-meta.conf
new file mode 100644
index 0000000..472fd6f
--- /dev/null
+++ b/tests/data/slapd-meta.conf
@@ -0,0 +1,83 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.m.pid
+argsfile @TESTDIR@/slapd.m.args
+
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+#metamod#modulepath ../servers/slapd/back-meta/
+#metamod#moduleload back_meta.la
+
+# seems to improve behavior under very heavy load
+# (i.e. it alleviates load on target systems)
+threads 8
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database meta
+suffix "o=Example,c=US"
+rootdn "cn=Manager,o=Example,c=US"
+rootpw secret
+chase-referrals no
+#nretries forever
+nretries 100
+# 1 sec timeout for binds
+bind-timeout 1000000
+#norefs true
+
+# local
+uri "@URI2@ou=Meta,o=Example,c=US"
+subtree-exclude "ou=Excluded,ou=Meta,o=Example,c=US"
+suffixmassage "ou=Meta,o=Example,c=US" "ou=Meta,dc=example,dc=com"
+###pseudorootdn "cn=manager,ou=meta,dc=example,dc=com"
+###pseudorootpw secret
+idassert-bind bindmethod=simple
+ binddn="cn=manager,ou=meta,dc=example,dc=com"
+ credentials="secret"
+ mode=self
+ flags=non-prescriptive
+idassert-authzFrom "dn.exact:cn=Manager,o=Local"
+
+# remote
+uri "@URI1@o=Example,c=US"
+subtree-include "dn.subtree:o=Example,c=US"
+suffixmassage "o=Example,c=US" "dc=example,dc=com"
+###pseudorootdn "cn=manager,dc=example,dc=com"
+###pseudorootpw secret
+idassert-bind bindmethod=simple
+ binddn="cn=manager,dc=example,dc=com"
+ credentials="secret"
+ mode=self
+ flags=non-prescriptive
+idassert-authzFrom "dn.exact:cn=Manager,o=Local"
+
+limits dn.exact="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,o=Example,c=US" time=1 size=8
+
+# This is only for binding as the rootdn
+database meta
+suffix "o=Local"
+rootdn "cn=Manager,o=Local"
+rootpw secret
+uri "@URI6@o=Local"
+
+database monitor
diff --git a/tests/data/slapd-nis-provider.conf b/tests/data/slapd-nis-provider.conf
new file mode 100644
index 0000000..cbb65d6
--- /dev/null
+++ b/tests/data/slapd-nis-provider.conf
@@ -0,0 +1,53 @@
+# provider slapd config -- for testing (needs updating)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/others_nis.at.conf
+include @SCHEMADIR@/others_nis.oc.conf
+include @SCHEMADIR@/nis.at.conf
+include @SCHEMADIR@/nis.oc.conf
+include @SCHEMADIR@/internet_mail.at.conf
+include @SCHEMADIR@/internet_mail.oc.conf
+pidfile @TESTDIR@/slapd.pid
+argsfile @TESTDIR@/slapd.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database bdb
+cachesize 4
+suffix "o=SGI, c=US"
+directory @TESTDIR@
+rootdn "cn=Manager, o=SGI, c=US"
+rootpw secret
+index objectClass eq
+index uid pres,eq,approx
+index gidNumber pres,eq,approx
+index uidNumber pres,eq,approx
+index cn pres,eq,approx
+index memberUid pres,eq,approx
+index macAddress pres,eq,approx
+index ipServiceProtocol pres,eq,approx
+index ipServicePort pres,eq,approx
+index oncRpcNumber pres,eq,approx
+index ipHostNumber pres,eq,approx
+index ipNetworkNumber pres,eq,approx
+index ipProtocolNumber pres,eq,approx
+index default none
+
+database monitor
diff --git a/tests/data/slapd-passwd.conf b/tests/data/slapd-passwd.conf
new file mode 100644
index 0000000..c2c887e
--- /dev/null
+++ b/tests/data/slapd-passwd.conf
@@ -0,0 +1,37 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.pid
+argsfile @TESTDIR@/slapd.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database passwd
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#file ./data/passwd
+
+database monitor
diff --git a/tests/data/slapd-ppolicy.conf b/tests/data/slapd-ppolicy.conf
new file mode 100644
index 0000000..366ebd4
--- /dev/null
+++ b/tests/data/slapd-ppolicy.conf
@@ -0,0 +1,58 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#ppolicymod#modulepath ../servers/slapd/overlays/
+#ppolicymod#moduleload ppolicy.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+lastbind on
+
+overlay ppolicy
+ppolicy_default "cn=Standard Policy,ou=Policies,dc=example,dc=com"
+ppolicy_use_lockout
+
+access to attrs=userpassword
+ by self write
+ by dn="uid=ndadmin, ou=People, dc=example, dc=com" manage
+ by * auth
+
+access to *
+ by self write
+ by * read
+
+database monitor
+
+database config
+include @TESTDIR@/configpw.conf
diff --git a/tests/data/slapd-provider.conf b/tests/data/slapd-provider.conf
new file mode 100644
index 0000000..d6eeb4d
--- /dev/null
+++ b/tests/data/slapd-provider.conf
@@ -0,0 +1,43 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+
+include @DATADIR@/ditcontentrules.conf
+
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/slapd-proxyauthz.conf b/tests/data/slapd-proxyauthz.conf
new file mode 100644
index 0000000..bdaff7f
--- /dev/null
+++ b/tests/data/slapd-proxyauthz.conf
@@ -0,0 +1,73 @@
+# proxy cache slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+
+pidfile @TESTDIR@/slapd.2.pid
+argsfile @TESTDIR@/slapd.2.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+#pcachemod#modulepath ../servers/slapd/overlays/
+#pcachemod#moduleload pcache.la
+
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database ldap
+suffix "dc=example,dc=com"
+rootdn "dc=example,dc=com"
+rootpw "secret"
+uri "@URI1@"
+
+limits dn="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" size=1
+
+idassert-bind bindmethod=simple binddn="cn=Manager,dc=example,dc=com" credentials="secret"
+ mode=self authz=proxyauthz flags="override"
+
+idassert-authzFrom "dn.children:dc=example,dc=com"
+
+#authz=proxyauthz
+
+overlay pcache
+pcache @BACKEND@ 100 2 @ENTRY_LIMIT@ @CCPERIOD@
+pcacheattrset 0 sn cn title uid
+pcacheattrset 1 mail postaladdress telephonenumber cn uid
+pcachetemplate (|(cn=)(sn=)) 0 @TTL@ @NTTL@ @STTL@
+pcachetemplate (sn=) 0 @TTL@ @NTTL@ @STTL@
+pcachetemplate (uid=) 1 @TTL@ @NTTL@ @STTL@
+pcachetemplate (mail=) 0 @TTL@ @NTTL@ @STTL@
+pcachetemplate (&(objectclass=)(uid=)) 1 @TTL@ @NTTL@ @STTL@ @TTR@
+pcachetemplate (cn=) 0 86400 86400 86400 180
+
+pcachebind (cn=) 0 3600 sub ou=people,dc=example,dc=com
+
+#mdb#dbnosync
+
+#~null~#directory @TESTDIR@/db.2.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid,mail pres,eq,sub
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/slapd-proxycache.conf b/tests/data/slapd-proxycache.conf
new file mode 100644
index 0000000..69ccf80
--- /dev/null
+++ b/tests/data/slapd-proxycache.conf
@@ -0,0 +1,63 @@
+# proxy cache slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+
+pidfile @TESTDIR@/slapd.2.pid
+argsfile @TESTDIR@/slapd.2.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+#pcachemod#modulepath ../servers/slapd/overlays/
+#pcachemod#moduleload pcache.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database ldap
+suffix "dc=example,dc=com"
+rootdn "dc=example,dc=com"
+rootpw "secret"
+uri "@URI1@"
+
+limits dn="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com" size=1
+
+overlay pcache
+pcache @BACKEND@ 100 2 @ENTRY_LIMIT@ @CCPERIOD@
+pcacheattrset 0 sn cn title uid
+pcacheattrset 1 mail postaladdress telephonenumber cn uid
+pcachetemplate (|(cn=)(sn=)) 0 @TTL@ @NTTL@ @STTL@
+pcachetemplate (sn=) 0 @TTL@ @NTTL@ @STTL@
+pcachetemplate (uid=) 1 @TTL@ @NTTL@ @STTL@
+pcachetemplate (mail=) 0 @TTL@ @NTTL@ @STTL@
+pcachetemplate (&(objectclass=)(uid=)) 1 @TTL@ @NTTL@ @STTL@ @TTR@
+pcachebind (&(objectclass=person)(uid=)) 1 @BTTR@ sub "ou=Alumni Association,ou=people,dc=example,dc=com"
+
+#mdb#dbnosync
+
+#~null~#directory @TESTDIR@/db.2.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid,mail pres,eq,sub
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/slapd-proxytimeout.conf b/tests/data/slapd-proxytimeout.conf
new file mode 100644
index 0000000..1c080fe
--- /dev/null
+++ b/tests/data/slapd-proxytimeout.conf
@@ -0,0 +1,71 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.m.pid
+argsfile @TESTDIR@/slapd.m.args
+
+#######################################################################
+# database definitions
+#######################################################################
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/:../servers/slapd/overlays
+#mod#moduleload back_@BACKEND@.la
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+#rwmmod#modulepath ../servers/slapd/overlays/
+#rwmmod#moduleload rwm.la
+#monitormod#modulepath ../servers/slapd/back-monitor/
+#monitormod#moduleload back_monitor.la
+
+# here the proxy is not only acting as a proxy, but it also has a local database dc=local,dc=com"
+database @BACKEND@
+suffix "dc=local,dc=com"
+rootdn "cn=Manager,dc=local,dc=com"
+rootpw "secret"
+#~null~#directory @TESTDIR@/db.2.a
+
+
+# Configure proxy
+# - normal user binds to "*,dc=example,dc=com" are proxied through to the remote slapd
+# - admin bind to local "cn=Manager,dc=local,dc=com" is overwritten by using idassert-bind
+database ldap
+uri "@URI1@"
+suffix "dc=idle-timeout,dc=example,dc=com"
+idassert-bind bindmethod=simple binddn="cn=Manager,dc=example,dc=com" credentials="secret"
+idassert-authzFrom "dn.exact:cn=Manager,dc=local,dc=com"
+rebind-as-user yes
+monitoring on
+idle-timeout @TIMEOUT@
+overlay rwm
+rwm-suffixmassage "dc=idle-timeout,dc=example,dc=com" "ou=People,dc=example,dc=com"
+
+database ldap
+uri "@URI1@"
+suffix "dc=conn-ttl,dc=example,dc=com"
+idassert-bind bindmethod=simple binddn="cn=Manager,dc=example,dc=com" credentials="secret"
+idassert-authzFrom "dn.exact:cn=Manager,dc=local,dc=com"
+rebind-as-user yes
+monitoring on
+conn-ttl @TIMEOUT@
+overlay rwm
+rwm-suffixmassage "dc=conn-ttl,dc=example,dc=com" "ou=People,dc=example,dc=com"
+
+database monitor
+
diff --git a/tests/data/slapd-pw.conf b/tests/data/slapd-pw.conf
new file mode 100644
index 0000000..cd0da38
--- /dev/null
+++ b/tests/data/slapd-pw.conf
@@ -0,0 +1,56 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#null#bind on
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+#
+# normal installations should protect root dse,
+# cn=monitor, cn=schema, and cn=config
+#
+
+access to attrs=userpassword
+ by anonymous auth
+ by self write
+
+access to *
+ by self write
+ by * read
+
+database monitor
diff --git a/tests/data/slapd-ref-consumer.conf b/tests/data/slapd-ref-consumer.conf
new file mode 100644
index 0000000..8d61df6
--- /dev/null
+++ b/tests/data/slapd-ref-consumer.conf
@@ -0,0 +1,45 @@
+# consumer slapd config -- for default referral testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+#
+pidfile @TESTDIR@/slapd.2.pid
+argsfile @TESTDIR@/slapd.2.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+referral "@URI1@"
+
+database @BACKEND@
+
+suffix "o=University of Mich,c=US"
+rootdn "cn=Manager,o=University of Mich,c=US"
+rootpw secret
+#~null~#directory @TESTDIR@/db.2.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/slapd-referrals.conf b/tests/data/slapd-referrals.conf
new file mode 100644
index 0000000..171a020
--- /dev/null
+++ b/tests/data/slapd-referrals.conf
@@ -0,0 +1,40 @@
+# referral slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.pid
+argsfile @TESTDIR@/slapd.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "c=us"
+rootdn "cn=Manager,c=us"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/slapd-refint.conf b/tests/data/slapd-refint.conf
new file mode 100644
index 0000000..3039f27
--- /dev/null
+++ b/tests/data/slapd-refint.conf
@@ -0,0 +1,48 @@
+# stand-alone slapd config -- for testing (with refint overlay)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2004-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#refintmod#modulepath ../servers/slapd/overlays/
+#refintmod#moduleload refint.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "o=refint"
+rootdn "cn=Manager,o=refint"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+overlay refint
+refint_attributes manager secretary member
+
+database monitor
diff --git a/tests/data/slapd-relay.conf b/tests/data/slapd-relay.conf
new file mode 100644
index 0000000..05506e0
--- /dev/null
+++ b/tests/data/slapd-relay.conf
@@ -0,0 +1,100 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.pid
+argsfile @TESTDIR@/slapd.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#relaymod#modulepath ../servers/slapd/back-relay/
+#relaymod#moduleload back_relay.la
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+#metamod#modulepath ../servers/slapd/back-meta/
+#metamod#moduleload back_meta.la
+#rwmmod#modulepath ../servers/slapd/overlays/
+#rwmmod#moduleload rwm.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#null#bind on
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+database @RELAY@
+suffix "o=Example,c=US"
+### back-relay can automatically instantiate the rwm overlay
+#relay-relay#relay "dc=example,dc=com"
+#relay-relay#overlay rwm
+#relay-relay#rwm-suffixmassage "dc=example,dc=com"
+#relay-relay#rwm-map objectClass groupOfNames groupOfUniqueNames
+#relay-relay#rwm-map objectClass uidObject dcObject
+#relay-relay#rwm-map attribute member uniqueMember
+#relay-relay#rwm-map attribute uid dc
+### back-ldap needs explicit instantiation of the rwm overlay
+#relay-ldap#uri "@URI1@"
+#relay-ldap#overlay rwm
+#relay-ldap#rwm-suffixmassage "dc=example,dc=com"
+#relay-ldap#rwm-map objectClass groupOfNames groupOfUniqueNames
+#relay-ldap#rwm-map objectClass uidObject dcObject
+#relay-ldap#rwm-map attribute member uniqueMember
+#relay-ldap#rwm-map attribute uid dc
+#relay-meta#uri "@URI1@o=Example,c=US"
+#relay-meta#suffixmassage "o=Example,c=US" "dc=example,dc=com"
+#relay-meta#map objectClass groupOfNames groupOfUniqueNames
+#relay-meta#map objectClass uidObject dcObject
+#relay-meta#map attribute member uniqueMember
+#relay-meta#map attribute uid dc
+
+database @RELAY@
+suffix "o=Esempio,c=IT"
+### use this alternate form of back-relay, without the "relay" directive,
+### which causes the target database to be selected after DN massaging
+#relay-relay#overlay rwm
+#relay-relay#rwm-suffixmassage "dc=example,dc=com"
+### back-ldap needs URI
+#relay-ldap#uri "@URI1@"
+#relay-ldap#overlay rwm
+#relay-ldap#rwm-suffixmassage "dc=example,dc=com"
+#relay-meta#uri "@URI1@o=Esempio,c=IT"
+#relay-meta#suffixmassage "o=Esempio,c=IT" "dc=example,dc=com"
+
+database @RELAY@
+suffix "o=Beispiel,c=DE"
+### back-relay can automatically instantiate the rwm overlay
+#relay-relay#relay "dc=example,dc=com"
+#relay-relay#overlay rwm
+#relay-relay#rwm-suffixmassage "dc=example,dc=com"
+### back-ldap needs explicit instantiation of the rwm overlay
+#relay-ldap#uri "@URI1@"
+#relay-ldap#overlay rwm
+#relay-ldap#rwm-suffixmassage "dc=example,dc=com"
+#relay-meta#uri "@URI1@o=Beispiel,c=DE"
+#relay-meta#suffixmassage "o=Beispiel,c=DE" "dc=example,dc=com"
+
+database monitor
diff --git a/tests/data/slapd-repl-consumer-remote.conf b/tests/data/slapd-repl-consumer-remote.conf
new file mode 100644
index 0000000..8aa8692
--- /dev/null
+++ b/tests/data/slapd-repl-consumer-remote.conf
@@ -0,0 +1,78 @@
+# consumer slapd config -- for testing of replication
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+#
+pidfile @TESTDIR@/slapd.2.pid
+argsfile @TESTDIR@/slapd.2.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+
+#ldapyes#overlay chain
+#ldapyes#chain-uri @URI1@
+#ldapyes#chain-idassert-bind bindmethod=simple binddn="cn=Manager,dc=example,dc=com" credentials=secret mode=self
+#ldapmod#overlay chain
+#ldapmod#chain-uri @URI1@
+#ldapmod#chain-idassert-bind bindmethod=simple binddn="cn=Manager,dc=example,dc=com" credentials=secret mode=self
+
+#######################################################################
+# database definitions
+#######################################################################
+
+access to dn.base="" attrs=children
+ by dn.exact="cn=Monitor" write
+ by * break
+
+access to *
+ by * read
+
+database @BACKEND@
+
+suffix "dc=example,dc=com"
+rootdn "cn=consumer,dc=example,dc=com"
+rootpw secret
+# HACK: use the RootDN of the monitor database as UpdateDN so ACLs apply
+# without the need to write the UpdateDN before starting replication
+updatedn "cn=Monitor"
+updateref @URI1@
+#null#bind on
+#~null~#directory @TESTDIR@/db.2.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#indexdb#index entryUUID pres,eq
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
+
+# Need to strip hasSubordinates from internal searches otherwise
+# syncrepl will try to delete it, since syncprov is not sending
+# it because it's generated
+access to dn.subtree="dc=example,dc=com" attrs=hasSubordinates
+ by dn.exact="cn=Monitor" none
+ by * read
+
+access to dn.subtree="dc=example,dc=com"
+ by dn.exact="cn=Monitor" write
+ by * read
+
+database monitor
+rootdn "cn=Monitor"
+rootpw monitor
diff --git a/tests/data/slapd-retcode.conf b/tests/data/slapd-retcode.conf
new file mode 100644
index 0000000..89f5b74
--- /dev/null
+++ b/tests/data/slapd-retcode.conf
@@ -0,0 +1,55 @@
+# stand-alone slapd config -- for testing (with indexing)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+include @DATADIR@/test.schema
+
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#retcodemod#modulepath ../servers/slapd/overlays/
+#retcodemod#moduleload retcode.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+overlay retcode
+retcode-parent "ou=RetCodes,dc=example,dc=com"
+include @DATADIR@/retcode.conf
+
+retcode-item "cn=Unsolicited" 0x00 unsolicited="0"
+retcode-item "cn=Notice of Disconnect" 0x00 unsolicited="1.3.6.1.4.1.1466.20036"
+retcode-item "cn=Pre-disconnect" 0x34 flags="pre-disconnect"
+retcode-item "cn=Post-disconnect" 0x34 flags="post-disconnect"
+
+database monitor
diff --git a/tests/data/slapd-schema.conf b/tests/data/slapd-schema.conf
new file mode 100644
index 0000000..02058d7
--- /dev/null
+++ b/tests/data/slapd-schema.conf
@@ -0,0 +1,51 @@
+# stand-alone slapd config -- for testing (with indexing)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+#
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+#
+include @SCHEMADIR@/corba.schema
+include @SCHEMADIR@/java.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/misc.schema
+include @SCHEMADIR@/nis.schema
+include @SCHEMADIR@/openldap.schema
+#
+include @SCHEMADIR@/duaconf.schema
+include @SCHEMADIR@/dyngroup.schema
+
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#
+rootdse @DATADIR@/rootdse.ldif
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "o=OpenLDAP Project,l=Internet"
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#ndb#dbname db_1_a
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/slapd-sql-syncrepl-provider.conf b/tests/data/slapd-sql-syncrepl-provider.conf
new file mode 100644
index 0000000..85face1
--- /dev/null
+++ b/tests/data/slapd-sql-syncrepl-provider.conf
@@ -0,0 +1,78 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+#ucdata-path ./ucdata
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#sqlmod#modulepath ../servers/slapd/back-sql/
+#sqlmod#moduleload back_sql.la
+#syncprovmod#modulepath ../servers/slapd/overlays/
+#syncprovmod#moduleload syncprov.la
+
+#
+# normal installations should protect root dse,
+# cn=monitor, cn=schema, and cn=config
+#
+
+access to attrs=userpassword
+ by self =w
+ by anonymous =x
+
+access to *
+ by * read
+
+#######################################################################
+# sql database definitions
+#######################################################################
+
+database sql
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+dbname example
+dbuser manager
+dbpasswd secret
+
+#
+# rdbms specific directives
+#
+# IBM db2
+#ibmdb2#upper_func "ucase"
+#ibmdb2#upper_needs_cast "yes"
+#ibmdb2#concat_pattern "?||?"
+#ibmdb2#children_cond "ucase(ldap_entries.dn)=ucase(cast(? as varchar(255)))"
+#ibmdb2#create_needs_select "yes"
+#ibmdb2#insentry_stmt "insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values ((select case when max(id) is null then 1 else max(id) + 1 end from ldap_entries),?,?,?,?)"
+#
+# PostgreSQL
+#pgsql#insentry_stmt "insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values ((select case when max(id) is null then 1 else max(id) + 1 end from ldap_entries),?,?,?,?)"
+#pgsql#upper_func "upper"
+#pgsql#strcast_func "text"
+#pgsql#concat_pattern "?||?"
+#
+# MySQL
+#mysql#concat_pattern "concat(?,?)"
+
+has_ldapinfo_dn_ru no
+
+overlay syncprov
+
+database monitor
diff --git a/tests/data/slapd-sql.conf b/tests/data/slapd-sql.conf
new file mode 100644
index 0000000..2aa28a4
--- /dev/null
+++ b/tests/data/slapd-sql.conf
@@ -0,0 +1,74 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+#ucdata-path ./ucdata
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#sqlmod#modulepath ../servers/slapd/back-sql/
+#sqlmod#moduleload back_sql.la
+
+#
+# normal installations should protect root dse,
+# cn=monitor, cn=schema, and cn=config
+#
+
+access to attrs=userpassword
+ by self =w
+ by anonymous =x
+
+access to *
+ by * read
+
+#######################################################################
+# sql database definitions
+#######################################################################
+
+database sql
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+dbname example
+dbuser manager
+dbpasswd secret
+
+#
+# rdbms specific directives
+#
+# IBM db2
+#ibmdb2#upper_func "ucase"
+#ibmdb2#upper_needs_cast "yes"
+#ibmdb2#concat_pattern "?||?"
+#ibmdb2#children_cond "ucase(ldap_entries.dn)=ucase(cast(? as varchar(255)))"
+#ibmdb2#create_needs_select "yes"
+#ibmdb2#insentry_stmt "insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values ((select case when max(id) is null then 1 else max(id) + 1 end from ldap_entries),?,?,?,?)"
+#
+# PostgreSQL
+#pgsql#insentry_stmt "insert into ldap_entries (id,dn,oc_map_id,parent,keyval) values ((select case when max(id) is null then 1 else max(id) + 1 end from ldap_entries),?,?,?,?)"
+#pgsql#upper_func "upper"
+#pgsql#strcast_func "text"
+#pgsql#concat_pattern "?||?"
+#
+# MySQL
+#mysql#concat_pattern "concat(?,?)"
+
+has_ldapinfo_dn_ru no
+
+database monitor
diff --git a/tests/data/slapd-syncrepl-consumer-persist-ldap.conf b/tests/data/slapd-syncrepl-consumer-persist-ldap.conf
new file mode 100644
index 0000000..7793e15
--- /dev/null
+++ b/tests/data/slapd-syncrepl-consumer-persist-ldap.conf
@@ -0,0 +1,74 @@
+# consumer slapd config -- for testing of SYNC replication with intermediate proxy
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+#
+pidfile @TESTDIR@/slapd.3.pid
+argsfile @TESTDIR@/slapd.3.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#syncprovmod#modulepath ../servers/slapd/overlays/
+#syncprovmod#moduleload syncprov.la
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+
+# We don't need any access to this DSA
+restrict all
+
+#######################################################################
+# consumer proxy database definitions
+#######################################################################
+
+database ldap
+suffix "dc=example,dc=com"
+rootdn "cn=Whoever"
+uri @URI2@
+
+# ITS#4632: syncprov now wants this on (ITS#4613); however, since checks
+# are in place to prevent lastmod operational attrs to be added twice,
+# this should cause no harm
+lastmod on
+
+# HACK: use the RootDN of the monitor database as UpdateDN so ACLs apply
+# without the need to write the UpdateDN before starting replication
+acl-bind bindmethod=simple
+ binddn="cn=Monitor"
+ credentials=monitor
+
+# Don't change syncrepl spec yet
+
+# HACK: use the RootDN of the monitor database as UpdateDN so ACLs apply
+# without the need to write the UpdateDN before starting replication
+syncrepl rid=1
+ provider=@URI1@
+ binddn="cn=Manager,dc=example,dc=com"
+ bindmethod=simple
+ credentials=secret
+ searchbase="dc=example,dc=com"
+ filter="(objectClass=*)"
+ attrs="*,structuralObjectClass,entryUUID,entryCSN,creatorsName,createTimestamp,modifiersName,modifyTimestamp"
+ schemachecking=off
+ scope=sub
+ type=refreshAndPersist
+ retry="3 10 5 +"
+
+overlay syncprov
+
+database monitor
diff --git a/tests/data/slapd-syncrepl-consumer-persist1.conf b/tests/data/slapd-syncrepl-consumer-persist1.conf
new file mode 100644
index 0000000..0b497dd
--- /dev/null
+++ b/tests/data/slapd-syncrepl-consumer-persist1.conf
@@ -0,0 +1,72 @@
+# consumer slapd config -- for testing of SYNC replication
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+#
+pidfile @TESTDIR@/slapd.4.pid
+argsfile @TESTDIR@/slapd.4.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#syncprovmod#modulepath ../servers/slapd/overlays/
+#syncprovmod#moduleload syncprov.la
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+
+#ldapyes#overlay chain
+#ldapyes#chain-uri @URI1@
+#ldapyes#chain-idassert-bind bindmethod=simple binddn="cn=Manager,dc=example,dc=com" credentials=secret mode=self
+#ldapmod#overlay chain
+#ldapmod#chain-uri @URI1@
+#ldapmod#chain-idassert-bind bindmethod=simple binddn="cn=Manager,dc=example,dc=com" credentials=secret mode=self
+
+#######################################################################
+# consumer database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=consumer,dc=example,dc=com"
+rootpw secret
+#null#bind on
+#~null~#directory @TESTDIR@/db.4.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#indexdb#index entryUUID,entryCSN eq
+#ndb#dbname db_4
+#ndb#include @DATADIR@/ndb.conf
+
+# Don't change syncrepl spec yet
+syncrepl rid=1
+ provider=@URI1@
+ binddn="cn=Manager,dc=example,dc=com"
+ bindmethod=simple
+ credentials=secret
+ searchbase="dc=example,dc=com"
+ filter="(objectClass=*)"
+ attrs="*,+"
+ schemachecking=off
+ scope=sub
+ type=refreshAndPersist
+ retry="3 5 300 5"
+updateref @URI1@
+
+overlay syncprov
+
+database monitor
diff --git a/tests/data/slapd-syncrepl-consumer-persist2.conf b/tests/data/slapd-syncrepl-consumer-persist2.conf
new file mode 100644
index 0000000..ee3a6dd
--- /dev/null
+++ b/tests/data/slapd-syncrepl-consumer-persist2.conf
@@ -0,0 +1,44 @@
+# consumer slapd config -- for testing of SYNC replication
+# $OpenLDAP$
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+#
+pidfile @TESTDIR@/slapd.5.pid
+argsfile @TESTDIR@/slapd.5.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# consumer database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=consumer,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.5.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#indexdb#index entryUUID,entryCSN eq
+#ndb#dbname db_5
+#ndb#include @DATADIR@/ndb.conf
+
+# Don't change syncrepl spec yet
+syncrepl rid=1
+ provider=@URI4@
+ binddn="cn=consumer,dc=example,dc=com"
+ bindmethod=simple
+ credentials=secret
+ searchbase="dc=example,dc=com"
+ filter="(objectClass=*)"
+ attrs="*"
+ schemachecking=off
+ scope=sub
+ type=refreshAndPersist
+
+database monitor
diff --git a/tests/data/slapd-syncrepl-consumer-persist3.conf b/tests/data/slapd-syncrepl-consumer-persist3.conf
new file mode 100644
index 0000000..39de32f
--- /dev/null
+++ b/tests/data/slapd-syncrepl-consumer-persist3.conf
@@ -0,0 +1,56 @@
+# consumer slapd config -- for testing of SYNC replication
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+#
+pidfile @TESTDIR@/slapd.6.pid
+argsfile @TESTDIR@/slapd.6.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# consumer database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=consumer,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.6.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#indexdb#index entryUUID,entryCSN eq
+#ndb#dbname db_6
+#ndb#include @DATADIR@/ndb.conf
+
+# Don't change syncrepl spec yet
+syncrepl rid=1
+ provider=@URI1@
+ binddn="cn=Manager,dc=example,dc=com"
+ bindmethod=simple
+ credentials=secret
+ searchbase="dc=example,dc=com"
+ filter="(objectClass=*)"
+ attrs="*"
+ schemachecking=off
+ scope=sub
+ type=refreshAndPersist
+
+database monitor
diff --git a/tests/data/slapd-syncrepl-consumer-refresh1.conf b/tests/data/slapd-syncrepl-consumer-refresh1.conf
new file mode 100644
index 0000000..7e1b41b
--- /dev/null
+++ b/tests/data/slapd-syncrepl-consumer-refresh1.conf
@@ -0,0 +1,63 @@
+# consumer slapd config -- for testing of SYNC replication
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+#
+pidfile @TESTDIR@/slapd.2.pid
+argsfile @TESTDIR@/slapd.2.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#syncprovmod#modulepath ../servers/slapd/overlays/
+#syncprovmod#moduleload syncprov.la
+
+#######################################################################
+# consumer database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=consumer,dc=example,dc=com"
+rootpw secret
+#null#bind on
+#~null~#directory @TESTDIR@/db.2.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#indexdb#index entryUUID,entryCSN eq
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
+
+# Don't change syncrepl spec yet
+syncrepl rid=1
+ provider=@URI1@
+ binddn="cn=Manager,dc=example,dc=com"
+ bindmethod=simple
+ credentials=secret
+ searchbase="dc=example,dc=com"
+ filter="(objectClass=*)"
+ schemachecking=off
+ scope=sub
+ type=refreshOnly
+ interval=00:00:00:03
+updateref @URI1@
+
+overlay syncprov
+syncprov-sessionlog 100
+
+database monitor
diff --git a/tests/data/slapd-syncrepl-consumer-refresh2.conf b/tests/data/slapd-syncrepl-consumer-refresh2.conf
new file mode 100644
index 0000000..4dc5909
--- /dev/null
+++ b/tests/data/slapd-syncrepl-consumer-refresh2.conf
@@ -0,0 +1,57 @@
+# consumer slapd config -- for testing of SYNC replication
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+#
+pidfile @TESTDIR@/slapd.3.pid
+argsfile @TESTDIR@/slapd.3.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# consumer database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=consumer,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.3.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#indexdb#index entryUUID,entryCSN eq
+#ndb#dbname db_3
+#ndb#include @DATADIR@/ndb.conf
+
+# Don't change syncrepl spec yet
+syncrepl rid=1
+ provider=@URI2@
+ binddn="cn=consumer,dc=example,dc=com"
+ bindmethod=simple
+ credentials=secret
+ searchbase="dc=example,dc=com"
+ filter="(objectClass=*)"
+ attrs="*"
+ schemachecking=off
+ scope=sub
+ type=refreshOnly
+ interval=00:00:00:03
+
+database monitor
diff --git a/tests/data/slapd-syncrepl-multiproxy.conf b/tests/data/slapd-syncrepl-multiproxy.conf
new file mode 100644
index 0000000..6435846
--- /dev/null
+++ b/tests/data/slapd-syncrepl-multiproxy.conf
@@ -0,0 +1,103 @@
+# consumer slapd config -- for testing of SYNC replication with intermediate proxy
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#syncprovmod#modulepath ../servers/slapd/overlays/
+#syncprovmod#moduleload syncprov.la
+#ldapmod#modulepath ../servers/slapd/back-ldap/
+#ldapmod#moduleload back_ldap.la
+
+#######################################################################
+# provider database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#indexdb#index entryUUID,entryCSN eq
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+overlay syncprov
+syncprov-sessionlog 100
+
+#######################################################################
+# consumer proxy database definitions
+#######################################################################
+
+database ldap
+hidden on
+suffix "dc=example,dc=com"
+rootdn "cn=Whoever"
+uri @URI2@
+
+acl-bind bindmethod=simple
+ binddn="cn=Monitor"
+ credentials=monitor
+
+# Don't change syncrepl spec yet
+
+syncrepl rid=1
+ provider=@URI1@
+ binddn="cn=Manager,dc=example,dc=com"
+ bindmethod=simple
+ credentials=secret
+ searchbase="dc=example,dc=com"
+ filter="(objectClass=*)"
+ schemachecking=off
+ scope=sub
+ type=refreshAndPersist
+ retry="3 5 300 5"
+
+database ldap
+hidden on
+suffix "dc=example,dc=com"
+rootdn "cn=Whoever"
+uri @URI3@
+
+acl-bind bindmethod=simple
+ binddn="cn=Monitor"
+ credentials=monitor
+
+# Don't change syncrepl spec yet
+
+syncrepl rid=2
+ provider=@URI1@
+ binddn="cn=Manager,dc=example,dc=com"
+ bindmethod=simple
+ credentials=secret
+ searchbase="dc=example,dc=com"
+ filter="(objectClass=*)"
+ schemachecking=off
+ scope=sub
+ type=refreshOnly
+ interval=00:00:00:03
+ retry="3 5 300 5"
+
+database monitor
diff --git a/tests/data/slapd-syncrepl-provider.conf b/tests/data/slapd-syncrepl-provider.conf
new file mode 100644
index 0000000..eec2ffd
--- /dev/null
+++ b/tests/data/slapd-syncrepl-provider.conf
@@ -0,0 +1,48 @@
+# provider slapd config -- for testing of SYNC replication
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#syncprovmod#modulepath ../servers/slapd/overlays/
+#syncprovmod#moduleload syncprov.la
+
+#######################################################################
+# provider database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#indexdb#index entryUUID,entryCSN eq
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+overlay syncprov
+#syncprov-sessionlog 100
+
+database monitor
diff --git a/tests/data/slapd-tls-sasl.conf b/tests/data/slapd-tls-sasl.conf
new file mode 100644
index 0000000..67a29a3
--- /dev/null
+++ b/tests/data/slapd-tls-sasl.conf
@@ -0,0 +1,62 @@
+# stand-alone slapd config -- for testing (with indexing)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+#
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+#
+include @SCHEMADIR@/corba.schema
+include @SCHEMADIR@/java.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/misc.schema
+include @SCHEMADIR@/nis.schema
+include @SCHEMADIR@/openldap.schema
+#
+include @SCHEMADIR@/duaconf.schema
+include @SCHEMADIR@/dyngroup.schema
+
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+# SSL configuration
+TLSCACertificateFile @TESTDIR@/tls/ca/certs/testsuiteCA.crt
+TLSCertificateKeyFile @TESTDIR@/tls/private/localhost.key
+TLSCertificateFile @TESTDIR@/tls/certs/localhost.crt
+TLSVerifyClient hard
+
+#
+rootdse @DATADIR@/rootdse.ldif
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+authz-regexp "email=([^,]*),cn=[^,]*,ou=OpenLDAP,o=OpenLDAP Foundation,st=CA,c=US" ldap:///ou=People,dc=example,dc=com??sub?(mail=$1)
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index mail eq
+#ndb#dbname db_1_a
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/slapd-tls.conf b/tests/data/slapd-tls.conf
new file mode 100644
index 0000000..fb3a987
--- /dev/null
+++ b/tests/data/slapd-tls.conf
@@ -0,0 +1,58 @@
+# stand-alone slapd config -- for testing (with indexing)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+#
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+#
+include @SCHEMADIR@/corba.schema
+include @SCHEMADIR@/java.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/misc.schema
+include @SCHEMADIR@/nis.schema
+include @SCHEMADIR@/openldap.schema
+#
+include @SCHEMADIR@/duaconf.schema
+include @SCHEMADIR@/dyngroup.schema
+
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+# SSL configuration
+TLSCertificateKeyFile @TESTDIR@/tls/private/localhost.key
+TLSCertificateFile @TESTDIR@/tls/certs/localhost.crt
+
+#
+rootdse @DATADIR@/rootdse.ldif
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index mail eq
+#ndb#dbname db_1_a
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/slapd-translucent-local.conf b/tests/data/slapd-translucent-local.conf
new file mode 100644
index 0000000..9809e0a
--- /dev/null
+++ b/tests/data/slapd-translucent-local.conf
@@ -0,0 +1,63 @@
+# stand-alone slapd config -- for testing (with translucent overlay)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2004-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+ucdata-path ./ucdata
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+
+#
+pidfile @TESTDIR@/slapd.2.pid
+argsfile @TESTDIR@/slapd.2.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@
+#mod#moduleload back_@BACKEND@.la
+#ldapmod#modulepath ../servers/slapd/back-ldap
+#ldapmod#moduleload back_ldap.la
+#translucentmod#modulepath ../servers/slapd/overlays
+#translucentmod#moduleload translucent.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database monitor
+
+database @BACKEND@
+suffix "o=translucent"
+rootdn "o=translucent"
+rootpw secret
+#null#bind on
+#~null~#directory @TESTDIR@/db.2.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
+
+overlay translucent
+translucent_no_glue
+
+uri @URI1@
+# "lastmod off" is not strictly required because the instance of back-ldap
+# added by the translucent overlay sets it off for the underlying database;
+# however, the local database needs to have "lastmod off" so it's here as
+# a reminder.
+lastmod off
+acl-bind binddn="uid=binder,o=translucent" credentials="bindtest"
+
+database config
+include @TESTDIR@/configpw.conf
diff --git a/tests/data/slapd-translucent-remote.conf b/tests/data/slapd-translucent-remote.conf
new file mode 100644
index 0000000..4c127b9
--- /dev/null
+++ b/tests/data/slapd-translucent-remote.conf
@@ -0,0 +1,44 @@
+# stand-alone slapd config -- for testing (with translucent overlay)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2004-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/:../servers/slapd/overlays
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database monitor
+
+database @BACKEND@
+suffix "o=translucent"
+rootdn "o=translucent"
+rootpw secret
+#null#bind on
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
diff --git a/tests/data/slapd-unique.conf b/tests/data/slapd-unique.conf
new file mode 100644
index 0000000..697028d
--- /dev/null
+++ b/tests/data/slapd-unique.conf
@@ -0,0 +1,59 @@
+# stand-alone slapd config -- for testing (with unique overlay)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2004-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#uniquemod#modulepath ../servers/slapd/overlays
+#uniquemod#moduleload unique.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "o=unique"
+rootdn "cn=Manager,o=unique"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+access to attrs=userPassword by * auth
+access to * by users write
+
+overlay unique
+
+unique_attributes employeeNumber displayName
+unique_base o=unique
+
+#unique_uri ldap:///?description?one
+#unique_uri ldap:///?employeeNumber,displayName?sub
+
+database monitor
+
+database config
+include @TESTDIR@/configpw.conf
diff --git a/tests/data/slapd-valregex.conf b/tests/data/slapd-valregex.conf
new file mode 100644
index 0000000..0cff837
--- /dev/null
+++ b/tests/data/slapd-valregex.conf
@@ -0,0 +1,70 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+# global ACLs
+#
+# normal installations should protect root dse, cn=monitor, cn=subschema
+#
+
+access to dn.exact="" attrs=objectClass
+ by users read
+access to *
+ by * read
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#null#bind on
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+access to attrs=userPassword
+ by anonymous auth
+ by * none stop
+
+access to attrs=sn val.regex="^(.*)$"
+ by dn.exact,expand="cn=${v1},ou=Alumni Association,ou=People,dc=example,dc=com" write
+ by * read stop
+
+access to attrs=sn val.regex="."
+ by * read stop
+
+access to attrs=sn
+ by dn.exact="cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com" write
+ by * read stop
+
+# fall into global ACLs
+
+database monitor
diff --git a/tests/data/slapd-valsort.conf b/tests/data/slapd-valsort.conf
new file mode 100644
index 0000000..5f180b8
--- /dev/null
+++ b/tests/data/slapd-valsort.conf
@@ -0,0 +1,53 @@
+# stand-alone slapd config -- for testing (with valsort overlay)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2004-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+#valsortmod#moduleload ../servers/slapd/overlays/valsort.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "o=valsort"
+rootdn "cn=Manager,o=valsort"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+overlay valsort
+valsort-attr sn ou=users,o=valsort alpha-ascend
+valsort-attr departmentNumber ou=users,o=valsort alpha-ascend
+valsort-attr mailPreferenceOption ou=users,o=valsort numeric-ascend
+valsort-attr ou ou=users,o=valsort weighted
+valsort-attr employeeType ou=users,o=valsort weighted alpha-ascend
+
+database config
+include @TESTDIR@/configpw.conf
+
+database monitor
diff --git a/tests/data/slapd-whoami.conf b/tests/data/slapd-whoami.conf
new file mode 100644
index 0000000..7be5cbc
--- /dev/null
+++ b/tests/data/slapd-whoami.conf
@@ -0,0 +1,62 @@
+# provider slapd config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+authz-policy both
+authz-regexp "^uid=group/([^,]+),.*" "ldap:///dc=example,dc=com??sub?cn=$1"
+authz-regexp "^uid=([^,]+),.*" "ldap:///dc=example,dc=com??sub?uid=$1"
+
+#
+# normal installations should protect root dse,
+# cn=monitor, cn=schema, and cn=config
+#
+
+access to attrs=authzFrom,authzTo
+ by * auth
+
+access to attrs=userpassword
+ by anonymous auth
+ by self write
+
+access to *
+ by self write
+ by * read
+
+database @BACKEND@
+
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/slapd.conf b/tests/data/slapd.conf
new file mode 100644
index 0000000..15db5b0
--- /dev/null
+++ b/tests/data/slapd.conf
@@ -0,0 +1,49 @@
+# stand-alone slapd config -- for testing (with indexing)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+include @DATADIR@/test.schema
+
+#
+pidfile @TESTDIR@/slapd.1.pid
+argsfile @TESTDIR@/slapd.1.args
+
+# allow big PDUs from anonymous (for testing purposes)
+sockbuf_max_incoming 4194303
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#null#bind on
+#~null~#directory @TESTDIR@/db.1.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#mdb#maxsize 33554432
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/slapd2.conf b/tests/data/slapd2.conf
new file mode 100644
index 0000000..99fb0c0
--- /dev/null
+++ b/tests/data/slapd2.conf
@@ -0,0 +1,42 @@
+# stand-alone slapd config -- for testing (with indexing)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+include @SCHEMADIR@/core.schema
+include @SCHEMADIR@/cosine.schema
+include @SCHEMADIR@/inetorgperson.schema
+include @SCHEMADIR@/openldap.schema
+include @SCHEMADIR@/nis.schema
+#
+pidfile @TESTDIR@/slapd.2.pid
+argsfile @TESTDIR@/slapd.2.args
+
+#mod#modulepath ../servers/slapd/back-@BACKEND@/
+#mod#moduleload back_@BACKEND@.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database @BACKEND@
+suffix "dc=example,dc=com"
+rootdn "cn=Manager,dc=example,dc=com"
+rootpw secret
+#~null~#directory @TESTDIR@/db.2.a
+#indexdb#index objectClass eq
+#indexdb#index cn,sn,uid pres,eq,sub
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
+
+database monitor
diff --git a/tests/data/sql-concurrency/do_add.1 b/tests/data/sql-concurrency/do_add.1
new file mode 100644
index 0000000..e4ce2d6
--- /dev/null
+++ b/tests/data/sql-concurrency/do_add.1
@@ -0,0 +1,9 @@
+cn=James Jones 1,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: James Jones 1
+givenName: James
+sn: Jones 1
+userpassword:: amFq
+telephoneNumber: +1 313 555 4772
+telephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
diff --git a/tests/data/sql-concurrency/do_add.2 b/tests/data/sql-concurrency/do_add.2
new file mode 100644
index 0000000..a60b619
--- /dev/null
+++ b/tests/data/sql-concurrency/do_add.2
@@ -0,0 +1,10 @@
+cn=James Jones 2,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: James Jones 2
+givenName: James
+sn: Jones 2
+userpassword:: amFq
+telephoneNumber: +1 313 555 4772
+telephoneNumber: +1 313 555 3923
+telephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
diff --git a/tests/data/sql-concurrency/do_add.3 b/tests/data/sql-concurrency/do_add.3
new file mode 100644
index 0000000..b479513
--- /dev/null
+++ b/tests/data/sql-concurrency/do_add.3
@@ -0,0 +1,10 @@
+cn=James Jones 3,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: James Jones 3
+givenName: James
+sn: Jones 3
+userpassword:: amFq
+telephoneNumber: +1 313 555 4772
+telephoneNumber: +1 313 555 3923
+telephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
diff --git a/tests/data/sql-concurrency/do_add.4 b/tests/data/sql-concurrency/do_add.4
new file mode 100644
index 0000000..065897e
--- /dev/null
+++ b/tests/data/sql-concurrency/do_add.4
@@ -0,0 +1,10 @@
+cn=James Jones 4,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: James Jones 4
+givenName: James
+sn: Jones 4
+userpassword:: amFq
+telephoneNumber: +1 313 555 4772
+telephoneNumber: +1 313 555 3923
+telephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
diff --git a/tests/data/sql-concurrency/do_bind.0 b/tests/data/sql-concurrency/do_bind.0
new file mode 100644
index 0000000..e0d0481
--- /dev/null
+++ b/tests/data/sql-concurrency/do_bind.0
@@ -0,0 +1,2 @@
+cn=Mitya Kovalev,dc=example,dc=com
+mit
diff --git a/tests/data/sql-concurrency/do_modrdn.0 b/tests/data/sql-concurrency/do_modrdn.0
new file mode 100644
index 0000000..29a77aa
--- /dev/null
+++ b/tests/data/sql-concurrency/do_modrdn.0
@@ -0,0 +1,2 @@
+cn=Mitya Kovalev,dc=example,dc=com
+cn=Torvlobnor Puzdoy,dc=example,dc=com
diff --git a/tests/data/sql-concurrency/do_read.0 b/tests/data/sql-concurrency/do_read.0
new file mode 100644
index 0000000..250ddbe
--- /dev/null
+++ b/tests/data/sql-concurrency/do_read.0
@@ -0,0 +1,4 @@
+documentTitle=book1,dc=example,dc=com
+dc=example,dc=com
+cn=Akakiy Zinberstein,dc=example,dc=com
+ou=Referral,dc=example,dc=com
diff --git a/tests/data/sql-concurrency/do_search.0 b/tests/data/sql-concurrency/do_search.0
new file mode 100644
index 0000000..1984ff1
--- /dev/null
+++ b/tests/data/sql-concurrency/do_search.0
@@ -0,0 +1,12 @@
+dc=example,dc=com
+cn=Mitya Kovalev
+cn=Akakiy Zinberstein,dc=example,dc=com
+sn=Zinberstein
+dc=example,dc=com
+cn=James*
+dc=example,dc=com
+sn=*
+dc=example,dc=com
+cn=*
+dc=example,dc=com
+cn=James*
diff --git a/tests/data/sql-read.out b/tests/data/sql-read.out
new file mode 100644
index 0000000..6c9749e
--- /dev/null
+++ b/tests/data/sql-read.out
@@ -0,0 +1,623 @@
+# Testing baseobject search...
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example
+dc: example
+
+# Testing onelevel search...
+dn: cn=Akakiy Zinberstein,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: pkiUser
+cn: Akakiy Zinberstein
+sn: Zinberstein
+givenName: Akakiy
+userCertificate;binary:: MIIDazCCAtSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADB3MQswCQYDV
+ QQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTH
+ RkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhc
+ NMDMxMDE3MTYzMzE5WhcNMDQxMDE2MTYzMzE5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs
+ aWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjEYMBYGA1UEAxMPVXJzdWxhI
+ EhhbXBzdGVyMR8wHQYJKoZIhvcNAQkBFhB1aGFtQGV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQ
+ UAA4GNADCBiQKBgQDuxgp5ELV9LmhxWMpV7qc4028QQT3+zzFDXhruuXE7ji2n3S3ea8bOwDtJh+q
+ nsDe561DhHHHlgIjMKCiDEizYMpxvJPYEXmvp0huRkMgpKZgmel95BSkt6TYmJ0erS3aoimOHLEFi
+ mmnTLolNRMiWqNBvqwobx940PGwUWEePKQIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJYIZIAYb4Q
+ gENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBSjI94TbBmuDEeUUO
+ iC37EK0Uf0XjCBoQYDVR0jBIGZMIGWgBRLbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1U
+ EBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0
+ ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAM
+ A0GCSqGSIb3DQEBBAUAA4GBAIgUcARb3OlWYNbmr1nmqESuxLn16uqI1Ot6WkcICvpkdQ+Bo+R9AP
+ 05xpoXocZtKdNvBu3FNxB/jFkiOcLU2lX7Px1Ijnsjh60qVRy9HOsHCungIKlGcnXLKHmKu0y//5j
+ ds/HnaJsGcHI5JRG7CBJbW+wrwge3trJ1xHJI8prN
+
+dn: documentTitle=book1,dc=example,dc=com
+objectClass: document
+description: abstract1
+documentTitle: book1
+documentAuthor: cn=Mitya Kovalev,dc=example,dc=com
+documentAuthor: cn=Torvlobnor Puzdoy,dc=example,dc=com
+documentIdentifier: document 1
+
+dn: documentTitle=book2,dc=example,dc=com
+objectClass: document
+description: abstract2
+documentTitle: book2
+documentAuthor: cn=Mitya Kovalev,dc=example,dc=com
+documentIdentifier: document 2
+
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Mitya Kovalev
+sn: Kovalev
+seeAlso: documentTitle=book1,dc=example,dc=com
+seeAlso: documentTitle=book2,dc=example,dc=com
+givenName: Mitya
+telephoneNumber: 222-3234
+telephoneNumber: 332-2334
+
+dn: cn=Torvlobnor Puzdoy,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Torvlobnor Puzdoy
+sn: Puzdoy
+seeAlso: documentTitle=book1,dc=example,dc=com
+givenName: Torvlobnor
+telephoneNumber: 545-4563
+
+# refldap://localhost:9012/dc=example,dc=com??one
+
+# Testing subtree search...
+dn: cn=Akakiy Zinberstein,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: pkiUser
+cn: Akakiy Zinberstein
+sn: Zinberstein
+givenName: Akakiy
+userCertificate;binary:: MIIDazCCAtSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADB3MQswCQYDV
+ QQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTH
+ RkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhc
+ NMDMxMDE3MTYzMzE5WhcNMDQxMDE2MTYzMzE5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs
+ aWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjEYMBYGA1UEAxMPVXJzdWxhI
+ EhhbXBzdGVyMR8wHQYJKoZIhvcNAQkBFhB1aGFtQGV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQ
+ UAA4GNADCBiQKBgQDuxgp5ELV9LmhxWMpV7qc4028QQT3+zzFDXhruuXE7ji2n3S3ea8bOwDtJh+q
+ nsDe561DhHHHlgIjMKCiDEizYMpxvJPYEXmvp0huRkMgpKZgmel95BSkt6TYmJ0erS3aoimOHLEFi
+ mmnTLolNRMiWqNBvqwobx940PGwUWEePKQIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJYIZIAYb4Q
+ gENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBSjI94TbBmuDEeUUO
+ iC37EK0Uf0XjCBoQYDVR0jBIGZMIGWgBRLbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1U
+ EBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0
+ ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAM
+ A0GCSqGSIb3DQEBBAUAA4GBAIgUcARb3OlWYNbmr1nmqESuxLn16uqI1Ot6WkcICvpkdQ+Bo+R9AP
+ 05xpoXocZtKdNvBu3FNxB/jFkiOcLU2lX7Px1Ijnsjh60qVRy9HOsHCungIKlGcnXLKHmKu0y//5j
+ ds/HnaJsGcHI5JRG7CBJbW+wrwge3trJ1xHJI8prN
+
+dn: documentTitle=book1,dc=example,dc=com
+objectClass: document
+description: abstract1
+documentTitle: book1
+documentAuthor: cn=Mitya Kovalev,dc=example,dc=com
+documentAuthor: cn=Torvlobnor Puzdoy,dc=example,dc=com
+documentIdentifier: document 1
+
+dn: documentTitle=book2,dc=example,dc=com
+objectClass: document
+description: abstract2
+documentTitle: book2
+documentAuthor: cn=Mitya Kovalev,dc=example,dc=com
+documentIdentifier: document 2
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example
+dc: example
+
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Mitya Kovalev
+sn: Kovalev
+seeAlso: documentTitle=book1,dc=example,dc=com
+seeAlso: documentTitle=book2,dc=example,dc=com
+givenName: Mitya
+telephoneNumber: 222-3234
+telephoneNumber: 332-2334
+
+dn: cn=Torvlobnor Puzdoy,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Torvlobnor Puzdoy
+sn: Puzdoy
+seeAlso: documentTitle=book1,dc=example,dc=com
+givenName: Torvlobnor
+telephoneNumber: 545-4563
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
+# Testing subtree search with manageDSAit...
+dn: cn=Akakiy Zinberstein,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: pkiUser
+cn: Akakiy Zinberstein
+sn: Zinberstein
+givenName: Akakiy
+userCertificate;binary:: MIIDazCCAtSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADB3MQswCQYDV
+ QQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTH
+ RkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhc
+ NMDMxMDE3MTYzMzE5WhcNMDQxMDE2MTYzMzE5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs
+ aWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjEYMBYGA1UEAxMPVXJzdWxhI
+ EhhbXBzdGVyMR8wHQYJKoZIhvcNAQkBFhB1aGFtQGV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQ
+ UAA4GNADCBiQKBgQDuxgp5ELV9LmhxWMpV7qc4028QQT3+zzFDXhruuXE7ji2n3S3ea8bOwDtJh+q
+ nsDe561DhHHHlgIjMKCiDEizYMpxvJPYEXmvp0huRkMgpKZgmel95BSkt6TYmJ0erS3aoimOHLEFi
+ mmnTLolNRMiWqNBvqwobx940PGwUWEePKQIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJYIZIAYb4Q
+ gENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBSjI94TbBmuDEeUUO
+ iC37EK0Uf0XjCBoQYDVR0jBIGZMIGWgBRLbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1U
+ EBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0
+ ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAM
+ A0GCSqGSIb3DQEBBAUAA4GBAIgUcARb3OlWYNbmr1nmqESuxLn16uqI1Ot6WkcICvpkdQ+Bo+R9AP
+ 05xpoXocZtKdNvBu3FNxB/jFkiOcLU2lX7Px1Ijnsjh60qVRy9HOsHCungIKlGcnXLKHmKu0y//5j
+ ds/HnaJsGcHI5JRG7CBJbW+wrwge3trJ1xHJI8prN
+
+dn: documentTitle=book1,dc=example,dc=com
+objectClass: document
+description: abstract1
+documentTitle: book1
+documentAuthor: cn=Mitya Kovalev,dc=example,dc=com
+documentAuthor: cn=Torvlobnor Puzdoy,dc=example,dc=com
+documentIdentifier: document 1
+
+dn: documentTitle=book2,dc=example,dc=com
+objectClass: document
+description: abstract2
+documentTitle: book2
+documentAuthor: cn=Mitya Kovalev,dc=example,dc=com
+documentIdentifier: document 2
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example
+dc: example
+
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Mitya Kovalev
+sn: Kovalev
+seeAlso: documentTitle=book1,dc=example,dc=com
+seeAlso: documentTitle=book2,dc=example,dc=com
+givenName: Mitya
+telephoneNumber: 222-3234
+telephoneNumber: 332-2334
+
+dn: ou=Referral,dc=example,dc=com
+objectClass: referral
+objectClass: extensibleObject
+ou: Referral
+ref: ldap://localhost:9012/
+
+dn: cn=Torvlobnor Puzdoy,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Torvlobnor Puzdoy
+sn: Puzdoy
+seeAlso: documentTitle=book1,dc=example,dc=com
+givenName: Torvlobnor
+telephoneNumber: 545-4563
+
+# Testing invalid filter...
+# Testing exact search...
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Mitya Kovalev
+sn: Kovalev
+seeAlso: documentTitle=book1,dc=example,dc=com
+seeAlso: documentTitle=book2,dc=example,dc=com
+givenName: Mitya
+telephoneNumber: 222-3234
+telephoneNumber: 332-2334
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
+# Testing substrings initial search...
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Mitya Kovalev
+sn: Kovalev
+seeAlso: documentTitle=book1,dc=example,dc=com
+seeAlso: documentTitle=book2,dc=example,dc=com
+givenName: Mitya
+telephoneNumber: 222-3234
+telephoneNumber: 332-2334
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
+# Testing substrings any search...
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Mitya Kovalev
+sn: Kovalev
+seeAlso: documentTitle=book1,dc=example,dc=com
+seeAlso: documentTitle=book2,dc=example,dc=com
+givenName: Mitya
+telephoneNumber: 222-3234
+telephoneNumber: 332-2334
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
+# Testing substrings final search...
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Mitya Kovalev
+sn: Kovalev
+seeAlso: documentTitle=book1,dc=example,dc=com
+seeAlso: documentTitle=book2,dc=example,dc=com
+givenName: Mitya
+telephoneNumber: 222-3234
+telephoneNumber: 332-2334
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
+# Testing approx search...
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Mitya Kovalev
+sn: Kovalev
+seeAlso: documentTitle=book1,dc=example,dc=com
+seeAlso: documentTitle=book2,dc=example,dc=com
+givenName: Mitya
+telephoneNumber: 222-3234
+telephoneNumber: 332-2334
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
+# Testing extensible filter search...
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Mitya Kovalev
+sn: Kovalev
+seeAlso: documentTitle=book1,dc=example,dc=com
+seeAlso: documentTitle=book2,dc=example,dc=com
+givenName: Mitya
+telephoneNumber: 222-3234
+telephoneNumber: 332-2334
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
+# Testing search for telephoneNumber...
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Mitya Kovalev
+sn: Kovalev
+seeAlso: documentTitle=book1,dc=example,dc=com
+seeAlso: documentTitle=book2,dc=example,dc=com
+givenName: Mitya
+telephoneNumber: 222-3234
+telephoneNumber: 332-2334
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
+# Testing AND search...
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Mitya Kovalev
+sn: Kovalev
+seeAlso: documentTitle=book1,dc=example,dc=com
+seeAlso: documentTitle=book2,dc=example,dc=com
+givenName: Mitya
+telephoneNumber: 222-3234
+telephoneNumber: 332-2334
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
+# Testing AND search on objectClass...
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example
+dc: example
+
+# Testing OR search...
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Mitya Kovalev
+sn: Kovalev
+seeAlso: documentTitle=book1,dc=example,dc=com
+seeAlso: documentTitle=book2,dc=example,dc=com
+givenName: Mitya
+telephoneNumber: 222-3234
+telephoneNumber: 332-2334
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
+# Testing OR search on objectClass...
+dn: documentTitle=book1,dc=example,dc=com
+objectClass: document
+description: abstract1
+documentTitle: book1
+documentAuthor: cn=Mitya Kovalev,dc=example,dc=com
+documentAuthor: cn=Torvlobnor Puzdoy,dc=example,dc=com
+documentIdentifier: document 1
+
+dn: documentTitle=book2,dc=example,dc=com
+objectClass: document
+description: abstract2
+documentTitle: book2
+documentAuthor: cn=Mitya Kovalev,dc=example,dc=com
+documentIdentifier: document 2
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example
+dc: example
+
+# Testing NOT search...
+dn: cn=Akakiy Zinberstein,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: pkiUser
+cn: Akakiy Zinberstein
+sn: Zinberstein
+givenName: Akakiy
+userCertificate;binary:: MIIDazCCAtSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADB3MQswCQYDV
+ QQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTH
+ RkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhc
+ NMDMxMDE3MTYzMzE5WhcNMDQxMDE2MTYzMzE5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs
+ aWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjEYMBYGA1UEAxMPVXJzdWxhI
+ EhhbXBzdGVyMR8wHQYJKoZIhvcNAQkBFhB1aGFtQGV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQ
+ UAA4GNADCBiQKBgQDuxgp5ELV9LmhxWMpV7qc4028QQT3+zzFDXhruuXE7ji2n3S3ea8bOwDtJh+q
+ nsDe561DhHHHlgIjMKCiDEizYMpxvJPYEXmvp0huRkMgpKZgmel95BSkt6TYmJ0erS3aoimOHLEFi
+ mmnTLolNRMiWqNBvqwobx940PGwUWEePKQIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJYIZIAYb4Q
+ gENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBSjI94TbBmuDEeUUO
+ iC37EK0Uf0XjCBoQYDVR0jBIGZMIGWgBRLbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1U
+ EBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0
+ ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAM
+ A0GCSqGSIb3DQEBBAUAA4GBAIgUcARb3OlWYNbmr1nmqESuxLn16uqI1Ot6WkcICvpkdQ+Bo+R9AP
+ 05xpoXocZtKdNvBu3FNxB/jFkiOcLU2lX7Px1Ijnsjh60qVRy9HOsHCungIKlGcnXLKHmKu0y//5j
+ ds/HnaJsGcHI5JRG7CBJbW+wrwge3trJ1xHJI8prN
+
+dn: cn=Torvlobnor Puzdoy,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Torvlobnor Puzdoy
+sn: Puzdoy
+seeAlso: documentTitle=book1,dc=example,dc=com
+givenName: Torvlobnor
+telephoneNumber: 545-4563
+
+# Testing NOT search on objectClass...
+dn: documentTitle=book1,dc=example,dc=com
+objectClass: document
+description: abstract1
+documentTitle: book1
+documentAuthor: cn=Mitya Kovalev,dc=example,dc=com
+documentAuthor: cn=Torvlobnor Puzdoy,dc=example,dc=com
+documentIdentifier: document 1
+
+dn: documentTitle=book2,dc=example,dc=com
+objectClass: document
+description: abstract2
+documentTitle: book2
+documentAuthor: cn=Mitya Kovalev,dc=example,dc=com
+documentIdentifier: document 2
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example
+dc: example
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
+# Testing NOT search on "auxiliary" objectClass...
+dn: cn=Akakiy Zinberstein,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: pkiUser
+cn: Akakiy Zinberstein
+sn: Zinberstein
+givenName: Akakiy
+userCertificate;binary:: MIIDazCCAtSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADB3MQswCQYDV
+ QQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTH
+ RkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhc
+ NMDMxMDE3MTYzMzE5WhcNMDQxMDE2MTYzMzE5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs
+ aWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjEYMBYGA1UEAxMPVXJzdWxhI
+ EhhbXBzdGVyMR8wHQYJKoZIhvcNAQkBFhB1aGFtQGV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQ
+ UAA4GNADCBiQKBgQDuxgp5ELV9LmhxWMpV7qc4028QQT3+zzFDXhruuXE7ji2n3S3ea8bOwDtJh+q
+ nsDe561DhHHHlgIjMKCiDEizYMpxvJPYEXmvp0huRkMgpKZgmel95BSkt6TYmJ0erS3aoimOHLEFi
+ mmnTLolNRMiWqNBvqwobx940PGwUWEePKQIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJYIZIAYb4Q
+ gENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBSjI94TbBmuDEeUUO
+ iC37EK0Uf0XjCBoQYDVR0jBIGZMIGWgBRLbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1U
+ EBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0
+ ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAM
+ A0GCSqGSIb3DQEBBAUAA4GBAIgUcARb3OlWYNbmr1nmqESuxLn16uqI1Ot6WkcICvpkdQ+Bo+R9AP
+ 05xpoXocZtKdNvBu3FNxB/jFkiOcLU2lX7Px1Ijnsjh60qVRy9HOsHCungIKlGcnXLKHmKu0y//5j
+ ds/HnaJsGcHI5JRG7CBJbW+wrwge3trJ1xHJI8prN
+
+dn: documentTitle=book1,dc=example,dc=com
+objectClass: document
+description: abstract1
+documentTitle: book1
+documentAuthor: cn=Mitya Kovalev,dc=example,dc=com
+documentAuthor: cn=Torvlobnor Puzdoy,dc=example,dc=com
+documentIdentifier: document 1
+
+dn: documentTitle=book2,dc=example,dc=com
+objectClass: document
+description: abstract2
+documentTitle: book2
+documentAuthor: cn=Mitya Kovalev,dc=example,dc=com
+documentIdentifier: document 2
+
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Mitya Kovalev
+sn: Kovalev
+seeAlso: documentTitle=book1,dc=example,dc=com
+seeAlso: documentTitle=book2,dc=example,dc=com
+givenName: Mitya
+telephoneNumber: 222-3234
+telephoneNumber: 332-2334
+
+dn: cn=Torvlobnor Puzdoy,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Torvlobnor Puzdoy
+sn: Puzdoy
+seeAlso: documentTitle=book1,dc=example,dc=com
+givenName: Torvlobnor
+telephoneNumber: 545-4563
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
+# Testing attribute inheritance in filter...
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example
+dc: example
+
+# Testing undefined attribute in filter...
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example
+dc: example
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
+# Testing objectClass inheritance in filter...
+dn: cn=Akakiy Zinberstein,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: pkiUser
+cn: Akakiy Zinberstein
+sn: Zinberstein
+givenName: Akakiy
+userCertificate;binary:: MIIDazCCAtSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADB3MQswCQYDV
+ QQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTH
+ RkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhc
+ NMDMxMDE3MTYzMzE5WhcNMDQxMDE2MTYzMzE5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs
+ aWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjEYMBYGA1UEAxMPVXJzdWxhI
+ EhhbXBzdGVyMR8wHQYJKoZIhvcNAQkBFhB1aGFtQGV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQ
+ UAA4GNADCBiQKBgQDuxgp5ELV9LmhxWMpV7qc4028QQT3+zzFDXhruuXE7ji2n3S3ea8bOwDtJh+q
+ nsDe561DhHHHlgIjMKCiDEizYMpxvJPYEXmvp0huRkMgpKZgmel95BSkt6TYmJ0erS3aoimOHLEFi
+ mmnTLolNRMiWqNBvqwobx940PGwUWEePKQIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJYIZIAYb4Q
+ gENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBSjI94TbBmuDEeUUO
+ iC37EK0Uf0XjCBoQYDVR0jBIGZMIGWgBRLbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1U
+ EBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0
+ ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAM
+ A0GCSqGSIb3DQEBBAUAA4GBAIgUcARb3OlWYNbmr1nmqESuxLn16uqI1Ot6WkcICvpkdQ+Bo+R9AP
+ 05xpoXocZtKdNvBu3FNxB/jFkiOcLU2lX7Px1Ijnsjh60qVRy9HOsHCungIKlGcnXLKHmKu0y//5j
+ ds/HnaJsGcHI5JRG7CBJbW+wrwge3trJ1xHJI8prN
+
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Mitya Kovalev
+sn: Kovalev
+seeAlso: documentTitle=book1,dc=example,dc=com
+seeAlso: documentTitle=book2,dc=example,dc=com
+givenName: Mitya
+telephoneNumber: 222-3234
+telephoneNumber: 332-2334
+
+dn: cn=Torvlobnor Puzdoy,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Torvlobnor Puzdoy
+sn: Puzdoy
+seeAlso: documentTitle=book1,dc=example,dc=com
+givenName: Torvlobnor
+telephoneNumber: 545-4563
+
+# Testing "auxiliary" objectClass in filter...
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example
+dc: example
+
+# Testing hasSubordinates in filter...
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example
+dc: example
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
+# Testing entryUUID in filter...
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Mitya Kovalev
+sn: Kovalev
+seeAlso: documentTitle=book1,dc=example,dc=com
+seeAlso: documentTitle=book2,dc=example,dc=com
+givenName: Mitya
+telephoneNumber: 222-3234
+telephoneNumber: 332-2334
+
+# Testing attribute inheritance in requested attributes...
+dn: cn=Mitya Kovalev,dc=example,dc=com
+cn: Mitya Kovalev
+sn: Kovalev
+givenName: Mitya
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
+# Testing objectClass in requested attributes...
+dn: cn=Akakiy Zinberstein,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: pkiUser
+
+dn: documentTitle=book1,dc=example,dc=com
+objectClass: document
+
+dn: documentTitle=book2,dc=example,dc=com
+objectClass: document
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+
+dn: cn=Torvlobnor Puzdoy,dc=example,dc=com
+objectClass: inetOrgPerson
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
+# Testing operational attributes in request...
+dn: cn=Akakiy Zinberstein,dc=example,dc=com
+structuralObjectClass: inetOrgPerson
+entryDN: cn=Akakiy Zinberstein,dc=example,dc=com
+subschemaSubentry: cn=Subschema
+hasSubordinates: FALSE
+entryUUID: 00000001-0000-0003-0000-000000000000
+
+dn: documentTitle=book1,dc=example,dc=com
+structuralObjectClass: document
+entryDN: documentTitle=book1,dc=example,dc=com
+subschemaSubentry: cn=Subschema
+hasSubordinates: FALSE
+entryUUID: 00000002-0000-0001-0000-000000000000
+
+dn: documentTitle=book2,dc=example,dc=com
+structuralObjectClass: document
+entryDN: documentTitle=book2,dc=example,dc=com
+subschemaSubentry: cn=Subschema
+hasSubordinates: FALSE
+entryUUID: 00000002-0000-0002-0000-000000000000
+
+dn: dc=example,dc=com
+structuralObjectClass: organization
+entryDN: dc=example,dc=com
+subschemaSubentry: cn=Subschema
+hasSubordinates: TRUE
+entryUUID: 00000003-0000-0001-0000-000000000000
+
+dn: cn=Mitya Kovalev,dc=example,dc=com
+structuralObjectClass: inetOrgPerson
+entryDN: cn=Mitya Kovalev,dc=example,dc=com
+subschemaSubentry: cn=Subschema
+hasSubordinates: FALSE
+entryUUID: 00000001-0000-0001-0000-000000000000
+
+dn: cn=Torvlobnor Puzdoy,dc=example,dc=com
+structuralObjectClass: inetOrgPerson
+entryDN: cn=Torvlobnor Puzdoy,dc=example,dc=com
+subschemaSubentry: cn=Subschema
+hasSubordinates: FALSE
+entryUUID: 00000001-0000-0002-0000-000000000000
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
diff --git a/tests/data/sql-write.out b/tests/data/sql-write.out
new file mode 100644
index 0000000..45fa164
--- /dev/null
+++ b/tests/data/sql-write.out
@@ -0,0 +1,576 @@
+# Using ldapsearch to retrieve all the entries...
+dn: cn=Akakiy Zinberstein,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: pkiUser
+cn: Akakiy Zinberstein
+sn: Zinberstein
+givenName: Akakiy
+userCertificate;binary:: MIIDazCCAtSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADB3MQswCQYDV
+ QQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTH
+ RkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhc
+ NMDMxMDE3MTYzMzE5WhcNMDQxMDE2MTYzMzE5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs
+ aWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjEYMBYGA1UEAxMPVXJzdWxhI
+ EhhbXBzdGVyMR8wHQYJKoZIhvcNAQkBFhB1aGFtQGV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQ
+ UAA4GNADCBiQKBgQDuxgp5ELV9LmhxWMpV7qc4028QQT3+zzFDXhruuXE7ji2n3S3ea8bOwDtJh+q
+ nsDe561DhHHHlgIjMKCiDEizYMpxvJPYEXmvp0huRkMgpKZgmel95BSkt6TYmJ0erS3aoimOHLEFi
+ mmnTLolNRMiWqNBvqwobx940PGwUWEePKQIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJYIZIAYb4Q
+ gENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBSjI94TbBmuDEeUUO
+ iC37EK0Uf0XjCBoQYDVR0jBIGZMIGWgBRLbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1U
+ EBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0
+ ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAM
+ A0GCSqGSIb3DQEBBAUAA4GBAIgUcARb3OlWYNbmr1nmqESuxLn16uqI1Ot6WkcICvpkdQ+Bo+R9AP
+ 05xpoXocZtKdNvBu3FNxB/jFkiOcLU2lX7Px1Ijnsjh60qVRy9HOsHCungIKlGcnXLKHmKu0y//5j
+ ds/HnaJsGcHI5JRG7CBJbW+wrwge3trJ1xHJI8prN
+
+dn: documentTitle=book1,dc=example,dc=com
+objectClass: document
+description: abstract1
+documentTitle: book1
+documentAuthor: cn=Mitya Kovalev,dc=example,dc=com
+documentAuthor: cn=Torvlobnor Puzdoy,dc=example,dc=com
+documentIdentifier: document 1
+
+dn: documentTitle=book2,dc=example,dc=com
+objectClass: document
+description: abstract2
+documentTitle: book2
+documentAuthor: cn=Mitya Kovalev,dc=example,dc=com
+documentIdentifier: document 2
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example
+dc: example
+
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Mitya Kovalev
+sn: Kovalev
+seeAlso: documentTitle=book1,dc=example,dc=com
+seeAlso: documentTitle=book2,dc=example,dc=com
+givenName: Mitya
+telephoneNumber: 222-3234
+telephoneNumber: 332-2334
+
+dn: cn=Torvlobnor Puzdoy,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Torvlobnor Puzdoy
+sn: Puzdoy
+seeAlso: documentTitle=book1,dc=example,dc=com
+givenName: Torvlobnor
+telephoneNumber: 545-4563
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
+# Using ldapsearch to retrieve all the entries...
+dn: cn=Akakiy Zinberstein,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: pkiUser
+cn: Akakiy Zinberstein
+sn: Zinberstein
+givenName: Akakiy
+userCertificate;binary:: MIIDazCCAtSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADB3MQswCQYDV
+ QQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTH
+ RkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhc
+ NMDMxMDE3MTYzMzE5WhcNMDQxMDE2MTYzMzE5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs
+ aWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjEYMBYGA1UEAxMPVXJzdWxhI
+ EhhbXBzdGVyMR8wHQYJKoZIhvcNAQkBFhB1aGFtQGV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQ
+ UAA4GNADCBiQKBgQDuxgp5ELV9LmhxWMpV7qc4028QQT3+zzFDXhruuXE7ji2n3S3ea8bOwDtJh+q
+ nsDe561DhHHHlgIjMKCiDEizYMpxvJPYEXmvp0huRkMgpKZgmel95BSkt6TYmJ0erS3aoimOHLEFi
+ mmnTLolNRMiWqNBvqwobx940PGwUWEePKQIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJYIZIAYb4Q
+ gENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBSjI94TbBmuDEeUUO
+ iC37EK0Uf0XjCBoQYDVR0jBIGZMIGWgBRLbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1U
+ EBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0
+ ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAM
+ A0GCSqGSIb3DQEBBAUAA4GBAIgUcARb3OlWYNbmr1nmqESuxLn16uqI1Ot6WkcICvpkdQ+Bo+R9AP
+ 05xpoXocZtKdNvBu3FNxB/jFkiOcLU2lX7Px1Ijnsjh60qVRy9HOsHCungIKlGcnXLKHmKu0y//5j
+ ds/HnaJsGcHI5JRG7CBJbW+wrwge3trJ1xHJI8prN
+
+dn: o=An Org,dc=example,dc=com
+objectClass: organization
+o: An Org
+
+dn: documentTitle=book1,dc=example,dc=com
+objectClass: document
+description: abstract1
+documentTitle: book1
+documentAuthor: cn=Mitya Kovalev,dc=example,dc=com
+documentAuthor: cn=Torvlobnor Puzdoy,dc=example,dc=com
+documentIdentifier: document 1
+
+dn: documentTitle=book2,dc=example,dc=com
+objectClass: document
+description: abstract2
+documentTitle: book2
+documentAuthor: cn=Mitya Kovalev,dc=example,dc=com
+documentIdentifier: document 2
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example
+dc: example
+
+dn: cn=Lev Tolstoij,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Lev Tolstoij
+sn: Tolstoij
+seeAlso: documentTitle=War and Peace,dc=example,dc=com
+givenName: Lev
+telephoneNumber: +39 02 XXXX YYYY
+telephoneNumber: +39 02 XXXX ZZZZ
+
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Mitya Kovalev
+sn: Kovalev
+seeAlso: documentTitle=book1,dc=example,dc=com
+seeAlso: documentTitle=book2,dc=example,dc=com
+givenName: Mitya
+telephoneNumber: 222-3234
+telephoneNumber: 332-2334
+
+dn: cn=Some One,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: simpleSecurityObject
+cn: Some One
+sn: One
+givenName: Some
+telephoneNumber: +1 800 900 1234
+telephoneNumber: +1 800 900 1235
+
+dn: dc=subnet,dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: SubNet
+dc: subnet
+
+dn: cn=SubNet User,dc=subnet,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: SubNet User
+sn: User
+givenName: SubNet
+
+dn: dc=subnet2,dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: SubNet 2
+dc: subnet 2
+
+dn: cn=Torvlobnor Puzdoy,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Torvlobnor Puzdoy
+sn: Puzdoy
+seeAlso: documentTitle=book1,dc=example,dc=com
+givenName: Torvlobnor
+telephoneNumber: 545-4563
+
+dn: documentTitle=War and Peace,dc=example,dc=com
+objectClass: document
+description: Historical novel
+documentTitle: War and Peace
+documentAuthor: cn=Lev Tolstoij,dc=example,dc=com
+documentIdentifier: document 3
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
+# Using ldapsearch to retrieve all the entries...
+dn: cn=Akakiy Zinberstein,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: pkiUser
+cn: Akakiy Zinberstein
+sn: Zinberstein
+givenName: Akakiy
+userCertificate;binary:: MIIDazCCAtSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADB3MQswCQYDV
+ QQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTH
+ RkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhc
+ NMDMxMDE3MTYzMzE5WhcNMDQxMDE2MTYzMzE5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs
+ aWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjEYMBYGA1UEAxMPVXJzdWxhI
+ EhhbXBzdGVyMR8wHQYJKoZIhvcNAQkBFhB1aGFtQGV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQ
+ UAA4GNADCBiQKBgQDuxgp5ELV9LmhxWMpV7qc4028QQT3+zzFDXhruuXE7ji2n3S3ea8bOwDtJh+q
+ nsDe561DhHHHlgIjMKCiDEizYMpxvJPYEXmvp0huRkMgpKZgmel95BSkt6TYmJ0erS3aoimOHLEFi
+ mmnTLolNRMiWqNBvqwobx940PGwUWEePKQIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJYIZIAYb4Q
+ gENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBSjI94TbBmuDEeUUO
+ iC37EK0Uf0XjCBoQYDVR0jBIGZMIGWgBRLbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1U
+ EBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0
+ ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAM
+ A0GCSqGSIb3DQEBBAUAA4GBAIgUcARb3OlWYNbmr1nmqESuxLn16uqI1Ot6WkcICvpkdQ+Bo+R9AP
+ 05xpoXocZtKdNvBu3FNxB/jFkiOcLU2lX7Px1Ijnsjh60qVRy9HOsHCungIKlGcnXLKHmKu0y//5j
+ ds/HnaJsGcHI5JRG7CBJbW+wrwge3trJ1xHJI8prN
+
+dn: o=An Org,dc=example,dc=com
+objectClass: organization
+o: An Org
+
+dn: documentTitle=book1,dc=example,dc=com
+objectClass: document
+description: abstract1
+documentTitle: book1
+documentAuthor: cn=Lev Tolstoij,dc=example,dc=com
+documentAuthor: cn=Mitya Kovalev,dc=example,dc=com
+documentAuthor: cn=Torvlobnor Puzdoy,dc=example,dc=com
+documentIdentifier: document 1
+
+dn: documentTitle=book2,dc=example,dc=com
+objectClass: document
+description: abstract2
+documentTitle: book2
+documentAuthor: cn=Lev Tolstoij,dc=example,dc=com
+documentAuthor: cn=Mitya Kovalev,dc=example,dc=com
+documentIdentifier: document 2
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example
+dc: example
+
+dn: cn=Lev Tolstoij,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Lev Tolstoij
+sn: Tolstoij
+seeAlso: documentTitle=book1,dc=example,dc=com
+seeAlso: documentTitle=book2,dc=example,dc=com
+seeAlso: documentTitle=War and Peace,dc=example,dc=com
+givenName: Lev
+telephoneNumber: +39 02 XXXX ZZZZ
+telephoneNumber: +39 333 ZZZ 1234
+
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Mitya Kovalev
+sn: Kovalev
+seeAlso: documentTitle=book1,dc=example,dc=com
+seeAlso: documentTitle=book2,dc=example,dc=com
+givenName: Mitya
+telephoneNumber: +1 800 123 4567
+telephoneNumber: 222-3234
+telephoneNumber: 332-2334
+
+dn: cn=Some One,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: simpleSecurityObject
+cn: Some One
+sn: One
+givenName: Some
+
+dn: dc=subnet,dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: SubNet
+dc: subnet
+
+dn: cn=SubNet User,dc=subnet,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: SubNet User
+sn: User
+givenName: SubNet
+
+dn: dc=subnet2,dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: SubNet 2
+dc: subnet 2
+
+dn: cn=Torvlobnor Puzdoy,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Torvlobnor Puzdoy
+sn: Puzdoy
+seeAlso: documentTitle=book1,dc=example,dc=com
+givenName: Torvlobnor
+telephoneNumber: 545-4563
+
+dn: documentTitle=War and Peace,dc=example,dc=com
+objectClass: document
+description: Historical novel
+documentTitle: War and Peace
+documentAuthor: cn=Lev Tolstoij,dc=example,dc=com
+documentIdentifier: document 3
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
+# Using ldapsearch to retrieve all the entries...
+dn: cn=Akakiy Zinberstein,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: pkiUser
+cn: Akakiy Zinberstein
+sn: Zinberstein
+givenName: Akakiy
+userCertificate;binary:: MIIDazCCAtSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADB3MQswCQYDV
+ QQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTH
+ RkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhc
+ NMDMxMDE3MTYzMzE5WhcNMDQxMDE2MTYzMzE5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs
+ aWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjEYMBYGA1UEAxMPVXJzdWxhI
+ EhhbXBzdGVyMR8wHQYJKoZIhvcNAQkBFhB1aGFtQGV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQ
+ UAA4GNADCBiQKBgQDuxgp5ELV9LmhxWMpV7qc4028QQT3+zzFDXhruuXE7ji2n3S3ea8bOwDtJh+q
+ nsDe561DhHHHlgIjMKCiDEizYMpxvJPYEXmvp0huRkMgpKZgmel95BSkt6TYmJ0erS3aoimOHLEFi
+ mmnTLolNRMiWqNBvqwobx940PGwUWEePKQIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJYIZIAYb4Q
+ gENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBSjI94TbBmuDEeUUO
+ iC37EK0Uf0XjCBoQYDVR0jBIGZMIGWgBRLbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1U
+ EBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0
+ ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAM
+ A0GCSqGSIb3DQEBBAUAA4GBAIgUcARb3OlWYNbmr1nmqESuxLn16uqI1Ot6WkcICvpkdQ+Bo+R9AP
+ 05xpoXocZtKdNvBu3FNxB/jFkiOcLU2lX7Px1Ijnsjh60qVRy9HOsHCungIKlGcnXLKHmKu0y//5j
+ ds/HnaJsGcHI5JRG7CBJbW+wrwge3trJ1xHJI8prN
+
+dn: o=An Org,dc=example,dc=com
+objectClass: organization
+o: An Org
+
+dn: documentTitle=book2,dc=example,dc=com
+objectClass: document
+description: abstract2
+documentTitle: book2
+documentAuthor: cn=Lev Tolstoij,dc=example,dc=com
+documentAuthor: cn=Mitya Kovalev,dc=example,dc=com
+documentIdentifier: document 2
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example
+dc: example
+
+dn: cn=Lev Tolstoij,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Lev Tolstoij
+sn: Tolstoij
+seeAlso: documentTitle=book2,dc=example,dc=com
+seeAlso: documentTitle=War and Peace,dc=example,dc=com
+givenName: Lev
+telephoneNumber: +39 02 XXXX ZZZZ
+telephoneNumber: +39 333 ZZZ 1234
+
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Mitya Kovalev
+sn: Kovalev
+seeAlso: documentTitle=book2,dc=example,dc=com
+givenName: Mitya
+telephoneNumber: +1 800 123 4567
+telephoneNumber: 222-3234
+telephoneNumber: 332-2334
+
+dn: cn=Some One,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: simpleSecurityObject
+cn: Some One
+sn: One
+givenName: Some
+
+dn: dc=subnet,dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: SubNet
+dc: subnet
+
+dn: cn=SubNet User,dc=subnet,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: SubNet User
+sn: User
+givenName: SubNet
+
+dn: documentTitle=War and Peace,dc=example,dc=com
+objectClass: document
+description: Historical novel
+documentTitle: War and Peace
+documentAuthor: cn=Lev Tolstoij,dc=example,dc=com
+documentIdentifier: document 3
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
+# Using ldapsearch to retrieve all the entries...
+dn: cn=Akakiy Zinberstein,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: pkiUser
+cn: Akakiy Zinberstein
+sn: Zinberstein
+givenName: Akakiy
+userCertificate;binary:: MIIDazCCAtSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADB3MQswCQYDV
+ QQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTH
+ RkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhc
+ NMDMxMDE3MTYzMzE5WhcNMDQxMDE2MTYzMzE5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs
+ aWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjEYMBYGA1UEAxMPVXJzdWxhI
+ EhhbXBzdGVyMR8wHQYJKoZIhvcNAQkBFhB1aGFtQGV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQ
+ UAA4GNADCBiQKBgQDuxgp5ELV9LmhxWMpV7qc4028QQT3+zzFDXhruuXE7ji2n3S3ea8bOwDtJh+q
+ nsDe561DhHHHlgIjMKCiDEizYMpxvJPYEXmvp0huRkMgpKZgmel95BSkt6TYmJ0erS3aoimOHLEFi
+ mmnTLolNRMiWqNBvqwobx940PGwUWEePKQIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJYIZIAYb4Q
+ gENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBSjI94TbBmuDEeUUO
+ iC37EK0Uf0XjCBoQYDVR0jBIGZMIGWgBRLbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1U
+ EBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0
+ ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAM
+ A0GCSqGSIb3DQEBBAUAA4GBAIgUcARb3OlWYNbmr1nmqESuxLn16uqI1Ot6WkcICvpkdQ+Bo+R9AP
+ 05xpoXocZtKdNvBu3FNxB/jFkiOcLU2lX7Px1Ijnsjh60qVRy9HOsHCungIKlGcnXLKHmKu0y//5j
+ ds/HnaJsGcHI5JRG7CBJbW+wrwge3trJ1xHJI8prN
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example
+dc: example
+
+dn: cn=Lev Tolstoij,dc=subnet,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Lev Tolstoij
+sn: Tolstoij
+seeAlso: documentTitle=Renamed Book,dc=example,dc=com
+seeAlso: documentTitle=War and Peace,dc=example,dc=com
+givenName: Lev
+telephoneNumber: +39 02 XXXX ZZZZ
+telephoneNumber: +39 333 ZZZ 1234
+
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Mitya Kovalev
+sn: Kovalev
+seeAlso: documentTitle=Renamed Book,dc=example,dc=com
+givenName: Mitya
+telephoneNumber: +1 800 123 4567
+telephoneNumber: 222-3234
+telephoneNumber: 332-2334
+
+dn: documentTitle=Renamed Book,dc=example,dc=com
+objectClass: document
+description: abstract2
+documentTitle: Renamed Book
+documentAuthor: cn=Lev Tolstoij,dc=subnet,dc=example,dc=com
+documentAuthor: cn=Mitya Kovalev,dc=example,dc=com
+documentIdentifier: document 2
+
+dn: o=Renamed Org,dc=example,dc=com
+objectClass: organization
+o: Renamed Org
+
+dn: cn=Some One,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: simpleSecurityObject
+cn: Some One
+sn: One
+givenName: Some
+
+dn: dc=subnet,dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: SubNet
+dc: subnet
+
+dn: cn=SubNet User,dc=subnet,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: SubNet User
+sn: User
+givenName: SubNet
+
+dn: documentTitle=War and Peace,dc=example,dc=com
+objectClass: document
+description: Historical novel
+documentTitle: War and Peace
+documentAuthor: cn=Lev Tolstoij,dc=subnet,dc=example,dc=com
+documentIdentifier: document 3
+
+# refldap://localhost:9012/dc=example,dc=com??sub
+
+# Using ldapsearch to retrieve the modified entry...
+dn: ou=Referral,dc=example,dc=com
+objectClass: referral
+objectClass: extensibleObject
+ou: Referral
+ref: ldap://localhost:9009/
+
+# Using ldapsearch to retrieve the renamed entry...
+dn: ou=Renamed Referral,dc=example,dc=com
+objectClass: referral
+objectClass: extensibleObject
+ou: Renamed Referral
+ref: ldap://localhost:9009/
+
+# Using ldapsearch to retrieve all the entries...
+dn: cn=Akakiy Zinberstein,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: pkiUser
+cn: Akakiy Zinberstein
+sn: Zinberstein
+givenName: Akakiy
+userCertificate;binary:: MIIDazCCAtSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADB3MQswCQYDV
+ QQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTH
+ RkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb20wHhc
+ NMDMxMDE3MTYzMzE5WhcNMDQxMDE2MTYzMzE5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs
+ aWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjEYMBYGA1UEAxMPVXJzdWxhI
+ EhhbXBzdGVyMR8wHQYJKoZIhvcNAQkBFhB1aGFtQGV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQ
+ UAA4GNADCBiQKBgQDuxgp5ELV9LmhxWMpV7qc4028QQT3+zzFDXhruuXE7ji2n3S3ea8bOwDtJh+q
+ nsDe561DhHHHlgIjMKCiDEizYMpxvJPYEXmvp0huRkMgpKZgmel95BSkt6TYmJ0erS3aoimOHLEFi
+ mmnTLolNRMiWqNBvqwobx940PGwUWEePKQIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJYIZIAYb4Q
+ gENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBSjI94TbBmuDEeUUO
+ iC37EK0Uf0XjCBoQYDVR0jBIGZMIGWgBRLbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1U
+ EBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0
+ ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAM
+ A0GCSqGSIb3DQEBBAUAA4GBAIgUcARb3OlWYNbmr1nmqESuxLn16uqI1Ot6WkcICvpkdQ+Bo+R9AP
+ 05xpoXocZtKdNvBu3FNxB/jFkiOcLU2lX7Px1Ijnsjh60qVRy9HOsHCungIKlGcnXLKHmKu0y//5j
+ ds/HnaJsGcHI5JRG7CBJbW+wrwge3trJ1xHJI8prN
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example
+dc: example
+
+dn: cn=Lev Tolstoij,dc=subnet,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Lev Tolstoij
+sn: Tolstoij
+seeAlso: documentTitle=Renamed Book,dc=example,dc=com
+seeAlso: documentTitle=War and Peace,dc=example,dc=com
+givenName: Lev
+telephoneNumber: +39 02 XXXX ZZZZ
+telephoneNumber: +39 333 ZZZ 1234
+
+dn: cn=Mitya Kovalev,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Mitya Kovalev
+sn: Kovalev
+seeAlso: documentTitle=Renamed Book,dc=example,dc=com
+givenName: Mitya
+telephoneNumber: +1 800 123 4567
+telephoneNumber: 222-3234
+telephoneNumber: 332-2334
+
+dn: documentTitle=Renamed Book,dc=example,dc=com
+objectClass: document
+description: abstract2
+documentTitle: Renamed Book
+documentAuthor: cn=Lev Tolstoij,dc=subnet,dc=example,dc=com
+documentAuthor: cn=Mitya Kovalev,dc=example,dc=com
+documentIdentifier: document 2
+
+dn: o=Renamed Org,dc=example,dc=com
+objectClass: organization
+o: Renamed Org
+
+dn: cn=Some One,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: simpleSecurityObject
+cn: Some One
+sn: One
+givenName: Some
+
+dn: dc=subnet,dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: SubNet
+dc: subnet
+
+dn: cn=SubNet User,dc=subnet,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: SubNet User
+sn: User
+givenName: SubNet
+
+dn: documentTitle=War and Peace,dc=example,dc=com
+objectClass: document
+description: Historical novel
+documentTitle: War and Peace
+documentAuthor: cn=Lev Tolstoij,dc=subnet,dc=example,dc=com
+documentIdentifier: document 3
+
+# refldap://localhost:9009/dc=example,dc=com??sub
+
diff --git a/tests/data/subtree-rename.out b/tests/data/subtree-rename.out
new file mode 100644
index 0000000..e5f753a
--- /dev/null
+++ b/tests/data/subtree-rename.out
@@ -0,0 +1,97 @@
+# Searching all database (after add)...
+dn: ou=Another parent,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Another parent
+
+dn: ou=Child,ou=Parent,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Child
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: ou=Grandchild,ou=Child,ou=Parent,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Grandchild
+
+dn: ou=Parent,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Parent
+
+# Searching all database (after PASS1)...
+dn: ou=Another parent,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Another parent
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: ou=Grandchild,ou=Renamed child,ou=Parent,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Grandchild
+
+dn: ou=Parent,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Parent
+
+dn: ou=Renamed child,ou=Parent,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Child
+ou: Renamed child
+
+# Searching all database (after PASS2)...
+dn: ou=Another parent,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Another parent
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: ou=Grandchild,ou=Renamed child,ou=Renamed parent,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Grandchild
+
+dn: ou=Renamed child,ou=Renamed parent,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Child
+ou: Renamed child
+
+dn: ou=Renamed parent,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Parent
+ou: Renamed parent
+
+# Searching all database (after PASS3)...
+dn: ou=Another parent,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Another parent
+
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: ou=Grandchild,ou=Renamed child,ou=Another parent,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Grandchild
+
+dn: ou=Renamed child,ou=Another parent,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Child
+ou: Renamed child
+
+dn: ou=Renamed parent,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Parent
+ou: Renamed parent
+
diff --git a/tests/data/test-chain1.ldif b/tests/data/test-chain1.ldif
new file mode 100644
index 0000000..76e22f7
--- /dev/null
+++ b/tests/data/test-chain1.ldif
@@ -0,0 +1,374 @@
+#LEAD COMMENT
+dn: dc=example,dc=com
+#EMBEDDED COMMENT
+objectclass: top
+objectclass: organization
+objectclass: domainRelatedObject
+objectclass: dcobject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postaladdress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephonenumber: +1 313 555 1817
+associateddomain: example.com
+
+dn: ou=People,dc=example,dc=com
+objectclass: organizationalUnit
+objectclass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: ou=Groups,dc=example,dc=com
+objectclass: referral
+objectclass: extensibleobject
+ou: Groups
+ref: @URI2@ou=Groups,dc=example,dc=com
+
+dn: ou=Other,dc=example,dc=com
+objectclass: referral
+objectclass: extensibleobject
+ou: Other
+# invalid URI first to test failover capabilities (search only)
+ref: @URI3@ou=Other,dc=example,dc=com
+ref: @URI2@ou=Other,dc=example,dc=com
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: organizationalUnit
+ou: Alumni Association
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectclass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectclass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postaladdress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+userpassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homepostaladdress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homephone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimiletelephonenumber: +1 313 555 2274
+telephonenumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectclass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+userpassword:: Ympvcm4=
+homepostaladdress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postaladdress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homephone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimiletelephonenumber: +1 313 555 2177
+telephonenumber: +1 313 555 0355
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Lemonade
+homepostaladdress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimiletelephonenumber: +1 313 555 3223
+telephonenumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homephone: +1 313 555 0454
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+userpassword:: amFq
+homepostaladdress: 3882 Beverly Rd. $ Anytown, MI 48105
+homephone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimiletelephonenumber: +1 313 555 4332
+telephonenumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectclass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 933 Brooks $ Anytown, MI 48104
+homephone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postaladdress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimiletelephonenumber: +1 313 555 8688
+telephonenumber: +1 313 555 7334
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homephone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimiletelephonenumber: +1 313 555 2311
+telephonenumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homepostaladdress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homephone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimiletelephonenumber: +1 313 555 2756
+telephonenumber: +1 313 555 8232
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postaladdress: ITD $ 535 W. William $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homephone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimiletelephonenumber: +1 313 555 4544
+telephonenumber: +1 313 555 9394
+
+dn: cn=Manager,dc=example,dc=com
+objectclass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userpassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 199 Outer Drive $ Ypsilanti, MI 48198
+homephone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimiletelephonenumber: +1 313 555 7762
+telephonenumber: +1 313 555 4177
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homephone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimiletelephonenumber: +1 313 555 9700
+telephonenumber: +1 313 555 5331
diff --git a/tests/data/test-chain2.ldif b/tests/data/test-chain2.ldif
new file mode 100644
index 0000000..e1fb680
--- /dev/null
+++ b/tests/data/test-chain2.ldif
@@ -0,0 +1,88 @@
+#LEAD COMMENT
+dn: dc=example,dc=com
+#EMBEDDED COMMENT
+objectclass: top
+objectclass: organization
+objectclass: domainRelatedObject
+objectclass: dcobject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postaladdress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephonenumber: +1 313 555 1817
+associateddomain: example.com
+
+dn: ou=People,dc=example,dc=com
+objectClass: referral
+objectclass: extensibleObject
+ou: People
+ref: @URI1@ou=People,dc=example,dc=com
+
+dn: ou=Groups,dc=example,dc=com
+objectclass: organizationalUnit
+ou: Groups
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectclass: groupofnames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectclass: groupofnames
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectclass: groupofuniquenames
+uniquemember: cn=Manager,dc=example,dc=com
+uniquemember: cn=Bjorn Jensen,OU=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniquemember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniquemember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=Manager,dc=example,dc=com
+objectclass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userpassword:: c2VjcmV0
+
+dn: ou=Other,dc=example,dc=com
+objectclass: organizationalUnit
+ou: Other
+
diff --git a/tests/data/test-compmatch.ldif b/tests/data/test-compmatch.ldif
new file mode 100644
index 0000000..d69f9f6
--- /dev/null
+++ b/tests/data/test-compmatch.ldif
@@ -0,0 +1,483 @@
+#LEAD COMMENT
+dn: dc=example,dc=com
+#EMBEDDED COMMENT
+objectclass: top
+objectclass: organization
+objectclass: domainRelatedObject
+objectclass: dcobject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postaladdress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephonenumber: +1 313 555 1817
+associateddomain: example.com
+
+dn: ou=People,dc=example,dc=com
+objectclass: organizationalUnit
+objectclass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: ou=Groups,dc=example,dc=com
+objectclass: organizationalUnit
+ou: Groups
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: organizationalUnit
+ou: Alumni Association
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectclass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectclass: groupofnames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectclass: groupofnames
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectclass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postaladdress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+userpassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homepostaladdress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homephone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimiletelephonenumber: +1 313 555 2274
+telephonenumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectclass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+userpassword:: Ympvcm4=
+homepostaladdress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postaladdress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homephone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimiletelephonenumber: +1 313 555 2177
+telephonenumber: +1 313 555 0355
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Lemonade
+homepostaladdress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimiletelephonenumber: +1 313 555 3223
+telephonenumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homephone: +1 313 555 0454
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectclass: groupofuniquenames
+uniquemember: cn=Manager,dc=example,dc=com
+uniquemember: cn=Bjorn Jensen,OU=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniquemember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniquemember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+userpassword:: amFq
+homepostaladdress: 3882 Beverly Rd. $ Anytown, MI 48105
+homephone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimiletelephonenumber: +1 313 555 4332
+telephonenumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectclass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 933 Brooks $ Anytown, MI 48104
+homephone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postaladdress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimiletelephonenumber: +1 313 555 8688
+telephonenumber: +1 313 555 7334
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homephone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimiletelephonenumber: +1 313 555 2311
+telephonenumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homepostaladdress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homephone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimiletelephonenumber: +1 313 555 2756
+telephonenumber: +1 313 555 8232
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postaladdress: ITD $ 535 W. William $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homephone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimiletelephonenumber: +1 313 555 4544
+telephonenumber: +1 313 555 9394
+
+dn: cn=Manager,dc=example,dc=com
+objectclass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userpassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 199 Outer Drive $ Ypsilanti, MI 48198
+homephone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimiletelephonenumber: +1 313 555 7762
+telephonenumber: +1 313 555 4177
+
+dn: cn=charlie,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+objectclass: extensibleObject
+uid: charlie
+cn: charlie
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDVQQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MONBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnAKUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUAzNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTySuz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5UedAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4XrEkBJ/Q=
+
+dn: cn=beta,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+objectclass: extensibleObject
+uid: charlie
+cn: beta
+sn: Jee
+userCertificate;binary:: MIIB9jCCAV+gAwIBAgIBADANBgkqhkiG9w0BAQQFADANMQswCQYDVQQGEwJVUzAeFw0wNDEwMTIwMDAxNTBaFw0wNDExMTEwMDAxNTBaMA0xCzAJBgNVBAYTAlVTMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCQcTs4uD+gAoQ1XkYN4woLtZaEi7XVEVIJQ6Rsn2QP3MONBT9jvrhVcnUJQtvEEkfnsNANKeYntUTvih76jErFNTmg7zl0govFSkiuS+tfrZnn/Ebix3+tTMnAKUQXkYi5Mr+x3U44yYo1EPLpZlcV1Caafc30EMRQ/Gv/PdrqYwIDAQABo2YwZDAdBgNVHQ4EFgQUAzNnruNiI38IPf39ZJGFx8mDsxgwNQYDVR0jBC4wLIAUAzNnruNiI38IPf39ZJGFx8mDsxihEaQPMA0xCzAJBgNVBAYTAlVTggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAf44laoKcTySuz3yQb/lfOBVDh6oMxysal0eEij+nypQJ1H+rsZ+ebUlKMiTYhrTk3n3H6moHaxICENIu4P5rD5UedAWtMjWq2ZJIa26bbvB4enGOF66KH5S823ZdKa0Kr2JcHAAYFpf+TQoGg5JO7TD3AECd7Qo9a+4XrEkBJ/Q=
+certificateRevocationList;binary:: MIIP0TCCDrkCAQEwDQYJKoZIhvcNAQEFBQAwgZMxCzA
+ JBgNVBAYTAkFVMSswKQYDVQQKEyJDZXJ0aWZpY2F0ZXMgQXVzdHJhbGlhIFB0eSBMaW1pdGVkMSUw
+ IwYDVQQDExxDQVBMIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTAwLgYKCZImiZPyLGQBAxQgY2FAY
+ 2VydGlmaWNhdGVzLWF1c3RyYWxpYS5jb20uYXUXDTAzMDcyMjAxMzAyMFoXDTAzMTEwMzAxMzUyMF
+ owgg27MCMCBDi/biUXDTAwMDMwNjA2MjEzM1owDDAKBgNVHRUEAwoBBDAjAgQ5Il0KFw0wMDA1MjM
+ wODAwNDNaMAwwCgYDVR0VBAMKAQQwIwIEOSo6ZxcNMDAwNTI5MDIyNTQzWjAMMAoGA1UdFQQDCgEE
+ MCMCBDkx1QAXDTAwMDUyOTAzMzYwMVowDDAKBgNVHRUEAwoBBDAjAgQ5Pd7GFw0wMDA2MDcwNTM3M
+ jRaMAwwCgYDVR0VBAMKAQQwIwIEOUcavBcNMDAwNjE0MDc0MjExWjAMMAoGA1UdFQQDCgEEMCMCBD
+ lIlLYXDTAwMDYxNTA4MzY1NlowDDAKBgNVHRUEAwoBBDAjAgQ5SeOkFw0wMDA2MTYwODIzMDVaMAw
+ wCgYDVR0VBAMKAQQwIwIEOUiGjRcNMDAwNjE2MDgyMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ30oX
+ DTAwMDYyOTA4MDQyM1owDDAKBgNVHRUEAwoBBDAjAgQ5SdUjFw0wMDA2MjkwODA1NDVaMAwwCgYDV
+ R0VBAMKAQQwIwIEOTHlfRcNMDAwNjMwMDYwNjA1WjAMMAoGA1UdFQQDCgEEMCMCBDkzV6EXDTAwMD
+ YzMDA2MDYxMVowDDAKBgNVHRUEAwoBBDAjAgQ5SIFOFw0wMDA2MzAwNjA2MjFaMAwwCgYDVR0VBAM
+ KAQQwIwIEOUiCbBcNMDAwNjMwMDYwNjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlIgzkXDTAwMDYzMDA2
+ MDYzNlowDDAKBgNVHRUEAwoBBDAjAgQ5SIQEFw0wMDA2MzAwNjA2NDFaMAwwCgYDVR0VBAMKAQQwI
+ wIEOUiFBBcNMDAwNjMwMDYwNjQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlIhfQXDTAwMDYzMDA2MDY1NV
+ owDDAKBgNVHRUEAwoBBDAjAgQ5SIcmFw0wMDA2MzAwNjA3MDJaMAwwCgYDVR0VBAMKAQQwIwIEOUi
+ H4hcNMDAwNjMwMDYwNzA4WjAMMAoGA1UdFQQDCgEEMCMCBDlIiGUXDTAwMDYzMDA2MDcxNFowDDAK
+ BgNVHRUEAwoBBDAjAgQ5SIjaFw0wMDA2MzAwNjA3NDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiJhRcNM
+ DAwNjMwMDYwNzU3WjAMMAoGA1UdFQQDCgEEMCMCBDlIjoIXDTAwMDYzMDA2MDgwNFowDDAKBgNVHR
+ UEAwoBBDAjAgQ5SI89Fw0wMDA2MzAwNjA4MTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiP1RcNMDAwNjM
+ wMDYwODE1WjAMMAoGA1UdFQQDCgEEMCMCBDlIkEoXDTAwMDYzMDA2MDg0NVowDDAKBgNVHRUEAwoB
+ BDAjAgQ5SJC7Fw0wMDA2MzAwNjA4NTBaMAwwCgYDVR0VBAMKAQQwIwIEOUiReRcNMDAwNjMwMDYwO
+ DU2WjAMMAoGA1UdFQQDCgEEMCMCBDlIkgMXDTAwMDYzMDA2MDkwNFowDDAKBgNVHRUEAwoBBDAjAg
+ Q5SJKqFw0wMDA2MzAwNjA5MDlaMAwwCgYDVR0VBAMKAQQwIwIEOUiTJhcNMDAwNjMwMDYwOTE2WjA
+ MMAoGA1UdFQQDCgEEMCMCBDlIk5AXDTAwMDYzMDA2MDkyMVowDDAKBgNVHRUEAwoBBDAjAgQ5SJQ3
+ Fw0wMDA2MzAwNjA5MjZaMAwwCgYDVR0VBAMKAQQwIwIEOUiVXhcNMDAwNjMwMDYwOTMyWjAMMAoGA
+ 1UdFQQDCgEEMCMCBDlIlgcXDTAwMDYzMDA2MDkzOFowDDAKBgNVHRUEAwoBBDAjAgQ5SJazFw0wMD
+ A2MzAwNjA5NDZaMAwwCgYDVR0VBAMKAQQwIwIEOUiXPxcNMDAwNjMwMDYwOTUxWjAMMAoGA1UdFQQ
+ DCgEEMCMCBDlIl7IXDTAwMDYzMDA2MDk1OFowDDAKBgNVHRUEAwoBBDAjAgQ5SJg0Fw0wMDA2MzAw
+ NjEwMDRaMAwwCgYDVR0VBAMKAQQwIwIEOUiZBBcNMDAwNjMwMDYxMDA5WjAMMAoGA1UdFQQDCgEEM
+ CMCBDlJzksXDTAwMDYzMDA2MTAxNVowDDAKBgNVHRUEAwoBBDAjAgQ5Sc64Fw0wMDA2MzAwNjEwMj
+ FaMAwwCgYDVR0VBAMKAQQwIwIEOUnPVxcNMDAwNjMwMDYxMDI3WjAMMAoGA1UdFQQDCgEEMCMCBDl
+ J0BAXDTAwMDYzMDA2MTAzNVowDDAKBgNVHRUEAwoBBDAjAgQ5SdDKFw0wMDA2MzAwNjEwNDNaMAww
+ CgYDVR0VBAMKAQQwIwIEOUnRZRcNMDAwNjMwMDYxMDQ5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ0d0XD
+ TAwMDYzMDA2MTA1N1owDDAKBgNVHRUEAwoBBDAjAgQ5SdJ4Fw0wMDA2MzAwNjExMTVaMAwwCgYDVR
+ 0VBAMKAQQwIwIEOUnTDBcNMDAwNjMwMDYxMTIxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ04oXDTAwMDY
+ zMDA2MTEyN1owDDAKBgNVHRUEAwoBBDAjAgQ5SdQSFw0wMDA2MzAwNjExMzNaMAwwCgYDVR0VBAMK
+ AQQwIwIEOUnUoBcNMDAwNjMwMDYxMTM5WjAMMAoGA1UdFQQDCgEEMCMCBDlJ2SQXDTAwMDYzMDA2M
+ TE1M1owDDAKBgNVHRUEAwoBBDAjAgQ5SdmwFw0wMDA2MzAwNjEyMDVaMAwwCgYDVR0VBAMKAQQwIw
+ IEOUnaTBcNMDAwNjMwMDYxMjExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ2vYXDTAwMDYzMDA2MTIxN1o
+ wDDAKBgNVHRUEAwoBBDAjAgQ5SducFw0wMDA2MzAwNjEyMjNaMAwwCgYDVR0VBAMKAQQwIwIEOUnc
+ IRcNMDAwNjMwMDYxMjI4WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3KQXDTAwMDYzMDA2MTIzM1owDDAKB
+ gNVHRUEAwoBBDAjAgQ5Sd2xFw0wMDA2MzAwNjEyNDBaMAwwCgYDVR0VBAMKAQQwIwIEOUneRBcNMD
+ AwNjMwMDYxMjQ1WjAMMAoGA1UdFQQDCgEEMCMCBDlJ3skXDTAwMDYzMDA2MTI1MVowDDAKBgNVHRU
+ EAwoBBDAjAgQ5Sd/IFw0wMDA2MzAwNjEzMDJaMAwwCgYDVR0VBAMKAQQwIwIEOUngPRcNMDAwNjMw
+ MDYxMzExWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4M8XDTAwMDYzMDA2MTMyMFowDDAKBgNVHRUEAwoBB
+ DAjAgQ5SeE/Fw0wMDA2MzAwNjEzMjVaMAwwCgYDVR0VBAMKAQQwIwIEOUnh2BcNMDAwNjMwMDYxMz
+ MxWjAMMAoGA1UdFQQDCgEEMCMCBDlJ4mgXDTAwMDYzMDA2MTMzOVowDDAKBgNVHRUEAwoBBDAjAgQ
+ 5SeQvFw0wMDA2MzAwNjEzNDRaMAwwCgYDVR0VBAMKAQQwIwIEOVsGJRcNMDAwNjMwMDYxMzUwWjAM
+ MAoGA1UdFQQDCgEEMCMCBDlbBusXDTAwMDYzMDA2MTM1NlowDDAKBgNVHRUEAwoBBDAjAgQ5XEKPF
+ w0wMDA3MTMwOTAwMzhaMAwwCgYDVR0VBAMKAQQwIwIEOVxEKRcNMDAwNzEzMDkwMDQ1WjAMMAoGA1
+ UdFQQDCgEEMCMCBDlcRukXDTAwMDcyNjA2MjkyN1owDDAKBgNVHRUEAwoBBDAjAgQ5fohgFw0wMDA
+ 3MjYwNjQ2NTFaMAwwCgYDVR0VBAMKAQQwIwIEOaNqPBcNMDAwODIzMDYwOTQxWjAMMAoGA1UdFQQD
+ CgEFMCMCBDlcX2QXDTAwMDgzMTA3MTM1OFowDDAKBgNVHRUEAwoBBDAjAgQ5YsflFw0wMDA5MDEwM
+ TQwMjRaMAwwCgYDVR0VBAMKAQQwIwIEOWGHDRcNMDAwOTA2MDcwMTE2WjAMMAoGA1UdFQQDCgEEMC
+ MCBDliz/4XDTAwMDkwNjA3MDcwNVowDDAKBgNVHRUEAwoBBDAjAgQ5m3S6Fw0wMDA5MjAwNzA2NTd
+ aMAwwCgYDVR0VBAMKAQQwIwIEOy6/hhcNMDEwNzAzMDYxMDQyWjAMMAoGA1UdFQQDCgEEMCMCBDtB
+ Yw4XDTAxMDcwMzA2MTkxNlowDDAKBgNVHRUEAwoBBDAjAgQ7MEG6Fw0wMTA3MTAwODA5NTNaMAwwC
+ gYDVR0VBAMKAQQwIwIEOy68CxcNMDEwNzExMDYxMzI5WjAMMAoGA1UdFQQDCgEEMCMCBDswSOsXDT
+ AxMDgwMTA0MTkyM1owDDAKBgNVHRUEAwoBBTAjAgQ7MYgeFw0wMTA4MDEwNDIwMDJaMAwwCgYDVR0
+ VBAMKAQQwIwIEOzGHeBcNMDEwODAyMDI0NTM4WjAMMAoGA1UdFQQDCgEEMCMCBDsuveEXDTAxMDgz
+ MDA2MjIwOFowDDAKBgNVHRUEAwoBBDAjAgQ7jdxLFw0wMTA4MzAwNjQzMjRaMAwwCgYDVR0VBAMKA
+ QQwIwIEOy67QxcNMDExMTIxMDYyMDUzWjAMMAoGA1UdFQQDCgEEMCMCBDsDNXcXDTAyMDUxNzA4ND
+ Y0MlowDDAKBgNVHRUEAwoBBDAjAgQ7AzXMFw0wMjA1MTcwODQ2NTdaMAwwCgYDVR0VBAMKAQSgMjA
+ wMAsGA1UdFAQEAgIQoDATBgNVHSMEDDAKgAhISAKVrWisNzAMBgNVHRwBAf8EAjAAMA0GCSqGSIb3
+ DQEBBQUAA4IBAQA1xNXgyrtVB5LSOc76JF+aJzf8IfJGqF04CMzbo4lDpec/LgOrTSFV223ccJzuq
+ cnxGUfDbXFfSWDHGnj9HLLTCkrS3clL1TPVjGXg5mFu1l6DCfcP2v4i4dlradNYDQg/AVBoJsYa3l
+ efSFHw8RFXNHJWwIjJA6J0CBJ/8Uq2ywr8umdndb10RLtPWp66A7wxu7OvTjt68d3LgSniQ0mIJCn
+ 4ooE30oF/ew0EznbxlSCNRPpB8jYYJTibGrTUVU43lr8h3URIgBkA4InOhuDv0ePMSCDSxBUhY0+G
+ eKo+YiXHy4SGUGLakahuq/hlGTRJJUddqFA1dNZdOUl23nVE
diff --git a/tests/data/test-deref.ldif b/tests/data/test-deref.ldif
new file mode 100755
index 0000000..82fdb6d
--- /dev/null
+++ b/tests/data/test-deref.ldif
@@ -0,0 +1,43 @@
+# base
+
+dn: o=deref
+objectClass: top
+objectClass: organization
+o: deref
+description: deref test database
+
+# user container
+
+dn: ou=users,o=deref
+objectClass: top
+objectClass: organizationalUnit
+ou: users
+description: container for test deref users
+
+# group container
+
+dn: ou=groups,o=deref
+objectClass: top
+objectClass: organizationalUnit
+ou: users
+description: container for test deref groups
+
+
+dn: cn=Howard Chu,ou=users,o=deref
+objectClass: inetOrgPerson
+cn: Howard Chu
+sn: Chu
+uid: hyc
+
+dn: cn=Pierangelo Masarati,ou=users,o=deref
+objectClass: inetOrgPerson
+cn: Pierangelo Masarati
+sn: Masarati
+uid: ando
+
+dn: cn=Test Group,ou=groups,o=deref
+objectClass: groupOfNames
+cn: Test Group
+member: cn=Howard Chu,ou=users,o=deref
+member: cn=Pierangelo Masarati,ou=users,o=deref
+
diff --git a/tests/data/test-dirsync-cp.ldif b/tests/data/test-dirsync-cp.ldif
new file mode 100644
index 0000000..551f2ae
--- /dev/null
+++ b/tests/data/test-dirsync-cp.ldif
@@ -0,0 +1,12 @@
+#LEAD COMMENT
+dn: ou=OpenLDAPtest,dc=example,dc=com
+ou: OpenLDAPtest
+#EMBEDDED COMMENT
+objectclass: organizationalUnit
+objectclass: domainRelatedObject
+l: Anytown, Michigan
+st: Michigan
+description: The Example, Inc. at Anytown
+postaladdress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephonenumber: +1 313 555 1817
+associatedDomain: test.openldap.org
diff --git a/tests/data/test-dirsync-nocp.ldif b/tests/data/test-dirsync-nocp.ldif
new file mode 100644
index 0000000..757c4ed
--- /dev/null
+++ b/tests/data/test-dirsync-nocp.ldif
@@ -0,0 +1,272 @@
+#LEAD COMMENT
+dn: ou=People,dc=example,dc=com
+#EMBEDDED COMMENT
+objectclass: organizationalUnit
+objectclass: domainRelatedObject
+ou: People
+associatedDomain: test.openldap.org
+
+dn: ou=Groups,dc=example,dc=com
+objectclass: organizationalUnit
+objectclass: domainRelatedObject
+ou: Groups
+associatedDomain: test.openldap.org
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: organizationalUnit
+objectclass: domainRelatedObject
+ou: Alumni Association
+associatedDomain: test.openldap.org
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectclass: organizationalUnit
+objectclass: domainRelatedObject
+ou: Information Technology Division
+associatedDomain: test.openldap.org
+description: MSAD doesn't like long descriptions
+description: 5K and 3K are too big
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectclass: inetOrgPerson
+objectclass: domainRelatedObject
+cn: Barbara Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postaladdress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+userpassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homepostaladdress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+carLicense: water
+homephone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimiletelephonenumber: +1 313 555 2274
+telephonenumber: +1 313 555 9022
+associatedDomain: test.openldap.org
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectclass: inetOrgPerson
+objectclass: domainRelatedObject
+cn: Bjorn Jensen
+sn: Jensen
+uid: bjorn
+userpassword:: Ympvcm4=
+homepostaladdress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+carLicense: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postaladdress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homephone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimiletelephonenumber: +1 313 555 2177
+telephonenumber: +1 313 555 0355
+associatedDomain: test.openldap.org
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: inetOrgPerson
+objectclass: domainRelatedObject
+cn: Dorothy Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+carLicense: Lemonade
+homepostaladdress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimiletelephonenumber: +1 313 555 3223
+telephonenumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homephone: +1 313 555 0454
+associatedDomain: test.openldap.org
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: inetOrgPerson
+objectclass: domainRelatedObject
+cn: James A Jones 1
+sn: Jones
+uid: jaj
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+userpassword:: amFq
+homepostaladdress: 3882 Beverly Rd. $ Anytown, MI 48105
+homephone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimiletelephonenumber: +1 313 555 4332
+telephonenumber: +1 313 555 0895
+associatedDomain: test.openldap.org
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectclass: inetOrgPerson
+objectclass: domainRelatedObject
+cn: James A Jones 2
+sn: Doe
+uid: jjones
+homepostaladdress: 933 Brooks $ Anytown, MI 48104
+homephone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postaladdress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimiletelephonenumber: +1 313 555 8688
+telephonenumber: +1 313 555 7334
+associatedDomain: test.openldap.org
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: inetOrgPerson
+objectclass: domainRelatedObject
+cn: Jane Doe
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+homepostaladdress: 123 Anystreet $ Anytown, MI 48104
+carLicense: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homephone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimiletelephonenumber: +1 313 555 2311
+telephonenumber: +1 313 555 4774
+associatedDomain: test.openldap.org
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: inetOrgPerson
+objectclass: domainRelatedObject
+cn: Jennifer Smith
+sn: Smith
+uid: jen
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+carLicense: Sam Adams
+homepostaladdress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homephone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimiletelephonenumber: +1 313 555 2756
+telephonenumber: +1 313 555 8232
+associatedDomain: test.openldap.org
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectclass: inetOrgPerson
+objectclass: domainRelatedObject
+cn: John Doe
+sn: Doe
+uid: johnd
+postaladdress: ITD $ 535 W. William $ Anytown, MI 48109
+homepostaladdress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homephone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimiletelephonenumber: +1 313 555 4544
+telephonenumber: +1 313 555 9394
+associatedDomain: test.openldap.org
+
+dn: cn=Manager,dc=example,dc=com
+objectclass: inetOrgPerson
+objectclass: domainRelatedObject
+cn: Manager
+sn: Manager
+description: Manager of the directory
+userpassword:: c2VjcmV0
+associatedDomain: test.openldap.org
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: inetOrgPerson
+objectclass: domainRelatedObject
+cn: Mark Elliot
+sn: Elliot
+uid: melliot
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+homepostaladdress: 199 Outer Drive $ Ypsilanti, MI 48198
+homephone: +1 313 555 0388
+carLicense: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimiletelephonenumber: +1 313 555 7762
+telephonenumber: +1 313 555 4177
+associatedDomain: test.openldap.org
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: inetOrgPerson
+objectclass: domainRelatedObject
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+homepostaladdress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homephone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimiletelephonenumber: +1 313 555 9700
+telephonenumber: +1 313 555 5331
+associatedDomain: test.openldap.org
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectclass: groupOfNames
+objectclass: domainRelatedObject
+associatedDomain: test.openldap.org
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectclass: groupOfUniqueNames
+objectclass: domainRelatedObject
+uniquemember: cn=Manager,dc=example,dc=com
+uniquemember: cn=Bjorn Jensen,OU=Information Technology Division,ou=People,dc=example,dc=com
+uniquemember: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example,dc=com
+uniquemember: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+associatedDomain: test.openldap.org
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectclass: groupOfNames
+objectclass: domainRelatedObject
+associatedDomain: test.openldap.org
+
+dn: ou=testdomain1,dc=example,dc=com
+objectclass: organizationalUnit
+objectclass: domainRelatedObject
+ou: testdomain1
+description: Example, Inc. modify+modrdn test domain
+associatedDomain: test.openldap.org
+
+dn: ou=testdomain2,dc=example,dc=com
+objectclass: organizationalUnit
+objectclass: domainRelatedObject
+ou: testdomain2
+description: Example, Inc. modify then modrdn test domain
+associatedDomain: test.openldap.org
diff --git a/tests/data/test-dn.ldif b/tests/data/test-dn.ldif
new file mode 100644
index 0000000..15cc678
--- /dev/null
+++ b/tests/data/test-dn.ldif
@@ -0,0 +1,362 @@
+# Tree Structure
+dn: dc=example,dc=com
+objectClass: domain
+objectClass: domainRelatedObject
+dc: example
+associatedDomain: example.com
+
+dn: ou=LDAPv3,dc=example,dc=com
+objectClass: organizationalUnit
+ou: LDAPv3
+description: RFC 2253 compliant DN string representation
+
+dn: cn=Must Succeed,ou=LDAPv3,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Succeed
+# at least one member must be present; thus we use the entry's DN
+member: cn=Must Succeed,ou=LDAPv3,dc=example,dc=com
+# specific DN forms
+member:
+member: UID=jsmith,DC=example,DC=net
+member: OU=Sales+CN=J. Smith,DC=example,DC=net
+member: CN=John Smith\, III,DC=example,DC=net
+member: OU=Sales\; Data\+Algorithms,DC=example,DC=net
+member: CN=Before\0dAfter,DC=example,DC=net
+member: CN=\23John Smith\20,DC=example,DC=net
+member: CN=Lu\C4\8Di\C4\87
+member: testUUID=597ae2f6-16a6-1027-98f4-abcdefABCDEF,DC=Example
+# DN forms already defined as "member" in a different string representation
+seeAlso: CN=John Smith\2C III,DC=example,DC=net
+seeAlso: OU=Sales\3B Data\2BAlgorithms,DC=example,DC=net
+seeAlso: CN=\#John Smith\ ,DC=example,DC=net
+# comment
+description: "member" values contain specific DN forms;
+description: "seeAlso" values contain DN forms already defined as "member",
+description: but in a different string representation;
+description: the following "description" values contain the "member" and
+description: "seeAlso" DN string representations used above.
+# list here all string representations used above in "member" and "seeAlso"
+description: ""
+description: UID=jsmith,DC=example,DC=net
+description: OU=Sales+CN=J. Smith,DC=example,DC=net
+description: CN=John Smith\, III,DC=example,DC=net
+description: CN=John Smith\2C III,DC=example,DC=net
+description: OU=Sales\; Data\+Algorithms,DC=example,DC=net
+description: OU=Sales\3B Data\2BAlgorithms,DC=example,DC=net
+description: CN=Before\0dAfter,DC=example,DC=net
+description: CN=\23John Smith\20,DC=example,DC=net
+description: CN=\#John Smith\ ,DC=example,DC=net
+description: CN=Lu\C4\8Di\C4\87
+description: testUUID=597ae2f6-16a6-1027-98f4-abcdefABCDEF,DC=Example
+
+dn: cn=Should Succeed,ou=LDAPv3,dc=example,dc=com
+objectClass: groupOfNames
+cn: Should Succeed
+member: cn=Should Succeed,ou=LDAPv3,dc=example,dc=com
+member: 1.3.6.1.4.1.1466.0=#04024869,DC=example,DC=com
+member: 1.1.1=
+description: 1.3.6.1.4.1.1466.0=#04024869,DC=example,DC=com
+description: 1.1.1=
+
+dn: cn=Unescaped Equals,ou=LDAPv3,dc=example,dc=com
+objectClass: groupOfNames
+cn: Unescaped Equals
+member: cn=Unescaped Equals,ou=LDAPv3,dc=example,dc=com
+member: cn=A*x=b is a linear algebra problem,ou=LDAPv3,dc=example,dc=com
+description: cn=A*x=b is a linear algebra problem,ou=LDAPv3,dc=example,dc=com // unescaped EQUALS
+
+dn: cn=Must Fail 1,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Fail 1
+member: uid;x-option=jsmith
+description: uid;x-option=jsmith // option
+
+dn: cn=Must Fail 2,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Fail 2
+member: at_tr=jsmith
+description: at_tr=jsmith // invalid attribute type name
+
+dn: cn=Must Fail 3,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Fail 3
+member: -attr=jsmith
+description: -attr=jsmith // invalid attribute type name
+
+dn: cn=Must Fail 4,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Fail 4
+
+dn: cn=Must Fail 5,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Fail 5
+member: 1..1=jsmith
+description: 1..1=jsmith // invalid numeric OID
+
+dn: cn=Must Fail 6,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Fail 6
+member: 1.1.=jsmith
+description: 1.1.=jsmith // invalid numeric OID
+
+dn: cn=Must Fail 7,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Fail 7
+member: 01.1=jsmith
+description: 01.1=jsmith // invalid numeric OID
+
+dn: cn=Must Fail 8,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Fail 8
+member: 1.ff=jsmith
+description: 1.ff=jsmith // invalid numeric OID
+
+dn: cn=Must Fail 9,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Fail 9
+member: 1.1.1=#GG
+description: 1.1.1=#GG // invalid HEX form
+
+dn: cn=Must Fail 10,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Fail 10
+member: 1.1.1=#000
+description: 1.1.1=#000 // invalid HEX form
+
+dn: cn=Must Fail 11,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Fail 11
+member: 1.1.1=#F
+description: 1.1.1=#F // invalid HEX form
+
+dn: cn=Must Fail 12,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Fail 12
+member: 1.1.1=#
+description: 1.1.1=# // invalid HEX form
+
+dn: cn=Must Fail 13,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Fail 13
+member: UID=jsmith,,DC=example,DC=net
+description: UID=jsmith,,DC=example,DC=net // extra comma
+
+dn: cn=Must Fail 14,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Fail 14
+member: UID=john,smith
+description: UID=john,smith // unescaped ,
+
+dn: cn=Must Fail 15,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Fail 15
+member: UID=john+smith
+description: UID=john+smith // unescaped +
+
+dn: cn=Must Fail 16,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Fail 16
+member: UID=john\?smith
+description: UID=john\?smith // invalid escape of ? or unescaped \
+
+dn: cn=Must Fail 17,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Fail 17
+member: UID=john\Fsmith
+description: UID=john\Fsmith // invalid HEX escape
+
+dn: cn=Must Fail 18,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Must Fail 18
+member: UID=john\GGsmith
+description: UID=john\GGsmith // invalid HEX escape
+
+# String representations we should accept for compatibility with RFC1779
+dn: ou=LDAPv2,dc=example,dc=com
+objectClass: organizationalUnit
+ou: LDAPv2
+description: RFC 1779 compliant DN string representation
+
+dn: cn=May Succeed 1,ou=LDAPv2,dc=example,dc=com
+objectClass: groupOfNames
+cn: May Succeed 1
+member:
+description: " " // space, quote characters (") are not part of the string
+
+dn: cn=May Succeed 2,ou=LDAPv2,dc=example,dc=com
+objectClass: groupOfNames
+cn: May Succeed 2
+member: OID.0.9.2342.19200300.100.1.1=jsmith
+description: OID.0.9.2342.19200300.100.1.1=jsmith // invalid attribute type name
+
+dn: cn=May Succeed 3,ou=LDAPv2,dc=example,dc=com
+objectClass: groupOfNames
+cn: May Succeed 3
+member: UID=jsmith, O=example, C=US
+description: UID=jsmith, O=example, C=US // spaces
+
+dn: cn=May Succeed 4,ou=LDAPv2,dc=example,dc=com
+objectClass: groupOfNames
+cn: May Succeed 4
+member: UID=jsmith;O=example;C=US
+description: UID=jsmith;O=example;C=US // semi-colons
+
+dn: cn=May Succeed 5,ou=LDAPv2,dc=example,dc=com
+objectClass: groupOfNames
+cn: May Succeed 5
+member: <UID=jsmith,O=example,C=US>
+description: <UID=jsmith,O=example,C=US> // brackets
+
+dn: cn=May Succeed 6,ou=LDAPv2,dc=example,dc=com
+objectClass: groupOfNames
+cn: May Succeed 6
+member: CN="John Smith",O=example,C=US
+description: CN="John Smith",O=example,C=US // quotes
+
+# Other DN-related syntaxes
+dn: ou=Related Syntaxes,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Related Syntaxes
+
+# Name and Optional UID
+dn: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
+objectClass: groupOfUniqueNames
+cn: Name and Optional UID
+uniqueMember: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
+uniqueMember: #'1'B
+uniqueMember: #'0010'B
+uniqueMember: dc=example,dc=com#'1000'B
+uniqueMember: dc=example,dc=com#''B
+description: cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com // only DN portion
+description: #'1'B // empty "" DN
+description: #'0010'B // empty "" DN with leading '0's
+description: dc=example,dc=com#'1000'B // with DN portion
+description: dc=example,dc=com#''B // with DN portion + bitstring with no bits
+
+dn: cn=Should Fail 1,cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
+objectClass: groupOfUniqueNames
+cn: Should Fail 1
+uniqueMember: #'1234'B
+description: #'1234'B // illegal digits other than '0' and '1'
+
+dn: cn=Should Fail 2,cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
+objectClass: groupOfUniqueNames
+cn: Should Fail 2
+uniqueMember: #'12ABCD'B
+description: #'12ABCD'B // illegal digits and chars other than '0' and '1'
+
+dn: cn=Should Parse as DN,cn=Name and Optional UID,ou=Related Syntaxes,dc=example,dc=com
+objectClass: groupOfUniqueNames
+cn: Should Parse as DN
+uniqueMember: dc=example,dc=com#0'B
+uniqueMember: dc=example,dc=com#'0B
+uniqueMember: dc=example,dc=com '0'B
+description: dc=example,dc=com#0'B // malformed UID?
+description: dc=example,dc=com#'0B // malformed UID?
+description: dc=example,dc=com '0'B // malformed UID?
+
+# UID=jsmith,DC=example,DC=net [AoOn]
+# 304631133011060A0992268993F22C64011916036E657431 [AoO]
+# 173015060A0992268993F22C64011916076578616D706C65
+# 31163014060A0992268993F22C64010113066A736D697468
+#
+# OU=Sales+CN=J. Smith,DC=example,DC=net [AoOn]
+# 304F31133011060A0992268993F22C64011916036E657431 [AoO]
+# 173015060A0992268993F22C64011916076578616D706C65
+# 311F300C060355040B130553616C6573300F060355040313
+# 084A2E20536D697468
+#
+# CN=John Smith\, III,DC=example,DC=net [AoOn]
+# 304831133011060A0992268993F22C64011916036E657431 [AoO]
+# 173015060A0992268993F22C64011916076578616D706C65
+# 311830160603550403130F4A6F686E20536D6974682C2049
+# 4949
+#
+# CN=John Smith\2C III,DC=example,DC=net [AoOn]
+# 304831133011060A0992268993F22C64011916036E657431 [AoO]
+# 173015060A0992268993F22C64011916076578616D706C65
+# 311830160603550403130F4A6F686E20536D6974682C2049
+# 4949
+#
+# CN=Before\0dAfter,DC=example,DC=net [AoOn]
+# 304531133011060A0992268993F22C64011916036E657431 [AoO]
+# 173015060A0992268993F22C64011916076578616D706C65
+# 3115301306035504030C0C4265666F72650D4166746572
+#
+# CN=\23John Smith\20,DC=example,DC=net [AoOn]
+# 304531133011060A0992268993F22C64011916036E657431 [AoO]
+# 173015060A0992268993F22C64011916076578616D706C65
+# 311530130603550403140C234A6F686E20536D69746820
+#
+# CN=\#John Smith\ ,DC=example,DC=net [AoOn]
+# 304531133011060A0992268993F22C64011916036E657431 [AoO]
+# 173015060A0992268993F22C64011916076578616D706C65
+# 311530130603550403140C234A6F686E20536D69746820
+#
+# FIXME: currently doesn't work
+# 1.3.6.1.4.1.1466.0=#04024869,DC=example,DC=com [AoOn]
+# 304031133011060A0992268993F22C64011916036E657431 [AoO]
+# 173015060A0992268993F22C64011916076578616D706C65
+# 3110300E06082B060104018B3A0004024869
+#
+# CN=Lu\C4\8Di\C4\87 [AoOn]
+# 30123110300E06035504030C074C75C48D69C487 [AoO]
+#
+# FIXME: currently doesn't work
+# 1.1.1= // empty value [AoO]
+# 300A31083006060229011300 [AoO]
+#
+#Invalid DNs
+# // some implementations may be liberal in what they accept
+# // but should strict in what they produce.
+#
+# uid;x-option=jsmith // option [oOn]
+#
+# at_tr=jsmith // invalid attribute type name [AoOn]
+#
+# -attr=jsmith // invalid attribute type name [AoOn]
+#
+# 1..1=jsmith // invalid numeric OID [AoO]
+#
+# 1.1.=jsmith // invalid numeric OID [AoO]
+#
+# 01.1=jsmith // invalid numeric OID [oO]
+#
+# 1.ff=jsmith // invalid numeric OID [AoOn]
+#
+# 1.1.1=#GG // invalid HEX form [AoOn]
+#
+# 1.1.1=#000 // invalid HEX form [AoO]
+#
+# 1.1.1=#F // invalid HEX form [AoO]
+#
+# 1.1.1=# // invalid HEX form [AoO]
+#
+# UID=jsmith,,DC=example,DC=net // extra comma [AoOn]
+#
+# UID=john,smith // unescaped , [AoOn]
+#
+# UID=john+smith // unescaped + [AoOn]
+#
+# UID=john\?smith // invalid escape of ? or unescaped \ [oOn]
+#
+# UID=john\Fsmith // invalid hex escape [AoOn]
+#
+# UID=john\GGsmith // invalid hex escape [oOn]
+#
+#The following strings are invalid for use in LDAPv3, but were
+#legal in LDAPv2 (RFC 1779). Some LDAPv3 implementations are
+#liberal in accepting these but should not generate them.
+#
+# " " // space, quote characters (") are not part of the string
+#
+# OID.1.1=jsmith // invalid attribute type name
+#
+# UID=jsmith, O=example, C=US // spaces
+#
+# UID=jsmith;O=example;C=US // semi-colons
+#
+# <UID=jsmith,O=example,C=US> // brackets [AoOn]
+#
+# CN="John Smith",O=example,C=US // quotes
+
diff --git a/tests/data/test-emptydn1.ldif b/tests/data/test-emptydn1.ldif
new file mode 100644
index 0000000..166dca5
--- /dev/null
+++ b/tests/data/test-emptydn1.ldif
@@ -0,0 +1,14 @@
+# dc=example,dc=com naming context
+dn: dc=example,dc=com
+objectClass: domain
+objectClass: domainRelatedObject
+dc: example
+associatedDomain: example.com
+
+dn: cn=Geographical Naming Contexts,dc=example,dc=com
+objectClass: groupOfNames
+cn: Geographical Naming Contexts
+member: o=Example,c=US
+member: o=Example,c=UK
+member: o=Esempio,c=IT
+
diff --git a/tests/data/test-emptydn2.ldif b/tests/data/test-emptydn2.ldif
new file mode 100644
index 0000000..64c4aaf
--- /dev/null
+++ b/tests/data/test-emptydn2.ldif
@@ -0,0 +1,39 @@
+# geographical naming contexts
+dn: c=US
+objectClass: country
+c: US
+
+dn: o=Example,c=US
+objectClass: organization
+o: Example
+o: Example, Inc.
+
+dn: c=UK
+objectClass: country
+c: UK
+
+dn: o=Example,c=UK
+objectClass: organization
+o: Example
+o: Example, Ltd.
+
+dn: c=IT
+objectClass: country
+c: IT
+
+dn: o=Esempio,c=IT
+objectClass: organization
+o: Esempio
+o: Esempio S.p.A.
+o: Example
+
+dn: c=DE
+objectClass: country
+c: DE
+
+dn: o=Beispiel,c=DE
+objectClass: organization
+o: Beispiel
+o: Beispiel GmbH
+o: Example
+
diff --git a/tests/data/test-glued.ldif b/tests/data/test-glued.ldif
new file mode 100644
index 0000000..eabb0c5
--- /dev/null
+++ b/tests/data/test-glued.ldif
@@ -0,0 +1,410 @@
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+
+dn: cn=Manager,dc=example,dc=com
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
diff --git a/tests/data/test-idassert1.ldif b/tests/data/test-idassert1.ldif
new file mode 100644
index 0000000..3ccbd1a
--- /dev/null
+++ b/tests/data/test-idassert1.ldif
@@ -0,0 +1,73 @@
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: cn=Manager,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Manager
+sn: Parson
+userPassword: secret
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: People
+
+dn: uid=bjorn,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Bjorn Jensen
+sn: Jensen
+uid: bjorn
+userPassword:: Ympvcm4=
+mail: bjorn@example.com
+description: ***
+authzFrom: dn.exact:uid=jaj,o=Example,c=US
+authzFrom: dn.subtree:ou=People,dc=example,dc=it
+
+dn: uid=bjensen,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Barbara Jensen
+sn: Jensen
+uid: bjensen
+userPassword:: YmplbnNlbg==
+mail: bjensen@example.com
+description: ***
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: cn=All,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: All
+member: uid=bjorn,ou=People,dc=example,dc=com
+member: uid=bjensen,ou=People,dc=example,dc=com
+
+dn: cn=Authorizable,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: Authorizable
+member: uid=bjorn,ou=People,dc=example,dc=com
+
+dn: ou=Admin,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Admin
+
+dn: cn=Proxy US,ou=Admin,dc=example,dc=com
+objectClass: applicationProcess
+objectClass: simpleSecurityObject
+cn: Proxy US
+userPassword:: cHJveHk=
+authzTo: dn.subtree:ou=People,dc=example,dc=it
+
+dn: cn=Proxy IT,ou=Admin,dc=example,dc=com
+objectClass: applicationProcess
+objectClass: simpleSecurityObject
+cn: Proxy IT
+userPassword:: cHJveHk=
+authzTo: dn.exact:cn=Sandbox,ou=Admin,dc=example,dc=com
+authzTo: dn.exact:
+
+dn: cn=Sandbox,ou=Admin,dc=example,dc=com
+objectClass: applicationProcess
+cn: Sandbox
diff --git a/tests/data/test-idassert2.ldif b/tests/data/test-idassert2.ldif
new file mode 100644
index 0000000..c8f3d68
--- /dev/null
+++ b/tests/data/test-idassert2.ldif
@@ -0,0 +1,27 @@
+dn: dc=example,dc=it
+objectClass: organization
+objectClass: dcObject
+o: Example
+o: Esempio S.p.A.
+dc: example
+
+dn: ou=People,dc=example,dc=it
+objectClass: organizationalUnit
+ou: People
+
+dn: uid=dots,ou=People,dc=example,dc=it
+objectClass: inetOrgPerson
+cn: Dorothy Stevens
+sn: Stevens
+uid: dots
+userPassword:: ZG90cw==
+mail: dots@example.it
+
+dn: uid=jaj,ou=People,dc=example,dc=it
+objectClass: inetOrgPerson
+cn: James A Jones 1
+sn: Jones
+uid: jaj
+userPassword:: amFq
+mail: jaj@example.it
+
diff --git a/tests/data/test-lang.ldif b/tests/data/test-lang.ldif
new file mode 100644
index 0000000..5b3b3b5
--- /dev/null
+++ b/tests/data/test-lang.ldif
@@ -0,0 +1,12 @@
+dn: dc=example,dc=com
+dc: example
+objectClass: organization
+objectClass: extensibleObject
+o: Example, Inc.
+o;lang-zz;lang-y;lang-yy;lang-xx;lang-x;lang-z: Example, Inc.
+name;lang-en-US: Billy Ray
+name;lang-en-US: Billy Bob
+CN;lang-en-US: Billy Ray
+name: Billy Ray
+SN;lang-en-US;lang-en-GB: Billy Ray
+SN: Ray
diff --git a/tests/data/test-ldapglue.ldif b/tests/data/test-ldapglue.ldif
new file mode 100644
index 0000000..d3795cb
--- /dev/null
+++ b/tests/data/test-ldapglue.ldif
@@ -0,0 +1,5 @@
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
diff --git a/tests/data/test-ldapgluegroups.ldif b/tests/data/test-ldapgluegroups.ldif
new file mode 100644
index 0000000..3d8254e
--- /dev/null
+++ b/tests/data/test-ldapgluegroups.ldif
@@ -0,0 +1,23 @@
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: cn=All,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: All
+member: uid=bjorn,ou=People,dc=example,dc=com
+member: uid=bjensen,ou=People,dc=example,dc=com
+
+dn: cn=ITD,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+cn: ITD
+member: uid=bjorn,ou=People,dc=example,dc=com
+
+dn: uid=proxy,ou=Groups,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Proxy
+sn: Proxy
+uid: proxy
+userPassword:: cHJveHk=
+authzTo: dn:*
+
diff --git a/tests/data/test-ldapgluepeople.ldif b/tests/data/test-ldapgluepeople.ldif
new file mode 100644
index 0000000..a8d3547
--- /dev/null
+++ b/tests/data/test-ldapgluepeople.ldif
@@ -0,0 +1,28 @@
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: People
+
+dn: uid=bjorn,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Bjorn Jensen
+sn: Jensen
+uid: bjorn
+userPassword:: Ympvcm4=
+mail: bjorn@example.com
+
+dn: uid=bjensen,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Barbara Jensen
+sn: Jensen
+uid: bjensen
+userPassword:: YmplbnNlbg==
+mail: bjensen@example.com
+
+dn: uid=proxy,ou=People,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Proxy
+sn: Proxy
+uid: proxy
+userPassword:: cHJveHk=
+authzTo: dn:*
+
diff --git a/tests/data/test-limits.ldif b/tests/data/test-limits.ldif
new file mode 100644
index 0000000..cd4aaec
--- /dev/null
+++ b/tests/data/test-limits.ldif
@@ -0,0 +1,137 @@
+#LEAD COMMENT
+dn: dc=example,dc=com
+#EMBEDDED COMMENT
+objectclass: top
+objectclass: organization
+objectclass: domainRelatedObject
+objectclass: dcobject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+description: Some example company at Anytown in Michigan
+postaladdress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephonenumber: +1 313 555 1817
+associateddomain: example.com
+
+dn: ou=People,dc=example,dc=com
+objectclass: organizationalUnit
+ou: People
+
+dn: cn=Unlimited User,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Unlimited User
+sn: User
+uid: unlimited
+userpassword:: c2VjcmV0
+
+dn: cn=Soft Limited User,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Soft Limited User
+sn: User
+uid: softlimited
+userpassword:: c2VjcmV0
+
+dn: cn=Hard Limited User,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Hard Limited User
+sn: User
+uid: hardlimited
+userpassword:: c2VjcmV0
+
+dn: cn=Unchecked Limited User,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Unchecked Limited User
+sn: User
+uid: uncheckedlimited
+userpassword:: c2VjcmV0
+
+dn: cn=Other User,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Other User
+sn: User
+uid: other
+userpassword:: c2VjcmV0
+
+dn: cn=Foo User,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Foo User
+sn: User
+uid: foo
+userpassword:: c2VjcmV0
+
+dn: cn=Bar User,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Bar User
+sn: User
+uid: bar
+userpassword:: c2VjcmV0
+
+dn: cn=Unchecked Limited User 2,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Unchecked Limited User 2
+sn: User 2
+uid: uncheckedlimited2
+userpassword:: c2VjcmV0
+
+dn: ou=Groups,dc=example,dc=com
+objectclass: organizationalUnit
+ou: Groups
+
+dn: cn=Unchecked Limited Users,ou=Groups,dc=example,dc=com
+objectClass: groupOfNames
+objectClass: simpleSecurityObject
+cn: Unchecked Limited Users
+userpassword:: c2VjcmV0
+member: cn=Unchecked Limited User 2,ou=People,dc=example,dc=com
+
+dn: ou=Admin,dc=example,dc=com
+objectclass: organizationalUnit
+ou: Admin
+
+dn: cn=Unchecked Limited User 3,ou=Admin,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Unchecked Limited User 3
+sn: User 3
+uid: uncheckedlimited3
+userpassword:: c2VjcmV0
+
+dn: cn=Special User,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Special User
+sn: User
+uid: special
+userpassword:: c2VjcmV0
+
+dn: ou=Paged Results Users,dc=example,dc=com
+objectclass: organizationalUnit
+ou: Paged Results Users
+
+dn: cn=Unlimited User,ou=Paged Results Users,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Unlimited User
+sn: User
+uid: unlimited
+userpassword:: c2VjcmV0
+
+dn: cn=Page Size Limited User,ou=Paged Results Users,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Page Size Limited User
+sn: User
+uid: pagesizelimited
+userpassword:: c2VjcmV0
+
+dn: cn=Paged Results Disabled User,ou=Paged Results Users,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Paged Results Disabled User
+sn: User
+uid: pagedresultsdisabled
+userpassword:: c2VjcmV0
+
+dn: cn=Paged Results Limited User,ou=Paged Results Users,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Paged Results Limited User
+sn: User
+uid: pagedresultslimited
+userpassword:: c2VjcmV0
+
diff --git a/tests/data/test-meta.ldif b/tests/data/test-meta.ldif
new file mode 100644
index 0000000..ba9e9e8
--- /dev/null
+++ b/tests/data/test-meta.ldif
@@ -0,0 +1,25 @@
+dn: ou=Meta,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Meta
+seeAlso: dc=OpenLDAP,dc=org
+
+dn: cn=John Belushi,ou=Meta,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: John Belushi
+sn: Belushi
+userPassword: jack
+description: Joliet Jack Blues
+
+dn: cn=Dan Aykroyd,ou=Meta,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Dan Aykroyd
+sn: Aykroyd
+userPassword: elwood
+description: Elwood Blues
+
+dn: cn=Somewhere,ou=Meta,dc=example,dc=com
+objectClass: referral
+objectClass: extensibleObject
+cn: Somewhere
+ref: ldap://localhost:9016
+
diff --git a/tests/data/test-modify.ldif b/tests/data/test-modify.ldif
new file mode 100644
index 0000000..2bd98be
--- /dev/null
+++ b/tests/data/test-modify.ldif
@@ -0,0 +1,110 @@
+version: 1
+
+# LEADING COMMENT AND WHITE SPACE
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+# EMBEDDED COMMENT
+changetype: modify
+add: drink
+drink: Pils
+-
+add: drink
+drink: Orange Juice
+-
+delete: drink
+drink: Pils
+-
+delete: sn
+sn: Jones
+-
+add: sn
+sn: Jones
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+changetype: modify
+# EMBEDDED COMMENT
+ CONTINUED
+replace: description
+description: The replaced multiLineDescription $ Blah Woof.
+-
+replace: drink
+drink: Iced Tea
+drink: Mad Dog 20/20
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: uniquemember
+uniquemember: cn=James A Jones 2,ou=Information Technology Division,
+ ou=People,dc=example,dc=com
+uniquemember: cn=Bjorn Jensen,ou=Information Technology Division,
+ ou=People,dc=example,dc=com
+-
+add: uniquemember
+uniquemember: cn=Dorothy Stevens,ou=Alumni Association,
+ ou=People,dc=example,dc=com
+uniquemember: cn=James A Jones 1,ou=Alumni Association,
+ ou=People,dc=example,dc=com
+-
+add: objectClass
+objectClass: OpenLDAPdisplayableObject
+objectClass: pkiUser
+objectClass: userSecurityInformation
+-
+delete: objectClass
+objectClass: userSecurityInformation
+objectClass: pkiUser
+objectClass: OpenLDAPdisplayableObject
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: member
+-
+add: member
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+-
+delete: description
+-
+add: objectClass
+objectClass: OpenLDAPdisplayableObject
+objectClass: pkiUser
+objectClass: userSecurityInformation
+-
+delete: objectClass
+objectClass: OpenLDAPdisplayableObject
+objectClass: pkiUser
+objectClass: userSecurityInformation
+
+dn: cn=Gern Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+changetype: add
+objectclass: testPerson
+cn: Gern Jensen
+sn: Jensen
+uid: gjensen
+title: Chief Investigator, ITD
+postaladdress: ITD $ 535 W. William St $ Anytown, MI 48103
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Coffee
+homepostaladdress: 844 Brown St. Apt. 4 $ Anytown, MI 48104
+description: Very odd
+facsimiletelephonenumber: +1 313 555 7557
+telephonenumber: +1 313 555 8343
+mail: gjensen@mailgw.example.com
+homephone: +1 313 555 8844
+testTime: 20050304001801.234Z
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example,dc=com
+changetype: delete
+# TRAILING COMMENT AND WHITE SPACE
+
+dn: ou=People,dc=example,dc=com
+changetype: modify
+increment: uidNumber
+uidNumber: 1
+-
+increment: gidNumber
+gidNumber: -1
+
+dn: dc=example,dc=com
+changetype: modify
+# EMPTY SEQUENCE OF CHANGE
+
diff --git a/tests/data/test-ordered-cp.ldif b/tests/data/test-ordered-cp.ldif
new file mode 100644
index 0000000..5561174
--- /dev/null
+++ b/tests/data/test-ordered-cp.ldif
@@ -0,0 +1,16 @@
+#LEAD COMMENT
+dn: dc=example,dc=com
+dc: example
+#EMBEDDED COMMENT
+objectclass: organization
+objectclass: domainRelatedObject
+objectclass: dcobject
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postaladdress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephonenumber: +1 313 555 1817
+associateddomain: example.com
diff --git a/tests/data/test-ordered-nocp.ldif b/tests/data/test-ordered-nocp.ldif
new file mode 100644
index 0000000..039d941
--- /dev/null
+++ b/tests/data/test-ordered-nocp.ldif
@@ -0,0 +1,402 @@
+#LEAD COMMENT
+dn: ou=People,dc=example,dc=com
+#EMBEDDED COMMENT
+objectclass: organizationalUnit
+ou: People
+
+dn: ou=Groups,dc=example,dc=com
+objectclass: organizationalUnit
+ou: Groups
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: organizationalUnit
+ou: Alumni Association
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectclass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectclass: groupofnames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectclass: groupofnames
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectclass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postaladdress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+userpassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homepostaladdress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homephone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimiletelephonenumber: +1 313 555 2274
+telephonenumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectclass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+userpassword:: Ympvcm4=
+homepostaladdress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postaladdress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homephone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimiletelephonenumber: +1 313 555 2177
+telephonenumber: +1 313 555 0355
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Lemonade
+homepostaladdress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimiletelephonenumber: +1 313 555 3223
+telephonenumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homephone: +1 313 555 0454
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectclass: groupofuniquenames
+uniquemember: cn=Manager,dc=example,dc=com
+uniquemember: cn=Bjorn Jensen,OU=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniquemember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniquemember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+userpassword:: amFq
+homepostaladdress: 3882 Beverly Rd. $ Anytown, MI 48105
+homephone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimiletelephonenumber: +1 313 555 4332
+telephonenumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectclass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 933 Brooks $ Anytown, MI 48104
+homephone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postaladdress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimiletelephonenumber: +1 313 555 8688
+telephonenumber: +1 313 555 7334
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homephone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimiletelephonenumber: +1 313 555 2311
+telephonenumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homepostaladdress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homephone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimiletelephonenumber: +1 313 555 2756
+telephonenumber: +1 313 555 8232
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postaladdress: ITD $ 535 W. William $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homephone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimiletelephonenumber: +1 313 555 4544
+telephonenumber: +1 313 555 9394
+
+dn: cn=Manager,dc=example,dc=com
+objectclass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userpassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 199 Outer Drive $ Ypsilanti, MI 48198
+homephone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimiletelephonenumber: +1 313 555 7762
+telephonenumber: +1 313 555 4177
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homephone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimiletelephonenumber: +1 313 555 9700
+telephonenumber: +1 313 555 5331
+
+dn: dc=testdomain1,dc=example,dc=com
+objectclass: domain
+dc: testdomain1
+description: Example, Inc. modify+modrdn test domain
+
+dn: dc=testdomain2,dc=example,dc=com
+objectclass: domain
+dc: testdomain2
+description: Example, Inc. modify then modrdn test domain
diff --git a/tests/data/test-ordered.ldif b/tests/data/test-ordered.ldif
new file mode 100644
index 0000000..733cf2b
--- /dev/null
+++ b/tests/data/test-ordered.ldif
@@ -0,0 +1,411 @@
+#LEAD COMMENT
+dn: dc=example,dc=com
+#EMBEDDED COMMENT
+objectclass: top
+objectclass: organization
+objectclass: domainRelatedObject
+objectclass: dcobject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postaladdress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephonenumber: +1 313 555 1817
+associateddomain: example.com
+
+dn: ou=People,dc=example,dc=com
+objectclass: organizationalUnit
+objectclass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: ou=Groups,dc=example,dc=com
+objectclass: organizationalUnit
+ou: Groups
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: organizationalUnit
+ou: Alumni Association
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectclass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectclass: groupofnames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectclass: groupofnames
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectclass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postaladdress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+userpassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homepostaladdress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homephone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimiletelephonenumber: +1 313 555 2274
+telephonenumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectclass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+userpassword:: Ympvcm4=
+homepostaladdress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postaladdress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homephone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimiletelephonenumber: +1 313 555 2177
+telephonenumber: +1 313 555 0355
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Lemonade
+homepostaladdress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimiletelephonenumber: +1 313 555 3223
+telephonenumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homephone: +1 313 555 0454
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectclass: groupofuniquenames
+uniquemember: cn=Manager,dc=example,dc=com
+uniquemember: cn=Bjorn Jensen,OU=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniquemember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniquemember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+userpassword:: amFq
+homepostaladdress: 3882 Beverly Rd. $ Anytown, MI 48105
+homephone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimiletelephonenumber: +1 313 555 4332
+telephonenumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectclass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 933 Brooks $ Anytown, MI 48104
+homephone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postaladdress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimiletelephonenumber: +1 313 555 8688
+telephonenumber: +1 313 555 7334
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homephone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimiletelephonenumber: +1 313 555 2311
+telephonenumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homepostaladdress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homephone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimiletelephonenumber: +1 313 555 2756
+telephonenumber: +1 313 555 8232
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postaladdress: ITD $ 535 W. William $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homephone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimiletelephonenumber: +1 313 555 4544
+telephonenumber: +1 313 555 9394
+
+dn: cn=Manager,dc=example,dc=com
+objectclass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userpassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 199 Outer Drive $ Ypsilanti, MI 48198
+homephone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimiletelephonenumber: +1 313 555 7762
+telephonenumber: +1 313 555 4177
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homephone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimiletelephonenumber: +1 313 555 9700
+telephonenumber: +1 313 555 5331
diff --git a/tests/data/test-refint.ldif b/tests/data/test-refint.ldif
new file mode 100755
index 0000000..df35bef
--- /dev/null
+++ b/tests/data/test-refint.ldif
@@ -0,0 +1,115 @@
+# base
+
+dn: o=refint
+objectClass: top
+objectClass: organization
+o: refint
+description: referential integrity test database
+
+# container
+
+dn: ou=users,o=refint
+objectClass: top
+objectClass: organizationalUnit
+ou: users
+description: container for test users
+
+# secretary
+dn: uid=alice,ou=users,o=refint
+objectClass: inetOrgPerson
+uid: alice
+sn: typist
+cn: alice
+businessCategory: test
+carLicense: ZOOM
+departmentNumber: 5151
+displayName: George
+employeeNumber: 6363
+employeeType: contractor
+givenName: Alice the Typist
+
+# manager
+dn: uid=george,ou=users,o=refint
+objectClass: inetOrgPerson
+uid: george
+sn: jungle
+cn: george
+businessCategory: test
+carLicense: SAMPLE
+departmentNumber: 6969
+displayName: George
+employeeNumber: 5150
+employeeType: contractor
+givenName: Big G
+
+dn: uid=dave,ou=users,o=refint
+objectClass: inetOrgPerson
+uid: dave
+sn: nothere
+cn: dave
+secretary: uid=george,ou=users,o=refint
+businessCategory: otest
+carLicense: ALGAE
+departmentNumber: 42
+displayName: Dave
+employeeNumber: 73
+employeeType: contractor
+givenName: Dave
+
+dn: uid=bob,ou=users,o=refint
+objectClass: inetOrgPerson
+uid: bob
+sn: bitchen
+cn: bob
+manager: uid=george,ou=users,o=refint
+businessCategory: rtest
+carLicense: SL49152
+departmentNumber: 42
+displayName: Bob
+employeeNumber: 38
+employeeType: contractor
+givenName: Bob
+
+dn: uid=bill,ou=users,o=refint
+objectClass: inetOrgPerson
+uid: bill
+sn: problem
+cn: bill
+businessCategory: otest
+manager: uid=george,ou=users,o=refint
+secretary: uid=alice,ou=users,o=refint
+carLicense: DRV818
+departmentNumber: 42
+displayName: Bill
+employeeNumber: 69
+employeeType: contractor
+givenName: Bill
+
+dn: uid=jorge,ou=users,o=refint
+objectClass: inetOrgPerson
+uid: jorge
+sn: burrito
+cn: jorge
+manager: uid=theman,ou=users,o=refint
+secretary: uid=alice,ou=users,o=refint
+businessCategory: rtest
+carLicense: CLA511
+departmentNumber: 42
+displayName: Jorge
+employeeNumber: 93
+employeeType: contractor
+givenName: Jorge
+
+dn: uid=richard,ou=users,o=refint
+objectClass: inetOrgPerson
+uid: richard
+sn: cranium
+cn: richard
+manager: uid=theman,ou=users,o=refint
+businessCategory: rtest
+carLicense: DHD722
+departmentNumber: 42
+displayName: Richard
+employeeNumber: 114
+employeeType: contractor
+givenName: Richard
diff --git a/tests/data/test-reordered.ldif b/tests/data/test-reordered.ldif
new file mode 100644
index 0000000..76e9177
--- /dev/null
+++ b/tests/data/test-reordered.ldif
@@ -0,0 +1,55 @@
+dn: ou=test,dc=example,dc=com
+objectClass: organizationalUnit
+ou: test
+
+dn: ou=Policies,ou=test,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Policies
+
+dn: ou=slapo-ppolicy,ou=Policies,ou=test,dc=example,dc=com
+objectClass: organizationalUnit
+ou: slapo-ppolicy
+ou: Password Policies
+
+dn: ou=Users,ou=test,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Users
+
+dn: uid=michael,ou=Users,ou=test,dc=example,dc=com
+uid: michael
+objectClass: account
+objectClass: simpleSecurityObject
+userPassword:: dGVzdHNlY3JldA==
+
+dn: ou=Systemkonten,ou=test,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Systemkonten
+
+dn: cn=slapd-1,ou=Systemkonten,ou=test,dc=example,dc=com
+cn: slapd-1
+objectClass: applicationProcess
+objectClass: simpleSecurityObject
+userPassword:: cHdfc2xhcGQx
+
+dn: cn=slapd-2,ou=Systemkonten,ou=test,dc=example,dc=com
+cn: slapd-2
+objectClass: applicationProcess
+objectClass: simpleSecurityObject
+userPassword:: cHdfc2xhcGQy
+
+dn: ou=Groups,ou=test,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: cn=Admins,ou=Groups,ou=test,dc=example,dc=com
+cn: Admins
+cn: Password Admins
+objectClass: groupOfNames
+member: uid=michael,ou=Users,ou=test,dc=example,dc=com
+
+dn: cn=Replicas,ou=Groups,ou=test,dc=example,dc=com
+member: cn=slapd-1,ou=Systemkonten,ou=test,dc=example,dc=com
+member: cn=slapd-2,ou=Systemkonten,ou=test,dc=example,dc=com
+cn: Replicas
+objectClass: groupOfNames
+
diff --git a/tests/data/test-translucent-add.ldif b/tests/data/test-translucent-add.ldif
new file mode 100644
index 0000000..fd6acc7
--- /dev/null
+++ b/tests/data/test-translucent-add.ldif
@@ -0,0 +1,10 @@
+dn: uid=danger,ou=users,o=translucent
+objectClass: inetOrgPerson
+uid: danger
+sn: danger
+cn: henry
+businessCategory: frontend-override
+carLicense: LIVID
+employeeType: special
+departmentNumber: 9999999
+roomNumber: 41L-535
diff --git a/tests/data/test-translucent-config.ldif b/tests/data/test-translucent-config.ldif
new file mode 100644
index 0000000..9285176
--- /dev/null
+++ b/tests/data/test-translucent-config.ldif
@@ -0,0 +1,26 @@
+# toplevel
+
+dn: o=translucent
+objectClass: top
+objectClass: organization
+o: translucent
+description: backend database root
+
+# backend OU
+
+dn: ou=users,o=translucent
+objectClass: top
+objectClass: organizationalUnit
+ou: users
+description: backend user container root
+
+# bind user for frontend connection
+
+dn: uid=binder,o=translucent
+objectClass: inetOrgPerson
+uid: binder
+sn: test
+cn: binder
+businessCategory: binder-test-user
+displayName: Binder Test User
+userPassword: bindtest
diff --git a/tests/data/test-translucent-data.ldif b/tests/data/test-translucent-data.ldif
new file mode 100644
index 0000000..2def65c
--- /dev/null
+++ b/tests/data/test-translucent-data.ldif
@@ -0,0 +1,43 @@
+# typical user
+dn: uid=danger,ou=users,o=translucent
+objectClass: inetOrgPerson
+uid: danger
+sn: warning
+cn: danger
+businessCategory: backend-opaque
+initials: dw
+carLicense: BACK
+departmentNumber: 7341
+displayName: Warning
+employeeNumber: 5150
+employeeType: contractor
+givenName: Danger Warning
+
+# another example
+dn: uid=example,ou=users,o=translucent
+objectClass: inetOrgPerson
+uid: example
+sn: user
+cn: example
+businessCategory: backend-opaque
+carLicense: SAMPLE
+departmentNumber: 7341
+displayName: Example
+employeeNumber: 5150
+employeeType: fulltime
+givenName: Example User
+
+#
+dn: uid=fred,ou=users,o=translucent
+objectClass: inetOrgPerson
+uid: fred
+sn: said
+cn: said
+businessCategory: backend-opaque
+carLicense: RIGHT
+departmentNumber: 9919
+displayName: Right Said Fred
+employeeNumber: 44199
+employeeType: fulltime
+givenName: Right Said
+
diff --git a/tests/data/test-translucent-merged.ldif b/tests/data/test-translucent-merged.ldif
new file mode 100644
index 0000000..583734a
--- /dev/null
+++ b/tests/data/test-translucent-merged.ldif
@@ -0,0 +1,41 @@
+dn: uid=danger,ou=users,o=translucent
+objectClass: inetOrgPerson
+uid: danger
+sn: danger
+cn: henry
+businessCategory: frontend-override
+initials: dw
+carLicense: LIVID
+departmentNumber: 9999999
+displayName: Warning
+employeeNumber: 5150
+employeeType: special
+givenName: Danger Warning
+roomNumber: 41L-535
+
+dn: uid=example,ou=users,o=translucent
+objectClass: inetOrgPerson
+uid: example
+sn: user
+cn: example
+businessCategory: backend-opaque
+carLicense: SAMPLE
+departmentNumber: 7341
+displayName: Example
+employeeNumber: 5150
+employeeType: fulltime
+givenName: Example User
+
+dn: uid=fred,ou=users,o=translucent
+objectClass: inetOrgPerson
+uid: fred
+sn: said
+cn: said
+businessCategory: backend-opaque
+carLicense: RIGHT
+departmentNumber: 9919
+displayName: Right Said Fred
+employeeNumber: 44199
+employeeType: fulltime
+givenName: Right Said
+
diff --git a/tests/data/test-unique.ldif b/tests/data/test-unique.ldif
new file mode 100755
index 0000000..9e594ff
--- /dev/null
+++ b/tests/data/test-unique.ldif
@@ -0,0 +1,29 @@
+# base
+
+dn: o=unique
+objectClass: top
+objectClass: organization
+o: unique
+description: unique test database
+
+# container
+
+dn: ou=users,o=unique
+objectClass: top
+objectClass: organizationalUnit
+ou: users
+description: container for test users
+
+# manager
+dn: uid=george,ou=users,o=unique
+objectClass: inetOrgPerson
+uid: george
+sn: jungle
+cn: george
+businessCategory: test
+carLicense: SAMPLE
+departmentNumber: 6969
+displayName: George
+employeeNumber: 5150
+employeeType: contractor
+givenName: Big G
diff --git a/tests/data/test-unordered.ldif b/tests/data/test-unordered.ldif
new file mode 100644
index 0000000..bdccea2
--- /dev/null
+++ b/tests/data/test-unordered.ldif
@@ -0,0 +1,55 @@
+dn: ou=test,dc=example,dc=com
+objectClass: organizationalUnit
+ou: test
+
+dn: ou=Policies,ou=test,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Policies
+
+dn: ou=slapo-ppolicy,ou=Policies,ou=test,dc=example,dc=com
+objectClass: organizationalUnit
+ou: slapo-ppolicy
+ou: Password Policies
+
+dn: uid=michael,ou=Users,ou=test,dc=example,dc=com
+uid: michael
+objectClass: account
+objectClass: simpleSecurityObject
+userPassword: testsecret
+
+dn: ou=Users,ou=test,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Users
+
+dn: cn=slapd-1,ou=Systemkonten,ou=test,dc=example,dc=com
+cn: slapd-1
+objectClass: applicationProcess
+objectClass: simpleSecurityObject
+userPassword: pw_slapd1
+
+dn: cn=slapd-2,ou=Systemkonten,ou=test,dc=example,dc=com
+cn: slapd-2
+objectClass: applicationProcess
+objectClass: simpleSecurityObject
+userPassword: pw_slapd2
+
+dn: ou=Systemkonten,ou=test,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Systemkonten
+
+dn: ou=Groups,ou=test,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: cn=Admins,ou=Groups,ou=test,dc=example,dc=com
+cn: Admins
+cn: Password Admins
+objectClass: groupOfNames
+member: uid=michael,ou=Users,ou=test,dc=example,dc=com
+
+dn: cn=Replicas,ou=Groups,ou=test,dc=example,dc=com
+member: cn=slapd-1,ou=Systemkonten,ou=test,dc=example,dc=com
+member: cn=slapd-2,ou=Systemkonten,ou=test,dc=example,dc=com
+cn: Replicas
+objectClass: groupOfNames
+
diff --git a/tests/data/test-valsort.ldif b/tests/data/test-valsort.ldif
new file mode 100755
index 0000000..c2c7f3e
--- /dev/null
+++ b/tests/data/test-valsort.ldif
@@ -0,0 +1,49 @@
+# base
+
+dn: o=valsort
+objectClass: top
+objectClass: organization
+o: valsort
+description: valsort test database
+
+# container
+
+dn: ou=users,o=valsort
+objectClass: top
+objectClass: organizationalUnit
+ou: users
+description: container for test valsort users
+
+# manager
+dn: uid=george,ou=users,o=valsort
+objectClass: OpenLDAPperson
+uid: george
+sn: jungle
+sn: alpha
+sn: zib
+sn: tree
+cn: george
+businessCategory: test
+carLicense: SAMPLE
+departmentNumber: 1
+departmentNumber: 5
+departmentNumber: 3
+departmentNumber: 10
+departmentNumber: 72
+departmentNumber: 37
+departmentNumber: 46
+displayName: George
+employeeNumber: 5150
+employeeType: {1}contractor
+employeeType: {1}staff
+employeeType: {1}anarchist
+givenName: Big G
+ou: {1}Chemistry
+ou: {8}Academia
+ou: {3}Hum Bio
+ou: {2}Computer Science
+mailPreferenceOption: 3
+mailPreferenceOption: 87
+mailPreferenceOption: 22
+mailPreferenceOption: 1
+mailPreferenceOption: 66
diff --git a/tests/data/test-whoami.ldif b/tests/data/test-whoami.ldif
new file mode 100644
index 0000000..6a70ab4
--- /dev/null
+++ b/tests/data/test-whoami.ldif
@@ -0,0 +1,468 @@
+#LEAD COMMENT
+dn: dc=example,dc=com
+#EMBEDDED COMMENT
+objectclass: top
+objectclass: organization
+objectclass: domainRelatedObject
+objectclass: dcobject
+objectClass: simpleSecurityObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postaladdress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephonenumber: +1 313 555 1817
+associateddomain: example.com
+userpassword:: ZXhhbXBsZQ==
+authzTo: dn:
+
+dn: ou=People,dc=example,dc=com
+objectclass: organizationalUnit
+objectclass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: ou=Groups,dc=example,dc=com
+objectclass: organizationalUnit
+ou: Groups
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: organizationalUnit
+ou: Alumni Association
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectclass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectclass: groupofnames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectclass: groupofnames
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectclass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postaladdress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+userpassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homepostaladdress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homephone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimiletelephonenumber: +1 313 555 2274
+telephonenumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectclass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+userpassword:: Ympvcm4=
+homepostaladdress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postaladdress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homephone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimiletelephonenumber: +1 313 555 2177
+telephonenumber: +1 313 555 0355
+authzFrom: dn.exact:cn=Barbara Jensen,ou=Information Technology Division,ou=Pe
+ ople,dc=example,dc=com
+authzFrom: u:melliot
+authzFrom: ldap:///ou=People,dc=example,dc=com??sub?(|(cn=Jane Doe)
+ (cn=Jennifer Smith))
+authzFrom: group/groupOfUniqueNames/uniqueMember:cn=ITD Staff,ou=Groups,dc=exa
+ mple,dc=com
+authzFrom: dn.onelevel:ou=Information Technology Division,ou=People,dc=example,dc=com
+authzFrom: dn.regex:^cn=Dorothy.*dc=example,dc=com$
+authzFrom: dn.children:ou=Alumni Association,ou=People,dc=example
+ ,dc=com
+authzFrom: dn.subtree:ou=Groups,dc=example,dc=com
+authzTo: dn.exact:cn=Barbara Jensen,ou=Information Technology Division,ou=Peop
+ le,dc=example,dc=com
+authzTo: u:melliot
+authzTo: ldap:///ou=People,dc=example,dc=com??sub?cn=Jane Doe
+authzTo: group/groupOfUniqueNames/uniqueMember:cn=ITD Staff,ou=Groups,dc=examp
+ le,dc=com
+authzTo: dn.onelevel:ou=Information Technology Division,ou=People,dc=example,dc=com
+authzTo: dn.regex:^cn=Dorothy.*dc=example,dc=com$
+authzTo: dn.children:ou=Alumni Association,ou=People,dc=example,dc=com
+authzTo: dn.subtree:ou=Groups,dc=example,dc=com
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Lemonade
+homepostaladdress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimiletelephonenumber: +1 313 555 3223
+telephonenumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homephone: +1 313 555 0454
+userpassword:: ZG90cw==
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectclass: groupofuniquenames
+objectclass: simplesecurityobject
+uniquemember: cn=Manager,dc=example,dc=com
+uniquemember: cn=Bjorn Jensen,OU=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniquemember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniquemember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+userpassword:: SVRE
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+userpassword:: amFq
+homepostaladdress: 3882 Beverly Rd. $ Anytown, MI 48105
+homephone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimiletelephonenumber: +1 313 555 4332
+telephonenumber: +1 313 555 0895
+userpassword:: amFq
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectclass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 933 Brooks $ Anytown, MI 48104
+homephone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postaladdress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimiletelephonenumber: +1 313 555 8688
+telephonenumber: +1 313 555 7334
+userpassword:: ampvbmVz
+
+dn: cn=No One,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: No One
+sn: One
+uid: noone
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+userpassword:: bm9vbmU=
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homephone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimiletelephonenumber: +1 313 555 2311
+telephonenumber: +1 313 555 4774
+userpassword:: amRvZQ==
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homepostaladdress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homephone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimiletelephonenumber: +1 313 555 2756
+telephonenumber: +1 313 555 8232
+userpassword:: amVu
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postaladdress: ITD $ 535 W. William $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homephone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimiletelephonenumber: +1 313 555 4544
+telephonenumber: +1 313 555 9394
+userpassword:: am9obmQ=
+
+dn: cn=Manager,dc=example,dc=com
+objectclass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userpassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 199 Outer Drive $ Ypsilanti, MI 48198
+homephone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimiletelephonenumber: +1 313 555 7762
+telephonenumber: +1 313 555 4177
+userpassword:: bWVsbGlvdA==
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postaladdress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+homepostaladdress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homephone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimiletelephonenumber: +1 313 555 9700
+telephonenumber: +1 313 555 5331
+
+dn: cn=Must Fail,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Must Fail
+sn: Fail
+uid: fail
+userpassword:: ZmFpbA==
+
+dn: cn=Should Fail,dc=example,dc=com
+objectclass: OpenLDAPperson
+cn: Should Fail
+sn: Fail
+uid: fail
+userpassword:: ZmFpbA==
+
diff --git a/tests/data/test.ldif b/tests/data/test.ldif
new file mode 100644
index 0000000..e7939c0
--- /dev/null
+++ b/tests/data/test.ldif
@@ -0,0 +1,412 @@
+#LEAD COMMENT
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+#EMBEDDED COMMENT
+member: cn=Manager,dc=example,dc=com
+member: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=exa
+ mple,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=exampl
+ e,dc=com
+owner: cn=Manager,dc=example,dc=com
+cn: All Staff
+description: Everyone in the sample data
+objectClass: groupOfNames
+
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All Alumni Assoc Staff
+cn: Alumni Assoc Staff
+objectClass: groupOfNames
+
+dn: ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Alumni Association
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+objectClass: OpenLDAPperson
+cn: Barbara Jensen
+cn: Babs Jensen
+sn:: IEplbnNlbiA=
+uid: bjensen
+title: Mythical Manager, Research Systems
+postalAddress: ITD Prod Dev & Deployment $ 535 W. William St. Room 4212 $ Anyt
+ own, MI 48103-4943
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: YmplbnNlbg==
+mail: bjensen@mailgw.example.com
+homePostalAddress: 123 Wesley $ Anytown, MI 48103
+description: Mythical manager of the rsdd unix project
+drink: water
+homePhone: +1 313 555 2333
+pager: +1 313 555 3233
+facsimileTelephoneNumber: +1 313 555 2274
+telephoneNumber: +1 313 555 9022
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+objectClass: OpenLDAPperson
+cn: Bjorn Jensen
+cn: Biiff Jensen
+sn: Jensen
+uid: bjorn
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: Ympvcm4=
+homePostalAddress: 19923 Seven Mile Rd. $ South Lyon, MI 49999
+drink: Iced Tea
+description: Hiker, biker
+title: Director, Embedded Systems
+postalAddress: Info Tech Division $ 535 W. William St. $ Anytown, MI 48103
+mail: bjorn@mailgw.example.com
+homePhone: +1 313 555 5444
+pager: +1 313 555 4474
+facsimileTelephoneNumber: +1 313 555 2177
+telephoneNumber: +1 313 555 0355
+
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Dorothy Stevens
+cn: Dot Stevens
+sn: Stevens
+uid: dots
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Lemonade
+homePostalAddress: 377 White St. Apt. 3 $ Anytown, MI 48104
+description: Very tall
+facsimileTelephoneNumber: +1 313 555 3223
+telephoneNumber: +1 313 555 3664
+mail: dots@mail.alumni.example.com
+homePhone: +1 313 555 0454
+
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: domainRelatedObject
+objectClass: dcObject
+dc: example
+l: Anytown, Michigan
+st: Michigan
+o: Example, Inc.
+o: EX
+o: Ex.
+description: The Example, Inc. at Anytown
+postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US
+telephoneNumber: +1 313 555 1817
+associatedDomain: example.com
+
+dn: ou=Groups,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Groups
+
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Information Technology Division
+description:: aMODwoPDgsKCw4PCgsOCwotFVlZQw4PCg8OCwoPDg8KCw4LCv0zDg8KDw4LCgsOD
+ woLDgsKKT8ODwoPDgsKDw4PCgsOCwqs6w4PCg8OCwoLDg8KCw4LCjUQkw4PCg8OCwoLDg8KCw4LCi
+ 01QUcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4
+ LCgsODwoLDgsKLRCQoZitEJMODwoPDgsKCw4PCgsOCwrfDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoP
+ Dg8KCw4LCgcODwoPDgsKDw4PCgsOCwqHDg8KDw4LCgsODwoLDgsKLRCQkZitEJMODwoPDgsKCw4PC
+ gsOCwrfDg8KDw4LCg8ODwoLDgsKQw4PCg8OCwoPDg8KCw4LCisODwoPDgsKCw4PCgsOCwotFUVZqU
+ MODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKAw4PCg8OCwoLDg8KCw4LCik85dCTDg8KDw4
+ LCgsODwoLDgsKFQ8ODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4L
+ Cvzl0JMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPD
+ gsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKLRCTDg8KDw4LCgsODwoLDgsKDw4PCg8OCwoLDg8KCw
+ 4LCuMODwoPDgsKDw4PCgsOCwoR0Q8ODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LChMODwo
+ PDgsKDw4PCgsOCwoFOdTrDg8KDw4LCg8ODwoLDgsKHw4PCg8OCwoPDg8KCw4LChMODwoPDgsKDw4P
+ CgsOCwoFOw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwrtHw4PCg8OCwoLDg8KCw4LChcOD
+ woPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsK4dMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODw
+ oLDgsKtR8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwo
+ PDgsKDw4PCgsOCwr9SfGrDg8KDw4LCgsODwoLDgsKLQGgxw4PCg8OCwoPDg8KCw4LCoWhQw4PCg8O
+ CwoPDg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKT8ODwoPDgsKCw4PCgsOC
+ wotEJDDDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHTDg8KDw4LCgsODwoLDgsKDw4PCg
+ 8OCwoPDg8KCw4LCuHXDg8KDw4LCgsODwoLDgsKLRCRqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4
+ PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpPDg8K
+ Dw4LCg8ODwoLDgsKQXV9eW8ODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoPD
+ g8KCw4LCgsODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODw
+ oPDgsKDw4PCgsOCwozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgs
+ OCwoxWV8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKxw4PCg8OCwoLDg8KCw4LCi3wkw4P
+ Cg8OCwoLDg8KCw4LCjcODwoPDgsKCw4PCgsOCwofDg8KDw4LCg8ODwoLDgsKof8ODwoPDgsKDw4PC
+ gsOCwr/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCg8ODwoPDgsKDw4PCgsOCwrh5w4PCg
+ 8OCwoLDg8KCw4LChzQzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PCgsOCworDg8KDw4LCgsODwo
+ LDgsKIw4PCg8OCwoLDg8KCw4LCuDFBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNdDF
+ Bw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPD
+ gsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw
+ 4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgs
+ KCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKAdcODwoPDgsKDw4PCgsOCwqhtw4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCsMODwoPDgsKC
+ w4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCt
+ sODwoPDgsKDw4PCgsOCwq7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4
+ PCgsOCwoPDg8KDw4LCg8ODwoLDgsKoZsODwoPDgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4w4P
+ Cg8OCwoLDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwpUzw4PCg8OCwoPDg8KCw4LCicODwoPDgsKCw4PC
+ gsOCworDg8KDw4LCgsODwoLDgsKISDJBw4PCg8OCwoPDg8KCw4LCvyTDg8KDw4LCgsODwoLDgsKNN
+ DJBw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwo
+ PDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8O
+ DwoPDgsKDw4PCgsOCwojDg8KDw4LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCnEzDg8KDw4LCgsOD
+ woLDgsKLSEBmw4PCg8OCwoLDg8KCw4LCg3lwdSTDg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw
+ 4LCv8ODwoPDgsKCw4PCgsOCwobDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwp/Dg8KDw4LCgsODwoLDgsKBw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwoj
+ Dg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCwpPDg8KDw4LCgsOD
+ woLDgsKBw4PCg8OCwoPDg8KCw4LCv1rDg8KDw4LCgsODwoLDgsKAw4PCg8OCwoLDg8KCw4LChMODw
+ oPDgsKCw4PCgsOCwodqw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwoBqaMODwoPDgsKCw4
+ PCgsOCwpBQw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDIMODwoPDgsKCw4PCgsOCwopPw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKOacODwoPDgsKCw4PCgsOCwrhf
+ XsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCw
+ oLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKGw4PCg8OCwoLDg8KCw4LCgM
+ ODwoPDgsKCw4PCgsOCwoRJw4PCg8OCwoLDg8KCw4LCgcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsO
+ DwoLDgsKIw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQ9w4PCg8OCwoLDg8KCw4LCgcOD
+ woPDgsKDw4PCgsOCwr9aw4PCg8OCwoLDg8KCw4LCgMODwoPDgsKCw4PCgsOCwoQxw4PCg8OCwoLDg
+ 8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwoM9w4PCg8OCwoPDg8KCw4LCm0
+ 7Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsK
+ Cw4PCgsOCwrhfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODw
+ oPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgs
+ OCwo7Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoLDg8KCw4LCkMODwoPDgsKDw4PCgsOCwojDg8KDw4L
+ CgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsK+
+ S8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKww4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKDw
+ 4PCgsOCwoTDg8KDw4LCgsODwoLDgsKKT1DDg8KDw4LCg8ODwoLDgsKoRsODwoPDgsKCw4PCgsOCwo
+ vDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwrZ0Y8ODwoPDgsK
+ Cw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK/dF/Dg8KDw4LCgsODwoLDgsKhdHpPw4PCg8OCwoLDg8KC
+ w4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PCg8OCwoPDg8KCw4LCqC1Jw4PCg8OCwoLDg8KCw4LChcODw
+ oPDgsKDw4PCgsOCwoB1RMODwoPDgsKCw4PCgsOCwqFwek/Dg8KDw4LCgsODwoLDgsKLw4PCg8OCwo
+ PDg8KCw4LCj1DDg8KDw4LCg8ODwoLDgsKoScODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsK
+ AdTPDg8KDw4LCgsODwoLDgsKhbHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo5Qw4PC
+ g8OCwoPDg8KCw4LCqEnDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHXDg8KDw4LCgsODw
+ oLDgsKhaHpPw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo9Qw4PCg8OCwoPDg8KCw4LCqM
+ ODwoPDgsKDw4PCgsOCwrpIw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoB1M8ODwoPDgsK
+ Dw4PCgsOCwoBfXsODwoPDgsKDw4PCgsOCwoLDg8KDw4LCgsODwoLDgsK4X17Dg8KDw4LCg8ODwoLD
+ gsKCw4PCg8OCwoLDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgjPDg8KDw4LCg8ODwoLDgsKAX17Dg
+ 8KDw4LCg8ODwoLDgsKCw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo7Dg8KDw4LCg8ODwo
+ LDgsKoJ8ODwoPDgsKDw4PCgsOCwq3Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODwoP
+ DgsKCw4PCgsOCwoPDg8KDw4LCg8ODwoLDgsK4aHU5w4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PC
+ gsOCwovDg8KDw4LCg8ODwoLDgsKOw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpDDg8KDw
+ 4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgs
+ KIw4PCg8OCwoPDg8KCw4LCv8ODwoPDgsKCw4PCgsOCwpLDg8KDw4LCg8ODwoLDgsKEw4PCg8OCwoL
+ Dg8KCw4LChcODwoPDgsKDw4PCgsOCwoB0IcODwoPDgsKCw4PCgsOCwovDg8KDw4LCgsODwoLDgsKA
+ w4PCg8OCwoPDg8KCw4LCtMODwoPDgsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKAdGbDg8KDw4LCg
+ sODwoLDgsKLQGY9dGY9dTPDg8KDw4LCg8ODwoLDgsKAX17Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwo
+ LDg8KCw4LCuF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwrhfXsODwoPDgsKDw4PCgsO
+ CwoIzw4PCg8OCwoPDg8KCw4LCgF9ew4PCg8OCwoPDg8KCw4LCgsODwoPDgsKCw4PCgsOCwovDg8KD
+ w4LCg8ODwoLDgsK/Ri9BUC9BRi9BWi9BZC9BWzBBZC9BZTBBZC9BZC9BbzBBZC9BeTBBw4PCg8OCw
+ oLDg8KCw4LCgzBBMUFhMUFrMUE=
+description:: UF7Dg8KDw4LCg8ODwoLDgsKCw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOC
+ wozDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg
+ 8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCqFDDg8KDw4LCg8ODwoLDgsKpRsODwoPDgsKDw4PCgsOCwo
+ zDg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKDw4PCgsOCwozDg8KDw4LCg8O
+ DwoLDgsKMw4PCg8OCwoPDg8KCw4LCjMODwoPDgsKCw4PCgsOCwotEJCDDg8KDw4LCgsODwoLDgsKD
+ w4PCg8OCwoPDg8KCw4LCrMODwoPDgsKCw4PCgsOCwotUJCRTw4PCg8OCwoLDg8KCw4LCi1wkJFbDg
+ 8KDw4LCgsODwoLDgsKJTCRXVVBSU8ODwoPDgsKDw4PCgsOCwqjDg8KDw4LCg8ODwoLDgsKdT8ODwo
+ PDgsKCw4PCgsOCwoN8JDB1w4PCg8OCwoPDg8KCw4LCh8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8O
+ DwoLDgsKBTsODwoPDgsKDw4PCgsOCwqktw4PCg8OCwoLDg8KCw4LCg3wkMHTDg8KDw4LCgsODwoLD
+ gsKDfCQww4PCg8OCwoLDg8KCw4LChTPDg8KDw4LCg8ODwoLDgsK2OTXDg8KDw4LCg8ODwoLDgsKAw
+ 4PCg8OCwoPDg8KCw4LCgU7Dg8KDw4LCgsODwoLDgsKEIMODwoPDgsKCw4PCgsOCwqFIw4PCg8OCwo
+ PDg8KCw4LChU7Dg8KDw4LCgsODwoLDgsKJNcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCg8ODwoLDgsK
+ BTsODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsKIw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKD
+ w4PCgsOCwr9TXMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw
+ 4LChMODwoPDgsKCw4PCgsOCwpHDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLIEjDg8
+ KDw4LCg8ODwoLDgsKFTlDDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ngw4PCg8OCwoL
+ Dg8KCw4LCi8ODwoPDgsKDw4PCgsOCwpjDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCm3Rx
+ w4PCg8OCwoLDg8KCw4LCizvDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi8ODwoPDgsKDw
+ 4PCgsOCwr9XaMODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGdGLDg8KDw4LCgsODwo
+ LDgsKLf2zDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCi1D
+ Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8OD
+ woLDgsKow4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwq10SmgoT03Dg8KDw4LCgsODwoLDg
+ sKLw4PCg8OCwoPDg8KCw4LCjcODwoPDgsKDw4PCgsOCwqggTMODwoPDgsKCw4PCgsOCwoXDg8KDw4
+ LCg8ODwoLDgsKAdDrDg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLTSBQUcODwoPDgsK
+ Dw4PCgsOCwr/Dg8KDw4LCg8ODwoLDgsKMw4PCg8OCwoLDg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKL
+ RCQoZitEJCDDg8KDw4LCgsODwoLDgsK3w4PCg8OCwoPDg8KCw4LCiMODwoPDgsKDw4PCgsOCwoHDg
+ 8KDw4LCg8ODwoLDgsKhw4PCg8OCwoLDg8KCw4LCi0QkJGYrRCTDg8KDw4LCgsODwoLDgsK3w4PCg8
+ OCwoPDg8KCw4LCkMODwoPDgsKDw4PCgsOCworDg8KDw4LCgsODwoLDgsKLRSBRVmpQw4PCg8OCwoP
+ Dg8KCw4LCv8ODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsODwoLDgsKKTzl0JHXDg8KDw4LCgsODwoLD
+ gsKhOXQkw4PCg8OCwoLDg8KCw4LChW/Dg8KDw4LCg8ODwoLDgsK/w4PCg8OCwoPDg8KCw4LCv8ODw
+ oPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKhRMODwoPDgsKDw4PCgsOCwoVOw4PCg8OCwoLDg8
+ KCw4LCi8ODwoPDgsKDw4PCgsOCwojDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCv1Ncw4P
+ Cg8OCwoLDg8KCw4LCiUQkw4PCg8OCwoLDg8KCw4LChcODwoPDgsKDw4PCgsOCwoDDg8KDw4LCgsOD
+ woLDgsKEw4PCg8OCwoPDg8KCw4LCtjPDg8KDw4LCg8ODwoLDgsK2w4PCg8OCwoLDg8KCw4LCjUQkw
+ 4PCg8OCwoLDg8KCw4LCiyBEw4PCg8OCwoPDg8KCw4LChU5Qw4PCg8OCwoLDg8KCw4LCi8ODwoPDgs
+ KDw4PCgsOCwr9TYMODwoPDgsKCw4PCgsOCwovDg8KDw4LCg8ODwoLDgsK4w4PCg8OCwoLDg8KCw4L
+ ChcODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKEw4PCg8OCwoPDg8KCw4LCkMODwoPDgsKC
+ w4PCgsOCwovDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCj8ODwoPDgsKDw4PCgsOCwr9Ta
+ MODwoPDgsKCw4PCgsOCwolEJDvDg8KDw4LCg8ODwoLDgsKGw4PCg8OCwoLDg8KCw4LChMODwoPDgs
+ KCw4PCgsOCwr3Dg8KDw4LCgsODwoLDgsKNRCTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4L
+ Cj1DDg8KDw4LCg8ODwoLDgsK/U2zDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoPDg8KCw4LCqMODwoPD
+ gsKCw4PCgsOCwoXDg8KDw4LCg8ODwoLDgsKtw4PCg8OCwoLDg8KCw4LChMODwoPDgsKCw4PCgsOCw
+ p9oMMODwoPDgsKDw4PCgsOCwolMw4PCg8OCwoLDg8KCw4LCi8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4
+ LCg8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCq0vDg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4L
+ CgMODwoPDgsKCw4PCgsOCwoTDg8KDw4LCgsODwoLDgsKLw4PCg8OCwoLDg8KCw4LCi0QkOcODwoPD
+ gsKCw4PCgsOCwrDDg8KDw4LCg8ODwoLDgsKEdEU5w4PCg8OCwoLDg8KCw4LCtTR0PcODwoPDgsKCw
+ 4PCgsOCwovDg8KDw4LCg8ODwoLDgsKNw4PCg8OCwoPDg8KCw4LCqMODwoPDgsKDw4PCgsOCwo5Lw4
+ PCg8OCwoLDg8KCw4LCi0AgUMODwoPDgsKDw4PCgsOCwr/Dg8KDw4LCgsODwoLDgsKsw4PCg8OCwoL
+ Dg8KCw4LCik/Dg8KDw4LCgsODwoLDgsKFw4PCg8OCwoPDg8KCw4LCgHUow4PCg8OCwoLDg8KCw4LC
+ i8ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCgsODwoLDgsKJw4PCg8OCwoLDg8KCw4LCtTTDg8KDw4LCg
+ 8ODwoLDgsKow4PCg8OCwoPDg8KCw4LCl8ODwoPDgsKDw4PCgsOCwrtWw4PCg8OCwoLDg8KCw4LCi8
+ ODwoPDgsKDw4PCgsOCwo3Dg8KDw4LCg8ODwoLDgsKow4PCg8OCwoLDg8KCw4LCnw==
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+owner: cn=Manager,dc=example,dc=com
+description: All ITD Staff
+cn: ITD Staff
+objectClass: groupOfUniqueNames
+uniqueMember: cn=Manager,dc=example,dc=com
+uniqueMember: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=
+ example,dc=com
+uniqueMember: cn=James A Jones 2,ou=Information Technology Division,ou=People,
+ dc=example,dc=com
+uniqueMember: cn=John Doe,ou=Information Technology Division,ou=People,dc=exam
+ ple,dc=com
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 1
+cn: James Jones
+cn: Jim Jones
+sn: Jones
+uid: jaj
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+userPassword:: amFq
+homePostalAddress: 3882 Beverly Rd. $ Anytown, MI 48105
+homePhone: +1 313 555 4772
+description: Outstanding
+title: Mad Cow Researcher, UM Alumni Association
+pager: +1 313 555 3923
+mail: jaj@mail.alumni.example.com
+facsimileTelephoneNumber: +1 313 555 4332
+telephoneNumber: +1 313 555 0895
+
+dn: cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example
+ ,dc=com
+objectClass: OpenLDAPperson
+cn: James A Jones 2
+cn: James Jones
+cn: Jim Jones
+sn: Doe
+uid: jjones
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 933 Brooks $ Anytown, MI 48104
+homePhone: +1 313 555 8838
+title: Senior Manager, Information Technology Division
+description: Not around very much
+mail: jjones@mailgw.example.com
+postalAddress: Info Tech Division $ 535 W William $ Anytown, MI 48103
+pager: +1 313 555 2833
+facsimileTelephoneNumber: +1 313 555 8688
+telephoneNumber: +1 313 555 7334
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jane Doe
+cn: Jane Alverson
+sn: Doe
+uid: jdoe
+title: Programmer Analyst, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+drink: diet coke
+description: Enthusiastic
+mail: jdoe@woof.net
+homePhone: +1 313 555 5445
+pager: +1 313 555 1220
+facsimileTelephoneNumber: +1 313 555 2311
+telephoneNumber: +1 313 555 4774
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Jennifer Smith
+cn: Jen Smith
+sn: Smith
+uid: jen
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+drink: Sam Adams
+homePostalAddress: 1000 Maple #44 $ Anytown, MI 48103
+title: Telemarketer, UM Alumni Association
+mail: jen@mail.alumni.example.com
+homePhone: +1 313 555 2333
+pager: +1 313 555 6442
+facsimileTelephoneNumber: +1 313 555 2756
+telephoneNumber: +1 313 555 8232
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: John Doe
+cn: Jonathon Doe
+sn: Doe
+uid: johnd
+postalAddress: ITD $ 535 W. William $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 912 East Bllvd $ Anytown, MI 48104
+title: System Administrator, Information Technology Division
+description: overworked!
+mail: johnd@mailgw.example.com
+homePhone: +1 313 555 3774
+pager: +1 313 555 6573
+facsimileTelephoneNumber: +1 313 555 4544
+telephoneNumber: +1 313 555 9394
+
+dn: cn=Manager,dc=example,dc=com
+objectClass: person
+cn: Manager
+cn: Directory Manager
+cn: Dir Man
+sn: Manager
+description: Manager of the directory
+userPassword:: c2VjcmV0
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Mark Elliot
+cn: Mark A Elliot
+sn: Elliot
+uid: melliot
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 199 Outer Drive $ Ypsilanti, MI 48198
+homePhone: +1 313 555 0388
+drink: Gasoline
+title: Director, UM Alumni Association
+mail: melliot@mail.alumni.example.com
+pager: +1 313 555 7671
+facsimileTelephoneNumber: +1 313 555 7762
+telephoneNumber: +1 313 555 4177
+
+dn: ou=People,dc=example,dc=com
+objectClass: organizationalUnit
+objectClass: extensibleObject
+ou: People
+uidNumber: 0
+gidNumber: 0
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+objectClass: OpenLDAPperson
+cn: Ursula Hampster
+sn: Hampster
+uid: uham
+title: Secretary, UM Alumni Association
+postalAddress: Alumni Association $ 111 Maple St $ Anytown, MI 48109
+seeAlso: cn=All Staff,ou=Groups,dc=example,dc=com
+homePostalAddress: 123 Anystreet $ Anytown, MI 48104
+mail: uham@mail.alumni.example.com
+homePhone: +1 313 555 8421
+pager: +1 313 555 2844
+facsimileTelephoneNumber: +1 313 555 9700
+telephoneNumber: +1 313 555 5331
+
diff --git a/tests/data/test.schema b/tests/data/test.schema
new file mode 100644
index 0000000..0635bf7
--- /dev/null
+++ b/tests/data/test.schema
@@ -0,0 +1,69 @@
+# OpenLDAP Test schema
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+#
+
+# For testing purposes only.
+
+# For Attribute Aliasing.
+attributetype ( 1.3.6.1.4.1.4203.1.12.1.1.1
+ NAME 'x509CertificateIssuer'
+ EQUALITY distinguishedNameMatch
+ DESC 'Aliasing attribute: Issuer, use'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
+
+attributetype ( 1.3.6.1.4.1.4203.1.12.1.1.2
+ NAME 'x509CertificateSerial'
+ DESC 'Aliasing attribute: Serial, use'
+ EQUALITY integerMatch
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )
+
+attributetype ( 1.3.6.1.4.1.4203.1.12.1.1.3
+ NAME 'x509CertificateSerialAndIssuer'
+ DESC 'Aliasing attribute: Serial and Issuer together, use'
+ EQUALITY certificateExactMatch
+ SYNTAX 1.3.6.1.1.15.1 )
+
+# generalized time testing
+attributetype ( 1.3.6.1.4.1.4203.1.12.1.1.4
+ name 'testTime'
+ equality generalizedTimeMatch
+ ordering generalizedTimeOrderingMatch
+ syntax 1.3.6.1.4.1.1466.115.121.1.24
+ single-value )
+
+# for UUID testing
+attributetype ( 1.3.6.1.4.1.4203.1.12.1.1.5
+ name 'testUUID'
+ equality UUIDMatch
+ ordering UUIDOrderingMatch
+ syntax 1.3.6.1.1.16.1 )
+
+# for obsolete testing
+attributetype ( 1.3.6.1.4.1.4203.1.12.1.1.6
+ name 'testObsolete'
+ obsolete
+ equality booleanMatch
+ syntax 1.3.6.1.4.1.1466.115.121.1.7
+ single-value )
+
+objectClass ( 1.3.6.1.4.1.4203.1.12.1.2.1
+ name 'testPerson' sup OpenLDAPperson
+ may testTime )
+
+objectClass ( 1.3.6.1.4.1.4203.1.12.1.2.2
+ name 'obsoletePerson'
+ obsolete auxiliary
+ may ( testObsolete ) )
+
diff --git a/tests/data/tls/ca/certs/testsuiteCA.crt b/tests/data/tls/ca/certs/testsuiteCA.crt
new file mode 100644
index 0000000..62c88ac
--- /dev/null
+++ b/tests/data/tls/ca/certs/testsuiteCA.crt
@@ -0,0 +1,121 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 0b:43:f8:e9:ee:d3:38:37:92:db:19:65:d9:94:17:cc:70:45:d4:06
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, ST=CA, O=OpenLDAP Foundation, OU=OpenLDAP Test Suite
+ Validity
+ Not Before: Oct 30 15:29:02 2018 GMT
+ Not After : Nov 13 15:29:02 2519 GMT
+ Subject: C=US, ST=CA, O=OpenLDAP Foundation, OU=OpenLDAP Test Suite
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (4096 bit)
+ Modulus:
+ 00:be:e0:ff:36:89:65:c0:4e:46:e6:24:e8:3d:81:
+ 97:92:28:4b:11:c6:21:ac:28:14:31:b2:a3:64:24:
+ 62:61:24:bd:76:7b:9e:7c:3a:50:65:fa:97:f3:c5:
+ 9d:49:cc:61:3a:31:6f:0d:a4:d8:70:57:73:c8:c6:
+ 66:06:d0:59:3f:24:3b:56:5d:70:20:e4:51:2b:88:
+ 5e:f4:78:82:bc:55:b5:d5:5b:f6:e5:55:1f:3a:af:
+ 59:9f:b7:5d:72:70:fe:b6:a4:dd:4e:f9:d0:38:e8:
+ 15:14:c7:45:ed:5e:d3:4c:ee:02:34:3a:37:d8:75:
+ f1:49:0d:f6:8a:7b:8c:87:39:c9:fb:f2:3a:96:57:
+ cd:7c:18:a7:bb:35:de:d3:c4:79:57:20:48:07:b9:
+ 65:f6:bd:7b:01:5c:99:8a:92:35:7c:b7:e3:96:1c:
+ 6f:4c:47:42:c1:77:d6:62:49:0e:be:01:8f:c9:f4:
+ 64:68:4c:b0:ec:10:12:d0:0e:5f:67:0e:e8:a4:bd:
+ df:9c:fb:5b:04:6f:3c:2a:35:1b:5a:ca:98:ba:f3:
+ 61:f4:3a:77:28:be:a3:63:f1:d6:94:0d:fb:a0:87:
+ e3:a5:9f:56:b6:a6:6a:90:13:80:2a:2e:ae:fe:af:
+ aa:e3:e7:d8:3b:2b:a3:52:4f:73:2d:12:aa:e2:a3:
+ 0c:aa:fb:11:40:86:68:de:be:2b:9b:36:19:9c:d7:
+ d7:5e:13:21:c9:b3:34:6d:09:53:ff:a3:2e:92:f4:
+ 33:80:de:7a:47:1c:47:57:68:53:2a:db:73:6e:6d:
+ fa:40:df:55:25:a1:fc:87:c4:86:ef:6e:16:ec:f8:
+ 48:35:f5:96:b3:55:ce:56:a9:6e:c1:8c:ea:32:85:
+ 26:ea:af:0c:92:24:05:e2:49:12:b7:07:8f:06:96:
+ be:13:fa:ec:49:f7:d4:49:6f:b9:c7:6c:79:53:39:
+ a3:89:c4:4a:92:66:b0:f3:0c:72:6d:50:3c:63:1f:
+ f3:76:63:a8:aa:b7:fd:db:ef:98:b4:5b:49:b6:84:
+ 66:e5:fc:60:0b:c1:f7:b0:f7:84:68:7e:71:5d:ac:
+ fc:a9:cb:f6:02:fc:86:d3:a7:c3:42:ef:ba:f4:1a:
+ 27:71:5d:22:f5:53:e1:a6:f4:a5:dc:31:38:45:0b:
+ a1:6d:ab:9c:05:2e:87:8c:31:02:99:80:6d:3f:66:
+ e8:8a:d7:64:4f:08:7e:2f:f0:1f:28:ff:85:57:22:
+ ee:6a:a7:05:72:f8:cf:5d:07:c6:73:23:82:85:82:
+ 76:4e:36:8a:ec:ea:f1:53:1e:e0:77:d1:4a:9f:df:
+ ec:87:91:0a:56:40:b7:23:19:fa:60:14:d0:f0:32:
+ 4d:11:39
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 90:CF:51:1D:E8:08:D4:4C:34:70:71:6B:D2:0B:00:68:D9:FD:60:50
+ X509v3 Authority Key Identifier:
+ keyid:90:CF:51:1D:E8:08:D4:4C:34:70:71:6B:D2:0B:00:68:D9:FD:60:50
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE
+ Signature Algorithm: sha256WithRSAEncryption
+ 0f:7f:a0:c5:3c:ac:dc:ed:8f:56:3e:64:89:e6:87:d0:ca:a5:
+ 37:b8:0e:49:aa:93:d3:e5:ac:ff:54:24:91:07:1b:9c:dc:08:
+ e6:cc:15:53:be:85:4c:51:52:d3:88:d0:d8:c7:b7:98:40:41:
+ 8a:a7:7a:4c:96:85:61:8c:98:76:f6:a3:2c:10:31:a1:d8:e6:
+ a7:4c:ec:c3:29:ad:04:8b:e3:f2:2d:4c:30:0d:a4:bc:c8:93:
+ d2:9b:88:1d:a4:25:eb:ff:9f:f2:d9:c5:3b:bf:51:91:71:06:
+ 92:35:96:5c:ca:6d:d6:86:47:63:07:7f:37:35:53:68:e9:4e:
+ d0:d0:25:42:18:e0:00:9e:ca:f5:bd:b7:94:ee:99:51:44:3a:
+ 0c:44:40:e3:87:e6:ce:6c:2b:3f:c1:01:6c:5c:32:d5:59:b5:
+ bd:25:a3:1a:ff:85:a5:89:9c:d8:24:4b:fa:59:99:5a:64:ab:
+ a1:d8:0f:c0:19:28:84:1e:89:c2:a1:15:4e:0f:7e:1f:bf:f8:
+ 92:df:9f:1c:d5:4a:98:40:82:ee:41:1f:de:f7:25:11:fd:76:
+ 0a:cf:37:40:bc:c2:2d:6a:ea:4a:0c:6d:b0:e6:75:37:b5:63:
+ a8:a1:c5:81:d0:84:c0:f3:e0:c3:5c:c4:9f:ec:3b:9f:8a:74:
+ ce:f0:cc:e3:e9:15:08:a0:ea:3e:a9:8e:bc:9a:01:00:96:fe:
+ 37:6f:61:b5:2c:4b:1f:5d:d7:24:09:fe:bf:f4:77:47:e4:ee:
+ 7c:ea:6b:67:84:ee:56:4f:5f:b9:b8:e4:db:70:e1:4a:b3:94:
+ 4d:dd:52:45:05:4d:79:d4:7c:8b:9d:9b:6a:0b:73:9e:f3:0e:
+ d5:d5:46:da:b4:fb:4a:ea:5b:ab:8e:42:68:0e:96:cd:8a:6e:
+ 35:a8:e6:1b:6a:ed:a8:9e:3c:cc:3b:44:54:b8:2d:ba:c7:83:
+ 91:7c:70:40:0c:14:b8:21:7a:12:ac:8c:96:4c:94:a6:ee:fe:
+ cc:77:34:8e:e3:c3:c0:44:19:51:85:07:6c:d8:d1:2e:69:8d:
+ b1:0e:42:fb:e6:16:65:86:c6:e3:2f:a7:3f:b4:8e:4f:1c:83:
+ c4:0a:ae:a0:d9:17:fd:cf:a2:38:a1:9f:70:dc:5c:df:3c:07:
+ 7b:64:01:ff:35:8c:45:43:e8:fa:a4:f6:c4:71:78:17:6e:6a:
+ 7f:d1:6e:66:c6:89:33:3b:28:4a:76:bf:ca:29:05:51:07:98:
+ ce:63:62:25:61:7f:5e:c6:91:23:02:13:15:4f:fd:24:58:9d:
+ 2d:ac:eb:cb:9a:c2:82:2f:50:5c:5a:16:bb:8c:bf:4d:66:2c:
+ 6f:1c:c4:a9:28:e1:3d:4d
+-----BEGIN CERTIFICATE-----
+MIIFjzCCA3egAwIBAgIUC0P46e7TODeS2xll2ZQXzHBF1AYwDQYJKoZIhvcNAQEL
+BQAwVjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRwwGgYDVQQKDBNPcGVuTERB
+UCBGb3VuZGF0aW9uMRwwGgYDVQQLDBNPcGVuTERBUCBUZXN0IFN1aXRlMCAXDTE4
+MTAzMDE1MjkwMloYDzI1MTkxMTEzMTUyOTAyWjBWMQswCQYDVQQGEwJVUzELMAkG
+A1UECAwCQ0ExHDAaBgNVBAoME09wZW5MREFQIEZvdW5kYXRpb24xHDAaBgNVBAsM
+E09wZW5MREFQIFRlc3QgU3VpdGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
+AoICAQC+4P82iWXATkbmJOg9gZeSKEsRxiGsKBQxsqNkJGJhJL12e558OlBl+pfz
+xZ1JzGE6MW8NpNhwV3PIxmYG0Fk/JDtWXXAg5FEriF70eIK8VbXVW/blVR86r1mf
+t11ycP62pN1O+dA46BUUx0XtXtNM7gI0OjfYdfFJDfaKe4yHOcn78jqWV818GKe7
+Nd7TxHlXIEgHuWX2vXsBXJmKkjV8t+OWHG9MR0LBd9ZiSQ6+AY/J9GRoTLDsEBLQ
+Dl9nDuikvd+c+1sEbzwqNRtaypi682H0OncovqNj8daUDfugh+Oln1a2pmqQE4Aq
+Lq7+r6rj59g7K6NST3MtEqriowyq+xFAhmjeviubNhmc19deEyHJszRtCVP/oy6S
+9DOA3npHHEdXaFMq23NubfpA31UlofyHxIbvbhbs+Eg19ZazVc5WqW7BjOoyhSbq
+rwySJAXiSRK3B48Glr4T+uxJ99RJb7nHbHlTOaOJxEqSZrDzDHJtUDxjH/N2Y6iq
+t/3b75i0W0m2hGbl/GALwfew94RofnFdrPypy/YC/IbTp8NC77r0GidxXSL1U+Gm
+9KXcMThFC6Ftq5wFLoeMMQKZgG0/ZuiK12RPCH4v8B8o/4VXIu5qpwVy+M9dB8Zz
+I4KFgnZONors6vFTHuB30Uqf3+yHkQpWQLcjGfpgFNDwMk0ROQIDAQABo1MwUTAd
+BgNVHQ4EFgQUkM9RHegI1Ew0cHFr0gsAaNn9YFAwHwYDVR0jBBgwFoAUkM9RHegI
+1Ew0cHFr0gsAaNn9YFAwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
+AgEAD3+gxTys3O2PVj5kieaH0MqlN7gOSaqT0+Ws/1QkkQcbnNwI5swVU76FTFFS
+04jQ2Me3mEBBiqd6TJaFYYyYdvajLBAxodjmp0zswymtBIvj8i1MMA2kvMiT0puI
+HaQl6/+f8tnFO79RkXEGkjWWXMpt1oZHYwd/NzVTaOlO0NAlQhjgAJ7K9b23lO6Z
+UUQ6DERA44fmzmwrP8EBbFwy1Vm1vSWjGv+FpYmc2CRL+lmZWmSrodgPwBkohB6J
+wqEVTg9+H7/4kt+fHNVKmECC7kEf3vclEf12Cs83QLzCLWrqSgxtsOZ1N7VjqKHF
+gdCEwPPgw1zEn+w7n4p0zvDM4+kVCKDqPqmOvJoBAJb+N29htSxLH13XJAn+v/R3
+R+TufOprZ4TuVk9fubjk23DhSrOUTd1SRQVNedR8i52bagtznvMO1dVG2rT7Supb
+q45CaA6WzYpuNajmG2rtqJ48zDtEVLgtuseDkXxwQAwUuCF6EqyMlkyUpu7+zHc0
+juPDwEQZUYUHbNjRLmmNsQ5C++YWZYbG4y+nP7SOTxyDxAquoNkX/c+iOKGfcNxc
+3zwHe2QB/zWMRUPo+qT2xHF4F25qf9FuZsaJMzsoSna/yikFUQeYzmNiJWF/XsaR
+IwITFU/9JFidLazry5rCgi9QXFoWu4y/TWYsbxzEqSjhPU0=
+-----END CERTIFICATE-----
diff --git a/tests/data/tls/ca/private/testsuiteCA.key b/tests/data/tls/ca/private/testsuiteCA.key
new file mode 100644
index 0000000..01a6614
--- /dev/null
+++ b/tests/data/tls/ca/private/testsuiteCA.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQC+4P82iWXATkbm
+JOg9gZeSKEsRxiGsKBQxsqNkJGJhJL12e558OlBl+pfzxZ1JzGE6MW8NpNhwV3PI
+xmYG0Fk/JDtWXXAg5FEriF70eIK8VbXVW/blVR86r1mft11ycP62pN1O+dA46BUU
+x0XtXtNM7gI0OjfYdfFJDfaKe4yHOcn78jqWV818GKe7Nd7TxHlXIEgHuWX2vXsB
+XJmKkjV8t+OWHG9MR0LBd9ZiSQ6+AY/J9GRoTLDsEBLQDl9nDuikvd+c+1sEbzwq
+NRtaypi682H0OncovqNj8daUDfugh+Oln1a2pmqQE4AqLq7+r6rj59g7K6NST3Mt
+Eqriowyq+xFAhmjeviubNhmc19deEyHJszRtCVP/oy6S9DOA3npHHEdXaFMq23Nu
+bfpA31UlofyHxIbvbhbs+Eg19ZazVc5WqW7BjOoyhSbqrwySJAXiSRK3B48Glr4T
++uxJ99RJb7nHbHlTOaOJxEqSZrDzDHJtUDxjH/N2Y6iqt/3b75i0W0m2hGbl/GAL
+wfew94RofnFdrPypy/YC/IbTp8NC77r0GidxXSL1U+Gm9KXcMThFC6Ftq5wFLoeM
+MQKZgG0/ZuiK12RPCH4v8B8o/4VXIu5qpwVy+M9dB8ZzI4KFgnZONors6vFTHuB3
+0Uqf3+yHkQpWQLcjGfpgFNDwMk0ROQIDAQABAoICAQCVkIdpnE92V9+GBfVT/G9f
+vuLTkoRf+SeZqXgNx9SuebNbW5HblXXZ8nmOMZIFeXfVuVZjQn+1x1CaSZs4S5ki
+uKkmCyEJJN3VVo3Q0XzfRemsvNrA5+oIec2oMG2wdomfY59leqmFbZTXKy3HyT2Y
+Uga4FcYcfo4JyD8eU6DRdJ6oJC10EGiajFchghyPoqvRcSH/q24R4Ha5om1M/zOZ
+/hz+SlmLU2sjXVtGuCgtCdw5Sp5Ce5VF43JaRGjMwAnazEyjHPE8kEx8ZhCBG66B
+DqP6UrV736T3c0/Hww0fxFrENA4mIE/vhNgwNVQ5jDxDSC9ObesTW93Lu4za+Re6
+pmP1eeS/oe1OcI1d/xK2IIQwzB7ZkJ0StbFLnjs7DATO7BGzhC9egC6s+z9oSgTS
+KvmLyoiL5U4fesVJwcCPKwwkVH9n22TuqmvB5mmvZvRTe2+OgDH55Nkfx1SoI8+Q
+/fwV9UXIIg5en+Kv8lOaWCZujmMsjHC79bwxPLeaePRwD/RBkT1MLW/T4fWGpAt3
+H89+yufH31Y/1QMxVVtR9OdxCtljiXno/bArMNZ0oE1TiCcckMzdjKh7RNfkEXRM
+Pga92HBTgtJ3tfWJ4qOtJ4NKJPQ7wRmR03Bug8+bGM4K5HDO08fNuag/pP3AQvrM
+QGbHFVho3I7/DXnmRBq/gQKCAQEA75eptBtP8PWnN9uNsQoWxvFKQBtbLfPKUcVP
++LWOWF4ag2YRRf6TIzvGfIk54OGSL/srWCDKjXWJ0NgUn6yiqOkoP4oxEE1m2QDY
+7oCk9vJipJcrtNCKL6NhKwZDOjlDSROb/hBeMgr14Da/WkPE6zQhuwN5y4Japbjs
+cBYTao2uOg4QQz5Aee+ee55L6iAgMT0PnlQtv1uVW3D46e02CrQKtRmtDxqT3Nux
+nudJdz+rMFM0EDgVKUYRwFCa6xjI4y2K1aCwCtJG9yTJpYqCD9hehfwEije6dNNg
+p5RX3M9ai710Yx4F26cwX/t8AxqgF/2XBI0ZWD6x69cp7suPTQKCAQEAy/NUEgXN
+nymq8NK+umZwFJU7cy3weozRuEkmgmCWj4XYhbvTw6MbK+2R9XKa3ilqSd2sU2lX
+qE66kfAgqZMJ9RB+7nDOaLAMUuGw1DrwFZE7r3mKXgc4NgjtmGav4E3URXPHj5zb
+JbbN95zl96Fm3Nevs5p8sb0KexgbzHe4UzJNYFgT0l+TjJbJUAiNPsEw1bnV4cxn
+b1HO2CWTeGtAOJyjMRNwI+40wnk2N6An+Ddvb2mj2h30HujSZHnL94RAqa7RHDb6
+lU+7JX/ll5G0mFQOFQAs4UPos2bg7hS1mfYO+UVrG4OH9gXns12158WqFED+lhmJ
+O8WDWEVAblVrnQKCAQAB9aOVrYOB3QB5HHqUMBjvl5mb3J1qSswkzxBQYGvBnUNq
+P7N0dxiM+TguXJD0neOsMMmx9tKxRXzTEHFavPa3mvCRVHgCQh/NNoyPps2yl1jn
+L7VTzUDUEuoAiBSUrVM3jcmA0nFyx1QreUcnXdaGde6wsN6WI4LKSDDm2cde37nF
+D8hiRGgSlzscl7bXO1wICw/No7KcFguqq8ndX+tJOx+7S3J25SjAbauOOSYIq6Si
+yItsdoj1xXTvtbkOoy1BbmXsSVwnOoEKFGrxx6g4qPRc9Cq1Vq9XtULdHAF79NYw
+vmPtS5mQqlVi85OYEuesSo6pot3KMvkRjLjzEwchAoIBACEvrvZfy12iwhX9tNtP
+39z5i3rqdr76OwXpoUKFxPoFpX3dWk/zMnCrb5yo0VplEs6CK5BHC+RvKxykHix5
+qJ0f2geig3O1ccvqvYNLM9XOlA+xjzpNom/odADgdK3i/C9w74AG3gH9BPbNqP3q
+XXqB/i0Tbkbdo97zxVI4CN5AySZsLo2Ez9WIk6laOuGDPhcI7iyXvhz3CtlRA/YM
+PZ74nfVWXGD8WclrP889WEOjgZZ3choD1b1R1SpUR0Q3WO5Da/NTXuL83k7zyMAp
+DWHcC46PQL5G9o56pw8Wf5ZV24nkKdGITY9S1qjxDrBwEYTKLqLt9M6tDPpICnvp
+mmECggEBALfnUgpdGugn46UmQUMI1y+NZbSKhJHG+OBWdcc1j4kDZhF/Ei7g8pvk
+hFU5p/YA6JbGioZxiqjdrYLvgTPnJVkxy7arLTN2j2GVlhUA74BY+kNzENk2Tj9c
+zJSMVZn+WZrXNQhfYyA3FyW3wGN67GBXAHPQxFTdU3G4mR1WcyJCxKIyzP+2M8o9
+16tpb80QRnc0OLm9Izppe7JUp2hCQt+O6E8izvLE8k2ldOr5ncTNWlxTJ0yx0hEO
+WTFqhwOM1pEmtxas1gLr8MX0hNsaQR+kjG2f8rPmH+GEZeeAwuhoJY1PcKAOYM5Y
+yu/1yFXYTrmhD/P0+nJn1DfS5JljCJY=
+-----END PRIVATE KEY-----
diff --git a/tests/data/tls/certs/bjensen@mailgw.example.com.crt b/tests/data/tls/certs/bjensen@mailgw.example.com.crt
new file mode 100644
index 0000000..eb0fc69
--- /dev/null
+++ b/tests/data/tls/certs/bjensen@mailgw.example.com.crt
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFfDCCA2SgAwIBAgIBADANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJVUzEL
+MAkGA1UECAwCQ0ExHDAaBgNVBAoME09wZW5MREFQIEZvdW5kYXRpb24xHDAaBgNV
+BAsME09wZW5MREFQIFRlc3QgU3VpdGUwIBcNMTgxMDMwMTUzNzQwWhgPMjUxOTEx
+MTMxNTM3NDBaMIGbMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExHDAaBgNVBAoM
+E09wZW5MREFQIEZvdW5kYXRpb24xETAPBgNVBAsMCE9wZW5MREFQMSMwIQYDVQQD
+DBpiamVuc2VuQG1haWxndy5leGFtcGxlLmNvbTEpMCcGCSqGSIb3DQEJARYaYmpl
+bnNlbkBtYWlsZ3cuZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
+ggIKAoICAQCcHBkHcUSKG4s7nKmcqZT3EoZkEgxoaMlpxUZtxBtO5ZXEfcpMaxuA
+7qkZvMJR8ws2u8TQU/18FhH4+0aZBefM0ExwqvGNJ8F0cTl3439DGNE+/psh5NWg
+qPYe/K3bAtSRtF7wDxF77eb2Yz0J3NIDxFrAbovfg0ydbt9pWJr5pDBvlqSdYu38
+kpIB5WENCEy77QK9GEGAlMVIRXneA5t2CKsljujRG1H5YJeS6qVAEdMllHZ6a0nN
+LxTdLe1qbZyRgEqRKgW5WcWrW46Co9CRDcFeMqoHdwAQsRdOGBivgkeYUST1yIms
+CbzlSRLC1dfj++2mzCMxoc3xpZNPyHyBuRgou8VqWpF2NuG+KS7QBtm1PVUhSAvR
+X9uQOnXnazQvlRfsaHQjGUKyhMUr5dcwpTqThW4BoqtStd6/097sZTZVWmsC+mzL
+twWkESVDU0tNg/czWLn56smV7DfPjFDDAV6eNcScFfD8w04aPdk8ODalW/wnsTjI
+LQuEBssrV1h8WblruWRU31Mn+mw9SA3tDfTk9sJiEyiTJh3B1DrEb+pIuk4vz5ui
+cNcYTXCfa5ZpPL608f7cWuG2GP8f5ug4PMKyRkh6qCt7BWrVgOheo1ZhjvrbmhI4
+yPXHATrCtYO1wqIyu9Yuirdg7WJD6npu8IV38VEgEBD3UFanY9xN7wIDAQABow0w
+CzAJBgNVHRMEAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQCq8VvpcoAgCK/D5yi/2puB
+LD7kYaVaSXxrUQBeLTmKERw3akpgW7QTGCNgM425VVaBQRPtv8YcX9OycUAylAA+
+7lzwdP95OJGnUOjQY4x4iRAwCPkpDCcnwc43c3WAyQb2S46aZJaWK4S0+RM3CmWH
+1Fzb6aODdnoBEKk0XgNrB6/teB+UWgtTSxWiY/HWiArDaZDPMAxqEK0hnB+b/sBD
+ZoBYnfnQXezylqbk9vkzTIbSVrv5ZZdQELOAnPuxUCFpYew1OGKcg+1twYKDHgBS
+s13zN03eMEnC/O4Z01dhu16vqdikdP+tJJrppjvZtJys0KIP24ltDnpA6h/3m/Cl
+U1eiTDgWO+SsfiL1K4gcTL1eLjnCBFfnHN5gfgAV5w5DaKzvKp7Qu8db4DtH+S4o
+W/MBKuaHHKWUPGksvFUiGNgE/XyDU4MK34/5ulzbrWmqb24pYAzm1MyjsdzmXObw
++fzg6EDBB14cWA2hA7mSqnzkiW1pELVym6+uTaIlopSIFr8nNAimwLiY5QJNGYvd
+hgNNvOyUUO+nON3aHsC/rRMgar3eo7A9AkQJ6qKVvPR2h1317PJLuKaLfjbaCzNw
+iA3JSQjcwR2ydlSgKKN2d/XXm/G4PZ9tUcBY4Zngn0ViT0/m7MFy9qsiWG97+yaZ
+nYsN5WfwDZrtG24dTotxVQ==
+-----END CERTIFICATE-----
diff --git a/tests/data/tls/certs/localhost.crt b/tests/data/tls/certs/localhost.crt
new file mode 100644
index 0000000..3aeae3c
--- /dev/null
+++ b/tests/data/tls/certs/localhost.crt
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFhTCCA22gAwIBAgIBADANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJVUzEL
+MAkGA1UECAwCQ0ExHDAaBgNVBAoME09wZW5MREFQIEZvdW5kYXRpb24xHDAaBgNV
+BAsME09wZW5MREFQIFRlc3QgU3VpdGUwIBcNMTgxMDMwMTUzNjMwWhgPMjUxOTEx
+MTMxNTM2MzBaMGoxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEcMBoGA1UECgwT
+T3BlbkxEQVAgRm91bmRhdGlvbjEcMBoGA1UECwwTT3BlbkxEQVAgVGVzdCBTdWl0
+ZTESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
+CgKCAgEA6Ud89ugah2oWY00q1g+M6NkpluewwvGq4tkMau1gq+Q5Biv61bubgdSA
+Z+Zkkxe3Sx0Zv7i5wldIN4wXqEDlMg2qhfzKDSNKUofc0z7FLMb0Cn46WqlciUCY
+VetHhBghGd+6fxOOz+x98FhiiAif+AdiUWBTKFFohWXo/9aiGgm0ueJj2NS3Eyac
+xOKoTcDd9TMsOJ2fMH2MlquArLobCvuphOrVbqBoeeol2SzFDDOW8ryPDzFGy5xh
+ZHkm/3sGIoDpDkDR0yhvBzn47qdLI5myc6Fj96s7S2xgqiqGXJW0D0FCfpUQXxfm
+ahz/Jdwl+hqs5Eg/aA+LE/7lmS7szo3zwJQ53ApdcaupHi4fU60wPVrdo29wLwDO
+hDuS+Oc1os1UyJt0T0a+zB4PIP2rxifyxI1iWmZFt7tJyLv1k7yMN7CLCWzsSy5P
+BZpGmHV9Wbvb660N6NzlFDMqnjJWDAr1BLoV4ywmpiWPhy/7JtKXFe1V3jT5MvGM
+26IOC+zCwwZVyEIIASeWepZDuto00Lqo7jOKSlLRmuhTX1ELK8xYX6ZU/fz0FwYn
+bLu6bI4mRGfbJ12fWYm5QMje2QAuvndfi759HUeuLl6TgmeQFgqFA/6Kkwoz0Ncb
+Kaaj+ByvLXfI4S3lvkwT26nOAt966fb1bsdkb8P52NdkqeSMk5cCAwEAAaNIMEYw
+CQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwLAYDVR0RBCUwI4IJbG9jYWxob3N0hwR/
+AAABhxAAAAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4ICAQCGQCs10hwY
+t5o3AWjU8oT8HWnLDsEzIvI/Z2dvtsFSOFotH14d8a7CdCKNiry8BbQ82A4sG/Xw
+0aVdP1EscxGhpJuMHG4Ph9PZBm31ZW2VoRHOEs7/Moi6G/1yldVxWUH/qXO00Dw9
+cEsiUQdPrPQDoVBKYAMuV15RP9b3iPpw3GY1EkIu+akGVziHFmFYUoU2gctiGIZ6
+6KiqBFvCP1Yvm3RSZ5t/Kv/jPMetAnCq+9JAUAodAh2+goBvUCAN9Itr/tEs98jq
+9d14J7gzIRDdNHKOLrRFmoMrTaDZNtqBe5jiMf0O55tgjv4BqN4w11M51bjY4umd
+GX+OXoBJG+MK7AZyaHPjHa1NMoLDOUhTvHb4zPNkPiVb8r3lYkQ4VCtre+4qqrEn
+cEt9KWGpHkoz4GSKn6uidQebdi4waexcGttsHbKPaKZqzYXAJ2bjFZnv85zPtpjO
+qxzqrMUruiCU7EfjGAdZ8S0lwjdMihznLATjKuwQkJ2mVg2HbLgxZu578FHTBOHW
+LjVIr/80auF4Ino9ocHpIwL/E4jpYQWP/Uv4KBHwkAktmUOwqyt0iysRaWy4Gp7S
+keBI9FoGtJ1Mq5M2tVINBzt1ESC3t03KqyY+/9r/IeY7A7yukC0YJnJ+HorfuQFf
+0//7DOEA58bRswyWTLOAjYMJHilTKOozSQ==
+-----END CERTIFICATE-----
diff --git a/tests/data/tls/conf/openssl.cnf b/tests/data/tls/conf/openssl.cnf
new file mode 100644
index 0000000..632cff1
--- /dev/null
+++ b/tests/data/tls/conf/openssl.cnf
@@ -0,0 +1,129 @@
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+
+oid_section = new_oids
+
+[ new_oids ]
+tsa_policy1 = 1.2.3.4.1
+tsa_policy2 = 1.2.3.4.5.6
+tsa_policy3 = 1.2.3.4.5.7
+
+[ ca ]
+default_ca = CA_default # The default ca section
+
+[ CA_default ]
+
+dir = ./cruft # Where everything is kept
+certs = $dir/certs # Where the issued certs are kept
+crl_dir = $dir/crl # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+new_certs_dir = $dir/certs # default place for new certs.
+certificate = $dir/cacert.pem # The CA certificate
+serial = $dir/serial # The current serial number
+crlnumber = $dir/crlnumber # the current crl number
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/private/cakey.pem# The private key
+RANDFILE = $dir/private/.rand # private random number file
+x509_extensions = usr_cert # The extentions to add to the cert
+name_opt = ca_default # Subject Name options
+cert_opt = ca_default # Certificate field options
+default_days = 365 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = default # use public key default MD
+preserve = no # keep passed DN ordering
+policy = policy_match
+
+[ policy_match ]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ req ]
+default_bits = @KEY_BITS@
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+string_mask = utf8only
+
+[ req_distinguished_name ]
+basicConstraints=CA:FALSE
+
+[ req_attributes ]
+challengePassword = A challenge password
+challengePassword_min = 4
+challengePassword_max = 20
+
+unstructuredName = An optional company name
+
+[ usr_cert ]
+
+basicConstraints=CA:FALSE
+nsComment = "OpenSSL Generated Certificate"
+
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+
+[ v3_req ]
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectAltName = DNS:localhost,IP:127.0.0.1,IP:::1
+
+[ v3_ca ]
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid:always,issuer
+basicConstraints = CA:true
+
+[ crl_ext ]
+
+authorityKeyIdentifier=keyid:always
+
+[ proxy_cert_ext ]
+basicConstraints=CA:FALSE
+nsComment = "OpenSSL Generated Certificate"
+
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo
+
+[ tsa ]
+
+default_tsa = tsa_config1 # the default TSA section
+
+[ tsa_config1 ]
+
+dir = ./demoCA # TSA root directory
+serial = $dir/tsaserial # The current serial number (mandatory)
+crypto_device = builtin # OpenSSL engine to use for signing
+signer_cert = $dir/tsacert.pem # The TSA signing certificate
+ # (optional)
+certs = $dir/cacert.pem # Certificate chain to include in reply
+ # (optional)
+signer_key = $dir/private/tsakey.pem # The TSA private key (optional)
+
+default_policy = tsa_policy1 # Policy if request did not specify it
+ # (optional)
+other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional)
+digests = md5, sha1 # Acceptable message digests (mandatory)
+accuracy = secs:1, millisecs:500, microsecs:100 # (optional)
+clock_precision_digits = 0 # number of digits after dot. (optional)
+ordering = yes # Is ordering defined for timestamps?
+ # (optional, default: no)
+tsa_name = yes # Must the TSA name be included in the reply?
+ # (optional, default: no)
+ess_cert_id_chain = no # Must the ESS cert id chain be included?
+ # (optional, default: no)
diff --git a/tests/data/tls/create-crt.sh b/tests/data/tls/create-crt.sh
new file mode 100755
index 0000000..739f8ea
--- /dev/null
+++ b/tests/data/tls/create-crt.sh
@@ -0,0 +1,81 @@
+#!/bin/sh
+openssl=$(which openssl)
+
+if [ x"$openssl" = "x" ]; then
+echo "OpenSSL command line binary not found, skipping..."
+fi
+
+KEY_BITS=4096
+KEY_TYPE=rsa:$KEY_BITS
+
+USAGE="$0 [-s] [-u <user@domain.com>]"
+SERVER=0
+USER=0
+EMAIL=
+
+while test $# -gt 0 ; do
+ case "$1" in
+ -s | -server)
+ SERVER=1;
+ shift;;
+ -u | -user)
+ if [ x"$2" = "x" ]; then
+ echo "User cert requires an email address as an argument"
+ exit;
+ fi
+ USER=1;
+ EMAIL="$2";
+ shift; shift;;
+ -)
+ shift;;
+ -*)
+ echo "$USAGE"; exit 1
+ ;;
+ *)
+ break;;
+ esac
+done
+
+if [ $SERVER = 0 -a $USER = 0 ]; then
+ echo "$USAGE";
+ exit 1;
+fi
+
+rm -rf ./openssl.cnf cruft
+mkdir -p private certs cruft/private cruft/certs
+
+echo "00" > cruft/serial
+touch cruft/index.txt
+touch cruft/index.txt.attr
+hn=$(hostname -f)
+sed -e "s;@HOSTNAME@;$hn;" -e "s;@KEY_BITS@;$KEY_BITS;" conf/openssl.cnf > ./openssl.cnf
+
+if [ $SERVER = 1 ]; then
+ rm -rf private/localhost.key certs/localhost.crt
+
+ $openssl req -new -nodes -out localhost.csr -keyout private/localhost.key \
+ -newkey $KEY_TYPE -config ./openssl.cnf \
+ -subj "/CN=localhost/OU=OpenLDAP Test Suite/O=OpenLDAP Foundation/ST=CA/C=US" \
+ -batch > /dev/null 2>&1
+
+ $openssl ca -out certs/localhost.crt -notext -config ./openssl.cnf -days 183000 -in localhost.csr \
+ -keyfile ca/private/testsuiteCA.key -extensions v3_req -cert ca/certs/testsuiteCA.crt \
+ -batch >/dev/null 2>&1
+
+ rm -rf ./openssl.cnf ./localhost.csr cruft
+fi
+
+if [ $USER = 1 ]; then
+ rm -f certs/$EMAIL.crt private/$EMAIL.key $EMAIL.csr
+
+ $openssl req -new -nodes -out $EMAIL.csr -keyout private/$EMAIL.key \
+ -newkey $KEY_TYPE -config ./openssl.cnf \
+ -subj "/emailAddress=$EMAIL/CN=$EMAIL/OU=OpenLDAP/O=OpenLDAP Foundation/ST=CA/C=US" \
+ -batch >/dev/null 2>&1
+
+ $openssl ca -out certs/$EMAIL.crt -notext -config ./openssl.cnf -days 183000 -in $EMAIL.csr \
+ -keyfile ca/private/testsuiteCA.key -extensions req_distinguished_name \
+ -cert ca/certs/testsuiteCA.crt -batch >/dev/null 2>&1
+
+ rm -rf ./openssl.cnf ./$EMAIL.csr cruft
+fi
diff --git a/tests/data/tls/private/bjensen@mailgw.example.com.key b/tests/data/tls/private/bjensen@mailgw.example.com.key
new file mode 100644
index 0000000..e30e115
--- /dev/null
+++ b/tests/data/tls/private/bjensen@mailgw.example.com.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQCcHBkHcUSKG4s7
+nKmcqZT3EoZkEgxoaMlpxUZtxBtO5ZXEfcpMaxuA7qkZvMJR8ws2u8TQU/18FhH4
++0aZBefM0ExwqvGNJ8F0cTl3439DGNE+/psh5NWgqPYe/K3bAtSRtF7wDxF77eb2
+Yz0J3NIDxFrAbovfg0ydbt9pWJr5pDBvlqSdYu38kpIB5WENCEy77QK9GEGAlMVI
+RXneA5t2CKsljujRG1H5YJeS6qVAEdMllHZ6a0nNLxTdLe1qbZyRgEqRKgW5WcWr
+W46Co9CRDcFeMqoHdwAQsRdOGBivgkeYUST1yImsCbzlSRLC1dfj++2mzCMxoc3x
+pZNPyHyBuRgou8VqWpF2NuG+KS7QBtm1PVUhSAvRX9uQOnXnazQvlRfsaHQjGUKy
+hMUr5dcwpTqThW4BoqtStd6/097sZTZVWmsC+mzLtwWkESVDU0tNg/czWLn56smV
+7DfPjFDDAV6eNcScFfD8w04aPdk8ODalW/wnsTjILQuEBssrV1h8WblruWRU31Mn
++mw9SA3tDfTk9sJiEyiTJh3B1DrEb+pIuk4vz5uicNcYTXCfa5ZpPL608f7cWuG2
+GP8f5ug4PMKyRkh6qCt7BWrVgOheo1ZhjvrbmhI4yPXHATrCtYO1wqIyu9Yuirdg
+7WJD6npu8IV38VEgEBD3UFanY9xN7wIDAQABAoICAQCWY/s40EXXRvG7XBGKe1Sn
+MZGGllyduVVQMFzJIkOsnkDKKuTY+dZlP4Zo5Q/PIvWKpRnWGRP6lsh5tJkukiHd
+jk4VvJk4AzS7mNhkRyYy3ZW3ulB5NpsXS67P610RwIhIVhuf6ORPH8GBW9lRxwoL
+1v4WpGjbywHkKQvR0Sp7lVGULuwnM0dSK2G9sdztUTGbWZlp0hRIawojtcrRt2ft
+Liyy4hooWMmAFS3wu1y3fHSNn5kEFpfis5jF+5jdDvvmsFElx/X7uiBUFMAV2vry
+wu2mceibiGjnq7Nn6I7fhgKzGnkgzzDSLA9uVBde2+RAHlO0fLTq+5YLVhe0pNBM
+J1Y0soNaO3XfVV6Vnyz8X+ruHItW2OBF9AYhIlXq/6d3MMX51BEM6odEtsi8zFgo
+ENN0GAXoyoofg+IvzPiVU2Ud7s4pAlK473d7sAQEeiFWaj7iwueAgofSUFRz7E/H
+umdhytKiJXqcjJ9O2k4sBsmQoPIB++LlUPRIlZY9UvTFxLbd/ifFUv5fqa6z0IX6
+wkIzXmRHhG+ETk1IZBJAAho7iyyYOTP+JnnToUAMWoUaZUO2bzaZfQha8Z3KVtG/
+PJUfHClBXqvFNaAUvA9Df3JoJddJ4pO1g0QjS/dp4C2KwNkH4oqMJctvCersoPWu
+5DYiWY6KR4GjokJ1lBeWAQKCAQEAzSKa+m2C4ANNCJB9tcKYDbYIdibCpzO+k1Fb
+gZUtNi9dEE0Po8rMG0jthm+GKJjNjiG5idSUMo+WNEGBPkELueex81AlEpOqQ6/9
+67cyjAsF/FvgkWOpKJnGOySF/TpK4kPGYyS3ICvs1KNE5HEywHyC4C/MD8N9Z5tX
+/DfW6sBM/wPipE9YDpKfAg3fDG9YJN/gJZ8TlZVqzzw75rKGcMeLc8f0mbMo+KWQ
+VKV4vrgz1eiVrHc5VeGUaXe1Yei5El671wAdtFdmm51A2fWd80fPlQdqfAwpX7x4
+FWuo9z2QX70rM/NTWfk4nQ6ZFEHxtm++OiTfh7RwauI8fxye6QKCAQEAwtF/tOth
+UgHrohB2DCE9gA0rxkynJHK9/SXSd0KBjERO2i41iuC9YlJT/NpNz9fM7l+L02aP
+wWLMqyC7moNmIpJMY2xBGU0EowQ/3xsSNo3u/fvOS4MyGLKENUPMFgO0J7yopiqt
+Ea31TcrFSTMSmFZCv8cGt38EwS6sdJZd/RB+h3yxesit8pouwpfbtLPx6LSGkPHY
+5nNVPgbt6xaxZJ/1kNbLFObSoZ3lzWBwp93dQh/WqeeeI51LGdM1G6fTL8HrmGFJ
+EX0AKpexFVnG/GROJc8taWtMbk9W5oK30JqR7hpSaluYbonpr9k4WQA+EAZjXfcJ
+0V0AMsMUhGtvFwKCAQAQZf7LnCuFKt5im+JgwFCVcALXJxwSb7GBZ1SQVFOL7Fdd
+MTvZ1SFh4P+T6qBn6GcuQIXrfcHnFNFmFgJ17o84akwwbiy4gnNu+8epqzhwN4Vf
++hxGoxfntftByRao+pr34YEfddTpznkdOnwMYvwypQF1WHzQmckRmjp7YB9fHsZI
+8I+SoQEiERiC+oblIJWERR1PBJt1Lr+eF2uWcpkKtPjx5X8pNkhFMD8MdTnkzSbf
+p7snUVSVB/ZsQ/SNAiShUk9jzY+SVhZOxFBl3BunUgtHF5OsnPBFxfQ3iia0tQgw
+jxfADGiSXbjn3T3hf7AJ7H7heQchewwtjy5U3v3ZAoIBAQCEAyRPe0SKJoT+X7su
+QwQClmo4SE7mUt5NAOkaKTXRz6PDEpbzkZCjZHhHGcKqeWgDizkbuh7lg0Z/G4Ik
+lK+L86jRolSGiXr/3+xMCXMRBqKQ9qV24+L5e1Y9JcDQlhfo6V06pCZ8mW1lFmcT
+UAlksucuPvZdNzQIl9ECe7YauqeStbsqIXxFrZbMA808KMde0Z1x8H/ywOpdSqLD
+r6/rKL1lNTeN5U+Ldox228fa6Gt62EpE/Y9aQMbYLBeLsvBXJ0e3DQ1PTW3kbr/v
+YNOGyY1u73GtQqkbAqY3MxLNxz/loW6BZanoFYoFv+L/5Dsp7ro8vR6pASUWQLzR
+cl9nAoIBAQCre87G76UXv6FIggT+cKM9MKS69KIE3mzNTYUo90L74vF65hJqlaIa
+mfEcPpEU+UY+ufZSIHtTDBj/9Rswaf5whJY7RfL42pSGnW2YOMpuwDIKAEvcJedu
+kZhbthBin4pa28X6L5sNxug+7Wykgesd48PmMLG4pTF+D9u7SgO37Ew5UzylPWNi
+Lrv9TlX1vv9rNFh/hOCA93DNrJlNNPltIcMDByVVjrq31QmxMJwE7cdvl1V7eoiO
+NQuGuGyFIEKPtl9dEUaA4SGYZ7fUqPZaZuzzM0Xa5UMpdcIzcuYYNn3G6FvV6vwU
+dH+lv5X1bTB18GK88ANpC2qLCKRJPCTx
+-----END PRIVATE KEY-----
diff --git a/tests/data/tls/private/localhost.key b/tests/data/tls/private/localhost.key
new file mode 100644
index 0000000..99cb512
--- /dev/null
+++ b/tests/data/tls/private/localhost.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDpR3z26BqHahZj
+TSrWD4zo2SmW57DC8ari2Qxq7WCr5DkGK/rVu5uB1IBn5mSTF7dLHRm/uLnCV0g3
+jBeoQOUyDaqF/MoNI0pSh9zTPsUsxvQKfjpaqVyJQJhV60eEGCEZ37p/E47P7H3w
+WGKICJ/4B2JRYFMoUWiFZej/1qIaCbS54mPY1LcTJpzE4qhNwN31Myw4nZ8wfYyW
+q4CsuhsK+6mE6tVuoGh56iXZLMUMM5byvI8PMUbLnGFkeSb/ewYigOkOQNHTKG8H
+Ofjup0sjmbJzoWP3qztLbGCqKoZclbQPQUJ+lRBfF+ZqHP8l3CX6GqzkSD9oD4sT
+/uWZLuzOjfPAlDncCl1xq6keLh9TrTA9Wt2jb3AvAM6EO5L45zWizVTIm3RPRr7M
+Hg8g/avGJ/LEjWJaZkW3u0nIu/WTvIw3sIsJbOxLLk8FmkaYdX1Zu9vrrQ3o3OUU
+MyqeMlYMCvUEuhXjLCamJY+HL/sm0pcV7VXeNPky8Yzbog4L7MLDBlXIQggBJ5Z6
+lkO62jTQuqjuM4pKUtGa6FNfUQsrzFhfplT9/PQXBidsu7psjiZEZ9snXZ9ZiblA
+yN7ZAC6+d1+Lvn0dR64uXpOCZ5AWCoUD/oqTCjPQ1xsppqP4HK8td8jhLeW+TBPb
+qc4C33rp9vVux2Rvw/nY12Sp5IyTlwIDAQABAoICADh1+wLvjmwz+xMxvCpvPRWm
+afCCR0AHqeqZye2fYoR4Cm05+837SFoWCrYbB0CqvsxJUNAcb6lf4rS/DYLFojOJ
+JzqiwmyHnBd5lrLyQFrkFHDtuEX1M9ZscfJprbeE944BnmvfWfNtM9YWLlLqc31e
+nCdB/x6FBZ0z2z8Avd87dih/aNc0NNNHxy3IBiA7i/0q04soaz0bRgm5nL0xlhYE
+bzUieWH7JQ5M47g6o76eReyeQqnUrWPeh5v/zraLGiMDvGScv6wx3x2KpHtutjr5
+mj1uVHm/UeyhYIwPGtIR0bDXhLaKcZnyeOw59G8/Z1mvVyUxb1dKW8kNKpj2yI2H
+Y1SjhW5qaOeaDPxAPqVyo6SUQIzOn6SD0l7aGyOyvYULjiw342HQYU4rQeSPOtjt
++NYMirnT7WNnmoSIsXx7nwUe38EWx5gCHy8taF4aZr5K85yZKnmsiX3vX/hH30yc
+GLOnDDa3b0FE2J2eYos14ru8RTqSLSxclr5Ru2yTdwLgE0gg+iygO1/tYYkqxZ09
+j+METJpg4wv+cQUG/BxysISqNjaPSPHdyJeTMzC8B+PUUpbRoBuvLLokkZ9P95nG
+72TFklEOB0m0VMxrEfev0HGSzkQm92s2Bf41TRaHTPSkg+G1s0haZTNqRVTGPrr/
+eyiz0qH2bgDeubJ3VuTBAoIBAQD9N+KeKo+hRWeV/I6BCBOfMeQOqlqIxYfYAxU+
+CuutILbTnGKFMTAx43syh/a5EV7q4yM81RCXKK/Lmja2OIeYJUb88bC/h0x/gq5W
+LLxHbKgFDUDF2VcWShMqDOo8J8FbzWwb9bOOShqASoR6FacJuOqlFvS8gaswZtiW
+fOvlWRKO2ybULgQctX5gOf1ctuab1VrzuHnNB30gVFc95Dg1b6RiyVAa8AFm6gs9
+6Rewk527+4T5Ho5UXvdsTVJsAhzJgVjPSyF2Vc1CRrp8lIffsg5Prb4w8kvB0i64
+09zn+jAfVRpjdGWqMI7BR1pCdheGMqv006ZVYY+QhcBIb0BHAoIBAQDr14d5PPDv
+pCjlJnCKNzX2irU6bdIY+zvXoemj/cYvHqQbPOe/kaCWFNPMxANKMmZSTdSM7qqR
+s0P1RW/R7moWNSesYwW+2Jp2hIhiWmy+E+ksXeTlFwVpuMHSDPS/N61N8XgmT3pI
+Qngl1hgxGbttniKEwI+Nc7Z3FYDDCp206nmC5y33D+ZYHv1L3e33pyqHdHD/uIeU
+57OPr7Mmd/J6pmClh1dqyZwVBClc2V6w0y2G8Lk1v79wOMrn+4/p9KH2BgkFe2gr
+uB8TOLlUhttQ8VfzXCd+Zi9s3oW0h7Vkvt4kDlJm0MrnMmK0aqgKB+7XkKE0ccVQ
+xSodzbBdDYoxAoIBAH2qGmD8JkOWug2JRP9sDrDWhaNxj3SI8x2Uiho8OTG2JoVl
++s621oArsJwnNZ4qrLxM9NPfuVgK7RNR+Qz9iO1MsqodF+Y1MxWkuPgzQ0z+83Nu
+XFLTxZBeOpyHxEcOQ7tXeut1SCK5S+WXFZ+w1zDQAELl3ZcfkuF2aM5mOHuddMRI
+pkBuhcPpnkoK/V3htxhnDbgeOPQzXzmIIbOpauu5+A6+cW6s5UU5qVKUNxl+aK09
+6YPoUiI07v1kch7//WFTO8vEMVsUwcS+bRYecD/nkYqhYt3PoSETOfSnz92gH/ms
+tmfdAAcyCeaJjpWlHY+P3h6mWsnMnP7QIdjQvUkCggEAGFkiBWRDQ5phFndHex2E
+FrXvS972p9mYLgTrSCD1CvxQ2PcKvf5c4+G2lBdQd6KIacrbPMmPFoe5ZmMKzlOc
+5DoMpIF8oF1gZQf9xJmtTFpl4ky3Sud7iZSnffYUdoFbBQb+7oWaDEfAe7eEu9z6
+OrDuw2HV8DaYCedQadJ4warLbLZNSop7r3FTmTeKT90USPO+jsgQR1E8eoMbLceI
+Yx02MSCt57p0wL6zPoC6g+rpclr75A6txvo2CIkyLGczKWEqIUTCVnEl1CgxCgb6
+MXsZJ2jGMwh9sPGwQBkaoxIJgRNxcmfv6rqK8jFos9Bp2ht2aSGty07vsDACGzlA
+oQKCAQEA8PzgkyGYHs2DwNhmv3j5ZFaP0RukwbdChSoxmbC9JP2JJxxYcnww5jYH
+xeM1bahqkdKyG5iDRiYB74EolZUMA3Zny13R4HWxNe4aUZW1H8mdmhllXX90aUOU
+WEvF2yYZbg9CQIq7zQh8HsF/S8sDTsXoZOx30zrPgb44spWKRmxdwUJt944weXvc
+p5XkLvVzBVJ+RD5IgPTBFl1iCkw3eq01CFcbTdfe9cS8V9IgDy0Jq2GvRE3Y2JS6
+xqtBB1MgZvrUoAZ8jPacRRXddg87Hwgs9+R1jaE+ZYixojOFg+JnQOGkUd9FhJAW
+bcnWV4XIPIMbouL4132Ove+GukJlPA==
+-----END PRIVATE KEY-----
diff --git a/tests/data/valsort1.out b/tests/data/valsort1.out
new file mode 100755
index 0000000..c470762
--- /dev/null
+++ b/tests/data/valsort1.out
@@ -0,0 +1,45 @@
+dn: o=valsort
+objectClass: top
+objectClass: organization
+o: valsort
+description: valsort test database
+
+dn: ou=users,o=valsort
+objectClass: top
+objectClass: organizationalUnit
+ou: users
+description: container for test valsort users
+
+dn: uid=george,ou=users,o=valsort
+objectClass: OpenLDAPperson
+uid: george
+sn: alpha
+sn: jungle
+sn: tree
+sn: zib
+cn: george
+businessCategory: test
+carLicense: SAMPLE
+departmentNumber: 1
+departmentNumber: 10
+departmentNumber: 3
+departmentNumber: 37
+departmentNumber: 46
+departmentNumber: 5
+departmentNumber: 72
+displayName: George
+employeeNumber: 5150
+employeeType: anarchist
+employeeType: contractor
+employeeType: staff
+givenName: Big G
+ou: Chemistry
+ou: Computer Science
+ou: Hum Bio
+ou: Academia
+mailPreferenceOption: 1
+mailPreferenceOption: 3
+mailPreferenceOption: 22
+mailPreferenceOption: 66
+mailPreferenceOption: 87
+
diff --git a/tests/data/valsort2.out b/tests/data/valsort2.out
new file mode 100755
index 0000000..c235865
--- /dev/null
+++ b/tests/data/valsort2.out
@@ -0,0 +1,45 @@
+dn: o=valsort
+objectClass: top
+objectClass: organization
+o: valsort
+description: valsort test database
+
+dn: ou=users,o=valsort
+objectClass: top
+objectClass: organizationalUnit
+ou: users
+description: container for test valsort users
+
+dn: uid=george,ou=users,o=valsort
+objectClass: OpenLDAPperson
+uid: george
+sn: zib
+sn: tree
+sn: jungle
+sn: alpha
+cn: george
+businessCategory: test
+carLicense: SAMPLE
+departmentNumber: 72
+departmentNumber: 5
+departmentNumber: 46
+departmentNumber: 37
+departmentNumber: 3
+departmentNumber: 10
+departmentNumber: 1
+displayName: George
+employeeNumber: 5150
+employeeType: staff
+employeeType: contractor
+employeeType: anarchist
+givenName: Big G
+ou: Chemistry
+ou: Computer Science
+ou: Hum Bio
+ou: Academia
+mailPreferenceOption: 87
+mailPreferenceOption: 66
+mailPreferenceOption: 22
+mailPreferenceOption: 3
+mailPreferenceOption: 1
+
diff --git a/tests/data/valsort3.out b/tests/data/valsort3.out
new file mode 100755
index 0000000..caefe1e
--- /dev/null
+++ b/tests/data/valsort3.out
@@ -0,0 +1,61 @@
+dn: o=valsort
+objectClass: top
+objectClass: organization
+o: valsort
+description: valsort test database
+
+dn: ou=users,o=valsort
+objectClass: top
+objectClass: organizationalUnit
+ou: users
+description: container for test valsort users
+
+dn: uid=george,ou=users,o=valsort
+objectClass: OpenLDAPperson
+uid: george
+sn: zib
+sn: tree
+sn: jungle
+sn: alpha
+cn: george
+businessCategory: test
+carLicense: SAMPLE
+departmentNumber: 72
+departmentNumber: 5
+departmentNumber: 46
+departmentNumber: 37
+departmentNumber: 3
+departmentNumber: 10
+departmentNumber: 1
+displayName: George
+employeeNumber: 5150
+employeeType: staff
+employeeType: contractor
+employeeType: anarchist
+givenName: Big G
+ou: Chemistry
+ou: Computer Science
+ou: Hum Bio
+ou: Academia
+mailPreferenceOption: 87
+mailPreferenceOption: 66
+mailPreferenceOption: 22
+mailPreferenceOption: 3
+mailPreferenceOption: 1
+
+dn: uid=dave,ou=users,o=valsort
+objectClass: OpenLDAPperson
+uid: dave
+sn: nothere
+cn: dave
+businessCategory: otest
+carLicense: TEST
+departmentNumber: 42
+displayName: Dave
+employeeNumber: 69
+employeeType: contractor
+givenName: Dave
+ou: Test
+ou: Is
+ou: Okay
+
diff --git a/tests/progs/Makefile.in b/tests/progs/Makefile.in
new file mode 100644
index 0000000..5e7a2a2
--- /dev/null
+++ b/tests/progs/Makefile.in
@@ -0,0 +1,66 @@
+## Makefile.in for test programs
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+PROGRAMS = slapd-tester slapd-search slapd-read slapd-addel slapd-modrdn \
+ slapd-modify slapd-bind slapd-mtread ldif-filter slapd-watcher
+
+SRCS = slapd-common.c \
+ slapd-tester.c slapd-search.c slapd-read.c slapd-addel.c \
+ slapd-modrdn.c slapd-modify.c slapd-bind.c slapd-mtread.c \
+ ldif-filter.c slapd-watcher.c
+
+LDAP_INCDIR= ../../include
+LDAP_LIBDIR= ../../libraries
+
+XLIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLUTIL_A) $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+XXLIBS = $(SECURITY_LIBS) $(LUTIL_LIBS)
+XXXLIBS = $(LTHREAD_LIBS)
+
+OBJS = slapd-common.o
+
+# build-tools: FORCE
+# $(MAKE) $(MFLAGS) load-tools
+
+# load-tools: $(PROGRAMS)
+
+slapd-tester: slapd-tester.o $(OBJS) $(XLIBS)
+ $(LTLINK) -o $@ slapd-tester.o $(OBJS) $(LIBS)
+
+slapd-search: slapd-search.o $(OBJS) $(XLIBS)
+ $(LTLINK) -o $@ slapd-search.o $(OBJS) $(LIBS)
+
+slapd-read: slapd-read.o $(OBJS) $(XLIBS)
+ $(LTLINK) -o $@ slapd-read.o $(OBJS) $(LIBS)
+
+slapd-addel: slapd-addel.o $(OBJS) $(XLIBS)
+ $(LTLINK) -o $@ slapd-addel.o $(OBJS) $(LIBS)
+
+slapd-modrdn: slapd-modrdn.o $(OBJS) $(XLIBS)
+ $(LTLINK) -o $@ slapd-modrdn.o $(OBJS) $(LIBS)
+
+slapd-modify: slapd-modify.o $(OBJS) $(XLIBS)
+ $(LTLINK) -o $@ slapd-modify.o $(OBJS) $(LIBS)
+
+slapd-bind: slapd-bind.o $(OBJS) $(XLIBS)
+ $(LTLINK) -o $@ slapd-bind.o $(OBJS) $(LIBS)
+
+ldif-filter: ldif-filter.o $(OBJS) $(XLIBS)
+ $(LTLINK) -o $@ ldif-filter.o $(OBJS) $(LIBS)
+
+slapd-mtread: slapd-mtread.o $(OBJS) $(XLIBS)
+ $(LTLINK) -o $@ slapd-mtread.o $(OBJS) $(LIBS)
+
+slapd-watcher: slapd-watcher.o $(OBJS) $(XLIBS)
+ $(LTLINK) -o $@ slapd-watcher.o $(OBJS) $(LIBS)
diff --git a/tests/progs/ldif-filter.c b/tests/progs/ldif-filter.c
new file mode 100644
index 0000000..355b716
--- /dev/null
+++ b/tests/progs/ldif-filter.c
@@ -0,0 +1,256 @@
+/* ldif-filter -- clean up LDIF testdata from stdin */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2009-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/ctype.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+#ifdef _WIN32
+#include <fcntl.h>
+#endif
+
+#define DEFAULT_SPECS "ndb=a,null=n"
+
+typedef struct { char *val; size_t len, alloc; } String;
+typedef struct { String *val; size_t len, alloc; } Strings;
+
+/* Flags and corresponding program options */
+enum { SORT_ATTRS = 1, SORT_ENTRIES = 2, NO_OUTPUT = 4, DUMMY_FLAG = 8 };
+static const char spec_options[] = "aen"; /* option index = log2(enum flag) */
+
+static const char *progname = "ldif-filter";
+static const String null_string = { NULL, 0, 0 };
+
+static void
+usage( void )
+{
+ fprintf( stderr, "\
+Usage: %s [-b backend] [-s spec[,spec]...]\n\
+Filter standard input by first <spec> matching '[<backend>]=[a][e][n]':\n\
+ - Remove LDIF comments.\n\
+ - 'a': Sort attributes in entries.\n\
+ - 'e': Sort any entries separated by just one empty line.\n\
+ - 'n': Output nothing.\n\
+<backend> defaults to the $BACKEND environment variable.\n\
+Use specs '%s' if no spec on the command line applies.\n",
+ progname, DEFAULT_SPECS );
+ exit( EXIT_FAILURE );
+}
+
+/* Return flags from "backend=flags" in spec; nonzero if backend found */
+static unsigned
+get_flags( const char *backend, const char *spec )
+{
+ size_t len = strlen( backend );
+ unsigned flags = DUMMY_FLAG;
+ const char *end, *tmp;
+
+ for ( ;; spec = end + ( *end != '\0' )) {
+ if ( !*spec )
+ return 0;
+ end = spec + strcspn( spec, "," );
+ if ( !(tmp = memchr( spec, '=', end-spec )))
+ break;
+ if ( tmp-spec == len && !memcmp( spec, backend, len )) {
+ spec = tmp+1;
+ break;
+ }
+ }
+
+ for ( ; spec < end; spec++ ) {
+ if ( (tmp = strchr( spec_options, *spec )) == NULL ) {
+ usage();
+ }
+ flags |= 1U << (tmp - spec_options);
+ }
+ return flags;
+}
+
+#define APPEND(s /* String or Strings */, data, count, isString) do { \
+ size_t slen = (s)->len, salloc = (s)->alloc, sz = sizeof *(s)->val; \
+ if ( salloc <= slen + (count) ) { \
+ (s)->alloc = salloc += salloc + ((count)|7) + 1; \
+ (s)->val = xrealloc( (s)->val, sz * salloc ); \
+ } \
+ memcpy( (s)->val + slen, data, sz * ((count) + !!(isString)) ); \
+ (s)->len = slen + (count); \
+} while (0)
+
+static void *
+xrealloc( void *ptr, size_t len )
+{
+ if ( (ptr = realloc( ptr, len )) == NULL ) {
+ perror( progname );
+ exit( EXIT_FAILURE );
+ }
+ return ptr;
+}
+
+static int
+cmp( const void *s, const void *t )
+{
+ return strcmp( ((const String *) s)->val, ((const String *) t)->val );
+}
+
+static void
+sort_strings( Strings *ss, size_t offset )
+{
+ qsort( ss->val + offset, ss->len - offset, sizeof(*ss->val), cmp );
+}
+
+/* Build entry ss[n] from attrs ss[n...], and free the attrs */
+static void
+build_entry( Strings *ss, size_t n, unsigned flags, size_t new_len )
+{
+ String *vals = ss->val, *e = &vals[n];
+ size_t end = ss->len;
+ char *ptr;
+
+ if ( flags & SORT_ATTRS ) {
+ sort_strings( ss, n + 1 );
+ }
+ e->val = xrealloc( e->val, e->alloc = new_len + 1 );
+ ptr = e->val + e->len;
+ e->len = new_len;
+ ss->len = ++n;
+ for ( ; n < end; free( vals[n++].val )) {
+ ptr = strcpy( ptr, vals[n].val ) + vals[n].len;
+ }
+ assert( ptr == e->val + new_len );
+}
+
+/* Flush entries to stdout and free them */
+static void
+flush_entries( Strings *ss, const char *sep, unsigned flags )
+{
+ size_t i, end = ss->len;
+ const char *prefix = "";
+
+ if ( flags & SORT_ENTRIES ) {
+ sort_strings( ss, 0 );
+ }
+ for ( i = 0; i < end; i++, prefix = sep ) {
+ if ( printf( "%s%s", prefix, ss->val[i].val ) < 0 ) {
+ perror( progname );
+ exit( EXIT_FAILURE );
+ }
+ free( ss->val[i].val );
+ }
+ ss->len = 0;
+}
+
+static void
+filter_stdin( unsigned flags )
+{
+ char line[256];
+ Strings ss = { NULL, 0, 0 }; /* entries + attrs of partial entry */
+ size_t entries = 0, attrs_totlen = 0, line_len;
+ const char *entry_sep = "\n", *sep = "";
+ int comment = 0, eof = 0, eol, prev_eol = 1; /* flags */
+ String *s;
+
+ /* LDIF = Entries ss[..entries-1] + sep + attrs ss[entries..] + line */
+ for ( ; !eof || ss.len || *sep; prev_eol = eol ) {
+ if ( eof || (eof = !fgets( line, sizeof(line), stdin ))) {
+ strcpy( line, prev_eol ? "" : *sep ? sep : "\n" );
+ }
+ line_len = strlen( line );
+ eol = (line_len == 0 || line[line_len - 1] == '\n');
+
+ if ( *line == ' ' ) { /* continuation line? */
+ prev_eol = 0;
+ } else if ( prev_eol ) { /* start of logical line? */
+ comment = (*line == '#');
+ }
+ if ( comment || (flags & NO_OUTPUT) ) {
+ continue;
+ }
+
+ /* Collect attrs for partial entry in ss[entries...] */
+ if ( !prev_eol && attrs_totlen != 0 ) {
+ goto grow_attr;
+ } else if ( line_len > (*line == '\r' ? 2 : 1) ) {
+ APPEND( &ss, &null_string, 1, 0 ); /* new attr */
+ grow_attr:
+ s = &ss.val[ss.len - 1];
+ APPEND( s, line, line_len, 1 ); /* strcat to attr */
+ attrs_totlen += line_len;
+ continue;
+ }
+
+ /* Empty line - consume sep+attrs or entries+sep */
+ if ( attrs_totlen != 0 ) {
+ entry_sep = sep;
+ if ( entries == 0 )
+ fputs( sep, stdout );
+ build_entry( &ss, entries++, flags, attrs_totlen );
+ attrs_totlen = 0;
+ } else {
+ flush_entries( &ss, entry_sep, flags );
+ fputs( sep, stdout );
+ entries = 0;
+ }
+ sep = "\r\n" + 2 - line_len; /* sep = copy(line) */
+ }
+
+ free( ss.val );
+}
+
+int
+main( int argc, char **argv )
+{
+ const char *backend = getenv( "BACKEND" ), *specs = "", *tmp;
+ unsigned flags;
+ int i;
+
+ if ( argc > 0 ) {
+ progname = (tmp = strrchr( argv[0], '/' )) ? tmp+1 : argv[0];
+ }
+
+ while ( (i = getopt( argc, argv, "b:s:" )) != EOF ) {
+ switch ( i ) {
+ case 'b':
+ backend = optarg;
+ break;
+ case 's':
+ specs = optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+ if ( optind < argc ) {
+ usage();
+ }
+ if ( backend == NULL ) {
+ backend = "";
+ }
+
+#ifdef _WIN32
+ _setmode(1, _O_BINARY); /* don't convert \n to \r\n on stdout */
+#endif
+ flags = get_flags( backend, specs );
+ filter_stdin( flags ? flags : get_flags( backend, DEFAULT_SPECS ));
+ if ( fclose( stdout ) == EOF ) {
+ perror( progname );
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/progs/slapd-addel.c b/tests/progs/slapd-addel.c
new file mode 100644
index 0000000..ca007ce
--- /dev/null
+++ b/tests/progs/slapd-addel.c
@@ -0,0 +1,302 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Kurt Spanier for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "ac/stdlib.h"
+
+#include "ac/ctype.h"
+#include "ac/param.h"
+#include "ac/socket.h"
+#include "ac/string.h"
+#include "ac/unistd.h"
+#include "ac/wait.h"
+
+#include "ldap.h"
+#include "lutil.h"
+#include "ldif.h"
+
+#include "slapd-common.h"
+
+static LDIFRecord *
+get_add_entry( char *filename );
+
+static void
+do_addel( struct tester_conn_args *config,
+ LDIFRecord *record, int friendly );
+
+static void
+usage( char *name, char opt )
+{
+ if ( opt ) {
+ fprintf( stderr, "%s: unable to handle option \'%c\'\n\n",
+ name, opt );
+ }
+
+ fprintf( stderr, "usage: %s " TESTER_COMMON_HELP
+ "-f <addfile> "
+ "[-F]\n",
+ name );
+ exit( EXIT_FAILURE );
+}
+
+int
+main( int argc, char **argv )
+{
+ int i;
+ char *filename = NULL, *buf = NULL;
+ int friendly = 0;
+ struct LDIFFP *fp;
+ LDIFRecord record = {};
+ struct tester_conn_args *config;
+ struct berval bv = {};
+ unsigned long lineno = 0;
+
+ config = tester_init( "slapd-addel", TESTER_ADDEL );
+
+ while ( ( i = getopt( argc, argv, TESTER_COMMON_OPTS "Ff:" ) ) != EOF )
+ {
+ switch ( i ) {
+ case 'F':
+ friendly++;
+ break;
+
+ case 'i':
+ /* ignored (!) by now */
+ break;
+
+ case 'f': /* file with entry search request */
+ filename = optarg;
+ break;
+
+ default:
+ if ( tester_config_opt( config, i, optarg ) == LDAP_SUCCESS ) {
+ break;
+ }
+ usage( argv[0], i );
+ break;
+ }
+ }
+
+ if ( filename == NULL )
+ usage( argv[0], 0 );
+
+ if ( (fp = ldif_open( filename, "r" )) == NULL ) {
+ tester_perror( filename, "while reading ldif file" );
+ exit( EXIT_FAILURE );
+ }
+
+ i = 0;
+ if ( ldif_read_record( fp, &lineno, &buf, &i ) < 0 ) {
+ tester_error( "ldif_read_record failed" );
+ exit( EXIT_FAILURE );
+ }
+ bv.bv_val = buf;
+ bv.bv_len = i;
+
+ if ( ldap_parse_ldif_record( &bv, lineno, &record, "slapd-addel",
+ LDIF_DEFAULT_ADD | LDIF_ENTRIES_ONLY ) ) {
+ tester_error( "ldif_read_record failed" );
+ exit( EXIT_FAILURE );
+ }
+ ldif_close( fp );
+
+ if ( ( record.lr_op != LDAP_REQ_ADD ) || ( !record.lrop_mods ) ) {
+
+ fprintf( stderr, "%s: invalid entry DN in file \"%s\".\n",
+ argv[0], filename );
+ exit( EXIT_FAILURE );
+
+ }
+
+ tester_config_finish( config );
+
+ for ( i = 0; i < config->outerloops; i++ ) {
+ do_addel( config, &record, friendly );
+ }
+
+ free( buf );
+ exit( EXIT_SUCCESS );
+}
+
+
+static void
+addmodifyop( LDAPMod ***pmodsp, int modop, char *attr, char *value, int vlen )
+{
+ LDAPMod **pmods;
+ int i, j;
+ struct berval *bvp;
+
+ pmods = *pmodsp;
+ modop |= LDAP_MOD_BVALUES;
+
+ i = 0;
+ if ( pmods != NULL ) {
+ for ( ; pmods[ i ] != NULL; ++i ) {
+ if ( strcasecmp( pmods[ i ]->mod_type, attr ) == 0 &&
+ pmods[ i ]->mod_op == modop ) {
+ break;
+ }
+ }
+ }
+
+ if ( pmods == NULL || pmods[ i ] == NULL ) {
+ if (( pmods = (LDAPMod **)realloc( pmods, (i + 2) *
+ sizeof( LDAPMod * ))) == NULL ) {
+ tester_perror( "realloc", NULL );
+ exit( EXIT_FAILURE );
+ }
+ *pmodsp = pmods;
+ pmods[ i + 1 ] = NULL;
+ if (( pmods[ i ] = (LDAPMod *)calloc( 1, sizeof( LDAPMod )))
+ == NULL ) {
+ tester_perror( "calloc", NULL );
+ exit( EXIT_FAILURE );
+ }
+ pmods[ i ]->mod_op = modop;
+ if (( pmods[ i ]->mod_type = strdup( attr )) == NULL ) {
+ tester_perror( "strdup", NULL );
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ if ( value != NULL ) {
+ j = 0;
+ if ( pmods[ i ]->mod_bvalues != NULL ) {
+ for ( ; pmods[ i ]->mod_bvalues[ j ] != NULL; ++j ) {
+ ;
+ }
+ }
+ if (( pmods[ i ]->mod_bvalues =
+ (struct berval **)ber_memrealloc( pmods[ i ]->mod_bvalues,
+ (j + 2) * sizeof( struct berval * ))) == NULL ) {
+ tester_perror( "ber_memrealloc", NULL );
+ exit( EXIT_FAILURE );
+ }
+ pmods[ i ]->mod_bvalues[ j + 1 ] = NULL;
+ if (( bvp = (struct berval *)ber_memalloc( sizeof( struct berval )))
+ == NULL ) {
+ tester_perror( "ber_memalloc", NULL );
+ exit( EXIT_FAILURE );
+ }
+ pmods[ i ]->mod_bvalues[ j ] = bvp;
+
+ bvp->bv_len = vlen;
+ if (( bvp->bv_val = (char *)malloc( vlen + 1 )) == NULL ) {
+ tester_perror( "malloc", NULL );
+ exit( EXIT_FAILURE );
+ }
+ AC_MEMCPY( bvp->bv_val, value, vlen );
+ bvp->bv_val[ vlen ] = '\0';
+ }
+}
+
+
+static void
+do_addel(
+ struct tester_conn_args *config,
+ LDIFRecord *record,
+ int friendly )
+{
+ LDAP *ld = NULL;
+ int i = 0, do_retry = config->retries;
+ int rc = LDAP_SUCCESS;
+
+retry:;
+ if ( ld == NULL ) {
+ tester_init_ld( &ld, config, 0 );
+ }
+
+ if ( do_retry == config->retries ) {
+ fprintf( stderr, "PID=%ld - Add/Delete(%d): entry=\"%s\".\n",
+ (long) pid, config->loops, record->lr_dn.bv_val );
+ }
+
+ for ( ; i < config->loops; i++ ) {
+
+ /* add the entry */
+ rc = ldap_add_ext_s( ld, record->lr_dn.bv_val, record->lrop_mods, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_add_ext_s", NULL );
+ switch ( rc ) {
+ case LDAP_ALREADY_EXISTS:
+ /* NOTE: this likely means
+ * the delete failed
+ * during the previous round... */
+ if ( !friendly ) {
+ goto done;
+ }
+ break;
+
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry > 0 ) {
+ do_retry--;
+ goto retry;
+ }
+ /* fall thru */
+
+ default:
+ goto done;
+ }
+ }
+
+#if 0
+ /* wait a second for the add to really complete */
+ /* This masks some race conditions though. */
+ sleep( 1 );
+#endif
+
+ /* now delete the entry again */
+ rc = ldap_delete_ext_s( ld, record->lr_dn.bv_val, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_delete_ext_s", NULL );
+ switch ( rc ) {
+ case LDAP_NO_SUCH_OBJECT:
+ /* NOTE: this likely means
+ * the add failed
+ * during the previous round... */
+ if ( !friendly ) {
+ goto done;
+ }
+ break;
+
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry > 0 ) {
+ do_retry--;
+ goto retry;
+ }
+ /* fall thru */
+
+ default:
+ goto done;
+ }
+ }
+ }
+
+done:;
+ fprintf( stderr, " PID=%ld - Add/Delete done (%d).\n", (long) pid, rc );
+
+ ldap_unbind_ext( ld, NULL, NULL );
+}
+
+
diff --git a/tests/progs/slapd-auth.c b/tests/progs/slapd-auth.c
new file mode 100644
index 0000000..dcb4690
--- /dev/null
+++ b/tests/progs/slapd-auth.c
@@ -0,0 +1,335 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2006-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/param.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+#include <ac/wait.h>
+#include <ac/time.h>
+#include <ac/signal.h>
+
+#include <ldap.h>
+#include <ldap_pvt_thread.h>
+#include <lutil.h>
+
+static int
+do_time( );
+
+/* This program is a simplified version of SLAMD's WeightedAuthRate jobclass.
+ * It doesn't offer as much configurability, but it's a good starting point.
+ * When run without the -R option it will behave as a Standard AuthRate job.
+ * Eventually this will grow into a set of C-based load generators for the SLAMD
+ * framework. This code is anywhere from 2 to 10 times more efficient than the
+ * original Java code, allowing servers to be fully loaded without requiring
+ * anywhere near as much load-generation hardware.
+ */
+static void
+usage( char *name )
+{
+ fprintf( stderr, "usage: %s -H <uri> -b <baseDN> -w <passwd> -t <seconds> -r lo:hi\n\t"
+ "[-R %:lo:hi] [-f <filter-template>] [-n <threads>] [-D <bindDN>] [-i <seconds>]\n",
+ name );
+ exit( EXIT_FAILURE );
+}
+
+static char *filter = "(uid=user.%d)";
+
+static char hname[1024];
+static char *uri = "ldap:///";
+static char *base;
+static char *pass;
+static char *binder;
+
+static int tdur, r1per, r1lo, r1hi, r2per, r2lo, r2hi;
+static int threads = 1;
+
+static int interval = 30;
+
+static volatile int *r1binds, *r2binds;
+static int *r1old, *r2old;
+static volatile int finish;
+
+int
+main( int argc, char **argv )
+{
+ int i;
+
+ while ( (i = getopt( argc, argv, "b:D:H:w:f:n:i:t:r:R:" )) != EOF ) {
+ switch( i ) {
+ case 'b': /* base DN of a tree of user DNs */
+ base = optarg;
+ break;
+
+ case 'D':
+ binder = optarg;
+ break;
+
+ case 'H': /* the server uri */
+ uri = optarg;
+ break;
+
+ case 'w':
+ pass = strdup( optarg );
+ break;
+
+ case 't': /* the duration to run */
+ if ( lutil_atoi( &tdur, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'i': /* the time interval */
+ if ( lutil_atoi( &interval, optarg ) != 0 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'r': /* the uid range */
+ if ( sscanf(optarg, "%d:%d", &r1lo, &r1hi) != 2 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'R': /* percentage:2nd uid range */
+ if ( sscanf(optarg, "%d:%d:%d", &r2per, &r2lo, &r2hi) != 3 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ case 'f':
+ filter = optarg;
+ break;
+
+ case 'n':
+ if ( lutil_atoi( &threads, optarg ) != 0 || threads < 1 ) {
+ usage( argv[0] );
+ }
+ break;
+
+ default:
+ usage( argv[0] );
+ break;
+ }
+ }
+
+ if ( tdur == 0 || r1hi <= r1lo )
+ usage( argv[0] );
+
+ r1per = 100 - r2per;
+ if ( r1per < 1 )
+ usage( argv[0] );
+
+ r1binds = calloc( threads*4, sizeof( int ));
+ r2binds = r1binds + threads;
+ r1old = (int *)r2binds + threads;
+ r2old = r1old + threads;
+
+ do_time( );
+
+ exit( EXIT_SUCCESS );
+}
+
+static void *
+my_task( void *my_num )
+{
+ LDAP *ld = NULL, *sld = NULL;
+ ber_int_t msgid;
+ LDAPMessage *res, *msg;
+ char *attrs[] = { "1.1", NULL };
+ int rc = LDAP_SUCCESS;
+ int tid = *(int *)my_num;
+
+ ldap_initialize( &ld, uri );
+ if ( ld == NULL ) {
+ perror( "ldap_initialize" );
+ return NULL;
+ }
+
+ {
+ int version = LDAP_VERSION3;
+ (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION,
+ &version );
+ }
+ (void) ldap_set_option( ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF );
+
+ ldap_initialize( &sld, uri );
+ if ( sld == NULL ) {
+ perror( "ldap_initialize" );
+ return NULL;
+ }
+
+ {
+ int version = LDAP_VERSION3;
+ (void) ldap_set_option( sld, LDAP_OPT_PROTOCOL_VERSION,
+ &version );
+ }
+ (void) ldap_set_option( sld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF );
+ if ( binder ) {
+ rc = ldap_bind_s( sld, binder, pass, LDAP_AUTH_SIMPLE );
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_perror( sld, "ldap_bind" );
+ }
+ }
+
+ r1binds[tid] = 0;
+
+ for (;;) {
+ char dn[BUFSIZ], *ptr, fstr[256];
+ int j, isr1;
+
+ if ( finish )
+ break;
+
+ j = rand() % 100;
+ if ( j < r1per ) {
+ j = rand() % r1hi;
+ isr1 = 1;
+ } else {
+ j = rand() % (r2hi - r2lo + 1 );
+ j += r2lo;
+ isr1 = 0;
+ }
+ sprintf(fstr, filter, j);
+
+ rc = ldap_search_ext( sld, base, LDAP_SCOPE_SUB,
+ fstr, attrs, 0, NULL, NULL, 0, 0, &msgid );
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_perror( sld, "ldap_search_ex" );
+ return NULL;
+ }
+
+ while (( rc=ldap_result( sld, LDAP_RES_ANY, LDAP_MSG_ONE, NULL, &res )) >0){
+ BerElement *ber;
+ struct berval bv;
+ char *ptr;
+ int done = 0;
+
+ for (msg = ldap_first_message( sld, res ); msg;
+ msg = ldap_next_message( sld, msg )) {
+ switch ( ldap_msgtype( msg )) {
+ case LDAP_RES_SEARCH_ENTRY:
+ rc = ldap_get_dn_ber( sld, msg, &ber, &bv );
+ strcpy(dn, bv.bv_val );
+ ber_free( ber, 0 );
+ break;
+ case LDAP_RES_SEARCH_RESULT:
+ done = 1;
+ break;
+ }
+ if ( done )
+ break;
+ }
+ ldap_msgfree( res );
+ if ( done ) break;
+ }
+
+ rc = ldap_bind_s( ld, dn, pass, LDAP_AUTH_SIMPLE );
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_perror( ld, "ldap_bind" );
+ }
+ if ( isr1 )
+ r1binds[tid]++;
+ else
+ r2binds[tid]++;
+ }
+
+ ldap_unbind( sld );
+ ldap_unbind( ld );
+
+ return NULL;
+}
+
+static int
+do_time( )
+{
+ struct timeval tv;
+ time_t now, prevt, start;
+
+ int r1new, r2new;
+ int dt, dr1, dr2, rr1, rr2;
+ int dr10, dr20;
+ int i;
+
+ gethostname(hname, sizeof(hname));
+ printf("%s(tid)\tdeltaT\tauth1\tauth2\trate1\trate2\tRate1+2\n", hname);
+ srand(getpid());
+
+ prevt = start = time(0L);
+
+ for ( i = 0; i<threads; i++ ) {
+ ldap_pvt_thread_t thr;
+ r1binds[i] = i;
+ ldap_pvt_thread_create( &thr, 1, my_task, (void *)&r1binds[i] );
+ }
+
+ for (;;) {
+ tv.tv_sec = interval;
+ tv.tv_usec = 0;
+
+ select(0, NULL, NULL, NULL, &tv);
+
+ now = time(0L);
+
+ dt = now - prevt;
+ prevt = now;
+
+ dr10 = 0;
+ dr20 = 0;
+
+ for ( i = 0; i < threads; i++ ) {
+ r1new = r1binds[i];
+ r2new = r2binds[i];
+
+ dr1 = r1new - r1old[i];
+ dr2 = r2new - r2old[i];
+ rr1 = dr1 / dt;
+ rr2 = dr2 / dt;
+
+ printf("%s(%d)\t%d\t%d\t%d\t%d\t%d\t%d\n",
+ hname, i, dt, dr1, dr2, rr1, rr2, rr1 + rr2);
+
+ dr10 += dr1;
+ dr20 += dr2;
+
+ r1old[i] = r1new;
+ r2old[i] = r2new;
+ }
+ if ( i > 1 ) {
+ rr1 = dr10 / dt;
+ rr2 = dr20 / dt;
+
+ printf("%s(sum)\t%d\t%d\t%d\t%d\t%d\t%d\n",
+ hname, 0, dr10, dr20, rr1, rr2, rr1 + rr2);
+ }
+
+ if ( now - start >= tdur ) {
+ finish = 1;
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/tests/progs/slapd-bind.c b/tests/progs/slapd-bind.c
new file mode 100644
index 0000000..dad0dcb
--- /dev/null
+++ b/tests/progs/slapd-bind.c
@@ -0,0 +1,551 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "ac/stdlib.h"
+#include "ac/time.h"
+
+#include "ac/ctype.h"
+#include "ac/param.h"
+#include "ac/socket.h"
+#include "ac/string.h"
+#include "ac/unistd.h"
+#include "ac/wait.h"
+#include "ac/time.h"
+
+#include "ldap.h"
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "lber_pvt.h"
+#include "ldap_pvt.h"
+
+#include "slapd-common.h"
+
+static int
+do_bind( struct tester_conn_args *config, char *dn, int maxloop, int force,
+ int noinit, LDAP **ldp, struct berval *pass, int action_type, void *action );
+
+static int
+do_base( struct tester_conn_args *config, char *dn, char *base, char *filter, char *pwattr,
+ int force, int noinit, int action_type, void *action );
+
+/* This program can be invoked two ways: if -D is used to specify a Bind DN,
+ * that DN will be used repeatedly for all of the Binds. If instead -b is used
+ * to specify a base DN, a search will be done for all "person" objects under
+ * that base DN. Then DNs from this list will be randomly selected for each
+ * Bind request. All of the users must have identical passwords. Also it is
+ * assumed that the users are all onelevel children of the base.
+ */
+static void
+usage( char *name, char opt )
+{
+ if ( opt ) {
+ fprintf( stderr, "%s: unable to handle option \'%c\'\n\n",
+ name, opt );
+ }
+
+ fprintf( stderr, "usage: %s " TESTER_COMMON_HELP
+ "[-b <baseDN> [-f <searchfilter>] [-a pwattr]] "
+ "[-B <extra>[,...]] "
+ "[-F] "
+ "[-I]\n",
+ name );
+ exit( EXIT_FAILURE );
+}
+
+int
+main( int argc, char **argv )
+{
+ int i;
+ char *base = NULL;
+ char *filter = "(objectClass=person)";
+ char *pwattr = NULL;
+ int force = 0;
+ int noinit = 1;
+ struct tester_conn_args *config;
+
+ /* extra action to do after bind... */
+ struct berval type[] = {
+ BER_BVC( "tester=" ),
+ BER_BVC( "add=" ),
+ BER_BVC( "bind=" ),
+ BER_BVC( "modify=" ),
+ BER_BVC( "modrdn=" ),
+ BER_BVC( "read=" ),
+ BER_BVC( "search=" ),
+ BER_BVNULL
+ };
+
+ LDAPURLDesc *extra_ludp = NULL;
+
+ config = tester_init( "slapd-bind", TESTER_BIND );
+
+ /* by default, tolerate invalid credentials */
+ tester_ignore_str2errlist( "*INVALID_CREDENTIALS" );
+
+ while ( ( i = getopt( argc, argv, TESTER_COMMON_OPTS "a:B:b:Ff:I" ) ) != EOF )
+ {
+ switch ( i ) {
+ case 'a':
+ pwattr = optarg;
+ break;
+
+ case 'b': /* base DN of a tree of user DNs */
+ base = optarg;
+ break;
+
+ case 'B':
+ {
+ int c;
+
+ for ( c = 0; type[c].bv_val; c++ ) {
+ if ( strncasecmp( optarg, type[c].bv_val, type[c].bv_len ) == 0 )
+ {
+ break;
+ }
+ }
+
+ if ( type[c].bv_val == NULL ) {
+ usage( argv[0], 'B' );
+ }
+
+ switch ( c ) {
+ case TESTER_TESTER:
+ case TESTER_BIND:
+ /* invalid */
+ usage( argv[0], 'B' );
+
+ case TESTER_SEARCH:
+ {
+ if ( ldap_url_parse( &optarg[type[c].bv_len], &extra_ludp ) != LDAP_URL_SUCCESS )
+ {
+ usage( argv[0], 'B' );
+ }
+ } break;
+
+ case TESTER_ADDEL:
+ case TESTER_MODIFY:
+ case TESTER_MODRDN:
+ case TESTER_READ:
+ /* nothing to do */
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+ } break;
+
+ case 'f':
+ filter = optarg;
+ break;
+
+ case 'F':
+ force++;
+ break;
+
+ case 'I':
+ /* reuse connection */
+ noinit = 0;
+ break;
+
+ default:
+ if ( tester_config_opt( config, i, optarg ) == LDAP_SUCCESS ) {
+ break;
+ }
+ usage( argv[0], i );
+ break;
+ }
+ }
+
+ tester_config_finish( config );
+
+ for ( i = 0; i < config->outerloops; i++ ) {
+ int rc;
+
+ if ( base != NULL ) {
+ rc = do_base( config, config->binddn, base,
+ filter, pwattr, force, noinit, -1, NULL );
+ } else {
+ rc = do_bind( config, config->binddn,
+ config->loops, force, noinit, NULL, &config->pass, -1, NULL );
+ }
+ if ( rc == LDAP_SERVER_DOWN )
+ break;
+ }
+
+ exit( EXIT_SUCCESS );
+}
+
+
+static int
+do_bind( struct tester_conn_args *config, char *dn, int maxloop, int force,
+ int noinit, LDAP **ldp, struct berval *pass, int action_type, void *action )
+{
+ LDAP *ld = ldp ? *ldp : NULL;
+ char *bindfunc = "ldap_sasl_bind_s";
+ int i, rc = -1;
+
+ /* for internal search */
+ int timelimit = 0;
+ int sizelimit = 0;
+
+ switch ( action_type ) {
+ case -1:
+ break;
+
+ case TESTER_SEARCH:
+ {
+ LDAPURLDesc *ludp = (LDAPURLDesc *)action;
+
+ assert( action != NULL );
+
+ if ( ludp->lud_exts != NULL ) {
+ for ( i = 0; ludp->lud_exts[ i ] != NULL; i++ ) {
+ char *ext = ludp->lud_exts[ i ];
+ int crit = 0;
+
+ if (ext[0] == '!') {
+ crit++;
+ ext++;
+ }
+
+ if ( strncasecmp( ext, "x-timelimit=", STRLENOF( "x-timelimit=" ) ) == 0 ) {
+ if ( lutil_atoi( &timelimit, &ext[ STRLENOF( "x-timelimit=" ) ] ) && crit ) {
+ tester_error( "unable to parse critical extension x-timelimit" );
+ }
+
+ } else if ( strncasecmp( ext, "x-sizelimit=", STRLENOF( "x-sizelimit=" ) ) == 0 ) {
+ if ( lutil_atoi( &sizelimit, &ext[ STRLENOF( "x-sizelimit=" ) ] ) && crit ) {
+ tester_error( "unable to parse critical extension x-sizelimit" );
+ }
+
+ } else if ( crit ) {
+ tester_error( "unknown critical extension" );
+ }
+ }
+ }
+ } break;
+
+ default:
+ /* nothing to do yet */
+ break;
+ }
+
+ if ( maxloop > 1 ) {
+ fprintf( stderr, "PID=%ld - Bind(%d): dn=\"%s\".\n",
+ (long) pid, maxloop, dn );
+ }
+
+ for ( i = 0; i < maxloop; i++ ) {
+ if ( !noinit || ld == NULL ) {
+ tester_init_ld( &ld, config, TESTER_INIT_ONLY );
+
+#ifdef HAVE_CYRUS_SASL
+ if ( config->secprops != NULL ) {
+ rc = ldap_set_option( ld,
+ LDAP_OPT_X_SASL_SECPROPS, config->secprops );
+
+ if( rc != LDAP_OPT_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_set_option(SECPROPS)", NULL );
+ exit( EXIT_FAILURE );
+ }
+ }
+#endif
+ }
+
+ if ( config->authmethod == LDAP_AUTH_SASL ) {
+#ifdef HAVE_CYRUS_SASL
+ bindfunc = "ldap_sasl_interactive_bind_s";
+ rc = ldap_sasl_interactive_bind_s( ld,
+ dn,
+ config->mech,
+ NULL, NULL,
+ LDAP_SASL_QUIET,
+ lutil_sasl_interact,
+ config->defaults );
+#else /* HAVE_CYRUS_SASL */
+ /* caller shouldn't have allowed this */
+ assert(0);
+#endif
+ } else if ( config->authmethod == LDAP_AUTH_SIMPLE ) {
+ bindfunc = "ldap_sasl_bind_s";
+ rc = ldap_sasl_bind_s( ld,
+ dn, LDAP_SASL_SIMPLE,
+ pass, NULL, NULL, NULL );
+ }
+
+ if ( rc ) {
+ int first = tester_ignore_err( rc );
+
+ /* if ignore.. */
+ if ( first ) {
+ /* only log if first occurrence */
+ if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
+ tester_ldap_error( ld, bindfunc, NULL );
+ }
+ rc = LDAP_SUCCESS;
+
+ } else {
+ tester_ldap_error( ld, bindfunc, NULL );
+ }
+ }
+
+ switch ( action_type ) {
+ case -1:
+ break;
+
+ case TESTER_SEARCH:
+ {
+ LDAPURLDesc *ludp = (LDAPURLDesc *)action;
+ LDAPMessage *res = NULL;
+ struct timeval tv = { 0 }, *tvp = NULL;
+
+ if ( timelimit ) {
+ tv.tv_sec = timelimit;
+ tvp = &tv;
+ }
+
+ assert( action != NULL );
+
+ rc = ldap_search_ext_s( ld,
+ ludp->lud_dn, ludp->lud_scope,
+ ludp->lud_filter, ludp->lud_attrs, 0,
+ NULL, NULL, tvp, sizelimit, &res );
+ ldap_msgfree( res );
+ } break;
+
+ default:
+ /* nothing to do yet */
+ break;
+ }
+
+ if ( !noinit ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ ld = NULL;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ break;
+ }
+ }
+
+ if ( maxloop > 1 ) {
+ fprintf( stderr, " PID=%ld - Bind done (%d).\n", (long) pid, rc );
+ }
+
+ if ( ldp && noinit ) {
+ *ldp = ld;
+
+ } else if ( ld != NULL ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ }
+
+ return rc;
+}
+
+
+static int
+do_base( struct tester_conn_args *config, char *dn, char *base, char *filter, char *pwattr,
+ int force, int noinit, int action_type, void *action )
+{
+ LDAP *ld = NULL;
+ int i = 0;
+ int rc = LDAP_SUCCESS;
+ ber_int_t msgid;
+ LDAPMessage *res, *msg;
+ char **dns = NULL;
+ struct berval *creds = NULL;
+ char *attrs[] = { LDAP_NO_ATTRS, NULL };
+ int ndns = 0;
+#ifdef _WIN32
+ DWORD beg, end;
+#else
+ struct timeval beg, end;
+#endif
+ char *nullstr = "";
+
+ tester_init_ld( &ld, config, 0 );
+
+ fprintf( stderr, "PID=%ld - Bind(%d): base=\"%s\", filter=\"%s\" attr=\"%s\".\n",
+ (long) pid, config->loops, base, filter, pwattr );
+
+ if ( pwattr != NULL ) {
+ attrs[ 0 ] = pwattr;
+ }
+ rc = ldap_search_ext( ld, base, LDAP_SCOPE_SUBTREE,
+ filter, attrs, 0, NULL, NULL, 0, 0, &msgid );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_search_ext", NULL );
+ exit( EXIT_FAILURE );
+ }
+
+ while ( ( rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ONE, NULL, &res ) ) > 0 )
+ {
+ BerElement *ber;
+ struct berval bv;
+ int done = 0;
+
+ for ( msg = ldap_first_message( ld, res ); msg;
+ msg = ldap_next_message( ld, msg ) )
+ {
+ switch ( ldap_msgtype( msg ) ) {
+ case LDAP_RES_SEARCH_ENTRY:
+ rc = ldap_get_dn_ber( ld, msg, &ber, &bv );
+ dns = realloc( dns, (ndns + 1)*sizeof(char *) );
+ if ( !dns ) {
+ tester_error( "realloc failed" );
+ exit( EXIT_FAILURE );
+ }
+ dns[ndns] = ber_strdup( bv.bv_val );
+ if ( pwattr != NULL ) {
+ struct berval **values = ldap_get_values_len( ld, msg, pwattr );
+
+ creds = realloc( creds, (ndns + 1)*sizeof(struct berval) );
+ if ( !creds ) {
+ tester_error( "realloc failed" );
+ exit( EXIT_FAILURE );
+ }
+ if ( values == NULL ) {
+novals:;
+ creds[ndns].bv_len = 0;
+ creds[ndns].bv_val = nullstr;
+
+ } else {
+ static struct berval cleartext = BER_BVC( "{CLEARTEXT} " );
+ struct berval value = *values[ 0 ];
+
+ if ( value.bv_val[ 0 ] == '{' ) {
+ char *end = ber_bvchr( &value, '}' );
+
+ if ( end ) {
+ if ( ber_bvcmp( &value, &cleartext ) == 0 ) {
+ value.bv_val += cleartext.bv_len;
+ value.bv_len -= cleartext.bv_len;
+
+ } else {
+ ldap_value_free_len( values );
+ goto novals;
+ }
+ }
+
+ }
+
+ ber_dupbv( &creds[ndns], &value );
+ ldap_value_free_len( values );
+ }
+ }
+ ndns++;
+ ber_free( ber, 0 );
+ break;
+
+ case LDAP_RES_SEARCH_RESULT:
+ done = 1;
+ break;
+ }
+ if ( done )
+ break;
+ }
+ ldap_msgfree( res );
+ if ( done ) break;
+ }
+
+#ifdef _WIN32
+ beg = GetTickCount();
+#else
+ gettimeofday( &beg, NULL );
+#endif
+
+ if ( ndns == 0 ) {
+ tester_error( "No DNs" );
+ if ( ld != NULL ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ }
+ return 1;
+ }
+
+ fprintf( stderr, " PID=%ld - Bind base=\"%s\" filter=\"%s\" got %d values.\n",
+ (long) pid, base, filter, ndns );
+
+ /* Ok, got list of DNs, now start binding to each */
+ for ( i = 0; i < config->loops; i++ ) {
+ struct berval *pass = &config->pass;
+ int j;
+
+#if 0 /* use high-order bits for better randomness (Numerical Recipes in "C") */
+ j = rand() % ndns;
+#endif
+ j = ((double)ndns)*rand()/(RAND_MAX + 1.0);
+
+ if ( creds && !BER_BVISEMPTY( &creds[j] ) ) {
+ pass = &creds[j];
+ }
+
+ if ( do_bind( config, dns[j], 1, force, noinit, &ld, pass,
+ action_type, action ) && !force )
+ {
+ break;
+ }
+ }
+
+ if ( ld != NULL ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ ld = NULL;
+ }
+
+#ifdef _WIN32
+ end = GetTickCount();
+ end -= beg;
+
+ fprintf( stderr, " PID=%ld - Bind done %d in %d.%03d seconds.\n",
+ (long) pid, i, end / 1000, end % 1000 );
+#else
+ gettimeofday( &end, NULL );
+ end.tv_usec -= beg.tv_usec;
+ if (end.tv_usec < 0 ) {
+ end.tv_usec += 1000000;
+ end.tv_sec -= 1;
+ }
+ end.tv_sec -= beg.tv_sec;
+
+ fprintf( stderr, " PID=%ld - Bind done %d in %ld.%06ld seconds.\n",
+ (long) pid, i, (long) end.tv_sec, (long) end.tv_usec );
+#endif
+
+ if ( dns ) {
+ for ( i = 0; i < ndns; i++ ) {
+ ber_memfree( dns[i] );
+ }
+ free( dns );
+ }
+
+ if ( creds ) {
+ for ( i = 0; i < ndns; i++ ) {
+ if ( creds[i].bv_val != nullstr ) {
+ ber_memfree( creds[i].bv_val );
+ }
+ }
+ free( creds );
+ }
+
+ return 0;
+}
diff --git a/tests/progs/slapd-common.c b/tests/progs/slapd-common.c
new file mode 100644
index 0000000..d9f509e
--- /dev/null
+++ b/tests/progs/slapd-common.c
@@ -0,0 +1,550 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "ac/stdlib.h"
+#include "ac/unistd.h"
+#include "ac/string.h"
+#include "ac/errno.h"
+
+#include "ldap.h"
+
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "ldap_pvt.h"
+#include "slapd-common.h"
+
+/* global vars */
+pid_t pid;
+int debug;
+
+/* static vars */
+static char progname[ BUFSIZ ];
+tester_t progtype;
+
+/*
+ * ignore_count[] is indexed by result code:
+ * negative for OpenLDAP client-side errors, positive for protocol codes.
+ */
+#define TESTER_CLIENT_FIRST LDAP_REFERRAL_LIMIT_EXCEEDED /* negative */
+#define TESTER_SERVER_LAST LDAP_OTHER
+static int ignore_base [ -TESTER_CLIENT_FIRST + TESTER_SERVER_LAST + 1 ];
+#define ignore_count (ignore_base - TESTER_CLIENT_FIRST)
+
+static const struct {
+ const char *name;
+ int err;
+} ignore_str2err[] = {
+ { "OPERATIONS_ERROR", LDAP_OPERATIONS_ERROR },
+ { "PROTOCOL_ERROR", LDAP_PROTOCOL_ERROR },
+ { "TIMELIMIT_EXCEEDED", LDAP_TIMELIMIT_EXCEEDED },
+ { "SIZELIMIT_EXCEEDED", LDAP_SIZELIMIT_EXCEEDED },
+ { "COMPARE_FALSE", LDAP_COMPARE_FALSE },
+ { "COMPARE_TRUE", LDAP_COMPARE_TRUE },
+ { "AUTH_METHOD_NOT_SUPPORTED", LDAP_AUTH_METHOD_NOT_SUPPORTED },
+ { "STRONG_AUTH_NOT_SUPPORTED", LDAP_STRONG_AUTH_NOT_SUPPORTED },
+ { "STRONG_AUTH_REQUIRED", LDAP_STRONG_AUTH_REQUIRED },
+ { "STRONGER_AUTH_REQUIRED", LDAP_STRONGER_AUTH_REQUIRED },
+ { "PARTIAL_RESULTS", LDAP_PARTIAL_RESULTS },
+
+ { "REFERRAL", LDAP_REFERRAL },
+ { "ADMINLIMIT_EXCEEDED", LDAP_ADMINLIMIT_EXCEEDED },
+ { "UNAVAILABLE_CRITICAL_EXTENSION", LDAP_UNAVAILABLE_CRITICAL_EXTENSION },
+ { "CONFIDENTIALITY_REQUIRED", LDAP_CONFIDENTIALITY_REQUIRED },
+ { "SASL_BIND_IN_PROGRESS", LDAP_SASL_BIND_IN_PROGRESS },
+
+ { "NO_SUCH_ATTRIBUTE", LDAP_NO_SUCH_ATTRIBUTE },
+ { "UNDEFINED_TYPE", LDAP_UNDEFINED_TYPE },
+ { "INAPPROPRIATE_MATCHING", LDAP_INAPPROPRIATE_MATCHING },
+ { "CONSTRAINT_VIOLATION", LDAP_CONSTRAINT_VIOLATION },
+ { "TYPE_OR_VALUE_EXISTS", LDAP_TYPE_OR_VALUE_EXISTS },
+ { "INVALID_SYNTAX", LDAP_INVALID_SYNTAX },
+
+ { "NO_SUCH_OBJECT", LDAP_NO_SUCH_OBJECT },
+ { "ALIAS_PROBLEM", LDAP_ALIAS_PROBLEM },
+ { "INVALID_DN_SYNTAX", LDAP_INVALID_DN_SYNTAX },
+ { "IS_LEAF", LDAP_IS_LEAF },
+ { "ALIAS_DEREF_PROBLEM", LDAP_ALIAS_DEREF_PROBLEM },
+
+ /* obsolete */
+ { "PROXY_AUTHZ_FAILURE", LDAP_X_PROXY_AUTHZ_FAILURE },
+ { "INAPPROPRIATE_AUTH", LDAP_INAPPROPRIATE_AUTH },
+ { "INVALID_CREDENTIALS", LDAP_INVALID_CREDENTIALS },
+ { "INSUFFICIENT_ACCESS", LDAP_INSUFFICIENT_ACCESS },
+
+ { "BUSY", LDAP_BUSY },
+ { "UNAVAILABLE", LDAP_UNAVAILABLE },
+ { "UNWILLING_TO_PERFORM", LDAP_UNWILLING_TO_PERFORM },
+ { "LOOP_DETECT", LDAP_LOOP_DETECT },
+
+ { "NAMING_VIOLATION", LDAP_NAMING_VIOLATION },
+ { "OBJECT_CLASS_VIOLATION", LDAP_OBJECT_CLASS_VIOLATION },
+ { "NOT_ALLOWED_ON_NONLEAF", LDAP_NOT_ALLOWED_ON_NONLEAF },
+ { "NOT_ALLOWED_ON_RDN", LDAP_NOT_ALLOWED_ON_RDN },
+ { "ALREADY_EXISTS", LDAP_ALREADY_EXISTS },
+ { "NO_OBJECT_CLASS_MODS", LDAP_NO_OBJECT_CLASS_MODS },
+ { "RESULTS_TOO_LARGE", LDAP_RESULTS_TOO_LARGE },
+ { "AFFECTS_MULTIPLE_DSAS", LDAP_AFFECTS_MULTIPLE_DSAS },
+
+ { "OTHER", LDAP_OTHER },
+
+ { "SERVER_DOWN", LDAP_SERVER_DOWN },
+ { "LOCAL_ERROR", LDAP_LOCAL_ERROR },
+ { "ENCODING_ERROR", LDAP_ENCODING_ERROR },
+ { "DECODING_ERROR", LDAP_DECODING_ERROR },
+ { "TIMEOUT", LDAP_TIMEOUT },
+ { "AUTH_UNKNOWN", LDAP_AUTH_UNKNOWN },
+ { "FILTER_ERROR", LDAP_FILTER_ERROR },
+ { "USER_CANCELLED", LDAP_USER_CANCELLED },
+ { "PARAM_ERROR", LDAP_PARAM_ERROR },
+ { "NO_MEMORY", LDAP_NO_MEMORY },
+ { "CONNECT_ERROR", LDAP_CONNECT_ERROR },
+ { "NOT_SUPPORTED", LDAP_NOT_SUPPORTED },
+ { "CONTROL_NOT_FOUND", LDAP_CONTROL_NOT_FOUND },
+ { "NO_RESULTS_RETURNED", LDAP_NO_RESULTS_RETURNED },
+ { "MORE_RESULTS_TO_RETURN", LDAP_MORE_RESULTS_TO_RETURN },
+ { "CLIENT_LOOP", LDAP_CLIENT_LOOP },
+ { "REFERRAL_LIMIT_EXCEEDED", LDAP_REFERRAL_LIMIT_EXCEEDED },
+
+ { NULL }
+};
+
+#define UNKNOWN_ERR (1234567890)
+
+#define RETRIES 0
+#define LOOPS 100
+
+static int
+tester_ignore_str2err( const char *err )
+{
+ int i, ignore = 1;
+
+ if ( strcmp( err, "ALL" ) == 0 ) {
+ for ( i = 0; ignore_str2err[ i ].name != NULL; i++ ) {
+ ignore_count[ ignore_str2err[ i ].err ] = 1;
+ }
+ ignore_count[ LDAP_SUCCESS ] = 0;
+
+ return 0;
+ }
+
+ if ( err[ 0 ] == '!' ) {
+ ignore = 0;
+ err++;
+
+ } else if ( err[ 0 ] == '*' ) {
+ ignore = -1;
+ err++;
+ }
+
+ for ( i = 0; ignore_str2err[ i ].name != NULL; i++ ) {
+ if ( strcmp( err, ignore_str2err[ i ].name ) == 0 ) {
+ int err = ignore_str2err[ i ].err;
+
+ if ( err != LDAP_SUCCESS ) {
+ ignore_count[ err ] = ignore;
+ }
+
+ return err;
+ }
+ }
+
+ return UNKNOWN_ERR;
+}
+
+int
+tester_ignore_str2errlist( const char *err )
+{
+ int i;
+ char **errs = ldap_str2charray( err, "," );
+
+ for ( i = 0; errs[ i ] != NULL; i++ ) {
+ /* TODO: allow <err>:<prog> to ignore <err> only when <prog> */
+ (void)tester_ignore_str2err( errs[ i ] );
+ }
+
+ ldap_charray_free( errs );
+
+ return 0;
+}
+
+int
+tester_ignore_err( int err )
+{
+ int rc = 1;
+
+ if ( err && TESTER_CLIENT_FIRST <= err && err <= TESTER_SERVER_LAST ) {
+ rc = ignore_count[ err ];
+ if ( rc != 0 ) {
+ ignore_count[ err ] = rc + (rc > 0 ? 1 : -1);
+ }
+ }
+
+ /* SUCCESS is always "ignored" */
+ return rc;
+}
+
+struct tester_conn_args *
+tester_init( const char *pname, tester_t ptype )
+{
+ static struct tester_conn_args config = {
+ .authmethod = -1,
+ .retries = RETRIES,
+ .loops = LOOPS,
+ .outerloops = 1,
+
+ .uri = NULL,
+ };
+
+ pid = getpid();
+ srand( pid );
+ snprintf( progname, sizeof( progname ), "%s PID=%d", pname, pid );
+ progtype = ptype;
+
+ return &config;
+}
+
+void
+tester_ldap_error( LDAP *ld, const char *fname, const char *msg )
+{
+ int err;
+ char *text = NULL;
+ LDAPControl **ctrls = NULL;
+
+ ldap_get_option( ld, LDAP_OPT_RESULT_CODE, (void *)&err );
+ if ( err != LDAP_SUCCESS ) {
+ ldap_get_option( ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void *)&text );
+ }
+
+ fprintf( stderr, "%s: %s: %s (%d) %s %s\n",
+ progname, fname, ldap_err2string( err ), err,
+ text == NULL ? "" : text,
+ msg ? msg : "" );
+
+ if ( text ) {
+ ldap_memfree( text );
+ text = NULL;
+ }
+
+ ldap_get_option( ld, LDAP_OPT_MATCHED_DN, (void *)&text );
+ if ( text != NULL ) {
+ if ( text[ 0 ] != '\0' ) {
+ fprintf( stderr, "\tmatched: %s\n", text );
+ }
+ ldap_memfree( text );
+ text = NULL;
+ }
+
+ ldap_get_option( ld, LDAP_OPT_SERVER_CONTROLS, (void *)&ctrls );
+ if ( ctrls != NULL ) {
+ int i;
+
+ fprintf( stderr, "\tcontrols:\n" );
+ for ( i = 0; ctrls[ i ] != NULL; i++ ) {
+ fprintf( stderr, "\t\t%s\n", ctrls[ i ]->ldctl_oid );
+ }
+ ldap_controls_free( ctrls );
+ ctrls = NULL;
+ }
+
+ if ( err == LDAP_REFERRAL ) {
+ char **refs = NULL;
+
+ ldap_get_option( ld, LDAP_OPT_REFERRAL_URLS, (void *)&refs );
+
+ if ( refs ) {
+ int i;
+
+ fprintf( stderr, "\treferral:\n" );
+ for ( i = 0; refs[ i ] != NULL; i++ ) {
+ fprintf( stderr, "\t\t%s\n", refs[ i ] );
+ }
+
+ ber_memvfree( (void **)refs );
+ }
+ }
+}
+
+void
+tester_perror( const char *fname, const char *msg )
+{
+ int save_errno = errno;
+ char buf[ BUFSIZ ];
+
+ fprintf( stderr, "%s: %s: (%d) %s %s\n",
+ progname, fname, save_errno,
+ AC_STRERROR_R( save_errno, buf, sizeof( buf ) ),
+ msg ? msg : "" );
+}
+
+int
+tester_config_opt( struct tester_conn_args *config, char opt, char *optarg )
+{
+ switch ( opt ) {
+ case 'C':
+ config->chaserefs++;
+ break;
+
+ case 'D':
+ config->binddn = optarg;
+ break;
+
+ case 'd':
+ {
+ if ( lutil_atoi( &debug, optarg ) != 0 ) {
+ return -1;
+ }
+
+ if ( ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &debug )
+ != LBER_OPT_SUCCESS )
+ {
+ fprintf( stderr,
+ "Could not set LBER_OPT_DEBUG_LEVEL %d\n", debug );
+ }
+
+ if ( ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &debug )
+ != LDAP_OPT_SUCCESS )
+ {
+ fprintf( stderr,
+ "Could not set LDAP_OPT_DEBUG_LEVEL %d\n", debug );
+ }
+ break;
+ }
+
+ case 'H':
+ config->uri = optarg;
+ break;
+
+ case 'i':
+ tester_ignore_str2errlist( optarg );
+ break;
+
+ case 'L':
+ if ( lutil_atoi( &config->outerloops, optarg ) != 0 ) {
+ return -1;
+ }
+ break;
+
+ case 'l':
+ if ( lutil_atoi( &config->loops, optarg ) != 0 ) {
+ return -1;
+ }
+ break;
+
+#ifdef HAVE_CYRUS_SASL
+ case 'O':
+ if ( config->secprops != NULL ) {
+ return -1;
+ }
+ if ( config->authmethod != -1 && config->authmethod != LDAP_AUTH_SASL ) {
+ return -1;
+ }
+ config->authmethod = LDAP_AUTH_SASL;
+ config->secprops = optarg;
+ break;
+
+ case 'R':
+ if ( config->realm != NULL ) {
+ return -1;
+ }
+ if ( config->authmethod != -1 && config->authmethod != LDAP_AUTH_SASL ) {
+ return -1;
+ }
+ config->authmethod = LDAP_AUTH_SASL;
+ config->realm = optarg;
+ break;
+
+ case 'U':
+ if ( config->authc_id != NULL ) {
+ return -1;
+ }
+ if ( config->authmethod != -1 && config->authmethod != LDAP_AUTH_SASL ) {
+ return -1;
+ }
+ config->authmethod = LDAP_AUTH_SASL;
+ config->authc_id = optarg;
+ break;
+
+ case 'X':
+ if ( config->authz_id != NULL ) {
+ return -1;
+ }
+ if ( config->authmethod != -1 && config->authmethod != LDAP_AUTH_SASL ) {
+ return -1;
+ }
+ config->authmethod = LDAP_AUTH_SASL;
+ config->authz_id = optarg;
+ break;
+
+ case 'Y':
+ if ( config->mech != NULL ) {
+ return -1;
+ }
+ if ( config->authmethod != -1 && config->authmethod != LDAP_AUTH_SASL ) {
+ return -1;
+ }
+ config->authmethod = LDAP_AUTH_SASL;
+ config->mech = optarg;
+ break;
+#endif
+
+ case 'r':
+ if ( lutil_atoi( &config->retries, optarg ) != 0 ) {
+ return -1;
+ }
+ break;
+
+ case 't':
+ if ( lutil_atoi( &config->delay, optarg ) != 0 ) {
+ return -1;
+ }
+ break;
+
+ case 'w':
+ config->pass.bv_val = strdup( optarg );
+ config->pass.bv_len = strlen( optarg );
+ memset( optarg, '*', config->pass.bv_len );
+ break;
+
+ case 'x':
+ if ( config->authmethod != -1 && config->authmethod != LDAP_AUTH_SIMPLE ) {
+ return -1;
+ }
+ config->authmethod = LDAP_AUTH_SIMPLE;
+ break;
+
+ default:
+ return -1;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+void
+tester_config_finish( struct tester_conn_args *config )
+{
+ if ( config->authmethod == -1 ) {
+#ifdef HAVE_CYRUS_SASL
+ if ( config->binddn != NULL ) {
+ config->authmethod = LDAP_AUTH_SIMPLE;
+ } else {
+ config->authmethod = LDAP_AUTH_SASL;
+ }
+#else
+ config->authmethod = LDAP_AUTH_SIMPLE;
+#endif
+ }
+
+#ifdef HAVE_CYRUS_SASL
+ if ( config->authmethod == LDAP_AUTH_SASL ) {
+ config->defaults = lutil_sasl_defaults( NULL,
+ config->mech,
+ config->realm,
+ config->authc_id,
+ config->pass.bv_val,
+ config->authz_id );
+
+ if ( config->defaults == NULL ) {
+ tester_error( "unable to prepare SASL defaults" );
+ exit( EXIT_FAILURE );
+ }
+ }
+#endif
+}
+
+void
+tester_init_ld( LDAP **ldp, struct tester_conn_args *config, int flags )
+{
+ LDAP *ld;
+ int rc, do_retry = config->retries;
+ int version = LDAP_VERSION3;
+
+retry:;
+ ldap_initialize( &ld, config->uri );
+ if ( ld == NULL ) {
+ tester_perror( "ldap_initialize", NULL );
+ exit( EXIT_FAILURE );
+ }
+
+ (void) ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
+ (void) ldap_set_option( ld, LDAP_OPT_REFERRALS,
+ config->chaserefs ? LDAP_OPT_ON: LDAP_OPT_OFF );
+
+ if ( !( flags & TESTER_INIT_ONLY ) ) {
+ if ( config->authmethod == LDAP_AUTH_SASL ) {
+#ifdef HAVE_CYRUS_SASL
+ if ( config->secprops != NULL ) {
+ rc = ldap_set_option( ld,
+ LDAP_OPT_X_SASL_SECPROPS, config->secprops );
+
+ if ( rc != LDAP_OPT_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_set_option(SECPROPS)", NULL );
+ ldap_unbind_ext( ld, NULL, NULL );
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ rc = ldap_sasl_interactive_bind_s( ld,
+ config->binddn,
+ config->mech,
+ NULL, NULL,
+ LDAP_SASL_QUIET,
+ lutil_sasl_interact,
+ config->defaults );
+#else /* HAVE_CYRUS_SASL */
+ /* caller shouldn't have allowed this */
+ assert(0);
+#endif
+ } else if ( config->authmethod == LDAP_AUTH_SIMPLE ) {
+ rc = ldap_sasl_bind_s( ld,
+ config->binddn, LDAP_SASL_SIMPLE,
+ &config->pass, NULL, NULL, NULL );
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_sasl_bind_s", NULL );
+ switch ( rc ) {
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry > 0 ) {
+ do_retry--;
+ if ( config->delay > 0 ) {
+ sleep( config->delay );
+ }
+ goto retry;
+ }
+ }
+ ldap_unbind_ext( ld, NULL, NULL );
+ ld = NULL;
+ if ( !( flags & TESTER_INIT_NOEXIT ))
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ *ldp = ld;
+}
+
+void
+tester_error( const char *msg )
+{
+ fprintf( stderr, "%s: %s\n", progname, msg );
+}
diff --git a/tests/progs/slapd-common.h b/tests/progs/slapd-common.h
new file mode 100644
index 0000000..64410c7
--- /dev/null
+++ b/tests/progs/slapd-common.h
@@ -0,0 +1,92 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software.
+ */
+
+#ifndef SLAPD_COMMON_H
+#define SLAPD_COMMON_H
+
+typedef enum {
+ TESTER_TESTER,
+ TESTER_ADDEL,
+ TESTER_BIND,
+ TESTER_MODIFY,
+ TESTER_MODRDN,
+ TESTER_READ,
+ TESTER_SEARCH,
+ TESTER_LAST
+} tester_t;
+
+extern struct tester_conn_args * tester_init( const char *pname, tester_t ptype );
+extern char * tester_uri( char *uri );
+extern void tester_error( const char *msg );
+extern void tester_perror( const char *fname, const char *msg );
+extern void tester_ldap_error( LDAP *ld, const char *fname, const char *msg );
+extern int tester_ignore_str2errlist( const char *err );
+extern int tester_ignore_err( int err );
+
+struct tester_conn_args {
+ char *uri;
+
+ int outerloops;
+ int loops;
+ int retries;
+ int delay;
+
+ int chaserefs;
+
+ int authmethod;
+
+ char *binddn;
+ struct berval pass;
+
+#ifdef HAVE_CYRUS_SASL
+ char *mech;
+ char *realm;
+ char *authz_id;
+ char *authc_id;
+ char *secprops;
+ void *defaults;
+#endif
+};
+
+#define TESTER_INIT_ONLY (1 << 0)
+#define TESTER_INIT_NOEXIT (1 << 1)
+#define TESTER_COMMON_OPTS "CD:d:H:L:l:i:O:R:U:X:Y:r:t:w:x"
+#define TESTER_COMMON_HELP \
+ "[-C] " \
+ "[-D <dn> [-w <passwd>]] " \
+ "[-d <level>] " \
+ "[-H <uri>]" \
+ "[-i <ignore>] " \
+ "[-l <loops>] " \
+ "[-L <outerloops>] " \
+ "[-r <maxretries>] " \
+ "[-t <delay>] " \
+ "[-O <SASL secprops>] " \
+ "[-R <SASL realm>] " \
+ "[-U <SASL authcid> [-X <SASL authzid>]] " \
+ "[-x | -Y <SASL mech>] "
+
+extern int tester_config_opt( struct tester_conn_args *config, char opt, char *optarg );
+extern void tester_config_finish( struct tester_conn_args *config );
+extern void tester_init_ld( LDAP **ldp, struct tester_conn_args *conf, int flags );
+
+extern pid_t pid;
+extern int debug;
+
+#endif /* SLAPD_COMMON_H */
diff --git a/tests/progs/slapd-modify.c b/tests/progs/slapd-modify.c
new file mode 100644
index 0000000..acc131a
--- /dev/null
+++ b/tests/progs/slapd-modify.c
@@ -0,0 +1,225 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "ac/stdlib.h"
+
+#include "ac/ctype.h"
+#include "ac/param.h"
+#include "ac/socket.h"
+#include "ac/string.h"
+#include "ac/unistd.h"
+#include "ac/wait.h"
+
+#include "ldap.h"
+#include "lutil.h"
+
+#include "slapd-common.h"
+
+#define LOOPS 100
+
+static void
+do_modify( struct tester_conn_args *config, char *entry,
+ char *attr, char *value, int friendly );
+
+
+static void
+usage( char *name, int opt )
+{
+ if ( opt ) {
+ fprintf( stderr, "%s: unable to handle option \'%c\'\n\n",
+ name, opt );
+ }
+
+ fprintf( stderr, "usage: %s " TESTER_COMMON_HELP
+ "-a <attr:val> "
+ "-e <entry> "
+ "[-F]\n",
+ name );
+ exit( EXIT_FAILURE );
+}
+
+int
+main( int argc, char **argv )
+{
+ int i;
+ char *entry = NULL;
+ char *ava = NULL;
+ char *value = NULL;
+ int friendly = 0;
+ struct tester_conn_args *config;
+
+ config = tester_init( "slapd-modify", TESTER_MODIFY );
+
+ while ( ( i = getopt( argc, argv, TESTER_COMMON_OPTS "a:e:F" ) ) != EOF )
+ {
+ switch ( i ) {
+ case 'F':
+ friendly++;
+ break;
+
+ case 'i':
+ /* ignored (!) by now */
+ break;
+
+ case 'e': /* entry to modify */
+ entry = optarg;
+ break;
+
+ case 'a':
+ ava = optarg;
+ break;
+
+ default:
+ if ( tester_config_opt( config, i, optarg ) == LDAP_SUCCESS ) {
+ break;
+ }
+ usage( argv[0], i );
+ break;
+ }
+ }
+
+ if (( entry == NULL ) || ( ava == NULL ))
+ usage( argv[0], 0 );
+
+ if ( *entry == '\0' ) {
+
+ fprintf( stderr, "%s: invalid EMPTY entry DN.\n",
+ argv[0] );
+ exit( EXIT_FAILURE );
+
+ }
+ if ( *ava == '\0' ) {
+ fprintf( stderr, "%s: invalid EMPTY AVA.\n",
+ argv[0] );
+ exit( EXIT_FAILURE );
+ }
+
+ if ( !( value = strchr( ava, ':' ))) {
+ fprintf( stderr, "%s: invalid AVA.\n",
+ argv[0] );
+ exit( EXIT_FAILURE );
+ }
+ *value++ = '\0';
+ while ( *value && isspace( (unsigned char) *value ))
+ value++;
+
+ tester_config_finish( config );
+
+ for ( i = 0; i < config->outerloops; i++ ) {
+ do_modify( config, entry, ava, value, friendly );
+ }
+
+ exit( EXIT_SUCCESS );
+}
+
+
+static void
+do_modify( struct tester_conn_args *config,
+ char *entry, char* attr, char* value, int friendly )
+{
+ LDAP *ld = NULL;
+ int i = 0, do_retry = config->retries;
+ int rc = LDAP_SUCCESS;
+
+ struct ldapmod mod;
+ struct ldapmod *mods[2];
+ char *values[2];
+
+ values[0] = value;
+ values[1] = NULL;
+ mod.mod_op = LDAP_MOD_ADD;
+ mod.mod_type = attr;
+ mod.mod_values = values;
+ mods[0] = &mod;
+ mods[1] = NULL;
+
+retry:;
+ if ( ld == NULL ) {
+ tester_init_ld( &ld, config, 0 );
+ }
+
+ if ( do_retry == config->retries ) {
+ fprintf( stderr, "PID=%ld - Modify(%d): entry=\"%s\".\n",
+ (long) pid, config->loops, entry );
+ }
+
+ for ( ; i < config->loops; i++ ) {
+ mod.mod_op = LDAP_MOD_ADD;
+ rc = ldap_modify_ext_s( ld, entry, mods, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_modify_ext_s", NULL );
+ switch ( rc ) {
+ case LDAP_TYPE_OR_VALUE_EXISTS:
+ /* NOTE: this likely means
+ * the second modify failed
+ * during the previous round... */
+ if ( !friendly ) {
+ goto done;
+ }
+ break;
+
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry > 0 ) {
+ do_retry--;
+ goto retry;
+ }
+ /* fall thru */
+
+ default:
+ goto done;
+ }
+ }
+
+ mod.mod_op = LDAP_MOD_DELETE;
+ rc = ldap_modify_ext_s( ld, entry, mods, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_modify_ext_s", NULL );
+ switch ( rc ) {
+ case LDAP_NO_SUCH_ATTRIBUTE:
+ /* NOTE: this likely means
+ * the first modify failed
+ * during the previous round... */
+ if ( !friendly ) {
+ goto done;
+ }
+ break;
+
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry > 0 ) {
+ do_retry--;
+ goto retry;
+ }
+ /* fall thru */
+
+ default:
+ goto done;
+ }
+ }
+
+ }
+
+done:;
+ fprintf( stderr, " PID=%ld - Modify done (%d).\n", (long) pid, rc );
+
+ ldap_unbind_ext( ld, NULL, NULL );
+}
+
+
diff --git a/tests/progs/slapd-modrdn.c b/tests/progs/slapd-modrdn.c
new file mode 100644
index 0000000..e224c0a
--- /dev/null
+++ b/tests/progs/slapd-modrdn.c
@@ -0,0 +1,229 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu, based in part
+ * on other OpenLDAP test tools, for inclusion in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "ac/stdlib.h"
+
+#include "ac/ctype.h"
+#include "ac/param.h"
+#include "ac/socket.h"
+#include "ac/string.h"
+#include "ac/unistd.h"
+#include "ac/wait.h"
+
+#include "ldap.h"
+#include "lutil.h"
+
+#include "slapd-common.h"
+
+#define LOOPS 100
+#define RETRIES 0
+
+static void
+do_modrdn( struct tester_conn_args *config,
+ char *entry, int friendly );
+
+static void
+usage( char *name, char opt )
+{
+ if ( opt ) {
+ fprintf( stderr, "%s: unable to handle option \'%c\'\n\n",
+ name, opt );
+ }
+
+ fprintf( stderr, "usage: %s " TESTER_COMMON_HELP
+ "-e <entry> "
+ "[-F]\n",
+ name );
+ exit( EXIT_FAILURE );
+}
+
+int
+main( int argc, char **argv )
+{
+ int i;
+ char *entry = NULL;
+ int friendly = 0;
+ struct tester_conn_args *config;
+
+ config = tester_init( "slapd-modrdn", TESTER_MODRDN );
+
+ while ( ( i = getopt( argc, argv, TESTER_COMMON_OPTS "e:F" ) ) != EOF )
+ {
+ switch ( i ) {
+ case 'F':
+ friendly++;
+ break;
+
+ case 'i':
+ /* ignored (!) by now */
+ break;
+
+ case 'e': /* entry to rename */
+ entry = optarg;
+ break;
+
+ default:
+ if ( tester_config_opt( config, i, optarg ) == LDAP_SUCCESS ) {
+ break;
+ }
+ usage( argv[0], i );
+ break;
+ }
+ }
+
+ if ( entry == NULL )
+ usage( argv[0], 0 );
+
+ if ( *entry == '\0' ) {
+
+ fprintf( stderr, "%s: invalid EMPTY entry DN.\n",
+ argv[0] );
+ exit( EXIT_FAILURE );
+
+ }
+
+ tester_config_finish( config );
+
+ for ( i = 0; i < config->outerloops; i++ ) {
+ do_modrdn( config, entry, friendly );
+ }
+
+ exit( EXIT_SUCCESS );
+}
+
+
+static void
+do_modrdn( struct tester_conn_args *config,
+ char *entry, int friendly )
+{
+ LDAP *ld = NULL;
+ int i, do_retry = config->retries;
+ char *DNs[2];
+ char *rdns[2];
+ int rc = LDAP_SUCCESS;
+ char *p1, *p2;
+
+ DNs[0] = entry;
+ DNs[1] = strdup( entry );
+ if ( DNs[1] == NULL ) {
+ tester_error( "strdup failed" );
+ exit( EXIT_FAILURE );
+ }
+
+ /* reverse the RDN, make new DN */
+ p1 = strchr( entry, '=' ) + 1;
+ p2 = strchr( p1, ',' );
+
+ *p2 = '\0';
+ rdns[1] = strdup( entry );
+ if ( rdns[1] == NULL ) {
+ tester_error( "strdup failed" );
+ exit( EXIT_FAILURE );
+ }
+ *p2-- = ',';
+
+ for (i = p1 - entry;p2 >= p1;)
+ DNs[1][i++] = *p2--;
+
+ DNs[1][i] = '\0';
+ rdns[0] = strdup( DNs[1] );
+ if ( rdns[0] == NULL ) {
+ tester_error( "strdup failed" );
+ exit( EXIT_FAILURE );
+ }
+ DNs[1][i] = ',';
+
+ i = 0;
+
+retry:;
+ if ( ld == NULL ) {
+ tester_init_ld( &ld, config, 0 );
+ }
+
+ if ( do_retry == config->retries ) {
+ fprintf( stderr, "PID=%ld - Modrdn(%d): entry=\"%s\".\n",
+ (long) pid, config->loops, entry );
+ }
+
+ for ( ; i < config->loops; i++ ) {
+ rc = ldap_rename_s( ld, DNs[0], rdns[0], NULL, 0, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_rename_s", NULL );
+ switch ( rc ) {
+ case LDAP_NO_SUCH_OBJECT:
+ /* NOTE: this likely means
+ * the second modrdn failed
+ * during the previous round... */
+ if ( !friendly ) {
+ goto done;
+ }
+ break;
+
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry > 0 ) {
+ do_retry--;
+ goto retry;
+ }
+ /* fall thru */
+
+ default:
+ goto done;
+ }
+ }
+ rc = ldap_rename_s( ld, DNs[1], rdns[1], NULL, 1, NULL, NULL );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_rename_s", NULL );
+ switch ( rc ) {
+ case LDAP_NO_SUCH_OBJECT:
+ /* NOTE: this likely means
+ * the first modrdn failed
+ * during the previous round... */
+ if ( !friendly ) {
+ goto done;
+ }
+ break;
+
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ if ( do_retry > 0 ) {
+ do_retry--;
+ goto retry;
+ }
+ /* fall thru */
+
+ default:
+ goto done;
+ }
+ }
+ }
+
+done:;
+ fprintf( stderr, " PID=%ld - Modrdn done (%d).\n", (long) pid, rc );
+
+ ldap_unbind_ext( ld, NULL, NULL );
+
+ free( DNs[1] );
+ free( rdns[0] );
+ free( rdns[1] );
+}
diff --git a/tests/progs/slapd-mtread.c b/tests/progs/slapd-mtread.c
new file mode 100644
index 0000000..587d3cf
--- /dev/null
+++ b/tests/progs/slapd-mtread.c
@@ -0,0 +1,722 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Kurt Spanier for inclusion
+ * in OpenLDAP Software.
+ */
+
+/*
+ * This tool is a MT reader. It behaves like slapd-read however
+ * with one or more threads simultaneously using the same connection.
+ * If -M is enabled, then M threads will also perform write operations.
+ */
+
+#include "portable.h"
+
+/* Requires libldap with threads */
+#ifndef NO_THREADS
+
+#include <stdio.h>
+#include "ldap_pvt_thread.h"
+
+#include "ac/stdlib.h"
+
+#include "ac/ctype.h"
+#include "ac/param.h"
+#include "ac/socket.h"
+#include "ac/string.h"
+#include "ac/unistd.h"
+#include "ac/wait.h"
+
+#include "ldap.h"
+#include "lutil.h"
+
+#include "ldap_pvt.h"
+
+#include "slapd-common.h"
+
+#define MAXCONN 512
+#define LOOPS 100
+#define RETRIES 0
+#define DEFAULT_BASE "ou=people,dc=example,dc=com"
+
+static void
+do_read( LDAP *ld, char *entry,
+ char **attrs, int noattrs, int nobind, int maxloop,
+ int force, int idx );
+
+static void
+do_random( LDAP *ld,
+ char *sbase, char *filter, char **attrs, int noattrs, int nobind,
+ int force, int idx );
+
+static void
+do_random2( LDAP *ld,
+ char *sbase, char *filter, char **attrs, int noattrs, int nobind,
+ int force, int idx );
+
+static void *
+do_onethread( void *arg );
+
+static void *
+do_onerwthread( void *arg );
+
+#define MAX_THREAD 1024
+/* Use same array for readers and writers, offset writers by MAX_THREAD */
+int rt_pass[MAX_THREAD*2];
+int rt_fail[MAX_THREAD*2];
+int *rwt_pass = rt_pass + MAX_THREAD;
+int *rwt_fail = rt_fail + MAX_THREAD;
+ldap_pvt_thread_t rtid[MAX_THREAD*2], *rwtid = rtid + MAX_THREAD;
+
+/*
+ * Shared globals (command line args)
+ */
+LDAP *ld = NULL;
+struct tester_conn_args *config;
+char *entry = NULL;
+char *filter = NULL;
+int force = 0;
+char *srchattrs[] = { "1.1", NULL };
+char **attrs = srchattrs;
+int noattrs = 0;
+int nobind = 0;
+int threads = 1;
+int rwthreads = 0;
+int verbose = 0;
+
+int noconns = 1;
+LDAP **lds = NULL;
+
+static void
+thread_error(int idx, char *string)
+{
+ char thrstr[BUFSIZ];
+
+ snprintf(thrstr, BUFSIZ, "error on tidx: %d: %s", idx, string);
+ tester_error( thrstr );
+}
+
+static void
+thread_output(int idx, char *string)
+{
+ char thrstr[BUFSIZ];
+
+ snprintf(thrstr, BUFSIZ, "tidx: %d says: %s", idx, string);
+ tester_error( thrstr );
+}
+
+static void
+thread_verbose(int idx, char *string)
+{
+ char thrstr[BUFSIZ];
+
+ if (!verbose)
+ return;
+ snprintf(thrstr, BUFSIZ, "tidx: %d says: %s", idx, string);
+ tester_error( thrstr );
+}
+
+static void
+usage( char *name, char opt )
+{
+ if ( opt ) {
+ fprintf( stderr, "%s: unable to handle option \'%c\'\n\n",
+ name, opt );
+ }
+
+ fprintf( stderr, "usage: %s " TESTER_COMMON_HELP
+ "-e <entry> "
+ "[-A] "
+ "[-F] "
+ "[-N] "
+ "[-v] "
+ "[-c connections] "
+ "[-f filter] "
+ "[-m threads] "
+ "[-M threads] "
+ "[-T <attrs>] "
+ "[<attrs>] "
+ "\n",
+ name );
+ exit( EXIT_FAILURE );
+}
+
+int
+main( int argc, char **argv )
+{
+ int i;
+ char *uri = NULL;
+ char *manager = NULL;
+ struct berval passwd = { 0, NULL };
+ char outstr[BUFSIZ];
+ int ptpass;
+ int testfail = 0;
+
+ config = tester_init( "slapd-mtread", TESTER_READ );
+
+ /* by default, tolerate referrals and no such object */
+ tester_ignore_str2errlist( "REFERRAL,NO_SUCH_OBJECT" );
+
+ while ( (i = getopt( argc, argv, TESTER_COMMON_OPTS "Ac:e:Ff:M:m:NT:v" )) != EOF ) {
+ switch ( i ) {
+ case 'A':
+ noattrs++;
+ break;
+
+ case 'N':
+ nobind = TESTER_INIT_ONLY;
+ break;
+
+ case 'v':
+ verbose++;
+ break;
+
+ case 'c': /* the number of connections */
+ if ( lutil_atoi( &noconns, optarg ) != 0 ) {
+ usage( argv[0], i );
+ }
+ break;
+
+ case 'e': /* DN to search for */
+ entry = optarg;
+ break;
+
+ case 'f': /* the search request */
+ filter = optarg;
+ break;
+
+ case 'F':
+ force++;
+ break;
+
+ case 'M': /* the number of R/W threads */
+ if ( lutil_atoi( &rwthreads, optarg ) != 0 ) {
+ usage( argv[0], i );
+ }
+ if (rwthreads > MAX_THREAD)
+ rwthreads = MAX_THREAD;
+ break;
+
+ case 'm': /* the number of threads */
+ if ( lutil_atoi( &threads, optarg ) != 0 ) {
+ usage( argv[0], i );
+ }
+ if (threads > MAX_THREAD)
+ threads = MAX_THREAD;
+ break;
+
+ case 'T':
+ attrs = ldap_str2charray( optarg, "," );
+ if ( attrs == NULL ) {
+ usage( argv[0], i );
+ }
+ break;
+
+ default:
+ if ( tester_config_opt( config, i, optarg ) == LDAP_SUCCESS ) {
+ break;
+ }
+ usage( argv[0], i );
+ break;
+ }
+ }
+
+ if ( entry == NULL )
+ usage( argv[0], 0 );
+
+ if ( *entry == '\0' ) {
+ fprintf( stderr, "%s: invalid EMPTY entry DN.\n",
+ argv[0] );
+ exit( EXIT_FAILURE );
+ }
+
+ if ( argv[optind] != NULL ) {
+ attrs = &argv[optind];
+ }
+
+ if (noconns < 1)
+ noconns = 1;
+ if (noconns > MAXCONN)
+ noconns = MAXCONN;
+ lds = (LDAP **) calloc( sizeof(LDAP *), noconns);
+ if (lds == NULL) {
+ fprintf( stderr, "%s: Memory error: calloc noconns.\n",
+ argv[0] );
+ exit( EXIT_FAILURE );
+ }
+
+ tester_config_finish( config );
+ ldap_pvt_thread_initialize();
+
+ for (i = 0; i < noconns; i++) {
+ tester_init_ld( &lds[i], config, nobind );
+ }
+
+ snprintf(outstr, BUFSIZ, "MT Test Start: conns: %d (%s)", noconns, uri);
+ tester_error(outstr);
+ snprintf(outstr, BUFSIZ, "Threads: RO: %d RW: %d", threads, rwthreads);
+ tester_error(outstr);
+
+ /* Set up read only threads */
+ for ( i = 0; i < threads; i++ ) {
+ ldap_pvt_thread_create( &rtid[i], 0, do_onethread, &rtid[i]);
+ snprintf(outstr, BUFSIZ, "Created RO thread %d", i);
+ thread_verbose(-1, outstr);
+ }
+ /* Set up read/write threads */
+ for ( i = 0; i < rwthreads; i++ ) {
+ ldap_pvt_thread_create( &rwtid[i], 0, do_onerwthread, &rwtid[i]);
+ snprintf(outstr, BUFSIZ, "Created RW thread %d", i + MAX_THREAD);
+ thread_verbose(-1, outstr);
+ }
+
+ ptpass = config->outerloops * config->loops;
+
+ /* wait for read only threads to complete */
+ for ( i = 0; i < threads; i++ )
+ ldap_pvt_thread_join(rtid[i], NULL);
+ /* wait for read/write threads to complete */
+ for ( i = 0; i < rwthreads; i++ )
+ ldap_pvt_thread_join(rwtid[i], NULL);
+
+ for(i = 0; i < noconns; i++) {
+ if ( lds[i] != NULL ) {
+ ldap_unbind_ext( lds[i], NULL, NULL );
+ }
+ }
+ free( lds );
+
+ for ( i = 0; i < threads; i++ ) {
+ snprintf(outstr, BUFSIZ, "RO thread %d pass=%d fail=%d", i,
+ rt_pass[i], rt_fail[i]);
+ tester_error(outstr);
+ if (rt_fail[i] != 0 || rt_pass[i] != ptpass) {
+ snprintf(outstr, BUFSIZ, "FAIL RO thread %d", i);
+ tester_error(outstr);
+ testfail++;
+ }
+ }
+ for ( i = 0; i < rwthreads; i++ ) {
+ snprintf(outstr, BUFSIZ, "RW thread %d pass=%d fail=%d", i + MAX_THREAD,
+ rwt_pass[i], rwt_fail[i]);
+ tester_error(outstr);
+ if (rwt_fail[i] != 0 || rwt_pass[i] != ptpass) {
+ snprintf(outstr, BUFSIZ, "FAIL RW thread %d", i);
+ tester_error(outstr);
+ testfail++;
+ }
+ }
+ snprintf(outstr, BUFSIZ, "MT Test complete" );
+ tester_error(outstr);
+
+ if (testfail)
+ exit( EXIT_FAILURE );
+ exit( EXIT_SUCCESS );
+}
+
+static void *
+do_onethread( void *arg )
+{
+ int i, j, thisconn;
+ LDAP **mlds;
+ char thrstr[BUFSIZ];
+ int rc, refcnt = 0;
+ int idx = (ldap_pvt_thread_t *)arg - rtid;
+
+ mlds = (LDAP **) calloc( sizeof(LDAP *), noconns);
+ if (mlds == NULL) {
+ thread_error( idx, "Memory error: thread calloc for noconns" );
+ exit( EXIT_FAILURE );
+ }
+
+ for ( j = 0; j < config->outerloops; j++ ) {
+ for(i = 0; i < noconns; i++) {
+ mlds[i] = ldap_dup(lds[i]);
+ if (mlds[i] == NULL) {
+ thread_error( idx, "ldap_dup error" );
+ }
+ }
+ rc = ldap_get_option(mlds[0], LDAP_OPT_SESSION_REFCNT, &refcnt);
+ snprintf(thrstr, BUFSIZ,
+ "RO Thread conns: %d refcnt: %d (rc = %d)",
+ noconns, refcnt, rc);
+ thread_verbose(idx, thrstr);
+
+ thisconn = (idx + j) % noconns;
+ if (thisconn < 0 || thisconn >= noconns)
+ thisconn = 0;
+ if (mlds[thisconn] == NULL) {
+ thread_error( idx, "(failed to dup)");
+ tester_perror( "ldap_dup", "(failed to dup)" );
+ exit( EXIT_FAILURE );
+ }
+ snprintf(thrstr, BUFSIZ, "Using conn %d", thisconn);
+ thread_verbose(idx, thrstr);
+ if ( filter != NULL ) {
+ if (strchr(filter, '['))
+ do_random2( mlds[thisconn], entry, filter, attrs,
+ noattrs, nobind, force, idx );
+ else
+ do_random( mlds[thisconn], entry, filter, attrs,
+ noattrs, nobind, force, idx );
+
+ } else {
+ do_read( mlds[thisconn], entry, attrs, noattrs,
+ nobind, config->loops, force, idx );
+ }
+ for(i = 0; i < noconns; i++) {
+ (void) ldap_destroy(mlds[i]);
+ mlds[i] = NULL;
+ }
+ }
+ free( mlds );
+ return( NULL );
+}
+
+static void *
+do_onerwthread( void *arg )
+{
+ int i, j, thisconn;
+ LDAP **mlds, *ld;
+ char thrstr[BUFSIZ];
+ char dn[256], uids[32], cns[32], *base;
+ LDAPMod *attrp[5], attrs[4];
+ char *oc_vals[] = { "top", "OpenLDAPperson", NULL };
+ char *cn_vals[] = { NULL, NULL };
+ char *sn_vals[] = { NULL, NULL };
+ char *uid_vals[] = { NULL, NULL };
+ int ret;
+ int adds = 0;
+ int dels = 0;
+ int rc, refcnt = 0;
+ int idx = (ldap_pvt_thread_t *)arg - rtid;
+
+ mlds = (LDAP **) calloc( sizeof(LDAP *), noconns);
+ if (mlds == NULL) {
+ thread_error( idx, "Memory error: thread calloc for noconns" );
+ exit( EXIT_FAILURE );
+ }
+
+ snprintf(uids, sizeof(uids), "rwtest%04d", idx);
+ snprintf(cns, sizeof(cns), "rwtest%04d", idx);
+ /* add setup */
+ for (i = 0; i < 4; i++) {
+ attrp[i] = &attrs[i];
+ attrs[i].mod_op = 0;
+ }
+ attrp[4] = NULL;
+ attrs[0].mod_type = "objectClass";
+ attrs[0].mod_values = oc_vals;
+ attrs[1].mod_type = "cn";
+ attrs[1].mod_values = cn_vals;
+ cn_vals[0] = &cns[0];
+ attrs[2].mod_type = "sn";
+ attrs[2].mod_values = sn_vals;
+ sn_vals[0] = &cns[0];
+ attrs[3].mod_type = "uid";
+ attrs[3].mod_values = uid_vals;
+ uid_vals[0] = &uids[0];
+
+ for ( j = 0; j < config->outerloops; j++ ) {
+ for(i = 0; i < noconns; i++) {
+ mlds[i] = ldap_dup(lds[i]);
+ if (mlds[i] == NULL) {
+ thread_error( idx, "ldap_dup error" );
+ }
+ }
+ rc = ldap_get_option(mlds[0], LDAP_OPT_SESSION_REFCNT, &refcnt);
+ snprintf(thrstr, BUFSIZ,
+ "RW Thread conns: %d refcnt: %d (rc = %d)",
+ noconns, refcnt, rc);
+ thread_verbose(idx, thrstr);
+
+ thisconn = (idx + j) % noconns;
+ if (thisconn < 0 || thisconn >= noconns)
+ thisconn = 0;
+ if (mlds[thisconn] == NULL) {
+ thread_error( idx, "(failed to dup)");
+ tester_perror( "ldap_dup", "(failed to dup)" );
+ exit( EXIT_FAILURE );
+ }
+ snprintf(thrstr, BUFSIZ, "START RW Thread using conn %d", thisconn);
+ thread_verbose(idx, thrstr);
+
+ ld = mlds[thisconn];
+ if (entry != NULL)
+ base = entry;
+ else
+ base = DEFAULT_BASE;
+ snprintf(dn, 256, "cn=%s,%s", cns, base);
+
+ adds = 0;
+ dels = 0;
+ for (i = 0; i < config->loops; i++) {
+ ret = ldap_add_ext_s(ld, dn, &attrp[0], NULL, NULL);
+ if (ret == LDAP_SUCCESS) {
+ adds++;
+ ret = ldap_delete_ext_s(ld, dn, NULL, NULL);
+ if (ret == LDAP_SUCCESS) {
+ dels++;
+ rt_pass[idx]++;
+ } else {
+ thread_output(idx, ldap_err2string(ret));
+ rt_fail[idx]++;
+ }
+ } else {
+ thread_output(idx, ldap_err2string(ret));
+ rt_fail[idx]++;
+ }
+ }
+
+ snprintf(thrstr, BUFSIZ,
+ "INNER STOP RW Thread using conn %d (%d/%d)",
+ thisconn, adds, dels);
+ thread_verbose(idx, thrstr);
+
+ for(i = 0; i < noconns; i++) {
+ (void) ldap_destroy(mlds[i]);
+ mlds[i] = NULL;
+ }
+ }
+
+ free( mlds );
+ return( NULL );
+}
+
+static void
+do_random( LDAP *ld,
+ char *sbase, char *filter, char **srchattrs, int noattrs, int nobind,
+ int force, int idx )
+{
+ int i = 0, do_retry = config->retries;
+ char *attrs[ 2 ];
+ int rc = LDAP_SUCCESS;
+ int nvalues = 0;
+ char **values = NULL;
+ LDAPMessage *res = NULL, *e = NULL;
+ char thrstr[BUFSIZ];
+
+ attrs[ 0 ] = LDAP_NO_ATTRS;
+ attrs[ 1 ] = NULL;
+
+ snprintf( thrstr, BUFSIZ,
+ "Read(%d): base=\"%s\", filter=\"%s\".\n",
+ config->loops, sbase, filter );
+ thread_verbose( idx, thrstr );
+
+ rc = ldap_search_ext_s( ld, sbase, LDAP_SCOPE_SUBTREE,
+ filter, attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res );
+ switch ( rc ) {
+ case LDAP_SIZELIMIT_EXCEEDED:
+ case LDAP_TIMELIMIT_EXCEEDED:
+ case LDAP_SUCCESS:
+ nvalues = ldap_count_entries( ld, res );
+ if ( nvalues == 0 ) {
+ if ( rc ) {
+ tester_ldap_error( ld, "ldap_search_ext_s", NULL );
+ }
+ break;
+ }
+
+ values = malloc( ( nvalues + 1 ) * sizeof( char * ) );
+ if (values == NULL) {
+ thread_error( idx, "(failed to malloc)");
+ exit( EXIT_FAILURE );
+ }
+ for ( i = 0, e = ldap_first_entry( ld, res ); e != NULL; i++, e = ldap_next_entry( ld, e ) )
+ {
+ values[ i ] = ldap_get_dn( ld, e );
+ }
+ values[ i ] = NULL;
+
+ ldap_msgfree( res );
+
+ if ( do_retry == config->retries ) {
+ snprintf( thrstr, BUFSIZ,
+ "Read base=\"%s\" filter=\"%s\" got %d values.\n",
+ sbase, filter, nvalues );
+ thread_verbose( idx, thrstr );
+ }
+
+ for ( i = 0; i < config->loops; i++ ) {
+ int r = ((double)nvalues)*rand()/(RAND_MAX + 1.0);
+
+ do_read( ld, values[ r ],
+ srchattrs, noattrs, nobind, 1, force, idx );
+ }
+ for( i = 0; i < nvalues; i++) {
+ if (values[i] != NULL)
+ ldap_memfree( values[i] );
+ }
+ free( values );
+ break;
+
+ default:
+ tester_ldap_error( ld, "ldap_search_ext_s", NULL );
+ break;
+ }
+
+ snprintf( thrstr, BUFSIZ, "Search done (%d).\n", rc );
+ thread_verbose( idx, thrstr );
+}
+
+/* substitute a generated int into the filter */
+static void
+do_random2( LDAP *ld,
+ char *sbase, char *filter, char **srchattrs, int noattrs, int nobind,
+ int force, int idx )
+{
+ int i = 0, do_retry = config->retries;
+ int rc = LDAP_SUCCESS;
+ int lo, hi, range;
+ int flen;
+ LDAPMessage *res = NULL;
+ char *ptr, *ftail;
+ char thrstr[BUFSIZ];
+ char fbuf[BUFSIZ];
+
+ snprintf( thrstr, BUFSIZ,
+ "Read(%d): base=\"%s\", filter=\"%s\".\n",
+ config->loops, sbase, filter );
+ thread_verbose( idx, thrstr );
+
+ ptr = strchr(filter, '[');
+ if (!ptr)
+ return;
+ ftail = strchr(filter, ']');
+ if (!ftail || ftail < ptr)
+ return;
+
+ sscanf(ptr, "[%d-%d]", &lo, &hi);
+ range = hi - lo + 1;
+
+ flen = ptr - filter;
+ ftail++;
+
+ for ( i = 0; i < config->loops; i++ ) {
+ int r = ((double)range)*rand()/(RAND_MAX + 1.0);
+ sprintf(fbuf, "%.*s%d%s", flen, filter, r, ftail);
+
+ rc = ldap_search_ext_s( ld, sbase, LDAP_SCOPE_SUBTREE,
+ fbuf, srchattrs, noattrs, NULL, NULL, NULL,
+ LDAP_NO_LIMIT, &res );
+ if ( res != NULL ) {
+ ldap_msgfree( res );
+ }
+ if ( rc == 0 ) {
+ rt_pass[idx]++;
+ } else {
+ int first = tester_ignore_err( rc );
+ char buf[ BUFSIZ ];
+
+ rt_fail[idx]++;
+ snprintf( buf, sizeof( buf ), "ldap_search_ext_s(%s)", entry );
+
+ /* if ignore.. */
+ if ( first ) {
+ /* only log if first occurrence */
+ if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
+ tester_ldap_error( ld, buf, NULL );
+ }
+ continue;
+ }
+
+ /* busy needs special handling */
+ tester_ldap_error( ld, buf, NULL );
+ if ( rc == LDAP_BUSY && do_retry > 0 ) {
+ do_retry--;
+ continue;
+ }
+ break;
+ }
+ }
+
+ snprintf( thrstr, BUFSIZ, "Search done (%d).\n", rc );
+ thread_verbose( idx, thrstr );
+}
+
+static void
+do_read( LDAP *ld, char *entry,
+ char **attrs, int noattrs, int nobind, int maxloop,
+ int force, int idx )
+{
+ int i = 0, do_retry = config->retries;
+ int rc = LDAP_SUCCESS;
+ char thrstr[BUFSIZ];
+
+retry:;
+ if ( do_retry == config->retries ) {
+ snprintf( thrstr, BUFSIZ, "Read(%d): entry=\"%s\".\n",
+ maxloop, entry );
+ thread_verbose( idx, thrstr );
+ }
+
+ snprintf(thrstr, BUFSIZ, "LD %p cnt: %d (retried %d) (%s)", \
+ (void *) ld, maxloop, (do_retry - config->retries), entry);
+ thread_verbose( idx, thrstr );
+
+ for ( ; i < maxloop; i++ ) {
+ LDAPMessage *res = NULL;
+
+ rc = ldap_search_ext_s( ld, entry, LDAP_SCOPE_BASE,
+ NULL, attrs, noattrs, NULL, NULL, NULL,
+ LDAP_NO_LIMIT, &res );
+ if ( res != NULL ) {
+ ldap_msgfree( res );
+ }
+
+ if ( rc == 0 ) {
+ rt_pass[idx]++;
+ } else {
+ int first = tester_ignore_err( rc );
+ char buf[ BUFSIZ ];
+
+ rt_fail[idx]++;
+ snprintf( buf, sizeof( buf ), "ldap_search_ext_s(%s)", entry );
+
+ /* if ignore.. */
+ if ( first ) {
+ /* only log if first occurrence */
+ if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
+ tester_ldap_error( ld, buf, NULL );
+ }
+ continue;
+ }
+
+ /* busy needs special handling */
+ tester_ldap_error( ld, buf, NULL );
+ if ( rc == LDAP_BUSY && do_retry > 0 ) {
+ do_retry--;
+ goto retry;
+ }
+ break;
+ }
+ }
+}
+
+#else /* NO_THREADS */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main( int argc, char **argv )
+{
+ fprintf( stderr, "%s: not available when configured --without-threads\n", argv[0] );
+ exit( EXIT_FAILURE );
+}
+
+#endif /* NO_THREADS */
diff --git a/tests/progs/slapd-read.c b/tests/progs/slapd-read.c
new file mode 100644
index 0000000..75d8c07
--- /dev/null
+++ b/tests/progs/slapd-read.c
@@ -0,0 +1,445 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Kurt Spanier for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "ac/stdlib.h"
+
+#include "ac/ctype.h"
+#include "ac/param.h"
+#include "ac/socket.h"
+#include "ac/string.h"
+#include "ac/unistd.h"
+#include "ac/wait.h"
+
+#include "ldap.h"
+#include "lutil.h"
+
+#include "ldap_pvt.h"
+
+#include "slapd-common.h"
+
+#define LOOPS 100
+#define RETRIES 0
+
+static void
+do_read( struct tester_conn_args *config, char *entry, LDAP **ld,
+ char **attrs, int noattrs, int nobind, int maxloop, int force );
+
+static void
+do_random( struct tester_conn_args *config, char *sbase,
+ char *filter, char **attrs, int noattrs, int nobind, int force );
+
+static void
+usage( char *name, int opt )
+{
+ if ( opt ) {
+ fprintf( stderr, "%s: unable to handle option \'%c\'\n\n",
+ name, opt );
+ }
+
+ fprintf( stderr, "usage: %s " TESTER_COMMON_HELP
+ "-e <entry> "
+ "[-A] "
+ "[-F] "
+ "[-N] "
+ "[-S[S[S]]] "
+ "[-f filter] "
+ "[-T <attrs>] "
+ "[<attrs>] "
+ "\n",
+ name );
+ exit( EXIT_FAILURE );
+}
+
+/* -S: just send requests without reading responses
+ * -SS: send all requests asynchronous and immediately start reading responses
+ * -SSS: send all requests asynchronous; then read responses
+ */
+static int swamp;
+
+int
+main( int argc, char **argv )
+{
+ int i;
+ char *entry = NULL;
+ char *filter = NULL;
+ int force = 0;
+ char *srchattrs[] = { "1.1", NULL };
+ char **attrs = srchattrs;
+ int noattrs = 0;
+ int nobind = 0;
+ struct tester_conn_args *config;
+
+ config = tester_init( "slapd-read", TESTER_READ );
+
+ /* by default, tolerate referrals and no such object */
+ tester_ignore_str2errlist( "REFERRAL,NO_SUCH_OBJECT" );
+
+ while ( (i = getopt( argc, argv, TESTER_COMMON_OPTS "Ae:Ff:NST:" )) != EOF ) {
+ switch ( i ) {
+ case 'A':
+ noattrs++;
+ break;
+
+ case 'N':
+ nobind = TESTER_INIT_ONLY;
+ break;
+
+ case 'e': /* DN to search for */
+ entry = optarg;
+ break;
+
+ case 'f': /* the search request */
+ filter = optarg;
+ break;
+
+ case 'F':
+ force++;
+ break;
+
+ case 'S':
+ swamp++;
+ break;
+
+ case 'T':
+ attrs = ldap_str2charray( optarg, "," );
+ if ( attrs == NULL ) {
+ usage( argv[0], i );
+ }
+ break;
+
+ default:
+ if ( tester_config_opt( config, i, optarg ) == LDAP_SUCCESS ) {
+ break;
+ }
+ usage( argv[0], i );
+ break;
+ }
+ }
+
+ if ( entry == NULL )
+ usage( argv[0], 0 );
+
+ if ( *entry == '\0' ) {
+ fprintf( stderr, "%s: invalid EMPTY entry DN.\n",
+ argv[0] );
+ exit( EXIT_FAILURE );
+ }
+
+ if ( argv[optind] != NULL ) {
+ attrs = &argv[optind];
+ }
+
+ tester_config_finish( config );
+
+ for ( i = 0; i < config->outerloops; i++ ) {
+ if ( filter != NULL ) {
+ do_random( config, entry, filter, attrs,
+ noattrs, nobind, force );
+
+ } else {
+ do_read( config, entry, NULL, attrs,
+ noattrs, nobind, config->loops, force );
+ }
+ }
+
+ exit( EXIT_SUCCESS );
+}
+
+static void
+do_random( struct tester_conn_args *config, char *sbase, char *filter,
+ char **srchattrs, int noattrs, int nobind, int force )
+{
+ LDAP *ld = NULL;
+ int i = 0, do_retry = config->retries;
+ char *attrs[ 2 ];
+ int rc = LDAP_SUCCESS;
+ int nvalues = 0;
+ char **values = NULL;
+ LDAPMessage *res = NULL, *e = NULL;
+
+ attrs[ 0 ] = LDAP_NO_ATTRS;
+ attrs[ 1 ] = NULL;
+
+ tester_init_ld( &ld, config, nobind );
+
+ if ( do_retry == config->retries ) {
+ fprintf( stderr, "PID=%ld - Read(%d): base=\"%s\", filter=\"%s\".\n",
+ (long) pid, config->loops, sbase, filter );
+ }
+
+ rc = ldap_search_ext_s( ld, sbase, LDAP_SCOPE_SUBTREE,
+ filter, attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res );
+ switch ( rc ) {
+ case LDAP_SIZELIMIT_EXCEEDED:
+ case LDAP_TIMELIMIT_EXCEEDED:
+ case LDAP_SUCCESS:
+ nvalues = ldap_count_entries( ld, res );
+ if ( nvalues == 0 ) {
+ if ( rc ) {
+ tester_ldap_error( ld, "ldap_search_ext_s", NULL );
+ }
+ break;
+ }
+
+ values = malloc( ( nvalues + 1 ) * sizeof( char * ) );
+ if ( !values ) {
+ tester_error( "malloc failed" );
+ exit( EXIT_FAILURE );
+ }
+ for ( i = 0, e = ldap_first_entry( ld, res ); e != NULL; i++, e = ldap_next_entry( ld, e ) )
+ {
+ values[ i ] = ldap_get_dn( ld, e );
+ }
+ values[ i ] = NULL;
+
+ ldap_msgfree( res );
+
+ if ( do_retry == config->retries ) {
+ fprintf( stderr, " PID=%ld - Read base=\"%s\" filter=\"%s\" got %d values.\n",
+ (long) pid, sbase, filter, nvalues );
+ }
+
+ for ( i = 0; i < config->loops; i++ ) {
+#if 0 /* use high-order bits for better randomness (Numerical Recipes in "C") */
+ int r = rand() % nvalues;
+#endif
+ int r = ((double)nvalues)*rand()/(RAND_MAX + 1.0);
+
+ do_read( config, values[ r ], &ld,
+ srchattrs, noattrs, nobind, 1, force );
+ }
+ free( values );
+ break;
+
+ default:
+ tester_ldap_error( ld, "ldap_search_ext_s", NULL );
+ break;
+ }
+
+ fprintf( stderr, " PID=%ld - Read done (%d).\n", (long) pid, rc );
+
+ if ( ld != NULL ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ }
+}
+
+static void
+do_read( struct tester_conn_args *config, char *entry, LDAP **ldp,
+ char **attrs, int noattrs, int nobind, int maxloop, int force )
+{
+ LDAP *ld = ldp ? *ldp : NULL;
+ int i = 0, do_retry = config->retries;
+ int rc = LDAP_SUCCESS;
+ int *msgids = NULL, active = 0;
+
+ /* make room for msgid */
+ if ( swamp > 1 ) {
+ msgids = (int *)calloc( sizeof(int), maxloop );
+ if ( !msgids ) {
+ tester_error( "calloc failed" );
+ exit( EXIT_FAILURE );
+ }
+ }
+
+retry:;
+ if ( ld == NULL ) {
+ tester_init_ld( &ld, config, nobind );
+ }
+
+ if ( do_retry == config->retries ) {
+ fprintf( stderr, "PID=%ld - Read(%d): entry=\"%s\".\n",
+ (long) pid, maxloop, entry );
+ }
+
+ if ( swamp > 1 ) {
+ do {
+ LDAPMessage *res = NULL;
+ int j, msgid;
+
+ if ( i < maxloop ) {
+ rc = ldap_search_ext( ld, entry, LDAP_SCOPE_BASE,
+ NULL, attrs, noattrs, NULL, NULL,
+ NULL, LDAP_NO_LIMIT, &msgids[i] );
+
+ active++;
+#if 0
+ fprintf( stderr,
+ ">>> PID=%ld - Read maxloop=%d cnt=%d active=%d msgid=%d: "
+ "entry=\"%s\"\n",
+ (long) pid, maxloop, i, active, msgids[i],
+ entry );
+#endif
+ i++;
+
+ if ( rc ) {
+ char buf[BUFSIZ];
+ int first = tester_ignore_err( rc );
+ /* if ignore.. */
+ if ( first ) {
+ /* only log if first occurrence */
+ if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
+ tester_ldap_error( ld, "ldap_search_ext", NULL );
+ }
+ continue;
+ }
+
+ /* busy needs special handling */
+ snprintf( buf, sizeof( buf ), "entry=\"%s\"\n", entry );
+ tester_ldap_error( ld, "ldap_search_ext", buf );
+ if ( rc == LDAP_BUSY && do_retry > 0 ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ ld = NULL;
+ do_retry--;
+ goto retry;
+ }
+ break;
+ }
+
+ if ( swamp > 2 ) {
+ continue;
+ }
+ }
+
+ rc = ldap_result( ld, LDAP_RES_ANY, 0, NULL, &res );
+ switch ( rc ) {
+ case -1:
+ /* gone really bad */
+#if 0
+ fprintf( stderr,
+ ">>> PID=%ld - Read maxloop=%d cnt=%d active=%d: "
+ "entry=\"%s\" ldap_result()=%d\n",
+ (long) pid, maxloop, i, active, entry, rc );
+#endif
+ goto cleanup;
+
+ case 0:
+ /* timeout (impossible) */
+ break;
+
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_REFERENCE:
+ /* ignore */
+ break;
+
+ case LDAP_RES_SEARCH_RESULT:
+ /* just remove, no error checking (TODO?) */
+ msgid = ldap_msgid( res );
+ ldap_parse_result( ld, res, &rc, NULL, NULL, NULL, NULL, 1 );
+ res = NULL;
+
+ /* linear search, bah */
+ for ( j = 0; j < i; j++ ) {
+ if ( msgids[ j ] == msgid ) {
+ msgids[ j ] = -1;
+ active--;
+#if 0
+ fprintf( stderr,
+ "<<< PID=%ld - ReadDone maxloop=%d cnt=%d active=%d msgid=%d: "
+ "entry=\"%s\"\n",
+ (long) pid, maxloop, j, active, msgid, entry );
+#endif
+ break;
+ }
+ }
+ break;
+
+ default:
+ /* other messages unexpected */
+ fprintf( stderr,
+ "### PID=%ld - Read(%d): "
+ "entry=\"%s\" attrs=%s%s. unexpected response tag=%d\n",
+ (long) pid, maxloop,
+ entry, attrs[0], attrs[1] ? " (more...)" : "", rc );
+ break;
+ }
+
+ if ( res != NULL ) {
+ ldap_msgfree( res );
+ }
+ } while ( i < maxloop || active > 0 );
+
+ } else {
+ for ( ; i < maxloop; i++ ) {
+ LDAPMessage *res = NULL;
+
+ if (swamp) {
+ int msgid;
+ rc = ldap_search_ext( ld, entry, LDAP_SCOPE_BASE,
+ NULL, attrs, noattrs, NULL, NULL,
+ NULL, LDAP_NO_LIMIT, &msgid );
+ if ( rc == LDAP_SUCCESS ) continue;
+ else break;
+ }
+
+ rc = ldap_search_ext_s( ld, entry, LDAP_SCOPE_BASE,
+ NULL, attrs, noattrs, NULL, NULL, NULL,
+ LDAP_NO_LIMIT, &res );
+ if ( res != NULL ) {
+ ldap_msgfree( res );
+ }
+
+ if ( rc ) {
+ int first = tester_ignore_err( rc );
+ char buf[ BUFSIZ ];
+
+ snprintf( buf, sizeof( buf ), "ldap_search_ext_s(%s)", entry );
+
+ /* if ignore.. */
+ if ( first ) {
+ /* only log if first occurrence */
+ if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
+ tester_ldap_error( ld, buf, NULL );
+ }
+ continue;
+ }
+
+ /* busy needs special handling */
+ tester_ldap_error( ld, buf, NULL );
+ if ( rc == LDAP_BUSY && do_retry > 0 ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ ld = NULL;
+ do_retry--;
+ goto retry;
+ }
+ break;
+ }
+ }
+ }
+
+cleanup:;
+ if ( msgids != NULL ) {
+ free( msgids );
+ }
+
+ if ( ldp != NULL ) {
+ *ldp = ld;
+
+ } else {
+ fprintf( stderr, " PID=%ld - Read done (%d).\n", (long) pid, rc );
+
+ if ( ld != NULL ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ }
+ }
+}
+
diff --git a/tests/progs/slapd-search.c b/tests/progs/slapd-search.c
new file mode 100644
index 0000000..13a2818
--- /dev/null
+++ b/tests/progs/slapd-search.c
@@ -0,0 +1,491 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Kurt Spanier for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "ac/stdlib.h"
+
+#include "ac/ctype.h"
+#include "ac/param.h"
+#include "ac/socket.h"
+#include "ac/string.h"
+#include "ac/unistd.h"
+#include "ac/wait.h"
+
+#include "ldap.h"
+#include "lutil.h"
+#include "ldap_pvt.h"
+
+#include "slapd-common.h"
+
+#define LOOPS 100
+#define RETRIES 0
+
+static void
+do_search( struct tester_conn_args *config,
+ char *sbase, int scope, char *filter, LDAP **ldp,
+ char **attrs, int noattrs, int nobind,
+ int innerloop, int force );
+
+static void
+do_random( struct tester_conn_args *config,
+ char *sbase, int scope, char *filter, char *attr,
+ char **attrs, int noattrs, int nobind, int force );
+
+static void
+usage( char *name, char opt )
+{
+ if ( opt != '\0' ) {
+ fprintf( stderr, "unknown/incorrect option \"%c\"\n", opt );
+ }
+
+ fprintf( stderr, "usage: %s " TESTER_COMMON_HELP
+ "-b <searchbase> "
+ "-s <scope> "
+ "-f <searchfilter> "
+ "[-a <attr>] "
+ "[-A] "
+ "[-F] "
+ "[-N] "
+ "[-S[S[S]]] "
+ "[<attrs>] "
+ "\n",
+ name );
+ exit( EXIT_FAILURE );
+}
+
+/* -S: just send requests without reading responses
+ * -SS: send all requests asynchronous and immediately start reading responses
+ * -SSS: send all requests asynchronous; then read responses
+ */
+static int swamp;
+
+int
+main( int argc, char **argv )
+{
+ int i;
+ char *sbase = NULL;
+ int scope = LDAP_SCOPE_SUBTREE;
+ char *filter = NULL;
+ char *attr = NULL;
+ char *srchattrs[] = { "cn", "sn", NULL };
+ char **attrs = srchattrs;
+ int force = 0;
+ int noattrs = 0;
+ int nobind = 0;
+ struct tester_conn_args *config;
+
+ config = tester_init( "slapd-search", TESTER_SEARCH );
+
+ /* by default, tolerate referrals and no such object */
+ tester_ignore_str2errlist( "REFERRAL,NO_SUCH_OBJECT" );
+
+ while ( ( i = getopt( argc, argv, TESTER_COMMON_OPTS "Aa:b:f:FNSs:T:" ) ) != EOF )
+ {
+ switch ( i ) {
+ case 'A':
+ noattrs++;
+ break;
+
+ case 'N':
+ nobind = TESTER_INIT_ONLY;
+ break;
+
+ case 'a':
+ attr = optarg;
+ break;
+
+ case 'b': /* file with search base */
+ sbase = optarg;
+ break;
+
+ case 'f': /* the search request */
+ filter = optarg;
+ break;
+
+ case 'F':
+ force++;
+ break;
+
+ case 'T':
+ attrs = ldap_str2charray( optarg, "," );
+ if ( attrs == NULL ) {
+ usage( argv[0], i );
+ }
+ break;
+
+ case 'S':
+ swamp++;
+ break;
+
+ case 's':
+ scope = ldap_pvt_str2scope( optarg );
+ if ( scope == -1 ) {
+ usage( argv[0], i );
+ }
+ break;
+
+ default:
+ if ( tester_config_opt( config, i, optarg ) == LDAP_SUCCESS ) {
+ break;
+ }
+ usage( argv[0], i );
+ break;
+ }
+ }
+
+ if (( sbase == NULL ) || ( filter == NULL ))
+ usage( argv[0], 0 );
+
+ if ( *filter == '\0' ) {
+
+ fprintf( stderr, "%s: invalid EMPTY search filter.\n",
+ argv[0] );
+ exit( EXIT_FAILURE );
+
+ }
+
+ if ( argv[optind] != NULL ) {
+ attrs = &argv[optind];
+ }
+
+ tester_config_finish( config );
+
+ for ( i = 0; i < config->outerloops; i++ ) {
+ if ( attr != NULL ) {
+ do_random( config,
+ sbase, scope, filter, attr,
+ attrs, noattrs, nobind, force );
+
+ } else {
+ do_search( config, sbase, scope, filter,
+ NULL, attrs, noattrs, nobind,
+ config->loops, force );
+ }
+ }
+
+ exit( EXIT_SUCCESS );
+}
+
+
+static void
+do_random( struct tester_conn_args *config,
+ char *sbase, int scope, char *filter, char *attr,
+ char **srchattrs, int noattrs, int nobind, int force )
+{
+ LDAP *ld = NULL;
+ int i = 0, do_retry = config->retries;
+ char *attrs[ 2 ];
+ int rc = LDAP_SUCCESS;
+ int nvalues = 0;
+ char **values = NULL;
+ LDAPMessage *res = NULL, *e = NULL;
+
+ attrs[ 0 ] = attr;
+ attrs[ 1 ] = NULL;
+
+ tester_init_ld( &ld, config, nobind );
+
+ rc = ldap_search_ext_s( ld, sbase, LDAP_SCOPE_SUBTREE,
+ filter, attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res );
+ switch ( rc ) {
+ case LDAP_SIZELIMIT_EXCEEDED:
+ case LDAP_TIMELIMIT_EXCEEDED:
+ case LDAP_SUCCESS:
+ if ( ldap_count_entries( ld, res ) == 0 ) {
+ if ( rc ) {
+ tester_ldap_error( ld, "ldap_search_ext_s", NULL );
+ }
+ break;
+ }
+
+ for ( e = ldap_first_entry( ld, res ); e != NULL; e = ldap_next_entry( ld, e ) )
+ {
+ struct berval **v = ldap_get_values_len( ld, e, attr );
+
+ if ( v != NULL ) {
+ int n = ldap_count_values_len( v );
+ int j;
+
+ values = realloc( values, ( nvalues + n + 1 )*sizeof( char * ) );
+ if ( !values ) {
+ tester_error( "realloc failed" );
+ exit( EXIT_FAILURE );
+ }
+ for ( j = 0; j < n; j++ ) {
+ values[ nvalues + j ] = strdup( v[ j ]->bv_val );
+ }
+ values[ nvalues + j ] = NULL;
+ nvalues += n;
+ ldap_value_free_len( v );
+ }
+ }
+
+ ldap_msgfree( res );
+
+ if ( !values ) {
+ fprintf( stderr, " PID=%ld - Search base=\"%s\" filter=\"%s\" got %d values.\n",
+ (long) pid, sbase, filter, nvalues );
+ exit(EXIT_FAILURE);
+ }
+
+ if ( do_retry == config->retries ) {
+ fprintf( stderr, " PID=%ld - Search base=\"%s\" filter=\"%s\" got %d values.\n",
+ (long) pid, sbase, filter, nvalues );
+ }
+
+ for ( i = 0; i < config->loops; i++ ) {
+ char buf[ BUFSIZ ];
+#if 0 /* use high-order bits for better randomness (Numerical Recipes in "C") */
+ int r = rand() % nvalues;
+#endif
+ int r = ((double)nvalues)*rand()/(RAND_MAX + 1.0);
+
+ snprintf( buf, sizeof( buf ), "(%s=%s)", attr, values[ r ] );
+
+ do_search( config,
+ sbase, scope, buf, &ld,
+ srchattrs, noattrs, nobind,
+ 1, force );
+ }
+ break;
+
+ default:
+ tester_ldap_error( ld, "ldap_search_ext_s", NULL );
+ break;
+ }
+
+ fprintf( stderr, " PID=%ld - Search done (%d).\n", (long) pid, rc );
+
+ if ( values ) {
+ for ( i = 0; i < nvalues; i++ ) {
+ free( values[i] );
+ }
+ free( values );
+ }
+
+ if ( ld != NULL ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ }
+}
+
+static void
+do_search( struct tester_conn_args *config,
+ char *sbase, int scope, char *filter, LDAP **ldp,
+ char **attrs, int noattrs, int nobind,
+ int innerloop, int force )
+{
+ LDAP *ld = ldp ? *ldp : NULL;
+ int i = 0, do_retry = config->retries;
+ int rc = LDAP_SUCCESS;
+ char buf[ BUFSIZ ];
+ int *msgids = NULL, active = 0;
+
+ /* make room for msgid */
+ if ( swamp > 1 ) {
+ msgids = (int *)calloc( sizeof(int), innerloop );
+ if ( !msgids ) {
+ tester_error( "calloc failed" );
+ exit( EXIT_FAILURE );
+ }
+ }
+
+retry:;
+ if ( ld == NULL ) {
+ fprintf( stderr,
+ "PID=%ld - Search(%d): "
+ "base=\"%s\" scope=%s filter=\"%s\" "
+ "attrs=%s%s.\n",
+ (long) pid, innerloop,
+ sbase, ldap_pvt_scope2str( scope ), filter,
+ attrs[0], attrs[1] ? " (more...)" : "" );
+
+ tester_init_ld( &ld, config, nobind );
+ }
+
+ if ( swamp > 1 ) {
+ do {
+ LDAPMessage *res = NULL;
+ int j, msgid;
+
+ if ( i < innerloop ) {
+ rc = ldap_search_ext( ld, sbase, scope,
+ filter, NULL, noattrs, NULL, NULL,
+ NULL, LDAP_NO_LIMIT, &msgids[i] );
+
+ active++;
+#if 0
+ fprintf( stderr,
+ ">>> PID=%ld - Search maxloop=%d cnt=%d active=%d msgid=%d: "
+ "base=\"%s\" scope=%s filter=\"%s\"\n",
+ (long) pid, innerloop, i, active, msgids[i],
+ sbase, ldap_pvt_scope2str( scope ), filter );
+#endif
+ i++;
+
+ if ( rc ) {
+ int first = tester_ignore_err( rc );
+ /* if ignore.. */
+ if ( first ) {
+ /* only log if first occurrence */
+ if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
+ tester_ldap_error( ld, "ldap_search_ext", NULL );
+ }
+ continue;
+ }
+
+ /* busy needs special handling */
+ snprintf( buf, sizeof( buf ),
+ "base=\"%s\" filter=\"%s\"\n",
+ sbase, filter );
+ tester_ldap_error( ld, "ldap_search_ext", buf );
+ if ( rc == LDAP_BUSY && do_retry > 0 ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ ld = NULL;
+ do_retry--;
+ goto retry;
+ }
+ break;
+ }
+
+ if ( swamp > 2 ) {
+ continue;
+ }
+ }
+
+ rc = ldap_result( ld, LDAP_RES_ANY, 0, NULL, &res );
+ switch ( rc ) {
+ case -1:
+ /* gone really bad */
+ goto cleanup;
+
+ case 0:
+ /* timeout (impossible) */
+ break;
+
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_REFERENCE:
+ /* ignore */
+ break;
+
+ case LDAP_RES_SEARCH_RESULT:
+ /* just remove, no error checking (TODO?) */
+ msgid = ldap_msgid( res );
+ ldap_parse_result( ld, res, &rc, NULL, NULL, NULL, NULL, 1 );
+ res = NULL;
+
+ /* linear search, bah */
+ for ( j = 0; j < i; j++ ) {
+ if ( msgids[ j ] == msgid ) {
+ msgids[ j ] = -1;
+ active--;
+#if 0
+ fprintf( stderr,
+ "<<< PID=%ld - SearchDone maxloop=%d cnt=%d active=%d msgid=%d: "
+ "base=\"%s\" scope=%s filter=\"%s\"\n",
+ (long) pid, innerloop, j, active, msgid,
+ sbase, ldap_pvt_scope2str( scope ), filter );
+#endif
+ break;
+ }
+ }
+ break;
+
+ default:
+ /* other messages unexpected */
+ fprintf( stderr,
+ "### PID=%ld - Search(%d): "
+ "base=\"%s\" scope=%s filter=\"%s\" "
+ "attrs=%s%s. unexpected response tag=%d\n",
+ (long) pid, innerloop,
+ sbase, ldap_pvt_scope2str( scope ), filter,
+ attrs[0], attrs[1] ? " (more...)" : "", rc );
+ break;
+ }
+
+ if ( res != NULL ) {
+ ldap_msgfree( res );
+ }
+ } while ( i < innerloop || active > 0 );
+
+ } else {
+ for ( ; i < innerloop; i++ ) {
+ LDAPMessage *res = NULL;
+
+ if (swamp) {
+ int msgid;
+ rc = ldap_search_ext( ld, sbase, scope,
+ filter, NULL, noattrs, NULL, NULL,
+ NULL, LDAP_NO_LIMIT, &msgid );
+ if ( rc == LDAP_SUCCESS ) continue;
+ else break;
+ }
+
+ rc = ldap_search_ext_s( ld, sbase, scope,
+ filter, attrs, noattrs, NULL, NULL,
+ NULL, LDAP_NO_LIMIT, &res );
+ if ( res != NULL ) {
+ ldap_msgfree( res );
+ }
+
+ if ( rc ) {
+ int first = tester_ignore_err( rc );
+ /* if ignore.. */
+ if ( first ) {
+ /* only log if first occurrence */
+ if ( ( force < 2 && first > 0 ) || abs(first) == 1 ) {
+ tester_ldap_error( ld, "ldap_search_ext_s", NULL );
+ }
+ continue;
+ }
+
+ /* busy needs special handling */
+ snprintf( buf, sizeof( buf ),
+ "base=\"%s\" filter=\"%s\"\n",
+ sbase, filter );
+ tester_ldap_error( ld, "ldap_search_ext_s", buf );
+ if ( rc == LDAP_BUSY && do_retry > 0 ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ ld = NULL;
+ do_retry--;
+ goto retry;
+ }
+ break;
+ }
+ }
+ }
+
+cleanup:;
+ if ( msgids != NULL ) {
+ free( msgids );
+ }
+
+ if ( ldp != NULL ) {
+ *ldp = ld;
+
+ } else {
+ fprintf( stderr, " PID=%ld - Search done (%d).\n", (long) pid, rc );
+
+ if ( ld != NULL ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ }
+ }
+}
diff --git a/tests/progs/slapd-tester.c b/tests/progs/slapd-tester.c
new file mode 100644
index 0000000..7ad88b8
--- /dev/null
+++ b/tests/progs/slapd-tester.c
@@ -0,0 +1,1143 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Kurt Spanier for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "ac/stdlib.h"
+
+#include "ac/ctype.h"
+#include "ac/dirent.h"
+#include "ac/param.h"
+#include "ac/socket.h"
+#include "ac/string.h"
+#include "ac/unistd.h"
+#include "ac/wait.h"
+
+
+#include "ldap_defaults.h"
+#include "lutil.h"
+
+#include "ldap.h"
+#include "ldap_pvt.h"
+#include "lber_pvt.h"
+#include "slapd-common.h"
+
+#ifdef _WIN32
+#define EXE ".exe"
+#else
+#define EXE
+#endif
+
+#define SEARCHCMD "slapd-search" EXE
+#define READCMD "slapd-read" EXE
+#define ADDCMD "slapd-addel" EXE
+#define MODRDNCMD "slapd-modrdn" EXE
+#define MODIFYCMD "slapd-modify" EXE
+#define BINDCMD "slapd-bind" EXE
+#define MAXARGS 100
+#define MAXREQS 5000
+#define LOOPS 100
+#define OUTERLOOPS "1"
+#define RETRIES "0"
+
+#define TSEARCHFILE "do_search.0"
+#define TREADFILE "do_read.0"
+#define TADDFILE "do_add."
+#define TMODRDNFILE "do_modrdn.0"
+#define TMODIFYFILE "do_modify.0"
+#define TBINDFILE "do_bind.0"
+
+static char *get_file_name( char *dirname, char *filename );
+static int get_search_filters( char *filename, char *filters[], char *attrs[], char *bases[], LDAPURLDesc *luds[] );
+static int get_read_entries( char *filename, char *entries[], char *filters[] );
+static void fork_child( char *prog, char **args );
+static void wait4kids( int nkidval );
+
+static int maxkids = 20;
+static int nkids;
+
+#ifdef HAVE_WINSOCK
+static HANDLE *children;
+static char argbuf[BUFSIZ];
+#define ArgDup(x) strdup(strcat(strcat(strcpy(argbuf,"\""),x),"\""))
+#else
+#define ArgDup(x) strdup(x)
+#endif
+
+static void
+usage( char *name, char opt )
+{
+ if ( opt ) {
+ fprintf( stderr, "%s: unable to handle option \'%c\'\n\n",
+ name, opt );
+ }
+
+ fprintf( stderr,
+ "usage: %s "
+ "-H <uri> "
+ "-D <manager> "
+ "-w <passwd> "
+ "-d <datadir> "
+ "[-i <ignore>] "
+ "[-j <maxchild>] "
+ "[-l {<loops>|<type>=<loops>[,...]}] "
+ "[-L <outerloops>] "
+ "-P <progdir> "
+ "[-r <maxretries>] "
+ "[-t <delay>] "
+ "[-C] "
+ "[-F] "
+ "[-I] "
+ "[-N]\n",
+ name );
+ exit( EXIT_FAILURE );
+}
+
+int
+main( int argc, char **argv )
+{
+ int i, j;
+ char *uri = NULL;
+ char *manager = NULL;
+ char *passwd = NULL;
+ char *dirname = NULL;
+ char *progdir = NULL;
+ int loops = LOOPS;
+ char *outerloops = OUTERLOOPS;
+ char *retries = RETRIES;
+ char *delay = "0";
+ DIR *datadir;
+ struct dirent *file;
+ int friendly = 0;
+ int chaserefs = 0;
+ int noattrs = 0;
+ int nobind = 0;
+ int noinit = 1;
+ char *ignore = NULL;
+ /* search */
+ char *sfile = NULL;
+ char *sreqs[MAXREQS];
+ char *sattrs[MAXREQS];
+ char *sbase[MAXREQS];
+ LDAPURLDesc *slud[MAXREQS];
+ int snum = 0;
+ char *sargs[MAXARGS];
+ int sanum;
+ int sextra_args = 0;
+ char scmd[MAXPATHLEN];
+ int swamp = 0;
+ char swampopt[sizeof("-SSS")];
+ /* static so that its address can be used in initializer below. */
+ static char sloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
+ /* read */
+ char *rfile = NULL;
+ char *rreqs[MAXREQS];
+ int rnum = 0;
+ char *rargs[MAXARGS];
+ char *rflts[MAXREQS];
+ int ranum;
+ int rextra_args = 0;
+ char rcmd[MAXPATHLEN];
+ static char rloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
+ /* addel */
+ char *afiles[MAXREQS];
+ int anum = 0;
+ char *aargs[MAXARGS];
+ int aanum;
+ char acmd[MAXPATHLEN];
+ static char aloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
+ /* modrdn */
+ char *nfile = NULL;
+ char *nreqs[MAXREQS];
+ int nnum = 0;
+ char *nargs[MAXARGS];
+ int nanum;
+ char ncmd[MAXPATHLEN];
+ static char nloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
+ /* modify */
+ char *mfile = NULL;
+ char *mreqs[MAXREQS];
+ char *mdn[MAXREQS];
+ int mnum = 0;
+ char *margs[MAXARGS];
+ int manum;
+ char mcmd[MAXPATHLEN];
+ static char mloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
+ /* bind */
+ char *bfile = NULL;
+ char *breqs[MAXREQS];
+ char *bcreds[MAXREQS];
+ char *battrs[MAXREQS];
+ int bnum = 0;
+ char *bargs[MAXARGS];
+ int banum;
+ char bcmd[MAXPATHLEN];
+ static char bloops[LDAP_PVT_INTTYPE_CHARS(unsigned long)];
+ char **bargs_extra = NULL;
+
+ char *friendlyOpt = NULL;
+ int pw_ask = 0;
+ char *pw_file = NULL;
+
+ /* extra action to do after bind... */
+ typedef struct extra_t {
+ char *action;
+ struct extra_t *next;
+ } extra_t;
+
+ extra_t *extra = NULL;
+ int nextra = 0;
+
+ tester_init( "slapd-tester", TESTER_TESTER );
+
+ sloops[0] = '\0';
+ rloops[0] = '\0';
+ aloops[0] = '\0';
+ nloops[0] = '\0';
+ mloops[0] = '\0';
+ bloops[0] = '\0';
+
+ while ( ( i = getopt( argc, argv, "AB:CD:d:FH:h:Ii:j:L:l:NP:p:r:St:Ww:y:" ) ) != EOF )
+ {
+ switch ( i ) {
+ case 'A':
+ noattrs++;
+ break;
+
+ case 'B': {
+ char **p,
+ **b = ldap_str2charray( optarg, "," );
+ extra_t **epp;
+
+ for ( epp = &extra; *epp; epp = &(*epp)->next )
+ ;
+
+ for ( p = b; p[0]; p++ ) {
+ *epp = calloc( 1, sizeof( extra_t ) );
+ (*epp)->action = p[0];
+ epp = &(*epp)->next;
+ nextra++;
+ }
+
+ ldap_memfree( b );
+ } break;
+
+ case 'C':
+ chaserefs++;
+ break;
+
+ case 'D': /* slapd manager */
+ manager = ArgDup( optarg );
+ break;
+
+ case 'd': /* data directory */
+ dirname = optarg;
+ break;
+
+ case 'F':
+ friendly++;
+ break;
+
+ case 'H': /* slapd uri */
+ uri = optarg;
+ break;
+
+ case 'I':
+ noinit = 0;
+ break;
+
+ case 'i':
+ ignore = optarg;
+ break;
+
+ case 'j': /* the number of parallel clients */
+ if ( lutil_atoi( &maxkids, optarg ) != 0 ) {
+ usage( argv[0], 'j' );
+ }
+ break;
+
+ case 'l': /* the number of loops per client */
+ if ( !isdigit( (unsigned char) optarg[0] ) ) {
+ char **p,
+ **l = ldap_str2charray( optarg, "," );
+
+ for ( p = l; p[0]; p++) {
+ struct {
+ struct berval type;
+ char *buf;
+ } types[] = {
+ { BER_BVC( "add=" ), aloops },
+ { BER_BVC( "bind=" ), bloops },
+ { BER_BVC( "modify=" ), mloops },
+ { BER_BVC( "modrdn=" ), nloops },
+ { BER_BVC( "read=" ), rloops },
+ { BER_BVC( "search=" ), sloops },
+ { BER_BVNULL, NULL }
+ };
+ int c, n;
+
+ for ( c = 0; types[c].type.bv_val; c++ ) {
+ if ( strncasecmp( p[0], types[c].type.bv_val, types[c].type.bv_len ) == 0 ) {
+ break;
+ }
+ }
+
+ if ( types[c].type.bv_val == NULL ) {
+ usage( argv[0], 'l' );
+ }
+
+ if ( lutil_atoi( &n, &p[0][types[c].type.bv_len] ) != 0 ) {
+ usage( argv[0], 'l' );
+ }
+
+ snprintf( types[c].buf, sizeof( aloops ), "%d", n );
+ }
+
+ ldap_charray_free( l );
+
+ } else if ( lutil_atoi( &loops, optarg ) != 0 ) {
+ usage( argv[0], 'l' );
+ }
+ break;
+
+ case 'L': /* the number of outerloops per client */
+ outerloops = optarg;
+ break;
+
+ case 'N':
+ nobind++;
+ break;
+
+ case 'P': /* prog directory */
+ progdir = optarg;
+ break;
+
+ case 'r': /* the number of retries in case of error */
+ retries = optarg;
+ break;
+
+ case 'S':
+ swamp++;
+ break;
+
+ case 't': /* the delay in seconds between each retry */
+ delay = optarg;
+ break;
+
+ case 'w': /* the managers passwd */
+ passwd = ArgDup( optarg );
+ memset( optarg, '*', strlen( optarg ) );
+ break;
+
+ case 'W':
+ pw_ask++;
+ break;
+
+ case 'y':
+ pw_file = optarg;
+ break;
+
+ default:
+ usage( argv[0], '\0' );
+ break;
+ }
+ }
+
+ if (( dirname == NULL ) || ( uri == NULL ) ||
+ ( manager == NULL ) || ( passwd == NULL ) || ( progdir == NULL ))
+ {
+ usage( argv[0], '\0' );
+ }
+
+#ifdef HAVE_WINSOCK
+ children = malloc( maxkids * sizeof(HANDLE) );
+#endif
+ /* get the file list */
+ if ( ( datadir = opendir( dirname )) == NULL ) {
+ fprintf( stderr, "%s: couldn't open data directory \"%s\".\n",
+ argv[0], dirname );
+ exit( EXIT_FAILURE );
+ }
+
+ /* look for search, read, modrdn, and add/delete files */
+ for ( file = readdir( datadir ); file; file = readdir( datadir )) {
+
+ if ( !strcasecmp( file->d_name, TSEARCHFILE )) {
+ sfile = get_file_name( dirname, file->d_name );
+ continue;
+ } else if ( !strcasecmp( file->d_name, TREADFILE )) {
+ rfile = get_file_name( dirname, file->d_name );
+ continue;
+ } else if ( !strcasecmp( file->d_name, TMODRDNFILE )) {
+ nfile = get_file_name( dirname, file->d_name );
+ continue;
+ } else if ( !strcasecmp( file->d_name, TMODIFYFILE )) {
+ mfile = get_file_name( dirname, file->d_name );
+ continue;
+ } else if ( !strncasecmp( file->d_name, TADDFILE, strlen( TADDFILE ))
+ && ( anum < MAXREQS )) {
+ afiles[anum++] = get_file_name( dirname, file->d_name );
+ continue;
+ } else if ( !strcasecmp( file->d_name, TBINDFILE )) {
+ bfile = get_file_name( dirname, file->d_name );
+ continue;
+ }
+ }
+
+ closedir( datadir );
+
+ if ( pw_ask ) {
+ passwd = getpassphrase( _("Enter LDAP Password: ") );
+
+ } else if ( pw_file ) {
+ struct berval pw;
+
+ if ( lutil_get_filed_password( pw_file, &pw ) ) {
+ exit( EXIT_FAILURE );
+ }
+
+ passwd = pw.bv_val;
+ }
+
+ if ( !sfile && !rfile && !nfile && !mfile && !bfile && !anum ) {
+ fprintf( stderr, "no data files found.\n" );
+ exit( EXIT_FAILURE );
+ }
+
+ /* look for search requests */
+ if ( sfile ) {
+ snum = get_search_filters( sfile, sreqs, sattrs, sbase, slud );
+ if ( snum < 0 ) {
+ fprintf( stderr,
+ "unable to parse file \"%s\" line %d\n",
+ sfile, -2*(snum + 1));
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ /* look for read requests */
+ if ( rfile ) {
+ rnum = get_read_entries( rfile, rreqs, rflts );
+ if ( rnum < 0 ) {
+ fprintf( stderr,
+ "unable to parse file \"%s\" line %d\n",
+ rfile, -2*(rnum + 1) );
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ /* look for modrdn requests */
+ if ( nfile ) {
+ nnum = get_read_entries( nfile, nreqs, NULL );
+ if ( nnum < 0 ) {
+ fprintf( stderr,
+ "unable to parse file \"%s\" line %d\n",
+ nfile, -2*(nnum + 1) );
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ /* look for modify requests */
+ if ( mfile ) {
+ mnum = get_search_filters( mfile, mreqs, NULL, mdn, NULL );
+ if ( mnum < 0 ) {
+ fprintf( stderr,
+ "unable to parse file \"%s\" line %d\n",
+ mfile, -2*(mnum + 1) );
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ /* look for bind requests */
+ if ( bfile ) {
+ bnum = get_search_filters( bfile, bcreds, battrs, breqs, NULL );
+ if ( bnum < 0 ) {
+ fprintf( stderr,
+ "unable to parse file \"%s\" line %d\n",
+ bfile, -2*(bnum + 1) );
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ /* setup friendly option */
+ switch ( friendly ) {
+ case 0:
+ break;
+
+ case 1:
+ friendlyOpt = "-F";
+ break;
+
+ default:
+ /* NOTE: right now we don't need it more than twice */
+ case 2:
+ friendlyOpt = "-FF";
+ break;
+ }
+
+ /* setup swamp option */
+ if ( swamp ) {
+ swampopt[0] = '-';
+ if ( swamp > 3 ) swamp = 3;
+ swampopt[swamp + 1] = '\0';
+ for ( ; swamp-- > 0; ) swampopt[swamp + 1] = 'S';
+ }
+
+ /* setup loop options */
+ if ( sloops[0] == '\0' ) snprintf( sloops, sizeof( sloops ), "%d", 10 * loops );
+ if ( rloops[0] == '\0' ) snprintf( rloops, sizeof( rloops ), "%d", 20 * loops );
+ if ( aloops[0] == '\0' ) snprintf( aloops, sizeof( aloops ), "%d", loops );
+ if ( nloops[0] == '\0' ) snprintf( nloops, sizeof( nloops ), "%d", loops );
+ if ( mloops[0] == '\0' ) snprintf( mloops, sizeof( mloops ), "%d", loops );
+ if ( bloops[0] == '\0' ) snprintf( bloops, sizeof( bloops ), "%d", 20 * loops );
+
+ /*
+ * generate the search clients
+ */
+
+ sanum = 0;
+ snprintf( scmd, sizeof scmd, "%s" LDAP_DIRSEP SEARCHCMD,
+ progdir );
+ sargs[sanum++] = scmd;
+ sargs[sanum++] = "-H";
+ sargs[sanum++] = uri;
+ sargs[sanum++] = "-D";
+ sargs[sanum++] = manager;
+ sargs[sanum++] = "-w";
+ sargs[sanum++] = passwd;
+ sargs[sanum++] = "-l";
+ sargs[sanum++] = sloops;
+ sargs[sanum++] = "-L";
+ sargs[sanum++] = outerloops;
+ sargs[sanum++] = "-r";
+ sargs[sanum++] = retries;
+ sargs[sanum++] = "-t";
+ sargs[sanum++] = delay;
+ if ( friendly ) {
+ sargs[sanum++] = friendlyOpt;
+ }
+ if ( chaserefs ) {
+ sargs[sanum++] = "-C";
+ }
+ if ( noattrs ) {
+ sargs[sanum++] = "-A";
+ }
+ if ( nobind ) {
+ sargs[sanum++] = "-N";
+ }
+ if ( ignore ) {
+ sargs[sanum++] = "-i";
+ sargs[sanum++] = ignore;
+ }
+ if ( swamp ) {
+ sargs[sanum++] = swampopt;
+ }
+ sargs[sanum++] = "-b";
+ sargs[sanum++] = NULL; /* will hold the search base */
+ sargs[sanum++] = "-s";
+ sargs[sanum++] = NULL; /* will hold the search scope */
+ sargs[sanum++] = "-f";
+ sargs[sanum++] = NULL; /* will hold the search request */
+
+ sargs[sanum++] = NULL;
+ sargs[sanum++] = NULL; /* might hold the "attr" request */
+ sextra_args += 2;
+
+ sargs[sanum] = NULL;
+
+ /*
+ * generate the read clients
+ */
+
+ ranum = 0;
+ snprintf( rcmd, sizeof rcmd, "%s" LDAP_DIRSEP READCMD,
+ progdir );
+ rargs[ranum++] = rcmd;
+ rargs[ranum++] = "-H";
+ rargs[ranum++] = uri;
+ rargs[ranum++] = "-D";
+ rargs[ranum++] = manager;
+ rargs[ranum++] = "-w";
+ rargs[ranum++] = passwd;
+ rargs[ranum++] = "-l";
+ rargs[ranum++] = rloops;
+ rargs[ranum++] = "-L";
+ rargs[ranum++] = outerloops;
+ rargs[ranum++] = "-r";
+ rargs[ranum++] = retries;
+ rargs[ranum++] = "-t";
+ rargs[ranum++] = delay;
+ if ( friendly ) {
+ rargs[ranum++] = friendlyOpt;
+ }
+ if ( chaserefs ) {
+ rargs[ranum++] = "-C";
+ }
+ if ( noattrs ) {
+ rargs[ranum++] = "-A";
+ }
+ if ( ignore ) {
+ rargs[ranum++] = "-i";
+ rargs[ranum++] = ignore;
+ }
+ if ( swamp ) {
+ rargs[ranum++] = swampopt;
+ }
+ rargs[ranum++] = "-e";
+ rargs[ranum++] = NULL; /* will hold the read entry */
+
+ rargs[ranum++] = NULL;
+ rargs[ranum++] = NULL; /* might hold the filter arg */
+ rextra_args += 2;
+
+ rargs[ranum] = NULL;
+
+ /*
+ * generate the modrdn clients
+ */
+
+ nanum = 0;
+ snprintf( ncmd, sizeof ncmd, "%s" LDAP_DIRSEP MODRDNCMD,
+ progdir );
+ nargs[nanum++] = ncmd;
+ nargs[nanum++] = "-H";
+ nargs[nanum++] = uri;
+ nargs[nanum++] = "-D";
+ nargs[nanum++] = manager;
+ nargs[nanum++] = "-w";
+ nargs[nanum++] = passwd;
+ nargs[nanum++] = "-l";
+ nargs[nanum++] = nloops;
+ nargs[nanum++] = "-L";
+ nargs[nanum++] = outerloops;
+ nargs[nanum++] = "-r";
+ nargs[nanum++] = retries;
+ nargs[nanum++] = "-t";
+ nargs[nanum++] = delay;
+ if ( friendly ) {
+ nargs[nanum++] = friendlyOpt;
+ }
+ if ( chaserefs ) {
+ nargs[nanum++] = "-C";
+ }
+ if ( ignore ) {
+ nargs[nanum++] = "-i";
+ nargs[nanum++] = ignore;
+ }
+ nargs[nanum++] = "-e";
+ nargs[nanum++] = NULL; /* will hold the modrdn entry */
+ nargs[nanum] = NULL;
+
+ /*
+ * generate the modify clients
+ */
+
+ manum = 0;
+ snprintf( mcmd, sizeof mcmd, "%s" LDAP_DIRSEP MODIFYCMD,
+ progdir );
+ margs[manum++] = mcmd;
+ margs[manum++] = "-H";
+ margs[manum++] = uri;
+ margs[manum++] = "-D";
+ margs[manum++] = manager;
+ margs[manum++] = "-w";
+ margs[manum++] = passwd;
+ margs[manum++] = "-l";
+ margs[manum++] = mloops;
+ margs[manum++] = "-L";
+ margs[manum++] = outerloops;
+ margs[manum++] = "-r";
+ margs[manum++] = retries;
+ margs[manum++] = "-t";
+ margs[manum++] = delay;
+ if ( friendly ) {
+ margs[manum++] = friendlyOpt;
+ }
+ if ( chaserefs ) {
+ margs[manum++] = "-C";
+ }
+ if ( ignore ) {
+ margs[manum++] = "-i";
+ margs[manum++] = ignore;
+ }
+ margs[manum++] = "-e";
+ margs[manum++] = NULL; /* will hold the modify entry */
+ margs[manum++] = "-a";;
+ margs[manum++] = NULL; /* will hold the ava */
+ margs[manum] = NULL;
+
+ /*
+ * generate the add/delete clients
+ */
+
+ aanum = 0;
+ snprintf( acmd, sizeof acmd, "%s" LDAP_DIRSEP ADDCMD,
+ progdir );
+ aargs[aanum++] = acmd;
+ aargs[aanum++] = "-H";
+ aargs[aanum++] = uri;
+ aargs[aanum++] = "-D";
+ aargs[aanum++] = manager;
+ aargs[aanum++] = "-w";
+ aargs[aanum++] = passwd;
+ aargs[aanum++] = "-l";
+ aargs[aanum++] = aloops;
+ aargs[aanum++] = "-L";
+ aargs[aanum++] = outerloops;
+ aargs[aanum++] = "-r";
+ aargs[aanum++] = retries;
+ aargs[aanum++] = "-t";
+ aargs[aanum++] = delay;
+ if ( friendly ) {
+ aargs[aanum++] = friendlyOpt;
+ }
+ if ( chaserefs ) {
+ aargs[aanum++] = "-C";
+ }
+ if ( ignore ) {
+ aargs[aanum++] = "-i";
+ aargs[aanum++] = ignore;
+ }
+ aargs[aanum++] = "-f";
+ aargs[aanum++] = NULL; /* will hold the add data file */
+ aargs[aanum] = NULL;
+
+ /*
+ * generate the bind clients
+ */
+
+ banum = 0;
+ snprintf( bcmd, sizeof bcmd, "%s" LDAP_DIRSEP BINDCMD,
+ progdir );
+ bargs[banum++] = bcmd;
+ if ( !noinit ) {
+ bargs[banum++] = "-I"; /* init on each bind */
+ }
+ bargs[banum++] = "-H";
+ bargs[banum++] = uri;
+ bargs[banum++] = "-l";
+ bargs[banum++] = bloops;
+ bargs[banum++] = "-L";
+ bargs[banum++] = outerloops;
+ bargs[banum++] = "-r";
+ bargs[banum++] = retries;
+ bargs[banum++] = "-t";
+ bargs[banum++] = delay;
+ if ( friendly ) {
+ bargs[banum++] = friendlyOpt;
+ }
+ if ( chaserefs ) {
+ bargs[banum++] = "-C";
+ }
+ if ( ignore ) {
+ bargs[banum++] = "-i";
+ bargs[banum++] = ignore;
+ }
+ if ( nextra ) {
+ bargs[banum++] = "-B";
+ bargs_extra = &bargs[banum++];
+ }
+ bargs[banum++] = "-D";
+ bargs[banum++] = NULL;
+ bargs[banum++] = "-w";
+ bargs[banum++] = NULL;
+ bargs[banum] = NULL;
+
+#define DOREQ(n,j) ((n) && ((maxkids > (n)) ? ((j) < maxkids ) : ((j) < (n))))
+
+ for ( j = 0; j < MAXREQS; j++ ) {
+ /* search */
+ if ( DOREQ( snum, j ) ) {
+ int jj = j % snum;
+ int x = sanum - sextra_args;
+
+ /* base */
+ if ( sbase[jj] != NULL ) {
+ sargs[sanum - 7] = sbase[jj];
+
+ } else {
+ sargs[sanum - 7] = slud[jj]->lud_dn;
+ }
+
+ /* scope */
+ if ( slud[jj] != NULL ) {
+ sargs[sanum - 5] = (char *)ldap_pvt_scope2str( slud[jj]->lud_scope );
+
+ } else {
+ sargs[sanum - 5] = "sub";
+ }
+
+ /* filter */
+ if ( sreqs[jj] != NULL ) {
+ sargs[sanum - 3] = sreqs[jj];
+
+ } else if ( slud[jj]->lud_filter != NULL ) {
+ sargs[sanum - 3] = slud[jj]->lud_filter;
+
+ } else {
+ sargs[sanum - 3] = "(objectClass=*)";
+ }
+
+ /* extras */
+ sargs[x] = NULL;
+
+ /* attr */
+ if ( sattrs[jj] != NULL ) {
+ sargs[x++] = "-a";
+ sargs[x++] = sattrs[jj];
+ }
+
+ /* attrs */
+ if ( slud[jj] != NULL && slud[jj]->lud_attrs != NULL ) {
+ int i;
+
+ for ( i = 0; slud[jj]->lud_attrs[ i ] != NULL && x + i < MAXARGS - 1; i++ ) {
+ sargs[x + i] = slud[jj]->lud_attrs[ i ];
+ }
+ sargs[x + i] = NULL;
+ }
+
+ fork_child( scmd, sargs );
+ }
+
+ /* read */
+ if ( DOREQ( rnum, j ) ) {
+ int jj = j % rnum;
+ int x = ranum - rextra_args;
+
+ rargs[ranum - 3] = rreqs[jj];
+ if ( rflts[jj] != NULL ) {
+ rargs[x++] = "-f";
+ rargs[x++] = rflts[jj];
+ }
+ rargs[x] = NULL;
+ fork_child( rcmd, rargs );
+ }
+
+ /* rename */
+ if ( j < nnum ) {
+ nargs[nanum - 1] = nreqs[j];
+ fork_child( ncmd, nargs );
+ }
+
+ /* modify */
+ if ( j < mnum ) {
+ margs[manum - 3] = mdn[j];
+ margs[manum - 1] = mreqs[j];
+ fork_child( mcmd, margs );
+ }
+
+ /* add/delete */
+ if ( j < anum ) {
+ aargs[aanum - 1] = afiles[j];
+ fork_child( acmd, aargs );
+ }
+
+ /* bind */
+ if ( DOREQ( bnum, j ) ) {
+ int jj = j % bnum;
+
+ if ( nextra ) {
+ int n = ((double)nextra)*rand()/(RAND_MAX + 1.0);
+ extra_t *e;
+
+ for ( e = extra; n-- > 0; e = e->next )
+ ;
+ *bargs_extra = e->action;
+ }
+
+ if ( battrs[jj] != NULL ) {
+ bargs[banum - 3] = manager ? manager : "";
+ bargs[banum - 1] = passwd ? passwd : "";
+
+ bargs[banum + 0] = "-b";
+ bargs[banum + 1] = breqs[jj];
+ bargs[banum + 2] = "-f";
+ bargs[banum + 3] = bcreds[jj];
+ bargs[banum + 4] = "-a";
+ bargs[banum + 5] = battrs[jj];
+ bargs[banum + 6] = NULL;
+
+ } else {
+ bargs[banum - 3] = breqs[jj];
+ bargs[banum - 1] = bcreds[jj];
+ bargs[banum] = NULL;
+ }
+
+ fork_child( bcmd, bargs );
+ bargs[banum] = NULL;
+ }
+ }
+
+ wait4kids( -1 );
+
+ exit( EXIT_SUCCESS );
+}
+
+static char *
+get_file_name( char *dirname, char *filename )
+{
+ char buf[MAXPATHLEN];
+
+ snprintf( buf, sizeof buf, "%s" LDAP_DIRSEP "%s",
+ dirname, filename );
+ return( strdup( buf ));
+}
+
+
+static int
+get_search_filters( char *filename, char *filters[], char *attrs[], char *bases[], LDAPURLDesc *luds[] )
+{
+ FILE *fp;
+ int filter = 0;
+
+ if ( (fp = fopen( filename, "r" )) != NULL ) {
+ char line[BUFSIZ];
+
+ while (( filter < MAXREQS ) && ( fgets( line, BUFSIZ, fp ))) {
+ char *nl;
+ int got_URL = 0;
+
+ if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
+ *nl = '\0';
+
+ if ( luds ) luds[filter] = NULL;
+
+ if ( luds && strncmp( line, "ldap:///", STRLENOF( "ldap:///" ) ) == 0 ) {
+ LDAPURLDesc *lud;
+
+ got_URL = 1;
+ bases[filter] = NULL;
+ if ( ldap_url_parse( line, &lud ) != LDAP_URL_SUCCESS ) {
+ filter = -filter - 1;
+ break;
+ }
+
+ if ( lud->lud_dn == NULL || lud->lud_exts != NULL ) {
+ filter = -filter - 1;
+ ldap_free_urldesc( lud );
+ break;
+ }
+
+ luds[filter] = lud;
+
+ } else {
+ bases[filter] = ArgDup( line );
+ }
+ if ( fgets( line, BUFSIZ, fp ) == NULL )
+ *line = '\0';
+ if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
+ *nl = '\0';
+
+ filters[filter] = ArgDup( line );
+ if ( attrs ) {
+ if ( filters[filter][0] == '+') {
+ char *sep = strchr( filters[filter], ':' );
+
+ attrs[ filter ] = &filters[ filter ][ 1 ];
+ if ( sep != NULL ) {
+ sep[ 0 ] = '\0';
+ /* NOTE: don't free this! */
+ filters[ filter ] = &sep[ 1 ];
+ }
+
+ } else {
+ attrs[ filter ] = NULL;
+ }
+ }
+ filter++;
+
+ }
+ fclose( fp );
+ }
+
+ return filter;
+}
+
+
+static int
+get_read_entries( char *filename, char *entries[], char *filters[] )
+{
+ FILE *fp;
+ int entry = 0;
+
+ if ( (fp = fopen( filename, "r" )) != NULL ) {
+ char line[BUFSIZ];
+
+ while (( entry < MAXREQS ) && ( fgets( line, BUFSIZ, fp ))) {
+ char *nl;
+
+ if (( nl = strchr( line, '\r' )) || ( nl = strchr( line, '\n' )))
+ *nl = '\0';
+ if ( filters != NULL && line[0] == '+' ) {
+ LDAPURLDesc *lud;
+
+ if ( ldap_url_parse( &line[1], &lud ) != LDAP_URL_SUCCESS ) {
+ entry = -entry - 1;
+ break;
+ }
+
+ if ( lud->lud_dn == NULL || lud->lud_dn[ 0 ] == '\0' ) {
+ ldap_free_urldesc( lud );
+ entry = -entry - 1;
+ break;
+ }
+
+ entries[entry] = ArgDup( lud->lud_dn );
+
+ if ( lud->lud_filter ) {
+ filters[entry] = ArgDup( lud->lud_filter );
+
+ } else {
+ filters[entry] = ArgDup( "(objectClass=*)" );
+ }
+ ldap_free_urldesc( lud );
+
+ } else {
+ if ( filters != NULL )
+ filters[entry] = NULL;
+
+ entries[entry] = ArgDup( line );
+ }
+
+ entry++;
+
+ }
+ fclose( fp );
+ }
+
+ return( entry );
+}
+
+#ifndef HAVE_WINSOCK
+static void
+fork_child( char *prog, char **args )
+{
+ /* note: obscures global pid var; intended */
+ pid_t pid;
+
+ wait4kids( maxkids );
+
+ switch ( pid = fork() ) {
+ case 0: /* child */
+#ifdef HAVE_EBCDIC
+ /* The __LIBASCII execvp only handles ASCII "prog",
+ * we still need to translate the arg vec ourselves.
+ */
+ { char *arg2[MAXREQS];
+ int i;
+
+ for (i=0; args[i]; i++) {
+ arg2[i] = ArgDup(args[i]);
+ __atoe(arg2[i]);
+ }
+ arg2[i] = NULL;
+ args = arg2; }
+#endif
+ execvp( prog, args );
+ tester_perror( "execvp", NULL );
+ { int i;
+ for (i=0; args[i]; i++);
+ fprintf(stderr,"%d args\n", i);
+ for (i=0; args[i]; i++)
+ fprintf(stderr,"%d %s\n", i, args[i]);
+ }
+
+ exit( EXIT_FAILURE );
+ break;
+
+ case -1: /* trouble */
+ tester_perror( "fork", NULL );
+ break;
+
+ default: /* parent */
+ nkids++;
+ break;
+ }
+}
+
+static void
+wait4kids( int nkidval )
+{
+ int status;
+
+ while ( nkids >= nkidval ) {
+ pid_t pid = wait( &status );
+
+ if ( WIFSTOPPED(status) ) {
+ fprintf( stderr,
+ "stopping: child PID=%ld stopped with signal %d\n",
+ (long) pid, (int) WSTOPSIG(status) );
+
+ } else if ( WIFSIGNALED(status) ) {
+ fprintf( stderr,
+ "stopping: child PID=%ld terminated with signal %d%s\n",
+ (long) pid, (int) WTERMSIG(status),
+#ifdef WCOREDUMP
+ WCOREDUMP(status) ? ", core dumped" : ""
+#else
+ ""
+#endif
+ );
+ exit( WEXITSTATUS(status) );
+
+ } else if ( WEXITSTATUS(status) != 0 ) {
+ fprintf( stderr,
+ "stopping: child PID=%ld exited with status %d\n",
+ (long) pid, (int) WEXITSTATUS(status) );
+ exit( WEXITSTATUS(status) );
+
+ } else {
+ nkids--;
+ }
+ }
+}
+#else
+
+static void
+wait4kids( int nkidval )
+{
+ int rc, i;
+
+ while ( nkids >= nkidval ) {
+ rc = WaitForMultipleObjects( nkids, children, FALSE, INFINITE );
+ for ( i=rc - WAIT_OBJECT_0; i<nkids-1; i++)
+ children[i] = children[i+1];
+ nkids--;
+ }
+}
+
+static void
+fork_child( char *prog, char **args )
+{
+ int rc;
+
+ wait4kids( maxkids );
+
+ rc = _spawnvp( _P_NOWAIT, prog, args );
+
+ if ( rc == -1 ) {
+ tester_perror( "_spawnvp", NULL );
+ } else {
+ children[nkids++] = (HANDLE)rc;
+ }
+}
+#endif
diff --git a/tests/progs/slapd-watcher.c b/tests/progs/slapd-watcher.c
new file mode 100644
index 0000000..0fed11f
--- /dev/null
+++ b/tests/progs/slapd-watcher.c
@@ -0,0 +1,823 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include "ac/signal.h"
+#include "ac/stdlib.h"
+#include "ac/time.h"
+
+#include "ac/ctype.h"
+#include "ac/param.h"
+#include "ac/socket.h"
+#include "ac/string.h"
+#include "ac/unistd.h"
+#include "ac/wait.h"
+#include "ac/time.h"
+
+#include "ldap.h"
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "lber_pvt.h"
+#include "ldap_pvt.h"
+
+#include "slapd-common.h"
+
+#define SLAP_SYNC_SID_MAX 4095
+
+#define HAS_MONITOR 1
+#define HAS_BASE 2
+#define HAS_ENTRIES 4
+#define HAS_SREPL 8
+#define HAS_ALL (HAS_MONITOR|HAS_BASE|HAS_ENTRIES|HAS_SREPL)
+
+
+#define WAS_LATE 0x100
+#define WAS_DOWN 0x200
+
+#define MONFILTER "(objectClass=monitorOperation)"
+
+static const char *default_monfilter = MONFILTER;
+
+typedef enum {
+ SLAP_OP_BIND = 0,
+ SLAP_OP_UNBIND,
+ SLAP_OP_SEARCH,
+ SLAP_OP_COMPARE,
+ SLAP_OP_MODIFY,
+ SLAP_OP_MODRDN,
+ SLAP_OP_ADD,
+ SLAP_OP_DELETE,
+ SLAP_OP_ABANDON,
+ SLAP_OP_EXTENDED,
+ SLAP_OP_LAST
+} slap_op_t;
+
+struct opname {
+ struct berval rdn;
+ char *display;
+} opnames[] = {
+ { BER_BVC("cn=Bind"), "Bind" },
+ { BER_BVC("cn=Unbind"), "Unbind" },
+ { BER_BVC("cn=Search"), "Search" },
+ { BER_BVC("cn=Compare"), "Compare" },
+ { BER_BVC("cn=Modify"), "Modify" },
+ { BER_BVC("cn=Modrdn"), "ModDN" },
+ { BER_BVC("cn=Add"), "Add" },
+ { BER_BVC("cn=Delete"), "Delete" },
+ { BER_BVC("cn=Abandon"), "Abandon" },
+ { BER_BVC("cn=Extended"), "Extended" },
+ { BER_BVNULL, NULL }
+};
+
+typedef struct counters {
+ struct timeval time;
+ unsigned long entries;
+ unsigned long ops[SLAP_OP_LAST];
+} counters;
+
+typedef struct csns {
+ struct berval *vals;
+ struct timeval *tvs;
+} csns;
+
+typedef struct activity {
+ time_t active;
+ time_t idle;
+ time_t maxlag;
+ time_t lag;
+} activity;
+
+typedef struct server {
+ char *url;
+ LDAP *ld;
+ int flags;
+ int sid;
+ struct berval monitorbase;
+ char *monitorfilter;
+ time_t late;
+ time_t down;
+ counters c_prev;
+ counters c_curr;
+ csns csn_prev;
+ csns csn_curr;
+ activity *times;
+} server;
+
+static void
+usage( char *name, char opt )
+{
+ if ( opt ) {
+ fprintf( stderr, "%s: unable to handle option \'%c\'\n\n",
+ name, opt );
+ }
+
+ fprintf( stderr, "usage: %s "
+ "[-D <dn> [ -w <passwd> ]] "
+ "[-d <level>] "
+ "[-O <SASL secprops>] "
+ "[-R <SASL realm>] "
+ "[-U <SASL authcid> [-X <SASL authzid>]] "
+ "[-x | -Y <SASL mech>] "
+ "[-i <interval>] "
+ "[-s <sids>] "
+ "[-c <contextDN>] "
+ "[-b <baseDN> ] URI[...]\n",
+ name );
+ exit( EXIT_FAILURE );
+}
+
+struct berval base, cbase;
+int interval = 10;
+int numservers;
+server *servers;
+char *monfilter;
+
+struct berval at_namingContexts = BER_BVC("namingContexts");
+struct berval at_monitorOpCompleted = BER_BVC("monitorOpCompleted");
+struct berval at_olmMDBEntries = BER_BVC("olmMDBEntries");
+struct berval at_contextCSN = BER_BVC("contextCSN");
+
+void timestamp(time_t *tt)
+{
+ struct tm *tm = gmtime(tt);
+ printf("%d-%02d-%02d %02d:%02d:%02d",
+ tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+}
+
+void deltat(time_t *tt)
+{
+ struct tm *tm = gmtime(tt);
+ if (tm->tm_mday-1)
+ printf("%02d+", tm->tm_mday-1);
+ printf("%02d:%02d:%02d",
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+}
+
+static char *clearscreen = "\033[H\033[2J";
+
+void rotate_stats( server *sv )
+{
+ if ( sv->flags & HAS_MONITOR )
+ sv->c_prev = sv->c_curr;
+ if ( sv->flags & HAS_BASE ) {
+ int i;
+
+ for (i=0; i<numservers; i++) {
+ if ( sv->csn_curr.vals[i].bv_len ) {
+ ber_bvreplace(&sv->csn_prev.vals[i],
+ &sv->csn_curr.vals[i]);
+ sv->csn_prev.tvs[i] = sv->csn_curr.tvs[i];
+ } else {
+ if ( sv->csn_prev.vals[i].bv_val )
+ sv->csn_prev.vals[i].bv_val[0] = '\0';
+ }
+ }
+ }
+}
+
+void display()
+{
+ int i, j;
+ struct timeval now;
+ time_t now_t;
+
+ gettimeofday(&now, NULL);
+ now_t = now.tv_sec;
+ printf("%s", clearscreen);
+ timestamp(&now_t);
+ printf("\n");
+
+ for (i=0; i<numservers; i++) {
+ printf("\n%s", servers[i].url );
+ if ( servers[i].flags & WAS_DOWN ) {
+ printf(", down@");
+ timestamp( &servers[i].down );
+ }
+ if ( servers[i].flags & WAS_LATE ) {
+ printf(", late@");
+ timestamp( &servers[i].late );
+ }
+ printf("\n");
+ if ( servers[i].flags & HAS_MONITOR ) {
+ struct timeval tv;
+ double rate, duration;
+ long delta;
+ printf(" ");
+ if ( servers[i].flags & HAS_ENTRIES )
+ printf(" Entries ");
+ for ( j = 0; j<SLAP_OP_LAST; j++ )
+ printf(" %9s ", opnames[j].display);
+ printf("\n");
+ printf("Num ");
+ if ( servers[i].flags & HAS_ENTRIES )
+ printf("%10lu ", servers[i].c_curr.entries);
+ for ( j = 0; j<SLAP_OP_LAST; j++ )
+ printf("%10lu ", servers[i].c_curr.ops[j]);
+ printf("\n");
+ printf("Num/s ");
+ tv.tv_usec = now.tv_usec - servers[i].c_prev.time.tv_usec;
+ tv.tv_sec = now.tv_sec - servers[i].c_prev.time.tv_sec;
+ if ( tv.tv_usec < 0 ) {
+ tv.tv_usec += 1000000;
+ tv.tv_sec--;
+ }
+ duration = tv.tv_sec + (tv.tv_usec / (double)1000000);
+ if ( servers[i].flags & HAS_ENTRIES ) {
+ delta = servers[i].c_curr.entries - servers[i].c_prev.entries;
+ rate = delta / duration;
+ printf("%10.2f ", rate);
+ }
+ for ( j = 0; j<SLAP_OP_LAST; j++ ) {
+ delta = servers[i].c_curr.ops[j] - servers[i].c_prev.ops[j];
+ rate = delta / duration;
+ printf("%10.2f ", rate);
+ }
+ printf("\n");
+ }
+ if ( servers[i].flags & HAS_BASE ) {
+ for (j=0; j<numservers; j++) {
+ /* skip empty CSNs */
+ if (!servers[i].csn_curr.vals[j].bv_len ||
+ !servers[i].csn_curr.vals[j].bv_val[0])
+ continue;
+ printf("contextCSN: %s", servers[i].csn_curr.vals[j].bv_val );
+ if (ber_bvcmp(&servers[i].csn_curr.vals[j],
+ &servers[i].csn_prev.vals[j])) {
+ /* a difference */
+ if (servers[i].times[j].idle) {
+ servers[i].times[j].idle = 0;
+ servers[i].times[j].active = 0;
+ servers[i].times[j].maxlag = 0;
+ servers[i].times[j].lag = 0;
+ }
+active:
+ if (!servers[i].times[j].active)
+ servers[i].times[j].active = now_t;
+ printf(" actv@");
+ timestamp(&servers[i].times[j].active);
+ } else if ( servers[i].times[j].lag || ( servers[i].flags & WAS_LATE )) {
+ goto active;
+ } else {
+ if (servers[i].times[j].active && !servers[i].times[j].idle)
+ servers[i].times[j].idle = now_t;
+ if (servers[i].times[j].active) {
+ printf(" actv@");
+ timestamp(&servers[i].times[j].active);
+ printf(", idle@");
+ timestamp(&servers[i].times[j].idle);
+ } else {
+ printf(" idle");
+ }
+ }
+ if (i != j) {
+ if (ber_bvcmp(&servers[i].csn_curr.vals[j],
+ &servers[j].csn_curr.vals[j])) {
+ struct timeval delta;
+ int ahead = 0;
+ time_t deltatt;
+ delta.tv_sec = servers[j].csn_curr.tvs[j].tv_sec -
+ servers[i].csn_curr.tvs[j].tv_sec;
+ delta.tv_usec = servers[j].csn_curr.tvs[j].tv_usec -
+ servers[i].csn_curr.tvs[j].tv_usec;
+ if (delta.tv_usec < 0) {
+ delta.tv_usec += 1000000;
+ delta.tv_sec--;
+ }
+ if (delta.tv_sec < 0) {
+ delta.tv_sec = -delta.tv_sec;
+ ahead = 1;
+ }
+ deltatt = delta.tv_sec;
+ if (ahead)
+ printf(", ahead ");
+ else
+ printf(", behind ");
+ deltat( &deltatt );
+ servers[i].times[j].lag = deltatt;
+ if (deltatt > servers[i].times[j].maxlag)
+ servers[i].times[j].maxlag = deltatt;
+ } else {
+ servers[i].times[j].lag = 0;
+ printf(", sync'd");
+ }
+ if (servers[i].times[j].maxlag) {
+ printf(", max delta ");
+ deltat( &servers[i].times[j].maxlag );
+ }
+ }
+ printf("\n");
+ }
+ }
+ if ( !( servers[i].flags & WAS_LATE ))
+ rotate_stats( &servers[i] );
+ }
+}
+
+void get_counters(
+ LDAP *ld,
+ LDAPMessage *e,
+ BerElement *ber,
+ counters *c )
+{
+ int rc;
+ slap_op_t op = SLAP_OP_BIND;
+ struct berval dn, bv, *bvals, **bvp = &bvals;
+
+ do {
+ int done = 0;
+ for ( rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp );
+ rc == LDAP_SUCCESS;
+ rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp )) {
+
+ if ( bv.bv_val == NULL ) break;
+ if ( !ber_bvcmp( &bv, &at_monitorOpCompleted ) && bvals ) {
+ c->ops[op] = strtoul( bvals[0].bv_val, NULL, 0 );
+ done = 1;
+ }
+ if ( bvals ) {
+ ber_memfree( bvals );
+ bvals = NULL;
+ }
+ if ( done )
+ break;
+ }
+ ber_free( ber, 0 );
+ e = ldap_next_entry( ld, e );
+ if ( !e )
+ break;
+ ldap_get_dn_ber( ld, e, &ber, &dn );
+ op++;
+ } while ( op < SLAP_OP_LAST );
+}
+
+int
+slap_parse_csn_sid( struct berval *csnp )
+{
+ char *p, *q;
+ struct berval csn = *csnp;
+ int i;
+
+ p = ber_bvchr( &csn, '#' );
+ if ( !p )
+ return -1;
+ p++;
+ csn.bv_len -= p - csn.bv_val;
+ csn.bv_val = p;
+
+ p = ber_bvchr( &csn, '#' );
+ if ( !p )
+ return -1;
+ p++;
+ csn.bv_len -= p - csn.bv_val;
+ csn.bv_val = p;
+
+ q = ber_bvchr( &csn, '#' );
+ if ( !q )
+ return -1;
+
+ csn.bv_len = q - p;
+
+ i = strtol( p, &q, 16 );
+ if ( p == q || q != p + csn.bv_len || i < 0 || i > SLAP_SYNC_SID_MAX ) {
+ i = -1;
+ }
+
+ return i;
+}
+
+void get_csns(
+ csns *c,
+ struct berval *bvs
+)
+{
+ int i, j;
+
+ /* clear old values if any */
+ for (i=0; i<numservers; i++)
+ if ( c->vals[i].bv_val )
+ c->vals[i].bv_val[0] = '\0';
+
+ for (i=0; bvs[i].bv_val; i++) {
+ struct lutil_tm tm;
+ struct lutil_timet tt;
+ int sid = slap_parse_csn_sid( &bvs[i] );
+ for (j=0; j<numservers; j++)
+ if (sid == servers[j].sid) break;
+ if (j < numservers) {
+ ber_bvreplace( &c->vals[j], &bvs[i] );
+ lutil_parsetime(bvs[i].bv_val, &tm);
+ c->tvs[j].tv_usec = tm.tm_nsec / 1000;
+ lutil_tm2time( &tm, &tt );
+ c->tvs[j].tv_sec = tt.tt_sec;
+ }
+ }
+}
+
+int
+setup_server( struct tester_conn_args *config, server *sv, int first )
+{
+ config->uri = sv->url;
+ tester_init_ld( &sv->ld, config, first ? 0 : TESTER_INIT_NOEXIT );
+ if ( !sv->ld )
+ return -1;
+
+ sv->flags &= ~HAS_ALL;
+ {
+ char *attrs[] = { at_namingContexts.bv_val, at_monitorOpCompleted.bv_val,
+ at_olmMDBEntries.bv_val, NULL };
+ LDAPMessage *res = NULL, *e = NULL;
+ BerElement *ber = NULL;
+ LDAP *ld = sv->ld;
+ struct berval dn, bv, *bvals, **bvp = &bvals;
+ int j, rc;
+
+ rc = ldap_search_ext_s( ld, "cn=monitor", LDAP_SCOPE_SUBTREE, monfilter,
+ attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res );
+ switch(rc) {
+ case LDAP_SIZELIMIT_EXCEEDED:
+ case LDAP_TIMELIMIT_EXCEEDED:
+ case LDAP_SUCCESS:
+ gettimeofday( &sv->c_curr.time, 0 );
+ sv->flags |= HAS_MONITOR;
+ for ( e = ldap_first_entry( ld, res ); e; e = ldap_next_entry( ld, e )) {
+ ldap_get_dn_ber( ld, e, &ber, &dn );
+ if ( !strncasecmp( dn.bv_val, "cn=Database", sizeof("cn=Database")-1 ) ||
+ !strncasecmp( dn.bv_val, "cn=Frontend", sizeof("cn=Frontend")-1 )) {
+ int matched = 0;
+ for ( rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp );
+ rc == LDAP_SUCCESS;
+ rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp )) {
+ if ( bv.bv_val == NULL ) break;
+ if (!ber_bvcmp( &bv, &at_namingContexts ) && bvals ) {
+ for (j=0; bvals[j].bv_val; j++) {
+ if ( !ber_bvstrcasecmp( &base, &bvals[j] )) {
+ matched = 1;
+ break;
+ }
+ }
+ if (!matched) {
+ ber_memfree( bvals );
+ bvals = NULL;
+ break;
+ }
+ }
+ if (!ber_bvcmp( &bv, &at_olmMDBEntries )) {
+ ber_bvreplace( &sv->monitorbase, &dn );
+ sv->flags |= HAS_ENTRIES;
+ sv->c_curr.entries = strtoul( bvals[0].bv_val, NULL, 0 );
+ }
+ ber_memfree( bvals );
+ bvals = NULL;
+ }
+ } else if (!strncasecmp( dn.bv_val, opnames[0].rdn.bv_val,
+ opnames[0].rdn.bv_len )) {
+ get_counters( ld, e, ber, &sv->c_curr );
+ break;
+ }
+ if ( ber )
+ ber_free( ber, 0 );
+ }
+ break;
+
+ case LDAP_NO_SUCH_OBJECT:
+ /* no cn=monitor */
+ break;
+
+ default:
+ tester_ldap_error( ld, "ldap_search_ext_s(cn=Monitor)", sv->url );
+ if ( first )
+ exit( EXIT_FAILURE );
+ }
+ ldap_msgfree( res );
+
+ if ( cbase.bv_val ) {
+ char *attr2[] = { at_contextCSN.bv_val, NULL };
+ rc = ldap_search_ext_s( ld, cbase.bv_val, LDAP_SCOPE_BASE, "(objectClass=*)",
+ attr2, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &res );
+ switch(rc) {
+ case LDAP_SUCCESS:
+ e = ldap_first_entry( ld, res );
+ if ( e ) {
+ sv->flags |= HAS_BASE;
+ ldap_get_dn_ber( ld, e, &ber, &dn );
+ for ( rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp );
+ rc == LDAP_SUCCESS;
+ rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp )) {
+ int done = 0;
+ if ( bv.bv_val == NULL ) break;
+ if ( bvals ) {
+ if ( !ber_bvcmp( &bv, &at_contextCSN )) {
+ get_csns( &sv->csn_curr, bvals );
+ done = 1;
+ }
+ ber_memfree( bvals );
+ bvals = NULL;
+ if ( done )
+ break;
+ }
+ }
+ }
+ ldap_msgfree( res );
+ break;
+
+ default:
+ tester_ldap_error( ld, "ldap_search_ext_s(baseDN)", sv->url );
+ if ( first )
+ exit( EXIT_FAILURE );
+ }
+ }
+ }
+
+ if ( sv->monitorfilter != default_monfilter )
+ free( sv->monitorfilter );
+ if ( sv->flags & HAS_ENTRIES ) {
+ int len = sv->monitorbase.bv_len + sizeof("(|(entryDN=)" MONFILTER ")");
+ char *ptr = malloc(len);
+ sprintf(ptr, "(|(entryDN=%s)" MONFILTER ")", sv->monitorbase.bv_val );
+ sv->monitorfilter = ptr;
+ } else if ( sv->flags & HAS_MONITOR ) {
+ sv->monitorfilter = (char *)default_monfilter;
+ }
+ if ( first )
+ rotate_stats( sv );
+ return 0;
+}
+
+int
+main( int argc, char **argv )
+{
+ int i, rc, *msg1, *msg2;
+ char **sids = NULL;
+ struct tester_conn_args *config;
+ int first = 1;
+
+ config = tester_init( "slapd-watcher", TESTER_TESTER );
+ config->authmethod = LDAP_AUTH_SIMPLE;
+
+ while ( ( i = getopt( argc, argv, "D:O:R:U:X:Y:b:c:d:i:s:w:x" ) ) != EOF )
+ {
+ switch ( i ) {
+ case 'b': /* base DN for DB entrycount lookups */
+ ber_str2bv( optarg, 0, 0, &base );
+ if ( !cbase.bv_val )
+ cbase = base;
+ break;
+
+ case 'c': /* base DN for contextCSN lookups */
+ ber_str2bv( optarg, 0, 0, &cbase );
+ break;
+
+ case 'i':
+ interval = atoi(optarg);
+ break;
+
+ case 's':
+ sids = ldap_str2charray( optarg, "," );
+ break;
+
+ default:
+ if ( tester_config_opt( config, i, optarg ) == LDAP_SUCCESS )
+ break;
+
+ usage( argv[0], i );
+ break;
+ }
+ }
+
+ tester_config_finish( config );
+#ifdef SIGPIPE
+ (void) SIGNAL(SIGPIPE, SIG_IGN);
+#endif
+
+ /* don't clear the screen if debug is enabled */
+ if (debug)
+ clearscreen = "\n\n";
+
+ numservers = argc - optind;
+ if ( !numservers )
+ usage( argv[0], 0 );
+
+ if ( sids ) {
+ for (i=0; sids[i]; i++ );
+ if ( i != numservers ) {
+ fprintf(stderr, "Number of sids doesn't equal number of server URLs\n");
+ exit( EXIT_FAILURE );
+ }
+ }
+
+ argv += optind;
+ argc -= optind;
+ servers = calloc( numservers, sizeof(server));
+
+ if ( base.bv_val ) {
+ monfilter = "(|(entryDN:dnOneLevelMatch:=cn=Databases,cn=Monitor)" MONFILTER ")";
+ } else {
+ monfilter = MONFILTER;
+ }
+
+ if ( sids || numservers > 1 ) {
+ for ( i=0; i<numservers; i++ )
+ if ( sids )
+ servers[i].sid = atoi(sids[i]);
+ else
+ servers[i].sid = i+1;
+ }
+
+ for ( i = 0; i < numservers; i++ ) {
+ servers[i].url = argv[i];
+ servers[i].times = calloc( numservers, sizeof(activity));
+ servers[i].csn_curr.vals = calloc( numservers, sizeof(struct berval));
+ servers[i].csn_prev.vals = calloc( numservers, sizeof(struct berval));
+ servers[i].csn_curr.tvs = calloc( numservers, sizeof(struct timeval));
+ servers[i].csn_prev.tvs = calloc( numservers, sizeof(struct timeval));
+ }
+
+ msg1 = malloc( numservers * 2 * sizeof(int));
+ msg2 = msg1 + numservers;
+
+ for (;;) {
+ LDAPMessage *res = NULL, *e = NULL;
+ BerElement *ber = NULL;
+ struct berval dn, bv, *bvals, **bvp = &bvals;
+ struct timeval tv;
+ LDAP *ld;
+
+ for (i=0; i<numservers; i++) {
+ if ( !servers[i].ld || !(servers[i].flags & WAS_LATE )) {
+ msg1[i] = 0;
+ msg2[i] = 0;
+ }
+ if ( !servers[i].ld ) {
+ setup_server( config, &servers[i], first );
+ } else {
+ ld = servers[i].ld;
+ rc = -1;
+ if ( servers[i].flags & WAS_DOWN )
+ servers[i].flags ^= WAS_DOWN;
+ if (( servers[i].flags & HAS_MONITOR ) && !msg1[i] ) {
+ char *attrs[3] = { at_monitorOpCompleted.bv_val };
+ if ( servers[i].flags & HAS_ENTRIES )
+ attrs[1] = at_olmMDBEntries.bv_val;
+ rc = ldap_search_ext( ld, "cn=monitor",
+ LDAP_SCOPE_SUBTREE, servers[i].monitorfilter,
+ attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &msg1[i] );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_search_ext(cn=Monitor)", servers[i].url );
+ if ( first )
+ exit( EXIT_FAILURE );
+ else {
+server_down1:
+ ldap_unbind_ext( ld, NULL, NULL );
+ servers[i].flags |= WAS_DOWN;
+ servers[i].ld = NULL;
+ gettimeofday( &tv, NULL );
+ servers[i].down = tv.tv_sec;
+ msg1[i] = 0;
+ msg2[i] = 0;
+ continue;
+ }
+ }
+ }
+ if (( servers[i].flags & HAS_BASE ) && !msg2[i] ) {
+ char *attrs[2] = { at_contextCSN.bv_val };
+ rc = ldap_search_ext( ld, cbase.bv_val,
+ LDAP_SCOPE_BASE, "(objectClass=*)",
+ attrs, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &msg2[i] );
+ if ( rc != LDAP_SUCCESS ) {
+ tester_ldap_error( ld, "ldap_search_ext(baseDN)", servers[i].url );
+ if ( first )
+ exit( EXIT_FAILURE );
+ else
+ goto server_down1;
+ }
+ }
+ if ( rc != -1 )
+ gettimeofday( &servers[i].c_curr.time, 0 );
+ }
+ }
+
+ for (i=0; i<numservers; i++) {
+ ld = servers[i].ld;
+ if ( msg1[i] ) {
+ tv.tv_sec = 0;
+ tv.tv_usec = 250000;
+ rc = ldap_result( ld, msg1[i], LDAP_MSG_ALL, &tv, &res );
+ if ( rc < 0 ) {
+ tester_ldap_error( ld, "ldap_result(cn=Monitor)", servers[i].url );
+ if ( first )
+ exit( EXIT_FAILURE );
+ else {
+server_down2:
+ ldap_unbind_ext( ld, NULL, NULL );
+ servers[i].flags |= WAS_DOWN;
+ servers[i].ld = NULL;
+ servers[i].down = servers[i].c_curr.time.tv_sec;
+ msg1[i] = 0;
+ msg2[i] = 0;
+ continue;
+ }
+ }
+ if ( rc == 0 ) {
+ if ( !( servers[i].flags & WAS_LATE ))
+ servers[i].late = servers[i].c_curr.time.tv_sec;
+ servers[i].flags |= WAS_LATE;
+ continue;
+ }
+ if ( servers[i].flags & WAS_LATE )
+ servers[i].flags ^= WAS_LATE;
+ for ( e = ldap_first_entry( ld, res ); e; e = ldap_next_entry( ld, e )) {
+ ldap_get_dn_ber( ld, e, &ber, &dn );
+ if ( !strncasecmp( dn.bv_val, "cn=Database", sizeof("cn=Database")-1 ) ||
+ !strncasecmp( dn.bv_val, "cn=Frontend", sizeof("cn=Frontend")-1 )) {
+ for ( rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp );
+ rc == LDAP_SUCCESS;
+ rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp )) {
+ if ( bv.bv_val == NULL ) break;
+ if ( !ber_bvcmp( &bv, &at_olmMDBEntries )) {
+ if ( !BER_BVISNULL( &servers[i].monitorbase )) {
+ servers[i].c_curr.entries = strtoul( bvals[0].bv_val, NULL, 0 );
+ }
+ }
+ ber_memfree( bvals );
+ bvals = NULL;
+ }
+ } else if (!strncasecmp( dn.bv_val, opnames[0].rdn.bv_val,
+ opnames[0].rdn.bv_len )) {
+ get_counters( ld, e, ber, &servers[i].c_curr );
+ break;
+ }
+ if ( ber )
+ ber_free( ber, 0 );
+ }
+ ldap_msgfree( res );
+ }
+ if ( msg2[i] ) {
+ tv.tv_sec = 0;
+ tv.tv_usec = 250000;
+ rc = ldap_result( ld, msg2[i], LDAP_MSG_ALL, &tv, &res );
+ if ( rc < 0 ) {
+ tester_ldap_error( ld, "ldap_result(baseDN)", servers[i].url );
+ if ( first )
+ exit( EXIT_FAILURE );
+ else
+ goto server_down2;
+ }
+ if ( rc == 0 ) {
+ if ( !( servers[i].flags & WAS_LATE ))
+ servers[i].late = servers[i].c_curr.time.tv_sec;
+ servers[i].flags |= WAS_LATE;
+ continue;
+ }
+ if ( servers[i].flags & WAS_LATE )
+ servers[i].flags ^= WAS_LATE;
+ e = ldap_first_entry( ld, res );
+ if ( e ) {
+ ldap_get_dn_ber( ld, e, &ber, &dn );
+ for ( rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp );
+ rc == LDAP_SUCCESS;
+ rc = ldap_get_attribute_ber( ld, e, ber, &bv, bvp )) {
+ int done = 0;
+ if ( bv.bv_val == NULL ) break;
+ if ( bvals ) {
+ if ( !ber_bvcmp( &bv, &at_contextCSN )) {
+ get_csns( &servers[i].csn_curr, bvals );
+ done = 1;
+ }
+ ber_memfree( bvals );
+ bvals = NULL;
+ if ( done )
+ break;
+ }
+ }
+ }
+ ldap_msgfree( res );
+ }
+ }
+ display();
+ sleep(interval);
+ first = 0;
+ }
+
+ exit( EXIT_SUCCESS );
+}
+
diff --git a/tests/run.in b/tests/run.in
new file mode 100644
index 0000000..8b690c8
--- /dev/null
+++ b/tests/run.in
@@ -0,0 +1,292 @@
+#!/bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+USAGE="$0 [-b <backend>] [-c] [-k] [-l #] [-p] [-s {ro|rp}] [-u] [-w] <script>"
+
+# configure generated
+SRCDIR="@srcdir@"
+TOPSRCDIR="@top_srcdir@"
+LN_S="@LN_S@"
+EGREP_CMD="@EGREP@"
+
+export SRCDIR TOPSRCDIR LN_S EGREP_CMD
+
+# Load balancer
+AC_lloadd=lloadd@BUILD_BALANCER@
+
+# backends known to ./run -b <backend> (used to deduce $BACKENDTYPE)
+AC_ldif=yes
+AC_mdb=@BUILD_MDB@
+AC_null=@BUILD_NULL@
+
+# other backends
+AC_ldap=ldap@BUILD_LDAP@
+AC_meta=meta@BUILD_META@
+AC_asyncmeta=asyncmeta@BUILD_ASYNCMETA@
+AC_perl=perl@BUILD_PERL@
+AC_relay=relay@BUILD_RELAY@
+AC_sql=sql@BUILD_SQL@
+AC_wt=@BUILD_WT@
+
+# overlays
+AC_accesslog=accesslog@BUILD_ACCESSLOG@
+AC_argon2=argon2@BUILD_PW_ARGON2@
+AC_autoca=autoca@BUILD_AUTOCA@
+AC_constraint=constraint@BUILD_CONSTRAINT@
+AC_dds=dds@BUILD_DDS@
+AC_deref=deref@BUILD_DEREF@
+AC_dynlist=dynlist@BUILD_DYNLIST@
+AC_homedir=homedir@BUILD_HOMEDIR@
+AC_memberof=memberof@BUILD_MEMBEROF@
+AC_otp=otp@BUILD_OTP@
+AC_pcache=pcache@BUILD_PROXYCACHE@
+AC_ppolicy=ppolicy@BUILD_PPOLICY@
+AC_refint=refint@BUILD_REFINT@
+AC_remoteauth=remoteauth@BUILD_REMOTEAUTH@
+AC_retcode=retcode@BUILD_RETCODE@
+AC_translucent=translucent@BUILD_TRANSLUCENT@
+AC_unique=unique@BUILD_UNIQUE@
+AC_rwm=rwm@BUILD_RWM@
+AC_syncprov=syncprov@BUILD_SYNCPROV@
+AC_valsort=valsort@BUILD_VALSORT@
+
+# misc
+AC_WITH_SASL=@WITH_SASL@
+AC_WITH_TLS=@WITH_TLS@
+AC_TLS_TYPE=@WITH_TLS_TYPE@
+AC_WITH_MODULES_ENABLED=@WITH_MODULES_ENABLED@
+AC_ACI_ENABLED=aci@WITH_ACI_ENABLED@
+AC_LIBS_DYNAMIC=lib@BUILD_LIBS_DYNAMIC@
+
+# sanitize
+if test "${AC_ldap}" = "ldapmod" && test "${AC_LIBS_DYNAMIC}" = "static" ; then
+ AC_ldap="ldapno"
+fi
+if test "${AC_meta}" = "metamod" && test "${AC_LIBS_DYNAMIC}" = "static" ; then
+ AC_meta="metano"
+fi
+if test "${AC_asyncmeta}" = "asyncmetamod" && test "${AC_LIBS_DYNAMIC}" = "static" ; then
+ AC_meta="asyncmetano"
+fi
+export AC_ldap AC_mdb AC_meta AC_asyncmeta AC_monitor AC_null AC_perl AC_relay AC_sql \
+ AC_accesslog AC_argon2 AC_autoca AC_constraint AC_dds AC_deref AC_dynlist \
+ AC_homedir AC_memberof AC_otp AC_pcache AC_ppolicy AC_refint AC_remoteauth \
+ AC_retcode AC_rwm AC_unique AC_syncprov AC_translucent \
+ AC_valsort \
+ AC_lloadd \
+ AC_WITH_SASL AC_WITH_TLS AC_WITH_MODULES_ENABLED AC_ACI_ENABLED \
+ AC_LIBS_DYNAMIC AC_WITH_TLS AC_TLS_TYPE
+
+if test ! -x ../servers/slapd/slapd ; then
+ echo "Could not locate slapd(8)"
+ exit 1
+fi
+
+BACKEND=
+CLEAN=no
+WAIT=0
+KILLSERVERS=yes
+PRESERVE=${PRESERVE-no}
+SYNCMODE=${SYNCMODE-rp}
+USERDATA=no
+LOOP=1
+COUNTER=1
+
+while test $# -gt 0 ; do
+ case "$1" in
+ -b | -backend)
+ BACKEND="$2"
+ shift; shift ;;
+
+ -c | -clean)
+ CLEAN=yes
+ shift ;;
+
+ -k | -kill)
+ KILLSERVERS=no
+ shift ;;
+ -l | -loop)
+ NUM="`echo $2 | sed 's/[0-9]//g'`"
+ if [ -z "$NUM" ]; then
+ LOOP=$2
+ else
+ echo "Loop variable not an int: $2"
+ echo "$USAGE"; exit 1
+ fi
+ shift ;
+ shift ;;
+
+ -p | -preserve)
+ PRESERVE=yes
+ shift ;;
+
+ -s | -syncmode)
+ case "$2" in
+ ro | rp)
+ SYNCMODE="$2"
+ ;;
+ *)
+ echo "unknown sync mode $2"
+ echo "$USAGE"; exit 1
+ ;;
+ esac
+ shift; shift ;;
+
+ -u | -userdata)
+ USERDATA=yes
+ shift ;;
+
+ -w | -wait)
+ WAIT=1
+ shift ;;
+
+ -)
+ shift
+ break ;;
+
+ -*)
+ echo "$USAGE"; exit 1
+ ;;
+
+ *)
+ break ;;
+ esac
+done
+
+if test -z "$BACKEND" ; then
+ for b in mdb ; do
+ if eval "test \"\$AC_$b\" != no" ; then
+ BACKEND=$b
+ break
+ fi
+ done
+ if test -z "$BACKEND" ; then
+ echo "No suitable default database backend configured" >&2
+ exit 1
+ fi
+fi
+
+BACKENDTYPE=`eval 'echo $AC_'$BACKEND`
+if test "x$BACKENDTYPE" = "x" ; then
+ BACKENDTYPE="unknown"
+fi
+
+# Backend features. indexdb: indexing and unchecked limit.
+# maindb: main storage backend. Currently index,limits,mode,paged results.
+INDEXDB=noindexdb MAINDB=nomaindb
+case $BACKEND in
+ mdb) INDEXDB=indexdb MAINDB=maindb ;;
+ ndb) INDEXDB=indexdb ;;
+ wt) INDEXDB=indexdb ;;
+esac
+
+export BACKEND BACKENDTYPE INDEXDB MAINDB \
+ WAIT KILLSERVERS PRESERVE SYNCMODE USERDATA
+
+if test $# = 0 ; then
+ echo "$USAGE"; exit 1
+fi
+
+# need defines.sh for the definitions of the directories
+. $SRCDIR/scripts/defines.sh
+
+SCRIPTDIR="${SRCDIR}/scripts"
+ITSDIR="${SRCDIR}/data/regressions"
+SCRIPTNAME="$1"
+shift
+
+if test -x "${SCRIPTDIR}/${SCRIPTNAME}" ; then
+ SCRIPT="${SCRIPTDIR}/${SCRIPTNAME}"
+elif test -x "`echo ${SCRIPTDIR}/test*-${SCRIPTNAME}`"; then
+ SCRIPT="`echo ${SCRIPTDIR}/test*-${SCRIPTNAME}`"
+elif test -x "`echo ${SCRIPTDIR}/${SCRIPTNAME}-*`"; then
+ SCRIPT="`echo ${SCRIPTDIR}/${SCRIPTNAME}-*`"
+elif test -x "`echo ${ITSDIR}/${SCRIPTNAME}/${SCRIPTNAME}`"; then
+ SCRIPT="`echo ${ITSDIR}/${SCRIPTNAME}/${SCRIPTNAME}`"
+else
+ echo "run: ${SCRIPTNAME} not found (or not executable)"
+ exit 1;
+fi
+
+if test ! -r ${DATADIR}/test.ldif ; then
+ ${LN_S} ${SRCDIR}/data ${DATADIR}
+fi
+if test ! -r ${SCHEMADIR}/core.schema ; then
+ ${LN_S} ${TOPSRCDIR}/servers/slapd/schema ${SCHEMADIR}
+fi
+
+if test -d ${TESTDIR} ; then
+ if test $PRESERVE = no ; then
+ echo "Cleaning up test run directory leftover from previous run."
+ /bin/rm -rf ${TESTDIR}
+ elif test $PRESERVE = yes ; then
+ echo "Cleaning up only database directories leftover from previous run."
+ /bin/rm -rf ${TESTDIR}/db.*
+ fi
+fi
+if test $BACKEND = ndb ; then
+ mysql --user root <<EOF
+ drop database if exists db_1;
+ drop database if exists db_2;
+ drop database if exists db_3;
+ drop database if exists db_4;
+ drop database if exists db_5;
+ drop database if exists db_6;
+EOF
+fi
+mkdir -p ${TESTDIR}
+
+if test $USERDATA = yes ; then
+ if test ! -d userdata ; then
+ echo "User data directory (userdata) does not exist."
+ exit 1
+ fi
+ cp -R userdata/* ${TESTDIR}
+fi
+
+# disable LDAP initialization
+LDAPNOINIT=true; export LDAPNOINIT
+
+echo "Running ${SCRIPT} for ${BACKEND}..."
+while [ $COUNTER -le $LOOP ]; do
+ if [ $LOOP -gt 1 ]; then
+ echo "Running $COUNTER of $LOOP iterations"
+ fi
+ START=`date +%s`
+ $SCRIPT $*
+ RC=$?
+ END=`date +%s`
+
+ if test $CLEAN = yes ; then
+ echo "Cleaning up test run directory from this run."
+ /bin/rm -rf ${TESTDIR}
+ echo "Cleaning up symlinks."
+ /bin/rm -f ${DATADIR} ${SCHEMADIR}
+ fi
+
+ if [ $RC -ne 0 ]; then
+ if [ $LOOP -gt 1 ]; then
+ echo "Failed after $COUNTER of $LOOP iterations"
+ fi
+ exit $RC
+ else
+ COUNTER=`expr $COUNTER + 1`
+ if [ $COUNTER -le $LOOP ]; then
+ echo "Cleaning up test run directory from this run."
+ /bin/rm -rf ${TESTDIR}
+ fi
+ fi
+done
+exit $RC
diff --git a/tests/scripts/all b/tests/scripts/all
new file mode 100755
index 0000000..e11b85f
--- /dev/null
+++ b/tests/scripts/all
@@ -0,0 +1,106 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+. $SRCDIR/scripts/defines.sh
+
+TB="" TN=""
+if test -t 1 ; then
+ TB=`$SHTOOL echo -e "%B" 2>/dev/null`
+ TN=`$SHTOOL echo -e "%b" 2>/dev/null`
+fi
+
+FAILCOUNT=0
+SKIPCOUNT=0
+SLEEPTIME=10
+
+echo ">>>>> Executing all LDAP tests for $BACKEND"
+
+if [ -n "$NOEXIT" ]; then
+ echo "Result Test" > $TESTWD/results
+fi
+
+for CMD in $SRCDIR/scripts/test*; do
+ case "$CMD" in
+ *~) continue;;
+ *.bak) continue;;
+ *.orig) continue;;
+ *.sav) continue;;
+ *.py) continue;;
+ *) test -f "$CMD" || continue;;
+ esac
+
+ # remove cruft from prior test
+ if test $PRESERVE = yes ; then
+ /bin/rm -rf $TESTDIR/db.*
+ else
+ /bin/rm -rf $TESTDIR
+ fi
+ if test $BACKEND = ndb ; then
+ mysql --user root <<EOF
+ drop database if exists db_1;
+ drop database if exists db_2;
+ drop database if exists db_3;
+ drop database if exists db_4;
+ drop database if exists db_5;
+ drop database if exists db_6;
+EOF
+ fi
+
+ BCMD=`basename $CMD`
+ if [ -x "$CMD" ]; then
+ echo ">>>>> Starting ${TB}$BCMD${TN} for $BACKEND..."
+ START=`date +%s`
+ $CMD
+ RC=$?
+ END=`date +%s`
+
+ if test $RC -eq 0 ; then
+ echo ">>>>> $BCMD completed ${TB}OK${TN} for $BACKEND after $(( $END - $START )) seconds."
+ else
+ echo ">>>>> $BCMD ${TB}failed${TN} for $BACKEND after $(( $END - $START )) seconds"
+ FAILCOUNT=`expr $FAILCOUNT + 1`
+
+ if [ -n "$NOEXIT" ]; then
+ echo "Continuing."
+ else
+ echo "(exit $RC)"
+ exit $RC
+ fi
+ fi
+ else
+ echo ">>>>> Skipping ${TB}$BCMD${TN} for $BACKEND."
+ SKIPCOUNT=`expr $SKIPCOUNT + 1`
+ RC="-"
+ fi
+
+ if [ -n "$NOEXIT" ]; then
+ echo "$RC $BCMD" >> $TESTWD/results
+ fi
+
+# echo ">>>>> waiting $SLEEPTIME seconds for things to exit"
+# sleep $SLEEPTIME
+ echo ""
+done
+
+if [ -n "$NOEXIT" ]; then
+ if [ "$FAILCOUNT" -gt 0 ]; then
+ cat $TESTWD/results
+ echo "$FAILCOUNT tests for $BACKEND ${TB}failed${TN}. Please review the test log."
+ else
+ echo "All executed tests for $BACKEND ${TB}succeeded${TN}."
+ fi
+fi
+
+echo "$SKIPCOUNT tests for $BACKEND were ${TB}skipped${TN}."
diff --git a/tests/scripts/conf.sh b/tests/scripts/conf.sh
new file mode 100755
index 0000000..d166eba
--- /dev/null
+++ b/tests/scripts/conf.sh
@@ -0,0 +1,98 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+if [ x"$WITH_SASL" = x"yes" -a x"$USE_SASL" != x"no" ] ; then
+ SASL="sasl"
+ if [ x"$USE_SASL" = x"yes" ] ; then
+ USE_SASL=DIGEST-MD5
+ fi
+ SASL_MECH="\"saslmech=$USE_SASL\""
+else
+ SASL="nosasl"
+ SASL_MECH=
+fi
+sed -e "s/@BACKEND@/${BACKEND}/" \
+ -e "s/^#${BACKEND}#//" \
+ -e "/^#~/s/^#[^#]*~${BACKEND}~[^#]*#/#omit: /" \
+ -e "s/^#~[^#]*~#//" \
+ -e "s/@RELAY@/${RELAY}/" \
+ -e "s/^#relay-${RELAY}#//" \
+ -e "s/^#${BACKENDTYPE}#//" \
+ -e "s/^#${AC_TLS_TYPE}#//" \
+ -e "s/^#${AC_ldap}#//" \
+ -e "s/^#${AC_meta}#//" \
+ -e "s/^#${AC_asyncmeta}#//" \
+ -e "s/^#${AC_relay}#//" \
+ -e "s/^#${AC_sql}#//" \
+ -e "s/^#${RDBMS}#//" \
+ -e "s/^#${AC_accesslog}#//" \
+ -e "s/^#${AC_dds}#//" \
+ -e "s/^#${AC_deref}#//" \
+ -e "s/^#${AC_dynlist}#//" \
+ -e "s/^#${AC_homedir}#//" \
+ -e "s/^#${AC_memberof}#//" \
+ -e "s/^#${AC_pcache}#//" \
+ -e "s/^#${AC_ppolicy}#//" \
+ -e "s/^#${AC_refint}#//" \
+ -e "s/^#${AC_retcode}#//" \
+ -e "s/^#${AC_remoteauth}#//" \
+ -e "s/^#${AC_rwm}#//" \
+ -e "s/^#${AC_syncprov}#//" \
+ -e "s/^#${AC_translucent}#//" \
+ -e "s/^#${AC_unique}#//" \
+ -e "s/^#${AC_valsort}#//" \
+ -e "s/^#${INDEXDB}#//" \
+ -e "s/^#${MAINDB}#//" \
+ -e "s/^#${SASL}#//" \
+ -e "s/^#${ACI}#//" \
+ -e "s;@URI1@;${URI1};" \
+ -e "s;@URI2@;${URI2};" \
+ -e "s;@URI3@;${URI3};" \
+ -e "s;@URI4@;${URI4};" \
+ -e "s;@URI5@;${URI5};" \
+ -e "s;@URI6@;${URI6};" \
+ -e "s;@PORT1@;${PORT1};" \
+ -e "s;@PORT2@;${PORT2};" \
+ -e "s;@PORT3@;${PORT3};" \
+ -e "s;@PORT4@;${PORT4};" \
+ -e "s;@PORT5@;${PORT5};" \
+ -e "s;@PORT6@;${PORT6};" \
+ -e "s;@SURI1@;${SURI1};" \
+ -e "s;@SURI2@;${SURI2};" \
+ -e "s;@SURI3@;${SURI3};" \
+ -e "s;@SURI4@;${SURI4};" \
+ -e "s;@SURI5@;${SURI5};" \
+ -e "s;@SURI6@;${SURI6};" \
+ -e "s;@URIP1@;${URIP1};" \
+ -e "s;@URIP2@;${URIP2};" \
+ -e "s;@URIP3@;${URIP3};" \
+ -e "s;@URIP4@;${URIP4};" \
+ -e "s;@URIP5@;${URIP5};" \
+ -e "s;@URIP6@;${URIP6};" \
+ -e "s;@SURIP1@;${SURIP1};" \
+ -e "s;@SURIP2@;${SURIP2};" \
+ -e "s;@SURIP3@;${SURIP3};" \
+ -e "s;@SURIP4@;${SURIP4};" \
+ -e "s;@SURIP5@;${SURIP5};" \
+ -e "s;@SURIP6@;${SURIP6};" \
+ -e "s/@SASL_MECH@/${SASL_MECH}/" \
+ -e "s;@TESTDIR@;${TESTDIR};" \
+ -e "s;@TESTWD@;${TESTWD};" \
+ -e "s;@DATADIR@;${DATADIR};" \
+ -e "s;@SCHEMADIR@;${SCHEMADIR};" \
+ -e "s;@KRB5REALM@;${KRB5REALM};" \
+ -e "s;@KDCHOST@;${KDCHOST};" \
+ -e "s;@KDCPORT@;${KDCPORT};" \
+ -e "s;@TIMEOUT@;${TIMEOUT};" \
+ -e "/^#/d"
diff --git a/tests/scripts/confdirsync.sh b/tests/scripts/confdirsync.sh
new file mode 100755
index 0000000..25efb1e
--- /dev/null
+++ b/tests/scripts/confdirsync.sh
@@ -0,0 +1,18 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+sed -e "s/@BASEDN@/${BASEDN}/" \
+ -e "s/@MSAD_ADMINDN@/${MSAD_ADMINDN}/" \
+ -e "s/@MSAD_ADMINPW@/${MSAD_ADMINPW}/" \
+ -e "s/@MSAD_SUFFIX@/${MSAD_SUFFIX}/"
diff --git a/tests/scripts/defines.sh b/tests/scripts/defines.sh
new file mode 100755
index 0000000..693f6af
--- /dev/null
+++ b/tests/scripts/defines.sh
@@ -0,0 +1,451 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+umask 077
+
+TESTWD=`pwd`
+
+# backends
+BACKLDAP=${AC_ldap-ldapno}
+BACKMETA=${AC_meta-metano}
+BACKASYNCMETA=${AC_asyncmeta-asyncmetano}
+BACKPERL=${AC_perl-perlno}
+BACKRELAY=${AC_relay-relayno}
+BACKSQL=${AC_sql-sqlno}
+ RDBMS=${SLAPD_USE_SQL-rdbmsno}
+ RDBMSWRITE=${SLAPD_USE_SQLWRITE-no}
+
+# overlays
+ACCESSLOG=${AC_accesslog-accesslogno}
+ARGON2=${AC_argon2-argon2no}
+AUTOCA=${AC_autoca-autocano}
+CONSTRAINT=${AC_constraint-constraintno}
+DDS=${AC_dds-ddsno}
+DEREF=${AC_deref-derefno}
+DYNLIST=${AC_dynlist-dynlistno}
+HOMEDIR=${AC_homedir-homedirno}
+MEMBEROF=${AC_memberof-memberofno}
+OTP=${AC_otp-otpno}
+PROXYCACHE=${AC_pcache-pcacheno}
+PPOLICY=${AC_ppolicy-ppolicyno}
+REFINT=${AC_refint-refintno}
+REMOTEAUTH=${AC_remoteauth-remoteauthno}
+RETCODE=${AC_retcode-retcodeno}
+RWM=${AC_rwm-rwmno}
+SYNCPROV=${AC_syncprov-syncprovno}
+TRANSLUCENT=${AC_translucent-translucentno}
+UNIQUE=${AC_unique-uniqueno}
+VALSORT=${AC_valsort-valsortno}
+
+# misc
+WITH_SASL=${AC_WITH_SASL-no}
+USE_SASL=${SLAPD_USE_SASL-no}
+WITH_TLS=${AC_WITH_TLS-no}
+WITH_TLS_TYPE=${AC_TLS_TYPE-no}
+
+ACI=${AC_ACI_ENABLED-acino}
+SLEEP0=${SLEEP0-1}
+SLEEP1=${SLEEP1-7}
+SLEEP2=${SLEEP2-15}
+TIMEOUT=${TIMEOUT-8}
+
+# dirs
+PROGDIR=./progs
+DATADIR=${USER_DATADIR-./testdata}
+TESTDIR=${USER_TESTDIR-$TESTWD/testrun}
+SCHEMADIR=${USER_SCHEMADIR-./schema}
+case "$SCHEMADIR" in
+.*) ABS_SCHEMADIR="$TESTWD/$SCHEMADIR" ;;
+*) ABS_SCHEMADIR="$SCHEMADIR" ;;
+esac
+case "$SRCDIR" in
+.*) ABS_SRCDIR="$TESTWD/$SRCDIR" ;;
+*) ABS_SRCDIR="$SRCDIR" ;;
+esac
+export TESTDIR
+
+DBDIR1A=$TESTDIR/db.1.a
+DBDIR1B=$TESTDIR/db.1.b
+DBDIR1C=$TESTDIR/db.1.c
+DBDIR1D=$TESTDIR/db.1.d
+DBDIR1=$DBDIR1A
+DBDIR2A=$TESTDIR/db.2.a
+DBDIR2B=$TESTDIR/db.2.b
+DBDIR2C=$TESTDIR/db.2.c
+DBDIR2=$DBDIR2A
+DBDIR3=$TESTDIR/db.3.a
+DBDIR4=$TESTDIR/db.4.a
+DBDIR5=$TESTDIR/db.5.a
+DBDIR6=$TESTDIR/db.6.a
+SQLCONCURRENCYDIR=$DATADIR/sql-concurrency
+
+CLIENTDIR=../clients/tools
+#CLIENTDIR=/usr/local/bin
+
+# conf
+CONF=$DATADIR/slapd.conf
+CONFTWO=$DATADIR/slapd2.conf
+CONF2DB=$DATADIR/slapd-2db.conf
+MCONF=$DATADIR/slapd-provider.conf
+COMPCONF=$DATADIR/slapd-component.conf
+PWCONF=$DATADIR/slapd-pw.conf
+WHOAMICONF=$DATADIR/slapd-whoami.conf
+ACLCONF=$DATADIR/slapd-acl.conf
+RCONF=$DATADIR/slapd-referrals.conf
+SRPROVIDERCONF=$DATADIR/slapd-syncrepl-provider.conf
+DSRPROVIDERCONF=$DATADIR/slapd-deltasync-provider.conf
+DSRCONSUMERCONF=$DATADIR/slapd-deltasync-consumer.conf
+PPOLICYCONF=$DATADIR/slapd-ppolicy.conf
+PROXYCACHECONF=$DATADIR/slapd-proxycache.conf
+PROXYAUTHZCONF=$DATADIR/slapd-proxyauthz.conf
+CACHEPROVIDERCONF=$DATADIR/slapd-cache-provider.conf
+PROXYAUTHZPROVIDERCONF=$DATADIR/slapd-cache-provider-proxyauthz.conf
+R1SRCONSUMERCONF=$DATADIR/slapd-syncrepl-consumer-refresh1.conf
+R2SRCONSUMERCONF=$DATADIR/slapd-syncrepl-consumer-refresh2.conf
+P1SRCONSUMERCONF=$DATADIR/slapd-syncrepl-consumer-persist1.conf
+P2SRCONSUMERCONF=$DATADIR/slapd-syncrepl-consumer-persist2.conf
+P3SRCONSUMERCONF=$DATADIR/slapd-syncrepl-consumer-persist3.conf
+DIRSYNC1CONF=$DATADIR/slapd-dirsync1.conf
+DSEESYNC1CONF=$DATADIR/slapd-dsee-consumer1.conf
+DSEESYNC2CONF=$DATADIR/slapd-dsee-consumer2.conf
+REFCONSUMERCONF=$DATADIR/slapd-ref-consumer.conf
+SCHEMACONF=$DATADIR/slapd-schema.conf
+TLSCONF=$DATADIR/slapd-tls.conf
+TLSSASLCONF=$DATADIR/slapd-tls-sasl.conf
+GLUECONF=$DATADIR/slapd-glue.conf
+REFINTCONF=$DATADIR/slapd-refint.conf
+RETCODECONF=$DATADIR/slapd-retcode.conf
+UNIQUECONF=$DATADIR/slapd-unique.conf
+LIMITSCONF=$DATADIR/slapd-limits.conf
+DNCONF=$DATADIR/slapd-dn.conf
+EMPTYDNCONF=$DATADIR/slapd-emptydn.conf
+IDASSERTCONF=$DATADIR/slapd-idassert.conf
+LDAPGLUECONF1=$DATADIR/slapd-ldapglue.conf
+LDAPGLUECONF2=$DATADIR/slapd-ldapgluepeople.conf
+LDAPGLUECONF3=$DATADIR/slapd-ldapgluegroups.conf
+RELAYCONF=$DATADIR/slapd-relay.conf
+CHAINCONF1=$DATADIR/slapd-chain1.conf
+CHAINCONF2=$DATADIR/slapd-chain2.conf
+GLUESYNCCONF1=$DATADIR/slapd-glue-syncrepl1.conf
+GLUESYNCCONF2=$DATADIR/slapd-glue-syncrepl2.conf
+SQLCONF=$DATADIR/slapd-sql.conf
+SQLSRPROVIDERCONF=$DATADIR/slapd-sql-syncrepl-provider.conf
+TRANSLUCENTLOCALCONF=$DATADIR/slapd-translucent-local.conf
+TRANSLUCENTREMOTECONF=$DATADIR/slapd-translucent-remote.conf
+METACONF=$DATADIR/slapd-meta.conf
+METACONF1=$DATADIR/slapd-meta-target1.conf
+METACONF2=$DATADIR/slapd-meta-target2.conf
+ASYNCMETACONF=$DATADIR/slapd-asyncmeta.conf
+GLUELDAPCONF=$DATADIR/slapd-glue-ldap.conf
+ACICONF=$DATADIR/slapd-aci.conf
+VALSORTCONF=$DATADIR/slapd-valsort.conf
+DEREFCONF=$DATADIR/slapd-deref.conf
+DYNLISTCONF=$DATADIR/slapd-dynlist.conf
+HOMEDIRCONF=$DATADIR/slapd-homedir.conf
+RCONSUMERCONF=$DATADIR/slapd-repl-consumer-remote.conf
+PLSRCONSUMERCONF=$DATADIR/slapd-syncrepl-consumer-persist-ldap.conf
+PLSRPROVIDERCONF=$DATADIR/slapd-syncrepl-multiproxy.conf
+DDSCONF=$DATADIR/slapd-dds.conf
+PASSWDCONF=$DATADIR/slapd-passwd.conf
+UNDOCONF=$DATADIR/slapd-config-undo.conf
+NAKEDCONF=$DATADIR/slapd-config-naked.conf
+VALREGEXCONF=$DATADIR/slapd-valregex.conf
+
+DYNAMICCONF=$DATADIR/slapd-dynamic.ldif
+
+SLAPDLLOADCONF=$DATADIR/slapd-lload.conf
+LLOADDCONF=$DATADIR/lloadd.conf
+LLOADDEMPTYCONF=$DATADIR/lloadd-empty.conf
+LLOADDANONCONF=$DATADIR/lloadd-anon.conf
+LLOADDUNREACHABLECONF=$DATADIR/lloadd-backend-issues.conf
+LLOADDTLSCONF=$DATADIR/lloadd-tls.conf
+LLOADDSASLCONF=$DATADIR/lloadd-sasl.conf
+
+# generated files
+CONF1=$TESTDIR/slapd.1.conf
+CONF2=$TESTDIR/slapd.2.conf
+CONF3=$TESTDIR/slapd.3.conf
+CONF4=$TESTDIR/slapd.4.conf
+CONF5=$TESTDIR/slapd.5.conf
+CONF6=$TESTDIR/slapd.6.conf
+ADDCONF=$TESTDIR/slapadd.conf
+CONFLDIF=$TESTDIR/slapd-dynamic.ldif
+
+LOG1=$TESTDIR/slapd.1.log
+LOG2=$TESTDIR/slapd.2.log
+LOG3=$TESTDIR/slapd.3.log
+LOG4=$TESTDIR/slapd.4.log
+LOG5=$TESTDIR/slapd.5.log
+LOG6=$TESTDIR/slapd.6.log
+SLAPADDLOG1=$TESTDIR/slapadd.1.log
+SLURPLOG=$TESTDIR/slurp.log
+
+CONFIGPWF=$TESTDIR/configpw
+
+LIBTOOL="${LIBTOOL-$TESTWD/../libtool}"
+# wrappers (valgrind, gdb, environment variables, etc.)
+if [ -n "$WRAPPER" ]; then
+ : # skip
+elif [ "$SLAPD_COMMON_WRAPPER" = gdb ]; then
+ WRAPPER="$ABS_SRCDIR/scripts/grandchild_wrapper.py gdb -nx -x $ABS_SRCDIR/scripts/gdb.py -batch-silent -return-child-result --args"
+elif [ "$SLAPD_COMMON_WRAPPER" = valgrind ]; then
+ WRAPPER="valgrind --log-file=$TESTDIR/valgrind.%p.log --fullpath-after=`dirname $ABS_SRCDIR` --keep-debuginfo=yes --leak-check=full"
+elif [ "$SLAPD_COMMON_WRAPPER" = "valgrind-errstop" ]; then
+ WRAPPER="valgrind --log-file=$TESTDIR/valgrind.%p.log --vgdb=yes --vgdb-error=1"
+elif [ "$SLAPD_COMMON_WRAPPER" = vgdb ]; then
+ WRAPPER="valgrind --log-file=$TESTDIR/valgrind.%p.log --vgdb=yes --vgdb-error=0"
+fi
+
+if [ -n "$WRAPPER" ]; then
+ SLAPD_WRAPPER="$LIBTOOL --mode=execute env $WRAPPER"
+fi
+
+# args
+SASLARGS="-Q"
+TOOLARGS="-x $LDAP_TOOLARGS"
+TOOLPROTO="-P 3"
+
+# cmds
+CONFFILTER=$SRCDIR/scripts/conf.sh
+CONFDIRSYNC=$SRCDIR/scripts/confdirsync.sh
+
+MONITORDATA=$SRCDIR/scripts/monitor_data.sh
+
+SLAPADD="$SLAPD_WRAPPER $TESTWD/../servers/slapd/slapd -Ta -d 0 $LDAP_VERBOSE"
+SLAPCAT="$SLAPD_WRAPPER $TESTWD/../servers/slapd/slapd -Tc -d 0 $LDAP_VERBOSE"
+SLAPINDEX="$SLAPD_WRAPPER $TESTWD/../servers/slapd/slapd -Ti -d 0 $LDAP_VERBOSE"
+SLAPMODIFY="$SLAPD_WRAPPER $TESTWD/../servers/slapd/slapd -Tm -d 0 $LDAP_VERBOSE"
+SLAPPASSWD="$SLAPD_WRAPPER $TESTWD/../servers/slapd/slapd -Tpasswd"
+
+unset DIFF_OPTIONS
+# NOTE: -u/-c is not that portable...
+DIFF="diff -i"
+CMP="diff -i"
+BCMP="diff -iB"
+CMPOUT=/dev/null
+SLAPD="$SLAPD_WRAPPER $TESTWD/../servers/slapd/slapd -s0"
+LLOADD="$SLAPD_WRAPPER $TESTWD/../servers/lloadd/lloadd -s0"
+LDAPPASSWD="$CLIENTDIR/ldappasswd $TOOLARGS"
+LDAPSASLSEARCH="$CLIENTDIR/ldapsearch $SASLARGS $TOOLPROTO $LDAP_TOOLARGS -LLL"
+LDAPSASLWHOAMI="$CLIENTDIR/ldapwhoami $SASLARGS $LDAP_TOOLARGS"
+LDAPSEARCH="$CLIENTDIR/ldapsearch $TOOLPROTO $TOOLARGS -LLL"
+LDAPRSEARCH="$CLIENTDIR/ldapsearch $TOOLPROTO $TOOLARGS"
+LDAPDELETE="$CLIENTDIR/ldapdelete $TOOLPROTO $TOOLARGS"
+LDAPMODIFY="$CLIENTDIR/ldapmodify $TOOLPROTO $TOOLARGS"
+LDAPADD="$CLIENTDIR/ldapmodify -a $TOOLPROTO $TOOLARGS"
+LDAPMODRDN="$CLIENTDIR/ldapmodrdn $TOOLPROTO $TOOLARGS"
+LDAPWHOAMI="$CLIENTDIR/ldapwhoami $TOOLARGS"
+LDAPCOMPARE="$CLIENTDIR/ldapcompare $TOOLARGS"
+LDAPEXOP="$CLIENTDIR/ldapexop $TOOLARGS"
+SLAPDTESTER=$PROGDIR/slapd-tester
+LDIFFILTER=$PROGDIR/ldif-filter
+SLAPDMTREAD=$PROGDIR/slapd-mtread
+LVL=${SLAPD_DEBUG-0x4105}
+LOCALHOST=localhost
+LOCALIP=127.0.0.1
+BASEPORT=${SLAPD_BASEPORT-9010}
+PORT1=`expr $BASEPORT + 1`
+PORT2=`expr $BASEPORT + 2`
+PORT3=`expr $BASEPORT + 3`
+PORT4=`expr $BASEPORT + 4`
+PORT5=`expr $BASEPORT + 5`
+PORT6=`expr $BASEPORT + 6`
+KDCPORT=`expr $BASEPORT + 7`
+URI1="ldap://${LOCALHOST}:$PORT1/"
+URIP1="ldap://${LOCALIP}:$PORT1/"
+URI2="ldap://${LOCALHOST}:$PORT2/"
+URIP2="ldap://${LOCALIP}:$PORT2/"
+URI3="ldap://${LOCALHOST}:$PORT3/"
+URIP3="ldap://${LOCALIP}:$PORT3/"
+URI4="ldap://${LOCALHOST}:$PORT4/"
+URIP4="ldap://${LOCALIP}:$PORT4/"
+URI5="ldap://${LOCALHOST}:$PORT5/"
+URIP5="ldap://${LOCALIP}:$PORT5/"
+URI6="ldap://${LOCALHOST}:$PORT6/"
+URIP6="ldap://${LOCALIP}:$PORT6/"
+SURI1="ldaps://${LOCALHOST}:$PORT1/"
+SURIP1="ldaps://${LOCALIP}:$PORT1/"
+SURI2="ldaps://${LOCALHOST}:$PORT2/"
+SURIP2="ldaps://${LOCALIP}:$PORT2/"
+SURI3="ldaps://${LOCALHOST}:$PORT3/"
+SURIP3="ldaps://${LOCALIP}:$PORT3/"
+SURI4="ldaps://${LOCALHOST}:$PORT4/"
+SURIP4="ldaps://${LOCALIP}:$PORT4/"
+SURI5="ldaps://${LOCALHOST}:$PORT5/"
+SURIP5="ldaps://${LOCALIP}:$PORT5/"
+SURI6="ldaps://${LOCALHOST}:$PORT6/"
+SURIP6="ldaps://${LOCALIP}:$PORT6/"
+
+KRB5REALM="K5.REALM"
+KDCHOST=$LOCALHOST
+
+# LDIF
+LDIF=$DATADIR/test.ldif
+LDIFADD1=$DATADIR/do_add.1
+LDIFGLUED=$DATADIR/test-glued.ldif
+LDIFORDERED=$DATADIR/test-ordered.ldif
+LDIFORDEREDCP=$DATADIR/test-ordered-cp.ldif
+LDIFORDEREDNOCP=$DATADIR/test-ordered-nocp.ldif
+LDIFBASE=$DATADIR/test-base.ldif
+LDIFPASSWD=$DATADIR/passwd.ldif
+LDIFWHOAMI=$DATADIR/test-whoami.ldif
+LDIFPASSWDOUT=$DATADIR/passwd-out.ldif
+LDIFPPOLICY=$DATADIR/ppolicy.ldif
+LDIFLANG=$DATADIR/test-lang.ldif
+LDIFLANGOUT=$DATADIR/lang-out.ldif
+LDIFREF=$DATADIR/referrals.ldif
+LDIFREFINT=$DATADIR/test-refint.ldif
+LDIFUNIQUE=$DATADIR/test-unique.ldif
+LDIFLIMITS=$DATADIR/test-limits.ldif
+LDIFDN=$DATADIR/test-dn.ldif
+LDIFEMPTYDN1=$DATADIR/test-emptydn1.ldif
+LDIFEMPTYDN2=$DATADIR/test-emptydn2.ldif
+LDIFIDASSERT1=$DATADIR/test-idassert1.ldif
+LDIFIDASSERT2=$DATADIR/test-idassert2.ldif
+LDIFLDAPGLUE1=$DATADIR/test-ldapglue.ldif
+LDIFLDAPGLUE2=$DATADIR/test-ldapgluepeople.ldif
+LDIFLDAPGLUE3=$DATADIR/test-ldapgluegroups.ldif
+LDIFCOMPMATCH=$DATADIR/test-compmatch.ldif
+LDIFCHAIN1=$DATADIR/test-chain1.ldif
+LDIFCHAIN2=$DATADIR/test-chain2.ldif
+LDIFTRANSLUCENTDATA=$DATADIR/test-translucent-data.ldif
+LDIFTRANSLUCENTCONFIG=$DATADIR/test-translucent-config.ldif
+LDIFTRANSLUCENTADD=$DATADIR/test-translucent-add.ldif
+LDIFTRANSLUCENTMERGED=$DATADIR/test-translucent-merged.ldif
+LDIFMETA=$DATADIR/test-meta.ldif
+LDIFDEREF=$DATADIR/test-deref.ldif
+LDIFVALSORT=$DATADIR/test-valsort.ldif
+SQLADD=$DATADIR/sql-add.ldif
+LDIFUNORDERED=$DATADIR/test-unordered.ldif
+LDIFREORDERED=$DATADIR/test-reordered.ldif
+LDIFMODIFY=$DATADIR/test-modify.ldif
+LDIFDIRSYNCCP=$DATADIR/test-dirsync-cp.ldif
+LDIFDIRSYNCNOCP=$DATADIR/test-dirsync-nocp.ldif
+
+# strings
+MONITOR=""
+REFDN="c=US"
+BASEDN="dc=example,dc=com"
+MANAGERDN="cn=Manager,$BASEDN"
+UPDATEDN="cn=consumer,$BASEDN"
+PASSWD=secret
+BABSDN="cn=Barbara Jensen,ou=Information Technology DivisioN,ou=People,$BASEDN"
+BJORNSDN="cn=Bjorn Jensen,ou=Information Technology DivisioN,ou=People,$BASEDN"
+BADBJORNSDN="cn=Bjorn JensenNotReally,ou=Information Technology DivisioN,ou=People,$BASEDN"
+JAJDN="cn=James A Jones 1,ou=Alumni Association,ou=People,$BASEDN"
+JOHNDDN="cn=John Doe,ou=Information Technology Division,ou=People,$BASEDN"
+MELLIOTDN="cn=Mark Elliot,ou=Alumni Association,ou=People,$BASEDN"
+REFINTDN="cn=Manager,o=refint"
+RETCODEDN="ou=RetCodes,$BASEDN"
+UNIQUEDN="cn=Manager,o=unique"
+EMPTYDNDN="cn=Manager,c=US"
+TRANSLUCENTROOT="o=translucent"
+TRANSLUCENTUSER="ou=users,o=translucent"
+TRANSLUCENTDN="uid=binder,o=translucent"
+TRANSLUCENTPASSWD="bindtest"
+METABASEDN="ou=Meta,$BASEDN"
+METAMANAGERDN="cn=Manager,$METABASEDN"
+DEREFDN="cn=Manager,o=deref"
+DEREFBASEDN="o=deref"
+VALSORTDN="cn=Manager,o=valsort"
+VALSORTBASEDN="o=valsort"
+MONITORDN="cn=Monitor"
+OPERATIONSMONITORDN="cn=Operations,$MONITORDN"
+CONNECTIONSMONITORDN="cn=Connections,$MONITORDN"
+DATABASESMONITORDN="cn=Databases,$MONITORDN"
+STATISTICSMONITORDN="cn=Statistics,$MONITORDN"
+
+# generated outputs
+SEARCHOUT=$TESTDIR/ldapsearch.out
+SEARCHOUT2=$TESTDIR/ldapsearch2.out
+SEARCHFLT=$TESTDIR/ldapsearch.flt
+SEARCHFLT2=$TESTDIR/ldapsearch2.flt
+LDIFFLT=$TESTDIR/ldif.flt
+LDIFFLT2=$TESTDIR/ldif2.flt
+TESTOUT=$TESTDIR/test.out
+INITOUT=$TESTDIR/init.out
+VALSORTOUT1=$DATADIR/valsort1.out
+VALSORTOUT2=$DATADIR/valsort2.out
+VALSORTOUT3=$DATADIR/valsort3.out
+MONITOROUT1=$DATADIR/monitor1.out
+MONITOROUT2=$DATADIR/monitor2.out
+MONITOROUT3=$DATADIR/monitor3.out
+MONITOROUT4=$DATADIR/monitor4.out
+
+SERVER1OUT=$TESTDIR/server1.out
+SERVER1FLT=$TESTDIR/server1.flt
+SERVER2OUT=$TESTDIR/server2.out
+SERVER2FLT=$TESTDIR/server2.flt
+SERVER3OUT=$TESTDIR/server3.out
+SERVER3FLT=$TESTDIR/server3.flt
+SERVER4OUT=$TESTDIR/server4.out
+SERVER4FLT=$TESTDIR/server4.flt
+SERVER5OUT=$TESTDIR/server5.out
+SERVER5FLT=$TESTDIR/server5.flt
+SERVER6OUT=$TESTDIR/server6.out
+SERVER6FLT=$TESTDIR/server6.flt
+
+PROVIDEROUT=$SERVER1OUT
+PROVIDERFLT=$SERVER1FLT
+CONSUMEROUT=$SERVER2OUT
+CONSUMER2OUT=$SERVER3OUT
+CONSUMERFLT=$SERVER2FLT
+CONSUMER2FLT=$SERVER3FLT
+
+MTREADOUT=$TESTDIR/mtread.out
+
+# original outputs for cmp
+PROXYCACHEOUT=$DATADIR/proxycache.out
+REFERRALOUT=$DATADIR/referrals.out
+SEARCHOUTPROVIDER=$DATADIR/search.out.provider
+SEARCHOUTX=$DATADIR/search.out.xsearch
+COMPSEARCHOUT=$DATADIR/compsearch.out
+MODIFYOUTPROVIDER=$DATADIR/modify.out.provider
+ADDDELOUTPROVIDER=$DATADIR/adddel.out.provider
+MODRDNOUTPROVIDER0=$DATADIR/modrdn.out.provider.0
+MODRDNOUTPROVIDER1=$DATADIR/modrdn.out.provider.1
+MODRDNOUTPROVIDER2=$DATADIR/modrdn.out.provider.2
+MODRDNOUTPROVIDER3=$DATADIR/modrdn.out.provider.3
+ACLOUTPROVIDER=$DATADIR/acl.out.provider
+REPLOUTPROVIDER=$DATADIR/repl.out.provider
+MODSRCHFILTERS=$DATADIR/modify.search.filters
+CERTIFICATETLS=$DATADIR/certificate.tls
+CERTIFICATEOUT=$DATADIR/certificate.out
+DNOUT=$DATADIR/dn.out
+EMPTYDNOUT1=$DATADIR/emptydn.out.slapadd
+EMPTYDNOUT2=$DATADIR/emptydn.out
+IDASSERTOUT=$DATADIR/idassert.out
+LDAPGLUEOUT=$DATADIR/ldapglue.out
+LDAPGLUEANONYMOUSOUT=$DATADIR/ldapglueanonymous.out
+RELAYOUT=$DATADIR/relay.out
+CHAINOUT=$DATADIR/chain.out
+CHAINREFOUT=$DATADIR/chainref.out
+CHAINMODOUT=$DATADIR/chainmod.out
+GLUESYNCOUT=$DATADIR/gluesync.out
+SQLREAD=$DATADIR/sql-read.out
+SQLWRITE=$DATADIR/sql-write.out
+TRANSLUCENTOUT=$DATADIR/translucent.search.out
+METAOUT=$DATADIR/meta.out
+METACONCURRENCYOUT=$DATADIR/metaconcurrency.out
+MANAGEOUT=$DATADIR/manage.out
+SUBTREERENAMEOUT=$DATADIR/subtree-rename.out
+ACIOUT=$DATADIR/aci.out
+DYNLISTOUT=$DATADIR/dynlist.out
+DDSOUT=$DATADIR/dds.out
+DEREFOUT=$DATADIR/deref.out
+MEMBEROFOUT=$DATADIR/memberof.out
+MEMBEROFREFINTOUT=$DATADIR/memberof-refint.out
+SHTOOL="$SRCDIR/../build/shtool"
+
diff --git a/tests/scripts/gdb.py b/tests/scripts/gdb.py
new file mode 100644
index 0000000..50b5fa9
--- /dev/null
+++ b/tests/scripts/gdb.py
@@ -0,0 +1,85 @@
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2020-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+"""
+This GDB script sets up the debugger to run the program and see if it finishes
+of its own accord or is terminated by a signal (like SIGABRT/SIGSEGV). In the
+latter case, it saves a full backtrace and core file.
+
+These signals are considered part of normal operation and will not trigger the
+above handling:
+- SIGPIPE: normal in a networked environment
+- SIGHUP: normally used to tell a process to shut down
+"""
+
+import os
+import os.path
+
+import gdb
+
+
+def format_program(inferior=None, thread=None):
+ "Format program name and p(t)id"
+
+ if thread:
+ inferior = thread.inferior
+ elif inferior is None:
+ inferior = gdb.selected_inferior()
+
+ try:
+ name = os.path.basename(inferior.progspace.filename)
+ except AttributeError: # inferior has died already
+ name = "unknown"
+
+ if thread:
+ pid = ".".join(tid for tid in thread.ptid if tid)
+ else:
+ pid = inferior.pid
+
+ return "{}.{}".format(name, pid)
+
+
+def stop_handler(event):
+ "Inferior stopped on a signal, record core, backtrace and exit"
+
+ if not isinstance(event, gdb.SignalEvent):
+ # Ignore breakpoints
+ return
+
+ thread = event.inferior_thread
+
+ identifier = format_program(thread=thread)
+ prefix = os.path.expandvars("${TESTDIR}/") + identifier
+
+ if event.stop_signal == "SIGHUP":
+ # TODO: start a timer to catch shutdown issues/deadlocks
+ gdb.execute("continue")
+ return
+
+ gdb.execute('generate-core-file {}.core'.format(prefix))
+
+ with open(prefix + ".backtrace", "w") as bt_file:
+ backtrace = gdb.execute("thread apply all backtrace full",
+ to_string=True)
+ bt_file.write(backtrace)
+
+ gdb.execute("continue")
+
+
+# We or we could allow the runner to disable randomisation
+gdb.execute("set disable-randomization off")
+
+gdb.execute("handle SIGPIPE noprint")
+gdb.execute("handle SIGINT pass")
+gdb.events.stop.connect(stop_handler)
+gdb.execute("run")
diff --git a/tests/scripts/grandchild_wrapper.py b/tests/scripts/grandchild_wrapper.py
new file mode 100755
index 0000000..b5e7194
--- /dev/null
+++ b/tests/scripts/grandchild_wrapper.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2020-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+"""
+Running slapd under GDB in our testsuite, KILLPIDS would record gdb's PID
+rather than slapd's. When we want the server to shut down, SIGHUP is sent to
+KILLPIDS but GDB cannot handle being signalled directly and the entire thing is
+terminated immediately. There might be tests that rely on slapd being given the
+chance to shut down gracefully, to do this, we need to make sure the signal is
+actually sent to slapd.
+
+This script attempts to address this shortcoming in our test suite, serving as
+the front for gdb/other wrappers, catching SIGHUPs and redirecting them to the
+oldest living grandchild. The way we start up gdb, that process should be
+slapd, our intended target.
+
+This requires the pgrep utility provided by the procps package on Debian
+systems.
+"""
+
+import asyncio
+import os
+import signal
+import sys
+
+
+async def signal_to_grandchild(child):
+ # Get the first child, that should be the one we're after
+ pgrep = await asyncio.create_subprocess_exec(
+ "pgrep", "-o", "--parent", str(child.pid),
+ stdout=asyncio.subprocess.PIPE)
+
+ stdout, _ = await pgrep.communicate()
+ if not stdout:
+ return
+
+ grandchild = [int(pid) for pid in stdout.split()][0]
+
+ os.kill(grandchild, signal.SIGHUP)
+
+
+def sighup_handler(child):
+ asyncio.create_task(signal_to_grandchild(child))
+
+
+async def main(args=None):
+ if args is None:
+ args = sys.argv[1:]
+
+ child = await asyncio.create_subprocess_exec(*args)
+
+ # If we got a SIGHUP before we got the child fully started, there's no
+ # point signalling anyway
+ loop = asyncio.get_running_loop()
+ loop.add_signal_handler(signal.SIGHUP, sighup_handler, child)
+
+ raise SystemExit(await child.wait())
+
+
+if __name__ == '__main__':
+ asyncio.run(main())
diff --git a/tests/scripts/its-all b/tests/scripts/its-all
new file mode 100755
index 0000000..f92a373
--- /dev/null
+++ b/tests/scripts/its-all
@@ -0,0 +1,52 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SHTOOL="$SRCDIR/../build/shtool"
+
+TB="" TN=""
+if test -t 1 ; then
+ TB=`$SHTOOL echo -e "%B" 2>/dev/null`
+ TN=`$SHTOOL echo -e "%b" 2>/dev/null`
+fi
+
+echo "#######################################################################"
+echo "### ###"
+echo "### regression tests ###"
+echo "### ###"
+echo "#######################################################################"
+echo "###"
+
+echo ">>>>> Executing all LDAP ITS regression tests"
+
+for CMD in $SRCDIR/data/regressions/its*/its*; do
+ # remove cruft from prior test
+ if test $PRESERVE = yes ; then
+ /bin/rm -rf testrun/db.*
+ else
+ /bin/rm -rf testrun
+ fi
+
+ echo ">>>>> Starting ${TB}`basename $CMD`${TN} ..."
+ $CMD
+ RC=$?
+ if test $RC -eq 0 ; then
+ echo ">>>>> $CMD completed ${TB}OK${TN}."
+ else
+ echo ">>>>> $CMD ${TB}failed${TN} (exit $RC)"
+ exit $RC
+ fi
+
+ echo ""
+done
diff --git a/tests/scripts/lloadd-all b/tests/scripts/lloadd-all
new file mode 100755
index 0000000..d531534
--- /dev/null
+++ b/tests/scripts/lloadd-all
@@ -0,0 +1,105 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+. $SRCDIR/scripts/defines.sh
+
+TB="" TN=""
+if test -t 1 ; then
+ TB=`$SHTOOL echo -e "%B" 2>/dev/null`
+ TN=`$SHTOOL echo -e "%b" 2>/dev/null`
+fi
+
+FAILCOUNT=0
+SKIPCOUNT=0
+SLEEPTIME=10
+
+echo ">>>>> Executing all LDAP tests for the Load Balancer"
+
+if [ -n "$NOEXIT" ]; then
+ echo "Result Test" > $TESTWD/results
+fi
+
+for CMD in $SRCDIR/scripts/lloadd/test*; do
+ case "$CMD" in
+ *~) continue;;
+ *.bak) continue;;
+ *.orig) continue;;
+ *.sav) continue;;
+ *) test -f "$CMD" || continue;;
+ esac
+
+ # remove cruft from prior test
+ if test $PRESERVE = yes ; then
+ /bin/rm -rf $TESTDIR/db.*
+ else
+ /bin/rm -rf $TESTDIR
+ fi
+ if test $BACKEND = ndb ; then
+ mysql --user root <<EOF
+ drop database if exists db_1;
+ drop database if exists db_2;
+ drop database if exists db_3;
+ drop database if exists db_4;
+ drop database if exists db_5;
+ drop database if exists db_6;
+EOF
+ fi
+
+ BCMD=`basename $CMD`
+ if [ -x "$CMD" ]; then
+ echo ">>>>> Starting ${TB}$BCMD${TN} for $BACKEND..."
+ START=`date +%s`
+ $CMD
+ RC=$?
+ END=`date +%s`
+
+ if test $RC -eq 0 ; then
+ echo ">>>>> $BCMD completed ${TB}OK${TN} for $BACKEND after $(( $END - $START )) seconds."
+ else
+ echo ">>>>> $BCMD ${TB}failed${TN} for $BACKEND after $(( $END - $START )) seconds"
+ FAILCOUNT=`expr $FAILCOUNT + 1`
+
+ if [ -n "$NOEXIT" ]; then
+ echo "Continuing."
+ else
+ echo "(exit $RC)"
+ exit $RC
+ fi
+ fi
+ else
+ echo ">>>>> Skipping ${TB}$BCMD${TN} for $BACKEND."
+ SKIPCOUNT=`expr $SKIPCOUNT + 1`
+ RC="-"
+ fi
+
+ if [ -n "$NOEXIT" ]; then
+ echo "$RC $BCMD" >> $TESTWD/results
+ fi
+
+# echo ">>>>> waiting $SLEEPTIME seconds for things to exit"
+# sleep $SLEEPTIME
+ echo ""
+done
+
+if [ -n "$NOEXIT" ]; then
+ if [ "$FAILCOUNT" -gt 0 ]; then
+ cat $TESTWD/results
+ echo "$FAILCOUNT tests for $BACKEND ${TB}failed${TN}. Please review the test log."
+ else
+ echo "All executed tests for $BACKEND ${TB}succeeded${TN}."
+ fi
+fi
+
+echo "$SKIPCOUNT tests for the Load Balancer were ${TB}skipped${TN}."
diff --git a/tests/scripts/lloadd/test000-rootdse b/tests/scripts/lloadd/test000-rootdse
new file mode 100755
index 0000000..9046b16
--- /dev/null
+++ b/tests/scripts/lloadd/test000-rootdse
@@ -0,0 +1,118 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2 $DBDIR3
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf
+
+echo "Starting slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $SCHEMACONF > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+echo "Starting a second slapd on TCP/IP port $PORT3..."
+sed -e "s,$DBDIR1,$DBDIR2," < $CONF2 > $CONF3
+$SLAPD -f $CONF3 -h $URI3 -d $LVL > $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+echo "Starting a third slapd on TCP/IP port $PORT4..."
+sed -e "s,$DBDIR1,$DBDIR3," < $CONF2 > $CONF4
+$SLAPD -f $CONF4 -h $URI4 -d $LVL > $LOG4 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+echo "Starting lloadd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $LLOADDANONCONF > $CONF1.lloadd
+if test $AC_lloadd = lloaddyes; then
+ $LLOADD -f $CONF1.lloadd -h $URI1 -d $LVL > $LOG1 2>&1 &
+else
+ . $CONFFILTER $BACKEND < $SLAPDLLOADCONF > $CONF1.slapd
+ # FIXME: this won't work on Windows, but lloadd doesn't support Windows yet
+ $SLAPD -f $CONF1.slapd -h $URI6 -d $LVL > $LOG1 2>&1 &
+fi
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep $SLEEP0
+
+echo "Using ldapsearch to retrieve the root DSE..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -b "" -s base -H $URI1 \
+ '@extensibleObject' > $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for lloadd to start..."
+ sleep $SLEEP1
+done
+
+if test $RC = 0 ; then
+ echo "Using ldapsearch to retrieve the cn=Subschema..."
+ $LDAPSEARCH -b "cn=Subschema" -s base -H $URI1 \
+ '(&(objectClasses=top)(objectClasses=2.5.6.0))' cn objectClass \
+ >> $SEARCHOUT 2>&1
+ RC=$?
+
+fi
+
+if test $RC = 0 ; then
+ echo "Using ldapsearch to retrieve the cn=Monitor..."
+ $LDAPSEARCH -b "cn=Monitor" -s base -H $URI1 \
+ '@monitor' >> $SEARCHOUT 2>&1
+ RC=$?
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+count=3
+if test $RC != 0 ; then
+ echo ">>>>> Test failed"
+else
+ RC=`grep '^dn:' $SEARCHOUT | wc -l`
+ if test $RC != $count ; then
+ echo ">>>>> Test failed: expected $count entries, got" $RC
+ RC=1
+ else
+ echo ">>>>> Test succeeded"
+ RC=0
+ fi
+fi
+
+test $KILLSERVERS != no && wait
+
+exit $RC
diff --git a/tests/scripts/lloadd/test001-backend-issues b/tests/scripts/lloadd/test001-backend-issues
new file mode 100755
index 0000000..9b0b0b2
--- /dev/null
+++ b/tests/scripts/lloadd/test001-backend-issues
@@ -0,0 +1,218 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf
+
+echo "Starting an empty slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $SCHEMACONF > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+PID2="$PID"
+KILLPIDS="$PID"
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $CONFTWO > $CONF3
+$SLAPADD -f $CONF3 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Running slapindex to index slapd database..."
+$SLAPINDEX -f $CONF3
+RC=$?
+if test $RC != 0 ; then
+ echo "warning: slapindex failed ($RC)"
+ echo " assuming no indexing support"
+fi
+
+echo "Starting second slapd on TCP/IP port $PORT3..."
+$SLAPD -f $CONF3 -h $URI3 -d $LVL > $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+PID3="$PID"
+KILLPIDS="$KILLPIDS $PID"
+
+sleep $SLEEP0
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI3 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting lloadd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $LLOADDUNREACHABLECONF > $CONF1.lloadd
+if test $AC_lloadd = lloaddyes; then
+ $LLOADD -f $CONF1.lloadd -h $URI1 -d $LVL > $LOG1 2>&1 &
+else
+ . $CONFFILTER $BACKEND < $SLAPDLLOADCONF > $CONF1.slapd
+ $SLAPD -f $CONF1.slapd -h $URI6 -d $LVL > $LOG1 2>&1 &
+fi
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for lloadd to start..."
+ sleep $SLEEP1
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing exact searching..."
+echo "# Testing exact searching..." > $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(sn=jENSEN)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing approximate searching..."
+echo "# Testing approximate searching..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(sn~=jENSEN)' name >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing OR searching..."
+echo "# Testing OR searching..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(|(givenname=Xx*yY*Z)(cn=)(undef=*)(objectclass=groupofnames)(sn=jones)(member=cn=Manager,dc=example,dc=com)(uniqueMember=cn=Manager,dc=example,dc=com))' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing AND matching and ends-with searching..."
+echo "# Testing AND matching and ends-with searching..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "ou=groups,$BASEDN" -s one -H $URI1 \
+ '(&(objectclass=groupofnames)(cn=A*)(member=cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com))' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing NOT searching..."
+echo "# Testing NOT searching..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(!(objectclass=pilotPerson))' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing objectClass/attributeType inheritance ..."
+echo "# Testing objectClass/attributeType inheritance ..." >> $SEARCHOUT
+$LDAPSEARCH -M -a never -S "" -b "$BASEDN" -H $URI1 \
+ '(&(objectClass=inetorgperson)(userid=uham))' \
+ "2.5.4.0" "userid" >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=$SEARCHOUTPROVIDER
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/lloadd/test002-load b/tests/scripts/lloadd/test002-load
new file mode 100755
index 0000000..942f6bc
--- /dev/null
+++ b/tests/scripts/lloadd/test002-load
@@ -0,0 +1,174 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test x$TESTLOOPS = x ; then
+ TESTLOOPS=50
+fi
+
+if test x$TESTCHILDREN = x ; then
+ TESTCHILDREN=20
+fi
+
+if test x$MAXRETRIES = x ; then
+ MAXRETRIES=5
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $CONF > $CONF2
+$SLAPADD -f $CONF2 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting a slapd on TCP/IP port $PORT2..."
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+PID2="$PID"
+KILLPIDS="$PID"
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $CONFTWO > $CONF3
+$SLAPADD -f $CONF3 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Running slapindex to index slapd database..."
+$SLAPINDEX -f $CONF3
+RC=$?
+if test $RC != 0 ; then
+ echo "warning: slapindex failed ($RC)"
+ echo " assuming no indexing support"
+fi
+
+echo "Starting second slapd on TCP/IP port $PORT3..."
+$SLAPD -f $CONF3 -h $URI3 -d $LVL > $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+PID2="$PID"
+KILLPIDS="$KILLPIDS $PID"
+
+sleep $SLEEP0
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI3 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting lloadd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $LLOADDCONF > $CONF1.lloadd
+if test $AC_lloadd = lloaddyes; then
+ $LLOADD -f $CONF1.lloadd -h $URI1 -d $LVL > $LOG1 2>&1 &
+else
+ . $CONFFILTER $BACKEND < $SLAPDLLOADCONF > $CONF1.slapd
+ # FIXME: this won't work on Windows, but lloadd doesn't support Windows yet
+ $SLAPD -f $CONF1.slapd -h $URI6 -d $LVL > $LOG1 2>&1 &
+fi
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for lloadd to start..."
+ sleep $SLEEP1
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# fix test data to include back-monitor, if available
+# NOTE: copies do_* files from $DATADIR to $TESTDIR
+$MONITORDATA "$DATADIR" "$TESTDIR"
+
+
+echo "Using tester for concurrent server access ($TESTCHILDREN x $TESTLOOPS ops)..."
+$SLAPDTESTER -P "$PROGDIR" -d "$TESTDIR" \
+ -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -t 1 -l $TESTLOOPS -r $MAXRETRIES -j $TESTCHILDREN \
+ -i '*INVALID_CREDENTIALS,*BUSY,UNWILLING_TO_PERFORM'
+RC=$?
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+if test $RC != 0 ; then
+ echo "slapd-tester failed ($RC)!"
+ exit $RC
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/lloadd/test003-cnconfig b/tests/scripts/lloadd/test003-cnconfig
new file mode 100755
index 0000000..edf5801
--- /dev/null
+++ b/tests/scripts/lloadd/test003-cnconfig
@@ -0,0 +1,433 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf
+
+if test $AC_lloadd = lloaddyes ; then
+ echo "Load balancer module not available, skipping..."
+ exit 0
+fi
+
+echo "Starting the first slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $CONF > $CONF2
+$SLAPADD -f $CONF2 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Running slapindex to index slapd database..."
+$SLAPINDEX -f $CONF2
+RC=$?
+if test $RC != 0 ; then
+ echo "warning: slapindex failed ($RC)"
+ echo " assuming no indexing support"
+fi
+
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+PID2="$PID"
+KILLPIDS="$PID"
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $CONFTWO > $CONF3
+$SLAPADD -f $CONF3 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Running slapindex to index slapd database..."
+$SLAPINDEX -f $CONF3
+RC=$?
+if test $RC != 0 ; then
+ echo "warning: slapindex failed ($RC)"
+ echo " assuming no indexing support"
+fi
+
+echo "Starting second slapd on TCP/IP port $PORT3..."
+$SLAPD -f $CONF3 -h $URI3 -d $LVL > $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+PID3="$PID"
+KILLPIDS="$KILLPIDS $PID"
+
+sleep $SLEEP0
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI3 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting lloadd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $LLOADDUNREACHABLECONF > $CONF1.lloadd
+. $CONFFILTER $BACKEND < $SLAPDLLOADCONF > $CONF1.slapd
+$SLAPD -f $CONF1.slapd -h $URI6 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+echo "Testing lloadd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for lloadd to start..."
+ sleep $SLEEP1
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+echo "Testing cn=config searching..."
+$LDAPSEARCH -H $URI6 -D cn=config -y $CONFIGPWF \
+ -s sub -b "olcBackend={0}lload,cn=config" '(objectclass=*)' > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Deleting backends"
+
+for i in 1 2 3 4 ; do
+ echo "cn={0}server "$i",olcBackend={0}lload,cn=config"
+ $LDAPDELETE -H $URI6 -D cn=config -y $CONFIGPWF \
+ "cn={0}server "$i",olcBackend={0}lload,cn=config" > /dev/null 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "deleting server failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+done
+
+echo "Testing cn=config searching..."
+$LDAPSEARCH -H $URI6 -D cn=config -y $CONFIGPWF \
+ -s sub -b "olcBackend={0}lload,cn=config" '(objectclass=*)' > /dev/null 2>&1
+
+
+echo "# Testing exact searching..."
+
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(sn=jENSEN)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 52 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing adding Server "
+$LDAPADD -D cn=config -H $URI6 -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+dn: cn=server 7,olcBackend={0}lload,cn=config
+objectClass: olcBkLloadBackendConfig
+cn: server 7
+olcBkLloadBackendUri: $URI3
+olcBkLloadBindconns: 2
+olcBkLloadMaxPendingConns: 3
+olcBkLloadMaxPendingOps: 5
+olcBkLloadNumconns: 3
+olcBkLloadRetry: 5000
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for cn=server 7 ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Verifying balancer operation..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(sn=jENSEN)' >> $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for backend to start..."
+ sleep $SLEEP1
+done
+
+echo "Testing bindconf modify"
+$LDAPMODIFY -D cn=config -H $URI6 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcBackend={0}lload,cn=config
+changetype: modify
+replace: olcBkLloadBindconf
+olcBkLloadBindconf: bindmethod=simple timeout=0 network-timeout=0 binddn="cn=wrongmanager,dc=example,dc=com" credentials="secret"
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "modify failed for bindconf ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "# Sending a search request..."
+
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(sn=jENSEN)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 52 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Restoring bindconf value"
+$LDAPMODIFY -D cn=config -H $URI6 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcBackend={0}lload,cn=config
+changetype: modify
+replace: olcBkLloadBindconf
+olcBkLloadBindconf: bindmethod=simple timeout=0 network-timeout=0 binddn="cn=Manager,dc=example,dc=com" credentials="secret"
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "modify failed for bindconf ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+echo "Verifying balancer operation..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(sn=jENSEN)' >> $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for backend to start..."
+ sleep $SLEEP1
+done
+
+echo "Testing global attributes"
+echo "Testing olcBkLloadMaxPDUPerCycle modify"
+$LDAPMODIFY -D cn=config -H $URI6 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcBackend={0}lload,cn=config
+changetype: modify
+replace: olcBkLloadMaxPDUPerCycle
+olcBkLloadMaxPDUPerCycle: 2000
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "modify failed for olcBkLloadMaxPDUPerCycle($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Sending a search request..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(sn=jENSEN)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing olcBkLloadSockbufMaxClient modify"
+$LDAPMODIFY -D cn=config -H $URI6 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcBackend={0}lload,cn=config
+changetype: modify
+replace: olcBkLloadSockbufMaxClient
+olcBkLloadSockbufMaxClient: 20000
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "modify failed for olcBkLloadSockbufMaxClient($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Sending a search request..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(sn=jENSEN)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing olcBkLloadSockbufMaxUpstream modify"
+$LDAPMODIFY -D cn=config -H $URI6 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcBackend={0}lload,cn=config
+changetype: modify
+replace: olcBkLloadSockbufMaxUpstream
+olcBkLloadSockbufMaxUpstream: 200000
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "modify failed for olcBkLloadSockbufMaxUpstream($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Sending a search request..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(sn=jENSEN)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing olcBkLloadIOTimeout modify"
+$LDAPMODIFY -D cn=config -H $URI6 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcBackend={0}lload,cn=config
+changetype: modify
+replace: olcBkLloadIOTimeout
+olcBkLloadIOTimeout: 20000
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "modify failed for olcBkLloadWriteTimeout($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Sending a search request..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(sn=jENSEN)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing backend attributes"
+echo "Testing olcBkLloadBindconns modify"
+$LDAPMODIFY -D cn=config -H $URI6 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: cn={0}server 7,olcBackend={0}lload,cn=config
+changetype: modify
+replace: olcBkLloadBindconns
+olcBkLloadBindconns: 20
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "modify failed for olcBkLloadBindconns($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing exact searching..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(sn=jENSEN)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+echo "Testing olcBkLloadMaxPendingConns modify"
+$LDAPMODIFY -D cn=config -H $URI6 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: cn={0}server 7,olcBackend={0}lload,cn=config
+changetype: modify
+replace: olcBkLloadMaxPendingConns
+olcBkLloadMaxPendingConns: 30
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "modify failed for olcBkLloadMaxPendingConns($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing exact searching..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(sn=jENSEN)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/lloadd/test004-monitor b/tests/scripts/lloadd/test004-monitor
new file mode 100755
index 0000000..d1db7c1
--- /dev/null
+++ b/tests/scripts/lloadd/test004-monitor
@@ -0,0 +1,345 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf
+
+if test $AC_lloadd = lloaddyes ; then
+ echo "Load balancer module not available, skipping..."
+ exit 0
+fi
+
+# Monitor counts are unstable in the face of concurrency, since different
+# clients may get different upstreams assigned for their operations. This might
+# also change later when tiered load balancing is available.
+# Another constraint is that some global counts are updated by the statistics
+# collection task scheduled to run every second.
+#
+# This test assumes current round-robin policy:
+# - default backend is rotated every time we successfully pick an upstream
+# - upstream connections within the same backend are rotated in the same way
+# - the monitor entry order for upstream connections reflects the connection
+# order within its CIRCLEQ_
+
+echo "Starting the first slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $CONF > $CONF2
+$SLAPADD -f $CONF2 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Running slapindex to index slapd database..."
+$SLAPINDEX -f $CONF2
+RC=$?
+if test $RC != 0 ; then
+ echo "warning: slapindex failed ($RC)"
+ echo " assuming no indexing support"
+fi
+
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+PID2="$PID"
+KILLPIDS="$PID"
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $CONFTWO > $CONF3
+$SLAPADD -f $CONF3 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Running slapindex to index slapd database..."
+$SLAPINDEX -f $CONF3
+RC=$?
+if test $RC != 0 ; then
+ echo "warning: slapindex failed ($RC)"
+ echo " assuming no indexing support"
+fi
+
+echo "Starting second slapd on TCP/IP port $PORT3..."
+$SLAPD -f $CONF3 -h $URI3 -d $LVL > $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+PID3="$PID"
+KILLPIDS="$KILLPIDS $PID"
+
+sleep $SLEEP0
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI3 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting lloadd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $LLOADDEMPTYCONF > $CONF1.lloadd
+. $CONFFILTER $BACKEND < $SLAPDLLOADCONF > $CONF1.slapd
+$SLAPD -f $CONF1.slapd -h $URI6 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI6 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for lloadd to start..."
+ sleep $SLEEP1
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Sending a search request to prime the counters..."
+$LDAPSEARCH -b "$BASEDN" -s base -H $URI1 >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 52 ; then
+ echo "ldapsearch should have failed ($RC != 52)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Retrieving data from cn=monitor..."
+echo "# Retrieving data from an empty lload's cn=monitor..." >>$SEARCHOUT
+echo "# Operations received:" >>$SEARCHOUT
+echo "# Bind: 1 (0 forwarded)" >>$SEARCHOUT
+echo "# Search: 0" >>$SEARCHOUT
+echo "# Unbind: 1" >>$SEARCHOUT
+$LDAPSEARCH -b "cn=Load Balancer,cn=Backends,cn=monitor" -H $URI6 \
+ olmBalancer olmBalancerServer olmBalancerOperation olmBalancerConnection >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding first backend server..."
+$LDAPMODIFY -D cn=config -H $URI6 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: cn=first,olcBackend={0}lload,cn=config
+changetype: add
+objectClass: olcBkLloadBackendConfig
+olcBkLloadBackendUri: $URI2
+olcBkLloadMaxPendingConns: 3
+olcBkLloadMaxPendingOps: 5
+olcBkLloadRetry: 1000
+olcBkLloadNumconns: 2
+olcBkLloadBindconns: 2
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for backend ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# At the moment, the global counters are updated by a recurring job,
+# wait for it to settle
+echo "Waiting until connections are established..."
+for i in 0 1 2 3 4 5; do
+ $LDAPCOMPARE "cn=Load Balancer,cn=Backends,cn=monitor" -H $URI6 \
+ 'olmOutgoingConnections:4' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 6 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds until connections are established..."
+ sleep $SLEEP1
+done
+if test $RC != 6 ; then
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Retrieving data from cn=monitor again..."
+echo >>$SEARCHOUT
+echo "# Retrieving data from lload's cn=monitor..." >>$SEARCHOUT
+$LDAPSEARCH -b "cn=Load Balancer,cn=Backends,cn=monitor" -H $URI6 \
+ olmBalancer olmBalancerServer olmBalancerOperation olmBalancerConnection >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding another backend server..."
+$LDAPMODIFY -D cn=config -H $URI6 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: cn=server 2,olcBackend={0}lload,cn=config
+changetype: add
+objectClass: olcBkLloadBackendConfig
+olcBkLloadBackendUri: $URI3
+olcBkLloadMaxPendingConns: 3
+olcBkLloadMaxPendingOps: 5
+olcBkLloadRetry: 1000
+olcBkLloadNumconns: 4
+olcBkLloadBindconns: 5
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for backend ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# At the moment, the global counters are updated by a recurring job,
+# wait for it to settle
+echo "Waiting until connections are established..."
+for i in 0 1 2 3 4 5; do
+ $LDAPCOMPARE "cn=Load Balancer,cn=Backends,cn=monitor" -H $URI6 \
+ 'olmOutgoingConnections:13' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 6 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds until connections are established..."
+ sleep $SLEEP1
+done
+if test $RC != 6 ; then
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Sending another search request..."
+$LDAPSEARCH -b "$BASEDN" -s base -H $URI1 >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Sending a WhoAmI? request..."
+$LDAPWHOAMI -D "$BABSDN" -w bjensen -H $URI1 >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# At the moment, the global counters are updated by a recurring job,
+# wait for it to settle
+echo "Waiting until global counters are updated..."
+for i in 0 1 2 3 4 5; do
+ $LDAPCOMPARE "cn=Other,cn=Operations,cn=Load Balancer,cn=Backends,cn=monitor" -H $URI6 \
+ 'olmCompletedOps:2' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 6 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds until connections are established..."
+ sleep $SLEEP1
+done
+if test $RC != 6 ; then
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Retrieving data from cn=monitor again..."
+echo >>$SEARCHOUT
+echo "# Retrieving data after a search+WhoAmI?..." >>$SEARCHOUT
+echo "# Operations received:" >>$SEARCHOUT
+echo "# Bind: 3 (2 forwarded)" >>$SEARCHOUT
+echo "# Search: 1" >>$SEARCHOUT
+echo "# Extended: 1 (WhoAmI?)" >>$SEARCHOUT
+echo "# Unbind: 3" >>$SEARCHOUT
+$LDAPSEARCH -b "cn=Load Balancer,cn=Backends,cn=monitor" -H $URI6 \
+ olmBalancer olmBalancerServer olmBalancerOperation olmBalancerConnection >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=$DATADIR/lloadd/monitor.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/lloadd/test005-tls b/tests/scripts/lloadd/test005-tls
new file mode 100755
index 0000000..7d6f87c
--- /dev/null
+++ b/tests/scripts/lloadd/test005-tls
@@ -0,0 +1,272 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $WITH_TLS = no ; then
+ echo "TLS support not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+cp -r $DATADIR/tls $TESTDIR
+
+cd $TESTWD
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $TLSSASLCONF > $CONF2
+$SLAPADD -f $CONF2 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting a slapd on TCP/IP port $PORT3..."
+$SLAPD -f $CONF2 -h $URI3 -d $LVL > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+PID2="$PID"
+KILLPIDS="$PID"
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI3 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $CONFTWO > $CONF3
+# FIXME: Hack!
+echo "TLSCertificateKeyFile $TESTDIR/tls/private/localhost.key" >>$CONF3
+echo "TLSCertificateFile $TESTDIR/tls/certs/localhost.crt" >>$CONF3
+echo 'authz-regexp "email=([^,]*),cn=[^,]*,ou=OpenLDAP,o=OpenLDAP Foundation,st=CA,c=US" ldap:///ou=People,dc=example,dc=com??sub?(mail=$1)' >>$CONF3
+$SLAPADD -f $CONF3 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running slapindex to index slapd database..."
+$SLAPINDEX -f $CONF3
+RC=$?
+if test $RC != 0 ; then
+ echo "warning: slapindex failed ($RC)"
+ echo " assuming no indexing support"
+fi
+
+echo "Starting second slapd on TCP/IP port $PORT4 w/ ldaps..."
+$SLAPD -f $CONF3 -h $SURI4 -d $LVL > $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+PID2="$PID"
+KILLPIDS="$KILLPIDS $PID"
+
+sleep $SLEEP0
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -o tls-reqcert=never -s base -b "$MONITOR" -H $SURI4 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting lloadd on TCP/IP port $PORT1 and ldaps $PORT2..."
+. $CONFFILTER $BACKEND < $LLOADDTLSCONF > $CONF1.lloadd
+if test $AC_lloadd = lloaddyes; then
+ $LLOADD -f $CONF1.lloadd -h "$URI1 $SURI2" -d $LVL > $LOG1 2>&1 &
+else
+ . $CONFFILTER $BACKEND < $SLAPDLLOADCONF | sed -e "s,listen.*,listen \"$URI1 $SURI2\"," > $CONF1.slapd
+ $SLAPD -f $CONF1.slapd -h $URI6 -d $LVL > $LOG1 2>&1 &
+fi
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for lloadd to start..."
+ sleep $SLEEP1
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo -n "Using ldapsearch with startTLS with no server cert validation...."
+$LDAPSEARCH -o tls-reqcert=never -ZZ -b "" -s base -H $URIP1 \
+ '@extensibleObject' > $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch (startTLS) failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+else
+ echo "success"
+fi
+
+echo -n "Using ldapsearch with startTLS with hard require cert...."
+$LDAPSEARCH -o tls-cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -o tls-reqcert=hard -ZZ -b "" -s base -H $URIP1 \
+ '@extensibleObject' > $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch (startTLS) failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+else
+ echo "success"
+fi
+
+if test $WITH_TLS_TYPE = openssl ; then
+ echo -n "Using ldapsearch with startTLS and specific protocol version...."
+ $LDAPSEARCH -o tls-cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -o tls-reqcert=hard -o tls-protocol-min=3.3 -ZZ -b "" -s base -H $URIP1 \
+ '@extensibleObject' > $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch (protocol-min) failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ else
+ echo "success"
+ fi
+fi
+
+echo -n "Using ldapsearch on $SURI2 with no server cert validation..."
+$LDAPSEARCH -o tls-reqcert=never -b "cn=Subschema" -s base -H $SURIP2 \
+ '(&(objectClasses=top)(objectClasses=2.5.6.0))' cn objectClass \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch (ldaps) failed($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+else
+ echo "success"
+fi
+
+echo -n "Using ldapsearch on $SURI2 with reqcert HARD and no CA cert. Should fail..."
+$LDAPSEARCH -o tls-reqcert=hard -b "cn=Subschema" -s base -H $SURIP2 \
+ '(&(objectClasses=top)(objectClasses=2.5.6.0))' cn objectClass \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC = 0 ; then
+ echo "ldapsearch (ldaps) succeeded when it should have failed($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+else
+ echo "failed correctly with error code ($RC)"
+fi
+
+echo -n "Using ldapsearch on $SURI2 with CA cert and reqcert HARD..."
+$LDAPSEARCH -o tls-cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt \
+ -o tls-reqcert=hard -b "cn=Subschema" -s base -H $SURIP2 \
+ '(&(objectClasses=top)(objectClasses=2.5.6.0))' cn objectClass \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch (ldaps) failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+else
+ echo "success"
+fi
+
+if test $WITH_SASL = no ; then
+ echo "SASL support not available, skipping client cert authentication"
+else
+ echo -n "Using ldapwhoami with SASL/EXTERNAL...."
+ $LDAPSASLWHOAMI -o tls-cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -o tls-reqcert=hard \
+ -o tls-cert=$TESTDIR/tls/certs/bjensen@mailgw.example.com.crt \
+ -o tls-key=$TESTDIR/tls/private/bjensen@mailgw.example.com.key \
+ -ZZ -Y EXTERNAL -H $URIP1 \
+ > $TESTOUT 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapwhoami (startTLS) failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ else
+ echo "success"
+ fi
+
+ echo -n "Validating mapped SASL ID..."
+ echo 'dn:cn=barbara jensen,ou=information technology division,ou=people,dc=example,dc=com' > $TESTDIR/dn.out
+ $CMP $TESTDIR/dn.out $TESTOUT > $CMPOUT
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ else
+ echo "success"
+ fi
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+if test $RC != 0 ; then
+ echo ">>>>> Test failed"
+else
+ echo ">>>>> Test succeeded"
+ RC=0
+fi
+
+test $KILLSERVERS != no && wait
+
+exit $RC
diff --git a/tests/scripts/lloadd/test006-sasl b/tests/scripts/lloadd/test006-sasl
new file mode 100755
index 0000000..a49dbbb
--- /dev/null
+++ b/tests/scripts/lloadd/test006-sasl
@@ -0,0 +1,252 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $WITH_SASL = "yes" ; then
+ if test $USE_SASL = "no" ; then
+ echo "Not asked to test SASL, skipping test, set SLAPD_USE_SASL to enable..."
+ exit 0
+ fi
+ if test $USE_SASL = "yes" ; then
+ MECH="DIGEST-MD5"
+ else
+ MECH="$USE_SASL"
+ fi
+ echo "Using SASL authc[/authz] with mech=$MECH; unset SLAPD_USE_SASL to disable"
+else
+ echo "SASL support not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+cp -r $DATADIR/tls $TESTDIR
+
+cd $TESTWD
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $TLSSASLCONF > $CONF2
+echo 'authz-regexp "^uid=([^,]*),.+" ldap:///dc=example,dc=com??sub?(|(cn=$1)(uid=$1))' >>$CONF2
+$SLAPADD -f $CONF2 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting a slapd on TCP/IP port $PORT2..."
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+PID2="$PID"
+KILLPIDS="$PID"
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $CONFTWO > $CONF3
+echo 'authz-regexp "^uid=([^,]*),.+" ldap:///dc=example,dc=com??sub?(|(cn=$1)(uid=$1))' >>$CONF3
+$SLAPADD -f $CONF3 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running slapindex to index slapd database..."
+$SLAPINDEX -f $CONF3
+RC=$?
+if test $RC != 0 ; then
+ echo "warning: slapindex failed ($RC)"
+ echo " assuming no indexing support"
+fi
+
+echo "Starting second slapd on TCP/IP port $PORT3..."
+$SLAPD -f $CONF3 -h $URI3 -d $LVL > $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+PID2="$PID"
+KILLPIDS="$KILLPIDS $PID"
+
+sleep $SLEEP0
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI3 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting lloadd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $LLOADDSASLCONF > $CONF1.lloadd
+if test $AC_lloadd = lloaddyes; then
+ $LLOADD -f $CONF1.lloadd -h $URI1 -d $LVL > $LOG1 2>&1 &
+else
+ . $CONFFILTER $BACKEND < $SLAPDLLOADCONF > $CONF1.slapd
+ $SLAPD -f $CONF1.slapd -h $URI6 -d $LVL > $LOG1 2>&1 &
+fi
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+echo "Testing lloadd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for lloadd to start..."
+ sleep $SLEEP1
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Checking whether $MECH is supported..."
+$LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectClass=*' supportedSASLMechanisms > $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+grep "supportedSASLMechanisms: $MECH" $SEARCHOUT > $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "SASL mechanism $MECH is not available, test skipped"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+fi
+
+AUTHZID="u:bjorn"
+echo "Testing lloadd's identity can assert any authzid..."
+$LDAPWHOAMI -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ -e\!"authzid=$AUTHZID" > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+AUTHZID="u:bjorn"
+echo "Testing a different identity cannot do the same thing..."
+$LDAPWHOAMI -D "$BABSDN" -H $URI1 -w bjensen \
+ -e\!"authzid=$AUTHZID" >> $TESTOUT 2>/dev/null
+RC=$?
+if test $RC != 1 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Validating WhoAmI? results..."
+echo 'dn:cn=bjorn jensen,ou=information technology division,ou=people,dc=example,dc=com' > $TESTDIR/whoami.out
+echo 'Result: Protocol error (2)
+Additional info: proxy authorization control specified multiple times' >> $TESTDIR/whoami.out
+$CMP $TESTDIR/whoami.out $TESTOUT > $CMPOUT
+
+RC=$?
+if test $RC != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+else
+ echo "Success"
+fi
+
+
+ID="jaj"
+echo "Testing ldapsearch as $ID for \"$BASEDN\" with SASL bind and identity assertion..."
+$LDAPSASLSEARCH -H $URI1 -b "$BASEDN" \
+ -Q -Y $MECH -O maxbufsize=0 -U "$ID" -w jaj > $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER -s e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - search with SASL bind and identity assertion didn't succeed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+if test $RC != 0 ; then
+ echo ">>>>> Test failed"
+else
+ echo ">>>>> Test succeeded"
+ RC=0
+fi
+
+test $KILLSERVERS != no && wait
+
+exit $RC
diff --git a/tests/scripts/monitor_data.sh b/tests/scripts/monitor_data.sh
new file mode 100755
index 0000000..7d08eda
--- /dev/null
+++ b/tests/scripts/monitor_data.sh
@@ -0,0 +1,42 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SRCDIR="$1"
+DSTDIR="$2"
+
+echo "SRCDIR $SRCDIR"
+echo "DSTDIR $DSTDIR"
+echo "pwd `pwd`"
+
+# copy test data
+cp "$SRCDIR"/do_* "$DSTDIR"
+
+# add back-monitor testing data
+cat >> "$DSTDIR/do_search.0" << EOF
+cn=Monitor
+(objectClass=*)
+cn=Monitor
+(objectClass=*)
+cn=Monitor
+(objectClass=*)
+cn=Monitor
+(objectClass=*)
+EOF
+
+cat >> "$DSTDIR/do_read.0" << EOF
+cn=Backend 1,cn=Backends,cn=Monitor
+cn=Entries,cn=Statistics,cn=Monitor
+cn=Database 1,cn=Databases,cn=Monitor
+EOF
diff --git a/tests/scripts/passwd-search b/tests/scripts/passwd-search
new file mode 100755
index 0000000..ecb07e9
--- /dev/null
+++ b/tests/scripts/passwd-search
@@ -0,0 +1,133 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+if test $# -eq 0 ; then
+ test -z "$SRCDIR" && SRCDIR="."
+else
+ SRCDIR=$1; shift
+fi
+if test $# -eq 1 ; then
+ BACKEND=$1; shift
+fi
+
+echo "running defines.sh $SRCDIR $BACKEND"
+. $SRCDIR/scripts/defines.sh
+
+if test -d "$TESTDIR"; then
+ echo "Cleaning up in $TESTDIR..."
+ /bin/rm -rf $TESTDIR/db.*
+fi
+mkdir -p $TESTDIR
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $PASSWDCONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test ${WAIT-0} != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -L -b "$BASEDN" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 1 ; then
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+ fi
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed!"
+ test "$KILLSERVERS" != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $TESTOUT
+
+echo "Testing base suffix searching..."
+$LDAPSEARCH -L -S "" -b "$BASEDN" -s base -H $URI1 \
+ '(objectclass=*)' >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed!"
+ test "$KILLSERVERS" != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo " ------------ " >> $TESTOUT
+
+echo "Testing user searching..."
+$LDAPSEARCH -L -S "" -b "uid=root,$BASEDN" -s base -H $URI1 \
+ '(objectclass=*)' >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed!"
+ test "$KILLSERVERS" != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo " ------------ " >> $TESTOUT
+
+echo "Testing exact searching..."
+$LDAPSEARCH -L -S "" -b "$BASEDN" -H $URI1 \
+ '(uid=root)' >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed!"
+ test "$KILLSERVERS" != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo " ------------ " >> $TESTOUT
+
+echo "Testing OR searching..."
+$LDAPSEARCH -L -S "" -b "$BASEDN" -H $URI1 \
+ '(|(objectclass=person)(cn=root))' >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed!"
+ test "$KILLSERVERS" != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo " ------------ " >> $TESTOUT
+
+echo "Testing AND searching..."
+$LDAPSEARCH -L -S "" -b "$BASEDN" -H $URI1 \
+ '(&(objectclass=person)(cn=root))' >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed!"
+ test "$KILLSERVERS" != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test "$KILLSERVERS" != no && kill -HUP $KILLPIDS
+
+echo "Assuming everything is fine."
+#echo "Comparing results"
+#$CMP $TESTOUT $SEARCHOUTPROVIDER
+#if test $? != 0 ; then
+# echo "Comparison failed"
+# exit 1
+#fi
+
+echo ">>>>> Test succeeded"
+
+exit 0
diff --git a/tests/scripts/relay b/tests/scripts/relay
new file mode 100755
index 0000000..c0e6181
--- /dev/null
+++ b/tests/scripts/relay
@@ -0,0 +1,395 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "Using $RELAY backend..."
+echo ""
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+echo "======== Starting slapd with $RELAY backend ========" >> $LOG1
+. $CONFFILTER $BACKEND < $RELAYCONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL >> $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDERED > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+BASEDN="dc=example,dc=com"
+echo "Searching base=\"$BASEDN\"..."
+echo "# searching base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S '' -H $URI1 -b "$BASEDN" >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BASEDN="o=Example,c=US"
+echo "Searching base=\"$BASEDN\"..."
+echo "# searching base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S '' -H $URI1 -b "$BASEDN" >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BASEDN="o=Esempio,c=IT"
+echo "Searching base=\"$BASEDN\"..."
+echo "# searching base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S '' -H $URI1 -b "$BASEDN" >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BASEDN="o=Beispiel,c=DE"
+echo "Searching base=\"$BASEDN\"..."
+echo "# searching base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S '' -H $URI1 -b "$BASEDN" >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#
+# Do some modifications
+#
+
+BASEDN="o=Beispiel,c=DE"
+echo "Modifying database \"$BASEDN\"..."
+$LDAPMODIFY -v -D "cn=Manager,$BASEDN" -H $URI1 -w $PASSWD \
+ -M >> $TESTOUT 2>&1 << EOMODS
+dn: cn=Added User,ou=Alumni Association,ou=People,$BASEDN
+changetype: add
+objectClass: OpenLDAPperson
+cn: Added User
+sn: User
+uid: auser
+seealso: cn=All Staff,ou=Groups,$BASEDN
+homephone: +49 1234567890
+drink: Beer
+mail: auser@mail.alumni.example.com
+telephonenumber: +49 1234-567-890
+description: Just added in o=Beispiel,c=DE naming context
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,$BASEDN
+changetype: modify
+add: seeAlso
+seeAlso: cn=Ursula Hampster,ou=Alumni Association,ou=People,$BASEDN
+-
+add: description
+description: Just added self to seeAlso in $BASEDN virtual naming context
+-
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,$BASEDN
+changetype: delete
+
+dn: cn=John Doe,ou=Information Technology Division,ou=People,$BASEDN
+changetype: modrdn
+newrdn: cn=John P. Doe
+deleteoldrdn: 1
+
+dn: cn=Jane Doe,ou=Alumni Association,ou=People,$BASEDN
+changetype: modrdn
+newrdn: cn=Jane Q. Doe
+deleteoldrdn: 1
+newsuperior: ou=Information Technology Division,ou=People,$BASEDN
+
+dn: cn=Jane Q. Doe,ou=Information Technology Division,ou=People,$BASEDN
+changetype: modify
+add: cn
+cn: Jane Qissapaolo Doe
+-
+# This operation (delete of DN-valued attribute) triggered ITS#3498
+delete: seeAlso
+-
+
+dn: cn=Jane Q. Doe,ou=Information Technology Division,ou=People,$BASEDN
+changetype: modify
+add: seeAlso
+seeAlso: cn=All Staff,ou=Groups,$BASEDN
+-
+
+dn: ou=Referrals,$BASEDN
+changetype: add
+objectclass: referral
+objectclass: extensibleObject
+ou: Referrals
+ref: ldap://localhost.localdomain/ou=Referrals,$BASEDN
+description: Just added as ldap://localhost.localdomain:389/ou=Referrals,$BASEDN
+
+dn: ou=Referrals,$BASEDN
+changetype: modify
+replace: ref
+ref: ldap://localhost:9012/ou=Referrals,$BASEDN
+-
+add: description
+description: ...and modified as ldap://localhost:9012/ou=Referrals,$BASEDN
+-
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "Modify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BASEDN="o=Example,c=US"
+echo "Modifying database \"$BASEDN\"..."
+$LDAPMODIFY -v -D "cn=Manager,$BASEDN" -H $URI1 -w $PASSWD \
+ -M >> $TESTOUT 2>&1 << EOMODS
+# These operations (updates with objectClass mapping) triggered ITS#3499
+dn: cn=Added Group,ou=Groups,$BASEDN
+changetype: add
+objectClass: groupOfNames
+objectClass: uidObject
+cn: Added Group
+member: cn=Added Group,ou=Groups,$BASEDN
+uid: added
+
+dn: cn=Another Added Group,ou=Groups,$BASEDN
+changetype: add
+objectClass: groupOfNames
+cn: Another Added Group
+member: cn=Added Group,ou=Groups,$BASEDN
+member: cn=Another Added Group,ou=Groups,$BASEDN
+
+dn: cn=Another Added Group,ou=Groups,$BASEDN
+changetype: modify
+add: objectClass
+objectClass: uidObject
+-
+add: uid
+uid: added
+-
+
+dn: cn=Added Group,ou=Groups,$BASEDN
+changetype: modify
+delete: objectClass
+objectClass: uidObject
+-
+delete: uid
+-
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "Modify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Searching base=\"$BASEDN\"..."
+echo "# searching base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S '' -H $URI1 -b "$BASEDN" >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BASEDN="o=Esempio,c=IT"
+echo "Searching base=\"$BASEDN\"..."
+echo "# searching base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S '' -H $URI1 -b "$BASEDN" >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+FILTER="(objectClass=referral)"
+echo "Searching filter=\"$FILTER\""
+echo " attrs=\"'*' ref\""
+echo "# searching filter=\"$FILTER\"" >> $SEARCHOUT
+echo "# attrs=\"'*' ref\"" >> $SEARCHOUT
+
+BASEDN="dc=example,dc=com"
+echo " base=\"$BASEDN\"..."
+echo "# base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S '' -H $URI1 -b "$BASEDN" -M "$FILTER" '*' ref \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BASEDN="o=Example,c=US"
+echo " base=\"$BASEDN\"..."
+echo "# base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S '' -H $URI1 -b "$BASEDN" -M "$FILTER" '*' ref \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BASEDN="o=Esempio,c=IT"
+echo " base=\"$BASEDN\"..."
+echo "# base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S '' -H $URI1 -b "$BASEDN" -M "$FILTER" '*' ref \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BASEDN="o=Example,c=US"
+FILTER="(seeAlso=cn=all staff,ou=Groups,$BASEDN)"
+echo "Searching filter=\"$FILTER\""
+echo " attrs=\"seeAlso\""
+echo " base=\"$BASEDN\"..."
+echo "# searching filter=\"$FILTER\"" >> $SEARCHOUT
+echo "# attrs=\"seeAlso\"" >> $SEARCHOUT
+echo "# base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S '' -H $URI1 -b "$BASEDN" "$FILTER" seeAlso \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+FILTER="(uid=example)"
+echo "Searching filter=\"$FILTER\""
+echo " attrs=\"uid\""
+echo " base=\"$BASEDN\"..."
+echo "# searching filter=\"$FILTER\"" >> $SEARCHOUT
+echo "# attrs=\"uid\"" >> $SEARCHOUT
+echo "# base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S '' -H $URI1 -b "$BASEDN" "$FILTER" uid \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+FILTER="(member=cn=Another Added Group,ou=Groups,$BASEDN)"
+echo "Searching filter=\"$FILTER\""
+echo " attrs=\"member\""
+echo " base=\"$BASEDN\"..."
+echo "# searching filter=\"$FILTER\"" >> $SEARCHOUT
+echo "# attrs=\"member\"" >> $SEARCHOUT
+echo "# base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S '' -H $URI1 -b "$BASEDN" "$FILTER" member \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $RELAYOUT > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - relay search/modification didn't succeed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+BASEDN="o=Example,c=US"
+echo "Changing password to database \"$BASEDN\"..."
+$LDAPPASSWD -H $URI1 -D "cn=Manager,$BASEDN" -w $PASSWD \
+ -s $PASSWD "cn=Added User,ou=Alumni Association,ou=People,$BASEDN" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Passwd ExOp failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BASEDN="o=Beispiel,c=DE"
+echo "Binding with newly changed password to database \"$BASEDN\"..."
+$LDAPWHOAMI -H $URI1 \
+ -D "cn=Added User,ou=Alumni Association,ou=People,$BASEDN" \
+ -w $PASSWD >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "WhoAmI failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BASEDN="o=Esempio,c=IT"
+echo "Comparing to database \"$BASEDN\"..."
+$LDAPCOMPARE -H $URI1 \
+ "cn=Added User,ou=Alumni Association,ou=People,$BASEDN" \
+ "seeAlso:cn=All Staff,ou=Groups,$BASEDN" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 6 && test $RC,$BACKEND != 5,null ; then
+ echo "Compare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
diff --git a/tests/scripts/setup_kdc.sh b/tests/scripts/setup_kdc.sh
new file mode 100755
index 0000000..3402f45
--- /dev/null
+++ b/tests/scripts/setup_kdc.sh
@@ -0,0 +1,158 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+KRB5_TRACE=$TESTDIR/k5_trace
+KRB5_CONFIG=$TESTDIR/krb5.conf
+KRB5_KDC_PROFILE=$KRB5_CONFIG
+KRB5_KTNAME=$TESTDIR/server.kt
+KRB5_CLIENT_KTNAME=$TESTDIR/client.kt
+KRB5CCNAME=$TESTDIR/client.ccache
+
+export KRB5_TRACE KRB5_CONFIG KRB5_KDC_PROFILE KRB5_KTNAME KRB5_CLIENT_KTNAME KRB5CCNAME
+
+KDCLOG=$TESTDIR/setup_kdc.log
+KSERVICE=ldap/$LOCALHOST
+KUSER=kuser
+
+. $CONFFILTER < $DATADIR/krb5.conf > $KRB5_CONFIG
+
+PATH=${PATH}:/usr/lib/heimdal-servers:/usr/sbin:/usr/local/sbin
+
+echo "Trying Heimdal KDC..."
+
+command -v kdc >/dev/null 2>&1
+if test $? = 0 ; then
+ kstash --random-key > $KDCLOG 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "Heimdal: kstash failed, skipping GSSAPI tests"
+ exit 0
+ fi
+
+ flags="--realm-max-ticket-life=1h --realm-max-renewable-life=1h"
+ kadmin -l init $flags $KRB5REALM > $KDCLOG 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "Heimdal: kadmin init failed, skipping GSSAPI tests"
+ exit 0
+ fi
+
+ kadmin -l add --random-key --use-defaults $KSERVICE > $KDCLOG 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "Heimdal: kadmin add failed, skipping GSSAPI tests"
+ exit 0
+ fi
+
+ kadmin -l ext -k $KRB5_KTNAME $KSERVICE > $KDCLOG 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "Heimdal: kadmin ext failed, skipping GSSAPI tests"
+ exit 0
+ fi
+
+ kadmin -l add --random-key --use-defaults $KUSER > $KDCLOG 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "Heimdal: kadmin add failed, skipping GSSAPI tests"
+ exit 0
+ fi
+
+ kadmin -l ext -k $KRB5_CLIENT_KTNAME $KUSER > $KDCLOG 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "Heimdal: kadmin ext failed, skipping GSSAPI tests"
+ exit 0
+ fi
+
+ kdc --addresses=$LOCALIP --ports="$KDCPORT/udp" > $KDCLOG 2>&1 &
+else
+ echo "Trying MIT KDC..."
+
+ command -v krb5kdc >/dev/null 2>&1
+ if test $? != 0; then
+ echo "No KDC available, skipping GSSAPI tests"
+ exit 0
+ fi
+
+ kdb5_util create -r $KRB5REALM -s -P password > $KDCLOG 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "MIT: kdb5_util create failed, skipping GSSAPI tests"
+ exit 0
+ fi
+
+ kadmin.local -q "addprinc -randkey $KSERVICE" > $KDCLOG 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "MIT: admin addprinc failed, skipping GSSAPI tests"
+ exit 0
+ fi
+
+ kadmin.local -q "ktadd -k $KRB5_KTNAME $KSERVICE" > $KDCLOG 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "MIT: kadmin ktadd failed, skipping GSSAPI tests"
+ exit 0
+ fi
+
+ kadmin.local -q "addprinc -randkey $KUSER" > $KDCLOG 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "MIT: kadmin addprinc failed, skipping GSSAPI tests"
+ exit 0
+ fi
+
+ kadmin.local -q "ktadd -k $KRB5_CLIENT_KTNAME $KUSER" > $KDCLOG 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "MIT: kadmin ktadd failed, skipping GSSAPI tests"
+ exit 0
+ fi
+
+ krb5kdc -n > $KDCLOG 2>&1 &
+fi
+
+KDCPROC=$!
+sleep 1
+
+kinit -kt $KRB5_CLIENT_KTNAME $KUSER > $KDCLOG 2>&1
+RC=$?
+if test $RC != 0 ; then
+ kill $KDCPROC
+ echo "SASL/GSSAPI: kinit failed, skipping GSSAPI tests"
+ exit 0
+fi
+
+pluginviewer -m GSSAPI > $TESTDIR/plugin_out 2>/dev/null
+RC=$?
+if test $RC != 0 ; then
+
+ saslpluginviewer -m GSSAPI > $TESTDIR/plugin_out 2>/dev/null
+ RC=$?
+ if test $RC != 0 ; then
+ kill $KDCPROC
+ echo "cyrus-sasl has no GSSAPI support, test skipped"
+ exit 0
+ fi
+fi
+
+HAVE_SASL_GSS_CBIND=no
+
+grep CHANNEL_BINDING $TESTDIR/plugin_out > /dev/null 2>&1
+RC=$?
+if test $RC = 0 ; then
+ HAVE_SASL_GSS_CBIND=yes
+fi
diff --git a/tests/scripts/sql-all b/tests/scripts/sql-all
new file mode 100755
index 0000000..c462c3c
--- /dev/null
+++ b/tests/scripts/sql-all
@@ -0,0 +1,70 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+SHTOOL="$SRCDIR/../build/shtool"
+
+TB="" TN=""
+if test -t 1 ; then
+ TB=`$SHTOOL echo -e "%B" 2>/dev/null`
+ TN=`$SHTOOL echo -e "%b" 2>/dev/null`
+fi
+
+SLEEPTIME=10
+
+echo "#######################################################################"
+echo "### ###"
+echo "### SQL tests ###"
+echo "### ###"
+echo "#######################################################################"
+echo "###"
+echo "### SQL tests require the sql backend, a properly configured"
+echo "### ODBC and a database populated with data from the applicable"
+echo "### servers/slapd/back-sql/rdbms_depend/* files."
+echo "###"
+echo "### Set SLAPD_USE_SQL to the desired RDBMS to enable this test;"
+echo "###"
+echo "### Currently supported RDBMSes are:"
+echo "### ibmdb2, mysql, pgsql"
+echo "###"
+echo "### Set SLAPD_USE_SQLWRITE=yes to enable the write tests"
+echo "###"
+echo "### See servers/slapd/back-sql/rdbms_depend/README for more "
+echo "### details on how to set up the RDBMS and the ODBC"
+echo "###"
+
+echo ">>>>> Executing all LDAP tests for $BACKEND"
+
+for CMD in $SRCDIR/scripts/sql-test*; do
+ # remove cruft from prior test
+ if test $PRESERVE = yes ; then
+ /bin/rm -rf testrun/db.*
+ else
+ /bin/rm -rf testrun
+ fi
+
+ echo ">>>>> Starting ${TB}`basename $CMD`${TN} ..."
+ $CMD
+ RC=$?
+ if test $RC -eq 0 ; then
+ echo ">>>>> $CMD completed ${TB}OK${TN}."
+ else
+ echo ">>>>> $CMD ${TB}failed${TN} (exit $RC)"
+ exit $RC
+ fi
+
+ echo ">>>>> waiting $SLEEPTIME seconds for things to exit"
+ sleep $SLEEPTIME
+ echo ""
+done
diff --git a/tests/scripts/sql-test000-read b/tests/scripts/sql-test000-read
new file mode 100755
index 0000000..6d1d766
--- /dev/null
+++ b/tests/scripts/sql-test000-read
@@ -0,0 +1,568 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $BACKSQL = "sqlno" ; then
+ echo "SQL backend not available, test skipped"
+ exit 0
+fi
+
+if test $RDBMS = "rdbmsno" ; then
+ echo "SQL test not requested, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $SQLCONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+echo "Testing SQL backend read operations..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BASEDN="dc=example,dc=com"
+BINDDN="cn=Mitya Kovalev,${BASEDN}"
+BINDPW="mit"
+echo -n "Testing correct bind... "
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo -n "Testing incorrect bind (should fail)... "
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w "XXX"
+RC=$?
+if test $RC = 0 ; then
+ echo "ldapwhoami should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing baseobject search..."
+echo "# Testing baseobject search..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -s base -S "" \
+ >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing onelevel search..."
+echo "# Testing onelevel search..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -s one -S "" \
+ >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing subtree search..."
+echo "# Testing subtree search..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" \
+ >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing subtree search with manageDSAit..."
+echo "# Testing subtree search with manageDSAit..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -M -S "" '*' ref \
+ >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing invalid filter..."
+echo "# Testing invalid filter..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" "(foo=)" \
+ >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing exact search..."
+echo "# Testing exact search..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" "(sn=Kovalev)" \
+ >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing substrings initial search..."
+echo "# Testing substrings initial search..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" "(cn=m*)" \
+ >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing substrings any search..."
+echo "# Testing substrings any search..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" "(cn=*m*)" \
+ >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing substrings final search..."
+echo "# Testing substrings final search..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" "(cn=*v)" \
+ >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing approx search..."
+echo "# Testing approx search..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" "(sn~=kovalev)" \
+ >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing extensible filter search..."
+echo "# Testing extensible filter search..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" \
+ "(sn:caseExactMatch:=Kovalev)" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing search for telephoneNumber..."
+echo "# Testing search for telephoneNumber..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" \
+ "(telephoneNumber=3322334)" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing AND search..."
+echo "# Testing AND search..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" \
+ "(&(sn=kovalev)(givenName=mitya))" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing AND search on objectClass..."
+echo "# Testing AND search on objectClass..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" \
+ "(&(objectClass=organization)(objectClass=dcObject))" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing OR search..."
+echo "# Testing OR search..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" \
+ "(|(sn=kovalev)(givenName=mitya))" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing OR search on objectClass..."
+echo "# Testing OR search on objectClass..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" \
+ "(|(objectClass=document)(objectClass=organization))" \
+ >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing NOT search..."
+echo "# Testing NOT search..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" \
+ '(!(sn=kovalev))' >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing NOT search on objectClass..."
+echo "# Testing NOT search on objectClass..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" \
+ '(!(objectClass=inetOrgPerson))' >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing NOT search on \"auxiliary\" objectClass..."
+echo "# Testing NOT search on \"auxiliary\" objectClass..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" \
+ '(!(objectClass=dcObject))' >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#### Needs work...
+echo "Testing NOT presence search... (disabled)"
+###echo "# Testing NOT presence search..." >> $SEARCHOUT
+###$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" \
+### '(!(sn=*))' >> $SEARCHOUT 2>&1
+###
+###RC=$?
+###if test $RC != 0 ; then
+### echo "ldapsearch failed ($RC)!"
+### test $KILLSERVERS != no && kill -HUP $KILLPIDS
+### exit $RC
+###fi
+
+echo "Testing attribute inheritance in filter..."
+echo "# Testing attribute inheritance in filter..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" \
+ "(name=example)" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# ITS#4604
+echo "Testing undefined attribute in filter..."
+echo "# Testing undefined attribute in filter..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" \
+ "(|(o=example)(foobar=x))" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing objectClass inheritance in filter..."
+echo "# Testing objectClass inheritance in filter..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" \
+ "(objectClass=person)" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing \"auxiliary\" objectClass in filter..."
+echo "# Testing \"auxiliary\" objectClass in filter..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" \
+ "(objectClass=dcObject)" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing hasSubordinates in filter..."
+echo "# Testing hasSubordinates in filter..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" \
+ "(hasSubordinates=TRUE)" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing entryUUID in filter..."
+echo "# Testing entryUUID in filter..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" \
+ "(entryUUID=00000001-0000-0001-0000-000000000000)" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing attribute inheritance in requested attributes..."
+echo "# Testing attribute inheritance in requested attributes..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" \
+ "(sn=kovalev)" name >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing objectClass in requested attributes..."
+echo "# Testing objectClass in requested attributes..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" \
+ objectClass >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing operational attributes in request..."
+echo "# Testing operational attributes in request..." >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" \
+ '+' 2>&1 > $SEARCHFLT
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+grep -v '^entryCSN:' $SEARCHFLT >> $SEARCHOUT
+
+SIZELIMIT=4
+echo "Testing size limit..."
+$LDAPRSEARCH -H $URI1 -b "$BASEDN" \
+ -z $SIZELIMIT -S "" '(objectClass=*)' >$SEARCHFLT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHFLT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ if test "$COUNT" -gt "$SIZELIMIT" ; then
+ echo "...error: got $COUNT entries instead of the requested $SIZELIMIT"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ echo "...didn't bump into the requested size limit ($SIZELIMIT; got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test x"$COUNT" != x ; then
+ if test "$COUNT" = "$SIZELIMIT" ; then
+ echo "...bumped into requested size limit ($SIZELIMIT)"
+ else
+ echo "...error: got $COUNT entries with a requested sizelimit of $SIZELIMIT"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ else
+ echo "...error: bumped into server-side size limit, but got no entries!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo -n "Testing compare (should be TRUE)... "
+$LDAPCOMPARE -H $URI1 "$BINDDN" \
+ "sn:kovalev" >> $TESTOUT 2>&1
+
+RC=$?
+case $RC in
+6)
+ echo "TRUE"
+ ;;
+5) echo "FALSE!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+*) echo "failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+esac
+
+echo -n "Testing compare (should be FALSE)... "
+$LDAPCOMPARE -H $URI1 "$BINDDN" \
+ "cn:foobar" >> $TESTOUT 2>&1
+
+RC=$?
+case $RC in
+6)
+ echo "TRUE!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+5) echo "FALSE"
+ ;;
+*) echo "failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+esac
+
+echo -n "Testing compare (should be UNDEFINED)... "
+$LDAPCOMPARE -H $URI1 "$BINDDN" \
+ "o:example" >> $TESTOUT 2>&1
+
+RC=$?
+case $RC in
+6)
+ echo "TRUE!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+5) echo "FALSE!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+*) echo "failed ($RC)"
+ ;;
+esac
+
+echo -n "Testing compare on hasSubordinates (should be TRUE)... "
+$LDAPCOMPARE -H $URI1 "$BASEDN" \
+ "hasSubordinates:TRUE" >> $TESTOUT 2>&1
+
+RC=$?
+case $RC in
+6)
+ echo "TRUE"
+ ;;
+5) echo "FALSE!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+*) echo "failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+esac
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif..."
+$LDIFFILTER < $SQLREAD > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - SQL search didn't succeed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+exit 0
diff --git a/tests/scripts/sql-test001-concurrency b/tests/scripts/sql-test001-concurrency
new file mode 100755
index 0000000..d474070
--- /dev/null
+++ b/tests/scripts/sql-test001-concurrency
@@ -0,0 +1,138 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $BACKSQL = "sqlno" ; then
+ echo "SQL backend not available, test skipped"
+ exit 0
+fi
+
+if test $RDBMS = "rdbmsno" ; then
+ echo "SQL test not requested, test skipped"
+ exit 0
+fi
+
+if test "x$TESTLOOPS" = "x" ; then
+ TESTLOOPS=5
+fi
+
+if test "x$CHILDREN" = "x" ; then
+ CHILDREN="-j 4"
+else
+ CHILDREN="-j $CHILDREN"
+fi
+
+SQLDATADIR=$TESTDIR/sql-concurrency
+mkdir -p $TESTDIR $SQLDATADIR
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $SQLCONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+echo "Testing SQL backend concurrency..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to retrieve all the entries..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $SEARCHOUT > $LDIFFLT
+
+if test "${RDBMSWRITE}" != "yes"; then
+ echo "write test disabled for ${RDBMS}; set SLAPD_USE_SQLWRITE=yes to enable"
+ cp $SQLCONCURRENCYDIR/do_read* $SQLCONCURRENCYDIR/do_search* \
+ $SQLCONCURRENCYDIR/do_bind* $SQLDATADIR
+else
+ case ${RDBMS} in
+ # list here the RDBMSes whose mapping allows writes
+ pgsql|ibmdb2)
+ cp $SQLCONCURRENCYDIR/do_* $SQLDATADIR
+ ;;
+ *)
+ echo "write is not supported for ${RDBMS}; performing read-only concurrency test"
+ cp $SQLCONCURRENCYDIR/do_read* $SQLCONCURRENCYDIR/do_search* \
+ $SQLCONCURRENCYDIR/do_bind* $SQLDATADIR
+ ;;
+ esac
+fi
+
+echo "Using tester for concurrent server access..."
+$SLAPDTESTER -P "$PROGDIR" -d "$SQLDATADIR" \
+ -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -l $TESTLOOPS $CHILDREN -FF
+RC=$?
+
+if test $RC != 0 ; then
+ echo "slapd-tester failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to retrieve all the entries..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - database was not created correctly"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+exit 0
+
diff --git a/tests/scripts/sql-test900-write b/tests/scripts/sql-test900-write
new file mode 100755
index 0000000..983d350
--- /dev/null
+++ b/tests/scripts/sql-test900-write
@@ -0,0 +1,573 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $BACKSQL = "sqlno" ; then
+ echo "SQL backend not available, test skipped"
+ exit 0
+fi
+
+if test $RDBMS = "rdbmsno" ; then
+ echo "SQL test not requested, test skipped"
+ exit 0
+fi
+
+if test "${RDBMSWRITE}" != "yes"; then
+ echo "write test disabled for ${RDBMS}; set SLAPD_USE_SQLWRITE=yes to enable"
+ exit 0
+fi
+
+mkdir -p $TESTDIR
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $SQLCONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+echo "Testing SQL backend write operations..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+BASEDN="dc=example,dc=com"
+
+echo "Using ldapsearch to retrieve all the entries..."
+echo "# Using ldapsearch to retrieve all the entries..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI1 -b "$BASEDN" \
+ "objectClass=*" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+case ${RDBMS} in
+ # list here the RDBMSes whose mapping allows writes
+pgsql|ibmdb2)
+ MANAGERDN="cn=Manager,${BASEDN}"
+ echo "Testing add..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+# Adding an organization...
+dn: o=An Org,${BASEDN}
+changetype: add
+objectClass: organization
+o: An Org
+
+# Adding an organization with an "auxiliary" objectClass..
+dn: dc=subnet,${BASEDN}
+changetype: add
+objectClass: organization
+objectClass: dcObject
+o: SubNet
+dc: subnet
+
+# Adding another organization with an "auxiliary" objectClass..
+dn: dc=subnet2,${BASEDN}
+changetype: add
+objectClass: organization
+objectClass: dcObject
+o: SubNet 2
+dc: subnet2
+
+# Adding a person...
+dn: cn=Lev Tolstoij,${BASEDN}
+changetype: add
+objectClass: inetOrgPerson
+cn: Lev Tolstoij
+sn: Tolstoij
+givenName: Lev
+telephoneNumber: +39 02 XXXX YYYY
+telephoneNumber: +39 02 XXXX ZZZZ
+userPassword: tanja
+
+# Adding a person with an "auxiliary" objectClass...
+dn: cn=Some One,${BASEDN}
+changetype: add
+objectClass: inetOrgPerson
+objectClass: simpleSecurityObject
+cn: Some One
+sn: One
+givenName: Some
+telephoneNumber: +1 800 900 1234
+telephoneNumber: +1 800 900 1235
+userPassword: someone
+
+# Adding a person in another subtree...
+dn: cn=SubNet User,dc=subnet,${BASEDN}
+changetype: add
+objectClass: inetOrgPerson
+cn: SubNet User
+sn: User
+givenName: SubNet
+
+# Adding a document...
+dn: documentTitle=War and Peace,${BASEDN}
+changetype: add
+objectClass: document
+description: Historical novel
+documentTitle: War and Peace
+documentAuthor: cn=Lev Tolstoij,dc=example,dc=com
+documentIdentifier: document 3
+EOMODS
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Using ldapsearch to retrieve all the entries..."
+ echo "# Using ldapsearch to retrieve all the entries..." >> $SEARCHOUT
+ $LDAPSEARCH -S "" -H $URI1 -b "$BASEDN" \
+ "objectClass=*" >> $SEARCHOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Testing modify..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+# Deleting all telephone numbers...
+dn: cn=Some One,${BASEDN}
+changetype: modify
+delete: telephoneNumber
+-
+
+# Adding a telephone number...
+dn: cn=Mitya Kovalev,${BASEDN}
+changetype: modify
+add: telephoneNumber
+telephoneNumber: +1 800 123 4567
+-
+
+# Deleting a specific telephone number and adding a new one...
+dn: cn=Lev Tolstoij,${BASEDN}
+changetype: modify
+delete: telephoneNumber
+telephoneNumber: +39 02 XXXX YYYY
+-
+add: telephoneNumber
+telephoneNumber: +39 333 ZZZ 1234
+-
+
+# Adding an author to a document...
+dn: documentTitle=book1,${BASEDN}
+changetype: modify
+add: documentAuthor
+documentAuthor: cn=Lev Tolstoij,${BASEDN}
+-
+
+# Adding an author to another document...
+dn: documentTitle=book2,${BASEDN}
+changetype: modify
+add: documentAuthor
+documentAuthor: cn=Lev Tolstoij,${BASEDN}
+-
+
+# Adding an "auxiliary" objectClass...
+dn: cn=Mitya Kovalev,${BASEDN}
+changetype: modify
+add: objectClass
+objectClass: simpleSecurityObject
+-
+
+# Deleting an "auxiliary" objectClass...
+dn: cn=Some One,${BASEDN}
+changetype: modify
+delete: objectClass
+objectClass: simpleSecurityObject
+-
+
+# Deleting userPasswords
+dn: cn=Lev Tolstoij,${BASEDN}
+changetype: modify
+delete: userPassword
+-
+EOMODS
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Using ldapsearch to retrieve all the entries..."
+ echo "# Using ldapsearch to retrieve all the entries..." >> $SEARCHOUT
+ $LDAPSEARCH -S "" -H $URI1 -b "$BASEDN" \
+ "objectClass=*" >> $SEARCHOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Testing delete..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+# Deleting a person...
+dn: cn=Torvlobnor Puzdoy,${BASEDN}
+changetype: delete
+
+# Deleting a document...
+dn: documentTitle=book1,${BASEDN}
+changetype: delete
+
+# Deleting an organization with an "auxiliary" objectClass...
+dn: dc=subnet2,${BASEDN}
+changetype: delete
+EOMODS
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Using ldapsearch to retrieve all the entries..."
+ echo "# Using ldapsearch to retrieve all the entries..." >> $SEARCHOUT
+ $LDAPSEARCH -S "" -H $URI1 -b "$BASEDN" \
+ "objectClass=*" >> $SEARCHOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Testing rename..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+# Renaming an organization...
+dn: o=An Org,${BASEDN}
+changetype: modrdn
+newrdn: o=Renamed Org
+deleteoldrdn: 1
+
+# Moving a person to another subtree...
+dn: cn=Lev Tolstoij,${BASEDN}
+changetype: modrdn
+newrdn: cn=Lev Tolstoij
+deleteoldrdn: 0
+newsuperior: dc=subnet,${BASEDN}
+
+# Renaming a book...
+dn: documentTitle=book2,${BASEDN}
+changetype: modrdn
+newrdn: documentTitle=Renamed Book
+deleteoldrdn: 1
+EOMODS
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Using ldapsearch to retrieve all the entries..."
+ echo "# Using ldapsearch to retrieve all the entries..." >> $SEARCHOUT
+ $LDAPSEARCH -S "" -H $URI1 -b "$BASEDN" \
+ "objectClass=*" >> $SEARCHOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Adding a child to a referral (should fail)..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+dn: cn=Should Fail,ou=Referral,${BASEDN}
+changetype: add
+objectClass: inetOrgPerson
+cn: Should Fail
+sn: Fail
+telephoneNumber: +39 02 23456789
+EOMODS
+
+ RC=$?
+ if test $RC = 0 ; then
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+
+ echo "Modifying a referral (should fail)..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+dn: ou=Referral,${BASEDN}
+changetype: modify
+replace: ref
+ref: ldap://localhost:9009/
+-
+EOMODS
+
+ RC=$?
+ if test $RC = 0 ; then
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+
+ echo "Renaming a referral (should fail)..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+dn: ou=Referral,${BASEDN}
+changetype: modrdn
+newrdn: ou=Renamed Referral
+deleteoldrdn: 1
+EOMODS
+
+ RC=$?
+ if test $RC = 0 ; then
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+
+ echo "Deleting a referral (should fail)..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+dn: ou=Referral,${BASEDN}
+changetype: delete
+EOMODS
+
+ RC=$?
+ if test $RC = 0 ; then
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+
+ echo "Adding a referral..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 -M >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+dn: ou=Another Referral,${BASEDN}
+changetype: add
+objectClass: referral
+objectClass: extensibleObject
+ou: Another Referral
+ref: ldap://localhost:9009/
+EOMODS
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Modifying a referral with manageDSAit..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 -M >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+dn: ou=Referral,${BASEDN}
+changetype: modify
+replace: ref
+ref: ldap://localhost:9009/
+-
+EOMODS
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Using ldapsearch to retrieve the modified entry..."
+ echo "# Using ldapsearch to retrieve the modified entry..." >> $SEARCHOUT
+ $LDAPSEARCH -S "" -H $URI1 -b "ou=Referral,$BASEDN" -M \
+ "objectClass=*" '*' ref >> $SEARCHOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Renaming a referral with manageDSAit..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 -M >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+dn: ou=Referral,${BASEDN}
+changetype: modrdn
+newrdn: ou=Renamed Referral
+deleteoldrdn: 1
+EOMODS
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Using ldapsearch to retrieve the renamed entry..."
+ echo "# Using ldapsearch to retrieve the renamed entry..." >> $SEARCHOUT
+ $LDAPSEARCH -S "" -H $URI1 -b "ou=Renamed Referral,$BASEDN" -M \
+ "objectClass=*" '*' ref >> $SEARCHOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Deleting a referral with manageDSAit..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 -M >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+dn: ou=Renamed Referral,${BASEDN}
+changetype: delete
+EOMODS
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ BINDDN="cn=Mitya Kovalev,${BASEDN}"
+ BINDPW="mit"
+ NEWPW="newsecret"
+ echo "Testing passwd change..."
+ $LDAPPASSWD -H $URI1 \
+ -D "${BINDDN}" -w ${BINDPW} -s ${NEWPW} \
+ "$BINDDN" >> $TESTOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldappasswd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo -n "Testing bind with new secret... "
+ $LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $NEWPW
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ BINDDN="cn=Some One,${BASEDN}"
+ BINDPW="someone"
+ echo -n "Testing bind with newly added user... "
+ $LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Using ldapsearch to retrieve all the entries..."
+ echo "# Using ldapsearch to retrieve all the entries..." >> $SEARCHOUT
+ $LDAPSEARCH -S "" -H $URI1 -b "$BASEDN" \
+ "objectClass=*" >> $SEARCHOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Filtering ldapsearch results..."
+ $LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+ echo "Filtering modified ldif..."
+ $LDIFFILTER < $SQLWRITE > $LDIFFLT
+ echo "Comparing filter output..."
+ $CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+ if test $? != 0 ; then
+ echo "comparison failed - SQL mods search didn't succeed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+
+*)
+ echo "apparently ${RDBMS} does not support writes; skipping..."
+ ;;
+esac
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+exit 0
diff --git a/tests/scripts/sql-test901-syncrepl b/tests/scripts/sql-test901-syncrepl
new file mode 100755
index 0000000..db94271
--- /dev/null
+++ b/tests/scripts/sql-test901-syncrepl
@@ -0,0 +1,692 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $BACKSQL = "sqlno" ; then
+ echo "SQL backend not available, test skipped"
+ exit 0
+fi
+
+if test $RDBMS = "rdbmsno" ; then
+ echo "SQL test not requested, test skipped"
+ exit 0
+fi
+
+if test "${RDBMSWRITE}" != "yes"; then
+ echo "write test disabled for ${RDBMS}; set SLAPD_USE_SQLWRITE=yes to enable"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR2A
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $SQLSRPROVIDERCONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+echo "Testing SQL backend write operations..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting consumer slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $R1SRCONSUMERCONF > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+echo "Using ldapsearch to check that consumer slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+echo "Using ldapsearch to retrieve all the entries from the provider..."
+echo "# Using ldapsearch to retrieve all the entries from the provider..." \
+ >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI1 -b "$BASEDN" \
+ -D "$MANAGERDN" -w $PASSWD \
+ "(!(objectClass=referral))" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT2
+
+echo "Using ldapsearch to retrieve all the entries from the consumer..."
+echo "# Using ldapsearch to retrieve all the entries from the consumer..." \
+ >> $SEARCHOUT2
+$LDAPSEARCH -S "" -H $URI2 -b "$BASEDN" \
+ -D "$UPDATEDN" -w $PASSWD \
+ "(objectClass=*)" >> $SEARCHOUT2 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results from provider..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering ldapsearch results from consumer..."
+$LDIFFILTER < $SEARCHOUT2 > $SEARCHFLT2
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $SEARCHFLT2 > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+cat /dev/null > $SEARCHOUT
+
+echo "Using ldapsearch to retrieve all the entries..."
+echo "# Using ldapsearch to retrieve all the entries..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI1 -b "$BASEDN" \
+ "objectClass=*" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+case ${RDBMS} in
+ # list here the RDBMSes whose mapping allows writes
+pgsql|ibmdb2)
+ MANAGERDN="cn=Manager,${BASEDN}"
+ echo "Testing add..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+# Adding an organization...
+dn: o=An Org,${BASEDN}
+changetype: add
+objectClass: organization
+o: An Org
+
+# Adding an organization with an "auxiliary" objectClass..
+dn: dc=subnet,${BASEDN}
+changetype: add
+objectClass: organization
+objectClass: dcObject
+o: SubNet
+dc: subnet
+
+# Adding another organization with an "auxiliary" objectClass..
+dn: dc=subnet2,${BASEDN}
+changetype: add
+objectClass: organization
+objectClass: dcObject
+o: SubNet 2
+dc: subnet2
+
+# Adding a person...
+dn: cn=Lev Tolstoij,${BASEDN}
+changetype: add
+objectClass: inetOrgPerson
+cn: Lev Tolstoij
+sn: Tolstoij
+givenName: Lev
+telephoneNumber: +39 02 XXXX YYYY
+telephoneNumber: +39 02 XXXX ZZZZ
+userPassword: tanja
+
+# Adding a person with an "auxiliary" objectClass...
+dn: cn=Some One,${BASEDN}
+changetype: add
+objectClass: inetOrgPerson
+objectClass: simpleSecurityObject
+cn: Some One
+sn: One
+givenName: Some
+telephoneNumber: +1 800 900 1234
+telephoneNumber: +1 800 900 1235
+userPassword: someone
+
+# Adding a person in another subtree...
+dn: cn=SubNet User,dc=subnet,${BASEDN}
+changetype: add
+objectClass: inetOrgPerson
+cn: SubNet User
+sn: User
+givenName: SubNet
+
+# Adding a document...
+dn: documentTitle=War and Peace,${BASEDN}
+changetype: add
+objectClass: document
+description: Historical novel
+documentTitle: War and Peace
+documentAuthor: cn=Lev Tolstoij,dc=example,dc=com
+documentIdentifier: document 3
+EOMODS
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Using ldapsearch to retrieve all the entries..."
+ echo "# Using ldapsearch to retrieve all the entries..." >> $SEARCHOUT
+ $LDAPSEARCH -S "" -H $URI1 -b "$BASEDN" \
+ "objectClass=*" >> $SEARCHOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Testing modify..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+# Deleting all telephone numbers...
+dn: cn=Some One,${BASEDN}
+changetype: modify
+delete: telephoneNumber
+-
+
+# Adding a telephone number...
+dn: cn=Mitya Kovalev,${BASEDN}
+changetype: modify
+add: telephoneNumber
+telephoneNumber: +1 800 123 4567
+-
+
+# Deleting a specific telephone number and adding a new one...
+dn: cn=Lev Tolstoij,${BASEDN}
+changetype: modify
+delete: telephoneNumber
+telephoneNumber: +39 02 XXXX YYYY
+-
+add: telephoneNumber
+telephoneNumber: +39 333 ZZZ 1234
+-
+
+# Adding an author to a document...
+dn: documentTitle=book1,${BASEDN}
+changetype: modify
+add: documentAuthor
+documentAuthor: cn=Lev Tolstoij,${BASEDN}
+-
+
+# Adding an author to another document...
+dn: documentTitle=book2,${BASEDN}
+changetype: modify
+add: documentAuthor
+documentAuthor: cn=Lev Tolstoij,${BASEDN}
+-
+
+# Adding an "auxiliary" objectClass...
+dn: cn=Mitya Kovalev,${BASEDN}
+changetype: modify
+add: objectClass
+objectClass: simpleSecurityObject
+-
+
+# Deleting an "auxiliary" objectClass...
+dn: cn=Some One,${BASEDN}
+changetype: modify
+delete: objectClass
+objectClass: simpleSecurityObject
+-
+
+# Deleting userPasswords
+dn: cn=Lev Tolstoij,${BASEDN}
+changetype: modify
+delete: userPassword
+-
+EOMODS
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Using ldapsearch to retrieve all the entries..."
+ echo "# Using ldapsearch to retrieve all the entries..." >> $SEARCHOUT
+ $LDAPSEARCH -S "" -H $URI1 -b "$BASEDN" \
+ "objectClass=*" >> $SEARCHOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Testing delete..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+# Deleting a person...
+dn: cn=Torvlobnor Puzdoy,${BASEDN}
+changetype: delete
+
+# Deleting a document...
+dn: documentTitle=book1,${BASEDN}
+changetype: delete
+
+# Deleting an organization with an "auxiliary" objectClass...
+dn: dc=subnet2,${BASEDN}
+changetype: delete
+EOMODS
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Using ldapsearch to retrieve all the entries..."
+ echo "# Using ldapsearch to retrieve all the entries..." >> $SEARCHOUT
+ $LDAPSEARCH -S "" -H $URI1 -b "$BASEDN" \
+ "objectClass=*" >> $SEARCHOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Testing rename..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+# Renaming an organization...
+dn: o=An Org,${BASEDN}
+changetype: modrdn
+newrdn: o=Renamed Org
+deleteoldrdn: 1
+
+# Moving a person to another subtree...
+dn: cn=Lev Tolstoij,${BASEDN}
+changetype: modrdn
+newrdn: cn=Lev Tolstoij
+deleteoldrdn: 0
+newsuperior: dc=subnet,${BASEDN}
+
+# Renaming a book...
+dn: documentTitle=book2,${BASEDN}
+changetype: modrdn
+newrdn: documentTitle=Renamed Book
+deleteoldrdn: 1
+EOMODS
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Using ldapsearch to retrieve all the entries..."
+ echo "# Using ldapsearch to retrieve all the entries..." >> $SEARCHOUT
+ $LDAPSEARCH -S "" -H $URI1 -b "$BASEDN" \
+ "objectClass=*" >> $SEARCHOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Adding a child to a referral (should fail)..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+dn: cn=Should Fail,ou=Referral,${BASEDN}
+changetype: add
+objectClass: inetOrgPerson
+cn: Should Fail
+sn: Fail
+telephoneNumber: +39 02 23456789
+EOMODS
+
+ RC=$?
+ if test $RC = 0 ; then
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+
+ echo "Modifying a referral (should fail)..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+dn: ou=Referral,${BASEDN}
+changetype: modify
+replace: ref
+ref: ldap://localhost:9009/
+-
+EOMODS
+
+ RC=$?
+ if test $RC = 0 ; then
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+
+ echo "Renaming a referral (should fail)..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+dn: ou=Referral,${BASEDN}
+changetype: modrdn
+newrdn: ou=Renamed Referral
+deleteoldrdn: 1
+EOMODS
+
+ RC=$?
+ if test $RC = 0 ; then
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+
+ echo "Deleting a referral (should fail)..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+dn: ou=Referral,${BASEDN}
+changetype: delete
+EOMODS
+
+ RC=$?
+ if test $RC = 0 ; then
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+
+ echo "Adding a referral..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 -M >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+dn: ou=Another Referral,${BASEDN}
+changetype: add
+objectClass: referral
+objectClass: extensibleObject
+ou: Another Referral
+ref: ldap://localhost:9009/
+EOMODS
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Modifying a referral with manageDSAit..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 -M >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+dn: ou=Referral,${BASEDN}
+changetype: modify
+replace: ref
+ref: ldap://localhost:9009/
+-
+EOMODS
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Using ldapsearch to retrieve the modified entry..."
+ echo "# Using ldapsearch to retrieve the modified entry..." >> $SEARCHOUT
+ $LDAPSEARCH -S "" -H $URI1 -b "ou=Referral,$BASEDN" -M \
+ "objectClass=*" '*' ref >> $SEARCHOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Renaming a referral with manageDSAit..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 -M >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+dn: ou=Referral,${BASEDN}
+changetype: modrdn
+newrdn: ou=Renamed Referral
+deleteoldrdn: 1
+EOMODS
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Using ldapsearch to retrieve the renamed entry..."
+ echo "# Using ldapsearch to retrieve the renamed entry..." >> $SEARCHOUT
+ $LDAPSEARCH -S "" -H $URI1 -b "ou=Renamed Referral,$BASEDN" -M \
+ "objectClass=*" '*' ref >> $SEARCHOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Deleting a referral with manageDSAit..."
+ $LDAPMODIFY -v -c -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 -M >> $TESTOUT 2>&1 << EOMODS
+version: 1
+
+dn: ou=Renamed Referral,${BASEDN}
+changetype: delete
+EOMODS
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ BINDDN="cn=Mitya Kovalev,${BASEDN}"
+ BINDPW="mit"
+ NEWPW="newsecret"
+ echo "Testing passwd change..."
+ $LDAPPASSWD -H $URI1 \
+ -D "${BINDDN}" -w ${BINDPW} -s ${NEWPW} \
+ "$BINDDN" >> $TESTOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldappasswd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo -n "Testing bind with new secret... "
+ $LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $NEWPW
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ BINDDN="cn=Some One,${BASEDN}"
+ BINDPW="someone"
+ echo -n "Testing bind with newly added user... "
+ $LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Using ldapsearch to retrieve all the entries..."
+ echo "# Using ldapsearch to retrieve all the entries..." >> $SEARCHOUT
+ $LDAPSEARCH -S "" -H $URI1 -b "$BASEDN" \
+ "objectClass=*" >> $SEARCHOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Filtering ldapsearch results..."
+ $LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+ echo "Filtering modified ldif..."
+ $LDIFFILTER < $SQLWRITE > $LDIFFLT
+ echo "Comparing filter output..."
+ $CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+ if test $? != 0 ; then
+ echo "comparison failed - SQL mods search didn't succeed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+
+ echo "Waiting 25 seconds for provider to send changes..."
+ sleep 25
+
+ cat /dev/null > $SEARCHOUT
+
+ echo "Using ldapsearch to retrieve all the entries from the provider..."
+ echo "# Using ldapsearch to retrieve all the entries from the provider..." \
+ >> $SEARCHOUT
+ $LDAPSEARCH -S "" -H $URI1 -b "$BASEDN" \
+ -D "$MANAGERDN" -w $PASSWD \
+ "(!(objectClass=referral))" >> $SEARCHOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ cat /dev/null > $SEARCHOUT2
+
+ echo "Using ldapsearch to retrieve all the entries from the consumer..."
+ echo "# Using ldapsearch to retrieve all the entries from the consumer..." \
+ >> $SEARCHOUT2
+ $LDAPSEARCH -S "" -H $URI2 -b "$BASEDN" \
+ -D "$UPDATEDN" -w $PASSWD \
+ "(objectClass=*)" >> $SEARCHOUT2 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Filtering ldapsearch results from provider..."
+ $LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+ echo "Filtering ldapsearch results from consumer..."
+ $LDIFFILTER < $SEARCHOUT2 > $SEARCHFLT2
+ echo "Comparing filter output..."
+ $CMP $SEARCHFLT $SEARCHFLT2 > $CMPOUT
+
+ if test $? != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+
+*)
+ echo "apparently ${RDBMS} does not support writes; skipping..."
+ ;;
+esac
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+exit 0
diff --git a/tests/scripts/start-server b/tests/scripts/start-server
new file mode 100755
index 0000000..aa8ea93
--- /dev/null
+++ b/tests/scripts/start-server
@@ -0,0 +1,63 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $CONF > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+
+echo "Using ldapsearch to retrieve all the entries..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 > $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC = 1 ; then
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+ fi
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - database was not created correctly"
+ exit 1
+fi
+
+echo ">>>>> Server1 (pid=$PID) started"
+exit 0
diff --git a/tests/scripts/start-server-nolog b/tests/scripts/start-server-nolog
new file mode 100755
index 0000000..a183d54
--- /dev/null
+++ b/tests/scripts/start-server-nolog
@@ -0,0 +1,63 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $CONF > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL >> /dev/null 2>&1 &
+PID=$!
+
+echo "Using ldapsearch to retrieve all the entries..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 > $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC = 1 ; then
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+ fi
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - database was not created correctly"
+ exit 1
+fi
+
+echo ">>>>> Server1 (pid=$PID) started"
+exit 0
diff --git a/tests/scripts/start-server2 b/tests/scripts/start-server2
new file mode 100755
index 0000000..044ef8e
--- /dev/null
+++ b/tests/scripts/start-server2
@@ -0,0 +1,42 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR2
+
+echo "Starting slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $CONFTWO > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID=$!
+
+echo "Using ldapsearch to retrieve all the entries..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -S "" -b "" -s base -H $URI2 > $SERVER2OUT 2>&1
+ RC=$?
+ if test $RC = 1 ; then
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+ fi
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+echo ">>>>> Server2 (pid=$PID) started"
+exit 0
diff --git a/tests/scripts/start-server2-nolog b/tests/scripts/start-server2-nolog
new file mode 100755
index 0000000..965c2c4
--- /dev/null
+++ b/tests/scripts/start-server2-nolog
@@ -0,0 +1,42 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR2
+
+echo "Starting slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $CONFTWO > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > /dev/null 2>&1 &
+PID=$!
+
+echo "Using ldapsearch to retrieve all the entries..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -S "" -b "" -s base -H $URI2 > $SERVER2OUT 2>&1
+ RC=$?
+ if test $RC = 1 ; then
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+ fi
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+echo ">>>>> Server2 (pid=$PID) started"
+exit 0
diff --git a/tests/scripts/startup_nis_ldap_server.sh b/tests/scripts/startup_nis_ldap_server.sh
new file mode 100755
index 0000000..0830f20
--- /dev/null
+++ b/tests/scripts/startup_nis_ldap_server.sh
@@ -0,0 +1,56 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+if [ $# -eq 0 ]; then
+ SRCDIR="."
+else
+ SRCDIR=$1; shift
+fi
+if [ $# -eq 1 ]; then
+ BDB2=$1; shift
+fi
+
+. $SRCDIR/scripts/defines.sh $SRCDIR $BDB2
+
+# Sample NIS database in LDIF format
+NIS_LDIF=$SRCDIR/data/nis_sample.ldif
+
+# Sample configuration file for your LDAP server
+if test "$BACKEND" = "bdb2" ; then
+ NIS_CONF=$DATADIR/slapd-bdb2-nis-provider.conf
+else
+ NIS_CONF=$DATADIR/slapd-nis-provider.conf
+fi
+
+echo "Cleaning up in $DBDIR..."
+
+rm -f $DBDIR/[!C]*
+
+echo "Running slapadd to build slapd database..."
+$SLAPADD -f $NIS_CONF -l $NIS_LDIF
+RC=$?
+if [ $RC != 0 ]; then
+ echo "slapadd failed!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT..."
+$SLAPD -f $NIS_CONF -p $PORT -d $LVL > $PROVIDERLOG 2>&1 &
+PID=$!
+
+echo ">>>>> LDAP server with NIS schema is up! PID=$PID"
+
+
+exit 0
diff --git a/tests/scripts/test000-rootdse b/tests/scripts/test000-rootdse
new file mode 100755
index 0000000..e01d7ae
--- /dev/null
+++ b/tests/scripts/test000-rootdse
@@ -0,0 +1,82 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $SCHEMACONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to retrieve the root DSE..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -b "" -s base -H $URI1 \
+ '@extensibleObject' > $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC = 0 ; then
+ echo "Using ldapsearch to retrieve the cn=Subschema..."
+ $LDAPSEARCH -b "cn=Subschema" -s base -H $URI1 \
+ '(&(objectClasses=top)(objectClasses=2.5.6.0))' cn objectClass \
+ >> $SEARCHOUT 2>&1
+ RC=$?
+
+fi
+
+if test $RC = 0 ; then
+ echo "Using ldapsearch to retrieve the cn=Monitor..."
+ $LDAPSEARCH -b "cn=Monitor" -s base -H $URI1 \
+ '@monitor' >> $SEARCHOUT 2>&1
+ RC=$?
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+cat $SEARCHOUT
+
+
+count=3
+if test $RC != 0 ; then
+ echo ">>>>> Test failed"
+else
+ RC=`grep '^dn:' $SEARCHOUT | wc -l`
+ if test $RC != $count ; then
+ echo ">>>>> Test failed: expected $count entries, got" $RC
+ RC=1
+ else
+ echo ">>>>> Test succeeded"
+ RC=0
+ fi
+fi
+
+test $KILLSERVERS != no && wait
+
+exit $RC
diff --git a/tests/scripts/test001-slapadd b/tests/scripts/test001-slapadd
new file mode 100755
index 0000000..fd5be88
--- /dev/null
+++ b/tests/scripts/test001-slapadd
@@ -0,0 +1,146 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $CONF > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to retrieve all the entries..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 > $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - database was not created correctly"
+ echo $SEARCHFLT $LDIFFLT
+ $DIFF $SEARCHFLT $LDIFFLT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+if test $BACKEND = ldif ; then
+ echo "Skipping test of unordered slapadd (unsupported in ldif backend)"
+else
+
+kill -HUP $KILLPIDS
+wait
+rm -f $DBDIR1/*
+
+BASE2="ou=test,dc=example,dc=com"
+sed -e "s;$BASEDN;$BASE2;" $ADDCONF > ${ADDCONF}2
+mv ${ADDCONF}2 $ADDCONF
+sed -e "s;$BASEDN;$BASE2;" $CONF1 > ${CONF1}2
+mv ${CONF1}2 $CONF1
+echo "Running slapadd with unordered LDIF..."
+$SLAPADD -f $ADDCONF -l $LDIFUNORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to retrieve all the entries..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -b "$BASE2" -H $URI1 > $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIFREORDERED > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - database was not created correctly"
+ echo $SEARCHFLT $LDIFFLT
+ $DIFF $SEARCHFLT $LDIFFLT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test002-populate b/tests/scripts/test002-populate
new file mode 100755
index 0000000..4ff45c7
--- /dev/null
+++ b/tests/scripts/test002-populate
@@ -0,0 +1,83 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDERED > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ 'objectclass=*' > $SEARCHOUT 2>&1
+RC=$?
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - database was not created correctly"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test003-search b/tests/scripts/test003-search
new file mode 100755
index 0000000..8ef54e5
--- /dev/null
+++ b/tests/scripts/test003-search
@@ -0,0 +1,155 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $MCONF > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Running slapindex to index slapd database..."
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+$SLAPINDEX -f $CONF1
+RC=$?
+if test $RC != 0 ; then
+ echo "warning: slapindex failed ($RC)"
+ echo " assuming no indexing support"
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing exact searching..."
+echo "# Testing exact searching..." > $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(sn=jENSEN)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing approximate searching..."
+echo "# Testing approximate searching..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(sn~=jENSEN)' name >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing OR searching..."
+echo "# Testing OR searching..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(|(givenname=Xx*yY*Z)(cn=)(undef=*)(objectclass=groupofnames)(sn=jones)(member=cn=Manager,dc=example,dc=com)(uniqueMember=cn=Manager,dc=example,dc=com))' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing AND matching and ends-with searching..."
+echo "# Testing AND matching and ends-with searching..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "ou=groups,$BASEDN" -s one -H $URI1 \
+ '(&(objectclass=groupofnames)(cn=A*)(member=cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com))' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing NOT searching..."
+echo "# Testing NOT searching..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(!(objectclass=pilotPerson))' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing objectClass/attributeType inheritance ..."
+echo "# Testing objectClass/attributeType inheritance ..." >> $SEARCHOUT
+$LDAPSEARCH -M -a never -S "" -b "$BASEDN" -H $URI1 \
+ '(&(objectClass=inetorgperson)(userid=uham))' \
+ "2.5.4.0" "userid" >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=$SEARCHOUTPROVIDER
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test004-modify b/tests/scripts/test004-modify
new file mode 100755
index 0000000..83cbd25
--- /dev/null
+++ b/tests/scripts/test004-modify
@@ -0,0 +1,122 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+$SLAPADD -f $CONF1 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd modify operations..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing modify, add, and delete..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT -f $LDIFMODIFY
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapmodify to add an empty entry (should fail with protocolError)..."
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOMODS
+dn: cn=Foo Bar,dc=example,dc=com
+changetype: add
+# EMPTY SEQUENCE OF ATTRS
+EOMODS
+
+RC=$?
+case $RC in
+2)
+ echo " ldapmodify failed ($RC)"
+ ;;
+0)
+ echo " ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo " ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Using ldapsearch to retrieve all the entries..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ 'objectClass=*' > $SEARCHOUT 2>&1
+RC=$?
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+LDIF=$MODIFYOUTPROVIDER
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - modify operations did not complete correctly"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test005-modrdn b/tests/scripts/test005-modrdn
new file mode 100755
index 0000000..6b028bb
--- /dev/null
+++ b/tests/scripts/test005-modrdn
@@ -0,0 +1,300 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1A $DBDIR1B
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $CONF2DB > $CONF1
+$SLAPADD -f $CONF1 -b "$BASEDN" -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd modrdn operations..."
+
+# Make sure we can search the database
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ 'objectClass=*' > $INITOUT 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# -r used to do remove of old rdn
+
+echo "Testing modrdn(deleteoldrdn=0)..."
+$LDAPMODRDN -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 'cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example, dc=com' 'cn=James A Jones III'
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodrdn failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing modrdn(deleteoldrdn=1)..."
+$LDAPMODRDN -D "$MANAGERDN" -r -H $URI1 -w $PASSWD >> \
+ $TESTOUT 2>&1 'cn=James A Jones 2, ou=Information Technology Division, ou=People, dc=example, dc=com' 'cn=James A Jones II'
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodrdn failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Ensure the new rdn's can be found
+
+echo "Using ldapsearch to retrieve entries using new rdn (cn=James A Jones III)..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ 'cn=James A Jones III' > $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+LDIF=$MODRDNOUTPROVIDER1
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - modrdn operations did not complete correctly"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+
+echo "Using ldapsearch to retrieve entries using new rdn (cn=James A Jones II)..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ 'cn=James A Jones II' > $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+LDIF=$MODRDNOUTPROVIDER2
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - modrdn operations did not complete correctly"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+# Ensure that you cannot find the entry for which the rdn was deleted as
+# an attribute.
+
+echo "Using ldapsearch to retrieve entries using removed rdn (cn=James A Jones 2)..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ 'cn=James A Jones 2' > $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+$CMP $SEARCHOUT - < /dev/null > $CMPOUT
+if test $? != 0 ; then
+ echo "failure: ldapsearch found attribute that was to be removed!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo "Using ldapsearch to retrieve all the entries..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ 'objectClass=*' > $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+LDIF=$MODRDNOUTPROVIDER0
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - modrdn operations did not complete correctly"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+# Test that you can use modrdn with an attribute value which was previously
+# present
+
+echo "Testing modrdn(deleteoldrdn=1), modrdn with new rdn already an att val..."
+$LDAPMODRDN -D "$MANAGERDN" -r -H $URI1 -w $PASSWD > \
+ /dev/null 2>&1 'cn=James A Jones III, ou=Alumni Association, ou=People, dc=example, dc=com' 'cn=James A Jones 1'
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodrdn failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to retrieve entries using new rdn (cn=James A Jones 1)..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ 'cn=James A Jones 1' > $SEARCHOUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+LDIF=$MODRDNOUTPROVIDER3
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - modrdn operations did not complete correctly"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo "Testing modrdn to another database (should fail with affectsMultipleDSAs)"
+$LDAPMODRDN -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 'cn=All Staff,ou=Groups,dc=example,dc=com' 'cn=Everyone'
+RC=$?
+case $RC in
+0)
+ echo "ldapmodrdn succeeded, should have failed!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+71)
+ ;;
+*)
+ echo "ldapmodrdn failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Testing modrdn with newSuperior = target (should fail with unwillingToPerform)"
+$LDAPMODRDN -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 -s 'cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example, dc=com' \
+ 'cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example, dc=com' 'cn=James A Jones 1'
+
+RC=$?
+case $RC in
+0)
+ echo "ldapmodrdn succeeded, should have failed!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+53)
+ ;;
+*)
+ echo "ldapmodrdn failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Testing modrdn with newRdn exact same as target..."
+$LDAPMODRDN -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 'cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example, dc=com' 'cn=James A Jones 1'
+
+RC=$?
+case $RC in
+0)
+ ;;
+*)
+ echo "ldapmodrdn failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Testing modrdn with newRdn same as target, changed case..."
+$LDAPMODRDN -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 'cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example, dc=com' 'cn=James A JONES 1'
+
+RC=$?
+case $RC in
+0)
+ ;;
+*)
+ echo "ldapmodrdn failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test006-acls b/tests/scripts/test006-acls
new file mode 100755
index 0000000..d0ffda7
--- /dev/null
+++ b/tests/scripts/test006-acls
@@ -0,0 +1,667 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+case "$BACKEND" in ldif | null)
+ echo "$BACKEND backend does not support access controls, test skipped"
+ exit 0
+esac
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $ACLCONF > $CONF1
+$SLAPADD -f $CONF1 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd access control..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+echo "# Try to read an entry inside the Alumni Association container.
+# It should give us noSuchObject if we're not bound..." \
+>> $SEARCHOUT
+# FIXME: temporarily remove the "No such object" message to make
+# the test succeed even if SLAP_ACL_HONOR_DISCLOSE is not #define'd
+$LDAPSEARCH -b "$JAJDN" -H $URI1 "(objectclass=*)" \
+ 2>&1 | grep -v "No such object" >> $SEARCHOUT
+
+echo "# ... and should return all attributes if we're bound as anyone
+# under Example." \
+>> $SEARCHOUT
+$LDAPSEARCH -b "$JAJDN" -H $URI1 \
+ -D "$BABSDN" -w bjensen "(objectclass=*)" >> $SEARCHOUT 2>&1
+
+# ITS#4253, ITS#4255
+echo "# Checking exact/regex attrval clause" >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 \
+ -D "$BABSDN" -w bjensen \
+ -b "$MELLIOTDN" -s base "(objectclass=*)" cn >> $SEARCHOUT 2>&1
+$LDAPSEARCH -H $URI1 \
+ -D "$BJORNSDN" -w bjorn \
+ -b "$MELLIOTDN" -s base "(objectclass=*)" cn >> $SEARCHOUT 2>&1
+
+$LDAPSEARCH -H $URI1 \
+ -D "$BABSDN" -w bjensen \
+ -b "$JOHNDDN" -s base "(objectclass=*)" cn >> $SEARCHOUT 2>&1
+$LDAPSEARCH -H $URI1 \
+ -D "$BJORNSDN" -w bjorn \
+ -b "$JOHNDDN" -s base "(objectclass=*)" cn >> $SEARCHOUT 2>&1
+
+$LDAPSEARCH -H $URI1 \
+ -D "$BABSDN" -w bjensen \
+ -b "$BJORNSDN" -s base "(objectclass=*)" cn >> $SEARCHOUT 2>&1
+$LDAPSEARCH -H $URI1 \
+ -D "$BJORNSDN" -w bjorn \
+ -b "$BABSDN" -s base "(objectclass=*)" cn >> $SEARCHOUT 2>&1
+
+# check selfwrite access (ITS#4587). 6 attempts are made:
+# 1) delete someone else (should fail)
+# 2) delete self (should succeed)
+# 3) add someone else (should fail)
+# 4) add someone else and self (should fail)
+# 5) add self and someone else (should fail)
+# 6) add self (should succeed)
+#
+$LDAPMODIFY -D "$JAJDN" -H $URI1 -w jaj >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: member
+member: $BABSDN
+EOMODS
+RC=$?
+case $RC in
+50)
+ ;;
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$JAJDN" -H $URI1 -w jaj >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: member
+member: $JAJDN
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$JAJDN" -H $URI1 -w jaj >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+add: member
+member: cn=Foo,ou=Bar
+EOMODS
+RC=$?
+case $RC in
+50)
+ ;;
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$JAJDN" -H $URI1 -w jaj >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+add: member
+member: cn=Foo,ou=Bar
+member: $JAJDN
+EOMODS
+RC=$?
+case $RC in
+50)
+ ;;
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$JAJDN" -H $URI1 -w jaj >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+add: member
+member: $JAJDN
+member: cn=Foo,ou=Bar
+EOMODS
+RC=$?
+case $RC in
+50)
+ ;;
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$JAJDN" -H $URI1 -w jaj >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+add: member
+member: $JAJDN
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#
+# Check group access. Try to modify Babs' entry. Two attempts:
+# 1) bound as "James A Jones 1" - should fail
+# 2) bound as "Bjorn Jensen" - should succeed
+
+$LDAPMODIFY -D "$JAJDN" -H $URI1 -w jaj >> \
+ $TESTOUT 2>&1 << EOMODS5
+dn: $BABSDN
+changetype: modify
+replace: drink
+drink: wine
+EOMODS5
+RC=$?
+case $RC in
+50)
+ ;;
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$BJORNSDN" -H $URI1 -w bjorn >> \
+ $TESTOUT 2>&1 << EOMODS6
+dn: $BABSDN
+changetype: modify
+add: homephone
+homephone: +1 313 555 5444
+EOMODS6
+RC=$?
+case $RC in
+0)
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+#
+# Try to add a "member" attribute to the "ITD Staff" group. It should
+# fail when we add some DN other than our own, and should succeed when
+# we add our own DN.
+# bjensen
+$LDAPMODIFY -D "$JAJDN" -H $URI1 -w jaj >> \
+ $TESTOUT 2>&1 << EOMODS1
+version: 1
+dn: cn=ITD Staff, ou=Groups, dc=example, dc=com
+changetype: modify
+add: uniquemember
+uniquemember: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+EOMODS1
+RC=$?
+case $RC in
+50)
+ ;;
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$JAJDN" -H $URI1 -w jaj >> \
+ $TESTOUT 2>&1 << EOMODS2
+version: 1
+
+dn: cn=ITD Staff, ou=Groups, dc=example, dc=com
+changetype: modify
+add: uniquemember
+uniquemember: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example, dc=com
+EOMODS2
+RC=$?
+case $RC in
+0)
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+#
+# Try to modify the "ITD Staff" group. Two attempts are made:
+# 1) bound as "James A Jones 1" - should fail
+# 2) bound as "Bjorn Jensen" - should succeed
+#
+$LDAPMODIFY -D "$JAJDN" -H $URI1 -w jaj >> \
+ $TESTOUT 2>&1 << EOMODS3
+
+dn: cn=ITD Staff, ou=Groups, dc=example, dc=com
+changetype: modify
+delete: description
+EOMODS3
+RC=$?
+case $RC in
+50)
+ ;;
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$BJORNSDN" -H $URI1 -w bjorn >> \
+ $TESTOUT 2>&1 << EOMODS4
+# COMMENT
+version: 1
+# comment
+dn: cn=ITD Staff, ou=Groups, dc=example, dc=com
+# comment
+changetype: modify
+# comment
+add: ou
+# comment
+ou: Groups
+# comment
+EOMODS4
+RC=$?
+case $RC in
+0)
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+#
+# Try to modify the "ITD Staff" group. Two attempts are made:
+# 1) bound as "James A Jones 1" - should succeed
+# 2) bound as "Barbara Jensen" - should fail
+# should exploit sets
+#
+$LDAPMODIFY -D "$JAJDN" -H $URI1 -w jaj >> \
+ $TESTOUT 2>&1 << EOMODS5
+dn: cn=Alumni Assoc Staff, ou=Groups, dc=example, dc=com
+changetype: modify
+add: description
+description: added by jaj (should succeed)
+-
+EOMODS5
+RC=$?
+case $RC in
+0)
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$BABSDN" -H $URI1 -w bjensen >> \
+ $TESTOUT 2>&1 << EOMODS6
+dn: cn=Alumni Assoc Staff, ou=Groups, dc=example, dc=com
+changetype: modify
+add: description
+description: added by bjensen (should fail)
+-
+EOMODS6
+RC=$?
+case $RC in
+50)
+ ;;
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD >> \
+ $TESTOUT 2>&1 << EOMODS7
+dn: ou=Add & Delete,dc=example,dc=com
+changetype: add
+objectClass: organizationalUnit
+ou: Add & Delete
+EOMODS7
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$BABSDN" -H $URI1 -w bjensen >> \
+ $TESTOUT 2>&1 << EOMODS8
+dn: cn=Added by Babs (must fail),ou=Add & Delete,dc=example,dc=com
+changetype: add
+objectClass: inetOrgPerson
+cn: Added by Babs (must fail)
+sn: None
+EOMODS8
+RC=$?
+case $RC in
+50)
+ ;;
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$BJORNSDN" -H $URI1 -w bjorn >> \
+ $TESTOUT 2>&1 << EOMODS9
+dn: cn=Added by Bjorn (must succeed),ou=Add & Delete,dc=example,dc=com
+changetype: add
+objectClass: inetOrgPerson
+cn: Added by Bjorn (must succeed)
+sn: None
+
+dn: cn=Added by Bjorn (will be deleted),ou=Add & Delete,dc=example,dc=com
+changetype: add
+objectClass: inetOrgPerson
+cn: Added by Bjorn (will be deleted)
+sn: None
+
+dn: cn=Added by Bjorn (will be renamed),ou=Add & Delete,dc=example,dc=com
+changetype: add
+objectClass: inetOrgPerson
+cn: Added by Bjorn (will be renamed)
+sn: None
+
+dn: cn=Added by Bjorn (must succeed),ou=Add & Delete,dc=example,dc=com
+changetype: modify
+add: description
+description: this attribute value has been added __after__entry creation
+description: this attribute value will be deleted by Babs (must succeed)
+description: Bjorn will try to delete this attribute value (should fail)
+-
+EOMODS9
+RC=$?
+case $RC in
+0)
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$BJORNSDN" -H $URI1 -w bjorn >> \
+ $TESTOUT 2>&1 << EOMODS10
+dn: cn=Added by Bjorn (will be deleted),ou=Add & Delete,dc=example,dc=com
+changetype: delete
+EOMODS10
+RC=$?
+case $RC in
+50)
+ ;;
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$BJORNSDN" -H $URI1 -w bjorn >> \
+ $TESTOUT 2>&1 << EOMODS11
+dn: cn=Added by Bjorn (will be renamed),ou=Add & Delete,dc=example,dc=com
+changetype: modrdn
+newrdn: cn=Added by Bjorn (renamed by Bjorn)
+deleteoldrdn: 1
+EOMODS11
+RC=$?
+case $RC in
+50)
+ ;;
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$BABSDN" -H $URI1 -w bjensen >> \
+ $TESTOUT 2>&1 << EOMODS12
+dn: cn=Added by Bjorn (will be renamed),ou=Add & Delete,dc=example,dc=com
+changetype: modrdn
+newrdn: cn=Added by Bjorn (renamed by Babs)
+deleteoldrdn: 1
+EOMODS12
+RC=$?
+case $RC in
+50)
+ ;;
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$JAJDN" -H $URI1 -w jaj >> \
+ $TESTOUT 2>&1 << EOMODS13
+dn: cn=Added by Bjorn (will be renamed),ou=Add & Delete,dc=example,dc=com
+changetype: modrdn
+newrdn: cn=Added by Bjorn (renamed by Jaj)
+deleteoldrdn: 1
+EOMODS13
+RC=$?
+case $RC in
+0)
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$BJORNSDN" -H $URI1 -w bjorn >> \
+ $TESTOUT 2>&1 << EOMODS14
+dn: cn=Added by Bjorn (must succeed),ou=Add & Delete,dc=example,dc=com
+changetype: modify
+delete: description
+description: Bjorn will try to delete this attribute value (should fail)
+-
+EOMODS14
+RC=$?
+case $RC in
+50)
+ ;;
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+$LDAPMODIFY -D "$BABSDN" -H $URI1 -w bjensen >> \
+ $TESTOUT 2>&1 << EOMODS15
+dn: cn=Added by Bjorn (will be deleted),ou=Add & Delete,dc=example,dc=com
+changetype: delete
+
+dn: cn=Added by Bjorn (must succeed),ou=Add & Delete,dc=example,dc=com
+changetype: modify
+delete: description
+description: this attribute value will be deleted by Babs (must succeed)
+-
+EOMODS15
+RC=$?
+case $RC in
+0)
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Using ldapsearch to retrieve all the entries..."
+echo "# Using ldapsearch to retrieve all the entries..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ 'objectClass=*' >> $SEARCHOUT 2>&1
+RC=$?
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+LDIF=$ACLOUTPROVIDER
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - operations did not complete correctly"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test007-slapmodify b/tests/scripts/test007-slapmodify
new file mode 100755
index 0000000..9acd579
--- /dev/null
+++ b/tests/scripts/test007-slapmodify
@@ -0,0 +1,90 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $CONF > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Testing modify, add, and delete using slapmodify..."
+$SLAPMODIFY -f $ADDCONF -d $LVL -l $LDIFMODIFY > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapmodify failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to retrieve all the entries..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 > $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+LDIF=$MODIFYOUTPROVIDER
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "modify operations did not complete correctly"
+ echo $SEARCHFLT $LDIFFLT
+ $DIFF $SEARCHFLT $LDIFFLT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test008-concurrency b/tests/scripts/test008-concurrency
new file mode 100755
index 0000000..2d71e3a
--- /dev/null
+++ b/tests/scripts/test008-concurrency
@@ -0,0 +1,99 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test x$TESTLOOPS = x ; then
+ TESTLOOPS=50
+fi
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+$SLAPADD -f $CONF1 -l $LDIFORDERED -d -1 2> $SLAPADDLOG1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+# fix test data to include back-monitor, if available
+# NOTE: copies do_* files from $DATADIR to $TESTDIR
+$MONITORDATA "$DATADIR" "$TESTDIR"
+
+echo "Using tester for concurrent server access..."
+$SLAPDTESTER -P "$PROGDIR" -d "$TESTDIR" -H $URI1 -D "$MANAGERDN" -w $PASSWD -l $TESTLOOPS
+RC=$?
+
+if test $RC != 0 ; then
+ echo "slapd-tester failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to retrieve all the entries..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ 'objectClass=*' > $SEARCHOUT 2>&1
+RC=$?
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - database was not created correctly"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test009-referral b/tests/scripts/test009-referral
new file mode 100755
index 0000000..b6f05fa
--- /dev/null
+++ b/tests/scripts/test009-referral
@@ -0,0 +1,181 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+#
+# Test default referral
+#
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+$SLAPADD -f $CONF1 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting provider slapd on TCP/IP port $PORT1..."
+$SLAPD -n provider -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+
+echo "Starting consumer slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $REFCONSUMERCONF > $CONF2
+$SLAPD -n consumer -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+
+KILLPIDS="$PID $CONSUMERPID"
+
+sleep 1
+
+echo "Testing for provider slapd..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for provider slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing for consumer slapd..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for consumer slapd to start..."
+ sleep 5
+done
+
+cat /dev/null > $SEARCHOUT
+
+echo "Testing exact searching..."
+$LDAPSEARCH -C -S "" -b "$BASEDN" -H $URI2 \
+ 'sn=jensen' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing approximate searching..."
+$LDAPSEARCH -C -S "" -b "$BASEDN" -H $URI2 \
+ '(sn=jENSEN)' name >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing OR searching..."
+$LDAPSEARCH -C -S "" -b "$BASEDN" -H $URI2 \
+ '(|(objectclass=groupofnames)(objectClass=groupofuniquenames)(sn=jones))' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing AND matching and ends-with searching..."
+$LDAPSEARCH -C -S "" -b "ou=groups,$BASEDN" -s one -H $URI2 \
+ '(&(objectclass=groupofnames)(cn=A*))' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing NOT searching..."
+$LDAPSEARCH -C -S "" -b "$BASEDN" -H $URI2 \
+ '(!(objectclass=pilotPerson))' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing objectClass/attributeType inheritance ..."
+$LDAPSEARCH -M -a never -S "" -b "$BASEDN" -H $URI1 \
+ '(&(objectClass=inetorgperson)(userid=uham))' \
+ "2.5.4.0" "userid" >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing dontUseCopy control..."
+$LDAPSEARCH -C -S "" -b "$BASEDN" -H $URI2 \
+ -E \!dontUseCopy \
+ 'sn=jensen' >> $SEARCHOUT
+RC=$?
+if test $RC = 10 ; then
+ echo "ldapsearch failed as expected ($RC)"
+else
+ echo "ldapsearch did not error as expected ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=$SEARCHOUTPROVIDER
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test010-passwd b/tests/scripts/test010-passwd
new file mode 100755
index 0000000..ac6fbb9
--- /dev/null
+++ b/tests/scripts/test010-passwd
@@ -0,0 +1,189 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $PWCONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFPASSWD > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo > $SEARCHOUT
+echo > $TESTOUT
+
+echo "Using ldapsearch to verify population ..."
+echo "++ Initial search" >> $SEARCHOUT
+$LDAPSEARCH -H $URI1 \
+ -D "$MANAGERDN" -w $PASSWD \
+ -b "$BASEDN" \
+ 'objectclass=*' >> $SEARCHOUT 2>&1
+
+echo "Using ldappasswd to test a few error conditions ..."
+echo "Pass 0" >> $TESTOUT
+$LDAPPASSWD -H $URI1 \
+ -w secret -a "" -s newsecret \
+ -D "cn=md5, $BASEDN" >> $TESTOUT 2>&1
+RC=$?
+if test $RC = 0 ; then
+ echo "ldappasswd unexpectantly passed ($RC)! old empty"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+$LDAPPASSWD -H $URI1 \
+ -w secret -a oldsecret -s "" \
+ -D "cn=md5, $BASEDN" >> $TESTOUT 2>&1
+RC=$?
+if test $RC = 0 ; then
+ echo "ldappasswd unexpectantly passed ($RC)! new empty"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+$LDAPPASSWD -H $URI1 \
+ -w secret -a oldsecret -s newsecret \
+ -D "cn=md5, $BASEDN" >> $TESTOUT 2>&1
+RC=$?
+if test $RC = 0 ; then
+ echo "ldappasswd unexpectantly passed ($RC)! wrong old"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Using ldappasswd (PASS 1) ..."
+echo "Pass 1" >> $TESTOUT
+$LDAPPASSWD -H $URI1 \
+ -w secret -s newsecret \
+ -D "cn=md5, $BASEDN" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldappasswd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDAPPASSWD -H $URI1 \
+ -w $PASSWD -s newsecret \
+ -D "$MANAGERDN" "cn=smd5, $BASEDN" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldappasswd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDAPPASSWD -H $URI1 \
+ -w secret -s newsecret \
+ -D "cn=sha, $BASEDN" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldappasswd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDAPPASSWD -H $URI1 \
+ -w secret -s newsecret \
+ -D "cn=ssha, $BASEDN" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldappasswd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "" >> $TESTOUT
+echo "Pass 2" >> $TESTOUT
+echo "Using ldappasswd (PASS 2) ..."
+$LDAPPASSWD -H $URI1 \
+ -w newsecret \
+ -D "cn=md5, $BASEDN" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldappasswd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDAPPASSWD -H $URI1 \
+ -w newsecret \
+ -D "cn=smd5, $BASEDN" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldappasswd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDAPPASSWD -H $URI1 \
+ -w newsecret \
+ -D "cn=sha, $BASEDN" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldappasswd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDAPPASSWD -H $URI1 \
+ -w newsecret \
+ -D "cn=ssha, $BASEDN" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldappasswd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Logging end state with ldapsearch..."
+echo "" >> $TESTOUT
+echo "++ End search" >> $TESTOUT
+$LDAPSEARCH -H $URI1 \
+ -D "$MANAGERDN" -w $PASSWD \
+ -b "$BASEDN" \
+ 'objectclass=*' >> $TESTOUT 2>&1
+
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test011-glue-slapadd b/tests/scripts/test011-glue-slapadd
new file mode 100755
index 0000000..710263d
--- /dev/null
+++ b/tests/scripts/test011-glue-slapadd
@@ -0,0 +1,98 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1A $DBDIR1B $DBDIR1C
+
+echo "Running slapadd to build glued slapd databases..."
+. $CONFFILTER $BACKEND < $GLUECONF > $CONF1
+$SLAPADD -d $LVL -f $CONF1 -l $LDIFORDERED > $SLAPADDLOG1 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to retrieve all the entries..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -b "$BASEDN" -H $URI1 > $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s ldif=e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER -s ldif=e < $LDIFGLUED > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - database was not created correctly"
+ echo $SEARCHFLT $LDIFFLT
+ $DIFF $SEARCHFLT $LDIFFLT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+if test $BACKEND != null ; then
+echo "Testing sizelimit..."
+$LDAPSEARCH -b "$BASEDN" -H $URI1 -s one -z 2 > $SEARCHOUT 2>&1
+RC=$?
+if test $RC = 0 ; then
+ echo "sizelimit not detected at end of search."
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+$LDAPSEARCH -b "$BASEDN" -H $URI1 -z 9 objectclass=OpenLDAPPerson > $SEARCHOUT 2>&1
+RC=$?
+if test $RC = 0 ; then
+ echo "sizelimit not detected at middle of search."
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test012-glue-populate b/tests/scripts/test012-glue-populate
new file mode 100755
index 0000000..121aa7c
--- /dev/null
+++ b/tests/scripts/test012-glue-populate
@@ -0,0 +1,83 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1A $DBDIR1B $DBDIR1C
+
+echo "Starting slapd on TCP/IP port $PORT..."
+. $CONFFILTER $BACKEND < $GLUECONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+echo "Using ldapadd to populate the glued database..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDERED > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries..."
+$LDAPSEARCH -b "$BASEDN" -H $URI1 \
+ 'objectclass=*' > $SEARCHOUT 2>&1
+RC=$?
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s ldif=e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER -s ldif=e < $LDIFGLUED > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - database was not created correctly"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test013-language b/tests/scripts/test013-language
new file mode 100755
index 0000000..aa69c62
--- /dev/null
+++ b/tests/scripts/test013-language
@@ -0,0 +1,117 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFLANG > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 -s base \
+ '(&)' > $SEARCHOUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read name ..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 -s base \
+ '(&)' 'name' >> $SEARCHOUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read name language tag ..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 -s base \
+ '(&)' 'name;lang-en-US' >> $SEARCHOUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read name language range ..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 -s base \
+ '(&)' 'name;lang-en-' >> $SEARCHOUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering language ldif ..."
+$LDIFFILTER < $LDIFLANGOUT > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - language test failed!"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test014-whoami b/tests/scripts/test014-whoami
new file mode 100755
index 0000000..b1ed009
--- /dev/null
+++ b/tests/scripts/test014-whoami
@@ -0,0 +1,468 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $WHOAMICONF > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFWHOAMI
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT..."
+. $CONFFILTER $BACKEND < $WHOAMICONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+echo "Testing ldapwhoami as anonymous..."
+$LDAPWHOAMI -H $URI1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing ldapwhoami as ${MANAGERDN}..."
+$LDAPWHOAMI -H $URI1 -D "$MANAGERDN" -w $PASSWD
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing ldapwhoami as ${MANAGERDN} for anonymous..."
+$LDAPWHOAMI -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e \!authzid=""
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing ldapwhoami as ${MANAGERDN} for dn:$BABSDN..."
+$LDAPWHOAMI -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e \!authzid="dn:$BABSDN"
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing ldapwhoami as ${MANAGERDN} for u:uham..."
+$LDAPWHOAMI -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e \!authzid="u:uham"
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# authzFrom: someone else => bjorn
+echo "Testing authzFrom..."
+
+BINDDN="cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+BINDPW=bjensen
+AUTHZID="u:bjorn"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID} (dn.exact)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BINDDN="cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com"
+BINDPW=melliot
+AUTHZID="u:bjorn"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID} (u)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BINDDN="cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com"
+BINDPW=jen
+AUTHZID="u:bjorn"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID} (URI)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BINDDN="cn=James A Jones 2,ou=Information Technology Division,ou=People,dc=example,dc=com"
+BINDPW=jjones
+AUTHZID="u:bjorn"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID} (group)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BINDDN="cn=No One,ou=Information Technology Division,ou=People,dc=example,dc=com"
+BINDPW=noone
+AUTHZID="u:bjorn"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID} (dn.onelevel)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BINDDN="cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com"
+BINDPW=dots
+AUTHZID="u:bjorn"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID} (dn.regex)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BINDDN="cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com"
+BINDPW=jaj
+AUTHZID="u:bjorn"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID} (dn.children)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BINDDN="cn=ITD Staff,ou=Groups,dc=example,dc=com"
+BINDPW=ITD
+AUTHZID="u:bjorn"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID} (dn.subtree)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BINDDN="cn=Should Fail,dc=example,dc=com"
+BINDPW=fail
+AUTHZID="u:bjorn"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID} (URI; should fail)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+case $RC in
+1)
+ ;;
+0)
+ echo "ldapwhoami should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+BINDDN="cn=Must Fail,dc=example,dc=com"
+BINDPW=fail
+AUTHZID="u:bjorn"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID} (URI; should fail)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+case $RC in
+1)
+ ;;
+0)
+ echo "ldapwhoami should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+# authzTo: bjorn => someone else
+echo "Testing authzTo..."
+
+BINDDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+BINDPW=bjorn
+AUTHZID="u:bjensen"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID} (dn.exact)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BINDDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+BINDPW=bjorn
+AUTHZID="u:melliot"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID} (u)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BINDDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+BINDPW=bjorn
+AUTHZID="u:jdoe"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID} (URI)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BINDDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+BINDPW=bjorn
+AUTHZID="u:jjones"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID} (group)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BINDDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+BINDPW=bjorn
+AUTHZID="u:noone"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID} (dn.onelevel)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BINDDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+BINDPW=bjorn
+AUTHZID="u:dots"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID} (dn.regex)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BINDDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+BINDPW=bjorn
+AUTHZID="u:jaj"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID} (dn.children)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BINDDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+BINDPW=bjorn
+AUTHZID="u:group/itd staff"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID} (dn.subtree)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+BINDDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+BINDPW=bjorn
+AUTHZID="u:fail"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID} (URI; should fail)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+case $RC in
+1)
+ ;;
+0)
+ echo "ldapwhoami should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+BINDDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+BINDPW=bjorn
+AUTHZID="dn:cn=Should Fail,dc=example,dc=com"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID} (URI; should fail)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+case $RC in
+1)
+ ;;
+0)
+ echo "ldapwhoami should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+BINDDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+BINDPW=bjorn
+AUTHZID="dn:cn=don't!"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID} (no authzTo; should fail)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+if test $RC != 1 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+BINDDN="dc=example,dc=com"
+BINDPW=example
+AUTHZID="dn:"
+echo "Testing ldapwhoami as ${BINDDN} for ${AUTHZID}\"\" (dn.exact; should succeed)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW \
+ -e \!authzid="$AUTHZID"
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
+
+## Note to developers: when SLAPD_DEBUG=-1 the command
+## awk '/^do_extended$/ {if (c) {print c} c=0} /<===slap_sasl_match:/ {c++} END {print c}' $TESTDIR/slapd.1.log
+## must return the sequence 1 2 3 4 5 6 7 8 8 8 1 2 3 4 5 6 7 8 8 8 8 1
+## to indicate that the authzFrom and authzTo rules applied in the right order.
diff --git a/tests/scripts/test015-xsearch b/tests/scripts/test015-xsearch
new file mode 100755
index 0000000..b24d4a4
--- /dev/null
+++ b/tests/scripts/test015-xsearch
@@ -0,0 +1,272 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $MCONF > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Running slapindex to index slapd database..."
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+$SLAPINDEX -f $CONF1
+RC=$?
+if test $RC != 0 ; then
+ echo "warning: slapindex failed ($RC)"
+ echo " assuming no indexing support"
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+echo "Testing exact searching..."
+echo "# Testing exact searching..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(sn:=jensen)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing approximate searching..."
+echo "# Testing approximate searching..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(sn~=jensen)' name >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing OR searching..."
+echo "# Testing OR searching..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(|(givenName=XX*YY*Z)(cn=)(undef=*)(objectclass=groupofnames)(objectclass=groupofuniquenames)(sn:caseExactMatch:=Jones))' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing AND matching and ends-with searching..."
+echo "# Testing AND matching and ends-with searching..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "ou=groups,$BASEDN" -s one -H $URI1 \
+ '(&(|(objectclass=groupofnames)(objectclass=groupofuniquenames))(cn=A*))' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing NOT searching..."
+echo "# Testing NOT searching..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(!(objectclass=pilotPerson))' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing objectClass/attributeType inheritance ..."
+echo "# Testing objectClass/attributeType inheritance ..." >> $SEARCHOUT
+$LDAPSEARCH -M -a never -S "" -b "$BASEDN" -H $URI1 \
+ '(&(objectClass=inetorgperson)(userid=uham))' \
+ "2.5.4.0" "userid" >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing extended RFC2254 searching:"
+echo "# Testing extended RFC2254 searching:" >> $SEARCHOUT
+
+FILTER="(:dn:caseIgnoreIA5Match:=example)"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "$FILTER" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+FILTER="(:dn:caseExactMatch:=Information Technology Division)"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "$FILTER" >> $SEARCHOUT 2>&1
+
+# ITS#4380: don't crash when a matchingRule without pretty/validate is used
+FILTER="(:dn:caseIgnoreSubstringsMatch:=Information Technology Division)"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "$FILTER" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+FILTER="(name:dn:=whatever)"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "" -s base -H $URI1 \
+ "$FILTER" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing values return filter searching:"
+echo "# Testing values return filter searching:" >> $SEARCHOUT
+
+FILTER="(o=Example, Inc.)"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ -E '!mv='"$FILTER" "$FILTER" >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+FILTER="(dc=example)"
+VRFILTER="((o:caseExactMatch:=Example, Inc.)(dc=example))"
+echo " f=$FILTER mv=$VRFILTER ..."
+echo "# f=$FILTER mv=$VRFILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ -E '!mv='"$VRFILTER" "$FILTER" >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+FILTER="(dc=example)"
+VRFILTER="((o={*)(dc=*))"
+echo " f=$FILTER mv=$VRFILTER ..."
+echo "# f=$FILTER mv=$VRFILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ -E '!mv='"$VRFILTER" "$FILTER" >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+FILTER="(attributeTypes=0.9.2342.19200300.100.1.25)"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "cn=Subschema" -s "base" -H $URI1 \
+ -E '!mv='"$FILTER" "$FILTER" "attributeTypes" >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing list substring searching..."
+echo "# Testing list substring searching..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(postalAddress=*Anytown*)' postalAddress >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=$SEARCHOUTPROVIDER
+LDIF2=$SEARCHOUTX
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+$LDIFFILTER < $LDIF2 >> $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test016-subref b/tests/scripts/test016-subref
new file mode 100755
index 0000000..851532f
--- /dev/null
+++ b/tests/scripts/test016-subref
@@ -0,0 +1,197 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+RCODE=10
+test $BACKEND = null && RCODE=0
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $RCONF > $CONF1
+$SLAPADD -f $CONF1 -l $LDIFREF
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+echo "Testing ManageDsaIT searching at $REFDN..."
+$LDAPRSEARCH -S "" -MM -b "$REFDN" -H $URI1 \
+ '(objectClass=referral)' '*' ref >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing ManageDsaIT searching at referral object..."
+$LDAPRSEARCH -S "" -MM -b "o=abc,$REFDN" -H $URI1 \
+ '(objectClass=referral)' '*' ref >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing ManageDsaIT searching below referral object..."
+$LDAPRSEARCH -S "" -MM -b "uid=xxx,o=abc,$REFDN" -H $URI1 \
+ '(objectClass=referral)' '*' ref >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != $RCODE ; then
+ echo "ldapsearch: unexpected result ($RC)! (referral expected)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+XREFDN="$REFDN"
+echo "Testing base searching at $XREFDN..."
+$LDAPRSEARCH -S "" -s base -b "$XREFDN" -H $URI1 1.1 >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing one-level searching at $XREFDN..."
+$LDAPRSEARCH -S "" -s one -b "$XREFDN" -H $URI1 1.1 >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing subtree searching at $XREFDN..."
+$LDAPRSEARCH -S "" -s sub -b "$XREFDN" -H $URI1 1.1 >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+XREFDN="o=abc,$REFDN"
+echo "Testing base searching at $XREFDN..."
+$LDAPRSEARCH -S "" -s base -b "$XREFDN" -H $URI1 1.1 >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != $RCODE ; then
+ echo "ldapsearch: unexpected result ($RC)! (referral expected)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing one-level searching at $XREFDN..."
+$LDAPRSEARCH -S "" -s one -b "$XREFDN" -H $URI1 1.1 >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != $RCODE ; then
+ echo "ldapsearch: unexpected result ($RC)! (referral expected)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing subtree searching at $XREFDN..."
+$LDAPRSEARCH -S "" -s sub -b "$XREFDN" -H $URI1 1.1 >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != $RCODE ; then
+ echo "ldapsearch: unexpected result ($RC)! (referral expected)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+XREFDN="uid=xxx,o=abc,$REFDN"
+echo "Testing base searching at $XREFDN..."
+$LDAPRSEARCH -S "" -s base -b "$XREFDN" -H $URI1 1.1 >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != $RCODE ; then
+ echo "ldapsearch: unexpected result ($RC)! (referral expected)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing one-level searching at $XREFDN..."
+$LDAPRSEARCH -S "" -s one -b "$XREFDN" -H $URI1 1.1 >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != $RCODE ; then
+ echo "ldapsearch: unexpected result ($RC)! (referral expected)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing subtree searching at $XREFDN..."
+$LDAPRSEARCH -S "" -s sub -b "$XREFDN" -H $URI1 1.1 >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != $RCODE ; then
+ echo "ldapsearch: unexpected result ($RC)! (referral expected)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=$SEARCHOUTPROVIDER
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected LDIF for comparison..."
+$LDIFFILTER < $REFERRALOUT > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test017-syncreplication-refresh b/tests/scripts/test017-syncreplication-refresh
new file mode 100755
index 0000000..7588203
--- /dev/null
+++ b/tests/scripts/test017-syncreplication-refresh
@@ -0,0 +1,356 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+#
+# Test replication:
+# - start provider
+# - start consumer
+# - populate over ldap
+# - perform some modifies and deleted
+# - attempt to modify the consumer (referral)
+# - retrieve database over ldap and compare against expected results
+#
+
+echo "Starting provider slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $SRPROVIDERCONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to create the context prefix entry in the provider..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDEREDCP > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting consumer slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $R1SRCONSUMERCONF > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that consumer slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the provider directory..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDEREDNOCP > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Using ldapmodify to modify provider directory..."
+
+#
+# Do some modifications
+#
+
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example,dc=com
+changetype: modify
+add: drink
+drink: Orange Juice
+-
+delete: sn
+sn: Jones
+-
+add: sn
+sn: Jones
+
+dn: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+replace: drink
+drink: Iced Tea
+drink: Mad Dog 20/20
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: uniquemember
+uniquemember: cn=James A Jones 2, ou=Information Technology Division, ou=People, dc=example,dc=com
+uniquemember: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+-
+add: uniquemember
+uniquemember: cn=Dorothy Stevens, ou=Alumni Association, ou=People, dc=example,dc=com
+uniquemember: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example,dc=com
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: description
+
+dn: cn=Gern Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+cn: Gern Jensen
+sn: Jensen
+uid: gjensen
+title: Chief Investigator, ITD
+postaladdress: ITD $ 535 W. William St $ Ann Arbor, MI 48103
+seealso: cn=All Staff, ou=Groups, dc=example,dc=com
+drink: Coffee
+homepostaladdress: 844 Brown St. Apt. 4 $ Ann Arbor, MI 48104
+description: Very odd
+facsimiletelephonenumber: +1 313 555 7557
+telephonenumber: +1 313 555 8343
+mail: gjensen@mailgw.example.com
+homephone: +1 313 555 8844
+
+dn: ou=Retired, ou=People, dc=example,dc=com
+changetype: add
+objectclass: organizationalUnit
+ou: Retired
+
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+cn: Rosco P. Coltrane
+sn: Coltrane
+uid: rosco
+
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modrdn
+newrdn: cn=Rosco P. Coltrane
+deleteoldrdn: 1
+newsuperior: ou=Retired, ou=People, dc=example,dc=com
+
+dn: cn=James A Jones 2, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: delete
+
+dn: dc=testdomain1,dc=example,dc=com
+changetype: modrdn
+newrdn: dc=itsdomain1
+deleteoldrdn: 1
+
+dn: dc=itsdomain1,dc=example,dc=com
+changetype: modify
+replace: description
+description: Example, Inc. ITS test domain
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Performing modrdn alone on the provider..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+dn: dc=testdomain2,dc=example,dc=com
+changetype: modrdn
+newrdn: dc=itsdomain2
+deleteoldrdn: 1
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Performing modify alone on the provider..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+dn: dc=itsdomain2,dc=example,dc=com
+changetype: modify
+replace: description
+description: Example, Inc. itsdomain2 test domain
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Performing larger modify on the provider..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+replace: objectClass
+objectClass: groupOfNames
+-
+replace: cn
+cn: Alumni Assoc Staff
+-
+replace: description
+description: blablabla
+-
+replace: member
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Try updating the consumer slapd..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI2 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example, dc=com
+changetype: modify
+add: description
+description: This write must fail because directed to a shadow context,
+description: unless the chain overlay is configured appropriately ;)
+
+EOMODS
+
+RC=$?
+
+# expect 10 (LDAP_REFERRAL)...
+if test $RC != 10 ; then
+ echo "ldapmodify should have returned referral ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+OPATTRS="entryUUID creatorsName createTimestamp modifiersName modifyTimestamp"
+
+echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectclass=*)' '*' $OPATTRS > $PROVIDEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ '(objectclass=*)' '*' $OPATTRS > $CONSUMEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo "Filtering provider results..."
+$LDIFFILTER < $PROVIDEROUT > $PROVIDERFLT
+echo "Filtering consumer results..."
+$LDIFFILTER < $CONSUMEROUT > $CONSUMERFLT
+
+echo "Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test018-syncreplication-persist b/tests/scripts/test018-syncreplication-persist
new file mode 100755
index 0000000..0d4a0b8
--- /dev/null
+++ b/tests/scripts/test018-syncreplication-persist
@@ -0,0 +1,548 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+OPATTRS="entryUUID creatorsName createTimestamp modifiersName modifyTimestamp"
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR4
+
+#
+# Test replication:
+# - start provider
+# - start consumer
+# - populate over ldap
+# - perform some modifies and deleted
+# - attempt to modify the consumer (referral or chain)
+# - retrieve database over ldap and compare against expected results
+#
+
+echo "Starting provider slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $SRPROVIDERCONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to create the context prefix entry in the provider..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDEREDCP > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting consumer slapd on TCP/IP port $PORT4..."
+. $CONFFILTER $BACKEND < $P1SRCONSUMERCONF > $CONF4
+$SLAPD -f $CONF4 -h $URI4 -d $LVL > $LOG4 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that consumer slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI4 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the provider directory..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDEREDNOCP > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectclass=*)' '*' $OPATTRS > $PROVIDEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI4 \
+ '(objectclass=*)' '*' $OPATTRS > $CONSUMEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering provider results..."
+$LDIFFILTER < $PROVIDEROUT > $PROVIDERFLT
+echo "Filtering consumer results..."
+$LDIFFILTER < $CONSUMEROUT > $CONSUMERFLT
+
+echo "Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Stopping the provider, sleeping 10 seconds and restarting it..."
+kill -HUP "$PID"
+wait $PID
+sleep 10
+echo "RESTART" >> $LOG1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL >> $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID $CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+echo "Waiting $SLEEP1 seconds for consumer to reconnect..."
+sleep $SLEEP1
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapmodify to modify provider directory..."
+
+#
+# Do some modifications
+#
+
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example,dc=com
+changetype: modify
+add: drink
+drink: Orange Juice
+-
+delete: sn
+sn: Jones
+-
+add: sn
+sn: Jones
+
+dn: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+replace: drink
+drink: Iced Tea
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: uniquemember
+uniquemember: cn=James A Jones 2, ou=Information Technology Division, ou=People, dc=example,dc=com
+uniquemember: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+-
+add: uniquemember
+uniquemember: cn=Dorothy Stevens, ou=Alumni Association, ou=People, dc=example,dc=com
+uniquemember: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example,dc=com
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: description
+
+dn: cn=Gern Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+cn: Gern Jensen
+sn: Jensen
+uid: gjensen
+title: Chief Investigator, ITD
+postaladdress: ITD $ 535 W. William St $ Ann Arbor, MI 48103
+seealso: cn=All Staff, ou=Groups, dc=example,dc=com
+drink: Coffee
+homepostaladdress: 844 Brown St. Apt. 4 $ Ann Arbor, MI 48104
+description: Very odd
+facsimiletelephonenumber: +1 313 555 7557
+facsimiletelephonenumber: +1 313 555 9998
+facsimiletelephonenumber: +1 313 555 9999
+telephonenumber: +1 313 555 8343
+mail: gjensen@mailgw.example.com
+homephone: +1 313 555 8844
+
+# modify attribute with no matching rule (ITS#6458)
+dn: cn=Gern Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+replace: facsimiletelephonenumber
+facsimiletelephonenumber: +1 313 555 9998
+facsimiletelephonenumber: +1 313 555 9999
+
+dn: cn=Gern Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+replace: facsimiletelephonenumber
+facsimiletelephonenumber: +1 313 555 9998
+facsimiletelephonenumber: +1 313 555 9999
+facsimiletelephonenumber: +1 313 555 7557
+
+dn: cn=Gern Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+replace: facsimiletelephonenumber
+facsimiletelephonenumber: +1 313 555 9998
+facsimiletelephonenumber: +1 313 555 9999
+
+dn: ou=Retired, ou=People, dc=example,dc=com
+changetype: add
+objectclass: organizationalUnit
+ou: Retired
+
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+cn: Rosco P. Coltrane
+sn: Coltrane
+uid: rosco
+description: Fat tycoon
+
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modrdn
+newrdn: cn=Rosco P. Coltrane
+deleteoldrdn: 1
+newsuperior: ou=Retired, ou=People, dc=example,dc=com
+
+dn: cn=James A Jones 2, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: delete
+
+dn: dc=testdomain1,dc=example,dc=com
+changetype: modrdn
+newrdn: dc=itsdomain1
+deleteoldrdn: 1
+
+dn: dc=itsdomain1,dc=example,dc=com
+changetype: modify
+replace: description
+description: Example, Inc. ITS test domain
+
+dn: dc=testdomain2,dc=example,dc=com
+changetype: modrdn
+newrdn: dc=itsdomain2
+deleteoldrdn: 1
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldappasswd to change some passwords..."
+$LDAPPASSWD -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ 'cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com' \
+ > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectclass=*)' '*' $OPATTRS > $PROVIDEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI4 \
+ '(objectclass=*)' '*' $OPATTRS > $CONSUMEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering provider results..."
+$LDIFFILTER < $PROVIDEROUT > $PROVIDERFLT
+echo "Filtering consumer results..."
+$LDIFFILTER < $CONSUMEROUT > $CONSUMERFLT
+
+echo "Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Stopping consumer to test recovery..."
+kill -HUP $CONSUMERPID
+wait $CONSUMERPID
+
+echo "Modifying more entries on the provider..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Rosco P. Coltrane, ou=Retired, ou=People, dc=example,dc=com
+changetype: delete
+
+dn: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+add: drink
+drink: Mad Dog 20/20
+
+dn: cn=Rosco P. Coltrane, ou=Retired, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+sn: Coltrane
+uid: rosco
+cn: Rosco P. Coltrane
+
+dn: dc=itsdomain2,dc=example,dc=com
+changetype: modify
+replace: description
+description: Example, Inc. itsdomain2 test domain
+
+# rename with a newly added newSuperior while the consumer is down (ITS#6472)
+dn: ou=New Branch,dc=example,dc=com
+changetype: add
+objectClass: organizationalUnit
+ou: New Branch
+
+dn: cn=Dorothy Stevens, ou=Alumni Association, ou=People, dc=example,dc=com
+changetype: modrdn
+newrdn: cn=Dorothy Stevens
+deleteoldrdn: 0
+newsuperior: ou=New Branch,dc=example,dc=com
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Restarting consumer..."
+echo "RESTART" >> $LOG4
+$SLAPD -f $CONF4 -h $URI4 -d $LVL >> $LOG4 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$PID $CONSUMERPID"
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectclass=*)' '*' $OPATTRS > $PROVIDEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI4 \
+ '(objectclass=*)' '*' $OPATTRS > $CONSUMEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering provider results..."
+$LDIFFILTER < $PROVIDEROUT > $PROVIDERFLT
+echo "Filtering consumer results..."
+$LDIFFILTER < $CONSUMEROUT > $CONSUMERFLT
+
+echo "Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+if test ! $BACKLDAP = "ldapno" ; then
+ echo "Try updating the consumer slapd..."
+ $LDAPMODIFY -v -D "$MANAGERDN" -H $URI4 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example, dc=com
+changetype: modify
+add: description
+description: This write must fail because directed to a shadow context,
+description: unless the chain overlay is configured appropriately ;)
+
+EOMODS
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ # ITS#4964
+ echo "Trying to change some passwords on the consumer..."
+ $LDAPPASSWD -D "$MANAGERDN" -H $URI4 -w $PASSWD \
+ 'cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com' \
+ > $TESTOUT 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+fi
+
+# Testing a cancel exop (should go in its own testcase)
+$LDAPSEARCH -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ -e '!cancel' \
+ '(objectclass=*)' '*' $OPATTRS > $TESTOUT 2>&1
+RC=$?
+# cancelled operation returns -1, so no point of checking return code, either
+# it's cancelled or we get stuck forever
+
+echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectclass=*)' '*' $OPATTRS > $PROVIDEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI4 \
+ '(objectclass=*)' '*' $OPATTRS > $CONSUMEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering provider results..."
+$LDIFFILTER < $PROVIDEROUT > $PROVIDERFLT
+echo "Filtering consumer results..."
+$LDIFFILTER < $CONSUMEROUT > $CONSUMERFLT
+
+echo "Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test019-syncreplication-cascade b/tests/scripts/test019-syncreplication-cascade
new file mode 100755
index 0000000..1501823
--- /dev/null
+++ b/tests/scripts/test019-syncreplication-cascade
@@ -0,0 +1,487 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2 $DBDIR3 $DBDIR4 $DBDIR5 $DBDIR6
+
+#
+# Test replication:
+# - start provider
+# - start consumer
+# - populate over ldap
+# - perform some modifies and deleted
+# - retrieve database over ldap and compare against expected results
+#
+
+echo "Starting provider slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $SRPROVIDERCONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd (pid=$PID) is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to create the context prefix entry in the provider..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDEREDCP > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting R1 consumer slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $R1SRCONSUMERCONF > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMER R1 PID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that R1 consumer slapd (pid=$CONSUMERPID) is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for R1 slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting R2 consumer slapd on TCP/IP port $PORT3..."
+. $CONFFILTER $BACKEND < $R2SRCONSUMERCONF > $CONF3
+$SLAPD -f $CONF3 -h $URI3 -d $LVL > $LOG3 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMER R2 PID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that R2 consumer slapd (pid=$CONSUMERPID) is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI3 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for R2 consumer slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting P1 consumer slapd on TCP/IP port $PORT4..."
+. $CONFFILTER $BACKEND < $P1SRCONSUMERCONF > $CONF4
+$SLAPD -f $CONF4 -h $URI4 -d $LVL > $LOG4 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMER P1 PID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that P1 consumer slapd (pid=$CONSUMERPID) is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI4 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for P1 consumer slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting P2 consumer slapd on TCP/IP port $PORT5..."
+. $CONFFILTER $BACKEND < $P2SRCONSUMERCONF > $CONF5
+$SLAPD -f $CONF5 -h $URI5 -d $LVL > $LOG5 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMER P2 PID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that P2 consumer slapd (pid=$CONSUMERPID) is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI5 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for P2 consumer slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting P3 consumer slapd on TCP/IP port $PORT6..."
+. $CONFFILTER $BACKEND < $P3SRCONSUMERCONF > $CONF6
+$SLAPD -f $CONF6 -h $URI6 -d $LVL > $LOG6 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMER P3 PID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that P3 consumer slapd (pid=$CONSUMERPID) is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI6 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for P3 consumer slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the provider directory..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDEREDNOCP > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP2 seconds for syncrepl to receive changes..."
+sleep $SLEEP2
+
+echo "Using ldapmodify to modify provider directory..."
+
+#
+# Do some modifications
+#
+
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example,dc=com
+changetype: modify
+add: drink
+drink: Orange Juice
+-
+delete: sn
+sn: Jones
+-
+add: sn
+sn: Jones
+
+dn: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+replace: drink
+drink: Iced Tea
+drink: Mad Dog 20/20
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: uniquemember
+uniquemember: cn=James A Jones 2, ou=Information Technology Division, ou=People, dc=example,dc=com
+uniquemember: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+-
+add: uniquemember
+uniquemember: cn=Dorothy Stevens, ou=Alumni Association, ou=People, dc=example,dc=com
+uniquemember: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example,dc=com
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: description
+
+dn: cn=Gern Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+cn: Gern Jensen
+sn: Jensen
+uid: gjensen
+title: Chief Investigator, ITD
+postaladdress: ITD $ 535 W. William St $ Ann Arbor, MI 48103
+seealso: cn=All Staff, ou=Groups, dc=example,dc=com
+drink: Coffee
+homepostaladdress: 844 Brown St. Apt. 4 $ Ann Arbor, MI 48104
+description: Very odd
+facsimiletelephonenumber: +1 313 555 7557
+telephonenumber: +1 313 555 8343
+mail: gjensen@mailgw.example.com
+homephone: +1 313 555 8844
+
+dn: ou=Retired, ou=People, dc=example,dc=com
+changetype: add
+objectclass: organizationalUnit
+ou: Retired
+
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+cn: Rosco P. Coltrane
+sn: Coltrane
+uid: rosco
+
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modrdn
+newrdn: cn=Rosco P. Coltrane
+deleteoldrdn: 1
+newsuperior: ou=Retired, ou=People, dc=example,dc=com
+
+dn: cn=James A Jones 2, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: delete
+
+dn: dc=testdomain1,dc=example,dc=com
+changetype: modrdn
+newrdn: dc=itsdomain1
+deleteoldrdn: 1
+
+dn: dc=itsdomain1,dc=example,dc=com
+changetype: modify
+replace: description
+description: Example, Inc. ITS test domain
+
+dn: dc=testdomain2,dc=example,dc=com
+changetype: modrdn
+newrdn: dc=itsdomain2
+deleteoldrdn: 1
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP2 seconds for syncrepl to receive changes..."
+sleep $SLEEP2
+
+echo "Performing modify alone on provider..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+dn: dc=itsdomain2,dc=example,dc=com
+changetype: modify
+replace: description
+description: Example, Inc. itsdomain2 test domain
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP2 seconds for syncrepl to receive changes..."
+sleep $SLEEP2
+
+echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' '*' entryCSN > $PROVIDEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the R1 consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ '(objectClass=*)' '*' entryCSN > $SERVER2OUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at R1 consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the R2 consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI3 \
+ '(objectClass=*)' '*' entryCSN > $SERVER3OUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at R2 consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the P1 consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI4 \
+ '(objectClass=*)' '*' entryCSN > $SERVER4OUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at P1 consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the P2 consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI5 \
+ '(objectClass=*)' '*' entryCSN > $SERVER5OUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at P2 consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the P3 consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI6 \
+ '(objectClass=*)' '*' entryCSN > $SERVER6OUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at P3 consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo "Filtering provider ldapsearch results..."
+$LDIFFILTER < $PROVIDEROUT > $PROVIDERFLT
+echo "Filtering R1 consumer ldapsearch results..."
+$LDIFFILTER < $SERVER2OUT > $SERVER2FLT
+echo "Filtering R2 consumer ldapsearch results..."
+$LDIFFILTER < $SERVER3OUT > $SERVER3FLT
+echo "Filtering P1 consumer ldapsearch results..."
+$LDIFFILTER < $SERVER4OUT > $SERVER4FLT
+echo "Filtering P2 consumer ldapsearch results..."
+$LDIFFILTER < $SERVER5OUT > $SERVER5FLT
+echo "Filtering P3 consumer ldapsearch results..."
+$LDIFFILTER < $SERVER6OUT > $SERVER6FLT
+
+echo "Comparing retrieved entries from provider and R1 consumer..."
+$CMP $PROVIDERFLT $SERVER2FLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and R1 consumer databases differ"
+ exit 1
+fi
+
+echo "Comparing retrieved entries from provider and R2 consumer..."
+$CMP $PROVIDERFLT $SERVER3FLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and R2 consumer databases differ"
+ exit 1
+fi
+
+echo "Comparing retrieved entries from provider and P1 consumer..."
+$CMP $PROVIDERFLT $SERVER4FLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and P1 consumer databases differ"
+ exit 1
+fi
+
+echo "Comparing retrieved entries from provider and P2 consumer..."
+$CMP $PROVIDERFLT $SERVER5FLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and P2 consumer databases differ"
+ exit 1
+fi
+
+echo "Comparing retrieved entries from provider and P3 consumer..."
+$CMP $PROVIDERFLT $SERVER6FLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and P3 consumer databases differ"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test020-proxycache b/tests/scripts/test020-proxycache
new file mode 100755
index 0000000..af4cc9e
--- /dev/null
+++ b/tests/scripts/test020-proxycache
@@ -0,0 +1,643 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+PCACHETTL=${PCACHETTL-"1m"}
+PCACHENTTL=${PCACHENTTL-"1m"}
+PCACHESTTL=${PCACHESTTL-"1m"}
+PCACHE_ENTRY_LIMIT=${PCACHE_ENTRY_LIMIT-"6"}
+PCACHE_CCPERIOD=${PCACHE_CCPERIOD-"2"}
+PCACHETTR=${PCACHETTR-"2"}
+PCACHEBTTR=${PCACHEBTTR-"5"}
+
+. $SRCDIR/scripts/defines.sh
+
+if test $PROXYCACHE = pcacheno; then
+ echo "Proxy cache overlay not available, test skipped"
+ exit 0
+fi
+
+if test $BACKLDAP = "ldapno" ; then
+ echo "LDAP backend not available, test skipped"
+ exit 0
+fi
+
+if test $BACKEND = ldif ; then
+ # The (mail=example.com*) queries hit a sizelimit, so which
+ # entry is returned depends on the ordering in the backend.
+ echo "Test does not support $BACKEND backend, test skipped"
+ exit 0
+fi
+
+if test $BACKEND = wt ; then
+ echo "Test does not support $BACKEND backend, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+# Test proxy caching:
+# - start provider
+# - start proxy cache
+# - populate provider
+# - perform first set of searches at the proxy
+# - verify cacheability
+# - perform second set of searches at the proxy
+# - verify answerability
+
+echo "Starting provider slapd on TCP/IP port $PORT1..."
+. $CONFFILTER < $CACHEPROVIDERCONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the provider directory..."
+$LDAPADD -x -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDERED > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting proxy cache on TCP/IP port $PORT2..."
+. $CONFFILTER < $PROXYCACHECONF | sed \
+ -e "s/@TTL@/${PCACHETTL}/" \
+ -e "s/@NTTL@/${PCACHENTTL}/" \
+ -e "s/@STTL@/${PCACHENTTL}/" \
+ -e "s/@TTR@/${PCACHETTR}/" \
+ -e "s/@ENTRY_LIMIT@/${PCACHE_ENTRY_LIMIT}/" \
+ -e "s/@CCPERIOD@/${PCACHE_CCPERIOD}/" \
+ -e "s/@BTTR@/${PCACHEBTTR}/" \
+ > $CONF2
+
+$SLAPD -f $CONF2 -h $URI2 -d $LVL -d pcache > $LOG2 2>&1 &
+CACHEPID=$!
+if test $WAIT != 0 ; then
+ echo CACHEPID $CACHEPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CACHEPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that proxy slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+echo "Making queries on the proxy cache..."
+CNT=0
+
+CNT=`expr $CNT + 1`
+FILTER="(sn=Jon)"
+echo "Query $CNT: filter:$FILTER attrs:all (expect nothing)"
+echo "# Query $CNT: filter:$FILTER attrs:all (expect nothing)" >> $SEARCHOUT
+$LDAPSEARCH -x -S "" -b "$BASEDN" -H $URI2 \
+ "$FILTER" >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# ITS#4491, if debug messages are unavailable, we can't verify the tests.
+grep "query template" $LOG2 > /dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "Debug messages unavailable, remaining test skipped..."
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS && wait
+ exit 0
+fi
+
+CNT=`expr $CNT + 1`
+FILTER="(|(cn=*Jon*)(sn=Jon*))"
+ATTRS="cn sn title uid"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
+$LDAPSEARCH -x -S "" -b "$BASEDN" -H $URI2 \
+ "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+CNT=`expr $CNT + 1`
+FILTER="(sn=Smith*)"
+ATTRS="cn sn uid"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+CNT=`expr $CNT + 1`
+FILTER="(sn=Doe*)"
+ATTRS="cn sn title uid"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+CNT=`expr $CNT + 1`
+FILTER="(uid=johnd)"
+ATTRS="mail postaladdress telephonenumber cn uid"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+CNT=`expr $CNT + 1`
+FILTER="(mail=*@mail.alumni.example.com)"
+ATTRS="cn sn title uid"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+CNT=`expr $CNT + 1`
+FILTER="(mail=*)"
+ATTRS="cn sn title uid"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+CNT=`expr $CNT + 1`
+FILTER="(mail=*example.com)"
+ATTRS="cn sn title uid"
+USERDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+UPASSWD="bjorn"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ -D "$USERDN" -w "$UPASSWD" \
+ "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+case $RC in
+0)
+ echo "ldapsearch should have failed!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+4)
+ echo "ldapsearch failed ($RC)"
+ ;;
+*)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+CNT=`expr $CNT + 1`
+FILTER="(uid=b*)"
+ATTRS="mail"
+USERDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+UPASSWD="bjorn"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ -D "$USERDN" -w "$UPASSWD" \
+ "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+case $RC in
+0)
+ echo "ldapsearch should have failed!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+4)
+ echo "ldapsearch failed ($RC)"
+ ;;
+*)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+CNT=`expr $CNT + 1`
+FILTER="(|(cn=All Staff)(sn=All Staff))"
+ATTRS="sn cn title uid undefinedAttr"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+FIRST=$CNT
+
+# queries 2-6,8-10 are cacheable
+CACHEABILITY=0111110111
+grep CACHEABLE $LOG2 | awk '
+ /NOT CACHEABLE/{printf "Query %d not cacheable\n",NR}
+ /QUERY CACHEABLE/{printf "Query %d cacheable\n",NR}'
+CACHED=`grep CACHEABLE $LOG2 | awk '
+ /NOT CACHEABLE/{printf "0"}
+ /QUERY CACHEABLE/{printf "1"}'`
+
+if test "$CACHEABILITY" = "$CACHED" ; then
+ echo "Successfully verified cacheability"
+else
+ echo "Error in verifying cacheability"
+ echo "$CACHED"
+ echo "$CACHEABILITY"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+CNT=`expr $CNT + 1`
+FILTER="(|(cn=*Jones)(sn=Jones))"
+ATTRS="cn sn title uid"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
+$LDAPSEARCH -x -S "" -b "$BASEDN" -H $URI2 \
+ "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+CNT=`expr $CNT + 1`
+FILTER="(sn=Smith)"
+ATTRS="cn sn title uid"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+CNT=`expr $CNT + 1`
+FILTER="(uid=bjorn)"
+ATTRS="mail postaladdress telephonenumber cn uid"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+CNT=`expr $CNT + 1`
+FILTER="(mail=jaj@mail.alumni.example.com)"
+ATTRS="cn sn title uid"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+CNT=`expr $CNT + 1`
+FILTER="(mail=*example.com)"
+ATTRS="cn sn title uid"
+USERDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+UPASSWD="bjorn"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ -D "$USERDN" -w "$UPASSWD" \
+ "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+case $RC in
+0)
+ echo "ldapsearch should have failed!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+4)
+ echo "ldapsearch failed ($RC)"
+ ;;
+*)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+CNT=`expr $CNT + 1`
+FILTER="(uid=b*)"
+ATTRS="mail"
+USERDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+UPASSWD="bjorn"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ -D "$USERDN" -w "$UPASSWD" \
+ "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+case $RC in
+0)
+ echo "ldapsearch should have failed!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+4)
+ echo "ldapsearch failed ($RC)"
+ ;;
+*)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+CNT=`expr $CNT + 1`
+FILTER="(|(cn=All Staff)(sn=All Staff))"
+ATTRS="sn cn title uid undefinedAttr"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#queries 11-13,16-17 are answerable, 14-15 are not
+#actually, 14 would be answerable, but since 8 made mail=*example.com
+#not answerable because of sizelimit, queries contained in it are no longer
+#answerable as well
+ANSWERABILITY=1110011
+grep ANSWERABLE $LOG2 | awk "BEGIN {FIRST=$FIRST}"'
+ /NOT ANSWERABLE/{if (NR > FIRST) printf "Query %d not answerable\n",NR}
+ /QUERY ANSWERABLE/{if (NR > FIRST) printf "Query %d answerable\n",NR}'
+ANSWERED=`grep ANSWERABLE $LOG2 | awk "BEGIN {FIRST=$FIRST}"'
+ /NOT ANSWERABLE/{if (NR > FIRST) printf "0"}
+ /QUERY ANSWERABLE/{if (NR > FIRST) printf "1"}'`
+
+if test "$ANSWERABILITY" = "$ANSWERED" ; then
+ echo "Successfully verified answerability"
+else
+ echo "Error in verifying answerability"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s ldif=a < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif..."
+$LDIFFILTER -s ldif=a < $PROXYCACHEOUT > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo ""
+echo "Testing cache refresh"
+
+CNT=`expr $CNT + 1`
+FILTER="(&(objectclass=person)(uid=dots))"
+ATTRS="cn mail telephonenumber"
+echo "Query $CNT: filter:$FILTER attrs:$ATTRS"
+echo "# Query $CNT: filter:$FILTER attrs:$ATTRS" >> $SEARCHOUT
+$LDAPSEARCH -x -S "" -b "$BASEDN" -H $URI2 \
+ "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -x -D "$MANAGERDN" -H $URI1 -w $PASSWD <<EOF \
+ > /dev/null 2>&1
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+replace: mail
+mail: dots@admin.example2.com
+-
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+SLEEP=`expr $PCACHETTR + $PCACHE_CCPERIOD + 1`
+echo "Waiting $SLEEP seconds for cache to refresh"
+
+sleep $SLEEP
+
+echo "Checking entry again"
+$LDAPSEARCH -x -S "" -b "$BASEDN" -H $URI2 \
+ "$FILTER" $ATTRS >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+grep "^mail: dots@admin" $SEARCHOUT > /dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "Refresh failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS && wait
+ exit 1
+fi
+
+echo ""
+echo "Testing Bind caching"
+
+CNT=`expr $CNT + 1`
+USERDN="cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com"
+UPASSWD="jaj"
+echo "Query $CNT: $USERDN"
+echo "# Query $CNT: $USERDN" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "" -s base -H $URI2 \
+ -D "$USERDN" -w "$UPASSWD" >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+grep "CACHING BIND" $LOG2 > /dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "Refresh failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS && wait
+ exit 1
+fi
+
+CNT=`expr $CNT + 1`
+USERDN="cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com"
+UPASSWD="jaj"
+echo "Query $CNT: (Bind should be cached)"
+echo "# Query $CNT: (Bind should be cached)" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "" -s base -H $URI2 \
+ -D "$USERDN" -w "$UPASSWD" >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+grep "CACHED BIND" $LOG2 > /dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "Refresh failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS && wait
+ exit 1
+fi
+
+echo ""
+echo "Testing pwdModify"
+$LDAPPASSWD -H $URI2 \
+ -D "$MANAGERDN" -w "$PASSWD" -s newpw "$USERDN" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldappasswd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+RC=`grep "CACH.* BIND" $LOG2 | wc -l`
+if test $RC != 3 ; then
+ echo "ldappasswd didn't update the cache"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS && wait
+ exit 1
+fi
+
+CNT=`expr $CNT + 1`
+USERDN="cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com"
+UPASSWD=newpw
+echo "Query $CNT: (Bind should be cached)"
+echo "# Query $CNT: (Bind should be cached)" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "" -s base -H $URI2 \
+ -D "$USERDN" -w "$UPASSWD" >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+RC=`grep "CACH.* BIND" $LOG2 | wc -l`
+if test $RC != 4 ; then
+ echo "Bind wasn't answered from cache"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS && wait
+ exit 1
+fi
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test021-certificate b/tests/scripts/test021-certificate
new file mode 100755
index 0000000..9be5c6a
--- /dev/null
+++ b/tests/scripts/test021-certificate
@@ -0,0 +1,325 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+#echo $SLAPADD -f $CONF1 -l $LDIFORDERED
+$SLAPADD -f $CONF1 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+#valgrind -v --gdb-attach=yes --logfile=info --num-callers=16 --leak-check=yes --leak-resolution=high $SLAPD -f $CONF1 -h $URI1 -d $LVL </dev/tty > $LOG1 2>&1 &
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+echo "Testing certificate handling..."
+
+sleep 1
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Add certificates..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+version: 1
+
+# LEADING COMMENT AND WHITE SPACE
+
+# should use certificationAuthority instead of extensibleObject
+dn: dc=example,dc=com
+changetype: modify
+add: objectClass
+objectClass: extensibleObject
+-
+add: cAcertificate;binary
+cAcertificate;binary::
+ MIIDVDCCAr2gAwIBAgIBADANBgkqhkiG9w0BAQQFADB3MQswCQYDVQQGEwJVUzET
+ MBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwg
+ THRkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhh
+ bXBsZS5jb20wHhcNMDMxMDE3MTYzMDQxWhcNMDQxMDE2MTYzMDQxWjB3MQswCQYD
+ VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAg
+ RXhhbXBsZSwgTHRkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJ
+ ARYOY2FAZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANlj
+ UGxiisAzEiALukzt3Gj/24MRw1J0AZx6GncXLhpNJsAFyA0bYZdAzgvydKeq/uX0
+ i5o/4Byc3G71XAAcbJZxDPtrLwpDAdMNOBvKV2r67yTgnpatFLfGRt/FWazj5EbF
+ YkorWWTe+4eEBd9VPzebHdIm+DPHipUfIAzRoNejAgMBAAGjge8wgewwHQYDVR0O
+ BBYEFEtvIRo2JNKQ+UOwU0ctfeHA5pgjMIGhBgNVHSMEgZkwgZaAFEtvIRo2JNKQ
+ +UOwU0ctfeHA5pgjoXukeTB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZv
+ cm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwgTHRkLjETMBEGA1UEAxMK
+ RXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhhbXBsZS5jb22CAQAwDAYD
+ VR0TBAUwAwEB/zAZBgNVHREEEjAQgQ5jYUBleGFtcGxlLmNvbTANBgkqhkiG9w0B
+ AQQFAAOBgQCgXD/+28El3GXi/uxMNEKqtnIhQdTnNU4il0fZ6pcmHPFC+61Bddow
+ 90ZZZh5Gbg5ZBxFRhDXN8K/fix3ewRSjASt40dGlEODkE+FsLMt04sYl6kX7RGKg
+ 9a46DkeG+uzZnN/3252uCgh+rjNMFAglueUTERv3EtUB1iXEoU3GyA==
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+add: objectClass
+objectClass: strongAuthenticationUser
+-
+add: userCertificate;binary
+userCertificate;binary::
+ MIIDazCCAtSgAwIBAgIBAjANBgkqhkiG9w0BAQQFADB3MQswCQYDVQQGEwJVUzET
+ MBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwg
+ THRkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhh
+ bXBsZS5jb20wHhcNMDMxMDE3MTYzMzE5WhcNMDQxMDE2MTYzMzE5WjB+MQswCQYD
+ VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAg
+ RXhhbXBsZSwgTHRkLjEYMBYGA1UEAxMPVXJzdWxhIEhhbXBzdGVyMR8wHQYJKoZI
+ hvcNAQkBFhB1aGFtQGV4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
+ iQKBgQDuxgp5ELV9LmhxWMpV7qc4028QQT3+zzFDXhruuXE7ji2n3S3ea8bOwDtJ
+ h+qnsDe561DhHHHlgIjMKCiDEizYMpxvJPYEXmvp0huRkMgpKZgmel95BSkt6TYm
+ J0erS3aoimOHLEFimmnTLolNRMiWqNBvqwobx940PGwUWEePKQIDAQABo4H/MIH8
+ MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENl
+ cnRpZmljYXRlMB0GA1UdDgQWBBSjI94TbBmuDEeUUOiC37EK0Uf0XjCBoQYDVR0j
+ BIGZMIGWgBRLbyEaNiTSkPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1UEBhMCVVMx
+ EzARBgNVBAgTCkNhbGlmb3JuaWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUs
+ IEx0ZC4xEzARBgNVBAMTCkV4YW1wbGUgQ0ExHTAbBgkqhkiG9w0BCQEWDmNhQGV4
+ YW1wbGUuY29tggEAMA0GCSqGSIb3DQEBBAUAA4GBAIgUcARb3OlWYNbmr1nmqESu
+ xLn16uqI1Ot6WkcICvpkdQ+Bo+R9AP05xpoXocZtKdNvBu3FNxB/jFkiOcLU2lX7
+ Px1Ijnsjh60qVRy9HOsHCungIKlGcnXLKHmKu0y//5jds/HnaJsGcHI5JRG7CBJb
+ W+wrwge3trJ1xHJI8prN
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+add: objectClass
+objectClass: strongAuthenticationUser
+-
+add: userCertificate;binary
+userCertificate;binary::
+ MIIDcDCCAtmgAwIBAgIBATANBgkqhkiG9w0BAQQFADB3MQswCQYDVQQGEwJVUzET
+ MBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwg
+ THRkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhh
+ bXBsZS5jb20wHhcNMDMxMDE3MTYzMTQwWhcNMDQxMDE2MTYzMTQwWjCBgjELMAkG
+ A1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExHzAdBgNVBAoTFk9wZW5MREFQ
+ IEV4YW1wbGUsIEx0ZC4xHTAbBgNVBAMUFEplbm5pZmVyICJKZW4iIFNtaXRoMR4w
+ HAYJKoZIhvcNAQkBFg9qZW5AZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD
+ gY0AMIGJAoGBANUgO8cP/SjqgCVxxsRYv36AP0+QL81iEkGvR4gG6jbtDDBdVYDC
+ YbS2oKKNJ5e99NxGMIjOYfmKcAwmkV46IhdzUtkutgjHEG9vl5ajSwc1KSsbTMTy
+ NtuG3k5k02JYFbP+FrGyUE8iPqK4+i7mVjW4bh/MBCHW88FptnpDJiuHAgMBAAGj
+ gf8wgfwwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0
+ ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFEdo4jpxCQXJ1sh/E1O3ZBkLTbHkMIGh
+ BgNVHSMEgZkwgZaAFEtvIRo2JNKQ+UOwU0ctfeHA5pgjoXukeTB3MQswCQYDVQQG
+ EwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhh
+ bXBsZSwgTHRkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYO
+ Y2FAZXhhbXBsZS5jb22CAQAwDQYJKoZIhvcNAQEEBQADgYEAFpHsQUtSZQzmm9k2
+ Vrfs0h7tdkWF3LcHzHk4a/t3k4EXcqlHBxh4f0tmb4XNP9QupRgm6ggr8t3Rq0Vt
+ T8k50x4C7oE8HwZuEEB4FM7S1Zig3dfeJ8MJgdaLqt5/U9Ip/hZdzG2dsUsIceH/
+ 5MCKLu9bGJUjsKnGdm/KpaNwaNo=
+
+dn: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+add: userCertificate;binary
+userCertificate;binary::
+ MIIDjDCCAvWgAwIBAgIBAzANBgkqhkiG9w0BAQQFADB3MQswCQYDVQQGEwJVUzET
+ MBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwg
+ THRkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhh
+ bXBsZS5jb20wHhcNMDMxMDE3MTYzNTM1WhcNMDQxMDE2MTYzNTM1WjCBnjELMAkG
+ A1UEBhMCVVMxETAPBgNVBAgTCE1pY2hpZ2FuMR8wHQYDVQQKExZPcGVuTERBUCBF
+ eGFtcGxlLCBMdGQuMRswGQYDVQQLExJBbHVtbmkgQXNzb2ljYXRpb24xEjAQBgNV
+ BAMTCUplbiBTbWl0aDEqMCgGCSqGSIb3DQEJARYbamVuQG1haWwuYWx1bW5pLmV4
+ YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDpnXWAL0VkROGO
+ 1Rg8J3u6F4F7yMqQCbUMsV9rxQisYj45+pmqiHV5urogvT4MGD6eLNFZKBn+0KRn
+ i++uu7gbartzpmBaHOlzRII9ZdVMFfrT2xYNgAlkne6pb6IZIN9UONuH/httENCD
+ J5WEpjZ48D1Lrml/HYO/W+SAMkpEqQIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJ
+ YIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1Ud
+ DgQWBBTB2saht/od/nis76b9m+pjxfhSPjCBoQYDVR0jBIGZMIGWgBRLbyEaNiTS
+ kPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0ZC4xEzARBgNVBAMT
+ CkV4YW1wbGUgQ0ExHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAMA0G
+ CSqGSIb3DQEBBAUAA4GBAIoGPc/AS0cNkMRDNoMIzcFdF9lONMduKBiSuFvv+x8n
+ Cek+LUdXxF59V2NPKh2V5gFh5xbAchyv6FVBnpVtPdB5akCr5tdFQhuBLUXXDk/t
+ THGpIWt7OAjEmpuMzsz3GUB8Zf9rioHOs1DMw+GpzWdnFITxXhAqEDc3quqPrpxZ
+-
+delete: userCertificate;binary
+userCertificate;binary::
+ MIIDcDCCAtmgAwIBAgIBATANBgkqhkiG9w0BAQQFADB3MQswCQYDVQQGEwJVUzET
+ MBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwg
+ THRkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhh
+ bXBsZS5jb20wHhcNMDMxMDE3MTYzMTQwWhcNMDQxMDE2MTYzMTQwWjCBgjELMAkG
+ A1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExHzAdBgNVBAoTFk9wZW5MREFQ
+ IEV4YW1wbGUsIEx0ZC4xHTAbBgNVBAMUFEplbm5pZmVyICJKZW4iIFNtaXRoMR4w
+ HAYJKoZIhvcNAQkBFg9qZW5AZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD
+ gY0AMIGJAoGBANUgO8cP/SjqgCVxxsRYv36AP0+QL81iEkGvR4gG6jbtDDBdVYDC
+ YbS2oKKNJ5e99NxGMIjOYfmKcAwmkV46IhdzUtkutgjHEG9vl5ajSwc1KSsbTMTy
+ NtuG3k5k02JYFbP+FrGyUE8iPqK4+i7mVjW4bh/MBCHW88FptnpDJiuHAgMBAAGj
+ gf8wgfwwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0
+ ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFEdo4jpxCQXJ1sh/E1O3ZBkLTbHkMIGh
+ BgNVHSMEgZkwgZaAFEtvIRo2JNKQ+UOwU0ctfeHA5pgjoXukeTB3MQswCQYDVQQG
+ EwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhh
+ bXBsZSwgTHRkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYO
+ Y2FAZXhhbXBsZS5jb22CAQAwDQYJKoZIhvcNAQEEBQADgYEAFpHsQUtSZQzmm9k2
+ Vrfs0h7tdkWF3LcHzHk4a/t3k4EXcqlHBxh4f0tmb4XNP9QupRgm6ggr8t3Rq0Vt
+ T8k50x4C7oE8HwZuEEB4FM7S1Zig3dfeJ8MJgdaLqt5/U9Ip/hZdzG2dsUsIceH/
+ 5MCKLu9bGJUjsKnGdm/KpaNwaNo=
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+replace: userCertificate;binary
+userCertificate;binary::
+ MIIDjDCCAvWgAwIBAgIBAzANBgkqhkiG9w0BAQQFADB3MQswCQYDVQQGEwJVUzET
+ MBEGA1UECBMKQ2FsaWZvcm5pYTEfMB0GA1UEChMWT3BlbkxEQVAgRXhhbXBsZSwg
+ THRkLjETMBEGA1UEAxMKRXhhbXBsZSBDQTEdMBsGCSqGSIb3DQEJARYOY2FAZXhh
+ bXBsZS5jb20wHhcNMDMxMDE3MTYzNTM1WhcNMDQxMDE2MTYzNTM1WjCBnjELMAkG
+ A1UEBhMCVVMxETAPBgNVBAgTCE1pY2hpZ2FuMR8wHQYDVQQKExZPcGVuTERBUCBF
+ eGFtcGxlLCBMdGQuMRswGQYDVQQLExJBbHVtbmkgQXNzb2ljYXRpb24xEjAQBgNV
+ BAMTCUplbiBTbWl0aDEqMCgGCSqGSIb3DQEJARYbamVuQG1haWwuYWx1bW5pLmV4
+ YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDpnXWAL0VkROGO
+ 1Rg8J3u6F4F7yMqQCbUMsV9rxQisYj45+pmqiHV5urogvT4MGD6eLNFZKBn+0KRn
+ i++uu7gbartzpmBaHOlzRII9ZdVMFfrT2xYNgAlkne6pb6IZIN9UONuH/httENCD
+ J5WEpjZ48D1Lrml/HYO/W+SAMkpEqQIDAQABo4H/MIH8MAkGA1UdEwQCMAAwLAYJ
+ YIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1Ud
+ DgQWBBTB2saht/od/nis76b9m+pjxfhSPjCBoQYDVR0jBIGZMIGWgBRLbyEaNiTS
+ kPlDsFNHLX3hwOaYI6F7pHkwdzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlm
+ b3JuaWExHzAdBgNVBAoTFk9wZW5MREFQIEV4YW1wbGUsIEx0ZC4xEzARBgNVBAMT
+ CkV4YW1wbGUgQ0ExHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tggEAMA0G
+ CSqGSIb3DQEBBAUAA4GBAIoGPc/AS0cNkMRDNoMIzcFdF9lONMduKBiSuFvv+x8n
+ Cek+LUdXxF59V2NPKh2V5gFh5xbAchyv6FVBnpVtPdB5akCr5tdFQhuBLUXXDk/t
+ THGpIWt7OAjEmpuMzsz3GUB8Zf9rioHOs1DMw+GpzWdnFITxXhAqEDc3quqPrpxZ
+-
+delete: userCertificate;binary
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo 'Using ldapsearch to retrieve (userCertificate;binary=*) ...'
+echo "# (userCertificate;binary=*)" > $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(userCertificate;binary=*)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo 'Using ldapsearch to retrieve (cAcertificate=*) ...'
+echo "# (cAcertificate=*)" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(cAcertificate=*)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+SNAI='2$EMAIL=ca@example.com,CN=Example CA,O=Openldap Example\5C, Ltd.,ST=California,C=US'
+
+echo 'Using ldapsearch to retrieve (userCertificate=serialNumberAndIssuer) [old format] ...'
+echo "# (userCertificate=$SNAI)" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "(userCertificate=$SNAI)" >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+SNAI='{ serialNumber 2, issuer "EMAIL=ca@example.com,CN=Example CA,O=Openldap Example\5C, Ltd.,ST=California,C=US" }'
+
+echo 'Using ldapsearch to retrieve (userCertificate=serialNumberAndIssuer) [new format] ...'
+echo "# (userCertificate=$SNAI)" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "(userCertificate=$SNAI)" >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+SNAI='3$EMAIL=ca@example.com,CN=Example CA,O=Openldap Example\5C, Ltd.,ST=California,C=US'
+
+echo 'Using ldapsearch to retrieve (userCertificate:certificateExactMatch:=serialNumberAndIssuer) [old format] ...'
+echo "# (userCertificate:certificateExactMatch:=$SNAI)" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "(userCertificate:certificateExactMatch:=$SNAI)" >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+SNAI='{ issuer "EMAIL=ca@example.com,CN=Example CA,O=Openldap Example\5C, Ltd.,ST=California,C=US", serialNumber 3 }'
+
+echo 'Using ldapsearch to retrieve (userCertificate:certificateExactMatch:=serialNumberAndIssuer) [new format]...'
+echo "# (userCertificate:certificateExactMatch:=$SNAI)" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "(userCertificate:certificateExactMatch:=$SNAI)" >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=$CERTIFICATETLS
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - certificate operations did not complete correctly"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test022-ppolicy b/tests/scripts/test022-ppolicy
new file mode 100755
index 0000000..e1281e7
--- /dev/null
+++ b/tests/scripts/test022-ppolicy
@@ -0,0 +1,778 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $PPOLICY = ppolicyno; then
+ echo "Password policy overlay not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $PPOLICYCONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+USER="uid=nd, ou=People, dc=example, dc=com"
+PASS=testpassword
+PWADMIN="uid=ndadmin, ou=People, dc=example, dc=com"
+ADMINPASSWD=testpw
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo /dev/null > $TESTOUT
+
+echo "Testing redundant ppolicy instance..."
+$LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcOverlay=ppolicy,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcPPolicyConfig
+olcOverlay: ppolicy
+olcPPolicyDefault: cn=duplicate policy,ou=policies,dc=example,dc=com
+EOF
+RC=$?
+if test $RC = 0 ; then
+ echo "ldapadd should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ < $LDIFPPOLICY >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing account lockout..."
+$LDAPSEARCH -H $URI1 -D "$USER" -w wrongpw >$SEARCHOUT 2>&1
+sleep 2
+$LDAPSEARCH -H $URI1 -D "$USER" -w wrongpw >>$SEARCHOUT 2>&1
+sleep 2
+$LDAPSEARCH -H $URI1 -D "$USER" -w wrongpw >>$SEARCHOUT 2>&1
+sleep 2
+$LDAPSEARCH -e ppolicy -H $URI1 -D "$USER" -w wrongpw >> $SEARCHOUT 2>&1
+$LDAPSEARCH -e ppolicy -H $URI1 -D "$USER" -w $PASS >> $SEARCHOUT 2>&1
+COUNT=`grep "Account locked" $SEARCHOUT | wc -l`
+if test $COUNT != 2 ; then
+ echo "Account lockout test failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+DELAY=`$LDAPSEARCH -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ -b "$USER" -E accountUsability 1.1 | sed -n -e 's/.*seconds_before_unlock=\(\d*\)/\1/p'`
+
+echo "Waiting $DELAY seconds for lockout to reset..."
+sleep $DELAY
+sleep 1
+
+$LDAPSEARCH -e ppolicy -H $URI1 -D "$USER" -w $PASS \
+ -b "$BASEDN" -s base >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+DELAY=`$LDAPSEARCH -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ -b "$USER" -E accountUsability 1.1 | sed -n -e 's/.*expire=\(\d*\)/\1/p'`
+
+echo "Testing password expiration"
+echo "Waiting $DELAY seconds for password to expire..."
+sleep $DELAY
+sleep 1
+
+$LDAPSEARCH -e ppolicy -H $URI1 -D "$USER" -w $PASS \
+ -b "$BASEDN" -s base > $SEARCHOUT 2>&1
+sleep 2
+$LDAPSEARCH -e ppolicy -H $URI1 -D "$USER" -w $PASS \
+ -b "$BASEDN" -s base >> $SEARCHOUT 2>&1
+sleep 2
+$LDAPSEARCH -e ppolicy -H $URI1 -D "$USER" -w $PASS \
+ -b "$BASEDN" -s base >> $SEARCHOUT 2>&1
+sleep 2
+$LDAPSEARCH -e ppolicy -H $URI1 -D "$USER" -w $PASS \
+ -b "$BASEDN" -s base >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC = 0 ; then
+ echo "Password expiration failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+COUNT=`grep "grace logins" $SEARCHOUT | wc -l`
+if test $COUNT != 3 ; then
+ echo "Password expiration test failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Resetting password to clear expired status"
+$LDAPPASSWD -H $URI1 \
+ -w secret -s $PASS \
+ -D "$MANAGERDN" "$USER" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldappasswd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filling password history..."
+$LDAPMODIFY -v -D "$USER" -H $URI1 -w $PASS >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: $USER
+changetype: modify
+delete: userpassword
+userpassword: $PASS
+-
+replace: userpassword
+userpassword: 20urgle12-1
+
+dn: $USER
+changetype: modify
+delete: userpassword
+userpassword: 20urgle12-1
+-
+replace: userpassword
+userpassword: 20urgle12-2
+
+dn: $USER
+changetype: modify
+delete: userpassword
+userpassword: 20urgle12-2
+-
+replace: userpassword
+userpassword: 20urgle12-3
+
+dn: $USER
+changetype: modify
+delete: userpassword
+userpassword: 20urgle12-3
+-
+replace: userpassword
+userpassword: 20urgle12-4
+
+dn: $USER
+changetype: modify
+delete: userpassword
+userpassword: 20urgle12-4
+-
+replace: userpassword
+userpassword: 20urgle12-5
+
+dn: $USER
+changetype: modify
+delete: userpassword
+userpassword: 20urgle12-5
+-
+replace: userpassword
+userpassword: 20urgle12-6
+
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+echo "Testing password history..."
+$LDAPMODIFY -v -D "$USER" -H $URI1 -w 20urgle12-6 >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: $USER
+changetype: modify
+delete: userPassword
+userPassword: 20urgle12-6
+-
+replace: userPassword
+userPassword: 20urgle12-2
+
+EOMODS
+RC=$?
+if test $RC = 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing failed logins when password/policy missing..."
+
+$LDAPSEARCH -e ppolicy -H $URI1 \
+ -D "uid=test, ou=People,$BASEDN" -w hasnopolicy \
+ -b "$BASEDN" -s base > $SEARCHOUT 2>&1
+RC=$?
+if test $RC = 0 ; then
+ echo "Password accepted ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+$LDAPSEARCH -e ppolicy -H $URI1 -D "$BASEDN" -w hasnopw \
+ -b "$BASEDN" -s base > $SEARCHOUT 2>&1
+RC=$?
+if test $RC = 0 ; then
+ echo "Password accepted ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+$LDAPSEARCH -H $URI1 -D "$MANAGERDN" -w $PASSWD -b "$BASEDN" \* \+ > $SEARCHOUT 2>&1
+COUNT=`grep "pwdFailureTime" $SEARCHOUT | wc -l`
+if test $COUNT != 0 ; then
+ echo "Failed login stored on an account without policy and or password"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing forced reset..."
+
+$LDAPMODIFY -v -D "$PWADMIN" -H $URI1 -w $ADMINPASSWD >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: $USER
+changetype: modify
+replace: userPassword
+userPassword: $PASS
+
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -e ppolicy -H $URI1 -D "$USER" -w $PASS \
+ -b "$BASEDN" -s base > $SEARCHOUT 2>&1
+RC=$?
+if test $RC = 0 ; then
+ echo "Forced reset failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+COUNT=`grep "Operations are restricted" $SEARCHOUT | wc -l`
+if test $COUNT != 1 ; then
+ echo "Forced reset test failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Clearing forced reset..."
+
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: $USER
+changetype: modify
+delete: pwdReset
+
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -e ppolicy -H $URI1 -D "$USER" -w $PASS \
+ -b "$BASEDN" -s base > $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Clearing forced reset failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing Safe modify..."
+
+$LDAPPASSWD -H $URI1 \
+ -w $PASS -s failexpect \
+ -D "$USER" >> $TESTOUT 2>&1
+RC=$?
+if test $RC = 0 ; then
+ echo "Safe modify test 1 failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+sleep 2
+
+OLDPASS=$PASS
+PASS=successexpect
+
+$LDAPPASSWD -H $URI1 \
+ -w $OLDPASS -s $PASS -a $OLDPASS \
+ -D "$USER" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Safe modify test 2 failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing length requirement..."
+# check control in response (ITS#5711)
+$LDAPPASSWD -H $URI1 \
+ -w $PASS -a $PASS -s 2shr \
+ -D "$USER" -e ppolicy > ${TESTOUT}.2 2>&1
+RC=$?
+cat ${TESTOUT}.2 >> $TESTOUT
+if test $RC = 0 ; then
+ echo "Length requirement test failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+COUNT=`grep "Password fails quality" ${TESTOUT}.2 | wc -l`
+if test $COUNT != 1 ; then
+ echo "Length requirement test failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+COUNT=`grep "Password is too short for policy" ${TESTOUT}.2 | wc -l`
+if test $COUNT != 1 ; then
+ echo "Control not returned in response"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+$LDAPPASSWD -H $URI1 \
+ -w $PASS -a $PASS -s passwordthatistoolong \
+ -D "$USER" -e ppolicy > ${TESTOUT}.2 2>&1
+RC=$?
+cat ${TESTOUT}.2 >> $TESTOUT
+COUNT=`grep "Password is too long for policy" ${TESTOUT}.2 | wc -l`
+if test $COUNT != 1 ; then
+ echo "Control not returned in response"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing hashed length requirement..."
+
+$LDAPMODIFY -H $URI1 -D "$USER" -w $PASS > \
+ ${TESTOUT}.2 2>&1 << EOMODS
+dn: $USER
+changetype: modify
+delete: userPassword
+userPassword: $PASS
+-
+add: userPassword
+userPassword: {MD5}xxxxxx
+
+EOMODS
+RC=$?
+cat ${TESTOUT}.2 >> $TESTOUT
+if test $RC = 0 ; then
+ echo "Hashed length requirement test failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+COUNT=`grep "Password fails quality" ${TESTOUT}.2 | wc -l`
+if test $COUNT != 1 ; then
+ echo "Hashed length requirement test failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing multiple password add/modify checks..."
+
+$LDAPMODIFY -H $URI1 -D "$MANAGERDN" -w $PASSWD >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Add Should Fail, ou=People, dc=example, dc=com
+changetype: add
+objectClass: inetOrgPerson
+cn: Add Should Fail
+sn: Fail
+userPassword: firstpw
+userPassword: secondpw
+EOMODS
+RC=$?
+if test $RC = 0 ; then
+ echo "Multiple password add test failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+$LDAPMODIFY -H $URI1 -D "$MANAGERDN" -w $PASSWD >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: $USER
+changetype: modify
+add: userPassword
+userPassword: firstpw
+userPassword: secondpw
+EOMODS
+RC=$?
+if test $RC = 0 ; then
+ echo "Multiple password modify add test failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+$LDAPMODIFY -H $URI1 -D "$MANAGERDN" -w $PASSWD >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: $USER
+changetype: modify
+replace: userPassword
+userPassword: firstpw
+userPassword: secondpw
+EOMODS
+RC=$?
+if test $RC = 0 ; then
+ echo "Multiple password modify replace test failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing idle password expiration"
+echo "Reconfiguring policy to replace expiration with idle expiration..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Standard Policy, ou=Policies, dc=example, dc=com
+changetype: modify
+delete: pwdMaxAge
+-
+add: pwdMaxIdle
+pwdMaxIdle: 15
+
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -e ppolicy -H $URI1 -D "$USER" -w $PASS \
+ -b "$BASEDN" -s base > $SEARCHOUT 2>&1
+
+DELAY=`$LDAPSEARCH -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ -b "$USER" -E accountUsability 1.1 | sed -n -e 's/.*expire=\(\d*\)/\1/p'`
+
+echo "Waiting $DELAY seconds for password to expire..."
+sleep $DELAY
+sleep 1
+
+$LDAPSEARCH -e ppolicy -H $URI1 -D "$USER" -w $PASS \
+ -b "$BASEDN" -s base >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 49 ; then
+ echo "Password idle expiration failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Reverting policy changes..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Standard Policy, ou=Policies, dc=example, dc=com
+changetype: modify
+delete: pwdMaxIdle
+-
+add: pwdMaxAge
+pwdMaxAge: 30
+
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if test "$BACKLDAP" != "ldapno" && test "$SYNCPROV" != "syncprovno" ; then
+echo ""
+echo "Setting up policy state forwarding test..."
+
+mkdir $DBDIR2
+sed -e "s,$DBDIR1,$DBDIR2," < $CONF1 > $CONF2
+echo "Starting slapd consumer on TCP/IP port $PORT2..."
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+echo "Configuring syncprov on provider..."
+if [ "$SYNCPROV" = syncprovmod ]; then
+ $LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: cn=module,cn=config
+objectclass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+olcModuleLoad: syncprov.la
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for moduleLoad ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+$LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcOverlay={1}syncprov,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: {1}syncprov
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for provider database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Configuring syncrepl on consumer..."
+if [ "$BACKLDAP" = ldapmod ]; then
+ $LDAPADD -D cn=config -H $URI2 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: cn=module,cn=config
+objectclass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/back-ldap
+olcModuleLoad: back_ldap.la
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for moduleLoad ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+$LDAPMODIFY -D cn=config -H $URI2 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcOverlay={0}chain,olcDatabase={-1}frontend,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectClass: olcChainConfig
+olcOverlay: {0}chain
+
+dn: olcDatabase=ldap,olcOverlay={0}chain,olcDatabase={-1}frontend,cn=config
+changetype: add
+objectClass: olcLDAPConfig
+objectClass: olcChainDatabase
+olcDBURI: $URI1
+olcDbIDAssertBind: bindmethod=simple
+ binddn="cn=manager,dc=example,dc=com"
+ credentials=secret
+ mode=self
+
+dn: olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcSyncrepl
+olcSyncrepl: rid=1
+ provider=$URI1
+ binddn="cn=manager,dc=example,dc=com"
+ bindmethod=simple
+ credentials=secret
+ searchbase="dc=example,dc=com"
+ type=refreshAndPersist
+ retry="3 5 300 5"
+-
+add: olcUpdateref
+olcUpdateref: $URI1
+-
+
+dn: olcOverlay={0}ppolicy,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcPPolicyForwardUpdates
+olcPPolicyForwardUpdates: TRUE
+-
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting for consumer to sync..."
+sleep $SLEEP1
+
+echo "Testing policy state forwarding..."
+$LDAPSEARCH -H $URI2 -D "$USER" -w wrongpw >$SEARCHOUT 2>&1
+RC=$?
+if test $RC != 49 ; then
+ echo "ldapsearch should have failed with 49, got ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+$LDAPSEARCH -H $URI1 -D "$MANAGERDN" -w $PASSWD -b "$USER" \* \+ >> $SEARCHOUT 2>&1
+COUNT=`grep "pwdFailureTime" $SEARCHOUT | wc -l`
+if test $COUNT != 1 ; then
+ echo "Policy state forwarding failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+# End of chaining test
+
+fi
+
+echo ""
+echo "Testing obsolete Netscape ppolicy controls..."
+echo "Enabling Netscape controls..."
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: olcOverlay={0}ppolicy,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcPPolicySendNetscapeControls
+olcPPolicySendNetscapeControls: TRUE
+-
+
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Reconfiguring policy to remove grace logins..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Standard Policy, ou=Policies, dc=example, dc=com
+changetype: modify
+delete: pwdGraceAuthnLimit
+-
+replace: pwdMaxAge
+pwdMaxAge: 15
+-
+
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+OLDPASS=$PASS
+PASS=newpass
+$LDAPPASSWD -H $URI1 \
+ -w secret -s $PASS \
+ -D "$MANAGERDN" "$USER" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Setting new password failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Clearing forced reset..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: $USER
+changetype: modify
+delete: pwdReset
+
+EOMODS
+
+DELAY=`$LDAPSEARCH -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ -b "$USER" -E accountUsability 1.1 | sed -n -e 's/.*expire=\(\d*\)/\1/p'`
+DELAY=`expr $DELAY - 10`
+
+echo "Testing password expiration"
+echo "Waiting $DELAY seconds for password to expire..."
+sleep $DELAY
+
+$LDAPSEARCH -H $URI1 -D "$USER" -w $PASS \
+ -b "$BASEDN" -s base > $SEARCHOUT 2>&1
+sleep 3
+$LDAPSEARCH -H $URI1 -D "$USER" -w $PASS \
+ -b "$BASEDN" -s base >> $SEARCHOUT 2>&1
+sleep 3
+$LDAPSEARCH -H $URI1 -D "$USER" -w $PASS \
+ -b "$BASEDN" -s base >> $SEARCHOUT 2>&1
+sleep 3
+$LDAPSEARCH -H $URI1 -D "$USER" -w $PASS \
+ -b "$BASEDN" -s base >> $SEARCHOUT 2>&1
+sleep 3
+$LDAPSEARCH -H $URI1 -D "$USER" -w $PASS \
+ -b "$BASEDN" -s base >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC = 0 ; then
+ echo "Password expiration failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+COUNT=`grep "PasswordExpiring" $SEARCHOUT | wc -l`
+if test $COUNT = 0 ; then
+ echo "Password expiring warning test failed!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test023-refint b/tests/scripts/test023-refint
new file mode 100755
index 0000000..6c10236
--- /dev/null
+++ b/tests/scripts/test023-refint
@@ -0,0 +1,276 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2004-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $REFINT = refintno; then
+ echo "Referential Integrity overlay not available, test skipped"
+ exit 0
+fi
+
+if test $BACKEND = wt ; then
+ echo "back-wt does not support subtree rename"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $REFINTCONF > $CONF1
+$SLAPADD -f $CONF1 -l $LDIFREFINT
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd referential integrity operations..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Searching unmodified database..."
+
+$LDAPSEARCH -S "" -b "o=refint" -H $URI1 > $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$EGREP_CMD "(manager|secretary):" $SEARCHOUT | sed "s/george/foster/g" | \
+ sort > $TESTOUT 2>&1
+
+echo "Testing modrdn..."
+$LDAPMODRDN -D "$REFINTDN" -r -H $URI1 -w $PASSWD > \
+ /dev/null 2>&1 'uid=george,ou=users,o=refint' 'uid=foster'
+#$LDAPMODRDN -D "$REFINTDN" -r -H $URI1 -w $PASSWD \
+# 'uid=george,ou=users,o=refint' 'uid=foster'
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodrdn failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1;
+
+echo "Using ldapsearch to check dependents new rdn..."
+
+$LDAPSEARCH -S "" -b "o=refint" -H $URI1 > $SEARCHOUT 2>&1
+
+RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$EGREP_CMD "(manager|secretary):" $SEARCHOUT | sort > $SEARCHFLT 2>&1
+
+echo "Comparing ldapsearch results against original..."
+$CMP $TESTOUT $SEARCHFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - modify operations did not complete correctly"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing delete..."
+$LDAPMODIFY -v -D "$REFINTDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EDEL
+version: 1
+dn: uid=foster,ou=users,o=refint
+changetype: delete
+EDEL
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1;
+
+echo "Using ldapsearch to verify dependents have been deleted..."
+$LDAPSEARCH -S "" -b "o=refint" -H $URI1 > $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$EGREP_CMD "(manager|secretary):" $SEARCHOUT > $SEARCHFLT 2>&1
+
+RC=`grep -c foster $SEARCHFLT`
+if test $RC != 0 ; then
+ echo "dependent modify failed - dependents were not deleted"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Additional test records..."
+
+$LDAPADD -D "$REFINTDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << ETEST
+dn: uid=special,ou=users,o=refint
+objectClass: inetOrgPerson
+objectClass: extensibleObject
+uid: special
+sn: special
+cn: special
+businessCategory: nothing
+carLicense: FOO
+departmentNumber: 933
+displayName: special
+employeeNumber: 41491
+employeeType: vendor
+givenName: special
+member: uid=alice,ou=users,o=refint
+ETEST
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing delete when referential attribute is a MUST..."
+$LDAPMODIFY -v -D "$REFINTDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EDEL
+version: 1
+dn: uid=alice,ou=users,o=refint
+changetype: delete
+EDEL
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -v -D "$REFINTDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EDEL
+version: 1
+dn: cn=group,o=refint
+changetype: add
+objectClass: groupOfNames
+cn: group
+member: uid=bill,ou=users,o=refint
+member: uid=bob,ou=users,o=refint
+member: uid=dave,ou=users,o=refint
+member: uid=jorge,ou=users,o=refint
+member: uid=theman,ou=users,o=refint
+member: uid=richard,ou=users,o=refint
+EDEL
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1;
+
+$LDAPSEARCH -S "" -b "o=refint" -H $URI1 \
+ manager member secretary > $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$EGREP_CMD "(manager|member|secretary):" $SEARCHOUT \
+ | sed "s/ou=users/ou=people/g" | \
+ sort > $TESTOUT 2>&1
+
+echo "testing subtree rename"
+$LDAPMODRDN -D "$REFINTDN" -r -H $URI1 -w $PASSWD > \
+ /dev/null 2>&1 'ou=users,o=refint' 'ou=people'
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodrdn failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1;
+
+echo "Using ldapsearch to check dependents new rdn..."
+
+$LDAPSEARCH -S "" -b "o=refint" -H $URI1 \
+ manager member secretary > $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$EGREP_CMD "(manager|member|secretary):" $SEARCHOUT \
+ | sort > $SEARCHFLT 2>&1
+
+echo "Comparing ldapsearch results against original..."
+$CMP $TESTOUT $SEARCHFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - subtree rename operations did not complete correctly"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test024-unique b/tests/scripts/test024-unique
new file mode 100755
index 0000000..0c68bdc
--- /dev/null
+++ b/tests/scripts/test024-unique
@@ -0,0 +1,845 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2004-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $UNIQUE = uniqueno; then
+ echo "Attribute Uniqueness overlay not available, test skipped"
+ exit 0
+fi
+
+RCODEconstraint=19
+RCODEnorelax=50
+test $BACKEND = null && RCODEconstraint=0
+
+mkdir -p $TESTDIR $DBDIR1
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $UNIQUECONF > $CONF1
+$SLAPADD -f $CONF1 -l $LDIFUNIQUE
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+mkdir $TESTDIR/confdir
+$SLAPD -f $CONF1 -F $TESTDIR/confdir -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd attribute uniqueness operations..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding a unique record..."
+$LDAPADD -D "$UNIQUEDN" -H $URI1 -w $PASSWD \
+ > /dev/null << EOTUNIQ1
+dn: uid=dave,ou=users,o=unique
+objectClass: inetOrgPerson
+objectClass: simpleSecurityObject
+uid: dave
+sn: nothere
+cn: dave
+businessCategory: otest
+carLicense: TEST
+departmentNumber: 42
+# NOTE: use special chars in attr value to be used
+# in internal searches ITS#4212
+displayName: Dave (ITS#4212)
+employeeNumber: 69
+employeeType: contractor
+givenName: Dave
+userpassword: $PASSWD
+EOTUNIQ1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding a non-unique record..."
+$LDAPADD -D "uid=dave,ou=users,o=unique" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOTUNIQ2
+dn: uid=bill,ou=users,o=unique
+objectClass: inetOrgPerson
+uid: bill
+sn: johnson
+cn: bill
+businessCategory: rtest
+carLicense: ABC123
+departmentNumber: 42
+displayName: Bill
+employeeNumber: 5150
+employeeType: contractor
+givenName: Bill
+EOTUNIQ2
+RC=$?
+if test $RC != $RCODEconstraint ; then
+ echo "unique check failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+# ITS#6641/8057/8245
+echo "Trying to bypass uniqueness as a normal user..."
+$LDAPADD -e \!relax -D "uid=dave,ou=users,o=unique" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOTUNIQ2
+dn: uid=bill,ou=users,o=unique
+objectClass: inetOrgPerson
+uid: bill
+sn: johnson
+cn: bill
+businessCategory: rtest
+carLicense: ABC123
+departmentNumber: 42
+displayName: Bill
+employeeNumber: 5150
+employeeType: contractor
+givenName: Bill
+EOTUNIQ2
+RC=$?
+if test $RC != $RCODEnorelax && test $RC != $RCODEconstraint ; then
+ echo "unique check failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo "Trying to bypass uniqueness as a normal user with ManageDSAIt..."
+$LDAPADD -M -D "uid=dave,ou=users,o=unique" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOTUNIQ2
+dn: uid=bill,ou=users,o=unique
+objectClass: inetOrgPerson
+uid: bill
+sn: johnson
+cn: bill
+businessCategory: rtest
+carLicense: ABC123
+departmentNumber: 42
+displayName: Bill
+employeeNumber: 5150
+employeeType: contractor
+givenName: Bill
+EOTUNIQ2
+RC=$?
+if test $RC != $RCODEconstraint ; then
+ echo "unique check failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo "Bypassing uniqueness as an admin user..."
+$LDAPADD -e \!relax -D "$UNIQUEDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOTUNIQ2
+dn: uid=bill,ou=users,o=unique
+objectClass: inetOrgPerson
+uid: bill
+sn: johnson
+cn: bill
+businessCategory: rtest
+carLicense: ABC123
+departmentNumber: 42
+displayName: Bill
+employeeNumber: 5150
+employeeType: contractor
+givenName: Bill
+EOTUNIQ2
+RC=$?
+if test $RC != 0 ; then
+ echo "spurious unique error ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Cleaning up"
+$LDAPDELETE -D "$UNIQUEDN" -H $URI1 -w $PASSWD \
+ "uid=bill,ou=users,o=unique" > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0; then
+ echo "ldapdelete failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo Dynamically retrieving initial configuration...
+$LDAPSEARCH -S "" -b olcOverlay='{0}'unique,olcDatabase='{1}'$BACKEND,cn=config -D cn=config -y $CONFIGPWF -H $URI1 -LLL | tr -d \\r >$TESTDIR/initial-config.ldif
+cat <<EOF >$TESTDIR/initial-reference.ldif
+dn: olcOverlay={0}unique,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcUniqueConfig
+olcOverlay: {0}unique
+olcUniqueBase: o=unique
+olcUniqueAttribute: employeeNumber
+olcUniqueAttribute: displayName
+
+EOF
+diff $TESTDIR/initial-config.ldif $TESTDIR/initial-reference.ldif > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Initial configuration is not reported correctly."
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo Dynamically trying to add a URI with legacy attrs present...
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOF
+dn: olcOverlay={0}unique,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcUniqueURI
+olcUniqueURI: ldap:///?employeeNumber,displayName?sub
+EOF
+RC=$?
+if test $RC != 80 ; then
+ echo "legacy and unique_uri allowed together"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo Dynamically trying to add legacy ignored attrs with legacy attrs present...
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOF
+dn: olcOverlay={0}unique,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcUniqueIgnore
+olcUniqueIgnore: objectClass
+EOF
+RC=$?
+if test $RC != 80 ; then
+ echo "legacy attrs and legacy ignore attrs allowed together"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo Verifying initial configuration intact...
+$LDAPSEARCH -S "" -b olcOverlay='{0}'unique,olcDatabase='{1}'$BACKEND,cn=config -D cn=config -y $CONFIGPWF -H $URI1 -LLL | tr -d \\r >$TESTDIR/initial-config-recheck.ldif
+diff $TESTDIR/initial-config-recheck.ldif $TESTDIR/initial-reference.ldif > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Initial configuration damaged by unsuccessful modifies."
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo Dynamically removing legacy base...
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOF
+dn: olcOverlay={0}unique,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+delete: olcUniqueBase
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "base removal failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo Verifying base removal...
+$LDAPSEARCH -S "" -b olcOverlay='{0}'unique,olcDatabase='{1}'$BACKEND,cn=config -D cn=config -y $CONFIGPWF -H $URI1 -LLL | tr -d \\r >$TESTDIR/baseremoval-config.ldif
+cat >$TESTDIR/baseremoval-reference.ldif <<EOF
+dn: olcOverlay={0}unique,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcUniqueConfig
+olcOverlay: {0}unique
+olcUniqueAttribute: employeeNumber
+olcUniqueAttribute: displayName
+
+EOF
+diff $TESTDIR/baseremoval-config.ldif $TESTDIR/baseremoval-reference.ldif > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Configuration damaged by base removal"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo "Adding a non-unique record..."
+$LDAPADD -D "uid=dave,ou=users,o=unique" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOTUNIQ2
+dn: uid=bill,ou=users,o=unique
+objectClass: inetOrgPerson
+uid: bill
+sn: johnson
+cn: bill
+businessCategory: rtest
+carLicense: ABC123
+departmentNumber: 42
+displayName: Bill
+employeeNumber: 5150
+employeeType: contractor
+givenName: Bill
+EOTUNIQ2
+RC=$?
+if test $RC != $RCODEconstraint ; then
+ echo "unique check failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo Trying a legacy base outside of the backend...
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOF
+dn: olcOverlay={0}unique,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcUniqueBase
+olcUniqueBase: cn=config
+EOF
+RC=$?
+if test $RC != 80 ; then
+ echo "out of backend scope base allowed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo "Adding and removing attrs..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOF
+dn: olcOverlay={0}unique,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcUniqueAttribute
+olcUniqueAttribute: description
+olcUniqueAttribute: telephoneNumber
+-
+delete: olcUniqueAttribute
+olcUniqueAttribute: displayName
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "Unable to remove an attribute"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo "Verifying we removed the right attr..."
+$LDAPADD -D "uid=dave,ou=users,o=unique" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOTUNIQ2
+dn: uid=bill,ou=users,o=unique
+objectClass: inetOrgPerson
+uid: bill
+sn: johnson
+cn: bill
+businessCategory: rtest
+carLicense: ABC123
+departmentNumber: 42
+displayName: Bill
+employeeNumber: 5150
+employeeType: contractor
+givenName: Bill
+EOTUNIQ2
+RC=$?
+if test $RC != $RCODEconstraint ; then
+ echo "olcUniqueAttribute single deletion hit the wrong value"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo Removing legacy config and adding URIs...
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOF
+dn: olcOverlay={0}unique,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+delete: olcUniqueAttribute
+-
+add: olcUniqueURI
+olcUniqueURI: ldap:///?employeeNumber,displayName?sub
+olcUniqueURI: ldap:///?description?one
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "Reconfiguration to URIs failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo Dynamically retrieving second configuration...
+$LDAPSEARCH -S "" -b olcOverlay='{0}'unique,olcDatabase='{1}'$BACKEND,cn=config -D cn=config -y $CONFIGPWF -H $URI1 -LLL | tr -d \\r >$TESTDIR/second-config.ldif
+cat >$TESTDIR/second-reference.ldif <<EOF
+dn: olcOverlay={0}unique,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcUniqueConfig
+olcOverlay: {0}unique
+olcUniqueURI: ldap:///?employeeNumber,displayName?sub
+olcUniqueURI: ldap:///?description?one
+
+EOF
+diff $TESTDIR/second-config.ldif $TESTDIR/second-reference.ldif > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Second configuration is not reported correctly."
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo "Adding a non-unique record..."
+$LDAPADD -D "uid=dave,ou=users,o=unique" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOTUNIQ2
+dn: uid=bill,ou=users,o=unique
+objectClass: inetOrgPerson
+uid: bill
+sn: johnson
+cn: bill
+businessCategory: rtest
+carLicense: ABC123
+departmentNumber: 42
+displayName: Bill
+employeeNumber: 5150
+employeeType: contractor
+givenName: Bill
+EOTUNIQ2
+RC=$?
+if test $RC != $RCODEconstraint ; then
+ echo "unique check failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo Dynamically trying to add legacy base
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOF
+dn: olcOverlay={0}unique,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcUniqueBase
+olcUniqueBase: o=unique
+EOF
+RC=$?
+if test $RC != 80 ; then
+ echo "legacy base allowed with URIs"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo Dynamically trying to add legacy attrs
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOF
+dn: olcOverlay={0}unique,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcUniqueAttribute
+olcUniqueAttribute: description
+EOF
+RC=$?
+if test $RC != 80 ; then
+ echo "legacy attributes allowed with URIs"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo Dynamically trying to add legacy strictness
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOF
+dn: olcOverlay={0}unique,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcUniqueStrict
+olcUniqueStrict: TRUE
+EOF
+RC=$?
+if test $RC != 80 ; then
+ echo "legacy strictness allowed with URIs"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+#echo ----------------------
+echo Dynamically trying a bad filter...
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOF
+dn: olcOverlay={0}unique,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcUniqueURI
+olcUniqueURI: ldap:///?sn?sub?((cn=e*))
+EOF
+RC=$?
+if test $RC != 80 ; then
+ echo "bad filter allowed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo Verifying second configuration intact...
+$LDAPSEARCH -S "" -b olcOverlay='{0}'unique,olcDatabase='{1}'$BACKEND,cn=config -D cn=config -y $CONFIGPWF -H $URI1 -LLL | tr -d \\r >$TESTDIR/second-config-recheck.ldif
+diff $TESTDIR/second-config-recheck.ldif $TESTDIR/second-reference.ldif > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Second configuration damaged by rejected modifies."
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+#echo ----------------------
+echo Dynamically reconfiguring to use different URIs...
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOF
+dn: olcOverlay={0}unique,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcUniqueURI
+olcUniqueURI: ldap:///?sn?sub?(cn=e*)
+olcUniqueURI: ldap:///?uid?sub?(cn=edgar)
+-
+delete: olcUniqueURI
+olcUniqueURI: ldap:///?description?one
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "unable to reconfigure"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo Dynamically retrieving third configuration...
+$LDAPSEARCH -S "" -b olcOverlay='{0}'unique,olcDatabase='{1}'$BACKEND,cn=config -D cn=config -y $CONFIGPWF -H $URI1 -LLL | tr -d \\r >$TESTDIR/third-config.ldif
+cat >$TESTDIR/third-reference.ldif <<EOF
+dn: olcOverlay={0}unique,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcUniqueConfig
+olcOverlay: {0}unique
+olcUniqueURI: ldap:///?employeeNumber,displayName?sub
+olcUniqueURI: ldap:///?sn?sub?(cn=e*)
+olcUniqueURI: ldap:///?uid?sub?(cn=edgar)
+
+EOF
+diff $TESTDIR/third-config.ldif $TESTDIR/third-reference.ldif > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Third configuration is not reported correctly."
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo "Adding a record unique in both domains if filtered..."
+
+$LDAPADD -D "$UNIQUEDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF
+dn: uid=edgar,ou=users,o=unique
+objectClass: inetOrgPerson
+uid: edgar
+sn: johnson
+cn: edgar
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "unique check failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo "Adding a record unique in all domains because of filter conditions "
+$LDAPADD -D "$UNIQUEDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF
+dn: uid=empty,ou=users,o=unique
+objectClass: inetOrgPerson
+uid: edgar
+cn: empty
+sn: empty
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "spurious unique error ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo "Sending an empty modification"
+
+$LDAPMODIFY -D "$UNIQUEDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF
+dn: uid=empty,ou=users,o=unique
+changetype: modify
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "spurious unique error ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo "Making a record non-unique"
+$LDAPMODIFY -D "uid=dave,ou=users,o=unique" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF
+dn: uid=empty,ou=users,o=unique
+changetype: modify
+replace: sn
+sn: johnson
+EOF
+
+RC=$?
+if test $RC != $RCODEconstraint ; then
+ echo "unique check failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+# ITS#6641/8057/8245
+echo "Trying to bypass uniqueness as a normal user..."
+$LDAPMODIFY -e \!relax -D "uid=dave,ou=users,o=unique" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF
+dn: uid=empty,ou=users,o=unique
+changetype: modify
+replace: sn
+sn: johnson
+EOF
+
+RC=$?
+if test $RC != $RCODEnorelax && test $RC != $RCODEconstraint ; then
+ echo "unique check failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo "Trying to bypass uniqueness as a normal user with ManageDSAIt..."
+$LDAPMODIFY -M -D "uid=dave,ou=users,o=unique" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF
+dn: uid=empty,ou=users,o=unique
+changetype: modify
+replace: sn
+sn: johnson
+EOF
+
+RC=$?
+if test $RC != $RCODEconstraint ; then
+ echo "unique check failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo "Bypassing uniqueness as an admin user..."
+$LDAPMODIFY -e \!relax -D "$UNIQUEDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF
+dn: uid=empty,ou=users,o=unique
+changetype: modify
+replace: sn
+sn: johnson
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "spurious unique error ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Cleaning up"
+$LDAPMODIFY -D "$UNIQUEDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF
+dn: uid=empty,ou=users,o=unique
+changetype: modify
+replace: sn
+sn: empty
+EOF
+
+RC=$?
+if test $RC != 0; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding another unique record..."
+$LDAPADD -D "$UNIQUEDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF
+dn: uid=not edgar,uid=edgar,ou=users,o=unique
+objectClass: inetOrgPerson
+uid: not edgar
+sn: Alan
+cn: not edgar
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "unique check failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo "Making the record non-unique with modrdn..."
+$LDAPMODRDN -D "uid=dave,ou=users,o=unique" -H $URI1 -w $PASSWD \
+ "uid=not edgar,uid=edgar,ou=users,o=unique" "uid=edgar" > $TESTOUT 2>&1
+
+RC=$?
+if test $RC != $RCODEconstraint ; then
+ echo "unique check failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+# ITS#6641/8057/8245
+echo "Trying to bypass uniqueness as a normal user..."
+$LDAPMODRDN -e \!relax -D "uid=dave,ou=users,o=unique" -H $URI1 -w $PASSWD \
+ "uid=not edgar,uid=edgar,ou=users,o=unique" "uid=edgar" > $TESTOUT 2>&1
+
+RC=$?
+if test $RC != $RCODEnorelax && test $RC != $RCODEconstraint ; then
+ echo "unique check failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo "Trying to bypass uniqueness as a normal user with a ManageDSAIt control..."
+$LDAPMODRDN -M -D "uid=dave,ou=users,o=unique" -H $URI1 -w $PASSWD \
+ "uid=not edgar,uid=edgar,ou=users,o=unique" "uid=edgar" > $TESTOUT 2>&1
+
+RC=$?
+if test $RC != $RCODEconstraint ; then
+ echo "unique check failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo "Bypassing uniqueness as an admin user..."
+$LDAPMODRDN -e \!relax -D "$UNIQUEDN" -H $URI1 -w $PASSWD \
+ "uid=not edgar,uid=edgar,ou=users,o=unique" "uid=edgar" > $TESTOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "spurious unique error ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Cleaning up"
+$LDAPDELETE -D "$UNIQUEDN" -H $URI1 -w $PASSWD \
+ "uid=edgar,uid=edgar,ou=users,o=unique" > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0; then
+ echo "ldapdelete failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding a record unique in one domain, non-unique in the filtered domain..."
+
+$LDAPADD -D "uid=dave,ou=users,o=unique" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF
+dn: uid=elvis,ou=users,o=unique
+objectClass: inetOrgPerson
+uid: elvis
+sn: johnson
+cn: elvis
+EOF
+
+RC=$?
+if test $RC != $RCODEconstraint ; then
+ echo "unique check failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+#echo ----------------------
+echo Dynamically reconfiguring to use attribute-ignore URIs...
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOF
+dn: olcOverlay={0}unique,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcUniqueURI
+olcUniqueURI: ignore ldap:///?objectClass,uid,cn,sn?sub
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "unable to reconfigure"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo Dynamically retrieving fourth configuration...
+$LDAPSEARCH -S "" -b olcOverlay='{0}'unique,olcDatabase='{1}'$BACKEND,cn=config -D cn=config -y $CONFIGPWF -H $URI1 -LLL | tr -d \\r >$TESTDIR/fourth-config.ldif
+cat >$TESTDIR/fourth-reference.ldif <<EOF
+dn: olcOverlay={0}unique,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcUniqueConfig
+olcOverlay: {0}unique
+olcUniqueURI: ignore ldap:///?objectClass,uid,cn,sn?sub
+
+EOF
+diff $TESTDIR/fourth-config.ldif $TESTDIR/fourth-reference.ldif > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "Fourth configuration is not reported correctly."
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo "Adding a record unique in the ignore-domain..."
+
+$LDAPADD -D "$UNIQUEDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF
+dn: uid=elvis,ou=users,o=unique
+objectClass: inetOrgPerson
+uid: elvis
+sn: johnson
+cn: elvis
+description: left the building
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "unique check failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo "Adding a record non-unique in the ignore-domain..."
+
+$LDAPADD -D "uid=dave,ou=users,o=unique" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF
+dn: uid=harry,ou=users,o=unique
+objectClass: inetOrgPerson
+uid: harry
+sn: johnson
+cn: harry
+description: left the building
+EOF
+
+RC=$?
+if test $RC != $RCODEconstraint ; then
+ echo "unique check failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test025-limits b/tests/scripts/test025-limits
new file mode 100755
index 0000000..09f8bec
--- /dev/null
+++ b/tests/scripts/test025-limits
@@ -0,0 +1,1420 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+## FIXME: need to exclude legal but wrong results...
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $BACKEND = null; then
+ echo "Limits irrelevant to $BACKEND backend, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $LIMITSCONF > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFLIMITS
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Running slapindex to index slapd database..."
+. $CONFFILTER $BACKEND < $LIMITSCONF > $CONF1
+$SLAPINDEX -f $CONF1
+RC=$?
+if test $RC != 0 ; then
+ echo "warning: slapindex failed ($RC)"
+ echo " assuming no indexing support"
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+echo ""
+echo "Testing regular search limits"
+echo ""
+
+echo "Testing no limits requested for unlimited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D 'cn=Unlimited User,ou=People,dc=example,dc=com' \
+ '(objectClass=*)' >$SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ echo "...success (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Testing no limits requested for rootdn=$MANAGERDN..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D "$MANAGERDN" \
+ '(objectClass=*)' >$SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ echo "...success (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+SIZELIMIT=4
+echo "Testing limit requested for rootdn=$MANAGERDN..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret -z $SIZELIMIT \
+ -D "$MANAGERDN" \
+ '(objectClass=*)' >$SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ if test "$COUNT" -gt "$SIZELIMIT" ; then
+ echo "...error: got $COUNT entries instead of the requested $SIZELIMIT"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ echo "...didn't bump into the requested size limit ($SIZELIMIT; got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test x"$COUNT" != x ; then
+ if test "$COUNT" = "$SIZELIMIT" ; then
+ echo "...bumped into requested size limit ($SIZELIMIT)"
+ else
+ echo "...error: got $COUNT entries with a requested sizelimit of $SIZELIMIT"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ else
+ echo "...error: bumped into server-side size limit, but got no entries!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+SIZELIMIT=2
+echo "Testing size limit request ($SIZELIMIT) for unlimited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret -z $SIZELIMIT \
+ -D 'cn=Unlimited User,ou=People,dc=example,dc=com' \
+ '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ if test "$COUNT" -gt "$SIZELIMIT" ; then
+ echo "...error: got $COUNT entries instead of the requested $SIZELIMIT"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ echo "...didn't bump into the requested size limit ($SIZELIMIT; got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test x"$COUNT" != x ; then
+ if test "$COUNT" = "$SIZELIMIT" ; then
+ echo "...bumped into requested size limit ($SIZELIMIT)"
+ else
+ echo "...error: got $COUNT entries with a requested sizelimit of $SIZELIMIT"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ else
+ echo "...error: bumped into server-side size limit, but got no entries!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+TIMELIMIT=10
+echo "Testing time limit request ($TIMELIMIT s) for unlimited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret -l $TIMELIMIT \
+ -D 'cn=Unlimited User,ou=People,dc=example,dc=com' \
+ '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ echo "...didn't bump into the requested time limit ($TIMELIMIT s; got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 3)
+ if test x"$COUNT" != x ; then
+ COUNT=0
+ fi
+ echo "...bumped into requested time limit ($TIMELIMIT s; got $COUNT entries)"
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Testing no limits requested for soft limited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D 'cn=Soft Limited User,ou=People,dc=example,dc=com' \
+ '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ echo "...didn't bump into server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test x"$COUNT" != x ; then
+ echo "...bumped into server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: bumped into server-side size limit, but got no entries!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+SIZELIMIT=2
+echo "Testing lower than soft limit request ($SIZELIMIT) for soft limited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret -z $SIZELIMIT \
+ -D 'cn=Soft Limited User,ou=People,dc=example,dc=com' \
+ '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ if test "$COUNT" -gt "$SIZELIMIT" ; then
+ echo "...error: got $COUNT entries instead of the requested $SIZELIMIT"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ echo "...didn't bump into either requested ($SIZELIMIT) or server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test "x$COUNT" != "x" ; then
+ if test "x$SIZELIMIT" = "x$COUNT" ; then
+ echo "...bumped into requested ($SIZELIMIT) size limit"
+ else
+ echo "...bumped into server-side size limit ($COUNT)"
+ fi
+ else
+ echo "...error: bumped into server-side size limit, but got no entries!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+SIZELIMIT=100
+echo "Testing higher than soft limit request ($SIZELIMIT) for soft limited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret -z $SIZELIMIT \
+ -D 'cn=Soft Limited User,ou=People,dc=example,dc=com' \
+ '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ if test "$COUNT" -gt "$SIZELIMIT" ; then
+ echo "...error: got $COUNT entries instead of the requested $SIZELIMIT"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ echo "...didn't bump into either requested ($SIZELIMIT) or server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test "x$COUNT" != "x" ; then
+ if test "x$SIZELIMIT" = "x$COUNT" ; then
+ echo "...bumped into requested ($SIZELIMIT) size limit"
+ else
+ echo "...bumped into server-side size limit ($COUNT)"
+ fi
+ else
+ echo "...error: bumped into server-side size limit, but got no entries!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+SIZELIMIT=2
+echo "Testing lower than hard limit request ($SIZELIMIT) for hard limited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret -z $SIZELIMIT \
+ -D 'cn=Hard Limited User,ou=People,dc=example,dc=com' \
+ '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ if test "$COUNT" -gt "$SIZELIMIT" ; then
+ echo "...error: got $COUNT entries instead of the requested $SIZELIMIT"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ echo "...didn't bump into either requested ($SIZELIMIT) or server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ echo "...bumped into requested ($SIZELIMIT) size limit"
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+SIZELIMIT=100
+echo "Testing higher than hard limit request ($SIZELIMIT) for hard limited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret -z $SIZELIMIT \
+ -D 'cn=Hard Limited User,ou=People,dc=example,dc=com' \
+ '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ if test "$COUNT" -gt "$SIZELIMIT" ; then
+ echo "...error: got $COUNT entries instead of the requested $SIZELIMIT"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ echo "...didn't bump into either requested ($SIZELIMIT) or server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test x"$COUNT" != x ; then
+ if test "$COUNT" = "$SIZELIMIT" ; then
+ echo "...error: bumped into requested ($SIZELIMIT) size limit"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ else
+ echo "...got size limit $COUNT instead of requested $SIZELIMIT entries"
+ fi
+ else
+ echo "...error: bumped into server-side size limit, but got no entries!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ ;;
+# 11)
+# echo "...bumped into server-side hard size administrative limit"
+# ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+SIZELIMIT=max
+echo "Testing max limit request ($SIZELIMIT) for hard limited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret -z $SIZELIMIT \
+ -D 'cn=Hard Limited User,ou=People,dc=example,dc=com' \
+ '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ echo "...didn't bump into either requested ($SIZELIMIT) or server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test x"$COUNT" != x ; then
+ echo "...bumped into requested ($SIZELIMIT=$COUNT) size limit"
+ else
+ echo "...error: bumped into server-side size limit, but got no entries!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ ;;
+# 11)
+# echo "...bumped into server-side hard size administrative limit"
+# ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Testing lower than unchecked limit request for unchecked limited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D 'cn=Unchecked Limited User,ou=People,dc=example,dc=com' \
+ '(uid=uncheckedlimited)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ echo "...success; didn't bump into server-side unchecked limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 11)
+ echo "...error: bumped into unchecked administrative limit"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+if test $INDEXDB = indexdb ; then
+
+echo "Testing higher than unchecked limit requested for unchecked limited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D 'cn=Unchecked Limited User,ou=People,dc=example,dc=com' \
+ '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ echo "...error: didn't bump into server-side unchecked limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ fi
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+ 11)
+ echo "...bumped into unchecked administrative limit"
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Testing no limits requested for unchecked limited group..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D 'cn=Unchecked Limited User 2,ou=People,dc=example,dc=com' \
+ '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ echo "...error: didn't bump into server-side unchecked limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ fi
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+ 11)
+ echo "...bumped into unchecked administrative limit"
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+else
+ echo "Skipping test for unchecked limit with $BACKEND backend."
+fi
+
+echo "Testing no limits requested for limited regex..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D 'cn=Foo User,ou=People,dc=example,dc=com' \
+ '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ echo "...didn't bump into server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test "x$COUNT" != "x" ; then
+ echo "...bumped into server-side size limit ($COUNT)"
+ else
+ echo "...error: bumped into server-side size limit, but got no entries!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Testing no limits requested for limited onelevel..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D 'cn=Bar User,ou=People,dc=example,dc=com' \
+ '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ echo "...didn't bump into server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test "x$COUNT" != "x" ; then
+ echo "...bumped into server-side size limit ($COUNT)"
+ else
+ echo "...error: bumped into server-side size limit, but got no entries!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Testing no limit requested for limited children..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D 'cn=Unchecked Limited Users,ou=Groups,dc=example,dc=com' \
+ '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ echo "...didn't bump into server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test "x$COUNT" != "x" ; then
+ echo "...bumped into server-side size limit ($COUNT)"
+ else
+ echo "...error: bumped into server-side size limit, but got no entries!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Testing no limit requested for limited subtree..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D 'cn=Unchecked Limited User 3,ou=Admin,dc=example,dc=com' \
+ '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ echo "...didn't bump into server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test "x$COUNT" != "x" ; then
+ echo "...bumped into server-side size limit ($COUNT)"
+ else
+ echo "...error: bumped into server-side size limit, but got no entries!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Testing no limit requested for limited users..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D 'cn=Special User,dc=example,dc=com' \
+ '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ echo "...didn't bump into server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test "x$COUNT" != "x" ; then
+ echo "...bumped into server-side size limit ($COUNT)"
+ else
+ echo "...error: bumped into server-side size limit, but got no entries!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Testing no limit requested for limited anonymous..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ echo "...didn't bump into server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test "x$COUNT" != "x" ; then
+ echo "...bumped into server-side size limit ($COUNT)"
+ else
+ echo "...error: bumped into server-side size limit, but got no entries!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+if test $MAINDB != maindb ; then
+ # only mdb currently supports pagedResults control
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS && wait
+
+ echo ">>>>> Test succeeded"
+ exit 0
+fi
+
+if test x"$SLAPD_PAGE_SIZE" != x ; then
+ PAGESIZE="$SLAPD_PAGE_SIZE"
+ if test "$PAGESIZE" -le 0 ; then
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+ echo ""
+ echo "Testing with pagedResults control disabled"
+ echo ""
+ echo ">>>>> Test succeeded"
+ exit 0
+ fi
+else
+ PAGESIZE=5
+fi
+
+echo ""
+echo "Testing regular search limits with pagedResults control (page size $PAGESIZE)"
+echo ""
+
+echo "Testing no limits requested for unlimited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D 'cn=Unlimited User,ou=People,dc=example,dc=com' \
+ -E '!pr='$PAGESIZE'/noprompt' '(objectClass=*)' >$SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ echo "...success (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+SIZELIMIT=2
+echo "Testing size limit request ($SIZELIMIT) for unlimited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret -z $SIZELIMIT \
+ -D 'cn=Unlimited User,ou=People,dc=example,dc=com' \
+ -E '!pr='$PAGESIZE'/noprompt' '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ if test "$COUNT" -gt "$SIZELIMIT" ; then
+ echo "...error: got $COUNT entries instead of the requested $SIZELIMIT"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ echo "...didn't bump into the requested size limit ($SIZELIMIT; got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test x"$COUNT" != x ; then
+ if test $COUNT = $SIZELIMIT ; then
+ echo "...bumped into requested size limit ($SIZELIMIT)"
+ else
+ echo "...error: got $COUNT entries while requesting $SIZELIMIT..."
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ else
+ echo "...error: bumped into server-side size limit, but got no entries!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+TIMELIMIT=10
+echo "Testing time limit request ($TIMELIMIT s) for unlimited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret -l $TIMELIMIT \
+ -D 'cn=Unlimited User,ou=People,dc=example,dc=com' \
+ -E '!pr='$PAGESIZE'/noprompt' '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ echo "...didn't bump into the requested time limit ($TIMELIMIT s; got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 3)
+ if test x"$COUNT" = x ; then
+ COUNT=0
+ fi
+ echo "...bumped into requested time limit ($TIMELIMIT s; got $COUNT entries)"
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Testing no limits requested for soft limited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D 'cn=Soft Limited User,ou=People,dc=example,dc=com' \
+ -E '!pr='$PAGESIZE'/noprompt' '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ echo "...didn't bump into server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test x"$COUNT" != x ; then
+ echo "...bumped into server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: bumped into server-side size limit, but got no entries!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+SIZELIMIT=2
+echo "Testing lower than soft limit request ($SIZELIMIT) for soft limited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret -z $SIZELIMIT \
+ -D 'cn=Soft Limited User,ou=People,dc=example,dc=com' \
+ -E '!pr='$PAGESIZE'/noprompt' '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ if test "$COUNT" -gt "$SIZELIMIT" ; then
+ echo "...error: got $COUNT entries instead of the requested $SIZELIMIT"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ echo "...didn't bump into either requested ($SIZELIMIT) or server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test "x$COUNT" != "x" ; then
+ if test "x$SIZELIMIT" = "x$COUNT" ; then
+ echo "...bumped into requested ($SIZELIMIT) size limit"
+ else
+ echo "...bumped into server-side size limit ($COUNT)"
+ fi
+ else
+ echo "...bumped into either requested ($SIZELIMIT) or server-side size limit"
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+SIZELIMIT=100
+echo "Testing higher than soft limit request ($SIZELIMIT) for soft limited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret -z $SIZELIMIT \
+ -D 'cn=Soft Limited User,ou=People,dc=example,dc=com' \
+ -E '!pr='$PAGESIZE'/noprompt' '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ if test "$COUNT" -gt "$SIZELIMIT" ; then
+ echo "...error: got $COUNT entries instead of the requested $SIZELIMIT"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ echo "...didn't bump into either requested ($SIZELIMIT) or server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test "x$COUNT" != "x" ; then
+ if test "x$SIZELIMIT" = "x$COUNT" ; then
+ echo "...bumped into requested ($SIZELIMIT) size limit"
+ else
+ echo "...bumped into server-side size limit ($COUNT)"
+ fi
+ else
+ echo "...bumped into either requested ($SIZELIMIT) or server-side size limit"
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+SIZELIMIT=2
+echo "Testing lower than hard limit request ($SIZELIMIT) for hard limited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret -z $SIZELIMIT \
+ -D 'cn=Hard Limited User,ou=People,dc=example,dc=com' \
+ -E '!pr='$PAGESIZE'/noprompt' '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ if test "$COUNT" -gt "$SIZELIMIT" ; then
+ echo "...error: got $COUNT entries instead of the requested $SIZELIMIT"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ echo "...didn't bump into either requested ($SIZELIMIT) or server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test x"$COUNT" != x ; then
+ if test "$COUNT" = "$SIZELIMIT" ; then
+ echo "...bumped into requested ($SIZELIMIT) size limit"
+ else
+ echo "...error: got size limit $SIZELIMIT but $COUNT entries"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ else
+ echo "...error: bumped into server-side size limit, but got no entries!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+SIZELIMIT=100
+echo "Testing higher than hard limit request ($SIZELIMIT) for hard limited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret -z $SIZELIMIT \
+ -D 'cn=Hard Limited User,ou=People,dc=example,dc=com' \
+ -E '!pr='$PAGESIZE'/noprompt' '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ if test "$COUNT" -gt "$SIZELIMIT" ; then
+ echo "...error: got $COUNT entries instead of the requested $SIZELIMIT"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ echo "...didn't bump into either requested ($SIZELIMIT) or server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test x"$COUNT" != x ; then
+ if test "$COUNT" = "$SIZELIMIT" ; then
+ echo "...error: bumped into requested ($SIZELIMIT) size limit"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ else
+ echo "...got size limit $COUNT instead of requested $SIZELIMIT entries"
+ fi
+ else
+ echo "...error: bumped into server-side size limit, but got no entries!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ ;;
+# 11)
+# echo "...bumped into hard size administrative limit"
+# ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+SIZELIMIT=max
+echo "Testing max limit request ($SIZELIMIT) for hard limited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret -z $SIZELIMIT \
+ -D 'cn=Hard Limited User,ou=People,dc=example,dc=com' \
+ -E '!pr='$PAGESIZE'/noprompt' '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ echo "...didn't bump into either requested ($SIZELIMIT) or server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test x"$COUNT" != x ; then
+ echo "...bumped into requested ($SIZELIMIT=$COUNT) size limit"
+ else
+ echo "...error: bumped into size limit but got no entries!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ ;;
+# 11)
+# echo "...bumped into hard size administrative limit"
+# ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Testing lower than unchecked limit request for unchecked limited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D 'cn=Unchecked Limited User,ou=People,dc=example,dc=com' \
+ -E '!pr='$PAGESIZE'/noprompt' '(uid=uncheckedlimited)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ echo "...success; didn't bump into server-side unchecked limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+# 11)
+# echo "...bumped into unchecked administrative limit"
+# ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Testing higher than unchecked limit requested for unchecked limited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D 'cn=Unchecked Limited User,ou=People,dc=example,dc=com' \
+ -E '!pr='$PAGESIZE'/noprompt' '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ echo "...error: didn't bump into server-side unchecked limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ fi
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+ 11)
+ echo "...bumped into unchecked administrative limit"
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo ""
+echo "Testing specific search limits with pagedResults control"
+echo ""
+
+echo "Testing no limit requested for unlimited page size ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D 'cn=Unlimited User,ou=Paged Results Users,dc=example,dc=com' \
+ -E '!pr='$PAGESIZE'/noprompt' '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ echo "...success; didn't bump into server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test x"$COUNT" != x ; then
+ echo "...bumped into server-side size limit (got $COUNT entries)"
+ else
+ echo "...error: bumped into server-side size limit, but got no entries!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Testing no limit requested for limited page size ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D 'cn=Page Size Limited User,ou=Paged Results Users,dc=example,dc=com' \
+ -E '!pr='$PAGESIZE'/noprompt' '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ echo "...success; didn't bump into server-side page size limit (got $COUNT entries)"
+ ;;
+ 4)
+ echo "...bumped into page size limit ($COUNT)"
+ ;;
+ 11)
+ echo "...bumped into page size administrative limit"
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Testing no limit requested for pagedResults disabled ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D 'cn=Paged Results Disabled User,ou=Paged Results Users,dc=example,dc=com' \
+ -E '!pr='$PAGESIZE'/noprompt' '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ echo "...success; didn't bump into server-side unchecked limit (got $COUNT entries)"
+ ;;
+ 4)
+ echo "...bumped into server-side size limit ($COUNT)"
+ ;;
+ 11)
+ echo "...bumped into pagedResults disabled administrative limit"
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Testing no limit requested for pagedResults total count limited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D 'cn=Paged Results Limited User,ou=Paged Results Users,dc=example,dc=com' \
+ -E '!pr='$PAGESIZE'/noprompt' '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ echo "...success; didn't bump into server-side unchecked limit (got $COUNT entries)"
+ ;;
+ 4)
+ echo "...bumped into server-side size limit ($COUNT)"
+ ;;
+ 11)
+ echo "...bumped into pagedResults total count administrative limit"
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+SIZELIMIT=8
+echo "Testing higher than hard but lower then total count limit requested for pagedResults total count limited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D 'cn=Paged Results Limited User,ou=Paged Results Users,dc=example,dc=com' \
+ -z $SIZELIMIT -E '!pr='$PAGESIZE'/noprompt' '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ echo "...success; didn't bump into either requested ($SIZELIMIT) or server-side unchecked limit (got $COUNT entries)"
+ ;;
+ 4)
+ if test "x$COUNT" != "x" ; then
+ if test "x$SIZELIMIT" = "x$COUNT" ; then
+ echo "...bumped into requested ($SIZELIMIT) size limit"
+ else
+ echo "...bumped into server-side size limit ($COUNT)"
+ fi
+ else
+ echo "...bumped into either requested ($SIZELIMIT) or server-side size limit"
+ fi
+ ;;
+ 11)
+ echo "...bumped into either hard size or pagedResults total count administrative limit"
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+SIZELIMIT=15
+echo "Testing higher than total count limit requested for pagedResults total count limited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D 'cn=Paged Results Limited User,ou=Paged Results Users,dc=example,dc=com' \
+ -z $SIZELIMIT -E '!pr='$PAGESIZE'/noprompt' '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ echo "...success; didn't bump into either requested ($SIZELIMIT) or server-side unchecked limit (got $COUNT entries)"
+ ;;
+ 4)
+ if test "x$COUNT" != "x" ; then
+ if test "x$SIZELIMIT" = "x$COUNT" ; then
+ echo "...bumped into requested ($SIZELIMIT) size limit"
+ else
+ echo "...bumped into server-side size limit ($COUNT)"
+ fi
+ else
+ echo "...bumped into either requested ($SIZELIMIT) or server-side size limit"
+ fi
+ ;;
+ 11)
+ echo "...bumped into pagedResults total count administrative limit"
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+SIZELIMIT=max
+echo "Testing max limit requested for pagedResults total count limited ID..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret \
+ -D 'cn=Paged Results Limited User,ou=Paged Results Users,dc=example,dc=com' \
+ -z $SIZELIMIT -E '!pr='$PAGESIZE'/noprompt' '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ echo "...success; didn't bump into either requested ($SIZELIMIT) or server-side unchecked limit (got $COUNT entries)"
+ ;;
+ 4)
+ if test "x$COUNT" != "x" ; then
+ if test "x$SIZELIMIT" = "x$COUNT" ; then
+ echo "...bumped into requested ($SIZELIMIT) size limit"
+ else
+ echo "...bumped into server-side size limit ($COUNT)"
+ fi
+ else
+ echo "...bumped into either requested ($SIZELIMIT) or server-side size limit"
+ fi
+ ;;
+ 11)
+ echo "...bumped into pagedResults total count administrative limit"
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+# ITS#4479
+PAGESIZE=1
+SIZELIMIT=2
+echo "Testing size limit request ($SIZELIMIT) for unlimited ID and pagesize=$PAGESIZE..."
+$LDAPRSEARCH -S "" -b "$BASEDN" -H $URI1 -w secret -z $SIZELIMIT \
+ -D 'cn=Unlimited User,ou=People,dc=example,dc=com' \
+ -E '!pr='$PAGESIZE'/noprompt' '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+COUNT=`awk '/^# numEntries:/ {print $3}' $SEARCHOUT`
+case $RC in
+ 0)
+ if test x"$COUNT" != x ; then
+ if test "$COUNT" -gt "$SIZELIMIT" ; then
+ echo "...error: got $COUNT entries instead of the requested $SIZELIMIT"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ echo "...didn't bump into the requested size limit ($SIZELIMIT; got $COUNT entries)"
+ else
+ echo "...error: did not expect ldapsearch success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ ;;
+ 4)
+ if test x"$COUNT" != x ; then
+ if test $COUNT = $SIZELIMIT ; then
+ echo "...bumped into requested size limit ($SIZELIMIT)"
+ else
+ echo "...error: got $COUNT entries while requesting $SIZELIMIT..."
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ else
+ echo "...error: bumped into server-side size limit, but got no entries!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ ;;
+ *)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test026-dn b/tests/scripts/test026-dn
new file mode 100755
index 0000000..3676139
--- /dev/null
+++ b/tests/scripts/test026-dn
@@ -0,0 +1,180 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2004-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1
+
+. $CONFFILTER $BACKEND < $DNCONF > $CONF1
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd DN parsing..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Loading database..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD -c -f $LDIFDN > \
+ $TESTOUT 2>&1
+
+cat /dev/null > $SEARCHOUT
+
+echo "Searching database..."
+echo "# Searching database..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+DN="OU=Sales+CN=J. Smith,DC=example,DC=net"
+echo "Searching database for DN=\"$DN\"..."
+echo "# Searching database for DN=\"$DN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "(member=$DN)" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+DN="testUUID=597ae2f6-16a6-1027-98f4-ABCDEFabcdef,DC=Example"
+echo "Searching database for entryUUID-named DN=\"$DN\"..."
+echo "# Searching database for entryUUID-named DN=\"$DN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "(member=$DN)" \
+ >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+DN="dc=example,dc=com"
+echo "Searching database for nameAndOptionalUID=\"$DN\"..."
+echo "# Searching database for nameAndOptionalUID=\"$DN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "(uniqueMember=$DN)" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+DN="dc=example,dc=com#'001000'B"
+echo "Searching database for nameAndOptionalUID=\"$DN\"..."
+echo "# Searching database for nameAndOptionalUID=\"$DN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "(uniqueMember=$DN)" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+DN="dc=example,dc=com#'1000'B"
+echo "Searching database for nameAndOptionalUID=\"$DN\"..."
+echo "# Searching database for nameAndOptionalUID=\"$DN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "(uniqueMember=$DN)" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+DN="dc=example,dc=com"
+echo "Searching database for uniqueMember~=\"$DN\" (approx)..."
+echo "# Searching database for uniqueMember~=\"$DN\" (approx)..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "(uniqueMember~=)" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+DN="dc=example,dc=com#'1000'B"
+echo "Searching database for uniqueMember~=\"$DN\" (approx)..."
+echo "# Searching database for uniqueMember~=\"$DN\" (approx)..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "(uniqueMember~=$DN)" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original data..."
+$LDIFFILTER < $DNOUT > $LDIFFLT
+echo "Comparing ldapsearch results against original..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - DN write operations did not complete correctly"
+ exit 1
+fi
+
+#####
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test027-emptydn b/tests/scripts/test027-emptydn
new file mode 100755
index 0000000..a1f1512
--- /dev/null
+++ b/tests/scripts/test027-emptydn
@@ -0,0 +1,175 @@
+#! /bin/sh
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2004-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+. $CONFFILTER $BACKEND < $EMPTYDNCONF > $CONF1
+
+echo "Running slapadd to build \"dc=example,dc=com\" slapd database..."
+$SLAPADD -f $CONF1 -n 1 -l $LDIFEMPTYDN1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Running slapadd to build empty DN slapd database..."
+$SLAPADD -f $CONF1 -b "" -l $LDIFEMPTYDN2
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd empty DN handling..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Searching database..."
+
+$LDAPSEARCH -S "" -b "" -H $URI1 > $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+kill -HUP $KILLPIDS
+wait
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected results..."
+$LDIFFILTER < $EMPTYDNOUT1 > $LDIFFLT
+echo "Comparing ldapsearch results against original..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - empty DN write operations did not complete correctly"
+ exit 1
+fi
+
+echo "Comparison of database generated via slapadd succeeded"
+
+echo "Cleaning up database directories..."
+/bin/rm -rf $TESTDIR/db.*
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd empty DN handling..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Loading database..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ -f $LDIFEMPTYDN1 > /dev/null 2>&1
+$LDAPADD -D "$EMPTYDNDN" -H $URI1 -w $PASSWD \
+ -f $LDIFEMPTYDN2 > /dev/null 2>&1
+
+$LDAPMODIFY -D "$EMPTYDNDN" -H $URI1 -w $PASSWD \
+ > /dev/null 2>&1 << EOF
+dn: o=Beispiel,c=DE
+changetype: delete
+
+dn: c=DE
+changetype: delete
+EOF
+
+echo "Searching database..."
+
+$LDAPSEARCH -S "" -b "" -H $URI1 > $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected results..."
+$LDIFFILTER < $EMPTYDNOUT2 > $LDIFFLT
+echo "Comparing ldapsearch results against original..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - empty DN write operations did not complete correctly"
+ exit 1
+fi
+
+#####
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test028-idassert b/tests/scripts/test028-idassert
new file mode 100755
index 0000000..76fb5c8
--- /dev/null
+++ b/tests/scripts/test028-idassert
@@ -0,0 +1,273 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+echo "### This test requires the LDAP backend and the rwm overlay."
+echo "### If available, and explicitly requested, it can use SASL bind;"
+echo "### note that SASL must be properly set up, and the requested"
+echo "### mechanism must be available. Define SLAPD_USE_SASL={yes|<mech>},"
+echo "### with \"yes\" defaulting to DIGEST-MD5 to enable SASL authc[/authz]."
+
+if test $BACKLDAP = "ldapno" ; then
+ echo "LDAP backend not available, test skipped"
+ exit 0
+fi
+
+if test $RWM = "rwmno" ; then
+ echo "Rewrite/remap overlay not available, test skipped"
+ exit 0
+fi
+
+if test $WITH_SASL = "yes" ; then
+ if test $USE_SASL != "no" ; then
+ if test $USE_SASL = "yes" ; then
+ MECH="DIGEST-MD5"
+ else
+ MECH="$USE_SASL"
+ fi
+ echo "Using SASL authc[/authz] with mech=$MECH; unset SLAPD_USE_SASL to disable"
+ else
+ echo "Using proxyAuthz with simple authc..."
+ fi
+else
+ echo "SASL not available; using proxyAuthz with simple authc..."
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $IDASSERTCONF > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFIDASSERT1 -n 1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd -n 1 failed ($RC)!"
+ exit $RC
+fi
+$SLAPADD -f $ADDCONF -l $LDIFIDASSERT2 -n 2
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd -n 2 failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT..."
+. $CONFFILTER $BACKEND < $IDASSERTCONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+echo "Testing ldapwhoami as proxy US..."
+$LDAPWHOAMI -H $URI1 -D "cn=proxy US,ou=Admin,dc=example,dc=com" -w proxy
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+AUTHZID="u:it/jaj"
+echo "Testing ldapwhoami as proxy US, $AUTHZID..."
+$LDAPWHOAMI -H $URI1 -D "cn=proxy US,ou=Admin,dc=example,dc=com" -w proxy -e\!"authzid=$AUTHZID"
+RC=$?
+if test $RC != 0 && test $BACKEND != null ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+AUTHZID="u:bjorn"
+echo "Testing ldapwhoami as proxy US, $AUTHZID... (should fail)"
+$LDAPWHOAMI -H $URI1 -D "cn=proxy US,ou=Admin,dc=example,dc=com" -w proxy -e\!"authzid=$AUTHZID"
+RC=$?
+if test $RC != 1 ; then
+ echo "ldapwhoami should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+AUTHZID="u:bjensen"
+echo "Testing ldapwhoami as proxy US, $AUTHZID... (should fail)"
+$LDAPWHOAMI -H $URI1 -D "cn=proxy US,ou=Admin,dc=example,dc=com" -w proxy -e\!"authzid=$AUTHZID"
+RC=$?
+if test $RC != 1 ; then
+ echo "ldapwhoami should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing ldapwhoami as proxy IT..."
+$LDAPWHOAMI -H $URI1 -D "cn=proxy IT,ou=Admin,dc=example,dc=com" -w proxy
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+AUTHZID="u:it/jaj"
+echo "Testing ldapwhoami as proxy IT, $AUTHZID... (should fail)"
+$LDAPWHOAMI -H $URI1 -D "cn=proxy IT,ou=Admin,dc=example,dc=com" -w proxy -e\!"authzid=$AUTHZID"
+RC=$?
+if test $RC != 1 ; then
+ echo "ldapwhoami should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+AUTHZID="u:bjorn"
+echo "Testing ldapwhoami as proxy IT, $AUTHZID... (should fail)"
+$LDAPWHOAMI -H $URI1 -D "cn=proxy IT,ou=Admin,dc=example,dc=com" -w proxy -e\!"authzid=$AUTHZID"
+RC=$?
+if test $RC != 1 ; then
+ echo "ldapwhoami should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+AUTHZID="dn:cn=Sandbox,ou=Admin,dc=example,dc=com"
+echo "Testing ldapwhoami as proxy IT, $AUTHZID..."
+$LDAPWHOAMI -H $URI1 -D "cn=proxy IT,ou=Admin,dc=example,dc=com" -w proxy -e\!"authzid=$AUTHZID"
+RC=$?
+if test $RC != 0 && test $BACKEND != null ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+AUTHZID="dn:uid=bjorn,ou=People,o=Example,c=US"
+echo "Testing ldapwhoami as bjorn, $AUTHZID..."
+$LDAPWHOAMI -H $URI1 -D "uid=bjorn,ou=people,dc=example,dc=com" -w bjorn -e\!"authzid=$AUTHZID"
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+AUTHZID="dn:uid=bjorn,ou=People,o=Esempio,c=IT"
+echo "Testing ldapwhoami as bjorn, $AUTHZID..."
+$LDAPWHOAMI -H $URI1 -D "uid=bjorn,ou=people,dc=example,dc=com" -w bjorn -e\!"authzid=$AUTHZID"
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+AUTHZID="u:it/jaj"
+echo "Checking another DB's rootdn can't assert identity from another DB..."
+$LDAPWHOAMI -H $URI1 -D "$MANAGERDN" -w $PASSWD -e\!"authzid=$AUTHZID"
+
+RC=$?
+if test $RC != 1 ; then
+ echo "ldapwhoami should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+ID="uid=jaj,ou=People,dc=example,dc=it"
+BASE="o=Example,c=US"
+echo "Testing ldapsearch as $ID for \"$BASE\"..."
+$LDAPSEARCH -H $URI1 -b "$BASE" \
+ -D "$ID" -w jaj > $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 && test $BACKEND != null ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s ldif=e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER -s ldif=e < $IDASSERTOUT > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - search with identity assertion didn't succeed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+if test $USE_SASL != "no" ; then
+ ID="it/jaj"
+ BASE="o=Example,c=US"
+ echo "Testing ldapsearch as $ID for \"$BASE\" with SASL bind and identity assertion..."
+ $LDAPSASLSEARCH -H $URI1 -b "$BASE" \
+ -Q -U "$ID" -w jaj -Y $MECH > $SEARCHOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ ID="manager"
+ AUTHZID="u:it/jaj"
+ echo "Checking another DB's rootdn can't assert in another (with SASL bind this time)..."
+ $LDAPSASLWHOAMI -H $URI1 \
+ -Q -U "$ID" -w $PASSWD -Y $MECH -X $AUTHZID
+
+ RC=$?
+ if test $RC != 50 ; then
+ echo "ldapwhoami should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+
+ echo "Filtering ldapsearch results..."
+ $LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+ echo "Filtering original ldif used to create database..."
+ $LDIFFILTER < $IDASSERTOUT > $LDIFFLT
+ echo "Comparing filter output..."
+ $CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+ if test $? != 0 ; then
+ echo "comparison failed - search with SASL bind and identity assertion didn't succeed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test029-ldapglue b/tests/scripts/test029-ldapglue
new file mode 100755
index 0000000..f0ad581
--- /dev/null
+++ b/tests/scripts/test029-ldapglue
@@ -0,0 +1,224 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+echo "### This test requires the ldap backend and glue overlay."
+echo "### If available, and explicitly requested, it can use SASL bind;"
+echo "### note that SASL must be properly set up, and the requested"
+echo "### mechanism must be available. Define SLAPD_USE_SASL={yes|<mech>},"
+echo "### with \"yes\" defaulting to DIGEST-MD5 to enable SASL authc[/authz]."
+
+if test $BACKLDAP = "ldapno" ; then
+ echo "LDAP backend not available, test skipped"
+ exit 0
+fi
+
+if test $WITH_SASL = "yes" ; then
+ if test $USE_SASL != "no" ; then
+ if test $USE_SASL = "yes" ; then
+ MECH="DIGEST-MD5"
+ else
+ MECH="$USE_SASL"
+ fi
+ echo "Using SASL authc[/authz] with mech=$MECH; unset SLAPD_USE_SASL to disable"
+ else
+ echo "Using proxyAuthz with simple authc..."
+ fi
+else
+ echo "SASL not available; using proxyAuthz with simple authc..."
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2 $DBDIR3
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $LDAPGLUECONF1 > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFLDAPGLUE1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd 1 failed ($RC)!"
+ exit $RC
+fi
+
+. $CONFFILTER $BACKEND < $LDAPGLUECONF2 > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFLDAPGLUE2
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd 2 failed ($RC)!"
+ exit $RC
+fi
+
+. $CONFFILTER $BACKEND < $LDAPGLUECONF3 > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFLDAPGLUE3
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd 3 failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting local slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $LDAPGLUECONF1 > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID1=$!
+if test $WAIT != 0 ; then
+ echo PID $PID1
+ read foo
+fi
+
+echo "Starting remote slapd 1 on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $LDAPGLUECONF2 > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID2=$!
+if test $WAIT != 0 ; then
+ echo PID $PID2
+ read foo
+fi
+
+echo "Starting remote slapd 2 on TCP/IP port $PORT3..."
+. $CONFFILTER $BACKEND < $LDAPGLUECONF3 > $CONF3
+$SLAPD -f $CONF3 -h $URI3 -d $LVL > $LOG3 2>&1 &
+PID3=$!
+if test $WAIT != 0 ; then
+ echo PID $PID3
+ read foo
+fi
+KILLPIDS="$PID1 $PID2 $PID3"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI3 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+ID="uid=bjorn,ou=People,dc=example,dc=com"
+BASE="dc=example,dc=com"
+echo "Testing ldapsearch as $ID for \"$BASE\"..."
+$LDAPSEARCH -H $URI1 -b "$BASE" \
+ -D "$ID" -w bjorn > $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s ldif=e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER -s ldif=e < $LDAPGLUEOUT > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - glued search with identity assertion didn't succeed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+BASE="dc=example,dc=com"
+echo "Testing ldapsearch as anonymous for \"$BASE\"..."
+$LDAPSEARCH -H $URI1 -b "$BASE" \
+ > $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDAPGLUEANONYMOUSOUT > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - anonymous glued search with identity assertion didn't succeed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+# FIXME: this cannot work as is, because SASL bind cannot be proxied!
+if test $USE_SASL != "no" ; then
+ ID="bjorn"
+ BASE="dc=example,dc=com"
+ echo "Testing ldapsearch as $ID for \"$BASE\" with SASL bind and identity assertion..."
+ $LDAPSASLSEARCH -H $URI1 -b "$BASE" \
+ -Q -U "$ID" -w bjorn -Y $MECH > $SEARCHOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Filtering ldapsearch results..."
+ $LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+ echo "Filtering original ldif used to create database..."
+ $LDIFFILTER < $LDAPGLUEOUT > $LDIFFLT
+ echo "Comparing filter output..."
+ $CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+ if test $? != 0 ; then
+ echo "comparison failed - glued search with SASL bind and identity assertion didn't succeed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test030-relay b/tests/scripts/test030-relay
new file mode 100755
index 0000000..1ce5250
--- /dev/null
+++ b/tests/scripts/test030-relay
@@ -0,0 +1,98 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $RWM = rwmno ; then
+ echo "rwm (Rewrite/remap) overlay not available, test skipped"
+ exit 0
+fi
+
+echo ""
+
+if test "x$RELAYS" = "x" ; then
+ RELAYS=
+ # back-relay
+ if test $BACKRELAY = relayno ; then
+ echo "relay backend not available, test skipped"
+ else
+ if test "x$RELAYS" != "x" ; then
+ RELAYS="${RELAYS} "
+ fi
+ RELAYS="${RELAYS}relay"
+ fi
+
+ # back-ldap
+ if test $BACKLDAP = ldapno ; then
+ echo "ldap backend not available, test skipped"
+ else
+ if test "x$RELAYS" != "x" ; then
+ RELAYS="${RELAYS} "
+ fi
+ RELAYS="${RELAYS}ldap"
+ fi
+
+ # back-meta
+ if test $BACKMETA = metano ; then
+ echo "meta backend not available, test skipped"
+ else
+ if test "x$RELAYS" != "x" ; then
+ RELAYS="${RELAYS} "
+ fi
+ RELAYS="${RELAYS}meta"
+ fi
+fi
+
+if test "x$RELAYS" = "x" ; then
+ echo "no relaying capable backend is available"
+ echo ">>>>> Test succeeded"
+ exit 0
+fi
+
+echo "Testing virtual naming context mapping with $RELAYS backend(s)..."
+echo ""
+
+tmpfile=savelog.log
+if test -f $tmpfile ; then
+ rm -f $tmpfile
+fi
+first=1
+for RELAY in $RELAYS ; do
+ if test $first = 1 ; then
+ first=0
+ else
+ echo ">>>>> waiting for things to exit"
+ test $KILLSERVERS != no && wait
+ echo ""
+
+ mv -f $LOG1 $tmpfile
+ rm -rf $TESTDIR
+ fi
+
+ mkdir -p $TESTDIR $DBDIR1
+
+ if test -f $tmpfile ; then
+ mv $tmpfile $LOG1
+ fi
+
+ . $SRCDIR/scripts/relay
+done
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test031-component-filter b/tests/scripts/test031-component-filter
new file mode 100755
index 0000000..b289054
--- /dev/null
+++ b/tests/scripts/test031-component-filter
@@ -0,0 +1,330 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+## If you use this script then
+## Make sure that you turn on LDAP_COMP_MATCH in slapd source codes
+## and --enable-modules is configured yes
+if test "$AC_WITH_MODULES_ENABLED" != "yes" ; then
+ echo "dynamic module disabled, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1
+
+## Make sure that you set a proper path to component matching
+## module directory in $COMPCONF
+## moduleload path/to/component/library/compmatch.la
+## otherwise it fails to execute slapd
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $COMPCONF > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFCOMPMATCH
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ echo "Be sure to have a certificate module in tests/data/comp_libs "
+ echo "The module is in openldap/contrib/slapd-modules/comp_match"
+ echo "Test skipped."
+ exit 0
+fi
+
+echo "Running slapindex to index slapd database..."
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+$SLAPINDEX -f $CONF1
+RC=$?
+if test $RC != 0 ; then
+ echo "warning: slapindex failed ($RC)"
+ echo " assuming no indexing support"
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $ADDCONF -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+echo "Testing Component Filter Match RFC3687 Certificate searching:"
+echo "# Testing Component Filter Match RFC3687 Certificate searching:" >> $SEARCHOUT
+
+FILTER="(userCertificate:componentFilterMatch:=item:{ component \"toBeSigned.serialNumber\", rule allComponentsMatch, value 0 })"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "$FILTER" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+FILTER="(userCertificate:componentFilterMatch:=item:{ component \"toBeSigned.version\", rule allComponentsMatch, value 2 })"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "$FILTER" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+FILTER="(userCertificate:componentFilterMatch:=item:{ component \"toBeSigned.issuer.rdnSequence.1.1.value\", rule caseExactMatch, value \"US\" })"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "$FILTER" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+FILTER="(userCertificate:componentFilterMatch:=item:{ component \"toBeSigned.issuer.rdnSequence.1.1.value\", rule allComponentsMatch, value \"US\" })"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "$FILTER" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+FILTER="(userCertificate:componentFilterMatch:=item:{ component \"toBeSigned.issuer.rdnSequence\", rule allComponentsMatch, value { { { type 2.5.4.6 , value \"US\" } } } })"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "$FILTER" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+FILTER="(userCertificate:componentFilterMatch:=item:{ component \"toBeSigned.extensions.0\", rule integerMatch, value 3 })"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "$FILTER" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+FILTER="(userCertificate:componentFilterMatch:=item:{ component \"toBeSigned.extensions.\2a.extnID\", rule allComponentsMatch, value 2.5.29.14 })"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "$FILTER" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+FILTER="(userCertificate:componentFilterMatch:=not:item:{ component \"toBeSigned.extensions.\2a\", rule allComponentsMatch, value { extnID 2.5.29.19 , extnValue '30030101FF'H })"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "$FILTER" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+FILTER="(userCertificate:componentFilterMatch:=item:{ component \"toBeSigned.issuer.rdnSequence\", rule distinguishedNameMatch, value \"c=US\" })"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "$FILTER" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+FILTER="(userCertificate:componentFilterMatch:=item:{ component \"toBeSigned.issuer.rdnSequence.1\", rule rdnMatch, value \"c=US\" })"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "$FILTER" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+FILTER="(userCertificate:componentFilterMatch:=item:{ component \"toBeSigned.extensions.\2a.extnValue.content.\282.5.29.35\29.authorityCertSerialNumber\", rule integerMatch, value 0 })"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "$FILTER" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+FILTER="(userCertificate:componentFilterMatch:=item:{ component \"toBeSigned.subject.rdnSequence.\2a\", rule rdnMatch, value \"c=US\" })"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "$FILTER" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+FILTER="(userCertificate:componentFilterMatch:=item:{ component \"toBeSigned.subject.rdnSequence.\2a.\2a.value.\282.5.4.6\29\", rule caseExactMatch, value \"US\" })"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "$FILTER" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# extraction filter
+FILTER="(x509CertificateIssuer=c=US)"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "$FILTER" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# extraction filter
+FILTER="(x509CertificateSerial=0)"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "$FILTER" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# extraction filter
+FILTER="(x509CertificateSerialAndIssuer:certificateExactMatch:=0\$c=US)"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "$FILTER" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+FILTER="(certificateRevocationList:componentFilterMatch:=item:{ component \"tbsCertList.revokedCertificates.\2a.userCertificate\", rule integerMatch, value 952069669 })"
+echo " f=$FILTER ..."
+echo "# f=$FILTER ..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ "$FILTER" >> $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $COMPSEARCHOUT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test032-chain b/tests/scripts/test032-chain
new file mode 100755
index 0000000..3da9a24
--- /dev/null
+++ b/tests/scripts/test032-chain
@@ -0,0 +1,340 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $BACKLDAP = "ldapno" ; then
+ echo "LDAP backend not available, test skipped"
+ exit 0
+fi
+
+rm -rf $TESTDIR
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $CHAINCONF1 > $ADDCONF
+. $CONFFILTER < $LDIFCHAIN1 > $SEARCHOUT
+$SLAPADD -f $ADDCONF -l $SEARCHOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd 1 failed ($RC)!"
+ exit $RC
+fi
+
+. $CONFFILTER $BACKEND < $CHAINCONF2 > $ADDCONF
+. $CONFFILTER < $LDIFCHAIN2 > $SEARCHOUT
+$SLAPADD -f $ADDCONF -l $SEARCHOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd 2 failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting first slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $CHAINCONF1 > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID1=$!
+if test $WAIT != 0 ; then
+ echo PID $PID1
+ read foo
+fi
+KILLPIDS="$PID1"
+
+echo "Starting second slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $CHAINCONF2 > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID2=$!
+if test $WAIT != 0 ; then
+ echo PID $PID2
+ read foo
+fi
+
+KILLPIDS="$KILLPIDS $PID2"
+
+sleep 1
+
+echo "Using ldapsearch to check that first slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check that second slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+for n in 1 2 ; do
+ URI=`eval echo '$URI'$n`
+ echo "Testing ldapsearch as anonymous for \"$BASEDN\" on server $n..."
+ $LDAPSEARCH -H $URI -b "$BASEDN" -S "" \
+ > $SEARCHOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Filtering ldapsearch results..."
+ $LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+ echo "Filtering original ldif used to create database..."
+ $LDIFFILTER < $CHAINOUT > $LDIFFLT
+ echo "Comparing filter output..."
+ $CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+ if test $? != 0 ; then
+ echo "comparison failed - chained search didn't succeed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+
+ echo "Reading the referral entry \"ou=Other,$BASEDN\" as anonymous on server $n..."
+ $LDAPSEARCH -H $URI -b "ou=Other,$BASEDN" -S "" \
+ > $SEARCHOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Filtering ldapsearch results..."
+ $LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+ echo "Filtering original ldif used to create database..."
+ $LDIFFILTER < $CHAINREFOUT > $LDIFFLT
+ echo "Comparing filter output..."
+ $CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+ if test $? != 0 ; then
+ echo "comparison failed - chained search didn't succeed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+
+ DN="cn=Mark Elliot,ou=Alumni Association,ou=People,$BASEDN"
+ echo "Comparing \"$DN\" on server $n..."
+ $LDAPCOMPARE -H $URI "$DN" "cn:Mark Elliot" \
+ > $TESTOUT 2>&1
+
+ RC=$?
+ if test $RC != 6 && test $RC,$BACKEND != 5,null ; then
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+
+ DN="ou=Other,$BASEDN"
+ echo "Comparing \"$DN\" on server $n with manageDSAit control..."
+ $LDAPCOMPARE -H $URI -M "$DN" "ou:Other" \
+ > $TESTOUT 2>&1
+
+ RC=$?
+ if test $RC != 6 && test $RC,$BACKEND != 5,null ; then
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+done
+
+#
+# Testing writes to first server
+#
+echo "Writing to first server with scope on second server..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=New Group,ou=Groups,dc=example,dc=com
+changetype: add
+objectClass: groupOfNames
+cn: New Group
+member:
+
+dn: cn=New Group,ou=Groups,dc=example,dc=com
+changetype: modify
+add: description
+description: testing chain overlay writes...
+-
+replace: member
+member: cn=New Group,ou=Groups,dc=example,dc=com
+member: cn=Manager,dc=example,dc=com
+-
+add: owner
+owner: cn=Manager,dc=example,dc=com
+-
+
+dn: cn=New Group,ou=Groups,dc=example,dc=com
+changetype: modrdn
+newrdn: cn=Renamed Group
+deleteoldrdn: 1
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+changetype: delete
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#
+# Testing writes to second server
+#
+echo "Writing to second server with scope on first server..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI2 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=New User,ou=People,dc=example,dc=com
+changetype: add
+objectClass: person
+cn: New User
+sn: User
+seeAlso: cn=New Group,ou=Groups,dc=example,dc=com
+
+dn: cn=New User,ou=People,dc=example,dc=com
+changetype: modify
+add: description
+description: testing chain overlay writes...
+-
+replace: seeAlso
+seeAlso: cn=Renamed Group,ou=Groups,dc=example,dc=com
+-
+
+dn: cn=New User,ou=People,dc=example,dc=com
+changetype: modrdn
+newrdn: cn=Renamed User
+deleteoldrdn: 1
+
+dn: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: delete
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+for n in 1 2 ; do
+ URI=`eval echo '$URI'$n`
+ echo "Testing ldapsearch as anonymous for \"$BASEDN\" on server $n..."
+ $LDAPSEARCH -H $URI -b "$BASEDN" -S "" \
+ > $SEARCHOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Filtering ldapsearch results..."
+ $LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+ echo "Filtering original ldif used to create database..."
+ $LDIFFILTER < $CHAINMODOUT > $LDIFFLT
+ echo "Comparing filter output..."
+ $CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+ if test $? != 0 ; then
+ echo "comparison failed - chained search didn't succeed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+done
+
+NEWPW=newsecret
+echo "Using ldappasswd on second server with scope on first server..."
+$LDAPPASSWD -H $URI2 \
+ -w secret -s $NEWPW \
+ -D "$MANAGERDN" "$BJORNSDN" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldappasswd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Binding with newly changed password on first server..."
+$LDAPWHOAMI -H $URI1 \
+ -D "$BJORNSDN" -w $NEWPW
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# ITS#57??
+$LDAPADD -H $URI1 \
+ -D "$MANAGERDN" -w secret \
+ >> $TESTOUT 2>&1 \
+ << EOMODS
+dn: ou=Can't Contact,dc=example,dc=com
+changetype: add
+objectclass: referral
+objectclass: extensibleobject
+ou: Can't Contact
+# invalid URI to test broken connectivity handling (search only)
+ref: ${URI3}ou=Can't%20Contact,dc=example,dc=com
+EOMODS
+
+echo "Reading the referral entry \"ou=Can't Contact,$BASEDN\" as anonymous on port $PORT1..."
+$LDAPSEARCH -H $URI1 -b "$BASEDN" -S "" "(cn=Can't Contact)" \
+ > $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test033-glue-syncrepl b/tests/scripts/test033-glue-syncrepl
new file mode 100755
index 0000000..c54e77d
--- /dev/null
+++ b/tests/scripts/test033-glue-syncrepl
@@ -0,0 +1,189 @@
+#! /bin/sh
+# $OpenLDAP$ */
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1A $DBDIR1B $DBDIR1C $DBDIR2A $DBDIR2B
+
+echo "Running slapadd to build glued slapd databases..."
+. $CONFFILTER $BACKEND < $GLUECONF > $CONF1
+$SLAPADD -d $LVL -f $CONF1 -l $LDIFORDERED > $SLAPADDLOG1 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+rm -rf $DBDIR1A/* $DBDIR1B/*
+cp -pr $DBDIR1C $DBDIR2C
+
+echo "Starting slapd 1 on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $GLUESYNCCONF1 > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd 1 is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+echo "Starting slapd 2 on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $GLUESYNCCONF2 > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd 2 is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+SUBTREE1="ou=Information Technology Division,ou=People,dc=example,dc=com"
+SUBTREE2="ou=Groups,dc=example,dc=com"
+
+echo "Using ldapadd to populate subtree=\"${SUBTREE1}\" on port $PORT1..."
+$LDAPADD -D "cn=Manager 1,$BASEDN" -w $PASSWD -H $URI1 \
+ -f $LDIFORDERED -c \
+ > /dev/null 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapadd should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+10|68)
+ # Fine if we get alreadyExists or referrals
+ ;;
+*)
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Using ldapadd to populate subtree=\"${SUBTREE2}\" on port $PORT2..."
+$LDAPADD -D "cn=Manager 2,$BASEDN" -w $PASSWD -H $URI2 \
+ -f $LDIFORDERED -c \
+ > /dev/null 2>&1
+RC=$?
+case $RC in
+0)
+ echo "ldapadd should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+10|68)
+ # Fine if we get alreadyExists or referrals
+ ;;
+*)
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Waiting $SLEEP1 seconds for shadow subtrees to sync..."
+sleep $SLEEP1
+
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $GLUESYNCOUT > $LDIFFLT
+
+for n in 1 2 ; do
+ URI=`eval echo '$URI'$n`
+ echo "Using ldapsearch to read all the entries from server $n..."
+ $LDAPSEARCH -b "$BASEDN" -H $URI \
+ -S "" '(objectclass=*)' > "${SEARCHOUT}.${n}" 2>&1
+ RC=$?
+
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Filtering ldapsearch results..."
+ $LDIFFILTER < "${SEARCHOUT}.${n}" > $SEARCHFLT
+ echo "Comparing filter output..."
+ $CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+ if test $? != 0 ; then
+ echo "comparison failed - database was not created correctly"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+done
+
+echo "Testing ldapdelete propagation..."
+$LDAPDELETE -D "cn=Manager 1,$BASEDN" -w $PASSWD -H $URI1 "$BABSDN" \
+ > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapdelete failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# This usually propagates immediately
+sleep 1
+
+$LDAPSEARCH -H $URI2 -b "$BABSDN" > $TESTOUT 2>&1
+RC=$?
+if test $RC = 0 && test $BACKEND != null ; then
+ echo "ldapsearch should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test034-translucent b/tests/scripts/test034-translucent
new file mode 100755
index 0000000..8b834d9
--- /dev/null
+++ b/tests/scripts/test034-translucent
@@ -0,0 +1,807 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2004-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+PERSONAL="(objectClass=inetOrgPerson)"
+NOWHERE="/dev/null"
+FAILURE="additional info:"
+
+if test $TRANSLUCENT = translucentno ; then
+ echo "Translucent Proxy overlay not available, test skipped"
+ exit 0
+fi
+
+if test $AC_ldap = ldapno ; then
+ echo "Translucent Proxy overlay requires back-ldap backend, test skipped"
+ exit 0
+fi
+
+# configure backside
+mkdir -p $TESTDIR $DBDIR1
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf
+
+DBIX=2
+
+. $CONFFILTER $BACKEND < $TRANSLUCENTREMOTECONF > $CONF1
+echo "Running slapadd to build remote slapd database..."
+$SLAPADD -f $CONF1 -l $LDIFTRANSLUCENTCONFIG
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting remote slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+REMOTEPID="$PID"
+KILLPIDS="$PID"
+
+sleep 1
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for remote slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# configure frontside
+mkdir -p $DBDIR2
+
+. $CONFFILTER $BACKEND < $TRANSLUCENTLOCALCONF > $CONF2
+
+echo "Starting local slapd on TCP/IP port $PORT2..."
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+LOCALPID="$PID"
+KILLPIDS="$LOCALPID $REMOTEPID"
+
+sleep 1
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for local slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing slapd Translucent Proxy operations..."
+
+echo "Testing search: no remote data defined..."
+
+$LDAPSEARCH -H $URI2 -b "$TRANSLUCENTUSER" "$PERSONAL" >$SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if test -s $SEARCHOUT; then
+ echo "ldapsearch should have returned no records!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Populating remote database..."
+
+$LDAPADD -D "$TRANSLUCENTROOT" -H $URI1 \
+ -w $PASSWD < $LDIFTRANSLUCENTDATA > $NOWHERE 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing search: remote database via local slapd..."
+
+$LDAPSEARCH -H $URI2 -b "$TRANSLUCENTUSER" "$PERSONAL" > $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+$LDIFFILTER < $LDIFTRANSLUCENTDATA > $LDIFFLT
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed -- corruption from remote to local!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing add: prohibited local record..."
+
+$LDAPADD -D "$TRANSLUCENTDN" -H $URI2 \
+ -w $TRANSLUCENTPASSWD < $LDIFTRANSLUCENTADD > $TESTOUT 2>&1
+
+RC=$?
+if test $RC != 50 ; then
+ echo "ldapadd failed ($RC), expected INSUFFICIENT ACCESS!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing add: valid local record, no_glue..."
+
+$LDAPADD -v -v -v -D "$TRANSLUCENTROOT" -H $URI2 \
+ -w $PASSWD < $LDIFTRANSLUCENTADD > $TESTOUT 2>&1
+
+RC=$?
+if test $RC != 32 && test $RC,$BACKEND != 0,null ; then
+ echo "ldapadd failed ($RC), expected NO SUCH OBJECT!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing modrdn: valid local record, no_glue..."
+
+$LDAPMODRDN -D "$TRANSLUCENTROOT" -H $URI2 -w $PASSWD > \
+ $TESTOUT 2>&1 'uid=fred,ou=users,o=translucent' 'uid=someguy'
+
+RC=$?
+if test $RC != 32 && test $RC,$BACKEND != 0,null ; then
+ echo "ldapmodrdn failed ($RC), expected NO SUCH OBJECT!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Dynamically configuring local slapd without translucent_no_glue..."
+
+$LDAPMODIFY -D cn=config -H $URI2 -y $CONFIGPWF <<EOF
+dn: olcOverlay={0}translucent,olcDatabase={$DBIX}$BACKEND,cn=config
+changetype: modify
+replace: olcTranslucentNoGlue
+olcTranslucentNoGlue: FALSE
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify of dynamic config failed ($RC)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing add: valid local record..."
+
+$LDAPADD -D "$TRANSLUCENTROOT" -H $URI2 \
+ -w $PASSWD < $LDIFTRANSLUCENTADD > $TESTOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing search: data merging..."
+
+$LDAPSEARCH -H $URI2 -b "$TRANSLUCENTUSER" "$PERSONAL" > $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+$LDIFFILTER < $LDIFTRANSLUCENTMERGED > $LDIFFLT
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed -- local data failed to merge with remote!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing compare: valid local..."
+
+$LDAPCOMPARE -z -H $URI2 -w $TRANSLUCENTPASSWD -D $TRANSLUCENTDN \
+ "uid=danger,ou=users,o=translucent" "carLicense:LIVID"
+
+RC=$?
+if test $RC != 6 ; then
+ echo "ldapcompare failed ($RC), expected TRUE!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing compare: valid remote..."
+
+$LDAPCOMPARE -z -x -H $URI2 -w $TRANSLUCENTPASSWD -D $TRANSLUCENTDN \
+ "uid=binder,o=translucent" "businessCategory:binder-test-user"
+
+RC=$?
+if test $RC != 6 ; then
+ echo "ldapcompare failed ($RC), expected TRUE!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing compare: bogus local..."
+
+$LDAPCOMPARE -z -x -H $URI2 -w $TRANSLUCENTPASSWD -D $TRANSLUCENTDN \
+ "uid=danger,ou=users,o=translucent" "businessCategory:invalid-test-value"
+
+RC=$?
+if test $RC != 5 ; then
+ echo "ldapcompare failed ($RC), expected FALSE!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing compare: bogus remote..."
+
+$LDAPCOMPARE -z -x -H $URI2 -w $TRANSLUCENTPASSWD -D $TRANSLUCENTDN \
+ "uid=binder,o=translucent" "businessCategory:invalid-test-value"
+
+RC=$?
+if test $RC != 5 ; then
+ echo "ldapcompare failed ($RC), expected FALSE!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing modify: nonexistent record..."
+
+$LDAPMODIFY -v -D "$TRANSLUCENTROOT" -H $URI2 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF_MOD
+version: 1
+dn: uid=bogus,ou=users,o=translucent
+changetype: modify
+replace: roomNumber
+roomNumber: 31J-2112
+EOF_MOD
+
+RC=$?
+if test $RC != 32 ; then
+ echo "ldapmodify failed ($RC), expected NO SUCH OBJECT!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing modify: valid local record, nonexistent attribute..."
+
+$LDAPMODIFY -v -D "$TRANSLUCENTROOT" -H $URI2 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF_MOD1
+version: 1
+dn: uid=danger,ou=users,o=translucent
+changetype: modify
+replace: roomNumber
+roomNumber: 9N-21
+EOF_MOD1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -H $URI2 -b "uid=danger,ou=users,o=translucent" > $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ATTR=`grep roomNumber $SEARCHOUT` > $NOWHERE 2>&1
+if test "$ATTR" != "roomNumber: 9N-21" ; then
+ echo "modification failed!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing search: specific nonexistent remote attribute..."
+
+$LDAPSEARCH -H $URI2 -b "uid=danger,ou=users,o=translucent" roomNumber > $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing modify: nonexistent local record, nonexistent attribute..."
+
+$LDAPMODIFY -v -D "$TRANSLUCENTROOT" -H $URI2 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF_MOD2
+version: 1
+dn: uid=fred,ou=users,o=translucent
+changetype: modify
+replace: roomNumber
+roomNumber: 31J-2112
+EOF_MOD2
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -H $URI2 -b "uid=fred,ou=users,o=translucent" > $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ATTR=`grep roomNumber $SEARCHOUT` > $NOWHERE 2>&1
+if test "$ATTR" != "roomNumber: 31J-2112" ; then
+ echo "modification failed!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing modify: valid remote record, nonexistent attribute..."
+
+$LDAPMODIFY -v -D "$TRANSLUCENTROOT" -H $URI2 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF_MOD9
+version: 1
+dn: uid=fred,ou=users,o=translucent
+changetype: modify
+delete: preferredLanguage
+EOF_MOD9
+
+RC=$?
+if test $RC != 16 ; then
+ echo "ldapmodify failed ($RC), expected NO SUCH ATTRIBUTE!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing delete: valid local record, nonexistent attribute..."
+
+$LDAPMODIFY -v -D "$TRANSLUCENTROOT" -H $URI2 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF_MOD4
+version: 1
+dn: uid=fred,ou=users,o=translucent
+changetype: modify
+delete: roomNumber
+EOF_MOD4
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing modrdn: prohibited local record..."
+
+$LDAPMODRDN -D "$TRANSLUCENTDN" -H $URI2 -w $TRANSLUCENTPASSWD > \
+ $TESTOUT 2>&1 'uid=fred,ou=users,o=translucent' 'uid=someguy'
+
+RC=$?
+if test $RC != 50 ; then
+ echo "ldapmodrdn failed ($RC), expected INSUFFICIENT ACCESS!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing modrdn: valid local record..."
+
+$LDAPMODRDN -D "$TRANSLUCENTROOT" -H $URI2 -w $PASSWD > \
+ $TESTOUT 2>&1 'uid=fred,ou=users,o=translucent' 'uid=someguy'
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodrdn failed ($RC)!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing delete: prohibited local record..."
+
+$LDAPMODIFY -v -D "$TRANSLUCENTDN" -H $URI2 -w $TRANSLUCENTPASSWD > \
+ $TESTOUT 2>&1 << EOF_DEL2
+version: 1
+dn: uid=someguy,ou=users,o=translucent
+changetype: delete
+EOF_DEL2
+
+RC=$?
+if test $RC != 50 ; then
+ echo "ldapadd failed ($RC), expected INSUFFICIENT ACCESS!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing delete: valid local record..."
+
+$LDAPMODIFY -v -D "$TRANSLUCENTROOT" -H $URI2 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF_DEL3
+version: 1
+dn: uid=someguy,ou=users,o=translucent
+changetype: delete
+EOF_DEL3
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing delete: valid remote record..."
+
+$LDAPMODIFY -v -D "$TRANSLUCENTROOT" -H $URI2 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF_DEL8
+version: 1
+dn: uid=fred,ou=users,o=translucent
+changetype: delete
+EOF_DEL8
+
+RC=$?
+if test $RC != 32 ; then
+ echo "ldapmodify failed ($RC), expected NO SUCH OBJECT!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing delete: nonexistent local record, nonexistent attribute..."
+
+$LDAPMODIFY -v -D "$TRANSLUCENTROOT" -H $URI2 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF_DEL1
+version: 1
+dn: uid=fred,ou=users,o=translucent
+changetype: modify
+delete: roomNumber
+EOF_DEL1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -H $URI2 -b "uid=fred,ou=users,o=translucent" > $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing delete: valid local record, nonexistent attribute..."
+
+$LDAPMODIFY -v -D "$TRANSLUCENTROOT" -H $URI2 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF_MOD8
+version: 1
+dn: uid=danger,ou=users,o=translucent
+changetype: modify
+delete: preferredLanguage
+EOF_MOD8
+
+RC=$?
+if test $RC != 16 ; then
+ echo "ldapmodify failed ($RC), expected NO SUCH ATTRIBUTE!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing delete: valid local record, remote attribute..."
+
+$LDAPMODIFY -v -D "$TRANSLUCENTROOT" -H $URI2 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF_MOD8
+version: 1
+dn: uid=danger,ou=users,o=translucent
+changetype: modify
+delete: initials
+EOF_MOD8
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing modify: valid remote record, combination add-modify-delete..."
+
+$LDAPMODIFY -v -D "$TRANSLUCENTROOT" -H $URI2 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF_MOD6
+version: 1
+dn: uid=fred,ou=users,o=translucent
+changetype: modify
+delete: carLicense
+-
+add: preferredLanguage
+preferredLanguage: ISO8859-1
+-
+replace: employeeType
+employeeType: consultant
+EOF_MOD6
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -H $URI2 -b "uid=fred,ou=users,o=translucent" > $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ATTR=`grep employeeType $SEARCHOUT` > $NOWHERE 2>&1
+if test "$ATTR" != "employeeType: consultant" ; then
+ echo "modification failed!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+ATTR=`grep preferredLanguage $SEARCHOUT` > $NOWHERE 2>&1
+if test "$ATTR" != "preferredLanguage: ISO8859-1" ; then
+ echo "modification failed!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Dynamically configuring local slapd with translucent_no_glue and translucent_strict..."
+
+$LDAPMODIFY -D cn=config -H $URI2 -y $CONFIGPWF <<EOF
+dn: olcOverlay={0}translucent,olcDatabase={$DBIX}$BACKEND,cn=config
+changetype: modify
+replace: olcTranslucentNoGlue
+olcTranslucentNoGlue: TRUE
+-
+replace: olcTranslucentStrict
+olcTranslucentStrict: TRUE
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify of dynamic config failed ($RC)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing strict mode delete: nonexistent local attribute..."
+
+$LDAPMODIFY -v -D "$TRANSLUCENTROOT" -H $URI2 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF_MOD5
+version: 1
+dn: uid=example,ou=users,o=translucent
+changetype: modify
+delete: preferredLanguage
+EOF_MOD5
+
+RC=$?
+if test $RC != 19 ; then
+ echo "ldapmodify failed ($RC), expected CONSTRAINT VIOLATION!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing strict mode delete: nonexistent remote attribute..."
+
+$LDAPMODIFY -v -D "$TRANSLUCENTROOT" -H $URI2 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF_MOD3
+version: 1
+dn: uid=danger,ou=users,o=translucent
+changetype: modify
+delete: displayName
+EOF_MOD3
+
+RC=$?
+if test $RC != 19 ; then
+ echo "ldapmodify failed ($RC), expected CONSTRAINT VIOLATION!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing strict mode modify: combination add-modify-delete..."
+
+$LDAPMODIFY -v -D "$TRANSLUCENTROOT" -H $URI2 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOF_MOD6
+version: 1
+dn: uid=example,ou=users,o=translucent
+changetype: modify
+delete: carLicense
+-
+add: preferredLanguage
+preferredLanguage: ISO8859-1
+-
+replace: employeeType
+employeeType: consultant
+EOF_MOD6
+
+RC=$?
+if test $RC != 19 ; then
+ echo "ldapmodify failed ($RC), expected CONSTRAINT VIOLATION!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing invalid Bind request..."
+$LDAPWHOAMI -D "$TRANSLUCENTDN" -H $URI2 -w Wrong"$TRANSLUCENTPASSWD" > \
+ $TESTOUT 2>&1
+RC=$?
+if test $RC != 49 ; then
+ echo "ldapwhoami failed ($RC), expected INVALID CREDENTIALS!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+$LDAPWHOAMI -D "$TRANSLUCENTDN" -H $URI2 -w "$TRANSLUCENTPASSWD" > \
+ $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC), expected SUCCESS!"
+ grep "$FAILURE" $TESTOUT
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing search: unconfigured local filter..."
+$LDAPSEARCH -H $URI2 -b "o=translucent" "(employeeType=consultant)" > $SEARCHOUT 2>&1
+
+ATTR=`grep dn: $SEARCHOUT` > $NOWHERE 2>&1
+if test -n "$ATTR" ; then
+ echo "got result $ATTR, should have been no result"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Dynamically configuring local slapd with translucent_local..."
+
+$LDAPMODIFY -D cn=config -H $URI2 -y $CONFIGPWF <<EOF
+dn: olcOverlay={0}translucent,olcDatabase={$DBIX}$BACKEND,cn=config
+changetype: modify
+add: olcTranslucentLocal
+olcTranslucentLocal: employeeType
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify of dynamic config failed ($RC)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing search: configured local filter..."
+$LDAPSEARCH -H $URI2 -b "o=translucent" "(employeeType=consultant)" > $SEARCHOUT 2>&1
+
+ATTR=`grep dn: $SEARCHOUT` > $NOWHERE 2>&1
+if test -z "$ATTR" ; then
+ echo "got no result, should have found entry"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+$LDAPSEARCH -H $URI2 -b "o=translucent" "(employeeType=consult*)" > $SEARCHOUT 2>&1
+ATTR=`grep dn: $SEARCHOUT` > $NOWHERE 2>&1
+if test -z "$ATTR" ; then
+ echo "got no result, should have found entry"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing search: unconfigured remote filter..."
+$LDAPSEARCH -H $URI2 -b "o=translucent" "(|(employeeType=foo)(carlicense=right))" > $SEARCHOUT 2>&1
+
+ATTR=`grep dn: $SEARCHOUT` > $NOWHERE 2>&1
+if test -n "$ATTR" ; then
+ echo "got result $ATTR, should have been no result"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Dynamically configuring local slapd with translucent_remote..."
+
+$LDAPMODIFY -D cn=config -H $URI2 -y $CONFIGPWF <<EOF
+dn: olcOverlay={0}translucent,olcDatabase={$DBIX}$BACKEND,cn=config
+changetype: modify
+add: olcTranslucentRemote
+olcTranslucentRemote: carLicense
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify of dynamic config failed ($RC)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing search: configured remote filter..."
+$LDAPSEARCH -H $URI2 -b "o=translucent" "(|(employeeType=foo)(carlicense=right))" > $SEARCHOUT 2>&1
+
+ATTR=`grep dn: $SEARCHOUT` > $NOWHERE 2>&1
+if test -z "$ATTR" ; then
+ echo "got no result, should have found entry"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test035-meta b/tests/scripts/test035-meta
new file mode 100755
index 0000000..67f7cf2
--- /dev/null
+++ b/tests/scripts/test035-meta
@@ -0,0 +1,739 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+echo ""
+
+if test $BACKMETA = metano ; then
+ echo "meta backend not available, test skipped"
+ exit 0
+fi
+
+if test $BACKLDAP = ldapno ; then
+ echo "ldap backend not available, test skipped"
+ exit 0
+fi
+
+rm -rf $TESTDIR
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $METACONF1 > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDERED > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $METACONF2 > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$METAMANAGERDN" -H $URI2 -w $PASSWD < \
+ $LDIFMETA >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT3..."
+. $CONFFILTER $BACKEND < $METACONF > $CONF3
+$SLAPD -f $CONF3 -h $URI3 -d $LVL > $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI3 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+BASEDN="o=Example,c=US"
+echo "Searching base=\"$BASEDN\"..."
+echo "# searching base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+# ITS#4195: spurious matchedDN when the search scopes the main target,
+# and the searchBase is not present, so that target returns noSuchObject
+BASEDN="ou=Meta,o=Example,c=US"
+echo "Searching base=\"$BASEDN\"..."
+echo "# searching base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+#
+# Do some modifications
+#
+
+BASEDN="o=Example,c=US"
+echo "Modifying database \"$BASEDN\"..."
+$LDAPMODIFY -v -D "cn=Manager,$BASEDN" -H $URI3 -w $PASSWD \
+ -M >> $TESTOUT 2>&1 << EOMODS
+# These operations (updates with objectClass mapping) triggered ITS#3499
+dn: cn=Added Group,ou=Groups,$BASEDN
+changetype: add
+objectClass: groupOfNames
+objectClass: uidObject
+cn: Added Group
+member: cn=Added Group,ou=Groups,$BASEDN
+uid: added
+
+dn: cn=Another Added Group,ou=Groups,$BASEDN
+changetype: add
+objectClass: groupOfNames
+cn: Another Added Group
+member: cn=Added Group,ou=Groups,$BASEDN
+member: cn=Another Added Group,ou=Groups,$BASEDN
+
+dn: cn=Another Added Group,ou=Groups,$BASEDN
+changetype: modify
+add: objectClass
+objectClass: uidObject
+-
+add: uid
+uid: added
+-
+
+dn: cn=Added Group,ou=Groups,$BASEDN
+changetype: modify
+delete: objectClass
+objectClass: uidObject
+-
+delete: uid
+-
+
+dn: ou=Meta,$BASEDN
+changetype: modify
+add: description
+description: added to "ou=Meta,$BASEDN"
+-
+
+dn: ou=Who's going to handle this?,$BASEDN
+changetype: add
+objectClass: organizationalUnit
+ou: Who's going to handle this?
+description: added
+description: will be deleted
+
+dn: ou=Same as above,$BASEDN
+changetype: add
+objectClass: organizationalUnit
+ou: Same as above
+description: added right after "Who's going to handle this?"
+description: will be preserved
+
+dn: ou=Who's going to handle this?,$BASEDN
+changetype: delete
+
+dn: ou=Who's going to handle this?,ou=Meta,$BASEDN
+changetype: add
+objectClass: organizationalUnit
+ou: Who's going to handle this?
+description: added
+description: will be deleted
+
+dn: ou=Same as above,ou=Meta,$BASEDN
+changetype: add
+objectClass: organizationalUnit
+ou: Same as above
+description: added right after "Who's going to handle this?"
+description: will be preserved
+
+dn: cn=Added User,ou=Same as above,ou=Meta,$BASEDN
+changetype: add
+objectClass: inetOrgPerson
+cn: Added User
+sn: User
+userPassword: secret
+
+dn: ou=Who's going to handle this?,ou=Meta,$BASEDN
+changetype: delete
+EOMODS
+
+RC=$?
+#if test $RC != 0 ; then
+# echo "Modify failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Modify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Searching base=\"$BASEDN\"..."
+echo "# searching base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+BASEDN="o=Example,c=US"
+echo " base=\"$BASEDN\"..."
+echo "# base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" -M "$FILTER" '*' ref \
+ >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+BASEDN="o=Example,c=US"
+FILTER="(seeAlso=cn=all staff,ou=Groups,$BASEDN)"
+echo "Searching filter=\"$FILTER\""
+echo " attrs=\"seeAlso\""
+echo " base=\"$BASEDN\"..."
+echo "# searching filter=\"$FILTER\"" >> $SEARCHOUT
+echo "# attrs=\"seeAlso\"" >> $SEARCHOUT
+echo "# base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" "$FILTER" seeAlso \
+ >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+FILTER="(uid=example)"
+echo "Searching filter=\"$FILTER\""
+echo " attrs=\"uid\""
+echo " base=\"$BASEDN\"..."
+echo "# searching filter=\"$FILTER\"" >> $SEARCHOUT
+echo "# attrs=\"uid\"" >> $SEARCHOUT
+echo "# base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" "$FILTER" uid \
+ >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+FILTER="(member=cn=Another Added Group,ou=Groups,$BASEDN)"
+echo "Searching filter=\"$FILTER\""
+echo " attrs=\"member\""
+echo " base=\"$BASEDN\"..."
+echo "# searching filter=\"$FILTER\"" >> $SEARCHOUT
+echo "# attrs=\"member\"" >> $SEARCHOUT
+echo "# base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" "$FILTER" member \
+ >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Waiting 10 seconds for cached connections to timeout..."
+sleep 10
+
+echo "Searching with a timed out connection..."
+echo "# searching filter=\"$FILTER\"" >> $SEARCHOUT
+echo "# attrs=\"member\"" >> $SEARCHOUT
+echo "# base=\"$BASEDN\"" >> $SEARCHOUT
+echo "# with a timed out connection..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -D "cn=Manager,$BASEDN" -w $PASSWD \
+ -b "$BASEDN" "$FILTER" member \
+ >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+# NOTE: cannot send to $SEARCHOUT because the returned entries
+# are not predictable...
+echo "Checking server-enforced size limit..."
+echo "# Checking server-enforced size limit..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 \
+ -D "cn=Bjorn Jensen,ou=Information Technology Division,ou=People,$BASEDN" -w bjorn \
+ -b "$BASEDN" "(objectClass=*)" 1.1 \
+ >> $TESTOUT 2>&1
+RC=$?
+case $RC,$BACKEND in
+ 4,* | 0,null)
+ ;;
+ 0,*)
+ echo "Search should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+# NOTE: cannot send to $SEARCHOUT because the returned entries
+# are not predictable...
+echo "Checking client-requested size limit..."
+echo "# Checking client-requested size limit..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 \
+ -D "cn=Bjorn Jensen,ou=Information Technology Division,ou=People,$BASEDN" -w bjorn \
+ -b "$BASEDN" -z 2 "(objectClass=*)" 1.1 \
+ >> $TESTOUT 2>&1
+RC=$?
+case $RC,$BACKEND in
+ 4,* | 0,null)
+ ;;
+ 0,*)
+ echo "Search should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $METAOUT > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - meta search/modification didn't succeed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+BASEDN="o=Example,c=US"
+echo "Changing password to database \"$BASEDN\"..."
+$LDAPPASSWD -H $URI3 -D "cn=Manager,$BASEDN" -w $PASSWD \
+ -s $PASSWD "cn=Ursula Hampster,ou=Alumni Association,ou=People,$BASEDN" \
+ >> $TESTOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Passwd ExOp failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Passwd ExOp failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Binding with newly changed password to database \"$BASEDN\"..."
+$LDAPWHOAMI -H $URI3 \
+ -D "cn=Ursula Hampster,ou=Alumni Association,ou=People,$BASEDN" \
+ -w $PASSWD >> $TESTOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "WhoAmI failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ ;;
+ *)
+ echo "WhoAmI failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Binding as newly added user to database \"$BASEDN\"..."
+$LDAPWHOAMI -H $URI3 \
+ -D "cn=Added User,ou=Same as above,ou=Meta,$BASEDN" \
+ -w $PASSWD >> $TESTOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "WhoAmI failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ ;;
+ *)
+ echo "WhoAmI failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Changing password to database \"$BASEDN\"..."
+$LDAPPASSWD -H $URI3 -D "cn=Manager,$BASEDN" -w $PASSWD \
+ -s meta "cn=Added User,ou=Same as above,ou=Meta,$BASEDN" \
+ >> $TESTOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Passwd ExOp failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Passwd ExOp failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Binding with newly changed password to database \"$BASEDN\"..."
+$LDAPWHOAMI -H $URI3 \
+ -D "cn=Added User,ou=Same as above,ou=Meta,$BASEDN" \
+ -w meta >> $TESTOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "WhoAmI failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ ;;
+ *)
+ echo "WhoAmI failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Binding with incorrect password to database \"$BASEDN\"..."
+$LDAPWHOAMI -H $URI3 \
+ -D "cn=Added User,ou=Same as above,ou=Meta,$BASEDN" \
+ -w bogus >> $TESTOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "WhoAmI failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC,$BACKEND in
+ 0,null)
+ ;;
+ 0,*)
+ echo "WhoAmI should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+ 51,*)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ ;;
+ *)
+ ;;
+esac
+
+echo "Binding with non-existing user to database \"$BASEDN\"..."
+$LDAPWHOAMI -H $URI3 \
+ -D "cn=Non-existing User,ou=Same as above,ou=Meta,$BASEDN" \
+ -w bogus >> $TESTOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "WhoAmI failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC,$BACKEND in
+ 0,null)
+ ;;
+ 0,*)
+ echo "WhoAmI should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+ 51,*)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ ;;
+ *)
+ ;;
+esac
+
+echo "Comparing to database \"$BASEDN\"..."
+$LDAPCOMPARE -H $URI3 \
+ "cn=Another Added Group,ou=Groups,$BASEDN" \
+ "member:cn=Added Group,ou=Groups,$BASEDN" >> $TESTOUT 2>&1
+RC=$?
+#if test $RC != 6 ; then
+# echo "Compare failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit -1
+#fi
+case $RC,$BACKEND in
+ 6,* | 5,null)
+ ;;
+ 51,*)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ ;;
+ *)
+ echo "Compare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+esac
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test036-meta-concurrency b/tests/scripts/test036-meta-concurrency
new file mode 100755
index 0000000..46f5cfb
--- /dev/null
+++ b/tests/scripts/test036-meta-concurrency
@@ -0,0 +1,225 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+echo ""
+
+if test $BACKMETA = metano ; then
+ echo "meta backend not available, test skipped"
+ exit 0
+fi
+
+if test $BACKLDAP = ldapno ; then
+ echo "ldap backend not available, test skipped"
+ exit 0
+fi
+
+if test x$TESTLOOPS = x ; then
+ TESTLOOPS=50
+fi
+
+if test x$TESTCHILDREN = x ; then
+ TESTCHILDREN=20
+fi
+
+rm -rf $TESTDIR
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $METACONF1 > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDERED > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $METACONF2 > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$METAMANAGERDN" -H $URI2 -w $PASSWD < \
+ $LDIFMETA >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT3..."
+. $CONFFILTER $BACKEND < $METACONF > $CONF3
+$SLAPD -f $CONF3 -h $URI3 -d $LVL > $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI3 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+mkdir -p $TESTDIR/$DATADIR
+METABASEDN="o=Example,c=US"
+for f in $DATADIR/do_* ; do
+ sed -e "s;$BASEDN;$METABASEDN;" $f > $TESTDIR/$f
+done
+
+# add a read that matches only the local database, but selects
+# also the remote as candidate; this should be removed to compare
+# execution times with test008...
+for f in $TESTDIR/$DATADIR/do_read.* ; do
+ echo "ou=Meta,$METABASEDN" >> $f
+done
+
+# add a read that matches a referral in the local database only,
+# but selects also the remote as candidate; this should be removed
+# to compare execution times with test008...
+for f in $TESTDIR/$DATADIR/do_read.* ; do
+ echo "cn=Somewhere,ou=Meta,$METABASEDN" >> $f
+done
+
+# add a bind that resolves to a referral
+for f in $TESTDIR/$DATADIR/do_bind.* ; do
+ echo "cn=Foo,ou=Meta,$METABASEDN" >> $f
+ echo "bar" >> $f
+ echo "" >> $f
+ echo "" >> $f
+done
+
+# NOTE: copies do_* files from $TESTDIR/$DATADIR to $TESTDIR
+$MONITORDATA "$TESTDIR/$DATADIR" "$TESTDIR"
+
+BINDDN="cn=Manager,o=Local"
+PASSWD="secret"
+echo "Using tester for concurrent server access..."
+$SLAPDTESTER -P "$PROGDIR" -d "$TESTDIR" -H $URI3 \
+ -D "$BINDDN" -w $PASSWD -l $TESTLOOPS -j $TESTCHILDREN \
+ -r 20 -i '!REFERRAL' -i '*INVALID_CREDENTIALS' -SS
+RC=$?
+
+if test $RC != 0 ; then
+ echo "slapd-tester failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to retrieve all the entries..."
+$LDAPSEARCH -S "" -b "$METABASEDN" -H $URI3 \
+ 'objectClass=*' > $SEARCHOUT 2>&1
+RC=$?
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $METACONCURRENCYOUT > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - slapd-meta search/modification didn't succeed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test037-manage b/tests/scripts/test037-manage
new file mode 100755
index 0000000..9587593
--- /dev/null
+++ b/tests/scripts/test037-manage
@@ -0,0 +1,219 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $BACKEND = "ldif" ; then
+ echo "LDIF backend does not support relax control, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+$SLAPADD -f $CONF1 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd Manage operations..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing modify, add, and delete..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ -e \!relax > \
+ $TESTOUT 2>&1 << EOMODS
+version: 1
+#
+# Working Tests
+#
+
+#
+# ObjectClass tests
+#
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+# add obsolete auxiliary objectclass
+changetype: modify
+add: objectClass
+objectClass: obsoletePerson
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+# add obsolete attribute
+changetype: modify
+add: testObsolete
+testObsolete: TRUE
+
+#
+# create/modify timestamp test
+#
+
+dn: ou=Groups,dc=example,dc=com
+# change creatorsName
+changetype: modify
+replace: creatorsName
+creatorsName: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+# change modifiersName
+changetype: modify
+replace: modifiersName
+modifiersName: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+
+dn: dc=example,dc=com
+# change timestamps
+changetype: modify
+replace: modifyTimestamp
+modifyTimestamp: 19700101000000Z
+-
+replace: createTimestamp
+createTimestamp: 19700101000000Z
+-
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+# change entryUUID
+changetype: modify
+replace: entryUUID
+entryUUID: badbadba-dbad-1029-92f7-badbadbadbad
+
+dn: cn=All Staff,dc=example,dc=com
+changetype: add
+objectClass: groupOfNames
+cn: All Staff
+member:
+creatorsName: cn=Someone
+createTimestamp: 19700101000000Z
+modifiersName: cn=Someone Else
+modifyTimestamp: 19700101000000Z
+entryUUID: badbadef-dbad-1029-92f7-badbadbadbad
+
+#
+# Tests that did not work until ITS#5792
+#
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+# update structural object class of entry via objectClass replace
+changetype: modify
+replace: objectClass
+objectClass: obsoletePerson
+objectClass: testPerson
+-
+
+dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+# update structural object class of entry via objectClass add
+changetype: modify
+add: objectClass
+objectClass: testPerson
+-
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+# update structural object class of entry via objectClass delete/add
+changetype: modify
+delete: objectClass
+objectClass: OpenLDAPperson
+-
+add: objectClass
+objectClass: testPerson
+-
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to retrieve all the entries..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ 'objectClass=*' '*' creatorsName modifiersName > $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+$LDAPSEARCH -S "" -b "$BASEDN" -s base -H $URI1 \
+ 'objectClass=*' '*' creatorsName createTimestamp \
+ modifiersName modifyTimestamp >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(cn=All Staff)' '*' entryUUID >> $SEARCHOUT 2>&1
+RC=$?
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+LDIF=$MANAGEOUT
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - manage operations did not complete correctly"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test038-retcode b/tests/scripts/test038-retcode
new file mode 100755
index 0000000..1e0ecb2
--- /dev/null
+++ b/tests/scripts/test038-retcode
@@ -0,0 +1,112 @@
+#! /bin/sh
+# $Header$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $RETCODE = retcodeno; then
+ echo "Retcode overlay not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $MCONF > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Running slapindex to index slapd database..."
+. $CONFFILTER $BACKEND < $RETCODECONF > $CONF1
+$SLAPINDEX -f $CONF1
+RC=$?
+if test $RC != 0 ; then
+ echo "warning: slapindex failed ($RC)"
+ echo " assuming no indexing support"
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing search for timelimitExceeded..."
+$LDAPSEARCH -b "cn=timelimitExceeded,ou=RetCodes,$BASEDN" \
+ -H $URI1 '(objectClass=*)' >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 3 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing modify for unwillingToPerform..."
+$LDAPMODIFY -D "$MANAGERDN" -w $PASSWD \
+ -H $URI1 >> $TESTOUT 2>&1 << EOMODS
+dn: cn=unwillingToPerform,ou=RetCodes,$BASEDN
+changetype: delete
+EOMODS
+RC=$?
+if test $RC != 53 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Testing compare for success after sleep (2 s)..."
+$LDAPCOMPARE -H $URI1 \
+ "cn=Success w/ Delay,ou=RetCodes,$BASEDN" "cn:foo" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test039-glue-ldap-concurrency b/tests/scripts/test039-glue-ldap-concurrency
new file mode 100755
index 0000000..5b5580a
--- /dev/null
+++ b/tests/scripts/test039-glue-ldap-concurrency
@@ -0,0 +1,231 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+echo ""
+
+if test $BACKLDAP = ldapno ; then
+ echo "ldap backend not available, test skipped"
+ exit 0
+fi
+
+if test $RWM = rwmno ; then
+ echo "rwm (rewrite/remap) overlay not available, test skipped"
+ exit 0
+fi
+
+if test x$TESTLOOPS = x ; then
+ TESTLOOPS=50
+fi
+
+if test x$TESTOLOOPS = x ; then
+ TESTOLOOPS=1
+fi
+
+if test x$TESTCHILDREN = x ; then
+ TESTCHILDREN=20
+fi
+
+rm -rf $TESTDIR
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $METACONF1 > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDERED > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $METACONF2 > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$METAMANAGERDN" -H $URI2 -w $PASSWD < \
+ $LDIFMETA >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT3..."
+. $CONFFILTER $BACKEND < $GLUELDAPCONF > $CONF3
+$SLAPD -f $CONF3 -h $URI3 -d $LVL > $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI3 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+mkdir -p $TESTDIR/$DATADIR
+METABASEDN="o=Example,c=US"
+for f in $DATADIR/do_* ; do
+ sed -e "s;$BASEDN;$METABASEDN;" $f > $TESTDIR/$f
+done
+
+# add a read that matches only the local database, but selects
+# also the remote as candidate; this should be removed to compare
+# execution times with test008...
+for f in $TESTDIR/$DATADIR/do_read.* ; do
+ echo "ou=Meta,$METABASEDN" >> $f
+done
+
+# add a read that matches a referral in the local database only,
+# but selects also the remote as candidate; this should be removed
+# to compare execution times with test008...
+for f in $TESTDIR/$DATADIR/do_read.* ; do
+ echo "cn=Somewhere,ou=Meta,$METABASEDN" >> $f
+done
+
+# add a bind that resolves to a referral
+for f in $TESTDIR/$DATADIR/do_bind.* ; do
+ echo "cn=Foo,ou=Meta,$METABASEDN" >> $f
+ echo "bar" >> $f
+ echo "" >> $f
+ echo "" >> $f
+done
+
+# fix test data to include back-monitor, if available
+# NOTE: copies do_* files from $TESTDIR/$DATADIR to $TESTDIR
+$MONITORDATA "$TESTDIR/$DATADIR" "$TESTDIR"
+
+echo "Using tester for concurrent server access..."
+BINDDN="cn=Manager,o=Local"
+PASSWD="secret"
+$SLAPDTESTER -P "$PROGDIR" -d "$TESTDIR" -H $URI3 \
+ -D "$BINDDN" -w $PASSWD \
+ -l $TESTLOOPS -L $TESTOLOOPS -j $TESTCHILDREN -r 20 \
+ -i '!REFERRAL' -i '*INVALID_CREDENTIALS' -SS
+RC=$?
+
+if test $RC != 0 ; then
+ echo "slapd-tester failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to retrieve all the entries..."
+$LDAPSEARCH -S "" -b "$METABASEDN" -H $URI3 \
+ '(objectClass=*)' > $SEARCHOUT 2>&1
+RC=$?
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $METACONCURRENCYOUT > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - slapd-ldap search/modification didn't succeed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test040-subtree-rename b/tests/scripts/test040-subtree-rename
new file mode 100755
index 0000000..9554da3
--- /dev/null
+++ b/tests/scripts/test040-subtree-rename
@@ -0,0 +1,209 @@
+#! /bin/sh
+# $OpenLDAP$ */
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $BACKEND = wt ; then
+ echo "back-wt does not support subtree rename"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $TESTOUT
+cat /dev/null > $SEARCHOUT
+
+# Add
+echo "Populating the database..."
+echo "# Populating the database..." >> $TESTOUT
+$LDAPADD -v -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOMODS0
+dn: dc=example,dc=com
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: ou=Parent,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Parent
+
+dn: ou=Another parent,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Another parent
+
+dn: ou=Child,ou=Parent,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Child
+
+dn: ou=Grandchild,ou=Child,ou=Parent,dc=example,dc=com
+objectClass: organizationalUnit
+ou: Grandchild
+EOMODS0
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Searching all database..."
+echo "# Searching all database (after add)..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Rename (PASS1)
+echo "Renaming (PASS1)..."
+echo "# Renaming (PASS1)..." >> $TESTOUT
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOMODS1
+dn: ou=Child,ou=Parent,dc=example,dc=com
+changetype: modrdn
+newrdn: ou=Renamed child
+deleteoldrdn: 0
+EOMODS1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Searching all database..."
+echo "# Searching all database (after PASS1)..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Rename (PASS2)
+echo "Renaming (PASS2)..."
+echo "# Renaming (PASS2)..." >> $TESTOUT
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOMODS2
+dn: ou=Parent,dc=example,dc=com
+changetype: modrdn
+newrdn: ou=Renamed parent
+deleteoldrdn: 0
+EOMODS2
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Searching all database..."
+echo "# Searching all database (after PASS2)..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Rename (PASS3)
+echo "Renaming (PASS3)..."
+echo "# Renaming (PASS3)..." >> $TESTOUT
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOMODS3
+dn: ou=Renamed child,ou=Renamed parent,dc=example,dc=com
+changetype: modrdn
+newrdn: ou=Renamed child
+deleteoldrdn: 0
+newsuperior: ou=Another parent,dc=example,dc=com
+EOMODS3
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Searching all database..."
+echo "# Searching all database (after PASS3)..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=$SUBTREERENAMEOUT
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test041-aci b/tests/scripts/test041-aci
new file mode 100755
index 0000000..c63676d
--- /dev/null
+++ b/tests/scripts/test041-aci
@@ -0,0 +1,258 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+case "$BACKEND" in ldif | null)
+ echo "$BACKEND backend does not support access controls, test skipped"
+ exit 0
+ ;;
+esac
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test "$ACI" = "acino" ; then
+ echo "ACI not enabled, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $ACICONF > $CONF1
+$SLAPADD -f $CONF1 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd ACI access control..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+cat /dev/null > $TESTOUT
+
+# Search must fail
+BASEDN="dc=example,dc=com"
+echo "Searching \"$BASEDN\" (should fail)..."
+echo "# Searching \"$BASEDN\" (should fail)..." >> $SEARCHOUT
+$LDAPSEARCH -s base -b "$BASEDN" -H $URI1 \
+ '(objectclass=*)' >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 32 ; then
+ echo "ldapsearch should have failed with noSuchObject ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ if test $RC = 0 ; then
+ exit -1
+ fi
+ exit $RC
+fi
+
+# Bind must fail
+BINDDN="cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+BINDPW=bjensen
+echo "Testing ldapwhoami as ${BINDDN} (should fail)..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW
+RC=$?
+if test $RC = 0 ; then
+ echo "ldapwhoami should have failed!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+# Populate ACIs
+echo "Writing ACIs as \"$MANAGERDN\"..."
+$LDAPMODIFY -D "$MANAGERDN" -w $PASSWD -H $URI1 \
+ >> $TESTOUT 2>&1 << EOMODS0
+dn: dc=example,dc=com
+changetype: modify
+add: OpenLDAPaci
+OpenLDAPaci: 0#subtree#grant;d,c,s,r;[all]#group/groupOfUniqueNames/uniqueMe
+ mber#cn=ITD Staff,ou=Groups,dc=example,dc=com
+OpenLDAPaci: 1#entry#grant;d;[all]#public#
+
+dn: ou=People,dc=example,dc=com
+changetype: modify
+add: OpenLDAPaci
+OpenLDAPaci: 0#subtree#grant;x;userPassword#public#
+OpenLDAPaci: 1#subtree#grant;w;userPassword#self#
+OpenLDAPaci: 2#subtree#grant;w;userPassword#access-id#cn=Bjorn Jensen,ou=Inf
+ ormation Technology Division,ou=People,dc=example,dc=com
+
+dn: ou=Groups,dc=example,dc=com
+changetype: modify
+add: OpenLDAPaci
+OpenLDAPaci: 0#entry#grant;s;[all]#public#
+OpenLDAPaci: 1#children#grant;r;member;r;uniqueMember#access-id#cn=Bjorn Jen
+ sen,ou=Information Technology Division,ou=People,dc=example,dc=com
+EOMODS0
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Search must succeed with no results
+BASEDN="dc=example,dc=com"
+echo "Searching \"$BASEDN\" (should succeed with no results)..."
+echo "# Searching \"$BASEDN\" (should succeed with no results)..." >> $SEARCHOUT
+$LDAPSEARCH -s base -b "$BASEDN" -H $URI1 \
+ '(objectclass=*)' >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ ### TEMPORARY (see ITS#3963)
+ echo "ldapsearch failed ($RC)! IGNORED..."
+ ###echo "ldapsearch failed ($RC)!"
+ ###test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ ###exit $RC
+fi
+
+BINDDN="cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+BINDPW=bjensen
+echo "Testing ldapwhoami as ${BINDDN}..."
+$LDAPWHOAMI -H $URI1 -D "$BINDDN" -w $BINDPW
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Search must succeed
+BINDDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+BINDPW=bjorn
+BASEDN="dc=example,dc=com"
+echo "Searching \"$BASEDN\" as \"$BINDDN\" (should succeed)..."
+echo "# Searching \"$BASEDN\" as \"$BINDDN\" (should succeed)..." >> $SEARCHOUT
+$LDAPSEARCH -s base -b "$BASEDN" -H $URI1 \
+ -D "$BINDDN" -w "$BINDPW" \
+ '(objectClass=*)' >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Passwd must succeed
+BINDDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+BINDPW=bjorn
+TGT="cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com"
+NEWPW=jdoe
+echo "Setting \"$TGT\" password..."
+$LDAPPASSWD -H $URI1 \
+ -w "$BINDPW" -s "$NEWPW" \
+ -D "$BINDDN" "$TGT" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldappasswd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Re-change as self...
+echo "Changing self password..."
+BINDDN="$TGT"
+BINDPW=$NEWPW
+TGT="cn=John Doe,ou=Information Technology Division,ou=People,dc=example,dc=com"
+NEWPW=newcred
+$LDAPPASSWD -H $URI1 \
+ -w "$BINDPW" -s "$NEWPW" \
+ -D "$BINDDN" "$TGT" >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldappasswd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Searching groups
+BINDPW=$NEWPW
+BASEDN="ou=Groups,dc=example,dc=com"
+echo "Searching \"$BASEDN\" as \"$BINDDN\" (should succeed)..."
+echo "# Searching \"$BASEDN\" as \"$BINDDN\" (should succeed)..." >> $SEARCHOUT
+$LDAPSEARCH -s one -b "$BASEDN" -H $URI1 \
+ -D "$BINDDN" -w "$BINDPW" \
+ '(objectClass=*)' >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Search must fail
+BINDDN="cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com"
+BINDPW=bjensen
+echo "Searching \"$BASEDN\" as \"$BINDDN\" (should succeed with no results)..."
+echo "# Searching \"$BASEDN\" as \"$BINDDN\" (should succeed with no results)..." >> $SEARCHOUT
+$LDAPSEARCH -s one -b "$BASEDN" -H $URI1 \
+ -D "$BINDDN" -w "$BINDPW" \
+ '(objectClass=*)' >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=$ACIOUT
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s mdb=e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER -s mdb=e < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - operations did not complete correctly"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test042-valsort b/tests/scripts/test042-valsort
new file mode 100755
index 0000000..7c22f02
--- /dev/null
+++ b/tests/scripts/test042-valsort
@@ -0,0 +1,229 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2004-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $VALSORT = valsortno; then
+ echo "Valsort overlay not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $VALSORTCONF > $CONF1
+$SLAPADD -f $CONF1 -l $LDIFVALSORT
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd sorted values operations..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing ascending and weighted sort"
+
+FILTER="objectClass=*"
+$LDAPSEARCH -b "$VALSORTBASEDN" -H $URI1 \
+ "$FILTER" > $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected ldif..."
+$LDIFFILTER < $VALSORTOUT1 > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Reconfiguring slapd to test valsort descending"
+
+$LDAPMODIFY -x -D cn=config -H $URI1 -y $CONFIGPWF > \
+ $TESTOUT 2>&1 << EOMODS
+version: 1
+dn: olcOverlay={0}valsort,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcValSortAttr
+olcValSortAttr: employeeType "ou=users,o=valsort" weighted alpha-descend
+olcValSortAttr: ou "ou=users,o=valsort" weighted
+olcValSortAttr: mailPreferenceOption "ou=users,o=valsort" numeric-descend
+olcValSortAttr: departmentNumber "ou=users,o=valsort" alpha-descend
+olcValSortAttr: sn "ou=users,o=valsort" alpha-descend
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing descending and weighted sort"
+
+$LDAPSEARCH -b "$VALSORTBASEDN" -H $URI1 \
+ "$FILTER" > $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected ldif..."
+$LDIFFILTER < $VALSORTOUT2 > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Adding a valsort record with weighted ou..."
+
+$LDAPADD -D "$VALSORTDN" -H $URI1 -w $PASSWD \
+ > /dev/null << EOTVALSORT1
+dn: uid=dave,ou=users,o=valsort
+objectClass: OpenLDAPperson
+uid: dave
+sn: nothere
+cn: dave
+businessCategory: otest
+carLicense: TEST
+departmentNumber: 42
+displayName: Dave
+employeeNumber: 69
+employeeType: {1}contractor
+givenName: Dave
+ou: {1}Test
+ou: {3}Okay
+ou: {2}Is
+EOTVALSORT1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo ----------------------
+#$LDAPSEARCH -b "o=valsort" -H $URI1
+
+echo "Adding a non-weighted valsort record with ou..."
+
+$LDAPADD -D "$VALSORTDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOTVALSORT2
+dn: uid=bill,ou=users,o=valsort
+objectClass: OpenLDAPperson
+uid: bill
+sn: johnson
+cn: bill
+businessCategory: rtest
+carLicense: ABC123
+departmentNumber: 42
+displayName: Bill
+employeeNumber: 5150
+employeeType: {1}contractor
+givenName: Bill
+ou: Test
+ou: Okay
+ou: Is
+EOTVALSORT2
+
+RC=$?
+if test $RC != 19 ; then
+ echo "valsort check failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+$LDAPSEARCH -b "$VALSORTBASEDN" -H $URI1 \
+ "$FILTER" > $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s ldif=e < $SEARCHOUT > $SEARCHFLT
+echo "Filtering expected ldif..."
+$LDIFFILTER -s ldif=e < $VALSORTOUT3 > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+test $KILLSERVERS != no && wait
+
+echo ">>>>> Test succeeded"
+
+exit 0
diff --git a/tests/scripts/test043-delta-syncrepl b/tests/scripts/test043-delta-syncrepl
new file mode 100755
index 0000000..0d30e72
--- /dev/null
+++ b/tests/scripts/test043-delta-syncrepl
@@ -0,0 +1,552 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+if test $ACCESSLOG = accesslogno; then
+ echo "Accesslog overlay not available, test skipped"
+ exit 0
+fi
+if test $BACKEND = ldif ; then
+ # Onelevel search does not return entries in order of creation or CSN.
+ echo "$BACKEND backend unsuitable for syncprov logdb, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1A $DBDIR1B $DBDIR2
+
+SPEC="mdb=a"
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf
+#
+# Test replication:
+# - start provider
+# - start consumer
+# - populate over ldap
+# - perform some modifies and deleted
+# - attempt to modify the consumer (referral or chain)
+# - retrieve database over ldap and compare against expected results
+#
+
+echo "Starting provider slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $DSRPROVIDERCONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to create the context prefix entries in the provider..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDEREDCP > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting consumer slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $DSRCONSUMERCONF > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that consumer slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the provider directory..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDEREDNOCP > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Stopping the provider, sleeping 10 seconds and restarting it..."
+kill -HUP "$PID"
+wait $PID
+sleep 10
+echo "RESTART" >> $LOG1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL >> $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID $CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapmodify to modify provider directory..."
+
+#
+# Do some modifications
+#
+
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example,dc=com
+changetype: modify
+add: drink
+drink: Orange Juice
+-
+delete: sn
+sn: Jones
+-
+add: sn
+sn: Jones
+-
+add: displayName
+displayName: The one
+
+dn: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example,dc=com
+changetype: modify
+add: displayName
+displayName: James the First
+-
+delete: displayName
+displayName: The one
+
+dn: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+replace: drink
+drink: Iced Tea
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: uniquemember
+uniquemember: cn=James A Jones 2, ou=Information Technology Division, ou=People, dc=example,dc=com
+uniquemember: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+-
+add: uniquemember
+uniquemember: cn=Dorothy Stevens, ou=Alumni Association, ou=People, dc=example,dc=com
+uniquemember: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example,dc=com
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: description
+
+dn: cn=Gern Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+cn: Gern Jensen
+sn: Jensen
+uid: gjensen
+title: Chief Investigator, ITD
+postaladdress: ITD $ 535 W. William St $ Ann Arbor, MI 48103
+seealso: cn=All Staff, ou=Groups, dc=example,dc=com
+drink: Coffee
+homepostaladdress: 844 Brown St. Apt. 4 $ Ann Arbor, MI 48104
+description: Very odd
+facsimiletelephonenumber: +1 313 555 7557
+telephonenumber: +1 313 555 8343
+mail: gjensen@mailgw.example.com
+homephone: +1 313 555 8844
+
+dn: ou=Retired, ou=People, dc=example,dc=com
+changetype: add
+objectclass: organizationalUnit
+ou: Retired
+
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+cn: Rosco P. Coltrane
+sn: Coltrane
+uid: rosco
+description: Fat tycoon
+
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modrdn
+newrdn: cn=Rosco P. Coltrane
+deleteoldrdn: 1
+newsuperior: ou=Retired, ou=People, dc=example,dc=com
+
+dn: cn=James A Jones 2, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: delete
+
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ 'objectclass=*' \* + > $PROVIDEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ 'objectclass=*' \* + > $CONSUMEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering provider results..."
+$LDIFFILTER -b $BACKEND -s $SPEC < $PROVIDEROUT | grep -iv "^auditcontext:" > $PROVIDERFLT
+echo "Filtering consumer results..."
+$LDIFFILTER -b $BACKEND -s $SPEC < $CONSUMEROUT | grep -iv "^auditcontext:" > $CONSUMERFLT
+
+echo "Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Stopping consumer to test recovery..."
+kill -HUP $CONSUMERPID
+wait $CONSUMERPID
+KILLPIDS="$PID"
+
+echo "Modifying more entries on the provider..."
+$LDAPMODIFY -v -D "$BJORNSDN" -H $URI1 -w bjorn >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Rosco P. Coltrane, ou=Retired, ou=People, dc=example,dc=com
+changetype: delete
+
+dn: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+add: drink
+drink: Mad Dog 20/20
+
+dn: cn=Rosco P. Coltrane, ou=Retired, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+sn: Coltrane
+uid: rosco
+cn: Rosco P. Coltrane
+
+dn: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: modify
+replace: drink
+drink: Red Wine
+-
+replace: drink
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+changetype: modrdn
+newrdn: cn=Some Staff
+deleteoldrdn: 1
+
+EOMODS
+
+echo "Restarting consumer..."
+echo "RESTART" >> $LOG2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL >> $LOG2 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$PID $CONSUMERPID"
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+if test ! $BACKLDAP = "ldapno" ; then
+ echo "Try updating the consumer slapd..."
+ $LDAPMODIFY -v -D "$MANAGERDN" -H $URI2 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example, dc=com
+changetype: modify
+add: description
+description: This write must fail because directed to a shadow context,
+description: unless the chain overlay is configured appropriately ;)
+
+EOMODS
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+fi
+
+echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ 'objectclass=*' \* + > $PROVIDEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ 'objectclass=*' \* + > $CONSUMEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering provider results..."
+$LDIFFILTER -b $BACKEND -s $SPEC < $PROVIDEROUT | grep -iv "^auditcontext:" > $PROVIDERFLT
+echo "Filtering consumer results..."
+$LDIFFILTER -b $BACKEND -s $SPEC < $CONSUMEROUT | grep -iv "^auditcontext:" > $CONSUMERFLT
+
+echo "Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Stopping consumer to test recovery after logpurge expired..."
+kill -HUP $CONSUMERPID
+wait $CONSUMERPID
+KILLPIDS="$PID"
+
+echo "Modifying even more entries on the provider..."
+$LDAPMODIFY -v -D "$BJORNSDN" -H $URI1 -w bjorn >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+changetype: delete
+
+dn: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+add: drink
+drink: Sangria
+
+dn: cn=George D. Stevens, ou=Retired, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+sn: Stevens
+uid: gstevens
+cn: George D. Stevens
+
+dn: cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,
+ dc=com
+changetype: modify
+replace: drink
+drink: cold water
+
+dn: cn=Some Staff,ou=Groups,dc=example,dc=com
+changetype: modrdn
+newrdn: cn=More Staff
+deleteoldrdn: 1
+
+EOMODS
+
+echo "Configuring logpurge of 1 second..."
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF >> \
+ $TESTOUT 2>&1 << EOMODS
+
+dn: olcOverlay={1}accesslog,olcDatabase={2}$BACKEND,cn=config
+changetype: modify
+replace: olcAccessLogPurge
+olcAccessLogPurge: 0+00:00:02 0+00:00:01
+-
+
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting 4 seconds for accesslog to be purged..."
+sleep 4
+
+echo "Using ldapsearch to check if accesslog is empty..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -b "cn=log" -H $URI1 -z 1 \
+ > $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 3 seconds for accesslog to be purged..."
+ sleep 3
+done
+
+if test $RC != 0; then
+ echo "Accesslog did not purge in time"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+
+echo "Restarting consumer..."
+echo "RESTART" >> $LOG2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL >> $LOG2 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$PID $CONSUMERPID"
+
+echo "Waiting $SLEEP1 seconds for syncrepl to reschedule (ITS#9878) and poking it..."
+sleep $SLEEP1
+
+$LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+RC=$?
+
+if test $RC != 0; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ 'objectclass=*' \* + > $PROVIDEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ 'objectclass=*' \* + > $CONSUMEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo "Filtering provider results..."
+$LDIFFILTER -b $BACKEND -s $SPEC < $PROVIDEROUT | grep -iv "^auditcontext:" > $PROVIDERFLT
+echo "Filtering consumer results..."
+$LDIFFILTER -b $BACKEND -s $SPEC < $CONSUMEROUT | grep -iv "^auditcontext:" > $CONSUMERFLT
+
+echo "Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test044-dynlist b/tests/scripts/test044-dynlist
new file mode 100755
index 0000000..b7a6b20
--- /dev/null
+++ b/tests/scripts/test044-dynlist
@@ -0,0 +1,1111 @@
+#! /bin/sh
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $DYNLIST = "dynlistno" ; then
+ echo "dynlist overlay not available, test skipped"
+ exit 0
+fi
+
+if test $BACKEND = ldif ; then
+ # dynlist+ldif fails because back-ldif lacks bi_op_compare()
+ echo "$BACKEND backend unsuitable for dynlist overlay, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf
+
+DBIX=2
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $DYNLISTCONF > $CONF1
+$SLAPADD -f $CONF1 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+LISTDN="ou=Dynamic Lists,$BASEDN"
+echo "Adding a dynamic list..."
+$LDAPADD -v -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ > $TESTOUT 2>&1 << EOMODS
+dn: $LISTDN
+objectClass: organizationalUnit
+ou: Dynamic Lists
+
+dn: cn=Dynamic List,$LISTDN
+objectClass: groupOfURLs
+cn: Dynamic List
+memberURL: ldap:///ou=People,${BASEDN}?cn,mail?sub?(objectClass=person)
+EOMODS
+
+echo "Testing list search of all attrs..."
+echo "# Testing list search of all attrs..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$LISTDN" -H $URI1 \
+ '(cn=Dynamic List)' '*' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing list search of a listed attr..."
+echo "# Testing list search of a listed attr..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$LISTDN" -H $URI1 \
+ '(cn=Dynamic List)' mail \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing list search of a non-listed attr..."
+echo "# Testing list search of a non-listed attr..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$LISTDN" -H $URI1 \
+ '(cn=Dynamic List)' objectClass \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing list search with (critical) manageDSAit..."
+echo "# Testing list search with (critical) manageDSAit..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$LISTDN" -H $URI1 -MM \
+ '(cn=Dynamic List)' '*' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing filtered search with all attrs..."
+echo "# Testing filtered search with all attrs..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$LISTDN" -H $URI1 \
+ '(mail=jdoe@woof.net)' '*' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing filtered search of a listed attr..."
+echo "# Testing filtered search of a listed attr..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$LISTDN" -H $URI1 \
+ '(mail=jdoe@woof.net)' mail \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing filtered search of a non-listed attr..."
+echo "# Testing filtered search of a non-listed attr..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$LISTDN" -H $URI1 \
+ '(mail=jdoe@woof.net)' objectClass \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing filtered search of a non-present attr..."
+echo "# Testing filtered search of a non-present attr..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$LISTDN" -H $URI1 \
+ '(mail=nobody@nowhere)' objectClass \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing list compare..."
+echo "# Testing list compare..." >> $SEARCHOUT
+$LDAPCOMPARE -H $URI1 \
+ "cn=Dynamic List,$LISTDN" "cn:Bjorn Jensen" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+case $RC in
+5)
+ echo "ldapcompare returned FALSE ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+6)
+ echo "ldapcompare returned TRUE ($RC)"
+ ;;
+0)
+ echo "ldapcompare returned success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+echo "" >> $SEARCHOUT
+
+echo "Testing list compare (should return FALSE)..."
+echo "# Testing list compare (should return FALSE)..." >> $SEARCHOUT
+$LDAPCOMPARE -H $URI1 \
+ "cn=Dynamic List,$LISTDN" "cn:FALSE" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+case $RC in
+5)
+ echo "ldapcompare returned FALSE ($RC)"
+ ;;
+6)
+ echo "ldapcompare returned TRUE ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+0)
+ echo "ldapcompare returned success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+echo "" >> $SEARCHOUT
+
+echo "Testing list compare (should return UNDEFINED)..."
+echo "# Testing list compare (should return UNDEFINED)..." >> $SEARCHOUT
+$LDAPCOMPARE -H $URI1 \
+ "cn=Dynamic List,$LISTDN" "dc:UNDEFINED" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+case $RC in
+5)
+ echo "ldapcompare returned FALSE ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+6)
+ echo "ldapcompare returned TRUE ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+16|32)
+ echo "ldapcompare returned UNDEFINED ($RC)"
+ ;;
+0)
+ echo "ldapcompare returned success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapcompare failed ($RC)"
+ ;;
+esac
+echo "" >> $SEARCHOUT
+
+echo "Testing list compare with manageDSAit..."
+echo "# Testing list compare with manageDSAit..." >> $SEARCHOUT
+$LDAPCOMPARE -H $URI1 -MM \
+ "cn=Dynamic List,$LISTDN" "cn:Bjorn Jensen" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+case $RC in
+5)
+ echo "ldapcompare returned FALSE ($RC)"
+ ;;
+6)
+ echo "ldapcompare returned TRUE ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+0)
+ echo "ldapcompare returned success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+echo "" >> $SEARCHOUT
+
+echo "Reconfiguring slapd..."
+$LDAPMODIFY -x -D cn=config -H $URI1 -y $CONFIGPWF > \
+ $TESTOUT 2>&1 << EOMODS
+version: 1
+dn: olcOverlay={0}dynlist,olcDatabase={$DBIX}$BACKEND,cn=config
+changetype: modify
+delete: olcDynListAttrSet
+olcDynListAttrSet: {0}
+-
+add: olcDynListAttrSet
+olcDynListAttrSet: groupOfURLs memberURL sn:cn mail
+-
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "==========================================================" >> $LOG1
+
+echo "Testing attribute mapping"
+
+echo "Testing list search of all (mapped) attrs..."
+echo "# Testing list search of all (mapped) attrs..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$LISTDN" -H $URI1 \
+ '(cn=Dynamic List)' '*' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing list search of a (mapped) listed attr..."
+echo "# Testing list search of a (mapped) listed attr..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$LISTDN" -H $URI1 \
+ '(cn=Dynamic List)' sn \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing list search of a (n unmapped) listed attr..."
+echo "# Testing list search of a (n unmapped) listed attr..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$LISTDN" -H $URI1 \
+ '(cn=Dynamic List)' mail \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing list compare (mapped attrs) ..."
+echo "# Testing list compare (mapped attrs) ..." >> $SEARCHOUT
+$LDAPCOMPARE -H $URI1 \
+ "cn=Dynamic List,$LISTDN" "sn:Bjorn Jensen" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+case $RC in
+5)
+ echo "ldapcompare returned FALSE ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+6)
+ echo "ldapcompare returned TRUE ($RC)"
+ ;;
+0)
+ echo "ldapcompare returned success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+echo "" >> $SEARCHOUT
+
+echo "Testing list compare (mapped attrs; should return FALSE)..."
+echo "# Testing list compare (mapped attrs; should return FALSE)..." >> $SEARCHOUT
+$LDAPCOMPARE -H $URI1 \
+ "cn=Dynamic List,$LISTDN" "sn:FALSE" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+case $RC in
+5)
+ echo "ldapcompare returned FALSE ($RC)"
+ ;;
+6)
+ echo "ldapcompare returned TRUE ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+0)
+ echo "ldapcompare returned success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+echo "" >> $SEARCHOUT
+
+echo "Reconfiguring slapd..."
+$LDAPMODIFY -x -D cn=config -H $URI1 -y $CONFIGPWF > \
+ $TESTOUT 2>&1 << EOMODS
+version: 1
+dn: olcOverlay={0}dynlist,olcDatabase={$DBIX}$BACKEND,cn=config
+changetype: modify
+delete: olcDynListAttrSet
+olcDynListAttrSet: {0}
+-
+add: olcDynListAttrSet
+olcDynListAttrSet: groupOfURLs memberURL member
+-
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "==========================================================" >> $LOG1
+
+echo "Adding a dynamic list..."
+$LDAPADD -v -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ > $TESTOUT 2>&1 << EOMODS
+dn: cn=Dynamic List of Members,$LISTDN
+objectClass: groupOfURLs
+cn: Dynamic List of Members
+memberURL: ldap:///ou=People,${BASEDN}??sub?(objectClass=person)
+EOMODS
+
+echo "Testing list search of all attrs..."
+echo "# Testing list search of all attrs..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$LISTDN" -H $URI1 \
+ '(cn=Dynamic List of Members)' '*' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing list search of a listed attr..."
+echo "# Testing list search of a listed attr..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$LISTDN" -H $URI1 \
+ '(cn=Dynamic List of Members)' member \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing list search of a non-listed attr..."
+echo "# Testing list search of a non-listed attr..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$LISTDN" -H $URI1 \
+ '(cn=Dynamic List of Members)' objectClass \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing list search with (critical) manageDSAit..."
+echo "# Testing list search with (critical) manageDSAit..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$LISTDN" -H $URI1 -MM \
+ '(&(cn=Dynamic List of Members)(objectClass=groupOfURLs))' '*' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+CMPDN="$BJORNSDN"
+echo "Testing list compare..."
+echo "# Testing list compare..." >> $SEARCHOUT
+$LDAPCOMPARE -H $URI1 \
+ "cn=Dynamic List of Members,$LISTDN" "member:$CMPDN" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+case $RC in
+5)
+ echo "ldapcompare returned FALSE ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+6)
+ echo "ldapcompare returned TRUE ($RC)"
+ ;;
+0)
+ echo "ldapcompare returned success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+echo "" >> $SEARCHOUT
+
+CMPDN="$BADBJORNSDN"
+echo "Testing list compare (should return FALSE)..."
+echo "# Testing list compare... (should return FALSE)" >> $SEARCHOUT
+$LDAPCOMPARE -H $URI1 \
+ "cn=Dynamic List of Members,$LISTDN" "member:$CMPDN" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+case $RC in
+5)
+ echo "ldapcompare returned FALSE ($RC)"
+ ;;
+6)
+ echo "ldapcompare returned TRUE ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+0)
+ echo "ldapcompare returned success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+echo "" >> $SEARCHOUT
+
+CMPDN="$BJORNSDN"
+echo "Testing list compare (should return FALSE)..."
+echo "# Testing list compare (should return FALSE)..." >> $SEARCHOUT
+$LDAPCOMPARE -H $URI1 \
+ "cn=Dynamic List of Members,$LISTDN" "member:cn=Foo Bar" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+case $RC in
+5)
+ echo "ldapcompare returned FALSE ($RC)"
+ ;;
+6)
+ echo "ldapcompare returned TRUE ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+0)
+ echo "ldapcompare returned success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+echo "" >> $SEARCHOUT
+
+echo "Testing list compare with manageDSAit (should return UNDEFINED)..."
+echo "# Testing list compare with manageDSAit (should return UNDEFINED)..." >> $SEARCHOUT
+$LDAPCOMPARE -H $URI1 -MM \
+ "cn=Dynamic List,$LISTDN" "member:$CMPDN" \
+ >> $SEARCHOUT 2>&1
+RC=$?
+case $RC in
+5)
+ echo "ldapcompare returned FALSE ($RC)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+6)
+ echo "ldapcompare returned TRUE ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+16|32)
+ echo "ldapcompare returned UNDEFINED ($RC)"
+ ;;
+0)
+ echo "ldapcompare returned success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+*)
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+echo "" >> $SEARCHOUT
+
+echo "==========================================================" >> $LOG1
+
+echo "Testing dgIdentity..."
+
+# Set ACL, require authentication to get list contents
+$LDAPMODIFY -x -D cn=config -H $URI1 -y $CONFIGPWF > \
+ $TESTOUT 2>&1 << EOMODS
+version: 1
+dn: olcDatabase={$DBIX}$BACKEND,cn=config
+changetype: modify
+add: olcAccess
+olcAccess: to dn.base="cn=Dynamic List of Members,$LISTDN" by * read
+olcAccess: to * by users read by * search
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing list search without dgIdentity..."
+echo "# Testing list search without dgIdentity..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$LISTDN" -H $URI1 \
+ '(cn=Dynamic List of Members)' '*' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ > $TESTOUT 2>&1 << EOMODS
+dn: cn=Dynamic List of Members,$LISTDN
+changetype: modify
+add: objectClass
+objectClass: dgIdentityAux
+-
+add: dgIdentity
+dgIdentity: $CMPDN
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing list search with dgIdentity..."
+echo "# Testing list search with dgIdentity..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$LISTDN" -H $URI1 \
+ '(cn=Dynamic List of Members)' '*' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing dgAuthz..."
+
+CMPDN="cn=Bjorn Jensen,ou=Information Technology Division,ou=People,$BASEDN"
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ > $TESTOUT 2>&1 << EOMODS
+dn: cn=Dynamic List of Members,$LISTDN
+changetype: modify
+add: dgAuthz
+dgAuthz: dn:$BABSDN
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing list search with dgIdentity and dgAuthz anonymously..."
+echo "# Testing list search with dgIdentity and dgAuthz anonymously..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$LISTDN" -H $URI1 \
+ '(cn=Dynamic List of Members)' '*' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing list search with dgIdentity and dgAuthz as the authorized identity..."
+echo "# Testing list search with dgIdentity and dgAuthz as the authorized identity..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$LISTDN" -H $URI1 \
+ -D "$BABSDN" -w bjensen \
+ '(cn=Dynamic List of Members)' '*' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Reconfiguring slapd..."
+$LDAPMODIFY -x -D cn=config -H $URI1 -y $CONFIGPWF > \
+ $TESTOUT 2>&1 << EOMODS
+version: 1
+dn: olcOverlay={0}dynlist,olcDatabase={$DBIX}$BACKEND,cn=config
+changetype: modify
+delete: olcDynListAttrSet
+olcDynListAttrSet: {0}
+-
+add: olcDynListAttrSet
+olcDynListAttrSet: groupOfURLs memberURL member+memberOf
+-
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "==========================================================" >> $LOG1
+
+echo "Testing memberOf functionality..."
+echo "# Testing memberOf functionality..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "ou=People,$BASEDN" -H $URI1 \
+ -D "$BABSDN" -w bjensen \
+ '(cn=Mark Elliot)' '*' 'memberOf' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing filtered memberOf functionality..."
+echo "# Testing filtered memberOf functionality..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "ou=People,$BASEDN" -H $URI1 \
+ -D "$BABSDN" -w bjensen \
+ '(&(memberOf=cn=Dynamic List of Members,ou=Dynamic Lists,dc=example,dc=com)(cn=Mark Elliot))' '*' 'memberOf' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Reconfiguring slapd..."
+$LDAPMODIFY -x -D cn=config -H $URI1 -y $CONFIGPWF > \
+ $TESTOUT 2>&1 << EOMODS
+version: 1
+dn: olcOverlay={0}dynlist,olcDatabase={$DBIX}$BACKEND,cn=config
+changetype: modify
+delete: olcDynListAttrSet
+olcDynListAttrSet: {0}
+-
+add: olcDynListAttrSet
+olcDynListAttrSet: groupOfURLs memberURL member+memberOf@groupOfNames
+-
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "==========================================================" >> $LOG1
+
+echo "Testing static group memberOf functionality..."
+echo "# Testing static group memberOf functionality..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "ou=People,$BASEDN" -H $URI1 \
+ -D "$BABSDN" -w bjensen \
+ '(cn=Mark Elliot)' '*' 'memberOf' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing static group member compare..."
+echo "# Testing static group member compare..." >> $SEARCHOUT
+$LDAPCOMPARE -H $URI1 \
+ "cn=all staff,ou=groups,$BASEDN" "member:cn=Mark Elliot,ou=Alumni Association,ou=People,$BASEDN" >> $SEARCHOUT
+RC=$?
+case $RC in
+5)
+ echo "ldapcompare returned FALSE ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+6)
+ echo "ldapcompare returned TRUE ($RC)"
+ ;;
+0)
+ echo "ldapcompare returned success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+*)
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+echo "" >> $SEARCHOUT
+
+echo "Testing static group non-member compare (should return FALSE)..."
+echo "# Testing static group non-member compare (should return FALSE)..." >> $SEARCHOUT
+$LDAPCOMPARE -H $URI1 \
+ "cn=all staff,ou=groups,$BASEDN" "member:cn=Not A User,ou=Alumni Association,ou=People,$BASEDN" >> $SEARCHOUT
+RC=$?
+case $RC in
+5)
+ echo "ldapcompare returned FALSE ($RC)"
+ ;;
+6)
+ echo "ldapcompare returned TRUE ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+0)
+ echo "ldapcompare returned success ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+*)
+ echo "ldapcompare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+echo "" >> $SEARCHOUT
+
+echo "Reconfiguring slapd..."
+$LDAPMODIFY -x -D cn=config -H $URI1 -y $CONFIGPWF > \
+ $TESTOUT 2>&1 << EOMODS
+version: 1
+dn: olcOverlay={0}dynlist,olcDatabase={$DBIX}$BACKEND,cn=config
+changetype: modify
+delete: olcDynListAttrSet
+olcDynListAttrSet: {0}
+-
+add: olcDynListAttrSet
+olcDynListAttrSet: groupOfURLs memberURL member+memberOf*
+-
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "==========================================================" >> $LOG1
+
+echo "Adding a couple dynamic groups..."
+$LDAPADD -v -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ > $TESTOUT 2>&1 << EOMODS
+dn: cn=The Smiths,$LISTDN
+objectClass: groupOfURLs
+cn: The Smiths
+memberURL: ldap:///ou=People,${BASEDN}??sub?(sn=Smith)
+description: Smith family
+
+dn: cn=Meta Group,$LISTDN
+objectClass: groupOfURLs
+cn: Meta Group
+memberURL: ldap:///${LISTDN}??sub?(description=Smith%20family)
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing nested dynamic group functionality..."
+echo "# Testing nested dynamic group functionality..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$LISTDN" -H $URI1 \
+ -D "$BABSDN" -w bjensen \
+ '(objectclass=*)' '*' 'memberOf' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -S "" -b "ou=People,$BASEDN" -H $URI1 \
+ -D "$BABSDN" -w bjensen \
+ '(cn=Mark Elliot)' '*' 'memberOf' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Reconfiguring slapd..."
+$LDAPMODIFY -x -D cn=config -H $URI1 -y $CONFIGPWF > \
+ $TESTOUT 2>&1 << EOMODS
+version: 1
+dn: olcOverlay={0}dynlist,olcDatabase={$DBIX}$BACKEND,cn=config
+changetype: modify
+delete: olcDynListAttrSet
+olcDynListAttrSet: {0}
+-
+add: olcDynListAttrSet
+olcDynListAttrSet: groupOfURLs memberURL member+memberOf@groupOfNames*
+olcDynListAttrSet: labeledURIObject labeledURI uniqueMember+seeAlso@groupOfUniqueNames
+-
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "==========================================================" >> $LOG1
+
+echo "Adding a couple static groups..."
+$LDAPADD -v -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ > $TESTOUT 2>&1 << EOMODS
+dn: cn=The Jensens,ou=Groups,$BASEDN
+objectClass: groupOfnames
+cn: The Jensens
+member: cn=Bjorn Jensen,ou=Information Technology DivisioN,ou=People,$BASEDN
+member: cn=Barbara Jensen,ou=Information Technology DivisioN,ou=People,$BASEDN
+
+dn: cn=JJs,ou=Groups,$BASEDN
+objectClass: groupOfnames
+cn: JJs
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,$BASEDN
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,$BASEDN
+member: cn=The Jensens,ou=Groups,$BASEDN
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing nested static group functionality..."
+echo "# Testing nested static group functionality..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "ou=People,$BASEDN" -H $URI1 \
+ -D "$BABSDN" -w bjensen \
+ '(sn=Jensen)' '*' 'memberOf' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding another nested group..."
+$LDAPADD -v -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ > $TESTOUT 2>&1 << EOMODS
+dn: cn=Bonus Group,ou=Groups,$BASEDN
+objectClass: groupOfnames
+cn: Bonus Group
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,$BASEDN
+member: cn=Meta Group,$LISTDN
+EOMODS
+
+$LDAPSEARCH -S "" -b "ou=People,$BASEDN" -H $URI1 \
+ -D "$BABSDN" -w bjensen \
+ '(sn=Hampster)' '*' 'memberOf' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -S "" -b "ou=People,$BASEDN" -H $URI1 \
+ -D "$BABSDN" -w bjensen \
+ '(sn=Doe)' '*' 'memberOf' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -S "" -b "ou=People,$BASEDN" -H $URI1 \
+ -D "$BABSDN" -w bjensen \
+ '(sn=Smith)' '*' 'memberOf' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing filtered nested memberOf functionality..."
+echo "# Testing filtered nested memberOf functionality..." >> $SEARCHOUT
+
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ -D "$BABSDN" -w bjensen \
+ "(memberOf=cn=bonus group,ou=groups,$BASEDN)" '*' 'memberOf' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ -D "$BABSDN" -w bjensen \
+ "(&(uid=jjones)(memberOf=cn=jjs,ou=groups,$BASEDN))" 'uid' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing negated filtered memberOf functionality..."
+echo "# Testing negated filtered memberOf functionality..." >> $SEARCHOUT
+
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ -D "$BABSDN" -w bjensen \
+ "(&(objectClass=OpenLDAPperson)(!(memberOf=cn=Alumni Assoc Staff,ou=groups,$BASEDN)))" '*' 'memberOf' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Testing filtered nested member functionality..."
+echo "# Testing filtered nested member functionality..." >> $SEARCHOUT
+
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ -D "$BABSDN" -w bjensen \
+ "(member=cn=Jennifer Smith,ou=Alumni Association,ou=People,$BASEDN)" '*' 'memberOf' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=$DYNLISTOUT
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test045-syncreplication-proxied b/tests/scripts/test045-syncreplication-proxied
new file mode 100755
index 0000000..8481168
--- /dev/null
+++ b/tests/scripts/test045-syncreplication-proxied
@@ -0,0 +1,867 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+
+# test now handles known issues
+#if test x"$PROXYSYNC" = x ; then
+# echo "Test disabled; set PROXYSYNC=yes to enable"
+# exit 0
+#fi
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $BACKLDAP = ldapno; then
+ echo "LDAP backend not available, test skipped"
+ exit 0
+fi
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+#
+# Test replication:
+# - start provider
+# - start consumer
+# - populate over ldap
+# - perform some modifies and deleted
+# - attempt to modify the consumer (referral or chain)
+# - retrieve database over ldap and compare against expected results
+#
+
+echo "Starting provider slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $SRPROVIDERCONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PROVIDERPID=$!
+if test $WAIT != 0 ; then
+ echo PROVIDERPID $PROVIDERPID
+ read foo
+fi
+KILLPIDS="$PROVIDERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ '(objectClass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to create the context prefix entry in the provider..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDEREDCP > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting consumer slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $RCONSUMERCONF > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$PROVIDERPID $CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that consumer slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ '(objectClass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting proxy slapd on TCP/IP port $PORT3..."
+. $CONFFILTER $BACKEND < $PLSRCONSUMERCONF > $CONF3
+$SLAPD -f $CONF3 -h $URI3 -d $LVL > $LOG3 2>&1 &
+PROXYPID=$!
+if test $WAIT != 0 ; then
+ echo PROXYPID $PROXYPID
+ read foo
+fi
+KILLPIDS="$PROVIDERPID $CONSUMERPID $PROXYPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that proxy slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI3 \
+ '(objectClass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 || test $RC = 53 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+case $RC in
+0 )
+ echo "ldapsearch should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+53)
+ ;;
+*)
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+CHECK=1
+echo "$CHECK > Using ldapadd to populate the provider directory..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDEREDNOCP > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# get provider contextCSN
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ -s base '(objectClass=*)' contextCSN > "${PROVIDEROUT}.$CHECK" 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# check consumer contextCSN
+sleep 1
+for i in 1 2 3; do
+ $LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ -s base '(objectClass=*)' contextCSN > "${CONSUMEROUT}.$CHECK" 2>&1
+ RC=$?
+
+ if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ $CMP ${PROVIDEROUT}.$CHECK ${CONSUMEROUT}.$CHECK > $CMPOUT
+
+ if test $? = 0 ; then
+ break
+ fi
+
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+done
+
+#echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' > "${PROVIDEROUT}.$CHECK" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ '(objectClass=*)' > "${CONSUMEROUT}.$CHECK" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Filtering provider results..."
+$LDIFFILTER < "${PROVIDEROUT}.$CHECK" > $PROVIDERFLT
+#echo "Filtering consumer results..."
+$LDIFFILTER < "${CONSUMEROUT}.$CHECK" > $CONSUMERFLT
+
+echo "$CHECK < Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+CHECK=`expr $CHECK + 1`
+echo "$CHECK > Stopping the provider, sleeping $SLEEP2 seconds and restarting it..."
+kill -HUP "$PROVIDERPID"
+wait $PROVIDERPID
+sleep $SLEEP2
+
+echo "======================= RESTART =======================" >> $LOG1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL >> $LOG1 2>&1 &
+PROVIDERPID=$!
+if test $WAIT != 0 ; then
+ echo PROVIDERPID $PROVIDERPID
+ read foo
+fi
+KILLPIDS="$PROVIDERPID $CONSUMERPID $PROXYPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ '(objectClass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapmodify to modify provider directory..."
+
+#
+# Do some modifications
+#
+
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example,dc=com
+changetype: modify
+add: drink
+drink: Orange Juice
+-
+delete: sn
+sn: Jones
+-
+add: sn
+sn: Jones
+
+dn: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+replace: drink
+drink: Iced Tea
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: uniquemember
+uniquemember: cn=James A Jones 2, ou=Information Technology Division, ou=People, dc=example,dc=com
+uniquemember: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+-
+add: uniquemember
+uniquemember: cn=Dorothy Stevens, ou=Alumni Association, ou=People, dc=example,dc=com
+uniquemember: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example,dc=com
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+changetype: modify
+delete: cn
+cn: Biiff Jensen
+
+dn: cn=Gern Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+cn: Gern Jensen
+sn: Jensen
+uid: gjensen
+title: Chief Investigator, ITD
+postaladdress: ITD $ 535 W. William St $ Ann Arbor, MI 48103
+seealso: cn=All Staff, ou=Groups, dc=example,dc=com
+drink: Coffee
+homepostaladdress: 844 Brown St. Apt. 4 $ Ann Arbor, MI 48104
+description: Very odd
+facsimiletelephonenumber: +1 313 555 7557
+telephonenumber: +1 313 555 8343
+mail: gjensen@mailgw.example.com
+homephone: +1 313 555 8844
+
+dn: ou=Retired, ou=People, dc=example,dc=com
+changetype: add
+objectclass: organizationalUnit
+ou: Retired
+
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+cn: Rosco P. Coltrane
+sn: Coltrane
+uid: rosco
+description: Fat tycoon
+
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modrdn
+newrdn: cn=Rosco P. Coltrane
+deleteoldrdn: 1
+newsuperior: ou=Retired, ou=People, dc=example,dc=com
+
+dn: cn=James A Jones 2, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: delete
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# get provider contextCSN
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ -s base '(objectClass=*)' contextCSN > "${PROVIDEROUT}.$CHECK" 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# check consumer contextCSN
+for i in 1 2 3; do
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+
+ $LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ -s base '(objectClass=*)' contextCSN > "${CONSUMEROUT}.$CHECK" 2>&1
+ RC=$?
+
+ if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ $CMP ${PROVIDEROUT}.$CHECK ${CONSUMEROUT}.$CHECK > $CMPOUT
+
+ if test $? = 0 ; then
+ break
+ fi
+done
+
+#echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' > "${PROVIDEROUT}.$CHECK" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ '(objectClass=*)' > "${CONSUMEROUT}.$CHECK" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Filtering provider results..."
+$LDIFFILTER < "${PROVIDEROUT}.$CHECK" > $PROVIDERFLT
+#echo "Filtering consumer results..."
+$LDIFFILTER < "${CONSUMEROUT}.$CHECK" > $CONSUMERFLT
+
+echo "$CHECK < Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+CHECK=`expr $CHECK + 1`
+echo "$CHECK > Stopping proxy to test recovery..."
+kill -HUP $PROXYPID
+wait $PROXYPID
+
+echo "Modifying more entries on the provider..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+add: description
+description: proxy is down...
+
+dn: cn=James T. Kirk, ou=Retired, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+sn: Kirk
+uid: jtk
+cn: James T. Kirk
+
+dn: cn=Tiberius J. Hooker, ou=Retired, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+sn: Hooker
+uid: tjh
+cn: Tiberius J. Hooker
+
+EOMODS
+
+echo "Restarting proxy..."
+echo "======================= RESTART =======================" >> $LOG3
+$SLAPD -f $CONF3 -h $URI3 -d $LVL >> $LOG3 2>&1 &
+PROXYPID=$!
+if test $WAIT != 0 ; then
+ echo PROXYPID $PROXYPID
+ read foo
+fi
+KILLPIDS="$PROVIDERPID $CONSUMERPID $PROXYPID"
+
+# get provider contextCSN
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ -s base '(objectClass=*)' contextCSN > "${PROVIDEROUT}.$CHECK" 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# check consumer contextCSN
+for i in 1 2 3 4 5; do
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+
+ $LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ -s base '(objectClass=*)' contextCSN > "${CONSUMEROUT}.$CHECK" 2>&1
+ RC=$?
+
+ if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ $CMP ${PROVIDEROUT}.$CHECK ${CONSUMEROUT}.$CHECK > $CMPOUT
+
+ if test $? = 0 ; then
+ break
+ fi
+done
+
+#echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' > "${PROVIDEROUT}.$CHECK" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ '(objectClass=*)' > "${CONSUMEROUT}.$CHECK" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Filtering provider results..."
+$LDIFFILTER < "${PROVIDEROUT}.$CHECK" > $PROVIDERFLT
+#echo "Filtering consumer results..."
+$LDIFFILTER < "${CONSUMEROUT}.$CHECK" > $CONSUMERFLT
+
+echo "$CHECK < Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+CHECK=`expr $CHECK + 1`
+echo "$CHECK > Try updating the consumer slapd..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI2 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example, dc=com
+changetype: modify
+add: description
+description: This write must fail because directed to a shadow context,
+description: unless the chain overlay is configured appropriately ;)
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# get provider contextCSN
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ -s base '(objectClass=*)' contextCSN > "${PROVIDEROUT}.$CHECK" 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# check consumer contextCSN
+sleep 1
+
+for i in 1 2 3; do
+ $LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ -s base '(objectClass=*)' contextCSN > "${CONSUMEROUT}.$CHECK" 2>&1
+ RC=$?
+
+ if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ $CMP ${PROVIDEROUT}.$CHECK ${CONSUMEROUT}.$CHECK > $CMPOUT
+
+ if test $? = 0 ; then
+ break
+ fi
+
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+done
+
+#echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' > "${PROVIDEROUT}.$CHECK" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+'(objectClass=*)' > "${CONSUMEROUT}.$CHECK" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Filtering provider results..."
+$LDIFFILTER < "${PROVIDEROUT}.$CHECK" > $PROVIDERFLT
+#echo "Filtering consumer results..."
+$LDIFFILTER < "${CONSUMEROUT}.$CHECK" > $CONSUMERFLT
+
+echo "$CHECK < Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+CHECK=`expr $CHECK + 1`
+echo "$CHECK > Stopping consumer to test recovery..."
+kill -HUP $CONSUMERPID
+wait $CONSUMERPID
+
+echo "Modifying more entries on the provider..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+add: drink
+drink: Mad Dog 20/20
+
+EOMODS
+
+echo "Waiting $SLEEP2 seconds for syncrepl to retry..."
+sleep $SLEEP2
+
+echo "Restarting consumer..."
+echo "======================= RESTART =======================" >> $LOG2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL >> $LOG2 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$PROVIDERPID $CONSUMERPID $PROXYPID"
+
+# get provider contextCSN
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ -s base '(objectClass=*)' contextCSN > "${PROVIDEROUT}.$CHECK" 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# check consumer contextCSN
+
+for i in 1 2 3 4 5; do
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+
+ $LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ -s base '(objectClass=*)' contextCSN > "${CONSUMEROUT}.$CHECK" 2>&1
+ RC=$?
+
+ if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ $CMP ${PROVIDEROUT}.$CHECK ${CONSUMEROUT}.$CHECK > $CMPOUT
+
+ if test $? = 0 ; then
+ break
+ fi
+done
+
+#echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' > "${PROVIDEROUT}.$CHECK" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ '(objectClass=*)' > "${CONSUMEROUT}.$CHECK" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Filtering provider results..."
+$LDIFFILTER < "${PROVIDEROUT}.$CHECK" > $PROVIDERFLT
+#echo "Filtering consumer results..."
+$LDIFFILTER < "${CONSUMEROUT}.$CHECK" > $CONSUMERFLT
+
+echo "$CHECK < Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ # FIXME: keep the original workaround in place, in case we needed again
+ if test 1 = 1 ; then
+ echo "test failed - provider and consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+
+ echo " test failed - provider and consumer databases differ (ignored by now)"
+ echo " Stopping proxy to see if it auto-recovers..."
+ kill -HUP $PROXYPID
+ wait $PROXYPID
+
+ echo " ${CHECK}.1 > Restarting proxy..."
+ echo "======================= RESTART =======================" >> $LOG3
+ $SLAPD -f $CONF3 -h $URI3 -d $LVL >> $LOG3 2>&1 &
+ PROXYPID=$!
+ if test $WAIT != 0 ; then
+ echo PROXYPID $PROXYPID
+ read foo
+ fi
+ KILLPIDS="$PROVIDERPID $CONSUMERPID $PROXYPID"
+
+ echo " Waiting $SLEEP2 seconds for syncrepl to receive changes..."
+ sleep $SLEEP2
+
+ #echo "Using ldapsearch to read all the entries from the consumer..."
+ $LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ '(objectClass=*)' > "${CONSUMEROUT}.5.1" 2>&1
+ RC=$?
+
+ if test $RC != 0 ; then
+ echo " ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ #echo "Filtering consumer results..."
+ $LDIFFILTER < "${CONSUMEROUT}.5.1" > $CONSUMERFLT
+
+ echo " ${CHECK}.1 < Comparing retrieved entries from provider and consumer..."
+ $CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+ if test $? != 0 ; then
+ echo " test failed - provider and consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+fi
+
+#
+# Modifications formerly known to fail
+#
+CHECK=`expr $CHECK + 1`
+echo "$CHECK > Performing modifications that were formerly known to fail..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+# First, back out previous change
+dn: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+delete: drink
+drink: Mad Dog 20/20
+
+# From now on, perform modifications that were formerly known to fail
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: description
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# get provider contextCSN
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ -s base '(objectClass=*)' contextCSN > "${PROVIDEROUT}.$CHECK" 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# check consumer contextCSN
+sleep 1
+for i in 1 2 3; do
+ $LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ -s base '(objectClass=*)' contextCSN > "${CONSUMEROUT}.$CHECK" 2>&1
+ RC=$?
+
+ if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ $CMP ${PROVIDEROUT}.$CHECK ${CONSUMEROUT}.$CHECK > $CMPOUT
+
+ if test $? = 0 ; then
+ break
+ fi
+
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+done
+
+#echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' > "${PROVIDEROUT}.$CHECK" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ '(objectClass=*)' > "${CONSUMEROUT}.$CHECK" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Filtering provider results..."
+$LDIFFILTER < "${PROVIDEROUT}.$CHECK" > $PROVIDERFLT
+#echo "Filtering consumer results..."
+$LDIFFILTER < "${CONSUMEROUT}.$CHECK" > $CONSUMERFLT
+
+echo "$CHECK < Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ (ignored by now)"
+ #echo "test failed - provider and consumer databases differ"
+ #test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ #exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test046-dds b/tests/scripts/test046-dds
new file mode 100755
index 0000000..f2e9baf
--- /dev/null
+++ b/tests/scripts/test046-dds
@@ -0,0 +1,575 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2005-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+case $BACKEND in ldif | null)
+ # LDIF lacks ACL support, NULL cannot hold dynamic entries
+ echo "Test does not support $BACKEND backend, test skipped"
+ exit 0
+esac
+
+if test $DDS = ddsno; then
+ echo "Dynamic Directory Services overlay not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $MCONF > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Running slapindex to index slapd database..."
+. $CONFFILTER $BACKEND < $DDSCONF > $CONF1
+$SLAPINDEX -f $CONF1
+RC=$?
+if test $RC != 0 ; then
+ echo "warning: slapindex failed ($RC)"
+ echo " assuming no indexing support"
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+echo "Creating a dynamic entry..."
+$LDAPADD -D $MANAGERDN -w $PASSWD -H $URI1 \
+ >> $TESTOUT 2>&1 << EOMODS
+dn: cn=Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: dynamicObject
+cn: Dynamic Object
+sn: Object
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Refreshing the newly created dynamic entry..."
+$LDAPEXOP -D $MANAGERDN -w $PASSWD -H $URI1 \
+ "refresh" "cn=Dynamic Object,dc=example,dc=com" "120" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapexop failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Modifying the newly created dynamic entry..."
+$LDAPMODIFY -D $MANAGERDN -w $PASSWD -H $URI1 \
+ >> $TESTOUT 2>&1 << EOMODS
+dn: cn=Dynamic Object,dc=example,dc=com
+changetype: modify
+add: userPassword
+userPassword: dynamic
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Binding as the newly created dynamic entry..."
+$LDAPWHOAMI -H $URI1 \
+ -D "cn=Dynamic Object,dc=example,dc=com" -w dynamic
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Creating a dynamic entry subordinate to another..."
+$LDAPADD -D $MANAGERDN -w $PASSWD -H $URI1 \
+ >> $TESTOUT 2>&1 << EOMODS
+dn: cn=Subordinate Dynamic Object,cn=Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: dynamicObject
+cn: Subordinate Dynamic Object
+sn: Object
+userPassword: dynamic
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+SEARCH=0
+
+SEARCH=`expr $SEARCH + 1`
+sleep $SLEEP0
+echo "# [$SEARCH] Searching the dynamic portion of the database..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=dynamicObject)' '*' entryTtl \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Creating a static entry subordinate to a dynamic one (should fail)..."
+$LDAPADD -D $MANAGERDN -w $PASSWD -H $URI1 \
+ >> $TESTOUT 2>&1 << EOMODS
+dn: cn=Subordinate Static Object,cn=Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Subordinate Static Object
+sn: Object
+userPassword: static
+EOMODS
+RC=$?
+case $RC in
+0)
+ echo "ldapadd should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+19)
+ echo "ldapadd failed ($RC)"
+ ;;
+*)
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Turning a static into a dynamic entry (should fail)..."
+$LDAPMODIFY -D $MANAGERDN -w $PASSWD -H $URI1 \
+ >> $TESTOUT 2>&1 << EOMODS
+dn: ou=People,dc=example,dc=com
+changetype: modify
+add: objectClass
+objectClass: dynamicObject
+EOMODS
+RC=$?
+case $RC in
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+65)
+ echo "ldapmodify failed ($RC)"
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Turning a dynamic into a static entry (should fail)..."
+$LDAPMODIFY -D $MANAGERDN -w $PASSWD -H $URI1 \
+ >> $TESTOUT 2>&1 << EOMODS
+dn: cn=Dynamic Object,dc=example,dc=com
+changetype: modify
+delete: objectClass
+objectClass: dynamicObject
+EOMODS
+RC=$?
+case $RC in
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+65)
+ echo "ldapmodify failed ($RC)"
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Renaming a dynamic entry..."
+$LDAPMODIFY -D $MANAGERDN -w $PASSWD -H $URI1 \
+ >> $TESTOUT 2>&1 << EOMODS
+dn: cn=Subordinate Dynamic Object,cn=Dynamic Object,dc=example,dc=com
+changetype: modrdn
+newrdn: cn=Renamed Dynamic Object
+deleteoldrdn: 1
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodrdn failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+SEARCH=`expr $SEARCH + 1`
+sleep $SLEEP0
+echo "# [$SEARCH] Searching the dynamic portion of the database..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=dynamicObject)' '*' entryTtl \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Refreshing the initial dynamic entry to make it expire earlier than the subordinate..."
+$LDAPEXOP -D $MANAGERDN -w $PASSWD -H $URI1 \
+ "refresh" "cn=Dynamic Object,dc=example,dc=com" "1" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapexop failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+SLEEP=10
+echo "Waiting $SLEEP seconds to force a subordinate/superior expiration conflict..."
+sleep $SLEEP
+
+echo "Re-vitalizing the initial dynamic entry..."
+$LDAPEXOP -D $MANAGERDN -w $PASSWD -H $URI1 \
+ "refresh" "cn=Dynamic Object,dc=example,dc=com" "120" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapexop failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Re-renaming the subordinate dynamic entry (new superior)..."
+$LDAPMODIFY -D $MANAGERDN -w $PASSWD -H $URI1 \
+ >> $TESTOUT 2>&1 << EOMODS
+dn: cn=Renamed Dynamic Object,cn=Dynamic Object,dc=example,dc=com
+changetype: modrdn
+newrdn: cn=Renamed Dynamic Object
+deleteoldrdn: 1
+newsuperior: dc=example,dc=com
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodrdn failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+SEARCH=`expr $SEARCH + 1`
+sleep $SLEEP0
+echo "# [$SEARCH] Searching the dynamic portion of the database..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=dynamicObject)' '*' entryTtl \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Deleting a dynamic entry..."
+$LDAPMODIFY -D $MANAGERDN -w $PASSWD -H $URI1 \
+ >> $TESTOUT 2>&1 << EOMODS
+dn: cn=Dynamic Object,dc=example,dc=com
+changetype: delete
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapdelete failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+SEARCH=`expr $SEARCH + 1`
+sleep $SLEEP0
+echo "# [$SEARCH] Searching the dynamic portion of the database..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=dynamicObject)' '*' entryTtl \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Refreshing the remaining dynamic entry..."
+$LDAPEXOP -D $MANAGERDN -w $PASSWD -H $URI1 \
+ "refresh" "cn=Renamed Dynamic Object,dc=example,dc=com" "1" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapexop failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+SEARCH=`expr $SEARCH + 1`
+sleep $SLEEP0
+echo "# [$SEARCH] Searching the dynamic portion of the database..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=dynamicObject)' '*' entryTtl \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+SLEEP=15
+echo "Waiting $SLEEP seconds for remaining entry to expire..."
+sleep $SLEEP
+
+SEARCH=`expr $SEARCH + 1`
+sleep $SLEEP0
+echo "# [$SEARCH] Searching the dynamic portion of the database..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=dynamicObject)' '*' entryTtl \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Meeting
+MEETINGDN="cn=Meeting,ou=Groups,dc=example,dc=com"
+echo "Creating a meeting as $BJORNSDN..."
+$LDAPMODIFY -D "$BJORNSDN" -w bjorn -H $URI1 \
+ >> $TESTOUT 2>&1 << EOMODS
+dn: $MEETINGDN
+changetype: add
+objectClass: groupOfNames
+objectClass: dynamicObject
+cn: Meeting
+member: $BJORNSDN
+
+dn: $MEETINGDN
+changetype: modify
+add: member
+member: $JOHNDDN
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Refreshing the meeting as $BJORNSDN..."
+$LDAPEXOP -D "$BJORNSDN" -w bjorn -H $URI1 \
+ "refresh" "$MEETINGDN" "120" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapexop failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Joining the meeting as $BABSDN..."
+$LDAPMODIFY -D "$BABSDN" -w bjensen -H $URI1 \
+ >> $TESTOUT 2>&1 << EOMODS
+dn: $MEETINGDN
+changetype: modify
+add: member
+member: $BABSDN
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Trying to add a member as $BABSDN (should fail)..."
+$LDAPMODIFY -D "$BABSDN" -w bjensen -H $URI1 \
+ >> $TESTOUT 2>&1 << EOMODS
+dn: $MEETINGDN
+changetype: modify
+add: member
+member: $MELLIOTDN
+EOMODS
+RC=$?
+case $RC in
+0)
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+50)
+ echo "ldapmodify failed ($RC)"
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Refreshing the meeting as $BABSDN..."
+$LDAPEXOP -D "$BABSDN" -w bjensen -H $URI1 \
+ "refresh" "$MEETINGDN" "180" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapexop failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Trying to refresh the meeting anonymously (should fail)..."
+$LDAPEXOP -H $URI1 \
+ "refresh" "$MEETINGDN" "240" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC = 0 ; then
+ echo "ldapexop should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo "Trying to refresh the meeting as $JAJDN (should fail)..."
+$LDAPEXOP -D "$JAJDN" -w "jaj" -H $URI1 \
+ "refresh" "$MEETINGDN" "240" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC = 0 ; then
+ echo "ldapexop should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo "Trying to delete the meeting as $BABSDN (should fail)..."
+$LDAPMODIFY -D "$BABSDN" -w bjensen -H $URI1 \
+ >> $TESTOUT 2>&1 << EOMODS
+dn: $MEETINGDN
+changetype: delete
+EOMODS
+RC=$?
+case $RC in
+0)
+ echo "ldapdelete should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+50)
+ echo "ldapdelete failed ($RC)"
+ ;;
+*)
+ echo "ldapdelete failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Deleting the meeting as $BJORNSDN..."
+$LDAPMODIFY -D "$BJORNSDN" -w bjorn -H $URI1 \
+ >> $TESTOUT 2>&1 << EOMODS
+dn: $MEETINGDN
+changetype: delete
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapdelete failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=$DDSOUT
+
+# dds removes entryTtl and re-adds it, changing the order of attributes
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s a < $SEARCHOUT > $SEARCHFLT
+grep -i -v -e '^entryttl: ' < $SEARCHFLT > $SEARCHFLT2
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER -s a < $LDIF > $LDIFFLT
+grep -i -v -e '^entryttl: ' < $LDIFFLT > $LDIFFLT2
+echo "Comparing filter output..."
+$CMP $SEARCHFLT2 $LDIFFLT2 > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo "Listing entryTtl values from ldapsearch results..."
+grep -i -e '^entryttl: ' < $SEARCHFLT | awk '{ print $2 }' > $SEARCHFLT2
+echo "Listing entryTtl values from original ldif used to create database..."
+grep -i -e '^entryttl: ' < $LDIFFLT | awk '{ print $2 }' > $LDIFFLT2
+
+if ! type paste >/dev/null 2>&1; then
+ echo "Cannot find 'paste' command, skipping entryTtl checks..."
+else
+ echo "Checking entryTtl appears to decrease with time..."
+ paste $SEARCHFLT2 $LDIFFLT2 | while read resultTTL savedTTL; do
+ if [ `expr $savedTTL - $resultTTL` -lt $SLEEP0 ]; then
+ echo "TTL has not reduced accordingly"
+ exit 1
+ fi
+ done
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test047-ldap b/tests/scripts/test047-ldap
new file mode 100755
index 0000000..032fe40
--- /dev/null
+++ b/tests/scripts/test047-ldap
@@ -0,0 +1,754 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+echo ""
+
+if test $BACKLDAP = ldapno ; then
+ echo "ldap backend not available, test skipped"
+ exit 0
+fi
+
+if test $RWM = rwmno ; then
+ echo "rwm (rewrite/remap) overlay not available, test skipped"
+ exit 0
+fi
+
+rm -rf $TESTDIR
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $METACONF1 > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDERED > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $METACONF2 > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$METAMANAGERDN" -H $URI2 -w $PASSWD < \
+ $LDIFMETA >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT3..."
+. $CONFFILTER $BACKEND < $GLUELDAPCONF > $CONF3
+$SLAPD -f $CONF3 -h $URI3 -d $LVL > $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI3 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+BASEDN="o=Example,c=US"
+echo "Searching base=\"$BASEDN\"..."
+echo "# searching base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+# ITS#4195: spurious matchedDN when the search scopes the main target,
+# and the searchBase is not present, so that target returns noSuchObject
+BASEDN="ou=Meta,o=Example,c=US"
+echo "Searching base=\"$BASEDN\"..."
+echo "# searching base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+#
+# Do some modifications
+#
+
+BASEDN="o=Example,c=US"
+echo "Modifying database \"$BASEDN\"..."
+$LDAPMODIFY -v -D "cn=Manager,$BASEDN" -H $URI3 -w $PASSWD \
+ -M >> $TESTOUT 2>&1 << EOMODS
+# These operations (updates with objectClass mapping) triggered ITS#3499
+dn: cn=Added Group,ou=Groups,$BASEDN
+changetype: add
+objectClass: groupOfNames
+objectClass: uidObject
+cn: Added Group
+member: cn=Added Group,ou=Groups,$BASEDN
+uid: added
+
+dn: cn=Another Added Group,ou=Groups,$BASEDN
+changetype: add
+objectClass: groupOfNames
+cn: Another Added Group
+member: cn=Added Group,ou=Groups,$BASEDN
+member: cn=Another Added Group,ou=Groups,$BASEDN
+
+dn: cn=Another Added Group,ou=Groups,$BASEDN
+changetype: modify
+add: objectClass
+objectClass: uidObject
+-
+add: uid
+uid: added
+-
+
+dn: cn=Added Group,ou=Groups,$BASEDN
+changetype: modify
+delete: objectClass
+objectClass: uidObject
+-
+delete: uid
+-
+
+dn: ou=Meta,$BASEDN
+changetype: modify
+add: description
+description: added to "ou=Meta,$BASEDN"
+-
+
+dn: ou=Who's going to handle this?,$BASEDN
+changetype: add
+objectClass: organizationalUnit
+ou: Who's going to handle this?
+description: added
+description: will be deleted
+
+dn: ou=Same as above,$BASEDN
+changetype: add
+objectClass: organizationalUnit
+ou: Same as above
+description: added right after "Who's going to handle this?"
+description: will be preserved
+
+dn: ou=Who's going to handle this?,$BASEDN
+changetype: delete
+
+dn: ou=Who's going to handle this?,ou=Meta,$BASEDN
+changetype: add
+objectClass: organizationalUnit
+ou: Who's going to handle this?
+description: added
+description: will be deleted
+
+dn: ou=Same as above,ou=Meta,$BASEDN
+changetype: add
+objectClass: organizationalUnit
+ou: Same as above
+description: added right after "Who's going to handle this?"
+description: will be preserved
+
+dn: cn=Added User,ou=Same as above,ou=Meta,$BASEDN
+changetype: add
+objectClass: inetOrgPerson
+cn: Added User
+sn: User
+userPassword: secret
+
+dn: ou=Who's going to handle this?,ou=Meta,$BASEDN
+changetype: delete
+EOMODS
+
+RC=$?
+#if test $RC != 0 ; then
+# echo "Modify failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Modify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Searching base=\"$BASEDN\"..."
+echo "# searching base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+BASEDN="o=Example,c=US"
+echo " base=\"$BASEDN\"..."
+echo "# base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" -M "$FILTER" '*' ref \
+ >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+BASEDN="o=Example,c=US"
+FILTER="(seeAlso=cn=all staff,ou=Groups,$BASEDN)"
+echo "Searching filter=\"$FILTER\""
+echo " attrs=\"seeAlso\""
+echo " base=\"$BASEDN\"..."
+echo "# searching filter=\"$FILTER\"" >> $SEARCHOUT
+echo "# attrs=\"seeAlso\"" >> $SEARCHOUT
+echo "# base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" "$FILTER" seeAlso \
+ >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+FILTER="(uid=example)"
+echo "Searching filter=\"$FILTER\""
+echo " attrs=\"uid\""
+echo " base=\"$BASEDN\"..."
+echo "# searching filter=\"$FILTER\"" >> $SEARCHOUT
+echo "# attrs=\"uid\"" >> $SEARCHOUT
+echo "# base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" "$FILTER" uid \
+ >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+FILTER="(member=cn=Another Added Group,ou=Groups,$BASEDN)"
+echo "Searching filter=\"$FILTER\""
+echo " attrs=\"member\""
+echo " base=\"$BASEDN\"..."
+echo "# searching filter=\"$FILTER\"" >> $SEARCHOUT
+echo "# attrs=\"member\"" >> $SEARCHOUT
+echo "# base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" "$FILTER" member \
+ >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Waiting 10 seconds for cached connections to timeout..."
+sleep 10
+
+echo "Searching with a timed out connection..."
+echo "# searching filter=\"$FILTER\"" >> $SEARCHOUT
+echo "# attrs=\"member\"" >> $SEARCHOUT
+echo "# base=\"$BASEDN\"" >> $SEARCHOUT
+echo "# with a timed out connection..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -D "cn=Manager,$BASEDN" -w $PASSWD \
+ -b "$BASEDN" "$FILTER" member \
+ >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+# NOTE: cannot send to $SEARCHOUT because the returned entries
+# are not predictable...
+echo "Checking server-enforced size limit..."
+echo "# Checking server-enforced size limit..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 \
+ -D "cn=Bjorn Jensen,ou=Information Technology Division,ou=People,$BASEDN" -w bjorn \
+ -b "$BASEDN" "(objectClass=*)" 1.1 \
+ >> $TESTOUT 2>&1
+RC=$?
+case $RC,$BACKEND in
+ 4,* | 0,null)
+ ;;
+ 0,*)
+ echo "Search should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+# NOTE: cannot send to $SEARCHOUT because the returned entries
+# are not predictable...
+echo "Checking client-requested size limit..."
+echo "# Checking client-requested size limit..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 \
+ -D "cn=Bjorn Jensen,ou=Information Technology Division,ou=People,$BASEDN" -w bjorn \
+ -b "$BASEDN" -z 2 "(objectClass=*)" 1.1 \
+ >> $TESTOUT 2>&1
+RC=$?
+case $RC,$BACKEND in
+ 4,* | 0,null)
+ ;;
+ 0,*)
+ echo "Search should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $METAOUT > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - meta search/modification didn't succeed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+# ITS#4458 needs patch to slapo-rwm for global rewriting of passwd_exop
+BASEDN="o=Example,c=US"
+echo "Changing password to database \"$BASEDN\"..."
+$LDAPPASSWD -H $URI3 -D "cn=Manager,$BASEDN" -w $PASSWD \
+ -s $PASSWD "cn=Ursula Hampster,ou=Alumni Association,ou=People,$BASEDN" \
+ >> $TESTOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Passwd ExOp failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+# 51)
+# echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit 0
+# ;;
+# 80)
+ 1)
+ echo "Passwd ExOp failed ($RC)! ITS#4458?"
+ ;;
+ *)
+ echo "Passwd ExOp failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+if test $RC = 0 ; then
+ echo "Binding with newly changed password to database \"$BASEDN\"..."
+ $LDAPWHOAMI -H $URI3 \
+ -D "cn=Ursula Hampster,ou=Alumni Association,ou=People,$BASEDN" \
+ -w $PASSWD >> $TESTOUT 2>&1
+ RC=$?
+ #if test $RC != 0 ; then
+ # echo "WhoAmI failed ($RC)!"
+ # test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ # exit $RC
+ #fi
+ case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ ;;
+ *)
+ echo "WhoAmI failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+ esac
+fi
+
+echo "Binding as newly added user to database \"$BASEDN\"..."
+$LDAPWHOAMI -H $URI3 \
+ -D "cn=Added User,ou=Same as above,ou=Meta,$BASEDN" \
+ -w $PASSWD >> $TESTOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "WhoAmI failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ ;;
+ *)
+ echo "WhoAmI failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Changing password to database \"$BASEDN\"..."
+$LDAPPASSWD -H $URI3 -D "cn=Manager,$BASEDN" -w $PASSWD \
+ -s meta "cn=Added User,ou=Same as above,ou=Meta,$BASEDN" \
+ >> $TESTOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Passwd ExOp failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+# 51)
+# echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit 0
+# ;;
+# 80)
+ 1)
+ echo "Passwd ExOp failed ($RC)! ITS#4458?"
+ ;;
+ *)
+ echo "Passwd ExOp failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+if test $RC = 0 ; then
+ echo "Binding with newly changed password to database \"$BASEDN\"..."
+ $LDAPWHOAMI -H $URI3 \
+ -D "cn=Added User,ou=Same as above,ou=Meta,$BASEDN" \
+ -w meta >> $TESTOUT 2>&1
+ RC=$?
+ #if test $RC != 0 ; then
+ # echo "WhoAmI failed ($RC)!"
+ # test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ # exit $RC
+ #fi
+ case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ ;;
+ *)
+ echo "WhoAmI failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+ esac
+fi
+
+echo "Binding with incorrect password to database \"$BASEDN\"..."
+$LDAPWHOAMI -H $URI3 \
+ -D "cn=Added User,ou=Same as above,ou=Meta,$BASEDN" \
+ -w bogus >> $TESTOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "WhoAmI failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC,$BACKEND in
+ 0,null)
+ ;;
+ 0,*)
+ echo "WhoAmI should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+ 51,*)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ ;;
+ *)
+ ;;
+esac
+
+echo "Binding with non-existing user to database \"$BASEDN\"..."
+$LDAPWHOAMI -H $URI3 \
+ -D "cn=Non-existing User,ou=Same as above,ou=Meta,$BASEDN" \
+ -w bogus >> $TESTOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "WhoAmI failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC,$BACKEND in
+ 0,null)
+ ;;
+ 0,*)
+ echo "WhoAmI should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+ 51,*)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ ;;
+ *)
+ ;;
+esac
+
+echo "Comparing to database \"$BASEDN\"..."
+$LDAPCOMPARE -H $URI3 \
+ "cn=Another Added Group,ou=Groups,$BASEDN" \
+ "member:cn=Added Group,ou=Groups,$BASEDN" >> $TESTOUT 2>&1
+RC=$?
+#if test $RC != 6 ; then
+# echo "Compare failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit -1
+#fi
+case $RC,$BACKEND in
+ 5,null)
+ ;;
+ 6,*)
+ ;;
+ 51,*)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ ;;
+ *)
+ echo "Compare failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ ;;
+esac
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test048-syncrepl-multiproxy b/tests/scripts/test048-syncrepl-multiproxy
new file mode 100755
index 0000000..fb231a2
--- /dev/null
+++ b/tests/scripts/test048-syncrepl-multiproxy
@@ -0,0 +1,596 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $BACKLDAP = ldapno; then
+ echo "LDAP backend not available, test skipped"
+ exit 0
+fi
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2 $DBDIR3
+
+#
+# Test replication:
+# - start provider
+# - start consumer
+# - populate over ldap
+# - perform some modifies and deleted
+# - attempt to modify the consumer (referral or chain)
+# - retrieve database over ldap and compare against expected results
+#
+
+echo "Starting provider slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $PLSRPROVIDERCONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PROVIDERPID=$!
+if test $WAIT != 0 ; then
+ echo PROVIDERPID $PROVIDERPID
+ read foo
+fi
+KILLPIDS="$PROVIDERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ '(objectClass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to create the context prefix entry in the provider..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDEREDCP > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting P1 consumer slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $RCONSUMERCONF > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+P1CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo P1CONSUMERPID $P1CONSUMERPID
+ read foo
+fi
+KILLPIDS="$PROVIDERPID $P1CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that P1 consumer slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ '(objectClass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting R1 consumer slapd on TCP/IP port $PORT3..."
+. $CONFFILTER $BACKEND < $RCONSUMERCONF | sed -e 's;\.2\.\([^/]*\)$;.3.\1;' > $CONF3
+$SLAPD -f $CONF3 -h $URI3 -d $LVL > $LOG3 2>&1 &
+R1CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo R1CONSUMERPID $R1CONSUMERPID
+ read foo
+fi
+KILLPIDS="$PROVIDERPID $P1CONSUMERPID $R1CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that R1 consumer slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI3 \
+ '(objectClass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+CHECK=1
+echo "$CHECK > Using ldapadd to populate the provider directory..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDEREDNOCP > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+#echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' > "${PROVIDEROUT}.1" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Using ldapsearch to read all the entries from the P1 consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ '(objectClass=*)' > "${CONSUMEROUT}.1" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at P1 consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Filtering provider results..."
+$LDIFFILTER < "${PROVIDEROUT}.1" > $PROVIDERFLT
+#echo "Filtering consumer results..."
+$LDIFFILTER < "${CONSUMEROUT}.1" > $CONSUMERFLT
+
+echo "$CHECK < Comparing retrieved entries from provider and P1 consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and P1 consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+#echo "Using ldapsearch to read all the entries from the R1 consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI3 \
+ '(objectClass=*)' > "${CONSUMEROUT}.1" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at R1 consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Filtering consumer results..."
+$LDIFFILTER < "${CONSUMEROUT}.1" > $CONSUMERFLT
+
+echo "$CHECK < Comparing retrieved entries from provider and R1 consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and R1 consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+CHECK=`expr $CHECK + 1`
+echo "$CHECK > Stopping the provider, sleeping $SLEEP2 seconds and restarting it..."
+kill -HUP "$PROVIDERPID"
+wait $PROVIDERPID
+sleep $SLEEP2
+
+echo "======================= RESTART =======================" >> $LOG1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL >> $LOG1 2>&1 &
+PROVIDERPID=$!
+if test $WAIT != 0 ; then
+ echo PROVIDERPID $PROVIDERPID
+ read foo
+fi
+KILLPIDS="$PROVIDERPID $P1CONSUMERPID $R1CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ '(objectClass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapmodify to modify provider directory..."
+
+#
+# Do some modifications
+#
+
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example,dc=com
+changetype: modify
+add: drink
+drink: Orange Juice
+-
+delete: sn
+sn: Jones
+-
+add: sn
+sn: Jones
+
+dn: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+replace: drink
+drink: Iced Tea
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: uniquemember
+uniquemember: cn=James A Jones 2, ou=Information Technology Division, ou=People, dc=example,dc=com
+uniquemember: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+-
+add: uniquemember
+uniquemember: cn=Dorothy Stevens, ou=Alumni Association, ou=People, dc=example,dc=com
+uniquemember: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example,dc=com
+
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc
+ =com
+changetype: modify
+delete: cn
+cn: Biiff Jensen
+
+dn: cn=Gern Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+cn: Gern Jensen
+sn: Jensen
+uid: gjensen
+title: Chief Investigator, ITD
+postaladdress: ITD $ 535 W. William St $ Ann Arbor, MI 48103
+seealso: cn=All Staff, ou=Groups, dc=example,dc=com
+drink: Coffee
+homepostaladdress: 844 Brown St. Apt. 4 $ Ann Arbor, MI 48104
+description: Very odd
+facsimiletelephonenumber: +1 313 555 7557
+telephonenumber: +1 313 555 8343
+mail: gjensen@mailgw.example.com
+homephone: +1 313 555 8844
+
+dn: ou=Retired, ou=People, dc=example,dc=com
+changetype: add
+objectclass: organizationalUnit
+ou: Retired
+
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+cn: Rosco P. Coltrane
+sn: Coltrane
+uid: rosco
+description: Fat tycoon
+
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modrdn
+newrdn: cn=Rosco P. Coltrane
+deleteoldrdn: 1
+newsuperior: ou=Retired, ou=People, dc=example,dc=com
+
+dn: cn=James A Jones 2, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: delete
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+#echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' > "${PROVIDEROUT}.2" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Using ldapsearch to read all the entries from the P1 consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ '(objectClass=*)' > "${CONSUMEROUT}.2" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at P1 consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Filtering provider results..."
+$LDIFFILTER < "${PROVIDEROUT}.2" > $PROVIDERFLT
+#echo "Filtering P1 consumer results..."
+$LDIFFILTER < "${CONSUMEROUT}.2" > $CONSUMERFLT
+
+echo "$CHECK < Comparing retrieved entries from provider and P1 consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and P1 consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+#echo "Using ldapsearch to read all the entries from the R1 consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI3 \
+ '(objectClass=*)' > "${CONSUMEROUT}.2" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at R1 consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Filtering consumer results..."
+$LDIFFILTER < "${CONSUMEROUT}.2" > $CONSUMERFLT
+
+echo "$CHECK < Comparing retrieved entries from provider and R1 consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and R1 consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+CHECK=`expr $CHECK + 1`
+echo "$CHECK > Stopping consumer to test recovery..."
+kill -HUP $P1CONSUMERPID $R1CONSUMERPID
+wait $P1CONSUMERPID
+wait $R1CONSUMERPID
+
+echo "Modifying more entries on the provider..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+add: description
+description: r1 consumer is down...
+
+dn: cn=James T. Kirk, ou=Retired, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+sn: Kirk
+uid: jtk
+cn: James T. Kirk
+
+dn: cn=Tiberius J. Hooker, ou=Retired, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+sn: Hooker
+uid: tjh
+cn: Tiberius J. Hooker
+
+EOMODS
+
+echo "Restarting P1 consumer..."
+echo "======================= RESTART =======================" >> $LOG3
+$SLAPD -f $CONF2 -h $URI2 -d $LVL >> $LOG2 2>&1 &
+P1CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo P1CONSUMERPID $P1CONSUMERPID
+ read foo
+fi
+
+echo "Restarting R1 consumer..."
+echo "======================= RESTART =======================" >> $LOG3
+$SLAPD -f $CONF3 -h $URI3 -d $LVL >> $LOG3 2>&1 &
+R1CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo R1CONSUMERPID $R1CONSUMERPID
+ read foo
+fi
+KILLPIDS="$PROVIDERPID $P1CONSUMERPID $R1CONSUMERPID"
+
+echo "Waiting $SLEEP2 seconds for syncrepl to receive changes..."
+sleep $SLEEP2
+
+#echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' > "${PROVIDEROUT}.3" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Using ldapsearch to read all the entries from the P1 consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ '(objectClass=*)' > "${CONSUMEROUT}.3" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Filtering provider results..."
+$LDIFFILTER < "${PROVIDEROUT}.3" > $PROVIDERFLT
+#echo "Filtering consumer results..."
+$LDIFFILTER < "${CONSUMEROUT}.3" > $CONSUMERFLT
+
+echo "$CHECK < Comparing retrieved entries from provider and P1 consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+#echo "Using ldapsearch to read all the entries from the R1 consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI3 \
+ '(objectClass=*)' > "${CONSUMEROUT}.3" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Filtering consumer results..."
+$LDIFFILTER < "${CONSUMEROUT}.3" > $CONSUMERFLT
+
+echo "$CHECK < Comparing retrieved entries from provider and R1 consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+CHECK=`expr $CHECK + 1`
+echo "$CHECK > Try updating the P1 consumer slapd..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI2 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example, dc=com
+changetype: modify
+add: description
+description: This write must fail because directed to a shadow context,
+description: unless the chain overlay is configured appropriately ;)
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+#echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' > "${PROVIDEROUT}.4" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Using ldapsearch to read all the entries from the P1 consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+'(objectClass=*)' > "${CONSUMEROUT}.4" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Filtering provider results..."
+$LDIFFILTER < "${PROVIDEROUT}.4" > $PROVIDERFLT
+#echo "Filtering consumer results..."
+$LDIFFILTER < "${CONSUMEROUT}.4" > $CONSUMERFLT
+
+echo "$CHECK < Comparing retrieved entries from provider and P1 consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and P1 consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+#echo "Using ldapsearch to read all the entries from the R1 consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI3 \
+'(objectClass=*)' > "${CONSUMEROUT}.4" 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#echo "Filtering consumer results..."
+$LDIFFILTER < "${CONSUMEROUT}.4" > $CONSUMERFLT
+
+echo "$CHECK < Comparing retrieved entries from provider and R1 consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and R1 consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test049-sync-config b/tests/scripts/test049-sync-config
new file mode 100755
index 0000000..88a57b9
--- /dev/null
+++ b/tests/scripts/test049-sync-config
@@ -0,0 +1,406 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+PRODIR=$TESTDIR/pro
+CONDIR=$TESTDIR/con1
+DBPRO=$PRODIR/db
+DBCON=$CONDIR/db
+CFPRO=$PRODIR/slapd.d
+CFCON=$CONDIR/slapd.d
+
+mkdir -p $TESTDIR $PRODIR $CONDIR $DBPRO $DBCON $CFPRO $CFCON
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+if test x"$SYNCMODE" = x ; then
+ SYNCMODE=rp
+fi
+case "$SYNCMODE" in
+ ro)
+ SYNCTYPE="type=refreshOnly interval=00:00:00:03"
+ ;;
+ rp)
+ SYNCTYPE="type=refreshAndPersist"
+ ;;
+ *)
+ echo "unknown sync mode $SYNCMODE"
+ exit 1;
+ ;;
+esac
+
+#
+# Test replication of dynamic config:
+# - start provider
+# - start consumer
+# - configure over ldap
+# - populate over ldap
+# - configure syncrepl over ldap
+# - retrieve database over ldap and compare against expected results
+#
+
+echo "Starting provider slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $DYNAMICCONF > $CONFLDIF
+$SLAPADD -F $CFPRO -n 0 -l $CONFLDIF
+cd $PRODIR
+$SLAPD -F ./slapd.d -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+cd $TESTWD
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Inserting syncprov overlay on provider..."
+if [ "$SYNCPROV" = syncprovmod ]; then
+ $LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+olcModuleLoad: syncprov.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for moduleLoad ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+read CONFIGPW < $CONFIGPWF
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={0}config,cn=config
+changetype: modify
+add: olcSyncRepl
+olcSyncRepl: rid=001 provider=$URI1 binddn="cn=config" bindmethod=simple
+ credentials=$CONFIGPW searchbase="cn=config" type=refreshAndPersist
+ retry="3 5 300 5" timeout=3
+-
+add: olcUpdateRef
+olcUpdateRef: $URI1
+
+dn: olcOverlay=syncprov,olcDatabase={0}config,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for syncrepl config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting consumer slapd on TCP/IP port $PORT2..."
+$SLAPADD -F $CFCON -n 0 -l $CONFLDIF
+cd $CONDIR
+$SLAPD -F ./slapd.d -h $URI2 -d $LVL > $LOG2 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+cd $TESTWD
+
+sleep 1
+
+echo "Using ldapsearch to check that consumer slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Configuring syncrepl on consumer..."
+$LDAPMODIFY -D cn=config -H $URI2 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcDatabase={0}config,cn=config
+changetype: modify
+add: olcSyncRepl
+olcSyncRepl: rid=001 provider=$URI1 binddn="cn=config" bindmethod=simple
+ credentials=$CONFIGPW searchbase="cn=config" type=refreshAndPersist
+ retry="3 5 300 5" timeout=3
+-
+add: olcUpdateRef
+olcUpdateRef: $URI1
+EOF
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Using ldapsearch to check that syncrepl received config changes..."
+RC=32
+for i in 0 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI2 -D cn=config -y $CONFIGPWF \
+ -s base -b "olcDatabase={0}config,cn=config" \
+ '(olcUpdateRef=*)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding schema and databases on provider..."
+$LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+include: file://$ABS_SCHEMADIR/core.ldif
+
+include: file://$ABS_SCHEMADIR/cosine.ldif
+
+include: file://$ABS_SCHEMADIR/inetorgperson.ldif
+
+include: file://$ABS_SCHEMADIR/openldap.ldif
+
+include: file://$ABS_SCHEMADIR/nis.ldif
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for schema config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+nullExclude="" nullOK=""
+test $BACKEND = null && nullExclude="# " nullOK="OK"
+
+if [ "$BACKENDTYPE" = mod ]; then
+ $LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/back-$BACKEND
+olcModuleLoad: back_$BACKEND.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for backend config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+$LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+olcSuffix: $BASEDN
+${nullExclude}olcDbDirectory: ./db
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+olcSyncRepl: rid=002 provider=$URI1 binddn="$MANAGERDN" bindmethod=simple
+ credentials=$PASSWD searchbase="$BASEDN" $SYNCTYPE
+ retry="3 5 300 5" timeout=3
+olcUpdateRef: $URI1
+
+dn: olcOverlay=syncprov,olcDatabase={1}${BACKEND},cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if test $INDEXDB = indexdb ; then
+ $LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcDbIndex
+olcDbIndex: objectClass,entryUUID,entryCSN eq
+olcDbIndex: cn,uid pres,eq,sub
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd modify for database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+echo "Using ldapadd to populate provider..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD -f $LDIFORDERED \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Using ldapsearch to check that syncrepl received database changes..."
+RC=32
+for i in 0 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI2 \
+ -s base -b "cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com" \
+ '(objectClass=*)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Replacing olcSyncrepl on provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={0}config,cn=config
+changetype: modify
+replace: olcSyncRepl
+olcSyncRepl: rid=003 provider=$URI1 binddn="cn=config" bindmethod=simple
+ credentials=$CONFIGPW searchbase="cn=config" type=refreshAndPersist
+ retry="3 5 300 5" timeout=3
+EOF
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Using ldapsearch to read config from the provider..."
+$LDAPSEARCH -b cn=config -D cn=config -H $URI1 -y $CONFIGPWF \
+ 'objectclass=*' > $PROVIDEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read config from the consumer..."
+$LDAPSEARCH -b cn=config -D cn=config -H $URI2 -y $CONFIGPWF \
+ 'objectclass=*' > $CONSUMEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering provider results..."
+$LDIFFILTER < $PROVIDEROUT > $PROVIDERFLT
+echo "Filtering consumer results..."
+$LDIFFILTER < $CONSUMEROUT > $CONSUMERFLT
+
+echo "Comparing retrieved configs from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer configs differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ 'objectclass=*' > $PROVIDEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI2 -w $PASSWD \
+ 'objectclass=*' > $CONSUMEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo "Filtering provider results..."
+$LDIFFILTER < $PROVIDEROUT > $PROVIDERFLT
+echo "Filtering consumer results..."
+$LDIFFILTER < $CONSUMEROUT > $CONSUMERFLT
+
+echo "Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test050-syncrepl-multiprovider b/tests/scripts/test050-syncrepl-multiprovider
new file mode 100755
index 0000000..d32ae66
--- /dev/null
+++ b/tests/scripts/test050-syncrepl-multiprovider
@@ -0,0 +1,789 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+MPR=${MPR-4}
+
+if [ $MPR -gt 9 ]; then
+MPR=9
+fi
+
+XDIR=$TESTDIR/srv
+TMP=$TESTDIR/tmp
+
+mkdir -p $TESTDIR
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+if test x"$SYNCMODE" = x ; then
+ SYNCMODE=rp
+fi
+case "$SYNCMODE" in
+ ro)
+ SYNCTYPE="type=refreshOnly interval=00:00:00:03"
+ ;;
+ rp)
+ SYNCTYPE="type=refreshAndPersist"
+ ;;
+ *)
+ echo "unknown sync mode $SYNCMODE"
+ exit 1;
+ ;;
+esac
+
+#
+# Test replication of dynamic config:
+# - start servers
+# - configure over ldap
+# - populate over ldap
+# - configure syncrepl over ldap
+# - retrieve database over ldap and compare against expected results
+#
+
+echo "Initializing server configurations..."
+n=1
+while [ $n -le $MPR ]; do
+
+DBDIR=${XDIR}$n/db
+CFDIR=${XDIR}$n/slapd.d
+
+mkdir -p ${XDIR}$n $DBDIR $CFDIR
+
+$SLAPADD -F $CFDIR -n 0 <<EOF
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcServerID: $n
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW:< file://$CONFIGPWF
+EOF
+
+n=`expr $n + 1`
+done
+
+echo "Starting server 1 on TCP/IP port $PORT1..."
+cd ${XDIR}1
+$SLAPD -F slapd.d -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+cd $TESTWD
+
+sleep 1
+
+echo "Using ldapsearch to check that server 1 is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Inserting syncprov overlay on server 1..."
+echo "" > $TMP
+if [ "$SYNCPROV" = syncprovmod ]; then
+cat <<EOF >> $TMP
+dn: cn=module,cn=config
+changetype: add
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+olcModuleLoad: syncprov.la
+
+EOF
+fi
+#
+# Note that we configure a timeout here; it's possible for both
+# servers to attempt to bind to each other while a modify to
+# cn=config is in progress. When the modify pauses the thread pool
+# neither server will progress. The timeout will drop the syncrepl
+# attempt and allow the modifies to complete.
+#
+read CONFIGPW < $CONFIGPWF
+echo "dn: cn=config" >> $TMP
+echo "changetype: modify" >> $TMP
+echo "replace: olcServerID" >> $TMP
+n=1
+while [ $n -le $MPR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldap://${LOCALHOST}:$PORT/"
+echo "olcServerID: $n $URI" >> $TMP
+n=`expr $n + 1`
+done
+
+cat <<EOF >> $TMP
+
+dn: olcOverlay=syncprov,olcDatabase={0}config,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+
+dn: olcDatabase={0}config,cn=config
+changetype: modify
+add: olcSyncRepl
+EOF
+
+n=1
+while [ $n -le $MPR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldap://${LOCALHOST}:$PORT/"
+echo "olcSyncRepl: rid=00$n provider=$URI binddn=\"cn=config\" bindmethod=simple" >> $TMP
+echo " credentials=$CONFIGPW searchbase=\"cn=config\" type=refreshAndPersist" >> $TMP
+echo " retry=\"3 10 300 5\" timeout=3" >> $TMP
+n=`expr $n + 1`
+done
+echo "-" >> $TMP
+echo "add: olcMultiProvider" >> $TMP
+echo "olcMultiProvider: TRUE" >> $TMP
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF < $TMP >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for syncrepl config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+n=2
+while [ $n -le $MPR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldap://${LOCALHOST}:$PORT/"
+LOG=$TESTDIR/slapd.$n.log
+echo "Starting server $n on TCP/IP port $PORT..."
+cd ${XDIR}$n
+$SLAPD -F ./slapd.d -h $URI -d $LVL > $LOG 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+cd $TESTWD
+
+sleep 1
+
+echo "Using ldapsearch to check that server $n is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Configuring syncrepl on server $n..."
+cat <<EOF > $TMP
+dn: olcDatabase={0}config,cn=config
+changetype: modify
+add: olcSyncRepl
+EOF
+j=1
+while [ $j -le $MPR ]; do
+P2=`expr $BASEPORT + $j`
+U2="ldap://${LOCALHOST}:$P2/"
+echo "olcSyncRepl: rid=00$j provider=$U2 binddn=\"cn=config\" bindmethod=simple" >> $TMP
+echo " credentials=$CONFIGPW searchbase=\"cn=config\" type=refreshAndPersist" >> $TMP
+echo " retry=\"3 10 300 5\" timeout=3" >> $TMP
+j=`expr $j + 1`
+done
+cat <<EOF >> $TMP
+-
+add: olcMultiProvider
+olcMultiProvider: TRUE
+EOF
+$LDAPMODIFY -D cn=config -H $URI -y $CONFIGPWF < $TMP >>$TESTOUT 2>&1
+n=`expr $n + 1`
+done
+
+echo "Adding schema and databases on server 1..."
+$LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+include: file://$ABS_SCHEMADIR/core.ldif
+
+include: file://$ABS_SCHEMADIR/cosine.ldif
+
+include: file://$ABS_SCHEMADIR/inetorgperson.ldif
+
+include: file://$ABS_SCHEMADIR/openldap.ldif
+
+include: file://$ABS_SCHEMADIR/nis.ldif
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for schema config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+nullExclude=""
+test $BACKEND = null && nullExclude="# "
+
+echo "" > $TMP
+if [ "$BACKENDTYPE" = mod ]; then
+cat <<EOF >> $TMP
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/back-$BACKEND
+olcModuleLoad: back_$BACKEND.la
+
+EOF
+fi
+
+cat <<EOF >> $TMP
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+olcSuffix: $BASEDN
+${nullExclude}olcDbDirectory: ./db
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+EOF
+
+n=1
+while [ $n -le $MPR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldap://${LOCALHOST}:$PORT/"
+
+echo "olcSyncRepl: rid=01$n provider=$URI binddn=\"$MANAGERDN\" bindmethod=simple" >> $TMP
+echo " credentials=$PASSWD searchbase=\"$BASEDN\" $SYNCTYPE" >> $TMP
+echo " retry=\"3 10 300 5\" timeout=3" >> $TMP
+n=`expr $n + 1`
+done
+
+cat <<EOF >> $TMP
+olcMultiProvider: TRUE
+
+dn: olcOverlay=syncprov,olcDatabase={1}${BACKEND},cn=config
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+EOF
+$LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF < $TMP >>$TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if test $INDEXDB = indexdb ; then
+ $LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcDbIndex
+olcDbIndex: objectClass,entryUUID,entryCSN eq
+olcDbIndex: cn,uid pres,eq,sub
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd modify for database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+echo "Using ldapadd to populate server 1..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD -f $LDIFORDERED \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP2 seconds for syncrepl to receive changes..."
+sleep $SLEEP2
+
+n=1
+while [ $n -le $MPR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldap://${LOCALHOST}:$PORT/"
+
+echo "Using ldapsearch to read config from server $n..."
+$LDAPSEARCH -b cn=config -D cn=config -H $URI -y $CONFIGPWF \
+ 'objectclass=*' > $TESTDIR/server$n.out 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at server $n ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDIFFILTER < $TESTDIR/server$n.out > $TESTDIR/server$n.flt
+
+n=`expr $n + 1`
+done
+
+n=2
+while [ $n -le $MPR ]; do
+echo "Comparing retrieved configs from server 1 and server $n..."
+$CMP $PROVIDERFLT $TESTDIR/server$n.flt > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - server 1 and server $n configs differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+n=`expr $n + 1`
+done
+
+n=1
+while [ $n -le $MPR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldap://${LOCALHOST}:$PORT/"
+
+echo "Using ldapsearch to read all the entries from server $n..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI -w $PASSWD \
+ 'objectclass=*' > $TESTDIR/server$n.out 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at server $n ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDIFFILTER < $TESTDIR/server$n.out > $TESTDIR/server$n.flt
+n=`expr $n + 1`
+done
+
+n=2
+while [ $n -le $MPR ]; do
+echo "Comparing retrieved entries from server 1 and server $n..."
+$CMP $PROVIDERFLT $TESTDIR/server$n.flt > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - server 1 and server $n databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+n=`expr $n + 1`
+done
+
+echo "Using ldapadd to populate server 2..."
+$LDAPADD -D "$MANAGERDN" -H $URI2 -w $PASSWD -f $LDIFADD1 \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for server 2 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+n=1
+while [ $n -le $MPR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldap://${LOCALHOST}:$PORT/"
+
+echo "Using ldapsearch to read all the entries from server $n..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI -w $PASSWD \
+ 'objectclass=*' > $TESTDIR/server$n.out 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at server $n ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDIFFILTER < $TESTDIR/server$n.out > $TESTDIR/server$n.flt
+n=`expr $n + 1`
+done
+
+n=2
+while [ $n -le $MPR ]; do
+echo "Comparing retrieved entries from server 1 and server $n..."
+$CMP $PROVIDERFLT $TESTDIR/server$n.flt > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - server 1 and server $n databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+n=`expr $n + 1`
+done
+
+echo "Using ldapadd to populate server 3..."
+$LDAPADD -D "$MANAGERDN" -H $URI3 -w $PASSWD \
+ << EOMODS >> $TESTOUT 2>&1
+dn: cn=Server 3 Test,dc=example,dc=com
+changetype: add
+objectClass: device
+cn: Server 3 Test
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for server 3 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+n=1
+while [ $n -le $MPR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldap://${LOCALHOST}:$PORT/"
+
+echo "Using ldapsearch to read all the entries from server $n..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI -w $PASSWD \
+ 'objectclass=*' > $TESTDIR/server$n.out 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at server $n ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDIFFILTER < $TESTDIR/server$n.out > $TESTDIR/server$n.flt
+n=`expr $n + 1`
+done
+
+n=2
+while [ $n -le $MPR ]; do
+echo "Comparing retrieved entries from server 1 and server $n..."
+$CMP $PROVIDERFLT $TESTDIR/server$n.flt > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - server 1 and server $n databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+n=`expr $n + 1`
+done
+
+echo "Using ldapmodify to add to server 1 entries that will be deleted..."
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOADDS
+dn: cn=To be deleted by server 1,dc=example,dc=com
+changetype: add
+objectClass: device
+# no distinguished values, will be added by DSA
+
+dn: cn=To be deleted by server 2,dc=example,dc=com
+changetype: add
+objectClass: device
+# no distinguished values, will be added by DSA
+
+dn: cn=To be deleted by server 3,dc=example,dc=com
+changetype: add
+objectClass: device
+# no distinguished values, will be added by DSA
+
+dn: cn=To be deleted by server 1,dc=example,dc=com
+changetype: delete
+EOADDS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+n=1
+while [ $n -le $MPR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldap://${LOCALHOST}:$PORT/"
+
+echo "Using ldapsearch to read all the entries from server $n..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI -w $PASSWD \
+ 'objectclass=*' > $TESTDIR/server$n.out 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at server $n ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDIFFILTER < $TESTDIR/server$n.out > $TESTDIR/server$n.flt
+n=`expr $n + 1`
+done
+
+n=2
+while [ $n -le $MPR ]; do
+echo "Comparing retrieved entries from server 1 and server $n..."
+$CMP $PROVIDERFLT $TESTDIR/server$n.flt > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - server 1 and server $n databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+n=`expr $n + 1`
+done
+
+echo "Using ldapmodify to delete entries from server 2..."
+$LDAPMODIFY -D "$MANAGERDN" -H $URI2 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOADDS
+dn: cn=To be deleted by server 2,dc=example,dc=com
+changetype: delete
+EOADDS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 2 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Using ldapmodify to delete entries from server 3..."
+$LDAPMODIFY -D "$MANAGERDN" -H $URI3 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOADDS
+dn: cn=To be deleted by server 3,dc=example,dc=com
+changetype: delete
+EOADDS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 3 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+n=1
+while [ $n -le $MPR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldap://${LOCALHOST}:$PORT/"
+
+echo "Using ldapsearch to read all the entries from server $n..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI -w $PASSWD \
+ 'objectclass=*' > $TESTDIR/server$n.out 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at server $n ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDIFFILTER < $TESTDIR/server$n.out > $TESTDIR/server$n.flt
+n=`expr $n + 1`
+done
+
+n=2
+while [ $n -le $MPR ]; do
+echo "Comparing retrieved entries from server 1 and server $n..."
+$CMP $PROVIDERFLT $TESTDIR/server$n.flt > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - server 1 and server $n databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+n=`expr $n + 1`
+done
+
+# kill!
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+kill -HUP $KILLPIDS
+
+# kill!
+# test $KILLSERVERS != no && wait
+wait
+
+echo "Restarting servers..."
+KILLPIDS=""
+
+echo "Starting server 1 on TCP/IP port $PORT1..."
+echo "======================= RESTART =======================" >> $LOG1
+cd ${XDIR}1
+$SLAPD -F slapd.d -h $URI1 -d $LVL >> $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+cd $TESTWD
+
+sleep 1
+
+echo "Using ldapsearch to check that server 1 is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+n=2
+while [ $n -le $MPR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldap://${LOCALHOST}:$PORT/"
+LOG=$TESTDIR/slapd.$n.log
+echo "Starting server $n on TCP/IP port $PORT..."
+cd ${XDIR}$n
+echo "======================= RESTART =======================" >> $LOG
+$SLAPD -F ./slapd.d -h $URI -d $LVL >> $LOG 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+cd $TESTWD
+n=`expr $n + 1`
+done
+
+n=2
+while [ $n -le $MPR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldap://${LOCALHOST}:$PORT/"
+echo "Using ldapsearch to check that server $n is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+n=`expr $n + 1`
+done
+
+# Insert modifications and more tests here.
+echo "Waiting $SLEEP1 seconds for servers to resync..."
+sleep $SLEEP1
+
+echo "Using ldapmodify to add/modify/delete entries from server 1..."
+for i in 1 2 3 4 5 6 7 8 9 10; do
+echo " iteration $i"
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOMODS
+dn: cn=Add-Mod-Del,dc=example,dc=com
+changetype: add
+cn: Add-Mod-Del
+objectclass: organizationalRole
+
+dn: cn=Add-Mod-Del,dc=example,dc=com
+changetype: modify
+replace: description
+description: guinea pig
+-
+
+dn: cn=Add-Mod-Del,dc=example,dc=com
+changetype: delete
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+done
+
+echo "Waiting $SLEEP1 seconds for servers to resync..."
+sleep $SLEEP1
+
+n=1
+while [ $n -le $MPR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldap://${LOCALHOST}:$PORT/"
+
+echo "Using ldapsearch to read all the entries from server $n..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI -w $PASSWD \
+ 'objectclass=*' > $TESTDIR/server$n.out 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at server $n ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDIFFILTER < $TESTDIR/server$n.out > $TESTDIR/server$n.flt
+n=`expr $n + 1`
+done
+
+n=2
+while [ $n -le $MPR ]; do
+echo "Comparing retrieved entries from server 1 and server $n..."
+$CMP $PROVIDERFLT $TESTDIR/server$n.flt > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - server 1 and server $n databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+n=`expr $n + 1`
+done
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test051-config-undo b/tests/scripts/test051-config-undo
new file mode 100755
index 0000000..322dec0
--- /dev/null
+++ b/tests/scripts/test051-config-undo
@@ -0,0 +1,117 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $UNDOCONF > $CONF1
+$SLAPADD -f $CONF1 <<EOF
+dn: o=undo
+objectClass: organization
+o: undo
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+mkdir $TESTDIR/confdir
+$SLAPD -f $CONF1 -F $TESTDIR/confdir -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo Dynamically assaulting the schema
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOF
+dn: cn={0}core,cn=schema,cn=config
+changetype: modify
+replace: olcObjectClasses
+olcObjectClasses: ( rawr )
+-
+EOF
+RC=$?
+if test $RC != 80 ; then
+ echo "invalid objectclass modify allowed ($RC)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF \
+ > $TESTOUT 2>&1 <<EOF
+dn: cn={0}core,cn=schema,cn=config
+changetype: modify
+replace: olcAttributeTypes
+olcAttributeTypes: ( rawr )
+-
+EOF
+RC=$?
+if test $RC != 80 ; then
+ echo "invalid attributeType modify allowed ($RC)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+echo Surveying the damage
+$LDAPMODIFY -D "cn=manager,o=undo" -w secret -H $URI1 <<EOF
+dn: o=foo,o=undo
+changetype: add
+objectClass: organization
+o: foo
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "schema destroyed by an unsuccessful operation"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test052-memberof b/tests/scripts/test052-memberof
new file mode 100755
index 0000000..afa5eb9
--- /dev/null
+++ b/tests/scripts/test052-memberof
@@ -0,0 +1,464 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $MEMBEROF = memberofno; then
+ echo "Memberof overlay not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $TESTDIR/confdir
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $NAKEDCONF > $CONF1
+$SLAPD -f $CONF1 -F $TESTDIR/confdir -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $TESTOUT
+
+if [ "$MEMBEROF" = memberofmod ]; then
+ echo "Inserting memberof overlay on provider..."
+ $LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: ../servers/slapd/overlays
+olcModuleLoad: memberof.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for moduleLoad ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+indexInclude="" mainInclude="" nullExclude=""
+test $INDEXDB = indexdb || indexInclude="# "
+test $MAINDB = maindb || mainInclude="# "
+case $BACKEND in
+null) nullExclude="# " ;;
+esac
+
+echo "Running ldapadd to build slapd config database..."
+$LDAPADD -H $URI1 -D 'cn=config' -w `cat $CONFIGPWF` \
+ >> $TESTOUT 2>&1 <<EOF
+dn: cn=symas group example,cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: symas group example
+olcAttributeTypes: ( 1.3.6.1.4.1.4754.31.1.1
+ NAME 'memberA' SUP distinguishedName )
+olcAttributeTypes: ( 1.3.6.1.4.1.4754.31.1.2
+ NAME 'memberOfA' SUP distinguishedName )
+olcAttributeTypes: ( 1.3.6.1.4.1.4754.31.1.3
+ NAME 'memberB' SUP distinguishedName )
+olcAttributeTypes: ( 1.3.6.1.4.1.4754.31.1.4
+ NAME 'memberOfB' SUP distinguishedName )
+olcAttributeTypes: ( 1.3.6.1.4.1.4754.31.1.5
+ NAME 'memberOfC' SUP distinguishedName )
+olcObjectClasses: ( 1.3.6.1.4.1.4754.31.2.1
+ NAME 'groupA' SUP top STRUCTURAL MUST cn MAY memberA )
+olcObjectClasses: ( 1.3.6.1.4.1.4754.31.2.2
+ NAME 'groupMemberA' SUP top AUXILIARY MAY ( memberOfA $ memberOfC ) )
+olcObjectClasses: ( 1.3.6.1.4.1.4754.31.2.3
+ NAME 'groupB' SUP top STRUCTURAL MUST cn MAY memberB )
+olcObjectClasses: ( 1.3.6.1.4.1.4754.31.2.4
+ NAME 'groupMemberB' SUP top AUXILIARY MAY memberOfB )
+
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+olcSuffix: $BASEDN
+olcRootDN: cn=Manager,$BASEDN
+olcRootPW:: c2VjcmV0
+olcMonitoring: TRUE
+${nullExclude}olcDbDirectory: $TESTDIR/db.1.a/
+${indexInclude}olcDbIndex: objectClass eq
+${indexInclude}olcDbIndex: cn pres,eq,sub
+${indexInclude}olcDbIndex: uid pres,eq,sub
+${indexInclude}olcDbIndex: sn pres,eq,sub
+${mainInclude}olcDbMode: 384"
+
+dn: olcOverlay={0}memberof,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcMemberOfConfig
+olcOverlay: {0}memberof
+olcMemberOfRefInt: TRUE
+olcMemberOfGroupOC: groupOfNames
+olcMemberOfMemberAD: member
+olcMemberOfMemberOfAD: memberOf
+
+dn: olcOverlay={1}memberof,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcMemberOfConfig
+olcOverlay: {1}memberof
+olcMemberOfRefInt: TRUE
+olcMemberOfGroupOC: groupA
+olcMemberOfMemberAD: memberA
+olcMemberOfMemberOfAD: memberOfA
+
+dn: olcOverlay={2}memberof,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcMemberOfConfig
+olcOverlay: {2}memberof
+olcMemberOfRefInt: TRUE
+olcMemberOfGroupOC: groupB
+olcMemberOfMemberAD: memberB
+olcMemberOfMemberOfAD: memberOfB
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running ldapadd to build slapd database..."
+$LDAPADD -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w secret \
+ >> $TESTOUT 2>&1 << EOF
+dn: $BASEDN
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: ou=People,$BASEDN
+objectClass: organizationalUnit
+ou: People
+
+dn: ou=Groups,$BASEDN
+objectClass: organizationalUnit
+ou: Groups
+
+dn: cn=Roger Rabbit,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: Roger Rabbit
+sn: Rabbit
+
+dn: cn=Baby Herman,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: Baby Herman
+sn: Herman
+
+dn: cn=Cartoonia,ou=Groups,$BASEDN
+objectClass: groupOfNames
+cn: Cartoonia
+member: cn=Roger Rabbit,ou=People,$BASEDN
+member: cn=Baby Herman,ou=People,$BASEDN
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Search the entire database..."
+echo "# Search the entire database..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' '*' memberOf >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running ldapmodify to add a member..."
+$LDAPMODIFY -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w secret \
+ >> $TESTOUT 2>&1 << EOF
+dn: cn=Jessica Rabbit,ou=People,$BASEDN
+changetype: add
+objectClass: inetOrgPerson
+cn: Jessica Rabbit
+sn: Rabbit
+
+dn: cn=Cartoonia,ou=Groups,$BASEDN
+changetype: modify
+add: member
+member: cn=Jessica Rabbit,ou=People,$BASEDN
+EOF
+
+echo "Re-search the entire database..."
+echo "# Re-search the entire database after adding Jessica Rabbit and Cartoonia..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' '*' memberOf >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running ldapmodify to rename a member..."
+$LDAPMODIFY -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w secret \
+ >> $TESTOUT 2>&1 << EOF
+dn: cn=Baby Herman,ou=People,$BASEDN
+changetype: modrdn
+newrdn: cn=Baby Herman Jr
+deleteoldrdn: 1
+EOF
+
+echo "Re-search the entire database..."
+echo "# Re-search the entire database after renaming Baby Herman..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' '*' memberOf >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running ldapmodify to rename a group..."
+$LDAPMODIFY -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w secret \
+ >> $TESTOUT 2>&1 << EOF
+dn: cn=Cartoonia,ou=Groups,$BASEDN
+changetype: modrdn
+newrdn: cn=Toon town
+deleteoldrdn: 1
+
+dn: cn=Toon town,ou=Groups,$BASEDN
+changetype: modrdn
+newrdn: cn=Toon Town
+deleteoldrdn: 1
+EOF
+
+echo "Re-search the entire database..."
+echo "# Re-search the entire database after renaming Cartoonia..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' '*' memberOf >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running ldapmodify to add self..."
+$LDAPMODIFY -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w secret \
+ >> $TESTOUT 2>&1 << EOF
+dn: cn=Toon Town,ou=Groups,$BASEDN
+changetype: modify
+add: member
+member: cn=Toon Town,ou=Groups,$BASEDN
+EOF
+
+echo "Re-search the entire database..."
+echo "# Re-search the entire database after adding Toon Town to self..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' '*' memberOf >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running ldapdelete to remove a member..."
+$LDAPMODIFY -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w secret \
+ >> $TESTOUT 2>&1 << EOF
+dn: cn=Baby Herman Jr,ou=People,$BASEDN
+changetype: delete
+EOF
+
+echo "Re-search the entire database..."
+echo "# Re-search the entire database after deleting Baby Herman..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' '*' memberOf >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running ldapdelete to remove a group..."
+$LDAPMODIFY -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w secret \
+ >> $TESTOUT 2>&1 << EOF
+dn: cn=Toon Town,ou=Groups,$BASEDN
+changetype: delete
+EOF
+
+echo "Re-search the entire database..."
+echo "# Re-search the entire database after deleting Toon Town..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' '*' memberOf >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding groups with MAY member type schemas..."
+$LDAPMODIFY -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w secret \
+ >> $TESTOUT 2>&1 <<EOF
+dn: cn=Roger Rabbit,ou=People,$BASEDN
+changetype: delete
+
+dn: cn=Jessica Rabbit,ou=People,$BASEDN
+changetype: delete
+
+dn: cn=person1,ou=People,$BASEDN
+changetype: add
+objectClass: person
+objectClass: groupMemberA
+objectClass: groupMemberB
+cn: person1
+sn: person1
+
+dn: cn=person2,ou=People,$BASEDN
+changetype: add
+objectClass: person
+objectClass: groupMemberA
+objectClass: groupMemberB
+cn: person2
+sn: person2
+
+dn: cn=group1,ou=Groups,$BASEDN
+changetype: add
+objectclass: groupA
+cn: group1
+memberA: cn=person1,ou=People,$BASEDN
+memberA: cn=person2,ou=People,$BASEDN
+
+dn: cn=group2,ou=Groups,$BASEDN
+changetype: add
+objectclass: groupB
+cn: group2
+memberB: cn=person1,ou=People,$BASEDN
+memberB: cn=person2,ou=People,$BASEDN
+
+dn: cn=group1,ou=Groups,$BASEDN
+changetype: modify
+delete: memberA
+
+EOF
+
+echo "Re-search the entire database..."
+echo "# Re-search the entire database after adding groups with MAY member type schemas..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' '*' memberOf >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running ldapmodify to reconfigure the schema used..."
+$LDAPADD -H $URI1 -D 'cn=config' -w `cat $CONFIGPWF` \
+ >> $TESTOUT 2>&1 <<EOF
+dn: olcOverlay={1}memberof,olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+replace: olcMemberOfMemberOfAD
+olcMemberOfMemberOfAD: memberOfC
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Updating groups to expose the new setting..."
+$LDAPMODIFY -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w secret \
+ >> $TESTOUT 2>&1 <<EOF
+dn: cn=group1,ou=Groups,$BASEDN
+changetype: modify
+add: memberA
+memberA: cn=person1,ou=People,$BASEDN
+memberA: cn=person2,ou=People,$BASEDN
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Re-search the entire database..."
+echo "# Re-search the entire database after updating memberof configuration..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' '*' memberOf >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=$MEMBEROFOUT
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test053-syncprov-glue b/tests/scripts/test053-syncprov-glue
new file mode 100755
index 0000000..a75a318
--- /dev/null
+++ b/tests/scripts/test053-syncprov-glue
@@ -0,0 +1,502 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+# This script tests a bug where syncprov used on a glue database
+# with a subordinate syncrepl consumer database looses a read-lock
+# on the glue suffix entry when a modification is received on the
+# syncrepl consumer. The bug is only triggered when there is an
+# active syncrepl consumers of the glue suffix entry.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+PRODDIR=$TESTDIR/prod
+PRO2DIR=$TESTDIR/pro2
+CONSDIR=$TESTDIR/cons
+CFPROD=$PRODDIR/slapd.d
+CFPRO2=$PRO2DIR/slapd.d
+CFCONS=$CONSDIR/slapd.d
+
+mkdir -p $TESTDIR
+mkdir -p $PRODDIR $CFPROD $PRODDIR/db $PRODDIR/ou1
+mkdir -p $PRO2DIR $CFPRO2 $PRO2DIR/db
+mkdir -p $CONSDIR $CFCONS $CONSDIR/db
+
+cd $TESTDIR
+
+KILLPIDS=
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+if test x"$SYNCMODE" = x ; then
+ SYNCMODE=rp
+fi
+case "$SYNCMODE" in
+ ro)
+ SYNCTYPE="type=refreshOnly interval=00:00:00:03"
+ ;;
+ rp)
+ SYNCTYPE="type=refreshAndPersist"
+ ;;
+ *)
+ echo "unknown sync mode $SYNCMODE"
+ exit 1;
+ ;;
+esac
+
+echo "Initializing provider configurations..."
+$SLAPADD -F $CFPROD -n 0 <<EOF
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcServerID: 1
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW:< file://$CONFIGPWF
+
+EOF
+
+echo "Initializing provider2 configurations..."
+$SLAPADD -F $CFPRO2 -n 0 <<EOF
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW:< file://$CONFIGPWF
+
+EOF
+
+$SLAPADD -F $CFCONS -n 0 <<EOF
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW:< file://$CONFIGPWF
+EOF
+
+echo "Starting provider slapd on TCP/IP port $PORT1..."
+cd $PRODDIR
+$SLAPD -F slapd.d -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+cd $TESTWD
+sleep 1
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting provider2 slapd on TCP/IP port $PORT2..."
+cd $PRO2DIR
+$SLAPD -F slapd.d -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+cd $TESTWD
+sleep 1
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting consumer slapd on TCP/IP port $PORT3..."
+cd $CONSDIR
+$SLAPD -F slapd.d -h $URI3 -d $LVL > $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+cd $TESTWD
+sleep 1
+echo "Using ldapsearch to check that consumer slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI3 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+for uri in $URI1 $URI2 $URI3; do
+ echo "Adding schema on $uri..."
+ $LDAPADD -D cn=config -H $uri -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+include: file://$ABS_SCHEMADIR/core.ldif
+
+include: file://$ABS_SCHEMADIR/cosine.ldif
+
+include: file://$ABS_SCHEMADIR/inetorgperson.ldif
+
+include: file://$ABS_SCHEMADIR/openldap.ldif
+
+include: file://$ABS_SCHEMADIR/nis.ldif
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for schema config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ [ "$BACKENDTYPE" = mod ] || continue
+
+ echo "Adding backend module on $uri..."
+ $LDAPADD -D cn=config -H $uri -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/back-$BACKEND
+olcModuleLoad: back_$BACKEND.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for backend module ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+done
+
+echo "Adding databases on provider..."
+if [ "$SYNCPROV" = syncprovmod ]; then
+ $LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+olcModuleLoad: syncprov.la
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for moduleLoad ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+nullExclude="" nullOK=""
+test $BACKEND = null && nullExclude="# " nullOK="OK"
+
+$LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+${nullExclude}olcDbDirectory: $PRODDIR/db
+olcSuffix: $BASEDN
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for provider database config1 ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcOverlay={0}glue,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+olcOverlay: {0}glue
+
+dn: olcOverlay={1}syncprov,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: {1}syncprov
+
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+${nullExclude}olcDbDirectory: $PRODDIR/ou1
+olcSubordinate: TRUE
+olcSuffix: ou=ou1,$BASEDN
+olcRootDN: $MANAGERDN
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for provider database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding databases on provider2..."
+if [ "$SYNCPROV" = syncprovmod ]; then
+ $LDAPADD -D cn=config -H $URI2 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+olcModuleLoad: syncprov.la
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for moduleLoad ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+$LDAPADD -D cn=config -H $URI2 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+${nullExclude}olcDbDirectory: $PRO2DIR/db
+olcSuffix: $BASEDN
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+
+dn: olcOverlay={0}syncprov,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: {0}syncprov
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for provider database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding databases on consumer..."
+$LDAPADD -D cn=config -H $URI3 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+${nullExclude}olcDbDirectory: $CONSDIR/db
+olcSuffix: $BASEDN
+olcRootDN: $MANAGERDN
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for consumer database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Populating provider..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: dcObject
+dc: example
+o: Example, Inc
+
+dn: ou=ou1,dc=example,dc=com
+objectClass: top
+objectClass: organizationalUnit
+ou: ou1
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed to populate provider entry ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Populating provider2..."
+$LDAPADD -D "$MANAGERDN" -H $URI2 -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: dcObject
+dc: example
+o: Example, Inc
+
+dn: ou=ou1,dc=example,dc=com
+objectClass: top
+objectClass: organizationalUnit
+ou: ou1
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed to populate provider entry ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding syncrepl on provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcSyncRepl
+olcSyncRepl: rid=1 provider=$URI2 searchbase="ou=ou1,$BASEDN"
+ binddn="$MANAGERDN" bindmethod=simple credentials=$PASSWD
+ $SYNCTYPE retry="3 5 300 5" timeout=1
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed to add syncrepl consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding syncrepl consumer on consumer..."
+$LDAPMODIFY -D cn=config -H $URI3 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcSyncRepl
+olcSyncRepl: rid=1 provider=$URI1 searchbase="$BASEDN"
+ binddn="$MANAGERDN" bindmethod=simple credentials=$PASSWD
+ $SYNCTYPE retry="3 5 300 5" timeout=1
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed to add syncrepl consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check that consumer received changes..."
+RC=32
+for i in 0 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI3 \
+ -s base -b "ou=ou1,$BASEDN" \
+ '(objectClass=*)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapmodify to modify provider2..."
+$LDAPADD -D "$MANAGERDN" -H $URI2 -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: ou=ou1,dc=example,dc=com
+changetype: modify
+add: description
+description: Modify1
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1
+
+echo "Using ldapsearch to check that consumer received changes..."
+RC=32
+for i in 0 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI3 \
+ -s base -b "ou=ou1,$BASEDN" \
+ '(description=Modify1)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapmodify to modify glue suffix on provider..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: dc=example,dc=com
+changetype: modify
+add: description
+description: Test1
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed to modify suffix ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+test "$lock_bug" = 2 && exit 2
+
+echo ">>>>> Test succeeded"
+
+exit 0
diff --git a/tests/scripts/test054-syncreplication-parallel-load b/tests/scripts/test054-syncreplication-parallel-load
new file mode 100755
index 0000000..98644c9
--- /dev/null
+++ b/tests/scripts/test054-syncreplication-parallel-load
@@ -0,0 +1,377 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR4
+
+#
+# Test replication:
+# - start provider
+# - start consumer
+# - populate over ldap
+# - perform some modifies and deleted
+# - attempt to modify the consumer (referral or chain)
+# - retrieve database over ldap and compare against expected results
+#
+
+echo "Starting provider slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $SRPROVIDERCONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to create the context prefix entry in the provider..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDEREDCP > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting consumer slapd on TCP/IP port $PORT4..."
+. $CONFFILTER $BACKEND < $P1SRCONSUMERCONF > $CONF4
+$SLAPD -f $CONF4 -h $URI4 -d $LVL > $LOG4 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that consumer slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI4 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+MORELDIF=$TESTDIR/more.ldif
+TESTOUT1=$TESTDIR/testout1.out
+TESTOUT2=$TESTDIR/testout2.out
+sed -e 's/[Oo][Uu]=/ou=More /g' -e 's/^[Oo][Uu]: /ou: More /' \
+ -e 's/cn=Manager/cn=More Manager/g' \
+ -e 's/^cn: Manager/cn: More Manager/' \
+ $LDIFORDEREDNOCP > $MORELDIF
+
+echo "Using ldapadd to populate the provider directory..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDEREDNOCP > $TESTOUT1 2>&1 &
+C1PID=$!
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $MORELDIF > $TESTOUT2 2>&1 &
+C2PID=$!
+wait $C1PID $C2PID
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Stopping the provider, sleeping 10 seconds and restarting it..."
+kill -HUP "$PID"
+wait $PID
+sleep 10
+echo "RESTART" >> $LOG1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL >> $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID $CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting 10 seconds to let the system catch up"
+sleep 10
+
+echo "Using ldapmodify to modify provider directory..."
+
+#
+# Do some modifications
+#
+
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example,dc=com
+changetype: modify
+add: drink
+drink: Orange Juice
+-
+delete: sn
+sn: Jones
+-
+add: sn
+sn: Jones
+
+dn: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+replace: drink
+drink: Iced Tea
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: uniquemember
+uniquemember: cn=James A Jones 2, ou=Information Technology Division, ou=People, dc=example,dc=com
+uniquemember: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+-
+add: uniquemember
+uniquemember: cn=Dorothy Stevens, ou=Alumni Association, ou=People, dc=example,dc=com
+uniquemember: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example,dc=com
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: description
+
+dn: cn=Gern Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+cn: Gern Jensen
+sn: Jensen
+uid: gjensen
+title: Chief Investigator, ITD
+postaladdress: ITD $ 535 W. William St $ Ann Arbor, MI 48103
+seealso: cn=All Staff, ou=Groups, dc=example,dc=com
+drink: Coffee
+homepostaladdress: 844 Brown St. Apt. 4 $ Ann Arbor, MI 48104
+description: Very odd
+facsimiletelephonenumber: +1 313 555 7557
+telephonenumber: +1 313 555 8343
+mail: gjensen@mailgw.example.com
+homephone: +1 313 555 8844
+
+dn: ou=Retired, ou=People, dc=example,dc=com
+changetype: add
+objectclass: organizationalUnit
+ou: Retired
+
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+cn: Rosco P. Coltrane
+sn: Coltrane
+uid: rosco
+description: Fat tycoon
+
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modrdn
+newrdn: cn=Rosco P. Coltrane
+deleteoldrdn: 1
+newsuperior: ou=Retired, ou=People, dc=example,dc=com
+
+dn: cn=James A Jones 2, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: delete
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldappasswd to change some passwords..."
+$LDAPPASSWD -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ 'cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com' \
+ > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Stopping consumer to test recovery..."
+kill -HUP $CONSUMERPID
+wait $CONSUMERPID
+
+echo "Modifying more entries on the provider..."
+$LDAPMODIFY -v -D "$MANAGERDN" -H $URI1 -w $PASSWD >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Rosco P. Coltrane, ou=Retired, ou=People, dc=example,dc=com
+changetype: delete
+
+dn: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+add: drink
+drink: Mad Dog 20/20
+
+dn: cn=Rosco P. Coltrane, ou=Retired, ou=People, dc=example,dc=com
+changetype: add
+objectclass: OpenLDAPperson
+sn: Coltrane
+uid: rosco
+cn: Rosco P. Coltrane
+
+EOMODS
+
+echo "Restarting consumer..."
+echo "RESTART" >> $LOG4
+$SLAPD -f $CONF4 -h $URI4 -d $LVL >> $LOG4 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$PID $CONSUMERPID"
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+if test ! $BACKLDAP = "ldapno" ; then
+ echo "Try updating the consumer slapd..."
+ $LDAPMODIFY -v -D "$MANAGERDN" -H $URI4 -w $PASSWD > \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example, dc=com
+changetype: modify
+add: description
+description: This write must fail because directed to a shadow context,
+description: unless the chain overlay is configured appropriately ;)
+
+EOMODS
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ # ITS#4964
+ echo "Trying to change some passwords on the consumer..."
+ $LDAPPASSWD -D "$MANAGERDN" -H $URI4 -w $PASSWD \
+ 'cn=Barbara Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com' \
+ > $TESTOUT 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+fi
+
+OPATTRS="entryUUID creatorsName createTimestamp modifiersName modifyTimestamp"
+
+echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectclass=*)' '*' $OPATTRS > $PROVIDEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI4 \
+ '(objectclass=*)' '*' $OPATTRS > $CONSUMEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo "Filtering provider results..."
+$LDIFFILTER < $PROVIDEROUT > $PROVIDERFLT
+echo "Filtering consumer results..."
+$LDIFFILTER < $CONSUMEROUT > $CONSUMERFLT
+
+echo "Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test055-valregex b/tests/scripts/test055-valregex
new file mode 100755
index 0000000..33df905
--- /dev/null
+++ b/tests/scripts/test055-valregex
@@ -0,0 +1,117 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+LVL=acl
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $VALREGEXCONF > $CONF1
+$SLAPADD -f $CONF1 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing attribute value regex substitution..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+echo "# Try an attribute vale regex that match, but substitute does not"
+echo "# this should fail"
+$LDAPMODIFY -D "$JAJDN" -H $URI1 -w jaj >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+changetype: modify
+replace: sn
+sn: foobarbuz
+EOMODS
+RC=$?
+case $RC in
+50)
+ echo "ldapmodify failed as expected"
+ ;;
+0)
+ if test $BACKEND != null ; then
+ echo "ldapmodify should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ fi
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "# Try an attribute vale regex that match and substitute does"
+echo "# this should succeed"
+$LDAPMODIFY -D "$JAJDN" -H $URI1 -w jaj >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Bjorn Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+changetype: modify
+replace: sn
+sn: James A Jones 1
+EOMODS
+RC=$?
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+case $RC in
+0)
+ echo "ldapmodify succeed as expected"
+ ;;
+*)
+ echo "ldapmodify failed ($RC)!"
+ exit $RC
+ ;;
+esac
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test056-monitor b/tests/scripts/test056-monitor
new file mode 100755
index 0000000..0c5241f
--- /dev/null
+++ b/tests/scripts/test056-monitor
@@ -0,0 +1,162 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Starting slapd on TCP/IP port $PORT..."
+. $CONFFILTER $BACKEND < $SCHEMACONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+echo "Using ldapsearch to read connection monitor entries..."
+$LDAPSEARCH -S "" -b "$CONNECTIONSMONITORDN" -H $URI1 \
+ 'objectclass=*' \
+ structuralObjectClass entryDN \
+ monitorConnectionProtocol monitorConnectionOpsReceived \
+ monitorConnectionOpsExecuting monitorConnectionOpsPending \
+ monitorConnectionOpsCompleted monitorConnectionGet \
+ monitorConnectionRead monitorConnectionWrite \
+ monitorConnectionMask monitorConnectionAuthzDN \
+ monitorConnectionListener monitorConnectionLocalAddress \
+ > $SEARCHOUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Compare results, ignoring possible difference of IPv4/IPv6 localhost address
+localrewrite='s/=127\.0\.0\.1:/=LOCAL:/; s/=\[::1\]:/=LOCAL:/'
+echo "Filtering ldapsearch results..."
+sed -e "$localrewrite" < $SEARCHOUT | $LDIFFILTER > $SEARCHFLT
+echo "Filtering expected data..."
+. $CONFFILTER < $MONITOROUT1 | sed -e "$localrewrite" | $LDIFFILTER > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - connection monitor output is not correct"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Using ldapsearch to read database monitor entries..."
+$LDAPSEARCH -S "" -b "$DATABASESMONITORDN" -H $URI1 \
+ 'objectclass=*' \
+ structuralObjectClass entryDN namingContexts readOnly \
+ monitorIsShadow monitorContext \
+ > $SEARCHOUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -b monitor < $SEARCHOUT > $SEARCHFLT
+
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $MONITOROUT2 > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - database monitor output is not correct"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Using ldapsearch to read statistics monitor entries..."
+$LDAPSEARCH -S "" -b "$STATISTICSMONITORDN" -H $URI1 \
+ '(|(cn=Entries)(cn=PDU)(cn=Referrals))' \
+ structuralObjectClass monitorCounter entryDN \
+ > $SEARCHOUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -b monitor < $SEARCHOUT > $SEARCHFLT
+
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $MONITOROUT3 > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - statistics monitor output is not correct"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Using ldapsearch to read operation monitor entries..."
+$LDAPSEARCH -S "" -b "$OPERATIONSMONITORDN" -H $URI1 \
+ 'objectclass=*' \
+ structuralObjectClass monitorOpInitiated monitorOpCompleted entryDN \
+ > $SEARCHOUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -b monitor < $SEARCHOUT > $SEARCHFLT
+
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $MONITOROUT4 > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - operations monitor output is not correct"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
+
diff --git a/tests/scripts/test057-memberof-refint b/tests/scripts/test057-memberof-refint
new file mode 100755
index 0000000..c30a4c1
--- /dev/null
+++ b/tests/scripts/test057-memberof-refint
@@ -0,0 +1,280 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## Portions Copyright 2008 Red Hat, Inc.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $MEMBEROF = memberofno; then
+ echo "Memberof overlay not available, test skipped"
+ exit 0
+fi
+
+if test $REFINT = refintno; then
+ echo "Referential Integrity overlay not available, test skipped"
+ exit 0
+fi
+
+if test $BACKEND = wt ; then
+ echo "back-wt does not support subtree rename"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $TESTDIR/confdir
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $NAKEDCONF > $CONF1
+$SLAPD -f $CONF1 -F $TESTDIR/confdir -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $TESTOUT
+
+indexInclude="" mainInclude="" nullExclude=""
+test $INDEXDB = indexdb || indexInclude="# "
+test $MAINDB = maindb || mainInclude="# "
+case $BACKEND in
+null) nullExclude="# " ;;
+esac
+
+if [ "$MEMBEROF" = memberofmod ]; then
+ echo "Inserting memberof overlay on provider..."
+ $LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: ../servers/slapd/overlays
+olcModuleLoad: memberof.la
+olcModuleLoad: refint.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for moduleLoad ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+echo "Running ldapadd to build slapd config database..."
+$LDAPADD -H $URI1 -D 'cn=config' -w `cat $CONFIGPWF` \
+ >> $TESTOUT 2>&1 <<EOF
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+olcSuffix: $BASEDN
+olcRootDN: cn=Manager,$BASEDN
+olcRootPW:: c2VjcmV0
+olcMonitoring: TRUE
+${nullExclude}olcDbDirectory: $TESTDIR/db.1.a/
+${indexInclude}olcDbIndex: objectClass eq
+${indexInclude}olcDbIndex: cn pres,eq,sub
+${indexInclude}olcDbIndex: uid pres,eq,sub
+${indexInclude}olcDbIndex: sn pres,eq,sub
+${mainInclude}olcDbMode: 384
+
+# {0}memberof, {1}$BACKEND, config
+dn: olcOverlay={0}memberof,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcMemberOfConfig
+olcOverlay: {0}memberof
+olcMemberOfRefInt: TRUE
+olcMemberOfGroupOC: groupOfNames
+olcMemberOfMemberAD: member
+olcMemberOfMemberOfAD: memberOf
+
+# {1}refint, {1}$BACKEND, config
+dn: olcOverlay={1}refint,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcRefintConfig
+olcOverlay: {1}refint
+olcRefintAttribute: member
+olcRefintAttribute: memberOf
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running ldapadd to build slapd database..."
+$LDAPADD -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w secret \
+ >> $TESTOUT 2>&1 << EOF
+dn: $BASEDN
+objectClass: organization
+objectClass: dcObject
+o: Example, Inc.
+dc: example
+
+dn: ou=People,$BASEDN
+objectClass: organizationalUnit
+ou: People
+
+dn: ou=Groups,$BASEDN
+objectClass: organizationalUnit
+ou: Groups
+
+dn: cn=Roger Rabbit,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: Roger Rabbit
+sn: Rabbit
+
+dn: cn=Baby Herman,ou=People,$BASEDN
+objectClass: inetOrgPerson
+cn: Baby Herman
+sn: Herman
+
+dn: cn=Cartoonia,ou=Groups,$BASEDN
+objectClass: groupOfNames
+cn: Cartoonia
+member: cn=Roger Rabbit,ou=People,$BASEDN
+member: cn=Baby Herman,ou=People,$BASEDN
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Search the entire database..."
+echo "# Search the entire database..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' '*' memberOf >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running ldapmodify to rename subtree..."
+$LDAPMODIFY -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w secret \
+ >> $TESTOUT 2>&1 << EOF
+dn: ou=People,$BASEDN
+changetype: modrdn
+newrdn: ou=Toons
+deleteoldrdn:1
+newsuperior: $BASEDN
+EOF
+
+# refint runs in a background thread, so it most likely won't complete
+# before the modify returns. Give it some time to execute.
+sleep $SLEEP0
+
+echo "Re-search the entire database..."
+echo "# Re-search the entire database..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' '*' memberOf >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running ldapmodify to rename subtree..."
+$LDAPMODIFY -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w secret \
+ >> $TESTOUT 2>&1 << EOF
+dn: ou=Groups,$BASEDN
+changetype: modrdn
+newrdn: ou=Studios
+deleteoldrdn:1
+newsuperior: $BASEDN
+EOF
+
+sleep $SLEEP0
+
+echo "Re-search the entire database..."
+echo "# Re-search the entire database..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' '*' memberOf >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running ldapdelete to remove a member..."
+$LDAPMODIFY -H $URI1 \
+ -D "cn=Manager,$BASEDN" -w secret \
+ >> $TESTOUT 2>&1 << EOF
+dn: cn=Baby Herman,ou=Toons,$BASEDN
+changetype: delete
+EOF
+
+sleep $SLEEP0
+
+echo "Re-search the entire database..."
+echo "# Re-search the entire database..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ '(objectClass=*)' '*' memberOf >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=$MEMBEROFREFINTOUT
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test058-syncrepl-asymmetric b/tests/scripts/test058-syncrepl-asymmetric
new file mode 100755
index 0000000..22015a7
--- /dev/null
+++ b/tests/scripts/test058-syncrepl-asymmetric
@@ -0,0 +1,2471 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+# This script tests a configuration scenario as described in these URLs:
+#
+# http://www.openldap.org/lists/openldap-devel/200806/msg00041.html
+# http://www.openldap.org/lists/openldap-devel/200806/msg00054.html
+#
+# Search for "TEST:" to find each major test this script performs.
+
+# The configuration here consist of 3 "sites", each with a "provider" and
+# a "search" server. One of the sites is the "central", the other two
+# are called "site1" and "site2".
+
+# The following notations are used in variable names below to identify
+# these servers, the first number defines the $URL# and $PORT# variable
+# that server uses:
+#
+# 1: SMC_* Site Provider Central
+# 2: SM1_* Site Provider 1
+# 3: SM2_* Site Provider 2
+# 4: SSC_* Search Site Central
+# 5: SS1_* Search Site 1
+# 6: SS2_* Search Site 2
+
+# The provider servers all have a set of subordinate databases glued below
+# the same suffix database. Each of the providers are the provider for at
+# least one of these subordinate databases, but there are never more
+# than one provider for any single database. I.e, this is neither a
+# traditional single-provider configuration, nor what most people think
+# of as multi-provider, but more what can be called multiple providers.
+
+# The central provider replicates to the two other providers, and receives
+# updates from them of the backends they are the provider for. There is
+# no direct connection between the other two provider servers. All of the
+# providers have the syncprov overlay configured on the glue database.
+
+# The search servers replicates from the provider server at their site.
+# They all have a single database with the glue suffix, but their
+# database configuration doesn't matter much in this test. (This
+# database layout was originally created before gluing was introduced
+# in OpenLDAP, which is why the search servers doesn't use it).
+
+# The primary objective for gluing the backend databases is not to make
+# them look like one huge database but to create a common search suffix
+# for the clients. Searching is mostly done on the search servers, only
+# updates are done on the providers.
+
+# It varies which backends that are replicated to which server (hence
+# the name asymmetric in this test). Access control rules on the
+# providers are used to control what their consumers receives. The table
+# below gives an overview of which backend (the columns) that are
+# replicated to which server (the rows). A "M" defines the provider for
+# the backend, a "S" is a replica, and "-" means it is not replicated
+# there. Oh, the table probably looks wrong without the 4-position
+# tab-stops OpenLDAP uses...
+
+# glue ou1 ou2 sm1ou1 sm1ou2 sm2ou1 sm2ou2
+# smc M M M S S S -
+# sm1 S S - M M - -
+# sm2 S S S S - M M
+# ssc S S - - S - -
+# ss1 S S - S S - -
+# ss2 S S S - - S S
+
+# On the central provider syncrepl is configured on the subordinate
+# databases, as it varies which backends that exists on its providers.
+# Had it been used on the glue database then syncrepl would have removed
+# the backends replicated from site1 but not present on site2 when it
+# synchronizes with site2 (and vice versa).
+#
+# All the other servers uses syncrepl on the glue database, since
+# replicating more than one subordinate database from the same provider
+# creates (as of the writing of this test script) race conditions that
+# causes the replication to fail, as the race tests at the end shows.
+
+# The databases controlled by syncrepl all have $UPDATEDN as their
+# RootDN, while the provider servers has other RootDN values for the
+# backends they are the backend for them self. This violates the current
+# guidelines for gluing databases, which states that the same rootdn
+# should be used on all of them. Unfortunately, this cannot be done on
+# site providers 1 and 2. The backends they manage locally are either not
+# present on the central provider, or when so they are not replicated back
+# to their source, which causes syncrepl to try to remove the content of
+# these backends when it synchronizes with the central provider. The
+# differing rootdn values used on the backends controlled by syncrepl
+# and those managed locally prevents it from succeeding in this. As
+# noted above, moving syncrepl to the subordinate databases is currently
+# not an option since that creates race conditions.
+
+# The binddn values used in the syncrepl configurations are chosen to
+# make the configuration and access control rules easiest to set up. It
+# occasionally uses a DN that is also used as a RootDN. This is not a
+# good practice and should not be taken as an example for real
+# configurations!
+
+# This script will print the content of any invalid contextCSN values it
+# detects if the environment variable CSN_VERBOSE is non-empty. The
+# environment variable RACE_TESTS can be set to the number of race test
+# iterations the script should perform.
+
+if test "$BACKEND" = ldif ; then
+ echo "$BACKEND backend does not support access controls, test skipped"
+ exit 0
+fi
+
+echo "Test 058 is currently disabled"
+exit 0
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+SMC_DIR=$TESTDIR/smc
+SM1_DIR=$TESTDIR/sm1
+SM2_DIR=$TESTDIR/sm2
+SS1_DIR=$TESTDIR/ss1
+SS2_DIR=$TESTDIR/ss2
+SSC_DIR=$TESTDIR/ssc
+
+MNUM=1
+
+mkdir -p $TESTDIR
+
+for dir in $SMC_DIR $SM1_DIR $SM2_DIR $SS1_DIR $SS2_DIR $SSC_DIR; do
+ mkdir -p $dir $dir/slapd.d $dir/db
+done
+
+mkdir -p $SMC_DIR/ou1 $SMC_DIR/sm1ou1 $SMC_DIR/sm1ou2
+mkdir -p $SMC_DIR/ou2 $SMC_DIR/sm2ou1
+mkdir -p $SM1_DIR/ou1 $SM1_DIR/sm1ou1 $SM1_DIR/sm1ou2
+mkdir -p $SM2_DIR/ou2 $SM2_DIR/sm1ou1 $SM2_DIR/sm2ou1 $SM2_DIR/sm2ou2
+
+cd $TESTDIR
+
+KILLPIDS=
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+ID=1
+
+if test $WAIT != 0 ; then
+ RETRY="1 60"
+else
+ RETRY="1 10"
+fi
+
+echo "Initializing provider configurations..."
+for dir in $SMC_DIR $SM1_DIR $SM2_DIR; do
+ $SLAPADD -F $dir/slapd.d -n 0 <<EOF
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcServerID: $ID
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW:< file://$CONFIGPWF
+
+EOF
+ ID=`expr $ID + 1`
+done
+
+echo "Initializing search configurations..."
+for dir in $SS1_DIR $SS2_DIR $SSC_DIR; do
+ $SLAPADD -F $dir/slapd.d -n 0 <<EOF
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW:< file://$CONFIGPWF
+
+EOF
+done
+
+echo "Starting central provider slapd on TCP/IP port $PORT1..."
+cd $SMC_DIR
+$SLAPD -F slapd.d -h $URI1 -d $LVL > $LOG1 2>&1 &
+SMC_PID=$!
+if test $WAIT != 0 ; then
+ echo PID $SMC_PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $SMC_PID"
+cd $TESTWD
+sleep 1
+echo "Using ldapsearch to check that central provider slapd is running..."
+for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting site1 provider slapd on TCP/IP port $PORT2..."
+cd $SM1_DIR
+$SLAPD -F slapd.d -h $URI2 -d $LVL > $LOG2 2>&1 &
+SM1_PID=$!
+if test $WAIT != 0 ; then
+ echo PID $SM1_PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $SM1_PID"
+cd $TESTWD
+sleep 1
+echo "Using ldapsearch to check that site1 provider is running..."
+for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting site2 provider slapd on TCP/IP port $PORT3..."
+cd $SM2_DIR
+$SLAPD -F slapd.d -h $URI3 -d $LVL > $LOG3 2>&1 &
+SM2_PID=$!
+if test $WAIT != 0 ; then
+ echo PID $SM2_PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $SM2_PID"
+cd $TESTWD
+sleep 1
+echo "Using ldapsearch to check that site2 provider is running..."
+for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI3 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting central search slapd on TCP/IP port $PORT4..."
+cd $SSC_DIR
+$SLAPD -F slapd.d -h $URI4 -d $LVL > $LOG4 2>&1 &
+SSC_PID=$!
+if test $WAIT != 0 ; then
+ echo PID $SSC_PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $SSC_PID"
+cd $TESTWD
+sleep 1
+echo "Using ldapsearch to check that central search slapd is running..."
+for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI4 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+echo "Starting site1 search slapd on TCP/IP port $PORT5..."
+cd $SS1_DIR
+$SLAPD -F slapd.d -h $URI5 -d $LVL > $LOG5 2>&1 &
+SS1_PID=$!
+if test $WAIT != 0 ; then
+ echo PID $SS1_PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $SS1_PID"
+cd $TESTWD
+sleep 1
+echo "Using ldapsearch to check that site1 search slapd is running..."
+for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI5 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+echo "Starting site2 search slapd on TCP/IP port $PORT6..."
+cd $SS2_DIR
+$SLAPD -F slapd.d -h $URI6 -d $LVL > $LOG6 2>&1 &
+SS2_PID=$!
+if test $WAIT != 0 ; then
+ echo PID $SS2_PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $SS2_PID"
+cd $TESTWD
+sleep 1
+echo "Using ldapsearch to check that site2 search slapd is running..."
+for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI6 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+for uri in $URI1 $URI2 $URI3 $URI4 $URI5 $URI6; do
+ echo "Adding schema on $uri..."
+ $LDAPADD -D cn=config -H $uri -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+include: file://$ABS_SCHEMADIR/core.ldif
+
+include: file://$ABS_SCHEMADIR/cosine.ldif
+
+include: file://$ABS_SCHEMADIR/inetorgperson.ldif
+
+include: file://$ABS_SCHEMADIR/openldap.ldif
+
+include: file://$ABS_SCHEMADIR/nis.ldif
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for schema config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ [ "$BACKENDTYPE" = mod ] || continue
+
+ echo "Adding backend module on $uri..."
+ $LDAPADD -D cn=config -H $uri -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/back-$BACKEND
+olcModuleLoad: back_$BACKEND.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for backend module ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+done
+
+echo "Adding database config on central provider..."
+if [ "$SYNCPROV" = syncprovmod ]; then
+ $LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+olcModuleLoad: syncprov.la
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for moduleLoad ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+nullExclude="" nullOK="" wantNoObj=32
+test $BACKEND = null && nullExclude="# " nullOK="OK" wantNoObj=0
+
+$LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+${nullExclude}olcDbDirectory: $SMC_DIR/db
+olcSuffix: $BASEDN
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+
+dn: olcOverlay={0}glue,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+olcOverlay: {0}glue
+
+dn: olcOverlay={1}syncprov,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: {1}syncprov
+olcSpCheckpoint: 3 1
+
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+${nullExclude}olcDbDirectory: $SMC_DIR/ou1
+olcSubordinate: TRUE
+olcSuffix: ou=ou1,$BASEDN
+olcRootDN: $MANAGERDN
+
+dn: olcDatabase={2}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {2}$BACKEND
+${nullExclude}olcDbDirectory: $SMC_DIR/ou2
+olcSubordinate: TRUE
+olcSuffix: ou=ou2,$BASEDN
+olcRootDN: $MANAGERDN
+
+dn: olcDatabase={3}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {3}$BACKEND
+${nullExclude}olcDbDirectory: $SMC_DIR/sm1ou1
+olcSubordinate: TRUE
+olcSuffix: ou=sm1ou1,$BASEDN
+olcRootDN: $UPDATEDN
+
+dn: olcDatabase={4}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {4}$BACKEND
+${nullExclude}olcDbDirectory: $SMC_DIR/sm1ou2
+olcSubordinate: TRUE
+olcSuffix: ou=sm1ou2,$BASEDN
+olcRootDN: $UPDATEDN
+
+dn: olcDatabase={5}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {5}$BACKEND
+${nullExclude}olcDbDirectory: $SMC_DIR/sm2ou1
+olcSubordinate: TRUE
+olcSuffix: ou=sm2ou1,$BASEDN
+olcRootDN: $UPDATEDN
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for central provider database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding database config on site1 provider..."
+if [ "$SYNCPROV" = syncprovmod ]; then
+ $LDAPADD -D cn=config -H $URI2 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+olcModuleLoad: syncprov.la
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for moduleLoad ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+$LDAPADD -D cn=config -H $URI2 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+${nullExclude}olcDbDirectory: $SM1_DIR/db
+olcSuffix: $BASEDN
+olcRootDN: $UPDATEDN
+
+dn: olcOverlay={0}glue,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+olcOverlay: {0}glue
+
+dn: olcOverlay={1}syncprov,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: {1}syncprov
+
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+${nullExclude}olcDbDirectory: $SM1_DIR/ou1
+olcSubordinate: TRUE
+olcSuffix: ou=ou1,$BASEDN
+olcRootDN: $UPDATEDN
+
+dn: olcDatabase={2}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {2}$BACKEND
+${nullExclude}olcDbDirectory: $SM1_DIR/sm1ou1
+olcSubordinate: TRUE
+olcSuffix: ou=sm1ou1,$BASEDN
+olcRootDN: ou=sm1ou1,$BASEDN
+olcRootPW: $PASSWD
+
+dn: olcDatabase={3}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {3}$BACKEND
+${nullExclude}olcDbDirectory: $SM1_DIR/sm1ou2
+olcSubordinate: TRUE
+olcSuffix: ou=sm1ou2,$BASEDN
+olcRootDN: ou=sm1ou1,$BASEDN
+
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for site1 provider database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding database config on site2 provider..."
+if [ "$SYNCPROV" = syncprovmod ]; then
+ $LDAPADD -D cn=config -H $URI3 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+olcModuleLoad: syncprov.la
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for moduleLoad ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+$LDAPADD -D cn=config -H $URI3 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+${nullExclude}olcDbDirectory: $SM2_DIR/db
+olcSuffix: $BASEDN
+olcRootDN: $UPDATEDN
+
+dn: olcOverlay={0}glue,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+olcOverlay: {0}glue
+
+dn: olcOverlay={1}syncprov,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: {1}syncprov
+olcSpCheckpoint: 1 1
+
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+${nullExclude}olcDbDirectory: $SM2_DIR/ou2
+olcSubordinate: TRUE
+olcSuffix: ou=ou2,$BASEDN
+olcRootDN: $UPDATEDN
+
+dn: olcDatabase={2}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {2}$BACKEND
+${nullExclude}olcDbDirectory: $SM2_DIR/sm1ou1
+olcSubordinate: TRUE
+olcSuffix: ou=sm1ou1,$BASEDN
+olcRootDN: $UPDATEDN
+
+dn: olcDatabase={3}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {3}$BACKEND
+${nullExclude}olcDbDirectory: $SM2_DIR/sm2ou1
+olcSubordinate: TRUE
+olcSuffix: ou=sm2ou1,$BASEDN
+olcRootDN: ou=sm2ou1,$BASEDN
+olcRootPW: $PASSWD
+
+dn: olcDatabase={4}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {4}$BACKEND
+${nullExclude}olcDbDirectory: $SM2_DIR/sm2ou2
+olcSubordinate: TRUE
+olcSuffix: ou=sm2ou2,$BASEDN
+olcRootDN: ou=sm2ou1,$BASEDN
+
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for site2 provider database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding access rules on central provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={-1}frontend,cn=config
+changetype: modify
+add: olcAccess
+olcAccess: to dn.exact=dc=example,dc=com
+ by * read
+olcAccess: to dn.subtree=ou=ou1,dc=example,dc=com
+ by * read
+olcAccess: to dn.subtree=ou=ou2,dc=example,dc=com
+ by dn.exact=ou=ou1,dc=example,dc=com none
+ by dn.exact=ou=ou2,dc=example,dc=com read
+ by dn.exact=dc=example,dc=com none
+ by * read
+olcAccess: to dn.subtree=ou=sm1ou1,dc=example,dc=com
+ by dn.exact=ou=ou1,dc=example,dc=com none
+ by dn.exact=ou=ou2,dc=example,dc=com read
+ by dn.exact=dc=example,dc=com none
+ by * read
+olcAccess: to dn.subtree=ou=sm1ou2,dc=example,dc=com
+ by dn.exact=ou=ou1,dc=example,dc=com none
+ by dn.exact=ou=ou2,dc=example,dc=com none
+ by dn.exact=dc=example,dc=com read
+ by * read
+olcAccess: to dn.subtree=ou=sm2ou1,dc=example,dc=com
+ by dn.exact=ou=ou1,dc=example,dc=com none
+ by dn.exact=ou=ou2,dc=example,dc=com none
+ by dn.exact=dc=example,dc=com none
+ by * read
+olcAccess: to * by * read
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for central provider access config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding access rules on site1 provider..."
+$LDAPMODIFY -D cn=config -H $URI2 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={-1}frontend,cn=config
+changetype: modify
+add: olcAccess
+olcAccess: to dn.subtree=dc=example,dc=com
+ by * read
+olcAccess: to * by * read
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for site1 provider access config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding access rules on site2 provider..."
+$LDAPMODIFY -D cn=config -H $URI3 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={-1}frontend,cn=config
+changetype: modify
+add: olcAccess
+olcAccess: to dn.exact=dc=example,dc=com
+ by * read
+olcAccess: to dn.subtree=ou=ou2,dc=example,dc=com
+ by * read
+olcAccess: to dn.subtree=ou=sm1ou1,dc=example,dc=com
+ by users none
+ by * read
+olcAccess: to dn.subtree=ou=sm2ou1,dc=example,dc=com
+ by * read
+olcAccess: to dn.subtree=ou=sm2ou2,dc=example,dc=com
+ by dn.exact=dc=example,dc=com read
+ by users none
+ by * read
+olcAccess: to * by * read
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for site2 provider access config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding database config on central search..."
+$LDAPADD -D cn=config -H $URI4 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+${nullExclude}olcDbDirectory: $SSC_DIR/db
+olcSuffix: $BASEDN
+olcRootDN: $UPDATEDN
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for central search database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding database config on site1 search..."
+$LDAPADD -D cn=config -H $URI5 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+${nullExclude}olcDbDirectory: $SS1_DIR/db
+olcSuffix: $BASEDN
+olcRootDN: $UPDATEDN
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for site1 search database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding database config on site2 search..."
+$LDAPADD -D cn=config -H $URI6 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+${nullExclude}olcDbDirectory: $SS2_DIR/db
+olcSuffix: $BASEDN
+olcRootDN: $UPDATEDN
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for site2 search database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Populating central provider..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: dc=example,dc=com
+objectClass: top
+objectClass: organization
+objectClass: dcObject
+dc: example
+o: Example, Inc
+userPassword: $PASSWD
+
+dn: ou=ou1,dc=example,dc=com
+objectClass: top
+objectClass: organizationalUnit
+ou: ou1
+userPassword: $PASSWD
+
+dn: ou=ou2,dc=example,dc=com
+objectClass: top
+objectClass: organizationalUnit
+ou: ou2
+userPassword: $PASSWD
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed to populate central provider entry ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding syncrepl on site1 provider..."
+$LDAPMODIFY -D cn=config -H $URI2 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={4}$BACKEND,cn=config
+changetype: modify
+add: olcSyncRepl
+olcSyncRepl: rid=1 provider=$URI1 searchbase="$BASEDN"
+ binddn="ou=ou1,$BASEDN" bindmethod=simple credentials=$PASSWD
+ type=refreshAndPersist retry="$RETRY" timeout=1
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed to add syncrepl on site1 provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding syncrepl on site2 provider..."
+$LDAPMODIFY -D cn=config -H $URI3 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={5}$BACKEND,cn=config
+changetype: modify
+add: olcSyncRepl
+olcSyncRepl: rid=1 provider=$URI1 searchbase="$BASEDN"
+ binddn="ou=ou2,$BASEDN" bindmethod=simple credentials=$PASSWD
+ type=refreshAndPersist retry="$RETRY" timeout=1
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed to add syncrepl on site2 provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+sleep 1
+
+echo "Using ldapsearch to check that site1 provider received changes..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI2 \
+ -s base -b "ou=ou1,$BASEDN" \
+ '(objectClass=*)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check that site2 provider received changes..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI3 \
+ -s base -b "ou=ou1,$BASEDN" \
+ '(objectClass=*)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1
+
+echo "Populating site1 provider..."
+$LDAPADD -D "ou=sm1ou1,$BASEDN" -H $URI2 -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: ou=sm1ou1,dc=example,dc=com
+objectClass: top
+objectClass: organizationalUnit
+ou: sm1ou1
+
+dn: ou=sm1ou2,dc=example,dc=com
+objectClass: top
+objectClass: organizationalUnit
+ou: sm1ou2
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed to populate site1 provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1
+
+echo "Populating site2 provider..."
+$LDAPADD -D "ou=sm2ou1,$BASEDN" -H $URI3 -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: ou=sm2ou1,dc=example,dc=com
+objectClass: top
+objectClass: organizationalUnit
+ou: sm2ou1
+
+dn: ou=sm2ou2,dc=example,dc=com
+objectClass: top
+objectClass: organizationalUnit
+ou: sm2ou2
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed to populate site2 provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ERRORS=0
+
+# TEST:
+# Stop site1 provider when adding syncrepl to the central provider. When
+# site1 provider is started again both it and the central provider will have
+# the same number of contextCSN values, but the ones on central provider
+# will be the newest. The central provider will not update its contextCSN
+# values unless the bug in ITS#5597 have been fixed.
+echo "Stopping site1 provider..."
+kill -HUP "$SM1_PID"
+wait "$SM1_PID"
+KILLPIDS=`echo "$KILLPIDS " | sed -e "s/ $SM1_PID / /"`;
+SM1_PID=
+
+echo "Adding syncrepl on central provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={3}$BACKEND,cn=config
+changetype: modify
+add: olcSyncRepl
+olcSyncRepl: rid=3 provider=$URI2 searchbase="ou=sm1ou1,$BASEDN"
+ binddn="ou=sm1ou1,$BASEDN" bindmethod=simple credentials=$PASSWD
+ type=refreshAndPersist retry="$RETRY" timeout=1
+
+dn: olcDatabase={5}$BACKEND,cn=config
+changetype: modify
+add: olcSyncRepl
+olcSyncRepl: rid=5 provider=$URI3 searchbase="ou=sm2ou1,$BASEDN"
+ binddn="ou=sm2ou1,$BASEDN" bindmethod=simple credentials=$PASSWD
+ type=refreshAndPersist retry="$RETRY" timeout=1
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed to add syncrepl on central provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+sleep 1
+echo "Using ldapsearch to check that central provider received site2 entries..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI1 \
+ -s base -b "ou=sm2ou1,$BASEDN" \
+ '(objectClass=*)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Test for ITS#6716, modify on central provider to ensure that the CSN
+# order is "sid2 < sid3 < sid1". When site1 provider starts it is likely
+# to sync with central provider before it syncs with site1 provider. When
+# central provider syncs with site1 provider they will share the sid1 and
+# sid3 CSNs, the additional sid2 CSN hold by site1 provider will be the
+# oldest. Central provider will not receive the changes made on site1
+# provider unless it completely ignores the CSNs presented by central
+# provider.
+echo "Using ldapmodify to modify central provider..."
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: dc=example,dc=com
+changetype: modify
+add: description
+description: Modify$MNUM
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if test -z "$SM1_PID" ; then
+ echo "Restarting site1 provider slapd on TCP/IP port $PORT2..."
+ cd $SM1_DIR
+ $SLAPD -F slapd.d -h $URI2 -d $LVL >> $LOG2 2>&1 &
+ SM1_PID=$!
+ if test $WAIT != 0 ; then
+ echo PID $SM1_PID
+ read foo
+ fi
+ KILLPIDS="$KILLPIDS $SM1_PID"
+ cd $TESTWD
+ sleep 1
+fi
+sleep 1
+echo "Using ldapsearch to check that site1 provider is running..."
+for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check that central provider received site1 entries..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI1 \
+ -s base -b "ou=sm1ou1,$BASEDN" \
+ '(objectClass=*)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check that site1 provider received central provider update..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI2 \
+ -s base -b "$BASEDN" \
+ "(description=Modify$MNUM)" 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check that site2 provider received central provider update..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI3 \
+ -s base -b "$BASEDN" \
+ "(description=Modify$MNUM)" 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Test done, now some more initialization...
+
+echo "Adding syncrepl consumer on central search..."
+$LDAPMODIFY -D cn=config -H $URI4 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcSyncRepl
+olcSyncRepl: rid=1 provider=$URI1 searchbase="$BASEDN"
+ binddn="$BASEDN" bindmethod=simple credentials=$PASSWD
+ type=refreshAndPersist retry="$RETRY" timeout=1
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed to add syncrepl on site1 search ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding syncrepl consumer on site1 search..."
+$LDAPMODIFY -D cn=config -H $URI5 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcSyncRepl
+olcSyncRepl: rid=1 provider=$URI2 searchbase="$BASEDN"
+ binddn="$BASEDN" bindmethod=simple credentials=$PASSWD
+ type=refreshAndPersist retry="$RETRY" timeout=1
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed to add syncrepl on site1 search ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding syncrepl consumer on site2 search..."
+$LDAPMODIFY -D cn=config -H $URI6 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcSyncRepl
+olcSyncRepl: rid=1 provider=$URI3 searchbase="$BASEDN"
+ binddn="$BASEDN" bindmethod=simple credentials=$PASSWD
+ type=refreshAndPersist retry="$RETRY" timeout=1
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed to add syncrepl on site2 search ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+sleep 1
+
+echo "Using ldapsearch to check that central search received changes..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI4 \
+ -s base -b "$BASEDN" \
+ '(objectClass=*)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check that site1 search received changes..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI5 \
+ -s base -b "$BASEDN" \
+ '(objectClass=*)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check that site2 search received changes..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI6 \
+ -s base -b "$BASEDN" \
+ '(objectClass=*)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Create a script that will check the contextCSN values of all servers,
+# and restart them to re-synchronize if it finds any errors:
+cat > $TESTDIR/checkcsn.sh <<'EOF'
+#!/bin/sh
+
+CSN_ERRORS=0
+
+CSN1=`$LDAPSEARCH -H $URI1 -b $BASEDN -s base contextCSN | grep contextCSN | sort`
+CSN2=`$LDAPSEARCH -H $URI2 -b $BASEDN -s base contextCSN | grep contextCSN | sort`
+CSN3=`$LDAPSEARCH -H $URI3 -b $BASEDN -s base contextCSN | grep contextCSN | sort`
+CSN4=`$LDAPSEARCH -H $URI4 -b $BASEDN -s base contextCSN | grep contextCSN | sort`
+CSN5=`$LDAPSEARCH -H $URI5 -b $BASEDN -s base contextCSN | grep contextCSN | sort`
+CSN6=`$LDAPSEARCH -H $URI6 -b $BASEDN -s base contextCSN | grep contextCSN | sort`
+
+if test -z "$CSN1" ; then
+ echo "ERROR: contextCSN empty on central provider"
+ CSN_ERRORS=`expr $CSN_ERRORS + 1`
+fi
+nCSN=`echo "$CSN1" | wc -l`
+if test "$nCSN" -ne 3 ; then
+ echo "ERROR: Wrong contextCSN count on central provider, should be 3"
+ CSN_ERRORS=`expr $CSN_ERRORS + 1`
+ if test -n "$CSN_VERBOSE"; then
+ echo "$CSN1"
+ fi
+fi
+if test -z "$CSN2" -o "$CSN1" != "$CSN2" ; then
+ echo "ERROR: contextCSN mismatch between central provider and site1 provider"
+ CSN_ERRORS=`expr $CSN_ERRORS + 1`;
+ if test -n "$CSN_VERBOSE"; then
+ echo "contextCSN on central provider:"
+ echo "$CSN1"
+ echo "contextCSN on site1 provider:"
+ echo "$CSN2"
+ fi
+fi
+if test -z "$CSN3" -o "$CSN1" != "$CSN3" ; then
+ echo "ERROR: contextCSN mismatch between central provider and site2 provider"
+ CSN_ERRORS=`expr $CSN_ERRORS + 1`;
+ if test -n "$CSN_VERBOSE"; then
+ echo "contextCSN on central provider:"
+ echo "$CSN1"
+ echo "contextCSN on site2 provider:"
+ echo "$CSN3"
+ fi
+fi
+if test -z "$CSN4" -o "$CSN1" != "$CSN4" ; then
+ echo "ERROR: contextCSN mismatch between central provider and central search"
+ CSN_ERRORS=`expr $CSN_ERRORS + 1`;
+ if test -n "$CSN_VERBOSE"; then
+ echo "contextCSN on central provider:"
+ echo "$CSN1"
+ echo "contextCSN on central search:"
+ echo "$CSN4"
+ fi
+fi
+if test -z "$CSN5" -o "$CSN2" != "$CSN5" ; then
+ echo "ERROR: contextCSN mismatch between site1 provider and site1 search"
+ CSN_ERRORS=`expr $CSN_ERRORS + 1`;
+ if test -n "$CSN_VERBOSE"; then
+ echo "contextCSN on site1 provider:"
+ echo "$CSN2"
+ echo "contextCSN on site1 search:"
+ echo "$CSN5"
+ fi
+fi
+if test -z "$CSN6" -o "$CSN3" != "$CSN6" ; then
+ echo "ERROR: contextCSN mismatch between site2 provider and site2 search:"
+ CSN_ERRORS=`expr $CSN_ERRORS + 1`;
+ if test -n "$CSN_VERBOSE"; then
+ echo "contextCSN on site2 provider:"
+ echo "$CSN3"
+ echo "contextCSN on site2 search:"
+ echo "$CSN6"
+ fi
+fi
+
+if test $CSN_ERRORS != 0 ; then
+ echo "Stopping all servers to synchronize contextCSN..."
+ kill -HUP $KILLPIDS
+ for pid in $KILLPIDS ; do wait $pid ; done
+ KILLPIDS=
+
+ echo "Restarting site1 provider slapd on TCP/IP port $PORT2..."
+ cd $SM1_DIR
+ $SLAPD -F slapd.d -h $URI2 -d $LVL >> $LOG2 2>&1 &
+ SM1_PID=$!
+ if test $WAIT != 0 ; then
+ echo PID $SM1_PID
+ read foo
+ fi
+ KILLPIDS="$KILLPIDS $SM1_PID"
+ cd $TESTWD
+ sleep 1
+ echo "Using ldapsearch to check that site1 provider is running..."
+ for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+ done
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Restarting site2 provider slapd on TCP/IP port $PORT3..."
+ cd $SM2_DIR
+ $SLAPD -F slapd.d -h $URI3 -d $LVL >> $LOG3 2>&1 &
+ SM2_PID=$!
+ if test $WAIT != 0 ; then
+ echo PID $SM2_PID
+ read foo
+ fi
+ KILLPIDS="$KILLPIDS $SM2_PID "
+ cd $TESTWD
+ sleep 1
+ echo "Using ldapsearch to check that site2 provider is running..."
+ for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI3 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+ done
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Restarting central provider slapd on TCP/IP port $PORT1..."
+ cd $SMC_DIR
+ $SLAPD -F slapd.d -h $URI1 -d $LVL >> $LOG1 2>&1 &
+ SMC_PID=$!
+ if test $WAIT != 0 ; then
+ echo PID $SMC_PID
+ read foo
+ fi
+ KILLPIDS="$KILLPIDS $SMC_PID"
+ cd $TESTWD
+ sleep 1
+ echo "Using ldapsearch to check that central provider slapd is running..."
+ for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+ done
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Sleeping 5 seconds to allow contextCSN to synchronize..."
+ sleep 5
+
+ echo "Stopping site1 and site2 provider..."
+ kill -HUP $SM1_PID $SM2_PID
+ for pid in $SM1_PID $SM2_PID ; do wait $pid ; done
+ KILLPIDS=" $SMC_PID"
+
+ echo "Restarting site1 provider slapd on TCP/IP port $PORT2..."
+ cd $SM1_DIR
+ $SLAPD -F slapd.d -h $URI2 -d $LVL >> $LOG2 2>&1 &
+ SM1_PID=$!
+ if test $WAIT != 0 ; then
+ echo PID $SM1_PID
+ read foo
+ fi
+ KILLPIDS="$KILLPIDS $SM1_PID"
+ cd $TESTWD
+ sleep 1
+ echo "Using ldapsearch to check that site1 provider is running..."
+ for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+ done
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Restarting site2 provider slapd on TCP/IP port $PORT3..."
+ cd $SM2_DIR
+ $SLAPD -F slapd.d -h $URI3 -d $LVL >> $LOG3 2>&1 &
+ SM2_PID=$!
+ if test $WAIT != 0 ; then
+ echo PID $SM2_PID
+ read foo
+ fi
+ KILLPIDS="$KILLPIDS $SM2_PID"
+ cd $TESTWD
+ sleep 1
+ echo "Using ldapsearch to check that site2 provider is running..."
+ for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI3 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+ done
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Sleeping 5 seconds to allow contextCSN to synchronize..."
+ sleep 5
+
+ echo "Restarting central search slapd on TCP/IP port $PORT4..."
+ cd $SSC_DIR
+ $SLAPD -F slapd.d -h $URI4 -d $LVL >> $LOG4 2>&1 &
+ SSC_PID=$!
+ if test $WAIT != 0 ; then
+ echo PID $SSC_PID
+ read foo
+ fi
+ KILLPIDS="$KILLPIDS $SSC_PID"
+ cd $TESTWD
+ sleep 1
+ echo "Using ldapsearch to check that central search slapd is running..."
+ for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI4 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+ done
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Restarting site1 search slapd on TCP/IP port $PORT5..."
+ cd $SS1_DIR
+ $SLAPD -F slapd.d -h $URI5 -d $LVL >> $LOG5 2>&1 &
+ SS1_PID=$!
+ if test $WAIT != 0 ; then
+ echo PID $SS1_PID
+ read foo
+ fi
+ KILLPIDS="$KILLPIDS $SS1_PID"
+ cd $TESTWD
+ sleep 1
+ echo "Using ldapsearch to check that site1 search slapd is running..."
+ for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI5 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+ done
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Restarting site2 search slapd on TCP/IP port $PORT6..."
+ cd $SS2_DIR
+ $SLAPD -F slapd.d -h $URI6 -d $LVL >> $LOG6 2>&1 &
+ SS2_PID=$!
+ if test $WAIT != 0 ; then
+ echo PID $SS2_PID
+ read foo
+ fi
+ KILLPIDS="$KILLPIDS $SS2_PID"
+ cd $TESTWD
+ sleep 1
+ echo "Using ldapsearch to check that site2 search slapd is running..."
+ for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI6 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+ done
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Sleeping 5 seconds to allow contextCSN to synchronize..."
+ sleep 5
+
+ echo "Checking contextCSN after restart..."
+ CSN1=`$LDAPSEARCH -H $URI1 -b $BASEDN -s base contextCSN | grep contextCSN | sort`
+ CSN2=`$LDAPSEARCH -H $URI2 -b $BASEDN -s base contextCSN | grep contextCSN | sort`
+ CSN3=`$LDAPSEARCH -H $URI3 -b $BASEDN -s base contextCSN | grep contextCSN | sort`
+ CSN4=`$LDAPSEARCH -H $URI4 -b $BASEDN -s base contextCSN | grep contextCSN | sort`
+ CSN5=`$LDAPSEARCH -H $URI5 -b $BASEDN -s base contextCSN | grep contextCSN | sort`
+ CSN6=`$LDAPSEARCH -H $URI6 -b $BASEDN -s base contextCSN | grep contextCSN | sort`
+ if test -z "$CSN1" ; then
+ echo "ERROR: contextCSN empty on central provider"
+ CSN_ERRORS=`expr $CSN_ERRORS + 1`
+ fi
+
+ if test -z "$CSN2" -o "$CSN1" != "$CSN2" ; then
+ echo "ERROR: contextCSN mismatch between central provider and site1 provider"
+ CSN_ERRORS=`expr $CSN_ERRORS + 1`;
+ if test -n "$CSN_VERBOSE"; then
+ echo "contextCSN on central provider:"
+ echo "$CSN1"
+ echo "contextCSN on site1 provider:"
+ echo "$CSN2"
+ fi
+ fi
+ if test -z "$CSN3" -o "$CSN1" != "$CSN3" ; then
+ echo "ERROR: contextCSN mismatch between central provider and site2 provider"
+ CSN_ERRORS=`expr $CSN_ERRORS + 1`;
+ if test -n "$CSN_VERBOSE"; then
+ echo "contextCSN on central provider:"
+ echo "$CSN1"
+ echo "contextCSN on site2 provider:"
+ echo "$CSN3"
+ fi
+ fi
+ if test -z "$CSN4" -o "$CSN1" != "$CSN4" ; then
+ echo "ERROR: contextCSN mismatch between central provider and central search"
+ CSN_ERRORS=`expr $CSN_ERRORS + 1`;
+ if test -n "$CSN_VERBOSE"; then
+ echo "contextCSN on central provider:"
+ echo "$CSN1"
+ echo "contextCSN on central search:"
+ echo "$CSN4"
+ fi
+ fi
+ if test -z "$CSN5" -o "$CSN2" != "$CSN5" ; then
+ echo "ERROR: contextCSN mismatch between site1 provider and site1 search"
+ CSN_ERRORS=`expr $CSN_ERRORS + 1`;
+ if test -n "$CSN_VERBOSE"; then
+ echo "contextCSN on site1 provider:"
+ echo "$CSN2"
+ echo "contextCSN on site1 search:"
+ echo "$CSN5"
+ fi
+ fi
+ if test -z "$CSN6" -o "$CSN3" != "$CSN6" ; then
+ echo "ERROR: contextCSN mismatch between site2 provider and site2 search:"
+ CSN_ERRORS=`expr $CSN_ERRORS + 1`;
+ if test -n "$CSN_VERBOSE"; then
+ echo "contextCSN on site2 provider:"
+ echo "$CSN3"
+ echo "contextCSN on site2 search:"
+ echo "$CSN6"
+ fi
+ fi
+fi
+
+ERRORS=`expr $ERRORS + $CSN_ERRORS`
+
+EOF
+
+test $BACKEND = null && echo : > $TESTDIR/checkcsn.sh
+
+chmod +x $TESTDIR/checkcsn.sh
+
+
+echo "Checking contextCSN after initial replication..."
+. $TESTDIR/checkcsn.sh
+
+# TEST:
+# Test that updates to the first backend on central provider, which should
+# be replicated to all servers actually is so, and that the contextCSN is
+# updated everywhere:
+echo "Using ldapmodify to modify first backend on central provider..."
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: ou=ou1,dc=example,dc=com
+changetype: modify
+add: description
+description: Modify$MNUM
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+sleep 1
+
+echo "Using ldapsearch to check replication to central search..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI4 \
+ -s base -b "ou=ou1,$BASEDN" \
+ "(description=Modify$MNUM)" 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check replication to site1 search..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI5 \
+ -s base -b "ou=ou1,$BASEDN" \
+ "(description=Modify$MNUM)" 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check replication to site2 search..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI6 \
+ -s base -b "ou=ou1,$BASEDN" \
+ "(description=Modify$MNUM)" 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Checking contextCSN after modify of first backend on central provider..."
+. $TESTDIR/checkcsn.sh
+
+# TEST:
+# Test that updates to the second backend on central provider is only
+# replicated to those search servers that should receive that backend.
+# The contextCSN should still be updated everywhere:
+MNUM=`expr $MNUM + 1`
+echo "Using ldapmodify to modify second backend on central provider..."
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: ou=ou2,dc=example,dc=com
+changetype: modify
+add: description
+description: Modify$MNUM
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+sleep 1
+
+echo "Using ldapsearch to check replication to site2 search..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI6 \
+ -s base -b "ou=ou2,$BASEDN" \
+ "(description=Modify$MNUM)" 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check no replication to site1 provider..."
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI2 \
+ -s base -b "ou=ou2,$BASEDN" \
+ "(description=Modify$NMUM)" 2>&1 | awk '/^dn:/ {print "NOK"}'`
+ if test "x$RESULT" = "xNOK" ; then
+ echo "Change was replicated to site1 search!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ sleep 1
+done
+
+echo "Using ldapsearch to check no replication to central search..."
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI4 \
+ -s base -b "ou=ou2,$BASEDN" \
+ "(description=Modify$MNUM)" 2>&1 | awk '/^dn:/ {print "NOK"}'`
+ if test "x$RESULT" = "xNOK" ; then
+ echo "Change was replicated to central search!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ sleep 1
+done
+
+echo "Checking contextCSN after modify of second backend on central provider..."
+. $TESTDIR/checkcsn.sh
+
+# TEST:
+# Test that updates to the first backend on site1 provider, which should be
+# replicated everywhere except to central and site2 search. The contextCSN
+# should be updated on all servers:
+MNUM=`expr $MNUM + 1`
+echo "Using ldapmodify to modify first backend on site1 provider..."
+$LDAPMODIFY -D "ou=sm1ou1,$BASEDN" -H $URI2 -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: ou=sm1ou1,dc=example,dc=com
+changetype: modify
+add: description
+description: Modify$MNUM
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+sleep 1
+
+echo "Using ldapsearch to check replication to site1 search..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI5 \
+ -s base -b "ou=sm1ou1,$BASEDN" \
+ "(description=Modify$MNUM)" 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check replication to site2 provider..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI3 \
+ -s base -b "ou=sm1ou1,$BASEDN" \
+ "(description=Modify$MNUM)" 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check no replication to site2 search..."
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI6 \
+ -s base -b "ou=sm1ou2,$BASEDN" \
+ "(description=Modify$MNUM)" 2>&1 | awk '/^dn:/ {print "NOK"}'`
+ if test "x$RESULT" = "xNOK" ; then
+ echo "Change was replicated to central search!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ sleep 1
+done
+
+echo "Using ldapsearch to check no replication to central search..."
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI4 \
+ -s base -b "ou=sm1ou2,$BASEDN" \
+ "(description=Modify$MNUM)" 2>&1 | awk '/^dn:/ {print "NOK"}'`
+ if test "x$RESULT" = "xNOK" ; then
+ echo "Change was replicated to central search!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ sleep 1
+done
+
+echo "Checking contextCSN after modify of first backend on site1 provider..."
+. $TESTDIR/checkcsn.sh
+
+
+# TEST:
+# Test updates to the second backend on site1 provider, which should only be
+# replicated to site1 search. The contextCSN should be updated everywhere.
+MNUM=`expr $MNUM + 1`
+echo "Using ldapmodify to modify second backend on site1 provider..."
+$LDAPMODIFY -D "ou=sm1ou1,$BASEDN" -H $URI2 -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: ou=sm1ou2,dc=example,dc=com
+changetype: modify
+add: description
+description: Modify$MNUM
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+sleep 1
+
+
+echo "Using ldapsearch to check replication to site1 search..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI5 \
+ -s base -b "ou=sm1ou2,$BASEDN" \
+ "(description=Modify$MNUM)" 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check no replication to central provider..."
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI1 \
+ -s base -b "ou=sm1ou2,$BASEDN" \
+ "(description=Modify$NMUM)" 2>&1 | awk '/^dn:/ {print "NOK"}'`
+ if test "x$RESULT" = "xNOK" ; then
+ echo "Change was replicated to site2 search!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ sleep 1
+done
+
+echo "Checking contextCSN after modify of second backend on site1 provider..."
+. $TESTDIR/checkcsn.sh
+
+
+# TEST:
+# Test updates to first backend on site2 provider, which should be
+# replicated to the central servers, but not site1. The contextCSN
+# should be updated everywhere:
+MNUM=`expr $MNUM + 1`
+echo "Using ldapmodify to modify first backend on site2 provider..."
+$LDAPMODIFY -D "ou=sm2ou1,$BASEDN" -H $URI3 -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: ou=sm2ou1,dc=example,dc=com
+changetype: modify
+add: description
+description: Modify$MNUM
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+sleep 1
+
+echo "Using ldapsearch to check replication to central provider..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI1 \
+ -s base -b "ou=sm2ou1,$BASEDN" \
+ "(description=Modify$MNUM)" 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check replication to site2 search..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI6 \
+ -s base -b "ou=sm2ou1,$BASEDN" \
+ "(description=Modify$MNUM)" 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check no replication to site1 provider..."
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI2 \
+ -s base -b "ou=sm2ou1,$BASEDN" \
+ "(description=Modify$MNUM)" 2>&1 | awk '/^dn:/ {print "NOK"}'`
+ if test "x$RESULT" = "xNOK" ; then
+ echo "Change was replicated to site2 search!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ sleep 1
+done
+
+echo "Using ldapsearch to check no replication to central search..."
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI4 \
+ -s base -b "ou=sm2ou1,$BASEDN" \
+ "(description=Modify$MNUM)" 2>&1 | awk '/^dn:/ {print "NOK"}'`
+ if test "x$RESULT" = "xNOK" ; then
+ echo "Change was replicated to site2 search!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ sleep 1
+done
+
+echo "Checking contextCSN after modify of first backend on site2 provider..."
+. $TESTDIR/checkcsn.sh
+
+
+# TEST:
+# Test updates to the second backend on site2 provider, which should only be
+# replicated to site2 search. As always, contextCSN should be updated
+# everywhere:
+MNUM=`expr $MNUM + 1`
+echo "Using ldapmodify to modify second backend on site2 provider..."
+$LDAPMODIFY -D "ou=sm2ou1,$BASEDN" -H $URI3 -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: ou=sm2ou2,dc=example,dc=com
+changetype: modify
+add: description
+description: Modify$MNUM
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+sleep 1
+
+echo "Using ldapsearch to check replication to site2 search..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI6 \
+ -s base -b "ou=sm2ou2,$BASEDN" \
+ "(description=Modify$MNUM)" 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check no replication to central provider..."
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI4 \
+ -s base -b "ou=sm2ou2,$BASEDN" \
+ "(description=Modify$MNUM)" 2>&1 | awk '/^dn:/ {print "NOK"}'`
+ if test "x$RESULT" = "xNOK" ; then
+ echo "Change was replicated to central search!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ sleep 1
+done
+
+echo "Checking contextCSN after modify of second backend on site2 provider..."
+. $TESTDIR/checkcsn.sh
+
+# TEST:
+# Test that all contextCSN values are updated on the replicas when they
+# starts with an empty database. Start site2 provider first, then site2
+# search and finally central provider so that the site2 search's syncrepl
+# connection has been set up when site2 provider receives the database:
+echo "Stopping central provider and site2 servers to test start with empty db..."
+kill -HUP $SMC_PID $SM2_PID $SS2_PID
+for pid in $SMC_PID $SM2_PID $SS2_PID; do wait $pid ; done
+KILLPIDS=`echo "$KILLPIDS " | sed -e "s/ $SMC_PID / /"`;
+KILLPIDS=`echo "$KILLPIDS " | sed -e "s/ $SM2_PID / /"`;
+KILLPIDS=`echo "$KILLPIDS " | sed -e "s/ $SS2_PID / /"`;
+SMC_PID=
+SM2_PID=
+SS2_PID=
+rm -rf $SM2_DIR/db/*
+rm -rf $SS2_DIR/db/*
+
+echo "Starting site2 provider slapd on TCP/IP port $PORT3..."
+cd $SM2_DIR
+$SLAPD -F slapd.d -h $URI3 -d $LVL >> $LOG3 2>&1 &
+SM2_PID=$!
+if test $WAIT != 0 ; then
+ echo PID $SM2_PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $SM2_PID"
+cd $TESTWD
+sleep 1
+echo "Using ldapsearch to check that site2 provider slapd is running..."
+for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI3 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting site2 search slapd on TCP/IP port $PORT6..."
+cd $SS2_DIR
+$SLAPD -F slapd.d -h $URI6 -d $LVL >> $LOG6 2>&1 &
+SS2_PID=$!
+if test $WAIT != 0 ; then
+ echo PID $SS2_PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $SS2_PID"
+cd $TESTWD
+sleep 1
+echo "Using ldapsearch to check that site2 search slapd is running..."
+for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI6 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting central provider slapd on TCP/IP port $PORT1..."
+cd $SMC_DIR
+$SLAPD -F slapd.d -h $URI1 -d $LVL >> $LOG1 2>&1 &
+SMC_PID=$!
+if test $WAIT != 0 ; then
+ echo PID $SMC_PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $SMC_PID"
+cd $TESTWD
+sleep 1
+echo "Using ldapsearch to check that central provider slapd is running..."
+for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check that site2 provider received base..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI3 \
+ -s base -b "$BASEDN" \
+ '(objectClass=*)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check that site2 search received base..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI6 \
+ -s base -b "$BASEDN" \
+ '(objectClass=*)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep $SLEEP1
+
+echo "Checking contextCSN after site2 servers repopulated..."
+. $TESTDIR/checkcsn.sh
+
+if test $ERRORS -ne 0; then
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ echo "Found $ERRORS errors"
+ exit $ERRORS
+fi
+
+# TEST:
+# Adding syncrepl of the second site1 provider backend on central provider
+# will not initialize the database unless the contextCSN attribute is
+# stored in the suffix of the database and not the suffix of the glue
+# database:
+echo "Adding syncrepl of second site1 provider backend on central provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcDatabase={4}$BACKEND,cn=config
+changetype: modify
+add: olcSyncRepl
+olcSyncRepl: rid=4 provider=$URI2 searchbase="ou=sm1ou2,$BASEDN"
+ binddn="ou=sm1ou1,$BASEDN" bindmethod=simple credentials=$PASSWD
+ type=refreshAndPersist retry="$RETRY" timeout=1
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed to add syncrepl on central provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+sleep 1
+
+echo "Using ldapsearch to check that central provider received second site1 backend..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI1 \
+ -s base -b "ou=sm1ou2,$BASEDN" \
+ '(objectClass=*)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ERROR: Second site1 backend not replicated to central provider"
+ ERRORS=`expr $ERRORS + 1`
+
+ echo "Restarting central provider slapd on TCP/IP port $PORT1..."
+ kill -HUP $SMC_PID
+ wait $SMC_PID
+ KILLPIDS=`echo "$KILLPIDS " | sed -e "s/ $SMC_PID / /"`;
+
+ cd $SMC_DIR
+ $SLAPD -F slapd.d -h $URI1 -c rid=4,csn=0 -d $LVL >> $LOG1 2>&1 &
+ SMC_PID=$!
+ if test $WAIT != 0 ; then
+ echo PID $SMC_PID
+ read foo
+ fi
+ KILLPIDS="$KILLPIDS $SMC_PID"
+ cd $TESTWD
+ echo "Using ldapsearch to check that central provider slapd is running..."
+ for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+ done
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Using ldapsearch to check that central provider received second site1 backend..."
+ RC=32
+ for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI1 \
+ -s base -b "ou=sm1ou2,$BASEDN" \
+ '(objectClass=*)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+ done
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+echo "Using ldapsearch to check that central search received second site1 backend..."
+RC=32
+for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI4 \
+ -s base -b "ou=sm1ou2,$BASEDN" \
+ '(objectClass=*)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ERROR: Second site1 backend not replicated to central search"
+ ERRORS=`expr $ERRORS + 1`
+
+ echo "Restarting central search slapd on TCP/IP port $PORT4..."
+ kill -HUP $SSC_PID
+ wait $SSC_PID
+ KILLPIDS=`echo "$KILLPIDS " | sed -e "s/ $SSC_PID / /"`;
+
+ cd $SSC_DIR
+ $SLAPD -F slapd.d -h $URI4 -c rid=1,csn=0 -d $LVL >> $LOG4 2>&1 &
+ SSC_PID=$!
+ if test $WAIT != 0 ; then
+ echo PID $SSC_PID
+ read foo
+ fi
+ KILLPIDS="$KILLPIDS $SSC_PID"
+ cd $TESTWD
+ echo "Using ldapsearch to check that central search slapd is running..."
+ for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI4 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+ done
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Using ldapsearch to check that central search received second site1 backend..."
+ RC=32
+ for i in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI4 \
+ -s base -b "ou=sm1ou2,$BASEDN" \
+ '(objectClass=*)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $i seconds for syncrepl to receive changes..."
+ sleep $i
+ done
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+
+# TEST:
+# Run race tests when more than one backend is replicated from the same
+# provider. This will usually fail long before 100 iterations unless
+# syncrepl stores the contextCSN in the suffix of its own database, and
+# that syncprov follows these rules before updating its own CSN when it
+# detects updates from syncrepl:
+# 1) A contextCSN value must have been stored in the suffix of all the
+# syncrepl configured databases within the glued syncprov database.
+# 2) Of all contextCSN values stored by syncrepl with the same SID,
+# syncprov must always select the one with the lowest csn value.
+test -z "$RACE_TESTS" && RACE_TESTS=10
+RACE_NUM=0
+RACE_ERROR=0
+
+SUB_DN=ou=sub,ou=sm1ou2,dc=example,dc=com
+
+while test $RACE_ERROR -eq 0 -a $RACE_NUM -lt $RACE_TESTS ; do
+ RACE_NUM=`expr $RACE_NUM + 1`
+ echo "Running $RACE_NUM of $RACE_TESTS syncrepl race tests..."
+
+ echo "Stopping central provider..."
+ kill -HUP $SMC_PID
+ wait $SMC_PID
+ KILLPIDS=`echo "$KILLPIDS " | sed -e "s/ $SMC_PID / /"`;
+
+ MNUM=`expr $MNUM + 1`
+ echo "Using ldapadd to add entry on site1 provider..."
+ $LDAPADD -D "ou=sm1ou1,$BASEDN" -H $URI2 -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: $SUB_DN
+objectClass: top
+objectClass: organizationalUnit
+ou: sub
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Starting central provider again..."
+ cd $SMC_DIR
+ $SLAPD -F slapd.d -h $URI1 -d $LVL >> $LOG1 2>&1 &
+ SMC_PID=$!
+ KILLPIDS="$KILLPIDS $SMC_PID"
+ cd $TESTWD
+ echo "Using ldapsearch to check that central provider received entry..."
+ for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$SUB_DN" -H $URI1 > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ sleep $i
+ done
+ if test $RC != 0 ; then
+ echo "ERROR: entry not replicated to central provider!"
+ RACE_ERROR=1
+ break
+ fi
+
+ echo "Using ldapsearch to check that central search received entry..."
+ for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$SUB_DN" -H $URI4 > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ sleep $i
+ done
+ if test $RC != 0 ; then
+ echo "ERROR: entry not replicated to central provider!"
+ RACE_ERROR=1
+ break
+ fi
+
+ echo "Stopping central provider..."
+ kill -HUP $SMC_PID
+ wait $SMC_PID
+ KILLPIDS=`echo "$KILLPIDS " | sed -e "s/ $SMC_PID / /"`;
+
+ echo "Using ldapdelete to delete entry on site1 provider..."
+ $LDAPDELETE -D "ou=sm1ou1,$BASEDN" -H $URI2 -w $PASSWD "$SUB_DN"
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapdelete failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Starting central provider again..."
+ cd $SMC_DIR
+ $SLAPD -F slapd.d -h $URI1 -d $LVL >> $LOG1 2>&1 &
+ SMC_PID=$!
+ KILLPIDS="$KILLPIDS $SMC_PID"
+ cd $TESTWD
+
+ echo "Using ldapsearch to check that entry was deleted on central provider..."
+ RC=0
+ for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$SUB_DN" -H $URI1 > /dev/null 2>&1
+ RC=$?
+ if test $RC = $wantNoObj; then break; fi
+ sleep $i
+ done
+
+ if test $RC != $wantNoObj; then
+ if test $RC != 0; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ echo "ERROR: Entry not removed on central provider!"
+ RACE_ERROR=1
+ break
+ fi
+
+ echo "Using ldapsearch to check that entry was deleted on central search..."
+ RC=0
+ for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$SUB_DN" -H $URI4 > /dev/null 2>&1
+ RC=$?
+ if test $RC != 0; then break; fi
+ sleep $i
+ done
+
+ if test $RC != $wantNoObj; then
+ echo "ERROR: Entry not removed on central search! (RC=$RC)"
+ RACE_ERROR=1
+ break
+ fi
+done
+
+if test $RACE_ERROR != 0; then
+ echo "Race error found after $RACE_NUM of $RACE_TESTS iterations"
+ ERRORS=`expr $ERRORS + $RACE_ERROR`
+else
+ echo "No race errors found after $RACE_TESTS iterations"
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+if test $ERRORS -ne 0; then
+ echo "Found $ERRORS errors"
+ echo ">>>>>> Exiting with a false success status for now"
+ exit 0
+fi
+
+echo ">>>>> Test succeeded"
+
+exit 0
diff --git a/tests/scripts/test059-consumer-config b/tests/scripts/test059-consumer-config
new file mode 100755
index 0000000..56848db
--- /dev/null
+++ b/tests/scripts/test059-consumer-config
@@ -0,0 +1,438 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+CFPRO=$TESTDIR/cfpro.d
+CFCON=$TESTDIR/cfcon.d
+
+mkdir -p $TESTDIR $DBDIR1A $DBDIR1B $DBDIR2A $CFPRO $CFCON
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+if test x"$SYNCMODE" = x ; then
+ SYNCMODE=rp
+fi
+case "$SYNCMODE" in
+ ro)
+ SYNCTYPE="type=refreshOnly interval=00:00:00:03"
+ ;;
+ rp)
+ SYNCTYPE="type=refreshAndPersist"
+ ;;
+ *)
+ echo "unknown sync mode $SYNCMODE"
+ exit 1;
+ ;;
+esac
+
+#
+# Test replication of dynamic config with alternate consumer config:
+# - start provider
+# - start consumer
+# - configure over ldap
+# - populate over ldap
+# - configure syncrepl over ldap
+# - retrieve database over ldap and compare against expected results
+#
+
+echo "Starting provider slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $DYNAMICCONF > $CONFLDIF
+$SLAPADD -F $CFPRO -n 0 -l $CONFLDIF
+$SLAPD -F $CFPRO -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Inserting syncprov overlay on provider..."
+if [ "$SYNCPROV" = syncprovmod ]; then
+ $LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: ../servers/slapd/overlays
+olcModuleLoad: syncprov.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for moduleLoad ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+read CONFIGPW < $CONFIGPWF
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcOverlay=syncprov,olcDatabase={0}config,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for syncprov config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Consumers will not replicate the provider's actual cn=config.
+# Instead, they will use an alternate DB so that they may be
+# configured differently from the provider. This alternate DB
+# will also be a consumer for the real cn=schema,cn=config tree.
+# It has multi-provider enabled so that it can be written directly
+# while being a consumer of the main schema.
+echo "Configuring consumer config DB on provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: cn=config
+changetype: modify
+add: olcServerID
+olcServerID: 1
+
+dn: olcDatabase={1}ldif,cn=config
+changetype: add
+objectClass: olcDatabaseConfig
+objectClass: olcLdifConfig
+olcDatabase: {1}ldif
+olcDbDirectory: $DBDIR1A
+olcSuffix: cn=config,cn=consumer
+olcRootDN: cn=config,cn=consumer
+olcRootPW: repsecret
+olcAccess: to * by dn.base="cn=config" write
+
+dn: olcOverlay=syncprov,olcDatabase={1}ldif,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+
+dn: cn=config,cn=consumer
+changetype: add
+objectClass: olcGlobal
+cn: consumerconfig
+
+dn: olcDatabase={0}config,cn=config,cn=consumer
+changetype: add
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW: topsecret
+olcSyncrepl: {0}rid=001 provider=$URI1 binddn="cn=config,cn=consumer"
+ bindmethod=simple credentials=repsecret searchbase="cn=config,cn=consumer"
+ $SYNCTYPE retry="3 5 300 5" timeout=3 suffixmassage="cn=config"
+olcUpdateRef: $URI1
+
+dn: olcDatabase={1}ldif,cn=config
+changetype: modify
+add: olcSyncrepl
+olcSyncrepl: {0}rid=001 provider=$URI1 binddn="cn=config"
+ bindmethod=simple credentials=$CONFIGPW searchbase="cn=schema,cn=config"
+ $SYNCTYPE retry="3 5 300 5" timeout=3
+ suffixmassage="cn=schema,cn=config,cn=consumer"
+-
+add: olcMultiProvider
+olcMultiProvider: TRUE
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for consumer DB config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting consumer slapd on TCP/IP port $PORT2..."
+$SLAPADD -F $CFCON -n 0 -l $CONFLDIF
+$SLAPD -F $CFCON -h $URI2 -d $LVL > $LOG2 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that consumer slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Configuring syncrepl on consumer..."
+$LDAPMODIFY -D cn=config -H $URI2 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcDatabase={0}config,cn=config
+changetype: modify
+add: olcSyncRepl
+olcSyncRepl: rid=001 provider=$URI1 binddn="cn=config,cn=consumer"
+ bindmethod=simple credentials=repsecret searchbase="cn=config,cn=consumer"
+ $SYNCTYPE retry="3 5 300 5" timeout=3
+ suffixmassage="cn=config"
+-
+add: olcUpdateRef
+olcUpdateRef: $URI1
+EOF
+
+sleep 1
+
+echo "Using ldapsearch to check that syncrepl received config changes..."
+RC=32
+for i in 0 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI2 -D cn=config -y $CONFIGPWF \
+ -s base -b "olcDatabase={0}config,cn=config" \
+ '(olcUpdateRef=*)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding schema and databases on provider..."
+$LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+include: file://$ABS_SCHEMADIR/core.ldif
+
+include: file://$ABS_SCHEMADIR/cosine.ldif
+
+include: file://$ABS_SCHEMADIR/inetorgperson.ldif
+
+include: file://$ABS_SCHEMADIR/openldap.ldif
+
+include: file://$ABS_SCHEMADIR/nis.ldif
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for schema config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check that syncrepl received the schema changes..."
+RC=32
+for i in 0 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI2 -D cn=config -y $CONFIGPWF \
+ -s sub -b "cn=schema,cn=config" \
+ '(cn=*openldap)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+done
+
+nullExclude="" nullOK=""
+test $BACKEND = null && nullExclude="# " nullOK="OK"
+
+if [ "$BACKENDTYPE" = mod ]; then
+ $LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: ../servers/slapd/back-$BACKEND
+olcModuleLoad: back_$BACKEND.la
+
+dn: cn=module,cn=config,cn=consumer
+objectClass: olcModuleList
+cn: module
+olcModulePath: ../servers/slapd/back-$BACKEND
+olcModuleLoad: back_$BACKEND.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for backend config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+$LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcDatabase={2}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {2}$BACKEND
+olcSuffix: $BASEDN
+${nullExclude}olcDbDirectory: $DBDIR1B
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+olcSyncRepl: rid=002 provider=$URI1 binddn="$MANAGERDN" bindmethod=simple
+ credentials=$PASSWD searchbase="$BASEDN" $SYNCTYPE
+ retry="3 5 300 5" timeout=3
+olcUpdateRef: $URI1
+
+dn: olcOverlay=syncprov,olcDatabase={2}${BACKEND},cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+
+dn: olcDatabase={1}$BACKEND,cn=config,cn=consumer
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+olcSuffix: $BASEDN
+${nullExclude}olcDbDirectory: $DBDIR2A
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+olcSyncRepl: rid=002 provider=$URI1 binddn="$MANAGERDN" bindmethod=simple
+ credentials=$PASSWD searchbase="$BASEDN" $SYNCTYPE
+ retry="3 5 300 5" timeout=3
+olcUpdateRef: $URI1
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if test $INDEXDB = indexdb ; then
+ $LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcDatabase={2}$BACKEND,cn=config
+changetype: modify
+add: olcDbIndex
+olcDbIndex: objectClass,entryUUID,entryCSN eq
+olcDbIndex: cn,uid pres,eq,sub
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd modify for database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+echo "Using ldapadd to populate provider..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD -f $LDIFORDERED \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Using ldapsearch to check that syncrepl received database changes..."
+RC=32
+for i in 0 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI2 \
+ -s base -b "cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com" \
+ '(objectClass=*)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ 'objectclass=*' > $PROVIDEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI2 -w $PASSWD \
+ 'objectclass=*' > $CONSUMEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo "Filtering provider results..."
+$LDIFFILTER < $PROVIDEROUT > $PROVIDERFLT
+echo "Filtering consumer results..."
+$LDIFFILTER < $CONSUMEROUT > $CONSUMERFLT
+
+echo "Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test060-mt-hot b/tests/scripts/test060-mt-hot
new file mode 100755
index 0000000..a571622
--- /dev/null
+++ b/tests/scripts/test060-mt-hot
@@ -0,0 +1,299 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+# The default debug level logs more than 1Gb:
+case "$SLAPD_DEBUG_MT_HOT/$SLAPD_DEBUG" in
+/0 | /0x0 | /0X0 | /none | /NONE | /32768 | /0x8000 | 0X8000 | /0100000) :;;
+*) SLAPD_DEBUG=${SLAPD_DEBUG_MT_HOT-stats} ;;
+esac
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test x$TESTLOOPS = x ; then
+ TESTLOOPS=50
+fi
+
+mkdir -p $TESTDIR $DBDIR1
+
+#
+# Populate and start up slapd server with some random data
+#
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $MCONF > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Running slapindex to index slapd database..."
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+$SLAPINDEX -f $CONF1
+RC=$?
+if test $RC != 0 ; then
+ echo "warning: slapindex failed ($RC)"
+ echo " assuming no indexing support"
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+echo $SLAPD -f $CONF1 -h $URI1 -d $LVL
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+# Perform a basic search, make sure of a functional setup
+echo "Testing basic monitor search..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITORDN" -H $URI1 \
+ '(objectclass=*)' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "mt-hot read failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $MTREADOUT
+
+echo "Monitor searches"
+# Perform a basic single threaded search on a single connection
+THR=1
+OUTER=1
+INNER=`expr $TESTLOOPS \* 1000`
+echo "Testing basic mt-hot search: $THR threads ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$MONITORDN" \
+ -m $THR -L $OUTER -l $INNER
+$SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$MONITORDN" -f "(objectclass=*)" \
+ -m $THR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapd-mtread failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Perform a basic multi-threaded search on a single connection
+THR=5
+OUTER=1
+INNER=`expr $TESTLOOPS \* 200`
+echo "Testing basic mt-hot search: $THR threads ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$MONITORDN" \
+ -m $THR -L $OUTER -l $INNER
+$SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$MONITORDN" -f "(objectclass=*)" \
+ -m $THR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapd-mtread failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Perform a basic multi-threaded search on a single connection
+THR=100
+OUTER=5
+INNER=`expr $TESTLOOPS \* 2`
+echo "Testing basic mt-hot search: $THR threads ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$MONITORDN" \
+ -m $THR -L $OUTER -l $INNER
+$SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$MONITORDN" -f "(objectclass=*)" \
+ -m $THR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapd-mtread failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Perform a single threaded random DB search on a single connection
+echo "Random searches"
+THR=1
+OUTER=1
+INNER=`expr $TESTLOOPS \* 1000`
+echo "Testing random mt-hot search: $THR threads ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$BASEDN" -f "(objectclass=*)" \
+ -m $THR -L $OUTER -l $INNER
+$SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$BASEDN" -f "(objectclass=*)" \
+ -m $THR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapd-mtread failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Perform a multi-threaded random DB search on a single connection
+THR=5
+OUTER=1
+INNER=`expr $TESTLOOPS \* 200`
+echo "Testing random mt-hot search: $THR threads ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$BASEDN" -f "(objectclass=*)" \
+ -m $THR -L $OUTER -l $INNER
+$SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$BASEDN" -f "(objectclass=*)" \
+ -m $THR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapd-mtread failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Perform a multi-threaded random DB search on a single connection
+THR=100
+OUTER=5
+INNER=`expr $TESTLOOPS \* 2`
+echo "Testing random mt-hot search: $THR threads ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$BASEDN" -f "(objectclass=*)" \
+ -m $THR -L $OUTER -l $INNER
+$SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$BASEDN" -f "(objectclass=*)" \
+ -m $THR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapd-mtread failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Perform a basic multi-threaded search using multiple connections
+echo "Multiple threads and connection searches"
+CONN=5
+THR=5
+OUTER=1
+INNER=`expr $TESTLOOPS \* 200`
+echo "Testing basic mt-hot search: $THR threads $CONN conns ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$MONITORDN" \
+ -c $CONN -m $THR -L $OUTER -l $INNER
+$SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$MONITORDN" -f "(objectclass=*)" \
+ -c $CONN -m $THR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapd-mtread failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Perform a basic multi-threaded search using multiple connections
+CONN=5
+THR=50
+OUTER=5
+INNER=`expr $TESTLOOPS \* 20`
+echo "Testing basic mt-hot search: $THR threads $CONN conns ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$MONITORDN" \
+ -c $CONN -m $THR -L $OUTER -l $INNER
+$SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$MONITORDN" -f "(objectclass=*)" \
+ -c $CONN -m $THR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapd-mtread failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Perform a multi-threaded random DB search using multiple connections
+CONN=5
+THR=100
+OUTER=5
+INNER=`expr $TESTLOOPS \* 2`
+echo "Testing random mt-hot search: $THR threads $CONN conns ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$BASEDN" -f "(objectclass=*)" \
+ -c $CONN -m $THR -L $OUTER -l $INNER
+$SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$BASEDN" -f "(objectclass=*)" \
+ -c $CONN -m $THR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapd-mtread failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Perform a multi-threaded random reads and writes using single connection
+CONN=1
+THR=10
+WTHR=10
+OUTER=5
+INNER=`expr $TESTLOOPS \* 2`
+echo "Testing random mt-hot r/w search: $THR read threads $WTHR write threads $CONN conns ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$BASEDN" -f "(&(!(cn=rwtest*))(objectclass=*))" \
+ -c $CONN -m $THR -M $WTHR -L $OUTER -l $INNER
+$SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$BASEDN" -f "(&(!(cn=rwtest*))(objectclass=*))" \
+ -c $CONN -m $THR -M $WTHR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapd-mtread failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Perform a multi-threaded random reads and writes using multiple connections
+CONN=5
+THR=10
+WTHR=10
+OUTER=5
+INNER=`expr $TESTLOOPS \* 2`
+echo "Testing random mt-hot r/w search: $THR read threads $WTHR write threads $CONN conns ($OUTER x $INNER) loops..."
+echo $SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$BASEDN" -f "(&(!(cn=rwtest*))(objectclass=*))" \
+ -c $CONN -m $THR -M $WTHR -L $OUTER -l $INNER
+$SLAPDMTREAD -H $URI1 -D "$MANAGERDN" -w $PASSWD \
+ -e "$BASEDN" -f "(&(!(cn=rwtest*))(objectclass=*))" \
+ -c $CONN -m $THR -M $WTHR -L $OUTER -l $INNER >> $MTREADOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slapd-mtread failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+exit 0
diff --git a/tests/scripts/test061-syncreplication-initiation b/tests/scripts/test061-syncreplication-initiation
new file mode 100755
index 0000000..a8ce51c
--- /dev/null
+++ b/tests/scripts/test061-syncreplication-initiation
@@ -0,0 +1,668 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+# This script tests race conditions related to setting up the syncrepl
+# refresh phase, especially when the provider is itself a consumer
+# refreshing from its provider again.
+
+# The configuration used is a provider->forwarder->consumer chain, where
+# the forwarder is restarted between add/delete of entries on the provider.
+
+echo "Running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+test "x$INITIATION_RACE_TESTS" = "x" && INITIATION_RACE_TESTS=1
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+RETRY="1 +"
+
+PROV_DIR=$TESTDIR/prov
+CONS_DIR=$TESTDIR/cons
+FWD1_DIR=$TESTDIR/fwd1
+FWD2_DIR=$TESTDIR/fwd2
+
+PROV_URI=$URI1
+CONS_URI=$URI2
+FWD1_URI=$URI3
+
+PROV_LOG=$LOG1
+CONS_LOG=$LOG2
+FWD1_LOG=$LOG3
+
+DIRS="$PROV_DIR $CONS_DIR $FWD1_DIR"
+URIS="$PROV_URI $CONS_URI $FWD1_URI"
+
+noObj=32
+nullExclude="" nullOK=""
+test $BACKEND = null && nullExclude="# " nullOK="OK" noObj=0
+
+mkdir -p $TESTDIR
+
+for dir in $DIRS; do
+ mkdir -p $dir $dir/slapd.d $dir/db
+done
+
+KILLPIDS=
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+case "$BACKEND" in
+ *) olcDbCheckpoint="# olcDbCheckpoint";;
+esac
+
+echo "Initializing server configurations"
+for dir in $DIRS; do
+ $SLAPADD -F $dir/slapd.d -n 0 <<EOF
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcServerID: 1 $PROV_URI
+olcServerID: 2 $CONS_URI
+olcServerID: 3 $FWD1_URI
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW:< file://$CONFIGPWF
+
+EOF
+done
+
+echo "Starting provider slapd on $PROV_URI"
+cd $PROV_DIR
+$SLAPD -F slapd.d -h $PROV_URI -d $LVL >> $PROV_LOG 2>&1 &
+PROV_PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PROV_PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PROV_PID"
+cd $TESTWD
+sleep 1
+for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $PROV_URI \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting forward1 slapd on $FWD1_URI"
+cd $FWD1_DIR
+$SLAPD -F slapd.d -h $FWD1_URI -d $LVL >> $FWD1_LOG 2>&1 &
+FWD1_PID=$!
+if test $WAIT != 0 ; then
+ echo PID $FWD1_PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $FWD1_PID"
+cd $TESTWD
+sleep 1
+for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $FWD1_URI \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting consumer slapd on $CONS_URI"
+cd $CONS_DIR
+$SLAPD -F slapd.d -h $CONS_URI -d $LVL >> $CONS_LOG 2>&1 &
+CONS_PID=$!
+if test $WAIT != 0 ; then
+ echo PID $CONS_PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONS_PID"
+cd $TESTWD
+sleep 1
+for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $CONS_URI \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to start..."
+ sleep $i
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+for uri in $URIS; do
+ echo "Adding schema on $uri"
+ $LDAPADD -D cn=config -H $uri -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+include: file://$ABS_SCHEMADIR/core.ldif
+
+include: file://$ABS_SCHEMADIR/cosine.ldif
+
+include: file://$ABS_SCHEMADIR/inetorgperson.ldif
+
+include: file://$ABS_SCHEMADIR/openldap.ldif
+
+include: file://$ABS_SCHEMADIR/nis.ldif
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ [ "$BACKENDTYPE" = mod ] || continue
+
+ echo "Adding backend module on $uri..."
+ $LDAPADD -D cn=config -H $uri -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/back-$BACKEND
+olcModuleLoad: back_$BACKEND.la
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for backend module ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+done
+
+syncprov_module=''
+[ "$AC_syncprov" = syncprovmod ] && syncprov_module="
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+olcModuleLoad: syncprov.la"
+
+for uri in $PROV_URI; do
+ echo "Adding database configuration on $uri"
+ $LDAPADD -D cn=config -H $uri -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+${nullExclude}olcDbDirectory: ./db
+$olcDbCheckpoint: 1024 5
+olcSuffix: $BASEDN
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+
+$syncprov_module
+
+dn: olcOverlay={0}syncprov,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: {0}syncprov
+olcSpCheckpoint: 1 1
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Populating provider on $uri"
+ $LDAPADD -D "$MANAGERDN" -H $PROV_URI -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: $BASEDN
+objectClass: top
+objectClass: organization
+objectClass: dcObject
+dc: example
+o: Example, Inc
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+done
+
+for uri in $FWD1_URI; do
+ echo "Adding database configuration on $uri"
+ $LDAPADD -D cn=config -H $uri -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+${nullExclude}olcDbDirectory: ./db
+$olcDbCheckpoint: 1024 5
+olcSuffix: $BASEDN
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+olcSyncRepl: rid=1 provider=$PROV_URI searchbase="$BASEDN"
+ binddn="$MANAGERDN" bindmethod=simple credentials=$PASSWD
+ type=refreshAndPersist retry="$RETRY" timeout=1
+
+$syncprov_module
+
+dn: olcOverlay={0}syncprov,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: {0}syncprov
+olcSpCheckpoint: 1 1
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+done
+
+for uri in $CONS_URI; do
+ echo "Adding database configuration on $uri"
+ $LDAPADD -D cn=config -H $uri -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+${nullExclude}olcDbDirectory: ./db
+$olcDbCheckpoint: 1024 5
+olcSuffix: $BASEDN
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+olcSyncRepl: rid=1 provider=$FWD1_URI searchbase="$BASEDN"
+ binddn="$MANAGERDN" bindmethod=simple credentials=$PASSWD
+ type=refreshAndPersist retry="$RETRY" timeout=1
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+done
+
+for uri in $FWD1_URI $CONS_URI; do
+ echo "Using ldapsearch to check that $uri received database..."
+ for i in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$BASEDN" -H $uri \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ test $RC = 0 && break
+ echo "Waiting $i seconds for slapd to receive database..."
+ sleep $i
+ done
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+done
+
+RACE_NUM=0
+ERROR=0
+
+nEntries=10
+
+addEnd=1
+delEnd=1
+
+addIdx=1
+delIdx=1
+
+while test $ERROR -eq 0 -a $RACE_NUM -lt $INITIATION_RACE_TESTS ; do
+ RACE_NUM=`expr $RACE_NUM + 1`
+ echo "Running $RACE_NUM of $INITIATION_RACE_TESTS syncrepl initiation race tests..."
+
+ echo "Stopping forwarders for add test"
+ for pid in $FWD1_PID; do
+ kill -HUP $pid
+ wait $pid
+ KILLPIDS=`echo "$KILLPIDS " | sed -e "s/ $pid / /"`;
+ done
+
+ addStart=$addEnd
+ addEnd=`expr $addEnd + $nEntries`
+
+ echo "Using ldapadd to add $nEntries entries on provider"
+ while test $addIdx -lt $addEnd; do
+ $LDAPADD -D "$MANAGERDN" -H $PROV_URI -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: ou=$addIdx,$BASEDN
+objectClass: top
+objectClass: organizationalUnit
+ou: $addIdx
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for entry $addIdx ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ addIdx=`expr $addIdx + 1`
+ done
+
+ echo "Starting forwarders again"
+ cd $FWD1_DIR
+ $SLAPD -F slapd.d -h $FWD1_URI -d $LVL >> $FWD1_LOG 2>&1 &
+ FWD1_PID=$!
+ KILLPIDS="$KILLPIDS $FWD1_PID"
+ cd $TESTWD
+
+ addEnd=`expr $addEnd + $nEntries`
+
+ echo "Using ldapadd to add $nEntries more entries on provider"
+ while test $addIdx -lt $addEnd; do
+ $LDAPADD -D "$MANAGERDN" -H $PROV_URI -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: ou=$addIdx,$BASEDN
+objectClass: top
+objectClass: organizationalUnit
+ou: $addIdx
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for entry $addIdx ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ addIdx=`expr $addIdx + 1`
+ done
+
+ for uri in $FWD1_URI $CONS_URI; do
+ echo "Checking replication to $uri"
+ RC=32
+ i=$addStart
+ while test $i -lt $addEnd; do
+ for j in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $uri -s base -b "ou=$i,$BASEDN" 2>&1 \
+ | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $j seconds for $uri to receive entry $i..."
+ sleep $j
+ done
+ if test $RC != 0 ; then
+ echo "ERROR: Entry $i not replicated to $uri! ($RC)!"
+ ERROR=1
+ break
+ fi
+ i=`expr $i + 1`
+ done
+ if test $ERROR != 0; then break; fi
+ done
+ if test $ERROR != 0; then break; fi
+
+ echo "Stopping forwarders for add/delete test"
+ for pid in $FWD1_PID; do
+ kill -HUP $pid
+ wait $pid
+ KILLPIDS=`echo "$KILLPIDS " | sed -e "s/ $pid / /"`;
+ done
+
+ addStart=$addEnd
+ addEnd=`expr $addEnd + $nEntries`
+
+ echo "Using ldapadd to add $nEntries entries on provider"
+ while test $addIdx -lt $addEnd; do
+ $LDAPADD -D "$MANAGERDN" -H $PROV_URI -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: ou=$addIdx,$BASEDN
+objectClass: top
+objectClass: organizationalUnit
+ou: $addIdx
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for entry $addIdx ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ addIdx=`expr $addIdx + 1`
+ done
+
+ delStart=$delEnd
+ delEnd=`expr $delEnd + $nEntries`
+
+ echo "Using ldapdelete to delete $nEntries entries on provider"
+ while test $delIdx -lt $delEnd; do
+ $LDAPDELETE -D "$MANAGERDN" -H $PROV_URI -w $PASSWD "ou=$delIdx,$BASEDN"
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapdelete failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ delIdx=`expr $delIdx + 1`
+ done
+
+ echo "Starting forwarders again"
+ cd $FWD1_DIR
+ $SLAPD -F slapd.d -h $FWD1_URI -d $LVL >> $FWD1_LOG 2>&1 &
+ FWD1_PID=$!
+ KILLPIDS="$KILLPIDS $FWD1_PID"
+ cd $TESTWD
+
+ addEnd=`expr $addEnd + $nEntries`
+ delEnd=`expr $delEnd + $nEntries`
+
+ echo "Using ldapadd to add $nEntries more entries on provider"
+ while test $addIdx -lt $addEnd; do
+ $LDAPADD -D "$MANAGERDN" -H $PROV_URI -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: ou=$addIdx,$BASEDN
+objectClass: top
+objectClass: organizationalUnit
+ou: $addIdx
+
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for entry $addIdx ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ addIdx=`expr $addIdx + 1`
+ done
+
+ echo "Using ldapdelete to delete $nEntries more entries on provider"
+ while test $delIdx -lt $delEnd; do
+ $LDAPDELETE -D "$MANAGERDN" -H $PROV_URI -w $PASSWD "ou=$delIdx,$BASEDN"
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapdelete failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ delIdx=`expr $delIdx + 1`
+ done
+
+ for uri in $FWD1_URI $CONS_URI; do
+ echo "Checking replication to $uri"
+ RC=32
+ i=$addStart
+ while test $i -lt $addEnd; do
+ for j in 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $uri -s base -b "ou=$i,$BASEDN" 2>&1 \
+ | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $j seconds for $uri to receive entry $i..."
+ sleep $j
+ done
+ if test $RC != 0 ; then
+ echo "ERROR: Entry $i not replicated to $uri! ($RC)!"
+ ERROR=1
+ break
+ fi
+ i=`expr $i + 1`
+ done
+ if test $ERROR != 0; then break; fi
+
+ i=$delStart
+ while test $i -lt $delEnd; do
+ for j in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "ou=$i,$BASEDN" -H $uri > /dev/null 2>&1
+ RC=$?
+ if test $RC = $noObj; then break; fi
+ echo "Waiting $j seconds for $uri to delete entry $i..."
+ sleep $j
+ done
+ if test $RC != $noObj; then
+ echo "ERROR: Entry $i not removed on $uri! (RC=$RC)"
+ ERROR=1
+ break
+ fi
+ i=`expr $i + 1`
+ done
+ if test $ERROR != 0; then break; fi
+ done
+ if test $ERROR != 0; then break; fi
+
+ echo "Stopping forwarders for delete test"
+ for pid in $FWD1_PID; do
+ kill -HUP $pid
+ wait $pid
+ KILLPIDS=`echo "$KILLPIDS " | sed -e "s/ $pid / /"`;
+ done
+
+ delStart=$delEnd
+ delEnd=`expr $delEnd + $nEntries`
+
+ echo "Using ldapdelete to delete entries on provider"
+ while test $delIdx -lt $delEnd; do
+ $LDAPDELETE -D "$MANAGERDN" -H $PROV_URI -w $PASSWD "ou=$delIdx,$BASEDN"
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapdelete failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ delIdx=`expr $delIdx + 1`
+ done
+
+ echo "Starting forwarders again"
+ cd $FWD1_DIR
+ $SLAPD -F slapd.d -h $FWD1_URI -d $LVL >> $FWD1_LOG 2>&1 &
+ FWD1_PID=$!
+ KILLPIDS="$KILLPIDS $FWD1_PID"
+ cd $TESTWD
+
+ delEnd=`expr $delEnd + $nEntries`
+
+ echo "Using ldapdelete to delete $nEntries more entries on provider"
+ while test $delIdx -lt $delEnd; do
+ $LDAPDELETE -D "$MANAGERDN" -H $PROV_URI -w $PASSWD "ou=$delIdx,$BASEDN"
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapdelete failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ delIdx=`expr $delIdx + 1`
+ done
+
+ for uri in $FWD1_URI $CONS_URI; do
+ echo "Checking replication to $uri"
+ RC=0
+ i=$delStart
+ while test $i -lt $delEnd; do
+ for j in 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "ou=$i,$BASEDN" -H $uri > /dev/null 2>&1
+ RC=$?
+ if test $RC = $noObj; then break; fi
+ echo "Waiting $j seconds for $uri to delete entry $i..."
+ sleep $j
+ done
+ if test $RC != $noObj; then
+ echo "ERROR: Entry $i not removed on $uri! (RC=$RC)"
+ ERROR=1
+ break
+ fi
+ i=`expr $i + 1`
+ done
+ if test $ERROR != 0; then break; fi
+ done
+ if test $ERROR != 0; then break; fi
+
+ sleep 1
+ echo "Checking contextCSN"
+ CSN_ERRORS=0
+ CSN1=`$LDAPSEARCH -H $URI1 -b $BASEDN -s base contextCSN | grep contextCSN`
+ CSN2=`$LDAPSEARCH -H $URI2 -b $BASEDN -s base contextCSN | grep contextCSN`
+ CSN3=`$LDAPSEARCH -H $URI3 -b $BASEDN -s base contextCSN | grep contextCSN`
+
+ if test -z "$CSN1" ; then
+ test $BACKEND = null && break
+ echo "ERROR: contextCSN empty on provider"
+ ERROR=1
+ break
+ fi
+ nCSN=`echo "$CSN1" | wc -l`
+ if test "$nCSN" -ne 1 ; then
+ echo "ERROR: Wrong contextCSN count on provider, should be 1"
+ echo "$CSN1"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+ if test -z "$CSN2" -o "$CSN1" != "$CSN2" ; then
+ echo "ERROR: contextCSN mismatch between provider and consumer"
+ echo "contextCSN on provider: $CSN1"
+ echo "contextCSN on consumer: $CSN2"
+ ERROR=1
+ break
+ fi
+ if test -z "$CSN3" -o "$CSN1" != "$CSN3" ; then
+ echo "ERROR: contextCSN mismatch between provider and forward1"
+ echo "contextCSN on provider: $CSN1"
+ echo "contextCSN on forward1: $CSN3"
+ ERROR=1
+ break
+ fi
+done
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+if test $ERROR != 0; then
+ echo "Error found after $RACE_NUM of $INITIATION_RACE_TESTS iterations"
+ exit 1
+else
+ echo "No race errors found after $INITIATION_RACE_TESTS iterations"
+fi
+
+echo ">>>>> Test succeeded"
+
+exit 0
diff --git a/tests/scripts/test062-config-delete b/tests/scripts/test062-config-delete
new file mode 100755
index 0000000..dde8acd
--- /dev/null
+++ b/tests/scripts/test062-config-delete
@@ -0,0 +1,177 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+CONFDIR=$TESTDIR/slapd.d
+DBDIR=$TESTDIR/db
+RCOUT=$TESTDIR/rcout
+
+mkdir -p $TESTDIR $CONFDIR $DBDIR
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+#
+# Test dynamic add/delete of syncprov overlay:
+# - Create minimal back-conf setup
+# - Add syncprov overlay to the cn=config database
+# - Remove the overlay again
+#
+
+echo "Starting slapd on TCP/IP port $PORT1... $PWD"
+. $CONFFILTER $BACKEND < $DYNAMICCONF > $CONFLDIF
+$SLAPADD -F $CONFDIR -n 0 -l $CONFLDIF
+cd $TESTDIR
+$SLAPD -F ./slapd.d -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+cd $TESTWD
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Inserting syncprov overlay ..."
+if [ "$SYNCPROV" = syncprovmod ]; then
+ $LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+olcModuleLoad: syncprov.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for moduleLoad ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+read CONFIGPW < $CONFIGPWF
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcOverlay=syncprov,olcDatabase={0}config,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for syncrepl config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting a refreshAndPersist search in background"
+rm -f $RCOUT
+(
+ $LDAPSEARCH -D cn=config -H $URI1 -y $CONFIGPWF -bcn=config -E \!sync=rp >/dev/null 2>&1
+ RC=$?
+ echo $RC > $RCOUT
+ exit $RC
+) &
+
+SEARCHPID=$!
+
+sleep 2
+
+echo "Removing syncprov overlay again ..."
+$LDAPDELETE -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+olcOverlay={0}syncprov,olcDatabase={0}config,cn=config
+EOF
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapmodify failed for syncrepl config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+for i in 0 1 2 3 4; do
+ if test -f "$RCOUT" ; then
+ break
+ else
+ echo "Waiting 2 seconds for RefreshAndPersist search to end ..."
+ sleep 2
+ fi
+done
+
+if test -f "$RCOUT" ; then
+ wait $SEARCHPID
+ SEARCHRC=`cat $RCOUT`
+ echo "Checking return code of backgrounded RefreshAndPersist search ..."
+ if test 52 != "$SEARCHRC" ; then
+ echo "Error: Backgrounded ldapsearch returned the wrong error code: $SEARCHRC"
+ RC=1
+ else
+ echo "Exit code correct."
+ fi
+else
+ echo "Backgrounded ldapsearch did not exit after overlay removal."
+ kill -HUP $SEARCHPID
+ RC=2
+fi
+if test $RC != 0 ; then
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Running a refreshOnly search, should fail..."
+$LDAPSEARCH -D cn=config -H $URI1 -y $CONFIGPWF -bcn=config -E \!sync=ro > /dev/null 2>&1
+
+RC=$?
+if test $RC != 12 ; then
+ echo "ldapsearch should have failed with Critical extension is unavailable (12)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+else
+ echo "Failed with \"Critical extension is unavailable (12)\". Ok."
+fi
+
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test063-delta-multiprovider b/tests/scripts/test063-delta-multiprovider
new file mode 100755
index 0000000..df4bdd1
--- /dev/null
+++ b/tests/scripts/test063-delta-multiprovider
@@ -0,0 +1,613 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+if test $ACCESSLOG = accesslogno; then
+ echo "Accesslog overlay not available, test skipped"
+ exit 0
+fi
+
+MPR=${MPR-4}
+
+XDIR=$TESTDIR/srv
+TMP=$TESTDIR/tmp
+
+mkdir -p $TESTDIR
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+if test x"$SYNCMODE" = x ; then
+ SYNCMODE=rp
+fi
+case "$SYNCMODE" in
+ ro)
+ SYNCTYPE="type=refreshOnly interval=00:00:00:03"
+ ;;
+ rp)
+ SYNCTYPE="type=refreshAndPersist interval=00:00:00:03"
+ ;;
+ *)
+ echo "unknown sync mode $SYNCMODE"
+ exit 1;
+ ;;
+esac
+
+#
+# Test delta-sync mpr
+# - start servers
+# - configure over ldap
+# - populate over ldap
+# - configure syncrepl over ldap
+# - break replication
+# - modify each server separately
+# - restore replication
+# - compare results
+#
+
+nullExclude=""
+test $BACKEND = null && nullExclude="# "
+
+KILLPIDS=
+
+echo "Initializing server configurations..."
+n=1
+while [ $n -le $MPR ]; do
+
+DBDIR=${XDIR}$n/db
+CFDIR=${XDIR}$n/slapd.d
+
+mkdir -p ${XDIR}$n $DBDIR.1 $DBDIR.2 $CFDIR
+
+cat > $TMP <<EOF
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+EOF
+
+o=1
+while [ $o -le $MPR ]; do
+PORT=`expr $BASEPORT + $o`
+URI="ldap://${LOCALHOST}:$PORT/"
+echo "olcServerID: $o $URI" >> $TMP
+o=`expr $o + 1`
+done
+echo "" >> $TMP
+
+if [ "$SYNCPROV" = syncprovmod -o "$ACCESSLOG" = accesslogmod ]; then
+ cat <<EOF >> $TMP
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+EOF
+ if [ "$SYNCPROV" = syncprovmod ]; then
+ echo "olcModuleLoad: syncprov.la" >> $TMP
+ fi
+ if [ "$ACCESSLOG" = accesslogmod ]; then
+ echo "olcModuleLoad: accesslog.la" >> $TMP
+ fi
+ echo "" >> $TMP
+fi
+
+if [ "$BACKENDTYPE" = mod ]; then
+cat <<EOF >> $TMP
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/back-$BACKEND
+olcModuleLoad: back_$BACKEND.la
+
+EOF
+fi
+
+MYURI=`eval echo '$URI'$n`
+PROVIDERURI=`eval echo '$URI'$o`
+if test $INDEXDB = indexdb ; then
+INDEX1="olcDbIndex: objectClass,entryCSN,reqStart,reqDN,reqResult eq"
+INDEX2="olcDbIndex: objectClass,entryCSN,entryUUID eq"
+else
+INDEX1=
+INDEX2=
+fi
+cat >> $TMP <<EOF
+dn: cn=schema,cn=config
+objectclass: olcSchemaconfig
+cn: schema
+
+include: file://$ABS_SCHEMADIR/core.ldif
+
+include: file://$ABS_SCHEMADIR/cosine.ldif
+
+include: file://$ABS_SCHEMADIR/inetorgperson.ldif
+
+include: file://$ABS_SCHEMADIR/openldap.ldif
+
+include: file://$ABS_SCHEMADIR/nis.ldif
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW:< file://$CONFIGPWF
+
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+olcSuffix: cn=log
+${nullExclude}olcDbDirectory: ${DBDIR}.1
+olcRootDN: $MANAGERDN
+$INDEX1
+
+dn: olcOverlay=syncprov,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+olcSpNoPresent: TRUE
+olcSpReloadHint: TRUE
+
+dn: olcDatabase={2}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {2}$BACKEND
+olcSuffix: $BASEDN
+${nullExclude}olcDbDirectory: ${DBDIR}.2
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+EOF
+
+o=1
+while [ $o -le $MPR ]; do
+PORT=`expr $BASEPORT + $o`
+URI="ldap://${LOCALHOST}:$PORT/"
+cat >>$TMP <<EOF
+olcSyncRepl: rid=00$o provider=$URI binddn="$MANAGERDN" bindmethod=simple
+ credentials=$PASSWD searchbase="$BASEDN" $SYNCTYPE
+ retry="3 +" timeout=3 logbase="cn=log"
+ logfilter="(&(objectclass=auditWriteObject)(reqresult=0))"
+ syncdata=accesslog
+EOF
+o=`expr $o + 1`
+done
+
+cat >> $TMP <<EOF
+olcMultiProvider: TRUE
+$INDEX2
+
+dn: olcOverlay=syncprov,olcDatabase={2}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+
+dn: olcOverlay=accesslog,olcDatabase={2}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcAccessLogConfig
+olcOverlay: accesslog
+olcAccessLogDB: cn=log
+olcAccessLogOps: writes
+olcAccessLogSuccess: TRUE
+
+EOF
+cat <<EOF >> $TMP
+dn: olcDatabase={3}monitor,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcmonitorConfig
+olcDatabase: {3}monitor
+
+EOF
+
+$SLAPADD -F $CFDIR -n 0 -d-1< $TMP > $TESTOUT 2>&1
+PORT=`eval echo '$PORT'$n`
+echo "Starting server $n on TCP/IP port $PORT..."
+cd ${XDIR}${n}
+LOG=`eval echo '$LOG'$n`
+$SLAPD -F slapd.d -h $MYURI -d $LVL > $LOG 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID $KILLPIDS"
+cd $TESTWD
+
+echo "Using ldapsearch to check that server $n is running..."
+sleep 1
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $MYURI \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if [ $n = 1 ]; then
+echo "Using ldapadd for context on server 1..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD -f $LDIFORDEREDCP \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for server $n database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+fi
+
+n=`expr $n + 1`
+done
+
+echo "Using ldapadd to populate server 1..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD -f $LDIFORDEREDNOCP \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for server $n database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+n=1
+while [ $n -le $MPR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldap://${LOCALHOST}:$PORT/"
+
+echo "Using ldapsearch to read all the entries from server $n..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI -w $PASSWD \
+ 'objectclass=*' > $TESTDIR/server$n.out 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at server $n ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDIFFILTER < $TESTDIR/server$n.out > $TESTDIR/server$n.flt
+n=`expr $n + 1`
+done
+
+n=2
+while [ $n -le $MPR ]; do
+echo "Comparing retrieved entries from server 1 and server $n..."
+$CMP $PROVIDERFLT $TESTDIR/server$n.flt > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - server 1 and server $n databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+n=`expr $n + 1`
+done
+
+echo "Using ldapadd to populate server 2..."
+cp $LDIFADD1 $TESTDIR/add.ldif
+echo "displayName: The other" >>$TESTDIR/add.ldif
+$LDAPADD -D "$MANAGERDN" -H $URI2 -w $PASSWD -f $TESTDIR/add.ldif \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for server 2 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+THEDN="cn=James A Jones 2,ou=Alumni Association,ou=People,dc=example,dc=com"
+sleep 1
+for i in 1 2 3; do
+ $LDAPSEARCH -S "" -b "$THEDN" -H $URI1 \
+ -s base '(objectClass=*)' entryCSN > "${PROVIDEROUT}.$i" 2>&1
+ RC=$?
+
+ if test $RC = 0 ; then
+ break
+ fi
+
+ if test $RC != 32 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+done
+
+n=1
+while [ $n -le $MPR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldap://${LOCALHOST}:$PORT/"
+
+echo "Using ldapsearch to read all the entries from server $n..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI -w $PASSWD \
+ 'objectclass=*' > $TESTDIR/server$n.out 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at server $n ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDIFFILTER < $TESTDIR/server$n.out > $TESTDIR/server$n.flt
+n=`expr $n + 1`
+done
+
+n=2
+while [ $n -le $MPR ]; do
+echo "Comparing retrieved entries from server 1 and server $n..."
+$CMP $PROVIDERFLT $TESTDIR/server$n.flt > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - server 1 and server $n databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+n=`expr $n + 1`
+done
+
+echo "Breaking replication between server 1 and 2..."
+n=1
+while [ $n -le 2 ]; do
+MYURI=`eval echo '$URI'$n`
+o=`expr $n - 1`
+$LDAPMODIFY -D cn=config -H $MYURI -y $CONFIGPWF > $TESTOUT 2>&1 <<EOF
+dn: olcDatabase={2}$BACKEND,cn=config
+changetype: modify
+delete: olcSyncRepl
+-
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server $n config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+n=`expr $n + 1`
+done
+
+echo "Using ldapmodify to force conflicts between server 1 and 2..."
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+add: description
+description: Amazing
+-
+add: displayName
+displayName: James the Second
+-
+delete: displayName
+displayName: The other
+-
+replace: mail
+mail: jaj2@mail.alumni.example.com
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI2 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+replace: employeetype
+-
+add: description
+description: Stupendous
+-
+add: displayName
+displayName: James II
+-
+delete: displayName
+displayName: The other
+-
+add: mail
+mail: jaj2@moo.net
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 2 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+delete: description
+description: Outstanding
+-
+add: description
+description: Mindboggling
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI2 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+delete: description
+description: OutStanding
+-
+add: description
+description: Bizarre
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 2 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+add: carLicense
+carLicense: 123-XYZ
+-
+add: employeeNumber
+employeeNumber: 32
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI2 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+add: employeeType
+employeeType: deadwood
+-
+add: employeeNumber
+employeeNumber: 64
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 2 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+replace: sn
+sn: Replaced later
+-
+replace: sn
+sn: Surname
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Restoring replication between server 1 and 2..."
+cat > $TMP <<EOF
+dn: olcDatabase={2}$BACKEND,cn=config
+changetype: modify
+add: olcSyncRepl
+EOF
+n=1
+while [ $n -le $MPR ]; do
+MYURI=`eval echo '$URI'$n`
+PROVIDERURI=`eval echo '$URI'$n`
+cat >> $TMP <<EOF
+olcSyncRepl: rid=00$n provider=$PROVIDERURI binddn="$MANAGERDN" bindmethod=simple
+ credentials=$PASSWD searchbase="$BASEDN" $SYNCTYPE
+ retry="3 +" timeout=3 logbase="cn=log"
+ logfilter="(&(objectclass=auditWriteObject)(reqresult=0))"
+ syncdata=accesslog
+EOF
+n=`expr $n + 1`
+done
+cat >> $TMP <<EOF
+-
+replace: olcMultiProvider
+olcMultiProvider: TRUE
+EOF
+n=1
+while [ $n -le 2 ]; do
+MYURI=`eval echo '$URI'$n`
+$LDAPMODIFY -D cn=config -H $MYURI -y $CONFIGPWF > $TESTOUT 2>&1 <$TMP
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server $n config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+n=`expr $n + 1`
+done
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+n=1
+while [ $n -le $MPR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldap://${LOCALHOST}:$PORT/"
+
+echo "Using ldapsearch to read all the entries from server $n..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI -w $PASSWD \
+ 'objectclass=*' > $TESTDIR/server$n.out 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at server $n ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDIFFILTER -s a < $TESTDIR/server$n.out > $TESTDIR/server$n.flt
+n=`expr $n + 1`
+done
+
+n=2
+while [ $n -le $MPR ]; do
+echo "Comparing retrieved entries from server 1 and server $n..."
+$CMP $PROVIDERFLT $TESTDIR/server$n.flt > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - server 1 and server $n databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+n=`expr $n + 1`
+done
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test064-constraint b/tests/scripts/test064-constraint
new file mode 100755
index 0000000..c263cf9
--- /dev/null
+++ b/tests/scripts/test064-constraint
@@ -0,0 +1,215 @@
+#!/bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $CONSTRAINT = constraintno; then
+ echo "Constraint overlay not available, test skipped"
+ exit 0
+fi
+
+CONSTRAINTDIR="$DATADIR/constraint"
+ROOTLDIF="$CONSTRAINTDIR/root.ldif"
+USERLDIF="$CONSTRAINTDIR/user.ldif"
+RESULTOUT="$CONSTRAINTDIR/constraint.out"
+SCRIPTOUT="$TESTDIR/constraint.out"
+USERDN="cn=John Doe,ou=users,$BASEDN"
+
+CONFDIR=$TESTDIR/slapd.d
+mkdir -p $TESTDIR $CONFDIR $DBDIR1
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+cat > $TESTDIR/config.ldif <<EOF
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcArgsFile: $TESTDIR/slapd.args
+olcPidFile: $TESTDIR/slapd.pid
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file://$TESTWD/schema/core.ldif
+include: file://$TESTWD/schema/cosine.ldif
+include: file://$TESTWD/schema/inetorgperson.ldif
+
+dn: olcDatabase=config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: config
+olcRootPW:< file://$CONFIGPWF
+EOF
+
+if [ "$BACKENDTYPE" = mod ]; then
+ cat >> $TESTDIR/config.ldif <<EOF
+
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/back-$BACKEND
+olcModuleLoad: back_$BACKEND.la
+EOF
+fi
+
+if [ "$CONSTRAINT" = constraintmod ]; then
+ cat >> $TESTDIR/config.ldif <<EOF
+
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+olcModuleLoad: constraint.la
+EOF
+fi
+
+cat >> $TESTDIR/config.ldif <<EOF
+
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc${BACKEND}Config
+olcDatabase: $BACKEND
+olcSuffix: $BASEDN
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+olcDbDirectory: $TESTDIR/db.1.a
+EOF
+
+if [ "$INDEXDB" = indexdb ]; then
+ cat >> $TESTDIR/config.ldif <<EOF
+olcDbIndex: objectClass eq,pres
+olcDbIndex: ou,cn,mail,surname,givenname eq,pres,sub
+EOF
+fi
+
+cat >> $TESTDIR/config.ldif <<EOF
+
+dn: olcOverlay=constraint,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcConstraintConfig
+olcOverlay: constraint
+olcConstraintAttribute: mail
+ count 3
+ restrict="ldap:///ou=users,$BASEDN??one?(objectClass=inetOrgPerson)"
+# check if restrict works (if not, this will apply to ou=users subtree as well
+# and some tests will fail)
+olcConstraintAttribute: mail count 1 restrict="ldap:///ou=groups,$BASEDN??one"
+olcConstraintAttribute: mail regex ^[[:alnum:]]+@example.com$
+olcConstraintAttribute: description count 2
+olcConstraintAttribute: jpegPhoto count 0
+# cn value has to be concatenated givenName SP sn
+olcConstraintAttribute: cn,sn,givenName
+ set "(this/givenName + [ ] + this/sn) & this/cn"
+ restrict="ldap:///$USERDN??sub?(objectClass=inetOrgPerson)"
+olcConstraintAttribute: uid
+ uri "ldap:///ou=groups,$BASEDN?uid?one?(objectClass=inetOrgPerson)"
+ restrict="ldap:///ou=users,$BASEDN??one"
+EOF
+
+$SLAPADD -F $CONFDIR -n 0 -l $TESTDIR/config.ldif
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -F $CONFDIR -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding basic structure..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD -f $ROOTLDIF >/dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $PID
+ exit $RC
+fi
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD -f $USERLDIF >/dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $PID
+ exit $RC
+fi
+
+echo "Running constraint tests..."
+for ldif in $CONSTRAINTDIR/*ok*.ldif $CONSTRAINTDIR/*fail*.ldif; do
+ ### reload
+ $LDAPDELETE -D "$MANAGERDN" -H $URI1 -w $PASSWD "$USERDN" >/dev/null 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapdelete failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $PID
+ exit $RC
+ fi
+ $LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD -f $USERLDIF >/dev/null 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $PID
+ exit $RC
+ fi
+
+ ### info
+ echo -n " [$ldif]: "
+
+ ### modify
+ $LDAPMODIFY -H $URI1 -x -D "$MANAGERDN" -f $ldif -w $PASSWD >/dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ echo "OK" | tee -a $SCRIPTOUT
+ elif test $RC = 19 ; then
+ echo "FAIL" | tee -a $SCRIPTOUT
+ else
+ echo "UNEXPECTED ($RC)"
+ fi
+done
+
+echo "Comparing output..."
+$DIFF $SCRIPTOUT $RESULTOUT > $CMPOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $PID
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $PID
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test065-proxyauthz b/tests/scripts/test065-proxyauthz
new file mode 100755
index 0000000..e96c877
--- /dev/null
+++ b/tests/scripts/test065-proxyauthz
@@ -0,0 +1,255 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+PCACHETTL=${PCACHETTL-"1m"}
+PCACHENTTL=${PCACHENTTL-"1m"}
+PCACHESTTL=${PCACHESTTL-"1m"}
+PCACHE_ENTRY_LIMIT=${PCACHE_ENTRY_LIMIT-"6"}
+PCACHE_CCPERIOD=${PCACHE_CCPERIOD-"2"}
+PCACHETTR=${PCACHETTR-"2"}
+PCACHEBTTR=${PCACHEBTTR-"5"}
+
+. $SRCDIR/scripts/defines.sh
+
+LVL=0x100
+
+if test $PROXYCACHE = pcacheno; then
+ echo "Proxy cache overlay not available, test skipped"
+ exit 0
+fi
+
+if test $BACKLDAP = "ldapno" ; then
+ echo "LDAP backend not available, test skipped"
+ exit 0
+fi
+
+if test $BACKEND = ldif ; then
+ # The (mail=example.com*) queries hit a sizelimit, so which
+ # entry is returned depends on the ordering in the backend.
+ echo "Test does not support $BACKEND backend, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+# Test proxy caching:
+# - start provider
+# - start proxy cache
+# - populate provider
+# - perform a first search
+# - verify cacheability
+# - perform a second search with the same filter and same user
+# - verify answerability and cacheability of the bind
+# - perform a third search with the same user but a different filter
+# - verify cacheability of the bind and the non-answerability of the result
+
+echo "Starting provider slapd on TCP/IP port $PORT1..."
+. $CONFFILTER < $PROXYAUTHZPROVIDERCONF > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ -D "cn=Manager,dc=example,dc=com" -w secret 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the provider directory..."
+$LDAPADD -x -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDERED > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting proxy cache on TCP/IP port $PORT2..."
+. $CONFFILTER < $PROXYAUTHZCONF | sed \
+ -e "s/@TTL@/${PCACHETTL}/" \
+ -e "s/@NTTL@/${PCACHENTTL}/" \
+ -e "s/@STTL@/${PCACHENTTL}/" \
+ -e "s/@TTR@/${PCACHETTR}/" \
+ -e "s/@ENTRY_LIMIT@/${PCACHE_ENTRY_LIMIT}/" \
+ -e "s/@CCPERIOD@/${PCACHE_CCPERIOD}/" \
+ -e "s/@BTTR@/${PCACHEBTTR}/" \
+ > $CONF2
+
+$SLAPD -f $CONF2 -h $URI2 -d $LVL -d pcache > $LOG2 2>&1 &
+CACHEPID=$!
+if test $WAIT != 0 ; then
+ echo CACHEPID $CACHEPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CACHEPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that proxy slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ -D "cn=Manager,dc=example,dc=com" -w secret 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+echo "Making queries on the proxy cache..."
+CNT=0
+
+
+CNT=`expr $CNT + 1`
+USERDN="cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com"
+UPASSWD="jaj"
+echo "Query $CNT: $USERDN"
+echo "# Query $CNT: $USERDN" >> $SEARCHOUT
+
+$LDAPSEARCH -S "" -b "dc=example,dc=com" -s SUB -H $URI2 \
+ -D "$USERDN" -w "$UPASSWD" "(sn=je*)" sn >> $SEARCHOUT 2>> $TESTOUT
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Check that the bind is cached
+grep "CACHING BIND for $USERDN" $LOG2 > /dev/null
+
+RC=$?
+if test $RC != 0 ; then
+ echo "Refresh failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS && wait
+ exit 1
+fi
+
+CNT=`expr $CNT + 1`
+USERDN="cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com"
+UPASSWD="jaj"
+echo "Query $CNT: (Bind should be cached)"
+echo "# Query $CNT: (Bind should be cached)" >> $SEARCHOUT
+
+$LDAPSEARCH -S "" -b "dc=example,dc=com" -s SUB -H $URI2 \
+ -D "$USERDN" -w "$UPASSWD" "(sn=je*)" sn >> $SEARCHOUT 2>> $TESTOUT
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+grep "CACHED BIND for $USERDN" $LOG2 > /dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "Refresh failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS && wait
+ exit 1
+fi
+
+CNT=`expr $CNT + 1`
+USERDN="cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com"
+
+echo "Query $CNT: (Bind should be cached)"
+echo "# Query $CNT: (Bind should be cached)" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "dc=example,dc=com" -s SUB -H $URI2 \
+ -D "$USERDN" -w "$UPASSWD" "(sn=je*)" sn >> $SEARCHOUT 2>> $TESTOUT
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+RC=`grep "CACHED BIND for $USERDN" $LOG2 | wc -l`
+if test $RC != 2 ; then
+ echo "Bind wasn't answered from cache"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS && wait
+ exit 1
+fi
+
+echo "=== New search on (sn=jo*)"
+cat /dev/null > $SEARCHOUT
+echo "# Query $CNT: (Bind should be cached)" >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "dc=example,dc=com" -s SUB -H $URI2 \
+ -D "$USERDN" -w "$UPASSWD" "(sn=jo*)" sn >> $SEARCHOUT 2>> $TESTOUT
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+RC=`grep "CACHED BIND for $USERDN" $LOG2 | wc -l`
+if test $RC != 3 ; then
+ echo "Bind wasn't answered from cache"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS && wait
+ exit 1
+fi
+
+RC=`grep "QUERY NOT ANSWERABLE" $LOG2 | wc -l`
+if test $RC != 3 ; then
+ echo "Search wasn't searched on remote peer"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS && wait
+ exit 1
+fi
+
+RC=`grep "dn: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com" $SEARCHOUT | wc -l`
+if test $RC != 1 ; then
+ echo "Search wasn't retrieved on remote peer"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS && wait
+ exit 1
+fi
+
+echo "Test succeeded"
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test066-autoca b/tests/scripts/test066-autoca
new file mode 100755
index 0000000..fd23140
--- /dev/null
+++ b/tests/scripts/test066-autoca
@@ -0,0 +1,339 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $AUTOCA = autocano; then
+ echo "Automatic CA overlay not available, test skipped"
+ exit 0
+fi
+
+if test $BACKEND = ldif ; then
+ # autoca tries to modify an entry in a search response,
+ # which deadlocks because the tree is readlocked by the search.
+ echo "Test does not support $BACKEND backend, test skipped"
+ exit 0
+fi
+
+CFDIR=$TESTDIR/slapd.d
+
+mkdir -p $TESTDIR $CFDIR $DBDIR1
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+#
+# Test operation of autoca:
+# - configure over ldap without TLS
+# - populate over ldap
+# - add host entry
+# - add autoca overlay
+# - generate server and user certs
+# - check for TLS operation
+#
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $DYNAMICCONF > $CONFLDIF
+$SLAPADD -F $CFDIR -n 0 -l $CONFLDIF
+$SLAPD -F $CFDIR -h $URIP1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+cd $TESTWD
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URIP1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding schema and databases on slapd..."
+$LDAPADD -D cn=config -H $URIP1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+include: file://$ABS_SCHEMADIR/core.ldif
+
+include: file://$ABS_SCHEMADIR/cosine.ldif
+
+include: file://$ABS_SCHEMADIR/inetorgperson.ldif
+
+include: file://$ABS_SCHEMADIR/openldap.ldif
+
+include: file://$ABS_SCHEMADIR/nis.ldif
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for schema config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+nullExclude="" nullOK=""
+test $BACKEND = null && nullExclude="# " nullOK="OK"
+
+if [ "$BACKENDTYPE" = mod ]; then
+ $LDAPADD -D cn=config -H $URIP1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/back-$BACKEND
+olcModuleLoad: back_$BACKEND.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for backend config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+$LDAPADD -D cn=config -H $URIP1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+olcSuffix: $BASEDN
+${nullExclude}olcDbDirectory: $DBDIR1
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if test $INDEXDB = indexdb ; then
+ $LDAPMODIFY -D cn=config -H $URIP1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcDbIndex
+olcDbIndex: objectClass,entryUUID,entryCSN eq
+olcDbIndex: cn,uid pres,eq,sub
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd modify for database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+echo "Using ldapadd to populate slapd..."
+$LDAPADD -D "$MANAGERDN" -H $URIP1 -w $PASSWD -f $LDIFORDERED \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for database populate ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding server entries to slapd..."
+$LDAPADD -D "$MANAGERDN" -H $URIP1 -w $PASSWD <<EOF >> $TESTOUT 2>&1
+dn: ou=Servers,$BASEDN
+objectClass: organizationalUnit
+ou: Servers
+
+dn: cn=localhost,ou=Servers,$BASEDN
+objectClass: device
+objectClass: ipHost
+cn: localhost
+ipHostNumber: 127.0.0.1
+
+dn: cn=www.example.com,ou=Servers,$BASEDN
+objectClass: device
+objectClass: ipHost
+cn: localhost
+ipHostNumber: 93.184.216.34
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for database populate ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Inserting autoca overlay on slapd..."
+if [ "$AUTOCA" = autocamod ]; then
+ $LDAPADD -D cn=config -H $URIP1 -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+olcModuleLoad: autoca.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for moduleLoad ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+$LDAPMODIFY -D cn=config -H $URIP1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcOverlay=autoca,olcDatabase={1}$BACKEND,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectClass: olcAutoCAConfig
+olcOverlay: autoca
+olcAutoCAlocalDN: cn=localhost,ou=Servers,$BASEDN
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for autoca config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+echo "Using ldapsearch to retrieve CA cert..."
+$LDAPSEARCH -b $BASEDN -D $MANAGERDN -H $URIP1 -w $PASSWD -s base \
+ 'objectclass=*' 'cACertificate;binary' > $SEARCHOUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Setting up CA cert..."
+echo "-----BEGIN CERTIFICATE-----" > $TESTDIR/cacert.pem
+sed -e "/^dn:/d" -e "s/cACertificate;binary:://" -e "/^$/d" $SEARCHOUT >> $TESTDIR/cacert.pem
+echo "-----END CERTIFICATE-----" >> $TESTDIR/cacert.pem
+
+echo "Using ldapsearch to generate localhost cert..."
+$LDAPSEARCH -b cn=localhost,ou=Servers,$BASEDN -D $MANAGERDN -H $URIP1 -w $PASSWD -s base \
+ -A 'objectclass=*' 'userCertificate;binary' 'userPrivateKey;binary' >> $TESTOUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to attempt TLS..."
+unset LDAPNOINIT
+LDAPTLS_CACERT=$TESTDIR/cacert.pem
+export LDAPTLS_CACERT
+$LDAPSEARCH -b $BASEDN -D $MANAGERDN -H $URIP1 -w $PASSWD -s base -ZZ \
+ 'objectclass=*' >> $TESTOUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if test $WITH_SASL = no ; then
+ echo "SASL support not available, skipping client cert authentication"
+else
+ # note - the attrs are being saved in raw DER form.
+ # they need to be base64 encoded into PEM for most programs to use them
+ # so we ignore those files for now.
+ echo "Using ldapsearch to generate user cert..."
+ $LDAPSEARCH -b "$BABSDN" -D $MANAGERDN -H $URIP1 -w $PASSWD -s base -ZZ \
+ -T $TESTDIR -t 'objectclass=*' 'userCertificate;binary' 'userPrivateKey;binary' >> $TESTOUT 2>&1
+ RC=$?
+
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Using ldapsearch to retrieve user cert..."
+ $LDAPSEARCH -b "$BABSDN" -D $MANAGERDN -H $URIP1 -w $PASSWD -s base -ZZ \
+ 'objectclass=*' 'userCertificate;binary' > $SEARCHOUT 2>&1
+ RC=$?
+
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Setting up user cert..."
+ echo "-----BEGIN CERTIFICATE-----" > $TESTDIR/usercert.pem
+ sed -e "/^dn:/d" -e "/^ dc=com/d" -e "s/userCertificate;binary:://" -e "/^$/d" $SEARCHOUT >> $TESTDIR/usercert.pem
+ echo "-----END CERTIFICATE-----" >> $TESTDIR/usercert.pem
+
+ echo "Using ldapsearch to retrieve user key..."
+ $LDAPSEARCH -b "$BABSDN" -D $MANAGERDN -H $URIP1 -w $PASSWD -s base -ZZ \
+ 'objectclass=*' 'userPrivateKey;binary' > $SEARCHOUT 2>&1
+ RC=$?
+
+ if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Setting up user key..."
+ echo "-----BEGIN PRIVATE KEY-----" > $TESTDIR/userkey.pem
+ sed -e "/^dn:/d" -e "/^ dc=com/d" -e "s/userPrivateKey;binary:://" -e "/^$/d" $SEARCHOUT >> $TESTDIR/userkey.pem
+ echo "-----END PRIVATE KEY-----" >> $TESTDIR/userkey.pem
+
+ LDAPTLS_CERT=$TESTDIR/usercert.pem
+ LDAPTLS_KEY=$TESTDIR/userkey.pem
+ export LDAPTLS_CERT
+ export LDAPTLS_KEY
+
+ echo "Setting TLSVerifyClient to try..."
+ $LDAPMODIFY -D cn=config -H $URIP1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: cn=config
+changetype: modify
+replace: olcTLSVerifyClient
+olcTLSVerifyClient: try
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed for autoca config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ $CLIENTDIR/ldapwhoami -Y EXTERNAL -H $URIP1 -ZZ
+
+ if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test067-tls b/tests/scripts/test067-tls
new file mode 100755
index 0000000..cd99fcf
--- /dev/null
+++ b/tests/scripts/test067-tls
@@ -0,0 +1,304 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $WITH_TLS = no ; then
+ echo "TLS support not available, test skipped"
+ exit 0
+fi
+
+openssl=`command -v openssl 2>/dev/null`
+certtool=`command -v certtool 2>/dev/null`
+base64=`command -v base64 2>/dev/null`
+
+mkdir -p $TESTDIR $DBDIR1
+cp -r $DATADIR/tls $TESTDIR
+
+cd $TESTWD
+
+if test -z "$TLS_PEERKEY_HASHALG"; then
+ TLS_PEERKEY_HASHALG=sha256
+fi
+if test -n "${openssl}"; then
+ TLS_PEERKEY="`"${openssl}" x509 -pubkey -noout -in $TESTDIR/tls/certs/localhost.crt | \
+ "${openssl}" rsa -pubin -outform der 2>/dev/null | \
+ "${openssl}" enc -base64 2>/dev/null`"
+
+ TLS_PEERKEY_HASHED="$TLS_PEERKEY_HASHALG:`"${openssl}" x509 -pubkey -noout -in $TESTDIR/tls/certs/localhost.crt | \
+ "${openssl}" rsa -pubin -outform der 2>/dev/null | \
+ "${openssl}" dgst "-$TLS_PEERKEY_HASHALG" -binary 2>/dev/null | \
+ "${openssl}" enc -base64 2>/dev/null`"
+
+ TLS_PEERKEY_HASHED_FAIL="$TLS_PEERKEY_HASHALG:`echo \"a fake key to hash\" | \
+ "${openssl}" dgst "-$TLS_PEERKEY_HASHALG" -binary 2>/dev/null | \
+ "${openssl}" enc -base64 2>/dev/null`"
+elif test -n "${certtool}" && test -n "${base64}"; then
+ echo "OpenSSL not found, falling back to certtool"
+ echo "This will not exercise hashed pin functionality"
+ TLS_PEERKEY="`"${certtool}" --certificate-pubkey --outder \
+ --infile $TESTDIR/tls/certs/localhost.crt \
+ --load-pubkey $TESTDIR/tls/certs/localhost.crt \
+ | "${base64}"`"
+else
+ echo "No way to extract the public key from certificate, key pinning tests will be skipped..."
+fi
+
+echo "Starting ldap:/// slapd on TCP/IP port $PORT1 and ldaps:/// slapd on $PORT2..."
+. $CONFFILTER $BACKEND < $TLSCONF > $CONF1
+$SLAPD -f $CONF1 -h "$URI1 $SURI2" -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo -n "Using ldapsearch with startTLS with no server cert validation...."
+$LDAPSEARCH -o tls_reqcert=never -ZZ -b "" -s base -H $URIP1 \
+ '@extensibleObject' > $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch (startTLS) failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+else
+ echo "success"
+fi
+
+echo -n "Using ldapsearch with startTLS with hard require cert...."
+$LDAPSEARCH -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -o tls_reqcert=hard -ZZ -b "" -s base -H $URIP1 \
+ '@extensibleObject' > $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch (startTLS) failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+else
+ echo "success"
+fi
+
+if test $WITH_TLS_TYPE = openssl ; then
+ echo -n "Using ldapsearch with startTLS and specific protocol version...."
+ $LDAPSEARCH -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -o tls_reqcert=hard -o tls_protocol_min=3.3 -ZZ -b "" -s base -H $URIP1 \
+ '@extensibleObject' > $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch (protocol-min) failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ else
+ echo "success"
+ fi
+fi
+
+echo -n "Using ldapsearch with StartTLS and pinning enabled but a pin that doesn't match..."
+$LDAPSEARCH -o tls_reqcert=never -o tls_peerkey_hash=abcd -ZZ \
+ -b "" -s base -H $URIP1 '@extensibleObject' > $SEARCHOUT 2>&1
+RC=$?
+if test $RC = 0 ; then
+ echo "ldapsearch (StartTLS) succeeded when it should have failed($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+else
+ echo "failed correctly with error code ($RC)"
+fi
+
+echo -n "Using ldapsearch with StartTLS and a valid plaintext pin..."
+if test -n "$TLS_PEERKEY"; then
+ $LDAPSEARCH -o tls_reqcert=hard -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt \
+ -o tls_peerkey_hash="${TLS_PEERKEY}" \
+ -ZZ -b "" -s base -H $URIP1 '@extensibleObject' > $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch (StartTLS) failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ else
+ echo "success"
+ fi
+else
+ echo "skipped"
+fi
+
+echo -n "Using ldapsearch with StartTLS and an invalid hashed pin..."
+if test -n "$TLS_PEERKEY_HASHED_FAIL"; then
+ $LDAPSEARCH -o tls_reqcert=hard -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt \
+ -o tls_peerkey_hash="${TLS_PEERKEY_HASHED_FAIL}" \
+ -ZZ -b "" -s base -H $URIP1 '@extensibleObject' > $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ echo "ldapsearch (StartTLS) succeeded when it should have failed($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ else
+ echo "failed correctly with error code ($RC)"
+ fi
+else
+ echo "skipped"
+fi
+
+echo -n "Using ldapsearch with StartTLS and a valid hashed pin..."
+if test -n "$TLS_PEERKEY_HASHED"; then
+ $LDAPSEARCH -o tls_reqcert=hard -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt \
+ -o tls_peerkey_hash="${TLS_PEERKEY_HASHED}" \
+ -ZZ -b "" -s base -H $URIP1 '@extensibleObject' > $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch (StartTLS) failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ else
+ echo "success"
+ fi
+else
+ echo "skipped"
+fi
+
+echo -n "Using ldapsearch on $SURI2 with no server cert validation..."
+$LDAPSEARCH -o tls_reqcert=never -b "cn=Subschema" -s base -H $SURIP2 \
+ '(&(objectClasses=top)(objectClasses=2.5.6.0))' cn objectClass \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch (ldaps) failed($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+else
+ echo "success"
+fi
+
+echo -n "Using ldapsearch on $SURI2 with reqcert HARD and no CA cert. Should fail..."
+$LDAPSEARCH -o tls_reqcert=hard -b "cn=Subschema" -s base -H $SURIP2 \
+ '(&(objectClasses=top)(objectClasses=2.5.6.0))' cn objectClass \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC = 0 ; then
+ echo "ldapsearch (ldaps) succeeded when it should have failed($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+else
+ echo "failed correctly with error code ($RC)"
+fi
+
+echo -n "Using ldapsearch on $SURI2 with CA cert and reqcert HARD..."
+$LDAPSEARCH -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -o tls_reqcert=hard -b "cn=Subschema" -s base -H $SURIP2 \
+ '(&(objectClasses=top)(objectClasses=2.5.6.0))' cn objectClass \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch (ldaps) failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+else
+ echo "success"
+fi
+
+echo -n "Using ldapsearch on $SURI2 with pinning enabled but a pin that doesn't match..."
+$LDAPSEARCH -o tls_reqcert=never -o tls_peerkey_hash=abcd \
+ -b "cn=Subschema" -s base -H $SURIP2 \
+ '(&(objectClasses=top)(objectClasses=2.5.6.0))' cn objectClass \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC = 0 ; then
+ echo "ldapsearch (ldaps) succeeded when it should have failed($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+else
+ echo "failed correctly with error code ($RC)"
+fi
+
+echo -n "Using ldapsearch on $SURI2 with a valid plaintext pin..."
+if test -n "$TLS_PEERKEY"; then
+ $LDAPSEARCH -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -o tls_reqcert=hard \
+ -o tls_peerkey_hash="${TLS_PEERKEY}" -b "cn=Subschema" -s base -H $SURIP2 \
+ '(&(objectClasses=top)(objectClasses=2.5.6.0))' cn objectClass \
+ >> $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch (ldaps) failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ else
+ echo "success"
+ fi
+else
+ echo "skipped"
+fi
+
+echo -n "Using ldapsearch on $SURI2 with an invalid hashed pin..."
+if test -n "$TLS_PEERKEY_HASHED_FAIL"; then
+ $LDAPSEARCH -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -o tls_reqcert=hard \
+ -o tls_peerkey_hash="${TLS_PEERKEY_HASHED_FAIL}" -b "cn=Subschema" -s base -H $SURIP2 \
+ '(&(objectClasses=top)(objectClasses=2.5.6.0))' cn objectClass \
+ >> $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ echo "ldapsearch (ldaps) succeeded when it should have failed($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ else
+ echo "failed correctly with error code ($RC)"
+ fi
+else
+ echo "skipped"
+fi
+
+echo -n "Using ldapsearch on $SURI2 with a valid hashed pin..."
+if test -n "$TLS_PEERKEY_HASHED"; then
+ $LDAPSEARCH -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -o tls_reqcert=hard \
+ -o tls_peerkey_hash="${TLS_PEERKEY_HASHED}" -b "cn=Subschema" -s base -H $SURIP2 \
+ '(&(objectClasses=top)(objectClasses=2.5.6.0))' cn objectClass \
+ >> $SEARCHOUT 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapsearch (ldaps) failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ else
+ echo "success"
+ fi
+else
+ echo "skipped"
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+RC=0
+
+test $KILLSERVERS != no && wait
+
+exit $RC
diff --git a/tests/scripts/test068-sasl-tls-external b/tests/scripts/test068-sasl-tls-external
new file mode 100755
index 0000000..f79471b
--- /dev/null
+++ b/tests/scripts/test068-sasl-tls-external
@@ -0,0 +1,129 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $WITH_TLS = no ; then
+ echo "TLS support not available, test skipped"
+ exit 0
+fi
+
+if test $WITH_SASL = no ; then
+ echo "SASL support not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1
+cp -r $DATADIR/tls $TESTDIR
+
+cd $TESTWD
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $TLSSASLCONF > $CONF1
+$SLAPADD -f $CONF1 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting ldap:/// slapd on TCP/IP port $PORT1 and ldaps:/// slapd on $PORT2..."
+$SLAPD -f $CONF1 -h "$URI1 $SURI2" -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo -n "Using ldapwhoami with SASL/EXTERNAL...."
+$LDAPSASLWHOAMI -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -o tls_reqcert=hard \
+ -o tls_cert=$TESTDIR/tls/certs/bjensen@mailgw.example.com.crt -o tls_key=$TESTDIR/tls/private/bjensen@mailgw.example.com.key -ZZ -Y EXTERNAL -H $URIP1 \
+ > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami (startTLS) failed ($RC)!"
+ exit $RC
+else
+ echo "success"
+fi
+
+echo -n "Validating mapped SASL ID..."
+echo 'dn:cn=barbara jensen,ou=information technology division,ou=people,dc=example,dc=com' > $TESTDIR/dn.out
+$CMP $TESTDIR/dn.out $TESTOUT > $CMPOUT
+
+RC=$?
+if test $RC != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $PID
+ exit $RC
+else
+ echo "success"
+fi
+
+# Exercise channel-bindings code in builds without SASL support
+for cb in "none" "tls-unique" "tls-endpoint" ; do
+
+ echo -n "Using ldapwhoami with SASL/EXTERNAL and SASL_CBINDING (${cb})...."
+
+ $LDAPSASLWHOAMI -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt \
+ -o tls_cert=$TESTDIR/tls/certs/bjensen@mailgw.example.com.crt \
+ -o tls_key=$TESTDIR/tls/private/bjensen@mailgw.example.com.key \
+ -o tls_reqcert=hard -o SASL_CBINDING=$cb -ZZ -Y EXTERNAL -H $URIP1 \
+ > $TESTOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $PID
+ exit $RC
+ else
+ echo "success"
+ fi
+done
+
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+if test $RC != 0 ; then
+ echo ">>>>> Test failed"
+else
+ echo ">>>>> Test succeeded"
+ RC=0
+fi
+
+test $KILLSERVERS != no && wait
+
+exit $RC
diff --git a/tests/scripts/test069-delta-multiprovider-starttls b/tests/scripts/test069-delta-multiprovider-starttls
new file mode 100755
index 0000000..2f5a0d3
--- /dev/null
+++ b/tests/scripts/test069-delta-multiprovider-starttls
@@ -0,0 +1,574 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $WITH_TLS = no ; then
+ echo "TLS support not available, test skipped"
+ exit 0
+fi
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+if test $ACCESSLOG = accesslogno; then
+ echo "Accesslog overlay not available, test skipped"
+ exit 0
+fi
+
+MMR=2
+
+XDIR=$TESTDIR/srv
+TMP=$TESTDIR/tmp
+
+mkdir -p $TESTDIR
+cp -r $DATADIR/tls $TESTDIR
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+if test x"$SYNCMODE" = x ; then
+ SYNCMODE=rp
+fi
+case "$SYNCMODE" in
+ ro)
+ SYNCTYPE="type=refreshOnly interval=00:00:00:03"
+ ;;
+ rp)
+ SYNCTYPE="type=refreshAndPersist interval=00:00:00:03"
+ ;;
+ *)
+ echo "unknown sync mode $SYNCMODE"
+ exit 1;
+ ;;
+esac
+
+#
+# Test delta-sync mmr
+# - start servers
+# - configure over ldap
+# - populate over ldap
+# - configure syncrepl over ldap
+# - break replication
+# - modify each server separately
+# - restore replication
+# - compare results
+#
+
+nullExclude=""
+test $BACKEND = null && nullExclude="# "
+
+KILLPIDS=
+
+echo "Initializing server configurations..."
+n=1
+while [ $n -le $MMR ]; do
+
+DBDIR=${XDIR}$n/db
+CFDIR=${XDIR}$n/slapd.d
+
+mkdir -p ${XDIR}$n $DBDIR.1 $DBDIR.2 $CFDIR
+
+o=`expr 3 - $n`
+cat > $TMP <<EOF
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcServerID: $n
+olcTLSCertificateFile: $TESTDIR/tls/certs/localhost.crt
+olcTLSCertificateKeyFile: $TESTDIR/tls/private/localhost.key
+
+EOF
+
+if [ "$SYNCPROV" = syncprovmod -o "$ACCESSLOG" = accesslogmod ]; then
+ cat <<EOF >> $TMP
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+EOF
+ if [ "$SYNCPROV" = syncprovmod ]; then
+ echo "olcModuleLoad: syncprov.la" >> $TMP
+ fi
+ if [ "$ACCESSLOG" = accesslogmod ]; then
+ echo "olcModuleLoad: accesslog.la" >> $TMP
+ fi
+ echo "" >> $TMP
+fi
+
+if [ "$BACKENDTYPE" = mod ]; then
+cat <<EOF >> $TMP
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/back-$BACKEND
+olcModuleLoad: back_$BACKEND.la
+
+EOF
+fi
+MYURI=`eval echo '$URI'$n`
+PROVIDERURI=`eval echo '$URIP'$o`
+if test $INDEXDB = indexdb ; then
+INDEX1="olcDbIndex: objectClass,entryCSN,reqStart,reqDN,reqResult eq"
+INDEX2="olcDbIndex: objectClass,entryCSN,entryUUID eq"
+else
+INDEX1=
+INDEX2=
+fi
+cat >> $TMP <<EOF
+dn: cn=schema,cn=config
+objectclass: olcSchemaconfig
+cn: schema
+
+include: file://$ABS_SCHEMADIR/core.ldif
+
+include: file://$ABS_SCHEMADIR/cosine.ldif
+
+include: file://$ABS_SCHEMADIR/inetorgperson.ldif
+
+include: file://$ABS_SCHEMADIR/openldap.ldif
+
+include: file://$ABS_SCHEMADIR/nis.ldif
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW:< file://$CONFIGPWF
+
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+olcSuffix: cn=log
+${nullExclude}olcDbDirectory: ${DBDIR}.1
+olcRootDN: $MANAGERDN
+$INDEX1
+
+dn: olcOverlay=syncprov,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+olcSpNoPresent: TRUE
+olcSpReloadHint: TRUE
+
+dn: olcDatabase={2}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {2}$BACKEND
+olcSuffix: $BASEDN
+${nullExclude}olcDbDirectory: ${DBDIR}.2
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+olcSyncRepl: rid=001 provider=$PROVIDERURI binddn="$MANAGERDN" bindmethod=simple
+ credentials=$PASSWD searchbase="$BASEDN" $SYNCTYPE
+ retry="3 +" timeout=3 logbase="cn=log"
+ logfilter="(&(objectclass=auditWriteObject)(reqresult=0))"
+ syncdata=accesslog tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt
+ starttls=critical
+olcMultiProvider: TRUE
+$INDEX2
+
+dn: olcOverlay=syncprov,olcDatabase={2}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+
+dn: olcOverlay=accesslog,olcDatabase={2}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcAccessLogConfig
+olcOverlay: accesslog
+olcAccessLogDB: cn=log
+olcAccessLogOps: writes
+olcAccessLogSuccess: TRUE
+
+EOF
+$SLAPADD -F $CFDIR -n 0 -d-1< $TMP > $TESTOUT 2>&1
+PORT=`eval echo '$PORT'$n`
+echo "Starting server $n on TCP/IP port $PORT..."
+cd ${XDIR}${n}
+LOG=`eval echo '$LOG'$n`
+$SLAPD -F slapd.d -h $MYURI -d $LVL > $LOG 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID $KILLPIDS"
+cd $TESTWD
+
+echo "Using ldapsearch to check that server $n is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $MYURI \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if [ $n = 1 ]; then
+echo "Using ldapadd for context on server 1..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD -f $LDIFORDEREDCP \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for server $n database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+fi
+
+n=`expr $n + 1`
+done
+
+echo "Using ldapadd to populate server 1..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD -f $LDIFORDEREDNOCP \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for server $n database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+n=1
+while [ $n -le $MMR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldap://${LOCALHOST}:$PORT/"
+
+echo "Using ldapsearch to read all the entries from server $n..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI -w $PASSWD \
+ 'objectclass=*' > $TESTDIR/server$n.out 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at server $n ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDIFFILTER < $TESTDIR/server$n.out > $TESTDIR/server$n.flt
+n=`expr $n + 1`
+done
+
+n=2
+while [ $n -le $MMR ]; do
+echo "Comparing retrieved entries from server 1 and server $n..."
+$CMP $PROVIDERFLT $TESTDIR/server$n.flt > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - server 1 and server $n databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+n=`expr $n + 1`
+done
+
+echo "Using ldapadd to populate server 2..."
+$LDAPADD -D "$MANAGERDN" -H $URI2 -w $PASSWD -f $LDIFADD1 \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for server 2 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+THEDN="cn=James A Jones 2,ou=Alumni Association,ou=People,dc=example,dc=com"
+sleep 1
+for i in 1 2 3; do
+ $LDAPSEARCH -S "" -b "$THEDN" -H $URI1 \
+ -s base '(objectClass=*)' entryCSN > "${PROVIDEROUT}.$i" 2>&1
+ RC=$?
+
+ if test $RC = 0 ; then
+ break
+ fi
+
+ if test $RC != 32 ; then
+ echo "ldapsearch failed at replica ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+done
+
+n=1
+while [ $n -le $MMR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldap://${LOCALHOST}:$PORT/"
+
+echo "Using ldapsearch to read all the entries from server $n..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI -w $PASSWD \
+ 'objectclass=*' > $TESTDIR/server$n.out 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at server $n ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDIFFILTER < $TESTDIR/server$n.out > $TESTDIR/server$n.flt
+n=`expr $n + 1`
+done
+
+n=2
+while [ $n -le $MMR ]; do
+echo "Comparing retrieved entries from server 1 and server $n..."
+$CMP $PROVIDERFLT $TESTDIR/server$n.flt > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - server 1 and server $n databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+n=`expr $n + 1`
+done
+
+echo "Breaking replication between server 1 and 2..."
+n=1
+while [ $n -le $MMR ]; do
+o=`expr 3 - $n`
+MYURI=`eval echo '$URI'$n`
+PROVIDERURI=`eval echo '$URIP'$o`
+$LDAPMODIFY -D cn=config -H $MYURI -y $CONFIGPWF > $TESTOUT 2>&1 <<EOF
+dn: olcDatabase={2}$BACKEND,cn=config
+changetype: modify
+replace: olcSyncRepl
+olcSyncRepl: rid=001 provider=$PROVIDERURI binddn="$MANAGERDN" bindmethod=simple
+ credentials=InvalidPw searchbase="$BASEDN" $SYNCTYPE
+ retry="3 +" timeout=3 logbase="cn=log"
+ logfilter="(&(objectclass=auditWriteObject)(reqresult=0))"
+ syncdata=accesslog tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt
+ starttls=critical
+-
+replace: olcMultiProvider
+olcMultiProvider: TRUE
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server $n config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+n=`expr $n + 1`
+done
+
+echo "Using ldapmodify to force conflicts between server 1 and 2..."
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+add: description
+description: Amazing
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI2 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+add: description
+description: Stupendous
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 2 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+delete: description
+description: Outstanding
+-
+add: description
+description: Mindboggling
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI2 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+delete: description
+description: OutStanding
+-
+add: description
+description: Bizarre
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 2 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+add: carLicense
+carLicense: 123-XYZ
+-
+add: employeeNumber
+employeeNumber: 32
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI2 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+add: employeeType
+employeeType: deadwood
+-
+add: employeeNumber
+employeeNumber: 64
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 2 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+replace: sn
+sn: Replaced later
+-
+replace: sn
+sn: Surname
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Restoring replication between server 1 and 2..."
+n=1
+while [ $n -le $MMR ]; do
+o=`expr 3 - $n`
+MYURI=`eval echo '$URI'$n`
+PROVIDERURI=`eval echo '$URIP'$o`
+$LDAPMODIFY -D cn=config -H $MYURI -y $CONFIGPWF > $TESTOUT 2>&1 <<EOF
+dn: olcDatabase={2}$BACKEND,cn=config
+changetype: modify
+replace: olcSyncRepl
+olcSyncRepl: rid=001 provider=$PROVIDERURI binddn="$MANAGERDN" bindmethod=simple
+ credentials=$PASSWD searchbase="$BASEDN" $SYNCTYPE
+ retry="3 +" timeout=3 logbase="cn=log"
+ logfilter="(&(objectclass=auditWriteObject)(reqresult=0))"
+ syncdata=accesslog tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt
+ starttls=critical
+-
+replace: olcMultiProvider
+olcMultiProvider: TRUE
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server $n config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+n=`expr $n + 1`
+done
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+n=1
+while [ $n -le $MMR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldap://${LOCALHOST}:$PORT/"
+
+echo "Using ldapsearch to read all the entries from server $n..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI -w $PASSWD \
+ 'objectclass=*' > $TESTDIR/server$n.out 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at server $n ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDIFFILTER -s a < $TESTDIR/server$n.out > $TESTDIR/server$n.flt
+n=`expr $n + 1`
+done
+
+n=2
+while [ $n -le $MMR ]; do
+echo "Comparing retrieved entries from server 1 and server $n..."
+$CMP $PROVIDERFLT $TESTDIR/server$n.flt > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - server 1 and server $n databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+n=`expr $n + 1`
+done
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test070-delta-multiprovider-ldaps b/tests/scripts/test070-delta-multiprovider-ldaps
new file mode 100755
index 0000000..18869d1
--- /dev/null
+++ b/tests/scripts/test070-delta-multiprovider-ldaps
@@ -0,0 +1,571 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $WITH_TLS = no ; then
+ echo "TLS support not available, test skipped"
+ exit 0
+fi
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+if test $ACCESSLOG = accesslogno; then
+ echo "Accesslog overlay not available, test skipped"
+ exit 0
+fi
+
+MMR=2
+
+XDIR=$TESTDIR/srv
+TMP=$TESTDIR/tmp
+
+mkdir -p $TESTDIR
+cp -r $DATADIR/tls $TESTDIR
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+if test x"$SYNCMODE" = x ; then
+ SYNCMODE=rp
+fi
+case "$SYNCMODE" in
+ ro)
+ SYNCTYPE="type=refreshOnly interval=00:00:00:03"
+ ;;
+ rp)
+ SYNCTYPE="type=refreshAndPersist interval=00:00:00:03"
+ ;;
+ *)
+ echo "unknown sync mode $SYNCMODE"
+ exit 1;
+ ;;
+esac
+
+#
+# Test delta-sync mmr
+# - start servers
+# - configure over ldap
+# - populate over ldap
+# - configure syncrepl over ldap
+# - break replication
+# - modify each server separately
+# - restore replication
+# - compare results
+#
+
+nullExclude=""
+test $BACKEND = null && nullExclude="# "
+
+KILLPIDS=
+
+echo "Initializing server configurations..."
+n=1
+while [ $n -le $MMR ]; do
+
+DBDIR=${XDIR}$n/db
+CFDIR=${XDIR}$n/slapd.d
+
+mkdir -p ${XDIR}$n $DBDIR.1 $DBDIR.2 $CFDIR
+
+o=`expr 3 - $n`
+cat > $TMP <<EOF
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcServerID: $n
+olcTLSCertificateFile: $TESTDIR/tls/certs/localhost.crt
+olcTLSCertificateKeyFile: $TESTDIR/tls/private/localhost.key
+
+EOF
+
+if [ "$SYNCPROV" = syncprovmod -o "$ACCESSLOG" = accesslogmod ]; then
+ cat <<EOF >> $TMP
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+EOF
+ if [ "$SYNCPROV" = syncprovmod ]; then
+ echo "olcModuleLoad: syncprov.la" >> $TMP
+ fi
+ if [ "$ACCESSLOG" = accesslogmod ]; then
+ echo "olcModuleLoad: accesslog.la" >> $TMP
+ fi
+ echo "" >> $TMP
+fi
+
+if [ "$BACKENDTYPE" = mod ]; then
+cat <<EOF >> $TMP
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/back-$BACKEND
+olcModuleLoad: back_$BACKEND.la
+
+EOF
+fi
+MYURI=`eval echo '$SURIP'$n`
+PROVIDERURI=`eval echo '$SURIP'$o`
+if test $INDEXDB = indexdb ; then
+INDEX1="olcDbIndex: objectClass,entryCSN,reqStart,reqDN,reqResult eq"
+INDEX2="olcDbIndex: objectClass,entryCSN,entryUUID eq"
+else
+INDEX1=
+INDEX2=
+fi
+cat >> $TMP <<EOF
+dn: cn=schema,cn=config
+objectclass: olcSchemaconfig
+cn: schema
+
+include: file://$ABS_SCHEMADIR/core.ldif
+
+include: file://$ABS_SCHEMADIR/cosine.ldif
+
+include: file://$ABS_SCHEMADIR/inetorgperson.ldif
+
+include: file://$ABS_SCHEMADIR/openldap.ldif
+
+include: file://$ABS_SCHEMADIR/nis.ldif
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW:< file://$CONFIGPWF
+
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+olcSuffix: cn=log
+${nullExclude}olcDbDirectory: ${DBDIR}.1
+olcRootDN: $MANAGERDN
+$INDEX1
+
+dn: olcOverlay=syncprov,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+olcSpNoPresent: TRUE
+olcSpReloadHint: TRUE
+
+dn: olcDatabase={2}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {2}$BACKEND
+olcSuffix: $BASEDN
+${nullExclude}olcDbDirectory: ${DBDIR}.2
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+olcSyncRepl: rid=001 provider=$PROVIDERURI binddn="$MANAGERDN" bindmethod=simple
+ credentials=$PASSWD searchbase="$BASEDN" $SYNCTYPE
+ retry="3 +" timeout=3 logbase="cn=log"
+ logfilter="(&(objectclass=auditWriteObject)(reqresult=0))"
+ syncdata=accesslog tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt
+olcMultiProvider: TRUE
+$INDEX2
+
+dn: olcOverlay=syncprov,olcDatabase={2}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+
+dn: olcOverlay=accesslog,olcDatabase={2}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcAccessLogConfig
+olcOverlay: accesslog
+olcAccessLogDB: cn=log
+olcAccessLogOps: writes
+olcAccessLogSuccess: TRUE
+
+EOF
+$SLAPADD -F $CFDIR -n 0 -d-1< $TMP > $TESTOUT 2>&1
+PORT=`eval echo '$PORT'$n`
+echo "Starting server $n on TCP/IP port $PORT..."
+cd ${XDIR}${n}
+LOG=`eval echo '$LOG'$n`
+$SLAPD -F slapd.d -h $MYURI -d $LVL > $LOG 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID $KILLPIDS"
+cd $TESTWD
+
+echo "Using ldapsearch to check that server $n is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -s base -b "" -H $MYURI \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if [ $n = 1 ]; then
+echo "Using ldapadd for context on server 1..."
+$LDAPADD -D "$MANAGERDN" -H $SURIP1 -w $PASSWD -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -f $LDIFORDEREDCP \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for server $n database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+fi
+
+n=`expr $n + 1`
+done
+
+echo "Using ldapadd to populate server 1..."
+$LDAPADD -D "$MANAGERDN" -H $SURIP1 -w $PASSWD -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -f $LDIFORDEREDNOCP \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for server $n database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+n=1
+while [ $n -le $MMR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldaps://${LOCALIP}:$PORT/"
+
+echo "Using ldapsearch to read all the entries from server $n..."
+$LDAPSEARCH -S "" -b "$BASEDN" -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -D "$MANAGERDN" -H $URI -w $PASSWD \
+ 'objectclass=*' > $TESTDIR/server$n.out 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at server $n ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDIFFILTER < $TESTDIR/server$n.out > $TESTDIR/server$n.flt
+n=`expr $n + 1`
+done
+
+n=2
+while [ $n -le $MMR ]; do
+echo "Comparing retrieved entries from server 1 and server $n..."
+$CMP $PROVIDERFLT $TESTDIR/server$n.flt > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - server 1 and server $n databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+n=`expr $n + 1`
+done
+
+echo "Using ldapadd to populate server 2..."
+$LDAPADD -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -D "$MANAGERDN" -H $SURIP2 -w $PASSWD -f $LDIFADD1 \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for server 2 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+THEDN="cn=James A Jones 2,ou=Alumni Association,ou=People,dc=example,dc=com"
+sleep 1
+for i in 1 2 3; do
+ $LDAPSEARCH -S "" -b "$THEDN" -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -H $SURIP1 \
+ -s base '(objectClass=*)' entryCSN > "${PROVIDEROUT}.$i" 2>&1
+ RC=$?
+
+ if test $RC = 0 ; then
+ break
+ fi
+
+ if test $RC != 32 ; then
+ echo "ldapsearch failed at replica ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+done
+
+n=1
+while [ $n -le $MMR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldaps://${LOCALIP}:$PORT/"
+
+echo "Using ldapsearch to read all the entries from server $n..."
+$LDAPSEARCH -S "" -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -b "$BASEDN" -D "$MANAGERDN" -H $URI -w $PASSWD \
+ 'objectclass=*' > $TESTDIR/server$n.out 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at server $n ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDIFFILTER < $TESTDIR/server$n.out > $TESTDIR/server$n.flt
+n=`expr $n + 1`
+done
+
+n=2
+while [ $n -le $MMR ]; do
+echo "Comparing retrieved entries from server 1 and server $n..."
+$CMP $PROVIDERFLT $TESTDIR/server$n.flt > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - server 1 and server $n databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+n=`expr $n + 1`
+done
+
+echo "Breaking replication between server 1 and 2..."
+n=1
+while [ $n -le $MMR ]; do
+o=`expr 3 - $n`
+MYURI=`eval echo '$SURIP'$n`
+PROVIDERURI=`eval echo '$SURIP'$o`
+$LDAPMODIFY -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -D cn=config -H $MYURI -y $CONFIGPWF > $TESTOUT 2>&1 <<EOF
+dn: olcDatabase={2}$BACKEND,cn=config
+changetype: modify
+replace: olcSyncRepl
+olcSyncRepl: rid=001 provider=$PROVIDERURI binddn="$MANAGERDN" bindmethod=simple
+ credentials=InvalidPw searchbase="$BASEDN" $SYNCTYPE
+ retry="3 +" timeout=3 logbase="cn=log"
+ logfilter="(&(objectclass=auditWriteObject)(reqresult=0))"
+ syncdata=accesslog tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt
+-
+replace: olcMultiProvider
+olcMultiProvider: TRUE
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server $n config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+n=`expr $n + 1`
+done
+
+echo "Using ldapmodify to force conflicts between server 1 and 2..."
+$LDAPMODIFY -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -D "$MANAGERDN" -H $SURIP1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+add: description
+description: Amazing
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -D "$MANAGERDN" -H $SURIP2 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+add: description
+description: Stupendous
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 2 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -D "$MANAGERDN" -H $SURIP1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+delete: description
+description: Outstanding
+-
+add: description
+description: Mindboggling
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -D "$MANAGERDN" -H $SURIP2 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+delete: description
+description: OutStanding
+-
+add: description
+description: Bizarre
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 2 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -D "$MANAGERDN" -H $SURIP1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+add: carLicense
+carLicense: 123-XYZ
+-
+add: employeeNumber
+employeeNumber: 32
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -D "$MANAGERDN" -H $SURIP2 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+add: employeeType
+employeeType: deadwood
+-
+add: employeeNumber
+employeeNumber: 64
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 2 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -D "$MANAGERDN" -H $SURIP1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+replace: sn
+sn: Replaced later
+-
+replace: sn
+sn: Surname
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Restoring replication between server 1 and 2..."
+n=1
+while [ $n -le $MMR ]; do
+o=`expr 3 - $n`
+MYURI=`eval echo '$SURIP'$n`
+PROVIDERURI=`eval echo '$SURIP'$o`
+$LDAPMODIFY -D cn=config -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -H $MYURI -y $CONFIGPWF > $TESTOUT 2>&1 <<EOF
+dn: olcDatabase={2}$BACKEND,cn=config
+changetype: modify
+replace: olcSyncRepl
+olcSyncRepl: rid=001 provider=$PROVIDERURI binddn="$MANAGERDN" bindmethod=simple
+ credentials=$PASSWD searchbase="$BASEDN" $SYNCTYPE
+ retry="3 +" timeout=3 logbase="cn=log"
+ logfilter="(&(objectclass=auditWriteObject)(reqresult=0))"
+ syncdata=accesslog tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt
+-
+replace: olcMultiProvider
+olcMultiProvider: TRUE
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server $n config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+n=`expr $n + 1`
+done
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+n=1
+while [ $n -le $MMR ]; do
+PORT=`expr $BASEPORT + $n`
+URI="ldaps://${LOCALIP}:$PORT/"
+
+echo "Using ldapsearch to read all the entries from server $n..."
+$LDAPSEARCH -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI -w $PASSWD \
+ 'objectclass=*' > $TESTDIR/server$n.out 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at server $n ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDIFFILTER -s a < $TESTDIR/server$n.out > $TESTDIR/server$n.flt
+n=`expr $n + 1`
+done
+
+n=2
+while [ $n -le $MMR ]; do
+echo "Comparing retrieved entries from server 1 and server $n..."
+$CMP $PROVIDERFLT $TESTDIR/server$n.flt > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - server 1 and server $n databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+n=`expr $n + 1`
+done
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test071-dirsync b/tests/scripts/test071-dirsync
new file mode 100755
index 0000000..9f5aede
--- /dev/null
+++ b/tests/scripts/test071-dirsync
@@ -0,0 +1,370 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+# requires MSAD_URI, MSAD_SUFFIX, MSAD_ADMINDN, MSAD_ADMINPW
+if test -z "$MSAD_URI"; then
+ echo "No MSAD envvars set, test skipped"
+ exit 0
+fi
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR2
+
+URI1=$MSAD_URI
+BASEDN="ou=OpenLDAPtest,$MSAD_SUFFIX"
+DC=`echo $MSAD_SUFFIX | sed -e 's/dc=//' -e 's/,.*//'`
+
+#
+# Test replication:
+# - populate MSAD over ldap
+# - start consumer
+# - perform some modifies and deletes
+# - attempt to modify the consumer (referral)
+# - retrieve database over ldap and compare against expected results
+#
+
+# Notes:
+# We use a separate OU under the MSAD suffix to contain our test objects,
+# since we can't just wipe out the entire directory when starting over.
+# The replication search filter is thus more convoluted than would normally
+# be needed. Typically it would only need (|(objectclass=user)(objectclass=group))
+#
+# MSAD does referential integrity by default, so to get 1-to-1 modifications
+# we must add users before creating groups that reference them, and we
+# should delete group memberships before deleting users. If we delete
+# users first, MSAD will automatically remove them from their groups,
+# but won't notify us of these changed groups.
+# We could use the refint overlay to duplicate this behavior, but that's
+# beyond the scope of this test.
+
+echo "Using ldapsearch to check that MSAD is running..."
+$LDAPSEARCH -D $MSAD_ADMINDN -w $MSAD_ADMINPW -s base -b "$MSAD_SUFFIX" -H $MSAD_URI 'objectclass=*' > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+echo "Using ldapdelete to delete old MSAD test tree, if any..."
+$LDAPDELETE -D "$MSAD_ADMINDN" -H $MSAD_URI -w $MSAD_ADMINPW -r "$BASEDN"
+RC=$?
+
+echo "Using ldapadd to create the test context entry in MSAD..."
+sed -e "s/dc=example,dc=com/$MSAD_SUFFIX/" < $LDIFDIRSYNCCP | \
+ $LDAPADD -D "$MSAD_ADMINDN" -H $MSAD_URI -w $MSAD_ADMINPW > /dev/null 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting consumer slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $DIRSYNC1CONF | . $CONFDIRSYNC > $CONF2
+$SLAPADD -f $CONF2 <<EOMODS
+dn: $MSAD_SUFFIX
+dc: $DC
+objectclass: organization
+objectclass: dcObject
+o: OpenLDAP Testing
+
+EOMODS
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that consumer slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+echo "Using ldapsearch to check that consumer received context entry..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$BASEDN" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for syncrepl to catch up..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate MSAD..."
+sed -e "s/dc=example,dc=com/$BASEDN/" < $LDIFDIRSYNCNOCP | \
+ $LDAPADD -D "$MSAD_ADMINDN" -H $MSAD_URI -w $MSAD_ADMINPW > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Using ldapmodify to modify provider directory..."
+
+#
+# Do some modifications
+#
+
+$LDAPMODIFY -v -H $MSAD_URI -D "$MSAD_ADMINDN" -w $MSAD_ADMINPW > \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=James A Jones 1, ou=Alumni Association, ou=People, $BASEDN
+changetype: modify
+add: carLicense
+carLicense: Orange Juice
+-
+delete: sn
+sn: Jones
+-
+add: sn
+sn: Jones
+
+dn: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, $BASEDN
+changetype: modify
+replace: carLicense
+carLicense: Iced Tea
+carLicense: Mad Dog 20/20
+
+dn: cn=ITD Staff,ou=Groups,$BASEDN
+changetype: modify
+delete: uniquemember
+uniquemember: cn=James A Jones 2, ou=Information Technology Division, ou=People, $BASEDN
+uniquemember: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, $BASEDN
+-
+add: uniquemember
+uniquemember: cn=Dorothy Stevens, ou=Alumni Association, ou=People, $BASEDN
+uniquemember: cn=James A Jones 1, ou=Alumni Association, ou=People, $BASEDN
+
+dn: cn=All Staff,ou=Groups,$BASEDN
+changetype: modify
+replace: description
+description: The whole universe
+-
+delete: member
+member: cn=James A Jones 2,ou=Information Technology Division,ou=People,$BASEDN
+
+dn: cn=Gern Jensen, ou=Information Technology Division, ou=People, $BASEDN
+changetype: add
+objectclass: inetorgperson
+objectclass: domainrelatedobject
+cn: Gern Jensen
+sn: Jensen
+uid: gjensen
+title: Chief Investigator, ITD
+postaladdress: ITD $ 535 W. William St $ Ann Arbor, MI 48103
+seealso: cn=All Staff, ou=Groups, $BASEDN
+carLicense: Coffee
+homepostaladdress: 844 Brown St. Apt. 4 $ Ann Arbor, MI 48104
+description: Very odd
+facsimiletelephonenumber: +1 313 555 7557
+telephonenumber: +1 313 555 8343
+mail: gjensen@mailgw.example.com
+homephone: +1 313 555 8844
+associateddomain: test.openldap.org
+
+dn: ou=Retired, ou=People, $BASEDN
+changetype: add
+objectclass: organizationalUnit
+ou: Retired
+
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, $BASEDN
+changetype: add
+objectclass: inetorgperson
+objectclass: domainrelatedobject
+cn: Rosco P. Coltrane
+sn: Coltrane
+uid: rosco
+associateddomain: test.openldap.org
+
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, $BASEDN
+changetype: modrdn
+newrdn: cn=Rosco P. Coltrane
+deleteoldrdn: 1
+newsuperior: ou=Retired, ou=People, $BASEDN
+
+dn: ou=testdomain1,$BASEDN
+changetype: modrdn
+newrdn: ou=itsdomain1
+deleteoldrdn: 1
+
+dn: ou=itsdomain1,$BASEDN
+changetype: modify
+replace: description
+description: Example, Inc. ITS test domain
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Performing modrdn alone on the provider..."
+$LDAPMODIFY -v -H $MSAD_URI -D "$MSAD_ADMINDN" -w $MSAD_ADMINPW > \
+ $TESTOUT 2>&1 << EOMODS
+dn: ou=testdomain2,$BASEDN
+changetype: modrdn
+newrdn: ou=itsdomain2
+deleteoldrdn: 1
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Performing modify alone on the provider..."
+$LDAPMODIFY -v -H $MSAD_URI -D "$MSAD_ADMINDN" -w $MSAD_ADMINPW > \
+ $TESTOUT 2>&1 << EOMODS
+dn: ou=itsdomain2,$BASEDN
+changetype: modify
+replace: description
+description: Example, Inc. itsdomain2 test domain
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Performing larger modify on the provider..."
+$LDAPMODIFY -v -H $MSAD_URI -D "$MSAD_ADMINDN" -w $MSAD_ADMINPW > \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=James A Jones 2, ou=Information Technology Division, ou=People, $BASEDN
+changetype: delete
+
+dn: cn=Alumni Assoc Staff,ou=Groups,$BASEDN
+changetype: modify
+replace: description
+description: blablabla
+-
+replace: member
+member: cn=Manager,$BASEDN
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,$BASEDN
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,$BASEDN
+member: cn=Jane Doe,ou=Alumni Association,ou=People,$BASEDN
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,$BASEDN
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,$BASEDN
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,$BASEDN
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+OPATTRS="entryUUID creatorsName createTimestamp modifiersName modifyTimestamp"
+
+echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -D $MSAD_ADMINDN -w $MSAD_ADMINPW -S "" -H $MSAD_URI -b "$MSAD_SUFFIX" -E \!dirsync=0/0 -o ldif_wrap=120 \
+ '(associatedDomain=test.openldap.org)' > $PROVIDEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 -o ldif_wrap=120 \
+ '(objectclass=*)' > $CONSUMEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo "Filtering provider results..."
+$LDIFFILTER -s a < $PROVIDEROUT | sed -e 's/CN=/cn=/g' -e 's/OU=/ou=/g' -e 's/DC=/dc=/g' > $PROVIDERFLT
+echo "Filtering consumer results..."
+$LDIFFILTER -s a < $CONSUMEROUT > $CONSUMERFLT
+
+echo "Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test072-dsee-sync b/tests/scripts/test072-dsee-sync
new file mode 100755
index 0000000..bb3ba7a
--- /dev/null
+++ b/tests/scripts/test072-dsee-sync
@@ -0,0 +1,331 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+DSADM=`command -v dsadm`
+if test -z "$DSADM"; then
+ echo "DSEE dsadm not in path, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR2
+
+#
+# Test replication:
+# - start provider
+# - start consumer
+# - populate over ldap
+# - perform some modifies and deleted
+# - attempt to modify the consumer (referral)
+# - retrieve database over ldap and compare against expected results
+#
+
+DSEEPW=secret21
+DSEEDN="cn=Directory Manager"
+DSEEPWF=$TESTDIR/dseepw
+
+echo "secret21" > $DSEEPWF
+
+echo "Setting up DSEE provider slapd on TCP/IP port $PORT1..."
+dsadm create -p $PORT1 -w $DSEEPWF $DBDIR1
+dsadm start $DBDIR1
+dsconf create-suffix -c -p $PORT1 -w $DSEEPWF $BASEDN
+dsconf set-server-prop -p $PORT1 -w $DSEEPWF moddn-enabled:on
+dsconf set-server-prop -p $PORT1 -w $DSEEPWF retro-cl-enabled:on
+dsadm restart $DBDIR1
+KILLPIDS=`basename $DBDIR1/locks/server/*`
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$BASEDN" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting consumer slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $DSEESYNC1CONF > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that consumer slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# using LDIFDIRSYNCNOCP to avoid custom OpenLDAP schema
+echo "Using ldapadd to populate the provider directory..."
+$LDAPADD -D "$DSEEDN" -H $URI1 -w $DSEEPW < \
+ $LDIFDIRSYNCNOCP > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Using ldapmodify to modify provider directory..."
+
+#
+# Do some modifications
+#
+
+$LDAPMODIFY -v -D "$DSEEDN" -H $URI1 -w $DSEEPW > \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example,dc=com
+changetype: modify
+add: carLicense
+carLicense: Orange Juice
+-
+delete: sn
+sn: Jones
+-
+add: sn
+sn: Jones
+
+dn: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+replace: carLicense
+carLicense: Iced Tea
+carLicense: Mad Dog 20/20
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: uniquemember
+uniquemember: cn=James A Jones 2, ou=Information Technology Division, ou=People, dc=example,dc=com
+uniquemember: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+-
+add: uniquemember
+uniquemember: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+uniquemember: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: description
+
+dn: cn=Gern Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+changetype: add
+objectclass: inetOrgPerson
+cn: Gern Jensen
+sn: Jensen
+uid: gjensen
+title: Chief Investigator, ITD
+postaladdress: ITD $ 535 W. William St $ Ann Arbor, MI 48103
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+carLicense: Coffee
+homepostaladdress: 844 Brown St. Apt. 4 $ Ann Arbor, MI 48104
+description: Very odd
+facsimiletelephonenumber: +1 313 555 7557
+telephonenumber: +1 313 555 8343
+mail: gjensen@mailgw.example.com
+homephone: +1 313 555 8844
+
+dn: ou=Retired,ou=People,dc=example,dc=com
+changetype: add
+objectclass: organizationalUnit
+ou: Retired
+
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: add
+objectclass: inetOrgPerson
+cn: Rosco P. Coltrane
+sn: Coltrane
+uid: rosco
+
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modrdn
+newrdn: cn=Rosco P. Coltrane
+deleteoldrdn: 1
+newsuperior: ou=Retired,ou=People,dc=example,dc=com
+
+dn: cn=James A Jones 2, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: delete
+
+dn: ou=testdomain1,dc=example,dc=com
+changetype: modrdn
+newrdn: ou=itsdomain1
+deleteoldrdn: 1
+
+dn: ou=itsdomain1,dc=example,dc=com
+changetype: modify
+replace: description
+description: Example, Inc. ITS test domain
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Performing modrdn alone on the provider..."
+$LDAPMODIFY -v -D "$DSEEDN" -H $URI1 -w $DSEEPW > \
+ $TESTOUT 2>&1 << EOMODS
+dn: ou=testdomain2,dc=example,dc=com
+changetype: modrdn
+newrdn: ou=itsdomain2
+deleteoldrdn: 1
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Performing modify alone on the provider..."
+$LDAPMODIFY -v -D "$DSEEDN" -H $URI1 -w $DSEEPW > \
+ $TESTOUT 2>&1 << EOMODS
+dn: ou=itsdomain2,dc=example,dc=com
+changetype: modify
+replace: description
+description: Example, Inc. itsdomain2 test domain
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Performing larger modify on the provider..."
+$LDAPMODIFY -v -D "$DSEEDN" -H $URI1 -w $DSEEPW > \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Alumni Assoc Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+replace: cn
+cn: Alumni Assoc Staff
+-
+replace: description
+description: blablabla
+-
+replace: member
+member: cn=Manager,dc=example,dc=com
+member: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jane Doe,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Jennifer Smith,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Mark Elliot,ou=Alumni Association,ou=People,dc=example,dc=com
+member: cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+OPATTRS="creatorsName createTimestamp modifiersName modifyTimestamp"
+
+echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ -D "$DSEEDN" -w $DSEEPW \
+ '(objectclass=*)' '*' $OPATTRS > $PROVIDEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI2 \
+ '(objectclass=*)' '*' $OPATTRS > $CONSUMEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo "Filtering provider results..."
+$LDIFFILTER -s a < $PROVIDEROUT > $PROVIDERFLT
+echo "Filtering consumer results..."
+$LDIFFILTER -s a < $CONSUMEROUT > $CONSUMERFLT
+
+echo "Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test073-asyncmeta b/tests/scripts/test073-asyncmeta
new file mode 100755
index 0000000..bee58d7
--- /dev/null
+++ b/tests/scripts/test073-asyncmeta
@@ -0,0 +1,620 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+echo ""
+
+if test $BACKASYNCMETA = asyncmetano ; then
+ echo "asyncmeta backend not available, test skipped"
+ exit 0
+fi
+
+if test $BACKLDAP = ldapno ; then
+ echo "ldap backend not available, test skipped"
+ exit 0
+fi
+
+rm -rf $TESTDIR
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $METACONF1 > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDERED > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $METACONF2 > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$METAMANAGERDN" -H $URI2 -w $PASSWD < \
+ $LDIFMETA >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT3..."
+. $CONFFILTER $BACKEND < $ASYNCMETACONF > $CONF3
+$SLAPD -f $CONF3 -h $URI3 -d $LVL > $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI3 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+BASEDN="o=Example,c=US"
+echo "Searching base=\"$BASEDN\"..."
+echo "# searching base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+# ITS#4195: spurious matchedDN when the search scopes the main target,
+# and the searchBase is not present, so that target returns noSuchObject
+BASEDN="ou=Meta,o=Example,c=US"
+echo "Searching base=\"$BASEDN\"..."
+echo "# searching base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+#
+# Do some modifications
+#
+
+BASEDN="o=Example,c=US"
+echo "Modifying database \"$BASEDN\"..."
+$LDAPMODIFY -v -D "cn=Manager,$BASEDN" -H $URI3 -w $PASSWD \
+ -M >> $TESTOUT 2>&1 << EOMODS
+# These operations (updates with objectClass mapping) triggered ITS#3499
+dn: cn=Added Group,ou=Groups,$BASEDN
+changetype: add
+objectClass: groupOfNames
+objectClass: uidObject
+cn: Added Group
+member: cn=Added Group,ou=Groups,$BASEDN
+uid: added
+
+dn: cn=Another Added Group,ou=Groups,$BASEDN
+changetype: add
+objectClass: groupOfNames
+cn: Another Added Group
+member: cn=Added Group,ou=Groups,$BASEDN
+member: cn=Another Added Group,ou=Groups,$BASEDN
+
+dn: cn=Another Added Group,ou=Groups,$BASEDN
+changetype: modify
+add: objectClass
+objectClass: uidObject
+-
+add: uid
+uid: added
+-
+
+dn: cn=Added Group,ou=Groups,$BASEDN
+changetype: modify
+delete: objectClass
+objectClass: uidObject
+-
+delete: uid
+-
+
+dn: ou=Meta,$BASEDN
+changetype: modify
+add: description
+description: added to "ou=Meta,$BASEDN"
+-
+
+dn: ou=Who's going to handle this?,$BASEDN
+changetype: add
+objectClass: organizationalUnit
+ou: Who's going to handle this?
+description: added
+description: will be deleted
+
+dn: ou=Same as above,$BASEDN
+changetype: add
+objectClass: organizationalUnit
+ou: Same as above
+description: added right after "Who's going to handle this?"
+description: will be preserved
+
+dn: ou=Who's going to handle this?,$BASEDN
+changetype: delete
+
+dn: ou=Who's going to handle this?,ou=Meta,$BASEDN
+changetype: add
+objectClass: organizationalUnit
+ou: Who's going to handle this?
+description: added
+description: will be deleted
+
+dn: ou=Same as above,ou=Meta,$BASEDN
+changetype: add
+objectClass: organizationalUnit
+ou: Same as above
+description: added right after "Who's going to handle this?"
+description: will be preserved
+
+dn: cn=Added User,ou=Same as above,ou=Meta,$BASEDN
+changetype: add
+objectClass: inetOrgPerson
+cn: Added User
+sn: User
+userPassword: secret
+
+dn: ou=Who's going to handle this?,ou=Meta,$BASEDN
+changetype: delete
+EOMODS
+
+RC=$?
+#if test $RC != 0 ; then
+# echo "Modify failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Modify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Searching base=\"$BASEDN\"..."
+echo "# searching base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+BASEDN="o=Example,c=US"
+echo " base=\"$BASEDN\"..."
+echo "# base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" -M "$FILTER" '*' ref \
+ >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+BASEDN="o=Example,c=US"
+FILTER="(seeAlso=cn=all staff,ou=Groups,$BASEDN)"
+echo "Searching filter=\"$FILTER\""
+echo " attrs=\"seeAlso\""
+echo " base=\"$BASEDN\"..."
+echo "# searching filter=\"$FILTER\"" >> $SEARCHOUT
+echo "# attrs=\"seeAlso\"" >> $SEARCHOUT
+echo "# base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" "$FILTER" seeAlso \
+ >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+FILTER="(uid=example)"
+echo "Searching filter=\"$FILTER\""
+echo " attrs=\"uid\""
+echo " base=\"$BASEDN\"..."
+echo "# searching filter=\"$FILTER\"" >> $SEARCHOUT
+echo "# attrs=\"uid\"" >> $SEARCHOUT
+echo "# base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" "$FILTER" uid \
+ >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+FILTER="(member=cn=Another Added Group,ou=Groups,$BASEDN)"
+echo "Searching filter=\"$FILTER\""
+echo " attrs=\"member\""
+echo " base=\"$BASEDN\"..."
+echo "# searching filter=\"$FILTER\"" >> $SEARCHOUT
+echo "# attrs=\"member\"" >> $SEARCHOUT
+echo "# base=\"$BASEDN\"..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -b "$BASEDN" "$FILTER" member \
+ >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Waiting 10 seconds for cached connections to timeout..."
+sleep 10
+
+echo "Searching with a timed out connection..."
+echo "# searching filter=\"$FILTER\"" >> $SEARCHOUT
+echo "# attrs=\"member\"" >> $SEARCHOUT
+echo "# base=\"$BASEDN\"" >> $SEARCHOUT
+echo "# with a timed out connection..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 -D "cn=Manager,$BASEDN" -w $PASSWD \
+ -b "$BASEDN" "$FILTER" member \
+ >> $SEARCHOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "Search failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+# NOTE: cannot send to $SEARCHOUT because the returned entries
+# are not predictable...
+echo "Checking server-enforced size limit..."
+echo "# Checking server-enforced size limit..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 \
+ -D "cn=Bjorn Jensen,ou=Information Technology Division,ou=People,$BASEDN" -w bjorn \
+ -b "$BASEDN" "(objectClass=*)" 1.1 \
+ >> $TESTOUT 2>&1
+RC=$?
+case $RC,$BACKEND in
+ 4,* | 0,null)
+ ;;
+ 0,*)
+ echo "Search should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+# NOTE: cannot send to $SEARCHOUT because the returned entries
+# are not predictable...
+echo "Checking client-requested size limit..."
+echo "# Checking client-requested size limit..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -H $URI3 \
+ -D "cn=Bjorn Jensen,ou=Information Technology Division,ou=People,$BASEDN" -w bjorn \
+ -b "$BASEDN" -z 2 "(objectClass=*)" 1.1 \
+ >> $TESTOUT 2>&1
+RC=$?
+case $RC,$BACKEND in
+ 4,* | 0,null)
+ ;;
+ 0,*)
+ echo "Search should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+ *)
+ echo "Search failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $METAOUT > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - meta search/modification didn't succeed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Binding as newly added user to database \"$BASEDN\"..."
+$LDAPWHOAMI -H $URI3 \
+ -D "cn=Added User,ou=Same as above,ou=Meta,$BASEDN" \
+ -w $PASSWD >> $TESTOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "WhoAmI failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC in
+ 0)
+ ;;
+ 51)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ ;;
+ *)
+ echo "WhoAmI failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ ;;
+esac
+
+
+echo "Binding with incorrect password to database \"$BASEDN\"..."
+$LDAPWHOAMI -H $URI3 \
+ -D "cn=Added User,ou=Same as above,ou=Meta,$BASEDN" \
+ -w bogus >> $TESTOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "WhoAmI failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC,$BACKEND in
+ 0,null)
+ ;;
+ 0,*)
+ echo "WhoAmI should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+ 51,*)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ ;;
+ *)
+ ;;
+esac
+
+echo "Binding with non-existing user to database \"$BASEDN\"..."
+$LDAPWHOAMI -H $URI3 \
+ -D "cn=Non-existing User,ou=Same as above,ou=Meta,$BASEDN" \
+ -w bogus >> $TESTOUT 2>&1
+RC=$?
+#if test $RC != 0 ; then
+# echo "WhoAmI failed ($RC)!"
+# test $KILLSERVERS != no && kill -HUP $KILLPIDS
+# exit $RC
+#fi
+case $RC,$BACKEND in
+ 0,null)
+ ;;
+ 0,*)
+ echo "WhoAmI should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit -1
+ ;;
+ 51,*)
+ echo "### Hit LDAP_BUSY problem; you may want to re-run the test"
+ ;;
+ *)
+ ;;
+esac
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test074-asyncmeta-concurrency b/tests/scripts/test074-asyncmeta-concurrency
new file mode 100755
index 0000000..09a14fd
--- /dev/null
+++ b/tests/scripts/test074-asyncmeta-concurrency
@@ -0,0 +1,226 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+echo ""
+
+if test $BACKASYNCMETA = asyncmetano ; then
+ echo "asyncmeta backend not available, test skipped"
+ exit 0
+fi
+
+if test $BACKLDAP = ldapno ; then
+ echo "ldap backend not available, test skipped"
+ exit 0
+fi
+
+if test x$TESTLOOPS = x ; then
+ TESTLOOPS=50
+fi
+
+if test x$TESTCHILDREN = x ; then
+ TESTCHILDREN=20
+fi
+
+rm -rf $TESTDIR
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $METACONF1 > $CONF1
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \
+ $LDIFORDERED > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $METACONF2 > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -D "$METAMANAGERDN" -H $URI2 -w $PASSWD < \
+ $LDIFMETA >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT3..."
+. $CONFFILTER $BACKEND < $ASYNCMETACONF > $CONF3
+$SLAPD -f $CONF3 -h $URI3 -d $LVL > $LOG3 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI3 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+mkdir -p $TESTDIR/$DATADIR
+METABASEDN="o=Example,c=US"
+for f in $DATADIR/do_* ; do
+ sed -e "s;$BASEDN;$METABASEDN;" $f > $TESTDIR/$f
+done
+
+# add a read that matches only the local database, but selects
+# also the remote as candidate; this should be removed to compare
+# execution times with test008...
+for f in $TESTDIR/$DATADIR/do_read.* ; do
+ echo "ou=Meta,$METABASEDN" >> $f
+done
+
+# add a read that matches a referral in the local database only,
+# but selects also the remote as candidate; this should be removed
+# to compare execution times with test008...
+for f in $TESTDIR/$DATADIR/do_read.* ; do
+ echo "cn=Somewhere,ou=Meta,$METABASEDN" >> $f
+done
+
+# add a bind that resolves to a referral
+for f in $TESTDIR/$DATADIR/do_bind.* ; do
+ echo "cn=Foo,ou=Meta,$METABASEDN" >> $f
+ echo "bar" >> $f
+ echo "" >> $f
+ echo "" >> $f
+done
+
+# fix test data to include back-monitor, if available
+# NOTE: copies do_* files from $TESTDIR/$DATADIR to $TESTDIR
+$MONITORDATA "$TESTDIR/$DATADIR" "$TESTDIR"
+
+BINDDN="cn=Manager,o=Local"
+PASSWD="secret"
+echo "Using tester for concurrent server access..."
+$SLAPDTESTER -P "$PROGDIR" -d "$TESTDIR" -H $URI3 \
+ -D "$BINDDN" -w $PASSWD -l $TESTLOOPS -j $TESTCHILDREN \
+ -r 20 -i '!REFERRAL' -i '*INVALID_CREDENTIALS' -SS
+RC=$?
+
+if test $RC != 0 ; then
+ echo "slapd-tester failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to retrieve all the entries..."
+$LDAPSEARCH -S "" -b "$METABASEDN" -H $URI3 \
+ 'objectClass=*' > $SEARCHOUT 2>&1
+RC=$?
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ exit $RC
+fi
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+$LDIFFILTER < $METACONCURRENCYOUT > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "comparison failed - slapd-asyncmeta search/modification didn't succeed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test075-dsee-persist b/tests/scripts/test075-dsee-persist
new file mode 100755
index 0000000..fff63ee
--- /dev/null
+++ b/tests/scripts/test075-dsee-persist
@@ -0,0 +1,421 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+OPATTRS="creatorsName createTimestamp modifiersName modifyTimestamp"
+
+DSADM=`command -v dsadm`
+if test -z "$DSADM"; then
+ echo "DSEE dsadm not in path, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR4
+
+#
+# Test replication:
+# - start provider
+# - start consumer
+# - populate over ldap
+# - perform some modifies and deleted
+# - attempt to modify the consumer (referral or chain)
+# - retrieve database over ldap and compare against expected results
+#
+
+DSEEPW=secret21
+DSEEDN="cn=Directory Manager"
+DSEEPWF=$TESTDIR/dseepw
+
+echo "secret21" > $DSEEPWF
+
+echo "Setting up DSEE provider slapd on TCP/IP port $PORT1..."
+dsadm create -p $PORT1 -w $DSEEPWF $DBDIR1
+dsadm start $DBDIR1
+dsconf create-suffix -c -p $PORT1 -w $DSEEPWF $BASEDN
+dsconf set-server-prop -p $PORT1 -w $DSEEPWF moddn-enabled:on
+dsconf set-server-prop -p $PORT1 -w $DSEEPWF retro-cl-enabled:on
+dsadm restart $DBDIR1
+PID=`basename $DBDIR1/locks/server/*`
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$BASEDN" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting consumer slapd on TCP/IP port $PORT4..."
+. $CONFFILTER $BACKEND < $DSEESYNC2CONF > $CONF4
+$SLAPD -f $CONF4 -h $URI4 -d $LVL > $LOG4 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that consumer slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI4 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# using LDIFDIRSYNCNOCP to avoid custom OpenLDAP schema
+echo "Using ldapadd to populate the provider directory..."
+$LDAPADD -D "$DSEEDN" -H $URI1 -w $DSEEPW < \
+ $LDIFDIRSYNCNOCP > /dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ -D "$DSEEDN" -w "$DSEEPW" \
+ '(objectclass=*)' '*' $OPATTRS > $PROVIDEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI4 \
+ '(objectclass=*)' '*' $OPATTRS > $CONSUMEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering provider results..."
+$LDIFFILTER -s a < $PROVIDEROUT > $PROVIDERFLT
+echo "Filtering consumer results..."
+$LDIFFILTER -s a < $CONSUMEROUT > $CONSUMERFLT
+
+echo "Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Stopping the provider, sleeping 10 seconds and restarting it..."
+kill -HUP "$PID"
+wait $PID
+sleep 10
+echo "RESTART" >> $LOG1
+dsadm start $DBDIR1
+PID=`basename $DBDIR1/locks/server/*`
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID $CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$BASEDN" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+echo "Waiting $SLEEP1 seconds for consumer to reconnect..."
+sleep $SLEEP1
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapmodify to modify provider directory..."
+
+#
+# Do some modifications
+#
+
+$LDAPMODIFY -v -D "$DSEEDN" -H $URI1 -w $DSEEPW > \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=James A Jones 1, ou=Alumni Association, ou=People, dc=example,dc=com
+changetype: modify
+add: carLicense
+carLicense: Orange Juice
+-
+delete: sn
+sn: Jones
+-
+add: sn
+sn: Jones
+
+dn: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+replace: carLicense
+carLicense: Iced Tea
+
+dn: cn=ITD Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: uniquemember
+uniquemember: cn=James A Jones 2, ou=Information Technology Division, ou=People, dc=example,dc=com
+uniquemember: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+-
+add: uniquemember
+uniquemember: cn=Dorothy Stevens,ou=Alumni Association,ou=People,dc=example,dc=com
+uniquemember: cn=James A Jones 1,ou=Alumni Association,ou=People,dc=example,dc=com
+
+dn: cn=All Staff,ou=Groups,dc=example,dc=com
+changetype: modify
+delete: description
+
+dn: cn=Gern Jensen,ou=Information Technology Division,ou=People,dc=example,dc=com
+changetype: add
+objectclass: inetOrgPerson
+cn: Gern Jensen
+sn: Jensen
+uid: gjensen
+title: Chief Investigator, ITD
+postaladdress: ITD $ 535 W. William St $ Ann Arbor, MI 48103
+seealso: cn=All Staff,ou=Groups,dc=example,dc=com
+carLicense: Coffee
+homepostaladdress: 844 Brown St. Apt. 4 $ Ann Arbor, MI 48104
+description: Very odd
+facsimiletelephonenumber: +1 313 555 7557
+facsimiletelephonenumber: +1 313 555 9998
+facsimiletelephonenumber: +1 313 555 9999
+telephonenumber: +1 313 555 8343
+mail: gjensen@mailgw.example.com
+homephone: +1 313 555 8844
+
+# modify attribute with no matching rule (ITS#6458)
+dn: cn=Gern Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+replace: facsimiletelephonenumber
+facsimiletelephonenumber: +1 313 555 9998
+facsimiletelephonenumber: +1 313 555 9999
+
+dn: cn=Gern Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+replace: facsimiletelephonenumber
+facsimiletelephonenumber: +1 313 555 9998
+facsimiletelephonenumber: +1 313 555 9999
+facsimiletelephonenumber: +1 313 555 7557
+
+dn: cn=Gern Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+replace: facsimiletelephonenumber
+facsimiletelephonenumber: +1 313 555 9998
+facsimiletelephonenumber: +1 313 555 9999
+
+dn: ou=Retired,ou=People,dc=example,dc=com
+changetype: add
+objectclass: organizationalUnit
+ou: Retired
+
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: add
+objectclass: inetOrgPerson
+cn: Rosco P. Coltrane
+sn: Coltrane
+uid: rosco
+description: Fat tycoon
+
+dn: cn=Rosco P. Coltrane, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modrdn
+newrdn: cn=Rosco P. Coltrane
+deleteoldrdn: 1
+newsuperior: ou=Retired,ou=People,dc=example,dc=com
+
+dn: cn=James A Jones 2, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: delete
+
+dn: ou=testdomain1,dc=example,dc=com
+changetype: modrdn
+newrdn: ou=itsdomain1
+deleteoldrdn: 1
+
+dn: ou=itsdomain1,dc=example,dc=com
+changetype: modify
+replace: description
+description: Example, Inc. ITS test domain
+
+dn: ou=testdomain2,dc=example,dc=com
+changetype: modrdn
+newrdn: ou=itsdomain2
+deleteoldrdn: 1
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+## ldappasswd test removed, not supported on DSEE
+
+echo "Stopping consumer to test recovery..."
+kill -HUP $CONSUMERPID
+wait $CONSUMERPID
+
+echo "Modifying more entries on the provider..."
+$LDAPMODIFY -v -D "$DSEEDN" -H $URI1 -w $DSEEPW >> \
+ $TESTOUT 2>&1 << EOMODS
+dn: cn=Rosco P. Coltrane, ou=Retired, ou=People, dc=example,dc=com
+changetype: delete
+
+dn: cn=Bjorn Jensen, ou=Information Technology Division, ou=People, dc=example,dc=com
+changetype: modify
+add: carLicense
+carLicense: Mad Dog 20/20
+
+dn: cn=Rosco P. Coltrane,ou=Retired,ou=People,dc=example,dc=com
+changetype: add
+objectclass: inetOrgPerson
+sn: Coltrane
+uid: rosco
+cn: Rosco P. Coltrane
+
+dn: ou=itsdomain2,dc=example,dc=com
+changetype: modify
+replace: description
+description: Example, Inc. itsdomain2 test domain
+
+# rename with a newly added newSuperior while the consumer is down (ITS#6472)
+dn: ou=New Branch,dc=example,dc=com
+changetype: add
+objectClass: organizationalUnit
+ou: New Branch
+
+dn: cn=Dorothy Stevens, ou=Alumni Association, ou=People, dc=example,dc=com
+changetype: modrdn
+newrdn: cn=Dorothy Stevens
+deleteoldrdn: 0
+newsuperior: ou=New Branch,dc=example,dc=com
+
+EOMODS
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Restarting consumer..."
+echo "RESTART" >> $LOG4
+$SLAPD -f $CONF4 -h $URI4 -d $LVL >> $LOG4 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$PID $CONSUMERPID"
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
+ -D "$DSEEDN" -w "$DSEEPW" \
+ '(objectclass=*)' '*' $OPATTRS > $PROVIDEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -H $URI4 \
+ '(objectclass=*)' '*' $OPATTRS > $CONSUMEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Filtering provider results..."
+$LDIFFILTER -s a < $PROVIDEROUT > $PROVIDERFLT
+echo "Filtering consumer results..."
+$LDIFFILTER -s a < $CONSUMEROUT > $CONSUMERFLT
+
+echo "Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test076-authid-rewrite b/tests/scripts/test076-authid-rewrite
new file mode 100755
index 0000000..7799d88
--- /dev/null
+++ b/tests/scripts/test076-authid-rewrite
@@ -0,0 +1,640 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $WITH_SASL = no; then
+ echo "SASL authentication not available, test skipped"
+ exit 0
+fi
+
+CONFDIR=$TESTDIR/slapd.d
+MECH=DIGEST-MD5
+
+mkdir -p $TESTDIR $CONFDIR $DBDIR1
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+echo "Starting slapd on TCP/IP port $PORT1... $PWD"
+. $CONFFILTER $BACKEND < $DYNAMICCONF > $CONFLDIF
+$SLAPADD -F $CONFDIR -n 0 -l $CONFLDIF
+cd $TESTDIR
+$SLAPD -F ./slapd.d -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+cd $TESTWD
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Checking whether $MECH is supported..."
+$LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectClass=*' supportedSASLMechanisms > $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+grep "supportedSASLMechanisms: $MECH" $SEARCHOUT > $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "SASL mechanism $MECH is not available, test skipped"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 0
+fi
+
+echo "Adding schema and database..."
+$LDAPADD -H $URI1 -D cn=config -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+include: file://$ABS_SCHEMADIR/core.ldif
+
+include: file://$ABS_SCHEMADIR/cosine.ldif
+
+include: file://$ABS_SCHEMADIR/inetorgperson.ldif
+
+include: file://$ABS_SCHEMADIR/openldap.ldif
+
+include: file://$ABS_SCHEMADIR/nis.ldif
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for schema config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if [ "$BACKENDTYPE" = mod ]; then
+ $LDAPADD -H $URI1 -D cn=config -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/back-$BACKEND
+olcModuleLoad: back_$BACKEND.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for backend config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+$LDAPADD -H $URI1 -D cn=config -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+olcSuffix: $BASEDN
+olcDbDirectory: $DBDIR1
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if test $INDEXDB = indexdb ; then
+ $LDAPMODIFY -H $URI1 -D cn=config -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+changetype: modify
+add: olcDbIndex
+olcDbIndex: objectClass,entryUUID,entryCSN eq
+olcDbIndex: cn,uid pres,eq,sub
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed for index config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+echo "Using ldapadd to populate the database..."
+$LDAPADD -H $URI1 -D "$MANAGERDN" -w $PASSWD < $LDIFORDERED >>$TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo
+
+echo "Adding olcAuthzRegexp rule for static mapping..."
+$LDAPMODIFY -H $URI1 -D cn=config -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=config
+changetype: modify
+add: olcAuthzRegexp
+olcAuthzRegexp: uid=manager,cn=[^,]+,cn=auth $MANAGERDN
+EOF
+RC=$?
+if test $RC != 0; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=Manager
+echo "Testing ldapwhoami as $ID..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w $PASSWD
+RC=$?
+if test $RC != 0; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo
+
+echo "Adding olcAuthzRegexp rule to search by uid..."
+$LDAPMODIFY -H $URI1 -D cn=config -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=config
+changetype: modify
+add: olcAuthzRegexp
+olcAuthzRegexp: uid=([^,]+),cn=[^,]+,cn=auth ldap:///$BASEDN??sub?(uid=\$1)
+EOF
+RC=$?
+if test $RC != 0; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=Manager
+echo "Testing ldapwhoami as $ID..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w $PASSWD
+RC=$?
+if test $RC != 0; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=bjensen
+echo "Testing ldapwhoami as $ID..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w $ID
+RC=$?
+if test $RC != 0; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo
+
+echo "Inserting olcAuthzRegexp rule before the last..."
+$LDAPMODIFY -H $URI1 -D cn=config -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=config
+changetype: modify
+add: olcAuthzRegexp
+olcAuthzRegexp: {1}uid=babs,cn=[^,]+,cn=auth ldap:///$BASEDN??sub?(uid=bjensen)
+EOF
+RC=$?
+if test $RC != 0; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=Manager
+echo "Testing ldapwhoami as $ID..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w $PASSWD
+RC=$?
+if test $RC != 0; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=babs
+echo "Testing ldapwhoami as $ID..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w bjensen
+RC=$?
+if test $RC != 0; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=bjensen
+echo "Testing ldapwhoami as $ID..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w $ID
+RC=$?
+if test $RC != 0; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo
+
+echo "Deleting the first olcAuthzRegexp rule..."
+$LDAPMODIFY -H $URI1 -D cn=config -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=config
+changetype: modify
+delete: olcAuthzRegexp
+olcAuthzRegexp: {0}
+EOF
+RC=$?
+if test $RC != 0; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=Manager
+echo "Testing ldapwhoami as $ID (should fail)..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w $PASSWD
+RC=$?
+if test $RC != 49; then
+ echo "ldapwhoami unexpected result ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=babs
+echo "Testing ldapwhoami as $ID..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w bjensen
+RC=$?
+if test $RC != 0; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=bjensen
+echo "Testing ldapwhoami as $ID..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w $ID
+RC=$?
+if test $RC != 0; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo
+
+echo "Updating an olcAuthzRegexp rule in place..."
+$LDAPMODIFY -H $URI1 -D cn=config -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=config
+changetype: modify
+delete: olcAuthzRegexp
+olcAuthzRegexp: {0}
+-
+add: olcAuthzRegexp
+olcAuthzRegexp: {0}uid=biff,cn=[^,]+,cn=auth ldap:///$BASEDN??sub?(uid=bjorn)
+EOF
+RC=$?
+if test $RC != 0; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=babs
+echo "Testing ldapwhoami as $ID (should fail)..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w bjensen
+RC=$?
+if test $RC != 49; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=biff
+echo "Testing ldapwhoami as $ID..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w bjorn
+RC=$?
+if test $RC != 0; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=bjensen
+echo "Testing ldapwhoami as $ID..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w $ID
+RC=$?
+if test $RC != 0; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo
+
+echo "Deleting all olcAuthzRegexp rules..."
+$LDAPMODIFY -H $URI1 -D cn=config -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=config
+changetype: modify
+delete: olcAuthzRegexp
+EOF
+RC=$?
+if test $RC != 0; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=bjensen
+echo "Testing ldapwhoami as $ID (should fail)..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w $ID
+RC=$?
+if test $RC != 49; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo
+
+echo "Initializing olcAuthIDRewrite engine..."
+$LDAPMODIFY -H $URI1 -D cn=config -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=config
+changetype: modify
+add: olcAuthIDRewrite
+olcAuthIDRewrite: rewriteEngine ON
+olcAuthIDRewrite: rewriteContext authid
+EOF
+RC=$?
+if test $RC != 0; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo
+
+echo "Adding olcAuthIDRewrite rule for static mapping..."
+$LDAPMODIFY -H $URI1 -D cn=config -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=config
+changetype: modify
+add: olcAuthIDRewrite
+olcAuthIDRewrite: rewriteRule uid=manager,cn=[^,]+,cn=auth $MANAGERDN :
+EOF
+RC=$?
+if test $RC != 0; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=Manager
+echo "Testing ldapwhoami as $ID..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w $PASSWD
+RC=$?
+if test $RC != 0; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo
+
+echo "Adding olcAuthIDRewrite rule to search by uid..."
+$LDAPMODIFY -H $URI1 -D cn=config -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=config
+changetype: modify
+add: olcAuthIDRewrite
+olcAuthIDRewrite: rewriteRule uid=([^,]+),cn=[^,]+,cn=auth ldap:///$BASEDN??sub?(uid=\$1) :
+EOF
+RC=$?
+if test $RC != 0; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=Manager
+echo "Testing ldapwhoami as $ID..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w $PASSWD
+RC=$?
+if test $RC != 0; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=bjensen
+echo "Testing ldapwhoami as $ID..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w $ID
+RC=$?
+if test $RC != 0; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo
+
+echo "Inserting olcAuthIDRewrite rule before the last..."
+$LDAPMODIFY -H $URI1 -D cn=config -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=config
+changetype: modify
+add: olcAuthIDRewrite
+olcAuthIDRewrite: {3}rewriteRule uid=babs,cn=[^,]+,cn=auth ldap:///$BASEDN??sub?(uid=bjensen) :
+EOF
+RC=$?
+if test $RC != 0; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=Manager
+echo "Testing ldapwhoami as $ID..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w $PASSWD
+RC=$?
+if test $RC != 0; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=babs
+echo "Testing ldapwhoami as $ID..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w bjensen
+RC=$?
+if test $RC != 0; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=bjensen
+echo "Testing ldapwhoami as $ID..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w $ID
+RC=$?
+if test $RC != 0; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo
+
+echo "Deleting the first olcAuthIDRewrite rule..."
+$LDAPMODIFY -H $URI1 -D cn=config -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=config
+changetype: modify
+delete: olcAuthIDRewrite
+olcAuthIDRewrite: {2}
+EOF
+RC=$?
+if test $RC != 0; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=Manager
+echo "Testing ldapwhoami as $ID (should fail)..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w $PASSWD
+RC=$?
+if test $RC != 49; then
+ echo "ldapwhoami unexpected result ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=babs
+echo "Testing ldapwhoami as $ID..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w bjensen
+RC=$?
+if test $RC != 0; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=bjensen
+echo "Testing ldapwhoami as $ID..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w $ID
+RC=$?
+if test $RC != 0; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo
+
+echo "Updating an olcAuthIDRewrite rule in place..."
+$LDAPMODIFY -H $URI1 -D cn=config -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=config
+changetype: modify
+delete: olcAuthIDRewrite
+olcAuthIDRewrite: {2}
+-
+add: olcAuthIDRewrite
+olcAuthIDRewrite: {2}rewriteRule uid=biff,cn=[^,]+,cn=auth ldap:///$BASEDN??sub?(uid=bjorn) :
+EOF
+RC=$?
+if test $RC != 0; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=babs
+echo "Testing ldapwhoami as $ID (should fail)..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w bjensen
+RC=$?
+if test $RC != 49; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=biff
+echo "Testing ldapwhoami as $ID..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w bjorn
+RC=$?
+if test $RC != 0; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=bjensen
+echo "Testing ldapwhoami as $ID..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w $ID
+RC=$?
+if test $RC != 0; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo
+
+echo "Deleting all olcAuthIDRewrite rules..."
+$LDAPMODIFY -H $URI1 -D cn=config -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=config
+changetype: modify
+delete: olcAuthIDRewrite
+EOF
+RC=$?
+if test $RC != 0; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+ID=bjensen
+echo "Testing ldapwhoami as $ID (should fail)..."
+$LDAPSASLWHOAMI -H $URI1 -Y $MECH -U $ID -w $ID
+RC=$?
+if test $RC != 49; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test077-sasl-gssapi b/tests/scripts/test077-sasl-gssapi
new file mode 100755
index 0000000..4d4e260
--- /dev/null
+++ b/tests/scripts/test077-sasl-gssapi
@@ -0,0 +1,255 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $WITH_SASL = no ; then
+ echo "SASL support not available, test skipped"
+ exit 0
+fi
+
+CONFDIR=$TESTDIR/slapd.d
+CONFLDIF=$TESTDIR/slapd.ldif
+
+mkdir -p $TESTDIR $DBDIR1 $CONFDIR
+cp -r $DATADIR/tls $TESTDIR
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+echo "Starting KDC for SASL/GSSAPI tests..."
+. $SRCDIR/scripts/setup_kdc.sh
+
+echo "Configuring slapd..."
+cat > $CONFLDIF <<EOF
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcSaslHost: localhost
+olcSaslRealm: $KRB5REALM
+olcTLSCACertificateFile: $TESTDIR/tls/ca/certs/testsuiteCA.crt
+olcTLSCertificateFile: $TESTDIR/tls/certs/localhost.crt
+olcTLSCertificateKeyFile: $TESTDIR/tls/private/localhost.key
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file://$ABS_SCHEMADIR/core.ldif
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW:< file://$TESTDIR/configpw
+
+EOF
+$SLAPADD -F $CONFDIR -n 0 -l $CONFLDIF
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ kill $KDCPROC
+ exit $RC
+fi
+
+echo "Starting ldap:/// slapd on TCP/IP port $PORT1 and ldaps:/// slapd on $PORT2..."
+$SLAPD -F $CONFDIR -h "$URI1 $SURI2" -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ kill $KDCPROC
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -x -H $URI1 -s "base" -b "" supportedSASLMechanisms > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ kill $KDCPROC
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+grep GSSAPI $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "failed: GSSAPI mechanism not in supportedSASLMechanisms."
+ kill $KDCPROC
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo -n "Using ldapwhoami with SASL/GSSAPI: "
+$LDAPSASLWHOAMI -N -Y GSSAPI -H $URI1 > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ kill $KDCPROC
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+else
+ echo "success"
+fi
+
+echo -n "Validating mapped SASL/GSSAPI ID: "
+echo "dn:uid=$KUSER,cn=$KRB5REALM,cn=gssapi,cn=auth" > $TESTDIR/dn.out
+$CMP $TESTDIR/dn.out $TESTOUT > $CMPOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "Comparison failed"
+ kill $KDCPROC
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+else
+ echo "success"
+fi
+
+if test $WITH_TLS = no ; then
+ echo "SASL/GSSAPI: TLS support not available, skipping TLS part."
+else
+ echo -n "Using ldapwhoami with SASL/GSSAPI with start-tls: "
+ $LDAPSASLWHOAMI -N -Y GSSAPI -H $URI1 -ZZ -o tls_reqcert=allow \
+ -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt \
+ > $TESTOUT 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ kill $KDCPROC
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ else
+ echo "success"
+ fi
+
+ echo -n "Using ldapwhoami with SASL/GSSAPI with ldaps: "
+ $LDAPSASLWHOAMI -N -Y GSSAPI -H $SURI2 -o tls_reqcert=allow \
+ -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt \
+ > $TESTOUT 2>&1
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ kill $KDCPROC
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ else
+ echo "success"
+ fi
+fi
+
+if test $WITH_TLS = no ; then
+ echo "TLS support not available, skipping channel-binding test"
+elif test $HAVE_SASL_GSS_CBIND = no ; then
+ echo "SASL has no channel-binding support in GSSAPI, test skipped"
+else
+ echo "Testing SASL/GSSAPI with SASL_CBINDING..."
+
+ for acb in "none" "tls-unique" "tls-endpoint" ; do
+
+ echo "Modifying slapd's olcSaslCBinding to ${acb} ..."
+ $LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+dn: cn=config
+changetype: modify
+replace: olcSaslCBinding
+olcSaslCBinding: ${acb}
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ kill $KDCPROC
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ for icb in "none" "tls-unique" "tls-endpoint" ; do
+
+ # The gnutls implementation of "tls-unique" seems broken
+ if test $icb = "tls-unique" -o $acb = "tls-unique" ; then
+ if test $WITH_TLS_TYPE = gnutls ; then
+ continue
+ fi
+ fi
+
+ fail="no"
+ if test $icb != $acb -a $acb != "none" ; then
+ # This currently fails in MIT, but it is planned to be
+ # fixed not to fail like in heimdal - avoid testing.
+ if test $icb = "none" ; then
+ continue
+ fi
+ # Otherwise unmatching bindings are expected to fail.
+ fail="yes"
+ fi
+
+ echo -n "Using ldapwhoami with SASL/GSSAPI and SASL_CBINDING "
+ echo -n "(client: ${icb}, server: ${acb}): "
+
+ $LDAPSASLWHOAMI -N -Y GSSAPI -H $URI1 -ZZ -o tls_reqcert=allow \
+ -o tls_cacert=$TESTDIR/tls/ca/certs/testsuiteCA.crt \
+ -o SASL_CBINDING=$icb > $TESTOUT 2>&1
+
+ RC=$?
+ if test $RC != 0 ; then
+ if test $fail = "no" ; then
+ echo "test failed ($RC)!"
+ kill $KDCPROC
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+ elif test $fail = "yes" ; then
+ echo "failed: command succeeded unexpectedly."
+ kill $KDCPROC
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+ fi
+
+ echo "success"
+ RC=0
+ done
+ done
+fi
+
+
+kill $KDCPROC
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+if test $RC != 0 ; then
+ echo ">>>>> Test failed"
+else
+ echo ">>>>> Test succeeded"
+ RC=0
+fi
+
+test $KILLSERVERS != no && wait
+
+exit $RC
diff --git a/tests/scripts/test078-persistent-sessionlog b/tests/scripts/test078-persistent-sessionlog
new file mode 100755
index 0000000..acb8fad
--- /dev/null
+++ b/tests/scripts/test078-persistent-sessionlog
@@ -0,0 +1,646 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+if test $ACCESSLOG = accesslogno; then
+ echo "Accesslog overlay not available, test skipped"
+ exit 0
+fi
+
+MMR=2
+
+XDIR=$TESTDIR/srv
+TMP=$TESTDIR/tmp
+
+mkdir -p $TESTDIR
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+if test x"$SYNCMODE" = x ; then
+ SYNCMODE=rp
+fi
+case "$SYNCMODE" in
+ ro)
+ SYNCTYPE="type=refreshOnly interval=00:00:00:03"
+ ;;
+ rp)
+ SYNCTYPE="type=refreshAndPersist interval=00:00:00:03"
+ ;;
+ *)
+ echo "unknown sync mode $SYNCMODE"
+ exit 1;
+ ;;
+esac
+
+#
+# Test delta-sync mmr
+# - start servers
+# - configure over ldap
+# - populate over ldap
+# - configure syncrepl over ldap
+# - break replication
+# - modify each server separately
+# - restore replication
+# - compare results
+#
+
+nullExclude=""
+test $BACKEND = null && nullExclude="# "
+
+KILLPIDS=
+
+echo "Initializing server configurations..."
+n=1
+while [ $n -le $MMR ]; do
+
+DBDIR=${XDIR}$n/db
+CFDIR=${XDIR}$n/slapd.d
+
+mkdir -p ${XDIR}$n $DBDIR.1 $DBDIR.2 $CFDIR
+
+o=`expr 3 - $n`
+cat > $TMP <<EOF
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcServerID: $n
+
+EOF
+
+if [ "$SYNCPROV" = syncprovmod -o "$ACCESSLOG" = accesslogmod ]; then
+ cat <<EOF >> $TMP
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+EOF
+ if [ "$SYNCPROV" = syncprovmod ]; then
+ echo "olcModuleLoad: syncprov.la" >> $TMP
+ fi
+ if [ "$ACCESSLOG" = accesslogmod ]; then
+ echo "olcModuleLoad: accesslog.la" >> $TMP
+ fi
+ echo "" >> $TMP
+fi
+
+if [ "$BACKENDTYPE" = mod ]; then
+cat <<EOF >> $TMP
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/back-$BACKEND
+olcModuleLoad: back_$BACKEND.la
+
+EOF
+fi
+MYURI=`eval echo '$URI'$n`
+PROVIDERURI=`eval echo '$URI'$o`
+if test $INDEXDB = indexdb ; then
+INDEX1="olcDbIndex: objectClass,entryCSN,reqStart,reqDN,reqResult eq"
+INDEX2="olcDbIndex: objectClass,entryCSN,entryUUID eq"
+else
+INDEX1=
+INDEX2=
+fi
+cat >> $TMP <<EOF
+dn: cn=schema,cn=config
+objectclass: olcSchemaconfig
+cn: schema
+
+include: file://$ABS_SCHEMADIR/core.ldif
+
+include: file://$ABS_SCHEMADIR/cosine.ldif
+
+include: file://$ABS_SCHEMADIR/inetorgperson.ldif
+
+include: file://$ABS_SCHEMADIR/openldap.ldif
+
+include: file://$ABS_SCHEMADIR/nis.ldif
+
+dn: olcDatabase={0}config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW:< file://$CONFIGPWF
+
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+olcSuffix: cn=log
+${nullExclude}olcDbDirectory: ${DBDIR}.1
+olcRootDN: $MANAGERDN
+$INDEX1
+
+dn: olcOverlay=syncprov,olcDatabase={1}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+olcSpNoPresent: TRUE
+olcSpReloadHint: TRUE
+
+dn: olcDatabase={2}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {2}$BACKEND
+olcSuffix: $BASEDN
+${nullExclude}olcDbDirectory: ${DBDIR}.2
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+olcSyncRepl: rid=001 provider=$PROVIDERURI binddn="$MANAGERDN" bindmethod=simple
+ credentials=$PASSWD searchbase="$BASEDN" $SYNCTYPE retry="3 +" timeout=3
+olcMirrorMode: TRUE
+$INDEX2
+
+dn: olcOverlay=syncprov,olcDatabase={2}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+olcSpSessionlogSource: cn=log
+
+dn: olcOverlay=accesslog,olcDatabase={2}$BACKEND,cn=config
+objectClass: olcOverlayConfig
+objectClass: olcAccessLogConfig
+olcOverlay: accesslog
+olcAccessLogDB: cn=log
+olcAccessLogOps: writes
+olcAccessLogSuccess: TRUE
+
+EOF
+$SLAPADD -F $CFDIR -n 0 -d-1< $TMP > $TESTOUT 2>&1
+PORT=`eval echo '$PORT'$n`
+echo "Starting server $n on TCP/IP port $PORT..."
+cd ${XDIR}${n}
+LOG=`eval echo '$LOG'$n`
+$SLAPD -F slapd.d -h $MYURI -d $LVL > $LOG 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID $KILLPIDS"
+cd $TESTWD
+
+echo "Using ldapsearch to check that server $n is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $MYURI \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if [ $n = 1 ]; then
+echo "Using ldapadd for context on server 1..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD -f $LDIFORDEREDCP \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for server $n database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+fi
+
+n=`expr $n + 1`
+done
+
+echo "Using ldapadd to populate server 1..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD -f $LDIFORDEREDNOCP \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+n=1
+while [ $n -le $MMR ]; do
+URI=`eval echo '$URI'$n`
+
+echo "Using ldapsearch to read all the entries from server $n..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI -w $PASSWD \
+ 'objectclass=*' > $TESTDIR/server$n.out 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at server $n ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDIFFILTER < $TESTDIR/server$n.out > $TESTDIR/server$n.flt
+n=`expr $n + 1`
+done
+
+n=2
+while [ $n -le $MMR ]; do
+echo "Comparing retrieved entries from server 1 and server $n..."
+$CMP $PROVIDERFLT $TESTDIR/server$n.flt > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - server 1 and server $n databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+n=`expr $n + 1`
+done
+
+echo "Using ldapadd to populate server 2..."
+$LDAPADD -D "$MANAGERDN" -H $URI2 -w $PASSWD -f $LDIFADD1 \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for server 2 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+THEDN="cn=James A Jones 2,ou=Alumni Association,ou=People,dc=example,dc=com"
+sleep 1
+for i in 1 2 3; do
+ $LDAPSEARCH -S "" -b "$THEDN" -H $URI1 \
+ -s base '(objectClass=*)' entryCSN > "${PROVIDEROUT}.$i" 2>&1
+ RC=$?
+
+ if test $RC = 0 ; then
+ break
+ fi
+
+ if test $RC != 32 ; then
+ echo "ldapsearch failed at slave ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+done
+
+n=1
+while [ $n -le $MMR ]; do
+URI=`eval echo '$URI'$n`
+
+echo "Using ldapsearch to read all the entries from server $n..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI -w $PASSWD \
+ 'objectclass=*' > $TESTDIR/server$n.out 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at server $n ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDIFFILTER < $TESTDIR/server$n.out > $TESTDIR/server$n.flt
+n=`expr $n + 1`
+done
+
+n=2
+while [ $n -le $MMR ]; do
+echo "Comparing retrieved entries from server 1 and server $n..."
+$CMP $PROVIDERFLT $TESTDIR/server$n.flt > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - server 1 and server $n databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+n=`expr $n + 1`
+done
+
+echo "Retrieving syncrepl cookie..."
+cookie=`$LDAPRSEARCH -b "$BASEDN" -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ -E "sync=ro" 'objectclass=*' 1.1 | grep cookie | sed "s/.*cookie: //"`
+
+if test -z "$cookie"; then
+ echo "Failed to retrieve cookie from server!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Deleting an entry from server 1..."
+$LDAPDELETE -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ "cn=Mark Elliot,ou=Alumni Association,ou=People,$BASEDN" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapdelete failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+echo "Restarting servers..."
+kill -HUP $KILLPIDS
+wait
+KILLPIDS=""
+n=1
+while [ $n -le $MMR ]; do
+o=`expr 3 - $n`
+MYURI=`eval echo '$URI'$n`
+PROVIDERURI=`eval echo '$URI'$o`
+
+echo "Starting server $n again..."
+cd ${XDIR}${n}
+LOG=`eval echo '$LOG'$n`
+echo "RESTART" >> $LOG
+#if test $n = 2; then
+#echo $SLAPD -F slapd.d -h $MYURI -d $LVL
+#else
+$SLAPD -F slapd.d -h $MYURI -d $LVL > $LOG 2>&1 &
+#fi
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID $KILLPIDS"
+cd $TESTWD
+
+echo "Using ldapsearch to check that server $n is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $MYURI \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+echo "Breaking replication between server $n and $o..."
+$LDAPMODIFY -D cn=config -H $MYURI -y $CONFIGPWF > $TESTOUT 2>&1 <<EOF
+dn: olcDatabase={2}$BACKEND,cn=config
+changetype: modify
+replace: olcSyncRepl
+olcSyncRepl: rid=001 provider=$PROVIDERURI binddn="$MANAGERDN" bindmethod=simple
+ credentials=InvalidPw searchbase="$BASEDN" $SYNCTYPE retry="3 +" timeout=3
+-
+replace: olcMirrorMode
+olcMirrorMode: TRUE
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server $n config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+n=`expr $n + 1`
+done
+
+echo "Using ldapmodify to force conflicts between server 1 and 2..."
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+add: description
+description: Amazing
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI2 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+add: description
+description: Stupendous
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 2 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+delete: description
+description: Outstanding
+-
+add: description
+description: Mindboggling
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI2 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+delete: description
+description: OutStanding
+-
+add: description
+description: Bizarre
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 2 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+add: carLicense
+carLicense: 123-XYZ
+-
+add: employeeNumber
+employeeNumber: 32
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI2 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+add: employeeType
+employeeType: deadwood
+-
+add: employeeNumber
+employeeNumber: 64
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 2 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 << EOF
+dn: $THEDN
+changetype: modify
+replace: sn
+sn: Replaced later
+-
+replace: sn
+sn: Surname
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Deleting an entry from both servers..."
+$LDAPDELETE -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ "cn=John Doe,ou=Information Technology Division,ou=People,$BASEDN" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapdelete failed for server 1 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPDELETE -D "$MANAGERDN" -H $URI2 -w $PASSWD \
+ "cn=John Doe,ou=Information Technology Division,ou=People,$BASEDN" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapdelete failed for server 2 database ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Restoring replication between server 1 and 2..."
+n=1
+while [ $n -le $MMR ]; do
+o=`expr 3 - $n`
+MYURI=`eval echo '$URI'$n`
+PROVIDERURI=`eval echo '$URI'$o`
+$LDAPMODIFY -D cn=config -H $MYURI -y $CONFIGPWF > $TESTOUT 2>&1 <<EOF
+dn: olcDatabase={2}$BACKEND,cn=config
+changetype: modify
+replace: olcSyncRepl
+olcSyncRepl: rid=001 provider=$PROVIDERURI binddn="$MANAGERDN" bindmethod=simple
+ credentials=$PASSWD searchbase="$BASEDN" $SYNCTYPE retry="3 +" timeout=3
+-
+replace: olcMirrorMode
+olcMirrorMode: TRUE
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for server $n config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+n=`expr $n + 1`
+done
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo 2 >$TESTDIR/repl.test
+echo 1 >>$TESTDIR/repl.test
+
+n=1
+while [ $n -le $MMR ]; do
+URI=`eval echo '$URI'$n`
+
+echo "Using ldapsearch to read all the entries from server $n..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI -w $PASSWD \
+ 'objectclass=*' > $TESTDIR/server$n.out 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at server $n ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+$LDIFFILTER -s a < $TESTDIR/server$n.out > $TESTDIR/server$n.flt
+
+echo "Checking server $n can remember which entries have been deleted even after it's been restarted..."
+$LDAPRSEARCH -b "$BASEDN" -D "$MANAGERDN" -H $URI -w $PASSWD \
+ -E "sync=ro/$cookie" 'objectclass=*' 1.1 | awk '/syncUUIDs/ {count++} END {print count}' >$TESTDIR/repl.out
+$LDAPRSEARCH -b "$BASEDN" -D "$MANAGERDN" -H $URI -w $PASSWD \
+ -E "sync=ro/$cookie" 'objectclass=*' 1.1 | grep SyncDone | awk '/refreshDeletes=1/ {count++} END {print count}' >>$TESTDIR/repl.out
+
+$CMP $TESTDIR/repl.out $TESTDIR/repl.test > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - server did not respond with delete phase"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+n=`expr $n + 1`
+done
+
+n=2
+while [ $n -le $MMR ]; do
+echo "Comparing retrieved entries from server 1 and server $n..."
+$CMP $PROVIDERFLT $TESTDIR/server$n.flt > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - server 1 and server $n databases differ"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+n=`expr $n + 1`
+done
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test079-proxy-timeout b/tests/scripts/test079-proxy-timeout
new file mode 100755
index 0000000..6a8e0c7
--- /dev/null
+++ b/tests/scripts/test079-proxy-timeout
@@ -0,0 +1,374 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $BACKLDAP = "ldapno" ; then
+ echo "LDAP backend not available, test skipped"
+ exit 0
+fi
+if test $RWM = "rwmno" ; then
+ echo "rwm (rewrite/remap) overlay not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+#
+# Start slapd that acts as a remote LDAP server that will be proxied
+#
+echo "Running slapadd to build database for the remote slapd server..."
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+$SLAPADD -f $CONF1 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting remote slapd server on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+SERVERPID=$!
+if test $WAIT != 0 ; then
+ echo SERVERPID $SERVERPID
+ read foo
+fi
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+#
+# Start ldapd that will proxy for the remote server
+#
+# Proxy is configured with two slapd-ldap backends:
+# - one with idle timeout set: dc=idle-timeout,$BASED
+# - one with connection TTL set: dc=conn-ttl,$BASEDN
+#
+echo "Starting slapd proxy on TCP/IP port $PORT2..."
+. $CONFFILTER $BACKEND < $DATADIR/slapd-proxytimeout.conf > $CONF2
+$SLAPD -f $CONF2 -h $URI2 -d $LVL > $LOG2 2>&1 &
+PROXYPID=$!
+if test $WAIT != 0 ; then
+ echo PROXYPID $PROXYPID
+ read foo
+fi
+
+KILLPIDS="$SERVERPID $PROXYPID"
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for slapd to start..."
+ sleep $SLEEP1
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+##############################################################################
+#
+# Test 1: Test that shared connections are timed out
+#
+
+CONN_BEGINS=`date +%s`
+CONN_EXPIRES=`expr $CONN_BEGINS + $TIMEOUT`
+echo "Create shared connection towards remote LDAP (time_t now=$CONN_BEGINS timeout=$CONN_EXPIRES)"
+
+$LDAPSEARCH -b "dc=idle-timeout,$BASEDN" \
+ -D "cn=Manager,dc=local,dc=com" \
+ -H $URI2 \
+ -w $PASSWD \
+ 'objectclass=*' > $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed for base: dc=idle-timeout,$BASEDN ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -b "dc=conn-ttl,$BASEDN" \
+ -D "cn=Manager,dc=local,dc=com" \
+ -H $URI2 \
+ -w $PASSWD \
+ 'objectclass=*' >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed for base: dc=conn-ttl,$BASEDN ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Check that connections are established by searching for olmDbConnURI from Monitor
+
+echo "Checking that proxy has created connections towards backend (time_t now=`date +%s`)"
+
+$LDAPSEARCH -b "cn=Connections,cn=database 2,cn=databases,cn=monitor" -s one -LLL olmDbConnURI \
+ -D "cn=Manager,dc=local,dc=com" \
+ -H $URI2 \
+ -w $PASSWD 2>&1 | tee -a $TESTOUT | grep ldap://${LOCALHOST}:$PORT1 >/dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "Error: LDAP connection to remote LDAP server is not found ($RC)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -b "cn=Connections,cn=database 3,cn=databases,cn=monitor" -s one -LLL olmDbConnURI \
+ -D "cn=Manager,dc=local,dc=com" \
+ -H $URI2 \
+ -w $PASSWD 2>&1 | tee -a $TESTOUT | grep ldap://${LOCALHOST}:$PORT1 >/dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "Error: LDAP connection to remote LDAP server is not found ($RC)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Wait for connections to be closed, either due to
+# - idle-timeout and
+# - conn-ttl
+# sleep 2 second overtime for robustness of the test case
+echo "Sleeping until idle-timeout and conn-ttl have passed"
+NOW=`date +%s`
+sleep `expr $CONN_EXPIRES - $NOW + 2`
+
+echo "Checking that proxy has closed expired connections towards the remote LDAP server (time_t now=`date +%s`)"
+
+$LDAPSEARCH -b "cn=Connections,cn=database 2,cn=databases,cn=monitor" -s one -LLL olmDbConnURI \
+ -D "cn=Manager,dc=local,dc=com" \
+ -H $URI2 \
+ -w $PASSWD 2>&1 | tee -a $TESTOUT | grep ldap://${LOCALHOST}:$PORT1 >/dev/null
+RC=$?
+if test $RC != 1 ; then
+ echo "Error: LDAP connection to remote LDAP server was not closed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -b "cn=Connections,cn=database 3,cn=databases,cn=monitor" -s one -LLL olmDbConnURI \
+ -D "cn=Manager,dc=local,dc=com" \
+ -H $URI2 \
+ -w $PASSWD 2>&1 | tee -a $TESTOUT | grep ldap://${LOCALHOST}:$PORT1 >/dev/null
+RC=$?
+if test $RC != 1 ; then
+ echo "Error: LDAP connection to remote LDAP server was not closed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+##############################################################################
+#
+# Test 2: Test that private connections are timed out
+#
+
+CONN_BEGINS=`date +%s`
+CONN_EXPIRES=`expr $CONN_BEGINS + $TIMEOUT`
+echo "Create private connection towards remote LDAP (time_t now=$CONN_BEGINS timeout=$CONN_EXPIRES)"
+
+# Create fifos that are used to pass searches from the test case to ldapsearch
+rm -f $TESTDIR/ldapsearch1.fifo $TESTDIR/ldapsearch2.fifo
+mkfifo $TESTDIR/ldapsearch1.fifo $TESTDIR/ldapsearch2.fifo
+
+# Execute ldapsearch on background and have it read searches from the fifo
+$LDAPSEARCH -b "dc=idle-timeout,$BASEDN" \
+ -D "cn=Barbara Jensen,ou=Information Technology Division,dc=idle-timeout,$BASEDN" \
+ -H $URI2 \
+ -w "bjensen" \
+ -f $TESTDIR/ldapsearch1.fifo >> $TESTOUT 2>&1 &
+LDAPSEARCHPIDS=$!
+
+$LDAPSEARCH -b "dc=conn-ttl,$BASEDN" \
+ -D "cn=Barbara Jensen,ou=Information Technology Division,dc=conn-ttl,$BASEDN" \
+ -H $URI2 \
+ -w "bjensen" \
+ -f $TESTDIR/ldapsearch2.fifo >> $TESTOUT 2>&1 &
+LDAPSEARCHPIDS="$LDAPSEARCHPIDS $!"
+
+# Open fifos as file descriptor
+exec 3>$TESTDIR/ldapsearch1.fifo
+exec 4>$TESTDIR/ldapsearch2.fifo
+
+# Trigger LDAP connections towards the proxy by executing a search
+echo 'objectclass=*' >&3
+echo 'objectclass=*' >&4
+
+# wait for ldapsearches (running as background processes) to execute search operations
+sleep 2
+
+echo "Checking that proxy has created connections towards backend (time_t now=`date +%s`)"
+
+$LDAPSEARCH -b "cn=Connections,cn=database 2,cn=databases,cn=monitor" -s one -LLL olmDbConnURI \
+ -D "cn=Manager,dc=local,dc=com" \
+ -H $URI2 \
+ -w $PASSWD 2>&1 | tee -a $TESTOUT | grep ldap://${LOCALHOST}:$PORT1 >/dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "Error: LDAP connection to remote LDAP server is not found ($RC)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS $LDAPSEARCHPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -b "cn=Connections,cn=database 3,cn=databases,cn=monitor" -s one -LLL olmDbConnURI \
+ -D "cn=Manager,dc=local,dc=com" \
+ -H $URI2 \
+ -w $PASSWD 2>&1 | tee -a $TESTOUT | grep ldap://${LOCALHOST}:$PORT1 >/dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "Error: LDAP connection to remote LDAP server is not found ($RC)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS $LDAPSEARCHPIDS
+ exit $RC
+fi
+
+# Wait for connections to be closed, either due to
+# - idle-timeout and
+# - conn-ttl
+# sleep 2 second overtime for robustness of the test case
+echo "Sleeping until idle-timeout and conn-ttl have passed"
+NOW=`date +%s`
+sleep `expr $CONN_EXPIRES - $NOW + 2`
+
+echo "Checking that proxy has closed expired connections towards the remote LDAP server (time_t now=`date +%s`)"
+
+$LDAPSEARCH -b "cn=Connections,cn=database 2,cn=databases,cn=monitor" -s one -LLL olmDbConnURI \
+ -D "cn=Manager,dc=local,dc=com" \
+ -H $URI2 \
+ -w $PASSWD 2>&1 | tee -a $TESTOUT | grep ldap://${LOCALHOST}:$PORT1 >/dev/null
+RC=$?
+if test $RC != 1 ; then
+ echo "Error: LDAP connection to remote LDAP server was not closed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS $LDAPSEARCHPIDS
+ exit $RC
+fi
+
+$LDAPSEARCH -b "cn=Connections,cn=database 3,cn=databases,cn=monitor" -s one -LLL olmDbConnURI \
+ -D "cn=Manager,dc=local,dc=com" \
+ -H $URI2 \
+ -w $PASSWD 2>&1 | tee -a $TESTOUT | grep ldap://${LOCALHOST}:$PORT1 >/dev/null
+RC=$?
+if test $RC != 1 ; then
+ echo "Error: LDAP connection to remote LDAP server was not closed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS $LDAPSEARCHPIDS
+ exit $RC
+fi
+
+# Close the file descriptors associated with the fifos.
+# This will trigger EOF to ldapsearch which will cause it to exit.
+exec 3>&-
+exec 4>&-
+
+
+##############################################################################
+#
+# Test 3: Check that idle-timeout is reset on activity
+#
+
+echo "Checking that idle-timeout is reset on activity"
+CONN_BEGINS=`date +%s`
+CONN_EXPIRES=`expr $CONN_BEGINS + $TIMEOUT`
+echo "Create cached connection: idle-timeout timeout starts (time_t now=$CONN_BEGINS, original_timeout=$CONN_EXPIRES)"
+$LDAPSEARCH -b "dc=idle-timeout,$BASEDN" \
+ -D "cn=Manager,dc=local,dc=com" \
+ -H $URI2 \
+ -w $PASSWD \
+ 'objectclass=*' >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed for base: dc=idle-timeout,$BASEDN ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# sleep until 2 seconds before idle-timeout, then extend the timeout by executing another search operation
+NOW=`date +%s`
+sleep `expr $CONN_EXPIRES - $NOW - 2`
+
+CONN_BEGINS=`date +%s`
+CONN_EXPIRES=`expr $CONN_BEGINS + $TIMEOUT`
+echo "Do another search to reset the timeout (time_t now=$CONN_BEGINS, new_timeout=$CONN_EXPIRES)"
+$LDAPSEARCH -b "dc=idle-timeout,$BASEDN" \
+ -D "cn=Manager,dc=local,dc=com" \
+ -H $URI2 \
+ -w $PASSWD \
+ 'objectclass=*' >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed for base: dc=idle-timeout,$BASEDN ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# sleep until 2 seconds before new extended idle-timeout, check that connection still exist
+NOW=`date +%s`
+sleep `expr $CONN_EXPIRES - $NOW - 2`
+echo "Check that connection is still alive due to idle-timeout reset (time_t now=`date +%s`)"
+$LDAPSEARCH -b "cn=Connections,cn=database 2,cn=databases,cn=monitor" -s one -LLL olmDbConnURI \
+ -D "cn=Manager,dc=local,dc=com" \
+ -H $URI2 \
+ -w $PASSWD 2>&1 | tee -a $TESTOUT | grep ldap://${LOCALHOST}:$PORT1 >/dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "Error: LDAP connection to remote LDAP server is not found ($RC)"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# sleep until 2 seconds after timeout, check that connection does not exist
+NOW=`date +%s`
+sleep `expr $CONN_EXPIRES - $NOW + 2`
+echo "Check that connection is closed after extended idle-timeout has passed (time_t now=`date +%s`)"
+$LDAPSEARCH -b "cn=Connections,cn=database 2,cn=databases,cn=monitor" -s one -LLL olmDbConnURI \
+ -D "cn=Manager,dc=local,dc=com" \
+ -H $URI2 \
+ -w $PASSWD 2>&1 | tee -a $TESTOUT | grep ldap://${LOCALHOST}:$PORT1 >/dev/null
+RC=$?
+if test $RC != 1 ; then
+ echo "Error: LDAP connection to remote LDAP server was not closed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test080-hotp b/tests/scripts/test080-hotp
new file mode 100755
index 0000000..5bfd14a
--- /dev/null
+++ b/tests/scripts/test080-hotp
@@ -0,0 +1,295 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2021 Ondřej Kuzník, Symas Corp.
+## Copyright 2021-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $OTP = otpno; then
+ echo "OTP overlay not available, test skipped"
+ exit 0
+fi
+
+OTP_DATA=$DATADIR/otp/hotp.ldif
+
+# OTPs for this token
+TOKEN_0=818800
+TOKEN_1=320382
+TOKEN_2=404533
+TOKEN_3=127122
+TOKEN_4=892599
+TOKEN_5=407030
+TOKEN_6=880935
+TOKEN_7=920291
+TOKEN_8=145192
+TOKEN_9=316404
+TOKEN_10=409144
+
+# OTPs for the second set of parameters
+TOKEN_SHA512_11=17544155
+TOKEN_SHA512_12=48953477
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $CONF > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+mkdir $TESTDIR/confdir
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "database config" >>$CONF1
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >>$CONF1
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -F $TESTDIR/confdir -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+if [ "$OTP" = otpmod ]; then
+$LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+olcModuleLoad: otp.la
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+fi
+
+echo "Loading test otp configuration..."
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: olcOverlay={0}otp,olcDatabase={1}$BACKEND,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Provisioning tokens and configuration..."
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 < $OTP_DATA
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+
+echo "Authentication tests:"
+echo "\ttoken that's not valid yet..."
+$LDAPWHOAMI -D "$BABSDN" -H $URI1 -w "bjensen$TOKEN_10" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 49 ; then
+ echo "ldapwhoami should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "\ta valid and expected token..."
+$LDAPWHOAMI -D "$BABSDN" -H $URI1 -w "bjensen$TOKEN_4" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "\ta valid token skipping some..."
+$LDAPWHOAMI -D "$BABSDN" -H $URI1 -w "bjensen$TOKEN_6" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "\treusing the same token..."
+$LDAPWHOAMI -D "$BABSDN" -H $URI1 -w "bjensen$TOKEN_6" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 49 ; then
+ echo "ldapwhoami should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "\tanother account sharing the same token..."
+$LDAPWHOAMI -D "$BJORNSDN" -H $URI1 -w "bjorn$TOKEN_7" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "\ttrying an old token..."
+$LDAPWHOAMI -D "$BJORNSDN" -H $URI1 -w "bjorn$TOKEN_5" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 49 ; then
+ echo "ldapwhoami should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "\tright token, wrong password..."
+$LDAPWHOAMI -D "$BJORNSDN" -H $URI1 -w "bjensen$TOKEN_8" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 49 ; then
+ echo "ldapwhoami should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "\tmaking sure previous token has been retired too..."
+$LDAPWHOAMI -D "$BJORNSDN" -H $URI1 -w "bjorn$TOKEN_8" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 49 ; then
+ echo "ldapwhoami should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "\tthe first token we tested that's just become valid..."
+$LDAPWHOAMI -D "$BABSDN" -H $URI1 -w "bjensen$TOKEN_10" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Reconfiguring token parameters..."
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >/dev/null 2>&1 << EOMODS
+dn: ou=Information Technology Division,ou=People,dc=example,dc=com
+changetype: modify
+replace: oathHOTPParams
+oathHOTPParams: ou=Alumni Association,ou=People,dc=example,dc=com
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "A new round of tests:"
+
+echo "\ta long token that's not valid yet..."
+$LDAPWHOAMI -D "$BABSDN" -H $URI1 -w "bjensen$TOKEN_SHA512_12" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 49 ; then
+ echo "ldapwhoami should have failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "\ta valid and expected token..."
+$LDAPWHOAMI -D "$BABSDN" -H $URI1 -w "bjensen$TOKEN_SHA512_11" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "\tthe previous long token that's just become valid..."
+$LDAPWHOAMI -D "$BABSDN" -H $URI1 -w "bjensen$TOKEN_SHA512_12" \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Retrieving token status..."
+$LDAPSEARCH -b "ou=Information Technology Division,ou=People,dc=example,dc=com" \
+ -H $URI1 objectclass=oathHOTPToken '@oathHOTPToken' \
+ >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=$DATADIR/otp/test001-out.ldif
+
+echo "Filtering ldapsearch results..."
+$LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering ldif with expected data..."
+$LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test081-totp b/tests/scripts/test081-totp
new file mode 100755
index 0000000..2c7a21c
--- /dev/null
+++ b/tests/scripts/test081-totp
@@ -0,0 +1,143 @@
+#!/bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2021 Ondřej Kuzník, Symas Corp.
+## Copyright 2021-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $OTP = otpno; then
+ echo "OTP overlay not available, test skipped"
+ exit 0
+fi
+
+for python in python3 python2 python2.7 python27 python ""; do
+ if test x"$python" = x; then
+ echo "Useable Python environment not found, skipping test"
+ exit 0
+ fi
+
+ "$python" "$0".py --check >>$TESTOUT 2>&1
+ RC=$?
+ case $RC in
+ 0)
+ break;;
+ 1)
+ echo "$python is missing some required modules, skipping"
+ python=""
+ continue;;
+ 127)
+ ;;
+ esac
+done
+
+export URI1 MANAGERDN PASSWD BABSDN BJORNSDN
+
+OTP_DATA=$DATADIR/otp/totp.ldif
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $CONF > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+mkdir $TESTDIR/confdir
+. $CONFFILTER $BACKEND < $CONF > $CONF1
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "database config" >>$CONF1
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >>$CONF1
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -F $TESTDIR/confdir -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+if [ "$OTP" = otpmod ]; then
+$LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+olcModuleLoad: otp.la
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+fi
+
+echo "Loading test otp configuration..."
+$LDAPMODIFY -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: olcOverlay={0}otp,olcDatabase={1}$BACKEND,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Provisioning tokens and configuration..."
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ >> $TESTOUT 2>&1 < $OTP_DATA
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+"$python" "$0".py
+RC=$?
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+if test $RC != 0 ; then
+ echo "Test failed ($RC)!"
+else
+ echo ">>>>> Test succeeded"
+fi
+
+test $KILLSERVERS != no && wait
+
+exit $RC
diff --git a/tests/scripts/test081-totp.py b/tests/scripts/test081-totp.py
new file mode 100755
index 0000000..aeedaf2
--- /dev/null
+++ b/tests/scripts/test081-totp.py
@@ -0,0 +1,182 @@
+# -*- coding: utf-8 -*-
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2021 Ondřej Kuzník, Symas Corp.
+## Copyright 2021-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+from __future__ import print_function
+
+import hashlib
+import hmac
+import os
+import struct
+import sys
+import time
+
+import ldap
+from ldap.cidict import cidict as CIDict
+from ldap.ldapobject import LDAPObject
+
+if len(sys.argv) > 1 and sys.argv[1] == "--check":
+ raise SystemExit(0)
+
+
+def get_digits(h, digits):
+ offset = h[19] & 15
+ number = struct.unpack(">I", h[offset:offset+4])[0] & 0x7fffffff
+ number %= (10 ** digits)
+ return ("%0*d" % (digits, number)).encode()
+
+
+def get_hotp_token(secret, interval_no):
+ msg = struct.pack(">Q", interval_no)
+ h = hmac.new(secret, msg, hashlib.sha1).digest()
+ return get_digits(bytearray(h), 6)
+
+
+def get_interval(period=30):
+ return int(time.time() // period)
+
+
+def get_token_for(connection, dn, typ="totp"):
+ result = connection.search_s(dn, ldap.SCOPE_BASE)
+ dn, attrs = result[0]
+ attrs = CIDict(attrs)
+
+ tokendn = attrs['oath'+typ+'token'][0].decode()
+
+ result = connection.search_s(tokendn, ldap.SCOPE_BASE)
+ dn, attrs = result[0]
+ attrs = CIDict(attrs)
+
+ return dn, attrs
+
+
+def main():
+ uri = os.environ["URI1"]
+
+ managerdn = os.environ['MANAGERDN']
+ passwd = os.environ['PASSWD']
+
+ babsdn = os.environ['BABSDN']
+ babspw = b"bjensen"
+
+ bjornsdn = os.environ['BJORNSDN']
+ bjornspw = b"bjorn"
+
+ connection = LDAPObject(uri)
+
+ start = time.time()
+ connection.bind_s(managerdn, passwd)
+ end = time.time()
+
+ if end - start > 1:
+ print("It takes more than a second to connect and bind, "
+ "skipping potentially unstable test", file=sys.stderr)
+ raise SystemExit(0)
+
+ dn, token_entry = get_token_for(connection, babsdn)
+
+ paramsdn = token_entry['oathTOTPParams'][0].decode()
+ result = connection.search_s(paramsdn, ldap.SCOPE_BASE)
+ _, attrs = result[0]
+ params = CIDict(attrs)
+
+ secret = token_entry['oathSecret'][0]
+ period = int(params['oathTOTPTimeStepPeriod'][0].decode())
+
+ bind_conn = LDAPObject(uri)
+
+ interval_no = get_interval(period)
+ token = get_hotp_token(secret, interval_no-3)
+
+ print("Testing old tokens are not useable")
+ bind_conn.bind_s(babsdn, babspw+token)
+ try:
+ bind_conn.bind_s(babsdn, babspw+token)
+ except ldap.INVALID_CREDENTIALS:
+ pass
+ else:
+ raise SystemExit("Bind with an old token should have failed")
+
+ interval_no = get_interval(period)
+ token = get_hotp_token(secret, interval_no)
+
+ print("Testing token can only be used once")
+ bind_conn.bind_s(babsdn, babspw+token)
+ try:
+ bind_conn.bind_s(babsdn, babspw+token)
+ except ldap.INVALID_CREDENTIALS:
+ pass
+ else:
+ raise SystemExit("Bind with a reused token should have failed")
+
+ token = get_hotp_token(secret, interval_no+1)
+ try:
+ bind_conn.bind_s(babsdn, babspw+token)
+ except ldap.INVALID_CREDENTIALS:
+ raise SystemExit("Bind should have succeeded")
+
+ dn, token_entry = get_token_for(connection, babsdn)
+ last = int(token_entry['oathTOTPLastTimeStep'][0].decode())
+ if last != interval_no+1:
+ SystemExit("Unexpected counter value %d (expected %d)" %
+ (last, interval_no+1))
+
+ print("Resetting counter and testing secret sharing between accounts")
+ connection.modify_s(dn, [(ldap.MOD_REPLACE, 'oathTOTPLastTimeStep', [])])
+
+ interval_no = get_interval(period)
+ token = get_hotp_token(secret, interval_no)
+
+ try:
+ bind_conn.bind_s(bjornsdn, bjornspw+token)
+ except ldap.INVALID_CREDENTIALS:
+ raise SystemExit("Bind should have succeeded")
+
+ try:
+ bind_conn.bind_s(babsdn, babspw+token)
+ except ldap.INVALID_CREDENTIALS:
+ pass
+ else:
+ raise SystemExit("Bind with a reused token should have failed")
+
+ print("Testing token is retired even with a wrong password")
+ connection.modify_s(dn, [(ldap.MOD_REPLACE, 'oathTOTPLastTimeStep', [])])
+
+ interval_no = get_interval(period)
+ token = get_hotp_token(secret, interval_no)
+
+ try:
+ bind_conn.bind_s(babsdn, b"not the password"+token)
+ except ldap.INVALID_CREDENTIALS:
+ pass
+ else:
+ raise SystemExit("Bind with an incorrect password should have failed")
+
+ try:
+ bind_conn.bind_s(babsdn, babspw+token)
+ except ldap.INVALID_CREDENTIALS:
+ pass
+ else:
+ raise SystemExit("Bind with a reused token should have failed")
+
+ token = get_hotp_token(secret, interval_no+1)
+ try:
+ bind_conn.bind_s(babsdn, babspw+token)
+ except ldap.INVALID_CREDENTIALS:
+ raise SystemExit("Bind should have succeeded")
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/tests/scripts/test082-remoteauth b/tests/scripts/test082-remoteauth
new file mode 100755
index 0000000..d3e0ba1
--- /dev/null
+++ b/tests/scripts/test082-remoteauth
@@ -0,0 +1,417 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2016-2021 Ondřej Kuzník, Symas Corp.
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $WITH_TLS = no ; then
+ echo "TLS support not available, test skipped"
+ exit 0
+fi
+
+if test $REMOTEAUTH = remoteauthno; then
+ echo "RemoteAuth overlay not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $DBDIR2 $TESTDIR/confdir
+cp -r $DATADIR/tls $TESTDIR
+
+. $CONFFILTER < $DATADIR/remoteauth/default_domain > $TESTDIR/default_domain
+
+. $CONFFILTER $BACKEND < $TLSCONF > $CONF1
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "database config" >>$CONF1
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >>$CONF1
+echo "TLSCACertificateFile $TESTDIR/tls/ca/certs/testsuiteCA.crt" >>$CONF1
+
+$SLAPD -Tt -n 0 -f $CONF1 -F $TESTDIR/confdir -d $LVL > $LOG1 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "slaptest failed ($RC)!"
+ exit $RC
+fi
+
+echo -n "Running slapadd to build slapd database... "
+$SLAPADD -F $TESTDIR/confdir -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "DB tweaks..."
+$SLAPMODIFY -F $TESTDIR/confdir >>$LOG1 2>&1 <<EOMODS
+dn: $MELLIOTDN
+changetype: modify
+add: o
+o: self
+-
+replace: seeAlso
+seeAlso: $BJORNSDN
+
+dn: $JOHNDDN
+changetype: modify
+replace: seeAlso
+seeAlso: $BJORNSDN
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "slapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1 for configuration..."
+$SLAPD -F $TESTDIR/confdir -h $URI1 -d $LVL >> $LOG1 2>&1 &
+REMOTEAUTH_PID=$!
+if test $WAIT != 0 ; then
+ echo REMOTEAUTH_PID $REMOTEAUTH_PID
+ read foo
+fi
+KILLPIDS="$REMOTEAUTH_PID"
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+if [ "$REMOTEAUTH" = remoteauthmod ]; then
+$LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1 <<EOMOD
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/overlays
+olcModuleLoad: remoteauth.la
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+fi
+
+echo "Loading test remoteauth configuration..."
+. $CONFFILTER $BACKEND < $DATADIR/remoteauth/config.ldif | \
+$LDAPADD -v -D cn=config -H $URI1 -y $CONFIGPWF \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo -n "Preparing second server on $URI2 and $SURIP3... "
+. $CONFFILTER $BACKEND < $TLSCONF | sed -e "s,$DBDIR1,$DBDIR2," > $CONF2
+
+echo -n "loading data... "
+$SLAPADD -f $CONF2 -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo -n "tweaking DB contents... "
+$SLAPMODIFY -f $CONF2 >>$LOG2 2>&1 <<EOMODS
+dn: $BJORNSDN
+changetype: modify
+replace: userPassword
+userPassword: bjorn2
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+ echo "slapmodify failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "starting up... "
+$SLAPD -f $CONF2 -h "$URI2 $SURIP3" -d $LVL > $LOG2 2>&1 &
+BACKEND_PID=$!
+if test $WAIT != 0 ; then
+ echo BACKEND_PID $BACKEND_PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $BACKEND_PID"
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+if test $RC != 0 ; then
+ echo "failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+. $CONFFILTER $BACKEND < $TLSCONF > $CONF1
+
+echo "TLSCACertificateFile $TESTDIR/tls/ca/certs/testsuiteCA.crt" >>$CONF1
+echo "database config" >>$CONF1
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >>$CONF1
+
+# We check basic remoteauth operation and generated configuration in these
+# circumstances:
+# 1. configured online through cn=config (what we set up above)
+# 2. the server from 1. restarted (loading from cn=config on startup)
+# 3. configured and started through a slapd.conf
+#
+# All of the above should present the same behaviour and cn=config output
+
+echo "Saving generated config before server restart..."
+echo "# search output from dynamically configured server..." >> $SERVER1OUT
+$LDAPSEARCH -D cn=config -H $URI1 -y $CONFIGPWF \
+ -b "olcOverlay={0}remoteauth,olcDatabase={1}$BACKEND,cn=config" \
+ >> $SERVER1OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo -n "Checking bind handling... "
+
+$LDAPWHOAMI -H $URI1 -x -D "$BJORNSDN" -w bjorn >/dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+echo -n "1 "
+
+$LDAPWHOAMI -H $URI1 -x -D "$JOHNDDN" -w bjorn2 >/dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+echo -n "2 "
+
+$LDAPWHOAMI -H $URI1 -x -D "$MELLIOTDN" -w bjorn >/dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+echo -n "3 "
+
+echo "ok"
+
+echo "Stopping slapd on TCP/IP port $PORT1..."
+kill -HUP $REMOTEAUTH_PID
+KILLPIDS="$BACKEND_PID"
+sleep $SLEEP0
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -F $TESTDIR/confdir -h $URI1 -d $LVL >> $LOG1 2>&1 &
+REMOTEAUTH_PID=$!
+if test $WAIT != 0 ; then
+ echo REMOTEAUTH_PID $REMOTEAUTH_PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $REMOTEAUTH_PID"
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+echo "Saving generated config after server restart..."
+echo "# search output from dynamically configured server after restart..." >> $SERVER2OUT
+$LDAPSEARCH -D cn=config -H $URI1 -y $CONFIGPWF \
+ -b "olcOverlay={0}remoteauth,olcDatabase={1}$BACKEND,cn=config" \
+ >> $SERVER2OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo -n "Checking bind handling... "
+
+$LDAPWHOAMI -H $URI1 -x -D "$BJORNSDN" -w bjorn >/dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+echo -n "1 "
+
+$LDAPWHOAMI -H $URI1 -x -D "$JOHNDDN" -w bjorn2 >/dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+echo -n "2 "
+
+$LDAPWHOAMI -H $URI1 -x -D "$MELLIOTDN" -w bjorn >/dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+echo -n "3 "
+
+echo "ok"
+
+echo "Stopping slapd on TCP/IP port $PORT1..."
+kill -HUP $REMOTEAUTH_PID
+KILLPIDS="$BACKEND_PID"
+sleep $SLEEP0
+
+echo "Testing slapd.conf support..."
+sed -e "s,database\\s*monitor,\\
+TLSCACertificateFile $TESTDIR/tls/ca/certs/testsuiteCA.crt\\
+\\
+#remoteauthmod#moduleload ../servers/slapd/overlays/remoteauth.la\\
+include $TESTDIR/remoteauth.conf\\
+\\
+database monitor," $TLSCONF | . $CONFFILTER $BACKEND >$CONF1
+echo "database config" >>$CONF1
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >>$CONF1
+
+. $CONFFILTER $BACKEND < $DATADIR/remoteauth/remoteauth.conf >$TESTDIR/remoteauth.conf
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL >> $LOG1 2>&1 &
+REMOTEAUTH_PID=$!
+if test $WAIT != 0 ; then
+ echo REMOTEAUTH_PID $REMOTEAUTH_PID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $REMOTEAUTH_PID"
+
+sleep $SLEEP0
+
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting ${SLEEP1} seconds for slapd to start..."
+ sleep ${SLEEP1}
+done
+
+echo "Saving generated config from a slapd.conf sourced server..."
+echo "# search output from server running from slapd.conf..." >> $SERVER3OUT
+$LDAPSEARCH -D cn=config -H $URI1 -y $CONFIGPWF \
+ -b "olcOverlay={0}remoteauth,olcDatabase={1}$BACKEND,cn=config" \
+ >> $SERVER3OUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo -n "Checking bind handling... "
+
+$LDAPWHOAMI -H $URI1 -x -D "$BJORNSDN" -w bjorn >/dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+echo -n "1 "
+
+$LDAPWHOAMI -H $URI1 -x -D "$JOHNDDN" -w bjorn2 >/dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+echo -n "2 "
+
+$LDAPWHOAMI -H $URI1 -x -D "$MELLIOTDN" -w bjorn >/dev/null
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+echo -n "3 "
+
+echo "ok"
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+# LDIFFILTER doesn't (un)wrap long lines yet, so the result would differ
+#. $CONFFILTER $BACKEND < $DATADIR/remoteauth/config.ldif \
+# | $LDIFFILTER -s a > $SERVER6FLT
+
+# We've already filtered out the ordering markers, now sort the entries
+echo "Filtering ldapsearch results..."
+$LDIFFILTER -s a < $SERVER1OUT > $SERVER1FLT
+$LDIFFILTER -s a < $SERVER2OUT > $SERVER2FLT
+$LDIFFILTER -s a < $SERVER3OUT > $SERVER3FLT
+echo "Filtering expected entries..."
+
+echo "Comparing filter output..."
+#$CMP $SERVER6FLT $SERVER1FLT > $CMPOUT && \
+$CMP $SERVER1FLT $SERVER2FLT > $CMPOUT && \
+$CMP $SERVER2FLT $SERVER3FLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test083-argon2 b/tests/scripts/test083-argon2
new file mode 100755
index 0000000..9700f1a
--- /dev/null
+++ b/tests/scripts/test083-argon2
@@ -0,0 +1,154 @@
+#!/bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2021-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $ARGON2 = argon2no; then
+ echo "argon2 overlay not available, test skipped"
+ exit 0
+fi
+
+USERDN="cn=argon2,$BASEDN"
+
+CONFDIR=$TESTDIR/slapd.d
+mkdir -p $TESTDIR $CONFDIR $DBDIR1
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+cat > $TESTDIR/config.ldif <<EOF
+dn: cn=config
+objectClass: olcGlobal
+cn: config
+olcArgsFile: $TESTDIR/slapd.args
+olcPidFile: $TESTDIR/slapd.pid
+
+dn: cn=schema,cn=config
+objectClass: olcSchemaConfig
+cn: schema
+
+include: file://$TESTWD/schema/core.ldif
+include: file://$TESTWD/schema/cosine.ldif
+include: file://$TESTWD/schema/inetorgperson.ldif
+EOF
+
+if [ "$BACKENDTYPE" = mod ]; then
+ cat >> $TESTDIR/config.ldif <<EOF
+
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/back-$BACKEND
+olcModuleLoad: back_$BACKEND.la
+EOF
+fi
+
+if [ "$ARGON2" = argon2yes ]; then
+ cat >> $TESTDIR/config.ldif <<EOF
+
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/pwmods
+olcModuleLoad: argon2.la
+EOF
+fi
+
+cat >> $TESTDIR/config.ldif <<EOF
+
+dn: olcDatabase={-1}frontend,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olcFrontendConfig
+olcDatabase: {-1}frontend
+olcPasswordHash: {ARGON2}
+
+dn: olcDatabase=config,cn=config
+objectClass: olcDatabaseConfig
+olcDatabase: config
+olcRootPW:< file://$CONFIGPWF
+
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc${BACKEND}Config
+olcDatabase: $BACKEND
+olcSuffix: $BASEDN
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+olcDbDirectory: $TESTDIR/db.1.a
+EOF
+
+if [ "$INDEXDB" = indexdb ]; then
+ cat >> $TESTDIR/config.ldif <<EOF
+olcDbIndex: objectClass eq,pres
+olcDbIndex: ou,cn,mail,surname,givenname eq,pres,sub
+EOF
+fi
+
+$SLAPADD -F $CONFDIR -n 0 -l $TESTDIR/config.ldif
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -F $CONFDIR -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding basic structure..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD -f $LDIFPASSWD >/dev/null 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $PID
+ exit $RC
+fi
+
+BINDPW=secret
+echo "Testing ldapwhoami as ${USERDN}..."
+$LDAPWHOAMI -H $URI1 -D "$USERDN" -w $BINDPW
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapwhoami failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $PID
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0
diff --git a/tests/scripts/test084-deref b/tests/scripts/test084-deref
new file mode 100755
index 0000000..b176d8e
--- /dev/null
+++ b/tests/scripts/test084-deref
@@ -0,0 +1,94 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2021-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $DEREF = derefno; then
+ echo "Deref overlay not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $DEREFCONF > $CONF1
+$SLAPADD -f $CONF1 -l $LDIFDEREF
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd deref control operations..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Sending deref control..."
+
+$LDAPSEARCH -b "$DEREFBASEDN" -H $URI1 \
+ -E 'deref=member:uid' > $SEARCHOUT 2>&1
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Comparing output..."
+$CMP $SEARCHOUT $DEREFOUT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "Comparison failed"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+test $KILLSERVERS != no && wait
+
+echo ">>>>> Test succeeded"
+
+exit 0
diff --git a/tests/scripts/test085-homedir b/tests/scripts/test085-homedir
new file mode 100755
index 0000000..8685b91
--- /dev/null
+++ b/tests/scripts/test085-homedir
@@ -0,0 +1,139 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2021-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $HOMEDIR = homedirno; then
+ echo "Homedir overlay not available, test skipped"
+ exit 0
+fi
+
+mkdir -p $TESTDIR $DBDIR1 $TESTDIR/home $TESTDIR/archive
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND < $HOMEDIRCONF | sed "s/@MINUID@/`id -u`/" > $CONF1
+$SLAPADD -f $CONF1 -l $LDIF
+RC=$?
+if test $RC != 0 ; then
+ echo "slapadd failed ($RC)!"
+ exit $RC
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding a new user..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD <<EOMOD >> $TESTOUT 2>&1
+dn: uid=user1,ou=People,$BASEDN
+objectClass: account
+objectClass: posixAccount
+uid: user1
+cn: One user
+uidNumber: `id -u`
+gidNumber: `id -g`
+homeDirectory: /home/user1
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1
+
+if ! test -e $TESTDIR/home/user1 ; then
+ echo "Home directory for user1 not created!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Moving home directory for user1..."
+$LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD <<EOMOD >> $TESTOUT 2>&1
+dn: uid=user1,ou=People,$BASEDN
+changetype: modify
+replace: homeDirectory
+homeDirectory: /home/user1_new
+EOMOD
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1
+
+if test -e $TESTDIR/home/user1 || ! test -e $TESTDIR/home/user1_new ; then
+ echo "Home directory for user1 not moved!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+echo "Removing user1, should get archived..."
+$LDAPDELETE -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ "uid=user1,ou=People,$BASEDN" >> $TESTOUT
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapdelete failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+sleep 1
+
+if test -e $TESTDIR/home/user1_new || \
+ ! test -e $TESTDIR/archive/user1_new-*-0.tar ; then
+ echo "Home directory for user1 not archived properly!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+test $KILLSERVERS != no && wait
+
+echo ">>>>> Test succeeded"
+
+exit 0
diff --git a/tests/scripts/test086-delta-consumer-config b/tests/scripts/test086-delta-consumer-config
new file mode 100755
index 0000000..b8f08cf
--- /dev/null
+++ b/tests/scripts/test086-delta-consumer-config
@@ -0,0 +1,581 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $SYNCPROV = syncprovno; then
+ echo "Syncrepl provider overlay not available, test skipped"
+ exit 0
+fi
+if test $ACCESSLOG = accesslogno; then
+ echo "Accesslog overlay not available, test skipped"
+ exit 0
+fi
+
+CFPRO=$TESTDIR/cfpro.d
+CFCON=$TESTDIR/cfcon.d
+
+mkdir -p $TESTDIR $DBDIR1A $DBDIR1B $DBDIR1C $DBDIR1D $DBDIR2A $CFPRO $CFCON
+
+$SLAPPASSWD -g -n >$CONFIGPWF
+
+if test x"$SYNCMODE" = x ; then
+ SYNCMODE=rp
+fi
+case "$SYNCMODE" in
+ ro)
+ SYNCTYPE="type=refreshOnly interval=00:00:00:03"
+ ;;
+ rp)
+ SYNCTYPE="type=refreshAndPersist"
+ ;;
+ *)
+ echo "unknown sync mode $SYNCMODE"
+ exit 1;
+ ;;
+esac
+
+#
+# Test replication of dynamic config with alternate consumer config:
+# - start provider
+# - start consumer
+# - configure over ldap
+# - populate over ldap
+# - configure syncrepl over ldap
+# - retrieve database over ldap and compare against expected results
+#
+
+echo "Starting provider slapd on TCP/IP port $PORT1..."
+. $CONFFILTER $BACKEND < $DYNAMICCONF > $CONFLDIF
+$SLAPADD -F $CFPRO -n 0 -l $CONFLDIF
+$SLAPD -F $CFPRO -h $URI1 -d $LVL > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+ echo PID $PID
+ read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Using ldapsearch to check that provider slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI1 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Inserting syncprov and accesslog overlays on provider..."
+if [ "$SYNCPROV" = syncprovmod -a "$ACCESSLOG" = accesslogmod ]; then
+ $LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: ../servers/slapd/overlays
+olcModuleLoad: syncprov.la
+olcModuleLoad: accesslog.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for moduleLoad of syncprov and accesslog ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+elif [ "$SYNCPROV" = syncprovmod ]; then
+ $LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: ../servers/slapd/overlays
+olcModuleLoad: syncprov.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for moduleLoad of syncprov ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+elif [ "$ACCESSLOG" = accesslogmod ]; then
+ $LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: ../servers/slapd/overlays
+olcModuleLoad: accesslog.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for moduleLoad of accesslog ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+echo "Adding backend accesslog databases using $BACKEND..."
+if [ "$BACKENDTYPE" = mod ]; then
+ $LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+dn: cn=module,cn=config
+objectClass: olcModuleList
+cn: module
+olcModulePath: $TESTWD/../servers/slapd/back-$BACKEND
+olcModuleLoad: back_$BACKEND.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for moduleLoad of $BACKEND ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+read CONFIGPW < $CONFIGPWF
+$LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF > $TESTOUT 2>&1
+dn: olcDatabase={1}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+olcSuffix: cn=accesslog
+${nullExclude}olcDbDirectory: $DBDIR1C
+olcRootDN: cn=config
+olcSizeLimit: unlimited
+olcTimeLimit: unlimited
+olcDbIndex: default eq
+olcDbIndex: entryCSN,objectClass,reqEnd,reqResult,reqStart,reqDN
+
+dn: olcOverlay=syncprov,olcDatabase={1}${BACKEND},cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+olcSpNoPresent: TRUE
+olcSpReloadHint: TRUE
+
+dn: olcDatabase={2}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+objectClass: olc${BACKEND}Config
+olcDatabase: {2}$BACKEND
+olcSuffix: cn=consumer-accesslog
+${nullExclude}olcDbDirectory: $DBDIR1D
+olcRootDN: cn=consumer,cn=config
+olcSizeLimit: unlimited
+olcTimeLimit: unlimited
+olcDbIndex: default eq
+olcDbIndex: entryCSN,objectClass,reqEnd,reqResult,reqStart,reqDN
+
+dn: olcOverlay=syncprov,olcDatabase={2}${BACKEND},cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+olcSpNoPresent: TRUE
+olcSpReloadHint: TRUE
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for accesslog databases using $BACKEND ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: olcOverlay=syncprov,olcDatabase={0}config,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+
+dn: olcOverlay=accesslog,olcDatabase={0}config,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectClass: olcAccessLogConfig
+olcOverlay: accesslog
+olcAccessLogDB: cn=accesslog
+olcAccessLogOps: writes
+olcAccessLogPurge: 07+00:00 01+00:00
+olcAccessLogSuccess: TRUE
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for syncprov and accesslog overlay config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+# Consumers will not replicate the provider's actual cn=config.
+# Instead, they will use an alternate DB so that they may be
+# configured differently from the provider. This alternate DB
+# will also be a consumer for the real cn=schema,cn=config tree.
+# It has multi-provider enabled so that it can be written directly
+# while being a consumer of the main schema.
+echo "Configuring accesslog config DB on provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: cn=config
+changetype: modify
+add: olcServerID
+olcServerID: 1
+
+dn: olcDatabase={1}ldif,cn=config
+changetype: add
+objectClass: olcDatabaseConfig
+objectClass: olcLdifConfig
+olcDatabase: {1}ldif
+olcDbDirectory: $DBDIR1A
+olcSuffix: cn=config,cn=consumer
+olcRootDN: cn=config,cn=consumer
+olcRootPW: repsecret
+olcAccess: to * by dn.base="cn=config" write
+
+dn: olcOverlay=syncprov,olcDatabase={1}ldif,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+
+dn: olcOverlay=accesslog,olcDatabase={1}ldif,cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectClass: olcAccessLogConfig
+olcOverlay: accesslog
+olcAccessLogDB: cn=consumer-accesslog
+olcAccessLogOps: writes
+olcAccessLogPurge: 07+00:00 01+00:00
+olcAccessLogSuccess: TRUE
+EOF
+
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for consumer DB config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting 3 seconds for syncrepl to make root accesslog entry..."
+sleep 3
+
+echo "Configuring consumer config DB on provider..."
+$LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >> $TESTOUT 2>&1
+dn: cn=config,cn=consumer
+changetype: add
+objectClass: olcGlobal
+cn: consumerconfig
+
+dn: olcDatabase={0}config,cn=config,cn=consumer
+changetype: add
+objectClass: olcDatabaseConfig
+olcDatabase: {0}config
+olcRootPW: topsecret
+olcSyncrepl: {0}rid=001 provider=$URI1 binddn="cn=config,cn=consumer"
+ bindmethod=simple credentials=repsecret searchbase="cn=config,cn=consumer"
+ $SYNCTYPE retry="3 5 300 5" timeout=3 suffixmassage="cn=config"
+olcUpdateRef: $URI1
+
+dn: olcDatabase={1}ldif,cn=config
+changetype: modify
+add: olcSyncrepl
+olcSyncrepl: {0}rid=001 provider=$URI1 binddn="cn=config"
+ bindmethod=simple credentials=$CONFIGPW searchbase="cn=schema,cn=config"
+ $SYNCTYPE retry="3 5 300 5" timeout=3
+ suffixmassage="cn=schema,cn=config,cn=consumer"
+# a dummy stanza we add to the beginning and remove again
+olcSyncrepl: {0}rid=006 provider=$URI6 binddn="cn=config"
+ bindmethod=simple credentials=$CONFIGPW searchbase="cn=schema,cn=config"
+ $SYNCTYPE retry="3 5 300 5" timeout=3
+ suffixmassage="cn=schema,cn=config,cn=consumer"
+-
+delete: olcSyncrepl
+olcSyncrepl: {0}
+-
+add: olcMultiProvider
+olcMultiProvider: TRUE
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapmodify failed for consumer DB config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Starting consumer slapd on TCP/IP port $PORT2..."
+$SLAPADD -F $CFCON -n 0 -l $CONFLDIF
+$SLAPD -F $CFCON -h $URI2 -d $LVL > $LOG2 2>&1 &
+CONSUMERPID=$!
+if test $WAIT != 0 ; then
+ echo CONSUMERPID $CONSUMERPID
+ read foo
+fi
+KILLPIDS="$KILLPIDS $CONSUMERPID"
+
+sleep 1
+
+echo "Using ldapsearch to check that consumer slapd is running..."
+for i in 0 1 2 3 4 5; do
+ $LDAPSEARCH -s base -b "" -H $URI2 \
+ 'objectclass=*' > /dev/null 2>&1
+ RC=$?
+ if test $RC = 0 ; then
+ break
+ fi
+ echo "Waiting 5 seconds for slapd to start..."
+ sleep 5
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Configuring syncrepl on consumer..."
+$LDAPMODIFY -D cn=config -H $URI2 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcDatabase={0}config,cn=config
+changetype: modify
+add: olcSyncRepl
+olcSyncRepl: rid=001 provider=$URI1 binddn="cn=config,cn=consumer"
+ bindmethod=simple credentials=repsecret searchbase="cn=config,cn=consumer"
+ $SYNCTYPE retry="3 5 300 5" timeout=3 logbase="cn=consumer-accesslog"
+ logfilter="(&(objectclass=auditWriteObject)(reqresult=0))"
+ syncdata=accesslog suffixmassage="cn=config"
+-
+add: olcUpdateRef
+olcUpdateRef: $URI1
+EOF
+
+sleep 1
+
+echo "Using ldapsearch to check that syncrepl received config changes..."
+RC=32
+for i in 0 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI2 -D cn=config -y $CONFIGPWF \
+ -s base -b "olcDatabase={0}config,cn=config" \
+ '(olcUpdateRef=*)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Adding schema and databases on provider..."
+$LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+include: file://$ABS_SCHEMADIR/core.ldif
+
+include: file://$ABS_SCHEMADIR/cosine.ldif
+
+include: file://$ABS_SCHEMADIR/inetorgperson.ldif
+
+include: file://$ABS_SCHEMADIR/openldap.ldif
+
+include: file://$ABS_SCHEMADIR/nis.ldif
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for schema config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to check that syncrepl received the schema changes..."
+RC=32
+for i in 0 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI2 -D cn=config -y $CONFIGPWF \
+ -s sub -b "cn=schema,cn=config" \
+ '(cn=*openldap)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+done
+
+if test "x$RESULT" != "xOK" ; then
+ echo "consumer never received complete schema!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit 1
+fi
+
+nullExclude="" nullOK=""
+test $BACKEND = null && nullExclude="# " nullOK="OK"
+
+if [ "$BACKENDTYPE" = mod ]; then
+ $LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: cn=module,cn=config,cn=consumer
+objectClass: olcModuleList
+cn: module
+olcModulePath: ../servers/slapd/back-$BACKEND
+olcModuleLoad: back_$BACKEND.la
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd failed for backend config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+$LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcDatabase={2}$BACKEND,cn=config
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {2}$BACKEND
+olcSuffix: $BASEDN
+${nullExclude}olcDbDirectory: $DBDIR1B
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+olcSyncRepl: rid=002 provider=$URI1 binddn="$MANAGERDN" bindmethod=simple
+ credentials=$PASSWD searchbase="$BASEDN" $SYNCTYPE
+ retry="3 5 300 5" timeout=3
+olcUpdateRef: $URI1
+
+dn: olcOverlay=syncprov,olcDatabase={2}${BACKEND},cn=config
+changetype: add
+objectClass: olcOverlayConfig
+objectClass: olcSyncProvConfig
+olcOverlay: syncprov
+
+dn: olcDatabase={1}$BACKEND,cn=config,cn=consumer
+objectClass: olcDatabaseConfig
+${nullExclude}objectClass: olc${BACKEND}Config
+olcDatabase: {1}$BACKEND
+olcSuffix: $BASEDN
+${nullExclude}olcDbDirectory: $DBDIR2A
+olcRootDN: $MANAGERDN
+olcRootPW: $PASSWD
+olcSyncRepl: rid=002 provider=$URI1 binddn="$MANAGERDN" bindmethod=simple
+ credentials=$PASSWD searchbase="$BASEDN" $SYNCTYPE
+ retry="3 5 300 5" timeout=3
+olcUpdateRef: $URI1
+
+EOF
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+if test $INDEXDB = indexdb ; then
+ $LDAPMODIFY -D cn=config -H $URI1 -y $CONFIGPWF <<EOF >>$TESTOUT 2>&1
+dn: olcDatabase={2}$BACKEND,cn=config
+changetype: modify
+add: olcDbIndex
+olcDbIndex: objectClass,entryUUID,entryCSN eq
+olcDbIndex: cn,uid pres,eq,sub
+EOF
+ RC=$?
+ if test $RC != 0 ; then
+ echo "ldapadd modify for database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+ fi
+fi
+
+echo "Using ldapadd to populate provider..."
+$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD -f $LDIFORDERED \
+ >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+ echo "ldapadd failed for database config ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+sleep $SLEEP1
+
+echo "Using ldapsearch to check that syncrepl received database changes..."
+RC=32
+for i in 0 1 2 3 4 5; do
+ RESULT=`$LDAPSEARCH -H $URI2 \
+ -s base -b "cn=Ursula Hampster,ou=Alumni Association,ou=People,dc=example,dc=com" \
+ '(objectClass=*)' 2>&1 | awk '/^dn:/ {print "OK"}'`
+ if test "x$RESULT$nullOK" = "xOK" ; then
+ RC=0
+ break
+ fi
+ echo "Waiting $SLEEP1 seconds for syncrepl to receive changes..."
+ sleep $SLEEP1
+done
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the provider..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI1 -w $PASSWD \
+ 'objectclass=*' > $PROVIDEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at provider ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+echo "Using ldapsearch to read all the entries from the consumer..."
+$LDAPSEARCH -S "" -b "$BASEDN" -D "$MANAGERDN" -H $URI2 -w $PASSWD \
+ 'objectclass=*' > $CONSUMEROUT 2>&1
+RC=$?
+
+if test $RC != 0 ; then
+ echo "ldapsearch failed at consumer ($RC)!"
+ test $KILLSERVERS != no && kill -HUP $KILLPIDS
+ exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+echo "Filtering provider results..."
+$LDIFFILTER < $PROVIDEROUT > $PROVIDERFLT
+echo "Filtering consumer results..."
+$LDIFFILTER < $CONSUMEROUT > $CONSUMERFLT
+
+echo "Comparing retrieved entries from provider and consumer..."
+$CMP $PROVIDERFLT $CONSUMERFLT > $CMPOUT
+
+if test $? != 0 ; then
+ echo "test failed - provider and consumer databases differ"
+ exit 1
+fi
+
+echo ">>>>> Test succeeded"
+
+test $KILLSERVERS != no && wait
+
+exit 0